View Javadoc

1   /****************************************************************
2    * Licensed to the Apache Software Foundation (ASF) under one   *
3    * or more contributor license agreements.  See the NOTICE file *
4    * distributed with this work for additional information        *
5    * regarding copyright ownership.  The ASF licenses this file   *
6    * to you under the Apache License, Version 2.0 (the            *
7    * "License"); you may not use this file except in compliance   *
8    * with the License.  You may obtain a copy of the License at   *
9    *                                                              *
10   *   http://www.apache.org/licenses/LICENSE-2.0                 *
11   *                                                              *
12   * Unless required by applicable law or agreed to in writing,   *
13   * software distributed under the License is distributed on an  *
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
15   * KIND, either express or implied.  See the License for the    *
16   * specific language governing permissions and limitations      *
17   * under the License.                                           *
18   ****************************************************************/
19  
20  
21  
22  package org.apache.james.api.dnsservice.util;
23  
24  import java.net.InetAddress;
25  import java.net.UnknownHostException;
26  
27  import org.apache.james.api.dnsservice.DNSService;
28  
29  class InetNetwork
30  {
31  
32      /*
33       * Implements network masking, and is compatible with RFC 1518 and
34       * RFC 1519, which describe CIDR: Classless Inter-Domain Routing.
35       */
36  
37      private InetAddress network;
38      private InetAddress netmask;
39      private DNSService dnsServer;
40      
41      /**
42       * Constructor
43       * 
44       * @param dnsServer the DNSService to use
45       */
46      InetNetwork(DNSService dnsServer) {
47          this.dnsServer = dnsServer;
48      }
49  
50      /**
51       * Constuctor
52       * 
53       * @param ip the InetAddress to init the class
54       * @param netmask the InetAddress represent the netmask to init the class
55       */
56      public InetNetwork(InetAddress ip, InetAddress netmask)
57      {
58          network = maskIP(ip, netmask);
59          this.netmask = netmask;
60      }
61  
62      /**
63       * Return true if the network contains the given name
64       * 
65       * @param name hostname or ipAddress
66       * @return true if the network contains the given name
67       * @throws java.net.UnknownHostException if the given name can not resolved
68       */
69      public boolean contains(final String name) throws java.net.UnknownHostException
70      {
71          return network.equals(maskIP(dnsServer.getByName(name), netmask));
72      }
73  
74      /**
75       * @see #contains(String)
76       */
77      public boolean contains(final InetAddress ip)
78      {
79          return network.equals(maskIP(ip, netmask));
80      }
81  
82      /**
83       * Return String represention of this class 
84       * 
85       * @return string String representation of this class
86       */
87      public String toString()
88      {
89          return network.getHostAddress() + "/" + netmask.getHostAddress();
90      }
91  
92      /**
93       * Return hashCode representation of this class
94       * 
95       * @return hashCode the hashCode representation of this class
96       */
97      public int hashCode()
98      {
99          return maskIP(network, netmask).hashCode();
100     }
101 
102     /**
103      * @see java.lang.Object#equals(java.lang.Object)
104      */
105     public boolean equals(Object obj)
106     {
107         return (obj != null) && (obj instanceof InetNetwork) &&
108                 ((((InetNetwork)obj).network.equals(network)) && (((InetNetwork)obj).netmask.equals(netmask)));
109     }
110 
111     /**
112      * Get InetNetwork of the given String
113      * 
114      * @param netspec the String which is will converted to InetNetwork
115      * @return network the InetNetwork
116      * @throws java.net.UnknownHostException
117      */
118     public InetNetwork getFromString(String netspec) throws java.net.UnknownHostException
119     {
120         if (netspec.endsWith("*")) netspec = normalizeFromAsterisk(netspec);
121         else
122         {
123             int iSlash = netspec.indexOf('/');
124             if (iSlash == -1) netspec += "/255.255.255.255";
125             else if (netspec.indexOf('.', iSlash) == -1) netspec = normalizeFromCIDR(netspec);
126         }
127 
128         return new InetNetwork(dnsServer.getByName(netspec.substring(0, netspec.indexOf('/'))),
129                                dnsServer.getByName(netspec.substring(netspec.indexOf('/') + 1)));
130     }
131 
132     /**
133      * Return InetAddress generated of the passed argements. Return Null if any errors accour
134      * 
135      * @param ip the byte[] represent the ip
136      * @param mask the byte[] represent the netmask
137      * @return inetAddress the InetAddress generated of the passed arguments. 
138      */
139     public static InetAddress maskIP(final byte[] ip, final byte[] mask)
140     {
141         try {
142             return getByAddress(new byte[] {
143             (byte) (mask[0] & ip[0]),
144             (byte) (mask[1] & ip[1]),
145             (byte) (mask[2] & ip[2]),
146             (byte) (mask[3] & ip[3])
147         });
148     } catch (UnknownHostException e) {
149             return null;
150     }
151     }
152 
153     /**
154      * @see #maskIP(byte[], byte[])
155      */
156     public static InetAddress maskIP(final InetAddress ip, final InetAddress mask)
157     {
158         return maskIP(ip.getAddress(), mask.getAddress());
159     }
160 
161     /**
162      * This converts from an uncommon "wildcard" CIDR format
163      * to "address + mask" format:
164      * 
165      *   *               =>  000.000.000.0/000.000.000.0
166      *   xxx.*           =>  xxx.000.000.0/255.000.000.0
167      *   xxx.xxx.*       =>  xxx.xxx.000.0/255.255.000.0
168      *   xxx.xxx.xxx.*   =>  xxx.xxx.xxx.0/255.255.255.0
169      * 
170      * @param netspec 
171      * @return addrMask the address/mask of the given argument
172      */
173     static private String normalizeFromAsterisk(final String netspec)
174     {
175         String[] masks = {  "0.0.0.0/0.0.0.0", "0.0.0/255.0.0.0", "0.0/255.255.0.0", "0/255.255.255.0" };
176         char[] srcb = netspec.toCharArray();                
177         int octets = 0;
178         for (int i = 1; i < netspec.length(); i++) {
179             if (srcb[i] == '.') octets++;
180         }
181         return (octets == 0) ? masks[0] : netspec.substring(0, netspec.length() -1 ).concat(masks[octets]);
182     }
183 
184     /**
185      * RFC 1518, 1519 - Classless Inter-Domain Routing (CIDR)
186      * This converts from "prefix + prefix-length" format to
187      * "address + mask" format, e.g. from xxx.xxx.xxx.xxx/yy
188      * to xxx.xxx.xxx.xxx/yyy.yyy.yyy.yyy.
189      * 
190      * @param netspec the xxx.xxx.xxx.xxx/yyy format
191      * @return addrMask the xxx.xxx.xxx.xxx/yyy.yyy.yyy.yyy format 
192      */
193     static private String normalizeFromCIDR(final String netspec)
194     {
195         final int bits = 32 - Integer.parseInt(netspec.substring(netspec.indexOf('/')+1));
196         final int mask = (bits == 32) ? 0 : 0xFFFFFFFF - ((1 << bits)-1);
197 
198         return netspec.substring(0, netspec.indexOf('/') + 1) +
199                 Integer.toString(mask >> 24 & 0xFF, 10) + "." +
200                 Integer.toString(mask >> 16 & 0xFF, 10) + "." +
201                 Integer.toString(mask >>  8 & 0xFF, 10) + "." +
202                 Integer.toString(mask >>  0 & 0xFF, 10);
203     }
204 
205     private static java.lang.reflect.Method getByAddress = null;
206 
207     static {
208         try {
209             Class inetAddressClass = Class.forName("java.net.InetAddress");
210             Class[] parameterTypes = { byte[].class };
211             getByAddress = inetAddressClass.getMethod("getByAddress", parameterTypes);
212         } catch (Exception e) {
213             getByAddress = null;
214         }
215     }
216 
217     /**
218      * Return InetAddress which represent the given byte[]
219      * 
220      * @param ip the byte[] represent the ip
221      * @return ip the InetAddress generated of the given byte[]
222      * @throws java.net.UnknownHostException
223      */
224     private static InetAddress getByAddress(byte[] ip) throws java.net.UnknownHostException
225     {
226         InetAddress addr = null;
227         if (getByAddress != null) try {
228             addr = (InetAddress) getByAddress.invoke(null, new Object[] { ip });
229         } catch (IllegalAccessException e) {
230         } catch (java.lang.reflect.InvocationTargetException e) {
231         }
232 
233         if (addr == null) {
234             addr = InetAddress.getByName
235                    (
236                     Integer.toString(ip[0] & 0xFF, 10) + "." +
237                     Integer.toString(ip[1] & 0xFF, 10) + "." +
238                     Integer.toString(ip[2] & 0xFF, 10) + "." +
239                     Integer.toString(ip[3] & 0xFF, 10)
240                    );
241         }
242         return addr;
243     }
244 }