geoip.c revision b454c0319685041db3f3e8fd7671e1b364fd20c5
/*
* Copyright (C) 2013, 2014 Internet Systems Consortium, Inc. ("ISC")
*
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*! \file */
#include <config.h>
#include <math.h>
#ifndef WIN32
#else
#ifndef _WINSOCKAPI_
#define _WINSOCKAPI_ /* Prevent inclusion of winsock.h in windows.h */
#endif
#include <winsock2.h>
#endif /* WIN32 */
#ifdef HAVE_GEOIP
#include <GeoIP.h>
#include <GeoIPCity.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 calls into the GeoIP library
* to look up data in the database. This should improve performance
* somwhat.
*
* For lookups in the City and Region databases, we preserve pointers
* to the GeoIPRecord and GeoIPregion structures; these will need to be
* freed by GeoIPRecord_delete() and GeoIPRegion_delete().
*
* for lookups in ISP, AS, Org and Domain we prserve a pointer to
* the returned name; these must be freed by free().
*
* For lookups in Country we preserve a pointer to the text of
* the country code, name, etc (we use a different pointer for this
* than for the names returned by Org, ISP, etc, because those need
* to be freed but country lookups do not).
*
* For lookups in Netspeed we preserve the returned ID.
*
* XXX: Currently this mechanism is only used for IPv4 lookups; the
* family and addr6 fields are to be used IPv6 is added.
*/
typedef struct geoip_state {
unsigned int family;
const char *text;
char *name;
int id;
#ifdef ISC_PLATFORM_USETHREADS
static isc_mutex_t key_mutex;
static isc_thread_key_t state_key;
static void
key_mutex_init(void) {
}
static void
free_state(void *arg) {
state, sizeof(geoip_state_t));
}
static isc_result_t
state_key_init(void) {
if (result != ISC_R_SUCCESS)
return (result);
if (!state_key_initialized) {
if (!state_key_initialized) {
int ret;
if (state_mctx == NULL)
if (result != ISC_R_SUCCESS)
goto unlock;
if (ret == 0)
else
}
}
return (result);
}
#else
#endif
static void
return;
}
static isc_result_t
{
#ifdef ISC_PLATFORM_USETHREADS
result = state_key_init();
if (result != ISC_R_SUCCESS)
return (result);
sizeof(geoip_state_t));
return (ISC_R_NOMEMORY);
if (result != ISC_R_SUCCESS) {
return (result);
}
} else
#else
state = &prev_state;
#endif
else
return (ISC_R_SUCCESS);
}
static geoip_state_t *
get_state(void) {
#ifdef ISC_PLATFORM_USETHREADS
result = state_key_init();
if (result != ISC_R_SUCCESS)
return (NULL);
return (state);
#else
return (&prev_state);
#endif
}
/*
* Country lookups are performed if the previous lookup was from a
* different IP address than the current, or was for a search of a
* different subtype.
*/
static const char *
unsigned int family,
{
#ifndef HAVE_GEOIP_V6
/* no IPv6 support? give up now */
return (NULL);
#endif
prev_state = get_state();
if (prev_state != NULL &&
switch (subtype) {
case dns_geoip_country_code:
#ifdef HAVE_GEOIP_V6
else
*ipnum6);
#endif
break;
case dns_geoip_country_code3:
#ifdef HAVE_GEOIP_V6
else
*ipnum6);
#endif
break;
case dns_geoip_country_name:
#ifdef HAVE_GEOIP_V6
else
*ipnum6);
#endif
break;
default:
INSIST(0);
}
}
return (text);
}
static char *
const char *s;
char *deconst;
/* Set '*maxlen' to the maximum length of this subtype, if any */
switch (subtype) {
case dns_geoip_city_region:
*maxlen = 2;
break;
*maxlen = 3;
break;
default:
/* No fixed length; just use strcasecmp() for comparison */
*maxlen = 255;
}
switch (subtype) {
return (record->country_code);
return (record->country_code3);
return (record->country_name);
case dns_geoip_city_region:
return (deconst);
case dns_geoip_city_name:
return (record->postal_code);
return (record->continent_code);
return (deconst);
default:
INSIST(0);
}
}
static isc_boolean_t
switch (subtype) {
case dns_geoip_city_region:
case dns_geoip_city_name:
case dns_geoip_city_metrocode:
case dns_geoip_city_areacode:
return (ISC_TRUE);
default:
return (ISC_FALSE);
}
}
/*
* GeoIPRecord lookups are performed if the previous lookup was
* from a different IP address than the current, or was for a search
* outside the City database.
*/
static GeoIPRecord *
{
#ifndef HAVE_GEOIP_V6
/* no IPv6 support? give up now */
return (NULL);
#endif
prev_state = get_state();
if (prev_state != NULL &&
#ifdef HAVE_GEOIP_V6
else
#endif
return (NULL);
}
return (record);
}
static char *
const char *s;
char *deconst;
switch (subtype) {
*maxlen = 2;
return (region->country_code);
case dns_geoip_region_code:
*maxlen = 2;
case dns_geoip_region_name:
*maxlen = 255;
return (deconst);
default:
INSIST(0);
}
}
static isc_boolean_t
switch (subtype) {
case dns_geoip_region_code:
return (ISC_TRUE);
default:
return (ISC_FALSE);
}
}
/*
* GeoIPRegion lookups are performed if the previous lookup was
* from a different IP address than the current, or was for a search
* outside the Region database.
*/
static GeoIPRegion *
prev_state = get_state();
return (NULL);
}
return (region);
}
/*
* ISP, Organization, AS Number and Domain lookups are performed if
* the previous lookup was from a different IP address than the current,
* or was for a search of a different subtype.
*/
static char *
prev_state = get_state();
return (NULL);
}
return (name);
}
/*
* Netspeed lookups are performed if the previous lookup was from a
* different IP address than the current, or was for a search of a
* different subtype.
*/
static int
int id = -1;
prev_state = get_state();
}
if (!found) {
}
return (id);
}
#endif /* HAVE_GEOIP */
#ifdef HAVE_GEOIP
/*
* Find the best database to answer a generic subtype
*/
static dns_geoip_subtype_t
{
switch (subtype) {
case dns_geoip_countrycode:
break;
case dns_geoip_countrycode3:
break;
case dns_geoip_countryname:
break;
case dns_geoip_region:
break;
case dns_geoip_regionname:
break;
default:
break;
}
return (ret);
}
#endif /* HAVE_GEOIP */
const dns_geoip_databases_t *geoip,
const dns_geoip_elem_t *elt)
{
#ifndef HAVE_GEOIP
return (ISC_FALSE);
#else
isc_uint32_t ipnum = 0;
const char *cs;
char *s;
#ifdef HAVE_GEOIP_V6
#else
#endif
case AF_INET:
break;
case AF_INET6:
#ifdef HAVE_GEOIP_V6
break;
#else
return (ISC_FALSE);
#endif
default:
return (ISC_FALSE);
}
switch (subtype) {
case dns_geoip_country_code:
maxlen = 2;
goto getcountry;
case dns_geoip_country_code3:
maxlen = 3;
goto getcountry;
case dns_geoip_country_name:
maxlen = 255;
return (ISC_FALSE);
return (ISC_TRUE);
break;
case dns_geoip_city_region:
case dns_geoip_city_name:
return (ISC_FALSE);
break;
return (ISC_TRUE);
break;
case dns_geoip_city_metrocode:
return (ISC_FALSE);
break;
return (ISC_TRUE);
break;
case dns_geoip_city_areacode:
return (ISC_FALSE);
break;
return (ISC_TRUE);
break;
case dns_geoip_region_code:
case dns_geoip_region_name:
case dns_geoip_region:
return (ISC_FALSE);
/* Region DB is not supported for IPv6 */
return (ISC_FALSE);
break;
return (ISC_TRUE);
break;
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);
/* ISP, Org, AS, and Domain are not supported for IPv6 */
return (ISC_FALSE);
return (ISC_TRUE);
break;
case dns_geoip_netspeed_id:
/* Netspeed DB is not supported for IPv6 */
return (ISC_FALSE);
return (ISC_TRUE);
break;
default:
INSIST(0);
}
return (ISC_FALSE);
#endif
}
void
dns_geoip_shutdown(void) {
#if defined(HAVE_GEOIP) && defined(ISC_PLATFORM_USETHREADS)
if (state_mctx != NULL)
#else
return;
#endif
}