Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Data type for storing IP addresses

Tags:

java

ip

Is there a specific data type for storing IP addresses in Java? I have one specific functionality requirement:

  1. Given an IP range and an IP, return true if it falls within it, false otherwise. For example: range 10.10.10.1-10.10.11.255 and IP 10.10.10.192 should return true.

I know of java.net.inetaddress but I believe it doesn't give me this functionality. Any ideas?

like image 769
n3o Avatar asked Dec 30 '11 08:12

n3o


2 Answers

I would use java.net.InetAddress or one of its subclasses and write a custom comparator plus a range class:

  • Maintaining and debugging are easier with the explicit type InetAddress instead of longs only: Your debugger will actually show "10.10.10.1" and not "168430081"
  • IPv6 is either a non-issue or can be implemented without much additional hassle.

One downside of InetAddress is that getByName results in a DNS access. You might want to look at Guava's com.google.common.net.InetAddresses helper class if you want to avoid the penalty for DNS.

public enum InetAddressComparator implements Comparator<InetAddress> {

  INSTANCE;

  public int compare(InetAddress first, InetAddress second) {
    byte[] firstBytes = first.getAddress();
    byte[] secondBytes = second.getAddress();
    if (firstBytes.length != secondBytes.length) {
      throw new IllegalArgumentException("Cannot compare IPv4 and IPv6 addresses");
    }
    // getAddress returns bytes in network byte order:
    // the least significant byte is at the last index
    for (int i = firstBytes.length - 1; i >= 0; i--) {
      // translate the byte to an int with only the last 8 bits set,
      // effectively treating it as unsigned
      int a = firstBytes[i] & 0xff;
      int b = secondBytes[i] & 0xff;
      if (a < b) {
        return -1;
      } else if (a > b) {
        return 1;
      }
    }
    return 0;
  }

}

public class Range<T> {

  private T lower;
  private T upper;
  private Comparator<T> comparator;

  public Range(T lower, T upper, Comparator<T> comparator) {
    if (comparator.compare(lower, upper) <= 0) {
      this.lower = lower;
      this.upper = upper;
    } else {
      this.lower = upper;
      this.upper = lower;
    }
    this.comparator = comparator;
  }

  public boolean contains(T element) {
    return comparator.compare(lower, element) <= 0 &&
      comparator.compare(upper, element) >= 0;
  }

}

public class Main {
  public static void main(String[] args) throws Exception {
    InetAddress start = InetAddress.getByName("10.10.10.1");
    InetAddress end = InetAddress.getByName("10.10.11.255");
    InetAddress test = InetAddress.getByName("10.10.10.192");
    assert InetAddressComparator.INSTANCE.compare(start, test) == -1;
    assert InetAddressComparator.INSTANCE.compare(end, test) == 1;
    assert InetAddressComparator.INSTANCE.compare(test, test) == 0;
    assert new Range<InetAddress>(start, end, InetAddressComparator.INSTANCE)
      .contains(test);
  }
}
like image 114
nd. Avatar answered Oct 28 '22 07:10

nd.


An IP (IPv4) is 32 bits (the same size as an int in Java). Since you want to do comparisons using unsigned ints (if you need to support IP's above 128.0.0.0) you need to use longs instead.

10.10.10.1 is: (10 << 24) + (10 << 16) + (10 << 8) + 1 = 168430081
10.10.11.255 is: (10 << 24) + (10 << 16) + (11 << 8) + 255 = 168430591

10.10.10.192 is: (10 << 24) + (10 << 16) + (10 << 8) + 192 = 168430272

Since 168430081 <= 168430272 && 168430272 <= 168430591, (In other words 168430272 is between 168430081 and 168430272) your IP is in the range.

like image 37
Paul Avatar answered Oct 28 '22 09:10

Paul