xref: /openbsd-src/usr.sbin/unbound/util/data/msgencode.c (revision 3be9681c7fa1893f390466d4384a920c9214057a)
1933707f3Ssthen /*
2933707f3Ssthen  * util/data/msgencode.c - Encode DNS messages, queries and replies.
3933707f3Ssthen  *
4933707f3Ssthen  * Copyright (c) 2007, NLnet Labs. All rights reserved.
5933707f3Ssthen  *
6933707f3Ssthen  * This software is open source.
7933707f3Ssthen  *
8933707f3Ssthen  * Redistribution and use in source and binary forms, with or without
9933707f3Ssthen  * modification, are permitted provided that the following conditions
10933707f3Ssthen  * are met:
11933707f3Ssthen  *
12933707f3Ssthen  * Redistributions of source code must retain the above copyright notice,
13933707f3Ssthen  * this list of conditions and the following disclaimer.
14933707f3Ssthen  *
15933707f3Ssthen  * Redistributions in binary form must reproduce the above copyright notice,
16933707f3Ssthen  * this list of conditions and the following disclaimer in the documentation
17933707f3Ssthen  * and/or other materials provided with the distribution.
18933707f3Ssthen  *
19933707f3Ssthen  * Neither the name of the NLNET LABS nor the names of its contributors may
20933707f3Ssthen  * be used to endorse or promote products derived from this software without
21933707f3Ssthen  * specific prior written permission.
22933707f3Ssthen  *
23933707f3Ssthen  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
245d76a658Ssthen  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
255d76a658Ssthen  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
265d76a658Ssthen  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
275d76a658Ssthen  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
285d76a658Ssthen  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
295d76a658Ssthen  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
305d76a658Ssthen  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
315d76a658Ssthen  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
325d76a658Ssthen  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
335d76a658Ssthen  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34933707f3Ssthen  */
35933707f3Ssthen 
36933707f3Ssthen /**
37933707f3Ssthen  * \file
38933707f3Ssthen  *
39933707f3Ssthen  * This file contains a routines to encode DNS messages.
40933707f3Ssthen  */
41933707f3Ssthen 
42933707f3Ssthen #include "config.h"
43933707f3Ssthen #include "util/data/msgencode.h"
44933707f3Ssthen #include "util/data/msgreply.h"
45933707f3Ssthen #include "util/data/msgparse.h"
46933707f3Ssthen #include "util/data/dname.h"
47933707f3Ssthen #include "util/log.h"
48933707f3Ssthen #include "util/regional.h"
49933707f3Ssthen #include "util/net_help.h"
50fdfb4ba6Ssthen #include "sldns/sbuffer.h"
5177079be7Ssthen #include "services/localzone.h"
52933707f3Ssthen 
533150e5f6Ssthen #ifdef HAVE_TIME_H
543150e5f6Ssthen #include <time.h>
553150e5f6Ssthen #endif
563150e5f6Ssthen #include <sys/time.h>
573150e5f6Ssthen 
58933707f3Ssthen /** return code that means the function ran out of memory. negative so it does
59933707f3Ssthen  * not conflict with DNS rcodes. */
60933707f3Ssthen #define RETVAL_OUTMEM	-2
61933707f3Ssthen /** return code that means the data did not fit (completely) in the packet */
62933707f3Ssthen #define RETVAL_TRUNC	-4
63933707f3Ssthen /** return code that means all is peachy keen. Equal to DNS rcode NOERROR */
64933707f3Ssthen #define RETVAL_OK	0
65*3be9681cSsthen /** Max compressions we are willing to perform; more than that will result
66*3be9681cSsthen  *  in semi-compressed messages, or truncated even on TCP for huge messages, to
67*3be9681cSsthen  *  avoid locking the CPU for long */
68*3be9681cSsthen #define MAX_COMPRESSION_PER_MESSAGE 120
69933707f3Ssthen 
70933707f3Ssthen /**
71933707f3Ssthen  * Data structure to help domain name compression in outgoing messages.
72933707f3Ssthen  * A tree of dnames and their offsets in the packet is kept.
73933707f3Ssthen  * It is kept sorted, not canonical, but by label at least, so that after
74933707f3Ssthen  * a lookup of a name you know its closest match, and the parent from that
75933707f3Ssthen  * closest match. These are possible compression targets.
76933707f3Ssthen  *
77933707f3Ssthen  * It is a binary tree, not a rbtree or balanced tree, as the effort
78933707f3Ssthen  * of keeping it balanced probably outweighs usefulness (given typical
79933707f3Ssthen  * DNS packet size).
80933707f3Ssthen  */
81933707f3Ssthen struct compress_tree_node {
82933707f3Ssthen 	/** left node in tree, all smaller to this */
83933707f3Ssthen 	struct compress_tree_node* left;
84933707f3Ssthen 	/** right node in tree, all larger than this */
85933707f3Ssthen 	struct compress_tree_node* right;
86933707f3Ssthen 
87933707f3Ssthen 	/** the parent node - not for tree, but zone parent. One less label */
88933707f3Ssthen 	struct compress_tree_node* parent;
89933707f3Ssthen 	/** the domain name for this node. Pointer to uncompressed memory. */
90933707f3Ssthen 	uint8_t* dname;
91933707f3Ssthen 	/** number of labels in domain name, kept to help compare func. */
92933707f3Ssthen 	int labs;
93933707f3Ssthen 	/** offset in packet that points to this dname */
94933707f3Ssthen 	size_t offset;
95933707f3Ssthen };
96933707f3Ssthen 
97933707f3Ssthen /**
98933707f3Ssthen  * Find domain name in tree, returns exact and closest match.
99933707f3Ssthen  * @param tree: root of tree.
100933707f3Ssthen  * @param dname: pointer to uncompressed dname.
101933707f3Ssthen  * @param labs: number of labels in domain name.
102933707f3Ssthen  * @param match: closest or exact match.
103933707f3Ssthen  *	guaranteed to be smaller or equal to the sought dname.
104933707f3Ssthen  *	can be null if the tree is empty.
105933707f3Ssthen  * @param matchlabels: number of labels that match with closest match.
106933707f3Ssthen  *	can be zero is there is no match.
107933707f3Ssthen  * @param insertpt: insert location for dname, if not found.
108933707f3Ssthen  * @return: 0 if no exact match.
109933707f3Ssthen  */
110933707f3Ssthen static int
111933707f3Ssthen compress_tree_search(struct compress_tree_node** tree, uint8_t* dname,
112933707f3Ssthen 	int labs, struct compress_tree_node** match, int* matchlabels,
113933707f3Ssthen 	struct compress_tree_node*** insertpt)
114933707f3Ssthen {
115933707f3Ssthen 	int c, n, closen=0;
116933707f3Ssthen 	struct compress_tree_node* p = *tree;
117933707f3Ssthen 	struct compress_tree_node* close = 0;
118933707f3Ssthen 	struct compress_tree_node** prev = tree;
119933707f3Ssthen 	while(p) {
120933707f3Ssthen 		if((c = dname_lab_cmp(dname, labs, p->dname, p->labs, &n))
121933707f3Ssthen 			== 0) {
122933707f3Ssthen 			*matchlabels = n;
123933707f3Ssthen 			*match = p;
124933707f3Ssthen 			return 1;
125933707f3Ssthen 		}
126933707f3Ssthen 		if(c<0) {
127933707f3Ssthen 			prev = &p->left;
128933707f3Ssthen 			p = p->left;
129933707f3Ssthen 		} else	{
130933707f3Ssthen 			closen = n;
131933707f3Ssthen 			close = p; /* p->dname is smaller than dname */
132933707f3Ssthen 			prev = &p->right;
133933707f3Ssthen 			p = p->right;
134933707f3Ssthen 		}
135933707f3Ssthen 	}
136933707f3Ssthen 	*insertpt = prev;
137933707f3Ssthen 	*matchlabels = closen;
138933707f3Ssthen 	*match = close;
139933707f3Ssthen 	return 0;
140933707f3Ssthen }
141933707f3Ssthen 
142933707f3Ssthen /**
143933707f3Ssthen  * Lookup a domain name in compression tree.
144933707f3Ssthen  * @param tree: root of tree (not the node with '.').
145933707f3Ssthen  * @param dname: pointer to uncompressed dname.
146933707f3Ssthen  * @param labs: number of labels in domain name.
147933707f3Ssthen  * @param insertpt: insert location for dname, if not found.
148933707f3Ssthen  * @return: 0 if not found or compress treenode with best compression.
149933707f3Ssthen  */
150933707f3Ssthen static struct compress_tree_node*
151933707f3Ssthen compress_tree_lookup(struct compress_tree_node** tree, uint8_t* dname,
152933707f3Ssthen 	int labs, struct compress_tree_node*** insertpt)
153933707f3Ssthen {
154933707f3Ssthen 	struct compress_tree_node* p;
155933707f3Ssthen 	int m;
156933707f3Ssthen 	if(labs <= 1)
157933707f3Ssthen 		return 0; /* do not compress root node */
158933707f3Ssthen 	if(compress_tree_search(tree, dname, labs, &p, &m, insertpt)) {
159933707f3Ssthen 		/* exact match */
160933707f3Ssthen 		return p;
161933707f3Ssthen 	}
162933707f3Ssthen 	/* return some ancestor of p that compresses well. */
163933707f3Ssthen 	if(m>1) {
164933707f3Ssthen 		/* www.example.com. (labs=4) matched foo.example.com.(labs=4)
165933707f3Ssthen 		 * then matchcount = 3. need to go up. */
166933707f3Ssthen 		while(p && p->labs > m)
167933707f3Ssthen 			p = p->parent;
168933707f3Ssthen 		return p;
169933707f3Ssthen 	}
170933707f3Ssthen 	return 0;
171933707f3Ssthen }
172933707f3Ssthen 
173933707f3Ssthen /**
174933707f3Ssthen  * Create node for domain name compression tree.
175933707f3Ssthen  * @param dname: pointer to uncompressed dname (stored in tree).
176933707f3Ssthen  * @param labs: number of labels in dname.
177933707f3Ssthen  * @param offset: offset into packet for dname.
178933707f3Ssthen  * @param region: how to allocate memory for new node.
179933707f3Ssthen  * @return new node or 0 on malloc failure.
180933707f3Ssthen  */
181933707f3Ssthen static struct compress_tree_node*
182933707f3Ssthen compress_tree_newnode(uint8_t* dname, int labs, size_t offset,
183933707f3Ssthen 	struct regional* region)
184933707f3Ssthen {
185933707f3Ssthen 	struct compress_tree_node* n = (struct compress_tree_node*)
186933707f3Ssthen 		regional_alloc(region, sizeof(struct compress_tree_node));
187933707f3Ssthen 	if(!n) return 0;
188933707f3Ssthen 	n->left = 0;
189933707f3Ssthen 	n->right = 0;
190933707f3Ssthen 	n->parent = 0;
191933707f3Ssthen 	n->dname = dname;
192933707f3Ssthen 	n->labs = labs;
193933707f3Ssthen 	n->offset = offset;
194933707f3Ssthen 	return n;
195933707f3Ssthen }
196933707f3Ssthen 
197933707f3Ssthen /**
198933707f3Ssthen  * Store domain name and ancestors into compression tree.
199933707f3Ssthen  * @param dname: pointer to uncompressed dname (stored in tree).
200933707f3Ssthen  * @param labs: number of labels in dname.
201933707f3Ssthen  * @param offset: offset into packet for dname.
202933707f3Ssthen  * @param region: how to allocate memory for new node.
203933707f3Ssthen  * @param closest: match from previous lookup, used to compress dname.
204933707f3Ssthen  *	may be NULL if no previous match.
205933707f3Ssthen  *	if the tree has an ancestor of dname already, this must be it.
206933707f3Ssthen  * @param insertpt: where to insert the dname in tree.
207933707f3Ssthen  * @return: 0 on memory error.
208933707f3Ssthen  */
209933707f3Ssthen static int
210933707f3Ssthen compress_tree_store(uint8_t* dname, int labs, size_t offset,
211933707f3Ssthen 	struct regional* region, struct compress_tree_node* closest,
212933707f3Ssthen 	struct compress_tree_node** insertpt)
213933707f3Ssthen {
214933707f3Ssthen 	uint8_t lablen;
215933707f3Ssthen 	struct compress_tree_node* newnode;
216933707f3Ssthen 	struct compress_tree_node* prevnode = NULL;
217933707f3Ssthen 	int uplabs = labs-1; /* does not store root in tree */
218933707f3Ssthen 	if(closest) uplabs = labs - closest->labs;
219933707f3Ssthen 	log_assert(uplabs >= 0);
220933707f3Ssthen 	/* algorithms builds up a vine of dname-labels to hang into tree */
221933707f3Ssthen 	while(uplabs--) {
222933707f3Ssthen 		if(offset > PTR_MAX_OFFSET) {
223933707f3Ssthen 			/* insertion failed, drop vine */
224933707f3Ssthen 			return 1; /* compression pointer no longer useful */
225933707f3Ssthen 		}
226933707f3Ssthen 		if(!(newnode = compress_tree_newnode(dname, labs, offset,
227933707f3Ssthen 			region))) {
228933707f3Ssthen 			/* insertion failed, drop vine */
229933707f3Ssthen 			return 0;
230933707f3Ssthen 		}
231933707f3Ssthen 
232933707f3Ssthen 		if(prevnode) {
233933707f3Ssthen 			/* chain nodes together, last one has one label more,
234933707f3Ssthen 			 * so is larger than newnode, thus goes right. */
235933707f3Ssthen 			newnode->right = prevnode;
236933707f3Ssthen 			prevnode->parent = newnode;
237933707f3Ssthen 		}
238933707f3Ssthen 
239933707f3Ssthen 		/* next label */
240933707f3Ssthen 		lablen = *dname++;
241933707f3Ssthen 		dname += lablen;
242933707f3Ssthen 		offset += lablen+1;
243933707f3Ssthen 		prevnode = newnode;
244933707f3Ssthen 		labs--;
245933707f3Ssthen 	}
246933707f3Ssthen 	/* if we have a vine, hang the vine into the tree */
247933707f3Ssthen 	if(prevnode) {
248933707f3Ssthen 		*insertpt = prevnode;
249933707f3Ssthen 		prevnode->parent = closest;
250933707f3Ssthen 	}
251933707f3Ssthen 	return 1;
252933707f3Ssthen }
253933707f3Ssthen 
254933707f3Ssthen /** compress a domain name */
255933707f3Ssthen static int
2565d76a658Ssthen write_compressed_dname(sldns_buffer* pkt, uint8_t* dname, int labs,
257933707f3Ssthen 	struct compress_tree_node* p)
258933707f3Ssthen {
259933707f3Ssthen 	/* compress it */
260933707f3Ssthen 	int labcopy = labs - p->labs;
261933707f3Ssthen 	uint8_t lablen;
262933707f3Ssthen 	uint16_t ptr;
263933707f3Ssthen 
264933707f3Ssthen 	if(labs == 1) {
265933707f3Ssthen 		/* write root label */
2665d76a658Ssthen 		if(sldns_buffer_remaining(pkt) < 1)
267933707f3Ssthen 			return 0;
2685d76a658Ssthen 		sldns_buffer_write_u8(pkt, 0);
269933707f3Ssthen 		return 1;
270933707f3Ssthen 	}
271933707f3Ssthen 
272933707f3Ssthen 	/* copy the first couple of labels */
273933707f3Ssthen 	while(labcopy--) {
274933707f3Ssthen 		lablen = *dname++;
2755d76a658Ssthen 		if(sldns_buffer_remaining(pkt) < (size_t)lablen+1)
276933707f3Ssthen 			return 0;
2775d76a658Ssthen 		sldns_buffer_write_u8(pkt, lablen);
2785d76a658Ssthen 		sldns_buffer_write(pkt, dname, lablen);
279933707f3Ssthen 		dname += lablen;
280933707f3Ssthen 	}
281933707f3Ssthen 	/* insert compression ptr */
2825d76a658Ssthen 	if(sldns_buffer_remaining(pkt) < 2)
283933707f3Ssthen 		return 0;
284933707f3Ssthen 	ptr = PTR_CREATE(p->offset);
2855d76a658Ssthen 	sldns_buffer_write_u16(pkt, ptr);
286933707f3Ssthen 	return 1;
287933707f3Ssthen }
288933707f3Ssthen 
289933707f3Ssthen /** compress owner name of RR, return RETVAL_OUTMEM RETVAL_TRUNC */
290933707f3Ssthen static int
2915d76a658Ssthen compress_owner(struct ub_packed_rrset_key* key, sldns_buffer* pkt,
292933707f3Ssthen 	struct regional* region, struct compress_tree_node** tree,
293*3be9681cSsthen 	size_t owner_pos, uint16_t* owner_ptr, int owner_labs,
294*3be9681cSsthen 	size_t* compress_count)
295933707f3Ssthen {
296933707f3Ssthen 	struct compress_tree_node* p;
297a961b961Ssthen 	struct compress_tree_node** insertpt = NULL;
298933707f3Ssthen 	if(!*owner_ptr) {
299933707f3Ssthen 		/* compress first time dname */
300*3be9681cSsthen 		if(*compress_count < MAX_COMPRESSION_PER_MESSAGE &&
301*3be9681cSsthen 			(p = compress_tree_lookup(tree, key->rk.dname,
302933707f3Ssthen 			owner_labs, &insertpt))) {
303933707f3Ssthen 			if(p->labs == owner_labs)
304933707f3Ssthen 				/* avoid ptr chains, since some software is
305933707f3Ssthen 				 * not capable of decoding ptr after a ptr. */
306933707f3Ssthen 				*owner_ptr = htons(PTR_CREATE(p->offset));
307933707f3Ssthen 			if(!write_compressed_dname(pkt, key->rk.dname,
308933707f3Ssthen 				owner_labs, p))
309933707f3Ssthen 				return RETVAL_TRUNC;
310*3be9681cSsthen 			(*compress_count)++;
311933707f3Ssthen 			/* check if typeclass+4 ttl + rdatalen is available */
3125d76a658Ssthen 			if(sldns_buffer_remaining(pkt) < 4+4+2)
313933707f3Ssthen 				return RETVAL_TRUNC;
314933707f3Ssthen 		} else {
315933707f3Ssthen 			/* no compress */
3165d76a658Ssthen 			if(sldns_buffer_remaining(pkt) < key->rk.dname_len+4+4+2)
317933707f3Ssthen 				return RETVAL_TRUNC;
3185d76a658Ssthen 			sldns_buffer_write(pkt, key->rk.dname,
319933707f3Ssthen 				key->rk.dname_len);
320933707f3Ssthen 			if(owner_pos <= PTR_MAX_OFFSET)
321933707f3Ssthen 				*owner_ptr = htons(PTR_CREATE(owner_pos));
322933707f3Ssthen 		}
323*3be9681cSsthen 		if(*compress_count < MAX_COMPRESSION_PER_MESSAGE &&
324*3be9681cSsthen 			!compress_tree_store(key->rk.dname, owner_labs,
325933707f3Ssthen 			owner_pos, region, p, insertpt))
326933707f3Ssthen 			return RETVAL_OUTMEM;
327933707f3Ssthen 	} else {
328933707f3Ssthen 		/* always compress 2nd-further RRs in RRset */
329933707f3Ssthen 		if(owner_labs == 1) {
3305d76a658Ssthen 			if(sldns_buffer_remaining(pkt) < 1+4+4+2)
331933707f3Ssthen 				return RETVAL_TRUNC;
3325d76a658Ssthen 			sldns_buffer_write_u8(pkt, 0);
333933707f3Ssthen 		} else {
3345d76a658Ssthen 			if(sldns_buffer_remaining(pkt) < 2+4+4+2)
335933707f3Ssthen 				return RETVAL_TRUNC;
3365d76a658Ssthen 			sldns_buffer_write(pkt, owner_ptr, 2);
337933707f3Ssthen 		}
338933707f3Ssthen 	}
339933707f3Ssthen 	return RETVAL_OK;
340933707f3Ssthen }
341933707f3Ssthen 
342933707f3Ssthen /** compress any domain name to the packet, return RETVAL_* */
343933707f3Ssthen static int
3445d76a658Ssthen compress_any_dname(uint8_t* dname, sldns_buffer* pkt, int labs,
345*3be9681cSsthen 	struct regional* region, struct compress_tree_node** tree,
346*3be9681cSsthen 	size_t* compress_count)
347933707f3Ssthen {
348933707f3Ssthen 	struct compress_tree_node* p;
349933707f3Ssthen 	struct compress_tree_node** insertpt = NULL;
3505d76a658Ssthen 	size_t pos = sldns_buffer_position(pkt);
351*3be9681cSsthen 	if(*compress_count < MAX_COMPRESSION_PER_MESSAGE &&
352*3be9681cSsthen 		(p = compress_tree_lookup(tree, dname, labs, &insertpt))) {
353933707f3Ssthen 		if(!write_compressed_dname(pkt, dname, labs, p))
354933707f3Ssthen 			return RETVAL_TRUNC;
355*3be9681cSsthen 		(*compress_count)++;
356933707f3Ssthen 	} else {
357933707f3Ssthen 		if(!dname_buffer_write(pkt, dname))
358933707f3Ssthen 			return RETVAL_TRUNC;
359933707f3Ssthen 	}
360*3be9681cSsthen 	if(*compress_count < MAX_COMPRESSION_PER_MESSAGE &&
361*3be9681cSsthen 		!compress_tree_store(dname, labs, pos, region, p, insertpt))
362933707f3Ssthen 		return RETVAL_OUTMEM;
363933707f3Ssthen 	return RETVAL_OK;
364933707f3Ssthen }
365933707f3Ssthen 
366933707f3Ssthen /** return true if type needs domain name compression in rdata */
3675d76a658Ssthen static const sldns_rr_descriptor*
368933707f3Ssthen type_rdata_compressable(struct ub_packed_rrset_key* key)
369933707f3Ssthen {
370933707f3Ssthen 	uint16_t t = ntohs(key->rk.type);
3715d76a658Ssthen 	if(sldns_rr_descript(t) &&
3725d76a658Ssthen 		sldns_rr_descript(t)->_compress == LDNS_RR_COMPRESS)
3735d76a658Ssthen 		return sldns_rr_descript(t);
374933707f3Ssthen 	return 0;
375933707f3Ssthen }
376933707f3Ssthen 
377933707f3Ssthen /** compress domain names in rdata, return RETVAL_* */
378933707f3Ssthen static int
3795d76a658Ssthen compress_rdata(sldns_buffer* pkt, uint8_t* rdata, size_t todolen,
380933707f3Ssthen 	struct regional* region, struct compress_tree_node** tree,
381*3be9681cSsthen 	const sldns_rr_descriptor* desc, size_t* compress_count)
382933707f3Ssthen {
383933707f3Ssthen 	int labs, r, rdf = 0;
3845d76a658Ssthen 	size_t dname_len, len, pos = sldns_buffer_position(pkt);
385933707f3Ssthen 	uint8_t count = desc->_dname_count;
386933707f3Ssthen 
3875d76a658Ssthen 	sldns_buffer_skip(pkt, 2); /* rdata len fill in later */
388933707f3Ssthen 	/* space for rdatalen checked for already */
389933707f3Ssthen 	rdata += 2;
390933707f3Ssthen 	todolen -= 2;
391933707f3Ssthen 	while(todolen > 0 && count) {
392933707f3Ssthen 		switch(desc->_wireformat[rdf]) {
393933707f3Ssthen 		case LDNS_RDF_TYPE_DNAME:
394933707f3Ssthen 			labs = dname_count_size_labels(rdata, &dname_len);
395933707f3Ssthen 			if((r=compress_any_dname(rdata, pkt, labs, region,
396*3be9681cSsthen 				tree, compress_count)) != RETVAL_OK)
397933707f3Ssthen 				return r;
398933707f3Ssthen 			rdata += dname_len;
399933707f3Ssthen 			todolen -= dname_len;
400933707f3Ssthen 			count--;
401933707f3Ssthen 			len = 0;
402933707f3Ssthen 			break;
403933707f3Ssthen 		case LDNS_RDF_TYPE_STR:
404933707f3Ssthen 			len = *rdata + 1;
405933707f3Ssthen 			break;
406933707f3Ssthen 		default:
407933707f3Ssthen 			len = get_rdf_size(desc->_wireformat[rdf]);
408933707f3Ssthen 		}
409933707f3Ssthen 		if(len) {
410933707f3Ssthen 			/* copy over */
4115d76a658Ssthen 			if(sldns_buffer_remaining(pkt) < len)
412933707f3Ssthen 				return RETVAL_TRUNC;
4135d76a658Ssthen 			sldns_buffer_write(pkt, rdata, len);
414933707f3Ssthen 			todolen -= len;
415933707f3Ssthen 			rdata += len;
416933707f3Ssthen 		}
417933707f3Ssthen 		rdf++;
418933707f3Ssthen 	}
419933707f3Ssthen 	/* copy remainder */
420933707f3Ssthen 	if(todolen > 0) {
4215d76a658Ssthen 		if(sldns_buffer_remaining(pkt) < todolen)
422933707f3Ssthen 			return RETVAL_TRUNC;
4235d76a658Ssthen 		sldns_buffer_write(pkt, rdata, todolen);
424933707f3Ssthen 	}
425933707f3Ssthen 
426933707f3Ssthen 	/* set rdata len */
4275d76a658Ssthen 	sldns_buffer_write_u16_at(pkt, pos, sldns_buffer_position(pkt)-pos-2);
428933707f3Ssthen 	return RETVAL_OK;
429933707f3Ssthen }
430933707f3Ssthen 
431933707f3Ssthen /** Returns true if RR type should be included */
432933707f3Ssthen static int
4335d76a658Ssthen rrset_belongs_in_reply(sldns_pkt_section s, uint16_t rrtype, uint16_t qtype,
434933707f3Ssthen 	int dnssec)
435933707f3Ssthen {
436933707f3Ssthen 	if(dnssec)
437933707f3Ssthen 		return 1;
438933707f3Ssthen 	/* skip non DNSSEC types, except if directly queried for */
439933707f3Ssthen 	if(s == LDNS_SECTION_ANSWER) {
440933707f3Ssthen 		if(qtype == LDNS_RR_TYPE_ANY || qtype == rrtype)
441933707f3Ssthen 			return 1;
442933707f3Ssthen 	}
443933707f3Ssthen 	/* check DNSSEC-ness */
444933707f3Ssthen 	switch(rrtype) {
445933707f3Ssthen 		case LDNS_RR_TYPE_SIG:
446933707f3Ssthen 		case LDNS_RR_TYPE_KEY:
447933707f3Ssthen 		case LDNS_RR_TYPE_NXT:
448933707f3Ssthen 		case LDNS_RR_TYPE_DS:
449933707f3Ssthen 		case LDNS_RR_TYPE_RRSIG:
450933707f3Ssthen 		case LDNS_RR_TYPE_NSEC:
451933707f3Ssthen 		case LDNS_RR_TYPE_DNSKEY:
452933707f3Ssthen 		case LDNS_RR_TYPE_NSEC3:
453933707f3Ssthen 		case LDNS_RR_TYPE_NSEC3PARAMS:
454933707f3Ssthen 			return 0;
455933707f3Ssthen 	}
456933707f3Ssthen 	return 1;
457933707f3Ssthen }
458933707f3Ssthen 
459933707f3Ssthen /** store rrset in buffer in wireformat, return RETVAL_* */
460933707f3Ssthen static int
4615d76a658Ssthen packed_rrset_encode(struct ub_packed_rrset_key* key, sldns_buffer* pkt,
462229e174cSsthen 	uint16_t* num_rrs, time_t timenow, struct regional* region,
463933707f3Ssthen 	int do_data, int do_sig, struct compress_tree_node** tree,
464*3be9681cSsthen 	sldns_pkt_section s, uint16_t qtype, int dnssec, size_t rr_offset,
465*3be9681cSsthen 	size_t* compress_count)
466933707f3Ssthen {
467d8d14d0cSsthen 	size_t i, j, owner_pos;
468933707f3Ssthen 	int r, owner_labs;
469933707f3Ssthen 	uint16_t owner_ptr = 0;
4709982a05dSsthen 	time_t adjust = 0;
471933707f3Ssthen 	struct packed_rrset_data* data = (struct packed_rrset_data*)
472933707f3Ssthen 		key->entry.data;
473933707f3Ssthen 
474933707f3Ssthen 	/* does this RR type belong in the answer? */
475933707f3Ssthen 	if(!rrset_belongs_in_reply(s, ntohs(key->rk.type), qtype, dnssec))
476933707f3Ssthen 		return RETVAL_OK;
477933707f3Ssthen 
478933707f3Ssthen 	owner_labs = dname_count_labels(key->rk.dname);
4795d76a658Ssthen 	owner_pos = sldns_buffer_position(pkt);
480933707f3Ssthen 
4819982a05dSsthen 	/** Determine relative time adjustment for TTL values.
4829982a05dSsthen 	 * For an rrset with a fixed TTL, use the rrset's TTL as given. */
4832be9e038Ssthen 	if((key->rk.flags & PACKED_RRSET_FIXEDTTL) != 0)
4849982a05dSsthen 		adjust = 0;
4859982a05dSsthen 	else
4869982a05dSsthen 		adjust = SERVE_ORIGINAL_TTL ? data->ttl_add : timenow;
4872be9e038Ssthen 
488933707f3Ssthen 	if(do_data) {
4895d76a658Ssthen 		const sldns_rr_descriptor* c = type_rdata_compressable(key);
490933707f3Ssthen 		for(i=0; i<data->count; i++) {
491d8d14d0cSsthen 			/* rrset roundrobin */
492d8d14d0cSsthen 			j = (i + rr_offset) % data->count;
493933707f3Ssthen 			if((r=compress_owner(key, pkt, region, tree,
494*3be9681cSsthen 				owner_pos, &owner_ptr, owner_labs,
495*3be9681cSsthen 				compress_count)) != RETVAL_OK)
496933707f3Ssthen 				return r;
4975d76a658Ssthen 			sldns_buffer_write(pkt, &key->rk.type, 2);
4985d76a658Ssthen 			sldns_buffer_write(pkt, &key->rk.rrset_class, 2);
4999982a05dSsthen 			if(data->rr_ttl[j] < adjust)
500eaf2578eSsthen 				sldns_buffer_write_u32(pkt,
501eaf2578eSsthen 					SERVE_EXPIRED?SERVE_EXPIRED_REPLY_TTL:0);
5029982a05dSsthen 			else	sldns_buffer_write_u32(pkt, data->rr_ttl[j]-adjust);
503933707f3Ssthen 			if(c) {
504d8d14d0cSsthen 				if((r=compress_rdata(pkt, data->rr_data[j],
505*3be9681cSsthen 					data->rr_len[j], region, tree, c,
506*3be9681cSsthen 					compress_count)) != RETVAL_OK)
507933707f3Ssthen 					return r;
508933707f3Ssthen 			} else {
5095d76a658Ssthen 				if(sldns_buffer_remaining(pkt) < data->rr_len[j])
510933707f3Ssthen 					return RETVAL_TRUNC;
5115d76a658Ssthen 				sldns_buffer_write(pkt, data->rr_data[j],
512d8d14d0cSsthen 					data->rr_len[j]);
513933707f3Ssthen 			}
514933707f3Ssthen 		}
515933707f3Ssthen 	}
516933707f3Ssthen 	/* insert rrsigs */
517933707f3Ssthen 	if(do_sig && dnssec) {
518933707f3Ssthen 		size_t total = data->count+data->rrsig_count;
519933707f3Ssthen 		for(i=data->count; i<total; i++) {
520933707f3Ssthen 			if(owner_ptr && owner_labs != 1) {
5215d76a658Ssthen 				if(sldns_buffer_remaining(pkt) <
522933707f3Ssthen 					2+4+4+data->rr_len[i])
523933707f3Ssthen 					return RETVAL_TRUNC;
5245d76a658Ssthen 				sldns_buffer_write(pkt, &owner_ptr, 2);
525933707f3Ssthen 			} else {
526933707f3Ssthen 				if((r=compress_any_dname(key->rk.dname,
527*3be9681cSsthen 					pkt, owner_labs, region, tree,
528*3be9681cSsthen 					compress_count)) != RETVAL_OK)
529933707f3Ssthen 					return r;
5305d76a658Ssthen 				if(sldns_buffer_remaining(pkt) <
531933707f3Ssthen 					4+4+data->rr_len[i])
532933707f3Ssthen 					return RETVAL_TRUNC;
533933707f3Ssthen 			}
5345d76a658Ssthen 			sldns_buffer_write_u16(pkt, LDNS_RR_TYPE_RRSIG);
5355d76a658Ssthen 			sldns_buffer_write(pkt, &key->rk.rrset_class, 2);
5369982a05dSsthen 			if(data->rr_ttl[i] < adjust)
537eaf2578eSsthen 				sldns_buffer_write_u32(pkt,
538eaf2578eSsthen 					SERVE_EXPIRED?SERVE_EXPIRED_REPLY_TTL:0);
5399982a05dSsthen 			else	sldns_buffer_write_u32(pkt, data->rr_ttl[i]-adjust);
540933707f3Ssthen 			/* rrsig rdata cannot be compressed, perform 100+ byte
541933707f3Ssthen 			 * memcopy. */
5425d76a658Ssthen 			sldns_buffer_write(pkt, data->rr_data[i],
543933707f3Ssthen 				data->rr_len[i]);
544933707f3Ssthen 		}
545933707f3Ssthen 	}
546933707f3Ssthen 	/* change rrnum only after we are sure it fits */
547933707f3Ssthen 	if(do_data)
548933707f3Ssthen 		*num_rrs += data->count;
549933707f3Ssthen 	if(do_sig && dnssec)
550933707f3Ssthen 		*num_rrs += data->rrsig_count;
551933707f3Ssthen 
552933707f3Ssthen 	return RETVAL_OK;
553933707f3Ssthen }
554933707f3Ssthen 
555933707f3Ssthen /** store msg section in wireformat buffer, return RETVAL_* */
556933707f3Ssthen static int
557933707f3Ssthen insert_section(struct reply_info* rep, size_t num_rrsets, uint16_t* num_rrs,
5585d76a658Ssthen 	sldns_buffer* pkt, size_t rrsets_before, time_t timenow,
559933707f3Ssthen 	struct regional* region, struct compress_tree_node** tree,
560*3be9681cSsthen 	sldns_pkt_section s, uint16_t qtype, int dnssec, size_t rr_offset,
561*3be9681cSsthen 	size_t* compress_count)
562933707f3Ssthen {
563933707f3Ssthen 	int r;
564933707f3Ssthen 	size_t i, setstart;
56577079be7Ssthen 	/* we now allow this function to be called multiple times for the
56677079be7Ssthen 	 * same section, incrementally updating num_rrs.  The caller is
56777079be7Ssthen 	 * responsible for initializing it (which is the case in the current
56877079be7Ssthen 	 * implementation). */
56977079be7Ssthen 
570933707f3Ssthen 	if(s != LDNS_SECTION_ADDITIONAL) {
571933707f3Ssthen 		if(s == LDNS_SECTION_ANSWER && qtype == LDNS_RR_TYPE_ANY)
572933707f3Ssthen 			dnssec = 1; /* include all types in ANY answer */
573933707f3Ssthen 	  	for(i=0; i<num_rrsets; i++) {
5745d76a658Ssthen 			setstart = sldns_buffer_position(pkt);
575933707f3Ssthen 			if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i],
576933707f3Ssthen 				pkt, num_rrs, timenow, region, 1, 1, tree,
577*3be9681cSsthen 				s, qtype, dnssec, rr_offset, compress_count))
578933707f3Ssthen 				!= RETVAL_OK) {
579933707f3Ssthen 				/* Bad, but if due to size must set TC bit */
580933707f3Ssthen 				/* trim off the rrset neatly. */
5815d76a658Ssthen 				sldns_buffer_set_position(pkt, setstart);
582933707f3Ssthen 				return r;
583933707f3Ssthen 			}
584933707f3Ssthen 		}
585933707f3Ssthen 	} else {
586933707f3Ssthen 	  	for(i=0; i<num_rrsets; i++) {
5875d76a658Ssthen 			setstart = sldns_buffer_position(pkt);
588933707f3Ssthen 			if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i],
589933707f3Ssthen 				pkt, num_rrs, timenow, region, 1, 0, tree,
590*3be9681cSsthen 				s, qtype, dnssec, rr_offset, compress_count))
591933707f3Ssthen 				!= RETVAL_OK) {
5925d76a658Ssthen 				sldns_buffer_set_position(pkt, setstart);
593933707f3Ssthen 				return r;
594933707f3Ssthen 			}
595933707f3Ssthen 		}
596933707f3Ssthen 		if(dnssec)
597933707f3Ssthen 	  	  for(i=0; i<num_rrsets; i++) {
5985d76a658Ssthen 			setstart = sldns_buffer_position(pkt);
599933707f3Ssthen 			if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i],
600933707f3Ssthen 				pkt, num_rrs, timenow, region, 0, 1, tree,
601*3be9681cSsthen 				s, qtype, dnssec, rr_offset, compress_count))
602933707f3Ssthen 				!= RETVAL_OK) {
6035d76a658Ssthen 				sldns_buffer_set_position(pkt, setstart);
604933707f3Ssthen 				return r;
605933707f3Ssthen 			}
606933707f3Ssthen 		  }
607933707f3Ssthen 	}
608933707f3Ssthen 	return RETVAL_OK;
609933707f3Ssthen }
610933707f3Ssthen 
611933707f3Ssthen /** store query section in wireformat buffer, return RETVAL */
612933707f3Ssthen static int
613933707f3Ssthen insert_query(struct query_info* qinfo, struct compress_tree_node** tree,
6145d76a658Ssthen 	sldns_buffer* buffer, struct regional* region)
615933707f3Ssthen {
61677079be7Ssthen 	uint8_t* qname = qinfo->local_alias ?
61777079be7Ssthen 		qinfo->local_alias->rrset->rk.dname : qinfo->qname;
61877079be7Ssthen 	size_t qname_len = qinfo->local_alias ?
61977079be7Ssthen 		qinfo->local_alias->rrset->rk.dname_len : qinfo->qname_len;
6205d76a658Ssthen 	if(sldns_buffer_remaining(buffer) <
621933707f3Ssthen 		qinfo->qname_len+sizeof(uint16_t)*2)
622933707f3Ssthen 		return RETVAL_TRUNC; /* buffer too small */
623933707f3Ssthen 	/* the query is the first name inserted into the tree */
62477079be7Ssthen 	if(!compress_tree_store(qname, dname_count_labels(qname),
6255d76a658Ssthen 		sldns_buffer_position(buffer), region, NULL, tree))
626933707f3Ssthen 		return RETVAL_OUTMEM;
62777079be7Ssthen 	if(sldns_buffer_current(buffer) == qname)
62877079be7Ssthen 		sldns_buffer_skip(buffer, (ssize_t)qname_len);
62977079be7Ssthen 	else	sldns_buffer_write(buffer, qname, qname_len);
6305d76a658Ssthen 	sldns_buffer_write_u16(buffer, qinfo->qtype);
6315d76a658Ssthen 	sldns_buffer_write_u16(buffer, qinfo->qclass);
632933707f3Ssthen 	return RETVAL_OK;
633933707f3Ssthen }
634933707f3Ssthen 
635d8d14d0cSsthen static int
636d8d14d0cSsthen positive_answer(struct reply_info* rep, uint16_t qtype) {
637d8d14d0cSsthen 	size_t i;
638d8d14d0cSsthen 	if (FLAGS_GET_RCODE(rep->flags) != LDNS_RCODE_NOERROR)
639d8d14d0cSsthen 		return 0;
640d8d14d0cSsthen 
641d8d14d0cSsthen 	for(i=0;i<rep->an_numrrsets; i++) {
642d8d14d0cSsthen 		if(ntohs(rep->rrsets[i]->rk.type) == qtype) {
643eba819a2Ssthen 			/* for priming queries, type NS, include addresses */
644eba819a2Ssthen 			if(qtype == LDNS_RR_TYPE_NS)
645eba819a2Ssthen 				return 0;
646d8d14d0cSsthen 			/* in case it is a wildcard with DNSSEC, there will
647d8d14d0cSsthen 			 * be NSEC/NSEC3 records in the authority section
648d8d14d0cSsthen 			 * that we cannot remove */
649d8d14d0cSsthen 			for(i=rep->an_numrrsets; i<rep->an_numrrsets+
650d8d14d0cSsthen 				rep->ns_numrrsets; i++) {
651d8d14d0cSsthen 				if(ntohs(rep->rrsets[i]->rk.type) ==
652d8d14d0cSsthen 					LDNS_RR_TYPE_NSEC ||
653d8d14d0cSsthen 				   ntohs(rep->rrsets[i]->rk.type) ==
654d8d14d0cSsthen 				   	LDNS_RR_TYPE_NSEC3)
655d8d14d0cSsthen 					return 0;
656d8d14d0cSsthen 			}
657d8d14d0cSsthen 			return 1;
658d8d14d0cSsthen 		}
659d8d14d0cSsthen 	}
660d8d14d0cSsthen 	return 0;
661d8d14d0cSsthen }
662d8d14d0cSsthen 
6638240c1b9Ssthen static int
6648240c1b9Ssthen negative_answer(struct reply_info* rep) {
6658240c1b9Ssthen 	size_t i;
6668240c1b9Ssthen 	int ns_seen = 0;
6678240c1b9Ssthen 	if(FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NXDOMAIN)
6688240c1b9Ssthen 		return 1;
6698240c1b9Ssthen 	if(FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NOERROR &&
6708240c1b9Ssthen 		rep->an_numrrsets != 0)
6718240c1b9Ssthen 		return 0; /* positive */
6728240c1b9Ssthen 	if(FLAGS_GET_RCODE(rep->flags) != LDNS_RCODE_NOERROR &&
6738240c1b9Ssthen 		FLAGS_GET_RCODE(rep->flags) != LDNS_RCODE_NXDOMAIN)
6748240c1b9Ssthen 		return 0;
6758240c1b9Ssthen 	for(i=rep->an_numrrsets; i<rep->an_numrrsets+rep->ns_numrrsets; i++){
6768240c1b9Ssthen 		if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_SOA)
6778240c1b9Ssthen 			return 1;
6788240c1b9Ssthen 		if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NS)
6798240c1b9Ssthen 			ns_seen = 1;
6808240c1b9Ssthen 	}
6818240c1b9Ssthen 	if(ns_seen) return 0; /* could be referral, NS, but no SOA */
6828240c1b9Ssthen 	return 1;
6838240c1b9Ssthen }
6848240c1b9Ssthen 
685933707f3Ssthen int
686933707f3Ssthen reply_info_encode(struct query_info* qinfo, struct reply_info* rep,
6875d76a658Ssthen 	uint16_t id, uint16_t flags, sldns_buffer* buffer, time_t timenow,
6888240c1b9Ssthen 	struct regional* region, uint16_t udpsize, int dnssec, int minimise)
689933707f3Ssthen {
690933707f3Ssthen 	uint16_t ancount=0, nscount=0, arcount=0;
691933707f3Ssthen 	struct compress_tree_node* tree = 0;
692933707f3Ssthen 	int r;
693d8d14d0cSsthen 	size_t rr_offset;
694*3be9681cSsthen 	size_t compress_count=0;
695933707f3Ssthen 
6965d76a658Ssthen 	sldns_buffer_clear(buffer);
6975d76a658Ssthen 	if(udpsize < sldns_buffer_limit(buffer))
6985d76a658Ssthen 		sldns_buffer_set_limit(buffer, udpsize);
6995d76a658Ssthen 	if(sldns_buffer_remaining(buffer) < LDNS_HEADER_SIZE)
700933707f3Ssthen 		return 0;
701933707f3Ssthen 
7025d76a658Ssthen 	sldns_buffer_write(buffer, &id, sizeof(uint16_t));
7035d76a658Ssthen 	sldns_buffer_write_u16(buffer, flags);
7045d76a658Ssthen 	sldns_buffer_write_u16(buffer, rep->qdcount);
705933707f3Ssthen 	/* set an, ns, ar counts to zero in case of small packets */
7065d76a658Ssthen 	sldns_buffer_write(buffer, "\000\000\000\000\000\000", 6);
707933707f3Ssthen 
708933707f3Ssthen 	/* insert query section */
709933707f3Ssthen 	if(rep->qdcount) {
710933707f3Ssthen 		if((r=insert_query(qinfo, &tree, buffer, region)) !=
711933707f3Ssthen 			RETVAL_OK) {
712933707f3Ssthen 			if(r == RETVAL_TRUNC) {
713933707f3Ssthen 				/* create truncated message */
7145d76a658Ssthen 				sldns_buffer_write_u16_at(buffer, 4, 0);
7155d76a658Ssthen 				LDNS_TC_SET(sldns_buffer_begin(buffer));
7165d76a658Ssthen 				sldns_buffer_flip(buffer);
717933707f3Ssthen 				return 1;
718933707f3Ssthen 			}
719933707f3Ssthen 			return 0;
720933707f3Ssthen 		}
721933707f3Ssthen 	}
722229e174cSsthen 	/* roundrobin offset. using query id for random number.  With ntohs
723229e174cSsthen 	 * for different roundrobins for sequential id client senders. */
7243150e5f6Ssthen 	rr_offset = RRSET_ROUNDROBIN?ntohs(id)+(timenow?timenow:time(NULL)):0;
725933707f3Ssthen 
72677079be7Ssthen 	/* "prepend" any local alias records in the answer section if this
72777079be7Ssthen 	 * response is supposed to be authoritative.  Currently it should
72877079be7Ssthen 	 * be a single CNAME record (sanity-checked in worker_handle_request())
72977079be7Ssthen 	 * but it can be extended if and when we support more variations of
73077079be7Ssthen 	 * aliases. */
73177079be7Ssthen 	if(qinfo->local_alias && (flags & BIT_AA)) {
73277079be7Ssthen 		struct reply_info arep;
73377079be7Ssthen 		time_t timezero = 0; /* to use the 'authoritative' TTL */
73477079be7Ssthen 		memset(&arep, 0, sizeof(arep));
73577079be7Ssthen 		arep.flags = rep->flags;
73677079be7Ssthen 		arep.an_numrrsets = 1;
73777079be7Ssthen 		arep.rrset_count = 1;
73877079be7Ssthen 		arep.rrsets = &qinfo->local_alias->rrset;
73977079be7Ssthen 		if((r=insert_section(&arep, 1, &ancount, buffer, 0,
74077079be7Ssthen 			timezero, region, &tree, LDNS_SECTION_ANSWER,
741*3be9681cSsthen 			qinfo->qtype, dnssec, rr_offset, &compress_count)) != RETVAL_OK) {
74277079be7Ssthen 			if(r == RETVAL_TRUNC) {
74377079be7Ssthen 				/* create truncated message */
74477079be7Ssthen 				sldns_buffer_write_u16_at(buffer, 6, ancount);
74577079be7Ssthen 				LDNS_TC_SET(sldns_buffer_begin(buffer));
74677079be7Ssthen 				sldns_buffer_flip(buffer);
74777079be7Ssthen 				return 1;
74877079be7Ssthen 			}
74977079be7Ssthen 			return 0;
75077079be7Ssthen 		}
75177079be7Ssthen 	}
75277079be7Ssthen 
753933707f3Ssthen 	/* insert answer section */
754933707f3Ssthen 	if((r=insert_section(rep, rep->an_numrrsets, &ancount, buffer,
755933707f3Ssthen 		0, timenow, region, &tree, LDNS_SECTION_ANSWER, qinfo->qtype,
756*3be9681cSsthen 		dnssec, rr_offset, &compress_count)) != RETVAL_OK) {
757933707f3Ssthen 		if(r == RETVAL_TRUNC) {
758933707f3Ssthen 			/* create truncated message */
7595d76a658Ssthen 			sldns_buffer_write_u16_at(buffer, 6, ancount);
7605d76a658Ssthen 			LDNS_TC_SET(sldns_buffer_begin(buffer));
7615d76a658Ssthen 			sldns_buffer_flip(buffer);
762933707f3Ssthen 			return 1;
763933707f3Ssthen 		}
764933707f3Ssthen 		return 0;
765933707f3Ssthen 	}
7665d76a658Ssthen 	sldns_buffer_write_u16_at(buffer, 6, ancount);
767933707f3Ssthen 
768d8d14d0cSsthen 	/* if response is positive answer, auth/add sections are not required */
7698240c1b9Ssthen 	if( ! (minimise && positive_answer(rep, qinfo->qtype)) ) {
770933707f3Ssthen 		/* insert auth section */
771933707f3Ssthen 		if((r=insert_section(rep, rep->ns_numrrsets, &nscount, buffer,
772933707f3Ssthen 			rep->an_numrrsets, timenow, region, &tree,
773d8d14d0cSsthen 			LDNS_SECTION_AUTHORITY, qinfo->qtype,
774*3be9681cSsthen 			dnssec, rr_offset, &compress_count)) != RETVAL_OK) {
775933707f3Ssthen 			if(r == RETVAL_TRUNC) {
776933707f3Ssthen 				/* create truncated message */
7775d76a658Ssthen 				sldns_buffer_write_u16_at(buffer, 8, nscount);
7785d76a658Ssthen 				LDNS_TC_SET(sldns_buffer_begin(buffer));
7795d76a658Ssthen 				sldns_buffer_flip(buffer);
780933707f3Ssthen 				return 1;
781933707f3Ssthen 			}
782933707f3Ssthen 			return 0;
783933707f3Ssthen 		}
7845d76a658Ssthen 		sldns_buffer_write_u16_at(buffer, 8, nscount);
785933707f3Ssthen 
7868240c1b9Ssthen 		if(! (minimise && negative_answer(rep))) {
787933707f3Ssthen 			/* insert add section */
788933707f3Ssthen 			if((r=insert_section(rep, rep->ar_numrrsets, &arcount, buffer,
789933707f3Ssthen 				rep->an_numrrsets + rep->ns_numrrsets, timenow, region,
790933707f3Ssthen 				&tree, LDNS_SECTION_ADDITIONAL, qinfo->qtype,
791*3be9681cSsthen 				dnssec, rr_offset, &compress_count)) != RETVAL_OK) {
792933707f3Ssthen 				if(r == RETVAL_TRUNC) {
793933707f3Ssthen 					/* no need to set TC bit, this is the additional */
7945d76a658Ssthen 					sldns_buffer_write_u16_at(buffer, 10, arcount);
7955d76a658Ssthen 					sldns_buffer_flip(buffer);
796933707f3Ssthen 					return 1;
797933707f3Ssthen 				}
798933707f3Ssthen 				return 0;
799933707f3Ssthen 			}
8005d76a658Ssthen 			sldns_buffer_write_u16_at(buffer, 10, arcount);
801d8d14d0cSsthen 		}
8028240c1b9Ssthen 	}
8035d76a658Ssthen 	sldns_buffer_flip(buffer);
804933707f3Ssthen 	return 1;
805933707f3Ssthen }
806933707f3Ssthen 
807933707f3Ssthen uint16_t
808933707f3Ssthen calc_edns_field_size(struct edns_data* edns)
809933707f3Ssthen {
8102ee382b6Ssthen 	size_t rdatalen = 0;
8112ee382b6Ssthen 	struct edns_option* opt;
812933707f3Ssthen 	if(!edns || !edns->edns_present)
813933707f3Ssthen 		return 0;
814e21c60efSsthen 	for(opt = edns->opt_list_inplace_cb_out; opt; opt = opt->next) {
815e21c60efSsthen 		rdatalen += 4 + opt->opt_len;
816e21c60efSsthen 	}
817e21c60efSsthen 	for(opt = edns->opt_list_out; opt; opt = opt->next) {
8182ee382b6Ssthen 		rdatalen += 4 + opt->opt_len;
8192ee382b6Ssthen 	}
8202ee382b6Ssthen 	/* domain root '.' + type + class + ttl + rdatalen */
8212ee382b6Ssthen 	return 1 + 2 + 2 + 4 + 2 + rdatalen;
822933707f3Ssthen }
823933707f3Ssthen 
8248b7325afSsthen uint16_t
8258b7325afSsthen calc_edns_option_size(struct edns_data* edns, uint16_t code)
8268b7325afSsthen {
8278b7325afSsthen 	size_t rdatalen = 0;
8288b7325afSsthen 	struct edns_option* opt;
8298b7325afSsthen 	if(!edns || !edns->edns_present)
8308b7325afSsthen 		return 0;
8318b7325afSsthen 	for(opt = edns->opt_list_inplace_cb_out; opt; opt = opt->next) {
8328b7325afSsthen 		if(opt->opt_code == code)
8338b7325afSsthen 			rdatalen += 4 + opt->opt_len;
8348b7325afSsthen 	}
8358b7325afSsthen 	for(opt = edns->opt_list_out; opt; opt = opt->next) {
8368b7325afSsthen 		if(opt->opt_code == code)
8378b7325afSsthen 			rdatalen += 4 + opt->opt_len;
8388b7325afSsthen 	}
8398b7325afSsthen 	return rdatalen;
8408b7325afSsthen }
8418b7325afSsthen 
8428b7325afSsthen uint16_t
8438b7325afSsthen calc_ede_option_size(struct edns_data* edns, uint16_t* txt_size)
8448b7325afSsthen {
8458b7325afSsthen 	size_t rdatalen = 0;
8468b7325afSsthen 	struct edns_option* opt;
8478b7325afSsthen 	*txt_size = 0;
8488b7325afSsthen 	if(!edns || !edns->edns_present)
8498b7325afSsthen 		return 0;
8508b7325afSsthen 	for(opt = edns->opt_list_inplace_cb_out; opt; opt = opt->next) {
8518b7325afSsthen 		if(opt->opt_code == LDNS_EDNS_EDE) {
8528b7325afSsthen 			rdatalen += 4 + opt->opt_len;
8538b7325afSsthen 			if(opt->opt_len > 2) *txt_size += opt->opt_len - 2;
8548b7325afSsthen 			if(opt->opt_len >= 2 && sldns_read_uint16(
8558b7325afSsthen 				opt->opt_data) == LDNS_EDE_OTHER) {
8568b7325afSsthen 				*txt_size += 4 + 2;
8578b7325afSsthen 			}
8588b7325afSsthen 		}
8598b7325afSsthen 	}
8608b7325afSsthen 	for(opt = edns->opt_list_out; opt; opt = opt->next) {
8618b7325afSsthen 		if(opt->opt_code == LDNS_EDNS_EDE) {
8628b7325afSsthen 			rdatalen += 4 + opt->opt_len;
8638b7325afSsthen 			if(opt->opt_len > 2) *txt_size += opt->opt_len - 2;
8648b7325afSsthen 			if(opt->opt_len >= 2 && sldns_read_uint16(
8658b7325afSsthen 				opt->opt_data) == LDNS_EDE_OTHER) {
8668b7325afSsthen 				*txt_size += 4 + 2;
8678b7325afSsthen 			}
8688b7325afSsthen 		}
8698b7325afSsthen 	}
8708b7325afSsthen 	return rdatalen;
8718b7325afSsthen }
8728b7325afSsthen 
8738b7325afSsthen /* Trims the EDE OPTION-DATA to not include any EXTRA-TEXT data.
8748b7325afSsthen  * Also removes any LDNS_EDE_OTHER options from the list since they are useless
8758b7325afSsthen  * without the extra text. */
8768b7325afSsthen static void
8778b7325afSsthen ede_trim_text(struct edns_option** list)
8788b7325afSsthen {
8798b7325afSsthen 	struct edns_option* curr, *prev = NULL;
8808b7325afSsthen 	if(!list || !(*list)) return;
8818b7325afSsthen 	/* Unlink and repoint if LDNS_EDE_OTHER are first in list */
8828b7325afSsthen 	while(list && *list && (*list)->opt_code == LDNS_EDNS_EDE
8838b7325afSsthen 		&& (*list)->opt_len >= 2
8848b7325afSsthen 		&& sldns_read_uint16((*list)->opt_data) == LDNS_EDE_OTHER ) {
8858b7325afSsthen 		*list = (*list)->next;
8868b7325afSsthen 	}
8878b7325afSsthen 	if(!list || !(*list)) return;
8888b7325afSsthen 	curr = *list;
8898b7325afSsthen 	while(curr) {
8908b7325afSsthen 		if(curr->opt_code == LDNS_EDNS_EDE) {
8918b7325afSsthen 			if(curr->opt_len >= 2 && sldns_read_uint16(
8928b7325afSsthen 				curr->opt_data) == LDNS_EDE_OTHER) {
8938b7325afSsthen 				/* LDNS_EDE_OTHER cannot be the first option in
8948b7325afSsthen 				 * this while, so prev is always initialized at
8958b7325afSsthen 				 * this point from the other branches;
8968b7325afSsthen 				 * cut this option off */
8978b7325afSsthen 				prev->next = curr->next;
8988b7325afSsthen 				curr = curr->next;
8998b7325afSsthen 			} else if(curr->opt_len > 2) {
9008b7325afSsthen 				/* trim this option's EXTRA-TEXT */
9018b7325afSsthen 				curr->opt_len = 2;
9028b7325afSsthen 				prev = curr;
9038b7325afSsthen 				curr = curr->next;
90422a98bd7Ssthen 			} else {
90522a98bd7Ssthen 				prev = curr;
90622a98bd7Ssthen 				curr = curr->next;
9078b7325afSsthen 			}
9088b7325afSsthen 		} else {
9098b7325afSsthen 			/* continue */
9108b7325afSsthen 			prev = curr;
9118b7325afSsthen 			curr = curr->next;
9128b7325afSsthen 		}
9138b7325afSsthen 	}
9148b7325afSsthen }
9158b7325afSsthen 
9169982a05dSsthen static void
9179982a05dSsthen attach_edns_record_max_msg_sz(sldns_buffer* pkt, struct edns_data* edns,
9189982a05dSsthen 	uint16_t max_msg_sz)
919933707f3Ssthen {
920933707f3Ssthen 	size_t len;
9212ee382b6Ssthen 	size_t rdatapos;
9222ee382b6Ssthen 	struct edns_option* opt;
9239982a05dSsthen 	struct edns_option* padding_option = NULL;
924933707f3Ssthen 	/* inc additional count */
9255d76a658Ssthen 	sldns_buffer_write_u16_at(pkt, 10,
9265d76a658Ssthen 		sldns_buffer_read_u16_at(pkt, 10) + 1);
9275d76a658Ssthen 	len = sldns_buffer_limit(pkt);
9285d76a658Ssthen 	sldns_buffer_clear(pkt);
9295d76a658Ssthen 	sldns_buffer_set_position(pkt, len);
930933707f3Ssthen 	/* write EDNS record */
9315d76a658Ssthen 	sldns_buffer_write_u8(pkt, 0); /* '.' label */
9325d76a658Ssthen 	sldns_buffer_write_u16(pkt, LDNS_RR_TYPE_OPT); /* type */
9335d76a658Ssthen 	sldns_buffer_write_u16(pkt, edns->udp_size); /* class */
9345d76a658Ssthen 	sldns_buffer_write_u8(pkt, edns->ext_rcode); /* ttl */
9355d76a658Ssthen 	sldns_buffer_write_u8(pkt, edns->edns_version);
9365d76a658Ssthen 	sldns_buffer_write_u16(pkt, edns->bits);
9372ee382b6Ssthen 	rdatapos = sldns_buffer_position(pkt);
9385d76a658Ssthen 	sldns_buffer_write_u16(pkt, 0); /* rdatalen */
9392ee382b6Ssthen 	/* write rdata */
940e21c60efSsthen 	for(opt=edns->opt_list_inplace_cb_out; opt; opt=opt->next) {
941e21c60efSsthen 		if (opt->opt_code == LDNS_EDNS_PADDING) {
942e21c60efSsthen 			padding_option = opt;
943e21c60efSsthen 			continue;
944e21c60efSsthen 		}
945e21c60efSsthen 		sldns_buffer_write_u16(pkt, opt->opt_code);
946e21c60efSsthen 		sldns_buffer_write_u16(pkt, opt->opt_len);
947e21c60efSsthen 		if(opt->opt_len != 0)
948e21c60efSsthen 			sldns_buffer_write(pkt, opt->opt_data, opt->opt_len);
949e21c60efSsthen 	}
950e21c60efSsthen 	for(opt=edns->opt_list_out; opt; opt=opt->next) {
9519982a05dSsthen 		if (opt->opt_code == LDNS_EDNS_PADDING) {
9529982a05dSsthen 			padding_option = opt;
9539982a05dSsthen 			continue;
9549982a05dSsthen 		}
9552ee382b6Ssthen 		sldns_buffer_write_u16(pkt, opt->opt_code);
9562ee382b6Ssthen 		sldns_buffer_write_u16(pkt, opt->opt_len);
9572ee382b6Ssthen 		if(opt->opt_len != 0)
9582ee382b6Ssthen 			sldns_buffer_write(pkt, opt->opt_data, opt->opt_len);
9592ee382b6Ssthen 	}
9609982a05dSsthen 	if (padding_option && edns->padding_block_size ) {
9619982a05dSsthen 		size_t pad_pos = sldns_buffer_position(pkt);
9629982a05dSsthen 		size_t msg_sz = ((pad_pos + 3) / edns->padding_block_size + 1)
9639982a05dSsthen 		                               * edns->padding_block_size;
9649982a05dSsthen 		size_t pad_sz;
9659982a05dSsthen 
9669982a05dSsthen 		if (msg_sz > max_msg_sz)
9679982a05dSsthen 			msg_sz = max_msg_sz;
9689982a05dSsthen 
9699982a05dSsthen 		/* By use of calc_edns_field_size, calling functions should
9709982a05dSsthen 		 * have made sure that there is enough space for at least a
9719982a05dSsthen 		 * zero sized padding option.
9729982a05dSsthen 		 */
9739982a05dSsthen 		log_assert(pad_pos + 4 <= msg_sz);
9749982a05dSsthen 
9759982a05dSsthen 		pad_sz = msg_sz - pad_pos - 4;
9769982a05dSsthen 		sldns_buffer_write_u16(pkt, LDNS_EDNS_PADDING);
9779982a05dSsthen 		sldns_buffer_write_u16(pkt, pad_sz);
9789982a05dSsthen 		if (pad_sz) {
9799982a05dSsthen 			memset(sldns_buffer_current(pkt), 0, pad_sz);
9809982a05dSsthen 			sldns_buffer_skip(pkt, pad_sz);
9819982a05dSsthen 		}
9829982a05dSsthen 	}
9832ee382b6Ssthen 	sldns_buffer_write_u16_at(pkt, rdatapos,
9842ee382b6Ssthen 			sldns_buffer_position(pkt)-rdatapos-2);
9855d76a658Ssthen 	sldns_buffer_flip(pkt);
986933707f3Ssthen }
987933707f3Ssthen 
9889982a05dSsthen void
9899982a05dSsthen attach_edns_record(sldns_buffer* pkt, struct edns_data* edns)
9909982a05dSsthen {
9919982a05dSsthen 	if(!edns || !edns->edns_present)
9929982a05dSsthen 		return;
9939982a05dSsthen 	attach_edns_record_max_msg_sz(pkt, edns, edns->udp_size);
9949982a05dSsthen }
9959982a05dSsthen 
996933707f3Ssthen int
997933707f3Ssthen reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep,
9985d76a658Ssthen 	uint16_t id, uint16_t qflags, sldns_buffer* pkt, time_t timenow,
999933707f3Ssthen 	int cached, struct regional* region, uint16_t udpsize,
1000933707f3Ssthen 	struct edns_data* edns, int dnssec, int secure)
1001933707f3Ssthen {
1002933707f3Ssthen 	uint16_t flags;
10032be9e038Ssthen 	unsigned int attach_edns = 0;
10048b7325afSsthen 	uint16_t edns_field_size, ede_size, ede_txt_size;
1005933707f3Ssthen 
1006933707f3Ssthen 	if(!cached || rep->authoritative) {
1007933707f3Ssthen 		/* original flags, copy RD and CD bits from query. */
1008933707f3Ssthen 		flags = rep->flags | (qflags & (BIT_RD|BIT_CD));
1009933707f3Ssthen 	} else {
1010933707f3Ssthen 		/* remove AA bit, copy RD and CD bits from query. */
1011933707f3Ssthen 		flags = (rep->flags & ~BIT_AA) | (qflags & (BIT_RD|BIT_CD));
1012933707f3Ssthen 	}
1013933707f3Ssthen 	if(secure && (dnssec || (qflags&BIT_AD)))
1014933707f3Ssthen 		flags |= BIT_AD;
101577079be7Ssthen 	/* restore AA bit if we have a local alias and the response can be
101677079be7Ssthen 	 * authoritative.  Also clear AD bit if set as the local data is the
101777079be7Ssthen 	 * primary answer. */
101877079be7Ssthen 	if(qinf->local_alias &&
101977079be7Ssthen 		(FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NOERROR ||
102077079be7Ssthen 		FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NXDOMAIN)) {
102177079be7Ssthen 		flags |= BIT_AA;
102277079be7Ssthen 		flags &= ~BIT_AD;
102377079be7Ssthen 	}
1024933707f3Ssthen 	log_assert(flags & BIT_QR); /* QR bit must be on in our replies */
1025933707f3Ssthen 	if(udpsize < LDNS_HEADER_SIZE)
1026933707f3Ssthen 		return 0;
10278b7325afSsthen 	/* currently edns does not change during calculations;
10288b7325afSsthen 	 * calculate sizes once here */
10298b7325afSsthen 	edns_field_size = calc_edns_field_size(edns);
10308b7325afSsthen 	ede_size = calc_ede_option_size(edns, &ede_txt_size);
10312be9e038Ssthen 	if(sldns_buffer_capacity(pkt) < udpsize)
10322be9e038Ssthen 		udpsize = sldns_buffer_capacity(pkt);
1033d896b962Ssthen 	if(!edns || !edns->edns_present) {
1034d896b962Ssthen 		attach_edns = 0;
10358b7325afSsthen 	/* EDEs are optional, try to fit anything else before them */
1036d896b962Ssthen 	} else if(udpsize < LDNS_HEADER_SIZE + edns_field_size - ede_size) {
1037933707f3Ssthen 		/* packet too small to contain edns, omit it. */
1038933707f3Ssthen 		attach_edns = 0;
1039933707f3Ssthen 	} else {
1040933707f3Ssthen 		/* reserve space for edns record */
10418b7325afSsthen 		attach_edns = (unsigned int)edns_field_size - ede_size;
1042933707f3Ssthen 	}
1043933707f3Ssthen 
1044933707f3Ssthen 	if(!reply_info_encode(qinf, rep, id, flags, pkt, timenow, region,
10458b7325afSsthen 		udpsize - attach_edns, dnssec, MINIMAL_RESPONSES)) {
1046933707f3Ssthen 		log_err("reply encode: out of memory");
1047933707f3Ssthen 		return 0;
1048933707f3Ssthen 	}
10498b7325afSsthen 	if(attach_edns) {
10508b7325afSsthen 		if(udpsize >= sldns_buffer_limit(pkt) + edns_field_size)
10518b7325afSsthen 			attach_edns_record_max_msg_sz(pkt, edns, udpsize);
10528b7325afSsthen 		else if(udpsize >= sldns_buffer_limit(pkt) + edns_field_size - ede_txt_size) {
10538b7325afSsthen 			ede_trim_text(&edns->opt_list_inplace_cb_out);
10548b7325afSsthen 			ede_trim_text(&edns->opt_list_out);
10558b7325afSsthen 			attach_edns_record_max_msg_sz(pkt, edns, udpsize);
10568b7325afSsthen 		} else if(udpsize >= sldns_buffer_limit(pkt) + edns_field_size - ede_size) {
10578b7325afSsthen 			edns_opt_list_remove(&edns->opt_list_inplace_cb_out, LDNS_EDNS_EDE);
10588b7325afSsthen 			edns_opt_list_remove(&edns->opt_list_out, LDNS_EDNS_EDE);
10598b7325afSsthen 			attach_edns_record_max_msg_sz(pkt, edns, udpsize);
10608b7325afSsthen 		}
10618b7325afSsthen 	}
1062933707f3Ssthen 	return 1;
1063933707f3Ssthen }
1064933707f3Ssthen 
1065933707f3Ssthen void
10665d76a658Ssthen qinfo_query_encode(sldns_buffer* pkt, struct query_info* qinfo)
1067933707f3Ssthen {
1068933707f3Ssthen 	uint16_t flags = 0; /* QUERY, NOERROR */
106977079be7Ssthen 	const uint8_t* qname = qinfo->local_alias ?
107077079be7Ssthen 		qinfo->local_alias->rrset->rk.dname : qinfo->qname;
107177079be7Ssthen 	size_t qname_len = qinfo->local_alias ?
107277079be7Ssthen 		qinfo->local_alias->rrset->rk.dname_len : qinfo->qname_len;
10735d76a658Ssthen 	sldns_buffer_clear(pkt);
10745d76a658Ssthen 	log_assert(sldns_buffer_remaining(pkt) >= 12+255+4/*max query*/);
10755d76a658Ssthen 	sldns_buffer_skip(pkt, 2); /* id done later */
10765d76a658Ssthen 	sldns_buffer_write_u16(pkt, flags);
10775d76a658Ssthen 	sldns_buffer_write_u16(pkt, 1); /* query count */
10785d76a658Ssthen 	sldns_buffer_write(pkt, "\000\000\000\000\000\000", 6); /* counts */
107977079be7Ssthen 	sldns_buffer_write(pkt, qname, qname_len);
10805d76a658Ssthen 	sldns_buffer_write_u16(pkt, qinfo->qtype);
10815d76a658Ssthen 	sldns_buffer_write_u16(pkt, qinfo->qclass);
10825d76a658Ssthen 	sldns_buffer_flip(pkt);
1083933707f3Ssthen }
1084933707f3Ssthen 
1085933707f3Ssthen void
10868b7325afSsthen extended_error_encode(sldns_buffer* buf, uint16_t rcode,
10878b7325afSsthen 	struct query_info* qinfo, uint16_t qid, uint16_t qflags,
10888b7325afSsthen 	uint16_t xflags, struct edns_data* edns)
1089933707f3Ssthen {
1090933707f3Ssthen 	uint16_t flags;
1091933707f3Ssthen 
10925d76a658Ssthen 	sldns_buffer_clear(buf);
10935d76a658Ssthen 	sldns_buffer_write(buf, &qid, sizeof(uint16_t));
10948b7325afSsthen 	flags = (uint16_t)(BIT_QR | BIT_RA | (rcode & 0xF)); /* QR and retcode*/
10958b7325afSsthen 	flags |= xflags;
1096933707f3Ssthen 	flags |= (qflags & (BIT_RD|BIT_CD)); /* copy RD and CD bit */
10975d76a658Ssthen 	sldns_buffer_write_u16(buf, flags);
1098933707f3Ssthen 	if(qinfo) flags = 1;
1099933707f3Ssthen 	else	flags = 0;
11005d76a658Ssthen 	sldns_buffer_write_u16(buf, flags);
1101933707f3Ssthen 	flags = 0;
11025d76a658Ssthen 	sldns_buffer_write(buf, &flags, sizeof(uint16_t));
11035d76a658Ssthen 	sldns_buffer_write(buf, &flags, sizeof(uint16_t));
11045d76a658Ssthen 	sldns_buffer_write(buf, &flags, sizeof(uint16_t));
1105933707f3Ssthen 	if(qinfo) {
110677079be7Ssthen 		const uint8_t* qname = qinfo->local_alias ?
110777079be7Ssthen 			qinfo->local_alias->rrset->rk.dname : qinfo->qname;
110877079be7Ssthen 		size_t qname_len = qinfo->local_alias ?
110977079be7Ssthen 			qinfo->local_alias->rrset->rk.dname_len :
111077079be7Ssthen 			qinfo->qname_len;
111177079be7Ssthen 		if(sldns_buffer_current(buf) == qname)
111277079be7Ssthen 			sldns_buffer_skip(buf, (ssize_t)qname_len);
111377079be7Ssthen 		else	sldns_buffer_write(buf, qname, qname_len);
11145d76a658Ssthen 		sldns_buffer_write_u16(buf, qinfo->qtype);
11155d76a658Ssthen 		sldns_buffer_write_u16(buf, qinfo->qclass);
1116933707f3Ssthen 	}
11175d76a658Ssthen 	sldns_buffer_flip(buf);
1118933707f3Ssthen 	if(edns) {
1119933707f3Ssthen 		struct edns_data es = *edns;
1120933707f3Ssthen 		es.edns_version = EDNS_ADVERTISED_VERSION;
1121933707f3Ssthen 		es.udp_size = EDNS_ADVERTISED_SIZE;
11228b7325afSsthen 		es.ext_rcode = (uint8_t)(rcode >> 4);
1123933707f3Ssthen 		es.bits &= EDNS_DO;
11245d76a658Ssthen 		if(sldns_buffer_limit(buf) + calc_edns_field_size(&es) >
11258b7325afSsthen 			edns->udp_size) {
11268b7325afSsthen 			edns_opt_list_remove(&es.opt_list_inplace_cb_out, LDNS_EDNS_EDE);
11278b7325afSsthen 			edns_opt_list_remove(&es.opt_list_out, LDNS_EDNS_EDE);
11288b7325afSsthen 			if(sldns_buffer_limit(buf) + calc_edns_field_size(&es) >
11298b7325afSsthen 				edns->udp_size) {
1130933707f3Ssthen 				return;
11318b7325afSsthen 			}
11328b7325afSsthen 		}
1133933707f3Ssthen 		attach_edns_record(buf, &es);
1134933707f3Ssthen 	}
1135933707f3Ssthen }
11368b7325afSsthen 
11378b7325afSsthen void
11388b7325afSsthen error_encode(sldns_buffer* buf, int r, struct query_info* qinfo,
11398b7325afSsthen 	uint16_t qid, uint16_t qflags, struct edns_data* edns)
11408b7325afSsthen {
11418b7325afSsthen 	extended_error_encode(buf, (r & 0x000F), qinfo, qid, qflags,
11428b7325afSsthen 		(r & 0xFFF0), edns);
11438b7325afSsthen }
1144