trafgen: parser: add possibility for negative numbers
[netsniff-ng.git] / geoip.c
blobc4c6cf83ba5d2d7051998207e5a215b619e57631
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 "xutils.h"
22 #include "xio.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 return -EIO;
183 shutdown(sock, SHUT_WR);
185 slprintf(zfile, sizeof(zfile), "%s.gz", files[which].local);
186 fd = open_or_die_m(zfile, O_WRONLY | O_CREAT | O_TRUNC, DEFFILEMODE);
188 memset(raw, 0, sizeof(raw));
189 ret = read(sock, raw, sizeof(raw));
190 if (ret <= 0)
191 return -EIO;
193 raw[sizeof(raw) - 1] = 0;
195 for (i = 0; i < ret; i++) {
196 if (!strncmp(raw + i, "Content-Length: ", min(ret - i, lenl))) {
197 ptr = raw + i + lenl;
198 rtotlen = strtoul(ptr, NULL, 10);
201 if (!strncmp(raw + i, "HTTP/1.1 200 OK", min(ret - i, lent)))
202 good = 1;
204 if (!strncmp(raw + i, "\r\n\r\n", min(ret - i, lenc))) {
205 ptr = raw + i + lenc;
206 len = ret - i - lenc;
207 found = 1;
208 break;
212 if (!found || ptr >= raw + ret || len < 0 || rtotlen == 0 || good == 0) {
213 if (retry == 0) {
214 retry = 1;
215 close(fd);
216 close(sock);
217 goto again;
220 return -ENOENT;
223 do {
224 write_or_die(fd, ptr, len);
225 totlen += len;
226 printf("\r%s [%.2f%%, %zd/%zd, %s]", files[which].desc,
227 100.f * totlen / rtotlen, totlen, rtotlen, host);
228 fflush(stdout);
230 memset(raw, 0, sizeof(raw));
231 ret = read(sock, raw, sizeof(raw));
233 ptr = raw;
234 len = ret;
235 } while(ret > 0);
237 printf("\n");
239 close(fd);
240 close(sock);
242 if (totlen != rtotlen) {
243 unlink(files[which].local);
244 return -EIO;
247 geoip_inflate(which);
248 unlink(zfile);
250 return 0;
253 static GeoIPRecord *geoip4_get_record(struct sockaddr_in sa)
255 bug_on(gi4_city == NULL);
257 return GeoIP_record_by_ipnum(gi4_city, ntohl(sa.sin_addr.s_addr)) ? : &empty;
260 static GeoIPRecord *geoip6_get_record(struct sockaddr_in6 sa)
262 bug_on(gi6_city == NULL);
264 return GeoIP_record_by_ipnum_v6(gi6_city, sa.sin6_addr) ? : &empty;
267 const char *geoip4_as_name(struct sockaddr_in sa)
269 bug_on(gi4_asname == NULL);
271 return GeoIP_name_by_ipnum(gi4_asname, ntohl(sa.sin_addr.s_addr));
274 const char *geoip6_as_name(struct sockaddr_in6 sa)
276 bug_on(gi6_asname == NULL);
278 return GeoIP_name_by_ipnum_v6(gi6_asname, sa.sin6_addr);
281 float geoip4_longitude(struct sockaddr_in sa)
283 return geoip4_get_record(sa)->longitude;
286 float geoip4_latitude(struct sockaddr_in sa)
288 return geoip4_get_record(sa)->latitude;
291 float geoip6_longitude(struct sockaddr_in6 sa)
293 return geoip6_get_record(sa)->longitude;
296 float geoip6_latitude(struct sockaddr_in6 sa)
298 return geoip6_get_record(sa)->latitude;
301 const char *geoip4_city_name(struct sockaddr_in sa)
303 return geoip4_get_record(sa)->city;
306 const char *geoip6_city_name(struct sockaddr_in6 sa)
308 return geoip6_get_record(sa)->city;
311 const char *geoip4_region_name(struct sockaddr_in sa)
313 return geoip4_get_record(sa)->region;
316 const char *geoip6_region_name(struct sockaddr_in6 sa)
318 return geoip6_get_record(sa)->region;
321 const char *geoip4_country_name(struct sockaddr_in sa)
323 bug_on(gi4_country == NULL);
325 return GeoIP_country_name_by_ipnum(gi4_country, ntohl(sa.sin_addr.s_addr));
328 const char *geoip6_country_name(struct sockaddr_in6 sa)
330 bug_on(gi6_country == NULL);
332 return GeoIP_country_name_by_ipnum_v6(gi6_country, sa.sin6_addr);
335 static int fdout, fderr;
337 /* GeoIP people were too stupid to come to the idea that you could set
338 * errno appropriately and return NULL instead of printing stuff from
339 * the library directly that noone can turn off.
342 static void geoip_open_prepare(void)
344 fflush(stdout);
345 fdout = dup(1);
347 fflush(stderr);
348 fderr = dup(2);
350 close(1);
351 close(2);
354 static void geoip_open_restore(void)
356 dup2(fdout, 1);
357 dup2(fderr, 2);
359 close(fdout);
360 close(fderr);
363 static GeoIP *geoip_open_type(int type, int flags)
365 GeoIP *ret;
367 geoip_open_prepare();
368 ret = GeoIP_open_type(type, flags);
369 geoip_open_restore();
371 return ret;
374 static GeoIP *geoip_open(const char *filename, int flags)
376 GeoIP *ret;
378 geoip_open_prepare();
379 ret = GeoIP_open(filename, flags);
380 geoip_open_restore();
382 return ret;
385 static void init_geoip_city_open4(int enforce)
387 gi4_city = geoip_open(files[GEOIP_CITY_EDITION_REV1].local, GEOIP_MMAP_CACHE);
388 if (gi4_city == NULL) {
389 gi4_city = geoip_open_type(GEOIP_CITY_EDITION_REV1, GEOIP_MMAP_CACHE);
390 if (gi4_city == NULL)
391 if (enforce)
392 panic("Cannot open GeoIP4 city database, try --update!\n");
395 if (gi4_city) {
396 GeoIP_set_charset(gi4_city, GEOIP_CHARSET_UTF8);
397 geoip_db_present |= CITYV4;
401 static void init_geoip_city_open6(int enforce)
403 gi6_city = geoip_open(files[GEOIP_CITY_EDITION_REV1_V6].local, GEOIP_MMAP_CACHE);
404 if (gi6_city == NULL) {
405 gi6_city = geoip_open_type(GEOIP_CITY_EDITION_REV1_V6, GEOIP_MMAP_CACHE);
406 if (gi6_city == NULL)
407 if (enforce)
408 panic("Cannot open GeoIP6 city database, try --update!\n");
411 if (gi6_city) {
412 GeoIP_set_charset(gi6_city, GEOIP_CHARSET_UTF8);
413 geoip_db_present |= CITYV6;
417 static void init_geoip_city(int enforce)
419 init_geoip_city_open4(enforce);
420 init_geoip_city_open6(enforce);
423 static void destroy_geoip_city(void)
425 GeoIP_delete(gi4_city);
426 GeoIP_delete(gi6_city);
429 static void init_geoip_country_open4(int enforce)
431 gi4_country = geoip_open(files[GEOIP_COUNTRY_EDITION].local, GEOIP_MMAP_CACHE);
432 if (gi4_country == NULL) {
433 gi4_country = geoip_open_type(GEOIP_COUNTRY_EDITION, GEOIP_MMAP_CACHE);
434 if (gi4_country == NULL)
435 if (enforce)
436 panic("Cannot open GeoIP4 country database, try --update!\n");
439 if (gi4_country) {
440 GeoIP_set_charset(gi4_country, GEOIP_CHARSET_UTF8);
441 geoip_db_present |= COUNTRYV4;
445 static void init_geoip_country_open6(int enforce)
447 gi6_country = geoip_open(files[GEOIP_COUNTRY_EDITION_V6].local, GEOIP_MMAP_CACHE);
448 if (gi6_country == NULL) {
449 gi6_country = geoip_open_type(GEOIP_COUNTRY_EDITION_V6, GEOIP_MMAP_CACHE);
450 if (gi6_country == NULL)
451 if (enforce)
452 panic("Cannot open GeoIP6 country database, try --update!\n");
455 if (gi6_country) {
456 GeoIP_set_charset(gi6_country, GEOIP_CHARSET_UTF8);
457 geoip_db_present |= COUNTRYV6;
461 static void init_geoip_country(int enforce)
463 init_geoip_country_open4(enforce);
464 init_geoip_country_open6(enforce);
467 static void destroy_geoip_country(void)
469 GeoIP_delete(gi4_country);
470 GeoIP_delete(gi6_country);
473 static void init_geoip_asname_open4(int enforce)
475 gi4_asname = geoip_open(files[GEOIP_ASNUM_EDITION].local, GEOIP_MMAP_CACHE);
476 if (gi4_asname == NULL) {
477 gi4_asname = geoip_open_type(GEOIP_ASNUM_EDITION, GEOIP_MMAP_CACHE);
478 if (gi4_asname == NULL)
479 if (enforce)
480 panic("Cannot open GeoIP4 AS database, try --update!\n");
483 if (gi4_asname) {
484 GeoIP_set_charset(gi4_asname, GEOIP_CHARSET_UTF8);
485 geoip_db_present |= ASNAMV4;
489 static void init_geoip_asname_open6(int enforce)
491 gi6_asname = geoip_open(files[GEOIP_ASNUM_EDITION_V6].local, GEOIP_MMAP_CACHE);
492 if (gi6_asname == NULL) {
493 gi6_asname = geoip_open_type(GEOIP_ASNUM_EDITION_V6, GEOIP_MMAP_CACHE);
494 if (gi6_asname == NULL)
495 if (enforce)
496 panic("Cannot open GeoIP6 AS database, try --update!\n");
499 if (gi6_asname) {
500 GeoIP_set_charset(gi6_asname, GEOIP_CHARSET_UTF8);
501 geoip_db_present |= ASNAMV6;
505 static void init_geoip_asname(int enforce)
507 init_geoip_asname_open4(enforce);
508 init_geoip_asname_open6(enforce);
511 static void destroy_geoip_asname(void)
513 GeoIP_delete(gi4_asname);
514 GeoIP_delete(gi6_asname);
517 static void init_mirrors(void)
519 int i = 0;
520 FILE *fp;
521 char buff[256];
523 fp = fopen(PREFIX_STRING "/etc/netsniff-ng/geoip.conf", "r");
524 if (!fp)
525 panic("Cannot open geoip.conf!\n");
527 fmemset(buff, 0, sizeof(buff));
528 while (fgets(buff, sizeof(buff), fp) != NULL &&
529 i < array_size(servers)) {
530 buff[sizeof(buff) - 1] = 0;
531 buff[strlen(buff) - 1] = 0;
533 if (buff[0] == '#') {
534 fmemset(buff, 0, sizeof(buff));
535 continue;
538 servers[i++] = xstrdup(buff);
539 fmemset(buff, 0, sizeof(buff));
542 fclose(fp);
545 static void destroy_mirrors(void)
547 int i;
549 for (i = 0; i < array_size(servers); ++i)
550 free(servers[i]);
553 void init_geoip(int enforce)
555 init_geoip_city(enforce);
556 init_geoip_country(enforce);
557 init_geoip_asname(enforce);
560 void update_geoip(void)
562 int i, j, ret, good = 0;
564 init_mirrors();
566 for (i = 0; i < array_size(files); ++i) {
567 if (files[i].local && files[i].remote) {
568 good = 0;
570 for (j = 0; j < array_size(servers); ++j) {
571 if (servers[j] == NULL)
572 continue;
573 ret = geoip_get_database(servers[j], i);
574 if (!ret) {
575 good = 1;
576 break;
580 if (good == 0)
581 panic("Cannot get %s from mirrors!\n",
582 files[i].remote);
586 destroy_mirrors();
589 void destroy_geoip(void)
591 destroy_geoip_city();
592 destroy_geoip_country();
593 destroy_geoip_asname();
595 geoip_db_present = 0;