Completed
Push — master ( 5f0ee2...6f3a7a )
by Lorenzo
04:41
created

ip.php ➔ checkIp6()   C

Complexity

Conditions 12
Paths 14

Size

Total Lines 29
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
eloc 21
c 0
b 0
f 0
nc 14
nop 2
dl 0
loc 29
rs 5.1612

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * get Client IP. DEPRECATED! use getClientIP().
5
 * @param array $server
6
 * @return string
7
 * @deprecated
8
 * @see getClientIp();
9
 */
10
function getIPVisitor(array $server = []) : string
11
{
12
    $IP2Check = '';
13
    if (array_key_exists('HTTP_X_FORWARDED_FOR', $server) && trim($server['HTTP_X_FORWARDED_FOR']) != '') {
14
        $IP2Check = $server['HTTP_X_FORWARDED_FOR'];
15
    } elseif (array_key_exists('REMOTE_ADDR', $server) && trim($server['REMOTE_ADDR'])) {
16
        $IP2Check = $server['REMOTE_ADDR'];
17
    }
18
19
    if (strpos($IP2Check, ',') === false) {
20
        return $IP2Check;
21
    }
22
23
    // Header can contain multiple IP-s of proxies that are passed through.
24
    // Only the IP added by the last proxy (last IP in the list) can be trusted.
25
    $arrIps = explode(',', $IP2Check);
26
    if (!is_array($arrIps) || count($arrIps) < 1) {
27
        return '';
28
    }
29
30
    return trim(head($arrIps));
31
}
32
33
/**
34
 * anonimizeIp masquerade last 3 digit of IP address
35
 * @param  string $ip
36
 * @return string  masked IP
37
 */
38
function anonimizeIp(string $ip) : string
39
{
40
    if ($ip === null || strlen($ip) < 2 || strrpos($ip, ".") === false) {
41
        return $ip;
42
    }
43
    return substr($ip, 0, strrpos($ip, ".") + 1) . '0';
44
}
45
46
/**
47
 * getHost Get the Internet host name corresponding to a given IP address
48
 * @param  string $ip the IP to resolve.
49
 * @return string    Returns the host name of the Internet host specified by ip_address on success,
50
 *                   the unmodified ip_address on failure,
51
 *                   or FALSE on malformed input.
52
 */
53
function getHost($ip)
54
{
55
    return gethostbyaddr($ip);
56
}
57
58
/**
59
 * Returns the client IP addresses.
60
 *
61
 * In the returned array the most trusted IP address is first, and the
62
 * least trusted one last. The "real" client IP address is the last one,
63
 * but this is also the least trusted one.
64
 * Trusted proxies are stripped if passed.
65
 *
66
 * Use this method carefully; you should use getClientIp() instead.
67
 *
68
 * @param array $server
69
 * @param array $trustedProxies
70
 *
71
 * @return array The client IP addresses
72
 *
73
 * @see getClientIp()
74
 * @see https://github.com/symfony/symfony/blob/0b39ce23150c34c990e3eccfae4e375161c0d352/src/Symfony/Component/HttpFoundation/Request.php#L831
75
 */
76
function getClientIps(array $server, array $trustedProxies = []) : array
77
{
78
    if (empty($server)) {
79
        return [''];
80
    }
81
    $clientIps = array();
82
    $ip = $server['REMOTE_ADDR'];
83
    if (!isFromTrustedProxy($trustedProxies, $ip)) {
84
        return array($ip);
85
    }
86
    if (array_key_exists('FORWARDED', $server) && trim($server['FORWARDED']) != '') {
87
        $forwardedHeader = $server['FORWARDED'];
88
        preg_match_all('{(for)=("?\[?)([a-z0-9\.:_\-/]*)}', $forwardedHeader, $matches);
89
        $clientIps = $matches[3];
90
    } elseif (array_key_exists('X_FORWARDED_FOR', $server) && trim($server['X_FORWARDED_FOR']) != '') {
91
        $clientIps = array_map('trim', explode(',', $server['X_FORWARDED_FOR']));
92
    }
93
    $clientIps[] = $ip; // Complete the IP chain with the IP the request actually came from
94
    $ip = $clientIps[0]; // Fallback to this when the client IP falls into the range of trusted proxies
95
    foreach ($clientIps as $key => $clientIp) {
96
        // Remove port (unfortunately, it does happen)
97
        if (preg_match('{((?:\d+\.){3}\d+)\:\d+}', $clientIp, $match)) {
98
            $clientIps[$key] = $clientIp = $match[1];
99
        }
100
        if (checkIp($clientIp, $trustedProxies)) {
101
            unset($clientIps[$key]);
102
        }
103
    }
104
    // Now the IP chain contains only untrusted proxies and the client IP
105
    return $clientIps ? array_reverse($clientIps) : array($ip);
106
}
107
108
/**
109
 * Returns the client IP address.
110
 *
111
 * This method can read the client IP address from the "X-Forwarded-For" header
112
 * when trusted proxies were set via "setTrustedProxies()". The "X-Forwarded-For"
113
 * header value is a comma+space separated list of IP addresses, the left-most
114
 * being the original client, and each successive proxy that passed the request
115
 * adding the IP address where it received the request from.
116
 *
117
 * If your reverse proxy uses a different header name than "X-Forwarded-For",
118
 * ("Client-Ip" for instance), configure it via "setTrustedHeaderName()" with
119
 * the "client-ip" key.
120
 *
121
 * @param array $server
122
 * @param array $trustedProxies
123
 *
124
 * @return string The client IP address
125
 *
126
 * @see getClientIps()
127
 * @see https://github.com/symfony/symfony/blob/0b39ce23150c34c990e3eccfae4e375161c0d352/src/Symfony/Component/HttpFoundation/Request.php#L886
128
 * @see http://en.wikipedia.org/wiki/X-Forwarded-For
129
 *
130
 */
131
function getClientIp(array $server, array $trustedProxies = []) : string
132
{
133
    $ipAddresses = getClientIps($server, $trustedProxies);
134
    return $ipAddresses[0];
135
}
136
137
/**
138
 * Checks if an IPv4 or IPv6 address is contained in the list of given IPs or subnets.
139
 *
140
 * @param string $requestIp IP to check
141
 * @param string|array $ips List of IPs or subnets (can be a string if only a single one)
142
 *
143
 * @return bool Whether the IP is valid
144
 * @see https://github.com/symfony/http-foundation/blob/master/IpUtils.php
145
 */
146
function checkIp($requestIp, $ips) : bool
147
{
148
    if (empty($ips)) {
149
        return false;
150
    }
151
    if (!is_array($ips)) {
152
        $ips = array($ips);
153
    }
154
    $method = substr_count($requestIp, ':') > 1 ? 'checkIp6' : 'checkIp4';
155
    foreach ($ips as $ip) {
156
        if ($method($requestIp, $ip)) {
157
            return true;
158
        }
159
    }
160
    return false;
161
}
162
163
/**
164
 * Compares two IPv4 addresses.
165
 * In case a subnet is given, it checks if it contains the request IP.
166
 *
167
 * @param string $requestIp IPv4 address to check
168
 * @param string $ip IPv4 address or subnet in CIDR notation
169
 *
170
 * @return bool Whether the request IP matches the IP, or whether the request IP is within the CIDR subnet
171
 * @see https://github.com/symfony/http-foundation/blob/master/IpUtils.php
172
 */
173
function checkIp4($requestIp, $ip) : bool
174
{
175
    if (false !== strpos($ip, '/')) {
176
        list($address, $netmask) = explode('/', $ip, 2);
177
        if ($netmask === '0') {
178
            // Ensure IP is valid - using ip2long below implicitly validates, but we need to do it manually here
179
            return filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
180
        }
181
        if ($netmask < 0 || $netmask > 32) {
182
            return false;
183
        }
184
    } else {
185
        $address = $ip;
186
        $netmask = 32;
187
    }
188
    return 0 === substr_compare(sprintf('%032b', ip2long($requestIp)), sprintf('%032b', ip2long($address)), 0,
189
        $netmask);
190
}
191
192
/**
193
 * Compares two IPv6 addresses.
194
 * In case a subnet is given, it checks if it contains the request IP.
195
 *
196
 * @author David Soria Parra <dsp at php dot net>
197
 *
198
 * @see https://github.com/symfony/http-foundation/blob/master/IpUtils.php
199
 * @see https://github.com/dsp/v6tools
200
 *
201
 * @param string $requestIp IPv6 address to check
202
 * @param string $ip IPv6 address or subnet in CIDR notation
203
 *
204
 * @return bool Whether the IP is valid
205
 *
206
 * @throws \RuntimeException When IPV6 support is not enabled
207
 */
208
function checkIp6($requestIp, $ip) : bool
209
{
210
    if (!((extension_loaded('sockets') && defined('AF_INET6')) || @inet_pton('::1'))) {
211
        throw new \RuntimeException('Unable to check Ipv6. Check that PHP was not compiled with option "disable-ipv6".');
212
    }
213
    if (false !== strpos($ip, '/')) {
214
        list($address, $netmask) = explode('/', $ip, 2);
215
        if ($netmask < 1 || $netmask > 128) {
216
            return false;
217
        }
218
    } else {
219
        $address = $ip;
220
        $netmask = 128;
221
    }
222
    $bytesAddr = unpack('n*', @inet_pton($address));
223
    $bytesTest = unpack('n*', @inet_pton($requestIp));
224
    if (!$bytesAddr || !$bytesTest) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $bytesAddr of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
Bug Best Practice introduced by
The expression $bytesTest of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
225
        return false;
226
    }
227
    for ($i = 1, $ceil = ceil($netmask / 16); $i <= $ceil; ++$i) {
228
        $left = $netmask - 16 * ($i - 1);
229
        $left = ($left <= 16) ? $left : 16;
230
        $mask = ~(0xffff >> $left) & 0xffff;
231
        if (($bytesAddr[$i] & $mask) != ($bytesTest[$i] & $mask)) {
232
            return false;
233
        }
234
    }
235
    return true;
236
}
237
238
/**
239
 * Check if $ip is contained in $trustedProxies array.
240
 * @param array $trustedProxies
241
 * @param $ip
242
 * @return bool
243
 */
244
function isFromTrustedProxy(array $trustedProxies, $ip)
245
{
246
    return $trustedProxies && checkIp($ip, $trustedProxies);
0 ignored issues
show
Bug Best Practice introduced by
The expression $trustedProxies of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
247
}
248