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 }