1. 概要

このチュートリアルでは、IPアドレスが特定の範囲内にあるかどうかをJavaを使用して見つける方法について説明します。 この問題については、この記事全体で、指定されたすべての IPアドレスが有効なIPv4(インターネットプロトコルバージョン4)およびIPv6(インターネットプロトコルバージョン6)アドレスであると見なします。

2. 問題の紹介

入力IPアドレスと他の2つのIPアドレスを範囲(fromとto)として指定します。 入力IPアドレスが指定された範囲内にあるかどうかを判別できるはずです。

例えば:

  • 入力=192.220.3.0、範囲192.210.0.0〜192.255.0.0出力= true
  • 入力=192.200.0.0、範囲192.210.0.0〜192.255.0.0出力= false

次に、さまざまなJavaライブラリを使用して、指定されたIPアドレスが範囲内にあるかどうかを確認するさまざまな方法を見てみましょう。

3. IPアドレスライブラリ

SeanCFoleyによって作成されたIPAddressライブラリは、さまざまなユースケースのIPv4アドレスとIPv6アドレスの両方の処理をサポートします。 このライブラリが機能するには、少なくともJava8が必要であることに注意してください。

このライブラリの設定は簡単です。 ipaddress依存関係をpom.xmlに追加する必要があります。

<dependency>
    <groupId>com.github.seancfoley</groupId>
    <artifactId>ipaddress</artifactId>
    <version>5.3.3</version>
</dependency>

問題を解決するために必要な次のJavaクラスを提供します。

  • IPAddress、は、IPアドレスをJavaインスタンスとして保持します
  • IPAddressString 、指定されたIPから文字列としてIPAddressインスタンスを構築します
  • IPAddressSeqRange、任意の範囲のIPアドレスを表します

次に、上記のクラスを使用して、IPアドレスが指定された範囲内にあるかどうかを確認するためのコードを見てみましょう。

public static boolean checkIPIsInGivenRange (String inputIP, String rangeStartIP, String rangeEndIP) 
  throws AddressStringException {
    IPAddress startIPAddress = new IPAddressString(rangeStartIP).getAddress();
    IPAddress endIPAddress = new IPAddressString(rangeEndIP).getAddress();
    IPAddressSeqRange ipRange = startIPAddress.toSequentialRange(endIPAddress);
    IPAddress inputIPAddress = new IPAddressString(inputIP).toAddress();

    return ipRange.contains(inputIPAddress);
}

上記のコードは、IPv4アドレスとIPv6アドレスの両方で機能します。 IPAddressString パラメーター化されたコンストラクターは、IPを文字列として受け取り、IPAddressインスタンスを構築します。 IPAddressString インスタンスは、次の2つの方法のいずれかを使用して、IPAddressに変換できます。

  • toAddress()
  • getAddress()

getAddress()メソッドは、指定されたIPが有効であると想定しますが、 toAddress()メソッドは、入力を1回検証し、無効な場合はAddressStringExceptionをスローします。 IPAddress クラスは、開始IP範囲と終了IP範囲を使用してIPAddressSeqRangeインスタンスを構築するtoSequentialRangeメソッドを提供します。

IPv4およびIPv6アドレスでcheckIPIsInGivenRangeを呼び出すいくつかのユニットケースを考えてみましょう。

@Test
void givenIPv4Addresses_whenIsInRange_thenReturnsTrue() throws Exception {
    assertTrue(IPWithGivenRangeCheck.checkIPIsInGivenRange("192.220.3.0", "192.210.0.0", "192.255.0.0"));
}

@Test
void givenIPv4Addresses_whenIsNotInRange_thenReturnsFalse() throws Exception {
    assertFalse(IPWithGivenRangeCheck.checkIPIsInGivenRange("192.200.0.0", "192.210.0.0", "192.255.0.0"));
}

@Test
void givenIPv6Addresses_whenIsInRange_thenReturnsTrue() throws Exception {
    assertTrue(IPWithGivenRangeCheck.checkIPIsInGivenRange(
      "2001:db8:85a3::8a03:a:b", "2001:db8:85a3::8a00:ff:ffff", "2001:db8:85a3::8a2e:370:7334"));
}

@Test
void givenIPv6Addresses_whenIsNotInRange_thenReturnsFalse() throws Exception {
    assertFalse(IPWithGivenRangeCheck.checkIPIsInGivenRange(
      "2002:db8:85a3::8a03:a:b", "2001:db8:85a3::8a00:ff:ffff", "2001:db8:85a3::8a2e:370:7334"));
}

4. コモンズIP数学

Commons IP Mathライブラリは、IPv4およびIPv6アドレスと範囲を表すためのクラスを提供します。最も一般的な操作を処理するためのAPIを提供し、さらに、IP範囲を操作するためのコンパレータやその他のユーティリティを提供します。

commons-ip-math依存関係をpom.xmlに追加する必要があります。

<dependency>
    <groupId>com.github.jgonian</groupId>
    <artifactId>commons-ip-math</artifactId>
    <version>1.32</version>
</dependency>

4.1. IPv4の場合

ライブラリは、単一のIPアドレスとアドレスの範囲をインスタンスとしてそれぞれ保持するためのIpv4クラスとIpv4Rangeクラスを提供します。 ここで、前述のクラスを使用するコードサンプルを見てみましょう。

public static boolean checkIPv4IsInRange (String inputIP, String rangeStartIP, String rangeEndIP) {
    Ipv4 startIPAddress = Ipv4.of(rangeStartIP);
    Ipv4 endIPAddress = Ipv4.of(rangeEndIP);
    Ipv4Range ipRange = Ipv4Range.from(startIPAddress).to(endIPAddress);
    Ipv4 inputIPAddress = Ipv4.of(inputIP);
    return ipRange.contains(inputIPAddress);
}

Ipv4 クラスは、 Ipv4インスタンスを構築するためにIP文字列を受け取る静的メソッドof()を提供します。 Ipv4Range クラスは、ビルダーデザインパターンを使用して、 from()および to()メソッドを使用して範囲を指定することにより、インスタンスを作成します。 さらに、には()機能が含まれており、指定された範囲にIPアドレスが存在するかどうかを確認できます。

次に、関数に対していくつかのテストを実行しましょう。

@Test
void givenIPv4Addresses_whenIsInRange_thenReturnsTrue() throws Exception {
    assertTrue(IPWithGivenRangeCheck.checkIPv4IsInRange("192.220.3.0", "192.210.0.0", "192.255.0.0"));
}

@Test
void givenIPv4Addresses_whenIsNotInRange_thenReturnsFalse() throws Exception {
    assertFalse(IPWithGivenRangeCheck.checkIPv4IsInRange("192.200.0.0", "192.210.0.0", "192.255.0.0"));
}

4.2. IPv6の場合

IPバージョン6の場合、ライブラリは同じクラスと関数を提供しますが、バージョン番号が4→6に変更されています。 バージョン6のクラスは次のとおりです。 Ipv6 Ipv6Range。 

前述のクラスを利用して、IPバージョン6のコード例を見てみましょう。

public static boolean checkIPv6IsInRange (String inputIP, String rangeStartIP, String rangeEndIP) {
    Ipv6 startIPAddress = Ipv6.of(rangeStartIP);
    Ipv6 endIPAddress = Ipv6.of(rangeEndIP);
    Ipv6Range ipRange = Ipv6Range.from(startIPAddress).to(endIPAddress);
    Ipv6 inputIPAddress = Ipv6.of(inputIP);
    return ipRange.contains(inputIPAddress);
}

次に、単体テストを実行してコードを確認しましょう。

@Test
void givenIPv6Addresses_whenIsInRange_thenReturnsTrue() throws Exception {
    assertTrue(IPWithGivenRangeCheck.checkIPv6IsInRange(
      "2001:db8:85a3::8a03:a:b", "2001:db8:85a3::8a00:ff:ffff", "2001:db8:85a3::8a2e:370:7334"));
}

@Test
void givenIPv6Addresses_whenIsNotInRange_thenReturnsFalse() throws Exception {
    assertFalse(IPWithGivenRangeCheck.checkIPv6IsInRange(
      "2002:db8:85a3::8a03:a:b", "2001:db8:85a3::8a00:ff:ffff", "2001:db8:85a3::8a2e:370:7334"));
}

5. JavaのInetAddressクラスをIPv4に使用する

IPv4アドレスは、4つの1バイト値のシーケンスです。 したがって、32ビット整数に変換できます。 所定の範囲内にあるか確認できます。

JavaのInetAddressクラスはIPアドレスを表し、任意のホスト名のIPを取得するためのメソッドを提供します。 InetAddressのインスタンスは、IPアドレスとそれに対応するホスト名を表します。

IPv4アドレスを長整数に変換するJavaコードは次のとおりです。

long ipToLongInt (InetAddress ipAddress) {
    long resultIP = 0;
    byte[] ipAddressOctets = ipAddress.getAddress();

    for (byte octet : ipAddressOctets) {
        resultIP <<= 8;
        resultIP |= octet & 0xFF;
    }
    return resultIP;
}

上記の方法を使用して、IPが次の範囲内にあるかどうかを確認しましょう。

public static boolean checkIPv4IsInRangeByConvertingToInt (String inputIP, String rangeStartIP, String rangeEndIP) 
  throws UnknownHostException {
    long startIPAddress = ipToLongInt(InetAddress.getByName(rangeStartIP));
    long endIPAddress = ipToLongInt(InetAddress.getByName(rangeEndIP));
    long inputIPAddress = ipToLongInt(InetAddress.getByName(inputIP));

    return (inputIPAddress >= startIPAddress && inputIPAddress <= endIPAddress);
}

InetAddressクラスのgetByName()メソッドは、ドメイン名またはIPアドレスのいずれかを入力として受け取り、無効な場合はUnknownHostExceptionをスローします。 単体テストを実行してコードを確認しましょう。

@Test
void givenIPv4Addresses_whenIsInRange_thenReturnsTrue() throws Exception {
    assertTrue(IPWithGivenRangeCheck.checkIPv4IsInRangeByConvertingToInt("192.220.3.0", "192.210.0.0", "192.255.0.0"));
}

@Test
void givenIPv4Addresses_whenIsNotInRange_thenReturnsFalse() throws Exception {
    assertFalse(IPWithGivenRangeCheck.checkIPv4IsInRangeByConvertingToInt("192.200.0.0", "192.210.0.0", "192.255.0.0"));
}

IPアドレスを整数に変換する上記のロジックはIPv6にも適用されますが、これは128ビット整数です。 Java言語は、プリミティブデータ型で最大64ビット(長整数)をサポートします。 上記のロジックをバージョン6に適用する必要がある場合は、計算に2つの長整数またはBigIntegerクラスのいずれかを使用する必要があります。 しかし、それは退屈なプロセスであり、複雑な計算も伴います。

6. JavaIPv6ライブラリ

Java IPv6ライブラリは、JavaでのIPv6サポートと、それに関連する操作を実行するために特別に作成されています。 このライブラリは、内部で2つの長整数を使用してIPv6アドレスを格納します。 また、動作するには少なくともJava6が必要です。

java-ipv6依存関係をpom.xmlに追加する必要があります。

<dependency>
    <groupId>com.googlecode.java-ipv6</groupId>
    <artifactId>java-ipv6</artifactId>
    <version>0.17</version>
</dependency>

ライブラリは、IPv6アドレスで動作するさまざまなクラスを提供します。 これが私たちの問題を解決するのに役立つ2つです:

  • IPv6Address 、IPv6をJavaインスタンスとして表現するため
  • IPv6AddressRange 、連続するIPv6アドレスの連続範囲を表すため

上記のクラスを使用してIPが指定された範囲内にあることを確認するコードスニペットを見てみましょう。

public static boolean checkIPv6IsInRangeByIPv6library (String inputIP, String rangeStartIP, String rangeEndIP) {
    IPv6Address startIPAddress = IPv6Address.fromString(rangeStartIP);
    IPv6Address endIPAddress = IPv6Address.fromString(rangeEndIP);
    IPv6AddressRange ipRange = IPv6AddressRange.fromFirstAndLast(startIPAddress, endIPAddress);
    IPv6Address inputIPAddress = IPv6Address.fromString(inputIP);
    return ipRange.contains(inputIPAddress);
}

IPv6Address クラスは、インスタンスを構築するためのさまざまな静的関数を提供します。

  • fromString
  • fromInetAddress
  • fromBigInteger
  • fromByteArray
  • fromLongs

上記のすべてのメソッドは自明であり、IPv6Addressインスタンスを作成するのに役立ちます。 IPv6AddressRange には、2つのIPアドレスを入力として受け取る fromFirstAndLast()という名前のメソッドがあります。 さらに、 contains()メソッドを提供します。このメソッドは、 IPv6Address をパラメーターとして受け取り、指定された範囲内に存在するかどうかを判別します。

定義した上記のメソッドを呼び出して、テストでいくつかのサンプル入力を渡します。

@Test
void givenIPv6Addresses_whenIsInRange_thenReturnsTrue() throws Exception {
    assertTrue(IPWithGivenRangeCheck.checkIPv6IsInRangeByIPv6library(
      "fe80::226:2dff:fefa:dcba",
      "fe80::226:2dff:fefa:cd1f",
      "fe80::226:2dff:fefa:ffff"
    ));
}

@Test
void givenIPv6Addresses_whenIsNotInRange_thenReturnsFalse() throws Exception {
    assertFalse(IPWithGivenRangeCheck.checkIPv6IsInRangeByIPv6library(
      "2002:db8:85a3::8a03:a:b",
      "2001:db8:85a3::8a00:ff:ffff",
      "2001:db8:85a3::8a2e:370:7334"
    ));
}

7. 結論

この記事では、指定されたIPアドレス(v4とv6の両方)が指定された範囲内にあるかどうかを判断する方法を検討しました。 さまざまなライブラリの助けを借りて、複雑なロジックや計算を行わずにIPアドレスの存在を確認することを分析しました。

いつものように、この記事のコードスニペットはGitHubにあります。