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 */
infra_lookup_host(struct infra_cache * infra,struct sockaddr_storage * addr,socklen_t addrlen,uint8_t * zone,size_t zonelen,int wr,time_t now,struct infra_key ** k)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
test_keep_probing(struct infra_cache * slab,struct config_file * cfg,struct sockaddr_storage one,socklen_t onelen,uint8_t * zone,size_t zonelen,time_t * now,int keep_probing,int rtt_max_timeout)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 */
infra_test(void)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 config_auto_slab_values(cfg);
135 slab = infra_create(cfg);
136 /* insert new record */
137 unit_assert( infra_host(slab, &one, onelen, zone, zonelen, now,
138 &vs, &edns_lame, &to) );
139 unit_assert( vs == 0 && to == init && edns_lame == 0 );
140
141 /* simulate no answer */
142 unit_assert( infra_rtt_update(slab, &one, onelen, zone, zonelen, LDNS_RR_TYPE_A, -1, init, now) );
143 unit_assert( infra_host(slab, &one, onelen, zone, zonelen,
144 now, &vs, &edns_lame, &to) );
145 unit_assert( vs == 0 && to == init*2 && edns_lame == 0 );
146
147 /* simulate EDNS lame */
148 unit_assert( infra_edns_update(slab, &one, onelen, zone, zonelen, -1, now) );
149 unit_assert( infra_host(slab, &one, onelen, zone, zonelen,
150 now, &vs, &edns_lame, &to) );
151 unit_assert( vs == -1 && to == init*2 && edns_lame == 1);
152
153 /* simulate cache expiry */
154 now += cfg->host_ttl + 10;
155 unit_assert( infra_host(slab, &one, onelen, zone, zonelen,
156 now, &vs, &edns_lame, &to) );
157 unit_assert( vs == 0 && to == init && edns_lame == 0 );
158
159 /* simulate no lame answer */
160 unit_assert( infra_set_lame(slab, &one, onelen,
161 zone, zonelen, now, 0, 0, LDNS_RR_TYPE_A) );
162 unit_assert( (d=infra_lookup_host(slab, &one, onelen, zone, zonelen, 0, now, &k)) );
163 unit_assert( d->ttl == now+cfg->host_ttl );
164 unit_assert( d->edns_version == 0 );
165 unit_assert(!d->isdnsseclame && !d->rec_lame && d->lame_type_A &&
166 !d->lame_other);
167 lock_rw_unlock(&k->entry.lock);
168
169 /* test merge of data */
170 unit_assert( infra_set_lame(slab, &one, onelen,
171 zone, zonelen, now, 0, 0, LDNS_RR_TYPE_AAAA) );
172 unit_assert( (d=infra_lookup_host(slab, &one, onelen, zone, zonelen, 0, now, &k)) );
173 unit_assert(!d->isdnsseclame && !d->rec_lame && d->lame_type_A &&
174 d->lame_other);
175 lock_rw_unlock(&k->entry.lock);
176
177 /* test that noEDNS cannot overwrite known-yesEDNS */
178 now += cfg->host_ttl + 10;
179 unit_assert( infra_host(slab, &one, onelen, zone, zonelen,
180 now, &vs, &edns_lame, &to) );
181 unit_assert( vs == 0 && to == init && edns_lame == 0 );
182
183 unit_assert( infra_edns_update(slab, &one, onelen, zone, zonelen, 0, now) );
184 unit_assert( infra_host(slab, &one, onelen, zone, zonelen,
185 now, &vs, &edns_lame, &to) );
186 unit_assert( vs == 0 && to == init && edns_lame == 1 );
187
188 unit_assert( infra_edns_update(slab, &one, onelen, zone, zonelen, -1, now) );
189 unit_assert( infra_host(slab, &one, onelen, zone, zonelen,
190 now, &vs, &edns_lame, &to) );
191 unit_assert( vs == 0 && to == init && edns_lame == 1 );
192
193 unit_show_feature("infra cache probing (keep-probing off, default infra-cache-max-rtt)");
194 test_keep_probing(slab, cfg, one, onelen, zone, zonelen, &now, 0, default_max_rtt);
195
196 unit_show_feature("infra cache probing (keep-probing on, default infra-cache-max-rtt)");
197 test_keep_probing(slab, cfg, one, onelen, zone, zonelen, &now, 1, default_max_rtt);
198
199 unit_show_feature("infra cache probing (keep-probing off, low infra-cache-max-rtt)");
200 test_keep_probing(slab, cfg, one, onelen, zone, zonelen, &now, 0, 3000);
201
202 unit_show_feature("infra cache probing (keep-probing on, low infra-cache-max-rtt)");
203 test_keep_probing(slab, cfg, one, onelen, zone, zonelen, &now, 1, 3000);
204
205 /* Re-apply defaults for other unit tests that follow */
206 config_apply_max_rtt(default_max_rtt);
207
208 infra_delete(slab);
209 config_delete(cfg);
210 }
211