#!/usr/bin/env python
# coding: utf-8
'''用python指令碼查詢純真ip庫
qqwry.dat的格式如下:
+----------+
| 檔案頭 | (8位元組)
+----------+
| 記錄區 | (不定長)
+----------+
| 索引區 | (大小由檔案頭決定)
+----------+
檔案頭:4位元組開始索引偏移值+4位元組結尾索引偏移值
對於國家記錄,可以有三種表示方式:
字串形式(ip記錄第5位元組不等於0x01和0x02的情況),
重定向模式1(第5位元組為0x01),則接下來3位元組為國家資訊儲存地的偏移值
重定向模式(第5位元組為0x02),
對於地區記錄,可以有兩種表示方式: 字串形式和重定向
最後一條規則:重定向模式1的國家記錄後不能跟地區記錄
索引區的ip和它指向的記錄區一條記錄中的ip構成乙個ip範圍。查詢資訊是這個
範圍內ip的資訊
'''import sys
import socket
from struct import pack, unpack
class ipinfo(object):
'''qqwry.dat資料庫查詢功能集合
'''def __init__(self, dbname):
''' 初始化類,讀取資料庫內容為乙個字串,
通過開始8位元組確定資料庫的索引資訊'''
self.dbname = dbname
#f = file(dbname, 'r')#srg
f = file(dbname, 'rb')
self.img = f.read()
f.close()
# qqwry.dat檔案的開始8位元組是索引資訊,前4位元組是開始索引的偏移值,
# 後4位元組是結束索引的偏移值。
(self.firstindex, self.lastindex) = unpack('ii', self.img[:8])
# 每條索引長7位元組,這裡得到索引總個數
self.indexcount = (self.lastindex - self.firstindex) / 7 + 1
def getstring(self, offset = 0):
''' 讀取字串資訊,包括"國家"資訊和"地區"資訊
qqwry.dat的記錄區每條資訊都是乙個以'\0'結尾的字串'''
o2 = self.img.find('\0', offset)
#return self.img[offset:o2]
# 有可能只有國家資訊沒有地區資訊,
gb2312_str = self.img[offset:o2]
try:
#utf8_str = unicode(gb2312_str,'gb2312').encode('utf-8')#srg
utf8_str = gb2312_str
except:
return '未知'
return utf8_str
def getlong3(self, offset = 0):
'''qqwry.dat中的偏移記錄都是3位元組,本函式取得3位元組的偏移量的常規表示
qqwry.dat使用「字串「儲存這些值'''
s = self.img[offset: offset + 3]
s += '\0'
# unpack用乙個'i'作為format,後面的字串必須是4位元組
return unpack('i', s)[0]
def getareaaddr(self, offset = 0):
''' 通過給出偏移值,取得區域資訊字串,'''
byte = ord(self.img[offset])
if byte == 1 or byte == 2:
# 第乙個位元組為1或者2時,取得2-4位元組作為乙個偏移量呼叫自己
p = self.getlong3(offset + 1)
return self.getareaaddr(p)
else:
return self.getstring(offset)
def getaddr(self, offset, ip = 0):
img = self.img
o = offset
byte = ord(img[o])
if byte == 1:
# 重定向模式1
# [ip][0x01][國家和地區資訊的絕對偏移位址]
# 使用接下來的3位元組作為偏移量呼叫位元組取得資訊
return self.getaddr(self.getlong3(o + 1))
if byte == 2:
# 重定向模式2
# [ip][0x02][國家資訊的絕對偏移][地區資訊字串]
# 使用國家資訊偏移量呼叫自己取得字串資訊
carea = self.getareaaddr(self.getlong3(o + 1))
o += 4
# 跳過前4位元組取字串作為地區資訊
aarea = self.getareaaddr(o)
return (carea, aarea)
if byte != 1 and byte != 2:
# 最簡單的ip記錄形式,[ip][國家資訊][地區資訊]
# 重定向模式1有種情況就是偏移量指向包含國家和地區資訊兩個字串
# 即偏移量指向的第乙個位元組不是1或2,就使用這裡的分支
# 簡單地說:取連續取兩個字串!
carea = self.getstring(o)
#o += len(carea) + 1
# 我們已經修改carea為utf-8字元編碼了,len取得的長度會有變,
# 用下面方法得到offset
o = self.img.find('\0',o) + 1
aarea = self.getstring(o)
return (carea, aarea)
def find(self, ip, l, r):
''' 使用二分法查詢網路位元組編碼的ip位址的索引記錄'''
if r - l <= 1:
return l
m = (l + r) / 2
o = self.firstindex + m * 7
new_ip = unpack('i', self.img[o: o+4])[0]
if ip <= new_ip:
return self.find(ip, l, m)
else:
return self.find(ip, m, r)
def getipaddr(self, ip):
''' 呼叫其他函式,取得資訊!'''
# 使用網路位元組編碼ip位址
ip = unpack('!i', socket.inet_aton(ip))[0]
# 使用 self.find 函式查詢ip的索引偏移
i = self.find(ip, 0, self.indexcount - 1)
# 得到索引記錄
o = self.firstindex + i * 7
# 索引記錄格式是: 前4位元組ip資訊+3位元組指向ip記錄資訊的偏移量
# 這裡就是使用後3位元組作為偏移量得到其常規表示(qqwry.dat用字串表示值)
o2 = self.getlong3(o + 4)
# ip記錄偏移值+4可以丟棄前4位元組的ip位址資訊。
(c, a) = self.getaddr(o2 + 4)
return (c, a)
def output(self, first, last):
for i in range(first, last):
o = self.firstindex + i * 7
ip = socket.inet_ntoa(pack('!i', unpack('i', self.img[o:o+4])[0]))
offset = self.getlong3(o + 4)
(c, a) = self.getaddr(offset + 4)
print "%s %d %s/%s" % (ip, offset, c, a)
def main():
i = ipinfo('qqwry.dat')
(c, a) = i.getipaddr(sys.argv[1])
print '%s %s/%s' % (sys.argv[1], c, a)
if __name__ == '__main__':
main()
# changelog
# 1. 工具下面網友的建議,修改"o += len(carea) + 1"
#
# 因為這個時候我已經把得到的字串變成utf-8編碼了,長度會有變化!
純真IP庫PHP查詢
class ip 檢查ip的合法性 public function checkip ip return true 讀取little endian編碼的4個位元組轉化為長整型數 public function getlong4 讀取little endian編碼的3個位元組轉化為長整型數 public...
使用mysql查詢純真IP庫的方法
為了把點格式的ip位址轉換成整數表示的ip位址,建立如下mysql函式 create function ip calc ip varchar 20 returns bigint 20 return substring index ip,1 256 256 256 substring index su...
用vbs指令碼修改IP位址
工作需要,經常要在n個固定ip位址間切換。煩。上script center,居然發現還可以用指令碼來設定ip。這下方便多啦,儲存為乙個vbs檔案,雙擊就能直接改ip了。strip 192.168.198.4 strmask 255.255.255.0 strgw 192.168.198.1 strd...