Home | History | Annotate | Line # | Download | only in dns
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