--- /dev/null
+/* 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;
+}
#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"
{ 0, NULL }
};
-enum geodb_key {
- CONTINENT,
- COUNTRY,
- CITY,
- ISP
-};
-
static const knot_lookup_t geodb_keys[] = {
{ CONTINENT, "continent" },
{ COUNTRY, "country" },
zone_keyset_t keyset;
kdnssec_ctx_t kctx;
-#if HAVE_MAXMINDDB
- MMDB_s db;
-#endif
+ void *geodb;
} geoip_ctx_t;
typedef struct {
{
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);
}
}
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;
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);
}
return KNOTD_IN_STATE_HIT;
}
-#endif
}
return state;
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;