ProfiPHPProfiPHP
Категория: Полезное в PHP

Работа с IPv6 в PHP

Очень часто для большинства проектов возникает необходимость работы с IP адресами. В те времена, когда существовал только IP версии 4, для упрощения работы использовались две функции Ip2long и Long2ip, которые переводили IP адрес в обычное число и обратно. Данная реализация позволяла экономично использовать память и ресурсы системы. Но в IP версии 6 нет такой возможности.

Для начала 32 бита, из которых состоит IPv4, вполне было достаточно. 32 битные программы поддерживают работу с числами от 0 до 4294967295 (или 2 в 32 степени), что является максимальным количеством IP адресов в сети. Самое большое беззнаковое число допустимое на 64bit платформе - 2 в 64 степени. Но IPv6 пошел еще далее, допуская количество адресов 2 в 128 степени, что иногда приводит к проблемам работы с памятью и неверным результатам.

Для правильной и безопасной проверки валидации IPv4 и Ipv6 в PHP существует функция Filter_var, которая осуществляет проверку входящих данных. Для валидации IP адреса, следует использовать фильтр FILTER_VALIDATE_IP. А для определения версии - флаги FILTER_FLAG_IPV4 или FILTER_FLAG_IPV6.

Для преобразования Ipv6 существуют функции Inet_pton и Inet_ntop, которые преобразуют человеко-понятное представление IP адреса в упакованное In_addr представление, и обратно. Поскольку результат выполнения функции Inet_pton не является чисто бинарным, необходимо воспользоваться функцией Unpack чтобы в дальнейшем можно было работать с битовыми операциями.

Для разных версий IP адресов необходимо использовать разные методы распаковывания результата. Для Ipv6 будем использовать формат A16, а для IPv4 - A4. Формат A4 означает строку (string) со SPACE-заполнением и 4-мя аргументами повторения. Аналогично с A16.

Давайте рассмотрим это на примере:
$ip4 = "44.67.85.126";
$ip6 = "FE80:0000:0000:0000:0202:B3FF:FE1E:8329";
// Пример с Ip2long
var_dump( ip2long( $ip4 ) ); // int(742610302)
var_dump( ip2long( $ip6 ) ); // bool(false)

// Пример с Inet_pton
var_dump( inet_pton( $ip4 ) ); // string(4) ",CU~"
var_dump( inet_pton( $ip6 ) ); // string(16) "���������)"
Теперь необходимо преобразовать упакованный результат в распакованное значение:
$u4 = current( unpack( "A4", inet_pton( $ip4 ) ) );
var_dump( inet_ntop( pack( "A4", $u4 ) ) ); // string(12) "44.67.85.126"

$u6 = current( unpack( "A16", inet_pton( $ip6 ) ) );
var_dump( inet_ntop( pack( "A16", $u6 ) ) ); // string(24) "fe80::202:b3ff:fe1e:8329"
Функция Current возвращает первый элемент массива.

В результате данные после распаковывания и упаковывания сохраняются одинаковыми.

Опишем функции, которые выполняют выше описанные операции. Функция для преобразования IP адреса в бинарный вид:
function iptobin( $ip ){
if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) ) {
return current( unpack( "A4", inet_pton( $ip ) ) );
}
elseif ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) ) {
return current( unpack( "A16", inet_pton( $ip ) ) );
}
else {
return false;
}
}
Функция для обратного преобразования:
function bintoip( $str ) {
if ( strlen( $str ) == 16 || strlen( $str ) == 4 ) {
return inet_ntop( pack( "A".strlen( $str ), $str ) );
}
else {
return false;
}
}
Функция Iptobin проверяет входящее значение и возвращает преобразованное значение или False в случае возникновения ошибки. Функция Bintoip преобразует результат выполнения Iptobin обратно, проверяя входящее значение на соответствие форматам A4/A16 и возвращая результат, или False в случае возникновения ошибки.

Для сохранения бинарных файлов в базе данных можно использовать человекочетаемый вид в формате VARCHAR(39). Но рекомендуется все же сохранять в бинарном виде используя BINARY(16), что позволит делать выборки по диапазонам, битовым маскам и предоставлять другие преимущества, для построения приложения поддерживающего Ipv6.

Добавить комментарий

Имя:
Текст комментария: