`
hzy3774
  • 浏览: 984859 次
  • 性别: Icon_minigender_1
  • 来自: 珠海
社区版块
存档分类
最新评论

C语言解析QQwry.dat

 
阅读更多
QQWry.Dat是显IP版QQ(彩虹外挂等),纯真IP数据库查询器等的IP数据库文件可以通过下载纯真IP数据库查询器来得到这个文件,纯真IP数据库查询器拥有在线更新IP库功能可以拥有最新的QQWry.Dat文件
 [文件头] + [结束IP + 地区1 + 地区2][m] + [开始IP + 结束IP偏移][n]

基本结构

QQWry.dat文件在结构上分为3块:文件头,记录区,索引区。一般我们要查找IP时,先在索引区查找记录偏移,然后再到记录区读出信息。由于记录区的记录是不定长的,所以直接在记录区中搜索是不可能的。由于记录数比较多,如果我们遍历索引区也会是有点慢的,一般来说,我们可以用二分查找法搜索索引区,其速度比遍历索引区快若干数量级。图1是QQWry.dat的文件结构图。


 

图1. QQWry.dat文件结构

要注意的是,QQWry.dat里面全部采用了little-endian字节序

一. 了解文件头

QQWry.dat的文件头只有8个字节,其结构非常简单,首四个字节是第一条索引的绝对偏移,后四个字节是最后一条索引的绝对偏移。

二. 了解记录区

每条IP记录都由国家和地区名组成,国家地区在这里并不是太确切,因为可能会查出来”清华大学计算机系”之类的,这里清华大学就成了国家名了,所以这个国家地区名和IP数据库制作的时候有关系。所以记录的格式有点像QName,有一个全局部分和局部部分组成,我们这里还是沿用国家名和地区名的说法。

于是我们想象着一条记录的格式应该是: [IP地址][国家名][地区名],当然,这个没有什么问题,但是这只是最简单的情况。很显然,国家名和地区名可能会有很多的重复,如果每条记录都保存一个完整的名称拷贝是非常不理想的,所以我们就需要重定向以节省空间。所以为了得到一个国家名或者地区名,我们就有了两个可能:第一就是直接的字符串表示的国家名,第二就是一个4字节的结构,第一个字节表明了重定向的模式,后面3个字节是国家名或者地区名的实际偏移位置。对于国家名来说,情况还可能更复杂些,因为这样的重定向最多可能有两次。

那么什么是重定向模式?根据上面所说,一条记录的格式是[IP地址][国家记录][地区记录],如果国家记录是重定向的话,那么地区记录是有可能没有的,于是就有了两种情况,我管他叫做模式1和模式2。我们对这些格式的情况举图说明:


 

图2. IP记录的最简单形式

图2表示了最简单的IP记录格式,我想没有什么可以解释的


 

图3. 重定向模式1

图3演示了重定向模式1的情况。我们看到在模式1的情况下,地区记录也跟着国家记录走了,在IP地址之后只剩下了国家记录的4字节,后面3个字节构成了一个指针,指向了实际的国家名,然后又跟着地址名。模式1的标识字节是0×01。


 

图4. 重定向模式2

图4演示了重定向模式2的情况。我们看到了在模式2的情况下(其标识字节是0×02),地区记录没有跟着国家记录走,因此在国家记录之后4个字节之后还是有地区记录。我想你已经明白了模式1和模式2的区别,即:模式1的国家记录后面不会再有地区记录,模式2的国家记录后会有地区记录。下面我们来看一下更复杂的情况。


 

图5. 混和情况1

图5演示了当国家记录为模式1的时候可能出现的更复杂情况,在这种情况下,重定向指向的位置仍然是个重定向,不过第二次重定向为模式2。大家不用担心,没有模式3了,这个重定向也最多只有两次,并且如果发生了第二次重定向,则其一定为模式2,而且这种情况只会发生在国家记录上,对于地区记录,模式1和模式2是一样的,地区记录也不会发生2次重定向。不过,这个图还可以更复杂,如图7:


 

图6. 混和情况2

图6是模式1下最复杂的混和情况,不过我想应该也很好理解,只不过地区记录也来重定向而已,有一点我要提醒你,如果重定向的地址是0,则表示未知的地区名。

所以我们总结如下:一条IP记录由[IP地址][国家记录][地区记录]组成,对于国家记录,可以有三种表示方式:字符串形式,重定向模式1和重定向模式2。对于地区记录,可以有两种表示方式:字符串形式和重定向,另外有一条规则:重定向模式1的国家记录后不能跟地区记录。按照这个总结,在这些方式中合理组合,就构成了IP记录的所有可能情况。

设计的理由

在我们继续去了解索引区的结构之前,我们先来了解一下为何记录区的结构要如此设计。我想你可能想到了答案:字符串重用。没错,在这种结构下,对于一个国家名和地区名,我只需要保存其一次就可以了。我们举例说明,为了表示方便,我们用小写字母代表IP记录,C表示国家名,A表示地区名:

  1. 有两条记录a(C1, A1), b(C2, A2),如果C1 = C2, A1 = A2,那么我们就可以使用图3显示的结构来实现重用
  2. 有三条记录a(C1, A1), b(C2, A2), c(C3, A3),如果C1 = C2, A2 = A3,现在我们想存储记录b,那么我们可以用图6的结构来实现重用
  3. 有两条记录a(C1, A1), b(C2, A2),如果C1 = C2,现在我们想存储记录b,那么我们可以采用模式2表示C2,用字符串表示A2

你可以举出更多的情况,你也会发现在这种结构下,不同的字符串只需要存储一次。

了解索引区

在”了解文件头”部分,我们说明了文件头实际上是两个指针,分别指向了第一条索引和最后一条索引的绝对偏移。如图8所示:


 

图8. 文件头指向索引区图示

实在是很简单,不是吗?从文件头你就可以定位到索引区,然后你就可以开始搜索IP了!每条索引长度为7个字节,前4个字节是起始IP地址,后三个字节就指向了IP记录。这里有些概念需要说明一下,什么是起始IP,那么有没有结束IP? 假设有这么一条记录:166.111.0.0 – 166.111.255.255,那么166.111.0.0就是起始IP,166.111.255.255就是结束IP,结束IP就是IP记录中的那头4个字节,这下你应该就清楚了吧。于是乎,每条索引配合一条记录,构成了一个IP范围,如果你要查找166.111.138.138所在的位置,你就会发现166.111.138.138落在了166.111.0.0 – 166.111.255.255 这个范围内,那么你就可以顺着这条索引去读取国家和地区名了。那么我们给出一个最详细的图解吧:


 

图9. 文件详细结构

现在一切都清楚了是不是?也许还有一点你不清楚,QQWry.dat的版本信息存在哪里呢? 答案是:最后一条IP记录实际上就是版本信息,最后一条记录显示出来就是这样:255.255.255.0 255.255.255.255 纯真网络 2004年6月25日IP数据。OK,到现在你应该全部清楚了。

 

首先我们来获取这两个偏移值:
#include <stdio.h>

#define DAT_FILE "../ipdat/qqwry.dat"

void parseDat(FILE *fp);

int main(void)
{
	FILE *fp = NULL;

	fp = fopen(DAT_FILE, "rb");
	if (fp == NULL )
	{
		printf("open file error!\n");
		return 0;
	}
	parseDat(fp);
	fclose(fp);
	return 0;
}

void parseDat(FILE *fp)
{
	unsigned int startIndexOffset = 0;
	unsigned int endIndexOffset = 0;
	fseek(fp, 0L, SEEK_SET);
	fread(&startIndexOffset, 4, 1, fp);
	fread(&endIndexOffset, 4, 1, fp);
	printf("%u ~ %u", startIndexOffset, endIndexOffset);
}
 结果:
6240699 ~ 9348825
 
 获取开始的几条开始IP和结束IP:
#include <stdio.h>

#define DAT_FILE "../ipdat/qqwry.dat"

void parseDat(FILE *fp);

int main(void)
{
	FILE *fp = NULL;

	fp = fopen(DAT_FILE, "rb");
	if (fp == NULL )
	{
		printf("open file error!\n");
		return 0;
	}
	parseDat(fp);
	fclose(fp);
	return 0;
}

void parseDat(FILE *fp)
{
	long int startIndexOffset = 0;
	long int endIndexOffset = 0;
	long int indexOffset = 0;
	long int recordOffset = 0;
	unsigned int startIp = 0;
	unsigned int endIp = 0;

	int i;

	fseek(fp, 0L, SEEK_SET);
	fread(&startIndexOffset, 4, 1, fp);
	fread(&endIndexOffset, 4, 1, fp);
	indexOffset = startIndexOffset;

	for (i = 0; i < 16; i++)
	{
		fseek(fp, indexOffset, SEEK_SET);
		fread(&startIp, 4, 1, fp);
		fread(&recordOffset, 3, 1, fp);
		indexOffset = ftell(fp);
		printf("%u.%u.%u.%u ~ ", (startIp >> 24) & 0xff, (startIp >> 16) & 0xff, (startIp >> 8) & 0xff, startIp & 0xff);
		fseek(fp, recordOffset, SEEK_SET);
		fread(&endIp, 4, 1, fp);
		printf("%u.%u.%u.%u\n", (endIp >> 24) & 0xff, (endIp >> 16) & 0xff, (endIp >> 8) & 0xff, endIp & 0xff);
	}
}
 结果:
0.0.0.0 ~ 0.255.255.255
1.0.0.0 ~ 1.0.0.255
1.0.1.0 ~ 1.0.3.255
1.0.4.0 ~ 1.0.7.255
1.0.8.0 ~ 1.0.15.255
1.0.16.0 ~ 1.0.31.255
1.0.32.0 ~ 1.0.63.255
1.0.64.0 ~ 1.0.127.255
1.0.128.0 ~ 1.0.255.255
1.1.0.0 ~ 1.1.0.255
1.1.1.0 ~ 1.1.1.255
1.1.2.0 ~ 1.1.7.255
1.1.8.0 ~ 1.1.63.255
1.1.64.0 ~ 1.1.127.255
1.1.128.0 ~ 1.1.255.255
1.2.0.0 ~ 1.2.1.255
 将所有数据另存在txt文本文件中:
#include <stdio.h>

#define DAT_FILE "../ipdat/qqwry.dat"
#define OUT_FILE "../ipdat/out.txt"

FILE *out = NULL;

void showIp(unsigned int ip)
{
	char buffer[20] = {0};
	sprintf(buffer, "%u.%u.%u.%u  ", (ip >> 24) & 0xff, (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff);
	fputs(buffer, out);
}

void getString(FILE *fp)	//讀取字符串并写入文件,以0为结尾标志
{
	char temp[128] = {0};
	int index = 0;
	while ((temp[index] = fgetc(fp)) != 0)
	{
		index++;
	}
	fputs(temp, out);
}

void getInfo(FILE *fp, int flag)	//获取数据区字符串信息,flag为1时表示已经读了一半字符串
{
	unsigned char mod;
	long int offset = 0;
	long int preOffset = 0;

	mod = fgetc(fp);			//获取模式
	if (mod > 2)				//如果无重定向
	{
		fseek(fp, -1L, SEEK_CUR);	//返回
		getString(fp);			//读取一个字符串
		if (flag == 0)			//如果调用函数时还未读
		{
			getInfo(fp, 1);		//标志置1,递归调用
		}
		return;
	}
	fread(&offset, 3, 1, fp);		//如果有重定向,获取重定向偏移
	if (mod == 1)					//如果模式一
	{
		fseek(fp, offset, SEEK_SET);	//seek到目标地址
		getInfo(fp, flag);				//递归读
		return;
	}
	preOffset = ftell(fp);			//如果模式二,记住当前偏移
	fseek(fp, offset, SEEK_SET);	//寻目标地址
	getInfo(fp, 1);					//读一个字符串
	if (flag == 0)					//如果调用函数时还未读
	{
		fseek(fp, preOffset, SEEK_SET);		//还要回到原来位置
		getInfo(fp, 1);						//再读一次
	}
}

void parseRecord(FILE *fp, long int recordOffset)
{
	unsigned int endIp = 0;

	fseek(fp, recordOffset, SEEK_SET);	//seek到数据区
	fread(&endIp, 4, 1, fp);		//读取结束IP
	showIp(endIp);					//显示结束IP
	getInfo(fp, 0);					//获取信息
	fputs("\n", out);
}

void parseDat(FILE *fp)
{
	long int startIndexOffset = 0;
	long int endIndexOffset = 0;
	long int indexOffset = 0;
	long int recordOffset = 0;
	unsigned int startIp = 0;

	fseek(fp, 0L, SEEK_SET);			//seek文件头
	fread(&startIndexOffset, 4, 1, fp);	//读索引开始偏移量
	fread(&endIndexOffset, 4, 1, fp);	//读索引结束偏移量
	indexOffset = startIndexOffset;
	while (indexOffset <= endIndexOffset)	//循环从根据索引读取数据
	{
		fseek(fp, indexOffset, SEEK_SET);	//seek到索引位置
		fread(&startIp, 4, 1, fp);			//读取索引中的开始IP
		fread(&recordOffset, 3, 1, fp);		//读取对应数据区的偏移量
		indexOffset = ftell(fp);			//记住索引的偏移量
		showIp(startIp);					//显示开始IP
		parseRecord(fp, recordOffset);		//读取数据区
	}
}

int main(void)
{
	FILE *fp = NULL;

	out = fopen(OUT_FILE, "wb");
	fp = fopen(DAT_FILE, "rb");
	if (fp == NULL )
	{
		printf("open file error!\n");
		return 0;
	}
	parseDat(fp);
	fclose(fp);
	return 0;
}
 结果:
223.255.232.0   223.255.235.255 澳大利亚 北领地Territory Technology Solutions公司
223.255.236.0   223.255.239.255 上海市 康宝莱(中国)保健品有限公司
223.255.240.0   223.255.243.255 香港 五邑發展有限公司
223.255.244.0   223.255.247.255 印度  CZ88.NET
223.255.248.0   223.255.251.255 澳大利亚 新南威尔士州Viocorp国际控股有限公司
223.255.252.0   223.255.253.255 福建省 电信
223.255.254.0   223.255.254.255 新加坡 滨海湾金沙私人有限公司
223.255.255.0   223.255.255.255 澳大利亚  CZ88.NET
224.0.0.0       224.255.255.255 IANA  CZ88.NET
225.0.0.0       239.255.255.255 IANA保留地址 用于多点传送
240.0.0.0       247.255.255.255 IANA保留地址  CZ88.NET
248.0.0.0       248.255.255.255 IANA保留地址  CZ88.NET
249.0.0.0       254.255.255.255 IANA保留地址  CZ88.NET
255.0.0.0       255.255.254.255  CZ88.NET
255.255.255.0   255.255.255.255 纯真网络 2013年4月20日IP数据
 能读到最后一条。
倒数第二条的数据很特殊,只有一个字符串,很奇怪。
有了这个,想把这些数据写成SQL语句,方便以后插入数据库,操作也不难了,不过这样做文件和数据库会很大:
#include <stdio.h>

#define DAT_FILE "../ipdat/qqwry.dat"
#define OUT_FILE "../ipdat/out.sql"

FILE *out = NULL;

void showIp(unsigned int ip)
{
	char buffer[20] = {0};
	sprintf(buffer, "\"%u.%u.%u.%u\"", (ip >> 24) & 0xff, (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff);
	fputs(buffer, out);
}

void getString(FILE *fp)	//讀取字符串并写入文件,以0为结尾标志
{
	char temp[128] = {0};
	int index = 0;
	while ((temp[index] = fgetc(fp)) != 0)
	{
		index++;
	}
	fputs(temp, out);
}

void getInfo(FILE *fp, int flag)	//获取数据区字符串信息,flag为1时表示已经读了一半字符串
{
	unsigned char mod;
	long int offset = 0;
	long int preOffset = 0;

	mod = fgetc(fp);			//获取模式
	if (mod > 2)				//如果无重定向
	{
		fseek(fp, -1L, SEEK_CUR);	//返回
		getString(fp);			//读取一个字符串
		if (flag == 0)			//如果调用函数时还未读
		{
			getInfo(fp, 1);		//标志置1,递归调用
		}
		return;
	}
	fread(&offset, 3, 1, fp);		//如果有重定向,获取重定向偏移
	if (mod == 1)					//如果模式一
	{
		fseek(fp, offset, SEEK_SET);	//seek到目标地址
		getInfo(fp, flag);				//递归读
		return;
	}
	preOffset = ftell(fp);			//如果模式二,记住当前偏移
	fseek(fp, offset, SEEK_SET);	//寻目标地址
	getInfo(fp, 1);					//读一个字符串
	if (flag == 0)					//如果调用函数时还未读
	{
		fseek(fp, preOffset, SEEK_SET);		//还要回到原来位置
		getInfo(fp, 1);						//再读一次
	}
}

void parseRecord(FILE *fp, long int recordOffset)
{
	unsigned int endIp = 0;

	fseek(fp, recordOffset, SEEK_SET);	//seek到数据区
	fread(&endIp, 4, 1, fp);		//读取结束IP
	fputs(",", out);
	showIp(endIp);					//显示结束IP
	fputs(",\"", out);
	getInfo(fp, 0);					//获取信息
	fputs("\");\n", out);
}

void parseDat(FILE *fp)
{
	long int startIndexOffset = 0;
	long int endIndexOffset = 0;
	long int indexOffset = 0;
	long int recordOffset = 0;
	unsigned int startIp = 0;

	fputs("CREATE TABLE qqwry(start_ip varchar(16), end_ip varchar(16), address varchar(32));\n", out);
	fseek(fp, 0L, SEEK_SET);			//seek文件头
	fread(&startIndexOffset, 4, 1, fp);	//读索引开始偏移量
	fread(&endIndexOffset, 4, 1, fp);	//读索引结束偏移量
	indexOffset = startIndexOffset;
	while (indexOffset <= endIndexOffset)	//循环从根据索引读取数据
	{
		fseek(fp, indexOffset, SEEK_SET);	//seek到索引位置
		fread(&startIp, 4, 1, fp);			//读取索引中的开始IP
		fread(&recordOffset, 3, 1, fp);		//读取对应数据区的偏移量
		indexOffset = ftell(fp);			//记住索引的偏移量
		fputs("INSERT INTO qqwry VALUES(", out);
		showIp(startIp);					//显示开始IP
		parseRecord(fp, recordOffset);		//读取数据区
	}
}

int main(void)
{
	FILE *fp = NULL;

	out = fopen(OUT_FILE, "wb");
	fp = fopen(DAT_FILE, "rb");
	if (fp == NULL )
	{
		printf("open file error!\n");
		return 0;
	}
	parseDat(fp);
	fclose(fp);
	return 0;
}
 生成的SQL语句:
CREATE TABLE qqwry(start_ip varchar(16), end_ip varchar(16), address varchar(32));
INSERT INTO qqwry VALUES("0.0.0.0","0.255.255.255","IANA保留地址 CZ88.NET");
INSERT INTO qqwry VALUES("1.0.0.0","1.0.0.255","澳大利亚 CZ88.NET");
INSERT INTO qqwry VALUES("1.0.1.0","1.0.3.255","福建省电信");
INSERT INTO qqwry VALUES("1.0.4.0","1.0.7.255","澳大利亚 CZ88.NET");
INSERT INTO qqwry VALUES("1.0.8.0","1.0.15.255","广东省电信");
INSERT INTO qqwry VALUES("1.0.16.0","1.0.31.255","日本Beacon服务器");
INSERT INTO qqwry VALUES("1.0.32.0","1.0.63.255","广东省电信");
INSERT INTO qqwry VALUES("1.0.64.0","1.0.127.255","日本 CZ88.NET");
INSERT INTO qqwry VALUES("1.0.128.0","1.0.255.255","泰国 CZ88.NET");
INSERT INTO qqwry VALUES("1.1.0.0","1.1.0.255","福建省电信");
INSERT INTO qqwry VALUES("1.1.1.0","1.1.1.255","澳大利亚 CZ88.NET");
INSERT INTO qqwry VALUES("1.1.2.0","1.1.7.255","福建省电信");
INSERT INTO qqwry VALUES("1.1.8.0","1.1.63.255","广东省电信");
INSERT INTO qqwry VALUES("1.1.64.0","1.1.127.255","日本 CZ88.NET");
INSERT INTO qqwry VALUES("1.1.128.0","1.1.255.255","泰国 CZ88.NET");
INSERT INTO qqwry VALUES("1.2.0.0","1.2.1.255","福建省电信");
INSERT INTO qqwry VALUES("1.2.2.0","1.2.2.255","北京市海淀区北龙中网(北京)科技有限责任公司");
 
查询数据的话可以参照谷歌项目:
main函数:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include "qqwry.h"

int main(void)
{
    char ip[32] = {0};
    char country[1024]={0};
    char area[1024]={0};

    FILE *wry_file;
    wry_file = fopen("../ipdat/qqwry.dat","rb");
    gets(ip);
    qqwry_get_location(country,area,ip,wry_file);
    fclose(wry_file);

    if (strlen(country)>0) {
        printf("%s",country);
    }
    if (strlen(area)>0) {
        if (strlen(country)>0) {
            printf(" ");
        }
        if (strlen(country)<=0) {
            printf("unknown");
        } else {
            printf("%s",area);
        }
    }
    printf("\n");
    return 0;
}

 QQwry.c
/*
 * Copyright 2008,2009 Surf Chen <http://www.surfchen.org>
 *
 *
 * This source code is under the terms of the
 * GNU Lesser General Public License. 
 * see <http://www.gnu.org/licenses/lgpl.txt>
 */

#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#define BE_32(x) ((((uint8_t*)(x))[0]<<24) |\
                  (((uint8_t*)(x))[1]<<16) |\
                  (((uint8_t*)(x))[2]<<8) |\
                  ((uint8_t*)(x))[3])

#define LE_32(x) ((((uint8_t*)(x))[3]<<24) |\
                  (((uint8_t*)(x))[2]<<16) |\
                  (((uint8_t*)(x))[1]<<8) |\
                  ((uint8_t*)(x))[0])

#define LE_24(x) ((((uint8_t*)(x))[2]<<16) |\
                  (((uint8_t*)(x))[1]<<8) |\
                  ((uint8_t*)(x))[0])

#define REDIRECT_TYPE_1 0x01
#define REDIRECT_TYPE_2 0x02

static uint32_t ip2long(const char *ip) {
    uint32_t ip_long=0;
    uint8_t ip_len=strlen(ip);
    uint32_t ip_sec=0;
    int8_t ip_level=3;
    uint8_t i,n;
    for (i=0;i<=ip_len;i++) {
        if (i!=ip_len && ip[i]!='.' && ip[i]<48 || ip[i]>57) {
            continue;
        }
        if (ip[i]=='.' || i==ip_len) {
            /*too many .*/
            if (ip_level==-1) {
                return 0;
            }
            for (n=0;n<ip_level;n++) {
                ip_sec*=256;
            }
            ip_long+=ip_sec;
            if (i==ip_len) {
                break;
            }
            ip_level--;
            ip_sec=0;
        } else {
            /*char '0' == int 48*/
            ip_sec=ip_sec*10+(ip[i]-48);
        }
    }
    return ip_long;
}
static uint32_t search_index(const uint32_t ip,FILE *qqwry_file) {
    uint32_t index_ip;
    unsigned char head[8];
    unsigned char index_bytes[7];
    fread(head,8,1,qqwry_file);
    uint32_t index_start,index_end,index_mid;
    index_start = (uint32_t)LE_32(&head[0]);
    index_end = (uint32_t)LE_32(&head[4]);
    while (1) {
        if ((index_end-index_start)==7) {
            break;
        }
        //printf("index:%u:%u\n",index_start,index_end);
        index_mid=index_end/7 - index_start/7;
        if (index_mid%2==0) {
            index_mid=index_mid/2;
        } else {
            index_mid=(index_mid+1)/2;
        }
        index_mid=index_start+index_mid*7;
        fseek(qqwry_file,index_mid,SEEK_SET);
        fread(index_bytes,7,1,qqwry_file);
        index_ip=(uint32_t)LE_32(&index_bytes[0]);
        if (index_ip==ip) {
            break;
        } else if (index_ip<ip) {
            index_start=index_mid;
        } else {
            index_end=index_mid;
        }
    }
    if (index_ip>ip) {
        fseek(qqwry_file,index_start,SEEK_SET);
        fread(index_bytes,7,1,qqwry_file);
    }
    return (uint32_t)LE_24(&index_bytes[4]);
}
static int readOrJumpRead(char *location,FILE *qqwry_file,const uint32_t data_index) {
    unsigned char c;
    unsigned char data_index_bytes[3];
    uint32_t jump_data_index=0;
    if (data_index) {
        fseek(qqwry_file,data_index,SEEK_SET);
    }
    c=fgetc(qqwry_file);
    switch (c) {
        case REDIRECT_TYPE_2:
        case REDIRECT_TYPE_1:
            fread(data_index_bytes,3,1,qqwry_file);
            jump_data_index=LE_24(&data_index_bytes[0]);
            fseek(qqwry_file,jump_data_index,SEEK_SET);
            break;
        default:
            location[strlen(location)]=c;
    }
    if (c) {
        while (c=fgetc(qqwry_file)) {
            location[strlen(location)]=c;
        }
    }
    return 1;
}
static int is_cz88(const char *str) {
    int i;
    int l=strlen(str)-7;
    for (i=0;i<l;i++) {
        if (str[i]=='C'
            && str[i+1]=='Z'
            && str[i+2]=='8'
            && str[i+3]=='8'
            && str[i+4]=='.'
            && str[i+5]=='N'
            && str[i+6]=='E'
            && str[i+7]=='T'
        ) {
            return 1;
        }
    }
    return 0;
}
int qqwry_get_location_by_long(char *addr1,char *addr2,const uint32_t ip,FILE *qqwry_file) {
    //printf("%u",ip);
    unsigned char data_index_bytes[3];
    uint32_t data_index;
    uint32_t addr2_offset;
    unsigned char c;

    if (!qqwry_file) {
        return 0;
    }
    fseek(qqwry_file,0,SEEK_SET);
    data_index = search_index(ip,qqwry_file);
    //fprintf(stderr,"index:%u:%u\n",ftell(qqwry_file),data_index);

    /*ip 4 + mode byte 1*/
    fseek(qqwry_file,data_index+4,SEEK_SET);
    c=fgetc(qqwry_file);
    if (c==REDIRECT_TYPE_1) {
        fread(data_index_bytes,3,1,qqwry_file);
        data_index=LE_24(&data_index_bytes[0]);
        fseek(qqwry_file,data_index,SEEK_SET);
        c=fgetc(qqwry_file);
        /*制造一个假的4bytes位移,抵充ip*/
        data_index-=4;
    }

    if (c==REDIRECT_TYPE_2) {
        /*
         * ip 4 + mode byte 1 + addr1 offset 3
         * 这里ip的4个bytes不一定是真的,有可能是上一条注释里提到的情况
         */
        addr2_offset=data_index+8;
        fread(data_index_bytes,3,1,qqwry_file);

        data_index=LE_24(&data_index_bytes[0]);
        fseek(qqwry_file,data_index,SEEK_SET);
        while (c=fgetc(qqwry_file)) {
            addr1[strlen(addr1)]=c;
        }
        readOrJumpRead(addr2,qqwry_file,addr2_offset);
    } else {
        addr1[strlen(addr1)]=c;
        while (c=fgetc(qqwry_file)) {
            addr1[strlen(addr1)]=c;
        }
        readOrJumpRead(addr2,qqwry_file,0);
    }
    if (is_cz88(addr1)) {
        addr1[0]='\0';
    }
    if (is_cz88(addr2)) {
        addr2[0]='\0';
    }
    return 1;
}
int qqwry_get_location(char *addr1,char *addr2,const char *ip,FILE *qqwry_file) {
    return qqwry_get_location_by_long(addr1,addr2,ip2long(ip),qqwry_file);
}
 
qqwry.h
/*
 * Copyright 2008,2009 Surf Chen <http://www.surfchen.org>
 *
 *
 * This source code is under the terms of the
 * GNU Lesser General Public License. 
 * see <http://www.gnu.org/licenses/lgpl.txt>
 */

#include <inttypes.h>
/*
 * 请自己给addr1和addr2分配内存,我的建议是:addr1为64字节,addr2为128字节。
 * 这个库不会自己分配内存,而是把结果写入addr1和addr2的内存。
 * 这样的设计可以使得调用方更方便地使用自己的内存机制。
 *
 * addr1是大的范围,例如南宁市
 * addr2是小的范围,例如邕宁区
 *
 * get_location和get_location_by_long的区别是前者传递的ip是一个字符串,例如
 * 222.89.22.122,这个也是我们通常所用的格式;后者传递的ip是一个十进制的ipv4地址
 */
int qqwry_get_location(char *addr1,char *addr2,const char *ip,FILE *qqwry_file);
int qqwry_get_location_by_long(char *addr1,char *addr2,const uint32_t ip,FILE *qqwry_file);
 
分享到:
评论
1 楼 di1984HIT 2014-06-22  
写的很好啊。

相关推荐

Global site tag (gtag.js) - Google Analytics