Мне нравится проверять, находится ли IP-адрес в частной сети. Это не работает.
Мой код:
<?php $ip = $_SERVER['REMOTE_ADDR']; function _isPrivate($ip) { $i = explode('.', $ip); if ($i[0] == 10) { return true; } else if ($i[0] == 172 && $i[1] > 15 && $i[1] < 32) { return true; } else if ($i[0] == 192 && $i[1] == 168) { return true; } return false; } ?>
Другой:
<?php $ip = $_SERVER['REMOTE_ADDR']; function _isPrivate($ip) { $ip = ip2long($ip); $net_a = ip2long('10.255.255.255') >> 24; $net_b = ip2long('172.31.255.255') >> 20; $net_c = ip2long('192.168.255.255') >> 16; return $ip >> 24 === $net_a || $ip >> 20 === $net_b || $ip >> 16 === $net_c; } ?>
Любая помощь будет очень признательна, спасибо!
function ip_is_private ($ip) { $pri_addrs = array ( '10.0.0.0|10.255.255.255', // single class A network '172.16.0.0|172.31.255.255', // 16 contiguous class B network '192.168.0.0|192.168.255.255', // 256 contiguous class C network '169.254.0.0|169.254.255.255', // Link-local address also refered to as Automatic Private IP Addressing '127.0.0.0|127.255.255.255' // localhost ); $long_ip = ip2long ($ip); if ($long_ip != -1) { foreach ($pri_addrs AS $pri_addr) { list ($start, $end) = explode('|', $pri_addr); // IF IS PRIVATE if ($long_ip >= ip2long ($start) && $long_ip <= ip2long ($end)) { return true; } } } return false; }
См. http://mebsd.com/coding-snipits/check-private-ip-function-php.html.
Вы также можете узнать о частных адресных пространствах здесь
Я думаю, что это должно решить проблему.
filter_var, используемый со следующими правилами проверки , вернет false, если IP-адрес является закрытым.
$user_ip = '127.0.0.1'; filter_var( $user_ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE )
Проверьте ссылки выше для документации php.
… мои 5 центов:
ИМХО, основной вопрос – это просто «как проверить, принадлежит ли IP-адрес сети?».
Ответ простой двоичный: IP_address И network_mask EQUALS network_address.
Например, имеет ли IP-адрес 10.1.2.3 сеть 10.0.0.0 с netmask 255.0.0.0? 10.1.2.3 & 255.0.0.0 – 10.0.0.0, поэтому ответ: да, это так.
Легче видеть его в двоичном формате:
00001010 00000001 00000010 00000011 ( 10.1.2.3) ip address & 11111111 00000000 00000000 00000000 (255.0.0.0) network mask = 00001010 00000000 00000000 00000000 ( 10.0.0.0) network address
Просто нужно проверить, что для нужных вам сетей (включая или нет loopback, link-local и т. Д.):
function _isPrivate($long_ip) { return ( ($long_ip & 0xFF000000) === 0x0A000000 ) || //Private A network: 00001010 .... ( ($long_ip & 0xFFF00000) === 0xAC100000 ) || //Private B network: 10101100 0001.... ( ($long_ip & 0xFFFF0000) === 0xC0A80000 ) || //Private C network: 11000000 10101000 .... //Link-local and loopback are NOT private range, so the function in the question yield right results to "is in private range?". Seems it was not the desired behaviour... Those cases can also be checked: ( ($long_ip & 0xFFFF0000) === 0xA9FE0000 ) || //Link-local : 10101001 11111110 .... ( ($long_ip & 0xFFFF0000) === 0x7F000000 ) || //Loopback : 01111111 .... //...and add all the fancy networks that you want... ( ($long_ip & 0xFFFFFF00) === 0xC0AF3000 ) || //Direct Delegation AS112 Service 192.175.48.0/24... ( ($long_ip & 0xF0000000) === 0xF0000000 ); //Reserved 240.0.0.0/4 }
Интересным моментом является отрицание возвращаемого значения. Возвращаемое значение на самом деле не означает, что данный IP-адрес находится в частной сети, но это отрицание действительно означает, что данный IP-адрес является «общедоступным IP-адресом» (общим / обычным IP-адресом), поскольку решение user4880112 дает четкое представление.
IPv6
То же самое работает для IPv6. Адресами «частной сети» (формально «Unique-Local», RFC 4193) являются «fc00 :: / 7». Итак, ip_address & 0xFE00 .. === 0xFC00 .. является «частной сетью»,
Принятие упомянутого ответа и включение в него обновленной информации от IANA …
http://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml http://www.iana.org/assignments/iana-ipv4-special-registry/iana -ipv4-специального registry.xhtml
… мы можем сделать немного более общую функцию следующим образом:
function isPublicAddress($ip) { // returns false on failure. // negative if it's a private or special address (-4:IPv4, -16:IPv6) // positive if it's a common IP public address (4:IPv4, 16:IPv6) $networks = array( '4' => array('0.0.0.0/8', '10.0.0.0/8', '100.64.0.0/10', '127.0.0.0/8', '169.254.0.0/16', '172.16.0.0/12', '192.0.0.0/24', '192.0.0.0/29', '192.0.0.8/32', '192.0.0.9/32', '192.0.0.170/32', '192.0.0.170/32', '192.0.2.0/24', '192.31.196.0/24', '192.52.193.0/24', '192.88.99.0/24', '192.168.0.0/16', '192.175.48.0/24', '198.18.0.0/15', '198.51.100.0/24', '203.0.113.0/24', '240.0.0.0/4', '255.255.255.255/32') , '16' => array('::1/128', '::/128', '::ffff:0:0/96', '64:ff9b::/96', '100::/64', '2001::/23', '2001::/32', '2001:1::1/128', '2001:2::/48', '2001:3::/32', '2001:4:112::/48', '2001:5::/32', '2001:10::/28', '2001:20::/28', '2001:db8::/32', '2002::/16', '2620:4f:8000::/48', 'fc00::/7', 'fe80::/10') ); $ip = inet_pton($ip); if( $ip === false ) return false; $space='16'; if (strlen($ip) === 4) { $space='4'; } //Is the IP in a private or special range? foreach($networks[$space] as $network) { //split $network in address and mask $parts=explode('/',$network); $network_address = inet_pton($parts[0]); $network_mask = inet_pton( _mask( $ip , $parts[1] ) ); if (($ip & $network_mask) === $network_address){ return -1*$space; } } //Success! return $space; } function _mask($ip,$nbits){ $mask=''; $nibble=array('0','8','C','E'); $f_s= $nbits >> 2 ; if( $f_s > 0 ) $mask.=str_repeat('F',$f_s); if( $nbits % 4 ) $mask.= $nibble[$nbits % 4]; if( strlen($ip) === 4 ){ if( strlen($mask) < 8 ) $mask.=str_repeat('0', 8 - strlen($mask) ); long2ip('0x'.$mask); $mask=long2ip('0x'.$mask); }else{ if( strlen($mask) < 32 ) $mask.=str_repeat('0', 32 - strlen($mask) ); $mask=rtrim(chunk_split($mask,4,':'),':'); } return $mask; }
Теперь мне интересно: адрес IPv6 в «IPv4-сопоставленном адресе» является «специальным» адресом в IPv6, даже если он был «нормальным» IP-адресом в IPv4. Должны ли мы рассматривать «личное использование» подсетей в :: ffff: 0: 0/96, которые соответствуют частным сетям IPv4?
РЕДАКТИРОВАТЬ, чтобы объяснить последний комментарий:
Сеть IPv6 :: ffff: 0: 0/96 отображает адрес IPv6 на каждый адрес IPv4. Эти IPv6-адреса находятся в одном наборе в реестре IANA («Special-Purpose»), но сопоставленный IPv4-адрес находится во всех типах наборов в IPv4 (приватная сеть, loopback, broadcast, public …). «Общий IPv4 адрес "всегда является« специальным адресом IPv6 ». Если мы настроим сеть с использованием адреса IPv6 в диапазоне :: ffff: 0: 0/96, которые соответствуют частным сетям IPv4 … Используем ли мы частный сетевой адрес?
Использование inet_pton вместо ip2long и включение некоторых из более неясных частных диапазонов:
function isPublicAddress($ip) { //Private ranges... //http://www.iana.org/assignments/iana-ipv4-special-registry/ $networks = array('10.0.0.0' => '255.0.0.0', //LAN. '172.16.0.0' => '255.240.0.0', //LAN. '192.168.0.0' => '255.255.0.0', //LAN. '127.0.0.0' => '255.0.0.0', //Loopback. '169.254.0.0' => '255.255.0.0', //Link-local. '100.64.0.0' => '255.192.0.0', //Carrier. '192.0.2.0' => '255.255.255.0', //Testing. '198.18.0.0' => '255.254.0.0', //Testing. '198.51.100.0' => '255.255.255.0', //Testing. '203.0.113.0' => '255.255.255.0', //Testing. '192.0.0.0' => '255.255.255.0', //Reserved. '224.0.0.0' => '224.0.0.0', //Reserved. '0.0.0.0' => '255.0.0.0'); //Reserved. //inet_pton. $ip = @inet_pton($ip); if (strlen($ip) !== 4) { return false; } //Is the IP in a private range? foreach($networks as $network_address => $network_mask) { $network_address = inet_pton($network_address); $network_mask = inet_pton($network_mask); assert(strlen($network_address) === 4); assert(strlen($network_mask) === 4); if (($ip & $network_mask) === $network_address) return false; } //Success! return true; }