Skip to content

Instantly share code, notes, and snippets.

@Dethnull
Created May 14, 2015 21:51
Show Gist options
  • Save Dethnull/a6feeebee498a76241d5 to your computer and use it in GitHub Desktop.
Save Dethnull/a6feeebee498a76241d5 to your computer and use it in GitHub Desktop.
This is a helper library to deal with IPv4 and IPv6 address. At the moment it only checks if the passed IP is in the subnet of some predefined IP's
<?php
/**
* Author: Derek Ellison
* Date: 5/15/15
*
* Copyright (c) 2015 Derek Ellison
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
* to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of
* the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
* THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
class IPSanity {
// Constants used for constructor array keys
const IP_RANGE = 'iprange';
const IP_ADDRESS = 'ipaddress';
const IPV4 = 4;
const IPV6 = 6;
private static $debug = false;
private static $range4 = array(); // IPv4 Ranges
private static $range6 = array(); // IPv6 Ranges
private static $temp_range4 = array(); // Stores temp ranges for ipv4
private static $temp_range6 = array(); // Stores temp ranges for ipv6
private function __construct() {
} // prevent creating instance of class
public static function config($args = array()) {
if ($args[self::IP_RANGE]) {
self::setRange($args[self::IP_RANGE]);
}
}
public static function setRange($args = array(), $temp = false) {
foreach ($args as $range) {
if (strpos($range, '/') !== false) {
list($range, $netmask) = explode('/', $range, 2); // separate the range and the netmask
// check if range is ipv4 or 6
if (strpos($range, '.') !== false) {
//IPv4 assumed
if ($temp) {
self::$temp_range4[$range] = $netmask; // temp range
} else {
self::$range4[$range] = $netmask; // global range
}
} else if (strpos($range, ':') !== false) {
//IPv6 assumed
if ($temp) {
self::$temp_range6[$range] = $netmask; // temp range
} else {
self::$range6[$range] = $netmask; // global range
}
}
}
//TODO allow setting range as array(range => netmask) with netmask being in CIDR format or IP format
}
}
public static function inRange($ip, $range = null, $temp = true) {
if (isset($range)) {
if (is_array($range)) {
self::setRange($range, $temp);
} else {
self::setRange(array($range), $temp); // still add the range to our list if it's a string
}
}
if (strpos($ip, '.') !== false) {
// IPv4 address
$ranges = array_merge(self::$range4, self::$temp_range4); // check both global and temp
$inRange = false;
foreach ($ranges as $range => $netmask) {
//expand the netmask to valid form
$range_dec = ip2long(self::fixIPv4($range)); // ensure that the range is appropriate for our math
$ip_dec = ip2long(self::fixIPv4($ip)); // ensure that the ip is appropriate for our math
//$netmask_dec = bindec(str_pad('', $netmask, '1').str_pad('', (32-$netmask), '0'));
$netmask_dec = pow(2, (32 - $netmask)) - 1;
$netmask_dec = ~$netmask_dec;
if (($ip_dec & $netmask_dec) == ($range_dec & $netmask_dec)) {
return true;
}
}
} else if (strpos($ip, ':') !== false) {
$ranges = array_filter(array_merge(self::$range6, self::$temp_range6));
foreach ($ranges as $range => $netmask) {
if (self::check6range($ip, $range, $netmask) !== false) {
return true;
}
}
}
return false;
}
private static function check6range($ip, $range, $netmask) {
$bytes_addr = unpack("n*", inet_pton($range));
$bytes_test = unpack("n*", inet_pton($ip));
for ($i = 1; $i <= ceil($netmask / 16); $i++) {
$left = $netmask - 16 * ($i-1);
$left = ($left <= 16) ? $left : 16;
$mask = ~(0xffff >> $left) & 0xffff;
if (($bytes_addr[$i] & $mask) != ($bytes_test[$i] & $mask)) {
return false;
}
}
return true;
}
public static function fixIPv4($ip) {
$x = explode('.', $ip);
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);
return $range;
}
public static function debug($debug = true) {
self::$debug = $debug;
}
public static function dump_ip_range($version = null) {
if ($version === self::IPV4) {
var_dump(self::$range4);
} elseif ($version === self::IPV6) {
var_dump(self::$range6);
} else {
self::dump_ip_range(self::IPV4);
self::dump_ip_range(self::IPV6);
}
}
}
@Dethnull
Copy link
Author

Example use:

IPSanity::setRange(array(
  '10.0.15/24',
  '69.25.192/25'
));

IPSanity::inRange($YouIpHereToCheck); // returns true or false

I have plans to add more features to this library, but for now this does what I need it to.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment