<p> 以前的追捕数据库太大,而且很久没有更新了。</p><p> 所以我想到利用QQwry.dat这个文件查询IP所在位置,QQwry.dat 在很多地方都能找到,一般看IP地址的QQ压缩包中都有。 </p><p> 但是没有任何相关格式资料。 </p><p> 我分析了这个文件的格式,目前如下结论: </p><p> 格式如下: </p><p> A。文件头,共8字节</p><p> B。若干条记录的结束地址+国家和区域</p><p> C。按照从小到大排列的若干条起始地址+结束地址偏移,定长,7字节</p><p> D。所有的IP都是用4字节整数记录的,并且遵照Intel次序,高位在后,低位在前。</p><p> E。所有偏移量都是绝对偏移,就是从文件最开头计算。</p><p> F。除了文件头用了两个4字节偏移,其余偏移量都用3字节。</p><p> G。所有的偏移量也是低位在前,高位在后</p><p> H。采用了一些字符串压缩技术 </p><p> 1。文件头,共8字节</p><p> FirstStartIpOffset:4 第一个起始IP的绝对偏移</p><p> LastStartIpOffset:4 最后一个起始IP的绝对偏移 </p><p> 2。起始地址+结束地址偏移记录区</p><p> 每条记录7字节,按照起始地址从小到大排列 </p><p> StartIp:4 起始地址,整数形式的IP</p><p> EndIpOffset:3 结束地址绝对偏移 </p><p> 3。结束地址+国家+区域记录区 </p><p> EndIP:4</p><p> 国家+区域记录:不定长 </p><p> 4。国家+区域记录,有几种形式</p><p> 4.1。</p><p> 国家字符串,以 0x0 结束</p><p> 区域字符串,以 0x0 结束 </p><p> 4.2。</p><p> Flag:1 标识取值: 0x1,后面没有Local记录</p><p> 0x2,后面还有Local记录</p><p> sCountryOffset:3 实际的字符串要去这个偏移位置去找</p><p> LocalRec:不定长,可选 根据Flag取值而定。这个记录也类似Country,可能采用压缩 </p><p> 4.3 LocalRec结构一</p><p> flag:1 还不是十分了解这个flag含义,取值 0x1 or 0x2</p>
<p> </p>
<p> sLocalOffset:3 </p><p> 4.4 LocalRec结构二</p><p> sLocal:不定长 普通的C风格字符串 </p><p> 注意:sCountryOffset指向的位置可能依然是4.2格式的,不知道为什么这样设计。 </p><p> Flag取0x1时,sCountryOffset指向的位置可能是Flag为0x2,这时,LocalRec也在这里寻找。 </p><p> 现在不明白当记录Local的位置遇到0x2的标志意味着什么。 </p><p> 在qqwry.dat中,似乎存在一些错误。</p><p> 个别的记录Local会被写为:</p><p> 0x2,0x0,0x0,0x0</p><p> 根据规则,应该到文件最开头去寻找,可是,文件最开头显然不是记录这些的。 </p><p> 我才学PHP不久,各位不要笑,你要能改进当然好,记得给我一份。</p><p> 我参考了一些网上找到的代码,就不一一写出出处了。 </p><p> 说老实话,我很头疼PHP无法明确指定变量的类型。</p><p> 比如,我想让某个数是无符号的整形,它很不听话,非要是带个负号,我只好尝试各种可能的写法..........</p><p> 各位都是怎么处理类似的事情? </p><p> <code>define('QQWRY' , $qqwry_root_path . 'QQwry.dat' ) ;<br />function IpToInt($Ip) {<br />
$array=explode('.',$Ip);<br />
$Int=($array[0] * 256*256*256) + ($array[1]*256*256) + ($array[2]*256) + $array[3];<br />
return $Int;<br />}<br />function IntToIp($Int) {<br />
$b1=($Int & 0xff000000)>>24;<br />
if ($b1<0) $b1+=0x100;<br />
$b2=($Int & 0x00ff0000)>>16;<br />
if ($b2<0) $b2+=0x100;<br />
$b3=($Int & 0x0000ff00)>>8;<br />
if ($b3<0) $b3+=0x100;<br />
$b4= $Int & 0x000000ff;<br />
if ($b4<0) $b4+=0x100;<br />
$Ip=$b1.'.'.$b2.'.'.$b3.'.'.$b4;<br />
return $Ip;<br />}<br />class TQQwry<br />{<br />
var $StartIP = 0;<br />
var $EndIP = 0;<br />
var $Country = '';<br />
var $Local = '';<br />var $CountryFlag = 0; // 标识 Country位置<br />
// 0x01,随后3字节为Country偏移,没有Local<br />
// 0x02,随后3字节为Country偏移,接着是Local<br />
// 其他,Country,Local,Local有类似的压缩。可能多重引用。<br />
var $fp;<br />var $FirstStartIp = 0;<br />
var $LastStartIp = 0;<br />
var $EndIpOff = 0 ;<br />function getStartIp ( $RecNo ) {<br />
$offset = $this->FirstStartIp + $RecNo * 7 ;<br />
@fseek ( $this->fp , $offset , SEEK_SET ) ;<br />
$buf = fread ( $this->fp , 7 ) ;<br />
$this->EndIpOff = ord($buf[4]) + (ord($buf[5])*256) + (ord($buf[6])* 256*256);<br />
$this->StartIp = ord($buf[0]) + (ord($buf[1])*256) + (ord($buf[2])*256*256) + (ord($buf[3])*256*256*256);<br />
return $this->StartIp ;<br />
}<br />function getEndIp ( ) {<br />
@fseek ( $this->fp , $this->EndIpOff , SEEK_SET ) ;<br />
$buf = fread ( $this->fp , 5 ) ;<br />
$this->EndIp = ord($buf[0]) + (ord($buf[1])*256) + (ord($buf[2])*256*256) + (ord($buf[3])*256*256*256);<br />
$this->CountryFlag = ord ( $buf[4] ) ;<br />
return $this->EndIp ;<br />
}<br />function getCountry ( ) {<br />switch ( $this->CountryFlag ) {<br />
case 1:<br />
case 2:<br />
$this->Country = $this->getFlagStr ( $this->EndIpOff+4) ;<br />
//echo sprintf('EndIpOffset=(%x)',$this->EndIpOff );<br />
$this->Local = ( 1 == $this->CountryFlag )? '' : $this->getFlagStr ( $this->EndIpOff+8);<br />
break ;<br />
default :<br />
$this->Country = $this->getFlagStr ($this->EndIpOff+4) ;<br />
$this->Local = $this->getFlagStr ( ftell ( $this->fp )) ;<br />}<br />
}<br />
function getFlagStr ( $offset )<br />
{<br />$flag = 0 ;<br />
while ( 1 ){<br />
@fseek ( $this->fp , $offset , SEEK_SET ) ;<br />
$flag = ord ( fgetc ( $this->fp ) ) ;<br />
if ( $flag == 1 || $flag == 2 ) {<br />
$buf = fread ($this->fp , 3 ) ;<br />
if ($flag == 2 ){<br />
$this->CountryFlag = 2 ;<br />
$this->EndIpOff = $offset - 4 ;<br />
}<br />
$offset = ord($buf[0]) + (ord($buf[1])*256) + (ord($buf[2])* 256*256);<br />
}else{<br />
break ;<br />
}<br />}<br />
if ( $offset < 12 )<br />
return '';<br />
@fseek($this->fp , $offset , SEEK_SET ) ;<br />
return $this->getStr();<br />
}<br />
function getStr ( )<br />
{<br />
$str = '' ;<br />
while ( 1 ) {<br />
$c = fgetc ( $this->fp ) ;<br />
if ( ord ( $c[0] ) == 0 )<br />
break ;<br />
$str .= $c ;<br />
}<br />
return $str ;<br />
}<br />
function qqwry ($dotip) {<br />$nRet;<br />
$ip = IpToInt ( $dotip );<br />$this->fp= @fopen(QQWRY, "rb");<br />
if ($this->fp == NULL) {<br />
$szLocal= "OpenFileError";<br />
return 1;<br />}<br />
@fseek ( $this->fp , 0 , SEEK_SET ) ;<br />
$buf = fread ( $this->fp , 8 ) ;<br />
$this->FirstStartIp = ord($buf[0]) + (ord($buf[1])*256) + (ord($buf[2])*256*256) + (ord($buf[3])*256*256*256);<br />
$this->LastStartIp = ord($buf[4]) + (ord($buf[5])*256) + (ord($buf[6])*256*256) + (ord($buf[7])*256*256*256);<br />$RecordCount= floor( ( $this->LastStartIp - $this->FirstStartIp ) / 7);<br />
if ($RecordCount <= 1){<br />
$this->Country = "FileDataError";<br />
fclose ( $this->fp ) ;<br />
return 2 ;<br />
}<br />$RangB= 0;<br />
$RangE= $RecordCount;<br />
// Match ...<br />
while ($RangB < $RangE-1)<br />
{<br />
$RecNo= floor(($RangB + $RangE) / 2);<br />
$this->getStartIp ( $RecNo ) ;<br />if ( $ip == $this->StartIp )<br />
{<br />
$RangB = $RecNo ;<br />
break ;<br />
}<br />
if ( $ip > $this->StartIp)<br />
$RangB= $RecNo;<br />
else<br />
$RangE= $RecNo;<br />
}<br />
$this->getStartIp ( $RangB ) ;<br />
$this->getEndIp ( ) ;<br />if ( ( $this->StartIp <= $ip ) && ( $this->EndIp >= $ip ) ){<br />
$nRet = 0 ;<br />
$this->getCountry ( ) ;<br />
//这样不太好..............所以..........<br />
$this->Local = str_replace("(我们一定要解放台湾!!!)", "", $this->Local);<br />}else {<br />
$nRet = 3 ;<br />
$this->Country = '未知' ;<br />
$this->Local = '' ;<br />
}<br />
fclose ( $this->fp ) ;<br />
return $nRet ;<br />
}<br />}<br />function ip2location ( $ip )<br />{<br />
$wry = new TQQwry ;<br />
$nRet = $wry->qqwry ( $ip );<br />
//可以利用 $nRet做一些事情,我是让他自动记录未知IP到一个表,代码就不写了。<br />
return $wry->Country.$wry->Local ;<br />}</code></p>