GNU Classpath (0.95) | |
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). 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: }
GNU Classpath (0.95) |