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