tags:

views:

186

answers:

3

Using JavaScript how would I validate an IP address "x.x.x.x" is a valid IPV4 unicast address e.g. is not 0.0.0.0 or multicast (224.0.0.0 to 224.0.0.255, 224.0.1.0 to 238.255.255.255, 239.0.0.0 to 239.255.255.255)?

+3  A: 

First you need to get it into a number, use this function:-

function IPToNumber(s)
{
 var arr = s.split(".");
 var n = 0
 for (var i = 0; i < 4; i++)
 {
  n = n * 256
  n += parseInt(arr[i],10)

 }
 return n;
}

Looking at you spec, whilst you seem to list a series of ranges those ranges appear to be contiguous to me, that is can be simplified to (224.0.0.0 to 239.255.255.255). Hence you can test with:-

var min = IPToNumber("224.0.0.0");
var max = IPToNumber("239.255.255.255");

var ipNum = IPToNumber(sTestIP);

var isValid = (ipNum != 0 && (ipNum < min || ipNum > max))

Note of course that without knowledge of the destinations subnet you can't tell whether the address is the network address or the broadcast address for that subnet.

AnthonyWJones
Your use of `*` instead of bitwise operators means your numbers shouldn't be cast to `Int32` at any point - clever!
Alex Barrett
Be careful of something like this, which is valid: 70.16.18.015. Your parseInt call might not treat everything as decimal.
Joel Coehoorn
A quick test using 0.0.0.015 confirmed the bug. I fixed your code.
Joel Coehoorn
Yeah... Those stupid octal strings. Who ever needs them anyway?
Ates Goral
@Joel: Ouch! well spotted ;)
AnthonyWJones
+2  A: 

The dotted quad notation you're seeing is just that: a notation that makes it easier on human eyes. An IP Address is really a 32 bit integer. I suggest you convert your address to that integer and then just check that it's in a valid range (ie, > 0 for the first requirement).

To that end:

function dottedQuadToInt(ip)
{      
    var parts = ip.split('.', 4);
    var result = 0, base = 1;
    for (var i = 3;i>=0;i--)
    {
       //validation
       if (parts[i].length == 0 || parts[i].length > 3) return -1;

       var segment = parseInt(parts[i],10);
       if (isNaN(segment) || segment<0 || segment > 255) return -1;

       //compute next segment
       result += base * segment;
       base = base << 8;
    }
    return result;
}

and then:

function isValidIP(ip)
{
   ip = dottedQuadToInt(ip);
   if (ip <= 0) return false;

   //mulitcast range:
   if (ip >= 3758096384 && ip <= 4026531839) return false;

   //alternate way to check multicast:
   if (ip >= dottedQuadToInt('224.0.0.0') && ip <= dottedQuadToInt('239.255.255.255')) return false;

   return true;
}
Joel Coehoorn
The << 8 is equivalent to * 256, and look again at my for loop.
Joel Coehoorn
Oh yes I see, I didn't spot you are shifting base not the value itself.
AnthonyWJones
+1  A: 
/**
 * Converts an IPv4 address to a (signed) 32-bit integer.
 */
function parse_ipv4_address(str) {
    var arr = str.split('.');
    for (var i = 0, val = 0; i < 4; i++) {
        val += parseInt(arr[i], 10) << (8 * (3 - i));
    }

    return val;
}

var min = parse_ipv4_address('224.0.0.0');
var max = parse_ipv4_address('239.255.255.255');

var myIp = parse_ipv4_address(myIpStr);

// because the values are signed ints, min and max will be
// negative and we need to reverse the comparison operators :(
if (myIp == 0 || (myIp <= min && myIp >= max)) {
    // multicast!
}
Alex Barrett
I like that 3 of us posted pretty much the exact same thing. I will vote you both up :)
Alex Barrett