xref: /freebsd/contrib/unbound/testcode/unitinfra.c (revision b670c9bafc0e31c7609969bf374b2e80bdc00211)
1 /*
2  * testcode/unitinfra.c - unit test for infra cache.
3  *
4  * Copyright (c) 2025, NLnet Labs. All rights reserved.
5  *
6  * This software is open source.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * Redistributions of source code must retain the above copyright notice,
13  * this list of conditions and the following disclaimer.
14  *
15  * Redistributions in binary form must reproduce the above copyright notice,
16  * this list of conditions and the following disclaimer in the documentation
17  * and/or other materials provided with the distribution.
18  *
19  * Neither the name of the NLNET LABS nor the names of its contributors may
20  * be used to endorse or promote products derived from this software without
21  * specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34  *
35  */
36 /**
37  * \file
38  * Tests the infra functionality.
39  */
40 
41 #include "config.h"
42 #include "testcode/unitmain.h"
43 #include "iterator/iterator.h"
44 #include "services/cache/infra.h"
45 #include "util/config_file.h"
46 #include "util/net_help.h"
47 
48 /* lookup and get key and data structs easily */
49 static struct infra_data* infra_lookup_host(struct infra_cache* infra,
50 	struct sockaddr_storage* addr, socklen_t addrlen, uint8_t* zone,
51 	size_t zonelen, int wr, time_t now, struct infra_key** k)
52 {
53 	struct infra_data* d;
54 	struct lruhash_entry* e = infra_lookup_nottl(infra, addr, addrlen,
55 		zone, zonelen, wr);
56 	if(!e) return NULL;
57 	d = (struct infra_data*)e->data;
58 	if(d->ttl < now) {
59 		lock_rw_unlock(&e->lock);
60 		return NULL;
61 	}
62 	*k = (struct infra_key*)e->key;
63 	return d;
64 }
65 
66 static void test_keep_probing(struct infra_cache* slab,
67 	struct config_file* cfg, struct sockaddr_storage one, socklen_t onelen,
68 	uint8_t* zone, size_t zonelen, time_t *now, int keep_probing,
69 	int rtt_max_timeout)
70 {
71 	uint8_t edns_lame;
72 	int vs, to, lame, dnsseclame, reclame, probedelay;
73 	struct infra_key* k;
74 	struct infra_data* d;
75 
76 	/* configure */
77 	cfg->infra_cache_max_rtt = rtt_max_timeout;
78 	config_apply_max_rtt(rtt_max_timeout);
79 	slab->infra_keep_probing = keep_probing;
80 
81 	/* expired previous entry */
82 	*now += cfg->host_ttl + 10;
83 	unit_assert( infra_host(slab, &one, onelen, zone, zonelen,
84 			*now, &vs, &edns_lame, &to) );
85 
86 	/* simulate timeouts until the USEFUL_SERVER_TOP_TIMEOUT is reached */
87 	while(to < USEFUL_SERVER_TOP_TIMEOUT) {
88 		unit_assert( infra_rtt_update(slab, &one, onelen, zone, zonelen,
89 			LDNS_RR_TYPE_A, -1, to, *now) );
90 		unit_assert( infra_host(slab, &one, onelen, zone, zonelen,
91 			*now, &vs, &edns_lame, &to) );
92 		unit_assert( vs == 0 && to <= USEFUL_SERVER_TOP_TIMEOUT && edns_lame == 0 );
93 	}
94 	unit_assert( vs == 0 && to == USEFUL_SERVER_TOP_TIMEOUT && edns_lame == 0 );
95 
96 	/* don't let the record expire */
97 	unit_assert( (d=infra_lookup_host(slab, &one, onelen, zone, zonelen, 0, *now, &k)) );
98 	unit_assert( d->timeout_A >= TIMEOUT_COUNT_MAX );
99 	unit_assert( d->probedelay > 0 );
100 	probedelay = d->probedelay;
101 	lock_rw_unlock(&k->entry.lock);
102 	cfg->host_ttl = cfg->host_ttl + *now < probedelay
103 		?cfg->host_ttl :probedelay + 10;
104 
105 	/* advance time and check that probing is as expected; we already had a
106 	 * lot of A timeouts (checked above). */
107 	*now = probedelay;
108 	unit_assert( infra_get_lame_rtt(slab, &one, onelen, zone, zonelen,
109 		LDNS_RR_TYPE_A, &lame, &dnsseclame, &reclame, &to, *now) );
110 	unit_assert( lame == 0 && dnsseclame == 0 && reclame == 0
111 		&& to == keep_probing ?still_useful_timeout() :USEFUL_SERVER_TOP_TIMEOUT);
112 }
113 
114 /** test host cache */
115 void infra_test(void)
116 {
117 	struct sockaddr_storage one;
118 	socklen_t onelen;
119 	uint8_t* zone = (uint8_t*)"\007example\003com\000";
120 	size_t zonelen = 13;
121 	struct infra_cache* slab;
122 	struct config_file* cfg = config_create();
123 	time_t now = 0;
124 	uint8_t edns_lame;
125 	int vs, to;
126 	struct infra_key* k;
127 	struct infra_data* d;
128 	int init = UNKNOWN_SERVER_NICENESS;
129 	int default_max_rtt = USEFUL_SERVER_TOP_TIMEOUT;
130 
131 	unit_show_feature("infra cache");
132 	unit_assert(ipstrtoaddr("127.0.0.1", 53, &one, &onelen));
133 
134 	slab = infra_create(cfg);
135 	/* insert new record */
136 	unit_assert( infra_host(slab, &one, onelen, zone, zonelen, now,
137 		&vs, &edns_lame, &to) );
138 	unit_assert( vs == 0 && to == init && edns_lame == 0 );
139 
140 	/* simulate no answer */
141 	unit_assert( infra_rtt_update(slab, &one, onelen, zone, zonelen, LDNS_RR_TYPE_A, -1, init, now) );
142 	unit_assert( infra_host(slab, &one, onelen, zone, zonelen,
143 			now, &vs, &edns_lame, &to) );
144 	unit_assert( vs == 0 && to == init*2 && edns_lame == 0 );
145 
146 	/* simulate EDNS lame */
147 	unit_assert( infra_edns_update(slab, &one, onelen, zone, zonelen, -1, now) );
148 	unit_assert( infra_host(slab, &one, onelen, zone, zonelen,
149 			now, &vs, &edns_lame, &to) );
150 	unit_assert( vs == -1 && to == init*2  && edns_lame == 1);
151 
152 	/* simulate cache expiry */
153 	now += cfg->host_ttl + 10;
154 	unit_assert( infra_host(slab, &one, onelen, zone, zonelen,
155 			now, &vs, &edns_lame, &to) );
156 	unit_assert( vs == 0 && to == init && edns_lame == 0 );
157 
158 	/* simulate no lame answer */
159 	unit_assert( infra_set_lame(slab, &one, onelen,
160 		zone, zonelen,  now, 0, 0, LDNS_RR_TYPE_A) );
161 	unit_assert( (d=infra_lookup_host(slab, &one, onelen, zone, zonelen, 0, now, &k)) );
162 	unit_assert( d->ttl == now+cfg->host_ttl );
163 	unit_assert( d->edns_version == 0 );
164 	unit_assert(!d->isdnsseclame && !d->rec_lame && d->lame_type_A &&
165 		!d->lame_other);
166 	lock_rw_unlock(&k->entry.lock);
167 
168 	/* test merge of data */
169 	unit_assert( infra_set_lame(slab, &one, onelen,
170 		zone, zonelen,  now, 0, 0, LDNS_RR_TYPE_AAAA) );
171 	unit_assert( (d=infra_lookup_host(slab, &one, onelen, zone, zonelen, 0, now, &k)) );
172 	unit_assert(!d->isdnsseclame && !d->rec_lame && d->lame_type_A &&
173 		d->lame_other);
174 	lock_rw_unlock(&k->entry.lock);
175 
176 	/* test that noEDNS cannot overwrite known-yesEDNS */
177 	now += cfg->host_ttl + 10;
178 	unit_assert( infra_host(slab, &one, onelen, zone, zonelen,
179 			now, &vs, &edns_lame, &to) );
180 	unit_assert( vs == 0 && to == init && edns_lame == 0 );
181 
182 	unit_assert( infra_edns_update(slab, &one, onelen, zone, zonelen, 0, now) );
183 	unit_assert( infra_host(slab, &one, onelen, zone, zonelen,
184 			now, &vs, &edns_lame, &to) );
185 	unit_assert( vs == 0 && to == init && edns_lame == 1 );
186 
187 	unit_assert( infra_edns_update(slab, &one, onelen, zone, zonelen, -1, now) );
188 	unit_assert( infra_host(slab, &one, onelen, zone, zonelen,
189 			now, &vs, &edns_lame, &to) );
190 	unit_assert( vs == 0 && to == init && edns_lame == 1 );
191 
192 	unit_show_feature("infra cache probing (keep-probing off, default infra-cache-max-rtt)");
193 	test_keep_probing(slab, cfg, one, onelen, zone, zonelen, &now, 0, default_max_rtt);
194 
195 	unit_show_feature("infra cache probing (keep-probing on,  default infra-cache-max-rtt)");
196 	test_keep_probing(slab, cfg, one, onelen, zone, zonelen, &now, 1, default_max_rtt);
197 
198 	unit_show_feature("infra cache probing (keep-probing off, low infra-cache-max-rtt)");
199 	test_keep_probing(slab, cfg, one, onelen, zone, zonelen, &now, 0, 3000);
200 
201 	unit_show_feature("infra cache probing (keep-probing on,  low infra-cache-max-rtt)");
202 	test_keep_probing(slab, cfg, one, onelen, zone, zonelen, &now, 1, 3000);
203 
204 	/* Re-apply defaults for other unit tests that follow */
205 	config_apply_max_rtt(default_max_rtt);
206 
207 	infra_delete(slab);
208 	config_delete(cfg);
209 }
210