rnd: break out prng from xutils
[netsniff-ng.git] / geoip.c
blob85c425cf6e20753b6e35dc4c9301e88dfcf80b89
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 "str.h"
24 #include "xmalloc.h"
25 #include "zlib.h"
26 #include "geoip.h"
28 struct file {
29 const char *desc, *local;
30 const char *remote, *possible_prefix;
33 #define PRE "/download/geoip/database"
34 static const struct file files[] = {
35 [GEOIP_CITY_EDITION_REV1] = {
36 .desc = "City IPv4",
37 .local = PREFIX_STRING "/etc/netsniff-ng/city4.dat",
38 .remote = "/GeoLiteCity.dat.gz",
39 .possible_prefix = PRE,
41 [GEOIP_CITY_EDITION_REV1_V6] = {
42 .desc = "City IPv6",
43 .local = PREFIX_STRING "/etc/netsniff-ng/city6.dat",
44 .remote = "/GeoLiteCityv6.dat.gz",
45 .possible_prefix = PRE "/GeoLiteCityv6-beta",
47 [GEOIP_COUNTRY_EDITION] = {
48 .desc = "Country IPv4",
49 .local = PREFIX_STRING "/etc/netsniff-ng/country4.dat",
50 .remote = "/GeoIP.dat.gz",
51 .possible_prefix = PRE "/GeoLiteCountry",
53 [GEOIP_COUNTRY_EDITION_V6] = {
54 .desc = "Country IPv6",
55 .local = PREFIX_STRING "/etc/netsniff-ng/country6.dat",
56 .remote = "/GeoIPv6.dat.gz",
57 .possible_prefix = PRE,
59 [GEOIP_ASNUM_EDITION] = {
60 .desc = "AS Numbers IPv4",
61 .local = PREFIX_STRING "/etc/netsniff-ng/asname4.dat",
62 .remote = "/GeoIPASNum.dat.gz",
63 .possible_prefix = PRE "/asnum",
65 [GEOIP_ASNUM_EDITION_V6] = {
66 .desc = "AS Numbers IPv6",
67 .local = PREFIX_STRING "/etc/netsniff-ng/asname6.dat",
68 .remote = "/GeoIPASNumv6.dat.gz",
69 .possible_prefix = PRE "/asnum",
73 static GeoIP *gi4_asname = NULL, *gi6_asname = NULL;
74 static GeoIP *gi4_country = NULL, *gi6_country = NULL;
75 static GeoIP *gi4_city = NULL, *gi6_city = NULL;
77 static GeoIPRecord empty = { 0 };
79 static char *servers[16] = { 0 };
81 #define CITYV4 (1 << 0)
82 #define CITYV6 (1 << 1)
83 #define COUNTRYV4 (1 << 2)
84 #define COUNTRYV6 (1 << 3)
85 #define ASNAMV4 (1 << 4)
86 #define ASNAMV6 (1 << 5)
88 #define HAVEALL (CITYV4 | CITYV6 | COUNTRYV4 | COUNTRYV6 | ASNAMV4 | ASNAMV6)
90 static int geoip_db_present = 0;
92 int geoip_working(void)
94 return geoip_db_present == HAVEALL;
97 static int geoip_get_remote_fd(const char *server, const char *port)
99 int ret, fd = -1;
100 struct addrinfo hints, *ahead, *ai;
102 bug_on(!server || !port);
104 memset(&hints, 0, sizeof(hints));
106 hints.ai_family = PF_UNSPEC;
107 hints.ai_socktype = SOCK_STREAM;
108 hints.ai_protocol = IPPROTO_TCP;
109 hints.ai_flags = AI_NUMERICSERV;
111 ret = getaddrinfo(server, port, &hints, &ahead);
112 if (ret != 0)
113 return -EIO;
115 for (ai = ahead; ai != NULL && fd < 0; ai = ai->ai_next) {
116 fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
117 if (fd < 0)
118 continue;
120 ret = connect(fd, ai->ai_addr, ai->ai_addrlen);
121 if (ret < 0) {
122 close(fd);
123 fd = -1;
124 continue;
127 break;
130 freeaddrinfo(ahead);
132 return fd;
135 static void geoip_inflate(int which)
137 int ret, ret2 = 1;
138 gzFile fpi;
139 FILE *fpo;
140 char zfile[128], raw[4096];
142 slprintf(zfile, sizeof(zfile), "%s.gz", files[which].local);
143 fpi = gzopen(zfile, "rb");
144 if (fpi == NULL)
145 panic("No %s file!\n", zfile);
147 fpo = fopen(files[which].local, "wb");
148 if (fpo == NULL)
149 panic("Cannot create %s!\n", files[which].local);
151 while ((ret = gzread(fpi, raw, sizeof(raw))) && ret2)
152 ret2 = fwrite(raw, ret, 1, fpo);
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");
167 again:
168 found = good = 0;
169 ptr = NULL;
170 len = 0;
172 sock = geoip_get_remote_fd(host, "80");
173 if (sock < 0)
174 return -EIO;
176 slprintf(raw, sizeof(raw), "GET %s%s HTTP/1.1\nHost: %s\r\n\r\n",
177 retry ? files[which].possible_prefix : "",
178 files[which].remote, host);
180 ret = write(sock, raw, strlen(raw));
181 if (ret <= 0) {
182 close(sock);
183 return -EIO;
186 shutdown(sock, SHUT_WR);
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 raw[sizeof(raw) - 1] = 0;
201 for (i = 0; i < ret; i++) {
202 if (!strncmp(raw + i, "Content-Length: ", min(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(ret - i, lent)))
208 good = 1;
210 if (!strncmp(raw + i, "\r\n\r\n", min(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 static int fdout, fderr;
344 /* GeoIP people were too stupid to come to the idea that you could set
345 * errno appropriately and return NULL instead of printing stuff from
346 * the library directly that noone can turn off.
349 static void geoip_open_prepare(void)
351 fflush(stdout);
352 fdout = dup(1);
354 fflush(stderr);
355 fderr = dup(2);
357 close(1);
358 close(2);
361 static void geoip_open_restore(void)
363 dup2(fdout, 1);
364 dup2(fderr, 2);
366 close(fdout);
367 close(fderr);
370 static GeoIP *geoip_open_type(int type, int flags)
372 GeoIP *ret;
374 geoip_open_prepare();
375 ret = GeoIP_open_type(type, flags);
376 geoip_open_restore();
378 return ret;
381 static GeoIP *geoip_open(const char *filename, int flags)
383 GeoIP *ret;
385 geoip_open_prepare();
386 ret = GeoIP_open(filename, flags);
387 geoip_open_restore();
389 return ret;
392 static void init_geoip_city_open4(int enforce)
394 gi4_city = geoip_open(files[GEOIP_CITY_EDITION_REV1].local, GEOIP_MMAP_CACHE);
395 if (gi4_city == NULL) {
396 gi4_city = geoip_open_type(GEOIP_CITY_EDITION_REV1, GEOIP_MMAP_CACHE);
397 if (gi4_city == NULL)
398 if (enforce)
399 panic("Cannot open GeoIP4 city database, try --update!\n");
402 if (gi4_city) {
403 GeoIP_set_charset(gi4_city, GEOIP_CHARSET_UTF8);
404 geoip_db_present |= CITYV4;
408 static void init_geoip_city_open6(int enforce)
410 gi6_city = geoip_open(files[GEOIP_CITY_EDITION_REV1_V6].local, GEOIP_MMAP_CACHE);
411 if (gi6_city == NULL) {
412 gi6_city = geoip_open_type(GEOIP_CITY_EDITION_REV1_V6, GEOIP_MMAP_CACHE);
413 if (gi6_city == NULL)
414 if (enforce)
415 panic("Cannot open GeoIP6 city database, try --update!\n");
418 if (gi6_city) {
419 GeoIP_set_charset(gi6_city, GEOIP_CHARSET_UTF8);
420 geoip_db_present |= CITYV6;
424 static void init_geoip_city(int enforce)
426 init_geoip_city_open4(enforce);
427 init_geoip_city_open6(enforce);
430 static void destroy_geoip_city(void)
432 GeoIP_delete(gi4_city);
433 GeoIP_delete(gi6_city);
436 static void init_geoip_country_open4(int enforce)
438 gi4_country = geoip_open(files[GEOIP_COUNTRY_EDITION].local, GEOIP_MMAP_CACHE);
439 if (gi4_country == NULL) {
440 gi4_country = geoip_open_type(GEOIP_COUNTRY_EDITION, GEOIP_MMAP_CACHE);
441 if (gi4_country == NULL)
442 if (enforce)
443 panic("Cannot open GeoIP4 country database, try --update!\n");
446 if (gi4_country) {
447 GeoIP_set_charset(gi4_country, GEOIP_CHARSET_UTF8);
448 geoip_db_present |= COUNTRYV4;
452 static void init_geoip_country_open6(int enforce)
454 gi6_country = geoip_open(files[GEOIP_COUNTRY_EDITION_V6].local, GEOIP_MMAP_CACHE);
455 if (gi6_country == NULL) {
456 gi6_country = geoip_open_type(GEOIP_COUNTRY_EDITION_V6, GEOIP_MMAP_CACHE);
457 if (gi6_country == NULL)
458 if (enforce)
459 panic("Cannot open GeoIP6 country database, try --update!\n");
462 if (gi6_country) {
463 GeoIP_set_charset(gi6_country, GEOIP_CHARSET_UTF8);
464 geoip_db_present |= COUNTRYV6;
468 static void init_geoip_country(int enforce)
470 init_geoip_country_open4(enforce);
471 init_geoip_country_open6(enforce);
474 static void destroy_geoip_country(void)
476 GeoIP_delete(gi4_country);
477 GeoIP_delete(gi6_country);
480 static void init_geoip_asname_open4(int enforce)
482 gi4_asname = geoip_open(files[GEOIP_ASNUM_EDITION].local, GEOIP_MMAP_CACHE);
483 if (gi4_asname == NULL) {
484 gi4_asname = geoip_open_type(GEOIP_ASNUM_EDITION, GEOIP_MMAP_CACHE);
485 if (gi4_asname == NULL)
486 if (enforce)
487 panic("Cannot open GeoIP4 AS database, try --update!\n");
490 if (gi4_asname) {
491 GeoIP_set_charset(gi4_asname, GEOIP_CHARSET_UTF8);
492 geoip_db_present |= ASNAMV4;
496 static void init_geoip_asname_open6(int enforce)
498 gi6_asname = geoip_open(files[GEOIP_ASNUM_EDITION_V6].local, GEOIP_MMAP_CACHE);
499 if (gi6_asname == NULL) {
500 gi6_asname = geoip_open_type(GEOIP_ASNUM_EDITION_V6, GEOIP_MMAP_CACHE);
501 if (gi6_asname == NULL)
502 if (enforce)
503 panic("Cannot open GeoIP6 AS database, try --update!\n");
506 if (gi6_asname) {
507 GeoIP_set_charset(gi6_asname, GEOIP_CHARSET_UTF8);
508 geoip_db_present |= ASNAMV6;
512 static void init_geoip_asname(int enforce)
514 init_geoip_asname_open4(enforce);
515 init_geoip_asname_open6(enforce);
518 static void destroy_geoip_asname(void)
520 GeoIP_delete(gi4_asname);
521 GeoIP_delete(gi6_asname);
524 static void init_mirrors(void)
526 int i = 0;
527 FILE *fp;
528 char buff[256];
530 fp = fopen(PREFIX_STRING "/etc/netsniff-ng/geoip.conf", "r");
531 if (!fp)
532 panic("Cannot open geoip.conf!\n");
534 fmemset(buff, 0, sizeof(buff));
535 while (fgets(buff, sizeof(buff), fp) != NULL &&
536 i < array_size(servers)) {
537 buff[sizeof(buff) - 1] = 0;
538 buff[strlen(buff) - 1] = 0;
540 if (buff[0] == '#') {
541 fmemset(buff, 0, sizeof(buff));
542 continue;
545 servers[i++] = xstrdup(buff);
546 fmemset(buff, 0, sizeof(buff));
549 fclose(fp);
552 static void destroy_mirrors(void)
554 int i;
556 for (i = 0; i < array_size(servers); ++i)
557 free(servers[i]);
560 void init_geoip(int enforce)
562 init_geoip_city(enforce);
563 init_geoip_country(enforce);
564 init_geoip_asname(enforce);
567 void update_geoip(void)
569 int i, j, ret, good = 0;
571 init_mirrors();
573 for (i = 0; i < array_size(files); ++i) {
574 if (files[i].local && files[i].remote) {
575 good = 0;
577 for (j = 0; j < array_size(servers); ++j) {
578 if (servers[j] == NULL)
579 continue;
580 ret = geoip_get_database(servers[j], i);
581 if (!ret) {
582 good = 1;
583 break;
587 if (good == 0)
588 panic("Cannot get %s from mirrors!\n",
589 files[i].remote);
593 destroy_mirrors();
596 void destroy_geoip(void)
598 destroy_geoip_city();
599 destroy_geoip_country();
600 destroy_geoip_asname();
602 geoip_db_present = 0;