首页 > 开发 > PHP > 正文

QQwry.dat格式分析和查询IP位置的PHP程序

2024-05-04 22:56:02
字体:
来源:转载
供稿:网友
qqwry.dat格式分析和查询ip位置的php程序

by strongc http://strongc.51.net/d2x/
转载时不要去掉我的名字和我的主页链接,谢谢!

以前的追捕数据库太大,而且很久没有更新了。
所以我想到利用qqwry.dat这个文件查询ip所在位置,qqwry.dat 在很多地方都能找到,一般看ip地址的qq压缩包中都有。

但是没有任何相关格式资料。

我分析了这个文件的格式,目前如下结论:

格式如下:

a。文件头,共8字节
b。若干条记录的结束地址+国家和区域
c。按照从小到大排列的若干条起始地址+结束地址偏移,定长,7字节
d。所有的ip都是用4字节整数记录的,并且遵照intel次序,高位在后,低位在前。
e。所有偏移量都是绝对偏移,就是从文件最开头计算。
f。除了文件头用了两个4字节偏移,其余偏移量都用3字节。
g。所有的偏移量也是低位在前,高位在后
h。采用了一些字符串压缩技术

1。文件头,共8字节
firststartipoffset:4 第一个起始ip的绝对偏移
laststartipoffset:4 最后一个起始ip的绝对偏移

2。起始地址+结束地址偏移记录区
每条记录7字节,按照起始地址从小到大排列

startip:4 起始地址,整数形式的ip
endipoffset:3 结束地址绝对偏移

3。结束地址+国家+区域记录区

endip:4
国家+区域记录:不定长

4。国家+区域记录,有几种形式
4.1。
国家字符串,以 0x0 结束
区域字符串,以 0x0 结束

4.2。
flag:1 标识取值: 0x1,后面没有local记录
0x2,后面还有local记录
scountryoffset:3 实际的字符串要去这个偏移位置去找
localrec:不定长,可选 根据flag取值而定。这个记录也类似country,可能采用压缩

4.3 localrec结构一
flag:1 还不是十分了解这个flag含义,取值 0x1 or 0x2
slocaloffset:3

4.4 localrec结构二
slocal:不定长 普通的c风格字符串

注意:scountryoffset指向的位置可能依然是4.2格式的,不知道为什么这样设计。


flag取0x1时,scountryoffset指向的位置可能是flag为0x2,这时,localrec也在这里寻找。

现在不明白当记录local的位置遇到0x2的标志意味着什么。

在qqwry.dat中,似乎存在一些错误。
个别的记录local会被写为:
0x2,0x0,0x0,0x0
根据规则,应该到文件最开头去寻找,可是,文件最开头显然不是记录这些的。

我才学php不久,各位不要笑,你要能改进当然好,记得给我一份。
我参考了一些网上找到的代码,就不一一写出出处了。

说老实话,我很头疼php无法明确指定变量的类型。
比如,我想让某个数是无符号的整形,它很不听话,非要是带个负号,我只好尝试各种可能的写法..........
各位都是怎么处理类似的事情?




define('qqwry' , $qqwry_root_path . 'qqwry.dat' ) ;

function iptoint($ip) {
    $array=explode('.',$ip);
    $int=($array[0] * 256*256*256) + ($array[1]*256*256) + ($array[2]*256) + $array[3];
    return $int;
}

function inttoip($int) {
    $b1=($int & 0xff000000)>>24;
    if ($b1<0) $b1+=0x100;
    $b2=($int & 0x00ff0000)>>16;
    if ($b2<0) $b2+=0x100;
    $b3=($int & 0x0000ff00)>>8;
    if ($b3<0) $b3+=0x100;
    $b4= $int & 0x000000ff;
    if ($b4<0) $b4+=0x100;
    $ip=$b1.'.'.$b2.'.'.$b3.'.'.$b4;
    return $ip;
}


class tqqwry
{
    var $startip = 0;
    var $endip   = 0;
    var $country = '';
    var $local   = '';

    var $countryflag = 0; // 标识 country位置
                          // 0x01,随后3字节为country偏移,没有local
                          // 0x02,随后3字节为country偏移,接着是local
                          // 其他,country,local,local有类似的压缩。可能多重引用。
    var $fp;

    var $firststartip = 0;
    var $laststartip = 0;
    var $endipoff = 0 ;

    function getstartip ( $recno ) {
        $offset = $this->firststartip + $recno * 7 ;
        @fseek ( $this->fp , $offset , seek_set ) ;
        $buf = fread ( $this->fp , 7 ) ;
        $this->endipoff = ord($buf[4]) + (ord($buf[5])*256) + (ord($buf[6])* 256*256);
        $this->startip = ord($buf[0]) + (ord($buf[1])*256) + (ord($buf[2])*256*256) + (ord($buf[3])*256*256*256);
        return $this->startip ;
    }

    function getendip ( ) {
        @fseek ( $this->fp , $this->endipoff , seek_set ) ;
        $buf = fread ( $this->fp , 5 ) ;
        $this->endip = ord($buf[0]) + (ord($buf[1])*256) + (ord($buf[2])*256*256) + (ord($buf[3])*256*256*256);
        $this->countryflag = ord ( $buf[4] ) ;
        return $this->endip ;
    }

    function getcountry ( ) {

        switch ( $this->countryflag ) {
            case 1:
            case 2:
                $this->country = $this->getflagstr ( $this->endipoff+4) ;
                //echo sprintf('endipoffset=(%x)',$this->endipoff );
                $this->local = ( 1 == $this->countryflag )? '' : $this->getflagstr ( $this->endipoff+8);
                break ;
            default :
                $this->country = $this->getflagstr ($this->endipoff+4) ;
                $this->local =   $this->getflagstr ( ftell ( $this->fp )) ;

        }
    }


    function getflagstr ( $offset )
    {

        $flag = 0 ;
        while ( 1 ){
            @fseek ( $this->fp , $offset , seek_set ) ;
            $flag = ord ( fgetc ( $this->fp ) ) ;
            if ( $flag == 1 || $flag == 2 ) {
                $buf = fread ($this->fp , 3 ) ;
                if ($flag == 2 ){
                    $this->countryflag = 2 ;
                    $this->endipoff = $offset - 4 ;
                }
                $offset = ord($buf[0]) + (ord($buf[1])*256) + (ord($buf[2])* 256*256);
            }else{
                break ;
            }

        }
        if ( $offset < 12 )
            return '';
        @fseek($this->fp , $offset , seek_set ) ;
        return $this->getstr();
    }
    function getstr ( )
    {
        $str = '' ;
        while ( 1 ) {
            $c = fgetc ( $this->fp ) ;
            if ( ord ( $c[0] ) == 0  )
               break ;
            $str .= $c ;
        }
        return $str ;
    }


    function qqwry ($dotip) {

        $nret;
        $ip = iptoint ( $dotip );

        $this->fp= @fopen(qqwry, "rb");
        if ($this->fp == null) {
              $szlocal= "openfileerror";
            return 1;

          }
          @fseek ( $this->fp , 0 , seek_set ) ;
        $buf = fread ( $this->fp , 8 ) ;
        $this->firststartip = ord($buf[0]) + (ord($buf[1])*256) + (ord($buf[2])*256*256) + (ord($buf[3])*256*256*256);
        $this->laststartip  = ord($buf[4]) + (ord($buf[5])*256) + (ord($buf[6])*256*256) + (ord($buf[7])*256*256*256);

        $recordcount= floor( ( $this->laststartip - $this->firststartip ) / 7);
        if ($recordcount <= 1){
            $this->country = "filedataerror";
            fclose ( $this->fp ) ;
            return 2 ;
        }

          $rangb= 0;
        $range= $recordcount;
        // match ...
        while ($rangb < $range-1)
        {
          $recno= floor(($rangb + $range) / 2);
          $this->getstartip ( $recno ) ;

            if ( $ip == $this->startip )
            {
                $rangb = $recno ;
                break ;
            }
          if ( $ip > $this->startip)
            $rangb= $recno;
          else
            $range= $recno;
        }
        $this->getstartip ( $rangb ) ;
        $this->getendip ( ) ;

        if ( ( $this->startip  <= $ip ) && ( $this->endip >= $ip ) ){
            $nret = 0 ;
            $this->getcountry ( ) ;
            //这样不太好..............所以..........
            $this->local = str_replace("(我们一定要解放台湾!!!)", "", $this->local);

        }else {
            $nret = 3 ;
            $this->country = '未知' ;
            $this->local = '' ;
        }
        fclose ( $this->fp ) ;
        return $nret ;
    }
}


function ip2location ( $ip )
{
    $wry = new tqqwry ;
    $nret = $wry->qqwry ( $ip );
    //可以利用 $nret做一些事情,我是让他自动记录未知ip到一个表,代码就不写了。
  return $wry->country.$wry->local ;
}



发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表