IPIP数据库解析
背景
IPIP的IPv4库每日旗舰版数据库中包含IDC、基站等数据,而IDC地址列表、基站IP是单独收费的。很多场景需要完整的IDC IP列表,所以尝试从IPv4库每日旗舰版数据库中dump出所有IDC IP。

存储结构
文件头 + 数据块
文件头中包含版本、数据块数量等信息。
数据块中包含索引信息、IP数据块,IP数据块又分为长度+IP信息。
代码分析
以14.215.177.39为例,转成十进制为:249016615,转成二进制为:00001110110101111011000100100111 。
查询IP数据的主要步骤就是查找IP数据块所在位置,关键代码在Reader类中的_find_node方法中。
https://github.com/ipipdotnet/ipdb-python/blob/97994fda89379c0ce93efa9f6dfd2a40eafc63ce/ipdb/database.py#L57
_find_node中会调用_read_node,按二进制位查找IP数据块,添加一行调试代码:

输出:
0000111011010111101
这串二进制是IP14.215.177.39二进制的前缀。
说明00001110110101111010000000000000 (14.215.160.0)到 00001110110101111011111111111111 (14.215.191.255)之间的IP都是指向同一个IP数据块。此时node的值为7834831。再调用_resolve方法,通过简单计算,就可以得到数据块的偏移地址,然后获取到内容。
| def _resolve(self, node): resolved = node - self._meta.node_count + self._meta.node_count * 8 size = bytes2long(0, 0, self.data[resolved], self.data[resolved + 1]) if (resolved+2+size) > len(self.data): raise DatabaseError("database is error") return self.data[resolved+2:resolved+2+size]
|
| print self.data[resolved+2:resolved+2+size] 中国 广东 广州 电信 23.12911 113.264385 Asia/Shanghai UTC+8 440100 86 CN AP IDC CHN 0 CNY Yuan Renminbi
|
DUMP IDC实现
通过以上分析可以知道IP数据的查找过程是对IP的二进制位进行查找,匹配到后就可以确定一个IP段的范围及对应的数据,由此从1开始,依次获取IP段范围及数据,直至255^4。
稍微改下Reader.py,即可实现dump idc数据。部分代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| int2ip = lambda x: '.'.join([str(x/(256**i)%256) for i in range(3,-1,-1)])
class MyReader: ... def dump(self,index): idx = 0 node = 0 bit_count = 32 start = 0 i = 0 while i < 96: if i >= 80: node = self._read_node(node, 1) else: node = self._read_node(node, 0) i += 1 packed = bytearray(struct.pack('>I', index)) while idx < bit_count: start += (1 & (packed[idx >> 3] >> 7 - (idx % 8))) * 2**(31-idx) if node > self._meta.node_count: break node = self._read_node(node, (1 & (packed[idx >> 3] >> 7 - (idx % 8)))) idx += 1
end = start while idx < bit_count: end += 2**(31-idx) idx += 1
if node > self._meta.node_count: bs = self._resolve(node) tmp = bs.decode("utf-8").split("\t") off = 0 node_data = tmp[off:off+len(self._meta.fields)] loc = node_data m = {} for idx, value in enumerate(self._meta.fields): if loc[idx] == '': continue m[value] = loc[idx] return start,end,m return None,None,None
def main(): db = Reader('mydata4vipday4_cn.ipdb') i = 1 while(i < 255**4): start, end, m = db.dump(i) if 'idc' in m: print int2ip(start) + '\t' + int2ip(end) + '\t' + json.dumps(m) i = end+1
if __name__ == '__main__': main()
|