From: skodak Date: Fri, 9 Jan 2009 21:16:26 +0000 (+0000) Subject: MDL-14123 Full IPv6 support - reimplemented address_in_subnet() + added more unittests X-Git-Url: http://git.mjollnir.org/gw?a=commitdiff_plain;h=e70da470a7c1df21edf8c2f34562a991aafd2e6b;p=moodle.git MDL-14123 Full IPv6 support - reimplemented address_in_subnet() + added more unittests --- diff --git a/lib/moodlelib.php b/lib/moodlelib.php index 1169490c5d..fd29779d03 100644 --- a/lib/moodlelib.php +++ b/lib/moodlelib.php @@ -7275,14 +7275,12 @@ function make_unique_id_code($extra='') { * * 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 @@ -7292,37 +7290,206 @@ function address_in_subnet($addr, $subnetstr) { $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; } /** diff --git a/lib/simpletest/testmoodlelib.php b/lib/simpletest/testmoodlelib.php index f500776782..8a37a16773 100644 --- a/lib/simpletest/testmoodlelib.php +++ b/lib/simpletest/testmoodlelib.php @@ -91,37 +91,92 @@ class moodlelib_test extends MoodleUnitTestCase { } 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', '')); } /**