diff options
Diffstat (limited to 'system')
-rw-r--r-- | system/core/Input.php | 94 |
1 files changed, 71 insertions, 23 deletions
diff --git a/system/core/Input.php b/system/core/Input.php index 4bb08f808..b65509fd7 100644 --- a/system/core/Input.php +++ b/system/core/Input.php @@ -328,14 +328,19 @@ class CI_Input { return $this->ip_address; } - $this->ip_address = $_SERVER['REMOTE_ADDR']; $proxy_ips = config_item('proxy_ips'); + if ( ! empty($proxy_ips) && ! is_array($proxy_ips)) + { + $proxy_ips = explode(',', str_replace(' ', '', $proxy_ips)); + } - if ( ! empty($proxy_ips)) + $this->ip_address = $this->server('REMOTE_ADDR'); + + if ($proxy_ips) { foreach (array('HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'HTTP_X_CLIENT_IP', 'HTTP_X_CLUSTER_CLIENT_IP') as $header) { - if (($spoof = $this->server($header)) !== FALSE) + if (($spoof = $this->server($header)) !== NULL) { // Some proxies typically list the whole chain of IP // addresses through which the client has reached us. @@ -348,7 +353,7 @@ class CI_Input { if ( ! $this->valid_ip($spoof)) { - $spoof = FALSE; + $spoof = NULL; } else { @@ -359,32 +364,75 @@ class CI_Input { if ($spoof) { - $has_ranges = (strpos($proxy_ips, '/') !== FALSE); - $proxy_ips = explode(',', str_replace(' ', '', $proxy_ips)); - - if ($has_ranges) + for ($i = 0, $c = count($proxy_ips), $separator = (strlen($ip) === 32 ? '.' : ':'); $i < $c; $i++) { - $long_ip = ip2long($_SERVER['REMOTE_ADDR']); - $bit_32 = 1 << 32; - - // Go through each of the IP Addresses to check for and - // test against range notation - foreach ($proxy_ips as $ip) + // Check if we have an IP address or a subnet + if (strpos($proxy_ips[$i], '/') === FALSE) { - list($address, $mask_length) = explode('/', $ip, 2); - - // Generate the bitmask for a 32 bit IP Address - $bitmask = $bit_32 - (1 << (32 - (int) $mask_length)); - if (($long_ip & $bitmask) === $address) + // An IP address (and not a subnet) is specified. + // We can compare right away. + if ($proxy_ips[$i] === $this->ip_address) { $this->ip_address = $spoof; break; } + + continue; + } + + // We have a subnet ... now the heavy lifting begins + isset($separator) OR $separator = $this->valid_ip($this->ip_address, 'ipv6') ? ':' : '.'; + + // If the proxy entry doesn't match the IP protocol - skip it + if (strpos($proxy_ips[$i], $separator) === FALSE) + { + continue; + } + + // Convert the REMOTE_ADDR IP address to binary, if needed + if ( ! isset($ip, $convert_func)) + { + if ($separator === ':') + { + // Make sure we're have the "full" IPv6 format + $ip = str_replace('::', str_repeat(':', 9 - substr_count($this->ip_address, ':')), $this->ip_address); + $convert_func = is_php('5.3') + ? function ($value) + { + return str_pad(base_convert($value, 16, 2), 16, '0', STR_PAD_LEFT); + } + : create_function('$value', 'return str_pad(base_convert($value, 16, 2), 16, "0", STR_PAD_LEFT);'); + } + else + { + $ip = $this->ip_address; + $convert_func = is_php('5.3') + ? function ($value) + { + return str_pad(decbin($value), 8, '0', STR_PAD_LEFT); + } + : create_function('$value', 'return str_pad(decbin($value), 8, "0", STR_PAD_LEFT);'); + } + + $ip = implode(array_map($convert_func, explode($separator, $ip))); + } + + // Split the netmask length off the network address + list($netaddr, $masklen) = explode('/', $proxy_ips[$i], 2); + + // Again, an IPv6 address is most likely in a compressed form + if ($separator === ':') + { + $netaddr = str_replace('::', str_repeat(':', 9 - substr_count($netaddr, ':')), $netaddr); + } + + // Convert to a binary form and finally compare + $netaddr = implode(array_map($convert_func, explode($separator, $netaddr))); + if (strncmp($ip, $netaddr, $masklen) === 0) + { + $this->ip_address = $spoof; + break; } - } - elseif (in_array($_SERVER['REMOTE_ADDR'], $proxy_ips, TRUE)) - { - $this->ip_address = $spoof; } } } |