`
hzy3774
  • 浏览: 998537 次
  • 性别: 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  
写的很好啊。

相关推荐

    获取指定ip地址区域信息(c)

    在这个项目中,我们可能需要读取QQWry.dat文件,解析其中的IP地址和地理位置信息,然后根据用户输入的IP地址或包含特定字符串的国家/地区进行查询。 4. 文件结构与API设计: - `qqwry.h`:这可能是头文件,包含了...

    C2000系列DSP芯片串口读写方案与FlashPro2000编程器应用详解

    内容概要:本文详细介绍了基于TMS320F系列芯片的C2000串口读写方案及其编程器——FlashPro2000的功能特点和支持的接口模式。文中不仅涵盖了硬件连接的具体步骤,还提供了代码实例来展示Flash擦除操作,并对比了JTAG和SCI-BOOT两种模式的优缺点。此外,针对不同型号的C2000系列芯片,给出了详细的适配指导以及避免烧录过程中可能出现的问题的方法。 适合人群:从事DSP开发的技术人员,尤其是对TI公司C2000系列芯片有一定了解并希望深入了解其编程和烧录细节的人群。 使用场景及目标:适用于实验室环境下的程序调试阶段,以及生产线上的批量烧录任务。主要目的是帮助开发者选择合适的编程工具和技术手段,提高工作效率,减少因误操作导致设备损坏的风险。 其他说明:文中提供的代码片段和命令行指令可以直接用于实际项目中,同时附带了一些实用技巧,如防止芯片变砖的小贴士和自动化重试脚本,有助于解决常见的烧录难题。

    汉字字库存储芯片扩展实验通常是为了学习和理解如何在嵌入式系统或计算机硬件中增加或管理存储资源,特别是针对需要处理中文字符的应用 这类实验对于想要深入了解计算机体系结构、嵌入式开发以及汉字编码的学生和工

    汉字字库存储芯片扩展实验 # 汉字字库存储芯片扩展实验 ## 实验目的 1. 了解汉字字库的存储原理和结构 2. 掌握存储芯片扩展技术 3. 学习如何通过硬件扩展实现大容量汉字字库存储 ## 实验原理 ### 汉字字库存储基础 - 汉字通常采用点阵方式存储(如16×16、24×24、32×32点阵) - 每个汉字需要占用32字节(16×16)到128字节(32×32)不等的存储空间 - 国标GB2312-80包含6763个汉字,需要较大存储容量 ### 存储芯片扩展方法 1. **位扩展**:增加数据总线宽度 2. **字扩展**:增加存储单元数量 3. **混合扩展**:同时进行位扩展和字扩展 ## 实验设备 - 单片机开发板(如STC89C52) - 存储芯片(如27C256、29C040等) - 逻辑门电路芯片(如74HC138、74HC373等) - 示波器、万用表等测试设备 - 连接线若干 ## 实验步骤 ### 1. 单芯片汉字存储实验 1. 连接27C256 EPROM芯片到单片机系统 2. 将16×16点阵汉字字库写入芯片 3. 编写程序读取并显示汉字 ### 2. 存储芯片字扩展实验 1. 使用地址译码器(如74HC138)扩展多片27C256 2. 将完整GB2312字库分布到各芯片中 3. 编写程序实现跨芯片汉字读取 ### 3. 存储芯片位扩展实验 1. 连接两片27C256实现16位数据总线扩展 2. 优化字库存储结构,提高读取速度 3. 测试并比较扩展前后的性能差异 ## 实验代码示例(单片机部分) ```c #include <reg52.h> #include <intrins.h> // 定义存储芯片控制引脚 sbit CE = P2^7; // 片选 sbit OE = P2^6; // 输出使能 sbit

    测控装备干扰源快速侦测系统设计研究.pdf

    测控装备干扰源快速侦测系统设计研究.pdf

    嵌入式八股文面试题库资料知识宝典-【开发】嵌入式开源项目&库&资料.zip

    嵌入式八股文面试题库资料知识宝典-【开发】嵌入式开源项目&库&资料.zip

    嵌入式八股文面试题库资料知识宝典-百度2022年嵌入式面试题.zip

    嵌入式八股文面试题库资料知识宝典-百度2022年嵌入式面试题.zip

    少儿编程scratch项目源代码文件案例素材-空间站.zip

    少儿编程scratch项目源代码文件案例素材-空间站.zip

    基于关联规则的商业银行个性化产品推荐.pdf

    基于关联规则的商业银行个性化产品推荐.pdf

    嵌入式八股文面试题库资料知识宝典-Linux基础使用.zip

    嵌入式八股文面试题库资料知识宝典-Linux基础使用.zip

    MATLAB仿真轴棱锥生成贝塞尔高斯光束及环形光束光强图像分析

    内容概要:本文详细介绍了利用MATLAB进行轴棱锥生成贝塞尔高斯光束及环形光束光强图像的仿真研究。首先阐述了实验的背景与目标,强调了MATLAB在光学和计算科学领域的广泛应用。接着,具体描述了实验的方法与步骤,包括材料准备、仿真过程中的参数设定和光束生成代码编写。最后,对实验结果进行了深入分析,展示了贝塞尔高斯光束和环形光束的光强分布特点,验证了其光学性能的预期表现。文章还对未来的研究方向和技术改进提出了展望。 适合人群:从事光学、物理学及相关领域研究的专业人士,特别是对光束生成和光学性能分析感兴趣的科研工作者。 使用场景及目标:适用于需要进行光束生成和性能分析的实验室环境,旨在帮助研究人员更好地理解和优化光束特性和传播行为。 其他说明:本文不仅提供了详细的实验方法和步骤,还附有丰富的实验结果和数据分析,为后续研究提供了宝贵的参考资料。

    三电平NPC型APF模型预测控制中滞环控制模块的应用与开关频率优化研究

    内容概要:本文探讨了三电平NPC型有源电力滤波器(APF)的模型预测控制(MPC)中存在的开关频率过高问题及其解决方案。传统MPC方法会导致极高的开关频率,增加了系统的能耗和热量。通过引入滞环控制模块,可以在不大幅牺牲性能的情况下有效降低开关频率。具体来说,滞环控制通过在价值函数计算后增加一个判断条件,对状态切换进行惩罚,从而减少不必要的开关动作。实验结果显示,开关频率从4392Hz降至3242Hz,降幅达26.2%,虽然电流总谐波畸变率(THD)略有上升,但仍符合国家标准。此外,文中还提出了动态调整滞环宽度的方法,以进一步优化不同负载条件下的表现。 适合人群:从事电力电子、电力系统控制领域的研究人员和技术人员,特别是关注APF和MPC技术的人群。 使用场景及目标:适用于需要优化APF系统开关频率的研究和工程项目,旨在提高系统效率并降低成本。目标是在不影响系统性能的前提下,显著降低开关频率,减少能量损失和热管理难度。 其他说明:文章不仅提供了理论分析,还包括具体的实现代码片段,有助于读者理解和实践。同时,强调了在实际应用中需要注意的问题,如中点电位漂移等。

    计算流体力学中三维POD DMD程序的原网格处理方法及应用

    内容概要:本文介绍了三维POD DMD程序在处理原网格数据方面的独特优势和技术细节。首先阐述了该程序能读取结构化和非结构化网格数据及其拓扑关系,在生成模态数据过程中保持原始网格形态而不需要进行网格插值操作。接着展示了简化版本的Python代码片段,揭示了读取网格数据和生成模态数据的核心逻辑。最后提到提供的辅助学习资料如代码、视频教程、Word教程和实例数据,帮助用户深入理解并掌握该程序的应用。 适合人群:从事计算流体力学领域的研究人员和技术爱好者,尤其是那些希望提高数据处理效率的人群。 使用场景及目标:适用于需要处理复杂网格数据的研究项目,旨在简化数据处理流程,提升工作效率,同时保持数据的原始特性。 其他说明:文中不仅提供了理论性的讲解,还有具体的代码示例和丰富的学习资源,使读者可以边学边练,快速上手。

    融合双向路由注意力的多尺度X光违禁品检测.pdf

    融合双向路由注意力的多尺度X光违禁品检测.pdf

    嵌入式八股文面试题库资料知识宝典-Linux_Shell基础使用.zip

    嵌入式八股文面试题库资料知识宝典-Linux_Shell基础使用.zip

    嵌入式八股文面试题库资料知识宝典-联发科2021武汉嵌入式软件开发.zip

    嵌入式八股文面试题库资料知识宝典-联发科2021武汉嵌入式软件开发.zip

    基于有限体积法Godunov格式的管道泄漏检测模型研究.pdf

    基于有限体积法Godunov格式的管道泄漏检测模型研究.pdf

    嵌入式八股文面试题库资料知识宝典-ARM常见面试题目.zip

    嵌入式八股文面试题库资料知识宝典-ARM常见面试题目.zip

    基于LWR问题的无证书全同态加密方案.pdf

    基于LWR问题的无证书全同态加密方案.pdf

    嵌入式八股文面试题库资料知识宝典-符坤面试经验.zip

    嵌入式八股文面试题库资料知识宝典-符坤面试经验.zip

    三电平逆变器带不平衡负载的DSC与双闭环PI控制策略仿真研究

    内容概要:本文详细探讨了三电平逆变器在带不平衡负载条件下的仿真研究。主要内容包括仿真环境的搭建、不同拓扑结构的选择(如T型、I型NPC和ANPC)、延时相消法(DSC)和双二阶广义积分器(DSOGI)的正负序分离控制策略、SVPWM或SPWM调制技术的应用、双闭环PI控制以及直流均压控制。文中通过具体的参数设置(交流电压220V,直流侧电压750V)进行了详细的仿真实验,并展示了各个控制策略的效果。最终,通过仿真实验验证了所提出方法的有效性,确保了交流侧三相电压波形的对称性和电流波形的自适应调节。 适合人群:从事电力电子、电机驱动、新能源发电等领域研究的技术人员和研究人员。 使用场景及目标:适用于需要理解和掌握三电平逆变器在复杂负载条件下控制策略的研究人员和技术人员。目标是提高对三电平逆变器及其控制策略的理解,优化实际应用中的性能。 其他说明:本文不仅提供了理论分析,还包含了具体的仿真步骤和代码实现,有助于读者更好地理解和应用相关技术。

Global site tag (gtag.js) - Google Analytics