Frames | No Frames |
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: }