diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index 089f4d88bd..2cae8b17f9 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -543,9 +543,9 @@ class Request } // Remove port number from host - $elements = explode(':', $host); + $host = preg_replace('/:\d+$/', '', $host); - return trim($elements[0]); + return trim($host); } public function setMethod($method) diff --git a/src/Symfony/Component/HttpFoundation/RequestMatcher.php b/src/Symfony/Component/HttpFoundation/RequestMatcher.php index 43825dae9e..5441d35344 100644 --- a/src/Symfony/Component/HttpFoundation/RequestMatcher.php +++ b/src/Symfony/Component/HttpFoundation/RequestMatcher.php @@ -121,6 +121,16 @@ class RequestMatcher implements RequestMatcherInterface } protected function checkIp($ip) + { + // IPv6 address + if (false !== strpos($ip, ':')) { + return $this->checkIp6($ip); + } else { + return $this->checkIp4($ip); + } + } + + protected function checkIp4($ip) { if (false !== strpos($this->ip, '/')) { list($address, $netmask) = explode('/', $this->ip); @@ -135,4 +145,27 @@ class RequestMatcher implements RequestMatcherInterface return 0 === substr_compare(sprintf('%032b', ip2long($ip)), sprintf('%032b', ip2long($address)), 0, $netmask); } + + /** + * @author David Soria Parra + * @see https://github.com/dsp/v6tools + */ + protected function checkIp6($ip) + { + list($address, $netmask) = explode('/', $this->ip); + + $bytes_addr = unpack("n*", inet_pton($address)); + $bytes_test = unpack("n*", inet_pton($ip)); + + for ($i = 1, $ceil = ceil($netmask / 16); $i <= $ceil; $i++) { + $left = $netmask - 16 * ($i-1); + $left = ($left <= 16) ?: 16; + $mask = ~(0xffff >> $left) & 0xffff; + if (($bytes_addr[$i] & $mask) != ($bytes_test[$i] & $mask)) { + return false; + } + } + + return true; + } } diff --git a/src/Symfony/Component/Validator/Constraints/UrlValidator.php b/src/Symfony/Component/Validator/Constraints/UrlValidator.php index d10cb00cca..2840cbb9f9 100644 --- a/src/Symfony/Component/Validator/Constraints/UrlValidator.php +++ b/src/Symfony/Component/Validator/Constraints/UrlValidator.php @@ -20,9 +20,13 @@ class UrlValidator extends ConstraintValidator const PATTERN = '~^ (%s):// # protocol ( - ([a-z0-9-]+\.)+[a-z]{2,6} # a domain name - | # or - \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3} # a IP address + ([a-z0-9-]+\.)+[a-z]{2,6} # a domain name + | # or + \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3} # a IP address + | # or + \[ + (?:(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){6})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:::(?:(?:(?:[0-9a-f]{1,4})):){5})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:[0-9a-f]{1,4})))?::(?:(?:(?:[0-9a-f]{1,4})):){4})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,1}(?:(?:[0-9a-f]{1,4})))?::(?:(?:(?:[0-9a-f]{1,4})):){3})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,2}(?:(?:[0-9a-f]{1,4})))?::(?:(?:(?:[0-9a-f]{1,4})):){2})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,3}(?:(?:[0-9a-f]{1,4})))?::(?:(?:[0-9a-f]{1,4})):)(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,4}(?:(?:[0-9a-f]{1,4})))?::)(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,5}(?:(?:[0-9a-f]{1,4})))?::)(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,6}(?:(?:[0-9a-f]{1,4})))?::)))) + \] # a IPv6 address ) (:[0-9]+)? # a port (optional) (/?|/\S+) # a /, nothing or a / with something diff --git a/tests/Symfony/Tests/Component/HttpFoundation/RequestMatcherTest.php b/tests/Symfony/Tests/Component/HttpFoundation/RequestMatcherTest.php index 167e03ccbf..d24eb54368 100644 --- a/tests/Symfony/Tests/Component/HttpFoundation/RequestMatcherTest.php +++ b/tests/Symfony/Tests/Component/HttpFoundation/RequestMatcherTest.php @@ -16,19 +16,28 @@ use Symfony\Component\HttpFoundation\Request; class RequestMatcherTest extends \PHPUnit_Framework_TestCase { - public function testIp() + /** + * @dataProvider testIpProvider + */ + public function testIp($matches, $remoteAddr, $cidr) { + $request = Request::create('', 'get', array(), array(), array(), array('REMOTE_ADDR' => $remoteAddr)); + $matcher = new RequestMatcher(); + $matcher->matchIp($cidr); - $matcher->matchIp('192.168.1.1/1'); - $request = Request::create('', 'get', array(), array(), array(), array('REMOTE_ADDR' => '192.168.1.1')); - $this->assertTrue($matcher->matches($request)); + $this->assertEquals($matches, $matcher->matches($request)); + } - $matcher->matchIp('192.168.1.0/24'); - $this->assertTrue($matcher->matches($request)); - - $matcher->matchIp('1.2.3.4/1'); - $this->assertFalse($matcher->matches($request)); + public function testIpProvider() + { + return array( + array(true, '192.168.1.1', '192.168.1.1/1'), + array(true, '192.168.1.1', '192.168.1.0/24'), + array(false, '192.168.1.1', '1.2.3.4/1'), + array(true, '2a01:198:603:0:396e:4789:8e99:890f', '2a01:198:603:0::/65'), + array(false, '2a00:198:603:0:396e:4789:8e99:890f', '2a01:198:603:0::/65'), + ); } public function testMethod() diff --git a/tests/Symfony/Tests/Component/HttpFoundation/RequestTest.php b/tests/Symfony/Tests/Component/HttpFoundation/RequestTest.php index bd88dc6889..30f51c0033 100644 --- a/tests/Symfony/Tests/Component/HttpFoundation/RequestTest.php +++ b/tests/Symfony/Tests/Component/HttpFoundation/RequestTest.php @@ -81,6 +81,22 @@ class RequestTest extends \PHPUnit_Framework_TestCase $this->assertEquals(90, $request->getPort()); $this->assertTrue($request->isSecure()); + $request = Request::create('https://127.0.0.1:90/foo'); + $this->assertEquals('https://127.0.0.1:90/foo', $request->getUri()); + $this->assertEquals('/foo', $request->getPathInfo()); + $this->assertEquals('127.0.0.1', $request->getHost()); + $this->assertEquals('127.0.0.1:90', $request->getHttpHost()); + $this->assertEquals(90, $request->getPort()); + $this->assertTrue($request->isSecure()); + + $request = Request::create('https://[::1]:90/foo'); + $this->assertEquals('https://[::1]:90/foo', $request->getUri()); + $this->assertEquals('/foo', $request->getPathInfo()); + $this->assertEquals('[::1]', $request->getHost()); + $this->assertEquals('[::1]:90', $request->getHttpHost()); + $this->assertEquals(90, $request->getPort()); + $this->assertTrue($request->isSecure()); + $json = '{"jsonrpc":"2.0","method":"echo","id":7,"params":["Hello World"]}'; $request = Request::create('http://example.com/jsonrpc', 'POST', array(), array(), array(), array(), $json); $this->assertEquals($json, $request->getContent()); @@ -434,21 +450,38 @@ class RequestTest extends \PHPUnit_Framework_TestCase $this->assertEquals('PURGE', $request->getMethod(), '->getMethod() returns the method from _method if defined and POST'); } - public function testGetClientIp() + /** + * @dataProvider testGetClientIpProvider + */ + public function testGetClientIp($expected, $proxy, $remoteAddr, $httpClientIp, $httpForwardedFor) { $request = new Request; $this->assertEquals('', $request->getClientIp()); $this->assertEquals('', $request->getClientIp(true)); - $request->initialize(array(), array(), array(), array(), array(), array('REMOTE_ADDR' => '88.88.88.88')); - $this->assertEquals('88.88.88.88', $request->getClientIp()); - $request->initialize(array(), array(), array(), array(), array(), array('REMOTE_ADDR' => '127.0.0.1', 'HTTP_CLIENT_IP' => '88.88.88.88')); - $this->assertEquals('127.0.0.1', $request->getClientIp()); - $request->initialize(array(), array(), array(), array(), array(), array('REMOTE_ADDR' => '127.0.0.1', 'HTTP_CLIENT_IP' => '88.88.88.88')); - $this->assertEquals('88.88.88.88', $request->getClientIp(true)); - $request->initialize(array(), array(), array(), array(), array(), array('REMOTE_ADDR' => '127.0.0.1', 'HTTP_X_FORWARDED_FOR' => '88.88.88.88')); - $this->assertEquals('127.0.0.1', $request->getClientIp()); - $request->initialize(array(), array(), array(), array(), array(), array('REMOTE_ADDR' => '127.0.0.1', 'HTTP_X_FORWARDED_FOR' => '88.88.88.88')); - $this->assertEquals('88.88.88.88', $request->getClientIp(true)); + + $server = array('REMOTE_ADDR' => $remoteAddr); + if (!is_null($httpClientIp)) { + $server['HTTP_CLIENT_IP'] = $httpClientIp; + } + if (!is_null($httpForwardedFor)) { + $server['HTTP_X_FORWARDED_FOR'] = $httpForwardedFor; + } + + $request->initialize(array(), array(), array(), array(), array(), $server); + $this->assertEquals($expected, $request->getClientIp($proxy)); + } + + public function testGetClientIpProvider() + { + return array( + array('88.88.88.88', false, '88.88.88.88', null, null), + array('127.0.0.1', false, '127.0.0.1', '88.88.88.88', null), + array('88.88.88.88', true, '127.0.0.1', '88.88.88.88', null), + array('127.0.0.1', false, '127.0.0.1', null, '88.88.88.88'), + array('88.88.88.88', true, '127.0.0.1', null, '88.88.88.88'), + array('::1', false, '::1', null, null), + array('2620:0:1cfe:face:b00c::3', true, '::1', '2620:0:1cfe:face:b00c::3', null), + ); } public function testGetContentWorksTwiceInDefaultMode() diff --git a/tests/Symfony/Tests/Component/Validator/Constraints/UrlValidatorTest.php b/tests/Symfony/Tests/Component/Validator/Constraints/UrlValidatorTest.php index a41261e4e4..a796ee0992 100755 --- a/tests/Symfony/Tests/Component/Validator/Constraints/UrlValidatorTest.php +++ b/tests/Symfony/Tests/Component/Validator/Constraints/UrlValidatorTest.php @@ -57,6 +57,8 @@ class UrlValidatorTest extends \PHPUnit_Framework_TestCase array('http://www.symfony.com/'), array('http://127.0.0.1/'), array('http://127.0.0.1:80/'), + array('http://[::1]/'), + array('http://[::1]:80/'), ); }