summaryrefslogtreecommitdiff
path: root/geoip.c
diff options
context:
space:
mode:
authorDaniel Borkmann <dborkman@redhat.com>2013-03-15 10:41:48 +0100
committerDaniel Borkmann <dborkman@redhat.com>2013-03-15 10:41:48 +0100
commit1a9fbac03c684f29cff9ac44875bd9504a89f54e (patch)
tree1b2e40dbe5dc1899ef5b62c4325c9b94c9c450fc /geoip.c
all: import netsniff-ng 0.5.8-rc0 source
We decided to get rid of the old Git history and start a new one for several reasons: *) Allow / enforce only high-quality commits (which was not the case for many commits in the history), have a policy that is more close to the one from the Linux kernel. With high quality commits, we mean code that is logically split into commits and commit messages that are signed-off and have a proper subject and message body. We do not allow automatic Github merges anymore, since they are total bullshit. However, we will either cherry-pick your patches or pull them manually. *) The old archive was about ~27MB for no particular good reason. This basically derived from the bad decision that also some PDF files where stored there. From this moment onwards, no binary objects are allowed to be stored in this repository anymore. The old archive is not wiped away from the Internet. You will still be able to find it, e.g. on git.cryptoism.org etc. Signed-off-by: Daniel Borkmann <dborkman@redhat.com> Signed-off-by: Tobias Klauser <tklauser@distanz.ch>
Diffstat (limited to 'geoip.c')
-rw-r--r--geoip.c595
1 files changed, 595 insertions, 0 deletions
diff --git a/geoip.c b/geoip.c
new file mode 100644
index 0000000..fc61184
--- /dev/null
+++ b/geoip.c
@@ -0,0 +1,595 @@
+/*
+ * netsniff-ng - the packet sniffing beast
+ * Copyright 2013 Daniel Borkmann.
+ * Subject to the GPL, version 2.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <GeoIP.h>
+#include <GeoIPCity.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "built_in.h"
+#include "die.h"
+#include "xutils.h"
+#include "xio.h"
+#include "xmalloc.h"
+#include "zlib.h"
+#include "geoip.h"
+
+struct file {
+ const char *desc, *local;
+ const char *remote, *possible_prefix;
+};
+
+#define PRE "/download/geoip/database"
+static const struct file files[] = {
+ [GEOIP_CITY_EDITION_REV1] = {
+ .desc = "City IPv4",
+ .local = "/etc/netsniff-ng/city4.dat",
+ .remote = "/GeoLiteCity.dat.gz",
+ .possible_prefix = PRE,
+ },
+ [GEOIP_CITY_EDITION_REV1_V6] = {
+ .desc = "City IPv6",
+ .local = "/etc/netsniff-ng/city6.dat",
+ .remote = "/GeoLiteCityv6.dat.gz",
+ .possible_prefix = PRE "/GeoLiteCityv6-beta",
+ },
+ [GEOIP_COUNTRY_EDITION] = {
+ .desc = "Country IPv4",
+ .local = "/etc/netsniff-ng/country4.dat",
+ .remote = "/GeoIP.dat.gz",
+ .possible_prefix = PRE "/GeoLiteCountry",
+ },
+ [GEOIP_COUNTRY_EDITION_V6] = {
+ .desc = "Country IPv6",
+ .local = "/etc/netsniff-ng/country6.dat",
+ .remote = "/GeoIPv6.dat.gz",
+ .possible_prefix = PRE,
+ },
+ [GEOIP_ASNUM_EDITION] = {
+ .desc = "AS Numbers IPv4",
+ .local = "/etc/netsniff-ng/asname4.dat",
+ .remote = "/GeoIPASNum.dat.gz",
+ .possible_prefix = PRE "/asnum",
+ },
+ [GEOIP_ASNUM_EDITION_V6] = {
+ .desc = "AS Numbers IPv6",
+ .local = "/etc/netsniff-ng/asname6.dat",
+ .remote = "/GeoIPASNumv6.dat.gz",
+ .possible_prefix = PRE "/asnum",
+ },
+};
+
+static GeoIP *gi4_asname = NULL, *gi6_asname = NULL;
+static GeoIP *gi4_country = NULL, *gi6_country = NULL;
+static GeoIP *gi4_city = NULL, *gi6_city = NULL;
+
+static GeoIPRecord empty = { 0 };
+
+static char *servers[16] = { 0 };
+
+#define CITYV4 (1 << 0)
+#define CITYV6 (1 << 1)
+#define COUNTRYV4 (1 << 2)
+#define COUNTRYV6 (1 << 3)
+#define ASNAMV4 (1 << 4)
+#define ASNAMV6 (1 << 5)
+
+#define HAVEALL (CITYV4 | CITYV6 | COUNTRYV4 | COUNTRYV6 | ASNAMV4 | ASNAMV6)
+
+static int geoip_db_present = 0;
+
+int geoip_working(void)
+{
+ return geoip_db_present == HAVEALL;
+}
+
+static int geoip_get_remote_fd(const char *server, const char *port)
+{
+ int ret, fd = -1;
+ struct addrinfo hints, *ahead, *ai;
+
+ bug_on(!server || !port);
+
+ memset(&hints, 0, sizeof(hints));
+
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+ hints.ai_flags = AI_NUMERICSERV;
+
+ ret = getaddrinfo(server, port, &hints, &ahead);
+ if (ret != 0)
+ return -EIO;
+
+ for (ai = ahead; ai != NULL && fd < 0; ai = ai->ai_next) {
+ fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ if (fd < 0)
+ continue;
+
+ ret = connect(fd, ai->ai_addr, ai->ai_addrlen);
+ if (ret < 0) {
+ close(fd);
+ fd = -1;
+ continue;
+ }
+
+ break;
+ }
+
+ freeaddrinfo(ahead);
+
+ return fd;
+}
+
+static void geoip_inflate(int which)
+{
+ int ret, ret2 = 1;
+ gzFile fpi;
+ FILE *fpo;
+ char zfile[128], raw[4096];
+
+ slprintf(zfile, sizeof(zfile), "%s.gz", files[which].local);
+ fpi = gzopen(zfile, "rb");
+ if (fpi == NULL)
+ panic("No %s file!\n", zfile);
+
+ fpo = fopen(files[which].local, "wb");
+ if (fpo == NULL)
+ panic("Cannot create %s!\n", files[which].local);
+
+ while ((ret = gzread(fpi, raw, sizeof(raw))) && ret2)
+ ret2 = fwrite(raw, ret, 1, fpo);
+
+ gzclose(fpi);
+ fclose(fpo);
+}
+
+static int geoip_get_database(const char *host, int which)
+{
+ int found, sock, fd, i, good, retry = 0;
+ ssize_t ret, len, rtotlen = 0, totlen = 0;
+ char raw[4096], *ptr, zfile[128];
+ size_t lenl = strlen("Content-Length: ");
+ size_t lent = strlen("HTTP/1.1 200 OK");
+ size_t lenc = strlen("\r\n\r\n");
+
+again:
+ found = good = 0;
+ ptr = NULL;
+ len = 0;
+
+ sock = geoip_get_remote_fd(host, "80");
+ if (sock < 0)
+ return -EIO;
+
+ slprintf(raw, sizeof(raw), "GET %s%s HTTP/1.1\nHost: %s\r\n\r\n",
+ retry ? files[which].possible_prefix : "",
+ files[which].remote, host);
+
+ ret = write(sock, raw, strlen(raw));
+ if (ret <= 0)
+ return -EIO;
+
+ shutdown(sock, SHUT_WR);
+
+ slprintf(zfile, sizeof(zfile), "%s.gz", files[which].local);
+ fd = open_or_die_m(zfile, O_WRONLY | O_CREAT | O_TRUNC, DEFFILEMODE);
+
+ memset(raw, 0, sizeof(raw));
+ ret = read(sock, raw, sizeof(raw));
+ if (ret <= 0)
+ return -EIO;
+
+ for (i = 0; i < ret; i++) {
+ if (!strncmp(raw + i, "Content-Length: ", min(ret - i, lenl))) {
+ ptr = raw + i + lenl;
+ rtotlen = strtoul(ptr, NULL, 10);
+ }
+
+ if (!strncmp(raw + i, "HTTP/1.1 200 OK", min(ret - i, lent)))
+ good = 1;
+
+ if (!strncmp(raw + i, "\r\n\r\n", min(ret - i, lenc))) {
+ ptr = raw + i + lenc;
+ len = ret - i - lenc;
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found || ptr >= raw + ret || len < 0 || rtotlen == 0 || good == 0) {
+ if (retry == 0) {
+ retry = 1;
+ close(fd);
+ close(sock);
+ goto again;
+ }
+
+ return -ENOENT;
+ }
+
+ do {
+ write_or_die(fd, ptr, len);
+ totlen += len;
+ printf("\r%s [%.2f%%, %zd/%zd, %s]", files[which].desc,
+ 100.f * totlen / rtotlen, totlen, rtotlen, host);
+ fflush(stdout);
+
+ memset(raw, 0, sizeof(raw));
+ ret = read(sock, raw, sizeof(raw));
+
+ ptr = raw;
+ len = ret;
+ } while(ret > 0);
+
+ printf("\n");
+
+ if (totlen != rtotlen) {
+ unlink(files[which].local);
+ return -EIO;
+ }
+
+ close(fd);
+ close(sock);
+
+ geoip_inflate(which);
+
+ unlink(zfile);
+
+ return 0;
+}
+
+static GeoIPRecord *geoip4_get_record(struct sockaddr_in sa)
+{
+ bug_on(gi4_city == NULL);
+
+ return GeoIP_record_by_ipnum(gi4_city, ntohl(sa.sin_addr.s_addr)) ? : &empty;
+}
+
+static GeoIPRecord *geoip6_get_record(struct sockaddr_in6 sa)
+{
+ bug_on(gi6_city == NULL);
+
+ return GeoIP_record_by_ipnum_v6(gi6_city, sa.sin6_addr) ? : &empty;
+}
+
+const char *geoip4_as_name(struct sockaddr_in sa)
+{
+ bug_on(gi4_asname == NULL);
+
+ return GeoIP_name_by_ipnum(gi4_asname, ntohl(sa.sin_addr.s_addr));
+}
+
+const char *geoip6_as_name(struct sockaddr_in6 sa)
+{
+ bug_on(gi6_asname == NULL);
+
+ return GeoIP_name_by_ipnum_v6(gi6_asname, sa.sin6_addr);
+}
+
+float geoip4_longitude(struct sockaddr_in sa)
+{
+ return geoip4_get_record(sa)->longitude;
+}
+
+float geoip4_latitude(struct sockaddr_in sa)
+{
+ return geoip4_get_record(sa)->latitude;
+}
+
+float geoip6_longitude(struct sockaddr_in6 sa)
+{
+ return geoip6_get_record(sa)->longitude;
+}
+
+float geoip6_latitude(struct sockaddr_in6 sa)
+{
+ return geoip6_get_record(sa)->latitude;
+}
+
+const char *geoip4_city_name(struct sockaddr_in sa)
+{
+ return geoip4_get_record(sa)->city;
+}
+
+const char *geoip6_city_name(struct sockaddr_in6 sa)
+{
+ return geoip6_get_record(sa)->city;
+}
+
+const char *geoip4_region_name(struct sockaddr_in sa)
+{
+ return geoip4_get_record(sa)->region;
+}
+
+const char *geoip6_region_name(struct sockaddr_in6 sa)
+{
+ return geoip6_get_record(sa)->region;
+}
+
+const char *geoip4_country_name(struct sockaddr_in sa)
+{
+ bug_on(gi4_country == NULL);
+
+ return GeoIP_country_name_by_ipnum(gi4_country, ntohl(sa.sin_addr.s_addr));
+}
+
+const char *geoip6_country_name(struct sockaddr_in6 sa)
+{
+ bug_on(gi6_country == NULL);
+
+ return GeoIP_country_name_by_ipnum_v6(gi6_country, sa.sin6_addr);
+}
+
+static int fdout, fderr;
+
+/* GeoIP people were too stupid to come to the idea that you could set
+ * errno appropriately and return NULL instead of printing stuff from
+ * the library directly that noone can turn off.
+ */
+
+static void geoip_open_prepare(void)
+{
+ fflush(stdout);
+ fdout = dup(1);
+
+ fflush(stderr);
+ fderr = dup(2);
+
+ close(1);
+ close(2);
+}
+
+static void geoip_open_restore(void)
+{
+ dup2(fdout, 1);
+ dup2(fderr, 2);
+
+ close(fdout);
+ close(fderr);
+}
+
+static GeoIP *geoip_open_type(int type, int flags)
+{
+ GeoIP *ret;
+
+ geoip_open_prepare();
+ ret = GeoIP_open_type(type, flags);
+ geoip_open_restore();
+
+ return ret;
+}
+
+static GeoIP *geoip_open(const char *filename, int flags)
+{
+ GeoIP *ret;
+
+ geoip_open_prepare();
+ ret = GeoIP_open(filename, flags);
+ geoip_open_restore();
+
+ return ret;
+}
+
+static void init_geoip_city_open4(int enforce)
+{
+ gi4_city = geoip_open(files[GEOIP_CITY_EDITION_REV1].local, GEOIP_MMAP_CACHE);
+ if (gi4_city == NULL) {
+ gi4_city = geoip_open_type(GEOIP_CITY_EDITION_REV1, GEOIP_MMAP_CACHE);
+ if (gi4_city == NULL)
+ if (enforce)
+ panic("Cannot open GeoIP4 city database, try --update!\n");
+ }
+
+ if (gi4_city) {
+ GeoIP_set_charset(gi4_city, GEOIP_CHARSET_UTF8);
+ geoip_db_present |= CITYV4;
+ }
+}
+
+static void init_geoip_city_open6(int enforce)
+{
+ gi6_city = geoip_open(files[GEOIP_CITY_EDITION_REV1_V6].local, GEOIP_MMAP_CACHE);
+ if (gi6_city == NULL) {
+ gi6_city = geoip_open_type(GEOIP_CITY_EDITION_REV1_V6, GEOIP_MMAP_CACHE);
+ if (gi6_city == NULL)
+ if (enforce)
+ panic("Cannot open GeoIP6 city database, try --update!\n");
+ }
+
+ if (gi6_city) {
+ GeoIP_set_charset(gi6_city, GEOIP_CHARSET_UTF8);
+ geoip_db_present |= CITYV6;
+ }
+}
+
+static void init_geoip_city(int enforce)
+{
+ init_geoip_city_open4(enforce);
+ init_geoip_city_open6(enforce);
+}
+
+static void destroy_geoip_city(void)
+{
+ GeoIP_delete(gi4_city);
+ GeoIP_delete(gi6_city);
+}
+
+static void init_geoip_country_open4(int enforce)
+{
+ gi4_country = geoip_open(files[GEOIP_COUNTRY_EDITION].local, GEOIP_MMAP_CACHE);
+ if (gi4_country == NULL) {
+ gi4_country = geoip_open_type(GEOIP_COUNTRY_EDITION, GEOIP_MMAP_CACHE);
+ if (gi4_country == NULL)
+ if (enforce)
+ panic("Cannot open GeoIP4 country database, try --update!\n");
+ }
+
+ if (gi4_country) {
+ GeoIP_set_charset(gi4_country, GEOIP_CHARSET_UTF8);
+ geoip_db_present |= COUNTRYV4;
+ }
+}
+
+static void init_geoip_country_open6(int enforce)
+{
+ gi6_country = geoip_open(files[GEOIP_COUNTRY_EDITION_V6].local, GEOIP_MMAP_CACHE);
+ if (gi6_country == NULL) {
+ gi6_country = geoip_open_type(GEOIP_COUNTRY_EDITION_V6, GEOIP_MMAP_CACHE);
+ if (gi6_country == NULL)
+ if (enforce)
+ panic("Cannot open GeoIP6 country database, try --update!\n");
+ }
+
+ if (gi6_country) {
+ GeoIP_set_charset(gi6_country, GEOIP_CHARSET_UTF8);
+ geoip_db_present |= COUNTRYV6;
+ }
+}
+
+static void init_geoip_country(int enforce)
+{
+ init_geoip_country_open4(enforce);
+ init_geoip_country_open6(enforce);
+}
+
+static void destroy_geoip_country(void)
+{
+ GeoIP_delete(gi4_country);
+ GeoIP_delete(gi6_country);
+}
+
+static void init_geoip_asname_open4(int enforce)
+{
+ gi4_asname = geoip_open(files[GEOIP_ASNUM_EDITION].local, GEOIP_MMAP_CACHE);
+ if (gi4_asname == NULL) {
+ gi4_asname = geoip_open_type(GEOIP_ASNUM_EDITION, GEOIP_MMAP_CACHE);
+ if (gi4_asname == NULL)
+ if (enforce)
+ panic("Cannot open GeoIP4 AS database, try --update!\n");
+ }
+
+ if (gi4_asname) {
+ GeoIP_set_charset(gi4_asname, GEOIP_CHARSET_UTF8);
+ geoip_db_present |= ASNAMV4;
+ }
+}
+
+static void init_geoip_asname_open6(int enforce)
+{
+ gi6_asname = geoip_open(files[GEOIP_ASNUM_EDITION_V6].local, GEOIP_MMAP_CACHE);
+ if (gi6_asname == NULL) {
+ gi6_asname = geoip_open_type(GEOIP_ASNUM_EDITION_V6, GEOIP_MMAP_CACHE);
+ if (gi6_asname == NULL)
+ if (enforce)
+ panic("Cannot open GeoIP6 AS database, try --update!\n");
+ }
+
+ if (gi6_asname) {
+ GeoIP_set_charset(gi6_asname, GEOIP_CHARSET_UTF8);
+ geoip_db_present |= ASNAMV6;
+ }
+}
+
+static void init_geoip_asname(int enforce)
+{
+ init_geoip_asname_open4(enforce);
+ init_geoip_asname_open6(enforce);
+}
+
+static void destroy_geoip_asname(void)
+{
+ GeoIP_delete(gi4_asname);
+ GeoIP_delete(gi6_asname);
+}
+
+static void init_mirrors(void)
+{
+ int i = 0;
+ FILE *fp;
+ char buff[256];
+
+ fp = fopen("/etc/netsniff-ng/geoip.conf", "r");
+ if (!fp)
+ panic("Cannot open geoip.conf!\n");
+
+ fmemset(buff, 0, sizeof(buff));
+ while (fgets(buff, sizeof(buff), fp) != NULL &&
+ i < array_size(servers)) {
+ buff[sizeof(buff) - 1] = 0;
+ buff[strlen(buff) - 1] = 0;
+
+ if (buff[0] == '#') {
+ fmemset(buff, 0, sizeof(buff));
+ continue;
+ }
+
+ servers[i++] = xstrdup(buff);
+ fmemset(buff, 0, sizeof(buff));
+ }
+
+ fclose(fp);
+}
+
+static void destroy_mirrors(void)
+{
+ int i;
+
+ for (i = 0; i < array_size(servers); ++i)
+ free(servers[i]);
+}
+
+void init_geoip(int enforce)
+{
+ init_geoip_city(enforce);
+ init_geoip_country(enforce);
+ init_geoip_asname(enforce);
+}
+
+void update_geoip(void)
+{
+ int i, j, ret, good = 0;
+
+ init_mirrors();
+
+ for (i = 0; i < array_size(files); ++i) {
+ if (files[i].local && files[i].remote) {
+ good = 0;
+
+ for (j = 0; j < array_size(servers); ++j) {
+ if (servers[j] == NULL)
+ continue;
+ ret = geoip_get_database(servers[j], i);
+ if (!ret) {
+ good = 1;
+ break;
+ }
+ }
+
+ if (good == 0)
+ panic("Cannot get %s from mirrors!\n",
+ files[i].remote);
+ }
+ }
+
+ destroy_mirrors();
+}
+
+void destroy_geoip(void)
+{
+ destroy_geoip_city();
+ destroy_geoip_country();
+ destroy_geoip_asname();
+
+ geoip_db_present = 0;
+}