KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > lenya > ac > impl > AbstractIPRange


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */

17
18 /* $Id: AbstractIPRange.java 43241 2004-08-16 16:36:57Z andreas $ */
19
20 package org.apache.lenya.ac.impl;
21
22 import java.io.File JavaDoc;
23 import java.net.InetAddress JavaDoc;
24 import java.net.UnknownHostException JavaDoc;
25 import java.util.Arrays JavaDoc;
26
27 import org.apache.lenya.ac.AccessControlException;
28 import org.apache.lenya.ac.IPRange;
29 import org.apache.lenya.ac.Machine;
30 import org.apache.lenya.net.InetAddressUtil;
31 import org.apache.log4j.Category;
32
33 /**
34  * <p>
35  * A range of IP addresses, expressed by a network address and a subnet mask.
36  * </p>
37  * <p>
38  * Note: this class does not enforce that the network address and the subnet mask have the same size
39  * (i.e. either both IPv4 or both IPv6 addresses). If the the network address and subnet mask have
40  * different sizes, the range does not contain any hosts, that is {@link #contains(Machine)} will
41  * always return <code>false</code>.
42  * </p>
43  */

44 public abstract class AbstractIPRange extends AbstractGroupable implements IPRange {
45     /*
46      * FIXME by zisch@dals.ch: Fixed this class for IPv6. However there are still some general
47      * flaws, partly coming from the IPRange interface. A redesign of (Abstract/File)IPRange and
48      * it's helper class org.apache.lenya.net.InetAddressUtil would be a good idea. Some problems of
49      * this implementation are:
50      * - The whole initialization seems flawed. Objects can be in an unitialized state and the
51      * class seems not to be aware of this.
52      * - Network-address and -mask can be set independently. Therefore it cannot be enforced that
53      * these have the same size (i.e. that both are IPv4 or both are IPv6). This shows up in
54      * InetAddressUtil.contains(...), where in a case of mismatch there is no good way to inform the
55      * user about the problem. This should be done once when the AbstractIPRange object is
56      * initialized.
57      * - Unless this functionality would be needed by other parts of Lenya or external software
58      * (which seems not to be the case ;-), InetAddressUtil should be removed (resp. deprecated)
59      * altogether, because it's mostly an internal implementation detail of AbstractIPRange.
60      * AbstractIPRange should implement the contains(...)-method internally to make use of the fact
61      * that the network- addresses and -masks validity and compatibility has already been checked
62      * when setting these. (Once the above problems have been fixed. ;-)
63      * - Especially for IPv6 it would be nice to have the possibility to specify the netmask as the
64      * number of bits (as in "::1/128" or "127.0.0.1/24").
65      * - I think, that logging should probably work the "Cocoon-Way", as explained in
66      * <http://wiki.cocoondev.org/Wiki.jsp?page=JavaLogging>, rather than using
67      * org.apache.log4j.Category. (But I may be wrong. ;-)
68      *
69      * FIXME II (from the previous version): why are we in the business of implementing IP ranges??
70      */

71
72     private static final Category log = Category.getInstance(AbstractIPRange.class);
73
74     /**
75      * Initializes the the IP range with the local host (127.0.0.1/24 for IPv4, ::1/128 for IPv6).
76      */

77     public AbstractIPRange() {
78         try {
79             networkAddress = InetAddress.getLocalHost();
80             byte[] mask = null;
81             int masklen = networkAddress.getAddress().length;
82             if (masklen == 4) {
83                 /* IPv4: */
84                 /*
85                  * FIXME? by zisch@dals.ch: Should this be { -1, 0, 0, 0 }??
86                  */

87                 mask = new byte[] { -1, -1, -1, 0 };
88             } else {
89                 /* IPv6 (and others ;-): */
90                 mask = new byte[masklen];
91                 Arrays.fill(mask, (byte) -1);
92             }
93             subnetMask = InetAddress.getByAddress(mask);
94         } catch (UnknownHostException JavaDoc ignore) {
95             /*
96              * FIXME? by zisch@dals.ch: Is it safe to ignore the exception and just leave the
97              * IPRange uninitialized!?
98              */

99         }
100     }
101
102     /**
103      * Ctor.
104      * @param id The IP range ID.
105      */

106     public AbstractIPRange(String JavaDoc id) {
107         /*
108          * FIXME? by zisch@dals.ch: Is it safe not to call the default constructor and just leave
109          * the IPRange uninitialized!?
110          */

111         setId(id);
112     }
113
114     private File JavaDoc configurationDirectory;
115
116     /**
117      * Returns the configuration directory.
118      * @return A file object.
119      */

120     public File JavaDoc getConfigurationDirectory() {
121         return configurationDirectory;
122     }
123
124     /**
125      * @see org.apache.lenya.ac.Item#setConfigurationDirectory(java.io.File)
126      */

127     public void setConfigurationDirectory(File JavaDoc configurationDirectory) {
128         this.configurationDirectory = configurationDirectory;
129     }
130
131     /**
132      * Save the IP range
133      *
134      * @throws AccessControlException if the save failed
135      */

136     public abstract void save() throws AccessControlException;
137
138     /**
139      * Delete an IP range
140      *
141      * @throws AccessControlException if the delete failed
142      */

143     public void delete() throws AccessControlException {
144         removeFromAllGroups();
145     }
146
147     private InetAddress JavaDoc networkAddress;
148
149     /**
150      * Sets the network address. This method accepts numeric IPv4 addresses like
151      * <code>"129.168.0.32"</code>, numeric IPv6 addresses like
152      * <code>"1080::8:800:200C:417A"</code> as well as hostnames (if DNS resolution is available)
153      * like <code>"localhost"</code> or <code>"www.apache.com"</code>.
154      *
155      * @param address a <code>String</code> like <code>"192.168.0.32"</code>,
156      * <code>"::1"</code>, ...
157      *
158      * @throws AccessControlException when the conversion of the <code>String</code> to an
159      * <code>InetAddress</code> failed
160      *
161      * @see #setNetworkAddress(byte[])
162      */

163     public void setNetworkAddress(String JavaDoc address) throws AccessControlException {
164         try {
165             networkAddress = InetAddress.getByName(address);
166         } catch (UnknownHostException JavaDoc e) {
167             throw new AccessControlException("Failed to convert address [" + address + "]: ", e);
168         }
169     }
170
171     /**
172      * Sets the network address. The method accepts numeric IPv4 addresses (specified by byte arrays
173      * of length 4) or IPv6 addresses (specified by byte arrays of length 16).
174      *
175      * @param address a byte array of the length 4 or 16
176      *
177      * @throws AccessControlException when the conversion of the byte array to an InetAddress
178      * failed.
179      *
180      * @see #setNetworkAddress(String)
181      */

182     public void setNetworkAddress(byte[] address) throws AccessControlException {
183         try {
184             networkAddress = InetAddress.getByAddress(address);
185         } catch (UnknownHostException JavaDoc e) {
186             throw new AccessControlException("Failed to convert address [" + addr2string(address)
187                     + "]: ", e);
188         }
189     }
190
191     /**
192      * Returns the network address.
193      *
194      * @return an <code>InetAddress</code> representing the network address
195      */

196     public InetAddress JavaDoc getNetworkAddress() {
197         return networkAddress;
198     }
199
200     private InetAddress JavaDoc subnetMask;
201
202     /**
203      * Sets the subnet mask. See {@link #setNetworkAddress(String)} for the allowed formats of the
204      * <code>mask</code> string. (However, the hostname format will usually not be of much use for
205      * setting the mask.)
206      * <p>
207      * Only valid subnet masks are accepted, for which the binary representation is a sequence of
208      * 1-bits followed by a sequence of 0-bits. For example <code>"255.128.0.0"</code> is valid
209      * while <code>"255.128.0.1"</code> is not.
210      *
211      * @param mask a <code>String</code> like <code>"255.255.255.0"</code>
212      *
213      * @throws AccessControlException when the conversion of the String to an
214      * <code>InetAddress</code> failed.
215      *
216      * @see #setSubnetMask(byte[])
217      */

218     public void setSubnetMask(String JavaDoc mask) throws AccessControlException {
219         try {
220             /* use setSubnetMask(...) to check the mask-format: */
221             setSubnetMask(InetAddress.getByName(mask).getAddress());
222         } catch (UnknownHostException JavaDoc e) {
223             throw new AccessControlException("Failed to convert mask [" + mask + "]: ", e);
224         }
225
226     }
227
228     /**
229      * Sets the subnet mask.
230      * <p>
231      * Only valid subnet masks are accepted, for which the binary representation is a sequence of
232      * 1-bits followed by a sequence of 0-bits. For example <code>{ 255, 128, 0, 0 }</code> is
233      * valid while <code>{ 255, 128, 0, 1 }</code> is not.
234      *
235      * @param mask A byte array of the length 4.
236      *
237      * @throws AccessControlException when the conversion of the byte array to an InetAddress
238      * failed.
239      *
240      * @see #setSubnetMask(String)
241      */

242     public void setSubnetMask(byte[] mask) throws AccessControlException {
243         /*
244          * check for correct netmask (i.e. any number of 1-bits followed by 0-bits filling the right
245          * part of the mask) ...
246          *
247          * FIXME: This "algorithm" is rather unelegant. There should be a better way to do it! ;-)
248          */

249         if (log.isDebugEnabled()) {
250             log.debug("CHECK_NETMASK: check " + addr2string(mask));
251         }
252         int i = 0;
253         CHECK_NETMASK: while (i < mask.length) {
254             int b = mask[i++] & 0xff;
255             /* the initial byte(s) must be 255: */
256             if (b != 0xff) {
257                 /* first byte != 255, test all possibilities: */
258                 if (log.isDebugEnabled()) {
259                     log.debug("CHECK_NETMASK: first byte != 255: idx: " + (i - 1)
260                             + ", mask[idx]: 0x" + b);
261                 }
262                 /* check if 0: */
263                 if (b == 0) {
264                     break CHECK_NETMASK;
265                 }
266                 for (int tst = 0xfe; tst != 0; tst = (tst << 1) & 0xff) {
267                     log.debug("CHECK_NETMASK: tst == 0x" + Integer.toHexString(tst));
268                     if (b == tst) {
269                         break CHECK_NETMASK;
270                     }
271                 }
272                 /*
273                  * Invalid byte found, i.e. one which is not element of { 11111111, 11111110,
274                  * 11111100, 11111000, ..., 00000000 }
275                  */

276                 throw new AccessControlException("Invalid byte in mask [" + addr2string(mask) + "]");
277             }
278         }
279         /* the remaining byte(s) (if any) must be 0: */
280         while (++i < mask.length) {
281             if (mask[i] != 0) {
282                 /*
283                  * Invalid byte found, i.e. some non-zero byte right of the first non-zero byte.
284                  */

285                 throw new AccessControlException("Invalid non-zero byte in mask ["
286                         + addr2string(mask) + "]");
287             }
288         }
289
290         /* convert the checked mask to InetAddress: */
291         try {
292             subnetMask = InetAddress.getByAddress(mask);
293         } catch (UnknownHostException JavaDoc e) {
294             throw new AccessControlException(
295                     "Failed to convert mask [" + addr2string(mask) + "]: ", e);
296         }
297     }
298
299     /**
300      * Returns the subnet mask.
301      * @return An InetAddress value.
302      */

303     public InetAddress JavaDoc getSubnetMask() {
304         return subnetMask;
305     }
306
307     /**
308      * Checks if a network address / subnet mask combination describes a valid subnet.
309      * @param networkAddress The network address.
310      * @param subnetMask The subnet mask.
311      * @return A boolean value.
312      *
313      * @deprecated This method is currently not implemented, probably not necessary.and could be
314      * removed in the future. Therefore it should not be used.
315      */

316     public static boolean isValidSubnet(InetAddress JavaDoc networkAddress, InetAddress JavaDoc subnetMask) {
317         /*
318          * FIXME? by zisch@dals.ch: Is this method really necessary (what for?) and (if so)
319          * shouldn't it be an internal (private) utility-method??
320          */

321         // TODO implement class
322
return false;
323     }
324
325     /**
326      * Checks if this IP range contains a certain machine.
327      * <p>
328      * Note: if the network address and the subnet mask of this IP range have different sizes (i.e.
329      * one is IPv4 and one is IPv6), this method will always return <code>false</code>, no matter
330      * what machine has been specified!
331      * <p>
332      * Further, if the machine address and the IP range (i.e. network address and subnet mask) have
333      * different sizes, the method will return <code>false</code>. (In other words: an IPv4 range
334      * never contains an IPv6 address and the other way round.)
335      * <p>
336      * Note that the above can lead to confusion. For example the local subnet in IPv4 (
337      * <code>127.0.0.0/8</code>) will <b>not </b> contain the localhost in IPv6 (
338      * <code>::1</code>), and the localhost in IPv4 (<code>127.0.0.1</code>) will <b>not </b>
339      * be contained in the local subnet in IPv6 (<code>::1/128</code>).
340      *
341      * @param machine the machine to check for
342      * @return a boolean value
343      *
344      * @see InetAddressUtil#contains
345      */

346     public boolean contains(Machine machine) {
347         /*
348          * FIXME? by zisch@dals.ch: Maybe some mapping between IPv4/v6 should be done here, p.e. for
349          * the localhost (see the javdoc comment above)? (I'm not a TCP/IP-guru, so I'm not sure
350          * about this. ;-)
351          */

352         log.debug("Checking IP range: [" + getId() + "]");
353         return InetAddressUtil.contains(networkAddress, subnetMask, machine.getAddress());
354     }
355
356     /**
357      * Format the specified numeric IP address.
358      * @param addr the raw numeric IP address
359      * @return the formatted address
360      */

361     private static String JavaDoc addr2string(byte[] addr) {
362         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
363         if (addr.length > 4) {
364             /* IPv6-format if more than 4 bytes: */
365             for (int i = 0; i < addr.length; i++) {
366                 if (i > 0 && (i & 1) == 0) {
367                     buf.append(':');
368                 }
369                 String JavaDoc hex = Integer.toHexString(addr[i] & 0xff);
370                 if (hex.length() == 1) {
371                     buf.append('0');
372                 }
373                 buf.append(hex);
374             }
375         } else {
376             /* IPv4-format: */
377             for (int i = 0; i < addr.length; i++) {
378                 if (i > 0) {
379                     buf.append('.');
380                 }
381                 buf.append(addr[i] & 0xff);
382             }
383         }
384         return buf.toString();
385     }
386 }
Popular Tags