xref: /minix3/external/bsd/bind/dist/lib/dns/geoip.c (revision 00b67f09dd46474d133c95011a48590a8e8f94c7)
1*00b67f09SDavid van Moolenbroek /*	$NetBSD: geoip.c,v 1.1.1.6 2015/07/08 15:38:01 christos Exp $	*/
2*00b67f09SDavid van Moolenbroek 
3*00b67f09SDavid van Moolenbroek /*
4*00b67f09SDavid van Moolenbroek  * Copyright (C) 2013-2015  Internet Systems Consortium, Inc. ("ISC")
5*00b67f09SDavid van Moolenbroek  *
6*00b67f09SDavid van Moolenbroek  * Permission to use, copy, modify, and/or distribute this software for any
7*00b67f09SDavid van Moolenbroek  * purpose with or without fee is hereby granted, provided that the above
8*00b67f09SDavid van Moolenbroek  * copyright notice and this permission notice appear in all copies.
9*00b67f09SDavid van Moolenbroek  *
10*00b67f09SDavid van Moolenbroek  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
11*00b67f09SDavid van Moolenbroek  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
12*00b67f09SDavid van Moolenbroek  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
13*00b67f09SDavid van Moolenbroek  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
14*00b67f09SDavid van Moolenbroek  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
15*00b67f09SDavid van Moolenbroek  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16*00b67f09SDavid van Moolenbroek  * PERFORMANCE OF THIS SOFTWARE.
17*00b67f09SDavid van Moolenbroek  */
18*00b67f09SDavid van Moolenbroek 
19*00b67f09SDavid van Moolenbroek /*! \file */
20*00b67f09SDavid van Moolenbroek 
21*00b67f09SDavid van Moolenbroek #include <config.h>
22*00b67f09SDavid van Moolenbroek 
23*00b67f09SDavid van Moolenbroek #include <isc/util.h>
24*00b67f09SDavid van Moolenbroek 
25*00b67f09SDavid van Moolenbroek #include <isc/mem.h>
26*00b67f09SDavid van Moolenbroek #include <isc/once.h>
27*00b67f09SDavid van Moolenbroek #include <isc/string.h>
28*00b67f09SDavid van Moolenbroek 
29*00b67f09SDavid van Moolenbroek #include <dns/acl.h>
30*00b67f09SDavid van Moolenbroek #include <dns/geoip.h>
31*00b67f09SDavid van Moolenbroek 
32*00b67f09SDavid van Moolenbroek #include <isc/thread.h>
33*00b67f09SDavid van Moolenbroek #include <math.h>
34*00b67f09SDavid van Moolenbroek #ifndef WIN32
35*00b67f09SDavid van Moolenbroek #include <netinet/in.h>
36*00b67f09SDavid van Moolenbroek #else
37*00b67f09SDavid van Moolenbroek #ifndef _WINSOCKAPI_
38*00b67f09SDavid van Moolenbroek #define _WINSOCKAPI_   /* Prevent inclusion of winsock.h in windows.h */
39*00b67f09SDavid van Moolenbroek #endif
40*00b67f09SDavid van Moolenbroek #include <winsock2.h>
41*00b67f09SDavid van Moolenbroek #endif	/* WIN32 */
42*00b67f09SDavid van Moolenbroek #include <dns/log.h>
43*00b67f09SDavid van Moolenbroek 
44*00b67f09SDavid van Moolenbroek #ifdef HAVE_GEOIP
45*00b67f09SDavid van Moolenbroek #include <GeoIP.h>
46*00b67f09SDavid van Moolenbroek #include <GeoIPCity.h>
47*00b67f09SDavid van Moolenbroek 
48*00b67f09SDavid van Moolenbroek /*
49*00b67f09SDavid van Moolenbroek  * This structure preserves state from the previous GeoIP lookup,
50*00b67f09SDavid van Moolenbroek  * so that successive lookups for the same data from the same IP
51*00b67f09SDavid van Moolenbroek  * address will not require repeated calls into the GeoIP library
52*00b67f09SDavid van Moolenbroek  * to look up data in the database. This should improve performance
53*00b67f09SDavid van Moolenbroek  * somewhat.
54*00b67f09SDavid van Moolenbroek  *
55*00b67f09SDavid van Moolenbroek  * For lookups in the City and Region databases, we preserve pointers
56*00b67f09SDavid van Moolenbroek  * to the GeoIPRecord and GeoIPregion structures; these will need to be
57*00b67f09SDavid van Moolenbroek  * freed by GeoIPRecord_delete() and GeoIPRegion_delete().
58*00b67f09SDavid van Moolenbroek  *
59*00b67f09SDavid van Moolenbroek  * for lookups in ISP, AS, Org and Domain we prserve a pointer to
60*00b67f09SDavid van Moolenbroek  * the returned name; these must be freed by free().
61*00b67f09SDavid van Moolenbroek  *
62*00b67f09SDavid van Moolenbroek  * For lookups in Country we preserve a pointer to the text of
63*00b67f09SDavid van Moolenbroek  * the country code, name, etc (we use a different pointer for this
64*00b67f09SDavid van Moolenbroek  * than for the names returned by Org, ISP, etc, because those need
65*00b67f09SDavid van Moolenbroek  * to be freed but country lookups do not).
66*00b67f09SDavid van Moolenbroek  *
67*00b67f09SDavid van Moolenbroek  * For lookups in Netspeed we preserve the returned ID.
68*00b67f09SDavid van Moolenbroek  *
69*00b67f09SDavid van Moolenbroek  * XXX: Currently this mechanism is only used for IPv4 lookups; the
70*00b67f09SDavid van Moolenbroek  * family and addr6 fields are to be used IPv6 is added.
71*00b67f09SDavid van Moolenbroek  */
72*00b67f09SDavid van Moolenbroek typedef struct geoip_state {
73*00b67f09SDavid van Moolenbroek 	isc_uint16_t subtype;
74*00b67f09SDavid van Moolenbroek 	unsigned int family;
75*00b67f09SDavid van Moolenbroek 	isc_uint32_t ipnum;
76*00b67f09SDavid van Moolenbroek 	geoipv6_t ipnum6;
77*00b67f09SDavid van Moolenbroek 	GeoIPRecord *record;
78*00b67f09SDavid van Moolenbroek 	GeoIPRegion *region;
79*00b67f09SDavid van Moolenbroek 	const char *text;
80*00b67f09SDavid van Moolenbroek 	char *name;
81*00b67f09SDavid van Moolenbroek 	int id;
82*00b67f09SDavid van Moolenbroek 	isc_mem_t *mctx;
83*00b67f09SDavid van Moolenbroek } geoip_state_t;
84*00b67f09SDavid van Moolenbroek 
85*00b67f09SDavid van Moolenbroek #ifdef ISC_PLATFORM_USETHREADS
86*00b67f09SDavid van Moolenbroek static isc_mutex_t key_mutex;
87*00b67f09SDavid van Moolenbroek static isc_boolean_t state_key_initialized = ISC_FALSE;
88*00b67f09SDavid van Moolenbroek static isc_thread_key_t state_key;
89*00b67f09SDavid van Moolenbroek static isc_once_t mutex_once = ISC_ONCE_INIT;
90*00b67f09SDavid van Moolenbroek static isc_mem_t *state_mctx = NULL;
91*00b67f09SDavid van Moolenbroek 
92*00b67f09SDavid van Moolenbroek static void
key_mutex_init(void)93*00b67f09SDavid van Moolenbroek key_mutex_init(void) {
94*00b67f09SDavid van Moolenbroek 	RUNTIME_CHECK(isc_mutex_init(&key_mutex) == ISC_R_SUCCESS);
95*00b67f09SDavid van Moolenbroek }
96*00b67f09SDavid van Moolenbroek 
97*00b67f09SDavid van Moolenbroek static void
free_state(void * arg)98*00b67f09SDavid van Moolenbroek free_state(void *arg) {
99*00b67f09SDavid van Moolenbroek 	geoip_state_t *state = arg;
100*00b67f09SDavid van Moolenbroek 	if (state != NULL && state->record != NULL)
101*00b67f09SDavid van Moolenbroek 		GeoIPRecord_delete(state->record);
102*00b67f09SDavid van Moolenbroek 	if (state != NULL)
103*00b67f09SDavid van Moolenbroek 		isc_mem_putanddetach(&state->mctx,
104*00b67f09SDavid van Moolenbroek 				     state, sizeof(geoip_state_t));
105*00b67f09SDavid van Moolenbroek 	isc_thread_key_setspecific(state_key, NULL);
106*00b67f09SDavid van Moolenbroek }
107*00b67f09SDavid van Moolenbroek 
108*00b67f09SDavid van Moolenbroek static isc_result_t
state_key_init(void)109*00b67f09SDavid van Moolenbroek state_key_init(void) {
110*00b67f09SDavid van Moolenbroek 	isc_result_t result;
111*00b67f09SDavid van Moolenbroek 
112*00b67f09SDavid van Moolenbroek 	result = isc_once_do(&mutex_once, key_mutex_init);
113*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
114*00b67f09SDavid van Moolenbroek 		return (result);
115*00b67f09SDavid van Moolenbroek 
116*00b67f09SDavid van Moolenbroek 	if (!state_key_initialized) {
117*00b67f09SDavid van Moolenbroek 		LOCK(&key_mutex);
118*00b67f09SDavid van Moolenbroek 		if (!state_key_initialized) {
119*00b67f09SDavid van Moolenbroek 			int ret;
120*00b67f09SDavid van Moolenbroek 
121*00b67f09SDavid van Moolenbroek 			if (state_mctx == NULL)
122*00b67f09SDavid van Moolenbroek 				result = isc_mem_create2(0, 0, &state_mctx, 0);
123*00b67f09SDavid van Moolenbroek 			if (result != ISC_R_SUCCESS)
124*00b67f09SDavid van Moolenbroek 				goto unlock;
125*00b67f09SDavid van Moolenbroek 			isc_mem_setname(state_mctx, "geoip_state", NULL);
126*00b67f09SDavid van Moolenbroek 			isc_mem_setdestroycheck(state_mctx, ISC_FALSE);
127*00b67f09SDavid van Moolenbroek 
128*00b67f09SDavid van Moolenbroek 			ret = isc_thread_key_create(&state_key, free_state);
129*00b67f09SDavid van Moolenbroek 			if (ret == 0)
130*00b67f09SDavid van Moolenbroek 				state_key_initialized = ISC_TRUE;
131*00b67f09SDavid van Moolenbroek 			else
132*00b67f09SDavid van Moolenbroek 				result = ISC_R_FAILURE;
133*00b67f09SDavid van Moolenbroek 		}
134*00b67f09SDavid van Moolenbroek  unlock:
135*00b67f09SDavid van Moolenbroek 		UNLOCK(&key_mutex);
136*00b67f09SDavid van Moolenbroek 	}
137*00b67f09SDavid van Moolenbroek 
138*00b67f09SDavid van Moolenbroek 	return (result);
139*00b67f09SDavid van Moolenbroek }
140*00b67f09SDavid van Moolenbroek #else
141*00b67f09SDavid van Moolenbroek geoip_state_t prev_state;
142*00b67f09SDavid van Moolenbroek #endif
143*00b67f09SDavid van Moolenbroek 
144*00b67f09SDavid van Moolenbroek static void
clean_state(geoip_state_t * state)145*00b67f09SDavid van Moolenbroek clean_state(geoip_state_t *state) {
146*00b67f09SDavid van Moolenbroek 	if (state == NULL)
147*00b67f09SDavid van Moolenbroek 		return;
148*00b67f09SDavid van Moolenbroek 
149*00b67f09SDavid van Moolenbroek 	if (state->record != NULL) {
150*00b67f09SDavid van Moolenbroek 		GeoIPRecord_delete(state->record);
151*00b67f09SDavid van Moolenbroek 		state->record = NULL;
152*00b67f09SDavid van Moolenbroek 	}
153*00b67f09SDavid van Moolenbroek 	if (state->region != NULL) {
154*00b67f09SDavid van Moolenbroek 		GeoIPRegion_delete(state->region);
155*00b67f09SDavid van Moolenbroek 		state->region = NULL;
156*00b67f09SDavid van Moolenbroek 	}
157*00b67f09SDavid van Moolenbroek 	if (state->name != NULL) {
158*00b67f09SDavid van Moolenbroek 		free (state->name);
159*00b67f09SDavid van Moolenbroek 		state->name = NULL;
160*00b67f09SDavid van Moolenbroek 	}
161*00b67f09SDavid van Moolenbroek 	state->ipnum = 0;
162*00b67f09SDavid van Moolenbroek 	state->text = NULL;
163*00b67f09SDavid van Moolenbroek 	state->id = 0;
164*00b67f09SDavid van Moolenbroek }
165*00b67f09SDavid van Moolenbroek 
166*00b67f09SDavid van Moolenbroek static isc_result_t
set_state(unsigned int family,isc_uint32_t ipnum,const geoipv6_t * ipnum6,dns_geoip_subtype_t subtype,GeoIPRecord * record,GeoIPRegion * region,char * name,const char * text,int id)167*00b67f09SDavid van Moolenbroek set_state(unsigned int family, isc_uint32_t ipnum, const geoipv6_t *ipnum6,
168*00b67f09SDavid van Moolenbroek 	  dns_geoip_subtype_t subtype, GeoIPRecord *record,
169*00b67f09SDavid van Moolenbroek 	  GeoIPRegion *region, char *name, const char *text, int id)
170*00b67f09SDavid van Moolenbroek {
171*00b67f09SDavid van Moolenbroek 	geoip_state_t *state = NULL;
172*00b67f09SDavid van Moolenbroek 
173*00b67f09SDavid van Moolenbroek #ifdef ISC_PLATFORM_USETHREADS
174*00b67f09SDavid van Moolenbroek 	isc_result_t result;
175*00b67f09SDavid van Moolenbroek 
176*00b67f09SDavid van Moolenbroek 	result = state_key_init();
177*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
178*00b67f09SDavid van Moolenbroek 		return (result);
179*00b67f09SDavid van Moolenbroek 
180*00b67f09SDavid van Moolenbroek 	state = (geoip_state_t *) isc_thread_key_getspecific(state_key);
181*00b67f09SDavid van Moolenbroek 	if (state == NULL) {
182*00b67f09SDavid van Moolenbroek 		state = (geoip_state_t *) isc_mem_get(state_mctx,
183*00b67f09SDavid van Moolenbroek 						      sizeof(geoip_state_t));
184*00b67f09SDavid van Moolenbroek 		if (state == NULL)
185*00b67f09SDavid van Moolenbroek 			return (ISC_R_NOMEMORY);
186*00b67f09SDavid van Moolenbroek 		memset(state, 0, sizeof(*state));
187*00b67f09SDavid van Moolenbroek 
188*00b67f09SDavid van Moolenbroek 		result = isc_thread_key_setspecific(state_key, state);
189*00b67f09SDavid van Moolenbroek 		if (result != ISC_R_SUCCESS) {
190*00b67f09SDavid van Moolenbroek 			isc_mem_put(state_mctx, state, sizeof(geoip_state_t));
191*00b67f09SDavid van Moolenbroek 			return (result);
192*00b67f09SDavid van Moolenbroek 		}
193*00b67f09SDavid van Moolenbroek 
194*00b67f09SDavid van Moolenbroek 		isc_mem_attach(state_mctx, &state->mctx);
195*00b67f09SDavid van Moolenbroek 	} else
196*00b67f09SDavid van Moolenbroek 		clean_state(state);
197*00b67f09SDavid van Moolenbroek #else
198*00b67f09SDavid van Moolenbroek 	state = &prev_state;
199*00b67f09SDavid van Moolenbroek 	clean_state(state);
200*00b67f09SDavid van Moolenbroek #endif
201*00b67f09SDavid van Moolenbroek 
202*00b67f09SDavid van Moolenbroek 	if (family == AF_INET)
203*00b67f09SDavid van Moolenbroek 		state->ipnum = ipnum;
204*00b67f09SDavid van Moolenbroek 	else
205*00b67f09SDavid van Moolenbroek 		state->ipnum6 = *ipnum6;
206*00b67f09SDavid van Moolenbroek 
207*00b67f09SDavid van Moolenbroek 	state->family = family;
208*00b67f09SDavid van Moolenbroek 	state->subtype = subtype;
209*00b67f09SDavid van Moolenbroek 	state->record = record;
210*00b67f09SDavid van Moolenbroek 	state->region = region;
211*00b67f09SDavid van Moolenbroek 	state->name = name;
212*00b67f09SDavid van Moolenbroek 	state->text = text;
213*00b67f09SDavid van Moolenbroek 	state->id = id;
214*00b67f09SDavid van Moolenbroek 
215*00b67f09SDavid van Moolenbroek 	return (ISC_R_SUCCESS);
216*00b67f09SDavid van Moolenbroek }
217*00b67f09SDavid van Moolenbroek 
218*00b67f09SDavid van Moolenbroek static geoip_state_t *
get_state_for(unsigned int family,isc_uint32_t ipnum,const geoipv6_t * ipnum6)219*00b67f09SDavid van Moolenbroek get_state_for(unsigned int family, isc_uint32_t ipnum,
220*00b67f09SDavid van Moolenbroek 	      const geoipv6_t *ipnum6)
221*00b67f09SDavid van Moolenbroek {
222*00b67f09SDavid van Moolenbroek 	geoip_state_t *state;
223*00b67f09SDavid van Moolenbroek 
224*00b67f09SDavid van Moolenbroek #ifdef ISC_PLATFORM_USETHREADS
225*00b67f09SDavid van Moolenbroek 	isc_result_t result;
226*00b67f09SDavid van Moolenbroek 
227*00b67f09SDavid van Moolenbroek 	result = state_key_init();
228*00b67f09SDavid van Moolenbroek 	if (result != ISC_R_SUCCESS)
229*00b67f09SDavid van Moolenbroek 		return (NULL);
230*00b67f09SDavid van Moolenbroek 
231*00b67f09SDavid van Moolenbroek 	state = (geoip_state_t *) isc_thread_key_getspecific(state_key);
232*00b67f09SDavid van Moolenbroek 	if (state == NULL)
233*00b67f09SDavid van Moolenbroek 		return (NULL);
234*00b67f09SDavid van Moolenbroek #else
235*00b67f09SDavid van Moolenbroek 	state = &prev_state;
236*00b67f09SDavid van Moolenbroek #endif
237*00b67f09SDavid van Moolenbroek 
238*00b67f09SDavid van Moolenbroek 	if (state->family == family &&
239*00b67f09SDavid van Moolenbroek 	    ((state->family == AF_INET && state->ipnum == ipnum) ||
240*00b67f09SDavid van Moolenbroek 	     (state->family == AF_INET6 && ipnum6 != NULL &&
241*00b67f09SDavid van Moolenbroek 	      memcmp(state->ipnum6.s6_addr, ipnum6->s6_addr, 16) == 0)))
242*00b67f09SDavid van Moolenbroek 		return (state);
243*00b67f09SDavid van Moolenbroek 
244*00b67f09SDavid van Moolenbroek 	return (NULL);
245*00b67f09SDavid van Moolenbroek }
246*00b67f09SDavid van Moolenbroek 
247*00b67f09SDavid van Moolenbroek /*
248*00b67f09SDavid van Moolenbroek  * Country lookups are performed if the previous lookup was from a
249*00b67f09SDavid van Moolenbroek  * different IP address than the current, or was for a search of a
250*00b67f09SDavid van Moolenbroek  * different subtype.
251*00b67f09SDavid van Moolenbroek  */
252*00b67f09SDavid van Moolenbroek static const char *
country_lookup(GeoIP * db,dns_geoip_subtype_t subtype,unsigned int family,isc_uint32_t ipnum,const geoipv6_t * ipnum6)253*00b67f09SDavid van Moolenbroek country_lookup(GeoIP *db, dns_geoip_subtype_t subtype,
254*00b67f09SDavid van Moolenbroek 	       unsigned int family,
255*00b67f09SDavid van Moolenbroek 	       isc_uint32_t ipnum, const geoipv6_t *ipnum6)
256*00b67f09SDavid van Moolenbroek {
257*00b67f09SDavid van Moolenbroek 	geoip_state_t *prev_state = NULL;
258*00b67f09SDavid van Moolenbroek 	const char *text = NULL;
259*00b67f09SDavid van Moolenbroek 
260*00b67f09SDavid van Moolenbroek 	REQUIRE(db != NULL);
261*00b67f09SDavid van Moolenbroek 
262*00b67f09SDavid van Moolenbroek #ifndef HAVE_GEOIP_V6
263*00b67f09SDavid van Moolenbroek 	/* no IPv6 support? give up now */
264*00b67f09SDavid van Moolenbroek 	if (family == AF_INET6)
265*00b67f09SDavid van Moolenbroek 		return (NULL);
266*00b67f09SDavid van Moolenbroek #endif
267*00b67f09SDavid van Moolenbroek 
268*00b67f09SDavid van Moolenbroek 	prev_state = get_state_for(family, ipnum, ipnum6);
269*00b67f09SDavid van Moolenbroek 	if (prev_state != NULL && prev_state->subtype == subtype)
270*00b67f09SDavid van Moolenbroek 		text = prev_state->text;
271*00b67f09SDavid van Moolenbroek 
272*00b67f09SDavid van Moolenbroek 	if (text == NULL) {
273*00b67f09SDavid van Moolenbroek 		switch (subtype) {
274*00b67f09SDavid van Moolenbroek 		case dns_geoip_country_code:
275*00b67f09SDavid van Moolenbroek 			if (family == AF_INET)
276*00b67f09SDavid van Moolenbroek 				text = GeoIP_country_code_by_ipnum(db, ipnum);
277*00b67f09SDavid van Moolenbroek #ifdef HAVE_GEOIP_V6
278*00b67f09SDavid van Moolenbroek 			else
279*00b67f09SDavid van Moolenbroek 				text = GeoIP_country_code_by_ipnum_v6(db,
280*00b67f09SDavid van Moolenbroek 								      *ipnum6);
281*00b67f09SDavid van Moolenbroek #endif
282*00b67f09SDavid van Moolenbroek 			break;
283*00b67f09SDavid van Moolenbroek 		case dns_geoip_country_code3:
284*00b67f09SDavid van Moolenbroek 			if (family == AF_INET)
285*00b67f09SDavid van Moolenbroek 				text = GeoIP_country_code3_by_ipnum(db, ipnum);
286*00b67f09SDavid van Moolenbroek #ifdef HAVE_GEOIP_V6
287*00b67f09SDavid van Moolenbroek 			else
288*00b67f09SDavid van Moolenbroek 				text = GeoIP_country_code3_by_ipnum_v6(db,
289*00b67f09SDavid van Moolenbroek 								       *ipnum6);
290*00b67f09SDavid van Moolenbroek #endif
291*00b67f09SDavid van Moolenbroek 			break;
292*00b67f09SDavid van Moolenbroek 		case dns_geoip_country_name:
293*00b67f09SDavid van Moolenbroek 			if (family == AF_INET)
294*00b67f09SDavid van Moolenbroek 				text = GeoIP_country_name_by_ipnum(db, ipnum);
295*00b67f09SDavid van Moolenbroek #ifdef HAVE_GEOIP_V6
296*00b67f09SDavid van Moolenbroek 			else
297*00b67f09SDavid van Moolenbroek 				text = GeoIP_country_name_by_ipnum_v6(db,
298*00b67f09SDavid van Moolenbroek 								      *ipnum6);
299*00b67f09SDavid van Moolenbroek #endif
300*00b67f09SDavid van Moolenbroek 			break;
301*00b67f09SDavid van Moolenbroek 		default:
302*00b67f09SDavid van Moolenbroek 			INSIST(0);
303*00b67f09SDavid van Moolenbroek 		}
304*00b67f09SDavid van Moolenbroek 
305*00b67f09SDavid van Moolenbroek 		set_state(family, ipnum, ipnum6, subtype,
306*00b67f09SDavid van Moolenbroek 			  NULL, NULL, NULL, text, 0);
307*00b67f09SDavid van Moolenbroek 	}
308*00b67f09SDavid van Moolenbroek 
309*00b67f09SDavid van Moolenbroek 	return (text);
310*00b67f09SDavid van Moolenbroek }
311*00b67f09SDavid van Moolenbroek 
312*00b67f09SDavid van Moolenbroek static char *
city_string(GeoIPRecord * record,dns_geoip_subtype_t subtype,int * maxlen)313*00b67f09SDavid van Moolenbroek city_string(GeoIPRecord *record, dns_geoip_subtype_t subtype, int *maxlen) {
314*00b67f09SDavid van Moolenbroek 	const char *s;
315*00b67f09SDavid van Moolenbroek 	char *deconst;
316*00b67f09SDavid van Moolenbroek 
317*00b67f09SDavid van Moolenbroek 	REQUIRE(record != NULL);
318*00b67f09SDavid van Moolenbroek 	REQUIRE(maxlen != NULL);
319*00b67f09SDavid van Moolenbroek 
320*00b67f09SDavid van Moolenbroek 	/* Set '*maxlen' to the maximum length of this subtype, if any */
321*00b67f09SDavid van Moolenbroek 	switch (subtype) {
322*00b67f09SDavid van Moolenbroek 	case dns_geoip_city_countrycode:
323*00b67f09SDavid van Moolenbroek 	case dns_geoip_city_region:
324*00b67f09SDavid van Moolenbroek 	case dns_geoip_city_continentcode:
325*00b67f09SDavid van Moolenbroek 		*maxlen = 2;
326*00b67f09SDavid van Moolenbroek 		break;
327*00b67f09SDavid van Moolenbroek 
328*00b67f09SDavid van Moolenbroek 	case dns_geoip_city_countrycode3:
329*00b67f09SDavid van Moolenbroek 		*maxlen = 3;
330*00b67f09SDavid van Moolenbroek 		break;
331*00b67f09SDavid van Moolenbroek 
332*00b67f09SDavid van Moolenbroek 	default:
333*00b67f09SDavid van Moolenbroek 		/* No fixed length; just use strcasecmp() for comparison */
334*00b67f09SDavid van Moolenbroek 		*maxlen = 255;
335*00b67f09SDavid van Moolenbroek 	}
336*00b67f09SDavid van Moolenbroek 
337*00b67f09SDavid van Moolenbroek 	switch (subtype) {
338*00b67f09SDavid van Moolenbroek 	case dns_geoip_city_countrycode:
339*00b67f09SDavid van Moolenbroek 		return (record->country_code);
340*00b67f09SDavid van Moolenbroek 	case dns_geoip_city_countrycode3:
341*00b67f09SDavid van Moolenbroek 		return (record->country_code3);
342*00b67f09SDavid van Moolenbroek 	case dns_geoip_city_countryname:
343*00b67f09SDavid van Moolenbroek 		return (record->country_name);
344*00b67f09SDavid van Moolenbroek 	case dns_geoip_city_region:
345*00b67f09SDavid van Moolenbroek 		return (record->region);
346*00b67f09SDavid van Moolenbroek 	case dns_geoip_city_regionname:
347*00b67f09SDavid van Moolenbroek 		s = GeoIP_region_name_by_code(record->country_code,
348*00b67f09SDavid van Moolenbroek 					      record->region);
349*00b67f09SDavid van Moolenbroek 		DE_CONST(s, deconst);
350*00b67f09SDavid van Moolenbroek 		return (deconst);
351*00b67f09SDavid van Moolenbroek 	case dns_geoip_city_name:
352*00b67f09SDavid van Moolenbroek 		return (record->city);
353*00b67f09SDavid van Moolenbroek 	case dns_geoip_city_postalcode:
354*00b67f09SDavid van Moolenbroek 		return (record->postal_code);
355*00b67f09SDavid van Moolenbroek 	case dns_geoip_city_continentcode:
356*00b67f09SDavid van Moolenbroek 		return (record->continent_code);
357*00b67f09SDavid van Moolenbroek 	case dns_geoip_city_timezonecode:
358*00b67f09SDavid van Moolenbroek 		s = GeoIP_time_zone_by_country_and_region(record->country_code,
359*00b67f09SDavid van Moolenbroek 							  record->region);
360*00b67f09SDavid van Moolenbroek 		DE_CONST(s, deconst);
361*00b67f09SDavid van Moolenbroek 		return (deconst);
362*00b67f09SDavid van Moolenbroek 	default:
363*00b67f09SDavid van Moolenbroek 		INSIST(0);
364*00b67f09SDavid van Moolenbroek 	}
365*00b67f09SDavid van Moolenbroek }
366*00b67f09SDavid van Moolenbroek 
367*00b67f09SDavid van Moolenbroek static isc_boolean_t
is_city(dns_geoip_subtype_t subtype)368*00b67f09SDavid van Moolenbroek is_city(dns_geoip_subtype_t subtype) {
369*00b67f09SDavid van Moolenbroek 	switch (subtype) {
370*00b67f09SDavid van Moolenbroek 	case dns_geoip_city_countrycode:
371*00b67f09SDavid van Moolenbroek 	case dns_geoip_city_countrycode3:
372*00b67f09SDavid van Moolenbroek 	case dns_geoip_city_countryname:
373*00b67f09SDavid van Moolenbroek 	case dns_geoip_city_region:
374*00b67f09SDavid van Moolenbroek 	case dns_geoip_city_regionname:
375*00b67f09SDavid van Moolenbroek 	case dns_geoip_city_name:
376*00b67f09SDavid van Moolenbroek 	case dns_geoip_city_postalcode:
377*00b67f09SDavid van Moolenbroek 	case dns_geoip_city_continentcode:
378*00b67f09SDavid van Moolenbroek 	case dns_geoip_city_timezonecode:
379*00b67f09SDavid van Moolenbroek 	case dns_geoip_city_metrocode:
380*00b67f09SDavid van Moolenbroek 	case dns_geoip_city_areacode:
381*00b67f09SDavid van Moolenbroek 		return (ISC_TRUE);
382*00b67f09SDavid van Moolenbroek 	default:
383*00b67f09SDavid van Moolenbroek 		return (ISC_FALSE);
384*00b67f09SDavid van Moolenbroek 	}
385*00b67f09SDavid van Moolenbroek }
386*00b67f09SDavid van Moolenbroek 
387*00b67f09SDavid van Moolenbroek /*
388*00b67f09SDavid van Moolenbroek  * GeoIPRecord lookups are performed if the previous lookup was
389*00b67f09SDavid van Moolenbroek  * from a different IP address than the current, or was for a search
390*00b67f09SDavid van Moolenbroek  * outside the City database.
391*00b67f09SDavid van Moolenbroek  */
392*00b67f09SDavid van Moolenbroek static GeoIPRecord *
city_lookup(GeoIP * db,dns_geoip_subtype_t subtype,unsigned int family,isc_uint32_t ipnum,const geoipv6_t * ipnum6)393*00b67f09SDavid van Moolenbroek city_lookup(GeoIP *db, dns_geoip_subtype_t subtype,
394*00b67f09SDavid van Moolenbroek 	    unsigned int family, isc_uint32_t ipnum, const geoipv6_t *ipnum6)
395*00b67f09SDavid van Moolenbroek {
396*00b67f09SDavid van Moolenbroek 	GeoIPRecord *record = NULL;
397*00b67f09SDavid van Moolenbroek 	geoip_state_t *prev_state = NULL;
398*00b67f09SDavid van Moolenbroek 
399*00b67f09SDavid van Moolenbroek 	REQUIRE(db != NULL);
400*00b67f09SDavid van Moolenbroek 
401*00b67f09SDavid van Moolenbroek #ifndef HAVE_GEOIP_V6
402*00b67f09SDavid van Moolenbroek 	/* no IPv6 support? give up now */
403*00b67f09SDavid van Moolenbroek 	if (family == AF_INET6)
404*00b67f09SDavid van Moolenbroek 		return (NULL);
405*00b67f09SDavid van Moolenbroek #endif
406*00b67f09SDavid van Moolenbroek 
407*00b67f09SDavid van Moolenbroek 	prev_state = get_state_for(family, ipnum, ipnum6);
408*00b67f09SDavid van Moolenbroek 	if (prev_state != NULL && is_city(prev_state->subtype))
409*00b67f09SDavid van Moolenbroek 		record = prev_state->record;
410*00b67f09SDavid van Moolenbroek 
411*00b67f09SDavid van Moolenbroek 	if (record == NULL) {
412*00b67f09SDavid van Moolenbroek 		if (family == AF_INET)
413*00b67f09SDavid van Moolenbroek 			record = GeoIP_record_by_ipnum(db, ipnum);
414*00b67f09SDavid van Moolenbroek #ifdef HAVE_GEOIP_V6
415*00b67f09SDavid van Moolenbroek 		else
416*00b67f09SDavid van Moolenbroek 			record = GeoIP_record_by_ipnum_v6(db, *ipnum6);
417*00b67f09SDavid van Moolenbroek #endif
418*00b67f09SDavid van Moolenbroek 		if (record == NULL)
419*00b67f09SDavid van Moolenbroek 			return (NULL);
420*00b67f09SDavid van Moolenbroek 
421*00b67f09SDavid van Moolenbroek 		set_state(family, ipnum, ipnum6, subtype,
422*00b67f09SDavid van Moolenbroek 			  record, NULL, NULL, NULL, 0);
423*00b67f09SDavid van Moolenbroek 	}
424*00b67f09SDavid van Moolenbroek 
425*00b67f09SDavid van Moolenbroek 	return (record);
426*00b67f09SDavid van Moolenbroek }
427*00b67f09SDavid van Moolenbroek 
428*00b67f09SDavid van Moolenbroek static char *
region_string(GeoIPRegion * region,dns_geoip_subtype_t subtype,int * maxlen)429*00b67f09SDavid van Moolenbroek region_string(GeoIPRegion *region, dns_geoip_subtype_t subtype, int *maxlen) {
430*00b67f09SDavid van Moolenbroek 	const char *s;
431*00b67f09SDavid van Moolenbroek 	char *deconst;
432*00b67f09SDavid van Moolenbroek 
433*00b67f09SDavid van Moolenbroek 	REQUIRE(region != NULL);
434*00b67f09SDavid van Moolenbroek 	REQUIRE(maxlen != NULL);
435*00b67f09SDavid van Moolenbroek 
436*00b67f09SDavid van Moolenbroek 	switch (subtype) {
437*00b67f09SDavid van Moolenbroek 	case dns_geoip_region_countrycode:
438*00b67f09SDavid van Moolenbroek 		*maxlen = 2;
439*00b67f09SDavid van Moolenbroek 		return (region->country_code);
440*00b67f09SDavid van Moolenbroek 	case dns_geoip_region_code:
441*00b67f09SDavid van Moolenbroek 		*maxlen = 2;
442*00b67f09SDavid van Moolenbroek 		return (region->region);
443*00b67f09SDavid van Moolenbroek 	case dns_geoip_region_name:
444*00b67f09SDavid van Moolenbroek 		*maxlen = 255;
445*00b67f09SDavid van Moolenbroek 		s = GeoIP_region_name_by_code(region->country_code,
446*00b67f09SDavid van Moolenbroek 					      region->region);
447*00b67f09SDavid van Moolenbroek 		DE_CONST(s, deconst);
448*00b67f09SDavid van Moolenbroek 		return (deconst);
449*00b67f09SDavid van Moolenbroek 	default:
450*00b67f09SDavid van Moolenbroek 		INSIST(0);
451*00b67f09SDavid van Moolenbroek 	}
452*00b67f09SDavid van Moolenbroek }
453*00b67f09SDavid van Moolenbroek 
454*00b67f09SDavid van Moolenbroek static isc_boolean_t
is_region(dns_geoip_subtype_t subtype)455*00b67f09SDavid van Moolenbroek is_region(dns_geoip_subtype_t subtype) {
456*00b67f09SDavid van Moolenbroek 	switch (subtype) {
457*00b67f09SDavid van Moolenbroek 	case dns_geoip_region_countrycode:
458*00b67f09SDavid van Moolenbroek 	case dns_geoip_region_code:
459*00b67f09SDavid van Moolenbroek 		return (ISC_TRUE);
460*00b67f09SDavid van Moolenbroek 	default:
461*00b67f09SDavid van Moolenbroek 		return (ISC_FALSE);
462*00b67f09SDavid van Moolenbroek 	}
463*00b67f09SDavid van Moolenbroek }
464*00b67f09SDavid van Moolenbroek 
465*00b67f09SDavid van Moolenbroek /*
466*00b67f09SDavid van Moolenbroek  * GeoIPRegion lookups are performed if the previous lookup was
467*00b67f09SDavid van Moolenbroek  * from a different IP address than the current, or was for a search
468*00b67f09SDavid van Moolenbroek  * outside the Region database.
469*00b67f09SDavid van Moolenbroek  */
470*00b67f09SDavid van Moolenbroek static GeoIPRegion *
region_lookup(GeoIP * db,dns_geoip_subtype_t subtype,isc_uint32_t ipnum)471*00b67f09SDavid van Moolenbroek region_lookup(GeoIP *db, dns_geoip_subtype_t subtype, isc_uint32_t ipnum) {
472*00b67f09SDavid van Moolenbroek 	GeoIPRegion *region = NULL;
473*00b67f09SDavid van Moolenbroek 	geoip_state_t *prev_state = NULL;
474*00b67f09SDavid van Moolenbroek 
475*00b67f09SDavid van Moolenbroek 	REQUIRE(db != NULL);
476*00b67f09SDavid van Moolenbroek 
477*00b67f09SDavid van Moolenbroek 	prev_state = get_state_for(AF_INET, ipnum, NULL);
478*00b67f09SDavid van Moolenbroek 	if (prev_state != NULL && is_region(prev_state->subtype))
479*00b67f09SDavid van Moolenbroek 		region = prev_state->region;
480*00b67f09SDavid van Moolenbroek 
481*00b67f09SDavid van Moolenbroek 	if (region == NULL) {
482*00b67f09SDavid van Moolenbroek 		region = GeoIP_region_by_ipnum(db, ipnum);
483*00b67f09SDavid van Moolenbroek 		if (region == NULL)
484*00b67f09SDavid van Moolenbroek 			return (NULL);
485*00b67f09SDavid van Moolenbroek 
486*00b67f09SDavid van Moolenbroek 		set_state(AF_INET, ipnum, NULL,
487*00b67f09SDavid van Moolenbroek 			  subtype, NULL, region, NULL, NULL, 0);
488*00b67f09SDavid van Moolenbroek 	}
489*00b67f09SDavid van Moolenbroek 
490*00b67f09SDavid van Moolenbroek 	return (region);
491*00b67f09SDavid van Moolenbroek }
492*00b67f09SDavid van Moolenbroek 
493*00b67f09SDavid van Moolenbroek /*
494*00b67f09SDavid van Moolenbroek  * ISP, Organization, AS Number and Domain lookups are performed if
495*00b67f09SDavid van Moolenbroek  * the previous lookup was from a different IP address than the current,
496*00b67f09SDavid van Moolenbroek  * or was for a search of a different subtype.
497*00b67f09SDavid van Moolenbroek  */
498*00b67f09SDavid van Moolenbroek static char *
name_lookup(GeoIP * db,dns_geoip_subtype_t subtype,isc_uint32_t ipnum)499*00b67f09SDavid van Moolenbroek name_lookup(GeoIP *db, dns_geoip_subtype_t subtype, isc_uint32_t ipnum) {
500*00b67f09SDavid van Moolenbroek 	char *name = NULL;
501*00b67f09SDavid van Moolenbroek 	geoip_state_t *prev_state = NULL;
502*00b67f09SDavid van Moolenbroek 
503*00b67f09SDavid van Moolenbroek 	REQUIRE(db != NULL);
504*00b67f09SDavid van Moolenbroek 
505*00b67f09SDavid van Moolenbroek 	prev_state = get_state_for(AF_INET, ipnum, NULL);
506*00b67f09SDavid van Moolenbroek 	if (prev_state != NULL && prev_state->subtype == subtype)
507*00b67f09SDavid van Moolenbroek 		name = prev_state->name;
508*00b67f09SDavid van Moolenbroek 
509*00b67f09SDavid van Moolenbroek 	if (name == NULL) {
510*00b67f09SDavid van Moolenbroek 		name = GeoIP_name_by_ipnum(db, ipnum);
511*00b67f09SDavid van Moolenbroek 		if (name == NULL)
512*00b67f09SDavid van Moolenbroek 			return (NULL);
513*00b67f09SDavid van Moolenbroek 
514*00b67f09SDavid van Moolenbroek 		set_state(AF_INET, ipnum, NULL,
515*00b67f09SDavid van Moolenbroek 			  subtype, NULL, NULL, name, NULL, 0);
516*00b67f09SDavid van Moolenbroek 	}
517*00b67f09SDavid van Moolenbroek 
518*00b67f09SDavid van Moolenbroek 	return (name);
519*00b67f09SDavid van Moolenbroek }
520*00b67f09SDavid van Moolenbroek 
521*00b67f09SDavid van Moolenbroek /*
522*00b67f09SDavid van Moolenbroek  * Netspeed lookups are performed if the previous lookup was from a
523*00b67f09SDavid van Moolenbroek  * different IP address than the current, or was for a search of a
524*00b67f09SDavid van Moolenbroek  * different subtype.
525*00b67f09SDavid van Moolenbroek  */
526*00b67f09SDavid van Moolenbroek static int
netspeed_lookup(GeoIP * db,dns_geoip_subtype_t subtype,isc_uint32_t ipnum)527*00b67f09SDavid van Moolenbroek netspeed_lookup(GeoIP *db, dns_geoip_subtype_t subtype, isc_uint32_t ipnum) {
528*00b67f09SDavid van Moolenbroek 	geoip_state_t *prev_state = NULL;
529*00b67f09SDavid van Moolenbroek 	isc_boolean_t found = ISC_FALSE;
530*00b67f09SDavid van Moolenbroek 	int id = -1;
531*00b67f09SDavid van Moolenbroek 
532*00b67f09SDavid van Moolenbroek 	REQUIRE(db != NULL);
533*00b67f09SDavid van Moolenbroek 
534*00b67f09SDavid van Moolenbroek 	prev_state = get_state_for(AF_INET, ipnum, NULL);
535*00b67f09SDavid van Moolenbroek 	if (prev_state != NULL && prev_state->subtype == subtype) {
536*00b67f09SDavid van Moolenbroek 		id = prev_state->id;
537*00b67f09SDavid van Moolenbroek 		found = ISC_TRUE;
538*00b67f09SDavid van Moolenbroek 	}
539*00b67f09SDavid van Moolenbroek 
540*00b67f09SDavid van Moolenbroek 	if (!found) {
541*00b67f09SDavid van Moolenbroek 		id = GeoIP_id_by_ipnum(db, ipnum);
542*00b67f09SDavid van Moolenbroek 		set_state(AF_INET, ipnum, NULL,
543*00b67f09SDavid van Moolenbroek 			  subtype, NULL, NULL, NULL, NULL, id);
544*00b67f09SDavid van Moolenbroek 	}
545*00b67f09SDavid van Moolenbroek 
546*00b67f09SDavid van Moolenbroek 	return (id);
547*00b67f09SDavid van Moolenbroek }
548*00b67f09SDavid van Moolenbroek #endif /* HAVE_GEOIP */
549*00b67f09SDavid van Moolenbroek 
550*00b67f09SDavid van Moolenbroek #define DB46(addr, geoip, name) \
551*00b67f09SDavid van Moolenbroek 	((addr->family == AF_INET) ? (geoip->name##_v4) : (geoip->name##_v6))
552*00b67f09SDavid van Moolenbroek 
553*00b67f09SDavid van Moolenbroek #ifdef HAVE_GEOIP
554*00b67f09SDavid van Moolenbroek /*
555*00b67f09SDavid van Moolenbroek  * Find the best database to answer a generic subtype
556*00b67f09SDavid van Moolenbroek  */
557*00b67f09SDavid van Moolenbroek static dns_geoip_subtype_t
fix_subtype(const isc_netaddr_t * reqaddr,const dns_geoip_databases_t * geoip,dns_geoip_subtype_t subtype)558*00b67f09SDavid van Moolenbroek fix_subtype(const isc_netaddr_t *reqaddr, const dns_geoip_databases_t *geoip,
559*00b67f09SDavid van Moolenbroek 	    dns_geoip_subtype_t subtype)
560*00b67f09SDavid van Moolenbroek {
561*00b67f09SDavid van Moolenbroek 	dns_geoip_subtype_t ret = subtype;
562*00b67f09SDavid van Moolenbroek 
563*00b67f09SDavid van Moolenbroek 	switch (subtype) {
564*00b67f09SDavid van Moolenbroek 	case dns_geoip_countrycode:
565*00b67f09SDavid van Moolenbroek 		if (DB46(reqaddr, geoip, city) != NULL)
566*00b67f09SDavid van Moolenbroek 			ret = dns_geoip_city_countrycode;
567*00b67f09SDavid van Moolenbroek 		else if (reqaddr->family == AF_INET && geoip->region != NULL)
568*00b67f09SDavid van Moolenbroek 			ret = dns_geoip_region_countrycode;
569*00b67f09SDavid van Moolenbroek 		else if (DB46(reqaddr, geoip, country) != NULL)
570*00b67f09SDavid van Moolenbroek 			ret = dns_geoip_country_code;
571*00b67f09SDavid van Moolenbroek 		break;
572*00b67f09SDavid van Moolenbroek 	case dns_geoip_countrycode3:
573*00b67f09SDavid van Moolenbroek 		if (DB46(reqaddr, geoip, city) != NULL)
574*00b67f09SDavid van Moolenbroek 			ret = dns_geoip_city_countrycode3;
575*00b67f09SDavid van Moolenbroek 		else if (DB46(reqaddr, geoip, country) != NULL)
576*00b67f09SDavid van Moolenbroek 			ret = dns_geoip_country_code3;
577*00b67f09SDavid van Moolenbroek 		break;
578*00b67f09SDavid van Moolenbroek 	case dns_geoip_countryname:
579*00b67f09SDavid van Moolenbroek 		if (DB46(reqaddr, geoip, city) != NULL)
580*00b67f09SDavid van Moolenbroek 			ret = dns_geoip_city_countryname;
581*00b67f09SDavid van Moolenbroek 		else if (DB46(reqaddr, geoip, country) != NULL)
582*00b67f09SDavid van Moolenbroek 			ret = dns_geoip_country_name;
583*00b67f09SDavid van Moolenbroek 		break;
584*00b67f09SDavid van Moolenbroek 	case dns_geoip_region:
585*00b67f09SDavid van Moolenbroek 		if (DB46(reqaddr, geoip, city) != NULL)
586*00b67f09SDavid van Moolenbroek 			ret = dns_geoip_city_region;
587*00b67f09SDavid van Moolenbroek 		else if (reqaddr->family == AF_INET && geoip->region != NULL)
588*00b67f09SDavid van Moolenbroek 			ret = dns_geoip_region_code;
589*00b67f09SDavid van Moolenbroek 		break;
590*00b67f09SDavid van Moolenbroek 	case dns_geoip_regionname:
591*00b67f09SDavid van Moolenbroek 		if (DB46(reqaddr, geoip, city) != NULL)
592*00b67f09SDavid van Moolenbroek 			ret = dns_geoip_city_regionname;
593*00b67f09SDavid van Moolenbroek 		else if (reqaddr->family == AF_INET && geoip->region != NULL)
594*00b67f09SDavid van Moolenbroek 			ret = dns_geoip_region_name;
595*00b67f09SDavid van Moolenbroek 		break;
596*00b67f09SDavid van Moolenbroek 	default:
597*00b67f09SDavid van Moolenbroek 		break;
598*00b67f09SDavid van Moolenbroek 	}
599*00b67f09SDavid van Moolenbroek 
600*00b67f09SDavid van Moolenbroek 	return (ret);
601*00b67f09SDavid van Moolenbroek }
602*00b67f09SDavid van Moolenbroek #endif /* HAVE_GEOIP */
603*00b67f09SDavid van Moolenbroek 
604*00b67f09SDavid van Moolenbroek isc_boolean_t
dns_geoip_match(const isc_netaddr_t * reqaddr,const dns_geoip_databases_t * geoip,const dns_geoip_elem_t * elt)605*00b67f09SDavid van Moolenbroek dns_geoip_match(const isc_netaddr_t *reqaddr,
606*00b67f09SDavid van Moolenbroek 		const dns_geoip_databases_t *geoip,
607*00b67f09SDavid van Moolenbroek 		const dns_geoip_elem_t *elt)
608*00b67f09SDavid van Moolenbroek {
609*00b67f09SDavid van Moolenbroek #ifndef HAVE_GEOIP
610*00b67f09SDavid van Moolenbroek 	UNUSED(reqaddr);
611*00b67f09SDavid van Moolenbroek 	UNUSED(geoip);
612*00b67f09SDavid van Moolenbroek 	UNUSED(elt);
613*00b67f09SDavid van Moolenbroek 
614*00b67f09SDavid van Moolenbroek 	return (ISC_FALSE);
615*00b67f09SDavid van Moolenbroek #else
616*00b67f09SDavid van Moolenbroek 	GeoIP *db;
617*00b67f09SDavid van Moolenbroek 	GeoIPRecord *record;
618*00b67f09SDavid van Moolenbroek 	GeoIPRegion *region;
619*00b67f09SDavid van Moolenbroek 	dns_geoip_subtype_t subtype;
620*00b67f09SDavid van Moolenbroek 	isc_uint32_t ipnum = 0;
621*00b67f09SDavid van Moolenbroek 	int maxlen = 0, id, family;
622*00b67f09SDavid van Moolenbroek 	const char *cs;
623*00b67f09SDavid van Moolenbroek 	char *s;
624*00b67f09SDavid van Moolenbroek #ifdef HAVE_GEOIP_V6
625*00b67f09SDavid van Moolenbroek 	const geoipv6_t *ipnum6 = NULL;
626*00b67f09SDavid van Moolenbroek #else
627*00b67f09SDavid van Moolenbroek 	const void *ipnum6 = NULL;
628*00b67f09SDavid van Moolenbroek #endif
629*00b67f09SDavid van Moolenbroek 
630*00b67f09SDavid van Moolenbroek 	INSIST(geoip != NULL);
631*00b67f09SDavid van Moolenbroek 
632*00b67f09SDavid van Moolenbroek 	family = reqaddr->family;
633*00b67f09SDavid van Moolenbroek 	switch (family) {
634*00b67f09SDavid van Moolenbroek 	case AF_INET:
635*00b67f09SDavid van Moolenbroek 		ipnum = ntohl(reqaddr->type.in.s_addr);
636*00b67f09SDavid van Moolenbroek 		break;
637*00b67f09SDavid van Moolenbroek 	case AF_INET6:
638*00b67f09SDavid van Moolenbroek #ifdef HAVE_GEOIP_V6
639*00b67f09SDavid van Moolenbroek 		ipnum6 = &reqaddr->type.in6;
640*00b67f09SDavid van Moolenbroek 		break;
641*00b67f09SDavid van Moolenbroek #else
642*00b67f09SDavid van Moolenbroek 		return (ISC_FALSE);
643*00b67f09SDavid van Moolenbroek #endif
644*00b67f09SDavid van Moolenbroek 	default:
645*00b67f09SDavid van Moolenbroek 		return (ISC_FALSE);
646*00b67f09SDavid van Moolenbroek 	}
647*00b67f09SDavid van Moolenbroek 
648*00b67f09SDavid van Moolenbroek 	subtype = fix_subtype(reqaddr, geoip, elt->subtype);
649*00b67f09SDavid van Moolenbroek 
650*00b67f09SDavid van Moolenbroek 	switch (subtype) {
651*00b67f09SDavid van Moolenbroek 	case dns_geoip_country_code:
652*00b67f09SDavid van Moolenbroek 		maxlen = 2;
653*00b67f09SDavid van Moolenbroek 		goto getcountry;
654*00b67f09SDavid van Moolenbroek 
655*00b67f09SDavid van Moolenbroek 	case dns_geoip_country_code3:
656*00b67f09SDavid van Moolenbroek 		maxlen = 3;
657*00b67f09SDavid van Moolenbroek 		goto getcountry;
658*00b67f09SDavid van Moolenbroek 
659*00b67f09SDavid van Moolenbroek 	case dns_geoip_country_name:
660*00b67f09SDavid van Moolenbroek 		maxlen = 255;
661*00b67f09SDavid van Moolenbroek  getcountry:
662*00b67f09SDavid van Moolenbroek 		db = DB46(reqaddr, geoip, country);
663*00b67f09SDavid van Moolenbroek 		if (db == NULL)
664*00b67f09SDavid van Moolenbroek 			return (ISC_FALSE);
665*00b67f09SDavid van Moolenbroek 
666*00b67f09SDavid van Moolenbroek 		INSIST(elt->as_string != NULL);
667*00b67f09SDavid van Moolenbroek 
668*00b67f09SDavid van Moolenbroek 		cs = country_lookup(db, subtype, family, ipnum, ipnum6);
669*00b67f09SDavid van Moolenbroek 		if (cs != NULL && strncasecmp(elt->as_string, cs, maxlen) == 0)
670*00b67f09SDavid van Moolenbroek 			return (ISC_TRUE);
671*00b67f09SDavid van Moolenbroek 		break;
672*00b67f09SDavid van Moolenbroek 
673*00b67f09SDavid van Moolenbroek 	case dns_geoip_city_countrycode:
674*00b67f09SDavid van Moolenbroek 	case dns_geoip_city_countrycode3:
675*00b67f09SDavid van Moolenbroek 	case dns_geoip_city_countryname:
676*00b67f09SDavid van Moolenbroek 	case dns_geoip_city_region:
677*00b67f09SDavid van Moolenbroek 	case dns_geoip_city_regionname:
678*00b67f09SDavid van Moolenbroek 	case dns_geoip_city_name:
679*00b67f09SDavid van Moolenbroek 	case dns_geoip_city_postalcode:
680*00b67f09SDavid van Moolenbroek 	case dns_geoip_city_continentcode:
681*00b67f09SDavid van Moolenbroek 	case dns_geoip_city_timezonecode:
682*00b67f09SDavid van Moolenbroek 		INSIST(elt->as_string != NULL);
683*00b67f09SDavid van Moolenbroek 
684*00b67f09SDavid van Moolenbroek 		db = DB46(reqaddr, geoip, city);
685*00b67f09SDavid van Moolenbroek 		if (db == NULL)
686*00b67f09SDavid van Moolenbroek 			return (ISC_FALSE);
687*00b67f09SDavid van Moolenbroek 
688*00b67f09SDavid van Moolenbroek 		record = city_lookup(db, subtype, family, ipnum, ipnum6);
689*00b67f09SDavid van Moolenbroek 		if (record == NULL)
690*00b67f09SDavid van Moolenbroek 			break;
691*00b67f09SDavid van Moolenbroek 
692*00b67f09SDavid van Moolenbroek 		s = city_string(record, subtype, &maxlen);
693*00b67f09SDavid van Moolenbroek 		INSIST(maxlen != 0);
694*00b67f09SDavid van Moolenbroek 		if (s != NULL && strncasecmp(elt->as_string, s, maxlen) == 0)
695*00b67f09SDavid van Moolenbroek 			return (ISC_TRUE);
696*00b67f09SDavid van Moolenbroek 		break;
697*00b67f09SDavid van Moolenbroek 
698*00b67f09SDavid van Moolenbroek 	case dns_geoip_city_metrocode:
699*00b67f09SDavid van Moolenbroek 		db = DB46(reqaddr, geoip, city);
700*00b67f09SDavid van Moolenbroek 		if (db == NULL)
701*00b67f09SDavid van Moolenbroek 			return (ISC_FALSE);
702*00b67f09SDavid van Moolenbroek 
703*00b67f09SDavid van Moolenbroek 		record = city_lookup(db, subtype, family, ipnum, ipnum6);
704*00b67f09SDavid van Moolenbroek 		if (record == NULL)
705*00b67f09SDavid van Moolenbroek 			break;
706*00b67f09SDavid van Moolenbroek 
707*00b67f09SDavid van Moolenbroek 		if (elt->as_int == record->metro_code)
708*00b67f09SDavid van Moolenbroek 			return (ISC_TRUE);
709*00b67f09SDavid van Moolenbroek 		break;
710*00b67f09SDavid van Moolenbroek 
711*00b67f09SDavid van Moolenbroek 	case dns_geoip_city_areacode:
712*00b67f09SDavid van Moolenbroek 		db = DB46(reqaddr, geoip, city);
713*00b67f09SDavid van Moolenbroek 		if (db == NULL)
714*00b67f09SDavid van Moolenbroek 			return (ISC_FALSE);
715*00b67f09SDavid van Moolenbroek 
716*00b67f09SDavid van Moolenbroek 		record = city_lookup(db, subtype, family, ipnum, ipnum6);
717*00b67f09SDavid van Moolenbroek 		if (record == NULL)
718*00b67f09SDavid van Moolenbroek 			break;
719*00b67f09SDavid van Moolenbroek 
720*00b67f09SDavid van Moolenbroek 		if (elt->as_int == record->area_code)
721*00b67f09SDavid van Moolenbroek 			return (ISC_TRUE);
722*00b67f09SDavid van Moolenbroek 		break;
723*00b67f09SDavid van Moolenbroek 
724*00b67f09SDavid van Moolenbroek 	case dns_geoip_region_countrycode:
725*00b67f09SDavid van Moolenbroek 	case dns_geoip_region_code:
726*00b67f09SDavid van Moolenbroek 	case dns_geoip_region_name:
727*00b67f09SDavid van Moolenbroek 	case dns_geoip_region:
728*00b67f09SDavid van Moolenbroek 		if (geoip->region == NULL)
729*00b67f09SDavid van Moolenbroek 			return (ISC_FALSE);
730*00b67f09SDavid van Moolenbroek 
731*00b67f09SDavid van Moolenbroek 		INSIST(elt->as_string != NULL);
732*00b67f09SDavid van Moolenbroek 
733*00b67f09SDavid van Moolenbroek 		/* Region DB is not supported for IPv6 */
734*00b67f09SDavid van Moolenbroek 		if (family == AF_INET6)
735*00b67f09SDavid van Moolenbroek 			return (ISC_FALSE);
736*00b67f09SDavid van Moolenbroek 
737*00b67f09SDavid van Moolenbroek 		region = region_lookup(geoip->region, subtype, ipnum);
738*00b67f09SDavid van Moolenbroek 		if (region == NULL)
739*00b67f09SDavid van Moolenbroek 			break;
740*00b67f09SDavid van Moolenbroek 
741*00b67f09SDavid van Moolenbroek 		s = region_string(region, subtype, &maxlen);
742*00b67f09SDavid van Moolenbroek 		INSIST(maxlen != 0);
743*00b67f09SDavid van Moolenbroek 		if (s != NULL && strncasecmp(elt->as_string, s, maxlen) == 0)
744*00b67f09SDavid van Moolenbroek 			return (ISC_TRUE);
745*00b67f09SDavid van Moolenbroek 		break;
746*00b67f09SDavid van Moolenbroek 
747*00b67f09SDavid van Moolenbroek 	case dns_geoip_isp_name:
748*00b67f09SDavid van Moolenbroek 		db = geoip->isp;
749*00b67f09SDavid van Moolenbroek 		goto getname;
750*00b67f09SDavid van Moolenbroek 
751*00b67f09SDavid van Moolenbroek 	case dns_geoip_org_name:
752*00b67f09SDavid van Moolenbroek 		db = geoip->org;
753*00b67f09SDavid van Moolenbroek 		goto getname;
754*00b67f09SDavid van Moolenbroek 
755*00b67f09SDavid van Moolenbroek 	case dns_geoip_as_asnum:
756*00b67f09SDavid van Moolenbroek 		db = geoip->as;
757*00b67f09SDavid van Moolenbroek 		goto getname;
758*00b67f09SDavid van Moolenbroek 
759*00b67f09SDavid van Moolenbroek 	case dns_geoip_domain_name:
760*00b67f09SDavid van Moolenbroek 		db = geoip->domain;
761*00b67f09SDavid van Moolenbroek 
762*00b67f09SDavid van Moolenbroek  getname:
763*00b67f09SDavid van Moolenbroek 		if (db == NULL)
764*00b67f09SDavid van Moolenbroek 			return (ISC_FALSE);
765*00b67f09SDavid van Moolenbroek 
766*00b67f09SDavid van Moolenbroek 		INSIST(elt->as_string != NULL);
767*00b67f09SDavid van Moolenbroek 		/* ISP, Org, AS, and Domain are not supported for IPv6 */
768*00b67f09SDavid van Moolenbroek 		if (family == AF_INET6)
769*00b67f09SDavid van Moolenbroek 			return (ISC_FALSE);
770*00b67f09SDavid van Moolenbroek 
771*00b67f09SDavid van Moolenbroek 		s = name_lookup(db, subtype, ipnum);
772*00b67f09SDavid van Moolenbroek 		if (s != NULL) {
773*00b67f09SDavid van Moolenbroek 			size_t l;
774*00b67f09SDavid van Moolenbroek 			if (strcasecmp(elt->as_string, s) == 0)
775*00b67f09SDavid van Moolenbroek 				return (ISC_TRUE);
776*00b67f09SDavid van Moolenbroek 			if (subtype != dns_geoip_as_asnum)
777*00b67f09SDavid van Moolenbroek 				break;
778*00b67f09SDavid van Moolenbroek 			/*
779*00b67f09SDavid van Moolenbroek 			 * Just check if the ASNNNN value matches.
780*00b67f09SDavid van Moolenbroek 			 */
781*00b67f09SDavid van Moolenbroek 			l = strlen(elt->as_string);
782*00b67f09SDavid van Moolenbroek 			if (l > 0U && strchr(elt->as_string, ' ') == NULL &&
783*00b67f09SDavid van Moolenbroek 			    strncasecmp(elt->as_string, s, l) == 0 &&
784*00b67f09SDavid van Moolenbroek 			    s[l] == ' ')
785*00b67f09SDavid van Moolenbroek 				return (ISC_TRUE);
786*00b67f09SDavid van Moolenbroek 		}
787*00b67f09SDavid van Moolenbroek 		break;
788*00b67f09SDavid van Moolenbroek 
789*00b67f09SDavid van Moolenbroek 	case dns_geoip_netspeed_id:
790*00b67f09SDavid van Moolenbroek 		INSIST(geoip->netspeed != NULL);
791*00b67f09SDavid van Moolenbroek 
792*00b67f09SDavid van Moolenbroek 		/* Netspeed DB is not supported for IPv6 */
793*00b67f09SDavid van Moolenbroek 		if (family == AF_INET6)
794*00b67f09SDavid van Moolenbroek 			return (ISC_FALSE);
795*00b67f09SDavid van Moolenbroek 
796*00b67f09SDavid van Moolenbroek 		id = netspeed_lookup(geoip->netspeed, subtype, ipnum);
797*00b67f09SDavid van Moolenbroek 		if (id == elt->as_int)
798*00b67f09SDavid van Moolenbroek 			return (ISC_TRUE);
799*00b67f09SDavid van Moolenbroek 		break;
800*00b67f09SDavid van Moolenbroek 
801*00b67f09SDavid van Moolenbroek 	case dns_geoip_countrycode:
802*00b67f09SDavid van Moolenbroek 	case dns_geoip_countrycode3:
803*00b67f09SDavid van Moolenbroek 	case dns_geoip_countryname:
804*00b67f09SDavid van Moolenbroek 	case dns_geoip_regionname:
805*00b67f09SDavid van Moolenbroek 		/*
806*00b67f09SDavid van Moolenbroek 		 * If these were not remapped by fix_subtype(),
807*00b67f09SDavid van Moolenbroek 		 * the database was unavailable. Always return false.
808*00b67f09SDavid van Moolenbroek 		 */
809*00b67f09SDavid van Moolenbroek 		break;
810*00b67f09SDavid van Moolenbroek 
811*00b67f09SDavid van Moolenbroek 	default:
812*00b67f09SDavid van Moolenbroek 		INSIST(0);
813*00b67f09SDavid van Moolenbroek 	}
814*00b67f09SDavid van Moolenbroek 
815*00b67f09SDavid van Moolenbroek 	return (ISC_FALSE);
816*00b67f09SDavid van Moolenbroek #endif
817*00b67f09SDavid van Moolenbroek }
818*00b67f09SDavid van Moolenbroek 
819*00b67f09SDavid van Moolenbroek void
dns_geoip_shutdown(void)820*00b67f09SDavid van Moolenbroek dns_geoip_shutdown(void) {
821*00b67f09SDavid van Moolenbroek #ifdef HAVE_GEOIP
822*00b67f09SDavid van Moolenbroek 	GeoIP_cleanup();
823*00b67f09SDavid van Moolenbroek #ifdef ISC_PLATFORM_USETHREADS
824*00b67f09SDavid van Moolenbroek 	if (state_mctx != NULL)
825*00b67f09SDavid van Moolenbroek 		isc_mem_detach(&state_mctx);
826*00b67f09SDavid van Moolenbroek #endif
827*00b67f09SDavid van Moolenbroek #else
828*00b67f09SDavid van Moolenbroek 	return;
829*00b67f09SDavid van Moolenbroek #endif
830*00b67f09SDavid van Moolenbroek }
831