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