$v){ if(filter_var($v, FILTER_VALIDATE_IP) && false !== ($ip = apply_filters('meow_filter_ip', $v)) && !in_array($ip, $server)) $ips[$k] = $v; } ksort($ips); return $ips; } //------------------------------------------------- // Sanitize IP // // strip out bad characters, compact // // @param ip(s) // @return ip(s) or false function meow_filter_ip_sanitize($data){ //already false? if($data === false) return false; $valid = array(); $single = is_string($data); $data = (array) $data; //process as an array foreach($data AS $v) { if(!is_string($v) || !strlen($v)) return false; //strip out bad characters $v = preg_replace('/[^\d\.\:a-f]/', '', strtolower($v)); if($v !== esc_sql($v)) continue; //compact ipv6 for consistency if(filter_var($v, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) $valid[] = meow_compact_ipv6($v); //ipv4 elseif(filter_var($v, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) $valid[] = $v; } $valid = array_unique($valid); sort($valid); if(!count($valid)) return false; //return an array or the single IP we were checking return $single ? $valid[0] : $valid; } add_filter('meow_filter_ip', 'meow_filter_ip_sanitize', 5, 1); //------------------------------------------------- // Filter Restricted // // @param ip(s) // @return ip(s) function meow_filter_ip_restricted($data){ //already false? if($data === false) return false; $valid = array(); $single = is_string($data); $data = (array) $data; //process as array foreach($data AS $v) { //check range if(filter_var($v, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) $valid[] = $v; } $valid = array_unique($valid); sort($valid); if(!count($valid)) return false; //return an array or the single IP we were checking return $single ? $valid[0] : $valid; } add_filter('meow_filter_ip', 'meow_filter_ip_sanitize', 5, 1); //------------------------------------------------- // IP as Number // // convert an IP to a number for cleaner comparison // // @param IP // @return num or false function meow_ip_to_number($ip){ if(!filter_var($ip, FILTER_VALIDATE_IP)) return false; //ipv4 is easy elseif(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) return ip2long($ip); //ipv6 is a little more roundabout elseif(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { //bcmath is best here if(extension_loaded('bcmath')){ $ip_n = inet_pton($ip); $bin = ''; for($bit = strlen($ip_n) - 1; $bit >= 0; $bit--) $bin = sprintf('%08b', ord($ip_n[$bit])) . $bin; $dec = '0'; for ($i = 0; $i < strlen($bin); $i++) { $dec = bcmul($dec, '2', 0); $dec = bcadd($dec, $bin[$i], 0); } return $dec; } //we can fallback to something *almost* accurate else { $binNum = ''; foreach (unpack('C*', inet_pton($ip)) as $byte) $binNum .= str_pad(decbin($byte), 8, "0", STR_PAD_LEFT); return base_convert(ltrim($binNum, '0'), 2, 10); } } return false; } //------------------------------------------------- // IP subnet // // return subnet as CIDR subnet (/24 or /64) // // @param ip // @return subnet or false function meow_ip_to_subnet($ip){ //ipv4 if(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { //find the minimum IP (simply last chunk to 0) $bits = explode('.', $ip); $bits[3] = 0; return implode('.', $bits) . '/24'; } //ipv6 elseif(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { //expand the ip $ip = meow_expand_ipv6($ip); //find the minimum IP (last 64 bytes to 0) $bits = explode(':', $ip); for($x=4; $x<=7; $x++) $bits[$x] = 0; return meow_compact_ipv6(implode(':', $bits)) . '/64'; } return false; } add_action('init', 'meow_ip_to_subnet'); //------------------------------------------------- // Expand IPv6 // // @param ip // @return ip function meow_expand_ipv6($ip){ $hex = unpack("H*hex", inet_pton($ip)); $ip = substr(preg_replace("/([A-f0-9]{4})/", "$1:", $hex['hex']), 0, -1); return $ip; } //------------------------------------------------- // Compact IPv6 // // @param ip // @return ip function meow_compact_ipv6($ip){ return inet_ntop(inet_pton($ip)); } //------------------------------------------------- // CIDR to Range // // @param cidr // @return range(min, max) function meow_cidr_to_range($cidr){ $range = array('min'=>0, 'max'=>0); if(substr_count($cidr, '/') !== 1) return false; $cidr = explode('/', $cidr); if(intval($cidr[1]) <= 0) return false; //ipv4? if(filter_var($cidr[0], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { $range['min'] = long2ip((ip2long($cidr[0])) & ((-1 << (32 - (int)$cidr[1])))); $range['max'] = long2ip((ip2long($cidr[0])) + pow(2, (32 - (int)$cidr[1])) - 1); return $range['min'] !== '0.0.0.0' && $range['max'] !== '0.0.0.0' ? $range : false; } //ipv6? of course a little more complicated if(filter_var($cidr[0], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { //parse the address into a binary string $firstaddrbin = inet_pton($cidr[0]); //convert the binary string to a string with hexadecimal characters (bin2hex) $firstaddrhex = unpack('H*', $firstaddrbin); $firstaddrhex = reset($firstaddrhex); //overwriting first address string to make sure notation is optimal $cidr[0] = inet_ntop($firstaddrbin); //calculate the number of 'flexible' bits $flexbits = 128 - $cidr[1]; //build the hexadecimal string of the last address $lastaddrhex = $firstaddrhex; //we start at the end of the string (which is always 32 characters long) $pos = 31; while($flexbits > 0) { //get the character at this position $orig = substr($lastaddrhex, $pos, 1); //convert it to an integer $origval = hexdec($orig); //OR it with (2^flexbits)-1, with flexbits limited to 4 at a time $newval = $origval | (pow(2, min(4, $flexbits)) - 1); //convert it back to a hexadecimal character $new = dechex($newval); //and put that character back in the string $lastaddrhex = substr_replace($lastaddrhex, $new, $pos, 1); //we processed one nibble, move to previous position $flexbits -= 4; $pos -= 1; } //convert the hexadecimal string to a binary string (hex2bin) $lastaddrbin = pack('H*', $lastaddrhex); //and create an IPv6 address from the binary string $lastaddrstr = inet_ntop($lastaddrbin); //pack and done! $range['min'] = meow_filter_ip_sanitize($cidr[0]); $range['max'] = meow_filter_ip_sanitize($lastaddrstr); return $range; } return false; } //------------------------------------------------- // IP in Range // // values should be filtered by this point // // @param ip // @param range // @return true/false function meow_ip_in_range($ip, $range){ //is range a regular IP? if(filter_var($range, FILTER_VALIDATE_IP)) return $ip === $range; //maybe a range with a dash? if(substr_count($range, '-')){ list($min, $max) = explode('-', $range); $ip = meow_ip_to_number($ip); $min = meow_ip_to_number($min); $max = meow_ip_to_number($max); return $ip >= $min && $ip <= $max; } //a cidr? elseif(substr_count($range, '/')){ $range = meow_cidr_to_range($range); $ip = meow_ip_to_number($ip); return $ip >= $range['min'] && $ip <= $range['max']; } return false; } //--------------------------------------------------------------------- end IP ?>