xref: /netbsd/external/mpl/bind/dist/lib/dns/message.c (revision 4ac1c27e)
1*4ac1c27eSchristos /*	$NetBSD: message.c,v 1.15 2023/01/25 21:43:30 christos Exp $	*/
2e2b1b9c0Schristos 
3e2b1b9c0Schristos /*
4e2b1b9c0Schristos  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5e2b1b9c0Schristos  *
6c0b5d9fbSchristos  * SPDX-License-Identifier: MPL-2.0
7c0b5d9fbSchristos  *
8e2b1b9c0Schristos  * This Source Code Form is subject to the terms of the Mozilla Public
9e2b1b9c0Schristos  * License, v. 2.0. If a copy of the MPL was not distributed with this
1073584a28Schristos  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11e2b1b9c0Schristos  *
12e2b1b9c0Schristos  * See the COPYRIGHT file distributed with this work for additional
13e2b1b9c0Schristos  * information regarding copyright ownership.
14e2b1b9c0Schristos  */
15e2b1b9c0Schristos 
16e2b1b9c0Schristos /*! \file */
17e2b1b9c0Schristos 
18e2b1b9c0Schristos /***
19e2b1b9c0Schristos  *** Imports
20e2b1b9c0Schristos  ***/
21e2b1b9c0Schristos 
22e2b1b9c0Schristos #include <ctype.h>
23f2e20987Schristos #include <inttypes.h>
24f2e20987Schristos #include <stdbool.h>
25e2b1b9c0Schristos 
26e2b1b9c0Schristos #include <isc/buffer.h>
27e2b1b9c0Schristos #include <isc/mem.h>
28e2b1b9c0Schristos #include <isc/print.h>
29e2b1b9c0Schristos #include <isc/string.h> /* Required for HP/UX (and others?) */
30803e9293Schristos #include <isc/utf8.h>
31e2b1b9c0Schristos #include <isc/util.h>
32e2b1b9c0Schristos 
33e2b1b9c0Schristos #include <dns/dnssec.h>
34e2b1b9c0Schristos #include <dns/keyvalues.h>
35e2b1b9c0Schristos #include <dns/log.h>
36e2b1b9c0Schristos #include <dns/masterdump.h>
37e2b1b9c0Schristos #include <dns/message.h>
38e2b1b9c0Schristos #include <dns/opcode.h>
39e2b1b9c0Schristos #include <dns/rcode.h>
40e2b1b9c0Schristos #include <dns/rdata.h>
41e2b1b9c0Schristos #include <dns/rdatalist.h>
42e2b1b9c0Schristos #include <dns/rdataset.h>
43e2b1b9c0Schristos #include <dns/rdatastruct.h>
44e2b1b9c0Schristos #include <dns/result.h>
45e2b1b9c0Schristos #include <dns/tsig.h>
46e2b1b9c0Schristos #include <dns/ttl.h>
47e2b1b9c0Schristos #include <dns/view.h>
48e2b1b9c0Schristos 
49e2b1b9c0Schristos #ifdef SKAN_MSG_DEBUG
50e2b1b9c0Schristos static void
hexdump(const char * msg,const char * msg2,void * base,size_t len)51e2b1b9c0Schristos hexdump(const char *msg, const char *msg2, void *base, size_t len) {
52e2b1b9c0Schristos 	unsigned char *p;
53e2b1b9c0Schristos 	unsigned int cnt;
54e2b1b9c0Schristos 
55e2b1b9c0Schristos 	p = base;
56e2b1b9c0Schristos 	cnt = 0;
57e2b1b9c0Schristos 
58e2b1b9c0Schristos 	printf("*** %s [%s] (%u bytes @ %p)\n", msg, msg2, (unsigned)len, base);
59e2b1b9c0Schristos 
60e2b1b9c0Schristos 	while (cnt < len) {
619742fdb4Schristos 		if (cnt % 16 == 0) {
62e2b1b9c0Schristos 			printf("%p: ", p);
639742fdb4Schristos 		} else if (cnt % 8 == 0) {
64e2b1b9c0Schristos 			printf(" |");
659742fdb4Schristos 		}
66e2b1b9c0Schristos 		printf(" %02x %c", *p, (isprint(*p) ? *p : ' '));
67e2b1b9c0Schristos 		p++;
68e2b1b9c0Schristos 		cnt++;
69e2b1b9c0Schristos 
709742fdb4Schristos 		if (cnt % 16 == 0) {
71e2b1b9c0Schristos 			printf("\n");
72e2b1b9c0Schristos 		}
739742fdb4Schristos 	}
74e2b1b9c0Schristos 
759742fdb4Schristos 	if (cnt % 16 != 0) {
76e2b1b9c0Schristos 		printf("\n");
77e2b1b9c0Schristos 	}
789742fdb4Schristos }
799742fdb4Schristos #endif /* ifdef SKAN_MSG_DEBUG */
80e2b1b9c0Schristos 
81e2b1b9c0Schristos #define DNS_MESSAGE_OPCODE_MASK	      0x7800U
82e2b1b9c0Schristos #define DNS_MESSAGE_OPCODE_SHIFT      11
83e2b1b9c0Schristos #define DNS_MESSAGE_RCODE_MASK	      0x000fU
84e2b1b9c0Schristos #define DNS_MESSAGE_FLAG_MASK	      0x8ff0U
85e2b1b9c0Schristos #define DNS_MESSAGE_EDNSRCODE_MASK    0xff000000U
86e2b1b9c0Schristos #define DNS_MESSAGE_EDNSRCODE_SHIFT   24
87e2b1b9c0Schristos #define DNS_MESSAGE_EDNSVERSION_MASK  0x00ff0000U
88e2b1b9c0Schristos #define DNS_MESSAGE_EDNSVERSION_SHIFT 16
89e2b1b9c0Schristos 
909742fdb4Schristos #define VALID_NAMED_SECTION(s) \
919742fdb4Schristos 	(((s) > DNS_SECTION_ANY) && ((s) < DNS_SECTION_MAX))
929742fdb4Schristos #define VALID_SECTION(s) (((s) >= DNS_SECTION_ANY) && ((s) < DNS_SECTION_MAX))
939742fdb4Schristos #define ADD_STRING(b, s)                                          \
949742fdb4Schristos 	{                                                         \
959742fdb4Schristos 		if (strlen(s) >= isc_buffer_availablelength(b)) { \
96e2b1b9c0Schristos 			result = ISC_R_NOSPACE;                   \
97e2b1b9c0Schristos 			goto cleanup;                             \
98e2b1b9c0Schristos 		} else                                            \
999742fdb4Schristos 			isc_buffer_putstr(b, s);                  \
1009742fdb4Schristos 	}
1019742fdb4Schristos #define VALID_PSEUDOSECTION(s) \
1029742fdb4Schristos 	(((s) >= DNS_PSEUDOSECTION_ANY) && ((s) < DNS_PSEUDOSECTION_MAX))
103e2b1b9c0Schristos 
104e2b1b9c0Schristos #define OPTOUT(x) (((x)->attributes & DNS_RDATASETATTR_OPTOUT) != 0)
105e2b1b9c0Schristos 
106e2b1b9c0Schristos /*%
107e2b1b9c0Schristos  * This is the size of each individual scratchpad buffer, and the numbers
108e2b1b9c0Schristos  * of various block allocations used within the server.
109e2b1b9c0Schristos  * XXXMLG These should come from a config setting.
110e2b1b9c0Schristos  */
111c0b5d9fbSchristos #define SCRATCHPAD_SIZE	   1232
112c0b5d9fbSchristos #define NAME_FILLCOUNT	   4
113c0b5d9fbSchristos #define NAME_FREEMAX	   8 * NAME_FILLCOUNT
114e2b1b9c0Schristos #define OFFSET_COUNT	   4
115e2b1b9c0Schristos #define RDATA_COUNT	   8
116e2b1b9c0Schristos #define RDATALIST_COUNT	   8
117c0b5d9fbSchristos #define RDATASET_FILLCOUNT 4
118c0b5d9fbSchristos #define RDATASET_FREEMAX   8 * RDATASET_FILLCOUNT
119e2b1b9c0Schristos 
120e2b1b9c0Schristos /*%
121e2b1b9c0Schristos  * Text representation of the different items, for message_totext
122e2b1b9c0Schristos  * functions.
123e2b1b9c0Schristos  */
1249742fdb4Schristos static const char *sectiontext[] = { "QUESTION", "ANSWER", "AUTHORITY",
1259742fdb4Schristos 				     "ADDITIONAL" };
126e2b1b9c0Schristos 
1279742fdb4Schristos static const char *updsectiontext[] = { "ZONE", "PREREQUISITE", "UPDATE",
1289742fdb4Schristos 					"ADDITIONAL" };
129e2b1b9c0Schristos 
1309742fdb4Schristos static const char *opcodetext[] = { "QUERY",	  "IQUERY",	"STATUS",
1319742fdb4Schristos 				    "RESERVED3",  "NOTIFY",	"UPDATE",
1329742fdb4Schristos 				    "RESERVED6",  "RESERVED7",	"RESERVED8",
1339742fdb4Schristos 				    "RESERVED9",  "RESERVED10", "RESERVED11",
1349742fdb4Schristos 				    "RESERVED12", "RESERVED13", "RESERVED14",
1359742fdb4Schristos 				    "RESERVED15" };
136e2b1b9c0Schristos 
137803e9293Schristos static const char *edetext[] = { "Other",
138803e9293Schristos 				 "Unsupported DNSKEY Algorithm",
139803e9293Schristos 				 "Unsupported DS Digest Type",
140803e9293Schristos 				 "Stale Answer",
141803e9293Schristos 				 "Forged Answer",
142803e9293Schristos 				 "DNSSEC Indeterminate",
143803e9293Schristos 				 "DNSSEC Bogus",
144803e9293Schristos 				 "Signature Expired",
145803e9293Schristos 				 "Signature Not Yet Valid",
146803e9293Schristos 				 "DNSKEY Missing",
147803e9293Schristos 				 "RRSIGs Missing",
148803e9293Schristos 				 "No Zone Key Bit Set",
149803e9293Schristos 				 "NSEC Missing",
150803e9293Schristos 				 "Cached Error",
151803e9293Schristos 				 "Not Ready",
152803e9293Schristos 				 "Blocked",
153803e9293Schristos 				 "Censored",
154803e9293Schristos 				 "Filtered",
155803e9293Schristos 				 "Prohibited",
156803e9293Schristos 				 "Stale NXDOMAIN Answer",
157803e9293Schristos 				 "Not Authoritative",
158803e9293Schristos 				 "Not Supported",
159803e9293Schristos 				 "No Reachable Authority",
160803e9293Schristos 				 "Network Error",
161803e9293Schristos 				 "Invalid Data" };
162803e9293Schristos 
163e2b1b9c0Schristos /*%
164e2b1b9c0Schristos  * "helper" type, which consists of a block of some type, and is linkable.
165e2b1b9c0Schristos  * For it to work, sizeof(dns_msgblock_t) must be a multiple of the pointer
166e2b1b9c0Schristos  * size, or the allocated elements will not be aligned correctly.
167e2b1b9c0Schristos  */
168e2b1b9c0Schristos struct dns_msgblock {
169e2b1b9c0Schristos 	unsigned int count;
170e2b1b9c0Schristos 	unsigned int remaining;
171e2b1b9c0Schristos 	ISC_LINK(dns_msgblock_t) link;
172e2b1b9c0Schristos }; /* dynamically sized */
173e2b1b9c0Schristos 
174c0b5d9fbSchristos static dns_msgblock_t *
175e2b1b9c0Schristos msgblock_allocate(isc_mem_t *, unsigned int, unsigned int);
176e2b1b9c0Schristos 
177e2b1b9c0Schristos #define msgblock_get(block, type) \
178e2b1b9c0Schristos 	((type *)msgblock_internalget(block, sizeof(type)))
179e2b1b9c0Schristos 
180c0b5d9fbSchristos static void *
181e2b1b9c0Schristos msgblock_internalget(dns_msgblock_t *, unsigned int);
182e2b1b9c0Schristos 
183c0b5d9fbSchristos static void
184e2b1b9c0Schristos msgblock_reset(dns_msgblock_t *);
185e2b1b9c0Schristos 
186c0b5d9fbSchristos static void
187e2b1b9c0Schristos msgblock_free(isc_mem_t *, dns_msgblock_t *, unsigned int);
188e2b1b9c0Schristos 
189e2b1b9c0Schristos static void
190e2b1b9c0Schristos logfmtpacket(dns_message_t *message, const char *description,
191e2b1b9c0Schristos 	     const isc_sockaddr_t *address, isc_logcategory_t *category,
192e2b1b9c0Schristos 	     isc_logmodule_t *module, const dns_master_style_t *style,
193e2b1b9c0Schristos 	     int level, isc_mem_t *mctx);
194e2b1b9c0Schristos 
195e2b1b9c0Schristos /*
196e2b1b9c0Schristos  * Allocate a new dns_msgblock_t, and return a pointer to it.  If no memory
197e2b1b9c0Schristos  * is free, return NULL.
198e2b1b9c0Schristos  */
199c0b5d9fbSchristos static dns_msgblock_t *
msgblock_allocate(isc_mem_t * mctx,unsigned int sizeof_type,unsigned int count)200e2b1b9c0Schristos msgblock_allocate(isc_mem_t *mctx, unsigned int sizeof_type,
2019742fdb4Schristos 		  unsigned int count) {
202e2b1b9c0Schristos 	dns_msgblock_t *block;
203e2b1b9c0Schristos 	unsigned int length;
204e2b1b9c0Schristos 
205e2b1b9c0Schristos 	length = sizeof(dns_msgblock_t) + (sizeof_type * count);
206e2b1b9c0Schristos 
207e2b1b9c0Schristos 	block = isc_mem_get(mctx, length);
208e2b1b9c0Schristos 
209e2b1b9c0Schristos 	block->count = count;
210e2b1b9c0Schristos 	block->remaining = count;
211e2b1b9c0Schristos 
212e2b1b9c0Schristos 	ISC_LINK_INIT(block, link);
213e2b1b9c0Schristos 
214e2b1b9c0Schristos 	return (block);
215e2b1b9c0Schristos }
216e2b1b9c0Schristos 
217e2b1b9c0Schristos /*
218e2b1b9c0Schristos  * Return an element from the msgblock.  If no more are available, return
219e2b1b9c0Schristos  * NULL.
220e2b1b9c0Schristos  */
221c0b5d9fbSchristos static void *
msgblock_internalget(dns_msgblock_t * block,unsigned int sizeof_type)222e2b1b9c0Schristos msgblock_internalget(dns_msgblock_t *block, unsigned int sizeof_type) {
223e2b1b9c0Schristos 	void *ptr;
224e2b1b9c0Schristos 
2259742fdb4Schristos 	if (block == NULL || block->remaining == 0) {
226e2b1b9c0Schristos 		return (NULL);
2279742fdb4Schristos 	}
228e2b1b9c0Schristos 
229e2b1b9c0Schristos 	block->remaining--;
230e2b1b9c0Schristos 
2319742fdb4Schristos 	ptr = (((unsigned char *)block) + sizeof(dns_msgblock_t) +
2329742fdb4Schristos 	       (sizeof_type * block->remaining));
233e2b1b9c0Schristos 
234e2b1b9c0Schristos 	return (ptr);
235e2b1b9c0Schristos }
236e2b1b9c0Schristos 
237c0b5d9fbSchristos static void
msgblock_reset(dns_msgblock_t * block)238e2b1b9c0Schristos msgblock_reset(dns_msgblock_t *block) {
239e2b1b9c0Schristos 	block->remaining = block->count;
240e2b1b9c0Schristos }
241e2b1b9c0Schristos 
242e2b1b9c0Schristos /*
243e2b1b9c0Schristos  * Release memory associated with a message block.
244e2b1b9c0Schristos  */
245c0b5d9fbSchristos static void
msgblock_free(isc_mem_t * mctx,dns_msgblock_t * block,unsigned int sizeof_type)2469742fdb4Schristos msgblock_free(isc_mem_t *mctx, dns_msgblock_t *block,
2479742fdb4Schristos 	      unsigned int sizeof_type) {
248e2b1b9c0Schristos 	unsigned int length;
249e2b1b9c0Schristos 
250e2b1b9c0Schristos 	length = sizeof(dns_msgblock_t) + (sizeof_type * block->count);
251e2b1b9c0Schristos 
252e2b1b9c0Schristos 	isc_mem_put(mctx, block, length);
253e2b1b9c0Schristos }
254e2b1b9c0Schristos 
255e2b1b9c0Schristos /*
256e2b1b9c0Schristos  * Allocate a new dynamic buffer, and attach it to this message as the
257e2b1b9c0Schristos  * "current" buffer.  (which is always the last on the list, for our
258e2b1b9c0Schristos  * uses)
259e2b1b9c0Schristos  */
260c0b5d9fbSchristos static isc_result_t
newbuffer(dns_message_t * msg,unsigned int size)261e2b1b9c0Schristos newbuffer(dns_message_t *msg, unsigned int size) {
262e2b1b9c0Schristos 	isc_buffer_t *dynbuf;
263e2b1b9c0Schristos 
264e2b1b9c0Schristos 	dynbuf = NULL;
2659742fdb4Schristos 	isc_buffer_allocate(msg->mctx, &dynbuf, size);
266e2b1b9c0Schristos 
267e2b1b9c0Schristos 	ISC_LIST_APPEND(msg->scratchpad, dynbuf, link);
268e2b1b9c0Schristos 	return (ISC_R_SUCCESS);
269e2b1b9c0Schristos }
270e2b1b9c0Schristos 
271c0b5d9fbSchristos static isc_buffer_t *
currentbuffer(dns_message_t * msg)272e2b1b9c0Schristos currentbuffer(dns_message_t *msg) {
273e2b1b9c0Schristos 	isc_buffer_t *dynbuf;
274e2b1b9c0Schristos 
275e2b1b9c0Schristos 	dynbuf = ISC_LIST_TAIL(msg->scratchpad);
276e2b1b9c0Schristos 	INSIST(dynbuf != NULL);
277e2b1b9c0Schristos 
278e2b1b9c0Schristos 	return (dynbuf);
279e2b1b9c0Schristos }
280e2b1b9c0Schristos 
281c0b5d9fbSchristos static void
releaserdata(dns_message_t * msg,dns_rdata_t * rdata)282e2b1b9c0Schristos releaserdata(dns_message_t *msg, dns_rdata_t *rdata) {
283e2b1b9c0Schristos 	ISC_LIST_PREPEND(msg->freerdata, rdata, link);
284e2b1b9c0Schristos }
285e2b1b9c0Schristos 
286c0b5d9fbSchristos static dns_rdata_t *
newrdata(dns_message_t * msg)287e2b1b9c0Schristos newrdata(dns_message_t *msg) {
288e2b1b9c0Schristos 	dns_msgblock_t *msgblock;
289e2b1b9c0Schristos 	dns_rdata_t *rdata;
290e2b1b9c0Schristos 
291e2b1b9c0Schristos 	rdata = ISC_LIST_HEAD(msg->freerdata);
292e2b1b9c0Schristos 	if (rdata != NULL) {
293e2b1b9c0Schristos 		ISC_LIST_UNLINK(msg->freerdata, rdata, link);
294e2b1b9c0Schristos 		return (rdata);
295e2b1b9c0Schristos 	}
296e2b1b9c0Schristos 
297e2b1b9c0Schristos 	msgblock = ISC_LIST_TAIL(msg->rdatas);
298e2b1b9c0Schristos 	rdata = msgblock_get(msgblock, dns_rdata_t);
299e2b1b9c0Schristos 	if (rdata == NULL) {
300e2b1b9c0Schristos 		msgblock = msgblock_allocate(msg->mctx, sizeof(dns_rdata_t),
301e2b1b9c0Schristos 					     RDATA_COUNT);
3029742fdb4Schristos 		if (msgblock == NULL) {
303e2b1b9c0Schristos 			return (NULL);
3049742fdb4Schristos 		}
305e2b1b9c0Schristos 
306e2b1b9c0Schristos 		ISC_LIST_APPEND(msg->rdatas, msgblock, link);
307e2b1b9c0Schristos 
308e2b1b9c0Schristos 		rdata = msgblock_get(msgblock, dns_rdata_t);
309e2b1b9c0Schristos 	}
310e2b1b9c0Schristos 
311e2b1b9c0Schristos 	dns_rdata_init(rdata);
312e2b1b9c0Schristos 	return (rdata);
313e2b1b9c0Schristos }
314e2b1b9c0Schristos 
315c0b5d9fbSchristos static void
releaserdatalist(dns_message_t * msg,dns_rdatalist_t * rdatalist)316e2b1b9c0Schristos releaserdatalist(dns_message_t *msg, dns_rdatalist_t *rdatalist) {
317e2b1b9c0Schristos 	ISC_LIST_PREPEND(msg->freerdatalist, rdatalist, link);
318e2b1b9c0Schristos }
319e2b1b9c0Schristos 
320c0b5d9fbSchristos static dns_rdatalist_t *
newrdatalist(dns_message_t * msg)321e2b1b9c0Schristos newrdatalist(dns_message_t *msg) {
322e2b1b9c0Schristos 	dns_msgblock_t *msgblock;
323e2b1b9c0Schristos 	dns_rdatalist_t *rdatalist;
324e2b1b9c0Schristos 
325e2b1b9c0Schristos 	rdatalist = ISC_LIST_HEAD(msg->freerdatalist);
326e2b1b9c0Schristos 	if (rdatalist != NULL) {
327e2b1b9c0Schristos 		ISC_LIST_UNLINK(msg->freerdatalist, rdatalist, link);
328e2b1b9c0Schristos 		goto out;
329e2b1b9c0Schristos 	}
330e2b1b9c0Schristos 
331e2b1b9c0Schristos 	msgblock = ISC_LIST_TAIL(msg->rdatalists);
332e2b1b9c0Schristos 	rdatalist = msgblock_get(msgblock, dns_rdatalist_t);
333e2b1b9c0Schristos 	if (rdatalist == NULL) {
3349742fdb4Schristos 		msgblock = msgblock_allocate(msg->mctx, sizeof(dns_rdatalist_t),
335e2b1b9c0Schristos 					     RDATALIST_COUNT);
3369742fdb4Schristos 		if (msgblock == NULL) {
337e2b1b9c0Schristos 			return (NULL);
3389742fdb4Schristos 		}
339e2b1b9c0Schristos 
340e2b1b9c0Schristos 		ISC_LIST_APPEND(msg->rdatalists, msgblock, link);
341e2b1b9c0Schristos 
342e2b1b9c0Schristos 		rdatalist = msgblock_get(msgblock, dns_rdatalist_t);
343e2b1b9c0Schristos 	}
344e2b1b9c0Schristos out:
3459742fdb4Schristos 	if (rdatalist != NULL) {
346e2b1b9c0Schristos 		dns_rdatalist_init(rdatalist);
3479742fdb4Schristos 	}
348e2b1b9c0Schristos 
349e2b1b9c0Schristos 	return (rdatalist);
350e2b1b9c0Schristos }
351e2b1b9c0Schristos 
352c0b5d9fbSchristos static dns_offsets_t *
newoffsets(dns_message_t * msg)353e2b1b9c0Schristos newoffsets(dns_message_t *msg) {
354e2b1b9c0Schristos 	dns_msgblock_t *msgblock;
355e2b1b9c0Schristos 	dns_offsets_t *offsets;
356e2b1b9c0Schristos 
357e2b1b9c0Schristos 	msgblock = ISC_LIST_TAIL(msg->offsets);
358e2b1b9c0Schristos 	offsets = msgblock_get(msgblock, dns_offsets_t);
359e2b1b9c0Schristos 	if (offsets == NULL) {
3609742fdb4Schristos 		msgblock = msgblock_allocate(msg->mctx, sizeof(dns_offsets_t),
361e2b1b9c0Schristos 					     OFFSET_COUNT);
3629742fdb4Schristos 		if (msgblock == NULL) {
363e2b1b9c0Schristos 			return (NULL);
3649742fdb4Schristos 		}
365e2b1b9c0Schristos 
366e2b1b9c0Schristos 		ISC_LIST_APPEND(msg->offsets, msgblock, link);
367e2b1b9c0Schristos 
368e2b1b9c0Schristos 		offsets = msgblock_get(msgblock, dns_offsets_t);
369e2b1b9c0Schristos 	}
370e2b1b9c0Schristos 
371e2b1b9c0Schristos 	return (offsets);
372e2b1b9c0Schristos }
373e2b1b9c0Schristos 
374c0b5d9fbSchristos static void
msginitheader(dns_message_t * m)375e2b1b9c0Schristos msginitheader(dns_message_t *m) {
376e2b1b9c0Schristos 	m->id = 0;
377e2b1b9c0Schristos 	m->flags = 0;
378e2b1b9c0Schristos 	m->rcode = 0;
379e2b1b9c0Schristos 	m->opcode = 0;
380e2b1b9c0Schristos 	m->rdclass = 0;
381e2b1b9c0Schristos }
382e2b1b9c0Schristos 
383c0b5d9fbSchristos static void
msginitprivate(dns_message_t * m)384e2b1b9c0Schristos msginitprivate(dns_message_t *m) {
385e2b1b9c0Schristos 	unsigned int i;
386e2b1b9c0Schristos 
387e2b1b9c0Schristos 	for (i = 0; i < DNS_SECTION_MAX; i++) {
388e2b1b9c0Schristos 		m->cursors[i] = NULL;
389e2b1b9c0Schristos 		m->counts[i] = 0;
390e2b1b9c0Schristos 	}
391e2b1b9c0Schristos 	m->opt = NULL;
392e2b1b9c0Schristos 	m->sig0 = NULL;
393e2b1b9c0Schristos 	m->sig0name = NULL;
394e2b1b9c0Schristos 	m->tsig = NULL;
395e2b1b9c0Schristos 	m->tsigname = NULL;
396e2b1b9c0Schristos 	m->state = DNS_SECTION_ANY; /* indicate nothing parsed or rendered */
397e2b1b9c0Schristos 	m->opt_reserved = 0;
398e2b1b9c0Schristos 	m->sig_reserved = 0;
399e2b1b9c0Schristos 	m->reserved = 0;
400e2b1b9c0Schristos 	m->padding = 0;
401e2b1b9c0Schristos 	m->padding_off = 0;
402e2b1b9c0Schristos 	m->buffer = NULL;
403e2b1b9c0Schristos }
404e2b1b9c0Schristos 
405c0b5d9fbSchristos static void
msginittsig(dns_message_t * m)406e2b1b9c0Schristos msginittsig(dns_message_t *m) {
407e2b1b9c0Schristos 	m->tsigstatus = dns_rcode_noerror;
408e2b1b9c0Schristos 	m->querytsigstatus = dns_rcode_noerror;
409e2b1b9c0Schristos 	m->tsigkey = NULL;
410e2b1b9c0Schristos 	m->tsigctx = NULL;
411e2b1b9c0Schristos 	m->sigstart = -1;
412e2b1b9c0Schristos 	m->sig0key = NULL;
413e2b1b9c0Schristos 	m->sig0status = dns_rcode_noerror;
414e2b1b9c0Schristos 	m->timeadjust = 0;
415e2b1b9c0Schristos }
416e2b1b9c0Schristos 
417e2b1b9c0Schristos /*
418e2b1b9c0Schristos  * Init elements to default state.  Used both when allocating a new element
419e2b1b9c0Schristos  * and when resetting one.
420e2b1b9c0Schristos  */
421c0b5d9fbSchristos static void
msginit(dns_message_t * m)422e2b1b9c0Schristos msginit(dns_message_t *m) {
423e2b1b9c0Schristos 	msginitheader(m);
424e2b1b9c0Schristos 	msginitprivate(m);
425e2b1b9c0Schristos 	msginittsig(m);
426e2b1b9c0Schristos 	m->header_ok = 0;
427e2b1b9c0Schristos 	m->question_ok = 0;
428e2b1b9c0Schristos 	m->tcp_continuation = 0;
429e2b1b9c0Schristos 	m->verified_sig = 0;
430e2b1b9c0Schristos 	m->verify_attempted = 0;
431e2b1b9c0Schristos 	m->order = NULL;
432e2b1b9c0Schristos 	m->order_arg.env = NULL;
433e2b1b9c0Schristos 	m->order_arg.acl = NULL;
434e2b1b9c0Schristos 	m->order_arg.element = NULL;
435e2b1b9c0Schristos 	m->query.base = NULL;
436e2b1b9c0Schristos 	m->query.length = 0;
437e2b1b9c0Schristos 	m->free_query = 0;
438e2b1b9c0Schristos 	m->saved.base = NULL;
439e2b1b9c0Schristos 	m->saved.length = 0;
440e2b1b9c0Schristos 	m->free_saved = 0;
441e2b1b9c0Schristos 	m->cc_ok = 0;
442e2b1b9c0Schristos 	m->cc_bad = 0;
443e2b1b9c0Schristos 	m->tkey = 0;
444e2b1b9c0Schristos 	m->rdclass_set = 0;
445e2b1b9c0Schristos 	m->querytsig = NULL;
4469742fdb4Schristos 	m->indent.string = "\t";
4479742fdb4Schristos 	m->indent.count = 0;
448e2b1b9c0Schristos }
449e2b1b9c0Schristos 
450c0b5d9fbSchristos static void
msgresetnames(dns_message_t * msg,unsigned int first_section)451e2b1b9c0Schristos msgresetnames(dns_message_t *msg, unsigned int first_section) {
452e2b1b9c0Schristos 	unsigned int i;
453e2b1b9c0Schristos 	dns_name_t *name, *next_name;
454e2b1b9c0Schristos 	dns_rdataset_t *rds, *next_rds;
455e2b1b9c0Schristos 
456e2b1b9c0Schristos 	/*
457e2b1b9c0Schristos 	 * Clean up name lists by calling the rdataset disassociate function.
458e2b1b9c0Schristos 	 */
459e2b1b9c0Schristos 	for (i = first_section; i < DNS_SECTION_MAX; i++) {
460e2b1b9c0Schristos 		name = ISC_LIST_HEAD(msg->sections[i]);
461e2b1b9c0Schristos 		while (name != NULL) {
462e2b1b9c0Schristos 			next_name = ISC_LIST_NEXT(name, link);
463e2b1b9c0Schristos 			ISC_LIST_UNLINK(msg->sections[i], name, link);
464e2b1b9c0Schristos 
465e2b1b9c0Schristos 			rds = ISC_LIST_HEAD(name->list);
466e2b1b9c0Schristos 			while (rds != NULL) {
467e2b1b9c0Schristos 				next_rds = ISC_LIST_NEXT(rds, link);
468e2b1b9c0Schristos 				ISC_LIST_UNLINK(name->list, rds, link);
469e2b1b9c0Schristos 
470e2b1b9c0Schristos 				INSIST(dns_rdataset_isassociated(rds));
471e2b1b9c0Schristos 				dns_rdataset_disassociate(rds);
472e2b1b9c0Schristos 				isc_mempool_put(msg->rdspool, rds);
473e2b1b9c0Schristos 				rds = next_rds;
474e2b1b9c0Schristos 			}
475fadf0758Schristos 			dns_message_puttempname(msg, &name);
476e2b1b9c0Schristos 			name = next_name;
477e2b1b9c0Schristos 		}
478e2b1b9c0Schristos 	}
479e2b1b9c0Schristos }
480e2b1b9c0Schristos 
481e2b1b9c0Schristos static void
msgresetopt(dns_message_t * msg)4829742fdb4Schristos msgresetopt(dns_message_t *msg) {
483e2b1b9c0Schristos 	if (msg->opt != NULL) {
484e2b1b9c0Schristos 		if (msg->opt_reserved > 0) {
485e2b1b9c0Schristos 			dns_message_renderrelease(msg, msg->opt_reserved);
486e2b1b9c0Schristos 			msg->opt_reserved = 0;
487e2b1b9c0Schristos 		}
488e2b1b9c0Schristos 		INSIST(dns_rdataset_isassociated(msg->opt));
489e2b1b9c0Schristos 		dns_rdataset_disassociate(msg->opt);
490e2b1b9c0Schristos 		isc_mempool_put(msg->rdspool, msg->opt);
491e2b1b9c0Schristos 		msg->opt = NULL;
492e2b1b9c0Schristos 		msg->cc_ok = 0;
493e2b1b9c0Schristos 		msg->cc_bad = 0;
494e2b1b9c0Schristos 	}
495e2b1b9c0Schristos }
496e2b1b9c0Schristos 
497e2b1b9c0Schristos static void
msgresetsigs(dns_message_t * msg,bool replying)498f2e20987Schristos msgresetsigs(dns_message_t *msg, bool replying) {
499e2b1b9c0Schristos 	if (msg->sig_reserved > 0) {
500e2b1b9c0Schristos 		dns_message_renderrelease(msg, msg->sig_reserved);
501e2b1b9c0Schristos 		msg->sig_reserved = 0;
502e2b1b9c0Schristos 	}
503e2b1b9c0Schristos 	if (msg->tsig != NULL) {
504e2b1b9c0Schristos 		INSIST(dns_rdataset_isassociated(msg->tsig));
505e2b1b9c0Schristos 		INSIST(msg->namepool != NULL);
506e2b1b9c0Schristos 		if (replying) {
507e2b1b9c0Schristos 			INSIST(msg->querytsig == NULL);
508e2b1b9c0Schristos 			msg->querytsig = msg->tsig;
509e2b1b9c0Schristos 		} else {
510e2b1b9c0Schristos 			dns_rdataset_disassociate(msg->tsig);
511e2b1b9c0Schristos 			isc_mempool_put(msg->rdspool, msg->tsig);
512e2b1b9c0Schristos 			if (msg->querytsig != NULL) {
513e2b1b9c0Schristos 				dns_rdataset_disassociate(msg->querytsig);
514e2b1b9c0Schristos 				isc_mempool_put(msg->rdspool, msg->querytsig);
515e2b1b9c0Schristos 			}
516e2b1b9c0Schristos 		}
517fadf0758Schristos 		dns_message_puttempname(msg, &msg->tsigname);
518e2b1b9c0Schristos 		msg->tsig = NULL;
519e2b1b9c0Schristos 	} else if (msg->querytsig != NULL && !replying) {
520e2b1b9c0Schristos 		dns_rdataset_disassociate(msg->querytsig);
521e2b1b9c0Schristos 		isc_mempool_put(msg->rdspool, msg->querytsig);
522e2b1b9c0Schristos 		msg->querytsig = NULL;
523e2b1b9c0Schristos 	}
524e2b1b9c0Schristos 	if (msg->sig0 != NULL) {
525e2b1b9c0Schristos 		INSIST(dns_rdataset_isassociated(msg->sig0));
526e2b1b9c0Schristos 		dns_rdataset_disassociate(msg->sig0);
527e2b1b9c0Schristos 		isc_mempool_put(msg->rdspool, msg->sig0);
528e2b1b9c0Schristos 		if (msg->sig0name != NULL) {
529fadf0758Schristos 			dns_message_puttempname(msg, &msg->sig0name);
530e2b1b9c0Schristos 		}
531e2b1b9c0Schristos 		msg->sig0 = NULL;
532e2b1b9c0Schristos 		msg->sig0name = NULL;
533e2b1b9c0Schristos 	}
534e2b1b9c0Schristos }
535e2b1b9c0Schristos 
536e2b1b9c0Schristos /*
537e2b1b9c0Schristos  * Free all but one (or everything) for this message.  This is used by
53873584a28Schristos  * both dns_message_reset() and dns__message_destroy().
539e2b1b9c0Schristos  */
540e2b1b9c0Schristos static void
msgreset(dns_message_t * msg,bool everything)541f2e20987Schristos msgreset(dns_message_t *msg, bool everything) {
542e2b1b9c0Schristos 	dns_msgblock_t *msgblock, *next_msgblock;
543e2b1b9c0Schristos 	isc_buffer_t *dynbuf, *next_dynbuf;
544e2b1b9c0Schristos 	dns_rdata_t *rdata;
545e2b1b9c0Schristos 	dns_rdatalist_t *rdatalist;
546e2b1b9c0Schristos 
547e2b1b9c0Schristos 	msgresetnames(msg, 0);
548e2b1b9c0Schristos 	msgresetopt(msg);
549f2e20987Schristos 	msgresetsigs(msg, false);
550e2b1b9c0Schristos 
551e2b1b9c0Schristos 	/*
552e2b1b9c0Schristos 	 * Clean up linked lists.
553e2b1b9c0Schristos 	 */
554e2b1b9c0Schristos 
555e2b1b9c0Schristos 	/*
556e2b1b9c0Schristos 	 * Run through the free lists, and just unlink anything found there.
557e2b1b9c0Schristos 	 * The memory isn't lost since these are part of message blocks we
558e2b1b9c0Schristos 	 * have allocated.
559e2b1b9c0Schristos 	 */
560e2b1b9c0Schristos 	rdata = ISC_LIST_HEAD(msg->freerdata);
561e2b1b9c0Schristos 	while (rdata != NULL) {
562e2b1b9c0Schristos 		ISC_LIST_UNLINK(msg->freerdata, rdata, link);
563e2b1b9c0Schristos 		rdata = ISC_LIST_HEAD(msg->freerdata);
564e2b1b9c0Schristos 	}
565e2b1b9c0Schristos 	rdatalist = ISC_LIST_HEAD(msg->freerdatalist);
566e2b1b9c0Schristos 	while (rdatalist != NULL) {
567e2b1b9c0Schristos 		ISC_LIST_UNLINK(msg->freerdatalist, rdatalist, link);
568e2b1b9c0Schristos 		rdatalist = ISC_LIST_HEAD(msg->freerdatalist);
569e2b1b9c0Schristos 	}
570e2b1b9c0Schristos 
571e2b1b9c0Schristos 	dynbuf = ISC_LIST_HEAD(msg->scratchpad);
572e2b1b9c0Schristos 	INSIST(dynbuf != NULL);
573e2b1b9c0Schristos 	if (!everything) {
574e2b1b9c0Schristos 		isc_buffer_clear(dynbuf);
575e2b1b9c0Schristos 		dynbuf = ISC_LIST_NEXT(dynbuf, link);
576e2b1b9c0Schristos 	}
577e2b1b9c0Schristos 	while (dynbuf != NULL) {
578e2b1b9c0Schristos 		next_dynbuf = ISC_LIST_NEXT(dynbuf, link);
579e2b1b9c0Schristos 		ISC_LIST_UNLINK(msg->scratchpad, dynbuf, link);
580e2b1b9c0Schristos 		isc_buffer_free(&dynbuf);
581e2b1b9c0Schristos 		dynbuf = next_dynbuf;
582e2b1b9c0Schristos 	}
583e2b1b9c0Schristos 
584e2b1b9c0Schristos 	msgblock = ISC_LIST_HEAD(msg->rdatas);
585e2b1b9c0Schristos 	if (!everything && msgblock != NULL) {
586e2b1b9c0Schristos 		msgblock_reset(msgblock);
587e2b1b9c0Schristos 		msgblock = ISC_LIST_NEXT(msgblock, link);
588e2b1b9c0Schristos 	}
589e2b1b9c0Schristos 	while (msgblock != NULL) {
590e2b1b9c0Schristos 		next_msgblock = ISC_LIST_NEXT(msgblock, link);
591e2b1b9c0Schristos 		ISC_LIST_UNLINK(msg->rdatas, msgblock, link);
592e2b1b9c0Schristos 		msgblock_free(msg->mctx, msgblock, sizeof(dns_rdata_t));
593e2b1b9c0Schristos 		msgblock = next_msgblock;
594e2b1b9c0Schristos 	}
595e2b1b9c0Schristos 
596e2b1b9c0Schristos 	/*
597e2b1b9c0Schristos 	 * rdatalists could be empty.
598e2b1b9c0Schristos 	 */
599e2b1b9c0Schristos 
600e2b1b9c0Schristos 	msgblock = ISC_LIST_HEAD(msg->rdatalists);
601e2b1b9c0Schristos 	if (!everything && msgblock != NULL) {
602e2b1b9c0Schristos 		msgblock_reset(msgblock);
603e2b1b9c0Schristos 		msgblock = ISC_LIST_NEXT(msgblock, link);
604e2b1b9c0Schristos 	}
605e2b1b9c0Schristos 	while (msgblock != NULL) {
606e2b1b9c0Schristos 		next_msgblock = ISC_LIST_NEXT(msgblock, link);
607e2b1b9c0Schristos 		ISC_LIST_UNLINK(msg->rdatalists, msgblock, link);
608e2b1b9c0Schristos 		msgblock_free(msg->mctx, msgblock, sizeof(dns_rdatalist_t));
609e2b1b9c0Schristos 		msgblock = next_msgblock;
610e2b1b9c0Schristos 	}
611e2b1b9c0Schristos 
612e2b1b9c0Schristos 	msgblock = ISC_LIST_HEAD(msg->offsets);
613e2b1b9c0Schristos 	if (!everything && msgblock != NULL) {
614e2b1b9c0Schristos 		msgblock_reset(msgblock);
615e2b1b9c0Schristos 		msgblock = ISC_LIST_NEXT(msgblock, link);
616e2b1b9c0Schristos 	}
617e2b1b9c0Schristos 	while (msgblock != NULL) {
618e2b1b9c0Schristos 		next_msgblock = ISC_LIST_NEXT(msgblock, link);
619e2b1b9c0Schristos 		ISC_LIST_UNLINK(msg->offsets, msgblock, link);
620e2b1b9c0Schristos 		msgblock_free(msg->mctx, msgblock, sizeof(dns_offsets_t));
621e2b1b9c0Schristos 		msgblock = next_msgblock;
622e2b1b9c0Schristos 	}
623e2b1b9c0Schristos 
624e2b1b9c0Schristos 	if (msg->tsigkey != NULL) {
625e2b1b9c0Schristos 		dns_tsigkey_detach(&msg->tsigkey);
626e2b1b9c0Schristos 		msg->tsigkey = NULL;
627e2b1b9c0Schristos 	}
628e2b1b9c0Schristos 
6299742fdb4Schristos 	if (msg->tsigctx != NULL) {
630e2b1b9c0Schristos 		dst_context_destroy(&msg->tsigctx);
6319742fdb4Schristos 	}
632e2b1b9c0Schristos 
633e2b1b9c0Schristos 	if (msg->query.base != NULL) {
6349742fdb4Schristos 		if (msg->free_query != 0) {
635e2b1b9c0Schristos 			isc_mem_put(msg->mctx, msg->query.base,
636e2b1b9c0Schristos 				    msg->query.length);
6379742fdb4Schristos 		}
638e2b1b9c0Schristos 		msg->query.base = NULL;
639e2b1b9c0Schristos 		msg->query.length = 0;
640e2b1b9c0Schristos 	}
641e2b1b9c0Schristos 
642e2b1b9c0Schristos 	if (msg->saved.base != NULL) {
6439742fdb4Schristos 		if (msg->free_saved != 0) {
644e2b1b9c0Schristos 			isc_mem_put(msg->mctx, msg->saved.base,
645e2b1b9c0Schristos 				    msg->saved.length);
6469742fdb4Schristos 		}
647e2b1b9c0Schristos 		msg->saved.base = NULL;
648e2b1b9c0Schristos 		msg->saved.length = 0;
649e2b1b9c0Schristos 	}
650e2b1b9c0Schristos 
651e2b1b9c0Schristos 	/*
652e2b1b9c0Schristos 	 * cleanup the buffer cleanup list
653e2b1b9c0Schristos 	 */
654e2b1b9c0Schristos 	dynbuf = ISC_LIST_HEAD(msg->cleanup);
655e2b1b9c0Schristos 	while (dynbuf != NULL) {
656e2b1b9c0Schristos 		next_dynbuf = ISC_LIST_NEXT(dynbuf, link);
657e2b1b9c0Schristos 		ISC_LIST_UNLINK(msg->cleanup, dynbuf, link);
658e2b1b9c0Schristos 		isc_buffer_free(&dynbuf);
659e2b1b9c0Schristos 		dynbuf = next_dynbuf;
660e2b1b9c0Schristos 	}
661e2b1b9c0Schristos 
662e2b1b9c0Schristos 	/*
663e2b1b9c0Schristos 	 * Set other bits to normal default values.
664e2b1b9c0Schristos 	 */
6659742fdb4Schristos 	if (!everything) {
666e2b1b9c0Schristos 		msginit(msg);
6679742fdb4Schristos 	}
668e2b1b9c0Schristos 
669e2b1b9c0Schristos 	ENSURE(isc_mempool_getallocated(msg->namepool) == 0);
670e2b1b9c0Schristos 	ENSURE(isc_mempool_getallocated(msg->rdspool) == 0);
671e2b1b9c0Schristos }
672e2b1b9c0Schristos 
673e2b1b9c0Schristos static unsigned int
spacefortsig(dns_tsigkey_t * key,int otherlen)674e2b1b9c0Schristos spacefortsig(dns_tsigkey_t *key, int otherlen) {
675e2b1b9c0Schristos 	isc_region_t r1, r2;
676e2b1b9c0Schristos 	unsigned int x;
677e2b1b9c0Schristos 	isc_result_t result;
678e2b1b9c0Schristos 
679e2b1b9c0Schristos 	/*
680e2b1b9c0Schristos 	 * The space required for an TSIG record is:
681e2b1b9c0Schristos 	 *
682e2b1b9c0Schristos 	 *	n1 bytes for the name
683e2b1b9c0Schristos 	 *	2 bytes for the type
684e2b1b9c0Schristos 	 *	2 bytes for the class
685e2b1b9c0Schristos 	 *	4 bytes for the ttl
686e2b1b9c0Schristos 	 *	2 bytes for the rdlength
687e2b1b9c0Schristos 	 *	n2 bytes for the algorithm name
688e2b1b9c0Schristos 	 *	6 bytes for the time signed
689e2b1b9c0Schristos 	 *	2 bytes for the fudge
690e2b1b9c0Schristos 	 *	2 bytes for the MAC size
691e2b1b9c0Schristos 	 *	x bytes for the MAC
692e2b1b9c0Schristos 	 *	2 bytes for the original id
693e2b1b9c0Schristos 	 *	2 bytes for the error
694e2b1b9c0Schristos 	 *	2 bytes for the other data length
695e2b1b9c0Schristos 	 *	y bytes for the other data (at most)
696e2b1b9c0Schristos 	 * ---------------------------------
697e2b1b9c0Schristos 	 *     26 + n1 + n2 + x + y bytes
698e2b1b9c0Schristos 	 */
699e2b1b9c0Schristos 
700e2b1b9c0Schristos 	dns_name_toregion(&key->name, &r1);
701e2b1b9c0Schristos 	dns_name_toregion(key->algorithm, &r2);
7029742fdb4Schristos 	if (key->key == NULL) {
703e2b1b9c0Schristos 		x = 0;
7049742fdb4Schristos 	} else {
705e2b1b9c0Schristos 		result = dst_key_sigsize(key->key, &x);
7069742fdb4Schristos 		if (result != ISC_R_SUCCESS) {
707e2b1b9c0Schristos 			x = 0;
708e2b1b9c0Schristos 		}
7099742fdb4Schristos 	}
710e2b1b9c0Schristos 	return (26 + r1.length + r2.length + x + otherlen);
711e2b1b9c0Schristos }
712e2b1b9c0Schristos 
71373584a28Schristos void
dns_message_create(isc_mem_t * mctx,unsigned int intent,dns_message_t ** msgp)7149742fdb4Schristos dns_message_create(isc_mem_t *mctx, unsigned int intent, dns_message_t **msgp) {
715fadf0758Schristos 	dns_message_t *m = NULL;
716fadf0758Schristos 	isc_buffer_t *dynbuf = NULL;
717e2b1b9c0Schristos 	unsigned int i;
718e2b1b9c0Schristos 
719e2b1b9c0Schristos 	REQUIRE(mctx != NULL);
720e2b1b9c0Schristos 	REQUIRE(msgp != NULL);
721e2b1b9c0Schristos 	REQUIRE(*msgp == NULL);
7229742fdb4Schristos 	REQUIRE(intent == DNS_MESSAGE_INTENTPARSE ||
7239742fdb4Schristos 		intent == DNS_MESSAGE_INTENTRENDER);
724e2b1b9c0Schristos 
725e2b1b9c0Schristos 	m = isc_mem_get(mctx, sizeof(dns_message_t));
726fadf0758Schristos 	*m = (dns_message_t){ .from_to_wire = intent };
727fadf0758Schristos 	isc_mem_attach(mctx, &m->mctx);
728e2b1b9c0Schristos 	msginit(m);
729e2b1b9c0Schristos 
7309742fdb4Schristos 	for (i = 0; i < DNS_SECTION_MAX; i++) {
731e2b1b9c0Schristos 		ISC_LIST_INIT(m->sections[i]);
7329742fdb4Schristos 	}
733e2b1b9c0Schristos 
734e2b1b9c0Schristos 	ISC_LIST_INIT(m->scratchpad);
735e2b1b9c0Schristos 	ISC_LIST_INIT(m->cleanup);
736e2b1b9c0Schristos 	ISC_LIST_INIT(m->rdatas);
737e2b1b9c0Schristos 	ISC_LIST_INIT(m->rdatalists);
738e2b1b9c0Schristos 	ISC_LIST_INIT(m->offsets);
739e2b1b9c0Schristos 	ISC_LIST_INIT(m->freerdata);
740e2b1b9c0Schristos 	ISC_LIST_INIT(m->freerdatalist);
741e2b1b9c0Schristos 
742fadf0758Schristos 	isc_mempool_create(m->mctx, sizeof(dns_fixedname_t), &m->namepool);
743c0b5d9fbSchristos 	isc_mempool_setfillcount(m->namepool, NAME_FILLCOUNT);
744c0b5d9fbSchristos 	isc_mempool_setfreemax(m->namepool, NAME_FREEMAX);
745e2b1b9c0Schristos 	isc_mempool_setname(m->namepool, "msg:names");
746e2b1b9c0Schristos 
7479742fdb4Schristos 	isc_mempool_create(m->mctx, sizeof(dns_rdataset_t), &m->rdspool);
748c0b5d9fbSchristos 	isc_mempool_setfillcount(m->rdspool, RDATASET_FILLCOUNT);
749c0b5d9fbSchristos 	isc_mempool_setfreemax(m->rdspool, RDATASET_FREEMAX);
750e2b1b9c0Schristos 	isc_mempool_setname(m->rdspool, "msg:rdataset");
751e2b1b9c0Schristos 
7529742fdb4Schristos 	isc_buffer_allocate(mctx, &dynbuf, SCRATCHPAD_SIZE);
753e2b1b9c0Schristos 	ISC_LIST_APPEND(m->scratchpad, dynbuf, link);
754e2b1b9c0Schristos 
75573584a28Schristos 	isc_refcount_init(&m->refcount, 1);
756fadf0758Schristos 	m->magic = DNS_MESSAGE_MAGIC;
75773584a28Schristos 
758e2b1b9c0Schristos 	*msgp = m;
759e2b1b9c0Schristos }
760e2b1b9c0Schristos 
761e2b1b9c0Schristos void
dns_message_reset(dns_message_t * msg,unsigned int intent)762e2b1b9c0Schristos dns_message_reset(dns_message_t *msg, unsigned int intent) {
763e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
7649742fdb4Schristos 	REQUIRE(intent == DNS_MESSAGE_INTENTPARSE ||
7659742fdb4Schristos 		intent == DNS_MESSAGE_INTENTRENDER);
766e2b1b9c0Schristos 
767f2e20987Schristos 	msgreset(msg, false);
768e2b1b9c0Schristos 	msg->from_to_wire = intent;
769e2b1b9c0Schristos }
770e2b1b9c0Schristos 
77173584a28Schristos static void
dns__message_destroy(dns_message_t * msg)77273584a28Schristos dns__message_destroy(dns_message_t *msg) {
77373584a28Schristos 	REQUIRE(msg != NULL);
77473584a28Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
775e2b1b9c0Schristos 
776f2e20987Schristos 	msgreset(msg, true);
777e2b1b9c0Schristos 	isc_mempool_destroy(&msg->namepool);
778e2b1b9c0Schristos 	isc_mempool_destroy(&msg->rdspool);
77973584a28Schristos 	isc_refcount_destroy(&msg->refcount);
780e2b1b9c0Schristos 	msg->magic = 0;
781e2b1b9c0Schristos 	isc_mem_putanddetach(&msg->mctx, msg, sizeof(dns_message_t));
782e2b1b9c0Schristos }
783e2b1b9c0Schristos 
78473584a28Schristos void
dns_message_attach(dns_message_t * source,dns_message_t ** target)78573584a28Schristos dns_message_attach(dns_message_t *source, dns_message_t **target) {
78673584a28Schristos 	REQUIRE(DNS_MESSAGE_VALID(source));
78773584a28Schristos 
78873584a28Schristos 	isc_refcount_increment(&source->refcount);
78973584a28Schristos 	*target = source;
79073584a28Schristos }
79173584a28Schristos 
79273584a28Schristos void
dns_message_detach(dns_message_t ** messagep)79373584a28Schristos dns_message_detach(dns_message_t **messagep) {
79473584a28Schristos 	REQUIRE(messagep != NULL && DNS_MESSAGE_VALID(*messagep));
79573584a28Schristos 	dns_message_t *msg = *messagep;
79673584a28Schristos 	*messagep = NULL;
79773584a28Schristos 
79873584a28Schristos 	if (isc_refcount_decrement(&msg->refcount) == 1) {
79973584a28Schristos 		dns__message_destroy(msg);
80073584a28Schristos 	}
80173584a28Schristos }
80273584a28Schristos 
803e2b1b9c0Schristos static isc_result_t
findname(dns_name_t ** foundname,const dns_name_t * target,dns_namelist_t * section)804e2b1b9c0Schristos findname(dns_name_t **foundname, const dns_name_t *target,
8059742fdb4Schristos 	 dns_namelist_t *section) {
806e2b1b9c0Schristos 	dns_name_t *curr;
807e2b1b9c0Schristos 
8089742fdb4Schristos 	for (curr = ISC_LIST_TAIL(*section); curr != NULL;
8099742fdb4Schristos 	     curr = ISC_LIST_PREV(curr, link))
8109742fdb4Schristos 	{
811e2b1b9c0Schristos 		if (dns_name_equal(curr, target)) {
8129742fdb4Schristos 			if (foundname != NULL) {
813e2b1b9c0Schristos 				*foundname = curr;
8149742fdb4Schristos 			}
815e2b1b9c0Schristos 			return (ISC_R_SUCCESS);
816e2b1b9c0Schristos 		}
817e2b1b9c0Schristos 	}
818e2b1b9c0Schristos 
819e2b1b9c0Schristos 	return (ISC_R_NOTFOUND);
820e2b1b9c0Schristos }
821e2b1b9c0Schristos 
822e2b1b9c0Schristos isc_result_t
dns_message_find(const dns_name_t * name,dns_rdataclass_t rdclass,dns_rdatatype_t type,dns_rdatatype_t covers,dns_rdataset_t ** rdataset)823e2b1b9c0Schristos dns_message_find(const dns_name_t *name, dns_rdataclass_t rdclass,
824e2b1b9c0Schristos 		 dns_rdatatype_t type, dns_rdatatype_t covers,
8259742fdb4Schristos 		 dns_rdataset_t **rdataset) {
826e2b1b9c0Schristos 	dns_rdataset_t *curr;
827e2b1b9c0Schristos 
828e2b1b9c0Schristos 	REQUIRE(name != NULL);
829e2b1b9c0Schristos 	REQUIRE(rdataset == NULL || *rdataset == NULL);
830e2b1b9c0Schristos 
8319742fdb4Schristos 	for (curr = ISC_LIST_TAIL(name->list); curr != NULL;
8329742fdb4Schristos 	     curr = ISC_LIST_PREV(curr, link))
8339742fdb4Schristos 	{
8349742fdb4Schristos 		if (curr->rdclass == rdclass && curr->type == type &&
835*4ac1c27eSchristos 		    curr->covers == covers)
836*4ac1c27eSchristos 		{
8379742fdb4Schristos 			if (rdataset != NULL) {
838e2b1b9c0Schristos 				*rdataset = curr;
8399742fdb4Schristos 			}
840e2b1b9c0Schristos 			return (ISC_R_SUCCESS);
841e2b1b9c0Schristos 		}
842e2b1b9c0Schristos 	}
843e2b1b9c0Schristos 
844e2b1b9c0Schristos 	return (ISC_R_NOTFOUND);
845e2b1b9c0Schristos }
846e2b1b9c0Schristos 
847e2b1b9c0Schristos isc_result_t
dns_message_findtype(const dns_name_t * name,dns_rdatatype_t type,dns_rdatatype_t covers,dns_rdataset_t ** rdataset)848e2b1b9c0Schristos dns_message_findtype(const dns_name_t *name, dns_rdatatype_t type,
8499742fdb4Schristos 		     dns_rdatatype_t covers, dns_rdataset_t **rdataset) {
850e2b1b9c0Schristos 	dns_rdataset_t *curr;
851e2b1b9c0Schristos 
852e2b1b9c0Schristos 	REQUIRE(name != NULL);
853e2b1b9c0Schristos 	REQUIRE(rdataset == NULL || *rdataset == NULL);
854e2b1b9c0Schristos 
8559742fdb4Schristos 	for (curr = ISC_LIST_TAIL(name->list); curr != NULL;
8569742fdb4Schristos 	     curr = ISC_LIST_PREV(curr, link))
8579742fdb4Schristos 	{
858e2b1b9c0Schristos 		if (curr->type == type && curr->covers == covers) {
8599742fdb4Schristos 			if (ISC_UNLIKELY(rdataset != NULL)) {
860e2b1b9c0Schristos 				*rdataset = curr;
8619742fdb4Schristos 			}
862e2b1b9c0Schristos 			return (ISC_R_SUCCESS);
863e2b1b9c0Schristos 		}
864e2b1b9c0Schristos 	}
865e2b1b9c0Schristos 
866e2b1b9c0Schristos 	return (ISC_R_NOTFOUND);
867e2b1b9c0Schristos }
868e2b1b9c0Schristos 
869e2b1b9c0Schristos /*
870e2b1b9c0Schristos  * Read a name from buffer "source".
871e2b1b9c0Schristos  */
872e2b1b9c0Schristos static isc_result_t
getname(dns_name_t * name,isc_buffer_t * source,dns_message_t * msg,dns_decompress_t * dctx)873e2b1b9c0Schristos getname(dns_name_t *name, isc_buffer_t *source, dns_message_t *msg,
8749742fdb4Schristos 	dns_decompress_t *dctx) {
875e2b1b9c0Schristos 	isc_buffer_t *scratch;
876e2b1b9c0Schristos 	isc_result_t result;
877e2b1b9c0Schristos 	unsigned int tries;
878e2b1b9c0Schristos 
879e2b1b9c0Schristos 	scratch = currentbuffer(msg);
880e2b1b9c0Schristos 
881e2b1b9c0Schristos 	/*
882e2b1b9c0Schristos 	 * First try:  use current buffer.
883e2b1b9c0Schristos 	 * Second try:  allocate a new buffer and use that.
884e2b1b9c0Schristos 	 */
885e2b1b9c0Schristos 	tries = 0;
886e2b1b9c0Schristos 	while (tries < 2) {
8879742fdb4Schristos 		result = dns_name_fromwire(name, source, dctx, 0, scratch);
888e2b1b9c0Schristos 
889e2b1b9c0Schristos 		if (result == ISC_R_NOSPACE) {
890e2b1b9c0Schristos 			tries++;
891e2b1b9c0Schristos 
892e2b1b9c0Schristos 			result = newbuffer(msg, SCRATCHPAD_SIZE);
8939742fdb4Schristos 			if (result != ISC_R_SUCCESS) {
894e2b1b9c0Schristos 				return (result);
8959742fdb4Schristos 			}
896e2b1b9c0Schristos 
897e2b1b9c0Schristos 			scratch = currentbuffer(msg);
898e2b1b9c0Schristos 			dns_name_reset(name);
899e2b1b9c0Schristos 		} else {
900e2b1b9c0Schristos 			return (result);
901e2b1b9c0Schristos 		}
902e2b1b9c0Schristos 	}
903e2b1b9c0Schristos 
904c0b5d9fbSchristos 	UNREACHABLE();
905e2b1b9c0Schristos }
906e2b1b9c0Schristos 
907e2b1b9c0Schristos static isc_result_t
getrdata(isc_buffer_t * source,dns_message_t * msg,dns_decompress_t * dctx,dns_rdataclass_t rdclass,dns_rdatatype_t rdtype,unsigned int rdatalen,dns_rdata_t * rdata)908e2b1b9c0Schristos getrdata(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
909e2b1b9c0Schristos 	 dns_rdataclass_t rdclass, dns_rdatatype_t rdtype,
9109742fdb4Schristos 	 unsigned int rdatalen, dns_rdata_t *rdata) {
911e2b1b9c0Schristos 	isc_buffer_t *scratch;
912e2b1b9c0Schristos 	isc_result_t result;
913e2b1b9c0Schristos 	unsigned int tries;
914e2b1b9c0Schristos 	unsigned int trysize;
915e2b1b9c0Schristos 
916e2b1b9c0Schristos 	scratch = currentbuffer(msg);
917e2b1b9c0Schristos 
918e2b1b9c0Schristos 	isc_buffer_setactive(source, rdatalen);
919e2b1b9c0Schristos 
920e2b1b9c0Schristos 	/*
921e2b1b9c0Schristos 	 * First try:  use current buffer.
922e2b1b9c0Schristos 	 * Second try:  allocate a new buffer of size
923e2b1b9c0Schristos 	 *     max(SCRATCHPAD_SIZE, 2 * compressed_rdatalen)
924e2b1b9c0Schristos 	 *     (the data will fit if it was not more than 50% compressed)
925e2b1b9c0Schristos 	 * Subsequent tries: double buffer size on each try.
926e2b1b9c0Schristos 	 */
927e2b1b9c0Schristos 	tries = 0;
928e2b1b9c0Schristos 	trysize = 0;
929e2b1b9c0Schristos 	/* XXX possibly change this to a while (tries < 2) loop */
930e2b1b9c0Schristos 	for (;;) {
9319742fdb4Schristos 		result = dns_rdata_fromwire(rdata, rdclass, rdtype, source,
9329742fdb4Schristos 					    dctx, 0, scratch);
933e2b1b9c0Schristos 
934e2b1b9c0Schristos 		if (result == ISC_R_NOSPACE) {
935e2b1b9c0Schristos 			if (tries == 0) {
936e2b1b9c0Schristos 				trysize = 2 * rdatalen;
9379742fdb4Schristos 				if (trysize < SCRATCHPAD_SIZE) {
938e2b1b9c0Schristos 					trysize = SCRATCHPAD_SIZE;
9399742fdb4Schristos 				}
940e2b1b9c0Schristos 			} else {
941e2b1b9c0Schristos 				INSIST(trysize != 0);
9429742fdb4Schristos 				if (trysize >= 65535) {
943e2b1b9c0Schristos 					return (ISC_R_NOSPACE);
9449742fdb4Schristos 				}
945e2b1b9c0Schristos 				/* XXX DNS_R_RRTOOLONG? */
946e2b1b9c0Schristos 				trysize *= 2;
947e2b1b9c0Schristos 			}
948e2b1b9c0Schristos 			tries++;
949e2b1b9c0Schristos 			result = newbuffer(msg, trysize);
9509742fdb4Schristos 			if (result != ISC_R_SUCCESS) {
951e2b1b9c0Schristos 				return (result);
9529742fdb4Schristos 			}
953e2b1b9c0Schristos 
954e2b1b9c0Schristos 			scratch = currentbuffer(msg);
955e2b1b9c0Schristos 		} else {
956e2b1b9c0Schristos 			return (result);
957e2b1b9c0Schristos 		}
958e2b1b9c0Schristos 	}
959e2b1b9c0Schristos }
960e2b1b9c0Schristos 
961e2b1b9c0Schristos #define DO_ERROR(r)                          \
962e2b1b9c0Schristos 	do {                                 \
9639742fdb4Schristos 		if (best_effort) {           \
964f2e20987Schristos 			seen_problem = true; \
9659742fdb4Schristos 		} else {                     \
966e2b1b9c0Schristos 			result = r;          \
967e2b1b9c0Schristos 			goto cleanup;        \
968e2b1b9c0Schristos 		}                            \
969245a9365Srillig 	} while (0)
970e2b1b9c0Schristos 
971e2b1b9c0Schristos static isc_result_t
getquestions(isc_buffer_t * source,dns_message_t * msg,dns_decompress_t * dctx,unsigned int options)972e2b1b9c0Schristos getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
9739742fdb4Schristos 	     unsigned int options) {
974e2b1b9c0Schristos 	isc_region_t r;
975e2b1b9c0Schristos 	unsigned int count;
976fadf0758Schristos 	dns_name_t *name = NULL;
977fadf0758Schristos 	dns_name_t *name2 = NULL;
978fadf0758Schristos 	dns_rdataset_t *rdataset = NULL;
979fadf0758Schristos 	dns_rdatalist_t *rdatalist = NULL;
980e2b1b9c0Schristos 	isc_result_t result;
981e2b1b9c0Schristos 	dns_rdatatype_t rdtype;
982e2b1b9c0Schristos 	dns_rdataclass_t rdclass;
983fadf0758Schristos 	dns_namelist_t *section = &msg->sections[DNS_SECTION_QUESTION];
984fadf0758Schristos 	bool best_effort = ((options & DNS_MESSAGEPARSE_BESTEFFORT) != 0);
985fadf0758Schristos 	bool seen_problem = false;
986fadf0758Schristos 	bool free_name = false;
987e2b1b9c0Schristos 
988e2b1b9c0Schristos 	for (count = 0; count < msg->counts[DNS_SECTION_QUESTION]; count++) {
989fadf0758Schristos 		name = NULL;
990fadf0758Schristos 		result = dns_message_gettempname(msg, &name);
991fadf0758Schristos 		if (result != ISC_R_SUCCESS) {
992e2b1b9c0Schristos 			goto cleanup;
993e2b1b9c0Schristos 		}
994fadf0758Schristos 		name->offsets = (unsigned char *)newoffsets(msg);
995fadf0758Schristos 		free_name = true;
996e2b1b9c0Schristos 
997e2b1b9c0Schristos 		/*
998e2b1b9c0Schristos 		 * Parse the name out of this packet.
999e2b1b9c0Schristos 		 */
1000e2b1b9c0Schristos 		isc_buffer_remainingregion(source, &r);
1001e2b1b9c0Schristos 		isc_buffer_setactive(source, r.length);
1002e2b1b9c0Schristos 		result = getname(name, source, msg, dctx);
10039742fdb4Schristos 		if (result != ISC_R_SUCCESS) {
1004e2b1b9c0Schristos 			goto cleanup;
10059742fdb4Schristos 		}
1006e2b1b9c0Schristos 
1007e2b1b9c0Schristos 		/*
1008e2b1b9c0Schristos 		 * Run through the section, looking to see if this name
1009e2b1b9c0Schristos 		 * is already there.  If it is found, put back the allocated
1010e2b1b9c0Schristos 		 * name since we no longer need it, and set our name pointer
1011e2b1b9c0Schristos 		 * to point to the name we found.
1012e2b1b9c0Schristos 		 */
1013e2b1b9c0Schristos 		result = findname(&name2, name, section);
1014e2b1b9c0Schristos 
1015e2b1b9c0Schristos 		/*
1016e2b1b9c0Schristos 		 * If it is the first name in the section, accept it.
1017e2b1b9c0Schristos 		 *
1018e2b1b9c0Schristos 		 * If it is not, but is not the same as the name already
1019e2b1b9c0Schristos 		 * in the question section, append to the section.  Note that
1020e2b1b9c0Schristos 		 * here in the question section this is illegal, so return
1021e2b1b9c0Schristos 		 * FORMERR.  In the future, check the opcode to see if
1022e2b1b9c0Schristos 		 * this should be legal or not.  In either case we no longer
1023e2b1b9c0Schristos 		 * need this name pointer.
1024e2b1b9c0Schristos 		 */
1025e2b1b9c0Schristos 		if (result != ISC_R_SUCCESS) {
10269742fdb4Schristos 			if (!ISC_LIST_EMPTY(*section)) {
1027e2b1b9c0Schristos 				DO_ERROR(DNS_R_FORMERR);
10289742fdb4Schristos 			}
1029e2b1b9c0Schristos 			ISC_LIST_APPEND(*section, name, link);
1030f2e20987Schristos 			free_name = false;
1031e2b1b9c0Schristos 		} else {
1032fadf0758Schristos 			dns_message_puttempname(msg, &name);
1033e2b1b9c0Schristos 			name = name2;
1034e2b1b9c0Schristos 			name2 = NULL;
1035f2e20987Schristos 			free_name = false;
1036e2b1b9c0Schristos 		}
1037e2b1b9c0Schristos 
1038e2b1b9c0Schristos 		/*
1039e2b1b9c0Schristos 		 * Get type and class.
1040e2b1b9c0Schristos 		 */
1041e2b1b9c0Schristos 		isc_buffer_remainingregion(source, &r);
1042e2b1b9c0Schristos 		if (r.length < 4) {
1043e2b1b9c0Schristos 			result = ISC_R_UNEXPECTEDEND;
1044e2b1b9c0Schristos 			goto cleanup;
1045e2b1b9c0Schristos 		}
1046e2b1b9c0Schristos 		rdtype = isc_buffer_getuint16(source);
1047e2b1b9c0Schristos 		rdclass = isc_buffer_getuint16(source);
1048e2b1b9c0Schristos 
1049e2b1b9c0Schristos 		/*
1050e2b1b9c0Schristos 		 * If this class is different than the one we already read,
1051e2b1b9c0Schristos 		 * this is an error.
1052e2b1b9c0Schristos 		 */
1053e2b1b9c0Schristos 		if (msg->rdclass_set == 0) {
1054e2b1b9c0Schristos 			msg->rdclass = rdclass;
1055e2b1b9c0Schristos 			msg->rdclass_set = 1;
10569742fdb4Schristos 		} else if (msg->rdclass != rdclass) {
1057e2b1b9c0Schristos 			DO_ERROR(DNS_R_FORMERR);
10589742fdb4Schristos 		}
1059e2b1b9c0Schristos 
1060e2b1b9c0Schristos 		/*
1061e2b1b9c0Schristos 		 * Is this a TKEY query?
1062e2b1b9c0Schristos 		 */
10639742fdb4Schristos 		if (rdtype == dns_rdatatype_tkey) {
1064e2b1b9c0Schristos 			msg->tkey = 1;
10659742fdb4Schristos 		}
1066e2b1b9c0Schristos 
1067e2b1b9c0Schristos 		/*
1068e2b1b9c0Schristos 		 * Can't ask the same question twice.
1069e2b1b9c0Schristos 		 */
1070e2b1b9c0Schristos 		result = dns_message_find(name, rdclass, rdtype, 0, NULL);
10719742fdb4Schristos 		if (result == ISC_R_SUCCESS) {
1072e2b1b9c0Schristos 			DO_ERROR(DNS_R_FORMERR);
10739742fdb4Schristos 		}
1074e2b1b9c0Schristos 
1075e2b1b9c0Schristos 		/*
1076e2b1b9c0Schristos 		 * Allocate a new rdatalist.
1077e2b1b9c0Schristos 		 */
1078e2b1b9c0Schristos 		rdatalist = newrdatalist(msg);
1079e2b1b9c0Schristos 		if (rdatalist == NULL) {
1080e2b1b9c0Schristos 			result = ISC_R_NOMEMORY;
1081e2b1b9c0Schristos 			goto cleanup;
1082e2b1b9c0Schristos 		}
1083e2b1b9c0Schristos 		rdataset = isc_mempool_get(msg->rdspool);
1084e2b1b9c0Schristos 		if (rdataset == NULL) {
1085e2b1b9c0Schristos 			result = ISC_R_NOMEMORY;
1086e2b1b9c0Schristos 			goto cleanup;
1087e2b1b9c0Schristos 		}
1088e2b1b9c0Schristos 
1089e2b1b9c0Schristos 		/*
1090e2b1b9c0Schristos 		 * Convert rdatalist to rdataset, and attach the latter to
1091e2b1b9c0Schristos 		 * the name.
1092e2b1b9c0Schristos 		 */
1093e2b1b9c0Schristos 		rdatalist->type = rdtype;
1094e2b1b9c0Schristos 		rdatalist->rdclass = rdclass;
1095e2b1b9c0Schristos 
1096e2b1b9c0Schristos 		dns_rdataset_init(rdataset);
1097e2b1b9c0Schristos 		result = dns_rdatalist_tordataset(rdatalist, rdataset);
10989742fdb4Schristos 		if (result != ISC_R_SUCCESS) {
1099e2b1b9c0Schristos 			goto cleanup;
11009742fdb4Schristos 		}
1101e2b1b9c0Schristos 
1102e2b1b9c0Schristos 		rdataset->attributes |= DNS_RDATASETATTR_QUESTION;
1103e2b1b9c0Schristos 
1104e2b1b9c0Schristos 		ISC_LIST_APPEND(name->list, rdataset, link);
1105e2b1b9c0Schristos 		rdataset = NULL;
1106e2b1b9c0Schristos 	}
1107e2b1b9c0Schristos 
11089742fdb4Schristos 	if (seen_problem) {
1109e2b1b9c0Schristos 		return (DNS_R_RECOVERABLE);
11109742fdb4Schristos 	}
1111e2b1b9c0Schristos 	return (ISC_R_SUCCESS);
1112e2b1b9c0Schristos 
1113e2b1b9c0Schristos cleanup:
1114e2b1b9c0Schristos 	if (rdataset != NULL) {
1115e2b1b9c0Schristos 		INSIST(!dns_rdataset_isassociated(rdataset));
1116e2b1b9c0Schristos 		isc_mempool_put(msg->rdspool, rdataset);
1117e2b1b9c0Schristos 	}
11189742fdb4Schristos 	if (free_name) {
1119fadf0758Schristos 		dns_message_puttempname(msg, &name);
11209742fdb4Schristos 	}
1121e2b1b9c0Schristos 
1122e2b1b9c0Schristos 	return (result);
1123e2b1b9c0Schristos }
1124e2b1b9c0Schristos 
1125f2e20987Schristos static bool
update(dns_section_t section,dns_rdataclass_t rdclass)1126e2b1b9c0Schristos update(dns_section_t section, dns_rdataclass_t rdclass) {
11279742fdb4Schristos 	if (section == DNS_SECTION_PREREQUISITE) {
1128f2e20987Schristos 		return (rdclass == dns_rdataclass_any ||
1129f2e20987Schristos 			rdclass == dns_rdataclass_none);
11309742fdb4Schristos 	}
11319742fdb4Schristos 	if (section == DNS_SECTION_UPDATE) {
1132f2e20987Schristos 		return (rdclass == dns_rdataclass_any);
11339742fdb4Schristos 	}
1134f2e20987Schristos 	return (false);
1135e2b1b9c0Schristos }
1136e2b1b9c0Schristos 
1137e2b1b9c0Schristos /*
1138e2b1b9c0Schristos  * Check to confirm that all DNSSEC records (DS, NSEC, NSEC3) have
1139e2b1b9c0Schristos  * covering RRSIGs.
1140e2b1b9c0Schristos  */
1141f2e20987Schristos static bool
auth_signed(dns_namelist_t * section)1142e2b1b9c0Schristos auth_signed(dns_namelist_t *section) {
1143e2b1b9c0Schristos 	dns_name_t *name;
1144e2b1b9c0Schristos 
11459742fdb4Schristos 	for (name = ISC_LIST_HEAD(*section); name != NULL;
1146e2b1b9c0Schristos 	     name = ISC_LIST_NEXT(name, link))
1147e2b1b9c0Schristos 	{
1148e2b1b9c0Schristos 		int auth_dnssec = 0, auth_rrsig = 0;
1149e2b1b9c0Schristos 		dns_rdataset_t *rds;
1150e2b1b9c0Schristos 
11519742fdb4Schristos 		for (rds = ISC_LIST_HEAD(name->list); rds != NULL;
1152e2b1b9c0Schristos 		     rds = ISC_LIST_NEXT(rds, link))
1153e2b1b9c0Schristos 		{
1154e2b1b9c0Schristos 			switch (rds->type) {
1155e2b1b9c0Schristos 			case dns_rdatatype_ds:
1156e2b1b9c0Schristos 				auth_dnssec |= 0x1;
1157e2b1b9c0Schristos 				break;
1158e2b1b9c0Schristos 			case dns_rdatatype_nsec:
1159e2b1b9c0Schristos 				auth_dnssec |= 0x2;
1160e2b1b9c0Schristos 				break;
1161e2b1b9c0Schristos 			case dns_rdatatype_nsec3:
1162e2b1b9c0Schristos 				auth_dnssec |= 0x4;
1163e2b1b9c0Schristos 				break;
1164e2b1b9c0Schristos 			case dns_rdatatype_rrsig:
1165e2b1b9c0Schristos 				break;
1166e2b1b9c0Schristos 			default:
1167e2b1b9c0Schristos 				continue;
1168e2b1b9c0Schristos 			}
1169e2b1b9c0Schristos 
1170e2b1b9c0Schristos 			switch (rds->covers) {
1171e2b1b9c0Schristos 			case dns_rdatatype_ds:
1172e2b1b9c0Schristos 				auth_rrsig |= 0x1;
1173e2b1b9c0Schristos 				break;
1174e2b1b9c0Schristos 			case dns_rdatatype_nsec:
1175e2b1b9c0Schristos 				auth_rrsig |= 0x2;
1176e2b1b9c0Schristos 				break;
1177e2b1b9c0Schristos 			case dns_rdatatype_nsec3:
1178e2b1b9c0Schristos 				auth_rrsig |= 0x4;
1179e2b1b9c0Schristos 				break;
1180e2b1b9c0Schristos 			default:
1181e2b1b9c0Schristos 				break;
1182e2b1b9c0Schristos 			}
1183e2b1b9c0Schristos 		}
1184e2b1b9c0Schristos 
11859742fdb4Schristos 		if (auth_dnssec != auth_rrsig) {
1186f2e20987Schristos 			return (false);
1187e2b1b9c0Schristos 		}
11889742fdb4Schristos 	}
1189e2b1b9c0Schristos 
1190f2e20987Schristos 	return (true);
1191e2b1b9c0Schristos }
1192e2b1b9c0Schristos 
1193e2b1b9c0Schristos static isc_result_t
getsection(isc_buffer_t * source,dns_message_t * msg,dns_decompress_t * dctx,dns_section_t sectionid,unsigned int options)1194e2b1b9c0Schristos getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
11959742fdb4Schristos 	   dns_section_t sectionid, unsigned int options) {
1196e2b1b9c0Schristos 	isc_region_t r;
1197e2b1b9c0Schristos 	unsigned int count, rdatalen;
1198e2b1b9c0Schristos 	dns_name_t *name = NULL;
1199e2b1b9c0Schristos 	dns_name_t *name2 = NULL;
12000377c12bSchristos 	dns_rdataset_t *rdataset = NULL;
1201fadf0758Schristos 	dns_rdatalist_t *rdatalist = NULL;
1202e2b1b9c0Schristos 	isc_result_t result;
1203e2b1b9c0Schristos 	dns_rdatatype_t rdtype, covers;
1204e2b1b9c0Schristos 	dns_rdataclass_t rdclass;
1205fadf0758Schristos 	dns_rdata_t *rdata = NULL;
1206e2b1b9c0Schristos 	dns_ttl_t ttl;
1207fadf0758Schristos 	dns_namelist_t *section = &msg->sections[sectionid];
1208fadf0758Schristos 	bool free_name = false, free_rdataset = false, seen_problem = false;
1209fadf0758Schristos 	bool preserve_order = ((options & DNS_MESSAGEPARSE_PRESERVEORDER) != 0);
1210fadf0758Schristos 	bool best_effort = ((options & DNS_MESSAGEPARSE_BESTEFFORT) != 0);
121177279b93Schristos 	bool isedns, issigzero, istsig;
1212e2b1b9c0Schristos 
1213e2b1b9c0Schristos 	for (count = 0; count < msg->counts[sectionid]; count++) {
1214e2b1b9c0Schristos 		int recstart = source->current;
1215f2e20987Schristos 		bool skip_name_search, skip_type_search;
1216e2b1b9c0Schristos 
1217f2e20987Schristos 		skip_name_search = false;
1218f2e20987Schristos 		skip_type_search = false;
1219f2e20987Schristos 		free_rdataset = false;
122077279b93Schristos 		isedns = false;
122177279b93Schristos 		issigzero = false;
122277279b93Schristos 		istsig = false;
1223e2b1b9c0Schristos 
1224fadf0758Schristos 		name = NULL;
1225fadf0758Schristos 		result = dns_message_gettempname(msg, &name);
1226fadf0758Schristos 		if (result != ISC_R_SUCCESS) {
1227e2b1b9c0Schristos 			goto cleanup;
1228e2b1b9c0Schristos 		}
1229fadf0758Schristos 		name->offsets = (unsigned char *)newoffsets(msg);
1230fadf0758Schristos 		free_name = true;
1231e2b1b9c0Schristos 
1232e2b1b9c0Schristos 		/*
1233e2b1b9c0Schristos 		 * Parse the name out of this packet.
1234e2b1b9c0Schristos 		 */
1235e2b1b9c0Schristos 		isc_buffer_remainingregion(source, &r);
1236e2b1b9c0Schristos 		isc_buffer_setactive(source, r.length);
1237e2b1b9c0Schristos 		result = getname(name, source, msg, dctx);
12389742fdb4Schristos 		if (result != ISC_R_SUCCESS) {
1239e2b1b9c0Schristos 			goto cleanup;
12409742fdb4Schristos 		}
1241e2b1b9c0Schristos 
1242e2b1b9c0Schristos 		/*
1243e2b1b9c0Schristos 		 * Get type, class, ttl, and rdatalen.  Verify that at least
1244e2b1b9c0Schristos 		 * rdatalen bytes remain.  (Some of this is deferred to
1245e2b1b9c0Schristos 		 * later.)
1246e2b1b9c0Schristos 		 */
1247e2b1b9c0Schristos 		isc_buffer_remainingregion(source, &r);
1248e2b1b9c0Schristos 		if (r.length < 2 + 2 + 4 + 2) {
1249e2b1b9c0Schristos 			result = ISC_R_UNEXPECTEDEND;
1250e2b1b9c0Schristos 			goto cleanup;
1251e2b1b9c0Schristos 		}
1252e2b1b9c0Schristos 		rdtype = isc_buffer_getuint16(source);
1253e2b1b9c0Schristos 		rdclass = isc_buffer_getuint16(source);
1254e2b1b9c0Schristos 
1255e2b1b9c0Schristos 		/*
1256e2b1b9c0Schristos 		 * If there was no question section, we may not yet have
1257e2b1b9c0Schristos 		 * established a class.  Do so now.
1258e2b1b9c0Schristos 		 */
1259e2b1b9c0Schristos 		if (msg->rdclass_set == 0 &&
1260e2b1b9c0Schristos 		    rdtype != dns_rdatatype_opt &&  /* class is UDP SIZE */
1261e2b1b9c0Schristos 		    rdtype != dns_rdatatype_tsig && /* class is ANY */
12629742fdb4Schristos 		    rdtype != dns_rdatatype_tkey)
12639742fdb4Schristos 		{ /* class is undefined */
1264e2b1b9c0Schristos 			msg->rdclass = rdclass;
1265e2b1b9c0Schristos 			msg->rdclass_set = 1;
1266e2b1b9c0Schristos 		}
1267e2b1b9c0Schristos 
1268e2b1b9c0Schristos 		/*
1269e2b1b9c0Schristos 		 * If this class is different than the one in the question
1270e2b1b9c0Schristos 		 * section, bail.
1271e2b1b9c0Schristos 		 */
12729742fdb4Schristos 		if (msg->opcode != dns_opcode_update &&
12739742fdb4Schristos 		    rdtype != dns_rdatatype_tsig &&
12749742fdb4Schristos 		    rdtype != dns_rdatatype_opt &&
12759742fdb4Schristos 		    rdtype != dns_rdatatype_key &&  /* in a TKEY query */
12769742fdb4Schristos 		    rdtype != dns_rdatatype_sig &&  /* SIG(0) */
12779742fdb4Schristos 		    rdtype != dns_rdatatype_tkey && /* Win2000 TKEY */
12789742fdb4Schristos 		    msg->rdclass != dns_rdataclass_any &&
12799742fdb4Schristos 		    msg->rdclass != rdclass)
12809742fdb4Schristos 		{
1281e2b1b9c0Schristos 			DO_ERROR(DNS_R_FORMERR);
12829742fdb4Schristos 		}
1283e2b1b9c0Schristos 
1284e2b1b9c0Schristos 		/*
1285e2b1b9c0Schristos 		 * If this is not a TKEY query/response then the KEY
1286e2b1b9c0Schristos 		 * record's class needs to match.
1287e2b1b9c0Schristos 		 */
1288e2b1b9c0Schristos 		if (msg->opcode != dns_opcode_update && !msg->tkey &&
1289e2b1b9c0Schristos 		    rdtype == dns_rdatatype_key &&
1290e2b1b9c0Schristos 		    msg->rdclass != dns_rdataclass_any &&
1291e2b1b9c0Schristos 		    msg->rdclass != rdclass)
12929742fdb4Schristos 		{
1293e2b1b9c0Schristos 			DO_ERROR(DNS_R_FORMERR);
12949742fdb4Schristos 		}
1295e2b1b9c0Schristos 
1296e2b1b9c0Schristos 		/*
1297e2b1b9c0Schristos 		 * Special type handling for TSIG, OPT, and TKEY.
1298e2b1b9c0Schristos 		 */
1299e2b1b9c0Schristos 		if (rdtype == dns_rdatatype_tsig) {
1300e2b1b9c0Schristos 			/*
1301e2b1b9c0Schristos 			 * If it is a tsig, verify that it is in the
1302e2b1b9c0Schristos 			 * additional data section.
1303e2b1b9c0Schristos 			 */
1304e2b1b9c0Schristos 			if (sectionid != DNS_SECTION_ADDITIONAL ||
1305e2b1b9c0Schristos 			    rdclass != dns_rdataclass_any ||
13069742fdb4Schristos 			    count != msg->counts[sectionid] - 1)
13079742fdb4Schristos 			{
1308e2b1b9c0Schristos 				DO_ERROR(DNS_R_BADTSIG);
130977279b93Schristos 			} else {
1310f2e20987Schristos 				skip_name_search = true;
1311f2e20987Schristos 				skip_type_search = true;
131277279b93Schristos 				istsig = true;
131377279b93Schristos 			}
1314e2b1b9c0Schristos 		} else if (rdtype == dns_rdatatype_opt) {
1315e2b1b9c0Schristos 			/*
1316e2b1b9c0Schristos 			 * The name of an OPT record must be ".", it
1317e2b1b9c0Schristos 			 * must be in the additional data section, and
1318e2b1b9c0Schristos 			 * it must be the first OPT we've seen.
1319e2b1b9c0Schristos 			 */
1320e2b1b9c0Schristos 			if (!dns_name_equal(dns_rootname, name) ||
1321e2b1b9c0Schristos 			    sectionid != DNS_SECTION_ADDITIONAL ||
13229742fdb4Schristos 			    msg->opt != NULL)
13239742fdb4Schristos 			{
1324e2b1b9c0Schristos 				DO_ERROR(DNS_R_FORMERR);
132577279b93Schristos 			} else {
1326f2e20987Schristos 				skip_name_search = true;
1327f2e20987Schristos 				skip_type_search = true;
132877279b93Schristos 				isedns = true;
132977279b93Schristos 			}
1330e2b1b9c0Schristos 		} else if (rdtype == dns_rdatatype_tkey) {
1331e2b1b9c0Schristos 			/*
1332e2b1b9c0Schristos 			 * A TKEY must be in the additional section if this
1333e2b1b9c0Schristos 			 * is a query, and the answer section if this is a
1334e2b1b9c0Schristos 			 * response.  Unless it's a Win2000 client.
1335e2b1b9c0Schristos 			 *
1336e2b1b9c0Schristos 			 * Its class is ignored.
1337e2b1b9c0Schristos 			 */
1338e2b1b9c0Schristos 			dns_section_t tkeysection;
1339e2b1b9c0Schristos 
13409742fdb4Schristos 			if ((msg->flags & DNS_MESSAGEFLAG_QR) == 0) {
1341e2b1b9c0Schristos 				tkeysection = DNS_SECTION_ADDITIONAL;
13429742fdb4Schristos 			} else {
1343e2b1b9c0Schristos 				tkeysection = DNS_SECTION_ANSWER;
13449742fdb4Schristos 			}
1345e2b1b9c0Schristos 			if (sectionid != tkeysection &&
1346*4ac1c27eSchristos 			    sectionid != DNS_SECTION_ANSWER)
1347*4ac1c27eSchristos 			{
1348e2b1b9c0Schristos 				DO_ERROR(DNS_R_FORMERR);
1349e2b1b9c0Schristos 			}
13509742fdb4Schristos 		}
1351e2b1b9c0Schristos 
1352e2b1b9c0Schristos 		/*
1353e2b1b9c0Schristos 		 * ... now get ttl and rdatalen, and check buffer.
1354e2b1b9c0Schristos 		 */
1355e2b1b9c0Schristos 		ttl = isc_buffer_getuint32(source);
1356e2b1b9c0Schristos 		rdatalen = isc_buffer_getuint16(source);
1357e2b1b9c0Schristos 		r.length -= (2 + 2 + 4 + 2);
1358e2b1b9c0Schristos 		if (r.length < rdatalen) {
1359e2b1b9c0Schristos 			result = ISC_R_UNEXPECTEDEND;
1360e2b1b9c0Schristos 			goto cleanup;
1361e2b1b9c0Schristos 		}
1362e2b1b9c0Schristos 
1363e2b1b9c0Schristos 		/*
1364e2b1b9c0Schristos 		 * Read the rdata from the wire format.  Interpret the
1365e2b1b9c0Schristos 		 * rdata according to its actual class, even if it had a
1366e2b1b9c0Schristos 		 * DynDNS meta-class in the packet (unless this is a TSIG).
1367e2b1b9c0Schristos 		 * Then put the meta-class back into the finished rdata.
1368e2b1b9c0Schristos 		 */
1369e2b1b9c0Schristos 		rdata = newrdata(msg);
1370e2b1b9c0Schristos 		if (rdata == NULL) {
1371e2b1b9c0Schristos 			result = ISC_R_NOMEMORY;
1372e2b1b9c0Schristos 			goto cleanup;
1373e2b1b9c0Schristos 		}
1374e2b1b9c0Schristos 		if (msg->opcode == dns_opcode_update &&
1375*4ac1c27eSchristos 		    update(sectionid, rdclass))
1376*4ac1c27eSchristos 		{
1377e2b1b9c0Schristos 			if (rdatalen != 0) {
1378e2b1b9c0Schristos 				result = DNS_R_FORMERR;
1379e2b1b9c0Schristos 				goto cleanup;
1380e2b1b9c0Schristos 			}
1381e2b1b9c0Schristos 			/*
1382e2b1b9c0Schristos 			 * When the rdata is empty, the data pointer is
1383e2b1b9c0Schristos 			 * never dereferenced, but it must still be non-NULL.
1384e2b1b9c0Schristos 			 * Casting 1 rather than "" avoids warnings about
1385e2b1b9c0Schristos 			 * discarding the const attribute of a string,
1386e2b1b9c0Schristos 			 * for compilers that would warn about such things.
1387e2b1b9c0Schristos 			 */
1388e2b1b9c0Schristos 			rdata->data = (unsigned char *)1;
1389e2b1b9c0Schristos 			rdata->length = 0;
1390e2b1b9c0Schristos 			rdata->rdclass = rdclass;
1391e2b1b9c0Schristos 			rdata->type = rdtype;
1392e2b1b9c0Schristos 			rdata->flags = DNS_RDATA_UPDATE;
1393e2b1b9c0Schristos 			result = ISC_R_SUCCESS;
1394e2b1b9c0Schristos 		} else if (rdclass == dns_rdataclass_none &&
1395e2b1b9c0Schristos 			   msg->opcode == dns_opcode_update &&
13969742fdb4Schristos 			   sectionid == DNS_SECTION_UPDATE)
13979742fdb4Schristos 		{
1398e2b1b9c0Schristos 			result = getrdata(source, msg, dctx, msg->rdclass,
1399e2b1b9c0Schristos 					  rdtype, rdatalen, rdata);
14009742fdb4Schristos 		} else {
14019742fdb4Schristos 			result = getrdata(source, msg, dctx, rdclass, rdtype,
14029742fdb4Schristos 					  rdatalen, rdata);
14039742fdb4Schristos 		}
14049742fdb4Schristos 		if (result != ISC_R_SUCCESS) {
1405e2b1b9c0Schristos 			goto cleanup;
14069742fdb4Schristos 		}
1407e2b1b9c0Schristos 		rdata->rdclass = rdclass;
14089742fdb4Schristos 		if (rdtype == dns_rdatatype_rrsig && rdata->flags == 0) {
1409e2b1b9c0Schristos 			covers = dns_rdata_covers(rdata);
14109742fdb4Schristos 			if (covers == 0) {
1411e2b1b9c0Schristos 				DO_ERROR(DNS_R_FORMERR);
14129742fdb4Schristos 			}
1413e2b1b9c0Schristos 		} else if (rdtype == dns_rdatatype_sig /* SIG(0) */ &&
1414*4ac1c27eSchristos 			   rdata->flags == 0)
1415*4ac1c27eSchristos 		{
1416e2b1b9c0Schristos 			covers = dns_rdata_covers(rdata);
1417e2b1b9c0Schristos 			if (covers == 0) {
1418e2b1b9c0Schristos 				if (sectionid != DNS_SECTION_ADDITIONAL ||
1419*4ac1c27eSchristos 				    count != msg->counts[sectionid] - 1)
1420*4ac1c27eSchristos 				{
1421e2b1b9c0Schristos 					DO_ERROR(DNS_R_BADSIG0);
142277279b93Schristos 				} else {
1423f2e20987Schristos 					skip_name_search = true;
1424f2e20987Schristos 					skip_type_search = true;
1425f2e20987Schristos 					issigzero = true;
142677279b93Schristos 				}
1427e2b1b9c0Schristos 			} else {
1428e2b1b9c0Schristos 				if (msg->rdclass != dns_rdataclass_any &&
1429*4ac1c27eSchristos 				    msg->rdclass != rdclass)
1430*4ac1c27eSchristos 				{
1431e2b1b9c0Schristos 					DO_ERROR(DNS_R_FORMERR);
1432e2b1b9c0Schristos 				}
14339742fdb4Schristos 			}
14349742fdb4Schristos 		} else {
1435e2b1b9c0Schristos 			covers = 0;
14369742fdb4Schristos 		}
1437e2b1b9c0Schristos 
1438e2b1b9c0Schristos 		/*
1439e2b1b9c0Schristos 		 * Check the ownername of NSEC3 records
1440e2b1b9c0Schristos 		 */
1441e2b1b9c0Schristos 		if (rdtype == dns_rdatatype_nsec3 &&
14429742fdb4Schristos 		    !dns_rdata_checkowner(name, msg->rdclass, rdtype, false))
14439742fdb4Schristos 		{
1444e2b1b9c0Schristos 			result = DNS_R_BADOWNERNAME;
1445e2b1b9c0Schristos 			goto cleanup;
1446e2b1b9c0Schristos 		}
1447e2b1b9c0Schristos 
1448e2b1b9c0Schristos 		/*
1449e2b1b9c0Schristos 		 * If we are doing a dynamic update or this is a meta-type,
1450e2b1b9c0Schristos 		 * don't bother searching for a name, just append this one
1451e2b1b9c0Schristos 		 * to the end of the message.
1452e2b1b9c0Schristos 		 */
1453e2b1b9c0Schristos 		if (preserve_order || msg->opcode == dns_opcode_update ||
1454*4ac1c27eSchristos 		    skip_name_search)
1455*4ac1c27eSchristos 		{
145677279b93Schristos 			if (!isedns && !istsig && !issigzero) {
1457e2b1b9c0Schristos 				ISC_LIST_APPEND(*section, name, link);
1458f2e20987Schristos 				free_name = false;
1459e2b1b9c0Schristos 			}
1460e2b1b9c0Schristos 		} else {
1461e2b1b9c0Schristos 			/*
1462e2b1b9c0Schristos 			 * Run through the section, looking to see if this name
1463e2b1b9c0Schristos 			 * is already there.  If it is found, put back the
1464e2b1b9c0Schristos 			 * allocated name since we no longer need it, and set
1465e2b1b9c0Schristos 			 * our name pointer to point to the name we found.
1466e2b1b9c0Schristos 			 */
1467e2b1b9c0Schristos 			result = findname(&name2, name, section);
1468e2b1b9c0Schristos 
1469e2b1b9c0Schristos 			/*
1470e2b1b9c0Schristos 			 * If it is a new name, append to the section.
1471e2b1b9c0Schristos 			 */
1472e2b1b9c0Schristos 			if (result == ISC_R_SUCCESS) {
1473fadf0758Schristos 				dns_message_puttempname(msg, &name);
1474e2b1b9c0Schristos 				name = name2;
1475e2b1b9c0Schristos 			} else {
1476e2b1b9c0Schristos 				ISC_LIST_APPEND(*section, name, link);
1477e2b1b9c0Schristos 			}
1478f2e20987Schristos 			free_name = false;
1479e2b1b9c0Schristos 		}
1480e2b1b9c0Schristos 
1481e2b1b9c0Schristos 		/*
1482e2b1b9c0Schristos 		 * Search name for the particular type and class.
1483e2b1b9c0Schristos 		 * Skip this stage if in update mode or this is a meta-type.
1484e2b1b9c0Schristos 		 */
1485e2b1b9c0Schristos 		if (preserve_order || msg->opcode == dns_opcode_update ||
1486*4ac1c27eSchristos 		    skip_type_search)
1487*4ac1c27eSchristos 		{
1488e2b1b9c0Schristos 			result = ISC_R_NOTFOUND;
14899742fdb4Schristos 		} else {
1490e2b1b9c0Schristos 			/*
1491e2b1b9c0Schristos 			 * If this is a type that can only occur in
1492e2b1b9c0Schristos 			 * the question section, fail.
1493e2b1b9c0Schristos 			 */
14949742fdb4Schristos 			if (dns_rdatatype_questiononly(rdtype)) {
1495e2b1b9c0Schristos 				DO_ERROR(DNS_R_FORMERR);
14969742fdb4Schristos 			}
1497e2b1b9c0Schristos 
1498e2b1b9c0Schristos 			rdataset = NULL;
14999742fdb4Schristos 			result = dns_message_find(name, rdclass, rdtype, covers,
15009742fdb4Schristos 						  &rdataset);
1501e2b1b9c0Schristos 		}
1502e2b1b9c0Schristos 
1503e2b1b9c0Schristos 		/*
1504e2b1b9c0Schristos 		 * If we found an rdataset that matches, we need to
1505e2b1b9c0Schristos 		 * append this rdata to that set.  If we did not, we need
1506e2b1b9c0Schristos 		 * to create a new rdatalist, store the important bits there,
1507e2b1b9c0Schristos 		 * convert it to an rdataset, and link the latter to the name.
1508e2b1b9c0Schristos 		 * Yuck.  When appending, make certain that the type isn't
1509e2b1b9c0Schristos 		 * a singleton type, such as SOA or CNAME.
1510e2b1b9c0Schristos 		 *
1511e2b1b9c0Schristos 		 * Note that this check will be bypassed when preserving order,
1512e2b1b9c0Schristos 		 * the opcode is an update, or the type search is skipped.
1513e2b1b9c0Schristos 		 */
1514e2b1b9c0Schristos 		if (result == ISC_R_SUCCESS) {
1515e2b1b9c0Schristos 			if (dns_rdatatype_issingleton(rdtype)) {
1516e2b1b9c0Schristos 				dns_rdata_t *first;
1517e2b1b9c0Schristos 				dns_rdatalist_fromrdataset(rdataset,
1518e2b1b9c0Schristos 							   &rdatalist);
1519e2b1b9c0Schristos 				first = ISC_LIST_HEAD(rdatalist->rdata);
1520e2b1b9c0Schristos 				INSIST(first != NULL);
15219742fdb4Schristos 				if (dns_rdata_compare(rdata, first) != 0) {
1522e2b1b9c0Schristos 					DO_ERROR(DNS_R_FORMERR);
1523e2b1b9c0Schristos 				}
1524e2b1b9c0Schristos 			}
15259742fdb4Schristos 		}
1526e2b1b9c0Schristos 
1527e2b1b9c0Schristos 		if (result == ISC_R_NOTFOUND) {
1528e2b1b9c0Schristos 			rdataset = isc_mempool_get(msg->rdspool);
1529e2b1b9c0Schristos 			if (rdataset == NULL) {
1530e2b1b9c0Schristos 				result = ISC_R_NOMEMORY;
1531e2b1b9c0Schristos 				goto cleanup;
1532e2b1b9c0Schristos 			}
1533f2e20987Schristos 			free_rdataset = true;
1534e2b1b9c0Schristos 
1535e2b1b9c0Schristos 			rdatalist = newrdatalist(msg);
1536e2b1b9c0Schristos 			if (rdatalist == NULL) {
1537e2b1b9c0Schristos 				result = ISC_R_NOMEMORY;
1538e2b1b9c0Schristos 				goto cleanup;
1539e2b1b9c0Schristos 			}
1540e2b1b9c0Schristos 
1541e2b1b9c0Schristos 			rdatalist->type = rdtype;
1542e2b1b9c0Schristos 			rdatalist->covers = covers;
1543e2b1b9c0Schristos 			rdatalist->rdclass = rdclass;
1544e2b1b9c0Schristos 			rdatalist->ttl = ttl;
1545e2b1b9c0Schristos 
1546e2b1b9c0Schristos 			dns_rdataset_init(rdataset);
15479742fdb4Schristos 			RUNTIME_CHECK(
15489742fdb4Schristos 				dns_rdatalist_tordataset(rdatalist, rdataset) ==
15499742fdb4Schristos 				ISC_R_SUCCESS);
1550e2b1b9c0Schristos 			dns_rdataset_setownercase(rdataset, name);
1551e2b1b9c0Schristos 
155277279b93Schristos 			if (!isedns && !istsig && !issigzero) {
1553e2b1b9c0Schristos 				ISC_LIST_APPEND(name->list, rdataset, link);
1554f2e20987Schristos 				free_rdataset = false;
1555e2b1b9c0Schristos 			}
1556e2b1b9c0Schristos 		}
1557e2b1b9c0Schristos 
1558e2b1b9c0Schristos 		/*
1559e2b1b9c0Schristos 		 * Minimize TTLs.
1560e2b1b9c0Schristos 		 *
1561e2b1b9c0Schristos 		 * Section 5.2 of RFC2181 says we should drop
1562e2b1b9c0Schristos 		 * nonauthoritative rrsets where the TTLs differ, but we
1563e2b1b9c0Schristos 		 * currently treat them the as if they were authoritative and
1564e2b1b9c0Schristos 		 * minimize them.
1565e2b1b9c0Schristos 		 */
1566e2b1b9c0Schristos 		if (ttl != rdataset->ttl) {
1567e2b1b9c0Schristos 			rdataset->attributes |= DNS_RDATASETATTR_TTLADJUSTED;
15689742fdb4Schristos 			if (ttl < rdataset->ttl) {
1569e2b1b9c0Schristos 				rdataset->ttl = ttl;
1570e2b1b9c0Schristos 			}
15719742fdb4Schristos 		}
1572e2b1b9c0Schristos 
1573e2b1b9c0Schristos 		/* Append this rdata to the rdataset. */
1574e2b1b9c0Schristos 		dns_rdatalist_fromrdataset(rdataset, &rdatalist);
1575e2b1b9c0Schristos 		ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
1576e2b1b9c0Schristos 
1577e2b1b9c0Schristos 		/*
1578e2b1b9c0Schristos 		 * If this is an OPT, SIG(0) or TSIG record, remember it.
1579e2b1b9c0Schristos 		 * Also, set the extended rcode for TSIG.
1580e2b1b9c0Schristos 		 *
1581e2b1b9c0Schristos 		 * Note msg->opt, msg->sig0 and msg->tsig will only be
1582e2b1b9c0Schristos 		 * already set if best-effort parsing is enabled otherwise
1583e2b1b9c0Schristos 		 * there will only be at most one of each.
1584e2b1b9c0Schristos 		 */
158577279b93Schristos 		if (isedns) {
1586e2b1b9c0Schristos 			dns_rcode_t ercode;
1587e2b1b9c0Schristos 
1588e2b1b9c0Schristos 			msg->opt = rdataset;
1589e2b1b9c0Schristos 			rdataset = NULL;
1590f2e20987Schristos 			free_rdataset = false;
1591fadf0758Schristos 			ercode = (dns_rcode_t)((msg->opt->ttl &
1592fadf0758Schristos 						DNS_MESSAGE_EDNSRCODE_MASK) >>
15939742fdb4Schristos 					       20);
1594e2b1b9c0Schristos 			msg->rcode |= ercode;
1595fadf0758Schristos 			dns_message_puttempname(msg, &name);
1596f2e20987Schristos 			free_name = false;
159777279b93Schristos 		} else if (issigzero) {
1598e2b1b9c0Schristos 			msg->sig0 = rdataset;
1599e2b1b9c0Schristos 			msg->sig0name = name;
160077279b93Schristos 			msg->sigstart = recstart;
1601e2b1b9c0Schristos 			rdataset = NULL;
1602f2e20987Schristos 			free_rdataset = false;
1603f2e20987Schristos 			free_name = false;
160477279b93Schristos 		} else if (istsig) {
1605e2b1b9c0Schristos 			msg->tsig = rdataset;
1606e2b1b9c0Schristos 			msg->tsigname = name;
160777279b93Schristos 			msg->sigstart = recstart;
160877279b93Schristos 			/*
160977279b93Schristos 			 * Windows doesn't like TSIG names to be compressed.
161077279b93Schristos 			 */
1611e2b1b9c0Schristos 			msg->tsigname->attributes |= DNS_NAMEATTR_NOCOMPRESS;
1612e2b1b9c0Schristos 			rdataset = NULL;
1613f2e20987Schristos 			free_rdataset = false;
1614f2e20987Schristos 			free_name = false;
1615e2b1b9c0Schristos 		}
1616e2b1b9c0Schristos 
1617e2b1b9c0Schristos 		if (seen_problem) {
16189742fdb4Schristos 			if (free_name) {
1619fadf0758Schristos 				dns_message_puttempname(msg, &name);
16209742fdb4Schristos 			}
16219742fdb4Schristos 			if (free_rdataset) {
1622e2b1b9c0Schristos 				isc_mempool_put(msg->rdspool, rdataset);
16239742fdb4Schristos 			}
1624f2e20987Schristos 			free_name = free_rdataset = false;
1625e2b1b9c0Schristos 		}
1626803e9293Schristos 		INSIST(!free_name);
1627803e9293Schristos 		INSIST(!free_rdataset);
1628e2b1b9c0Schristos 	}
1629e2b1b9c0Schristos 
1630e2b1b9c0Schristos 	/*
1631e2b1b9c0Schristos 	 * If any of DS, NSEC or NSEC3 appeared in the
1632e2b1b9c0Schristos 	 * authority section of a query response without
1633e2b1b9c0Schristos 	 * a covering RRSIG, FORMERR
1634e2b1b9c0Schristos 	 */
1635e2b1b9c0Schristos 	if (sectionid == DNS_SECTION_AUTHORITY &&
1636e2b1b9c0Schristos 	    msg->opcode == dns_opcode_query &&
1637e2b1b9c0Schristos 	    ((msg->flags & DNS_MESSAGEFLAG_QR) != 0) &&
16389742fdb4Schristos 	    ((msg->flags & DNS_MESSAGEFLAG_TC) == 0) && !preserve_order &&
1639e2b1b9c0Schristos 	    !auth_signed(section))
16409742fdb4Schristos 	{
1641e2b1b9c0Schristos 		DO_ERROR(DNS_R_FORMERR);
16429742fdb4Schristos 	}
1643e2b1b9c0Schristos 
16449742fdb4Schristos 	if (seen_problem) {
1645e2b1b9c0Schristos 		return (DNS_R_RECOVERABLE);
16469742fdb4Schristos 	}
1647e2b1b9c0Schristos 	return (ISC_R_SUCCESS);
1648e2b1b9c0Schristos 
1649e2b1b9c0Schristos cleanup:
16509742fdb4Schristos 	if (free_name) {
1651fadf0758Schristos 		dns_message_puttempname(msg, &name);
16529742fdb4Schristos 	}
16539742fdb4Schristos 	if (free_rdataset) {
1654e2b1b9c0Schristos 		isc_mempool_put(msg->rdspool, rdataset);
16559742fdb4Schristos 	}
1656e2b1b9c0Schristos 
1657e2b1b9c0Schristos 	return (result);
1658e2b1b9c0Schristos }
1659e2b1b9c0Schristos 
1660e2b1b9c0Schristos isc_result_t
dns_message_parse(dns_message_t * msg,isc_buffer_t * source,unsigned int options)1661e2b1b9c0Schristos dns_message_parse(dns_message_t *msg, isc_buffer_t *source,
16629742fdb4Schristos 		  unsigned int options) {
1663e2b1b9c0Schristos 	isc_region_t r;
1664e2b1b9c0Schristos 	dns_decompress_t dctx;
1665e2b1b9c0Schristos 	isc_result_t ret;
1666f2e20987Schristos 	uint16_t tmpflags;
1667e2b1b9c0Schristos 	isc_buffer_t origsource;
1668f2e20987Schristos 	bool seen_problem;
1669f2e20987Schristos 	bool ignore_tc;
1670e2b1b9c0Schristos 
1671e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
1672e2b1b9c0Schristos 	REQUIRE(source != NULL);
1673e2b1b9c0Schristos 	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTPARSE);
1674e2b1b9c0Schristos 
1675f2e20987Schristos 	seen_problem = false;
1676f2e20987Schristos 	ignore_tc = ((options & DNS_MESSAGEPARSE_IGNORETRUNCATION) != 0);
1677e2b1b9c0Schristos 
1678e2b1b9c0Schristos 	origsource = *source;
1679e2b1b9c0Schristos 
1680e2b1b9c0Schristos 	msg->header_ok = 0;
1681e2b1b9c0Schristos 	msg->question_ok = 0;
1682e2b1b9c0Schristos 
168373584a28Schristos 	if ((options & DNS_MESSAGEPARSE_CLONEBUFFER) == 0) {
168473584a28Schristos 		isc_buffer_usedregion(&origsource, &msg->saved);
168573584a28Schristos 	} else {
168673584a28Schristos 		msg->saved.length = isc_buffer_usedlength(&origsource);
168773584a28Schristos 		msg->saved.base = isc_mem_get(msg->mctx, msg->saved.length);
168873584a28Schristos 		memmove(msg->saved.base, isc_buffer_base(&origsource),
168973584a28Schristos 			msg->saved.length);
169073584a28Schristos 		msg->free_saved = 1;
169173584a28Schristos 	}
169273584a28Schristos 
1693e2b1b9c0Schristos 	isc_buffer_remainingregion(source, &r);
16949742fdb4Schristos 	if (r.length < DNS_MESSAGE_HEADERLEN) {
1695e2b1b9c0Schristos 		return (ISC_R_UNEXPECTEDEND);
16969742fdb4Schristos 	}
1697e2b1b9c0Schristos 
1698e2b1b9c0Schristos 	msg->id = isc_buffer_getuint16(source);
1699e2b1b9c0Schristos 	tmpflags = isc_buffer_getuint16(source);
17009742fdb4Schristos 	msg->opcode = ((tmpflags & DNS_MESSAGE_OPCODE_MASK) >>
17019742fdb4Schristos 		       DNS_MESSAGE_OPCODE_SHIFT);
1702e2b1b9c0Schristos 	msg->rcode = (dns_rcode_t)(tmpflags & DNS_MESSAGE_RCODE_MASK);
1703e2b1b9c0Schristos 	msg->flags = (tmpflags & DNS_MESSAGE_FLAG_MASK);
1704e2b1b9c0Schristos 	msg->counts[DNS_SECTION_QUESTION] = isc_buffer_getuint16(source);
1705e2b1b9c0Schristos 	msg->counts[DNS_SECTION_ANSWER] = isc_buffer_getuint16(source);
1706e2b1b9c0Schristos 	msg->counts[DNS_SECTION_AUTHORITY] = isc_buffer_getuint16(source);
1707e2b1b9c0Schristos 	msg->counts[DNS_SECTION_ADDITIONAL] = isc_buffer_getuint16(source);
1708e2b1b9c0Schristos 
1709e2b1b9c0Schristos 	msg->header_ok = 1;
1710e2b1b9c0Schristos 	msg->state = DNS_SECTION_QUESTION;
1711e2b1b9c0Schristos 
1712e2b1b9c0Schristos 	/*
1713e2b1b9c0Schristos 	 * -1 means no EDNS.
1714e2b1b9c0Schristos 	 */
1715e2b1b9c0Schristos 	dns_decompress_init(&dctx, -1, DNS_DECOMPRESS_ANY);
1716e2b1b9c0Schristos 
1717e2b1b9c0Schristos 	dns_decompress_setmethods(&dctx, DNS_COMPRESS_GLOBAL14);
1718e2b1b9c0Schristos 
1719e2b1b9c0Schristos 	ret = getquestions(source, msg, &dctx, options);
17209742fdb4Schristos 	if (ret == ISC_R_UNEXPECTEDEND && ignore_tc) {
1721e2b1b9c0Schristos 		goto truncated;
17229742fdb4Schristos 	}
1723e2b1b9c0Schristos 	if (ret == DNS_R_RECOVERABLE) {
1724f2e20987Schristos 		seen_problem = true;
1725e2b1b9c0Schristos 		ret = ISC_R_SUCCESS;
1726e2b1b9c0Schristos 	}
17279742fdb4Schristos 	if (ret != ISC_R_SUCCESS) {
1728e2b1b9c0Schristos 		return (ret);
17299742fdb4Schristos 	}
1730e2b1b9c0Schristos 	msg->question_ok = 1;
1731e2b1b9c0Schristos 
1732e2b1b9c0Schristos 	ret = getsection(source, msg, &dctx, DNS_SECTION_ANSWER, options);
17339742fdb4Schristos 	if (ret == ISC_R_UNEXPECTEDEND && ignore_tc) {
1734e2b1b9c0Schristos 		goto truncated;
17359742fdb4Schristos 	}
1736e2b1b9c0Schristos 	if (ret == DNS_R_RECOVERABLE) {
1737f2e20987Schristos 		seen_problem = true;
1738e2b1b9c0Schristos 		ret = ISC_R_SUCCESS;
1739e2b1b9c0Schristos 	}
17409742fdb4Schristos 	if (ret != ISC_R_SUCCESS) {
1741e2b1b9c0Schristos 		return (ret);
17429742fdb4Schristos 	}
1743e2b1b9c0Schristos 
1744e2b1b9c0Schristos 	ret = getsection(source, msg, &dctx, DNS_SECTION_AUTHORITY, options);
17459742fdb4Schristos 	if (ret == ISC_R_UNEXPECTEDEND && ignore_tc) {
1746e2b1b9c0Schristos 		goto truncated;
17479742fdb4Schristos 	}
1748e2b1b9c0Schristos 	if (ret == DNS_R_RECOVERABLE) {
1749f2e20987Schristos 		seen_problem = true;
1750e2b1b9c0Schristos 		ret = ISC_R_SUCCESS;
1751e2b1b9c0Schristos 	}
17529742fdb4Schristos 	if (ret != ISC_R_SUCCESS) {
1753e2b1b9c0Schristos 		return (ret);
17549742fdb4Schristos 	}
1755e2b1b9c0Schristos 
1756e2b1b9c0Schristos 	ret = getsection(source, msg, &dctx, DNS_SECTION_ADDITIONAL, options);
17579742fdb4Schristos 	if (ret == ISC_R_UNEXPECTEDEND && ignore_tc) {
1758e2b1b9c0Schristos 		goto truncated;
17599742fdb4Schristos 	}
1760e2b1b9c0Schristos 	if (ret == DNS_R_RECOVERABLE) {
1761f2e20987Schristos 		seen_problem = true;
1762e2b1b9c0Schristos 		ret = ISC_R_SUCCESS;
1763e2b1b9c0Schristos 	}
17649742fdb4Schristos 	if (ret != ISC_R_SUCCESS) {
1765e2b1b9c0Schristos 		return (ret);
17669742fdb4Schristos 	}
1767e2b1b9c0Schristos 
1768e2b1b9c0Schristos 	isc_buffer_remainingregion(source, &r);
1769e2b1b9c0Schristos 	if (r.length != 0) {
1770e2b1b9c0Schristos 		isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
1771e2b1b9c0Schristos 			      DNS_LOGMODULE_MESSAGE, ISC_LOG_DEBUG(3),
1772e2b1b9c0Schristos 			      "message has %u byte(s) of trailing garbage",
1773e2b1b9c0Schristos 			      r.length);
1774e2b1b9c0Schristos 	}
1775e2b1b9c0Schristos 
1776e2b1b9c0Schristos truncated:
1777e2b1b9c0Schristos 
17789742fdb4Schristos 	if (ret == ISC_R_UNEXPECTEDEND && ignore_tc) {
1779e2b1b9c0Schristos 		return (DNS_R_RECOVERABLE);
17809742fdb4Schristos 	}
1781803e9293Schristos 	if (seen_problem) {
1782e2b1b9c0Schristos 		return (DNS_R_RECOVERABLE);
17839742fdb4Schristos 	}
1784e2b1b9c0Schristos 	return (ISC_R_SUCCESS);
1785e2b1b9c0Schristos }
1786e2b1b9c0Schristos 
1787e2b1b9c0Schristos isc_result_t
dns_message_renderbegin(dns_message_t * msg,dns_compress_t * cctx,isc_buffer_t * buffer)1788e2b1b9c0Schristos dns_message_renderbegin(dns_message_t *msg, dns_compress_t *cctx,
17899742fdb4Schristos 			isc_buffer_t *buffer) {
1790e2b1b9c0Schristos 	isc_region_t r;
1791e2b1b9c0Schristos 
1792e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
1793e2b1b9c0Schristos 	REQUIRE(buffer != NULL);
1794e2b1b9c0Schristos 	REQUIRE(msg->buffer == NULL);
1795e2b1b9c0Schristos 	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
1796e2b1b9c0Schristos 
1797e2b1b9c0Schristos 	msg->cctx = cctx;
1798e2b1b9c0Schristos 
1799e2b1b9c0Schristos 	/*
1800e2b1b9c0Schristos 	 * Erase the contents of this buffer.
1801e2b1b9c0Schristos 	 */
1802e2b1b9c0Schristos 	isc_buffer_clear(buffer);
1803e2b1b9c0Schristos 
1804e2b1b9c0Schristos 	/*
1805e2b1b9c0Schristos 	 * Make certain there is enough for at least the header in this
1806e2b1b9c0Schristos 	 * buffer.
1807e2b1b9c0Schristos 	 */
1808e2b1b9c0Schristos 	isc_buffer_availableregion(buffer, &r);
18099742fdb4Schristos 	if (r.length < DNS_MESSAGE_HEADERLEN) {
1810e2b1b9c0Schristos 		return (ISC_R_NOSPACE);
18119742fdb4Schristos 	}
1812e2b1b9c0Schristos 
18139742fdb4Schristos 	if (r.length - DNS_MESSAGE_HEADERLEN < msg->reserved) {
1814e2b1b9c0Schristos 		return (ISC_R_NOSPACE);
18159742fdb4Schristos 	}
1816e2b1b9c0Schristos 
1817e2b1b9c0Schristos 	/*
1818e2b1b9c0Schristos 	 * Reserve enough space for the header in this buffer.
1819e2b1b9c0Schristos 	 */
1820e2b1b9c0Schristos 	isc_buffer_add(buffer, DNS_MESSAGE_HEADERLEN);
1821e2b1b9c0Schristos 
1822e2b1b9c0Schristos 	msg->buffer = buffer;
1823e2b1b9c0Schristos 
1824e2b1b9c0Schristos 	return (ISC_R_SUCCESS);
1825e2b1b9c0Schristos }
1826e2b1b9c0Schristos 
1827e2b1b9c0Schristos isc_result_t
dns_message_renderchangebuffer(dns_message_t * msg,isc_buffer_t * buffer)1828e2b1b9c0Schristos dns_message_renderchangebuffer(dns_message_t *msg, isc_buffer_t *buffer) {
1829e2b1b9c0Schristos 	isc_region_t r, rn;
1830e2b1b9c0Schristos 
1831e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
1832e2b1b9c0Schristos 	REQUIRE(buffer != NULL);
1833e2b1b9c0Schristos 	REQUIRE(msg->buffer != NULL);
1834e2b1b9c0Schristos 
1835e2b1b9c0Schristos 	/*
1836e2b1b9c0Schristos 	 * Ensure that the new buffer is empty, and has enough space to
1837e2b1b9c0Schristos 	 * hold the current contents.
1838e2b1b9c0Schristos 	 */
1839e2b1b9c0Schristos 	isc_buffer_clear(buffer);
1840e2b1b9c0Schristos 
1841e2b1b9c0Schristos 	isc_buffer_availableregion(buffer, &rn);
1842e2b1b9c0Schristos 	isc_buffer_usedregion(msg->buffer, &r);
1843e2b1b9c0Schristos 	REQUIRE(rn.length > r.length);
1844e2b1b9c0Schristos 
1845e2b1b9c0Schristos 	/*
1846e2b1b9c0Schristos 	 * Copy the contents from the old to the new buffer.
1847e2b1b9c0Schristos 	 */
1848e2b1b9c0Schristos 	isc_buffer_add(buffer, r.length);
1849e2b1b9c0Schristos 	memmove(rn.base, r.base, r.length);
1850e2b1b9c0Schristos 
1851e2b1b9c0Schristos 	msg->buffer = buffer;
1852e2b1b9c0Schristos 
1853e2b1b9c0Schristos 	return (ISC_R_SUCCESS);
1854e2b1b9c0Schristos }
1855e2b1b9c0Schristos 
1856e2b1b9c0Schristos void
dns_message_renderrelease(dns_message_t * msg,unsigned int space)1857e2b1b9c0Schristos dns_message_renderrelease(dns_message_t *msg, unsigned int space) {
1858e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
1859e2b1b9c0Schristos 	REQUIRE(space <= msg->reserved);
1860e2b1b9c0Schristos 
1861e2b1b9c0Schristos 	msg->reserved -= space;
1862e2b1b9c0Schristos }
1863e2b1b9c0Schristos 
1864e2b1b9c0Schristos isc_result_t
dns_message_renderreserve(dns_message_t * msg,unsigned int space)1865e2b1b9c0Schristos dns_message_renderreserve(dns_message_t *msg, unsigned int space) {
1866e2b1b9c0Schristos 	isc_region_t r;
1867e2b1b9c0Schristos 
1868e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
1869e2b1b9c0Schristos 
1870e2b1b9c0Schristos 	if (msg->buffer != NULL) {
1871e2b1b9c0Schristos 		isc_buffer_availableregion(msg->buffer, &r);
18729742fdb4Schristos 		if (r.length < (space + msg->reserved)) {
1873e2b1b9c0Schristos 			return (ISC_R_NOSPACE);
1874e2b1b9c0Schristos 		}
18759742fdb4Schristos 	}
1876e2b1b9c0Schristos 
1877e2b1b9c0Schristos 	msg->reserved += space;
1878e2b1b9c0Schristos 
1879e2b1b9c0Schristos 	return (ISC_R_SUCCESS);
1880e2b1b9c0Schristos }
1881e2b1b9c0Schristos 
1882c0b5d9fbSchristos static bool
wrong_priority(dns_rdataset_t * rds,int pass,dns_rdatatype_t preferred_glue)1883e2b1b9c0Schristos wrong_priority(dns_rdataset_t *rds, int pass, dns_rdatatype_t preferred_glue) {
1884e2b1b9c0Schristos 	int pass_needed;
1885e2b1b9c0Schristos 
1886e2b1b9c0Schristos 	/*
1887e2b1b9c0Schristos 	 * If we are not rendering class IN, this ordering is bogus.
1888e2b1b9c0Schristos 	 */
18899742fdb4Schristos 	if (rds->rdclass != dns_rdataclass_in) {
1890f2e20987Schristos 		return (false);
18919742fdb4Schristos 	}
1892e2b1b9c0Schristos 
1893e2b1b9c0Schristos 	switch (rds->type) {
1894e2b1b9c0Schristos 	case dns_rdatatype_a:
1895e2b1b9c0Schristos 	case dns_rdatatype_aaaa:
18969742fdb4Schristos 		if (preferred_glue == rds->type) {
1897e2b1b9c0Schristos 			pass_needed = 4;
18989742fdb4Schristos 		} else {
1899e2b1b9c0Schristos 			pass_needed = 3;
19009742fdb4Schristos 		}
1901e2b1b9c0Schristos 		break;
1902e2b1b9c0Schristos 	case dns_rdatatype_rrsig:
1903e2b1b9c0Schristos 	case dns_rdatatype_dnskey:
1904e2b1b9c0Schristos 		pass_needed = 2;
1905e2b1b9c0Schristos 		break;
1906e2b1b9c0Schristos 	default:
1907e2b1b9c0Schristos 		pass_needed = 1;
1908e2b1b9c0Schristos 	}
1909e2b1b9c0Schristos 
19109742fdb4Schristos 	if (pass_needed >= pass) {
1911f2e20987Schristos 		return (false);
19129742fdb4Schristos 	}
1913e2b1b9c0Schristos 
1914f2e20987Schristos 	return (true);
1915e2b1b9c0Schristos }
1916e2b1b9c0Schristos 
1917e2b1b9c0Schristos static isc_result_t
renderset(dns_rdataset_t * rdataset,const dns_name_t * owner_name,dns_compress_t * cctx,isc_buffer_t * target,unsigned int reserved,unsigned int options,unsigned int * countp)1918e2b1b9c0Schristos renderset(dns_rdataset_t *rdataset, const dns_name_t *owner_name,
19199742fdb4Schristos 	  dns_compress_t *cctx, isc_buffer_t *target, unsigned int reserved,
19209742fdb4Schristos 	  unsigned int options, unsigned int *countp) {
1921e2b1b9c0Schristos 	isc_result_t result;
1922e2b1b9c0Schristos 
1923e2b1b9c0Schristos 	/*
1924e2b1b9c0Schristos 	 * Shrink the space in the buffer by the reserved amount.
1925e2b1b9c0Schristos 	 */
19269742fdb4Schristos 	if (target->length - target->used < reserved) {
1927e2b1b9c0Schristos 		return (ISC_R_NOSPACE);
19289742fdb4Schristos 	}
1929e2b1b9c0Schristos 
1930e2b1b9c0Schristos 	target->length -= reserved;
19319742fdb4Schristos 	result = dns_rdataset_towire(rdataset, owner_name, cctx, target,
19329742fdb4Schristos 				     options, countp);
1933e2b1b9c0Schristos 	target->length += reserved;
1934e2b1b9c0Schristos 
1935e2b1b9c0Schristos 	return (result);
1936e2b1b9c0Schristos }
1937e2b1b9c0Schristos 
1938e2b1b9c0Schristos static void
maybe_clear_ad(dns_message_t * msg,dns_section_t sectionid)1939e2b1b9c0Schristos maybe_clear_ad(dns_message_t *msg, dns_section_t sectionid) {
1940e2b1b9c0Schristos 	if (msg->counts[sectionid] == 0 &&
1941e2b1b9c0Schristos 	    (sectionid == DNS_SECTION_ANSWER ||
1942e2b1b9c0Schristos 	     (sectionid == DNS_SECTION_AUTHORITY &&
1943e2b1b9c0Schristos 	      msg->counts[DNS_SECTION_ANSWER] == 0)))
19449742fdb4Schristos 	{
1945e2b1b9c0Schristos 		msg->flags &= ~DNS_MESSAGEFLAG_AD;
1946e2b1b9c0Schristos 	}
19479742fdb4Schristos }
1948e2b1b9c0Schristos 
1949e2b1b9c0Schristos isc_result_t
dns_message_rendersection(dns_message_t * msg,dns_section_t sectionid,unsigned int options)1950e2b1b9c0Schristos dns_message_rendersection(dns_message_t *msg, dns_section_t sectionid,
19519742fdb4Schristos 			  unsigned int options) {
1952e2b1b9c0Schristos 	dns_namelist_t *section;
1953e2b1b9c0Schristos 	dns_name_t *name, *next_name;
1954e2b1b9c0Schristos 	dns_rdataset_t *rdataset, *next_rdataset;
1955e2b1b9c0Schristos 	unsigned int count, total;
1956e2b1b9c0Schristos 	isc_result_t result;
1957e2b1b9c0Schristos 	isc_buffer_t st; /* for rollbacks */
1958e2b1b9c0Schristos 	int pass;
1959f2e20987Schristos 	bool partial = false;
1960e2b1b9c0Schristos 	unsigned int rd_options;
1961e2b1b9c0Schristos 	dns_rdatatype_t preferred_glue = 0;
1962e2b1b9c0Schristos 
1963e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
1964e2b1b9c0Schristos 	REQUIRE(msg->buffer != NULL);
1965e2b1b9c0Schristos 	REQUIRE(VALID_NAMED_SECTION(sectionid));
1966e2b1b9c0Schristos 
1967e2b1b9c0Schristos 	section = &msg->sections[sectionid];
1968e2b1b9c0Schristos 
19699742fdb4Schristos 	if ((sectionid == DNS_SECTION_ADDITIONAL) &&
19709742fdb4Schristos 	    (options & DNS_MESSAGERENDER_ORDERED) == 0)
19719742fdb4Schristos 	{
1972e2b1b9c0Schristos 		if ((options & DNS_MESSAGERENDER_PREFER_A) != 0) {
1973e2b1b9c0Schristos 			preferred_glue = dns_rdatatype_a;
1974e2b1b9c0Schristos 			pass = 4;
1975e2b1b9c0Schristos 		} else if ((options & DNS_MESSAGERENDER_PREFER_AAAA) != 0) {
1976e2b1b9c0Schristos 			preferred_glue = dns_rdatatype_aaaa;
1977e2b1b9c0Schristos 			pass = 4;
19789742fdb4Schristos 		} else {
1979e2b1b9c0Schristos 			pass = 3;
19809742fdb4Schristos 		}
19819742fdb4Schristos 	} else {
1982e2b1b9c0Schristos 		pass = 1;
19839742fdb4Schristos 	}
1984e2b1b9c0Schristos 
19859742fdb4Schristos 	if ((options & DNS_MESSAGERENDER_OMITDNSSEC) == 0) {
1986e2b1b9c0Schristos 		rd_options = 0;
19879742fdb4Schristos 	} else {
1988e2b1b9c0Schristos 		rd_options = DNS_RDATASETTOWIRE_OMITDNSSEC;
19899742fdb4Schristos 	}
1990e2b1b9c0Schristos 
1991e2b1b9c0Schristos 	/*
1992e2b1b9c0Schristos 	 * Shrink the space in the buffer by the reserved amount.
1993e2b1b9c0Schristos 	 */
19949742fdb4Schristos 	if (msg->buffer->length - msg->buffer->used < msg->reserved) {
1995e2b1b9c0Schristos 		return (ISC_R_NOSPACE);
19969742fdb4Schristos 	}
1997e2b1b9c0Schristos 	msg->buffer->length -= msg->reserved;
1998e2b1b9c0Schristos 
1999e2b1b9c0Schristos 	total = 0;
20009742fdb4Schristos 	if (msg->reserved == 0 && (options & DNS_MESSAGERENDER_PARTIAL) != 0) {
2001f2e20987Schristos 		partial = true;
20029742fdb4Schristos 	}
2003e2b1b9c0Schristos 
2004e2b1b9c0Schristos 	/*
2005e2b1b9c0Schristos 	 * Render required glue first.  Set TC if it won't fit.
2006e2b1b9c0Schristos 	 */
2007e2b1b9c0Schristos 	name = ISC_LIST_HEAD(*section);
2008e2b1b9c0Schristos 	if (name != NULL) {
2009e2b1b9c0Schristos 		rdataset = ISC_LIST_HEAD(name->list);
2010e2b1b9c0Schristos 		if (rdataset != NULL &&
20119742fdb4Schristos 		    (rdataset->attributes & DNS_RDATASETATTR_REQUIREDGLUE) !=
20129742fdb4Schristos 			    0 &&
20139742fdb4Schristos 		    (rdataset->attributes & DNS_RDATASETATTR_RENDERED) == 0)
20149742fdb4Schristos 		{
2015e2b1b9c0Schristos 			const void *order_arg = &msg->order_arg;
2016e2b1b9c0Schristos 			st = *(msg->buffer);
2017e2b1b9c0Schristos 			count = 0;
20189742fdb4Schristos 			if (partial) {
20199742fdb4Schristos 				result = dns_rdataset_towirepartial(
20209742fdb4Schristos 					rdataset, name, msg->cctx, msg->buffer,
20219742fdb4Schristos 					msg->order, order_arg, rd_options,
20229742fdb4Schristos 					&count, NULL);
20239742fdb4Schristos 			} else {
20249742fdb4Schristos 				result = dns_rdataset_towiresorted(
20259742fdb4Schristos 					rdataset, name, msg->cctx, msg->buffer,
20269742fdb4Schristos 					msg->order, order_arg, rd_options,
2027e2b1b9c0Schristos 					&count);
20289742fdb4Schristos 			}
2029e2b1b9c0Schristos 			total += count;
2030e2b1b9c0Schristos 			if (partial && result == ISC_R_NOSPACE) {
2031e2b1b9c0Schristos 				msg->flags |= DNS_MESSAGEFLAG_TC;
2032e2b1b9c0Schristos 				msg->buffer->length += msg->reserved;
2033e2b1b9c0Schristos 				msg->counts[sectionid] += total;
2034e2b1b9c0Schristos 				return (result);
2035e2b1b9c0Schristos 			}
20369742fdb4Schristos 			if (result == ISC_R_NOSPACE) {
2037e2b1b9c0Schristos 				msg->flags |= DNS_MESSAGEFLAG_TC;
20389742fdb4Schristos 			}
2039e2b1b9c0Schristos 			if (result != ISC_R_SUCCESS) {
2040e2b1b9c0Schristos 				INSIST(st.used < 65536);
2041e2b1b9c0Schristos 				dns_compress_rollback(msg->cctx,
2042f2e20987Schristos 						      (uint16_t)st.used);
2043e2b1b9c0Schristos 				*(msg->buffer) = st; /* rollback */
2044e2b1b9c0Schristos 				msg->buffer->length += msg->reserved;
2045e2b1b9c0Schristos 				msg->counts[sectionid] += total;
2046e2b1b9c0Schristos 				return (result);
2047e2b1b9c0Schristos 			}
2048e2b1b9c0Schristos 			rdataset->attributes |= DNS_RDATASETATTR_RENDERED;
2049e2b1b9c0Schristos 		}
2050e2b1b9c0Schristos 	}
2051e2b1b9c0Schristos 
2052e2b1b9c0Schristos 	do {
2053e2b1b9c0Schristos 		name = ISC_LIST_HEAD(*section);
2054e2b1b9c0Schristos 		if (name == NULL) {
2055e2b1b9c0Schristos 			msg->buffer->length += msg->reserved;
2056e2b1b9c0Schristos 			msg->counts[sectionid] += total;
2057e2b1b9c0Schristos 			return (ISC_R_SUCCESS);
2058e2b1b9c0Schristos 		}
2059e2b1b9c0Schristos 
2060e2b1b9c0Schristos 		while (name != NULL) {
2061e2b1b9c0Schristos 			next_name = ISC_LIST_NEXT(name, link);
2062e2b1b9c0Schristos 
2063e2b1b9c0Schristos 			rdataset = ISC_LIST_HEAD(name->list);
2064e2b1b9c0Schristos 			while (rdataset != NULL) {
2065e2b1b9c0Schristos 				next_rdataset = ISC_LIST_NEXT(rdataset, link);
2066e2b1b9c0Schristos 
2067e2b1b9c0Schristos 				if ((rdataset->attributes &
2068*4ac1c27eSchristos 				     DNS_RDATASETATTR_RENDERED) != 0)
2069*4ac1c27eSchristos 				{
2070e2b1b9c0Schristos 					goto next;
20719742fdb4Schristos 				}
2072e2b1b9c0Schristos 
20739742fdb4Schristos 				if (((options & DNS_MESSAGERENDER_ORDERED) ==
20749742fdb4Schristos 				     0) &&
20759742fdb4Schristos 				    (sectionid == DNS_SECTION_ADDITIONAL) &&
20769742fdb4Schristos 				    wrong_priority(rdataset, pass,
2077e2b1b9c0Schristos 						   preferred_glue))
20789742fdb4Schristos 				{
2079e2b1b9c0Schristos 					goto next;
20809742fdb4Schristos 				}
2081e2b1b9c0Schristos 
2082e2b1b9c0Schristos 				st = *(msg->buffer);
2083e2b1b9c0Schristos 
2084e2b1b9c0Schristos 				count = 0;
20859742fdb4Schristos 				if (partial) {
2086e2b1b9c0Schristos 					result = dns_rdataset_towirepartial(
20879742fdb4Schristos 						rdataset, name, msg->cctx,
20889742fdb4Schristos 						msg->buffer, msg->order,
20899742fdb4Schristos 						&msg->order_arg, rd_options,
20909742fdb4Schristos 						&count, NULL);
20919742fdb4Schristos 				} else {
2092e2b1b9c0Schristos 					result = dns_rdataset_towiresorted(
20939742fdb4Schristos 						rdataset, name, msg->cctx,
20949742fdb4Schristos 						msg->buffer, msg->order,
20959742fdb4Schristos 						&msg->order_arg, rd_options,
2096e2b1b9c0Schristos 						&count);
20979742fdb4Schristos 				}
2098e2b1b9c0Schristos 
2099e2b1b9c0Schristos 				total += count;
2100e2b1b9c0Schristos 
2101e2b1b9c0Schristos 				/*
2102e2b1b9c0Schristos 				 * If out of space, record stats on what we
2103e2b1b9c0Schristos 				 * rendered so far, and return that status.
2104e2b1b9c0Schristos 				 *
2105e2b1b9c0Schristos 				 * XXXMLG Need to change this when
2106e2b1b9c0Schristos 				 * dns_rdataset_towire() can render partial
2107e2b1b9c0Schristos 				 * sets starting at some arbitrary point in the
2108e2b1b9c0Schristos 				 * set.  This will include setting a bit in the
2109e2b1b9c0Schristos 				 * rdataset to indicate that a partial
2110e2b1b9c0Schristos 				 * rendering was done, and some state saved
2111e2b1b9c0Schristos 				 * somewhere (probably in the message struct)
2112e2b1b9c0Schristos 				 * to indicate where to continue from.
2113e2b1b9c0Schristos 				 */
2114e2b1b9c0Schristos 				if (partial && result == ISC_R_NOSPACE) {
2115e2b1b9c0Schristos 					msg->buffer->length += msg->reserved;
2116e2b1b9c0Schristos 					msg->counts[sectionid] += total;
2117e2b1b9c0Schristos 					return (result);
2118e2b1b9c0Schristos 				}
2119e2b1b9c0Schristos 				if (result != ISC_R_SUCCESS) {
2120e2b1b9c0Schristos 					INSIST(st.used < 65536);
21219742fdb4Schristos 					dns_compress_rollback(
21229742fdb4Schristos 						msg->cctx, (uint16_t)st.used);
2123e2b1b9c0Schristos 					*(msg->buffer) = st; /* rollback */
2124e2b1b9c0Schristos 					msg->buffer->length += msg->reserved;
2125e2b1b9c0Schristos 					msg->counts[sectionid] += total;
2126e2b1b9c0Schristos 					maybe_clear_ad(msg, sectionid);
2127e2b1b9c0Schristos 					return (result);
2128e2b1b9c0Schristos 				}
2129e2b1b9c0Schristos 
2130e2b1b9c0Schristos 				/*
2131e2b1b9c0Schristos 				 * If we have rendered non-validated data,
2132e2b1b9c0Schristos 				 * ensure that the AD bit is not set.
2133e2b1b9c0Schristos 				 */
2134e2b1b9c0Schristos 				if (rdataset->trust != dns_trust_secure &&
2135e2b1b9c0Schristos 				    (sectionid == DNS_SECTION_ANSWER ||
2136e2b1b9c0Schristos 				     sectionid == DNS_SECTION_AUTHORITY))
21379742fdb4Schristos 				{
2138e2b1b9c0Schristos 					msg->flags &= ~DNS_MESSAGEFLAG_AD;
21399742fdb4Schristos 				}
21409742fdb4Schristos 				if (OPTOUT(rdataset)) {
2141e2b1b9c0Schristos 					msg->flags &= ~DNS_MESSAGEFLAG_AD;
21429742fdb4Schristos 				}
2143e2b1b9c0Schristos 
2144e2b1b9c0Schristos 				rdataset->attributes |=
2145e2b1b9c0Schristos 					DNS_RDATASETATTR_RENDERED;
2146e2b1b9c0Schristos 
2147e2b1b9c0Schristos 			next:
2148e2b1b9c0Schristos 				rdataset = next_rdataset;
2149e2b1b9c0Schristos 			}
2150e2b1b9c0Schristos 
2151e2b1b9c0Schristos 			name = next_name;
2152e2b1b9c0Schristos 		}
2153e2b1b9c0Schristos 	} while (--pass != 0);
2154e2b1b9c0Schristos 
2155e2b1b9c0Schristos 	msg->buffer->length += msg->reserved;
2156e2b1b9c0Schristos 	msg->counts[sectionid] += total;
2157e2b1b9c0Schristos 
2158e2b1b9c0Schristos 	return (ISC_R_SUCCESS);
2159e2b1b9c0Schristos }
2160e2b1b9c0Schristos 
2161e2b1b9c0Schristos void
dns_message_renderheader(dns_message_t * msg,isc_buffer_t * target)2162e2b1b9c0Schristos dns_message_renderheader(dns_message_t *msg, isc_buffer_t *target) {
2163f2e20987Schristos 	uint16_t tmp;
2164e2b1b9c0Schristos 	isc_region_t r;
2165e2b1b9c0Schristos 
2166e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
2167e2b1b9c0Schristos 	REQUIRE(target != NULL);
2168e2b1b9c0Schristos 
2169e2b1b9c0Schristos 	isc_buffer_availableregion(target, &r);
2170e2b1b9c0Schristos 	REQUIRE(r.length >= DNS_MESSAGE_HEADERLEN);
2171e2b1b9c0Schristos 
2172e2b1b9c0Schristos 	isc_buffer_putuint16(target, msg->id);
2173e2b1b9c0Schristos 
21749742fdb4Schristos 	tmp = ((msg->opcode << DNS_MESSAGE_OPCODE_SHIFT) &
21759742fdb4Schristos 	       DNS_MESSAGE_OPCODE_MASK);
2176e2b1b9c0Schristos 	tmp |= (msg->rcode & DNS_MESSAGE_RCODE_MASK);
2177e2b1b9c0Schristos 	tmp |= (msg->flags & DNS_MESSAGE_FLAG_MASK);
2178e2b1b9c0Schristos 
2179e2b1b9c0Schristos 	INSIST(msg->counts[DNS_SECTION_QUESTION] < 65536 &&
2180e2b1b9c0Schristos 	       msg->counts[DNS_SECTION_ANSWER] < 65536 &&
2181e2b1b9c0Schristos 	       msg->counts[DNS_SECTION_AUTHORITY] < 65536 &&
2182e2b1b9c0Schristos 	       msg->counts[DNS_SECTION_ADDITIONAL] < 65536);
2183e2b1b9c0Schristos 
2184e2b1b9c0Schristos 	isc_buffer_putuint16(target, tmp);
2185e2b1b9c0Schristos 	isc_buffer_putuint16(target,
2186f2e20987Schristos 			     (uint16_t)msg->counts[DNS_SECTION_QUESTION]);
21879742fdb4Schristos 	isc_buffer_putuint16(target, (uint16_t)msg->counts[DNS_SECTION_ANSWER]);
2188e2b1b9c0Schristos 	isc_buffer_putuint16(target,
2189f2e20987Schristos 			     (uint16_t)msg->counts[DNS_SECTION_AUTHORITY]);
2190e2b1b9c0Schristos 	isc_buffer_putuint16(target,
2191f2e20987Schristos 			     (uint16_t)msg->counts[DNS_SECTION_ADDITIONAL]);
2192e2b1b9c0Schristos }
2193e2b1b9c0Schristos 
2194e2b1b9c0Schristos isc_result_t
dns_message_renderend(dns_message_t * msg)2195e2b1b9c0Schristos dns_message_renderend(dns_message_t *msg) {
2196e2b1b9c0Schristos 	isc_buffer_t tmpbuf;
2197e2b1b9c0Schristos 	isc_region_t r;
2198e2b1b9c0Schristos 	int result;
2199e2b1b9c0Schristos 	unsigned int count;
2200e2b1b9c0Schristos 
2201e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
2202e2b1b9c0Schristos 	REQUIRE(msg->buffer != NULL);
2203e2b1b9c0Schristos 
2204e2b1b9c0Schristos 	if ((msg->rcode & ~DNS_MESSAGE_RCODE_MASK) != 0 && msg->opt == NULL) {
2205e2b1b9c0Schristos 		/*
2206e2b1b9c0Schristos 		 * We have an extended rcode but are not using EDNS.
2207e2b1b9c0Schristos 		 */
2208e2b1b9c0Schristos 		return (DNS_R_FORMERR);
2209e2b1b9c0Schristos 	}
2210e2b1b9c0Schristos 
2211e2b1b9c0Schristos 	/*
2212e2b1b9c0Schristos 	 * If we're adding a OPT, TSIG or SIG(0) to a truncated message,
2213e2b1b9c0Schristos 	 * clear all rdatasets from the message except for the question
2214e2b1b9c0Schristos 	 * before adding the OPT, TSIG or SIG(0).  If the question doesn't
2215e2b1b9c0Schristos 	 * fit, don't include it.
2216e2b1b9c0Schristos 	 */
2217e2b1b9c0Schristos 	if ((msg->tsigkey != NULL || msg->sig0key != NULL || msg->opt) &&
2218e2b1b9c0Schristos 	    (msg->flags & DNS_MESSAGEFLAG_TC) != 0)
2219e2b1b9c0Schristos 	{
2220e2b1b9c0Schristos 		isc_buffer_t *buf;
2221e2b1b9c0Schristos 
2222e2b1b9c0Schristos 		msgresetnames(msg, DNS_SECTION_ANSWER);
2223e2b1b9c0Schristos 		buf = msg->buffer;
2224e2b1b9c0Schristos 		dns_message_renderreset(msg);
2225e2b1b9c0Schristos 		msg->buffer = buf;
2226e2b1b9c0Schristos 		isc_buffer_clear(msg->buffer);
2227e2b1b9c0Schristos 		isc_buffer_add(msg->buffer, DNS_MESSAGE_HEADERLEN);
2228e2b1b9c0Schristos 		dns_compress_rollback(msg->cctx, 0);
2229e2b1b9c0Schristos 		result = dns_message_rendersection(msg, DNS_SECTION_QUESTION,
2230e2b1b9c0Schristos 						   0);
22319742fdb4Schristos 		if (result != ISC_R_SUCCESS && result != ISC_R_NOSPACE) {
2232e2b1b9c0Schristos 			return (result);
2233e2b1b9c0Schristos 		}
22349742fdb4Schristos 	}
2235e2b1b9c0Schristos 
2236e2b1b9c0Schristos 	/*
2237e2b1b9c0Schristos 	 * If we've got an OPT record, render it.
2238e2b1b9c0Schristos 	 */
2239e2b1b9c0Schristos 	if (msg->opt != NULL) {
2240e2b1b9c0Schristos 		dns_message_renderrelease(msg, msg->opt_reserved);
2241e2b1b9c0Schristos 		msg->opt_reserved = 0;
2242e2b1b9c0Schristos 		/*
224373584a28Schristos 		 * Set the extended rcode.  Cast msg->rcode to dns_ttl_t
224473584a28Schristos 		 * so that we do a unsigned shift.
2245e2b1b9c0Schristos 		 */
2246e2b1b9c0Schristos 		msg->opt->ttl &= ~DNS_MESSAGE_EDNSRCODE_MASK;
224773584a28Schristos 		msg->opt->ttl |= (((dns_ttl_t)(msg->rcode) << 20) &
2248e2b1b9c0Schristos 				  DNS_MESSAGE_EDNSRCODE_MASK);
2249e2b1b9c0Schristos 		/*
2250e2b1b9c0Schristos 		 * Render.
2251e2b1b9c0Schristos 		 */
2252e2b1b9c0Schristos 		count = 0;
2253e2b1b9c0Schristos 		result = renderset(msg->opt, dns_rootname, msg->cctx,
2254e2b1b9c0Schristos 				   msg->buffer, msg->reserved, 0, &count);
2255e2b1b9c0Schristos 		msg->counts[DNS_SECTION_ADDITIONAL] += count;
22569742fdb4Schristos 		if (result != ISC_R_SUCCESS) {
2257e2b1b9c0Schristos 			return (result);
2258e2b1b9c0Schristos 		}
22599742fdb4Schristos 	}
2260e2b1b9c0Schristos 
2261e2b1b9c0Schristos 	/*
2262e2b1b9c0Schristos 	 * Deal with EDNS padding.
2263e2b1b9c0Schristos 	 *
2264e2b1b9c0Schristos 	 * padding_off is the length of the OPT with the 0-length PAD
2265e2b1b9c0Schristos 	 * at the end.
2266e2b1b9c0Schristos 	 */
2267e2b1b9c0Schristos 	if (msg->padding_off > 0) {
2268e2b1b9c0Schristos 		unsigned char *cp = isc_buffer_used(msg->buffer);
2269e2b1b9c0Schristos 		unsigned int used, remaining;
2270f2e20987Schristos 		uint16_t len, padsize = 0;
2271e2b1b9c0Schristos 
2272e2b1b9c0Schristos 		/* Check PAD */
22739742fdb4Schristos 		if ((cp[-4] != 0) || (cp[-3] != DNS_OPT_PAD) || (cp[-2] != 0) ||
2274*4ac1c27eSchristos 		    (cp[-1] != 0))
2275*4ac1c27eSchristos 		{
2276e2b1b9c0Schristos 			return (ISC_R_UNEXPECTED);
22779742fdb4Schristos 		}
2278e2b1b9c0Schristos 
2279e2b1b9c0Schristos 		/*
2280e2b1b9c0Schristos 		 * Zero-fill the PAD to the computed size;
2281e2b1b9c0Schristos 		 * patch PAD length and OPT rdlength
2282e2b1b9c0Schristos 		 */
2283e2b1b9c0Schristos 
2284e2b1b9c0Schristos 		/* Aligned used length + reserved to padding block */
2285e2b1b9c0Schristos 		used = isc_buffer_usedlength(msg->buffer);
2286e2b1b9c0Schristos 		if (msg->padding != 0) {
22879742fdb4Schristos 			padsize = ((uint16_t)used + msg->reserved) %
22889742fdb4Schristos 				  msg->padding;
2289e2b1b9c0Schristos 		}
2290e2b1b9c0Schristos 		if (padsize != 0) {
2291e2b1b9c0Schristos 			padsize = msg->padding - padsize;
2292e2b1b9c0Schristos 		}
2293e2b1b9c0Schristos 		/* Stay below the available length */
2294e2b1b9c0Schristos 		remaining = isc_buffer_availablelength(msg->buffer);
22959742fdb4Schristos 		if (padsize > remaining) {
2296e2b1b9c0Schristos 			padsize = remaining;
22979742fdb4Schristos 		}
2298e2b1b9c0Schristos 
2299e2b1b9c0Schristos 		isc_buffer_add(msg->buffer, padsize);
2300e2b1b9c0Schristos 		memset(cp, 0, padsize);
2301e2b1b9c0Schristos 		cp[-2] = (unsigned char)((padsize & 0xff00U) >> 8);
2302e2b1b9c0Schristos 		cp[-1] = (unsigned char)(padsize & 0x00ffU);
2303e2b1b9c0Schristos 		cp -= msg->padding_off;
2304f2e20987Schristos 		len = ((uint16_t)(cp[-2])) << 8;
2305f2e20987Schristos 		len |= ((uint16_t)(cp[-1]));
2306e2b1b9c0Schristos 		len += padsize;
2307e2b1b9c0Schristos 		cp[-2] = (unsigned char)((len & 0xff00U) >> 8);
2308e2b1b9c0Schristos 		cp[-1] = (unsigned char)(len & 0x00ffU);
2309e2b1b9c0Schristos 	}
2310e2b1b9c0Schristos 
2311e2b1b9c0Schristos 	/*
2312e2b1b9c0Schristos 	 * If we're adding a TSIG record, generate and render it.
2313e2b1b9c0Schristos 	 */
2314e2b1b9c0Schristos 	if (msg->tsigkey != NULL) {
2315e2b1b9c0Schristos 		dns_message_renderrelease(msg, msg->sig_reserved);
2316e2b1b9c0Schristos 		msg->sig_reserved = 0;
2317e2b1b9c0Schristos 		result = dns_tsig_sign(msg);
23189742fdb4Schristos 		if (result != ISC_R_SUCCESS) {
2319e2b1b9c0Schristos 			return (result);
23209742fdb4Schristos 		}
2321e2b1b9c0Schristos 		count = 0;
2322e2b1b9c0Schristos 		result = renderset(msg->tsig, msg->tsigname, msg->cctx,
2323e2b1b9c0Schristos 				   msg->buffer, msg->reserved, 0, &count);
2324e2b1b9c0Schristos 		msg->counts[DNS_SECTION_ADDITIONAL] += count;
23259742fdb4Schristos 		if (result != ISC_R_SUCCESS) {
2326e2b1b9c0Schristos 			return (result);
2327e2b1b9c0Schristos 		}
23289742fdb4Schristos 	}
2329e2b1b9c0Schristos 
2330e2b1b9c0Schristos 	/*
2331e2b1b9c0Schristos 	 * If we're adding a SIG(0) record, generate and render it.
2332e2b1b9c0Schristos 	 */
2333e2b1b9c0Schristos 	if (msg->sig0key != NULL) {
2334e2b1b9c0Schristos 		dns_message_renderrelease(msg, msg->sig_reserved);
2335e2b1b9c0Schristos 		msg->sig_reserved = 0;
2336e2b1b9c0Schristos 		result = dns_dnssec_signmessage(msg, msg->sig0key);
23379742fdb4Schristos 		if (result != ISC_R_SUCCESS) {
2338e2b1b9c0Schristos 			return (result);
23399742fdb4Schristos 		}
2340e2b1b9c0Schristos 		count = 0;
2341e2b1b9c0Schristos 		/*
2342e2b1b9c0Schristos 		 * Note: dns_rootname is used here, not msg->sig0name, since
2343e2b1b9c0Schristos 		 * the owner name of a SIG(0) is irrelevant, and will not
2344e2b1b9c0Schristos 		 * be set in a message being rendered.
2345e2b1b9c0Schristos 		 */
2346e2b1b9c0Schristos 		result = renderset(msg->sig0, dns_rootname, msg->cctx,
2347e2b1b9c0Schristos 				   msg->buffer, msg->reserved, 0, &count);
2348e2b1b9c0Schristos 		msg->counts[DNS_SECTION_ADDITIONAL] += count;
23499742fdb4Schristos 		if (result != ISC_R_SUCCESS) {
2350e2b1b9c0Schristos 			return (result);
2351e2b1b9c0Schristos 		}
23529742fdb4Schristos 	}
2353e2b1b9c0Schristos 
2354e2b1b9c0Schristos 	isc_buffer_usedregion(msg->buffer, &r);
2355e2b1b9c0Schristos 	isc_buffer_init(&tmpbuf, r.base, r.length);
2356e2b1b9c0Schristos 
2357e2b1b9c0Schristos 	dns_message_renderheader(msg, &tmpbuf);
2358e2b1b9c0Schristos 
2359e2b1b9c0Schristos 	msg->buffer = NULL; /* forget about this buffer only on success XXX */
2360e2b1b9c0Schristos 
2361e2b1b9c0Schristos 	return (ISC_R_SUCCESS);
2362e2b1b9c0Schristos }
2363e2b1b9c0Schristos 
2364e2b1b9c0Schristos void
dns_message_renderreset(dns_message_t * msg)2365e2b1b9c0Schristos dns_message_renderreset(dns_message_t *msg) {
2366e2b1b9c0Schristos 	unsigned int i;
2367e2b1b9c0Schristos 	dns_name_t *name;
2368e2b1b9c0Schristos 	dns_rdataset_t *rds;
2369e2b1b9c0Schristos 
2370e2b1b9c0Schristos 	/*
2371e2b1b9c0Schristos 	 * Reset the message so that it may be rendered again.
2372e2b1b9c0Schristos 	 */
2373e2b1b9c0Schristos 
2374e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
2375e2b1b9c0Schristos 	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
2376e2b1b9c0Schristos 
2377e2b1b9c0Schristos 	msg->buffer = NULL;
2378e2b1b9c0Schristos 
2379e2b1b9c0Schristos 	for (i = 0; i < DNS_SECTION_MAX; i++) {
2380e2b1b9c0Schristos 		msg->cursors[i] = NULL;
2381e2b1b9c0Schristos 		msg->counts[i] = 0;
23829742fdb4Schristos 		for (name = ISC_LIST_HEAD(msg->sections[i]); name != NULL;
23839742fdb4Schristos 		     name = ISC_LIST_NEXT(name, link))
23849742fdb4Schristos 		{
23859742fdb4Schristos 			for (rds = ISC_LIST_HEAD(name->list); rds != NULL;
23869742fdb4Schristos 			     rds = ISC_LIST_NEXT(rds, link))
23879742fdb4Schristos 			{
2388e2b1b9c0Schristos 				rds->attributes &= ~DNS_RDATASETATTR_RENDERED;
2389e2b1b9c0Schristos 			}
2390e2b1b9c0Schristos 		}
2391e2b1b9c0Schristos 	}
23929742fdb4Schristos 	if (msg->tsigname != NULL) {
2393e2b1b9c0Schristos 		dns_message_puttempname(msg, &msg->tsigname);
23949742fdb4Schristos 	}
2395e2b1b9c0Schristos 	if (msg->tsig != NULL) {
2396e2b1b9c0Schristos 		dns_rdataset_disassociate(msg->tsig);
2397e2b1b9c0Schristos 		dns_message_puttemprdataset(msg, &msg->tsig);
2398e2b1b9c0Schristos 	}
2399e2b1b9c0Schristos 	if (msg->sig0 != NULL) {
2400e2b1b9c0Schristos 		dns_rdataset_disassociate(msg->sig0);
2401e2b1b9c0Schristos 		dns_message_puttemprdataset(msg, &msg->sig0);
2402e2b1b9c0Schristos 	}
2403e2b1b9c0Schristos }
2404e2b1b9c0Schristos 
2405e2b1b9c0Schristos isc_result_t
dns_message_firstname(dns_message_t * msg,dns_section_t section)2406e2b1b9c0Schristos dns_message_firstname(dns_message_t *msg, dns_section_t section) {
2407e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
2408e2b1b9c0Schristos 	REQUIRE(VALID_NAMED_SECTION(section));
2409e2b1b9c0Schristos 
2410e2b1b9c0Schristos 	msg->cursors[section] = ISC_LIST_HEAD(msg->sections[section]);
2411e2b1b9c0Schristos 
24129742fdb4Schristos 	if (msg->cursors[section] == NULL) {
2413e2b1b9c0Schristos 		return (ISC_R_NOMORE);
24149742fdb4Schristos 	}
2415e2b1b9c0Schristos 
2416e2b1b9c0Schristos 	return (ISC_R_SUCCESS);
2417e2b1b9c0Schristos }
2418e2b1b9c0Schristos 
2419e2b1b9c0Schristos isc_result_t
dns_message_nextname(dns_message_t * msg,dns_section_t section)2420e2b1b9c0Schristos dns_message_nextname(dns_message_t *msg, dns_section_t section) {
2421e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
2422e2b1b9c0Schristos 	REQUIRE(VALID_NAMED_SECTION(section));
2423e2b1b9c0Schristos 	REQUIRE(msg->cursors[section] != NULL);
2424e2b1b9c0Schristos 
2425e2b1b9c0Schristos 	msg->cursors[section] = ISC_LIST_NEXT(msg->cursors[section], link);
2426e2b1b9c0Schristos 
24279742fdb4Schristos 	if (msg->cursors[section] == NULL) {
2428e2b1b9c0Schristos 		return (ISC_R_NOMORE);
24299742fdb4Schristos 	}
2430e2b1b9c0Schristos 
2431e2b1b9c0Schristos 	return (ISC_R_SUCCESS);
2432e2b1b9c0Schristos }
2433e2b1b9c0Schristos 
2434e2b1b9c0Schristos void
dns_message_currentname(dns_message_t * msg,dns_section_t section,dns_name_t ** name)2435e2b1b9c0Schristos dns_message_currentname(dns_message_t *msg, dns_section_t section,
24369742fdb4Schristos 			dns_name_t **name) {
2437e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
2438e2b1b9c0Schristos 	REQUIRE(VALID_NAMED_SECTION(section));
2439e2b1b9c0Schristos 	REQUIRE(name != NULL && *name == NULL);
2440e2b1b9c0Schristos 	REQUIRE(msg->cursors[section] != NULL);
2441e2b1b9c0Schristos 
2442e2b1b9c0Schristos 	*name = msg->cursors[section];
2443e2b1b9c0Schristos }
2444e2b1b9c0Schristos 
2445e2b1b9c0Schristos isc_result_t
dns_message_findname(dns_message_t * msg,dns_section_t section,const dns_name_t * target,dns_rdatatype_t type,dns_rdatatype_t covers,dns_name_t ** name,dns_rdataset_t ** rdataset)2446e2b1b9c0Schristos dns_message_findname(dns_message_t *msg, dns_section_t section,
2447e2b1b9c0Schristos 		     const dns_name_t *target, dns_rdatatype_t type,
2448e2b1b9c0Schristos 		     dns_rdatatype_t covers, dns_name_t **name,
24499742fdb4Schristos 		     dns_rdataset_t **rdataset) {
2450e2b1b9c0Schristos 	dns_name_t *foundname;
2451e2b1b9c0Schristos 	isc_result_t result;
2452e2b1b9c0Schristos 
2453e2b1b9c0Schristos 	/*
2454e2b1b9c0Schristos 	 * XXX These requirements are probably too intensive, especially
2455e2b1b9c0Schristos 	 * where things can be NULL, but as they are they ensure that if
2456e2b1b9c0Schristos 	 * something is NON-NULL, indicating that the caller expects it
2457e2b1b9c0Schristos 	 * to be filled in, that we can in fact fill it in.
2458e2b1b9c0Schristos 	 */
2459e2b1b9c0Schristos 	REQUIRE(msg != NULL);
2460e2b1b9c0Schristos 	REQUIRE(VALID_SECTION(section));
2461e2b1b9c0Schristos 	REQUIRE(target != NULL);
2462e2b1b9c0Schristos 	REQUIRE(name == NULL || *name == NULL);
2463e2b1b9c0Schristos 
2464e2b1b9c0Schristos 	if (type == dns_rdatatype_any) {
2465e2b1b9c0Schristos 		REQUIRE(rdataset == NULL);
2466e2b1b9c0Schristos 	} else {
2467e2b1b9c0Schristos 		REQUIRE(rdataset == NULL || *rdataset == NULL);
2468e2b1b9c0Schristos 	}
2469e2b1b9c0Schristos 
24709742fdb4Schristos 	result = findname(&foundname, target, &msg->sections[section]);
2471e2b1b9c0Schristos 
24729742fdb4Schristos 	if (result == ISC_R_NOTFOUND) {
2473e2b1b9c0Schristos 		return (DNS_R_NXDOMAIN);
24749742fdb4Schristos 	} else if (result != ISC_R_SUCCESS) {
2475e2b1b9c0Schristos 		return (result);
24769742fdb4Schristos 	}
2477e2b1b9c0Schristos 
24789742fdb4Schristos 	if (name != NULL) {
2479e2b1b9c0Schristos 		*name = foundname;
24809742fdb4Schristos 	}
2481e2b1b9c0Schristos 
2482e2b1b9c0Schristos 	/*
2483e2b1b9c0Schristos 	 * And now look for the type.
2484e2b1b9c0Schristos 	 */
24859742fdb4Schristos 	if (ISC_UNLIKELY(type == dns_rdatatype_any)) {
2486e2b1b9c0Schristos 		return (ISC_R_SUCCESS);
24879742fdb4Schristos 	}
2488e2b1b9c0Schristos 
2489e2b1b9c0Schristos 	result = dns_message_findtype(foundname, type, covers, rdataset);
24909742fdb4Schristos 	if (result == ISC_R_NOTFOUND) {
2491e2b1b9c0Schristos 		return (DNS_R_NXRRSET);
24929742fdb4Schristos 	}
2493e2b1b9c0Schristos 
2494e2b1b9c0Schristos 	return (result);
2495e2b1b9c0Schristos }
2496e2b1b9c0Schristos 
2497e2b1b9c0Schristos void
dns_message_movename(dns_message_t * msg,dns_name_t * name,dns_section_t fromsection,dns_section_t tosection)2498e2b1b9c0Schristos dns_message_movename(dns_message_t *msg, dns_name_t *name,
24999742fdb4Schristos 		     dns_section_t fromsection, dns_section_t tosection) {
2500e2b1b9c0Schristos 	REQUIRE(msg != NULL);
2501e2b1b9c0Schristos 	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
2502e2b1b9c0Schristos 	REQUIRE(name != NULL);
2503e2b1b9c0Schristos 	REQUIRE(VALID_NAMED_SECTION(fromsection));
2504e2b1b9c0Schristos 	REQUIRE(VALID_NAMED_SECTION(tosection));
2505e2b1b9c0Schristos 
2506e2b1b9c0Schristos 	/*
2507e2b1b9c0Schristos 	 * Unlink the name from the old section
2508e2b1b9c0Schristos 	 */
2509e2b1b9c0Schristos 	ISC_LIST_UNLINK(msg->sections[fromsection], name, link);
2510e2b1b9c0Schristos 	ISC_LIST_APPEND(msg->sections[tosection], name, link);
2511e2b1b9c0Schristos }
2512e2b1b9c0Schristos 
2513e2b1b9c0Schristos void
dns_message_addname(dns_message_t * msg,dns_name_t * name,dns_section_t section)2514e2b1b9c0Schristos dns_message_addname(dns_message_t *msg, dns_name_t *name,
25159742fdb4Schristos 		    dns_section_t section) {
2516e2b1b9c0Schristos 	REQUIRE(msg != NULL);
2517e2b1b9c0Schristos 	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
2518e2b1b9c0Schristos 	REQUIRE(name != NULL);
2519e2b1b9c0Schristos 	REQUIRE(VALID_NAMED_SECTION(section));
2520e2b1b9c0Schristos 
2521e2b1b9c0Schristos 	ISC_LIST_APPEND(msg->sections[section], name, link);
2522e2b1b9c0Schristos }
2523e2b1b9c0Schristos 
2524e2b1b9c0Schristos void
dns_message_removename(dns_message_t * msg,dns_name_t * name,dns_section_t section)2525e2b1b9c0Schristos dns_message_removename(dns_message_t *msg, dns_name_t *name,
25269742fdb4Schristos 		       dns_section_t section) {
2527e2b1b9c0Schristos 	REQUIRE(msg != NULL);
2528e2b1b9c0Schristos 	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
2529e2b1b9c0Schristos 	REQUIRE(name != NULL);
2530e2b1b9c0Schristos 	REQUIRE(VALID_NAMED_SECTION(section));
2531e2b1b9c0Schristos 
2532e2b1b9c0Schristos 	ISC_LIST_UNLINK(msg->sections[section], name, link);
2533e2b1b9c0Schristos }
2534e2b1b9c0Schristos 
2535e2b1b9c0Schristos isc_result_t
dns_message_gettempname(dns_message_t * msg,dns_name_t ** item)2536e2b1b9c0Schristos dns_message_gettempname(dns_message_t *msg, dns_name_t **item) {
2537fadf0758Schristos 	dns_fixedname_t *fn = NULL;
2538fadf0758Schristos 
2539e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
2540e2b1b9c0Schristos 	REQUIRE(item != NULL && *item == NULL);
2541e2b1b9c0Schristos 
2542fadf0758Schristos 	fn = isc_mempool_get(msg->namepool);
2543fadf0758Schristos 	if (fn == NULL) {
2544e2b1b9c0Schristos 		return (ISC_R_NOMEMORY);
25459742fdb4Schristos 	}
2546fadf0758Schristos 	*item = dns_fixedname_initname(fn);
2547e2b1b9c0Schristos 
2548e2b1b9c0Schristos 	return (ISC_R_SUCCESS);
2549e2b1b9c0Schristos }
2550e2b1b9c0Schristos 
2551e2b1b9c0Schristos isc_result_t
dns_message_gettemprdata(dns_message_t * msg,dns_rdata_t ** item)2552e2b1b9c0Schristos dns_message_gettemprdata(dns_message_t *msg, dns_rdata_t **item) {
2553e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
2554e2b1b9c0Schristos 	REQUIRE(item != NULL && *item == NULL);
2555e2b1b9c0Schristos 
2556e2b1b9c0Schristos 	*item = newrdata(msg);
25579742fdb4Schristos 	if (*item == NULL) {
2558e2b1b9c0Schristos 		return (ISC_R_NOMEMORY);
25599742fdb4Schristos 	}
2560e2b1b9c0Schristos 
2561e2b1b9c0Schristos 	return (ISC_R_SUCCESS);
2562e2b1b9c0Schristos }
2563e2b1b9c0Schristos 
2564e2b1b9c0Schristos isc_result_t
dns_message_gettemprdataset(dns_message_t * msg,dns_rdataset_t ** item)2565e2b1b9c0Schristos dns_message_gettemprdataset(dns_message_t *msg, dns_rdataset_t **item) {
2566e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
2567e2b1b9c0Schristos 	REQUIRE(item != NULL && *item == NULL);
2568e2b1b9c0Schristos 
2569e2b1b9c0Schristos 	*item = isc_mempool_get(msg->rdspool);
25709742fdb4Schristos 	if (*item == NULL) {
2571e2b1b9c0Schristos 		return (ISC_R_NOMEMORY);
25729742fdb4Schristos 	}
2573e2b1b9c0Schristos 
2574e2b1b9c0Schristos 	dns_rdataset_init(*item);
2575e2b1b9c0Schristos 	return (ISC_R_SUCCESS);
2576e2b1b9c0Schristos }
2577e2b1b9c0Schristos 
2578e2b1b9c0Schristos isc_result_t
dns_message_gettemprdatalist(dns_message_t * msg,dns_rdatalist_t ** item)2579e2b1b9c0Schristos dns_message_gettemprdatalist(dns_message_t *msg, dns_rdatalist_t **item) {
2580e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
2581e2b1b9c0Schristos 	REQUIRE(item != NULL && *item == NULL);
2582e2b1b9c0Schristos 
2583e2b1b9c0Schristos 	*item = newrdatalist(msg);
25849742fdb4Schristos 	if (*item == NULL) {
2585e2b1b9c0Schristos 		return (ISC_R_NOMEMORY);
25869742fdb4Schristos 	}
2587e2b1b9c0Schristos 
2588e2b1b9c0Schristos 	return (ISC_R_SUCCESS);
2589e2b1b9c0Schristos }
2590e2b1b9c0Schristos 
2591e2b1b9c0Schristos void
dns_message_puttempname(dns_message_t * msg,dns_name_t ** itemp)2592e2b1b9c0Schristos dns_message_puttempname(dns_message_t *msg, dns_name_t **itemp) {
2593fadf0758Schristos 	dns_name_t *item = NULL;
2594e2b1b9c0Schristos 
2595e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
2596e2b1b9c0Schristos 	REQUIRE(itemp != NULL && *itemp != NULL);
2597fadf0758Schristos 
2598e2b1b9c0Schristos 	item = *itemp;
25999742fdb4Schristos 	*itemp = NULL;
2600fadf0758Schristos 
2601e2b1b9c0Schristos 	REQUIRE(!ISC_LINK_LINKED(item, link));
2602e2b1b9c0Schristos 	REQUIRE(ISC_LIST_HEAD(item->list) == NULL);
2603e2b1b9c0Schristos 
2604fadf0758Schristos 	/*
2605fadf0758Schristos 	 * we need to check this in case dns_name_dup() was used.
2606fadf0758Schristos 	 */
26079742fdb4Schristos 	if (dns_name_dynamic(item)) {
2608e2b1b9c0Schristos 		dns_name_free(item, msg->mctx);
26099742fdb4Schristos 	}
2610fadf0758Schristos 
2611fadf0758Schristos 	/*
2612fadf0758Schristos 	 * 'name' is the first field in dns_fixedname_t, so putting
2613fadf0758Schristos 	 * back the address of name is the same as putting back
2614fadf0758Schristos 	 * the fixedname.
2615fadf0758Schristos 	 */
2616e2b1b9c0Schristos 	isc_mempool_put(msg->namepool, item);
2617e2b1b9c0Schristos }
2618e2b1b9c0Schristos 
2619e2b1b9c0Schristos void
dns_message_puttemprdata(dns_message_t * msg,dns_rdata_t ** item)2620e2b1b9c0Schristos dns_message_puttemprdata(dns_message_t *msg, dns_rdata_t **item) {
2621e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
2622e2b1b9c0Schristos 	REQUIRE(item != NULL && *item != NULL);
2623e2b1b9c0Schristos 
2624e2b1b9c0Schristos 	releaserdata(msg, *item);
2625e2b1b9c0Schristos 	*item = NULL;
2626e2b1b9c0Schristos }
2627e2b1b9c0Schristos 
2628e2b1b9c0Schristos void
dns_message_puttemprdataset(dns_message_t * msg,dns_rdataset_t ** item)2629e2b1b9c0Schristos dns_message_puttemprdataset(dns_message_t *msg, dns_rdataset_t **item) {
2630e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
2631e2b1b9c0Schristos 	REQUIRE(item != NULL && *item != NULL);
2632e2b1b9c0Schristos 
2633e2b1b9c0Schristos 	REQUIRE(!dns_rdataset_isassociated(*item));
2634e2b1b9c0Schristos 	isc_mempool_put(msg->rdspool, *item);
2635e2b1b9c0Schristos 	*item = NULL;
2636e2b1b9c0Schristos }
2637e2b1b9c0Schristos 
2638e2b1b9c0Schristos void
dns_message_puttemprdatalist(dns_message_t * msg,dns_rdatalist_t ** item)2639e2b1b9c0Schristos dns_message_puttemprdatalist(dns_message_t *msg, dns_rdatalist_t **item) {
2640e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
2641e2b1b9c0Schristos 	REQUIRE(item != NULL && *item != NULL);
2642e2b1b9c0Schristos 
2643e2b1b9c0Schristos 	releaserdatalist(msg, *item);
2644e2b1b9c0Schristos 	*item = NULL;
2645e2b1b9c0Schristos }
2646e2b1b9c0Schristos 
2647e2b1b9c0Schristos isc_result_t
dns_message_peekheader(isc_buffer_t * source,dns_messageid_t * idp,unsigned int * flagsp)2648e2b1b9c0Schristos dns_message_peekheader(isc_buffer_t *source, dns_messageid_t *idp,
26499742fdb4Schristos 		       unsigned int *flagsp) {
2650e2b1b9c0Schristos 	isc_region_t r;
2651e2b1b9c0Schristos 	isc_buffer_t buffer;
2652e2b1b9c0Schristos 	dns_messageid_t id;
2653e2b1b9c0Schristos 	unsigned int flags;
2654e2b1b9c0Schristos 
2655e2b1b9c0Schristos 	REQUIRE(source != NULL);
2656e2b1b9c0Schristos 
2657e2b1b9c0Schristos 	buffer = *source;
2658e2b1b9c0Schristos 
2659e2b1b9c0Schristos 	isc_buffer_remainingregion(&buffer, &r);
26609742fdb4Schristos 	if (r.length < DNS_MESSAGE_HEADERLEN) {
2661e2b1b9c0Schristos 		return (ISC_R_UNEXPECTEDEND);
26629742fdb4Schristos 	}
2663e2b1b9c0Schristos 
2664e2b1b9c0Schristos 	id = isc_buffer_getuint16(&buffer);
2665e2b1b9c0Schristos 	flags = isc_buffer_getuint16(&buffer);
2666e2b1b9c0Schristos 	flags &= DNS_MESSAGE_FLAG_MASK;
2667e2b1b9c0Schristos 
26689742fdb4Schristos 	if (flagsp != NULL) {
2669e2b1b9c0Schristos 		*flagsp = flags;
26709742fdb4Schristos 	}
26719742fdb4Schristos 	if (idp != NULL) {
2672e2b1b9c0Schristos 		*idp = id;
26739742fdb4Schristos 	}
2674e2b1b9c0Schristos 
2675e2b1b9c0Schristos 	return (ISC_R_SUCCESS);
2676e2b1b9c0Schristos }
2677e2b1b9c0Schristos 
2678e2b1b9c0Schristos isc_result_t
dns_message_reply(dns_message_t * msg,bool want_question_section)2679f2e20987Schristos dns_message_reply(dns_message_t *msg, bool want_question_section) {
2680e2b1b9c0Schristos 	unsigned int clear_from;
2681e2b1b9c0Schristos 	isc_result_t result;
2682e2b1b9c0Schristos 
2683e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
2684e2b1b9c0Schristos 	REQUIRE((msg->flags & DNS_MESSAGEFLAG_QR) == 0);
2685e2b1b9c0Schristos 
26869742fdb4Schristos 	if (!msg->header_ok) {
2687e2b1b9c0Schristos 		return (DNS_R_FORMERR);
26889742fdb4Schristos 	}
26899742fdb4Schristos 	if (msg->opcode != dns_opcode_query && msg->opcode != dns_opcode_notify)
26909742fdb4Schristos 	{
2691f2e20987Schristos 		want_question_section = false;
26929742fdb4Schristos 	}
26939742fdb4Schristos 	if (msg->opcode == dns_opcode_update) {
2694e2b1b9c0Schristos 		clear_from = DNS_SECTION_PREREQUISITE;
26959742fdb4Schristos 	} else if (want_question_section) {
26969742fdb4Schristos 		if (!msg->question_ok) {
2697e2b1b9c0Schristos 			return (DNS_R_FORMERR);
26989742fdb4Schristos 		}
2699e2b1b9c0Schristos 		clear_from = DNS_SECTION_ANSWER;
27009742fdb4Schristos 	} else {
2701e2b1b9c0Schristos 		clear_from = DNS_SECTION_QUESTION;
27029742fdb4Schristos 	}
2703e2b1b9c0Schristos 	msg->from_to_wire = DNS_MESSAGE_INTENTRENDER;
2704e2b1b9c0Schristos 	msgresetnames(msg, clear_from);
2705e2b1b9c0Schristos 	msgresetopt(msg);
2706f2e20987Schristos 	msgresetsigs(msg, true);
2707e2b1b9c0Schristos 	msginitprivate(msg);
2708e2b1b9c0Schristos 	/*
2709e2b1b9c0Schristos 	 * We now clear most flags and then set QR, ensuring that the
2710e2b1b9c0Schristos 	 * reply's flags will be in a reasonable state.
2711e2b1b9c0Schristos 	 */
27129742fdb4Schristos 	if (msg->opcode == dns_opcode_query) {
2713e2b1b9c0Schristos 		msg->flags &= DNS_MESSAGE_REPLYPRESERVE;
27149742fdb4Schristos 	} else {
2715e2b1b9c0Schristos 		msg->flags = 0;
27169742fdb4Schristos 	}
2717e2b1b9c0Schristos 	msg->flags |= DNS_MESSAGEFLAG_QR;
2718e2b1b9c0Schristos 
2719e2b1b9c0Schristos 	/*
2720e2b1b9c0Schristos 	 * This saves the query TSIG status, if the query was signed, and
2721e2b1b9c0Schristos 	 * reserves space in the reply for the TSIG.
2722e2b1b9c0Schristos 	 */
2723e2b1b9c0Schristos 	if (msg->tsigkey != NULL) {
2724e2b1b9c0Schristos 		unsigned int otherlen = 0;
2725e2b1b9c0Schristos 		msg->querytsigstatus = msg->tsigstatus;
2726e2b1b9c0Schristos 		msg->tsigstatus = dns_rcode_noerror;
27279742fdb4Schristos 		if (msg->querytsigstatus == dns_tsigerror_badtime) {
2728e2b1b9c0Schristos 			otherlen = 6;
27299742fdb4Schristos 		}
2730e2b1b9c0Schristos 		msg->sig_reserved = spacefortsig(msg->tsigkey, otherlen);
2731e2b1b9c0Schristos 		result = dns_message_renderreserve(msg, msg->sig_reserved);
2732e2b1b9c0Schristos 		if (result != ISC_R_SUCCESS) {
2733e2b1b9c0Schristos 			msg->sig_reserved = 0;
2734e2b1b9c0Schristos 			return (result);
2735e2b1b9c0Schristos 		}
2736e2b1b9c0Schristos 	}
2737e2b1b9c0Schristos 	if (msg->saved.base != NULL) {
2738e2b1b9c0Schristos 		msg->query.base = msg->saved.base;
2739e2b1b9c0Schristos 		msg->query.length = msg->saved.length;
2740e2b1b9c0Schristos 		msg->free_query = msg->free_saved;
2741e2b1b9c0Schristos 		msg->saved.base = NULL;
2742e2b1b9c0Schristos 		msg->saved.length = 0;
2743e2b1b9c0Schristos 		msg->free_saved = 0;
2744e2b1b9c0Schristos 	}
2745e2b1b9c0Schristos 
2746e2b1b9c0Schristos 	return (ISC_R_SUCCESS);
2747e2b1b9c0Schristos }
2748e2b1b9c0Schristos 
2749e2b1b9c0Schristos dns_rdataset_t *
dns_message_getopt(dns_message_t * msg)2750e2b1b9c0Schristos dns_message_getopt(dns_message_t *msg) {
2751e2b1b9c0Schristos 	/*
2752e2b1b9c0Schristos 	 * Get the OPT record for 'msg'.
2753e2b1b9c0Schristos 	 */
2754e2b1b9c0Schristos 
2755e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
2756e2b1b9c0Schristos 
2757e2b1b9c0Schristos 	return (msg->opt);
2758e2b1b9c0Schristos }
2759e2b1b9c0Schristos 
2760e2b1b9c0Schristos isc_result_t
dns_message_setopt(dns_message_t * msg,dns_rdataset_t * opt)2761e2b1b9c0Schristos dns_message_setopt(dns_message_t *msg, dns_rdataset_t *opt) {
2762e2b1b9c0Schristos 	isc_result_t result;
2763e2b1b9c0Schristos 	dns_rdata_t rdata = DNS_RDATA_INIT;
2764e2b1b9c0Schristos 
2765e2b1b9c0Schristos 	/*
2766e2b1b9c0Schristos 	 * Set the OPT record for 'msg'.
2767e2b1b9c0Schristos 	 */
2768e2b1b9c0Schristos 
2769e2b1b9c0Schristos 	/*
2770e2b1b9c0Schristos 	 * The space required for an OPT record is:
2771e2b1b9c0Schristos 	 *
2772e2b1b9c0Schristos 	 *	1 byte for the name
2773e2b1b9c0Schristos 	 *	2 bytes for the type
2774e2b1b9c0Schristos 	 *	2 bytes for the class
2775e2b1b9c0Schristos 	 *	4 bytes for the ttl
2776e2b1b9c0Schristos 	 *	2 bytes for the rdata length
2777e2b1b9c0Schristos 	 * ---------------------------------
2778e2b1b9c0Schristos 	 *     11 bytes
2779e2b1b9c0Schristos 	 *
2780e2b1b9c0Schristos 	 * plus the length of the rdata.
2781e2b1b9c0Schristos 	 */
2782e2b1b9c0Schristos 
2783e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
2784e2b1b9c0Schristos 	REQUIRE(opt->type == dns_rdatatype_opt);
2785e2b1b9c0Schristos 	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
2786e2b1b9c0Schristos 	REQUIRE(msg->state == DNS_SECTION_ANY);
2787e2b1b9c0Schristos 
2788e2b1b9c0Schristos 	msgresetopt(msg);
2789e2b1b9c0Schristos 
2790e2b1b9c0Schristos 	result = dns_rdataset_first(opt);
27919742fdb4Schristos 	if (result != ISC_R_SUCCESS) {
2792e2b1b9c0Schristos 		goto cleanup;
27939742fdb4Schristos 	}
2794e2b1b9c0Schristos 	dns_rdataset_current(opt, &rdata);
2795e2b1b9c0Schristos 	msg->opt_reserved = 11 + rdata.length;
2796e2b1b9c0Schristos 	result = dns_message_renderreserve(msg, msg->opt_reserved);
2797e2b1b9c0Schristos 	if (result != ISC_R_SUCCESS) {
2798e2b1b9c0Schristos 		msg->opt_reserved = 0;
2799e2b1b9c0Schristos 		goto cleanup;
2800e2b1b9c0Schristos 	}
2801e2b1b9c0Schristos 
2802e2b1b9c0Schristos 	msg->opt = opt;
2803e2b1b9c0Schristos 
2804e2b1b9c0Schristos 	return (ISC_R_SUCCESS);
2805e2b1b9c0Schristos 
2806e2b1b9c0Schristos cleanup:
2807e2b1b9c0Schristos 	dns_rdataset_disassociate(opt);
2808e2b1b9c0Schristos 	dns_message_puttemprdataset(msg, &opt);
2809e2b1b9c0Schristos 	return (result);
2810e2b1b9c0Schristos }
2811e2b1b9c0Schristos 
2812e2b1b9c0Schristos dns_rdataset_t *
dns_message_gettsig(dns_message_t * msg,const dns_name_t ** owner)2813e2b1b9c0Schristos dns_message_gettsig(dns_message_t *msg, const dns_name_t **owner) {
2814e2b1b9c0Schristos 	/*
2815e2b1b9c0Schristos 	 * Get the TSIG record and owner for 'msg'.
2816e2b1b9c0Schristos 	 */
2817e2b1b9c0Schristos 
2818e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
2819e2b1b9c0Schristos 	REQUIRE(owner == NULL || *owner == NULL);
2820e2b1b9c0Schristos 
28219742fdb4Schristos 	if (owner != NULL) {
2822e2b1b9c0Schristos 		*owner = msg->tsigname;
28239742fdb4Schristos 	}
2824e2b1b9c0Schristos 	return (msg->tsig);
2825e2b1b9c0Schristos }
2826e2b1b9c0Schristos 
2827e2b1b9c0Schristos isc_result_t
dns_message_settsigkey(dns_message_t * msg,dns_tsigkey_t * key)2828e2b1b9c0Schristos dns_message_settsigkey(dns_message_t *msg, dns_tsigkey_t *key) {
2829e2b1b9c0Schristos 	isc_result_t result;
2830e2b1b9c0Schristos 
2831e2b1b9c0Schristos 	/*
2832e2b1b9c0Schristos 	 * Set the TSIG key for 'msg'
2833e2b1b9c0Schristos 	 */
2834e2b1b9c0Schristos 
2835e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
2836e2b1b9c0Schristos 
2837e2b1b9c0Schristos 	if (key == NULL && msg->tsigkey != NULL) {
2838e2b1b9c0Schristos 		if (msg->sig_reserved != 0) {
2839e2b1b9c0Schristos 			dns_message_renderrelease(msg, msg->sig_reserved);
2840e2b1b9c0Schristos 			msg->sig_reserved = 0;
2841e2b1b9c0Schristos 		}
2842e2b1b9c0Schristos 		dns_tsigkey_detach(&msg->tsigkey);
2843e2b1b9c0Schristos 	}
2844e2b1b9c0Schristos 	if (key != NULL) {
2845e2b1b9c0Schristos 		REQUIRE(msg->tsigkey == NULL && msg->sig0key == NULL);
2846e2b1b9c0Schristos 		dns_tsigkey_attach(key, &msg->tsigkey);
2847e2b1b9c0Schristos 		if (msg->from_to_wire == DNS_MESSAGE_INTENTRENDER) {
2848e2b1b9c0Schristos 			msg->sig_reserved = spacefortsig(msg->tsigkey, 0);
2849e2b1b9c0Schristos 			result = dns_message_renderreserve(msg,
2850e2b1b9c0Schristos 							   msg->sig_reserved);
2851e2b1b9c0Schristos 			if (result != ISC_R_SUCCESS) {
2852e2b1b9c0Schristos 				dns_tsigkey_detach(&msg->tsigkey);
2853e2b1b9c0Schristos 				msg->sig_reserved = 0;
2854e2b1b9c0Schristos 				return (result);
2855e2b1b9c0Schristos 			}
2856e2b1b9c0Schristos 		}
2857e2b1b9c0Schristos 	}
2858e2b1b9c0Schristos 	return (ISC_R_SUCCESS);
2859e2b1b9c0Schristos }
2860e2b1b9c0Schristos 
2861e2b1b9c0Schristos dns_tsigkey_t *
dns_message_gettsigkey(dns_message_t * msg)2862e2b1b9c0Schristos dns_message_gettsigkey(dns_message_t *msg) {
2863e2b1b9c0Schristos 	/*
2864e2b1b9c0Schristos 	 * Get the TSIG key for 'msg'
2865e2b1b9c0Schristos 	 */
2866e2b1b9c0Schristos 
2867e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
2868e2b1b9c0Schristos 
2869e2b1b9c0Schristos 	return (msg->tsigkey);
2870e2b1b9c0Schristos }
2871e2b1b9c0Schristos 
2872e2b1b9c0Schristos isc_result_t
dns_message_setquerytsig(dns_message_t * msg,isc_buffer_t * querytsig)2873e2b1b9c0Schristos dns_message_setquerytsig(dns_message_t *msg, isc_buffer_t *querytsig) {
2874e2b1b9c0Schristos 	dns_rdata_t *rdata = NULL;
2875e2b1b9c0Schristos 	dns_rdatalist_t *list = NULL;
2876e2b1b9c0Schristos 	dns_rdataset_t *set = NULL;
2877e2b1b9c0Schristos 	isc_buffer_t *buf = NULL;
2878e2b1b9c0Schristos 	isc_region_t r;
2879e2b1b9c0Schristos 	isc_result_t result;
2880e2b1b9c0Schristos 
2881e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
2882e2b1b9c0Schristos 	REQUIRE(msg->querytsig == NULL);
2883e2b1b9c0Schristos 
28849742fdb4Schristos 	if (querytsig == NULL) {
2885e2b1b9c0Schristos 		return (ISC_R_SUCCESS);
28869742fdb4Schristos 	}
2887e2b1b9c0Schristos 
2888e2b1b9c0Schristos 	result = dns_message_gettemprdata(msg, &rdata);
28899742fdb4Schristos 	if (result != ISC_R_SUCCESS) {
2890e2b1b9c0Schristos 		goto cleanup;
28919742fdb4Schristos 	}
2892e2b1b9c0Schristos 
2893e2b1b9c0Schristos 	result = dns_message_gettemprdatalist(msg, &list);
28949742fdb4Schristos 	if (result != ISC_R_SUCCESS) {
2895e2b1b9c0Schristos 		goto cleanup;
28969742fdb4Schristos 	}
2897e2b1b9c0Schristos 	result = dns_message_gettemprdataset(msg, &set);
28989742fdb4Schristos 	if (result != ISC_R_SUCCESS) {
2899e2b1b9c0Schristos 		goto cleanup;
29009742fdb4Schristos 	}
2901e2b1b9c0Schristos 
2902e2b1b9c0Schristos 	isc_buffer_usedregion(querytsig, &r);
29039742fdb4Schristos 	isc_buffer_allocate(msg->mctx, &buf, r.length);
2904e2b1b9c0Schristos 	isc_buffer_putmem(buf, r.base, r.length);
2905e2b1b9c0Schristos 	isc_buffer_usedregion(buf, &r);
2906e2b1b9c0Schristos 	dns_rdata_init(rdata);
2907e2b1b9c0Schristos 	dns_rdata_fromregion(rdata, dns_rdataclass_any, dns_rdatatype_tsig, &r);
2908e2b1b9c0Schristos 	dns_message_takebuffer(msg, &buf);
2909e2b1b9c0Schristos 	ISC_LIST_APPEND(list->rdata, rdata, link);
2910e2b1b9c0Schristos 	result = dns_rdatalist_tordataset(list, set);
29119742fdb4Schristos 	if (result != ISC_R_SUCCESS) {
2912e2b1b9c0Schristos 		goto cleanup;
29139742fdb4Schristos 	}
2914e2b1b9c0Schristos 
2915e2b1b9c0Schristos 	msg->querytsig = set;
2916e2b1b9c0Schristos 
2917e2b1b9c0Schristos 	return (result);
2918e2b1b9c0Schristos 
2919e2b1b9c0Schristos cleanup:
29209742fdb4Schristos 	if (rdata != NULL) {
2921e2b1b9c0Schristos 		dns_message_puttemprdata(msg, &rdata);
29229742fdb4Schristos 	}
29239742fdb4Schristos 	if (list != NULL) {
2924e2b1b9c0Schristos 		dns_message_puttemprdatalist(msg, &list);
29259742fdb4Schristos 	}
29269742fdb4Schristos 	if (set != NULL) {
2927e2b1b9c0Schristos 		dns_message_puttemprdataset(msg, &set);
29289742fdb4Schristos 	}
2929e2b1b9c0Schristos 	return (ISC_R_NOMEMORY);
2930e2b1b9c0Schristos }
2931e2b1b9c0Schristos 
2932e2b1b9c0Schristos isc_result_t
dns_message_getquerytsig(dns_message_t * msg,isc_mem_t * mctx,isc_buffer_t ** querytsig)2933e2b1b9c0Schristos dns_message_getquerytsig(dns_message_t *msg, isc_mem_t *mctx,
2934e2b1b9c0Schristos 			 isc_buffer_t **querytsig) {
2935e2b1b9c0Schristos 	isc_result_t result;
2936e2b1b9c0Schristos 	dns_rdata_t rdata = DNS_RDATA_INIT;
2937e2b1b9c0Schristos 	isc_region_t r;
2938e2b1b9c0Schristos 
2939e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
2940e2b1b9c0Schristos 	REQUIRE(mctx != NULL);
2941e2b1b9c0Schristos 	REQUIRE(querytsig != NULL && *querytsig == NULL);
2942e2b1b9c0Schristos 
29439742fdb4Schristos 	if (msg->tsig == NULL) {
2944e2b1b9c0Schristos 		return (ISC_R_SUCCESS);
29459742fdb4Schristos 	}
2946e2b1b9c0Schristos 
2947e2b1b9c0Schristos 	result = dns_rdataset_first(msg->tsig);
29489742fdb4Schristos 	if (result != ISC_R_SUCCESS) {
2949e2b1b9c0Schristos 		return (result);
29509742fdb4Schristos 	}
2951e2b1b9c0Schristos 	dns_rdataset_current(msg->tsig, &rdata);
2952e2b1b9c0Schristos 	dns_rdata_toregion(&rdata, &r);
2953e2b1b9c0Schristos 
29549742fdb4Schristos 	isc_buffer_allocate(mctx, querytsig, r.length);
2955e2b1b9c0Schristos 	isc_buffer_putmem(*querytsig, r.base, r.length);
2956e2b1b9c0Schristos 	return (ISC_R_SUCCESS);
2957e2b1b9c0Schristos }
2958e2b1b9c0Schristos 
2959e2b1b9c0Schristos dns_rdataset_t *
dns_message_getsig0(dns_message_t * msg,const dns_name_t ** owner)2960e2b1b9c0Schristos dns_message_getsig0(dns_message_t *msg, const dns_name_t **owner) {
2961e2b1b9c0Schristos 	/*
2962e2b1b9c0Schristos 	 * Get the SIG(0) record for 'msg'.
2963e2b1b9c0Schristos 	 */
2964e2b1b9c0Schristos 
2965e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
2966e2b1b9c0Schristos 	REQUIRE(owner == NULL || *owner == NULL);
2967e2b1b9c0Schristos 
2968e2b1b9c0Schristos 	if (msg->sig0 != NULL && owner != NULL) {
2969e2b1b9c0Schristos 		/* If dns_message_getsig0 is called on a rendered message
2970e2b1b9c0Schristos 		 * after the SIG(0) has been applied, we need to return the
2971e2b1b9c0Schristos 		 * root name, not NULL.
2972e2b1b9c0Schristos 		 */
29739742fdb4Schristos 		if (msg->sig0name == NULL) {
2974e2b1b9c0Schristos 			*owner = dns_rootname;
29759742fdb4Schristos 		} else {
2976e2b1b9c0Schristos 			*owner = msg->sig0name;
2977e2b1b9c0Schristos 		}
29789742fdb4Schristos 	}
2979e2b1b9c0Schristos 	return (msg->sig0);
2980e2b1b9c0Schristos }
2981e2b1b9c0Schristos 
2982e2b1b9c0Schristos isc_result_t
dns_message_setsig0key(dns_message_t * msg,dst_key_t * key)2983e2b1b9c0Schristos dns_message_setsig0key(dns_message_t *msg, dst_key_t *key) {
2984e2b1b9c0Schristos 	isc_region_t r;
2985e2b1b9c0Schristos 	unsigned int x;
2986e2b1b9c0Schristos 	isc_result_t result;
2987e2b1b9c0Schristos 
2988e2b1b9c0Schristos 	/*
2989e2b1b9c0Schristos 	 * Set the SIG(0) key for 'msg'
2990e2b1b9c0Schristos 	 */
2991e2b1b9c0Schristos 
2992e2b1b9c0Schristos 	/*
2993e2b1b9c0Schristos 	 * The space required for an SIG(0) record is:
2994e2b1b9c0Schristos 	 *
2995e2b1b9c0Schristos 	 *	1 byte for the name
2996e2b1b9c0Schristos 	 *	2 bytes for the type
2997e2b1b9c0Schristos 	 *	2 bytes for the class
2998e2b1b9c0Schristos 	 *	4 bytes for the ttl
2999e2b1b9c0Schristos 	 *	2 bytes for the type covered
3000e2b1b9c0Schristos 	 *	1 byte for the algorithm
3001e2b1b9c0Schristos 	 *	1 bytes for the labels
3002e2b1b9c0Schristos 	 *	4 bytes for the original ttl
3003e2b1b9c0Schristos 	 *	4 bytes for the signature expiration
3004e2b1b9c0Schristos 	 *	4 bytes for the signature inception
3005e2b1b9c0Schristos 	 *	2 bytes for the key tag
3006e2b1b9c0Schristos 	 *	n bytes for the signer's name
3007e2b1b9c0Schristos 	 *	x bytes for the signature
3008e2b1b9c0Schristos 	 * ---------------------------------
3009e2b1b9c0Schristos 	 *     27 + n + x bytes
3010e2b1b9c0Schristos 	 */
3011e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
3012e2b1b9c0Schristos 	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
3013e2b1b9c0Schristos 	REQUIRE(msg->state == DNS_SECTION_ANY);
3014e2b1b9c0Schristos 
3015e2b1b9c0Schristos 	if (key != NULL) {
3016e2b1b9c0Schristos 		REQUIRE(msg->sig0key == NULL && msg->tsigkey == NULL);
3017e2b1b9c0Schristos 		dns_name_toregion(dst_key_name(key), &r);
3018e2b1b9c0Schristos 		result = dst_key_sigsize(key, &x);
3019e2b1b9c0Schristos 		if (result != ISC_R_SUCCESS) {
3020e2b1b9c0Schristos 			msg->sig_reserved = 0;
3021e2b1b9c0Schristos 			return (result);
3022e2b1b9c0Schristos 		}
3023e2b1b9c0Schristos 		msg->sig_reserved = 27 + r.length + x;
3024e2b1b9c0Schristos 		result = dns_message_renderreserve(msg, msg->sig_reserved);
3025e2b1b9c0Schristos 		if (result != ISC_R_SUCCESS) {
3026e2b1b9c0Schristos 			msg->sig_reserved = 0;
3027e2b1b9c0Schristos 			return (result);
3028e2b1b9c0Schristos 		}
3029e2b1b9c0Schristos 		msg->sig0key = key;
3030e2b1b9c0Schristos 	}
3031e2b1b9c0Schristos 	return (ISC_R_SUCCESS);
3032e2b1b9c0Schristos }
3033e2b1b9c0Schristos 
3034e2b1b9c0Schristos dst_key_t *
dns_message_getsig0key(dns_message_t * msg)3035e2b1b9c0Schristos dns_message_getsig0key(dns_message_t *msg) {
3036e2b1b9c0Schristos 	/*
3037e2b1b9c0Schristos 	 * Get the SIG(0) key for 'msg'
3038e2b1b9c0Schristos 	 */
3039e2b1b9c0Schristos 
3040e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
3041e2b1b9c0Schristos 
3042e2b1b9c0Schristos 	return (msg->sig0key);
3043e2b1b9c0Schristos }
3044e2b1b9c0Schristos 
3045e2b1b9c0Schristos void
dns_message_takebuffer(dns_message_t * msg,isc_buffer_t ** buffer)3046e2b1b9c0Schristos dns_message_takebuffer(dns_message_t *msg, isc_buffer_t **buffer) {
3047e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
3048e2b1b9c0Schristos 	REQUIRE(buffer != NULL);
3049e2b1b9c0Schristos 	REQUIRE(ISC_BUFFER_VALID(*buffer));
3050e2b1b9c0Schristos 
3051e2b1b9c0Schristos 	ISC_LIST_APPEND(msg->cleanup, *buffer, link);
3052e2b1b9c0Schristos 	*buffer = NULL;
3053e2b1b9c0Schristos }
3054e2b1b9c0Schristos 
3055e2b1b9c0Schristos isc_result_t
dns_message_signer(dns_message_t * msg,dns_name_t * signer)3056e2b1b9c0Schristos dns_message_signer(dns_message_t *msg, dns_name_t *signer) {
3057e2b1b9c0Schristos 	isc_result_t result = ISC_R_SUCCESS;
3058e2b1b9c0Schristos 	dns_rdata_t rdata = DNS_RDATA_INIT;
3059e2b1b9c0Schristos 
3060e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
3061e2b1b9c0Schristos 	REQUIRE(signer != NULL);
3062e2b1b9c0Schristos 	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTPARSE);
3063e2b1b9c0Schristos 
30649742fdb4Schristos 	if (msg->tsig == NULL && msg->sig0 == NULL) {
3065e2b1b9c0Schristos 		return (ISC_R_NOTFOUND);
30669742fdb4Schristos 	}
3067e2b1b9c0Schristos 
30689742fdb4Schristos 	if (msg->verify_attempted == 0) {
3069e2b1b9c0Schristos 		return (DNS_R_NOTVERIFIEDYET);
30709742fdb4Schristos 	}
3071e2b1b9c0Schristos 
3072e2b1b9c0Schristos 	if (!dns_name_hasbuffer(signer)) {
3073e2b1b9c0Schristos 		isc_buffer_t *dynbuf = NULL;
30749742fdb4Schristos 		isc_buffer_allocate(msg->mctx, &dynbuf, 512);
3075e2b1b9c0Schristos 		dns_name_setbuffer(signer, dynbuf);
3076e2b1b9c0Schristos 		dns_message_takebuffer(msg, &dynbuf);
3077e2b1b9c0Schristos 	}
3078e2b1b9c0Schristos 
3079e2b1b9c0Schristos 	if (msg->sig0 != NULL) {
3080e2b1b9c0Schristos 		dns_rdata_sig_t sig;
3081e2b1b9c0Schristos 
3082e2b1b9c0Schristos 		result = dns_rdataset_first(msg->sig0);
3083e2b1b9c0Schristos 		INSIST(result == ISC_R_SUCCESS);
3084e2b1b9c0Schristos 		dns_rdataset_current(msg->sig0, &rdata);
3085e2b1b9c0Schristos 
3086e2b1b9c0Schristos 		result = dns_rdata_tostruct(&rdata, &sig, NULL);
30879742fdb4Schristos 		if (result != ISC_R_SUCCESS) {
3088e2b1b9c0Schristos 			return (result);
30899742fdb4Schristos 		}
3090e2b1b9c0Schristos 
30919742fdb4Schristos 		if (msg->verified_sig && msg->sig0status == dns_rcode_noerror) {
3092e2b1b9c0Schristos 			result = ISC_R_SUCCESS;
30939742fdb4Schristos 		} else {
3094e2b1b9c0Schristos 			result = DNS_R_SIGINVALID;
30959742fdb4Schristos 		}
3096e2b1b9c0Schristos 		dns_name_clone(&sig.signer, signer);
3097e2b1b9c0Schristos 		dns_rdata_freestruct(&sig);
3098e2b1b9c0Schristos 	} else {
30998b4c8a26Schristos 		const dns_name_t *identity;
3100e2b1b9c0Schristos 		dns_rdata_any_tsig_t tsig;
3101e2b1b9c0Schristos 
3102e2b1b9c0Schristos 		result = dns_rdataset_first(msg->tsig);
3103e2b1b9c0Schristos 		INSIST(result == ISC_R_SUCCESS);
3104e2b1b9c0Schristos 		dns_rdataset_current(msg->tsig, &rdata);
3105e2b1b9c0Schristos 
3106e2b1b9c0Schristos 		result = dns_rdata_tostruct(&rdata, &tsig, NULL);
3107e2b1b9c0Schristos 		INSIST(result == ISC_R_SUCCESS);
31089742fdb4Schristos 		if (msg->verified_sig && msg->tsigstatus == dns_rcode_noerror &&
3109e2b1b9c0Schristos 		    tsig.error == dns_rcode_noerror)
3110e2b1b9c0Schristos 		{
3111e2b1b9c0Schristos 			result = ISC_R_SUCCESS;
3112e2b1b9c0Schristos 		} else if ((!msg->verified_sig) ||
3113*4ac1c27eSchristos 			   (msg->tsigstatus != dns_rcode_noerror))
3114*4ac1c27eSchristos 		{
3115e2b1b9c0Schristos 			result = DNS_R_TSIGVERIFYFAILURE;
3116e2b1b9c0Schristos 		} else {
3117e2b1b9c0Schristos 			INSIST(tsig.error != dns_rcode_noerror);
3118e2b1b9c0Schristos 			result = DNS_R_TSIGERRORSET;
3119e2b1b9c0Schristos 		}
3120e2b1b9c0Schristos 		dns_rdata_freestruct(&tsig);
3121e2b1b9c0Schristos 
3122e2b1b9c0Schristos 		if (msg->tsigkey == NULL) {
3123e2b1b9c0Schristos 			/*
3124e2b1b9c0Schristos 			 * If msg->tsigstatus & tsig.error are both
3125e2b1b9c0Schristos 			 * dns_rcode_noerror, the message must have been
3126e2b1b9c0Schristos 			 * verified, which means msg->tsigkey will be
3127e2b1b9c0Schristos 			 * non-NULL.
3128e2b1b9c0Schristos 			 */
3129e2b1b9c0Schristos 			INSIST(result != ISC_R_SUCCESS);
3130e2b1b9c0Schristos 		} else {
3131e2b1b9c0Schristos 			identity = dns_tsigkey_identity(msg->tsigkey);
3132e2b1b9c0Schristos 			if (identity == NULL) {
31339742fdb4Schristos 				if (result == ISC_R_SUCCESS) {
3134e2b1b9c0Schristos 					result = DNS_R_NOIDENTITY;
31359742fdb4Schristos 				}
3136e2b1b9c0Schristos 				identity = &msg->tsigkey->name;
3137e2b1b9c0Schristos 			}
3138e2b1b9c0Schristos 			dns_name_clone(identity, signer);
3139e2b1b9c0Schristos 		}
3140e2b1b9c0Schristos 	}
3141e2b1b9c0Schristos 
3142e2b1b9c0Schristos 	return (result);
3143e2b1b9c0Schristos }
3144e2b1b9c0Schristos 
3145e2b1b9c0Schristos void
dns_message_resetsig(dns_message_t * msg)3146e2b1b9c0Schristos dns_message_resetsig(dns_message_t *msg) {
3147e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
3148e2b1b9c0Schristos 	msg->verified_sig = 0;
3149e2b1b9c0Schristos 	msg->verify_attempted = 0;
3150e2b1b9c0Schristos 	msg->tsigstatus = dns_rcode_noerror;
3151e2b1b9c0Schristos 	msg->sig0status = dns_rcode_noerror;
3152e2b1b9c0Schristos 	msg->timeadjust = 0;
3153e2b1b9c0Schristos 	if (msg->tsigkey != NULL) {
3154e2b1b9c0Schristos 		dns_tsigkey_detach(&msg->tsigkey);
3155e2b1b9c0Schristos 		msg->tsigkey = NULL;
3156e2b1b9c0Schristos 	}
3157e2b1b9c0Schristos }
3158e2b1b9c0Schristos 
3159e2b1b9c0Schristos isc_result_t
dns_message_rechecksig(dns_message_t * msg,dns_view_t * view)3160e2b1b9c0Schristos dns_message_rechecksig(dns_message_t *msg, dns_view_t *view) {
3161e2b1b9c0Schristos 	dns_message_resetsig(msg);
3162e2b1b9c0Schristos 	return (dns_message_checksig(msg, view));
3163e2b1b9c0Schristos }
3164e2b1b9c0Schristos 
3165e2b1b9c0Schristos #ifdef SKAN_MSG_DEBUG
3166e2b1b9c0Schristos void
dns_message_dumpsig(dns_message_t * msg,char * txt1)3167e2b1b9c0Schristos dns_message_dumpsig(dns_message_t *msg, char *txt1) {
3168e2b1b9c0Schristos 	dns_rdata_t querytsigrdata = DNS_RDATA_INIT;
3169e2b1b9c0Schristos 	dns_rdata_any_tsig_t querytsig;
3170e2b1b9c0Schristos 	isc_result_t result;
3171e2b1b9c0Schristos 
3172e2b1b9c0Schristos 	if (msg->tsig != NULL) {
3173e2b1b9c0Schristos 		result = dns_rdataset_first(msg->tsig);
3174e2b1b9c0Schristos 		RUNTIME_CHECK(result == ISC_R_SUCCESS);
3175e2b1b9c0Schristos 		dns_rdataset_current(msg->tsig, &querytsigrdata);
3176e2b1b9c0Schristos 		result = dns_rdata_tostruct(&querytsigrdata, &querytsig, NULL);
3177e2b1b9c0Schristos 		RUNTIME_CHECK(result == ISC_R_SUCCESS);
31789742fdb4Schristos 		hexdump(txt1, "TSIG", querytsig.signature, querytsig.siglen);
3179e2b1b9c0Schristos 	}
3180e2b1b9c0Schristos 
3181e2b1b9c0Schristos 	if (msg->querytsig != NULL) {
3182e2b1b9c0Schristos 		result = dns_rdataset_first(msg->querytsig);
3183e2b1b9c0Schristos 		RUNTIME_CHECK(result == ISC_R_SUCCESS);
3184e2b1b9c0Schristos 		dns_rdataset_current(msg->querytsig, &querytsigrdata);
3185e2b1b9c0Schristos 		result = dns_rdata_tostruct(&querytsigrdata, &querytsig, NULL);
3186e2b1b9c0Schristos 		RUNTIME_CHECK(result == ISC_R_SUCCESS);
3187e2b1b9c0Schristos 		hexdump(txt1, "QUERYTSIG", querytsig.signature,
3188e2b1b9c0Schristos 			querytsig.siglen);
3189e2b1b9c0Schristos 	}
3190e2b1b9c0Schristos }
31919742fdb4Schristos #endif /* ifdef SKAN_MSG_DEBUG */
3192e2b1b9c0Schristos 
3193e2b1b9c0Schristos isc_result_t
dns_message_checksig(dns_message_t * msg,dns_view_t * view)3194e2b1b9c0Schristos dns_message_checksig(dns_message_t *msg, dns_view_t *view) {
3195e2b1b9c0Schristos 	isc_buffer_t b, msgb;
3196e2b1b9c0Schristos 
3197e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
3198e2b1b9c0Schristos 
31999742fdb4Schristos 	if (msg->tsigkey == NULL && msg->tsig == NULL && msg->sig0 == NULL) {
3200e2b1b9c0Schristos 		return (ISC_R_SUCCESS);
32019742fdb4Schristos 	}
3202e2b1b9c0Schristos 
3203e2b1b9c0Schristos 	INSIST(msg->saved.base != NULL);
3204e2b1b9c0Schristos 	isc_buffer_init(&msgb, msg->saved.base, msg->saved.length);
3205e2b1b9c0Schristos 	isc_buffer_add(&msgb, msg->saved.length);
3206e2b1b9c0Schristos 	if (msg->tsigkey != NULL || msg->tsig != NULL) {
3207e2b1b9c0Schristos #ifdef SKAN_MSG_DEBUG
3208e2b1b9c0Schristos 		dns_message_dumpsig(msg, "dns_message_checksig#1");
32099742fdb4Schristos #endif /* ifdef SKAN_MSG_DEBUG */
32109742fdb4Schristos 		if (view != NULL) {
3211e2b1b9c0Schristos 			return (dns_view_checksig(view, &msgb, msg));
32129742fdb4Schristos 		} else {
3213e2b1b9c0Schristos 			return (dns_tsig_verify(&msgb, msg, NULL, NULL));
32149742fdb4Schristos 		}
3215e2b1b9c0Schristos 	} else {
3216e2b1b9c0Schristos 		dns_rdata_t rdata = DNS_RDATA_INIT;
3217e2b1b9c0Schristos 		dns_rdata_sig_t sig;
3218e2b1b9c0Schristos 		dns_rdataset_t keyset;
3219e2b1b9c0Schristos 		isc_result_t result;
3220e2b1b9c0Schristos 
3221e2b1b9c0Schristos 		result = dns_rdataset_first(msg->sig0);
3222e2b1b9c0Schristos 		INSIST(result == ISC_R_SUCCESS);
3223e2b1b9c0Schristos 		dns_rdataset_current(msg->sig0, &rdata);
3224e2b1b9c0Schristos 
3225e2b1b9c0Schristos 		/*
3226e2b1b9c0Schristos 		 * This can occur when the message is a dynamic update, since
3227e2b1b9c0Schristos 		 * the rdata length checking is relaxed.  This should not
3228e2b1b9c0Schristos 		 * happen in a well-formed message, since the SIG(0) is only
3229e2b1b9c0Schristos 		 * looked for in the additional section, and the dynamic update
3230e2b1b9c0Schristos 		 * meta-records are in the prerequisite and update sections.
3231e2b1b9c0Schristos 		 */
32329742fdb4Schristos 		if (rdata.length == 0) {
3233e2b1b9c0Schristos 			return (ISC_R_UNEXPECTEDEND);
32349742fdb4Schristos 		}
3235e2b1b9c0Schristos 
3236*4ac1c27eSchristos 		result = dns_rdata_tostruct(&rdata, &sig, NULL);
32379742fdb4Schristos 		if (result != ISC_R_SUCCESS) {
3238e2b1b9c0Schristos 			return (result);
32399742fdb4Schristos 		}
3240e2b1b9c0Schristos 
3241e2b1b9c0Schristos 		dns_rdataset_init(&keyset);
32429742fdb4Schristos 		if (view == NULL) {
3243*4ac1c27eSchristos 			result = DNS_R_KEYUNAUTHORIZED;
3244*4ac1c27eSchristos 			goto freesig;
32459742fdb4Schristos 		}
3246e2b1b9c0Schristos 		result = dns_view_simplefind(view, &sig.signer,
32479742fdb4Schristos 					     dns_rdatatype_key /* SIG(0) */, 0,
32489742fdb4Schristos 					     0, false, &keyset, NULL);
3249e2b1b9c0Schristos 
3250e2b1b9c0Schristos 		if (result != ISC_R_SUCCESS) {
3251e2b1b9c0Schristos 			/* XXXBEW Should possibly create a fetch here */
3252e2b1b9c0Schristos 			result = DNS_R_KEYUNAUTHORIZED;
3253e2b1b9c0Schristos 			goto freesig;
3254e2b1b9c0Schristos 		} else if (keyset.trust < dns_trust_secure) {
3255e2b1b9c0Schristos 			/* XXXBEW Should call a validator here */
3256e2b1b9c0Schristos 			result = DNS_R_KEYUNAUTHORIZED;
3257e2b1b9c0Schristos 			goto freesig;
3258e2b1b9c0Schristos 		}
3259e2b1b9c0Schristos 		result = dns_rdataset_first(&keyset);
3260e2b1b9c0Schristos 		INSIST(result == ISC_R_SUCCESS);
32619742fdb4Schristos 		for (; result == ISC_R_SUCCESS;
3262*4ac1c27eSchristos 		     result = dns_rdataset_next(&keyset))
3263*4ac1c27eSchristos 		{
3264e2b1b9c0Schristos 			dst_key_t *key = NULL;
3265e2b1b9c0Schristos 
3266e2b1b9c0Schristos 			dns_rdata_reset(&rdata);
3267e2b1b9c0Schristos 			dns_rdataset_current(&keyset, &rdata);
3268e2b1b9c0Schristos 			isc_buffer_init(&b, rdata.data, rdata.length);
3269e2b1b9c0Schristos 			isc_buffer_add(&b, rdata.length);
3270e2b1b9c0Schristos 
32719742fdb4Schristos 			result = dst_key_fromdns(&sig.signer, rdata.rdclass, &b,
32729742fdb4Schristos 						 view->mctx, &key);
32739742fdb4Schristos 			if (result != ISC_R_SUCCESS) {
3274e2b1b9c0Schristos 				continue;
32759742fdb4Schristos 			}
3276e2b1b9c0Schristos 			if (dst_key_alg(key) != sig.algorithm ||
3277e2b1b9c0Schristos 			    dst_key_id(key) != sig.keyid ||
3278e2b1b9c0Schristos 			    !(dst_key_proto(key) == DNS_KEYPROTO_DNSSEC ||
3279e2b1b9c0Schristos 			      dst_key_proto(key) == DNS_KEYPROTO_ANY))
3280e2b1b9c0Schristos 			{
3281e2b1b9c0Schristos 				dst_key_free(&key);
3282e2b1b9c0Schristos 				continue;
3283e2b1b9c0Schristos 			}
3284e2b1b9c0Schristos 			result = dns_dnssec_verifymessage(&msgb, msg, key);
3285e2b1b9c0Schristos 			dst_key_free(&key);
32869742fdb4Schristos 			if (result == ISC_R_SUCCESS) {
3287e2b1b9c0Schristos 				break;
3288e2b1b9c0Schristos 			}
32899742fdb4Schristos 		}
32909742fdb4Schristos 		if (result == ISC_R_NOMORE) {
3291e2b1b9c0Schristos 			result = DNS_R_KEYUNAUTHORIZED;
32929742fdb4Schristos 		}
3293e2b1b9c0Schristos 
3294e2b1b9c0Schristos 	freesig:
32959742fdb4Schristos 		if (dns_rdataset_isassociated(&keyset)) {
3296e2b1b9c0Schristos 			dns_rdataset_disassociate(&keyset);
32979742fdb4Schristos 		}
3298e2b1b9c0Schristos 		dns_rdata_freestruct(&sig);
3299e2b1b9c0Schristos 		return (result);
3300e2b1b9c0Schristos 	}
3301e2b1b9c0Schristos }
3302e2b1b9c0Schristos 
3303e2b1b9c0Schristos #define INDENT(sp)                                                           \
3304e2b1b9c0Schristos 	do {                                                                 \
3305e2b1b9c0Schristos 		unsigned int __i;                                            \
3306e2b1b9c0Schristos 		dns_masterstyle_flags_t __flags = dns_master_styleflags(sp); \
3307e2b1b9c0Schristos 		if ((__flags & DNS_STYLEFLAG_INDENT) == 0ULL &&              \
3308e2b1b9c0Schristos 		    (__flags & DNS_STYLEFLAG_YAML) == 0ULL)                  \
3309e2b1b9c0Schristos 			break;                                               \
33109742fdb4Schristos 		for (__i = 0; __i < msg->indent.count; __i++) {              \
33119742fdb4Schristos 			ADD_STRING(target, msg->indent.string);              \
3312e2b1b9c0Schristos 		}                                                            \
3313245a9365Srillig 	} while (0)
3314e2b1b9c0Schristos 
3315e2b1b9c0Schristos isc_result_t
dns_message_sectiontotext(dns_message_t * msg,dns_section_t section,const dns_master_style_t * style,dns_messagetextflag_t flags,isc_buffer_t * target)3316e2b1b9c0Schristos dns_message_sectiontotext(dns_message_t *msg, dns_section_t section,
3317e2b1b9c0Schristos 			  const dns_master_style_t *style,
33189742fdb4Schristos 			  dns_messagetextflag_t flags, isc_buffer_t *target) {
3319e2b1b9c0Schristos 	dns_name_t *name, empty_name;
3320e2b1b9c0Schristos 	dns_rdataset_t *rdataset;
3321e2b1b9c0Schristos 	isc_result_t result = ISC_R_SUCCESS;
3322f2e20987Schristos 	bool seensoa = false;
33239742fdb4Schristos 	size_t saved_count;
3324e2b1b9c0Schristos 	dns_masterstyle_flags_t sflags;
3325e2b1b9c0Schristos 
3326e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
3327e2b1b9c0Schristos 	REQUIRE(target != NULL);
3328e2b1b9c0Schristos 	REQUIRE(VALID_SECTION(section));
3329e2b1b9c0Schristos 
33309742fdb4Schristos 	saved_count = msg->indent.count;
3331e2b1b9c0Schristos 
33329742fdb4Schristos 	if (ISC_LIST_EMPTY(msg->sections[section])) {
33339742fdb4Schristos 		goto cleanup;
33349742fdb4Schristos 	}
33359742fdb4Schristos 
33369742fdb4Schristos 	sflags = dns_master_styleflags(style);
3337e2b1b9c0Schristos 
3338e2b1b9c0Schristos 	INDENT(style);
3339e2b1b9c0Schristos 	if ((sflags & DNS_STYLEFLAG_YAML) != 0) {
3340e2b1b9c0Schristos 		if (msg->opcode != dns_opcode_update) {
3341e2b1b9c0Schristos 			ADD_STRING(target, sectiontext[section]);
3342e2b1b9c0Schristos 		} else {
3343e2b1b9c0Schristos 			ADD_STRING(target, updsectiontext[section]);
3344e2b1b9c0Schristos 		}
3345e2b1b9c0Schristos 		ADD_STRING(target, "_SECTION:\n");
3346e2b1b9c0Schristos 	} else if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) {
3347e2b1b9c0Schristos 		ADD_STRING(target, ";; ");
3348e2b1b9c0Schristos 		if (msg->opcode != dns_opcode_update) {
3349e2b1b9c0Schristos 			ADD_STRING(target, sectiontext[section]);
3350e2b1b9c0Schristos 		} else {
3351e2b1b9c0Schristos 			ADD_STRING(target, updsectiontext[section]);
3352e2b1b9c0Schristos 		}
3353e2b1b9c0Schristos 		ADD_STRING(target, " SECTION:\n");
3354e2b1b9c0Schristos 	}
3355e2b1b9c0Schristos 
3356e2b1b9c0Schristos 	dns_name_init(&empty_name, NULL);
3357e2b1b9c0Schristos 	result = dns_message_firstname(msg, section);
3358e2b1b9c0Schristos 	if (result != ISC_R_SUCCESS) {
3359e2b1b9c0Schristos 		goto cleanup;
3360e2b1b9c0Schristos 	}
3361e2b1b9c0Schristos 	if ((sflags & DNS_STYLEFLAG_YAML) != 0) {
33629742fdb4Schristos 		msg->indent.count++;
3363e2b1b9c0Schristos 	}
3364e2b1b9c0Schristos 	do {
3365e2b1b9c0Schristos 		name = NULL;
3366e2b1b9c0Schristos 		dns_message_currentname(msg, section, &name);
33679742fdb4Schristos 		for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL;
33689742fdb4Schristos 		     rdataset = ISC_LIST_NEXT(rdataset, link))
33699742fdb4Schristos 		{
3370e2b1b9c0Schristos 			if (section == DNS_SECTION_ANSWER &&
3371*4ac1c27eSchristos 			    rdataset->type == dns_rdatatype_soa)
3372*4ac1c27eSchristos 			{
3373e2b1b9c0Schristos 				if ((flags & DNS_MESSAGETEXTFLAG_OMITSOA) != 0)
33749742fdb4Schristos 				{
3375e2b1b9c0Schristos 					continue;
33769742fdb4Schristos 				}
3377e2b1b9c0Schristos 				if (seensoa &&
3378*4ac1c27eSchristos 				    (flags & DNS_MESSAGETEXTFLAG_ONESOA) != 0)
3379*4ac1c27eSchristos 				{
3380e2b1b9c0Schristos 					continue;
33819742fdb4Schristos 				}
3382f2e20987Schristos 				seensoa = true;
3383e2b1b9c0Schristos 			}
3384e2b1b9c0Schristos 			if (section == DNS_SECTION_QUESTION) {
3385e2b1b9c0Schristos 				INDENT(style);
3386e2b1b9c0Schristos 				if ((sflags & DNS_STYLEFLAG_YAML) != 0) {
3387e2b1b9c0Schristos 					ADD_STRING(target, "- ");
3388e2b1b9c0Schristos 				} else {
3389e2b1b9c0Schristos 					ADD_STRING(target, ";");
3390e2b1b9c0Schristos 				}
33919742fdb4Schristos 				result = dns_master_questiontotext(
33929742fdb4Schristos 					name, rdataset, style, target);
3393e2b1b9c0Schristos 			} else {
33949742fdb4Schristos 				result = dns_master_rdatasettotext(
33959742fdb4Schristos 					name, rdataset, style, &msg->indent,
3396e2b1b9c0Schristos 					target);
3397e2b1b9c0Schristos 			}
33989742fdb4Schristos 			if (result != ISC_R_SUCCESS) {
3399e2b1b9c0Schristos 				goto cleanup;
3400e2b1b9c0Schristos 			}
34019742fdb4Schristos 		}
3402e2b1b9c0Schristos 		result = dns_message_nextname(msg, section);
3403e2b1b9c0Schristos 	} while (result == ISC_R_SUCCESS);
3404e2b1b9c0Schristos 	if ((sflags & DNS_STYLEFLAG_YAML) != 0) {
34059742fdb4Schristos 		msg->indent.count--;
3406e2b1b9c0Schristos 	}
3407e2b1b9c0Schristos 	if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0 &&
3408e2b1b9c0Schristos 	    (flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0 &&
3409e2b1b9c0Schristos 	    (sflags & DNS_STYLEFLAG_YAML) == 0)
3410e2b1b9c0Schristos 	{
3411e2b1b9c0Schristos 		INDENT(style);
3412e2b1b9c0Schristos 		ADD_STRING(target, "\n");
3413e2b1b9c0Schristos 	}
34149742fdb4Schristos 	if (result == ISC_R_NOMORE) {
3415e2b1b9c0Schristos 		result = ISC_R_SUCCESS;
34169742fdb4Schristos 	}
3417e2b1b9c0Schristos 
3418e2b1b9c0Schristos cleanup:
34199742fdb4Schristos 	msg->indent.count = saved_count;
3420e2b1b9c0Schristos 	return (result);
3421e2b1b9c0Schristos }
3422e2b1b9c0Schristos 
3423e2b1b9c0Schristos static isc_result_t
render_ecs(isc_buffer_t * ecsbuf,isc_buffer_t * target)3424e2b1b9c0Schristos render_ecs(isc_buffer_t *ecsbuf, isc_buffer_t *target) {
3425e2b1b9c0Schristos 	int i;
3426e2b1b9c0Schristos 	char addr[16], addr_text[64];
3427f2e20987Schristos 	uint16_t family;
3428f2e20987Schristos 	uint8_t addrlen, addrbytes, scopelen;
3429e2b1b9c0Schristos 	isc_result_t result;
3430e2b1b9c0Schristos 
3431e2b1b9c0Schristos 	/*
3432e2b1b9c0Schristos 	 * Note: This routine needs to handle malformed ECS options.
3433e2b1b9c0Schristos 	 */
3434e2b1b9c0Schristos 
34359742fdb4Schristos 	if (isc_buffer_remaininglength(ecsbuf) < 4) {
3436e2b1b9c0Schristos 		return (DNS_R_OPTERR);
34379742fdb4Schristos 	}
3438e2b1b9c0Schristos 	family = isc_buffer_getuint16(ecsbuf);
3439e2b1b9c0Schristos 	addrlen = isc_buffer_getuint8(ecsbuf);
3440e2b1b9c0Schristos 	scopelen = isc_buffer_getuint8(ecsbuf);
3441e2b1b9c0Schristos 
3442e2b1b9c0Schristos 	addrbytes = (addrlen + 7) / 8;
34439742fdb4Schristos 	if (isc_buffer_remaininglength(ecsbuf) < addrbytes) {
3444e2b1b9c0Schristos 		return (DNS_R_OPTERR);
34459742fdb4Schristos 	}
3446e2b1b9c0Schristos 
34479742fdb4Schristos 	if (addrbytes > sizeof(addr)) {
3448e2b1b9c0Schristos 		return (DNS_R_OPTERR);
34499742fdb4Schristos 	}
3450e2b1b9c0Schristos 
3451e2b1b9c0Schristos 	memset(addr, 0, sizeof(addr));
34529742fdb4Schristos 	for (i = 0; i < addrbytes; i++) {
3453e2b1b9c0Schristos 		addr[i] = isc_buffer_getuint8(ecsbuf);
34549742fdb4Schristos 	}
3455e2b1b9c0Schristos 
3456e2b1b9c0Schristos 	switch (family) {
3457e2b1b9c0Schristos 	case 0:
34589742fdb4Schristos 		if (addrlen != 0U || scopelen != 0U) {
3459e2b1b9c0Schristos 			return (DNS_R_OPTERR);
34609742fdb4Schristos 		}
3461e2b1b9c0Schristos 		strlcpy(addr_text, "0", sizeof(addr_text));
3462e2b1b9c0Schristos 		break;
3463e2b1b9c0Schristos 	case 1:
34649742fdb4Schristos 		if (addrlen > 32 || scopelen > 32) {
3465e2b1b9c0Schristos 			return (DNS_R_OPTERR);
34669742fdb4Schristos 		}
3467e2b1b9c0Schristos 		inet_ntop(AF_INET, addr, addr_text, sizeof(addr_text));
3468e2b1b9c0Schristos 		break;
3469e2b1b9c0Schristos 	case 2:
34709742fdb4Schristos 		if (addrlen > 128 || scopelen > 128) {
3471e2b1b9c0Schristos 			return (DNS_R_OPTERR);
34729742fdb4Schristos 		}
3473e2b1b9c0Schristos 		inet_ntop(AF_INET6, addr, addr_text, sizeof(addr_text));
3474e2b1b9c0Schristos 		break;
3475e2b1b9c0Schristos 	default:
3476e2b1b9c0Schristos 		return (DNS_R_OPTERR);
3477e2b1b9c0Schristos 	}
3478e2b1b9c0Schristos 
34799742fdb4Schristos 	ADD_STRING(target, " ");
3480e2b1b9c0Schristos 	ADD_STRING(target, addr_text);
3481e2b1b9c0Schristos 	snprintf(addr_text, sizeof(addr_text), "/%d/%d", addrlen, scopelen);
3482e2b1b9c0Schristos 	ADD_STRING(target, addr_text);
3483e2b1b9c0Schristos 
3484e2b1b9c0Schristos 	result = ISC_R_SUCCESS;
3485e2b1b9c0Schristos 
3486e2b1b9c0Schristos cleanup:
3487e2b1b9c0Schristos 	return (result);
3488e2b1b9c0Schristos }
3489e2b1b9c0Schristos 
349055ba2b39Schristos static isc_result_t
render_llq(isc_buffer_t * optbuf,isc_buffer_t * target)349155ba2b39Schristos render_llq(isc_buffer_t *optbuf, isc_buffer_t *target) {
349255ba2b39Schristos 	char buf[sizeof("18446744073709551615")]; /* 2^64-1 */
349355ba2b39Schristos 	isc_result_t result = ISC_R_SUCCESS;
349455ba2b39Schristos 	uint32_t u;
349555ba2b39Schristos 	uint64_t q;
349655ba2b39Schristos 
349755ba2b39Schristos 	u = isc_buffer_getuint16(optbuf);
349855ba2b39Schristos 	ADD_STRING(target, " Version: ");
349955ba2b39Schristos 	snprintf(buf, sizeof(buf), "%u", u);
350055ba2b39Schristos 	ADD_STRING(target, buf);
350155ba2b39Schristos 
350255ba2b39Schristos 	u = isc_buffer_getuint16(optbuf);
350355ba2b39Schristos 	ADD_STRING(target, ", Opcode: ");
350455ba2b39Schristos 	snprintf(buf, sizeof(buf), "%u", u);
350555ba2b39Schristos 	ADD_STRING(target, buf);
350655ba2b39Schristos 
350755ba2b39Schristos 	u = isc_buffer_getuint16(optbuf);
350855ba2b39Schristos 	ADD_STRING(target, ", Error: ");
350955ba2b39Schristos 	snprintf(buf, sizeof(buf), "%u", u);
351055ba2b39Schristos 	ADD_STRING(target, buf);
351155ba2b39Schristos 
351255ba2b39Schristos 	q = isc_buffer_getuint32(optbuf);
351355ba2b39Schristos 	q <<= 32;
351455ba2b39Schristos 	q |= isc_buffer_getuint32(optbuf);
351555ba2b39Schristos 	ADD_STRING(target, ", Identifier: ");
351655ba2b39Schristos 	snprintf(buf, sizeof(buf), "%" PRIu64, q);
351755ba2b39Schristos 	ADD_STRING(target, buf);
351855ba2b39Schristos 
351955ba2b39Schristos 	u = isc_buffer_getuint32(optbuf);
352055ba2b39Schristos 	ADD_STRING(target, ", Lifetime: ");
352155ba2b39Schristos 	snprintf(buf, sizeof(buf), "%u", u);
352255ba2b39Schristos 	ADD_STRING(target, buf);
352355ba2b39Schristos cleanup:
352455ba2b39Schristos 	return (result);
352555ba2b39Schristos }
3526e2b1b9c0Schristos 
3527e2b1b9c0Schristos static isc_result_t
dns_message_pseudosectiontoyaml(dns_message_t * msg,dns_pseudosection_t section,const dns_master_style_t * style,dns_messagetextflag_t flags,isc_buffer_t * target)35289742fdb4Schristos dns_message_pseudosectiontoyaml(dns_message_t *msg, dns_pseudosection_t section,
3529e2b1b9c0Schristos 				const dns_master_style_t *style,
3530e2b1b9c0Schristos 				dns_messagetextflag_t flags,
35319742fdb4Schristos 				isc_buffer_t *target) {
3532e2b1b9c0Schristos 	dns_rdataset_t *ps = NULL;
3533e2b1b9c0Schristos 	const dns_name_t *name = NULL;
3534e2b1b9c0Schristos 	isc_result_t result = ISC_R_SUCCESS;
3535e2b1b9c0Schristos 	char buf[sizeof("1234567890")];
3536f2e20987Schristos 	uint32_t mbz;
3537e2b1b9c0Schristos 	dns_rdata_t rdata;
3538e2b1b9c0Schristos 	isc_buffer_t optbuf;
3539f2e20987Schristos 	uint16_t optcode, optlen;
35409742fdb4Schristos 	size_t saved_count;
3541e2b1b9c0Schristos 	unsigned char *optdata;
3542803e9293Schristos 	unsigned int indent;
3543e2b1b9c0Schristos 
3544e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
3545e2b1b9c0Schristos 	REQUIRE(target != NULL);
3546e2b1b9c0Schristos 	REQUIRE(VALID_PSEUDOSECTION(section));
3547e2b1b9c0Schristos 
35489742fdb4Schristos 	saved_count = msg->indent.count;
35499742fdb4Schristos 
3550e2b1b9c0Schristos 	switch (section) {
3551e2b1b9c0Schristos 	case DNS_PSEUDOSECTION_OPT:
3552e2b1b9c0Schristos 		ps = dns_message_getopt(msg);
3553e2b1b9c0Schristos 		if (ps == NULL) {
3554e2b1b9c0Schristos 			goto cleanup;
3555e2b1b9c0Schristos 		}
3556e2b1b9c0Schristos 
3557e2b1b9c0Schristos 		INDENT(style);
3558e2b1b9c0Schristos 		ADD_STRING(target, "OPT_PSEUDOSECTION:\n");
35599742fdb4Schristos 		msg->indent.count++;
3560e2b1b9c0Schristos 
3561e2b1b9c0Schristos 		INDENT(style);
3562e2b1b9c0Schristos 		ADD_STRING(target, "EDNS:\n");
3563803e9293Schristos 		indent = ++msg->indent.count;
3564e2b1b9c0Schristos 
3565e2b1b9c0Schristos 		INDENT(style);
3566e2b1b9c0Schristos 		ADD_STRING(target, "version: ");
3567e2b1b9c0Schristos 		snprintf(buf, sizeof(buf), "%u",
3568e2b1b9c0Schristos 			 (unsigned int)((ps->ttl & 0x00ff0000) >> 16));
3569e2b1b9c0Schristos 		ADD_STRING(target, buf);
3570e2b1b9c0Schristos 		ADD_STRING(target, "\n");
3571e2b1b9c0Schristos 		INDENT(style);
3572e2b1b9c0Schristos 		ADD_STRING(target, "flags:");
35739742fdb4Schristos 		if ((ps->ttl & DNS_MESSAGEEXTFLAG_DO) != 0) {
3574e2b1b9c0Schristos 			ADD_STRING(target, " do");
35759742fdb4Schristos 		}
3576e2b1b9c0Schristos 		ADD_STRING(target, "\n");
3577e2b1b9c0Schristos 		mbz = ps->ttl & 0xffff;
3578e2b1b9c0Schristos 		mbz &= ~DNS_MESSAGEEXTFLAG_DO; /* Known Flags. */
3579e2b1b9c0Schristos 		if (mbz != 0) {
3580e2b1b9c0Schristos 			INDENT(style);
3581e2b1b9c0Schristos 			ADD_STRING(target, "MBZ: ");
3582e2b1b9c0Schristos 			snprintf(buf, sizeof(buf), "0x%.4x", mbz);
3583e2b1b9c0Schristos 			ADD_STRING(target, buf);
3584e2b1b9c0Schristos 			ADD_STRING(target, "\n");
3585e2b1b9c0Schristos 		}
3586e2b1b9c0Schristos 		INDENT(style);
3587e2b1b9c0Schristos 		ADD_STRING(target, "udp: ");
3588e2b1b9c0Schristos 		snprintf(buf, sizeof(buf), "%u\n", (unsigned int)ps->rdclass);
3589e2b1b9c0Schristos 		ADD_STRING(target, buf);
3590e2b1b9c0Schristos 		result = dns_rdataset_first(ps);
3591e2b1b9c0Schristos 		if (result != ISC_R_SUCCESS) {
3592e2b1b9c0Schristos 			result = ISC_R_SUCCESS;
3593e2b1b9c0Schristos 			goto cleanup;
3594e2b1b9c0Schristos 		}
3595e2b1b9c0Schristos 
3596e2b1b9c0Schristos 		/*
3597e2b1b9c0Schristos 		 * Print EDNS info, if any.
3598e2b1b9c0Schristos 		 *
3599e2b1b9c0Schristos 		 * WARNING: The option contents may be malformed as
3600803e9293Schristos 		 * dig +ednsopt=value:<content> does not perform validity
3601e2b1b9c0Schristos 		 * checking.
3602e2b1b9c0Schristos 		 */
3603e2b1b9c0Schristos 		dns_rdata_init(&rdata);
3604e2b1b9c0Schristos 		dns_rdataset_current(ps, &rdata);
3605e2b1b9c0Schristos 
3606e2b1b9c0Schristos 		isc_buffer_init(&optbuf, rdata.data, rdata.length);
3607e2b1b9c0Schristos 		isc_buffer_add(&optbuf, rdata.length);
3608e2b1b9c0Schristos 		while (isc_buffer_remaininglength(&optbuf) != 0) {
3609803e9293Schristos 			bool extra_text = false;
3610803e9293Schristos 			msg->indent.count = indent;
3611e2b1b9c0Schristos 			INSIST(isc_buffer_remaininglength(&optbuf) >= 4U);
3612e2b1b9c0Schristos 			optcode = isc_buffer_getuint16(&optbuf);
3613e2b1b9c0Schristos 			optlen = isc_buffer_getuint16(&optbuf);
3614e2b1b9c0Schristos 			INSIST(isc_buffer_remaininglength(&optbuf) >= optlen);
3615e2b1b9c0Schristos 
361655ba2b39Schristos 			if (optcode == DNS_OPT_LLQ) {
361755ba2b39Schristos 				INDENT(style);
361855ba2b39Schristos 				ADD_STRING(target, "LLQ:");
36199742fdb4Schristos 				if (optlen == 18U) {
362055ba2b39Schristos 					result = render_llq(&optbuf, target);
362155ba2b39Schristos 					if (result != ISC_R_SUCCESS) {
362255ba2b39Schristos 						goto cleanup;
362355ba2b39Schristos 					}
362455ba2b39Schristos 					ADD_STRING(target, "\n");
362555ba2b39Schristos 					continue;
362655ba2b39Schristos 				}
362755ba2b39Schristos 			} else if (optcode == DNS_OPT_NSID) {
3628e2b1b9c0Schristos 				INDENT(style);
36299742fdb4Schristos 				ADD_STRING(target, "NSID:");
3630e2b1b9c0Schristos 			} else if (optcode == DNS_OPT_COOKIE) {
3631e2b1b9c0Schristos 				INDENT(style);
36329742fdb4Schristos 				ADD_STRING(target, "COOKIE:");
3633e2b1b9c0Schristos 			} else if (optcode == DNS_OPT_CLIENT_SUBNET) {
3634e2b1b9c0Schristos 				isc_buffer_t ecsbuf;
3635e2b1b9c0Schristos 				INDENT(style);
36369742fdb4Schristos 				ADD_STRING(target, "CLIENT-SUBNET:");
3637e2b1b9c0Schristos 				isc_buffer_init(&ecsbuf,
3638e2b1b9c0Schristos 						isc_buffer_current(&optbuf),
3639e2b1b9c0Schristos 						optlen);
3640e2b1b9c0Schristos 				isc_buffer_add(&ecsbuf, optlen);
3641e2b1b9c0Schristos 				result = render_ecs(&ecsbuf, target);
36429742fdb4Schristos 				if (result == ISC_R_NOSPACE) {
3643e2b1b9c0Schristos 					goto cleanup;
36449742fdb4Schristos 				}
3645e2b1b9c0Schristos 				if (result == ISC_R_SUCCESS) {
3646e2b1b9c0Schristos 					isc_buffer_forward(&optbuf, optlen);
3647e2b1b9c0Schristos 					ADD_STRING(target, "\n");
3648e2b1b9c0Schristos 					continue;
3649e2b1b9c0Schristos 				}
3650e2b1b9c0Schristos 				ADD_STRING(target, "\n");
3651e2b1b9c0Schristos 			} else if (optcode == DNS_OPT_EXPIRE) {
36529742fdb4Schristos 				INDENT(style);
36539742fdb4Schristos 				ADD_STRING(target, "EXPIRE:");
3654e2b1b9c0Schristos 				if (optlen == 4) {
3655f2e20987Schristos 					uint32_t secs;
3656e2b1b9c0Schristos 					secs = isc_buffer_getuint32(&optbuf);
3657e2b1b9c0Schristos 					snprintf(buf, sizeof(buf), " %u", secs);
3658e2b1b9c0Schristos 					ADD_STRING(target, buf);
3659e2b1b9c0Schristos 					ADD_STRING(target, " (");
36609742fdb4Schristos 					result = dns_ttl_totext(secs, true,
36619742fdb4Schristos 								true, target);
36629742fdb4Schristos 					if (result != ISC_R_SUCCESS) {
3663e2b1b9c0Schristos 						goto cleanup;
36649742fdb4Schristos 					}
3665e2b1b9c0Schristos 					ADD_STRING(target, ")\n");
3666e2b1b9c0Schristos 					continue;
3667e2b1b9c0Schristos 				}
36689742fdb4Schristos 			} else if (optcode == DNS_OPT_TCP_KEEPALIVE) {
36699742fdb4Schristos 				if (optlen == 2) {
36709742fdb4Schristos 					unsigned int dsecs;
36719742fdb4Schristos 					dsecs = isc_buffer_getuint16(&optbuf);
3672e2b1b9c0Schristos 					INDENT(style);
36739742fdb4Schristos 					ADD_STRING(target, "TCP-KEEPALIVE: ");
36749742fdb4Schristos 					snprintf(buf, sizeof(buf), "%u.%u",
36759742fdb4Schristos 						 dsecs / 10U, dsecs % 10U);
36769742fdb4Schristos 					ADD_STRING(target, buf);
36779742fdb4Schristos 					ADD_STRING(target, " secs\n");
36789742fdb4Schristos 					continue;
36799742fdb4Schristos 				}
36809742fdb4Schristos 				INDENT(style);
36819742fdb4Schristos 				ADD_STRING(target, "TCP-KEEPALIVE:");
3682e2b1b9c0Schristos 			} else if (optcode == DNS_OPT_PAD) {
3683e2b1b9c0Schristos 				INDENT(style);
36849742fdb4Schristos 				ADD_STRING(target, "PAD:");
3685e2b1b9c0Schristos 			} else if (optcode == DNS_OPT_KEY_TAG) {
3686e2b1b9c0Schristos 				INDENT(style);
36879742fdb4Schristos 				ADD_STRING(target, "KEY-TAG:");
3688e2b1b9c0Schristos 				if (optlen > 0U && (optlen % 2U) == 0U) {
36899742fdb4Schristos 					const char *sep = "";
3690f2e20987Schristos 					uint16_t id;
3691e2b1b9c0Schristos 					while (optlen > 0U) {
36929742fdb4Schristos 						id = isc_buffer_getuint16(
36939742fdb4Schristos 							&optbuf);
36949742fdb4Schristos 						snprintf(buf, sizeof(buf),
36959742fdb4Schristos 							 "%s %u", sep, id);
3696e2b1b9c0Schristos 						ADD_STRING(target, buf);
3697e2b1b9c0Schristos 						sep = ",";
3698e2b1b9c0Schristos 						optlen -= 2;
3699e2b1b9c0Schristos 					}
3700e2b1b9c0Schristos 					ADD_STRING(target, "\n");
3701e2b1b9c0Schristos 					continue;
3702e2b1b9c0Schristos 				}
3703803e9293Schristos 			} else if (optcode == DNS_OPT_EDE) {
3704803e9293Schristos 				INDENT(style);
3705803e9293Schristos 				ADD_STRING(target, "EDE:");
3706803e9293Schristos 				if (optlen >= 2U) {
3707803e9293Schristos 					uint16_t ede;
3708803e9293Schristos 					ADD_STRING(target, "\n");
3709803e9293Schristos 					msg->indent.count++;
3710803e9293Schristos 					INDENT(style);
3711803e9293Schristos 					ADD_STRING(target, "INFO-CODE:");
3712803e9293Schristos 					ede = isc_buffer_getuint16(&optbuf);
3713803e9293Schristos 					snprintf(buf, sizeof(buf), " %u", ede);
3714803e9293Schristos 					ADD_STRING(target, buf);
3715803e9293Schristos 					if (ede < ARRAY_SIZE(edetext)) {
3716803e9293Schristos 						ADD_STRING(target, " (");
3717803e9293Schristos 						ADD_STRING(target,
3718803e9293Schristos 							   edetext[ede]);
3719803e9293Schristos 						ADD_STRING(target, ")");
3720803e9293Schristos 					}
3721803e9293Schristos 					ADD_STRING(target, "\n");
3722803e9293Schristos 					optlen -= 2;
3723803e9293Schristos 					if (optlen != 0) {
3724803e9293Schristos 						INDENT(style);
3725803e9293Schristos 						ADD_STRING(target,
3726803e9293Schristos 							   "EXTRA-TEXT:");
3727803e9293Schristos 						extra_text = true;
3728803e9293Schristos 					}
3729803e9293Schristos 				}
37309ad14dd7Schristos 			} else if (optcode == DNS_OPT_CLIENT_TAG) {
37319ad14dd7Schristos 				uint16_t id;
37329ad14dd7Schristos 				INDENT(style);
37339742fdb4Schristos 				ADD_STRING(target, "CLIENT-TAG:");
37349ad14dd7Schristos 				if (optlen == 2U) {
37359ad14dd7Schristos 					id = isc_buffer_getuint16(&optbuf);
37369742fdb4Schristos 					snprintf(buf, sizeof(buf), " %u\n", id);
37379ad14dd7Schristos 					ADD_STRING(target, buf);
37389ad14dd7Schristos 					optlen -= 2;
37399ad14dd7Schristos 					POST(optlen);
37409ad14dd7Schristos 					continue;
37419ad14dd7Schristos 				}
37429ad14dd7Schristos 			} else if (optcode == DNS_OPT_SERVER_TAG) {
37439ad14dd7Schristos 				uint16_t id;
37449ad14dd7Schristos 				INDENT(style);
37459742fdb4Schristos 				ADD_STRING(target, "SERVER-TAG:");
37469ad14dd7Schristos 				if (optlen == 2U) {
37479ad14dd7Schristos 					id = isc_buffer_getuint16(&optbuf);
37489742fdb4Schristos 					snprintf(buf, sizeof(buf), " %u\n", id);
37499ad14dd7Schristos 					ADD_STRING(target, buf);
37509ad14dd7Schristos 					optlen -= 2;
37519ad14dd7Schristos 					POST(optlen);
37529ad14dd7Schristos 					continue;
37539ad14dd7Schristos 				}
3754e2b1b9c0Schristos 			} else {
3755e2b1b9c0Schristos 				INDENT(style);
37569742fdb4Schristos 				ADD_STRING(target, "OPT=");
37579742fdb4Schristos 				snprintf(buf, sizeof(buf), "%u:", optcode);
3758e2b1b9c0Schristos 				ADD_STRING(target, buf);
3759e2b1b9c0Schristos 			}
3760e2b1b9c0Schristos 
3761e2b1b9c0Schristos 			if (optlen != 0) {
3762e2b1b9c0Schristos 				int i;
3763803e9293Schristos 				bool utf8ok = false;
37649742fdb4Schristos 
37659742fdb4Schristos 				ADD_STRING(target, " ");
3766e2b1b9c0Schristos 
3767e2b1b9c0Schristos 				optdata = isc_buffer_current(&optbuf);
3768803e9293Schristos 				if (extra_text) {
3769803e9293Schristos 					utf8ok = isc_utf8_valid(optdata,
3770803e9293Schristos 								optlen);
3771803e9293Schristos 				}
3772803e9293Schristos 				if (!utf8ok) {
3773e2b1b9c0Schristos 					for (i = 0; i < optlen; i++) {
3774e2b1b9c0Schristos 						const char *sep;
3775e2b1b9c0Schristos 						switch (optcode) {
3776e2b1b9c0Schristos 						case DNS_OPT_COOKIE:
3777e2b1b9c0Schristos 							sep = "";
3778e2b1b9c0Schristos 							break;
3779e2b1b9c0Schristos 						default:
3780e2b1b9c0Schristos 							sep = " ";
3781e2b1b9c0Schristos 							break;
3782e2b1b9c0Schristos 						}
3783803e9293Schristos 						snprintf(buf, sizeof(buf),
3784803e9293Schristos 							 "%02x%s", optdata[i],
3785803e9293Schristos 							 sep);
3786e2b1b9c0Schristos 						ADD_STRING(target, buf);
3787e2b1b9c0Schristos 					}
3788803e9293Schristos 				}
3789e2b1b9c0Schristos 
3790e2b1b9c0Schristos 				isc_buffer_forward(&optbuf, optlen);
3791e2b1b9c0Schristos 
3792e2b1b9c0Schristos 				if (optcode == DNS_OPT_COOKIE) {
3793e2b1b9c0Schristos 					/*
3794e2b1b9c0Schristos 					 * Valid server cookie?
3795e2b1b9c0Schristos 					 */
37969742fdb4Schristos 					if (msg->cc_ok && optlen >= 16) {
3797e2b1b9c0Schristos 						ADD_STRING(target, " (good)");
37989742fdb4Schristos 					}
3799e2b1b9c0Schristos 					/*
3800e2b1b9c0Schristos 					 * Server cookie is not valid but
3801e2b1b9c0Schristos 					 * we had our cookie echoed back.
3802e2b1b9c0Schristos 					 */
38039742fdb4Schristos 					if (msg->cc_ok && optlen < 16) {
3804e2b1b9c0Schristos 						ADD_STRING(target, " (echoed)");
38059742fdb4Schristos 					}
3806e2b1b9c0Schristos 					/*
3807e2b1b9c0Schristos 					 * We didn't get our cookie echoed
3808e2b1b9c0Schristos 					 * back.
3809e2b1b9c0Schristos 					 */
38109742fdb4Schristos 					if (msg->cc_bad) {
3811e2b1b9c0Schristos 						ADD_STRING(target, " (bad)");
38129742fdb4Schristos 					}
3813e2b1b9c0Schristos 					ADD_STRING(target, "\n");
3814e2b1b9c0Schristos 					continue;
3815e2b1b9c0Schristos 				}
3816e2b1b9c0Schristos 
3817e2b1b9c0Schristos 				if (optcode == DNS_OPT_CLIENT_SUBNET) {
3818e2b1b9c0Schristos 					ADD_STRING(target, "\n");
3819e2b1b9c0Schristos 					continue;
3820e2b1b9c0Schristos 				}
3821e2b1b9c0Schristos 
3822e2b1b9c0Schristos 				/*
3823e2b1b9c0Schristos 				 * For non-COOKIE options, add a printable
3824e2b1b9c0Schristos 				 * version
3825e2b1b9c0Schristos 				 */
3826803e9293Schristos 				if (!extra_text) {
3827e2b1b9c0Schristos 					ADD_STRING(target, "(\"");
3828803e9293Schristos 				} else {
3829803e9293Schristos 					ADD_STRING(target, "\"");
3830803e9293Schristos 				}
3831e2b1b9c0Schristos 				if (isc_buffer_availablelength(target) < optlen)
3832e2b1b9c0Schristos 				{
3833e2b1b9c0Schristos 					result = ISC_R_NOSPACE;
3834e2b1b9c0Schristos 					goto cleanup;
3835e2b1b9c0Schristos 				}
3836e2b1b9c0Schristos 				for (i = 0; i < optlen; i++) {
3837803e9293Schristos 					if (isprint(optdata[i]) ||
3838*4ac1c27eSchristos 					    (utf8ok && optdata[i] > 127))
3839*4ac1c27eSchristos 					{
38409742fdb4Schristos 						isc_buffer_putmem(
38419742fdb4Schristos 							target, &optdata[i], 1);
38429742fdb4Schristos 					} else {
3843e2b1b9c0Schristos 						isc_buffer_putstr(target, ".");
3844e2b1b9c0Schristos 					}
38459742fdb4Schristos 				}
3846803e9293Schristos 				if (!extra_text) {
3847e2b1b9c0Schristos 					ADD_STRING(target, "\")");
3848803e9293Schristos 				} else {
3849803e9293Schristos 					ADD_STRING(target, "\"");
3850803e9293Schristos 				}
3851e2b1b9c0Schristos 			}
3852e2b1b9c0Schristos 			ADD_STRING(target, "\n");
3853e2b1b9c0Schristos 		}
3854803e9293Schristos 		msg->indent.count = indent;
3855e2b1b9c0Schristos 		result = ISC_R_SUCCESS;
3856e2b1b9c0Schristos 		goto cleanup;
3857e2b1b9c0Schristos 	case DNS_PSEUDOSECTION_TSIG:
3858e2b1b9c0Schristos 		ps = dns_message_gettsig(msg, &name);
3859e2b1b9c0Schristos 		if (ps == NULL) {
3860e2b1b9c0Schristos 			result = ISC_R_SUCCESS;
3861e2b1b9c0Schristos 			goto cleanup;
3862e2b1b9c0Schristos 		}
3863e2b1b9c0Schristos 		INDENT(style);
3864e2b1b9c0Schristos 		ADD_STRING(target, "TSIG_PSEUDOSECTION:\n");
38659742fdb4Schristos 		result = dns_master_rdatasettotext(name, ps, style,
38669742fdb4Schristos 						   &msg->indent, target);
3867e2b1b9c0Schristos 		ADD_STRING(target, "\n");
3868e2b1b9c0Schristos 		goto cleanup;
3869e2b1b9c0Schristos 	case DNS_PSEUDOSECTION_SIG0:
3870e2b1b9c0Schristos 		ps = dns_message_getsig0(msg, &name);
3871e2b1b9c0Schristos 		if (ps == NULL) {
3872e2b1b9c0Schristos 			result = ISC_R_SUCCESS;
3873e2b1b9c0Schristos 			goto cleanup;
3874e2b1b9c0Schristos 		}
3875e2b1b9c0Schristos 		INDENT(style);
3876e2b1b9c0Schristos 		ADD_STRING(target, "SIG0_PSEUDOSECTION:\n");
38779742fdb4Schristos 		result = dns_master_rdatasettotext(name, ps, style,
38789742fdb4Schristos 						   &msg->indent, target);
3879e2b1b9c0Schristos 		if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0 &&
3880e2b1b9c0Schristos 		    (flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
38819742fdb4Schristos 		{
3882e2b1b9c0Schristos 			ADD_STRING(target, "\n");
38839742fdb4Schristos 		}
3884e2b1b9c0Schristos 		goto cleanup;
3885e2b1b9c0Schristos 	}
3886e2b1b9c0Schristos 
3887e2b1b9c0Schristos 	result = ISC_R_UNEXPECTED;
3888e2b1b9c0Schristos 
3889e2b1b9c0Schristos cleanup:
38909742fdb4Schristos 	msg->indent.count = saved_count;
3891e2b1b9c0Schristos 	return (result);
3892e2b1b9c0Schristos }
3893e2b1b9c0Schristos 
3894e2b1b9c0Schristos isc_result_t
dns_message_pseudosectiontotext(dns_message_t * msg,dns_pseudosection_t section,const dns_master_style_t * style,dns_messagetextflag_t flags,isc_buffer_t * target)38959742fdb4Schristos dns_message_pseudosectiontotext(dns_message_t *msg, dns_pseudosection_t section,
3896e2b1b9c0Schristos 				const dns_master_style_t *style,
3897e2b1b9c0Schristos 				dns_messagetextflag_t flags,
38989742fdb4Schristos 				isc_buffer_t *target) {
3899e2b1b9c0Schristos 	dns_rdataset_t *ps = NULL;
3900e2b1b9c0Schristos 	const dns_name_t *name = NULL;
3901e2b1b9c0Schristos 	isc_result_t result;
3902e2b1b9c0Schristos 	char buf[sizeof(" (65000 bytes)")];
3903f2e20987Schristos 	uint32_t mbz;
3904e2b1b9c0Schristos 	dns_rdata_t rdata;
3905e2b1b9c0Schristos 	isc_buffer_t optbuf;
3906f2e20987Schristos 	uint16_t optcode, optlen;
3907e2b1b9c0Schristos 	unsigned char *optdata;
3908e2b1b9c0Schristos 
3909e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
3910e2b1b9c0Schristos 	REQUIRE(target != NULL);
3911e2b1b9c0Schristos 	REQUIRE(VALID_PSEUDOSECTION(section));
3912e2b1b9c0Schristos 
39139742fdb4Schristos 	if ((dns_master_styleflags(style) & DNS_STYLEFLAG_YAML) != 0) {
3914e2b1b9c0Schristos 		return (dns_message_pseudosectiontoyaml(msg, section, style,
3915e2b1b9c0Schristos 							flags, target));
39169742fdb4Schristos 	}
39179742fdb4Schristos 
3918e2b1b9c0Schristos 	switch (section) {
3919e2b1b9c0Schristos 	case DNS_PSEUDOSECTION_OPT:
3920e2b1b9c0Schristos 		ps = dns_message_getopt(msg);
39219742fdb4Schristos 		if (ps == NULL) {
3922e2b1b9c0Schristos 			return (ISC_R_SUCCESS);
39239742fdb4Schristos 		}
3924e2b1b9c0Schristos 		if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) {
3925e2b1b9c0Schristos 			INDENT(style);
3926e2b1b9c0Schristos 			ADD_STRING(target, ";; OPT PSEUDOSECTION:\n");
3927e2b1b9c0Schristos 		}
3928e2b1b9c0Schristos 
3929e2b1b9c0Schristos 		INDENT(style);
3930e2b1b9c0Schristos 		ADD_STRING(target, "; EDNS: version: ");
3931e2b1b9c0Schristos 		snprintf(buf, sizeof(buf), "%u",
3932e2b1b9c0Schristos 			 (unsigned int)((ps->ttl & 0x00ff0000) >> 16));
3933e2b1b9c0Schristos 		ADD_STRING(target, buf);
3934e2b1b9c0Schristos 		ADD_STRING(target, ", flags:");
39359742fdb4Schristos 		if ((ps->ttl & DNS_MESSAGEEXTFLAG_DO) != 0) {
3936e2b1b9c0Schristos 			ADD_STRING(target, " do");
39379742fdb4Schristos 		}
3938e2b1b9c0Schristos 		mbz = ps->ttl & 0xffff;
3939e2b1b9c0Schristos 		mbz &= ~DNS_MESSAGEEXTFLAG_DO; /* Known Flags. */
3940e2b1b9c0Schristos 		if (mbz != 0) {
3941e2b1b9c0Schristos 			ADD_STRING(target, "; MBZ: ");
3942e2b1b9c0Schristos 			snprintf(buf, sizeof(buf), "0x%.4x", mbz);
3943e2b1b9c0Schristos 			ADD_STRING(target, buf);
3944e2b1b9c0Schristos 			ADD_STRING(target, ", udp: ");
39459742fdb4Schristos 		} else {
3946e2b1b9c0Schristos 			ADD_STRING(target, "; udp: ");
39479742fdb4Schristos 		}
3948e2b1b9c0Schristos 		snprintf(buf, sizeof(buf), "%u\n", (unsigned int)ps->rdclass);
3949e2b1b9c0Schristos 		ADD_STRING(target, buf);
3950e2b1b9c0Schristos 
3951e2b1b9c0Schristos 		result = dns_rdataset_first(ps);
39529742fdb4Schristos 		if (result != ISC_R_SUCCESS) {
3953e2b1b9c0Schristos 			return (ISC_R_SUCCESS);
39549742fdb4Schristos 		}
3955e2b1b9c0Schristos 
3956e2b1b9c0Schristos 		/*
3957e2b1b9c0Schristos 		 * Print EDNS info, if any.
3958e2b1b9c0Schristos 		 *
3959e2b1b9c0Schristos 		 * WARNING: The option contents may be malformed as
3960e2b1b9c0Schristos 		 * dig +ednsopt=value:<content> does no validity
3961e2b1b9c0Schristos 		 * checking.
3962e2b1b9c0Schristos 		 */
3963e2b1b9c0Schristos 		dns_rdata_init(&rdata);
3964e2b1b9c0Schristos 		dns_rdataset_current(ps, &rdata);
3965e2b1b9c0Schristos 
3966e2b1b9c0Schristos 		isc_buffer_init(&optbuf, rdata.data, rdata.length);
3967e2b1b9c0Schristos 		isc_buffer_add(&optbuf, rdata.length);
3968e2b1b9c0Schristos 		while (isc_buffer_remaininglength(&optbuf) != 0) {
3969e2b1b9c0Schristos 			INSIST(isc_buffer_remaininglength(&optbuf) >= 4U);
3970e2b1b9c0Schristos 			optcode = isc_buffer_getuint16(&optbuf);
3971e2b1b9c0Schristos 			optlen = isc_buffer_getuint16(&optbuf);
3972e2b1b9c0Schristos 
3973e2b1b9c0Schristos 			INSIST(isc_buffer_remaininglength(&optbuf) >= optlen);
3974e2b1b9c0Schristos 
3975e2b1b9c0Schristos 			INDENT(style);
3976e2b1b9c0Schristos 
397755ba2b39Schristos 			if (optcode == DNS_OPT_LLQ) {
397855ba2b39Schristos 				ADD_STRING(target, "; LLQ:");
39799742fdb4Schristos 				if (optlen == 18U) {
398055ba2b39Schristos 					result = render_llq(&optbuf, target);
398155ba2b39Schristos 					if (result != ISC_R_SUCCESS) {
398255ba2b39Schristos 						return (result);
398355ba2b39Schristos 					}
398455ba2b39Schristos 					ADD_STRING(target, "\n");
398555ba2b39Schristos 					continue;
398655ba2b39Schristos 				}
398755ba2b39Schristos 			} else if (optcode == DNS_OPT_NSID) {
39889742fdb4Schristos 				ADD_STRING(target, "; NSID:");
3989e2b1b9c0Schristos 			} else if (optcode == DNS_OPT_COOKIE) {
39909742fdb4Schristos 				ADD_STRING(target, "; COOKIE:");
3991e2b1b9c0Schristos 			} else if (optcode == DNS_OPT_CLIENT_SUBNET) {
3992e2b1b9c0Schristos 				isc_buffer_t ecsbuf;
3993e2b1b9c0Schristos 
39949742fdb4Schristos 				ADD_STRING(target, "; CLIENT-SUBNET:");
3995e2b1b9c0Schristos 				isc_buffer_init(&ecsbuf,
3996e2b1b9c0Schristos 						isc_buffer_current(&optbuf),
3997e2b1b9c0Schristos 						optlen);
3998e2b1b9c0Schristos 				isc_buffer_add(&ecsbuf, optlen);
3999e2b1b9c0Schristos 				result = render_ecs(&ecsbuf, target);
40009742fdb4Schristos 				if (result == ISC_R_NOSPACE) {
4001e2b1b9c0Schristos 					return (result);
40029742fdb4Schristos 				}
4003e2b1b9c0Schristos 				if (result == ISC_R_SUCCESS) {
4004e2b1b9c0Schristos 					isc_buffer_forward(&optbuf, optlen);
4005e2b1b9c0Schristos 					ADD_STRING(target, "\n");
4006e2b1b9c0Schristos 					continue;
4007e2b1b9c0Schristos 				}
4008e2b1b9c0Schristos 			} else if (optcode == DNS_OPT_EXPIRE) {
40099742fdb4Schristos 				ADD_STRING(target, "; EXPIRE:");
4010e2b1b9c0Schristos 				if (optlen == 4) {
4011f2e20987Schristos 					uint32_t secs;
4012e2b1b9c0Schristos 					secs = isc_buffer_getuint32(&optbuf);
4013e2b1b9c0Schristos 					snprintf(buf, sizeof(buf), " %u", secs);
4014e2b1b9c0Schristos 					ADD_STRING(target, buf);
4015e2b1b9c0Schristos 					ADD_STRING(target, " (");
40169742fdb4Schristos 					result = dns_ttl_totext(secs, true,
40179742fdb4Schristos 								true, target);
40189742fdb4Schristos 					if (result != ISC_R_SUCCESS) {
4019e2b1b9c0Schristos 						return (result);
40209742fdb4Schristos 					}
4021e2b1b9c0Schristos 					ADD_STRING(target, ")\n");
4022e2b1b9c0Schristos 					continue;
4023e2b1b9c0Schristos 				}
4024e2b1b9c0Schristos 			} else if (optcode == DNS_OPT_TCP_KEEPALIVE) {
40259742fdb4Schristos 				ADD_STRING(target, "; TCP KEEPALIVE:");
4026e2b1b9c0Schristos 				if (optlen == 2) {
4027e2b1b9c0Schristos 					unsigned int dsecs;
4028e2b1b9c0Schristos 					dsecs = isc_buffer_getuint16(&optbuf);
4029e2b1b9c0Schristos 					snprintf(buf, sizeof(buf), " %u.%u",
4030e2b1b9c0Schristos 						 dsecs / 10U, dsecs % 10U);
4031e2b1b9c0Schristos 					ADD_STRING(target, buf);
4032e2b1b9c0Schristos 					ADD_STRING(target, " secs\n");
4033e2b1b9c0Schristos 					continue;
4034e2b1b9c0Schristos 				}
4035e2b1b9c0Schristos 			} else if (optcode == DNS_OPT_PAD) {
40369742fdb4Schristos 				ADD_STRING(target, "; PAD:");
4037e2b1b9c0Schristos 				if (optlen > 0U) {
4038e2b1b9c0Schristos 					snprintf(buf, sizeof(buf),
4039e2b1b9c0Schristos 						 " (%u bytes)", optlen);
4040e2b1b9c0Schristos 					ADD_STRING(target, buf);
4041e2b1b9c0Schristos 					isc_buffer_forward(&optbuf, optlen);
4042e2b1b9c0Schristos 				}
4043e2b1b9c0Schristos 				ADD_STRING(target, "\n");
4044e2b1b9c0Schristos 				continue;
4045e2b1b9c0Schristos 			} else if (optcode == DNS_OPT_KEY_TAG) {
40469742fdb4Schristos 				ADD_STRING(target, "; KEY-TAG:");
4047e2b1b9c0Schristos 				if (optlen > 0U && (optlen % 2U) == 0U) {
40489742fdb4Schristos 					const char *sep = "";
4049f2e20987Schristos 					uint16_t id;
4050e2b1b9c0Schristos 					while (optlen > 0U) {
40519742fdb4Schristos 						id = isc_buffer_getuint16(
40529742fdb4Schristos 							&optbuf);
40539742fdb4Schristos 						snprintf(buf, sizeof(buf),
40549742fdb4Schristos 							 "%s %u", sep, id);
4055e2b1b9c0Schristos 						ADD_STRING(target, buf);
4056e2b1b9c0Schristos 						sep = ",";
4057e2b1b9c0Schristos 						optlen -= 2;
4058e2b1b9c0Schristos 					}
4059e2b1b9c0Schristos 					ADD_STRING(target, "\n");
4060e2b1b9c0Schristos 					continue;
4061e2b1b9c0Schristos 				}
4062803e9293Schristos 			} else if (optcode == DNS_OPT_EDE) {
4063803e9293Schristos 				ADD_STRING(target, "; EDE:");
4064803e9293Schristos 				if (optlen >= 2U) {
4065803e9293Schristos 					uint16_t ede;
4066803e9293Schristos 					ede = isc_buffer_getuint16(&optbuf);
4067803e9293Schristos 					snprintf(buf, sizeof(buf), " %u", ede);
4068803e9293Schristos 					ADD_STRING(target, buf);
4069803e9293Schristos 					if (ede < ARRAY_SIZE(edetext)) {
4070803e9293Schristos 						ADD_STRING(target, " (");
4071803e9293Schristos 						ADD_STRING(target,
4072803e9293Schristos 							   edetext[ede]);
4073803e9293Schristos 						ADD_STRING(target, ")");
4074803e9293Schristos 					}
4075803e9293Schristos 					optlen -= 2;
4076803e9293Schristos 					if (optlen != 0) {
4077803e9293Schristos 						ADD_STRING(target, ":");
4078803e9293Schristos 					}
4079803e9293Schristos 				} else if (optlen == 1U) {
4080803e9293Schristos 					/* Malformed */
4081803e9293Schristos 					optdata = isc_buffer_current(&optbuf);
4082803e9293Schristos 					snprintf(buf, sizeof(buf),
4083803e9293Schristos 						 " %02x (\"%c\")\n", optdata[0],
4084803e9293Schristos 						 isprint(optdata[0])
4085803e9293Schristos 							 ? optdata[0]
4086803e9293Schristos 							 : '.');
4087803e9293Schristos 					isc_buffer_forward(&optbuf, optlen);
4088803e9293Schristos 					ADD_STRING(target, buf);
4089803e9293Schristos 					continue;
4090803e9293Schristos 				}
40919ad14dd7Schristos 			} else if (optcode == DNS_OPT_CLIENT_TAG) {
40929ad14dd7Schristos 				uint16_t id;
40939742fdb4Schristos 				ADD_STRING(target, "; CLIENT-TAG:");
40949ad14dd7Schristos 				if (optlen == 2U) {
40959ad14dd7Schristos 					id = isc_buffer_getuint16(&optbuf);
40969742fdb4Schristos 					snprintf(buf, sizeof(buf), " %u\n", id);
40979ad14dd7Schristos 					ADD_STRING(target, buf);
40989ad14dd7Schristos 					optlen -= 2;
40999ad14dd7Schristos 					POST(optlen);
41009ad14dd7Schristos 					continue;
41019ad14dd7Schristos 				}
41029ad14dd7Schristos 			} else if (optcode == DNS_OPT_SERVER_TAG) {
41039ad14dd7Schristos 				uint16_t id;
41049742fdb4Schristos 				ADD_STRING(target, "; SERVER-TAG:");
41059ad14dd7Schristos 				if (optlen == 2U) {
41069ad14dd7Schristos 					id = isc_buffer_getuint16(&optbuf);
41079742fdb4Schristos 					snprintf(buf, sizeof(buf), " %u\n", id);
41089ad14dd7Schristos 					ADD_STRING(target, buf);
41099ad14dd7Schristos 					optlen -= 2;
41109ad14dd7Schristos 					POST(optlen);
41119ad14dd7Schristos 					continue;
41129ad14dd7Schristos 				}
4113e2b1b9c0Schristos 			} else {
4114e2b1b9c0Schristos 				ADD_STRING(target, "; OPT=");
41159742fdb4Schristos 				snprintf(buf, sizeof(buf), "%u:", optcode);
4116e2b1b9c0Schristos 				ADD_STRING(target, buf);
4117e2b1b9c0Schristos 			}
4118e2b1b9c0Schristos 
4119e2b1b9c0Schristos 			if (optlen != 0) {
4120e2b1b9c0Schristos 				int i;
4121803e9293Schristos 				bool utf8ok = false;
41229742fdb4Schristos 
41239742fdb4Schristos 				ADD_STRING(target, " ");
4124e2b1b9c0Schristos 
4125e2b1b9c0Schristos 				optdata = isc_buffer_current(&optbuf);
4126803e9293Schristos 				if (optcode == DNS_OPT_EDE) {
4127803e9293Schristos 					utf8ok = isc_utf8_valid(optdata,
4128803e9293Schristos 								optlen);
4129803e9293Schristos 				}
4130803e9293Schristos 				if (!utf8ok) {
4131e2b1b9c0Schristos 					for (i = 0; i < optlen; i++) {
4132e2b1b9c0Schristos 						const char *sep;
4133e2b1b9c0Schristos 						switch (optcode) {
4134e2b1b9c0Schristos 						case DNS_OPT_COOKIE:
4135e2b1b9c0Schristos 							sep = "";
4136e2b1b9c0Schristos 							break;
4137e2b1b9c0Schristos 						default:
4138e2b1b9c0Schristos 							sep = " ";
4139e2b1b9c0Schristos 							break;
4140e2b1b9c0Schristos 						}
4141803e9293Schristos 						snprintf(buf, sizeof(buf),
4142803e9293Schristos 							 "%02x%s", optdata[i],
4143803e9293Schristos 							 sep);
4144e2b1b9c0Schristos 						ADD_STRING(target, buf);
4145e2b1b9c0Schristos 					}
4146803e9293Schristos 				}
4147e2b1b9c0Schristos 
4148e2b1b9c0Schristos 				isc_buffer_forward(&optbuf, optlen);
4149e2b1b9c0Schristos 
4150e2b1b9c0Schristos 				if (optcode == DNS_OPT_COOKIE) {
4151e2b1b9c0Schristos 					/*
4152e2b1b9c0Schristos 					 * Valid server cookie?
4153e2b1b9c0Schristos 					 */
41549742fdb4Schristos 					if (msg->cc_ok && optlen >= 16) {
4155e2b1b9c0Schristos 						ADD_STRING(target, " (good)");
41569742fdb4Schristos 					}
4157e2b1b9c0Schristos 					/*
4158e2b1b9c0Schristos 					 * Server cookie is not valid but
4159e2b1b9c0Schristos 					 * we had our cookie echoed back.
4160e2b1b9c0Schristos 					 */
41619742fdb4Schristos 					if (msg->cc_ok && optlen < 16) {
4162e2b1b9c0Schristos 						ADD_STRING(target, " (echoed)");
41639742fdb4Schristos 					}
4164e2b1b9c0Schristos 					/*
4165e2b1b9c0Schristos 					 * We didn't get our cookie echoed
4166e2b1b9c0Schristos 					 * back.
4167e2b1b9c0Schristos 					 */
41689742fdb4Schristos 					if (msg->cc_bad) {
4169e2b1b9c0Schristos 						ADD_STRING(target, " (bad)");
41709742fdb4Schristos 					}
4171e2b1b9c0Schristos 					ADD_STRING(target, "\n");
4172e2b1b9c0Schristos 					continue;
4173e2b1b9c0Schristos 				}
4174e2b1b9c0Schristos 
4175e2b1b9c0Schristos 				if (optcode == DNS_OPT_CLIENT_SUBNET) {
4176e2b1b9c0Schristos 					ADD_STRING(target, "\n");
4177e2b1b9c0Schristos 					continue;
4178e2b1b9c0Schristos 				}
4179e2b1b9c0Schristos 
4180e2b1b9c0Schristos 				/*
4181e2b1b9c0Schristos 				 * For non-COOKIE options, add a printable
4182803e9293Schristos 				 * version.
4183e2b1b9c0Schristos 				 */
4184803e9293Schristos 				if (optcode != DNS_OPT_EDE) {
4185e2b1b9c0Schristos 					ADD_STRING(target, "(\"");
4186803e9293Schristos 				} else {
4187803e9293Schristos 					ADD_STRING(target, "(");
4188803e9293Schristos 				}
4189e2b1b9c0Schristos 				if (isc_buffer_availablelength(target) < optlen)
41909742fdb4Schristos 				{
4191e2b1b9c0Schristos 					return (ISC_R_NOSPACE);
41929742fdb4Schristos 				}
4193e2b1b9c0Schristos 				for (i = 0; i < optlen; i++) {
4194803e9293Schristos 					if (isprint(optdata[i]) ||
4195*4ac1c27eSchristos 					    (utf8ok && optdata[i] > 127))
4196*4ac1c27eSchristos 					{
41979742fdb4Schristos 						isc_buffer_putmem(
41989742fdb4Schristos 							target, &optdata[i], 1);
41999742fdb4Schristos 					} else {
4200e2b1b9c0Schristos 						isc_buffer_putstr(target, ".");
4201e2b1b9c0Schristos 					}
42029742fdb4Schristos 				}
4203803e9293Schristos 				if (optcode != DNS_OPT_EDE) {
4204e2b1b9c0Schristos 					ADD_STRING(target, "\")");
4205803e9293Schristos 				} else {
4206803e9293Schristos 					ADD_STRING(target, ")");
4207803e9293Schristos 				}
4208e2b1b9c0Schristos 			}
4209e2b1b9c0Schristos 			ADD_STRING(target, "\n");
4210e2b1b9c0Schristos 		}
4211e2b1b9c0Schristos 		return (ISC_R_SUCCESS);
4212e2b1b9c0Schristos 	case DNS_PSEUDOSECTION_TSIG:
4213e2b1b9c0Schristos 		ps = dns_message_gettsig(msg, &name);
42149742fdb4Schristos 		if (ps == NULL) {
4215e2b1b9c0Schristos 			return (ISC_R_SUCCESS);
42169742fdb4Schristos 		}
4217e2b1b9c0Schristos 		INDENT(style);
42189742fdb4Schristos 		if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) {
4219e2b1b9c0Schristos 			ADD_STRING(target, ";; TSIG PSEUDOSECTION:\n");
42209742fdb4Schristos 		}
42219742fdb4Schristos 		result = dns_master_rdatasettotext(name, ps, style,
42229742fdb4Schristos 						   &msg->indent, target);
4223e2b1b9c0Schristos 		if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0 &&
4224e2b1b9c0Schristos 		    (flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
42259742fdb4Schristos 		{
4226e2b1b9c0Schristos 			ADD_STRING(target, "\n");
42279742fdb4Schristos 		}
4228e2b1b9c0Schristos 		return (result);
4229e2b1b9c0Schristos 	case DNS_PSEUDOSECTION_SIG0:
4230e2b1b9c0Schristos 		ps = dns_message_getsig0(msg, &name);
42319742fdb4Schristos 		if (ps == NULL) {
4232e2b1b9c0Schristos 			return (ISC_R_SUCCESS);
42339742fdb4Schristos 		}
4234e2b1b9c0Schristos 		INDENT(style);
42359742fdb4Schristos 		if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) {
4236e2b1b9c0Schristos 			ADD_STRING(target, ";; SIG0 PSEUDOSECTION:\n");
42379742fdb4Schristos 		}
42389742fdb4Schristos 		result = dns_master_rdatasettotext(name, ps, style,
42399742fdb4Schristos 						   &msg->indent, target);
4240e2b1b9c0Schristos 		if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0 &&
4241e2b1b9c0Schristos 		    (flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
42429742fdb4Schristos 		{
4243e2b1b9c0Schristos 			ADD_STRING(target, "\n");
42449742fdb4Schristos 		}
4245e2b1b9c0Schristos 		return (result);
4246e2b1b9c0Schristos 	}
4247e2b1b9c0Schristos 	result = ISC_R_UNEXPECTED;
4248e2b1b9c0Schristos cleanup:
4249e2b1b9c0Schristos 	return (result);
4250e2b1b9c0Schristos }
4251e2b1b9c0Schristos 
4252e2b1b9c0Schristos isc_result_t
dns_message_headertotext(dns_message_t * msg,const dns_master_style_t * style,dns_messagetextflag_t flags,isc_buffer_t * target)42539742fdb4Schristos dns_message_headertotext(dns_message_t *msg, const dns_master_style_t *style,
42549742fdb4Schristos 			 dns_messagetextflag_t flags, isc_buffer_t *target) {
4255e2b1b9c0Schristos 	char buf[sizeof("1234567890")];
4256e2b1b9c0Schristos 	isc_result_t result;
4257e2b1b9c0Schristos 
4258e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
4259e2b1b9c0Schristos 	REQUIRE(target != NULL);
4260e2b1b9c0Schristos 
42619742fdb4Schristos 	if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) != 0) {
42629742fdb4Schristos 		return (ISC_R_SUCCESS);
42639742fdb4Schristos 	}
42649742fdb4Schristos 
42659742fdb4Schristos 	if (dns_master_styleflags(style) & DNS_STYLEFLAG_YAML) {
4266e2b1b9c0Schristos 		INDENT(style);
4267e2b1b9c0Schristos 		ADD_STRING(target, "opcode: ");
4268e2b1b9c0Schristos 		ADD_STRING(target, opcodetext[msg->opcode]);
4269e2b1b9c0Schristos 		ADD_STRING(target, "\n");
4270e2b1b9c0Schristos 		INDENT(style);
4271e2b1b9c0Schristos 		ADD_STRING(target, "status: ");
4272e2b1b9c0Schristos 		result = dns_rcode_totext(msg->rcode, target);
42739742fdb4Schristos 		if (result != ISC_R_SUCCESS) {
4274e2b1b9c0Schristos 			return (result);
42759742fdb4Schristos 		}
4276e2b1b9c0Schristos 		ADD_STRING(target, "\n");
4277e2b1b9c0Schristos 		INDENT(style);
4278e2b1b9c0Schristos 		ADD_STRING(target, "id: ");
42799742fdb4Schristos 		snprintf(buf, sizeof(buf), "%u", msg->id);
4280e2b1b9c0Schristos 		ADD_STRING(target, buf);
4281e2b1b9c0Schristos 		ADD_STRING(target, "\n");
4282e2b1b9c0Schristos 		INDENT(style);
4283e2b1b9c0Schristos 		ADD_STRING(target, "flags:");
42849742fdb4Schristos 		if ((msg->flags & DNS_MESSAGEFLAG_QR) != 0) {
4285e2b1b9c0Schristos 			ADD_STRING(target, " qr");
42869742fdb4Schristos 		}
42879742fdb4Schristos 		if ((msg->flags & DNS_MESSAGEFLAG_AA) != 0) {
4288e2b1b9c0Schristos 			ADD_STRING(target, " aa");
42899742fdb4Schristos 		}
42909742fdb4Schristos 		if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0) {
4291e2b1b9c0Schristos 			ADD_STRING(target, " tc");
42929742fdb4Schristos 		}
42939742fdb4Schristos 		if ((msg->flags & DNS_MESSAGEFLAG_RD) != 0) {
4294e2b1b9c0Schristos 			ADD_STRING(target, " rd");
42959742fdb4Schristos 		}
42969742fdb4Schristos 		if ((msg->flags & DNS_MESSAGEFLAG_RA) != 0) {
4297e2b1b9c0Schristos 			ADD_STRING(target, " ra");
42989742fdb4Schristos 		}
42999742fdb4Schristos 		if ((msg->flags & DNS_MESSAGEFLAG_AD) != 0) {
4300e2b1b9c0Schristos 			ADD_STRING(target, " ad");
43019742fdb4Schristos 		}
43029742fdb4Schristos 		if ((msg->flags & DNS_MESSAGEFLAG_CD) != 0) {
4303e2b1b9c0Schristos 			ADD_STRING(target, " cd");
43049742fdb4Schristos 		}
4305e2b1b9c0Schristos 		ADD_STRING(target, "\n");
4306e2b1b9c0Schristos 		/*
4307e2b1b9c0Schristos 		 * The final unnamed flag must be zero.
4308e2b1b9c0Schristos 		 */
4309e2b1b9c0Schristos 		if ((msg->flags & 0x0040U) != 0) {
4310e2b1b9c0Schristos 			INDENT(style);
4311e2b1b9c0Schristos 			ADD_STRING(target, "MBZ: 0x4");
4312e2b1b9c0Schristos 			ADD_STRING(target, "\n");
4313e2b1b9c0Schristos 		}
4314e2b1b9c0Schristos 		if (msg->opcode != dns_opcode_update) {
4315e2b1b9c0Schristos 			INDENT(style);
4316e2b1b9c0Schristos 			ADD_STRING(target, "QUESTION: ");
4317e2b1b9c0Schristos 		} else {
4318c0b5d9fbSchristos 			INDENT(style);
4319e2b1b9c0Schristos 			ADD_STRING(target, "ZONE: ");
4320e2b1b9c0Schristos 		}
4321e2b1b9c0Schristos 		snprintf(buf, sizeof(buf), "%1u",
4322e2b1b9c0Schristos 			 msg->counts[DNS_SECTION_QUESTION]);
4323e2b1b9c0Schristos 		ADD_STRING(target, buf);
4324e2b1b9c0Schristos 		ADD_STRING(target, "\n");
4325e2b1b9c0Schristos 		if (msg->opcode != dns_opcode_update) {
4326e2b1b9c0Schristos 			INDENT(style);
4327e2b1b9c0Schristos 			ADD_STRING(target, "ANSWER: ");
4328e2b1b9c0Schristos 		} else {
4329e2b1b9c0Schristos 			INDENT(style);
4330e2b1b9c0Schristos 			ADD_STRING(target, "PREREQ: ");
4331e2b1b9c0Schristos 		}
4332e2b1b9c0Schristos 		snprintf(buf, sizeof(buf), "%1u",
4333e2b1b9c0Schristos 			 msg->counts[DNS_SECTION_ANSWER]);
4334e2b1b9c0Schristos 		ADD_STRING(target, buf);
4335e2b1b9c0Schristos 		ADD_STRING(target, "\n");
4336e2b1b9c0Schristos 		if (msg->opcode != dns_opcode_update) {
4337e2b1b9c0Schristos 			INDENT(style);
4338e2b1b9c0Schristos 			ADD_STRING(target, "AUTHORITY: ");
4339e2b1b9c0Schristos 		} else {
4340e2b1b9c0Schristos 			INDENT(style);
4341e2b1b9c0Schristos 			ADD_STRING(target, "UPDATE: ");
4342e2b1b9c0Schristos 		}
4343e2b1b9c0Schristos 		snprintf(buf, sizeof(buf), "%1u",
4344e2b1b9c0Schristos 			 msg->counts[DNS_SECTION_AUTHORITY]);
4345e2b1b9c0Schristos 		ADD_STRING(target, buf);
4346e2b1b9c0Schristos 		ADD_STRING(target, "\n");
4347e2b1b9c0Schristos 		INDENT(style);
4348e2b1b9c0Schristos 		ADD_STRING(target, "ADDITIONAL: ");
4349e2b1b9c0Schristos 		snprintf(buf, sizeof(buf), "%1u",
4350e2b1b9c0Schristos 			 msg->counts[DNS_SECTION_ADDITIONAL]);
4351e2b1b9c0Schristos 		ADD_STRING(target, buf);
4352e2b1b9c0Schristos 		ADD_STRING(target, "\n");
43539742fdb4Schristos 	} else {
4354e2b1b9c0Schristos 		INDENT(style);
4355e2b1b9c0Schristos 		ADD_STRING(target, ";; ->>HEADER<<- opcode: ");
4356e2b1b9c0Schristos 		ADD_STRING(target, opcodetext[msg->opcode]);
4357e2b1b9c0Schristos 		ADD_STRING(target, ", status: ");
4358e2b1b9c0Schristos 		result = dns_rcode_totext(msg->rcode, target);
43599742fdb4Schristos 		if (result != ISC_R_SUCCESS) {
4360e2b1b9c0Schristos 			return (result);
43619742fdb4Schristos 		}
4362e2b1b9c0Schristos 		ADD_STRING(target, ", id: ");
4363e2b1b9c0Schristos 		snprintf(buf, sizeof(buf), "%6u", msg->id);
4364e2b1b9c0Schristos 		ADD_STRING(target, buf);
4365e2b1b9c0Schristos 		ADD_STRING(target, "\n");
4366e2b1b9c0Schristos 		INDENT(style);
4367e2b1b9c0Schristos 		ADD_STRING(target, ";; flags:");
43689742fdb4Schristos 		if ((msg->flags & DNS_MESSAGEFLAG_QR) != 0) {
4369e2b1b9c0Schristos 			ADD_STRING(target, " qr");
43709742fdb4Schristos 		}
43719742fdb4Schristos 		if ((msg->flags & DNS_MESSAGEFLAG_AA) != 0) {
4372e2b1b9c0Schristos 			ADD_STRING(target, " aa");
43739742fdb4Schristos 		}
43749742fdb4Schristos 		if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0) {
4375e2b1b9c0Schristos 			ADD_STRING(target, " tc");
43769742fdb4Schristos 		}
43779742fdb4Schristos 		if ((msg->flags & DNS_MESSAGEFLAG_RD) != 0) {
4378e2b1b9c0Schristos 			ADD_STRING(target, " rd");
43799742fdb4Schristos 		}
43809742fdb4Schristos 		if ((msg->flags & DNS_MESSAGEFLAG_RA) != 0) {
4381e2b1b9c0Schristos 			ADD_STRING(target, " ra");
43829742fdb4Schristos 		}
43839742fdb4Schristos 		if ((msg->flags & DNS_MESSAGEFLAG_AD) != 0) {
4384e2b1b9c0Schristos 			ADD_STRING(target, " ad");
43859742fdb4Schristos 		}
43869742fdb4Schristos 		if ((msg->flags & DNS_MESSAGEFLAG_CD) != 0) {
4387e2b1b9c0Schristos 			ADD_STRING(target, " cd");
43889742fdb4Schristos 		}
4389e2b1b9c0Schristos 		/*
4390e2b1b9c0Schristos 		 * The final unnamed flag must be zero.
4391e2b1b9c0Schristos 		 */
4392e2b1b9c0Schristos 		if ((msg->flags & 0x0040U) != 0) {
4393e2b1b9c0Schristos 			INDENT(style);
4394e2b1b9c0Schristos 			ADD_STRING(target, "; MBZ: 0x4");
4395e2b1b9c0Schristos 		}
4396e2b1b9c0Schristos 		if (msg->opcode != dns_opcode_update) {
4397e2b1b9c0Schristos 			INDENT(style);
4398e2b1b9c0Schristos 			ADD_STRING(target, "; QUESTION: ");
4399e2b1b9c0Schristos 		} else {
4400e2b1b9c0Schristos 			INDENT(style);
4401e2b1b9c0Schristos 			ADD_STRING(target, "; ZONE: ");
4402e2b1b9c0Schristos 		}
4403e2b1b9c0Schristos 		snprintf(buf, sizeof(buf), "%1u",
4404e2b1b9c0Schristos 			 msg->counts[DNS_SECTION_QUESTION]);
4405e2b1b9c0Schristos 		ADD_STRING(target, buf);
4406e2b1b9c0Schristos 		if (msg->opcode != dns_opcode_update) {
4407e2b1b9c0Schristos 			ADD_STRING(target, ", ANSWER: ");
4408e2b1b9c0Schristos 		} else {
4409e2b1b9c0Schristos 			ADD_STRING(target, ", PREREQ: ");
4410e2b1b9c0Schristos 		}
4411e2b1b9c0Schristos 		snprintf(buf, sizeof(buf), "%1u",
4412e2b1b9c0Schristos 			 msg->counts[DNS_SECTION_ANSWER]);
4413e2b1b9c0Schristos 		ADD_STRING(target, buf);
4414e2b1b9c0Schristos 		if (msg->opcode != dns_opcode_update) {
4415e2b1b9c0Schristos 			ADD_STRING(target, ", AUTHORITY: ");
4416e2b1b9c0Schristos 		} else {
4417e2b1b9c0Schristos 			ADD_STRING(target, ", UPDATE: ");
4418e2b1b9c0Schristos 		}
4419e2b1b9c0Schristos 		snprintf(buf, sizeof(buf), "%1u",
4420e2b1b9c0Schristos 			 msg->counts[DNS_SECTION_AUTHORITY]);
4421e2b1b9c0Schristos 		ADD_STRING(target, buf);
4422e2b1b9c0Schristos 		ADD_STRING(target, ", ADDITIONAL: ");
4423e2b1b9c0Schristos 		snprintf(buf, sizeof(buf), "%1u",
4424e2b1b9c0Schristos 			 msg->counts[DNS_SECTION_ADDITIONAL]);
4425e2b1b9c0Schristos 		ADD_STRING(target, buf);
4426e2b1b9c0Schristos 		ADD_STRING(target, "\n");
4427e2b1b9c0Schristos 	}
4428e2b1b9c0Schristos 
4429e2b1b9c0Schristos cleanup:
4430e2b1b9c0Schristos 	return (result);
4431e2b1b9c0Schristos }
4432e2b1b9c0Schristos 
44339742fdb4Schristos isc_result_t
dns_message_totext(dns_message_t * msg,const dns_master_style_t * style,dns_messagetextflag_t flags,isc_buffer_t * target)44349742fdb4Schristos dns_message_totext(dns_message_t *msg, const dns_master_style_t *style,
44359742fdb4Schristos 		   dns_messagetextflag_t flags, isc_buffer_t *target) {
44369742fdb4Schristos 	isc_result_t result;
44379742fdb4Schristos 
44389742fdb4Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
44399742fdb4Schristos 	REQUIRE(target != NULL);
44409742fdb4Schristos 
44419742fdb4Schristos 	result = dns_message_headertotext(msg, style, flags, target);
44429742fdb4Schristos 	if (result != ISC_R_SUCCESS) {
44439742fdb4Schristos 		return (result);
44449742fdb4Schristos 	}
44459742fdb4Schristos 
44469742fdb4Schristos 	result = dns_message_pseudosectiontotext(msg, DNS_PSEUDOSECTION_OPT,
44479742fdb4Schristos 						 style, flags, target);
44489742fdb4Schristos 	if (result != ISC_R_SUCCESS) {
44499742fdb4Schristos 		return (result);
44509742fdb4Schristos 	}
44519742fdb4Schristos 
44529742fdb4Schristos 	result = dns_message_sectiontotext(msg, DNS_SECTION_QUESTION, style,
44539742fdb4Schristos 					   flags, target);
44549742fdb4Schristos 	if (result != ISC_R_SUCCESS) {
44559742fdb4Schristos 		return (result);
44569742fdb4Schristos 	}
44579742fdb4Schristos 
44589742fdb4Schristos 	result = dns_message_sectiontotext(msg, DNS_SECTION_ANSWER, style,
44599742fdb4Schristos 					   flags, target);
44609742fdb4Schristos 	if (result != ISC_R_SUCCESS) {
44619742fdb4Schristos 		return (result);
44629742fdb4Schristos 	}
44639742fdb4Schristos 
44649742fdb4Schristos 	result = dns_message_sectiontotext(msg, DNS_SECTION_AUTHORITY, style,
44659742fdb4Schristos 					   flags, target);
44669742fdb4Schristos 	if (result != ISC_R_SUCCESS) {
44679742fdb4Schristos 		return (result);
44689742fdb4Schristos 	}
44699742fdb4Schristos 
44709742fdb4Schristos 	result = dns_message_sectiontotext(msg, DNS_SECTION_ADDITIONAL, style,
44719742fdb4Schristos 					   flags, target);
44729742fdb4Schristos 	if (result != ISC_R_SUCCESS) {
44739742fdb4Schristos 		return (result);
44749742fdb4Schristos 	}
44759742fdb4Schristos 
44769742fdb4Schristos 	result = dns_message_pseudosectiontotext(msg, DNS_PSEUDOSECTION_TSIG,
44779742fdb4Schristos 						 style, flags, target);
44789742fdb4Schristos 	if (result != ISC_R_SUCCESS) {
44799742fdb4Schristos 		return (result);
44809742fdb4Schristos 	}
44819742fdb4Schristos 
44829742fdb4Schristos 	result = dns_message_pseudosectiontotext(msg, DNS_PSEUDOSECTION_SIG0,
44839742fdb4Schristos 						 style, flags, target);
44849742fdb4Schristos 	return (result);
44859742fdb4Schristos }
44869742fdb4Schristos 
4487e2b1b9c0Schristos isc_region_t *
dns_message_getrawmessage(dns_message_t * msg)4488e2b1b9c0Schristos dns_message_getrawmessage(dns_message_t *msg) {
4489e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
4490e2b1b9c0Schristos 	return (&msg->saved);
4491e2b1b9c0Schristos }
4492e2b1b9c0Schristos 
4493e2b1b9c0Schristos void
dns_message_setsortorder(dns_message_t * msg,dns_rdatasetorderfunc_t order,dns_aclenv_t * env,const dns_acl_t * acl,const dns_aclelement_t * elem)4494e2b1b9c0Schristos dns_message_setsortorder(dns_message_t *msg, dns_rdatasetorderfunc_t order,
4495e2b1b9c0Schristos 			 dns_aclenv_t *env, const dns_acl_t *acl,
44969742fdb4Schristos 			 const dns_aclelement_t *elem) {
4497e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
4498e2b1b9c0Schristos 	REQUIRE((order == NULL) == (env == NULL));
4499e2b1b9c0Schristos 	REQUIRE(env == NULL || (acl != NULL || elem != NULL));
4500e2b1b9c0Schristos 
4501e2b1b9c0Schristos 	msg->order = order;
4502e2b1b9c0Schristos 	msg->order_arg.env = env;
4503e2b1b9c0Schristos 	msg->order_arg.acl = acl;
4504e2b1b9c0Schristos 	msg->order_arg.element = elem;
4505e2b1b9c0Schristos }
4506e2b1b9c0Schristos 
4507e2b1b9c0Schristos void
dns_message_settimeadjust(dns_message_t * msg,int timeadjust)4508e2b1b9c0Schristos dns_message_settimeadjust(dns_message_t *msg, int timeadjust) {
4509e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
4510e2b1b9c0Schristos 	msg->timeadjust = timeadjust;
4511e2b1b9c0Schristos }
4512e2b1b9c0Schristos 
4513e2b1b9c0Schristos int
dns_message_gettimeadjust(dns_message_t * msg)4514e2b1b9c0Schristos dns_message_gettimeadjust(dns_message_t *msg) {
4515e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
4516e2b1b9c0Schristos 	return (msg->timeadjust);
4517e2b1b9c0Schristos }
4518e2b1b9c0Schristos 
4519e2b1b9c0Schristos isc_result_t
dns_opcode_totext(dns_opcode_t opcode,isc_buffer_t * target)4520e2b1b9c0Schristos dns_opcode_totext(dns_opcode_t opcode, isc_buffer_t *target) {
4521e2b1b9c0Schristos 	REQUIRE(opcode < 16);
4522e2b1b9c0Schristos 
45239742fdb4Schristos 	if (isc_buffer_availablelength(target) < strlen(opcodetext[opcode])) {
4524e2b1b9c0Schristos 		return (ISC_R_NOSPACE);
45259742fdb4Schristos 	}
4526e2b1b9c0Schristos 	isc_buffer_putstr(target, opcodetext[opcode]);
4527e2b1b9c0Schristos 	return (ISC_R_SUCCESS);
4528e2b1b9c0Schristos }
4529e2b1b9c0Schristos 
4530e2b1b9c0Schristos void
dns_message_logpacket(dns_message_t * message,const char * description,const isc_sockaddr_t * address,isc_logcategory_t * category,isc_logmodule_t * module,int level,isc_mem_t * mctx)45319742fdb4Schristos dns_message_logpacket(dns_message_t *message, const char *description,
45329742fdb4Schristos 		      const isc_sockaddr_t *address,
4533e2b1b9c0Schristos 		      isc_logcategory_t *category, isc_logmodule_t *module,
45349742fdb4Schristos 		      int level, isc_mem_t *mctx) {
4535e2b1b9c0Schristos 	REQUIRE(address != NULL);
4536e2b1b9c0Schristos 
4537e2b1b9c0Schristos 	logfmtpacket(message, description, address, category, module,
4538e2b1b9c0Schristos 		     &dns_master_style_debug, level, mctx);
4539e2b1b9c0Schristos }
4540e2b1b9c0Schristos 
4541e2b1b9c0Schristos void
dns_message_logfmtpacket(dns_message_t * message,const char * description,const isc_sockaddr_t * address,isc_logcategory_t * category,isc_logmodule_t * module,const dns_master_style_t * style,int level,isc_mem_t * mctx)45429742fdb4Schristos dns_message_logfmtpacket(dns_message_t *message, const char *description,
4543e2b1b9c0Schristos 			 const isc_sockaddr_t *address,
4544e2b1b9c0Schristos 			 isc_logcategory_t *category, isc_logmodule_t *module,
4545e2b1b9c0Schristos 			 const dns_master_style_t *style, int level,
45469742fdb4Schristos 			 isc_mem_t *mctx) {
4547e2b1b9c0Schristos 	REQUIRE(address != NULL);
4548e2b1b9c0Schristos 
4549e2b1b9c0Schristos 	logfmtpacket(message, description, address, category, module, style,
4550e2b1b9c0Schristos 		     level, mctx);
4551e2b1b9c0Schristos }
4552e2b1b9c0Schristos 
4553e2b1b9c0Schristos static void
logfmtpacket(dns_message_t * message,const char * description,const isc_sockaddr_t * address,isc_logcategory_t * category,isc_logmodule_t * module,const dns_master_style_t * style,int level,isc_mem_t * mctx)4554e2b1b9c0Schristos logfmtpacket(dns_message_t *message, const char *description,
4555e2b1b9c0Schristos 	     const isc_sockaddr_t *address, isc_logcategory_t *category,
4556e2b1b9c0Schristos 	     isc_logmodule_t *module, const dns_master_style_t *style,
45579742fdb4Schristos 	     int level, isc_mem_t *mctx) {
4558e2b1b9c0Schristos 	char addrbuf[ISC_SOCKADDR_FORMATSIZE] = { 0 };
4559e2b1b9c0Schristos 	const char *newline = "\n";
4560e2b1b9c0Schristos 	const char *space = " ";
4561e2b1b9c0Schristos 	isc_buffer_t buffer;
4562e2b1b9c0Schristos 	char *buf = NULL;
4563e2b1b9c0Schristos 	int len = 1024;
4564e2b1b9c0Schristos 	isc_result_t result;
4565e2b1b9c0Schristos 
45669742fdb4Schristos 	if (!isc_log_wouldlog(dns_lctx, level)) {
4567e2b1b9c0Schristos 		return;
45689742fdb4Schristos 	}
4569e2b1b9c0Schristos 
4570e2b1b9c0Schristos 	/*
4571e2b1b9c0Schristos 	 * Note that these are multiline debug messages.  We want a newline
4572e2b1b9c0Schristos 	 * to appear in the log after each message.
4573e2b1b9c0Schristos 	 */
4574e2b1b9c0Schristos 
45759742fdb4Schristos 	if (address != NULL) {
4576e2b1b9c0Schristos 		isc_sockaddr_format(address, addrbuf, sizeof(addrbuf));
45779742fdb4Schristos 	} else {
4578e2b1b9c0Schristos 		newline = space = "";
45799742fdb4Schristos 	}
4580e2b1b9c0Schristos 
4581e2b1b9c0Schristos 	do {
4582e2b1b9c0Schristos 		buf = isc_mem_get(mctx, len);
4583e2b1b9c0Schristos 		isc_buffer_init(&buffer, buf, len);
4584e2b1b9c0Schristos 		result = dns_message_totext(message, style, 0, &buffer);
4585e2b1b9c0Schristos 		if (result == ISC_R_NOSPACE) {
4586e2b1b9c0Schristos 			isc_mem_put(mctx, buf, len);
4587e2b1b9c0Schristos 			len += 1024;
45889742fdb4Schristos 		} else if (result == ISC_R_SUCCESS) {
4589e2b1b9c0Schristos 			isc_log_write(dns_lctx, category, module, level,
4590e2b1b9c0Schristos 				      "%s%s%s%s%.*s", description, space,
4591e2b1b9c0Schristos 				      addrbuf, newline,
45929742fdb4Schristos 				      (int)isc_buffer_usedlength(&buffer), buf);
45939742fdb4Schristos 		}
4594e2b1b9c0Schristos 	} while (result == ISC_R_NOSPACE);
4595e2b1b9c0Schristos 
45969742fdb4Schristos 	if (buf != NULL) {
4597e2b1b9c0Schristos 		isc_mem_put(mctx, buf, len);
4598e2b1b9c0Schristos 	}
45999742fdb4Schristos }
4600e2b1b9c0Schristos 
4601e2b1b9c0Schristos isc_result_t
dns_message_buildopt(dns_message_t * message,dns_rdataset_t ** rdatasetp,unsigned int version,uint16_t udpsize,unsigned int flags,dns_ednsopt_t * ednsopts,size_t count)4602e2b1b9c0Schristos dns_message_buildopt(dns_message_t *message, dns_rdataset_t **rdatasetp,
46039742fdb4Schristos 		     unsigned int version, uint16_t udpsize, unsigned int flags,
46049742fdb4Schristos 		     dns_ednsopt_t *ednsopts, size_t count) {
4605e2b1b9c0Schristos 	dns_rdataset_t *rdataset = NULL;
4606e2b1b9c0Schristos 	dns_rdatalist_t *rdatalist = NULL;
4607e2b1b9c0Schristos 	dns_rdata_t *rdata = NULL;
4608e2b1b9c0Schristos 	isc_result_t result;
4609e2b1b9c0Schristos 	unsigned int len = 0, i;
4610e2b1b9c0Schristos 
4611e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(message));
4612e2b1b9c0Schristos 	REQUIRE(rdatasetp != NULL && *rdatasetp == NULL);
4613e2b1b9c0Schristos 
4614e2b1b9c0Schristos 	result = dns_message_gettemprdatalist(message, &rdatalist);
46159742fdb4Schristos 	if (result != ISC_R_SUCCESS) {
4616e2b1b9c0Schristos 		return (result);
46179742fdb4Schristos 	}
4618e2b1b9c0Schristos 	result = dns_message_gettemprdata(message, &rdata);
46199742fdb4Schristos 	if (result != ISC_R_SUCCESS) {
4620e2b1b9c0Schristos 		goto cleanup;
46219742fdb4Schristos 	}
4622e2b1b9c0Schristos 	result = dns_message_gettemprdataset(message, &rdataset);
46239742fdb4Schristos 	if (result != ISC_R_SUCCESS) {
4624e2b1b9c0Schristos 		goto cleanup;
46259742fdb4Schristos 	}
4626e2b1b9c0Schristos 
4627e2b1b9c0Schristos 	rdatalist->type = dns_rdatatype_opt;
4628e2b1b9c0Schristos 
4629e2b1b9c0Schristos 	/*
4630e2b1b9c0Schristos 	 * Set Maximum UDP buffer size.
4631e2b1b9c0Schristos 	 */
4632e2b1b9c0Schristos 	rdatalist->rdclass = udpsize;
4633e2b1b9c0Schristos 
4634e2b1b9c0Schristos 	/*
4635e2b1b9c0Schristos 	 * Set EXTENDED-RCODE and Z to 0.
4636e2b1b9c0Schristos 	 */
4637e2b1b9c0Schristos 	rdatalist->ttl = (version << 16);
4638e2b1b9c0Schristos 	rdatalist->ttl |= (flags & 0xffff);
4639e2b1b9c0Schristos 
4640e2b1b9c0Schristos 	/*
4641e2b1b9c0Schristos 	 * Set EDNS options if applicable
4642e2b1b9c0Schristos 	 */
4643e2b1b9c0Schristos 	if (count != 0U) {
4644e2b1b9c0Schristos 		isc_buffer_t *buf = NULL;
4645f2e20987Schristos 		bool seenpad = false;
46469742fdb4Schristos 		for (i = 0; i < count; i++) {
4647e2b1b9c0Schristos 			len += ednsopts[i].length + 4;
46489742fdb4Schristos 		}
4649e2b1b9c0Schristos 
4650e2b1b9c0Schristos 		if (len > 0xffffU) {
4651e2b1b9c0Schristos 			result = ISC_R_NOSPACE;
4652e2b1b9c0Schristos 			goto cleanup;
4653e2b1b9c0Schristos 		}
4654e2b1b9c0Schristos 
46559742fdb4Schristos 		isc_buffer_allocate(message->mctx, &buf, len);
4656e2b1b9c0Schristos 
4657e2b1b9c0Schristos 		for (i = 0; i < count; i++) {
4658e2b1b9c0Schristos 			if (ednsopts[i].code == DNS_OPT_PAD &&
4659*4ac1c27eSchristos 			    ednsopts[i].length == 0U && !seenpad)
4660*4ac1c27eSchristos 			{
4661f2e20987Schristos 				seenpad = true;
4662e2b1b9c0Schristos 				continue;
4663e2b1b9c0Schristos 			}
4664e2b1b9c0Schristos 			isc_buffer_putuint16(buf, ednsopts[i].code);
4665e2b1b9c0Schristos 			isc_buffer_putuint16(buf, ednsopts[i].length);
4666e2b1b9c0Schristos 			if (ednsopts[i].length != 0) {
4667e2b1b9c0Schristos 				isc_buffer_putmem(buf, ednsopts[i].value,
4668e2b1b9c0Schristos 						  ednsopts[i].length);
4669e2b1b9c0Schristos 			}
4670e2b1b9c0Schristos 		}
4671e2b1b9c0Schristos 
4672e2b1b9c0Schristos 		/* Padding must be the final option */
4673e2b1b9c0Schristos 		if (seenpad) {
4674e2b1b9c0Schristos 			isc_buffer_putuint16(buf, DNS_OPT_PAD);
4675e2b1b9c0Schristos 			isc_buffer_putuint16(buf, 0);
4676e2b1b9c0Schristos 		}
4677e2b1b9c0Schristos 		rdata->data = isc_buffer_base(buf);
4678e2b1b9c0Schristos 		rdata->length = len;
4679e2b1b9c0Schristos 		dns_message_takebuffer(message, &buf);
46809742fdb4Schristos 		if (seenpad) {
4681e2b1b9c0Schristos 			message->padding_off = len;
46829742fdb4Schristos 		}
4683e2b1b9c0Schristos 	} else {
4684e2b1b9c0Schristos 		rdata->data = NULL;
4685e2b1b9c0Schristos 		rdata->length = 0;
4686e2b1b9c0Schristos 	}
4687e2b1b9c0Schristos 
4688e2b1b9c0Schristos 	rdata->rdclass = rdatalist->rdclass;
4689e2b1b9c0Schristos 	rdata->type = rdatalist->type;
4690e2b1b9c0Schristos 	rdata->flags = 0;
4691e2b1b9c0Schristos 
4692e2b1b9c0Schristos 	ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
4693e2b1b9c0Schristos 	result = dns_rdatalist_tordataset(rdatalist, rdataset);
4694e2b1b9c0Schristos 	RUNTIME_CHECK(result == ISC_R_SUCCESS);
4695e2b1b9c0Schristos 
4696e2b1b9c0Schristos 	*rdatasetp = rdataset;
4697e2b1b9c0Schristos 	return (ISC_R_SUCCESS);
4698e2b1b9c0Schristos 
4699e2b1b9c0Schristos cleanup:
47009742fdb4Schristos 	if (rdata != NULL) {
4701e2b1b9c0Schristos 		dns_message_puttemprdata(message, &rdata);
47029742fdb4Schristos 	}
47039742fdb4Schristos 	if (rdataset != NULL) {
4704e2b1b9c0Schristos 		dns_message_puttemprdataset(message, &rdataset);
47059742fdb4Schristos 	}
47069742fdb4Schristos 	if (rdatalist != NULL) {
4707e2b1b9c0Schristos 		dns_message_puttemprdatalist(message, &rdatalist);
47089742fdb4Schristos 	}
4709e2b1b9c0Schristos 	return (result);
4710e2b1b9c0Schristos }
4711e2b1b9c0Schristos 
4712e2b1b9c0Schristos void
dns_message_setclass(dns_message_t * msg,dns_rdataclass_t rdclass)4713e2b1b9c0Schristos dns_message_setclass(dns_message_t *msg, dns_rdataclass_t rdclass) {
4714e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
4715e2b1b9c0Schristos 	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTPARSE);
4716e2b1b9c0Schristos 	REQUIRE(msg->state == DNS_SECTION_ANY);
4717e2b1b9c0Schristos 	REQUIRE(msg->rdclass_set == 0);
4718e2b1b9c0Schristos 
4719e2b1b9c0Schristos 	msg->rdclass = rdclass;
4720e2b1b9c0Schristos 	msg->rdclass_set = 1;
4721e2b1b9c0Schristos }
4722e2b1b9c0Schristos 
4723e2b1b9c0Schristos void
dns_message_setpadding(dns_message_t * msg,uint16_t padding)4724f2e20987Schristos dns_message_setpadding(dns_message_t *msg, uint16_t padding) {
4725e2b1b9c0Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
4726e2b1b9c0Schristos 
4727e2b1b9c0Schristos 	/* Avoid silly large padding */
47289742fdb4Schristos 	if (padding > 512) {
4729e2b1b9c0Schristos 		padding = 512;
47309742fdb4Schristos 	}
4731e2b1b9c0Schristos 	msg->padding = padding;
4732e2b1b9c0Schristos }
473373584a28Schristos 
473473584a28Schristos void
dns_message_clonebuffer(dns_message_t * msg)473573584a28Schristos dns_message_clonebuffer(dns_message_t *msg) {
473673584a28Schristos 	REQUIRE(DNS_MESSAGE_VALID(msg));
473773584a28Schristos 
473873584a28Schristos 	if (msg->free_saved == 0 && msg->saved.base != NULL) {
473973584a28Schristos 		msg->saved.base =
474073584a28Schristos 			memmove(isc_mem_get(msg->mctx, msg->saved.length),
474173584a28Schristos 				msg->saved.base, msg->saved.length);
474273584a28Schristos 		msg->free_saved = 1;
474373584a28Schristos 	}
474473584a28Schristos 	if (msg->free_query == 0 && msg->query.base != NULL) {
474573584a28Schristos 		msg->query.base =
474673584a28Schristos 			memmove(isc_mem_get(msg->mctx, msg->query.length),
474773584a28Schristos 				msg->query.base, msg->query.length);
474873584a28Schristos 		msg->free_query = 1;
474973584a28Schristos 	}
475073584a28Schristos }
4751