diff --git a/Knot.files b/Knot.files index b00ad8a193368415e138767ae27d59d3feb19edb..590ad1ad6ba5b6911a58170d040d409d2669ccdd 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 0000000000000000000000000000000000000000..a339eebf4db7349ca768eb9ebedb725b4186b859 --- /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 37049ab08c479f97cfacdf4531cf576a20fee2fc..84b572813cedebb9ab1fc761e53b7f68e67f462e 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;