xref: /dokuwiki/_test/tests/inc/IpTest.php (revision 40981bcc82357fbbd1b690d99568e7e7a022a5c0)
17caad012SAndreas Gohr<?php
27caad012SAndreas Gohr
37caad012SAndreas Gohrnamespace dokuwiki\test;
47caad012SAndreas Gohr
57caad012SAndreas Gohruse dokuwiki\Input\Input;
67caad012SAndreas Gohruse dokuwiki\Ip;
77caad012SAndreas Gohr
87caad012SAndreas Gohrclass IpTest extends \DokuWikiTest {
97caad012SAndreas Gohr
107caad012SAndreas Gohr    /**
117caad012SAndreas Gohr     * The data provider for ipToNumber() tests.
127caad012SAndreas Gohr     *
137caad012SAndreas Gohr     * @return mixed[][] Returns an array of test cases.
147caad012SAndreas Gohr     */
157caad012SAndreas Gohr    public function ip_to_number_provider() : array
167caad012SAndreas Gohr    {
177caad012SAndreas Gohr        $tests = [
187caad012SAndreas Gohr            ['127.0.0.1', 4, 0x00000000, 0x7f000001],
197caad012SAndreas Gohr            ['::127.0.0.1', 6, 0x00000000, 0x7f000001],
207caad012SAndreas Gohr            ['::1', 6, 0x00000000, 0x00000001],
217caad012SAndreas Gohr            ['38AF:3033:AA39:CDE3:1A46:094C:44ED:5300', 6, 0x38AF3033AA39CDE3, 0x1A46094C44ED5300],
22e7cd6878SWillForan            ['0000:0000:0000:0000:0000:0000:0000:0000', 6, 0, 0],
237caad012SAndreas Gohr            ['193.53.125.7', 4, 0x00000000, 0xC1357D07],
242f70db90SWillForan            // NOTE: wrap around! 0xFFFFFFFFFFFFFFFE seen as -2
252f70db90SWillForan            ['7FFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFE', 6, 0x7FFFFFFFFFFFFFFF, -2],
267caad012SAndreas Gohr        ];
277caad012SAndreas Gohr
282f70db90SWillForan        // float loses percision and give confusing test results!
292f70db90SWillForan        // sprintf("%.0f",0x7FFFFFFFFFFFFFFF) == sprintf("%.0f",0x7FFFFFFFFFFFFF00)
302f70db90SWillForan        // using string of decimal values instead
312f70db90SWillForan        if(PHP_INT_SIZE == 4) {
322f70db90SWillForan            $tests = [
332f70db90SWillForan                ['127.0.0.1', 4, '0', '2130706433'],
342f70db90SWillForan                ['::127.0.0.1', 6, '0','2130706433'],
352f70db90SWillForan                ['::1', 6, '0', '1'],
362f70db90SWillForan                ['0000:0000:0000:0000:0000:0000:0000:0000', 6, '0', '0'],
372f70db90SWillForan                ['193.53.125.7', 4, '0', '3241508103'],
382f70db90SWillForan                ['7FFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFE', 6, '9223372036854775807', '18446744073709551614'],
392f70db90SWillForan                ['38AF:3033:AA39:CDE3:1A46:094C:44ED:5300', 6, '4084536385505709539', '1893210916534440704']
402f70db90SWillForan            ];
412f70db90SWillForan        }
427caad012SAndreas Gohr        return $tests;
437caad012SAndreas Gohr    }
447caad012SAndreas Gohr
457caad012SAndreas Gohr    /**
467caad012SAndreas Gohr     * Test ipToNumber().
477caad012SAndreas Gohr     *
487caad012SAndreas Gohr     * @dataProvider ip_to_number_provider
497caad012SAndreas Gohr     *
507caad012SAndreas Gohr     * @param string $ip The IP address to convert.
517caad012SAndreas Gohr     * @param int    $version The IP version, either 4 or 6.
527caad012SAndreas Gohr     * @param int    $upper   The upper 64 bits of the IP.
537caad012SAndreas Gohr     * @param int    $lower   The lower 64 bits of the IP.
547caad012SAndreas Gohr     *
557caad012SAndreas Gohr     * @return void
562f70db90SWillForan     *
572f70db90SWillForan     * Note: $upper and $lower are likley 'float' instead of 'int' for large values.
582f70db90SWillForan     * The more accurate hint 'int|float' is not supported in php7.4.
597caad012SAndreas Gohr     */
602f70db90SWillForan    public function test_ip_to_number(string $ip, int $version, $upper, $lower): void
617caad012SAndreas Gohr    {
627caad012SAndreas Gohr        $result = Ip::ipToNumber($ip);
637caad012SAndreas Gohr
642f70db90SWillForan        // force output of ipv4 to string for easy compare
652f70db90SWillForan        if(PHP_INT_SIZE == 4 and !is_string($result['upper']) ) {
662f70db90SWillForan            $result['upper'] = sprintf("%.0f", $result['upper']);
672f70db90SWillForan            $result['lower'] = sprintf("%.0f", $result['lower']);
68e7cd6878SWillForan        }
69e7cd6878SWillForan
707caad012SAndreas Gohr        $this->assertSame($version, $result['version']);
717caad012SAndreas Gohr        $this->assertSame($upper, $result['upper']);
727caad012SAndreas Gohr        $this->assertSame($lower, $result['lower']);
737caad012SAndreas Gohr    }
747caad012SAndreas Gohr
757caad012SAndreas Gohr    /**
767caad012SAndreas Gohr     * The data provider for test_ip_in_range().
777caad012SAndreas Gohr     *
787caad012SAndreas Gohr     * @return mixed[][] Returns an array of test cases.
797caad012SAndreas Gohr     */
807caad012SAndreas Gohr    public function ip_in_range_provider(): array
817caad012SAndreas Gohr    {
827caad012SAndreas Gohr        $tests = [
837caad012SAndreas Gohr            ['192.168.11.2', '192.168.0.0/16', true],
847caad012SAndreas Gohr            ['192.168.11.2', '192.168.64.1/16', true],
857caad012SAndreas Gohr            ['192.168.11.2', '192.168.64.1/18', false],
867caad012SAndreas Gohr            ['192.168.11.2', '192.168.11.0/20', true],
877caad012SAndreas Gohr            ['127.0.0.1', '127.0.0.0/7', true],
887caad012SAndreas Gohr            ['127.0.0.1', '127.0.0.0/8', true],
897caad012SAndreas Gohr            ['127.0.0.1', '127.200.0.0/8', true],
907caad012SAndreas Gohr            ['127.0.0.1', '127.200.0.0/9', false],
917caad012SAndreas Gohr            ['127.0.0.1', '127.0.0.0/31', true],
927caad012SAndreas Gohr            ['127.0.0.1', '127.0.0.0/32', false],
937caad012SAndreas Gohr            ['127.0.0.1', '127.0.0.1/32', true],
947caad012SAndreas Gohr            ['1111:2222:3333:4444:5555:6666:7777:8888', '1110::/12', true],
957caad012SAndreas Gohr            ['1110:2222:3333:4444:5555:6666:7777:8888', '1110::/12', true],
967caad012SAndreas Gohr            ['1100:2222:3333:4444:5555:6666:7777:8888', '1110::/12', false],
977caad012SAndreas Gohr            ['1111:2222:3333:4444:5555:6666:7777:8888', '1111:2222:3300::/40', true],
987caad012SAndreas Gohr            ['1111:2222:3333:4444:5555:6666:7777:8888', '1111:2222:3200::/40', false],
997caad012SAndreas Gohr            ['1111:2222:3333:4444:5555:6666:7777:8888', '1111:2222:3333:4444:5555:6666:7777:8889/127', true],
1007caad012SAndreas Gohr            ['1111:2222:3333:4444:5555:6666:7777:8888', '1111:2222:3333:4444:5555:6666:7777:8889/128', false],
1017caad012SAndreas Gohr            ['1111:2222:3333:4444:5555:6666:7777:8889', '1111:2222:3333:4444:5555:6666:7777:8889/128', true],
1027caad012SAndreas Gohr            ['abcd:ef0a:bcde:f0ab:cdef:0abc:def0:abcd', 'abcd:ef0a:bcde:f0ab:cdef:0abc:def0:abcd/128', true],
1037caad012SAndreas Gohr            ['abcd:ef0a:bcde:f0ab:cdef:0abc:def0:abce', 'abcd:ef0a:bcde:f0ab:cdef:0abc:def0:abcd/128', false],
1047caad012SAndreas Gohr        ];
1057caad012SAndreas Gohr
1067caad012SAndreas Gohr        return $tests;
1077caad012SAndreas Gohr    }
1087caad012SAndreas Gohr
1097caad012SAndreas Gohr    /**
1107caad012SAndreas Gohr     * Test ipInRange().
1117caad012SAndreas Gohr     *
1127caad012SAndreas Gohr     * @dataProvider ip_in_range_provider
1137caad012SAndreas Gohr     *
1147caad012SAndreas Gohr     * @param string $ip The IP to test.
1157caad012SAndreas Gohr     * @param string $range The IP range to test against.
1167caad012SAndreas Gohr     * @param bool $expected The expected result from ipInRange().
1177caad012SAndreas Gohr     *
1187caad012SAndreas Gohr     * @return void
1197caad012SAndreas Gohr     */
1207caad012SAndreas Gohr    public function test_ip_in_range(string $ip, string $range, bool $expected): void
1217caad012SAndreas Gohr    {
1227caad012SAndreas Gohr        $result = Ip::ipInRange($ip, $range);
1237caad012SAndreas Gohr
1247caad012SAndreas Gohr        $this->assertSame($expected, $result);
1257caad012SAndreas Gohr    }
1267caad012SAndreas Gohr
1277caad012SAndreas Gohr    /**
128*40981bccSAndreas Gohr     * The data provider for test_ip_in_range_invalid_mask().
129*40981bccSAndreas Gohr     *
130*40981bccSAndreas Gohr     * @return string[][] Returns an array of [ip, range] with an invalid mask.
131*40981bccSAndreas Gohr     */
132*40981bccSAndreas Gohr    public function ip_in_range_invalid_mask_provider(): array
133*40981bccSAndreas Gohr    {
134*40981bccSAndreas Gohr        return [
135*40981bccSAndreas Gohr            // Non-numeric mask: would throw a TypeError on the "+= 96" IPv4 path.
136*40981bccSAndreas Gohr            ['127.0.0.1', '10.0.0.0/abc'],
137*40981bccSAndreas Gohr            // Empty mask.
138*40981bccSAndreas Gohr            ['127.0.0.1', '10.0.0.0/'],
139*40981bccSAndreas Gohr            // Negative mask: would pass the "> 128" check and match every IPv4.
140*40981bccSAndreas Gohr            ['1.2.3.4', '10.0.0.0/-1'],
141*40981bccSAndreas Gohr            ['1.2.3.4', '10.0.0.0/-5'],
142*40981bccSAndreas Gohr        ];
143*40981bccSAndreas Gohr    }
144*40981bccSAndreas Gohr
145*40981bccSAndreas Gohr    /**
146*40981bccSAndreas Gohr     * Test that ipInRange() rejects an invalid mask with an Exception rather
147*40981bccSAndreas Gohr     * than a TypeError or an over-broad match.
148*40981bccSAndreas Gohr     *
149*40981bccSAndreas Gohr     * @dataProvider ip_in_range_invalid_mask_provider
150*40981bccSAndreas Gohr     *
151*40981bccSAndreas Gohr     * @param string $ip    The IP to test.
152*40981bccSAndreas Gohr     * @param string $range The IP range with the invalid mask.
153*40981bccSAndreas Gohr     *
154*40981bccSAndreas Gohr     * @return void
155*40981bccSAndreas Gohr     */
156*40981bccSAndreas Gohr    public function test_ip_in_range_invalid_mask(string $ip, string $range): void
157*40981bccSAndreas Gohr    {
158*40981bccSAndreas Gohr        $this->expectException(\Exception::class);
159*40981bccSAndreas Gohr        Ip::ipInRange($ip, $range);
160*40981bccSAndreas Gohr    }
161*40981bccSAndreas Gohr
162*40981bccSAndreas Gohr    /**
1637caad012SAndreas Gohr     * Data provider for test_ip_matches().
1647caad012SAndreas Gohr     *
1657caad012SAndreas Gohr     * @return mixed[][] Returns an array of test cases.
1667caad012SAndreas Gohr     */
1677caad012SAndreas Gohr    public function ip_matches_provider(): array
1687caad012SAndreas Gohr    {
1697caad012SAndreas Gohr        // Tests for a CIDR range.
1707caad012SAndreas Gohr        $rangeTests = $this->ip_in_range_provider();
1717caad012SAndreas Gohr
1727caad012SAndreas Gohr        // Tests for an exact IP match.
1737caad012SAndreas Gohr        $exactTests = [
1747caad012SAndreas Gohr            ['127.0.0.1', '127.0.0.1', true],
1757caad012SAndreas Gohr            ['127.0.0.1', '127.0.0.0', false],
1767caad012SAndreas Gohr            ['aaaa:bbbb:cccc:dddd:eeee::', 'aaaa:bbbb:cccc:dddd:eeee:0000:0000:0000', true],
1777caad012SAndreas Gohr            ['aaaa:bbbb:cccc:dddd:eeee:0000:0000:0000', 'aaaa:bbbb:cccc:dddd:eeee::', true],
1787caad012SAndreas Gohr            ['aaaa:bbbb:0000:0000:0000:0000:0000:0001', 'aaaa:bbbb::1', true],
1797caad012SAndreas Gohr            ['aaaa:bbbb::0001', 'aaaa:bbbb::1', true],
1807caad012SAndreas Gohr            ['aaaa:bbbb::0001', 'aaaa:bbbb::', false],
1817caad012SAndreas Gohr            ['::ffff:127.0.0.1', '127.0.0.1', false],
1827caad012SAndreas Gohr            ['::ffff:127.0.0.1', '::0:ffff:127.0.0.1', true],
1837caad012SAndreas Gohr        ];
1847caad012SAndreas Gohr
185*40981bccSAndreas Gohr        // Invalid masks must degrade to "no match" instead of a fatal error
186*40981bccSAndreas Gohr        // (non-numeric mask) or an over-broad match (negative mask).
187*40981bccSAndreas Gohr        $invalidMaskTests = [
188*40981bccSAndreas Gohr            ['127.0.0.1', '10.0.0.0/abc', false],
189*40981bccSAndreas Gohr            ['127.0.0.1', '10.0.0.0/', false],
190*40981bccSAndreas Gohr            ['1.2.3.4', '10.0.0.0/-1', false],
191*40981bccSAndreas Gohr            ['1.2.3.4', '10.0.0.0/-5', false],
192*40981bccSAndreas Gohr        ];
1937caad012SAndreas Gohr
194*40981bccSAndreas Gohr
195*40981bccSAndreas Gohr        return array_merge($rangeTests, $exactTests, $invalidMaskTests);
1967caad012SAndreas Gohr    }
1977caad012SAndreas Gohr
1987caad012SAndreas Gohr    /**
1997caad012SAndreas Gohr     * Test ipMatches().
2007caad012SAndreas Gohr     *
2017caad012SAndreas Gohr     * @dataProvider ip_matches_provider
2027caad012SAndreas Gohr     *
2037caad012SAndreas Gohr     * @param string $ip        The IP to test.
2047caad012SAndreas Gohr     * @param string $ipOrRange The IP or IP range to test against.
2057caad012SAndreas Gohr     * @param bool   $expected  The expeced result from ipMatches().
2067caad012SAndreas Gohr     *
2077caad012SAndreas Gohr     * @return void
2087caad012SAndreas Gohr     */
2097caad012SAndreas Gohr    public function test_ip_matches(string $ip, string $ipOrRange, bool $expected): void
2107caad012SAndreas Gohr    {
2117caad012SAndreas Gohr        $result = Ip::ipMatches($ip, $ipOrRange);
2127caad012SAndreas Gohr
2137caad012SAndreas Gohr        $this->assertSame($expected, $result);
2147caad012SAndreas Gohr    }
2157caad012SAndreas Gohr
2167caad012SAndreas Gohr    /**
2177caad012SAndreas Gohr     * Data provider for proxyIsTrusted().
2187caad012SAndreas Gohr     *
2197caad012SAndreas Gohr     * @return mixed[][] Returns an array of test cases.
2207caad012SAndreas Gohr     */
2217caad012SAndreas Gohr    public function proxy_is_trusted_provider(): array
2227caad012SAndreas Gohr    {
2237caad012SAndreas Gohr        // The new default configuration value.
2247caad012SAndreas Gohr        $default = ['::1', 'fe80::/10', '127.0.0.0/8', '10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'];
2257caad012SAndreas Gohr
2267caad012SAndreas Gohr        // Adding some custom trusted proxies.
2277caad012SAndreas Gohr        $custom = array_merge($default, ['1.2.3.4', '1122::', '3.0.0.1/8', '1111:2222::/32']);
2287caad012SAndreas Gohr
2297caad012SAndreas Gohr        $tests = [
2307caad012SAndreas Gohr            // Empty configuration.
2317caad012SAndreas Gohr            ['', '127.0.0.1', false],
2327caad012SAndreas Gohr
2337caad012SAndreas Gohr            // Configuration with an array of  IPs/CIDRs.
2347caad012SAndreas Gohr            [$default, '127.0.0.1', true],
2357caad012SAndreas Gohr            [$default, '127.1.2.3', true],
2367caad012SAndreas Gohr            [$default, '10.1.2.3', true],
2377caad012SAndreas Gohr            [$default, '11.1.2.3', false],
2387caad012SAndreas Gohr            [$default, '172.16.0.1', true],
2397caad012SAndreas Gohr            [$default, '172.160.0.1', false],
2407caad012SAndreas Gohr            [$default, '172.31.255.255', true],
2417caad012SAndreas Gohr            [$default, '172.32.0.0', false],
2427caad012SAndreas Gohr            [$default, '172.200.0.0', false],
2437caad012SAndreas Gohr            [$default, '192.168.2.3', true],
2447caad012SAndreas Gohr            [$default, '192.169.1.2', false],
2457caad012SAndreas Gohr            [$default, '::1', true],
2467caad012SAndreas Gohr            [$default, '0000:0000:0000:0000:0000:0000:0000:0001', true],
2477caad012SAndreas Gohr
2487caad012SAndreas Gohr            // With custom proxies set.
2497caad012SAndreas Gohr            [$custom, '127.0.0.1', true],
2507caad012SAndreas Gohr            [$custom, '1.2.3.4', true],
2517caad012SAndreas Gohr            [$custom, '3.0.1.2', true],
2527caad012SAndreas Gohr            [$custom, '1122::', true],
2537caad012SAndreas Gohr            [$custom, '1122:0000:0000:0000:0000:0000:0000:0000', true],
2547caad012SAndreas Gohr            [$custom, '1111:2223::', false],
2557caad012SAndreas Gohr            [$custom, '1111:2222::', true],
2567caad012SAndreas Gohr            [$custom, '1111:2222:3333::', true],
2577caad012SAndreas Gohr            [$custom, '1111:2222:3333::1', true],
2587caad012SAndreas Gohr        ];
2597caad012SAndreas Gohr
2607caad012SAndreas Gohr        return $tests;
2617caad012SAndreas Gohr    }
2627caad012SAndreas Gohr
2637caad012SAndreas Gohr    /**
2647caad012SAndreas Gohr     * Test proxyIsTrusted().
2657caad012SAndreas Gohr     *
2667caad012SAndreas Gohr     * @dataProvider proxy_is_trusted_provider
2677caad012SAndreas Gohr     *
2687caad012SAndreas Gohr     * @param string|string[] $config   The value for $conf[trustedproxies].
2697caad012SAndreas Gohr     * @param string          $ip       The proxy IP to test.
2707caad012SAndreas Gohr     * @param bool            $expected The expected result from proxyIsTrusted().
2717caad012SAndreas Gohr     */
2727caad012SAndreas Gohr    public function test_proxy_is_trusted($config, string $ip, bool $expected): void
2737caad012SAndreas Gohr    {
2747caad012SAndreas Gohr        global $conf;
2757caad012SAndreas Gohr        $conf['trustedproxies'] = $config;
2767caad012SAndreas Gohr
2777caad012SAndreas Gohr        $result = Ip::proxyIsTrusted($ip);
2787caad012SAndreas Gohr
2797caad012SAndreas Gohr        $this->assertSame($expected, $result);
2807caad012SAndreas Gohr    }
2817caad012SAndreas Gohr
2827caad012SAndreas Gohr    /**
2837caad012SAndreas Gohr     * Data provider for test_forwarded_for().
2847caad012SAndreas Gohr     *
2857caad012SAndreas Gohr     * @return mixed[][] Returns an array of test cases.
2867caad012SAndreas Gohr     */
2877caad012SAndreas Gohr    public function forwarded_for_provider(): array
2887caad012SAndreas Gohr    {
2897caad012SAndreas Gohr        // The new default configuration value.
2907caad012SAndreas Gohr        $default = ['::1', 'fe80::/10', '127.0.0.0/8', '10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'];
2917caad012SAndreas Gohr
2927caad012SAndreas Gohr        // Adding some custom trusted proxies.
2937caad012SAndreas Gohr        $custom = array_merge($default, ['1.2.3.4', '1122::', '3.0.0.1/8', '1111:2222::/32']);
2947caad012SAndreas Gohr
2957caad012SAndreas Gohr        $tests = [
2967caad012SAndreas Gohr            // Empty config value should always return empty array.
2977caad012SAndreas Gohr            [[], '', '127.0.0.1', []],
2987caad012SAndreas Gohr            [[], '127.0.0.1', '127.0.0.1', []],
2997caad012SAndreas Gohr
3007caad012SAndreas Gohr            // The new default configuration.
3017caad012SAndreas Gohr            [$default, '', '127.0.0.1', []],
3027caad012SAndreas Gohr            [$default, '1.2.3.4', '127.0.0.1', ['1.2.3.4', '127.0.0.1']],
3037caad012SAndreas Gohr            [$default, '1.2.3.4', '192.168.1.1', ['1.2.3.4', '192.168.1.1']],
3047caad012SAndreas Gohr            [$default, '1.2.3.4,172.16.0.1', '192.168.1.1', ['1.2.3.4', '172.16.0.1', '192.168.1.1']],
3057caad012SAndreas Gohr            [$default, '1.2.3.4,172.16.0.1', '::1', ['1.2.3.4', '172.16.0.1', '::1']],
3067caad012SAndreas Gohr            [$default, '1.2.3.4,172.16.0.1', '::0001', ['1.2.3.4', '172.16.0.1', '::0001']],
3077caad012SAndreas Gohr
3087caad012SAndreas Gohr            // Directly from an untrusted proxy.
3097caad012SAndreas Gohr            [$default, '', '127.0.0.1', []],
3107caad012SAndreas Gohr            [$default, '1.2.3.4', '11.22.33.44', []],
3117caad012SAndreas Gohr            [$default, '::1', '11.22.33.44', []],
3127caad012SAndreas Gohr            [$default, '::1', '::2', []],
3137caad012SAndreas Gohr
3147caad012SAndreas Gohr            // From a trusted proxy, but via an untrusted proxy.
3157caad012SAndreas Gohr            [$default, '1.2.3.4,11.22.33.44,172.16.0.1', '192.168.1.1', []],
3167caad012SAndreas Gohr            [$default, '1.2.3.4,::2,172.16.0.1', '::1', []],
3177caad012SAndreas Gohr
3187caad012SAndreas Gohr            // A custom configuration.
3197caad012SAndreas Gohr            [$custom, '', '127.0.0.1', []],
3207caad012SAndreas Gohr            [$custom, '1.2.3.4', '127.0.0.1', ['1.2.3.4', '127.0.0.1']],
3217caad012SAndreas Gohr            [$custom, '1.2.3.4', '192.168.1.1', ['1.2.3.4', '192.168.1.1']],
3227caad012SAndreas Gohr            [$custom, '1.2.3.4,172.16.0.1', '192.168.1.1', ['1.2.3.4', '172.16.0.1', '192.168.1.1']],
3237caad012SAndreas Gohr            [$custom, '1.2.3.4,172.16.0.1', '::1', ['1.2.3.4', '172.16.0.1', '::1']],
3247caad012SAndreas Gohr            [$custom, '1.2.3.4,172.16.0.1', '::0001', ['1.2.3.4', '172.16.0.1', '::0001']],
3257caad012SAndreas Gohr
3267caad012SAndreas Gohr            // Directly from an untrusted proxy.
3277caad012SAndreas Gohr            [$custom, '', '127.0.0.1', []],
3287caad012SAndreas Gohr            [$custom, '1.2.3.4', '11.22.33.44', []],
3297caad012SAndreas Gohr            [$custom, '::1', '11.22.33.44', []],
3307caad012SAndreas Gohr            [$custom, '::1', '::2', []],
3317caad012SAndreas Gohr
3327caad012SAndreas Gohr            // From a trusted proxy, but via an untrusted proxy.
3337caad012SAndreas Gohr            [$custom, '1.2.3.4,11.22.33.44,172.16.0.1', '192.168.1.1', []],
3347caad012SAndreas Gohr            [$custom, '1.2.3.4,::2,172.16.0.1', '::1', []],
3357caad012SAndreas Gohr
3367caad012SAndreas Gohr            // Via a custom proxy.
3377caad012SAndreas Gohr            [$custom, '11.2.3.4,3.1.2.3,172.16.0.1', '192.168.1.1', ['11.2.3.4', '3.1.2.3', '172.16.0.1', '192.168.1.1']],
3387caad012SAndreas Gohr            [$custom, '11.2.3.4,1122::,172.16.0.1', '3.0.0.1', ['11.2.3.4', '1122::', '172.16.0.1', '3.0.0.1']],
3397caad012SAndreas Gohr            [$custom, '11.2.3.4,1122::,172.16.0.1', '1111:2222:3333::', ['11.2.3.4', '1122::', '172.16.0.1', '1111:2222:3333::']],
3407caad012SAndreas Gohr        ];
3417caad012SAndreas Gohr
3427caad012SAndreas Gohr        return $tests;
3437caad012SAndreas Gohr    }
3447caad012SAndreas Gohr
3457caad012SAndreas Gohr    /**
3467caad012SAndreas Gohr     * Test forwardedFor().
3477caad012SAndreas Gohr     *
3487caad012SAndreas Gohr     * @dataProvider forwarded_for_provider
3497caad012SAndreas Gohr     *
3507caad012SAndreas Gohr     * @param string|string[] $config     The trustedproxies config value.
3517caad012SAndreas Gohr     * @param string          $header     The X-Forwarded-For header value.
3527caad012SAndreas Gohr     * @param string          $remoteAddr The TCP/IP peer address.
3537caad012SAndreas Gohr     * @param array           $expected   The expected result from forwardedFor().
3547caad012SAndreas Gohr     *
3557caad012SAndreas Gohr     * @return void
3567caad012SAndreas Gohr     */
3577caad012SAndreas Gohr    public function test_forwarded_for($config, string $header, string $remoteAddr, array $expected): void
3587caad012SAndreas Gohr    {
3597caad012SAndreas Gohr        /* @var Input $INPUT */
3607caad012SAndreas Gohr        global $INPUT, $conf;
3617caad012SAndreas Gohr
3627caad012SAndreas Gohr        $conf['trustedproxies'] = $config;
3637caad012SAndreas Gohr        $INPUT->server->set('HTTP_X_FORWARDED_FOR', $header);
3647caad012SAndreas Gohr        $INPUT->server->set('REMOTE_ADDR', $remoteAddr);
3657caad012SAndreas Gohr
3667caad012SAndreas Gohr        $result = Ip::forwardedFor();
3677caad012SAndreas Gohr
3687caad012SAndreas Gohr        $this->assertSame($expected, $result);
3697caad012SAndreas Gohr    }
3707caad012SAndreas Gohr
3717caad012SAndreas Gohr    /**
3727caad012SAndreas Gohr     * Data provider for test_is_ssl().
3737caad012SAndreas Gohr     *
3747caad012SAndreas Gohr     * @return mixed[][] Returns an array of test cases.
3757caad012SAndreas Gohr     */
3767caad012SAndreas Gohr    public function is_ssl_provider(): array
3777caad012SAndreas Gohr    {
3787caad012SAndreas Gohr        // The new default configuration value.
3797caad012SAndreas Gohr        $default = ['::1', 'fe80::/10', '127.0.0.0/8', '10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'];
3807caad012SAndreas Gohr
3817caad012SAndreas Gohr        $tests = [
3827caad012SAndreas Gohr            // Running behind an SSL proxy, HTTP between server and proxy
3837caad012SAndreas Gohr            // Proxy (REMOTE_ADDR) is matched by trustedproxies config
3847caad012SAndreas Gohr            // HTTPS not set, HTTP_X_FORWARDED_PROTO set to https
3857caad012SAndreas Gohr            [$default, '127.0.0.1', '', 'https', true],
3867caad012SAndreas Gohr
3877caad012SAndreas Gohr            // Running behind an SSL proxy, HTTP between server and proxy
3887caad012SAndreas Gohr            // Proxy (REMOTE_ADDR) is not matched by trustedproxies config
3897caad012SAndreas Gohr            // HTTPS not set, HTTP_X_FORWARDED_PROTO set to https
3907caad012SAndreas Gohr            [[], '8.8.8.8', '', 'https', false],
3917caad012SAndreas Gohr
3927caad012SAndreas Gohr            // Running behind a plain HTTP proxy, HTTP between server and proxy
3937caad012SAndreas Gohr            // HTTPS not set, HTTP_X_FORWARDED_PROTO set to http
3947caad012SAndreas Gohr            [$default, '127.0.0.1', '', 'http', false],
3957caad012SAndreas Gohr
3967caad012SAndreas Gohr            // Running behind an SSL proxy, HTTP between server and proxy
3977caad012SAndreas Gohr            // HTTPS set to off, HTTP_X_FORWARDED_PROTO set to https
3987caad012SAndreas Gohr            [$default, '127.0.0.1', 'off', 'https', true],
3997caad012SAndreas Gohr
4007caad012SAndreas Gohr            // Not running behind a proxy, HTTPS server
4017caad012SAndreas Gohr            // HTTPS set to on, HTTP_X_FORWARDED_PROTO not set
4027caad012SAndreas Gohr            [[], '8.8.8.8', 'on', '', true],
4037caad012SAndreas Gohr
4047caad012SAndreas Gohr            // Not running behind a proxy, plain HTTP server
4057caad012SAndreas Gohr            // HTTPS not set, HTTP_X_FORWARDED_PROTO not set
4067caad012SAndreas Gohr            [[], '8.8.8.8', '', '', false],
4077caad012SAndreas Gohr
4087caad012SAndreas Gohr            // Not running behind a proxy, plain HTTP server
4097caad012SAndreas Gohr            // HTTPS set to off, HTTP_X_FORWARDED_PROTO not set
4107caad012SAndreas Gohr            [[], '8.8.8.8', 'off', '', false],
4117caad012SAndreas Gohr
4127caad012SAndreas Gohr            // Running behind an SSL proxy, SSL between proxy and HTTP server
4137caad012SAndreas Gohr            // HTTPS set to on, HTTP_X_FORWARDED_PROTO set to https
4147caad012SAndreas Gohr            [$default, '127.0.0.1', 'on', 'https', true],
4157caad012SAndreas Gohr        ];
4167caad012SAndreas Gohr
4177caad012SAndreas Gohr        return $tests;
4187caad012SAndreas Gohr    }
4197caad012SAndreas Gohr
4207caad012SAndreas Gohr    /**
4217caad012SAndreas Gohr     * Test isSsl().
4227caad012SAndreas Gohr     *
4237caad012SAndreas Gohr     * @dataProvider is_ssl_provider
4247caad012SAndreas Gohr     *
4257caad012SAndreas Gohr     * @param string|string[] $config           The trustedproxies config value.
4267caad012SAndreas Gohr     * @param string          $remoteAddr       The REMOTE_ADDR value.
4277caad012SAndreas Gohr     * @param string          $https            The HTTPS value.
4287caad012SAndreas Gohr     * @param string          $forwardedProto   The HTTP_X_FORWARDED_PROTO value.
4297caad012SAndreas Gohr     * @param bool            $expected         The expected result from isSsl().
4307caad012SAndreas Gohr     *
4317caad012SAndreas Gohr     * @return void
4327caad012SAndreas Gohr     */
4337caad012SAndreas Gohr    public function test_is_ssl($config, string $remoteAddr, string $https, string $forwardedProto, bool $expected): void
4347caad012SAndreas Gohr    {
4357caad012SAndreas Gohr        /* @var Input $INPUT */
4367caad012SAndreas Gohr        global $INPUT, $conf;
4377caad012SAndreas Gohr
4387caad012SAndreas Gohr        $conf['trustedproxies'] = $config;
4397caad012SAndreas Gohr        $INPUT->server->set('REMOTE_ADDR', $remoteAddr);
4407caad012SAndreas Gohr        $INPUT->server->set('HTTPS', $https);
4417caad012SAndreas Gohr        $INPUT->server->set('HTTP_X_FORWARDED_PROTO', $forwardedProto);
4427caad012SAndreas Gohr
4437caad012SAndreas Gohr        $result = Ip::isSsl();
4447caad012SAndreas Gohr
4457caad012SAndreas Gohr        $this->assertSame($expected, $result);
4467caad012SAndreas Gohr    }
4477caad012SAndreas Gohr
4487caad012SAndreas Gohr    /**
4497caad012SAndreas Gohr     * Data provider for test_host_name().
4507caad012SAndreas Gohr     *
4517caad012SAndreas Gohr     * @return mixed[][] Returns an array of test cases.
4527caad012SAndreas Gohr     */
4537caad012SAndreas Gohr    public function host_name_provider(): array
4547caad012SAndreas Gohr    {
4557caad012SAndreas Gohr        // The new default configuration value.
4567caad012SAndreas Gohr        $default = ['::1', 'fe80::/10', '127.0.0.0/8', '10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'];
4577caad012SAndreas Gohr
4587caad012SAndreas Gohr        $tests = [
4597caad012SAndreas Gohr            // X-Forwarded-Host with trusted proxy
4607caad012SAndreas Gohr            [$default, '127.0.0.1', 'proxy.example.com', 'www.example.com', 'server.local', 'proxy.example.com'],
4617caad012SAndreas Gohr
4627caad012SAndreas Gohr            // X-Forwarded-Host with untrusted proxy (should fall back to HTTP_HOST)
4637caad012SAndreas Gohr            [[], '8.8.8.8', 'proxy.example.com', 'www.example.com', 'server.local', 'www.example.com'],
4647caad012SAndreas Gohr
4657caad012SAndreas Gohr            // No X-Forwarded-Host, use HTTP_HOST
4667caad012SAndreas Gohr            [$default, '127.0.0.1', '', 'www.example.com', 'server.local', 'www.example.com'],
4677caad012SAndreas Gohr
4687caad012SAndreas Gohr            // No X-Forwarded-Host or HTTP_HOST, use SERVER_NAME
4697caad012SAndreas Gohr            [$default, '127.0.0.1', '', '', 'server.local', 'server.local'],
4707caad012SAndreas Gohr
4717caad012SAndreas Gohr            // No headers set, should fall back to system hostname
4727caad012SAndreas Gohr            [$default, '127.0.0.1', '', '', '', php_uname('n')],
4737caad012SAndreas Gohr        ];
4747caad012SAndreas Gohr
4757caad012SAndreas Gohr        return $tests;
4767caad012SAndreas Gohr    }
4777caad012SAndreas Gohr
4787caad012SAndreas Gohr    /**
4797caad012SAndreas Gohr     * Test hostName().
4807caad012SAndreas Gohr     *
4817caad012SAndreas Gohr     * @dataProvider host_name_provider
4827caad012SAndreas Gohr     *
4837caad012SAndreas Gohr     * @param string|string[] $config           The trustedproxies config value.
4847caad012SAndreas Gohr     * @param string          $remoteAddr       The REMOTE_ADDR value.
4857caad012SAndreas Gohr     * @param string          $forwardedHost    The HTTP_X_FORWARDED_HOST value.
4867caad012SAndreas Gohr     * @param string          $httpHost         The HTTP_HOST value.
4877caad012SAndreas Gohr     * @param string          $serverName       The SERVER_NAME value.
4887caad012SAndreas Gohr     * @param string          $expected         The expected result from hostName().
4897caad012SAndreas Gohr     *
4907caad012SAndreas Gohr     * @return void
4917caad012SAndreas Gohr     */
4927caad012SAndreas Gohr    public function test_host_name($config, string $remoteAddr, string $forwardedHost, string $httpHost, string $serverName, string $expected): void
4937caad012SAndreas Gohr    {
4947caad012SAndreas Gohr        /* @var Input $INPUT */
4957caad012SAndreas Gohr        global $INPUT, $conf;
4967caad012SAndreas Gohr
4977caad012SAndreas Gohr        $conf['trustedproxies'] = $config;
4987caad012SAndreas Gohr        $INPUT->server->set('REMOTE_ADDR', $remoteAddr);
4997caad012SAndreas Gohr        $INPUT->server->set('HTTP_X_FORWARDED_HOST', $forwardedHost);
5007caad012SAndreas Gohr        $INPUT->server->set('HTTP_HOST', $httpHost);
5017caad012SAndreas Gohr        $INPUT->server->set('SERVER_NAME', $serverName);
5027caad012SAndreas Gohr
5037caad012SAndreas Gohr        $result = Ip::hostName();
5047caad012SAndreas Gohr
5057caad012SAndreas Gohr        $this->assertSame($expected, $result);
5067caad012SAndreas Gohr    }
5072b760c9fSAlexander Lehmann
5082b760c9fSAlexander Lehmann    /**
50990c2f6e3SAndreas Gohr     * Test custom client IP header configuration.
5102b760c9fSAlexander Lehmann     */
5118f178b70SAlexander Lehmann    public function client_ip_provider(): array
5122b760c9fSAlexander Lehmann    {
5138f178b70SAlexander Lehmann        return [
514fe6048ccSAlexander Lehmann            // client_ip_header disabled, X-Real-IP present -> use REMOTE_ADDR
515fe6048ccSAlexander Lehmann            [null, ['HTTP_X_REAL_IP' => '5.6.7.8', 'REMOTE_ADDR' => '1.2.3.4'], '1.2.3.4'],
5162b760c9fSAlexander Lehmann
51790c2f6e3SAndreas Gohr            // client_ip_header set to X_REAL_IP, X-Real-IP present -> use X-Real-IP
518fe6048ccSAlexander Lehmann            ['X_REAL_IP', ['HTTP_X_REAL_IP' => '5.6.7.8', 'REMOTE_ADDR' => '1.2.3.4'], '5.6.7.8'],
5192b760c9fSAlexander Lehmann
5208f178b70SAlexander Lehmann            // custom client_ip_header set to CF_CONNECTING_IP -> use CF header
521fe6048ccSAlexander Lehmann            ['CF_CONNECTING_IP', ['HTTP_CF_CONNECTING_IP' => '5.6.7.8', 'REMOTE_ADDR' => '1.2.3.4'], '5.6.7.8'],
5222b760c9fSAlexander Lehmann
5238f178b70SAlexander Lehmann            // client_ip_header set to X_REAL_IP but only CF header present -> fallback to REMOTE_ADDR
524fe6048ccSAlexander Lehmann            ['X_REAL_IP', ['HTTP_CF_CONNECTING_IP' => '5.6.7.8', 'REMOTE_ADDR' => '1.2.3.4'], '1.2.3.4'],
5258f178b70SAlexander Lehmann        ];
5262b760c9fSAlexander Lehmann    }
5272b760c9fSAlexander Lehmann
5288f178b70SAlexander Lehmann    /**
5298f178b70SAlexander Lehmann     * @dataProvider client_ip_provider
5308f178b70SAlexander Lehmann     */
531fe6048ccSAlexander Lehmann    public function test_client_ip($client_ip_header, array $server, string $expected): void
5322b760c9fSAlexander Lehmann    {
5332b760c9fSAlexander Lehmann        /* @var Input $INPUT */
5342b760c9fSAlexander Lehmann        global $INPUT, $conf;
5352b760c9fSAlexander Lehmann
5368f178b70SAlexander Lehmann        if ($client_ip_header !== null) {
5378f178b70SAlexander Lehmann            $conf['client_ip_header'] = $client_ip_header;
5388f178b70SAlexander Lehmann        } else {
5398f178b70SAlexander Lehmann            unset($conf['client_ip_header']);
5402b760c9fSAlexander Lehmann        }
5412b760c9fSAlexander Lehmann
5428f178b70SAlexander Lehmann        // Set provided header variables
5438f178b70SAlexander Lehmann        foreach ($server as $key => $value) {
5448f178b70SAlexander Lehmann            $INPUT->server->set($key, $value);
5452b760c9fSAlexander Lehmann        }
5462b760c9fSAlexander Lehmann
54790c2f6e3SAndreas Gohr        $result = Ip::clientIp();
5482b760c9fSAlexander Lehmann
5498f178b70SAlexander Lehmann        $this->assertSame($expected, $result);
5502b760c9fSAlexander Lehmann    }
5517caad012SAndreas Gohr}
552