summaryrefslogtreecommitdiffstats
path: root/system/core/Input.php
diff options
context:
space:
mode:
Diffstat (limited to 'system/core/Input.php')
-rw-r--r--system/core/Input.php94
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;
}
}
}