8000 merged branch vicb/httputils (PR #6005) · symfony/symfony@0c6e145 · GitHub
[go: up one dir, main page]

Skip to content

Commit 0c6e145

Browse files
committed
merged branch vicb/httputils (PR #6005)
This PR was squashed before being merged into the master branch (closes #6005). Commits ------- 577ee80 [HttpFoundation] Move IP check methods to a HttpUtils class for reuse Discussion ---------- [HttpFoundation] Move IP check methods to a HttpUtils class for reuse --------------------------------------------------------------------------- by vicb at 2012-11-13T18:05:18Z Thanks @stof ! (didn't get my copy paste error as PHP allow calling non static method w/o a warning). --------------------------------------------------------------------------- by GromNaN at 2012-11-17T23:19:29Z Having an `Utils` class with mixed functions doesn't seem to be a good practice. I think the class should be called something like `Symfony\Component\HttpFoundation\IpAddress`. --------------------------------------------------------------------------- by vicb at 2012-11-27T09:37:20Z @fabpot could this be merged if `HttpUtils` is renamed to `IpUtils` ? --------------------------------------------------------------------------- by fabpot at 2012-12-06T13:35:28Z Renaming the class to `IpUtils` is indeed a good idea. --------------------------------------------------------------------------- by vicb at 2012-12-06T14:07:59Z ready ! --------------------------------------------------------------------------- by fabpot at 2012-12-06T14:39:19Z Can you add an entry in the CHANGELOG? --------------------------------------------------------------------------- by vicb at 2012-12-06T14:53:09Z done, thanks for the reminder !
2 parents 9de5ffa + 577ee80 commit 0c6e145

File tree

5 files changed

+184
-159
lines changed

5 files changed

+184
-159
lines changed

src/Symfony/Component/HttpFoundation/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ CHANGELOG
44
2.2.0
55
-----
66

7+
* added a IpUtils class to check if an IP belongs to a CIDR
78
* added Request::getRealMethod() to get the "real" HTTP method (getMethod() returns the "intended" HTTP method)
89
* disabled _method request parameter support by default (call Request::enableHttpMethodParameterOverride() to enable it)
910
* Request::splitHttpAcceptHeader() method is deprecated and will be removed in 2.3
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\HttpFoundation;
13+
14+
/**
15+
* Http utility functions.
16+
*
17+
* @author Fabien Potencier <fabien@symfony.com>
18+
*/
19+
class IpUtils
20+
{
21+
/**
22+
* This class should not be instantiated
23+
*/
24+
private function __construct() {}
25+
26+
/**
27+
* Validates an IPv4 or IPv6 address.
28+
*
29+
* @param string $requestIp
30+
* @param string $ip
31+
*
32+
* @return boolean Whether the IP is valid
33+
*/
34+
public static function checkIp($requestIp, $ip)
35+
{
36+
if (false !== strpos($requestIp, ':')) {
37+
return self::checkIp6($requestIp, $ip);
38+
}
39+
40+
return self::checkIp4($requestIp, $ip);
41+
}
42+
43+
/**
44+
* Validates an IPv4 address.
45+
*
46+
* @param string $requestIp
47+
* @param string $ip
48+
*
49+
* @return boolean Whether the IP is valid
50+
*/
51+
public static function checkIp4($requestIp, $ip)
52+
{
53+
if (false !== strpos($ip, '/')) {
54+
list($address, $netmask) = explode('/', $ip, 2);
55+
56+
if ($netmask < 1 || $netmask > 32) {
57+
return false;
58+
}
59+
} else {
60+
$address = $ip;
61+
$netmask = 32;
62+
}
63+
64+
return 0 === substr_compare(sprintf('%032b', ip2long($requestIp)), sprintf('%032b', ip2long($address)), 0, $netmask);
65+
}
66+
67+
/**
68+
* Validates an IPv6 address.
69+
*
70+
* @author David Soria Parra <dsp at php dot net>
71+
* @see https://github.com/dsp/v6tools
72+
*
73+
* @param string $requestIp
74+
* @param string $ip
75+
*
76+
* @return boolean Whether the IP is valid
77+
*
78+
* @throws \RuntimeException When IPV6 support is not enabled
79+
*/
80+
public static function checkIp6($requestIp, $ip)
81+
{
82+
if (!((extension_loaded('sockets') && defined('AF_INET6')) || @inet_pton('::1'))) {
83+
throw new \RuntimeException('Unable to check Ipv6. Check that PHP was not compiled with option "disable-ipv6".');
84+
}
85+
86+
if (false !== strpos($ip, '/')) {
87+
list($address, $netmask) = explode('/', $ip, 2);
88+
89+
if ($netmask < 1 || $netmask > 128) {
90+
return false;
91+
}
92+
} else {
93+
$address = $ip;
94+
$netmask = 128;
95+
}
96+
97+
$bytesAddr = unpack("n*", inet_pton($address));
98+
$bytesTest = unpack("n*", inet_pton($requestIp));
99+
100+
for ($i = 1, $ceil = ceil($netmask / 16); $i <= $ceil; $i++) {
101+
$left = $netmask - 16 * ($i-1);
102+
$left = ($left <= 16) ? $left : 16;
103+
$mask = ~(0xffff >> $left) & 0xffff;
104+
if (($bytesAddr[$i] & $mask) != ($bytesTest[$i] & $mask)) {
105+
return false;
106+
}
107+
}
108+
109+
return true;
110+
}
111+
}

src/Symfony/Component/HttpFoundation/RequestMatcher.php

Lines changed: 1 addition & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -143,96 +143,11 @@ public function matches(Request $request)
143143
return false;
144144
}
145145

146-
if (null !== $this->ip && !$this->checkIp($request->getClientIp(), $this->ip)) {
146+
if (null !== $this->ip && !IpUtils::checkIp($request->getClientIp(), $this->ip)) {
147147
return false;
148148
}
149149

150150
return true;
151151
}
152-
153-
/**
154-
* Validates an IP address.
155-
*
156-
* @param string $requestIp
157-
* @param string $ip
158-
*
159-
* @return boolean True valid, false if not.
160-
*/
161-
protected function checkIp($requestIp, $ip)
162-
{
163-
// IPv6 address
164-
if (false !== strpos($requestIp, ':')) {
165-
return $this->checkIp6($requestIp, $ip);
166-
} else {
167-
return $this->checkIp4($requestIp, $ip);
168-
}
169-
}
170-
171-
/**
172-
* Validates an IPv4 address.
173-
*
174-
* @param string $requestIp
175-
* @param string $ip
176-
*
177-
* @return boolean True valid, false if not.
178-
*/
179-
protected function checkIp4($requestIp, $ip)
180-
{
181-
if (false !== strpos($ip, '/')) {
182-
list($address, $netmask) = explode('/', $ip, 2);
183-
184-
if ($netmask < 1 || $netmask > 32) {
185-
return false;
186-
}
187-
} else {
188-
$address = $ip;
189-
$netmask = 32;
190-
}
191-
192-
return 0 === substr_compare(sprintf('%032b', ip2long($requestIp)), sprintf('%032b', ip2long($address)), 0, $netmask);
193-
}
194-
195-
/**
196-
* Validates an IPv6 address.
197-
*
198-
* @author David Soria Parra <dsp at php dot net>
199-
* @see https://github.com/dsp/v6tools
200-
*
201-
* @param string $requestIp
202-
* @param string $ip
203-
*
204-
* @return boolean True valid, false if not.
205-
*/
206-
protected function checkIp6($requestIp, $ip)
207-
{
208-
if (!((extension_loaded('sockets') && defined('AF_INET6')) || @inet_pton('::1'))) {
209-
throw new \RuntimeException('Unable to check Ipv6. Check that PHP was not compiled with option "disable-ipv6".');
210-
}
211-
212-
if (false !== strpos($ip, '/')) {
213-
list($address, $netmask) = explode('/', $ip, 2);
214-
215-
if ($netmask < 1 || $netmask > 128) {
216-
return false;
217-
}
218-
} else {
219-
$address = $ip;
220-
$netmask = 128;
221-
}
222-
223-
$bytesAddr = unpack("n*", inet_pton($address));
224-
$bytesTest = unpack("n*", inet_pton($requestIp));
225-
226-
for ($i = 1, $ceil = ceil($netmask / 16); $i <= $ceil; $i++) {
227-
$left = $netmask - 16 * ($i-1);
228-
$left = ($left <= 1241 16) ? $left : 16;
229-
$mask = ~(0xffff >> $left) & 0xffff;
230-
if (($bytesAddr[$i] & $mask) != ($bytesTest[$i] & $mask)) {
231-
return false;
232-
}
233-
}
234-
235-
return true;
236-
}
237152
}
238153

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\HttpFoundation\Tests;
13+
14+
use Symfony\Component\HttpFoundation\IpUtils;
15+
16+
class IpUtilsTest extends \PHPUnit_Framework_TestCase
17+
{
18+
/**
19+
* @dataProvider testIpv4Provider
20+
*/
21+
public function testIpv4($matches, $remoteAddr, $cidr)
22+
{
23+
$this->assertSame($matches, IpUtils::checkIp($remoteAddr, $cidr));
24+
}
25+
26+
public function testIpv4Provider()
27+
{
28+
return array(
29+
array(true, '192.168.1.1', '192.168.1.1'),
30+
array(true, '192.168.1.1', '192.168.1.1/1'),
31+
array(true, '192.168.1.1', '192.168.1.0/24'),
32+
array(false, '192.168.1.1', '1.2.3.4/1'),
33+
array(false, '192.168.1.1', '192.168.1/33'),
34+
);
35+
}
36+
37+
/**
38+
* @dataProvider testIpv6Provider
39+
*/
40+
public function testIpv6($matches, $remoteAddr, $cidr)
41+
{
42+
if (!defined('AF_INET6')) {
43+
$this->markTestSkipped('Only works when PHP is compiled without the option "disable-ipv6".');
44+
}
45+
46+
$this->assertSame($matches, IpUtils::checkIp($remoteAddr, $cidr));
47+
}
48+
49+
public function testIpv6Provider()
50+
{
51+
return array(
52+
array(true, '2a01:198:603:0:396e:4789:8e99:890f', '2a01:198:603:0::/65'),
53+
array(false, '2a00:198:603:0:396e:4789:8e99:890f', '2a01:198:603:0::/65'),
54+
array(false, '2a01:198:603:0:396e:4789:8e99:890f', '::1'),
55+
array(true, '0:0:0:0:0:0:0:1', '::1'),
56+
array(false, '0:0:603:0:396e:4789:8e99:0001', '::1'),
57+
);
58+
}
59+
60+
/**
61+
* @expectedException \RuntimeException
62+
*/
63+
public function testAnIpv6WithOptionDisabledIpv6()
64+
{
65+
if (defined('AF_INET6')) {
66+
$this->markTestSkipped('Only works when PHP is compiled with the option "disable-ipv6".');
67+
}
68+
69+
IpUtils::checkIp('2a01:198:603:0:396e:4789:8e99:890f', '2a01:198:603:0::/65');
70+
}
71+
}

src/Symfony/Component/HttpFoundation/Tests/RequestMatcherTest.php

Lines changed: 0 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -16,78 +16,6 @@
1616

1717
class RequestMatcherTest extends \PHPUnit_Framework_TestCase
1818
{
19-
/**
20-
* @dataProvider testIpv4Provider
21-
*/
22-
public function testIpv4($matches, $remoteAddr, $cidr)
23-
{
24-
$request = Request::create('', 'get', array(), array(), array(), array('REMOTE_ADDR' => $remoteAddr));
25-
26-
$matcher = new RequestMatcher();
27-
$matcher->matchIp($cidr);
28-
29-
$this->assertEquals($matches, $matcher->matches($request));
30-
}
31-
32-
public function testIpv4Provider()
33-
{
34-
return array(
35-
array(true, '192.168.1.1', '192.168.1.1'),
36-
array(true, '192.168.1.1', '192.168.1.1/1'),
37-
array(true, '192.168.1.1', '192.168.1.0/24'),
38-
array(false, '192.168.1.1', '1.2.3.4/1'),
39-
array(false, '192.168.1.1', '192.168.1/33'),
40-
);
41-
}
42-
43-
/**
44-
* @dataProvider testIpv6Provider
45-
*/
46-
public function testIpv6($matches, $remoteAddr, $cidr)
47-
{
48-
if (!defined('AF_INET6')) {
49-
$this->markTestSkipped('Only works when PHP is compiled without the option "disable-ipv6".');
50-
}
51-
52-
$request = Request::create('', 'get', array(), array(), array(), array('REMOTE_ADDR' => $remoteAddr));
53-
54-
$matcher = new RequestMatcher();
55-
$matcher->matchIp($cidr);
56-
57-
$this->assertEquals($matches, $matcher->matches($request));
58-
}
59-
60-
public function testIpv6Provider()
61-
{
62-
return array(
63-
array(true, '2a01:198:603:0:396e:4789:8e99:890f', '2a01:198:603:0::/65'),
64-
array(false, '2a00:198:603:0:396e:4789:8e99:890f', '2a01:198:603:0::/65'),
65-
array(false, '2a01:198:603:0:396e:4789:8e99:890f', '::1'),
66-
array(true, '0:0:0:0:0:0:0:1', '::1'),
67-
array(false, '0:0:603:0:396e:4789:8e99:0001', '::1'),
68-
);
69-
}
70-
71-
public function testAnIpv6WithOptionDisabledIpv6()
72-
{
73-
if (defined('AF_INET6')) {
74-
$this->markTestSkipped('Only works when PHP is compiled with the option "disable-ipv6".');
75-
}
76-
77-
$request = Request::create('', 'get', array(), array(), array(), array('REMOTE_ADDR' => '2a01:198:603:0:396e:4789:8e99:890f'));
78-
79-
$matcher = new RequestMatcher();
80-
$matcher->matchIp('2a01:198:603:0::/65');
81-
82-
try {
83-
$matcher->matches($request);
84-
85-
$this->fail('An expected RuntimeException has not been raised.');
86-
} catch (\Exception $e) {
87-
$this->assertInstanceOf('\RuntimeException', $e);
88-
}
89-
}
90-
9119
/**
9220
* @dataProvider testMethodFixtures
9321
*/
@@ -200,4 +128,3 @@ public function testAttributes()
200128
$this->assertFalse($matcher->matches($request));
201129
}
202130
}
203-

0 commit comments

Comments
 (0)
0