IPIP数据库解析

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方法,通过简单计算,就可以得到数据块的偏移地址,然后获取到内容。

1
2
3
4
5
6
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]
1
2
3
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()