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
= (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_init(void) {
235 geoip_dat_arr
= g_array_new(FALSE
, FALSE
, sizeof(GeoIP
*));
237 for (i
= 0; i
< num_geoip_db_paths
; i
++) {
238 if (geoip_db_paths
[i
].path
) {
239 geoip_dat_scan_dir(geoip_db_paths
[i
].path
);
243 /* add fake databases for latitude and longitude (using "City" in reality) */
248 gi_lat
= (GeoIP
*)g_malloc(sizeof (GeoIP
));
249 gi_lat
->databaseType
= WS_LAT_FAKE_EDITION
;
250 g_array_append_val(geoip_dat_arr
, gi_lat
);
251 gi_lon
= (GeoIP
*)g_malloc(sizeof (GeoIP
));
252 gi_lon
->databaseType
= WS_LON_FAKE_EDITION
;
253 g_array_append_val(geoip_dat_arr
, gi_lon
);
258 geoip_db_num_dbs(void) {
259 return geoip_dat_arr
->len
;
263 geoip_db_name(guint dbnum
) {
266 gi
= g_array_index(geoip_dat_arr
, GeoIP
*, dbnum
);
268 return (val_to_str_const(gi
->databaseType
, geoip_type_name_vals
, "Unknown database"));
270 return "Invalid database";
274 geoip_db_type(guint dbnum
) {
277 gi
= g_array_index(geoip_dat_arr
, GeoIP
*, dbnum
);
279 return (gi
->databaseType
);
285 geoip_db_lookup_latlon4(guint32 addr
, float *lat
, float *lon
) {
290 for (i
= 0; i
< geoip_db_num_dbs(); i
++) {
291 gi
= g_array_index(geoip_dat_arr
, GeoIP
*, i
);
293 switch (gi
->databaseType
) {
294 case GEOIP_CITY_EDITION_REV0
:
295 case GEOIP_CITY_EDITION_REV1
:
296 gir
= GeoIP_record_by_ipnum(gi
, addr
);
298 *lat
= gir
->latitude
;
299 *lon
= gir
->longitude
;
313 #define VAL_STR_LEN 100
316 * GeoIP 1.4.3 and later provide GeoIP_set_charset(), but in versions
317 * 1.4.3 to 1.4.6 that only applies to the City databases. I.e., it's
318 * possible to produce invalid UTF-8 sequences even if GeoIP_set_charset()
322 iso_8859_1_to_utf_8(char *val
) {
325 utf8_val
= g_convert(val
, VAL_STR_LEN
, "UTF-8", "ISO-8859-1", NULL
, NULL
, NULL
);
327 g_strlcpy(val
, utf8_val
, VAL_STR_LEN
);
334 geoip_db_lookup_ipv4(guint dbnum
, guint32 addr
, const char *not_found
) {
337 const char *raw_val
, *ret
= not_found
;
338 static char val
[VAL_STR_LEN
];
340 gi
= g_array_index(geoip_dat_arr
, GeoIP
*, dbnum
);
342 switch (gi
->databaseType
) {
343 case GEOIP_COUNTRY_EDITION
:
344 raw_val
= GeoIP_country_name_by_ipnum(gi
, addr
);
346 g_snprintf(val
, VAL_STR_LEN
, "%s", raw_val
);
347 iso_8859_1_to_utf_8(val
);
352 case GEOIP_CITY_EDITION_REV0
:
353 case GEOIP_CITY_EDITION_REV1
:
354 gir
= GeoIP_record_by_ipnum(gi
, addr
);
355 if (gir
&& gir
->city
&& gir
->region
) {
356 g_snprintf(val
, VAL_STR_LEN
, "%s, %s", gir
->city
, gir
->region
);
357 iso_8859_1_to_utf_8(val
);
359 } else if (gir
&& gir
->city
) {
360 g_snprintf(val
, VAL_STR_LEN
, "%s", gir
->city
);
361 iso_8859_1_to_utf_8(val
);
366 case GEOIP_ORG_EDITION
:
367 case GEOIP_ISP_EDITION
:
368 case GEOIP_ASNUM_EDITION
:
369 raw_val
= GeoIP_name_by_ipnum(gi
, addr
);
371 g_snprintf(val
, VAL_STR_LEN
, "%s", raw_val
);
372 iso_8859_1_to_utf_8(val
);
377 case WS_LAT_FAKE_EDITION
:
382 if(geoip_db_lookup_latlon4(addr
, &lat
, &lon
) == 0) {
383 g_snprintf(val
, VAL_STR_LEN
, "%f", lat
);
384 c
= strchr(val
, ',');
385 if (c
!= NULL
) *c
= '.';
391 case WS_LON_FAKE_EDITION
:
396 if(geoip_db_lookup_latlon4(addr
, &lat
, &lon
) == 0) {
397 g_snprintf(val
, VAL_STR_LEN
, "%f", lon
);
398 c
= strchr(val
, ',');
399 if (c
!= NULL
) *c
= '.';
415 #if NUM_DB_TYPES > 31 /* 1.4.7 */
416 geoip_db_lookup_latlon6(geoipv6_t addr
, float *lat
, float *lon
) {
421 for (i
= 0; i
< geoip_db_num_dbs(); i
++) {
422 gi
= g_array_index(geoip_dat_arr
, GeoIP
*, i
);
424 switch (gi
->databaseType
) {
425 case GEOIP_CITY_EDITION_REV0_V6
:
426 case GEOIP_CITY_EDITION_REV1_V6
:
427 gir
= GeoIP_record_by_ipnum_v6(gi
, addr
);
429 *lat
= gir
->latitude
;
430 *lon
= gir
->longitude
;
443 #else /* NUM_DB_TYPES */
444 geoip_db_lookup_latlon6(geoipv6_t addr _U_
, float *lat _U_
, float *lon _U_
) {
447 #endif /* NUM_DB_TYPES */
450 geoip_db_lookup_ipv6(guint dbnum
, struct e_in6_addr addr
, const char *not_found
) {
453 const char *raw_val
, *ret
= not_found
;
454 static char val
[VAL_STR_LEN
];
455 #if NUM_DB_TYPES > 31
459 memcpy(&gaddr
, &addr
, sizeof(addr
));
461 gi
= g_array_index(geoip_dat_arr
, GeoIP
*, dbnum
);
463 switch (gi
->databaseType
) {
464 case GEOIP_COUNTRY_EDITION_V6
:
465 raw_val
= GeoIP_country_name_by_ipnum_v6(gi
, gaddr
);
467 g_snprintf(val
, VAL_STR_LEN
, "%s", raw_val
);
468 iso_8859_1_to_utf_8(val
);
473 #if NUM_DB_TYPES > 31
474 case GEOIP_CITY_EDITION_REV0_V6
:
475 case GEOIP_CITY_EDITION_REV1_V6
:
476 gir
= GeoIP_record_by_ipnum_v6(gi
, gaddr
);
477 if (gir
&& gir
->city
&& gir
->region
) {
478 g_snprintf(val
, VAL_STR_LEN
, "%s, %s", gir
->city
, gir
->region
);
479 iso_8859_1_to_utf_8(val
);
481 } else if (gir
&& gir
->city
) {
482 g_snprintf(val
, VAL_STR_LEN
, "%s", gir
->city
);
483 iso_8859_1_to_utf_8(val
);
488 case GEOIP_ORG_EDITION_V6
:
489 case GEOIP_ISP_EDITION_V6
:
490 case GEOIP_ASNUM_EDITION_V6
:
491 raw_val
= GeoIP_name_by_ipnum_v6(gi
, gaddr
);
493 g_snprintf(val
, VAL_STR_LEN
, "%s", raw_val
);
494 iso_8859_1_to_utf_8(val
);
498 #endif /* NUM_DB_TYPES */
500 case WS_LAT_FAKE_EDITION
:
505 if(geoip_db_lookup_latlon6(gaddr
, &lat
, &lon
) == 0) {
506 g_snprintf(val
, VAL_STR_LEN
, "%f", lat
);
507 c
= strchr(val
, ',');
508 if (c
!= NULL
) *c
= '.';
514 case WS_LON_FAKE_EDITION
:
519 if(geoip_db_lookup_latlon6(gaddr
, &lat
, &lon
) == 0) {
520 g_snprintf(val
, VAL_STR_LEN
, "%f", lon
);
521 c
= strchr(val
, ',');
522 if (c
!= NULL
) *c
= '.';
535 #else /* HAVE_GEOIP_V6 */
538 geoip_db_lookup_ipv6(guint dbnum _U_
, struct e_in6_addr addr _U_
, const char *not_found
) {
542 #endif /* HAVE_GEOIP_V6 */
545 geoip_db_get_paths(void) {
546 GString
* path_str
= NULL
;
551 path_str
= g_string_new("");
553 path_separator
= ';';
555 path_separator
= ':';
558 for (i
= 0; i
< num_geoip_db_paths
; i
++) {
559 if (geoip_db_paths
[i
].path
) {
560 g_string_append_printf(path_str
, "%s%c", geoip_db_paths
[i
].path
, path_separator
);
564 g_string_truncate(path_str
, path_str
->len
-1);
565 path_ret
= path_str
->str
;
566 g_string_free(path_str
, FALSE
);
571 #else /* HAVE_GEOIP */
573 geoip_db_init(void) {}
576 geoip_db_num_dbs(void) {
581 geoip_db_name(guint dbnum _U_
) {
582 return "Unsupported";
586 geoip_db_type(guint dbnum _U_
) {
591 geoip_db_lookup_ipv4(guint dbnum _U_
, guint32 addr _U_
, const char *not_found
) {
596 geoip_db_lookup_ipv6(guint dbnum _U_
, guint32 addr _U_
, const char *not_found
) {
601 geoip_db_get_paths(void) {
605 #endif /* HAVE_GEOIP */
613 * indent-tabs-mode: nil
616 * ex: set shiftwidth=4 tabstop=8 expandtab:
617 * :indentSize=4:tabSize=8:noTabs=true: