Completed
Push — master ( 9d4be0...988869 )
by Lorenzo
04:20
created

ip.php ➔ anonimizeIpv4()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 3
Ratio 42.86 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 1
dl 3
loc 7
rs 9.4285
c 0
b 0
f 0
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)) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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)) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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