geoip.c revision 83f69fcd6ef72c9e2ebcb025b66a2ee74176becd
bf43fdafa3bff9e84cb03f1a19aca74514d2516eBob Halley * Copyright (C) 2013, 2014 Internet Systems Consortium, Inc. ("ISC")
bf43fdafa3bff9e84cb03f1a19aca74514d2516eBob Halley * Permission to use, copy, modify, and/or distribute this software for any
bf43fdafa3bff9e84cb03f1a19aca74514d2516eBob Halley * purpose with or without fee is hereby granted, provided that the above
bf43fdafa3bff9e84cb03f1a19aca74514d2516eBob Halley * copyright notice and this permission notice appear in all copies.
bf43fdafa3bff9e84cb03f1a19aca74514d2516eBob Halley * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
bf43fdafa3bff9e84cb03f1a19aca74514d2516eBob Halley * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
bf43fdafa3bff9e84cb03f1a19aca74514d2516eBob Halley * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
bf43fdafa3bff9e84cb03f1a19aca74514d2516eBob Halley * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
bf43fdafa3bff9e84cb03f1a19aca74514d2516eBob Halley * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
bf43fdafa3bff9e84cb03f1a19aca74514d2516eBob Halley * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
bf43fdafa3bff9e84cb03f1a19aca74514d2516eBob Halley * PERFORMANCE OF THIS SOFTWARE.
0ec4b862c9abd11c82c88ed62438f0cf06fed25dBob Halley#define _WINSOCKAPI_ /* Prevent inclusion of winsock.h in windows.h */
e419f613d8591885df608cb73065921be07dd12eBob Halley#endif /* WIN32 */
b5debbe212097d1c573a2ba3bd9a3d526d86b0aeBrian Wellington * This structure preserves state from the previous GeoIP lookup,
b5debbe212097d1c573a2ba3bd9a3d526d86b0aeBrian Wellington * so that successive lookups for the same data from the same IP
b5debbe212097d1c573a2ba3bd9a3d526d86b0aeBrian Wellington * address will not require repeated calls into the GeoIP library
b5debbe212097d1c573a2ba3bd9a3d526d86b0aeBrian Wellington * to look up data in the database. This should improve performance
b5debbe212097d1c573a2ba3bd9a3d526d86b0aeBrian Wellington * For lookups in the City and Region databases, we preserve pointers
b5debbe212097d1c573a2ba3bd9a3d526d86b0aeBrian Wellington * to the GeoIPRecord and GeoIPregion structures; these will need to be
b5debbe212097d1c573a2ba3bd9a3d526d86b0aeBrian Wellington * freed by GeoIPRecord_delete() and GeoIPRegion_delete().
e419f613d8591885df608cb73065921be07dd12eBob Halley * for lookups in ISP, AS, Org and Domain we prserve a pointer to
e419f613d8591885df608cb73065921be07dd12eBob Halley * the returned name; these must be freed by free().
e419f613d8591885df608cb73065921be07dd12eBob Halley * For lookups in Country we preserve a pointer to the text of
e419f613d8591885df608cb73065921be07dd12eBob Halley * the country code, name, etc (we use a different pointer for this
e419f613d8591885df608cb73065921be07dd12eBob Halley * than for the names returned by Org, ISP, etc, because those need
e419f613d8591885df608cb73065921be07dd12eBob Halley * to be freed but country lookups do not).
e419f613d8591885df608cb73065921be07dd12eBob Halley * For lookups in Netspeed we preserve the returned ID.
0ec4b862c9abd11c82c88ed62438f0cf06fed25dBob Halley * XXX: Currently this mechanism is only used for IPv4 lookups; the
0ec4b862c9abd11c82c88ed62438f0cf06fed25dBob Halley * family and addr6 fields are to be used IPv6 is added.
0ec4b862c9abd11c82c88ed62438f0cf06fed25dBob Halleytypedef struct geoip_state {
e419f613d8591885df608cb73065921be07dd12eBob Halley const char *text;
b5debbe212097d1c573a2ba3bd9a3d526d86b0aeBrian Wellingtonstatic isc_boolean_t state_key_initialized = ISC_FALSE;
b5debbe212097d1c573a2ba3bd9a3d526d86b0aeBrian Wellingtonstatic isc_once_t mutex_once = ISC_ONCE_INIT;
0ec4b862c9abd11c82c88ed62438f0cf06fed25dBob Halley RUNTIME_CHECK(isc_mutex_init(&key_mutex) == ISC_R_SUCCESS);
0ec4b862c9abd11c82c88ed62438f0cf06fed25dBob Halley result = isc_once_do(&mutex_once, key_mutex_init);
0ec4b862c9abd11c82c88ed62438f0cf06fed25dBob Halley isc_mem_setname(state_mctx, "geoip_state", NULL);
b5debbe212097d1c573a2ba3bd9a3d526d86b0aeBrian Wellington isc_mem_setdestroycheck(state_mctx, ISC_FALSE);
b5debbe212097d1c573a2ba3bd9a3d526d86b0aeBrian Wellington ret = isc_thread_key_create(&state_key, free_state);
b5debbe212097d1c573a2ba3bd9a3d526d86b0aeBrian Wellingtonset_state(unsigned int family, isc_uint32_t ipnum, const geoipv6_t *ipnum6,
b5debbe212097d1c573a2ba3bd9a3d526d86b0aeBrian Wellington dns_geoip_subtype_t subtype, GeoIPRecord *record,
b5debbe212097d1c573a2ba3bd9a3d526d86b0aeBrian Wellington GeoIPRegion *region, char *name, const char *text, int id)
3676eeb6ca95c66aae1256f37af8c990d9f25eb4Brian Wellington state = (geoip_state_t *) isc_thread_key_getspecific(state_key);
3676eeb6ca95c66aae1256f37af8c990d9f25eb4Brian Wellington state = (geoip_state_t *) isc_mem_get(state_mctx,
3676eeb6ca95c66aae1256f37af8c990d9f25eb4Brian Wellington result = isc_thread_key_setspecific(state_key, state);
3676eeb6ca95c66aae1256f37af8c990d9f25eb4Brian Wellington isc_mem_put(state_mctx, state, sizeof(geoip_state_t));
0ec4b862c9abd11c82c88ed62438f0cf06fed25dBob Halley state = (geoip_state_t *) isc_thread_key_getspecific(state_key);
0ec4b862c9abd11c82c88ed62438f0cf06fed25dBob Halley * Country lookups are performed if the previous lookup was from a
0ec4b862c9abd11c82c88ed62438f0cf06fed25dBob Halley * different IP address than the current, or was for a search of a
0ec4b862c9abd11c82c88ed62438f0cf06fed25dBob Halley * different subtype.
0ec4b862c9abd11c82c88ed62438f0cf06fed25dBob Halleystatic const char *
0ec4b862c9abd11c82c88ed62438f0cf06fed25dBob Halleycountry_lookup(GeoIP *db, dns_geoip_subtype_t subtype,
0ec4b862c9abd11c82c88ed62438f0cf06fed25dBob Halley unsigned int family,
0ec4b862c9abd11c82c88ed62438f0cf06fed25dBob Halley /* no IPv6 support? give up now */
0ec4b862c9abd11c82c88ed62438f0cf06fed25dBob Halley ((prev_state->family == AF_INET && prev_state->ipnum == ipnum) ||
0ec4b862c9abd11c82c88ed62438f0cf06fed25dBob Halley memcmp(prev_state->ipnum6.s6_addr, ipnum6->s6_addr, 16) == 0)))
0ec4b862c9abd11c82c88ed62438f0cf06fed25dBob Halleystatic char *
0ec4b862c9abd11c82c88ed62438f0cf06fed25dBob Halleycity_string(GeoIPRecord *record, dns_geoip_subtype_t subtype, int *maxlen) {
0ec4b862c9abd11c82c88ed62438f0cf06fed25dBob Halley const char *s;
0ec4b862c9abd11c82c88ed62438f0cf06fed25dBob Halley /* Set '*maxlen' to the maximum length of this subtype, if any */
0ec4b862c9abd11c82c88ed62438f0cf06fed25dBob Halley /* No fixed length; just use strcasecmp() for comparison */
0ec4b862c9abd11c82c88ed62438f0cf06fed25dBob Halley s = GeoIP_region_name_by_code(record->country_code,
0ec4b862c9abd11c82c88ed62438f0cf06fed25dBob Halley s = GeoIP_time_zone_by_country_and_region(record->country_code,
3676eeb6ca95c66aae1256f37af8c990d9f25eb4Brian Wellington * GeoIPRecord lookups are performed if the previous lookup was
3676eeb6ca95c66aae1256f37af8c990d9f25eb4Brian Wellington * from a different IP address than the current, or was for a search
3676eeb6ca95c66aae1256f37af8c990d9f25eb4Brian Wellington * outside the City database.
3676eeb6ca95c66aae1256f37af8c990d9f25eb4Brian Wellingtoncity_lookup(GeoIP *db, dns_geoip_subtype_t subtype,
3676eeb6ca95c66aae1256f37af8c990d9f25eb4Brian Wellington unsigned int family, isc_uint32_t ipnum, const geoipv6_t *ipnum6)
0ec4b862c9abd11c82c88ed62438f0cf06fed25dBob Halley /* no IPv6 support? give up now */
0ec4b862c9abd11c82c88ed62438f0cf06fed25dBob Halley ((prev_state->family == AF_INET && prev_state->ipnum == ipnum) ||
0ec4b862c9abd11c82c88ed62438f0cf06fed25dBob Halley memcmp(prev_state->ipnum6.s6_addr, ipnum6->s6_addr, 16) == 0)))
b5debbe212097d1c573a2ba3bd9a3d526d86b0aeBrian Wellington record = GeoIP_record_by_ipnum_v6(db, *ipnum6);
b5debbe212097d1c573a2ba3bd9a3d526d86b0aeBrian Wellingtonregion_string(GeoIPRegion *region, dns_geoip_subtype_t subtype, int *maxlen) {
b5debbe212097d1c573a2ba3bd9a3d526d86b0aeBrian Wellington const char *s;
0ec4b862c9abd11c82c88ed62438f0cf06fed25dBob Halley s = GeoIP_region_name_by_code(region->country_code,
0ec4b862c9abd11c82c88ed62438f0cf06fed25dBob Halley * GeoIPRegion lookups are performed if the previous lookup was
0ec4b862c9abd11c82c88ed62438f0cf06fed25dBob Halley * from a different IP address than the current, or was for a search
0ec4b862c9abd11c82c88ed62438f0cf06fed25dBob Halley * outside the Region database.
93c786e0924aeca2c258e32355349e6ae60a0f72Andreas Gustafssonregion_lookup(GeoIP *db, dns_geoip_subtype_t subtype, isc_uint32_t ipnum) {
93c786e0924aeca2c258e32355349e6ae60a0f72Andreas Gustafsson if (prev_state != NULL && prev_state->ipnum == ipnum &&
93c786e0924aeca2c258e32355349e6ae60a0f72Andreas Gustafsson * ISP, Organization, AS Number and Domain lookups are performed if
93c786e0924aeca2c258e32355349e6ae60a0f72Andreas Gustafsson * the previous lookup was from a different IP address than the current,
0a3e2e1d590dac7fb011e72bd3a4982c179d8e68Brian Wellington * or was for a search of a different subtype.
0ec4b862c9abd11c82c88ed62438f0cf06fed25dBob Halleystatic char *
0ec4b862c9abd11c82c88ed62438f0cf06fed25dBob Halleyname_lookup(GeoIP *db, dns_geoip_subtype_t subtype, isc_uint32_t ipnum) {
b5debbe212097d1c573a2ba3bd9a3d526d86b0aeBrian Wellington if (prev_state != NULL && prev_state->ipnum == ipnum &&
b5debbe212097d1c573a2ba3bd9a3d526d86b0aeBrian Wellington * Netspeed lookups are performed if the previous lookup was from a
b5debbe212097d1c573a2ba3bd9a3d526d86b0aeBrian Wellington * different IP address than the current, or was for a search of a
0ec4b862c9abd11c82c88ed62438f0cf06fed25dBob Halley * different subtype.
0ec4b862c9abd11c82c88ed62438f0cf06fed25dBob Halleynetspeed_lookup(GeoIP *db, dns_geoip_subtype_t subtype, isc_uint32_t ipnum) {
0ec4b862c9abd11c82c88ed62438f0cf06fed25dBob Halley if (prev_state != NULL && prev_state->ipnum == ipnum &&
bf43fdafa3bff9e84cb03f1a19aca74514d2516eBob Halley#endif /* HAVE_GEOIP */
e419f613d8591885df608cb73065921be07dd12eBob Halley ((addr->family == AF_INET) ? (geoip->name##_v4) : (geoip->name##_v6))
e419f613d8591885df608cb73065921be07dd12eBob Halley * Find the best database to answer a generic subtype
e419f613d8591885df608cb73065921be07dd12eBob Halleyfix_subtype(const isc_netaddr_t *reqaddr, const dns_geoip_databases_t *geoip,
b5debbe212097d1c573a2ba3bd9a3d526d86b0aeBrian Wellington else if (reqaddr->family == AF_INET && geoip->region != NULL)
b5debbe212097d1c573a2ba3bd9a3d526d86b0aeBrian Wellington else if (DB46(reqaddr, geoip, country) != NULL)
e419f613d8591885df608cb73065921be07dd12eBob Halley else if (reqaddr->family == AF_INET && geoip->region != NULL)
b5debbe212097d1c573a2ba3bd9a3d526d86b0aeBrian Wellington else if (reqaddr->family == AF_INET && geoip->region != NULL)
e419f613d8591885df608cb73065921be07dd12eBob Halley#endif /* HAVE_GEOIP */
e419f613d8591885df608cb73065921be07dd12eBob Halley const char *cs;
1872808932603066d401d3de97db11af8ffee78aAndreas Gustafsson subtype = fix_subtype(reqaddr, geoip, elt->subtype);
0ec4b862c9abd11c82c88ed62438f0cf06fed25dBob Halley cs = country_lookup(db, subtype, reqaddr->family,
0ec4b862c9abd11c82c88ed62438f0cf06fed25dBob Halley if (cs != NULL && strncasecmp(elt->as_string, cs, maxlen) == 0)
case dns_geoip_city_name:
return (ISC_FALSE);
return (ISC_TRUE);
case dns_geoip_city_metrocode:
return (ISC_FALSE);
return (ISC_TRUE);
case dns_geoip_city_areacode:
return (ISC_FALSE);
return (ISC_TRUE);
case dns_geoip_region_code:
case dns_geoip_region_name:
case dns_geoip_region:
return (ISC_FALSE);
return (ISC_FALSE);
return (ISC_TRUE);
case dns_geoip_isp_name:
goto getname;
case dns_geoip_org_name:
goto getname;
case dns_geoip_as_asnum:
goto getname;
case dns_geoip_domain_name:
return (ISC_FALSE);
return (ISC_FALSE);
return (ISC_TRUE);
case dns_geoip_netspeed_id:
return (ISC_FALSE);
return (ISC_TRUE);
INSIST(0);
return (ISC_FALSE);
dns_geoip_shutdown(void) {