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  package org.apache.james.util;
21  
22  import java.net.InetAddress;
23  import java.util.Collection;
24  import java.util.ArrayList;
25  import java.util.Iterator;
26  
27  public class NetMatcher
28  {
29      private ArrayList networks;
30  
31      public void initInetNetworks(final Collection nets)
32      {
33          networks = new ArrayList();
34          for (Iterator iter = nets.iterator(); iter.hasNext(); ) try
35          {
36              InetNetwork net = InetNetwork.getFromString((String) iter.next());
37              if (!networks.contains(net)) networks.add(net);
38          }
39          catch (java.net.UnknownHostException uhe)
40          {
41              log("Cannot resolve address: " + uhe.getMessage());
42          }
43          networks.trimToSize();
44      }
45  
46      public void initInetNetworks(final String[] nets)
47      {
48          networks = new ArrayList();
49          for (int i = 0; i < nets.length; i++) try
50          {
51              InetNetwork net = InetNetwork.getFromString(nets[i]);
52              if (!networks.contains(net)) networks.add(net);
53          }
54          catch (java.net.UnknownHostException uhe)
55          {
56              log("Cannot resolve address: " + uhe.getMessage());
57          }
58          networks.trimToSize();
59      }
60  
61      public boolean matchInetNetwork(final String hostIP)
62      {
63          InetAddress ip = null;
64  
65          try
66          {
67              ip = org.apache.james.dnsserver.DNSServer.getByName(hostIP);
68          }
69          catch (java.net.UnknownHostException uhe)
70          {
71              log("Cannot resolve address for " + hostIP + ": " + uhe.getMessage());
72          }
73  
74          boolean sameNet = false;
75  
76          if (ip != null) for (Iterator iter = networks.iterator(); (!sameNet) && iter.hasNext(); )
77          {
78              InetNetwork network = (InetNetwork) iter.next();
79              sameNet = network.contains(ip);
80          }
81          return sameNet;
82      }
83  
84      public boolean matchInetNetwork(final InetAddress ip)
85      {
86          boolean sameNet = false;
87  
88          for (Iterator iter = networks.iterator(); (!sameNet) && iter.hasNext(); )
89          {
90              InetNetwork network = (InetNetwork) iter.next();
91              sameNet = network.contains(ip);
92          }
93          return sameNet;
94      }
95  
96      public NetMatcher()
97      {
98      }
99  
100     public NetMatcher(final String[] nets)
101     {
102         initInetNetworks(nets);
103     }
104 
105     public NetMatcher(final Collection nets)
106     {
107         initInetNetworks(nets);
108     }
109 
110     public String toString() {
111         return networks.toString();
112     }
113 
114     protected void log(String s) { }
115 }
116 
117 class InetNetwork
118 {
119     /*
120      * Implements network masking, and is compatible with RFC 1518 and
121      * RFC 1519, which describe CIDR: Classless Inter-Domain Routing.
122      */
123 
124     private InetAddress network;
125     private InetAddress netmask;
126 
127     public InetNetwork(InetAddress ip, InetAddress netmask)
128     {
129         network = maskIP(ip, netmask);
130         this.netmask = netmask;
131     }
132 
133     public boolean contains(final String name) throws java.net.UnknownHostException
134     {
135         return network.equals(maskIP(org.apache.james.dnsserver.DNSServer.getByName(name), netmask));
136     }
137 
138     public boolean contains(final InetAddress ip)
139     {
140         return network.equals(maskIP(ip, netmask));
141     }
142 
143     public String toString()
144     {
145         return network.getHostAddress() + "/" + netmask.getHostAddress();
146     }
147 
148     public int hashCode()
149     {
150         return maskIP(network, netmask).hashCode();
151     }
152 
153     public boolean equals(Object obj)
154     {
155         return (obj != null) && (obj instanceof InetNetwork) &&
156                 ((((InetNetwork)obj).network.equals(network)) && (((InetNetwork)obj).netmask.equals(netmask)));
157     }
158 
159     public static InetNetwork getFromString(String netspec) throws java.net.UnknownHostException
160     {
161         if (netspec.endsWith("*")) netspec = normalizeFromAsterisk(netspec);
162         else
163         {
164             int iSlash = netspec.indexOf('/');
165             if (iSlash == -1) netspec += "/255.255.255.255";
166             else if (netspec.indexOf('.', iSlash) == -1) netspec = normalizeFromCIDR(netspec);
167         }
168 
169         return new InetNetwork(org.apache.james.dnsserver.DNSServer.getByName(netspec.substring(0, netspec.indexOf('/'))),
170                                org.apache.james.dnsserver.DNSServer.getByName(netspec.substring(netspec.indexOf('/') + 1)));
171     }
172 
173     public static InetAddress maskIP(final byte[] ip, final byte[] mask)
174     {
175         try
176         {
177             return getByAddress(new byte[]
178             {
179                 (byte) (mask[0] & ip[0]),
180                 (byte) (mask[1] & ip[1]),
181                 (byte) (mask[2] & ip[2]),
182                 (byte) (mask[3] & ip[3])
183             });
184         }
185         catch(Exception _) {}
186         {
187             return null;
188         }
189     }
190 
191     public static InetAddress maskIP(final InetAddress ip, final InetAddress mask)
192     {
193         return maskIP(ip.getAddress(), mask.getAddress());
194     }
195 
196     /*
197      * This converts from an uncommon "wildcard" CIDR format
198      * to "address + mask" format:
199      * 
200      *   *               =>  000.000.000.0/000.000.000.0
201      *   xxx.*           =>  xxx.000.000.0/255.000.000.0
202      *   xxx.xxx.*       =>  xxx.xxx.000.0/255.255.000.0
203      *   xxx.xxx.xxx.*   =>  xxx.xxx.xxx.0/255.255.255.0
204      */
205     static private String normalizeFromAsterisk(final String netspec)
206     {
207         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" };
208         char[] srcb = netspec.toCharArray();                
209         int octets = 0;
210         for (int i = 1; i < netspec.length(); i++) {
211             if (srcb[i] == '.') octets++;
212         }
213         return (octets == 0) ? masks[0] : netspec.substring(0, netspec.length() -1 ).concat(masks[octets]);
214     }
215 
216     /*
217      * RFC 1518, 1519 - Classless Inter-Domain Routing (CIDR)
218      * This converts from "prefix + prefix-length" format to
219      * "address + mask" format, e.g. from xxx.xxx.xxx.xxx/yy
220      * to xxx.xxx.xxx.xxx/yyy.yyy.yyy.yyy.
221      */
222     static private String normalizeFromCIDR(final String netspec)
223     {
224         final int bits = 32 - Integer.parseInt(netspec.substring(netspec.indexOf('/')+1));
225         final int mask = (bits == 32) ? 0 : 0xFFFFFFFF - ((1 << bits)-1);
226 
227         return netspec.substring(0, netspec.indexOf('/') + 1) +
228                 Integer.toString(mask >> 24 & 0xFF, 10) + "." +
229                 Integer.toString(mask >> 16 & 0xFF, 10) + "." +
230                 Integer.toString(mask >>  8 & 0xFF, 10) + "." +
231                 Integer.toString(mask >>  0 & 0xFF, 10);
232     }
233 
234     private static java.lang.reflect.Method getByAddress = null;
235 
236     static {
237         try {
238             Class inetAddressClass = Class.forName("java.net.InetAddress");
239             Class[] parameterTypes = { byte[].class };
240             getByAddress = inetAddressClass.getMethod("getByAddress", parameterTypes);
241         } catch (Exception e) {
242             getByAddress = null;
243         }
244     }
245 
246     private static InetAddress getByAddress(byte[] ip) throws java.net.UnknownHostException
247     {
248         InetAddress addr = null;
249         if (getByAddress != null) try {
250             addr = (InetAddress) getByAddress.invoke(null, new Object[] { ip });
251         } catch (IllegalAccessException e) {
252         } catch (java.lang.reflect.InvocationTargetException e) {
253         }
254 
255         if (addr == null) {
256             addr = InetAddress.getByName
257                    (
258                     Integer.toString(ip[0] & 0xFF, 10) + "." +
259                     Integer.toString(ip[1] & 0xFF, 10) + "." +
260                     Integer.toString(ip[2] & 0xFF, 10) + "." +
261                     Integer.toString(ip[3] & 0xFF, 10)
262                    );
263         }
264         return addr;
265     }
266 }