Оптимизированный метод сравнения IP-адресов с подстановочными знаками в PHP?

Кто-нибудь знает об эффективном и безопасном методе, чтобы увидеть, является ли этот вход:

$_SERVER['REMOTE_ADDR'] 

совпадает с этим массивом непоследовательных фильтров (обратите внимание, что 200.100. *. * может быть выражено как 200.100. *) с подстановочными знаками, обозначенными символом *:

 array( '192.168.1.*', '192.168.2.1*', '10.0.0.*', '200.100.*.*', '300.200.*', ) 

Обновить

Мысли?

 foreach($instanceSettings['accessControl']['allowedIpV4Addresses'] as $ipV4Address) { echo 'Now checking against '.$ipV4Address.'.'; // Compare each octet $ipV4AddressOctets = String::explode('.', $ipV4Address); $remoteIpV4AddressOctets = String::explode('.', $_SERVER['REMOTE_ADDR']); $remoteIpV4AddressIsAllowed = true; for($i = 0; $i < Arr::size($ipV4AddressOctets); $i++) { echo 'Comparing '.$ipV4AddressOctets[$i].' against '.$remoteIpV4AddressOctets[$i].'.'; if($ipV4AddressOctets[$i] != $remoteIpV4AddressOctets[$i] && $ipV4AddressOctets[$i] != '*') { echo 'No match.'; $remoteIpV4AddressIsAllowed = false; break; } } // Get out of the foreach if we've found a match if($remoteIpV4AddressIsAllowed) { break; } } 

Я не отметил это, но я бы предпочел использовать метод, который использует сетевое аппаратное / программное обеспечение …

Замените любые * на 0 и 255. Преобразуйте IP в целые числа

Итак, если 255.255.255. * Становится 255.255.255.0 и 255.255.255.255 Затем выполните ip2long функцию на этих двух ips.

Затем вы можете преобразовать данный ip в long ip. например 255.255.50.51 в длинный ip.

Затем вы можете сравнить, является ли длинный ip для данного заданного ip между конвертированными длинными ips в черном списке. Если это так, то это еще не разрешено.

 $ips = array("ip1", "ip2"); foreach($ips as $ip){ $ip1 = str_replace("*", "0", $ip); $ip2 = str_replace("*", "255", $ip); $ip1 = ip2long($ip1); $ip2 = ip2long($ip2); $givenip = $_GET["ip"]; $givenip = ip2long($givenip); if($givenip >= $ip1 && $ip <= $givenip){ echo "blacklist ip hit between {$ip1} and {$ip2} on {$ip}"; } } 

Удалите звездочки и просто выполните:

 $ips = array('192.168.1.', '10.0.0.'); foreach ($ips as $ip) { if (strpos($_SERVER['REMOTE_ADDR'], $ip) === 0) { // match } } 

Это разрешает все случаи в вопросе плюс короткие маски без звездочек, таких как 123.123.

 /** * Checks given IP against array of masks like 123.123.123.123, 123.123.*.101, 123.123., 123.123.1*.* * * @param $ip * @param $masks * @return bool */ public static function checkIp($ip, $masks) { if (in_array($ip, $masks)) { return true; // Simple match } else { foreach ($masks as $mask) { if (substr($mask, -1) == '.' AND substr($ip, 0, strlen($mask)) == $mask) { return true; // Case for 123.123. mask } if (strpos($mask, '*') === false) { continue; // No simple matching and no wildcard in the mask, leaves no chance to match } // Breaking into triads $maskParts = explode('.', $mask); $ipParts = explode('.', $ip); foreach ($maskParts as $key => $maskPart) { if ($maskPart == '*') { continue; // This triad is matching, continue with next triad } elseif (strpos($maskPart, '*') !== false) { // Case like 1*, 1*2, *1 // Let's use regexp for this $regExp = str_replace('*', '\d{0,3}', $maskPart); if (preg_match('/^' . $regExp . '$/', $ipParts[$key])) { continue; // Matching, go to check next triad } else { continue 2; // Not matching, Go to check next mask } } else { if ($maskPart != $ipParts[$key]) { continue 2; // If triad has no wildcard and not matching, check next mask } // otherwise just continue } } // We checked all triads and all matched, hence this mask is matching return true; } // We went through all masks and none has matched. return false; } } 

Почему бы просто не использовать регулярное выражение?

  preg_match("((192\\.168\\.1)|(10\\.0\\.0)|(127\\.0\\.0)\\.[012]\\d{0,2}|(\\:\\:1))",$_SERVER['REMOTE_ADDR']) 

Просто для удовольствия, я собираюсь переоценить это. Ну, если у вас довольно длинный список, чтобы соответствовать.

Предполагая, что вы используете только подстановочные знаки, чтобы означать «я не забочусь об этом октете», вы можете проанализировать каждую запись в своем массиве на четыре значения (по одному на октет). Скажем, что вы используете -1 для обозначения подстановочного знака, 0-255 означает точно соответствовать этому значению. (Если вам нужна более высокая производительность, чем O (n), где n – это размер списка совпадений, то есть лучшие структуры данных, которые вы можете использовать здесь, например, trie.) Назовите этот массив L. Конечно, вы только нужно сделать это один раз, а не по запросу.

Затем вы можете разобрать удаленный адрес таким же образом (за исключением без подстановочных знаков). Вы также можете поймать REMOTE_ADDR, не находясь в ожидаемом формате здесь. Теперь становится достаточно тривиально проверять совпадения:

 has_match(ip) = for n in [0 … L.length) if (-1 == Ln0 || Ln0 = ip.0) && (-1 == Ln1 || Ln1 == ip.1) && … return true return false 

(Это, конечно, псевдокод)