1 /*
   2  * Copyright (c) 1997, 2011, 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     @SuppressWarnings("fallthrough")
 496     private static int getMask(String action) {
 497 
 498         if (action == null) {
 499             throw new NullPointerException("action can't be null");
 500         }
 501 
 502         if (action.equals("")) {
 503             throw new IllegalArgumentException("action can't be empty");
 504         }
 505 
 506         int mask = NONE;
 507 
 508         // Check against use of constants (used heavily within the JDK)
 509         if (action == SecurityConstants.SOCKET_RESOLVE_ACTION) {
 510             return RESOLVE;
 511         } else if (action == SecurityConstants.SOCKET_CONNECT_ACTION) {
 512             return CONNECT;
 513         } else if (action == SecurityConstants.SOCKET_LISTEN_ACTION) {
 514             return LISTEN;
 515         } else if (action == SecurityConstants.SOCKET_ACCEPT_ACTION) {
 516             return ACCEPT;
 517         } else if (action == SecurityConstants.SOCKET_CONNECT_ACCEPT_ACTION) {
 518             return CONNECT|ACCEPT;
 519         }
 520 
 521         char[] a = action.toCharArray();
 522 
 523         int i = a.length - 1;
 524         if (i < 0)
 525             return mask;
 526 
 527         while (i != -1) {
 528             char c;
 529 
 530             // skip whitespace
 531             while ((i!=-1) && ((c = a[i]) == ' ' ||
 532                                c == '\r' ||
 533                                c == '\n' ||
 534                                c == '\f' ||
 535                                c == '\t'))
 536                 i--;
 537 
 538             // check for the known strings
 539             int matchlen;
 540 
 541             if (i >= 6 && (a[i-6] == 'c' || a[i-6] == 'C') &&
 542                           (a[i-5] == 'o' || a[i-5] == 'O') &&
 543                           (a[i-4] == 'n' || a[i-4] == 'N') &&
 544                           (a[i-3] == 'n' || a[i-3] == 'N') &&
 545                           (a[i-2] == 'e' || a[i-2] == 'E') &&
 546                           (a[i-1] == 'c' || a[i-1] == 'C') &&
 547                           (a[i] == 't' || a[i] == 'T'))
 548             {
 549                 matchlen = 7;
 550                 mask |= CONNECT;
 551 
 552             } else if (i >= 6 && (a[i-6] == 'r' || a[i-6] == 'R') &&
 553                                  (a[i-5] == 'e' || a[i-5] == 'E') &&
 554                                  (a[i-4] == 's' || a[i-4] == 'S') &&
 555                                  (a[i-3] == 'o' || a[i-3] == 'O') &&
 556                                  (a[i-2] == 'l' || a[i-2] == 'L') &&
 557                                  (a[i-1] == 'v' || a[i-1] == 'V') &&
 558                                  (a[i] == 'e' || a[i] == 'E'))
 559             {
 560                 matchlen = 7;
 561                 mask |= RESOLVE;
 562 
 563             } else if (i >= 5 && (a[i-5] == 'l' || a[i-5] == 'L') &&
 564                                  (a[i-4] == 'i' || a[i-4] == 'I') &&
 565                                  (a[i-3] == 's' || a[i-3] == 'S') &&
 566                                  (a[i-2] == 't' || a[i-2] == 'T') &&
 567                                  (a[i-1] == 'e' || a[i-1] == 'E') &&
 568                                  (a[i] == 'n' || a[i] == 'N'))
 569             {
 570                 matchlen = 6;
 571                 mask |= LISTEN;
 572 
 573             } else if (i >= 5 && (a[i-5] == 'a' || a[i-5] == 'A') &&
 574                                  (a[i-4] == 'c' || a[i-4] == 'C') &&
 575                                  (a[i-3] == 'c' || a[i-3] == 'C') &&
 576                                  (a[i-2] == 'e' || a[i-2] == 'E') &&
 577                                  (a[i-1] == 'p' || a[i-1] == 'P') &&
 578                                  (a[i] == 't' || a[i] == 'T'))
 579             {
 580                 matchlen = 6;
 581                 mask |= ACCEPT;
 582 
 583             } else {
 584                 // parse error
 585                 throw new IllegalArgumentException(
 586                         "invalid permission: " + action);
 587             }
 588 
 589             // make sure we didn't just match the tail of a word
 590             // like "ackbarfaccept".  Also, skip to the comma.
 591             boolean seencomma = false;
 592             while (i >= matchlen && !seencomma) {
 593                 switch(a[i-matchlen]) {
 594                 case ',':
 595                     seencomma = true;
 596                     /*FALLTHROUGH*/
 597                 case ' ': case '\r': case '\n':
 598                 case '\f': case '\t':
 599                     break;
 600                 default:
 601                     throw new IllegalArgumentException(
 602                             "invalid permission: " + action);
 603                 }
 604                 i--;
 605             }
 606 
 607             // point i at the location of the comma minus one (or -1).
 608             i -= matchlen;
 609         }
 610 
 611         return mask;
 612     }
 613 
 614     /**
 615      * attempt to get the fully qualified domain name
 616      *
 617      */
 618     void getCanonName()
 619         throws UnknownHostException
 620     {
 621         if (cname != null || invalid || untrusted) return;
 622 
 623         // attempt to get the canonical name
 624 
 625         try {
 626             // first get the IP addresses if we don't have them yet
 627             // this is because we need the IP address to then get
 628             // FQDN.
 629             if (addresses == null) {
 630                 getIP();
 631             }
 632 
 633             // we have to do this check, otherwise we might not
 634             // get the fully qualified domain name
 635             if (init_with_ip) {
 636                 cname = addresses[0].getHostName(false).toLowerCase();
 637             } else {
 638                 cname = InetAddress.getByName(addresses[0].getHostAddress()).
 639                                               getHostName(false).toLowerCase();
 640                 if (!trustNameService && sun.net.www.URLConnection.isProxiedHost(hostname)) {
 641                     if (!match(cname, hostname) && 
 642                         (defaultDeny || !cname.equals(addresses[0].getHostAddress()))) {
 643                         // Last chance
 644                         if (!authorized(hostname, addresses[0].getAddress())) {
 645                             untrusted = true;
 646                             Debug debug = getDebug();
 647                             if (debug != null && Debug.isOn("failure")) {
 648                                 debug.println("socket access restriction: proxied host " + "(" + addresses[0] + ")" + " does not match " + cname + " from reverse lookup");
 649                             }
 650                         }
 651                     }
 652                 }
 653             }
 654         } catch (UnknownHostException uhe) {
 655             invalid = true;
 656             throw uhe;
 657         }
 658     }
 659 
 660     private boolean match(String cname, String hname) {
 661         String a = cname.toLowerCase();
 662         String b = hname.toLowerCase();
 663         if (a.startsWith(b)  &&
 664             ((a.length() == b.length()) || (a.charAt(b.length()) == '.')))
 665             return true;
 666         if (b.endsWith(".akamai.net") || b.endsWith(".akamai.com"))
 667             return true;
 668         String af = fragment(a);
 669         String bf = fragment(b);
 670         return af.length() != 0 && bf.length() != 0 && fragment(a).equals(fragment(b));
 671     }
 672 
 673     // www.sun.com. -> sun.com
 674     // www.sun.co.uk -> sun.co.uk
 675     // www.sun.com.au -> sun.com.au
 676     private String fragment(String cname) {
 677         int dot;
 678         dot = cname.lastIndexOf('.');
 679         if (dot == -1)
 680             return cname;
 681         if (dot == 0)
 682             return "";
 683         if (dot == cname.length() - 1) {
 684             cname = cname.substring(0, cname.length() -1);
 685             dot = cname.lastIndexOf('.');
 686         }
 687         if (dot < 1)
 688             return "";
 689         int second = cname.lastIndexOf('.', dot - 1);
 690         if (second == -1)
 691             return cname;
 692         if (((cname.length() - dot) <= 3) && ((dot - second) <= 4) && second > 0) {
 693             if (dot - second == 4) {
 694                 String s = cname.substring(second + 1, dot);
 695                 if (!(s.equals("com") || s.equals("org") || s.equals("edu"))) {
 696                     return cname.substring(second + 1);
 697                 }
 698             }
 699             int third = cname.lastIndexOf('.', second - 1);
 700             if (third == -1)
 701                 return cname.substring(second + 1);
 702             else
 703                 return cname.substring(third + 1);
 704         }
 705         return cname.substring(second + 1);
 706     }
 707 
 708     private boolean authorized(String cname, byte[] addr) {
 709         if (addr.length == 4)
 710             return authorizedIPv4(cname, addr);
 711         else if (addr.length == 16)
 712             return authorizedIPv6(cname, addr);
 713         else
 714             return false;
 715     }
 716 
 717     private boolean authorizedIPv4(String cname, byte[] addr) {
 718         String authHost = "";
 719         InetAddress auth;
 720 
 721         try {
 722             authHost = "auth." + 
 723                         (addr[3] & 0xff) + "." + (addr[2] & 0xff) + "." + 
 724                         (addr[1] & 0xff) + "." + (addr[0] & 0xff) +
 725                         ".in-addr.arpa";
 726             //auth = InetAddress.getAllByName0(authHost, false)[0];
 727             authHost = hostname + '.' + authHost;
 728             auth = InetAddress.getAllByName0(authHost, false)[0];
 729             if (auth.equals(InetAddress.getByAddress(addr)))
 730                 return true;
 731             Debug debug = getDebug();
 732             if (debug != null && Debug.isOn("failure")) {
 733                 debug.println("socket access restriction: IP address of " + auth + " != " + InetAddress.getByAddress(addr));
 734             }
 735         } catch (UnknownHostException uhe) {
 736             Debug debug = getDebug();
 737             if (debug != null && Debug.isOn("failure")) {
 738                 debug.println("socket access restriction: forward lookup failed for " + authHost);
 739             }
 740         } catch (IOException x) {
 741         }
 742         return false;
 743     }
 744 
 745     private boolean authorizedIPv6(String cname, byte[] addr) {
 746         String authHost = "";
 747         InetAddress auth;
 748 
 749         try {
 750             StringBuffer sb = new StringBuffer(39);
 751 
 752             for (int i = 15; i >= 0; i--) {
 753                 sb.append(Integer.toHexString(((addr[i]) & 0x0f)));
 754                 sb.append('.');
 755                 sb.append(Integer.toHexString(((addr[i] >> 4) & 0x0f)));
 756                 sb.append('.');
 757             }
 758             authHost = "auth." + sb.toString() + "IP6.ARPA";
 759             //auth = InetAddress.getAllByName0(authHost, false)[0];
 760             authHost = hostname + '.' + authHost;
 761             auth = InetAddress.getAllByName0(authHost, false)[0];
 762             if (auth.equals(InetAddress.getByAddress(addr)))
 763                 return true;
 764             Debug debug = getDebug();
 765             if (debug != null && Debug.isOn("failure")) {
 766                 debug.println("socket access restriction: IP address of " + auth + " != " + InetAddress.getByAddress(addr));
 767             }
 768         } catch (UnknownHostException uhe) {
 769             Debug debug = getDebug();
 770             if (debug != null && Debug.isOn("failure")) {
 771                 debug.println("socket access restriction: forward lookup failed for " + authHost);
 772             }
 773         } catch (IOException x) {
 774         }
 775         return false;
 776     }
 777 
 778     /**
 779      * get IP addresses. Sets invalid to true if we can't get them.
 780      *
 781      */
 782     void getIP()
 783         throws UnknownHostException
 784     {
 785         if (addresses != null || wildcard || invalid) return;
 786 
 787         try {
 788             // now get all the IP addresses
 789             String host;
 790             if (getName().charAt(0) == '[') {
 791                 // Literal IPv6 address
 792                 host = getName().substring(1, getName().indexOf(']'));
 793             } else {
 794                 int i = getName().indexOf(":");
 795                 if (i == -1)
 796                     host = getName();
 797                 else {
 798                     host = getName().substring(0,i);
 799                 }
 800             }
 801 
 802             addresses =
 803                 new InetAddress[] {InetAddress.getAllByName0(host, false)[0]};
 804 
 805         } catch (UnknownHostException uhe) {
 806             invalid = true;
 807             throw uhe;
 808         }  catch (IndexOutOfBoundsException iobe) {
 809             invalid = true;
 810             throw new UnknownHostException(getName());
 811         }
 812     }
 813 
 814     /**
 815      * Checks if this socket permission object "implies" the
 816      * specified permission.
 817      * <P>
 818      * More specifically, this method first ensures that all of the following
 819      * are true (and returns false if any of them are not):<p>
 820      * <ul>
 821      * <li> <i>p</i> is an instanceof SocketPermission,<p>
 822      * <li> <i>p</i>'s actions are a proper subset of this
 823      * object's actions, and<p>
 824      * <li> <i>p</i>'s port range is included in this port range. Note:
 825      * port range is ignored when p only contains the action, 'resolve'.<p>
 826      * </ul>
 827      *
 828      * Then <code>implies</code> checks each of the following, in order,
 829      * and for each returns true if the stated condition is true:<p>
 830      * <ul>
 831      * <li> If this object was initialized with a single IP address and one of <i>p</i>'s
 832      * IP addresses is equal to this object's IP address.<p>
 833      * <li>If this object is a wildcard domain (such as *.sun.com), and
 834      * <i>p</i>'s canonical name (the name without any preceding *)
 835      * ends with this object's canonical host name. For example, *.sun.com
 836      * implies *.eng.sun.com..<p>
 837      * <li>If this object was not initialized with a single IP address, and one of this
 838      * object's IP addresses equals one of <i>p</i>'s IP addresses.<p>
 839      * <li>If this canonical name equals <i>p</i>'s canonical name.<p>
 840      * </ul>
 841      *
 842      * If none of the above are true, <code>implies</code> returns false.
 843      * @param p the permission to check against.
 844      *
 845      * @return true if the specified permission is implied by this object,
 846      * false if not.
 847      */
 848 
 849     public boolean implies(Permission p) {
 850         int i,j;
 851 
 852         if (!(p instanceof SocketPermission))
 853             return false;
 854 
 855         if (p == this)
 856             return true;
 857 
 858         SocketPermission that = (SocketPermission) p;
 859 
 860         return ((this.mask & that.mask) == that.mask) &&
 861                                         impliesIgnoreMask(that);
 862     }
 863 
 864     /**
 865      * Checks if the incoming Permission's action are a proper subset of
 866      * the this object's actions.
 867      * <P>
 868      * Check, in the following order:
 869      * <ul>
 870      * <li> Checks that "p" is an instanceof a SocketPermission
 871      * <li> Checks that "p"'s actions are a proper subset of the
 872      * current object's actions.
 873      * <li> Checks that "p"'s port range is included in this port range
 874      * <li> If this object was initialized with an IP address, checks that
 875      *      one of "p"'s IP addresses is equal to this object's IP address.
 876      * <li> If either object is a wildcard domain (i.e., "*.sun.com"),
 877      *      attempt to match based on the wildcard.
 878      * <li> If this object was not initialized with an IP address, attempt
 879      *      to find a match based on the IP addresses in both objects.
 880      * <li> Attempt to match on the canonical hostnames of both objects.
 881      * </ul>
 882      * @param p the incoming permission request
 883      *
 884      * @return true if "permission" is a proper subset of the current object,
 885      * false if not.
 886      */
 887 
 888     boolean impliesIgnoreMask(SocketPermission that) {
 889         int i,j;
 890 
 891         if ((that.mask & RESOLVE) != that.mask) {
 892 
 893             // check simple port range
 894             if ((that.portrange[0] < this.portrange[0]) ||
 895                     (that.portrange[1] > this.portrange[1])) {
 896 
 897                 // if either includes the ephemeral range, do full check
 898                 if (this.includesEphemerals() || that.includesEphemerals()) {
 899                     if (!inRange(this.portrange[0], this.portrange[1],
 900                                      that.portrange[0], that.portrange[1]))
 901                     {
 902                                 return false;
 903                     }
 904                 } else {
 905                     return false;
 906                 }
 907             }
 908         }
 909 
 910         // allow a "*" wildcard to always match anything
 911         if (this.wildcard && "".equals(this.cname))
 912             return true;
 913 
 914         // return if either one of these NetPerm objects are invalid...
 915         if (this.invalid || that.invalid) {
 916             return (trustProxy ? inProxyWeTrust(that) : false);
 917         }
 918 
 919 
 920         if (this.getName().equalsIgnoreCase(that.getName())) {
 921             return true;
 922         }
 923 
 924         try {
 925             if (this.init_with_ip) { // we only check IP addresses
 926                 if (that.wildcard)
 927                     return false;
 928 
 929                 if (that.init_with_ip) {
 930                     return (this.addresses[0].equals(that.addresses[0]));
 931                 } else {
 932                     if (that.addresses == null) {
 933                         that.getIP();
 934                     }
 935                     for (i=0; i < that.addresses.length; i++) {
 936                         if (this.addresses[0].equals(that.addresses[i]))
 937                             return true;
 938                     }
 939                 }
 940                 // since "this" was initialized with an IP address, we
 941                 // don't check any other cases
 942                 return false;
 943             }
 944 
 945             // check and see if we have any wildcards...
 946             if (this.wildcard || that.wildcard) {
 947                 // if they are both wildcards, return true iff
 948                 // that's cname ends with this cname (i.e., *.sun.com
 949                 // implies *.eng.sun.com)
 950                 if (this.wildcard && that.wildcard)
 951                     return (that.cname.endsWith(this.cname));
 952 
 953                 // a non-wildcard can't imply a wildcard
 954                 if (that.wildcard)
 955                     return false;
 956 
 957                 // this is a wildcard, lets see if that's cname ends with
 958                 // it...
 959                 if (that.cname == null) {
 960                     that.getCanonName();
 961                 }
 962                 return (that.cname.endsWith(this.cname));
 963             }
 964 
 965             if (this.cname == null) {
 966                 this.getCanonName();
 967             }
 968 
 969             // comapare IP addresses
 970             if (this.addresses == null) {
 971                 this.getIP();
 972             }
 973 
 974             if (that.addresses == null) {
 975                 that.getIP();
 976             }
 977 
 978             if (!(that.init_with_ip && this.untrusted)) {
 979                 for (j = 0; j < this.addresses.length; j++) {
 980                     for (i=0; i < that.addresses.length; i++) {
 981                         if (this.addresses[j].equals(that.addresses[i]))
 982                             return true;
 983                     }
 984                 }
 985 
 986                 // XXX: if all else fails, compare hostnames?
 987                 // Do we really want this?
 988                 if (that.cname == null) {
 989                     that.getCanonName();
 990                 }
 991 
 992                 return (this.cname.equalsIgnoreCase(that.cname));
 993             }
 994 
 995         } catch (UnknownHostException uhe) {
 996             if (trustProxy)
 997                 return inProxyWeTrust(that);
 998         }
 999 
1000         // make sure the first thing that is done here is to return
1001         // false. If not, uncomment the return false in the above catch.
1002 
1003         return false;
1004     }
1005 
1006     private boolean inProxyWeTrust(SocketPermission that) {
1007         // if we trust the proxy, we see if the original names/IPs passed
1008         // in were equal.
1009 
1010         String thisHost = hostname;
1011         String thatHost = that.hostname;
1012 
1013         if (thisHost == null)
1014             return false;
1015         else
1016             return thisHost.equalsIgnoreCase(thatHost);
1017 
1018     }
1019     /**
1020      * Checks two SocketPermission objects for equality.
1021      * <P>
1022      * @param obj the object to test for equality with this object.
1023      *
1024      * @return true if <i>obj</i> is a SocketPermission, and has the
1025      *  same hostname, port range, and actions as this
1026      *  SocketPermission object. However, port range will be ignored
1027      *  in the comparison if <i>obj</i> only contains the action, 'resolve'.
1028      */
1029     public boolean equals(Object obj) {
1030         if (obj == this)
1031             return true;
1032 
1033         if (! (obj instanceof SocketPermission))
1034             return false;
1035 
1036         SocketPermission that = (SocketPermission) obj;
1037 
1038         //this is (overly?) complex!!!
1039 
1040         // check the mask first
1041         if (this.mask != that.mask) return false;
1042 
1043         if ((that.mask & RESOLVE) != that.mask) {
1044             // now check the port range...
1045             if ((this.portrange[0] != that.portrange[0]) ||
1046                 (this.portrange[1] != that.portrange[1])) {
1047                 return false;
1048             }
1049         }
1050 
1051         // short cut. This catches:
1052         //  "crypto" equal to "crypto", or
1053         // "1.2.3.4" equal to "1.2.3.4.", or
1054         //  "*.edu" equal to "*.edu", but it
1055         //  does not catch "crypto" equal to
1056         // "crypto.eng.sun.com".
1057 
1058         if (this.getName().equalsIgnoreCase(that.getName())) {
1059             return true;
1060         }
1061 
1062         // we now attempt to get the Canonical (FQDN) name and
1063         // compare that. If this fails, about all we can do is return
1064         // false.
1065 
1066         try {
1067             this.getCanonName();
1068             that.getCanonName();
1069         } catch (UnknownHostException uhe) {
1070             return false;
1071         }
1072 
1073         if (this.invalid || that.invalid)
1074             return false;
1075 
1076         if (this.cname != null) {
1077             return this.cname.equalsIgnoreCase(that.cname);
1078         }
1079 
1080         return false;
1081     }
1082 
1083     /**
1084      * Returns the hash code value for this object.
1085      *
1086      * @return a hash code value for this object.
1087      */
1088 
1089     public int hashCode() {
1090         /*
1091          * If this SocketPermission was initialized with an IP address
1092          * or a wildcard, use getName().hashCode(), otherwise use
1093          * the hashCode() of the host name returned from
1094          * java.net.InetAddress.getHostName method.
1095          */
1096 
1097         if (init_with_ip || wildcard) {
1098             return this.getName().hashCode();
1099         }
1100 
1101         try {
1102             getCanonName();
1103         } catch (UnknownHostException uhe) {
1104 
1105         }
1106 
1107         if (invalid || cname == null)
1108             return this.getName().hashCode();
1109         else
1110             return this.cname.hashCode();
1111     }
1112 
1113     /**
1114      * Return the current action mask.
1115      *
1116      * @return the actions mask.
1117      */
1118 
1119     int getMask() {
1120         return mask;
1121     }
1122 
1123     /**
1124      * Returns the "canonical string representation" of the actions in the
1125      * specified mask.
1126      * Always returns present actions in the following order:
1127      * connect, listen, accept, resolve.
1128      *
1129      * @param mask a specific integer action mask to translate into a string
1130      * @return the canonical string representation of the actions
1131      */
1132     private static String getActions(int mask)
1133     {
1134         StringBuilder sb = new StringBuilder();
1135         boolean comma = false;
1136 
1137         if ((mask & CONNECT) == CONNECT) {
1138             comma = true;
1139             sb.append("connect");
1140         }
1141 
1142         if ((mask & LISTEN) == LISTEN) {
1143             if (comma) sb.append(',');
1144             else comma = true;
1145             sb.append("listen");
1146         }
1147 
1148         if ((mask & ACCEPT) == ACCEPT) {
1149             if (comma) sb.append(',');
1150             else comma = true;
1151             sb.append("accept");
1152         }
1153 
1154 
1155         if ((mask & RESOLVE) == RESOLVE) {
1156             if (comma) sb.append(',');
1157             else comma = true;
1158             sb.append("resolve");
1159         }
1160 
1161         return sb.toString();
1162     }
1163 
1164     /**
1165      * Returns the canonical string representation of the actions.
1166      * Always returns present actions in the following order:
1167      * connect, listen, accept, resolve.
1168      *
1169      * @return the canonical string representation of the actions.
1170      */
1171     public String getActions()
1172     {
1173         if (actions == null)
1174             actions = getActions(this.mask);
1175 
1176         return actions;
1177     }
1178 
1179     /**
1180      * Returns a new PermissionCollection object for storing SocketPermission
1181      * objects.
1182      * <p>
1183      * SocketPermission objects must be stored in a manner that allows them
1184      * to be inserted into the collection in any order, but that also enables the
1185      * PermissionCollection <code>implies</code>
1186      * method to be implemented in an efficient (and consistent) manner.
1187      *
1188      * @return a new PermissionCollection object suitable for storing SocketPermissions.
1189      */
1190 
1191     public PermissionCollection newPermissionCollection() {
1192         return new SocketPermissionCollection();
1193     }
1194 
1195     /**
1196      * WriteObject is called to save the state of the SocketPermission
1197      * to a stream. The actions are serialized, and the superclass
1198      * takes care of the name.
1199      */
1200     private synchronized void writeObject(java.io.ObjectOutputStream s)
1201         throws IOException
1202     {
1203         // Write out the actions. The superclass takes care of the name
1204         // call getActions to make sure actions field is initialized
1205         if (actions == null)
1206             getActions();
1207         s.defaultWriteObject();
1208     }
1209 
1210     /**
1211      * readObject is called to restore the state of the SocketPermission from
1212      * a stream.
1213      */
1214     private synchronized void readObject(java.io.ObjectInputStream s)
1215          throws IOException, ClassNotFoundException
1216     {
1217         // Read in the action, then initialize the rest
1218         s.defaultReadObject();
1219         init(getName(),getMask(actions));
1220     }
1221 
1222     /**
1223      * Check the system/security property for the ephemeral port range
1224      * for this system. The suffix is either "high" or "low"
1225      */
1226     private static int initEphemeralPorts(
1227         final String suffix, final int defval
1228     )
1229     {
1230         return AccessController.doPrivileged(
1231             new PrivilegedAction<Integer>(){
1232                 public Integer run() {
1233                     int val = Integer.getInteger(
1234                             "jdk.net.ephemeralPortRange."+suffix, -1
1235                     );
1236                     if (val != -1) {
1237                         return val;
1238                     } else {
1239                         String prop = Security.getProperty(
1240                             "network.ephemeralPortRange."+suffix
1241                         );
1242                         try {
1243                                 val = Integer.parseInt(prop);
1244                         } catch (NumberFormatException e) {
1245                             // shouldn't happen
1246                             return defval;
1247                         }
1248                     }
1249                     return val;
1250                 }
1251             }
1252         );
1253     }
1254 
1255     /**
1256      * Check if the target range is within the policy range
1257      * together with the ephemeral range for this platform
1258      * (if policy includes ephemeral range)
1259      */
1260     private static boolean inRange(
1261         int policyLow, int policyHigh, int targetLow, int targetHigh
1262     )
1263     {
1264         if (targetLow == 0) {
1265             // check policy includes ephemeral range
1266             if (!inRange(policyLow, policyHigh, ephemeralLow, ephemeralHigh)) {
1267                 return false;
1268             }
1269             if (targetHigh == 0) {
1270                 // nothing left to do
1271                 return true;
1272             }
1273             // continue check with first real port number
1274             targetLow = 1;
1275         }
1276 
1277         if (policyLow == 0 && policyHigh == 0) {
1278             // ephemeral range only
1279             return targetLow >= ephemeralLow && targetHigh <= ephemeralHigh;
1280         }
1281 
1282         if (policyLow != 0) {
1283             // simple check of policy only
1284             return targetLow >= policyLow && targetHigh <= policyHigh;
1285         }
1286 
1287         // policyLow == 0 which means possibly two ranges to check
1288 
1289         // first check if policy and ephem range overlap/contiguous
1290 
1291         if (policyHigh >= ephemeralLow - 1) {
1292             return targetHigh <= ephemeralHigh;
1293         }
1294 
1295         // policy and ephem range do not overlap
1296 
1297         // target range must lie entirely inside policy range or eph range
1298 
1299         return  (targetLow <= policyHigh && targetHigh <= policyHigh) ||
1300                 (targetLow >= ephemeralLow && targetHigh <= ephemeralHigh);
1301     }
1302     /*
1303     public String toString()
1304     {
1305         StringBuffer s = new StringBuffer(super.toString() + "\n" +
1306             "cname = " + cname + "\n" +
1307             "wildcard = " + wildcard + "\n" +
1308             "invalid = " + invalid + "\n" +
1309             "portrange = " + portrange[0] + "," + portrange[1] + "\n");
1310         if (addresses != null) for (int i=0; i<addresses.length; i++) {
1311             s.append( addresses[i].getHostAddress());
1312             s.append("\n");
1313         } else {
1314             s.append("(no addresses)\n");
1315         }
1316 
1317         return s.toString();
1318     }
1319 
1320     public static void main(String args[]) throws Exception {
1321         SocketPermission this_ = new SocketPermission(args[0], "connect");
1322         SocketPermission that_ = new SocketPermission(args[1], "connect");
1323         System.out.println("-----\n");
1324         System.out.println("this.implies(that) = " + this_.implies(that_));
1325         System.out.println("-----\n");
1326         System.out.println("this = "+this_);
1327         System.out.println("-----\n");
1328         System.out.println("that = "+that_);
1329         System.out.println("-----\n");
1330 
1331         SocketPermissionCollection nps = new SocketPermissionCollection();
1332         nps.add(this_);
1333         nps.add(new SocketPermission("www-leland.stanford.edu","connect"));
1334         nps.add(new SocketPermission("www-sun.com","connect"));
1335         System.out.println("nps.implies(that) = " + nps.implies(that_));
1336         System.out.println("-----\n");
1337     }
1338     */
1339 }
1340 
1341 /**
1342 
1343 if (init'd with IP, key is IP as string)
1344 if wildcard, its the wild card
1345 else its the cname?
1346 
1347  *
1348  * @see java.security.Permission
1349  * @see java.security.Permissions
1350  * @see java.security.PermissionCollection
1351  *
1352  *
1353  * @author Roland Schemers
1354  *
1355  * @serial include
1356  */
1357 
1358 final class SocketPermissionCollection extends PermissionCollection
1359 implements Serializable
1360 {
1361     // Not serialized; see serialization section at end of class
1362     private transient List<SocketPermission> perms;
1363 
1364     /**
1365      * Create an empty SocketPermissions object.
1366      *
1367      */
1368 
1369     public SocketPermissionCollection() {
1370         perms = new ArrayList<SocketPermission>();
1371     }
1372 
1373     /**
1374      * Adds a permission to the SocketPermissions. The key for the hash is
1375      * the name in the case of wildcards, or all the IP addresses.
1376      *
1377      * @param permission the Permission object to add.
1378      *
1379      * @exception IllegalArgumentException - if the permission is not a
1380      *                                       SocketPermission
1381      *
1382      * @exception SecurityException - if this SocketPermissionCollection object
1383      *                                has been marked readonly
1384      */
1385 
1386     public void add(Permission permission)
1387     {
1388         if (! (permission instanceof SocketPermission))
1389             throw new IllegalArgumentException("invalid permission: "+
1390                                                permission);
1391         if (isReadOnly())
1392             throw new SecurityException(
1393                 "attempt to add a Permission to a readonly PermissionCollection");
1394 
1395         // optimization to ensure perms most likely to be tested
1396         // show up early (4301064)
1397         synchronized (this) {
1398             perms.add(0, (SocketPermission)permission);
1399         }
1400     }
1401 
1402     /**
1403      * Check and see if this collection of permissions implies the permissions
1404      * expressed in "permission".
1405      *
1406      * @param p the Permission object to compare
1407      *
1408      * @return true if "permission" is a proper subset of a permission in
1409      * the collection, false if not.
1410      */
1411 
1412     public boolean implies(Permission permission)
1413     {
1414         if (! (permission instanceof SocketPermission))
1415                 return false;
1416 
1417         SocketPermission np = (SocketPermission) permission;
1418 
1419         int desired = np.getMask();
1420         int effective = 0;
1421         int needed = desired;
1422 
1423         synchronized (this) {
1424             int len = perms.size();
1425             //System.out.println("implies "+np);
1426             for (int i = 0; i < len; i++) {
1427                 SocketPermission x = perms.get(i);
1428                 //System.out.println("  trying "+x);
1429                 if (((needed & x.getMask()) != 0) && x.impliesIgnoreMask(np)) {
1430                     effective |=  x.getMask();
1431                     if ((effective & desired) == desired)
1432                         return true;
1433                     needed = (desired ^ effective);
1434                 }
1435             }
1436         }
1437         return false;
1438     }
1439 
1440     /**
1441      * Returns an enumeration of all the SocketPermission objects in the
1442      * container.
1443      *
1444      * @return an enumeration of all the SocketPermission objects.
1445      */
1446 
1447     @SuppressWarnings("unchecked")
1448     public Enumeration<Permission> elements() {
1449         // Convert Iterator into Enumeration
1450         synchronized (this) {
1451             return Collections.enumeration((List<Permission>)(List)perms);
1452         }
1453     }
1454 
1455     private static final long serialVersionUID = 2787186408602843674L;
1456 
1457     // Need to maintain serialization interoperability with earlier releases,
1458     // which had the serializable field:
1459 
1460     //
1461     // The SocketPermissions for this set.
1462     // @serial
1463     //
1464     // private Vector permissions;
1465 
1466     /**
1467      * @serialField permissions java.util.Vector
1468      *     A list of the SocketPermissions for this set.
1469      */
1470     private static final ObjectStreamField[] serialPersistentFields = {
1471         new ObjectStreamField("permissions", Vector.class),
1472     };
1473 
1474     /**
1475      * @serialData "permissions" field (a Vector containing the SocketPermissions).
1476      */
1477     /*
1478      * Writes the contents of the perms field out as a Vector for
1479      * serialization compatibility with earlier releases.
1480      */
1481     private void writeObject(ObjectOutputStream out) throws IOException {
1482         // Don't call out.defaultWriteObject()
1483 
1484         // Write out Vector
1485         Vector<SocketPermission> permissions = new Vector<SocketPermission>(perms.size());
1486 
1487         synchronized (this) {
1488             permissions.addAll(perms);
1489         }
1490 
1491         ObjectOutputStream.PutField pfields = out.putFields();
1492         pfields.put("permissions", permissions);
1493         out.writeFields();
1494     }
1495 
1496     /*
1497      * Reads in a Vector of SocketPermissions and saves them in the perms field.
1498      */
1499     private void readObject(ObjectInputStream in) throws IOException,
1500     ClassNotFoundException {
1501         // Don't call in.defaultReadObject()
1502 
1503         // Read in serialized fields
1504         ObjectInputStream.GetField gfields = in.readFields();
1505 
1506         // Get the one we want
1507         @SuppressWarnings("unchecked")
1508         Vector<SocketPermission> permissions = (Vector<SocketPermission>)gfields.get("permissions", null);
1509         perms = new ArrayList<SocketPermission>(permissions.size());
1510         perms.addAll(permissions);
1511     }
1512 }