IPv4 format validation

Small, short code snippets that other people may find useful. Do you have a good regex that you would like to share? Share it! Even better, the code can be commented on, and improved.

Moderator: General Moderators

Post Reply
redmonkey
Forum Regular
Posts: 836
Joined: Thu Dec 18, 2003 3:58 pm

IPv4 format validation

Post by redmonkey »

Where would this live? here or code snippets?

Code: Select all

<?php
/**
* @return bool          true on success, false on failure
* @param  string  ip    an IP address
* @desc                 validates the format of $ip string against IPv4
*                       dotted quad format
*/
function is_ipv4($ip)
{
  return preg_match('/^(?:(25[0-5]|2[0-4]\d|(1[0-9]{2})?|[1-9]?\d)\.){3}(25[0-5]|2[0-4]\d|(1[0-9]{2})?|[1-9]?\d)$/', $ip) ? true : false;

  /**
  * an alternative method, should probably test which one is the more efficient
  * a single regex vs (a quick regex sanity check followed by a single explode
  * to a four loop comparison check).
  */

  /*
  if (!preg_match('/^([0-9]{1,3}\.){3}[0-9]{1,3}$/', $ip))
  {
    return false;
  }

  for ($i = 0, $ip = explode('.', $ip); $i < 4; $i++)
  {
    if ($ip[$i] > 0xff)
    {
      return false;
    }
  }

  return true;
  */
}
?>
Test with...

Code: Select all

echo is_ipv4('255.113.0.234') ? "Valid\x0a" : "Invalid\x0a";
Not fully tested yet, I'm currently chunking through it with this highly amusing test......

Code: Select all

for ($i = 0; $i < 0xffffffff; $i++)
{
  $hex = pack('V', $i);
  $dec = unpack('vmsb/vlsb', $hex);
  $ip[3] = 0x00ff & $dec['msb'];
  $ip[2] = $dec['msb'] >> 8;
  $ip[1] = 0x00ff & $dec['lsb'];
  $ip[0] = $dec['lsb'] >> 8;
  echo "Testing IP : {$ip[0]}.{$ip[1]}.{$ip[2]}.{$ip[3]} ";
  echo is_ipv4("{$ip[0]}.{$ip[1]}.{$ip[2]}.{$ip[3]}") ? "Passed\x0a" : "Failed\x0a";
}
It's taking some time not surprisingly :lol:
User avatar
feyd
Neighborhood Spidermoddy
Posts: 31559
Joined: Mon Mar 29, 2004 3:24 pm
Location: Bothell, Washington, USA

Re: IPv4 format validation

Post by feyd »

redmonkey wrote:Where would this live? here or code snippets?
snippets.
TJ
Forum Newbie
Posts: 20
Joined: Thu Nov 03, 2005 10:22 pm
Location: Nottingham, UK

Post by TJ »

This got me to thinking about the way we often forget that the various representations of an IP address are all just ways of writing a decimal value that can be up to 4294967295 (largest unsigned value in 32 bits).

I sometimes like confusing friends by giving them addresses like this to visit

http://1073170174/viewtopic.php?t=40333

(will work in most browsers, but ensure you're not accessing via a proxy that treats is as a DNS name)

Just for the fun of it, here are four functions that test an IP, with decreasing levels of clarity and increasing levels of compactness, and one that returns HTML with a report on the validity of a dotted-quad IP address and highlights any illegal elements.

Its interesting that with dotted-quad theres also the hex-based validity test:

1. Convert each decimal element to its hex representation
2. If an element has more than 2 hex digits, its illegal (useful to save multiplication)
3. If there are not exactly 4 elements, its illegal

In the code below, its called isValidIpv4_Hex().

Code: Select all

<?php
function isValidIPv4_Hex($dottedQuad) {
    preg_match('/(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/', $dottedQuad, $quads = array());
    foreach($quads as $element) if (strlen(dechex($element)) > 2) return false;
    return (count($quads)==5);
}

function isValidIPv4_A($dottedQuad) {
    $ret = true; // try to disprove this
    $regexp = '/(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/';
    $quads = array();
    $count = preg_match($regexp, $dottedQuad, $quads);
    $maxVal = 0;
    $decVal = 0;
    if (count($quads)==5) {
      for($i=1;$i<=4;$i++) {
        $multiplier = pow(2,8*(5-$i)-8);
        $decVal = $multiplier * $quads[$i];
        $maxVal = $multiplier * 255;
        $ret = ($decVal > $maxVal ? false : $ret);
      }
    }
    else $ret = false;
    return $ret;
}

function isValidIPv4_B($dottedQuad) {
    $regexp = '/(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/';
    $quads = array();
    $count = preg_match($regexp, $dottedQuad, $quads);
    return (($count==0 || ($quads[1]>255 || $quads[2]>255 || $quads[3]>255 || $quads[4]>255) || ($quads[1]*16777216 + $quads[2]*65536 + $quads[3]*256 + $quads[4]*1 >= 4294967296)) ? false : true);
}

function isValidIPv4_C($dottedQuad) {
    $count = preg_match('/(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/', $dottedQuad, $quads = array());
    return (($count==0 || ($quads[1]>255 || $quads[2]>255 || $quads[3]>255 || $quads[4]>255) || ($quads[1]*16777216 + $quads[2]*65536 + $quads[3]*256 + $quads[4] >= 4294967296)) ? false : true);
}

function isValidIPv4HTML($dottedQuad) {
    $ret = '<style type="text/css">.error{color:red;font-weight:bold;}</style>';
    $regexp = '/(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/';
    $quads = array();
    $count = preg_match($regexp, $dottedQuad, $quads);
    $validFlag = true; // assume ok unles proved otherwise
    $maxVal = 0;
    $decVal = 0;
    if (count($quads)==5) {
      for($i=1;$i<=4;$i++) {
        $multiplier = (8*(5-$i)-8)+1;
        $decVal = pow(2,$multiplier) * $quads[$i];
        $maxVal = pow(2,$multiplier) * 255;
        $validFlag = ($decVal > $maxVal ? false : $validFlag);
        $ret .= '<span class="'.($decVal <= $maxVal ? '' : 'error').'">'.$quads[$i].'</span>'.($i<4 ? '.' : '');
      }
      $ret .= " (".(count($quads)-1)." quads) is ". (($count!=0 && $validFlag) ? 'valid' : 'invalid');
    }
    else $ret .= $dottedQuad.'<span class="error"> has illegal format';
    return $ret;
}

$ip = "63.247.70.256";

echo "isValidIPv4_A($ip)=".sprintf('%d',isValidIPv4_A($ip))."<br/>\r\n";
echo "isValidIPv4_B($ip)=".sprintf('%d',isValidIPv4_B($ip))."<br/>\r\n";
echo "isValidIPv4_C($ip)=".sprintf('%d',isValidIPv4_C($ip))."<br/>\r\n";
echo "isValidIPv4_Hex($ip)=".sprintf('%d',isValidIPv4_Hex($ip))."<br/>\r\n";
echo "isValidIPv4HTML($ip) ".isValidIPv4HTML($ip)."<br/>\r\n";
?>
User avatar
Weirdan
Moderator
Posts: 5978
Joined: Mon Nov 03, 2003 6:13 pm
Location: Odessa, Ukraine

Post by Weirdan »

and some really simple alternative:

Code: Select all

function isValidIPv4_A($ip) {
   return ((ip2long($ip) != -1) && (ip2long($ip) !== false));
}
redmonkey
Forum Regular
Posts: 836
Joined: Thu Dec 18, 2003 3:58 pm

Post by redmonkey »

Weirdan wrote:and some really simple alternative:

Code: Select all

function isValidIPv4_A($ip) {
   return ((ip2long($ip) != -1) && (ip2long($ip) !== false));
}

Code: Select all

echo isValidIPv4_A('127') ? 'yes' : 'no';
outputs 'yes', the other snippets presented are checking against a full dotted quad string representation of an IP address.
redmonkey
Forum Regular
Posts: 836
Joined: Thu Dec 18, 2003 3:58 pm

Post by redmonkey »

TJ wrote:In the code below, its called isValidIpv4_Hex().
I pondered this for a while as I couldn't quite decide what was technically correct (I should probably spend some time reading more of the specs).

The problem is *most* regex based validators only check the pattern for a combination of any digit with a max length of 3 repeated 4 times with a dot in between. And as I'm sure you are aware this means addresses like 222.300.555.10 would pass.

The next problem (and this is where I couldn't decide) is that all other functions (and I include my alternative commented function above in this) pass addresses like 234.010.000.5 now each octet is supposed to be a decimal representation, in my mind the true decimal representation of '010' is 10 similarly the true decimal representation of '000' is 0. So, my function returns false for any address with an octet with a leading zero.
Roja
Tutorials Group
Posts: 2692
Joined: Sun Jan 04, 2004 10:30 pm

Post by Roja »

redmonkey wrote:So, my function returns false for any address with an octet with a leading zero.
In more than one situation, you can end up with leading zeroes - depending on where you get the IP address from in the first place.

I wouldn't say a leading zero makes it invalid, but then again, if you want to get really pedantic, you'd need to filter out broadcast (1.1.1.1), wire (0.0.0.0), and so forth.

It all depends on where the ip address is coming from, and what you are trying to validate.
TJ
Forum Newbie
Posts: 20
Joined: Thu Nov 03, 2005 10:22 pm
Location: Nottingham, UK

Post by TJ »

Using ip2long() is cheating though :lol:

I was enjoying trying to write the code that usually sits under the hood, rather than looking for another way to do it in PHP.

Its fascinating (to me at least) that what looks so simple can take so much thought and be so intriguing. Anyhow it gave me a nice hours distraction this morning :D
redmonkey
Forum Regular
Posts: 836
Joined: Thu Dec 18, 2003 3:58 pm

Post by redmonkey »

Roja wrote:In more than one situation, you can end up with leading zeroes - depending on where you get the IP address from in the first place.
In what situations would this format of IP address come from? In no sys admin or network guru, in my (admitedly limited) exposure to networks I've never seen any IP being reported with leading zeros. I'm not saying it doesn't or can't happen, I'm just curious as to which situations they may arise in, as perhaps I may need to rethink and allow IPs with leading zeros.

I also had to revise my regex as I noticed it was passing addresses like '127.0..1' and '127.0.0.' so revised function....

Code: Select all

<?php
/**
* @return bool          true on success, false on failure
* @param  string  ip    an IP address
* @desc                 validates the format of $ip string against IPv4
*                       dotted quad format
*/
function is_ipv4($ip)
{
  return preg_match('/^(?:(?:25[0-5]|2[0-4]\d|(?:(?:1\d)?|[1-9]?)\d)\.){3}(?:25[0-5]|2[0-4]\d|(?:(?:1\d)?|[1-9]?)\d)$/', $ip) ? true : false;
}
?>
Roja
Tutorials Group
Posts: 2692
Joined: Sun Jan 04, 2004 10:30 pm

Post by Roja »

redmonkey wrote:In what situations would this format of IP address come from?
Automated systems that convert dotted quads to decimals and make sure to fill every digit - perhaps a proxy, cache, or other software. User input, admin input, basically any of the sources for ip addresses other than an actual network route itself. Even a network route on an odd operating system might..

Technically speaking, the leading zero isnt invalid, its just needlessly formatted.
redmonkey
Forum Regular
Posts: 836
Joined: Thu Dec 18, 2003 3:58 pm

Post by redmonkey »

Thanks, I went with a, by default allow leading zeros unless the optional 'strict' flag/option is passed type implementation.
Post Reply