dev: make paths unlikely, simplify code
[netsniff-ng.git] / geoip.c
blobd027e14abd7d1987ca29e637ca413b4e74ef1578
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 = PREFIX_STRING "/etc/netsniff-ng/city4.dat",
37 .remote = "/GeoLiteCity.dat.gz",
38 .possible_prefix = PRE,
40 [GEOIP_CITY_EDITION_REV1_V6] = {
41 .desc = "City IPv6",
42 .local = PREFIX_STRING "/etc/netsniff-ng/city6.dat",
43 .remote = "/GeoLiteCityv6.dat.gz",
44 .possible_prefix = PRE "/GeoLiteCityv6-beta",
46 [GEOIP_COUNTRY_EDITION] = {
47 .desc = "Country IPv4",
48 .local = PREFIX_STRING "/etc/netsniff-ng/country4.dat",
49 .remote = "/GeoIP.dat.gz",
50 .possible_prefix = PRE "/GeoLiteCountry",
52 [GEOIP_COUNTRY_EDITION_V6] = {
53 .desc = "Country IPv6",
54 .local = PREFIX_STRING "/etc/netsniff-ng/country6.dat",
55 .remote = "/GeoIPv6.dat.gz",
56 .possible_prefix = PRE,
58 [GEOIP_ASNUM_EDITION] = {
59 .desc = "AS Numbers IPv4",
60 .local = PREFIX_STRING "/etc/netsniff-ng/asname4.dat",
61 .remote = "/GeoIPASNum.dat.gz",
62 .possible_prefix = PRE "/asnum",
64 [GEOIP_ASNUM_EDITION_V6] = {
65 .desc = "AS Numbers IPv6",
66 .local = PREFIX_STRING "/etc/netsniff-ng/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 = { 0 };
78 static char *servers[16] = { 0 };
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 gzclose(fpi);
154 fclose(fpo);
157 static int geoip_get_database(const char *host, int which)
159 int found, sock, fd, i, good, retry = 0;
160 ssize_t ret, len, rtotlen = 0, totlen = 0;
161 char raw[4096], *ptr, zfile[128];
162 size_t lenl = strlen("Content-Length: ");
163 size_t lent = strlen("HTTP/1.1 200 OK");
164 size_t lenc = strlen("\r\n\r\n");
166 again:
167 found = good = 0;
168 ptr = NULL;
169 len = 0;
171 sock = geoip_get_remote_fd(host, "80");
172 if (sock < 0)
173 return -EIO;
175 slprintf(raw, sizeof(raw), "GET %s%s HTTP/1.1\nHost: %s\r\n\r\n",
176 retry ? files[which].possible_prefix : "",
177 files[which].remote, host);
179 ret = write(sock, raw, strlen(raw));
180 if (ret <= 0) {
181 close(sock);
182 return -EIO;
185 shutdown(sock, SHUT_WR);
187 slprintf(zfile, sizeof(zfile), "%s.gz", files[which].local);
188 fd = open_or_die_m(zfile, O_WRONLY | O_CREAT | O_TRUNC, DEFFILEMODE);
190 memset(raw, 0, sizeof(raw));
191 ret = read(sock, raw, sizeof(raw));
192 if (ret <= 0) {
193 close(fd);
194 close(sock);
195 return -EIO;
198 raw[sizeof(raw) - 1] = 0;
200 for (i = 0; i < ret; i++) {
201 if (!strncmp(raw + i, "Content-Length: ", min(ret - i, lenl))) {
202 ptr = raw + i + lenl;
203 rtotlen = strtoul(ptr, NULL, 10);
206 if (!strncmp(raw + i, "HTTP/1.1 200 OK", min(ret - i, lent)))
207 good = 1;
209 if (!strncmp(raw + i, "\r\n\r\n", min(ret - i, lenc))) {
210 ptr = raw + i + lenc;
211 len = ret - i - lenc;
212 found = 1;
213 break;
217 if (!found || ptr >= raw + ret || len < 0 || rtotlen == 0 || good == 0) {
218 close(fd);
219 close(sock);
221 if (retry == 0) {
222 retry = 1;
223 goto again;
226 return -ENOENT;
229 do {
230 write_or_die(fd, ptr, len);
231 totlen += len;
232 printf("\r%s [%.2f%%, %zd/%zd, %s]", files[which].desc,
233 100.f * totlen / rtotlen, totlen, rtotlen, host);
234 fflush(stdout);
236 memset(raw, 0, sizeof(raw));
237 ret = read(sock, raw, sizeof(raw));
239 ptr = raw;
240 len = ret;
241 } while(ret > 0);
243 printf("\n");
245 close(fd);
246 close(sock);
248 if (totlen != rtotlen) {
249 unlink(files[which].local);
250 return -EIO;
253 geoip_inflate(which);
254 unlink(zfile);
256 return 0;
259 static GeoIPRecord *geoip4_get_record(struct sockaddr_in sa)
261 bug_on(gi4_city == NULL);
263 return GeoIP_record_by_ipnum(gi4_city, ntohl(sa.sin_addr.s_addr)) ? : &empty;
266 static GeoIPRecord *geoip6_get_record(struct sockaddr_in6 sa)
268 bug_on(gi6_city == NULL);
270 return GeoIP_record_by_ipnum_v6(gi6_city, sa.sin6_addr) ? : &empty;
273 const char *geoip4_as_name(struct sockaddr_in sa)
275 bug_on(gi4_asname == NULL);
277 return GeoIP_name_by_ipnum(gi4_asname, ntohl(sa.sin_addr.s_addr));
280 const char *geoip6_as_name(struct sockaddr_in6 sa)
282 bug_on(gi6_asname == NULL);
284 return GeoIP_name_by_ipnum_v6(gi6_asname, sa.sin6_addr);
287 float geoip4_longitude(struct sockaddr_in sa)
289 return geoip4_get_record(sa)->longitude;
292 float geoip4_latitude(struct sockaddr_in sa)
294 return geoip4_get_record(sa)->latitude;
297 float geoip6_longitude(struct sockaddr_in6 sa)
299 return geoip6_get_record(sa)->longitude;
302 float geoip6_latitude(struct sockaddr_in6 sa)
304 return geoip6_get_record(sa)->latitude;
307 const char *geoip4_city_name(struct sockaddr_in sa)
309 return geoip4_get_record(sa)->city;
312 const char *geoip6_city_name(struct sockaddr_in6 sa)
314 return geoip6_get_record(sa)->city;
317 const char *geoip4_region_name(struct sockaddr_in sa)
319 return geoip4_get_record(sa)->region;
322 const char *geoip6_region_name(struct sockaddr_in6 sa)
324 return geoip6_get_record(sa)->region;
327 const char *geoip4_country_name(struct sockaddr_in sa)
329 bug_on(gi4_country == NULL);
331 return GeoIP_country_name_by_ipnum(gi4_country, ntohl(sa.sin_addr.s_addr));
334 const char *geoip6_country_name(struct sockaddr_in6 sa)
336 bug_on(gi6_country == NULL);
338 return GeoIP_country_name_by_ipnum_v6(gi6_country, sa.sin6_addr);
341 static int fdout, fderr;
343 /* GeoIP people were too stupid to come to the idea that you could set
344 * errno appropriately and return NULL instead of printing stuff from
345 * the library directly that noone can turn off.
348 static void geoip_open_prepare(void)
350 fflush(stdout);
351 fdout = dup_or_die(1);
353 fflush(stderr);
354 fderr = dup_or_die(2);
356 close(1);
357 close(2);
360 static void geoip_open_restore(void)
362 dup2_or_die(fdout, 1);
363 dup2_or_die(fderr, 2);
365 close(fdout);
366 close(fderr);
369 static GeoIP *geoip_open_type(int type, int flags)
371 GeoIP *ret;
373 geoip_open_prepare();
374 ret = GeoIP_open_type(type, flags);
375 geoip_open_restore();
377 return ret;
380 static GeoIP *geoip_open(const char *filename, int flags)
382 GeoIP *ret;
384 geoip_open_prepare();
385 ret = GeoIP_open(filename, flags);
386 geoip_open_restore();
388 return ret;
391 static void init_geoip_city_open4(int enforce)
393 gi4_city = geoip_open(files[GEOIP_CITY_EDITION_REV1].local, GEOIP_MMAP_CACHE);
394 if (gi4_city == NULL) {
395 gi4_city = geoip_open_type(GEOIP_CITY_EDITION_REV1, GEOIP_MMAP_CACHE);
396 if (gi4_city == NULL)
397 if (enforce)
398 panic("Cannot open GeoIP4 city database, try --update!\n");
401 if (gi4_city) {
402 GeoIP_set_charset(gi4_city, GEOIP_CHARSET_UTF8);
403 geoip_db_present |= CITYV4;
407 static void init_geoip_city_open6(int enforce)
409 gi6_city = geoip_open(files[GEOIP_CITY_EDITION_REV1_V6].local, GEOIP_MMAP_CACHE);
410 if (gi6_city == NULL) {
411 gi6_city = geoip_open_type(GEOIP_CITY_EDITION_REV1_V6, GEOIP_MMAP_CACHE);
412 if (gi6_city == NULL)
413 if (enforce)
414 panic("Cannot open GeoIP6 city database, try --update!\n");
417 if (gi6_city) {
418 GeoIP_set_charset(gi6_city, GEOIP_CHARSET_UTF8);
419 geoip_db_present |= CITYV6;
423 static void init_geoip_city(int enforce)
425 init_geoip_city_open4(enforce);
426 init_geoip_city_open6(enforce);
429 static void destroy_geoip_city(void)
431 GeoIP_delete(gi4_city);
432 GeoIP_delete(gi6_city);
435 static void init_geoip_country_open4(int enforce)
437 gi4_country = geoip_open(files[GEOIP_COUNTRY_EDITION].local, GEOIP_MMAP_CACHE);
438 if (gi4_country == NULL) {
439 gi4_country = geoip_open_type(GEOIP_COUNTRY_EDITION, GEOIP_MMAP_CACHE);
440 if (gi4_country == NULL)
441 if (enforce)
442 panic("Cannot open GeoIP4 country database, try --update!\n");
445 if (gi4_country) {
446 GeoIP_set_charset(gi4_country, GEOIP_CHARSET_UTF8);
447 geoip_db_present |= COUNTRYV4;
451 static void init_geoip_country_open6(int enforce)
453 gi6_country = geoip_open(files[GEOIP_COUNTRY_EDITION_V6].local, GEOIP_MMAP_CACHE);
454 if (gi6_country == NULL) {
455 gi6_country = geoip_open_type(GEOIP_COUNTRY_EDITION_V6, GEOIP_MMAP_CACHE);
456 if (gi6_country == NULL)
457 if (enforce)
458 panic("Cannot open GeoIP6 country database, try --update!\n");
461 if (gi6_country) {
462 GeoIP_set_charset(gi6_country, GEOIP_CHARSET_UTF8);
463 geoip_db_present |= COUNTRYV6;
467 static void init_geoip_country(int enforce)
469 init_geoip_country_open4(enforce);
470 init_geoip_country_open6(enforce);
473 static void destroy_geoip_country(void)
475 GeoIP_delete(gi4_country);
476 GeoIP_delete(gi6_country);
479 static void init_geoip_asname_open4(int enforce)
481 gi4_asname = geoip_open(files[GEOIP_ASNUM_EDITION].local, GEOIP_MMAP_CACHE);
482 if (gi4_asname == NULL) {
483 gi4_asname = geoip_open_type(GEOIP_ASNUM_EDITION, GEOIP_MMAP_CACHE);
484 if (gi4_asname == NULL)
485 if (enforce)
486 panic("Cannot open GeoIP4 AS database, try --update!\n");
489 if (gi4_asname) {
490 GeoIP_set_charset(gi4_asname, GEOIP_CHARSET_UTF8);
491 geoip_db_present |= ASNAMV4;
495 static void init_geoip_asname_open6(int enforce)
497 gi6_asname = geoip_open(files[GEOIP_ASNUM_EDITION_V6].local, GEOIP_MMAP_CACHE);
498 if (gi6_asname == NULL) {
499 gi6_asname = geoip_open_type(GEOIP_ASNUM_EDITION_V6, GEOIP_MMAP_CACHE);
500 if (gi6_asname == NULL)
501 if (enforce)
502 panic("Cannot open GeoIP6 AS database, try --update!\n");
505 if (gi6_asname) {
506 GeoIP_set_charset(gi6_asname, GEOIP_CHARSET_UTF8);
507 geoip_db_present |= ASNAMV6;
511 static void init_geoip_asname(int enforce)
513 init_geoip_asname_open4(enforce);
514 init_geoip_asname_open6(enforce);
517 static void destroy_geoip_asname(void)
519 GeoIP_delete(gi4_asname);
520 GeoIP_delete(gi6_asname);
523 static void init_mirrors(void)
525 int i = 0;
526 FILE *fp;
527 char buff[256];
529 fp = fopen(PREFIX_STRING "/etc/netsniff-ng/geoip.conf", "r");
530 if (!fp)
531 panic("Cannot open geoip.conf!\n");
533 fmemset(buff, 0, sizeof(buff));
534 while (fgets(buff, sizeof(buff), fp) != NULL &&
535 i < array_size(servers)) {
536 buff[sizeof(buff) - 1] = 0;
537 buff[strlen(buff) - 1] = 0;
539 if (buff[0] == '#') {
540 fmemset(buff, 0, sizeof(buff));
541 continue;
544 servers[i++] = xstrdup(buff);
545 fmemset(buff, 0, sizeof(buff));
548 fclose(fp);
551 static void destroy_mirrors(void)
553 int i;
555 for (i = 0; i < array_size(servers); ++i)
556 free(servers[i]);
559 void init_geoip(int enforce)
561 init_geoip_city(enforce);
562 init_geoip_country(enforce);
563 init_geoip_asname(enforce);
566 void update_geoip(void)
568 int i, j, ret, good = 0;
570 init_mirrors();
572 for (i = 0; i < array_size(files); ++i) {
573 if (files[i].local && files[i].remote) {
574 good = 0;
576 for (j = 0; j < array_size(servers); ++j) {
577 if (servers[j] == NULL)
578 continue;
579 ret = geoip_get_database(servers[j], i);
580 if (!ret) {
581 good = 1;
582 break;
586 if (good == 0)
587 panic("Cannot get %s from mirrors!\n",
588 files[i].remote);
592 destroy_mirrors();
595 void destroy_geoip(void)
597 destroy_geoip_city();
598 destroy_geoip_country();
599 destroy_geoip_asname();
601 geoip_db_present = 0;