libjava/ChangeLog:
[official-gcc.git] / libjava / classpath / java / net / SocketPermission.java
bloba5a848eee7aae53632890b2acc657623fe19f45a
1 /* SocketPermission.java -- Class modeling permissions for socket operations
2 Copyright (C) 1998, 2000, 2001, 2002, 2004, 2006 Free Software
3 Foundation, Inc.
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)
10 any later version.
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
20 02110-1301 USA.
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.
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. */
39 package java.net;
41 import gnu.java.lang.CPStringBuilder;
43 import java.io.IOException;
44 import java.io.ObjectInputStream;
45 import java.io.ObjectOutputStream;
46 import java.io.Serializable;
47 import java.security.Permission;
48 import java.security.PermissionCollection;
49 import java.util.StringTokenizer;
52 /**
53 * This class models a specific set of permssions for connecting to a
54 * host. There are two elements to this, the host/port combination and
55 * the permission list.
56 * <p>
57 * The host/port combination is specified as followed
58 * <p>
59 * <pre>
60 * hostname[:[-]port[-[port]]]
61 * </pre>
62 * <p>
63 * The hostname portion can be either a hostname or IP address. If it is
64 * a hostname, a wildcard is allowed in hostnames. This wildcard is a "*"
65 * and matches one or more characters. Only one "*" may appear in the
66 * host and it must be the leftmost character. For example,
67 * "*.urbanophile.com" matches all hosts in the "urbanophile.com" domain.
68 * <p>
69 * The port portion can be either a single value, or a range of values
70 * treated as inclusive. The first or the last port value in the range
71 * can be omitted in which case either the minimum or maximum legal
72 * value for a port (respectively) is used by default. Here are some
73 * examples:
74 * <p><ul>
75 * <li>8080 - Represents port 8080 only</li>
76 * <li>2000-3000 - Represents ports 2000 through 3000 inclusive</li>
77 * <li>-4000 - Represents ports 0 through 4000 inclusive</li>
78 * <li>1024- - Represents ports 1024 through 65535 inclusive</li>
79 * </ul><p>
80 * The permission list is a comma separated list of individual permissions.
81 * These individual permissions are:
82 * <p>
83 * <pre>
84 * accept
85 * connect
86 * listen
87 * resolve
88 * </pre>
89 * <p>
90 * The "listen" permission is only relevant if the host is localhost. If
91 * any permission at all is specified, then resolve permission is implied to
92 * exist.
93 * <p>
94 * Here are a variety of examples of how to create SocketPermission's
95 * <p><pre>
96 * SocketPermission("www.urbanophile.com", "connect");
97 * Can connect to any port on www.urbanophile.com
98 * SocketPermission("www.urbanophile.com:80", "connect,accept");
99 * Can connect to or accept connections from www.urbanophile.com on port 80
100 * SocketPermission("localhost:1024-", "listen,accept,connect");
101 * Can connect to, accept from, an listen on any local port number 1024
102 * and up.
103 * SocketPermission("*.edu", "connect");
104 * Can connect to any host in the edu domain
105 * SocketPermission("197.197.20.1", "accept");
106 * Can accept connections from 197.197.20.1
107 * </pre><p>
109 * This class also supports IPv6 addresses. These should be specified
110 * in either RFC 2732 format or in full uncompressed form.
112 * @since 1.2
114 * @author Written by Aaron M. Renn (arenn@urbanophile.com)
115 * @author Extensively modified by Gary Benson (gbenson@redhat.com)
117 public final class SocketPermission extends Permission implements Serializable
119 static final long serialVersionUID = -7204263841984476862L;
122 * A hostname (possibly wildcarded). Will be set if and only if
123 * this object was initialized with a hostname.
125 private transient String hostname = null;
128 * An IP address (IPv4 or IPv6). Will be set if and only if this
129 * object was initialized with a single literal IP address.
131 private transient InetAddress address = null;
134 * A range of ports.
136 private transient int minport;
137 private transient int maxport;
140 * Values used for minimum and maximum ports when one or both bounds
141 * are omitted. This class is essentially independent of the
142 * networking code it describes, so we do not limit ports to the
143 * usual network limits of 1 and 65535.
145 private static final int MIN_PORT = 0;
146 private static final int MAX_PORT = Integer.MAX_VALUE;
149 * The actions for which we have permission. This field is present
150 * to make the serialized form correct and should not be used by
151 * anything other than writeObject: everything else should use
152 * actionmask.
154 private String actions;
157 * A bitmask representing the actions for which we have permission.
159 private transient int actionmask;
162 * The available actions, in the canonical order required for getActions().
164 private static final String[] ACTIONS = new String[] {
165 "connect", "listen", "accept", "resolve"};
168 * Initializes a new instance of <code>SocketPermission</code> with the
169 * specified host/port combination and actions string.
171 * @param hostport The hostname/port number combination
172 * @param actions The actions string
174 public SocketPermission(String hostport, String actions)
176 super(processHostport(hostport));
178 setHostPort(getName());
179 setActions(actions);
183 * There are two cases in which hostport needs rewriting before
184 * being passed to the superclass constructor. If hostport is an
185 * empty string then it is substituted with "localhost". And if
186 * the host part of hostport is a literal IPv6 address in the full
187 * uncompressed form not enclosed with "[" and "]" then we enclose
188 * it with them.
190 private static String processHostport(String hostport)
192 if (hostport.length() == 0)
193 return "localhost";
195 if (hostport.charAt(0) == '[')
196 return hostport;
198 int colons = 0;
199 boolean colon_allowed = true;
200 for (int i = 0; i < hostport.length(); i++)
202 if (hostport.charAt(i) == ':')
204 if (!colon_allowed)
205 throw new IllegalArgumentException("Ambiguous hostport part");
206 colons++;
207 colon_allowed = false;
209 else
210 colon_allowed = true;
213 switch (colons)
215 case 0:
216 case 1:
217 // a hostname or IPv4 address
218 return hostport;
220 case 7:
221 // an IPv6 address with no ports
222 return "[" + hostport + "]";
224 case 8:
225 // an IPv6 address with ports
226 int last_colon = hostport.lastIndexOf(':');
227 return "[" + hostport.substring(0, last_colon) + "]"
228 + hostport.substring(last_colon);
230 default:
231 throw new IllegalArgumentException("Ambiguous hostport part");
236 * Parse the hostport argument to the constructor.
238 private void setHostPort(String hostport)
240 // Split into host and ports
241 String host, ports;
242 if (hostport.charAt(0) == '[')
244 // host is a bracketed IPv6 address
245 int end = hostport.indexOf("]");
246 if (end == -1)
247 throw new IllegalArgumentException("Unmatched '['");
248 host = hostport.substring(1, end);
250 address = InetAddress.getByLiteral(host);
251 if (address == null)
252 throw new IllegalArgumentException("Bad IPv6 address");
254 if (end == hostport.length() - 1)
255 ports = "";
256 else if (hostport.charAt(end + 1) == ':')
257 ports = hostport.substring(end + 2);
258 else
259 throw new IllegalArgumentException("Bad character after ']'");
261 else
263 // host is a hostname or IPv4 address
264 int sep = hostport.indexOf(":");
265 if (sep == -1)
267 host = hostport;
268 ports = "";
270 else
272 host = hostport.substring(0, sep);
273 ports = hostport.substring(sep + 1);
276 address = InetAddress.getByLiteral(host);
277 if (address == null)
279 if (host.lastIndexOf('*') > 0)
280 throw new IllegalArgumentException("Bad hostname");
282 hostname = host;
286 // Parse and validate the ports
287 if (ports.length() == 0)
289 minport = MIN_PORT;
290 maxport = MAX_PORT;
292 else
294 int sep = ports.indexOf("-");
295 if (sep == -1)
297 // a single port
298 minport = maxport = Integer.parseInt(ports);
300 else
302 if (ports.indexOf("-", sep + 1) != -1)
303 throw new IllegalArgumentException("Unexpected '-'");
305 if (sep == 0)
307 // an upper bound
308 minport = MIN_PORT;
309 maxport = Integer.parseInt(ports.substring(1));
311 else if (sep == ports.length() - 1)
313 // a lower bound
314 minport =
315 Integer.parseInt(ports.substring(0, ports.length() - 1));
316 maxport = MAX_PORT;
318 else
320 // a range with two bounds
321 minport = Integer.parseInt(ports.substring(0, sep));
322 maxport = Integer.parseInt(ports.substring(sep + 1));
329 * Parse the actions argument to the constructor.
331 private void setActions(String actionstring)
333 actionmask = 0;
335 boolean resolve_needed = false;
336 boolean resolve_present = false;
338 StringTokenizer t = new StringTokenizer(actionstring, ",");
339 while (t.hasMoreTokens())
341 String action = t.nextToken();
342 action = action.trim().toLowerCase();
343 setAction(action);
345 if (action.equals("resolve"))
346 resolve_present = true;
347 else
348 resolve_needed = true;
351 if (resolve_needed && !resolve_present)
352 setAction("resolve");
356 * Parse one element of the actions argument to the constructor.
358 private void setAction(String action)
360 for (int i = 0; i < ACTIONS.length; i++)
362 if (action.equals(ACTIONS[i]))
364 actionmask |= 1 << i;
365 return;
368 throw new IllegalArgumentException("Unknown action " + action);
372 * Tests this object for equality against another. This will be true if
373 * and only if the passed object is an instance of
374 * <code>SocketPermission</code> and both its hostname/port combination
375 * and permissions string are identical.
377 * @param obj The object to test against for equality
379 * @return <code>true</code> if object is equal to this object,
380 * <code>false</code> otherwise.
382 public boolean equals(Object obj)
384 SocketPermission p;
386 if (obj instanceof SocketPermission)
387 p = (SocketPermission) obj;
388 else
389 return false;
391 if (p.actionmask != actionmask ||
392 p.minport != minport ||
393 p.maxport != maxport)
394 return false;
396 if (address != null)
398 if (p.address == null)
399 return false;
400 else
401 return p.address.equals(address);
403 else
405 if (p.hostname == null)
406 return false;
407 else
408 return p.hostname.equals(hostname);
413 * Returns a hash code value for this object. Overrides the
414 * <code>Permission.hashCode()</code>.
416 * @return A hash code
418 public int hashCode()
420 int code = actionmask + minport + maxport;
421 if (address != null)
422 code += address.hashCode();
423 else
424 code += hostname.hashCode();
425 return code;
429 * Returns the list of permission actions in this object in canonical
430 * order. The canonical order is "connect,listen,accept,resolve"
432 * @return The permitted action string.
434 public String getActions()
436 CPStringBuilder sb = new CPStringBuilder("");
438 for (int i = 0; i < ACTIONS.length; i++)
440 if ((actionmask & (1 << i)) != 0)
442 if (sb.length() != 0)
443 sb.append(",");
444 sb.append(ACTIONS[i]);
448 return sb.toString();
452 * Returns a new <code>PermissionCollection</code> object that can hold
453 * <code>SocketPermission</code>'s.
455 * @return A new <code>PermissionCollection</code>.
457 public PermissionCollection newPermissionCollection()
459 // FIXME: Implement
461 return null;
465 * Returns an array of all IP addresses represented by this object.
467 private InetAddress[] getAddresses()
469 if (address != null)
470 return new InetAddress[] {address};
474 return InetAddress.getAllByName(hostname);
476 catch (UnknownHostException e)
478 return new InetAddress[0];
483 * Returns the canonical hostname represented by this object,
484 * or null if this object represents a wildcarded domain.
486 private String getCanonicalHostName()
488 if (address != null)
489 return address.internalGetCanonicalHostName();
490 if (hostname.charAt(0) == '*')
491 return null;
494 return InetAddress.getByName(hostname).internalGetCanonicalHostName();
496 catch (UnknownHostException e)
498 return null;
503 * Returns true if the permission object passed it is implied by the
504 * this permission. This will be true if:
506 * <ul>
507 * <li>The argument is of type <code>SocketPermission</code></li>
508 * <li>The actions list of the argument are in this object's actions</li>
509 * <li>The port range of the argument is within this objects port range</li>
510 * <li>The hostname is equal to or a subset of this objects hostname</li>
511 * </ul>
513 * <p>The argument's hostname will be a subset of this object's hostname if:</p>
515 * <ul>
516 * <li>The argument's hostname or IP address is equal to this object's.</li>
517 * <li>The argument's canonical hostname is equal to this object's.</li>
518 * <li>The argument's canonical name matches this domains hostname with
519 * wildcards</li>
520 * </ul>
522 * @param perm The <code>Permission</code> to check against
524 * @return <code>true</code> if the <code>Permission</code> is implied by
525 * this object, <code>false</code> otherwise.
527 public boolean implies(Permission perm)
529 SocketPermission p;
531 // First make sure we are the right object type
532 if (perm instanceof SocketPermission)
533 p = (SocketPermission) perm;
534 else
535 return false;
537 // If p was initialised with an empty hostname then we do not
538 // imply it. This is not part of the spec, but it seems necessary.
539 if (p.hostname != null && p.hostname.length() == 0)
540 return false;
542 // Next check the actions
543 if ((p.actionmask & actionmask) != p.actionmask)
544 return false;
546 // Then check the ports
547 if ((p.minport < minport) || (p.maxport > maxport))
548 return false;
550 // Finally check the hosts
551 String p_canon = null;
553 // Return true if this object was initialized with a single
554 // IP address which one of p's IP addresses is equal to.
555 if (address != null)
557 InetAddress[] addrs = p.getAddresses();
558 for (int i = 0; i < addrs.length; i++)
560 if (address.equals(addrs[i]))
561 return true;
565 // Return true if this object is a wildcarded domain that
566 // p's canonical name matches.
567 if (hostname != null && hostname.charAt(0) == '*')
569 p_canon = p.getCanonicalHostName();
570 if (p_canon != null && p_canon.endsWith(hostname.substring(1)))
571 return true;
575 // Return true if this one of this object's IP addresses
576 // is equal to one of p's.
577 if (address == null)
579 InetAddress[] addrs = p.getAddresses();
580 InetAddress[] p_addrs = p.getAddresses();
582 for (int i = 0; i < addrs.length; i++)
584 for (int j = 0; j < p_addrs.length; j++)
586 if (addrs[i].equals(p_addrs[j]))
587 return true;
592 // Return true if this object's canonical name equals p's.
593 String canon = getCanonicalHostName();
594 if (canon != null)
596 if (p_canon == null)
597 p_canon = p.getCanonicalHostName();
598 if (p_canon != null && canon.equals(p_canon))
599 return true;
602 // Didn't make it
603 return false;
607 * Deserializes a <code>SocketPermission</code> object from
608 * an input stream.
610 * @param input the input stream.
611 * @throws IOException if an I/O error occurs in the stream.
612 * @throws ClassNotFoundException if the class of the
613 * serialized object could not be found.
615 private void readObject(ObjectInputStream input)
616 throws IOException, ClassNotFoundException
618 input.defaultReadObject();
619 setHostPort(getName());
620 setActions(actions);
624 * Serializes a <code>SocketPermission</code> object to an
625 * output stream.
627 * @param output the output stream.
628 * @throws IOException if an I/O error occurs in the stream.
630 private void writeObject(ObjectOutputStream output)
631 throws IOException
633 actions = getActions();
634 output.defaultWriteObject();