/src/unbound/util/data/msgreply.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * util/data/msgreply.c - store message and reply data. |
3 | | * |
4 | | * Copyright (c) 2007, 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 | | * |
39 | | * This file contains a data structure to store a message and its reply. |
40 | | */ |
41 | | |
42 | | #include "config.h" |
43 | | #include "util/data/msgreply.h" |
44 | | #include "util/storage/lookup3.h" |
45 | | #include "util/log.h" |
46 | | #include "util/alloc.h" |
47 | | #include "util/netevent.h" |
48 | | #include "util/net_help.h" |
49 | | #include "util/data/dname.h" |
50 | | #include "util/regional.h" |
51 | | #include "util/data/msgparse.h" |
52 | | #include "util/data/msgencode.h" |
53 | | #include "sldns/sbuffer.h" |
54 | | #include "sldns/wire2str.h" |
55 | | #include "util/module.h" |
56 | | #include "util/fptr_wlist.h" |
57 | | |
58 | | /** MAX TTL default for messages and rrsets */ |
59 | | time_t MAX_TTL = 3600 * 24 * 10; /* ten days */ |
60 | | /** MIN TTL default for messages and rrsets */ |
61 | | time_t MIN_TTL = 0; |
62 | | /** MAX Negative TTL, for SOA records in authority section */ |
63 | | time_t MAX_NEG_TTL = 3600; /* one hour */ |
64 | | /** MIN Negative TTL, for SOA records in authority section */ |
65 | | time_t MIN_NEG_TTL = 0; |
66 | | /** If we serve expired entries and prefetch them */ |
67 | | int SERVE_EXPIRED = 0; |
68 | | /** Time to serve records after expiration */ |
69 | | time_t SERVE_EXPIRED_TTL = 86400; |
70 | | /** Reset serve expired TTL after failed update attempt */ |
71 | | time_t SERVE_EXPIRED_TTL_RESET = 0; |
72 | | /** TTL to use for expired records */ |
73 | | time_t SERVE_EXPIRED_REPLY_TTL = 30; |
74 | | /** If we serve the original TTL or decrementing TTLs */ |
75 | | int SERVE_ORIGINAL_TTL = 0; |
76 | | |
77 | | /** allocate qinfo, return 0 on error */ |
78 | | static int |
79 | | parse_create_qinfo(sldns_buffer* pkt, struct msg_parse* msg, |
80 | | struct query_info* qinf, struct regional* region) |
81 | 0 | { |
82 | 0 | if(msg->qname) { |
83 | 0 | if(region) |
84 | 0 | qinf->qname = (uint8_t*)regional_alloc(region, |
85 | 0 | msg->qname_len); |
86 | 0 | else qinf->qname = (uint8_t*)malloc(msg->qname_len); |
87 | 0 | if(!qinf->qname) return 0; |
88 | 0 | dname_pkt_copy(pkt, qinf->qname, msg->qname); |
89 | 0 | } else qinf->qname = 0; |
90 | 0 | qinf->qname_len = msg->qname_len; |
91 | 0 | qinf->qtype = msg->qtype; |
92 | 0 | qinf->qclass = msg->qclass; |
93 | 0 | qinf->local_alias = NULL; |
94 | 0 | return 1; |
95 | 0 | } |
96 | | |
97 | | /** constructor for replyinfo */ |
98 | | struct reply_info* |
99 | | construct_reply_info_base(struct regional* region, uint16_t flags, size_t qd, |
100 | | time_t ttl, time_t prettl, time_t expttl, time_t norecttl, size_t an, |
101 | | size_t ns, size_t ar, size_t total, enum sec_status sec, |
102 | | sldns_ede_code reason_bogus) |
103 | 0 | { |
104 | 0 | struct reply_info* rep; |
105 | | /* rrset_count-1 because the first ref is part of the struct. */ |
106 | 0 | size_t s = sizeof(struct reply_info) - sizeof(struct rrset_ref) + |
107 | 0 | sizeof(struct ub_packed_rrset_key*) * total; |
108 | 0 | if(total >= RR_COUNT_MAX) return NULL; /* sanity check on numRRS*/ |
109 | 0 | if(region) |
110 | 0 | rep = (struct reply_info*)regional_alloc(region, s); |
111 | 0 | else rep = (struct reply_info*)malloc(s + |
112 | 0 | sizeof(struct rrset_ref) * (total)); |
113 | 0 | if(!rep) |
114 | 0 | return NULL; |
115 | 0 | rep->flags = flags; |
116 | 0 | rep->qdcount = qd; |
117 | 0 | rep->ttl = ttl; |
118 | 0 | rep->prefetch_ttl = prettl; |
119 | 0 | rep->serve_expired_ttl = expttl; |
120 | 0 | rep->serve_expired_norec_ttl = norecttl; |
121 | 0 | rep->an_numrrsets = an; |
122 | 0 | rep->ns_numrrsets = ns; |
123 | 0 | rep->ar_numrrsets = ar; |
124 | 0 | rep->rrset_count = total; |
125 | 0 | rep->security = sec; |
126 | 0 | rep->reason_bogus = reason_bogus; |
127 | | /* this is only allocated and used for caching on copy */ |
128 | 0 | rep->reason_bogus_str = NULL; |
129 | 0 | rep->authoritative = 0; |
130 | | /* array starts after the refs */ |
131 | 0 | if(region) |
132 | 0 | rep->rrsets = (struct ub_packed_rrset_key**)&(rep->ref[0]); |
133 | 0 | else rep->rrsets = (struct ub_packed_rrset_key**)&(rep->ref[total]); |
134 | | /* zero the arrays to assist cleanup in case of malloc failure */ |
135 | 0 | memset( rep->rrsets, 0, sizeof(struct ub_packed_rrset_key*) * total); |
136 | 0 | if(!region) |
137 | 0 | memset( &rep->ref[0], 0, sizeof(struct rrset_ref) * total); |
138 | 0 | return rep; |
139 | 0 | } |
140 | | |
141 | | /** allocate replyinfo, return 0 on error */ |
142 | | static int |
143 | | parse_create_repinfo(struct msg_parse* msg, struct reply_info** rep, |
144 | | struct regional* region) |
145 | 0 | { |
146 | 0 | *rep = construct_reply_info_base(region, msg->flags, msg->qdcount, 0, |
147 | 0 | 0, 0, 0, msg->an_rrsets, msg->ns_rrsets, msg->ar_rrsets, |
148 | 0 | msg->rrset_count, sec_status_unchecked, LDNS_EDE_NONE); |
149 | 0 | if(!*rep) |
150 | 0 | return 0; |
151 | 0 | return 1; |
152 | 0 | } |
153 | | |
154 | | int |
155 | | reply_info_alloc_rrset_keys(struct reply_info* rep, struct alloc_cache* alloc, |
156 | | struct regional* region) |
157 | 0 | { |
158 | 0 | size_t i; |
159 | 0 | for(i=0; i<rep->rrset_count; i++) { |
160 | 0 | if(region) { |
161 | 0 | rep->rrsets[i] = (struct ub_packed_rrset_key*) |
162 | 0 | regional_alloc(region, |
163 | 0 | sizeof(struct ub_packed_rrset_key)); |
164 | 0 | if(rep->rrsets[i]) { |
165 | 0 | memset(rep->rrsets[i], 0, |
166 | 0 | sizeof(struct ub_packed_rrset_key)); |
167 | 0 | rep->rrsets[i]->entry.key = rep->rrsets[i]; |
168 | 0 | } |
169 | 0 | } |
170 | 0 | else rep->rrsets[i] = alloc_special_obtain(alloc); |
171 | 0 | if(!rep->rrsets[i]) |
172 | 0 | return 0; |
173 | 0 | rep->rrsets[i]->entry.data = NULL; |
174 | 0 | } |
175 | 0 | return 1; |
176 | 0 | } |
177 | | |
178 | | int |
179 | | reply_info_can_answer_expired(struct reply_info* rep, time_t timenow) |
180 | 0 | { |
181 | 0 | log_assert(rep->ttl < timenow); |
182 | | /* Really expired */ |
183 | 0 | if(SERVE_EXPIRED_TTL && rep->serve_expired_ttl < timenow) return 0; |
184 | | /* Ignore expired failure answers */ |
185 | 0 | if(FLAGS_GET_RCODE(rep->flags) != LDNS_RCODE_NOERROR && |
186 | 0 | FLAGS_GET_RCODE(rep->flags) != LDNS_RCODE_NXDOMAIN && |
187 | 0 | FLAGS_GET_RCODE(rep->flags) != LDNS_RCODE_YXDOMAIN) return 0; |
188 | 0 | return 1; |
189 | 0 | } |
190 | | |
191 | | int reply_info_could_use_expired(struct reply_info* rep, time_t timenow) |
192 | 0 | { |
193 | 0 | log_assert(rep->ttl < timenow); |
194 | | /* Really expired */ |
195 | 0 | if(SERVE_EXPIRED_TTL && rep->serve_expired_ttl < timenow && |
196 | 0 | !SERVE_EXPIRED_TTL_RESET) return 0; |
197 | | /* Ignore expired failure answers */ |
198 | 0 | if(FLAGS_GET_RCODE(rep->flags) != LDNS_RCODE_NOERROR && |
199 | 0 | FLAGS_GET_RCODE(rep->flags) != LDNS_RCODE_NXDOMAIN && |
200 | 0 | FLAGS_GET_RCODE(rep->flags) != LDNS_RCODE_YXDOMAIN) return 0; |
201 | 0 | return 1; |
202 | 0 | } |
203 | | |
204 | | struct reply_info * |
205 | | make_new_reply_info(const struct reply_info* rep, struct regional* region, |
206 | | size_t an_numrrsets, size_t copy_rrsets) |
207 | 0 | { |
208 | 0 | struct reply_info* new_rep; |
209 | 0 | size_t i; |
210 | | |
211 | | /* create a base struct. we specify 'insecure' security status as |
212 | | * the modified response won't be DNSSEC-valid. In our faked response |
213 | | * the authority and additional sections will be empty (except possible |
214 | | * EDNS0 OPT RR in the additional section appended on sending it out), |
215 | | * so the total number of RRsets is an_numrrsets. */ |
216 | 0 | new_rep = construct_reply_info_base(region, rep->flags, |
217 | 0 | rep->qdcount, rep->ttl, rep->prefetch_ttl, |
218 | 0 | rep->serve_expired_ttl, rep->serve_expired_norec_ttl, |
219 | 0 | an_numrrsets, 0, 0, an_numrrsets, |
220 | 0 | sec_status_insecure, LDNS_EDE_NONE); |
221 | 0 | if(!new_rep) |
222 | 0 | return NULL; |
223 | 0 | if(!reply_info_alloc_rrset_keys(new_rep, NULL, region)) |
224 | 0 | return NULL; |
225 | 0 | for(i=0; i<copy_rrsets; i++) |
226 | 0 | new_rep->rrsets[i] = rep->rrsets[i]; |
227 | |
|
228 | 0 | return new_rep; |
229 | 0 | } |
230 | | |
231 | | /** find the minimumttl in the rdata of SOA record */ |
232 | | static time_t |
233 | | soa_find_minttl(struct rr_parse* rr) |
234 | 0 | { |
235 | 0 | uint16_t rlen = sldns_read_uint16(rr->ttl_data+4); |
236 | 0 | if(rlen < 20) |
237 | 0 | return 0; /* rdata too small for SOA (dname, dname, 5*32bit) */ |
238 | | /* minimum TTL is the last 32bit value in the rdata of the record */ |
239 | | /* at position ttl_data + 4(ttl) + 2(rdatalen) + rdatalen - 4(timeval)*/ |
240 | 0 | return (time_t)sldns_read_uint32(rr->ttl_data+6+rlen-4); |
241 | 0 | } |
242 | | |
243 | | /** do the rdata copy */ |
244 | | static int |
245 | | rdata_copy(sldns_buffer* pkt, struct packed_rrset_data* data, uint8_t* to, |
246 | | struct rr_parse* rr, time_t* rr_ttl, uint16_t type, |
247 | | sldns_pkt_section section) |
248 | 128k | { |
249 | 128k | uint16_t pkt_len; |
250 | 128k | const sldns_rr_descriptor* desc; |
251 | | |
252 | 128k | *rr_ttl = sldns_read_uint32(rr->ttl_data); |
253 | | /* RFC 2181 Section 8. if msb of ttl is set treat as if zero. */ |
254 | 128k | if((*rr_ttl & 0x80000000U)) |
255 | 102k | *rr_ttl = 0; |
256 | 128k | if(type == LDNS_RR_TYPE_SOA && section == LDNS_SECTION_AUTHORITY) { |
257 | | /* negative response. see if TTL of SOA record larger than the |
258 | | * minimum-ttl in the rdata of the SOA record */ |
259 | 0 | if(*rr_ttl > soa_find_minttl(rr)) *rr_ttl = soa_find_minttl(rr); |
260 | 0 | if(!SERVE_ORIGINAL_TTL) { |
261 | | /* If MIN_NEG_TTL is configured skip setting MIN_TTL */ |
262 | 0 | if(MIN_NEG_TTL <= 0 && *rr_ttl < MIN_TTL) { |
263 | 0 | *rr_ttl = MIN_TTL; |
264 | 0 | } |
265 | 0 | if(*rr_ttl > MAX_TTL) *rr_ttl = MAX_TTL; |
266 | 0 | } |
267 | | /* MAX_NEG_TTL overrides the min and max ttl of everything |
268 | | * else; it is for a more specific record */ |
269 | 0 | if(*rr_ttl > MAX_NEG_TTL) *rr_ttl = MAX_NEG_TTL; |
270 | | /* MIN_NEG_TTL overrides the min and max ttl of everything |
271 | | * else if configured; it is for a more specific record */ |
272 | 0 | if(MIN_NEG_TTL > 0 && *rr_ttl < MIN_NEG_TTL) { |
273 | 0 | *rr_ttl = MIN_NEG_TTL; |
274 | 0 | } |
275 | 128k | } else if(!SERVE_ORIGINAL_TTL) { |
276 | 128k | if(*rr_ttl < MIN_TTL) *rr_ttl = MIN_TTL; |
277 | 128k | if(*rr_ttl > MAX_TTL) *rr_ttl = MAX_TTL; |
278 | 128k | } |
279 | 128k | if(*rr_ttl < data->ttl) |
280 | 5.85k | data->ttl = *rr_ttl; |
281 | | |
282 | 128k | if(rr->outside_packet) { |
283 | | /* uncompressed already, only needs copy */ |
284 | 0 | memmove(to, rr->ttl_data+sizeof(uint32_t), rr->size); |
285 | 0 | return 1; |
286 | 0 | } |
287 | | |
288 | 128k | sldns_buffer_set_position(pkt, (size_t) |
289 | 128k | (rr->ttl_data - sldns_buffer_begin(pkt) + sizeof(uint32_t))); |
290 | | /* insert decompressed size into rdata len stored in memory */ |
291 | | /* -2 because rdatalen bytes are not included. */ |
292 | 128k | pkt_len = htons(rr->size - 2); |
293 | 128k | memmove(to, &pkt_len, sizeof(uint16_t)); |
294 | 128k | to += 2; |
295 | | /* read packet rdata len */ |
296 | 128k | pkt_len = sldns_buffer_read_u16(pkt); |
297 | 128k | if(sldns_buffer_remaining(pkt) < pkt_len) |
298 | 0 | return 0; |
299 | 128k | desc = sldns_rr_descript(type); |
300 | 128k | if(pkt_len > 0 && desc && desc->_dname_count > 0) { |
301 | 11.8k | int count = (int)desc->_dname_count; |
302 | 11.8k | int rdf = 0; |
303 | 11.8k | size_t len; |
304 | 11.8k | size_t oldpos; |
305 | | /* decompress dnames. */ |
306 | 106k | while(pkt_len > 0 && count) { |
307 | 94.7k | switch(desc->_wireformat[rdf]) { |
308 | 11.8k | case LDNS_RDF_TYPE_DNAME: |
309 | 11.8k | oldpos = sldns_buffer_position(pkt); |
310 | 11.8k | dname_pkt_copy(pkt, to, |
311 | 11.8k | sldns_buffer_current(pkt)); |
312 | 11.8k | to += pkt_dname_len(pkt); |
313 | 11.8k | pkt_len -= sldns_buffer_position(pkt)-oldpos; |
314 | 11.8k | count--; |
315 | 11.8k | len = 0; |
316 | 11.8k | break; |
317 | 0 | case LDNS_RDF_TYPE_STR: |
318 | 0 | len = sldns_buffer_current(pkt)[0] + 1; |
319 | 0 | break; |
320 | 82.9k | default: |
321 | 82.9k | len = get_rdf_size(desc->_wireformat[rdf]); |
322 | 82.9k | break; |
323 | 94.7k | } |
324 | 94.7k | if(len) { |
325 | 82.9k | log_assert(len <= pkt_len); |
326 | 82.9k | memmove(to, sldns_buffer_current(pkt), len); |
327 | 82.9k | to += len; |
328 | 82.9k | sldns_buffer_skip(pkt, (ssize_t)len); |
329 | 82.9k | pkt_len -= len; |
330 | 82.9k | } |
331 | 94.7k | rdf++; |
332 | 94.7k | } |
333 | 11.8k | } |
334 | | /* copy remaining rdata */ |
335 | 128k | if(pkt_len > 0) |
336 | 118k | memmove(to, sldns_buffer_current(pkt), pkt_len); |
337 | | |
338 | 128k | return 1; |
339 | 128k | } |
340 | | |
341 | | /** copy over the data into packed rrset */ |
342 | | static int |
343 | | parse_rr_copy(sldns_buffer* pkt, struct rrset_parse* pset, |
344 | | struct packed_rrset_data* data) |
345 | 9.28k | { |
346 | 9.28k | size_t i; |
347 | 9.28k | struct rr_parse* rr = pset->rr_first; |
348 | 9.28k | uint8_t* nextrdata; |
349 | 9.28k | size_t total = pset->rr_count + pset->rrsig_count; |
350 | 9.28k | data->ttl = MAX_TTL; |
351 | 9.28k | data->count = pset->rr_count; |
352 | 9.28k | data->rrsig_count = pset->rrsig_count; |
353 | 9.28k | data->trust = rrset_trust_none; |
354 | 9.28k | data->security = sec_status_unchecked; |
355 | | /* layout: struct - rr_len - rr_data - rr_ttl - rdata - rrsig */ |
356 | 9.28k | data->rr_len = (size_t*)((uint8_t*)data + |
357 | 9.28k | sizeof(struct packed_rrset_data)); |
358 | 9.28k | data->rr_data = (uint8_t**)&(data->rr_len[total]); |
359 | 9.28k | data->rr_ttl = (time_t*)&(data->rr_data[total]); |
360 | 9.28k | nextrdata = (uint8_t*)&(data->rr_ttl[total]); |
361 | 126k | for(i=0; i<data->count; i++) { |
362 | 117k | data->rr_len[i] = rr->size; |
363 | 117k | data->rr_data[i] = nextrdata; |
364 | 117k | nextrdata += rr->size; |
365 | 117k | if(!rdata_copy(pkt, data, data->rr_data[i], rr, |
366 | 117k | &data->rr_ttl[i], pset->type, pset->section)) |
367 | 0 | return 0; |
368 | 117k | rr = rr->next; |
369 | 117k | } |
370 | | /* if rrsig, its rdata is at nextrdata */ |
371 | 9.28k | rr = pset->rrsig_first; |
372 | 21.1k | for(i=data->count; i<total; i++) { |
373 | 11.8k | data->rr_len[i] = rr->size; |
374 | 11.8k | data->rr_data[i] = nextrdata; |
375 | 11.8k | nextrdata += rr->size; |
376 | 11.8k | if(!rdata_copy(pkt, data, data->rr_data[i], rr, |
377 | 11.8k | &data->rr_ttl[i], LDNS_RR_TYPE_RRSIG, pset->section)) |
378 | 0 | return 0; |
379 | 11.8k | rr = rr->next; |
380 | 11.8k | } |
381 | 9.28k | return 1; |
382 | 9.28k | } |
383 | | |
384 | | /** create rrset return 0 on failure */ |
385 | | static int |
386 | | parse_create_rrset(sldns_buffer* pkt, struct rrset_parse* pset, |
387 | | struct packed_rrset_data** data, struct regional* region) |
388 | 9.28k | { |
389 | | /* allocate */ |
390 | 9.28k | size_t s; |
391 | 9.28k | if(pset->rr_count > RR_COUNT_MAX || pset->rrsig_count > RR_COUNT_MAX || |
392 | 9.28k | pset->size > RR_COUNT_MAX) |
393 | 0 | return 0; /* protect against integer overflow */ |
394 | 9.28k | s = sizeof(struct packed_rrset_data) + |
395 | 9.28k | (pset->rr_count + pset->rrsig_count) * |
396 | 9.28k | (sizeof(size_t)+sizeof(uint8_t*)+sizeof(time_t)) + |
397 | 9.28k | pset->size; |
398 | 9.28k | if(region) |
399 | 0 | *data = regional_alloc_zero(region, s); |
400 | 9.28k | else *data = calloc(1, s); |
401 | 9.28k | if(!*data) |
402 | 0 | return 0; |
403 | | /* copy & decompress */ |
404 | 9.28k | if(!parse_rr_copy(pkt, pset, *data)) { |
405 | 0 | if(!region) { |
406 | 0 | free(*data); |
407 | 0 | *data = NULL; |
408 | 0 | } |
409 | 0 | return 0; |
410 | 0 | } |
411 | 9.28k | return 1; |
412 | 9.28k | } |
413 | | |
414 | | /** get trust value for rrset */ |
415 | | static enum rrset_trust |
416 | | get_rrset_trust(struct msg_parse* msg, struct rrset_parse* rrset) |
417 | 9.28k | { |
418 | 9.28k | uint16_t AA = msg->flags & BIT_AA; |
419 | 9.28k | if(rrset->section == LDNS_SECTION_ANSWER) { |
420 | 0 | if(AA) { |
421 | | /* RFC2181 says remainder of CNAME chain is nonauth*/ |
422 | 0 | if(msg->rrset_first && |
423 | 0 | msg->rrset_first->section==LDNS_SECTION_ANSWER |
424 | 0 | && msg->rrset_first->type==LDNS_RR_TYPE_CNAME){ |
425 | 0 | if(rrset == msg->rrset_first) |
426 | 0 | return rrset_trust_ans_AA; |
427 | 0 | else return rrset_trust_ans_noAA; |
428 | 0 | } |
429 | 0 | if(msg->rrset_first && |
430 | 0 | msg->rrset_first->section==LDNS_SECTION_ANSWER |
431 | 0 | && msg->rrset_first->type==LDNS_RR_TYPE_DNAME){ |
432 | 0 | if(rrset == msg->rrset_first || |
433 | 0 | rrset == msg->rrset_first->rrset_all_next) |
434 | 0 | return rrset_trust_ans_AA; |
435 | 0 | else return rrset_trust_ans_noAA; |
436 | 0 | } |
437 | 0 | return rrset_trust_ans_AA; |
438 | 0 | } |
439 | 0 | else return rrset_trust_ans_noAA; |
440 | 9.28k | } else if(rrset->section == LDNS_SECTION_AUTHORITY) { |
441 | 4.95k | if(AA) return rrset_trust_auth_AA; |
442 | 740 | else return rrset_trust_auth_noAA; |
443 | 4.95k | } else { |
444 | | /* addit section */ |
445 | 4.33k | if(AA) return rrset_trust_add_AA; |
446 | 2.78k | else return rrset_trust_add_noAA; |
447 | 4.33k | } |
448 | | /* NOTREACHED */ |
449 | 0 | return rrset_trust_none; |
450 | 9.28k | } |
451 | | |
452 | | int |
453 | | parse_copy_decompress_rrset(sldns_buffer* pkt, struct msg_parse* msg, |
454 | | struct rrset_parse *pset, struct regional* region, |
455 | | struct ub_packed_rrset_key* pk) |
456 | 9.28k | { |
457 | 9.28k | struct packed_rrset_data* data; |
458 | 9.28k | pk->rk.flags = pset->flags; |
459 | 9.28k | pk->rk.dname_len = pset->dname_len; |
460 | 9.28k | if(region) |
461 | 0 | pk->rk.dname = (uint8_t*)regional_alloc( |
462 | 0 | region, pset->dname_len); |
463 | 9.28k | else pk->rk.dname = |
464 | 9.28k | (uint8_t*)malloc(pset->dname_len); |
465 | 9.28k | if(!pk->rk.dname) |
466 | 0 | return 0; |
467 | | /** copy & decompress dname */ |
468 | 9.28k | dname_pkt_copy(pkt, pk->rk.dname, pset->dname); |
469 | | /** copy over type and class */ |
470 | 9.28k | pk->rk.type = htons(pset->type); |
471 | 9.28k | pk->rk.rrset_class = pset->rrset_class; |
472 | | /** read data part. */ |
473 | 9.28k | if(!parse_create_rrset(pkt, pset, &data, region)) { |
474 | 0 | if(!region) { |
475 | 0 | free(pk->rk.dname); |
476 | 0 | pk->rk.dname = NULL; |
477 | 0 | } |
478 | 0 | return 0; |
479 | 0 | } |
480 | 9.28k | pk->entry.data = (void*)data; |
481 | 9.28k | pk->entry.key = (void*)pk; |
482 | 9.28k | pk->entry.hash = pset->hash; |
483 | 9.28k | data->trust = get_rrset_trust(msg, pset); |
484 | 9.28k | return 1; |
485 | 9.28k | } |
486 | | |
487 | | /** |
488 | | * Copy and decompress rrs |
489 | | * @param pkt: the packet for compression pointer resolution. |
490 | | * @param msg: the parsed message |
491 | | * @param rep: reply info to put rrs into. |
492 | | * @param region: if not NULL, used for allocation. |
493 | | * @return 0 on failure. |
494 | | */ |
495 | | static int |
496 | | parse_copy_decompress(sldns_buffer* pkt, struct msg_parse* msg, |
497 | | struct reply_info* rep, struct regional* region) |
498 | 0 | { |
499 | 0 | size_t i; |
500 | 0 | struct rrset_parse *pset = msg->rrset_first; |
501 | 0 | struct packed_rrset_data* data; |
502 | 0 | log_assert(rep); |
503 | 0 | rep->ttl = MAX_TTL; |
504 | 0 | rep->security = sec_status_unchecked; |
505 | 0 | if(rep->rrset_count == 0) |
506 | 0 | rep->ttl = NORR_TTL; |
507 | |
|
508 | 0 | for(i=0; i<rep->rrset_count; i++) { |
509 | 0 | if(!parse_copy_decompress_rrset(pkt, msg, pset, region, |
510 | 0 | rep->rrsets[i])) |
511 | 0 | return 0; |
512 | 0 | data = (struct packed_rrset_data*)rep->rrsets[i]->entry.data; |
513 | 0 | if(data->ttl < rep->ttl) |
514 | 0 | rep->ttl = data->ttl; |
515 | |
|
516 | 0 | pset = pset->rrset_all_next; |
517 | 0 | } |
518 | 0 | rep->prefetch_ttl = PREFETCH_TTL_CALC(rep->ttl); |
519 | 0 | rep->serve_expired_ttl = rep->ttl + SERVE_EXPIRED_TTL; |
520 | | /* rep->serve_expired_norec_ttl should stay at 0 */ |
521 | 0 | log_assert(rep->serve_expired_norec_ttl == 0); |
522 | 0 | return 1; |
523 | 0 | } |
524 | | |
525 | | int |
526 | | parse_create_msg(sldns_buffer* pkt, struct msg_parse* msg, |
527 | | struct alloc_cache* alloc, struct query_info* qinf, |
528 | | struct reply_info** rep, struct regional* region) |
529 | 0 | { |
530 | 0 | log_assert(pkt && msg); |
531 | 0 | if(!parse_create_qinfo(pkt, msg, qinf, region)) |
532 | 0 | return 0; |
533 | 0 | if(!parse_create_repinfo(msg, rep, region)) |
534 | 0 | return 0; |
535 | 0 | if(!reply_info_alloc_rrset_keys(*rep, alloc, region)) { |
536 | 0 | if(!region) reply_info_parsedelete(*rep, alloc); |
537 | 0 | return 0; |
538 | 0 | } |
539 | 0 | if(!parse_copy_decompress(pkt, msg, *rep, region)) { |
540 | 0 | if(!region) reply_info_parsedelete(*rep, alloc); |
541 | 0 | return 0; |
542 | 0 | } |
543 | 0 | return 1; |
544 | 0 | } |
545 | | |
546 | | int reply_info_parse(sldns_buffer* pkt, struct alloc_cache* alloc, |
547 | | struct query_info* qinf, struct reply_info** rep, |
548 | | struct regional* region, struct edns_data* edns) |
549 | 0 | { |
550 | | /* use scratch pad region-allocator during parsing. */ |
551 | 0 | struct msg_parse* msg; |
552 | 0 | int ret; |
553 | | |
554 | 0 | qinf->qname = NULL; |
555 | 0 | qinf->local_alias = NULL; |
556 | 0 | *rep = NULL; |
557 | 0 | if(!(msg = regional_alloc(region, sizeof(*msg)))) { |
558 | 0 | return LDNS_RCODE_SERVFAIL; |
559 | 0 | } |
560 | 0 | memset(msg, 0, sizeof(*msg)); |
561 | | |
562 | 0 | sldns_buffer_set_position(pkt, 0); |
563 | 0 | if((ret = parse_packet(pkt, msg, region)) != 0) { |
564 | 0 | return ret; |
565 | 0 | } |
566 | 0 | if((ret = parse_extract_edns_from_response_msg(msg, edns, region)) != 0) |
567 | 0 | return ret; |
568 | | |
569 | | /* parse OK, allocate return structures */ |
570 | | /* this also performs dname decompression */ |
571 | 0 | if(!parse_create_msg(pkt, msg, alloc, qinf, rep, NULL)) { |
572 | 0 | query_info_clear(qinf); |
573 | 0 | *rep = NULL; |
574 | 0 | return LDNS_RCODE_SERVFAIL; |
575 | 0 | } |
576 | 0 | return 0; |
577 | 0 | } |
578 | | |
579 | | /** helper compare function to sort in lock order */ |
580 | | static int |
581 | | reply_info_sortref_cmp(const void* a, const void* b) |
582 | 0 | { |
583 | 0 | struct rrset_ref* x = (struct rrset_ref*)a; |
584 | 0 | struct rrset_ref* y = (struct rrset_ref*)b; |
585 | 0 | if(x->key < y->key) return -1; |
586 | 0 | if(x->key > y->key) return 1; |
587 | 0 | return 0; |
588 | 0 | } |
589 | | |
590 | | void |
591 | | reply_info_sortref(struct reply_info* rep) |
592 | 0 | { |
593 | 0 | qsort(&rep->ref[0], rep->rrset_count, sizeof(struct rrset_ref), |
594 | 0 | reply_info_sortref_cmp); |
595 | 0 | } |
596 | | |
597 | | void |
598 | | reply_info_set_ttls(struct reply_info* rep, time_t timenow) |
599 | 0 | { |
600 | 0 | size_t i, j; |
601 | 0 | rep->ttl += timenow; |
602 | 0 | rep->prefetch_ttl += timenow; |
603 | 0 | rep->serve_expired_ttl += timenow; |
604 | | /* Don't set rep->serve_expired_norec_ttl; this should only be set |
605 | | * on cached records when encountering an error */ |
606 | 0 | log_assert(rep->serve_expired_norec_ttl == 0); |
607 | 0 | for(i=0; i<rep->rrset_count; i++) { |
608 | 0 | struct packed_rrset_data* data = (struct packed_rrset_data*) |
609 | 0 | rep->ref[i].key->entry.data; |
610 | 0 | if(i>0 && rep->ref[i].key == rep->ref[i-1].key) |
611 | 0 | continue; |
612 | 0 | data->ttl += timenow; |
613 | 0 | for(j=0; j<data->count + data->rrsig_count; j++) { |
614 | 0 | data->rr_ttl[j] += timenow; |
615 | 0 | } |
616 | 0 | data->ttl_add = timenow; |
617 | 0 | } |
618 | 0 | } |
619 | | |
620 | | void |
621 | | reply_info_parsedelete(struct reply_info* rep, struct alloc_cache* alloc) |
622 | 0 | { |
623 | 0 | size_t i; |
624 | 0 | if(!rep) |
625 | 0 | return; |
626 | | /* no need to lock, since not shared in hashtables. */ |
627 | 0 | for(i=0; i<rep->rrset_count; i++) { |
628 | 0 | ub_packed_rrset_parsedelete(rep->rrsets[i], alloc); |
629 | 0 | } |
630 | 0 | if(rep->reason_bogus_str) { |
631 | 0 | free(rep->reason_bogus_str); |
632 | 0 | rep->reason_bogus_str = NULL; |
633 | 0 | } |
634 | 0 | free(rep); |
635 | 0 | } |
636 | | |
637 | | int |
638 | | query_info_parse(struct query_info* m, sldns_buffer* query) |
639 | 0 | { |
640 | 0 | uint8_t* q = sldns_buffer_begin(query); |
641 | | /* minimum size: header + \0 + qtype + qclass */ |
642 | 0 | if(sldns_buffer_limit(query) < LDNS_HEADER_SIZE + 5) |
643 | 0 | return 0; |
644 | 0 | if((LDNS_OPCODE_WIRE(q) != LDNS_PACKET_QUERY && LDNS_OPCODE_WIRE(q) != |
645 | 0 | LDNS_PACKET_NOTIFY) || LDNS_QDCOUNT(q) != 1 || |
646 | 0 | sldns_buffer_position(query) != 0) |
647 | 0 | return 0; |
648 | 0 | sldns_buffer_skip(query, LDNS_HEADER_SIZE); |
649 | 0 | m->qname = sldns_buffer_current(query); |
650 | 0 | if((m->qname_len = query_dname_len(query)) == 0) |
651 | 0 | return 0; /* parse error */ |
652 | 0 | if(sldns_buffer_remaining(query) < 4) |
653 | 0 | return 0; /* need qtype, qclass */ |
654 | 0 | m->qtype = sldns_buffer_read_u16(query); |
655 | 0 | m->qclass = sldns_buffer_read_u16(query); |
656 | 0 | m->local_alias = NULL; |
657 | 0 | return 1; |
658 | 0 | } |
659 | | |
660 | | /** tiny subroutine for msgreply_compare */ |
661 | | #define COMPARE_IT(x, y) \ |
662 | 0 | if( (x) < (y) ) return -1; \ |
663 | 0 | else if( (x) > (y) ) return +1; \ |
664 | 0 | log_assert( (x) == (y) ); |
665 | | |
666 | | int |
667 | | query_info_compare(void* m1, void* m2) |
668 | 0 | { |
669 | 0 | struct query_info* msg1 = (struct query_info*)m1; |
670 | 0 | struct query_info* msg2 = (struct query_info*)m2; |
671 | 0 | int mc; |
672 | | /* from most different to least different for speed */ |
673 | 0 | COMPARE_IT(msg1->qtype, msg2->qtype); |
674 | 0 | if((mc = query_dname_compare(msg1->qname, msg2->qname)) != 0) |
675 | 0 | return mc; |
676 | 0 | log_assert(msg1->qname_len == msg2->qname_len); |
677 | 0 | COMPARE_IT(msg1->qclass, msg2->qclass); |
678 | 0 | return 0; |
679 | 0 | #undef COMPARE_IT |
680 | 0 | } |
681 | | |
682 | | void |
683 | | query_info_clear(struct query_info* m) |
684 | 0 | { |
685 | 0 | free(m->qname); |
686 | 0 | m->qname = NULL; |
687 | 0 | } |
688 | | |
689 | | size_t |
690 | | msgreply_sizefunc(void* k, void* d) |
691 | 0 | { |
692 | 0 | struct msgreply_entry* q = (struct msgreply_entry*)k; |
693 | 0 | struct reply_info* r = (struct reply_info*)d; |
694 | 0 | size_t s = sizeof(struct msgreply_entry) + sizeof(struct reply_info) |
695 | 0 | + q->key.qname_len + lock_get_mem(&q->entry.lock) |
696 | 0 | - sizeof(struct rrset_ref); |
697 | 0 | s += r->rrset_count * sizeof(struct rrset_ref); |
698 | 0 | s += r->rrset_count * sizeof(struct ub_packed_rrset_key*); |
699 | 0 | return s; |
700 | 0 | } |
701 | | |
702 | | void |
703 | | query_entry_delete(void *k, void* ATTR_UNUSED(arg)) |
704 | 0 | { |
705 | 0 | struct msgreply_entry* q = (struct msgreply_entry*)k; |
706 | 0 | lock_rw_destroy(&q->entry.lock); |
707 | 0 | query_info_clear(&q->key); |
708 | 0 | free(q); |
709 | 0 | } |
710 | | |
711 | | void |
712 | | reply_info_delete(void* d, void* ATTR_UNUSED(arg)) |
713 | 0 | { |
714 | 0 | struct reply_info* r = (struct reply_info*)d; |
715 | 0 | if(r->reason_bogus_str) { |
716 | 0 | free(r->reason_bogus_str); |
717 | 0 | r->reason_bogus_str = NULL; |
718 | 0 | } |
719 | 0 | free(r); |
720 | 0 | } |
721 | | |
722 | | hashvalue_type |
723 | | query_info_hash(struct query_info *q, uint16_t flags) |
724 | 0 | { |
725 | 0 | hashvalue_type h = 0xab; |
726 | 0 | h = hashlittle(&q->qtype, sizeof(q->qtype), h); |
727 | 0 | if(q->qtype == LDNS_RR_TYPE_AAAA && (flags&BIT_CD)) |
728 | 0 | h++; |
729 | 0 | h = hashlittle(&q->qclass, sizeof(q->qclass), h); |
730 | 0 | h = dname_query_hash(q->qname, h); |
731 | 0 | return h; |
732 | 0 | } |
733 | | |
734 | | struct msgreply_entry* |
735 | | query_info_entrysetup(struct query_info* q, struct reply_info* r, |
736 | | hashvalue_type h) |
737 | 0 | { |
738 | 0 | struct msgreply_entry* e = (struct msgreply_entry*)malloc( |
739 | 0 | sizeof(struct msgreply_entry)); |
740 | 0 | if(!e) return NULL; |
741 | 0 | memcpy(&e->key, q, sizeof(*q)); |
742 | 0 | e->entry.hash = h; |
743 | 0 | e->entry.key = e; |
744 | 0 | e->entry.data = r; |
745 | 0 | lock_rw_init(&e->entry.lock); |
746 | 0 | lock_protect(&e->entry.lock, &e->key.qname, sizeof(e->key.qname)); |
747 | 0 | lock_protect(&e->entry.lock, &e->key.qname_len, sizeof(e->key.qname_len)); |
748 | 0 | lock_protect(&e->entry.lock, &e->key.qtype, sizeof(e->key.qtype)); |
749 | 0 | lock_protect(&e->entry.lock, &e->key.qclass, sizeof(e->key.qclass)); |
750 | 0 | lock_protect(&e->entry.lock, &e->key.local_alias, sizeof(e->key.local_alias)); |
751 | 0 | lock_protect(&e->entry.lock, &e->entry.hash, sizeof(e->entry.hash)); |
752 | 0 | lock_protect(&e->entry.lock, &e->entry.key, sizeof(e->entry.key)); |
753 | 0 | lock_protect(&e->entry.lock, &e->entry.data, sizeof(e->entry.data)); |
754 | 0 | lock_protect(&e->entry.lock, e->key.qname, e->key.qname_len); |
755 | 0 | q->qname = NULL; |
756 | 0 | return e; |
757 | 0 | } |
758 | | |
759 | | /** copy rrsets from replyinfo to dest replyinfo */ |
760 | | static int |
761 | | repinfo_copy_rrsets(struct reply_info* dest, struct reply_info* from, |
762 | | struct regional* region) |
763 | 0 | { |
764 | 0 | size_t i, s; |
765 | 0 | struct packed_rrset_data* fd, *dd; |
766 | 0 | struct ub_packed_rrset_key* fk, *dk; |
767 | 0 | for(i=0; i<dest->rrset_count; i++) { |
768 | 0 | fk = from->rrsets[i]; |
769 | 0 | dk = dest->rrsets[i]; |
770 | 0 | fd = (struct packed_rrset_data*)fk->entry.data; |
771 | 0 | dk->entry.hash = fk->entry.hash; |
772 | 0 | dk->rk = fk->rk; |
773 | 0 | if(region) { |
774 | 0 | dk->id = fk->id; |
775 | 0 | dk->rk.dname = (uint8_t*)regional_alloc_init(region, |
776 | 0 | fk->rk.dname, fk->rk.dname_len); |
777 | 0 | } else |
778 | 0 | dk->rk.dname = (uint8_t*)memdup(fk->rk.dname, |
779 | 0 | fk->rk.dname_len); |
780 | 0 | if(!dk->rk.dname) |
781 | 0 | return 0; |
782 | 0 | s = packed_rrset_sizeof(fd); |
783 | 0 | if(region) |
784 | 0 | dd = (struct packed_rrset_data*)regional_alloc_init( |
785 | 0 | region, fd, s); |
786 | 0 | else dd = (struct packed_rrset_data*)memdup(fd, s); |
787 | 0 | if(!dd) |
788 | 0 | return 0; |
789 | 0 | packed_rrset_ptr_fixup(dd); |
790 | 0 | dk->entry.data = (void*)dd; |
791 | 0 | } |
792 | 0 | return 1; |
793 | 0 | } |
794 | | |
795 | | struct reply_info* |
796 | | reply_info_copy(struct reply_info* rep, struct alloc_cache* alloc, |
797 | | struct regional* region) |
798 | 0 | { |
799 | 0 | struct reply_info* cp; |
800 | 0 | cp = construct_reply_info_base(region, rep->flags, rep->qdcount, |
801 | 0 | rep->ttl, rep->prefetch_ttl, rep->serve_expired_ttl, |
802 | 0 | rep->serve_expired_norec_ttl, |
803 | 0 | rep->an_numrrsets, rep->ns_numrrsets, rep->ar_numrrsets, |
804 | 0 | rep->rrset_count, rep->security, rep->reason_bogus); |
805 | 0 | if(!cp) |
806 | 0 | return NULL; |
807 | | |
808 | 0 | if(rep->reason_bogus_str && *rep->reason_bogus_str != 0) { |
809 | 0 | if(region) { |
810 | 0 | cp->reason_bogus_str = (char*)regional_alloc(region, |
811 | 0 | sizeof(char) |
812 | 0 | * (strlen(rep->reason_bogus_str)+1)); |
813 | 0 | } else { |
814 | 0 | cp->reason_bogus_str = malloc(sizeof(char) |
815 | 0 | * (strlen(rep->reason_bogus_str)+1)); |
816 | 0 | } |
817 | 0 | if(!cp->reason_bogus_str) { |
818 | 0 | if(!region) |
819 | 0 | reply_info_parsedelete(cp, alloc); |
820 | 0 | return NULL; |
821 | 0 | } |
822 | 0 | memcpy(cp->reason_bogus_str, rep->reason_bogus_str, |
823 | 0 | strlen(rep->reason_bogus_str)+1); |
824 | 0 | } |
825 | | |
826 | | /* allocate ub_key structures special or not */ |
827 | 0 | if(!reply_info_alloc_rrset_keys(cp, alloc, region)) { |
828 | 0 | if(!region) |
829 | 0 | reply_info_parsedelete(cp, alloc); |
830 | 0 | return NULL; |
831 | 0 | } |
832 | 0 | if(!repinfo_copy_rrsets(cp, rep, region)) { |
833 | 0 | if(!region) |
834 | 0 | reply_info_parsedelete(cp, alloc); |
835 | 0 | return NULL; |
836 | 0 | } |
837 | 0 | return cp; |
838 | 0 | } |
839 | | |
840 | | uint8_t* |
841 | | reply_find_final_cname_target(struct query_info* qinfo, struct reply_info* rep) |
842 | 0 | { |
843 | 0 | uint8_t* sname = qinfo->qname; |
844 | 0 | size_t snamelen = qinfo->qname_len; |
845 | 0 | size_t i; |
846 | 0 | for(i=0; i<rep->an_numrrsets; i++) { |
847 | 0 | struct ub_packed_rrset_key* s = rep->rrsets[i]; |
848 | | /* follow CNAME chain (if any) */ |
849 | 0 | if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME && |
850 | 0 | ntohs(s->rk.rrset_class) == qinfo->qclass && |
851 | 0 | snamelen == s->rk.dname_len && |
852 | 0 | query_dname_compare(sname, s->rk.dname) == 0) { |
853 | 0 | get_cname_target(s, &sname, &snamelen); |
854 | 0 | } |
855 | 0 | } |
856 | 0 | if(sname != qinfo->qname) |
857 | 0 | return sname; |
858 | 0 | return NULL; |
859 | 0 | } |
860 | | |
861 | | struct ub_packed_rrset_key* |
862 | | reply_find_answer_rrset(struct query_info* qinfo, struct reply_info* rep) |
863 | 0 | { |
864 | 0 | uint8_t* sname = qinfo->qname; |
865 | 0 | size_t snamelen = qinfo->qname_len; |
866 | 0 | size_t i; |
867 | 0 | for(i=0; i<rep->an_numrrsets; i++) { |
868 | 0 | struct ub_packed_rrset_key* s = rep->rrsets[i]; |
869 | | /* first match type, for query of qtype cname */ |
870 | 0 | if(ntohs(s->rk.type) == qinfo->qtype && |
871 | 0 | ntohs(s->rk.rrset_class) == qinfo->qclass && |
872 | 0 | snamelen == s->rk.dname_len && |
873 | 0 | query_dname_compare(sname, s->rk.dname) == 0) { |
874 | 0 | return s; |
875 | 0 | } |
876 | | /* follow CNAME chain (if any) */ |
877 | 0 | if(ntohs(s->rk.type) == LDNS_RR_TYPE_CNAME && |
878 | 0 | ntohs(s->rk.rrset_class) == qinfo->qclass && |
879 | 0 | snamelen == s->rk.dname_len && |
880 | 0 | query_dname_compare(sname, s->rk.dname) == 0) { |
881 | 0 | get_cname_target(s, &sname, &snamelen); |
882 | 0 | } |
883 | 0 | } |
884 | 0 | return NULL; |
885 | 0 | } |
886 | | |
887 | | struct ub_packed_rrset_key* reply_find_rrset_section_an(struct reply_info* rep, |
888 | | uint8_t* name, size_t namelen, uint16_t type, uint16_t dclass) |
889 | 0 | { |
890 | 0 | size_t i; |
891 | 0 | for(i=0; i<rep->an_numrrsets; i++) { |
892 | 0 | struct ub_packed_rrset_key* s = rep->rrsets[i]; |
893 | 0 | if(ntohs(s->rk.type) == type && |
894 | 0 | ntohs(s->rk.rrset_class) == dclass && |
895 | 0 | namelen == s->rk.dname_len && |
896 | 0 | query_dname_compare(name, s->rk.dname) == 0) { |
897 | 0 | return s; |
898 | 0 | } |
899 | 0 | } |
900 | 0 | return NULL; |
901 | 0 | } |
902 | | |
903 | | struct ub_packed_rrset_key* reply_find_rrset_section_ns(struct reply_info* rep, |
904 | | uint8_t* name, size_t namelen, uint16_t type, uint16_t dclass) |
905 | 0 | { |
906 | 0 | size_t i; |
907 | 0 | for(i=rep->an_numrrsets; i<rep->an_numrrsets+rep->ns_numrrsets; i++) { |
908 | 0 | struct ub_packed_rrset_key* s = rep->rrsets[i]; |
909 | 0 | if(ntohs(s->rk.type) == type && |
910 | 0 | ntohs(s->rk.rrset_class) == dclass && |
911 | 0 | namelen == s->rk.dname_len && |
912 | 0 | query_dname_compare(name, s->rk.dname) == 0) { |
913 | 0 | return s; |
914 | 0 | } |
915 | 0 | } |
916 | 0 | return NULL; |
917 | 0 | } |
918 | | |
919 | | struct ub_packed_rrset_key* reply_find_rrset(struct reply_info* rep, |
920 | | uint8_t* name, size_t namelen, uint16_t type, uint16_t dclass) |
921 | 0 | { |
922 | 0 | size_t i; |
923 | 0 | for(i=0; i<rep->rrset_count; i++) { |
924 | 0 | struct ub_packed_rrset_key* s = rep->rrsets[i]; |
925 | 0 | if(ntohs(s->rk.type) == type && |
926 | 0 | ntohs(s->rk.rrset_class) == dclass && |
927 | 0 | namelen == s->rk.dname_len && |
928 | 0 | query_dname_compare(name, s->rk.dname) == 0) { |
929 | 0 | return s; |
930 | 0 | } |
931 | 0 | } |
932 | 0 | return NULL; |
933 | 0 | } |
934 | | |
935 | | void |
936 | | log_dns_msg(const char* str, struct query_info* qinfo, struct reply_info* rep) |
937 | 0 | { |
938 | | /* not particularly fast but flexible, make wireformat and print */ |
939 | 0 | sldns_buffer* buf = sldns_buffer_new(65535); |
940 | 0 | struct regional* region = regional_create(); |
941 | 0 | if(!(buf && region)) { |
942 | 0 | log_err("%s: log_dns_msg: out of memory", str); |
943 | 0 | sldns_buffer_free(buf); |
944 | 0 | regional_destroy(region); |
945 | 0 | return; |
946 | 0 | } |
947 | 0 | if(!reply_info_encode(qinfo, rep, 0, rep->flags, buf, 0, |
948 | 0 | region, 65535, 1, 0)) { |
949 | 0 | log_err("%s: log_dns_msg: out of memory", str); |
950 | 0 | } else { |
951 | 0 | char* s = sldns_wire2str_pkt(sldns_buffer_begin(buf), |
952 | 0 | sldns_buffer_limit(buf)); |
953 | 0 | if(!s) { |
954 | 0 | log_info("%s: log_dns_msg: ldns tostr failed", str); |
955 | 0 | } else { |
956 | 0 | log_info("%s %s", str, s); |
957 | 0 | } |
958 | 0 | free(s); |
959 | 0 | } |
960 | 0 | sldns_buffer_free(buf); |
961 | 0 | regional_destroy(region); |
962 | 0 | } |
963 | | |
964 | | void |
965 | | log_reply_info(enum verbosity_value v, struct query_info *qinf, |
966 | | struct sockaddr_storage *addr, socklen_t addrlen, struct timeval dur, |
967 | | int cached, struct sldns_buffer *rmsg, struct sockaddr_storage* daddr, |
968 | | enum comm_point_type tp, void* ssl) |
969 | 0 | { |
970 | 0 | char clientip_buf[128]; |
971 | 0 | char rcode_buf[16]; |
972 | 0 | char dest_buf[160]; |
973 | 0 | uint16_t rcode = FLAGS_GET_RCODE(sldns_buffer_read_u16_at(rmsg, 2)); |
974 | |
|
975 | 0 | if(verbosity < v) |
976 | 0 | return; |
977 | | |
978 | 0 | sldns_wire2str_rcode_buf((int)rcode, rcode_buf, sizeof(rcode_buf)); |
979 | 0 | addr_to_str(addr, addrlen, clientip_buf, sizeof(clientip_buf)); |
980 | 0 | if(daddr) { |
981 | 0 | char da[128]; |
982 | 0 | int port = 0; |
983 | 0 | char* comm; |
984 | 0 | if(daddr->ss_family == AF_INET6) { |
985 | 0 | struct sockaddr_in6 *d = (struct sockaddr_in6 *)daddr; |
986 | 0 | if(inet_ntop(d->sin6_family, &d->sin6_addr, da, |
987 | 0 | sizeof(da)) == 0) |
988 | 0 | snprintf(dest_buf, sizeof(dest_buf), |
989 | 0 | "(inet_ntop_error)"); |
990 | 0 | port = ntohs(d->sin6_port); |
991 | 0 | } else if(daddr->ss_family == AF_INET) { |
992 | 0 | struct sockaddr_in *d = (struct sockaddr_in *)daddr; |
993 | 0 | if(inet_ntop(d->sin_family, &d->sin_addr, da, |
994 | 0 | sizeof(da)) == 0) |
995 | 0 | snprintf(dest_buf, sizeof(dest_buf), |
996 | 0 | "(inet_ntop_error)"); |
997 | 0 | port = ntohs(d->sin_port); |
998 | 0 | } else { |
999 | 0 | snprintf(da, sizeof(da), "socket%d", |
1000 | 0 | (int)daddr->ss_family); |
1001 | 0 | } |
1002 | 0 | comm = "udp"; |
1003 | 0 | if(tp == comm_tcp) comm = (ssl?"dot":"tcp"); |
1004 | 0 | else if(tp == comm_tcp_accept) comm = (ssl?"dot":"tcp"); |
1005 | 0 | else if(tp == comm_http) comm = "doh"; |
1006 | 0 | else if(tp == comm_local) comm = "unix"; |
1007 | 0 | else if(tp == comm_raw) comm = "raw"; |
1008 | 0 | snprintf(dest_buf, sizeof(dest_buf), " on %s %s %d", |
1009 | 0 | comm, da, port); |
1010 | 0 | } else { |
1011 | 0 | dest_buf[0]=0; |
1012 | 0 | } |
1013 | 0 | if(rcode == LDNS_RCODE_FORMERR) |
1014 | 0 | { |
1015 | 0 | if(LOG_TAG_QUERYREPLY) |
1016 | 0 | log_reply("%s - - - %s - - -%s", clientip_buf, |
1017 | 0 | rcode_buf, dest_buf); |
1018 | 0 | else log_info("%s - - - %s - - -%s", clientip_buf, |
1019 | 0 | rcode_buf, dest_buf); |
1020 | 0 | } else { |
1021 | 0 | char qname_buf[LDNS_MAX_DOMAINLEN]; |
1022 | 0 | char type_buf[16]; |
1023 | 0 | char class_buf[16]; |
1024 | 0 | size_t pktlen; |
1025 | 0 | if(qinf->qname) |
1026 | 0 | dname_str(qinf->qname, qname_buf); |
1027 | 0 | else snprintf(qname_buf, sizeof(qname_buf), "null"); |
1028 | 0 | pktlen = sldns_buffer_limit(rmsg); |
1029 | 0 | sldns_wire2str_type_buf(qinf->qtype, type_buf, sizeof(type_buf)); |
1030 | 0 | sldns_wire2str_class_buf(qinf->qclass, class_buf, sizeof(class_buf)); |
1031 | 0 | if(LOG_TAG_QUERYREPLY) |
1032 | 0 | log_reply("%s %s %s %s %s " ARG_LL "d.%6.6d %d %d%s", |
1033 | 0 | clientip_buf, qname_buf, type_buf, class_buf, |
1034 | 0 | rcode_buf, (long long)dur.tv_sec, (int)dur.tv_usec, |
1035 | 0 | cached, (int)pktlen, dest_buf); |
1036 | 0 | else log_info("%s %s %s %s %s " ARG_LL "d.%6.6d %d %d%s", |
1037 | 0 | clientip_buf, qname_buf, type_buf, class_buf, |
1038 | 0 | rcode_buf, (long long)dur.tv_sec, (int)dur.tv_usec, |
1039 | 0 | cached, (int)pktlen, dest_buf); |
1040 | 0 | } |
1041 | 0 | } |
1042 | | |
1043 | | void |
1044 | | log_query_info(enum verbosity_value v, const char* str, |
1045 | | struct query_info* qinf) |
1046 | 0 | { |
1047 | 0 | log_nametypeclass(v, str, qinf->qname, qinf->qtype, qinf->qclass); |
1048 | 0 | } |
1049 | | |
1050 | | int |
1051 | | reply_check_cname_chain(struct query_info* qinfo, struct reply_info* rep) |
1052 | 0 | { |
1053 | | /* check only answer section rrs for matching cname chain. |
1054 | | * the cache may return changed rdata, but owner names are untouched.*/ |
1055 | 0 | size_t i; |
1056 | 0 | uint8_t* sname = qinfo->qname; |
1057 | 0 | size_t snamelen = qinfo->qname_len; |
1058 | 0 | for(i=0; i<rep->an_numrrsets; i++) { |
1059 | 0 | uint16_t t = ntohs(rep->rrsets[i]->rk.type); |
1060 | 0 | if(t == LDNS_RR_TYPE_DNAME) |
1061 | 0 | continue; /* skip dnames; note TTL 0 not cached */ |
1062 | | /* verify that owner matches current sname */ |
1063 | 0 | if(query_dname_compare(sname, rep->rrsets[i]->rk.dname) != 0){ |
1064 | | /* cname chain broken */ |
1065 | 0 | return 0; |
1066 | 0 | } |
1067 | | /* if this is a cname; move on */ |
1068 | 0 | if(t == LDNS_RR_TYPE_CNAME) { |
1069 | 0 | get_cname_target(rep->rrsets[i], &sname, &snamelen); |
1070 | 0 | } |
1071 | 0 | } |
1072 | 0 | return 1; |
1073 | 0 | } |
1074 | | |
1075 | | int |
1076 | | reply_all_rrsets_secure(struct reply_info* rep) |
1077 | 0 | { |
1078 | 0 | size_t i; |
1079 | 0 | for(i=0; i<rep->rrset_count; i++) { |
1080 | 0 | if( ((struct packed_rrset_data*)rep->rrsets[i]->entry.data) |
1081 | 0 | ->security != sec_status_secure ) |
1082 | 0 | return 0; |
1083 | 0 | } |
1084 | 0 | return 1; |
1085 | 0 | } |
1086 | | |
1087 | | struct reply_info* |
1088 | | parse_reply_in_temp_region(sldns_buffer* pkt, struct regional* region, |
1089 | | struct query_info* qi) |
1090 | 0 | { |
1091 | 0 | struct reply_info* rep; |
1092 | 0 | struct msg_parse* msg; |
1093 | 0 | if(!(msg = regional_alloc(region, sizeof(*msg)))) { |
1094 | 0 | return NULL; |
1095 | 0 | } |
1096 | 0 | memset(msg, 0, sizeof(*msg)); |
1097 | 0 | sldns_buffer_set_position(pkt, 0); |
1098 | 0 | if(parse_packet(pkt, msg, region) != 0){ |
1099 | 0 | return 0; |
1100 | 0 | } |
1101 | 0 | if(!parse_create_msg(pkt, msg, NULL, qi, &rep, region)) { |
1102 | 0 | return 0; |
1103 | 0 | } |
1104 | 0 | return rep; |
1105 | 0 | } |
1106 | | |
1107 | | int edns_opt_list_append_ede(struct edns_option** list, struct regional* region, |
1108 | | sldns_ede_code code, const char *txt) |
1109 | 0 | { |
1110 | 0 | struct edns_option** prevp; |
1111 | 0 | struct edns_option* opt; |
1112 | 0 | size_t txt_len = txt ? strlen(txt) : 0; |
1113 | | |
1114 | | /* allocate new element */ |
1115 | 0 | opt = (struct edns_option*)regional_alloc(region, sizeof(*opt)); |
1116 | 0 | if(!opt) |
1117 | 0 | return 0; |
1118 | 0 | opt->next = NULL; |
1119 | 0 | opt->opt_code = LDNS_EDNS_EDE; |
1120 | 0 | opt->opt_len = txt_len + sizeof(uint16_t); |
1121 | 0 | opt->opt_data = regional_alloc(region, txt_len + sizeof(uint16_t)); |
1122 | 0 | if(!opt->opt_data) |
1123 | 0 | return 0; |
1124 | 0 | sldns_write_uint16(opt->opt_data, (uint16_t)code); |
1125 | 0 | if (txt_len) |
1126 | 0 | memmove(opt->opt_data + 2, txt, txt_len); |
1127 | | |
1128 | | /* append at end of list */ |
1129 | 0 | prevp = list; |
1130 | 0 | while(*prevp != NULL) |
1131 | 0 | prevp = &((*prevp)->next); |
1132 | 0 | verbose(VERB_ALGO, "attached EDE code: %d with message: %s", code, (txt?txt:"\"\"")); |
1133 | 0 | *prevp = opt; |
1134 | 0 | return 1; |
1135 | 0 | } |
1136 | | |
1137 | | int edns_opt_list_append_keepalive(struct edns_option** list, int msec, |
1138 | | struct regional* region) |
1139 | 0 | { |
1140 | 0 | uint8_t data[2]; /* For keepalive value */ |
1141 | 0 | data[0] = (uint8_t)((msec >> 8) & 0xff); |
1142 | 0 | data[1] = (uint8_t)(msec & 0xff); |
1143 | 0 | return edns_opt_list_append(list, LDNS_EDNS_KEEPALIVE, sizeof(data), |
1144 | 0 | data, region); |
1145 | 0 | } |
1146 | | |
1147 | | int edns_opt_list_append(struct edns_option** list, uint16_t code, size_t len, |
1148 | | uint8_t* data, struct regional* region) |
1149 | 7.33k | { |
1150 | 7.33k | struct edns_option** prevp; |
1151 | 7.33k | struct edns_option* opt; |
1152 | | |
1153 | | /* allocate new element */ |
1154 | 7.33k | opt = (struct edns_option*)regional_alloc(region, sizeof(*opt)); |
1155 | 7.33k | if(!opt) |
1156 | 0 | return 0; |
1157 | 7.33k | opt->next = NULL; |
1158 | 7.33k | opt->opt_code = code; |
1159 | 7.33k | opt->opt_len = len; |
1160 | 7.33k | opt->opt_data = NULL; |
1161 | 7.33k | if(len > 0) { |
1162 | 676 | opt->opt_data = regional_alloc_init(region, data, len); |
1163 | 676 | if(!opt->opt_data) |
1164 | 0 | return 0; |
1165 | 676 | } |
1166 | | |
1167 | | /* append at end of list */ |
1168 | 7.33k | prevp = list; |
1169 | 2.46M | while(*prevp != NULL) { |
1170 | 2.45M | prevp = &((*prevp)->next); |
1171 | 2.45M | } |
1172 | 7.33k | *prevp = opt; |
1173 | 7.33k | return 1; |
1174 | 7.33k | } |
1175 | | |
1176 | | int edns_opt_list_remove(struct edns_option** list, uint16_t code) |
1177 | 0 | { |
1178 | | /* The list should already be allocated in a region. Freeing the |
1179 | | * allocated space in a region is not possible. We just unlink the |
1180 | | * required elements and they will be freed together with the region. */ |
1181 | |
|
1182 | 0 | struct edns_option* prev; |
1183 | 0 | struct edns_option* curr; |
1184 | 0 | if(!list || !(*list)) return 0; |
1185 | | |
1186 | | /* Unlink and repoint if the element(s) are first in list */ |
1187 | 0 | while(list && *list && (*list)->opt_code == code) { |
1188 | 0 | *list = (*list)->next; |
1189 | 0 | } |
1190 | |
|
1191 | 0 | if(!list || !(*list)) return 1; |
1192 | | /* Unlink elements and reattach the list */ |
1193 | 0 | prev = *list; |
1194 | 0 | curr = (*list)->next; |
1195 | 0 | while(curr != NULL) { |
1196 | 0 | if(curr->opt_code == code) { |
1197 | 0 | prev->next = curr->next; |
1198 | 0 | curr = curr->next; |
1199 | 0 | } else { |
1200 | 0 | prev = curr; |
1201 | 0 | curr = curr->next; |
1202 | 0 | } |
1203 | 0 | } |
1204 | 0 | return 1; |
1205 | 0 | } |
1206 | | |
1207 | | static int inplace_cb_reply_call_generic( |
1208 | | struct inplace_cb* callback_list, enum inplace_cb_list_type type, |
1209 | | struct query_info* qinfo, struct module_qstate* qstate, |
1210 | | struct reply_info* rep, int rcode, struct edns_data* edns, |
1211 | | struct comm_reply* repinfo, struct regional* region, |
1212 | | struct timeval* start_time) |
1213 | 0 | { |
1214 | 0 | struct inplace_cb* cb; |
1215 | 0 | struct edns_option* opt_list_out = NULL; |
1216 | | #if defined(EXPORT_ALL_SYMBOLS) |
1217 | | (void)type; /* param not used when fptr_ok disabled */ |
1218 | | #endif |
1219 | 0 | if(qstate) |
1220 | 0 | opt_list_out = qstate->edns_opts_front_out; |
1221 | 0 | for(cb=callback_list; cb; cb=cb->next) { |
1222 | 0 | fptr_ok(fptr_whitelist_inplace_cb_reply_generic( |
1223 | 0 | (inplace_cb_reply_func_type*)cb->cb, type)); |
1224 | 0 | (void)(*(inplace_cb_reply_func_type*)cb->cb)(qinfo, qstate, rep, |
1225 | 0 | rcode, edns, &opt_list_out, repinfo, region, start_time, cb->id, cb->cb_arg); |
1226 | 0 | } |
1227 | 0 | edns->opt_list_inplace_cb_out = opt_list_out; |
1228 | 0 | return 1; |
1229 | 0 | } |
1230 | | |
1231 | | int inplace_cb_reply_call(struct module_env* env, struct query_info* qinfo, |
1232 | | struct module_qstate* qstate, struct reply_info* rep, int rcode, |
1233 | | struct edns_data* edns, struct comm_reply* repinfo, struct regional* region, |
1234 | | struct timeval* start_time) |
1235 | 0 | { |
1236 | 0 | return inplace_cb_reply_call_generic( |
1237 | 0 | env->inplace_cb_lists[inplace_cb_reply], inplace_cb_reply, qinfo, |
1238 | 0 | qstate, rep, rcode, edns, repinfo, region, start_time); |
1239 | 0 | } |
1240 | | |
1241 | | int inplace_cb_reply_cache_call(struct module_env* env, |
1242 | | struct query_info* qinfo, struct module_qstate* qstate, |
1243 | | struct reply_info* rep, int rcode, struct edns_data* edns, |
1244 | | struct comm_reply* repinfo, struct regional* region, |
1245 | | struct timeval* start_time) |
1246 | 0 | { |
1247 | 0 | return inplace_cb_reply_call_generic( |
1248 | 0 | env->inplace_cb_lists[inplace_cb_reply_cache], inplace_cb_reply_cache, |
1249 | 0 | qinfo, qstate, rep, rcode, edns, repinfo, region, start_time); |
1250 | 0 | } |
1251 | | |
1252 | | int inplace_cb_reply_local_call(struct module_env* env, |
1253 | | struct query_info* qinfo, struct module_qstate* qstate, |
1254 | | struct reply_info* rep, int rcode, struct edns_data* edns, |
1255 | | struct comm_reply* repinfo, struct regional* region, |
1256 | | struct timeval* start_time) |
1257 | 0 | { |
1258 | 0 | return inplace_cb_reply_call_generic( |
1259 | 0 | env->inplace_cb_lists[inplace_cb_reply_local], inplace_cb_reply_local, |
1260 | 0 | qinfo, qstate, rep, rcode, edns, repinfo, region, start_time); |
1261 | 0 | } |
1262 | | |
1263 | | int inplace_cb_reply_servfail_call(struct module_env* env, |
1264 | | struct query_info* qinfo, struct module_qstate* qstate, |
1265 | | struct reply_info* rep, int rcode, struct edns_data* edns, |
1266 | | struct comm_reply* repinfo, struct regional* region, |
1267 | | struct timeval* start_time) |
1268 | 0 | { |
1269 | | /* We are going to servfail. Remove any potential edns options. */ |
1270 | 0 | if(qstate) |
1271 | 0 | qstate->edns_opts_front_out = NULL; |
1272 | 0 | return inplace_cb_reply_call_generic( |
1273 | 0 | env->inplace_cb_lists[inplace_cb_reply_servfail], |
1274 | 0 | inplace_cb_reply_servfail, qinfo, qstate, rep, rcode, edns, repinfo, |
1275 | 0 | region, start_time); |
1276 | 0 | } |
1277 | | |
1278 | | int inplace_cb_query_call(struct module_env* env, struct query_info* qinfo, |
1279 | | uint16_t flags, struct sockaddr_storage* addr, socklen_t addrlen, |
1280 | | uint8_t* zone, size_t zonelen, struct module_qstate* qstate, |
1281 | | struct regional* region) |
1282 | 0 | { |
1283 | 0 | struct inplace_cb* cb = env->inplace_cb_lists[inplace_cb_query]; |
1284 | 0 | for(; cb; cb=cb->next) { |
1285 | 0 | fptr_ok(fptr_whitelist_inplace_cb_query( |
1286 | 0 | (inplace_cb_query_func_type*)cb->cb)); |
1287 | 0 | (void)(*(inplace_cb_query_func_type*)cb->cb)(qinfo, flags, |
1288 | 0 | qstate, addr, addrlen, zone, zonelen, region, |
1289 | 0 | cb->id, cb->cb_arg); |
1290 | 0 | } |
1291 | 0 | return 1; |
1292 | 0 | } |
1293 | | |
1294 | | int inplace_cb_edns_back_parsed_call(struct module_env* env, |
1295 | | struct module_qstate* qstate) |
1296 | 0 | { |
1297 | 0 | struct inplace_cb* cb = |
1298 | 0 | env->inplace_cb_lists[inplace_cb_edns_back_parsed]; |
1299 | 0 | for(; cb; cb=cb->next) { |
1300 | 0 | fptr_ok(fptr_whitelist_inplace_cb_edns_back_parsed( |
1301 | 0 | (inplace_cb_edns_back_parsed_func_type*)cb->cb)); |
1302 | 0 | (void)(*(inplace_cb_edns_back_parsed_func_type*)cb->cb)(qstate, |
1303 | 0 | cb->id, cb->cb_arg); |
1304 | 0 | } |
1305 | 0 | return 1; |
1306 | 0 | } |
1307 | | |
1308 | | int inplace_cb_query_response_call(struct module_env* env, |
1309 | 0 | struct module_qstate* qstate, struct dns_msg* response) { |
1310 | 0 | struct inplace_cb* cb = |
1311 | 0 | env->inplace_cb_lists[inplace_cb_query_response]; |
1312 | 0 | for(; cb; cb=cb->next) { |
1313 | 0 | fptr_ok(fptr_whitelist_inplace_cb_query_response( |
1314 | 0 | (inplace_cb_query_response_func_type*)cb->cb)); |
1315 | 0 | (void)(*(inplace_cb_query_response_func_type*)cb->cb)(qstate, |
1316 | 0 | response, cb->id, cb->cb_arg); |
1317 | 0 | } |
1318 | 0 | return 1; |
1319 | 0 | } |
1320 | | |
1321 | | struct edns_option* edns_opt_copy_region(struct edns_option* list, |
1322 | | struct regional* region) |
1323 | 0 | { |
1324 | 0 | struct edns_option* result = NULL, *cur = NULL, *s; |
1325 | 0 | while(list) { |
1326 | | /* copy edns option structure */ |
1327 | 0 | s = regional_alloc_init(region, list, sizeof(*list)); |
1328 | 0 | if(!s) return NULL; |
1329 | 0 | s->next = NULL; |
1330 | | |
1331 | | /* copy option data */ |
1332 | 0 | if(s->opt_data) { |
1333 | 0 | s->opt_data = regional_alloc_init(region, s->opt_data, |
1334 | 0 | s->opt_len); |
1335 | 0 | if(!s->opt_data) |
1336 | 0 | return NULL; |
1337 | 0 | } |
1338 | | |
1339 | | /* link into list */ |
1340 | 0 | if(cur) |
1341 | 0 | cur->next = s; |
1342 | 0 | else result = s; |
1343 | 0 | cur = s; |
1344 | | |
1345 | | /* examine next element */ |
1346 | 0 | list = list->next; |
1347 | 0 | } |
1348 | 0 | return result; |
1349 | 0 | } |
1350 | | |
1351 | | struct edns_option* edns_opt_copy_filter_region(struct edns_option* list, |
1352 | | uint16_t* filter_list, size_t filter_list_len, struct regional* region) |
1353 | 0 | { |
1354 | 0 | struct edns_option* result = NULL, *cur = NULL, *s; |
1355 | 0 | size_t i; |
1356 | 0 | while(list) { |
1357 | 0 | for(i=0; i<filter_list_len; i++) |
1358 | 0 | if(filter_list[i] == list->opt_code) goto found; |
1359 | 0 | if(i == filter_list_len) goto next; |
1360 | 0 | found: |
1361 | | /* copy edns option structure */ |
1362 | 0 | s = regional_alloc_init(region, list, sizeof(*list)); |
1363 | 0 | if(!s) return NULL; |
1364 | 0 | s->next = NULL; |
1365 | | |
1366 | | /* copy option data */ |
1367 | 0 | if(s->opt_data) { |
1368 | 0 | s->opt_data = regional_alloc_init(region, s->opt_data, |
1369 | 0 | s->opt_len); |
1370 | 0 | if(!s->opt_data) |
1371 | 0 | return NULL; |
1372 | 0 | } |
1373 | | |
1374 | | /* link into list */ |
1375 | 0 | if(cur) |
1376 | 0 | cur->next = s; |
1377 | 0 | else result = s; |
1378 | 0 | cur = s; |
1379 | |
|
1380 | 0 | next: |
1381 | | /* examine next element */ |
1382 | 0 | list = list->next; |
1383 | 0 | } |
1384 | 0 | return result; |
1385 | 0 | } |
1386 | | |
1387 | | int edns_opt_compare(struct edns_option* p, struct edns_option* q) |
1388 | 0 | { |
1389 | 0 | if(!p && !q) return 0; |
1390 | 0 | if(!p) return -1; |
1391 | 0 | if(!q) return 1; |
1392 | 0 | log_assert(p && q); |
1393 | 0 | if(p->opt_code != q->opt_code) |
1394 | 0 | return (int)q->opt_code - (int)p->opt_code; |
1395 | 0 | if(p->opt_len != q->opt_len) |
1396 | 0 | return (int)q->opt_len - (int)p->opt_len; |
1397 | 0 | if(p->opt_len != 0) |
1398 | 0 | return memcmp(p->opt_data, q->opt_data, p->opt_len); |
1399 | 0 | return 0; |
1400 | 0 | } |
1401 | | |
1402 | | int edns_opt_list_compare(struct edns_option* p, struct edns_option* q) |
1403 | 0 | { |
1404 | 0 | int r; |
1405 | 0 | while(p && q) { |
1406 | 0 | r = edns_opt_compare(p, q); |
1407 | 0 | if(r != 0) |
1408 | 0 | return r; |
1409 | 0 | p = p->next; |
1410 | 0 | q = q->next; |
1411 | 0 | } |
1412 | 0 | if(p || q) { |
1413 | | /* uneven length lists */ |
1414 | 0 | if(p) return 1; |
1415 | 0 | if(q) return -1; |
1416 | 0 | } |
1417 | 0 | return 0; |
1418 | 0 | } |
1419 | | |
1420 | | void edns_opt_list_free(struct edns_option* list) |
1421 | 0 | { |
1422 | 0 | struct edns_option* n; |
1423 | 0 | while(list) { |
1424 | 0 | free(list->opt_data); |
1425 | 0 | n = list->next; |
1426 | 0 | free(list); |
1427 | 0 | list = n; |
1428 | 0 | } |
1429 | 0 | } |
1430 | | |
1431 | | struct edns_option* edns_opt_copy_alloc(struct edns_option* list) |
1432 | 0 | { |
1433 | 0 | struct edns_option* result = NULL, *cur = NULL, *s; |
1434 | 0 | while(list) { |
1435 | | /* copy edns option structure */ |
1436 | 0 | s = memdup(list, sizeof(*list)); |
1437 | 0 | if(!s) { |
1438 | 0 | edns_opt_list_free(result); |
1439 | 0 | return NULL; |
1440 | 0 | } |
1441 | 0 | s->next = NULL; |
1442 | | |
1443 | | /* copy option data */ |
1444 | 0 | if(s->opt_data) { |
1445 | 0 | s->opt_data = memdup(s->opt_data, s->opt_len); |
1446 | 0 | if(!s->opt_data) { |
1447 | 0 | free(s); |
1448 | 0 | edns_opt_list_free(result); |
1449 | 0 | return NULL; |
1450 | 0 | } |
1451 | 0 | } |
1452 | | |
1453 | | /* link into list */ |
1454 | 0 | if(cur) |
1455 | 0 | cur->next = s; |
1456 | 0 | else result = s; |
1457 | 0 | cur = s; |
1458 | | |
1459 | | /* examine next element */ |
1460 | 0 | list = list->next; |
1461 | 0 | } |
1462 | 0 | return result; |
1463 | 0 | } |
1464 | | |
1465 | | struct edns_option* edns_opt_list_find(struct edns_option* list, uint16_t code) |
1466 | 0 | { |
1467 | 0 | struct edns_option* p; |
1468 | 0 | for(p=list; p; p=p->next) { |
1469 | 0 | if(p->opt_code == code) |
1470 | 0 | return p; |
1471 | 0 | } |
1472 | 0 | return NULL; |
1473 | 0 | } |
1474 | | |
1475 | | int local_alias_shallow_copy_qname(struct local_rrset* local_alias, uint8_t** qname, |
1476 | | size_t* qname_len) |
1477 | 0 | { |
1478 | 0 | struct ub_packed_rrset_key* rrset = local_alias->rrset; |
1479 | 0 | struct packed_rrset_data* d = rrset->entry.data; |
1480 | | |
1481 | | /* Sanity check: our current implementation only supports |
1482 | | * a single CNAME RRset as a local alias. */ |
1483 | 0 | if(local_alias->next || |
1484 | 0 | rrset->rk.type != htons(LDNS_RR_TYPE_CNAME) || |
1485 | 0 | d->count != 1) { |
1486 | 0 | log_err("assumption failure: unexpected local alias"); |
1487 | 0 | return 0; |
1488 | 0 | } |
1489 | 0 | *qname = d->rr_data[0] + 2; |
1490 | 0 | *qname_len = d->rr_len[0] - 2; |
1491 | 0 | return 1; |
1492 | 0 | } |