001
002 /*
003 * Copyright (C) 2011 Archie L. Cobbs. All rights reserved.
004 *
005 * $Id: IPv4Util.java 2 2011-02-05 21:51:43Z archie.cobbs $
006 */
007
008 package org.dellroad.stuff.net;
009
010 import java.net.Inet4Address;
011 import java.net.InetAddress;
012 import java.net.UnknownHostException;
013 import java.util.regex.Matcher;
014 import java.util.regex.Pattern;
015
016 /**
017 * Utility methods for working with IPv4 addresses and netmasks.
018 */
019 public final class IPv4Util {
020
021 public static final int MIN_VALID_NETMASK_WIDTH = 8;
022 public static final int MAX_VALID_NETMASK_WIDTH = 30;
023
024 private IPv4Util() {
025 }
026
027 /**
028 * Convert from 32-bit integer representation.
029 */
030 public static Inet4Address toAddress(int address) {
031 return IPv4Util.toAddress(new byte[] {
032 (byte)((address >> 24) & 0xff),
033 (byte)((address >> 16) & 0xff),
034 (byte)((address >> 8) & 0xff),
035 (byte)(address & 0xff),
036 });
037 }
038
039 /**
040 * Convert to 32-bit integer representation.
041 */
042 public static int toInt32(Inet4Address address) {
043 byte[] bytes = address.getAddress();
044 return ((bytes[0] & 0xff) << 24)
045 | ((bytes[1] & 0xff) << 16)
046 | ((bytes[2] & 0xff) << 8)
047 | (bytes[3] & 0xff);
048 }
049
050 /**
051 * Convert to an unsigned 32-bit integer representation within a 64-bit long.
052 */
053 public static long toUInt32(Inet4Address address) {
054 return toInt32(address) & 0x00000000ffffffffL;
055 }
056
057 /**
058 * Convert raw bytes to an {@link Inet4Address}.
059 *
060 * @throws IllegalArgumentException if array does not have length four
061 */
062 public static Inet4Address toAddress(byte[] bytes) {
063 if (bytes.length != 4)
064 throw new IllegalArgumentException("array has length " + bytes.length + " != 4");
065 try {
066 return (Inet4Address)InetAddress.getByAddress(toString(bytes), bytes);
067 } catch (UnknownHostException e) {
068 throw new RuntimeException("unexpected exception", e);
069 }
070 }
071
072 /**
073 * Convert an IPv4 address to string representation.
074 *
075 * @throws IllegalArgumentException if array does not have length four
076 */
077 public static String toString(Inet4Address address) {
078 return IPv4Util.toString(address.getAddress());
079 }
080
081 /**
082 * Convert a network address (IPv4 base address and netmask) to string representation.
083 *
084 * @throws IllegalArgumentException if array does not have length four
085 */
086 public static String toString(Inet4Address address, Inet4Address netmask) {
087 return IPv4Util.toString(address) + "/" + IPv4Util.getWidth(netmask);
088 }
089
090 private static String toString(byte[] bytes) {
091 assert bytes.length == 4;
092 return (bytes[0] & 0xff) + "." + (bytes[1] & 0xff) + "." + (bytes[2] & 0xff) + "." + (bytes[3] & 0xff);
093 }
094
095 /**
096 * Convert from string representation.
097 *
098 * @throws IllegalArgumentException if string is not an IP address
099 */
100 public static Inet4Address fromString(String string) {
101 String bytepat = "([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])";
102 Pattern pattern = Pattern.compile(bytepat + "\\." + bytepat + "\\." + bytepat + "\\." + bytepat);
103 Matcher matcher = pattern.matcher(string);
104 if (!matcher.matches())
105 throw new IllegalArgumentException("invalid IP address: " + string);
106 return toAddress(new byte[] {
107 (byte)Integer.parseInt(matcher.group(1)),
108 (byte)Integer.parseInt(matcher.group(2)),
109 (byte)Integer.parseInt(matcher.group(3)),
110 (byte)Integer.parseInt(matcher.group(4))
111 });
112 }
113
114 /**
115 * Get netmask width.
116 *
117 * @return a value between zero and 32 (inclusive)
118 * @throws IllegalArgumentException if the value is not a valid CIDR netmask
119 */
120 public static int getWidth(Inet4Address netmask) {
121 int mask = toInt32(netmask);
122 if (mask == 0)
123 return 0;
124 for (int shift = 0; shift < 32; shift++) {
125 if (mask == (~0 << shift))
126 return 32 - shift;
127 }
128 throw new IllegalArgumentException("invalid netmask " + netmask);
129 }
130
131 /**
132 * Determine if the given address is a valid CIDR netmask.
133 * The netmask must have a width between 8 and 30 (inclusive).
134 */
135 public static boolean isValidNetmask(Inet4Address netmask) {
136 try {
137 int width = getWidth(netmask);
138 return width >= MIN_VALID_NETMASK_WIDTH && width <= MAX_VALID_NETMASK_WIDTH;
139 } catch (IllegalArgumentException e) {
140 return false;
141 }
142 }
143
144 /**
145 * Get netmask with the given width.
146 *
147 * @throws IllegalArgumentException if width is less than zero or greater than 32
148 */
149 public static Inet4Address getNetmaskForWidth(int width) {
150 if (width < 0 || width > 32)
151 throw new IllegalArgumentException("invalid netmask width " + width);
152 return toAddress(~0 << (32 - width));
153 }
154
155 /**
156 * Get the base address (all host bits zero) of the given IP network.
157 *
158 * @param address any IP address on the network
159 * @param netmask the netmask of the network
160 * @throws IllegalArgumentException if {@code netmask} is not a valid netmask
161 */
162 public static Inet4Address getBaseAddress(Inet4Address address, Inet4Address netmask) {
163 int addr = toInt32(address);
164 int width = getWidth(netmask);
165 addr &= ~0 << (32 - width);
166 return toAddress(addr);
167 }
168
169 /**
170 * Get the broadcast address (all host bits one) of the given IP network.
171 *
172 * @param address any IP address on the network
173 * @param netmask the netmask of the network
174 * @throws IllegalArgumentException if {@code netmask} is not a valid netmask
175 */
176 public static Inet4Address getBroadcastAddress(Inet4Address address, Inet4Address netmask) {
177 int addr = toInt32(address);
178 int width = getWidth(netmask);
179 addr |= ~(~0 << (32 - width));
180 return toAddress(addr);
181 }
182
183 /**
184 * Determine if the given address lives on the given network.
185 *
186 * @param address IP address in question
187 * @param network network address
188 * @param netmask network netmask
189 * @throws IllegalArgumentException if {@code netmask} is not a valid netmask
190 */
191 public static boolean isOnNetwork(Inet4Address address, Inet4Address network, Inet4Address netmask) {
192 int addr1 = toInt32(address);
193 int addr2 = toInt32(network);
194 int width = getWidth(netmask);
195 int mask = ~0 << (32 - width);
196 return (addr1 & mask) == (addr2 & mask);
197 }
198
199 /**
200 * Determine if the given address is a valid host address on the given network.
201 * The address must lie on the given network and not equal either the base or broadcast addresses.
202 *
203 * @param address IP address in question
204 * @param network network address
205 * @param netmask network netmask
206 * @throws IllegalArgumentException if {@code netmask} is not a valid netmask
207 */
208 public static boolean isValidHostOnNetwork(Inet4Address address, Inet4Address network, Inet4Address netmask) {
209 return isOnNetwork(address, network, netmask)
210 && !address.equals(getBaseAddress(network, netmask))
211 && !address.equals(getBroadcastAddress(network, netmask));
212 }
213 }
214