/*! \file */
+#if defined(HAVE_GEOIP2)
+#include <maxminddb.h>
+#elif defined(HAVE_GEOIP)
+#include <GeoIP.h>
+#include <GeoIPCity.h>
+#endif
+
+#include <isc/print.h>
+#include <isc/string.h>
#include <isc/util.h>
+#include <dns/geoip.h>
+
#include <named/log.h>
#include <named/geoip.h>
-#include <dns/geoip.h>
-
-#ifdef HAVE_GEOIP
-static dns_geoip_databases_t geoip_table = {
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
-};
+static dns_geoip_databases_t geoip_table = DNS_GEOIP_DATABASE_INIT;
+#if defined(HAVE_GEOIP)
static void
init_geoip_db(void **dbp, GeoIPDBTypes edition, GeoIPDBTypes fallback,
GeoIPOptions method, const char *name)
*dbp = db;
return;
+
fail:
- if (fallback != 0)
+ if (fallback != 0) {
init_geoip_db(dbp, fallback, 0, method, name);
+ }
+
+}
+#elif defined(HAVE_GEOIP2)
+static MMDB_s geoip_country, geoip_city, geoip_as, geoip_isp, geoip_domain;
+
+static MMDB_s *
+open_geoip2(const char *dir, const char *dbfile, MMDB_s *mmdb) {
+ char pathbuf[PATH_MAX];
+ unsigned int n;
+ int ret;
+
+ n = snprintf(pathbuf, sizeof(pathbuf), "%s/%s", dir, dbfile);
+ if (n >= sizeof(pathbuf)) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "GeoIP2 database '%s/%s': path too long",
+ (dir != NULL) ? dir : ".", dbfile);
+ return (NULL);
+ }
+
+ ret = MMDB_open(pathbuf, MMDB_MODE_MMAP, mmdb);
+ if (ret == MMDB_SUCCESS) {
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "opened GeoIP2 database '%s'", pathbuf);
+ return (mmdb);
+ }
+
+ isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
+ NAMED_LOGMODULE_SERVER, ISC_LOG_DEBUG(1),
+ "unable to open GeoIP2 database '%s' (status %d)",
+ pathbuf, ret);
+ return (NULL);
}
-#endif /* HAVE_GEOIP */
+#endif /* HAVE_GEOIP2 */
+
void
named_geoip_init(void) {
-#if defined(HAVE_GEOIP2)
- /* TODO GEOIP2 */
-#elif defined(HAVE_GEOIP)
- GeoIP_cleanup();
- if (named_g_geoip == NULL)
+#if defined(HAVE_GEOIP) || defined(HAVE_GEOIP2)
+ if (named_g_geoip == NULL) {
named_g_geoip = &geoip_table;
+ }
+#if defined(HAVE_GEOIP)
+ GeoIP_cleanup();
+#endif
#else
return;
#endif
void
named_geoip_load(char *dir) {
#if defined(HAVE_GEOIP2)
- /* TODO GEOIP2 */
+ REQUIRE(dir != NULL);
+
+ named_g_geoip->country = open_geoip2(dir, "GeoIP2-Country.mmdb",
+ &geoip_country);
+ if (named_g_geoip->country == NULL) {
+ named_g_geoip->country = open_geoip2(dir,
+ "GeoLite2-Country.mmdb",
+ &geoip_country);
+ }
+
+ named_g_geoip->city = open_geoip2(dir, "GeoIP2-City.mmdb",
+ &geoip_city);
+ if (named_g_geoip->city == NULL) {
+ named_g_geoip->city = open_geoip2(dir, "GeoLite2-City.mmdb",
+ &geoip_city);
+ }
+
+ named_g_geoip->as = open_geoip2(dir, "GeoIP2-ASN.mmdb", &geoip_as);
+ if (named_g_geoip->as == NULL) {
+ named_g_geoip->as = open_geoip2(dir, "GeoLite2-ASN.mmdb",
+ &geoip_as);
+ }
+
+ named_g_geoip->isp = open_geoip2(dir, "GeoIP2-ISP.mmdb", &geoip_isp);
+ named_g_geoip->domain = open_geoip2(dir, "GeoIP2-Domain.mmdb",
+ &geoip_domain);
#elif defined(HAVE_GEOIP)
GeoIPOptions method;
return;
#endif
}
+
+void
+named_geoip_shutdown(void) {
+#ifdef HAVE_GEOIP2
+ if (named_g_geoip->country != NULL) {
+ MMDB_close(named_g_geoip->country);
+ named_g_geoip->country = NULL;
+ }
+ if (named_g_geoip->city != NULL) {
+ MMDB_close(named_g_geoip->city);
+ named_g_geoip->city = NULL;
+ }
+ if (named_g_geoip->as != NULL) {
+ MMDB_close(named_g_geoip->as);
+ named_g_geoip->as = NULL;
+ }
+ if (named_g_geoip->isp != NULL) {
+ MMDB_close(named_g_geoip->isp);
+ named_g_geoip->isp = NULL;
+ }
+ if (named_g_geoip->domain != NULL) {
+ MMDB_close(named_g_geoip->domain);
+ named_g_geoip->domain = NULL;
+ }
+#endif /* HAVE_GEOIP2 */
+
+ dns_geoip_shutdown();
+}
* information regarding copyright ownership.
*/
-#ifndef _GEOIP_H
-#define _GEOIP_H
+#pragma once
-#ifdef HAVE_GEOIP
-#include <GeoIP.h>
-#include <GeoIPCity.h>
-#endif /* HAVE_GEOIP */
+extern dns_geoip_databases_t *named_g_geoip;
-void named_geoip_init(void);
-void named_geoip_load(char *dir);
+void
+named_geoip_init(void);
-#ifdef HAVE_GEOIP
-extern dns_geoip_databases_t *named_g_geoip;
-#endif /* HAVE_GEOIP */
-#endif
+void
+named_geoip_load(char *dir);
+
+void
+named_geoip_shutdown(void);
dns_dt_shutdown();
#endif
#if defined(HAVE_GEOIP) || defined(HAVE_GEOIP2)
- dns_geoip_shutdown();
+ named_geoip_shutdown();
#endif /* HAVE_GEOIP || HAVE_GEOIP2 */
dns_db_detach(&server->in_roothints);
/*! \file */
-#include <dns/geoip.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,
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) {
+ 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) {
+ 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) {
+ 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) {
+ 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) {
+ 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) {
+ 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) {
+ 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) {
+ 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) {
+ 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) {
+ 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) {
+ 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) {
+ 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) {
+ 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) {
+ 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 <dns/types.h>
#include <dns/iptable.h>
-#if defined(HAVE_GEOIP2)
-#include <maxminddb.h>
-#elif defined(HAVE_GEOIP)
-#include <GeoIP.h>
-#endif
-
/***
*** Types
***/
dns_geoip_countrycode,
dns_geoip_countrycode3,
dns_geoip_countryname,
+ dns_geoip_continentcode,
+ dns_geoip_continent,
dns_geoip_region,
dns_geoip_regionname,
dns_geoip_country_code,
dns_geoip_country_code3,
dns_geoip_country_name,
+ dns_geoip_country_continentcode,
+ dns_geoip_country_continent,
dns_geoip_region_countrycode,
dns_geoip_region_code,
dns_geoip_region_name,
dns_geoip_city_metrocode,
dns_geoip_city_areacode,
dns_geoip_city_continentcode,
+ dns_geoip_city_continent,
dns_geoip_city_timezonecode,
dns_geoip_isp_name,
dns_geoip_org_name,
void *domain; /* GeoIP2-Domain */
void *isp; /* GeoIP2-ISP */
void *as; /* GeoIP2-ASN or GeoLite2-ASN */
+#define DNS_GEOIP_DATABASE_INIT \
+ { NULL, NULL, NULL, NULL, NULL }
#elif defined(HAVE_GEOIP)
void *country_v4; /* DB 1 */
void *city_v4; /* DB 2 or 6 */
void *domain; /* DB 11 */
void *country_v6; /* DB 12 */
void *city_v6; /* DB 30 or 31 */
+#define DNS_GEOIP_DATABASE_INIT \
+ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
#endif
} dns_geoip_databases_t;
/* TODO GEOIP2 */
#define TEST_GEOIP_DATA ""
-#elif defined(HAVE_GEOI2)
+#elif defined(HAVE_GEOIP)
#include <GeoIP.h>
/* We use GeoIP databases from the 'geoip' system test */
return (0);
}
+
+static dns_geoip_databases_t geoip = DNS_GEOIP_DATABASE_INIT;
#endif /* HAVE_GEOIP || HAVE_GEOIP2 */
#ifdef HAVE_GEOIP
* Helper functions
* (Mostly copied from bin/named/geoip.c)
*/
-static dns_geoip_databases_t geoip = {
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
-};
+static dns_geoip_databases_t geoip = { NULL };
static void
init_geoip_db(void **dbp, GeoIPDBTypes edition, GeoIPDBTypes fallback,
#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",
+ "asnum",
+ "isp",
+ "domain",
+ "netspeed",
+ "org",
+ NULL,
+};
+#endif /* HAVE_GEOIP */
+
isc_result_t
cfg_aclconfctx_create(isc_mem_t *mctx, cfg_aclconfctx_t **ret) {
cfg_aclconfctx_t *actx;
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 GeoIP",
+ 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);
}
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);
+ }
}
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,
cfg_doc_enum, &cfg_rep_string, &geoiptype_enums
};
-static const char *geoipdb_enums[] = {
- "asnum", "city", "country", "domain", "isp", "netspeed",
- "org", "region", NULL
-};
-static cfg_type_t cfg_type_geoipdb = {
- "geoipdb", cfg_parse_enum, cfg_print_ustring,
- cfg_doc_enum, &cfg_rep_string, &geoipdb_enums
-};
-
static cfg_tuplefielddef_t geoip_fields[] = {
{ "negated", &cfg_type_void, 0 },
- { "db", &cfg_type_geoipdb, 0 },
+ { "db", &cfg_type_astring, 0 },
{ "subtype", &cfg_type_geoiptype, 0 },
{ "search", &cfg_type_astring, 0 },
{ NULL, NULL, 0 }
doc_geoip(cfg_printer_t *pctx, const cfg_type_t *type) {
UNUSED(type);
cfg_print_cstr(pctx, "[ db ");
- cfg_doc_enum(pctx, &cfg_type_geoipdb);
+ cfg_doc_obj(pctx, &cfg_type_astring);
cfg_print_cstr(pctx, " ]");
cfg_print_cstr(pctx, " ");
cfg_doc_enum(pctx, &cfg_type_geoiptype);
cfg_print_cstr(pctx, " ");
- cfg_print_cstr(pctx, "<quoted_string>");
+ cfg_doc_obj(pctx, &cfg_type_astring);
}
#endif /* HAVE_GEOIP || HAVE_GEOIP2 */