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 )