1 /*
2 * util/data/msgencode.c - Encode DNS messages, queries and replies.
3 *
4 * Copyright (c) 2007, NLnet Labs. All rights reserved.
5 *
6 * This software is open source.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
14 *
15 * Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
18 *
19 * Neither the name of the NLNET LABS nor the names of its contributors may
20 * be used to endorse or promote products derived from this software without
21 * specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35
36 /**
37 * \file
38 *
39 * This file contains a routines to encode DNS messages.
40 */
41
42 #include "config.h"
43 #include "util/data/msgencode.h"
44 #include "util/data/msgreply.h"
45 #include "util/data/msgparse.h"
46 #include "util/data/dname.h"
47 #include "util/log.h"
48 #include "util/regional.h"
49 #include "util/net_help.h"
50 #include "sldns/sbuffer.h"
51 #include "services/localzone.h"
52
53 #ifdef HAVE_TIME_H
54 #include <time.h>
55 #endif
56 #include <sys/time.h>
57
58 /** return code that means the function ran out of memory. negative so it does
59 * not conflict with DNS rcodes. */
60 #define RETVAL_OUTMEM -2
61 /** return code that means the data did not fit (completely) in the packet */
62 #define RETVAL_TRUNC -4
63 /** return code that means all is peachy keen. Equal to DNS rcode NOERROR */
64 #define RETVAL_OK 0
65 /** Max compressions we are willing to perform; more than that will result
66 * in semi-compressed messages, or truncated even on TCP for huge messages, to
67 * avoid locking the CPU for long */
68 #define MAX_COMPRESSION_PER_MESSAGE 120
69
70 /**
71 * Data structure to help domain name compression in outgoing messages.
72 * A tree of dnames and their offsets in the packet is kept.
73 * It is kept sorted, not canonical, but by label at least, so that after
74 * a lookup of a name you know its closest match, and the parent from that
75 * closest match. These are possible compression targets.
76 *
77 * It is a binary tree, not a rbtree or balanced tree, as the effort
78 * of keeping it balanced probably outweighs usefulness (given typical
79 * DNS packet size).
80 */
81 struct compress_tree_node {
82 /** left node in tree, all smaller to this */
83 struct compress_tree_node* left;
84 /** right node in tree, all larger than this */
85 struct compress_tree_node* right;
86
87 /** the parent node - not for tree, but zone parent. One less label */
88 struct compress_tree_node* parent;
89 /** the domain name for this node. Pointer to uncompressed memory. */
90 uint8_t* dname;
91 /** number of labels in domain name, kept to help compare func. */
92 int labs;
93 /** offset in packet that points to this dname */
94 size_t offset;
95 };
96
97 /**
98 * Find domain name in tree, returns exact and closest match.
99 * @param tree: root of tree.
100 * @param dname: pointer to uncompressed dname.
101 * @param labs: number of labels in domain name.
102 * @param match: closest or exact match.
103 * guaranteed to be smaller or equal to the sought dname.
104 * can be null if the tree is empty.
105 * @param matchlabels: number of labels that match with closest match.
106 * can be zero is there is no match.
107 * @param insertpt: insert location for dname, if not found.
108 * @return: 0 if no exact match.
109 */
110 static int
compress_tree_search(struct compress_tree_node ** tree,uint8_t * dname,int labs,struct compress_tree_node ** match,int * matchlabels,struct compress_tree_node *** insertpt)111 compress_tree_search(struct compress_tree_node** tree, uint8_t* dname,
112 int labs, struct compress_tree_node** match, int* matchlabels,
113 struct compress_tree_node*** insertpt)
114 {
115 int c, n, closen=0;
116 struct compress_tree_node* p = *tree;
117 struct compress_tree_node* close = 0;
118 struct compress_tree_node** prev = tree;
119 while(p) {
120 if((c = dname_lab_cmp(dname, labs, p->dname, p->labs, &n))
121 == 0) {
122 *matchlabels = n;
123 *match = p;
124 return 1;
125 }
126 if(c<0) {
127 prev = &p->left;
128 p = p->left;
129 } else {
130 closen = n;
131 close = p; /* p->dname is smaller than dname */
132 prev = &p->right;
133 p = p->right;
134 }
135 }
136 *insertpt = prev;
137 *matchlabels = closen;
138 *match = close;
139 return 0;
140 }
141
142 /**
143 * Lookup a domain name in compression tree.
144 * @param tree: root of tree (not the node with '.').
145 * @param dname: pointer to uncompressed dname.
146 * @param labs: number of labels in domain name.
147 * @param insertpt: insert location for dname, if not found.
148 * @return: 0 if not found or compress treenode with best compression.
149 */
150 static struct compress_tree_node*
compress_tree_lookup(struct compress_tree_node ** tree,uint8_t * dname,int labs,struct compress_tree_node *** insertpt)151 compress_tree_lookup(struct compress_tree_node** tree, uint8_t* dname,
152 int labs, struct compress_tree_node*** insertpt)
153 {
154 struct compress_tree_node* p;
155 int m;
156 if(labs <= 1)
157 return 0; /* do not compress root node */
158 if(compress_tree_search(tree, dname, labs, &p, &m, insertpt)) {
159 /* exact match */
160 return p;
161 }
162 /* return some ancestor of p that compresses well. */
163 if(m>1) {
164 /* www.example.com. (labs=4) matched foo.example.com.(labs=4)
165 * then matchcount = 3. need to go up. */
166 while(p && p->labs > m)
167 p = p->parent;
168 return p;
169 }
170 return 0;
171 }
172
173 /**
174 * Create node for domain name compression tree.
175 * @param dname: pointer to uncompressed dname (stored in tree).
176 * @param labs: number of labels in dname.
177 * @param offset: offset into packet for dname.
178 * @param region: how to allocate memory for new node.
179 * @return new node or 0 on malloc failure.
180 */
181 static struct compress_tree_node*
compress_tree_newnode(uint8_t * dname,int labs,size_t offset,struct regional * region)182 compress_tree_newnode(uint8_t* dname, int labs, size_t offset,
183 struct regional* region)
184 {
185 struct compress_tree_node* n = (struct compress_tree_node*)
186 regional_alloc(region, sizeof(struct compress_tree_node));
187 if(!n) return 0;
188 n->left = 0;
189 n->right = 0;
190 n->parent = 0;
191 n->dname = dname;
192 n->labs = labs;
193 n->offset = offset;
194 return n;
195 }
196
197 /**
198 * Store domain name and ancestors into compression tree.
199 * @param dname: pointer to uncompressed dname (stored in tree).
200 * @param labs: number of labels in dname.
201 * @param offset: offset into packet for dname.
202 * @param region: how to allocate memory for new node.
203 * @param closest: match from previous lookup, used to compress dname.
204 * may be NULL if no previous match.
205 * if the tree has an ancestor of dname already, this must be it.
206 * @param insertpt: where to insert the dname in tree.
207 * @return: 0 on memory error.
208 */
209 static int
compress_tree_store(uint8_t * dname,int labs,size_t offset,struct regional * region,struct compress_tree_node * closest,struct compress_tree_node ** insertpt)210 compress_tree_store(uint8_t* dname, int labs, size_t offset,
211 struct regional* region, struct compress_tree_node* closest,
212 struct compress_tree_node** insertpt)
213 {
214 uint8_t lablen;
215 struct compress_tree_node* newnode;
216 struct compress_tree_node* prevnode = NULL;
217 int uplabs = labs-1; /* does not store root in tree */
218 if(closest) uplabs = labs - closest->labs;
219 log_assert(uplabs >= 0);
220 /* algorithms builds up a vine of dname-labels to hang into tree */
221 while(uplabs--) {
222 if(offset > PTR_MAX_OFFSET) {
223 /* insertion failed, drop vine */
224 return 1; /* compression pointer no longer useful */
225 }
226 if(!(newnode = compress_tree_newnode(dname, labs, offset,
227 region))) {
228 /* insertion failed, drop vine */
229 return 0;
230 }
231
232 if(prevnode) {
233 /* chain nodes together, last one has one label more,
234 * so is larger than newnode, thus goes right. */
235 newnode->right = prevnode;
236 prevnode->parent = newnode;
237 }
238
239 /* next label */
240 lablen = *dname++;
241 dname += lablen;
242 offset += lablen+1;
243 prevnode = newnode;
244 labs--;
245 }
246 /* if we have a vine, hang the vine into the tree */
247 if(prevnode) {
248 *insertpt = prevnode;
249 prevnode->parent = closest;
250 }
251 return 1;
252 }
253
254 /** compress a domain name */
255 static int
write_compressed_dname(sldns_buffer * pkt,uint8_t * dname,int labs,struct compress_tree_node * p)256 write_compressed_dname(sldns_buffer* pkt, uint8_t* dname, int labs,
257 struct compress_tree_node* p)
258 {
259 /* compress it */
260 int labcopy = labs - p->labs;
261 uint8_t lablen;
262 uint16_t ptr;
263
264 if(labs == 1) {
265 /* write root label */
266 if(sldns_buffer_remaining(pkt) < 1)
267 return 0;
268 sldns_buffer_write_u8(pkt, 0);
269 return 1;
270 }
271
272 /* copy the first couple of labels */
273 while(labcopy--) {
274 lablen = *dname++;
275 if(sldns_buffer_remaining(pkt) < (size_t)lablen+1)
276 return 0;
277 sldns_buffer_write_u8(pkt, lablen);
278 sldns_buffer_write(pkt, dname, lablen);
279 dname += lablen;
280 }
281 /* insert compression ptr */
282 if(sldns_buffer_remaining(pkt) < 2)
283 return 0;
284 ptr = PTR_CREATE(p->offset);
285 sldns_buffer_write_u16(pkt, ptr);
286 return 1;
287 }
288
289 /** compress owner name of RR, return RETVAL_OUTMEM RETVAL_TRUNC */
290 static int
compress_owner(struct ub_packed_rrset_key * key,sldns_buffer * pkt,struct regional * region,struct compress_tree_node ** tree,size_t owner_pos,uint16_t * owner_ptr,int owner_labs,size_t * compress_count)291 compress_owner(struct ub_packed_rrset_key* key, sldns_buffer* pkt,
292 struct regional* region, struct compress_tree_node** tree,
293 size_t owner_pos, uint16_t* owner_ptr, int owner_labs,
294 size_t* compress_count)
295 {
296 struct compress_tree_node* p;
297 struct compress_tree_node** insertpt = NULL;
298 if(!*owner_ptr) {
299 /* compress first time dname */
300 if(*compress_count < MAX_COMPRESSION_PER_MESSAGE &&
301 (p = compress_tree_lookup(tree, key->rk.dname,
302 owner_labs, &insertpt))) {
303 if(p->labs == owner_labs)
304 /* avoid ptr chains, since some software is
305 * not capable of decoding ptr after a ptr. */
306 *owner_ptr = htons(PTR_CREATE(p->offset));
307 if(!write_compressed_dname(pkt, key->rk.dname,
308 owner_labs, p))
309 return RETVAL_TRUNC;
310 (*compress_count)++;
311 /* check if typeclass+4 ttl + rdatalen is available */
312 if(sldns_buffer_remaining(pkt) < 4+4+2)
313 return RETVAL_TRUNC;
314 } else {
315 /* no compress */
316 if(sldns_buffer_remaining(pkt) < key->rk.dname_len+4+4+2)
317 return RETVAL_TRUNC;
318 sldns_buffer_write(pkt, key->rk.dname,
319 key->rk.dname_len);
320 if(owner_pos <= PTR_MAX_OFFSET)
321 *owner_ptr = htons(PTR_CREATE(owner_pos));
322 }
323 if(*compress_count < MAX_COMPRESSION_PER_MESSAGE &&
324 !compress_tree_store(key->rk.dname, owner_labs,
325 owner_pos, region, p, insertpt))
326 return RETVAL_OUTMEM;
327 } else {
328 /* always compress 2nd-further RRs in RRset */
329 if(owner_labs == 1) {
330 if(sldns_buffer_remaining(pkt) < 1+4+4+2)
331 return RETVAL_TRUNC;
332 sldns_buffer_write_u8(pkt, 0);
333 } else {
334 if(sldns_buffer_remaining(pkt) < 2+4+4+2)
335 return RETVAL_TRUNC;
336 sldns_buffer_write(pkt, owner_ptr, 2);
337 }
338 }
339 return RETVAL_OK;
340 }
341
342 /** compress any domain name to the packet, return RETVAL_* */
343 static int
compress_any_dname(uint8_t * dname,sldns_buffer * pkt,int labs,struct regional * region,struct compress_tree_node ** tree,size_t * compress_count)344 compress_any_dname(uint8_t* dname, sldns_buffer* pkt, int labs,
345 struct regional* region, struct compress_tree_node** tree,
346 size_t* compress_count)
347 {
348 struct compress_tree_node* p;
349 struct compress_tree_node** insertpt = NULL;
350 size_t pos = sldns_buffer_position(pkt);
351 if(*compress_count < MAX_COMPRESSION_PER_MESSAGE &&
352 (p = compress_tree_lookup(tree, dname, labs, &insertpt))) {
353 if(!write_compressed_dname(pkt, dname, labs, p))
354 return RETVAL_TRUNC;
355 } else {
356 if(!dname_buffer_write(pkt, dname))
357 return RETVAL_TRUNC;
358 }
359 if(*compress_count < MAX_COMPRESSION_PER_MESSAGE &&
360 !compress_tree_store(dname, labs, pos, region, p, insertpt))
361 return RETVAL_OUTMEM;
362 (*compress_count)++;
363 return RETVAL_OK;
364 }
365
366 /** return true if type needs domain name compression in rdata */
367 static const sldns_rr_descriptor*
type_rdata_compressible(struct ub_packed_rrset_key * key)368 type_rdata_compressible(struct ub_packed_rrset_key* key)
369 {
370 uint16_t t = ntohs(key->rk.type);
371 if(sldns_rr_descript(t) &&
372 sldns_rr_descript(t)->_compress == LDNS_RR_COMPRESS)
373 return sldns_rr_descript(t);
374 return 0;
375 }
376
377 /** compress domain names in rdata, return RETVAL_* */
378 static int
compress_rdata(sldns_buffer * pkt,uint8_t * rdata,size_t todolen,struct regional * region,struct compress_tree_node ** tree,const sldns_rr_descriptor * desc,size_t * compress_count)379 compress_rdata(sldns_buffer* pkt, uint8_t* rdata, size_t todolen,
380 struct regional* region, struct compress_tree_node** tree,
381 const sldns_rr_descriptor* desc, size_t* compress_count)
382 {
383 int labs, r, rdf = 0;
384 size_t dname_len, len, pos = sldns_buffer_position(pkt);
385 uint8_t count = desc->_dname_count;
386
387 sldns_buffer_skip(pkt, 2); /* rdata len fill in later */
388 /* space for rdatalen checked for already */
389 rdata += 2;
390 todolen -= 2;
391 while(todolen > 0 && count) {
392 switch(desc->_wireformat[rdf]) {
393 case LDNS_RDF_TYPE_DNAME:
394 labs = dname_count_size_labels(rdata, &dname_len);
395 if((r=compress_any_dname(rdata, pkt, labs, region,
396 tree, compress_count)) != RETVAL_OK)
397 return r;
398 rdata += dname_len;
399 todolen -= dname_len;
400 count--;
401 len = 0;
402 break;
403 case LDNS_RDF_TYPE_STR:
404 len = *rdata + 1;
405 break;
406 default:
407 len = get_rdf_size(desc->_wireformat[rdf]);
408 }
409 if(len) {
410 /* copy over */
411 if(sldns_buffer_remaining(pkt) < len)
412 return RETVAL_TRUNC;
413 sldns_buffer_write(pkt, rdata, len);
414 todolen -= len;
415 rdata += len;
416 }
417 rdf++;
418 }
419 /* copy remainder */
420 if(todolen > 0) {
421 if(sldns_buffer_remaining(pkt) < todolen)
422 return RETVAL_TRUNC;
423 sldns_buffer_write(pkt, rdata, todolen);
424 }
425
426 /* set rdata len */
427 sldns_buffer_write_u16_at(pkt, pos, sldns_buffer_position(pkt)-pos-2);
428 return RETVAL_OK;
429 }
430
431 /** Returns true if RR type should be included */
432 static int
rrset_belongs_in_reply(sldns_pkt_section s,uint16_t rrtype,uint16_t qtype,int dnssec)433 rrset_belongs_in_reply(sldns_pkt_section s, uint16_t rrtype, uint16_t qtype,
434 int dnssec)
435 {
436 if(dnssec)
437 return 1;
438 /* skip non DNSSEC types, except if directly queried for */
439 if(s == LDNS_SECTION_ANSWER) {
440 if(qtype == LDNS_RR_TYPE_ANY || qtype == rrtype)
441 return 1;
442 }
443 /* check DNSSEC-ness */
444 switch(rrtype) {
445 case LDNS_RR_TYPE_SIG:
446 case LDNS_RR_TYPE_KEY:
447 case LDNS_RR_TYPE_NXT:
448 case LDNS_RR_TYPE_DS:
449 case LDNS_RR_TYPE_RRSIG:
450 case LDNS_RR_TYPE_NSEC:
451 case LDNS_RR_TYPE_DNSKEY:
452 case LDNS_RR_TYPE_NSEC3:
453 case LDNS_RR_TYPE_NSEC3PARAMS:
454 return 0;
455 }
456 return 1;
457 }
458
459 /** store rrset in buffer in wireformat, return RETVAL_* */
460 static int
packed_rrset_encode(struct ub_packed_rrset_key * key,sldns_buffer * pkt,uint16_t * num_rrs,time_t timenow,struct regional * region,int do_data,int do_sig,struct compress_tree_node ** tree,sldns_pkt_section s,uint16_t qtype,int dnssec,size_t rr_offset,size_t * compress_count)461 packed_rrset_encode(struct ub_packed_rrset_key* key, sldns_buffer* pkt,
462 uint16_t* num_rrs, time_t timenow, struct regional* region,
463 int do_data, int do_sig, struct compress_tree_node** tree,
464 sldns_pkt_section s, uint16_t qtype, int dnssec, size_t rr_offset,
465 size_t* compress_count)
466 {
467 size_t i, j, owner_pos;
468 int r, owner_labs;
469 uint16_t owner_ptr = 0;
470 time_t adjust = 0;
471 struct packed_rrset_data* data = (struct packed_rrset_data*)
472 key->entry.data;
473
474 /* does this RR type belong in the answer? */
475 if(!rrset_belongs_in_reply(s, ntohs(key->rk.type), qtype, dnssec))
476 return RETVAL_OK;
477
478 owner_labs = dname_count_labels(key->rk.dname);
479 owner_pos = sldns_buffer_position(pkt);
480
481 /** Determine relative time adjustment for TTL values.
482 * For an rrset with a fixed TTL, use the rrset's TTL as given. */
483 if((key->rk.flags & PACKED_RRSET_FIXEDTTL) != 0)
484 adjust = 0;
485 else
486 adjust = SERVE_ORIGINAL_TTL ? data->ttl_add : timenow;
487
488 if(do_data) {
489 const sldns_rr_descriptor* c = type_rdata_compressible(key);
490 for(i=0; i<data->count; i++) {
491 /* rrset roundrobin */
492 j = (i + rr_offset) % data->count;
493 if((r=compress_owner(key, pkt, region, tree,
494 owner_pos, &owner_ptr, owner_labs,
495 compress_count)) != RETVAL_OK)
496 return r;
497 sldns_buffer_write(pkt, &key->rk.type, 2);
498 sldns_buffer_write(pkt, &key->rk.rrset_class, 2);
499 if(key->rk.flags & PACKED_RRSET_UPSTREAM_0TTL) {
500 sldns_buffer_write_u32(pkt, 0);
501 } else if(adjust == 0) {
502 sldns_buffer_write_u32(pkt, data->rr_ttl[j]);
503 } else if(TTL_IS_EXPIRED(data->rr_ttl[j], adjust)) {
504 sldns_buffer_write_u32(pkt,
505 EXPIRED_REPLY_TTL_CALC(
506 data->rr_ttl[j], data->ttl_add));
507 } else {
508 sldns_buffer_write_u32(pkt,
509 data->rr_ttl[j] - adjust);
510 }
511 if(c) {
512 if((r=compress_rdata(pkt, data->rr_data[j],
513 data->rr_len[j], region, tree, c,
514 compress_count)) != RETVAL_OK)
515 return r;
516 } else {
517 if(sldns_buffer_remaining(pkt) < data->rr_len[j])
518 return RETVAL_TRUNC;
519 sldns_buffer_write(pkt, data->rr_data[j],
520 data->rr_len[j]);
521 }
522 }
523 }
524 /* insert rrsigs */
525 if(do_sig && dnssec) {
526 size_t total = data->count+data->rrsig_count;
527 for(i=data->count; i<total; i++) {
528 if(owner_ptr && owner_labs != 1) {
529 if(sldns_buffer_remaining(pkt) <
530 2+4+4+data->rr_len[i])
531 return RETVAL_TRUNC;
532 sldns_buffer_write(pkt, &owner_ptr, 2);
533 } else {
534 if((r=compress_any_dname(key->rk.dname,
535 pkt, owner_labs, region, tree,
536 compress_count)) != RETVAL_OK)
537 return r;
538 if(sldns_buffer_remaining(pkt) <
539 4+4+data->rr_len[i])
540 return RETVAL_TRUNC;
541 }
542 sldns_buffer_write_u16(pkt, LDNS_RR_TYPE_RRSIG);
543 sldns_buffer_write(pkt, &key->rk.rrset_class, 2);
544 if(key->rk.flags & PACKED_RRSET_UPSTREAM_0TTL) {
545 sldns_buffer_write_u32(pkt, 0);
546 } else if(adjust == 0) {
547 sldns_buffer_write_u32(pkt, data->rr_ttl[i]);
548 } else if(TTL_IS_EXPIRED(data->rr_ttl[i], adjust)) {
549 sldns_buffer_write_u32(pkt,
550 EXPIRED_REPLY_TTL_CALC(
551 data->rr_ttl[i], data->ttl_add));
552 } else {
553 sldns_buffer_write_u32(pkt,
554 data->rr_ttl[i] - adjust);
555 }
556 /* rrsig rdata cannot be compressed, perform 100+ byte
557 * memcopy. */
558 sldns_buffer_write(pkt, data->rr_data[i],
559 data->rr_len[i]);
560 }
561 }
562 /* change rrnum only after we are sure it fits */
563 if(do_data)
564 *num_rrs += data->count;
565 if(do_sig && dnssec)
566 *num_rrs += data->rrsig_count;
567
568 return RETVAL_OK;
569 }
570
571 /** store msg section in wireformat buffer, return RETVAL_* */
572 static int
insert_section(struct reply_info * rep,size_t num_rrsets,uint16_t * num_rrs,sldns_buffer * pkt,size_t rrsets_before,time_t timenow,struct regional * region,struct compress_tree_node ** tree,sldns_pkt_section s,uint16_t qtype,int dnssec,size_t rr_offset,size_t * compress_count)573 insert_section(struct reply_info* rep, size_t num_rrsets, uint16_t* num_rrs,
574 sldns_buffer* pkt, size_t rrsets_before, time_t timenow,
575 struct regional* region, struct compress_tree_node** tree,
576 sldns_pkt_section s, uint16_t qtype, int dnssec, size_t rr_offset,
577 size_t* compress_count)
578 {
579 int r;
580 size_t i, setstart;
581 /* we now allow this function to be called multiple times for the
582 * same section, incrementally updating num_rrs. The caller is
583 * responsible for initializing it (which is the case in the current
584 * implementation). */
585
586 if(s != LDNS_SECTION_ADDITIONAL) {
587 if(s == LDNS_SECTION_ANSWER && qtype == LDNS_RR_TYPE_ANY)
588 dnssec = 1; /* include all types in ANY answer */
589 for(i=0; i<num_rrsets; i++) {
590 setstart = sldns_buffer_position(pkt);
591 if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i],
592 pkt, num_rrs, timenow, region, 1, 1, tree,
593 s, qtype, dnssec, rr_offset, compress_count))
594 != RETVAL_OK) {
595 /* Bad, but if due to size must set TC bit */
596 /* trim off the rrset neatly. */
597 sldns_buffer_set_position(pkt, setstart);
598 return r;
599 }
600 }
601 } else {
602 for(i=0; i<num_rrsets; i++) {
603 setstart = sldns_buffer_position(pkt);
604 if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i],
605 pkt, num_rrs, timenow, region, 1, 0, tree,
606 s, qtype, dnssec, rr_offset, compress_count))
607 != RETVAL_OK) {
608 sldns_buffer_set_position(pkt, setstart);
609 return r;
610 }
611 }
612 if(dnssec)
613 for(i=0; i<num_rrsets; i++) {
614 setstart = sldns_buffer_position(pkt);
615 if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i],
616 pkt, num_rrs, timenow, region, 0, 1, tree,
617 s, qtype, dnssec, rr_offset, compress_count))
618 != RETVAL_OK) {
619 sldns_buffer_set_position(pkt, setstart);
620 return r;
621 }
622 }
623 }
624 return RETVAL_OK;
625 }
626
627 /** store query section in wireformat buffer, return RETVAL */
628 static int
insert_query(struct query_info * qinfo,struct compress_tree_node ** tree,sldns_buffer * buffer,struct regional * region)629 insert_query(struct query_info* qinfo, struct compress_tree_node** tree,
630 sldns_buffer* buffer, struct regional* region)
631 {
632 uint8_t* qname = qinfo->local_alias ?
633 qinfo->local_alias->rrset->rk.dname : qinfo->qname;
634 size_t qname_len = qinfo->local_alias ?
635 qinfo->local_alias->rrset->rk.dname_len : qinfo->qname_len;
636 if(sldns_buffer_remaining(buffer) <
637 qinfo->qname_len+sizeof(uint16_t)*2)
638 return RETVAL_TRUNC; /* buffer too small */
639 /* the query is the first name inserted into the tree */
640 if(!compress_tree_store(qname, dname_count_labels(qname),
641 sldns_buffer_position(buffer), region, NULL, tree))
642 return RETVAL_OUTMEM;
643 if(sldns_buffer_current(buffer) == qname)
644 sldns_buffer_skip(buffer, (ssize_t)qname_len);
645 else sldns_buffer_write(buffer, qname, qname_len);
646 sldns_buffer_write_u16(buffer, qinfo->qtype);
647 sldns_buffer_write_u16(buffer, qinfo->qclass);
648 return RETVAL_OK;
649 }
650
651 static int
positive_answer(struct reply_info * rep,uint16_t qtype)652 positive_answer(struct reply_info* rep, uint16_t qtype) {
653 size_t i;
654 if (FLAGS_GET_RCODE(rep->flags) != LDNS_RCODE_NOERROR)
655 return 0;
656
657 for(i=0;i<rep->an_numrrsets; i++) {
658 if(ntohs(rep->rrsets[i]->rk.type) == qtype) {
659 /* for priming queries, type NS, include addresses */
660 if(qtype == LDNS_RR_TYPE_NS)
661 return 0;
662 /* in case it is a wildcard with DNSSEC, there will
663 * be NSEC/NSEC3 records in the authority section
664 * that we cannot remove */
665 for(i=rep->an_numrrsets; i<rep->an_numrrsets+
666 rep->ns_numrrsets; i++) {
667 if(ntohs(rep->rrsets[i]->rk.type) ==
668 LDNS_RR_TYPE_NSEC ||
669 ntohs(rep->rrsets[i]->rk.type) ==
670 LDNS_RR_TYPE_NSEC3)
671 return 0;
672 }
673 return 1;
674 }
675 }
676 return 0;
677 }
678
679 static int
negative_answer(struct reply_info * rep)680 negative_answer(struct reply_info* rep) {
681 size_t i;
682 int ns_seen = 0;
683 if(FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NXDOMAIN)
684 return 1;
685 if(FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NOERROR &&
686 rep->an_numrrsets != 0)
687 return 0; /* positive */
688 if(FLAGS_GET_RCODE(rep->flags) != LDNS_RCODE_NOERROR &&
689 FLAGS_GET_RCODE(rep->flags) != LDNS_RCODE_NXDOMAIN)
690 return 0;
691 for(i=rep->an_numrrsets; i<rep->an_numrrsets+rep->ns_numrrsets; i++){
692 if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_SOA)
693 return 1;
694 if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NS)
695 ns_seen = 1;
696 }
697 if(ns_seen) return 0; /* could be referral, NS, but no SOA */
698 return 1;
699 }
700
701 int
reply_info_encode(struct query_info * qinfo,struct reply_info * rep,uint16_t id,uint16_t flags,sldns_buffer * buffer,time_t timenow,struct regional * region,uint16_t udpsize,int dnssec,int minimise)702 reply_info_encode(struct query_info* qinfo, struct reply_info* rep,
703 uint16_t id, uint16_t flags, sldns_buffer* buffer, time_t timenow,
704 struct regional* region, uint16_t udpsize, int dnssec, int minimise)
705 {
706 uint16_t ancount=0, nscount=0, arcount=0;
707 struct compress_tree_node* tree = 0;
708 int r;
709 size_t rr_offset;
710 size_t compress_count=0;
711
712 sldns_buffer_clear(buffer);
713 if(udpsize < sldns_buffer_limit(buffer))
714 sldns_buffer_set_limit(buffer, udpsize);
715 if(sldns_buffer_remaining(buffer) < LDNS_HEADER_SIZE)
716 return 0;
717
718 sldns_buffer_write(buffer, &id, sizeof(uint16_t));
719 sldns_buffer_write_u16(buffer, flags);
720 sldns_buffer_write_u16(buffer, rep->qdcount);
721 /* set an, ns, ar counts to zero in case of small packets */
722 sldns_buffer_write(buffer, "\000\000\000\000\000\000", 6);
723
724 /* insert query section */
725 if(rep->qdcount) {
726 if((r=insert_query(qinfo, &tree, buffer, region)) !=
727 RETVAL_OK) {
728 if(r == RETVAL_TRUNC) {
729 /* create truncated message */
730 sldns_buffer_write_u16_at(buffer, 4, 0);
731 LDNS_TC_SET(sldns_buffer_begin(buffer));
732 sldns_buffer_flip(buffer);
733 return 1;
734 }
735 return 0;
736 }
737 }
738 /* roundrobin offset. using query id for random number. With ntohs
739 * for different roundrobins for sequential id client senders. */
740 rr_offset = RRSET_ROUNDROBIN?ntohs(id)+(timenow?timenow:time(NULL)):0;
741
742 /* "prepend" any local alias records in the answer section if this
743 * response is supposed to be authoritative. Currently it should
744 * be a single CNAME record (sanity-checked in worker_handle_request())
745 * but it can be extended if and when we support more variations of
746 * aliases. */
747 if(qinfo->local_alias && (flags & BIT_AA)) {
748 struct reply_info arep;
749 time_t timezero = 0; /* to use the 'authoritative' TTL */
750 memset(&arep, 0, sizeof(arep));
751 arep.flags = rep->flags;
752 arep.an_numrrsets = 1;
753 arep.rrset_count = 1;
754 arep.rrsets = &qinfo->local_alias->rrset;
755 if((r=insert_section(&arep, 1, &ancount, buffer, 0,
756 timezero, region, &tree, LDNS_SECTION_ANSWER,
757 qinfo->qtype, dnssec, rr_offset, &compress_count)) != RETVAL_OK) {
758 if(r == RETVAL_TRUNC) {
759 /* create truncated message */
760 sldns_buffer_write_u16_at(buffer, 6, ancount);
761 LDNS_TC_SET(sldns_buffer_begin(buffer));
762 sldns_buffer_flip(buffer);
763 return 1;
764 }
765 return 0;
766 }
767 }
768
769 /* insert answer section */
770 if((r=insert_section(rep, rep->an_numrrsets, &ancount, buffer,
771 0, timenow, region, &tree, LDNS_SECTION_ANSWER, qinfo->qtype,
772 dnssec, rr_offset, &compress_count)) != RETVAL_OK) {
773 if(r == RETVAL_TRUNC) {
774 /* create truncated message */
775 sldns_buffer_write_u16_at(buffer, 6, ancount);
776 LDNS_TC_SET(sldns_buffer_begin(buffer));
777 sldns_buffer_flip(buffer);
778 return 1;
779 }
780 return 0;
781 }
782 sldns_buffer_write_u16_at(buffer, 6, ancount);
783
784 /* if response is positive answer, auth/add sections are not required */
785 if( ! (minimise && positive_answer(rep, qinfo->qtype)) ) {
786 /* insert auth section */
787 if((r=insert_section(rep, rep->ns_numrrsets, &nscount, buffer,
788 rep->an_numrrsets, timenow, region, &tree,
789 LDNS_SECTION_AUTHORITY, qinfo->qtype,
790 dnssec, rr_offset, &compress_count)) != RETVAL_OK) {
791 if(r == RETVAL_TRUNC) {
792 /* create truncated message */
793 sldns_buffer_write_u16_at(buffer, 8, nscount);
794 LDNS_TC_SET(sldns_buffer_begin(buffer));
795 sldns_buffer_flip(buffer);
796 return 1;
797 }
798 return 0;
799 }
800 sldns_buffer_write_u16_at(buffer, 8, nscount);
801
802 if(! (minimise && negative_answer(rep))) {
803 /* insert add section */
804 if((r=insert_section(rep, rep->ar_numrrsets, &arcount, buffer,
805 rep->an_numrrsets + rep->ns_numrrsets, timenow, region,
806 &tree, LDNS_SECTION_ADDITIONAL, qinfo->qtype,
807 dnssec, rr_offset, &compress_count)) != RETVAL_OK) {
808 if(r == RETVAL_TRUNC) {
809 /* no need to set TC bit, this is the additional */
810 sldns_buffer_write_u16_at(buffer, 10, arcount);
811 sldns_buffer_flip(buffer);
812 return 1;
813 }
814 return 0;
815 }
816 sldns_buffer_write_u16_at(buffer, 10, arcount);
817 }
818 }
819 sldns_buffer_flip(buffer);
820 return 1;
821 }
822
823 size_t
calc_edns_field_size(struct edns_data * edns)824 calc_edns_field_size(struct edns_data* edns)
825 {
826 size_t rdatalen = 0;
827 struct edns_option* opt;
828 if(!edns || !edns->edns_present)
829 return 0;
830 for(opt = edns->opt_list_inplace_cb_out; opt; opt = opt->next) {
831 rdatalen += 4 + opt->opt_len;
832 }
833 for(opt = edns->opt_list_out; opt; opt = opt->next) {
834 rdatalen += 4 + opt->opt_len;
835 }
836 /* domain root '.' + type + class + ttl + rdatalen */
837 return 1 + 2 + 2 + 4 + 2 + rdatalen;
838 }
839
840 uint16_t
calc_edns_option_size(struct edns_data * edns,uint16_t code)841 calc_edns_option_size(struct edns_data* edns, uint16_t code)
842 {
843 size_t rdatalen = 0;
844 struct edns_option* opt;
845 if(!edns || !edns->edns_present)
846 return 0;
847 for(opt = edns->opt_list_inplace_cb_out; opt; opt = opt->next) {
848 if(opt->opt_code == code)
849 rdatalen += 4 + opt->opt_len;
850 }
851 for(opt = edns->opt_list_out; opt; opt = opt->next) {
852 if(opt->opt_code == code)
853 rdatalen += 4 + opt->opt_len;
854 }
855 return rdatalen;
856 }
857
858 uint16_t
calc_ede_option_size(struct edns_data * edns,size_t * txt_size)859 calc_ede_option_size(struct edns_data* edns, size_t* txt_size)
860 {
861 size_t rdatalen = 0;
862 struct edns_option* opt;
863 *txt_size = 0;
864 if(!edns || !edns->edns_present)
865 return 0;
866 for(opt = edns->opt_list_inplace_cb_out; opt; opt = opt->next) {
867 if(opt->opt_code == LDNS_EDNS_EDE) {
868 rdatalen += 4 + opt->opt_len;
869 if(opt->opt_len > 2) *txt_size += opt->opt_len - 2;
870 if(opt->opt_len >= 2 && sldns_read_uint16(
871 opt->opt_data) == LDNS_EDE_OTHER) {
872 *txt_size += 4 + 2;
873 }
874 }
875 }
876 for(opt = edns->opt_list_out; opt; opt = opt->next) {
877 if(opt->opt_code == LDNS_EDNS_EDE) {
878 rdatalen += 4 + opt->opt_len;
879 if(opt->opt_len > 2) *txt_size += opt->opt_len - 2;
880 if(opt->opt_len >= 2 && sldns_read_uint16(
881 opt->opt_data) == LDNS_EDE_OTHER) {
882 *txt_size += 4 + 2;
883 }
884 }
885 }
886 return rdatalen;
887 }
888
889 /* Trims the EDE OPTION-DATA to not include any EXTRA-TEXT data.
890 * Also removes any LDNS_EDE_OTHER options from the list since they are useless
891 * without the extra text. */
892 static void
ede_trim_text(struct edns_option ** list)893 ede_trim_text(struct edns_option** list)
894 {
895 struct edns_option* curr, *prev = NULL;
896 if(!list || !(*list)) return;
897 /* Unlink and repoint if LDNS_EDE_OTHER are first in list */
898 while(list && *list && (*list)->opt_code == LDNS_EDNS_EDE
899 && (*list)->opt_len >= 2
900 && sldns_read_uint16((*list)->opt_data) == LDNS_EDE_OTHER ) {
901 *list = (*list)->next;
902 }
903 if(!list || !(*list)) return;
904 curr = *list;
905 while(curr) {
906 if(curr->opt_code == LDNS_EDNS_EDE) {
907 if(curr->opt_len >= 2 && sldns_read_uint16(
908 curr->opt_data) == LDNS_EDE_OTHER) {
909 /* LDNS_EDE_OTHER cannot be the first option in
910 * this while, so prev is always initialized at
911 * this point from the other branches;
912 * cut this option off */
913 prev->next = curr->next;
914 curr = curr->next;
915 } else if(curr->opt_len > 2) {
916 /* trim this option's EXTRA-TEXT */
917 curr->opt_len = 2;
918 prev = curr;
919 curr = curr->next;
920 } else {
921 prev = curr;
922 curr = curr->next;
923 }
924 } else {
925 /* continue */
926 prev = curr;
927 curr = curr->next;
928 }
929 }
930 }
931
932 static void
attach_edns_record_max_msg_sz(sldns_buffer * pkt,struct edns_data * edns,uint16_t max_msg_sz)933 attach_edns_record_max_msg_sz(sldns_buffer* pkt, struct edns_data* edns,
934 uint16_t max_msg_sz)
935 {
936 size_t len;
937 size_t rdatapos;
938 struct edns_option* opt;
939 struct edns_option* padding_option = NULL;
940 /* inc additional count */
941 sldns_buffer_write_u16_at(pkt, 10,
942 sldns_buffer_read_u16_at(pkt, 10) + 1);
943 len = sldns_buffer_limit(pkt);
944 sldns_buffer_clear(pkt);
945 sldns_buffer_set_position(pkt, len);
946 /* write EDNS record */
947 sldns_buffer_write_u8(pkt, 0); /* '.' label */
948 sldns_buffer_write_u16(pkt, LDNS_RR_TYPE_OPT); /* type */
949 sldns_buffer_write_u16(pkt, edns->udp_size); /* class */
950 sldns_buffer_write_u8(pkt, edns->ext_rcode); /* ttl */
951 sldns_buffer_write_u8(pkt, edns->edns_version);
952 sldns_buffer_write_u16(pkt, edns->bits);
953 rdatapos = sldns_buffer_position(pkt);
954 sldns_buffer_write_u16(pkt, 0); /* rdatalen */
955 /* write rdata */
956 for(opt=edns->opt_list_inplace_cb_out; opt; opt=opt->next) {
957 if (opt->opt_code == LDNS_EDNS_PADDING) {
958 padding_option = opt;
959 continue;
960 }
961 if(sldns_buffer_position(pkt) + opt->opt_len + 4 > max_msg_sz)
962 break; /* no space for it */
963 if(!sldns_buffer_available(pkt, 4 + opt->opt_len))
964 break;
965 sldns_buffer_write_u16(pkt, opt->opt_code);
966 sldns_buffer_write_u16(pkt, opt->opt_len);
967 if(opt->opt_len != 0)
968 sldns_buffer_write(pkt, opt->opt_data, opt->opt_len);
969 }
970 for(opt=edns->opt_list_out; opt; opt=opt->next) {
971 if (opt->opt_code == LDNS_EDNS_PADDING) {
972 padding_option = opt;
973 continue;
974 }
975 if(sldns_buffer_position(pkt) + opt->opt_len + 4 > max_msg_sz)
976 break; /* no space for it */
977 if(!sldns_buffer_available(pkt, 4 + opt->opt_len))
978 break;
979 sldns_buffer_write_u16(pkt, opt->opt_code);
980 sldns_buffer_write_u16(pkt, opt->opt_len);
981 if(opt->opt_len != 0)
982 sldns_buffer_write(pkt, opt->opt_data, opt->opt_len);
983 }
984 if (padding_option && edns->padding_block_size &&
985 sldns_buffer_position(pkt)+4 <= max_msg_sz &&
986 sldns_buffer_available(pkt, 4) /* if there is space for it */) {
987 size_t pad_pos = sldns_buffer_position(pkt);
988 size_t msg_sz = ((pad_pos + 3) / edns->padding_block_size + 1)
989 * edns->padding_block_size;
990 size_t pad_sz;
991
992 if (msg_sz > max_msg_sz)
993 msg_sz = max_msg_sz;
994
995 /* By use of calc_edns_field_size, calling functions should
996 * have made sure that there is enough space for at least a
997 * zero sized padding option.
998 */
999 log_assert(pad_pos + 4 <= msg_sz);
1000
1001 pad_sz = msg_sz - pad_pos - 4;
1002 sldns_buffer_write_u16(pkt, LDNS_EDNS_PADDING);
1003 sldns_buffer_write_u16(pkt, pad_sz);
1004 if (pad_sz) {
1005 memset(sldns_buffer_current(pkt), 0, pad_sz);
1006 sldns_buffer_skip(pkt, pad_sz);
1007 }
1008 }
1009 sldns_buffer_write_u16_at(pkt, rdatapos,
1010 sldns_buffer_position(pkt)-rdatapos-2);
1011 sldns_buffer_flip(pkt);
1012 }
1013
1014 void
attach_edns_record(sldns_buffer * pkt,struct edns_data * edns)1015 attach_edns_record(sldns_buffer* pkt, struct edns_data* edns)
1016 {
1017 if(!edns || !edns->edns_present)
1018 return;
1019 attach_edns_record_max_msg_sz(pkt, edns, edns->udp_size);
1020 }
1021
1022 int
reply_info_answer_encode(struct query_info * qinf,struct reply_info * rep,uint16_t id,uint16_t qflags,sldns_buffer * pkt,time_t timenow,int cached,struct regional * region,uint16_t udpsize,struct edns_data * edns,int dnssec,int secure)1023 reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep,
1024 uint16_t id, uint16_t qflags, sldns_buffer* pkt, time_t timenow,
1025 int cached, struct regional* region, uint16_t udpsize,
1026 struct edns_data* edns, int dnssec, int secure)
1027 {
1028 uint16_t flags;
1029 unsigned int attach_edns = 0;
1030 size_t edns_field_size, ede_size, ede_txt_size;
1031
1032 if(!cached || rep->authoritative) {
1033 /* original flags, copy RD and CD bits from query. */
1034 flags = rep->flags | (qflags & (BIT_RD|BIT_CD));
1035 } else {
1036 /* remove AA bit, copy RD and CD bits from query. */
1037 flags = (rep->flags & ~BIT_AA) | (qflags & (BIT_RD|BIT_CD));
1038 }
1039 if(secure && (dnssec || (qflags&BIT_AD)))
1040 flags |= BIT_AD;
1041 /* restore AA bit if we have a local alias and the response can be
1042 * authoritative. Also clear AD bit if set as the local data is the
1043 * primary answer. */
1044 if(qinf->local_alias &&
1045 (FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NOERROR ||
1046 FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NXDOMAIN)) {
1047 flags |= BIT_AA;
1048 flags &= ~BIT_AD;
1049 }
1050 log_assert((flags & BIT_QR)); /* QR bit must be on in our replies */
1051 if(udpsize < LDNS_HEADER_SIZE)
1052 return 0;
1053 /* currently edns does not change during calculations;
1054 * calculate sizes once here */
1055 edns_field_size = calc_edns_field_size(edns);
1056 ede_size = calc_ede_option_size(edns, &ede_txt_size);
1057 if(sldns_buffer_capacity(pkt) < (size_t)udpsize)
1058 udpsize = sldns_buffer_capacity(pkt);
1059 if(!edns || !edns->edns_present) {
1060 attach_edns = 0;
1061 /* EDEs are optional, try to fit anything else before them */
1062 } else if((size_t)udpsize < (size_t)LDNS_HEADER_SIZE + edns_field_size - ede_size) {
1063 /* packet too small to contain edns, omit it. */
1064 attach_edns = 0;
1065 } else {
1066 /* reserve space for edns record */
1067 attach_edns = (unsigned int)edns_field_size - ede_size;
1068 }
1069
1070 if(!reply_info_encode(qinf, rep, id, flags, pkt, timenow, region,
1071 udpsize - attach_edns, dnssec, MINIMAL_RESPONSES)) {
1072 log_err("reply encode: out of memory");
1073 return 0;
1074 }
1075 if(attach_edns) {
1076 if((size_t)udpsize >= sldns_buffer_limit(pkt) + edns_field_size)
1077 attach_edns_record_max_msg_sz(pkt, edns, udpsize);
1078 else if((size_t)udpsize >= sldns_buffer_limit(pkt) + edns_field_size - ede_txt_size) {
1079 ede_trim_text(&edns->opt_list_inplace_cb_out);
1080 ede_trim_text(&edns->opt_list_out);
1081 attach_edns_record_max_msg_sz(pkt, edns, udpsize);
1082 } else if((size_t)udpsize >= sldns_buffer_limit(pkt) + edns_field_size - ede_size) {
1083 edns_opt_list_remove(&edns->opt_list_inplace_cb_out, LDNS_EDNS_EDE);
1084 edns_opt_list_remove(&edns->opt_list_out, LDNS_EDNS_EDE);
1085 attach_edns_record_max_msg_sz(pkt, edns, udpsize);
1086 }
1087 }
1088 return 1;
1089 }
1090
1091 void
qinfo_query_encode(sldns_buffer * pkt,struct query_info * qinfo)1092 qinfo_query_encode(sldns_buffer* pkt, struct query_info* qinfo)
1093 {
1094 uint16_t flags = 0; /* QUERY, NOERROR */
1095 const uint8_t* qname = qinfo->local_alias ?
1096 qinfo->local_alias->rrset->rk.dname : qinfo->qname;
1097 size_t qname_len = qinfo->local_alias ?
1098 qinfo->local_alias->rrset->rk.dname_len : qinfo->qname_len;
1099 sldns_buffer_clear(pkt);
1100 log_assert(sldns_buffer_remaining(pkt) >= 12+255+4/*max query*/);
1101 sldns_buffer_skip(pkt, 2); /* id done later */
1102 sldns_buffer_write_u16(pkt, flags);
1103 sldns_buffer_write_u16(pkt, 1); /* query count */
1104 sldns_buffer_write(pkt, "\000\000\000\000\000\000", 6); /* counts */
1105 sldns_buffer_write(pkt, qname, qname_len);
1106 sldns_buffer_write_u16(pkt, qinfo->qtype);
1107 sldns_buffer_write_u16(pkt, qinfo->qclass);
1108 sldns_buffer_flip(pkt);
1109 }
1110
1111 void
extended_error_encode(sldns_buffer * buf,uint16_t rcode,struct query_info * qinfo,uint16_t qid,uint16_t qflags,uint16_t xflags,struct edns_data * edns)1112 extended_error_encode(sldns_buffer* buf, uint16_t rcode,
1113 struct query_info* qinfo, uint16_t qid, uint16_t qflags,
1114 uint16_t xflags, struct edns_data* edns)
1115 {
1116 uint16_t flags;
1117
1118 sldns_buffer_clear(buf);
1119 sldns_buffer_write(buf, &qid, sizeof(uint16_t));
1120 flags = (uint16_t)(BIT_QR | BIT_RA | (rcode & 0xF)); /* QR and retcode*/
1121 flags |= xflags;
1122 flags |= (qflags & (BIT_RD|BIT_CD)); /* copy RD and CD bit */
1123 sldns_buffer_write_u16(buf, flags);
1124 if(qinfo) flags = 1;
1125 else flags = 0;
1126 sldns_buffer_write_u16(buf, flags);
1127 flags = 0;
1128 sldns_buffer_write(buf, &flags, sizeof(uint16_t));
1129 sldns_buffer_write(buf, &flags, sizeof(uint16_t));
1130 sldns_buffer_write(buf, &flags, sizeof(uint16_t));
1131 if(qinfo) {
1132 const uint8_t* qname = qinfo->local_alias ?
1133 qinfo->local_alias->rrset->rk.dname : qinfo->qname;
1134 size_t qname_len = qinfo->local_alias ?
1135 qinfo->local_alias->rrset->rk.dname_len :
1136 qinfo->qname_len;
1137 if(sldns_buffer_current(buf) == qname)
1138 sldns_buffer_skip(buf, (ssize_t)qname_len);
1139 else sldns_buffer_write(buf, qname, qname_len);
1140 sldns_buffer_write_u16(buf, qinfo->qtype);
1141 sldns_buffer_write_u16(buf, qinfo->qclass);
1142 }
1143 sldns_buffer_flip(buf);
1144 if(edns && edns->edns_present) {
1145 size_t edns_field_size, ede_size, ede_txt_size;
1146 struct edns_data es = *edns;
1147 es.edns_version = EDNS_ADVERTISED_VERSION;
1148 es.udp_size = EDNS_ADVERTISED_SIZE;
1149 es.ext_rcode = (uint8_t)(rcode >> 4);
1150 es.bits &= EDNS_DO;
1151 /* EDEs are optional. If space is a concern try in order:
1152 * - removing any EXTRA-TEXT fields from explicit EDEs, or
1153 * - removing all EDEs,
1154 * to see if EDNS can fit. */
1155 edns_field_size = calc_edns_field_size(&es);
1156 ede_size = calc_ede_option_size(&es, &ede_txt_size);
1157 if((size_t)edns->udp_size >= sldns_buffer_limit(buf) + edns_field_size)
1158 attach_edns_record_max_msg_sz(buf, &es, edns->udp_size);
1159 else if((size_t)edns->udp_size >= sldns_buffer_limit(buf) + edns_field_size - ede_txt_size) {
1160 ede_trim_text(&es.opt_list_inplace_cb_out);
1161 ede_trim_text(&es.opt_list_out);
1162 attach_edns_record_max_msg_sz(buf, &es, edns->udp_size);
1163 } else if((size_t)edns->udp_size >= sldns_buffer_limit(buf) + edns_field_size - ede_size) {
1164 edns_opt_list_remove(&es.opt_list_inplace_cb_out, LDNS_EDNS_EDE);
1165 edns_opt_list_remove(&es.opt_list_out, LDNS_EDNS_EDE);
1166 attach_edns_record_max_msg_sz(buf, &es, edns->udp_size);
1167 }
1168 }
1169 }
1170
1171 void
error_encode(sldns_buffer * buf,int r,struct query_info * qinfo,uint16_t qid,uint16_t qflags,struct edns_data * edns)1172 error_encode(sldns_buffer* buf, int r, struct query_info* qinfo,
1173 uint16_t qid, uint16_t qflags, struct edns_data* edns)
1174 {
1175 extended_error_encode(buf, (r & 0x000F), qinfo, qid, qflags,
1176 (r & 0xFFF0), edns);
1177 }
1178