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