1 /*
   2  * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.net;
  27 
  28 import java.util.Enumeration;
  29 import java.util.Vector;
  30 import java.util.List;
  31 import java.util.ArrayList;
  32 import java.util.Collections;
  33 import java.util.StringTokenizer;
  34 import java.net.InetAddress;
  35 import java.security.Permission;
  36 import java.security.PermissionCollection;
  37 import java.security.PrivilegedAction;
  38 import java.security.AccessController;
  39 import java.security.Security;
  40 import java.security.Policy;
  41 import java.io.Serializable;
  42 import java.io.ObjectStreamField;
  43 import java.io.ObjectOutputStream;
  44 import java.io.ObjectInputStream;
  45 import java.io.IOException;
  46 import sun.net.util.IPAddressUtil;
  47 import sun.security.util.SecurityConstants;
  48 import sun.security.util.Debug;
  49 
  50 
  51 /**
  52  * This class represents access to a network via sockets.
  53  * A SocketPermission consists of a
  54  * host specification and a set of "actions" specifying ways to
  55  * connect to that host. The host is specified as
  56  * <pre>
  57  *    host = (hostname | IPv4address | iPv6reference) [:portrange]
  58  *    portrange = portnumber | -portnumber | portnumber-[portnumber]
  59  * </pre>
  60  * The host is expressed as a DNS name, as a numerical IP address,
  61  * or as "localhost" (for the local machine).
  62  * The wildcard "*" may be included once in a DNS name host
  63  * specification. If it is included, it must be in the leftmost
  64  * position, as in "*.sun.com".
  65  * <p>
  66  * The format of the IPv6reference should follow that specified in <a
  67  * href="http://www.ietf.org/rfc/rfc2732.txt"><i>RFC&nbsp;2732: Format
  68  * for Literal IPv6 Addresses in URLs</i></a>:
  69  * <pre>
  70  *    ipv6reference = "[" IPv6address "]"
  71  *</pre>
  72  * For example, you can construct a SocketPermission instance
  73  * as the following:
  74  * <pre>
  75  *    String hostAddress = inetaddress.getHostAddress();
  76  *    if (inetaddress instanceof Inet6Address) {
  77  *        sp = new SocketPermission("[" + hostAddress + "]:" + port, action);
  78  *    } else {
  79  *        sp = new SocketPermission(hostAddress + ":" + port, action);
  80  *    }
  81  * </pre>
  82  * or
  83  * <pre>
  84  *    String host = url.getHost();
  85  *    sp = new SocketPermission(host + ":" + port, action);
  86  * </pre>
  87  * <p>
  88  * The <A HREF="Inet6Address.html#lform">full uncompressed form</A> of
  89  * an IPv6 literal address is also valid.
  90  * <p>
  91  * The port or portrange is optional. A port specification of the
  92  * form "N-", where <i>N</i> is a port number, signifies all ports
  93  * numbered <i>N</i> and above, while a specification of the
  94  * form "-N" indicates all ports numbered <i>N</i> and below.
  95  * <p>
  96  * The possible ways to connect to the host are
  97  * <pre>
  98  * accept
  99  * connect
 100  * listen
 101  * resolve
 102  * </pre>
 103  * The "listen" action is only meaningful when used with "localhost".
 104  * The "resolve" action is implied when any of the other actions are present.
 105  * The action "resolve" refers to host/ip name service lookups.
 106  *
 107  * <p>As an example of the creation and meaning of SocketPermissions,
 108  * note that if the following permission:
 109  *
 110  * <pre>
 111  *   p1 = new SocketPermission("puffin.eng.sun.com:7777", "connect,accept");
 112  * </pre>
 113  *
 114  * is granted to some code, it allows that code to connect to port 7777 on
 115  * <code>puffin.eng.sun.com</code>, and to accept connections on that port.
 116  *
 117  * <p>Similarly, if the following permission:
 118  *
 119  * <pre>
 120  *   p1 = new SocketPermission("puffin.eng.sun.com:7777", "connect,accept");
 121  *   p2 = new SocketPermission("localhost:1024-", "accept,connect,listen");
 122  * </pre>
 123  *
 124  * is granted to some code, it allows that code to
 125  * accept connections on, connect to, or listen on any port between
 126  * 1024 and 65535 on the local host.
 127  *
 128  * <p>Note: Granting code permission to accept or make connections to remote
 129  * hosts may be dangerous because malevolent code can then more easily
 130  * transfer and share confidential data among parties who may not
 131  * otherwise have access to the data.
 132  *
 133  * @see java.security.Permissions
 134  * @see SocketPermission
 135  *
 136  *
 137  * @author Marianne Mueller
 138  * @author Roland Schemers
 139  *
 140  * @serial exclude
 141  */
 142 
 143 public final class SocketPermission extends Permission
 144 implements java.io.Serializable
 145 {
 146     private static final long serialVersionUID = -7204263841984476862L;
 147 
 148     /**
 149      * Connect to host:port
 150      */
 151     private final static int CONNECT    = 0x1;
 152 
 153     /**
 154      * Listen on host:port
 155      */
 156     private final static int LISTEN     = 0x2;
 157 
 158     /**
 159      * Accept a connection from host:port
 160      */
 161     private final static int ACCEPT     = 0x4;
 162 
 163     /**
 164      * Resolve DNS queries
 165      */
 166     private final static int RESOLVE    = 0x8;
 167 
 168     /**
 169      * No actions
 170      */
 171     private final static int NONE               = 0x0;
 172 
 173     /**
 174      * All actions
 175      */
 176     private final static int ALL        = CONNECT|LISTEN|ACCEPT|RESOLVE;
 177 
 178     // various port constants
 179     private static final int PORT_MIN = 0;
 180     private static final int PORT_MAX = 65535;
 181     private static final int PRIV_PORT_MAX = 1023;
 182     private static final int DEF_EPH_LOW = 49152;
 183 
 184     // the actions mask
 185     private transient int mask;
 186 
 187     /**
 188      * the actions string.
 189      *
 190      * @serial
 191      */
 192 
 193     private String actions; // Left null as long as possible, then
 194                             // created and re-used in the getAction function.
 195 
 196     // hostname part as it is passed
 197     private transient String hostname;
 198 
 199     // the canonical name of the host
 200     // in the case of "*.foo.com", cname is ".foo.com".
 201 
 202     private transient String cname;
 203 
 204     // all the IP addresses of the host
 205     private transient InetAddress[] addresses;
 206 
 207     // true if the hostname is a wildcard (e.g. "*.sun.com")
 208     private transient boolean wildcard;
 209 
 210     // true if we were initialized with a single numeric IP address
 211     private transient boolean init_with_ip;
 212 
 213     // true if this SocketPermission represents an invalid/unknown host
 214     // used for implies when the delayed lookup has already failed
 215     private transient boolean invalid;
 216 
 217     // port range on host
 218     private transient int[] portrange;
 219 
 220     private transient boolean defaultDeny = false;
 221 
 222     // true if this SocketPermission represents a hostname
 223     // that failed our reverse mapping heuristic test
 224     private transient boolean untrusted;
 225 
 226     // true if the trustProxy system property is set
 227     private static boolean trustProxy;
 228 
 229     // true if the sun.net.trustNameService system property is set
 230     private static boolean trustNameService;
 231 
 232     private static Debug debug = null;
 233     private static boolean debugInit = false;
 234 
 235     // ephemeral port range for this system
 236     private static final int ephemeralLow = initEphemeralPorts(
 237         "low", DEF_EPH_LOW
 238     );
 239     private static final int ephemeralHigh = initEphemeralPorts(
 240         "high", PORT_MAX
 241     );
 242 
 243     static {
 244         Boolean tmp = java.security.AccessController.doPrivileged(
 245                 new sun.security.action.GetBooleanAction("trustProxy"));
 246         trustProxy = tmp.booleanValue();
 247         tmp = java.security.AccessController.doPrivileged(
 248             new sun.security.action.GetBooleanAction("sun.net.trustNameService"));
 249         trustNameService = tmp.booleanValue();
 250     }
 251 
 252     private static synchronized Debug getDebug()
 253     {
 254         if (!debugInit) {
 255             debug = Debug.getInstance("access");
 256             debugInit = true;
 257         }
 258         return debug;
 259     }
 260 
 261     /**
 262      * Creates a new SocketPermission object with the specified actions.
 263      * The host is expressed as a DNS name, or as a numerical IP address.
 264      * Optionally, a port or a portrange may be supplied (separated
 265      * from the DNS name or IP address by a colon).
 266      * <p>
 267      * To specify the local machine, use "localhost" as the <i>host</i>.
 268      * Also note: An empty <i>host</i> String ("") is equivalent to "localhost".
 269      * <p>
 270      * The <i>actions</i> parameter contains a comma-separated list of the
 271      * actions granted for the specified host (and port(s)). Possible actions are
 272      * "connect", "listen", "accept", "resolve", or
 273      * any combination of those. "resolve" is automatically added
 274      * when any of the other three are specified.
 275      * <p>
 276      * Examples of SocketPermission instantiation are the following:
 277      * <pre>
 278      *    nr = new SocketPermission("www.catalog.com", "connect");
 279      *    nr = new SocketPermission("www.sun.com:80", "connect");
 280      *    nr = new SocketPermission("*.sun.com", "connect");
 281      *    nr = new SocketPermission("*.edu", "resolve");
 282      *    nr = new SocketPermission("204.160.241.0", "connect");
 283      *    nr = new SocketPermission("localhost:1024-65535", "listen");
 284      *    nr = new SocketPermission("204.160.241.0:1024-65535", "connect");
 285      * </pre>
 286      *
 287      * @param host the hostname or IPaddress of the computer, optionally
 288      * including a colon followed by a port or port range.
 289      * @param action the action string.
 290      */
 291     public SocketPermission(String host, String action) {
 292         super(getHost(host));
 293         // name initialized to getHost(host); NPE detected in getHost()
 294         init(getName(), getMask(action));
 295     }
 296 
 297 
 298     SocketPermission(String host, int mask) {
 299         super(getHost(host));
 300         // name initialized to getHost(host); NPE detected in getHost()
 301         init(getName(), mask);
 302     }
 303 
 304     private void setDeny() {
 305         defaultDeny = true;
 306     }
 307 
 308     private static String getHost(String host)
 309     {
 310         if (host.equals("")) {
 311             return "localhost";
 312         } else {
 313             /* IPv6 literal address used in this context should follow
 314              * the format specified in RFC 2732;
 315              * if not, we try to solve the unambiguous case
 316              */
 317             int ind;
 318             if (host.charAt(0) != '[') {
 319                 if ((ind = host.indexOf(':')) != host.lastIndexOf(':')) {
 320                     /* More than one ":", meaning IPv6 address is not
 321                      * in RFC 2732 format;
 322                      * We will rectify user errors for all unambiguious cases
 323                      */
 324                     StringTokenizer st = new StringTokenizer(host, ":");
 325                     int tokens = st.countTokens();
 326                     if (tokens == 9) {
 327                         // IPv6 address followed by port
 328                         ind = host.lastIndexOf(':');
 329                         host = "[" + host.substring(0, ind) + "]" +
 330                             host.substring(ind);
 331                     } else if (tokens == 8 && host.indexOf("::") == -1) {
 332                         // IPv6 address only, not followed by port
 333                         host = "[" + host + "]";
 334                     } else {
 335                         // could be ambiguous
 336                         throw new IllegalArgumentException("Ambiguous"+
 337                                                            " hostport part");
 338                     }
 339                 }
 340             }
 341             return host;
 342         }
 343     }
 344 
 345     private int[] parsePort(String port)
 346         throws Exception
 347     {
 348 
 349         if (port == null || port.equals("") || port.equals("*")) {
 350             return new int[] {PORT_MIN, PORT_MAX};
 351         }
 352 
 353         int dash = port.indexOf('-');
 354 
 355         if (dash == -1) {
 356             int p = Integer.parseInt(port);
 357             return new int[] {p, p};
 358         } else {
 359             String low = port.substring(0, dash);
 360             String high = port.substring(dash+1);
 361             int l,h;
 362 
 363             if (low.equals("")) {
 364                 l = PORT_MIN;
 365             } else {
 366                 l = Integer.parseInt(low);
 367             }
 368 
 369             if (high.equals("")) {
 370                 h = PORT_MAX;
 371             } else {
 372                 h = Integer.parseInt(high);
 373             }
 374             if (l < 0 || h < 0 || h<l)
 375                 throw new IllegalArgumentException("invalid port range");
 376 
 377             return new int[] {l, h};
 378         }
 379     }
 380 
 381     /**
 382      * Returns true if the permission has specified zero
 383      * as its value (or lower bound) signifying the ephemeral range
 384      */
 385     private boolean includesEphemerals() {
 386         return portrange[0] == 0;
 387     }
 388 
 389     /**
 390      * Initialize the SocketPermission object. We don't do any DNS lookups
 391      * as this point, instead we hold off until the implies method is
 392      * called.
 393      */
 394     private void init(String host, int mask) {
 395         // Set the integer mask that represents the actions
 396 
 397         if ((mask & ALL) != mask)
 398             throw new IllegalArgumentException("invalid actions mask");
 399 
 400         // always OR in RESOLVE if we allow any of the others
 401         this.mask = mask | RESOLVE;
 402 
 403         // Parse the host name.  A name has up to three components, the
 404         // hostname, a port number, or two numbers representing a port
 405         // range.   "www.sun.com:8080-9090" is a valid host name.
 406 
 407         // With IPv6 an address can be 2010:836B:4179::836B:4179
 408         // An IPv6 address needs to be enclose in []
 409         // For ex: [2010:836B:4179::836B:4179]:8080-9090
 410         // Refer to RFC 2732 for more information.
 411 
 412         int rb = 0 ;
 413         int start = 0, end = 0;
 414         int sep = -1;
 415         String hostport = host;
 416         if (host.charAt(0) == '[') {
 417             start = 1;
 418             rb = host.indexOf(']');
 419             if (rb != -1) {
 420                 host = host.substring(start, rb);
 421             } else {
 422                 throw new
 423                     IllegalArgumentException("invalid host/port: "+host);
 424             }
 425             sep = hostport.indexOf(':', rb+1);
 426         } else {
 427             start = 0;
 428             sep = host.indexOf(':', rb);
 429             end = sep;
 430             if (sep != -1) {
 431                 host = host.substring(start, end);
 432             }
 433         }
 434 
 435         if (sep != -1) {
 436             String port = hostport.substring(sep+1);
 437             try {
 438                 portrange = parsePort(port);
 439             } catch (Exception e) {
 440                 throw new
 441                     IllegalArgumentException("invalid port range: "+port);
 442             }
 443         } else {
 444             portrange = new int[] { PORT_MIN, PORT_MAX };
 445         }
 446 
 447         hostname = host;
 448 
 449         // is this a domain wildcard specification
 450         if (host.lastIndexOf('*') > 0) {
 451             throw new
 452                IllegalArgumentException("invalid host wildcard specification");
 453         } else if (host.startsWith("*")) {
 454             wildcard = true;
 455             if (host.equals("*")) {
 456                 cname = "";
 457             } else if (host.startsWith("*.")) {
 458                 cname = host.substring(1).toLowerCase();
 459             } else {
 460               throw new
 461                IllegalArgumentException("invalid host wildcard specification");
 462             }
 463             return;
 464         } else {
 465             if (host.length() > 0) {
 466                 // see if we are being initialized with an IP address.
 467                 char ch = host.charAt(0);
 468                 if (ch == ':' || Character.digit(ch, 16) != -1) {
 469                     byte ip[] = IPAddressUtil.textToNumericFormatV4(host);
 470                     if (ip == null) {
 471                         ip = IPAddressUtil.textToNumericFormatV6(host);
 472                     }
 473                     if (ip != null) {
 474                         try {
 475                             addresses =
 476                                 new InetAddress[]
 477                                 {InetAddress.getByAddress(ip) };
 478                             init_with_ip = true;
 479                         } catch (UnknownHostException uhe) {
 480                             // this shouldn't happen
 481                             invalid = true;
 482                         }
 483                     }
 484                 }
 485             }
 486         }
 487     }
 488 
 489     /**
 490      * Convert an action string to an integer actions mask.
 491      *
 492      * @param action the action string
 493      * @return the action mask
 494      */
 495     private static int getMask(String action) {
 496 
 497         if (action == null) {
 498             throw new NullPointerException("action can't be null");
 499         }
 500 
 501         if (action.equals("")) {
 502             throw new IllegalArgumentException("action can't be empty");
 503         }
 504 
 505         int mask = NONE;
 506 
 507         // Check against use of constants (used heavily within the JDK)
 508         if (action == SecurityConstants.SOCKET_RESOLVE_ACTION) {
 509             return RESOLVE;
 510         } else if (action == SecurityConstants.SOCKET_CONNECT_ACTION) {
 511             return CONNECT;
 512         } else if (action == SecurityConstants.SOCKET_LISTEN_ACTION) {
 513             return LISTEN;
 514         } else if (action == SecurityConstants.SOCKET_ACCEPT_ACTION) {
 515             return ACCEPT;
 516         } else if (action == SecurityConstants.SOCKET_CONNECT_ACCEPT_ACTION) {
 517             return CONNECT|ACCEPT;
 518         }
 519 
 520         char[] a = action.toCharArray();
 521 
 522         int i = a.length - 1;
 523         if (i < 0)
 524             return mask;
 525 
 526         while (i != -1) {
 527             char c;
 528 
 529             // skip whitespace
 530             while ((i!=-1) && ((c = a[i]) == ' ' ||
 531                                c == '\r' ||
 532                                c == '\n' ||
 533                                c == '\f' ||
 534                                c == '\t'))
 535                 i--;
 536 
 537             // check for the known strings
 538             int matchlen;
 539 
 540             if (i >= 6 && (a[i-6] == 'c' || a[i-6] == 'C') &&
 541                           (a[i-5] == 'o' || a[i-5] == 'O') &&
 542                           (a[i-4] == 'n' || a[i-4] == 'N') &&
 543                           (a[i-3] == 'n' || a[i-3] == 'N') &&
 544                           (a[i-2] == 'e' || a[i-2] == 'E') &&
 545                           (a[i-1] == 'c' || a[i-1] == 'C') &&
 546                           (a[i] == 't' || a[i] == 'T'))
 547             {
 548                 matchlen = 7;
 549                 mask |= CONNECT;
 550 
 551             } else if (i >= 6 && (a[i-6] == 'r' || a[i-6] == 'R') &&
 552                                  (a[i-5] == 'e' || a[i-5] == 'E') &&
 553                                  (a[i-4] == 's' || a[i-4] == 'S') &&
 554                                  (a[i-3] == 'o' || a[i-3] == 'O') &&
 555                                  (a[i-2] == 'l' || a[i-2] == 'L') &&
 556                                  (a[i-1] == 'v' || a[i-1] == 'V') &&
 557                                  (a[i] == 'e' || a[i] == 'E'))
 558             {
 559                 matchlen = 7;
 560                 mask |= RESOLVE;
 561 
 562             } else if (i >= 5 && (a[i-5] == 'l' || a[i-5] == 'L') &&
 563                                  (a[i-4] == 'i' || a[i-4] == 'I') &&
 564                                  (a[i-3] == 's' || a[i-3] == 'S') &&
 565                                  (a[i-2] == 't' || a[i-2] == 'T') &&
 566                                  (a[i-1] == 'e' || a[i-1] == 'E') &&
 567                                  (a[i] == 'n' || a[i] == 'N'))
 568             {
 569                 matchlen = 6;
 570                 mask |= LISTEN;
 571 
 572             } else if (i >= 5 && (a[i-5] == 'a' || a[i-5] == 'A') &&
 573                                  (a[i-4] == 'c' || a[i-4] == 'C') &&
 574                                  (a[i-3] == 'c' || a[i-3] == 'C') &&
 575                                  (a[i-2] == 'e' || a[i-2] == 'E') &&
 576                                  (a[i-1] == 'p' || a[i-1] == 'P') &&
 577                                  (a[i] == 't' || a[i] == 'T'))
 578             {
 579                 matchlen = 6;
 580                 mask |= ACCEPT;
 581 
 582             } else {
 583                 // parse error
 584                 throw new IllegalArgumentException(
 585                         "invalid permission: " + action);
 586             }
 587 
 588             // make sure we didn't just match the tail of a word
 589             // like "ackbarfaccept".  Also, skip to the comma.
 590             boolean seencomma = false;
 591             while (i >= matchlen && !seencomma) {
 592                 switch(a[i-matchlen]) {
 593                 case ',':
 594                     seencomma = true;
 595                     /*FALLTHROUGH*/
 596                 case ' ': case '\r': case '\n':
 597                 case '\f': case '\t':
 598                     break;
 599                 default:
 600                     throw new IllegalArgumentException(
 601                             "invalid permission: " + action);
 602                 }
 603                 i--;
 604             }
 605 
 606             // point i at the location of the comma minus one (or -1).
 607             i -= matchlen;
 608         }
 609 
 610         return mask;
 611     }
 612 
 613     /**
 614      * attempt to get the fully qualified domain name
 615      *
 616      */
 617     void getCanonName()
 618         throws UnknownHostException
 619     {
 620         if (cname != null || invalid || untrusted) return;
 621 
 622         // attempt to get the canonical name
 623 
 624         try {
 625             // first get the IP addresses if we don't have them yet
 626             // this is because we need the IP address to then get
 627             // FQDN.
 628             if (addresses == null) {
 629                 getIP();
 630             }
 631 
 632             // we have to do this check, otherwise we might not
 633             // get the fully qualified domain name
 634             if (init_with_ip) {
 635                 cname = addresses[0].getHostName(false).toLowerCase();
 636             } else {
 637                 cname = InetAddress.getByName(addresses[0].getHostAddress()).
 638                                               getHostName(false).toLowerCase();
 639                 if (!trustNameService && sun.net.www.URLConnection.isProxiedHost(hostname)) {
 640                     if (!match(cname, hostname) && 
 641                         (defaultDeny || !cname.equals(addresses[0].getHostAddress()))) {
 642                         // Last chance
 643                         if (!authorized(hostname, addresses[0].getAddress())) {
 644                             untrusted = true;
 645                             Debug debug = getDebug();
 646                             if (debug != null && Debug.isOn("failure")) {
 647                                 debug.println("socket access restriction: proxied host " + "(" + addresses[0] + ")" + " does not match " + cname + " from reverse lookup");
 648                             }
 649                         }
 650                     }
 651                 }
 652             }
 653         } catch (UnknownHostException uhe) {
 654             invalid = true;
 655             throw uhe;
 656         }
 657     }
 658 
 659     private boolean match(String cname, String hname) {
 660         String a = cname.toLowerCase();
 661         String b = hname.toLowerCase();
 662         if (a.startsWith(b)  &&
 663             ((a.length() == b.length()) || (a.charAt(b.length()) == '.')))
 664             return true;
 665         if (b.endsWith(".akamai.net") || b.endsWith(".akamai.com"))
 666             return true;
 667         String af = fragment(a);
 668         String bf = fragment(b);
 669         return af.length() != 0 && bf.length() != 0 && fragment(a).equals(fragment(b));
 670     }
 671 
 672     // www.sun.com. -> sun.com
 673     // www.sun.co.uk -> sun.co.uk
 674     // www.sun.com.au -> sun.com.au
 675     private String fragment(String cname) {
 676         int dot;
 677         dot = cname.lastIndexOf('.');
 678         if (dot == -1)
 679             return cname;
 680         if (dot == 0)
 681             return "";
 682         if (dot == cname.length() - 1) {
 683             cname = cname.substring(0, cname.length() -1);
 684             dot = cname.lastIndexOf('.');
 685         }
 686         if (dot < 1)
 687             return "";
 688         int second = cname.lastIndexOf('.', dot - 1);
 689         if (second == -1)
 690             return cname;
 691         if (((cname.length() - dot) <= 3) && ((dot - second) <= 4) && second > 0) {
 692             if (dot - second == 4) {
 693                 String s = cname.substring(second + 1, dot);
 694                 if (!(s.equals("com") || s.equals("org") || s.equals("edu"))) {
 695                     return cname.substring(second + 1);
 696                 }
 697             }
 698             int third = cname.lastIndexOf('.', second - 1);
 699             if (third == -1)
 700                 return cname.substring(second + 1);
 701             else
 702                 return cname.substring(third + 1);
 703         }
 704         return cname.substring(second + 1);
 705     }
 706 
 707     private boolean authorized(String cname, byte[] addr) {
 708         if (addr.length == 4)
 709             return authorizedIPv4(cname, addr);
 710         else if (addr.length == 16)
 711             return authorizedIPv6(cname, addr);
 712         else
 713             return false;
 714     }
 715 
 716     private boolean authorizedIPv4(String cname, byte[] addr) {
 717         String authHost = "";
 718         InetAddress auth;
 719 
 720         try {
 721             authHost = "auth." + 
 722                         (addr[3] & 0xff) + "." + (addr[2] & 0xff) + "." + 
 723                         (addr[1] & 0xff) + "." + (addr[0] & 0xff) +
 724                         ".in-addr.arpa";
 725             //auth = InetAddress.getAllByName0(authHost, false)[0];
 726             authHost = hostname + '.' + authHost;
 727             auth = InetAddress.getAllByName0(authHost, false)[0];
 728             if (auth.equals(InetAddress.getByAddress(addr)))
 729                 return true;
 730             Debug debug = getDebug();
 731             if (debug != null && Debug.isOn("failure")) {
 732                 debug.println("socket access restriction: IP address of " + auth + " != " + InetAddress.getByAddress(addr));
 733             }
 734         } catch (UnknownHostException uhe) {
 735             Debug debug = getDebug();
 736             if (debug != null && Debug.isOn("failure")) {
 737                 debug.println("socket access restriction: forward lookup failed for " + authHost);
 738             }
 739         } catch (IOException x) {
 740         }
 741         return false;
 742     }
 743 
 744     private boolean authorizedIPv6(String cname, byte[] addr) {
 745         String authHost = "";
 746         InetAddress auth;
 747 
 748         try {
 749             StringBuffer sb = new StringBuffer(39);
 750 
 751             for (int i = 15; i >= 0; i--) {
 752                 sb.append(Integer.toHexString(((addr[i]) & 0x0f)));
 753                 sb.append('.');
 754                 sb.append(Integer.toHexString(((addr[i] >> 4) & 0x0f)));
 755                 sb.append('.');
 756             }
 757             authHost = "auth." + sb.toString() + "IP6.ARPA";
 758             //auth = InetAddress.getAllByName0(authHost, false)[0];
 759             authHost = hostname + '.' + authHost;
 760             auth = InetAddress.getAllByName0(authHost, false)[0];
 761             if (auth.equals(InetAddress.getByAddress(addr)))
 762                 return true;
 763             Debug debug = getDebug();
 764             if (debug != null && Debug.isOn("failure")) {
 765                 debug.println("socket access restriction: IP address of " + auth + " != " + InetAddress.getByAddress(addr));
 766             }
 767         } catch (UnknownHostException uhe) {
 768             Debug debug = getDebug();
 769             if (debug != null && Debug.isOn("failure")) {
 770                 debug.println("socket access restriction: forward lookup failed for " + authHost);
 771             }
 772         } catch (IOException x) {
 773         }
 774         return false;
 775     }
 776 
 777     /**
 778      * get IP addresses. Sets invalid to true if we can't get them.
 779      *
 780      */
 781     void getIP()
 782         throws UnknownHostException
 783     {
 784         if (addresses != null || wildcard || invalid) return;
 785 
 786         try {
 787             // now get all the IP addresses
 788             String host;
 789             if (getName().charAt(0) == '[') {
 790                 // Literal IPv6 address
 791                 host = getName().substring(1, getName().indexOf(']'));
 792             } else {
 793                 int i = getName().indexOf(":");
 794                 if (i == -1)
 795                     host = getName();
 796                 else {
 797                     host = getName().substring(0,i);
 798                 }
 799             }
 800 
 801             addresses =
 802                 new InetAddress[] {InetAddress.getAllByName0(host, false)[0]};
 803 
 804         } catch (UnknownHostException uhe) {
 805             invalid = true;
 806             throw uhe;
 807         }  catch (IndexOutOfBoundsException iobe) {
 808             invalid = true;
 809             throw new UnknownHostException(getName());
 810         }
 811     }
 812 
 813     /**
 814      * Checks if this socket permission object "implies" the
 815      * specified permission.
 816      * <P>
 817      * More specifically, this method first ensures that all of the following
 818      * are true (and returns false if any of them are not):<p>
 819      * <ul>
 820      * <li> <i>p</i> is an instanceof SocketPermission,<p>
 821      * <li> <i>p</i>'s actions are a proper subset of this
 822      * object's actions, and<p>
 823      * <li> <i>p</i>'s port range is included in this port range. Note:
 824      * port range is ignored when p only contains the action, 'resolve'.<p>
 825      * </ul>
 826      *
 827      * Then <code>implies</code> checks each of the following, in order,
 828      * and for each returns true if the stated condition is true:<p>
 829      * <ul>
 830      * <li> If this object was initialized with a single IP address and one of <i>p</i>'s
 831      * IP addresses is equal to this object's IP address.<p>
 832      * <li>If this object is a wildcard domain (such as *.sun.com), and
 833      * <i>p</i>'s canonical name (the name without any preceding *)
 834      * ends with this object's canonical host name. For example, *.sun.com
 835      * implies *.eng.sun.com..<p>
 836      * <li>If this object was not initialized with a single IP address, and one of this
 837      * object's IP addresses equals one of <i>p</i>'s IP addresses.<p>
 838      * <li>If this canonical name equals <i>p</i>'s canonical name.<p>
 839      * </ul>
 840      *
 841      * If none of the above are true, <code>implies</code> returns false.
 842      * @param p the permission to check against.
 843      *
 844      * @return true if the specified permission is implied by this object,
 845      * false if not.
 846      */
 847 
 848     public boolean implies(Permission p) {
 849         int i,j;
 850 
 851         if (!(p instanceof SocketPermission))
 852             return false;
 853 
 854         if (p == this)
 855             return true;
 856 
 857         SocketPermission that = (SocketPermission) p;
 858 
 859         return ((this.mask & that.mask) == that.mask) &&
 860                                         impliesIgnoreMask(that);
 861     }
 862 
 863     /**
 864      * Checks if the incoming Permission's action are a proper subset of
 865      * the this object's actions.
 866      * <P>
 867      * Check, in the following order:
 868      * <ul>
 869      * <li> Checks that "p" is an instanceof a SocketPermission
 870      * <li> Checks that "p"'s actions are a proper subset of the
 871      * current object's actions.
 872      * <li> Checks that "p"'s port range is included in this port range
 873      * <li> If this object was initialized with an IP address, checks that
 874      *      one of "p"'s IP addresses is equal to this object's IP address.
 875      * <li> If either object is a wildcard domain (i.e., "*.sun.com"),
 876      *      attempt to match based on the wildcard.
 877      * <li> If this object was not initialized with an IP address, attempt
 878      *      to find a match based on the IP addresses in both objects.
 879      * <li> Attempt to match on the canonical hostnames of both objects.
 880      * </ul>
 881      * @param p the incoming permission request
 882      *
 883      * @return true if "permission" is a proper subset of the current object,
 884      * false if not.
 885      */
 886 
 887     boolean impliesIgnoreMask(SocketPermission that) {
 888         int i,j;
 889 
 890         if ((that.mask & RESOLVE) != that.mask) {
 891 
 892             // check simple port range
 893             if ((that.portrange[0] < this.portrange[0]) ||
 894                     (that.portrange[1] > this.portrange[1])) {
 895 
 896                 // if either includes the ephemeral range, do full check
 897                 if (this.includesEphemerals() || that.includesEphemerals()) {
 898                     if (!inRange(this.portrange[0], this.portrange[1],
 899                                      that.portrange[0], that.portrange[1]))
 900                     {
 901                                 return false;
 902                     }
 903                 } else {
 904                     return false;
 905                 }
 906             }
 907         }
 908 
 909         // allow a "*" wildcard to always match anything
 910         if (this.wildcard && "".equals(this.cname))
 911             return true;
 912 
 913         // return if either one of these NetPerm objects are invalid...
 914         if (this.invalid || that.invalid) {
 915             return (trustProxy ? inProxyWeTrust(that) : false);
 916         }
 917 
 918 
 919         if (this.getName().equalsIgnoreCase(that.getName())) {
 920             return true;
 921         }
 922 
 923         try {
 924             if (this.init_with_ip) { // we only check IP addresses
 925                 if (that.wildcard)
 926                     return false;
 927 
 928                 if (that.init_with_ip) {
 929                     return (this.addresses[0].equals(that.addresses[0]));
 930                 } else {
 931                     if (that.addresses == null) {
 932                         that.getIP();
 933                     }
 934                     for (i=0; i < that.addresses.length; i++) {
 935                         if (this.addresses[0].equals(that.addresses[i]))
 936                             return true;
 937                     }
 938                 }
 939                 // since "this" was initialized with an IP address, we
 940                 // don't check any other cases
 941                 return false;
 942             }
 943 
 944             // check and see if we have any wildcards...
 945             if (this.wildcard || that.wildcard) {
 946                 // if they are both wildcards, return true iff
 947                 // that's cname ends with this cname (i.e., *.sun.com
 948                 // implies *.eng.sun.com)
 949                 if (this.wildcard && that.wildcard)
 950                     return (that.cname.endsWith(this.cname));
 951 
 952                 // a non-wildcard can't imply a wildcard
 953                 if (that.wildcard)
 954                     return false;
 955 
 956                 // this is a wildcard, lets see if that's cname ends with
 957                 // it...
 958                 if (that.cname == null) {
 959                     that.getCanonName();
 960                 }
 961                 return (that.cname.endsWith(this.cname));
 962             }
 963 
 964             if (this.cname == null) {
 965                 this.getCanonName();
 966             }
 967 
 968             // comapare IP addresses
 969             if (this.addresses == null) {
 970                 this.getIP();
 971             }
 972 
 973             if (that.addresses == null) {
 974                 that.getIP();
 975             }
 976 
 977             if (!(that.init_with_ip && this.untrusted)) {
 978                 for (j = 0; j < this.addresses.length; j++) {
 979                     for (i=0; i < that.addresses.length; i++) {
 980                         if (this.addresses[j].equals(that.addresses[i]))
 981                             return true;
 982                     }
 983                 }
 984 
 985                 // XXX: if all else fails, compare hostnames?
 986                 // Do we really want this?
 987                 if (that.cname == null) {
 988                     that.getCanonName();
 989                 }
 990 
 991                 return (this.cname.equalsIgnoreCase(that.cname));
 992             }
 993 
 994         } catch (UnknownHostException uhe) {
 995             if (trustProxy)
 996                 return inProxyWeTrust(that);
 997         }
 998 
 999         // make sure the first thing that is done here is to return
1000         // false. If not, uncomment the return false in the above catch.
1001 
1002         return false;
1003     }
1004 
1005     private boolean inProxyWeTrust(SocketPermission that) {
1006         // if we trust the proxy, we see if the original names/IPs passed
1007         // in were equal.
1008 
1009         String thisHost = hostname;
1010         String thatHost = that.hostname;
1011 
1012         if (thisHost == null)
1013             return false;
1014         else
1015             return thisHost.equalsIgnoreCase(thatHost);
1016 
1017     }
1018     /**
1019      * Checks two SocketPermission objects for equality.
1020      * <P>
1021      * @param obj the object to test for equality with this object.
1022      *
1023      * @return true if <i>obj</i> is a SocketPermission, and has the
1024      *  same hostname, port range, and actions as this
1025      *  SocketPermission object. However, port range will be ignored
1026      *  in the comparison if <i>obj</i> only contains the action, 'resolve'.
1027      */
1028     public boolean equals(Object obj) {
1029         if (obj == this)
1030             return true;
1031 
1032         if (! (obj instanceof SocketPermission))
1033             return false;
1034 
1035         SocketPermission that = (SocketPermission) obj;
1036 
1037         //this is (overly?) complex!!!
1038 
1039         // check the mask first
1040         if (this.mask != that.mask) return false;
1041 
1042         if ((that.mask & RESOLVE) != that.mask) {
1043             // now check the port range...
1044             if ((this.portrange[0] != that.portrange[0]) ||
1045                 (this.portrange[1] != that.portrange[1])) {
1046                 return false;
1047             }
1048         }
1049 
1050         // short cut. This catches:
1051         //  "crypto" equal to "crypto", or
1052         // "1.2.3.4" equal to "1.2.3.4.", or
1053         //  "*.edu" equal to "*.edu", but it
1054         //  does not catch "crypto" equal to
1055         // "crypto.eng.sun.com".
1056 
1057         if (this.getName().equalsIgnoreCase(that.getName())) {
1058             return true;
1059         }
1060 
1061         // we now attempt to get the Canonical (FQDN) name and
1062         // compare that. If this fails, about all we can do is return
1063         // false.
1064 
1065         try {
1066             this.getCanonName();
1067             that.getCanonName();
1068         } catch (UnknownHostException uhe) {
1069             return false;
1070         }
1071 
1072         if (this.invalid || that.invalid)
1073             return false;
1074 
1075         if (this.cname != null) {
1076             return this.cname.equalsIgnoreCase(that.cname);
1077         }
1078 
1079         return false;
1080     }
1081 
1082     /**
1083      * Returns the hash code value for this object.
1084      *
1085      * @return a hash code value for this object.
1086      */
1087 
1088     public int hashCode() {
1089         /*
1090          * If this SocketPermission was initialized with an IP address
1091          * or a wildcard, use getName().hashCode(), otherwise use
1092          * the hashCode() of the host name returned from
1093          * java.net.InetAddress.getHostName method.
1094          */
1095 
1096         if (init_with_ip || wildcard) {
1097             return this.getName().hashCode();
1098         }
1099 
1100         try {
1101             getCanonName();
1102         } catch (UnknownHostException uhe) {
1103 
1104         }
1105 
1106         if (invalid || cname == null)
1107             return this.getName().hashCode();
1108         else
1109             return this.cname.hashCode();
1110     }
1111 
1112     /**
1113      * Return the current action mask.
1114      *
1115      * @return the actions mask.
1116      */
1117 
1118     int getMask() {
1119         return mask;
1120     }
1121 
1122     /**
1123      * Returns the "canonical string representation" of the actions in the
1124      * specified mask.
1125      * Always returns present actions in the following order:
1126      * connect, listen, accept, resolve.
1127      *
1128      * @param mask a specific integer action mask to translate into a string
1129      * @return the canonical string representation of the actions
1130      */
1131     private static String getActions(int mask)
1132     {
1133         StringBuilder sb = new StringBuilder();
1134         boolean comma = false;
1135 
1136         if ((mask & CONNECT) == CONNECT) {
1137             comma = true;
1138             sb.append("connect");
1139         }
1140 
1141         if ((mask & LISTEN) == LISTEN) {
1142             if (comma) sb.append(',');
1143             else comma = true;
1144             sb.append("listen");
1145         }
1146 
1147         if ((mask & ACCEPT) == ACCEPT) {
1148             if (comma) sb.append(',');
1149             else comma = true;
1150             sb.append("accept");
1151         }
1152 
1153 
1154         if ((mask & RESOLVE) == RESOLVE) {
1155             if (comma) sb.append(',');
1156             else comma = true;
1157             sb.append("resolve");
1158         }
1159 
1160         return sb.toString();
1161     }
1162 
1163     /**
1164      * Returns the canonical string representation of the actions.
1165      * Always returns present actions in the following order:
1166      * connect, listen, accept, resolve.
1167      *
1168      * @return the canonical string representation of the actions.
1169      */
1170     public String getActions()
1171     {
1172         if (actions == null)
1173             actions = getActions(this.mask);
1174 
1175         return actions;
1176     }
1177 
1178     /**
1179      * Returns a new PermissionCollection object for storing SocketPermission
1180      * objects.
1181      * <p>
1182      * SocketPermission objects must be stored in a manner that allows them
1183      * to be inserted into the collection in any order, but that also enables the
1184      * PermissionCollection <code>implies</code>
1185      * method to be implemented in an efficient (and consistent) manner.
1186      *
1187      * @return a new PermissionCollection object suitable for storing SocketPermissions.
1188      */
1189 
1190     public PermissionCollection newPermissionCollection() {
1191         return new SocketPermissionCollection();
1192     }
1193 
1194     /**
1195      * WriteObject is called to save the state of the SocketPermission
1196      * to a stream. The actions are serialized, and the superclass
1197      * takes care of the name.
1198      */
1199     private synchronized void writeObject(java.io.ObjectOutputStream s)
1200         throws IOException
1201     {
1202         // Write out the actions. The superclass takes care of the name
1203         // call getActions to make sure actions field is initialized
1204         if (actions == null)
1205             getActions();
1206         s.defaultWriteObject();
1207     }
1208 
1209     /**
1210      * readObject is called to restore the state of the SocketPermission from
1211      * a stream.
1212      */
1213     private synchronized void readObject(java.io.ObjectInputStream s)
1214          throws IOException, ClassNotFoundException
1215     {
1216         // Read in the action, then initialize the rest
1217         s.defaultReadObject();
1218         init(getName(),getMask(actions));
1219     }
1220 
1221     /**
1222      * Check the system/security property for the ephemeral port range
1223      * for this system. The suffix is either "high" or "low"
1224      */
1225     private static int initEphemeralPorts(
1226         final String suffix, final int defval
1227     )
1228     {
1229         return AccessController.doPrivileged(
1230             new PrivilegedAction<Integer>(){
1231                 public Integer run() {
1232                     int val = Integer.getInteger(
1233                             "jdk.net.ephemeralPortRange."+suffix, -1
1234                     );
1235                     if (val != -1) {
1236                         return val;
1237                     } else {
1238                         String prop = Security.getProperty(
1239                             "network.ephemeralPortRange."+suffix
1240                         );
1241                         try {
1242                                 val = Integer.parseInt(prop);
1243                         } catch (NumberFormatException e) {
1244                             // shouldn't happen
1245                             return defval;
1246                         }
1247                     }
1248                     return val;
1249                 }
1250             }
1251         );
1252     }
1253 
1254     /**
1255      * Check if the target range is within the policy range
1256      * together with the ephemeral range for this platform
1257      * (if policy includes ephemeral range)
1258      */
1259     private static boolean inRange(
1260         int policyLow, int policyHigh, int targetLow, int targetHigh
1261     )
1262     {
1263         if (targetLow == 0) {
1264             // check policy includes ephemeral range
1265             if (!inRange(policyLow, policyHigh, ephemeralLow, ephemeralHigh)) {
1266                 return false;
1267             }
1268             if (targetHigh == 0) {
1269                 // nothing left to do
1270                 return true;
1271             }
1272             // continue check with first real port number
1273             targetLow = 1;
1274         }
1275 
1276         if (policyLow == 0 && policyHigh == 0) {
1277             // ephemeral range only
1278             return targetLow >= ephemeralLow && targetHigh <= ephemeralHigh;
1279         }
1280 
1281         if (policyLow != 0) {
1282             // simple check of policy only
1283             return targetLow >= policyLow && targetHigh <= policyHigh;
1284         }
1285 
1286         // policyLow == 0 which means possibly two ranges to check
1287 
1288         // first check if policy and ephem range overlap/contiguous
1289 
1290         if (policyHigh >= ephemeralLow - 1) {
1291             return targetHigh <= ephemeralHigh;
1292         }
1293 
1294         // policy and ephem range do not overlap
1295 
1296         // target range must lie entirely inside policy range or eph range
1297 
1298         return  (targetLow <= policyHigh && targetHigh <= policyHigh) ||
1299                 (targetLow >= ephemeralLow && targetHigh <= ephemeralHigh);
1300     }
1301     /*
1302     public String toString()
1303     {
1304         StringBuffer s = new StringBuffer(super.toString() + "\n" +
1305             "cname = " + cname + "\n" +
1306             "wildcard = " + wildcard + "\n" +
1307             "invalid = " + invalid + "\n" +
1308             "portrange = " + portrange[0] + "," + portrange[1] + "\n");
1309         if (addresses != null) for (int i=0; i<addresses.length; i++) {
1310             s.append( addresses[i].getHostAddress());
1311             s.append("\n");
1312         } else {
1313             s.append("(no addresses)\n");
1314         }
1315 
1316         return s.toString();
1317     }
1318 
1319     public static void main(String args[]) throws Exception {
1320         SocketPermission this_ = new SocketPermission(args[0], "connect");
1321         SocketPermission that_ = new SocketPermission(args[1], "connect");
1322         System.out.println("-----\n");
1323         System.out.println("this.implies(that) = " + this_.implies(that_));
1324         System.out.println("-----\n");
1325         System.out.println("this = "+this_);
1326         System.out.println("-----\n");
1327         System.out.println("that = "+that_);
1328         System.out.println("-----\n");
1329 
1330         SocketPermissionCollection nps = new SocketPermissionCollection();
1331         nps.add(this_);
1332         nps.add(new SocketPermission("www-leland.stanford.edu","connect"));
1333         nps.add(new SocketPermission("www-sun.com","connect"));
1334         System.out.println("nps.implies(that) = " + nps.implies(that_));
1335         System.out.println("-----\n");
1336     }
1337     */
1338 }
1339 
1340 /**
1341 
1342 if (init'd with IP, key is IP as string)
1343 if wildcard, its the wild card
1344 else its the cname?
1345 
1346  *
1347  * @see java.security.Permission
1348  * @see java.security.Permissions
1349  * @see java.security.PermissionCollection
1350  *
1351  *
1352  * @author Roland Schemers
1353  *
1354  * @serial include
1355  */
1356 
1357 final class SocketPermissionCollection extends PermissionCollection
1358 implements Serializable
1359 {
1360     // Not serialized; see serialization section at end of class
1361     private transient List perms;
1362 
1363     /**
1364      * Create an empty SocketPermissions object.
1365      *
1366      */
1367 
1368     public SocketPermissionCollection() {
1369         perms = new ArrayList();
1370     }
1371 
1372     /**
1373      * Adds a permission to the SocketPermissions. The key for the hash is
1374      * the name in the case of wildcards, or all the IP addresses.
1375      *
1376      * @param permission the Permission object to add.
1377      *
1378      * @exception IllegalArgumentException - if the permission is not a
1379      *                                       SocketPermission
1380      *
1381      * @exception SecurityException - if this SocketPermissionCollection object
1382      *                                has been marked readonly
1383      */
1384 
1385     public void add(Permission permission)
1386     {
1387         if (! (permission instanceof SocketPermission))
1388             throw new IllegalArgumentException("invalid permission: "+
1389                                                permission);
1390         if (isReadOnly())
1391             throw new SecurityException(
1392                 "attempt to add a Permission to a readonly PermissionCollection");
1393 
1394         // optimization to ensure perms most likely to be tested
1395         // show up early (4301064)
1396         synchronized (this) {
1397             perms.add(0, permission);
1398         }
1399     }
1400 
1401     /**
1402      * Check and see if this collection of permissions implies the permissions
1403      * expressed in "permission".
1404      *
1405      * @param p the Permission object to compare
1406      *
1407      * @return true if "permission" is a proper subset of a permission in
1408      * the collection, false if not.
1409      */
1410 
1411     public boolean implies(Permission permission)
1412     {
1413         if (! (permission instanceof SocketPermission))
1414                 return false;
1415 
1416         SocketPermission np = (SocketPermission) permission;
1417 
1418         int desired = np.getMask();
1419         int effective = 0;
1420         int needed = desired;
1421 
1422         synchronized (this) {
1423             int len = perms.size();
1424             //System.out.println("implies "+np);
1425             for (int i = 0; i < len; i++) {
1426                 SocketPermission x = (SocketPermission) perms.get(i);
1427                 //System.out.println("  trying "+x);
1428                 if (((needed & x.getMask()) != 0) && x.impliesIgnoreMask(np)) {
1429                     effective |=  x.getMask();
1430                     if ((effective & desired) == desired)
1431                         return true;
1432                     needed = (desired ^ effective);
1433                 }
1434             }
1435         }
1436         return false;
1437     }
1438 
1439     /**
1440      * Returns an enumeration of all the SocketPermission objects in the
1441      * container.
1442      *
1443      * @return an enumeration of all the SocketPermission objects.
1444      */
1445 
1446     public Enumeration elements() {
1447         // Convert Iterator into Enumeration
1448         synchronized (this) {
1449             return Collections.enumeration(perms);
1450         }
1451     }
1452 
1453     private static final long serialVersionUID = 2787186408602843674L;
1454 
1455     // Need to maintain serialization interoperability with earlier releases,
1456     // which had the serializable field:
1457 
1458     //
1459     // The SocketPermissions for this set.
1460     // @serial
1461     //
1462     // private Vector permissions;
1463 
1464     /**
1465      * @serialField permissions java.util.Vector
1466      *     A list of the SocketPermissions for this set.
1467      */
1468     private static final ObjectStreamField[] serialPersistentFields = {
1469         new ObjectStreamField("permissions", Vector.class),
1470     };
1471 
1472     /**
1473      * @serialData "permissions" field (a Vector containing the SocketPermissions).
1474      */
1475     /*
1476      * Writes the contents of the perms field out as a Vector for
1477      * serialization compatibility with earlier releases.
1478      */
1479     private void writeObject(ObjectOutputStream out) throws IOException {
1480         // Don't call out.defaultWriteObject()
1481 
1482         // Write out Vector
1483         Vector permissions = new Vector(perms.size());
1484 
1485         synchronized (this) {
1486             permissions.addAll(perms);
1487         }
1488 
1489         ObjectOutputStream.PutField pfields = out.putFields();
1490         pfields.put("permissions", permissions);
1491         out.writeFields();
1492     }
1493 
1494     /*
1495      * Reads in a Vector of SocketPermissions and saves them in the perms field.
1496      */
1497     private void readObject(ObjectInputStream in) throws IOException,
1498     ClassNotFoundException {
1499         // Don't call in.defaultReadObject()
1500 
1501         // Read in serialized fields
1502         ObjectInputStream.GetField gfields = in.readFields();
1503 
1504         // Get the one we want
1505         Vector permissions = (Vector)gfields.get("permissions", null);
1506         perms = new ArrayList(permissions.size());
1507         perms.addAll(permissions);
1508     }
1509 }