Revert "plaintext=decrypt_krb5_data => enc_key_t"
[metze/wireshark/wip.git] / epan / maxmind_db.c
1 /* maxmind_db.c
2  * GeoIP database support
3  *
4  * Copyright 2018, Gerald Combs <gerald@wireshark.org>
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * SPDX-License-Identifier: GPL-2.0-or-later
11  */
12
13 #include "config.h"
14
15 #include <glib.h>
16
17 #include <epan/maxmind_db.h>
18
19 static mmdb_lookup_t mmdb_not_found;
20
21 #ifdef HAVE_MAXMINDDB
22
23 #include <stdio.h>
24 #include <errno.h>
25
26 #include <epan/wmem/wmem.h>
27
28 #include <epan/addr_resolv.h>
29 #include <epan/uat.h>
30 #include <epan/prefs.h>
31
32 #include <wsutil/report_message.h>
33 #include <wsutil/file_util.h>
34 #include <wsutil/filesystem.h>
35 #include <wsutil/ws_pipe.h>
36 #include <wsutil/strtoi.h>
37
38 // To do:
39 // - Add RBL lookups? Along with the "is this a spammer" information that most RBL databases
40 //   provide, you can also fetch AS information: http://www.team-cymru.org/IP-ASN-mapping.html
41 // - Switch to a different format? I was going to use g_key_file_* to parse
42 //   the mmdbresolve output, but it was easier to just parse it directly.
43
44 static GThread *mmdbr_thread;
45 static GAsyncQueue *mmdbr_request_q; // g_allocated char *
46 static GMutex mmdbr_pipe_mtx;
47
48 // Hashes of mmdb_lookup_t
49 static wmem_map_t *mmdb_ipv4_map;
50 static wmem_map_t *mmdb_ipv6_map;
51 static gboolean new_entries;
52
53 // Interned strings
54 static wmem_map_t *mmdb_str_chunk;
55 static wmem_map_t *mmdb_ipv6_chunk;
56
57 /* Child mmdbresolve process */
58 static ws_pipe_t mmdbr_pipe; // Requires mutex
59 static FILE *mmdbr_stdout; // Requires mutex
60
61 /* UAT definitions. Copied from oids.c */
62 typedef struct _maxmind_db_path_t {
63     char* path;
64 } maxmind_db_path_t;
65
66 static maxmind_db_path_t *maxmind_db_paths;
67 static guint num_maxmind_db_paths;
68 static const maxmind_db_path_t maxmind_db_system_paths[] = {
69 #ifdef _WIN32
70     // XXX Properly expand "%ProgramData%\GeoIP".
71     { "C:\\ProgramData\\GeoIP" },
72     { "C:\\GeoIP" },
73 #else
74     { "/usr/share/GeoIP" },
75     { "/var/lib/GeoIP" },
76 #endif
77     { NULL }
78 };
79 static uat_t *maxmind_db_paths_uat;
80 UAT_DIRECTORYNAME_CB_DEF(maxmind_mod, path, maxmind_db_path_t)
81
82 static GPtrArray *mmdb_file_arr; // .mmdb files
83
84 #if 0
85 #define MMDB_DEBUG(...) { \
86     char *MMDB_DEBUG_MSG = g_strdup_printf(__VA_ARGS__); \
87     g_warning("mmdb: %s:%d %s", G_STRFUNC, __LINE__, MMDB_DEBUG_MSG); \
88     g_free(MMDB_DEBUG_MSG); \
89 }
90 #else
91 #define MMDB_DEBUG(...)
92 #endif
93
94 static void mmdb_resolve_stop(void);
95
96 // Hopefully scanning a few lines asynchronously has less overhead than
97 // reading in a child thread.
98 #define RES_STATUS_ERROR        "mmdbresolve.status: false"
99 #define RES_COUNTRY_ISO_CODE    "country.iso_code"
100 #define RES_COUNTRY_NAMES_EN    "country.names.en"
101 #define RES_CITY_NAMES_EN       "city.names.en"
102 #define RES_ASN_ORG             "autonomous_system_organization"
103 #define RES_ASN_NUMBER          "autonomous_system_number"
104 #define RES_LOCATION_LATITUDE   "location.latitude"
105 #define RES_LOCATION_LONGITUDE  "location.longitude"
106 #define RES_END                 "# End "
107
108 // Interned strings and v6 addresses, similar to GLib's string chunks.
109 static const char *chunkify_string(char *key) {
110     key = g_strstrip(key);
111     char *chunk_string = (char *) wmem_map_lookup(mmdb_str_chunk, key);
112
113     if (!chunk_string) {
114         chunk_string = wmem_strdup(wmem_epan_scope(), key);
115         wmem_map_insert(mmdb_str_chunk, chunk_string, chunk_string);
116     }
117
118     return chunk_string;
119 }
120
121 static const void *chunkify_v6_addr(const ws_in6_addr *addr) {
122     void *chunk_v6_bytes = (char *) wmem_map_lookup(mmdb_ipv6_chunk, addr->bytes);
123
124     if (!chunk_v6_bytes) {
125         chunk_v6_bytes = wmem_memdup(wmem_epan_scope(), addr->bytes, sizeof(ws_in6_addr));
126         wmem_map_insert(mmdb_ipv6_chunk, chunk_v6_bytes, chunk_v6_bytes);
127     }
128
129     return chunk_v6_bytes;
130 }
131
132 static void init_lookup(mmdb_lookup_t *lookup) {
133     mmdb_lookup_t empty_lookup = { FALSE, NULL, NULL, NULL, 0, NULL, DBL_MAX, DBL_MAX };
134     *lookup = empty_lookup;
135 }
136
137 static gboolean mmdbr_pipe_valid(void) {
138     g_mutex_lock(&mmdbr_pipe_mtx);
139     gboolean pipe_valid = ws_pipe_valid(&mmdbr_pipe);
140     g_mutex_unlock(&mmdbr_pipe_mtx);
141     return pipe_valid;
142 }
143
144 // Writing to mmdbr_pipe.stdin_fd can block. Do so in a separate thread.
145 #define MMDB_WAIT_TIME (150 * 1000) // microseconds
146 static gpointer
147 write_mmdbr_stdin_worker(gpointer data _U_) {
148     while (1) {
149         if (!mmdbr_pipe_valid()) {
150             // Should be due to mmdb_resolve_stop.
151             MMDB_DEBUG("invalid mmdbr pipe. exiting thread.");
152             return NULL;
153         }
154
155         char *request = (char *) g_async_queue_timeout_pop(mmdbr_request_q, MMDB_WAIT_TIME);
156         if (!request) {
157             continue;
158         }
159
160         MMDB_DEBUG("write %s ql %d", request, g_async_queue_length(mmdbr_request_q));
161         g_mutex_lock(&mmdbr_pipe_mtx);
162         ssize_t req_status = ws_write(mmdbr_pipe.stdin_fd, request, (unsigned int)strlen(request));
163         g_mutex_unlock(&mmdbr_pipe_mtx);
164         if (req_status < 0) {
165             MMDB_DEBUG("write error %s. exiting thread.", g_strerror(errno));
166             return NULL;
167         }
168         g_free(request);
169     }
170     return NULL;
171 }
172
173 static void
174 read_mmdbr_stdout(void) {
175     static char cur_addr[WS_INET6_ADDRSTRLEN];
176     static mmdb_lookup_t cur_lookup;
177
178     g_mutex_lock(&mmdbr_pipe_mtx);
179     if (!ws_pipe_valid(&mmdbr_pipe)) {
180         g_mutex_unlock(&mmdbr_pipe_mtx);
181         return;
182     }
183     MMDB_DEBUG("read mmdbr %d", ws_pipe_data_available(mmdbr_pipe.stdout_fd));
184
185     int read_buf_size = 2048;
186     char *read_buf = (char *) g_malloc(read_buf_size);
187
188     while (ws_pipe_data_available(mmdbr_pipe.stdout_fd)) {
189         read_buf[0] = '\0';
190         char *line = fgets(read_buf, read_buf_size, mmdbr_stdout);
191         if (!line || ferror(mmdbr_stdout)) {
192             MMDB_DEBUG("read error %s", g_strerror(errno));
193             mmdb_resolve_stop();
194             break;
195         }
196
197         line = g_strstrip(line);
198         size_t line_len = strlen(line);
199         MMDB_DEBUG("read %zd bytes, feof %d: %s", line_len, feof(mmdbr_stdout), line);
200         if (line_len < 1) continue;
201
202         char *val_start = strchr(line, ':');
203         if (val_start) val_start++;
204
205         if (line[0] == '[' && line_len > 2) {
206             // [init] or resolved address in square brackets.
207             line[line_len - 1] = '\0';
208             g_strlcpy(cur_addr, line + 1, WS_INET6_ADDRSTRLEN);
209             init_lookup(&cur_lookup);
210         } else if (strcmp(line, RES_STATUS_ERROR) == 0) {
211             // Error during init.
212             cur_addr[0] = '\0';
213             init_lookup(&cur_lookup);
214             mmdb_resolve_stop();
215         } else if (val_start && g_str_has_prefix(line, RES_COUNTRY_ISO_CODE)) {
216             cur_lookup.found = TRUE;
217             cur_lookup.country_iso = chunkify_string(val_start);
218         } else if (val_start && g_str_has_prefix(line, RES_COUNTRY_NAMES_EN)) {
219             cur_lookup.found = TRUE;
220             cur_lookup.country = chunkify_string(val_start);
221         } else if (val_start && g_str_has_prefix(line, RES_CITY_NAMES_EN)) {
222             cur_lookup.found = TRUE;
223             cur_lookup.city = chunkify_string(val_start);
224         } else if (val_start && g_str_has_prefix(line, RES_ASN_ORG)) {
225             cur_lookup.found = TRUE;
226             cur_lookup.as_org = chunkify_string(val_start);
227         } else if (val_start && g_str_has_prefix(line, RES_ASN_NUMBER)) {
228             if (ws_strtou32(val_start, NULL, &cur_lookup.as_number)) {
229                 cur_lookup.found = TRUE;
230             } else {
231                 MMDB_DEBUG("Invalid as number: %s", val_start);
232             }
233         } else if (val_start && g_str_has_prefix(line, RES_LOCATION_LATITUDE)) {
234             cur_lookup.found = TRUE;
235             cur_lookup.latitude = g_ascii_strtod(val_start, NULL);
236         } else if (val_start && g_str_has_prefix(line, RES_LOCATION_LONGITUDE)) {
237             cur_lookup.found = TRUE;
238             cur_lookup.longitude = g_ascii_strtod(val_start, NULL);
239         } else if (g_str_has_prefix(line, RES_END)) {
240             if (cur_lookup.found) {
241                 mmdb_lookup_t *mmdb_val = (mmdb_lookup_t *) wmem_memdup(wmem_epan_scope(), &cur_lookup, sizeof(cur_lookup));
242                 if (strstr(cur_addr, ".")) {
243                     MMDB_DEBUG("inserting v4 %p %s: city %s country %s", (void *) mmdb_val, cur_addr, mmdb_val->city, mmdb_val->country);
244                     guint32 addr;
245                     ws_inet_pton4(cur_addr, &addr);
246                     wmem_map_insert(mmdb_ipv4_map, GUINT_TO_POINTER(addr), mmdb_val);
247                     new_entries = TRUE;
248                 } else if (strstr(cur_addr, ":")) {
249                     MMDB_DEBUG("inserting v6 %p %s: city %s country %s", (void *) mmdb_val, cur_addr, mmdb_val->city, mmdb_val->country);
250                     ws_in6_addr addr;
251                     ws_inet_pton6(cur_addr, &addr);
252                     wmem_map_insert(mmdb_ipv6_map, chunkify_v6_addr(&addr), mmdb_val);
253                     new_entries = TRUE;
254                 }
255             }
256             cur_addr[0] = '\0';
257             init_lookup(&cur_lookup);
258         }
259     }
260     g_mutex_unlock(&mmdbr_pipe_mtx);
261
262     g_free(read_buf);
263 }
264
265 /**
266  * Stop our mmdbresolve process.
267  */
268 static void mmdb_resolve_stop(void) {
269     char *request;
270
271     while (mmdbr_request_q && (request = (char *) g_async_queue_try_pop(mmdbr_request_q)) != NULL) {
272         g_free(request);
273     }
274
275     if (!mmdbr_pipe_valid()) {
276         MMDB_DEBUG("not cleaning up, invalid PID %d", mmdbr_pipe.pid);
277         return;
278     }
279
280     g_mutex_lock(&mmdbr_pipe_mtx);
281     ws_close(mmdbr_pipe.stdin_fd);
282     fclose(mmdbr_stdout);
283     MMDB_DEBUG("closing pid %d", mmdbr_pipe.pid);
284     g_spawn_close_pid(mmdbr_pipe.pid);
285     mmdbr_pipe.pid = WS_INVALID_PID;
286     mmdbr_stdout = NULL;
287     g_mutex_unlock(&mmdbr_pipe_mtx);
288
289     g_thread_join(mmdbr_thread);
290     mmdbr_thread = NULL;
291 }
292
293 /**
294  * Start an mmdbresolve process.
295  */
296 static void mmdb_resolve_start(void) {
297     if (!mmdbr_request_q) {
298         mmdbr_request_q = g_async_queue_new();
299     }
300
301     if (!mmdb_ipv4_map) {
302         mmdb_ipv4_map = wmem_map_new(wmem_epan_scope(), g_direct_hash, g_direct_equal);
303     }
304
305     if (!mmdb_ipv6_map) {
306         mmdb_ipv6_map = wmem_map_new(wmem_epan_scope(), ipv6_oat_hash, ipv6_equal);
307     }
308
309     if (!mmdb_str_chunk) {
310         mmdb_str_chunk = wmem_map_new(wmem_epan_scope(), wmem_str_hash, g_str_equal);
311     }
312
313     if (!mmdb_ipv6_chunk) {
314         mmdb_ipv6_chunk = wmem_map_new(wmem_epan_scope(), ipv6_oat_hash, ipv6_equal);
315     }
316
317     if (!mmdb_file_arr) {
318         MMDB_DEBUG("unexpected mmdb_file_arr == NULL");
319         return;
320     }
321
322     mmdb_resolve_stop();
323
324     if (mmdb_file_arr->len == 0) {
325         MMDB_DEBUG("no GeoIP databases found");
326         return;
327     }
328
329     GPtrArray *args = g_ptr_array_new();
330     char *mmdbresolve = g_strdup_printf("%s%c%s", get_progfile_dir(), G_DIR_SEPARATOR, "mmdbresolve");
331     g_ptr_array_add(args, mmdbresolve);
332     for (guint i = 0; i < mmdb_file_arr->len; i++) {
333         g_ptr_array_add(args, g_strdup("-f"));
334         g_ptr_array_add(args, g_strdup((const gchar *)g_ptr_array_index(mmdb_file_arr, i)));
335     }
336     g_ptr_array_add(args, NULL);
337
338     ws_pipe_init(&mmdbr_pipe);
339     mmdbr_stdout = NULL;
340     GPid pipe_pid = ws_pipe_spawn_async(&mmdbr_pipe, args);
341     MMDB_DEBUG("spawned %s pid %d", mmdbresolve, pipe_pid);
342
343     for (guint i = 0; i < args->len; i++) {
344         char *arg = (char *)g_ptr_array_index(args, i);
345         MMDB_DEBUG("args: %s", arg);
346         g_free(arg);
347     }
348     g_ptr_array_free(args, TRUE);
349
350     if (pipe_pid == WS_INVALID_PID) {
351         ws_pipe_init(&mmdbr_pipe);
352         return;
353     }
354
355     // XXX Should we set O_NONBLOCK similar to dumpcap?
356     mmdbr_stdout = ws_fdopen(mmdbr_pipe.stdout_fd, "r");
357     setvbuf(mmdbr_stdout, NULL, _IONBF, 0);
358
359     mmdbr_thread = g_thread_new("write_mmdbr_stdin_worker", write_mmdbr_stdin_worker, NULL);
360 }
361
362 /**
363  * Scan a directory for GeoIP databases and load them
364  */
365 static void
366 maxmind_db_scan_dir(const char *dirname) {
367     WS_DIR *dir;
368     WS_DIRENT *file;
369
370     if ((dir = ws_dir_open(dirname, 0, NULL)) != NULL) {
371         while ((file = ws_dir_read_name(dir)) != NULL) {
372             const char *name = ws_dir_get_name(file);
373             if (g_str_has_suffix(file, ".mmdb")) {
374                 char *datname = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", dirname, name);
375                 FILE *mmdb_f = ws_fopen(datname, "r");
376                 if (mmdb_f) {
377                     g_ptr_array_add(mmdb_file_arr, datname);
378                     fclose(mmdb_f);
379                 } else {
380                     g_free(datname);
381                 }
382             }
383         }
384         ws_dir_close (dir);
385     }
386 }
387
388 /* UAT callbacks */
389 static void* maxmind_db_path_copy_cb(void* dest, const void* orig, size_t len _U_) {
390     const maxmind_db_path_t *m = (const maxmind_db_path_t *)orig;
391     maxmind_db_path_t *d = (maxmind_db_path_t *)dest;
392
393     d->path = g_strdup(m->path);
394
395     return d;
396 }
397
398 static void maxmind_db_path_free_cb(void* p) {
399     maxmind_db_path_t *m = (maxmind_db_path_t *)p;
400     g_free(m->path);
401 }
402
403 static void maxmind_db_cleanup(void) {
404     guint i;
405
406     mmdb_resolve_stop();
407
408     /* If we have old data, clear out the whole thing
409      * and start again. TODO: Just update the ones that
410      * have changed for efficiency's sake. */
411     if (mmdb_file_arr) {
412         for (i = 0; i < mmdb_file_arr->len; i++) {
413             g_free(g_ptr_array_index(mmdb_file_arr, i));
414         }
415         /* finally, free the array itself */
416         g_ptr_array_free(mmdb_file_arr, TRUE);
417         mmdb_file_arr = NULL;
418     }
419 }
420
421 /* called every time the user presses "Apply" or "OK in the list of
422  * GeoIP directories, and also once on startup */
423 static void maxmind_db_post_update_cb(void) {
424     guint i;
425
426     maxmind_db_cleanup();
427
428     /* allocate the array */
429     mmdb_file_arr = g_ptr_array_new();
430
431     /* First try the system paths */
432     for (i = 0; maxmind_db_system_paths[i].path != NULL; i++) {
433         maxmind_db_scan_dir(maxmind_db_system_paths[i].path);
434     }
435
436     /* Walk all the directories */
437     for (i = 0; i < num_maxmind_db_paths; i++) {
438         if (maxmind_db_paths[i].path) {
439             maxmind_db_scan_dir(maxmind_db_paths[i].path);
440         }
441     }
442
443     mmdb_resolve_start();
444 }
445
446 /**
447  * Initialize GeoIP lookups
448  */
449 void
450 maxmind_db_pref_init(module_t *nameres)
451 {
452     static uat_field_t maxmind_db_paths_fields[] = {
453         UAT_FLD_DIRECTORYNAME(maxmind_mod, path, "MaxMind Database Directory", "The MaxMind database directory path"),
454         UAT_END_FIELDS
455     };
456
457     maxmind_db_paths_uat = uat_new("MaxMind Database Paths",
458             sizeof(maxmind_db_path_t),
459             "maxmind_db_paths",
460             FALSE, // Global, not per-profile
461             (void**)&maxmind_db_paths,
462             &num_maxmind_db_paths,
463             UAT_AFFECTS_DISSECTION, // Affects IP4 and IPv6 packets.
464             "ChMaxMindDbPaths",
465             maxmind_db_path_copy_cb,
466             NULL, // update_cb
467             maxmind_db_path_free_cb,
468             maxmind_db_post_update_cb,
469             maxmind_db_cleanup,
470             maxmind_db_paths_fields);
471
472     prefs_register_uat_preference(nameres,
473             "maxmind_db_paths",
474             "MaxMind database directories",
475             "Search paths for MaxMind address mapping databases."
476             " Wireshark will look in each directory for files ending"
477             " with \".mmdb\".",
478             maxmind_db_paths_uat);
479 }
480
481 void maxmind_db_pref_cleanup(void)
482 {
483     mmdb_resolve_stop();
484 }
485
486 /**
487  * Public API
488  */
489
490 gboolean maxmind_db_lookup_process(void)
491 {
492     if (mmdbr_pipe_valid()) {
493         read_mmdbr_stdout();
494     }
495
496     gboolean prev_ne = new_entries;
497     new_entries = FALSE;
498     return prev_ne;
499 }
500
501 const mmdb_lookup_t *
502 maxmind_db_lookup_ipv4(guint32 addr) {
503     mmdb_lookup_t *result = (mmdb_lookup_t *) wmem_map_lookup(mmdb_ipv4_map, GUINT_TO_POINTER(addr));
504
505     if (!result) {
506         // Try again, mainly so that we empty our pipe buffers.
507         read_mmdbr_stdout();
508         result = (mmdb_lookup_t *) wmem_map_lookup(mmdb_ipv4_map, GUINT_TO_POINTER(addr));
509     }
510
511     if (!result) {
512         if (mmdbr_pipe_valid()) {
513             char addr_str[WS_INET_ADDRSTRLEN];
514             ws_inet_ntop4(&addr, addr_str, WS_INET_ADDRSTRLEN);
515             MMDB_DEBUG("looking up %s", addr_str);
516             g_async_queue_push(mmdbr_request_q, g_strdup_printf("%s\n", addr_str));
517         }
518
519         result = &mmdb_not_found;
520         wmem_map_insert(mmdb_ipv4_map, GUINT_TO_POINTER(addr), result);
521     }
522
523     return result;
524 }
525
526 const mmdb_lookup_t *
527 maxmind_db_lookup_ipv6(const ws_in6_addr *addr) {
528     mmdb_lookup_t * result = (mmdb_lookup_t *) wmem_map_lookup(mmdb_ipv6_map, addr->bytes);
529
530     if (!result) {
531         // Try again, mainly so that we empty our pipe buffers.
532         read_mmdbr_stdout();
533         result = (mmdb_lookup_t *) wmem_map_lookup(mmdb_ipv6_map, addr->bytes);
534     }
535
536     if (!result) {
537         if (mmdbr_pipe_valid()) {
538             char addr_str[WS_INET6_ADDRSTRLEN];
539             ws_inet_ntop6(addr, addr_str, WS_INET6_ADDRSTRLEN);
540             MMDB_DEBUG("looking up %s", addr_str);
541             g_async_queue_push(mmdbr_request_q, g_strdup_printf("%s\n", addr_str));
542         }
543
544         result = &mmdb_not_found;
545         wmem_map_insert(mmdb_ipv6_map, chunkify_v6_addr(addr), result);
546     }
547
548     return result;
549 }
550
551 gchar *
552 maxmind_db_get_paths(void) {
553     GString* path_str = NULL;
554     guint i;
555
556     path_str = g_string_new("");
557
558     for (i = 0; maxmind_db_system_paths[i].path != NULL; i++) {
559         g_string_append_printf(path_str,
560                 "%s" G_SEARCHPATH_SEPARATOR_S, maxmind_db_system_paths[i].path);
561     }
562
563     for (i = 0; i < num_maxmind_db_paths; i++) {
564         if (maxmind_db_paths[i].path) {
565             g_string_append_printf(path_str,
566                     "%s" G_SEARCHPATH_SEPARATOR_S, maxmind_db_paths[i].path);
567         }
568     }
569
570     g_string_truncate(path_str, path_str->len-1);
571
572     return g_string_free(path_str, FALSE);
573 }
574
575 #else // HAVE_MAXMINDDB
576
577 void
578 maxmind_db_pref_init(module_t *nameres _U_) {}
579
580 void
581 maxmind_db_pref_cleanup(void) {}
582
583
584 gboolean
585 maxmind_db_lookup_process(void)
586 {
587     return FALSE;
588 }
589
590 const mmdb_lookup_t *
591 maxmind_db_lookup_ipv4(guint32 addr _U_) {
592     return &mmdb_not_found;
593 }
594
595 const mmdb_lookup_t *
596 maxmind_db_lookup_ipv6(const ws_in6_addr *addr _U_) {
597     return &mmdb_not_found;
598 }
599
600 gchar *
601 maxmind_db_get_paths(void) {
602     return g_strdup("");
603 }
604 #endif // HAVE_MAXMINDDB
605
606
607 /*
608  * Editor modelines
609  *
610  * Local Variables:
611  * c-basic-offset: 4
612  * tab-width: 8
613  * indent-tabs-mode: nil
614  * End:
615  *
616  * ex: set shiftwidth=4 tabstop=8 expandtab:
617  * :indentSize=4:tabSize=8:noTabs=true:
618  */