如何在 PHP 中比较二进制字符串形式的 IP 地址?

Set*_*ley 5 php ipv4 ipv6 inet

我目前正在一个基于 PHP 的项目中使用 IPv4 和 IPv6 地址,并且我需要能够比较两个 IP 以确定哪一个数字更大。例如,192.168.1.9 大于 192.168.1.1。为了做到这一点,我使用inet_ptonunpack将 IP 转换为二进制字符串(我熟悉ip2long,但它仅限于 IPv4)。

这种方法一开始似乎工作正常,但我很快发现,当我将任何以 .32 结尾的 IP 与较低的 IP 地址进行比较时,我得到的结果不正确。例如,如果我将 192.168.1.0 与 192.168.1.32 进行比较,我的脚本会告诉我 192.168.1.0 大于 192.168.1.32。仅当其中一个 IP 以 0.32 结尾时才会发生这种情况。IP 的前三个八位字节可以更改,结果是相同的。

以下 PHP 代码生成一个说明此问题的页面:

// Loop through every possible last octet, starting with zero
for ($i = 0; $i <= 255; $i++) {

    // Define two IPs, with second IP increasing on each loop by 1
    $IP1 = "192.168.1.0";
    $IP2 = "192.168.1.".$i;

    // Convert each IP to a binary string
    $IP1_bin = current(unpack("A4",inet_pton($IP1)));
    $IP2_bin = current(unpack("A4",inet_pton($IP2)));

    // Convert each IP back to human readable format, just to show they were converted properly
    $IP1_string = inet_ntop(pack("A4",$IP1_bin));
    $IP2_string = inet_ntop(pack("A4",$IP2_bin));

    // Compare each IP and echo the result
    if ($IP1_bin < $IP2_bin) {echo '<p>'.$IP1_string.' is LESS than '.$IP2_string.'</p>';}
    if ($IP1_bin === $IP2_bin) {echo '<p>'.$IP1_string.' is EQUAL to '.$IP2_string.'</p>';}
    if ($IP1_bin > $IP2_bin) {echo '<p>'.$IP1_string.' is GREATER than '.$IP2_string.'</p>';}

    // I have also tried using strcmp for the binary comparison, with the same result
    // if (strcmp($IP1_bin,$IP2_bin) < 0) {echo '<p>'.$IP1_string.' is LESS than '.$IP2_string.'</p>';}
    // if (strcmp($IP1_bin,$IP2_bin) === 0) {echo '<p>'.$IP1_string.' iS EQUAL to '.$IP2_string.'</p>';}
    // if (strcmp($IP1_bin,$IP2_bin) > 0) {echo '<p>'.$IP1_string.' is GREATER than '.$IP2_string.'</p>';}
}

?>
Run Code Online (Sandbox Code Playgroud)

以下是结果示例:

192.168.1.0 is EQUAL to 192.168.1.0
192.168.1.0 is LESS than 192.168.1.1
192.168.1.0 is LESS than 192.168.1.2
192.168.1.0 is LESS than 192.168.1.3
192.168.1.0 is LESS than 192.168.1.4
...
192.168.1.0 is LESS than 192.168.1.31
192.168.1.0 is GREATER than 192.168.1.32
192.168.1.0 is LESS than 192.168.1.33
...
Run Code Online (Sandbox Code Playgroud)

将 IP 转换回人类可读格式会返回正确的 IP,因此我认为问题在于比较。我尝试切换到strcmp进行二进制比较,但结果是相同的。

任何有助于确定其原因的帮助将不胜感激。我不打算使用示例脚本中显示的 IP 转换和比较方法,但我确实需要坚持使用同时支持 IPv4 和 IPv6 的方法。谢谢。

我正在运行 PHP 版本 5.3.3,使用 Zend Engine v2.3.0 和 ionCube PHP Loader v4.6.1

编辑:我通过将解包格式从“A4”(空格填充字符串)更改为“a4”(NUL 填充字符串)解决了该问题。详情请参阅下面我的回答。

Set*_*ley 4

经过多次排查,我发现了这个问题的原因。当使用解包函数时,我使用了格式代码“A4”,它用于空格填充的字符串。这导致以数字 32 结尾的任何内容都被视为空白,然后将被修剪。例如,在解压 的二进制值后192.168.1.32,我将其转换为十六进制。结果是C0A801但应该是C0A80120。如果 IP 为 ,结果会更糟192.32.32.32,因为它会一直削减到C0(本质上192.0.0.0)。

切换到解包格式“a4”(NUL 填充字符串)解决了该问题。现在,唯一被截断的 IP 是以 .0 结尾的 IP,这是预期的行为。我发现这解决了这个问题,这很奇怪,因为我遇到的几乎每个解包 IPv4 和 IPv6 地址的示例都使用格式“A4”。也许他们在新版本中将inet_pton更改为空格填充。