OSDN Git Service

97e93dcbb3520cd1d7169adc305e398a7c56f956
[pf3gnuchains/gcc-fork.git] / libjava / classpath / java / net / SocketPermission.java
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(processHostport(hostport));
168
169     setHostPort(getName());
170     setActions(actions);
171   }
172
173   /**
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
179    * it with them.
180    */
181   private static String processHostport(String hostport)
182   {
183     if (hostport.length() == 0)
184       return "localhost";
185
186     if (hostport.charAt(0) == '[')
187       return hostport;
188
189     int colons = 0, last_colon = 0;
190     for (int i = 0; i < hostport.length(); i++)
191       {
192         if (hostport.charAt(i) == ':')
193           {
194             if (i - last_colon == 1)
195               throw new IllegalArgumentException("Ambiguous hostport part");
196             colons++;
197             last_colon = i;
198           }
199       }
200
201     switch (colons)
202       {
203       case 0:
204       case 1:
205         // a hostname or IPv4 address
206         return hostport;
207         
208       case 7:
209         // an IPv6 address with no ports
210         return "[" + hostport + "]";
211
212       case 8:
213         // an IPv6 address with ports
214         return "[" + hostport.substring(0, last_colon) + "]"
215           + hostport.substring(last_colon);
216
217       default:
218         throw new IllegalArgumentException("Ambiguous hostport part");
219       }
220   }
221   
222   /**
223    * Parse the hostport argument to the constructor.
224    */
225   private void setHostPort(String hostport)
226   {
227     // Split into host and ports
228     String ports;
229     if (hostport.charAt(0) == '[')
230       {
231         // host is a bracketed IPv6 address
232         int end = hostport.indexOf("]");
233         if (end == -1)
234           throw new IllegalArgumentException("Unmatched '['");
235         host = hostport.substring(1, end);
236
237         if (end == hostport.length() - 1)
238           ports = "";
239         else if (hostport.charAt(end + 1) == ':')
240           ports = hostport.substring(end + 2);
241         else
242           throw new IllegalArgumentException("Bad character after ']'");
243       }
244     else
245       {
246         // host is a hostname or IPv4 address
247         int sep = hostport.indexOf(":");
248         if (sep == -1)
249           {
250             host = hostport;
251             ports = "";
252           }
253         else
254           {
255             host = hostport.substring(0, sep);
256             ports = hostport.substring(sep + 1);
257           }
258       }
259
260     // Parse and validate the ports
261     if (ports.length() == 0)
262       {
263         minport = MIN_PORT;
264         maxport = MAX_PORT;
265       }
266     else
267       {
268         int sep = ports.indexOf("-");
269         if (sep == -1)
270           {
271             // a single port
272             minport = maxport = Integer.parseInt(ports);
273           }
274         else
275           {
276             if (ports.indexOf("-", sep + 1) != -1)
277               throw new IllegalArgumentException("Unexpected '-'");
278
279             if (sep == 0)
280               {
281                 // an upper bound
282                 minport = MIN_PORT;
283                 maxport = Integer.parseInt(ports.substring(1));
284               }
285             else if (sep == ports.length() - 1)
286               {
287                 // a lower bound
288                 minport =
289                   Integer.parseInt(ports.substring(0, ports.length() - 1));
290                 maxport = MAX_PORT;
291               }
292             else
293               {
294                 // a range with two bounds
295                 minport = Integer.parseInt(ports.substring(0, sep));
296                 maxport = Integer.parseInt(ports.substring(sep + 1));
297               }
298           }
299       }
300   }
301   
302   /**
303    * Parse the actions argument to the constructor.
304    */
305   private void setActions(String actionstring)
306   {
307     actionmask = 0;
308
309     boolean resolve_needed = false;
310     boolean resolve_present = false;
311     
312     StringTokenizer t = new StringTokenizer(actionstring, ",");
313     while (t.hasMoreTokens())
314       {
315         String action = t.nextToken();
316         action = action.trim().toLowerCase();
317         setAction(action);
318
319         if (action.equals("resolve"))
320           resolve_present = true;
321         else
322           resolve_needed = true;
323       }
324
325     if (resolve_needed && !resolve_present)
326       setAction("resolve");
327   }
328
329   /**
330    * Parse one element of the actions argument to the constructor.
331    */
332   private void setAction(String action)
333   {
334     for (int i = 0; i < ACTIONS.length; i++)
335       {
336         if (action.equals(ACTIONS[i]))
337           {
338             actionmask |= 1 << i;
339             return;
340           }
341       }
342     throw new IllegalArgumentException("Unknown action " + action);
343   }
344
345   /**
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.
350    *
351    * @param obj The object to test against for equality
352    *
353    * @return <code>true</code> if object is equal to this object,
354    *         <code>false</code> otherwise.
355    */
356   public boolean equals(Object obj)
357   {
358     SocketPermission p;
359
360     if (obj instanceof SocketPermission)
361       p = (SocketPermission) obj;
362     else
363       return false;
364
365     return p.actionmask == actionmask &&
366       p.minport == minport &&
367       p.maxport == maxport &&
368       p.host.equals(host);
369   }
370
371   /**
372    * Returns a hash code value for this object.  Overrides the
373    * <code>Permission.hashCode()</code>.
374    *
375    * @return A hash code
376    */
377   public int hashCode()
378   {
379     return actionmask + minport + maxport + host.hashCode();
380   }
381
382   /**
383    * Returns the list of permission actions in this object in canonical
384    * order.  The canonical order is "connect,listen,accept,resolve"
385    *
386    * @return The permitted action string.
387    */
388   public String getActions()
389   {
390     StringBuffer sb = new StringBuffer("");
391
392     for (int i = 0; i < ACTIONS.length; i++)
393       {
394         if ((actionmask & (1 << i)) != 0)
395           {
396             if (sb.length() != 0)
397               sb.append(",");
398             sb.append(ACTIONS[i]);
399           }
400       }
401
402     return sb.toString();
403   }
404
405   /**
406    * Returns a new <code>PermissionCollection</code> object that can hold
407    * <code>SocketPermission</code>'s.
408    *
409    * @return A new <code>PermissionCollection</code>.
410    */
411   public PermissionCollection newPermissionCollection()
412   {
413     // FIXME: Implement
414
415     return null;
416   }
417
418   /**
419    * Returns true if the permission object passed it is implied by the
420    * this permission.  This will be true if:
421    * 
422    * <ul>
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>
427    * </ul>
428    *
429    * <p>The argument's hostname will be a subset of this object's hostname if:</p>
430    * 
431    * <ul>
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
435    * wildcards</li>
436    * </ul>
437    *
438    * @param perm The <code>Permission</code> to check against
439    *
440    * @return <code>true</code> if the <code>Permission</code> is implied by
441    * this object, <code>false</code> otherwise.
442    */
443   public boolean implies(Permission perm)
444   {
445     SocketPermission p;
446
447     // First make sure we are the right object type
448     if (perm instanceof SocketPermission)
449       p = (SocketPermission) perm;
450     else
451       return false;
452
453     // Next check the actions
454     if ((p.actionmask & actionmask) != p.actionmask)
455         return false;
456
457     // Then check the ports
458     if ((p.minport < minport) || (p.maxport > maxport))
459       return false;
460
461     // Finally check the hosts
462     if (host.equals(p.host))
463       return true;
464
465     // Try the canonical names
466     String ourcanonical = null;
467     String theircanonical = null;
468     try
469       {
470         ourcanonical = InetAddress.getByName(host).getHostName();
471         theircanonical = InetAddress.getByName(p.host).getHostName();
472       }
473     catch (UnknownHostException e)
474       {
475         // Who didn't resolve?  Just assume current address is canonical enough
476         // Is this ok to do?
477         if (ourcanonical == null)
478           ourcanonical = host;
479         if (theircanonical == null)
480           theircanonical = p.host;
481       }
482
483     if (ourcanonical.equals(theircanonical))
484       return true;
485
486     // Well, last chance.  Try for a wildcard
487     if (host.indexOf("*.") != -1)
488       {
489         String wild_domain =
490           host.substring(host.indexOf("*" + 1));
491         if (theircanonical.endsWith(wild_domain))
492           return true;
493       }
494
495     // Didn't make it
496     return false;
497   }
498
499   /**
500    * Deserializes a <code>SocketPermission</code> object from
501    * an input stream.
502    *
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.
507    */
508   private void readObject(ObjectInputStream input)
509     throws IOException, ClassNotFoundException
510   {
511     input.defaultReadObject();
512     setHostPort(getName());
513     setActions(actions);
514   }
515
516   /**
517    * Serializes a <code>SocketPermission</code> object to an
518    * output stream.
519    *
520    * @param output the output stream.
521    * @throws IOException if an I/O error occurs in the stream.
522    */
523   private void writeObject(ObjectOutputStream output)
524     throws IOException
525   {
526     actions = getActions();
527     output.defaultWriteObject();
528   }
529 }