1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2025-06-16 07:07:13 +02:00

Integrate MaxmindDB module.

This commit is contained in:
Sandu Liviu Catalin
2020-03-22 16:33:48 +02:00
parent e46c1b0aa9
commit 3b7568f13a
88 changed files with 15336 additions and 1 deletions

31
module/Vendor/MaxmindDB/t/Makefile.am vendored Normal file
View File

@ -0,0 +1,31 @@
include $(top_srcdir)/common.mk
all-local:
cd libtap && $(MAKE) $(AM_MAKEFLAGS) all
clean-local:
cd libtap && $(MAKE) $(AM_MAKEFLAGS) clean
AM_LDFLAGS = $(top_builddir)/src/libmaxminddb.la
CFLAGS += -I$(top_srcdir)/src
noinst_LTLIBRARIES = libmmdbtest.la
libmmdbtest_la_SOURCES = maxminddb_test_helper.c maxminddb_test_helper.h
EXTRA_DIST = compile_c++_t.pl external_symbols_t.pl mmdblookup_t.pl \
libtap/COPYING libtap/INSTALL libtap/Makefile libtap/README.md \
libtap/tap.c libtap/tap.h maxmind-db
check_PROGRAMS = \
bad_pointers_t bad_databases_t basic_lookup_t data_entry_list_t \
data-pool-t data_types_t dump_t get_value_t get_value_pointer_bug_t \
ipv4_start_cache_t ipv6_lookup_in_ipv4_t metadata_t metadata_pointers_t \
no_map_get_value_t read_node_t threads_t version_t
data_pool_t_LDFLAGS = $(AM_LDFLAGS) -lm
data_pool_t_SOURCES = data-pool-t.c ../src/data-pool.c
threads_t_CFLAGS = $(CFLAGS) -pthread
TESTS = $(check_PROGRAMS) compile_c++_t.pl external_symbols_t.pl mmdblookup_t.pl
LDADD = libmmdbtest.la libtap/libtap.a

View File

@ -0,0 +1,67 @@
// This test currently does not work on Windows as nftw is
// not available.
#define _XOPEN_SOURCE 500
#include <ftw.h>
#include <libgen.h>
#include <unistd.h>
#include "maxminddb_test_helper.h"
int test_read(const char *path, const struct stat *UNUSED(
sbuf), int flags, struct FTW *UNUSED(ftw))
{
// Check if path is a regular file)
if (flags != FTW_F) {
return 0;
}
MMDB_s *mmdb = (MMDB_s *)calloc(1, sizeof(MMDB_s));
if (NULL == mmdb) {
BAIL_OUT("could not allocate memory for our MMDB_s struct");
}
int status = MMDB_open(path, MMDB_MODE_MMAP, mmdb);
if (status != MMDB_SUCCESS) {
ok(1, "received error when opening %s", path);
free(mmdb);
return 0;
}
int gai_error, mmdb_error;
MMDB_lookup_string(mmdb, "1.1.1.1", &gai_error, &mmdb_error);
if (gai_error != 0) {
BAIL_OUT("could not parse IP address");
}
cmp_ok(mmdb_error, "!=", MMDB_SUCCESS, "opening %s returned an error",
path);
MMDB_close(mmdb);
free(mmdb);
return 0;
}
int main(void)
{
char *test_db_dir;
#ifdef _WIN32
test_db_dir = "./t/maxmind-db/bad-data";
#else
char cwd[500];
char *UNUSED(tmp) = getcwd(cwd, 500);
if (strcmp(basename(cwd), "t") == 0) {
test_db_dir = "./maxmind-db/bad-data";
} else {
test_db_dir = "./t/maxmind-db/bad-data";
}
#endif
plan(NO_PLAN);
if (nftw(test_db_dir, test_read, 10, FTW_PHYS) != 0) {
BAIL_OUT("nftw failed");
}
done_testing();
}

View File

@ -0,0 +1,53 @@
#include "maxminddb_test_helper.h"
void run_tests(int mode, const char *mode_desc)
{
const char *filename = "MaxMind-DB-test-broken-pointers-24.mmdb";
const char *path = test_database_path(filename);
MMDB_s *mmdb = open_ok(path, mode, mode_desc);
free((void *)path);
{
const char *ip = "1.1.1.16";
MMDB_lookup_result_s result =
lookup_string_ok(mmdb, ip, filename, mode_desc);
MMDB_entry_data_s entry_data;
int status = MMDB_get_value(&result.entry, &entry_data, NULL);
cmp_ok(
status, "==", MMDB_INVALID_DATA_ERROR,
"MMDB_get_value returns MMDB_INVALID_DATA_ERROR for bad pointer in data section");
MMDB_entry_data_list_s *entry_data_list;
status = MMDB_get_entry_data_list(&result.entry, &entry_data_list);
cmp_ok(
status, "==", MMDB_INVALID_DATA_ERROR,
"MMDB_get_entry_data_list returns MMDB_INVALID_DATA_ERROR for bad pointer in data section");
MMDB_free_entry_data_list(entry_data_list);
}
{
const char *ip = "1.1.1.32";
int gai_error, mmdb_error;
MMDB_lookup_result_s UNUSED(result) =
MMDB_lookup_string(mmdb, ip, &gai_error, &mmdb_error);
cmp_ok(
mmdb_error, "==", MMDB_CORRUPT_SEARCH_TREE_ERROR,
"MMDB_lookup_string sets mmdb_error to MMDB_CORRUPT_SEARCH_TREE_ERROR when a search tree record points outside the data section");
}
MMDB_close(mmdb);
free(mmdb);
}
int main(void)
{
plan(NO_PLAN);
for_all_modes(&run_tests);
done_testing();
}

View File

@ -0,0 +1,207 @@
#include "maxminddb_test_helper.h"
static void test_big_lookup(void);
/* These globals are gross but it's the easiest way to mix calling
* for_all_modes() and for_all_record_sizes() */
static int Current_Mode;
static const char *Current_Mode_Description;
void test_one_result(MMDB_s *mmdb, MMDB_lookup_result_s result,
const char *ip, const char *expect,
const char *function, const char *filename,
const char *mode_desc)
{
int is_ok = ok(result.found_entry,
"got a result for an IP in the database - %s - %s - %s - %s",
function, ip, filename, mode_desc);
if (!is_ok) {
return;
}
MMDB_entry_data_s data =
data_ok(&result, MMDB_DATA_TYPE_UTF8_STRING, "result{ip}", "ip", NULL);
char *string = mmdb_strndup(data.utf8_string, data.data_size);
char *real_expect;
if (mmdb->metadata.ip_version == 4 || strncmp(expect, "::", 2) == 0) {
real_expect = mmdb_strndup(expect, strlen(expect));
} else {
// When looking up IPv4 addresses in a mixed DB the result will be
// something like "::1.2.3.4", not just "1.2.3.4".
int maxlen = strlen(expect) + 3;
real_expect = malloc(maxlen);
snprintf(real_expect, maxlen, "::%s", expect);
}
is(string, real_expect,
"found expected result for ip key - %s - %s - %s - %s", function, ip,
filename, mode_desc);
free(real_expect);
free(string);
}
void test_one_ip(MMDB_s *mmdb, const char *ip, const char *expect,
const char *filename, const char *mode_desc)
{
MMDB_lookup_result_s result =
lookup_string_ok(mmdb, ip, filename, mode_desc);
test_one_result(mmdb, result, ip, expect, "MMDB_lookup_string", filename,
mode_desc);
result = lookup_sockaddr_ok(mmdb, ip, filename, mode_desc);
test_one_result(mmdb, result, ip, expect, "MMDB_lookup_addrinfo", filename,
mode_desc);
}
void run_ipX_tests(const char *filename, const char **missing_ips,
int missing_ips_length, const char *pairs[][2],
int pairs_rows)
{
const char *path = test_database_path(filename);
int mode = Current_Mode;
const char *mode_desc = Current_Mode_Description;
MMDB_s *mmdb = open_ok(path, mode, mode_desc);
free((void *)path);
char desc_suffix[500];
snprintf(desc_suffix, 500, "%s - %s", filename, mode_desc);
for (int i = 0; i < missing_ips_length; i++) {
const char *ip = missing_ips[i];
MMDB_lookup_result_s result =
lookup_string_ok(mmdb, ip, filename, mode_desc);
ok(
!result.found_entry,
"no result entry struct returned for IP address not in the database (string lookup) - %s - %s - %s",
ip, filename, mode_desc);
result = lookup_sockaddr_ok(mmdb, ip, filename, mode_desc);
ok(
!result.found_entry,
"no result entry struct returned for IP address not in the database (ipv4 lookup) - %s - %s - %s",
ip, filename, mode_desc);
}
for (int i = 0; i < pairs_rows; i += 1) {
const char *ip_to_lookup = pairs[i][0];
const char *expect = pairs[i][1];
test_one_ip(mmdb, ip_to_lookup, expect, filename, mode_desc);
}
MMDB_close(mmdb);
free(mmdb);
}
void run_ipv4_tests(int UNUSED(
record_size), const char *filename, const char *UNUSED(
ignored))
{
const char *pairs[9][2] = {
{ "1.1.1.1", "1.1.1.1" },
{ "1.1.1.2", "1.1.1.2" },
{ "1.1.1.3", "1.1.1.2" },
{ "1.1.1.7", "1.1.1.4" },
{ "1.1.1.9", "1.1.1.8" },
{ "1.1.1.15", "1.1.1.8" },
{ "1.1.1.17", "1.1.1.16" },
{ "1.1.1.31", "1.1.1.16" },
{ "1.1.1.32", "1.1.1.32" },
};
const char *missing[1] = { "2.3.4.5" };
run_ipX_tests(filename, missing, 1, pairs, 9);
}
void run_ipv6_tests(int UNUSED(
record_size), const char *filename, const char *UNUSED(
ignored))
{
const char *pairs[9][2] = {
{ "::1:ffff:ffff", "::1:ffff:ffff" },
{ "::2:0:0", "::2:0:0" },
{ "::2:0:1a", "::2:0:0" },
{ "::2:0:40", "::2:0:40" },
{ "::2:0:4f", "::2:0:40" },
{ "::2:0:50", "::2:0:50" },
{ "::2:0:52", "::2:0:50" },
{ "::2:0:58", "::2:0:58" },
{ "::2:0:59", "::2:0:58" },
};
const char *missing[2] = { "2.3.4.5", "::abcd" };
run_ipX_tests(filename, missing, 2, pairs, 9);
}
void all_record_sizes(int mode, const char *description)
{
const char *ipv4_filename_fmts[] = {
"MaxMind-DB-test-ipv4-%i.mmdb",
"MaxMind-DB-test-mixed-%i.mmdb"
};
Current_Mode = mode;
Current_Mode_Description = description;
for (int i = 0; i < 2; i++) {
for_all_record_sizes(ipv4_filename_fmts[i], &run_ipv4_tests);
}
const char *ipv6_filename_fmts[] = {
"MaxMind-DB-test-ipv6-%i.mmdb",
"MaxMind-DB-test-mixed-%i.mmdb"
};
for (int i = 0; i < 2; i++) {
for_all_record_sizes(ipv6_filename_fmts[i], &run_ipv6_tests);
}
}
static void test_big_lookup(void)
{
const char *const db_filename = "GeoIP2-Precision-Enterprise-Test.mmdb";
const char *const db_path = test_database_path(db_filename);
ok(db_path != NULL, "got database path");
MMDB_s * const mmdb = open_ok(db_path, MMDB_MODE_MMAP, "mmap mode");
ok(mmdb != NULL, "opened MMDB");
free((char *)db_path);
int gai_err = 0, mmdb_err = 0;
const char *const ip_address = "81.2.69.160";
MMDB_lookup_result_s result = MMDB_lookup_string(mmdb, ip_address, &gai_err,
&mmdb_err);
ok(gai_err == 0, "no getaddrinfo error");
ok(mmdb_err == MMDB_SUCCESS, "no error from maxminddb library");
ok(result.found_entry, "found IP");
MMDB_entry_data_list_s *entry_data_list = NULL;
ok(
MMDB_get_entry_data_list(&result.entry,
&entry_data_list) == MMDB_SUCCESS,
"successfully looked up entry data list"
);
ok(entry_data_list != NULL, "got an entry_data_list");
MMDB_free_entry_data_list(entry_data_list);
MMDB_close(mmdb);
free(mmdb);
}
int main(void)
{
plan(NO_PLAN);
for_all_modes(&all_record_sizes);
test_big_lookup();
done_testing();
}

View File

@ -0,0 +1,107 @@
#!/usr/bin/env perl
use strict;
use warnings;
use Cwd qw( abs_path );
use FindBin qw( $Bin );
eval <<'EOF';
use Test::More 0.88;
use File::Temp qw( tempdir );
use IPC::Run3 qw( run3 );
EOF
if ($@) {
print
"1..0 # skip all tests skipped - these tests need the Test::More 0.88, File::Temp, and IPC::Run3 modules:\n";
print "$@";
exit 0;
}
my $test_db = "$Bin/maxmind-db/test-data/GeoIP2-City-Test.mmdb";
my $cpp_code = <<"EOF";
#include <maxminddb.h>
int main(int argc, char *argv[])
{
const char *fname = "$test_db";
MMDB_s mmdb;
return MMDB_open(fname, MMDB_MODE_MMAP, &mmdb);
}
EOF
my $tempdir = tempdir(CLEANUP => 1 );
my $file = "$tempdir/open.cpp";
open my $fh, '>', $file or die $!;
print {$fh} $cpp_code or die $!;
close $fh or die $!;
my $exe = "$tempdir/open";
my $include_dir = abs_path("$Bin/../include");
my $lib_dir = abs_path("$Bin/../src/.libs");
my $cxx = $ENV{CXX} || 'c++';
_test_cmd(
[ $cxx, $file, "-I$include_dir", "-L$lib_dir", "-lmaxminddb", "-o$exe" ],
qr/^$/,
q{},
0,
'compile C++ program which links against libmaxminddb',
);
# DYLD_LIBRARY_PATH is for Mac OS X
$ENV{LD_LIBRARY_PATH} = $ENV{DYLD_LIBRARY_PATH} = $lib_dir;
_test_cmd(
[$exe],
qr/^$/,
q{},
0,
'compiled C++ program executes without errors'
);
done_testing();
sub _test_cmd {
my $cmd = shift;
my $expect_stdout = shift;
my $expect_stderr = shift;
my $expect_status = shift;
my $desc = shift;
my $stdout;
my $stderr;
run3(
$cmd,
\undef,
\$stdout,
\$stderr,
);
my $exit_status = $? >> 8;
# We don't need to retest that the help output shows up for all errors
if ( defined $expect_stdout ) {
like(
$stdout,
$expect_stdout,
"stdout for @{$cmd}"
);
}
if ( ref $expect_stderr ) {
like( $stderr, $expect_stderr, "stderr for @{$cmd}" );
}
else {
is( $stderr, $expect_stderr, "stderr for @{$cmd}" );
}
is(
$exit_status, $expect_status,
"exit status was $expect_status for @{$cmd}"
);
}

374
module/Vendor/MaxmindDB/t/data-pool-t.c vendored Normal file
View File

@ -0,0 +1,374 @@
#include <assert.h>
#include <data-pool.h>
#include <inttypes.h>
#include "libtap/tap.h"
#include <math.h>
#include "maxminddb_test_helper.h"
static void test_data_pool_new(void);
static void test_data_pool_destroy(void);
static void test_data_pool_alloc(void);
static void test_data_pool_to_list(void);
static bool create_and_check_list(size_t const,
size_t const);
static void check_block_count(MMDB_entry_data_list_s const *const,
size_t const);
int main(void)
{
plan(NO_PLAN);
test_data_pool_new();
test_data_pool_destroy();
test_data_pool_alloc();
test_data_pool_to_list();
done_testing();
}
static void test_data_pool_new(void)
{
{
MMDB_data_pool_s *const pool = data_pool_new(0);
ok(!pool, "size 0 is not valid");
}
{
MMDB_data_pool_s *const pool = data_pool_new(SIZE_MAX - 10);
ok(!pool, "very large size is not valid");
}
{
MMDB_data_pool_s *const pool = data_pool_new(512);
ok(pool != NULL, "size 512 is valid");
cmp_ok(pool->size, "==", 512, "size is 512");
cmp_ok(pool->used, "==", 0, "used size is 0");
data_pool_destroy(pool);
}
}
static void test_data_pool_destroy(void)
{
{
data_pool_destroy(NULL);
}
{
MMDB_data_pool_s *const pool = data_pool_new(512);
ok(pool != NULL, "created pool");
data_pool_destroy(pool);
}
}
static void test_data_pool_alloc(void)
{
{
MMDB_data_pool_s *const pool = data_pool_new(1);
ok(pool != NULL, "created pool");
cmp_ok(pool->used, "==", 0, "used size starts at 0");
MMDB_entry_data_list_s *const entry1 = data_pool_alloc(pool);
ok(entry1 != NULL, "allocated first entry");
// Arbitrary so that we can recognize it.
entry1->entry_data.offset = (uint32_t)123;
cmp_ok(pool->size, "==", 1, "size is still 1");
cmp_ok(pool->used, "==", 1, "used size is 1 after taking one");
MMDB_entry_data_list_s *const entry2 = data_pool_alloc(pool);
ok(entry2 != NULL, "got another entry");
ok(entry1 != entry2, "second entry is different from first entry");
cmp_ok(pool->size, "==", 2, "size is 2 (new block)");
cmp_ok(pool->used, "==", 1, "used size is 1 in current block");
ok(entry1->entry_data.offset == 123,
"accessing the original entry's memory is ok");
data_pool_destroy(pool);
}
{
size_t const initial_size = 10;
MMDB_data_pool_s *const pool = data_pool_new(initial_size);
ok(pool != NULL, "created pool");
MMDB_entry_data_list_s *entry1 = NULL;
for (size_t i = 0; i < initial_size; i++) {
MMDB_entry_data_list_s *const entry = data_pool_alloc(pool);
ok(entry != NULL, "got an entry");
// Give each a unique number so we can check it.
entry->entry_data.offset = (uint32_t)i;
if (i == 0) {
entry1 = entry;
}
}
cmp_ok(pool->size, "==", initial_size, "size is the initial size");
cmp_ok(pool->used, "==", initial_size, "used size is as expected");
MMDB_entry_data_list_s *const entry = data_pool_alloc(pool);
ok(entry != NULL, "got an entry");
entry->entry_data.offset = (uint32_t)initial_size;
cmp_ok(pool->size, "==", initial_size * 2,
"size is the initial size*2");
cmp_ok(pool->used, "==", 1, "used size is as expected");
MMDB_entry_data_list_s *const list = data_pool_to_list(pool);
MMDB_entry_data_list_s *element = list;
for (size_t i = 0; i < initial_size + 1; i++) {
ok(
element->entry_data.offset == (uint32_t)i,
"found offset %" PRIu32 ", should have %zu",
element->entry_data.offset,
i
);
element = element->next;
}
ok(entry1->entry_data.offset == (uint32_t)0,
"accessing entry1's original memory is ok after growing the pool");
data_pool_destroy(pool);
}
}
static void test_data_pool_to_list(void)
{
{
size_t const initial_size = 16;
MMDB_data_pool_s *const pool = data_pool_new(initial_size);
ok(pool != NULL, "created pool");
MMDB_entry_data_list_s *const entry1 = data_pool_alloc(pool);
ok(entry1 != NULL, "got an entry");
MMDB_entry_data_list_s *const list_one_element
= data_pool_to_list(pool);
ok(list_one_element != NULL, "got a list");
ok(list_one_element == entry1,
"list's first element is the first we retrieved");
ok(list_one_element->next == NULL, "list is one element in size");
MMDB_entry_data_list_s *const entry2 = data_pool_alloc(pool);
ok(entry2 != NULL, "got another entry");
MMDB_entry_data_list_s *const list_two_elements
= data_pool_to_list(pool);
ok(list_two_elements != NULL, "got a list");
ok(list_two_elements == entry1,
"list's first element is the first we retrieved");
ok(list_two_elements->next != NULL, "list has a second element");
MMDB_entry_data_list_s *const second_element = list_two_elements->next;
ok(second_element == entry2,
"second item in list is second we retrieved");
ok(second_element->next == NULL, "list ends with the second element");
data_pool_destroy(pool);
}
{
size_t const initial_size = 1;
MMDB_data_pool_s *const pool = data_pool_new(initial_size);
ok(pool != NULL, "created pool");
MMDB_entry_data_list_s *const entry1 = data_pool_alloc(pool);
ok(entry1 != NULL, "got an entry");
MMDB_entry_data_list_s *const list_one_element
= data_pool_to_list(pool);
ok(list_one_element != NULL, "got a list");
ok(list_one_element == entry1,
"list's first element is the first we retrieved");
ok(list_one_element->next == NULL, "list ends with this element");
data_pool_destroy(pool);
}
{
size_t const initial_size = 2;
MMDB_data_pool_s *const pool = data_pool_new(initial_size);
ok(pool != NULL, "created pool");
MMDB_entry_data_list_s *const entry1 = data_pool_alloc(pool);
ok(entry1 != NULL, "got an entry");
MMDB_entry_data_list_s *const entry2 = data_pool_alloc(pool);
ok(entry2 != NULL, "got an entry");
ok(entry1 != entry2, "second entry is different from the first");
MMDB_entry_data_list_s *const list_element1 = data_pool_to_list(pool);
ok(list_element1 != NULL, "got a list");
ok(list_element1 == entry1,
"list's first element is the first we retrieved");
MMDB_entry_data_list_s *const list_element2 = list_element1->next;
ok(list_element2 == entry2,
"second element is the second we retrieved");
ok(list_element2->next == NULL, "list ends with this element");
data_pool_destroy(pool);
}
{
diag("starting test: fill one block save for one spot");
ok(
create_and_check_list(3, 2),
"fill one block save for one spot"
);
}
{
diag("starting test: fill one block");
ok(
create_and_check_list(3, 3),
"fill one block"
);
}
{
diag("starting test: fill one block and use one spot in the next block");
ok(
create_and_check_list(3, 3 + 1),
"fill one block and use one spot in the next block"
);
}
{
diag("starting test: fill two blocks save for one spot");
ok(
create_and_check_list(3, 3 + 3 * 2 - 1),
"fill two blocks save for one spot"
);
}
{
diag("starting test: fill two blocks");
ok(
create_and_check_list(3, 3 + 3 * 2),
"fill two blocks"
);
}
{
diag("starting test: fill two blocks and use one spot in the next");
ok(
create_and_check_list(3, 3 + 3 * 2 + 1),
"fill two blocks and use one spot in the next"
);
}
{
diag("starting test: fill three blocks save for one spot");
ok(
create_and_check_list(3, 3 + 3 * 2 + 3 * 2 * 2 - 1),
"fill three blocks save for one spot"
);
}
{
diag("starting test: fill three blocks");
ok(
create_and_check_list(3, 3 + 3 * 2 + 3 * 2 * 2),
"fill three blocks"
);
}
// It would be nice to have a larger number of these, but it's expensive to
// run many. We currently hardcode what this will be anyway, so varying
// this is not very interesting.
size_t const initial_sizes[] = { 1, 2, 32, 64, 128, 256 };
size_t const max_element_count = 4096;
for (size_t i = 0; i < sizeof(initial_sizes) / sizeof(initial_sizes[0]);
i++) {
size_t const initial_size = initial_sizes[i];
for (size_t element_count = 0; element_count < max_element_count;
element_count++) {
assert(create_and_check_list(initial_size, element_count));
}
}
}
// Use assert() rather than libtap as libtap is significantly slower and we run
// this frequently.
static bool create_and_check_list(size_t const initial_size,
size_t const element_count)
{
MMDB_data_pool_s *const pool = data_pool_new(initial_size);
assert(pool != NULL);
assert(pool->used == 0);
// Hold on to the pointers as we initially see them so that we can check
// they are still valid after building the list.
MMDB_entry_data_list_s **const entry_array
= calloc(element_count, sizeof(MMDB_entry_data_list_s *));
assert(entry_array != NULL);
for (size_t i = 0; i < element_count; i++) {
MMDB_entry_data_list_s *const entry = data_pool_alloc(pool);
assert(entry != NULL);
entry->entry_data.offset = (uint32_t)i;
entry_array[i] = entry;
}
MMDB_entry_data_list_s *const list = data_pool_to_list(pool);
if (element_count == 0) {
assert(list == NULL);
data_pool_destroy(pool);
free(entry_array);
return true;
}
assert(list != NULL);
MMDB_entry_data_list_s *element = list;
for (size_t i = 0; i < element_count; i++) {
assert(element->entry_data.offset == (uint32_t)i);
assert(element == entry_array[i]);
element = element->next;
}
assert(element == NULL);
check_block_count(list, initial_size);
data_pool_destroy(pool);
free(entry_array);
return true;
}
// Use assert() rather than libtap as libtap is significantly slower and we run
// this frequently.
static void check_block_count(MMDB_entry_data_list_s const *const list,
size_t const initial_size)
{
size_t got_block_count = 0;
size_t got_element_count = 0;
MMDB_entry_data_list_s const *element = list;
while (element) {
got_element_count++;
if (element->pool) {
got_block_count++;
}
element = element->next;
}
// Because <number of elements> = <initial size> * 2^(number of blocks)
double const a = ceil((double)got_element_count / (double)initial_size);
double const b = log2(a);
size_t const expected_block_count = ((size_t)b) + 1;
assert(got_block_count == expected_block_count);
}

View File

@ -0,0 +1,353 @@
#include "maxminddb_test_helper.h"
MMDB_entry_data_list_s *test_array_value(MMDB_entry_data_list_s
*entry_data_list)
{
MMDB_entry_data_list_s *array = entry_data_list = entry_data_list->next;
cmp_ok(array->entry_data.type, "==", MMDB_DATA_TYPE_ARRAY,
"'array' key's value is an array");
cmp_ok(array->entry_data.data_size, "==", 3,
"'array' key's value has 3 elements");
MMDB_entry_data_list_s *idx0 = entry_data_list = entry_data_list->next;
cmp_ok(idx0->entry_data.type, "==", MMDB_DATA_TYPE_UINT32,
"first array entry is a UINT32");
cmp_ok(idx0->entry_data.uint32, "==", 1, "first array entry value is 1");
MMDB_entry_data_list_s *idx1 = entry_data_list = entry_data_list->next;
cmp_ok(idx1->entry_data.type, "==", MMDB_DATA_TYPE_UINT32,
"second array entry is a UINT32");
cmp_ok(idx1->entry_data.uint32, "==", 2, "second array entry value is 2");
MMDB_entry_data_list_s *idx2 = entry_data_list = entry_data_list->next;
cmp_ok(idx2->entry_data.type, "==", MMDB_DATA_TYPE_UINT32,
"third array entry is a UINT32");
cmp_ok(idx2->entry_data.uint32, "==", 3, "third array entry value is 3");
return entry_data_list;
}
MMDB_entry_data_list_s *test_boolean_value(MMDB_entry_data_list_s
*entry_data_list)
{
MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next;
cmp_ok(value->entry_data.type, "==", MMDB_DATA_TYPE_BOOLEAN,
"'boolean' key's value is a boolean");
ok(value->entry_data.boolean, "'boolean' key's value is true");
return entry_data_list;
}
MMDB_entry_data_list_s *test_bytes_value(MMDB_entry_data_list_s
*entry_data_list)
{
MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next;
cmp_ok(value->entry_data.type, "==", MMDB_DATA_TYPE_BYTES,
"'bytes' key's value is bytes");
uint8_t *bytes = malloc(value->entry_data.data_size);
if (NULL == bytes) {
BAIL_OUT("malloc failed");
}
memcpy(bytes, value->entry_data.bytes, value->entry_data.data_size);
uint8_t expect[] = { 0x00, 0x00, 0x00, 0x2a };
ok(memcmp(bytes, expect, 4) == 0, "got expected value for bytes key");
free((void *)bytes);
return entry_data_list;
}
MMDB_entry_data_list_s *test_double_value(MMDB_entry_data_list_s
*entry_data_list)
{
MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next;
cmp_ok(value->entry_data.type, "==", MMDB_DATA_TYPE_DOUBLE,
"'double' key's value is a double");
compare_double(value->entry_data.double_value, 42.123456);
return entry_data_list;
}
MMDB_entry_data_list_s *test_float_value(MMDB_entry_data_list_s
*entry_data_list)
{
MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next;
cmp_ok(value->entry_data.type, "==", MMDB_DATA_TYPE_FLOAT,
"'float' key's value is a float");
compare_float(value->entry_data.float_value, 1.1F);
return entry_data_list;
}
MMDB_entry_data_list_s *test_int32_value(MMDB_entry_data_list_s
*entry_data_list)
{
MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next;
cmp_ok(value->entry_data.type, "==", MMDB_DATA_TYPE_INT32,
"'int32' key's value is an int32");
int32_t expect = 1 << 28;
expect *= -1;
cmp_ok(value->entry_data.int32, "==", expect,
"got expected value for int32 key");
return entry_data_list;
}
MMDB_entry_data_list_s *test_arrayX_value(MMDB_entry_data_list_s
*entry_data_list)
{
MMDB_entry_data_list_s *arrayX = entry_data_list = entry_data_list->next;
cmp_ok(arrayX->entry_data.type, "==", MMDB_DATA_TYPE_ARRAY,
"'map{mapX}{arrayX}' key's value is an array");
cmp_ok(arrayX->entry_data.data_size, "==", 3,
"'map{mapX}{arrayX}' key's value has 3 elements");
MMDB_entry_data_list_s *idx0 = entry_data_list = entry_data_list->next;
cmp_ok(idx0->entry_data.type, "==", MMDB_DATA_TYPE_UINT32,
"first array entry is a UINT32");
cmp_ok(idx0->entry_data.uint32, "==", 7, "first array entry value is 7");
MMDB_entry_data_list_s *idx1 = entry_data_list = entry_data_list->next;
cmp_ok(idx1->entry_data.type, "==", MMDB_DATA_TYPE_UINT32,
"second array entry is a UINT32");
cmp_ok(idx1->entry_data.uint32, "==", 8, "second array entry value is 8");
MMDB_entry_data_list_s *idx2 = entry_data_list = entry_data_list->next;
cmp_ok(idx2->entry_data.type, "==", MMDB_DATA_TYPE_UINT32,
"third array entry is a UINT32");
cmp_ok(idx2->entry_data.uint32, "==", 9, "third array entry value is 9");
return entry_data_list;
}
MMDB_entry_data_list_s *test_mapX_key_value_pair(MMDB_entry_data_list_s
*entry_data_list)
{
MMDB_entry_data_list_s *mapX_key = entry_data_list = entry_data_list->next;
cmp_ok(mapX_key->entry_data.type, "==", MMDB_DATA_TYPE_UTF8_STRING,
"found a map key in 'map{mapX}'");
const char *mapX_key_name = dup_entry_string_or_bail(mapX_key->entry_data);
if (strcmp(mapX_key_name, "utf8_stringX") == 0) {
MMDB_entry_data_list_s *mapX_value =
entry_data_list = entry_data_list->next;
cmp_ok(mapX_value->entry_data.type, "==", MMDB_DATA_TYPE_UTF8_STRING,
"'map{mapX}{utf8_stringX}' type is utf8_string");
const char *utf8_stringX_value = dup_entry_string_or_bail(
mapX_value->entry_data);
ok(strcmp(utf8_stringX_value, "hello") == 0,
"map{mapX}{utf8_stringX} value is 'hello'");
free((void *)utf8_stringX_value);
} else if (strcmp(mapX_key_name, "arrayX") == 0) {
entry_data_list = test_arrayX_value(entry_data_list);
} else {
ok(0, "unknown key found in map{mapX} - %s", mapX_key_name);
}
free((void *)mapX_key_name);
return entry_data_list;
}
MMDB_entry_data_list_s *test_map_value(MMDB_entry_data_list_s *entry_data_list)
{
MMDB_entry_data_list_s *map = entry_data_list = entry_data_list->next;
cmp_ok(map->entry_data.type, "==", MMDB_DATA_TYPE_MAP,
"'map' key's value is a map");
cmp_ok(map->entry_data.data_size, "==", 1,
"'map' key's value has 1 key/value pair");
MMDB_entry_data_list_s *map_key_1 = entry_data_list = entry_data_list->next;
cmp_ok(map_key_1->entry_data.type, "==", MMDB_DATA_TYPE_UTF8_STRING,
"found a map key in 'map'");
const char *map_key_1_name =
dup_entry_string_or_bail(map_key_1->entry_data);
ok(strcmp(map_key_1_name, "mapX") == 0, "key name is mapX");
free((void *)map_key_1_name);
MMDB_entry_data_list_s *mapX = entry_data_list = entry_data_list->next;
cmp_ok(mapX->entry_data.type, "==", MMDB_DATA_TYPE_MAP,
"'map{mapX}' key's value is a map");
cmp_ok(mapX->entry_data.data_size, "==", 2,
"'map' key's value has 2 key/value pairs");
entry_data_list = test_mapX_key_value_pair(entry_data_list);
entry_data_list = test_mapX_key_value_pair(entry_data_list);
return entry_data_list;
}
MMDB_entry_data_list_s *test_uint128_value(MMDB_entry_data_list_s
*entry_data_list)
{
MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next;
cmp_ok(value->entry_data.type, "==", MMDB_DATA_TYPE_UINT128,
"'uint128' key's value is an uint128");
#if MMDB_UINT128_IS_BYTE_ARRAY
uint8_t expect[16] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
ok(memcmp(value->entry_data.uint128, expect, 16) == 0,
"uint128 field is 2**120");
#else
mmdb_uint128_t expect = 1;
expect <<= 120;
cmp_ok(value->entry_data.uint128, "==", expect, "uint128 field is 2**120");
#endif
return entry_data_list;
}
MMDB_entry_data_list_s *test_uint16_value(MMDB_entry_data_list_s
*entry_data_list)
{
MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next;
cmp_ok(value->entry_data.type, "==", MMDB_DATA_TYPE_UINT16,
"'uint16' key's value is an uint16");
uint16_t expect = 100;
ok(value->entry_data.uint16 == expect, "uint16 field is 100");
return entry_data_list;
}
MMDB_entry_data_list_s *test_uint32_value(MMDB_entry_data_list_s
*entry_data_list)
{
MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next;
cmp_ok(value->entry_data.type, "==", MMDB_DATA_TYPE_UINT32,
"'uint32' key's value is an uint32");
uint32_t expect = 1 << 28;
cmp_ok(value->entry_data.uint32, "==", expect, "uint32 field is 100");
return entry_data_list;
}
MMDB_entry_data_list_s *test_uint64_value(MMDB_entry_data_list_s
*entry_data_list)
{
MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next;
cmp_ok(value->entry_data.type, "==", MMDB_DATA_TYPE_UINT64,
"'uint64' key's value is an uint64");
uint64_t expect = 1;
expect <<= 60;
cmp_ok(value->entry_data.uint64, "==", expect, "uint64 field is 2**60");
return entry_data_list;
}
MMDB_entry_data_list_s *test_utf8_string_value(MMDB_entry_data_list_s
*entry_data_list)
{
MMDB_entry_data_list_s *value = entry_data_list = entry_data_list->next;
cmp_ok(value->entry_data.type, "==", MMDB_DATA_TYPE_UTF8_STRING,
"'utf8_string' key's value is a string");
const char *utf8_string = dup_entry_string_or_bail(value->entry_data);
// This is hex for "unicode! ☯ - ♫" as bytes
char expect[19] =
{ 0x75, 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, 0x21, 0x20, 0xe2, 0x98,
0xaf, 0x20, 0x2d, 0x20, 0xe2, 0x99, 0xab, 0x00 };
is(utf8_string, expect, "got expected value for utf8_string key");
free((void *)utf8_string);
return entry_data_list;
}
void run_tests(int mode, const char *description)
{
const char *filename = "MaxMind-DB-test-decoder.mmdb";
const char *path = test_database_path(filename);
MMDB_s *mmdb = open_ok(path, mode, description);
free((void *)path);
char *ip = "1.1.1.1";
MMDB_lookup_result_s result =
lookup_string_ok(mmdb, ip, filename, description);
MMDB_entry_data_list_s *entry_data_list, *first;
int status = MMDB_get_entry_data_list(&result.entry, &entry_data_list);
if (MMDB_SUCCESS != status) {
BAIL_OUT("MMDB_get_entry_data_list failed with %s",
MMDB_strerror(status));
} else {
cmp_ok(status, "==", MMDB_SUCCESS,
"MMDB_get_entry_data_list succeeded");
}
first = entry_data_list;
cmp_ok(entry_data_list->entry_data.type, "==", MMDB_DATA_TYPE_MAP,
"first entry in entry data list is a map");
cmp_ok(entry_data_list->entry_data.data_size, "==", 12,
"first map in entry data list has 12 k/v pairs");
while (1) {
MMDB_entry_data_list_s *key = entry_data_list = entry_data_list->next;
if (!key) {
break;
}
cmp_ok(key->entry_data.type, "==", MMDB_DATA_TYPE_UTF8_STRING,
"found a map key");
const char *key_name = dup_entry_string_or_bail(key->entry_data);
if (strcmp(key_name, "array") == 0) {
entry_data_list = test_array_value(entry_data_list);
} else if (strcmp(key_name, "boolean") == 0) {
entry_data_list = test_boolean_value(entry_data_list);
} else if (strcmp(key_name, "bytes") == 0) {
entry_data_list = test_bytes_value(entry_data_list);
} else if (strcmp(key_name, "double") == 0) {
entry_data_list = test_double_value(entry_data_list);
} else if (strcmp(key_name, "float") == 0) {
entry_data_list = test_float_value(entry_data_list);
} else if (strcmp(key_name, "int32") == 0) {
entry_data_list = test_int32_value(entry_data_list);
} else if (strcmp(key_name, "map") == 0) {
entry_data_list = test_map_value(entry_data_list);
} else if (strcmp(key_name, "uint128") == 0) {
entry_data_list = test_uint128_value(entry_data_list);
} else if (strcmp(key_name, "uint16") == 0) {
entry_data_list = test_uint16_value(entry_data_list);
} else if (strcmp(key_name, "uint32") == 0) {
entry_data_list = test_uint32_value(entry_data_list);
} else if (strcmp(key_name, "uint64") == 0) {
entry_data_list = test_uint64_value(entry_data_list);
} else if (strcmp(key_name, "utf8_string") == 0) {
entry_data_list = test_utf8_string_value(entry_data_list);
} else {
ok(0, "unknown key found in map - %s", key_name);
}
free((void *)key_name);
}
MMDB_free_entry_data_list(first);
MMDB_close(mmdb);
free(mmdb);
}
int main(void)
{
plan(NO_PLAN);
for_all_modes(&run_tests);
done_testing();
}

439
module/Vendor/MaxmindDB/t/data_types_t.c vendored Normal file
View File

@ -0,0 +1,439 @@
#include "maxminddb_test_helper.h"
void test_all_data_types(MMDB_lookup_result_s *result, const char *ip,
const char *UNUSED(filename), const char *mode_desc)
{
{
char description[500];
snprintf(description, 500, "utf8_string field for %s - %s", ip,
mode_desc);
MMDB_entry_data_s data =
data_ok(result, MMDB_DATA_TYPE_UTF8_STRING, description,
"utf8_string", NULL);
const char *string = mmdb_strndup(data.utf8_string, data.data_size);
// This is hex for "unicode! ☯ - ♫" as bytes
char expect[19] =
{ 0x75, 0x6e, 0x69, 0x63, 0x6f, 0x64, 0x65, 0x21, 0x20, 0xe2, 0x98,
0xaf, 0x20, 0x2d, 0x20, 0xe2, 0x99, 0xab, 0x00 };
is(string, expect, "got expected utf8_string value");
free((char *)string);
}
{
char description[500];
snprintf(description, 500, "double field for %s - %s", ip, mode_desc);
MMDB_entry_data_s data =
data_ok(result, MMDB_DATA_TYPE_DOUBLE, description, "double", NULL);
compare_double(data.double_value, 42.123456);
}
{
char description[500];
snprintf(description, 500, "float field for %s - %s", ip, mode_desc);
MMDB_entry_data_s data =
data_ok(result, MMDB_DATA_TYPE_FLOAT, description, "float", NULL);
compare_float(data.float_value, 1.1F);
}
{
char description[500];
snprintf(description, 500, "bytes field for %s - %s", ip, mode_desc);
MMDB_entry_data_s data =
data_ok(result, MMDB_DATA_TYPE_BYTES, description, "bytes", NULL);
uint8_t expect[] = { 0x00, 0x00, 0x00, 0x2a };
ok(memcmp((uint8_t *)data.bytes, expect, 4) == 0,
"bytes field has expected value");
}
{
char description[500];
snprintf(description, 500, "uint16 field for %s - %s", ip, mode_desc);
MMDB_entry_data_s data =
data_ok(result, MMDB_DATA_TYPE_UINT16, description, "uint16", NULL);
uint16_t expect = 100;
ok(data.uint16 == expect, "uint16 field is 100");
}
{
char description[500];
snprintf(description, 500, "uint32 field for %s - %s", ip, mode_desc);
MMDB_entry_data_s data =
data_ok(result, MMDB_DATA_TYPE_UINT32, description, "uint32", NULL);
uint32_t expect = 1 << 28;
cmp_ok(data.uint32, "==", expect, "uint32 field is 2**28");
}
{
char description[500];
snprintf(description, 500, "int32 field for %s - %s", ip, mode_desc);
MMDB_entry_data_s data =
data_ok(result, MMDB_DATA_TYPE_INT32, description, "int32", NULL);
int32_t expect = 1 << 28;
expect *= -1;
cmp_ok(data.int32, "==", expect, "int32 field is -(2**28)");
}
{
char description[500];
snprintf(description, 500, "uint64 field for %s - %s", ip, mode_desc);
MMDB_entry_data_s data =
data_ok(result, MMDB_DATA_TYPE_UINT64, description, "uint64", NULL);
uint64_t expect = 1;
expect <<= 60;
cmp_ok(data.uint64, "==", expect, "uint64 field is 2**60");
}
{
char description[500];
snprintf(description, 500, "uint128 field for %s - %s", ip, mode_desc);
MMDB_entry_data_s data =
data_ok(result, MMDB_DATA_TYPE_UINT128, description, "uint128",
NULL);
#if MMDB_UINT128_IS_BYTE_ARRAY
uint8_t expect[16] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
ok(memcmp(data.uint128, expect, 16) == 0, "uint128 field is 2**120");
#else
mmdb_uint128_t expect = 1;
expect <<= 120;
cmp_ok(data.uint128, "==", expect, "uint128 field is 2**120");
#endif
}
{
char description[500];
snprintf(description, 500, "boolean field for %s - %s", ip, mode_desc);
MMDB_entry_data_s data =
data_ok(result, MMDB_DATA_TYPE_BOOLEAN, description, "boolean",
NULL);
cmp_ok(data.boolean, "==", true, "boolean field is true");
}
{
char description[500];
snprintf(description, 500, "array field for %s - %s", ip, mode_desc);
MMDB_entry_data_s data =
data_ok(result, MMDB_DATA_TYPE_ARRAY, description, "array", NULL);
ok(data.data_size == 3, "array field has 3 elements");
snprintf(description, 500, "array[0] for %s - %s", ip, mode_desc);
data =
data_ok(result, MMDB_DATA_TYPE_UINT32, description, "array", "0",
NULL);
ok(data.uint32 == 1, "array[0] is 1");
snprintf(description, 500, "array[1] for %s - %s", ip, mode_desc);
data =
data_ok(result, MMDB_DATA_TYPE_UINT32, description, "array", "1",
NULL);
ok(data.uint32 == 2, "array[1] is 1");
snprintf(description, 500, "array[2] for %s - %s", ip, mode_desc);
data =
data_ok(result, MMDB_DATA_TYPE_UINT32, description, "array", "2",
NULL);
ok(data.uint32 == 3, "array[2] is 1");
}
{
char description[500];
snprintf(description, 500, "map field for %s - %s", ip, mode_desc);
MMDB_entry_data_s data =
data_ok(result, MMDB_DATA_TYPE_MAP, description, "map", NULL);
ok(data.data_size == 1, "map field has 1 element");
snprintf(description, 500, "map{mapX} for %s - %s", ip, mode_desc);
data =
data_ok(result, MMDB_DATA_TYPE_MAP, description, "map", "mapX",
NULL);
ok(data.data_size == 2, "map{mapX} field has 2 elements");
snprintf(description, 500, "map{mapX}{utf8_stringX} for %s - %s", ip,
mode_desc);
data =
data_ok(result, MMDB_DATA_TYPE_UTF8_STRING, description, "map",
"mapX", "utf8_stringX", NULL);
const char *string = mmdb_strndup(data.utf8_string, data.data_size);
is(string, "hello", "map{mapX}{utf8_stringX} is 'hello'");
free((char *)string);
snprintf(description, 500, "map{mapX}{arrayX} for %s - %s", ip,
mode_desc);
data =
data_ok(result, MMDB_DATA_TYPE_ARRAY, description, "map", "mapX",
"arrayX", NULL);
ok(data.data_size == 3, "map{mapX}{arrayX} field has 3 elements");
snprintf(description, 500, "map{mapX}{arrayX}[0] for %s - %s", ip,
mode_desc);
data =
data_ok(result, MMDB_DATA_TYPE_UINT32, description, "map", "mapX",
"arrayX", "0", NULL);
ok(data.uint32 == 7, "map{mapX}{arrayX}[0] is 7");
snprintf(description, 500, "map{mapX}{arrayX}[1] for %s - %s", ip,
mode_desc);
data =
data_ok(result, MMDB_DATA_TYPE_UINT32, description, "map", "mapX",
"arrayX", "1", NULL);
ok(data.uint32 == 8, "map{mapX}{arrayX}[1] is 8");
snprintf(description, 500, "map{mapX}{arrayX}[2] for %s - %s", ip,
mode_desc);
data =
data_ok(result, MMDB_DATA_TYPE_UINT32, description, "map", "mapX",
"arrayX", "2", NULL);
ok(data.uint32 == 9, "map{mapX}{arrayX}[2] is 9");
}
}
void test_all_data_types_as_zero(MMDB_lookup_result_s *result, const char *ip,
const char *UNUSED(
filename), const char *mode_desc)
{
{
char description[500];
snprintf(description, 500, "utf8_string field for %s - %s", ip,
mode_desc);
MMDB_entry_data_s data =
data_ok(result, MMDB_DATA_TYPE_UTF8_STRING, description,
"utf8_string", NULL);
is(data.utf8_string, "", "got expected utf8_string value (NULL)");
}
{
char description[500];
snprintf(description, 500, "double field for %s - %s", ip, mode_desc);
MMDB_entry_data_s data =
data_ok(result, MMDB_DATA_TYPE_DOUBLE, description, "double", NULL);
compare_double(data.double_value, 0.0);
}
{
char description[500];
snprintf(description, 500, "float field for %s - %s", ip, mode_desc);
MMDB_entry_data_s data =
data_ok(result, MMDB_DATA_TYPE_FLOAT, description, "float", NULL);
compare_float(data.float_value, 0.0F);
}
{
char description[500];
snprintf(description, 500, "bytes field for %s - %s", ip, mode_desc);
MMDB_entry_data_s data =
data_ok(result, MMDB_DATA_TYPE_BYTES, description, "bytes", NULL);
ok(data.data_size == 0, "bytes field data_size is 0");
/* In C does it makes sense to write something like this?
uint8_t expect[0] = {};
ok(memcmp(data.bytes, expect, 0) == 0, "got expected bytes value (NULL)"); */
}
{
char description[500];
snprintf(description, 500, "uint16 field for %s - %s", ip, mode_desc);
MMDB_entry_data_s data =
data_ok(result, MMDB_DATA_TYPE_UINT16, description, "uint16", NULL);
uint16_t expect = 0;
ok(data.uint16 == expect, "uint16 field is 0");
}
{
char description[500];
snprintf(description, 500, "uint32 field for %s - %s", ip, mode_desc);
MMDB_entry_data_s data =
data_ok(result, MMDB_DATA_TYPE_UINT32, description, "uint32", NULL);
uint32_t expect = 0;
cmp_ok(data.uint32, "==", expect, "uint32 field is 0");
}
{
char description[500];
snprintf(description, 500, "int32 field for %s - %s", ip, mode_desc);
MMDB_entry_data_s data =
data_ok(result, MMDB_DATA_TYPE_INT32, description, "int32", NULL);
int32_t expect = 0;
expect *= -1;
cmp_ok(data.int32, "==", expect, "int32 field is 0");
}
{
char description[500];
snprintf(description, 500, "uint64 field for %s - %s", ip, mode_desc);
MMDB_entry_data_s data =
data_ok(result, MMDB_DATA_TYPE_UINT64, description, "uint64", NULL);
uint64_t expect = 0;
cmp_ok(data.uint64, "==", expect, "uint64 field is 0");
}
{
char description[500];
snprintf(description, 500, "uint128 field for %s - %s", ip, mode_desc);
MMDB_entry_data_s data =
data_ok(result, MMDB_DATA_TYPE_UINT128, description, "uint128",
NULL);
#if MMDB_UINT128_IS_BYTE_ARRAY
uint8_t expect[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
ok(memcmp(data.uint128, expect, 16) == 0, "uint128 field is 0");
#else
mmdb_uint128_t expect = 0;
cmp_ok(data.uint128, "==", expect, "uint128 field is 0");
#endif
}
{
char description[500];
snprintf(description, 500, "boolean field for %s - %s", ip, mode_desc);
MMDB_entry_data_s data =
data_ok(result, MMDB_DATA_TYPE_BOOLEAN, description, "boolean",
NULL);
cmp_ok(data.boolean, "==", false, "boolean field is false");
}
{
char description[500];
snprintf(description, 500, "array field for %s - %s", ip, mode_desc);
MMDB_entry_data_s data =
data_ok(result, MMDB_DATA_TYPE_ARRAY, description, "array", NULL);
ok(data.data_size == 0, "array field has 0 elements");
}
{
char description[500];
snprintf(description, 500, "map field for %s - %s", ip, mode_desc);
MMDB_entry_data_s data =
data_ok(result, MMDB_DATA_TYPE_MAP, description, "map", NULL);
ok(data.data_size == 0, "map field has 0 elements");
}
}
void run_tests(int mode, const char *mode_desc)
{
const char *filename = "MaxMind-DB-test-decoder.mmdb";
const char *path = test_database_path(filename);
MMDB_s *mmdb = open_ok(path, mode, mode_desc);
// All of the remaining tests require an open mmdb
if (NULL == mmdb) {
diag("could not open %s - skipping remaining tests", path);
return;
}
free((void *)path);
{
const char *ip = "not an ip";
int gai_error, mmdb_error;
MMDB_lookup_result_s result =
MMDB_lookup_string(mmdb, ip, &gai_error, &mmdb_error);
cmp_ok(gai_error, "==", EAI_NONAME,
"MMDB_lookup populates getaddrinfo error properly - %s", ip);
ok(!result.found_entry,
"no result entry struct returned for invalid IP address '%s'", ip);
}
{
const char *ip = "e900::";
MMDB_lookup_result_s result =
lookup_string_ok(mmdb, ip, filename, mode_desc);
ok(
!result.found_entry,
"no result entry struct returned for IP address not in the database - %s - %s - %s",
ip, filename, mode_desc);
}
{
const char *ip = "::1.1.1.1";
MMDB_lookup_result_s result =
lookup_string_ok(mmdb, ip, filename, mode_desc);
ok(
result.found_entry,
"got a result entry struct for IP address in the database - %s - %s - %s",
ip, filename, mode_desc);
cmp_ok(
result.entry.offset, ">", 0,
"result.entry.offset > 0 for address in the database - %s - %s - %s",
ip, filename, mode_desc);
test_all_data_types(&result, ip, filename, mode_desc);
}
{
const char *ip = "::4.5.6.7";
MMDB_lookup_result_s result =
lookup_string_ok(mmdb, ip, filename, mode_desc);
ok(
result.found_entry,
"got a result entry struct for IP address in the database - %s - %s - %s",
ip, filename, mode_desc);
cmp_ok(
result.entry.offset, ">", 0,
"result.entry.offset > 0 for address in the database - %s - %s - %s",
ip, filename, mode_desc);
test_all_data_types(&result, ip, filename, mode_desc);
}
{
const char *ip = "::0.0.0.0";
MMDB_lookup_result_s result =
lookup_string_ok(mmdb, ip, filename, mode_desc);
ok(
result.found_entry,
"got a result entry struct for IP address in the database - %s - %s - %s",
ip, filename, mode_desc);
test_all_data_types_as_zero(&result, ip, filename, mode_desc);
}
MMDB_close(mmdb);
free(mmdb);
}
int main(void)
{
plan(NO_PLAN);
for_all_modes(&run_tests);
done_testing();
}

103
module/Vendor/MaxmindDB/t/dump_t.c vendored Normal file
View File

@ -0,0 +1,103 @@
#define _XOPEN_SOURCE 700
#include "maxminddb_test_helper.h"
#ifdef HAVE_OPEN_MEMSTREAM
void run_tests(int mode, const char *mode_desc)
{
const char *filename = "MaxMind-DB-test-decoder.mmdb";
const char *path = test_database_path(filename);
MMDB_s *mmdb = open_ok(path, mode, mode_desc);
free((void *)path);
const char *ip = "1.1.1.1";
MMDB_lookup_result_s result =
lookup_string_ok(mmdb, ip, filename, mode_desc);
MMDB_entry_data_list_s *entry_data_list;
int status = MMDB_get_entry_data_list(&result.entry, &entry_data_list);
ok(MMDB_SUCCESS == status,
"MMDB_get_entry_data_list is successful");
char *dump_output;
size_t dump_size;
FILE *stream = open_memstream(&dump_output, &dump_size);
status = MMDB_dump_entry_data_list(stream, entry_data_list, 0);
fclose(stream);
MMDB_free_entry_data_list(entry_data_list);
ok(MMDB_SUCCESS == status,
"MMDB_dump_entry_data_list is successful - %s",
mode_desc);
cmp_ok(dump_size, ">", 0, "MMDB_dump produced output - %s", mode_desc);
char *expect[] = {
"{",
" \"array\": ",
" [",
" 1 <uint32>",
" 2 <uint32>",
" 3 <uint32>",
" ]",
" \"boolean\": ",
" true <boolean>",
" \"bytes\": ",
" 0000002A <bytes>",
" \"double\": ",
" 42.123456 <double>",
" \"float\": ",
" 1.100000 <float>",
" \"int32\": ",
" -268435456 <int32>",
" \"map\": ",
" {",
" \"mapX\": ",
" {",
" \"arrayX\": ",
" [",
" 7 <uint32>",
" 8 <uint32>",
" 9 <uint32>",
" ]",
" \"utf8_stringX\": ",
" \"hello\" <utf8_string>",
" }",
" }",
" \"uint128\": ",
" 0x01000000000000000000000000000000 <uint128>",
" \"uint16\": ",
" 100 <uint16>",
" \"uint32\": ",
" 268435456 <uint32>",
" \"uint64\": ",
" 1152921504606846976 <uint64>",
" \"utf8_string\": ",
" \"unicode! ☯ - ♫\" <utf8_string>",
"}"
};
for (int i = 0; i < 42; i++) {
ok((strstr(dump_output, expect[i]) != NULL),
"dump output contains expected line (%s) - %s", expect[i],
mode_desc);
}
free(dump_output);
MMDB_close(mmdb);
free(mmdb);
}
int main(void)
{
plan(NO_PLAN);
for_all_modes(&run_tests);
done_testing();
}
#else
int main(void)
{
plan(SKIP_ALL, "This test requires the open_memstream() function");
}
#endif

View File

@ -0,0 +1,106 @@
#!/usr/bin/env perl
use strict;
use warnings;
use FindBin qw( $Bin );
_skip_tests_if_required_modules_are_not_present();
_skip_tests_if_nm_is_not_present();
_test_libs_external_symbols();
done_testing();
sub _skip_tests_if_required_modules_are_not_present {
eval <<'EOF';
use Test::More 0.88;
use IPC::Run3 qw( run3 );
EOF
if ($@) {
print
"1..0 # skip all tests skipped - these tests need the Test::More 0.88, IPC::Run3 modules:\n";
print "$@";
exit 0;
}
}
sub _skip_tests_if_nm_is_not_present {
run3(
[ 'nm', '-V' ],
\undef,
\undef,
\undef,
);
my $exit_status = $? >> 8;
if ($exit_status) {
print
"1..0 # skipp all tests skipped - this test requires the command line utility `nm`.\n";
exit 0;
}
}
sub _test_libs_external_symbols {
my @libs = _libs_to_test();
if (@libs) {
for my $lib (@libs) {
_test_lib_external_symbols($lib);
}
}
else {
fail('No libs were found to test');
}
}
sub _libs_to_test {
my $lib_dir = "$Bin/../src/.libs";
opendir my $dh, $lib_dir
or die "Failed to open the lib dir at $lib_dir for reading: $!\n";
my @libs = map { $lib_dir . q{/} . $_ }
grep { $_ =~ m/\.so$/ } readdir $dh;
closedir $dh;
return @libs;
}
sub _test_lib_external_symbols {
my $lib = shift;
my $stdout;
my $stderr;
run3(
[ 'nm', '-g', '--defined-only', $lib ],
\undef,
\$stdout,
\$stderr,
);
my $exit_status = $? >> 8;
ok( !$exit_status, 'nm returned a non-error status' )
or diag($stderr);
my @external_symbols = _extract_external_symbols($stdout);
is_deeply(
[ grep { $_ !~ m/^MMDB_/ } @external_symbols ],
[],
"$lib exports only MMDB_ symbols"
);
}
sub _extract_external_symbols {
my $nm_output = shift;
my @lines = split /\r\n|\r|\n/, $nm_output;
my @external_symbols;
for my $line (@lines) {
my @fields = split /\s+/, $line;
die "Unexpected nm output for line $line\n"
if @fields != 3;
push @external_symbols, $fields[2];
}
return @external_symbols;
}

View File

@ -0,0 +1,66 @@
#include "maxminddb_test_helper.h"
/* This test exercises a bug found in MMDB_get_value for certain types of
* nested data structures which contain pointers. See
* https://github.com/maxmind/libmaxminddb/issues/2 and
* https://github.com/maxmind/libmaxminddb/issues/3.
*
* There is also the potential for a similar bug when looking up a value by
* path in an array. This is not tested (yet) as we don't have the right test
* data for it.
*
* These tests are somewhat fragile since they depend on a specific data
* layout in the database. Ideally the test would check that this layout
* exists before checking to see if the lookups are correct.
*/
void test_one_ip(MMDB_s *mmdb, const char *filename, const char *mode_desc,
char *ip, char *country_code)
{
MMDB_lookup_result_s result =
lookup_string_ok(mmdb, ip, filename, mode_desc);
MMDB_entry_data_s entry_data =
data_ok(&result, MMDB_DATA_TYPE_UTF8_STRING, "country{iso_code}",
"country", "iso_code", NULL);
if (ok(entry_data.has_data, "found data for country{iso_code}")) {
char *string = mmdb_strndup(entry_data.utf8_string, entry_data.data_size);
if (!string) {
ok(0, "mmdb_strndup() call failed");
exit(1);
}
if (!ok(strcmp(string,
country_code) == 0, "iso_code is %s", country_code)) {
diag(" value is %s", string);
}
free(string);
}
}
void run_tests(int mode, const char *mode_desc)
{
const char *filename = "GeoIP2-City-Test.mmdb";
const char *path = test_database_path(filename);
MMDB_s *mmdb = open_ok(path, mode, mode_desc);
free((void *)path);
/* This exercises a bug where the entire top-level value is a pointer to
* another part of the data section. */
test_one_ip(mmdb, filename, mode_desc, "2001:218::", "JP");
/* This exercises a bug where one subnet's data shares part of the data
* with another subnet - in this case it is the "country" key (and others)
* in the top level map. We are testing that the "country" key's value is
* handled correctly. The value _should_ be a pointer to another map. */
test_one_ip(mmdb, filename, mode_desc, "81.2.69.160", "GB");
MMDB_close(mmdb);
free(mmdb);
}
int main(void)
{
plan(NO_PLAN);
for_all_modes(&run_tests);
done_testing();
}

313
module/Vendor/MaxmindDB/t/get_value_t.c vendored Normal file
View File

@ -0,0 +1,313 @@
#include "maxminddb_test_helper.h"
void test_array_0_result(int status, MMDB_entry_data_s entry_data,
char *function)
{
cmp_ok(status, "==", MMDB_SUCCESS,
"status for %s() is MMDB_SUCCESS - array[0]", function);
ok(entry_data.has_data, "found a value for array[0]");
cmp_ok(entry_data.type, "==", MMDB_DATA_TYPE_UINT32,
"returned entry type is uint32 - array[0]");
cmp_ok(entry_data.uint32, "==", 1, "entry value is 1 - array[0]");
}
void test_array_2_result(int status, MMDB_entry_data_s entry_data,
char *function)
{
cmp_ok(status, "==", MMDB_SUCCESS,
"status for %s() is MMDB_SUCCESS - array[2]", function);
ok(entry_data.has_data, "found a value for array[2]");
cmp_ok(entry_data.type, "==", MMDB_DATA_TYPE_UINT32,
"returned entry type is uint32 - array[2]");
cmp_ok(entry_data.uint32, "==", 3, "entry value is 3 - array[2]");
}
void test_array_minus_3_result(int status, MMDB_entry_data_s entry_data,
char *function)
{
cmp_ok(status, "==", MMDB_SUCCESS,
"status for %s() is MMDB_SUCCESS - array[-3]", function);
ok(entry_data.has_data, "found a value for array[-3]");
cmp_ok(entry_data.type, "==", MMDB_DATA_TYPE_UINT32,
"returned entry type is uint32 - array[-3]");
cmp_ok(entry_data.uint32, "==", 1, "entry value is 1 - array[-3]");
}
void test_array_minus_1_result(int status, MMDB_entry_data_s entry_data,
char *function)
{
cmp_ok(status, "==", MMDB_SUCCESS,
"status for %s() is MMDB_SUCCESS - array[-1]", function);
ok(entry_data.has_data, "found a value for array[-1]");
cmp_ok(entry_data.type, "==", MMDB_DATA_TYPE_UINT32,
"returned entry type is uint32 - array[-1]");
cmp_ok(entry_data.uint32, "==", 3, "entry value is 3 - array[-1]");
}
int call_vget_value(MMDB_entry_s *entry, MMDB_entry_data_s *entry_data, ...)
{
va_list keys;
va_start(keys, entry_data);
int status = MMDB_vget_value(entry, entry_data, keys);
va_end(keys);
return status;
}
void test_simple_structure(int mode, const char *mode_desc)
{
const char *filename = "MaxMind-DB-test-decoder.mmdb";
const char *path = test_database_path(filename);
MMDB_s *mmdb = open_ok(path, mode, mode_desc);
free((void *)path);
const char *ip = "1.1.1.1";
MMDB_lookup_result_s result =
lookup_string_ok(mmdb, ip, filename, mode_desc);
{
MMDB_entry_data_s entry_data;
const char *lookup_path[] = { "array", "0", NULL };
int status = MMDB_aget_value(&result.entry, &entry_data, lookup_path);
test_array_0_result(status, entry_data, "MMDB_aget_value");
status = MMDB_get_value(&result.entry, &entry_data, "array", "0", NULL);
test_array_0_result(status, entry_data, "MMDB_get_value");
status =
call_vget_value(&result.entry, &entry_data, "array", "0", NULL);
test_array_0_result(status, entry_data, "MMDB_vget_value");
}
{
MMDB_entry_data_s entry_data;
const char *lookup_path[] = { "array", "2", NULL };
int status = MMDB_aget_value(&result.entry, &entry_data, lookup_path);
test_array_2_result(status, entry_data, "MMDB_aget_value");
status = MMDB_get_value(&result.entry, &entry_data, "array", "2", NULL);
test_array_2_result(status, entry_data, "MMDB_get_value");
status =
call_vget_value(&result.entry, &entry_data, "array", "2", NULL);
test_array_2_result(status, entry_data, "MMDB_vget_value");
}
{
MMDB_entry_data_s entry_data;
int status = MMDB_get_value(&result.entry, &entry_data, "array", "zero",
NULL);
cmp_ok(status, "==", MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR,
"MMDB_get_value() returns error on non-integer array index");
}
{
MMDB_entry_data_s entry_data;
const char *lookup_path[] = { "array", "-1", NULL };
int status = MMDB_aget_value(&result.entry, &entry_data, lookup_path);
test_array_minus_1_result(status, entry_data, "MMDB_aget_value");
status =
MMDB_get_value(&result.entry, &entry_data, "array", "-1", NULL);
test_array_minus_1_result(status, entry_data, "MMDB_get_value");
status =
call_vget_value(&result.entry, &entry_data, "array", "-1", NULL);
test_array_minus_1_result(status, entry_data, "MMDB_vget_value");
}
{
MMDB_entry_data_s entry_data;
const char *lookup_path[] = { "array", "-3", NULL };
int status = MMDB_aget_value(&result.entry, &entry_data, lookup_path);
test_array_minus_3_result(status, entry_data, "MMDB_aget_value");
status =
MMDB_get_value(&result.entry, &entry_data, "array", "-3", NULL);
test_array_minus_3_result(status, entry_data, "MMDB_get_value");
status =
call_vget_value(&result.entry, &entry_data, "array", "-3", NULL);
test_array_minus_3_result(status, entry_data, "MMDB_vget_value");
}
{
MMDB_entry_data_s entry_data;
int status = MMDB_get_value(&result.entry, &entry_data, "array", "-4",
NULL);
cmp_ok(status, "==", MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR,
"MMDB_get_value() returns error on too large negative integer");
}
{
MMDB_entry_data_s entry_data;
int status =
MMDB_get_value(&result.entry, &entry_data, "array",
"-18446744073709551616",
NULL);
cmp_ok(status, "==", MMDB_INVALID_LOOKUP_PATH_ERROR,
"MMDB_get_value() returns error on integer smaller than LONG_MIN");
}
{
MMDB_entry_data_s entry_data;
int status =
MMDB_get_value(&result.entry, &entry_data, "array",
"18446744073709551616",
NULL);
cmp_ok(status, "==", MMDB_INVALID_LOOKUP_PATH_ERROR,
"MMDB_get_value() returns error on integer larger than LONG_MAX");
}
MMDB_close(mmdb);
free(mmdb);
}
void test_complex_map_a_result(int status, MMDB_entry_data_s entry_data,
char *function)
{
cmp_ok(status, "==", MMDB_SUCCESS,
"status for %s() is MMDB_SUCCESS - map1{map2}{array}[0]{map3}{a}",
function);
ok(entry_data.has_data,
"found a value for map1{map2}{array}[0]{map3}{a}");
cmp_ok(entry_data.type, "==", MMDB_DATA_TYPE_UINT32,
"returned entry type is uint32 - map1{map2}{array}[0]{map3}{a}");
cmp_ok(entry_data.uint32, "==", 1,
"entry value is 1 - map1{map2}{array}[0]{map3}{a}");
}
void test_complex_map_c_result(int status, MMDB_entry_data_s entry_data,
char *function)
{
cmp_ok(
status, "==", MMDB_SUCCESS,
"status for %s() is MMDB_SUCCESS - map1{map2}{array}[0]{map3}{c}",
function);
ok(entry_data.has_data,
"found a value for map1{map2}{array}[0]{map3}{c}");
cmp_ok(entry_data.type, "==", MMDB_DATA_TYPE_UINT32,
"returned entry type is uint32 - map1{map2}{array}[0]{map3}{c}");
cmp_ok(entry_data.uint32, "==", 3,
"entry value is 3 - map1{map2}{array}[0]{map3}{c}");
}
void test_no_result(int status, MMDB_entry_data_s entry_data, char *function,
char *path_description)
{
cmp_ok(status, "==", MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR,
"status for %s() is MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR - %s",
function, path_description);
ok(!entry_data.has_data, "did not find a value for %s", path_description);
}
void test_nested_structure(int mode, const char *mode_desc)
{
const char *filename = "MaxMind-DB-test-nested.mmdb";
const char *path = test_database_path(filename);
MMDB_s *mmdb = open_ok(path, mode, mode_desc);
free((void *)path);
const char *ip = "1.1.1.1";
MMDB_lookup_result_s result =
lookup_string_ok(mmdb, ip, filename, mode_desc);
{
MMDB_entry_data_s entry_data;
const char *lookup_path[] =
{ "map1", "map2", "array", "0", "map3", "a", NULL };
int status = MMDB_aget_value(&result.entry, &entry_data, lookup_path);
test_complex_map_a_result(status, entry_data, "MMDB_aget_value");
status = MMDB_get_value(&result.entry, &entry_data,
"map1", "map2", "array", "0", "map3", "a",
NULL);
test_complex_map_a_result(status, entry_data, "MMDB_get_value");
status = call_vget_value(&result.entry, &entry_data,
"map1", "map2", "array", "0", "map3", "a",
NULL);
test_complex_map_a_result(status, entry_data, "MMDB_vget_value");
}
{
MMDB_entry_data_s entry_data;
const char *lookup_path[] =
{ "map1", "map2", "array", "0", "map3", "c", NULL };
int status = MMDB_aget_value(&result.entry, &entry_data, lookup_path);
test_complex_map_c_result(status, entry_data, "MMDB_aget_value");
status = MMDB_get_value(&result.entry, &entry_data,
"map1", "map2", "array", "0", "map3", "c",
NULL);
test_complex_map_c_result(status, entry_data, "MMDB_get_value");
status = call_vget_value(&result.entry, &entry_data,
"map1", "map2", "array", "0", "map3", "c",
NULL);
test_complex_map_c_result(status, entry_data, "MMDB_vget_value");
}
{
MMDB_entry_data_s entry_data;
const char *lookup_path[] =
{ "map1", "map42", "array", "0", "map3", "c", NULL };
int status = MMDB_aget_value(&result.entry, &entry_data, lookup_path);
test_no_result(status, entry_data, "MMDB_aget_value",
"map1{map42}{array}[0]{map3}{c}");
status = MMDB_get_value(&result.entry, &entry_data,
"map1", "map42", "array", "0", "map3", "c",
NULL);
test_no_result(status, entry_data, "MMDB_get_value",
"map1{map42}{array}[0]{map3}{c}");
status = call_vget_value(&result.entry, &entry_data,
"map1", "map42", "array", "0", "map3", "c",
NULL);
test_no_result(status, entry_data, "MMDB_vget_value",
"map1{map42}{array}[0]{map3}{c}");
}
{
MMDB_entry_data_s entry_data;
const char *lookup_path[] =
{ "map1", "map2", "array", "9", "map3", "c", NULL };
int status = MMDB_aget_value(&result.entry, &entry_data, lookup_path);
test_no_result(status, entry_data, "MMDB_aget_value",
"map1{map42}{array}[9]{map3}{c}");
status = MMDB_get_value(&result.entry, &entry_data,
"map1", "map2", "array", "9", "map3", "c",
NULL);
test_no_result(status, entry_data, "MMDB_get_value",
"map1{map42}{array}[9]{map3}{c}");
status = call_vget_value(&result.entry, &entry_data,
"map1", "map2", "array", "9", "map3", "c",
NULL);
test_no_result(status, entry_data, "MMDB_vget_value",
"map1{map42}{array}[9]{map3}{c}");
}
MMDB_close(mmdb);
free(mmdb);
}
void run_tests(int mode, const char *mode_desc)
{
test_simple_structure(mode, mode_desc);
test_nested_structure(mode, mode_desc);
}
int main(void)
{
plan(NO_PLAN);
for_all_modes(&run_tests);
done_testing();
}

View File

@ -0,0 +1,36 @@
#include "maxminddb_test_helper.h"
void test_one_ip(MMDB_s *mmdb, const char *ip, const char *filename,
const char *mode_desc)
{
MMDB_lookup_result_s result =
lookup_string_ok(mmdb, ip, filename, mode_desc);
ok(
result.found_entry,
"got a result for an IPv4 address included in a larger-than-IPv4 subnet - %s - %s",
ip, mode_desc);
data_ok(&result, MMDB_DATA_TYPE_UTF8_STRING, "string value for IP", NULL);
}
void run_tests(int mode, const char *mode_desc)
{
const char *filename = "MaxMind-DB-no-ipv4-search-tree.mmdb";
const char *path = test_database_path(filename);
MMDB_s *mmdb = open_ok(path, mode, mode_desc);
free((void *)path);
test_one_ip(mmdb, "1.1.1.1", filename, mode_desc);
test_one_ip(mmdb, "255.255.255.255", filename, mode_desc);
MMDB_close(mmdb);
free(mmdb);
}
int main(void)
{
plan(NO_PLAN);
for_all_modes(&run_tests);
done_testing();
}

View File

@ -0,0 +1,48 @@
#include "maxminddb_test_helper.h"
void run_tests(int mode, const char *mode_desc)
{
const char *filename = "MaxMind-DB-test-ipv4-28.mmdb";
const char *path = test_database_path(filename);
MMDB_s *mmdb = open_ok(path, mode, mode_desc);
free((void *)path);
const char *ip = "::abcd";
int gai_error, mmdb_error;
MMDB_lookup_result_s UNUSED(result) =
MMDB_lookup_string(mmdb, ip, &gai_error, &mmdb_error);
cmp_ok(
mmdb_error, "==", MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR,
"MMDB_lookup_string sets mmdb_error to MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR when we try to look up an IPv6 address in an IPv4-only database");
struct addrinfo hints = {
.ai_family = AF_INET6,
.ai_flags = AI_NUMERICHOST
};
struct addrinfo *addresses;
gai_error = getaddrinfo("2001:db8:85a3:0:0:8a2e:370:7334", NULL,
&hints, &addresses);
if (gai_error) {
BAIL_OUT("getaddrinfo failed: %s", gai_strerror(gai_error));
}
mmdb_error = 0;
MMDB_lookup_sockaddr(mmdb, addresses->ai_addr, &mmdb_error);
cmp_ok(
mmdb_error, "==", MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR,
"MMDB_lookup_sockaddr sets mmdb_error to MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR when we try to look up an IPv6 address in an IPv4-only database");
freeaddrinfo(addresses);
MMDB_close(mmdb);
free(mmdb);
}
int main(void)
{
plan(NO_PLAN);
for_all_modes(&run_tests);
done_testing();
}

View File

@ -0,0 +1,232 @@
#if HAVE_CONFIG_H
#include <config.h>
#endif
#define _POSIX_C_SOURCE 200112L
#include <assert.h>
#include <stdarg.h>
#include <sys/types.h>
#include "maxminddb.h"
#include "maxminddb_test_helper.h"
#ifdef _WIN32
#include <io.h>
#else
#include <libgen.h>
#include <unistd.h>
#endif
void for_all_record_sizes(const char *filename_fmt,
void (*tests)(int record_size, const char *filename,
const char *description))
{
int sizes[] = { 24, 28, 32 };
for (int i = 0; i < 3; i++) {
int size = sizes[i];
char filename[500];
snprintf(filename, 500, filename_fmt, size);
char description[14];
snprintf(description, 14, "%i bit record", size);
tests(size, filename, description);
}
}
void for_all_modes(void (*tests)(int mode, const char *description))
{
tests(MMDB_MODE_MMAP, "mmap mode");
}
const char *test_database_path(const char *filename)
{
char cwd[500];
char *UNUSED(tmp) = getcwd(cwd, 500);
char *test_db_dir;
#ifdef _WIN32
test_db_dir = "./t/maxmind-db/test-data";
#else
if (strcmp(basename(cwd), "t") == 0) {
test_db_dir = "./maxmind-db/test-data";
} else {
test_db_dir = "./t/maxmind-db/test-data";
}
#endif
char *path = malloc(500);
assert(NULL != path);
snprintf(path, 500, "%s/%s", test_db_dir, filename);
return (const char *)path;
}
const char *dup_entry_string_or_bail(MMDB_entry_data_s entry_data)
{
const char *string = mmdb_strndup(entry_data.utf8_string, entry_data.data_size);
if (NULL == string) {
BAIL_OUT("mmdb_strndup failed");
}
return string;
}
MMDB_s *open_ok(const char *db_file, int mode, const char *mode_desc)
{
if (0 != access(db_file, R_OK)) {
BAIL_OUT(
"could not read the specified file - %s\nIf you are in a git checkout try running 'git submodule update --init'",
db_file);
}
MMDB_s *mmdb = (MMDB_s *)calloc(1, sizeof(MMDB_s));
if (NULL == mmdb) {
BAIL_OUT("could not allocate memory for our MMDB_s struct");
}
int status = MMDB_open(db_file, mode, mmdb);
int is_ok = ok(MMDB_SUCCESS == status, "open %s status is success - %s",
db_file, mode_desc);
if (!is_ok) {
diag("open status code = %d (%s)", status, MMDB_strerror(status));
free(mmdb);
return NULL;
}
is_ok = ok(mmdb->file_size > 0,
"mmdb struct has been set for %s - %s",
db_file, mode_desc);
if (!is_ok) {
free(mmdb);
return NULL;
}
return mmdb;
}
MMDB_lookup_result_s lookup_string_ok(MMDB_s *mmdb, const char *ip,
const char *file, const char *mode_desc)
{
int gai_error, mmdb_error;
MMDB_lookup_result_s result =
MMDB_lookup_string(mmdb, ip, &gai_error, &mmdb_error);
test_lookup_errors(gai_error, mmdb_error, "MMDB_lookup_string", ip, file,
mode_desc);
return result;
}
MMDB_lookup_result_s lookup_sockaddr_ok(MMDB_s *mmdb, const char *ip,
const char *file, const char *mode_desc)
{
int ai_flags = AI_NUMERICHOST;
struct addrinfo hints = {
.ai_socktype = SOCK_STREAM
};
struct addrinfo *addresses = NULL;
if (ip[0] == ':') {
hints.ai_flags = ai_flags;
#if defined AI_V4MAPPED && !defined __FreeBSD__
hints.ai_flags |= AI_V4MAPPED;
#endif
hints.ai_family = AF_INET6;
} else {
hints.ai_flags = ai_flags;
hints.ai_family = AF_INET;
}
int gai_error = getaddrinfo(ip, NULL, &hints, &addresses);
int mmdb_error = 0;
MMDB_lookup_result_s result = { .found_entry = false };
if (gai_error == 0) {
result = MMDB_lookup_sockaddr(mmdb, addresses->ai_addr, &mmdb_error);
}
if (NULL != addresses) {
freeaddrinfo(addresses);
}
test_lookup_errors(gai_error, mmdb_error, "MMDB_lookup_sockaddr", ip, file,
mode_desc);
return result;
}
void test_lookup_errors(int gai_error, int mmdb_error,
const char *function, const char *ip,
const char *file, const char *mode_desc)
{
int is_ok = ok(0 == gai_error,
"no getaddrinfo error in call to %s for %s - %s - %s",
function, ip, file, mode_desc);
if (!is_ok) {
diag("error from call to getaddrinfo for %s - %s",
ip, gai_strerror(gai_error));
}
is_ok = ok(0 == mmdb_error,
"no MMDB error in call to %s for %s - %s - %s",
function, ip, file, mode_desc);
if (!is_ok) {
diag("MMDB error - %s", MMDB_strerror(mmdb_error));
}
}
MMDB_entry_data_s data_ok(MMDB_lookup_result_s *result, uint32_t expect_type,
const char *description, ...)
{
va_list keys;
va_start(keys, description);
MMDB_entry_data_s data;
int status = MMDB_vget_value(&result->entry, &data, keys);
va_end(keys);
if (cmp_ok(status, "==", MMDB_SUCCESS,
"no error from call to MMDB_vget_value - %s", description)) {
if (!cmp_ok(data.type, "==", expect_type,
"got the expected data type - %s", description)) {
diag(" data type value is %i but expected %i", data.type,
expect_type);
}
} else {
diag(" error from MMDB_vget_value - %s", MMDB_strerror(status));
}
return data;
}
void compare_double(double got, double expect)
{
double diff = fabs(got - expect);
int is_ok = ok(diff < 0.01, "double value was approximately %2.6f", expect);
if (!is_ok) {
diag(" got %2.6f but expected %2.6f (diff = %2.6f)",
got, expect, diff);
}
}
void compare_float(float got, float expect)
{
float diff = fabsf(got - expect);
int is_ok = ok(diff < 0.01, "float value was approximately %2.1f", expect);
if (!is_ok) {
diag(" got %2.4f but expected %2.1f (diff = %2.1f)",
got, expect, diff);
}
}

View File

@ -0,0 +1,66 @@
/* Some test files may require something newer */
#if !defined(_GNU_SOURCE) && !defined(_POSIX_C_SOURCE)
#define _POSIX_C_SOURCE 200112L
#endif
#if HAVE_CONFIG_H
#include <config.h>
#endif
#include <math.h>
#include <stdio.h>
#include <string.h>
#include "maxminddb.h"
#include "maxminddb-compat-util.h"
#include "libtap/tap.h"
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#define R_OK 4
#else
#include <netdb.h>
#endif
#if (_MSC_VER && _MSC_VER < 1900)
/* _snprintf has security issues, but I don't think it is worth
worrying about for the unit tests. */
#define snprintf _snprintf
#endif
#ifndef MMDB_TEST_HELPER_C
#define MMDB_TEST_HELPER_C (1)
#ifdef __GNUC__
# define UNUSED(x) UNUSED_ ## x __attribute__((__unused__))
#else
# define UNUSED
#endif
#define MAX_DESCRIPTION_LENGTH 500
extern void for_all_record_sizes(const char *filename_fmt,
void (*tests)(int record_size,
const char *filename,
const char *description));
extern void for_all_modes(void (*tests)(int mode, const char *description));
extern const char *test_database_path(const char *filename);
extern const char *dup_entry_string_or_bail(MMDB_entry_data_s entry_data);
extern MMDB_s *open_ok(const char *db_file, int mode, const char *mode_desc);
extern MMDB_lookup_result_s lookup_string_ok(MMDB_s *mmdb, const char *ip,
const char *file,
const char *mode_desc);
extern MMDB_lookup_result_s lookup_sockaddr_ok(MMDB_s *mmdb, const char *ip,
const char *file,
const char *mode_desc);
extern void test_lookup_errors(int gai_error, int mmdb_error,
const char *function, const char *ip,
const char *file, const char *mode_desc);
extern MMDB_entry_data_s data_ok(MMDB_lookup_result_s *result,
uint32_t expect_type,
const char *description, ...);
extern void compare_double(double got, double expect);
extern void compare_float(float got, float expect);
#endif

View File

@ -0,0 +1,32 @@
#include "maxminddb_test_helper.h"
void run_tests(int mode, const char *mode_desc)
{
const char *filename = "MaxMind-DB-test-metadata-pointers.mmdb";
const char *path = test_database_path(filename);
MMDB_s *mmdb = open_ok(path, mode, mode_desc);
free((void *)path);
char *repeated_string = "Lots of pointers in metadata";
is(mmdb->metadata.database_type, repeated_string,
"decoded pointer database_type");
for (uint16_t i = 0; i < mmdb->metadata.description.count; i++) {
const char *language =
mmdb->metadata.description.descriptions[i]->language;
const char *description =
mmdb->metadata.description.descriptions[i]->description;
is(description, repeated_string, "%s description", language);
}
MMDB_close(mmdb);
free(mmdb);
}
int main(void)
{
plan(NO_PLAN);
for_all_modes(&run_tests);
done_testing();
}

226
module/Vendor/MaxmindDB/t/metadata_t.c vendored Normal file
View File

@ -0,0 +1,226 @@
#include "maxminddb_test_helper.h"
void test_metadata(MMDB_s *mmdb, const char *mode_desc)
{
cmp_ok(mmdb->metadata.node_count, "==", 37, "node_count is 37 - %s",
mode_desc);
cmp_ok(mmdb->metadata.record_size, "==", 24, "record_size is 24 - %s",
mode_desc);
cmp_ok(mmdb->metadata.ip_version, "==", 4, "ip_version is 4 - %s",
mode_desc);
is(mmdb->metadata.database_type, "Test", "database_type is Test - %s",
mode_desc);
// 2013-07-01T00:00:00Z
uint64_t expect_epoch = 1372636800;
int is_ok =
cmp_ok(mmdb->metadata.build_epoch, ">=", expect_epoch,
"build_epoch > %lli", expect_epoch);
if (!is_ok) {
diag(" epoch is %lli", mmdb->metadata.build_epoch);
}
cmp_ok(mmdb->metadata.binary_format_major_version, "==", 2,
"binary_format_major_version is 2 - %s", mode_desc);
cmp_ok(mmdb->metadata.binary_format_minor_version, "==", 0,
"binary_format_minor_version is 0 - %s", mode_desc);
cmp_ok(mmdb->metadata.languages.count, "==", 2, "found 2 languages - %s",
mode_desc);
is(mmdb->metadata.languages.names[0], "en", "first language is en - %s",
mode_desc);
is(mmdb->metadata.languages.names[1], "zh", "second language is zh - %s",
mode_desc);
cmp_ok(mmdb->metadata.description.count, "==", 2,
"found 2 descriptions - %s", mode_desc);
for (uint16_t i = 0; i < mmdb->metadata.description.count; i++) {
const char *language =
mmdb->metadata.description.descriptions[i]->language;
const char *description =
mmdb->metadata.description.descriptions[i]->description;
if (strncmp(language, "en", 2) == 0) {
ok(1, "found en description");
is(description, "Test Database", "en description");
} else if (strncmp(language, "zh", 2) == 0) {
ok(1, "found zh description");
is(description, "Test Database Chinese", "zh description");
} else {
ok(0, "found unknown description in unexpected language - %s",
language);
}
}
cmp_ok(mmdb->full_record_byte_size, "==", 6,
"full_record_byte_size is 6 - %s", mode_desc);
}
MMDB_entry_data_list_s *test_languages_value(MMDB_entry_data_list_s
*entry_data_list)
{
MMDB_entry_data_list_s *languages = entry_data_list = entry_data_list->next;
cmp_ok(languages->entry_data.type, "==", MMDB_DATA_TYPE_ARRAY,
"'languages' key's value is an array");
cmp_ok(languages->entry_data.data_size, "==", 2,
"'languages' key's value has 2 elements");
MMDB_entry_data_list_s *idx0 = entry_data_list = entry_data_list->next;
cmp_ok(idx0->entry_data.type, "==", MMDB_DATA_TYPE_UTF8_STRING,
"first array entry is a UTF8_STRING");
const char *lang0 = dup_entry_string_or_bail(idx0->entry_data);
is(lang0, "en", "first language is en");
free((void *)lang0);
MMDB_entry_data_list_s *idx1 = entry_data_list = entry_data_list->next;
cmp_ok(idx1->entry_data.type, "==", MMDB_DATA_TYPE_UTF8_STRING,
"second array entry is a UTF8_STRING");
const char *lang1 = dup_entry_string_or_bail(idx1->entry_data);
is(lang1, "zh", "second language is zh");
free((void *)lang1);
return entry_data_list;
}
MMDB_entry_data_list_s *test_description_value(
MMDB_entry_data_list_s *entry_data_list)
{
MMDB_entry_data_list_s *description = entry_data_list =
entry_data_list->next;
cmp_ok(description->entry_data.type, "==", MMDB_DATA_TYPE_MAP,
"'description' key's value is a map");
cmp_ok(description->entry_data.data_size, "==", 2,
"'description' key's value has 2 key/value pairs");
for (int i = 0; i < 2; i++) {
MMDB_entry_data_list_s *key = entry_data_list =
entry_data_list->next;
cmp_ok(key->entry_data.type, "==",
MMDB_DATA_TYPE_UTF8_STRING,
"found a map key in 'map'");
const char *key_name = dup_entry_string_or_bail(key->entry_data);
MMDB_entry_data_list_s *value = entry_data_list =
entry_data_list->next;
cmp_ok(value->entry_data.type, "==", MMDB_DATA_TYPE_UTF8_STRING,
"map value is a UTF8_STRING");
const char *description =
dup_entry_string_or_bail(value->entry_data);
if (strcmp(key_name, "en") == 0) {
is(description, "Test Database",
"en description == 'Test Database'");
} else if (strcmp(key_name, "zh") == 0) {
is(description, "Test Database Chinese",
"zh description == 'Test Database Chinese'");
} else {
ok(0, "unknown key found in description map - %s", key_name);
}
free((void *)key_name);
free((void *)description);
}
return entry_data_list;
}
void test_metadata_as_data_entry_list(MMDB_s * mmdb,
const char *mode_desc)
{
MMDB_entry_data_list_s *entry_data_list, *first;
int status =
MMDB_get_metadata_as_entry_data_list(mmdb, &entry_data_list);
first = entry_data_list;
cmp_ok(status, "==", MMDB_SUCCESS, "get metadata as data_entry_list - %s",
mode_desc);
cmp_ok(first->entry_data.data_size, "==", 9,
"metadata map has 9 key/value pairs");
while (1) {
MMDB_entry_data_list_s *key = entry_data_list =
entry_data_list->next;
if (!key) {
break;
}
cmp_ok(key->entry_data.type, "==", MMDB_DATA_TYPE_UTF8_STRING,
"found a map key");
const char *key_name = dup_entry_string_or_bail(key->entry_data);
if (strcmp(key_name, "node_count") == 0) {
MMDB_entry_data_list_s *value
= entry_data_list = entry_data_list->next;
cmp_ok(value->entry_data.uint32, "==", 37, "node_count == 37");
} else if (strcmp(key_name, "record_size") == 0) {
MMDB_entry_data_list_s *value
= entry_data_list = entry_data_list->next;
cmp_ok(value->entry_data.uint16, "==", 24, "record_size == 24");
} else if (strcmp(key_name, "ip_version") == 0) {
MMDB_entry_data_list_s *value
= entry_data_list = entry_data_list->next;
cmp_ok(value->entry_data.uint16, "==", 4, "ip_version == 4");
} else if (strcmp(key_name, "binary_format_major_version") == 0) {
MMDB_entry_data_list_s *value
= entry_data_list = entry_data_list->next;
cmp_ok(value->entry_data.uint16, "==", 2,
"binary_format_major_version == 2");
} else if (strcmp(key_name, "binary_format_minor_version") == 0) {
MMDB_entry_data_list_s *value
= entry_data_list = entry_data_list->next;
cmp_ok(value->entry_data.uint16, "==", 0,
"binary_format_minor_version == 0");
} else if (strcmp(key_name, "build_epoch") == 0) {
MMDB_entry_data_list_s *value
= entry_data_list = entry_data_list->next;
ok(value->entry_data.uint64 > 1373571901,
"build_epoch > 1373571901");
} else if (strcmp(key_name, "database_type") == 0) {
MMDB_entry_data_list_s *value
= entry_data_list = entry_data_list->next;
const char *type = dup_entry_string_or_bail(value->entry_data);
is(type, "Test", "type == Test");
free((void *)type);
} else if (strcmp(key_name, "languages") == 0) {
entry_data_list = test_languages_value(entry_data_list);
} else if (strcmp(key_name, "description") == 0) {
entry_data_list = test_description_value(entry_data_list);
} else {
ok(0, "unknown key found in metadata map - %s",
key_name);
}
free((void *)key_name);
}
MMDB_free_entry_data_list(first);
}
void run_tests(int mode, const char *mode_desc)
{
const char *file = "MaxMind-DB-test-ipv4-24.mmdb";
const char *path = test_database_path(file);
MMDB_s *mmdb = open_ok(path, mode, mode_desc);
// All of the remaining tests require an open mmdb
if (NULL == mmdb) {
diag("could not open %s - skipping remaining tests", path);
return;
}
free((void *)path);
test_metadata(mmdb, mode_desc);
test_metadata_as_data_entry_list(mmdb, mode_desc);
MMDB_close(mmdb);
free(mmdb);
}
int main(void)
{
plan(NO_PLAN);
for_all_modes(&run_tests);
done_testing();
}

View File

@ -0,0 +1,158 @@
#!/usr/bin/env perl
use strict;
use warnings;
use FindBin qw( $Bin );
eval <<'EOF';
use Test::More 0.88;
use IPC::Run3 qw( run3 );
EOF
if ($@) {
print
"1..0 # skip all tests skipped - these tests need the Test::More 0.88, IPC::Run3 and Test::Output modules:\n";
print "$@";
exit 0;
}
my $mmdblookup = "$Bin/../bin/mmdblookup";
my $test_data_dir = "$Bin/maxmind-db/test-data";
{
ok( -x $mmdblookup, 'mmdblookup script is executable' );
}
for my $arg (qw( -h -? --help )) {
_test_stdout(
[$arg],
qr{mmdblookup --file.+This application accepts the following options:}s,
0,
"help output from $arg"
);
}
_test_both(
[],
qr{mmdblookup --file.+This application accepts the following options:}s,
qr{ERROR: You must provide a filename with --file},
1,
"help output with no CLI options"
);
_test_stderr(
[qw( --file foo )],
qr{ERROR: You must provide an IP address with --ip},
1,
'error when no --ip is given'
);
_test_stdout(
[qw( --version )],
qr/mmdblookup version \d+\.\d+\.\d+/,
0,
'output for --version'
);
_test_stdout(
['--file', "$test_data_dir/GeoIP2-City-Test.mmdb", '--ip', '2.125.160.216'],
qr/"en"\s*:\s*"Boxford"/,
0,
'output for 2.125.160.216'
);
_test_stdout(
['--file', "$test_data_dir/GeoIP2-City-Test.mmdb", '--ip', '2.125.160.216', '--verbose'],
qr/Database metadata.+"en"\s*:\s*"Boxford"/s,
0,
'verbose output for 2.125.160.216'
);
_test_stdout(
['--file', "$test_data_dir/GeoIP2-City-Test.mmdb", '--ip', '2.125.160.216', qw( location latitude )],
qr/^\s*51\.750000 <double>\s*$/s,
0,
'output for 2.125.160.216 with lookup path'
);
_test_stderr(
[ qw( --file this/path/better/not/exist.mmdb --ip 1.2.3.4 ) ],
qr{Can't open this/path/better/not/exist.mmdb}s,
2,
'error for file that does not exist'
);
_test_stderr(
['--file', "$test_data_dir/GeoIP2-City-Test.mmdb", '--ip', 'not-an-ip-address' ],
qr{Error from call to getaddrinfo for not-an-ip-address}s,
3,
'error for bad IP address'
);
_test_stderr(
['--file', "$test_data_dir/GeoIP2-City-Test.mmdb", '--ip', '10.2.3.4' ],
qr{\QCould not find an entry for this IP address (10.2.3.4)}s,
6,
'error for bad PI address'
);
done_testing();
sub _test_stdout {
my $args = shift;
my $expect_stdout = shift;
my $expect_status = shift;
my $desc = shift;
_test_both( $args, $expect_stdout, q{}, $expect_status, $desc );
}
sub _test_stderr {
my $args = shift;
my $expect_stderr = shift;
my $expect_status = shift;
my $desc = shift;
_test_both( $args, undef, $expect_stderr, $expect_status, $desc );
}
sub _test_both {
my $args = shift;
my $expect_stdout = shift;
my $expect_stderr = shift;
my $expect_status = shift;
my $desc = shift;
my $stdout;
my $stderr;
run3(
[ $mmdblookup, @{$args} ],
\undef,
\$stdout,
\$stderr,
);
my $exit_status = $? >> 8;
# We don't need to retest that the help output shows up for all errors
if ( defined $expect_stdout ) {
like(
$stdout,
$expect_stdout,
"stdout for mmdblookup @{$args}"
);
}
if ( ref $expect_stderr ) {
like( $stderr, $expect_stderr, "stderr for mmdblookup @{$args}" );
}
else {
is( $stderr, $expect_stderr, "stderr for mmdblookup @{$args}" );
}
is(
$exit_status, $expect_status,
"exit status was $expect_status for mmdblookup @{$args}"
);
}

View File

@ -0,0 +1,32 @@
#include "maxminddb_test_helper.h"
void run_tests(int mode, const char *mode_desc)
{
const char *filename = "MaxMind-DB-string-value-entries.mmdb";
const char *path = test_database_path(filename);
MMDB_s *mmdb = open_ok(path, mode, mode_desc);
free((void *)path);
const char *ip = "1.1.1.1";
MMDB_lookup_result_s result =
lookup_string_ok(mmdb, ip, filename, mode_desc);
MMDB_entry_data_s entry_data;
int status = MMDB_get_value(&result.entry, &entry_data, NULL);
cmp_ok(status, "==", MMDB_SUCCESS,
"status for MMDB_get_value() is MMDB_SUCCESS");
ok(entry_data.has_data, "found a value when varargs list is just NULL");
cmp_ok(entry_data.type, "==", MMDB_DATA_TYPE_UTF8_STRING,
"returned entry type is utf8_string");
MMDB_close(mmdb);
free(mmdb);
}
int main(void)
{
plan(NO_PLAN);
for_all_modes(&run_tests);
done_testing();
}

157
module/Vendor/MaxmindDB/t/read_node_t.c vendored Normal file
View File

@ -0,0 +1,157 @@
#include "maxminddb_test_helper.h"
void test_entry_data(MMDB_s *mmdb, MMDB_entry_s *entry, uint32_t node_number,
char * node_record)
{
MMDB_entry_data_s entry_data;
int status =
MMDB_get_value(entry, &entry_data, "ip",
NULL);
cmp_ok(status, "==", MMDB_SUCCESS,
"successful data lookup for node");
cmp_ok(
entry_data.type, "==", MMDB_DATA_TYPE_UTF8_STRING,
"returned entry type is UTF8_STRING for %s record of node %i",
node_record, node_number);
}
void run_read_node_tests(MMDB_s *mmdb, const uint32_t tests[][5],
int test_count,
uint8_t record_size)
{
for (int i = 0; i < test_count; i++) {
uint32_t node_number = tests[i][0];
MMDB_search_node_s node;
int status = MMDB_read_node(mmdb, node_number, &node);
if (MMDB_SUCCESS == status) {
cmp_ok(node.left_record, "==", tests[i][1],
"left record for node %i is %i - %i bit DB",
node_number, tests[i][1], record_size);
cmp_ok(node.left_record_type, "==", tests[i][2],
"left record type for node %i is %i", node_number,
tests[i][2]);
if (node.left_record_type == MMDB_RECORD_TYPE_DATA) {
test_entry_data(mmdb, &node.left_record_entry, node_number,
"left");
}
cmp_ok(node.right_record, "==", tests[i][3],
"right record for node %i is %i - %i bit DB",
node_number, tests[i][3], record_size);
cmp_ok(node.right_record_type, "==", tests[i][4],
"right record type for node %i is %i", node_number,
tests[i][4]);
if (node.right_record_type == MMDB_RECORD_TYPE_DATA) {
test_entry_data(mmdb, &node.right_record_entry, node_number,
"right");
}
} else {
diag("call to MMDB_read_node for node %i failed - %i bit DB",
node_number,
record_size);
}
}
}
void run_24_bit_record_tests(int mode, const char *mode_desc)
{
const char *filename = "MaxMind-DB-test-mixed-24.mmdb";
const char *path = test_database_path(filename);
MMDB_s *mmdb = open_ok(path, mode, mode_desc);
free((void *)path);
const uint32_t tests[7][5] = {
{ 0, 1, MMDB_RECORD_TYPE_SEARCH_NODE, 242,
MMDB_RECORD_TYPE_EMPTY },
{ 80, 81, MMDB_RECORD_TYPE_SEARCH_NODE, 197,
MMDB_RECORD_TYPE_SEARCH_NODE, },
{ 96, 97, MMDB_RECORD_TYPE_SEARCH_NODE, 242,
MMDB_RECORD_TYPE_EMPTY, },
{ 103, 242, MMDB_RECORD_TYPE_EMPTY, 104,
MMDB_RECORD_TYPE_SEARCH_NODE, },
{ 127, 242, MMDB_RECORD_TYPE_EMPTY, 315,
MMDB_RECORD_TYPE_DATA, },
{ 132, 329, MMDB_RECORD_TYPE_DATA, 242,
MMDB_RECORD_TYPE_EMPTY, },
{ 241, 96, MMDB_RECORD_TYPE_SEARCH_NODE, 242,
MMDB_RECORD_TYPE_EMPTY, }
};
run_read_node_tests(mmdb, tests, 7, 24);
MMDB_close(mmdb);
free(mmdb);
}
void run_28_bit_record_tests(int mode, const char *mode_desc)
{
const char *filename = "MaxMind-DB-test-mixed-28.mmdb";
const char *path = test_database_path(filename);
MMDB_s *mmdb = open_ok(path, mode, mode_desc);
free((void *)path);
const uint32_t tests[7][5] = {
{ 0, 1, MMDB_RECORD_TYPE_SEARCH_NODE, 242,
MMDB_RECORD_TYPE_EMPTY },
{ 80, 81, MMDB_RECORD_TYPE_SEARCH_NODE, 197,
MMDB_RECORD_TYPE_SEARCH_NODE, },
{ 96, 97, MMDB_RECORD_TYPE_SEARCH_NODE, 242,
MMDB_RECORD_TYPE_EMPTY, },
{ 103, 242, MMDB_RECORD_TYPE_EMPTY, 104,
MMDB_RECORD_TYPE_SEARCH_NODE, },
{ 127, 242, MMDB_RECORD_TYPE_EMPTY, 315,
MMDB_RECORD_TYPE_DATA, },
{ 132, 329, MMDB_RECORD_TYPE_DATA, 242,
MMDB_RECORD_TYPE_EMPTY, },
{ 241, 96, MMDB_RECORD_TYPE_SEARCH_NODE, 242,
MMDB_RECORD_TYPE_EMPTY, }
};
run_read_node_tests(mmdb, tests, 7, 28);
MMDB_close(mmdb);
free(mmdb);
}
void run_32_bit_record_tests(int mode, const char *mode_desc)
{
const char *filename = "MaxMind-DB-test-mixed-32.mmdb";
const char *path = test_database_path(filename);
MMDB_s *mmdb = open_ok(path, mode, mode_desc);
free((void *)path);
const uint32_t tests[7][5] = {
{ 0, 1, MMDB_RECORD_TYPE_SEARCH_NODE, 242,
MMDB_RECORD_TYPE_EMPTY },
{ 80, 81, MMDB_RECORD_TYPE_SEARCH_NODE, 197,
MMDB_RECORD_TYPE_SEARCH_NODE, },
{ 96, 97, MMDB_RECORD_TYPE_SEARCH_NODE, 242,
MMDB_RECORD_TYPE_EMPTY, },
{ 103, 242, MMDB_RECORD_TYPE_EMPTY, 104,
MMDB_RECORD_TYPE_SEARCH_NODE, },
{ 127, 242, MMDB_RECORD_TYPE_EMPTY, 315,
MMDB_RECORD_TYPE_DATA, },
{ 132, 329, MMDB_RECORD_TYPE_DATA, 242,
MMDB_RECORD_TYPE_EMPTY, },
{ 241, 96, MMDB_RECORD_TYPE_SEARCH_NODE, 242,
MMDB_RECORD_TYPE_EMPTY, }
};
run_read_node_tests(mmdb, tests, 7, 32);
MMDB_close(mmdb);
free(mmdb);
}
void run_tests(int mode, const char *mode_desc)
{
run_24_bit_record_tests(mode, mode_desc);
run_28_bit_record_tests(mode, mode_desc);
run_32_bit_record_tests(mode, mode_desc);
}
int main(void)
{
plan(NO_PLAN);
for_all_modes(&run_tests);
done_testing();
}

196
module/Vendor/MaxmindDB/t/threads_t.c vendored Normal file
View File

@ -0,0 +1,196 @@
#include "maxminddb_test_helper.h"
#include <pthread.h>
typedef struct thread_arg {
int thread_id;
MMDB_s *mmdb;
const char *ip_to_lookup;
} thread_arg_s;
typedef struct test_result {
const char *ip_looked_up;
int lookup_string_gai_error;
int lookup_string_mmdb_error;
int found_entry;
int get_value_status;
int data_type_ok;
char *data_value;
} test_result_s;
void test_one_ip(MMDB_s *mmdb, const char *ip, test_result_s *test_result)
{
test_result->ip_looked_up = ip;
int gai_error = 0;
int mmdb_error = 0;
MMDB_lookup_result_s result =
MMDB_lookup_string(mmdb, ip, &gai_error, &mmdb_error);
test_result->lookup_string_gai_error = gai_error;
if (gai_error) {
return;
}
test_result->lookup_string_mmdb_error = mmdb_error;
if (mmdb_error) {
return;
}
test_result->found_entry = result.found_entry;
if (!result.found_entry) {
return;
}
MMDB_entry_data_s data;
int status = MMDB_get_value(&result.entry, &data, "ip", NULL);
test_result->get_value_status = status;
if (status) {
return;
}
test_result->data_type_ok = data.type == MMDB_DATA_TYPE_UTF8_STRING;
if (!test_result->data_type_ok) {
return;
}
test_result->data_value = mmdb_strndup(data.utf8_string, data.data_size);
return;
}
void *run_one_thread(void *arg)
{
thread_arg_s *thread_arg = (thread_arg_s *)arg;
MMDB_s *mmdb = thread_arg->mmdb;
const char *ip = thread_arg->ip_to_lookup;
test_result_s *result = malloc(sizeof(test_result_s));
test_one_ip(mmdb, ip, result);
pthread_exit((void *)result);
}
void process_result(test_result_s *result, const char *expect,
const char *mode_desc)
{
int is_ok;
is_ok =
ok(!result->lookup_string_gai_error, "no getaddrinfo error for %s - %s",
result->ip_looked_up, mode_desc);
if (!is_ok) {
return;
}
is_ok = ok(!result->lookup_string_mmdb_error, "no mmdb error for %s - %s",
result->ip_looked_up, mode_desc);
if (!is_ok) {
return;
}
is_ok = ok(result->found_entry, "got a result for %s in the database - %s",
result->ip_looked_up, mode_desc);
if (!is_ok) {
return;
}
is_ok =
ok(!result->get_value_status,
"no error from MMDB_get_value for %s - %s",
result->ip_looked_up,
mode_desc);
if (!is_ok) {
return;
}
is_ok = ok(result->data_type_ok,
"MMDB_get_value found a utf8_string at 'ip' key for %s - %s",
result->ip_looked_up, mode_desc);
if (!is_ok) {
return;
}
is(result->data_value, expect,
"found expected result for 'ip' key for %s - %s",
result->ip_looked_up, mode_desc);
}
void run_ipX_tests(MMDB_s *mmdb, const char *pairs[][2], int pairs_rows,
int mode, const char *mode_desc)
{
pthread_t threads[pairs_rows];
struct thread_arg thread_args[pairs_rows];
for (int i = 0; i < pairs_rows; i += 1) {
thread_args[i].thread_id = i;
thread_args[i].mmdb = mmdb;
thread_args[i].ip_to_lookup = pairs[i][0];
int error = pthread_create(&threads[i], NULL, run_one_thread,
&thread_args[i]);
if (error) {
BAIL_OUT("pthread_create failed");
}
}
for (int i = 0; i < pairs_rows; i += 1) {
void *thread_return;
int error = pthread_join(threads[i], &thread_return);
if (error) {
BAIL_OUT("pthread_join failed");
}
test_result_s *test_result = (test_result_s *)thread_return;
if (NULL != test_result) {
process_result(test_result, pairs[i][1], mode_desc);
if (test_result->data_type_ok) {
free(test_result->data_value);
}
free(test_result);
}
}
}
void run_tests(int mode, const char *mode_desc)
{
const char *filename = "MaxMind-DB-test-mixed-32.mmdb";
const char *path = test_database_path(filename);
MMDB_s *mmdb = open_ok(path, mode, mode_desc);
free((void *)path);
const char *pairs[18][2] = {
{ "1.1.1.1", "::1.1.1.1" },
{ "1.1.1.2", "::1.1.1.2" },
{ "1.1.1.3", "::1.1.1.2" },
{ "1.1.1.7", "::1.1.1.4" },
{ "1.1.1.9", "::1.1.1.8" },
{ "1.1.1.15", "::1.1.1.8" },
{ "1.1.1.17", "::1.1.1.16" },
{ "1.1.1.31", "::1.1.1.16" },
{ "1.1.1.32", "::1.1.1.32" },
{ "::1:ffff:ffff", "::1:ffff:ffff" },
{ "::2:0:0", "::2:0:0" },
{ "::2:0:1a", "::2:0:0" },
{ "::2:0:40", "::2:0:40" },
{ "::2:0:4f", "::2:0:40" },
{ "::2:0:50", "::2:0:50" },
{ "::2:0:52", "::2:0:50" },
{ "::2:0:58", "::2:0:58" },
{ "::2:0:59", "::2:0:58" },
};
run_ipX_tests(mmdb, pairs, 18, mode, mode_desc);
MMDB_close(mmdb);
free(mmdb);
}
int main(void)
{
plan(NO_PLAN);
for_all_modes(&run_tests);
done_testing();
pthread_exit(NULL);
}

10
module/Vendor/MaxmindDB/t/version_t.c vendored Normal file
View File

@ -0,0 +1,10 @@
#include "maxminddb_test_helper.h"
int main(void)
{
const char *version = MMDB_lib_version();
if (ok((version != NULL), "MMDB_lib_version exists")) {
is(version, PACKAGE_VERSION, "version is " PACKAGE_VERSION);
}
done_testing();
}