Source for java.net.SocketPermission

   1: /* SocketPermission.java -- Class modeling permissions for socket operations
   2:    Copyright (C) 1998, 2000, 2001, 2002, 2004, 2006 Free Software
   3:    Foundation, Inc.
   4: 
   5: This file is part of GNU Classpath.
   6: 
   7: GNU Classpath is free software; you can redistribute it and/or modify
   8: it under the terms of the GNU General Public License as published by
   9: the Free Software Foundation; either version 2, or (at your option)
  10: any later version.
  11: 
  12: GNU Classpath is distributed in the hope that it will be useful, but
  13: WITHOUT ANY WARRANTY; without even the implied warranty of
  14: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15: General Public License for more details.
  16: 
  17: You should have received a copy of the GNU General Public License
  18: along with GNU Classpath; see the file COPYING.  If not, write to the
  19: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  20: 02110-1301 USA.
  21: 
  22: Linking this library statically or dynamically with other modules is
  23: making a combined work based on this library.  Thus, the terms and
  24: conditions of the GNU General Public License cover the whole
  25: combination.
  26: 
  27: As a special exception, the copyright holders of this library give you
  28: permission to link this library with independent modules to produce an
  29: executable, regardless of the license terms of these independent
  30: modules, and to copy and distribute the resulting executable under
  31: terms of your choice, provided that you also meet, for each linked
  32: independent module, the terms and conditions of the license of that
  33: module.  An independent module is a module which is not derived from
  34: or based on this library.  If you modify this library, you may extend
  35: this exception to your version of the library, but you are not
  36: obligated to do so.  If you do not wish to do so, delete this
  37: exception statement from your version. */
  38: 
  39: package java.net;
  40: 
  41: import gnu.java.lang.CPStringBuilder;
  42: 
  43: import java.io.IOException;
  44: import java.io.ObjectInputStream;
  45: import java.io.ObjectOutputStream;
  46: import java.io.Serializable;
  47: import java.security.Permission;
  48: import java.security.PermissionCollection;
  49: import java.util.StringTokenizer;
  50: 
  51: 
  52: /**
  53:  * This class models a specific set of permssions for connecting to a
  54:  * host.  There are two elements to this, the host/port combination and
  55:  * the permission list.
  56:  * <p>
  57:  * The host/port combination is specified as followed
  58:  * <p>
  59:  * <pre>
  60:  * hostname[:[-]port[-[port]]]
  61:  * </pre>
  62:  * <p>
  63:  * The hostname portion can be either a hostname or IP address.  If it is
  64:  * a hostname, a wildcard is allowed in hostnames.  This wildcard is a "*"
  65:  * and matches one or more characters.  Only one "*" may appear in the
  66:  * host and it must be the leftmost character.  For example,
  67:  * "*.urbanophile.com" matches all hosts in the "urbanophile.com" domain.
  68:  * <p>
  69:  * The port portion can be either a single value, or a range of values
  70:  * treated as inclusive.  The first or the last port value in the range
  71:  * can be omitted in which case either the minimum or maximum legal
  72:  * value for a port (respectively) is used by default.  Here are some
  73:  * examples:
  74:  * <p><ul>
  75:  * <li>8080 - Represents port 8080 only</li>
  76:  * <li>2000-3000 - Represents ports 2000 through 3000 inclusive</li>
  77:  * <li>-4000 - Represents ports 0 through 4000 inclusive</li>
  78:  * <li>1024- - Represents ports 1024 through 65535 inclusive</li>
  79:  * </ul><p>
  80:  * The permission list is a comma separated list of individual permissions.
  81:  * These individual permissions are:
  82:  * <p>
  83:  * <pre>
  84:  * accept
  85:  * connect
  86:  * listen
  87:  * resolve
  88:  * </pre>
  89:  * <p>
  90:  * The "listen" permission is only relevant if the host is localhost.  If
  91:  * any permission at all is specified, then resolve permission is implied to
  92:  * exist.
  93:  * <p>
  94:  * Here are a variety of examples of how to create SocketPermission's
  95:  * <p><pre>
  96:  * SocketPermission("www.urbanophile.com", "connect");
  97:  *   Can connect to any port on www.urbanophile.com
  98:  * SocketPermission("www.urbanophile.com:80", "connect,accept");
  99:  *   Can connect to or accept connections from www.urbanophile.com on port 80
 100:  * SocketPermission("localhost:1024-", "listen,accept,connect");
 101:  *   Can connect to, accept from, an listen on any local port number 1024
 102:  *   and up.
 103:  * SocketPermission("*.edu", "connect");
 104:  *   Can connect to any host in the edu domain
 105:  * SocketPermission("197.197.20.1", "accept");
 106:  *   Can accept connections from 197.197.20.1
 107:  * </pre><p>
 108:  *
 109:  * This class also supports IPv6 addresses.  These should be specified
 110:  * in either RFC 2732 format or in full uncompressed form.
 111:  *
 112:  * @since 1.2
 113:  *
 114:  * @author Written by Aaron M. Renn (arenn@urbanophile.com)
 115:  * @author Extensively modified by Gary Benson (gbenson@redhat.com)
 116:  */
 117: public final class SocketPermission extends Permission implements Serializable
 118: {
 119:   static final long serialVersionUID = -7204263841984476862L;
 120: 
 121:   /**
 122:    * A hostname (possibly wildcarded).  Will be set if and only if
 123:    * this object was initialized with a hostname.
 124:    */
 125:   private transient String hostname = null;
 126: 
 127:   /**
 128:    * An IP address (IPv4 or IPv6).  Will be set if and only if this
 129:    * object was initialized with a single literal IP address.
 130:    */
 131:   private transient InetAddress address = null;
 132: 
 133:   /**
 134:    * A range of ports.
 135:    */
 136:   private transient int minport;
 137:   private transient int maxport;
 138: 
 139:   /**
 140:    * Values used for minimum and maximum ports when one or both bounds
 141:    * are omitted.  This class is essentially independent of the
 142:    * networking code it describes, so we do not limit ports to the
 143:    * usual network limits of 1 and 65535.
 144:    */
 145:   private static final int MIN_PORT = 0;
 146:   private static final int MAX_PORT = Integer.MAX_VALUE;
 147: 
 148:   /**
 149:    * The actions for which we have permission.  This field is present
 150:    * to make the serialized form correct and should not be used by
 151:    * anything other than writeObject: everything else should use
 152:    * actionmask.
 153:    */
 154:   private String actions;
 155: 
 156:   /**
 157:    * A bitmask representing the actions for which we have permission.
 158:    */
 159:   private transient int actionmask;
 160: 
 161:   /**
 162:    * The available actions, in the canonical order required for getActions().
 163:    */
 164:   private static final String[] ACTIONS = new String[] {
 165:     "connect", "listen", "accept", "resolve"};
 166: 
 167:   /**
 168:    * Initializes a new instance of <code>SocketPermission</code> with the
 169:    * specified host/port combination and actions string.
 170:    *
 171:    * @param hostport The hostname/port number combination
 172:    * @param actions The actions string
 173:    */
 174:   public SocketPermission(String hostport, String actions)
 175:   {
 176:     super(processHostport(hostport));
 177: 
 178:     setHostPort(getName());
 179:     setActions(actions);
 180:   }
 181: 
 182:   /**
 183:    * There are two cases in which hostport needs rewriting before
 184:    * being passed to the superclass constructor.  If hostport is an
 185:    * empty string then it is substituted with "localhost".  And if
 186:    * the host part of hostport is a literal IPv6 address in the full
 187:    * uncompressed form not enclosed with "[" and "]" then we enclose
 188:    * it with them.
 189:    */
 190:   private static String processHostport(String hostport)
 191:   {
 192:     if (hostport.length() == 0)
 193:       return "localhost";
 194: 
 195:     if (hostport.charAt(0) == '[')
 196:       return hostport;
 197: 
 198:     int colons = 0;
 199:     boolean colon_allowed = true;
 200:     for (int i = 0; i < hostport.length(); i++)
 201:       {
 202:         if (hostport.charAt(i) == ':')
 203:           {
 204:             if (!colon_allowed)
 205:               throw new IllegalArgumentException("Ambiguous hostport part");
 206:             colons++;
 207:             colon_allowed = false;
 208:           }
 209:         else
 210:           colon_allowed = true;
 211:       }
 212: 
 213:     switch (colons)
 214:       {
 215:       case 0:
 216:       case 1:
 217:         // a hostname or IPv4 address
 218:         return hostport;
 219: 
 220:       case 7:
 221:         // an IPv6 address with no ports
 222:         return "[" + hostport + "]";
 223: 
 224:       case 8:
 225:         // an IPv6 address with ports
 226:         int last_colon = hostport.lastIndexOf(':');
 227:         return "[" + hostport.substring(0, last_colon) + "]"
 228:           + hostport.substring(last_colon);
 229: 
 230:       default:
 231:         throw new IllegalArgumentException("Ambiguous hostport part");
 232:       }
 233:   }
 234: 
 235:   /**
 236:    * Parse the hostport argument to the constructor.
 237:    */
 238:   private void setHostPort(String hostport)
 239:   {
 240:     // Split into host and ports
 241:     String host, ports;
 242:     if (hostport.charAt(0) == '[')
 243:       {
 244:         // host is a bracketed IPv6 address
 245:         int end = hostport.indexOf("]");
 246:         if (end == -1)
 247:           throw new IllegalArgumentException("Unmatched '['");
 248:         host = hostport.substring(1, end);
 249: 
 250:         address = InetAddress.getByLiteral(host);
 251:         if (address == null)
 252:           throw new IllegalArgumentException("Bad IPv6 address");
 253: 
 254:         if (end == hostport.length() - 1)
 255:           ports = "";
 256:         else if (hostport.charAt(end + 1) == ':')
 257:           ports = hostport.substring(end + 2);
 258:         else
 259:           throw new IllegalArgumentException("Bad character after ']'");
 260:       }
 261:     else
 262:       {
 263:         // host is a hostname or IPv4 address
 264:         int sep = hostport.indexOf(":");
 265:         if (sep == -1)
 266:           {
 267:             host = hostport;
 268:             ports = "";
 269:           }
 270:         else
 271:           {
 272:             host = hostport.substring(0, sep);
 273:             ports = hostport.substring(sep + 1);
 274:           }
 275: 
 276:         address = InetAddress.getByLiteral(host);
 277:         if (address == null)
 278:           {
 279:             if (host.lastIndexOf('*') > 0)
 280:               throw new IllegalArgumentException("Bad hostname");
 281: 
 282:             hostname = host;
 283:           }
 284:       }
 285: 
 286:     // Parse and validate the ports
 287:     if (ports.length() == 0)
 288:       {
 289:         minport = MIN_PORT;
 290:         maxport = MAX_PORT;
 291:       }
 292:     else
 293:       {
 294:         int sep = ports.indexOf("-");
 295:         if (sep == -1)
 296:           {
 297:             // a single port
 298:             minport = maxport = Integer.parseInt(ports);
 299:           }
 300:         else
 301:           {
 302:             if (ports.indexOf("-", sep + 1) != -1)
 303:               throw new IllegalArgumentException("Unexpected '-'");
 304: 
 305:             if (sep == 0)
 306:               {
 307:                 // an upper bound
 308:                 minport = MIN_PORT;
 309:                 maxport = Integer.parseInt(ports.substring(1));
 310:               }
 311:             else if (sep == ports.length() - 1)
 312:               {
 313:                 // a lower bound
 314:                 minport =
 315:                   Integer.parseInt(ports.substring(0, ports.length() - 1));
 316:                 maxport = MAX_PORT;
 317:               }
 318:             else
 319:               {
 320:                 // a range with two bounds
 321:                 minport = Integer.parseInt(ports.substring(0, sep));
 322:                 maxport = Integer.parseInt(ports.substring(sep + 1));
 323:               }
 324:           }
 325:       }
 326:   }
 327: 
 328:   /**
 329:    * Parse the actions argument to the constructor.
 330:    */
 331:   private void setActions(String actionstring)
 332:   {
 333:     actionmask = 0;
 334: 
 335:     boolean resolve_needed = false;
 336:     boolean resolve_present = false;
 337: 
 338:     StringTokenizer t = new StringTokenizer(actionstring, ",");
 339:     while (t.hasMoreTokens())
 340:       {
 341:         String action = t.nextToken();
 342:         action = action.trim().toLowerCase();
 343:         setAction(action);
 344: 
 345:         if (action.equals("resolve"))
 346:           resolve_present = true;
 347:         else
 348:           resolve_needed = true;
 349:       }
 350: 
 351:     if (resolve_needed && !resolve_present)
 352:       setAction("resolve");
 353:   }
 354: 
 355:   /**
 356:    * Parse one element of the actions argument to the constructor.
 357:    */
 358:   private void setAction(String action)
 359:   {
 360:     for (int i = 0; i < ACTIONS.length; i++)
 361:       {
 362:         if (action.equals(ACTIONS[i]))
 363:           {
 364:             actionmask |= 1 << i;
 365:             return;
 366:           }
 367:       }
 368:     throw new IllegalArgumentException("Unknown action " + action);
 369:   }
 370: 
 371:   /**
 372:    * Tests this object for equality against another.  This will be true if
 373:    * and only if the passed object is an instance of
 374:    * <code>SocketPermission</code> and both its hostname/port combination
 375:    * and permissions string are identical.
 376:    *
 377:    * @param obj The object to test against for equality
 378:    *
 379:    * @return <code>true</code> if object is equal to this object,
 380:    *         <code>false</code> otherwise.
 381:    */
 382:   public boolean equals(Object obj)
 383:   {
 384:     SocketPermission p;
 385: 
 386:     if (obj instanceof SocketPermission)
 387:       p = (SocketPermission) obj;
 388:     else
 389:       return false;
 390: 
 391:     if (p.actionmask != actionmask ||
 392:         p.minport != minport ||
 393:         p.maxport != maxport)
 394:       return false;
 395: 
 396:     if (address != null)
 397:       {
 398:         if (p.address == null)
 399:           return false;
 400:         else
 401:           return p.address.equals(address);
 402:       }
 403:     else
 404:       {
 405:         if (p.hostname == null)
 406:           return false;
 407:         else
 408:           return p.hostname.equals(hostname);
 409:       }
 410:   }
 411: 
 412:   /**
 413:    * Returns a hash code value for this object.  Overrides the
 414:    * <code>Permission.hashCode()</code>.
 415:    *
 416:    * @return A hash code
 417:    */
 418:   public int hashCode()
 419:   {
 420:     int code = actionmask + minport + maxport;
 421:     if (address != null)
 422:       code += address.hashCode();
 423:     else
 424:       code += hostname.hashCode();
 425:     return code;
 426:   }
 427: 
 428:   /**
 429:    * Returns the list of permission actions in this object in canonical
 430:    * order.  The canonical order is "connect,listen,accept,resolve"
 431:    *
 432:    * @return The permitted action string.
 433:    */
 434:   public String getActions()
 435:   {
 436:     CPStringBuilder sb = new CPStringBuilder("");
 437: 
 438:     for (int i = 0; i < ACTIONS.length; i++)
 439:       {
 440:         if ((actionmask & (1 << i)) != 0)
 441:           {
 442:             if (sb.length() != 0)
 443:               sb.append(",");
 444:             sb.append(ACTIONS[i]);
 445:           }
 446:       }
 447: 
 448:     return sb.toString();
 449:   }
 450: 
 451:   /**
 452:    * Returns a new <code>PermissionCollection</code> object that can hold
 453:    * <code>SocketPermission</code>'s.
 454:    *
 455:    * @return A new <code>PermissionCollection</code>.
 456:    */
 457:   public PermissionCollection newPermissionCollection()
 458:   {
 459:     // FIXME: Implement
 460: 
 461:     return null;
 462:   }
 463: 
 464:   /**
 465:    * Returns an array of all IP addresses represented by this object.
 466:    */
 467:   private InetAddress[] getAddresses()
 468:   {
 469:     if (address != null)
 470:       return new InetAddress[] {address};
 471: 
 472:     try
 473:       {
 474:         return InetAddress.getAllByName(hostname);
 475:       }
 476:     catch (UnknownHostException e)
 477:       {
 478:         return new InetAddress[0];
 479:       }
 480:   }
 481: 
 482:   /**
 483:    * Returns the canonical hostname represented by this object,
 484:    * or null if this object represents a wildcarded domain.
 485:    */
 486:   private String getCanonicalHostName()
 487:   {
 488:     if (address != null)
 489:       return address.internalGetCanonicalHostName();
 490:     if (hostname.charAt(0) == '*')
 491:       return null;
 492:     try
 493:       {
 494:         return InetAddress.getByName(hostname).internalGetCanonicalHostName();
 495:       }
 496:     catch (UnknownHostException e)
 497:       {
 498:         return null;
 499:       }
 500:   }
 501: 
 502:   /**
 503:    * Returns true if the permission object passed it is implied by the
 504:    * this permission.  This will be true if:
 505:    *
 506:    * <ul>
 507:    * <li>The argument is of type <code>SocketPermission</code></li>
 508:    * <li>The actions list of the argument are in this object's actions</li>
 509:    * <li>The port range of the argument is within this objects port range</li>
 510:    * <li>The hostname is equal to or a subset of this objects hostname</li>
 511:    * </ul>
 512:    *
 513:    * <p>The argument's hostname will be a subset of this object's hostname if:</p>
 514:    *
 515:    * <ul>
 516:    * <li>The argument's hostname or IP address is equal to this object's.</li>
 517:    * <li>The argument's canonical hostname is equal to this object's.</li>
 518:    * <li>The argument's canonical name matches this domains hostname with
 519:    * wildcards</li>
 520:    * </ul>
 521:    *
 522:    * @param perm The <code>Permission</code> to check against
 523:    *
 524:    * @return <code>true</code> if the <code>Permission</code> is implied by
 525:    * this object, <code>false</code> otherwise.
 526:    */
 527:   public boolean implies(Permission perm)
 528:   {
 529:     SocketPermission p;
 530: 
 531:     // First make sure we are the right object type
 532:     if (perm instanceof SocketPermission)
 533:       p = (SocketPermission) perm;
 534:     else
 535:       return false;
 536: 
 537:     // If p was initialised with an empty hostname then we do not
 538:     // imply it. This is not part of the spec, but it seems necessary.
 539:     if (p.hostname != null && p.hostname.length() == 0)
 540:       return false;
 541: 
 542:     // Next check the actions
 543:     if ((p.actionmask & actionmask) != p.actionmask)
 544:         return false;
 545: 
 546:     // Then check the ports
 547:     if ((p.minport < minport) || (p.maxport > maxport))
 548:       return false;
 549: 
 550:     // Finally check the hosts
 551:     String p_canon = null;
 552: 
 553:     // Return true if this object was initialized with a single
 554:     // IP address which one of p's IP addresses is equal to.
 555:     if (address != null)
 556:       {
 557:         InetAddress[] addrs = p.getAddresses();
 558:         for (int i = 0; i < addrs.length; i++)
 559:           {
 560:             if (address.equals(addrs[i]))
 561:               return true;
 562:           }
 563:       }
 564: 
 565:     // Return true if this object is a wildcarded domain that
 566:     // p's canonical name matches.
 567:     if (hostname != null && hostname.charAt(0) == '*')
 568:       {
 569:         p_canon = p.getCanonicalHostName();
 570:         if (p_canon != null && p_canon.endsWith(hostname.substring(1)))
 571:           return true;
 572: 
 573:       }
 574: 
 575:     // Return true if this one of this object's IP addresses
 576:     // is equal to one of p's.
 577:     if (address == null)
 578:       {
 579:         InetAddress[] addrs = p.getAddresses();
 580:         InetAddress[] p_addrs = p.getAddresses();
 581: 
 582:         for (int i = 0; i < addrs.length; i++)
 583:           {
 584:             for (int j = 0; j < p_addrs.length; j++)
 585:               {
 586:                 if (addrs[i].equals(p_addrs[j]))
 587:                   return true;
 588:               }
 589:           }
 590:       }
 591: 
 592:     // Return true if this object's canonical name equals p's.
 593:     String canon = getCanonicalHostName();
 594:     if (canon != null)
 595:       {
 596:         if (p_canon == null)
 597:           p_canon = p.getCanonicalHostName();
 598:         if (p_canon != null && canon.equals(p_canon))
 599:           return true;
 600:       }
 601: 
 602:     // Didn't make it
 603:     return false;
 604:   }
 605: 
 606:   /**
 607:    * Deserializes a <code>SocketPermission</code> object from
 608:    * an input stream.
 609:    *
 610:    * @param input the input stream.
 611:    * @throws IOException if an I/O error occurs in the stream.
 612:    * @throws ClassNotFoundException if the class of the
 613:    *         serialized object could not be found.
 614:    */
 615:   private void readObject(ObjectInputStream input)
 616:     throws IOException, ClassNotFoundException
 617:   {
 618:     input.defaultReadObject();
 619:     setHostPort(getName());
 620:     setActions(actions);
 621:   }
 622: 
 623:   /**
 624:    * Serializes a <code>SocketPermission</code> object to an
 625:    * output stream.
 626:    *
 627:    * @param output the output stream.
 628:    * @throws IOException if an I/O error occurs in the stream.
 629:    */
 630:   private void writeObject(ObjectOutputStream output)
 631:     throws IOException
 632:   {
 633:     actions = getActions();
 634:     output.defaultWriteObject();
 635:   }
 636: }