1 /*
2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3  *
4  * This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7  *
8  * See the COPYRIGHT file distributed with this work for additional
9  * information regarding copyright ownership.
10  */
11 
12 /*! \file */
13 
14 #include <inttypes.h>
15 #include <stdbool.h>
16 #include <stdlib.h>
17 
18 /*
19  * This file is only built and linked if GeoIP2 has been configured.
20  */
21 #include <math.h>
22 #include <maxminddb.h>
23 
24 #include <isc/mem.h>
25 #include <isc/once.h>
26 #include <isc/sockaddr.h>
27 #include <isc/string.h>
28 #include <isc/thread.h>
29 #include <isc/util.h>
30 
31 #include <dns/acl.h>
32 #include <dns/geoip.h>
33 #ifndef WIN32
34 #include <netinet/in.h>
35 #else /* ifndef WIN32 */
36 #ifndef _WINSOCKAPI_
37 #define _WINSOCKAPI_ /* Prevent inclusion of winsock.h in windows.h */
38 #endif		     /* ifndef _WINSOCKAPI_ */
39 #include <winsock2.h>
40 #endif /* WIN32 */
41 #include <dns/log.h>
42 
43 /*
44  * This structure preserves state from the previous GeoIP lookup,
45  * so that successive lookups for the same data from the same IP
46  * address will not require repeated database lookups.
47  * This should improve performance somewhat.
48  *
49  * For all lookups we preserve pointers to the MMDB_lookup_result_s
50  * and MMDB_entry_s structures, a pointer to the database from which
51  * the lookup was answered, and a copy of the request address.
52  *
53  * If the next geoip ACL lookup is for the same database and from the
54  * same address, we can reuse the MMDB entry without repeating the lookup.
55  * This is for the case when a single query has to process multiple
56  * geoip ACLs: for example, when there are multiple views with
57  * match-clients statements that search for different countries.
58  *
59  * (XXX: Currently the persistent state is stored in thread specific
60  * memory, but it could more simply be stored in the client object.
61  * Also multiple entries could be stored in case the ACLs require
62  * searching in more than one GeoIP database.)
63  */
64 
65 typedef struct geoip_state {
66 	uint16_t subtype;
67 	const MMDB_s *db;
68 	isc_netaddr_t addr;
69 	MMDB_lookup_result_s mmresult;
70 	MMDB_entry_s entry;
71 } geoip_state_t;
72 
73 ISC_THREAD_LOCAL geoip_state_t geoip_state = { 0 };
74 
75 static void
set_state(const MMDB_s * db,const isc_netaddr_t * addr,MMDB_lookup_result_s mmresult,MMDB_entry_s entry)76 set_state(const MMDB_s *db, const isc_netaddr_t *addr,
77 	  MMDB_lookup_result_s mmresult, MMDB_entry_s entry) {
78 	geoip_state.db = db;
79 	geoip_state.addr = *addr;
80 	geoip_state.mmresult = mmresult;
81 	geoip_state.entry = entry;
82 }
83 
84 static geoip_state_t *
get_entry_for(MMDB_s * const db,const isc_netaddr_t * addr)85 get_entry_for(MMDB_s *const db, const isc_netaddr_t *addr) {
86 	isc_sockaddr_t sa;
87 	MMDB_lookup_result_s match;
88 	int err;
89 
90 	if (db == geoip_state.db && isc_netaddr_equal(addr, &geoip_state.addr))
91 	{
92 		return (&geoip_state);
93 	}
94 
95 	isc_sockaddr_fromnetaddr(&sa, addr, 0);
96 	match = MMDB_lookup_sockaddr(db, &sa.type.sa, &err);
97 	if (err != MMDB_SUCCESS || !match.found_entry) {
98 		return (NULL);
99 	}
100 
101 	set_state(db, addr, match, match.entry);
102 
103 	return (&geoip_state);
104 }
105 
106 static dns_geoip_subtype_t
fix_subtype(const dns_geoip_databases_t * geoip,dns_geoip_subtype_t subtype)107 fix_subtype(const dns_geoip_databases_t *geoip, dns_geoip_subtype_t subtype) {
108 	dns_geoip_subtype_t ret = subtype;
109 
110 	switch (subtype) {
111 	case dns_geoip_countrycode:
112 		if (geoip->city != NULL) {
113 			ret = dns_geoip_city_countrycode;
114 		} else if (geoip->country != NULL) {
115 			ret = dns_geoip_country_code;
116 		}
117 		break;
118 	case dns_geoip_countryname:
119 		if (geoip->city != NULL) {
120 			ret = dns_geoip_city_countryname;
121 		} else if (geoip->country != NULL) {
122 			ret = dns_geoip_country_name;
123 		}
124 		break;
125 	case dns_geoip_continentcode:
126 		if (geoip->city != NULL) {
127 			ret = dns_geoip_city_continentcode;
128 		} else if (geoip->country != NULL) {
129 			ret = dns_geoip_country_continentcode;
130 		}
131 		break;
132 	case dns_geoip_continent:
133 		if (geoip->city != NULL) {
134 			ret = dns_geoip_city_continent;
135 		} else if (geoip->country != NULL) {
136 			ret = dns_geoip_country_continent;
137 		}
138 		break;
139 	case dns_geoip_region:
140 		if (geoip->city != NULL) {
141 			ret = dns_geoip_city_region;
142 		}
143 		break;
144 	case dns_geoip_regionname:
145 		if (geoip->city != NULL) {
146 			ret = dns_geoip_city_regionname;
147 		}
148 	default:
149 		break;
150 	}
151 
152 	return (ret);
153 }
154 
155 static MMDB_s *
geoip2_database(const dns_geoip_databases_t * geoip,dns_geoip_subtype_t subtype)156 geoip2_database(const dns_geoip_databases_t *geoip,
157 		dns_geoip_subtype_t subtype) {
158 	switch (subtype) {
159 	case dns_geoip_country_code:
160 	case dns_geoip_country_name:
161 	case dns_geoip_country_continentcode:
162 	case dns_geoip_country_continent:
163 		return (geoip->country);
164 
165 	case dns_geoip_city_countrycode:
166 	case dns_geoip_city_countryname:
167 	case dns_geoip_city_continentcode:
168 	case dns_geoip_city_continent:
169 	case dns_geoip_city_region:
170 	case dns_geoip_city_regionname:
171 	case dns_geoip_city_name:
172 	case dns_geoip_city_postalcode:
173 	case dns_geoip_city_timezonecode:
174 	case dns_geoip_city_metrocode:
175 	case dns_geoip_city_areacode:
176 		return (geoip->city);
177 
178 	case dns_geoip_isp_name:
179 		return (geoip->isp);
180 
181 	case dns_geoip_as_asnum:
182 	case dns_geoip_org_name:
183 		return (geoip->as);
184 
185 	case dns_geoip_domain_name:
186 		return (geoip->domain);
187 
188 	default:
189 		/*
190 		 * All other subtypes are unavailable in GeoIP2.
191 		 */
192 		return (NULL);
193 	}
194 }
195 
196 static bool
match_string(MMDB_entry_data_s * value,const char * str)197 match_string(MMDB_entry_data_s *value, const char *str) {
198 	REQUIRE(str != NULL);
199 
200 	if (value == NULL || !value->has_data ||
201 	    value->type != MMDB_DATA_TYPE_UTF8_STRING ||
202 	    value->utf8_string == NULL)
203 	{
204 		return (false);
205 	}
206 
207 	return (strncasecmp(value->utf8_string, str, value->data_size) == 0);
208 }
209 
210 static bool
match_int(MMDB_entry_data_s * value,const uint32_t ui32)211 match_int(MMDB_entry_data_s *value, const uint32_t ui32) {
212 	if (value == NULL || !value->has_data ||
213 	    (value->type != MMDB_DATA_TYPE_UINT32 &&
214 	     value->type != MMDB_DATA_TYPE_UINT16))
215 	{
216 		return (false);
217 	}
218 
219 	return (value->uint32 == ui32);
220 }
221 
222 bool
dns_geoip_match(const isc_netaddr_t * reqaddr,const dns_geoip_databases_t * geoip,const dns_geoip_elem_t * elt)223 dns_geoip_match(const isc_netaddr_t *reqaddr,
224 		const dns_geoip_databases_t *geoip,
225 		const dns_geoip_elem_t *elt) {
226 	MMDB_s *db = NULL;
227 	MMDB_entry_data_s value;
228 	geoip_state_t *state = NULL;
229 	dns_geoip_subtype_t subtype;
230 	const char *s = NULL;
231 	int ret;
232 
233 	REQUIRE(reqaddr != NULL);
234 	REQUIRE(elt != NULL);
235 	REQUIRE(geoip != NULL);
236 
237 	subtype = fix_subtype(geoip, elt->subtype);
238 	db = geoip2_database(geoip, subtype);
239 	if (db == NULL) {
240 		return (false);
241 	}
242 
243 	state = get_entry_for(db, reqaddr);
244 	if (state == NULL) {
245 		return (false);
246 	}
247 
248 	switch (subtype) {
249 	case dns_geoip_country_code:
250 	case dns_geoip_city_countrycode:
251 		ret = MMDB_get_value(&state->entry, &value, "country",
252 				     "iso_code", (char *)0);
253 		if (ret == MMDB_SUCCESS) {
254 			return (match_string(&value, elt->as_string));
255 		}
256 		break;
257 
258 	case dns_geoip_country_name:
259 	case dns_geoip_city_countryname:
260 		ret = MMDB_get_value(&state->entry, &value, "country", "names",
261 				     "en", (char *)0);
262 		if (ret == MMDB_SUCCESS) {
263 			return (match_string(&value, elt->as_string));
264 		}
265 		break;
266 
267 	case dns_geoip_country_continentcode:
268 	case dns_geoip_city_continentcode:
269 		ret = MMDB_get_value(&state->entry, &value, "continent", "code",
270 				     (char *)0);
271 		if (ret == MMDB_SUCCESS) {
272 			return (match_string(&value, elt->as_string));
273 		}
274 		break;
275 
276 	case dns_geoip_country_continent:
277 	case dns_geoip_city_continent:
278 		ret = MMDB_get_value(&state->entry, &value, "continent",
279 				     "names", "en", (char *)0);
280 		if (ret == MMDB_SUCCESS) {
281 			return (match_string(&value, elt->as_string));
282 		}
283 		break;
284 
285 	case dns_geoip_region:
286 	case dns_geoip_city_region:
287 		ret = MMDB_get_value(&state->entry, &value, "subdivisions", "0",
288 				     "iso_code", (char *)0);
289 		if (ret == MMDB_SUCCESS) {
290 			return (match_string(&value, elt->as_string));
291 		}
292 		break;
293 
294 	case dns_geoip_regionname:
295 	case dns_geoip_city_regionname:
296 		ret = MMDB_get_value(&state->entry, &value, "subdivisions", "0",
297 				     "names", "en", (char *)0);
298 		if (ret == MMDB_SUCCESS) {
299 			return (match_string(&value, elt->as_string));
300 		}
301 		break;
302 
303 	case dns_geoip_city_name:
304 		ret = MMDB_get_value(&state->entry, &value, "city", "names",
305 				     "en", (char *)0);
306 		if (ret == MMDB_SUCCESS) {
307 			return (match_string(&value, elt->as_string));
308 		}
309 		break;
310 
311 	case dns_geoip_city_postalcode:
312 		ret = MMDB_get_value(&state->entry, &value, "postal", "code",
313 				     (char *)0);
314 		if (ret == MMDB_SUCCESS) {
315 			return (match_string(&value, elt->as_string));
316 		}
317 		break;
318 
319 	case dns_geoip_city_timezonecode:
320 		ret = MMDB_get_value(&state->entry, &value, "location",
321 				     "time_zone", (char *)0);
322 		if (ret == MMDB_SUCCESS) {
323 			return (match_string(&value, elt->as_string));
324 		}
325 		break;
326 
327 	case dns_geoip_city_metrocode:
328 		ret = MMDB_get_value(&state->entry, &value, "location",
329 				     "metro_code", (char *)0);
330 		if (ret == MMDB_SUCCESS) {
331 			return (match_string(&value, elt->as_string));
332 		}
333 		break;
334 
335 	case dns_geoip_isp_name:
336 		ret = MMDB_get_value(&state->entry, &value, "isp", (char *)0);
337 		if (ret == MMDB_SUCCESS) {
338 			return (match_string(&value, elt->as_string));
339 		}
340 		break;
341 
342 	case dns_geoip_as_asnum:
343 		INSIST(elt->as_string != NULL);
344 
345 		ret = MMDB_get_value(&state->entry, &value,
346 				     "autonomous_system_number", (char *)0);
347 		if (ret == MMDB_SUCCESS) {
348 			int i;
349 			s = elt->as_string;
350 			if (strncasecmp(s, "AS", 2) == 0) {
351 				s += 2;
352 			}
353 			i = strtol(s, NULL, 10);
354 			return (match_int(&value, i));
355 		}
356 		break;
357 
358 	case dns_geoip_org_name:
359 		ret = MMDB_get_value(&state->entry, &value,
360 				     "autonomous_system_organization",
361 				     (char *)0);
362 		if (ret == MMDB_SUCCESS) {
363 			return (match_string(&value, elt->as_string));
364 		}
365 		break;
366 
367 	case dns_geoip_domain_name:
368 		ret = MMDB_get_value(&state->entry, &value, "domain",
369 				     (char *)0);
370 		if (ret == MMDB_SUCCESS) {
371 			return (match_string(&value, elt->as_string));
372 		}
373 		break;
374 
375 	default:
376 		/*
377 		 * For any other subtype, we assume the database was
378 		 * unavailable and return false.
379 		 */
380 		return (false);
381 	}
382 
383 	/*
384 	 * No database matched: return false.
385 	 */
386 	return (false);
387 }
388