2 * GeoIP database support
4 * Copyright 2008, Gerald Combs <gerald@wireshark.org>
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 * We currently return a single string for each database. Some databases,
27 * e.g. GeoIPCity, can return other info such as area codes.
34 #include <epan/wmem/wmem.h>
38 #include <GeoIPCity.h>
40 #include <epan/geoip_db.h>
42 #include <epan/prefs.h>
43 #include <epan/value_string.h>
45 #include <wsutil/report_message.h>
46 #include <wsutil/file_util.h>
48 /* This needs to match NUM_GEOIP_COLS in hostlist_table.h */
49 #define MAX_GEOIP_DBS 13
52 * GeoIP_free is patched into our GeoIP distribution on Windows.
55 #ifndef HAVE_GEOIP_FREE
56 #define GeoIP_free free
59 /* Column names for each database type */
60 value_string geoip_type_name_vals[] = {
61 { GEOIP_COUNTRY_EDITION, "Country" },
62 { GEOIP_REGION_EDITION_REV0, "Region" },
63 { GEOIP_CITY_EDITION_REV0, "City"},
64 { GEOIP_ORG_EDITION, "Organization" },
65 { GEOIP_ISP_EDITION, "ISP" },
66 { GEOIP_CITY_EDITION_REV1, "City" },
67 { GEOIP_REGION_EDITION_REV1, "Region" },
68 { GEOIP_PROXY_EDITION, "Proxy" },
69 { GEOIP_ASNUM_EDITION, "AS Number" },
70 { GEOIP_NETSPEED_EDITION, "Speed" },
71 { GEOIP_DOMAIN_EDITION, "Domain" },
73 { GEOIP_COUNTRY_EDITION_V6, "Country" },
74 /* This is the closest thing to a version that GeoIP.h seems to provide. */
75 #if NUM_DB_TYPES > 31 /* 1.4.7 */
76 { GEOIP_CITY_EDITION_REV0_V6, "City"},
77 { GEOIP_CITY_EDITION_REV1_V6, "City"},
78 { GEOIP_ASNUM_EDITION_V6, "AS Number" },
79 { GEOIP_ISP_EDITION_V6, "ISP" },
80 { GEOIP_ORG_EDITION_V6, "Organization" },
81 { GEOIP_DOMAIN_EDITION_V6, "Domain" },
82 #endif /* NUM_DB_TYPES > 31 */
83 #if NUM_DB_TYPES > 32 /* 1.4.8 */
84 { GEOIP_NETSPEED_EDITION_REV1_V6, "Speed" },
85 #endif /* NUM_DB_TYPES > 32 */
86 #endif /* HAVE_GEOIP_V6 */
87 { WS_LAT_FAKE_EDITION, "Latitude" }, /* fake database */
88 { WS_LON_FAKE_EDITION, "Longitude" }, /* fake database */
92 static GArray *geoip_dat_arr = NULL;
94 /* UAT definitions. Copied from oids.c */
95 typedef struct _geoip_db_path_t {
99 static geoip_db_path_t *geoip_db_paths = NULL;
100 static guint num_geoip_db_paths = 0;
101 static const geoip_db_path_t geoip_db_system_paths[] = {
103 { "/usr/share/GeoIP" },
107 static uat_t *geoip_db_paths_uat = NULL;
108 UAT_DIRECTORYNAME_CB_DEF(geoip_mod, path, geoip_db_path_t)
112 * Scan a directory for GeoIP databases and load them
115 geoip_dat_scan_dir(const char *dirname) {
122 if ((dir = ws_dir_open(dirname, 0, NULL)) != NULL) {
123 while ((file = ws_dir_read_name(dir)) != NULL) {
124 name = ws_dir_get_name(file);
125 if (g_str_has_prefix(file, "Geo") && g_str_has_suffix(file, ".dat")) {
126 datname = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", dirname, name);
127 gi = GeoIP_open(datname, GEOIP_MEMORY_CACHE);
129 g_array_append_val(geoip_dat_arr, gi);
139 static void* geoip_db_path_copy_cb(void* dest, const void* orig, size_t len _U_) {
140 const geoip_db_path_t *m = (const geoip_db_path_t *)orig;
141 geoip_db_path_t *d = (geoip_db_path_t *)dest;
143 d->path = g_strdup(m->path);
148 static void geoip_db_path_free_cb(void* p) {
149 geoip_db_path_t *m = (geoip_db_path_t *)p;
153 static void geoip_dat_cleanup(void) {
157 /* If we have old data, clear out the whole thing
158 * and start again. TODO: Just update the ones that
159 * have changed for efficiency's sake. */
161 /* skip the last two, as they are fake */
162 for (i = 0; i < geoip_db_num_dbs() - 2; i++) {
163 gi = g_array_index(geoip_dat_arr, GeoIP *, i);
168 /* don't use GeoIP_delete() on the two fake
169 * databases as they weren't created by GeoIP_new()
171 gi = g_array_index(geoip_dat_arr, GeoIP *, i);
173 gi = g_array_index(geoip_dat_arr, GeoIP *, i+1);
175 /* finally, free the array itself */
176 g_array_free(geoip_dat_arr, TRUE);
177 geoip_dat_arr = NULL;
181 /* called every time the user presses "Apply" or "OK in the list of
182 * GeoIP directories, and also once on startup */
183 static void geoip_db_post_update_cb(void) {
189 /* allocate the array */
190 geoip_dat_arr = g_array_new(FALSE, FALSE, sizeof(GeoIP *));
192 /* First try the system paths */
193 for (i = 0; geoip_db_system_paths[i].path != NULL; i++) {
194 geoip_dat_scan_dir(geoip_db_system_paths[i].path);
197 /* Walk all the directories */
198 for (i = 0; i < num_geoip_db_paths; i++) {
199 if (geoip_db_paths[i].path) {
200 geoip_dat_scan_dir(geoip_db_paths[i].path);
204 /* add fake databases for latitude and longitude
205 * (using "City" in reality) */
208 gi = (GeoIP *)g_malloc(sizeof (GeoIP));
209 gi->databaseType = WS_LAT_FAKE_EDITION;
210 g_array_append_val(geoip_dat_arr, gi);
213 gi = (GeoIP *)g_malloc(sizeof (GeoIP));
214 gi->databaseType = WS_LON_FAKE_EDITION;
215 g_array_append_val(geoip_dat_arr, gi);
218 static void geoip_db_cleanup(void)
224 * Initialize GeoIP lookups
227 geoip_db_pref_init(module_t *nameres)
229 static uat_field_t geoip_db_paths_fields[] = {
230 UAT_FLD_DIRECTORYNAME(geoip_mod, path, "GeoIP Database Directory", "The GeoIP database directory path"),
234 geoip_db_paths_uat = uat_new("GeoIP Database Paths",
235 sizeof(geoip_db_path_t),
238 (void**)&geoip_db_paths,
240 /* affects dissection of packets (as the GeoIP database is
241 used when dissecting), but not set of named fields */
242 UAT_AFFECTS_DISSECTION,
244 geoip_db_path_copy_cb,
246 geoip_db_path_free_cb,
247 geoip_db_post_update_cb,
249 geoip_db_paths_fields);
251 prefs_register_uat_preference(nameres,
253 "GeoIP database directories",
254 "Search paths for GeoIP address mapping databases."
255 " Wireshark will look in each directory for files beginning"
256 " with \"Geo\" and ending with \".dat\".",
261 geoip_db_num_dbs(void) {
262 return (geoip_dat_arr == NULL) ? 0 : geoip_dat_arr->len;
266 geoip_db_name(guint dbnum) {
269 gi = g_array_index(geoip_dat_arr, GeoIP *, dbnum);
271 return (val_to_str_const(gi->databaseType, geoip_type_name_vals, "Unknown database"));
273 return "Invalid database";
277 geoip_db_type(guint dbnum) {
280 gi = g_array_index(geoip_dat_arr, GeoIP *, dbnum);
282 return (gi->databaseType);
288 geoip_db_lookup_latlon4(guint32 addr, float *lat, float *lon) {
293 for (i = 0; i < geoip_db_num_dbs(); i++) {
294 gi = g_array_index(geoip_dat_arr, GeoIP *, i);
296 switch (gi->databaseType) {
297 case GEOIP_CITY_EDITION_REV0:
298 case GEOIP_CITY_EDITION_REV1:
299 gir = GeoIP_record_by_ipnum(gi, addr);
301 *lat = gir->latitude;
302 *lon = gir->longitude;
303 GeoIPRecord_delete(gir);
318 * GeoIP 1.4.3 and later provide GeoIP_set_charset(), but in versions
319 * 1.4.3 to 1.4.6 that only applies to the City databases. I.e., it's
320 * possible to produce invalid UTF-8 sequences even if GeoIP_set_charset()
324 /* Ensure that a given db value is UTF-8 */
326 db_val_to_utf_8(const char *val, GeoIP *gi) {
328 if (GeoIP_charset(gi) == GEOIP_CHARSET_ISO_8859_1) {
330 utf8_val = g_convert(val, -1, "UTF-8", "ISO-8859-1", NULL, NULL, NULL);
332 char *ret_val = wmem_strdup(NULL, utf8_val);
337 return wmem_strdup(NULL, val);
341 geoip_db_lookup_ipv4(guint dbnum, guint32 addr, const char *not_found) {
346 char *val, *ret = NULL;
348 if (dbnum > geoip_db_num_dbs()) {
349 if (not_found == NULL)
352 return wmem_strdup(NULL, not_found);
354 gi = g_array_index(geoip_dat_arr, GeoIP *, dbnum);
356 switch (gi->databaseType) {
357 case GEOIP_COUNTRY_EDITION:
358 country = GeoIP_country_name_by_ipnum(gi, addr);
360 ret = db_val_to_utf_8(country, gi);
364 case GEOIP_CITY_EDITION_REV0:
365 case GEOIP_CITY_EDITION_REV1:
366 gir = GeoIP_record_by_ipnum(gi, addr);
367 if (gir && gir->city && gir->region) {
368 val = wmem_strdup_printf(NULL, "%s, %s", gir->city, gir->region);
369 ret = db_val_to_utf_8(val, gi);
370 wmem_free(NULL, val);
371 } else if (gir && gir->city) {
372 ret = db_val_to_utf_8(gir->city, gi);
375 GeoIPRecord_delete(gir);
378 case GEOIP_ORG_EDITION:
379 case GEOIP_ISP_EDITION:
380 case GEOIP_ASNUM_EDITION:
381 name = GeoIP_name_by_ipnum(gi, addr);
383 ret = db_val_to_utf_8(name, gi);
388 case WS_LAT_FAKE_EDITION:
393 if(geoip_db_lookup_latlon4(addr, &lat, &lon) == 0) {
394 val = wmem_strdup_printf(NULL, "%f", lat);
395 c = strchr(val, ',');
396 if (c != NULL) *c = '.';
402 case WS_LON_FAKE_EDITION:
407 if(geoip_db_lookup_latlon4(addr, &lat, &lon) == 0) {
408 val = wmem_strdup_printf(NULL, "%f", lon);
409 c = strchr(val, ',');
410 if (c != NULL) *c = '.';
422 if (not_found == NULL)
425 return wmem_strdup(NULL, not_found);
434 #if NUM_DB_TYPES > 31 /* 1.4.7 */
435 geoip_db_lookup_latlon6(geoipv6_t addr, float *lat, float *lon) {
440 for (i = 0; i < geoip_db_num_dbs(); i++) {
441 gi = g_array_index(geoip_dat_arr, GeoIP *, i);
443 switch (gi->databaseType) {
444 case GEOIP_CITY_EDITION_REV0_V6:
445 case GEOIP_CITY_EDITION_REV1_V6:
446 gir = GeoIP_record_by_ipnum_v6(gi, addr);
448 *lat = gir->latitude;
449 *lon = gir->longitude;
462 #else /* NUM_DB_TYPES */
463 geoip_db_lookup_latlon6(geoipv6_t addr _U_, float *lat _U_, float *lon _U_) {
466 #endif /* NUM_DB_TYPES */
469 geoip_db_lookup_ipv6(guint dbnum, ws_in6_addr addr, const char *not_found) {
474 char *val, *ret = NULL;
475 #if NUM_DB_TYPES > 31
478 if (dbnum > geoip_db_num_dbs()) {
479 if (not_found == NULL)
482 return wmem_strdup(NULL, not_found);
485 memcpy(&gaddr, &addr, sizeof(addr));
487 gi = g_array_index(geoip_dat_arr, GeoIP *, dbnum);
489 switch (gi->databaseType) {
490 case GEOIP_COUNTRY_EDITION_V6:
491 country = GeoIP_country_name_by_ipnum_v6(gi, gaddr);
493 ret = db_val_to_utf_8(country, gi);
497 #if NUM_DB_TYPES > 31
498 case GEOIP_CITY_EDITION_REV0_V6:
499 case GEOIP_CITY_EDITION_REV1_V6:
500 gir = GeoIP_record_by_ipnum_v6(gi, gaddr);
501 if (gir && gir->city && gir->region) {
502 val = wmem_strdup_printf(NULL, "%s, %s", gir->city, gir->region);
503 ret = db_val_to_utf_8(val, gi);
504 wmem_free(NULL, val);
505 } else if (gir && gir->city) {
506 ret = db_val_to_utf_8(gir->city, gi);
510 case GEOIP_ORG_EDITION_V6:
511 case GEOIP_ISP_EDITION_V6:
512 case GEOIP_ASNUM_EDITION_V6:
513 name = GeoIP_name_by_ipnum_v6(gi, gaddr);
515 ret = db_val_to_utf_8(name, gi);
519 #endif /* NUM_DB_TYPES */
521 case WS_LAT_FAKE_EDITION:
526 if(geoip_db_lookup_latlon6(gaddr, &lat, &lon) == 0) {
527 val = wmem_strdup_printf(NULL, "%f", lat);
528 c = strchr(val, ',');
529 if (c != NULL) *c = '.';
535 case WS_LON_FAKE_EDITION:
540 if(geoip_db_lookup_latlon6(gaddr, &lat, &lon) == 0) {
541 val = wmem_strdup_printf(NULL, "%f", lon);
542 c = strchr(val, ',');
543 if (c != NULL) *c = '.';
555 if (not_found == NULL)
558 return wmem_strdup(NULL, not_found);
564 #else /* HAVE_GEOIP_V6 */
567 geoip_db_lookup_ipv6(guint dbnum _U_, ws_in6_addr addr _U_, const char *not_found) {
568 if (not_found == NULL)
571 return wmem_strdup(NULL, not_found);
574 #endif /* HAVE_GEOIP_V6 */
577 geoip_db_get_paths(void) {
578 GString* path_str = NULL;
581 path_str = g_string_new("");
583 for (i = 0; geoip_db_system_paths[i].path != NULL; i++) {
584 g_string_append_printf(path_str,
585 "%s" G_SEARCHPATH_SEPARATOR_S, geoip_db_system_paths[i].path);
588 for (i = 0; i < num_geoip_db_paths; i++) {
589 if (geoip_db_paths[i].path) {
590 g_string_append_printf(path_str,
591 "%s" G_SEARCHPATH_SEPARATOR_S, geoip_db_paths[i].path);
595 g_string_truncate(path_str, path_str->len-1);
597 return g_string_free(path_str, FALSE);
600 #else /* HAVE_GEOIP */
602 geoip_db_num_dbs(void) {
607 geoip_db_name(guint dbnum _U_) {
608 return "Unsupported";
612 geoip_db_type(guint dbnum _U_) {
617 geoip_db_lookup_ipv4(guint dbnum _U_, guint32 addr _U_, const char *not_found) {
618 if (not_found == NULL)
621 return (char *)wmem_strdup(NULL, not_found);
625 geoip_db_lookup_ipv6(guint dbnum _U_, guint32 addr _U_, const char *not_found) {
626 if (not_found == NULL)
629 return (char *)wmem_strdup(NULL, not_found);
633 geoip_db_get_paths(void) {
637 #endif /* HAVE_GEOIP */
645 * indent-tabs-mode: nil
648 * ex: set shiftwidth=4 tabstop=8 expandtab:
649 * :indentSize=4:tabSize=8:noTabs=true: