xref: /netbsd-src/external/mpl/bind/dist/tests/dns/geoip_test.c (revision 345cf9fb81bd0411c53e25d62cd93bdcaa865312)
1 /*	$NetBSD: geoip_test.c,v 1.2 2024/02/21 22:52:50 christos Exp $	*/
2 
3 /*
4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5  *
6  * SPDX-License-Identifier: MPL-2.0
7  *
8  * This Source Code Form is subject to the terms of the Mozilla Public
9  * License, v. 2.0. If a copy of the MPL was not distributed with this
10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11  *
12  * See the COPYRIGHT file distributed with this work for additional
13  * information regarding copyright ownership.
14  */
15 
16 #include <inttypes.h>
17 #include <sched.h> /* IWYU pragma: keep */
18 #include <setjmp.h>
19 #include <stdarg.h>
20 #include <stdbool.h>
21 #include <stddef.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 
26 #define UNIT_TESTING
27 #include <cmocka.h>
28 #include <maxminddb.h>
29 
30 #include <isc/dir.h>
31 #include <isc/print.h>
32 #include <isc/string.h>
33 #include <isc/types.h>
34 #include <isc/util.h>
35 
36 #include <dns/geoip.h>
37 
38 #include "geoip2.c"
39 
40 #include <tests/dns.h>
41 
42 static dns_geoip_databases_t geoip;
43 
44 static MMDB_s geoip_country, geoip_city, geoip_as, geoip_isp, geoip_domain;
45 
46 static void
47 load_geoip(const char *dir);
48 static void
49 close_geoip(void);
50 
51 static int
52 setup_test(void **state) {
53 	UNUSED(state);
54 
55 	/* Use databases from the geoip system test */
56 	load_geoip(TEST_GEOIP_DATA);
57 
58 	return (0);
59 }
60 
61 static int
62 teardown_test(void **state) {
63 	UNUSED(state);
64 
65 	close_geoip();
66 
67 	return (0);
68 }
69 
70 static MMDB_s *
71 open_geoip2(const char *dir, const char *dbfile, MMDB_s *mmdb) {
72 	char pathbuf[PATH_MAX];
73 	int ret;
74 
75 	snprintf(pathbuf, sizeof(pathbuf), "%s/%s", dir, dbfile);
76 	ret = MMDB_open(pathbuf, MMDB_MODE_MMAP, mmdb);
77 	if (ret == MMDB_SUCCESS) {
78 		return (mmdb);
79 	}
80 
81 	return (NULL);
82 }
83 
84 static void
85 load_geoip(const char *dir) {
86 	geoip.country = open_geoip2(dir, "GeoIP2-Country.mmdb", &geoip_country);
87 	geoip.city = open_geoip2(dir, "GeoIP2-City.mmdb", &geoip_city);
88 	geoip.as = open_geoip2(dir, "GeoLite2-ASN.mmdb", &geoip_as);
89 	geoip.isp = open_geoip2(dir, "GeoIP2-ISP.mmdb", &geoip_isp);
90 	geoip.domain = open_geoip2(dir, "GeoIP2-Domain.mmdb", &geoip_domain);
91 }
92 
93 static void
94 close_geoip(void) {
95 	MMDB_close(&geoip_country);
96 	MMDB_close(&geoip_city);
97 	MMDB_close(&geoip_as);
98 	MMDB_close(&geoip_isp);
99 	MMDB_close(&geoip_domain);
100 }
101 
102 static bool
103 /* Check if an MMDB entry of a given subtype exists for the given IP */
104 entry_exists(dns_geoip_subtype_t subtype, const char *addr) {
105 	struct in6_addr in6;
106 	struct in_addr in4;
107 	isc_netaddr_t na;
108 	MMDB_s *db;
109 
110 	if (inet_pton(AF_INET6, addr, &in6) == 1) {
111 		isc_netaddr_fromin6(&na, &in6);
112 	} else if (inet_pton(AF_INET, addr, &in4) == 1) {
113 		isc_netaddr_fromin(&na, &in4);
114 	} else {
115 		UNREACHABLE();
116 	}
117 
118 	db = geoip2_database(&geoip, fix_subtype(&geoip, subtype));
119 
120 	return (db != NULL && get_entry_for(db, &na) != NULL);
121 }
122 
123 /*
124  * Baseline test - check if get_entry_for() works as expected, i.e. that its
125  * return values are consistent with the contents of the test MMDBs found in
126  * bin/tests/system/geoip2/data/ (10.53.0.1 and fd92:7065:b8e:ffff::1 should be
127  * present in all databases, 192.0.2.128 should only be present in the country
128  * database, ::1 should be absent from all databases).
129  */
130 ISC_RUN_TEST_IMPL(baseline) {
131 	dns_geoip_subtype_t subtype;
132 
133 	UNUSED(state);
134 
135 	subtype = dns_geoip_city_name;
136 
137 	assert_true(entry_exists(subtype, "10.53.0.1"));
138 	assert_false(entry_exists(subtype, "192.0.2.128"));
139 	assert_true(entry_exists(subtype, "fd92:7065:b8e:ffff::1"));
140 	assert_false(entry_exists(subtype, "::1"));
141 
142 	subtype = dns_geoip_country_name;
143 
144 	assert_true(entry_exists(subtype, "10.53.0.1"));
145 	assert_true(entry_exists(subtype, "192.0.2.128"));
146 	assert_true(entry_exists(subtype, "fd92:7065:b8e:ffff::1"));
147 	assert_false(entry_exists(subtype, "::1"));
148 
149 	subtype = dns_geoip_domain_name;
150 
151 	assert_true(entry_exists(subtype, "10.53.0.1"));
152 	assert_false(entry_exists(subtype, "192.0.2.128"));
153 	assert_true(entry_exists(subtype, "fd92:7065:b8e:ffff::1"));
154 	assert_false(entry_exists(subtype, "::1"));
155 
156 	subtype = dns_geoip_isp_name;
157 
158 	assert_true(entry_exists(subtype, "10.53.0.1"));
159 	assert_false(entry_exists(subtype, "192.0.2.128"));
160 	assert_true(entry_exists(subtype, "fd92:7065:b8e:ffff::1"));
161 	assert_false(entry_exists(subtype, "::1"));
162 
163 	subtype = dns_geoip_as_asnum;
164 
165 	assert_true(entry_exists(subtype, "10.53.0.1"));
166 	assert_false(entry_exists(subtype, "192.0.2.128"));
167 	assert_true(entry_exists(subtype, "fd92:7065:b8e:ffff::1"));
168 	assert_false(entry_exists(subtype, "::1"));
169 }
170 
171 static bool
172 do_lookup_string(const char *addr, dns_geoip_subtype_t subtype,
173 		 const char *string) {
174 	dns_geoip_elem_t elt;
175 	struct in_addr in4;
176 	isc_netaddr_t na;
177 	int n;
178 
179 	n = inet_pton(AF_INET, addr, &in4);
180 	assert_int_equal(n, 1);
181 	isc_netaddr_fromin(&na, &in4);
182 
183 	elt.subtype = subtype;
184 	strlcpy(elt.as_string, string, sizeof(elt.as_string));
185 
186 	return (dns_geoip_match(&na, &geoip, &elt));
187 }
188 
189 static bool
190 do_lookup_string_v6(const char *addr, dns_geoip_subtype_t subtype,
191 		    const char *string) {
192 	dns_geoip_elem_t elt;
193 	struct in6_addr in6;
194 	isc_netaddr_t na;
195 	int n;
196 
197 	n = inet_pton(AF_INET6, addr, &in6);
198 	assert_int_equal(n, 1);
199 	isc_netaddr_fromin6(&na, &in6);
200 
201 	elt.subtype = subtype;
202 	strlcpy(elt.as_string, string, sizeof(elt.as_string));
203 
204 	return (dns_geoip_match(&na, &geoip, &elt));
205 }
206 
207 /* GeoIP country matching */
208 ISC_RUN_TEST_IMPL(country) {
209 	bool match;
210 
211 	UNUSED(state);
212 
213 	if (geoip.country == NULL) {
214 		skip();
215 	}
216 
217 	match = do_lookup_string("10.53.0.1", dns_geoip_country_code, "AU");
218 	assert_true(match);
219 
220 	match = do_lookup_string("10.53.0.1", dns_geoip_country_name,
221 				 "Australia");
222 	assert_true(match);
223 
224 	match = do_lookup_string("192.0.2.128", dns_geoip_country_code, "O1");
225 	assert_true(match);
226 
227 	match = do_lookup_string("192.0.2.128", dns_geoip_country_name,
228 				 "Other");
229 	assert_true(match);
230 }
231 
232 /* GeoIP country (ipv6) matching */
233 ISC_RUN_TEST_IMPL(country_v6) {
234 	bool match;
235 
236 	UNUSED(state);
237 
238 	if (geoip.country == NULL) {
239 		skip();
240 	}
241 
242 	match = do_lookup_string_v6("fd92:7065:b8e:ffff::1",
243 				    dns_geoip_country_code, "AU");
244 	assert_true(match);
245 
246 	match = do_lookup_string_v6("fd92:7065:b8e:ffff::1",
247 				    dns_geoip_country_name, "Australia");
248 	assert_true(match);
249 }
250 
251 /* GeoIP city (ipv4) matching */
252 ISC_RUN_TEST_IMPL(city) {
253 	bool match;
254 
255 	UNUSED(state);
256 
257 	if (geoip.city == NULL) {
258 		skip();
259 	}
260 
261 	match = do_lookup_string("10.53.0.1", dns_geoip_city_continentcode,
262 				 "NA");
263 	assert_true(match);
264 
265 	match = do_lookup_string("10.53.0.1", dns_geoip_city_countrycode, "US");
266 	assert_true(match);
267 
268 	match = do_lookup_string("10.53.0.1", dns_geoip_city_countryname,
269 				 "United States");
270 	assert_true(match);
271 
272 	match = do_lookup_string("10.53.0.1", dns_geoip_city_region, "CA");
273 	assert_true(match);
274 
275 	match = do_lookup_string("10.53.0.1", dns_geoip_city_regionname,
276 				 "California");
277 	assert_true(match);
278 
279 	match = do_lookup_string("10.53.0.1", dns_geoip_city_name,
280 				 "Redwood City");
281 	assert_true(match);
282 
283 	match = do_lookup_string("10.53.0.1", dns_geoip_city_postalcode,
284 				 "94063");
285 	assert_true(match);
286 }
287 
288 /* GeoIP city (ipv6) matching */
289 ISC_RUN_TEST_IMPL(city_v6) {
290 	bool match;
291 
292 	UNUSED(state);
293 
294 	if (geoip.city == NULL) {
295 		skip();
296 	}
297 
298 	match = do_lookup_string_v6("fd92:7065:b8e:ffff::1",
299 				    dns_geoip_city_continentcode, "NA");
300 	assert_true(match);
301 
302 	match = do_lookup_string_v6("fd92:7065:b8e:ffff::1",
303 				    dns_geoip_city_countrycode, "US");
304 	assert_true(match);
305 
306 	match = do_lookup_string_v6("fd92:7065:b8e:ffff::1",
307 				    dns_geoip_city_countryname,
308 				    "United States");
309 	assert_true(match);
310 
311 	match = do_lookup_string_v6("fd92:7065:b8e:ffff::1",
312 				    dns_geoip_city_region, "CA");
313 	assert_true(match);
314 
315 	match = do_lookup_string_v6("fd92:7065:b8e:ffff::1",
316 				    dns_geoip_city_regionname, "California");
317 	assert_true(match);
318 
319 	match = do_lookup_string_v6("fd92:7065:b8e:ffff::1",
320 				    dns_geoip_city_name, "Redwood City");
321 	assert_true(match);
322 
323 	match = do_lookup_string_v6("fd92:7065:b8e:ffff::1",
324 				    dns_geoip_city_postalcode, "94063");
325 	assert_true(match);
326 }
327 
328 /* GeoIP asnum matching */
329 ISC_RUN_TEST_IMPL(asnum) {
330 	bool match;
331 
332 	UNUSED(state);
333 
334 	if (geoip.as == NULL) {
335 		skip();
336 	}
337 
338 	match = do_lookup_string("10.53.0.3", dns_geoip_as_asnum, "AS100003");
339 	assert_true(match);
340 }
341 
342 /* GeoIP isp matching */
343 ISC_RUN_TEST_IMPL(isp) {
344 	bool match;
345 
346 	UNUSED(state);
347 
348 	if (geoip.isp == NULL) {
349 		skip();
350 	}
351 
352 	match = do_lookup_string("10.53.0.1", dns_geoip_isp_name,
353 				 "One Systems, Inc.");
354 	assert_true(match);
355 }
356 
357 /* GeoIP org matching */
358 ISC_RUN_TEST_IMPL(org) {
359 	bool match;
360 
361 	UNUSED(state);
362 
363 	if (geoip.as == NULL) {
364 		skip();
365 	}
366 
367 	match = do_lookup_string("10.53.0.2", dns_geoip_org_name,
368 				 "Two Technology Ltd.");
369 	assert_true(match);
370 }
371 
372 /* GeoIP domain matching */
373 ISC_RUN_TEST_IMPL(domain) {
374 	bool match;
375 
376 	UNUSED(state);
377 
378 	if (geoip.domain == NULL) {
379 		skip();
380 	}
381 
382 	match = do_lookup_string("10.53.0.5", dns_geoip_domain_name, "five.es");
383 	assert_true(match);
384 }
385 
386 ISC_TEST_LIST_START
387 ISC_TEST_ENTRY_CUSTOM(baseline, setup_test, teardown_test)
388 ISC_TEST_ENTRY_CUSTOM(country, setup_test, teardown_test)
389 ISC_TEST_ENTRY_CUSTOM(country_v6, setup_test, teardown_test)
390 ISC_TEST_ENTRY_CUSTOM(city, setup_test, teardown_test)
391 ISC_TEST_ENTRY_CUSTOM(city_v6, setup_test, teardown_test)
392 ISC_TEST_ENTRY_CUSTOM(asnum, setup_test, teardown_test)
393 ISC_TEST_ENTRY_CUSTOM(isp, setup_test, teardown_test)
394 ISC_TEST_ENTRY_CUSTOM(org, setup_test, teardown_test)
395 ISC_TEST_ENTRY_CUSTOM(domain, setup_test, teardown_test)
396 ISC_TEST_LIST_END
397 
398 ISC_TEST_MAIN
399