IP-addresses stored as int results in overflow?

2020-05-25 06:26发布

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?

标签: javascript
11条回答
做个烂人
2楼-- · 2020-05-25 06:48

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);
}
查看更多
甜甜的少女心
3楼-- · 2020-05-25 06:51

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.

查看更多
我命由我不由天
4楼-- · 2020-05-25 06:52
const ip2int = (x) => (x.split('.').reduce((a, v) => ((a << 8) + (+v)), 0) >>> 0);
查看更多
手持菜刀,她持情操
5楼-- · 2020-05-25 06:52

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;
}
查看更多
贼婆χ
6楼-- · 2020-05-25 06:56

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.

查看更多
Explosion°爆炸
7楼-- · 2020-05-25 07:00

One-Liner:

const ipToLong = ip => ip.split('.').map(parseFloat).reduce((total, part) => total * 256 + part);
查看更多
登录 后发表回答