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