QQ IP 地址查询相关

时间:2023-03-09 18:45:56
QQ IP 地址查询相关

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

以前的追捕数据库太大,而且很久没有更新了。

所以我想到利用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。

国家字符串,以 0×0 结束

区域字符串,以 0×0 结束

4.2。

Flag:1 标识取值: 0×1,后面没有Local记录

0×2,后面还有Local记录

sCountryOffset:3 实际的字符串要去这个偏移位置去找

LocalRec:不定长,可选 根据Flag取值而定。这个记录也类似Country,可能采用压缩

4.3 LocalRec结构一

flag:1 还不是十分了解这个flag含义,取值 0×1 or 0×2

sLocalOffset:3

4.4 LocalRec结构二

sLocal:不定长 普通的C风格字符串

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

Flag取0×1时,sCountryOffset指向的位置可能是Flag为0×2,这时,LocalRec也在这里寻找。

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

在qqwry.dat中,似乎存在一些错误。

个别的记录Local会被写为:

0×2,0×0,0×0,0×0

根据规则,应该到文件最开头去寻找,可是,文件最开头显然不是记录这些的。

我才学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+=0×100;

$b2=($Int & 0x00ff0000)>>16;

if ($b2<0) $b2+=0×100;

$b3=($Int & 0x0000ff00)>>8;

if ($b3<0) $b3+=0×100;

$b4= $Int & 0x000000ff;

if ($b4<0) $b4+=0×100;

$Ip=$b1.’.’.$b2.’.’.$b3.’.’.$b4;

return $Ip;

}

class TQQwry

{

var $StartIP = 0;

var $EndIP = 0;

var $Country = ”;

var $Local = ”;

var $CountryFlag = 0; // 标识 Country位置

// 0×01,随后3字节为Country偏移,没有Local

// 0×02,随后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 ;

}

突然在某某网站看到IP地址查询,于是心血来潮做了一个。这是用asp查询全球IP地址的程式,由于类似的的用Asp查询IP的速度不是很理想,本人使用的也是宏志宏的IP地址数据库,只是对它进行了改进。

本人在 win98+pws+access2000上测试通过,效果比较理想。

数据库的设计在一个软件中的比例,毫不夸张的说占60%,虽然这是一个小的程式,但也得到一定的体现。

有任何错误或建议请一定要给我发E-mail: ljz811@163.com ,我也不了解“追捕”等类似算法,欢迎交流,谢谢!

好了废话少说,开始吧。

1、 转wry.dll为.mdb由于我们要优化数据库(适合我们用的),建议这样做:

将文件改为wry.xls用Excel打开。

在Access中新建表IP

字段名 类型 长度 说明

————————————————————————————————————

id 自动编号

newid 数字 5 startip前三个数字

seconded 数字 5 startip第二部分三个数字

startip 文本 20 起始IP段

endip 文本 20 止 IP 段

country 文本 此段IP所在国家或省

local 文本 此段IP所在地

从Excel中copy startip/endip/country/local 四个字段至Access ,id会自动添加,对于newid和secondid当然我们不可能手工添加,可以这样实现。

<!–#include file="dbpath.asp"–>

<%

sqlcmd="select * from ip"

‘建议使用sqlcmd="select * from ipwhere id>=值1 and id<值2" ,因为对于一万个左右的数据一下子添加资源占用很大

set rs=ip.execute(sqlcmd)

do while not rs.eof

fir=left(rs("startip"),3)

sec=mid(rs("startip"),5,3)

sqlcmd3="update ip set newid=’"&str&"’,secondid=’"sec"’ "

ip.execute sqlcmd3

rs.movenext

loop

%>

这样就完成数据库的修改,对于dbpath.asp 文件下面讲

2、 文件介绍

本程序分 dbpath.asp 和 search.asp

以下是源程序及简要描述

Dbpath.Asp 代码如下:(本人命名数据库名字为ip.mdb)

<%@ Language=VBScript %>

<% set ip=server.createobject("ADODB.CONNECTION")

ip.Open "driver={Microsoft Access Driver (*.mdb)};dbq=" & Server.MapPath("ip.mdb") %>

search.asp 代码如下:

<!–#include file="dbpath.asp"–>

<%

‘ Write by 尥蹶子

‘ If you find some error or you have better idea

‘ Please contact me

‘ My Email: ljz811@163.com Oicq:30763914

%>

<%

‘建立一个提交的表单

%>

<BODY bgColor=azure>

<P align=center>IP查询<BR><BR><BR>

<FORM action="index.asp" method=post id=form1 name=form1>

<font size=2>输入IP:</font><INPUT id=text1 name=putip

style ="BACKGROUND-COLOR: lavender; HEIGHT: 22px; WIDTH: 182px" >

<INPUT id=submit1 name=sub1 style="BACKGROUND-COLOR: lightblue; FONT-SIZE: 12" type=submit value="我查" tabindex="0">

</FORM>

<%

userip=Request.ServerVariables ("REMOTE_ADDR")

listip=trim(request("putip"))

if listip="" or listip=" " then

Response.Write "<p align=center><font size=2>请输入IP地址!</font></p>"

else

‘—判断访问者的IP是否与输入的ip同

cmp=strcomp(userip,listip,1)

if cmp=0 then %>

<p align=center><font size=2 >这就是你自己呀!</p>

<%

else

‘获得输入ip的第1段,并置3位

num1=Instr(listip,".")

‘判断第一段是否正确

if mun1=1 then

Response.Write "<p align=center>"&listip&"<br>"&" <font size=2 >非有效的IP地址,IP每段必须1~3位!</p>"

else

fir=left(listip,num1-1)

if len(fir)=2 then

fir=0&fir

end if

if len(fir)=1 then

fir=0&fir

fir=0&fir

end if

‘获得输入ip的第2段,并置3位

num2=Instr(num1+1,listip,".")

sec=mid(listip,num1+1,num2-(num1+1))

if len(sec)=2 then

sec=0&sec

end if

if len(sec)=1 then

sec=0&sec

sec=0&sec

end if

‘获得输入ip的第3段,并置3位

num3=Instr(num2+1,listip,".")

thr=mid(listip,num2+1,num3-(num2+1))

if len(thr)=2 then

thr=0&thr

end if

if len(thr)=1 then

thr=0&thr

thr=0&thr

end if

‘获得输入ip的第4段,并置3位

fou=right(listip,len(listip)-num3)

if len(fou)=2 then

fou=0&fou

end if

if len(fou)=1 then

fou=0&fou

fou=0&fou

end if

‘判断是否为有效IP

if len(fir)=0 or len(fir)>3 or len(sec)=0 or len(sec)>3 or len(thr)=0 or len(thr)>3 or len(fou)=0 or len(fou)>3 then

Response.Write "<p align=center>"&listip&"<br>"&" <font size=2 >非有效的IP地址,IP每段必须1~3位!</p>"

else

‘判断是否为保留地址

if ((fir>=0) and (fir<= 2)) or ((fir>=58) and (fir<=60)) or ((fir>=67) and (fir<=126)) then

Response.Write "<p align=center>"&listip&"<br>"&" <font size=2 >Internet IP保留地址!</p>"

else

‘判断是否为分配地址

if (fir>=219 and fir<=223) or fir=225 then

Response.Write "<p align=center>"&listip&"<br>"&" <font size=2 >此IP地址尚未分配!</p>"

else

if fir>255 or fir<0 or sec>255 or sec<0 or thr>255 or thr<0 or fou>255 or fou<0 then

Response.Write "<p align=center>"&listip&"<br>"&" <font size=2 >Internet IP地址输入值不正确!</p>"

else

‘查询IP地址数据库

Set sql= Server.CreateObject("ADODB.Recordset")

rs1="select id,startip,endip,country,local from ip where id<9904 and newid="&fir&" and secondid="&sec&" "

sql.open rs1,ip,1,1

‘对于类似224.000.000.000~224.255.255.255的ip地址进行处理

if sql.RecordCount = 1 then

sql.Close

sqlcmd2="select id,startip,endip,country,local from ip where id<9904 and newid="&fir&" and secondid="&sec&" order by id desc"

set rs=ip.execute(sqlcmd2)

else

sqlcmd2="select id,startip,endip,country,local from ip where id<9904 and newid="&fir&" order by id desc"

set rs=ip.execute(sqlcmd2)

if rs.eof then

%>

<br><br><font size="2"><P align=center><%=listip%><br>★未知IP数据★<BR>如果你知道请告诉我! OICQ:30763914 谢谢!<BR>=尥蹶子=</font></P>

<% else

do while not rs.eof

‘******* 处理country or local为空的情况,使用了按id倒排(why?因为根据IP地址表可知,先列大地区的ip段,例如先列出欧洲的 062.000.000.000~062.255.255.255,再列英国等IP段)

‘对后面三段IP进行处理,是否在IP表内

if (sec>=(mid(rs("startip"),5,3)) and (sec<=mid(rs("endip"),5,3))) and (thr>=(mid(rs("startip"),9,3)) and (thr<=mid(rs("endip"),9,3))) and (fou>=(mid(rs("startip"),13,3)) and (fou<=mid(rs("endip"),13,3))) then

%>

<center><font size=2><%=listip%><br><%=rs("country")%>  <%=rs("local")%></font></center>

<%

‘若查到数据,马上跳出以免占用资源

exit do

else

end if

if rs.eof then

‘如果没有打出提示

%>

<br><br><font size="2"><P align=center><%=listip%><br>★未知IP数据★<BR>如果你知道请告诉我! OICQ:30763914 谢谢!<BR>=尥蹶子=</font></P>

<%

end if

rs.movenext

loop

end if

end if

%>

<%

end if

end if%>

<%end if%>

<%end if%>

<%end if%>

<%end if%><%end if%>

</BODY>

</HTML>

<?php

/**********************************************************************

* IP 来源追踪 Ver 1.1a

* 作者 耙子 pazee@21cn.com http://www.fogsun.com

* 2002/08/10

*

* 程序中的数据库来自《追捕》,请把追捕中wry.dll 拷贝到函数当前目录。

* 追捕的数据库是个的dbf文件,只不过它的扩展名字变成了dll。

* 2000年我在一个偶然的机会发现了他的dll的真实格式,后来的很多文章也提到了此格式,

* 《追捕》的数据文件目前应用非常广泛,很多查IP来源程序的基本都用到了他的数据库。

* 比如有个去广告,能显示IP来源的QQ就使用了他。

* 2001年初写过一个php的函数,但是需要php的dbf模块支持,很多网站并不提供此模块。

* 现在的版本采用二进制的文件读写,不依赖php的dbf的支持了,没有用到

* 任何shell命令.

* 由于数据文件本身是有序的,所以非常方便的采用了折半查找的算法,

* 速度很快,目前的20020325版本的数据库,大约有记录28905条,最多比较14次。

*

* 在此感谢《追捕》作者“冯志宏”

* 有任何问题请于我联系,谢谢! pazee@21cn.com 

*

*

* 声明:

* 你可以随意传播、复制、修改此程序,但是请保留此段文字。

* 代码请勿用在商业软件上、请勿用在不正当的地方(这是《追捕》的要求),

* 再次表示谢谢。

***********************************************************************/

// define path of wry.dll

define("DBFILENAME", "wry.dll");

class TRec

{

var $StartIP;

var $EndIP;

var $Country;

var $Local;

}

class TWru

{

var $ip;

var $fp;

var $Rec;

var $DATAFIELDBEGIN= 0xc2;

var $RECORDLENGTH;

// Check IP and Format IP

function FormatIP($ip)

{

$ret= ereg("^([0-9]+).([0-9]+).([0-9]+).([0-9]+)$", $ip, $IPSection);

if ($ret == false)

return -1; // Invild IP

$this->ip= ””;

for ($i=1; $i<=4; $i++)

if ($IPSection[$i] > 255)

return -1;

else

$this->ip.= sprintf("%03.0f", $IPSection[$i]). (($i<4) ? ”.” : ””);

return 0;

}

// read a record from DB 

function ReadRec($RecNo)

{

$this->Seek($RecNo);

$buf= fread($this->fp, $this->RECORDLENGTH);

if (strlen($buf) == 0)

{

return 1;

}

$this->Rec->StartIP= (substr($buf, 0, 17));

$this->Rec->EndIP= trim(substr($buf, 17, 22));

$this->Rec->Country= trim(substr($buf, 17+22, 13));

$this->Rec->Local= trim(substr($buf, 17+22+13, 47));

return 0;

}

// Go to Record Number

function Seek($RecNo)

{

return fseek($this->fp, $RecNo * $this->RECORDLENGTH + $this->DATAFIELDBEGIN, SEEK_SET);

}

// Where_are_you Main Fucntion

/*********************************************

* 使用说明

* 参数:

* IP 合法IP地址即可

* szLocal 是保存返回的结果字符串的

* 返回值:

* 此函数有返回值,可以根据返回值自行处理结果

* 0: 查找成功

* -1: 无效的IP

* 1: 打开数据库文件失败

* 2: 数据文件错误(没找到有效记录)

* 3: 未知 IP

**********************************************/

function wru($ip, &$szLocal)

{

$this->Rec= new TRec;

$nRet= 0;

$this->RECORDLENGTH= 17 + 22 + 13 + 47 + 12 + 1; 

if ($this->FormatIP($ip) != 0)

{

$szLocal= "InvalidIP";

return -1;

}

$this->fp= fopen(DBFILENAME, "rb");

if ($this->fp == NULL) {

$szLocal= "OpenFileError";

return 1;

}

// Get Record Count

fseek($this->fp, 0, SEEK_END);

$RecordCount= floor((ftell($this->fp) – $this->DATAFIELDBEGIN) / $this->RECORDLENGTH);

if ($RecordCount <= 1)

{

$szLocal= "FileDataError";

$nRet= 2;

}

else

{

$RangB= 0;

$RangE= $RecordCount;

// Match …

while ($RangB < $RangE-1)

{

$RecNo= floor(($RangB + $RangE) / 2);

$this->ReadRec($RecNo);

if (strcmp($this->ip, $this->Rec->StartIP) >=0 && strcmp($this->ip, $this->Rec->EndIP) <=0 )

break; //Found match record

if (strcmp($this->ip, $this->Rec->StartIP) > 0)

$RangB= $RecNo;

else

$RangE= $RecNo;

}

if (!($RangB < $RangE-1))

{

$szLocal= "UnknowLocal!";

$nRet= 3;

}

else

{ // Match Success

$szLocal= $this->Rec->Country;

$szLocal.= $this->Rec->Local;

}

}

fclose($this->fp);

return $nRet;

}

}

/*******************************************************************

* 变更记录:

* 2002/08/10 完成版本 1.0a

* 2002/08/12 增加FormatIP成员函数,提供了对IP的标准格式化,支持

* 202.96.128.68 这类的写法,类的内部自动转为 202.096.128.068,

* 同时提供了完整的对IP地址的有效检查。规则是4个整数部分均不超

* 过255的自然数。

* ********************************************************************/

?>

<?

// Test Code.

$wru= new TWru;

$szResult="";

$ip= "202.96.134.133";

// $ip= $REMOTE_ADDR;

$wru->wru($ip, $szResult);

echo $ip."<br>";

echo $szResult;

//—————————————————————————

?>

QQWry.dll 是cnss设计的格式

采用的是索引+二分查找来减小内存占用和提高查找速度.

由于采用二分查找,所以IP数据要被分为最小的片,假设有A,B两条数据,B数据完全覆盖A数据,那么转换为QQwry 后两条数据就变成了三条.如果原始数据非常有条理,就可以避免这个现象,不过这是不可能的,几万条数据会越来越乱,所以QQwry的尺寸会迅速增加,之所以增长的不是特别快,是因为格式对重复数据有一定压缩.

QQwry.dat:"咦?我没吃那么多,怎么胖的那么快!?"

那篇文章是作者猜测的格式,我再把原来整理的发一遍吧.0×2 0×0 0×0 0×0不是错误,可能是给御风而行放版权信息的地方,另外附带ipsearcher.dll源码.

———————————————–

新格式说明

主要分为数据区和索引区

★数据区元素:

存放IP信息中的:结束IP(4字节),国家(不定长),地区(不定长)

排列顺序:无要求

★索引区元素:

存放IP信息中的:起始IP(4字节),索引值(3字节)

排列顺序:起始IP按升序排列

★IP为4字节,如"255.0.0.0"表示为0xFF000000,存在文件中则为00 00 00 FF(字节序原因)

★索引值为该IP消息的<结束IP、国家、地区>在文件中的位置。指向<结束IP>

★如果结束IP后的字节为0×01,则说明该IP消息的<国家、地区>与前面的IP信息重复,这时0×01后面的3个字节为国家、地区字符串的偏移量。可以根据这三个字节去前面找国家、地区。

★如果国家的第一个字节为0×02,说明该国家串与前面的国家或地区串重复,0×02后面的三个字节为该串的偏移量,可以根据该偏移量找到前面的串。

★如果地区的第一个字节为0×02,说明该地区串与前面的国家或地区串重复,0×02后面的三个字节为该串的偏移量,可以根据该偏移量找到前面的串。

★有可能在出现0×01的情况下出现0×02,这时需要跳转两次查找国家、地区字符串。

★正常的字符串以NULL做结尾。

★IP信息不允许有重复、覆盖

★使用索引是为了保证能以线性速度搜索

★新格式不允许为未知数据的IP消息,原格式中的未知数据已经都去掉了。如果有未知数据的IP信息,将大大增加文件长度。

文件的头4个字节是索引区第一个元素的偏移量,第二个4字节是索引区最后一个元素的偏移量。通过这两个偏移量,可以用二分法快速查找IP信息。如:一条IP信息是,要查询的IP为150

起始 结束 国家 地区

100 200 中国 北京

首先在索引区找到起始IP小于150的最后一个元素,通过索引,找到结束IP,如果150大于结束IP,说明是未知数据;如果150小于等于结束IP,则找到国家、地区。

ipsearch.dll

// ipsearcher.cpp : Defines the entry point for the DLL application.

//

#include "stdafx.h"

#include <windows.h>

#include <stdlib.h>

extern "C" __declspec(dllexport) void* __cdecl _GetAddress(const char *IPstr);

void *ret[2];          //for return

char *ptr = NULL;      //ptr of image

char *p = NULL;        //point to index

unsigned int total;    //ip count

inline unsigned int get_3b(const char *mem)

{

  return 0x00ffffff & *(unsigned int*)(mem);

}

inline void Load(void)

{

  HANDLE hnd;      //file handle

  DWORD NumberOfBytesRead; //len

  char text[2048];  //patch

  char *temp;

  unsigned int len;

//get patch

  if( !GetModuleFileName(0, text, 2048) )

    return;

  temp = strrchr(text, 92);  // 92 = ”

  *(temp + 1) = NULL;

  strcat(temp, "QQwry.dat");

//CreateFile

  hnd = CreateFile(text, GENERIC_READ, FILE_SHARE_READ, NULL, 

    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

  if(INVALID_HANDLE_VALUE == hnd)

  {

    ::MessageBox(NULL, text, "不能打开文件!", NULL);

    return;

  }

//get len

  len = SetFilePointer(hnd, NULL, NULL, FILE_END);

  SetFilePointer(hnd, NULL, NULL, FILE_BEGIN);

//malloc

  ptr = (char*)malloc(len+9);

  if(!ptr)

  {

    CloseHandle(hnd);

    ::MessageBox(NULL, "不能分配内存!", NULL, NULL);

    return;

  }

//read

  if(!ReadFile(hnd, ptr, len, &NumberOfBytesRead, NULL))

  {

    CloseHandle(hnd);

    free(ptr);

    ::MessageBox(NULL, text, "不能读入文件!", NULL);

    return;

  }

  CloseHandle(hnd);

//calc total – 1

  total = (*((unsigned int*)ptr+1) – *(unsigned int*)ptr);

//check file

  if(total % 7 != 0)

  {

    free(ptr);

    ::MessageBox(NULL, text, "QQwry.dat文件有损坏!", NULL);

    return;

  }

total /= 7;

  ++total;

  p = ptr + *(unsigned int*)ptr;  //ptr of index area

}

inline unsigned int str2ip(const char *lp)

{

  unsigned int ret = 0;

  unsigned int now = 0;

while(*lp)

  {

    if(‘.’ == *lp)

    {

      ret = 256 * ret + now;

      now = 0;

    }

    else

      now = 10 * now + *lp – ’0′;

    ++lp;

  }

ret = 256 * ret + now;

  return ret;

}

void* __cdecl _GetAddress(const char *IPstr)

{

  if(NULL == p)

  {

    ret[0] = "无法打开数据";

    ret[1] = "";

    return ret;

  }

unsigned int ip = str2ip(IPstr);

  char *now_p;

unsigned int begin = 0, end = total;

  while(1)

  {

    if( begin >= end – 1 )

      break;

    if( ip < *(unsigned int*)(p + (begin + end)/2 * 7) )

      end = (begin + end)/2;

    else

      begin = (begin + end)/2;

  }

unsigned int temp = get_3b(p + 7 * begin + 4);

  if(ip <= *(unsigned int*)(ptr + temp)) //ok, found

  {

    now_p = ptr + temp + 4;

    if( 0×01 == *now_p )

      now_p = ptr + get_3b(now_p + 1);

    //country

    if( 0×02 == *now_p ) //jump

    {

      ret[0] = ptr + get_3b(now_p + 1);

      now_p += 4;

    }

    else

    {

      ret[0] = now_p;

      for(; *now_p; ++now_p)

        ;

      ++now_p;

    }

    //local

    if( 0×02 == *now_p ) //jump

      ret[1] = ptr + get_3b(now_p + 1);

    else

      ret[1] = now_p;

  }

  else

  {

    ret[0] = "未知数据";

    ret[1] = "";

  }

  return ret;

}

BOOL APIENTRY DllMain( HANDLE hModule, 

      DWORD  ul_reason_for_call, 

      LPVOID lpReserved

      )

{

  switch(ul_reason_for_call)

  {

    //case DLL_THREAD_ATTACH:

    //case DLL_THREAD_DETACH:

    case DLL_PROCESS_ATTACH:  //attach

    {

      Load();

    } 

    break;

    //————————————

    case DLL_PROCESS_DETACH:  //detach

    {

      free(ptr); 

    }

  }

  return true;

}

<?php

$ip = $REMOTE_ADDR; // Get visitor’s ip address

$connection = mysql_connect("your host", "your login", "your password") or die ("ERROR");

$db= mysql_select_db(your_db,$connection) or die ("ERROR");

$fact = explode(".",$ip); // create an array with the ip numbers

$ip_code = $fact[3] + $fact[2]*256 + $fact[1] * 65536 + $fact[0] * 16777216; // calculate the base 10 ip

// now I execute a query to select a ip range that fits with my visitor’s ip

$sql = "select * from world_ip where $ip_code <= real_end_ip and $pi_code >= real_start_ip";

$result = mysql_query($sql,$connection) or die ("ERROR in $sql");

$row = mysql_fetch_array($result);

echo $row[long_nation];

?>