IPUtils   A
last analyzed

Complexity

Total Complexity 24

Size/Duplication

Total Lines 124
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 45
dl 0
loc 124
rs 10
c 0
b 0
f 0
wmc 24

4 Methods

Rating   Name   Duplication   Size   Complexity  
A checkIP() 0 15 5
A __construct() 0 2 1
C checkIP6() 0 36 12
A checkIP4() 0 28 6
1
<?php
2
/**
3
 * These helpful functions were lifted from the Symfony library
4
 * https://github.com/symfony/http-foundation/blob/master/LICENSE
5
 *
6
 * Http utility functions.
7
 *
8
 * @author Fabien Potencier <[email protected]>
9
 */
10
namespace SilverStripe\Control;
11
12
/**
13
 * Http utility functions.
14
 *
15
 * @author Fabien Potencier <[email protected]>
16
 */
17
class IPUtils
18
{
19
    /**
20
     * This class should not be instantiated.
21
     */
22
    private function __construct()
23
    {
24
    }
25
    /**
26
     * Checks if an IPv4 or IPv6 address is contained in the list of given IPs or subnets.
27
     *
28
     * @param string       $requestIP IP to check
29
     * @param string|array $ips       List of IPs or subnets (can be a string if only a single one)
30
     *
31
     * @return bool Whether the IP is valid
32
     *
33
     * @package framework
34
     * @subpackage core
35
     */
36
    public static function checkIP($requestIP, $ips)
37
    {
38
        if (!is_array($ips)) {
39
            $ips = array($ips);
40
        }
41
42
        $method = substr_count($requestIP, ':') > 1 ? 'checkIP6' : 'checkIP4';
43
44
        foreach ($ips as $ip) {
45
            if (self::$method($requestIP, trim($ip))) {
46
                return true;
47
            }
48
        }
49
50
        return false;
51
    }
52
    /**
53
     * Compares two IPv4 addresses.
54
     * In case a subnet is given, it checks if it contains the request IP.
55
     *
56
     * @param string $requestIP IPv4 address to check
57
     * @param string $ip        IPv4 address or subnet in CIDR notation
58
     *
59
     * @return bool Whether the request IP matches the IP, or whether the request IP is within the CIDR subnet
60
     */
61
    public static function checkIP4($requestIP, $ip)
62
    {
63
        if (!filter_var($requestIP, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
64
            return false;
65
        }
66
67
        if (false !== strpos($ip, '/')) {
68
            list($address, $netmask) = explode('/', $ip, 2);
69
70
            if ($netmask === '0') {
71
                return filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
72
            }
73
74
            if ($netmask < 0 || $netmask > 32) {
75
                return false;
76
            }
77
        } else {
78
            $address = $ip;
79
            $netmask = 32;
80
        }
81
82
        $result = substr_compare(
83
            sprintf('%032b', ip2long($requestIP)),
84
            sprintf('%032b', ip2long($address)),
85
            0,
86
            $netmask
0 ignored issues
show
Bug introduced by
It seems like $netmask can also be of type string; however, parameter $length of substr_compare() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

86
            /** @scrutinizer ignore-type */ $netmask
Loading history...
87
        );
88
        return 0 === $result;
89
    }
90
    /**
91
     * Compares two IPv6 addresses.
92
     * In case a subnet is given, it checks if it contains the request IP.
93
     *
94
     * @author David Soria Parra <dsp at php dot net>
95
     *
96
     * @see https://github.com/dsp/v6tools
97
     *
98
     * @param string $requestIP IPv6 address to check
99
     * @param string $ip        IPv6 address or subnet in CIDR notation
100
     *
101
     * @return bool Whether the IP is valid
102
     *
103
     * @throws \RuntimeException When IPV6 support is not enabled
104
     */
105
    public static function checkIP6($requestIP, $ip)
106
    {
107
        if (!((extension_loaded('sockets') && defined('AF_INET6')) || @inet_pton('::1'))) {
108
            throw new \RuntimeException(
109
                'Unable to check IPv6. Check that PHP was not compiled with option "disable-ipv6".'
110
            );
111
        }
112
113
        if (false !== strpos($ip, '/')) {
114
            list($address, $netmask) = explode('/', $ip, 2);
115
116
            if ($netmask < 1 || $netmask > 128) {
117
                return false;
118
            }
119
        } else {
120
            $address = $ip;
121
            $netmask = 128;
122
        }
123
124
        $bytesAddr = unpack('n*', @inet_pton($address));
125
        $bytesTest = unpack('n*', @inet_pton($requestIP));
126
127
        if (!$bytesAddr || !$bytesTest) {
128
            return false;
129
        }
130
131
        for ($i = 1, $ceil = ceil($netmask / 16); $i <= $ceil; ++$i) {
132
            $left = $netmask - 16 * ($i - 1);
133
            $left = ($left <= 16) ? $left : 16;
134
            $mask = ~(0xffff >> $left) & 0xffff;
135
            if (($bytesAddr[$i] & $mask) != ($bytesTest[$i] & $mask)) {
136
                return false;
137
            }
138
        }
139
140
        return true;
141
    }
142
}
143