*
* The parameter is a comma separated string of subnet definitions.
* Subnet strings can be in one of three formats:
- * 1: xxx.xxx.xxx.xxx/xx
- * 2: xxx.xxx
- * 3: xxx.xxx.xxx.xxx-xxx //a range of IP addresses in the last group.
+ * 1: xxx.xxx.xxx.xxx/nn or xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx/nnn (number of bits in net mask)
+ * 2: xxx.xxx.xxx.xxx-yyy or xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx::xxxx-yyyy (a range of IP addresses in the last group)
+ * 3: xxx.xxx or xxx.xxx. or xxx:xxx:xxxx or xxx:xxx:xxxx. (incomplete address, a bit non-technical ;-)
* Code for type 1 modified from user posted comments by mediator at
* {@link http://au.php.net/manual/en/function.ip2long.php}
*
- * TODO one day we will have to make this work with IP6.
- *
* @param string $addr The address you are checking
* @param string $subnetstr The string of subnet addresses
* @return bool
$subnets = explode(',', $subnetstr);
$found = false;
$addr = trim($addr);
+ $addr = cleanremoteaddr($addr, false); // normalise
+ if ($addr === null) {
+ return false;
+ }
+ $addrparts = explode(':', $addr);
+
+ $ipv6 = strpos($addr, ':');
foreach ($subnets as $subnet) {
$subnet = trim($subnet);
- if (strpos($subnet, '/') !== false) { /// type 1
+ if ($subnet === '') {
+ continue;
+ }
+
+ if (strpos($subnet, '/') !== false) {
+ ///1: xxx.xxx.xxx.xxx/nn or xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx/nnn
list($ip, $mask) = explode('/', $subnet);
- if ($mask === '' || $mask > 32) {
- $mask = 32;
+ $mask = trim($mask);
+ if (!is_number($mask)) {
+ continue; // incorect mask number, eh?
+ }
+ $ip = cleanremoteaddr($ip, false); // normalise
+ if ($ip === null) {
+ continue;
}
- $mask = 0xffffffff << (32 - $mask);
- $found = ((ip2long($addr) & $mask) == (ip2long($ip) & $mask));
- } else if (strpos($subnet, '-') !== false) {/// type 3
- $subnetparts = explode('.', $subnet);
- $addrparts = explode('.', $addr);
- $subnetrange = explode('-', array_pop($subnetparts));
- if (count($subnetrange) == 2) {
- $lastaddrpart = array_pop($addrparts);
- $found = ($subnetparts == $addrparts &&
- $subnetrange[0] <= $lastaddrpart && $lastaddrpart <= $subnetrange[1]);
+ if (strpos($ip, ':') !== false) {
+ // IPv6
+ if (!$ipv6) {
+ continue;
+ }
+ if ($mask > 128 or $mask < 0) {
+ continue; // nonsense
+ }
+ if ($mask == 0) {
+ return true; // any address
+ }
+ if ($mask == 128) {
+ if ($ip === $addr) {
+ return true;
+ }
+ continue;
+ }
+ $ipparts = explode(':', $ip);
+ $modulo = $mask % 16;
+ $ipnet = array_slice($ipparts, 0, ($mask-$modulo)/16);
+ $addrnet = array_slice($addrparts, 0, ($mask-$modulo)/16);
+ if (implode(':', $ipnet) === implode(':', $addrnet)) {
+ if ($modulo == 0) {
+ return true;
+ }
+ $pos = ($mask-$modulo)/16;
+ $ipnet = hexdec($ipparts[$pos]);
+ $addrnet = hexdec($addrparts[$pos]);
+ $mask = 0xffff << (16 - $modulo);
+ if (($addrnet & $mask) == ($ipnet & $mask)) {
+ return true;
+ }
+ }
+
+ } else {
+ // IPv4
+ if ($ipv6) {
+ continue;
+ }
+ if ($mask > 32 or $mask < 0) {
+ continue; // nonsense
+ }
+ if ($mask == 0) {
+ return true;
+ }
+ if ($mask == 32) {
+ if ($ip === $addr) {
+ return true;
+ }
+ continue;
+ }
+ $mask = 0xffffffff << (32 - $mask);
+ if (((ip2long($addr) & $mask) == (ip2long($ip) & $mask))) {
+ return true;
+ }
}
- } else { /// type 2
- if ($subnet[strlen($subnet) - 1] != '.') {
- $subnet .= '.';
+
+ } else if (strpos($subnet, '-') !== false) {
+ /// 2: xxx.xxx.xxx.xxx-yyy or xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx::xxxx-yyyy ...a range of IP addresses in the last group.
+ $parts = explode('-', $subnet);
+ if (count($parts) != 2) {
+ continue;
}
- $found = (strpos($addr . '.', $subnet) === 0);
- }
- if ($found) {
- break;
+ if (strpos($subnet, ':') !== false) {
+ // IPv6
+ if (!$ipv6) {
+ continue;
+ }
+ $ipstart = cleanremoteaddr(trim($parts[0]), false); // normalise
+ if ($ipstart === null) {
+ continue;
+ }
+ $ipparts = explode(':', $ipstart);
+ $start = hexdec(array_pop($ipparts));
+ $ipparts[] = trim($parts[1]);
+ $ipend = cleanremoteaddr(implode(':', $ipparts), false); // normalise
+ if ($ipend === null) {
+ continue;
+ }
+ $ipparts[7] = '';
+ $ipnet = implode(':', $ipparts);
+ if (strpos($addr, $ipnet) !== 0) {
+ continue;
+ }
+ $ipparts = explode(':', $ipend);
+ $end = hexdec($ipparts[7]);
+
+ $addrend = hexdec($addrparts[7]);
+
+ if (($addrend >= $start) and ($addrend <= $end)) {
+ return true;
+ }
+
+ } else {
+ // IPv4
+ if ($ipv6) {
+ continue;
+ }
+ $ipstart = cleanremoteaddr(trim($parts[0]), false); // normalise
+ if ($ipstart === null) {
+ continue;
+ }
+ $ipparts = explode('.', $ipstart);
+ $ipparts[3] = trim($parts[1]);
+ $ipend = cleanremoteaddr(implode('.', $ipparts), false); // normalise
+ if ($ipend === null) {
+ continue;
+ }
+
+ if ((ip2long($addr) >= ip2long($ipstart)) and (ip2long($addr) <= ip2long($ipend))) {
+ return true;
+ }
+ }
+
+ } else {
+ /// 3: xxx.xxx or xxx.xxx. or xxx:xxx:xxxx or xxx:xxx:xxxx.
+ if (strpos($subnet, ':') !== false) {
+ // IPv6
+ if (!$ipv6) {
+ continue;
+ }
+ $parts = explode(':', $subnet);
+ $count = count($parts);
+ if ($parts[$count-1] === '') {
+ unset($parts[$count-1]); // trim trailing :
+ $count--;
+ $subnet = implode('.', $parts);
+ }
+ $isip = cleanremoteaddr($subnet, false); // normalise
+ if ($isip !== null) {
+ if ($isip === $addr) {
+ return true;
+ }
+ continue;
+ } else if ($count > 8) {
+ continue;
+ }
+ $zeros = array_fill(0, 8-$count, '0');
+ $subnet = $subnet.':'.implode(':', $zeros).'/'.($count*16);
+ if (address_in_subnet($addr, $subnet)) {
+ return true;
+ }
+
+ } else {
+ // IPv4
+ if ($ipv6) {
+ continue;
+ }
+ $parts = explode('.', $subnet);
+ $count = count($parts);
+ if ($parts[$count-1] === '') {
+ unset($parts[$count-1]); // trim trailing .
+ $count--;
+ $subnet = implode('.', $parts);
+ }
+ if ($count == 4) {
+ $subnet = cleanremoteaddr($subnet, false); // normalise
+ if ($subnet === $addr) {
+ return true;
+ }
+ continue;
+ } else if ($count > 4) {
+ continue;
+ }
+ $zeros = array_fill(0, 4-$count, '0');
+ $subnet = $subnet.'.'.implode('.', $zeros).'/'.($count*8);
+ if (address_in_subnet($addr, $subnet)) {
+ return true;
+ }
+ }
}
}
- return $found;
+
+ return false;
}
/**
}
function test_address_in_subnet() {
- $this->assertTrue(address_in_subnet('123.121.234.1', '123.121.234.1'));
- $this->assertFalse(address_in_subnet('123.121.234.2', '123.121.234.1'));
- $this->assertFalse(address_in_subnet('123.121.134.1', '123.121.234.1'));
- $this->assertFalse(address_in_subnet('113.121.234.1', '123.121.234.1'));
- $this->assertTrue(address_in_subnet('123.121.234.0', '123.121.234.2/28'));
- $this->assertTrue(address_in_subnet('123.121.234.15', '123.121.234.2/28'));
- $this->assertFalse(address_in_subnet('123.121.234.16', '123.121.234.2/28'));
- $this->assertFalse(address_in_subnet('123.121.234.255', '123.121.234.2/28'));
- $this->assertTrue(address_in_subnet('123.121.234.0', '123.121.234.0/')); // / is like /32.
- $this->assertFalse(address_in_subnet('123.121.234.1', '123.121.234.0/'));
- $this->assertFalse(address_in_subnet('232.232.232.232', '123.121.234.0/0'));
- $this->assertFalse(address_in_subnet('123.122.234.1', '123.121.'));
- $this->assertFalse(address_in_subnet('223.121.234.1', '123.121.'));
- $this->assertTrue(address_in_subnet('123.121.234.1', '123.121'));
- $this->assertFalse(address_in_subnet('123.122.234.1', '123.121'));
- $this->assertFalse(address_in_subnet('223.121.234.1', '123.121'));
- $this->assertFalse(address_in_subnet('123.121.234.100', '123.121.234.10'));
- $this->assertFalse(address_in_subnet('123.121.234.9', '123.121.234.10-20'));
- $this->assertTrue(address_in_subnet('123.121.234.10', '123.121.234.10-20'));
- $this->assertTrue(address_in_subnet('123.121.234.15', '123.121.234.10-20'));
- $this->assertTrue(address_in_subnet('123.121.234.20', '123.121.234.10-20'));
- $this->assertFalse(address_in_subnet('123.121.234.21', '123.121.234.10-20'));
- $this->assertTrue(address_in_subnet(' 123.121.234.1 ', ' 123.121.234.1 , 1.1.1.1/16,2.2.,3.3.3.3-6 '));
- $this->assertTrue(address_in_subnet(' 1.1.2.3 ', ' 123.121.234.1 , 1.1.1.1/16,2.2.,3.3.3.3-6 '));
- $this->assertTrue(address_in_subnet(' 2.2.234.1 ', ' 123.121.234.1 , 1.1.1.1/16,2.2.,3.3.3.3-6 '));
- $this->assertTrue(address_in_subnet(' 3.3.3.4 ', ' 123.121.234.1 , 1.1.1.1/16,2.2.,3.3.3.3-6 '));
- $this->assertFalse(address_in_subnet(' 123.121.234.2 ', ' 123.121.234.1 , 1.1.1.1/16,2.2.,3.3.3.3-6 '));
- $this->assertFalse(address_in_subnet(' 2.1.2.3 ', ' 123.121.234.1 , 1.1.1.1/16,2.2.,3.3.3.3-6 '));
- $this->assertFalse(address_in_subnet(' 2.3.234.1 ', ' 123.121.234.1 , 1.1.1.1/16,2.2.,3.3.3.3-6 '));
- $this->assertFalse(address_in_subnet(' 3.3.3.7 ', ' 123.121.234.1 , 1.1.1.1/16,2.2.,3.3.3.3-6 '));
- $this->assertFalse(address_in_subnet('172.16.1.142', '172.16.1.143/148'));
+ /// 1: xxx.xxx.xxx.xxx/nn or xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx/nnn (number of bits in net mask)
+ $this->assertTrue(address_in_subnet('123.121.234.1', '123.121.234.1/32'));
+ $this->assertFalse(address_in_subnet('123.121.23.1', '123.121.23.0/32'));
+ $this->assertTrue(address_in_subnet('10.10.10.100', '123.121.23.45/0'));
+ $this->assertTrue(address_in_subnet('123.121.234.1', '123.121.234.0/24'));
+ $this->assertFalse(address_in_subnet('123.121.34.1', '123.121.234.0/24'));
+ $this->assertTrue(address_in_subnet('123.121.234.1', '123.121.234.0/30'));
+ $this->assertFalse(address_in_subnet('123.121.23.8', '123.121.23.0/30'));
+ $this->assertTrue(address_in_subnet('baba:baba::baba', 'baba:baba::baba/128'));
+ $this->assertFalse(address_in_subnet('bab:baba::baba', 'bab:baba::cece/128'));
+ $this->assertTrue(address_in_subnet('baba:baba::baba', 'cece:cece::cece/0'));
+ $this->assertTrue(address_in_subnet('baba:baba::baba', 'baba:baba::baba/128'));
+ $this->assertTrue(address_in_subnet('baba:baba::00ba', 'baba:baba::/120'));
+ $this->assertFalse(address_in_subnet('baba:baba::aba', 'baba:baba::/120'));
+ $this->assertTrue(address_in_subnet('baba::baba:00ba', 'baba::baba:0/112'));
+ $this->assertFalse(address_in_subnet('baba::aba:00ba', 'baba::baba:0/112'));
+ $this->assertFalse(address_in_subnet('aba::baba:0000', 'baba::baba:0/112'));
+
+ // fixed input
+ $this->assertTrue(address_in_subnet('123.121.23.1 ', ' 123.121.23.0 / 24'));
+ $this->assertTrue(address_in_subnet('::ffff:10.1.1.1', ' 0:0:0:000:0:ffff:a1:10 / 126'));
+
+ // incorrect input
+ $this->assertFalse(address_in_subnet('123.121.234.1', '123.121.234.1/-2'));
+ $this->assertFalse(address_in_subnet('123.121.234.1', '123.121.234.1/64'));
+ $this->assertFalse(address_in_subnet('123.121.234.x', '123.121.234.1/24'));
+ $this->assertFalse(address_in_subnet('123.121.234.0', '123.121.234.xx/24'));
+ $this->assertFalse(address_in_subnet('123.121.234.1', '123.121.234.1/xx0'));
+ $this->assertFalse(address_in_subnet('::1', '::aa:0/xx0'));
+ $this->assertFalse(address_in_subnet('::1', '::aa:0/-5'));
+ $this->assertFalse(address_in_subnet('::1', '::aa:0/130'));
+ $this->assertFalse(address_in_subnet('x:1', '::aa:0/130'));
+ $this->assertFalse(address_in_subnet('::1', '::ax:0/130'));
+
+
+ /// 2: xxx.xxx.xxx.xxx-yyy or xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx::xxxx-yyyy (a range of IP addresses in the last group)
+ $this->assertTrue(address_in_subnet('123.121.234.12', '123.121.234.12-14'));
+ $this->assertTrue(address_in_subnet('123.121.234.13', '123.121.234.12-14'));
+ $this->assertTrue(address_in_subnet('123.121.234.14', '123.121.234.12-14'));
+ $this->assertFalse(address_in_subnet('123.121.234.1', '123.121.234.12-14'));
+ $this->assertFalse(address_in_subnet('123.121.234.20', '123.121.234.12-14'));
+ $this->assertFalse(address_in_subnet('123.121.23.12', '123.121.234.12-14'));
+ $this->assertFalse(address_in_subnet('123.12.234.12', '123.121.234.12-14'));
+ $this->assertTrue(address_in_subnet('baba:baba::baba', 'baba:baba::baba-babe'));
+ $this->assertTrue(address_in_subnet('baba:baba::babc', 'baba:baba::baba-babe'));
+ $this->assertTrue(address_in_subnet('baba:baba::babe', 'baba:baba::baba-babe'));
+ $this->assertFalse(address_in_subnet('bab:baba::bab0', 'bab:baba::baba-babe'));
+ $this->assertFalse(address_in_subnet('bab:baba::babf', 'bab:baba::baba-babe'));
+ $this->assertFalse(address_in_subnet('bab:baba::bfbe', 'bab:baba::baba-babe'));
+ $this->assertFalse(address_in_subnet('bfb:baba::babe', 'bab:baba::baba-babe'));
+
+ // fixed input
+ $this->assertTrue(address_in_subnet('123.121.234.12', '123.121.234.12 - 14 '));
+ $this->assertTrue(address_in_subnet('bab:baba::babe', 'bab:baba::baba - babe '));
+
+ // incorrect input
+ $this->assertFalse(address_in_subnet('123.121.234.12', '123.121.234.12-234.14'));
+ $this->assertFalse(address_in_subnet('123.121.234.12', '123.121.234.12-256'));
+ $this->assertFalse(address_in_subnet('123.121.234.12', '123.121.234.12--256'));
+
+
+ /// 3: xxx.xxx or xxx.xxx. or xxx:xxx:xxxx or xxx:xxx:xxxx. (incomplete address, a bit non-technical ;-)
+ $this->assertTrue(address_in_subnet('123.121.234.12', '123.121.234.12'));
+ $this->assertFalse(address_in_subnet('123.121.23.12', '123.121.23.13'));
+ $this->assertTrue(address_in_subnet('123.121.234.12', '123.121.234.'));
+ $this->assertTrue(address_in_subnet('123.121.234.12', '123.121.234'));
+ $this->assertTrue(address_in_subnet('123.121.234.12', '123.121'));
+ $this->assertTrue(address_in_subnet('123.121.234.12', '123'));
+ $this->assertFalse(address_in_subnet('123.121.234.1', '12.121.234.'));
+ $this->assertFalse(address_in_subnet('123.121.234.1', '12.121.234'));
+ $this->assertTrue(address_in_subnet('baba:baba::bab', 'baba:baba::bab'));
+ $this->assertFalse(address_in_subnet('baba:baba::ba', 'baba:baba::bc'));
+ $this->assertTrue(address_in_subnet('baba:baba::bab', 'baba:baba'));
+ $this->assertTrue(address_in_subnet('baba:baba::bab', 'baba:'));
+ $this->assertFalse(address_in_subnet('bab:baba::bab', 'baba:'));
+
+
+ /// multiple subnets
+ $this->assertTrue(address_in_subnet('123.121.234.12', '::1/64, 124., 123.121.234.10-30'));
+ $this->assertTrue(address_in_subnet('124.121.234.12', '::1/64, 124., 123.121.234.10-30'));
+ $this->assertTrue(address_in_subnet('::2', '::1/64, 124., 123.121.234.10-30'));
+ $this->assertFalse(address_in_subnet('12.121.234.12', '::1/64, 124., 123.121.234.10-30'));
+
+
+ /// other incorrect input
+ $this->assertFalse(address_in_subnet('123.123.123.123', ''));
}
/**