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