Chinaunix首页 | 论坛 | 博客

分类: LINUX

2012-04-26 21:30:05

参考文章:
1. java版实现:
2. 纯真IP数据库格式详解:
以下是我的实现:使用的时候很简单:

点击(此处)折叠或打开

  1. /**
  2. *1.调用qqwry_init_parser创建一个qqwry_parser_t类型的解析器
  3. *2.初始化一个存放查询结果信息的qqwry_record
  4. *3.调用qqwry_querey_iprecord查询指定ip的归属地
  5. *4.调用qqwry_release_parser释放资源
  6. **/

  7. qqwry_parser_t parser = qqwry_init_parser("/root/QQWry.Dat", VERBOSE_ENABLE,
  8. CACHE_ENABLE, 1);
  9. qqwry_record qr = QQWARY_RECORD_INITIALIZER();
  10. qqwry_querey_iprecord(parser, "61.172.201.195", &qr);
  11. qqwry_release_parser(parser);
这是一个初级版本,还没有经过大量的测试,可能存在很多不完备的地方,希望亲们试用了之后,能够提出改进意见啊。

qqwry_utils.h

点击(此处)折叠或打开

  1. /*
  2.  * qqwry_utils.h
  3.  *
  4.  * Created on: 2012-4-26
  5.  * Author: zhanlin
  6.  */

  7. #ifndef QQWRY_UTILS_H_
  8. #define QQWRY_UTILS_H_


  9. int ipaddr_to_bytes(const char *src, char *dest);
  10. void bytes_to_ipaddr(const char *src, char *dest);
  11. inline int32_t char3_to_int32(const char *buf);
  12. #endif /* QQWRY_UTILS_H_ */
qqwry_utils.c

点击(此处)折叠或打开

  1. /*
  2.  * qqwry_utils.c
  3.  *
  4.  * Created on: 2012-4-26
  5.  * Author: zhanlin
  6.  */

  7. #include <string.h>
  8. #include <sys/types.h>
  9. #include <stdlib.h>
  10. #include <stdio.h>
  11. #include <string.h>
  12. #include "qqwry_utils.h"

  13. int ipaddr_to_bytes(const char *src, char *dest) {
  14.     int n;
  15.     char *newstr;
  16.     char *sub;
  17.     int i = 0;

  18.     n = strlen(src);
  19.     newstr = (char *) malloc(n + 1);
  20.     if (newstr == NULL) {
  21.         return -1;
  22.     }

  23.     memcpy(newstr, src, n);
  24.     *(newstr + n) = 0;
  25.     sub = strtok(newstr, ".");

  26.     while (sub != NULL) {
  27.         // printf("%s atoi %d\n", sub, atoi(sub));
  28.         *(dest + i) = (char) atoi(sub);
  29.         sub = strtok(NULL, ".");
  30.         i++;
  31.     }
  32.     free(newstr);
  33.     return 0;
  34. }
  35. void bytes_to_ipaddr(const char *src, char *dest) {
  36.     sprintf(dest, "%c.%c.%c.%c", src[0], src[1], src[2], src[3]);
  37. }
  38. inline int32_t char3_to_int32(const char *buf) {
  39.     int32_t i = 0;
  40.     memcpy(&i, buf, 3);
  41.     return i;
  42. }
qqwry_types.h

点击(此处)折叠或打开

  1. /*
  2.  * qqwry_types.h
  3.  *
  4.  * Created on: 2012-4-26
  5.  * Author: zhanlin
  6.  */

  7. #ifndef QQWRY_TYPES_H_
  8. #define QQWRY_TYPES_H_
  9. #include <sys/types.h>
  10. #include <string.h>
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <dirent.h>

  14. #pragma pack(0)

  15. typedef struct _qqwry_header {
  16.     u_int32_t qh_idx_start;
  17.     u_int32_t qh_idx_end;
  18. } qqwry_header, *qqwry_header_t;

  19. #endif /* QQWRY_TYPES_H_ */
qqwry_parser.h

点击(此处)折叠或打开

  1. /*
  2.  * qqwry_parser.h
  3.  *
  4.  * Created on: 2012-4-26
  5.  * Author: zhanlin
  6.  */

  7. #ifndef QQWRY_PARSER_H_
  8. #define QQWRY_PARSER_H_

  9. #include <sys/types.h>
  10. enum {
  11.     VERBOSE_DISABLE = 0, VERBOSE_ENABLE = 1
  12. };

  13. enum {
  14.     CACHE_DISABLE = 0, CACHE_ENABLE = 1
  15. };

  16. typedef struct _qqwry_record {
  17.     char qr_ipaddr[20];
  18.     int32_t qr_ip;
  19.     char qr_bigzone[1024];
  20.     char qr_smallzone[1024];
  21. } qqwry_record, *qqwry_record_t;

  22. #define QQWARY_RECORD_INITIALIZER() {\
  23.         .qr_ipaddr = {0}, \
  24.         .qr_ip = 0, \
  25.         .qr_bigzone = {0}, \
  26.         .qr_smallzone = {0}, \
  27. }
  28. struct _qqwry_parser;
  29. typedef struct _qqwry_parser qqwry_parser, *qqwry_parser_t;
  30. qqwry_parser_t qqwry_init_parser(const char *dbpath, int verb, int cache,
  31.         int loaddb);
  32. int qqwry_load_db(qqwry_parser_t parser);
  33. void qqwry_unload_db(qqwry_parser_t parser);
  34. int qqwry_querey_iprecord(qqwry_parser_t parser, const char *ipaddr, qqwry_record_t qr);
  35. void qqwry_release_parser(qqwry_parser_t parser);

  36. int load_db_to_mem(qqwry_parser_t parser);

  37. #endif /* QQWRY_PARSER_H_ */
qqwry_types.c

点击(此处)折叠或打开

  1. /*
  2.  * qqwry_types.c
  3.  *
  4.  * Created on: 2012-4-26
  5.  * Author: root
  6.  */

  7. #include "qqwry_parser.h"
  8. #include "qqwry_utils.h"
  9. #include "qqwry_types.h"
  10. #include <sys/stat.h>
  11. #include <fcntl.h>
  12. #include <unistd.h>
  13. #include <errno.h>
  14. #include <iconv.h>
  15. #include <arpa/inet.h>
  16. #define trace(parser, fmt, ...) {\
  17.     if((parser)->verbose) {\
  18.         printf(fmt, ##);\
  19.     }\
  20. }

  21. struct _qqwry_parser {
  22.     char qp_db_path[PATH_MAX];
  23.     int qp_db_fd;
  24.     int qp_verbose;
  25.     int qp_cache;
  26.     int qp_errno;
  27.     char *qp_memaddr;
  28.     u_int32_t qp_foff;
  29.     u_int32_t qp_fsize;
  30.     qqwry_header_t qp_header;
  31.     iconv_t qp_cd;
  32. };
  33. /*
  34.  int load_db_to_mem(qqwry_parser_t parser);
  35.  void unload_db(qqwry_parser_t parser);*/
  36. int qqwry_load_db(qqwry_parser_t parser) {
  37.     struct stat buf;
  38.     int retval = 0;

  39.     parser->qp_errno = 0;
  40.     //
  41.     if ((retval = fstat(parser->qp_db_fd, &buf)) != 0) {
  42.         parser->qp_errno = errno;
  43.         if (parser->qp_verbose)
  44.             perror("fstat execute failed");
  45.         return retval;
  46.     }
  47.     //
  48.     parser->qp_fsize = buf.st_size;
  49.     parser->qp_memaddr = (char *) malloc(buf.st_size);
  50.     if (parser->qp_memaddr == NULL) {
  51.         parser->qp_errno = errno;
  52.         if (parser->qp_verbose)
  53.             perror("malloc execute failed");
  54.         return -1;
  55.     }
  56.     //
  57.     if ((retval = read(parser->qp_db_fd, parser->qp_memaddr, buf.st_size))
  58.             != buf.st_size) {
  59.         parser->qp_errno = errno;
  60.         if (parser->qp_verbose)
  61.             perror("read file failed");
  62.     }
  63.     parser->qp_header = (qqwry_header_t) parser->qp_memaddr;

  64.     //
  65.     parser->qp_cd = iconv_open("UTF-8", "GBK");
  66.     return 0;
  67. }

  68. void qqwry_unload_db(qqwry_parser_t parser) {
  69.     if (parser->qp_memaddr) {
  70.         free(parser->qp_memaddr);
  71.         parser->qp_memaddr = NULL;
  72.     }
  73.     if (parser->qp_cd) {
  74.         iconv_close(parser->qp_cd);
  75.     }
  76. }

  77. int qqwry_open_db(qqwry_parser_t parser) {
  78.     parser->qp_db_fd = open(parser->qp_db_path, O_RDONLY, 0777);
  79.     parser->qp_errno = errno;
  80.     if (parser->qp_verbose && parser->qp_db_fd < 0)
  81.         perror("open qqwry datafile failed");
  82.     return parser->qp_db_fd;
  83. }

  84. void qqwry_close_db(qqwry_parser_t parser) {
  85.     if (parser->qp_db_fd >= 0) {
  86.         close(parser->qp_db_fd);
  87.         parser->qp_db_fd = -1;
  88.     }
  89. }

  90. int qqwry_read(qqwry_parser_t p, void *buf, int n) {
  91.     if (p->qp_fsize < p->qp_foff + n) {
  92.         n = p->qp_fsize - p->qp_foff;
  93.     }
  94.     memcpy(buf, (p->qp_memaddr + p->qp_foff), n);
  95.     p->qp_foff += n;
  96.     return n;
  97. }

  98. int qqwry_seek(qqwry_parser_t p, int32_t off) {
  99.     if (p->qp_fsize < off) {
  100.         return -1;
  101.     }
  102.     return (p->qp_foff = off);
  103. }

  104. int32_t qqwry_read3c_to_int32(qqwry_parser_t p) {
  105.     char b3[3] = { 0 };
  106.     int32_t ret = 0;
  107.     if (qqwry_read(p, b3, 3) != 3) {
  108.         return -1;
  109.     }
  110.     ret |= (b3[0] & 0xFF);
  111.     ret |= ((b3[1] << 8) & 0xFF00);
  112.     ret |= ((b3[2] << 16) & 0xFF0000);
  113.     return ret;
  114. }

  115. char qqwry_readc(qqwry_parser_t p) {
  116.     char b[1] = { 0 };
  117.     if (qqwry_read(p, b, 1) != 1) {
  118.         return -1;
  119.     }
  120.     return b[0];
  121. }

  122. int qqwry_read_ip(qqwry_parser_t p, int32_t offset, int32_t *ip) {
  123.     int ret = 0;
  124.     ret = qqwry_seek(p, offset);
  125.     if (ret < 0)
  126.         return ret;
  127.     ret = qqwry_read(p, ip, 4);
  128.     *ip = ntohl(*ip);
  129.     return ret;
  130. }

  131. /**
  132.  * 把两个byte当作无符号数进行比较
  133.  *
  134.  * @param b1
  135.  * @param b2
  136.  * @return 若b1大于b2则返回1,相等返回0,小于返回-1
  137.  */
  138. int qqwry_compare_char(char c1, char c2) {
  139.     if ((c1 & 0xFF) > (c2 & 0xFF)) // 比较是否大于
  140.         return 1;
  141.     else if ((c1 ^ c2) == 0) // 判断是否相等
  142.         return 0;
  143.     else
  144.         return -1;
  145. }
  146. /**
  147.  * 把类成员ip和beginIp比较,注意这个beginIp是big-endian的
  148.  *
  149.  * @param ip
  150.  * 要查询的IP
  151.  * @param beginIp
  152.  * 和被查询IP相比较的IP
  153.  * @return 相等返回0,ip大于beginIp则返回1,小于返回-1。
  154.  */
  155. inline int qqwry_compare_ip(int32_t *ip, int32_t *beginip) {
  156.     char *ipc = (char *) ip;
  157.     char *beginipc = (char *) beginip;
  158.     int i = 0;
  159.     for (; i < 4; i++) {
  160.         int r = qqwry_compare_char((*(ipc + i)), *(beginipc + i));
  161.         if (r != 0)
  162.             return r;
  163.     }
  164.     return 0;
  165. }

  166. qqwry_parser_t qqwry_init_parser(const char *dbpath, int verb, int cache,
  167.         int loaddb) {
  168.     qqwry_parser_t qp = (qqwry_parser_t) malloc(sizeof(qqwry_parser));
  169.     if (qp) {
  170.         //
  171.         qp->qp_cache = cache;
  172.         qp->qp_errno = 0;
  173.         qp->qp_db_fd = -1;
  174.         qp->qp_verbose = verb;
  175.         qp->qp_memaddr = NULL;
  176.         qp->qp_foff = 0;
  177.         qp->qp_header = NULL;
  178.         strncpy(qp->qp_db_path, dbpath, PATH_MAX);
  179.         //
  180.         if (loaddb) {
  181.             if (qqwry_open_db(qp) >= 0) {
  182.                 if (qqwry_load_db(qp) != 0)
  183.                     qp->qp_errno = errno;
  184.                 else
  185.                     qqwry_close_db(qp);
  186.             } else {
  187.                 qp->qp_errno = errno;
  188.             }
  189.         }
  190.     }
  191.     return qp;
  192. }
  193. int32_t get_middle_offset(int32_t begin, int32_t end) {
  194. #define IP_RECORD_LENGTH 7
  195.     long records = (end - begin) / IP_RECORD_LENGTH;
  196.     records >>= 1;
  197.     if (records == 0)
  198.         records = 1;
  199.     return begin + records * IP_RECORD_LENGTH;
  200. }

  201. int32_t qqwry_locate_ip(qqwry_parser_t p, const char *ipaddr) {
  202.     int32_t ip = 0;
  203.     int32_t rip;
  204.     int ret = 0;

  205.     int32_t m;
  206.     int32_t i = p->qp_header->qh_idx_start;
  207.     int j = p->qp_header->qh_idx_end;

  208.     ip = (int32_t) inet_addr(ipaddr) ;
  209.     // ipaddr_to_bytes(ipaddr, (char *) &ip);
  210.     qqwry_read_ip(p, p->qp_header->qh_idx_start, &rip);
  211.     ret = qqwry_compare_ip(&ip, &rip);
  212.     if (ret == 0)
  213.         return p->qp_header->qh_idx_start;
  214.     else if (ret < 0)
  215.         return -1;

  216.     // 开始二分搜索
  217.     for (; i < j;) {
  218.         m = get_middle_offset(i, j);
  219.         qqwry_read_ip(p, m, &rip);
  220.         ret = qqwry_compare_ip(&ip, &rip);
  221.         // log.debug(Utils.getIpStringFromBytes(b));
  222.         if (ret > 0)
  223.             i = m;
  224.         else if (ret < 0) {
  225.             if (m == j) {
  226.                 j -= IP_RECORD_LENGTH;
  227.                 m = j;
  228.             } else {
  229.                 j = m;
  230.             }
  231.         } else {
  232.             qqwry_seek(p, m + 4);
  233.             return qqwry_read3c_to_int32(p);
  234.         }
  235.     }
  236.     // 如果循环结束了,那么i和j必定是相等的,这个记录为最可能的记录,但是并非
  237.     // 肯定就是,还要检查一下,如果是,就返回结束地址区的绝对偏移
  238.     qqwry_seek(p, m + 4);
  239.     m = qqwry_read3c_to_int32(p);
  240.     qqwry_read_ip(p, m, &rip);
  241.     ret = qqwry_compare_ip(&ip, &rip);
  242.     if (ret <= 0)
  243.         return m;
  244.     else
  245.         return -1;
  246. }
  247. size_t conver_gbk_to_utf8(qqwry_parser_t p, char *inbuf, char *outbuf) {
  248.     int n = strlen(inbuf);
  249.     char *in = inbuf;
  250.     char *out = outbuf;
  251.     size_t outlen = n * 4;
  252.     iconv(p->qp_cd, &in, (size_t *) &n, &out, &outlen);
  253.     outlen = strlen(outbuf);
  254.     return outlen;
  255. }
  256. void qqwry_read_string(qqwry_parser_t p, char *buf) {
  257.     int i = 0;
  258.     char tmp[512] = { 0 };
  259.     for (i = 0, tmp[i] = qqwry_readc(p); tmp[i] != 0; tmp[++i] = qqwry_readc(p))
  260.         ;
  261.     conver_gbk_to_utf8(p, tmp, buf);
  262. }
  263. void qqwry_read_smallzone(qqwry_parser_t p, char *buf) {
  264.     char flag = qqwry_readc(p);
  265.     if (flag == 0x01 || flag == 0x02) {
  266.         long areaOffset = qqwry_read3c_to_int32(p);
  267.         if (areaOffset == 0)
  268.             p->qp_errno = 0;
  269.         else {
  270.             qqwry_seek(p, areaOffset);
  271.             qqwry_read_string(p, buf);
  272.         }
  273.     } else {

  274.         return qqwry_read_string(p, buf);
  275.     }
  276. }
  277. int qqwry_querey_iprecord(qqwry_parser_t parser, const char *ipaddr,
  278.         qqwry_record_t qr) {

  279. #define AREA_FOLLOWED 0x1
  280. #define NO_AREA 0x2
  281.     int32_t offset = qqwry_locate_ip(parser, ipaddr);
  282.     char flag = 0;
  283.     printf("ip off: %d\n", offset);

  284.     qqwry_seek(parser, offset + 4);
  285.     // 读取第一个字节判断是否标志字节
  286.     flag = qqwry_readc(parser);
  287.     if (flag == AREA_FOLLOWED) {
  288.         // 读取国家偏移
  289.         int32_t country_offset = qqwry_read3c_to_int32(parser);
  290.         // 跳转至偏移处
  291.         qqwry_seek(parser, country_offset);
  292.         // 再检查一次标志字节,因为这个时候这个地方仍然可能是个重定向
  293.         flag = qqwry_readc(parser);
  294.         if (flag == NO_AREA) {
  295.             qqwry_seek(parser, qqwry_read3c_to_int32(parser));
  296.             qqwry_read_string(parser, qr->qr_bigzone);
  297.             qqwry_seek(parser, country_offset + 4);
  298.         } else {
  299.             qqwry_seek(parser, country_offset);
  300.             qqwry_read_string(parser, qr->qr_bigzone);
  301.         }
  302.         // 读取地区标志
  303.         qqwry_read_smallzone(parser, qr->qr_smallzone);
  304.     } else if (flag == NO_AREA) {
  305.         int32_t country_offset = qqwry_read3c_to_int32(parser);
  306.         qqwry_seek(parser, country_offset);
  307.         qqwry_read_string(parser, qr->qr_bigzone);
  308.         qqwry_seek(parser, offset + 8);
  309.         qqwry_read_string(parser, qr->qr_smallzone);
  310.     } else {
  311.         qqwry_seek(parser, parser->qp_foff - 1);
  312.         qqwry_read_string(parser, qr->qr_bigzone);
  313.         qqwry_read_string(parser, qr->qr_smallzone);
  314.     }
  315.     return 0;
  316. }

  317. void qqwry_release_parser(qqwry_parser_t parser) {
  318.     if (parser != NULL) {
  319.         //
  320.         if (parser->qp_memaddr) {
  321.             qqwry_unload_db(parser);
  322.         }
  323.         //
  324.         free(parser);
  325.         parser = NULL;
  326.     }

  327. }
ipinfo.c

点击(此处)折叠或打开

  1. /*
  2.  ============================================================================
  3.  Name : ipinfo.c
  4.  Author : ZhanLin
  5.  Version :
  6.  Copyright : GPL v2
  7.  Ansi-style
  8.  ============================================================================
  9.  */

  10. #include <stdio.h>
  11. #include <stdlib.h>
  12. #include <string.h>
  13. #include "qqwry_parser.h"
  14. #include <iconv.h>


  15. int main(void) {
  16.     qqwry_parser_t parser = qqwry_init_parser("/root/QQWry.Dat", VERBOSE_ENABLE,
  17.             CACHE_ENABLE, 1);
  18.     qqwry_record qr = QQWARY_RECORD_INITIALIZER();
  19.     qqwry_querey_iprecord(parser, "61.172.201.195", &qr);
  20.     qqwry_release_parser(parser);
  21.     printf("big zone: %s, small zone: %s\n", qr.qr_bigzone, qr.qr_smallzone);
  22.     return EXIT_SUCCESS;
  23. }





阅读(5036) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~