'IP-addresses stored as int results in overflow?

I'm writing a chat-server in node.js, and I want to store connected users IP-addresses in a mysql database as (unsigned) integers. I have written a javascript method to convert an ip-address as string to an integer. I get some strange results however.

Here is my code:

function ipToInt(ip) {
    var parts = ip.split(".");
    var res = 0;

    res += parseInt(parts[0], 10) << 24;
    res += parseInt(parts[1], 10) << 16;
    res += parseInt(parts[2], 10) << 8;
    res += parseInt(parts[3], 10);

    return res;
}

When I run call the method as ipToInt("192.168.2.44"); the result I get is -1062731220. It seems like an overflow has occurred, which is strange, because the expected output (3232236076) is inside the number range in javascript (2^52).

When I inspect -1062731220 in binary form, I can see the 3232236076 is preserved, but filled with leading 1's.

I'm not sure, but I think the problem is with signed vs. unsigned integers.

Can any of you explain what is going on? And possibly how to parse -1062731220 back to an string ip?



Solution 1:[1]

The result of the "<<" operator is always a signed, 32-bit integer, as per the spec.

When you shift back, use ">>>" to do an unsigned right shift.

Solution 2:[2]

You might also find this pattern useful:

ip.toLong = function toInt(ip){
  var ipl=0;
  ip.split('.').forEach(function( octet ) {
      ipl<<=8;
      ipl+=parseInt(octet);
  });
  return(ipl >>>0);
};

ip.fromLong = function fromInt(ipl){
  return ( (ipl>>>24) +'.' +
      (ipl>>16 & 255) +'.' +
      (ipl>>8 & 255) +'.' +
      (ipl & 255) );
};

If you're using something like node.js where you can add functionality through something like Npm then you can simply do:

npm install ip

To get that functionality from the source which is here:
https://github.com/indutny/node-ip/blob/master/lib/ip.js

You will also get a bunch of other IP utility functions with that.

Solution 3:[3]

You shifted left to get the original number - which is just 4 sets of bits regardless of the sign.

Shift right to get back to the IP. Doesn't matter what the sign is.

Solution 4:[4]

const ip2int = (x) => (x.split('.').reduce((a, v) => ((a << 8) + (+v)), 0) >>> 0);

Solution 5:[5]

One-Liner:

const ipToLong = ip => ip.split('.').map(parseFloat).reduce((total, part) => total * 256 + part);

Solution 6:[6]

Use this

function num2string(ip) {
    return [24,16,8,0].map(n => (ip >> n) & 0xff).join(".")
}
function string2num(ip) {
    return ip.split(".").reduce((sum,x,i) => sum + (x << 8*(3-i)), 0)
}

Solution 7:[7]

IP Addresses in the V4 space are unsigned 32 bit numbers, hence the IP address of FF.FF.FF.FF is 2^32 and cannot be greater then that number. Please see:

This stack overflow article on the same subject

To turn that number back into an IP address you must break the number down into its 4 parts since each byte is one octet of the address so convert the number to hex and then parse out each pair. You may or may not have to add a leading zero for the first octet.

Additionally you may have to deal with byte order of the integer ( endien issues ) but since most systems are intel based these days you might not have to deal with that.

Solution 8:[8]

var aaa = Number("0b"+ "192.168.2.44".split(".").map(
  function(dec){
    return ("00000000" + Number(dec).toString(2)).slice(-8); 
  }).join(""));

aaa.toString(2).match(/.{1,8}/g).map(
  function(bin){
    return Number("0b"+bin); 
  }).join(".");

Solution 9:[9]

I revised Evan's final answer a bit, particularly dot2num. It functions the same but might be more readable and is marginally slower.

function ip2num(ip) {
    var parts = ip.split('.');

    var num = 0;
    num += d[0] * Math.pow(2, 24);
    num += d[1] * Math.pow(2, 16);
    num += d[2] * Math.pow(2, 8);
    num += d[3];

    return num;
}

function num2ip(num) {
    var ip = num % 256;

    for (var i=3; i > 0; i--) { 
        num = Math.floor(num / 256);
        ip = num % 256 + '.' + ip;
    }

    return ip;
}

Solution 10:[10]

Try this solution, it might help:

function IpToInteger(ipAddr)
{
    var parts = ipAddr.split('.');
    return (((parts[0] ? parts[0] << 24 : 0) |
             (parts[1] ? parts[1] << 16 : 0) |
             (parts[2] ? parts[2] << 8  : 0) |
             (parts[3])) >>> 0);
}

Solution 11:[11]

function IpAddressToLong(ip){
  return ip.split('.').map((octet, index, array) => {
      return parseInt(octet) * Math.pow(256, (array.length - index - 1));
    }).reduce((prev, curr) => {
      return prev + curr;
  });
}

Taken from repo

Solution 12:[12]

function ip2num(ip) {
  var d = ip.split(".");

  var num = 0;
  num += Number(d[0]) * Math.pow(256, 3);
  num += Number(d[1]) * Math.pow(256, 2);
  num += Number(d[2]) * Math.pow(256, 1);
  num += Number(d[3]);

  return num;
}

function num2ip(num) {
  var ip = num % 256;

  for (var i = 3; i > 0; i--) {
    num = Math.floor(num / 256);
    ip = (num % 256) + "." + ip;
  }

  return ip;
}

console.log(ip2num("192.168.0.1"));
console.log(num2ip(3232235521))
<h1>YOU IS WELCOME</h1>