Reputation: 903
Most questions I've seen regarding validating private IPs in PHP have to do with validating if a specific IP address is private or not, or whether an IP exists within a specific range.
However, I want to be able to determine in PHP whether or not an IP range, given in the format of e.g. "X.X.X.X - Y.Y.Y.Y" is an exclusively private range. Just so it's clear, I want to see if the entire range is private or not. Examples:
10.0.0.1 - 10.0.0.14
would return true since all IPs in this range are internal.
10.0.0.1 - 127.0.0.16
would return false because not all of the IPs in the range are internal/private, even though the start and end points are.
My initial thought was to just validate the start and end IPs, and if they're internal, then all good. But, as I said above, if I had a range like $range = '10.0.0.1 - 127.0.0.16'
, while the start and end IP addresses are both considered to be private IP addresses, it spans IP addresses that are not internal, hence it's not an exclusively internal IP address range.
I could perhaps generate every single IP address within the range, and validate each one, but this seems incredibly inefficient.
Is there an easier and more efficient way of doing this?
Edit: Just to make it more explicitly clear to those flagging this as a duplicate: I am not trying to validate a single given IP and see if it is private. I want to check that every single possible IP in a given range of the format $range = '10.0.0.1 - 127.0.0.16'
is private. Doing something like this is far too slow and inefficient (assuming I've exploded the string to get the start and end IP addresses):
<?php
function checkRange($start, $end)
{
$start = ip2long($start);
$end = ip2long($end);
for ($i = $start; $i <= $end; $i++) {
$ip = long2ip($i);
if (!filter_var(
$ip,
FILTER_VALIDATE_IP,
FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE | FILTER_FLAG_IPV4
)) {
continue;
}
return false;
}
return true;
}
Upvotes: 3
Views: 1473
Reputation: 3667
All private ranges in the IP space are separated by public ranges (p
for private, _
for public):
__ppp___pppp___ppp___
If you want a user defined range to be completely private, it needs to be fully contained in one of the private ranges (u
for user-defined all-private ranges = hits, x
for wrong ranges, fails):
__ppp___pppp___ppp___
uu
u
xxxxxxx
uu
u
Thus, we need to check whether both input values ($start
and $end
) are not only private, but also in the same private range.
function checkRange($start, $end)
{
$start = ip2long($start);
$end = ip2long($end);
if (!$start || !$end)
throw new Exception('Invalid input.');
if ($start > $end)
throw new Exception('Invalid range.'); // Alternative: switch $start and $end
$range1_start = ip2long('10.0.0.0');
$range1_end = ip2long('10.255.255.255');
$range2_start = ip2long('172.16.0.0');
$range2_end = ip2long('172.31.255.255');
$range3_start = ip2long('192.168.0.0');
$range3_end = ip2long('192.168.255.255');
return ($start >= $range1_start && $start <= $range1_end &&
$end >= $range1_start && $end <= $range1_end) ||
($start >= $range2_start && $start <= $range2_end &&
$end >= $range2_start && $end <= $range2_end) ||
($start >= $range3_start && $start <= $range3_end &&
$end >= $range3_start && $end <= $range3_end);
}
Upvotes: 3
Reputation: 10163
An IP address is a 32 bit number. It can be represented as four decimals separated by a period, each decimal being 8 bits of the 32 bit number in base 10. PHP's built-in ip2long converts from the IPv4 string notation to a standard 32 bit integer.
With this in mind (IPv4 string notation strings are just 32 bit integers) you can easily come up with an algorithm that checks if an integer is in a certain range or if a range is inside another.
<?php
function check($start, $end) {
$r10Start = ip2long('10.0.0.0');
$r10End = ip2long('10.255.255.255');
$r127Start = ip2long('127.0.0.0');
$r127End = ip2long('127.255.255.255');
if (
($start >= $r10Start && $end <= $r10End)
|| ($start >= $r127Start && $end <= $r127End)
) {
return true;
}
return false;
}
var_dump(check(ip2long('10.0.0.1'), ip2long('10.0.0.14')));
var_dump(check(ip2long('10.0.0.1'), ip2long('127.0.0.16')));
Upvotes: 0