diff --git a/.gitmodules b/.gitmodules index fde0c20f8..0a9fa2ef9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,9 @@ -[submodule "public/amtl"] - path = public/amtl - url = https://github.com/alliedmodders/amtl -[submodule "sourcepawn"] - path = sourcepawn - url = https://github.com/alliedmodders/sourcepawn +[submodule "public/amtl"] + path = public/amtl + url = https://github.com/alliedmodders/amtl +[submodule "sourcepawn"] + path = sourcepawn + url = https://github.com/alliedmodders/sourcepawn +[submodule "extensions/geoip/libmaxminddb"] + path = extensions/geoip/libmaxminddb + url = https://github.com/maxmind/libmaxminddb.git diff --git a/extensions/geoip/libmaxminddb b/extensions/geoip/libmaxminddb new file mode 160000 index 000000000..9660f7e14 --- /dev/null +++ b/extensions/geoip/libmaxminddb @@ -0,0 +1 @@ +Subproject commit 9660f7e14492c31c52b4579ce88136aae4526f23 diff --git a/extensions/geoip/libmaxminddb/include/maxminddb.h b/extensions/geoip/libmaxminddb/include/maxminddb.h deleted file mode 100644 index de1fdf86f..000000000 --- a/extensions/geoip/libmaxminddb/include/maxminddb.h +++ /dev/null @@ -1,241 +0,0 @@ -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef MAXMINDDB_H -#define MAXMINDDB_H - -/* Request POSIX.1-2008. However, we want to remain compatible with - * POSIX.1-2001 (since we have been historically and see no reason to drop - * compatibility). By requesting POSIX.1-2008, we can conditionally use - * features provided by that standard if the implementation provides it. We can - * check for what the implementation provides by checking the _POSIX_VERSION - * macro after including unistd.h. If a feature is in POSIX.1-2008 but not - * POSIX.1-2001, check that macro before using the feature (or check for the - * feature directly if possible). */ -#ifndef _POSIX_C_SOURCE -#define _POSIX_C_SOURCE 200809L -#endif - -#include "maxminddb_config.h" -#include -#include -#include -#include -#include - -#ifdef _WIN32 -#include -#include -/* libmaxminddb package version from configure */ -#define PACKAGE_VERSION "1.3.2" - -typedef ADDRESS_FAMILY sa_family_t; - -#if defined(_MSC_VER) -/* MSVC doesn't define signed size_t, copy it from configure */ -#define ssize_t SSIZE_T - -/* MSVC doesn't support restricted pointers */ -#define restrict -#endif -#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) - -#define MMDB_RECORD_TYPE_SEARCH_NODE (0) -#define MMDB_RECORD_TYPE_EMPTY (1) -#define MMDB_RECORD_TYPE_DATA (2) -#define MMDB_RECORD_TYPE_INVALID (3) - -/* flags for open */ -#define MMDB_MODE_MMAP (1) -#define MMDB_MODE_MASK (7) - -/* error 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; - void *pool; -} 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; - 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; - - /* *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 */ - -#ifdef __cplusplus -} -#endif diff --git a/extensions/geoip/libmaxminddb/include/maxminddb_config.h b/extensions/geoip/libmaxminddb/include/maxminddb_config.h deleted file mode 100644 index b76f12f97..000000000 --- a/extensions/geoip/libmaxminddb/include/maxminddb_config.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef MAXMINDDB_CONFIG_H -#define MAXMINDDB_CONFIG_H - -#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 */ diff --git a/extensions/geoip/libmaxminddb/src/data-pool.c b/extensions/geoip/libmaxminddb/src/data-pool.c deleted file mode 100644 index 48521b64d..000000000 --- a/extensions/geoip/libmaxminddb/src/data-pool.c +++ /dev/null @@ -1,180 +0,0 @@ -#include "data-pool.h" -#include "maxminddb.h" - -#include -#include -#include - -static bool can_multiply(size_t const, size_t const, size_t const); - -// Allocate an MMDB_data_pool_s. It initially has space for size -// MMDB_entry_data_list_s structs. -MMDB_data_pool_s *data_pool_new(size_t const size) -{ - MMDB_data_pool_s *const pool = calloc(1, sizeof(MMDB_data_pool_s)); - if (!pool) { - return NULL; - } - - if (size == 0 || - !can_multiply(SIZE_MAX, size, sizeof(MMDB_entry_data_list_s))) { - data_pool_destroy(pool); - return NULL; - } - pool->size = size; - pool->blocks[0] = calloc(pool->size, sizeof(MMDB_entry_data_list_s)); - if (!pool->blocks[0]) { - data_pool_destroy(pool); - return NULL; - } - pool->blocks[0]->pool = pool; - - pool->sizes[0] = size; - - pool->block = pool->blocks[0]; - - return pool; -} - -// Determine if we can multiply m*n. We can do this if the result will be below -// the given max. max will typically be SIZE_MAX. -// -// We want to know if we'll wrap around. -static bool can_multiply(size_t const max, size_t const m, size_t const n) -{ - if (m == 0) { - return false; - } - - return n <= max / m; -} - -// Clean up the data pool. -void data_pool_destroy(MMDB_data_pool_s *const pool) -{ - if (!pool) { - return; - } - - for (size_t i = 0; i <= pool->index; i++) { - free(pool->blocks[i]); - } - - free(pool); -} - -// Claim a new struct from the pool. Doing this may cause the pool's size to -// grow. -MMDB_entry_data_list_s *data_pool_alloc(MMDB_data_pool_s *const pool) -{ - if (!pool) { - return NULL; - } - - if (pool->used < pool->size) { - MMDB_entry_data_list_s *const element = pool->block + pool->used; - pool->used++; - return element; - } - - // Take it from a new block of memory. - - size_t const new_index = pool->index + 1; - if (new_index == DATA_POOL_NUM_BLOCKS) { - // See the comment about not growing this on DATA_POOL_NUM_BLOCKS. - return NULL; - } - - if (!can_multiply(SIZE_MAX, pool->size, 2)) { - return NULL; - } - size_t const new_size = pool->size * 2; - - if (!can_multiply(SIZE_MAX, new_size, sizeof(MMDB_entry_data_list_s))) { - return NULL; - } - pool->blocks[new_index] = calloc(new_size, sizeof(MMDB_entry_data_list_s)); - if (!pool->blocks[new_index]) { - return NULL; - } - - // We don't need to set this, but it's useful for introspection in tests. - pool->blocks[new_index]->pool = pool; - - pool->index = new_index; - pool->block = pool->blocks[pool->index]; - - pool->size = new_size; - pool->sizes[pool->index] = pool->size; - - MMDB_entry_data_list_s *const element = pool->block; - pool->used = 1; - return element; -} - -// Turn the structs in the array-like pool into a linked list. -// -// Before calling this function, the list isn't linked up. -MMDB_entry_data_list_s *data_pool_to_list(MMDB_data_pool_s *const pool) -{ - if (!pool) { - return NULL; - } - - if (pool->index == 0 && pool->used == 0) { - return NULL; - } - - for (size_t i = 0; i <= pool->index; i++) { - MMDB_entry_data_list_s *const block = pool->blocks[i]; - - size_t size = pool->sizes[i]; - if (i == pool->index) { - size = pool->used; - } - - for (size_t j = 0; j < size - 1; j++) { - MMDB_entry_data_list_s *const cur = block + j; - cur->next = block + j + 1; - } - - if (i < pool->index) { - MMDB_entry_data_list_s *const last = block + size - 1; - last->next = pool->blocks[i + 1]; - } - } - - return pool->blocks[0]; -} - -#ifdef TEST_DATA_POOL - -#include -#include - -static void test_can_multiply(void); - -int main(void) -{ - plan(NO_PLAN); - test_can_multiply(); - done_testing(); -} - -static void test_can_multiply(void) -{ - { - ok(can_multiply(SIZE_MAX, 1, SIZE_MAX), "1*SIZE_MAX is ok"); - } - - { - ok(!can_multiply(SIZE_MAX, 2, SIZE_MAX), "2*SIZE_MAX is not ok"); - } - - { - ok(can_multiply(SIZE_MAX, 10240, sizeof(MMDB_entry_data_list_s)), - "1024 entry_data_list_s's are okay"); - } -} - -#endif diff --git a/extensions/geoip/libmaxminddb/src/data-pool.h b/extensions/geoip/libmaxminddb/src/data-pool.h deleted file mode 100644 index 25d09923e..000000000 --- a/extensions/geoip/libmaxminddb/src/data-pool.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef DATA_POOL_H -#define DATA_POOL_H - -#include "maxminddb.h" - -#include -#include - -// This should be large enough that we never need to grow the array of pointers -// to blocks. 32 is enough. Even starting out of with size 1 (1 struct), the -// 32nd element alone will provide 2**32 structs as we exponentially increase -// the number in each block. Being confident that we do not have to grow the -// array lets us avoid writing code to do that. That code would be risky as it -// would rarely be hit and likely not be well tested. -#define DATA_POOL_NUM_BLOCKS 32 - -// A pool of memory for MMDB_entry_data_list_s structs. This is so we can -// allocate multiple up front rather than one at a time for performance -// reasons. -// -// The order you add elements to it (by calling data_pool_alloc()) ends up as -// the order of the list. -// -// The memory only grows. There is no support for releasing an element you take -// back to the pool. -typedef struct MMDB_data_pool_s { - // Index of the current block we're allocating out of. - size_t index; - - // The size of the current block, counting by structs. - size_t size; - - // How many used in the current block, counting by structs. - size_t used; - - // The current block we're allocating out of. - MMDB_entry_data_list_s *block; - - // The size of each block. - size_t sizes[DATA_POOL_NUM_BLOCKS]; - - // An array of pointers to blocks of memory holding space for list - // elements. - MMDB_entry_data_list_s *blocks[DATA_POOL_NUM_BLOCKS]; -} MMDB_data_pool_s; - -MMDB_data_pool_s *data_pool_new(size_t const); -void data_pool_destroy(MMDB_data_pool_s *const); -MMDB_entry_data_list_s *data_pool_alloc(MMDB_data_pool_s *const); -MMDB_entry_data_list_s *data_pool_to_list(MMDB_data_pool_s *const); - -#endif diff --git a/extensions/geoip/libmaxminddb/src/maxminddb-compat-util.h b/extensions/geoip/libmaxminddb/src/maxminddb-compat-util.h deleted file mode 100644 index 96e443fa2..000000000 --- a/extensions/geoip/libmaxminddb/src/maxminddb-compat-util.h +++ /dev/null @@ -1,167 +0,0 @@ -#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/extensions/geoip/libmaxminddb/src/maxminddb.c b/extensions/geoip/libmaxminddb/src/maxminddb.c deleted file mode 100644 index b13196e40..000000000 --- a/extensions/geoip/libmaxminddb/src/maxminddb.c +++ /dev/null @@ -1,2182 +0,0 @@ -#if HAVE_CONFIG_H -#include -#endif -#include "data-pool.h" -#include "maxminddb.h" -#include "maxminddb-compat-util.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef _WIN32 -#include -#include -#else -#include -#include -#include -#endif - -#define MMDB_DATA_SECTION_SEPARATOR (16) -#define MAXIMUM_DATA_STRUCTURE_DEPTH (512) - -#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 - -/* None of the values we check on the lhs are bigger than uint32_t, so on - * platforms where SIZE_MAX is a 64-bit integer, this would be a no-op, and it - * makes the compiler complain if we do the check anyway. */ -#if SIZE_MAX == UINT32_MAX -#define MAYBE_CHECK_SIZE_OVERFLOW(lhs, rhs, error) \ - if ((lhs) > (rhs)) { \ - return error; \ - } -#else -#define MAYBE_CHECK_SIZE_OVERFLOW(...) -#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 - -// 64 leads us to allocating 4 KiB on a 64bit system. -#define MMDB_POOL_INIT_SIZE 64 - -/* *INDENT-OFF* */ -/* --prototypes automatically generated by dev-bin/regen-prototypes.pl - don't remove this comment */ -LOCAL int map_file(MMDB_s *const mmdb); -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 int value_for_key_as_uint16(MMDB_entry_s *start, char *key, - uint16_t *value); -LOCAL int value_for_key_as_uint32(MMDB_entry_s *start, char *key, - uint32_t *value); -LOCAL int value_for_key_as_uint64(MMDB_entry_s *start, char *key, - uint64_t *value); -LOCAL int value_for_key_as_string(MMDB_entry_s *start, char *key, - char const **value); -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 int find_ipv4_start_node(MMDB_s *mmdb); -LOCAL uint8_t maybe_populate_result(MMDB_s *mmdb, uint32_t record, - uint16_t netmask, - MMDB_lookup_result_s *result); -LOCAL uint8_t record_type(MMDB_s *const mmdb, uint64_t record); -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 uint32_t data_section_offset_for_record(MMDB_s *const mmdb, - uint64_t record); -LOCAL int path_length(va_list va_path); -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, - MMDB_data_pool_s *const pool, - int depth); -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 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) { \ - DEBUG_MSGF("CHECKED_DECODE_ONE failed." \ - " status = %d (%s)", status, MMDB_strerror(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) { \ - DEBUG_MSGF("CHECKED_DECODE_ONE_FOLLOW failed." \ - " status = %d (%s)", status, MMDB_strerror(status)); \ - return status; \ - } \ - } while (0) - -#define FREE_AND_SET_NULL(p) { free((void *)(p)); (p) = NULL; } - -int MMDB_open(const char *const filename, uint32_t flags, MMDB_s *const mmdb) -{ - int status = MMDB_SUCCESS; - - mmdb->file_content = NULL; - mmdb->data_section = NULL; - mmdb->metadata.database_type = NULL; - mmdb->metadata.languages.count = 0; - mmdb->metadata.languages.names = NULL; - mmdb->metadata.description.count = 0; - - mmdb->filename = mmdb_strdup(filename); - if (NULL == mmdb->filename) { - status = MMDB_OUT_OF_MEMORY_ERROR; - goto cleanup; - } - - if ((flags & MMDB_MODE_MASK) == 0) { - flags |= MMDB_MODE_MMAP; - } - mmdb->flags = flags; - - if (MMDB_SUCCESS != (status = map_file(mmdb))) { - goto cleanup; - } - -#ifdef _WIN32 - WSADATA wsa; - WSAStartup(MAKEWORD(2, 2), &wsa); -#endif - - uint32_t metadata_size = 0; - const uint8_t *metadata = find_metadata(mmdb->file_content, mmdb->file_size, - &metadata_size); - if (NULL == metadata) { - status = MMDB_INVALID_METADATA_ERROR; - goto cleanup; - } - - mmdb->metadata_section = metadata; - mmdb->metadata_section_size = metadata_size; - - status = read_metadata(mmdb); - if (MMDB_SUCCESS != status) { - goto cleanup; - } - - if (mmdb->metadata.binary_format_major_version != 2) { - status = MMDB_UNKNOWN_DATABASE_FORMAT_ERROR; - goto cleanup; - } - - uint32_t search_tree_size = mmdb->metadata.node_count * - mmdb->full_record_byte_size; - - mmdb->data_section = mmdb->file_content + search_tree_size - + MMDB_DATA_SECTION_SEPARATOR; - if (search_tree_size + MMDB_DATA_SECTION_SEPARATOR > - (uint32_t)mmdb->file_size) { - status = MMDB_INVALID_METADATA_ERROR; - goto cleanup; - } - mmdb->data_section_size = (uint32_t)mmdb->file_size - search_tree_size - - MMDB_DATA_SECTION_SEPARATOR; - - // Although it is likely not possible to construct a database with valid - // valid metadata, as parsed above, and a data_section_size less than 3, - // we do this check as later we assume it is at least three when doing - // bound checks. - if (mmdb->data_section_size < 3) { - status = MMDB_INVALID_DATA_ERROR; - goto cleanup; - } - - mmdb->metadata_section = metadata; - mmdb->ipv4_start_node.node_value = 0; - mmdb->ipv4_start_node.netmask = 0; - - // We do this immediately as otherwise there is a race to set - // ipv4_start_node.node_value and ipv4_start_node.netmask. - if (mmdb->metadata.ip_version == 6) { - status = find_ipv4_start_node(mmdb); - if (status != MMDB_SUCCESS) { - goto cleanup; - } - } - - cleanup: - if (MMDB_SUCCESS != status) { - int saved_errno = errno; - free_mmdb_struct(mmdb); - errno = saved_errno; - } - return status; -} - -#ifdef _WIN32 - -LOCAL int map_file(MMDB_s *const mmdb) -{ - DWORD size; - int status = MMDB_SUCCESS; - HANDLE mmh = NULL; - HANDLE fd = CreateFileA(mmdb->filename, GENERIC_READ, FILE_SHARE_READ, NULL, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (fd == INVALID_HANDLE_VALUE) { - status = MMDB_FILE_OPEN_ERROR; - goto cleanup; - } - size = GetFileSize(fd, NULL); - if (size == INVALID_FILE_SIZE) { - status = MMDB_FILE_OPEN_ERROR; - goto cleanup; - } - mmh = CreateFileMappingA(fd, NULL, PAGE_READONLY, 0, size, NULL); - /* Microsoft documentation for CreateFileMapping indicates this returns - NULL not INVALID_HANDLE_VALUE on error */ - if (NULL == mmh) { - status = MMDB_IO_ERROR; - goto cleanup; - } - uint8_t *file_content = - (uint8_t *)MapViewOfFile(mmh, FILE_MAP_READ, 0, 0, 0); - if (file_content == NULL) { - status = MMDB_IO_ERROR; - goto cleanup; - } - - mmdb->file_size = size; - mmdb->file_content = file_content; - - cleanup:; - int saved_errno = errno; - if (INVALID_HANDLE_VALUE != fd) { - CloseHandle(fd); - } - if (NULL != mmh) { - CloseHandle(mmh); - } - errno = saved_errno; - - return status; -} - -#else - -LOCAL int map_file(MMDB_s *const mmdb) -{ - ssize_t size; - int status = MMDB_SUCCESS; - - int flags = O_RDONLY; -#ifdef O_CLOEXEC - flags |= O_CLOEXEC; -#endif - int fd = open(mmdb->filename, flags); - struct stat s; - if (fd < 0 || fstat(fd, &s)) { - status = MMDB_FILE_OPEN_ERROR; - goto cleanup; - } - - size = s.st_size; - if (size < 0 || size != s.st_size) { - status = MMDB_OUT_OF_MEMORY_ERROR; - goto cleanup; - } - - uint8_t *file_content = - (uint8_t *)mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); - if (MAP_FAILED == file_content) { - if (ENOMEM == errno) { - status = MMDB_OUT_OF_MEMORY_ERROR; - } else { - status = MMDB_IO_ERROR; - } - goto cleanup; - } - - mmdb->file_size = size; - mmdb->file_content = file_content; - - cleanup:; - int saved_errno = errno; - if (fd >= 0) { - close(fd); - } - errno = saved_errno; - - return status; -} - -#endif - -LOCAL const uint8_t *find_metadata(const uint8_t *file_content, - ssize_t file_size, uint32_t *metadata_size) -{ - const ssize_t marker_len = sizeof(METADATA_MARKER) - 1; - 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 *start = search_area; - uint8_t *tmp; - do { - tmp = mmdb_memmem(search_area, max_size, - METADATA_MARKER, marker_len); - - if (NULL != tmp) { - max_size -= tmp - search_area; - search_area = tmp; - - /* Continue searching just after the marker we just read, in case - * there are multiple markers in the same file. This would be odd - * but is certainly not impossible. */ - max_size -= marker_len; - search_area += marker_len; - } - } while (NULL != tmp); - - if (search_area == start) { - return NULL; - } - - *metadata_size = (uint32_t)max_size; - - return search_area; -} - -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 - }; - - int status = - value_for_key_as_uint32(&metadata_start, "node_count", - &mmdb->metadata.node_count); - if (MMDB_SUCCESS != status) { - return status; - } - if (!mmdb->metadata.node_count) { - DEBUG_MSG("could not find node_count value in metadata"); - return MMDB_INVALID_METADATA_ERROR; - } - - status = value_for_key_as_uint16(&metadata_start, "record_size", - &mmdb->metadata.record_size); - if (MMDB_SUCCESS != status) { - return status; - } - 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; - } - - status = value_for_key_as_uint16(&metadata_start, "ip_version", - &mmdb->metadata.ip_version); - if (MMDB_SUCCESS != status) { - return status; - } - 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; - } - - status = value_for_key_as_string(&metadata_start, "database_type", - &mmdb->metadata.database_type); - if (MMDB_SUCCESS != status) { - DEBUG_MSG("error finding database_type value in metadata"); - return status; - } - - status = - populate_languages_metadata(mmdb, &metadata_db, &metadata_start); - if (MMDB_SUCCESS != status) { - DEBUG_MSG("could not populate languages from metadata"); - return status; - } - - status = value_for_key_as_uint16( - &metadata_start, "binary_format_major_version", - &mmdb->metadata.binary_format_major_version); - if (MMDB_SUCCESS != status) { - return status; - } - if (!mmdb->metadata.binary_format_major_version) { - DEBUG_MSG( - "could not find binary_format_major_version value in metadata"); - return MMDB_INVALID_METADATA_ERROR; - } - - status = value_for_key_as_uint16( - &metadata_start, "binary_format_minor_version", - &mmdb->metadata.binary_format_minor_version); - if (MMDB_SUCCESS != status) { - return status; - } - - status = value_for_key_as_uint64(&metadata_start, "build_epoch", - &mmdb->metadata.build_epoch); - if (MMDB_SUCCESS != status) { - return status; - } - 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 int value_for_key_as_uint16(MMDB_entry_s *start, char *key, - uint16_t *value) -{ - MMDB_entry_data_s entry_data; - const char *path[] = { key, NULL }; - int status = MMDB_aget_value(start, &entry_data, path); - if (MMDB_SUCCESS != status) { - return status; - } - if (MMDB_DATA_TYPE_UINT16 != entry_data.type) { - DEBUG_MSGF("expect uint16 for %s but received %s", key, - type_num_to_name( - entry_data.type)); - return MMDB_INVALID_METADATA_ERROR; - } - *value = entry_data.uint16; - return MMDB_SUCCESS; -} - -LOCAL int value_for_key_as_uint32(MMDB_entry_s *start, char *key, - uint32_t *value) -{ - MMDB_entry_data_s entry_data; - const char *path[] = { key, NULL }; - int status = MMDB_aget_value(start, &entry_data, path); - if (MMDB_SUCCESS != status) { - return status; - } - if (MMDB_DATA_TYPE_UINT32 != entry_data.type) { - DEBUG_MSGF("expect uint32 for %s but received %s", key, - type_num_to_name( - entry_data.type)); - return MMDB_INVALID_METADATA_ERROR; - } - *value = entry_data.uint32; - return MMDB_SUCCESS; -} - -LOCAL int value_for_key_as_uint64(MMDB_entry_s *start, char *key, - uint64_t *value) -{ - MMDB_entry_data_s entry_data; - const char *path[] = { key, NULL }; - int status = MMDB_aget_value(start, &entry_data, path); - if (MMDB_SUCCESS != status) { - return status; - } - if (MMDB_DATA_TYPE_UINT64 != entry_data.type) { - DEBUG_MSGF("expect uint64 for %s but received %s", key, - type_num_to_name( - entry_data.type)); - return MMDB_INVALID_METADATA_ERROR; - } - *value = entry_data.uint64; - return MMDB_SUCCESS; -} - -LOCAL int value_for_key_as_string(MMDB_entry_s *start, char *key, - char const **value) -{ - MMDB_entry_data_s entry_data; - const char *path[] = { key, NULL }; - int status = MMDB_aget_value(start, &entry_data, path); - if (MMDB_SUCCESS != status) { - return status; - } - if (MMDB_DATA_TYPE_UTF8_STRING != entry_data.type) { - DEBUG_MSGF("expect string for %s but received %s", key, - type_num_to_name( - entry_data.type)); - return MMDB_INVALID_METADATA_ERROR; - } - *value = mmdb_strndup((char *)entry_data.utf8_string, entry_data.data_size); - if (NULL == *value) { - return MMDB_OUT_OF_MEMORY_ERROR; - } - return MMDB_SUCCESS; -} - -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 }; - int status = MMDB_aget_value(metadata_start, &entry_data, path); - if (MMDB_SUCCESS != status) { - return status; - } - 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; - status = MMDB_get_entry_data_list(&array_start, &member); - if (MMDB_SUCCESS != status) { - return status; - } - - MMDB_entry_data_list_s *first_member = member; - - uint32_t array_size = member->entry_data.data_size; - MAYBE_CHECK_SIZE_OVERFLOW(array_size, SIZE_MAX / sizeof(char *), - MMDB_INVALID_METADATA_ERROR); - - 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 }; - int status = MMDB_aget_value(metadata_start, &entry_data, path); - if (MMDB_SUCCESS != status) { - return status; - } - - if (MMDB_DATA_TYPE_MAP != entry_data.type) { - DEBUG_MSGF("Unexpected entry_data type: %d", 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; - status = MMDB_get_entry_data_list(&map_start, &member); - if (MMDB_SUCCESS != status) { - DEBUG_MSGF( - "MMDB_get_entry_data_list failed while populating description." - " status = %d (%s)", status, MMDB_strerror(status)); - return status; - } - - MMDB_entry_data_list_s *first_member = member; - - uint32_t map_size = member->entry_data.data_size; - mmdb->metadata.description.count = 0; - if (0 == map_size) { - mmdb->metadata.description.descriptions = NULL; - goto cleanup; - } - MAYBE_CHECK_SIZE_OVERFLOW(map_size, SIZE_MAX / sizeof(MMDB_description_s *), - MMDB_INVALID_METADATA_ERROR); - - mmdb->metadata.description.descriptions = - malloc(map_size * sizeof(MMDB_description_s *)); - if (NULL == mmdb->metadata.description.descriptions) { - status = MMDB_OUT_OF_MEMORY_ERROR; - goto cleanup; - } - - 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]) { - status = MMDB_OUT_OF_MEMORY_ERROR; - goto cleanup; - } - - 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) { - status = MMDB_INVALID_METADATA_ERROR; - goto cleanup; - } - - 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) { - status = MMDB_OUT_OF_MEMORY_ERROR; - goto cleanup; - } - - member = member->next; - - if (MMDB_DATA_TYPE_UTF8_STRING != member->entry_data.type) { - status = MMDB_INVALID_METADATA_ERROR; - goto cleanup; - } - - 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) { - status = MMDB_OUT_OF_MEMORY_ERROR; - goto cleanup; - } - } - - cleanup: - MMDB_free_entry_data_list(first_member); - - return status; -} - -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) { - result = MMDB_lookup_sockaddr(mmdb, addresses->ai_addr, mmdb_error); - } - - if (NULL != addresses) { - freeaddrinfo(addresses); - } - - return result; -} - -LOCAL int resolve_any_address(const char *ipstr, struct addrinfo **addresses) -{ - struct addrinfo hints = { - .ai_family = AF_UNSPEC, - .ai_flags = AI_NUMERICHOST, - // We set ai_socktype so that we only get one result back - .ai_socktype = SOCK_STREAM - }; - - int 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) { - *mmdb_error = MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR; - 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 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) { - int mmdb_error = find_ipv4_start_node(mmdb); - if (MMDB_SUCCESS != mmdb_error) { - return mmdb_error; - } - DEBUG_MSGF("IPv4 start node is %u (netmask %u)", - mmdb->ipv4_start_node.node_value, - mmdb->ipv4_start_node.netmask); - - uint8_t type = maybe_populate_result(mmdb, - mmdb->ipv4_start_node.node_value, - mmdb->ipv4_start_node.netmask, - result); - if (MMDB_RECORD_TYPE_INVALID == type) { - return MMDB_CORRUPT_SEARCH_TREE_ERROR; - } - - /* We have an IPv6 database with no IPv4 data */ - if (MMDB_RECORD_TYPE_SEARCH_NODE != type) { - return MMDB_SUCCESS; - } - - value = mmdb->ipv4_start_node.node_value; - start_bit -= mmdb->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 (record_pointer + record_info.record_length > mmdb->data_section) { - return MMDB_CORRUPT_SEARCH_TREE_ERROR; - } - 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); - } - - uint8_t type = maybe_populate_result(mmdb, value, (uint16_t)current_bit, - result); - if (MMDB_RECORD_TYPE_INVALID == type) { - return MMDB_CORRUPT_SEARCH_TREE_ERROR; - } - - if (MMDB_RECORD_TYPE_SEARCH_NODE != type) { - return MMDB_SUCCESS; - } - - 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; - } else { - assert(false); - } - - return record_info; -} - -LOCAL int 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_SUCCESS; - } - - 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; - uint16_t netmask; - for (netmask = 0; netmask < 96; netmask++) { - record_pointer = &search_tree[node_value * record_info.record_length]; - if (record_pointer + record_info.record_length > mmdb->data_section) { - return MMDB_CORRUPT_SEARCH_TREE_ERROR; - } - 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_SUCCESS; -} - -LOCAL uint8_t maybe_populate_result(MMDB_s *mmdb, uint32_t record, - uint16_t netmask, - MMDB_lookup_result_s *result) -{ - uint8_t type = record_type(mmdb, record); - - if (MMDB_RECORD_TYPE_SEARCH_NODE == type || - MMDB_RECORD_TYPE_INVALID == type) { - return type; - } - - result->netmask = mmdb->depth - netmask; - - result->entry.offset = data_section_offset_for_record(mmdb, record); - - // type is either MMDB_RECORD_TYPE_DATA or MMDB_RECORD_TYPE_EMPTY - // at this point - result->found_entry = MMDB_RECORD_TYPE_DATA == type; - - return type; -} - -LOCAL uint8_t record_type(MMDB_s *const mmdb, uint64_t record) -{ - uint32_t node_count = mmdb->metadata.node_count; - - /* 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 (record == 0) { - DEBUG_MSG("record has a value of 0"); - return MMDB_RECORD_TYPE_INVALID; - } - - if (record < node_count) { - return MMDB_RECORD_TYPE_SEARCH_NODE; - } - - if (record == node_count) { - return MMDB_RECORD_TYPE_EMPTY; - } - - if (record - node_count < mmdb->data_section_size) { - return MMDB_RECORD_TYPE_DATA; - } - - DEBUG_MSG("record has a value that points outside of the database"); - return MMDB_RECORD_TYPE_INVALID; -} - -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); - - node->left_record_type = record_type(mmdb, node->left_record); - node->right_record_type = record_type(mmdb, node->right_record); - - // Note that offset will be invalid if the record type is not - // MMDB_RECORD_TYPE_DATA, but that's ok. Any use of the record entry - // for other data types is a programming error. - node->left_record_entry = (struct MMDB_entry_s) { - .mmdb = mmdb, - .offset = data_section_offset_for_record(mmdb, node->left_record), - }; - node->right_record_entry = (struct MMDB_entry_s) { - .mmdb = mmdb, - .offset = data_section_offset_for_record(mmdb, node->right_record), - }; - - return MMDB_SUCCESS; -} - -LOCAL uint32_t data_section_offset_for_record(MMDB_s *const mmdb, - uint64_t record) -{ - return (uint32_t)record - mmdb->metadata.node_count - - MMDB_DATA_SECTION_SEPARATOR; -} - -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) -{ - int length = path_length(va_path); - const char *path_elem; - int i = 0; - - MAYBE_CHECK_SIZE_OVERFLOW(length, SIZE_MAX / sizeof(const char *) - 1, - MMDB_INVALID_METADATA_ERROR); - - const char **path = malloc((length + 1) * sizeof(const char *)); - if (NULL == path) { - return MMDB_OUT_OF_MEMORY_ERROR; - } - - while (NULL != (path_elem = va_arg(va_path, char *))) { - path[i] = path_elem; - i++; - } - path[i] = NULL; - - int status = MMDB_aget_value(start, entry_data, path); - - free((char **)path); - - return status; -} - -LOCAL int path_length(va_list va_path) -{ - int i = 0; - const char *ignore; - va_list path_copy; - va_copy(path_copy, va_path); - - while (NULL != (ignore = va_arg(path_copy, char *))) { - i++; - } - - va_end(path_copy); - - return i; -} - -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; - char *first_invalid; - - int saved_errno = errno; - errno = 0; - int array_index = strtol(path_elem, &first_invalid, 10); - if (array_index < 0 || ERANGE == errno) { - errno = saved_errno; - return MMDB_INVALID_LOOKUP_PATH_ERROR; - } - errno = saved_errno; - - if (*first_invalid || (uint32_t)array_index >= size) { - 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) { - uint32_t next = entry_data->offset_to_next; - CHECKED_DECODE_ONE(mmdb, entry_data->pointer, entry_data); - /* Pointers to pointers are illegal under the spec */ - if (entry_data->type == MMDB_DATA_TYPE_POINTER) { - DEBUG_MSG("pointer points to another pointer"); - return MMDB_INVALID_DATA_ERROR; - } - - /* 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. */ - 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; - - // We subtract rather than add as it possible that offset + 1 - // could overflow for a corrupt database while an underflow - // from data_section_size - 1 should not be possible. - if (offset > mmdb->data_section_size - 1) { - DEBUG_MSGF("Offset (%d) past data section (%d)", 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) { - // Subtracting 1 to avoid possible overflow on offset + 1 - if (offset > mmdb->data_section_size - 1) { - DEBUG_MSGF("Extended type offset (%d) past data section (%d)", - offset, - mmdb->data_section_size); - return MMDB_INVALID_DATA_ERROR; - } - 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) { - uint8_t psize = ((ctrl >> 3) & 3) + 1; - DEBUG_MSGF("Pointer size: %i", psize); - - // We check that the offset does not extend past the end of the - // database and that the subtraction of psize did not underflow. - if (offset > mmdb->data_section_size - psize || - mmdb->data_section_size < psize) { - DEBUG_MSGF("Pointer offset (%d) past data section (%d)", offset + - psize, - mmdb->data_section_size); - return MMDB_INVALID_DATA_ERROR; - } - entry_data->pointer = get_ptr_from(ctrl, &mem[offset], psize); - DEBUG_MSGF("Pointer to: %i", entry_data->pointer); - - entry_data->data_size = psize; - entry_data->offset_to_next = offset + psize; - return MMDB_SUCCESS; - } - - uint32_t size = ctrl & 31; - switch (size) { - case 29: - // We subtract when checking offset to avoid possible overflow - if (offset > mmdb->data_section_size - 1) { - DEBUG_MSGF("String end (%d, case 29) past data section (%d)", - offset, - mmdb->data_section_size); - return MMDB_INVALID_DATA_ERROR; - } - size = 29 + mem[offset++]; - break; - case 30: - // We subtract when checking offset to avoid possible overflow - if (offset > mmdb->data_section_size - 2) { - DEBUG_MSGF("String end (%d, case 30) past data section (%d)", - offset, - mmdb->data_section_size); - return MMDB_INVALID_DATA_ERROR; - } - size = 285 + get_uint16(&mem[offset]); - offset += 2; - break; - case 31: - // We subtract when checking offset to avoid possible overflow - if (offset > mmdb->data_section_size - 3) { - DEBUG_MSGF("String end (%d, case 31) past data section (%d)", - offset, - mmdb->data_section_size); - return MMDB_INVALID_DATA_ERROR; - } - 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; - } - - // Check that the data doesn't extend past the end of the memory - // buffer and that the calculation in doing this did not underflow. - if (offset > mmdb->data_section_size - size || - mmdb->data_section_size < size) { - DEBUG_MSGF("Data end (%d) past data section (%d)", offset + size, - mmdb->data_section_size); - return MMDB_INVALID_DATA_ERROR; - } - - if (type == MMDB_DATA_TYPE_UINT16) { - if (size > 2) { - DEBUG_MSGF("uint16 of size %d", size); - 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) { - DEBUG_MSGF("uint32 of size %d", size); - 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) { - DEBUG_MSGF("int32 of size %d", size); - 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) { - DEBUG_MSGF("uint64 of size %d", size); - 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) { - DEBUG_MSGF("uint128 of size %d", size); - 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) { - DEBUG_MSGF("float of size %d", size); - 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) { - DEBUG_MSGF("double of size %d", size); - 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 1: - new_offset = ( (ctrl & 7) << 8) + ptr[0]; - break; - case 2: - new_offset = 2048 + ( (ctrl & 7) << 16 ) + ( ptr[0] << 8) + ptr[1]; - break; - case 3: - new_offset = 2048 + 524288 + ( (ctrl & 7) << 24 ) + get_uint24(ptr); - break; - case 4: - default: - new_offset = get_uint32(ptr); - break; - } - return 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) -{ - MMDB_data_pool_s *const pool = data_pool_new(MMDB_POOL_INIT_SIZE); - if (!pool) { - return MMDB_OUT_OF_MEMORY_ERROR; - } - - MMDB_entry_data_list_s *const list = data_pool_alloc(pool); - if (!list) { - data_pool_destroy(pool); - return MMDB_OUT_OF_MEMORY_ERROR; - } - - int const status = get_entry_data_list(start->mmdb, start->offset, list, - pool, 0); - - *entry_data_list = data_pool_to_list(pool); - if (!*entry_data_list) { - data_pool_destroy(pool); - return MMDB_OUT_OF_MEMORY_ERROR; - } - - return status; -} - -LOCAL int get_entry_data_list(MMDB_s *mmdb, - uint32_t offset, - MMDB_entry_data_list_s *const entry_data_list, - MMDB_data_pool_s *const pool, - int depth) -{ - if (depth >= MAXIMUM_DATA_STRUCTURE_DEPTH) { - DEBUG_MSG("reached the maximum data structure depth"); - return MMDB_INVALID_DATA_ERROR; - } - depth++; - 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; - CHECKED_DECODE_ONE(mmdb, last_offset = - entry_data_list->entry_data.pointer, - &entry_data_list->entry_data); - - /* Pointers to pointers are illegal under the spec */ - if (entry_data_list->entry_data.type == MMDB_DATA_TYPE_POINTER) { - DEBUG_MSG("pointer points to another pointer"); - return MMDB_INVALID_DATA_ERROR; - } - - 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, - pool, depth); - if (MMDB_SUCCESS != status) { - DEBUG_MSG("get_entry_data_list on pointer failed."); - 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; - while (array_size-- > 0) { - MMDB_entry_data_list_s *entry_data_list_to = - data_pool_alloc(pool); - if (!entry_data_list_to) { - return MMDB_OUT_OF_MEMORY_ERROR; - } - - int status = - get_entry_data_list(mmdb, array_offset, entry_data_list_to, - pool, depth); - if (MMDB_SUCCESS != status) { - DEBUG_MSG("get_entry_data_list on array element failed."); - return status; - } - - array_offset = entry_data_list_to->entry_data.offset_to_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; - while (size-- > 0) { - MMDB_entry_data_list_s *list_key = data_pool_alloc(pool); - if (!list_key) { - return MMDB_OUT_OF_MEMORY_ERROR; - } - - int status = - get_entry_data_list(mmdb, offset, list_key, pool, depth); - if (MMDB_SUCCESS != status) { - DEBUG_MSG("get_entry_data_list on map key failed."); - return status; - } - - offset = list_key->entry_data.offset_to_next; - - MMDB_entry_data_list_s *list_value = data_pool_alloc(pool); - if (!list_value) { - return MMDB_OUT_OF_MEMORY_ERROR; - } - - status = get_entry_data_list(mmdb, offset, list_value, pool, - depth); - if (MMDB_SUCCESS != status) { - DEBUG_MSG("get_entry_data_list on map element failed."); - return status; - } - offset = list_value->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; -/* Windows builds don't use autoconf but we can assume they're all - * little-endian. */ -#if MMDB_LITTLE_ENDIAN || _WIN32 - 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 MMDB_LITTLE_ENDIAN || _WIN32 - 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); -} - -void MMDB_free_entry_data_list(MMDB_entry_data_list_s *const entry_data_list) -{ - if (entry_data_list == NULL) { - return; - } - data_pool_destroy(entry_data_list->pool); -} - -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_AND_SET_NULL(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_AND_SET_NULL(mmdb->metadata.database_type); - } - - free_languages_metadata(mmdb); - free_descriptions_metadata(mmdb); -} - -LOCAL void free_languages_metadata(MMDB_s *mmdb) -{ - if (!mmdb->metadata.languages.names) { - return; - } - - for (size_t i = 0; i < mmdb->metadata.languages.count; i++) { - FREE_AND_SET_NULL(mmdb->metadata.languages.names[i]); - } - FREE_AND_SET_NULL(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_AND_SET_NULL( - mmdb->metadata.description.descriptions[i]->language); - } - - if (NULL != - mmdb->metadata.description.descriptions[i]->description) { - FREE_AND_SET_NULL( - mmdb->metadata.description.descriptions[i]->description); - } - FREE_AND_SET_NULL(mmdb->metadata.description.descriptions[i]); - } - } - - FREE_AND_SET_NULL(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--) { - - if (MMDB_DATA_TYPE_UTF8_STRING != - entry_data_list->entry_data.type) { - *status = MMDB_INVALID_DATA_ERROR; - return NULL; - } - 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); - if (NULL == hex_string) { - *status = MMDB_OUT_OF_MEMORY_ERROR; - return NULL; - } - 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; - MAYBE_CHECK_SIZE_OVERFLOW(size, SIZE_MAX / 2 - 1, NULL); - - hex_string = malloc((size * 2) + 1); - if (NULL == hex_string) { - return NULL; - } - - for (uint32_t i = 0; i < size; i++) { - sprintf(hex_string + (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"; - } -}