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