/*! \file */
-#include <dns/geoip.h>
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+/*
+ * This file is only built and linked if GeoIP2 has been configured.
+ */
+#include <maxminddb.h>
+#include <isc/mem.h>
+#include <isc/once.h>
+#include <isc/sockaddr.h>
+#include <isc/string.h>
+#include <isc/thread.h>
#include <isc/util.h>
+#include <dns/acl.h>
+#include <dns/geoip.h>
+
+#include <math.h>
+#ifndef WIN32
+#include <netinet/in.h>
+#else
+#ifndef _WINSOCKAPI_
+#define _WINSOCKAPI_ /* Prevent inclusion of winsock.h in windows.h */
+#endif
+#include <winsock2.h>
+#endif /* WIN32 */
+#include <dns/log.h>
+
+/*
+ * This structure preserves state from the previous GeoIP lookup,
+ * so that successive lookups for the same data from the same IP
+ * address will not require repeated database lookups.
+ * This should improve performance somewhat.
+ *
+ * For all lookups we preserve pointers to the MMDB_lookup_result_s
+ * and MMDB_entry_s structures, a pointer to the database from which
+ * the lookup was answered, and a copy of the request address.
+ *
+ * If the next geoip ACL lookup is for the same database and from the
+ * same address, we can reuse the MMDB entry without repeating the lookup.
+ * This is for the case when a single query has to process multiple
+ * geoip ACLs: for example, when there are multiple views with
+ * match-clients statements that search for different countries.
+ *
+ * (XXX: Currently the persistent state is stored in thread specific
+ * memory, but it could more simply be stored in the client object.
+ * Also multiple entries could be stored in case the ACLs require
+ * searching in more than one GeoIP database.)
+ */
+
+typedef struct geoip_state {
+ isc_mem_t *mctx;
+ uint16_t subtype;
+ const MMDB_s *db;
+ isc_netaddr_t addr;
+ MMDB_lookup_result_s mmresult;
+ MMDB_entry_s entry;
+} geoip_state_t;
+
+static isc_mutex_t key_mutex;
+static bool state_key_initialized = false;
+static isc_thread_key_t state_key;
+static isc_once_t mutex_once = ISC_ONCE_INIT;
+static isc_mem_t *state_mctx = NULL;
+
+static void
+key_mutex_init(void) {
+ isc_mutex_init(&key_mutex);
+}
+
+static void
+free_state(void *arg) {
+ geoip_state_t *state = arg;
+ if (state != NULL) {
+ isc_mem_putanddetach(&state->mctx,
+ state, sizeof(geoip_state_t));
+ }
+ isc_thread_key_setspecific(state_key, NULL);
+}
+
+static isc_result_t
+state_key_init(void) {
+ isc_result_t result;
+
+ result = isc_once_do(&mutex_once, key_mutex_init);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ if (!state_key_initialized) {
+ LOCK(&key_mutex);
+ if (!state_key_initialized) {
+ int ret;
+
+ if (state_mctx == NULL) {
+ result = isc_mem_create(0, 0, &state_mctx);
+ }
+ if (result != ISC_R_SUCCESS) {
+ goto unlock;
+ }
+ isc_mem_setname(state_mctx, "geoip_state", NULL);
+ isc_mem_setdestroycheck(state_mctx, false);
+
+ ret = isc_thread_key_create(&state_key, free_state);
+ if (ret == 0) {
+ state_key_initialized = true;
+ } else {
+ result = ISC_R_FAILURE;
+ }
+ }
+ unlock:
+ UNLOCK(&key_mutex);
+ }
+
+ return (result);
+}
+
+static isc_result_t
+set_state(const MMDB_s *db, const isc_netaddr_t *addr,
+ MMDB_lookup_result_s mmresult, MMDB_entry_s entry,
+ geoip_state_t **statep)
+{
+ geoip_state_t *state = NULL;
+ isc_result_t result;
+
+ result = state_key_init();
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ state = (geoip_state_t *) isc_thread_key_getspecific(state_key);
+ if (state == NULL) {
+ state = (geoip_state_t *) isc_mem_get(state_mctx,
+ sizeof(geoip_state_t));
+ memset(state, 0, sizeof(*state));
+
+ result = isc_thread_key_setspecific(state_key, state);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(state_mctx, state, sizeof(geoip_state_t));
+ return (result);
+ }
+
+ isc_mem_attach(state_mctx, &state->mctx);
+ }
+
+ state->db = db;
+ state->addr = *addr;
+ state->mmresult = mmresult;
+ state->entry = entry;
+
+ if (statep != NULL) {
+ *statep = state;
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static geoip_state_t *
+get_entry_for(MMDB_s * const db, const isc_netaddr_t *addr) {
+ isc_result_t result;
+ isc_sockaddr_t sa;
+ geoip_state_t *state;
+ MMDB_lookup_result_s match;
+ int err;
+
+ result = state_key_init();
+ if (result != ISC_R_SUCCESS) {
+ return (NULL);
+ }
+
+ state = (geoip_state_t *) isc_thread_key_getspecific(state_key);
+ if (state != NULL) {
+ if (db == state->db && isc_netaddr_equal(addr, &state->addr)) {
+ return (state);
+ }
+ }
+
+ isc_sockaddr_fromnetaddr(&sa, addr, 0);
+ match = MMDB_lookup_sockaddr(db, &sa.type.sa, &err);
+ if (err != MMDB_SUCCESS) {
+ return (NULL);
+ }
+
+ result = set_state(db, addr, match, match.entry, &state);
+ if (result != ISC_R_SUCCESS) {
+ return (NULL);
+ }
+
+ return (state);
+}
+
+static dns_geoip_subtype_t
+fix_subtype(const dns_geoip_databases_t *geoip, dns_geoip_subtype_t subtype) {
+ dns_geoip_subtype_t ret = subtype;
+
+ switch (subtype) {
+ case dns_geoip_countrycode:
+ if (geoip->city != NULL) {
+ ret = dns_geoip_city_countrycode;
+ } else if (geoip->country != NULL) {
+ ret = dns_geoip_country_code;
+ }
+ break;
+ case dns_geoip_countryname:
+ if (geoip->city != NULL) {
+ ret = dns_geoip_city_countryname;
+ } else if (geoip->country != NULL) {
+ ret = dns_geoip_country_name;
+ }
+ break;
+ case dns_geoip_continentcode:
+ if (geoip->city != NULL) {
+ ret = dns_geoip_city_continentcode;
+ } else if (geoip->country != NULL) {
+ ret = dns_geoip_country_continentcode;
+ }
+ break;
+ case dns_geoip_continent:
+ if (geoip->city != NULL) {
+ ret = dns_geoip_city_continent;
+ } else if (geoip->country != NULL) {
+ ret = dns_geoip_country_continent;
+ }
+ break;
+ case dns_geoip_region:
+ if (geoip->city != NULL) {
+ ret = dns_geoip_city_region;
+ }
+ break;
+ case dns_geoip_regionname:
+ if (geoip->city != NULL) {
+ ret = dns_geoip_city_regionname;
+ }
+ default:
+ break;
+ }
+
+ return (ret);
+}
+
+static MMDB_s *
+geoip2_database(const dns_geoip_databases_t *geoip,
+ dns_geoip_subtype_t subtype)
+{
+ switch (subtype) {
+ case dns_geoip_country_code:
+ case dns_geoip_country_name:
+ case dns_geoip_country_continentcode:
+ case dns_geoip_country_continent:
+ return (geoip->country);
+
+ case dns_geoip_city_countrycode:
+ case dns_geoip_city_countryname:
+ case dns_geoip_city_continentcode:
+ case dns_geoip_city_continent:
+ case dns_geoip_city_region:
+ case dns_geoip_city_regionname:
+ case dns_geoip_city_name:
+ case dns_geoip_city_postalcode:
+ case dns_geoip_city_timezonecode:
+ case dns_geoip_city_metrocode:
+ case dns_geoip_city_areacode:
+ return (geoip->city);
+
+ case dns_geoip_isp_name:
+ return (geoip->isp);
+
+ case dns_geoip_as_asnum:
+ case dns_geoip_org_name:
+ return (geoip->as);
+
+ case dns_geoip_domain_name:
+ return (geoip->domain);
+
+ default:
+ /*
+ * All other subtypes are unavailable in GeoIP2.
+ */
+ return (NULL);
+ }
+}
+
+static bool
+match_string(MMDB_entry_data_s *value, const char *str) {
+ REQUIRE(str != NULL);
+
+ if (value == NULL || !value->has_data ||
+ value->type != MMDB_DATA_TYPE_UTF8_STRING ||
+ value->utf8_string == NULL)
+ {
+ return (false);
+ }
+
+ return (strncasecmp(value->utf8_string, str, value->data_size) == 0);
+}
+
+static bool
+match_int(MMDB_entry_data_s *value, const uint32_t ui32) {
+ if (value == NULL || !value->has_data ||
+ (value->type != MMDB_DATA_TYPE_UINT32 &&
+ value->type != MMDB_DATA_TYPE_UINT16))
+ {
+ return (false);
+ }
+
+ return (value->uint32 == ui32);
+}
+
bool
-dns_geoip_match(const isc_netaddr_t *reqaddr,
+dns_geoip_match(const isc_netaddr_t *reqaddr, uint8_t *scope,
const dns_geoip_databases_t *geoip,
const dns_geoip_elem_t *elt)
{
- UNUSED(reqaddr);
- UNUSED(geoip);
- UNUSED(elt);
+ MMDB_s *db = NULL;
+ MMDB_entry_data_s value;
+ geoip_state_t *state = NULL;
+ dns_geoip_subtype_t subtype;
+ const char *s = NULL;
+ int ret, i;
+
+ REQUIRE(reqaddr != NULL);
+ REQUIRE(elt != NULL);
+ REQUIRE(geoip != NULL);
+
+ subtype = fix_subtype(geoip, elt->subtype);
+ db = geoip2_database(geoip, subtype);
+ if (db == NULL) {
+ return (false);
+ }
+
+ state = get_entry_for(db, reqaddr);
+ if (state == NULL) {
+ return (false);
+ }
+
+ switch (subtype) {
+ case dns_geoip_country_code:
+ case dns_geoip_city_countrycode:
+ ret = MMDB_get_value(&state->entry, &value,
+ "country", "iso_code", NULL);
+ if (ret == MMDB_SUCCESS) {
+ if (scope != NULL) {
+ *scope = state->mmresult.netmask;
+#define isc_netaddr_pf(x) (x)->family
+ if (isc_netaddr_pf(reqaddr) == AF_INET) {
+ *scope -= 96;
+ }
+ }
+ return (match_string(&value, elt->as_string));
+ }
+ break;
+
+ case dns_geoip_country_name:
+ case dns_geoip_city_countryname:
+ ret = MMDB_get_value(&state->entry, &value,
+ "country", "names", "en", NULL);
+ if (ret == MMDB_SUCCESS) {
+ if (scope != NULL) {
+ *scope = state->mmresult.netmask;
+ if (isc_netaddr_pf(reqaddr) == AF_INET) {
+ *scope -= 96;
+ }
+ }
+ return (match_string(&value, elt->as_string));
+ }
+ break;
+
+ case dns_geoip_country_continentcode:
+ case dns_geoip_city_continentcode:
+ ret = MMDB_get_value(&state->entry, &value,
+ "continent", "code", NULL);
+ if (ret == MMDB_SUCCESS) {
+ if (scope != NULL) {
+ *scope = state->mmresult.netmask;
+ if (isc_netaddr_pf(reqaddr) == AF_INET) {
+ *scope -= 96;
+ }
+ }
+ return (match_string(&value, elt->as_string));
+ }
+ break;
+
+ case dns_geoip_country_continent:
+ case dns_geoip_city_continent:
+ ret = MMDB_get_value(&state->entry, &value,
+ "continent", "names", "en", NULL);
+ if (ret == MMDB_SUCCESS) {
+ if (scope != NULL) {
+ *scope = state->mmresult.netmask;
+ if (isc_netaddr_pf(reqaddr) == AF_INET) {
+ *scope -= 96;
+ }
+ }
+ return (match_string(&value, elt->as_string));
+ }
+ break;
+
+ case dns_geoip_region:
+ case dns_geoip_city_region:
+ ret = MMDB_get_value(&state->entry, &value,
+ "subdivisions", "0", "iso_code", NULL);
+ if (ret == MMDB_SUCCESS) {
+ if (scope != NULL) {
+ *scope = state->mmresult.netmask;
+ if (isc_netaddr_pf(reqaddr) == AF_INET) {
+ *scope -= 96;
+ }
+ }
+ return (match_string(&value, elt->as_string));
+ }
+ break;
+
+ case dns_geoip_regionname:
+ case dns_geoip_city_regionname:
+ ret = MMDB_get_value(&state->entry, &value,
+ "subdivisions", "0", "names", "en", NULL);
+ if (ret == MMDB_SUCCESS) {
+ if (scope != NULL) {
+ *scope = state->mmresult.netmask;
+ if (isc_netaddr_pf(reqaddr) == AF_INET) {
+ *scope -= 96;
+ }
+ }
+ return (match_string(&value, elt->as_string));
+ }
+ break;
+
+ case dns_geoip_city_name:
+ ret = MMDB_get_value(&state->entry, &value,
+ "city", "names", "en", NULL);
+ if (ret == MMDB_SUCCESS) {
+ if (scope != NULL) {
+ *scope = state->mmresult.netmask;
+ if (isc_netaddr_pf(reqaddr) == AF_INET) {
+ *scope -= 96;
+ }
+ }
+ return (match_string(&value, elt->as_string));
+ }
+ break;
+
+ case dns_geoip_city_postalcode:
+ ret = MMDB_get_value(&state->entry, &value,
+ "postal", "code", NULL);
+ if (ret == MMDB_SUCCESS) {
+ if (scope != NULL) {
+ *scope = state->mmresult.netmask;
+ if (isc_netaddr_pf(reqaddr) == AF_INET) {
+ *scope -= 96;
+ }
+ }
+ return (match_string(&value, elt->as_string));
+ }
+ break;
+
+ case dns_geoip_city_timezonecode:
+ ret = MMDB_get_value(&state->entry, &value,
+ "location", "time_zone", NULL);
+ if (ret == MMDB_SUCCESS) {
+ if (scope != NULL) {
+ *scope = state->mmresult.netmask;
+ if (isc_netaddr_pf(reqaddr) == AF_INET) {
+ *scope -= 96;
+ }
+ }
+ return (match_string(&value, elt->as_string));
+ }
+ break;
+
+
+ case dns_geoip_city_metrocode:
+ ret = MMDB_get_value(&state->entry, &value,
+ "location", "metro_code", NULL);
+ if (ret == MMDB_SUCCESS) {
+ if (scope != NULL) {
+ *scope = state->mmresult.netmask;
+ if (isc_netaddr_pf(reqaddr) == AF_INET) {
+ *scope -= 96;
+ }
+ }
+ return (match_string(&value, elt->as_string));
+ }
+ break;
+
+ case dns_geoip_isp_name:
+ ret = MMDB_get_value(&state->entry, &value, "isp", NULL);
+ if (ret == MMDB_SUCCESS) {
+ if (scope != NULL) {
+ *scope = state->mmresult.netmask;
+ if (isc_netaddr_pf(reqaddr) == AF_INET) {
+ *scope -= 96;
+ }
+ }
+ return (match_string(&value, elt->as_string));
+ }
+ break;
+
+ case dns_geoip_as_asnum:
+ INSIST(elt->as_string != NULL);
+
+ ret = MMDB_get_value(&state->entry, &value,
+ "autonomous_system_number", NULL);
+ if (ret == MMDB_SUCCESS) {
+ if (scope != NULL) {
+ *scope = state->mmresult.netmask;
+ if (isc_netaddr_pf(reqaddr) == AF_INET) {
+ *scope -= 96;
+ }
+ }
+
+ s = elt->as_string;
+ if (strncasecmp(s, "AS", 2) == 0) {
+ s += 2;
+ }
+
+ i = strtol(s, NULL, 10);
+ return (match_int(&value, i));
+ }
+ break;
+
+ case dns_geoip_org_name:
+ ret = MMDB_get_value(&state->entry, &value,
+ "autonomous_system_organization", NULL);
+ if (ret == MMDB_SUCCESS) {
+ if (scope != NULL) {
+ *scope = state->mmresult.netmask;
+ if (isc_netaddr_pf(reqaddr) == AF_INET) {
+ *scope -= 96;
+ }
+ }
+ return (match_string(&value, elt->as_string));
+ }
+ break;
+
+ case dns_geoip_domain_name:
+ ret = MMDB_get_value(&state->entry, &value, "domain", NULL);
+ if (ret == MMDB_SUCCESS) {
+ if (scope != NULL) {
+ *scope = state->mmresult.netmask;
+ if (isc_netaddr_pf(reqaddr) == AF_INET) {
+ *scope -= 96;
+ }
+ }
+ return (match_string(&value, elt->as_string));
+ }
+ break;
+
+ default:
+ /*
+ * For any other subtype, we assume the database was
+ * unavailable and return false.
+ */
+ return (false);
+ }
+ /*
+ * No database matched: return false.
+ */
return (false);
}
void
dns_geoip_shutdown(void) {
- return;
+ if (state_mctx != NULL) {
+ isc_mem_detach(&state_mctx);
+ }
}
#include <inttypes.h>
#include <stdbool.h>
+#include <stdlib.h>
#include <isc/mem.h>
#include <isc/print.h>
#include <dns/fixedname.h>
#include <dns/log.h>
-#ifdef HAVE_GEOIP
-#include <stdlib.h>
-#include <math.h>
-#endif /* HAVE_GEOIP */
-
#define LOOP_MAGIC ISC_MAGIC('L','O','O','P')
+#if defined(HAVE_GEOIP2)
+static const char *geoip_dbnames[] = {
+ "country",
+ "city",
+ "asnum",
+ "isp",
+ "domain",
+ NULL,
+};
+#elif defined(HAVE_GEOIP)
+static const char *geoip_dbnames[] = {
+ "country",
+ "city",
+ "region",
+ "asnum",
+ "isp",
+ "domain",
+ "netspeed",
+ "org",
+ NULL,
+};
+#endif /* HAVE_GEOIP */
+
isc_result_t
cfg_aclconfctx_create(isc_mem_t *mctx, cfg_aclconfctx_t **ret) {
isc_result_t result;
return (ISC_R_SUCCESS);
}
-#if defined(HAVE_GEOIP)
+#if defined(HAVE_GEOIP2)
static dns_geoip_subtype_t
get_subtype(const cfg_obj_t *obj, isc_log_t *lctx,
dns_geoip_subtype_t subtype, const char *dbname)
case dns_geoip_countrycode:
if (strcasecmp(dbname, "city") == 0) {
return (dns_geoip_city_countrycode);
- } else if (strcasecmp(dbname, "region") == 0) {
- return (dns_geoip_region_countrycode);
} else if (strcasecmp(dbname, "country") == 0) {
return (dns_geoip_country_code);
}
cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
- "invalid GeoIP DB specified for "
+ "invalid database specified for "
"country search: ignored");
return (subtype);
- case dns_geoip_countrycode3:
+ case dns_geoip_countryname:
if (strcasecmp(dbname, "city") == 0) {
- return (dns_geoip_city_countrycode3);
+ return (dns_geoip_city_countryname);
} else if (strcasecmp(dbname, "country") == 0) {
- return (dns_geoip_country_code3);
+ return (dns_geoip_country_name);
}
cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
- "invalid GeoIP DB specified for "
+ "invalid database specified for "
"country search: ignored");
return (subtype);
- case dns_geoip_countryname:
+ case dns_geoip_continentcode:
if (strcasecmp(dbname, "city") == 0) {
- return (dns_geoip_city_countryname);
+ return (dns_geoip_city_continentcode);
} else if (strcasecmp(dbname, "country") == 0) {
- return (dns_geoip_country_name);
+ return (dns_geoip_country_continentcode);
}
cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
- "invalid GeoIP DB specified for "
- "country search: ignored");
+ "invalid database specified for "
+ "continent search: ignored");
+ return (subtype);
+ case dns_geoip_continent:
+ if (strcasecmp(dbname, "city") == 0) {
+ return (dns_geoip_city_continent);
+ } else if (strcasecmp(dbname, "country") == 0) {
+ return (dns_geoip_country_continent);
+ }
+ cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
+ "invalid database specified for "
+ "continent search: ignored");
return (subtype);
case dns_geoip_region:
if (strcasecmp(dbname, "city") == 0) {
return (dns_geoip_city_region);
- } else if (strcasecmp(dbname, "region") == 0) {
- return (dns_geoip_region_code);
}
cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
- "invalid GeoIP DB specified for "
- "region search: ignored");
+ "invalid database specified for "
+ "region/subdivision search: ignored");
return (subtype);
case dns_geoip_regionname:
if (strcasecmp(dbname, "city") == 0) {
- return (dns_geoip_city_region);
- } else if (strcasecmp(dbname, "region") == 0) {
- return (dns_geoip_region_name);
+ return (dns_geoip_city_regionname);
}
cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
- "invalid GeoIP DB specified for "
- "region search: ignored");
+ "invalid database specified for "
+ "region/subdivision search: ignored");
return (subtype);
/*
case dns_geoip_city_postalcode:
case dns_geoip_city_metrocode:
case dns_geoip_city_areacode:
- case dns_geoip_city_continentcode:
case dns_geoip_city_timezonecode:
if (strcasecmp(dbname, "city") != 0) {
cfg_obj_log(obj, lctx, ISC_LOG_WARNING,
- "invalid GeoIP DB specified for "
+ "invalid database specified for "
"a 'city'-only search type: ignoring");
}
return (subtype);
case dns_geoip_isp_name:
if (strcasecmp(dbname, "isp") != 0) {
cfg_obj_log(obj, lctx, ISC_LOG_WARNING,
- "invalid GeoIP DB specified for "
+ "invalid database specified for "
"an 'isp' search: ignoring");
}
return (subtype);
case dns_geoip_org_name:
if (strcasecmp(dbname, "org") != 0) {
cfg_obj_log(obj, lctx, ISC_LOG_WARNING,
- "invalid GeoIP DB specified for "
+ "invalid database specified for "
"an 'org' search: ignoring");
}
return (subtype);
case dns_geoip_as_asnum:
if (strcasecmp(dbname, "asnum") != 0) {
cfg_obj_log(obj, lctx, ISC_LOG_WARNING,
- "invalid GeoIP DB specified for "
+ "invalid database specified for "
"an 'asnum' search: ignoring");
}
return (subtype);
case dns_geoip_domain_name:
if (strcasecmp(dbname, "domain") != 0) {
cfg_obj_log(obj, lctx, ISC_LOG_WARNING,
- "invalid GeoIP DB specified for "
+ "invalid database specified for "
"a 'domain' search: ignoring");
}
return (subtype);
case dns_geoip_netspeed_id:
if (strcasecmp(dbname, "netspeed") != 0) {
cfg_obj_log(obj, lctx, ISC_LOG_WARNING,
- "invalid GeoIP DB specified for "
+ "invalid database specified for "
"a 'netspeed' search: ignoring");
}
return (subtype);
return (true);
}
+ switch (elt->geoip_elem.subtype) {
+ case dns_geoip_countrycode:
+ case dns_geoip_countryname:
+ case dns_geoip_continentcode:
+ case dns_geoip_continent:
+ if (ctx->geoip->country != NULL ||
+ ctx->geoip->city != NULL)
+ {
+ return (true);
+ }
+ break;
+ case dns_geoip_country_code:
+ case dns_geoip_country_name:
+ case dns_geoip_country_continentcode:
+ case dns_geoip_country_continent:
+ if (ctx->geoip->country != NULL) {
+ return (true);
+ }
+ /* city db can answer these too, so: */
+ /* FALLTHROUGH */
+ case dns_geoip_region:
+ case dns_geoip_regionname:
+ case dns_geoip_city_countrycode:
+ case dns_geoip_city_countryname:
+ case dns_geoip_city_region:
+ case dns_geoip_city_regionname:
+ case dns_geoip_city_name:
+ case dns_geoip_city_postalcode:
+ case dns_geoip_city_metrocode:
+ case dns_geoip_city_areacode:
+ case dns_geoip_city_continentcode:
+ case dns_geoip_city_continent:
+ case dns_geoip_city_timezonecode:
+ if (ctx->geoip->city != NULL) {
+ return (true);
+ }
+ break;
+ case dns_geoip_isp_name:
+ if (ctx->geoip->isp != NULL) {
+ return (true);
+ }
+ break;
+ case dns_geoip_as_asnum:
+ case dns_geoip_org_name:
+ if (ctx->geoip->as != NULL) {
+ return (true);
+ }
+ break;
+ case dns_geoip_domain_name:
+ if (ctx->geoip->domain != NULL) {
+ return (true);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return (false);
+}
+
+static isc_result_t
+parse_geoip_element(const cfg_obj_t *obj, isc_log_t *lctx,
+ cfg_aclconfctx_t *ctx, dns_aclelement_t *dep)
+{
+ const cfg_obj_t *ge;
+ const char *dbname = NULL;
+ const char *stype = NULL, *search = NULL;
+ dns_geoip_subtype_t subtype;
+ dns_aclelement_t de;
+ size_t len;
+
+ REQUIRE(dep != NULL);
+
+ de = *dep;
+
+ ge = cfg_tuple_get(obj, "db");
+ if (!cfg_obj_isvoid(ge)) {
+ int i;
+
+ dbname = cfg_obj_asstring(ge);
+
+ for (i = 0; geoip_dbnames[i] != NULL; i++) {
+ if (strcasecmp(dbname, geoip_dbnames[i]) == 0) {
+ break;
+ }
+ }
+ if (geoip_dbnames[i] == NULL) {
+ cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
+ "database '%s' is not defined for GeoIP2",
+ dbname);
+ return (ISC_R_UNEXPECTED);
+ }
+ }
+
+ stype = cfg_obj_asstring(cfg_tuple_get(obj, "subtype"));
+ search = cfg_obj_asstring(cfg_tuple_get(obj, "search"));
+ len = strlen(search);
+
+ if (len == 0) {
+ cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
+ "zero-length geoip search field");
+ return (ISC_R_FAILURE);
+ }
+
+ if (strcasecmp(stype, "country") == 0 && len == 2) {
+ /* Two-letter country code */
+ subtype = dns_geoip_countrycode;
+ strlcpy(de.geoip_elem.as_string, search,
+ sizeof(de.geoip_elem.as_string));
+ } else if (strcasecmp(stype, "country") == 0 && len == 3) {
+ /* Three-letter country code */
+ cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
+ "three-letter country codes are unavailable "
+ "in GeoIP2 databases");
+ return (ISC_R_FAILURE);
+ } else if (strcasecmp(stype, "country") == 0) {
+ /* Country name */
+ subtype = dns_geoip_countryname;
+ strlcpy(de.geoip_elem.as_string, search,
+ sizeof(de.geoip_elem.as_string));
+ } else if (strcasecmp(stype, "continent") == 0 && len == 2) {
+ /* Two-letter continent code */
+ subtype = dns_geoip_continentcode;
+ strlcpy(de.geoip_elem.as_string, search,
+ sizeof(de.geoip_elem.as_string));
+ } else if (strcasecmp(stype, "continent") == 0) {
+ subtype = dns_geoip_continent;
+ strlcpy(de.geoip_elem.as_string, search,
+ sizeof(de.geoip_elem.as_string));
+ } else if ((strcasecmp(stype, "region") == 0 ||
+ strcasecmp(stype, "subdivision") == 0) && len == 2)
+ {
+ /* Two-letter region code */
+ subtype = dns_geoip_region;
+ strlcpy(de.geoip_elem.as_string, search,
+ sizeof(de.geoip_elem.as_string));
+ } else if (strcasecmp(stype, "region") == 0 ||
+ strcasecmp(stype, "subdivision") == 0)
+ {
+ /* Region name */
+ subtype = dns_geoip_regionname;
+ strlcpy(de.geoip_elem.as_string, search,
+ sizeof(de.geoip_elem.as_string));
+ } else if (strcasecmp(stype, "city") == 0) {
+ /* City name */
+ subtype = dns_geoip_city_name;
+ strlcpy(de.geoip_elem.as_string, search,
+ sizeof(de.geoip_elem.as_string));
+ } else if (strcasecmp(stype, "postal") == 0 ||
+ strcasecmp(stype, "postalcode") == 0)
+ {
+ if (len < 7) {
+ subtype = dns_geoip_city_postalcode;
+ strlcpy(de.geoip_elem.as_string, search,
+ sizeof(de.geoip_elem.as_string));
+ } else {
+ cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
+ "geoiop postal code (%s) too long",
+ search);
+ return (ISC_R_FAILURE);
+ }
+ } else if (strcasecmp(stype, "metro") == 0 ||
+ strcasecmp(stype, "metrocode") == 0)
+ {
+ subtype = dns_geoip_city_metrocode;
+ de.geoip_elem.as_int = atoi(search);
+ } else if (strcasecmp(stype, "tz") == 0 ||
+ strcasecmp(stype, "timezone") == 0)
+ {
+ subtype = dns_geoip_city_timezonecode;
+ strlcpy(de.geoip_elem.as_string, search,
+ sizeof(de.geoip_elem.as_string));
+ } else if (strcasecmp(stype, "isp") == 0) {
+ subtype = dns_geoip_isp_name;
+ strlcpy(de.geoip_elem.as_string, search,
+ sizeof(de.geoip_elem.as_string));
+ } else if (strcasecmp(stype, "asnum") == 0) {
+ subtype = dns_geoip_as_asnum;
+ strlcpy(de.geoip_elem.as_string, search,
+ sizeof(de.geoip_elem.as_string));
+ } else if (strcasecmp(stype, "org") == 0) {
+ subtype = dns_geoip_org_name;
+ strlcpy(de.geoip_elem.as_string, search,
+ sizeof(de.geoip_elem.as_string));
+ } else if (strcasecmp(stype, "domain") == 0) {
+ subtype = dns_geoip_domain_name;
+ strlcpy(de.geoip_elem.as_string, search,
+ sizeof(de.geoip_elem.as_string));
+ } else {
+ cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
+ "type '%s' is unavailable "
+ "in GeoIP2 databases", stype);
+ return (ISC_R_FAILURE);
+ }
+
+ de.geoip_elem.subtype = get_subtype(obj, lctx, subtype, dbname);
+
+ if (! geoip_can_answer(&de, ctx)) {
+ cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
+ "no GeoIP2 database installed which can answer "
+ "queries of type '%s'", stype);
+ return (ISC_R_FAILURE);
+ }
+
+ *dep = de;
+
+ return (ISC_R_SUCCESS);
+}
+#elif defined(HAVE_GEOIP)
+static dns_geoip_subtype_t
+get_subtype(const cfg_obj_t *obj, isc_log_t *lctx,
+ dns_geoip_subtype_t subtype, const char *dbname)
+{
+ if (dbname == NULL)
+ return (subtype);
+
+ switch (subtype) {
+ case dns_geoip_countrycode:
+ if (strcasecmp(dbname, "city") == 0)
+ return (dns_geoip_city_countrycode);
+ else if (strcasecmp(dbname, "region") == 0)
+ return (dns_geoip_region_countrycode);
+ else if (strcasecmp(dbname, "country") == 0)
+ return (dns_geoip_country_code);
+ cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
+ "invalid GeoIP DB specified for "
+ "country search: ignored");
+ return (subtype);
+ case dns_geoip_countrycode3:
+ if (strcasecmp(dbname, "city") == 0)
+ return (dns_geoip_city_countrycode3);
+ else if (strcasecmp(dbname, "country") == 0)
+ return (dns_geoip_country_code3);
+ cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
+ "invalid GeoIP DB specified for "
+ "country search: ignored");
+ return (subtype);
+ case dns_geoip_countryname:
+ if (strcasecmp(dbname, "city") == 0)
+ return (dns_geoip_city_countryname);
+ else if (strcasecmp(dbname, "country") == 0)
+ return (dns_geoip_country_name);
+ cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
+ "invalid GeoIP DB specified for "
+ "country search: ignored");
+ return (subtype);
+ case dns_geoip_region:
+ if (strcasecmp(dbname, "city") == 0)
+ return (dns_geoip_city_region);
+ else if (strcasecmp(dbname, "region") == 0)
+ return (dns_geoip_region_code);
+ cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
+ "invalid GeoIP DB specified for "
+ "region search: ignored");
+ return (subtype);
+ case dns_geoip_regionname:
+ if (strcasecmp(dbname, "city") == 0)
+ return (dns_geoip_city_region);
+ else if (strcasecmp(dbname, "region") == 0)
+ return (dns_geoip_region_name);
+ cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
+ "invalid GeoIP DB specified for "
+ "region search: ignored");
+ return (subtype);
+
+ /*
+ * Log a warning if the wrong database was specified
+ * on an unambiguous query
+ */
+ case dns_geoip_city_name:
+ case dns_geoip_city_postalcode:
+ case dns_geoip_city_metrocode:
+ case dns_geoip_city_areacode:
+ case dns_geoip_city_continentcode:
+ case dns_geoip_city_timezonecode:
+ if (strcasecmp(dbname, "city") != 0)
+ cfg_obj_log(obj, lctx, ISC_LOG_WARNING,
+ "invalid GeoIP DB specified for "
+ "a 'city'-only search type: ignoring");
+ return (subtype);
+ case dns_geoip_isp_name:
+ if (strcasecmp(dbname, "isp") != 0)
+ cfg_obj_log(obj, lctx, ISC_LOG_WARNING,
+ "invalid GeoIP DB specified for "
+ "an 'isp' search: ignoring");
+ return (subtype);
+ case dns_geoip_org_name:
+ if (strcasecmp(dbname, "org") != 0)
+ cfg_obj_log(obj, lctx, ISC_LOG_WARNING,
+ "invalid GeoIP DB specified for "
+ "an 'org' search: ignoring");
+ return (subtype);
+ case dns_geoip_as_asnum:
+ if (strcasecmp(dbname, "asnum") != 0)
+ cfg_obj_log(obj, lctx, ISC_LOG_WARNING,
+ "invalid GeoIP DB specified for "
+ "an 'asnum' search: ignoring");
+ return (subtype);
+ case dns_geoip_domain_name:
+ if (strcasecmp(dbname, "domain") != 0)
+ cfg_obj_log(obj, lctx, ISC_LOG_WARNING,
+ "invalid GeoIP DB specified for "
+ "a 'domain' search: ignoring");
+ return (subtype);
+ case dns_geoip_netspeed_id:
+ if (strcasecmp(dbname, "netspeed") != 0)
+ cfg_obj_log(obj, lctx, ISC_LOG_WARNING,
+ "invalid GeoIP DB specified for "
+ "a 'netspeed' search: ignoring");
+ return (subtype);
+ default:
+ INSIST(0);
+ ISC_UNREACHABLE();
+ }
+}
+
+static bool
+geoip_can_answer(dns_aclelement_t *elt, cfg_aclconfctx_t *ctx) {
+ if (ctx->geoip == NULL)
+ return (true);
+
switch (elt->geoip_elem.subtype) {
case dns_geoip_countrycode:
case dns_geoip_countrycode3:
case dns_geoip_city_timezonecode:
if (ctx->geoip->city_v4 != NULL ||
ctx->geoip->city_v6 != NULL)
- {
return (true);
- }
/* FALLTHROUGH */
case dns_geoip_isp_name:
- if (ctx->geoip->isp != NULL) {
+ if (ctx->geoip->isp != NULL)
return (true);
- }
/* FALLTHROUGH */
case dns_geoip_org_name:
- if (ctx->geoip->org != NULL) {
+ if (ctx->geoip->org != NULL)
return (true);
- }
/* FALLTHROUGH */
case dns_geoip_as_asnum:
- if (ctx->geoip->as != NULL) {
+ if (ctx->geoip->as != NULL)
return (true);
- }
/* FALLTHROUGH */
case dns_geoip_domain_name:
- if (ctx->geoip->domain != NULL) {
+ if (ctx->geoip->domain != NULL)
return (true);
- }
/* FALLTHROUGH */
case dns_geoip_netspeed_id:
- if (ctx->geoip->netspeed != NULL) {
+ if (ctx->geoip->netspeed != NULL)
return (true);
- }
+ /*
+ * The following enums are only valid with GeoIP2,
+ * not legacy GeoIP.
+ */
+ case dns_geoip_continentcode:
+ case dns_geoip_continent:
+ case dns_geoip_country_continentcode:
+ case dns_geoip_country_continent:
+ case dns_geoip_city_continent:
+ INSIST(0);
+ ISC_UNREACHABLE();
}
return (false);
}
-#endif
static isc_result_t
parse_geoip_element(const cfg_obj_t *obj, isc_log_t *lctx,
{
const cfg_obj_t *ge;
const char *dbname = NULL;
- const char *stype, *search;
+ const char *stype = NULL, *search = NULL;
dns_geoip_subtype_t subtype;
dns_aclelement_t de;
size_t len;
ge = cfg_tuple_get(obj, "db");
if (!cfg_obj_isvoid(ge)) {
+ int i;
+
dbname = cfg_obj_asstring(ge);
+
+ for (i = 0; geoip_dbnames[i] != NULL; i++) {
+ if (strcasecmp(dbname, geoip_dbnames[i]) == 0) {
+ break;
+ }
+ }
+ if (geoip_dbnames[i] == NULL) {
+ cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
+ "database '%s' is not defined for GeoIP",
+ dbname);
+ return (ISC_R_UNEXPECTED);
+ }
}
stype = cfg_obj_asstring(cfg_tuple_get(obj, "subtype"));
return (ISC_R_SUCCESS);
}
+#endif /* HAVE_GEOIP */
isc_result_t
cfg_acl_fromconfig(const cfg_obj_t *caml, const cfg_obj_t *cctx,