QQ IP 地址查询相关

来源:互联网 发布:live photos软件 编辑:程序博客网 时间:2024/04/29 19:38

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];

?>