b7339e324b7474cb976a03cebc7ba6fff7298e54
[obnox/wireshark/wip.git] / epan / geoip_db.c
1 /* geoip_db.c
2  * GeoIP database support
3  *
4  * Copyright 2008, Gerald Combs <gerald@wireshark.org>
5  *
6  * $Id$
7  *
8  * Wireshark - Network traffic analyzer
9  * By Gerald Combs <gerald@wireshark.org>
10  * Copyright 1998 Gerald Combs
11  *
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.
16  *
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.
21  *
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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25  */
26
27 /* To do:
28  * We currently return a single string for each database. Some databases,
29  * e.g. GeoIPCity, can return other info such as area codes.
30  */
31
32 #ifdef HAVE_CONFIG_H
33 # include "config.h"
34 #endif
35
36 #include <glib.h>
37
38 #ifdef HAVE_GEOIP
39 #include "GeoIP.h"
40 #include "GeoIPCity.h"
41
42 #include "geoip_db.h"
43 #include "uat.h"
44 #include "prefs.h"
45 #include "report_err.h"
46 #include "value_string.h"
47 #include <wsutil/file_util.h>
48
49 /* This needs to match NUM_GEOIP_COLS in hostlist_table.h */
50 #define MAX_GEOIP_DBS 13
51
52 /* Column names for each database type */
53 value_string geoip_type_name_vals[] = {
54         { GEOIP_COUNTRY_EDITION,                "Country" },
55         { GEOIP_REGION_EDITION_REV0,    "Region" },
56         { GEOIP_CITY_EDITION_REV0,              "City"},
57         { GEOIP_ORG_EDITION,                    "Organization" },
58         { GEOIP_ISP_EDITION,                    "ISP" },
59         { GEOIP_CITY_EDITION_REV1,              "City" },
60         { GEOIP_REGION_EDITION_REV1,    "Region" },
61         { GEOIP_PROXY_EDITION,                  "Proxy" },
62         { GEOIP_ASNUM_EDITION,                  "AS Number" },
63         { GEOIP_NETSPEED_EDITION,               "Speed" },
64         { GEOIP_DOMAIN_EDITION,                 "Domain" },
65         { WS_LAT_FAKE_EDITION,                  "Latitude" },   /* fake database */
66         { WS_LON_FAKE_EDITION,                  "Longitude" },  /* fake database */
67         { 0, NULL }
68 };
69
70 static GArray *geoip_dat_arr = NULL;
71
72 /* UAT definitions. Copied from oids.c */
73 typedef struct _geoip_db_path_t {
74         char* path;
75 } geoip_db_path_t;
76
77 static geoip_db_path_t *geoip_db_paths = NULL;
78 static guint num_geoip_db_paths = 0;
79 static uat_t *geoip_db_paths_uat = NULL;
80 UAT_CSTRING_CB_DEF(geoip_mod, path, geoip_db_path_t)
81
82
83 /**
84  * Scan a directory for GeoIP databases and load them
85  */
86 static void
87 geoip_dat_scan_dir(const char *dirname) {
88         WS_DIR *dir;
89         WS_DIRENT *file;
90         const char *name;
91         char *datname;
92         GeoIP *gi;
93
94         if ((dir = ws_dir_open(dirname, 0, NULL)) != NULL) {
95                 while ((file = ws_dir_read_name(dir)) != NULL) {
96                         name = ws_dir_get_name(file);
97                         if (g_str_has_prefix(file, "Geo") && g_str_has_suffix(file, ".dat")) {
98                                 datname = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", dirname, name);
99                                 gi = GeoIP_open(datname, GEOIP_MEMORY_CACHE);
100                                 if (gi) {
101                                         g_array_append_val(geoip_dat_arr, gi);
102                                 }
103                                 g_free(datname);
104                         }
105                 }
106         }
107 }
108
109 /* UAT callbacks */
110 static void* geoip_db_path_copy_cb(void* dest, const void* orig, unsigned len _U_) {
111         const geoip_db_path_t *m = orig;
112         geoip_db_path_t *d = dest;
113
114         d->path = g_strdup(m->path);
115
116         return d;
117 }
118
119 static void geoip_db_path_free_cb(void* p) {
120         geoip_db_path_t *m = p;
121         g_free(m->path);
122 }
123
124 /**
125  * Initialize GeoIP lookups
126  */
127 void
128 geoip_db_init(void) {
129         guint i;
130         static uat_field_t geoip_db_paths_fields[] = {
131                 UAT_FLD_PATHNAME(geoip_mod, path, "Database pathname", "The database path"),
132                 UAT_END_FIELDS
133         };
134         char* geoip_load_error = NULL;
135
136         geoip_dat_arr = g_array_new(FALSE, FALSE, sizeof(GeoIP *));
137
138         geoip_db_paths_uat = uat_new("GeoIP Database Paths",
139                         sizeof(geoip_db_path_t),
140                         "geoip_db_paths",
141                         FALSE,
142                         (void*)&geoip_db_paths,
143                         &num_geoip_db_paths,
144                         UAT_CAT_GENERAL,
145                         "ChGeoIPDbPaths",
146                         geoip_db_path_copy_cb,
147                         NULL,
148                         geoip_db_path_free_cb,
149                         geoip_db_paths_fields);
150
151         uat_load(geoip_db_paths_uat, &geoip_load_error);
152
153         if (geoip_load_error) {
154                 report_failure("Error loading GeoIP database path table: %s", geoip_load_error);
155                 return;
156         }
157
158         for (i = 0; i < num_geoip_db_paths; i++) {
159                 if (geoip_db_paths[i].path) {
160                         geoip_dat_scan_dir(geoip_db_paths[i].path);
161                 }
162         }
163
164     /* add fake databases for latitude and longitude (using "City" in reality) */
165     {
166         GeoIP *gi_lat;
167         GeoIP *gi_lon;
168
169         gi_lat = g_malloc(sizeof (GeoIP));
170         gi_lat->databaseType = WS_LAT_FAKE_EDITION;
171                 g_array_append_val(geoip_dat_arr, gi_lat);
172         gi_lon = g_malloc(sizeof (GeoIP));
173         gi_lon->databaseType = WS_LON_FAKE_EDITION;
174                 g_array_append_val(geoip_dat_arr, gi_lon);
175     }
176 }
177
178 guint
179 geoip_db_num_dbs(void) {
180         return geoip_dat_arr->len;
181 }
182
183 const gchar *
184 geoip_db_name(guint dbnum) {
185         GeoIP *gi;
186
187         gi = g_array_index(geoip_dat_arr, GeoIP *, dbnum);
188         if (gi) {
189                 return (val_to_str(gi->databaseType, geoip_type_name_vals, "Unknown database"));
190         }
191         return "Invalid database";
192 }
193
194 int
195 geoip_db_type(guint dbnum) {
196         GeoIP *gi;
197
198         gi = g_array_index(geoip_dat_arr, GeoIP *, dbnum);
199         if (gi) {
200                 return (gi->databaseType);
201         }
202         return -1;
203 }
204
205 int
206 geoip_db_lookup_latlon(guint32 addr, float *lat, float *lon) {
207         GeoIP *gi;
208         GeoIPRecord *gir;
209     guint i;
210
211         for (i = 0; i < geoip_db_num_dbs(); i++) {
212         gi = g_array_index(geoip_dat_arr, GeoIP *, i);
213         if (gi) {
214             switch (gi->databaseType) {
215                 case GEOIP_CITY_EDITION_REV0:
216                 case GEOIP_CITY_EDITION_REV1:
217                     gir = GeoIP_record_by_ipnum(gi, addr);
218                     if(gir) {
219                         *lat = gir->latitude;
220                         *lon = gir->longitude;
221                                                 return 0;
222                     }
223                     return -1;
224                     /*break;*/
225
226                 default:
227                     break;
228             }
229         }
230     }
231         return -1;
232 }
233
234 #define VAL_STR_LEN 100
235 const char *
236 geoip_db_lookup_ipv4(guint dbnum, guint32 addr, char *not_found) {
237         GeoIP *gi;
238         GeoIPRecord *gir;
239         const char *ret = not_found;
240         static char val[VAL_STR_LEN];
241
242         gi = g_array_index(geoip_dat_arr, GeoIP *, dbnum);
243         if (gi) {
244                 switch (gi->databaseType) {
245                         case GEOIP_COUNTRY_EDITION:
246                                 ret = GeoIP_country_name_by_ipnum(gi, addr);
247                                 break;
248
249                         case GEOIP_CITY_EDITION_REV0:
250                         case GEOIP_CITY_EDITION_REV1:
251                                 gir = GeoIP_record_by_ipnum(gi, addr);
252                                 if (gir && gir->city && gir->region) {
253                                         g_snprintf(val, VAL_STR_LEN, "%s, %s", gir->city, gir->region);
254                                         ret = val;
255                                 } else if (gir && gir->city) {
256                                         g_snprintf(val, VAL_STR_LEN, "%s", gir->city);
257                                         ret = val;
258                                 }
259                                 break;
260
261                         case GEOIP_ORG_EDITION:
262                         case GEOIP_ISP_EDITION:
263                         case GEOIP_ASNUM_EDITION:
264                                 ret = GeoIP_name_by_ipnum(gi, addr);
265                                 break;
266
267             case WS_LAT_FAKE_EDITION:
268             {
269                 float lat;
270                 float lon;
271                                 char *c;
272                 if(geoip_db_lookup_latlon(addr, &lat, &lon) == 0) {
273                     g_snprintf(val, VAL_STR_LEN, "%f", lat);
274                                         c = strchr(val, ',');
275                                         if (c != NULL) *c = '.';
276                     ret = val;
277                 }
278             }
279                 break;
280
281             case WS_LON_FAKE_EDITION:
282             {
283                 float lat;
284                 float lon;
285                                 char *c;
286                 if(geoip_db_lookup_latlon(addr, &lat, &lon) == 0) {
287                     g_snprintf(val, VAL_STR_LEN, "%f", lon);
288                                         c = strchr(val, ',');
289                                         if (c != NULL) *c = '.';
290                     ret = val;
291                 }
292             }
293                 break;
294
295                         default:
296                                 break;
297                 }
298         }
299         if (ret) {
300                 return ret;
301         }
302         return not_found;
303 }
304
305 gchar *
306 geoip_db_get_paths(void) {
307         GString* path_str = NULL;
308         gchar *path_ret;
309         char path_separator;
310         guint i;
311
312         path_str = g_string_new("");
313 #ifdef _WIN32
314         path_separator = ';';
315 #else
316         path_separator = ':';
317 #endif
318
319         for (i = 0; i < num_geoip_db_paths; i++) {
320                 if (geoip_db_paths[i].path) {
321                         g_string_append_printf(path_str, "%s%c", geoip_db_paths[i].path, path_separator);
322                 }
323         }
324
325         g_string_truncate(path_str, path_str->len-1);
326         path_ret = path_str->str;
327         g_string_free(path_str, FALSE);
328
329         return path_ret;
330 }
331
332 #else /* HAVE_GEOIP */
333 void
334 geoip_db_init(void) {}
335
336 guint
337 geoip_db_num_dbs(void) {
338         return 0;
339 }
340
341 const gchar *
342 geoip_db_name(guint dbnum _U_) {
343         return "Unsupported";
344 }
345
346 int
347 geoip_db_type(guint dbnum _U_) {
348         return -1;
349 }
350
351 const char *
352 geoip_db_lookup_ipv4(guint dbnum _U_, guint32 addr _U_, char *not_found) {
353         return not_found;
354 }
355
356 gchar *
357 geoip_db_get_paths(void) {
358         return "";
359 }
360
361 #endif /* HAVE_GEOIP */
362
363 /*
364  * Editor modelines
365  *
366  * Local Variables:
367  * c-basic-offset: 4
368  * tab-width: 4
369  * indent-tabs-mode: t
370  * End:
371  *
372  * ex: set shiftwidth=4 tabstop=4 noexpandtab
373  * :indentSize=4:tabSize=4:noTabs=false:
374  */