使用Python查询纯真IP
纯真IP挺全,IP数据库也是中文的,在日志分析等方面比较有用 。如果denyhosts日志、nginx和apache web访问日志分析等能用下,这里就结合一个网上找来的python脚本和QQ纯真IP库,解析日志里的IP归属 。
1#!/usr/bin/env python
2# coding: utf-8
3'''用Python脚本查询纯真IP库
4QQWry.Dat的格式如下:
5+----------+
6| 文件头 | (8字节)
7+----------+
8| 记录区 | (不定长)
9+----------+
10| 索引区 | (大小由文件头决定)
11+----------+
12文件头:4字节开始索引偏移值+4字节结尾索引偏移值
13记录区: 每条IP记录格式 ==> IP地址[国家信息][地区信息]
14 对于国家记录,可以有三种表示方式:
15 字符串形式(IP记录第5字节不等于0x01和0x02的情况),
16 重定向模式1(第5字节为0x01),则接下来3字节为国家信息存储地的偏移值
17 重定向模式(第5字节为0x02),
18 对于地区记录,可以有两种表示方式: 字符串形式和重定向
19 最后一条规则:重定向模式1的国家记录后不能跟地区记录
20索引区: 每条索引记录格式 ==> 4字节起始IP地址 + 3字节指向IP记录的偏移值
21 索引区的IP和它指向的记录区一条记录中的IP构成一个IP范围。查询信息是这个
22 范围内IP的信息
23'''
24import sys
25import socket
26from struct import pack, unpack
27class IPInfo(object):
28 '''QQWry.Dat数据库查询功能集合
29 '''
30 def __init__(self, dbname):
31 ''' 初始化类,读取数据库内容为一个字符串,
32 通过开始8字节确定数据库的索引信息'''
33 self.dbname = dbname
34 # f = file(dbname, 'r')
35 # Demon注:在Windows下用'r'会有问题,会把rn转换成n
36 # 详见http://demon.tw/programming/python-open-mode.html
37 # 还有Python文档中不提倡用file函数来打开文件,推荐用open
38 f = open(dbname, 'rb')
39 self.img = f.read()
40 f.close()
41 # QQWry.Dat文件的开始8字节是索引信息,前4字节是开始索引的偏移值,
42 # 后4字节是结束索引的偏移值。
43 # (self.firstIndex, self.lastIndex) = unpack('II', self.img[:8])
44 # Demon注:unpack默认使用的endian是和机器有关的
45 # Intel x86和AMD64(x86-64)是little-endian
46 # Motorola 68000和PowerPC G5是big-endian
47 # 而纯真数据库全部采用了little-endian字节序
48 # 所以在某些big-endian的机器上原代码会出错
49 (self.firstIndex, self.lastIndex) = unpack('<II', self.img[:8])
50 # 每条索引长7字节,这里得到索引总个数
51 self.indexCount = (self.lastIndex - self.firstIndex) / 7 + 1
52 def getString(self, offset = 0):
53 ''' 读取字符串信息,包括"国家"信息和"地区"信息
54 QQWry.Dat的记录区每条信息都是一个以'�'结尾的字符串'''
55 o2 = self.img.find('�', offset)
56 #return self.img[offset:o2]
57 # 有可能只有国家信息没有地区信息,
58 gb2312_str = self.img[offset:o2]
59 try:
60 utf8_str = unicode(gb2312_str,'gb2312').encode('utf-8')
61 except:
62 return '未知'
63 return utf8_str
64 def getLong3(self, offset = 0):
65 '''QQWry.Dat中的偏移记录都是3字节,本函数取得3字节的偏移量的常规表示
66 QQWry.Dat使用“字符串“存储这些值'''
67 s = self.img[offset: offset + 3]
68 s += '�'
69 # unpack用一个'I'作为format,后面的字符串必须是4字节
70 # return unpack('I', s)[0]
71 # Demon注:和上面一样,强制使用little-endian
72 return unpack('<I', s)[0]
73 def getAreaAddr(self, offset = 0):
74 ''' 通过给出偏移值,取得区域信息字符串,'''
75 byte = ord(self.img[offset])
76 if byte == 1 or byte == 2:
77 # 第一个字节为1或者2时,取得2-4字节作为一个偏移量调用自己
78 p = self.getLong3(offset + 1)
79 return self.getAreaAddr(p)
80 else:
81 return self.getString(offset)
82 def getAddr(self, offset, ip = 0):
83 img = self.img
84 o = offset
85 byte = ord(img[o])
86 if byte == 1:
87 # 重定向模式1
88 # [IP][0x01][国家和地区信息的绝对偏移地址]
89 # 使用接下来的3字节作为偏移量调用字节取得信息
90 return self.getAddr(self.getLong3(o + 1))
91 if byte == 2:
92 # 重定向模式2
93 # [IP][0x02][国家信息的绝对偏移][地区信息字符串]
94 # 使用国家信息偏移量调用自己取得字符串信息
95 cArea = self.getAreaAddr(self.getLong3(o + 1))
96 o += 4
97 # 跳过前4字节取字符串作为地区信息
98 aArea = self.getAreaAddr(o)
99 return (cArea, aArea)
100 if byte != 1 and byte != 2:
101 # 最简单的IP记录形式,[IP][国家信息][地区信息]
102 # 重定向模式1有种情况就是偏移量指向包含国家和地区信息两个字符串
103 # 即偏移量指向的第一个字节不是1或2,就使用这里的分支
104 # 简单地说:取连续取两个字符串!
105 cArea = self.getString(o)
106 #o += len(cArea) + 1
107 # 我们已经修改cArea为utf-8字符编码了,len取得的长度会有变,
108 # 用下面方法得到offset
109 o = self.img.find('�',o) + 1
110 aArea = self.getString(o)
111 return (cArea, aArea)
112 def find(self, ip, l, r):
113 ''' 使用二分法查找网络字节编码的IP地址的索引记录'''
114 if r - l <= 1:
115 return l
116 m = (l + r) / 2
117 o = self.firstIndex + m * 7
118 #new_ip = unpack('I', self.img[o: o+4])[0]
119 # Demon注:和上面一样,强制使用little-endian
120 new_ip = unpack('<I', self.img[o: o+4])[0]
121 if ip <= new_ip:
122 return self.find(ip, l, m)
123 else:
124 return self.find(ip, m, r)
125 def getIPAddr(self, ip):
126 ''' 调用其他函数,取得信息!'''
127 # 使用网络字节编码IP地址
128 ip = unpack('!I', socket.inet_aton(ip))[0]
129 # 使用 self.find 函数查找ip的索引偏移
130 i = self.find(ip, 0, self.indexCount - 1)
131 # 得到索引记录
132 o = self.firstIndex + i * 7
133 # 索引记录格式是: 前4字节IP信息+3字节指向IP记录信息的偏移量
134 # 这里就是使用后3字节作为偏移量得到其常规表示(QQWry.Dat用字符串表示值)
135 o2 = self.getLong3(o + 4)
136 # IP记录偏移值+4可以丢弃前4字节的IP地址信息。
137 (c, a) = self.getAddr(o2 + 4)
138 return (c, a)
139 def output(self, first, last):
140 for i in range(first, last):
141 o = self.firstIndex + i * 7
142 ip = socket.inet_ntoa(pack('!I', unpack('I', self.img[o:o+4])[0]))
143 offset = self.getLong3(o + 4)
144 (c, a) = self.getAddr(offset + 4)
145 print "%s %d %s/%s" % (ip, offset, c, a)
146def main():
147 i = IPInfo('QQWry.Dat')
148 (c, a) = i.getIPAddr(sys.argv[1])
149 # Demon注:如果是在Windows命令行中运行把编码转回gb2312以避免乱码
150 if sys.platform == 'win32':
151 c = unicode(c, 'utf-8').encode('gb2312')
152 a = unicode(a, 'utf-8').encode('gb2312')
153 print '%s %s/%s' % (sys.argv[1], c, a)
154if __name__ == '__main__':
155 main()
用法:这里以denyhosts屏蔽的主机为例
1#for i in `sed -e '/^ALL/!d' /etc/hosts.deny |cut -d' ' -f2`; do /root/QQwryIP.py $i;done
289.163.144.165 俄罗斯/
3211.174.187.161 韩国/首尔
459.166.120.184 日本/ATHOME网络
5147.231.70.91 捷克/
685.28.26.66 英国/伦敦大学皇家医学院皇家法医实验鉴定中心
7218.239.223.77 韩国/
8216.146.47.37 美国/加拿大/未知
969.31.5.120 美国/
1075.109.170.56 美国/
1169.43.142.150 美国/
1259.109.6.83 北京市/方正宽带
捐赠本站(Donate)
如您感觉文章有用,可扫码捐赠本站!(If the article useful, you can scan the QR code to donate))
- Author: shisekong
- Link: https://blog.361way.com/python-qqwry-dat-ip/3314.html
- License: This work is under a 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议. Kindly fulfill the requirements of the aforementioned License when adapting or creating a derivative of this work.