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