mz: Check device argument by ifindex instead of name prefix
[netsniff-ng.git] / geoip.c
blob917b1a72dea3c8c2ae4fd22cb6c734f07fb83cc1
1 /*
2 * netsniff-ng - the packet sniffing beast
3 * Copyright 2013 Daniel Borkmann.
4 * Subject to the GPL, version 2.
5 */
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <GeoIP.h>
11 #include <GeoIPCity.h>
12 #include <netinet/in.h>
13 #include <netdb.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <fcntl.h>
17 #include <unistd.h>
19 #include "built_in.h"
20 #include "die.h"
21 #include "ioops.h"
22 #include "str.h"
23 #include "xmalloc.h"
24 #include "zlib.h"
25 #include "geoip.h"
27 struct file {
28 const char *desc, *local;
29 const char *remote, *possible_prefix;
32 #define PRE "/download/geoip/database"
33 static const struct file files[] = {
34 [GEOIP_CITY_EDITION_REV1] = {
35 .desc = "City IPv4",
36 .local = ETCDIRE_STRING "/city4.dat",
37 .remote = "/GeoLiteCity.dat.gz",
38 .possible_prefix = PRE,
40 [GEOIP_CITY_EDITION_REV1_V6] = {
41 .desc = "City IPv6",
42 .local = ETCDIRE_STRING "/city6.dat",
43 .remote = "/GeoLiteCityv6.dat.gz",
44 .possible_prefix = PRE "/GeoLiteCityv6-beta",
46 [GEOIP_COUNTRY_EDITION] = {
47 .desc = "Country IPv4",
48 .local = ETCDIRE_STRING "/country4.dat",
49 .remote = "/GeoIP.dat.gz",
50 .possible_prefix = PRE "/GeoLiteCountry",
52 [GEOIP_COUNTRY_EDITION_V6] = {
53 .desc = "Country IPv6",
54 .local = ETCDIRE_STRING "/country6.dat",
55 .remote = "/GeoIPv6.dat.gz",
56 .possible_prefix = PRE,
58 [GEOIP_ASNUM_EDITION] = {
59 .desc = "AS Numbers IPv4",
60 .local = ETCDIRE_STRING "/asname4.dat",
61 .remote = "/GeoIPASNum.dat.gz",
62 .possible_prefix = PRE "/asnum",
64 [GEOIP_ASNUM_EDITION_V6] = {
65 .desc = "AS Numbers IPv6",
66 .local = ETCDIRE_STRING "/asname6.dat",
67 .remote = "/GeoIPASNumv6.dat.gz",
68 .possible_prefix = PRE "/asnum",
72 static GeoIP *gi4_asname = NULL, *gi6_asname = NULL;
73 static GeoIP *gi4_country = NULL, *gi6_country = NULL;
74 static GeoIP *gi4_city = NULL, *gi6_city = NULL;
76 static GeoIPRecord empty = { NULL };
78 static char *servers[16] = { NULL };
80 #define CITYV4 (1 << 0)
81 #define CITYV6 (1 << 1)
82 #define COUNTRYV4 (1 << 2)
83 #define COUNTRYV6 (1 << 3)
84 #define ASNAMV4 (1 << 4)
85 #define ASNAMV6 (1 << 5)
87 #define HAVEALL (CITYV4 | CITYV6 | COUNTRYV4 | COUNTRYV6 | ASNAMV4 | ASNAMV6)
89 static int geoip_db_present = 0;
91 int geoip_working(void)
93 return geoip_db_present == HAVEALL;
96 static int geoip_get_remote_fd(const char *server, const char *port)
98 int ret, fd = -1;
99 struct addrinfo hints, *ahead, *ai;
101 bug_on(!server || !port);
103 memset(&hints, 0, sizeof(hints));
105 hints.ai_family = PF_UNSPEC;
106 hints.ai_socktype = SOCK_STREAM;
107 hints.ai_protocol = IPPROTO_TCP;
108 hints.ai_flags = AI_NUMERICSERV;
110 ret = getaddrinfo(server, port, &hints, &ahead);
111 if (ret != 0)
112 return -EIO;
114 for (ai = ahead; ai != NULL && fd < 0; ai = ai->ai_next) {
115 fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
116 if (fd < 0)
117 continue;
119 ret = connect(fd, ai->ai_addr, ai->ai_addrlen);
120 if (ret < 0) {
121 close(fd);
122 fd = -1;
123 continue;
126 break;
129 freeaddrinfo(ahead);
131 return fd;
134 static void geoip_inflate(int which)
136 int ret, ret2 = 1;
137 gzFile fpi;
138 FILE *fpo;
139 char zfile[128], raw[4096];
141 slprintf(zfile, sizeof(zfile), "%s.gz", files[which].local);
142 fpi = gzopen(zfile, "rb");
143 if (fpi == NULL)
144 panic("No %s file!\n", zfile);
146 fpo = fopen(files[which].local, "wb");
147 if (fpo == NULL)
148 panic("Cannot create %s!\n", files[which].local);
150 while ((ret = gzread(fpi, raw, sizeof(raw))) && ret2)
151 ret2 = fwrite(raw, ret, 1, fpo);
153 if (!gzeof(fpi))
154 panic("Error in gzread: %s\n", gzerror(fpi, &ret));
156 gzclose(fpi);
157 fclose(fpo);
160 static int geoip_get_database(const char *host, int which)
162 int found, sock, fd, i, good, retry = 0;
163 ssize_t ret, len, rtotlen = 0, totlen = 0;
164 char raw[4096], *ptr, zfile[128];
165 size_t lenl = strlen("Content-Length: ");
166 size_t lent = strlen("HTTP/1.1 200 OK");
167 size_t lenc = strlen("\r\n\r\n");
168 const char *http_req_fmt = "GET %s%s HTTP/1.1\r\n"
169 "Connection: close\r\n"
170 "Host: %s\r\n\r\n";
171 again:
172 found = good = 0;
173 ptr = NULL;
174 len = 0;
176 sock = geoip_get_remote_fd(host, "80");
177 if (sock < 0)
178 return -EIO;
180 slprintf(raw, sizeof(raw), http_req_fmt,
181 retry ? files[which].possible_prefix : "",
182 files[which].remote, host);
184 ret = write(sock, raw, strlen(raw));
185 if (ret <= 0) {
186 close(sock);
187 return -EIO;
190 slprintf(zfile, sizeof(zfile), "%s.gz", files[which].local);
191 fd = open_or_die_m(zfile, O_WRONLY | O_CREAT | O_TRUNC, DEFFILEMODE);
193 memset(raw, 0, sizeof(raw));
194 ret = read(sock, raw, sizeof(raw));
195 if (ret <= 0) {
196 close(fd);
197 close(sock);
198 return -EIO;
201 for (i = 0; i < ret; i++) {
202 if (!strncmp(raw + i, "Content-Length: ", min_t(size_t, ret - i, lenl))) {
203 ptr = raw + i + lenl;
204 rtotlen = strtoul(ptr, NULL, 10);
207 if (!strncmp(raw + i, "HTTP/1.1 200 OK", min_t(size_t, ret - i, lent)))
208 good = 1;
210 if (!strncmp(raw + i, "\r\n\r\n", min_t(size_t, ret - i, lenc))) {
211 ptr = raw + i + lenc;
212 len = ret - i - lenc;
213 found = 1;
214 break;
218 if (!found || ptr >= raw + ret || len < 0 || rtotlen == 0 || good == 0) {
219 close(fd);
220 close(sock);
222 if (retry == 0) {
223 retry = 1;
224 goto again;
227 return -ENOENT;
230 do {
231 write_or_die(fd, ptr, len);
232 totlen += len;
233 printf("\r%s [%.2f%%, %zd/%zd, %s]", files[which].desc,
234 100.f * totlen / rtotlen, totlen, rtotlen, host);
235 fflush(stdout);
237 memset(raw, 0, sizeof(raw));
238 ret = read(sock, raw, sizeof(raw));
240 ptr = raw;
241 len = ret;
242 } while (ret > 0);
244 printf("\n");
246 close(fd);
247 close(sock);
249 if (totlen != rtotlen) {
250 unlink(files[which].local);
251 return -EIO;
254 geoip_inflate(which);
255 unlink(zfile);
257 return 0;
260 static GeoIPRecord *geoip4_get_record(struct sockaddr_in *sa)
262 bug_on(gi4_city == NULL);
264 return GeoIP_record_by_ipnum(gi4_city, ntohl(sa->sin_addr.s_addr)) ? : &empty;
267 static GeoIPRecord *geoip6_get_record(struct sockaddr_in6 *sa)
269 bug_on(gi6_city == NULL);
271 return GeoIP_record_by_ipnum_v6(gi6_city, sa->sin6_addr) ? : &empty;
274 const char *geoip4_as_name(struct sockaddr_in *sa)
276 bug_on(gi4_asname == NULL);
278 return GeoIP_name_by_ipnum(gi4_asname, ntohl(sa->sin_addr.s_addr));
281 const char *geoip6_as_name(struct sockaddr_in6 *sa)
283 bug_on(gi6_asname == NULL);
285 return GeoIP_name_by_ipnum_v6(gi6_asname, sa->sin6_addr);
288 float geoip4_longitude(struct sockaddr_in *sa)
290 return geoip4_get_record(sa)->longitude;
293 float geoip4_latitude(struct sockaddr_in *sa)
295 return geoip4_get_record(sa)->latitude;
298 float geoip6_longitude(struct sockaddr_in6 *sa)
300 return geoip6_get_record(sa)->longitude;
303 float geoip6_latitude(struct sockaddr_in6 *sa)
305 return geoip6_get_record(sa)->latitude;
308 const char *geoip4_city_name(struct sockaddr_in *sa)
310 return geoip4_get_record(sa)->city;
313 const char *geoip6_city_name(struct sockaddr_in6 *sa)
315 return geoip6_get_record(sa)->city;
318 const char *geoip4_region_name(struct sockaddr_in *sa)
320 return geoip4_get_record(sa)->region;
323 const char *geoip6_region_name(struct sockaddr_in6 *sa)
325 return geoip6_get_record(sa)->region;
328 const char *geoip4_country_name(struct sockaddr_in *sa)
330 bug_on(gi4_country == NULL);
332 return GeoIP_country_name_by_ipnum(gi4_country, ntohl(sa->sin_addr.s_addr));
335 const char *geoip6_country_name(struct sockaddr_in6 *sa)
337 bug_on(gi6_country == NULL);
339 return GeoIP_country_name_by_ipnum_v6(gi6_country, sa->sin6_addr);
342 const char *geoip4_country_code3_name(struct sockaddr_in *sa)
344 bug_on(gi4_country == NULL);
346 return GeoIP_country_code3_by_ipnum(gi4_country, ntohl(sa->sin_addr.s_addr));
349 const char *geoip6_country_code3_name(struct sockaddr_in6 *sa)
351 bug_on(gi6_country == NULL);
353 return GeoIP_country_code3_by_ipnum_v6(gi6_country, sa->sin6_addr);
356 static int fdout, fderr;
358 /* GeoIP people were too stupid to come to the idea that you could set
359 * errno appropriately and return NULL instead of printing stuff from
360 * the library directly that no one can turn off.
363 static void geoip_open_prepare(void)
365 fflush(stdout);
366 fdout = dup_or_die(1);
368 fflush(stderr);
369 fderr = dup_or_die(2);
371 close(1);
372 close(2);
375 static void geoip_open_restore(void)
377 dup2_or_die(fdout, 1);
378 dup2_or_die(fderr, 2);
380 close(fdout);
381 close(fderr);
384 static GeoIP *geoip_open_type(int type, int flags)
386 GeoIP *ret;
388 geoip_open_prepare();
389 ret = GeoIP_open_type(type, flags);
390 geoip_open_restore();
392 return ret;
395 static GeoIP *geoip_open(const char *filename, int flags)
397 GeoIP *ret;
399 geoip_open_prepare();
400 ret = GeoIP_open(filename, flags);
401 geoip_open_restore();
403 return ret;
406 static void init_geoip_city_open4(int enforce)
408 gi4_city = geoip_open(files[GEOIP_CITY_EDITION_REV1].local, GEOIP_MMAP_CACHE);
409 if (gi4_city == NULL) {
410 gi4_city = geoip_open_type(GEOIP_CITY_EDITION_REV1, GEOIP_MMAP_CACHE);
411 if (gi4_city == NULL)
412 if (enforce)
413 panic("Cannot open GeoIP4 city database, try --update!\n");
416 if (gi4_city) {
417 GeoIP_set_charset(gi4_city, GEOIP_CHARSET_UTF8);
418 geoip_db_present |= CITYV4;
422 static void init_geoip_city_open6(int enforce)
424 gi6_city = geoip_open(files[GEOIP_CITY_EDITION_REV1_V6].local, GEOIP_MMAP_CACHE);
425 if (gi6_city == NULL) {
426 gi6_city = geoip_open_type(GEOIP_CITY_EDITION_REV1_V6, GEOIP_MMAP_CACHE);
427 if (gi6_city == NULL)
428 if (enforce)
429 panic("Cannot open GeoIP6 city database, try --update!\n");
432 if (gi6_city) {
433 GeoIP_set_charset(gi6_city, GEOIP_CHARSET_UTF8);
434 geoip_db_present |= CITYV6;
438 static void init_geoip_city(int enforce)
440 init_geoip_city_open4(enforce);
441 init_geoip_city_open6(enforce);
444 static void destroy_geoip_city(void)
446 GeoIP_delete(gi4_city);
447 GeoIP_delete(gi6_city);
450 static void init_geoip_country_open4(int enforce)
452 gi4_country = geoip_open(files[GEOIP_COUNTRY_EDITION].local, GEOIP_MMAP_CACHE);
453 if (gi4_country == NULL) {
454 gi4_country = geoip_open_type(GEOIP_COUNTRY_EDITION, GEOIP_MMAP_CACHE);
455 if (gi4_country == NULL)
456 if (enforce)
457 panic("Cannot open GeoIP4 country database, try --update!\n");
460 if (gi4_country) {
461 GeoIP_set_charset(gi4_country, GEOIP_CHARSET_UTF8);
462 geoip_db_present |= COUNTRYV4;
466 static void init_geoip_country_open6(int enforce)
468 gi6_country = geoip_open(files[GEOIP_COUNTRY_EDITION_V6].local, GEOIP_MMAP_CACHE);
469 if (gi6_country == NULL) {
470 gi6_country = geoip_open_type(GEOIP_COUNTRY_EDITION_V6, GEOIP_MMAP_CACHE);
471 if (gi6_country == NULL)
472 if (enforce)
473 panic("Cannot open GeoIP6 country database, try --update!\n");
476 if (gi6_country) {
477 GeoIP_set_charset(gi6_country, GEOIP_CHARSET_UTF8);
478 geoip_db_present |= COUNTRYV6;
482 static void init_geoip_country(int enforce)
484 init_geoip_country_open4(enforce);
485 init_geoip_country_open6(enforce);
488 static void destroy_geoip_country(void)
490 GeoIP_delete(gi4_country);
491 GeoIP_delete(gi6_country);
494 static void init_geoip_asname_open4(int enforce)
496 gi4_asname = geoip_open(files[GEOIP_ASNUM_EDITION].local, GEOIP_MMAP_CACHE);
497 if (gi4_asname == NULL) {
498 gi4_asname = geoip_open_type(GEOIP_ASNUM_EDITION, GEOIP_MMAP_CACHE);
499 if (gi4_asname == NULL)
500 if (enforce)
501 panic("Cannot open GeoIP4 AS database, try --update!\n");
504 if (gi4_asname) {
505 GeoIP_set_charset(gi4_asname, GEOIP_CHARSET_UTF8);
506 geoip_db_present |= ASNAMV4;
510 static void init_geoip_asname_open6(int enforce)
512 gi6_asname = geoip_open(files[GEOIP_ASNUM_EDITION_V6].local, GEOIP_MMAP_CACHE);
513 if (gi6_asname == NULL) {
514 gi6_asname = geoip_open_type(GEOIP_ASNUM_EDITION_V6, GEOIP_MMAP_CACHE);
515 if (gi6_asname == NULL)
516 if (enforce)
517 panic("Cannot open GeoIP6 AS database, try --update!\n");
520 if (gi6_asname) {
521 GeoIP_set_charset(gi6_asname, GEOIP_CHARSET_UTF8);
522 geoip_db_present |= ASNAMV6;
526 static void init_geoip_asname(int enforce)
528 init_geoip_asname_open4(enforce);
529 init_geoip_asname_open6(enforce);
532 static void destroy_geoip_asname(void)
534 GeoIP_delete(gi4_asname);
535 GeoIP_delete(gi6_asname);
538 static void init_mirrors(void)
540 size_t i = 0;
541 FILE *fp;
542 char buff[256];
544 fp = fopen(ETCDIRE_STRING "/geoip.conf", "r");
545 if (!fp)
546 panic("Cannot open geoip.conf!\n");
548 fmemset(buff, 0, sizeof(buff));
549 while (fgets(buff, sizeof(buff), fp) != NULL &&
550 i < array_size(servers)) {
551 buff[sizeof(buff) - 1] = 0;
552 buff[strlen(buff) - 1] = 0;
554 if (buff[0] == '#') {
555 fmemset(buff, 0, sizeof(buff));
556 continue;
559 servers[i++] = xstrdup(buff);
560 fmemset(buff, 0, sizeof(buff));
563 fclose(fp);
566 static void destroy_mirrors(void)
568 size_t i;
570 for (i = 0; i < array_size(servers); ++i)
571 free(servers[i]);
574 void init_geoip(int enforce)
576 init_geoip_city(enforce);
577 init_geoip_country(enforce);
578 init_geoip_asname(enforce);
581 void update_geoip(void)
583 size_t i, j;
584 int ret, good = 0;
586 init_mirrors();
588 for (i = 0; i < array_size(files); ++i) {
589 if (files[i].local && files[i].remote) {
590 good = 0;
592 for (j = 0; j < array_size(servers); ++j) {
593 if (servers[j] == NULL)
594 continue;
595 ret = geoip_get_database(servers[j], i);
596 if (!ret) {
597 good = 1;
598 break;
602 if (good == 0)
603 panic("Cannot get %s from mirrors!\n",
604 files[i].remote);
608 destroy_mirrors();
611 void destroy_geoip(void)
613 destroy_geoip_city();
614 destroy_geoip_country();
615 destroy_geoip_asname();
617 geoip_db_present = 0;