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) or IP address (IPv4 or IPv6).
 121:    */
 122:   private transient String host;
 123: 
 124:   /**
 125:    * A range of ports.
 126:    */
 127:   private transient int minport;
 128:   private transient int maxport;
 129: 
 130:   /**
 131:    * Values used for minimum and maximum ports when one or both bounds
 132:    * are omitted.  This class is essentially independent of the
 133:    * networking code it describes, so we do not limit ports to the
 134:    * usual network limits of 1 and 65535.
 135:    */
 136:   private static final int MIN_PORT = 0;
 137:   private static final int MAX_PORT = Integer.MAX_VALUE;
 138: 
 139:   /**
 140:    * The actions for which we have permission.  This field is present
 141:    * to make the serialized form correct and should not be used by
 142:    * anything other than writeObject: everything else should use
 143:    * actionmask.
 144:    */
 145:   private String actions;
 146: 
 147:   /**
 148:    * A bitmask representing the actions for which we have permission.
 149:    */
 150:   private transient int actionmask;
 151: 
 152:   /**
 153:    * The available actions, in the canonical order required for getActions().
 154:    */
 155:   private static final String[] ACTIONS = new String[] {
 156:     "connect", "listen", "accept", "resolve"};
 157: 
 158:   /**
 159:    * Initializes a new instance of <code>SocketPermission</code> with the
 160:    * specified host/port combination and actions string.
 161:    *
 162:    * @param hostport The hostname/port number combination
 163:    * @param actions The actions string
 164:    */
 165:   public SocketPermission(String hostport, String actions)
 166:   {
 167:     super(hostport);
 168: 
 169:     setHostPort(hostport);
 170:     setActions(actions);
 171:   }
 172: 
 173:   /**
 174:    * Parse the hostport argument to the constructor.
 175:    */
 176:   private void setHostPort(String hostport)
 177:   {
 178:     // Split into host and ports
 179:     String ports;
 180:     if (hostport.length() == 0)
 181:       {
 182:     host = ports = "";
 183:       }
 184:     else if (hostport.charAt(0) == '[')
 185:       {
 186:     // host is a bracketed IPv6 address
 187:     int end = hostport.indexOf("]");
 188:     if (end == -1)
 189:       throw new IllegalArgumentException("Unmatched '['");
 190:     host = hostport.substring(1, end);
 191: 
 192:     if (end == hostport.length() - 1)
 193:       ports = "";
 194:     else if (hostport.charAt(end + 1) == ':')
 195:       ports = hostport.substring(end + 2);
 196:     else
 197:       throw new IllegalArgumentException("Bad character after ']'");
 198:       }
 199:     else
 200:       {
 201:     // host is a hostname or IPv4 address
 202:     int sep = hostport.indexOf(":");
 203:     if (sep == -1)
 204:       {
 205:         host = hostport;
 206:         ports = "";
 207:       }
 208:     else
 209:       {
 210:         host = hostport.substring(0, sep);
 211:         ports = hostport.substring(sep + 1);
 212:       }
 213:       }
 214:     if (ports.indexOf(":") != -1)
 215:       throw new IllegalArgumentException("Unexpected ':'");
 216: 
 217:     // Parse and validate the ports
 218:     if (ports.length() == 0)
 219:       {
 220:     minport = MIN_PORT;
 221:     maxport = MAX_PORT;
 222:       }
 223:     else
 224:       {
 225:     int sep = ports.indexOf("-");
 226:     if (sep == -1)
 227:       {
 228:         // a single port
 229:         minport = maxport = Integer.parseInt(ports);
 230:       }
 231:     else
 232:       {
 233:         if (ports.indexOf("-", sep + 1) != -1)
 234:           throw new IllegalArgumentException("Unexpected '-'");
 235: 
 236:         if (sep == 0)
 237:           {
 238:         // an upper bound
 239:         minport = MIN_PORT;
 240:         maxport = Integer.parseInt(ports.substring(1));
 241:           }
 242:         else if (sep == ports.length() - 1)
 243:           {
 244:         // a lower bound
 245:         minport =
 246:           Integer.parseInt(ports.substring(0, ports.length() - 1));
 247:         maxport = MAX_PORT;
 248:           }
 249:         else
 250:           {
 251:         // a range with two bounds
 252:         minport = Integer.parseInt(ports.substring(0, sep));
 253:         maxport = Integer.parseInt(ports.substring(sep + 1));
 254:           }
 255:       }
 256:       }
 257:   }
 258:   
 259:   /**
 260:    * Parse the actions argument to the constructor.
 261:    */
 262:   private void setActions(String actionstring)
 263:   {
 264:     actionmask = 0;
 265: 
 266:     boolean resolve_needed = false;
 267:     boolean resolve_present = false;
 268:     
 269:     StringTokenizer t = new StringTokenizer(actionstring, ",");
 270:     while (t.hasMoreTokens())
 271:       {
 272:     String action = t.nextToken();
 273:     action = action.trim().toLowerCase();
 274:     setAction(action);
 275: 
 276:     if (action.equals("resolve"))
 277:       resolve_present = true;
 278:     else
 279:       resolve_needed = true;
 280:       }
 281: 
 282:     if (resolve_needed && !resolve_present)
 283:       setAction("resolve");
 284:   }
 285: 
 286:   /**
 287:    * Parse one element of the actions argument to the constructor.
 288:    */
 289:   private void setAction(String action)
 290:   {
 291:     for (int i = 0; i < ACTIONS.length; i++)
 292:       {
 293:     if (action.equals(ACTIONS[i]))
 294:       {
 295:         actionmask |= 1 << i;
 296:         return;
 297:       }
 298:       }
 299:     throw new IllegalArgumentException("Unknown action " + action);
 300:   }
 301: 
 302:   /**
 303:    * Tests this object for equality against another.  This will be true if
 304:    * and only if the passed object is an instance of
 305:    * <code>SocketPermission</code> and both its hostname/port combination
 306:    * and permissions string are identical.
 307:    *
 308:    * @param obj The object to test against for equality
 309:    *
 310:    * @return <code>true</code> if object is equal to this object,
 311:    *         <code>false</code> otherwise.
 312:    */
 313:   public boolean equals(Object obj)
 314:   {
 315:     SocketPermission p;
 316: 
 317:     if (obj instanceof SocketPermission)
 318:       p = (SocketPermission) obj;
 319:     else
 320:       return false;
 321: 
 322:     return p.actionmask == actionmask &&
 323:       p.minport == minport &&
 324:       p.maxport == maxport &&
 325:       p.host.equals(host);
 326:   }
 327: 
 328:   /**
 329:    * Returns a hash code value for this object.  Overrides the
 330:    * <code>Permission.hashCode()</code>.
 331:    *
 332:    * @return A hash code
 333:    */
 334:   public int hashCode()
 335:   {
 336:     return actionmask + minport + maxport + host.hashCode();
 337:   }
 338: 
 339:   /**
 340:    * Returns the list of permission actions in this object in canonical
 341:    * order.  The canonical order is "connect,listen,accept,resolve"
 342:    *
 343:    * @return The permitted action string.
 344:    */
 345:   public String getActions()
 346:   {
 347:     StringBuffer sb = new StringBuffer("");
 348: 
 349:     for (int i = 0; i < ACTIONS.length; i++)
 350:       {
 351:     if ((actionmask & (1 << i)) != 0)
 352:       {
 353:         if (sb.length() != 0)
 354:           sb.append(",");
 355:         sb.append(ACTIONS[i]);
 356:       }
 357:       }
 358: 
 359:     return sb.toString();
 360:   }
 361: 
 362:   /**
 363:    * Returns a new <code>PermissionCollection</code> object that can hold
 364:    * <code>SocketPermission</code>'s.
 365:    *
 366:    * @return A new <code>PermissionCollection</code>.
 367:    */
 368:   public PermissionCollection newPermissionCollection()
 369:   {
 370:     // FIXME: Implement
 371: 
 372:     return null;
 373:   }
 374: 
 375:   /**
 376:    * Returns true if the permission object passed it is implied by the
 377:    * this permission.  This will be true if:
 378:    * 
 379:    * <ul>
 380:    * <li>The argument is of type <code>SocketPermission</code></li>
 381:    * <li>The actions list of the argument are in this object's actions</li>
 382:    * <li>The port range of the argument is within this objects port range</li>
 383:    * <li>The hostname is equal to or a subset of this objects hostname</li>
 384:    * </ul>
 385:    *
 386:    * <p>The argument's hostname will be a subset of this object's hostname if:</p>
 387:    * 
 388:    * <ul>
 389:    * <li>The argument's hostname or IP address is equal to this object's.</li>
 390:    * <li>The argument's canonical hostname is equal to this object's.</li>
 391:    * <li>The argument's canonical name matches this domains hostname with
 392:    * wildcards</li>
 393:    * </ul>
 394:    *
 395:    * @param perm The <code>Permission</code> to check against
 396:    *
 397:    * @return <code>true</code> if the <code>Permission</code> is implied by
 398:    * this object, <code>false</code> otherwise.
 399:    */
 400:   public boolean implies(Permission perm)
 401:   {
 402:     SocketPermission p;
 403: 
 404:     // First make sure we are the right object type
 405:     if (perm instanceof SocketPermission)
 406:       p = (SocketPermission) perm;
 407:     else
 408:       return false;
 409: 
 410:     // Next check the actions
 411:     if ((p.actionmask & actionmask) != p.actionmask)
 412:     return false;
 413: 
 414:     // Then check the ports
 415:     if ((p.minport < minport) || (p.maxport > maxport))
 416:       return false;
 417: 
 418:     // Finally check the hosts
 419:     if (host.equals(p.host))
 420:       return true;
 421: 
 422:     // Try the canonical names
 423:     String ourcanonical = null;
 424:     String theircanonical = null;
 425:     try
 426:       {
 427:     ourcanonical = InetAddress.getByName(host).getHostName();
 428:     theircanonical = InetAddress.getByName(p.host).getHostName();
 429:       }
 430:     catch (UnknownHostException e)
 431:       {
 432:     // Who didn't resolve?  Just assume current address is canonical enough
 433:     // Is this ok to do?
 434:     if (ourcanonical == null)
 435:       ourcanonical = host;
 436:     if (theircanonical == null)
 437:       theircanonical = p.host;
 438:       }
 439: 
 440:     if (ourcanonical.equals(theircanonical))
 441:       return true;
 442: 
 443:     // Well, last chance.  Try for a wildcard
 444:     if (host.indexOf("*.") != -1)
 445:       {
 446:     String wild_domain =
 447:       host.substring(host.indexOf("*" + 1));
 448:     if (theircanonical.endsWith(wild_domain))
 449:       return true;
 450:       }
 451: 
 452:     // Didn't make it
 453:     return false;
 454:   }
 455: 
 456:   /**
 457:    * Deserializes a <code>SocketPermission</code> object from
 458:    * an input stream.
 459:    *
 460:    * @param input the input stream.
 461:    * @throws IOException if an I/O error occurs in the stream.
 462:    * @throws ClassNotFoundException if the class of the
 463:    *         serialized object could not be found.
 464:    */
 465:   private void readObject(ObjectInputStream input)
 466:     throws IOException, ClassNotFoundException
 467:   {
 468:     input.defaultReadObject();
 469:     setHostPort(getName());
 470:     setActions(actions);
 471:   }
 472: 
 473:   /**
 474:    * Serializes a <code>SocketPermission</code> object to an
 475:    * output stream.
 476:    *
 477:    * @param output the output stream.
 478:    * @throws IOException if an I/O error occurs in the stream.
 479:    */
 480:   private void writeObject(ObjectOutputStream output)
 481:     throws IOException
 482:   {
 483:     actions = getActions();
 484:     output.defaultWriteObject();
 485:   }
 486: }