0x00. 简介

GeoIP库可以根据IP地址(支持IPv4 和 IPv6), 定位该IP所在的 洲、经纬度、国家、省市、ASN 等信息。

GeoIP目前已经升级到GeoIP2,GeoIP2有两个版本,一个免费版(GeoLite2),一个收费版本(GeoIP2, 200$起步)。

收费版本的准确率稍高一些,更新频率为每周二更新一次, 免费版是每月第一个周二更新一次。

两者对比可以参考官网说明 https://www.maxmind.com/en/geoip2-city-accuracy-comparison

对于大部分项目来说免费版已经足够使用了.

除了GeoIP外, 其实还有 ip2location、Quova等也提供类似的功能, 但都是收费的.

 

0x01. 资源下载

很多linux版本支持这个库, 可以使用yum 或 apt 进行下载, windows上使用的话就需要自己编译了.

源码下载:

https://dev.maxmind.com/geoip/geoip2/downloadable/

GeoIP2提供了多种语言的API接口供选择.

这里我需要使用C语言接口, 所以下载C语言版的源码.

https://github.com/maxmind/libmaxminddb/releases

GeoIP数据库下载:

https://dev.maxmind.com/geoip/geoip2/geolite2/

可以看到官网提供三种库,2种格式, 首先 官网API是需要使用二进制库文件, CSV格式的库可以导入其他程序 或 供你简单浏览。

三种库的区别可以从名字上就可以看出来:

  City       精确到城市(大小70M左右),

  Country 精确到国家(4M左右),

  ASN       用于产看IP地址的拥有者(7M左右). 需要注意的是 City 和 Country 库中不含ASN信息

对于ASN的理解可以通过知乎了解一下 https://www.zhihu.com/question/21024981

根据业务需求选择. 这里我们下载精确到城市的数据库文件.

https://geolite.maxmind.com/download/geoip/database/GeoLite2-City.tar.gz

由于数据库经常更新, 官网还提供了更新的方案:

https://dev.maxmind.com/geoip/geoipupdate/#For_Free_GeoLite2_Databases

0x02.接口说明

http://maxmind.github.io/libmaxminddb/

/* ------------------数据库的关闭与打开* 这里 MMDB_open 的 flags 参数需要说明一下, * 目前代码实现db文件都是使用mmap()映射到内存的,* 所以flags其实设置什么都无所谓,所以默认使用 MMDB_MODE_MMAP就好.*/
int MMDB_open(const char *const filename, uint32_t flags, MMDB_s *const mmdb);
void MMDB_close(MMDB_s *const mmdb);/* ------------------数据搜索* 网络字节序地址搜索API, 即传入sockaddr参数是网络字节序的地址 */
MMDB_lookup_result_s MMDB_lookup_sockaddr(MMDB_s *const mmdb,const struct sockaddr *const sockaddr, int *const mmdb_error);/* 字符串IP地址搜索API, 即传入ipstr参数的null结尾字符串,如"114.240.52.162" * 这个API其实就是调用 getaddrinfo() 将字符串转换为网络地址, * 然后再调用 MMDB_lookup_sockaddr() 实现的, * 所以不要将网络地址转成字符串,再调用这个函数, 直接使用MMDB_lookup_sockaddr()* gai_error参数 是用来返回 getaddrinfo() 错误码的.*/
MMDB_lookup_result_s MMDB_lookup_string(MMDB_s *const mmdb, const char *const ipstr,int *const gai_error, int *const mmdb_error);/* ------------------从搜索到数据中提取指定数据* 下面 3 个函数意义相同, 只不过传入参数的方法不同.* 3个函数前2个参数相同, 第一个是 搜索结果中搜到的entry,具体看例子程序* 第二个参数是 获取数据的存放处, 剩下的参数都是搜索用的数据.* 其实这3个函数实现是层层调用的关系:* MMDB_get_value()是可变参数函数, 函数内部把 可变参数 用 va_list 封装起来,* 接着调用 MMDB_vget_value(), 这个 vget函数把所有可变参数 再分离开来,* 放到一个 字符串指针数组里面, 再把这个数组传递给 MMDB_aget_value()函数,* 最终就是 aget 函数完成搜索功能. * 所以如果考虑到性能, 那个最好直接调用 aget 函数.* 另外, 需要注意 传入的参数 最后一个必须是 NULL, 否则会导致程序崩溃*/
int MMDB_get_value(MMDB_entry_s *const start, MMDB_entry_data_s *const entry_data, ...);
int MMDB_vget_value(MMDB_entry_s *const start, MMDB_entry_data_s *const entry_data, va_list va_path);
int MMDB_aget_value(MMDB_entry_s *const start, MMDB_entry_data_s *const entry_data, const char *const *const path);/* ------------------从搜索到数据中提取全部数据* 调用完MMDB_lookup_sockaddr/MMDB_lookup_string 后获取所有该IP地址相关信息* 具体使用看官方例子, 用处不大, 主要是可以用来了解一下结果数据的组成*/
int MMDB_get_entry_data_list(MMDB_entry_s *start, MMDB_entry_data_list_s **const entry_data_list);
int MMDB_dump_entry_data_list(FILE *const stream,MMDB_entry_data_list_s *const entry_data_list, int indent);
void MMDB_free_entry_data_list(MMDB_entry_data_list_s *const entry_data_list);
int MMDB_get_metadata_as_entry_data_list(MMDB_s *const mmdb, MMDB_entry_data_list_s **const entry_data_list);/* ------------------其他, MMDB_read_node例子可以看源码的 read_node_t.c,个人觉得没什么用 */
const char *MMDB_strerror(int error_code);
const char *MMDB_lib_version(void);
int MMDB_read_node(MMDB_s *const mmdb, uint32_t node_number, MMDB_search_node_s *const node);

0x03. 例子

例子1:使用 City 或 Country 库 查询IP所属位置信息

/* this file must be utf8 encode */
#include <errno.h>
#include <maxminddb.h>
#include <stdlib.h>
#include <string.h>int main(int argc, char **argv)
{char *filename = "./GeoLite2-City.mmdb";char *ip_address = "114.240.52.162";MMDB_s mmdb;MMDB_entry_data_s entry_data;MMDB_lookup_result_s result;int gai_error, mmdb_error;int status = MMDB_open(filename, MMDB_MODE_MMAP, &mmdb);if (MMDB_SUCCESS != status) {fprintf(stderr, "Can't open %s - %s\n", filename, MMDB_strerror(status));if (MMDB_IO_ERROR == status) {fprintf(stderr, "IO error: %s\n", strerror(errno));}exit(1);}result = MMDB_lookup_string(&mmdb, ip_address, &gai_error, &mmdb_error);if (0 != gai_error) {fprintf(stderr, "Error from getaddrinfo for %s - %s\n", ip_address, gai_strerror(gai_error));exit(2);}if (MMDB_SUCCESS != mmdb_error) {fprintf(stderr, "Got an error from libmaxminddb: %s\n", MMDB_strerror(mmdb_error));exit(3);}if (result.found_entry) {/* 获取国家名称(简体中文) */status = MMDB_get_value(&result.entry, &entry_data, "country", "names", "zh-CN", NULL);/* 获取国家简称(CN/AU/JP等) */
//        status = MMDB_get_value(&result.entry, &entry_data, "country", "iso_code", NULL);/* 获取城市名称(简体中文) */
//        status = MMDB_get_value(&result.entry, &entry_data, "city", "names", "zh-CN", NULL);if (MMDB_SUCCESS == status) {/* MMDB_get_value 成功 */if (entry_data.has_data) {/* 找到了想要的数据 */if (entry_data.type == MMDB_DATA_TYPE_UTF8_STRING) {/* entry_data.utf8_string 返回的不是null-terminated 字符串,需要根据长度自己截取数据 */fwrite(entry_data.utf8_string, entry_data.data_size, 1, stdout);printf("\n");}else {printf("data_type = %d\n", entry_data.type);}}else {fprintf(stderr, "MMDB_get_value not found\n");}}else {fprintf(stderr, "MMDB_get_value failed,%s\n", MMDB_strerror(status));}}MMDB_close(&mmdb);exit(EXIT_SUCCESS);
}

例子2: 使用ASN库查询IP地址的ASN

/* this file must be utf8 encode */
#include <errno.h>
#include <maxminddb.h>
#include <stdlib.h>
#include <string.h>int main(int argc, char **argv)
{char *filename = "./GeoLite2-ASN.mmdb";char *ip_address = "114.240.52.162";MMDB_s mmdb;MMDB_entry_data_s entry_data;MMDB_lookup_result_s result;int gai_error, mmdb_error;int status = MMDB_open(filename, MMDB_MODE_MMAP, &mmdb);if (MMDB_SUCCESS != status) {fprintf(stderr, "Can't open %s - %s\n", filename, MMDB_strerror(status));if (MMDB_IO_ERROR == status) {fprintf(stderr, "IO error: %s\n", strerror(errno));}exit(1);}result = MMDB_lookup_string(&mmdb, ip_address, &gai_error, &mmdb_error);if (0 != gai_error) {fprintf(stderr, "Error from getaddrinfo for %s - %s\n", ip_address, gai_strerror(gai_error));exit(2);}if (MMDB_SUCCESS != mmdb_error) {fprintf(stderr, "Got an error from libmaxminddb: %s\n", MMDB_strerror(mmdb_error));exit(3);}/* ASN DB INFO Example
{"autonomous_system_number": 4808 <uint32>"autonomous_system_organization": "China Unicom Beijing Province Network" <utf8_string>}
*/if (result.found_entry) {/* 获取ASN */status = MMDB_get_value(&result.entry, &entry_data, "autonomous_system_organization", NULL);if (MMDB_SUCCESS == status) {/* MMDB_get_value 成功 */if (entry_data.has_data) {/* 找到了想要的数据 */if (entry_data.type == MMDB_DATA_TYPE_UTF8_STRING) {/* entry_data.utf8_string 返回的不是null-terminated 字符串,需要根据长度自己截取数据 */fwrite(entry_data.utf8_string, entry_data.data_size, 1, stdout);printf("\n");}else {printf("data_type = %d\n", entry_data.type);}}else {fprintf(stderr, "MMDB_get_value not found\n");}}else {fprintf(stderr, "MMDB_get_value failed,%s\n", MMDB_strerror(status));}}MMDB_close(&mmdb);exit(EXIT_SUCCESS);
}

0x04. 题外话

从源代码来看, 官方提供的库并不适合高速大量查询, 所以有很多第三方实现,例如nginx等都自己实现了数据库的搜索模块.

转载于:https://www.cnblogs.com/LubinLew/p/GeoIP.html

GeoIP的使用-C语言版相关推荐

  1. c语言 静态链表插入排序,数据结构C语言版 表插入排序

    西门豹治邺奇计 数据结构C语言版 表插入排序.txt两个人吵架,先说对不起的人,并不是认输了,并不是原谅了.他只是比对方更珍惜这份感情./* 数据结构C语言版 表插入排序 算法10.3 P267-P2 ...

  2. 资料分享:送你一本《数据结构(C语言版)》电子书!

    要想写出可复用.可扩展.易维护.灵活性好的代码,「数据结构」这一关必须要过啊! 在数据结构与算法的众多教材中,奉为经典的当属清华大学严蔚敏老师的著作.很多学校也选择这本书作为考研指定教材. 正在学习数 ...

  3. 资料分享:送你一本《数据结构(C#语言版)》电子书!

    对于信息类专业的学生而言,数据结构与算法是一门必修的课程.只有学好这门课程,熟练掌握线性表.栈.队列.树.图等基本结构,以及在这些结构上的各种算法,才能利用计算机去解决实际问题. 如何学好这门课程呢, ...

  4. 数据结构(C语言版) 第 八 章 排序 知识梳理 + 习题详解

    目录 一.归并排序 二.交换排序 1.快速排序 2.冒泡排序 三.插入排序 1.直接插入排序(基于顺序查找) 2.折半插入排序(基于折半查找) 3.希尔排序(基于逐趟缩小增量) 四.选择排序 0.直接 ...

  5. 数据结构(C语言版) 第 六 章 图 知识梳理 + 习题详解

    目录 一. 图的基本定义和术语 一.图的基本概念 1.度 2.连通 (1)连通图 (2)强连通/强连通图 3.回路 4.完全图 二.图的三种存储结构 1.邻接矩阵表示法 2.邻接表(链式)表示法 3. ...

  6. 数据结构(C语言版) 第 三 章 栈与队列 知识梳理 + 作业习题详解

    目录 一.栈 0.栈的基本概念 1.栈的实现 2.栈与递归 3.Hanoi塔问题 二.队列 0.队列的基本概念 1.队列的实现 2.循环队列 2.1循环队列的相关条件和公式: 3.链队列 4.链队列完 ...

  7. 数据结构(C语言版) 第二章 线性表 知识梳理+作业习题详解

    目录 一.线性表顺序存储结构(顺序表) 0.线性表的基本概念 1.样例引入:多项式相加 二.线性表链式存储结构(链表) 0.链表的基本概念 1.前插法代码实例 2.链表尾插法完整代码附带各种操作 三. ...

  8. 数据结构c语言函数大全,数据结构习题库(c语言版).doc

    数据结构习题库(c语言版) 第一章 绪 论 一.基本内容 数据.数据元素.数据对象.数据结构.存储结构和数据类型等概念术语的确定含义.抽象数据类型的定义.表示和实现方法.描述算法的类C语言.算法设计的 ...

  9. 为什么C语言是非形式化的,《数据结构C语言版》——绪论

    <数据结构C语言版>--绪论<笔记> 一.基本概念和术语 1.数据,数据元素, 数据项,数据对象 数据:是客观事物的符号表示,是所有能够输入到计算机中并被计算机程序处理的符号的 ...

最新文章

  1. 211高校副校长:我发了170多篇论文,最满意的一篇不到2分
  2. 计算机应用基础 黄国兴 研读,《计算机应用基础》大纲解读黄国兴
  3. 1号店11.11:分布式搜索引擎的架构实践
  4. 这个热图上面的树是根据系统发育关系画的吗?
  5. python字符串用空格切片_Python切片操作去除字符串首尾的空格
  6. Google 发布全中文机器学习速成课,曾内部培训过 1.8 万人!
  7. OA系统中的HRM的发展和存在的误区,值得每一个HR学习
  8. avast6.0网络安全软件破解至2050年_avast激活码_avast有效激活
  9. 使用Kotlin创建动态Android TextWatcher
  10. 以存储为例说明设备busy的解决方法
  11. 漏洞修复:Cache Management: Insecure Policy
  12. asc超级计算机题目,通知公告|2019 ASC 世界大学生超级计算机竞赛(ASC19)报名通知|信息与软件工程学院...
  13. java8新特性 Stream流的优雅操作
  14. 有用的.NET开发资料
  15. 1.App Inventor开发环境构建
  16. 日文输入手写汉字模式快速切换
  17. java1.7 apk 签名_【keytool jarsigner工具的使用】Android 使用JDK1.7的工具 进行APK文件的签名,以及keystore文件的使用...
  18. 使用你的linux系统下载BT
  19. linux双系统uefi启动顺序,UEFI下Windows启动引导的顺序(附带linux双系统)
  20. opencv-qt大津算法(Otsu)

热门文章

  1. 苹果新的编程语言 Swift 语言进阶(三)--基本运算和扩展运算
  2. [转]MongoDB基本使用
  3. Android 记住密码和自动登录界面的实现(SharedPreferences 的用法)(转载)
  4. 格式资料python sqlalchemy 查询结果转化为 Json格式
  5. 20130410 现阶段的学习状况
  6. 敏捷开发的艺术读书笔记
  7. [Jsp] 如何在JSP页面快速输出从Servlet接收的数据_看JSTL常用C标签的威力
  8. 看麦田“蚂蚁网” 想网络新社区
  9. fMRI在认知心理学上的研究
  10. Python之几种常用模块