此文档翻译自 http://maxmind.github.io/libmaxminddb/

名字

libmaxminddb - 用于处理 MaxMind 数据库文件的库

简介

#include <maxminddb.h>int MMDB_open(const char *const filename,uint32_t flags,MMDB_s *const mmdb);
void MMDB_close(MMDB_s *const mmdb);MMDB_lookup_result_s MMDB_lookup_string(MMDB_s *const mmdb,const char *const ipstr,int *const gai_error,int *const mmdb_error);
MMDB_lookup_result_s MMDB_lookup_sockaddr(MMDB_s *const mmdb,const struct sockaddr *constsockaddr,int *const mmdb_error);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);int MMDB_get_entry_data_list(MMDB_entry_s *start,MMDB_entry_data_list_s **const entry_data_list);
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);
int MMDB_dump_entry_data_list(FILE *const stream,MMDB_entry_data_list_s *const entry_data_list,int indent);int MMDB_read_node(MMDB_s *const mmdb,uint32_t node_number,MMDB_search_node_s *const node);const char *MMDB_lib_version(void);
const char *MMDB_strerror(int error_code);typedef struct MMDB_lookup_result_s {bool found_entry;MMDB_entry_s entry;uint16_t netmask;
} MMDB_lookup_result_s;typedef struct MMDB_entry_data_s {bool has_data;union {uint32_t pointer;const char *utf8_string;double double_value;const uint8_t *bytes;uint16_t uint16;uint32_t uint32;int32_t int32;uint64_t uint64;{mmdb_uint128_t or uint8_t[16]} uint128;bool boolean;float float_value;};...uint32_t data_size;uint32_t type;
} MMDB_entry_data_s;typedef struct MMDB_entry_data_list_s {MMDB_entry_data_s entry_data;struct MMDB_entry_data_list_s *next;
} MMDB_entry_data_list_s;

描述

libmaxminddb 库提供用于处理 MaxMind DB 文件的功能。有关 MaxMind DB 格式规范,请参见 http://maxmind.github.io/MaxMind-DB/ 。 数据库和结果都由不同的数据结构表示。通过调用MMDB_open()打开数据库。 您可以使用MMDB_lookup_string()查找 IP 地址,或者使用MMDB_lookup_sockaddr()指向sockaddr结构的指针。

如果查找查找数据库中的IP地址,则返回MMDB_lookup_result_s结构。如果该结构表明数据库具有该IP的数据,则可以使用许多函数获取该数据。这些包括MMDB_get_value()MMDB_get_entry_data_list()。有关详细信息,请参阅下面的功能文档。

完成数据库处理后,你应该调用MMDB_close()

所有公开可见的函数,结构和宏以“MMDB_”开头。

数据结构

由该库的maxminddb.h头文件导出的所有数据结构都是以typedef struct foo_s {...} foo_s的形式,因此您可以引用它们而不使用struct前缀。

该库提供以下数据结构:

MMDB_s

这是 MaxMind DB 文件的句柄。我们只记录了这个结构的一些供公众使用的字段。所有其他字段可能会更改,仅供内部使用。

typedef struct MMDB_s {uint32_t flags;const char *filename;...MMDB_metadata_s metadata;
} MMDB_s;
  • uint32_t flags - 这个数据库打开的标志。详细信息,请参阅MMDB_open()文档。
  • const char * filename - 已打开文件名,传递给MMDB_open()
  • MMDB_metadata_s metadata - 数据库的元数据。

MMDB_metadata_sMMDB_description_s

可以从MMDB_s结构获取此结构。它包含从数据库文件读取的元数据。 注意,你可以通过调用MMDB_get_metadata_as_entry_data_list()来更方便地访问元数据。

typedef struct MMDB_metadata_s {uint32_t node_count;uint16_t record_size;uint16_t ip_version;const char *database_type;struct {size_t count;const char **names;} languages;uint16_t binary_format_major_version;uint16_t binary_format_minor_version;uint64_t build_epoch;struct {size_t count;MMDB_description_s **descriptions;} description;
} MMDB_metadata_s;typedef struct MMDB_description_s {const char *language;const char *description;
} MMDB_description_s;

这些结构大多是不言自明的。

ip_version应始终为46binary_format_major_version应始终为2

不要求数据库元数据包括语言或描述,因此元数据的这些部分的count可以为零。 MMDB_metadata_s的其他所有字段都应该被填充。

MMDB_lookup_result_s

这个结构作为查找IP的结果返回。

typedef struct MMDB_lookup_result_s {bool found_entry;MMDB_entry_s entry;uint16_t netmask;
} MMDB_lookup_result_s;

如果found_entry为 false,则该结构的其他成员不包含有意义的值。 应始终首先检查found_entry是否为真。

entry用于查找与IP地址相关的数据。

netmask表示该数据库中IP地址属于哪个子网。 例如,如果在 IPv4 数据库中查找地址1.1.1.1,并且返回的netmask为16,则该地址是1.1.0.0/16子网的一部分。

如果数据库是 IPv6 数据库,则返回的网络掩码始终是 IPv6 前缀长度(从0到128),即使该数据库还包含 IPv4 网络。 如果您查找 IPv4 地址,并希望将网络掩码转换为 IPv4 网络掩码值,则可以从值中减去96

MMDB_result_s

不需要挖掘这个结构。你将从MMDB_lookup_result_s结构中得到这个结果,并将其传递给各种函数。

MMDB_entry_data_s

此结构用于返回 IP 的单个数据段条目。 这些条目可以反过来指向其他条目,就像 maps 和 arrays。这种结构的一些成员没有记录,因为它们仅供内部使用。

typedef struct MMDB_entry_data_s {bool has_data;union {uint32_t pointer;const char *utf8_string;double double_value;const uint8_t *bytes;uint16_t uint16;uint32_t uint32;int32_t int32;uint64_t uint64;{mmdb_uint128_t or uint8_t[16]} uint128;bool boolean;float float_value;};...uint32_t data_size;uint32_t type;
} MMDB_entry_data_s;

如果对于给定的查询找到数据,则has_data成员为 true。更多详细信息,请参阅MMDB_get_value()。如果该成员为 false,那么结构中的其他值都没有意义。

结构开始时的 union 定义了实际数据。要确定哪个 union 成员被填充,应该查看type成员。 不应该在 API 返回的任何数据中填充 union 的pointer成员。指针应该始终在内部解决。

data_size成员仅与utf8_stringbytes数据相关。 utf8_string不以 null 结尾,必须使用data_size来确定其长度。

type成员可以与MMDB_DTYPE_ *宏中的一个进行比较。

128位整型

uint128数据的处理取决于你的平台如何支持 128 位整型。使用 GCC 4.4 和 4.5,我们可以写成unsigned int __attribute__ ((__mode__ (TI)))。 使用较新版本的 GCC(4.6+) 和 clang(3.2+),我们可以简单地写成“unsigned __int128”。

为了解决这些差异,这个库定义了一个mmdb_uint128_t类型。 此类型在maxminddb.h头文件中定义,因此您可以在自己的代码中使用它。

对于较老的编译器,我们不能使用整型,所以我们通过 16 字节的数组替代uint8_t的值。这是数据库中的原始数据。

该库提供了一个公共宏MMDB_UINT128_IS_BYTE_ARRAY。 如果为 true,那么uint128值作为字节数组返回,如果是 false,那么它们返回为mmdb_uint128_t整型。

数据类型的宏

该库为 MaxMind DB 定义的每种数据类型提供一个宏。

  • MMDB_DATA_TYPE_UTF8_STRING
  • MMDB_DATA_TYPE_DOUBLE
  • MMDB_DATA_TYPE_BYTES
  • MMDB_DATA_TYPE_UINT16
  • MMDB_DATA_TYPE_UINT32
  • MMDB_DATA_TYPE_MAP
  • MMDB_DATA_TYPE_INT32
  • MMDB_DATA_TYPE_UINT64
  • MMDB_DATA_TYPE_UINT128
  • MMDB_DATA_TYPE_ARRAY
  • MMDB_DATA_TYPE_BOOLEAN
  • MMDB_DATA_TYPE_FLOAT

还有一些仅供内部使用的类型:

  • MMDB_DATA_TYPE_EXTENDED
  • MMDB_DATA_TYPE_POINTER
  • MMDB_DATA_TYPE_CONTAINER
  • MMDB_DATA_TYPE_END_MARKER

如果您在返回的数据中看到其中的一个,那么一定是发生错误了。 可能是数据库已损坏或生成不正确或 libmaxminddb 代码中存在错误。

指针的值和MMDB_close()

utf8_stringbytes和(也许)这个结构的uint128成员都是直接指向数据库数据部分的指针。这可能是一个mallocmmap的内存块。在这两种情况下,调用MMDB_close()后,这些指针将变为无效。

如果您在此之后需要引用此数据,则应使用适当的函数(strdupmemcpy等)复制数据。

MMDB_entry_data_list_s

该结构封装了MMDB_entry_data_s结构的链表。

typedef struct MMDB_entry_data_list_s {MMDB_entry_data_s entry_data;struct MMDB_entry_data_list_s *next;
} MMDB_entry_data_list_s;

此结构可让您通过遍历链表来查看整个 map 或 array 数据。

MMDB_search_node_s

该结构将两个记录封装在搜索节点中。如果您想要编写遍历整个搜索树的代码,而不是查找特定的 IP 地址,这是非常有用的。

typedef struct MMDB_search_node_s {uint64_t left_record;uint64_t right_record;uint8_t left_record_type;uint8_t right_record_type;MMDB_entry_s left_record_entry;MMDB_entry_s right_record_entry;
} MMDB_search_node_s;

两种记录类型将采用以下值之一:

  • MMDB_RECORD_TYPE_SEARCH_NODE - 记录指向下一个搜索节点。
  • MMDB_RECORD_TYPE_EMPTY - 记录是一个占位符,表示没有 IP 地址的数据。搜索应该在此处结束。
  • MMDB_RECORD_TYPE_DATA - 记录用于数据库数据部分中的数据。查找记录的数据时,使用记录条目。
  • MMDB_RECORD_TYPE_INVALID - 记录无效。查找无效节点或数据库已损坏。

MMDB_entry_s仅在类型为MMDB_RECORD_TYPE_DATA时有效。尝试使用其他记录类型的条目将导致错误或无效的数据。

状态码

该库返回(或填充)许多函数的状态码。这些状态码是:

  • MMDB_SUCCESS - 一切正常
  • MMDB_FILE_OPEN_ERROR - 尝试打开 MaxMind DB 文件时出错。
  • MMDB_IO_ERROR - IO 操作失败。检查 errno 获取更多的细节。
  • MMDB_CORRUPT_SEARCH_TREE_ERROR - 在搜索树中查找 IP 地址,给了我们一个不可能的结果。数据库已损坏或生成不正确或 libmaxminddb 代码中存在错误。
  • MMDB_INVALID_METADATA_ERROR - 数据库中的某些内容是错误的。包括丢失的元数据以及不可能的值(如ip_version为7)。
  • MMDB_UNKNOWN_DATABASE_FORMAT_ERROR - 数据库元数据表明它的主版本不是 2。该库只能处理版本 2。
  • MMDB_OUT_OF_MEMORY_ERROR - 内存分配调用(malloc等)失败。
  • MMDB_INVALID_DATA_ERROR - 数据部分中的条目包含无效数据。例如,一个uint16字段声称超过 2 个字节长。数据库可能已损坏或生成不正确。
  • MMDB_INVALID_LOOKUP_PATH_ERROR - 传递给MMDB_get_valueMMDB_vget_valueMMDB_aget_value的查找路径包含一个数组偏移量为负整数或大于 LONG_MAX 的整数。
  • MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR - 传递到MMDB_get_valueMMDB_vget_valueMMDB_aget_value的查找路径与条目的数据结构不匹配。有很多原因可能会发生。查找路径可以包括不在 map 中的 key。查找路径可以包括大于数组的索引。当路径期望找到不存在的 map 或 array 时,也可能发生。

所有状态代码都应该被视为int值。

MMDB_strerror()

const char *MMDB_strerror(int error_code)

此函数需要状态代码并返回一个解释状态的字符串。

函数

此库提供以下导出的函数:

MMDB_open()

int MMDB_open(const char *const filename,uint32_t flags,MMDB_s *const mmdb);

此函数打开 MaxMind DB 文件的句柄。它的返回值是上面定义的状态代码。始终检查此调用的返回值。

MMDB_s mmdb;
int status =MMDB_open("/path/to/file.mmdb", MMDB_MODE_MMAP, &mmdb);
if (MMDB_SUCCESS != status) { ... }
...
MMDB_close(&mmdb);

传入的MMDB_s结构可以在栈中或从堆中分配。但是,如果打开成功,它将包含堆分配的数据,因此需要使用MMDB_close()关闭它。如果返回的状态不是MMDB_SUCCESS,那么这个库确保在返回之前释放所有分配的内存。

目前提供的 flags 是:

  • MMDB_MODE_MMAP - 用mmap()打开数据库。

传递其他值的flags可能会产生不可预知的结果。将来我们可以添加额外的 flags,可以按位或与模式一起,以及其他模式。

也可以传递0作为flags值,在这种情况下,数据库将使用默认 flags 打开。然而,这些默认值可能会在将来的版本中更改。当前默认值为MMDB_MODE_MMAP

MMDB_close()

void MMDB_close(MMDB_s *const mmdb);

释放从MMDB_s结构中保留的任何已分配的或 mmap 的内存。它不释放分配给结构本身的内存! 如果你从堆中分配了结构,那么你将负责释放它。

MMDB_lookup_string()

MMDB_lookup_result_s MMDB_lookup_string(MMDB_s *const mmdb,const char *const ipstr,int *const gai_error,int *const mmdb_error);

此函数查找以 null 结尾的 IP 地址。在内部,它调用getaddrinfo()来将地址解析为二进制形式。 然后它调用MMDB_lookup_sockaddr()来查找数据库中的地址。如果您已经解析了一个地址,可以直接调用MMDB_lookup_sockaddr(),而不用两次解析地址。

int gai_error, mmdb_error;
MMDB_lookup_result_s result =MMDB_lookup_string(&mmdb, "1.2.3.4", &gai_error, &mmdb_error);
if (0 != gai_error) { ... }
if (MMDB_SUCCESS != mmdb_error) { ... }if (result.found_entry) { ... }

此函数始终返回一个MMDB_lookup_result_s结构,但您也应该检查gai_errormmdb_error参数。如果这些中的任何一个错误,则返回的结构是无意义的。

如果没有发生错误,您仍然需要确保返回结果中的found_entry成员为 true。 如果不是,这意味着 IP 地址在数据库中没有条目。

即使数据库包含 IPv4 和 IPv6 地址的数据,此函数也可以与 IPv4 地址一起使用。IPv4 地址将被查找为“::xxx.xxx.xxx.xxx”,而不是重新映射到为 IPv4 映射的 IPv6 地址分配的:: ffff:xxx.xxx.xxx.xxx块。

如果将 IPv6 地址传递到只有 IPv4 数据的数据库,那么found_entry将为 false,但是mmdb_error状态仍将是MMDB_SUCCESS

MMDB_lookup_sockaddr()

MMDB_lookup_result_s MMDB_lookup_sockaddr(MMDB_s *const mmdb,const struct sockaddr *const sockaddr,int *const mmdb_error);

此函数查找已由getaddrinfo()解析的 IP 地址。

除了不调用getaddrinfo(),此函数与MMDB_lookup_string()函数相同。

int mmdb_error;
MMDB_lookup_result_s result =MMDB_lookup_sockaddr(&mmdb, address->ai_addr, &mmdb_error);
if (MMDB_SUCCESS != mmdb_error) { ... }if (result.found_entry) { ... }

数据查找函数

有 3 个函数与查找 IP 地址相关。

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_entry_s值。在大多数情况下,这将来自MMDB_lookup_string()MMDB_lookup_sockaddr()返回的MMDB_lookup_result_s值。

第二个参数是对MMDB_entry_data_s结构的引用。这将被填充正在查找的数据(如果有)。如果没有找到,那么这个结构的has_data成员将是 false。如果has_data为 true,那么可以查看data_type成员。

最后一个参数是查找路径。该路径由表示在查找中使用的 map keys(例如,“城市”)或数组索引(例如,“0”,“1”)的字符串组成。这允许您操纵复杂的数据结构。例如:

{"names": {"en": "Germany","de": "Deutschland"},"cities": [ "Berlin", "Frankfurt" ]
}

我们可以使用以下代码查找英文名称:

MMDB_lookup_result_s result =MMDB_lookup_sockaddr(&mmdb, address->ai_addr, &mmdb_error);
MMDB_entry_data_s entry_data;
int status =MMDB_get_value(&result.entry, &entry_data,"names", "en", NULL);
if (MMDB_SUCCESS != status) { ... }
if (entry_data.has_data) { ... }

如果我们想找到第一个城市,查找路径将是"cities", "0"。如果没有提供查找路径,将获得与顶级 map 对应的条目。查找路径必须始终以NULL结尾,无论您调用哪个函数。

MMDB_get_value函数接受可变数量的参数。MMDB_entry_data_s *结构指针之后的所有参数都是查找路径。最后一个参数必须为NULL

MMDB_vget_value函数接受一个va_list作为查找路径。va_arg()检索的最后一个元素必须为NULL

最后,MMDB_aget_value接受字符串数组作为查找路径。该数组的最后一个成员必须为NULL

如果要一次获取所有条目数据,可以调用MMDB_get_entry_data_list()

对于三个函数中的每个函数,返回值是如上定义的状态代码。

MMDB_get_entry_data_list()

int MMDB_get_entry_data_list(MMDB_entry_s *start,MMDB_entry_data_list_s **const entry_data_list);

此函数允许一次获取复杂数据结构的所有数据,而不是重复调用MMDB_get_value()查找每个数据。

MMDB_lookup_result_s result =MMDB_lookup_sockaddr(&mmdb, address->ai_addr, &mmdb_error);
MMDB_entry_data_list_s *entry_data_list, *first;
int status =MMDB_get_entry_data_list(&result.entry, &entry_data_list);
if (MMDB_SUCCESS != status) { ... }
// save this so we can free this data later
first = entry_data_list;while (1) {MMDB_entry_data_list_s *next = entry_data_list = entry_data_list->next;if (NULL == next) {break;}switch (next->entry_data.type) {case MMDB_DATA_TYPE_MAP: { ... }case MMDB_DATA_TYPE_UTF8_STRING: { ... }...}
}MMDB_free_entry_data_list(first);

您可以解释entry_data_list数据结构。列表以深度优先遍历连接。我们以这种结构为例:

{"names": {"en": "Germany","de": "Deutschland"},"cities": [ "Berlin", "Frankfurt" ]
}

该清单将包括以下项目:

  1. MAP - 顶级 map
  2. UTF8_STRING - “名称”键
  3. MAP - “名称”键的 map
  4. UTF8_STRING - “en”键
  5. UTF8_STRING - “en”键的值
  6. UTF8_STRING - “de”键
  7. UTF8_STRING - “de”键的值
  8. UTF8_STRING - “cities”键
  9. ARRAY - “cities”键的值
  10. UTF8_STRING - 数组[0]
  11. UTF8_STRING - 数组[1]

函数的返回值是如上定义的状态码。

MMDB_free_entry_data_list()

void MMDB_free_entry_data_list(MMDB_entry_data_list_s *const entry_data_list);

MMDB_get_entry_data_list()MMDB_get_metadata_as_entry_data_list()函数将从堆中分配链表结构。调用此函数释放MMDB_entry_data_list_s结构。

MMDB_get_metadata_as_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_entry_data_list_s结构的链表。这可以是直接使用元数据结构来处理元数据的更方便的方法。

MMDB_entry_data_list_s *entry_data_list, *first;
int status =MMDB_get_metadata_as_entry_data_list(&mmdb, &entry_data_list);
if (MMDB_SUCCESS != status) { ... }
first = entry_data_list;
... // do something with the data
MMDB_free_entry_data_list(first);

函数的返回值是如上定义的状态码。

MMDB_dump_entry_data_list()

int MMDB_dump_entry_data_list(FILE *const stream,MMDB_entry_data_list_s *const entry_data_list,int indent);

此函数接收MMDB_entry_data_list_s结构的链表,并将其字符串给streamindent参数是生成输出的起始缩进级别。嵌套数据结构(maps,array 等)递增。

stream必须是文件句柄(stdout等)。如果您的平台提供了类似 GNU open_memstream()的功能,你可以使用它来以字符串形式捕获输出。

输出以 JSON-ish 格式进行格式化,但值以其数据类型标记(除了分别用“{}”和“[]”显示的 maps 和 arrays)。

具体的输出格式可能会在将来的版本中更改,因此您不应该依赖此功能生成的特定格式。 它旨在用于以可读方式向用户显示数据,并用于调试目的。

函数的返回值是如上定义的状态码。

MMDB_read_node()

int MMDB_read_node(MMDB_s *const mmdb,uint32_t node_number,MMDB_search_node_s *const node);

读取搜索树中的特定节点。第三个参数是对该函数的MMDB_search_node_s结构的引用。

返回值是状态码。如果传递的node_number大于数据库中的节点数,则此函数将返回MMDB_INVALID_NODE_NUMBER_ERROR,否则返回MMDB_SUCCESS

搜索树中的第一个节点始终是节点 0。如果要遍历整个搜索树,您将首先读取节点 0,然后根据每个记录的类型,按照构成该节点的记录进行跟踪。如果类型为MMDB_RECORD_TYPE_SEARCH_NODE,那么该记录将包含一个整数,以便下一个查找节点。

MMDB_lib_version()

const char *MMDB_lib_version(void)

这个函数返回一个字符串的库版本,类似“2.0.0”。

例子

#include <errno.h>
#include <maxminddb.h>
#include <stdlib.h>
#include <string.h>int main(int argc, char **argv)
{char *filename = argv[1];char *ip_address = argv[2];MMDB_s mmdb;int status = MMDB_open(filename, MMDB_MODE_MMAP, &mmdb);if (MMDB_SUCCESS != status) {fprintf(stderr, "\n  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);}int gai_error, mmdb_error;MMDB_lookup_result_s result =MMDB_lookup_string(&mmdb, ip_address, &gai_error, &mmdb_error);if (0 != gai_error) {fprintf(stderr,"\n  Error from getaddrinfo for %s - %s\n\n",ip_address, gai_strerror(gai_error));exit(2);}if (MMDB_SUCCESS != mmdb_error) {fprintf(stderr,"\n  Got an error from libmaxminddb: %s\n\n",MMDB_strerror(mmdb_error));exit(3);}MMDB_entry_data_list_s *entry_data_list = NULL;int exit_code = 0;if (result.found_entry) {int status = MMDB_get_entry_data_list(&result.entry,&entry_data_list);if (MMDB_SUCCESS != status) {fprintf(stderr,"Got an error looking up the entry data - %s\n",MMDB_strerror(status));exit_code = 4;goto end;}if (NULL != entry_data_list) {MMDB_dump_entry_data_list(stdout, entry_data_list, 2);}} else {fprintf(stderr,"\n  No entry for this IP address (%s) was found\n\n",ip_address);exit_code = 5;}end:MMDB_free_entry_data_list(entry_data_list);MMDB_close(&mmdb);exit(exit_code);
}

线程安全

当编译并链接线程安全的mallocfree实现时,该库是线程安全的。

安装和资源

您可以从 GitHub 下载最新版本的 libmaxminddb。

我们的 Github 仓库 是公开的,请 fork 吧!

Bug report 和 pull request

请将所有问题报告给 我们的 GitHub issue tracker。 我们欢迎 bug report 和 pull request。

作者

该库由 Boris Zentner(bzentner@maxmind.com)和 Dave Rolsky(drolsky@maxmind.com)编写。

libmaxminddb相关推荐

  1. html判断国家,nginx通过geoip2模块实现判断用户来源国家跳转中英站

    ip数据库文件下载地址:https://dev.maxmind.com/geoip/geoip2/geolite2/ 编译nginx添加第二代geoip2模块,第一代自带 的–with-http_ge ...

  2. centos 7--LNMP环境部署

    系统环境:centos 7.3 软件环境:mysql 5.6.12 采用二进制免编译安装包 php 7.17 增加扩展模块phalcon nginx 1.12.1 增加扩展模块 nginx_http_ ...

  3. GeoIP的使用-C语言版

    0x00. 简介 GeoIP库可以根据IP地址(支持IPv4 和 IPv6), 定位该IP所在的 洲.经纬度.国家.省市.ASN 等信息. GeoIP目前已经升级到GeoIP2,GeoIP2有两个版本 ...

  4. 通过GeoIP2分析访问者IP获取地理位置信息

    MaxMind GeoIP2 服务能识别互联网用户的地点位置与其他特征,应用广泛,包括个性化定制内容.诈欺检测.广告定向.网站流量分析.执行规定.地理目标定位.地理围栏定位 (geo-fencing) ...

  5. openresty开发系列40--nginx+lua实现获取客户端ip所在的国家信息

    openresty开发系列40--nginx+lua实现获取客户端ip所在的国家信息 为了实现业务系统针对不同地区IP访问,展示包含不同地区信息的业务交互界面.很多情况下系统需要根据用户访问的IP信息 ...

  6. Nginx通过地理位置限制访问

    Nginx通过地理位置限制访问 介绍 先决条件 获取数据库 了解数据库结构 在NGINX Plus中配置GeoIP2 方案:选择最近的服务器 介绍 NGINX Plus可以根据用户的地理位置来区分用户 ...

  7. 【记录一次服务器被攻击】-[附带解决方案]

    当我准备重新构建docker项目时,发现一条某个IP的不正常请求. 日志如下 yudao-ui-admin | 156.219.223.76 - - [09/Apr/2022:16:49:37 +00 ...

  8. 【nginx 扩容及常用模块扩展】

    Nginx高级 第一部分:扩容 通过扩容提升整体吞吐量 1.单机垂直扩容:硬件资源增加 云服务资源增加 整机:IBM.浪潮.DELL.HP等 CPU/主板:更新到主流 网卡:10G/40G网卡 磁盘: ...

  9. nginx禁止外网访问

    1.安装 libmaxminddb 库 apt updateapt install libmaxminddb0 libmaxminddb-dev mmdb-bin 上面安装的软件包是: libmaxm ...

  10. nginx利用ngx_http_geoip2_module模块对国外ip限制

    1. 安装必要的环境 yum -y install gcc gcc-c++ automake pcre pcre-devel zlib zlib-devel openssl openssl-devel ...

最新文章

  1. java无刷新上传图片_【java实现web文件无刷新上传】
  2. 面向对象之__isset__unset
  3. cogs 1456. [UVa 10881,Piotr's Ants]蚂蚁
  4. bpmn2 vue 设计器_vue项目中使用bpmn-基础篇
  5. 【重难点】【Java集合 03】ArrayList、LinkedList、 Vector 和 Stack 的区别、CopyOnWriteArrayList
  6. java-集合(三)
  7. 狐狸抓老鼠,为何东北向才会成功
  8. Flink 动态配置(参数 算子 CEP)
  9. ZigBee-CC2530单片机 - LED呼吸灯
  10. PPT开场,吸引人的几点技巧
  11. bootstrap-select 的多选+模糊查询下拉框详解
  12. git push -u origin XXX 报错
  13. Mac 安装element-ui
  14. ESP32学习笔记(45)——DAC接口使用
  15. minio Non-XML response from server
  16. 【Pytorch安装】Failed building wheel for XXX踩坑
  17. 计算机课程CAP,大学计算机基础CAP
  18. Python实现进制转换器
  19. 【报告分享】中国移动阅读市场年度综合分析2021-易观智库(附下载)
  20. 网站开发主要有哪几种类型?

热门文章

  1. 视频教程-HTML5基础视频课程 - 实用的HTML教程-HTML5/CSS
  2. C# 操作Excel(不需要安装Excel),.Net版的 Excel 控件
  3. MySQL 中的 repeate() 函数
  4. Reflex WMS入门系列七:收货(Receipt)
  5. H5 微信分享显示标题和图标
  6. arduino/Mixly使用MAX30102心率传感器
  7. 使用python修改微信支付宝运动步数
  8. 程序员学习交流的网站论坛
  9. Python数据分析案例09——航空公司客户聚类分析
  10. 十大排行优惠券app,哪个更适合