1 /* SocketPermission.java -- Class modeling permissions for socket operations
2 Copyright (C) 1998, 2000, 2001, 2002, 2004, 2006 Free Software
5 This file is part of GNU Classpath.
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)
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.
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
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
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. */
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
;
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.
55 * The host/port combination is specified as followed
58 * hostname[:[-]port[-[port]]]
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.
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
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>
78 * The permission list is a comma separated list of individual permissions.
79 * These individual permissions are:
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
92 * Here are a variety of examples of how to create SocketPermission's
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
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
107 * This class also supports IPv6 addresses. These should be specified
108 * in either RFC 2732 format or in full uncompressed form.
112 * @author Written by Aaron M. Renn (arenn@urbanophile.com)
113 * @author Extensively modified by Gary Benson (gbenson@redhat.com)
115 public final class SocketPermission
extends Permission
implements Serializable
117 static final long serialVersionUID
= -7204263841984476862L;
120 * A hostname (possibly wildcarded) or IP address (IPv4 or IPv6).
122 private transient String host
;
127 private transient int minport
;
128 private transient int maxport
;
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.
136 private static final int MIN_PORT
= 0;
137 private static final int MAX_PORT
= Integer
.MAX_VALUE
;
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
145 private String actions
;
148 * A bitmask representing the actions for which we have permission.
150 private transient int actionmask
;
153 * The available actions, in the canonical order required for getActions().
155 private static final String
[] ACTIONS
= new String
[] {
156 "connect", "listen", "accept", "resolve"};
159 * Initializes a new instance of <code>SocketPermission</code> with the
160 * specified host/port combination and actions string.
162 * @param hostport The hostname/port number combination
163 * @param actions The actions string
165 public SocketPermission(String hostport
, String actions
)
167 super(processHostport(hostport
));
169 setHostPort(getName());
174 * There are two cases in which hostport needs rewriting before
175 * being passed to the superclass constructor. If hostport is an
176 * empty string then it is substituted with "localhost". And if
177 * the host part of hostport is a literal IPv6 address in the full
178 * uncompressed form not enclosed with "[" and "]" then we enclose
181 private static String
processHostport(String hostport
)
183 if (hostport
.length() == 0)
186 if (hostport
.charAt(0) == '[')
189 int colons
= 0, last_colon
= 0;
190 for (int i
= 0; i
< hostport
.length(); i
++)
192 if (hostport
.charAt(i
) == ':')
194 if (i
- last_colon
== 1)
195 throw new IllegalArgumentException("Ambiguous hostport part");
205 // a hostname or IPv4 address
209 // an IPv6 address with no ports
210 return "[" + hostport
+ "]";
213 // an IPv6 address with ports
214 return "[" + hostport
.substring(0, last_colon
) + "]"
215 + hostport
.substring(last_colon
);
218 throw new IllegalArgumentException("Ambiguous hostport part");
223 * Parse the hostport argument to the constructor.
225 private void setHostPort(String hostport
)
227 // Split into host and ports
229 if (hostport
.charAt(0) == '[')
231 // host is a bracketed IPv6 address
232 int end
= hostport
.indexOf("]");
234 throw new IllegalArgumentException("Unmatched '['");
235 host
= hostport
.substring(1, end
);
237 if (end
== hostport
.length() - 1)
239 else if (hostport
.charAt(end
+ 1) == ':')
240 ports
= hostport
.substring(end
+ 2);
242 throw new IllegalArgumentException("Bad character after ']'");
246 // host is a hostname or IPv4 address
247 int sep
= hostport
.indexOf(":");
255 host
= hostport
.substring(0, sep
);
256 ports
= hostport
.substring(sep
+ 1);
260 // Parse and validate the ports
261 if (ports
.length() == 0)
268 int sep
= ports
.indexOf("-");
272 minport
= maxport
= Integer
.parseInt(ports
);
276 if (ports
.indexOf("-", sep
+ 1) != -1)
277 throw new IllegalArgumentException("Unexpected '-'");
283 maxport
= Integer
.parseInt(ports
.substring(1));
285 else if (sep
== ports
.length() - 1)
289 Integer
.parseInt(ports
.substring(0, ports
.length() - 1));
294 // a range with two bounds
295 minport
= Integer
.parseInt(ports
.substring(0, sep
));
296 maxport
= Integer
.parseInt(ports
.substring(sep
+ 1));
303 * Parse the actions argument to the constructor.
305 private void setActions(String actionstring
)
309 boolean resolve_needed
= false;
310 boolean resolve_present
= false;
312 StringTokenizer t
= new StringTokenizer(actionstring
, ",");
313 while (t
.hasMoreTokens())
315 String action
= t
.nextToken();
316 action
= action
.trim().toLowerCase();
319 if (action
.equals("resolve"))
320 resolve_present
= true;
322 resolve_needed
= true;
325 if (resolve_needed
&& !resolve_present
)
326 setAction("resolve");
330 * Parse one element of the actions argument to the constructor.
332 private void setAction(String action
)
334 for (int i
= 0; i
< ACTIONS
.length
; i
++)
336 if (action
.equals(ACTIONS
[i
]))
338 actionmask
|= 1 << i
;
342 throw new IllegalArgumentException("Unknown action " + action
);
346 * Tests this object for equality against another. This will be true if
347 * and only if the passed object is an instance of
348 * <code>SocketPermission</code> and both its hostname/port combination
349 * and permissions string are identical.
351 * @param obj The object to test against for equality
353 * @return <code>true</code> if object is equal to this object,
354 * <code>false</code> otherwise.
356 public boolean equals(Object obj
)
360 if (obj
instanceof SocketPermission
)
361 p
= (SocketPermission
) obj
;
365 return p
.actionmask
== actionmask
&&
366 p
.minport
== minport
&&
367 p
.maxport
== maxport
&&
372 * Returns a hash code value for this object. Overrides the
373 * <code>Permission.hashCode()</code>.
375 * @return A hash code
377 public int hashCode()
379 return actionmask
+ minport
+ maxport
+ host
.hashCode();
383 * Returns the list of permission actions in this object in canonical
384 * order. The canonical order is "connect,listen,accept,resolve"
386 * @return The permitted action string.
388 public String
getActions()
390 StringBuffer sb
= new StringBuffer("");
392 for (int i
= 0; i
< ACTIONS
.length
; i
++)
394 if ((actionmask
& (1 << i
)) != 0)
396 if (sb
.length() != 0)
398 sb
.append(ACTIONS
[i
]);
402 return sb
.toString();
406 * Returns a new <code>PermissionCollection</code> object that can hold
407 * <code>SocketPermission</code>'s.
409 * @return A new <code>PermissionCollection</code>.
411 public PermissionCollection
newPermissionCollection()
419 * Returns true if the permission object passed it is implied by the
420 * this permission. This will be true if:
423 * <li>The argument is of type <code>SocketPermission</code></li>
424 * <li>The actions list of the argument are in this object's actions</li>
425 * <li>The port range of the argument is within this objects port range</li>
426 * <li>The hostname is equal to or a subset of this objects hostname</li>
429 * <p>The argument's hostname will be a subset of this object's hostname if:</p>
432 * <li>The argument's hostname or IP address is equal to this object's.</li>
433 * <li>The argument's canonical hostname is equal to this object's.</li>
434 * <li>The argument's canonical name matches this domains hostname with
438 * @param perm The <code>Permission</code> to check against
440 * @return <code>true</code> if the <code>Permission</code> is implied by
441 * this object, <code>false</code> otherwise.
443 public boolean implies(Permission perm
)
447 // First make sure we are the right object type
448 if (perm
instanceof SocketPermission
)
449 p
= (SocketPermission
) perm
;
453 // Next check the actions
454 if ((p
.actionmask
& actionmask
) != p
.actionmask
)
457 // Then check the ports
458 if ((p
.minport
< minport
) || (p
.maxport
> maxport
))
461 // Finally check the hosts
462 if (host
.equals(p
.host
))
465 // Try the canonical names
466 String ourcanonical
= null;
467 String theircanonical
= null;
470 ourcanonical
= InetAddress
.getByName(host
).getHostName();
471 theircanonical
= InetAddress
.getByName(p
.host
).getHostName();
473 catch (UnknownHostException e
)
475 // Who didn't resolve? Just assume current address is canonical enough
477 if (ourcanonical
== null)
479 if (theircanonical
== null)
480 theircanonical
= p
.host
;
483 if (ourcanonical
.equals(theircanonical
))
486 // Well, last chance. Try for a wildcard
487 if (host
.indexOf("*.") != -1)
490 host
.substring(host
.indexOf("*" + 1));
491 if (theircanonical
.endsWith(wild_domain
))
500 * Deserializes a <code>SocketPermission</code> object from
503 * @param input the input stream.
504 * @throws IOException if an I/O error occurs in the stream.
505 * @throws ClassNotFoundException if the class of the
506 * serialized object could not be found.
508 private void readObject(ObjectInputStream input
)
509 throws IOException
, ClassNotFoundException
511 input
.defaultReadObject();
512 setHostPort(getName());
517 * Serializes a <code>SocketPermission</code> object to an
520 * @param output the output stream.
521 * @throws IOException if an I/O error occurs in the stream.
523 private void writeObject(ObjectOutputStream output
)
526 actions
= getActions();
527 output
.defaultWriteObject();