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