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