21

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?

cpx
  • 17,009
  • 20
  • 87
  • 142
JPuge
  • 586
  • 1
  • 5
  • 14
  • 2
    Your whole ipToInt function could be replaced with: `new Buffer(ip.split('.')).readInt32BE(0)` – Nikolai Aug 01 '13 at 18:25
  • 2
    Regarding Nikolai's solution - Under certain conditions this throws RangeError('Trying to access beyond buffer length') – Adria May 25 '14 at 00:39

13 Answers13

51

Why is the converted IP negative?

It's NOT an overflow. The first part of your IP address is 192 which converts to 11000000 in binary. You then shift that all the way to the left. When there is a 1 in the leftmost position of a 32 bit number, it's negative.

How do you convert back to a string?

Do the same thing you did to convert from a string but in reverse. Shift right (and mask)!

function intToIP(int) {
    var part1 = int & 255;
    var part2 = ((int >> 8) & 255);
    var part3 = ((int >> 16) & 255);
    var part4 = ((int >> 24) & 255);

    return part4 + "." + part3 + "." + part2 + "." + part1;
}

Why reinvent the wheel? From Google:

OR, you can use what I found here:
http://javascript.about.com/library/blipconvert.htm

function dot2num(dot) 
{
    var d = dot.split('.');
    return ((((((+d[0])*256)+(+d[1]))*256)+(+d[2]))*256)+(+d[3]);
}

function num2dot(num) 
{
    var d = num%256;
    for (var i = 3; i > 0; i--) 
    { 
        num = Math.floor(num/256);
        d = num%256 + '.' + d;
    }
    return d;
}
evan
  • 12,307
  • 7
  • 37
  • 51
  • 2
    Thank you very much, this was very helpfull! I thought javascript allways used 64-bit signed floats but, as you said, bitshifts are performed after conversion to 32-bits signed integers. That's where I was wrong! – JPuge Nov 12 '11 at 16:57
  • @JPuge You're welcome! Most languages avoid floats whenever possible. They're horribly imprecise. They also require a completely different kind of processor (floating point processor) where ints just use adders/muxes and other stuff that's really easy. – evan Nov 12 '11 at 17:04
8

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.

Pointy
  • 405,095
  • 59
  • 585
  • 614
6

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.

Guy
  • 65,082
  • 97
  • 254
  • 325
  • ip.toLong might not work, because the value is returned, before the function in the forEach loop is executed. Just use an normal for(int i=0, ...)... – Sir2B Nov 16 '18 at 11:54
2

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)
}
Sarsaparilla
  • 6,300
  • 1
  • 32
  • 21
1
const ip2int = (x) => (x.split('.').reduce((a, v) => ((a << 8) + (+v)), 0) >>> 0);
Pradeep
  • 9,667
  • 13
  • 27
  • 34
tsypa
  • 11
  • 2
1

One-Liner:

const ipToLong = ip => ip.split('.').map(parseFloat).reduce((total, part) => total * 256 + part);
Shl
  • 3,130
  • 1
  • 17
  • 16
1

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.

Mike
  • 78
  • 5
  • `((192<<24)>>24) !== 192` so I'm not sure if it's reliable. – pimvdb Nov 12 '11 at 16:19
  • 3
    You need to do a bit mask as shifting extends the sign. (((192<<24) >> 24) & 255) should be equal to 192. – evan Nov 12 '11 at 16:27
  • @pimvdb @evan or use the *Bitwise Unsigned Right Shift*-operator `>>>` that has been available for ages: `((192<<24)>>>24) === 192` – some Oct 05 '17 at 05:27
0
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(".");
atmd
  • 7,430
  • 2
  • 33
  • 64
user2321638
  • 59
  • 1
  • 2
0

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 += parts[0] * Math.pow(2, 24);
    num += parts[1] * Math.pow(2, 16);
    num += parts[2] * Math.pow(2, 8);
    num += parts[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;
}
Welcor
  • 2,431
  • 21
  • 32
  • your `ip2num` returns `string` because `parts[3]` is a string and you're not converting it to a number – Sagid Aug 04 '23 at 08:53
0

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);
}
Fabien
  • 4,862
  • 2
  • 19
  • 33
TCK
  • 1
0
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

Davit Tvildiani
  • 1,915
  • 3
  • 19
  • 29
0

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>
Ernesto
  • 3,944
  • 1
  • 14
  • 29
0

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.

Community
  • 1
  • 1
FlyingGuy
  • 333
  • 1
  • 9
  • "IP in V4 is 32bits" == "2^32" == 32 bit number. – evan Nov 12 '11 at 16:45
  • Interesting comment, isn't that what I said? – FlyingGuy Nov 16 '11 at 00:53
  • Hmm... I must have misunderstood what you were saying. No idea what "that number" at the end of the statement refers to. I might have "filled it in" when I read it with something different than what you meant. – evan Nov 16 '11 at 07:15