纯真IP数据库(QQWry.Dat)查询 C源码

今天看了利用 QQWry.Dat 实现 IP 地址高效检索(PHP),一时兴起,也打算的研究一下:)。于是参考纯真IP数据库格式详解我写了一个C语言版的,于gcc 3.3.5 (Debian 1:3.3.5-13)测试通过。

主要难点是见识了C语言的”平台相关性”:P。我在Debian Linux / i386上面程序运行正常,因为Intel系统是Little Endian,和我的程序一样。而我上传到学校的SunOS上面,执行到第一步取文件头index区数据时就退出了,因为Sun的系统是Big Endian。

程序遵守GPL,随便取用,虽然我也不知道有没有用:P。不过我想可以稍加修改做一个把QQWry.Dat导成SQL文件的程序。数据库直接查询更快了吧:)。不过因为重复数据太多,光到成文本文件就有14MB呢。

源码如下:

/*
* IP Search client for QQWry.Dat
* Author: Windix Feng [windix AT gmail.com]
* http://www.douzi.org
* 30/06/2005 [GPL]
*
* Running with QQWry.Dat (http://www.cz88.net)
* Referring from “Structure of IP DB” by Luma
* http://lumaqq.linuxsir.org/article/qqwry_format_detail.html
*
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

unsigned long ip_str2dec(char *ip_in) {
char *ip = strdup(ip_in);
unsigned long ip_dec = 0;

char *p;
int i;
for(i=0; i<3; i++) {
p = strrchr(ip, ‘.’);
ip_dec |= atoi(p+1) < < (8*i);
*p = '\0';
}
ip_dec |= atoi(ip) << (8*i);
free(ip);
return ip_dec;
}

unsigned long ip_arr2dec(char **ip_arr) {
unsigned long ip_dec = 0;
int i;
for(i=0; i<4; i++) {
ip_dec |= atoi(ip_arr[i]) << (8*(3-i));
}
return ip_dec;
}

unsigned long ip_arr2dec_r(unsigned char *ip_arr) {
unsigned long ip_dec = 0;
int i;
for(i=0; i<4; i++) {
ip_dec |= ip_arr[i] << (8*i);
}
return ip_dec;
}

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

typedef struct ip_info {
char ip[16];
char *country;
char *area;
char start_ip[16]; // For debug
char end_ip[16]; // For debug
char *mode; // For debug
} IP_INFO;

unsigned long get_long_addr3(unsigned char *buf);
char* get_string_by_addr(long addr, FILE *fp);
IP_INFO *get_ip_by_index(unsigned long index_addr, FILE *fp);
char* get_area(unsigned char* buffer, FILE *fp);
void print_iptable(FILE *fp);
unsigned long search_ip(char *ip_in, FILE *fp);

unsigned long get_long_addr3(unsigned char *buf) {
unsigned long addr = 0;
/* addr = buf[0] + buf[1]*256 + buf[2]*65536; */
addr = buf[0] | buf[1] << 8 | buf[2] << 16;
return addr;
}

char* get_string_by_addr(long addr, FILE *fp) {
unsigned char buffer[1024];
fseek(fp, addr, SEEK_SET);
fread(buffer, 1024, 1, fp);
return strdup(buffer);
}

IP_INFO *get_ip_by_index(unsigned long index_addr, FILE *fp) {
IP_INFO *ipinfo = (IP_INFO *)malloc(sizeof(IP_INFO));

unsigned char ip[4];
unsigned char addr[3];
unsigned long record_addr = 0;

fseek(fp, index_addr, SEEK_SET);
fread(ip, 4, 1, fp);
fread(addr, 3, 1, fp);
record_addr = get_long_addr3(addr);
sprintf(ipinfo->start_ip, “%u.%u.%u.%u”, ip[3], ip[2], ip[1], ip[0]);

unsigned char buffer[1024];
unsigned long country_addr;

fseek(fp, record_addr, SEEK_SET);
fread(ip, 4, 1, fp);
fread(buffer, 1024, 1, fp);
sprintf(ipinfo->end_ip, “%u.%u.%u.%u”, ip[3], ip[2], ip[1], ip[0]);

if (buffer[0] == 1) {
country_addr = get_long_addr3(buffer + 1);
fseek(fp, country_addr, SEEK_SET);
fread(buffer, 1024, 1, fp);

if (buffer[0] == 2) {
ipinfo->country = get_string_by_addr(get_long_addr3(buffer + 1), fp);
ipinfo->area = get_area(buffer + 4, fp);
ipinfo->mode = “1 + 2″;
} else {
ipinfo->country = get_string_by_addr(country_addr, fp);
ipinfo->area = get_area(buffer + strlen(ipinfo->country) + 1, fp);
ipinfo->mode = “1 + D”;
}

} else if (buffer[0] == 2) {
ipinfo->country = get_string_by_addr(get_long_addr3(buffer + 1), fp);
ipinfo->area = get_area(buffer + 4, fp);
ipinfo->mode = “2 + D”;

} else {
ipinfo->country = strdup(buffer);
ipinfo->area = get_area(buffer + strlen(ipinfo->country) + 1, fp);
ipinfo->mode = “D + D”;
}
return ipinfo;
}

char* get_area(unsigned char* buffer, FILE *fp) {
if (buffer[0] == 1 || buffer[0] == 2) {
return get_string_by_addr(get_long_addr3(buffer + 1), fp);
} else {
return strdup(buffer);
}
}

void print_iptable(FILE *fp) {
unsigned long index_start, index_end;
fseek(fp, 0, SEEK_SET);
fread(&index_start, 4, 1, fp);
fread(&index_end, 4, 1, fp);
/* printf(”%lu %lu\n”, index_start, index_end); */

unsigned long i;
IP_INFO *ipinfo;

for(i=index_start; i< =index_end; i+=7) {
ipinfo = get_ip_by_index(i, fp);
printf("%s - %s\n%s, %s\n", ipinfo->start_ip, ipinfo->end_ip, ipinfo->country, ipinfo->area);

free(ipinfo);
}
}

unsigned long search_ip(char *ip_in, FILE *fp) {
unsigned long index_start, index_end, lo, hi, i, ip_i, ip_dest;
unsigned char ip_arr[4];

fseek(fp, 0, SEEK_SET);
fread(&index_start, 4, 1, fp);
fread(&index_end, 4, 1, fp);

lo = 0;
hi = (index_end – index_start) / 7;
ip_dest = ip_str2dec(ip_in);

while(lo < = hi) {
i = (lo + hi) / 2;

fseek(fp, index_start + i * 7, SEEK_SET);
fread(ip_arr, 4, 1, fp);
ip_i = ip_arr2dec_r(ip_arr);
if (ip_i == ip_dest)
return index_start + i * 7;
else if (ip_i < ip_dest)
lo = i + 1;
else
hi = i - 1;
}
/* hi return index_start + hi * 7;
}

int main(int argc, char **argv) {
FILE *fp;
fp = fopen("QQWry.Dat", "r");

IP_INFO *ipinfo = get_ip_by_index(search_ip(argv[1], fp), fp);
printf("%s, %s\n", ipinfo->country, ipinfo->area);
free(ipinfo);

/* print_iptable(fp); */

fclose(fp);
return EXIT_SUCCESS;
}

3 Responses to “纯真IP数据库(QQWry.Dat)查询 C源码”

  1. Wintel Says:

    恩,不错,比起我们的海图格式还算简单,等我也写个Java的,给douzi鼓鼓劲。
    网站备案应该没什么问题,你用家里人的手机就行了,我的手机已经备案了就不能用了,要不就帮你弄。我的备案今天才下来。没下来的时候网站就是有时能上,有时不能上。早知道不申请了,不申请他也不知道封我。唉!

    看你的学校网站的照片,胖了?

    听douzi讲了你考试的事,别放在心上哈。大家都相信你的实力。

    3G了呀,好,给我打电话吧,哈哈哈哈!

  2. windix Says:

    晕,”纯真IP数据库格式详解”就是Java版的LumaQQ的作者写的… :)

  3. victor Says:

    兄弟,你这个C程序能否提供下载?
    能否说明一下你的编译和运行过程?
    谢谢