搜索
首页php教程php手册珊瑚虫IP库浅析

珊瑚虫IP库浅析

Jun 13, 2016 pm 12:33 PM
php使用操作是什么

这不是什么新鲜事情了,很早之前就已经有人做出来了。
就是使用PHP操作纯真IP库或珊瑚虫IP库,根据来访者的IP得到所在的物理位置。

我先帖出代码。然后再慢慢一步步浅析出来。希望对想了解这一块的朋友们有帮助。

Only For PHP5的代码。会继续优化代码的。

class IpLocation{
    private $fp;
    private $wrydat;
    private $wrydat_version;
    private $ipnumber;
    private $firstip;
    private $lastip;
    private $ip_range_begin;
    private $ip_range_end;
    private $country;
    private $area;
    const REDIRECT_MODE_0 = 0;
    const REDIRECT_MODE_1 = 1;
    const REDIRECT_MODE_2 = 2;
    function __construct(){
        $args = func_get_args();
        $this->wrydat = func_num_args()>0?$args[0]:'CoralWry.dat';
        $this->initialize();
    }
    function __destruct(){
        fclose($this->fp);
    }
    private function initialize(){
        if(file_exists($this->wrydat))
            $this->fp = fopen($this->wrydat,'rb');
        $this->getipnumber();
        $this->getwryversion();
    }
    public function get($str){
        return $this->$str;
    }
    public function set($str,$val){
        $this->$str = $val;
    }
    private function getbyte($length,$offset=null){
        if(!is_null($offset)){
            fseek($this->fp,$offset,SEEK_SET);
        }
        $b = fread($this->fp,$length);
        return $b;
    }
/**
* 把IP地址打包成二进制数据,以big endian(高位在前)格式打包
* 数据存储格式为 little endian(低位在前) 如:
* 00 28 C6 DA    218.198.40.0    little endian
* 3F 28 C6 DA    218.198.40.0    little endian
* 这样的数据无法作二分搜索查找的比较,所以必须先把获得的IP数据使用strrev转换为big endian
* @param $ip
* @return big endian格式的二进制数据
*/
    private function packip($ip){
        return pack( "N", intval( ip2long( $ip)));
    }

    private function getlong($length=4, $offset=null){
        $chr=null;
        for($c=0;$length%4!=0&&$c            $chr .= chr(0);
        }
        $var = unpack( "Vlong", $this->getbyte($length, $offset).$chr);
        return $var['long'];
    }

    private function getwryversion(){
        $length = preg_match("/coral/i",$this->wrydat)?26:30;
        $this->wrydat_version = $this->getbyte($length, $this->firstip-$length);
    }

    private function getipnumber(){
        $this->firstip = $this->getlong();
        $this->lastip = $this->getlong();
        $this->ipnumber = ($this->lastip-$this->firstip)/7+1;
    }

    private function getstring($data="",$offset=null){
        $char = $this->getbyte(1,$offset);
        while(ord($char) > 0){
            $data .= $char;
            $char = $this->getbyte(1);
        }
        return $data;
    }

    private function iplocaltion($ip){
        $ip = $this->packip($ip);
        $low = 0;
        $high = $this->ipnumber-1;
        $ipposition = $this->lastip;
        while($low             $t = floor(($low+$high)/2);
            if($ip getbyte(4,$this->firstip+$t*7))){
                $high = $t - 1;
            } else {
                if($ip > strrev($this->getbyte(4,$this->getlong(3)))){
                    $low = $t + 1;
                }else{
                    $ipposition = $this->firstip+$t*7;
                    break;
                }
            }
        }
        return $ipposition;
    }
    private function getarea(){
        $b = $this->getbyte(1);
        switch(ord($b)){
            case self::REDIRECT_MODE_0 :
                return "未知";
                break;
            case self::REDIRECT_MODE_1:
            case self::REDIRECT_MODE_2:
                return $this->getstring("",$this->getlong(3));
                break;
            default:
                return $this->getstring($b);
                break;
        }
    }
    public function getiplocation($ip){
        $ippos = $this->iplocaltion($ip);
        $this->ip_range_begin = long2ip($this->getlong(4,$ippos));
        $this->ip_range_end = long2ip($this->getlong(4,$this->getlong(3)));
        $b = $this->getbyte(1);
        switch (ord($b)){
            case self::REDIRECT_MODE_1:
                $b = $this->getbyte(1,$this->getlong(3));
                if(ord($b) == REDIRECT_MODE_2){
                    $countryoffset = $this->getlong(3);
                    $this->area = $this->getarea();
                    $this->country = $this->getstring("",$countryoffset);
                }else{
                    $this->country = $this->getstring($b);
                    $this->area    = $this->getarea();
                }
                break;

            case self::REDIRECT_MODE_2:
                    $countryoffset = $this->getlong(3);
                    $this->area = $this->getarea();
                    $this->country = $this->getstring("",$countryoffset);
                break;

            default:
                $this->country = $this->getstring($b);
                $this->area    = $this->getarea();
                break;
        }
    }
}
/* */
echo microtime();
echo "\n";
$iploca = new IpLocation;
//$iploca = new IpLocation('QQWry.dat');
echo $iploca->get('wrydat_version');
echo "\n";
echo $iploca->get('ipnumber');
echo "\n";
$iploca->getiplocation('211.44.32.34');
/**/
echo $iploca->get('ip_range_begin');
echo "\n";
echo $iploca->get('ip_range_end');
echo "\n";
echo $iploca->get('country');
echo "\n";
echo $iploca->get('area');

echo "\n";
echo $iploca->get('lastip');
echo "\n";
echo microtime();
echo "\n";
unset($iploca);

参考资料:LumaQQ的 纯真IP数据库格式详解

CoralWry.dat文件结构上分为3个区域:

  • 文件头[固定8个字节]
  • 数据区[不固定长度,记录IP的地址信息]
  • 索引区[大小由文件头决定]

该文件数据的存储方式是:little endian。
在这里引用了谈谈Unicode编码里的关于little endian 与 big endian的区别

引用

big endian和little endian是CPU处理多字节数的不同方式。例如“汉”字的Unicode编码是6C49。那么写到文件里时,究竟是将6C写在前面,还是将49写在前面?如果将6C写在前面,就是big endian。还是将49写在前面,就是little endian。

  “endian”这个词出自《格列佛游记》。小人国的内战就源于吃鸡蛋时是究竟从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开,由此曾发生过六次叛乱,其中一个皇帝送了命,另一个丢了王位。

  我们一般将endian翻译成“字节序”,将big endian和little endian称作“大尾”和“小尾”。

文件头:
红色框框里的就是文件头,前4个字节是索引区的开始地址,后4个字节是索引区的结束地址。

如下图所示:


点击放大

由于数据库是使用了little endian的字节库,所以我们需要把它倒过来。
把文件头的0-3的字节读取出来,再使用 unpack 函数把二进制数据转换为big endian格式的无符号整型。
处理后,索引区的开始地址位置是:00077450 ;索引区的结束地址位置是:000CE17C。
如果你手头上有UltraEdit的软件,可以打开CoralWry.dat文件,查找地址为:00077450 的位置,那就是IP地址索引区的开始。
如下图所示:


点击放大

红色框框住那就是索引区的开始位置。

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

功能强大的PHP集成开发环境

螳螂BT

螳螂BT

Mantis是一个易于部署的基于Web的缺陷跟踪工具,用于帮助产品缺陷跟踪。它需要PHP、MySQL和一个Web服务器。请查看我们的演示和托管服务。

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)