Alien-libmaxminddb
view release on metacpan or search on metacpan
maxminddb/src/maxminddb.c view on Meta::CPAN
#define UNICODE
#endif
#include <windows.h>
#include <ws2ipdef.h>
#ifndef SSIZE_MAX
#define SSIZE_MAX INTPTR_MAX
#endif
typedef ADDRESS_FAMILY sa_family_t;
#else
#include <arpa/inet.h>
#include <sys/mman.h>
#include <unistd.h>
#endif
#define MMDB_DATA_SECTION_SEPARATOR (16)
#define MAXIMUM_DATA_STRUCTURE_DEPTH (512)
#ifdef MMDB_DEBUG
#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, "Calloc failed in DEBUG_BINARY\n"); \
abort(); \
} \
fprintf(stderr, fmt "\n", binary); \
free(binary); \
} while (0)
#define DEBUG_NL fprintf(stderr, "\n")
#else
#define DEBUG_MSG(...)
#define DEBUG_MSGF(...)
#define DEBUG_BINARY(...)
#define DEBUG_NL
#endif
#ifdef MMDB_DEBUG
char *byte_to_binary(uint8_t byte) {
char *bits = calloc(9, sizeof(char));
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;
}
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
static int map_file(MMDB_s *const mmdb);
static const uint8_t *find_metadata(const uint8_t *file_content,
maxminddb/src/maxminddb.c view on Meta::CPAN
goto cleanup;
}
if (!can_multiply(SSIZE_MAX,
mmdb->metadata.node_count,
mmdb->full_record_byte_size)) {
status = MMDB_INVALID_METADATA_ERROR;
goto cleanup;
}
ssize_t search_tree_size = (ssize_t)mmdb->metadata.node_count *
(ssize_t)mmdb->full_record_byte_size;
mmdb->data_section =
mmdb->file_content + search_tree_size + MMDB_DATA_SECTION_SEPARATOR;
if (mmdb->file_size < MMDB_DATA_SECTION_SEPARATOR ||
search_tree_size > mmdb->file_size - MMDB_DATA_SECTION_SEPARATOR) {
status = MMDB_INVALID_METADATA_ERROR;
goto cleanup;
}
ssize_t data_section_size =
mmdb->file_size - search_tree_size - MMDB_DATA_SECTION_SEPARATOR;
if (data_section_size > UINT32_MAX || data_section_size <= 0) {
status = MMDB_INVALID_METADATA_ERROR;
goto cleanup;
}
mmdb->data_section_size = (uint32_t)data_section_size;
// 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
static LPWSTR utf8_to_utf16(const char *utf8_str) {
int wide_chars = MultiByteToWideChar(CP_UTF8, 0, utf8_str, -1, NULL, 0);
wchar_t *utf16_str = (wchar_t *)calloc(wide_chars, sizeof(wchar_t));
if (!utf16_str) {
return NULL;
}
if (MultiByteToWideChar(CP_UTF8, 0, utf8_str, -1, utf16_str, wide_chars) <
1) {
free(utf16_str);
return NULL;
}
return utf16_str;
}
static int map_file(MMDB_s *const mmdb) {
ssize_t size;
int status = MMDB_SUCCESS;
HANDLE mmh = NULL;
HANDLE fd = INVALID_HANDLE_VALUE;
LPWSTR utf16_filename = utf8_to_utf16(mmdb->filename);
if (!utf16_filename) {
status = MMDB_FILE_OPEN_ERROR;
goto cleanup;
}
fd = CreateFileW(utf16_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;
}
LARGE_INTEGER file_size;
if (!GetFileSizeEx(fd, &file_size)) {
status = MMDB_IO_ERROR;
goto cleanup;
}
if (file_size.QuadPart < 0 || file_size.QuadPart > SSIZE_MAX) {
status = MMDB_IO_ERROR;
goto cleanup;
}
size = (ssize_t)file_size.QuadPart;
mmh = CreateFileMapping(fd, NULL, PAGE_READONLY, 0, 0, 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;
free(utf16_filename);
return status;
}
#else // _WIN32
static int map_file(MMDB_s *const mmdb) {
int status = MMDB_SUCCESS;
maxminddb/src/maxminddb.c view on Meta::CPAN
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;
}
static 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;
}
static 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;
}
static 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(entry_data.utf8_string, entry_data.data_size);
if (NULL == *value) {
return MMDB_OUT_OF_MEMORY_ERROR;
}
return MMDB_SUCCESS;
}
static 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 = calloc(array_size, sizeof(char *));
if (NULL == mmdb->metadata.languages.names) {
MMDB_free_entry_data_list(first_member);
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) {
MMDB_free_entry_data_list(first_member);
return MMDB_INVALID_METADATA_ERROR;
}
mmdb->metadata.languages.names[i] = mmdb_strndup(
member->entry_data.utf8_string, member->entry_data.data_size);
if (NULL == mmdb->metadata.languages.names[i]) {
MMDB_free_entry_data_list(first_member);
return MMDB_OUT_OF_MEMORY_ERROR;
}
// We assign this as we go so that if we fail a calloc 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;
}
static 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 =
calloc(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] =
calloc(1, 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(
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(
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(const 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);
} else {
/* No MMDB error occurred; the GAI failure is reported via
* *gai_error. Set *mmdb_error to a defined value so callers
* don't read indeterminate memory. */
*mmdb_error = MMDB_SUCCESS;
}
if (NULL != addresses) {
freeaddrinfo(addresses);
}
return result;
}
static 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(const MMDB_s *const mmdb,
const struct sockaddr *const sockaddr,
int *const mmdb_error) {
MMDB_lookup_result_s result = {.found_entry = false,
.netmask = 0,
maxminddb/src/maxminddb.c view on Meta::CPAN
MMDB_entry_data_s *entry_data) {
uint32_t size = entry_data->data_size;
char *first_invalid;
int saved_errno = errno;
errno = 0;
long array_index = strtol(path_elem, &first_invalid, 10);
if (ERANGE == errno) {
errno = saved_errno;
return MMDB_INVALID_LOOKUP_PATH_ERROR;
}
errno = saved_errno;
if (array_index < 0) {
array_index += size;
if (array_index < 0) {
return MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR;
}
}
if (*first_invalid || (unsigned long)array_index >= size) {
return MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR;
}
for (long 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, 0);
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;
}
static int lookup_path_in_map(const char *path_elem,
const MMDB_s *const 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, 0);
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;
}
static int skip_map_or_array(const MMDB_s *const mmdb,
MMDB_entry_data_s *entry_data,
int depth) {
if (depth >= MAXIMUM_DATA_STRUCTURE_DEPTH) {
DEBUG_MSG("reached the maximum data structure depth");
return MMDB_INVALID_DATA_ERROR;
}
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, depth + 1);
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, depth + 1);
if (MMDB_SUCCESS != status) {
return status;
}
}
}
return MMDB_SUCCESS;
}
static int decode_one_follow(const MMDB_s *const mmdb,
uint32_t offset,
MMDB_entry_data_s *entry_data) {
maxminddb/src/maxminddb.c view on Meta::CPAN
}
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], (int)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], (int)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], (int)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], (int)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], (int)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 const *)&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;
}
static int get_ext_type(int raw_ext_type) { return 7 + raw_ext_type; }
static 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 = (uint32_t)((ctrl & 7) << 8) + ptr[0];
break;
case 2:
new_offset = 2048 + (uint32_t)((ctrl & 7) << 16) +
(uint32_t)(ptr[0] << 8) + ptr[1];
break;
case 3:
new_offset =
2048 + 524288 + (uint32_t)((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(
const 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 = NULL;
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);
maxminddb/src/maxminddb.c view on Meta::CPAN
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
}
if (NULL !=
mmdb->metadata.description.descriptions[i]->description) {
#if defined(__clang__)
// This is a const char * that we need to free, which isn't valid. However
// it would mean changing the public API to fix this.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wcast-qual"
#endif
FREE_AND_SET_NULL(
mmdb->metadata.description.descriptions[i]->description);
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
}
FREE_AND_SET_NULL(mmdb->metadata.description.descriptions[i]);
}
}
FREE_AND_SET_NULL(mmdb->metadata.description.descriptions);
mmdb->metadata.description.count = 0;
}
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;
}
static 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(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(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\" <utf8_string>\n", string);
free(string);
entry_data_list = entry_data_list->next;
} break;
case MMDB_DATA_TYPE_BYTES: {
char *hex_string =
bytes_to_hex(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 <bytes>\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 <double>\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 <float>\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 <uint16>\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 <uint32>\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 <boolean>\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 " <uint64>\n",
entry_data_list->entry_data.uint64);
entry_data_list = entry_data_list->next;
break;
case MMDB_DATA_TYPE_UINT128:
( run in 0.794 second using v1.01-cache-2.11-cpan-fa01517f264 )