ring: use xzmalloc_aligned
[netsniff-ng.git] / geoip.c
blobd95305c8812f021e6bb887a21fc4f41cb108e3cf
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 char *servers[16] = { NULL };
78 #define CITYV4 (1 << 0)
79 #define CITYV6 (1 << 1)
80 #define COUNTRYV4 (1 << 2)
81 #define COUNTRYV6 (1 << 3)
82 #define ASNAMV4 (1 << 4)
83 #define ASNAMV6 (1 << 5)
85 #define HAVEALL (CITYV4 | CITYV6 | COUNTRYV4 | COUNTRYV6 | ASNAMV4 | ASNAMV6)
87 static int geoip_db_present = 0;
89 int geoip_working(void)
91 return geoip_db_present == HAVEALL;
94 static int geoip_get_remote_fd(const char *server, const char *port)
96 int ret, fd = -1;
97 struct addrinfo hints, *ahead, *ai;
99 bug_on(!server || !port);
101 memset(&hints, 0, sizeof(hints));
103 hints.ai_family = PF_UNSPEC;
104 hints.ai_socktype = SOCK_STREAM;
105 hints.ai_protocol = IPPROTO_TCP;
106 hints.ai_flags = AI_NUMERICSERV;
108 ret = getaddrinfo(server, port, &hints, &ahead);
109 if (ret != 0)
110 return -EIO;
112 for (ai = ahead; ai != NULL && fd < 0; ai = ai->ai_next) {
113 fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
114 if (fd < 0)
115 continue;
117 ret = connect(fd, ai->ai_addr, ai->ai_addrlen);
118 if (ret < 0) {
119 close(fd);
120 fd = -1;
121 continue;
124 break;
127 freeaddrinfo(ahead);
129 return fd;
132 static void geoip_inflate(int which)
134 int ret, ret2 = 1;
135 gzFile fpi;
136 FILE *fpo;
137 char zfile[128], raw[4096];
139 slprintf(zfile, sizeof(zfile), "%s.gz", files[which].local);
140 fpi = gzopen(zfile, "rb");
141 if (fpi == NULL)
142 panic("No %s file!\n", zfile);
144 fpo = fopen(files[which].local, "wb");
145 if (fpo == NULL)
146 panic("Cannot create %s!\n", files[which].local);
148 while ((ret = gzread(fpi, raw, sizeof(raw))) && ret2)
149 ret2 = fwrite(raw, ret, 1, fpo);
151 if (!gzeof(fpi))
152 panic("Error in gzread: %s\n", gzerror(fpi, &ret));
154 gzclose(fpi);
155 fclose(fpo);
158 static int geoip_get_database(const char *host, int which)
160 int found, sock, fd, i, good, retry = 0;
161 ssize_t ret, len, rtotlen = 0, totlen = 0;
162 char raw[4096], *ptr, zfile[128];
163 size_t lenl = strlen("Content-Length: ");
164 size_t lent = strlen("HTTP/1.1 200 OK");
165 size_t lenc = strlen("\r\n\r\n");
166 const char *http_req_fmt = "GET %s%s HTTP/1.1\r\n"
167 "Connection: close\r\n"
168 "Host: %s\r\n\r\n";
169 again:
170 found = good = 0;
171 ptr = NULL;
172 len = 0;
174 sock = geoip_get_remote_fd(host, "80");
175 if (sock < 0)
176 return -EIO;
178 slprintf(raw, sizeof(raw), http_req_fmt,
179 retry ? files[which].possible_prefix : "",
180 files[which].remote, host);
182 ret = write(sock, raw, strlen(raw));
183 if (ret <= 0) {
184 close(sock);
185 return -EIO;
188 slprintf(zfile, sizeof(zfile), "%s.gz", files[which].local);
189 fd = open_or_die_m(zfile, O_WRONLY | O_CREAT | O_TRUNC, DEFFILEMODE);
191 memset(raw, 0, sizeof(raw));
192 ret = read(sock, raw, sizeof(raw));
193 if (ret <= 0) {
194 close(fd);
195 close(sock);
196 return -EIO;
199 for (i = 0; i < ret; i++) {
200 if (!strncmp(raw + i, "Content-Length: ", min_t(size_t, ret - i, lenl))) {
201 ptr = raw + i + lenl;
202 rtotlen = strtoul(ptr, NULL, 10);
205 if (!strncmp(raw + i, "HTTP/1.1 200 OK", min_t(size_t, ret - i, lent)))
206 good = 1;
208 if (!strncmp(raw + i, "\r\n\r\n", min_t(size_t, ret - i, lenc))) {
209 ptr = raw + i + lenc;
210 len = ret - i - lenc;
211 found = 1;
212 break;
216 if (!found || ptr >= raw + ret || len < 0 || rtotlen == 0 || good == 0) {
217 close(fd);
218 close(sock);
220 if (retry == 0) {
221 retry = 1;
222 goto again;
225 return -ENOENT;
228 do {
229 write_or_die(fd, ptr, len);
230 totlen += len;
231 printf("\r%s [%.2f%%, %zd/%zd, %s]", files[which].desc,
232 100.f * totlen / rtotlen, totlen, rtotlen, host);
233 fflush(stdout);
235 memset(raw, 0, sizeof(raw));
236 ret = read(sock, raw, sizeof(raw));
238 ptr = raw;
239 len = ret;
240 } while (ret > 0);
242 printf("\n");
244 close(fd);
245 close(sock);
247 if (totlen != rtotlen) {
248 unlink(files[which].local);
249 return -EIO;
252 geoip_inflate(which);
253 unlink(zfile);
255 return 0;
258 static GeoIPRecord *geoip4_get_record(struct sockaddr_in *sa)
260 bug_on(gi4_city == NULL);
262 return GeoIP_record_by_ipnum(gi4_city, ntohl(sa->sin_addr.s_addr));
265 static GeoIPRecord *geoip6_get_record(struct sockaddr_in6 *sa)
267 bug_on(gi6_city == NULL);
269 return GeoIP_record_by_ipnum_v6(gi6_city, sa->sin6_addr);
272 const char *geoip4_as_name(struct sockaddr_in *sa)
274 bug_on(gi4_asname == NULL);
276 return GeoIP_name_by_ipnum(gi4_asname, ntohl(sa->sin_addr.s_addr));
279 const char *geoip6_as_name(struct sockaddr_in6 *sa)
281 bug_on(gi6_asname == NULL);
283 return GeoIP_name_by_ipnum_v6(gi6_asname, sa->sin6_addr);
286 float geoip4_longitude(struct sockaddr_in *sa)
288 GeoIPRecord *record;
289 float longitude;
291 record = geoip4_get_record(sa);
292 if (!record)
293 return 0;
295 longitude = record->longitude;
297 GeoIPRecord_delete(record);
298 return longitude;
301 float geoip4_latitude(struct sockaddr_in *sa)
303 GeoIPRecord *record;
304 float latitude;
306 record = geoip4_get_record(sa);
307 if (!record)
308 return 0;
310 latitude = record->latitude;
312 GeoIPRecord_delete(record);
313 return latitude;
316 float geoip6_longitude(struct sockaddr_in6 *sa)
318 GeoIPRecord *record;
319 float longitude;
321 record = geoip6_get_record(sa);
322 if (!record)
323 return 0;
325 longitude = record->longitude;
327 GeoIPRecord_delete(record);
328 return longitude;
331 float geoip6_latitude(struct sockaddr_in6 *sa)
333 GeoIPRecord *record;
334 float latitude;
336 record = geoip6_get_record(sa);
337 if (!record)
338 return 0;
340 latitude = record->latitude;
342 GeoIPRecord_delete(record);
343 return latitude;
346 char *geoip4_city_name(struct sockaddr_in *sa)
348 GeoIPRecord *record;
349 char *city = NULL;
351 record = geoip4_get_record(sa);
352 if (!record)
353 return NULL;
355 if (record->city)
356 city = xstrdup(record->city);
358 GeoIPRecord_delete(record);
359 return city;
362 char *geoip6_city_name(struct sockaddr_in6 *sa)
364 GeoIPRecord *record;
365 char *city = NULL;
367 record = geoip6_get_record(sa);
368 if (!record)
369 return NULL;
371 if (record->city)
372 city = xstrdup(record->city);
374 GeoIPRecord_delete(record);
375 return city;
378 char *geoip4_region_name(struct sockaddr_in *sa)
380 GeoIPRecord *record;
381 char *region = NULL;
383 record = geoip4_get_record(sa);
384 if (!record)
385 return NULL;
387 if (record->region)
388 region = xstrdup(record->region);
390 GeoIPRecord_delete(record);
391 return region;
394 char *geoip6_region_name(struct sockaddr_in6 *sa)
396 GeoIPRecord *record;
397 char *region = NULL;
399 record = geoip6_get_record(sa);
400 if (!record)
401 return NULL;
403 if (record->region)
404 region = xstrdup(record->region);
406 GeoIPRecord_delete(record);
407 return region;
410 const char *geoip4_country_name(struct sockaddr_in *sa)
412 bug_on(gi4_country == NULL);
414 return GeoIP_country_name_by_ipnum(gi4_country, ntohl(sa->sin_addr.s_addr));
417 const char *geoip6_country_name(struct sockaddr_in6 *sa)
419 bug_on(gi6_country == NULL);
421 return GeoIP_country_name_by_ipnum_v6(gi6_country, sa->sin6_addr);
424 const char *geoip4_country_code3_name(struct sockaddr_in *sa)
426 bug_on(gi4_country == NULL);
428 return GeoIP_country_code3_by_ipnum(gi4_country, ntohl(sa->sin_addr.s_addr));
431 const char *geoip6_country_code3_name(struct sockaddr_in6 *sa)
433 bug_on(gi6_country == NULL);
435 return GeoIP_country_code3_by_ipnum_v6(gi6_country, sa->sin6_addr);
438 static int fdout, fderr;
440 /* GeoIP people were too stupid to come to the idea that you could set
441 * errno appropriately and return NULL instead of printing stuff from
442 * the library directly that no one can turn off.
445 static void geoip_open_prepare(void)
447 fflush(stdout);
448 fdout = dup_or_die(1);
450 fflush(stderr);
451 fderr = dup_or_die(2);
453 close(1);
454 close(2);
457 static void geoip_open_restore(void)
459 dup2_or_die(fdout, 1);
460 dup2_or_die(fderr, 2);
462 close(fdout);
463 close(fderr);
466 static GeoIP *geoip_open_type(int type, int flags)
468 GeoIP *ret;
470 geoip_open_prepare();
471 ret = GeoIP_open_type(type, flags);
472 geoip_open_restore();
474 return ret;
477 static GeoIP *geoip_open(const char *filename, int flags)
479 GeoIP *ret;
481 geoip_open_prepare();
482 ret = GeoIP_open(filename, flags);
483 geoip_open_restore();
485 return ret;
488 static void init_geoip_city_open4(int enforce)
490 gi4_city = geoip_open(files[GEOIP_CITY_EDITION_REV1].local, GEOIP_MMAP_CACHE);
491 if (gi4_city == NULL) {
492 gi4_city = geoip_open_type(GEOIP_CITY_EDITION_REV1, GEOIP_MMAP_CACHE);
493 if (gi4_city == NULL)
494 if (enforce)
495 panic("Cannot open GeoIP4 city database, try --update!\n");
498 if (gi4_city) {
499 GeoIP_set_charset(gi4_city, GEOIP_CHARSET_UTF8);
500 geoip_db_present |= CITYV4;
504 static void init_geoip_city_open6(int enforce)
506 gi6_city = geoip_open(files[GEOIP_CITY_EDITION_REV1_V6].local, GEOIP_MMAP_CACHE);
507 if (gi6_city == NULL) {
508 gi6_city = geoip_open_type(GEOIP_CITY_EDITION_REV1_V6, GEOIP_MMAP_CACHE);
509 if (gi6_city == NULL)
510 if (enforce)
511 panic("Cannot open GeoIP6 city database, try --update!\n");
514 if (gi6_city) {
515 GeoIP_set_charset(gi6_city, GEOIP_CHARSET_UTF8);
516 geoip_db_present |= CITYV6;
520 static void init_geoip_city(int enforce)
522 init_geoip_city_open4(enforce);
523 init_geoip_city_open6(enforce);
526 static void destroy_geoip_city(void)
528 GeoIP_delete(gi4_city);
529 GeoIP_delete(gi6_city);
532 static void init_geoip_country_open4(int enforce)
534 gi4_country = geoip_open(files[GEOIP_COUNTRY_EDITION].local, GEOIP_MMAP_CACHE);
535 if (gi4_country == NULL) {
536 gi4_country = geoip_open_type(GEOIP_COUNTRY_EDITION, GEOIP_MMAP_CACHE);
537 if (gi4_country == NULL)
538 if (enforce)
539 panic("Cannot open GeoIP4 country database, try --update!\n");
542 if (gi4_country) {
543 GeoIP_set_charset(gi4_country, GEOIP_CHARSET_UTF8);
544 geoip_db_present |= COUNTRYV4;
548 static void init_geoip_country_open6(int enforce)
550 gi6_country = geoip_open(files[GEOIP_COUNTRY_EDITION_V6].local, GEOIP_MMAP_CACHE);
551 if (gi6_country == NULL) {
552 gi6_country = geoip_open_type(GEOIP_COUNTRY_EDITION_V6, GEOIP_MMAP_CACHE);
553 if (gi6_country == NULL)
554 if (enforce)
555 panic("Cannot open GeoIP6 country database, try --update!\n");
558 if (gi6_country) {
559 GeoIP_set_charset(gi6_country, GEOIP_CHARSET_UTF8);
560 geoip_db_present |= COUNTRYV6;
564 static void init_geoip_country(int enforce)
566 init_geoip_country_open4(enforce);
567 init_geoip_country_open6(enforce);
570 static void destroy_geoip_country(void)
572 GeoIP_delete(gi4_country);
573 GeoIP_delete(gi6_country);
576 static void init_geoip_asname_open4(int enforce)
578 gi4_asname = geoip_open(files[GEOIP_ASNUM_EDITION].local, GEOIP_MMAP_CACHE);
579 if (gi4_asname == NULL) {
580 gi4_asname = geoip_open_type(GEOIP_ASNUM_EDITION, GEOIP_MMAP_CACHE);
581 if (gi4_asname == NULL)
582 if (enforce)
583 panic("Cannot open GeoIP4 AS database, try --update!\n");
586 if (gi4_asname) {
587 GeoIP_set_charset(gi4_asname, GEOIP_CHARSET_UTF8);
588 geoip_db_present |= ASNAMV4;
592 static void init_geoip_asname_open6(int enforce)
594 gi6_asname = geoip_open(files[GEOIP_ASNUM_EDITION_V6].local, GEOIP_MMAP_CACHE);
595 if (gi6_asname == NULL) {
596 gi6_asname = geoip_open_type(GEOIP_ASNUM_EDITION_V6, GEOIP_MMAP_CACHE);
597 if (gi6_asname == NULL)
598 if (enforce)
599 panic("Cannot open GeoIP6 AS database, try --update!\n");
602 if (gi6_asname) {
603 GeoIP_set_charset(gi6_asname, GEOIP_CHARSET_UTF8);
604 geoip_db_present |= ASNAMV6;
608 static void init_geoip_asname(int enforce)
610 init_geoip_asname_open4(enforce);
611 init_geoip_asname_open6(enforce);
614 static void destroy_geoip_asname(void)
616 GeoIP_delete(gi4_asname);
617 GeoIP_delete(gi6_asname);
620 static void init_mirrors(void)
622 size_t i = 0;
623 FILE *fp;
624 char buff[256];
626 fp = fopen(ETCDIRE_STRING "/geoip.conf", "r");
627 if (!fp)
628 panic("Cannot open geoip.conf!\n");
630 fmemset(buff, 0, sizeof(buff));
631 while (fgets(buff, sizeof(buff), fp) != NULL &&
632 i < array_size(servers)) {
633 buff[sizeof(buff) - 1] = 0;
634 buff[strlen(buff) - 1] = 0;
636 if (buff[0] == '#') {
637 fmemset(buff, 0, sizeof(buff));
638 continue;
641 servers[i++] = xstrdup(buff);
642 fmemset(buff, 0, sizeof(buff));
645 fclose(fp);
648 static void destroy_mirrors(void)
650 size_t i;
652 for (i = 0; i < array_size(servers); ++i)
653 free(servers[i]);
656 void init_geoip(int enforce)
658 init_geoip_city(enforce);
659 init_geoip_country(enforce);
660 init_geoip_asname(enforce);
663 void update_geoip(void)
665 size_t i, j;
666 int ret, good = 0;
668 init_mirrors();
670 for (i = 0; i < array_size(files); ++i) {
671 if (files[i].local && files[i].remote) {
672 good = 0;
674 for (j = 0; j < array_size(servers); ++j) {
675 if (servers[j] == NULL)
676 continue;
677 ret = geoip_get_database(servers[j], i);
678 if (!ret) {
679 good = 1;
680 break;
684 if (good == 0)
685 panic("Cannot get %s from mirrors!\n",
686 files[i].remote);
690 destroy_mirrors();
693 void destroy_geoip(void)
695 destroy_geoip_city();
696 destroy_geoip_country();
697 destroy_geoip_asname();
699 geoip_db_present = 0;