1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Jasny; |
4
|
|
|
|
5
|
|
|
/** |
6
|
|
|
* Convert an IPv4 address or CIDR into an IP6 address or CIDR. |
7
|
|
|
* |
8
|
|
|
* @param string $ip |
9
|
|
|
* @return string |
10
|
|
|
* @throws \InvalidArgumentException if ip isn't valid |
11
|
|
|
*/ |
12
|
|
|
function ipv4_to_ipv6($ip) |
|
|
|
|
13
|
|
|
{ |
14
|
6 |
|
if ($ip === '0.0.0.0/0') { |
15
|
1 |
|
return '::'; |
16
|
|
|
} |
17
|
|
|
|
18
|
5 |
|
list($address, $mask) = explode('/', $ip, 2) + [null, null]; |
19
|
|
|
|
20
|
5 |
|
if (!filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) || (isset($mask) && !ctype_digit($mask))) { |
21
|
2 |
|
throw new \InvalidArgumentException("'$ip' is not a valid IPv4 address or cidr"); |
22
|
|
|
} |
23
|
|
|
|
24
|
3 |
|
$bytes = array_map('dechex', explode('.', $address)); |
25
|
|
|
|
26
|
3 |
|
return vsprintf('0:0:0:0:0:ffff:%02s%02s:%02s%02s', $bytes) . (isset($mask) ? '/' . ($mask + 96) : ''); |
27
|
|
|
} |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* Check if IP address is in CIDR block |
31
|
|
|
* |
32
|
|
|
* @param string $ip An IPv4 or IPv6 |
33
|
|
|
* @param string $cidr An IPv4 CIDR block or IPv6 CIDR block |
34
|
|
|
* @return boolean |
35
|
|
|
*/ |
36
|
|
|
function ip_in_cidr($ip, $cidr) |
|
|
|
|
37
|
|
|
{ |
38
|
37 |
|
if ($cidr === '0.0.0.0/0' || $cidr === '::/0' || $cidr === '::') { |
39
|
4 |
|
return true; |
40
|
|
|
} |
41
|
|
|
|
42
|
33 |
|
$ipv = strpos($ip, ':') === false ? 4 : 6; |
43
|
33 |
|
$cidrv = strpos($cidr, ':') === false ? 4 : 6; |
44
|
|
|
|
45
|
|
|
try { |
46
|
33 |
|
if ($ipv < $cidrv) { |
47
|
5 |
|
$ip = ipv4_to_ipv6($ip); |
48
|
4 |
|
$ipv = 6; |
49
|
32 |
|
} elseif ($ipv > $cidrv) { |
50
|
5 |
|
$cidr = ipv4_to_ipv6($cidr); |
51
|
4 |
|
} |
52
|
33 |
|
} catch (\InvalidArgumentException $e) { |
53
|
2 |
|
return false; |
54
|
|
|
} |
55
|
|
|
|
56
|
31 |
|
$fn = __NAMESPACE__ . "\\ipv{$ipv}_in_cidr"; |
57
|
31 |
|
return $fn($ip, $cidr); |
58
|
|
|
} |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* Check if IPv4 address is in CIDR block |
62
|
|
|
* |
63
|
|
|
* @param string $ip |
64
|
|
|
* @param string $cidr |
65
|
|
|
* @return boolean |
66
|
|
|
*/ |
67
|
|
|
function ipv4_in_cidr($ip, $cidr) |
|
|
|
|
68
|
|
|
{ |
69
|
13 |
|
list($subnet, $mask) = explode('/', $cidr, 2) + [null, '32']; |
70
|
|
|
|
71
|
|
|
if ( |
72
|
13 |
|
!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) || |
73
|
11 |
|
!filter_var($subnet, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) |
74
|
13 |
|
) { |
75
|
3 |
|
return false; |
76
|
|
|
} |
77
|
|
|
|
78
|
10 |
|
$ipLong = ip2long($ip); |
79
|
10 |
|
$subnetLong = ip2long($subnet); |
80
|
|
|
|
81
|
10 |
|
$ipMasked = $ipLong & ~((1 << (32 - $mask)) - 1); |
82
|
10 |
|
$subnetMasked = $subnetLong & ~((1 << (32 - $mask)) - 1); |
83
|
|
|
|
84
|
10 |
|
return $ipMasked == $subnetMasked; |
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
/** |
88
|
|
|
* Check if IPv6 address is in CIDR block |
89
|
|
|
* |
90
|
|
|
* @param string $ip |
91
|
|
|
* @param string $cidr |
92
|
|
|
* @return boolean |
93
|
|
|
*/ |
94
|
|
|
function ipv6_in_cidr($ip, $cidr) |
|
|
|
|
95
|
|
|
{ |
96
|
14 |
|
if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { |
97
|
2 |
|
return false; |
98
|
|
|
} |
99
|
|
|
|
100
|
12 |
|
$inetIp = inet_pton($ip); |
101
|
12 |
|
$binaryIp = inet_to_bits($inetIp); |
102
|
|
|
|
103
|
12 |
|
if (strpos($cidr, '/') === false) { |
104
|
3 |
|
$net = $cidr; |
105
|
3 |
|
$mask = $cidr === '::' ? 0 : substr_count(rtrim($cidr, ':'), ':') * 16; |
106
|
3 |
|
} else { |
107
|
9 |
|
list($net, $mask) = explode('/', $cidr, 2); |
108
|
|
|
} |
109
|
|
|
|
110
|
12 |
|
if (!filter_var($net, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { |
111
|
1 |
|
return false; |
112
|
|
|
} |
113
|
|
|
|
114
|
11 |
|
$inetNet = inet_pton($net); |
115
|
11 |
|
$binaryNet = inet_to_bits($inetNet); |
116
|
|
|
|
117
|
11 |
|
$ipNetBits = substr($binaryIp, 0, $mask); |
118
|
11 |
|
$netBits = substr($binaryNet, 0, $mask); |
119
|
|
|
|
120
|
11 |
|
return $ipNetBits === $netBits; |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
/** |
124
|
|
|
* Converts inet_pton output to string with bits. |
125
|
|
|
* |
126
|
|
|
* @param string $inet |
127
|
|
|
* @return string |
128
|
|
|
*/ |
129
|
|
|
function inet_to_bits($inet) |
|
|
|
|
130
|
|
|
{ |
131
|
12 |
|
$unpackedArr = unpack('A16', $inet); |
132
|
12 |
|
$unpacked = str_split($unpackedArr[1]); |
133
|
|
|
|
134
|
12 |
|
$binaryip = ''; |
135
|
|
|
|
136
|
12 |
|
foreach ($unpacked as $char) { |
137
|
12 |
|
$binaryip .= str_pad(decbin(ord($char)), 8, '0', STR_PAD_LEFT); |
138
|
12 |
|
} |
139
|
|
|
|
140
|
12 |
|
return str_pad($binaryip, 128, '0', STR_PAD_RIGHT); |
141
|
|
|
} |
142
|
|
|
|
This check looks for functions that have already been defined in other files.
Some Codebases, like WordPress, make a practice of defining functions multiple times. This may lead to problems with the detection of function parameters and types. If you really need to do this, you can mark the duplicate definition with the
@ignore
annotation.See also the PhpDoc documentation for @ignore.