/src/unbound/util/data/msgparse.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * util/data/msgparse.c - parse wireformat DNS messages. |
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 | | * \file |
37 | | * Routines for message parsing a packet buffer to a descriptive structure. |
38 | | */ |
39 | | #include "config.h" |
40 | | #include "util/config_file.h" |
41 | | #include "util/data/msgparse.h" |
42 | | #include "util/data/msgreply.h" |
43 | | #include "util/data/dname.h" |
44 | | #include "util/data/packed_rrset.h" |
45 | | #include "util/netevent.h" |
46 | | #include "util/storage/lookup3.h" |
47 | | #include "util/regional.h" |
48 | | #include "util/rfc_1982.h" |
49 | | #include "util/edns.h" |
50 | | #include "util/net_help.h" |
51 | | #include "sldns/rrdef.h" |
52 | | #include "sldns/sbuffer.h" |
53 | | #include "sldns/parseutil.h" |
54 | | #include "sldns/wire2str.h" |
55 | | |
56 | | /** smart comparison of (compressed, valid) dnames from packet */ |
57 | | static int |
58 | | smart_compare(sldns_buffer* pkt, uint8_t* dnow, |
59 | | uint8_t* dprfirst, uint8_t* dprlast) |
60 | 0 | { |
61 | 0 | if(LABEL_IS_PTR(*dnow)) { |
62 | | /* ptr points to a previous dname */ |
63 | 0 | uint8_t* p; |
64 | 0 | if((size_t)PTR_OFFSET(dnow[0], dnow[1]) |
65 | 0 | >= sldns_buffer_limit(pkt)) |
66 | 0 | return -1; |
67 | 0 | p = sldns_buffer_at(pkt, PTR_OFFSET(dnow[0], dnow[1])); |
68 | 0 | if( p == dprfirst || p == dprlast ) |
69 | 0 | return 0; |
70 | | /* prev dname is also a ptr, both ptrs are the same. */ |
71 | 0 | if(LABEL_IS_PTR(*dprlast) && |
72 | 0 | dprlast[0] == dnow[0] && dprlast[1] == dnow[1]) |
73 | 0 | return 0; |
74 | 0 | } |
75 | 0 | return dname_pkt_compare(pkt, dnow, dprlast); |
76 | 0 | } |
77 | | |
78 | | /** |
79 | | * Allocate new rrset in region, fill with data. |
80 | | */ |
81 | | static struct rrset_parse* |
82 | | new_rrset(struct msg_parse* msg, uint8_t* dname, size_t dnamelen, |
83 | | uint16_t type, uint16_t dclass, hashvalue_type hash, |
84 | | uint32_t rrset_flags, sldns_pkt_section section, |
85 | | struct regional* region) |
86 | 0 | { |
87 | 0 | struct rrset_parse* p = regional_alloc(region, sizeof(*p)); |
88 | 0 | if(!p) return NULL; |
89 | 0 | p->rrset_bucket_next = msg->hashtable[hash & (PARSE_TABLE_SIZE-1)]; |
90 | 0 | msg->hashtable[hash & (PARSE_TABLE_SIZE-1)] = p; |
91 | 0 | p->rrset_all_next = 0; |
92 | 0 | if(msg->rrset_last) |
93 | 0 | msg->rrset_last->rrset_all_next = p; |
94 | 0 | else msg->rrset_first = p; |
95 | 0 | msg->rrset_last = p; |
96 | 0 | p->hash = hash; |
97 | 0 | p->section = section; |
98 | 0 | p->dname = dname; |
99 | 0 | p->dname_len = dnamelen; |
100 | 0 | p->type = type; |
101 | 0 | p->rrset_class = dclass; |
102 | 0 | p->flags = rrset_flags; |
103 | 0 | p->rr_count = 0; |
104 | 0 | p->size = 0; |
105 | 0 | p->rr_first = 0; |
106 | 0 | p->rr_last = 0; |
107 | 0 | p->rrsig_count = 0; |
108 | 0 | p->rrsig_first = 0; |
109 | 0 | p->rrsig_last = 0; |
110 | 0 | return p; |
111 | 0 | } |
112 | | |
113 | | /** See if next rrset is nsec at zone apex */ |
114 | | static int |
115 | | nsec_at_apex(sldns_buffer* pkt) |
116 | 0 | { |
117 | | /* we are at ttl position in packet. */ |
118 | 0 | size_t pos = sldns_buffer_position(pkt); |
119 | 0 | uint16_t rdatalen; |
120 | 0 | if(sldns_buffer_remaining(pkt) < 7) /* ttl+len+root */ |
121 | 0 | return 0; /* eek! */ |
122 | 0 | sldns_buffer_skip(pkt, 4); /* ttl */; |
123 | 0 | rdatalen = sldns_buffer_read_u16(pkt); |
124 | 0 | if(sldns_buffer_remaining(pkt) < rdatalen) { |
125 | 0 | sldns_buffer_set_position(pkt, pos); |
126 | 0 | return 0; /* parse error happens later */ |
127 | 0 | } |
128 | | /* must validate the nsec next domain name format */ |
129 | 0 | if(pkt_dname_len(pkt) == 0) { |
130 | 0 | sldns_buffer_set_position(pkt, pos); |
131 | 0 | return 0; /* parse error */ |
132 | 0 | } |
133 | | |
134 | | /* see if SOA bit is set. */ |
135 | 0 | if(sldns_buffer_position(pkt) < pos+4+rdatalen) { |
136 | | /* nsec type bitmap contains items */ |
137 | 0 | uint8_t win, blen, bits; |
138 | | /* need: windownum, bitmap len, firstbyte */ |
139 | 0 | if(sldns_buffer_position(pkt)+3 > pos+4+rdatalen) { |
140 | 0 | sldns_buffer_set_position(pkt, pos); |
141 | 0 | return 0; /* malformed nsec */ |
142 | 0 | } |
143 | 0 | win = sldns_buffer_read_u8(pkt); |
144 | 0 | blen = sldns_buffer_read_u8(pkt); |
145 | 0 | bits = sldns_buffer_read_u8(pkt); |
146 | | /* 0window always first window. bitlen >=1 or parse |
147 | | error really. bit 0x2 is SOA. */ |
148 | 0 | if(win == 0 && blen >= 1 && (bits & 0x02)) { |
149 | 0 | sldns_buffer_set_position(pkt, pos); |
150 | 0 | return 1; |
151 | 0 | } |
152 | 0 | } |
153 | | |
154 | 0 | sldns_buffer_set_position(pkt, pos); |
155 | 0 | return 0; |
156 | 0 | } |
157 | | |
158 | | /** Calculate rrset flags */ |
159 | | static uint32_t |
160 | | pkt_rrset_flags(sldns_buffer* pkt, uint16_t type, sldns_pkt_section sec) |
161 | 0 | { |
162 | 0 | uint32_t f = 0; |
163 | 0 | if(type == LDNS_RR_TYPE_NSEC && nsec_at_apex(pkt)) { |
164 | 0 | f |= PACKED_RRSET_NSEC_AT_APEX; |
165 | 0 | } else if(type == LDNS_RR_TYPE_SOA && sec == LDNS_SECTION_AUTHORITY) { |
166 | 0 | f |= PACKED_RRSET_SOA_NEG; |
167 | 0 | } |
168 | 0 | return f; |
169 | 0 | } |
170 | | |
171 | | hashvalue_type |
172 | | pkt_hash_rrset(sldns_buffer* pkt, uint8_t* dname, uint16_t type, |
173 | | uint16_t dclass, uint32_t rrset_flags) |
174 | 0 | { |
175 | | /* note this MUST be identical to rrset_key_hash in packed_rrset.c */ |
176 | | /* this routine handles compressed names */ |
177 | 0 | hashvalue_type h = 0xab; |
178 | 0 | h = dname_pkt_hash(pkt, dname, h); |
179 | 0 | h = hashlittle(&type, sizeof(type), h); /* host order */ |
180 | 0 | h = hashlittle(&dclass, sizeof(dclass), h); /* netw order */ |
181 | 0 | h = hashlittle(&rrset_flags, sizeof(uint32_t), h); |
182 | 0 | return h; |
183 | 0 | } |
184 | | |
185 | | /** create partial dname hash for rrset hash */ |
186 | | static hashvalue_type |
187 | | pkt_hash_rrset_first(sldns_buffer* pkt, uint8_t* dname) |
188 | 0 | { |
189 | | /* works together with pkt_hash_rrset_rest */ |
190 | | /* note this MUST be identical to rrset_key_hash in packed_rrset.c */ |
191 | | /* this routine handles compressed names */ |
192 | 0 | hashvalue_type h = 0xab; |
193 | 0 | h = dname_pkt_hash(pkt, dname, h); |
194 | 0 | return h; |
195 | 0 | } |
196 | | |
197 | | /** create a rrset hash from a partial dname hash */ |
198 | | static hashvalue_type |
199 | | pkt_hash_rrset_rest(hashvalue_type dname_h, uint16_t type, uint16_t dclass, |
200 | | uint32_t rrset_flags) |
201 | 0 | { |
202 | | /* works together with pkt_hash_rrset_first */ |
203 | | /* note this MUST be identical to rrset_key_hash in packed_rrset.c */ |
204 | 0 | hashvalue_type h; |
205 | 0 | h = hashlittle(&type, sizeof(type), dname_h); /* host order */ |
206 | 0 | h = hashlittle(&dclass, sizeof(dclass), h); /* netw order */ |
207 | 0 | h = hashlittle(&rrset_flags, sizeof(uint32_t), h); |
208 | 0 | return h; |
209 | 0 | } |
210 | | |
211 | | /** compare rrset_parse with data */ |
212 | | static int |
213 | | rrset_parse_equals(struct rrset_parse* p, sldns_buffer* pkt, hashvalue_type h, |
214 | | uint32_t rrset_flags, uint8_t* dname, size_t dnamelen, |
215 | | uint16_t type, uint16_t dclass) |
216 | 0 | { |
217 | 0 | if(p->hash == h && p->dname_len == dnamelen && p->type == type && |
218 | 0 | p->rrset_class == dclass && p->flags == rrset_flags && |
219 | 0 | dname_pkt_compare(pkt, dname, p->dname) == 0) |
220 | 0 | return 1; |
221 | 0 | return 0; |
222 | 0 | } |
223 | | |
224 | | |
225 | | struct rrset_parse* |
226 | | msgparse_hashtable_lookup(struct msg_parse* msg, sldns_buffer* pkt, |
227 | | hashvalue_type h, uint32_t rrset_flags, uint8_t* dname, |
228 | | size_t dnamelen, uint16_t type, uint16_t dclass) |
229 | 0 | { |
230 | 0 | struct rrset_parse* p = msg->hashtable[h & (PARSE_TABLE_SIZE-1)]; |
231 | 0 | while(p) { |
232 | 0 | if(rrset_parse_equals(p, pkt, h, rrset_flags, dname, dnamelen, |
233 | 0 | type, dclass)) |
234 | 0 | return p; |
235 | 0 | p = p->rrset_bucket_next; |
236 | 0 | } |
237 | 0 | return NULL; |
238 | 0 | } |
239 | | |
240 | | /** return type networkformat that rrsig in packet covers */ |
241 | | static int |
242 | | pkt_rrsig_covered(sldns_buffer* pkt, uint8_t* here, uint16_t* type) |
243 | 0 | { |
244 | 0 | size_t pos = sldns_buffer_position(pkt); |
245 | 0 | sldns_buffer_set_position(pkt, (size_t)(here-sldns_buffer_begin(pkt))); |
246 | | /* ttl + len + size of small rrsig(rootlabel, no signature) */ |
247 | 0 | if(sldns_buffer_remaining(pkt) < 4+2+19) |
248 | 0 | return 0; |
249 | 0 | sldns_buffer_skip(pkt, 4); /* ttl */ |
250 | 0 | if(sldns_buffer_read_u16(pkt) < 19) /* too short */ { |
251 | 0 | sldns_buffer_set_position(pkt, pos); |
252 | 0 | return 0; |
253 | 0 | } |
254 | 0 | *type = sldns_buffer_read_u16(pkt); |
255 | 0 | sldns_buffer_set_position(pkt, pos); |
256 | 0 | return 1; |
257 | 0 | } |
258 | | |
259 | | /** true if covered type equals prevtype */ |
260 | | static int |
261 | | pkt_rrsig_covered_equals(sldns_buffer* pkt, uint8_t* here, uint16_t type) |
262 | 0 | { |
263 | 0 | uint16_t t; |
264 | 0 | if(pkt_rrsig_covered(pkt, here, &t) && t == type) |
265 | 0 | return 1; |
266 | 0 | return 0; |
267 | 0 | } |
268 | | |
269 | | void |
270 | | msgparse_bucket_remove(struct msg_parse* msg, struct rrset_parse* rrset) |
271 | 0 | { |
272 | 0 | struct rrset_parse** p; |
273 | 0 | p = &msg->hashtable[ rrset->hash & (PARSE_TABLE_SIZE-1) ]; |
274 | 0 | while(*p) { |
275 | 0 | if(*p == rrset) { |
276 | 0 | *p = rrset->rrset_bucket_next; |
277 | 0 | return; |
278 | 0 | } |
279 | 0 | p = &( (*p)->rrset_bucket_next ); |
280 | 0 | } |
281 | 0 | } |
282 | | |
283 | | /** change section of rrset from previous to current section */ |
284 | | static void |
285 | | change_section(struct msg_parse* msg, struct rrset_parse* rrset, |
286 | | sldns_pkt_section section) |
287 | 0 | { |
288 | 0 | struct rrset_parse *p, *prev; |
289 | | /* remove from list */ |
290 | 0 | if(section == rrset->section) |
291 | 0 | return; |
292 | 0 | p = msg->rrset_first; |
293 | 0 | prev = 0; |
294 | 0 | while(p) { |
295 | 0 | if(p == rrset) { |
296 | 0 | if(prev) prev->rrset_all_next = p->rrset_all_next; |
297 | 0 | else msg->rrset_first = p->rrset_all_next; |
298 | 0 | if(msg->rrset_last == rrset) |
299 | 0 | msg->rrset_last = prev; |
300 | 0 | break; |
301 | 0 | } |
302 | 0 | prev = p; |
303 | 0 | p = p->rrset_all_next; |
304 | 0 | } |
305 | | /* remove from count */ |
306 | 0 | switch(rrset->section) { |
307 | 0 | case LDNS_SECTION_ANSWER: msg->an_rrsets--; break; |
308 | 0 | case LDNS_SECTION_AUTHORITY: msg->ns_rrsets--; break; |
309 | 0 | case LDNS_SECTION_ADDITIONAL: msg->ar_rrsets--; break; |
310 | 0 | default: log_assert(0); |
311 | 0 | } |
312 | | /* insert at end of list */ |
313 | 0 | rrset->rrset_all_next = 0; |
314 | 0 | if(msg->rrset_last) |
315 | 0 | msg->rrset_last->rrset_all_next = rrset; |
316 | 0 | else msg->rrset_first = rrset; |
317 | 0 | msg->rrset_last = rrset; |
318 | | /* up count of new section */ |
319 | 0 | switch(section) { |
320 | 0 | case LDNS_SECTION_AUTHORITY: msg->ns_rrsets++; break; |
321 | 0 | case LDNS_SECTION_ADDITIONAL: msg->ar_rrsets++; break; |
322 | 0 | default: log_assert(0); |
323 | 0 | } |
324 | 0 | rrset->section = section; |
325 | 0 | } |
326 | | |
327 | | /** see if rrset of type RRSIG contains sig over given type */ |
328 | | static int |
329 | | rrset_has_sigover(sldns_buffer* pkt, struct rrset_parse* rrset, uint16_t type, |
330 | | int* hasother) |
331 | 0 | { |
332 | 0 | int res = 0; |
333 | 0 | struct rr_parse* rr = rrset->rr_first; |
334 | 0 | log_assert( rrset->type == LDNS_RR_TYPE_RRSIG ); |
335 | 0 | while(rr) { |
336 | 0 | if(pkt_rrsig_covered_equals(pkt, rr->ttl_data, type)) |
337 | 0 | res = 1; |
338 | 0 | else *hasother = 1; |
339 | 0 | rr = rr->next; |
340 | 0 | } |
341 | 0 | return res; |
342 | 0 | } |
343 | | |
344 | | /** move rrsigs from sigset to dataset */ |
345 | | static int |
346 | | moveover_rrsigs(sldns_buffer* pkt, struct regional* region, |
347 | | struct rrset_parse* sigset, struct rrset_parse* dataset, int duplicate) |
348 | 0 | { |
349 | 0 | struct rr_parse* sig = sigset->rr_first; |
350 | 0 | struct rr_parse* prev = NULL; |
351 | 0 | struct rr_parse* insert; |
352 | 0 | struct rr_parse* nextsig; |
353 | 0 | while(sig) { |
354 | 0 | nextsig = sig->next; |
355 | 0 | if(pkt_rrsig_covered_equals(pkt, sig->ttl_data, |
356 | 0 | dataset->type)) { |
357 | 0 | if(duplicate) { |
358 | | /* new */ |
359 | 0 | insert = (struct rr_parse*)regional_alloc( |
360 | 0 | region, sizeof(struct rr_parse)); |
361 | 0 | if(!insert) return 0; |
362 | 0 | insert->outside_packet = 0; |
363 | 0 | insert->ttl_data = sig->ttl_data; |
364 | 0 | insert->size = sig->size; |
365 | | /* prev not used */ |
366 | 0 | } else { |
367 | | /* remove from sigset */ |
368 | 0 | if(prev) prev->next = sig->next; |
369 | 0 | else sigset->rr_first = sig->next; |
370 | 0 | if(sigset->rr_last == sig) |
371 | 0 | sigset->rr_last = prev; |
372 | 0 | sigset->rr_count--; |
373 | 0 | sigset->size -= sig->size; |
374 | 0 | insert = sig; |
375 | | /* prev not changed */ |
376 | 0 | } |
377 | | /* add to dataset */ |
378 | 0 | dataset->rrsig_count++; |
379 | 0 | insert->next = 0; |
380 | 0 | if(dataset->rrsig_last) |
381 | 0 | dataset->rrsig_last->next = insert; |
382 | 0 | else dataset->rrsig_first = insert; |
383 | 0 | dataset->rrsig_last = insert; |
384 | 0 | dataset->size += insert->size; |
385 | 0 | } else { |
386 | 0 | prev = sig; |
387 | 0 | } |
388 | 0 | sig = nextsig; |
389 | 0 | } |
390 | 0 | return 1; |
391 | 0 | } |
392 | | |
393 | | /** change an rrsig rrset for use as data rrset */ |
394 | | static struct rrset_parse* |
395 | | change_rrsig_rrset(struct rrset_parse* sigset, struct msg_parse* msg, |
396 | | sldns_buffer* pkt, uint16_t datatype, uint32_t rrset_flags, |
397 | | int hasother, sldns_pkt_section section, struct regional* region) |
398 | 0 | { |
399 | 0 | struct rrset_parse* dataset = sigset; |
400 | 0 | hashvalue_type hash = pkt_hash_rrset(pkt, sigset->dname, datatype, |
401 | 0 | sigset->rrset_class, rrset_flags); |
402 | 0 | log_assert( sigset->type == LDNS_RR_TYPE_RRSIG ); |
403 | 0 | log_assert( datatype != LDNS_RR_TYPE_RRSIG ); |
404 | 0 | if(hasother) { |
405 | | /* need to make new rrset to hold data type */ |
406 | 0 | dataset = new_rrset(msg, sigset->dname, sigset->dname_len, |
407 | 0 | datatype, sigset->rrset_class, hash, rrset_flags, |
408 | 0 | section, region); |
409 | 0 | if(!dataset) |
410 | 0 | return NULL; |
411 | 0 | switch(section) { |
412 | 0 | case LDNS_SECTION_ANSWER: msg->an_rrsets++; break; |
413 | 0 | case LDNS_SECTION_AUTHORITY: msg->ns_rrsets++; break; |
414 | 0 | case LDNS_SECTION_ADDITIONAL: msg->ar_rrsets++; break; |
415 | 0 | default: log_assert(0); |
416 | 0 | } |
417 | 0 | if(!moveover_rrsigs(pkt, region, sigset, dataset, |
418 | 0 | msg->qtype == LDNS_RR_TYPE_RRSIG || |
419 | 0 | (msg->qtype == LDNS_RR_TYPE_ANY && |
420 | 0 | section != LDNS_SECTION_ANSWER) )) |
421 | 0 | return NULL; |
422 | 0 | return dataset; |
423 | 0 | } |
424 | | /* changeover the type of the rrset to data set */ |
425 | 0 | msgparse_bucket_remove(msg, dataset); |
426 | | /* insert into new hash bucket */ |
427 | 0 | dataset->rrset_bucket_next = msg->hashtable[hash&(PARSE_TABLE_SIZE-1)]; |
428 | 0 | msg->hashtable[hash&(PARSE_TABLE_SIZE-1)] = dataset; |
429 | 0 | dataset->hash = hash; |
430 | | /* use section of data item for result */ |
431 | 0 | change_section(msg, dataset, section); |
432 | 0 | dataset->type = datatype; |
433 | 0 | dataset->flags = rrset_flags; |
434 | 0 | dataset->rrsig_count += dataset->rr_count; |
435 | 0 | dataset->rr_count = 0; |
436 | | /* move sigs to end of siglist */ |
437 | 0 | if(dataset->rrsig_last) |
438 | 0 | dataset->rrsig_last->next = dataset->rr_first; |
439 | 0 | else dataset->rrsig_first = dataset->rr_first; |
440 | 0 | dataset->rrsig_last = dataset->rr_last; |
441 | 0 | dataset->rr_first = 0; |
442 | 0 | dataset->rr_last = 0; |
443 | 0 | return dataset; |
444 | 0 | } |
445 | | |
446 | | /** Find rrset. If equal to previous it is fast. hash if not so. |
447 | | * @param msg: the message with hash table. |
448 | | * @param pkt: the packet in wireformat (needed for compression ptrs). |
449 | | * @param dname: pointer to start of dname (compressed) in packet. |
450 | | * @param dnamelen: uncompressed wirefmt length of dname. |
451 | | * @param type: type of current rr. |
452 | | * @param dclass: class of current rr. |
453 | | * @param hash: hash value is returned if the rrset could not be found. |
454 | | * @param rrset_flags: is returned if the rrset could not be found. |
455 | | * @param prev_dname_first: dname of last seen RR. First seen dname. |
456 | | * @param prev_dname_last: dname of last seen RR. Last seen dname. |
457 | | * @param prev_dnamelen: dname len of last seen RR. |
458 | | * @param prev_type: type of last seen RR. |
459 | | * @param prev_dclass: class of last seen RR. |
460 | | * @param rrset_prev: last seen RRset. |
461 | | * @param section: the current section in the packet. |
462 | | * @param region: used to allocate temporary parsing data. |
463 | | * @return 0 on out of memory. |
464 | | */ |
465 | | static int |
466 | | find_rrset(struct msg_parse* msg, sldns_buffer* pkt, uint8_t* dname, |
467 | | size_t dnamelen, uint16_t type, uint16_t dclass, hashvalue_type* hash, |
468 | | uint32_t* rrset_flags, |
469 | | uint8_t** prev_dname_first, uint8_t** prev_dname_last, |
470 | | size_t* prev_dnamelen, uint16_t* prev_type, |
471 | | uint16_t* prev_dclass, struct rrset_parse** rrset_prev, |
472 | | sldns_pkt_section section, struct regional* region) |
473 | 0 | { |
474 | 0 | hashvalue_type dname_h = pkt_hash_rrset_first(pkt, dname); |
475 | 0 | uint16_t covtype; |
476 | 0 | if(*rrset_prev) { |
477 | | /* check if equal to previous item */ |
478 | 0 | if(type == *prev_type && dclass == *prev_dclass && |
479 | 0 | dnamelen == *prev_dnamelen && |
480 | 0 | smart_compare(pkt, dname, *prev_dname_first, |
481 | 0 | *prev_dname_last) == 0 && |
482 | 0 | type != LDNS_RR_TYPE_RRSIG) { |
483 | | /* same as previous */ |
484 | 0 | *prev_dname_last = dname; |
485 | 0 | return 1; |
486 | 0 | } |
487 | | /* check if rrsig over previous item */ |
488 | 0 | if(type == LDNS_RR_TYPE_RRSIG && dclass == *prev_dclass && |
489 | 0 | pkt_rrsig_covered_equals(pkt, sldns_buffer_current(pkt), |
490 | 0 | *prev_type) && |
491 | 0 | smart_compare(pkt, dname, *prev_dname_first, |
492 | 0 | *prev_dname_last) == 0) { |
493 | | /* covers previous */ |
494 | 0 | *prev_dname_last = dname; |
495 | 0 | return 1; |
496 | 0 | } |
497 | 0 | } |
498 | | /* find by hashing and lookup in hashtable */ |
499 | 0 | *rrset_flags = pkt_rrset_flags(pkt, type, section); |
500 | | |
501 | | /* if rrsig - try to lookup matching data set first */ |
502 | 0 | if(type == LDNS_RR_TYPE_RRSIG && pkt_rrsig_covered(pkt, |
503 | 0 | sldns_buffer_current(pkt), &covtype)) { |
504 | 0 | *hash = pkt_hash_rrset_rest(dname_h, covtype, dclass, |
505 | 0 | *rrset_flags); |
506 | 0 | *rrset_prev = msgparse_hashtable_lookup(msg, pkt, *hash, |
507 | 0 | *rrset_flags, dname, dnamelen, covtype, dclass); |
508 | 0 | if(!*rrset_prev && covtype == LDNS_RR_TYPE_NSEC) { |
509 | | /* if NSEC try with NSEC apex bit twiddled */ |
510 | 0 | *rrset_flags ^= PACKED_RRSET_NSEC_AT_APEX; |
511 | 0 | *hash = pkt_hash_rrset_rest(dname_h, covtype, dclass, |
512 | 0 | *rrset_flags); |
513 | 0 | *rrset_prev = msgparse_hashtable_lookup(msg, pkt, |
514 | 0 | *hash, *rrset_flags, dname, dnamelen, covtype, |
515 | 0 | dclass); |
516 | 0 | if(!*rrset_prev) /* untwiddle if not found */ |
517 | 0 | *rrset_flags ^= PACKED_RRSET_NSEC_AT_APEX; |
518 | 0 | } |
519 | 0 | if(!*rrset_prev && covtype == LDNS_RR_TYPE_SOA) { |
520 | | /* if SOA try with SOA neg flag twiddled */ |
521 | 0 | *rrset_flags ^= PACKED_RRSET_SOA_NEG; |
522 | 0 | *hash = pkt_hash_rrset_rest(dname_h, covtype, dclass, |
523 | 0 | *rrset_flags); |
524 | 0 | *rrset_prev = msgparse_hashtable_lookup(msg, pkt, |
525 | 0 | *hash, *rrset_flags, dname, dnamelen, covtype, |
526 | 0 | dclass); |
527 | 0 | if(!*rrset_prev) /* untwiddle if not found */ |
528 | 0 | *rrset_flags ^= PACKED_RRSET_SOA_NEG; |
529 | 0 | } |
530 | 0 | if(*rrset_prev) { |
531 | 0 | *prev_dname_first = (*rrset_prev)->dname; |
532 | 0 | *prev_dname_last = dname; |
533 | 0 | *prev_dnamelen = dnamelen; |
534 | 0 | *prev_type = covtype; |
535 | 0 | *prev_dclass = dclass; |
536 | 0 | return 1; |
537 | 0 | } |
538 | 0 | } |
539 | 0 | if(type != LDNS_RR_TYPE_RRSIG) { |
540 | 0 | int hasother = 0; |
541 | | /* find matching rrsig */ |
542 | 0 | *hash = pkt_hash_rrset_rest(dname_h, LDNS_RR_TYPE_RRSIG, |
543 | 0 | dclass, 0); |
544 | 0 | *rrset_prev = msgparse_hashtable_lookup(msg, pkt, *hash, |
545 | 0 | 0, dname, dnamelen, LDNS_RR_TYPE_RRSIG, |
546 | 0 | dclass); |
547 | 0 | if(*rrset_prev && rrset_has_sigover(pkt, *rrset_prev, type, |
548 | 0 | &hasother)) { |
549 | | /* yes! */ |
550 | 0 | *prev_dname_first = (*rrset_prev)->dname; |
551 | 0 | *prev_dname_last = dname; |
552 | 0 | *prev_dnamelen = dnamelen; |
553 | 0 | *prev_type = type; |
554 | 0 | *prev_dclass = dclass; |
555 | 0 | *rrset_prev = change_rrsig_rrset(*rrset_prev, msg, |
556 | 0 | pkt, type, *rrset_flags, hasother, section, |
557 | 0 | region); |
558 | 0 | if(!*rrset_prev) return 0; |
559 | 0 | return 1; |
560 | 0 | } |
561 | 0 | } |
562 | | |
563 | 0 | *hash = pkt_hash_rrset_rest(dname_h, type, dclass, *rrset_flags); |
564 | 0 | *rrset_prev = msgparse_hashtable_lookup(msg, pkt, *hash, *rrset_flags, |
565 | 0 | dname, dnamelen, type, dclass); |
566 | 0 | if(*rrset_prev) |
567 | 0 | *prev_dname_first = (*rrset_prev)->dname; |
568 | 0 | else *prev_dname_first = dname; |
569 | 0 | *prev_dname_last = dname; |
570 | 0 | *prev_dnamelen = dnamelen; |
571 | 0 | *prev_type = type; |
572 | 0 | *prev_dclass = dclass; |
573 | 0 | return 1; |
574 | 0 | } |
575 | | |
576 | | /** |
577 | | * Parse query section. |
578 | | * @param pkt: packet, position at call must be at start of query section. |
579 | | * at end position is after query section. |
580 | | * @param msg: store results here. |
581 | | * @return: 0 if OK, or rcode on error. |
582 | | */ |
583 | | static int |
584 | | parse_query_section(sldns_buffer* pkt, struct msg_parse* msg) |
585 | 0 | { |
586 | 0 | if(msg->qdcount == 0) |
587 | 0 | return 0; |
588 | 0 | if(msg->qdcount > 1) |
589 | 0 | return LDNS_RCODE_FORMERR; |
590 | 0 | log_assert(msg->qdcount == 1); |
591 | 0 | if(sldns_buffer_remaining(pkt) <= 0) |
592 | 0 | return LDNS_RCODE_FORMERR; |
593 | 0 | msg->qname = sldns_buffer_current(pkt); |
594 | 0 | if((msg->qname_len = pkt_dname_len(pkt)) == 0) |
595 | 0 | return LDNS_RCODE_FORMERR; |
596 | 0 | if(sldns_buffer_remaining(pkt) < sizeof(uint16_t)*2) |
597 | 0 | return LDNS_RCODE_FORMERR; |
598 | 0 | msg->qtype = sldns_buffer_read_u16(pkt); |
599 | 0 | msg->qclass = sldns_buffer_read_u16(pkt); |
600 | 0 | return 0; |
601 | 0 | } |
602 | | |
603 | | size_t |
604 | | get_rdf_size(sldns_rdf_type rdf) |
605 | 0 | { |
606 | 0 | switch(rdf) { |
607 | 0 | case LDNS_RDF_TYPE_CLASS: |
608 | 0 | case LDNS_RDF_TYPE_ALG: |
609 | 0 | case LDNS_RDF_TYPE_INT8: |
610 | 0 | return 1; |
611 | 0 | break; |
612 | 0 | case LDNS_RDF_TYPE_INT16: |
613 | 0 | case LDNS_RDF_TYPE_TYPE: |
614 | 0 | case LDNS_RDF_TYPE_CERT_ALG: |
615 | 0 | return 2; |
616 | 0 | break; |
617 | 0 | case LDNS_RDF_TYPE_INT32: |
618 | 0 | case LDNS_RDF_TYPE_TIME: |
619 | 0 | case LDNS_RDF_TYPE_A: |
620 | 0 | case LDNS_RDF_TYPE_PERIOD: |
621 | 0 | return 4; |
622 | 0 | break; |
623 | 0 | case LDNS_RDF_TYPE_TSIGTIME: |
624 | 0 | return 6; |
625 | 0 | break; |
626 | 0 | case LDNS_RDF_TYPE_AAAA: |
627 | 0 | return 16; |
628 | 0 | break; |
629 | 0 | default: |
630 | 0 | log_assert(0); /* add type above */ |
631 | | /* only types that appear before a domain * |
632 | | * name are needed. rest is simply copied. */ |
633 | 0 | } |
634 | 0 | return 0; |
635 | 0 | } |
636 | | |
637 | | /** calculate the size of one rr */ |
638 | | static int |
639 | | calc_size(sldns_buffer* pkt, uint16_t type, struct rr_parse* rr) |
640 | 0 | { |
641 | 0 | const sldns_rr_descriptor* desc; |
642 | 0 | uint16_t pkt_len; /* length of rr inside the packet */ |
643 | 0 | rr->size = sizeof(uint16_t); /* the rdatalen */ |
644 | 0 | sldns_buffer_skip(pkt, 4); /* skip ttl */ |
645 | 0 | pkt_len = sldns_buffer_read_u16(pkt); |
646 | 0 | if(sldns_buffer_remaining(pkt) < pkt_len) |
647 | 0 | return 0; |
648 | 0 | desc = sldns_rr_descript(type); |
649 | 0 | if(pkt_len > 0 && desc && desc->_dname_count > 0) { |
650 | 0 | int count = (int)desc->_dname_count; |
651 | 0 | int rdf = 0; |
652 | 0 | size_t len; |
653 | 0 | size_t oldpos; |
654 | | /* skip first part. */ |
655 | 0 | while(pkt_len > 0 && count) { |
656 | 0 | switch(desc->_wireformat[rdf]) { |
657 | 0 | case LDNS_RDF_TYPE_DNAME: |
658 | | /* decompress every domain name */ |
659 | 0 | oldpos = sldns_buffer_position(pkt); |
660 | 0 | if((len = pkt_dname_len(pkt)) == 0) |
661 | 0 | return 0; /* malformed dname */ |
662 | 0 | if(sldns_buffer_position(pkt)-oldpos > pkt_len) |
663 | 0 | return 0; /* dname exceeds rdata */ |
664 | 0 | pkt_len -= sldns_buffer_position(pkt)-oldpos; |
665 | 0 | rr->size += len; |
666 | 0 | count--; |
667 | 0 | len = 0; |
668 | 0 | break; |
669 | 0 | case LDNS_RDF_TYPE_STR: |
670 | 0 | if(pkt_len < 1) { |
671 | | /* NOTREACHED, due to 'while(>0)' */ |
672 | 0 | return 0; /* len byte exceeds rdata */ |
673 | 0 | } |
674 | 0 | len = sldns_buffer_current(pkt)[0] + 1; |
675 | 0 | break; |
676 | 0 | default: |
677 | 0 | len = get_rdf_size(desc->_wireformat[rdf]); |
678 | 0 | } |
679 | 0 | if(len) { |
680 | 0 | if(pkt_len < len) |
681 | 0 | return 0; /* exceeds rdata */ |
682 | 0 | pkt_len -= len; |
683 | 0 | sldns_buffer_skip(pkt, (ssize_t)len); |
684 | 0 | rr->size += len; |
685 | 0 | } |
686 | 0 | rdf++; |
687 | 0 | } |
688 | 0 | } |
689 | | /* remaining rdata */ |
690 | 0 | rr->size += pkt_len; |
691 | 0 | sldns_buffer_skip(pkt, (ssize_t)pkt_len); |
692 | 0 | return 1; |
693 | 0 | } |
694 | | |
695 | | /** skip rr ttl and rdata */ |
696 | | static int |
697 | | skip_ttl_rdata(sldns_buffer* pkt) |
698 | 0 | { |
699 | 0 | uint16_t rdatalen; |
700 | 0 | if(sldns_buffer_remaining(pkt) < 6) /* ttl + rdatalen */ |
701 | 0 | return 0; |
702 | 0 | sldns_buffer_skip(pkt, 4); /* ttl */ |
703 | 0 | rdatalen = sldns_buffer_read_u16(pkt); |
704 | 0 | if(sldns_buffer_remaining(pkt) < rdatalen) |
705 | 0 | return 0; |
706 | 0 | sldns_buffer_skip(pkt, (ssize_t)rdatalen); |
707 | 0 | return 1; |
708 | 0 | } |
709 | | |
710 | | /** see if RRSIG is a duplicate of another */ |
711 | | static int |
712 | | sig_is_double(sldns_buffer* pkt, struct rrset_parse* rrset, uint8_t* ttldata) |
713 | 0 | { |
714 | 0 | uint16_t rlen, siglen; |
715 | 0 | size_t pos = sldns_buffer_position(pkt); |
716 | 0 | struct rr_parse* sig; |
717 | 0 | if(sldns_buffer_remaining(pkt) < 6) |
718 | 0 | return 0; |
719 | 0 | sldns_buffer_skip(pkt, 4); /* ttl */ |
720 | 0 | rlen = sldns_buffer_read_u16(pkt); |
721 | 0 | if(sldns_buffer_remaining(pkt) < rlen) { |
722 | 0 | sldns_buffer_set_position(pkt, pos); |
723 | 0 | return 0; |
724 | 0 | } |
725 | 0 | sldns_buffer_set_position(pkt, pos); |
726 | |
|
727 | 0 | sig = rrset->rrsig_first; |
728 | 0 | while(sig) { |
729 | | /* check if rdatalen is same */ |
730 | 0 | memmove(&siglen, sig->ttl_data+4, sizeof(siglen)); |
731 | 0 | siglen = ntohs(siglen); |
732 | | /* checks if data in packet is exactly the same, this means |
733 | | * also dname in rdata is the same, but rrsig is not allowed |
734 | | * to have compressed dnames anyway. If it is compressed anyway |
735 | | * it will lead to duplicate rrs for qtype=RRSIG. (or ANY). |
736 | | * |
737 | | * Cannot use sig->size because size of the other one is not |
738 | | * calculated yet. |
739 | | */ |
740 | 0 | if(siglen == rlen) { |
741 | 0 | if(siglen>0 && memcmp(sig->ttl_data+6, ttldata+6, |
742 | 0 | siglen) == 0) { |
743 | | /* same! */ |
744 | 0 | return 1; |
745 | 0 | } |
746 | 0 | } |
747 | 0 | sig = sig->next; |
748 | 0 | } |
749 | 0 | return 0; |
750 | 0 | } |
751 | | |
752 | | /** Add rr (from packet here) to rrset, skips rr */ |
753 | | static int |
754 | | add_rr_to_rrset(struct rrset_parse* rrset, sldns_buffer* pkt, |
755 | | struct msg_parse* msg, struct regional* region, |
756 | | sldns_pkt_section section, uint16_t type) |
757 | 0 | { |
758 | 0 | struct rr_parse* rr; |
759 | | /* check section of rrset. */ |
760 | 0 | if(rrset->section != section && type != LDNS_RR_TYPE_RRSIG && |
761 | 0 | rrset->type != LDNS_RR_TYPE_RRSIG) { |
762 | | /* silently drop it - we drop the last part, since |
763 | | * trust in rr data depends on the section it is in. |
764 | | * the less trustworthy part is discarded. |
765 | | * also the last part is more likely to be incomplete. |
766 | | * RFC 2181: must put RRset only once in response. */ |
767 | | /* |
768 | | verbose(VERB_QUERY, "Packet contains rrset data in " |
769 | | "multiple sections, dropped last part."); |
770 | | log_buf(VERB_QUERY, "packet was", pkt); |
771 | | */ |
772 | | /* forwards */ |
773 | 0 | if(!skip_ttl_rdata(pkt)) |
774 | 0 | return LDNS_RCODE_FORMERR; |
775 | 0 | return 0; |
776 | 0 | } |
777 | | |
778 | 0 | if( (msg->qtype == LDNS_RR_TYPE_RRSIG || |
779 | 0 | msg->qtype == LDNS_RR_TYPE_ANY) |
780 | 0 | && sig_is_double(pkt, rrset, sldns_buffer_current(pkt))) { |
781 | 0 | if(!skip_ttl_rdata(pkt)) |
782 | 0 | return LDNS_RCODE_FORMERR; |
783 | 0 | return 0; |
784 | 0 | } |
785 | | |
786 | | /* create rr */ |
787 | 0 | if(!(rr = (struct rr_parse*)regional_alloc(region, sizeof(*rr)))) |
788 | 0 | return LDNS_RCODE_SERVFAIL; |
789 | 0 | rr->outside_packet = 0; |
790 | 0 | rr->ttl_data = sldns_buffer_current(pkt); |
791 | 0 | rr->next = 0; |
792 | 0 | if(type == LDNS_RR_TYPE_RRSIG && rrset->type != LDNS_RR_TYPE_RRSIG) { |
793 | 0 | if(rrset->rrsig_last) |
794 | 0 | rrset->rrsig_last->next = rr; |
795 | 0 | else rrset->rrsig_first = rr; |
796 | 0 | rrset->rrsig_last = rr; |
797 | 0 | rrset->rrsig_count++; |
798 | 0 | } else { |
799 | 0 | if(rrset->rr_last) |
800 | 0 | rrset->rr_last->next = rr; |
801 | 0 | else rrset->rr_first = rr; |
802 | 0 | rrset->rr_last = rr; |
803 | 0 | rrset->rr_count++; |
804 | 0 | } |
805 | | |
806 | | /* calc decompressed size */ |
807 | 0 | if(!calc_size(pkt, type, rr)) |
808 | 0 | return LDNS_RCODE_FORMERR; |
809 | 0 | rrset->size += rr->size; |
810 | |
|
811 | 0 | return 0; |
812 | 0 | } |
813 | | |
814 | | /** |
815 | | * Parse packet RR section, for answer, authority and additional sections. |
816 | | * @param pkt: packet, position at call must be at start of section. |
817 | | * at end position is after section. |
818 | | * @param msg: store results here. |
819 | | * @param region: how to alloc results. |
820 | | * @param section: section enum. |
821 | | * @param num_rrs: how many rrs are in the section. |
822 | | * @param num_rrsets: returns number of rrsets in the section. |
823 | | * @return: 0 if OK, or rcode on error. |
824 | | */ |
825 | | static int |
826 | | parse_section(sldns_buffer* pkt, struct msg_parse* msg, |
827 | | struct regional* region, sldns_pkt_section section, |
828 | | uint16_t num_rrs, size_t* num_rrsets) |
829 | 0 | { |
830 | 0 | uint16_t i; |
831 | 0 | uint8_t* dname, *prev_dname_f = NULL, *prev_dname_l = NULL; |
832 | 0 | size_t dnamelen, prev_dnamelen = 0; |
833 | 0 | uint16_t type, prev_type = 0; |
834 | 0 | uint16_t dclass, prev_dclass = 0; |
835 | 0 | uint32_t rrset_flags = 0; |
836 | 0 | hashvalue_type hash = 0; |
837 | 0 | struct rrset_parse* rrset = NULL; |
838 | 0 | int r; |
839 | |
|
840 | 0 | if(num_rrs == 0) |
841 | 0 | return 0; |
842 | 0 | if(sldns_buffer_remaining(pkt) <= 0) |
843 | 0 | return LDNS_RCODE_FORMERR; |
844 | 0 | for(i=0; i<num_rrs; i++) { |
845 | | /* parse this RR. */ |
846 | 0 | dname = sldns_buffer_current(pkt); |
847 | 0 | if((dnamelen = pkt_dname_len(pkt)) == 0) |
848 | 0 | return LDNS_RCODE_FORMERR; |
849 | 0 | if(sldns_buffer_remaining(pkt) < 10) /* type, class, ttl, len */ |
850 | 0 | return LDNS_RCODE_FORMERR; |
851 | 0 | type = sldns_buffer_read_u16(pkt); |
852 | 0 | sldns_buffer_read(pkt, &dclass, sizeof(dclass)); |
853 | |
|
854 | 0 | if(0) { /* debug show what is being parsed. */ |
855 | 0 | if(type == LDNS_RR_TYPE_RRSIG) { |
856 | 0 | uint16_t t; |
857 | 0 | if(pkt_rrsig_covered(pkt, |
858 | 0 | sldns_buffer_current(pkt), &t)) |
859 | 0 | fprintf(stderr, "parse of %s(%d) [%s(%d)]", |
860 | 0 | sldns_rr_descript(type)? |
861 | 0 | sldns_rr_descript(type)->_name: "??", |
862 | 0 | (int)type, |
863 | 0 | sldns_rr_descript(t)? |
864 | 0 | sldns_rr_descript(t)->_name: "??", |
865 | 0 | (int)t); |
866 | 0 | } else |
867 | 0 | fprintf(stderr, "parse of %s(%d)", |
868 | 0 | sldns_rr_descript(type)? |
869 | 0 | sldns_rr_descript(type)->_name: "??", |
870 | 0 | (int)type); |
871 | 0 | fprintf(stderr, " %s(%d) ", |
872 | 0 | sldns_lookup_by_id(sldns_rr_classes, |
873 | 0 | (int)ntohs(dclass))?sldns_lookup_by_id( |
874 | 0 | sldns_rr_classes, (int)ntohs(dclass))->name: |
875 | 0 | "??", (int)ntohs(dclass)); |
876 | 0 | dname_print(stderr, pkt, dname); |
877 | 0 | fprintf(stderr, "\n"); |
878 | 0 | } |
879 | | |
880 | | /* see if it is part of an existing RR set */ |
881 | 0 | if(!find_rrset(msg, pkt, dname, dnamelen, type, dclass, &hash, |
882 | 0 | &rrset_flags, &prev_dname_f, &prev_dname_l, |
883 | 0 | &prev_dnamelen, &prev_type, &prev_dclass, &rrset, |
884 | 0 | section, region)) |
885 | 0 | return LDNS_RCODE_SERVFAIL; |
886 | 0 | if(!rrset) { |
887 | | /* it is a new RR set. hash&flags already calculated.*/ |
888 | 0 | (*num_rrsets)++; |
889 | 0 | rrset = new_rrset(msg, dname, dnamelen, type, dclass, |
890 | 0 | hash, rrset_flags, section, region); |
891 | 0 | if(!rrset) |
892 | 0 | return LDNS_RCODE_SERVFAIL; |
893 | 0 | } |
894 | 0 | else if(0) { |
895 | 0 | fprintf(stderr, "is part of existing: "); |
896 | 0 | dname_print(stderr, pkt, rrset->dname); |
897 | 0 | fprintf(stderr, " type %s(%d)\n", |
898 | 0 | sldns_rr_descript(rrset->type)? |
899 | 0 | sldns_rr_descript(rrset->type)->_name: "??", |
900 | 0 | (int)rrset->type); |
901 | 0 | } |
902 | | /* add to rrset. */ |
903 | 0 | if((r=add_rr_to_rrset(rrset, pkt, msg, region, section, |
904 | 0 | type)) != 0) |
905 | 0 | return r; |
906 | 0 | } |
907 | 0 | return 0; |
908 | 0 | } |
909 | | |
910 | | int |
911 | | parse_packet(sldns_buffer* pkt, struct msg_parse* msg, struct regional* region) |
912 | 0 | { |
913 | 0 | int ret; |
914 | 0 | if(sldns_buffer_remaining(pkt) < LDNS_HEADER_SIZE) |
915 | 0 | return LDNS_RCODE_FORMERR; |
916 | | /* read the header */ |
917 | 0 | sldns_buffer_read(pkt, &msg->id, sizeof(uint16_t)); |
918 | 0 | msg->flags = sldns_buffer_read_u16(pkt); |
919 | 0 | msg->qdcount = sldns_buffer_read_u16(pkt); |
920 | 0 | msg->ancount = sldns_buffer_read_u16(pkt); |
921 | 0 | msg->nscount = sldns_buffer_read_u16(pkt); |
922 | 0 | msg->arcount = sldns_buffer_read_u16(pkt); |
923 | 0 | if(msg->qdcount > 1) |
924 | 0 | return LDNS_RCODE_FORMERR; |
925 | 0 | if((ret = parse_query_section(pkt, msg)) != 0) |
926 | 0 | return ret; |
927 | 0 | if((ret = parse_section(pkt, msg, region, LDNS_SECTION_ANSWER, |
928 | 0 | msg->ancount, &msg->an_rrsets)) != 0) |
929 | 0 | return ret; |
930 | 0 | if((ret = parse_section(pkt, msg, region, LDNS_SECTION_AUTHORITY, |
931 | 0 | msg->nscount, &msg->ns_rrsets)) != 0) |
932 | 0 | return ret; |
933 | 0 | if(sldns_buffer_remaining(pkt) == 0 && msg->arcount == 1) { |
934 | | /* BIND accepts leniently that an EDNS record is missing. |
935 | | * so, we do too. */ |
936 | 0 | } else if((ret = parse_section(pkt, msg, region, |
937 | 0 | LDNS_SECTION_ADDITIONAL, msg->arcount, &msg->ar_rrsets)) != 0) |
938 | 0 | return ret; |
939 | | /* if(sldns_buffer_remaining(pkt) > 0) { */ |
940 | | /* there is spurious data at end of packet. ignore */ |
941 | | /* } */ |
942 | 0 | msg->rrset_count = msg->an_rrsets + msg->ns_rrsets + msg->ar_rrsets; |
943 | 0 | return 0; |
944 | 0 | } |
945 | | |
946 | | /** parse EDNS options from EDNS wireformat rdata */ |
947 | | static int |
948 | | parse_edns_options_from_query(uint8_t* rdata_ptr, size_t rdata_len, |
949 | | struct edns_data* edns, struct config_file* cfg, struct comm_point* c, |
950 | | struct comm_reply* repinfo, uint32_t now, struct regional* region, |
951 | | struct cookie_secrets* cookie_secrets) |
952 | 0 | { |
953 | | /* To respond with a Keepalive option, the client connection must have |
954 | | * received one message with a TCP Keepalive EDNS option, and that |
955 | | * option must have 0 length data. Subsequent messages sent on that |
956 | | * connection will have a TCP Keepalive option. |
957 | | * |
958 | | * In the if-statement below, the option is added unsolicited. This |
959 | | * means that the client has sent an KEEPALIVE option earlier. We know |
960 | | * here this is true, because c->tcp_keepalive is set. |
961 | | */ |
962 | 0 | if (cfg && cfg->do_tcp_keepalive && c && c->type != comm_udp && c->tcp_keepalive) { |
963 | 0 | if(!edns_opt_list_append_keepalive(&edns->opt_list_out, |
964 | 0 | c->tcp_timeout_msec / 100, region)) { |
965 | 0 | log_err("out of memory"); |
966 | 0 | return LDNS_RCODE_SERVFAIL; |
967 | 0 | } |
968 | 0 | } |
969 | | |
970 | | /* while still more options, and have code+len to read */ |
971 | | /* ignores partial content (i.e. rdata len 3) */ |
972 | 0 | while(rdata_len >= 4) { |
973 | 0 | uint16_t opt_code = sldns_read_uint16(rdata_ptr); |
974 | 0 | uint16_t opt_len = sldns_read_uint16(rdata_ptr+2); |
975 | 0 | uint8_t server_cookie[40]; |
976 | 0 | enum edns_cookie_val_status cookie_val_status; |
977 | 0 | int cookie_is_v4 = 1; |
978 | |
|
979 | 0 | rdata_ptr += 4; |
980 | 0 | rdata_len -= 4; |
981 | 0 | if(opt_len > rdata_len) |
982 | 0 | break; /* option code partial */ |
983 | | |
984 | | /* handle parse time edns options here */ |
985 | 0 | switch(opt_code) { |
986 | 0 | case LDNS_EDNS_NSID: |
987 | 0 | if (!cfg || !cfg->nsid) |
988 | 0 | break; |
989 | 0 | if(!edns_opt_list_append(&edns->opt_list_out, |
990 | 0 | LDNS_EDNS_NSID, cfg->nsid_len, |
991 | 0 | cfg->nsid, region)) { |
992 | 0 | log_err("out of memory"); |
993 | 0 | return LDNS_RCODE_SERVFAIL; |
994 | 0 | } |
995 | 0 | break; |
996 | | |
997 | 0 | case LDNS_EDNS_KEEPALIVE: |
998 | | /* To respond with a Keepalive option, the client |
999 | | * connection must have received one message with a TCP |
1000 | | * Keepalive EDNS option, and that option must have 0 |
1001 | | * length data. Subsequent messages sent on that |
1002 | | * connection will have a TCP Keepalive option. |
1003 | | * |
1004 | | * This should be the first time the client sends this |
1005 | | * option, so c->tcp_keepalive is not set. |
1006 | | * Besides adding the reply KEEPALIVE option, |
1007 | | * c->tcp_keepalive will be set so that the |
1008 | | * option will be added unsolicited in subsequent |
1009 | | * responses (see the comment above the if-statement |
1010 | | * at the start of this function). |
1011 | | */ |
1012 | 0 | if (!cfg || !cfg->do_tcp_keepalive || !c || |
1013 | 0 | c->type == comm_udp || c->tcp_keepalive) |
1014 | 0 | break; |
1015 | 0 | if(opt_len) { |
1016 | 0 | verbose(VERB_ALGO, "query with bad edns keepalive."); |
1017 | 0 | return LDNS_RCODE_FORMERR; |
1018 | 0 | } |
1019 | 0 | if(!edns_opt_list_append_keepalive(&edns->opt_list_out, |
1020 | 0 | c->tcp_timeout_msec / 100, |
1021 | 0 | region)) { |
1022 | 0 | log_err("out of memory"); |
1023 | 0 | return LDNS_RCODE_SERVFAIL; |
1024 | 0 | } |
1025 | 0 | c->tcp_keepalive = 1; |
1026 | 0 | break; |
1027 | | |
1028 | 0 | case LDNS_EDNS_PADDING: |
1029 | 0 | if(!cfg || !cfg->pad_responses || |
1030 | 0 | !c || c->type != comm_tcp ||!c->ssl) |
1031 | 0 | break; |
1032 | 0 | if(!edns_opt_list_append(&edns->opt_list_out, |
1033 | 0 | LDNS_EDNS_PADDING, |
1034 | 0 | 0, NULL, region)) { |
1035 | 0 | log_err("out of memory"); |
1036 | 0 | return LDNS_RCODE_SERVFAIL; |
1037 | 0 | } |
1038 | 0 | edns->padding_block_size = cfg->pad_responses_block_size; |
1039 | 0 | break; |
1040 | | |
1041 | 0 | case LDNS_EDNS_COOKIE: |
1042 | 0 | if(!cfg || !cfg->do_answer_cookie || !repinfo) |
1043 | 0 | break; |
1044 | 0 | if(opt_len != 8 && (opt_len < 16 || opt_len > 40)) { |
1045 | 0 | verbose(VERB_ALGO, "worker request: " |
1046 | 0 | "badly formatted cookie"); |
1047 | 0 | return LDNS_RCODE_FORMERR; |
1048 | 0 | } |
1049 | 0 | edns->cookie_present = 1; |
1050 | | |
1051 | | /* Copy client cookie, version and timestamp for |
1052 | | * validation and creation purposes. |
1053 | | */ |
1054 | 0 | if(opt_len >= 16) { |
1055 | 0 | memmove(server_cookie, rdata_ptr, 16); |
1056 | 0 | } else { |
1057 | 0 | memset(server_cookie, 0, 16); |
1058 | 0 | memmove(server_cookie, rdata_ptr, opt_len); |
1059 | 0 | } |
1060 | | |
1061 | | /* Copy client ip for validation and creation |
1062 | | * purposes. It will be overwritten if (re)creation |
1063 | | * is needed. |
1064 | | */ |
1065 | 0 | if(repinfo->remote_addr.ss_family == AF_INET) { |
1066 | 0 | memcpy(server_cookie + 16, |
1067 | 0 | &((struct sockaddr_in*)&repinfo->remote_addr)->sin_addr, 4); |
1068 | 0 | } else { |
1069 | 0 | cookie_is_v4 = 0; |
1070 | 0 | memcpy(server_cookie + 16, |
1071 | 0 | &((struct sockaddr_in6*)&repinfo->remote_addr)->sin6_addr, 16); |
1072 | 0 | } |
1073 | |
|
1074 | 0 | if(cfg->cookie_secret_file && |
1075 | 0 | cfg->cookie_secret_file[0]) { |
1076 | | /* Loop over the active and staging cookies. */ |
1077 | 0 | cookie_val_status = |
1078 | 0 | cookie_secrets_server_validate( |
1079 | 0 | rdata_ptr, opt_len, cookie_secrets, |
1080 | 0 | cookie_is_v4, server_cookie, now); |
1081 | 0 | } else { |
1082 | | /* Use the cookie option value to validate. */ |
1083 | 0 | cookie_val_status = edns_cookie_server_validate( |
1084 | 0 | rdata_ptr, opt_len, cfg->cookie_secret, |
1085 | 0 | cfg->cookie_secret_len, cookie_is_v4, |
1086 | 0 | server_cookie, now); |
1087 | 0 | } |
1088 | 0 | if(cookie_val_status == COOKIE_STATUS_VALID_RENEW) |
1089 | 0 | edns->cookie_valid = 1; |
1090 | 0 | switch(cookie_val_status) { |
1091 | 0 | case COOKIE_STATUS_VALID: |
1092 | 0 | edns->cookie_valid = 1; |
1093 | | /* Reuse cookie */ |
1094 | 0 | if(!edns_opt_list_append( |
1095 | 0 | &edns->opt_list_out, LDNS_EDNS_COOKIE, |
1096 | 0 | opt_len, rdata_ptr, region)) { |
1097 | 0 | log_err("out of memory"); |
1098 | 0 | return LDNS_RCODE_SERVFAIL; |
1099 | 0 | } |
1100 | | /* Cookie to be reused added to outgoing |
1101 | | * options. Done! |
1102 | | */ |
1103 | 0 | break; |
1104 | 0 | case COOKIE_STATUS_CLIENT_ONLY: |
1105 | 0 | edns->cookie_client = 1; |
1106 | 0 | ATTR_FALLTHROUGH |
1107 | | /* fallthrough */ |
1108 | 0 | case COOKIE_STATUS_VALID_RENEW: |
1109 | 0 | case COOKIE_STATUS_FUTURE: |
1110 | 0 | case COOKIE_STATUS_EXPIRED: |
1111 | 0 | case COOKIE_STATUS_INVALID: |
1112 | 0 | default: |
1113 | 0 | if(cfg->cookie_secret_file && |
1114 | 0 | cfg->cookie_secret_file[0]) { |
1115 | 0 | if(!cookie_secrets) |
1116 | 0 | break; |
1117 | 0 | lock_basic_lock(&cookie_secrets->lock); |
1118 | 0 | if(cookie_secrets->cookie_count < 1) { |
1119 | 0 | lock_basic_unlock(&cookie_secrets->lock); |
1120 | 0 | break; |
1121 | 0 | } |
1122 | 0 | edns_cookie_server_write(server_cookie, |
1123 | 0 | cookie_secrets->cookie_secrets[0].cookie_secret, |
1124 | 0 | cookie_is_v4, now); |
1125 | 0 | lock_basic_unlock(&cookie_secrets->lock); |
1126 | 0 | } else { |
1127 | 0 | edns_cookie_server_write(server_cookie, |
1128 | 0 | cfg->cookie_secret, cookie_is_v4, now); |
1129 | 0 | } |
1130 | 0 | if(!edns_opt_list_append(&edns->opt_list_out, |
1131 | 0 | LDNS_EDNS_COOKIE, 24, server_cookie, |
1132 | 0 | region)) { |
1133 | 0 | log_err("out of memory"); |
1134 | 0 | return LDNS_RCODE_SERVFAIL; |
1135 | 0 | } |
1136 | 0 | break; |
1137 | 0 | } |
1138 | 0 | break; |
1139 | 0 | default: |
1140 | 0 | break; |
1141 | 0 | } |
1142 | 0 | if(!edns_opt_list_append(&edns->opt_list_in, |
1143 | 0 | opt_code, opt_len, rdata_ptr, region)) { |
1144 | 0 | log_err("out of memory"); |
1145 | 0 | return LDNS_RCODE_SERVFAIL; |
1146 | 0 | } |
1147 | 0 | rdata_ptr += opt_len; |
1148 | 0 | rdata_len -= opt_len; |
1149 | 0 | } |
1150 | 0 | return LDNS_RCODE_NOERROR; |
1151 | 0 | } |
1152 | | |
1153 | | int |
1154 | | parse_extract_edns_from_response_msg(struct msg_parse* msg, |
1155 | | struct edns_data* edns, struct regional* region) |
1156 | 0 | { |
1157 | 0 | struct rrset_parse* rrset = msg->rrset_first; |
1158 | 0 | struct rrset_parse* prev = 0; |
1159 | 0 | struct rrset_parse* found = 0; |
1160 | 0 | struct rrset_parse* found_prev = 0; |
1161 | 0 | size_t rdata_len; |
1162 | 0 | uint8_t* rdata_ptr; |
1163 | | /* since the class encodes the UDP size, we cannot use hash table to |
1164 | | * find the EDNS OPT record. Scan the packet. */ |
1165 | 0 | while(rrset) { |
1166 | 0 | if(rrset->type == LDNS_RR_TYPE_OPT) { |
1167 | | /* only one OPT RR allowed. */ |
1168 | 0 | if(found) return LDNS_RCODE_FORMERR; |
1169 | | /* found it! */ |
1170 | 0 | found_prev = prev; |
1171 | 0 | found = rrset; |
1172 | 0 | } |
1173 | 0 | prev = rrset; |
1174 | 0 | rrset = rrset->rrset_all_next; |
1175 | 0 | } |
1176 | 0 | if(!found) { |
1177 | 0 | memset(edns, 0, sizeof(*edns)); |
1178 | 0 | edns->udp_size = 512; |
1179 | 0 | return 0; |
1180 | 0 | } |
1181 | | /* check the found RRset */ |
1182 | | /* most lenient check possible. ignore dname, use last opt */ |
1183 | 0 | if(found->section != LDNS_SECTION_ADDITIONAL) |
1184 | 0 | return LDNS_RCODE_FORMERR; |
1185 | 0 | if(found->rr_count == 0) |
1186 | 0 | return LDNS_RCODE_FORMERR; |
1187 | 0 | if(0) { /* strict checking of dname and RRcount */ |
1188 | 0 | if(found->dname_len != 1 || !found->dname |
1189 | 0 | || found->dname[0] != 0) return LDNS_RCODE_FORMERR; |
1190 | 0 | if(found->rr_count != 1) return LDNS_RCODE_FORMERR; |
1191 | 0 | } |
1192 | 0 | log_assert(found->rr_first && found->rr_last); |
1193 | | |
1194 | | /* remove from packet */ |
1195 | 0 | if(found_prev) found_prev->rrset_all_next = found->rrset_all_next; |
1196 | 0 | else msg->rrset_first = found->rrset_all_next; |
1197 | 0 | if(found == msg->rrset_last) |
1198 | 0 | msg->rrset_last = found_prev; |
1199 | 0 | msg->arcount --; |
1200 | 0 | msg->ar_rrsets --; |
1201 | 0 | msg->rrset_count --; |
1202 | | |
1203 | | /* take the data ! */ |
1204 | 0 | edns->edns_present = 1; |
1205 | 0 | edns->ext_rcode = found->rr_last->ttl_data[0]; |
1206 | 0 | edns->edns_version = found->rr_last->ttl_data[1]; |
1207 | 0 | edns->bits = sldns_read_uint16(&found->rr_last->ttl_data[2]); |
1208 | 0 | edns->udp_size = ntohs(found->rrset_class); |
1209 | 0 | edns->opt_list_in = NULL; |
1210 | 0 | edns->opt_list_out = NULL; |
1211 | 0 | edns->opt_list_inplace_cb_out = NULL; |
1212 | 0 | edns->padding_block_size = 0; |
1213 | 0 | edns->cookie_present = 0; |
1214 | 0 | edns->cookie_valid = 0; |
1215 | | |
1216 | | /* take the options */ |
1217 | 0 | rdata_len = found->rr_first->size-2; |
1218 | 0 | rdata_ptr = found->rr_first->ttl_data+6; |
1219 | | |
1220 | | /* while still more options, and have code+len to read */ |
1221 | | /* ignores partial content (i.e. rdata len 3) */ |
1222 | 0 | while(rdata_len >= 4) { |
1223 | 0 | uint16_t opt_code = sldns_read_uint16(rdata_ptr); |
1224 | 0 | uint16_t opt_len = sldns_read_uint16(rdata_ptr+2); |
1225 | 0 | rdata_ptr += 4; |
1226 | 0 | rdata_len -= 4; |
1227 | 0 | if(opt_len > rdata_len) |
1228 | 0 | break; /* option code partial */ |
1229 | | |
1230 | 0 | if(!edns_opt_list_append(&edns->opt_list_in, |
1231 | 0 | opt_code, opt_len, rdata_ptr, region)) { |
1232 | 0 | log_err("out of memory"); |
1233 | 0 | break; |
1234 | 0 | } |
1235 | 0 | rdata_ptr += opt_len; |
1236 | 0 | rdata_len -= opt_len; |
1237 | 0 | } |
1238 | | /* ignore rrsigs */ |
1239 | 0 | return LDNS_RCODE_NOERROR; |
1240 | 0 | } |
1241 | | |
1242 | | /** skip RR in packet */ |
1243 | | static int |
1244 | | skip_pkt_rr(sldns_buffer* pkt) |
1245 | 0 | { |
1246 | 0 | if(sldns_buffer_remaining(pkt) < 1) return 0; |
1247 | 0 | if(!pkt_dname_len(pkt)) |
1248 | 0 | return 0; |
1249 | 0 | if(sldns_buffer_remaining(pkt) < 4) return 0; |
1250 | 0 | sldns_buffer_skip(pkt, 4); /* type and class */ |
1251 | 0 | if(!skip_ttl_rdata(pkt)) |
1252 | 0 | return 0; |
1253 | 0 | return 1; |
1254 | 0 | } |
1255 | | |
1256 | | /** skip RRs from packet */ |
1257 | | int |
1258 | | skip_pkt_rrs(sldns_buffer* pkt, int num) |
1259 | 0 | { |
1260 | 0 | int i; |
1261 | 0 | for(i=0; i<num; i++) { |
1262 | 0 | if(!skip_pkt_rr(pkt)) |
1263 | 0 | return 0; |
1264 | 0 | } |
1265 | 0 | return 1; |
1266 | 0 | } |
1267 | | |
1268 | | int |
1269 | | parse_edns_from_query_pkt(sldns_buffer* pkt, struct edns_data* edns, |
1270 | | struct config_file* cfg, struct comm_point* c, |
1271 | | struct comm_reply* repinfo, time_t now, struct regional* region, |
1272 | | struct cookie_secrets* cookie_secrets) |
1273 | 0 | { |
1274 | 0 | size_t rdata_len; |
1275 | 0 | uint8_t* rdata_ptr; |
1276 | 0 | log_assert(LDNS_QDCOUNT(sldns_buffer_begin(pkt)) == 1); |
1277 | 0 | memset(edns, 0, sizeof(*edns)); |
1278 | 0 | if(LDNS_ANCOUNT(sldns_buffer_begin(pkt)) != 0 || |
1279 | 0 | LDNS_NSCOUNT(sldns_buffer_begin(pkt)) != 0) { |
1280 | 0 | if(!skip_pkt_rrs(pkt, ((int)LDNS_ANCOUNT(sldns_buffer_begin(pkt)))+ |
1281 | 0 | ((int)LDNS_NSCOUNT(sldns_buffer_begin(pkt))))) |
1282 | 0 | return LDNS_RCODE_FORMERR; |
1283 | 0 | } |
1284 | | /* check edns section is present */ |
1285 | 0 | if(LDNS_ARCOUNT(sldns_buffer_begin(pkt)) > 1) { |
1286 | 0 | return LDNS_RCODE_FORMERR; |
1287 | 0 | } |
1288 | 0 | if(LDNS_ARCOUNT(sldns_buffer_begin(pkt)) == 0) { |
1289 | 0 | edns->udp_size = 512; |
1290 | 0 | return 0; |
1291 | 0 | } |
1292 | | /* domain name must be the root of length 1. */ |
1293 | 0 | if(pkt_dname_len(pkt) != 1) |
1294 | 0 | return LDNS_RCODE_FORMERR; |
1295 | 0 | if(sldns_buffer_remaining(pkt) < 10) /* type, class, ttl, rdatalen */ |
1296 | 0 | return LDNS_RCODE_FORMERR; |
1297 | 0 | if(sldns_buffer_read_u16(pkt) != LDNS_RR_TYPE_OPT) |
1298 | 0 | return LDNS_RCODE_FORMERR; |
1299 | 0 | edns->edns_present = 1; |
1300 | 0 | edns->udp_size = sldns_buffer_read_u16(pkt); /* class is udp size */ |
1301 | 0 | edns->ext_rcode = sldns_buffer_read_u8(pkt); /* ttl used for bits */ |
1302 | 0 | edns->edns_version = sldns_buffer_read_u8(pkt); |
1303 | 0 | edns->bits = sldns_buffer_read_u16(pkt); |
1304 | 0 | edns->opt_list_in = NULL; |
1305 | 0 | edns->opt_list_out = NULL; |
1306 | 0 | edns->opt_list_inplace_cb_out = NULL; |
1307 | 0 | edns->padding_block_size = 0; |
1308 | 0 | edns->cookie_present = 0; |
1309 | 0 | edns->cookie_valid = 0; |
1310 | | |
1311 | | /* take the options */ |
1312 | 0 | rdata_len = sldns_buffer_read_u16(pkt); |
1313 | 0 | if(sldns_buffer_remaining(pkt) < rdata_len) |
1314 | 0 | return LDNS_RCODE_FORMERR; |
1315 | 0 | rdata_ptr = sldns_buffer_current(pkt); |
1316 | | /* ignore rrsigs */ |
1317 | 0 | return parse_edns_options_from_query(rdata_ptr, rdata_len, edns, cfg, |
1318 | 0 | c, repinfo, now, region, cookie_secrets); |
1319 | 0 | } |
1320 | | |
1321 | | void |
1322 | | log_edns_opt_list(enum verbosity_value level, const char* info_str, |
1323 | | struct edns_option* list) |
1324 | 0 | { |
1325 | 0 | if(verbosity >= level && list) { |
1326 | 0 | char str[128], *s; |
1327 | 0 | size_t slen; |
1328 | 0 | verbose(level, "%s", info_str); |
1329 | 0 | while(list) { |
1330 | 0 | s = str; |
1331 | 0 | slen = sizeof(str); |
1332 | 0 | (void)sldns_wire2str_edns_option_print(&s, &slen, list->opt_code, |
1333 | 0 | list->opt_data, list->opt_len); |
1334 | 0 | verbose(level, " %s", str); |
1335 | 0 | list = list->next; |
1336 | 0 | } |
1337 | 0 | } |
1338 | 0 | } |
1339 | | |
1340 | | /** remove RR from msgparse RRset, return true if rrset is entirely bad */ |
1341 | | int |
1342 | | msgparse_rrset_remove_rr(const char* str, sldns_buffer* pkt, struct rrset_parse* rrset, |
1343 | | struct rr_parse* prev, struct rr_parse* rr, struct sockaddr_storage* addr, socklen_t addrlen) |
1344 | 0 | { |
1345 | 0 | if(verbosity >= VERB_QUERY && rrset->dname_len <= LDNS_MAX_DOMAINLEN && str) { |
1346 | 0 | uint8_t buf[LDNS_MAX_DOMAINLEN+1]; |
1347 | 0 | dname_pkt_copy(pkt, buf, rrset->dname); |
1348 | 0 | if(addr) |
1349 | 0 | log_name_addr(VERB_QUERY, str, buf, addr, addrlen); |
1350 | 0 | else log_nametypeclass(VERB_QUERY, str, buf, |
1351 | 0 | rrset->type, ntohs(rrset->rrset_class)); |
1352 | 0 | } |
1353 | 0 | if(prev) |
1354 | 0 | prev->next = rr->next; |
1355 | 0 | else rrset->rr_first = rr->next; |
1356 | 0 | if(rrset->rr_last == rr) |
1357 | 0 | rrset->rr_last = prev; |
1358 | 0 | rrset->rr_count --; |
1359 | 0 | rrset->size -= rr->size; |
1360 | | /* rr struct still exists, but is unlinked, so that in the for loop |
1361 | | * the rr->next works fine to continue. */ |
1362 | 0 | return rrset->rr_count == 0; |
1363 | 0 | } |