xref: /freebsd/contrib/unbound/util/data/msgencode.c (revision 5a5c2279813012882e59aa7bb51d50c5baba3b1e)
1b7579f77SDag-Erling Smørgrav /*
2b7579f77SDag-Erling Smørgrav  * util/data/msgencode.c - Encode DNS messages, queries and replies.
3b7579f77SDag-Erling Smørgrav  *
4b7579f77SDag-Erling Smørgrav  * Copyright (c) 2007, NLnet Labs. All rights reserved.
5b7579f77SDag-Erling Smørgrav  *
6b7579f77SDag-Erling Smørgrav  * This software is open source.
7b7579f77SDag-Erling Smørgrav  *
8b7579f77SDag-Erling Smørgrav  * Redistribution and use in source and binary forms, with or without
9b7579f77SDag-Erling Smørgrav  * modification, are permitted provided that the following conditions
10b7579f77SDag-Erling Smørgrav  * are met:
11b7579f77SDag-Erling Smørgrav  *
12b7579f77SDag-Erling Smørgrav  * Redistributions of source code must retain the above copyright notice,
13b7579f77SDag-Erling Smørgrav  * this list of conditions and the following disclaimer.
14b7579f77SDag-Erling Smørgrav  *
15b7579f77SDag-Erling Smørgrav  * Redistributions in binary form must reproduce the above copyright notice,
16b7579f77SDag-Erling Smørgrav  * this list of conditions and the following disclaimer in the documentation
17b7579f77SDag-Erling Smørgrav  * and/or other materials provided with the distribution.
18b7579f77SDag-Erling Smørgrav  *
19b7579f77SDag-Erling Smørgrav  * Neither the name of the NLNET LABS nor the names of its contributors may
20b7579f77SDag-Erling Smørgrav  * be used to endorse or promote products derived from this software without
21b7579f77SDag-Erling Smørgrav  * specific prior written permission.
22b7579f77SDag-Erling Smørgrav  *
23b7579f77SDag-Erling Smørgrav  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2417d15b25SDag-Erling Smørgrav  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2517d15b25SDag-Erling Smørgrav  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2617d15b25SDag-Erling Smørgrav  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2717d15b25SDag-Erling Smørgrav  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2817d15b25SDag-Erling Smørgrav  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
2917d15b25SDag-Erling Smørgrav  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
3017d15b25SDag-Erling Smørgrav  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
3117d15b25SDag-Erling Smørgrav  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
3217d15b25SDag-Erling Smørgrav  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
3317d15b25SDag-Erling Smørgrav  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34b7579f77SDag-Erling Smørgrav  */
35b7579f77SDag-Erling Smørgrav 
36b7579f77SDag-Erling Smørgrav /**
37b7579f77SDag-Erling Smørgrav  * \file
38b7579f77SDag-Erling Smørgrav  *
39b7579f77SDag-Erling Smørgrav  * This file contains a routines to encode DNS messages.
40b7579f77SDag-Erling Smørgrav  */
41b7579f77SDag-Erling Smørgrav 
42b7579f77SDag-Erling Smørgrav #include "config.h"
43b7579f77SDag-Erling Smørgrav #include "util/data/msgencode.h"
44b7579f77SDag-Erling Smørgrav #include "util/data/msgreply.h"
45b7579f77SDag-Erling Smørgrav #include "util/data/msgparse.h"
46b7579f77SDag-Erling Smørgrav #include "util/data/dname.h"
47b7579f77SDag-Erling Smørgrav #include "util/log.h"
48b7579f77SDag-Erling Smørgrav #include "util/regional.h"
49b7579f77SDag-Erling Smørgrav #include "util/net_help.h"
5009a3aaf3SDag-Erling Smørgrav #include "sldns/sbuffer.h"
51bc892140SDag-Erling Smørgrav #include "services/localzone.h"
52b7579f77SDag-Erling Smørgrav 
53e86b9096SDag-Erling Smørgrav #ifdef HAVE_TIME_H
54e86b9096SDag-Erling Smørgrav #include <time.h>
55e86b9096SDag-Erling Smørgrav #endif
56e86b9096SDag-Erling Smørgrav #include <sys/time.h>
57e86b9096SDag-Erling Smørgrav 
58b7579f77SDag-Erling Smørgrav /** return code that means the function ran out of memory. negative so it does
59b7579f77SDag-Erling Smørgrav  * not conflict with DNS rcodes. */
60b7579f77SDag-Erling Smørgrav #define RETVAL_OUTMEM	-2
61b7579f77SDag-Erling Smørgrav /** return code that means the data did not fit (completely) in the packet */
62b7579f77SDag-Erling Smørgrav #define RETVAL_TRUNC	-4
63b7579f77SDag-Erling Smørgrav /** return code that means all is peachy keen. Equal to DNS rcode NOERROR */
64b7579f77SDag-Erling Smørgrav #define RETVAL_OK	0
65*5a5c2279SCy Schubert /** Max compressions we are willing to perform; more than that will result
66*5a5c2279SCy Schubert  *  in semi-compressed messages, or truncated even on TCP for huge messages, to
67*5a5c2279SCy Schubert  *  avoid locking the CPU for long */
68*5a5c2279SCy Schubert #define MAX_COMPRESSION_PER_MESSAGE 120
69b7579f77SDag-Erling Smørgrav 
70b7579f77SDag-Erling Smørgrav /**
71b7579f77SDag-Erling Smørgrav  * Data structure to help domain name compression in outgoing messages.
72b7579f77SDag-Erling Smørgrav  * A tree of dnames and their offsets in the packet is kept.
73b7579f77SDag-Erling Smørgrav  * It is kept sorted, not canonical, but by label at least, so that after
74b7579f77SDag-Erling Smørgrav  * a lookup of a name you know its closest match, and the parent from that
75b7579f77SDag-Erling Smørgrav  * closest match. These are possible compression targets.
76b7579f77SDag-Erling Smørgrav  *
77b7579f77SDag-Erling Smørgrav  * It is a binary tree, not a rbtree or balanced tree, as the effort
78b7579f77SDag-Erling Smørgrav  * of keeping it balanced probably outweighs usefulness (given typical
79b7579f77SDag-Erling Smørgrav  * DNS packet size).
80b7579f77SDag-Erling Smørgrav  */
81b7579f77SDag-Erling Smørgrav struct compress_tree_node {
82b7579f77SDag-Erling Smørgrav 	/** left node in tree, all smaller to this */
83b7579f77SDag-Erling Smørgrav 	struct compress_tree_node* left;
84b7579f77SDag-Erling Smørgrav 	/** right node in tree, all larger than this */
85b7579f77SDag-Erling Smørgrav 	struct compress_tree_node* right;
86b7579f77SDag-Erling Smørgrav 
87b7579f77SDag-Erling Smørgrav 	/** the parent node - not for tree, but zone parent. One less label */
88b7579f77SDag-Erling Smørgrav 	struct compress_tree_node* parent;
89b7579f77SDag-Erling Smørgrav 	/** the domain name for this node. Pointer to uncompressed memory. */
90b7579f77SDag-Erling Smørgrav 	uint8_t* dname;
91b7579f77SDag-Erling Smørgrav 	/** number of labels in domain name, kept to help compare func. */
92b7579f77SDag-Erling Smørgrav 	int labs;
93b7579f77SDag-Erling Smørgrav 	/** offset in packet that points to this dname */
94b7579f77SDag-Erling Smørgrav 	size_t offset;
95b7579f77SDag-Erling Smørgrav };
96b7579f77SDag-Erling Smørgrav 
97b7579f77SDag-Erling Smørgrav /**
98b7579f77SDag-Erling Smørgrav  * Find domain name in tree, returns exact and closest match.
99b7579f77SDag-Erling Smørgrav  * @param tree: root of tree.
100b7579f77SDag-Erling Smørgrav  * @param dname: pointer to uncompressed dname.
101b7579f77SDag-Erling Smørgrav  * @param labs: number of labels in domain name.
102b7579f77SDag-Erling Smørgrav  * @param match: closest or exact match.
103b7579f77SDag-Erling Smørgrav  *	guaranteed to be smaller or equal to the sought dname.
104b7579f77SDag-Erling Smørgrav  *	can be null if the tree is empty.
105b7579f77SDag-Erling Smørgrav  * @param matchlabels: number of labels that match with closest match.
106b7579f77SDag-Erling Smørgrav  *	can be zero is there is no match.
107b7579f77SDag-Erling Smørgrav  * @param insertpt: insert location for dname, if not found.
108b7579f77SDag-Erling Smørgrav  * @return: 0 if no exact match.
109b7579f77SDag-Erling Smørgrav  */
110b7579f77SDag-Erling Smørgrav 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)111b7579f77SDag-Erling Smørgrav compress_tree_search(struct compress_tree_node** tree, uint8_t* dname,
112b7579f77SDag-Erling Smørgrav 	int labs, struct compress_tree_node** match, int* matchlabels,
113b7579f77SDag-Erling Smørgrav 	struct compress_tree_node*** insertpt)
114b7579f77SDag-Erling Smørgrav {
115b7579f77SDag-Erling Smørgrav 	int c, n, closen=0;
116b7579f77SDag-Erling Smørgrav 	struct compress_tree_node* p = *tree;
117b7579f77SDag-Erling Smørgrav 	struct compress_tree_node* close = 0;
118b7579f77SDag-Erling Smørgrav 	struct compress_tree_node** prev = tree;
119b7579f77SDag-Erling Smørgrav 	while(p) {
120b7579f77SDag-Erling Smørgrav 		if((c = dname_lab_cmp(dname, labs, p->dname, p->labs, &n))
121b7579f77SDag-Erling Smørgrav 			== 0) {
122b7579f77SDag-Erling Smørgrav 			*matchlabels = n;
123b7579f77SDag-Erling Smørgrav 			*match = p;
124b7579f77SDag-Erling Smørgrav 			return 1;
125b7579f77SDag-Erling Smørgrav 		}
126b7579f77SDag-Erling Smørgrav 		if(c<0) {
127b7579f77SDag-Erling Smørgrav 			prev = &p->left;
128b7579f77SDag-Erling Smørgrav 			p = p->left;
129b7579f77SDag-Erling Smørgrav 		} else	{
130b7579f77SDag-Erling Smørgrav 			closen = n;
131b7579f77SDag-Erling Smørgrav 			close = p; /* p->dname is smaller than dname */
132b7579f77SDag-Erling Smørgrav 			prev = &p->right;
133b7579f77SDag-Erling Smørgrav 			p = p->right;
134b7579f77SDag-Erling Smørgrav 		}
135b7579f77SDag-Erling Smørgrav 	}
136b7579f77SDag-Erling Smørgrav 	*insertpt = prev;
137b7579f77SDag-Erling Smørgrav 	*matchlabels = closen;
138b7579f77SDag-Erling Smørgrav 	*match = close;
139b7579f77SDag-Erling Smørgrav 	return 0;
140b7579f77SDag-Erling Smørgrav }
141b7579f77SDag-Erling Smørgrav 
142b7579f77SDag-Erling Smørgrav /**
143b7579f77SDag-Erling Smørgrav  * Lookup a domain name in compression tree.
144b7579f77SDag-Erling Smørgrav  * @param tree: root of tree (not the node with '.').
145b7579f77SDag-Erling Smørgrav  * @param dname: pointer to uncompressed dname.
146b7579f77SDag-Erling Smørgrav  * @param labs: number of labels in domain name.
147b7579f77SDag-Erling Smørgrav  * @param insertpt: insert location for dname, if not found.
148b7579f77SDag-Erling Smørgrav  * @return: 0 if not found or compress treenode with best compression.
149b7579f77SDag-Erling Smørgrav  */
150b7579f77SDag-Erling Smørgrav static struct compress_tree_node*
compress_tree_lookup(struct compress_tree_node ** tree,uint8_t * dname,int labs,struct compress_tree_node *** insertpt)151b7579f77SDag-Erling Smørgrav compress_tree_lookup(struct compress_tree_node** tree, uint8_t* dname,
152b7579f77SDag-Erling Smørgrav 	int labs, struct compress_tree_node*** insertpt)
153b7579f77SDag-Erling Smørgrav {
154b7579f77SDag-Erling Smørgrav 	struct compress_tree_node* p;
155b7579f77SDag-Erling Smørgrav 	int m;
156b7579f77SDag-Erling Smørgrav 	if(labs <= 1)
157b7579f77SDag-Erling Smørgrav 		return 0; /* do not compress root node */
158b7579f77SDag-Erling Smørgrav 	if(compress_tree_search(tree, dname, labs, &p, &m, insertpt)) {
159b7579f77SDag-Erling Smørgrav 		/* exact match */
160b7579f77SDag-Erling Smørgrav 		return p;
161b7579f77SDag-Erling Smørgrav 	}
162b7579f77SDag-Erling Smørgrav 	/* return some ancestor of p that compresses well. */
163b7579f77SDag-Erling Smørgrav 	if(m>1) {
164b7579f77SDag-Erling Smørgrav 		/* www.example.com. (labs=4) matched foo.example.com.(labs=4)
165b7579f77SDag-Erling Smørgrav 		 * then matchcount = 3. need to go up. */
166b7579f77SDag-Erling Smørgrav 		while(p && p->labs > m)
167b7579f77SDag-Erling Smørgrav 			p = p->parent;
168b7579f77SDag-Erling Smørgrav 		return p;
169b7579f77SDag-Erling Smørgrav 	}
170b7579f77SDag-Erling Smørgrav 	return 0;
171b7579f77SDag-Erling Smørgrav }
172b7579f77SDag-Erling Smørgrav 
173b7579f77SDag-Erling Smørgrav /**
174b7579f77SDag-Erling Smørgrav  * Create node for domain name compression tree.
175b7579f77SDag-Erling Smørgrav  * @param dname: pointer to uncompressed dname (stored in tree).
176b7579f77SDag-Erling Smørgrav  * @param labs: number of labels in dname.
177b7579f77SDag-Erling Smørgrav  * @param offset: offset into packet for dname.
178b7579f77SDag-Erling Smørgrav  * @param region: how to allocate memory for new node.
179b7579f77SDag-Erling Smørgrav  * @return new node or 0 on malloc failure.
180b7579f77SDag-Erling Smørgrav  */
181b7579f77SDag-Erling Smørgrav static struct compress_tree_node*
compress_tree_newnode(uint8_t * dname,int labs,size_t offset,struct regional * region)182b7579f77SDag-Erling Smørgrav compress_tree_newnode(uint8_t* dname, int labs, size_t offset,
183b7579f77SDag-Erling Smørgrav 	struct regional* region)
184b7579f77SDag-Erling Smørgrav {
185b7579f77SDag-Erling Smørgrav 	struct compress_tree_node* n = (struct compress_tree_node*)
186b7579f77SDag-Erling Smørgrav 		regional_alloc(region, sizeof(struct compress_tree_node));
187b7579f77SDag-Erling Smørgrav 	if(!n) return 0;
188b7579f77SDag-Erling Smørgrav 	n->left = 0;
189b7579f77SDag-Erling Smørgrav 	n->right = 0;
190b7579f77SDag-Erling Smørgrav 	n->parent = 0;
191b7579f77SDag-Erling Smørgrav 	n->dname = dname;
192b7579f77SDag-Erling Smørgrav 	n->labs = labs;
193b7579f77SDag-Erling Smørgrav 	n->offset = offset;
194b7579f77SDag-Erling Smørgrav 	return n;
195b7579f77SDag-Erling Smørgrav }
196b7579f77SDag-Erling Smørgrav 
197b7579f77SDag-Erling Smørgrav /**
198b7579f77SDag-Erling Smørgrav  * Store domain name and ancestors into compression tree.
199b7579f77SDag-Erling Smørgrav  * @param dname: pointer to uncompressed dname (stored in tree).
200b7579f77SDag-Erling Smørgrav  * @param labs: number of labels in dname.
201b7579f77SDag-Erling Smørgrav  * @param offset: offset into packet for dname.
202b7579f77SDag-Erling Smørgrav  * @param region: how to allocate memory for new node.
203b7579f77SDag-Erling Smørgrav  * @param closest: match from previous lookup, used to compress dname.
204b7579f77SDag-Erling Smørgrav  *	may be NULL if no previous match.
205b7579f77SDag-Erling Smørgrav  *	if the tree has an ancestor of dname already, this must be it.
206b7579f77SDag-Erling Smørgrav  * @param insertpt: where to insert the dname in tree.
207b7579f77SDag-Erling Smørgrav  * @return: 0 on memory error.
208b7579f77SDag-Erling Smørgrav  */
209b7579f77SDag-Erling Smørgrav 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)210b7579f77SDag-Erling Smørgrav compress_tree_store(uint8_t* dname, int labs, size_t offset,
211b7579f77SDag-Erling Smørgrav 	struct regional* region, struct compress_tree_node* closest,
212b7579f77SDag-Erling Smørgrav 	struct compress_tree_node** insertpt)
213b7579f77SDag-Erling Smørgrav {
214b7579f77SDag-Erling Smørgrav 	uint8_t lablen;
215b7579f77SDag-Erling Smørgrav 	struct compress_tree_node* newnode;
216b7579f77SDag-Erling Smørgrav 	struct compress_tree_node* prevnode = NULL;
217b7579f77SDag-Erling Smørgrav 	int uplabs = labs-1; /* does not store root in tree */
218b7579f77SDag-Erling Smørgrav 	if(closest) uplabs = labs - closest->labs;
219b7579f77SDag-Erling Smørgrav 	log_assert(uplabs >= 0);
220b7579f77SDag-Erling Smørgrav 	/* algorithms builds up a vine of dname-labels to hang into tree */
221b7579f77SDag-Erling Smørgrav 	while(uplabs--) {
222b7579f77SDag-Erling Smørgrav 		if(offset > PTR_MAX_OFFSET) {
223b7579f77SDag-Erling Smørgrav 			/* insertion failed, drop vine */
224b7579f77SDag-Erling Smørgrav 			return 1; /* compression pointer no longer useful */
225b7579f77SDag-Erling Smørgrav 		}
226b7579f77SDag-Erling Smørgrav 		if(!(newnode = compress_tree_newnode(dname, labs, offset,
227b7579f77SDag-Erling Smørgrav 			region))) {
228b7579f77SDag-Erling Smørgrav 			/* insertion failed, drop vine */
229b7579f77SDag-Erling Smørgrav 			return 0;
230b7579f77SDag-Erling Smørgrav 		}
231b7579f77SDag-Erling Smørgrav 
232b7579f77SDag-Erling Smørgrav 		if(prevnode) {
233b7579f77SDag-Erling Smørgrav 			/* chain nodes together, last one has one label more,
234b7579f77SDag-Erling Smørgrav 			 * so is larger than newnode, thus goes right. */
235b7579f77SDag-Erling Smørgrav 			newnode->right = prevnode;
236b7579f77SDag-Erling Smørgrav 			prevnode->parent = newnode;
237b7579f77SDag-Erling Smørgrav 		}
238b7579f77SDag-Erling Smørgrav 
239b7579f77SDag-Erling Smørgrav 		/* next label */
240b7579f77SDag-Erling Smørgrav 		lablen = *dname++;
241b7579f77SDag-Erling Smørgrav 		dname += lablen;
242b7579f77SDag-Erling Smørgrav 		offset += lablen+1;
243b7579f77SDag-Erling Smørgrav 		prevnode = newnode;
244b7579f77SDag-Erling Smørgrav 		labs--;
245b7579f77SDag-Erling Smørgrav 	}
246b7579f77SDag-Erling Smørgrav 	/* if we have a vine, hang the vine into the tree */
247b7579f77SDag-Erling Smørgrav 	if(prevnode) {
248b7579f77SDag-Erling Smørgrav 		*insertpt = prevnode;
249b7579f77SDag-Erling Smørgrav 		prevnode->parent = closest;
250b7579f77SDag-Erling Smørgrav 	}
251b7579f77SDag-Erling Smørgrav 	return 1;
252b7579f77SDag-Erling Smørgrav }
253b7579f77SDag-Erling Smørgrav 
254b7579f77SDag-Erling Smørgrav /** compress a domain name */
255b7579f77SDag-Erling Smørgrav static int
write_compressed_dname(sldns_buffer * pkt,uint8_t * dname,int labs,struct compress_tree_node * p)25617d15b25SDag-Erling Smørgrav write_compressed_dname(sldns_buffer* pkt, uint8_t* dname, int labs,
257b7579f77SDag-Erling Smørgrav 	struct compress_tree_node* p)
258b7579f77SDag-Erling Smørgrav {
259b7579f77SDag-Erling Smørgrav 	/* compress it */
260b7579f77SDag-Erling Smørgrav 	int labcopy = labs - p->labs;
261b7579f77SDag-Erling Smørgrav 	uint8_t lablen;
262b7579f77SDag-Erling Smørgrav 	uint16_t ptr;
263b7579f77SDag-Erling Smørgrav 
264b7579f77SDag-Erling Smørgrav 	if(labs == 1) {
265b7579f77SDag-Erling Smørgrav 		/* write root label */
26617d15b25SDag-Erling Smørgrav 		if(sldns_buffer_remaining(pkt) < 1)
267b7579f77SDag-Erling Smørgrav 			return 0;
26817d15b25SDag-Erling Smørgrav 		sldns_buffer_write_u8(pkt, 0);
269b7579f77SDag-Erling Smørgrav 		return 1;
270b7579f77SDag-Erling Smørgrav 	}
271b7579f77SDag-Erling Smørgrav 
272b7579f77SDag-Erling Smørgrav 	/* copy the first couple of labels */
273b7579f77SDag-Erling Smørgrav 	while(labcopy--) {
274b7579f77SDag-Erling Smørgrav 		lablen = *dname++;
27517d15b25SDag-Erling Smørgrav 		if(sldns_buffer_remaining(pkt) < (size_t)lablen+1)
276b7579f77SDag-Erling Smørgrav 			return 0;
27717d15b25SDag-Erling Smørgrav 		sldns_buffer_write_u8(pkt, lablen);
27817d15b25SDag-Erling Smørgrav 		sldns_buffer_write(pkt, dname, lablen);
279b7579f77SDag-Erling Smørgrav 		dname += lablen;
280b7579f77SDag-Erling Smørgrav 	}
281b7579f77SDag-Erling Smørgrav 	/* insert compression ptr */
28217d15b25SDag-Erling Smørgrav 	if(sldns_buffer_remaining(pkt) < 2)
283b7579f77SDag-Erling Smørgrav 		return 0;
284b7579f77SDag-Erling Smørgrav 	ptr = PTR_CREATE(p->offset);
28517d15b25SDag-Erling Smørgrav 	sldns_buffer_write_u16(pkt, ptr);
286b7579f77SDag-Erling Smørgrav 	return 1;
287b7579f77SDag-Erling Smørgrav }
288b7579f77SDag-Erling Smørgrav 
289b7579f77SDag-Erling Smørgrav /** compress owner name of RR, return RETVAL_OUTMEM RETVAL_TRUNC */
290b7579f77SDag-Erling Smørgrav 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)29117d15b25SDag-Erling Smørgrav compress_owner(struct ub_packed_rrset_key* key, sldns_buffer* pkt,
292b7579f77SDag-Erling Smørgrav 	struct regional* region, struct compress_tree_node** tree,
293*5a5c2279SCy Schubert 	size_t owner_pos, uint16_t* owner_ptr, int owner_labs,
294*5a5c2279SCy Schubert 	size_t* compress_count)
295b7579f77SDag-Erling Smørgrav {
296b7579f77SDag-Erling Smørgrav 	struct compress_tree_node* p;
297b75612f8SDag-Erling Smørgrav 	struct compress_tree_node** insertpt = NULL;
298b7579f77SDag-Erling Smørgrav 	if(!*owner_ptr) {
299b7579f77SDag-Erling Smørgrav 		/* compress first time dname */
300*5a5c2279SCy Schubert 		if(*compress_count < MAX_COMPRESSION_PER_MESSAGE &&
301*5a5c2279SCy Schubert 			(p = compress_tree_lookup(tree, key->rk.dname,
302b7579f77SDag-Erling Smørgrav 			owner_labs, &insertpt))) {
303b7579f77SDag-Erling Smørgrav 			if(p->labs == owner_labs)
304b7579f77SDag-Erling Smørgrav 				/* avoid ptr chains, since some software is
305b7579f77SDag-Erling Smørgrav 				 * not capable of decoding ptr after a ptr. */
306b7579f77SDag-Erling Smørgrav 				*owner_ptr = htons(PTR_CREATE(p->offset));
307b7579f77SDag-Erling Smørgrav 			if(!write_compressed_dname(pkt, key->rk.dname,
308b7579f77SDag-Erling Smørgrav 				owner_labs, p))
309b7579f77SDag-Erling Smørgrav 				return RETVAL_TRUNC;
310*5a5c2279SCy Schubert 			(*compress_count)++;
311b7579f77SDag-Erling Smørgrav 			/* check if typeclass+4 ttl + rdatalen is available */
31217d15b25SDag-Erling Smørgrav 			if(sldns_buffer_remaining(pkt) < 4+4+2)
313b7579f77SDag-Erling Smørgrav 				return RETVAL_TRUNC;
314b7579f77SDag-Erling Smørgrav 		} else {
315b7579f77SDag-Erling Smørgrav 			/* no compress */
31617d15b25SDag-Erling Smørgrav 			if(sldns_buffer_remaining(pkt) < key->rk.dname_len+4+4+2)
317b7579f77SDag-Erling Smørgrav 				return RETVAL_TRUNC;
31817d15b25SDag-Erling Smørgrav 			sldns_buffer_write(pkt, key->rk.dname,
319b7579f77SDag-Erling Smørgrav 				key->rk.dname_len);
320b7579f77SDag-Erling Smørgrav 			if(owner_pos <= PTR_MAX_OFFSET)
321b7579f77SDag-Erling Smørgrav 				*owner_ptr = htons(PTR_CREATE(owner_pos));
322b7579f77SDag-Erling Smørgrav 		}
323*5a5c2279SCy Schubert 		if(*compress_count < MAX_COMPRESSION_PER_MESSAGE &&
324*5a5c2279SCy Schubert 			!compress_tree_store(key->rk.dname, owner_labs,
325b7579f77SDag-Erling Smørgrav 			owner_pos, region, p, insertpt))
326b7579f77SDag-Erling Smørgrav 			return RETVAL_OUTMEM;
327b7579f77SDag-Erling Smørgrav 	} else {
328b7579f77SDag-Erling Smørgrav 		/* always compress 2nd-further RRs in RRset */
329b7579f77SDag-Erling Smørgrav 		if(owner_labs == 1) {
33017d15b25SDag-Erling Smørgrav 			if(sldns_buffer_remaining(pkt) < 1+4+4+2)
331b7579f77SDag-Erling Smørgrav 				return RETVAL_TRUNC;
33217d15b25SDag-Erling Smørgrav 			sldns_buffer_write_u8(pkt, 0);
333b7579f77SDag-Erling Smørgrav 		} else {
33417d15b25SDag-Erling Smørgrav 			if(sldns_buffer_remaining(pkt) < 2+4+4+2)
335b7579f77SDag-Erling Smørgrav 				return RETVAL_TRUNC;
33617d15b25SDag-Erling Smørgrav 			sldns_buffer_write(pkt, owner_ptr, 2);
337b7579f77SDag-Erling Smørgrav 		}
338b7579f77SDag-Erling Smørgrav 	}
339b7579f77SDag-Erling Smørgrav 	return RETVAL_OK;
340b7579f77SDag-Erling Smørgrav }
341b7579f77SDag-Erling Smørgrav 
342b7579f77SDag-Erling Smørgrav /** compress any domain name to the packet, return RETVAL_* */
343b7579f77SDag-Erling Smørgrav 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)34417d15b25SDag-Erling Smørgrav compress_any_dname(uint8_t* dname, sldns_buffer* pkt, int labs,
345*5a5c2279SCy Schubert 	struct regional* region, struct compress_tree_node** tree,
346*5a5c2279SCy Schubert 	size_t* compress_count)
347b7579f77SDag-Erling Smørgrav {
348b7579f77SDag-Erling Smørgrav 	struct compress_tree_node* p;
349b7579f77SDag-Erling Smørgrav 	struct compress_tree_node** insertpt = NULL;
35017d15b25SDag-Erling Smørgrav 	size_t pos = sldns_buffer_position(pkt);
351*5a5c2279SCy Schubert 	if(*compress_count < MAX_COMPRESSION_PER_MESSAGE &&
352*5a5c2279SCy Schubert 		(p = compress_tree_lookup(tree, dname, labs, &insertpt))) {
353b7579f77SDag-Erling Smørgrav 		if(!write_compressed_dname(pkt, dname, labs, p))
354b7579f77SDag-Erling Smørgrav 			return RETVAL_TRUNC;
355*5a5c2279SCy Schubert 		(*compress_count)++;
356b7579f77SDag-Erling Smørgrav 	} else {
357b7579f77SDag-Erling Smørgrav 		if(!dname_buffer_write(pkt, dname))
358b7579f77SDag-Erling Smørgrav 			return RETVAL_TRUNC;
359b7579f77SDag-Erling Smørgrav 	}
360*5a5c2279SCy Schubert 	if(*compress_count < MAX_COMPRESSION_PER_MESSAGE &&
361*5a5c2279SCy Schubert 		!compress_tree_store(dname, labs, pos, region, p, insertpt))
362b7579f77SDag-Erling Smørgrav 		return RETVAL_OUTMEM;
363b7579f77SDag-Erling Smørgrav 	return RETVAL_OK;
364b7579f77SDag-Erling Smørgrav }
365b7579f77SDag-Erling Smørgrav 
366b7579f77SDag-Erling Smørgrav /** return true if type needs domain name compression in rdata */
36717d15b25SDag-Erling Smørgrav static const sldns_rr_descriptor*
type_rdata_compressable(struct ub_packed_rrset_key * key)368b7579f77SDag-Erling Smørgrav type_rdata_compressable(struct ub_packed_rrset_key* key)
369b7579f77SDag-Erling Smørgrav {
370b7579f77SDag-Erling Smørgrav 	uint16_t t = ntohs(key->rk.type);
37117d15b25SDag-Erling Smørgrav 	if(sldns_rr_descript(t) &&
37217d15b25SDag-Erling Smørgrav 		sldns_rr_descript(t)->_compress == LDNS_RR_COMPRESS)
37317d15b25SDag-Erling Smørgrav 		return sldns_rr_descript(t);
374b7579f77SDag-Erling Smørgrav 	return 0;
375b7579f77SDag-Erling Smørgrav }
376b7579f77SDag-Erling Smørgrav 
377b7579f77SDag-Erling Smørgrav /** compress domain names in rdata, return RETVAL_* */
378b7579f77SDag-Erling Smørgrav 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)37917d15b25SDag-Erling Smørgrav compress_rdata(sldns_buffer* pkt, uint8_t* rdata, size_t todolen,
380b7579f77SDag-Erling Smørgrav 	struct regional* region, struct compress_tree_node** tree,
381*5a5c2279SCy Schubert 	const sldns_rr_descriptor* desc, size_t* compress_count)
382b7579f77SDag-Erling Smørgrav {
383b7579f77SDag-Erling Smørgrav 	int labs, r, rdf = 0;
38417d15b25SDag-Erling Smørgrav 	size_t dname_len, len, pos = sldns_buffer_position(pkt);
385b7579f77SDag-Erling Smørgrav 	uint8_t count = desc->_dname_count;
386b7579f77SDag-Erling Smørgrav 
38717d15b25SDag-Erling Smørgrav 	sldns_buffer_skip(pkt, 2); /* rdata len fill in later */
388b7579f77SDag-Erling Smørgrav 	/* space for rdatalen checked for already */
389b7579f77SDag-Erling Smørgrav 	rdata += 2;
390b7579f77SDag-Erling Smørgrav 	todolen -= 2;
391b7579f77SDag-Erling Smørgrav 	while(todolen > 0 && count) {
392b7579f77SDag-Erling Smørgrav 		switch(desc->_wireformat[rdf]) {
393b7579f77SDag-Erling Smørgrav 		case LDNS_RDF_TYPE_DNAME:
394b7579f77SDag-Erling Smørgrav 			labs = dname_count_size_labels(rdata, &dname_len);
395b7579f77SDag-Erling Smørgrav 			if((r=compress_any_dname(rdata, pkt, labs, region,
396*5a5c2279SCy Schubert 				tree, compress_count)) != RETVAL_OK)
397b7579f77SDag-Erling Smørgrav 				return r;
398b7579f77SDag-Erling Smørgrav 			rdata += dname_len;
399b7579f77SDag-Erling Smørgrav 			todolen -= dname_len;
400b7579f77SDag-Erling Smørgrav 			count--;
401b7579f77SDag-Erling Smørgrav 			len = 0;
402b7579f77SDag-Erling Smørgrav 			break;
403b7579f77SDag-Erling Smørgrav 		case LDNS_RDF_TYPE_STR:
404b7579f77SDag-Erling Smørgrav 			len = *rdata + 1;
405b7579f77SDag-Erling Smørgrav 			break;
406b7579f77SDag-Erling Smørgrav 		default:
407b7579f77SDag-Erling Smørgrav 			len = get_rdf_size(desc->_wireformat[rdf]);
408b7579f77SDag-Erling Smørgrav 		}
409b7579f77SDag-Erling Smørgrav 		if(len) {
410b7579f77SDag-Erling Smørgrav 			/* copy over */
41117d15b25SDag-Erling Smørgrav 			if(sldns_buffer_remaining(pkt) < len)
412b7579f77SDag-Erling Smørgrav 				return RETVAL_TRUNC;
41317d15b25SDag-Erling Smørgrav 			sldns_buffer_write(pkt, rdata, len);
414b7579f77SDag-Erling Smørgrav 			todolen -= len;
415b7579f77SDag-Erling Smørgrav 			rdata += len;
416b7579f77SDag-Erling Smørgrav 		}
417b7579f77SDag-Erling Smørgrav 		rdf++;
418b7579f77SDag-Erling Smørgrav 	}
419b7579f77SDag-Erling Smørgrav 	/* copy remainder */
420b7579f77SDag-Erling Smørgrav 	if(todolen > 0) {
42117d15b25SDag-Erling Smørgrav 		if(sldns_buffer_remaining(pkt) < todolen)
422b7579f77SDag-Erling Smørgrav 			return RETVAL_TRUNC;
42317d15b25SDag-Erling Smørgrav 		sldns_buffer_write(pkt, rdata, todolen);
424b7579f77SDag-Erling Smørgrav 	}
425b7579f77SDag-Erling Smørgrav 
426b7579f77SDag-Erling Smørgrav 	/* set rdata len */
42717d15b25SDag-Erling Smørgrav 	sldns_buffer_write_u16_at(pkt, pos, sldns_buffer_position(pkt)-pos-2);
428b7579f77SDag-Erling Smørgrav 	return RETVAL_OK;
429b7579f77SDag-Erling Smørgrav }
430b7579f77SDag-Erling Smørgrav 
431b7579f77SDag-Erling Smørgrav /** Returns true if RR type should be included */
432b7579f77SDag-Erling Smørgrav static int
rrset_belongs_in_reply(sldns_pkt_section s,uint16_t rrtype,uint16_t qtype,int dnssec)43317d15b25SDag-Erling Smørgrav rrset_belongs_in_reply(sldns_pkt_section s, uint16_t rrtype, uint16_t qtype,
434b7579f77SDag-Erling Smørgrav 	int dnssec)
435b7579f77SDag-Erling Smørgrav {
436b7579f77SDag-Erling Smørgrav 	if(dnssec)
437b7579f77SDag-Erling Smørgrav 		return 1;
438b7579f77SDag-Erling Smørgrav 	/* skip non DNSSEC types, except if directly queried for */
439b7579f77SDag-Erling Smørgrav 	if(s == LDNS_SECTION_ANSWER) {
440b7579f77SDag-Erling Smørgrav 		if(qtype == LDNS_RR_TYPE_ANY || qtype == rrtype)
441b7579f77SDag-Erling Smørgrav 			return 1;
442b7579f77SDag-Erling Smørgrav 	}
443b7579f77SDag-Erling Smørgrav 	/* check DNSSEC-ness */
444b7579f77SDag-Erling Smørgrav 	switch(rrtype) {
445b7579f77SDag-Erling Smørgrav 		case LDNS_RR_TYPE_SIG:
446b7579f77SDag-Erling Smørgrav 		case LDNS_RR_TYPE_KEY:
447b7579f77SDag-Erling Smørgrav 		case LDNS_RR_TYPE_NXT:
448b7579f77SDag-Erling Smørgrav 		case LDNS_RR_TYPE_DS:
449b7579f77SDag-Erling Smørgrav 		case LDNS_RR_TYPE_RRSIG:
450b7579f77SDag-Erling Smørgrav 		case LDNS_RR_TYPE_NSEC:
451b7579f77SDag-Erling Smørgrav 		case LDNS_RR_TYPE_DNSKEY:
452b7579f77SDag-Erling Smørgrav 		case LDNS_RR_TYPE_NSEC3:
453b7579f77SDag-Erling Smørgrav 		case LDNS_RR_TYPE_NSEC3PARAMS:
454b7579f77SDag-Erling Smørgrav 			return 0;
455b7579f77SDag-Erling Smørgrav 	}
456b7579f77SDag-Erling Smørgrav 	return 1;
457b7579f77SDag-Erling Smørgrav }
458b7579f77SDag-Erling Smørgrav 
459b7579f77SDag-Erling Smørgrav /** store rrset in buffer in wireformat, return RETVAL_* */
460b7579f77SDag-Erling Smørgrav 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)46117d15b25SDag-Erling Smørgrav packed_rrset_encode(struct ub_packed_rrset_key* key, sldns_buffer* pkt,
46217d15b25SDag-Erling Smørgrav 	uint16_t* num_rrs, time_t timenow, struct regional* region,
463b7579f77SDag-Erling Smørgrav 	int do_data, int do_sig, struct compress_tree_node** tree,
464*5a5c2279SCy Schubert 	sldns_pkt_section s, uint16_t qtype, int dnssec, size_t rr_offset,
465*5a5c2279SCy Schubert 	size_t* compress_count)
466b7579f77SDag-Erling Smørgrav {
467b7579f77SDag-Erling Smørgrav 	size_t i, j, owner_pos;
468b7579f77SDag-Erling Smørgrav 	int r, owner_labs;
469b7579f77SDag-Erling Smørgrav 	uint16_t owner_ptr = 0;
470f44e67d1SCy Schubert 	time_t adjust = 0;
471b7579f77SDag-Erling Smørgrav 	struct packed_rrset_data* data = (struct packed_rrset_data*)
472b7579f77SDag-Erling Smørgrav 		key->entry.data;
473b7579f77SDag-Erling Smørgrav 
474b7579f77SDag-Erling Smørgrav 	/* does this RR type belong in the answer? */
475b7579f77SDag-Erling Smørgrav 	if(!rrset_belongs_in_reply(s, ntohs(key->rk.type), qtype, dnssec))
476b7579f77SDag-Erling Smørgrav 		return RETVAL_OK;
477b7579f77SDag-Erling Smørgrav 
478b7579f77SDag-Erling Smørgrav 	owner_labs = dname_count_labels(key->rk.dname);
47917d15b25SDag-Erling Smørgrav 	owner_pos = sldns_buffer_position(pkt);
480b7579f77SDag-Erling Smørgrav 
481f44e67d1SCy Schubert 	/** Determine relative time adjustment for TTL values.
482f44e67d1SCy Schubert 	 * For an rrset with a fixed TTL, use the rrset's TTL as given. */
48365b390aaSDag-Erling Smørgrav 	if((key->rk.flags & PACKED_RRSET_FIXEDTTL) != 0)
484f44e67d1SCy Schubert 		adjust = 0;
485f44e67d1SCy Schubert 	else
486f44e67d1SCy Schubert 		adjust = SERVE_ORIGINAL_TTL ? data->ttl_add : timenow;
48765b390aaSDag-Erling Smørgrav 
488b7579f77SDag-Erling Smørgrav 	if(do_data) {
48917d15b25SDag-Erling Smørgrav 		const sldns_rr_descriptor* c = type_rdata_compressable(key);
490b7579f77SDag-Erling Smørgrav 		for(i=0; i<data->count; i++) {
491b7579f77SDag-Erling Smørgrav 			/* rrset roundrobin */
492b7579f77SDag-Erling Smørgrav 			j = (i + rr_offset) % data->count;
493b7579f77SDag-Erling Smørgrav 			if((r=compress_owner(key, pkt, region, tree,
494*5a5c2279SCy Schubert 				owner_pos, &owner_ptr, owner_labs,
495*5a5c2279SCy Schubert 				compress_count)) != RETVAL_OK)
496b7579f77SDag-Erling Smørgrav 				return r;
49717d15b25SDag-Erling Smørgrav 			sldns_buffer_write(pkt, &key->rk.type, 2);
49817d15b25SDag-Erling Smørgrav 			sldns_buffer_write(pkt, &key->rk.rrset_class, 2);
499f44e67d1SCy Schubert 			if(data->rr_ttl[j] < adjust)
500091e9e46SCy Schubert 				sldns_buffer_write_u32(pkt,
501091e9e46SCy Schubert 					SERVE_EXPIRED?SERVE_EXPIRED_REPLY_TTL:0);
502f44e67d1SCy Schubert 			else	sldns_buffer_write_u32(pkt, data->rr_ttl[j]-adjust);
503b7579f77SDag-Erling Smørgrav 			if(c) {
504b7579f77SDag-Erling Smørgrav 				if((r=compress_rdata(pkt, data->rr_data[j],
505*5a5c2279SCy Schubert 					data->rr_len[j], region, tree, c,
506*5a5c2279SCy Schubert 					compress_count)) != RETVAL_OK)
507b7579f77SDag-Erling Smørgrav 					return r;
508b7579f77SDag-Erling Smørgrav 			} else {
50917d15b25SDag-Erling Smørgrav 				if(sldns_buffer_remaining(pkt) < data->rr_len[j])
510b7579f77SDag-Erling Smørgrav 					return RETVAL_TRUNC;
51117d15b25SDag-Erling Smørgrav 				sldns_buffer_write(pkt, data->rr_data[j],
512b7579f77SDag-Erling Smørgrav 					data->rr_len[j]);
513b7579f77SDag-Erling Smørgrav 			}
514b7579f77SDag-Erling Smørgrav 		}
515b7579f77SDag-Erling Smørgrav 	}
516b7579f77SDag-Erling Smørgrav 	/* insert rrsigs */
517b7579f77SDag-Erling Smørgrav 	if(do_sig && dnssec) {
518b7579f77SDag-Erling Smørgrav 		size_t total = data->count+data->rrsig_count;
519b7579f77SDag-Erling Smørgrav 		for(i=data->count; i<total; i++) {
520b7579f77SDag-Erling Smørgrav 			if(owner_ptr && owner_labs != 1) {
52117d15b25SDag-Erling Smørgrav 				if(sldns_buffer_remaining(pkt) <
522b7579f77SDag-Erling Smørgrav 					2+4+4+data->rr_len[i])
523b7579f77SDag-Erling Smørgrav 					return RETVAL_TRUNC;
52417d15b25SDag-Erling Smørgrav 				sldns_buffer_write(pkt, &owner_ptr, 2);
525b7579f77SDag-Erling Smørgrav 			} else {
526b7579f77SDag-Erling Smørgrav 				if((r=compress_any_dname(key->rk.dname,
527*5a5c2279SCy Schubert 					pkt, owner_labs, region, tree,
528*5a5c2279SCy Schubert 					compress_count)) != RETVAL_OK)
529b7579f77SDag-Erling Smørgrav 					return r;
53017d15b25SDag-Erling Smørgrav 				if(sldns_buffer_remaining(pkt) <
531b7579f77SDag-Erling Smørgrav 					4+4+data->rr_len[i])
532b7579f77SDag-Erling Smørgrav 					return RETVAL_TRUNC;
533b7579f77SDag-Erling Smørgrav 			}
53417d15b25SDag-Erling Smørgrav 			sldns_buffer_write_u16(pkt, LDNS_RR_TYPE_RRSIG);
53517d15b25SDag-Erling Smørgrav 			sldns_buffer_write(pkt, &key->rk.rrset_class, 2);
536f44e67d1SCy Schubert 			if(data->rr_ttl[i] < adjust)
537091e9e46SCy Schubert 				sldns_buffer_write_u32(pkt,
538091e9e46SCy Schubert 					SERVE_EXPIRED?SERVE_EXPIRED_REPLY_TTL:0);
539f44e67d1SCy Schubert 			else	sldns_buffer_write_u32(pkt, data->rr_ttl[i]-adjust);
540b7579f77SDag-Erling Smørgrav 			/* rrsig rdata cannot be compressed, perform 100+ byte
541b7579f77SDag-Erling Smørgrav 			 * memcopy. */
54217d15b25SDag-Erling Smørgrav 			sldns_buffer_write(pkt, data->rr_data[i],
543b7579f77SDag-Erling Smørgrav 				data->rr_len[i]);
544b7579f77SDag-Erling Smørgrav 		}
545b7579f77SDag-Erling Smørgrav 	}
546b7579f77SDag-Erling Smørgrav 	/* change rrnum only after we are sure it fits */
547b7579f77SDag-Erling Smørgrav 	if(do_data)
548b7579f77SDag-Erling Smørgrav 		*num_rrs += data->count;
549b7579f77SDag-Erling Smørgrav 	if(do_sig && dnssec)
550b7579f77SDag-Erling Smørgrav 		*num_rrs += data->rrsig_count;
551b7579f77SDag-Erling Smørgrav 
552b7579f77SDag-Erling Smørgrav 	return RETVAL_OK;
553b7579f77SDag-Erling Smørgrav }
554b7579f77SDag-Erling Smørgrav 
555b7579f77SDag-Erling Smørgrav /** store msg section in wireformat buffer, return RETVAL_* */
556b7579f77SDag-Erling Smørgrav 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)557b7579f77SDag-Erling Smørgrav insert_section(struct reply_info* rep, size_t num_rrsets, uint16_t* num_rrs,
55817d15b25SDag-Erling Smørgrav 	sldns_buffer* pkt, size_t rrsets_before, time_t timenow,
559b7579f77SDag-Erling Smørgrav 	struct regional* region, struct compress_tree_node** tree,
560*5a5c2279SCy Schubert 	sldns_pkt_section s, uint16_t qtype, int dnssec, size_t rr_offset,
561*5a5c2279SCy Schubert 	size_t* compress_count)
562b7579f77SDag-Erling Smørgrav {
563b7579f77SDag-Erling Smørgrav 	int r;
564b7579f77SDag-Erling Smørgrav 	size_t i, setstart;
565bc892140SDag-Erling Smørgrav 	/* we now allow this function to be called multiple times for the
566bc892140SDag-Erling Smørgrav 	 * same section, incrementally updating num_rrs.  The caller is
567bc892140SDag-Erling Smørgrav 	 * responsible for initializing it (which is the case in the current
568bc892140SDag-Erling Smørgrav 	 * implementation). */
569bc892140SDag-Erling Smørgrav 
570b7579f77SDag-Erling Smørgrav 	if(s != LDNS_SECTION_ADDITIONAL) {
571b7579f77SDag-Erling Smørgrav 		if(s == LDNS_SECTION_ANSWER && qtype == LDNS_RR_TYPE_ANY)
572b7579f77SDag-Erling Smørgrav 			dnssec = 1; /* include all types in ANY answer */
573b7579f77SDag-Erling Smørgrav 	  	for(i=0; i<num_rrsets; i++) {
57417d15b25SDag-Erling Smørgrav 			setstart = sldns_buffer_position(pkt);
575b7579f77SDag-Erling Smørgrav 			if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i],
576b7579f77SDag-Erling Smørgrav 				pkt, num_rrs, timenow, region, 1, 1, tree,
577*5a5c2279SCy Schubert 				s, qtype, dnssec, rr_offset, compress_count))
578b7579f77SDag-Erling Smørgrav 				!= RETVAL_OK) {
579b7579f77SDag-Erling Smørgrav 				/* Bad, but if due to size must set TC bit */
580b7579f77SDag-Erling Smørgrav 				/* trim off the rrset neatly. */
58117d15b25SDag-Erling Smørgrav 				sldns_buffer_set_position(pkt, setstart);
582b7579f77SDag-Erling Smørgrav 				return r;
583b7579f77SDag-Erling Smørgrav 			}
584b7579f77SDag-Erling Smørgrav 		}
585b7579f77SDag-Erling Smørgrav 	} else {
586b7579f77SDag-Erling Smørgrav 	  	for(i=0; i<num_rrsets; i++) {
58717d15b25SDag-Erling Smørgrav 			setstart = sldns_buffer_position(pkt);
588b7579f77SDag-Erling Smørgrav 			if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i],
589b7579f77SDag-Erling Smørgrav 				pkt, num_rrs, timenow, region, 1, 0, tree,
590*5a5c2279SCy Schubert 				s, qtype, dnssec, rr_offset, compress_count))
591b7579f77SDag-Erling Smørgrav 				!= RETVAL_OK) {
59217d15b25SDag-Erling Smørgrav 				sldns_buffer_set_position(pkt, setstart);
593b7579f77SDag-Erling Smørgrav 				return r;
594b7579f77SDag-Erling Smørgrav 			}
595b7579f77SDag-Erling Smørgrav 		}
596b7579f77SDag-Erling Smørgrav 		if(dnssec)
597b7579f77SDag-Erling Smørgrav 	  	  for(i=0; i<num_rrsets; i++) {
59817d15b25SDag-Erling Smørgrav 			setstart = sldns_buffer_position(pkt);
599b7579f77SDag-Erling Smørgrav 			if((r=packed_rrset_encode(rep->rrsets[rrsets_before+i],
600b7579f77SDag-Erling Smørgrav 				pkt, num_rrs, timenow, region, 0, 1, tree,
601*5a5c2279SCy Schubert 				s, qtype, dnssec, rr_offset, compress_count))
602b7579f77SDag-Erling Smørgrav 				!= RETVAL_OK) {
60317d15b25SDag-Erling Smørgrav 				sldns_buffer_set_position(pkt, setstart);
604b7579f77SDag-Erling Smørgrav 				return r;
605b7579f77SDag-Erling Smørgrav 			}
606b7579f77SDag-Erling Smørgrav 		  }
607b7579f77SDag-Erling Smørgrav 	}
608b7579f77SDag-Erling Smørgrav 	return RETVAL_OK;
609b7579f77SDag-Erling Smørgrav }
610b7579f77SDag-Erling Smørgrav 
611b7579f77SDag-Erling Smørgrav /** store query section in wireformat buffer, return RETVAL */
612b7579f77SDag-Erling Smørgrav static int
insert_query(struct query_info * qinfo,struct compress_tree_node ** tree,sldns_buffer * buffer,struct regional * region)613b7579f77SDag-Erling Smørgrav insert_query(struct query_info* qinfo, struct compress_tree_node** tree,
61417d15b25SDag-Erling Smørgrav 	sldns_buffer* buffer, struct regional* region)
615b7579f77SDag-Erling Smørgrav {
616bc892140SDag-Erling Smørgrav 	uint8_t* qname = qinfo->local_alias ?
617bc892140SDag-Erling Smørgrav 		qinfo->local_alias->rrset->rk.dname : qinfo->qname;
618bc892140SDag-Erling Smørgrav 	size_t qname_len = qinfo->local_alias ?
619bc892140SDag-Erling Smørgrav 		qinfo->local_alias->rrset->rk.dname_len : qinfo->qname_len;
62017d15b25SDag-Erling Smørgrav 	if(sldns_buffer_remaining(buffer) <
621b7579f77SDag-Erling Smørgrav 		qinfo->qname_len+sizeof(uint16_t)*2)
622b7579f77SDag-Erling Smørgrav 		return RETVAL_TRUNC; /* buffer too small */
623b7579f77SDag-Erling Smørgrav 	/* the query is the first name inserted into the tree */
624bc892140SDag-Erling Smørgrav 	if(!compress_tree_store(qname, dname_count_labels(qname),
62517d15b25SDag-Erling Smørgrav 		sldns_buffer_position(buffer), region, NULL, tree))
626b7579f77SDag-Erling Smørgrav 		return RETVAL_OUTMEM;
627bc892140SDag-Erling Smørgrav 	if(sldns_buffer_current(buffer) == qname)
628bc892140SDag-Erling Smørgrav 		sldns_buffer_skip(buffer, (ssize_t)qname_len);
629bc892140SDag-Erling Smørgrav 	else	sldns_buffer_write(buffer, qname, qname_len);
63017d15b25SDag-Erling Smørgrav 	sldns_buffer_write_u16(buffer, qinfo->qtype);
63117d15b25SDag-Erling Smørgrav 	sldns_buffer_write_u16(buffer, qinfo->qclass);
632b7579f77SDag-Erling Smørgrav 	return RETVAL_OK;
633b7579f77SDag-Erling Smørgrav }
634b7579f77SDag-Erling Smørgrav 
635b7579f77SDag-Erling Smørgrav static int
positive_answer(struct reply_info * rep,uint16_t qtype)636b7579f77SDag-Erling Smørgrav positive_answer(struct reply_info* rep, uint16_t qtype) {
637b7579f77SDag-Erling Smørgrav 	size_t i;
638b7579f77SDag-Erling Smørgrav 	if (FLAGS_GET_RCODE(rep->flags) != LDNS_RCODE_NOERROR)
639b7579f77SDag-Erling Smørgrav 		return 0;
640b7579f77SDag-Erling Smørgrav 
641b7579f77SDag-Erling Smørgrav 	for(i=0;i<rep->an_numrrsets; i++) {
642b7579f77SDag-Erling Smørgrav 		if(ntohs(rep->rrsets[i]->rk.type) == qtype) {
643369c6923SCy Schubert 			/* for priming queries, type NS, include addresses */
644369c6923SCy Schubert 			if(qtype == LDNS_RR_TYPE_NS)
645369c6923SCy Schubert 				return 0;
646b7579f77SDag-Erling Smørgrav 			/* in case it is a wildcard with DNSSEC, there will
647b7579f77SDag-Erling Smørgrav 			 * be NSEC/NSEC3 records in the authority section
648b7579f77SDag-Erling Smørgrav 			 * that we cannot remove */
649b7579f77SDag-Erling Smørgrav 			for(i=rep->an_numrrsets; i<rep->an_numrrsets+
650b7579f77SDag-Erling Smørgrav 				rep->ns_numrrsets; i++) {
651b7579f77SDag-Erling Smørgrav 				if(ntohs(rep->rrsets[i]->rk.type) ==
652b7579f77SDag-Erling Smørgrav 					LDNS_RR_TYPE_NSEC ||
653b7579f77SDag-Erling Smørgrav 				   ntohs(rep->rrsets[i]->rk.type) ==
654b7579f77SDag-Erling Smørgrav 				   	LDNS_RR_TYPE_NSEC3)
655b7579f77SDag-Erling Smørgrav 					return 0;
656b7579f77SDag-Erling Smørgrav 			}
657b7579f77SDag-Erling Smørgrav 			return 1;
658b7579f77SDag-Erling Smørgrav 		}
659b7579f77SDag-Erling Smørgrav 	}
660b7579f77SDag-Erling Smørgrav 	return 0;
661b7579f77SDag-Erling Smørgrav }
662b7579f77SDag-Erling Smørgrav 
6630eefd307SCy Schubert static int
negative_answer(struct reply_info * rep)6640eefd307SCy Schubert negative_answer(struct reply_info* rep) {
6650eefd307SCy Schubert 	size_t i;
6660eefd307SCy Schubert 	int ns_seen = 0;
6670eefd307SCy Schubert 	if(FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NXDOMAIN)
6680eefd307SCy Schubert 		return 1;
6690eefd307SCy Schubert 	if(FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NOERROR &&
6700eefd307SCy Schubert 		rep->an_numrrsets != 0)
6710eefd307SCy Schubert 		return 0; /* positive */
6720eefd307SCy Schubert 	if(FLAGS_GET_RCODE(rep->flags) != LDNS_RCODE_NOERROR &&
6730eefd307SCy Schubert 		FLAGS_GET_RCODE(rep->flags) != LDNS_RCODE_NXDOMAIN)
6740eefd307SCy Schubert 		return 0;
6750eefd307SCy Schubert 	for(i=rep->an_numrrsets; i<rep->an_numrrsets+rep->ns_numrrsets; i++){
6760eefd307SCy Schubert 		if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_SOA)
6770eefd307SCy Schubert 			return 1;
6780eefd307SCy Schubert 		if(ntohs(rep->rrsets[i]->rk.type) == LDNS_RR_TYPE_NS)
6790eefd307SCy Schubert 			ns_seen = 1;
6800eefd307SCy Schubert 	}
6810eefd307SCy Schubert 	if(ns_seen) return 0; /* could be referral, NS, but no SOA */
6820eefd307SCy Schubert 	return 1;
6830eefd307SCy Schubert }
6840eefd307SCy Schubert 
685b7579f77SDag-Erling Smørgrav 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)686b7579f77SDag-Erling Smørgrav reply_info_encode(struct query_info* qinfo, struct reply_info* rep,
68717d15b25SDag-Erling Smørgrav 	uint16_t id, uint16_t flags, sldns_buffer* buffer, time_t timenow,
6880eefd307SCy Schubert 	struct regional* region, uint16_t udpsize, int dnssec, int minimise)
689b7579f77SDag-Erling Smørgrav {
690b7579f77SDag-Erling Smørgrav 	uint16_t ancount=0, nscount=0, arcount=0;
691b7579f77SDag-Erling Smørgrav 	struct compress_tree_node* tree = 0;
692b7579f77SDag-Erling Smørgrav 	int r;
693b7579f77SDag-Erling Smørgrav 	size_t rr_offset;
694*5a5c2279SCy Schubert 	size_t compress_count=0;
695b7579f77SDag-Erling Smørgrav 
69617d15b25SDag-Erling Smørgrav 	sldns_buffer_clear(buffer);
69717d15b25SDag-Erling Smørgrav 	if(udpsize < sldns_buffer_limit(buffer))
69817d15b25SDag-Erling Smørgrav 		sldns_buffer_set_limit(buffer, udpsize);
69917d15b25SDag-Erling Smørgrav 	if(sldns_buffer_remaining(buffer) < LDNS_HEADER_SIZE)
700b7579f77SDag-Erling Smørgrav 		return 0;
701b7579f77SDag-Erling Smørgrav 
70217d15b25SDag-Erling Smørgrav 	sldns_buffer_write(buffer, &id, sizeof(uint16_t));
70317d15b25SDag-Erling Smørgrav 	sldns_buffer_write_u16(buffer, flags);
70417d15b25SDag-Erling Smørgrav 	sldns_buffer_write_u16(buffer, rep->qdcount);
705b7579f77SDag-Erling Smørgrav 	/* set an, ns, ar counts to zero in case of small packets */
70617d15b25SDag-Erling Smørgrav 	sldns_buffer_write(buffer, "\000\000\000\000\000\000", 6);
707b7579f77SDag-Erling Smørgrav 
708b7579f77SDag-Erling Smørgrav 	/* insert query section */
709b7579f77SDag-Erling Smørgrav 	if(rep->qdcount) {
710b7579f77SDag-Erling Smørgrav 		if((r=insert_query(qinfo, &tree, buffer, region)) !=
711b7579f77SDag-Erling Smørgrav 			RETVAL_OK) {
712b7579f77SDag-Erling Smørgrav 			if(r == RETVAL_TRUNC) {
713b7579f77SDag-Erling Smørgrav 				/* create truncated message */
71417d15b25SDag-Erling Smørgrav 				sldns_buffer_write_u16_at(buffer, 4, 0);
71517d15b25SDag-Erling Smørgrav 				LDNS_TC_SET(sldns_buffer_begin(buffer));
71617d15b25SDag-Erling Smørgrav 				sldns_buffer_flip(buffer);
717b7579f77SDag-Erling Smørgrav 				return 1;
718b7579f77SDag-Erling Smørgrav 			}
719b7579f77SDag-Erling Smørgrav 			return 0;
720b7579f77SDag-Erling Smørgrav 		}
721b7579f77SDag-Erling Smørgrav 	}
72217d15b25SDag-Erling Smørgrav 	/* roundrobin offset. using query id for random number.  With ntohs
72317d15b25SDag-Erling Smørgrav 	 * for different roundrobins for sequential id client senders. */
724e86b9096SDag-Erling Smørgrav 	rr_offset = RRSET_ROUNDROBIN?ntohs(id)+(timenow?timenow:time(NULL)):0;
725b7579f77SDag-Erling Smørgrav 
726bc892140SDag-Erling Smørgrav 	/* "prepend" any local alias records in the answer section if this
727bc892140SDag-Erling Smørgrav 	 * response is supposed to be authoritative.  Currently it should
728bc892140SDag-Erling Smørgrav 	 * be a single CNAME record (sanity-checked in worker_handle_request())
729bc892140SDag-Erling Smørgrav 	 * but it can be extended if and when we support more variations of
730bc892140SDag-Erling Smørgrav 	 * aliases. */
731bc892140SDag-Erling Smørgrav 	if(qinfo->local_alias && (flags & BIT_AA)) {
732bc892140SDag-Erling Smørgrav 		struct reply_info arep;
733bc892140SDag-Erling Smørgrav 		time_t timezero = 0; /* to use the 'authoritative' TTL */
734bc892140SDag-Erling Smørgrav 		memset(&arep, 0, sizeof(arep));
735bc892140SDag-Erling Smørgrav 		arep.flags = rep->flags;
736bc892140SDag-Erling Smørgrav 		arep.an_numrrsets = 1;
737bc892140SDag-Erling Smørgrav 		arep.rrset_count = 1;
738bc892140SDag-Erling Smørgrav 		arep.rrsets = &qinfo->local_alias->rrset;
739bc892140SDag-Erling Smørgrav 		if((r=insert_section(&arep, 1, &ancount, buffer, 0,
740bc892140SDag-Erling Smørgrav 			timezero, region, &tree, LDNS_SECTION_ANSWER,
741*5a5c2279SCy Schubert 			qinfo->qtype, dnssec, rr_offset, &compress_count)) != RETVAL_OK) {
742bc892140SDag-Erling Smørgrav 			if(r == RETVAL_TRUNC) {
743bc892140SDag-Erling Smørgrav 				/* create truncated message */
744bc892140SDag-Erling Smørgrav 				sldns_buffer_write_u16_at(buffer, 6, ancount);
745bc892140SDag-Erling Smørgrav 				LDNS_TC_SET(sldns_buffer_begin(buffer));
746bc892140SDag-Erling Smørgrav 				sldns_buffer_flip(buffer);
747bc892140SDag-Erling Smørgrav 				return 1;
748bc892140SDag-Erling Smørgrav 			}
749bc892140SDag-Erling Smørgrav 			return 0;
750bc892140SDag-Erling Smørgrav 		}
751bc892140SDag-Erling Smørgrav 	}
752bc892140SDag-Erling Smørgrav 
753b7579f77SDag-Erling Smørgrav 	/* insert answer section */
754b7579f77SDag-Erling Smørgrav 	if((r=insert_section(rep, rep->an_numrrsets, &ancount, buffer,
755b7579f77SDag-Erling Smørgrav 		0, timenow, region, &tree, LDNS_SECTION_ANSWER, qinfo->qtype,
756*5a5c2279SCy Schubert 		dnssec, rr_offset, &compress_count)) != RETVAL_OK) {
757b7579f77SDag-Erling Smørgrav 		if(r == RETVAL_TRUNC) {
758b7579f77SDag-Erling Smørgrav 			/* create truncated message */
75917d15b25SDag-Erling Smørgrav 			sldns_buffer_write_u16_at(buffer, 6, ancount);
76017d15b25SDag-Erling Smørgrav 			LDNS_TC_SET(sldns_buffer_begin(buffer));
76117d15b25SDag-Erling Smørgrav 			sldns_buffer_flip(buffer);
762b7579f77SDag-Erling Smørgrav 			return 1;
763b7579f77SDag-Erling Smørgrav 		}
764b7579f77SDag-Erling Smørgrav 		return 0;
765b7579f77SDag-Erling Smørgrav 	}
76617d15b25SDag-Erling Smørgrav 	sldns_buffer_write_u16_at(buffer, 6, ancount);
767b7579f77SDag-Erling Smørgrav 
768b7579f77SDag-Erling Smørgrav 	/* if response is positive answer, auth/add sections are not required */
7690eefd307SCy Schubert 	if( ! (minimise && positive_answer(rep, qinfo->qtype)) ) {
770b7579f77SDag-Erling Smørgrav 		/* insert auth section */
771b7579f77SDag-Erling Smørgrav 		if((r=insert_section(rep, rep->ns_numrrsets, &nscount, buffer,
772b7579f77SDag-Erling Smørgrav 			rep->an_numrrsets, timenow, region, &tree,
773b7579f77SDag-Erling Smørgrav 			LDNS_SECTION_AUTHORITY, qinfo->qtype,
774*5a5c2279SCy Schubert 			dnssec, rr_offset, &compress_count)) != RETVAL_OK) {
775b7579f77SDag-Erling Smørgrav 			if(r == RETVAL_TRUNC) {
776b7579f77SDag-Erling Smørgrav 				/* create truncated message */
77717d15b25SDag-Erling Smørgrav 				sldns_buffer_write_u16_at(buffer, 8, nscount);
77817d15b25SDag-Erling Smørgrav 				LDNS_TC_SET(sldns_buffer_begin(buffer));
77917d15b25SDag-Erling Smørgrav 				sldns_buffer_flip(buffer);
780b7579f77SDag-Erling Smørgrav 				return 1;
781b7579f77SDag-Erling Smørgrav 			}
782b7579f77SDag-Erling Smørgrav 			return 0;
783b7579f77SDag-Erling Smørgrav 		}
78417d15b25SDag-Erling Smørgrav 		sldns_buffer_write_u16_at(buffer, 8, nscount);
785b7579f77SDag-Erling Smørgrav 
7860eefd307SCy Schubert 		if(! (minimise && negative_answer(rep))) {
787b7579f77SDag-Erling Smørgrav 			/* insert add section */
788b7579f77SDag-Erling Smørgrav 			if((r=insert_section(rep, rep->ar_numrrsets, &arcount, buffer,
789b7579f77SDag-Erling Smørgrav 				rep->an_numrrsets + rep->ns_numrrsets, timenow, region,
790b7579f77SDag-Erling Smørgrav 				&tree, LDNS_SECTION_ADDITIONAL, qinfo->qtype,
791*5a5c2279SCy Schubert 				dnssec, rr_offset, &compress_count)) != RETVAL_OK) {
792b7579f77SDag-Erling Smørgrav 				if(r == RETVAL_TRUNC) {
793b7579f77SDag-Erling Smørgrav 					/* no need to set TC bit, this is the additional */
79417d15b25SDag-Erling Smørgrav 					sldns_buffer_write_u16_at(buffer, 10, arcount);
79517d15b25SDag-Erling Smørgrav 					sldns_buffer_flip(buffer);
796b7579f77SDag-Erling Smørgrav 					return 1;
797b7579f77SDag-Erling Smørgrav 				}
798b7579f77SDag-Erling Smørgrav 				return 0;
799b7579f77SDag-Erling Smørgrav 			}
80017d15b25SDag-Erling Smørgrav 			sldns_buffer_write_u16_at(buffer, 10, arcount);
801b7579f77SDag-Erling Smørgrav 		}
8020eefd307SCy Schubert 	}
80317d15b25SDag-Erling Smørgrav 	sldns_buffer_flip(buffer);
804b7579f77SDag-Erling Smørgrav 	return 1;
805b7579f77SDag-Erling Smørgrav }
806b7579f77SDag-Erling Smørgrav 
807b7579f77SDag-Erling Smørgrav uint16_t
calc_edns_field_size(struct edns_data * edns)808b7579f77SDag-Erling Smørgrav calc_edns_field_size(struct edns_data* edns)
809b7579f77SDag-Erling Smørgrav {
810e2d15004SDag-Erling Smørgrav 	size_t rdatalen = 0;
811e2d15004SDag-Erling Smørgrav 	struct edns_option* opt;
812b7579f77SDag-Erling Smørgrav 	if(!edns || !edns->edns_present)
813b7579f77SDag-Erling Smørgrav 		return 0;
81424e36522SCy Schubert 	for(opt = edns->opt_list_inplace_cb_out; opt; opt = opt->next) {
81524e36522SCy Schubert 		rdatalen += 4 + opt->opt_len;
81624e36522SCy Schubert 	}
81724e36522SCy Schubert 	for(opt = edns->opt_list_out; opt; opt = opt->next) {
818e2d15004SDag-Erling Smørgrav 		rdatalen += 4 + opt->opt_len;
819e2d15004SDag-Erling Smørgrav 	}
820e2d15004SDag-Erling Smørgrav 	/* domain root '.' + type + class + ttl + rdatalen */
821e2d15004SDag-Erling Smørgrav 	return 1 + 2 + 2 + 4 + 2 + rdatalen;
822b7579f77SDag-Erling Smørgrav }
823b7579f77SDag-Erling Smørgrav 
8248f76bb7dSCy Schubert uint16_t
calc_edns_option_size(struct edns_data * edns,uint16_t code)8258f76bb7dSCy Schubert calc_edns_option_size(struct edns_data* edns, uint16_t code)
8268f76bb7dSCy Schubert {
8278f76bb7dSCy Schubert 	size_t rdatalen = 0;
8288f76bb7dSCy Schubert 	struct edns_option* opt;
8298f76bb7dSCy Schubert 	if(!edns || !edns->edns_present)
8308f76bb7dSCy Schubert 		return 0;
8318f76bb7dSCy Schubert 	for(opt = edns->opt_list_inplace_cb_out; opt; opt = opt->next) {
8328f76bb7dSCy Schubert 		if(opt->opt_code == code)
8338f76bb7dSCy Schubert 			rdatalen += 4 + opt->opt_len;
8348f76bb7dSCy Schubert 	}
8358f76bb7dSCy Schubert 	for(opt = edns->opt_list_out; opt; opt = opt->next) {
8368f76bb7dSCy Schubert 		if(opt->opt_code == code)
8378f76bb7dSCy Schubert 			rdatalen += 4 + opt->opt_len;
8388f76bb7dSCy Schubert 	}
8398f76bb7dSCy Schubert 	return rdatalen;
8408f76bb7dSCy Schubert }
8418f76bb7dSCy Schubert 
8428f76bb7dSCy Schubert uint16_t
calc_ede_option_size(struct edns_data * edns,uint16_t * txt_size)8438f76bb7dSCy Schubert calc_ede_option_size(struct edns_data* edns, uint16_t* txt_size)
8448f76bb7dSCy Schubert {
8458f76bb7dSCy Schubert 	size_t rdatalen = 0;
8468f76bb7dSCy Schubert 	struct edns_option* opt;
8478f76bb7dSCy Schubert 	*txt_size = 0;
8488f76bb7dSCy Schubert 	if(!edns || !edns->edns_present)
8498f76bb7dSCy Schubert 		return 0;
8508f76bb7dSCy Schubert 	for(opt = edns->opt_list_inplace_cb_out; opt; opt = opt->next) {
8518f76bb7dSCy Schubert 		if(opt->opt_code == LDNS_EDNS_EDE) {
8528f76bb7dSCy Schubert 			rdatalen += 4 + opt->opt_len;
8538f76bb7dSCy Schubert 			if(opt->opt_len > 2) *txt_size += opt->opt_len - 2;
8548f76bb7dSCy Schubert 			if(opt->opt_len >= 2 && sldns_read_uint16(
8558f76bb7dSCy Schubert 				opt->opt_data) == LDNS_EDE_OTHER) {
8568f76bb7dSCy Schubert 				*txt_size += 4 + 2;
8578f76bb7dSCy Schubert 			}
8588f76bb7dSCy Schubert 		}
8598f76bb7dSCy Schubert 	}
8608f76bb7dSCy Schubert 	for(opt = edns->opt_list_out; opt; opt = opt->next) {
8618f76bb7dSCy Schubert 		if(opt->opt_code == LDNS_EDNS_EDE) {
8628f76bb7dSCy Schubert 			rdatalen += 4 + opt->opt_len;
8638f76bb7dSCy Schubert 			if(opt->opt_len > 2) *txt_size += opt->opt_len - 2;
8648f76bb7dSCy Schubert 			if(opt->opt_len >= 2 && sldns_read_uint16(
8658f76bb7dSCy Schubert 				opt->opt_data) == LDNS_EDE_OTHER) {
8668f76bb7dSCy Schubert 				*txt_size += 4 + 2;
8678f76bb7dSCy Schubert 			}
8688f76bb7dSCy Schubert 		}
8698f76bb7dSCy Schubert 	}
8708f76bb7dSCy Schubert 	return rdatalen;
8718f76bb7dSCy Schubert }
8728f76bb7dSCy Schubert 
8738f76bb7dSCy Schubert /* Trims the EDE OPTION-DATA to not include any EXTRA-TEXT data.
8748f76bb7dSCy Schubert  * Also removes any LDNS_EDE_OTHER options from the list since they are useless
8758f76bb7dSCy Schubert  * without the extra text. */
8768f76bb7dSCy Schubert static void
ede_trim_text(struct edns_option ** list)8778f76bb7dSCy Schubert ede_trim_text(struct edns_option** list)
8788f76bb7dSCy Schubert {
8798f76bb7dSCy Schubert 	struct edns_option* curr, *prev = NULL;
8808f76bb7dSCy Schubert 	if(!list || !(*list)) return;
8818f76bb7dSCy Schubert 	/* Unlink and repoint if LDNS_EDE_OTHER are first in list */
8828f76bb7dSCy Schubert 	while(list && *list && (*list)->opt_code == LDNS_EDNS_EDE
8838f76bb7dSCy Schubert 		&& (*list)->opt_len >= 2
8848f76bb7dSCy Schubert 		&& sldns_read_uint16((*list)->opt_data) == LDNS_EDE_OTHER ) {
8858f76bb7dSCy Schubert 		*list = (*list)->next;
8868f76bb7dSCy Schubert 	}
8878f76bb7dSCy Schubert 	if(!list || !(*list)) return;
8888f76bb7dSCy Schubert 	curr = *list;
8898f76bb7dSCy Schubert 	while(curr) {
8908f76bb7dSCy Schubert 		if(curr->opt_code == LDNS_EDNS_EDE) {
8918f76bb7dSCy Schubert 			if(curr->opt_len >= 2 && sldns_read_uint16(
8928f76bb7dSCy Schubert 				curr->opt_data) == LDNS_EDE_OTHER) {
8938f76bb7dSCy Schubert 				/* LDNS_EDE_OTHER cannot be the first option in
8948f76bb7dSCy Schubert 				 * this while, so prev is always initialized at
8958f76bb7dSCy Schubert 				 * this point from the other branches;
8968f76bb7dSCy Schubert 				 * cut this option off */
8978f76bb7dSCy Schubert 				prev->next = curr->next;
8988f76bb7dSCy Schubert 				curr = curr->next;
8998f76bb7dSCy Schubert 			} else if(curr->opt_len > 2) {
9008f76bb7dSCy Schubert 				/* trim this option's EXTRA-TEXT */
9018f76bb7dSCy Schubert 				curr->opt_len = 2;
9028f76bb7dSCy Schubert 				prev = curr;
9038f76bb7dSCy Schubert 				curr = curr->next;
904b7c0c8c1SCy Schubert 			} else {
905b7c0c8c1SCy Schubert 				prev = curr;
906b7c0c8c1SCy Schubert 				curr = curr->next;
9078f76bb7dSCy Schubert 			}
9088f76bb7dSCy Schubert 		} else {
9098f76bb7dSCy Schubert 			/* continue */
9108f76bb7dSCy Schubert 			prev = curr;
9118f76bb7dSCy Schubert 			curr = curr->next;
9128f76bb7dSCy Schubert 		}
9138f76bb7dSCy Schubert 	}
9148f76bb7dSCy Schubert }
9158f76bb7dSCy Schubert 
916f44e67d1SCy Schubert static void
attach_edns_record_max_msg_sz(sldns_buffer * pkt,struct edns_data * edns,uint16_t max_msg_sz)917f44e67d1SCy Schubert attach_edns_record_max_msg_sz(sldns_buffer* pkt, struct edns_data* edns,
918f44e67d1SCy Schubert 	uint16_t max_msg_sz)
919b7579f77SDag-Erling Smørgrav {
920b7579f77SDag-Erling Smørgrav 	size_t len;
921e2d15004SDag-Erling Smørgrav 	size_t rdatapos;
922e2d15004SDag-Erling Smørgrav 	struct edns_option* opt;
923f44e67d1SCy Schubert 	struct edns_option* padding_option = NULL;
924b7579f77SDag-Erling Smørgrav 	/* inc additional count */
92517d15b25SDag-Erling Smørgrav 	sldns_buffer_write_u16_at(pkt, 10,
92617d15b25SDag-Erling Smørgrav 		sldns_buffer_read_u16_at(pkt, 10) + 1);
92717d15b25SDag-Erling Smørgrav 	len = sldns_buffer_limit(pkt);
92817d15b25SDag-Erling Smørgrav 	sldns_buffer_clear(pkt);
92917d15b25SDag-Erling Smørgrav 	sldns_buffer_set_position(pkt, len);
930b7579f77SDag-Erling Smørgrav 	/* write EDNS record */
93117d15b25SDag-Erling Smørgrav 	sldns_buffer_write_u8(pkt, 0); /* '.' label */
93217d15b25SDag-Erling Smørgrav 	sldns_buffer_write_u16(pkt, LDNS_RR_TYPE_OPT); /* type */
93317d15b25SDag-Erling Smørgrav 	sldns_buffer_write_u16(pkt, edns->udp_size); /* class */
93417d15b25SDag-Erling Smørgrav 	sldns_buffer_write_u8(pkt, edns->ext_rcode); /* ttl */
93517d15b25SDag-Erling Smørgrav 	sldns_buffer_write_u8(pkt, edns->edns_version);
93617d15b25SDag-Erling Smørgrav 	sldns_buffer_write_u16(pkt, edns->bits);
937e2d15004SDag-Erling Smørgrav 	rdatapos = sldns_buffer_position(pkt);
93817d15b25SDag-Erling Smørgrav 	sldns_buffer_write_u16(pkt, 0); /* rdatalen */
939e2d15004SDag-Erling Smørgrav 	/* write rdata */
94024e36522SCy Schubert 	for(opt=edns->opt_list_inplace_cb_out; opt; opt=opt->next) {
94124e36522SCy Schubert 		if (opt->opt_code == LDNS_EDNS_PADDING) {
94224e36522SCy Schubert 			padding_option = opt;
94324e36522SCy Schubert 			continue;
94424e36522SCy Schubert 		}
94524e36522SCy Schubert 		sldns_buffer_write_u16(pkt, opt->opt_code);
94624e36522SCy Schubert 		sldns_buffer_write_u16(pkt, opt->opt_len);
94724e36522SCy Schubert 		if(opt->opt_len != 0)
94824e36522SCy Schubert 			sldns_buffer_write(pkt, opt->opt_data, opt->opt_len);
94924e36522SCy Schubert 	}
95024e36522SCy Schubert 	for(opt=edns->opt_list_out; opt; opt=opt->next) {
951f44e67d1SCy Schubert 		if (opt->opt_code == LDNS_EDNS_PADDING) {
952f44e67d1SCy Schubert 			padding_option = opt;
953f44e67d1SCy Schubert 			continue;
954f44e67d1SCy Schubert 		}
955e2d15004SDag-Erling Smørgrav 		sldns_buffer_write_u16(pkt, opt->opt_code);
956e2d15004SDag-Erling Smørgrav 		sldns_buffer_write_u16(pkt, opt->opt_len);
957e2d15004SDag-Erling Smørgrav 		if(opt->opt_len != 0)
958e2d15004SDag-Erling Smørgrav 			sldns_buffer_write(pkt, opt->opt_data, opt->opt_len);
959e2d15004SDag-Erling Smørgrav 	}
960f44e67d1SCy Schubert 	if (padding_option && edns->padding_block_size ) {
961f44e67d1SCy Schubert 		size_t pad_pos = sldns_buffer_position(pkt);
962f44e67d1SCy Schubert 		size_t msg_sz = ((pad_pos + 3) / edns->padding_block_size + 1)
963f44e67d1SCy Schubert 		                               * edns->padding_block_size;
964f44e67d1SCy Schubert 		size_t pad_sz;
965f44e67d1SCy Schubert 
966f44e67d1SCy Schubert 		if (msg_sz > max_msg_sz)
967f44e67d1SCy Schubert 			msg_sz = max_msg_sz;
968f44e67d1SCy Schubert 
969f44e67d1SCy Schubert 		/* By use of calc_edns_field_size, calling functions should
970f44e67d1SCy Schubert 		 * have made sure that there is enough space for at least a
971f44e67d1SCy Schubert 		 * zero sized padding option.
972f44e67d1SCy Schubert 		 */
973f44e67d1SCy Schubert 		log_assert(pad_pos + 4 <= msg_sz);
974f44e67d1SCy Schubert 
975f44e67d1SCy Schubert 		pad_sz = msg_sz - pad_pos - 4;
976f44e67d1SCy Schubert 		sldns_buffer_write_u16(pkt, LDNS_EDNS_PADDING);
977f44e67d1SCy Schubert 		sldns_buffer_write_u16(pkt, pad_sz);
978f44e67d1SCy Schubert 		if (pad_sz) {
979f44e67d1SCy Schubert 			memset(sldns_buffer_current(pkt), 0, pad_sz);
980f44e67d1SCy Schubert 			sldns_buffer_skip(pkt, pad_sz);
981f44e67d1SCy Schubert 		}
982f44e67d1SCy Schubert 	}
983e2d15004SDag-Erling Smørgrav 	sldns_buffer_write_u16_at(pkt, rdatapos,
984e2d15004SDag-Erling Smørgrav 			sldns_buffer_position(pkt)-rdatapos-2);
98517d15b25SDag-Erling Smørgrav 	sldns_buffer_flip(pkt);
986b7579f77SDag-Erling Smørgrav }
987b7579f77SDag-Erling Smørgrav 
988f44e67d1SCy Schubert void
attach_edns_record(sldns_buffer * pkt,struct edns_data * edns)989f44e67d1SCy Schubert attach_edns_record(sldns_buffer* pkt, struct edns_data* edns)
990f44e67d1SCy Schubert {
991f44e67d1SCy Schubert 	if(!edns || !edns->edns_present)
992f44e67d1SCy Schubert 		return;
993f44e67d1SCy Schubert 	attach_edns_record_max_msg_sz(pkt, edns, edns->udp_size);
994f44e67d1SCy Schubert }
995f44e67d1SCy Schubert 
996b7579f77SDag-Erling Smørgrav 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)997b7579f77SDag-Erling Smørgrav reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep,
99817d15b25SDag-Erling Smørgrav 	uint16_t id, uint16_t qflags, sldns_buffer* pkt, time_t timenow,
999b7579f77SDag-Erling Smørgrav 	int cached, struct regional* region, uint16_t udpsize,
1000b7579f77SDag-Erling Smørgrav 	struct edns_data* edns, int dnssec, int secure)
1001b7579f77SDag-Erling Smørgrav {
1002b7579f77SDag-Erling Smørgrav 	uint16_t flags;
1003c7f4d7adSDag-Erling Smørgrav 	unsigned int attach_edns = 0;
10048f76bb7dSCy Schubert 	uint16_t edns_field_size, ede_size, ede_txt_size;
1005b7579f77SDag-Erling Smørgrav 
1006b7579f77SDag-Erling Smørgrav 	if(!cached || rep->authoritative) {
1007b7579f77SDag-Erling Smørgrav 		/* original flags, copy RD and CD bits from query. */
1008b7579f77SDag-Erling Smørgrav 		flags = rep->flags | (qflags & (BIT_RD|BIT_CD));
1009b7579f77SDag-Erling Smørgrav 	} else {
1010b7579f77SDag-Erling Smørgrav 		/* remove AA bit, copy RD and CD bits from query. */
1011b7579f77SDag-Erling Smørgrav 		flags = (rep->flags & ~BIT_AA) | (qflags & (BIT_RD|BIT_CD));
1012b7579f77SDag-Erling Smørgrav 	}
1013b7579f77SDag-Erling Smørgrav 	if(secure && (dnssec || (qflags&BIT_AD)))
1014b7579f77SDag-Erling Smørgrav 		flags |= BIT_AD;
1015bc892140SDag-Erling Smørgrav 	/* restore AA bit if we have a local alias and the response can be
1016bc892140SDag-Erling Smørgrav 	 * authoritative.  Also clear AD bit if set as the local data is the
1017bc892140SDag-Erling Smørgrav 	 * primary answer. */
1018bc892140SDag-Erling Smørgrav 	if(qinf->local_alias &&
1019bc892140SDag-Erling Smørgrav 		(FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NOERROR ||
1020bc892140SDag-Erling Smørgrav 		FLAGS_GET_RCODE(rep->flags) == LDNS_RCODE_NXDOMAIN)) {
1021bc892140SDag-Erling Smørgrav 		flags |= BIT_AA;
1022bc892140SDag-Erling Smørgrav 		flags &= ~BIT_AD;
1023bc892140SDag-Erling Smørgrav 	}
1024b7579f77SDag-Erling Smørgrav 	log_assert(flags & BIT_QR); /* QR bit must be on in our replies */
1025b7579f77SDag-Erling Smørgrav 	if(udpsize < LDNS_HEADER_SIZE)
1026b7579f77SDag-Erling Smørgrav 		return 0;
10278f76bb7dSCy Schubert 	/* currently edns does not change during calculations;
10288f76bb7dSCy Schubert 	 * calculate sizes once here */
10298f76bb7dSCy Schubert 	edns_field_size = calc_edns_field_size(edns);
10308f76bb7dSCy Schubert 	ede_size = calc_ede_option_size(edns, &ede_txt_size);
1031c7f4d7adSDag-Erling Smørgrav 	if(sldns_buffer_capacity(pkt) < udpsize)
1032c7f4d7adSDag-Erling Smørgrav 		udpsize = sldns_buffer_capacity(pkt);
1033103ba509SCy Schubert 	if(!edns || !edns->edns_present) {
1034103ba509SCy Schubert 		attach_edns = 0;
10358f76bb7dSCy Schubert 	/* EDEs are optional, try to fit anything else before them */
1036103ba509SCy Schubert 	} else if(udpsize < LDNS_HEADER_SIZE + edns_field_size - ede_size) {
1037b7579f77SDag-Erling Smørgrav 		/* packet too small to contain edns, omit it. */
1038b7579f77SDag-Erling Smørgrav 		attach_edns = 0;
1039b7579f77SDag-Erling Smørgrav 	} else {
1040b7579f77SDag-Erling Smørgrav 		/* reserve space for edns record */
10418f76bb7dSCy Schubert 		attach_edns = (unsigned int)edns_field_size - ede_size;
1042b7579f77SDag-Erling Smørgrav 	}
1043b7579f77SDag-Erling Smørgrav 
1044b7579f77SDag-Erling Smørgrav 	if(!reply_info_encode(qinf, rep, id, flags, pkt, timenow, region,
10458f76bb7dSCy Schubert 		udpsize - attach_edns, dnssec, MINIMAL_RESPONSES)) {
1046b7579f77SDag-Erling Smørgrav 		log_err("reply encode: out of memory");
1047b7579f77SDag-Erling Smørgrav 		return 0;
1048b7579f77SDag-Erling Smørgrav 	}
10498f76bb7dSCy Schubert 	if(attach_edns) {
10508f76bb7dSCy Schubert 		if(udpsize >= sldns_buffer_limit(pkt) + edns_field_size)
10518f76bb7dSCy Schubert 			attach_edns_record_max_msg_sz(pkt, edns, udpsize);
10528f76bb7dSCy Schubert 		else if(udpsize >= sldns_buffer_limit(pkt) + edns_field_size - ede_txt_size) {
10538f76bb7dSCy Schubert 			ede_trim_text(&edns->opt_list_inplace_cb_out);
10548f76bb7dSCy Schubert 			ede_trim_text(&edns->opt_list_out);
10558f76bb7dSCy Schubert 			attach_edns_record_max_msg_sz(pkt, edns, udpsize);
10568f76bb7dSCy Schubert 		} else if(udpsize >= sldns_buffer_limit(pkt) + edns_field_size - ede_size) {
10578f76bb7dSCy Schubert 			edns_opt_list_remove(&edns->opt_list_inplace_cb_out, LDNS_EDNS_EDE);
10588f76bb7dSCy Schubert 			edns_opt_list_remove(&edns->opt_list_out, LDNS_EDNS_EDE);
10598f76bb7dSCy Schubert 			attach_edns_record_max_msg_sz(pkt, edns, udpsize);
10608f76bb7dSCy Schubert 		}
10618f76bb7dSCy Schubert 	}
1062b7579f77SDag-Erling Smørgrav 	return 1;
1063b7579f77SDag-Erling Smørgrav }
1064b7579f77SDag-Erling Smørgrav 
1065b7579f77SDag-Erling Smørgrav void
qinfo_query_encode(sldns_buffer * pkt,struct query_info * qinfo)106617d15b25SDag-Erling Smørgrav qinfo_query_encode(sldns_buffer* pkt, struct query_info* qinfo)
1067b7579f77SDag-Erling Smørgrav {
1068b7579f77SDag-Erling Smørgrav 	uint16_t flags = 0; /* QUERY, NOERROR */
1069bc892140SDag-Erling Smørgrav 	const uint8_t* qname = qinfo->local_alias ?
1070bc892140SDag-Erling Smørgrav 		qinfo->local_alias->rrset->rk.dname : qinfo->qname;
1071bc892140SDag-Erling Smørgrav 	size_t qname_len = qinfo->local_alias ?
1072bc892140SDag-Erling Smørgrav 		qinfo->local_alias->rrset->rk.dname_len : qinfo->qname_len;
107317d15b25SDag-Erling Smørgrav 	sldns_buffer_clear(pkt);
107417d15b25SDag-Erling Smørgrav 	log_assert(sldns_buffer_remaining(pkt) >= 12+255+4/*max query*/);
107517d15b25SDag-Erling Smørgrav 	sldns_buffer_skip(pkt, 2); /* id done later */
107617d15b25SDag-Erling Smørgrav 	sldns_buffer_write_u16(pkt, flags);
107717d15b25SDag-Erling Smørgrav 	sldns_buffer_write_u16(pkt, 1); /* query count */
107817d15b25SDag-Erling Smørgrav 	sldns_buffer_write(pkt, "\000\000\000\000\000\000", 6); /* counts */
1079bc892140SDag-Erling Smørgrav 	sldns_buffer_write(pkt, qname, qname_len);
108017d15b25SDag-Erling Smørgrav 	sldns_buffer_write_u16(pkt, qinfo->qtype);
108117d15b25SDag-Erling Smørgrav 	sldns_buffer_write_u16(pkt, qinfo->qclass);
108217d15b25SDag-Erling Smørgrav 	sldns_buffer_flip(pkt);
1083b7579f77SDag-Erling Smørgrav }
1084b7579f77SDag-Erling Smørgrav 
1085b7579f77SDag-Erling Smørgrav 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)10868f76bb7dSCy Schubert extended_error_encode(sldns_buffer* buf, uint16_t rcode,
10878f76bb7dSCy Schubert 	struct query_info* qinfo, uint16_t qid, uint16_t qflags,
10888f76bb7dSCy Schubert 	uint16_t xflags, struct edns_data* edns)
1089b7579f77SDag-Erling Smørgrav {
1090b7579f77SDag-Erling Smørgrav 	uint16_t flags;
1091b7579f77SDag-Erling Smørgrav 
109217d15b25SDag-Erling Smørgrav 	sldns_buffer_clear(buf);
109317d15b25SDag-Erling Smørgrav 	sldns_buffer_write(buf, &qid, sizeof(uint16_t));
10948f76bb7dSCy Schubert 	flags = (uint16_t)(BIT_QR | BIT_RA | (rcode & 0xF)); /* QR and retcode*/
10958f76bb7dSCy Schubert 	flags |= xflags;
1096b7579f77SDag-Erling Smørgrav 	flags |= (qflags & (BIT_RD|BIT_CD)); /* copy RD and CD bit */
109717d15b25SDag-Erling Smørgrav 	sldns_buffer_write_u16(buf, flags);
1098b7579f77SDag-Erling Smørgrav 	if(qinfo) flags = 1;
1099b7579f77SDag-Erling Smørgrav 	else	flags = 0;
110017d15b25SDag-Erling Smørgrav 	sldns_buffer_write_u16(buf, flags);
1101b7579f77SDag-Erling Smørgrav 	flags = 0;
110217d15b25SDag-Erling Smørgrav 	sldns_buffer_write(buf, &flags, sizeof(uint16_t));
110317d15b25SDag-Erling Smørgrav 	sldns_buffer_write(buf, &flags, sizeof(uint16_t));
110417d15b25SDag-Erling Smørgrav 	sldns_buffer_write(buf, &flags, sizeof(uint16_t));
1105b7579f77SDag-Erling Smørgrav 	if(qinfo) {
1106bc892140SDag-Erling Smørgrav 		const uint8_t* qname = qinfo->local_alias ?
1107bc892140SDag-Erling Smørgrav 			qinfo->local_alias->rrset->rk.dname : qinfo->qname;
1108bc892140SDag-Erling Smørgrav 		size_t qname_len = qinfo->local_alias ?
1109bc892140SDag-Erling Smørgrav 			qinfo->local_alias->rrset->rk.dname_len :
1110bc892140SDag-Erling Smørgrav 			qinfo->qname_len;
1111bc892140SDag-Erling Smørgrav 		if(sldns_buffer_current(buf) == qname)
1112bc892140SDag-Erling Smørgrav 			sldns_buffer_skip(buf, (ssize_t)qname_len);
1113bc892140SDag-Erling Smørgrav 		else	sldns_buffer_write(buf, qname, qname_len);
111417d15b25SDag-Erling Smørgrav 		sldns_buffer_write_u16(buf, qinfo->qtype);
111517d15b25SDag-Erling Smørgrav 		sldns_buffer_write_u16(buf, qinfo->qclass);
1116b7579f77SDag-Erling Smørgrav 	}
111717d15b25SDag-Erling Smørgrav 	sldns_buffer_flip(buf);
1118b7579f77SDag-Erling Smørgrav 	if(edns) {
1119b7579f77SDag-Erling Smørgrav 		struct edns_data es = *edns;
1120b7579f77SDag-Erling Smørgrav 		es.edns_version = EDNS_ADVERTISED_VERSION;
1121b7579f77SDag-Erling Smørgrav 		es.udp_size = EDNS_ADVERTISED_SIZE;
11228f76bb7dSCy Schubert 		es.ext_rcode = (uint8_t)(rcode >> 4);
1123b7579f77SDag-Erling Smørgrav 		es.bits &= EDNS_DO;
112417d15b25SDag-Erling Smørgrav 		if(sldns_buffer_limit(buf) + calc_edns_field_size(&es) >
11258f76bb7dSCy Schubert 			edns->udp_size) {
11268f76bb7dSCy Schubert 			edns_opt_list_remove(&es.opt_list_inplace_cb_out, LDNS_EDNS_EDE);
11278f76bb7dSCy Schubert 			edns_opt_list_remove(&es.opt_list_out, LDNS_EDNS_EDE);
11288f76bb7dSCy Schubert 			if(sldns_buffer_limit(buf) + calc_edns_field_size(&es) >
11298f76bb7dSCy Schubert 				edns->udp_size) {
1130b7579f77SDag-Erling Smørgrav 				return;
11318f76bb7dSCy Schubert 			}
11328f76bb7dSCy Schubert 		}
1133b7579f77SDag-Erling Smørgrav 		attach_edns_record(buf, &es);
1134b7579f77SDag-Erling Smørgrav 	}
1135b7579f77SDag-Erling Smørgrav }
11368f76bb7dSCy Schubert 
11378f76bb7dSCy Schubert void
error_encode(sldns_buffer * buf,int r,struct query_info * qinfo,uint16_t qid,uint16_t qflags,struct edns_data * edns)11388f76bb7dSCy Schubert error_encode(sldns_buffer* buf, int r, struct query_info* qinfo,
11398f76bb7dSCy Schubert 	uint16_t qid, uint16_t qflags, struct edns_data* edns)
11408f76bb7dSCy Schubert {
11418f76bb7dSCy Schubert 	extended_error_encode(buf, (r & 0x000F), qinfo, qid, qflags,
11428f76bb7dSCy Schubert 		(r & 0xFFF0), edns);
11438f76bb7dSCy Schubert }
1144