"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "bin/mmdblookup.c" between
libmaxminddb-1.5.0.tar.gz and libmaxminddb-1.5.2.tar.gz

About: libmaxminddb is a library that provides functions for working with MaxMind DB files (free GeoLite 2 or commercial GeoIP2).

mmdblookup.c  (libmaxminddb-1.5.0):mmdblookup.c  (libmaxminddb-1.5.2)
skipping to change at line 26 skipping to change at line 26
#ifdef _WIN32 #ifdef _WIN32
#ifndef UNICODE #ifndef UNICODE
#define UNICODE #define UNICODE
#endif #endif
#include <malloc.h> #include <malloc.h>
#else #else
#include <libgen.h> #include <libgen.h>
#include <unistd.h> #include <unistd.h>
#endif #endif
#define LOCAL static static void usage(char *program, int exit_code, const char *error);
static const char **get_options(int argc,
LOCAL void usage(char *program, int exit_code, const char *error); char **argv,
LOCAL const char **get_options( char **mmdb_file,
int argc, char **ip_address,
char **argv, int *verbose,
char **mmdb_file, int *iterations,
char **ip_address, int *lookup_path_length,
int *verbose, int *const thread_count,
int *iterations, char **const ip_file);
int *lookup_path_length, static MMDB_s open_or_die(const char *fname);
int *const thread_count, static void dump_meta(MMDB_s *mmdb);
char **const ip_file); static bool lookup_from_file(MMDB_s *const mmdb,
LOCAL MMDB_s open_or_die(const char *fname); char const *const ip_file,
LOCAL void dump_meta(MMDB_s *mmdb); bool const dump);
LOCAL bool lookup_from_file(MMDB_s *const mmdb, static int lookup_and_print(MMDB_s *mmdb,
char const *const ip_file, const char *ip_address,
bool const dump); const char **lookup_path,
LOCAL int lookup_and_print(MMDB_s *mmdb, const char *ip_address, int lookup_path_length,
const char **lookup_path, bool verbose);
int lookup_path_length, static int benchmark(MMDB_s *mmdb, int iterations);
bool verbose); static MMDB_lookup_result_s lookup_or_die(MMDB_s *mmdb, const char *ipstr);
LOCAL int benchmark(MMDB_s *mmdb, int iterations); static void random_ipv4(char *ip);
LOCAL MMDB_lookup_result_s lookup_or_die(MMDB_s *mmdb, const char *ipstr);
LOCAL void random_ipv4(char *ip);
#ifndef _WIN32 #ifndef _WIN32
// These aren't with the automatically generated prototypes as we'd lose the // These aren't with the automatically generated prototypes as we'd lose the
// enclosing macros. // enclosing macros.
static bool start_threaded_benchmark( static bool start_threaded_benchmark(MMDB_s *const mmdb,
MMDB_s *const mmdb, int const thread_count,
int const thread_count, int const iterations);
int const iterations);
static long double get_time(void); static long double get_time(void);
static void *thread(void *arg); static void *thread(void *arg);
#endif #endif
#ifdef _WIN32 #ifdef _WIN32
int wmain(int argc, wchar_t **wargv) int wmain(int argc, wchar_t **wargv) {
{
// Convert our argument list from UTF-16 to UTF-8. // Convert our argument list from UTF-16 to UTF-8.
char **argv = (char **)calloc(argc, sizeof(char *)); char **argv = (char **)calloc(argc, sizeof(char *));
if (!argv) { if (!argv) {
fprintf(stderr, "calloc(): %s\n", strerror(errno)); fprintf(stderr, "calloc(): %s\n", strerror(errno));
exit(1); exit(1);
} }
for (int i = 0; i < argc; i++) { for (int i = 0; i < argc; i++) {
int utf8_width; int utf8_width;
char *utf8_string; char *utf8_string;
utf8_width = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, NULL, 0, utf8_width =
NULL, NULL); WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, NULL, 0, NULL, NULL);
if (utf8_width < 1) { if (utf8_width < 1) {
fprintf(stderr, "WideCharToMultiByte() failed: %d\n", fprintf(
GetLastError()); stderr, "WideCharToMultiByte() failed: %d\n", GetLastError());
exit(1); exit(1);
} }
utf8_string = calloc(utf8_width, sizeof(char)); utf8_string = calloc(utf8_width, sizeof(char));
if (!utf8_string) { if (!utf8_string) {
fprintf(stderr, "calloc(): %s\n", strerror(errno)); fprintf(stderr, "calloc(): %s\n", strerror(errno));
exit(1); exit(1);
} }
if (WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, utf8_string, if (WideCharToMultiByte(
utf8_width, NULL, NULL) < 1) { CP_UTF8, 0, wargv[i], -1, utf8_string, utf8_width, NULL, NULL) <
fprintf(stderr, "WideCharToMultiByte() failed: %d\n", 1) {
GetLastError()); fprintf(
stderr, "WideCharToMultiByte() failed: %d\n", GetLastError());
exit(1); exit(1);
} }
argv[i] = utf8_string; argv[i] = utf8_string;
} }
#else // _WIN32 #else // _WIN32
int main(int argc, char **argv) int main(int argc, char **argv) {
{
#endif // _WIN32 #endif // _WIN32
char *mmdb_file = NULL; char *mmdb_file = NULL;
char *ip_address = NULL; char *ip_address = NULL;
int verbose = 0; int verbose = 0;
int iterations = 0; int iterations = 0;
int lookup_path_length = 0; int lookup_path_length = 0;
int thread_count = 0; int thread_count = 0;
char *ip_file = NULL; char *ip_file = NULL;
const char **lookup_path = const char **lookup_path = get_options(argc,
get_options(argc, argv, &mmdb_file, &ip_address, &verbose, &iterations, argv,
&lookup_path_length, &thread_count, &ip_file); &mmdb_file,
&ip_address,
&verbose,
&iterations,
&lookup_path_length,
&thread_count,
&ip_file);
MMDB_s mmdb = open_or_die(mmdb_file); MMDB_s mmdb = open_or_die(mmdb_file);
if (verbose) { if (verbose) {
dump_meta(&mmdb); dump_meta(&mmdb);
} }
// The benchmarking and lookup from file modes are hidden features mainly // The benchmarking and lookup from file modes are hidden features mainly
// intended for development right now. This means there are several flags // intended for development right now. This means there are several flags
// that exist but are intentionally not mentioned in the usage or man page. // that exist but are intentionally not mentioned in the usage or man page.
skipping to change at line 134 skipping to change at line 136
free((void *)lookup_path); free((void *)lookup_path);
if (!lookup_from_file(&mmdb, ip_file, verbose == 1)) { if (!lookup_from_file(&mmdb, ip_file, verbose == 1)) {
MMDB_close(&mmdb); MMDB_close(&mmdb);
return 1; return 1;
} }
MMDB_close(&mmdb); MMDB_close(&mmdb);
return 0; return 0;
} }
if (0 == iterations) { if (0 == iterations) {
exit(lookup_and_print(&mmdb, ip_address, lookup_path, exit(lookup_and_print(
lookup_path_length, verbose)); &mmdb, ip_address, lookup_path, lookup_path_length, verbose));
} }
free((void *)lookup_path); free((void *)lookup_path);
srand( (int)time(NULL) ); srand((int)time(NULL));
#ifndef _WIN32 #ifndef _WIN32
if (thread_count > 0) { if (thread_count > 0) {
if (!start_threaded_benchmark(&mmdb, thread_count, iterations)) { if (!start_threaded_benchmark(&mmdb, thread_count, iterations)) {
MMDB_close(&mmdb); MMDB_close(&mmdb);
exit(1); exit(1);
} }
MMDB_close(&mmdb); MMDB_close(&mmdb);
exit(0); exit(0);
} }
#endif #endif
exit(benchmark(&mmdb, iterations)); exit(benchmark(&mmdb, iterations));
} }
LOCAL void usage(char *program, int exit_code, const char *error) static void usage(char *program, int exit_code, const char *error) {
{
if (NULL != error) { if (NULL != error) {
fprintf(stderr, "\n *ERROR: %s\n", error); fprintf(stderr, "\n *ERROR: %s\n", error);
} }
char *usage = "\n" char *usage =
" %s --file /path/to/file.mmdb --ip 1.2.3.4 [path to lookup]\ "\n"
n" " %s --file /path/to/file.mmdb --ip 1.2.3.4 [path to lookup]\n"
"\n" "\n"
" This application accepts the following options:\n" " This application accepts the following options:\n"
"\n" "\n"
" --file (-f) The path to the MMDB file. Required.\n" " --file (-f) The path to the MMDB file. Required.\n"
"\n" "\n"
" --ip (-i) The IP address to look up. Required.\n" " --ip (-i) The IP address to look up. Required.\n"
"\n" "\n"
" --verbose (-v) Turns on verbose output. Specifically, " --verbose (-v) Turns on verbose output. Specifically, this "
this causes this\n" "causes this\n"
" application to output the database meta " application to output the database metadata.\n"
data.\n" "\n"
"\n" " --version Print the program's version number and exit.\n"
" --version Print the program's version number and "\n"
exit.\n" " --help (-h -?) Show usage information.\n"
"\n" "\n"
" --help (-h -?) Show usage information.\n" " If an IP's data entry resolves to a map or array, you can provide\n"
"\n" " a lookup path to only show part of that data.\n"
" If an IP's data entry resolves to a map or array, you can p "\n"
rovide\n" " For example, given a JSON structure like this:\n"
" a lookup path to only show part of that data.\n" "\n"
"\n" " {\n"
" For example, given a JSON structure like this:\n" " \"names\": {\n"
"\n" " \"en\": \"Germany\",\n"
" {\n" " \"de\": \"Deutschland\"\n"
" \"names\": {\n" " },\n"
" \"en\": \"Germany\",\n" " \"cities\": [ \"Berlin\", \"Frankfurt\" ]\n"
" \"de\": \"Deutschland\"\n" " }\n"
" },\n" "\n"
" \"cities\": [ \"Berlin\", \"Frankfurt\" ]\n" " You could look up just the English name by calling mmdblookup with "
" }\n" "a lookup path of:\n"
"\n" "\n"
" You could look up just the English name by calling mmdblook " mmdblookup --file ... --ip ... names en\n"
up with a lookup path of:\n" "\n"
"\n" " Or you could look up the second city in the list with:\n"
" mmdblookup --file ... --ip ... names en\n" "\n"
"\n" " mmdblookup --file ... --ip ... cities 1\n"
" Or you could look up the second city in the list with:\n" "\n"
"\n" " Array numbering begins with zero (0).\n"
" mmdblookup --file ... --ip ... cities 1\n" "\n"
"\n" " If you do not provide a path to lookup, all of the information for "
" Array numbering begins with zero (0).\n" "a given IP\n"
"\n" " will be shown.\n"
" If you do not provide a path to lookup, all of the informat "\n";
ion for a given IP\n"
" will be shown.\n"
"\n";
fprintf(stdout, usage, program); fprintf(stdout, usage, program);
exit(exit_code); exit(exit_code);
} }
LOCAL const char **get_options( static const char **get_options(int argc,
int argc, char **argv,
char **argv, char **mmdb_file,
char **mmdb_file, char **ip_address,
char **ip_address, int *verbose,
int *verbose, int *iterations,
int *iterations, int *lookup_path_length,
int *lookup_path_length, int *const thread_count,
int *const thread_count, char **const ip_file) {
char **const ip_file)
{
static int help = 0; static int help = 0;
static int version = 0; static int version = 0;
while (1) { while (1) {
static struct option options[] = { static struct option options[] = {
{ "file", required_argument, 0, 'f' }, {"file", required_argument, 0, 'f'},
{ "ip", required_argument, 0, 'i' }, {"ip", required_argument, 0, 'i'},
{ "verbose", no_argument, 0, 'v' }, {"verbose", no_argument, 0, 'v'},
{ "version", no_argument, 0, 'n' }, {"version", no_argument, 0, 'n'},
{ "benchmark", required_argument, 0, 'b' }, {"benchmark", required_argument, 0, 'b'},
#ifndef _WIN32 #ifndef _WIN32
{ "threads", required_argument, 0, 't' }, {"threads", required_argument, 0, 't'},
#endif #endif
{ "ip-file", required_argument, 0, 'I' }, {"ip-file", required_argument, 0, 'I'},
{ "help", no_argument, 0, 'h' }, {"help", no_argument, 0, 'h'},
{ "?", no_argument, 0, 1 }, {"?", no_argument, 0, 1},
{ 0, 0, 0, 0 } {0, 0, 0, 0}};
};
int opt_index; int opt_index;
#ifdef _WIN32 #ifdef _WIN32
char const * const optstring = "f:i:b:I:vnh?"; char const *const optstring = "f:i:b:I:vnh?";
#else #else
char const * const optstring = "f:i:b:t:I:vnh?"; char const *const optstring = "f:i:b:t:I:vnh?";
#endif #endif
int opt_char = getopt_long(argc, argv, optstring, options, int opt_char = getopt_long(argc, argv, optstring, options, &opt_index);
&opt_index);
if (-1 == opt_char) { if (-1 == opt_char) {
break; break;
} }
if ('f' == opt_char) { if ('f' == opt_char) {
*mmdb_file = optarg; *mmdb_file = optarg;
} else if ('i' == opt_char) { } else if ('i' == opt_char) {
*ip_address = optarg; *ip_address = optarg;
} else if ('v' == opt_char) { } else if ('v' == opt_char) {
skipping to change at line 298 skipping to change at line 299
if (NULL == *mmdb_file) { if (NULL == *mmdb_file) {
usage(program, 1, "You must provide a filename with --file"); usage(program, 1, "You must provide a filename with --file");
} }
if (*ip_address == NULL && *iterations == 0 && !*ip_file) { if (*ip_address == NULL && *iterations == 0 && !*ip_file) {
usage(program, 1, "You must provide an IP address with --ip"); usage(program, 1, "You must provide an IP address with --ip");
} }
const char **lookup_path = const char **lookup_path =
calloc((argc - optind) + 1, sizeof(const char *)); calloc((argc - optind) + 1, sizeof(const char *));
if (!lookup_path) {
fprintf(stderr, "calloc(): %s\n", strerror(errno));
exit(1);
}
int i; int i;
for (i = 0; i < argc - optind; i++) { for (i = 0; i < argc - optind; i++) {
lookup_path[i] = argv[i + optind]; lookup_path[i] = argv[i + optind];
(*lookup_path_length)++; (*lookup_path_length)++;
} }
lookup_path[i] = NULL; lookup_path[i] = NULL;
return lookup_path; return lookup_path;
} }
LOCAL MMDB_s open_or_die(const char *fname) static MMDB_s open_or_die(const char *fname) {
{
MMDB_s mmdb; MMDB_s mmdb;
int status = MMDB_open(fname, MMDB_MODE_MMAP, &mmdb); int status = MMDB_open(fname, MMDB_MODE_MMAP, &mmdb);
if (MMDB_SUCCESS != status) { if (MMDB_SUCCESS != status) {
fprintf(stderr, "\n Can't open %s - %s\n", fname, fprintf(
MMDB_strerror(status)); stderr, "\n Can't open %s - %s\n", fname, MMDB_strerror(status));
if (MMDB_IO_ERROR == status) { if (MMDB_IO_ERROR == status) {
fprintf(stderr, " IO error: %s\n", strerror(errno)); fprintf(stderr, " IO error: %s\n", strerror(errno));
} }
fprintf(stderr, "\n"); fprintf(stderr, "\n");
exit(2); exit(2);
} }
return mmdb; return mmdb;
} }
LOCAL void dump_meta(MMDB_s *mmdb) static void dump_meta(MMDB_s *mmdb) {
{
const char *meta_dump = "\n" const char *meta_dump = "\n"
" Database metadata\n" " Database metadata\n"
" Node count: %i\n" " Node count: %i\n"
" Record size: %i bits\n" " Record size: %i bits\n"
" IP version: IPv%i\n" " IP version: IPv%i\n"
" Binary format: %i.%i\n" " Binary format: %i.%i\n"
" Build epoch: %llu (%s)\n" " Build epoch: %llu (%s)\n"
" Type: %s\n" " Type: %s\n"
" Languages: "; " Languages: ";
char date[40]; char date[40];
const time_t epoch = (const time_t)mmdb->metadata.build_epoch; const time_t epoch = (const time_t)mmdb->metadata.build_epoch;
strftime(date, 40, "%F %T UTC", gmtime(&epoch)); strftime(date, 40, "%F %T UTC", gmtime(&epoch));
fprintf(stdout, meta_dump, fprintf(stdout,
meta_dump,
mmdb->metadata.node_count, mmdb->metadata.node_count,
mmdb->metadata.record_size, mmdb->metadata.record_size,
mmdb->metadata.ip_version, mmdb->metadata.ip_version,
mmdb->metadata.binary_format_major_version, mmdb->metadata.binary_format_major_version,
mmdb->metadata.binary_format_minor_version, mmdb->metadata.binary_format_minor_version,
mmdb->metadata.build_epoch, mmdb->metadata.build_epoch,
date, date,
mmdb->metadata.database_type); mmdb->metadata.database_type);
for (size_t i = 0; i < mmdb->metadata.languages.count; i++) { for (size_t i = 0; i < mmdb->metadata.languages.count; i++) {
fprintf(stdout, "%s", mmdb->metadata.languages.names[i]); fprintf(stdout, "%s", mmdb->metadata.languages.names[i]);
if (i < mmdb->metadata.languages.count - 1) { if (i < mmdb->metadata.languages.count - 1) {
fprintf(stdout, " "); fprintf(stdout, " ");
} }
} }
fprintf(stdout, "\n"); fprintf(stdout, "\n");
fprintf(stdout, " Description:\n"); fprintf(stdout, " Description:\n");
for (size_t i = 0; i < mmdb->metadata.description.count; i++) { for (size_t i = 0; i < mmdb->metadata.description.count; i++) {
fprintf(stdout, " %s: %s\n", fprintf(stdout,
" %s: %s\n",
mmdb->metadata.description.descriptions[i]->language, mmdb->metadata.description.descriptions[i]->language,
mmdb->metadata.description.descriptions[i]->description); mmdb->metadata.description.descriptions[i]->description);
} }
fprintf(stdout, "\n"); fprintf(stdout, "\n");
} }
// The input file should have one IP per line. // The input file should have one IP per line.
// //
// We look up each IP. // We look up each IP.
// //
// If dump is true, we dump the data for each IP to stderr. This is useful for // If dump is true, we dump the data for each IP to stderr. This is useful for
// comparison in that you can dump out the data for the IPs before and after // comparison in that you can dump out the data for the IPs before and after
// making changes. It goes to stderr rather than stdout so that the report does // making changes. It goes to stderr rather than stdout so that the report does
// not get included in what you will compare (since it will almost always be // not get included in what you will compare (since it will almost always be
// different). // different).
// //
// In addition to being useful for comparisons, this function provides a way to // In addition to being useful for comparisons, this function provides a way to
// have a more deterministic set of lookups for benchmarking. // have a more deterministic set of lookups for benchmarking.
LOCAL bool lookup_from_file(MMDB_s *const mmdb, static bool lookup_from_file(MMDB_s *const mmdb,
char const *const ip_file, char const *const ip_file,
bool const dump) bool const dump) {
{
FILE *const fh = fopen(ip_file, "r"); FILE *const fh = fopen(ip_file, "r");
if (!fh) { if (!fh) {
fprintf(stderr, "fopen(): %s: %s\n", ip_file, strerror(errno)); fprintf(stderr, "fopen(): %s: %s\n", ip_file, strerror(errno));
return false; return false;
} }
clock_t const clock_start = clock(); clock_t const clock_start = clock();
char buf[1024] = { 0 }; char buf[1024] = {0};
// I'd normally use uint64_t, but support for it is optional in C99. // I'd normally use uint64_t, but support for it is optional in C99.
unsigned long long i = 0; unsigned long long i = 0;
while (1) { while (1) {
if (fgets(buf, sizeof(buf), fh) == NULL) { if (fgets(buf, sizeof(buf), fh) == NULL) {
if (!feof(fh)) { if (!feof(fh)) {
fprintf(stderr, "fgets(): %s\n", strerror(errno)); fprintf(stderr, "fgets(): %s\n", strerror(errno));
fclose(fh); fclose(fh);
return false; return false;
} }
if (fclose(fh) != 0) { if (fclose(fh) != 0) {
skipping to change at line 432 skipping to change at line 436
} }
i++; i++;
MMDB_lookup_result_s result = lookup_or_die(mmdb, buf); MMDB_lookup_result_s result = lookup_or_die(mmdb, buf);
if (!result.found_entry) { if (!result.found_entry) {
continue; continue;
} }
MMDB_entry_data_list_s *entry_data_list = NULL; MMDB_entry_data_list_s *entry_data_list = NULL;
int const status = MMDB_get_entry_data_list(&result.entry, int const status =
&entry_data_list); MMDB_get_entry_data_list(&result.entry, &entry_data_list);
if (status != MMDB_SUCCESS) { if (status != MMDB_SUCCESS) {
fprintf(stderr, "MMDB_get_entry_data_list(): %s\n", fprintf(stderr,
"MMDB_get_entry_data_list(): %s\n",
MMDB_strerror(status)); MMDB_strerror(status));
fclose(fh); fclose(fh);
MMDB_free_entry_data_list(entry_data_list); MMDB_free_entry_data_list(entry_data_list);
return false; return false;
} }
if (!entry_data_list) { if (!entry_data_list) {
fprintf(stderr, "entry_data_list is NULL\n"); fprintf(stderr, "entry_data_list is NULL\n");
fclose(fh); fclose(fh);
return false; return false;
} }
if (dump) { if (dump) {
fprintf(stdout, "%s:\n", buf); fprintf(stdout, "%s:\n", buf);
int const status = MMDB_dump_entry_data_list(stderr, int const status =
entry_data_list, 0); MMDB_dump_entry_data_list(stderr, entry_data_list, 0);
if (status != MMDB_SUCCESS) { if (status != MMDB_SUCCESS) {
fprintf(stderr, "MMDB_dump_entry_data_list(): %s\n", fprintf(stderr,
"MMDB_dump_entry_data_list(): %s\n",
MMDB_strerror(status)); MMDB_strerror(status));
fclose(fh); fclose(fh);
MMDB_free_entry_data_list(entry_data_list); MMDB_free_entry_data_list(entry_data_list);
return false; return false;
} }
} }
MMDB_free_entry_data_list(entry_data_list); MMDB_free_entry_data_list(entry_data_list);
} }
clock_t const clock_diff = clock() - clock_start; clock_t const clock_diff = clock() - clock_start;
double const seconds = (double)clock_diff / CLOCKS_PER_SEC; double const seconds = (double)clock_diff / CLOCKS_PER_SEC;
fprintf( fprintf(
stdout, stdout,
"Looked up %llu addresses in %.2f seconds. %.2f lookups per second.\n", "Looked up %llu addresses in %.2f seconds. %.2f lookups per second.\n",
i, seconds, i / seconds); i,
seconds,
i / seconds);
return true; return true;
} }
LOCAL int lookup_and_print(MMDB_s *mmdb, const char *ip_address, static int lookup_and_print(MMDB_s *mmdb,
const char **lookup_path, const char *ip_address,
int lookup_path_length, const char **lookup_path,
bool verbose) int lookup_path_length,
{ bool verbose) {
MMDB_lookup_result_s result = lookup_or_die(mmdb, ip_address); MMDB_lookup_result_s result = lookup_or_die(mmdb, ip_address);
MMDB_entry_data_list_s *entry_data_list = NULL; MMDB_entry_data_list_s *entry_data_list = NULL;
int exit_code = 0; int exit_code = 0;
if (verbose) { if (verbose) {
fprintf( fprintf(stdout, "\n Record prefix length: %d\n", result.netmask);
stdout,
"\n Record prefix length: %d\n",
result.netmask
);
} }
if (result.found_entry) { if (result.found_entry) {
int status; int status;
if (lookup_path_length) { if (lookup_path_length) {
MMDB_entry_data_s entry_data; MMDB_entry_data_s entry_data;
status = MMDB_aget_value(&result.entry, &entry_data, status = MMDB_aget_value(&result.entry, &entry_data, lookup_path);
lookup_path);
if (MMDB_SUCCESS == status) { if (MMDB_SUCCESS == status) {
if (entry_data.offset) { if (entry_data.offset) {
MMDB_entry_s entry = MMDB_entry_s entry = {.mmdb = mmdb,
{ .mmdb = mmdb, .offset = entry_data.offset }; .offset = entry_data.offset};
status = MMDB_get_entry_data_list(&entry, status = MMDB_get_entry_data_list(&entry, &entry_data_list);
&entry_data_list);
} else { } else {
fprintf( fprintf(stdout,
stdout, "\n No data was found at the lookup path you "
"\n No data was found at the lookup path you provided\n "provided\n\n");
\n");
} }
} }
} else { } else {
status = MMDB_get_entry_data_list(&result.entry, status = MMDB_get_entry_data_list(&result.entry, &entry_data_list);
&entry_data_list);
} }
if (MMDB_SUCCESS != status) { if (MMDB_SUCCESS != status) {
fprintf(stderr, "Got an error looking up the entry data - %s\n", fprintf(stderr,
"Got an error looking up the entry data - %s\n",
MMDB_strerror(status)); MMDB_strerror(status));
exit_code = 5; exit_code = 5;
goto end; goto end;
} }
if (NULL != entry_data_list) { if (NULL != entry_data_list) {
fprintf(stdout, "\n"); fprintf(stdout, "\n");
MMDB_dump_entry_data_list(stdout, entry_data_list, 2); MMDB_dump_entry_data_list(stdout, entry_data_list, 2);
fprintf(stdout, "\n"); fprintf(stdout, "\n");
} }
} else { } else {
fprintf(stderr, fprintf(stderr,
"\n Could not find an entry for this IP address (%s)\n\n", "\n Could not find an entry for this IP address (%s)\n\n",
ip_address); ip_address);
exit_code = 6; exit_code = 6;
} }
end: end:
MMDB_free_entry_data_list(entry_data_list); MMDB_free_entry_data_list(entry_data_list);
MMDB_close(mmdb); MMDB_close(mmdb);
free((void *)lookup_path); free((void *)lookup_path);
return exit_code; return exit_code;
} }
LOCAL int benchmark(MMDB_s *mmdb, int iterations) static int benchmark(MMDB_s *mmdb, int iterations) {
{
char ip_address[16]; char ip_address[16];
int exit_code = 0; int exit_code = 0;
clock_t time = clock(); clock_t time = clock();
for (int i = 0; i < iterations; i++) { for (int i = 0; i < iterations; i++) {
random_ipv4(ip_address); random_ipv4(ip_address);
MMDB_lookup_result_s result = lookup_or_die(mmdb, ip_address); MMDB_lookup_result_s result = lookup_or_die(mmdb, ip_address);
MMDB_entry_data_list_s *entry_data_list = NULL; MMDB_entry_data_list_s *entry_data_list = NULL;
if (result.found_entry) { if (result.found_entry) {
int status = MMDB_get_entry_data_list(&result.entry, int status =
&entry_data_list); MMDB_get_entry_data_list(&result.entry, &entry_data_list);
if (MMDB_SUCCESS != status) { if (MMDB_SUCCESS != status) {
fprintf(stderr, "Got an error looking up the entry data - %s\n", fprintf(stderr,
"Got an error looking up the entry data - %s\n",
MMDB_strerror(status)); MMDB_strerror(status));
exit_code = 5; exit_code = 5;
MMDB_free_entry_data_list(entry_data_list); MMDB_free_entry_data_list(entry_data_list);
goto end; goto end;
} }
} }
MMDB_free_entry_data_list(entry_data_list); MMDB_free_entry_data_list(entry_data_list);
} }
time = clock() - time; time = clock() - time;
double seconds = ((double)time / CLOCKS_PER_SEC); double seconds = ((double)time / CLOCKS_PER_SEC);
fprintf( fprintf(stdout,
stdout, "\n Looked up %i addresses in %.2f seconds. %.2f lookups per "
"\n Looked up %i addresses in %.2f seconds. %.2f lookups per second.\n\ "second.\n\n",
n", iterations,
iterations, seconds, iterations / seconds); seconds,
iterations / seconds);
end: end:
MMDB_close(mmdb); MMDB_close(mmdb);
return exit_code; return exit_code;
} }
LOCAL MMDB_lookup_result_s lookup_or_die(MMDB_s *mmdb, const char *ipstr) static MMDB_lookup_result_s lookup_or_die(MMDB_s *mmdb, const char *ipstr) {
{
int gai_error, mmdb_error; int gai_error, mmdb_error;
MMDB_lookup_result_s result = MMDB_lookup_result_s result =
MMDB_lookup_string(mmdb, ipstr, &gai_error, &mmdb_error); MMDB_lookup_string(mmdb, ipstr, &gai_error, &mmdb_error);
if (0 != gai_error) { if (0 != gai_error) {
fprintf(stderr, fprintf(stderr,
"\n Error from call to getaddrinfo for %s - %s\n\n", "\n Error from call to getaddrinfo for %s - %s\n\n",
ipstr, ipstr,
#ifdef _WIN32 #ifdef _WIN32
gai_strerrorA(gai_error) gai_strerrorA(gai_error)
#else #else
gai_strerror(gai_error) gai_strerror(gai_error)
#endif #endif
); );
exit(3); exit(3);
} }
if (MMDB_SUCCESS != mmdb_error) { if (MMDB_SUCCESS != mmdb_error) {
fprintf(stderr, "\n Got an error from the maxminddb library: %s\n\n", fprintf(stderr,
"\n Got an error from the maxminddb library: %s\n\n",
MMDB_strerror(mmdb_error)); MMDB_strerror(mmdb_error));
exit(4); exit(4);
} }
return result; return result;
} }
LOCAL void random_ipv4(char *ip) static void random_ipv4(char *ip) {
{
// rand() is perfectly fine for this use case // rand() is perfectly fine for this use case
// coverity[dont_call] // coverity[dont_call]
int ip_int = rand(); int ip_int = rand();
uint8_t *bytes = (uint8_t *)&ip_int; uint8_t *bytes = (uint8_t *)&ip_int;
snprintf(ip, 16, "%u.%u.%u.%u", snprintf(ip,
*bytes, *(bytes + 1), *(bytes + 2), *(bytes + 3)); 16,
"%u.%u.%u.%u",
*bytes,
*(bytes + 1),
*(bytes + 2),
*(bytes + 3));
} }
#ifndef _WIN32 #ifndef _WIN32
struct thread_info { struct thread_info {
pthread_t id; pthread_t id;
int num; int num;
MMDB_s *mmdb; MMDB_s *mmdb;
int iterations; int iterations;
}; };
static bool start_threaded_benchmark( static bool start_threaded_benchmark(MMDB_s *const mmdb,
MMDB_s *const mmdb, int const thread_count,
int const thread_count, int const iterations) {
int const iterations) struct thread_info *const tinfo =
{ calloc(thread_count, sizeof(struct thread_info));
struct thread_info *const tinfo = calloc(thread_count,
sizeof(struct thread_info));
if (!tinfo) { if (!tinfo) {
fprintf(stderr, "calloc(): %s\n", strerror(errno)); fprintf(stderr, "calloc(): %s\n", strerror(errno));
return false; return false;
} }
// Using clock() isn't appropriate for multiple threads. It's CPU time, not // Using clock() isn't appropriate for multiple threads. It's CPU time, not
// wall time. // wall time.
long double const start_time = get_time(); long double const start_time = get_time();
if (start_time == -1) { if (start_time == -1) {
free(tinfo); free(tinfo);
skipping to change at line 688 skipping to change at line 694
return false; return false;
} }
long double const elapsed = end_time - start_time; long double const elapsed = end_time - start_time;
unsigned long long const total_ips = iterations * thread_count; unsigned long long const total_ips = iterations * thread_count;
long double rate = total_ips; long double rate = total_ips;
if (elapsed != 0) { if (elapsed != 0) {
rate = total_ips / elapsed; rate = total_ips / elapsed;
} }
fprintf( fprintf(stdout,
stdout, "Looked up %llu addresses using %d threads in %.2Lf seconds. %.2Lf "
"Looked up %llu addresses using %d threads in %.2Lf seconds. %.2Lf looku "lookups per second.\n",
ps per second.\n", total_ips,
total_ips, thread_count, elapsed, rate); thread_count,
elapsed,
rate);
return true; return true;
} }
static long double get_time(void) static long double get_time(void) {
{
// clock_gettime() is not present on OSX until 10.12. // clock_gettime() is not present on OSX until 10.12.
#ifdef HAVE_CLOCK_GETTIME #ifdef HAVE_CLOCK_GETTIME
struct timespec tp = { struct timespec tp = {
.tv_sec = 0, .tv_sec = 0,
.tv_nsec = 0, .tv_nsec = 0,
}; };
clockid_t clk_id = CLOCK_REALTIME; clockid_t clk_id = CLOCK_REALTIME;
#ifdef _POSIX_MONOTONIC_CLOCK #ifdef _POSIX_MONOTONIC_CLOCK
clk_id = CLOCK_MONOTONIC; clk_id = CLOCK_MONOTONIC;
#endif #endif
if (clock_gettime(clk_id, &tp) != 0) { if (clock_gettime(clk_id, &tp) != 0) {
fprintf(stderr, "clock_gettime(): %s\n", strerror(errno)); fprintf(stderr, "clock_gettime(): %s\n", strerror(errno));
return -1; return -1;
} }
skipping to change at line 723 skipping to change at line 731
#else #else
time_t t = time(NULL); time_t t = time(NULL);
if (t == (time_t)-1) { if (t == (time_t)-1) {
fprintf(stderr, "time(): %s\n", strerror(errno)); fprintf(stderr, "time(): %s\n", strerror(errno));
return -1; return -1;
} }
return (long double)t; return (long double)t;
#endif #endif
} }
static void *thread(void *arg) static void *thread(void *arg) {
{
const struct thread_info *const tinfo = arg; const struct thread_info *const tinfo = arg;
if (!tinfo) { if (!tinfo) {
fprintf(stderr, "thread(): %s\n", strerror(EINVAL)); fprintf(stderr, "thread(): %s\n", strerror(EINVAL));
return NULL; return NULL;
} }
char ip_address[16] = { 0 }; char ip_address[16] = {0};
for (int i = 0; i < tinfo->iterations; i++) { for (int i = 0; i < tinfo->iterations; i++) {
memset(ip_address, 0, 16); memset(ip_address, 0, 16);
random_ipv4(ip_address); random_ipv4(ip_address);
MMDB_lookup_result_s result = lookup_or_die(tinfo->mmdb, ip_address); MMDB_lookup_result_s result = lookup_or_die(tinfo->mmdb, ip_address);
if (!result.found_entry) { if (!result.found_entry) {
continue; continue;
} }
MMDB_entry_data_list_s *entry_data_list = NULL; MMDB_entry_data_list_s *entry_data_list = NULL;
int const status = MMDB_get_entry_data_list(&result.entry, int const status =
&entry_data_list); MMDB_get_entry_data_list(&result.entry, &entry_data_list);
if (status != MMDB_SUCCESS) { if (status != MMDB_SUCCESS) {
fprintf(stderr, "MMDB_get_entry_data_list(): %s\n", fprintf(stderr,
"MMDB_get_entry_data_list(): %s\n",
MMDB_strerror(status)); MMDB_strerror(status));
MMDB_free_entry_data_list(entry_data_list); MMDB_free_entry_data_list(entry_data_list);
return NULL; return NULL;
} }
if (!entry_data_list) { if (!entry_data_list) {
fprintf(stderr, "entry_data_list is NULL\n"); fprintf(stderr, "entry_data_list is NULL\n");
return NULL; return NULL;
} }
 End of changes. 58 change blocks. 
209 lines changed or deleted 207 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)