From c84661c854fcb4e5ad3e075dc0c69b5287c097ed Mon Sep 17 00:00:00 2001
From: Mark Karpilovskij <mark.karpilovskij@nic.cz>
Date: Wed, 11 Jul 2018 10:34:09 +0200
Subject: [PATCH] geoip: move mmdb logic to separate file

---
 Knot.files                     |   1 +
 src/knot/modules/geoip/geodb.h | 104 +++++++++++++++++++++++++++++++++
 src/knot/modules/geoip/geoip.c |  64 +++++---------------
 3 files changed, 120 insertions(+), 49 deletions(-)
 create mode 100644 src/knot/modules/geoip/geodb.h

diff --git a/Knot.files b/Knot.files
index b00ad8a193..590ad1ad6b 100644
--- a/Knot.files
+++ b/Knot.files
@@ -144,6 +144,7 @@ src/knot/journal/serialization.h
 src/knot/modules/cookies/cookies.c
 src/knot/modules/dnsproxy/dnsproxy.c
 src/knot/modules/dnstap/dnstap.c
+src/knot/modules/geoip/geodb.h
 src/knot/modules/geoip/geoip.c
 src/knot/modules/noudp/noudp.c
 src/knot/modules/onlinesign/nsec_next.c
diff --git a/src/knot/modules/geoip/geodb.h b/src/knot/modules/geoip/geodb.h
new file mode 100644
index 0000000000..a339eebf4d
--- /dev/null
+++ b/src/knot/modules/geoip/geodb.h
@@ -0,0 +1,104 @@
+/*  Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if HAVE_MAXMINDDB
+#include <maxminddb.h>
+#endif
+
+enum geodb_key {
+	CONTINENT,
+	COUNTRY,
+	CITY,
+	ISP
+};
+
+// MaxMind DB related constants.
+#define MAX_PATH_LEN 4
+#define GEODB_KEYS 4
+
+typedef struct {
+	const char *path[MAX_PATH_LEN];
+}mmdb_path_t;
+
+mmdb_path_t paths[] = {
+	{{"continent", "code", NULL}},
+	{{"country", "iso_code", NULL}},
+	{{"city", "names", "en", NULL}},
+	{{"isp", NULL}}
+};
+
+void *geodb_open(const char *filename)
+{
+#if HAVE_MAXMINDDB
+	MMDB_s *db = calloc(1, sizeof(MMDB_s));
+	if (db == NULL) {
+		return NULL;
+	}
+	int mmdb_error = MMDB_open(filename, MMDB_MODE_MMAP, db);
+	if (mmdb_error != MMDB_SUCCESS) {
+		return NULL;
+	}
+	return (void *)db;
+#endif
+	return NULL;
+}
+
+void geodb_close(void *geodb)
+{
+#if HAVE_MAXMINDDB
+	MMDB_s *db = (MMDB_s *)geodb;
+	MMDB_close(db);
+#endif
+}
+
+int geodb_query(void *geodb, struct sockaddr *remote,
+                enum geodb_key *keys, uint16_t keyc,
+                char **geodata, uint32_t *geodata_len, uint16_t *netmask)
+{
+#if HAVE_MAXMINDDB
+	MMDB_s *db = (MMDB_s *)geodb;
+	int mmdb_error = 0;
+	MMDB_lookup_result_s res;
+	res = MMDB_lookup_sockaddr(db, remote, &mmdb_error);
+	if (mmdb_error != MMDB_SUCCESS || !res.found_entry) {
+		return -1;
+	}
+
+	// Save netmask.
+	*netmask = res.netmask;
+
+	MMDB_entry_data_s entry;
+	// Set the remote's geo information.
+	for (uint16_t i = 0; i < keyc; i++) {
+		enum geodb_key key = keys[i];
+		geodata[key] = NULL;
+		mmdb_error = MMDB_aget_value(&res.entry, &entry, paths[key].path);
+		if (mmdb_error != MMDB_SUCCESS &&
+			mmdb_error != MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR) {
+			return -1;
+		}
+		if (mmdb_error == MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR ||
+			!entry.has_data || entry.type != MMDB_DATA_TYPE_UTF8_STRING) {
+			continue;
+		}
+		geodata[key] = (char *)entry.utf8_string;
+		geodata_len[key] = entry.data_size;
+	}
+
+	return 0;
+#endif
+	return -1;
+}
diff --git a/src/knot/modules/geoip/geoip.c b/src/knot/modules/geoip/geoip.c
index 37049ab08c..84b572813c 100644
--- a/src/knot/modules/geoip/geoip.c
+++ b/src/knot/modules/geoip/geoip.c
@@ -24,6 +24,7 @@
 #include "contrib/openbsd/strlcpy.h"
 #include "contrib/string.h"
 #include "libzscanner/scanner.h"
+#include "geodb.h"
 
 // Next dependecies force static module!
 #include "knot/dnssec/rrset-sign.h"
@@ -51,13 +52,6 @@ static const knot_lookup_t modes[] = {
 	{ 0, NULL }
 };
 
-enum geodb_key {
-	CONTINENT,
-	COUNTRY,
-	CITY,
-	ISP
-};
-
 static const knot_lookup_t geodb_keys[] = {
 	{ CONTINENT, "continent" },
 	{ COUNTRY, "country" },
@@ -104,9 +98,7 @@ typedef struct {
 	zone_keyset_t keyset;
 	kdnssec_ctx_t kctx;
 
-#if HAVE_MAXMINDDB
-	MMDB_s db;
-#endif
+	void *geodb;
 } geoip_ctx_t;
 
 typedef struct {
@@ -534,9 +526,8 @@ void clear_geo_ctx(geoip_ctx_t *ctx)
 {
 	kdnssec_ctx_deinit(&ctx->kctx);
 	free_zone_keys(&ctx->keyset);
-#if HAVE_MAXMINDDB
-	MMDB_close(&ctx->db);
-#endif
+	geodb_close(ctx->geodb);
+	free(ctx->geodb);
 	clear_geo_trie(ctx->geo_trie);
 	trie_free(ctx->geo_trie);
 }
@@ -603,45 +594,21 @@ static knotd_in_state_t geoip_process(knotd_in_state_t state, knot_pkt_t *pkt,
 	}
 
 	if (ctx->mode == MODE_GEODB) {
-#if HAVE_MAXMINDDB
-		int mmdb_error = 0;
-		MMDB_lookup_result_s res;
-		res = MMDB_lookup_sockaddr(&ctx->db, (struct sockaddr *)remote, &mmdb_error);
-		if (mmdb_error != MMDB_SUCCESS) {
-			knotd_mod_log(mod, LOG_ERR, "a lookup error in MMDB occured");
-			return state;
-		}
-		if (!res.found_entry) {
+		geo_view_t remote_view;
+		uint16_t netmask;
+		int ret = geodb_query(ctx->geodb, (struct sockaddr *)remote, ctx->keys, ctx->keyc,
+		                      remote_view.geodata, remote_view.geodata_len, &netmask);
+		if (ret != 0) {
 			return state;
 		}
 
-		geo_view_t client_view;
-		MMDB_entry_data_s entry;
-		// Set the remote's geo information.
-		for (uint16_t i = 0; i < ctx->keyc; i++) {
-			enum geodb_key key = ctx->keys[i];
-			client_view.geodata[key] = NULL;
-			mmdb_error = MMDB_aget_value(&res.entry, &entry, paths[key].path);
-			if (mmdb_error != MMDB_SUCCESS &&
-				mmdb_error != MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR) {
-				knotd_mod_log(mod, LOG_ERR, "an entry error in MMDB occured (%s)", geodb_keys[key].name);
-				return state;
-			}
-			if (mmdb_error == MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR ||
-				!entry.has_data || entry.type != MMDB_DATA_TYPE_UTF8_STRING) {
-				continue;
-			}
-			client_view.geodata[key] = (char *)entry.utf8_string;
-			client_view.geodata_len[key] = entry.data_size;
-		}
-
 		uint8_t best_depth = 0;
 		knot_rrset_t *rr = NULL;
 		knot_rrset_t *rrsig = NULL;
 		// Check whether the remote falls into any geo location.
 		for (int i = 0; i < data->count; i++) {
 			geo_view_t *view = &data->views[i];
-			if (addr_in_geo(ctx, view, &client_view) && view->geodepth >= best_depth) {
+			if (addr_in_geo(ctx, view, &remote_view) && view->geodepth >= best_depth) {
 				for (int j = 0; j < view->count; j++) {
 					if (view->rrsets[j].type == qtype) {
 						best_depth = view->geodepth;
@@ -657,7 +624,7 @@ static knotd_in_state_t geoip_process(knotd_in_state_t state, knot_pkt_t *pkt,
 		if (rr != NULL) {
 			// Update ECS if used.
 			if (qdata->ecs != NULL) {
-				qdata->ecs->scope_len = res.netmask;
+				qdata->ecs->scope_len = netmask;
 			}
 
 			knot_pkt_put(pkt, KNOT_COMPR_HINT_QNAME, rr, 0);
@@ -666,7 +633,6 @@ static knotd_in_state_t geoip_process(knotd_in_state_t state, knot_pkt_t *pkt,
 			}
 			return KNOTD_IN_STATE_HIT;
 		}
-#endif
 	}
 
 	return state;
@@ -691,14 +657,14 @@ int geoip_load(knotd_mod_t *mod)
 	int ret;
 
 	if (ctx->mode == MODE_GEODB) {
-#if HAVE_MAXMINDDB // Initialize geodb if configured.
+		// Initialize geodb.
 		conf = knotd_conf_mod(mod, MOD_GEODB_FILE);
-		ret = MMDB_open(conf.single.string, MMDB_MODE_MMAP, &ctx->db);
-		if (ret != MMDB_SUCCESS) {
+		ctx->geodb = geodb_open(conf.single.string);
+		if (ctx->geodb == NULL) {
 			knotd_mod_log(mod, LOG_ERR, "failed to open Geo DB");
 			return KNOT_EINVAL;
 		}
-#endif
+
 		// Load configured geodb keys.
 		conf = knotd_conf_mod(mod, MOD_GEODB_KEY);
 		ctx->keyc = conf.count;
-- 
GitLab