2 * GeoIP database support
4 * Copyright 2018, Gerald Combs <gerald@wireshark.org>
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * SPDX-License-Identifier: GPL-2.0-or-later
15 #define WS_LOG_DOMAIN LOG_DOMAIN_MMDB
19 #include <epan/maxmind_db.h>
21 static mmdb_lookup_t mmdb_not_found
;
29 #ifdef HAVE_SYS_WAIT_H
33 #include <epan/wmem_scopes.h>
35 #include <epan/addr_resolv.h>
37 #include <epan/prefs.h>
39 #include <wsutil/report_message.h>
40 #include <wsutil/file_util.h>
41 #include <wsutil/filesystem.h>
42 #include <wsutil/ws_pipe.h>
43 #include <wsutil/strtoi.h>
44 #include <wsutil/glib-compat.h>
47 // - Add RBL lookups? Along with the "is this a spammer" information that most RBL databases
48 // provide, you can also fetch AS information: https://www.team-cymru.com/IP-ASN-mapping.html
49 // - Switch to a different format? I was going to use g_key_file_* to parse
50 // the mmdbresolve output, but it was easier to just parse it directly.
52 static GThread
*write_mmdbr_stdin_thread
;
53 static GAsyncQueue
*mmdbr_request_q
; // g_allocated char *
54 static char mmdbr_stop_sentinel
[] = "\x04"; // ASCII EOT. Could be anything.
56 // The GLib documentation says that g_rw_lock_reader_lock can be called
58 // https://developer-old.gnome.org/glib/stable/glib-Threads.html#g-rw-lock-reader-lock
59 // However, g_rw_lock_reader_lock calls AcquireSRWLockShared
60 // https://gitlab.gnome.org/GNOME/glib/blob/master/glib/gthread-win32.c#L206
61 // and SRW locks "cannot be acquired recursively"
62 // https://docs.microsoft.com/en-us/windows/desktop/Sync/slim-reader-writer--srw--locks
63 // https://devblogs.microsoft.com/oldnewthing/?p=93416
64 static GRWLock mmdbr_pipe_mtx
;
66 // Hashes of mmdb_lookup_t
67 typedef struct _mmdbr_response_t
{
70 ws_in4_addr ipv4_addr
;
71 ws_in6_addr ipv6_addr
;
72 mmdb_lookup_t mmdb_val
;
75 static wmem_map_t
*mmdb_ipv4_map
;
76 static wmem_map_t
*mmdb_ipv6_map
;
77 static GAsyncQueue
*mmdbr_response_q
; // g_allocated mmdbr_response_t *
78 static GThread
*read_mmdbr_stdout_thread
;
81 static wmem_map_t
*mmdb_str_chunk
;
82 static wmem_map_t
*mmdb_ipv6_chunk
;
84 /* Child mmdbresolve process */
85 static ws_pipe_t mmdbr_pipe
; // Requires mutex
87 /* UAT definitions. Copied from oids.c */
88 typedef struct _maxmind_db_path_t
{
92 static maxmind_db_path_t
*maxmind_db_paths
;
93 static unsigned num_maxmind_db_paths
;
94 static const maxmind_db_path_t maxmind_db_system_paths
[] = {
96 // XXX Properly expand "%ProgramData%\GeoIP".
97 { "C:\\ProgramData\\GeoIP" },
100 { "/usr/share/GeoIP" },
101 { "/var/lib/GeoIP" },
105 static uat_t
*maxmind_db_paths_uat
;
106 UAT_DIRECTORYNAME_CB_DEF(maxmind_mod
, path
, maxmind_db_path_t
)
108 static GPtrArray
*mmdb_file_arr
; // .mmdb files
110 static bool resolve_synchronously
;
112 static void mmdb_resolve_stop(void);
114 // Hopefully scanning a few lines asynchronously has less overhead than
115 // reading in a child thread.
116 #define RES_INVALID_LINE "# Invalid"
117 #define RES_STATUS_ERROR "mmdbresolve.status: false"
118 #define RES_COUNTRY_ISO_CODE "country.iso_code"
119 #define RES_COUNTRY_NAMES_EN "country.names.en"
120 #define RES_CITY_NAMES_EN "city.names.en"
121 #define RES_ASN_ORG "autonomous_system_organization"
122 #define RES_ASN_NUMBER "autonomous_system_number"
123 #define RES_LOCATION_LATITUDE "location.latitude"
124 #define RES_LOCATION_LONGITUDE "location.longitude"
125 #define RES_LOCATION_ACCURACY "location.accuracy_radius"
126 #define RES_END "# End "
128 // Interned strings and v6 addresses, similar to GLib's string chunks.
129 static const char *chunkify_string(char *key
) {
130 char *chunk_string
= (char *) wmem_map_lookup(mmdb_str_chunk
, key
);
133 chunk_string
= wmem_strdup(wmem_epan_scope(), key
);
134 wmem_map_insert(mmdb_str_chunk
, chunk_string
, chunk_string
);
140 static const void *chunkify_v6_addr(const ws_in6_addr
*addr
) {
141 void *chunk_v6_bytes
= (char *) wmem_map_lookup(mmdb_ipv6_chunk
, addr
->bytes
);
143 if (!chunk_v6_bytes
) {
144 chunk_v6_bytes
= wmem_memdup(wmem_epan_scope(), addr
->bytes
, sizeof(ws_in6_addr
));
145 wmem_map_insert(mmdb_ipv6_chunk
, chunk_v6_bytes
, chunk_v6_bytes
);
148 return chunk_v6_bytes
;
151 static void init_lookup(mmdb_lookup_t
*lookup
) {
152 mmdb_lookup_t empty_lookup
= { false, NULL
, NULL
, NULL
, 0, NULL
, DBL_MAX
, DBL_MAX
, 0 };
153 *lookup
= empty_lookup
;
156 static bool mmdbr_pipe_valid(void) {
157 g_rw_lock_reader_lock(&mmdbr_pipe_mtx
);
158 bool pipe_valid
= ws_pipe_valid(&mmdbr_pipe
);
159 g_rw_lock_reader_unlock(&mmdbr_pipe_mtx
);
163 // Writing to mmdbr_pipe.stdin_fd can block. Do so in a separate thread.
165 write_mmdbr_stdin_worker(void *data _U_
) {
168 size_t bytes_written
;
169 ws_debug("starting write worker");
172 // On some operating systems (most notably macOS), g_async_queue_timeout_pop
173 // will return immediately if we've been built with an older version of GLib:
174 // https://bugzilla.gnome.org/show_bug.cgi?id=673607
175 // Call g_async_queue_pop instead. When we need to stop processing,
176 // mmdb_resolve_stop will close our pipe and then push an invalid address
177 // (mmdbr_stop_sentinel) onto the queue.
178 char *request
= (char *) g_async_queue_pop(mmdbr_request_q
);
182 if (strcmp(request
, mmdbr_stop_sentinel
) == 0) {
187 ws_noisy("write %s ql %d", request
, g_async_queue_length(mmdbr_request_q
));
188 status
= g_io_channel_write_chars(mmdbr_pipe
.stdin_io
, request
, strlen(request
), &bytes_written
, &err
);
189 if (status
!= G_IO_STATUS_NORMAL
) {
190 ws_debug("write error %s. exiting thread.", err
->message
);
193 mmdb_response_t
*response
= g_new0(mmdb_response_t
, 1);
194 response
->fatal_err
= true;
195 g_async_queue_push(mmdbr_response_q
, response
); // Will be freed by maxmind_db_pop_response.
204 #define MAX_MMDB_LINE_LEN 2001
206 read_mmdbr_stdout_worker(void *data _U_
) {
207 mmdb_response_t
*response
= g_new0(mmdb_response_t
, 1);
208 char *line_buf
= g_new(char, MAX_MMDB_LINE_LEN
);
209 GString
*country_iso
= g_string_new("");
210 GString
*country
= g_string_new("");
211 GString
*city
= g_string_new("");
212 GString
*as_org
= g_string_new("");
213 char cur_addr
[WS_INET6_ADDRSTRLEN
] = { 0 };
215 size_t bytes_in_buffer
, search_offset
;
216 bool line_feed_found
;
218 ws_debug("starting read worker");
220 bytes_in_buffer
= search_offset
= 0;
221 line_feed_found
= false;
223 if (line_feed_found
) {
224 /* Line parsed, move all (if any) next line bytes to beginning */
225 bytes_in_buffer
-= (search_offset
+ 1);
226 memmove(line_buf
, &line_buf
[search_offset
+ 1], bytes_in_buffer
);
228 line_feed_found
= false;
231 while (search_offset
< bytes_in_buffer
) {
232 if (line_buf
[search_offset
] == '\n') {
233 line_buf
[search_offset
] = 0; /* NULL-terminate the string */
234 line_feed_found
= true;
240 if (!line_feed_found
) {
241 int space_available
= (int)(MAX_MMDB_LINE_LEN
- bytes_in_buffer
);
242 if (space_available
> 0) {
244 g_io_channel_read_chars(mmdbr_pipe
.stdout_io
, &line_buf
[bytes_in_buffer
],
245 space_available
, &bytes_read
, NULL
);
246 if (bytes_read
> 0) {
247 bytes_in_buffer
+= bytes_read
;
249 ws_debug("no pipe data. exiting thread.");
250 response
->fatal_err
= true;
251 g_async_queue_push(mmdbr_response_q
, response
); // Will be freed by maxmind_db_pop_response.
256 ws_debug("long line");
257 bytes_in_buffer
= g_strlcpy(line_buf
, RES_INVALID_LINE
, MAX_MMDB_LINE_LEN
);
258 search_offset
= bytes_in_buffer
;
263 char *line
= g_strstrip(line_buf
);
264 size_t line_len
= strlen(line
);
265 ws_noisy("read %zd bytes: %s", line_len
, line
);
266 if (line_len
< 1) continue;
268 char *val_start
= strchr(line
, ':');
270 val_start
= g_strstrip(val_start
+ 1);
273 if (line
[0] == '[' && line_len
> 2) {
274 // [init] or resolved address in square brackets.
275 line
[line_len
- 1] = '\0';
276 (void) g_strlcpy(cur_addr
, line
+ 1, WS_INET6_ADDRSTRLEN
);
277 if (ws_inet_pton4(cur_addr
, &response
->ipv4_addr
)) {
278 response
->is_ipv4
= true;
279 } else if (ws_inet_pton6(cur_addr
, &response
->ipv6_addr
)) {
280 response
->is_ipv4
= false;
281 } else if (strcmp(cur_addr
, "init") != 0) {
282 ws_debug("Invalid address: %s", cur_addr
);
286 init_lookup(&response
->mmdb_val
);
287 g_string_truncate(country_iso
, 0);
288 g_string_truncate(country
, 0);
289 g_string_truncate(city
, 0);
290 g_string_truncate(as_org
, 0);
291 } else if (strcmp(line
, RES_STATUS_ERROR
) == 0) {
292 // Error during init.
294 init_lookup(&response
->mmdb_val
);
296 } else if (val_start
&& g_str_has_prefix(line
, RES_COUNTRY_ISO_CODE
)) {
297 response
->mmdb_val
.found
= true;
298 g_string_assign(country_iso
, val_start
);
299 } else if (val_start
&& g_str_has_prefix(line
, RES_COUNTRY_NAMES_EN
)) {
300 response
->mmdb_val
.found
= true;
301 g_string_assign(country
, val_start
);
302 } else if (val_start
&& g_str_has_prefix(line
, RES_CITY_NAMES_EN
)) {
303 response
->mmdb_val
.found
= true;
304 g_string_assign(city
, val_start
);
305 } else if (val_start
&& g_str_has_prefix(line
, RES_ASN_ORG
)) {
306 response
->mmdb_val
.found
= true;
307 g_string_assign(as_org
, val_start
);
308 } else if (val_start
&& g_str_has_prefix(line
, RES_ASN_NUMBER
)) {
309 if (ws_strtou32(val_start
, NULL
, &response
->mmdb_val
.as_number
)) {
310 response
->mmdb_val
.found
= true;
312 ws_debug("Invalid ASN: %s", val_start
);
314 } else if (val_start
&& g_str_has_prefix(line
, RES_LOCATION_LATITUDE
)) {
315 response
->mmdb_val
.found
= true;
316 response
->mmdb_val
.latitude
= g_ascii_strtod(val_start
, NULL
);
317 } else if (val_start
&& g_str_has_prefix(line
, RES_LOCATION_LONGITUDE
)) {
318 response
->mmdb_val
.found
= true;
319 response
->mmdb_val
.longitude
= g_ascii_strtod(val_start
, NULL
);
320 } else if (val_start
&& g_str_has_prefix(line
, RES_LOCATION_ACCURACY
)) {
321 if (ws_strtou16(val_start
, NULL
, &response
->mmdb_val
.accuracy
)) {
322 response
->mmdb_val
.found
= true;
324 ws_debug("Invalid accuracy radius: %s", val_start
);
326 } else if (g_str_has_prefix(line
, RES_END
)) {
327 if (response
->mmdb_val
.found
&& cur_addr
[0]) {
328 if (country_iso
->len
) {
329 response
->mmdb_val
.country_iso
= g_strdup(country_iso
->str
);
332 response
->mmdb_val
.country
= g_strdup(country
->str
);
335 response
->mmdb_val
.city
= g_strdup(city
->str
);
338 response
->mmdb_val
.as_org
= g_strdup(as_org
->str
);
340 ws_debug("queued %p %s %s: city %s country %s", response
, response
->is_ipv4
? "v4" : "v6", cur_addr
, response
->mmdb_val
.city
, response
->mmdb_val
.country
);
341 g_async_queue_push(mmdbr_response_q
, response
); // Will be freed by maxmind_db_pop_response.
342 response
= g_new0(mmdb_response_t
, 1);
343 } else if (strcmp(cur_addr
, "init") != 0) {
344 if (resolve_synchronously
) {
345 // Synchronous lookups expect a 1-in 1-out resolution.
346 ws_debug("Pushing not-found result due to bad address");
347 g_async_queue_push(mmdbr_response_q
, response
); // Will be freed by maxmind_db_pop_response.
348 response
= g_new0(mmdb_response_t
, 1);
351 ws_debug("Discarded previous values due to bad address");
355 init_lookup(&response
->mmdb_val
);
359 g_string_free(country_iso
, TRUE
);
360 g_string_free(country
, TRUE
);
361 g_string_free(city
, TRUE
);
362 g_string_free(as_org
, TRUE
);
369 * Stop our mmdbresolve process.
372 static void mmdb_resolve_stop(void) {
374 mmdb_response_t
*response
;
376 while (mmdbr_request_q
&& (request
= (char *) g_async_queue_try_pop(mmdbr_request_q
)) != NULL
) {
380 if (!mmdbr_pipe_valid()) {
381 ws_debug("not cleaning up, invalid PID %"G_PID_FORMAT
, mmdbr_pipe
.pid
);
385 g_rw_lock_writer_lock(&mmdbr_pipe_mtx
);
387 g_async_queue_push(mmdbr_request_q
, g_strdup(mmdbr_stop_sentinel
));
389 g_rw_lock_writer_unlock(&mmdbr_pipe_mtx
);
391 // write_mmdbr_stdin_worker should exit
392 g_thread_join(write_mmdbr_stdin_thread
);
393 write_mmdbr_stdin_thread
= NULL
;
395 ws_debug("closing stdin IO");
396 g_io_channel_unref(mmdbr_pipe
.stdin_io
);
398 ws_debug("closing pid %"G_PID_FORMAT
, mmdbr_pipe
.pid
);
399 g_spawn_close_pid(mmdbr_pipe
.pid
);
401 /* Reap mmdbresolve, especially as we may not be shutting down.
402 * (E.g. when the configuration changed or it terminated unexpectedly,
403 * leading to this getting called when the worker threads exited.)
405 for (int retry_waitpid
= 0; retry_waitpid
<= 3; ++retry_waitpid
) {
406 if (waitpid(mmdbr_pipe
.pid
, NULL
, 0) != -1) {
407 /* waitpid() succeeded. We don't care about the exit status
408 * (we could log it in case it's unexpected)
411 /* waitpid() failed */
412 if (errno
== EINTR
) {
413 /* signal interrupted. Just try again */
415 } else if (errno
== ECHILD
) {
416 /* PID doesn't exist any more or isn't our child.
417 * possibly already reaped?
420 /* Unexpected error. */
421 ws_warning("Error from waitpid(): %s", g_strerror(errno
));
427 mmdbr_pipe
.pid
= WS_INVALID_PID
;
429 // child process notices broken stdin pipe and exits (breaks stdout pipe)
430 // read_mmdbr_stdout_worker should exit
432 g_thread_join(read_mmdbr_stdout_thread
);
433 read_mmdbr_stdout_thread
= NULL
;
435 ws_debug("closing stdout IO");
436 g_io_channel_unref(mmdbr_pipe
.stdout_io
);
438 while (mmdbr_response_q
&& (response
= (mmdb_response_t
*) g_async_queue_try_pop(mmdbr_response_q
)) != NULL
) {
439 g_free((char *) response
->mmdb_val
.country_iso
);
440 g_free((char *) response
->mmdb_val
.country
);
441 g_free((char *) response
->mmdb_val
.city
);
442 g_free((char *) response
->mmdb_val
.as_org
);
443 ws_debug("cleaned response %p", response
);
449 * Start an mmdbresolve process.
451 static void mmdb_resolve_start(void) {
452 if (!mmdbr_request_q
) {
453 mmdbr_request_q
= g_async_queue_new();
456 if (!mmdbr_response_q
) {
457 mmdbr_response_q
= g_async_queue_new();
460 if (!mmdb_ipv4_map
) {
461 mmdb_ipv4_map
= wmem_map_new(wmem_epan_scope(), g_direct_hash
, g_direct_equal
);
464 if (!mmdb_ipv6_map
) {
465 mmdb_ipv6_map
= wmem_map_new(wmem_epan_scope(), ipv6_oat_hash
, ipv6_equal
);
468 if (!mmdb_str_chunk
) {
469 mmdb_str_chunk
= wmem_map_new(wmem_epan_scope(), wmem_str_hash
, g_str_equal
);
472 if (!mmdb_ipv6_chunk
) {
473 mmdb_ipv6_chunk
= wmem_map_new(wmem_epan_scope(), ipv6_oat_hash
, ipv6_equal
);
476 if (!mmdb_file_arr
) {
477 ws_debug("unexpected mmdb_file_arr == NULL");
483 if (mmdb_file_arr
->len
== 0) {
484 ws_debug("no GeoIP databases found");
488 GPtrArray
*args
= g_ptr_array_new();
489 char *mmdbresolve
= get_executable_path("mmdbresolve");
490 g_ptr_array_add(args
, mmdbresolve
);
491 for (unsigned i
= 0; i
< mmdb_file_arr
->len
; i
++) {
492 g_ptr_array_add(args
, g_strdup("-f"));
493 g_ptr_array_add(args
, g_strdup((const char *)g_ptr_array_index(mmdb_file_arr
, i
)));
495 g_ptr_array_add(args
, NULL
);
497 ws_pipe_init(&mmdbr_pipe
);
498 GPid pipe_pid
= ws_pipe_spawn_async(&mmdbr_pipe
, args
);
499 ws_debug("spawned %s pid %"G_PID_FORMAT
, mmdbresolve
, pipe_pid
);
501 for (unsigned i
= 0; i
< args
->len
; i
++) {
502 char *arg
= (char *)g_ptr_array_index(args
, i
);
503 ws_debug("args: %s", arg
);
506 g_ptr_array_free(args
, true);
508 if (pipe_pid
== WS_INVALID_PID
) {
509 ws_pipe_init(&mmdbr_pipe
);
512 g_io_channel_unref(mmdbr_pipe
.stderr_io
);
514 /* Make sure that these close if we spawn a dumpcap process
515 * (capturing or capture stats/sparklines.)
517 fcntl(g_io_channel_unix_get_fd(mmdbr_pipe
.stdin_io
), F_SETFD
, FD_CLOEXEC
);
518 fcntl(g_io_channel_unix_get_fd(mmdbr_pipe
.stdout_io
), F_SETFD
, FD_CLOEXEC
);
521 write_mmdbr_stdin_thread
= g_thread_new("write_mmdbr_stdin_worker", write_mmdbr_stdin_worker
, NULL
);
522 read_mmdbr_stdout_thread
= g_thread_new("read_mmdbr_stdout_worker", read_mmdbr_stdout_worker
, NULL
);
526 * Scan a directory for GeoIP databases and load them
529 maxmind_db_scan_dir(const char *dirname
) {
533 if ((dir
= ws_dir_open(dirname
, 0, NULL
)) != NULL
) {
534 while ((file
= ws_dir_read_name(dir
)) != NULL
) {
535 const char *name
= ws_dir_get_name(file
);
536 if (g_str_has_suffix(file
, ".mmdb")) {
537 char *datname
= ws_strdup_printf("%s" G_DIR_SEPARATOR_S
"%s", dirname
, name
);
538 FILE *mmdb_f
= ws_fopen(datname
, "r");
540 g_ptr_array_add(mmdb_file_arr
, datname
);
552 static void* maxmind_db_path_copy_cb(void* dest
, const void* orig
, size_t len _U_
) {
553 const maxmind_db_path_t
*m
= (const maxmind_db_path_t
*)orig
;
554 maxmind_db_path_t
*d
= (maxmind_db_path_t
*)dest
;
556 d
->path
= g_strdup(m
->path
);
561 static void maxmind_db_path_free_cb(void* p
) {
562 maxmind_db_path_t
*m
= (maxmind_db_path_t
*)p
;
566 static void maxmind_db_cleanup(void) {
571 /* If we have old data, clear out the whole thing
572 * and start again. TODO: Just update the ones that
573 * have changed for efficiency's sake. */
575 for (i
= 0; i
< mmdb_file_arr
->len
; i
++) {
576 g_free(g_ptr_array_index(mmdb_file_arr
, i
));
578 /* finally, free the array itself */
579 g_ptr_array_free(mmdb_file_arr
, true);
580 mmdb_file_arr
= NULL
;
584 /* called every time the user presses "Apply" or "OK in the list of
585 * GeoIP directories, and also once on startup */
586 static void maxmind_db_post_update_cb(void) {
589 maxmind_db_cleanup();
591 /* allocate the array */
592 mmdb_file_arr
= g_ptr_array_new();
594 /* First try the system paths */
595 for (i
= 0; maxmind_db_system_paths
[i
].path
!= NULL
; i
++) {
596 maxmind_db_scan_dir(maxmind_db_system_paths
[i
].path
);
599 /* Walk all the directories */
600 for (i
= 0; i
< num_maxmind_db_paths
; i
++) {
601 if (maxmind_db_paths
[i
].path
) {
602 maxmind_db_scan_dir(maxmind_db_paths
[i
].path
);
606 if (gbl_resolv_flags
.maxmind_geoip
) {
607 mmdb_resolve_start();
612 * Initialize GeoIP lookups
615 maxmind_db_pref_init(module_t
*nameres
)
617 prefs_register_bool_preference(nameres
,
619 "Enable IP geolocation",
620 "Lookup geolocation information for IPv4 and IPv6 addresses with configured MaxMind databases",
621 &gbl_resolv_flags
.maxmind_geoip
);
623 static uat_field_t maxmind_db_paths_fields
[] = {
624 UAT_FLD_DIRECTORYNAME(maxmind_mod
, path
, "MaxMind Database Directory", "The MaxMind database directory path"),
628 maxmind_db_paths_uat
= uat_new("MaxMind Database Paths",
629 sizeof(maxmind_db_path_t
),
631 false, // Global, not per-profile
632 (void**)&maxmind_db_paths
,
633 &num_maxmind_db_paths
,
634 UAT_AFFECTS_DISSECTION
, // Affects IP4 and IPv6 packets.
636 maxmind_db_path_copy_cb
,
638 maxmind_db_path_free_cb
,
639 maxmind_db_post_update_cb
,
641 maxmind_db_paths_fields
);
643 prefs_register_uat_preference(nameres
,
645 "MaxMind database directories",
646 "Search paths for MaxMind address mapping databases."
647 " Wireshark will look in each directory for files ending"
649 maxmind_db_paths_uat
);
652 void maxmind_db_pref_cleanup(void)
657 void maxmind_db_pref_apply(void)
659 if (gbl_resolv_flags
.maxmind_geoip
) {
660 if (!mmdbr_pipe_valid()) {
661 mmdb_resolve_start();
664 if (mmdbr_pipe_valid()) {
670 static void maxmind_db_pop_response(mmdb_response_t
*response
)
672 /* This is only called in the main thread */
673 if (response
->fatal_err
== true) {
675 /* XXX: We could call mmdb_resolve_start() instead */
677 mmdb_lookup_t
*mmdb_val
= (mmdb_lookup_t
*) wmem_memdup(wmem_epan_scope(), &response
->mmdb_val
, sizeof(mmdb_lookup_t
));
678 if (response
->mmdb_val
.country_iso
) {
679 char *country_iso
= (char *) response
->mmdb_val
.country_iso
;
680 mmdb_val
->country_iso
= chunkify_string(country_iso
);
683 if (response
->mmdb_val
.country
) {
684 char *country
= (char *) response
->mmdb_val
.country
;
685 mmdb_val
->country
= chunkify_string(country
);
688 if (response
->mmdb_val
.city
) {
689 char *city
= (char *) response
->mmdb_val
.city
;
690 mmdb_val
->city
= chunkify_string(city
);
693 if (response
->mmdb_val
.as_org
) {
694 char *as_org
= (char *) response
->mmdb_val
.as_org
;
695 mmdb_val
->as_org
= chunkify_string(as_org
);
698 ws_debug("popped response %s city %s country %s", response
->is_ipv4
? "v4" : "v6", mmdb_val
->city
, mmdb_val
->country
);
700 if (response
->is_ipv4
) {
701 wmem_map_insert(mmdb_ipv4_map
, GUINT_TO_POINTER(response
->ipv4_addr
), mmdb_val
);
703 wmem_map_insert(mmdb_ipv6_map
, chunkify_v6_addr(&response
->ipv6_addr
), mmdb_val
);
709 static void maxmind_db_await_response(void)
711 mmdb_response_t
*response
;
713 if (mmdbr_response_q
!= NULL
) {
714 ws_debug("entering blocking wait for response");
715 response
= (mmdb_response_t
*) g_async_queue_pop(mmdbr_response_q
);
716 ws_debug("exiting blocking wait for response");
717 maxmind_db_pop_response(response
);
725 bool maxmind_db_lookup_process(void)
727 bool new_entries
= false;
728 mmdb_response_t
*response
;
730 while (mmdbr_response_q
&& (response
= (mmdb_response_t
*) g_async_queue_try_pop(mmdbr_response_q
)) != NULL
) {
732 maxmind_db_pop_response(response
);
738 const mmdb_lookup_t
*
739 maxmind_db_lookup_ipv4(const ws_in4_addr
*addr
) {
740 if (!gbl_resolv_flags
.maxmind_geoip
) {
741 return &mmdb_not_found
;
744 mmdb_lookup_t
*result
= (mmdb_lookup_t
*) wmem_map_lookup(mmdb_ipv4_map
, GUINT_TO_POINTER(*addr
));
747 result
= &mmdb_not_found
;
748 wmem_map_insert(mmdb_ipv4_map
, GUINT_TO_POINTER(*addr
), result
);
750 if (mmdbr_pipe_valid()) {
751 char addr_str
[WS_INET_ADDRSTRLEN
];
752 ws_inet_ntop4(addr
, addr_str
, WS_INET_ADDRSTRLEN
);
753 ws_debug("looking up %s", addr_str
);
754 g_async_queue_push(mmdbr_request_q
, ws_strdup_printf("%s\n", addr_str
));
755 if (resolve_synchronously
) {
756 maxmind_db_await_response();
757 result
= (mmdb_lookup_t
*) wmem_map_lookup(mmdb_ipv4_map
, GUINT_TO_POINTER(*addr
));
765 const mmdb_lookup_t
*
766 maxmind_db_lookup_ipv6(const ws_in6_addr
*addr
) {
767 if (!gbl_resolv_flags
.maxmind_geoip
) {
768 return &mmdb_not_found
;
771 mmdb_lookup_t
* result
= (mmdb_lookup_t
*) wmem_map_lookup(mmdb_ipv6_map
, addr
->bytes
);
774 result
= &mmdb_not_found
;
775 wmem_map_insert(mmdb_ipv6_map
, chunkify_v6_addr(addr
), result
);
777 if (mmdbr_pipe_valid()) {
778 char addr_str
[WS_INET6_ADDRSTRLEN
];
779 ws_inet_ntop6(addr
, addr_str
, WS_INET6_ADDRSTRLEN
);
780 ws_debug("looking up %s", addr_str
);
781 g_async_queue_push(mmdbr_request_q
, ws_strdup_printf("%s\n", addr_str
));
782 if (resolve_synchronously
) {
783 maxmind_db_await_response();
784 result
= (mmdb_lookup_t
*) wmem_map_lookup(mmdb_ipv6_map
, addr
->bytes
);
793 maxmind_db_get_paths(void) {
794 GString
* path_str
= NULL
;
797 path_str
= g_string_new("");
799 for (i
= 0; maxmind_db_system_paths
[i
].path
!= NULL
; i
++) {
800 g_string_append_printf(path_str
,
801 "%s" G_SEARCHPATH_SEPARATOR_S
, maxmind_db_system_paths
[i
].path
);
804 for (i
= 0; i
< num_maxmind_db_paths
; i
++) {
805 if (maxmind_db_paths
[i
].path
) {
806 g_string_append_printf(path_str
,
807 "%s" G_SEARCHPATH_SEPARATOR_S
, maxmind_db_paths
[i
].path
);
811 g_string_truncate(path_str
, path_str
->len
-1);
813 return g_string_free(path_str
, FALSE
);
817 maxmind_db_set_synchrony(bool synchronous
) {
818 resolve_synchronously
= synchronous
;
821 #else // HAVE_MAXMINDDB
824 maxmind_db_pref_init(module_t
*nameres _U_
) {}
827 maxmind_db_pref_cleanup(void) {}
830 maxmind_db_pref_apply(void) {}
833 maxmind_db_lookup_process(void)
838 const mmdb_lookup_t
*
839 maxmind_db_lookup_ipv4(const ws_in4_addr
*addr _U_
) {
840 return &mmdb_not_found
;
843 const mmdb_lookup_t
*
844 maxmind_db_lookup_ipv6(const ws_in6_addr
*addr _U_
) {
845 return &mmdb_not_found
;
849 maxmind_db_get_paths(void) {
854 maxmind_db_set_synchrony(bool synchronous _U_
) {
855 /* Nothing to set. */
858 #endif // HAVE_MAXMINDDB
867 * indent-tabs-mode: nil
870 * ex: set shiftwidth=4 tabstop=8 expandtab:
871 * :indentSize=4:tabSize=8:noTabs=true: