I am currently working with IPv4 and IPv6 addresses in a PHP based project, and I need to be able to compare two IPs to determine which one is a higher number. For example, that 192.168.1.9 is greater than 192.168.1.1. In order to do this, I converted the IPs to binary strings using inet_pton and unpack (I am familiar with ip2long, however it is limited to IPv4).
This method seemed to work fine at first, however I soon discovered that I get the incorrect result when I compare any IP ending in .32 to a lower IP address. For example, if I compare 192.168.1.0 to 192.168.1.32, my script tells me that 192.168.1.0 is greater than 192.168.1.32. This only happens when one of the IPs ends in .32. The first three octets of the IP can be changed, and the result is the same.
The following PHP code generates a page that illustrates this issue:
// 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>';}
}
?>
Here is a sample of the result:
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
...
Converting the IPs back to human readable format returns the correct IP, so I believe the issue is with the comparison. I have tried switching to strcmp for the binary comparison, however the result was the same.
Any help in determining the cause of this would be greatly appreciated. I am not set on using the IP conversion and comparison methods shown in the example script, however I do need to stick with methods that support both IPv4 and IPv6. Thanks.
I am running PHP verison 5.3.3, with Zend Engine v2.3.0 and ionCube PHP Loader v4.6.1
Edit: I resolved the issue by changing the unpack format from "A4" (space-padded strings) to "a4" (NUL-padded strings). See my answer below for details.