Getting list IPs from CIDR notation in PHP - php

Is there a way (or function/class) to get the list of IP addresses from a CIDR notation?
For example, I have 73.35.143.32/27 CIDR and want to get the list of all IP's in this notation. Any suggestions?
Thank you.

I'll edit the aforementioned class to contain a method for that. Here is the code I came up with that might help you until then.
function cidrToRange($cidr) {
$range = array();
$cidr = explode('/', $cidr);
$range[0] = long2ip((ip2long($cidr[0])) & ((-1 << (32 - (int)$cidr[1]))));
$range[1] = long2ip((ip2long($range[0])) + pow(2, (32 - (int)$cidr[1])) - 1);
return $range;
}
var_dump(cidrToRange("73.35.143.32/27"));
//////////////////OUTPUT////////////////////////
// array(2) {
// [0]=>
// string(12) "73.35.143.32"
// [1]=>
// string(12) "73.35.143.63"
// }
/////////////////////////////////////////////////
Returns the low end of the ip range as the first entry in the array, then returns the high end as the second entry.

Well, it's a bitmask - 73.35.143.32/27 means that 27 bits are the network mask, and the rest is available for assigning to the nodes in the network:
73.35.143.32
in binary is this (dots shown for legibility):
01001001.00100011.10001111.00100000
The netmask is 27 bits:
11111111.11111111.11111111.11100000
So you can just AND them together and get this:
01001001.00100011.10001111.001 00000
network prefix (27 bits) | node address (5 bits)
From here, you can just enumerate all the combinations in the node address (00000 is 0, 11111 is 31, so a simple loop is enough), and you'll have all the available hosts.
Converting this pseudocode to PHP is left as an exercise to the reader ;)
Oh, and the obligatory deprecation warning: IPv4 is now full, consider also IPv6.

Here is one fast 64bits function to do it, please comment the return line you don't need. Accepting any valid Ipv4 with or without valid CIDR Routing Prefix for example 63.161.156.0/24 or 63.161.156.0
<?php
function cidr2range($ipv4){
if ($ip=strpos($ipv4,'/'))
{$n_ip=(1<<(32-substr($ipv4,1+$ip)))-1; $ip_dec=ip2long(substr($ipv4,0,$ip)); }
else
{$n_ip=0; $ip_dec=ip2long($ipv4); }
$ip_min=$ip_dec&~$n_ip;
$ip_max=$ip_min+$n_ip;
#Array(2) of Decimal Values Range
return [$ip_min,$ip_max];
#Array(2) of Ipv4 Human Readable Range
return [long2ip($ip_min),long2ip($ip_max)];
#Array(2) of Ipv4 and Subnet Range
return [long2ip($ip_min),long2ip(~$n_ip)];
#Array(2) of Ipv4 and Wildcard Bits
return [long2ip($ip_min),long2ip($n_ip)];
#Integer Number of Ipv4 in Range
return ++$n_ip;
}
To run fast the function don't check input but formally it should be a string matching the following regex
#^(?:((?:0)|(?:2(?:(?:[0-4][0-9])|(?:5[0-5])))|(?:1?[0-9]{1,2}))\.((?:0)|(?:2(?:(?:[0-4][0-9])|(?:5[0-5])))|(?:1?[0-9]{1,2}))\.((?:0)|(?:2(?:(?:[0-4][0-9])|(?:5[0-5])))|(?:1?[0-9]{1,2}))\.((?:0)|(?:2(?:(?:[0-4][0-9])|(?:5[0-5])))|(?:1?[0-9]{1,2}))(?:/((?:(?:0)|(?:3[0-2])|(?:[1-2]?[0-9]))))?)$#
If you want to verify the input before using the function
<?php
if (is_string($ipv4) && preg_match('#^(?:((?:0)|(?:2(?:(?:[0-4][0-9])|(?:5[0-5])))|(?:1?[0-9]{1,2}))\.((?:0)|(?:2(?:(?:[0-4][0-9])|(?:5[0-5])))|(?:1?[0-9]{1,2}))\.((?:0)|(?:2(?:(?:[0-4][0-9])|(?:5[0-5])))|(?:1?[0-9]{1,2}))\.((?:0)|(?:2(?:(?:[0-4][0-9])|(?:5[0-5])))|(?:1?[0-9]{1,2}))(?:/((?:(?:0)|(?:3[0-2])|(?:[1-2]?[0-9]))))?)$#',$ipv4))
{
#This is a valid ipv4 with or without CIDR Routing Prefix
$result=cidr2range($ipv4);
print_r($result);
}
To get the full range as an array for a given IP (with or without CIDR Routing Prefix) you can use the following code but be carefull because for example 25.25.25.25/16 return an array with 65536 elements and you can easily run out of memory using a smaller Routing Prefix
<?php
$result=cidr2range($ipv4);
for($ip_dec=$result[0];$ip_dec<=$result[1];$ip_dec++)
$full_range[$ip_dec]=long2ip($ip_dec);
print_r($full_range);
To fast check if a given ipv4 is matching a given CIDR you can do it inline like in this example
<?php
$given_cidr='55.55.55.0/24';
$given_ipv4='55.55.55.55';
if(($range=cidr2range($given_cidr)) &&
($check=ip2long($given_ipv4))!==false &&
$check>=$range[0] && $check<=$range[1])
{
echo 'Yes, '.$given_ipv4.' is included in '.$given_cidr;
}
else
{
echo 'No, '.$given_ipv4.' is not included in '.$given_cidr;
}
To fast check if a given ipv4 is matching a given array of IP (with or without CIDR Routing Prefix)
<?php
#This code is checking if a given ip belongs to googlebot
$given_ipv4='74.125.61.208';
$given_cidr_array=['108.59.93.43/32','108.59.93.40/31','108.59.93.44/30','108.59.93.32/29','108.59.93.48/28','108.59.93.0/27','108.59.93.64/26','108.59.93.192/26','108.59.92.192/27','108.59.92.128/26','108.59.92.96/27','108.59.92.0/27','108.59.94.208/29','108.59.94.192/28','108.59.94.240/28','108.59.94.128/26','108.59.94.16/29','108.59.94.0/28','108.59.94.32/27','108.59.94.64/26','108.59.95.0/24','108.59.88.0/22','108.59.81.0/27','108.59.80.0/24','108.59.82.0/23','108.59.84.0/22','108.170.217.128/28','108.170.217.160/27','108.170.217.192/26','108.170.217.0/25','108.170.216.0/24','108.170.218.0/23','108.170.220.0/22','108.170.208.0/21','108.170.192.0/20','108.170.224.0/19','108.177.0.0/17','104.132.0.0/14','104.154.0.0/15','104.196.0.0/14','107.167.160.0/19','107.178.192.0/18','125.17.82.112/30','125.16.7.72/30','74.125.0.0/16','72.14.192.0/18','77.109.131.208/28','77.67.50.32/27','66.102.0.0/20','66.227.77.144/29','66.249.64.0/19','67.148.177.136/29','64.124.98.104/29','64.71.148.240/29','64.68.64.64/26','64.68.80.0/20','64.41.221.192/28','64.41.146.208/28','64.9.224.0/19','64.233.160.0/19','65.171.1.144/28','65.170.13.0/28','65.167.144.64/28','65.220.13.0/24','65.216.183.0/24','70.32.132.0/23','70.32.128.0/22','70.32.136.0/21','70.32.144.0/20','85.182.250.128/26','85.182.250.0/25','80.239.168.192/26','80.149.20.0/25','61.246.224.136/30','61.246.190.124/30','63.237.119.112/29','63.226.245.56/29','63.158.137.224/29','63.166.17.128/25','63.161.156.0/24','63.88.22.0/23','41.206.188.128/26','12.234.149.240/29','12.216.80.0/24','8.34.217.24/29','8.34.217.0/28','8.34.217.32/27','8.34.217.64/26','8.34.217.128/25','8.34.216.0/24','8.34.218.0/23','8.34.220.0/22','8.34.208.128/29','8.34.208.144/28','8.34.208.160/27','8.34.208.192/26','8.34.208.0/25','8.34.209.0/24','8.34.210.0/23','8.34.212.0/22','8.35.195.128/28','8.35.195.160/27','8.35.195.192/26','8.35.195.0/25','8.35.194.0/24','8.35.192.0/23','8.35.196.0/22','8.35.200.0/21','8.8.8.0/24','8.8.4.0/24','8.6.48.0/21','4.3.2.0/24','23.236.48.0/20','23.251.128.0/19','216.239.32.0/19','216.252.220.0/22','216.136.145.128/27','216.33.229.160/29','216.33.229.144/29','216.34.7.176/28','216.58.192.0/19','216.109.75.80/28','216.74.130.48/28','216.74.153.0/27','217.118.234.96/28','208.46.199.160/29','208.44.48.240/29','208.21.209.0/28','208.184.125.240/28','209.185.108.128/25','209.85.128.0/17','213.200.103.128/26','213.200.99.192/26','213.155.151.128/26','199.192.112.224/29','199.192.112.192/27','199.192.112.128/26','199.192.112.0/25','199.192.113.176/28','199.192.113.128/27','199.192.113.192/26','199.192.113.0/25','199.192.115.80/28','199.192.115.96/27','199.192.115.0/28','199.192.115.128/25','199.192.114.192/26','199.192.114.0/25','199.223.232.0/21','198.108.100.192/28','195.16.45.144/29','192.104.160.0/23','192.158.28.0/22','192.178.0.0/15','206.160.135.240/28','207.223.160.0/20','203.222.167.144/28','173.255.125.72/29','173.255.125.80/28','173.255.125.96/27','173.255.125.0/27','173.255.125.128/25','173.255.124.240/29','173.255.124.232/29','173.255.124.192/27','173.255.124.128/29','173.255.124.144/28','173.255.124.160/27','173.255.124.48/29','173.255.124.32/28','173.255.124.0/27','173.255.124.64/26','173.255.126.0/23','173.255.122.128/26','173.255.122.64/26','173.255.123.0/24','173.255.121.128/26','173.255.121.0/25','173.255.120.0/24','173.255.117.32/27','173.255.117.64/26','173.255.117.128/25','173.255.116.192/27','173.255.116.128/26','173.255.116.0/25','173.255.118.0/23','173.255.112.0/22','173.194.0.0/16','172.102.8.0/21','172.253.0.0/16','172.217.0.0/16','162.216.148.0/22','162.222.176.0/21','180.87.33.64/26','128.177.109.0/26','128.177.119.128/25','128.177.163.0/25','130.211.0.0/16','142.250.0.0/15','146.148.0.0/17'];
echo '<pre>';
$in_range=false;
if (($given_ipv4_dec=ip2long($given_ipv4))!==false)
{
foreach($given_cidr_array as $given_cidr){
if(($range=cidr2range($given_cidr)) &&
$given_ipv4_dec>=$range[0] && $given_ipv4_dec<=$range[1])
{
$in_range=true;
echo $given_ipv4.' matched '.$given_cidr.' ('.join(array_map('long2ip',$range),' - ').")\n";
}
}
}
echo $given_ipv4.' is probably'.($in_range?'':' not').' a Googlebot IP';
hope that these few lines have helped you…

There is small fix - when someone place ip prefix like: 127.0.0.15/26 this function returns bad last IP adress. In this function is at line 4 (starts: $range[1]...) changed $cidr[0] to $range[0] - now its return last IP address good for every IP in range.
function cidrToRange($cidr) {
$range = array();
$cidr = explode('/', $cidr);
$range[0] = long2ip((ip2long($cidr[0])) & ((-1 << (32 - (int)$cidr[1]))));
$range[1] = long2ip((ip2long($range[0])) + pow(2, (32 - (int)$cidr[1])) - 1);
return $range;
}
var_dump(cidrToRange("127.0.0.15/26"));
Before fix:
array(2) {
[0]=>
string(9) "127.0.0.0"
[1]=>
string(10) "127.0.0.78"
}
string(41) "127.0.0.15/26 >> 2130706432 -> 2130706510"
After fix:
array(2) {
[0]=>
string(9) "127.0.0.0"
[1]=>
string(10) "127.0.0.63"
}
string(41) "127.0.0.15/26 >> 2130706432 -> 2130706495"

Highly recommend this PHP Lib:
https://github.com/S1lentium/IPTools
It can manipulate network easily with many functions.
e.g. Iterate over Network IP adresses:
$network = Network::parse('192.168.1.0/24');
foreach($network as $ip) {
echo (string)$ip . PHP_EOL;
}
// output:
192.168.1.0
...
192.168.1.255

I come up with a better idea
$ip_from= long2ip(ip2long($ip)& (-1<<(32-$net_mask)));
$ip_to= long2ip(ip2long($ip)| (~(-1<<(32-$net_mask))));
P.S:$ip is a ipv4 address like 60.12.34.5;$net_mask is a int net mask like 25;
it's very fast because of the bit shift operation

this returns an array of ips:
function get_list_ip($ip_addr_cidr){
$ip_arr = explode("/", $ip_addr_cidr);
$bin = "";
for($i=1;$i<=32;$i++) {
$bin .= $ip_arr[1] >= $i ? '1' : '0';
}
$ip_arr[1] = bindec($bin);
$ip = ip2long($ip_arr[0]);
$nm = $ip_arr[1];
$nw = ($ip & $nm);
$bc = $nw | ~$nm;
$bc_long = ip2long(long2ip($bc));
for($zm=1;($nw + $zm)<=($bc_long - 1);$zm++)
{
$ret[]=long2ip($nw + $zm);
}
return $ret;
}

Extension to #jonavon 's answer. If you need flat list of IPs. You can convert his function like below.
function cidrToRange($value) {
$range = array();
$split = explode('/', $value);
if (!empty($split[0]) && is_scalar($split[1]) && filter_var($split[0], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
$rangeStart = ip2long($split[0]) & ((-1 << (32 - (int)$split[1])));
$rangeEnd = ip2long($split[0]) + pow(2, (32 - (int)$split[1])) - 1;
for ($i = $rangeStart; $i <= $rangeEnd; $i++) {
$range[] = long2ip($i);
}
return $range;
} else {
return $value;
}
}

I don't believe this class will return a list of IPs, but it does provide some useful methods for working with CIDR blocks.

I previously wrote an entire Class which handled this task pretty well (for another Language), and thanks to PHP's ip2long(...) method, we don't need to calculate manually in PHP.
But my Unit-Test data can be used to test any of the other answers:
<?php
// Below has "CIDR = Minimum ... Maximum" format.
$validRangeMap = [
'0.0.0.0/1=0.0.0.0...127.255.255.255',
'128.0.0.0/2=128.0.0.0...191.255.255.255',
'192.0.0.0/9=192.0.0.0...192.127.255.255',
'192.128.0.0/11=192.128.0.0...192.159.255.255',
'192.160.0.0/13=192.160.0.0...192.167.255.255',
'192.168.0.0/19=192.168.0.0...192.168.31.255',
'192.168.32.0/21=192.168.32.0...192.168.39.255',
'192.168.40.0/23=192.168.40.0...192.168.41.255',
'192.168.42.0/23=192.168.42.0...192.168.43.255',
'192.168.44.0/22=192.168.44.0...192.168.47.255',
'192.168.48.0/20=192.168.48.0...192.168.63.255',
'192.168.64.0/18=192.168.64.0...192.168.127.255',
'192.168.128.0/17=192.168.128.0...192.168.255.255',
'192.169.0.0/16=192.169.0.0...192.169.255.255',
'192.170.0.0/15=192.170.0.0...192.171.255.255',
'192.172.0.0/14=192.172.0.0...192.175.255.255',
'192.176.0.0/12=192.176.0.0...192.191.255.255',
'192.192.0.0/10=192.192.0.0...192.255.255.255',
'193.0.0.0/8=193.0.0.0...193.255.255.255',
'194.0.0.0/7=194.0.0.0...195.255.255.255',
'196.0.0.0/6=196.0.0.0...199.255.255.255',
'200.0.0.0/5=200.0.0.0...207.255.255.255',
'208.0.0.0/4=208.0.0.0...223.255.255.255',
'224.0.0.0/4=224.0.0.0...239.255.255.255',
'240.0.0.0/5=240.0.0.0...247.255.255.255',
'248.0.0.0/6=248.0.0.0...251.255.255.255',
'252.0.0.0/7=252.0.0.0...253.255.255.255',
'254.0.0.0/8=254.0.0.0...254.255.255.255',
'255.0.0.0/9=255.0.0.0...255.127.255.255',
'255.128.0.0/10=255.128.0.0...255.191.255.255',
'255.192.0.0/11=255.192.0.0...255.223.255.255',
'255.224.0.0/12=255.224.0.0...255.239.255.255',
'255.240.0.0/13=255.240.0.0...255.247.255.255',
'255.248.0.0/14=255.248.0.0...255.251.255.255',
'255.252.0.0/15=255.252.0.0...255.253.255.255',
'255.254.0.0/16=255.254.0.0...255.254.255.255',
'255.255.0.0/17=255.255.0.0...255.255.127.255',
'255.255.128.0/18=255.255.128.0...255.255.191.255',
'255.255.192.0/19=255.255.192.0...255.255.223.255',
'255.255.224.0/20=255.255.224.0...255.255.239.255',
'255.255.240.0/21=255.255.240.0...255.255.247.255',
'255.255.248.0/22=255.255.248.0...255.255.251.255',
'255.255.252.0/23=255.255.252.0...255.255.253.255',
'255.255.254.0/24=255.255.254.0...255.255.254.255',
'255.255.255.0/25=255.255.255.0...255.255.255.127',
'255.255.255.128/26=255.255.255.128...255.255.255.191',
'255.255.255.192/27=255.255.255.192...255.255.255.223',
'255.255.255.224/28=255.255.255.224...255.255.255.239',
'255.255.255.240/29=255.255.255.240...255.255.255.247',
'255.255.255.248/30=255.255.255.248...255.255.255.251',
'255.255.255.252/31=255.255.255.252...255.255.255.253',
'255.255.255.254/32=255.255.255.254...255.255.255.254',
];
Example
We can test Jonavon's answer, like:
foreach ($validRangeMap as $entry) {
$expected = preg_split('/(=|(\.\.\.))+/', $entry);
$actual = cidrToRange($expected[0]);
if ($actual[0] !== $expected[1]) {
echo "Test-Failed! '$actual[0]' should be equal '$expected[1]' <br>" . PHP_EOL;
} else if ($actual[1] !== $expected[2]) {
echo "Test-Failed! '$actual[1]' should be equal '$expected[2]' <br>" . PHP_EOL;
} else {
echo "Tested '$expected[0]' CIDR <br>" . PHP_EOL;
}
}
function cidrToRange($cidr) {
$range = array();
$cidr = explode('/', $cidr);
$range[0] = long2ip((ip2long($cidr[0])) & ((-1 << (32 - (int)$cidr[1]))));
$range[1] = long2ip((ip2long($range[0])) + pow(2, (32 - (int)$cidr[1])) - 1);
return $range;
}
Note that it passed the tests.

The utility cidrl will do this:
$ cidrl 73.35.143.32/27
73.35.143.32
73.35.143.33
73.35.143.34
...
73.35.143.63

The PHP function cidrl() iterates over each IP address in a CIDR block using an anonymous function:
cidrl('194.168.0.1/28', $error_code, function($address) {
print "$address\n";
});
Or, as an array:
$addresses = cidrl('194.168.0.1/28', $error_code);
Can be installed from Packagist emden-norfolk/cidrl with Composer:
composer require emden-norfolk/cidrl
(Note: This package is no longer supported in favour of the cidrl command mentioned in my other answer.)

Hej. I also needed this function and I edit 1 other to return me list of all IP addresses from range.
This will return list of IP addresses from CIDR Notation. Enjoy it ;)
<?php
$ip_addr_cidr = "192.256.0.0/16";
$ip_arr = explode('/', $ip_addr_cidr);
$bin = '';
for($i=1;$i<=32;$i++) {
$bin .= $ip_arr[1] >= $i ? '1' : '0';
}
$ip_arr[1] = bindec($bin);
$ip = ip2long($ip_arr[0]);
$nm = ip2long($ip_arr[1]);
$nw = ($ip & $nm);
$bc = $nw | (~$nm);
echo "Number of Hosts: " . ($bc - $nw - 1) . "<br/>";
echo "Host Range: " . long2ip($nw + 1) . " -> " . long2ip($bc - 1) . "<br/>". "<br/>";
for($zm=1;($nw + $zm)<=($bc - 1);$zm++)
{
echo long2ip($nw + $zm). "<br/>";
}
?>

Fixed version of Kosmonaft script:
function get_list_ip($ip_addr_cidr){
$ip_arr = explode("/", $ip_addr_cidr);
$bin = "";
for($i=1;$i<=32;$i++) {
$bin .= $ip_arr[1] >= $i ? '1' : '0';
}
$ip_arr[1] = bindec($bin);
$ip = ip2long($ip_arr[0]);
$nm = $ip_arr[1];
$nw = ($ip & $nm);
$bc = $nw | ~$nm;
$bc_long = ip2long(long2ip($bc));
echo "Number of Hosts: " . ($bc_long - $nw - 1) . "<br/>";
echo "Host Range: " . long2ip($nw + 1) . " -> " . long2ip($bc - 1) . "<br/>". "<br/>";
for($zm=1;($nw + $zm)<=($bc_long - 1);$zm++)
{
echo long2ip($nw + $zm). "<br/>";
}
}

I had some problems with the previous versions, various PHP errors like undefined offset etc.
I found what I think is a better way to do it without the errors as detailed below.
This takes IPV4 CIDR notation IP addresses and can be used to get the start/end IP addresses of the CIDR range or count the total number of IP addresses in a database of IP addresses.
$count = 0;
$result = $sia_db_con->query("SELECT `ip_address` FROM `ip4` WHERE `ip_ban_active`='True' ORDER BY ip4.added ASC");
while ($row = $result->fetch_array()) {
$pos = strpos($row[0], "/");
$ip_arr = [0 => substr($row[0], 0, $pos), 1 => substr($row[0], $pos+1)];
$start = cidr2ip($ip_arr)[0];
$end = cidr2ip($ip_arr)[1];
$count = $count + (ip2long($end) - ip2long($start)) + 1;
}
unset($result, $row, $pos, $ip_arr, $start, $end, $range);
function cidr2ip($cidr) {
$start = ip2long($cidr[0]);
$nm = $cidr[1];
$num = pow(2, 32 - $nm);
$end = $start + $num - 1;
$range = [0 => $cidr[0], 1 => long2ip($end)];
unset($start, $nm, $num, $end);
return $range;
}
The secret here is the way in which I create the $ip_arr variable. Rather than using PHP explode, I set the array dimensions manually for each IP address. This is of course a database version of the code and cycles through all IP addresses in the database which meet the search criteria.

Related

How to check if an IPv6 address is in a specific network (for ACL implementation) in PHP

I have an ACL implementation in PHP that takes an array of IPv4 networks and checks if an address matches one of them, works like this:
$acl = array("192.168.0.0/24", "10.6.0.0/16");
if (address_in_range($_SERVER['REMOTE_ADDR'], $acl) {
// Allow the action...
}
The address_in_range() function I made works for IPv4, I did it like this:
list($subnet, $mask) = explode('/', $cidr);
if ((ip2long($ip) & ~((1 << (32 - $mask)) - 1) ) == ip2long($subnet)) {
return true;
}
However, I also need it to work with IPv6 addresses, so I can supply an array of mixed IPv4 and IPv6 ranges to my function, like this:
$acl = array("192.168.0.0/24", "10.6.0.0/16", "2a01:1098:15::/48");
And the checked IP can be either IPv4 or IPv6.
I ended up writing this function to do the job:
/**
* Check if an IP address matches an entry in an access control list (ACL)
* Returns true if match, false otherwise (including if $ip is not a valid IP
* address). Works with both IPv4 and IPv6 addresses.
*
* Example: check_acl("10.6.1.16", array("10.6.0.0/16","2a01:fe8:95::/48"));
* #param string $ip IP address to check
* #param array $acl Array of CIDR-notation IP addresses
* #return boolean
*/
function check_acl($ip, $acl) {
$ipb = inet_pton($ip);
$iplen = strlen($ipb);
if (strlen($ipb) < 4) {
// Invalid IP address
return false;
}
foreach ($acl as $cidr) {
$ar = explode('/',$cidr);
$ip1 = $ar[0];
$ip1b = inet_pton($ip1);
$ip1len = strlen($ip1b);
if ($ip1len != $iplen) {
// Different type
continue;
}
if (count($ar)>1) {
$bits=(int)($ar[1]);
} else {
$bits = $iplen * 8;
}
for ($c=0; $bits>0; $c++) {
$bytemask = ($bits < 8) ? 0xff ^ ((1 << (8-$bits))-1) : 0xff;
if (((ord($ipb[$c]) ^ ord($ip1b[$c])) & $bytemask) != 0)
continue 2;
$bits-=8;
}
return true;
}
return false;
}

how to compare the first three octets of two Ip adresses contained in two PhP variables

I have two php variables which contains two ipv4 addresses, i need to compare the first three octets and return true if they match, and false if they don't. Help for writing a block of code is appreciated.
<?php
include('adodb/adodb.inc.php');
mysql_connect("173.86.45,9","abcd","1236");
mysql_select_db("vc");
$pl=mysql_query("SELECT stat_ip from Hasoffers");
$count=mysql_num_rows($pl);
while($row=mysql_fetch_array($pl))
{
$stat_ip=$row['stat_ip'];
echo sec($stat_ip)."<br>";
}
function sec($stat_ip)
{
$result = mysql_query("select stat_ip from Hasoffers where stat_ip ='".$stat_ip."'");
if(condition to check if the octets match)
{
//i need to write the condition if within the table Hasoffers, there are more than 2 'stat_ip'(column) values, having the same 3 octets.
printf("true");
}
else
{
printf("false, octets don't match");
}
return $num_rows;
}
?>
Simple way to implement this is:
$ip1 = '192.168.0.1';
$ip2 = '192.168.0.2';
$ip1 = explode('.', $ip1);
$ip2 = explode('.', $ip2);
if ($ip1[0]==$ip2[0] && $ip1[1]==$ip2[1] && $ip1[2]==$ip2[2]) {
//your code here
}
EDIT:
Try to replace your sec() function with this one(read the comments),and edit it.
function sec($stat_ip)
{
$octets = explode('.', $stat_ip);
$first_three = $octets[0].'.'.$octets[1].'.'.$octets[2].'.'; //this looks like 192.168.0.
$result = mysql_query("SELECT stat_ip from Hasoffers where stat_ip LIKE '".$first_three."%'"); //this gives you all ip's starting with the current ip
if (mysql_num_rows($result)>1)
{
//we have more than one ip starting with current ip
//do something here
}
else
{
//result returns 1 or 0 rows, no matching ip's
}
//return $something;
}
The solution using strrpos and substr functions:
$ip1 = '192.168.10.121';
$ip2 = '192.168.10.122';
// the position of the last octet separator
$last_dot_pos = strrpos($ip1, '.');
$is_matched = substr($ip1, 0, $last_dot_pos) == substr($ip2, 0, $last_dot_pos);
var_dump($is_matched);
The output:
bool(true)
Use this code:
$ipOne = "192.168.1.1";
$ipTwo = "192.168.1.2";
$ipOneParts = explode(".", $ipOne);
$ipTwoParts = explode(".", $ipTwo);
if(($ipOneParts[0] == $ipTwoParts[0]) &&
($ipOneParts[1] == $ipTwoParts[1]) &&
($ipOneParts[2] == $ipTwoParts[2])){
return true;
} else {
return false;
}
convert them into array using explode using "." and compare the first index of both array.

How do I retrieve house number in magento street value via soap?

I have magento, and I'm posting a request via the soap v2 api to get the address of an order.
With that I get the following object which contains the street name + housenumber(God knows why these fields are not seperate...)
$shipping_address->street = "4th avenue 108";
Now what I want is to have the housenumber 108.
How do I get this house number without getting the 4?
(if someone has a more reliable function/piece of code than the one I post below, please feel free to post it.)
What you basically have to do is check for the first number occurence with a space in front of it.
This way you minimse the risk of fetching the wrong number:
// Code by Michael Dibbets
// shared under creative commons Attribution 3.0 Unported (CC BY 3.0 http://creativecommons.org/licenses/by/3.0/ )
$value = "4th street26";
$spl = str_split($value);
$pos = 0;
$location = 0;
// simple loop to find the first character with space + integer
foreach($spl as $char)
{
if(is_numeric($char) && $spl[$pos-1]==' ')
{
$location = $pos;
break;
}
$pos++;
}
// If we didn't encounter the space + integer combination
if(!$location)
{
// is the last character an integer? Assume that the last numbers are house numbers
if(is_numeric($spl[count($spl)-1]))
{
for($c=count($spl)-1;$c>0;$c--)
{
if(is_numeric($spl[$c]))
{
continue;
}
else
{
$location = $c+1;
break;
}
}
}
}
if($location)
{
$street = substr($value,0,$location);
$number = substr($value,$location);
}
else
{
$street = $value;
$number = null;
}
// echoing the results. The number should appear after the dash.
echo $street . ' - ' . $number;

$_SERVER['REMOTE_ADDR' string and changing subnets

I want to expand the IP range that my ~$_SERVER['REMOTE_ADDR'~ check for. The following works and only allows the 10.0.4.* subnet access to the site:
$chk = "10.0.4.";
if( substr($_SERVER['REMOTE_ADDR'],0,strlen($chk)) !== $chk)
$wgGroupPermissions['*']['read'] = false;
When I modify the $chk string to open the site to my entire local network I end up opening the site to the entire world.
$chk = "10.0.";
if( substr($_SERVER['REMOTE_ADDR'],0,strlen($chk)) !== $chk)
$wgGroupPermissions['*']['read'] = false;
I only want my local subnet 10.0.. to have read access to the site. How do I do this?
Using mask is a better way than spliting strings:
function testSubnet($ip, $subnet, $mask) {
$ip = ip2long($ip);
$subnet = ip2long($subnet);
$mask = ip2long($mask);
return ($ip & $mask) == ($subnet & $mask);
}
var_dump(testSubnet('10.0.0.1', '10.0.0.0', '255.255.255.0'));
var_dump(testSubnet('10.0.0.2', '10.0.0.0', '255.255.255.0'));
var_dump(testSubnet('10.0.1.1', '10.0.0.0', '255.255.255.0'));
var_dump(testSubnet('10.0.1.1', '10.0.0.0', '255.255.0.0'));
In this case:
if(testSubnet($_SERVER['REMOTE_ADDR'], '10.0.4.0', '255.255.255.0')) {
// Anything, blablabla...
}
Why can't you just do:
$z = $_SERVER['REMOTE_ADDR'];
function check($ip, $octet = 2) {
$allow = explode(".", "10.0.0.1");
$ipa = explode(".", $ip);
for($i = 0; $i < $octet; $i++) {
$ch .= $ipa[$i];
$ah .= $allow[$i];
}
return $ch == $ah;
}
echo check($z);
Where $octet is the amount of octets you want to match.. The default is 2.
You could use the header() function?
Try this.
$allowed_ip = "10.0.4";
if (!strstr($_SERVER['REMOTE_ADDR'],$allowed_ip))
header("Location: login.php");
// include("login.php");
/* The user will be redirected to another page or you could
always include your login.php page and just continue from there. */

Do you know a simple php script to throw 403, after ip check?

I have a script where payment processors come with payment confirmations.
To make the page secure, as it can access order information and other user related stuff, I had to limit the acces by ip(/24) as it follows:
$ipAllowed = array(
'192.192.192',
'172.172.172'
);
$ipAllowed = str_replace(".", "\.", implode("|", $ipAllowed));
if(!preg_match("/^($ipAllowed)\.[0-9]{1,3}$/", $_SERVER['REMOTE_ADDR'])){
header('HTTP/1.0 403 Forbidden');
die('You are not allowed to access this file.');
}
*the ip's are just as an example
Before i used:
if(!in_array(#$_SERVER['REMOTE_ADDR'], array('ips here'))); //only works with full ip
The !in_array was much neater then the one I use now, but i need something that works with /24 ips, or even with both!
Do you know something that works better/faster, is reliable and much neater?
#rap-2-h As you stated this is the neater version that works with full ip, /24 or even /16
$ipAllowed = array( '192.168.1.153' '172.172.172');
$allowed = false;
foreach($ipAllowed as $ip):
if(strpos($_SERVER['REMOTE_ADDR'], $ip) === 0) $allowed = true;
endforeach;
if (!$allowed) {
header('HTTP/1.0 403 Forbidden');
die('You are not allowed to access this file.');
}
You can try something like this :
$ipAllowed = array('192.192.192', '172.172.172');
$allowed = false;
foreach($ipAllowed as $ip) {
if (strpos($_SERVER['REMOTE_ADDR'], $ip) !== false) {
$allowed = true;
}
}
if (!$allowed) {
header('HTTP/1.0 403 Forbidden');
die('You are not allowed to access this file.');
}
So you can have only ip fragment in your $ipAllowed array.
It's not very elegant but it should work...
Use this function to check if you're ip is in Specified network :
eg: is 192.168.1.25 in network 192.168.1.0/24
<?php
/*
* ip_in_range.php - Function to determine if an IP is located in a
* specific range as specified via several alternative
* formats.
*
* 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
*
* Return value BOOLEAN : ip_in_range($ip, $range);
*
* Copyright 2008: Paul Gregg <pgregg#pgregg.com>
* 10 January 2008
* Version: 1.2
*
* Source website: http://www.pgregg.com/projects/php/ip_in_range/
* Version 1.2
*
* This software is Donationware - if you feel you have benefited from
* the use of this tool then please consider a donation. The value of
* which is entirely left up to your discretion.
* http://www.pgregg.com/donate/
*
* Please do not remove this header, or source attibution from this file.
*/
// decbin32
// In order to simplify working with IP addresses (in binary) and their
// netmasks, it is easier to ensure that the binary strings are padded
// with zeros out to 32 characters - IP addresses are 32 bit numbers
Function decbin32 ($dec) {
return str_pad(decbin($dec), 32, '0', STR_PAD_LEFT);
}
// ip_in_range
// This function takes 2 arguments, an IP address and a "range" in several
// different formats.
// 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
// The function will return true if the supplied IP is within the range.
// Note little validation is done on the range inputs - it expects you to
// use one of the above 3 formats.
Function ip_in_range($ip, $range) {
if (strpos($range, '/') !== false) {
// $range is in IP/NETMASK format
list($range, $netmask) = explode('/', $range, 2);
if (strpos($netmask, '.') !== false) {
// $netmask is a 255.255.0.0 format
$netmask = str_replace('*', '0', $netmask);
$netmask_dec = ip2long($netmask);
return ( (ip2long($ip) & $netmask_dec) == (ip2long($range) & $netmask_dec) );
} else {
// $netmask is a CIDR size block
// fix the range argument
$x = explode('.', $range);
while(count($x)<4) $x[] = '0';
list($a,$b,$c,$d) = $x;
$range = sprintf("%u.%u.%u.%u", empty($a)?'0':$a, empty($b)?'0':$b,empty($c)?'0':$c,empty($d)?'0':$d);
$range_dec = ip2long($range);
$ip_dec = ip2long($ip);
# Strategy 1 - Create the netmask with 'netmask' 1s and then fill it to 32 with 0s
#$netmask_dec = bindec(str_pad('', $netmask, '1') . str_pad('', 32-$netmask, '0'));
# Strategy 2 - Use math to create it
$wildcard_dec = pow(2, (32-$netmask)) - 1;
$netmask_dec = ~ $wildcard_dec;
return (($ip_dec & $netmask_dec) == ($range_dec & $netmask_dec));
}
} else {
// range might be 255.255.*.* or 1.2.3.0-1.2.3.255
if (strpos($range, '*') !==false) { // a.b.*.* format
// Just convert to A-B format by setting * to 0 for A and 255 for B
$lower = str_replace('*', '0', $range);
$upper = str_replace('*', '255', $range);
$range = "$lower-$upper";
}
if (strpos($range, '-')!==false) { // A-B format
list($lower, $upper) = explode('-', $range, 2);
$lower_dec = (float)sprintf("%u",ip2long($lower));
$upper_dec = (float)sprintf("%u",ip2long($upper));
$ip_dec = (float)sprintf("%u",ip2long($ip));
return ( ($ip_dec>=$lower_dec) && ($ip_dec<=$upper_dec) );
}
echo 'Range argument is not in 1.2.3.4/24 or 1.2.3.4/255.255.255.0 format';
return false;
}
}
?>
<?php
$ips = array(
'192.160.0',
'172.0.0'
);
/**
* Strip off the last number.
*/
$_SERVER['REMOTE_ADDR'] = '192.160.0.254';
$ip = preg_replace( '~\.(\d+)$~', '', $_SERVER['REMOTE_ADDR'] );
if( in_array( $ip, $ips ) ) {
var_dump( 'allowed' );
}

Categories