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