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