2 * GeoIP database support
4 * Copyright 2008, Gerald Combs <gerald@wireshark.org>
8 * Wireshark - Network traffic analyzer
9 * By Gerald Combs <gerald@wireshark.org>
10 * Copyright 1998 Gerald Combs
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28 * We currently return a single string for each database. Some databases,
29 * e.g. GeoIPCity, can return other info such as area codes.
38 #include <GeoIPCity.h>
43 #include "value_string.h"
45 #include <wsutil/report_err.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
51 /* Column names for each database type */
52 value_string geoip_type_name_vals[] = {
53 { GEOIP_COUNTRY_EDITION, "Country" },
54 { GEOIP_REGION_EDITION_REV0, "Region" },
55 { GEOIP_CITY_EDITION_REV0, "City"},
56 { GEOIP_ORG_EDITION, "Organization" },
57 { GEOIP_ISP_EDITION, "ISP" },
58 { GEOIP_CITY_EDITION_REV1, "City" },
59 { GEOIP_REGION_EDITION_REV1, "Region" },
60 { GEOIP_PROXY_EDITION, "Proxy" },
61 { GEOIP_ASNUM_EDITION, "AS Number" },
62 { GEOIP_NETSPEED_EDITION, "Speed" },
63 { GEOIP_DOMAIN_EDITION, "Domain" },
65 { GEOIP_COUNTRY_EDITION_V6, "Country" },
66 /* This is the closest thing to a version that GeoIP.h seems to provide. */
67 #if NUM_DB_TYPES > 31 /* 1.4.7 */
68 { GEOIP_CITY_EDITION_REV0_V6, "City"},
69 { GEOIP_CITY_EDITION_REV1_V6, "City"},
70 { GEOIP_ASNUM_EDITION_V6, "AS Number" },
71 { GEOIP_ISP_EDITION_V6, "ISP" },
72 { GEOIP_ORG_EDITION_V6, "Organization" },
73 { GEOIP_DOMAIN_EDITION_V6, "Domain" },
74 #endif /* NUM_DB_TYPES > 31 */
75 #if NUM_DB_TYPES > 32 /* 1.4.8 */
76 { GEOIP_NETSPEED_EDITION_REV1_V6, "Speed" },
77 #endif /* NUM_DB_TYPES > 32 */
78 #endif /* HAVE_GEOIP_V6 */
79 { WS_LAT_FAKE_EDITION, "Latitude" }, /* fake database */
80 { WS_LON_FAKE_EDITION, "Longitude" }, /* fake database */
84 static GArray *geoip_dat_arr = NULL;
86 /* UAT definitions. Copied from oids.c */
87 typedef struct _geoip_db_path_t {
91 static geoip_db_path_t *geoip_db_paths = NULL;
92 static guint num_geoip_db_paths = 0;
93 static uat_t *geoip_db_paths_uat = NULL;
94 UAT_DIRECTORYNAME_CB_DEF(geoip_mod, path, geoip_db_path_t)
98 * Scan a directory for GeoIP databases and load them
101 geoip_dat_scan_dir(const char *dirname) {
108 if ((dir = ws_dir_open(dirname, 0, NULL)) != NULL) {
109 while ((file = ws_dir_read_name(dir)) != NULL) {
110 name = ws_dir_get_name(file);
111 if (g_str_has_prefix(file, "Geo") && g_str_has_suffix(file, ".dat")) {
112 datname = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", dirname, name);
113 gi = GeoIP_open(datname, GEOIP_MEMORY_CACHE);
115 g_array_append_val(geoip_dat_arr, gi);
125 static void* geoip_db_path_copy_cb(void* dest, const void* orig, size_t len _U_) {
126 const geoip_db_path_t *m = (const geoip_db_path_t *)orig;
127 geoip_db_path_t *d = (geoip_db_path_t *)dest;
129 d->path = g_strdup(m->path);
134 static void geoip_db_path_free_cb(void* p) {
135 geoip_db_path_t *m = (geoip_db_path_t *)p;
139 /* called every time the user presses "Apply" or "OK in the list of
140 * GeoIP directories, and also once on startup */
141 static void geoip_db_post_update_cb(void) {
145 /* If we have old data, clear out the whole thing
146 * and start again. TODO: Just update the ones that
147 * have changed for efficiency's sake. */
149 /* skip the last two, as they are fake */
150 for (i = 0; i < geoip_db_num_dbs() - 2; i++) {
151 gi = g_array_index(geoip_dat_arr, GeoIP *, i);
156 /* don't use GeoIP_delete() on the two fake
157 * databases as they weren't created by GeoIP_new()
159 gi = g_array_index(geoip_dat_arr, GeoIP *, i);
163 gi = g_array_index(geoip_dat_arr, GeoIP *, i+1);
167 /* finally, free the array itself */
168 g_array_free(geoip_dat_arr, TRUE);
171 /* allocate the array */
172 geoip_dat_arr = g_array_new(FALSE, FALSE, sizeof(GeoIP *));
174 /* Walk all the directories */
175 for (i = 0; i < num_geoip_db_paths; i++) {
176 if (geoip_db_paths[i].path) {
177 geoip_dat_scan_dir(geoip_db_paths[i].path);
181 /* add fake databases for latitude and longitude
182 * (using "City" in reality) */
185 gi = (GeoIP *)g_malloc(sizeof (GeoIP));
186 gi->databaseType = WS_LAT_FAKE_EDITION;
187 g_array_append_val(geoip_dat_arr, gi);
190 gi = (GeoIP *)g_malloc(sizeof (GeoIP));
191 gi->databaseType = WS_LON_FAKE_EDITION;
192 g_array_append_val(geoip_dat_arr, gi);
196 * Initialize GeoIP lookups
199 geoip_db_pref_init(module_t *nameres)
201 static uat_field_t geoip_db_paths_fields[] = {
202 UAT_FLD_DIRECTORYNAME(geoip_mod, path, "GeoIP Database Directory", "The GeoIP database directory path"),
206 geoip_db_paths_uat = uat_new("GeoIP Database Paths",
207 sizeof(geoip_db_path_t),
210 (void**)&geoip_db_paths,
212 /* affects dissection of packets (as the GeoIP database is
213 used when dissecting), but not set of named fields */
214 UAT_AFFECTS_DISSECTION,
216 geoip_db_path_copy_cb,
218 geoip_db_path_free_cb,
219 geoip_db_post_update_cb,
220 geoip_db_paths_fields);
222 prefs_register_uat_preference(nameres,
224 "GeoIP database directories",
225 "Search paths for GeoIP address mapping databases.\n"
226 "Wireshark will look in each directory for files beginning\n"
227 "with \"Geo\" and ending with \".dat\".",
232 geoip_db_num_dbs(void) {
233 return geoip_dat_arr->len;
237 geoip_db_name(guint dbnum) {
240 gi = g_array_index(geoip_dat_arr, GeoIP *, dbnum);
242 return (val_to_str_const(gi->databaseType, geoip_type_name_vals, "Unknown database"));
244 return "Invalid database";
248 geoip_db_type(guint dbnum) {
251 gi = g_array_index(geoip_dat_arr, GeoIP *, dbnum);
253 return (gi->databaseType);
259 geoip_db_lookup_latlon4(guint32 addr, float *lat, float *lon) {
264 for (i = 0; i < geoip_db_num_dbs(); i++) {
265 gi = g_array_index(geoip_dat_arr, GeoIP *, i);
267 switch (gi->databaseType) {
268 case GEOIP_CITY_EDITION_REV0:
269 case GEOIP_CITY_EDITION_REV1:
270 gir = GeoIP_record_by_ipnum(gi, addr);
272 *lat = gir->latitude;
273 *lon = gir->longitude;
287 #define VAL_STR_LEN 100
290 * GeoIP 1.4.3 and later provide GeoIP_set_charset(), but in versions
291 * 1.4.3 to 1.4.6 that only applies to the City databases. I.e., it's
292 * possible to produce invalid UTF-8 sequences even if GeoIP_set_charset()
296 iso_8859_1_to_utf_8(char *val) {
299 utf8_val = g_convert(val, VAL_STR_LEN, "UTF-8", "ISO-8859-1", NULL, NULL, NULL);
301 g_strlcpy(val, utf8_val, VAL_STR_LEN);
308 geoip_db_lookup_ipv4(guint dbnum, guint32 addr, const char *not_found) {
311 const char *raw_val, *ret = not_found;
312 static char val[VAL_STR_LEN];
314 gi = g_array_index(geoip_dat_arr, GeoIP *, dbnum);
316 switch (gi->databaseType) {
317 case GEOIP_COUNTRY_EDITION:
318 raw_val = GeoIP_country_name_by_ipnum(gi, addr);
320 g_snprintf(val, VAL_STR_LEN, "%s", raw_val);
321 iso_8859_1_to_utf_8(val);
326 case GEOIP_CITY_EDITION_REV0:
327 case GEOIP_CITY_EDITION_REV1:
328 gir = GeoIP_record_by_ipnum(gi, addr);
329 if (gir && gir->city && gir->region) {
330 g_snprintf(val, VAL_STR_LEN, "%s, %s", gir->city, gir->region);
331 iso_8859_1_to_utf_8(val);
333 } else if (gir && gir->city) {
334 g_snprintf(val, VAL_STR_LEN, "%s", gir->city);
335 iso_8859_1_to_utf_8(val);
340 case GEOIP_ORG_EDITION:
341 case GEOIP_ISP_EDITION:
342 case GEOIP_ASNUM_EDITION:
343 raw_val = GeoIP_name_by_ipnum(gi, addr);
345 g_snprintf(val, VAL_STR_LEN, "%s", raw_val);
346 iso_8859_1_to_utf_8(val);
351 case WS_LAT_FAKE_EDITION:
356 if(geoip_db_lookup_latlon4(addr, &lat, &lon) == 0) {
357 g_snprintf(val, VAL_STR_LEN, "%f", lat);
358 c = strchr(val, ',');
359 if (c != NULL) *c = '.';
365 case WS_LON_FAKE_EDITION:
370 if(geoip_db_lookup_latlon4(addr, &lat, &lon) == 0) {
371 g_snprintf(val, VAL_STR_LEN, "%f", lon);
372 c = strchr(val, ',');
373 if (c != NULL) *c = '.';
389 #if NUM_DB_TYPES > 31 /* 1.4.7 */
390 geoip_db_lookup_latlon6(geoipv6_t addr, float *lat, float *lon) {
395 for (i = 0; i < geoip_db_num_dbs(); i++) {
396 gi = g_array_index(geoip_dat_arr, GeoIP *, i);
398 switch (gi->databaseType) {
399 case GEOIP_CITY_EDITION_REV0_V6:
400 case GEOIP_CITY_EDITION_REV1_V6:
401 gir = GeoIP_record_by_ipnum_v6(gi, addr);
403 *lat = gir->latitude;
404 *lon = gir->longitude;
417 #else /* NUM_DB_TYPES */
418 geoip_db_lookup_latlon6(geoipv6_t addr _U_, float *lat _U_, float *lon _U_) {
421 #endif /* NUM_DB_TYPES */
424 geoip_db_lookup_ipv6(guint dbnum, struct e_in6_addr addr, const char *not_found) {
427 const char *raw_val, *ret = not_found;
428 static char val[VAL_STR_LEN];
429 #if NUM_DB_TYPES > 31
433 memcpy(&gaddr, &addr, sizeof(addr));
435 gi = g_array_index(geoip_dat_arr, GeoIP *, dbnum);
437 switch (gi->databaseType) {
438 case GEOIP_COUNTRY_EDITION_V6:
439 raw_val = GeoIP_country_name_by_ipnum_v6(gi, gaddr);
441 g_snprintf(val, VAL_STR_LEN, "%s", raw_val);
442 iso_8859_1_to_utf_8(val);
447 #if NUM_DB_TYPES > 31
448 case GEOIP_CITY_EDITION_REV0_V6:
449 case GEOIP_CITY_EDITION_REV1_V6:
450 gir = GeoIP_record_by_ipnum_v6(gi, gaddr);
451 if (gir && gir->city && gir->region) {
452 g_snprintf(val, VAL_STR_LEN, "%s, %s", gir->city, gir->region);
453 iso_8859_1_to_utf_8(val);
455 } else if (gir && gir->city) {
456 g_snprintf(val, VAL_STR_LEN, "%s", gir->city);
457 iso_8859_1_to_utf_8(val);
462 case GEOIP_ORG_EDITION_V6:
463 case GEOIP_ISP_EDITION_V6:
464 case GEOIP_ASNUM_EDITION_V6:
465 raw_val = GeoIP_name_by_ipnum_v6(gi, gaddr);
467 g_snprintf(val, VAL_STR_LEN, "%s", raw_val);
468 iso_8859_1_to_utf_8(val);
472 #endif /* NUM_DB_TYPES */
474 case WS_LAT_FAKE_EDITION:
479 if(geoip_db_lookup_latlon6(gaddr, &lat, &lon) == 0) {
480 g_snprintf(val, VAL_STR_LEN, "%f", lat);
481 c = strchr(val, ',');
482 if (c != NULL) *c = '.';
488 case WS_LON_FAKE_EDITION:
493 if(geoip_db_lookup_latlon6(gaddr, &lat, &lon) == 0) {
494 g_snprintf(val, VAL_STR_LEN, "%f", lon);
495 c = strchr(val, ',');
496 if (c != NULL) *c = '.';
509 #else /* HAVE_GEOIP_V6 */
512 geoip_db_lookup_ipv6(guint dbnum _U_, struct e_in6_addr addr _U_, const char *not_found) {
516 #endif /* HAVE_GEOIP_V6 */
519 geoip_db_get_paths(void) {
520 GString* path_str = NULL;
525 path_str = g_string_new("");
527 path_separator = ';';
529 path_separator = ':';
532 for (i = 0; i < num_geoip_db_paths; i++) {
533 if (geoip_db_paths[i].path) {
534 g_string_append_printf(path_str, "%s%c", geoip_db_paths[i].path, path_separator);
538 g_string_truncate(path_str, path_str->len-1);
539 path_ret = path_str->str;
540 g_string_free(path_str, FALSE);
545 #else /* HAVE_GEOIP */
547 geoip_db_num_dbs(void) {
552 geoip_db_name(guint dbnum _U_) {
553 return "Unsupported";
557 geoip_db_type(guint dbnum _U_) {
562 geoip_db_lookup_ipv4(guint dbnum _U_, guint32 addr _U_, const char *not_found) {
567 geoip_db_lookup_ipv6(guint dbnum _U_, guint32 addr _U_, const char *not_found) {
572 geoip_db_get_paths(void) {
576 #endif /* HAVE_GEOIP */
584 * indent-tabs-mode: nil
587 * ex: set shiftwidth=4 tabstop=8 expandtab:
588 * :indentSize=4:tabSize=8:noTabs=true: