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