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