OSDN Git Service

2006-09-20 Gary Benson <gbenson@redhat.com>
[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).  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, last_colon = 0;
197     for (int i = 0; i < hostport.length(); i++)
198       {
199         if (hostport.charAt(i) == ':')
200           {
201             if (i - last_colon == 1)
202               throw new IllegalArgumentException("Ambiguous hostport part");
203             colons++;
204             last_colon = i;
205           }
206       }
207
208     switch (colons)
209       {
210       case 0:
211       case 1:
212         // a hostname or IPv4 address
213         return hostport;
214         
215       case 7:
216         // an IPv6 address with no ports
217         return "[" + hostport + "]";
218
219       case 8:
220         // an IPv6 address with ports
221         return "[" + hostport.substring(0, last_colon) + "]"
222           + hostport.substring(last_colon);
223
224       default:
225         throw new IllegalArgumentException("Ambiguous hostport part");
226       }
227   }
228   
229   /**
230    * Parse the hostport argument to the constructor.
231    */
232   private void setHostPort(String hostport)
233   {
234     // Split into host and ports
235     String host, ports;
236     if (hostport.charAt(0) == '[')
237       {
238         // host is a bracketed IPv6 address
239         int end = hostport.indexOf("]");
240         if (end == -1)
241           throw new IllegalArgumentException("Unmatched '['");
242         host = hostport.substring(1, end);
243
244         address = InetAddress.getByLiteral(host);
245         if (address == null)
246           throw new IllegalArgumentException("Bad IPv6 address");
247
248         if (end == hostport.length() - 1)
249           ports = "";
250         else if (hostport.charAt(end + 1) == ':')
251           ports = hostport.substring(end + 2);
252         else
253           throw new IllegalArgumentException("Bad character after ']'");
254       }
255     else
256       {
257         // host is a hostname or IPv4 address
258         int sep = hostport.indexOf(":");
259         if (sep == -1)
260           {
261             host = hostport;
262             ports = "";
263           }
264         else
265           {
266             host = hostport.substring(0, sep);
267             ports = hostport.substring(sep + 1);
268           }
269
270         address = InetAddress.getByLiteral(host);
271         if (address == null)
272           {
273             if (host.lastIndexOf('*') > 0)
274               throw new IllegalArgumentException("Bad hostname");
275
276             hostname = host;
277           }
278       }
279
280     // Parse and validate the ports
281     if (ports.length() == 0)
282       {
283         minport = MIN_PORT;
284         maxport = MAX_PORT;
285       }
286     else
287       {
288         int sep = ports.indexOf("-");
289         if (sep == -1)
290           {
291             // a single port
292             minport = maxport = Integer.parseInt(ports);
293           }
294         else
295           {
296             if (ports.indexOf("-", sep + 1) != -1)
297               throw new IllegalArgumentException("Unexpected '-'");
298
299             if (sep == 0)
300               {
301                 // an upper bound
302                 minport = MIN_PORT;
303                 maxport = Integer.parseInt(ports.substring(1));
304               }
305             else if (sep == ports.length() - 1)
306               {
307                 // a lower bound
308                 minport =
309                   Integer.parseInt(ports.substring(0, ports.length() - 1));
310                 maxport = MAX_PORT;
311               }
312             else
313               {
314                 // a range with two bounds
315                 minport = Integer.parseInt(ports.substring(0, sep));
316                 maxport = Integer.parseInt(ports.substring(sep + 1));
317               }
318           }
319       }
320   }
321   
322   /**
323    * Parse the actions argument to the constructor.
324    */
325   private void setActions(String actionstring)
326   {
327     actionmask = 0;
328
329     boolean resolve_needed = false;
330     boolean resolve_present = false;
331     
332     StringTokenizer t = new StringTokenizer(actionstring, ",");
333     while (t.hasMoreTokens())
334       {
335         String action = t.nextToken();
336         action = action.trim().toLowerCase();
337         setAction(action);
338
339         if (action.equals("resolve"))
340           resolve_present = true;
341         else
342           resolve_needed = true;
343       }
344
345     if (resolve_needed && !resolve_present)
346       setAction("resolve");
347   }
348
349   /**
350    * Parse one element of the actions argument to the constructor.
351    */
352   private void setAction(String action)
353   {
354     for (int i = 0; i < ACTIONS.length; i++)
355       {
356         if (action.equals(ACTIONS[i]))
357           {
358             actionmask |= 1 << i;
359             return;
360           }
361       }
362     throw new IllegalArgumentException("Unknown action " + action);
363   }
364
365   /**
366    * Tests this object for equality against another.  This will be true if
367    * and only if the passed object is an instance of
368    * <code>SocketPermission</code> and both its hostname/port combination
369    * and permissions string are identical.
370    *
371    * @param obj The object to test against for equality
372    *
373    * @return <code>true</code> if object is equal to this object,
374    *         <code>false</code> otherwise.
375    */
376   public boolean equals(Object obj)
377   {
378     SocketPermission p;
379
380     if (obj instanceof SocketPermission)
381       p = (SocketPermission) obj;
382     else
383       return false;
384
385     if (p.actionmask != actionmask ||
386         p.minport != minport ||
387         p.maxport != maxport)
388       return false;
389
390     if (address != null)
391       {
392         if (p.address == null)
393           return false;
394         else
395           return p.address.equals(address);
396       }
397     else
398       {
399         if (p.hostname == null)
400           return false;
401         else
402           return p.hostname.equals(hostname);
403       }
404   }
405
406   /**
407    * Returns a hash code value for this object.  Overrides the
408    * <code>Permission.hashCode()</code>.
409    *
410    * @return A hash code
411    */
412   public int hashCode()
413   {
414     int code = actionmask + minport + maxport;
415     if (address != null)
416       code += address.hashCode();
417     else
418       code += hostname.hashCode();
419     return code;
420   }
421
422   /**
423    * Returns the list of permission actions in this object in canonical
424    * order.  The canonical order is "connect,listen,accept,resolve"
425    *
426    * @return The permitted action string.
427    */
428   public String getActions()
429   {
430     StringBuffer sb = new StringBuffer("");
431
432     for (int i = 0; i < ACTIONS.length; i++)
433       {
434         if ((actionmask & (1 << i)) != 0)
435           {
436             if (sb.length() != 0)
437               sb.append(",");
438             sb.append(ACTIONS[i]);
439           }
440       }
441
442     return sb.toString();
443   }
444
445   /**
446    * Returns a new <code>PermissionCollection</code> object that can hold
447    * <code>SocketPermission</code>'s.
448    *
449    * @return A new <code>PermissionCollection</code>.
450    */
451   public PermissionCollection newPermissionCollection()
452   {
453     // FIXME: Implement
454
455     return null;
456   }
457
458   /**
459    * Returns an array of all IP addresses represented by this object.
460    */
461   private InetAddress[] getAddresses()
462   {
463     if (address != null)
464       return new InetAddress[] {address};
465
466     try
467       {
468         return InetAddress.getAllByName(hostname);
469       }
470     catch (UnknownHostException e)
471       {
472         return new InetAddress[0];
473       }
474   }
475
476   /**
477    * Returns the canonical hostname represented by this object,
478    * or null if this object represents a wildcarded domain.
479    */
480   private String getCanonicalHostName()
481   {
482     if (address != null)
483       return address.internalGetCanonicalHostName();
484     if (hostname.charAt(0) == '*')
485       return null;
486     try
487       {
488         return InetAddress.getByName(hostname).internalGetCanonicalHostName();
489       }
490     catch (UnknownHostException e)
491       {
492         return null;
493       }
494   }
495   
496   /**
497    * Returns true if the permission object passed it is implied by the
498    * this permission.  This will be true if:
499    * 
500    * <ul>
501    * <li>The argument is of type <code>SocketPermission</code></li>
502    * <li>The actions list of the argument are in this object's actions</li>
503    * <li>The port range of the argument is within this objects port range</li>
504    * <li>The hostname is equal to or a subset of this objects hostname</li>
505    * </ul>
506    *
507    * <p>The argument's hostname will be a subset of this object's hostname if:</p>
508    * 
509    * <ul>
510    * <li>The argument's hostname or IP address is equal to this object's.</li>
511    * <li>The argument's canonical hostname is equal to this object's.</li>
512    * <li>The argument's canonical name matches this domains hostname with
513    * wildcards</li>
514    * </ul>
515    *
516    * @param perm The <code>Permission</code> to check against
517    *
518    * @return <code>true</code> if the <code>Permission</code> is implied by
519    * this object, <code>false</code> otherwise.
520    */
521   public boolean implies(Permission perm)
522   {
523     SocketPermission p;
524
525     // First make sure we are the right object type
526     if (perm instanceof SocketPermission)
527       p = (SocketPermission) perm;
528     else
529       return false;
530
531     // If p was initialised with an empty hostname then we do not
532     // imply it. This is not part of the spec, but it seems necessary.
533     if (p.hostname != null && p.hostname.length() == 0)
534       return false;
535     
536     // Next check the actions
537     if ((p.actionmask & actionmask) != p.actionmask)
538         return false;
539
540     // Then check the ports
541     if ((p.minport < minport) || (p.maxport > maxport))
542       return false;
543
544     // Finally check the hosts
545     String p_canon = null;
546
547     // Return true if this object was initialized with a single
548     // IP address which one of p's IP addresses is equal to.
549     if (address != null)
550       {
551         InetAddress[] addrs = p.getAddresses();
552         for (int i = 0; i < addrs.length; i++)
553           {
554             if (address.equals(addrs[i]))
555               return true;
556           }
557       }
558
559     // Return true if this object is a wildcarded domain that
560     // p's canonical name matches.
561     if (hostname != null && hostname.charAt(0) == '*')
562       {
563         p_canon = p.getCanonicalHostName();
564         if (p_canon != null && p_canon.endsWith(hostname.substring(1)))
565           return true;
566         
567       }
568
569     // Return true if this one of this object's IP addresses
570     // is equal to one of p's.
571     if (address == null)
572       {
573         InetAddress[] addrs = p.getAddresses();
574         InetAddress[] p_addrs = p.getAddresses();
575
576         for (int i = 0; i < addrs.length; i++)
577           {
578             for (int j = 0; j < p_addrs.length; j++)
579               {
580                 if (addrs[i].equals(p_addrs[j]))
581                   return true;
582               }
583           }
584       }
585
586     // Return true if this object's canonical name equals p's.
587     String canon = getCanonicalHostName();
588     if (canon != null)
589       {
590         if (p_canon == null)
591           p_canon = p.getCanonicalHostName();
592         if (p_canon != null && canon.equals(p_canon))
593           return true;
594       }
595
596     // Didn't make it
597     return false;
598   }
599
600   /**
601    * Deserializes a <code>SocketPermission</code> object from
602    * an input stream.
603    *
604    * @param input the input stream.
605    * @throws IOException if an I/O error occurs in the stream.
606    * @throws ClassNotFoundException if the class of the
607    *         serialized object could not be found.
608    */
609   private void readObject(ObjectInputStream input)
610     throws IOException, ClassNotFoundException
611   {
612     input.defaultReadObject();
613     setHostPort(getName());
614     setActions(actions);
615   }
616
617   /**
618    * Serializes a <code>SocketPermission</code> object to an
619    * output stream.
620    *
621    * @param output the output stream.
622    * @throws IOException if an I/O error occurs in the stream.
623    */
624   private void writeObject(ObjectOutputStream output)
625     throws IOException
626   {
627     actions = getActions();
628     output.defaultWriteObject();
629   }
630 }