/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 | 1.95M | { |
61 | 1.95M | if(LABEL_IS_PTR(*dnow)) { |
62 | | /* ptr points to a previous dname */ |
63 | 43.1k | uint8_t* p; |
64 | 43.1k | if((size_t)PTR_OFFSET(dnow[0], dnow[1]) |
65 | 43.1k | >= sldns_buffer_limit(pkt)) |
66 | 0 | return -1; |
67 | 43.1k | p = sldns_buffer_at(pkt, PTR_OFFSET(dnow[0], dnow[1])); |
68 | 43.1k | if( p == dprfirst || p == dprlast ) |
69 | 2.94k | return 0; |
70 | | /* prev dname is also a ptr, both ptrs are the same. */ |
71 | 40.2k | if(LABEL_IS_PTR(*dprlast) && |
72 | 40.2k | dprlast[0] == dnow[0] && dprlast[1] == dnow[1]) |
73 | 21.6k | return 0; |
74 | 40.2k | } |
75 | 1.93M | return dname_pkt_compare(pkt, dnow, dprlast); |
76 | 1.95M | } |
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 | 168k | { |
87 | 168k | struct rrset_parse* p = regional_alloc(region, sizeof(*p)); |
88 | 168k | if(!p) return NULL; |
89 | 168k | p->rrset_bucket_next = msg->hashtable[hash & (PARSE_TABLE_SIZE-1)]; |
90 | 168k | msg->hashtable[hash & (PARSE_TABLE_SIZE-1)] = p; |
91 | 168k | p->rrset_all_next = 0; |
92 | 168k | if(msg->rrset_last) |
93 | 158k | msg->rrset_last->rrset_all_next = p; |
94 | 10.7k | else msg->rrset_first = p; |
95 | 168k | msg->rrset_last = p; |
96 | 168k | p->hash = hash; |
97 | 168k | p->section = section; |
98 | 168k | p->dname = dname; |
99 | 168k | p->dname_len = dnamelen; |
100 | 168k | p->type = type; |
101 | 168k | p->rrset_class = dclass; |
102 | 168k | p->flags = rrset_flags; |
103 | 168k | p->rr_count = 0; |
104 | 168k | p->size = 0; |
105 | 168k | p->rr_first = 0; |
106 | 168k | p->rr_last = 0; |
107 | 168k | p->rrsig_count = 0; |
108 | 168k | p->rrsig_first = 0; |
109 | 168k | p->rrsig_last = 0; |
110 | 168k | return p; |
111 | 168k | } |
112 | | |
113 | | /** See if next rrset is nsec at zone apex */ |
114 | | static int |
115 | | nsec_at_apex(sldns_buffer* pkt) |
116 | 41.3k | { |
117 | | /* we are at ttl position in packet. */ |
118 | 41.3k | size_t pos = sldns_buffer_position(pkt); |
119 | 41.3k | uint16_t rdatalen; |
120 | 41.3k | if(sldns_buffer_remaining(pkt) < 7) /* ttl+len+root */ |
121 | 185 | return 0; /* eek! */ |
122 | 41.1k | sldns_buffer_skip(pkt, 4); /* ttl */; |
123 | 41.1k | rdatalen = sldns_buffer_read_u16(pkt); |
124 | 41.1k | if(sldns_buffer_remaining(pkt) < rdatalen) { |
125 | 137 | sldns_buffer_set_position(pkt, pos); |
126 | 137 | return 0; /* parse error happens later */ |
127 | 137 | } |
128 | | /* must validate the nsec next domain name format */ |
129 | 41.0k | if(pkt_dname_len(pkt) == 0) { |
130 | 5.59k | sldns_buffer_set_position(pkt, pos); |
131 | 5.59k | return 0; /* parse error */ |
132 | 5.59k | } |
133 | | |
134 | | /* see if SOA bit is set. */ |
135 | 35.4k | if(sldns_buffer_position(pkt) < pos+4+rdatalen) { |
136 | | /* nsec type bitmap contains items */ |
137 | 8.60k | uint8_t win, blen, bits; |
138 | | /* need: windownum, bitmap len, firstbyte */ |
139 | 8.60k | if(sldns_buffer_position(pkt)+3 > pos+4+rdatalen) { |
140 | 2.07k | sldns_buffer_set_position(pkt, pos); |
141 | 2.07k | return 0; /* malformed nsec */ |
142 | 2.07k | } |
143 | 6.52k | win = sldns_buffer_read_u8(pkt); |
144 | 6.52k | blen = sldns_buffer_read_u8(pkt); |
145 | 6.52k | bits = sldns_buffer_read_u8(pkt); |
146 | | /* 0window always first window. bitlen >=1 or parse |
147 | | error really. bit 0x2 is SOA. */ |
148 | 6.52k | if(win == 0 && blen >= 1 && (bits & 0x02)) { |
149 | 2.01k | sldns_buffer_set_position(pkt, pos); |
150 | 2.01k | return 1; |
151 | 2.01k | } |
152 | 6.52k | } |
153 | | |
154 | 31.3k | sldns_buffer_set_position(pkt, pos); |
155 | 31.3k | return 0; |
156 | 35.4k | } |
157 | | |
158 | | /** Calculate rrset flags */ |
159 | | static uint32_t |
160 | | pkt_rrset_flags(sldns_buffer* pkt, uint16_t type, sldns_pkt_section sec) |
161 | 627k | { |
162 | 627k | uint32_t f = 0; |
163 | 627k | if(type == LDNS_RR_TYPE_NSEC && nsec_at_apex(pkt)) { |
164 | 2.01k | f |= PACKED_RRSET_NSEC_AT_APEX; |
165 | 625k | } else if(type == LDNS_RR_TYPE_SOA && sec == LDNS_SECTION_AUTHORITY) { |
166 | 2.23k | f |= PACKED_RRSET_SOA_NEG; |
167 | 2.23k | } |
168 | 627k | return f; |
169 | 627k | } |
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 | 25.0k | { |
175 | | /* note this MUST be identical to rrset_key_hash in packed_rrset.c */ |
176 | | /* this routine handles compressed names */ |
177 | 25.0k | hashvalue_type h = 0xab; |
178 | 25.0k | h = dname_pkt_hash(pkt, dname, h); |
179 | 25.0k | h = hashlittle(&type, sizeof(type), h); /* host order */ |
180 | 25.0k | h = hashlittle(&dclass, sizeof(dclass), h); /* netw order */ |
181 | 25.0k | h = hashlittle(&rrset_flags, sizeof(uint32_t), h); |
182 | 25.0k | return h; |
183 | 25.0k | } |
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 | 2.53M | { |
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 | 2.53M | hashvalue_type h = 0xab; |
193 | 2.53M | h = dname_pkt_hash(pkt, dname, h); |
194 | 2.53M | return h; |
195 | 2.53M | } |
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 | 1.18M | { |
202 | | /* works together with pkt_hash_rrset_first */ |
203 | | /* note this MUST be identical to rrset_key_hash in packed_rrset.c */ |
204 | 1.18M | hashvalue_type h; |
205 | 1.18M | h = hashlittle(&type, sizeof(type), dname_h); /* host order */ |
206 | 1.18M | h = hashlittle(&dclass, sizeof(dclass), h); /* netw order */ |
207 | 1.18M | h = hashlittle(&rrset_flags, sizeof(uint32_t), h); |
208 | 1.18M | return h; |
209 | 1.18M | } |
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 | 5.09M | { |
217 | 5.09M | if(p->hash == h && p->dname_len == dnamelen && p->type == type && |
218 | 5.09M | p->rrset_class == dclass && p->flags == rrset_flags && |
219 | 5.09M | dname_pkt_compare(pkt, dname, p->dname) == 0) |
220 | 582k | return 1; |
221 | 4.51M | return 0; |
222 | 5.09M | } |
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 | 1.19M | { |
230 | 1.19M | struct rrset_parse* p = msg->hashtable[h & (PARSE_TABLE_SIZE-1)]; |
231 | 5.71M | while(p) { |
232 | 5.09M | if(rrset_parse_equals(p, pkt, h, rrset_flags, dname, dnamelen, |
233 | 5.09M | type, dclass)) |
234 | 582k | return p; |
235 | 4.51M | p = p->rrset_bucket_next; |
236 | 4.51M | } |
237 | 616k | return NULL; |
238 | 1.19M | } |
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 | 5.02M | { |
244 | 5.02M | size_t pos = sldns_buffer_position(pkt); |
245 | 5.02M | sldns_buffer_set_position(pkt, (size_t)(here-sldns_buffer_begin(pkt))); |
246 | | /* ttl + len + size of small rrsig(rootlabel, no signature) */ |
247 | 5.02M | if(sldns_buffer_remaining(pkt) < 4+2+19) |
248 | 4.53k | return 0; |
249 | 5.01M | sldns_buffer_skip(pkt, 4); /* ttl */ |
250 | 5.01M | if(sldns_buffer_read_u16(pkt) < 19) /* too short */ { |
251 | 3.31M | sldns_buffer_set_position(pkt, pos); |
252 | 3.31M | return 0; |
253 | 3.31M | } |
254 | 1.70M | *type = sldns_buffer_read_u16(pkt); |
255 | 1.70M | sldns_buffer_set_position(pkt, pos); |
256 | 1.70M | return 1; |
257 | 5.01M | } |
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 | 4.92M | { |
263 | 4.92M | uint16_t t; |
264 | 4.92M | if(pkt_rrsig_covered(pkt, here, &t) && t == type) |
265 | 618k | return 1; |
266 | 4.30M | return 0; |
267 | 4.92M | } |
268 | | |
269 | | void |
270 | | msgparse_bucket_remove(struct msg_parse* msg, struct rrset_parse* rrset) |
271 | 72.0k | { |
272 | 72.0k | struct rrset_parse** p; |
273 | 72.0k | p = &msg->hashtable[ rrset->hash & (PARSE_TABLE_SIZE-1) ]; |
274 | 365k | while(*p) { |
275 | 365k | if(*p == rrset) { |
276 | 71.1k | *p = rrset->rrset_bucket_next; |
277 | 71.1k | return; |
278 | 71.1k | } |
279 | 293k | p = &( (*p)->rrset_bucket_next ); |
280 | 293k | } |
281 | 72.0k | } |
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 | 1.41k | { |
288 | 1.41k | struct rrset_parse *p, *prev; |
289 | | /* remove from list */ |
290 | 1.41k | if(section == rrset->section) |
291 | 1.14k | return; |
292 | 273 | p = msg->rrset_first; |
293 | 273 | prev = 0; |
294 | 1.19k | while(p) { |
295 | 1.19k | if(p == rrset) { |
296 | 273 | if(prev) prev->rrset_all_next = p->rrset_all_next; |
297 | 124 | else msg->rrset_first = p->rrset_all_next; |
298 | 273 | if(msg->rrset_last == rrset) |
299 | 63 | msg->rrset_last = prev; |
300 | 273 | break; |
301 | 273 | } |
302 | 924 | prev = p; |
303 | 924 | p = p->rrset_all_next; |
304 | 924 | } |
305 | | /* remove from count */ |
306 | 273 | switch(rrset->section) { |
307 | 160 | case LDNS_SECTION_ANSWER: msg->an_rrsets--; break; |
308 | 113 | 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 | 273 | } |
312 | | /* insert at end of list */ |
313 | 273 | rrset->rrset_all_next = 0; |
314 | 273 | if(msg->rrset_last) |
315 | 233 | msg->rrset_last->rrset_all_next = rrset; |
316 | 40 | else msg->rrset_first = rrset; |
317 | 273 | msg->rrset_last = rrset; |
318 | | /* up count of new section */ |
319 | 273 | switch(section) { |
320 | 133 | case LDNS_SECTION_AUTHORITY: msg->ns_rrsets++; break; |
321 | 140 | case LDNS_SECTION_ADDITIONAL: msg->ar_rrsets++; break; |
322 | 0 | default: log_assert(0); |
323 | 273 | } |
324 | 273 | rrset->section = section; |
325 | 273 | } |
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 | 122k | { |
332 | 122k | int res = 0; |
333 | 122k | struct rr_parse* rr = rrset->rr_first; |
334 | 122k | log_assert( rrset->type == LDNS_RR_TYPE_RRSIG ); |
335 | 4.46M | while(rr) { |
336 | 4.33M | if(pkt_rrsig_covered_equals(pkt, rr->ttl_data, type)) |
337 | 292k | res = 1; |
338 | 4.04M | else *hasother = 1; |
339 | 4.33M | rr = rr->next; |
340 | 4.33M | } |
341 | 122k | return res; |
342 | 122k | } |
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 | 12.8k | { |
349 | 12.8k | struct rr_parse* sig = sigset->rr_first; |
350 | 12.8k | struct rr_parse* prev = NULL; |
351 | 12.8k | struct rr_parse* insert; |
352 | 12.8k | struct rr_parse* nextsig; |
353 | 504k | while(sig) { |
354 | 491k | nextsig = sig->next; |
355 | 491k | if(pkt_rrsig_covered_equals(pkt, sig->ttl_data, |
356 | 491k | dataset->type)) { |
357 | 289k | if(duplicate) { |
358 | | /* new */ |
359 | 285k | insert = (struct rr_parse*)regional_alloc( |
360 | 285k | region, sizeof(struct rr_parse)); |
361 | 285k | if(!insert) return 0; |
362 | 285k | insert->outside_packet = 0; |
363 | 285k | insert->ttl_data = sig->ttl_data; |
364 | 285k | insert->size = sig->size; |
365 | | /* prev not used */ |
366 | 285k | } else { |
367 | | /* remove from sigset */ |
368 | 3.99k | if(prev) prev->next = sig->next; |
369 | 1.18k | else sigset->rr_first = sig->next; |
370 | 3.99k | if(sigset->rr_last == sig) |
371 | 426 | sigset->rr_last = prev; |
372 | 3.99k | sigset->rr_count--; |
373 | 3.99k | sigset->size -= sig->size; |
374 | 3.99k | insert = sig; |
375 | | /* prev not changed */ |
376 | 3.99k | } |
377 | | /* add to dataset */ |
378 | 289k | dataset->rrsig_count++; |
379 | 289k | insert->next = 0; |
380 | 289k | if(dataset->rrsig_last) |
381 | 277k | dataset->rrsig_last->next = insert; |
382 | 12.8k | else dataset->rrsig_first = insert; |
383 | 289k | dataset->rrsig_last = insert; |
384 | 289k | dataset->size += insert->size; |
385 | 289k | } else { |
386 | 201k | prev = sig; |
387 | 201k | } |
388 | 491k | sig = nextsig; |
389 | 491k | } |
390 | 12.8k | return 1; |
391 | 12.8k | } |
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 | 14.3k | { |
399 | 14.3k | struct rrset_parse* dataset = sigset; |
400 | 14.3k | hashvalue_type hash = pkt_hash_rrset(pkt, sigset->dname, datatype, |
401 | 14.3k | sigset->rrset_class, rrset_flags); |
402 | 14.3k | log_assert( sigset->type == LDNS_RR_TYPE_RRSIG ); |
403 | 14.3k | log_assert( datatype != LDNS_RR_TYPE_RRSIG ); |
404 | 14.3k | if(hasother) { |
405 | | /* need to make new rrset to hold data type */ |
406 | 12.8k | dataset = new_rrset(msg, sigset->dname, sigset->dname_len, |
407 | 12.8k | datatype, sigset->rrset_class, hash, rrset_flags, |
408 | 12.8k | section, region); |
409 | 12.8k | if(!dataset) |
410 | 0 | return NULL; |
411 | 12.8k | switch(section) { |
412 | 6.32k | case LDNS_SECTION_ANSWER: msg->an_rrsets++; break; |
413 | 4.44k | case LDNS_SECTION_AUTHORITY: msg->ns_rrsets++; break; |
414 | 2.10k | case LDNS_SECTION_ADDITIONAL: msg->ar_rrsets++; break; |
415 | 0 | default: log_assert(0); |
416 | 12.8k | } |
417 | 12.8k | if(!moveover_rrsigs(pkt, region, sigset, dataset, |
418 | 12.8k | msg->qtype == LDNS_RR_TYPE_RRSIG || |
419 | 12.8k | (msg->qtype == LDNS_RR_TYPE_ANY && |
420 | 4.37k | section != LDNS_SECTION_ANSWER) )) |
421 | 0 | return NULL; |
422 | 12.8k | return dataset; |
423 | 12.8k | } |
424 | | /* changeover the type of the rrset to data set */ |
425 | 1.41k | msgparse_bucket_remove(msg, dataset); |
426 | | /* insert into new hash bucket */ |
427 | 1.41k | dataset->rrset_bucket_next = msg->hashtable[hash&(PARSE_TABLE_SIZE-1)]; |
428 | 1.41k | msg->hashtable[hash&(PARSE_TABLE_SIZE-1)] = dataset; |
429 | 1.41k | dataset->hash = hash; |
430 | | /* use section of data item for result */ |
431 | 1.41k | change_section(msg, dataset, section); |
432 | 1.41k | dataset->type = datatype; |
433 | 1.41k | dataset->flags = rrset_flags; |
434 | 1.41k | dataset->rrsig_count += dataset->rr_count; |
435 | 1.41k | dataset->rr_count = 0; |
436 | | /* move sigs to end of siglist */ |
437 | 1.41k | if(dataset->rrsig_last) |
438 | 0 | dataset->rrsig_last->next = dataset->rr_first; |
439 | 1.41k | else dataset->rrsig_first = dataset->rr_first; |
440 | 1.41k | dataset->rrsig_last = dataset->rr_last; |
441 | 1.41k | dataset->rr_first = 0; |
442 | 1.41k | dataset->rr_last = 0; |
443 | 1.41k | return dataset; |
444 | 14.3k | } |
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 | 2.53M | { |
474 | 2.53M | hashvalue_type dname_h = pkt_hash_rrset_first(pkt, dname); |
475 | 2.53M | uint16_t covtype; |
476 | 2.53M | if(*rrset_prev) { |
477 | | /* check if equal to previous item */ |
478 | 2.52M | if(type == *prev_type && dclass == *prev_dclass && |
479 | 2.52M | dnamelen == *prev_dnamelen && |
480 | 2.52M | smart_compare(pkt, dname, *prev_dname_first, |
481 | 1.91M | *prev_dname_last) == 0 && |
482 | 2.52M | type != LDNS_RR_TYPE_RRSIG) { |
483 | | /* same as previous */ |
484 | 1.88M | *prev_dname_last = dname; |
485 | 1.88M | return 1; |
486 | 1.88M | } |
487 | | /* check if rrsig over previous item */ |
488 | 644k | if(type == LDNS_RR_TYPE_RRSIG && dclass == *prev_dclass && |
489 | 644k | pkt_rrsig_covered_equals(pkt, sldns_buffer_current(pkt), |
490 | 97.5k | *prev_type) && |
491 | 644k | smart_compare(pkt, dname, *prev_dname_first, |
492 | 36.3k | *prev_dname_last) == 0) { |
493 | | /* covers previous */ |
494 | 30.1k | *prev_dname_last = dname; |
495 | 30.1k | return 1; |
496 | 30.1k | } |
497 | 644k | } |
498 | | /* find by hashing and lookup in hashtable */ |
499 | 627k | *rrset_flags = pkt_rrset_flags(pkt, type, section); |
500 | | |
501 | | /* if rrsig - try to lookup matching data set first */ |
502 | 627k | if(type == LDNS_RR_TYPE_RRSIG && pkt_rrsig_covered(pkt, |
503 | 94.3k | sldns_buffer_current(pkt), &covtype)) { |
504 | 55.7k | *hash = pkt_hash_rrset_rest(dname_h, covtype, dclass, |
505 | 55.7k | *rrset_flags); |
506 | 55.7k | *rrset_prev = msgparse_hashtable_lookup(msg, pkt, *hash, |
507 | 55.7k | *rrset_flags, dname, dnamelen, covtype, dclass); |
508 | 55.7k | if(!*rrset_prev && covtype == LDNS_RR_TYPE_NSEC) { |
509 | | /* if NSEC try with NSEC apex bit twiddled */ |
510 | 7.06k | *rrset_flags ^= PACKED_RRSET_NSEC_AT_APEX; |
511 | 7.06k | *hash = pkt_hash_rrset_rest(dname_h, covtype, dclass, |
512 | 7.06k | *rrset_flags); |
513 | 7.06k | *rrset_prev = msgparse_hashtable_lookup(msg, pkt, |
514 | 7.06k | *hash, *rrset_flags, dname, dnamelen, covtype, |
515 | 7.06k | dclass); |
516 | 7.06k | if(!*rrset_prev) /* untwiddle if not found */ |
517 | 5.70k | *rrset_flags ^= PACKED_RRSET_NSEC_AT_APEX; |
518 | 7.06k | } |
519 | 55.7k | if(!*rrset_prev && covtype == LDNS_RR_TYPE_SOA) { |
520 | | /* if SOA try with SOA neg flag twiddled */ |
521 | 5.88k | *rrset_flags ^= PACKED_RRSET_SOA_NEG; |
522 | 5.88k | *hash = pkt_hash_rrset_rest(dname_h, covtype, dclass, |
523 | 5.88k | *rrset_flags); |
524 | 5.88k | *rrset_prev = msgparse_hashtable_lookup(msg, pkt, |
525 | 5.88k | *hash, *rrset_flags, dname, dnamelen, covtype, |
526 | 5.88k | dclass); |
527 | 5.88k | if(!*rrset_prev) /* untwiddle if not found */ |
528 | 4.87k | *rrset_flags ^= PACKED_RRSET_SOA_NEG; |
529 | 5.88k | } |
530 | 55.7k | if(*rrset_prev) { |
531 | 26.7k | *prev_dname_first = (*rrset_prev)->dname; |
532 | 26.7k | *prev_dname_last = dname; |
533 | 26.7k | *prev_dnamelen = dnamelen; |
534 | 26.7k | *prev_type = covtype; |
535 | 26.7k | *prev_dclass = dclass; |
536 | 26.7k | return 1; |
537 | 26.7k | } |
538 | 55.7k | } |
539 | 601k | if(type != LDNS_RR_TYPE_RRSIG) { |
540 | 533k | int hasother = 0; |
541 | | /* find matching rrsig */ |
542 | 533k | *hash = pkt_hash_rrset_rest(dname_h, LDNS_RR_TYPE_RRSIG, |
543 | 533k | dclass, 0); |
544 | 533k | *rrset_prev = msgparse_hashtable_lookup(msg, pkt, *hash, |
545 | 533k | 0, dname, dnamelen, LDNS_RR_TYPE_RRSIG, |
546 | 533k | dclass); |
547 | 533k | if(*rrset_prev && rrset_has_sigover(pkt, *rrset_prev, type, |
548 | 122k | &hasother)) { |
549 | | /* yes! */ |
550 | 14.3k | *prev_dname_first = (*rrset_prev)->dname; |
551 | 14.3k | *prev_dname_last = dname; |
552 | 14.3k | *prev_dnamelen = dnamelen; |
553 | 14.3k | *prev_type = type; |
554 | 14.3k | *prev_dclass = dclass; |
555 | 14.3k | *rrset_prev = change_rrsig_rrset(*rrset_prev, msg, |
556 | 14.3k | pkt, type, *rrset_flags, hasother, section, |
557 | 14.3k | region); |
558 | 14.3k | if(!*rrset_prev) return 0; |
559 | 14.3k | return 1; |
560 | 14.3k | } |
561 | 533k | } |
562 | | |
563 | 586k | *hash = pkt_hash_rrset_rest(dname_h, type, dclass, *rrset_flags); |
564 | 586k | *rrset_prev = msgparse_hashtable_lookup(msg, pkt, *hash, *rrset_flags, |
565 | 586k | dname, dnamelen, type, dclass); |
566 | 586k | if(*rrset_prev) |
567 | 430k | *prev_dname_first = (*rrset_prev)->dname; |
568 | 155k | else *prev_dname_first = dname; |
569 | 586k | *prev_dname_last = dname; |
570 | 586k | *prev_dnamelen = dnamelen; |
571 | 586k | *prev_type = type; |
572 | 586k | *prev_dclass = dclass; |
573 | 586k | return 1; |
574 | 601k | } |
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 | 11.3k | { |
586 | 11.3k | if(msg->qdcount == 0) |
587 | 6.91k | return 0; |
588 | 4.42k | if(msg->qdcount > 1) |
589 | 0 | return LDNS_RCODE_FORMERR; |
590 | 4.42k | log_assert(msg->qdcount == 1); |
591 | 4.42k | if(sldns_buffer_remaining(pkt) <= 0) |
592 | 3 | return LDNS_RCODE_FORMERR; |
593 | 4.42k | msg->qname = sldns_buffer_current(pkt); |
594 | 4.42k | if((msg->qname_len = pkt_dname_len(pkt)) == 0) |
595 | 61 | return LDNS_RCODE_FORMERR; |
596 | 4.36k | if(sldns_buffer_remaining(pkt) < sizeof(uint16_t)*2) |
597 | 46 | return LDNS_RCODE_FORMERR; |
598 | 4.31k | msg->qtype = sldns_buffer_read_u16(pkt); |
599 | 4.31k | msg->qclass = sldns_buffer_read_u16(pkt); |
600 | 4.31k | return 0; |
601 | 4.36k | } |
602 | | |
603 | | size_t |
604 | | get_rdf_size(sldns_rdf_type rdf) |
605 | 737k | { |
606 | 737k | switch(rdf) { |
607 | 0 | case LDNS_RDF_TYPE_CLASS: |
608 | 105k | case LDNS_RDF_TYPE_ALG: |
609 | 211k | case LDNS_RDF_TYPE_INT8: |
610 | 211k | return 1; |
611 | 0 | break; |
612 | 107k | case LDNS_RDF_TYPE_INT16: |
613 | 213k | case LDNS_RDF_TYPE_TYPE: |
614 | 213k | case LDNS_RDF_TYPE_CERT_ALG: |
615 | 213k | return 2; |
616 | 0 | break; |
617 | 104k | case LDNS_RDF_TYPE_INT32: |
618 | 312k | case LDNS_RDF_TYPE_TIME: |
619 | 312k | case LDNS_RDF_TYPE_A: |
620 | 312k | case LDNS_RDF_TYPE_PERIOD: |
621 | 312k | 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 | 737k | } |
634 | 0 | return 0; |
635 | 737k | } |
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 | 2.00M | { |
641 | 2.00M | const sldns_rr_descriptor* desc; |
642 | 2.00M | uint16_t pkt_len; /* length of rr inside the packet */ |
643 | 2.00M | rr->size = sizeof(uint16_t); /* the rdatalen */ |
644 | 2.00M | sldns_buffer_skip(pkt, 4); /* skip ttl */ |
645 | 2.00M | pkt_len = sldns_buffer_read_u16(pkt); |
646 | 2.00M | if(sldns_buffer_remaining(pkt) < pkt_len) |
647 | 2.93k | return 0; |
648 | 2.00M | desc = sldns_rr_descript(type); |
649 | 2.00M | if(pkt_len > 0 && desc && desc->_dname_count > 0) { |
650 | 146k | int count = (int)desc->_dname_count; |
651 | 146k | int rdf = 0; |
652 | 146k | size_t len; |
653 | 146k | size_t oldpos; |
654 | | /* skip first part. */ |
655 | 828k | while(pkt_len > 0 && count) { |
656 | 682k | switch(desc->_wireformat[rdf]) { |
657 | 143k | case LDNS_RDF_TYPE_DNAME: |
658 | | /* decompress every domain name */ |
659 | 143k | oldpos = sldns_buffer_position(pkt); |
660 | 143k | if((len = pkt_dname_len(pkt)) == 0) |
661 | 538 | return 0; /* malformed dname */ |
662 | 143k | if(sldns_buffer_position(pkt)-oldpos > pkt_len) |
663 | 85 | return 0; /* dname exceeds rdata */ |
664 | 142k | pkt_len -= sldns_buffer_position(pkt)-oldpos; |
665 | 142k | rr->size += len; |
666 | 142k | count--; |
667 | 142k | len = 0; |
668 | 142k | break; |
669 | 2.78k | case LDNS_RDF_TYPE_STR: |
670 | 2.78k | if(pkt_len < 1) { |
671 | | /* NOTREACHED, due to 'while(>0)' */ |
672 | 0 | return 0; /* len byte exceeds rdata */ |
673 | 0 | } |
674 | 2.78k | len = sldns_buffer_current(pkt)[0] + 1; |
675 | 2.78k | break; |
676 | 536k | default: |
677 | 536k | len = get_rdf_size(desc->_wireformat[rdf]); |
678 | 682k | } |
679 | 682k | if(len) { |
680 | 539k | if(pkt_len < len) |
681 | 123 | return 0; /* exceeds rdata */ |
682 | 538k | pkt_len -= len; |
683 | 538k | sldns_buffer_skip(pkt, (ssize_t)len); |
684 | 538k | rr->size += len; |
685 | 538k | } |
686 | 681k | rdf++; |
687 | 681k | } |
688 | 146k | } |
689 | | /* remaining rdata */ |
690 | 2.00M | rr->size += pkt_len; |
691 | 2.00M | sldns_buffer_skip(pkt, (ssize_t)pkt_len); |
692 | 2.00M | return 1; |
693 | 2.00M | } |
694 | | |
695 | | /** skip rr ttl and rdata */ |
696 | | static int |
697 | | skip_ttl_rdata(sldns_buffer* pkt) |
698 | 532k | { |
699 | 532k | uint16_t rdatalen; |
700 | 532k | if(sldns_buffer_remaining(pkt) < 6) /* ttl + rdatalen */ |
701 | 0 | return 0; |
702 | 532k | sldns_buffer_skip(pkt, 4); /* ttl */ |
703 | 532k | rdatalen = sldns_buffer_read_u16(pkt); |
704 | 532k | if(sldns_buffer_remaining(pkt) < rdatalen) |
705 | 356 | return 0; |
706 | 532k | sldns_buffer_skip(pkt, (ssize_t)rdatalen); |
707 | 532k | return 1; |
708 | 532k | } |
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 | 764k | { |
714 | 764k | uint16_t rlen, siglen; |
715 | 764k | size_t pos = sldns_buffer_position(pkt); |
716 | 764k | struct rr_parse* sig; |
717 | 764k | if(sldns_buffer_remaining(pkt) < 6) |
718 | 0 | return 0; |
719 | 764k | sldns_buffer_skip(pkt, 4); /* ttl */ |
720 | 764k | rlen = sldns_buffer_read_u16(pkt); |
721 | 764k | if(sldns_buffer_remaining(pkt) < rlen) { |
722 | 620 | sldns_buffer_set_position(pkt, pos); |
723 | 620 | return 0; |
724 | 620 | } |
725 | 764k | sldns_buffer_set_position(pkt, pos); |
726 | | |
727 | 764k | sig = rrset->rrsig_first; |
728 | 11.8M | while(sig) { |
729 | | /* check if rdatalen is same */ |
730 | 11.1M | memmove(&siglen, sig->ttl_data+4, sizeof(siglen)); |
731 | 11.1M | 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 | 11.1M | if(siglen == rlen) { |
741 | 99.1k | if(siglen>0 && memcmp(sig->ttl_data+6, ttldata+6, |
742 | 99.1k | siglen) == 0) { |
743 | | /* same! */ |
744 | 11.4k | return 1; |
745 | 11.4k | } |
746 | 99.1k | } |
747 | 11.1M | sig = sig->next; |
748 | 11.1M | } |
749 | 752k | return 0; |
750 | 764k | } |
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 | 2.53M | { |
758 | 2.53M | struct rr_parse* rr; |
759 | | /* check section of rrset. */ |
760 | 2.53M | if(rrset->section != section && type != LDNS_RR_TYPE_RRSIG && |
761 | 2.53M | 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 | 521k | if(!skip_ttl_rdata(pkt)) |
774 | 356 | return LDNS_RCODE_FORMERR; |
775 | 520k | return 0; |
776 | 521k | } |
777 | | |
778 | 2.01M | if( (msg->qtype == LDNS_RR_TYPE_RRSIG || |
779 | 2.01M | msg->qtype == LDNS_RR_TYPE_ANY) |
780 | 2.01M | && sig_is_double(pkt, rrset, sldns_buffer_current(pkt))) { |
781 | 11.4k | if(!skip_ttl_rdata(pkt)) |
782 | 0 | return LDNS_RCODE_FORMERR; |
783 | 11.4k | return 0; |
784 | 11.4k | } |
785 | | |
786 | | /* create rr */ |
787 | 2.00M | if(!(rr = (struct rr_parse*)regional_alloc(region, sizeof(*rr)))) |
788 | 0 | return LDNS_RCODE_SERVFAIL; |
789 | 2.00M | rr->outside_packet = 0; |
790 | 2.00M | rr->ttl_data = sldns_buffer_current(pkt); |
791 | 2.00M | rr->next = 0; |
792 | 2.00M | if(type == LDNS_RR_TYPE_RRSIG && rrset->type != LDNS_RR_TYPE_RRSIG) { |
793 | 41.1k | if(rrset->rrsig_last) |
794 | 40.0k | rrset->rrsig_last->next = rr; |
795 | 1.10k | else rrset->rrsig_first = rr; |
796 | 41.1k | rrset->rrsig_last = rr; |
797 | 41.1k | rrset->rrsig_count++; |
798 | 1.96M | } else { |
799 | 1.96M | if(rrset->rr_last) |
800 | 1.79M | rrset->rr_last->next = rr; |
801 | 170k | else rrset->rr_first = rr; |
802 | 1.96M | rrset->rr_last = rr; |
803 | 1.96M | rrset->rr_count++; |
804 | 1.96M | } |
805 | | |
806 | | /* calc decompressed size */ |
807 | 2.00M | if(!calc_size(pkt, type, rr)) |
808 | 3.67k | return LDNS_RCODE_FORMERR; |
809 | 2.00M | rrset->size += rr->size; |
810 | | |
811 | 2.00M | return 0; |
812 | 2.00M | } |
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 | 22.7k | { |
830 | 22.7k | uint16_t i; |
831 | 22.7k | uint8_t* dname, *prev_dname_f = NULL, *prev_dname_l = NULL; |
832 | 22.7k | size_t dnamelen, prev_dnamelen = 0; |
833 | 22.7k | uint16_t type, prev_type = 0; |
834 | 22.7k | uint16_t dclass, prev_dclass = 0; |
835 | 22.7k | uint32_t rrset_flags = 0; |
836 | 22.7k | hashvalue_type hash = 0; |
837 | 22.7k | struct rrset_parse* rrset = NULL; |
838 | 22.7k | int r; |
839 | | |
840 | 22.7k | if(num_rrs == 0) |
841 | 8.57k | return 0; |
842 | 14.1k | if(sldns_buffer_remaining(pkt) <= 0) |
843 | 135 | return LDNS_RCODE_FORMERR; |
844 | 2.54M | for(i=0; i<num_rrs; i++) { |
845 | | /* parse this RR. */ |
846 | 2.54M | dname = sldns_buffer_current(pkt); |
847 | 2.54M | if((dnamelen = pkt_dname_len(pkt)) == 0) |
848 | 2.76k | return LDNS_RCODE_FORMERR; |
849 | 2.53M | if(sldns_buffer_remaining(pkt) < 10) /* type, class, ttl, len */ |
850 | 745 | return LDNS_RCODE_FORMERR; |
851 | 2.53M | type = sldns_buffer_read_u16(pkt); |
852 | 2.53M | sldns_buffer_read(pkt, &dclass, sizeof(dclass)); |
853 | | |
854 | 2.53M | 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 | 2.53M | if(!find_rrset(msg, pkt, dname, dnamelen, type, dclass, &hash, |
882 | 2.53M | &rrset_flags, &prev_dname_f, &prev_dname_l, |
883 | 2.53M | &prev_dnamelen, &prev_type, &prev_dclass, &rrset, |
884 | 2.53M | section, region)) |
885 | 0 | return LDNS_RCODE_SERVFAIL; |
886 | 2.53M | if(!rrset) { |
887 | | /* it is a new RR set. hash&flags already calculated.*/ |
888 | 155k | (*num_rrsets)++; |
889 | 155k | rrset = new_rrset(msg, dname, dnamelen, type, dclass, |
890 | 155k | hash, rrset_flags, section, region); |
891 | 155k | if(!rrset) |
892 | 0 | return LDNS_RCODE_SERVFAIL; |
893 | 155k | } |
894 | 2.38M | 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 | 2.53M | if((r=add_rr_to_rrset(rrset, pkt, msg, region, section, |
904 | 2.53M | type)) != 0) |
905 | 4.03k | return r; |
906 | 2.53M | } |
907 | 6.46k | return 0; |
908 | 14.0k | } |
909 | | |
910 | | int |
911 | | parse_packet(sldns_buffer* pkt, struct msg_parse* msg, struct regional* region) |
912 | 11.4k | { |
913 | 11.4k | int ret; |
914 | 11.4k | if(sldns_buffer_remaining(pkt) < LDNS_HEADER_SIZE) |
915 | 30 | return LDNS_RCODE_FORMERR; |
916 | | /* read the header */ |
917 | 11.3k | sldns_buffer_read(pkt, &msg->id, sizeof(uint16_t)); |
918 | 11.3k | msg->flags = sldns_buffer_read_u16(pkt); |
919 | 11.3k | msg->qdcount = sldns_buffer_read_u16(pkt); |
920 | 11.3k | msg->ancount = sldns_buffer_read_u16(pkt); |
921 | 11.3k | msg->nscount = sldns_buffer_read_u16(pkt); |
922 | 11.3k | msg->arcount = sldns_buffer_read_u16(pkt); |
923 | 11.3k | if(msg->qdcount > 1) |
924 | 49 | return LDNS_RCODE_FORMERR; |
925 | 11.3k | if((ret = parse_query_section(pkt, msg)) != 0) |
926 | 110 | return ret; |
927 | 11.2k | if((ret = parse_section(pkt, msg, region, LDNS_SECTION_ANSWER, |
928 | 11.2k | msg->ancount, &msg->an_rrsets)) != 0) |
929 | 4.17k | return ret; |
930 | 7.05k | if((ret = parse_section(pkt, msg, region, LDNS_SECTION_AUTHORITY, |
931 | 7.05k | msg->nscount, &msg->ns_rrsets)) != 0) |
932 | 2.05k | return ret; |
933 | 5.00k | 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 | 4.42k | } else if((ret = parse_section(pkt, msg, region, |
937 | 4.42k | LDNS_SECTION_ADDITIONAL, msg->arcount, &msg->ar_rrsets)) != 0) |
938 | 1.45k | return ret; |
939 | | /* if(sldns_buffer_remaining(pkt) > 0) { */ |
940 | | /* there is spurious data at end of packet. ignore */ |
941 | | /* } */ |
942 | 3.55k | msg->rrset_count = msg->an_rrsets + msg->ns_rrsets + msg->ar_rrsets; |
943 | 3.55k | return 0; |
944 | 5.00k | } |
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 | 0 | { |
952 | | /* To respond with a Keepalive option, the client connection must have |
953 | | * received one message with a TCP Keepalive EDNS option, and that |
954 | | * option must have 0 length data. Subsequent messages sent on that |
955 | | * connection will have a TCP Keepalive option. |
956 | | * |
957 | | * In the if-statement below, the option is added unsolicited. This |
958 | | * means that the client has sent an KEEPALIVE option earlier. We know |
959 | | * here this is true, because c->tcp_keepalive is set. |
960 | | */ |
961 | 0 | if (cfg && cfg->do_tcp_keepalive && c && c->type != comm_udp && c->tcp_keepalive) { |
962 | 0 | if(!edns_opt_list_append_keepalive(&edns->opt_list_out, |
963 | 0 | c->tcp_timeout_msec / 100, region)) { |
964 | 0 | log_err("out of memory"); |
965 | 0 | return LDNS_RCODE_SERVFAIL; |
966 | 0 | } |
967 | 0 | } |
968 | | |
969 | | /* while still more options, and have code+len to read */ |
970 | | /* ignores partial content (i.e. rdata len 3) */ |
971 | 0 | while(rdata_len >= 4) { |
972 | 0 | uint16_t opt_code = sldns_read_uint16(rdata_ptr); |
973 | 0 | uint16_t opt_len = sldns_read_uint16(rdata_ptr+2); |
974 | 0 | uint8_t server_cookie[40]; |
975 | 0 | enum edns_cookie_val_status cookie_val_status; |
976 | 0 | int cookie_is_v4 = 1; |
977 | |
|
978 | 0 | rdata_ptr += 4; |
979 | 0 | rdata_len -= 4; |
980 | 0 | if(opt_len > rdata_len) |
981 | 0 | break; /* option code partial */ |
982 | | |
983 | | /* handle parse time edns options here */ |
984 | 0 | switch(opt_code) { |
985 | 0 | case LDNS_EDNS_NSID: |
986 | 0 | if (!cfg || !cfg->nsid) |
987 | 0 | break; |
988 | 0 | if(!edns_opt_list_append(&edns->opt_list_out, |
989 | 0 | LDNS_EDNS_NSID, cfg->nsid_len, |
990 | 0 | cfg->nsid, region)) { |
991 | 0 | log_err("out of memory"); |
992 | 0 | return LDNS_RCODE_SERVFAIL; |
993 | 0 | } |
994 | 0 | break; |
995 | | |
996 | 0 | case LDNS_EDNS_KEEPALIVE: |
997 | | /* To respond with a Keepalive option, the client |
998 | | * connection must have received one message with a TCP |
999 | | * Keepalive EDNS option, and that option must have 0 |
1000 | | * length data. Subsequent messages sent on that |
1001 | | * connection will have a TCP Keepalive option. |
1002 | | * |
1003 | | * This should be the first time the client sends this |
1004 | | * option, so c->tcp_keepalive is not set. |
1005 | | * Besides adding the reply KEEPALIVE option, |
1006 | | * c->tcp_keepalive will be set so that the |
1007 | | * option will be added unsolicited in subsequent |
1008 | | * responses (see the comment above the if-statement |
1009 | | * at the start of this function). |
1010 | | */ |
1011 | 0 | if (!cfg || !cfg->do_tcp_keepalive || !c || |
1012 | 0 | c->type == comm_udp || c->tcp_keepalive) |
1013 | 0 | break; |
1014 | 0 | if(opt_len) { |
1015 | 0 | verbose(VERB_ALGO, "query with bad edns keepalive."); |
1016 | 0 | return LDNS_RCODE_FORMERR; |
1017 | 0 | } |
1018 | 0 | if(!edns_opt_list_append_keepalive(&edns->opt_list_out, |
1019 | 0 | c->tcp_timeout_msec / 100, |
1020 | 0 | region)) { |
1021 | 0 | log_err("out of memory"); |
1022 | 0 | return LDNS_RCODE_SERVFAIL; |
1023 | 0 | } |
1024 | 0 | c->tcp_keepalive = 1; |
1025 | 0 | break; |
1026 | | |
1027 | 0 | case LDNS_EDNS_PADDING: |
1028 | 0 | if(!cfg || !cfg->pad_responses || |
1029 | 0 | !c || c->type != comm_tcp ||!c->ssl) |
1030 | 0 | break; |
1031 | 0 | if(!edns_opt_list_append(&edns->opt_list_out, |
1032 | 0 | LDNS_EDNS_PADDING, |
1033 | 0 | 0, NULL, region)) { |
1034 | 0 | log_err("out of memory"); |
1035 | 0 | return LDNS_RCODE_SERVFAIL; |
1036 | 0 | } |
1037 | 0 | edns->padding_block_size = cfg->pad_responses_block_size; |
1038 | 0 | break; |
1039 | | |
1040 | 0 | case LDNS_EDNS_COOKIE: |
1041 | 0 | if(!cfg || !cfg->do_answer_cookie || !repinfo) |
1042 | 0 | break; |
1043 | 0 | if(opt_len != 8 && (opt_len < 16 || opt_len > 40)) { |
1044 | 0 | verbose(VERB_ALGO, "worker request: " |
1045 | 0 | "badly formatted cookie"); |
1046 | 0 | return LDNS_RCODE_FORMERR; |
1047 | 0 | } |
1048 | 0 | edns->cookie_present = 1; |
1049 | | |
1050 | | /* Copy client cookie, version and timestamp for |
1051 | | * validation and creation purposes. |
1052 | | */ |
1053 | 0 | if(opt_len >= 16) { |
1054 | 0 | memmove(server_cookie, rdata_ptr, 16); |
1055 | 0 | } else { |
1056 | 0 | memset(server_cookie, 0, 16); |
1057 | 0 | memmove(server_cookie, rdata_ptr, opt_len); |
1058 | 0 | } |
1059 | | |
1060 | | /* Copy client ip for validation and creation |
1061 | | * purposes. It will be overwritten if (re)creation |
1062 | | * is needed. |
1063 | | */ |
1064 | 0 | if(repinfo->remote_addr.ss_family == AF_INET) { |
1065 | 0 | memcpy(server_cookie + 16, |
1066 | 0 | &((struct sockaddr_in*)&repinfo->remote_addr)->sin_addr, 4); |
1067 | 0 | } else { |
1068 | 0 | cookie_is_v4 = 0; |
1069 | 0 | memcpy(server_cookie + 16, |
1070 | 0 | &((struct sockaddr_in6*)&repinfo->remote_addr)->sin6_addr, 16); |
1071 | 0 | } |
1072 | |
|
1073 | 0 | cookie_val_status = edns_cookie_server_validate( |
1074 | 0 | rdata_ptr, opt_len, cfg->cookie_secret, |
1075 | 0 | cfg->cookie_secret_len, cookie_is_v4, |
1076 | 0 | server_cookie, now); |
1077 | 0 | switch(cookie_val_status) { |
1078 | 0 | case COOKIE_STATUS_VALID: |
1079 | 0 | case COOKIE_STATUS_VALID_RENEW: |
1080 | 0 | edns->cookie_valid = 1; |
1081 | | /* Reuse cookie */ |
1082 | 0 | if(!edns_opt_list_append( |
1083 | 0 | &edns->opt_list_out, LDNS_EDNS_COOKIE, |
1084 | 0 | opt_len, rdata_ptr, region)) { |
1085 | 0 | log_err("out of memory"); |
1086 | 0 | return LDNS_RCODE_SERVFAIL; |
1087 | 0 | } |
1088 | | /* Cookie to be reused added to outgoing |
1089 | | * options. Done! |
1090 | | */ |
1091 | 0 | break; |
1092 | 0 | case COOKIE_STATUS_CLIENT_ONLY: |
1093 | 0 | edns->cookie_client = 1; |
1094 | | /* fallthrough */ |
1095 | 0 | case COOKIE_STATUS_FUTURE: |
1096 | 0 | case COOKIE_STATUS_EXPIRED: |
1097 | 0 | case COOKIE_STATUS_INVALID: |
1098 | 0 | default: |
1099 | 0 | edns_cookie_server_write(server_cookie, |
1100 | 0 | cfg->cookie_secret, cookie_is_v4, now); |
1101 | 0 | if(!edns_opt_list_append(&edns->opt_list_out, |
1102 | 0 | LDNS_EDNS_COOKIE, 24, server_cookie, |
1103 | 0 | region)) { |
1104 | 0 | log_err("out of memory"); |
1105 | 0 | return LDNS_RCODE_SERVFAIL; |
1106 | 0 | } |
1107 | 0 | break; |
1108 | 0 | } |
1109 | 0 | break; |
1110 | 0 | default: |
1111 | 0 | break; |
1112 | 0 | } |
1113 | 0 | if(!edns_opt_list_append(&edns->opt_list_in, |
1114 | 0 | opt_code, opt_len, rdata_ptr, region)) { |
1115 | 0 | log_err("out of memory"); |
1116 | 0 | return LDNS_RCODE_SERVFAIL; |
1117 | 0 | } |
1118 | 0 | rdata_ptr += opt_len; |
1119 | 0 | rdata_len -= opt_len; |
1120 | 0 | } |
1121 | 0 | return LDNS_RCODE_NOERROR; |
1122 | 0 | } |
1123 | | |
1124 | | int |
1125 | | parse_extract_edns_from_response_msg(struct msg_parse* msg, |
1126 | | struct edns_data* edns, struct regional* region) |
1127 | 3.52k | { |
1128 | 3.52k | struct rrset_parse* rrset = msg->rrset_first; |
1129 | 3.52k | struct rrset_parse* prev = 0; |
1130 | 3.52k | struct rrset_parse* found = 0; |
1131 | 3.52k | struct rrset_parse* found_prev = 0; |
1132 | 3.52k | size_t rdata_len; |
1133 | 3.52k | uint8_t* rdata_ptr; |
1134 | | /* since the class encodes the UDP size, we cannot use hash table to |
1135 | | * find the EDNS OPT record. Scan the packet. */ |
1136 | 80.2k | while(rrset) { |
1137 | 76.6k | if(rrset->type == LDNS_RR_TYPE_OPT) { |
1138 | | /* only one OPT RR allowed. */ |
1139 | 231 | if(found) return LDNS_RCODE_FORMERR; |
1140 | | /* found it! */ |
1141 | 229 | found_prev = prev; |
1142 | 229 | found = rrset; |
1143 | 229 | } |
1144 | 76.6k | prev = rrset; |
1145 | 76.6k | rrset = rrset->rrset_all_next; |
1146 | 76.6k | } |
1147 | 3.52k | if(!found) { |
1148 | 3.29k | memset(edns, 0, sizeof(*edns)); |
1149 | 3.29k | edns->udp_size = 512; |
1150 | 3.29k | return 0; |
1151 | 3.29k | } |
1152 | | /* check the found RRset */ |
1153 | | /* most lenient check possible. ignore dname, use last opt */ |
1154 | 227 | if(found->section != LDNS_SECTION_ADDITIONAL) |
1155 | 5 | return LDNS_RCODE_FORMERR; |
1156 | 222 | if(found->rr_count == 0) |
1157 | 1 | return LDNS_RCODE_FORMERR; |
1158 | 221 | if(0) { /* strict checking of dname and RRcount */ |
1159 | 0 | if(found->dname_len != 1 || !found->dname |
1160 | 0 | || found->dname[0] != 0) return LDNS_RCODE_FORMERR; |
1161 | 0 | if(found->rr_count != 1) return LDNS_RCODE_FORMERR; |
1162 | 0 | } |
1163 | 221 | log_assert(found->rr_first && found->rr_last); |
1164 | | |
1165 | | /* remove from packet */ |
1166 | 221 | if(found_prev) found_prev->rrset_all_next = found->rrset_all_next; |
1167 | 183 | else msg->rrset_first = found->rrset_all_next; |
1168 | 221 | if(found == msg->rrset_last) |
1169 | 192 | msg->rrset_last = found_prev; |
1170 | 221 | msg->arcount --; |
1171 | 221 | msg->ar_rrsets --; |
1172 | 221 | msg->rrset_count --; |
1173 | | |
1174 | | /* take the data ! */ |
1175 | 221 | edns->edns_present = 1; |
1176 | 221 | edns->ext_rcode = found->rr_last->ttl_data[0]; |
1177 | 221 | edns->edns_version = found->rr_last->ttl_data[1]; |
1178 | 221 | edns->bits = sldns_read_uint16(&found->rr_last->ttl_data[2]); |
1179 | 221 | edns->udp_size = ntohs(found->rrset_class); |
1180 | 221 | edns->opt_list_in = NULL; |
1181 | 221 | edns->opt_list_out = NULL; |
1182 | 221 | edns->opt_list_inplace_cb_out = NULL; |
1183 | 221 | edns->padding_block_size = 0; |
1184 | 221 | edns->cookie_present = 0; |
1185 | 221 | edns->cookie_valid = 0; |
1186 | | |
1187 | | /* take the options */ |
1188 | 221 | rdata_len = found->rr_first->size-2; |
1189 | 221 | rdata_ptr = found->rr_first->ttl_data+6; |
1190 | | |
1191 | | /* while still more options, and have code+len to read */ |
1192 | | /* ignores partial content (i.e. rdata len 3) */ |
1193 | 17.8k | while(rdata_len >= 4) { |
1194 | 17.7k | uint16_t opt_code = sldns_read_uint16(rdata_ptr); |
1195 | 17.7k | uint16_t opt_len = sldns_read_uint16(rdata_ptr+2); |
1196 | 17.7k | rdata_ptr += 4; |
1197 | 17.7k | rdata_len -= 4; |
1198 | 17.7k | if(opt_len > rdata_len) |
1199 | 102 | break; /* option code partial */ |
1200 | | |
1201 | 17.6k | if(!edns_opt_list_append(&edns->opt_list_in, |
1202 | 17.6k | opt_code, opt_len, rdata_ptr, region)) { |
1203 | 0 | log_err("out of memory"); |
1204 | 0 | break; |
1205 | 0 | } |
1206 | 17.6k | rdata_ptr += opt_len; |
1207 | 17.6k | rdata_len -= opt_len; |
1208 | 17.6k | } |
1209 | | /* ignore rrsigs */ |
1210 | 221 | return LDNS_RCODE_NOERROR; |
1211 | 221 | } |
1212 | | |
1213 | | /** skip RR in packet */ |
1214 | | static int |
1215 | | skip_pkt_rr(sldns_buffer* pkt) |
1216 | 0 | { |
1217 | 0 | if(sldns_buffer_remaining(pkt) < 1) return 0; |
1218 | 0 | if(!pkt_dname_len(pkt)) |
1219 | 0 | return 0; |
1220 | 0 | if(sldns_buffer_remaining(pkt) < 4) return 0; |
1221 | 0 | sldns_buffer_skip(pkt, 4); /* type and class */ |
1222 | 0 | if(!skip_ttl_rdata(pkt)) |
1223 | 0 | return 0; |
1224 | 0 | return 1; |
1225 | 0 | } |
1226 | | |
1227 | | /** skip RRs from packet */ |
1228 | | int |
1229 | | skip_pkt_rrs(sldns_buffer* pkt, int num) |
1230 | 0 | { |
1231 | 0 | int i; |
1232 | 0 | for(i=0; i<num; i++) { |
1233 | 0 | if(!skip_pkt_rr(pkt)) |
1234 | 0 | return 0; |
1235 | 0 | } |
1236 | 0 | return 1; |
1237 | 0 | } |
1238 | | |
1239 | | int |
1240 | | parse_edns_from_query_pkt(sldns_buffer* pkt, struct edns_data* edns, |
1241 | | struct config_file* cfg, struct comm_point* c, |
1242 | | struct comm_reply* repinfo, time_t now, struct regional* region) |
1243 | 0 | { |
1244 | 0 | size_t rdata_len; |
1245 | 0 | uint8_t* rdata_ptr; |
1246 | 0 | log_assert(LDNS_QDCOUNT(sldns_buffer_begin(pkt)) == 1); |
1247 | 0 | memset(edns, 0, sizeof(*edns)); |
1248 | 0 | if(LDNS_ANCOUNT(sldns_buffer_begin(pkt)) != 0 || |
1249 | 0 | LDNS_NSCOUNT(sldns_buffer_begin(pkt)) != 0) { |
1250 | 0 | if(!skip_pkt_rrs(pkt, ((int)LDNS_ANCOUNT(sldns_buffer_begin(pkt)))+ |
1251 | 0 | ((int)LDNS_NSCOUNT(sldns_buffer_begin(pkt))))) |
1252 | 0 | return LDNS_RCODE_FORMERR; |
1253 | 0 | } |
1254 | | /* check edns section is present */ |
1255 | 0 | if(LDNS_ARCOUNT(sldns_buffer_begin(pkt)) > 1) { |
1256 | 0 | return LDNS_RCODE_FORMERR; |
1257 | 0 | } |
1258 | 0 | if(LDNS_ARCOUNT(sldns_buffer_begin(pkt)) == 0) { |
1259 | 0 | edns->udp_size = 512; |
1260 | 0 | return 0; |
1261 | 0 | } |
1262 | | /* domain name must be the root of length 1. */ |
1263 | 0 | if(pkt_dname_len(pkt) != 1) |
1264 | 0 | return LDNS_RCODE_FORMERR; |
1265 | 0 | if(sldns_buffer_remaining(pkt) < 10) /* type, class, ttl, rdatalen */ |
1266 | 0 | return LDNS_RCODE_FORMERR; |
1267 | 0 | if(sldns_buffer_read_u16(pkt) != LDNS_RR_TYPE_OPT) |
1268 | 0 | return LDNS_RCODE_FORMERR; |
1269 | 0 | edns->edns_present = 1; |
1270 | 0 | edns->udp_size = sldns_buffer_read_u16(pkt); /* class is udp size */ |
1271 | 0 | edns->ext_rcode = sldns_buffer_read_u8(pkt); /* ttl used for bits */ |
1272 | 0 | edns->edns_version = sldns_buffer_read_u8(pkt); |
1273 | 0 | edns->bits = sldns_buffer_read_u16(pkt); |
1274 | 0 | edns->opt_list_in = NULL; |
1275 | 0 | edns->opt_list_out = NULL; |
1276 | 0 | edns->opt_list_inplace_cb_out = NULL; |
1277 | 0 | edns->padding_block_size = 0; |
1278 | 0 | edns->cookie_present = 0; |
1279 | 0 | edns->cookie_valid = 0; |
1280 | | |
1281 | | /* take the options */ |
1282 | 0 | rdata_len = sldns_buffer_read_u16(pkt); |
1283 | 0 | if(sldns_buffer_remaining(pkt) < rdata_len) |
1284 | 0 | return LDNS_RCODE_FORMERR; |
1285 | 0 | rdata_ptr = sldns_buffer_current(pkt); |
1286 | | /* ignore rrsigs */ |
1287 | 0 | return parse_edns_options_from_query(rdata_ptr, rdata_len, edns, cfg, |
1288 | 0 | c, repinfo, now, region); |
1289 | 0 | } |
1290 | | |
1291 | | void |
1292 | | log_edns_opt_list(enum verbosity_value level, const char* info_str, |
1293 | | struct edns_option* list) |
1294 | 0 | { |
1295 | 0 | if(verbosity >= level && list) { |
1296 | 0 | char str[128], *s; |
1297 | 0 | size_t slen; |
1298 | 0 | verbose(level, "%s", info_str); |
1299 | 0 | while(list) { |
1300 | 0 | s = str; |
1301 | 0 | slen = sizeof(str); |
1302 | 0 | (void)sldns_wire2str_edns_option_print(&s, &slen, list->opt_code, |
1303 | 0 | list->opt_data, list->opt_len); |
1304 | 0 | verbose(level, " %s", str); |
1305 | 0 | list = list->next; |
1306 | 0 | } |
1307 | 0 | } |
1308 | 0 | } |
1309 | | |
1310 | | /** remove RR from msgparse RRset, return true if rrset is entirely bad */ |
1311 | | int |
1312 | | msgparse_rrset_remove_rr(const char* str, sldns_buffer* pkt, struct rrset_parse* rrset, |
1313 | | struct rr_parse* prev, struct rr_parse* rr, struct sockaddr_storage* addr, socklen_t addrlen) |
1314 | 190k | { |
1315 | 190k | if(verbosity >= VERB_QUERY && rrset->dname_len <= LDNS_MAX_DOMAINLEN && str) { |
1316 | 0 | uint8_t buf[LDNS_MAX_DOMAINLEN+1]; |
1317 | 0 | dname_pkt_copy(pkt, buf, rrset->dname); |
1318 | 0 | if(addr) |
1319 | 0 | log_name_addr(VERB_QUERY, str, buf, addr, addrlen); |
1320 | 0 | else log_nametypeclass(VERB_QUERY, str, buf, |
1321 | 0 | rrset->type, ntohs(rrset->rrset_class)); |
1322 | 0 | } |
1323 | 190k | if(prev) |
1324 | 56.2k | prev->next = rr->next; |
1325 | 134k | else rrset->rr_first = rr->next; |
1326 | 190k | if(rrset->rr_last == rr) |
1327 | 5.46k | rrset->rr_last = prev; |
1328 | 190k | rrset->rr_count --; |
1329 | 190k | rrset->size -= rr->size; |
1330 | | /* rr struct still exists, but is unlinked, so that in the for loop |
1331 | | * the rr->next works fine to continue. */ |
1332 | 190k | return rrset->rr_count == 0; |
1333 | 190k | } |