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