Перейти к основному содержимому

Найти, находится ли IP-адрес в указанном диапазоне или нет в Java

· 7 мин. чтения

1. Обзор

В этом руководстве мы обсудим, как определить, находится ли IP-адрес в заданном диапазоне или не использует Java. Для этой проблемы мы будем считать, что все заданные IP-адреса являются действительными адресами IPv4 (Интернет-протокол версии 4) и IPv6 (Интернет-протокол версии 6) на протяжении всей статьи.

2. Введение в проблему

Дан входной IP-адрес вместе с двумя другими IP-адресами в виде диапазона (от и до). Мы должны иметь возможность определить, находится ли входной IP-адрес в заданном диапазоне или нет .

Например:

  • Вход = 192.220.3.0, диапазон от 192.210.0.0 до 192.255.0.0

    Выход = правда

  • Вход = 192.200.0.0, диапазон от 192.210.0.0 до 192.255.0.0

    Выход = ложь

Теперь давайте рассмотрим различные способы проверки того, находится ли заданный IP-адрес в диапазоне или нет, используя различные библиотеки Java.

3. Библиотека IP-адресов

Библиотека IPAddress , написанная Шоном С. Фоули, поддерживает обработку адресов IPv4 и IPv6 для широкого спектра вариантов использования . Важно отметить, что для работы этой библиотеки требуется как минимум Java 8.

Настроить эту библиотеку несложно. Нам нужно добавить зависимость ipaddress к нашему pom.xml:

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

Он предоставляет следующие классы Java, необходимые для решения нашей проблемы:

  • IPAddress, чтобы хранить IP-адрес как экземпляр Java.
  • IPAddressString , чтобы создать экземпляр IPAddress из заданного IP-адреса в виде строки.
  • 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 можно преобразовать в IPAddress с помощью любого из следующих двух методов:

  • адресовать()
  • получитьадрес()

Метод getAddress() предполагает, что данный IP-адрес действителен, но метод toAddress() проверяет ввод один раз и выдает AddressStringException , если он недействителен. Класс IPAddress предоставляет метод toSequentialRange , который создает экземпляр IPAddressSeqRange , используя начальный и конечный диапазоны IP-адресов.

Давайте рассмотрим несколько единичных случаев, которые вызывают checkIPIsInGivenRange с адресами IPv4 и IPv6:

@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. Математика Commons 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

Библиотека предоставляет классы Ipv4 и Ipv4Range для хранения одного IP-адреса и диапазона адресов в качестве экземпляров соответственно. Теперь давайте взглянем на пример кода, в котором используются вышеупомянутые классы:

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 предоставляет статический метод () , который использует строку IP для создания экземпляра IPv4 . Класс Ipv4Range использует шаблон проектирования построителя для создания своего экземпляра с помощью методов from() и to() для указания диапазона. Кроме того, он предоставляет функцию contains the () для проверки наличия 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. Использование класса InetAddress Java для IPv4

Адрес IPv4 представляет собой последовательность из четырех 1-байтовых значений. Следовательно, его можно преобразовать в 32-битное целое число. Мы можем проверить, попадает ли оно в заданный диапазон.

Класс Java InetAddress представляет IP-адрес и предоставляет методы для получения IP-адреса для любых заданных имен хостов. Экземпляр InetAddress представляет IP-адрес с соответствующим именем хоста.

Вот код Java для преобразования адреса IPv4 в длинное целое число:

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);
}

Метод getByName() в классе InetAddress принимает либо доменное имя, либо 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, нам нужно использовать либо два длинных целых числа, либо класс BigInteger для вычислений. Но это был бы утомительный процесс, а также связанный со сложными расчетами.

6. Библиотека Java IPv6

Библиотека Java IPv6 написана специально для поддержки IPv6 в Java и для выполнения связанных с ним операций. Эта библиотека внутренне использует два длинных целых числа для хранения адреса IPv6. И для работы требуется как минимум Java 6.

Нам нужно добавить зависимость java-ipv6 в наш pom.xml:

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

Библиотека предоставляет различные классы для работы с адресами IPv6. Вот два из них, которые помогают нам решить нашу проблему:

  • 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 дает нам различные статические функции для создания его экземпляра:

  • из строки
  • fromInetAddress
  • отBigInteger
  • fromByteArray
  • от Лонгс

Все вышеперечисленные методы говорят сами за себя, что помогает нам создать экземпляр IPv6Address . IPv6AddressRange имеет метод fromFirstAndLast() , который принимает два IP-адреса в качестве входных данных. Кроме того, он предоставляет метод 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 .