From 58a64597e8b0f8607c34ce7c22c4d1eeb0c9785a Mon Sep 17 00:00:00 2001 From: Arkshine Date: Tue, 29 Jul 2014 11:31:26 +0200 Subject: [PATCH] Geoip: Import libmaxminddb library which supports Geoip2 databases. --- dlls/geoip/GeoIP2/maxminddb-compat-util.h | 167 ++ dlls/geoip/GeoIP2/maxminddb.c | 1790 +++++++++++++++++++++ dlls/geoip/GeoIP2/maxminddb.h | 204 +++ dlls/geoip/GeoIP2/maxminddb_config.h | 16 + 4 files changed, 2177 insertions(+) create mode 100644 dlls/geoip/GeoIP2/maxminddb-compat-util.h create mode 100644 dlls/geoip/GeoIP2/maxminddb.c create mode 100644 dlls/geoip/GeoIP2/maxminddb.h create mode 100644 dlls/geoip/GeoIP2/maxminddb_config.h diff --git a/dlls/geoip/GeoIP2/maxminddb-compat-util.h b/dlls/geoip/GeoIP2/maxminddb-compat-util.h new file mode 100644 index 00000000..e3f0320f --- /dev/null +++ b/dlls/geoip/GeoIP2/maxminddb-compat-util.h @@ -0,0 +1,167 @@ +#include +#include + +/* *INDENT-OFF* */ + +/* The memmem, strdup, and strndup functions were all copied from the + * FreeBSD source, along with the relevant copyright notice. + * + * It'd be nicer to simply use the functions available on the system if they + * exist, but there doesn't seem to be a good way to detect them without also + * defining things like _GNU_SOURCE, which we want to avoid, because then we + * end up _accidentally_ using GNU features without noticing, which then + * breaks on systems like OSX. + * + * C is fun! */ + +/* Applies to memmem implementation */ +/*- + * Copyright (c) 2005 Pascal Gloor + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +static void * +mmdb_memmem(const void *l, size_t l_len, const void *s, size_t s_len) +{ + register char *cur, *last; + const char *cl = (const char *)l; + const char *cs = (const char *)s; + + /* we need something to compare */ + if (l_len == 0 || s_len == 0) + return NULL; + + /* "s" must be smaller or equal to "l" */ + if (l_len < s_len) + return NULL; + + /* special case where s_len == 1 */ + if (s_len == 1) + return memchr(l, (int)*cs, l_len); + + /* the last position where its possible to find "s" in "l" */ + last = (char *)cl + l_len - s_len; + + for (cur = (char *)cl; cur <= last; cur++) + if (cur[0] == cs[0] && memcmp(cur, cs, s_len) == 0) + return cur; + + return NULL; +} + +/* Applies to strnlen implementation */ +/*- + * Copyright (c) 2009 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +static size_t +mmdb_strnlen(const char *s, size_t maxlen) +{ + size_t len; + + for (len = 0; len < maxlen; len++, s++) { + if (!*s) + break; + } + return (len); +} + +/* Applies to strdup and strndup implementation */ +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +static char * +mmdb_strdup(const char *str) +{ + size_t len; + char *copy; + + len = strlen(str) + 1; + if ((copy = malloc(len)) == NULL) + return (NULL); + memcpy(copy, str, len); + return (copy); +} + +static char * +mmdb_strndup(const char *str, size_t n) +{ + size_t len; + char *copy; + + len = mmdb_strnlen(str, n); + if ((copy = malloc(len + 1)) == NULL) + return (NULL); + memcpy(copy, str, len); + copy[len] = '\0'; + return (copy); +} +/* *INDENT-ON* */ diff --git a/dlls/geoip/GeoIP2/maxminddb.c b/dlls/geoip/GeoIP2/maxminddb.c new file mode 100644 index 00000000..d4ad1fef --- /dev/null +++ b/dlls/geoip/GeoIP2/maxminddb.c @@ -0,0 +1,1790 @@ +#if HAVE_CONFIG_H +#include +#endif +#include "maxminddb.h" +#include "maxminddb-compat-util.h" +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#else +#include +#include +#include +#endif + +#define MMDB_DATA_SECTION_SEPARATOR (16) + +#ifdef MMDB_DEBUG +#define LOCAL +#define NO_PROTO +#define DEBUG_FUNC +#define DEBUG_MSG(msg) fprintf(stderr, msg "\n") +#define DEBUG_MSGF(fmt, ...) fprintf(stderr, fmt "\n", __VA_ARGS__) +#define DEBUG_BINARY(fmt, byte) \ + do { \ + char *binary = byte_to_binary(byte); \ + if (NULL == binary) { \ + fprintf(stderr, "Malloc failed in DEBUG_BINARY\n"); \ + abort(); \ + } \ + fprintf(stderr, fmt "\n", binary); \ + free(binary); \ + } while (0) +#define DEBUG_NL fprintf(stderr, "\n") +#else +#define LOCAL static +#define NO_PROTO static +#define DEBUG_MSG(...) +#define DEBUG_MSGF(...) +#define DEBUG_BINARY(...) +#define DEBUG_NL +#endif + +#ifdef MMDB_DEBUG +DEBUG_FUNC char *byte_to_binary(uint8_t byte) +{ + char *bits = malloc(sizeof(char) * 9); + if (NULL == bits) { + return bits; + } + + for (uint8_t i = 0; i < 8; i++) { + bits[i] = byte & (128 >> i) ? '1' : '0'; + } + bits[8] = '\0'; + + return bits; +} + +DEBUG_FUNC char *type_num_to_name(uint8_t num) +{ + switch (num) { + case 0: + return "extended"; + case 1: + return "pointer"; + case 2: + return "utf8_string"; + case 3: + return "double"; + case 4: + return "bytes"; + case 5: + return "uint16"; + case 6: + return "uint32"; + case 7: + return "map"; + case 8: + return "int32"; + case 9: + return "uint64"; + case 10: + return "uint128"; + case 11: + return "array"; + case 12: + return "container"; + case 13: + return "end_marker"; + case 14: + return "boolean"; + case 15: + return "float"; + default: + return "unknown type"; + } +} +#endif + +typedef struct record_info_s { + uint16_t record_length; + uint32_t (*left_record_getter)(const uint8_t *); + uint32_t (*right_record_getter)(const uint8_t *); + uint8_t right_record_offset; +} record_info_s; + +#define METADATA_MARKER "\xab\xcd\xefMaxMind.com" +/* This is 128kb */ +#define METADATA_BLOCK_MAX_SIZE 131072 + +/* *INDENT-OFF* */ +/* --prototypes automatically generated by dev-bin/regen-prototypes.pl - don't remove this comment */ +LOCAL const uint8_t *find_metadata(const uint8_t *file_content, + ssize_t file_size, uint32_t *metadata_size); +LOCAL int read_metadata(MMDB_s *mmdb); +LOCAL MMDB_s make_fake_metadata_db(MMDB_s *mmdb); +LOCAL uint16_t value_for_key_as_uint16(MMDB_entry_s *start, char *key); +LOCAL uint32_t value_for_key_as_uint32(MMDB_entry_s *start, char *key); +LOCAL uint64_t value_for_key_as_uint64(MMDB_entry_s *start, char *key); +LOCAL char *value_for_key_as_string(MMDB_entry_s *start, char *key); +LOCAL int populate_languages_metadata(MMDB_s *mmdb, MMDB_s *metadata_db, + MMDB_entry_s *metadata_start); +LOCAL int populate_description_metadata(MMDB_s *mmdb, MMDB_s *metadata_db, + MMDB_entry_s *metadata_start); +LOCAL int resolve_any_address(const char *ipstr, struct addrinfo **addresses); +LOCAL int find_address_in_search_tree(MMDB_s *mmdb, uint8_t *address, + sa_family_t address_family, + MMDB_lookup_result_s *result); +LOCAL record_info_s record_info_for_database(MMDB_s *mmdb); +LOCAL MMDB_ipv4_start_node_s find_ipv4_start_node(MMDB_s *mmdb); +LOCAL int populate_result(MMDB_s *mmdb, uint32_t node_count, uint32_t value, + uint16_t netmask, MMDB_lookup_result_s *result); +LOCAL uint32_t get_left_28_bit_record(const uint8_t *record); +LOCAL uint32_t get_right_28_bit_record(const uint8_t *record); +LOCAL int lookup_path_in_array(const char *path_elem, MMDB_s *mmdb, + MMDB_entry_data_s *entry_data); +LOCAL int lookup_path_in_map(const char *path_elem, MMDB_s *mmdb, + MMDB_entry_data_s *entry_data); +LOCAL int skip_map_or_array(MMDB_s *mmdb, MMDB_entry_data_s *entry_data); +LOCAL int decode_one_follow(MMDB_s *mmdb, uint32_t offset, + MMDB_entry_data_s *entry_data); +LOCAL int decode_one(MMDB_s *mmdb, uint32_t offset, + MMDB_entry_data_s *entry_data); +LOCAL int get_ext_type(int raw_ext_type); +LOCAL uint32_t get_ptr_from(uint8_t ctrl, uint8_t const *const ptr, + int ptr_size); +LOCAL int get_entry_data_list(MMDB_s *mmdb, uint32_t offset, + MMDB_entry_data_list_s *const entry_data_list); +LOCAL float get_ieee754_float(const uint8_t *restrict p); +LOCAL double get_ieee754_double(const uint8_t *restrict p); +LOCAL uint32_t get_uint32(const uint8_t *p); +LOCAL uint32_t get_uint24(const uint8_t *p); +LOCAL uint32_t get_uint16(const uint8_t *p); +LOCAL uint64_t get_uintX(const uint8_t *p, int length); +LOCAL int32_t get_sintX(const uint8_t *p, int length); +LOCAL MMDB_entry_data_list_s *new_entry_data_list(void); +LOCAL void free_mmdb_struct(MMDB_s *const mmdb); +LOCAL void free_languages_metadata(MMDB_s *mmdb); +LOCAL void free_descriptions_metadata(MMDB_s *mmdb); +LOCAL MMDB_entry_data_list_s *dump_entry_data_list( + FILE *stream, MMDB_entry_data_list_s *entry_data_list, int indent, + int *status); +LOCAL void print_indentation(FILE *stream, int i); +LOCAL char *bytes_to_hex(uint8_t *bytes, uint32_t size); +/* --prototypes end - don't remove this comment-- */ +/* *INDENT-ON* */ + +#define CHECKED_DECODE_ONE(mmdb, offset, entry_data) \ + do { \ + int status = decode_one(mmdb, offset, entry_data); \ + if (MMDB_SUCCESS != status) { \ + return status; \ + } \ + } while (0) + +#define CHECKED_DECODE_ONE_FOLLOW(mmdb, offset, entry_data) \ + do { \ + int status = decode_one_follow(mmdb, offset, entry_data); \ + if (MMDB_SUCCESS != status) { \ + return status; \ + } \ + } while (0) + +int MMDB_open(const char *const filename, uint32_t flags, MMDB_s *const mmdb) +{ + mmdb->file_content = NULL; + mmdb->data_section = NULL; + mmdb->metadata.database_type = NULL; + mmdb->metadata.languages.count = 0; + mmdb->metadata.description.count = 0; + + mmdb->filename = mmdb_strdup(filename); + if (NULL == mmdb->filename) { + free_mmdb_struct(mmdb); + return MMDB_OUT_OF_MEMORY_ERROR; + } + + ssize_t size; +#ifdef _WIN32 + HANDLE fd = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (fd == INVALID_HANDLE_VALUE) { + free_mmdb_struct(mmdb); + return MMDB_FILE_OPEN_ERROR; + } + size = GetFileSize(fd, NULL); +#else + int fd = open(filename, O_RDONLY); + if (fd < 0) { + free_mmdb_struct(mmdb); + return MMDB_FILE_OPEN_ERROR; + } + + struct stat s; + fstat(fd, &s); + size = s.st_size; +#endif + + if ((flags & MMDB_MODE_MASK) == 0) { + flags |= MMDB_MODE_MMAP; + } + mmdb->flags = flags; + mmdb->file_size = size; + +#ifdef _WIN32 + HANDLE mmh = CreateFileMappingA(fd, NULL, PAGE_READONLY, 0, size, filename); + uint8_t *file_content = + (uint8_t *)MapViewOfFile(mmh, FILE_MAP_READ, 0, 0, 0); + if (file_content == NULL) { + CloseHandle(mmh); + CloseHandle(fd); + free_mmdb_struct(mmdb); + return MMDB_IO_ERROR; + } +#else + uint8_t *file_content = + (uint8_t *)mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + if (MAP_FAILED == file_content) { + free_mmdb_struct(mmdb); + return MMDB_IO_ERROR; + } +#endif + + uint32_t metadata_size = 0; + const uint8_t *metadata = find_metadata(file_content, size, &metadata_size); + if (NULL == metadata) { + free_mmdb_struct(mmdb); + return MMDB_INVALID_METADATA_ERROR; + } + + mmdb->metadata_section = metadata; + mmdb->metadata_section_size = metadata_size; + + int status = read_metadata(mmdb); + if (MMDB_SUCCESS != status) { + free_mmdb_struct(mmdb); + return status; + } + + if (mmdb->metadata.binary_format_major_version != 2) { + free_mmdb_struct(mmdb); + return MMDB_UNKNOWN_DATABASE_FORMAT_ERROR; + } + +#ifdef _WIN32 + WSADATA wsa; + WSAStartup(MAKEWORD(2, 2), &wsa); + CloseHandle(fd); +#else + close(fd); +#endif + + uint32_t search_tree_size = mmdb->metadata.node_count * + mmdb->full_record_byte_size; + + mmdb->file_content = file_content; + mmdb->data_section = file_content + search_tree_size; + mmdb->data_section_size = mmdb->file_size - search_tree_size; + mmdb->metadata_section = metadata; + mmdb->ipv4_start_node.node_value = 0; + mmdb->ipv4_start_node.netmask = 0; + + return MMDB_SUCCESS; +} + +LOCAL const uint8_t *find_metadata(const uint8_t *file_content, + ssize_t file_size, uint32_t *metadata_size) +{ + ssize_t max_size = file_size > + METADATA_BLOCK_MAX_SIZE ? METADATA_BLOCK_MAX_SIZE : + file_size; + + uint8_t *search_area = (uint8_t *)(file_content + (file_size - max_size)); + uint8_t *tmp = search_area; + do { + tmp = mmdb_memmem(search_area, max_size, + METADATA_MARKER, strlen(METADATA_MARKER)); + + if (NULL != tmp) { + max_size -= tmp - search_area; + search_area = tmp; + } + } while (NULL != tmp && tmp != search_area); + + const uint8_t *metadata_start = search_area + strlen(METADATA_MARKER); + *metadata_size = file_size - (search_area - file_content); + + return metadata_start; +} + +LOCAL int read_metadata(MMDB_s *mmdb) +{ + /* We need to create a fake MMDB_s struct in order to decode values from + the metadata. The metadata is basically just like the data section, so we + want to use the same functions we use for the data section to get metadata + values. */ + MMDB_s metadata_db = make_fake_metadata_db(mmdb); + + MMDB_entry_s metadata_start = { + .mmdb = &metadata_db, + .offset = 0 + }; + + mmdb->metadata.node_count = + value_for_key_as_uint32(&metadata_start, "node_count"); + if (!mmdb->metadata.node_count) { + DEBUG_MSG("could not find node_count value in metadata"); + return MMDB_INVALID_METADATA_ERROR; + } + + mmdb->metadata.record_size = + value_for_key_as_uint16(&metadata_start, "record_size"); + if (!mmdb->metadata.record_size) { + DEBUG_MSG("could not find record_size value in metadata"); + return MMDB_INVALID_METADATA_ERROR; + } + + if (mmdb->metadata.record_size != 24 && mmdb->metadata.record_size != 28 + && mmdb->metadata.record_size != 32) { + DEBUG_MSGF("bad record size in metadata: %i", + mmdb->metadata.record_size); + return MMDB_UNKNOWN_DATABASE_FORMAT_ERROR; + } + + mmdb->metadata.ip_version = + value_for_key_as_uint16(&metadata_start, "ip_version"); + if (!mmdb->metadata.ip_version) { + DEBUG_MSG("could not find ip_version value in metadata"); + return MMDB_INVALID_METADATA_ERROR; + } + if (!(mmdb->metadata.ip_version == 4 || mmdb->metadata.ip_version == 6)) { + DEBUG_MSGF("ip_version value in metadata is not 4 or 6 - it was %i", + mmdb->metadata.ip_version); + return MMDB_INVALID_METADATA_ERROR; + } + + mmdb->metadata.database_type = + value_for_key_as_string(&metadata_start, "database_type"); + if (NULL == mmdb->metadata.database_type) { + DEBUG_MSG("could not find database_type value in metadata"); + return MMDB_INVALID_METADATA_ERROR; + } + + int status = + populate_languages_metadata(mmdb, &metadata_db, &metadata_start); + if (MMDB_SUCCESS != status) { + DEBUG_MSG("could not populate languages from metadata"); + return status; + } + + mmdb->metadata.binary_format_major_version = + value_for_key_as_uint16(&metadata_start, "binary_format_major_version"); + if (!mmdb->metadata.binary_format_major_version) { + DEBUG_MSG( + "could not find binary_format_major_version value in metadata"); + return MMDB_INVALID_METADATA_ERROR; + } + + mmdb->metadata.binary_format_minor_version = + value_for_key_as_uint16(&metadata_start, "binary_format_minor_version"); + + mmdb->metadata.build_epoch = + value_for_key_as_uint64(&metadata_start, "build_epoch"); + if (!mmdb->metadata.build_epoch) { + DEBUG_MSG("could not find build_epoch value in metadata"); + return MMDB_INVALID_METADATA_ERROR; + } + + status = populate_description_metadata(mmdb, &metadata_db, &metadata_start); + if (MMDB_SUCCESS != status) { + DEBUG_MSG("could not populate description from metadata"); + return status; + } + + mmdb->full_record_byte_size = mmdb->metadata.record_size * 2 / 8U; + + mmdb->depth = mmdb->metadata.ip_version == 4 ? 32 : 128; + + return MMDB_SUCCESS; +} + +LOCAL MMDB_s make_fake_metadata_db(MMDB_s *mmdb) +{ + MMDB_s fake_metadata_db = { + .data_section = mmdb->metadata_section, + .data_section_size = mmdb->metadata_section_size + }; + + return fake_metadata_db; +} + +LOCAL uint16_t value_for_key_as_uint16(MMDB_entry_s *start, char *key) +{ + MMDB_entry_data_s entry_data; + const char *path[] = { key, NULL }; + MMDB_aget_value(start, &entry_data, path); + return entry_data.uint16; +} + +LOCAL uint32_t value_for_key_as_uint32(MMDB_entry_s *start, char *key) +{ + MMDB_entry_data_s entry_data; + const char *path[] = { key, NULL }; + MMDB_aget_value(start, &entry_data, path); + return entry_data.uint32; +} + +LOCAL uint64_t value_for_key_as_uint64(MMDB_entry_s *start, char *key) +{ + MMDB_entry_data_s entry_data; + const char *path[] = { key, NULL }; + MMDB_aget_value(start, &entry_data, path); + return entry_data.uint64; +} + +LOCAL char *value_for_key_as_string(MMDB_entry_s *start, char *key) +{ + MMDB_entry_data_s entry_data; + const char *path[] = { key, NULL }; + MMDB_aget_value(start, &entry_data, path); + return mmdb_strndup((char *)entry_data.utf8_string, entry_data.data_size); +} + +LOCAL int populate_languages_metadata(MMDB_s *mmdb, MMDB_s *metadata_db, + MMDB_entry_s *metadata_start) +{ + MMDB_entry_data_s entry_data; + + const char *path[] = { "languages", NULL }; + MMDB_aget_value(metadata_start, &entry_data, path); + + if (MMDB_DATA_TYPE_ARRAY != entry_data.type) { + return MMDB_INVALID_METADATA_ERROR; + } + + MMDB_entry_s array_start = { + .mmdb = metadata_db, + .offset = entry_data.offset + }; + + MMDB_entry_data_list_s *member; + MMDB_get_entry_data_list(&array_start, &member); + + MMDB_entry_data_list_s *first_member = member; + + uint32_t array_size = member->entry_data.data_size; + mmdb->metadata.languages.count = 0; + mmdb->metadata.languages.names = malloc(array_size * sizeof(char *)); + if (NULL == mmdb->metadata.languages.names) { + return MMDB_OUT_OF_MEMORY_ERROR; + } + + for (uint32_t i = 0; i < array_size; i++) { + member = member->next; + if (MMDB_DATA_TYPE_UTF8_STRING != member->entry_data.type) { + return MMDB_INVALID_METADATA_ERROR; + } + + mmdb->metadata.languages.names[i] = + mmdb_strndup((char *)member->entry_data.utf8_string, + member->entry_data.data_size); + + if (NULL == mmdb->metadata.languages.names[i]) { + return MMDB_OUT_OF_MEMORY_ERROR; + } + // We assign this as we go so that if we fail a malloc and need to + // free it, the count is right. + mmdb->metadata.languages.count = i + 1; + } + + MMDB_free_entry_data_list(first_member); + + return MMDB_SUCCESS; +} + +LOCAL int populate_description_metadata(MMDB_s *mmdb, MMDB_s *metadata_db, + MMDB_entry_s *metadata_start) +{ + MMDB_entry_data_s entry_data; + + const char *path[] = { "description", NULL }; + MMDB_aget_value(metadata_start, &entry_data, path); + + if (MMDB_DATA_TYPE_MAP != entry_data.type) { + return MMDB_INVALID_METADATA_ERROR; + } + + MMDB_entry_s map_start = { + .mmdb = metadata_db, + .offset = entry_data.offset + }; + + MMDB_entry_data_list_s *member; + MMDB_get_entry_data_list(&map_start, &member); + + MMDB_entry_data_list_s *first_member = member; + + uint32_t map_size = member->entry_data.data_size; + mmdb->metadata.description.count = 0; + mmdb->metadata.description.descriptions = + malloc(map_size * sizeof(MMDB_description_s *)); + if (NULL == mmdb->metadata.description.descriptions) { + return MMDB_OUT_OF_MEMORY_ERROR; + } + + for (uint32_t i = 0; i < map_size; i++) { + mmdb->metadata.description.descriptions[i] = + malloc(sizeof(MMDB_description_s)); + if (NULL == mmdb->metadata.description.descriptions[i]) { + return MMDB_OUT_OF_MEMORY_ERROR; + } + + mmdb->metadata.description.count = i + 1; + mmdb->metadata.description.descriptions[i]->language = NULL; + mmdb->metadata.description.descriptions[i]->description = NULL; + + member = member->next; + + if (MMDB_DATA_TYPE_UTF8_STRING != member->entry_data.type) { + return MMDB_INVALID_METADATA_ERROR; + } + + mmdb->metadata.description.descriptions[i]->language = + mmdb_strndup((char *)member->entry_data.utf8_string, + member->entry_data.data_size); + + if (NULL == mmdb->metadata.description.descriptions[i]->language) { + return MMDB_OUT_OF_MEMORY_ERROR; + } + + member = member->next; + + if (MMDB_DATA_TYPE_UTF8_STRING != member->entry_data.type) { + return MMDB_INVALID_METADATA_ERROR; + } + + mmdb->metadata.description.descriptions[i]->description = + mmdb_strndup((char *)member->entry_data.utf8_string, + member->entry_data.data_size); + + if (NULL == mmdb->metadata.description.descriptions[i]->description) { + return MMDB_OUT_OF_MEMORY_ERROR; + } + } + + MMDB_free_entry_data_list(first_member); + + return MMDB_SUCCESS; +} + +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 result = { + .found_entry = false, + .netmask = 0, + .entry = { + .mmdb = mmdb, + .offset = 0 + } + }; + + struct addrinfo *addresses = NULL; + *gai_error = resolve_any_address(ipstr, &addresses); + + if (*gai_error) { + if (NULL != addresses) { + freeaddrinfo(addresses); + } + return result; + } + + if (mmdb->metadata.ip_version == 4 + && addresses->ai_addr->sa_family == AF_INET6) { + + *mmdb_error = MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR; + freeaddrinfo(addresses); + return result; + } + + result = MMDB_lookup_sockaddr(mmdb, addresses->ai_addr, mmdb_error); + + freeaddrinfo(addresses); + + return result; +} + +LOCAL int resolve_any_address(const char *ipstr, struct addrinfo **addresses) +{ + struct addrinfo hints = { + .ai_socktype = SOCK_STREAM + }; + int gai_status; + + if (NULL != strchr(ipstr, ':')) { + hints.ai_flags = AI_NUMERICHOST; +#if defined AI_V4MAPPED && !defined __FreeBSD__ + hints.ai_flags |= AI_V4MAPPED; +#endif + hints.ai_family = AF_INET6; + } else { + hints.ai_flags = AI_NUMERICHOST; + hints.ai_family = AF_INET; + } + + gai_status = getaddrinfo(ipstr, NULL, &hints, addresses); + if (gai_status) { + return gai_status; + } + + return 0; +} + +MMDB_lookup_result_s MMDB_lookup_sockaddr( + MMDB_s *const mmdb, + const struct sockaddr *const sockaddr, + int *const mmdb_error) +{ + MMDB_lookup_result_s result = { + .found_entry = false, + .netmask = 0, + .entry = { + .mmdb = mmdb, + .offset = 0 + } + }; + + uint8_t mapped_address[16], *address; + if (mmdb->metadata.ip_version == 4) { + if (sockaddr->sa_family == AF_INET6) { + return result; + } + address = (uint8_t *)&((struct sockaddr_in *)sockaddr)->sin_addr.s_addr; + } else { + if (sockaddr->sa_family == AF_INET6) { + address = + (uint8_t *)&((struct sockaddr_in6 *)sockaddr)->sin6_addr. + s6_addr; + } else { + address = mapped_address; + memset(address, 0, 12); + memcpy(address + 12, + &((struct sockaddr_in *)sockaddr)->sin_addr.s_addr, 4); + } + } + + *mmdb_error = + find_address_in_search_tree(mmdb, address, sockaddr->sa_family, + &result); + + return result; +} + +LOCAL int find_address_in_search_tree(MMDB_s *mmdb, uint8_t *address, + sa_family_t address_family, + MMDB_lookup_result_s *result) +{ + record_info_s record_info = record_info_for_database(mmdb); + if (0 == record_info.right_record_offset) { + return MMDB_UNKNOWN_DATABASE_FORMAT_ERROR; + } + + DEBUG_NL; + DEBUG_MSG("Looking for address in search tree"); + + uint32_t node_count = mmdb->metadata.node_count; + uint32_t value = 0; + uint16_t max_depth0 = mmdb->depth - 1; + uint16_t start_bit = max_depth0; + + if (mmdb->metadata.ip_version == 6 && address_family == AF_INET) { + MMDB_ipv4_start_node_s ipv4_start_node = find_ipv4_start_node(mmdb); + DEBUG_MSGF("IPv4 start node is %u (netmask %u)", + ipv4_start_node.node_value, ipv4_start_node.netmask); + /* We have an IPv6 database with no IPv4 data */ + if (ipv4_start_node.node_value >= node_count) { + return populate_result(mmdb, node_count, ipv4_start_node.node_value, + ipv4_start_node.netmask, result); + } + + value = ipv4_start_node.node_value; + start_bit -= ipv4_start_node.netmask; + } + + const uint8_t *search_tree = mmdb->file_content; + const uint8_t *record_pointer; + for (int current_bit = start_bit; current_bit >= 0; current_bit--) { + uint8_t bit_is_true = + address[(max_depth0 - current_bit) >> 3] + & (1U << (~(max_depth0 - current_bit) & 7)) ? 1 : 0; + + DEBUG_MSGF("Looking at bit %i - bit's value is %i", current_bit, + bit_is_true); + DEBUG_MSGF(" current node = %u", value); + + record_pointer = &search_tree[value * record_info.record_length]; + if (bit_is_true) { + record_pointer += record_info.right_record_offset; + value = record_info.right_record_getter(record_pointer); + } else { + value = record_info.left_record_getter(record_pointer); + } + + /* Ideally we'd check to make sure that a record never points to a + * previously seen value, but that's more complicated. For now, we can + * at least check that we don't end up at the top of the tree again. */ + if (0 == value) { + DEBUG_MSGF(" %s record has a value of 0", + bit_is_true ? "right" : "left"); + return MMDB_CORRUPT_SEARCH_TREE_ERROR; + } + + if (value >= node_count) { + return populate_result(mmdb, node_count, value, current_bit, result); + } else { + DEBUG_MSGF(" proceeding to search tree node %i", value); + } + } + + DEBUG_MSG( + "Reached the end of the address bits without leaving the search tree"); + + // We should not be able to reach this return. If we do, something very bad happened. + return MMDB_CORRUPT_SEARCH_TREE_ERROR; +} + +LOCAL record_info_s record_info_for_database(MMDB_s *mmdb) +{ + record_info_s record_info = { + .record_length = mmdb->full_record_byte_size, + .right_record_offset = 0 + }; + + if (record_info.record_length == 6) { + record_info.left_record_getter = &get_uint24; + record_info.right_record_getter = &get_uint24; + record_info.right_record_offset = 3; + } else if (record_info.record_length == 7) { + record_info.left_record_getter = &get_left_28_bit_record; + record_info.right_record_getter = &get_right_28_bit_record; + record_info.right_record_offset = 3; + } else if (record_info.record_length == 8) { + record_info.left_record_getter = &get_uint32; + record_info.right_record_getter = &get_uint32; + record_info.right_record_offset = 4; + } + + return record_info; +} + +LOCAL MMDB_ipv4_start_node_s find_ipv4_start_node(MMDB_s *mmdb) +{ + /* In a pathological case of a database with a single node search tree, + * this check will be true even after we've found the IPv4 start node, but + * that doesn't seem worth trying to fix. */ + if (mmdb->ipv4_start_node.node_value != 0) { + return mmdb->ipv4_start_node; + } + + record_info_s record_info = record_info_for_database(mmdb); + + const uint8_t *search_tree = mmdb->file_content; + uint32_t node_value = 0; + const uint8_t *record_pointer; + uint32_t netmask; + for (netmask = 0; netmask < 96; netmask++) { + record_pointer = &search_tree[node_value * record_info.record_length]; + node_value = record_info.left_record_getter(record_pointer); + /* This can happen if there's no IPv4 data _or_ if there is a subnet + * with data that contains the entire IPv4 range (like ::/64) */ + if (node_value >= mmdb->metadata.node_count) { + break; + } + } + + mmdb->ipv4_start_node.node_value = node_value; + mmdb->ipv4_start_node.netmask = netmask; + + return mmdb->ipv4_start_node; +} + +LOCAL int populate_result(MMDB_s *mmdb, uint32_t node_count, uint32_t value, + uint16_t netmask, MMDB_lookup_result_s *result) +{ + uint32_t offset = value - node_count; + DEBUG_MSGF(" data section offset is %i (record value = %i)", offset, value); + + if (offset > mmdb->data_section_size) { + return MMDB_CORRUPT_SEARCH_TREE_ERROR; + } + + result->netmask = mmdb->depth - netmask; + result->entry.offset = offset; + result->found_entry = result->entry.offset > 0 ? true : false; + return MMDB_SUCCESS; +} + +LOCAL uint32_t get_left_28_bit_record(const uint8_t *record) +{ + return record[0] * 65536 + record[1] * 256 + record[2] + + ((record[3] & 0xf0) << 20); +} + +LOCAL uint32_t get_right_28_bit_record(const uint8_t *record) +{ + uint32_t value = get_uint32(record); + return value & 0xfffffff; +} + +int MMDB_read_node(MMDB_s *const mmdb, uint32_t node_number, + MMDB_search_node_s *const node) +{ + record_info_s record_info = record_info_for_database(mmdb); + if (0 == record_info.right_record_offset) { + return MMDB_UNKNOWN_DATABASE_FORMAT_ERROR; + } + + if (node_number > mmdb->metadata.node_count) { + return MMDB_INVALID_NODE_NUMBER_ERROR; + } + + const uint8_t *search_tree = mmdb->file_content; + const uint8_t *record_pointer = + &search_tree[node_number * record_info.record_length]; + node->left_record = record_info.left_record_getter(record_pointer); + record_pointer += record_info.right_record_offset; + node->right_record = record_info.right_record_getter(record_pointer); + + return MMDB_SUCCESS; +} + +int MMDB_get_value(MMDB_entry_s *const start, + MMDB_entry_data_s *const entry_data, + ...) +{ + va_list path; + va_start(path, entry_data); + int status = MMDB_vget_value(start, entry_data, path); + va_end(path); + return status; +} + +int MMDB_vget_value(MMDB_entry_s *const start, + MMDB_entry_data_s *const entry_data, + va_list va_path) +{ + const char **path = NULL; + + int i = 0; + const char *path_elem; + while (NULL != (path_elem = va_arg(va_path, char *))) { + path = realloc(path, sizeof(const char *) * (i + 1)); + if (NULL == path) { + return MMDB_OUT_OF_MEMORY_ERROR; + } + + path[i] = mmdb_strdup(path_elem); + if (NULL == path[i]) { + return MMDB_OUT_OF_MEMORY_ERROR; + } + i++; + } + + path = realloc(path, sizeof(char *) * (i + 1)); + if (NULL == path) { + return MMDB_OUT_OF_MEMORY_ERROR; + } + path[i] = NULL; + + int status = MMDB_aget_value(start, entry_data, path); + + i = 0; + while (NULL != path[i]) { + free((void *)path[i]); + i++; + } + free(path); + + return status; +} + +int MMDB_aget_value(MMDB_entry_s *const start, + MMDB_entry_data_s *const entry_data, + const char *const *const path) +{ + MMDB_s *mmdb = start->mmdb; + uint32_t offset = start->offset; + + memset(entry_data, 0, sizeof(MMDB_entry_data_s)); + DEBUG_NL; + DEBUG_MSG("looking up value by path"); + + CHECKED_DECODE_ONE_FOLLOW(mmdb, offset, entry_data); + + DEBUG_NL; + DEBUG_MSGF("top level element is a %s", type_num_to_name(entry_data->type)); + + /* Can this happen? It'd probably represent a pathological case under + * normal use, but there's nothing preventing someone from passing an + * invalid MMDB_entry_s struct to this function */ + if (!entry_data->has_data) { + return MMDB_INVALID_LOOKUP_PATH_ERROR; + } + + const char *path_elem; + int i = 0; + while (NULL != (path_elem = path[i++])) { + DEBUG_NL; + DEBUG_MSGF("path elem = %s", path_elem); + + /* XXX - it'd be good to find a quicker way to skip through these + entries that doesn't involve decoding them + completely. Basically we need to just use the size from the + control byte to advance our pointer rather than calling + decode_one(). */ + if (entry_data->type == MMDB_DATA_TYPE_ARRAY) { + int status = lookup_path_in_array(path_elem, mmdb, entry_data); + if (MMDB_SUCCESS != status) { + memset(entry_data, 0, sizeof(MMDB_entry_data_s)); + return status; + } + } else if (entry_data->type == MMDB_DATA_TYPE_MAP) { + int status = lookup_path_in_map(path_elem, mmdb, entry_data); + if (MMDB_SUCCESS != status) { + memset(entry_data, 0, sizeof(MMDB_entry_data_s)); + return status; + } + } else { + /* Once we make the code traverse maps & arrays without calling + * decode_one() we can get rid of this. */ + memset(entry_data, 0, sizeof(MMDB_entry_data_s)); + return MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR; + } + } + + return MMDB_SUCCESS; +} + +LOCAL int lookup_path_in_array(const char *path_elem, MMDB_s *mmdb, + MMDB_entry_data_s *entry_data) +{ + uint32_t size = entry_data->data_size; + int array_index = strtol(path_elem, NULL, 10); + if (array_index < 0) { + return MMDB_INVALID_LOOKUP_PATH_ERROR; + } + + if ((uint32_t)array_index >= size) { + memset(entry_data, 0, sizeof(MMDB_entry_data_s)); + return MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR; + } + + for (int i = 0; i < array_index; i++) { + /* We don't want to follow a pointer here. If the next element is a + * pointer we simply skip it and keep going */ + CHECKED_DECODE_ONE(mmdb, entry_data->offset_to_next, entry_data); + int status = skip_map_or_array(mmdb, entry_data); + if (MMDB_SUCCESS != status) { + return status; + } + } + + MMDB_entry_data_s value; + CHECKED_DECODE_ONE_FOLLOW(mmdb, entry_data->offset_to_next, &value); + memcpy(entry_data, &value, sizeof(MMDB_entry_data_s)); + + return MMDB_SUCCESS; +} + +LOCAL int lookup_path_in_map(const char *path_elem, MMDB_s *mmdb, + MMDB_entry_data_s *entry_data) +{ + uint32_t size = entry_data->data_size; + uint32_t offset = entry_data->offset_to_next; + size_t path_elem_len = strlen(path_elem); + + while (size-- > 0) { + MMDB_entry_data_s key, value; + CHECKED_DECODE_ONE_FOLLOW(mmdb, offset, &key); + + uint32_t offset_to_value = key.offset_to_next; + + if (MMDB_DATA_TYPE_UTF8_STRING != key.type) { + return MMDB_INVALID_DATA_ERROR; + } + + if (key.data_size == path_elem_len && + !memcmp(path_elem, key.utf8_string, path_elem_len)) { + + DEBUG_MSG("found key matching path elem"); + + CHECKED_DECODE_ONE_FOLLOW(mmdb, offset_to_value, &value); + memcpy(entry_data, &value, sizeof(MMDB_entry_data_s)); + return MMDB_SUCCESS; + } else { + /* We don't want to follow a pointer here. If the next element is + * a pointer we simply skip it and keep going */ + CHECKED_DECODE_ONE(mmdb, offset_to_value, &value); + int status = skip_map_or_array(mmdb, &value); + if (MMDB_SUCCESS != status) { + return status; + } + offset = value.offset_to_next; + } + } + + memset(entry_data, 0, sizeof(MMDB_entry_data_s)); + return MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR; +} + +LOCAL int skip_map_or_array(MMDB_s *mmdb, MMDB_entry_data_s *entry_data) +{ + if (entry_data->type == MMDB_DATA_TYPE_MAP) { + uint32_t size = entry_data->data_size; + while (size-- > 0) { + CHECKED_DECODE_ONE(mmdb, entry_data->offset_to_next, entry_data); // key + CHECKED_DECODE_ONE(mmdb, entry_data->offset_to_next, entry_data); // value + int status = skip_map_or_array(mmdb, entry_data); + if (MMDB_SUCCESS != status) { + return status; + } + } + } else if (entry_data->type == MMDB_DATA_TYPE_ARRAY) { + uint32_t size = entry_data->data_size; + while (size-- > 0) { + CHECKED_DECODE_ONE(mmdb, entry_data->offset_to_next, entry_data); // value + int status = skip_map_or_array(mmdb, entry_data); + if (MMDB_SUCCESS != status) { + return status; + } + } + } + + return MMDB_SUCCESS; +} + +LOCAL int decode_one_follow(MMDB_s *mmdb, uint32_t offset, + MMDB_entry_data_s *entry_data) +{ + CHECKED_DECODE_ONE(mmdb, offset, entry_data); + if (entry_data->type == MMDB_DATA_TYPE_POINTER) { + /* The pointer could point to any part of the data section but the + * next entry for this particular offset may be the one after the + * pointer, not the one after whatever the pointer points to. This + * depends on whether the pointer points to something that is a simple + * value or a compound value. For a compound value, the next one is + * the one after the pointer result, not the one after the pointer. */ + uint32_t next = entry_data->offset_to_next; + CHECKED_DECODE_ONE(mmdb, entry_data->pointer, entry_data); + if (entry_data->type != MMDB_DATA_TYPE_MAP + && entry_data->type != MMDB_DATA_TYPE_ARRAY) { + + entry_data->offset_to_next = next; + } + } + + return MMDB_SUCCESS; +} + +#if !MMDB_UINT128_IS_BYTE_ARRAY +NO_PROTO mmdb_uint128_t get_uint128(const uint8_t *p, int length) +{ + mmdb_uint128_t value = 0; + while (length-- > 0) { + value <<= 8; + value += *p++; + } + return value; +} +#endif + +LOCAL int decode_one(MMDB_s *mmdb, uint32_t offset, + MMDB_entry_data_s *entry_data) +{ + const uint8_t *mem = mmdb->data_section; + + if (offset > mmdb->data_section_size) { + return MMDB_INVALID_DATA_ERROR; + } + + entry_data->offset = offset; + entry_data->has_data = true; + + DEBUG_NL; + DEBUG_MSGF("Offset: %i", offset); + + uint8_t ctrl = mem[offset++]; + DEBUG_BINARY("Control byte: %s", ctrl); + + int type = (ctrl >> 5) & 7; + DEBUG_MSGF("Type: %i (%s)", type, type_num_to_name(type)); + + if (type == MMDB_DATA_TYPE_EXTENDED) { + type = get_ext_type(mem[offset++]); + DEBUG_MSGF("Extended type: %i (%s)", type, type_num_to_name(type)); + } + + entry_data->type = type; + + if (type == MMDB_DATA_TYPE_POINTER) { + int psize = (ctrl >> 3) & 3; + DEBUG_MSGF("Pointer size: %i", psize); + + entry_data->pointer = get_ptr_from(ctrl, &mem[offset], psize); + DEBUG_MSGF("Pointer to: %i", entry_data->pointer); + + entry_data->data_size = psize + 1; + entry_data->offset_to_next = offset + psize + 1; + return MMDB_SUCCESS; + } + + uint32_t size = ctrl & 31; + switch (size) { + case 29: + size = 29 + mem[offset++]; + break; + case 30: + size = 285 + get_uint16(&mem[offset]); + offset += 2; + break; + case 31: + size = 65821 + get_uint24(&mem[offset]); + offset += 3; + default: + break; + } + + DEBUG_MSGF("Size: %i", size); + + if (type == MMDB_DATA_TYPE_MAP || type == MMDB_DATA_TYPE_ARRAY) { + entry_data->data_size = size; + entry_data->offset_to_next = offset; + return MMDB_SUCCESS; + } + + if (type == MMDB_DATA_TYPE_BOOLEAN) { + entry_data->boolean = size ? true : false; + entry_data->data_size = 0; + entry_data->offset_to_next = offset; + DEBUG_MSGF("boolean value: %s", entry_data->boolean ? "true" : "false"); + return MMDB_SUCCESS; + } + + if (type == MMDB_DATA_TYPE_UINT16) { + if (size > 2) { + return MMDB_INVALID_DATA_ERROR; + } + entry_data->uint16 = (uint16_t)get_uintX(&mem[offset], size); + DEBUG_MSGF("uint16 value: %u", entry_data->uint16); + } else if (type == MMDB_DATA_TYPE_UINT32) { + if (size > 4) { + return MMDB_INVALID_DATA_ERROR; + } + entry_data->uint32 = (uint32_t)get_uintX(&mem[offset], size); + DEBUG_MSGF("uint32 value: %u", entry_data->uint32); + } else if (type == MMDB_DATA_TYPE_INT32) { + if (size > 4) { + return MMDB_INVALID_DATA_ERROR; + } + entry_data->int32 = get_sintX(&mem[offset], size); + DEBUG_MSGF("int32 value: %i", entry_data->int32); + } else if (type == MMDB_DATA_TYPE_UINT64) { + if (size > 8) { + return MMDB_INVALID_DATA_ERROR; + } + entry_data->uint64 = get_uintX(&mem[offset], size); + DEBUG_MSGF("uint64 value: %" PRIu64, entry_data->uint64); + } else if (type == MMDB_DATA_TYPE_UINT128) { + if (size > 16) { + return MMDB_INVALID_DATA_ERROR; + } +#if MMDB_UINT128_IS_BYTE_ARRAY + memset(entry_data->uint128, 0, 16); + if (size > 0) { + memcpy(entry_data->uint128 + 16 - size, &mem[offset], size); + } +#else + entry_data->uint128 = get_uint128(&mem[offset], size); +#endif + } else if (type == MMDB_DATA_TYPE_FLOAT) { + if (size != 4) { + return MMDB_INVALID_DATA_ERROR; + } + size = 4; + entry_data->float_value = get_ieee754_float(&mem[offset]); + DEBUG_MSGF("float value: %f", entry_data->float_value); + } else if (type == MMDB_DATA_TYPE_DOUBLE) { + if (size != 8) { + return MMDB_INVALID_DATA_ERROR; + } + size = 8; + entry_data->double_value = get_ieee754_double(&mem[offset]); + DEBUG_MSGF("double value: %f", entry_data->double_value); + } else if (type == MMDB_DATA_TYPE_UTF8_STRING) { + entry_data->utf8_string = size == 0 ? "" : (char *)&mem[offset]; + entry_data->data_size = size; +#ifdef MMDB_DEBUG + char *string = mmdb_strndup(entry_data->utf8_string, + size > 50 ? 50 : size); + if (NULL == string) { + abort(); + } + DEBUG_MSGF("string value: %s", string); + free(string); +#endif + } else if (type == MMDB_DATA_TYPE_BYTES) { + entry_data->bytes = &mem[offset]; + entry_data->data_size = size; + } + + entry_data->offset_to_next = offset + size; + + return MMDB_SUCCESS; +} + +LOCAL int get_ext_type(int raw_ext_type) +{ + return 7 + raw_ext_type; +} + +LOCAL uint32_t get_ptr_from(uint8_t ctrl, uint8_t const *const ptr, + int ptr_size) +{ + uint32_t new_offset; + switch (ptr_size) { + case 0: + new_offset = (ctrl & 7) * 256 + ptr[0]; + break; + case 1: + new_offset = 2048 + (ctrl & 7) * 65536 + ptr[0] * 256 + ptr[1]; + break; + case 2: + new_offset = 2048 + 524288 + (ctrl & 7) * 16777216 + get_uint24(ptr); + break; + case 3: + default: + new_offset = get_uint32(ptr); + break; + } + return MMDB_DATA_SECTION_SEPARATOR + new_offset; +} + +int MMDB_get_metadata_as_entry_data_list( + MMDB_s *const mmdb, MMDB_entry_data_list_s **const entry_data_list) +{ + MMDB_s metadata_db = make_fake_metadata_db(mmdb); + + MMDB_entry_s metadata_start = { + .mmdb = &metadata_db, + .offset = 0 + }; + + return MMDB_get_entry_data_list(&metadata_start, entry_data_list); +} + +int MMDB_get_entry_data_list( + MMDB_entry_s *start, MMDB_entry_data_list_s **const entry_data_list) +{ + *entry_data_list = new_entry_data_list(); + if (NULL == entry_data_list) { + return MMDB_OUT_OF_MEMORY_ERROR; + } + return get_entry_data_list(start->mmdb, start->offset, *entry_data_list); +} + +LOCAL int get_entry_data_list(MMDB_s *mmdb, uint32_t offset, + MMDB_entry_data_list_s *const entry_data_list) +{ + CHECKED_DECODE_ONE(mmdb, offset, &entry_data_list->entry_data); + + switch (entry_data_list->entry_data.type) { + case MMDB_DATA_TYPE_POINTER: + { + uint32_t next_offset = entry_data_list->entry_data.offset_to_next; + uint32_t last_offset; + while (entry_data_list->entry_data.type == + MMDB_DATA_TYPE_POINTER) { + CHECKED_DECODE_ONE(mmdb, last_offset = + entry_data_list->entry_data.pointer, + &entry_data_list->entry_data); + } + + if (entry_data_list->entry_data.type == MMDB_DATA_TYPE_ARRAY + || entry_data_list->entry_data.type == MMDB_DATA_TYPE_MAP) { + + int status = + get_entry_data_list(mmdb, last_offset, entry_data_list); + if (MMDB_SUCCESS != status) { + return status; + } + } + entry_data_list->entry_data.offset_to_next = next_offset; + } + break; + case MMDB_DATA_TYPE_ARRAY: + { + uint32_t array_size = entry_data_list->entry_data.data_size; + uint32_t array_offset = entry_data_list->entry_data.offset_to_next; + MMDB_entry_data_list_s *previous = entry_data_list; + while (array_size-- > 0) { + MMDB_entry_data_list_s *entry_data_list_to = previous->next = + new_entry_data_list(); + if (NULL == entry_data_list_to) { + return MMDB_OUT_OF_MEMORY_ERROR; + } + + int status = + get_entry_data_list(mmdb, array_offset, entry_data_list_to); + if (MMDB_SUCCESS != status) { + return status; + } + + array_offset = entry_data_list_to->entry_data.offset_to_next; + while (previous->next) { + previous = previous->next; + } + } + entry_data_list->entry_data.offset_to_next = array_offset; + + } + break; + case MMDB_DATA_TYPE_MAP: + { + uint32_t size = entry_data_list->entry_data.data_size; + + offset = entry_data_list->entry_data.offset_to_next; + MMDB_entry_data_list_s *previous = entry_data_list; + while (size-- > 0) { + MMDB_entry_data_list_s *entry_data_list_to = previous->next = + new_entry_data_list(); + if (NULL == entry_data_list_to) { + return MMDB_OUT_OF_MEMORY_ERROR; + } + + int status = + get_entry_data_list(mmdb, offset, entry_data_list_to); + if (MMDB_SUCCESS != status) { + return status; + } + + while (previous->next) { + previous = previous->next; + } + + offset = entry_data_list_to->entry_data.offset_to_next; + entry_data_list_to = previous->next = + new_entry_data_list(); + + if (NULL == entry_data_list_to) { + return MMDB_OUT_OF_MEMORY_ERROR; + } + + status = get_entry_data_list(mmdb, offset, entry_data_list_to); + if (MMDB_SUCCESS != status) { + return status; + } + + while (previous->next) { + previous = previous->next; + } + offset = entry_data_list_to->entry_data.offset_to_next; + } + entry_data_list->entry_data.offset_to_next = offset; + } + break; + default: + break; + } + + return MMDB_SUCCESS; +} + +LOCAL float get_ieee754_float(const uint8_t *restrict p) +{ + volatile float f; + uint8_t *q = (void *)&f; +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + q[3] = p[0]; + q[2] = p[1]; + q[1] = p[2]; + q[0] = p[3]; +#else + memcpy(q, p, 4); +#endif + return f; +} + +LOCAL double get_ieee754_double(const uint8_t *restrict p) +{ + volatile double d; + uint8_t *q = (void *)&d; +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + q[7] = p[0]; + q[6] = p[1]; + q[5] = p[2]; + q[4] = p[3]; + q[3] = p[4]; + q[2] = p[5]; + q[1] = p[6]; + q[0] = p[7]; +#else + memcpy(q, p, 8); +#endif + + return d; +} + +LOCAL uint32_t get_uint32(const uint8_t *p) +{ + return p[0] * 16777216U + p[1] * 65536 + p[2] * 256 + p[3]; +} + +LOCAL uint32_t get_uint24(const uint8_t *p) +{ + return p[0] * 65536U + p[1] * 256 + p[2]; +} + +LOCAL uint32_t get_uint16(const uint8_t *p) +{ + return p[0] * 256U + p[1]; +} + +LOCAL uint64_t get_uintX(const uint8_t *p, int length) +{ + uint64_t value = 0; + while (length-- > 0) { + value <<= 8; + value += *p++; + } + return value; +} + +LOCAL int32_t get_sintX(const uint8_t *p, int length) +{ + return (int32_t)get_uintX(p, length); +} + +LOCAL MMDB_entry_data_list_s *new_entry_data_list(void) +{ + /* We need calloc here in order to ensure that the ->next pointer in the + * struct doesn't point to some random address. */ + return calloc(1, sizeof(MMDB_entry_data_list_s)); +} + +void MMDB_free_entry_data_list(MMDB_entry_data_list_s *const entry_data_list) +{ + if (entry_data_list == NULL) { + return; + } + if (entry_data_list->next) { + MMDB_free_entry_data_list(entry_data_list->next); + } + free(entry_data_list); +} + +void MMDB_close(MMDB_s *const mmdb) +{ + free_mmdb_struct(mmdb); +} + +LOCAL void free_mmdb_struct(MMDB_s *const mmdb) +{ + if (!mmdb) { + return; + } + + if (NULL != mmdb->filename) { + free((void *)mmdb->filename); + } + if (NULL != mmdb->file_content) { +#ifdef _WIN32 + UnmapViewOfFile(mmdb->file_content); + /* Winsock is only initialized if open was successful so we only have + * to cleanup then. */ + WSACleanup(); +#else + munmap((void *)mmdb->file_content, mmdb->file_size); +#endif + } + + if (NULL != mmdb->metadata.database_type) { + free((void *)mmdb->metadata.database_type); + } + + free_languages_metadata(mmdb); + free_descriptions_metadata(mmdb); +} + +LOCAL void free_languages_metadata(MMDB_s *mmdb) +{ + if (!mmdb->metadata.languages.count) { + return; + } + + for (size_t i = 0; i < mmdb->metadata.languages.count; i++) { + free((char *)mmdb->metadata.languages.names[i]); + } + free(mmdb->metadata.languages.names); +} + +LOCAL void free_descriptions_metadata(MMDB_s *mmdb) +{ + if (!mmdb->metadata.description.count) { + return; + } + + for (size_t i = 0; i < mmdb->metadata.description.count; i++) { + if (NULL != mmdb->metadata.description.descriptions[i]) { + if (NULL != + mmdb->metadata.description.descriptions[i]->language) { + free( + (char *)mmdb->metadata.description.descriptions[i]-> + language); + } + + if (NULL != + mmdb->metadata.description.descriptions[i]->description) { + free( + (char *)mmdb->metadata.description.descriptions[i]-> + description); + } + free(mmdb->metadata.description.descriptions[i]); + } + } + + free(mmdb->metadata.description.descriptions); +} + +const char *MMDB_lib_version(void) +{ + return PACKAGE_VERSION; +} + +int MMDB_dump_entry_data_list(FILE *const stream, + MMDB_entry_data_list_s *const entry_data_list, + int indent) +{ + int status; + dump_entry_data_list(stream, entry_data_list, indent, &status); + return status; +} + +LOCAL MMDB_entry_data_list_s *dump_entry_data_list( + FILE *stream, MMDB_entry_data_list_s *entry_data_list, int indent, + int *status) +{ + switch (entry_data_list->entry_data.type) { + case MMDB_DATA_TYPE_MAP: + { + uint32_t size = entry_data_list->entry_data.data_size; + + print_indentation(stream, indent); + fprintf(stream, "{\n"); + indent += 2; + + for (entry_data_list = entry_data_list->next; + size && entry_data_list; size--) { + + char *key = + mmdb_strndup( + (char *)entry_data_list->entry_data.utf8_string, + entry_data_list->entry_data.data_size); + if (NULL == key) { + *status = MMDB_OUT_OF_MEMORY_ERROR; + return NULL; + } + + print_indentation(stream, indent); + fprintf(stream, "\"%s\": \n", key); + free(key); + + entry_data_list = entry_data_list->next; + entry_data_list = + dump_entry_data_list(stream, entry_data_list, indent + 2, + status); + + if (MMDB_SUCCESS != *status) { + return NULL; + } + } + + indent -= 2; + print_indentation(stream, indent); + fprintf(stream, "}\n"); + } + break; + case MMDB_DATA_TYPE_ARRAY: + { + uint32_t size = entry_data_list->entry_data.data_size; + + print_indentation(stream, indent); + fprintf(stream, "[\n"); + indent += 2; + + for (entry_data_list = entry_data_list->next; + size && entry_data_list; size--) { + entry_data_list = + dump_entry_data_list(stream, entry_data_list, indent, + status); + if (MMDB_SUCCESS != *status) { + return NULL; + } + } + + indent -= 2; + print_indentation(stream, indent); + fprintf(stream, "]\n"); + } + break; + case MMDB_DATA_TYPE_UTF8_STRING: + { + char *string = + mmdb_strndup((char *)entry_data_list->entry_data.utf8_string, + entry_data_list->entry_data.data_size); + if (NULL == string) { + *status = MMDB_OUT_OF_MEMORY_ERROR; + return NULL; + } + print_indentation(stream, indent); + fprintf(stream, "\"%s\" \n", string); + free(string); + entry_data_list = entry_data_list->next; + } + break; + case MMDB_DATA_TYPE_BYTES: + { + char *hex_string = + bytes_to_hex((uint8_t *)entry_data_list->entry_data.bytes, + entry_data_list->entry_data.data_size); + if (NULL == hex_string) { + *status = MMDB_OUT_OF_MEMORY_ERROR; + return NULL; + } + + print_indentation(stream, indent); + fprintf(stream, "%s \n", hex_string); + free(hex_string); + + entry_data_list = entry_data_list->next; + } + break; + case MMDB_DATA_TYPE_DOUBLE: + print_indentation(stream, indent); + fprintf(stream, "%f \n", + entry_data_list->entry_data.double_value); + entry_data_list = entry_data_list->next; + break; + case MMDB_DATA_TYPE_FLOAT: + print_indentation(stream, indent); + fprintf(stream, "%f \n", + entry_data_list->entry_data.float_value); + entry_data_list = entry_data_list->next; + break; + case MMDB_DATA_TYPE_UINT16: + print_indentation(stream, indent); + fprintf(stream, "%u \n", entry_data_list->entry_data.uint16); + entry_data_list = entry_data_list->next; + break; + case MMDB_DATA_TYPE_UINT32: + print_indentation(stream, indent); + fprintf(stream, "%u \n", entry_data_list->entry_data.uint32); + entry_data_list = entry_data_list->next; + break; + case MMDB_DATA_TYPE_BOOLEAN: + print_indentation(stream, indent); + fprintf(stream, "%s \n", + entry_data_list->entry_data.boolean ? "true" : "false"); + entry_data_list = entry_data_list->next; + break; + case MMDB_DATA_TYPE_UINT64: + print_indentation(stream, indent); + fprintf(stream, "%" PRIu64 " \n", + entry_data_list->entry_data.uint64); + entry_data_list = entry_data_list->next; + break; + case MMDB_DATA_TYPE_UINT128: + print_indentation(stream, indent); +#if MMDB_UINT128_IS_BYTE_ARRAY + char *hex_string = + bytes_to_hex((uint8_t *)entry_data_list->entry_data.uint128, 16); + fprintf(stream, "0x%s \n", hex_string); + free(hex_string); +#else + uint64_t high = entry_data_list->entry_data.uint128 >> 64; + uint64_t low = (uint64_t)entry_data_list->entry_data.uint128; + fprintf(stream, "0x%016" PRIX64 "%016" PRIX64 " \n", high, + low); +#endif + entry_data_list = entry_data_list->next; + break; + case MMDB_DATA_TYPE_INT32: + print_indentation(stream, indent); + fprintf(stream, "%d \n", entry_data_list->entry_data.int32); + entry_data_list = entry_data_list->next; + break; + default: + *status = MMDB_INVALID_DATA_ERROR; + return NULL; + } + + *status = MMDB_SUCCESS; + return entry_data_list; +} + +LOCAL void print_indentation(FILE *stream, int i) +{ + char buffer[1024]; + int size = i >= 1024 ? 1023 : i; + memset(buffer, 32, size); + buffer[size] = '\0'; + fputs(buffer, stream); +} + +LOCAL char *bytes_to_hex(uint8_t *bytes, uint32_t size) +{ + char *hex_string = malloc((size * 2) + 1); + char *hex_pointer = hex_string; + + for (uint32_t i = 0; i < size; i++) { + sprintf(hex_pointer + (2 * i), "%02X", bytes[i]); + } + + return hex_string; +} + +const char *MMDB_strerror(int error_code) +{ + switch (error_code) { + case MMDB_SUCCESS: + return "Success (not an error)"; + case MMDB_FILE_OPEN_ERROR: + return "Error opening the specified MaxMind DB file"; + case MMDB_CORRUPT_SEARCH_TREE_ERROR: + return "The MaxMind DB file's search tree is corrupt"; + case MMDB_INVALID_METADATA_ERROR: + return "The MaxMind DB file contains invalid metadata"; + case MMDB_IO_ERROR: + return "An attempt to read data from the MaxMind DB file failed"; + case MMDB_OUT_OF_MEMORY_ERROR: + return "A memory allocation call failed"; + case MMDB_UNKNOWN_DATABASE_FORMAT_ERROR: + return + "The MaxMind DB file is in a format this library can't handle (unknown record size or binary format version)"; + case MMDB_INVALID_DATA_ERROR: + return + "The MaxMind DB file's data section contains bad data (unknown data type or corrupt data)"; + case MMDB_INVALID_LOOKUP_PATH_ERROR: + return + "The lookup path contained an invalid value (like a negative integer for an array index)"; + case MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR: + return + "The lookup path does not match the data (key that doesn't exist, array index bigger than the array, expected array or map where none exists)"; + case MMDB_INVALID_NODE_NUMBER_ERROR: + return + "The MMDB_read_node function was called with a node number that does not exist in the search tree"; + case MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR: + return + "You attempted to look up an IPv6 address in an IPv4-only database"; + default: + return "Unknown error code"; + } +} diff --git a/dlls/geoip/GeoIP2/maxminddb.h b/dlls/geoip/GeoIP2/maxminddb.h new file mode 100644 index 00000000..8d4318f7 --- /dev/null +++ b/dlls/geoip/GeoIP2/maxminddb.h @@ -0,0 +1,204 @@ +#ifndef MAXMINDDB_H +#define MAXMINDDB_H + +#ifndef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 200112L +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#include +typedef ADDRESS_FAMILY sa_family_t; +#else +#include +#include +#include +#endif + +#define MMDB_DATA_TYPE_EXTENDED (0) +#define MMDB_DATA_TYPE_POINTER (1) +#define MMDB_DATA_TYPE_UTF8_STRING (2) +#define MMDB_DATA_TYPE_DOUBLE (3) +#define MMDB_DATA_TYPE_BYTES (4) +#define MMDB_DATA_TYPE_UINT16 (5) +#define MMDB_DATA_TYPE_UINT32 (6) +#define MMDB_DATA_TYPE_MAP (7) +#define MMDB_DATA_TYPE_INT32 (8) +#define MMDB_DATA_TYPE_UINT64 (9) +#define MMDB_DATA_TYPE_UINT128 (10) +#define MMDB_DATA_TYPE_ARRAY (11) +#define MMDB_DATA_TYPE_CONTAINER (12) +#define MMDB_DATA_TYPE_END_MARKER (13) +#define MMDB_DATA_TYPE_BOOLEAN (14) +#define MMDB_DATA_TYPE_FLOAT (15) + +/* GEOIPDB flags */ +#define MMDB_MODE_MMAP (1) +#define MMDB_MODE_MASK (7) + +/* GEOIPDB err codes */ +#define MMDB_SUCCESS (0) +#define MMDB_FILE_OPEN_ERROR (1) +#define MMDB_CORRUPT_SEARCH_TREE_ERROR (2) +#define MMDB_INVALID_METADATA_ERROR (3) +#define MMDB_IO_ERROR (4) +#define MMDB_OUT_OF_MEMORY_ERROR (5) +#define MMDB_UNKNOWN_DATABASE_FORMAT_ERROR (6) +#define MMDB_INVALID_DATA_ERROR (7) +#define MMDB_INVALID_LOOKUP_PATH_ERROR (8) +#define MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR (9) +#define MMDB_INVALID_NODE_NUMBER_ERROR (10) +#define MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR (11) + +#if !(MMDB_UINT128_IS_BYTE_ARRAY) +#if MMDB_UINT128_USING_MODE +typedef unsigned int mmdb_uint128_t __attribute__ ((__mode__(TI))); +#else +typedef unsigned __int128 mmdb_uint128_t; +#endif +#endif + +/* This is a pointer into the data section for a given IP address lookup */ +typedef struct MMDB_entry_s { + struct MMDB_s *mmdb; + uint32_t offset; +} MMDB_entry_s; + +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; +#if MMDB_UINT128_IS_BYTE_ARRAY + uint8_t uint128[16]; +#else + mmdb_uint128_t uint128; +#endif + bool boolean; + float float_value; + }; + /* This is a 0 if a given entry cannot be found. This can only happen + * when a call to MMDB_(v)get_value() asks for hash keys or array + * indices that don't exist. */ + uint32_t offset; + /* This is the next entry in the data section, but it's really only + * relevant for entries that part of a larger map or array + * struct. There's no good reason for an end user to look at this + * directly. */ + uint32_t offset_to_next; + /* This is only valid for strings, utf8_strings or binary data */ + uint32_t data_size; + /* This is an MMDB_DATA_TYPE_* constant */ + uint32_t type; +} MMDB_entry_data_s; + +/* This is the return type when someone asks for all the entry data in a map or array */ +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; + +typedef struct MMDB_description_s { + const char *language; + const char *description; +} MMDB_description_s; + +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_ipv4_start_node_s { + uint16_t netmask; + uint32_t node_value; +} MMDB_ipv4_start_node_s; + +typedef struct MMDB_s { + uint32_t flags; + const char *filename; + ssize_t file_size; + const uint8_t *file_content; + const uint8_t *data_section; + uint32_t data_section_size; + const uint8_t *metadata_section; + uint32_t metadata_section_size; + uint16_t full_record_byte_size; + uint16_t depth; + MMDB_ipv4_start_node_s ipv4_start_node; + MMDB_metadata_s metadata; +} MMDB_s; + +typedef struct MMDB_search_node_s { + uint64_t left_record; + uint64_t right_record; +} MMDB_search_node_s; + + /* *INDENT-OFF* */ + /* --prototypes automatically generated by dev-bin/regen-prototypes.pl - don't remove this comment */ + extern int MMDB_open(const char *const filename, uint32_t flags, MMDB_s *const mmdb); + extern MMDB_lookup_result_s MMDB_lookup_string(MMDB_s *const mmdb, + const char *const ipstr, + int *const gai_error, + int *const mmdb_error); + extern MMDB_lookup_result_s MMDB_lookup_sockaddr( + MMDB_s *const mmdb, + const struct sockaddr *const sockaddr, + int *const mmdb_error); + extern int MMDB_read_node(MMDB_s *const mmdb, uint32_t node_number, + MMDB_search_node_s *const node); + extern int MMDB_get_value(MMDB_entry_s *const start, + MMDB_entry_data_s *const entry_data, + ...); + extern int MMDB_vget_value(MMDB_entry_s *const start, + MMDB_entry_data_s *const entry_data, + va_list va_path); + extern int MMDB_aget_value(MMDB_entry_s *const start, + MMDB_entry_data_s *const entry_data, + const char *const *const path); + extern int MMDB_get_metadata_as_entry_data_list( + MMDB_s *const mmdb, MMDB_entry_data_list_s **const entry_data_list); + extern int MMDB_get_entry_data_list( + MMDB_entry_s *start, MMDB_entry_data_list_s **const entry_data_list); + extern void MMDB_free_entry_data_list(MMDB_entry_data_list_s *const entry_data_list); + extern void MMDB_close(MMDB_s *const mmdb); + extern const char *MMDB_lib_version(void); + extern int MMDB_dump_entry_data_list(FILE *const stream, + MMDB_entry_data_list_s *const entry_data_list, + int indent); + extern const char *MMDB_strerror(int error_code); + /* --prototypes end - don't remove this comment-- */ + /* *INDENT-ON* */ + +#endif /* MAXMINDDB_H */ diff --git a/dlls/geoip/GeoIP2/maxminddb_config.h b/dlls/geoip/GeoIP2/maxminddb_config.h new file mode 100644 index 00000000..4c9f70eb --- /dev/null +++ b/dlls/geoip/GeoIP2/maxminddb_config.h @@ -0,0 +1,16 @@ +#ifndef MAXMINDDB_CONFIG_H +#define MAXMINDDB_CONFIG_H + +#define PACKAGE_VERSION "0.5.6" + +#ifndef MMDB_UINT128_USING_MODE +/* Define as 1 if we we use unsigned int __atribute__ ((__mode__(TI))) for uint128 values */ +#define MMDB_UINT128_USING_MODE 0 +#endif + +#ifndef MMDB_UINT128_IS_BYTE_ARRAY +/* Define as 1 if we don't have an unsigned __int128 type */ +#define MMDB_UINT128_IS_BYTE_ARRAY 1 +#endif + +#endif /* MAXMINDDB_CONFIG_H */