1<?php 2/** 3* Class to provide IPv4 calculations 4* 5* PHP versions 4 and 5 6* 7* LICENSE: This source file is subject to version 3.01 of the PHP license 8* that is available through the world-wide-web at the following URI: 9* http://www.php.net/license/3_01.txt. If you did not receive a copy of 10* the PHP License and are unable to obtain it through the web, please 11* send a note to license@php.net so we can mail you a copy immediately. 12* 13* @category Net 14* @package Net_IPv4 15* @author Eric Kilfoil <edk@ypass.net> 16* @author Marco Kaiser <bate@php.net> 17* @author Florian Anderiasch <fa@php.net> 18* @copyright 1997-2005 The PHP Group 19* @license http://www.php.net/license/3_01.txt PHP License 3.01 20* @version CVS: $Id: IPv4.php 302879 2010-08-30 06:52:41Z bate $ 21* @link http://pear.php.net/package/Net_IPv4 22*/ 23 24require_once 'PEAR.php'; 25 26// {{{ GLOBALS 27/** 28 * Map of bitmasks to subnets 29 * 30 * This array contains every valid netmask. The index of the dot quad 31 * netmask value is the corresponding CIDR notation (bitmask). 32 * 33 * @global array $GLOBALS['Net_IPv4_Netmask_Map'] 34 */ 35$GLOBALS['Net_IPv4_Netmask_Map'] = array( 36 0 => "0.0.0.0", 37 1 => "128.0.0.0", 38 2 => "192.0.0.0", 39 3 => "224.0.0.0", 40 4 => "240.0.0.0", 41 5 => "248.0.0.0", 42 6 => "252.0.0.0", 43 7 => "254.0.0.0", 44 8 => "255.0.0.0", 45 9 => "255.128.0.0", 46 10 => "255.192.0.0", 47 11 => "255.224.0.0", 48 12 => "255.240.0.0", 49 13 => "255.248.0.0", 50 14 => "255.252.0.0", 51 15 => "255.254.0.0", 52 16 => "255.255.0.0", 53 17 => "255.255.128.0", 54 18 => "255.255.192.0", 55 19 => "255.255.224.0", 56 20 => "255.255.240.0", 57 21 => "255.255.248.0", 58 22 => "255.255.252.0", 59 23 => "255.255.254.0", 60 24 => "255.255.255.0", 61 25 => "255.255.255.128", 62 26 => "255.255.255.192", 63 27 => "255.255.255.224", 64 28 => "255.255.255.240", 65 29 => "255.255.255.248", 66 30 => "255.255.255.252", 67 31 => "255.255.255.254", 68 32 => "255.255.255.255" 69 ); 70// }}} 71// {{{ Net_IPv4 72 73/** 74* Class to provide IPv4 calculations 75* 76* Provides methods for validating IP addresses, calculating netmasks, 77* broadcast addresses, network addresses, conversion routines, etc. 78* 79* @category Net 80* @package Net_IPv4 81* @author Eric Kilfoil <edk@ypass.net> 82* @author Marco Kaiser <bate@php.net> 83* @author Florian Anderiasch <fa@php.net> 84* @copyright 1997-2005 The PHP Group 85* @license http://www.php.net/license/3_01.txt PHP License 3.01 86* @version CVS: @package_version@ 87* @link http://pear.php.net/package/Net_IPv4 88* @access public 89*/ 90class Net_IPv4 91{ 92 // {{{ properties 93 var $ip = ""; 94 var $bitmask = false; 95 var $netmask = ""; 96 var $network = ""; 97 var $broadcast = ""; 98 var $long = 0; 99 100 //pear 101 public $pear; 102 103 104 //initialize PEAR object on init 105 public function __construct () { 106 $this->pear = new PEAR (); 107 } 108 109 // }}} 110 // {{{ validateIP() 111 112 /** 113 * Validate the syntax of the given IP adress 114 * 115 * Using the PHP long2ip() and ip2long() functions, convert the IP 116 * address from a string to a long and back. If the original still 117 * matches the converted IP address, it's a valid address. This 118 * function does not allow for IP addresses to be formatted as long 119 * integers. 120 * 121 * @param string $ip IP address in the format x.x.x.x 122 * @return bool true if syntax is valid, otherwise false 123 */ 124 function validateIP($ip) 125 { 126 if ($ip == long2ip(ip2long($ip))) { 127 return true; 128 } else { 129 return false; 130 } 131 } 132 133 // }}} 134 // {{{ check_ip() 135 136 /** 137 * Validate the syntax of the given IP address (compatibility) 138 * 139 * This function is identical to Net_IPv4::validateIP(). It is included 140 * merely for compatibility reasons. 141 * 142 * @param string $ip IP address 143 * @return bool true if syntax is valid, otherwise false 144 */ 145 function check_ip($ip) 146 { 147 return $this->validateIP($ip); 148 } 149 150 // }}} 151 // {{{ validateNetmask() 152 153 /** 154 * Validate the syntax of a four octet netmask 155 * 156 * There are 33 valid netmask values. This function will compare the 157 * string passed as $netmask to the predefined 33 values and return 158 * true or false. This is most likely much faster than performing the 159 * calculation to determine the validity of the netmask. 160 * 161 * @param string $netmask Netmask 162 * @return bool true if syntax is valid, otherwise false 163 */ 164 function validateNetmask($netmask) 165 { 166 if (! in_array($netmask, $GLOBALS['Net_IPv4_Netmask_Map'])) { 167 return false; 168 } 169 return true; 170 } 171 172 // }}} 173 // {{{ parseAddress() 174 175 /** 176 * Parse a formatted IP address 177 * 178 * Given a network qualified IP address, attempt to parse out the parts 179 * and calculate qualities of the address. 180 * 181 * The following formats are possible: 182 * 183 * [dot quad ip]/[ bitmask ] 184 * [dot quad ip]/[ dot quad netmask ] 185 * [dot quad ip]/[ hex string netmask ] 186 * 187 * The first would be [IP Address]/[BitMask]: 188 * 192.168.0.0/16 189 * 190 * The second would be [IP Address] [Subnet Mask in dot quad notation]: 191 * 192.168.0.0/255.255.0.0 192 * 193 * The third would be [IP Address] [Subnet Mask as Hex string] 194 * 192.168.0.0/ffff0000 195 * 196 * Usage: 197 * 198 * $cidr = '192.168.0.50/16'; 199 * $net = Net_IPv4::parseAddress($cidr); 200 * echo $net->network; // 192.168.0.0 201 * echo $net->ip; // 192.168.0.50 202 * echo $net->broadcast; // 192.168.255.255 203 * echo $net->bitmask; // 16 204 * echo $net->long; // 3232235520 (long/double version of 192.168.0.50) 205 * echo $net->netmask; // 255.255.0.0 206 * 207 * @param string $ip IP address netmask combination 208 * @return object true if syntax is valid, otherwise false 209 */ 210 function parseAddress($address) 211 { 212 $myself = new Net_IPv4; 213 214 // ctype fix 215 if(!function_exists('ctype_digit')) { 216 function ctype_digit ($int) { 217 return is_numeric($int); 218 } 219 } 220 221 if (strchr($address, "/")) { 222 $parts = explode("/", $address); 223 if (! $myself->validateIP($parts[0])) { 224 return $this->pear->raiseError("invalid IP address"); 225 } 226 $myself->ip = $parts[0]; 227 228 // Check the style of netmask that was entered 229 /* 230 * a hexadecimal string was entered 231 */ 232 if (preg_match("/^([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i", $parts[1], $regs)) { 233 // hexadecimal string 234 $myself->netmask = hexdec($regs[1]) . "." . hexdec($regs[2]) . "." . 235 hexdec($regs[3]) . "." . hexdec($regs[4]); 236 237 /* 238 * a standard dot quad netmask was entered. 239 */ 240 } else if (strchr($parts[1], ".")) { 241 if (! $myself->validateNetmask($parts[1])) { 242 return $this->pear->raiseError("invalid netmask value"); 243 } 244 $myself->netmask = $parts[1]; 245 246 /* 247 * a CIDR bitmask type was entered 248 */ 249 } else if (ctype_digit($parts[1]) && $parts[1] >= 0 && $parts[1] <= 32) { 250 // bitmask was entered 251 $myself->bitmask = $parts[1]; 252 253 /* 254 * Some unknown format of netmask was entered 255 */ 256 } else { 257 return $this->pear->raiseError("invalid netmask value"); 258 } 259 $myself->calculate(); 260 return $myself; 261 } else if ($myself->validateIP($address)) { 262 $myself->ip = $address; 263 return $myself; 264 } else { 265 return $this->pear->raiseError("invalid IP address"); 266 } 267 } 268 269 // }}} 270 // {{{ calculate() 271 272 /** 273 * Calculates network information based on an IP address and netmask. 274 * 275 * Fully populates the object properties based on the IP address and 276 * netmask/bitmask properties. Once these two fields are populated, 277 * calculate() will perform calculations to determine the network and 278 * broadcast address of the network. 279 * 280 * @return mixed true if no errors occured, otherwise PEAR_Error object 281 */ 282 function calculate() 283 { 284 $validNM = $GLOBALS['Net_IPv4_Netmask_Map']; 285 286 if (! is_a($this, "net_ipv4")) { 287 $myself = new Net_IPv4; 288 return $this->pear->raiseError("cannot calculate on uninstantiated Net_IPv4 class"); 289 } 290 291 /* Find out if we were given an ip address in dot quad notation or 292 * a network long ip address. Whichever was given, populate the 293 * other field 294 */ 295 if (strlen($this->ip)) { 296 if (! $this->validateIP($this->ip)) { 297 return $this->pear->raiseError("invalid IP address"); 298 } 299 $this->long = $this->ip2double($this->ip); 300 } else if (is_numeric($this->long)) { 301 $this->ip = long2ip($this->long); 302 } else { 303 return $this->pear->raiseError("ip address not specified"); 304 } 305 306 /* 307 * Check to see if we were supplied with a bitmask or a netmask. 308 * Populate the other field as needed. 309 */ 310 if (strlen($this->bitmask)) { 311 $this->netmask = $validNM[$this->bitmask]; 312 } else if (strlen($this->netmask)) { 313 $validNM_rev = array_flip($validNM); 314 $this->bitmask = $validNM_rev[$this->netmask]; 315 } else { 316 return $this->pear->raiseError("netmask or bitmask are required for calculation"); 317 } 318 $this->network = long2ip(ip2long($this->ip) & ip2long($this->netmask)); 319 $this->broadcast = long2ip(ip2long($this->ip) | 320 (ip2long($this->netmask) ^ ip2long("255.255.255.255"))); 321 return true; 322 } 323 324 // }}} 325 // {{{ getNetmask() 326 327 function getNetmask($length) 328 { 329 if (! PEAR::isError($ipobj = Net_IPv4::parseAddress("0.0.0.0/" . $length))) { 330 $mask = $ipobj->netmask; 331 unset($ipobj); 332 return $mask; 333 } 334 return false; 335 } 336 337 // }}} 338 // {{{ getNetLength() 339 340 function getNetLength($netmask) 341 { 342 if (! PEAR::isError($ipobj = Net_IPv4::parseAddress("0.0.0.0/" . $netmask))) { 343 $bitmask = $ipobj->bitmask; 344 unset($ipobj); 345 return $bitmask; 346 } 347 return false; 348 } 349 350 // }}} 351 // {{{ getSubnet() 352 353 function getSubnet($ip, $netmask) 354 { 355 if (! PEAR::isError($ipobj = Net_IPv4::parseAddress($ip . "/" . $netmask))) { 356 $net = $ipobj->network; 357 unset($ipobj); 358 return $net; 359 } 360 return false; 361 } 362 363 // }}} 364 // {{{ inSameSubnet() 365 366 function inSameSubnet($ip1, $ip2) 367 { 368 if (! is_object($ip1) || strcasecmp(get_class($ip1), 'net_ipv4') <> 0) { 369 $ipobj1 = Net_IPv4::parseAddress($ip1); 370 if (PEAR::isError($ipobj)) { 371 return $this->pear->raiseError("IP addresses must be an understood format or a Net_IPv4 object"); 372 } 373 } 374 if (! is_object($ip2) || strcasecmp(get_class($ip2), 'net_ipv4') <> 0) { 375 $ipobj2 = Net_IPv4::parseAddress($ip2); 376 if (PEAR::isError($ipobj)) { 377 return $this->pear->raiseError("IP addresses must be an understood format or a Net_IPv4 object"); 378 } 379 } 380 if ($ipobj1->network == $ipobj2->network && 381 $ipobj1->bitmask == $ipobj2->bitmask) { 382 return true; 383 } 384 return false; 385 } 386 387 // }}} 388 // {{{ atoh() 389 390 /** 391 * Converts a dot-quad formatted IP address into a hexadecimal string 392 * @param string $addr IP-adress in dot-quad format 393 * @return mixed false if invalid IP and hexadecimal representation as string if valid 394 */ 395 function atoh($addr) 396 { 397 if (! Net_IPv4::validateIP($addr)) { 398 return false; 399 } 400 $ap = explode(".", $addr); 401 return sprintf("%02x%02x%02x%02x", $ap[0], $ap[1], $ap[2], $ap[3]); 402 } 403 404 // }}} 405 // {{{ htoa() 406 407 /** 408 * Converts a hexadecimal string into a dot-quad formatted IP address 409 * @param string $addr IP-adress in hexadecimal format 410 * @return mixed false if invalid IP and dot-quad formatted IP as string if valid 411 */ 412 function htoa($addr) 413 { 414 if (preg_match("/^([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i", 415 $addr, $regs)) { 416 return hexdec($regs[1]) . "." . hexdec($regs[2]) . "." . 417 hexdec($regs[3]) . "." . hexdec($regs[4]); 418 } 419 return false; 420 } 421 422 // }}} 423 // {{{ ip2double() 424 425 /** 426 * Converts an IP address to a PHP double. Better than ip2long because 427 * a long in PHP is a signed integer. 428 * @param string $ip dot-quad formatted IP adress 429 * @return float IP adress as double - positive value unlike ip2long 430 */ 431 function ip2double($ip) 432 { 433 return (double)(sprintf("%u", ip2long($ip))); 434 } 435 436 // }}} 437 // {{{ ipInNetwork() 438 439 /** 440 * Determines whether or not the supplied IP is within the supplied network. 441 * 442 * This function determines whether an IP address is within a network. 443 * The IP address ($ip) must be supplied in dot-quad format, and the 444 * network ($network) may be either a string containing a CIDR 445 * formatted network definition, or a Net_IPv4 object. 446 * 447 * @param string $ip A dot quad representation of an IP address 448 * @param string $network A string representing the network in CIDR format or a Net_IPv4 object. 449 * @return bool true if the IP address exists within the network 450 */ 451 function ipInNetwork($ip, $network) 452 { 453 if (! is_object($network) || strcasecmp(get_class($network), 'net_ipv4') <> 0) { 454 $network = Net_IPv4::parseAddress($network); 455 } 456 if (strcasecmp(get_class($network), 'pear_error') === 0) { 457 return false; 458 } 459 $net = Net_IPv4::ip2double($network->network); 460 $bcast = Net_IPv4::ip2double($network->broadcast); 461 $ip = Net_IPv4::ip2double($ip); 462 unset($network); 463 if ($ip >= $net && $ip <= $bcast) { 464 return true; 465 } 466 return false; 467 } 468 469 // }}} 470} 471 472// }}} 473 474/* 475 * vim: sts=4 ts=4 sw=4 cindent fdm=marker 476 */ 477?> 478