珊瑚虫IP库浅析_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<(4-$length%4);$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 <= $high){
            $t = floor(($low+$high)/2);
            if($ip < strrev($this->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地址索引区的开始。
如下图所示:


点击放大

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

时间: 2024-09-15 05:21:05

珊瑚虫IP库浅析_php实例的相关文章

WAMP环境中扩展oracle函数库(oci)_php实例

同事昨天接到一个任务,要用php处理oracle数据库的内容,但是php打开oracle扩展不是像mysql那样直接用就行,需要下一点东西才能打开 第一步 需要到oracle官方下载一个install client 包,在win下找到你对应系统版本的zip(注意这里是系统版本) 截止到2015-06-25,下载地址如下http://www.oracle.com/technetwork/database/features/instant-client/index-097480.html 例如选择

从刷票了解获得客户端IP的方法_php实例

前两个星期帮一个朋友的亲戚的孩子刷票,谁让咱们是程序员呢.这当中也遇到过重装系统,除灰尘,淘宝购物,盗QQ,下电影,某一个软件为什么不能使用等等,要是说不会,他们就说你电脑技术不是挺牛逼的吗,这点问题都解决不了. 刷票,分为多种限制,注册用户,验证码,以及IP限制.这个刷票网站,而不,是这个投票网站,限制了IP.如果要突破限制,我们需要了解如何获得用户的IP. getenv('HTTP_X_FORWARDED_FOR') getenv('HTTP_CLIENT_IP') getenv('REMO

基于CI框架的微信网页授权库示例_php实例

本文实例讲述了基于CI框架的微信网页授权库.分享给大家供大家参考,具体如下: 这里演示建立在CI框架上的微信网页授权功能. 1. 微信小类库,网页授权放置在libraries文件夹 <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); Class Weixin { private $appId; private $appSecret; function __construct() { $this->

php获取访问者IP地址汇总_php实例

//方法1: $ip = $_SERVER["REMOTE_ADDR"]; echo $ip; //方法2: 复制代码 代码如下: $user_IP = ($_SERVER["HTTP_VIA"]) ? $_SERVER["HTTP_X_FORWARDED_FOR"] : $_SERVER["REMOTE_ADDR"]; $user_IP = ($user_IP) ? $user_IP : $_SERVER["REM

可定制的PHP缩略图生成程式(需要GD库支持)_php实例

经典的PHP缩略图生成程式,基于GD库,可指定生成路径及生成目标的宽高细节 使用方法: 在支持GD库的PHP环境中,将以下代码另存为resize.php测试  经典的PHP缩略图生成程式,基于GD库,可指定生成路径及生成目标的宽高细节  使用方法: 在支持GD库的PHP环境中,将以下代码另存为resize.php测试   复制代码 代码如下: <?  $FILENAME="image_name";  // 生成图片的宽度  $RESIZEWIDTH=400;  // 生成图片的高

PHP获取用户客户端真实IP的解决方案_php实例

获取客户端ip其实不是个简单的活儿,因为存在Ip欺骗,和代理问题,所以获取客户端的IP的真实性会打折扣的,不能百分百准确.但是我们还是尽量找一个比较完善的获取客户端真正ip方法.使用php获取IP的方法能找到很多. function getIp(){ if (getenv("HTTP_CLIENT_IP") && strcasecmp(getenv("HTTP_CLIENT_IP"), "unknown")) $ip = gete

thinkPHP中create方法与令牌验证实例浅析_php实例

本文实例讲述了thinkPHP中create方法与令牌验证.分享给大家供大家参考,具体如下: thinkPHP的create方法与令牌验证主要是涉及表单的安全性. 代码如下: <?php // 本类由系统自动生成,仅供测试用途 class IndexAction extends Action{ public function index(){ $this->display(); } //一般用户在网站完成信息的添加修改--但是有意外,用户吧网页另存为到本地了,然后在这当中模拟了很多组数据,然后

PHP禁止个别IP访问网站_php实例

想不让某个IP访问网站,可以封他的IP,下面就提供这个方法.看下面的代码. 复制代码 代码如下: function get_ip_data(){       $ip=file_get_contents("http://ip.taobao.com/service/getIpInfo.php?ip=".get_client_ip());    $ip = json_decode($ip);    if($ip->code){        return false;    }   

PHP中的流(streams)浅析_php实例

概述 流(streams)是PHP4.3版本引入的一个特性,主要是为了统一文件.sockets以及其他类似资源的工作方法.PHP4.3距今已经有很长时间了,但是很多程序员似乎都不能正确使用PHP中的流,当然这也包括我.以前也在一些程序中遇到过流的使用,如php://input,但是一直没机会整理,今天就把这部分知识整理下. 流是由PHP提供的资源,可以供我们透明的使用,而且流是一个非常强大的工具.适当的在程序中使用流,可以将我们的程序带到一个新的高度. PHP手册中对流的描述如下: 复制代码 代