geoip_test.c revision 1.2.6.1 1 /* $NetBSD: geoip_test.c,v 1.2.6.1 2025/08/02 05:54:11 perseant 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