ip.php ➔ ipInRange()   C
last analyzed

Complexity

Conditions 13
Paths 10

Size

Total Lines 52

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 13
nc 10
nop 2
dl 0
loc 52
rs 6.6166
c 0
b 0
f 0

How to fix   Long Method    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 digit of IP address.
35
 * With bad ip argument return 0.0.0.0
36
 * Support IPv4, Ipv6 and IPv4 Compatibility.
37
 * @param  string $ip
38
 * @return string  masked IP
39
 */
40
function anonimizeIp(string $ip) : string
41
{
42
    if (isIPv4($ip)) {
43
        return anonimizeIpv4($ip);
44
    } elseif (isIPv4Compatibility($ip)) {
45
        return anonimizeIpv4Compatibility($ip);
46
    }
47
    return anonimizeIpv6($ip);
48
}
49
50
/**
51
 * masquerade last digit of IP address.
52
 * With bad ip argument return 0.0.0.0
53
 * @param string $ip
54
 * @return string
55
 */
56
function anonimizeIpv4Compatibility(string $ip):string
57
{
58 View Code Duplication
    if (isIPv4Compatibility($ip)) {
59
        return substr($ip, 0, strrpos($ip, ".") + 1) . '0';
60
    }
61
    return '0.0.0.0';
62
}
63
64
/**
65
 * masquerade last digit of IP address with inet php functions.
66
 * With bad ip argument return 0.0.0.0
67
 * @param string $ip
68
 * @return string
69
 */
70
function anonimizeIpWithInet(string $ip):string
71
{
72
    if ($ip = @inet_pton($ip)) {
73
        return inet_ntop(substr($ip, 0, strlen($ip) / 2) . str_repeat(chr(0), strlen($ip) / 2));
74
    }
75
    return '0.0.0.0';
76
}
77
78
/**
79
 * masquerade last digit of IP address.
80
 * With bad ip argument return 0.0.0.0
81
 * @param string $ip
82
 * @return string
83
 */
84
function anonimizeIpv6(string $ip):string
85
{
86
    if (strrpos($ip, ":") > 0) {
87
        return substr($ip, 0, strrpos($ip, ":") + 1) . '0';
88
    }
89
    return '0.0.0.0';
90
}
91
92
/**
93
 * masquerade last digit of IP address.
94
 * With bad ip argument return 0.0.0.0
95
 * @param string $ip
96
 * @return string
97
 */
98
function anonimizeIpv4(string $ip):string
99
{
100 View Code Duplication
    if (isIPv4($ip)) {
101
        return substr($ip, 0, strrpos($ip, ".") + 1) . '0';
102
    }
103
    return '0.0.0.0';
104
}
105
106
/**
107
 * getHost Get the Internet host name corresponding to a given IP address
108
 * @param  string $ip the IP to resolve.
109
 * @return string    Returns the host name of the Internet host specified by ip_address on success,
110
 *                   the unmodified ip_address on failure,
111
 *                   or FALSE on malformed input.
112
 */
113
function getHost($ip)
114
{
115
    return gethostbyaddr($ip);
116
}
117
118
/**
119
 * Returns the client IP addresses.
120
 *
121
 * In the returned array the most trusted IP address is first, and the
122
 * least trusted one last. The "real" client IP address is the last one,
123
 * but this is also the least trusted one.
124
 * Trusted proxies are stripped if passed.
125
 *
126
 * Use this method carefully; you should use getClientIp() instead.
127
 *
128
 * @param array $server
129
 * @param array $trustedProxies
130
 *
131
 * @return array The client IP addresses
132
 *
133
 * @see getClientIp()
134
 * @see https://github.com/symfony/symfony/blob/0b39ce23150c34c990e3eccfae4e375161c0d352/src/Symfony/Component/HttpFoundation/Request.php#L831
135
 */
136
function getClientIps(array $server, array $trustedProxies = []) : array
137
{
138
    if (empty($server)) {
139
        return [''];
140
    }
141
    $clientIps = array();
142
    $ip = $server['REMOTE_ADDR'];
143
    if (!isFromTrustedProxy($trustedProxies, $ip)) {
144
        return array($ip);
145
    }
146
    if (array_key_exists('FORWARDED', $server) && trim($server['FORWARDED']) != '') {
147
        $forwardedHeader = $server['FORWARDED'];
148
        preg_match_all('{(for)=("?\[?)([a-z0-9\.:_\-/]*)}', $forwardedHeader, $matches);
149
        $clientIps = $matches[3];
150
    } elseif (array_key_exists('X_FORWARDED_FOR', $server) && trim($server['X_FORWARDED_FOR']) != '') {
151
        $clientIps = array_map('trim', explode(',', $server['X_FORWARDED_FOR']));
152
    }
153
    $clientIps[] = $ip; // Complete the IP chain with the IP the request actually came from
154
    $ip = $clientIps[0]; // Fallback to this when the client IP falls into the range of trusted proxies
155
    foreach ($clientIps as $key => $clientIp) {
156
        // Remove port (unfortunately, it does happen)
157
        if (preg_match('{((?:\d+\.){3}\d+)\:\d+}', $clientIp, $match)) {
158
            $clientIps[$key] = $clientIp = $match[1];
159
        }
160
        if (checkIp($clientIp, $trustedProxies)) {
161
            unset($clientIps[$key]);
162
        }
163
    }
164
    // Now the IP chain contains only untrusted proxies and the client IP
165
    return $clientIps ? array_reverse($clientIps) : array($ip);
166
}
167
168
/**
169
 * Returns the client IP address.
170
 *
171
 * This method can read the client IP address from the "X-Forwarded-For" header
172
 * when trusted proxies were set via "setTrustedProxies()". The "X-Forwarded-For"
173
 * header value is a comma+space separated list of IP addresses, the left-most
174
 * being the original client, and each successive proxy that passed the request
175
 * adding the IP address where it received the request from.
176
 *
177
 * If your reverse proxy uses a different header name than "X-Forwarded-For",
178
 * ("Client-Ip" for instance), configure it via "setTrustedHeaderName()" with
179
 * the "client-ip" key.
180
 *
181
 * @param array $server
182
 * @param array $trustedProxies
183
 *
184
 * @return string The client IP address
185
 *
186
 * @see getClientIps()
187
 * @see https://github.com/symfony/symfony/blob/0b39ce23150c34c990e3eccfae4e375161c0d352/src/Symfony/Component/HttpFoundation/Request.php#L886
188
 * @see http://en.wikipedia.org/wiki/X-Forwarded-For
189
 *
190
 */
191
function getClientIp(array $server, array $trustedProxies = []) : string
192
{
193
    $ipAddresses = getClientIps($server, $trustedProxies);
194
    return $ipAddresses[0];
195
}
196
197
/**
198
 * Checks if an IPv4 or IPv6 address is contained in the list of given IPs or subnets.
199
 *
200
 * @param string $requestIp IP to check
201
 * @param string|array $ips List of IPs or subnets (can be a string if only a single one)
202
 *
203
 * @return bool Whether the IP is valid
204
 * @see https://github.com/symfony/http-foundation/blob/master/IpUtils.php
205
 */
206
function checkIp($requestIp, $ips) : bool
207
{
208
    if (empty($ips)) {
209
        return false;
210
    }
211
    if (!is_array($ips)) {
212
        $ips = array($ips);
213
    }
214
    $method = substr_count($requestIp, ':') > 1 ? 'checkIp6' : 'checkIp4';
215
    foreach ($ips as $ip) {
216
        if ($method($requestIp, $ip)) {
217
            return true;
218
        }
219
    }
220
    return false;
221
}
222
223
/**
224
 * Compares two IPv4 addresses.
225
 * In case a subnet is given, it checks if it contains the request IP.
226
 *
227
 * @param string $requestIp IPv4 address to check
228
 * @param string $ip IPv4 address or subnet in CIDR notation
229
 *
230
 * @return bool Whether the request IP matches the IP, or whether the request IP is within the CIDR subnet
231
 * @see https://github.com/symfony/http-foundation/blob/master/IpUtils.php
232
 */
233
function checkIp4($requestIp, $ip) : bool
234
{
235
    if (false !== strpos($ip, '/')) {
236
        list($address, $netmask) = explode('/', $ip, 2);
237
        if ($netmask === '0') {
238
            // Ensure IP is valid - using ip2long below implicitly validates, but we need to do it manually here
239
            return filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
240
        }
241
        if ($netmask < 0 || $netmask > 32) {
242
            return false;
243
        }
244
    } else {
245
        $address = $ip;
246
        $netmask = 32;
247
    }
248
    return 0 === substr_compare(sprintf('%032b', ip2long($requestIp)), sprintf('%032b', ip2long($address)), 0,
249
        $netmask);
250
}
251
252
/**
253
 * Compares two IPv6 addresses.
254
 * In case a subnet is given, it checks if it contains the request IP.
255
 *
256
 * @author David Soria Parra <dsp at php dot net>
257
 *
258
 * @see https://github.com/symfony/http-foundation/blob/master/IpUtils.php
259
 * @see https://github.com/dsp/v6tools
260
 *
261
 * @param string $requestIp IPv6 address to check
262
 * @param string $ip IPv6 address or subnet in CIDR notation
263
 *
264
 * @return bool Whether the IP is valid
265
 *
266
 * @throws \RuntimeException When IPV6 support is not enabled
267
 */
268
function checkIp6($requestIp, $ip) : bool
269
{
270
    if (!((extension_loaded('sockets') && defined('AF_INET6')) || @inet_pton('::1'))) {
271
        throw new \RuntimeException('Unable to check Ipv6. Check that PHP was not compiled with option "disable-ipv6".');
272
    }
273
    if (false !== strpos($ip, '/')) {
274
        list($address, $netmask) = explode('/', $ip, 2);
275
        if ($netmask < 1 || $netmask > 128) {
276
            return false;
277
        }
278
    } else {
279
        $address = $ip;
280
        $netmask = 128;
281
    }
282
    $bytesAddr = unpack('n*', @inet_pton($address));
283
    $bytesTest = unpack('n*', @inet_pton($requestIp));
284
    if (empty($bytesAddr) || empty($bytesTest)) {
285
        return false;
286
    }
287
    for ($i = 1, $ceil = ceil($netmask / 16); $i <= $ceil; ++$i) {
288
        $left = $netmask - 16 * ($i - 1);
289
        $left = ($left <= 16) ? $left : 16;
290
        $mask = ~(0xffff >> $left) & 0xffff;
291
        if (($bytesAddr[$i] & $mask) != ($bytesTest[$i] & $mask)) {
292
            return false;
293
        }
294
    }
295
    return true;
296
}
297
298
/**
299
 * Check if $ip is contained in $trustedProxies array.
300
 * @param array $trustedProxies
301
 * @param $ip
302
 * @return bool
303
 */
304
function isFromTrustedProxy(array $trustedProxies, $ip)
305
{
306
    return !empty($trustedProxies) && checkIp($ip, $trustedProxies);
307
}
308
309
/**
310
 * Convert an IPv4 address to IPv6
311
 *
312
 * @param string IP Address in dot notation (192.168.1.100)
313
 * @return string IPv6 formatted address or false if invalid input
314
 * @see http://stackoverflow.com/questions/444966/working-with-ipv6-addresses-in-php
315
 */
316
function iPv4To6($ip)
317
{
318
    static $Mask = '::ffff:'; // This tells IPv6 it has an IPv4 address
319
    $IPv6 = (strpos($ip, '::') === 0);
320
    $IPv4 = (strpos($ip, '.') > 0);
321
322
    if (!$IPv4 && !$IPv6) {
323
        return false;
324
    }
325
    if ($IPv6 && $IPv4) {
326
        // Strip IPv4 Compatibility notation
327
        $ip = substr($ip, strrpos($ip, ':') + 1);
328
    } elseif (!$IPv4) {
329
        // Seems to be IPv6 already?
330
        return $ip;
331
    }
332
    $ip = array_pad(explode('.', $ip), 4, 0);
333
    if (count($ip) > 4) {
334
        return false;
335
    }
336
    for ($i = 0; $i < 4; $i++) {
337
        if ($ip[$i] > 255) {
338
            return false;
339
        }
340
    }
341
342
    $Part7 = base_convert(($ip[0] * 256) + $ip[1], 10, 16);
343
    $Part8 = base_convert(($ip[2] * 256) + $ip[3], 10, 16);
344
    return $Mask . $Part7 . ':' . $Part8;
345
}
346
347
/**
348
 * Replace '::' with appropriate number of ':0'
349
 * @param $ip
350
 * @return mixed|string
351
 * @see http://stackoverflow.com/questions/12095835/quick-way-of-expanding-ipv6-addresses-with-php
352
 */
353
function expandIPv6Notation($ip)
354
{
355
    if (!iPv4To6($ip)) {
356
        return $ip;
357
    }
358
    $hex = unpack("H*hex", inet_pton($ip));
359
    $ip = substr(preg_replace("/([A-f0-9]{4})/", "$1:", $hex['hex']), 0, -1);
360
    return $ip;
361
}
362
363
/**
364
 * decbin32
365
 * In order to simplify working with IP addresses (in binary) and their
366
 * netmasks, it is easier to ensure that the binary strings are padded
367
 * with zeros out to 32 characters - IP addresses are 32 bit numbers
368
 *
369
 * @param $dec
370
 * @return string
371
 */
372
function decbin32 ($dec)
373
{
374
    return str_pad(decbin($dec), 32, '0', STR_PAD_LEFT);
375
}
376
377
/**
378
 * ipInRange
379
 *
380
 * Function to determine if an IP is located in a
381
 * specific range as specified via several alternative formats.
382
 *
383
 * This function takes 2 arguments, an IP address and a "range" in several* different formats.
384
 *
385
 * Network ranges can be specified as:
386
 * 1. Wildcard format:     1.2.3.*
387
 * 2. CIDR format:         1.2.3/24  OR  1.2.3.4/255.255.255.0
388
 * 3. Start-End IP format: 1.2.3.0-1.2.3.255
389
 *
390
 * Return value BOOLEAN : ipInRange($ip, $range);
391
 * The function will return true if the supplied IP is within the range.
392
 *
393
 * Note little validation is done on the range inputs - it expects you to use one of the above 3 formats.
394
 *
395
 * Copyright 2008: Paul Gregg <[email protected]>
396
 * 10 January 2008
397
 * Version: 1.2
398
 *
399
 * Source website: http://www.pgregg.com/projects/php/ip_in_range/
400
 * Version 1.2
401
 *
402
 * This software is Donationware - if you feel you have benefited from
403
 * the use of this tool then please consider a donation. The value of
404
 * which is entirely left up to your discretion.
405
 * http://www.pgregg.com/donate/
406
 *
407
 * Please do not remove this header, or source attibution from this file.
408
 *
409
 * @param $ip
410
 * @param $range Network ranges can be specified as: 1. Wildcard format 1.2.3.*, 2. CIDR format: 1.2.3/24  OR  1.2.3.4/255.255.255.0, 3. Start-End IP format 1.2.3.0-1.2.3.255
411
 * @return bool The function will return true if the supplied IP is within the range.
412
 */
413
function ipInRange($ip, $range) : bool
414
{
415
    if(!is_string($ip) || !is_string($range)){
416
        return false;
417
    }
418
419
    if (strpos($range, '/') !== false) {
420
        // $range is in IP/NETMASK format
421
        list($range, $netmask) = explode('/', $range, 2);
422
        if (strpos($netmask, '.') !== false) {
423
            // $netmask is a 255.255.0.0 format
424
            $netmask = str_replace('*', '0', $netmask);
425
            $netmask_dec = ip2long($netmask);
426
            return ( (ip2long($ip) & $netmask_dec) == (ip2long($range) & $netmask_dec) );
427
        } else {
428
            // $netmask is a CIDR size block
429
            // fix the range argument
430
            $x = explode('.', $range);
431
            while(count($x)<4) $x[] = '0';
432
            list($a,$b,$c,$d) = $x;
433
            $range = sprintf("%u.%u.%u.%u", empty($a)?'0':$a, empty($b)?'0':$b,empty($c)?'0':$c,empty($d)?'0':$d);
434
            $range_dec = ip2long($range);
435
            $ip_dec = ip2long($ip);
436
437
            # Strategy 1 - Create the netmask with 'netmask' 1s and then fill it to 32 with 0s
438
            #$netmask_dec = bindec(str_pad('', $netmask, '1') . str_pad('', 32-$netmask, '0'));
439
440
            # Strategy 2 - Use math to create it
441
            $wildcard_dec = pow(2, (32-$netmask)) - 1;
442
            $netmask_dec = ~ $wildcard_dec;
443
444
            return (($ip_dec & $netmask_dec) == ($range_dec & $netmask_dec));
445
        }
446
    } else {
447
        // range might be 255.255.*.* or 1.2.3.0-1.2.3.255
448
        if (strpos($range, '*') !==false) { // a.b.*.* format
449
            // Just convert to A-B format by setting * to 0 for A and 255 for B
450
            $lower = str_replace('*', '0', $range);
451
            $upper = str_replace('*', '255', $range);
452
            $range = "$lower-$upper";
453
        }
454
455
        if (strpos($range, '-')!==false) { // A-B format
456
            list($lower, $upper) = explode('-', $range, 2);
457
            $lower_dec = (float)sprintf("%u",ip2long($lower));
458
            $upper_dec = (float)sprintf("%u",ip2long($upper));
459
            $ip_dec = (float)sprintf("%u",ip2long($ip));
460
            return ( ($ip_dec>=$lower_dec) && ($ip_dec<=$upper_dec) );
461
        }
462
        return false;
463
    }
464
}
465