[removed] Is IP In One Of These Subnets?

前端 未结 6 549
独厮守ぢ
独厮守ぢ 2021-01-30 23:26

So I have ~12600 subnets:

eg. 123.123.208.0/20

and an IP.

I can use a SQLite Database or an array or whatever

There was a similar question asked

6条回答
  •  青春惊慌失措
    2021-01-31 00:05

    Keywords: Binary searching, preprocessing, sorting

    I had a similar problem and binary search appears to be very efficient if you can pre-process your subnet list and sort it. Then you can achieve an asymptotic time complexity of O(log n).

    Here's my code (MIT License, original location: https://github.com/iBug/pac/blob/854289a674578d096f60241804f5893a3fa17523/code.js):

    function belongsToSubnet(host, list) {
      var ip = host.split(".").map(Number);
      ip = 0x1000000 * ip[0] + 0x10000 * ip[1] + 0x100 * ip[2] + ip[3];
    
      if (ip < list[0][0])
        return false;
    
      // Binary search
      var x = 0, y = list.length, middle;
      while (y - x > 1) {
        middle = Math.floor((x + y) / 2);
        if (list[middle][0] < ip)
          x = middle;
        else
          y = middle;
      }
    
      // Match
      var masked = ip & list[x][1];
      return (masked ^ list[x][0]) == 0;
    }
    

    And an example usage:

    function isLan(host) {
      return belongsToSubnet(host, LAN);
    }
    
    var LAN = [
      [0x0A000000, 0xFF000000], // 10.0.0.0/8
      [0x64400000, 0xFFC00000], // 100.64.0.0/10
      [0x7F000000, 0xFF000000], // 127.0.0.0/8
      [0xA9FE0000, 0xFFFF0000], // 169.254.0.0/16
      [0xAC100000, 0xFFF00000], // 172.16.0.0/12
      [0xC0A80000, 0xFFFF0000]  // 192.168.0.0/16
    ];
    
    isLan("127.12.34.56"); // => true
    isLan("8.8.8.8"); // => false (Google's Public DNS)
    

    You can get a PAC script* and see how it performs (it loads a China IP list from somewhere else, sorts them and formats them appropriately) against 5000s of subnets. In practice its speed is surprisingly satisfactory.

    The preprocessing code can be inspected using F12 Dev Tools on the above page. In short, you need to convert 1.2.3.4/16 to [0x01020304, 0xFFFF0000], i.e. 32-bit unsigned integer for the IP address and network mask.

    * Link goes to my personal website.

提交回复
热议问题