message.c revision 234010
1135446Strhodes/*
2234010Sdougb * Copyright (C) 2004-2012  Internet Systems Consortium, Inc. ("ISC")
3135446Strhodes * Copyright (C) 1999-2003  Internet Software Consortium.
4135446Strhodes *
5174187Sdougb * Permission to use, copy, modify, and/or distribute this software for any
6135446Strhodes * purpose with or without fee is hereby granted, provided that the above
7135446Strhodes * copyright notice and this permission notice appear in all copies.
8135446Strhodes *
9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11135446Strhodes * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15135446Strhodes * PERFORMANCE OF THIS SOFTWARE.
16135446Strhodes */
17135446Strhodes
18234010Sdougb/* $Id$ */
19135446Strhodes
20170222Sdougb/*! \file */
21170222Sdougb
22135446Strhodes/***
23135446Strhodes *** Imports
24135446Strhodes ***/
25135446Strhodes
26135446Strhodes#include <config.h>
27193149Sdougb#include <ctype.h>
28135446Strhodes
29135446Strhodes#include <isc/buffer.h>
30135446Strhodes#include <isc/mem.h>
31135446Strhodes#include <isc/print.h>
32135446Strhodes#include <isc/string.h>		/* Required for HP/UX (and others?) */
33135446Strhodes#include <isc/util.h>
34135446Strhodes
35135446Strhodes#include <dns/dnssec.h>
36135446Strhodes#include <dns/keyvalues.h>
37135446Strhodes#include <dns/log.h>
38135446Strhodes#include <dns/masterdump.h>
39135446Strhodes#include <dns/message.h>
40135446Strhodes#include <dns/opcode.h>
41135446Strhodes#include <dns/rdata.h>
42135446Strhodes#include <dns/rdatalist.h>
43135446Strhodes#include <dns/rdataset.h>
44135446Strhodes#include <dns/rdatastruct.h>
45135446Strhodes#include <dns/result.h>
46135446Strhodes#include <dns/tsig.h>
47135446Strhodes#include <dns/view.h>
48135446Strhodes
49193149Sdougb#ifdef SKAN_MSG_DEBUG
50193149Sdougbstatic void
51193149Sdougbhexdump(const char *msg, const char *msg2, void *base, size_t len) {
52193149Sdougb	unsigned char *p;
53193149Sdougb	unsigned int cnt;
54193149Sdougb
55193149Sdougb	p = base;
56193149Sdougb	cnt = 0;
57193149Sdougb
58193149Sdougb	printf("*** %s [%s] (%u bytes @ %p)\n", msg, msg2, len, base);
59193149Sdougb
60193149Sdougb	while (cnt < len) {
61193149Sdougb		if (cnt % 16 == 0)
62193149Sdougb			printf("%p: ", p);
63193149Sdougb		else if (cnt % 8 == 0)
64193149Sdougb			printf(" |");
65193149Sdougb		printf(" %02x %c", *p, (isprint(*p) ? *p : ' '));
66193149Sdougb		p++;
67193149Sdougb		cnt++;
68193149Sdougb
69193149Sdougb		if (cnt % 16 == 0)
70193149Sdougb			printf("\n");
71193149Sdougb	}
72193149Sdougb
73193149Sdougb	if (cnt % 16 != 0)
74193149Sdougb		printf("\n");
75193149Sdougb}
76193149Sdougb#endif
77193149Sdougb
78135446Strhodes#define DNS_MESSAGE_OPCODE_MASK		0x7800U
79135446Strhodes#define DNS_MESSAGE_OPCODE_SHIFT	11
80135446Strhodes#define DNS_MESSAGE_RCODE_MASK		0x000fU
81135446Strhodes#define DNS_MESSAGE_FLAG_MASK		0x8ff0U
82135446Strhodes#define DNS_MESSAGE_EDNSRCODE_MASK	0xff000000U
83135446Strhodes#define DNS_MESSAGE_EDNSRCODE_SHIFT	24
84135446Strhodes#define DNS_MESSAGE_EDNSVERSION_MASK	0x00ff0000U
85135446Strhodes#define DNS_MESSAGE_EDNSVERSION_SHIFT	16
86135446Strhodes
87135446Strhodes#define VALID_NAMED_SECTION(s)  (((s) > DNS_SECTION_ANY) \
88135446Strhodes				 && ((s) < DNS_SECTION_MAX))
89135446Strhodes#define VALID_SECTION(s)	(((s) >= DNS_SECTION_ANY) \
90135446Strhodes				 && ((s) < DNS_SECTION_MAX))
91135446Strhodes#define ADD_STRING(b, s)	{if (strlen(s) >= \
92135446Strhodes				   isc_buffer_availablelength(b)) \
93135446Strhodes				       return(ISC_R_NOSPACE); else \
94135446Strhodes				       isc_buffer_putstr(b, s);}
95135446Strhodes#define VALID_PSEUDOSECTION(s)	(((s) >= DNS_PSEUDOSECTION_ANY) \
96135446Strhodes				 && ((s) < DNS_PSEUDOSECTION_MAX))
97135446Strhodes
98193149Sdougb#define OPTOUT(x) (((x)->attributes & DNS_RDATASETATTR_OPTOUT) != 0)
99193149Sdougb
100170222Sdougb/*%
101135446Strhodes * This is the size of each individual scratchpad buffer, and the numbers
102135446Strhodes * of various block allocations used within the server.
103135446Strhodes * XXXMLG These should come from a config setting.
104135446Strhodes */
105135446Strhodes#define SCRATCHPAD_SIZE		512
106135446Strhodes#define NAME_COUNT		  8
107135446Strhodes#define OFFSET_COUNT		  4
108135446Strhodes#define RDATA_COUNT		  8
109135446Strhodes#define RDATALIST_COUNT		  8
110135446Strhodes#define RDATASET_COUNT		 RDATALIST_COUNT
111135446Strhodes
112170222Sdougb/*%
113135446Strhodes * Text representation of the different items, for message_totext
114135446Strhodes * functions.
115135446Strhodes */
116135446Strhodesstatic const char *sectiontext[] = {
117135446Strhodes	"QUESTION",
118135446Strhodes	"ANSWER",
119135446Strhodes	"AUTHORITY",
120135446Strhodes	"ADDITIONAL"
121135446Strhodes};
122135446Strhodes
123135446Strhodesstatic const char *updsectiontext[] = {
124135446Strhodes	"ZONE",
125135446Strhodes	"PREREQUISITE",
126135446Strhodes	"UPDATE",
127135446Strhodes	"ADDITIONAL"
128135446Strhodes};
129135446Strhodes
130135446Strhodesstatic const char *opcodetext[] = {
131135446Strhodes	"QUERY",
132135446Strhodes	"IQUERY",
133135446Strhodes	"STATUS",
134135446Strhodes	"RESERVED3",
135135446Strhodes	"NOTIFY",
136135446Strhodes	"UPDATE",
137135446Strhodes	"RESERVED6",
138135446Strhodes	"RESERVED7",
139135446Strhodes	"RESERVED8",
140135446Strhodes	"RESERVED9",
141135446Strhodes	"RESERVED10",
142135446Strhodes	"RESERVED11",
143135446Strhodes	"RESERVED12",
144135446Strhodes	"RESERVED13",
145135446Strhodes	"RESERVED14",
146135446Strhodes	"RESERVED15"
147135446Strhodes};
148135446Strhodes
149135446Strhodesstatic const char *rcodetext[] = {
150135446Strhodes	"NOERROR",
151135446Strhodes	"FORMERR",
152135446Strhodes	"SERVFAIL",
153135446Strhodes	"NXDOMAIN",
154135446Strhodes	"NOTIMP",
155135446Strhodes	"REFUSED",
156135446Strhodes	"YXDOMAIN",
157135446Strhodes	"YXRRSET",
158135446Strhodes	"NXRRSET",
159135446Strhodes	"NOTAUTH",
160135446Strhodes	"NOTZONE",
161135446Strhodes	"RESERVED11",
162135446Strhodes	"RESERVED12",
163135446Strhodes	"RESERVED13",
164135446Strhodes	"RESERVED14",
165135446Strhodes	"RESERVED15",
166135446Strhodes	"BADVERS"
167135446Strhodes};
168135446Strhodes
169135446Strhodes
170170222Sdougb/*%
171135446Strhodes * "helper" type, which consists of a block of some type, and is linkable.
172135446Strhodes * For it to work, sizeof(dns_msgblock_t) must be a multiple of the pointer
173193149Sdougb * size, or the allocated elements will not be aligned correctly.
174135446Strhodes */
175135446Strhodesstruct dns_msgblock {
176135446Strhodes	unsigned int			count;
177135446Strhodes	unsigned int			remaining;
178135446Strhodes	ISC_LINK(dns_msgblock_t)	link;
179135446Strhodes}; /* dynamically sized */
180135446Strhodes
181135446Strhodesstatic inline dns_msgblock_t *
182135446Strhodesmsgblock_allocate(isc_mem_t *, unsigned int, unsigned int);
183135446Strhodes
184135446Strhodes#define msgblock_get(block, type) \
185135446Strhodes	((type *)msgblock_internalget(block, sizeof(type)))
186135446Strhodes
187135446Strhodesstatic inline void *
188135446Strhodesmsgblock_internalget(dns_msgblock_t *, unsigned int);
189135446Strhodes
190135446Strhodesstatic inline void
191135446Strhodesmsgblock_reset(dns_msgblock_t *);
192135446Strhodes
193135446Strhodesstatic inline void
194135446Strhodesmsgblock_free(isc_mem_t *, dns_msgblock_t *, unsigned int);
195135446Strhodes
196135446Strhodes/*
197135446Strhodes * Allocate a new dns_msgblock_t, and return a pointer to it.  If no memory
198135446Strhodes * is free, return NULL.
199135446Strhodes */
200135446Strhodesstatic inline dns_msgblock_t *
201135446Strhodesmsgblock_allocate(isc_mem_t *mctx, unsigned int sizeof_type,
202135446Strhodes		  unsigned int count)
203135446Strhodes{
204135446Strhodes	dns_msgblock_t *block;
205135446Strhodes	unsigned int length;
206135446Strhodes
207135446Strhodes	length = sizeof(dns_msgblock_t) + (sizeof_type * count);
208135446Strhodes
209135446Strhodes	block = isc_mem_get(mctx, length);
210135446Strhodes	if (block == NULL)
211135446Strhodes		return (NULL);
212135446Strhodes
213135446Strhodes	block->count = count;
214135446Strhodes	block->remaining = count;
215135446Strhodes
216135446Strhodes	ISC_LINK_INIT(block, link);
217135446Strhodes
218135446Strhodes	return (block);
219135446Strhodes}
220135446Strhodes
221135446Strhodes/*
222135446Strhodes * Return an element from the msgblock.  If no more are available, return
223135446Strhodes * NULL.
224135446Strhodes */
225135446Strhodesstatic inline void *
226135446Strhodesmsgblock_internalget(dns_msgblock_t *block, unsigned int sizeof_type) {
227135446Strhodes	void *ptr;
228135446Strhodes
229135446Strhodes	if (block == NULL || block->remaining == 0)
230135446Strhodes		return (NULL);
231135446Strhodes
232135446Strhodes	block->remaining--;
233135446Strhodes
234135446Strhodes	ptr = (((unsigned char *)block)
235135446Strhodes	       + sizeof(dns_msgblock_t)
236135446Strhodes	       + (sizeof_type * block->remaining));
237135446Strhodes
238135446Strhodes	return (ptr);
239135446Strhodes}
240135446Strhodes
241135446Strhodesstatic inline void
242135446Strhodesmsgblock_reset(dns_msgblock_t *block) {
243135446Strhodes	block->remaining = block->count;
244135446Strhodes}
245135446Strhodes
246135446Strhodes/*
247135446Strhodes * Release memory associated with a message block.
248135446Strhodes */
249135446Strhodesstatic inline void
250135446Strhodesmsgblock_free(isc_mem_t *mctx, dns_msgblock_t *block, unsigned int sizeof_type)
251135446Strhodes{
252135446Strhodes	unsigned int length;
253135446Strhodes
254135446Strhodes	length = sizeof(dns_msgblock_t) + (sizeof_type * block->count);
255135446Strhodes
256135446Strhodes	isc_mem_put(mctx, block, length);
257135446Strhodes}
258135446Strhodes
259135446Strhodes/*
260135446Strhodes * Allocate a new dynamic buffer, and attach it to this message as the
261135446Strhodes * "current" buffer.  (which is always the last on the list, for our
262135446Strhodes * uses)
263135446Strhodes */
264135446Strhodesstatic inline isc_result_t
265135446Strhodesnewbuffer(dns_message_t *msg, unsigned int size) {
266135446Strhodes	isc_result_t result;
267135446Strhodes	isc_buffer_t *dynbuf;
268135446Strhodes
269135446Strhodes	dynbuf = NULL;
270135446Strhodes	result = isc_buffer_allocate(msg->mctx, &dynbuf, size);
271135446Strhodes	if (result != ISC_R_SUCCESS)
272135446Strhodes		return (ISC_R_NOMEMORY);
273135446Strhodes
274135446Strhodes	ISC_LIST_APPEND(msg->scratchpad, dynbuf, link);
275135446Strhodes	return (ISC_R_SUCCESS);
276135446Strhodes}
277135446Strhodes
278135446Strhodesstatic inline isc_buffer_t *
279135446Strhodescurrentbuffer(dns_message_t *msg) {
280135446Strhodes	isc_buffer_t *dynbuf;
281135446Strhodes
282135446Strhodes	dynbuf = ISC_LIST_TAIL(msg->scratchpad);
283135446Strhodes	INSIST(dynbuf != NULL);
284135446Strhodes
285135446Strhodes	return (dynbuf);
286135446Strhodes}
287135446Strhodes
288135446Strhodesstatic inline void
289135446Strhodesreleaserdata(dns_message_t *msg, dns_rdata_t *rdata) {
290135446Strhodes	ISC_LIST_PREPEND(msg->freerdata, rdata, link);
291135446Strhodes}
292135446Strhodes
293135446Strhodesstatic inline dns_rdata_t *
294135446Strhodesnewrdata(dns_message_t *msg) {
295135446Strhodes	dns_msgblock_t *msgblock;
296135446Strhodes	dns_rdata_t *rdata;
297135446Strhodes
298135446Strhodes	rdata = ISC_LIST_HEAD(msg->freerdata);
299135446Strhodes	if (rdata != NULL) {
300135446Strhodes		ISC_LIST_UNLINK(msg->freerdata, rdata, link);
301135446Strhodes		return (rdata);
302135446Strhodes	}
303135446Strhodes
304135446Strhodes	msgblock = ISC_LIST_TAIL(msg->rdatas);
305135446Strhodes	rdata = msgblock_get(msgblock, dns_rdata_t);
306135446Strhodes	if (rdata == NULL) {
307135446Strhodes		msgblock = msgblock_allocate(msg->mctx, sizeof(dns_rdata_t),
308135446Strhodes					     RDATA_COUNT);
309135446Strhodes		if (msgblock == NULL)
310135446Strhodes			return (NULL);
311135446Strhodes
312135446Strhodes		ISC_LIST_APPEND(msg->rdatas, msgblock, link);
313135446Strhodes
314135446Strhodes		rdata = msgblock_get(msgblock, dns_rdata_t);
315135446Strhodes	}
316135446Strhodes
317135446Strhodes	dns_rdata_init(rdata);
318135446Strhodes	return (rdata);
319135446Strhodes}
320135446Strhodes
321135446Strhodesstatic inline void
322135446Strhodesreleaserdatalist(dns_message_t *msg, dns_rdatalist_t *rdatalist) {
323135446Strhodes	ISC_LIST_PREPEND(msg->freerdatalist, rdatalist, link);
324135446Strhodes}
325135446Strhodes
326135446Strhodesstatic inline dns_rdatalist_t *
327135446Strhodesnewrdatalist(dns_message_t *msg) {
328135446Strhodes	dns_msgblock_t *msgblock;
329135446Strhodes	dns_rdatalist_t *rdatalist;
330135446Strhodes
331135446Strhodes	rdatalist = ISC_LIST_HEAD(msg->freerdatalist);
332135446Strhodes	if (rdatalist != NULL) {
333135446Strhodes		ISC_LIST_UNLINK(msg->freerdatalist, rdatalist, link);
334135446Strhodes		return (rdatalist);
335135446Strhodes	}
336135446Strhodes
337135446Strhodes	msgblock = ISC_LIST_TAIL(msg->rdatalists);
338135446Strhodes	rdatalist = msgblock_get(msgblock, dns_rdatalist_t);
339135446Strhodes	if (rdatalist == NULL) {
340135446Strhodes		msgblock = msgblock_allocate(msg->mctx,
341135446Strhodes					     sizeof(dns_rdatalist_t),
342135446Strhodes					     RDATALIST_COUNT);
343135446Strhodes		if (msgblock == NULL)
344135446Strhodes			return (NULL);
345135446Strhodes
346135446Strhodes		ISC_LIST_APPEND(msg->rdatalists, msgblock, link);
347135446Strhodes
348135446Strhodes		rdatalist = msgblock_get(msgblock, dns_rdatalist_t);
349135446Strhodes	}
350135446Strhodes
351135446Strhodes	return (rdatalist);
352135446Strhodes}
353135446Strhodes
354135446Strhodesstatic inline dns_offsets_t *
355135446Strhodesnewoffsets(dns_message_t *msg) {
356135446Strhodes	dns_msgblock_t *msgblock;
357135446Strhodes	dns_offsets_t *offsets;
358135446Strhodes
359135446Strhodes	msgblock = ISC_LIST_TAIL(msg->offsets);
360135446Strhodes	offsets = msgblock_get(msgblock, dns_offsets_t);
361135446Strhodes	if (offsets == NULL) {
362135446Strhodes		msgblock = msgblock_allocate(msg->mctx,
363135446Strhodes					     sizeof(dns_offsets_t),
364135446Strhodes					     OFFSET_COUNT);
365135446Strhodes		if (msgblock == NULL)
366135446Strhodes			return (NULL);
367135446Strhodes
368135446Strhodes		ISC_LIST_APPEND(msg->offsets, msgblock, link);
369135446Strhodes
370135446Strhodes		offsets = msgblock_get(msgblock, dns_offsets_t);
371135446Strhodes	}
372135446Strhodes
373135446Strhodes	return (offsets);
374135446Strhodes}
375135446Strhodes
376135446Strhodesstatic inline void
377135446Strhodesmsginitheader(dns_message_t *m) {
378135446Strhodes	m->id = 0;
379135446Strhodes	m->flags = 0;
380135446Strhodes	m->rcode = 0;
381135446Strhodes	m->opcode = 0;
382135446Strhodes	m->rdclass = 0;
383135446Strhodes}
384135446Strhodes
385135446Strhodesstatic inline void
386135446Strhodesmsginitprivate(dns_message_t *m) {
387135446Strhodes	unsigned int i;
388135446Strhodes
389135446Strhodes	for (i = 0; i < DNS_SECTION_MAX; i++) {
390135446Strhodes		m->cursors[i] = NULL;
391135446Strhodes		m->counts[i] = 0;
392135446Strhodes	}
393135446Strhodes	m->opt = NULL;
394135446Strhodes	m->sig0 = NULL;
395135446Strhodes	m->sig0name = NULL;
396135446Strhodes	m->tsig = NULL;
397135446Strhodes	m->tsigname = NULL;
398135446Strhodes	m->state = DNS_SECTION_ANY;  /* indicate nothing parsed or rendered */
399135446Strhodes	m->opt_reserved = 0;
400135446Strhodes	m->sig_reserved = 0;
401135446Strhodes	m->reserved = 0;
402135446Strhodes	m->buffer = NULL;
403135446Strhodes}
404135446Strhodes
405135446Strhodesstatic inline void
406135446Strhodesmsginittsig(dns_message_t *m) {
407135446Strhodes	m->tsigstatus = dns_rcode_noerror;
408135446Strhodes	m->querytsigstatus = dns_rcode_noerror;
409135446Strhodes	m->tsigkey = NULL;
410135446Strhodes	m->tsigctx = NULL;
411135446Strhodes	m->sigstart = -1;
412135446Strhodes	m->sig0key = NULL;
413135446Strhodes	m->sig0status = dns_rcode_noerror;
414135446Strhodes	m->timeadjust = 0;
415135446Strhodes}
416135446Strhodes
417135446Strhodes/*
418135446Strhodes * Init elements to default state.  Used both when allocating a new element
419135446Strhodes * and when resetting one.
420135446Strhodes */
421135446Strhodesstatic inline void
422135446Strhodesmsginit(dns_message_t *m) {
423135446Strhodes	msginitheader(m);
424135446Strhodes	msginitprivate(m);
425135446Strhodes	msginittsig(m);
426135446Strhodes	m->header_ok = 0;
427135446Strhodes	m->question_ok = 0;
428135446Strhodes	m->tcp_continuation = 0;
429135446Strhodes	m->verified_sig = 0;
430135446Strhodes	m->verify_attempted = 0;
431135446Strhodes	m->order = NULL;
432135446Strhodes	m->order_arg = NULL;
433135446Strhodes	m->query.base = NULL;
434135446Strhodes	m->query.length = 0;
435135446Strhodes	m->free_query = 0;
436135446Strhodes	m->saved.base = NULL;
437135446Strhodes	m->saved.length = 0;
438135446Strhodes	m->free_saved = 0;
439135446Strhodes	m->querytsig = NULL;
440135446Strhodes}
441135446Strhodes
442135446Strhodesstatic inline void
443135446Strhodesmsgresetnames(dns_message_t *msg, unsigned int first_section) {
444135446Strhodes	unsigned int i;
445135446Strhodes	dns_name_t *name, *next_name;
446135446Strhodes	dns_rdataset_t *rds, *next_rds;
447135446Strhodes
448135446Strhodes	/*
449135446Strhodes	 * Clean up name lists by calling the rdataset disassociate function.
450135446Strhodes	 */
451135446Strhodes	for (i = first_section; i < DNS_SECTION_MAX; i++) {
452135446Strhodes		name = ISC_LIST_HEAD(msg->sections[i]);
453135446Strhodes		while (name != NULL) {
454135446Strhodes			next_name = ISC_LIST_NEXT(name, link);
455135446Strhodes			ISC_LIST_UNLINK(msg->sections[i], name, link);
456135446Strhodes
457135446Strhodes			rds = ISC_LIST_HEAD(name->list);
458135446Strhodes			while (rds != NULL) {
459135446Strhodes				next_rds = ISC_LIST_NEXT(rds, link);
460135446Strhodes				ISC_LIST_UNLINK(name->list, rds, link);
461135446Strhodes
462135446Strhodes				INSIST(dns_rdataset_isassociated(rds));
463135446Strhodes				dns_rdataset_disassociate(rds);
464135446Strhodes				isc_mempool_put(msg->rdspool, rds);
465135446Strhodes				rds = next_rds;
466135446Strhodes			}
467135446Strhodes			if (dns_name_dynamic(name))
468135446Strhodes				dns_name_free(name, msg->mctx);
469135446Strhodes			isc_mempool_put(msg->namepool, name);
470135446Strhodes			name = next_name;
471135446Strhodes		}
472135446Strhodes	}
473135446Strhodes}
474135446Strhodes
475135446Strhodesstatic void
476135446Strhodesmsgresetopt(dns_message_t *msg)
477135446Strhodes{
478135446Strhodes	if (msg->opt != NULL) {
479135446Strhodes		if (msg->opt_reserved > 0) {
480135446Strhodes			dns_message_renderrelease(msg, msg->opt_reserved);
481135446Strhodes			msg->opt_reserved = 0;
482135446Strhodes		}
483135446Strhodes		INSIST(dns_rdataset_isassociated(msg->opt));
484135446Strhodes		dns_rdataset_disassociate(msg->opt);
485135446Strhodes		isc_mempool_put(msg->rdspool, msg->opt);
486135446Strhodes		msg->opt = NULL;
487135446Strhodes	}
488135446Strhodes}
489135446Strhodes
490135446Strhodesstatic void
491135446Strhodesmsgresetsigs(dns_message_t *msg, isc_boolean_t replying) {
492135446Strhodes	if (msg->sig_reserved > 0) {
493135446Strhodes		dns_message_renderrelease(msg, msg->sig_reserved);
494135446Strhodes		msg->sig_reserved = 0;
495135446Strhodes	}
496135446Strhodes	if (msg->tsig != NULL) {
497135446Strhodes		INSIST(dns_rdataset_isassociated(msg->tsig));
498135446Strhodes		INSIST(msg->namepool != NULL);
499135446Strhodes		if (replying) {
500135446Strhodes			INSIST(msg->querytsig == NULL);
501135446Strhodes			msg->querytsig = msg->tsig;
502135446Strhodes		} else {
503135446Strhodes			dns_rdataset_disassociate(msg->tsig);
504135446Strhodes			isc_mempool_put(msg->rdspool, msg->tsig);
505135446Strhodes			if (msg->querytsig != NULL) {
506135446Strhodes				dns_rdataset_disassociate(msg->querytsig);
507135446Strhodes				isc_mempool_put(msg->rdspool, msg->querytsig);
508135446Strhodes			}
509135446Strhodes		}
510135446Strhodes		if (dns_name_dynamic(msg->tsigname))
511135446Strhodes			dns_name_free(msg->tsigname, msg->mctx);
512135446Strhodes		isc_mempool_put(msg->namepool, msg->tsigname);
513135446Strhodes		msg->tsig = NULL;
514135446Strhodes		msg->tsigname = NULL;
515135446Strhodes	} else if (msg->querytsig != NULL && !replying) {
516135446Strhodes		dns_rdataset_disassociate(msg->querytsig);
517135446Strhodes		isc_mempool_put(msg->rdspool, msg->querytsig);
518135446Strhodes		msg->querytsig = NULL;
519135446Strhodes	}
520135446Strhodes	if (msg->sig0 != NULL) {
521135446Strhodes		INSIST(dns_rdataset_isassociated(msg->sig0));
522135446Strhodes		dns_rdataset_disassociate(msg->sig0);
523135446Strhodes		isc_mempool_put(msg->rdspool, msg->sig0);
524135446Strhodes		if (msg->sig0name != NULL) {
525135446Strhodes			if (dns_name_dynamic(msg->sig0name))
526135446Strhodes				dns_name_free(msg->sig0name, msg->mctx);
527135446Strhodes			isc_mempool_put(msg->namepool, msg->sig0name);
528135446Strhodes		}
529135446Strhodes		msg->sig0 = NULL;
530135446Strhodes		msg->sig0name = NULL;
531135446Strhodes	}
532135446Strhodes}
533135446Strhodes
534135446Strhodes/*
535135446Strhodes * Free all but one (or everything) for this message.  This is used by
536135446Strhodes * both dns_message_reset() and dns_message_destroy().
537135446Strhodes */
538135446Strhodesstatic void
539135446Strhodesmsgreset(dns_message_t *msg, isc_boolean_t everything) {
540135446Strhodes	dns_msgblock_t *msgblock, *next_msgblock;
541135446Strhodes	isc_buffer_t *dynbuf, *next_dynbuf;
542135446Strhodes	dns_rdata_t *rdata;
543135446Strhodes	dns_rdatalist_t *rdatalist;
544135446Strhodes
545135446Strhodes	msgresetnames(msg, 0);
546135446Strhodes	msgresetopt(msg);
547135446Strhodes	msgresetsigs(msg, ISC_FALSE);
548135446Strhodes
549135446Strhodes	/*
550135446Strhodes	 * Clean up linked lists.
551135446Strhodes	 */
552135446Strhodes
553135446Strhodes	/*
554135446Strhodes	 * Run through the free lists, and just unlink anything found there.
555135446Strhodes	 * The memory isn't lost since these are part of message blocks we
556135446Strhodes	 * have allocated.
557135446Strhodes	 */
558135446Strhodes	rdata = ISC_LIST_HEAD(msg->freerdata);
559135446Strhodes	while (rdata != NULL) {
560135446Strhodes		ISC_LIST_UNLINK(msg->freerdata, rdata, link);
561135446Strhodes		rdata = ISC_LIST_HEAD(msg->freerdata);
562135446Strhodes	}
563135446Strhodes	rdatalist = ISC_LIST_HEAD(msg->freerdatalist);
564135446Strhodes	while (rdatalist != NULL) {
565135446Strhodes		ISC_LIST_UNLINK(msg->freerdatalist, rdatalist, link);
566135446Strhodes		rdatalist = ISC_LIST_HEAD(msg->freerdatalist);
567135446Strhodes	}
568135446Strhodes
569135446Strhodes	dynbuf = ISC_LIST_HEAD(msg->scratchpad);
570135446Strhodes	INSIST(dynbuf != NULL);
571135446Strhodes	if (!everything) {
572135446Strhodes		isc_buffer_clear(dynbuf);
573135446Strhodes		dynbuf = ISC_LIST_NEXT(dynbuf, link);
574135446Strhodes	}
575135446Strhodes	while (dynbuf != NULL) {
576135446Strhodes		next_dynbuf = ISC_LIST_NEXT(dynbuf, link);
577135446Strhodes		ISC_LIST_UNLINK(msg->scratchpad, dynbuf, link);
578135446Strhodes		isc_buffer_free(&dynbuf);
579135446Strhodes		dynbuf = next_dynbuf;
580135446Strhodes	}
581135446Strhodes
582135446Strhodes	msgblock = ISC_LIST_HEAD(msg->rdatas);
583135446Strhodes	if (!everything && msgblock != NULL) {
584135446Strhodes		msgblock_reset(msgblock);
585135446Strhodes		msgblock = ISC_LIST_NEXT(msgblock, link);
586135446Strhodes	}
587135446Strhodes	while (msgblock != NULL) {
588135446Strhodes		next_msgblock = ISC_LIST_NEXT(msgblock, link);
589135446Strhodes		ISC_LIST_UNLINK(msg->rdatas, msgblock, link);
590135446Strhodes		msgblock_free(msg->mctx, msgblock, sizeof(dns_rdata_t));
591135446Strhodes		msgblock = next_msgblock;
592135446Strhodes	}
593135446Strhodes
594135446Strhodes	/*
595135446Strhodes	 * rdatalists could be empty.
596135446Strhodes	 */
597135446Strhodes
598135446Strhodes	msgblock = ISC_LIST_HEAD(msg->rdatalists);
599135446Strhodes	if (!everything && msgblock != NULL) {
600135446Strhodes		msgblock_reset(msgblock);
601135446Strhodes		msgblock = ISC_LIST_NEXT(msgblock, link);
602135446Strhodes	}
603135446Strhodes	while (msgblock != NULL) {
604135446Strhodes		next_msgblock = ISC_LIST_NEXT(msgblock, link);
605135446Strhodes		ISC_LIST_UNLINK(msg->rdatalists, msgblock, link);
606135446Strhodes		msgblock_free(msg->mctx, msgblock, sizeof(dns_rdatalist_t));
607135446Strhodes		msgblock = next_msgblock;
608135446Strhodes	}
609135446Strhodes
610135446Strhodes	msgblock = ISC_LIST_HEAD(msg->offsets);
611135446Strhodes	if (!everything && msgblock != NULL) {
612135446Strhodes		msgblock_reset(msgblock);
613135446Strhodes		msgblock = ISC_LIST_NEXT(msgblock, link);
614135446Strhodes	}
615135446Strhodes	while (msgblock != NULL) {
616135446Strhodes		next_msgblock = ISC_LIST_NEXT(msgblock, link);
617135446Strhodes		ISC_LIST_UNLINK(msg->offsets, msgblock, link);
618135446Strhodes		msgblock_free(msg->mctx, msgblock, sizeof(dns_offsets_t));
619135446Strhodes		msgblock = next_msgblock;
620135446Strhodes	}
621135446Strhodes
622135446Strhodes	if (msg->tsigkey != NULL) {
623135446Strhodes		dns_tsigkey_detach(&msg->tsigkey);
624135446Strhodes		msg->tsigkey = NULL;
625135446Strhodes	}
626135446Strhodes
627186462Sdougb	if (msg->tsigctx != NULL)
628186462Sdougb		dst_context_destroy(&msg->tsigctx);
629186462Sdougb
630135446Strhodes	if (msg->query.base != NULL) {
631135446Strhodes		if (msg->free_query != 0)
632135446Strhodes			isc_mem_put(msg->mctx, msg->query.base,
633135446Strhodes				    msg->query.length);
634135446Strhodes		msg->query.base = NULL;
635135446Strhodes		msg->query.length = 0;
636135446Strhodes	}
637135446Strhodes
638135446Strhodes	if (msg->saved.base != NULL) {
639135446Strhodes		if (msg->free_saved != 0)
640135446Strhodes			isc_mem_put(msg->mctx, msg->saved.base,
641135446Strhodes				    msg->saved.length);
642135446Strhodes		msg->saved.base = NULL;
643135446Strhodes		msg->saved.length = 0;
644135446Strhodes	}
645135446Strhodes
646135446Strhodes	/*
647135446Strhodes	 * cleanup the buffer cleanup list
648135446Strhodes	 */
649135446Strhodes	dynbuf = ISC_LIST_HEAD(msg->cleanup);
650135446Strhodes	while (dynbuf != NULL) {
651135446Strhodes		next_dynbuf = ISC_LIST_NEXT(dynbuf, link);
652135446Strhodes		ISC_LIST_UNLINK(msg->cleanup, dynbuf, link);
653135446Strhodes		isc_buffer_free(&dynbuf);
654135446Strhodes		dynbuf = next_dynbuf;
655135446Strhodes	}
656135446Strhodes
657135446Strhodes	/*
658135446Strhodes	 * Set other bits to normal default values.
659135446Strhodes	 */
660135446Strhodes	if (!everything)
661135446Strhodes		msginit(msg);
662135446Strhodes
663135446Strhodes	ENSURE(isc_mempool_getallocated(msg->namepool) == 0);
664135446Strhodes	ENSURE(isc_mempool_getallocated(msg->rdspool) == 0);
665135446Strhodes}
666135446Strhodes
667135446Strhodesstatic unsigned int
668135446Strhodesspacefortsig(dns_tsigkey_t *key, int otherlen) {
669135446Strhodes	isc_region_t r1, r2;
670135446Strhodes	unsigned int x;
671135446Strhodes	isc_result_t result;
672135446Strhodes
673135446Strhodes	/*
674135446Strhodes	 * The space required for an TSIG record is:
675135446Strhodes	 *
676135446Strhodes	 *	n1 bytes for the name
677135446Strhodes	 *	2 bytes for the type
678135446Strhodes	 *	2 bytes for the class
679135446Strhodes	 *	4 bytes for the ttl
680135446Strhodes	 *	2 bytes for the rdlength
681135446Strhodes	 *	n2 bytes for the algorithm name
682135446Strhodes	 *	6 bytes for the time signed
683135446Strhodes	 *	2 bytes for the fudge
684135446Strhodes	 *	2 bytes for the MAC size
685135446Strhodes	 *	x bytes for the MAC
686135446Strhodes	 *	2 bytes for the original id
687135446Strhodes	 *	2 bytes for the error
688135446Strhodes	 *	2 bytes for the other data length
689135446Strhodes	 *	y bytes for the other data (at most)
690135446Strhodes	 * ---------------------------------
691135446Strhodes	 *     26 + n1 + n2 + x + y bytes
692135446Strhodes	 */
693135446Strhodes
694135446Strhodes	dns_name_toregion(&key->name, &r1);
695135446Strhodes	dns_name_toregion(key->algorithm, &r2);
696135446Strhodes	if (key->key == NULL)
697135446Strhodes		x = 0;
698135446Strhodes	else {
699135446Strhodes		result = dst_key_sigsize(key->key, &x);
700135446Strhodes		if (result != ISC_R_SUCCESS)
701135446Strhodes			x = 0;
702135446Strhodes	}
703135446Strhodes	return (26 + r1.length + r2.length + x + otherlen);
704135446Strhodes}
705135446Strhodes
706135446Strhodesisc_result_t
707135446Strhodesdns_message_create(isc_mem_t *mctx, unsigned int intent, dns_message_t **msgp)
708135446Strhodes{
709135446Strhodes	dns_message_t *m;
710135446Strhodes	isc_result_t result;
711135446Strhodes	isc_buffer_t *dynbuf;
712135446Strhodes	unsigned int i;
713135446Strhodes
714135446Strhodes	REQUIRE(mctx != NULL);
715135446Strhodes	REQUIRE(msgp != NULL);
716135446Strhodes	REQUIRE(*msgp == NULL);
717135446Strhodes	REQUIRE(intent == DNS_MESSAGE_INTENTPARSE
718135446Strhodes		|| intent == DNS_MESSAGE_INTENTRENDER);
719135446Strhodes
720135446Strhodes	m = isc_mem_get(mctx, sizeof(dns_message_t));
721135446Strhodes	if (m == NULL)
722135446Strhodes		return (ISC_R_NOMEMORY);
723135446Strhodes
724135446Strhodes	/*
725135446Strhodes	 * No allocations until further notice.  Just initialize all lists
726135446Strhodes	 * and other members that are freed in the cleanup phase here.
727135446Strhodes	 */
728135446Strhodes
729135446Strhodes	m->magic = DNS_MESSAGE_MAGIC;
730135446Strhodes	m->from_to_wire = intent;
731135446Strhodes	msginit(m);
732135446Strhodes
733135446Strhodes	for (i = 0; i < DNS_SECTION_MAX; i++)
734135446Strhodes		ISC_LIST_INIT(m->sections[i]);
735135446Strhodes	m->mctx = mctx;
736135446Strhodes
737135446Strhodes	ISC_LIST_INIT(m->scratchpad);
738135446Strhodes	ISC_LIST_INIT(m->cleanup);
739135446Strhodes	m->namepool = NULL;
740135446Strhodes	m->rdspool = NULL;
741135446Strhodes	ISC_LIST_INIT(m->rdatas);
742135446Strhodes	ISC_LIST_INIT(m->rdatalists);
743135446Strhodes	ISC_LIST_INIT(m->offsets);
744135446Strhodes	ISC_LIST_INIT(m->freerdata);
745135446Strhodes	ISC_LIST_INIT(m->freerdatalist);
746135446Strhodes
747135446Strhodes	/*
748135446Strhodes	 * Ok, it is safe to allocate (and then "goto cleanup" if failure)
749135446Strhodes	 */
750135446Strhodes
751135446Strhodes	result = isc_mempool_create(m->mctx, sizeof(dns_name_t), &m->namepool);
752135446Strhodes	if (result != ISC_R_SUCCESS)
753135446Strhodes		goto cleanup;
754135446Strhodes	isc_mempool_setfreemax(m->namepool, NAME_COUNT);
755135446Strhodes	isc_mempool_setname(m->namepool, "msg:names");
756135446Strhodes
757135446Strhodes	result = isc_mempool_create(m->mctx, sizeof(dns_rdataset_t),
758135446Strhodes				    &m->rdspool);
759135446Strhodes	if (result != ISC_R_SUCCESS)
760135446Strhodes		goto cleanup;
761135446Strhodes	isc_mempool_setfreemax(m->rdspool, NAME_COUNT);
762135446Strhodes	isc_mempool_setname(m->rdspool, "msg:rdataset");
763135446Strhodes
764135446Strhodes	dynbuf = NULL;
765135446Strhodes	result = isc_buffer_allocate(mctx, &dynbuf, SCRATCHPAD_SIZE);
766135446Strhodes	if (result != ISC_R_SUCCESS)
767135446Strhodes		goto cleanup;
768135446Strhodes	ISC_LIST_APPEND(m->scratchpad, dynbuf, link);
769135446Strhodes
770135446Strhodes	m->cctx = NULL;
771135446Strhodes
772135446Strhodes	*msgp = m;
773135446Strhodes	return (ISC_R_SUCCESS);
774135446Strhodes
775135446Strhodes	/*
776135446Strhodes	 * Cleanup for error returns.
777135446Strhodes	 */
778135446Strhodes cleanup:
779135446Strhodes	dynbuf = ISC_LIST_HEAD(m->scratchpad);
780135446Strhodes	if (dynbuf != NULL) {
781135446Strhodes		ISC_LIST_UNLINK(m->scratchpad, dynbuf, link);
782135446Strhodes		isc_buffer_free(&dynbuf);
783135446Strhodes	}
784135446Strhodes	if (m->namepool != NULL)
785135446Strhodes		isc_mempool_destroy(&m->namepool);
786135446Strhodes	if (m->rdspool != NULL)
787135446Strhodes		isc_mempool_destroy(&m->rdspool);
788135446Strhodes	m->magic = 0;
789135446Strhodes	isc_mem_put(mctx, m, sizeof(dns_message_t));
790135446Strhodes
791135446Strhodes	return (ISC_R_NOMEMORY);
792135446Strhodes}
793135446Strhodes
794135446Strhodesvoid
795135446Strhodesdns_message_reset(dns_message_t *msg, unsigned int intent) {
796135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
797135446Strhodes	REQUIRE(intent == DNS_MESSAGE_INTENTPARSE
798135446Strhodes		|| intent == DNS_MESSAGE_INTENTRENDER);
799135446Strhodes
800135446Strhodes	msgreset(msg, ISC_FALSE);
801135446Strhodes	msg->from_to_wire = intent;
802135446Strhodes}
803135446Strhodes
804135446Strhodesvoid
805135446Strhodesdns_message_destroy(dns_message_t **msgp) {
806135446Strhodes	dns_message_t *msg;
807135446Strhodes
808135446Strhodes	REQUIRE(msgp != NULL);
809135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(*msgp));
810135446Strhodes
811135446Strhodes	msg = *msgp;
812135446Strhodes	*msgp = NULL;
813135446Strhodes
814135446Strhodes	msgreset(msg, ISC_TRUE);
815135446Strhodes	isc_mempool_destroy(&msg->namepool);
816135446Strhodes	isc_mempool_destroy(&msg->rdspool);
817135446Strhodes	msg->magic = 0;
818135446Strhodes	isc_mem_put(msg->mctx, msg, sizeof(dns_message_t));
819135446Strhodes}
820135446Strhodes
821135446Strhodesstatic isc_result_t
822135446Strhodesfindname(dns_name_t **foundname, dns_name_t *target,
823135446Strhodes	 dns_namelist_t *section)
824135446Strhodes{
825135446Strhodes	dns_name_t *curr;
826135446Strhodes
827135446Strhodes	for (curr = ISC_LIST_TAIL(*section);
828135446Strhodes	     curr != NULL;
829135446Strhodes	     curr = ISC_LIST_PREV(curr, link)) {
830135446Strhodes		if (dns_name_equal(curr, target)) {
831135446Strhodes			if (foundname != NULL)
832135446Strhodes				*foundname = curr;
833135446Strhodes			return (ISC_R_SUCCESS);
834135446Strhodes		}
835135446Strhodes	}
836135446Strhodes
837135446Strhodes	return (ISC_R_NOTFOUND);
838135446Strhodes}
839135446Strhodes
840135446Strhodesisc_result_t
841165071Sdougbdns_message_find(dns_name_t *name, dns_rdataclass_t rdclass,
842165071Sdougb		 dns_rdatatype_t type, dns_rdatatype_t covers,
843165071Sdougb		 dns_rdataset_t **rdataset)
844165071Sdougb{
845165071Sdougb	dns_rdataset_t *curr;
846165071Sdougb
847165071Sdougb	if (rdataset != NULL) {
848165071Sdougb		REQUIRE(*rdataset == NULL);
849165071Sdougb	}
850165071Sdougb
851165071Sdougb	for (curr = ISC_LIST_TAIL(name->list);
852165071Sdougb	     curr != NULL;
853165071Sdougb	     curr = ISC_LIST_PREV(curr, link)) {
854165071Sdougb		if (curr->rdclass == rdclass &&
855165071Sdougb		    curr->type == type && curr->covers == covers) {
856165071Sdougb			if (rdataset != NULL)
857165071Sdougb				*rdataset = curr;
858165071Sdougb			return (ISC_R_SUCCESS);
859165071Sdougb		}
860165071Sdougb	}
861165071Sdougb
862165071Sdougb	return (ISC_R_NOTFOUND);
863165071Sdougb}
864165071Sdougb
865165071Sdougbisc_result_t
866135446Strhodesdns_message_findtype(dns_name_t *name, dns_rdatatype_t type,
867135446Strhodes		     dns_rdatatype_t covers, dns_rdataset_t **rdataset)
868135446Strhodes{
869135446Strhodes	dns_rdataset_t *curr;
870135446Strhodes
871165071Sdougb	REQUIRE(name != NULL);
872135446Strhodes	if (rdataset != NULL) {
873135446Strhodes		REQUIRE(*rdataset == NULL);
874135446Strhodes	}
875135446Strhodes
876135446Strhodes	for (curr = ISC_LIST_TAIL(name->list);
877135446Strhodes	     curr != NULL;
878135446Strhodes	     curr = ISC_LIST_PREV(curr, link)) {
879135446Strhodes		if (curr->type == type && curr->covers == covers) {
880135446Strhodes			if (rdataset != NULL)
881135446Strhodes				*rdataset = curr;
882135446Strhodes			return (ISC_R_SUCCESS);
883135446Strhodes		}
884135446Strhodes	}
885135446Strhodes
886135446Strhodes	return (ISC_R_NOTFOUND);
887135446Strhodes}
888135446Strhodes
889135446Strhodes/*
890135446Strhodes * Read a name from buffer "source".
891135446Strhodes */
892135446Strhodesstatic isc_result_t
893135446Strhodesgetname(dns_name_t *name, isc_buffer_t *source, dns_message_t *msg,
894135446Strhodes	dns_decompress_t *dctx)
895135446Strhodes{
896135446Strhodes	isc_buffer_t *scratch;
897135446Strhodes	isc_result_t result;
898135446Strhodes	unsigned int tries;
899135446Strhodes
900135446Strhodes	scratch = currentbuffer(msg);
901135446Strhodes
902135446Strhodes	/*
903135446Strhodes	 * First try:  use current buffer.
904135446Strhodes	 * Second try:  allocate a new buffer and use that.
905135446Strhodes	 */
906135446Strhodes	tries = 0;
907135446Strhodes	while (tries < 2) {
908135446Strhodes		result = dns_name_fromwire(name, source, dctx, ISC_FALSE,
909135446Strhodes					   scratch);
910135446Strhodes
911135446Strhodes		if (result == ISC_R_NOSPACE) {
912135446Strhodes			tries++;
913135446Strhodes
914135446Strhodes			result = newbuffer(msg, SCRATCHPAD_SIZE);
915135446Strhodes			if (result != ISC_R_SUCCESS)
916135446Strhodes				return (result);
917135446Strhodes
918135446Strhodes			scratch = currentbuffer(msg);
919135446Strhodes			dns_name_reset(name);
920135446Strhodes		} else {
921135446Strhodes			return (result);
922135446Strhodes		}
923135446Strhodes	}
924135446Strhodes
925135446Strhodes	INSIST(0);  /* Cannot get here... */
926135446Strhodes	return (ISC_R_UNEXPECTED);
927135446Strhodes}
928135446Strhodes
929135446Strhodesstatic isc_result_t
930135446Strhodesgetrdata(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
931135446Strhodes	 dns_rdataclass_t rdclass, dns_rdatatype_t rdtype,
932135446Strhodes	 unsigned int rdatalen, dns_rdata_t *rdata)
933135446Strhodes{
934135446Strhodes	isc_buffer_t *scratch;
935135446Strhodes	isc_result_t result;
936135446Strhodes	unsigned int tries;
937135446Strhodes	unsigned int trysize;
938135446Strhodes
939135446Strhodes	scratch = currentbuffer(msg);
940135446Strhodes
941135446Strhodes	isc_buffer_setactive(source, rdatalen);
942135446Strhodes
943135446Strhodes	/*
944135446Strhodes	 * First try:  use current buffer.
945135446Strhodes	 * Second try:  allocate a new buffer of size
946135446Strhodes	 *     max(SCRATCHPAD_SIZE, 2 * compressed_rdatalen)
947135446Strhodes	 *     (the data will fit if it was not more than 50% compressed)
948135446Strhodes	 * Subsequent tries: double buffer size on each try.
949135446Strhodes	 */
950135446Strhodes	tries = 0;
951135446Strhodes	trysize = 0;
952135446Strhodes	/* XXX possibly change this to a while (tries < 2) loop */
953135446Strhodes	for (;;) {
954135446Strhodes		result = dns_rdata_fromwire(rdata, rdclass, rdtype,
955135446Strhodes					    source, dctx, 0,
956135446Strhodes					    scratch);
957135446Strhodes
958135446Strhodes		if (result == ISC_R_NOSPACE) {
959135446Strhodes			if (tries == 0) {
960135446Strhodes				trysize = 2 * rdatalen;
961135446Strhodes				if (trysize < SCRATCHPAD_SIZE)
962135446Strhodes					trysize = SCRATCHPAD_SIZE;
963135446Strhodes			} else {
964135446Strhodes				INSIST(trysize != 0);
965135446Strhodes				if (trysize >= 65535)
966135446Strhodes					return (ISC_R_NOSPACE);
967135446Strhodes					/* XXX DNS_R_RRTOOLONG? */
968135446Strhodes				trysize *= 2;
969135446Strhodes			}
970135446Strhodes			tries++;
971135446Strhodes			result = newbuffer(msg, trysize);
972135446Strhodes			if (result != ISC_R_SUCCESS)
973135446Strhodes				return (result);
974135446Strhodes
975135446Strhodes			scratch = currentbuffer(msg);
976135446Strhodes		} else {
977135446Strhodes			return (result);
978135446Strhodes		}
979135446Strhodes	}
980135446Strhodes}
981135446Strhodes
982135446Strhodes#define DO_FORMERR					\
983135446Strhodes	do {						\
984135446Strhodes		if (best_effort)			\
985135446Strhodes			seen_problem = ISC_TRUE;	\
986135446Strhodes		else {					\
987135446Strhodes			result = DNS_R_FORMERR;		\
988135446Strhodes			goto cleanup;			\
989135446Strhodes		}					\
990135446Strhodes	} while (0)
991135446Strhodes
992135446Strhodesstatic isc_result_t
993135446Strhodesgetquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
994135446Strhodes	     unsigned int options)
995135446Strhodes{
996135446Strhodes	isc_region_t r;
997135446Strhodes	unsigned int count;
998135446Strhodes	dns_name_t *name;
999135446Strhodes	dns_name_t *name2;
1000135446Strhodes	dns_offsets_t *offsets;
1001135446Strhodes	dns_rdataset_t *rdataset;
1002135446Strhodes	dns_rdatalist_t *rdatalist;
1003135446Strhodes	isc_result_t result;
1004135446Strhodes	dns_rdatatype_t rdtype;
1005135446Strhodes	dns_rdataclass_t rdclass;
1006135446Strhodes	dns_namelist_t *section;
1007135446Strhodes	isc_boolean_t free_name;
1008135446Strhodes	isc_boolean_t best_effort;
1009135446Strhodes	isc_boolean_t seen_problem;
1010135446Strhodes
1011135446Strhodes	section = &msg->sections[DNS_SECTION_QUESTION];
1012135446Strhodes
1013135446Strhodes	best_effort = ISC_TF(options & DNS_MESSAGEPARSE_BESTEFFORT);
1014135446Strhodes	seen_problem = ISC_FALSE;
1015135446Strhodes
1016135446Strhodes	name = NULL;
1017135446Strhodes	rdataset = NULL;
1018135446Strhodes	rdatalist = NULL;
1019135446Strhodes
1020135446Strhodes	for (count = 0; count < msg->counts[DNS_SECTION_QUESTION]; count++) {
1021135446Strhodes		name = isc_mempool_get(msg->namepool);
1022135446Strhodes		if (name == NULL)
1023135446Strhodes			return (ISC_R_NOMEMORY);
1024135446Strhodes		free_name = ISC_TRUE;
1025186462Sdougb
1026135446Strhodes		offsets = newoffsets(msg);
1027135446Strhodes		if (offsets == NULL) {
1028135446Strhodes			result = ISC_R_NOMEMORY;
1029135446Strhodes			goto cleanup;
1030135446Strhodes		}
1031135446Strhodes		dns_name_init(name, *offsets);
1032135446Strhodes
1033135446Strhodes		/*
1034135446Strhodes		 * Parse the name out of this packet.
1035135446Strhodes		 */
1036135446Strhodes		isc_buffer_remainingregion(source, &r);
1037135446Strhodes		isc_buffer_setactive(source, r.length);
1038135446Strhodes		result = getname(name, source, msg, dctx);
1039135446Strhodes		if (result != ISC_R_SUCCESS)
1040135446Strhodes			goto cleanup;
1041135446Strhodes
1042135446Strhodes		/*
1043135446Strhodes		 * Run through the section, looking to see if this name
1044135446Strhodes		 * is already there.  If it is found, put back the allocated
1045135446Strhodes		 * name since we no longer need it, and set our name pointer
1046135446Strhodes		 * to point to the name we found.
1047135446Strhodes		 */
1048135446Strhodes		result = findname(&name2, name, section);
1049135446Strhodes
1050135446Strhodes		/*
1051135446Strhodes		 * If it is the first name in the section, accept it.
1052135446Strhodes		 *
1053135446Strhodes		 * If it is not, but is not the same as the name already
1054135446Strhodes		 * in the question section, append to the section.  Note that
1055135446Strhodes		 * here in the question section this is illegal, so return
1056135446Strhodes		 * FORMERR.  In the future, check the opcode to see if
1057135446Strhodes		 * this should be legal or not.  In either case we no longer
1058135446Strhodes		 * need this name pointer.
1059135446Strhodes		 */
1060135446Strhodes		if (result != ISC_R_SUCCESS) {
1061135446Strhodes			if (!ISC_LIST_EMPTY(*section))
1062135446Strhodes				DO_FORMERR;
1063135446Strhodes			ISC_LIST_APPEND(*section, name, link);
1064135446Strhodes			free_name = ISC_FALSE;
1065135446Strhodes		} else {
1066135446Strhodes			isc_mempool_put(msg->namepool, name);
1067135446Strhodes			name = name2;
1068135446Strhodes			name2 = NULL;
1069135446Strhodes			free_name = ISC_FALSE;
1070135446Strhodes		}
1071135446Strhodes
1072135446Strhodes		/*
1073135446Strhodes		 * Get type and class.
1074135446Strhodes		 */
1075135446Strhodes		isc_buffer_remainingregion(source, &r);
1076135446Strhodes		if (r.length < 4) {
1077135446Strhodes			result = ISC_R_UNEXPECTEDEND;
1078135446Strhodes			goto cleanup;
1079135446Strhodes		}
1080135446Strhodes		rdtype = isc_buffer_getuint16(source);
1081135446Strhodes		rdclass = isc_buffer_getuint16(source);
1082135446Strhodes
1083135446Strhodes		/*
1084135446Strhodes		 * If this class is different than the one we already read,
1085135446Strhodes		 * this is an error.
1086135446Strhodes		 */
1087135446Strhodes		if (msg->state == DNS_SECTION_ANY) {
1088135446Strhodes			msg->state = DNS_SECTION_QUESTION;
1089135446Strhodes			msg->rdclass = rdclass;
1090135446Strhodes		} else if (msg->rdclass != rdclass)
1091135446Strhodes			DO_FORMERR;
1092135446Strhodes
1093135446Strhodes		/*
1094135446Strhodes		 * Can't ask the same question twice.
1095135446Strhodes		 */
1096165071Sdougb		result = dns_message_find(name, rdclass, rdtype, 0, NULL);
1097135446Strhodes		if (result == ISC_R_SUCCESS)
1098135446Strhodes			DO_FORMERR;
1099135446Strhodes
1100135446Strhodes		/*
1101135446Strhodes		 * Allocate a new rdatalist.
1102135446Strhodes		 */
1103135446Strhodes		rdatalist = newrdatalist(msg);
1104135446Strhodes		if (rdatalist == NULL) {
1105135446Strhodes			result = ISC_R_NOMEMORY;
1106135446Strhodes			goto cleanup;
1107135446Strhodes		}
1108135446Strhodes		rdataset =  isc_mempool_get(msg->rdspool);
1109135446Strhodes		if (rdataset == NULL) {
1110135446Strhodes			result = ISC_R_NOMEMORY;
1111135446Strhodes			goto cleanup;
1112135446Strhodes		}
1113135446Strhodes
1114135446Strhodes		/*
1115135446Strhodes		 * Convert rdatalist to rdataset, and attach the latter to
1116135446Strhodes		 * the name.
1117135446Strhodes		 */
1118135446Strhodes		rdatalist->type = rdtype;
1119135446Strhodes		rdatalist->covers = 0;
1120135446Strhodes		rdatalist->rdclass = rdclass;
1121135446Strhodes		rdatalist->ttl = 0;
1122135446Strhodes		ISC_LIST_INIT(rdatalist->rdata);
1123135446Strhodes
1124135446Strhodes		dns_rdataset_init(rdataset);
1125135446Strhodes		result = dns_rdatalist_tordataset(rdatalist, rdataset);
1126135446Strhodes		if (result != ISC_R_SUCCESS)
1127135446Strhodes			goto cleanup;
1128135446Strhodes
1129135446Strhodes		rdataset->attributes |= DNS_RDATASETATTR_QUESTION;
1130135446Strhodes
1131135446Strhodes		ISC_LIST_APPEND(name->list, rdataset, link);
1132135446Strhodes		rdataset = NULL;
1133135446Strhodes	}
1134135446Strhodes
1135135446Strhodes	if (seen_problem)
1136135446Strhodes		return (DNS_R_RECOVERABLE);
1137135446Strhodes	return (ISC_R_SUCCESS);
1138135446Strhodes
1139135446Strhodes cleanup:
1140135446Strhodes	if (rdataset != NULL) {
1141135446Strhodes		INSIST(!dns_rdataset_isassociated(rdataset));
1142135446Strhodes		isc_mempool_put(msg->rdspool, rdataset);
1143135446Strhodes	}
1144135446Strhodes#if 0
1145135446Strhodes	if (rdatalist != NULL)
1146135446Strhodes		isc_mempool_put(msg->rdlpool, rdatalist);
1147135446Strhodes#endif
1148135446Strhodes	if (free_name)
1149135446Strhodes		isc_mempool_put(msg->namepool, name);
1150135446Strhodes
1151135446Strhodes	return (result);
1152135446Strhodes}
1153135446Strhodes
1154135446Strhodesstatic isc_boolean_t
1155135446Strhodesupdate(dns_section_t section, dns_rdataclass_t rdclass) {
1156135446Strhodes	if (section == DNS_SECTION_PREREQUISITE)
1157135446Strhodes		return (ISC_TF(rdclass == dns_rdataclass_any ||
1158135446Strhodes			       rdclass == dns_rdataclass_none));
1159135446Strhodes	if (section == DNS_SECTION_UPDATE)
1160135446Strhodes		return (ISC_TF(rdclass == dns_rdataclass_any));
1161135446Strhodes	return (ISC_FALSE);
1162135446Strhodes}
1163135446Strhodes
1164135446Strhodesstatic isc_result_t
1165135446Strhodesgetsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
1166135446Strhodes	   dns_section_t sectionid, unsigned int options)
1167135446Strhodes{
1168135446Strhodes	isc_region_t r;
1169135446Strhodes	unsigned int count, rdatalen;
1170135446Strhodes	dns_name_t *name;
1171135446Strhodes	dns_name_t *name2;
1172135446Strhodes	dns_offsets_t *offsets;
1173135446Strhodes	dns_rdataset_t *rdataset;
1174135446Strhodes	dns_rdatalist_t *rdatalist;
1175135446Strhodes	isc_result_t result;
1176135446Strhodes	dns_rdatatype_t rdtype, covers;
1177135446Strhodes	dns_rdataclass_t rdclass;
1178135446Strhodes	dns_rdata_t *rdata;
1179135446Strhodes	dns_ttl_t ttl;
1180135446Strhodes	dns_namelist_t *section;
1181135446Strhodes	isc_boolean_t free_name, free_rdataset;
1182135446Strhodes	isc_boolean_t preserve_order, best_effort, seen_problem;
1183135446Strhodes	isc_boolean_t issigzero;
1184135446Strhodes
1185135446Strhodes	preserve_order = ISC_TF(options & DNS_MESSAGEPARSE_PRESERVEORDER);
1186135446Strhodes	best_effort = ISC_TF(options & DNS_MESSAGEPARSE_BESTEFFORT);
1187135446Strhodes	seen_problem = ISC_FALSE;
1188135446Strhodes
1189135446Strhodes	for (count = 0; count < msg->counts[sectionid]; count++) {
1190135446Strhodes		int recstart = source->current;
1191135446Strhodes		isc_boolean_t skip_name_search, skip_type_search;
1192135446Strhodes
1193135446Strhodes		section = &msg->sections[sectionid];
1194135446Strhodes
1195135446Strhodes		skip_name_search = ISC_FALSE;
1196135446Strhodes		skip_type_search = ISC_FALSE;
1197135446Strhodes		free_rdataset = ISC_FALSE;
1198135446Strhodes
1199135446Strhodes		name = isc_mempool_get(msg->namepool);
1200135446Strhodes		if (name == NULL)
1201135446Strhodes			return (ISC_R_NOMEMORY);
1202135446Strhodes		free_name = ISC_TRUE;
1203135446Strhodes
1204135446Strhodes		offsets = newoffsets(msg);
1205135446Strhodes		if (offsets == NULL) {
1206135446Strhodes			result = ISC_R_NOMEMORY;
1207135446Strhodes			goto cleanup;
1208135446Strhodes		}
1209135446Strhodes		dns_name_init(name, *offsets);
1210135446Strhodes
1211135446Strhodes		/*
1212135446Strhodes		 * Parse the name out of this packet.
1213135446Strhodes		 */
1214135446Strhodes		isc_buffer_remainingregion(source, &r);
1215135446Strhodes		isc_buffer_setactive(source, r.length);
1216135446Strhodes		result = getname(name, source, msg, dctx);
1217135446Strhodes		if (result != ISC_R_SUCCESS)
1218135446Strhodes			goto cleanup;
1219135446Strhodes
1220135446Strhodes		/*
1221135446Strhodes		 * Get type, class, ttl, and rdatalen.  Verify that at least
1222135446Strhodes		 * rdatalen bytes remain.  (Some of this is deferred to
1223135446Strhodes		 * later.)
1224135446Strhodes		 */
1225135446Strhodes		isc_buffer_remainingregion(source, &r);
1226135446Strhodes		if (r.length < 2 + 2 + 4 + 2) {
1227135446Strhodes			result = ISC_R_UNEXPECTEDEND;
1228135446Strhodes			goto cleanup;
1229135446Strhodes		}
1230135446Strhodes		rdtype = isc_buffer_getuint16(source);
1231135446Strhodes		rdclass = isc_buffer_getuint16(source);
1232135446Strhodes
1233135446Strhodes		/*
1234135446Strhodes		 * If there was no question section, we may not yet have
1235135446Strhodes		 * established a class.  Do so now.
1236135446Strhodes		 */
1237135446Strhodes		if (msg->state == DNS_SECTION_ANY &&
1238135446Strhodes		    rdtype != dns_rdatatype_opt &&	/* class is UDP SIZE */
1239135446Strhodes		    rdtype != dns_rdatatype_tsig &&	/* class is ANY */
1240135446Strhodes		    rdtype != dns_rdatatype_tkey) {	/* class is undefined */
1241135446Strhodes			msg->rdclass = rdclass;
1242135446Strhodes			msg->state = DNS_SECTION_QUESTION;
1243135446Strhodes		}
1244135446Strhodes
1245135446Strhodes		/*
1246135446Strhodes		 * If this class is different than the one in the question
1247135446Strhodes		 * section, bail.
1248135446Strhodes		 */
1249135446Strhodes		if (msg->opcode != dns_opcode_update
1250135446Strhodes		    && rdtype != dns_rdatatype_tsig
1251135446Strhodes		    && rdtype != dns_rdatatype_opt
1252135446Strhodes		    && rdtype != dns_rdatatype_dnskey /* in a TKEY query */
1253135446Strhodes		    && rdtype != dns_rdatatype_sig /* SIG(0) */
1254135446Strhodes		    && rdtype != dns_rdatatype_tkey /* Win2000 TKEY */
1255165071Sdougb		    && msg->rdclass != dns_rdataclass_any
1256135446Strhodes		    && msg->rdclass != rdclass)
1257135446Strhodes			DO_FORMERR;
1258135446Strhodes
1259135446Strhodes		/*
1260135446Strhodes		 * Special type handling for TSIG, OPT, and TKEY.
1261135446Strhodes		 */
1262135446Strhodes		if (rdtype == dns_rdatatype_tsig) {
1263135446Strhodes			/*
1264135446Strhodes			 * If it is a tsig, verify that it is in the
1265135446Strhodes			 * additional data section.
1266135446Strhodes			 */
1267135446Strhodes			if (sectionid != DNS_SECTION_ADDITIONAL ||
1268135446Strhodes			    rdclass != dns_rdataclass_any ||
1269135446Strhodes			    count != msg->counts[sectionid]  - 1)
1270135446Strhodes				DO_FORMERR;
1271135446Strhodes			msg->sigstart = recstart;
1272135446Strhodes			skip_name_search = ISC_TRUE;
1273135446Strhodes			skip_type_search = ISC_TRUE;
1274135446Strhodes		} else if (rdtype == dns_rdatatype_opt) {
1275135446Strhodes			/*
1276135446Strhodes			 * The name of an OPT record must be ".", it
1277135446Strhodes			 * must be in the additional data section, and
1278135446Strhodes			 * it must be the first OPT we've seen.
1279135446Strhodes			 */
1280135446Strhodes			if (!dns_name_equal(dns_rootname, name) ||
1281135446Strhodes			    msg->opt != NULL)
1282135446Strhodes				DO_FORMERR;
1283135446Strhodes			skip_name_search = ISC_TRUE;
1284135446Strhodes			skip_type_search = ISC_TRUE;
1285135446Strhodes		} else if (rdtype == dns_rdatatype_tkey) {
1286135446Strhodes			/*
1287135446Strhodes			 * A TKEY must be in the additional section if this
1288135446Strhodes			 * is a query, and the answer section if this is a
1289135446Strhodes			 * response.  Unless it's a Win2000 client.
1290135446Strhodes			 *
1291135446Strhodes			 * Its class is ignored.
1292135446Strhodes			 */
1293135446Strhodes			dns_section_t tkeysection;
1294135446Strhodes
1295135446Strhodes			if ((msg->flags & DNS_MESSAGEFLAG_QR) == 0)
1296135446Strhodes				tkeysection = DNS_SECTION_ADDITIONAL;
1297135446Strhodes			else
1298135446Strhodes				tkeysection = DNS_SECTION_ANSWER;
1299135446Strhodes			if (sectionid != tkeysection &&
1300135446Strhodes			    sectionid != DNS_SECTION_ANSWER)
1301135446Strhodes				DO_FORMERR;
1302135446Strhodes		}
1303135446Strhodes
1304135446Strhodes		/*
1305135446Strhodes		 * ... now get ttl and rdatalen, and check buffer.
1306135446Strhodes		 */
1307135446Strhodes		ttl = isc_buffer_getuint32(source);
1308135446Strhodes		rdatalen = isc_buffer_getuint16(source);
1309135446Strhodes		r.length -= (2 + 2 + 4 + 2);
1310135446Strhodes		if (r.length < rdatalen) {
1311135446Strhodes			result = ISC_R_UNEXPECTEDEND;
1312135446Strhodes			goto cleanup;
1313135446Strhodes		}
1314135446Strhodes
1315135446Strhodes		/*
1316135446Strhodes		 * Read the rdata from the wire format.  Interpret the
1317135446Strhodes		 * rdata according to its actual class, even if it had a
1318135446Strhodes		 * DynDNS meta-class in the packet (unless this is a TSIG).
1319135446Strhodes		 * Then put the meta-class back into the finished rdata.
1320135446Strhodes		 */
1321135446Strhodes		rdata = newrdata(msg);
1322135446Strhodes		if (rdata == NULL) {
1323135446Strhodes			result = ISC_R_NOMEMORY;
1324135446Strhodes			goto cleanup;
1325135446Strhodes		}
1326135446Strhodes		if (msg->opcode == dns_opcode_update &&
1327135446Strhodes		    update(sectionid, rdclass)) {
1328135446Strhodes			if (rdatalen != 0) {
1329135446Strhodes				result = DNS_R_FORMERR;
1330135446Strhodes				goto cleanup;
1331135446Strhodes			}
1332135446Strhodes			/*
1333135446Strhodes			 * When the rdata is empty, the data pointer is
1334186462Sdougb			 * never dereferenced, but it must still be non-NULL.
1335135446Strhodes			 * Casting 1 rather than "" avoids warnings about
1336135446Strhodes			 * discarding the const attribute of a string,
1337135446Strhodes			 * for compilers that would warn about such things.
1338135446Strhodes			 */
1339135446Strhodes			rdata->data = (unsigned char *)1;
1340135446Strhodes			rdata->length = 0;
1341135446Strhodes			rdata->rdclass = rdclass;
1342135446Strhodes			rdata->type = rdtype;
1343135446Strhodes			rdata->flags = DNS_RDATA_UPDATE;
1344135446Strhodes			result = ISC_R_SUCCESS;
1345174187Sdougb		} else if (rdclass == dns_rdataclass_none &&
1346174187Sdougb			   msg->opcode == dns_opcode_update &&
1347174187Sdougb			   sectionid == DNS_SECTION_UPDATE) {
1348174187Sdougb			result = getrdata(source, msg, dctx, msg->rdclass,
1349174187Sdougb					  rdtype, rdatalen, rdata);
1350165071Sdougb		} else
1351135446Strhodes			result = getrdata(source, msg, dctx, rdclass,
1352135446Strhodes					  rdtype, rdatalen, rdata);
1353135446Strhodes		if (result != ISC_R_SUCCESS)
1354135446Strhodes			goto cleanup;
1355135446Strhodes		rdata->rdclass = rdclass;
1356135446Strhodes		issigzero = ISC_FALSE;
1357135446Strhodes		if (rdtype == dns_rdatatype_rrsig  &&
1358135446Strhodes		    rdata->flags == 0) {
1359135446Strhodes			covers = dns_rdata_covers(rdata);
1360135446Strhodes			if (covers == 0)
1361135446Strhodes				DO_FORMERR;
1362135446Strhodes		} else if (rdtype == dns_rdatatype_sig /* SIG(0) */ &&
1363135446Strhodes			   rdata->flags == 0) {
1364135446Strhodes			covers = dns_rdata_covers(rdata);
1365135446Strhodes			if (covers == 0) {
1366135446Strhodes				if (sectionid != DNS_SECTION_ADDITIONAL ||
1367135446Strhodes				    count != msg->counts[sectionid]  - 1)
1368135446Strhodes					DO_FORMERR;
1369135446Strhodes				msg->sigstart = recstart;
1370135446Strhodes				skip_name_search = ISC_TRUE;
1371135446Strhodes				skip_type_search = ISC_TRUE;
1372135446Strhodes				issigzero = ISC_TRUE;
1373135446Strhodes			}
1374135446Strhodes		} else
1375135446Strhodes			covers = 0;
1376135446Strhodes
1377135446Strhodes		/*
1378135446Strhodes		 * If we are doing a dynamic update or this is a meta-type,
1379135446Strhodes		 * don't bother searching for a name, just append this one
1380135446Strhodes		 * to the end of the message.
1381135446Strhodes		 */
1382135446Strhodes		if (preserve_order || msg->opcode == dns_opcode_update ||
1383135446Strhodes		    skip_name_search) {
1384135446Strhodes			if (rdtype != dns_rdatatype_opt &&
1385135446Strhodes			    rdtype != dns_rdatatype_tsig &&
1386135446Strhodes			    !issigzero)
1387135446Strhodes			{
1388135446Strhodes				ISC_LIST_APPEND(*section, name, link);
1389135446Strhodes				free_name = ISC_FALSE;
1390135446Strhodes			}
1391135446Strhodes		} else {
1392135446Strhodes			/*
1393135446Strhodes			 * Run through the section, looking to see if this name
1394135446Strhodes			 * is already there.  If it is found, put back the
1395135446Strhodes			 * allocated name since we no longer need it, and set
1396135446Strhodes			 * our name pointer to point to the name we found.
1397135446Strhodes			 */
1398135446Strhodes			result = findname(&name2, name, section);
1399135446Strhodes
1400135446Strhodes			/*
1401135446Strhodes			 * If it is a new name, append to the section.
1402135446Strhodes			 */
1403135446Strhodes			if (result == ISC_R_SUCCESS) {
1404135446Strhodes				isc_mempool_put(msg->namepool, name);
1405135446Strhodes				name = name2;
1406135446Strhodes			} else {
1407135446Strhodes				ISC_LIST_APPEND(*section, name, link);
1408135446Strhodes			}
1409135446Strhodes			free_name = ISC_FALSE;
1410135446Strhodes		}
1411135446Strhodes
1412135446Strhodes		/*
1413135446Strhodes		 * Search name for the particular type and class.
1414135446Strhodes		 * Skip this stage if in update mode or this is a meta-type.
1415135446Strhodes		 */
1416135446Strhodes		if (preserve_order || msg->opcode == dns_opcode_update ||
1417135446Strhodes		    skip_type_search)
1418135446Strhodes			result = ISC_R_NOTFOUND;
1419135446Strhodes		else {
1420135446Strhodes			/*
1421135446Strhodes			 * If this is a type that can only occur in
1422135446Strhodes			 * the question section, fail.
1423135446Strhodes			 */
1424135446Strhodes			if (dns_rdatatype_questiononly(rdtype))
1425135446Strhodes				DO_FORMERR;
1426135446Strhodes
1427135446Strhodes			rdataset = NULL;
1428165071Sdougb			result = dns_message_find(name, rdclass, rdtype,
1429165071Sdougb						   covers, &rdataset);
1430135446Strhodes		}
1431135446Strhodes
1432135446Strhodes		/*
1433135446Strhodes		 * If we found an rdataset that matches, we need to
1434135446Strhodes		 * append this rdata to that set.  If we did not, we need
1435135446Strhodes		 * to create a new rdatalist, store the important bits there,
1436135446Strhodes		 * convert it to an rdataset, and link the latter to the name.
1437135446Strhodes		 * Yuck.  When appending, make certain that the type isn't
1438135446Strhodes		 * a singleton type, such as SOA or CNAME.
1439135446Strhodes		 *
1440135446Strhodes		 * Note that this check will be bypassed when preserving order,
1441135446Strhodes		 * the opcode is an update, or the type search is skipped.
1442135446Strhodes		 */
1443135446Strhodes		if (result == ISC_R_SUCCESS) {
1444135446Strhodes			if (dns_rdatatype_issingleton(rdtype))
1445135446Strhodes				DO_FORMERR;
1446135446Strhodes		}
1447135446Strhodes
1448135446Strhodes		if (result == ISC_R_NOTFOUND) {
1449135446Strhodes			rdataset = isc_mempool_get(msg->rdspool);
1450135446Strhodes			if (rdataset == NULL) {
1451135446Strhodes				result = ISC_R_NOMEMORY;
1452135446Strhodes				goto cleanup;
1453135446Strhodes			}
1454135446Strhodes			free_rdataset = ISC_TRUE;
1455135446Strhodes
1456135446Strhodes			rdatalist = newrdatalist(msg);
1457135446Strhodes			if (rdatalist == NULL) {
1458135446Strhodes				result = ISC_R_NOMEMORY;
1459135446Strhodes				goto cleanup;
1460135446Strhodes			}
1461135446Strhodes
1462135446Strhodes			rdatalist->type = rdtype;
1463135446Strhodes			rdatalist->covers = covers;
1464135446Strhodes			rdatalist->rdclass = rdclass;
1465135446Strhodes			rdatalist->ttl = ttl;
1466135446Strhodes			ISC_LIST_INIT(rdatalist->rdata);
1467135446Strhodes
1468135446Strhodes			dns_rdataset_init(rdataset);
1469135446Strhodes			RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist,
1470135446Strhodes							       rdataset)
1471135446Strhodes				      == ISC_R_SUCCESS);
1472135446Strhodes
1473186462Sdougb			if (rdtype != dns_rdatatype_opt &&
1474135446Strhodes			    rdtype != dns_rdatatype_tsig &&
1475135446Strhodes			    !issigzero)
1476135446Strhodes			{
1477135446Strhodes				ISC_LIST_APPEND(name->list, rdataset, link);
1478135446Strhodes				free_rdataset = ISC_FALSE;
1479135446Strhodes			}
1480135446Strhodes		}
1481135446Strhodes
1482135446Strhodes		/*
1483135446Strhodes		 * Minimize TTLs.
1484135446Strhodes		 *
1485170222Sdougb		 * Section 5.2 of RFC2181 says we should drop
1486135446Strhodes		 * nonauthoritative rrsets where the TTLs differ, but we
1487135446Strhodes		 * currently treat them the as if they were authoritative and
1488135446Strhodes		 * minimize them.
1489135446Strhodes		 */
1490135446Strhodes		if (ttl != rdataset->ttl) {
1491135446Strhodes			rdataset->attributes |= DNS_RDATASETATTR_TTLADJUSTED;
1492135446Strhodes			if (ttl < rdataset->ttl)
1493135446Strhodes				rdataset->ttl = ttl;
1494135446Strhodes		}
1495135446Strhodes
1496193149Sdougb		/* Append this rdata to the rdataset. */
1497193149Sdougb		dns_rdatalist_fromrdataset(rdataset, &rdatalist);
1498135446Strhodes		ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
1499135446Strhodes
1500135446Strhodes		/*
1501135446Strhodes		 * If this is an OPT record, remember it.  Also, set
1502135446Strhodes		 * the extended rcode.  Note that msg->opt will only be set
1503135446Strhodes		 * if best-effort parsing is enabled.
1504135446Strhodes		 */
1505135446Strhodes		if (rdtype == dns_rdatatype_opt && msg->opt == NULL) {
1506135446Strhodes			dns_rcode_t ercode;
1507135446Strhodes
1508135446Strhodes			msg->opt = rdataset;
1509135446Strhodes			rdataset = NULL;
1510135446Strhodes			free_rdataset = ISC_FALSE;
1511135446Strhodes			ercode = (dns_rcode_t)
1512135446Strhodes				((msg->opt->ttl & DNS_MESSAGE_EDNSRCODE_MASK)
1513135446Strhodes				 >> 20);
1514135446Strhodes			msg->rcode |= ercode;
1515135446Strhodes			isc_mempool_put(msg->namepool, name);
1516135446Strhodes			free_name = ISC_FALSE;
1517135446Strhodes		}
1518135446Strhodes
1519135446Strhodes		/*
1520135446Strhodes		 * If this is an SIG(0) or TSIG record, remember it.  Note
1521135446Strhodes		 * that msg->sig0 or msg->tsig will only be set if best-effort
1522135446Strhodes		 * parsing is enabled.
1523135446Strhodes		 */
1524135446Strhodes		if (issigzero && msg->sig0 == NULL) {
1525135446Strhodes			msg->sig0 = rdataset;
1526135446Strhodes			msg->sig0name = name;
1527135446Strhodes			rdataset = NULL;
1528135446Strhodes			free_rdataset = ISC_FALSE;
1529135446Strhodes			free_name = ISC_FALSE;
1530135446Strhodes		} else if (rdtype == dns_rdatatype_tsig && msg->tsig == NULL) {
1531135446Strhodes			msg->tsig = rdataset;
1532135446Strhodes			msg->tsigname = name;
1533218384Sdougb			/* Windows doesn't like TSIG names to be compressed. */
1534218384Sdougb			msg->tsigname->attributes |= DNS_NAMEATTR_NOCOMPRESS;
1535135446Strhodes			rdataset = NULL;
1536135446Strhodes			free_rdataset = ISC_FALSE;
1537135446Strhodes			free_name = ISC_FALSE;
1538135446Strhodes		}
1539135446Strhodes
1540153816Sdougb		if (seen_problem) {
1541153816Sdougb			if (free_name)
1542153816Sdougb				isc_mempool_put(msg->namepool, name);
1543153816Sdougb			if (free_rdataset)
1544153816Sdougb				isc_mempool_put(msg->rdspool, rdataset);
1545153816Sdougb			free_name = free_rdataset = ISC_FALSE;
1546153816Sdougb		}
1547135446Strhodes		INSIST(free_name == ISC_FALSE);
1548135446Strhodes		INSIST(free_rdataset == ISC_FALSE);
1549135446Strhodes	}
1550135446Strhodes
1551135446Strhodes	if (seen_problem)
1552135446Strhodes		return (DNS_R_RECOVERABLE);
1553135446Strhodes	return (ISC_R_SUCCESS);
1554135446Strhodes
1555135446Strhodes cleanup:
1556135446Strhodes	if (free_name)
1557135446Strhodes		isc_mempool_put(msg->namepool, name);
1558135446Strhodes	if (free_rdataset)
1559135446Strhodes		isc_mempool_put(msg->rdspool, rdataset);
1560135446Strhodes
1561135446Strhodes	return (result);
1562135446Strhodes}
1563135446Strhodes
1564135446Strhodesisc_result_t
1565135446Strhodesdns_message_parse(dns_message_t *msg, isc_buffer_t *source,
1566135446Strhodes		  unsigned int options)
1567135446Strhodes{
1568135446Strhodes	isc_region_t r;
1569135446Strhodes	dns_decompress_t dctx;
1570135446Strhodes	isc_result_t ret;
1571135446Strhodes	isc_uint16_t tmpflags;
1572135446Strhodes	isc_buffer_t origsource;
1573135446Strhodes	isc_boolean_t seen_problem;
1574135446Strhodes	isc_boolean_t ignore_tc;
1575135446Strhodes
1576135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
1577135446Strhodes	REQUIRE(source != NULL);
1578135446Strhodes	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTPARSE);
1579135446Strhodes
1580135446Strhodes	seen_problem = ISC_FALSE;
1581135446Strhodes	ignore_tc = ISC_TF(options & DNS_MESSAGEPARSE_IGNORETRUNCATION);
1582135446Strhodes
1583135446Strhodes	origsource = *source;
1584135446Strhodes
1585135446Strhodes	msg->header_ok = 0;
1586135446Strhodes	msg->question_ok = 0;
1587135446Strhodes
1588135446Strhodes	isc_buffer_remainingregion(source, &r);
1589135446Strhodes	if (r.length < DNS_MESSAGE_HEADERLEN)
1590135446Strhodes		return (ISC_R_UNEXPECTEDEND);
1591135446Strhodes
1592135446Strhodes	msg->id = isc_buffer_getuint16(source);
1593135446Strhodes	tmpflags = isc_buffer_getuint16(source);
1594135446Strhodes	msg->opcode = ((tmpflags & DNS_MESSAGE_OPCODE_MASK)
1595135446Strhodes		       >> DNS_MESSAGE_OPCODE_SHIFT);
1596135446Strhodes	msg->rcode = (dns_rcode_t)(tmpflags & DNS_MESSAGE_RCODE_MASK);
1597135446Strhodes	msg->flags = (tmpflags & DNS_MESSAGE_FLAG_MASK);
1598135446Strhodes	msg->counts[DNS_SECTION_QUESTION] = isc_buffer_getuint16(source);
1599135446Strhodes	msg->counts[DNS_SECTION_ANSWER] = isc_buffer_getuint16(source);
1600135446Strhodes	msg->counts[DNS_SECTION_AUTHORITY] = isc_buffer_getuint16(source);
1601135446Strhodes	msg->counts[DNS_SECTION_ADDITIONAL] = isc_buffer_getuint16(source);
1602135446Strhodes
1603135446Strhodes	msg->header_ok = 1;
1604135446Strhodes
1605135446Strhodes	/*
1606135446Strhodes	 * -1 means no EDNS.
1607135446Strhodes	 */
1608135446Strhodes	dns_decompress_init(&dctx, -1, DNS_DECOMPRESS_ANY);
1609135446Strhodes
1610135446Strhodes	dns_decompress_setmethods(&dctx, DNS_COMPRESS_GLOBAL14);
1611135446Strhodes
1612135446Strhodes	ret = getquestions(source, msg, &dctx, options);
1613135446Strhodes	if (ret == ISC_R_UNEXPECTEDEND && ignore_tc)
1614135446Strhodes		goto truncated;
1615135446Strhodes	if (ret == DNS_R_RECOVERABLE) {
1616135446Strhodes		seen_problem = ISC_TRUE;
1617135446Strhodes		ret = ISC_R_SUCCESS;
1618135446Strhodes	}
1619135446Strhodes	if (ret != ISC_R_SUCCESS)
1620135446Strhodes		return (ret);
1621135446Strhodes	msg->question_ok = 1;
1622135446Strhodes
1623135446Strhodes	ret = getsection(source, msg, &dctx, DNS_SECTION_ANSWER, options);
1624135446Strhodes	if (ret == ISC_R_UNEXPECTEDEND && ignore_tc)
1625135446Strhodes		goto truncated;
1626135446Strhodes	if (ret == DNS_R_RECOVERABLE) {
1627135446Strhodes		seen_problem = ISC_TRUE;
1628135446Strhodes		ret = ISC_R_SUCCESS;
1629135446Strhodes	}
1630135446Strhodes	if (ret != ISC_R_SUCCESS)
1631135446Strhodes		return (ret);
1632135446Strhodes
1633135446Strhodes	ret = getsection(source, msg, &dctx, DNS_SECTION_AUTHORITY, options);
1634135446Strhodes	if (ret == ISC_R_UNEXPECTEDEND && ignore_tc)
1635135446Strhodes		goto truncated;
1636135446Strhodes	if (ret == DNS_R_RECOVERABLE) {
1637135446Strhodes		seen_problem = ISC_TRUE;
1638135446Strhodes		ret = ISC_R_SUCCESS;
1639135446Strhodes	}
1640135446Strhodes	if (ret != ISC_R_SUCCESS)
1641135446Strhodes		return (ret);
1642135446Strhodes
1643135446Strhodes	ret = getsection(source, msg, &dctx, DNS_SECTION_ADDITIONAL, options);
1644135446Strhodes	if (ret == ISC_R_UNEXPECTEDEND && ignore_tc)
1645135446Strhodes		goto truncated;
1646135446Strhodes	if (ret == DNS_R_RECOVERABLE) {
1647135446Strhodes		seen_problem = ISC_TRUE;
1648135446Strhodes		ret = ISC_R_SUCCESS;
1649135446Strhodes	}
1650135446Strhodes	if (ret != ISC_R_SUCCESS)
1651135446Strhodes		return (ret);
1652135446Strhodes
1653135446Strhodes	isc_buffer_remainingregion(source, &r);
1654135446Strhodes	if (r.length != 0) {
1655135446Strhodes		isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
1656135446Strhodes			      DNS_LOGMODULE_MESSAGE, ISC_LOG_DEBUG(3),
1657135446Strhodes			      "message has %u byte(s) of trailing garbage",
1658135446Strhodes			      r.length);
1659135446Strhodes	}
1660135446Strhodes
1661135446Strhodes truncated:
1662135446Strhodes	if ((options & DNS_MESSAGEPARSE_CLONEBUFFER) == 0)
1663135446Strhodes		isc_buffer_usedregion(&origsource, &msg->saved);
1664135446Strhodes	else {
1665135446Strhodes		msg->saved.length = isc_buffer_usedlength(&origsource);
1666135446Strhodes		msg->saved.base = isc_mem_get(msg->mctx, msg->saved.length);
1667135446Strhodes		if (msg->saved.base == NULL)
1668135446Strhodes			return (ISC_R_NOMEMORY);
1669135446Strhodes		memcpy(msg->saved.base, isc_buffer_base(&origsource),
1670135446Strhodes		       msg->saved.length);
1671135446Strhodes		msg->free_saved = 1;
1672135446Strhodes	}
1673135446Strhodes
1674135446Strhodes	if (ret == ISC_R_UNEXPECTEDEND && ignore_tc)
1675135446Strhodes		return (DNS_R_RECOVERABLE);
1676135446Strhodes	if (seen_problem == ISC_TRUE)
1677135446Strhodes		return (DNS_R_RECOVERABLE);
1678135446Strhodes	return (ISC_R_SUCCESS);
1679135446Strhodes}
1680135446Strhodes
1681135446Strhodesisc_result_t
1682135446Strhodesdns_message_renderbegin(dns_message_t *msg, dns_compress_t *cctx,
1683135446Strhodes			isc_buffer_t *buffer)
1684135446Strhodes{
1685135446Strhodes	isc_region_t r;
1686135446Strhodes
1687135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
1688135446Strhodes	REQUIRE(buffer != NULL);
1689135446Strhodes	REQUIRE(msg->buffer == NULL);
1690135446Strhodes	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
1691135446Strhodes
1692135446Strhodes	msg->cctx = cctx;
1693135446Strhodes
1694135446Strhodes	/*
1695135446Strhodes	 * Erase the contents of this buffer.
1696135446Strhodes	 */
1697135446Strhodes	isc_buffer_clear(buffer);
1698135446Strhodes
1699135446Strhodes	/*
1700135446Strhodes	 * Make certain there is enough for at least the header in this
1701135446Strhodes	 * buffer.
1702135446Strhodes	 */
1703135446Strhodes	isc_buffer_availableregion(buffer, &r);
1704135446Strhodes	if (r.length < DNS_MESSAGE_HEADERLEN)
1705135446Strhodes		return (ISC_R_NOSPACE);
1706135446Strhodes
1707135446Strhodes	if (r.length < msg->reserved)
1708135446Strhodes		return (ISC_R_NOSPACE);
1709135446Strhodes
1710135446Strhodes	/*
1711135446Strhodes	 * Reserve enough space for the header in this buffer.
1712135446Strhodes	 */
1713135446Strhodes	isc_buffer_add(buffer, DNS_MESSAGE_HEADERLEN);
1714135446Strhodes
1715135446Strhodes	msg->buffer = buffer;
1716135446Strhodes
1717135446Strhodes	return (ISC_R_SUCCESS);
1718135446Strhodes}
1719135446Strhodes
1720135446Strhodesisc_result_t
1721135446Strhodesdns_message_renderchangebuffer(dns_message_t *msg, isc_buffer_t *buffer) {
1722135446Strhodes	isc_region_t r, rn;
1723135446Strhodes
1724135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
1725135446Strhodes	REQUIRE(buffer != NULL);
1726135446Strhodes	REQUIRE(msg->buffer != NULL);
1727135446Strhodes
1728135446Strhodes	/*
1729135446Strhodes	 * Ensure that the new buffer is empty, and has enough space to
1730135446Strhodes	 * hold the current contents.
1731135446Strhodes	 */
1732135446Strhodes	isc_buffer_clear(buffer);
1733135446Strhodes
1734135446Strhodes	isc_buffer_availableregion(buffer, &rn);
1735135446Strhodes	isc_buffer_usedregion(msg->buffer, &r);
1736135446Strhodes	REQUIRE(rn.length > r.length);
1737135446Strhodes
1738135446Strhodes	/*
1739135446Strhodes	 * Copy the contents from the old to the new buffer.
1740135446Strhodes	 */
1741135446Strhodes	isc_buffer_add(buffer, r.length);
1742135446Strhodes	memcpy(rn.base, r.base, r.length);
1743135446Strhodes
1744135446Strhodes	msg->buffer = buffer;
1745135446Strhodes
1746135446Strhodes	return (ISC_R_SUCCESS);
1747135446Strhodes}
1748135446Strhodes
1749135446Strhodesvoid
1750135446Strhodesdns_message_renderrelease(dns_message_t *msg, unsigned int space) {
1751135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
1752135446Strhodes	REQUIRE(space <= msg->reserved);
1753135446Strhodes
1754135446Strhodes	msg->reserved -= space;
1755135446Strhodes}
1756135446Strhodes
1757135446Strhodesisc_result_t
1758135446Strhodesdns_message_renderreserve(dns_message_t *msg, unsigned int space) {
1759135446Strhodes	isc_region_t r;
1760135446Strhodes
1761135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
1762135446Strhodes
1763135446Strhodes	if (msg->buffer != NULL) {
1764135446Strhodes		isc_buffer_availableregion(msg->buffer, &r);
1765135446Strhodes		if (r.length < (space + msg->reserved))
1766135446Strhodes			return (ISC_R_NOSPACE);
1767135446Strhodes	}
1768135446Strhodes
1769135446Strhodes	msg->reserved += space;
1770135446Strhodes
1771135446Strhodes	return (ISC_R_SUCCESS);
1772135446Strhodes}
1773135446Strhodes
1774135446Strhodesstatic inline isc_boolean_t
1775135446Strhodeswrong_priority(dns_rdataset_t *rds, int pass, dns_rdatatype_t preferred_glue) {
1776135446Strhodes	int pass_needed;
1777135446Strhodes
1778135446Strhodes	/*
1779135446Strhodes	 * If we are not rendering class IN, this ordering is bogus.
1780135446Strhodes	 */
1781135446Strhodes	if (rds->rdclass != dns_rdataclass_in)
1782135446Strhodes		return (ISC_FALSE);
1783135446Strhodes
1784135446Strhodes	switch (rds->type) {
1785135446Strhodes	case dns_rdatatype_a:
1786135446Strhodes	case dns_rdatatype_aaaa:
1787135446Strhodes		if (preferred_glue == rds->type)
1788135446Strhodes			pass_needed = 4;
1789135446Strhodes		else
1790135446Strhodes			pass_needed = 3;
1791135446Strhodes		break;
1792135446Strhodes	case dns_rdatatype_rrsig:
1793135446Strhodes	case dns_rdatatype_dnskey:
1794135446Strhodes		pass_needed = 2;
1795135446Strhodes		break;
1796135446Strhodes	default:
1797135446Strhodes		pass_needed = 1;
1798135446Strhodes	}
1799135446Strhodes
1800135446Strhodes	if (pass_needed >= pass)
1801135446Strhodes		return (ISC_FALSE);
1802135446Strhodes
1803135446Strhodes	return (ISC_TRUE);
1804135446Strhodes}
1805135446Strhodes
1806224092Sdougb#ifdef ALLOW_FILTER_AAAA_ON_V4
1807224092Sdougb/*
1808224092Sdougb * Decide whether to not answer with an AAAA record and its RRSIG
1809224092Sdougb */
1810224092Sdougbstatic inline isc_boolean_t
1811224092Sdougbnorender_rdataset(const dns_rdataset_t *rdataset, unsigned int options)
1812224092Sdougb{
1813224092Sdougb	switch (rdataset->type) {
1814224092Sdougb	case dns_rdatatype_aaaa:
1815224092Sdougb		if ((options & DNS_MESSAGERENDER_FILTER_AAAA) == 0)
1816224092Sdougb			return (ISC_FALSE);
1817224092Sdougb		break;
1818224092Sdougb
1819224092Sdougb	case dns_rdatatype_rrsig:
1820224092Sdougb		if ((options & DNS_MESSAGERENDER_FILTER_AAAA) == 0 ||
1821224092Sdougb		    rdataset->covers != dns_rdatatype_aaaa)
1822224092Sdougb			return (ISC_FALSE);
1823224092Sdougb		break;
1824224092Sdougb
1825224092Sdougb	default:
1826224092Sdougb		return (ISC_FALSE);
1827224092Sdougb	}
1828224092Sdougb
1829224092Sdougb	if (rdataset->rdclass != dns_rdataclass_in)
1830224092Sdougb		return (ISC_FALSE);
1831224092Sdougb
1832224092Sdougb	return (ISC_TRUE);
1833224092Sdougb}
1834224092Sdougb
1835224092Sdougb#endif
1836135446Strhodesisc_result_t
1837135446Strhodesdns_message_rendersection(dns_message_t *msg, dns_section_t sectionid,
1838135446Strhodes			  unsigned int options)
1839135446Strhodes{
1840135446Strhodes	dns_namelist_t *section;
1841135446Strhodes	dns_name_t *name, *next_name;
1842135446Strhodes	dns_rdataset_t *rdataset, *next_rdataset;
1843135446Strhodes	unsigned int count, total;
1844135446Strhodes	isc_result_t result;
1845135446Strhodes	isc_buffer_t st; /* for rollbacks */
1846135446Strhodes	int pass;
1847135446Strhodes	isc_boolean_t partial = ISC_FALSE;
1848135446Strhodes	unsigned int rd_options;
1849135446Strhodes	dns_rdatatype_t preferred_glue = 0;
1850135446Strhodes
1851135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
1852135446Strhodes	REQUIRE(msg->buffer != NULL);
1853135446Strhodes	REQUIRE(VALID_NAMED_SECTION(sectionid));
1854135446Strhodes
1855135446Strhodes	section = &msg->sections[sectionid];
1856135446Strhodes
1857135446Strhodes	if ((sectionid == DNS_SECTION_ADDITIONAL)
1858135446Strhodes	    && (options & DNS_MESSAGERENDER_ORDERED) == 0) {
1859135446Strhodes		if ((options & DNS_MESSAGERENDER_PREFER_A) != 0) {
1860135446Strhodes			preferred_glue = dns_rdatatype_a;
1861135446Strhodes			pass = 4;
1862135446Strhodes		} else if ((options & DNS_MESSAGERENDER_PREFER_AAAA) != 0) {
1863135446Strhodes			preferred_glue = dns_rdatatype_aaaa;
1864135446Strhodes			pass = 4;
1865135446Strhodes		} else
1866135446Strhodes			pass = 3;
1867135446Strhodes	} else
1868135446Strhodes		pass = 1;
1869135446Strhodes
1870135446Strhodes	if ((options & DNS_MESSAGERENDER_OMITDNSSEC) == 0)
1871135446Strhodes		rd_options = 0;
1872135446Strhodes	else
1873135446Strhodes		rd_options = DNS_RDATASETTOWIRE_OMITDNSSEC;
1874135446Strhodes
1875135446Strhodes	/*
1876135446Strhodes	 * Shrink the space in the buffer by the reserved amount.
1877135446Strhodes	 */
1878135446Strhodes	msg->buffer->length -= msg->reserved;
1879135446Strhodes
1880135446Strhodes	total = 0;
1881135446Strhodes	if (msg->reserved == 0 && (options & DNS_MESSAGERENDER_PARTIAL) != 0)
1882135446Strhodes		partial = ISC_TRUE;
1883135446Strhodes
1884153816Sdougb	/*
1885153816Sdougb	 * Render required glue first.  Set TC if it won't fit.
1886153816Sdougb	 */
1887153816Sdougb	name = ISC_LIST_HEAD(*section);
1888153816Sdougb	if (name != NULL) {
1889153816Sdougb		rdataset = ISC_LIST_HEAD(name->list);
1890153816Sdougb		if (rdataset != NULL &&
1891153816Sdougb		    (rdataset->attributes & DNS_RDATASETATTR_REQUIREDGLUE) != 0 &&
1892153816Sdougb		    (rdataset->attributes & DNS_RDATASETATTR_RENDERED) == 0) {
1893165071Sdougb			const void *order_arg = msg->order_arg;
1894153816Sdougb			st = *(msg->buffer);
1895153816Sdougb			count = 0;
1896153816Sdougb			if (partial)
1897153816Sdougb				result = dns_rdataset_towirepartial(rdataset,
1898153816Sdougb								    name,
1899153816Sdougb								    msg->cctx,
1900153816Sdougb								    msg->buffer,
1901153816Sdougb								    msg->order,
1902153816Sdougb								    order_arg,
1903153816Sdougb								    rd_options,
1904153816Sdougb								    &count,
1905153816Sdougb								    NULL);
1906153816Sdougb			else
1907153816Sdougb				result = dns_rdataset_towiresorted(rdataset,
1908153816Sdougb								   name,
1909153816Sdougb								   msg->cctx,
1910153816Sdougb								   msg->buffer,
1911153816Sdougb								   msg->order,
1912153816Sdougb								   order_arg,
1913153816Sdougb								   rd_options,
1914153816Sdougb								   &count);
1915153816Sdougb			total += count;
1916153816Sdougb			if (partial && result == ISC_R_NOSPACE) {
1917153816Sdougb				msg->flags |= DNS_MESSAGEFLAG_TC;
1918153816Sdougb				msg->buffer->length += msg->reserved;
1919153816Sdougb				msg->counts[sectionid] += total;
1920153816Sdougb				return (result);
1921153816Sdougb			}
1922204619Sdougb			if (result == ISC_R_NOSPACE)
1923204619Sdougb				msg->flags |= DNS_MESSAGEFLAG_TC;
1924153816Sdougb			if (result != ISC_R_SUCCESS) {
1925153816Sdougb				INSIST(st.used < 65536);
1926153816Sdougb				dns_compress_rollback(msg->cctx,
1927153816Sdougb						      (isc_uint16_t)st.used);
1928153816Sdougb				*(msg->buffer) = st;  /* rollback */
1929153816Sdougb				msg->buffer->length += msg->reserved;
1930153816Sdougb				msg->counts[sectionid] += total;
1931153816Sdougb				return (result);
1932153816Sdougb			}
1933153816Sdougb			rdataset->attributes |= DNS_RDATASETATTR_RENDERED;
1934153816Sdougb		}
1935153816Sdougb	}
1936153816Sdougb
1937135446Strhodes	do {
1938135446Strhodes		name = ISC_LIST_HEAD(*section);
1939135446Strhodes		if (name == NULL) {
1940135446Strhodes			msg->buffer->length += msg->reserved;
1941135446Strhodes			msg->counts[sectionid] += total;
1942135446Strhodes			return (ISC_R_SUCCESS);
1943135446Strhodes		}
1944135446Strhodes
1945135446Strhodes		while (name != NULL) {
1946135446Strhodes			next_name = ISC_LIST_NEXT(name, link);
1947135446Strhodes
1948135446Strhodes			rdataset = ISC_LIST_HEAD(name->list);
1949135446Strhodes			while (rdataset != NULL) {
1950135446Strhodes				next_rdataset = ISC_LIST_NEXT(rdataset, link);
1951135446Strhodes
1952135446Strhodes				if ((rdataset->attributes &
1953135446Strhodes				     DNS_RDATASETATTR_RENDERED) != 0)
1954135446Strhodes					goto next;
1955135446Strhodes
1956135446Strhodes				if (((options & DNS_MESSAGERENDER_ORDERED)
1957135446Strhodes				     == 0)
1958135446Strhodes				    && (sectionid == DNS_SECTION_ADDITIONAL)
1959135446Strhodes				    && wrong_priority(rdataset, pass,
1960135446Strhodes						      preferred_glue))
1961135446Strhodes					goto next;
1962135446Strhodes
1963224092Sdougb#ifdef ALLOW_FILTER_AAAA_ON_V4
1964224092Sdougb				/*
1965224092Sdougb				 * Suppress AAAAs if asked and we are
1966224092Sdougb				 * not doing DNSSEC or are breaking DNSSEC.
1967224092Sdougb				 * Say so in the AD bit if we break DNSSEC.
1968224092Sdougb				 */
1969224092Sdougb				if (norender_rdataset(rdataset, options) &&
1970224092Sdougb				    sectionid != DNS_SECTION_QUESTION) {
1971224092Sdougb					if (sectionid == DNS_SECTION_ANSWER ||
1972224092Sdougb					    sectionid == DNS_SECTION_AUTHORITY)
1973224092Sdougb					    msg->flags &= ~DNS_MESSAGEFLAG_AD;
1974224092Sdougb					if (OPTOUT(rdataset))
1975224092Sdougb					    msg->flags &= ~DNS_MESSAGEFLAG_AD;
1976224092Sdougb					goto next;
1977224092Sdougb				}
1978224092Sdougb
1979224092Sdougb#endif
1980135446Strhodes				st = *(msg->buffer);
1981135446Strhodes
1982135446Strhodes				count = 0;
1983135446Strhodes				if (partial)
1984135446Strhodes					result = dns_rdataset_towirepartial(
1985135446Strhodes							  rdataset,
1986135446Strhodes							  name,
1987135446Strhodes							  msg->cctx,
1988135446Strhodes							  msg->buffer,
1989135446Strhodes							  msg->order,
1990135446Strhodes							  msg->order_arg,
1991135446Strhodes							  rd_options,
1992135446Strhodes							  &count,
1993135446Strhodes							  NULL);
1994135446Strhodes				else
1995135446Strhodes					result = dns_rdataset_towiresorted(
1996135446Strhodes							  rdataset,
1997135446Strhodes							  name,
1998135446Strhodes							  msg->cctx,
1999135446Strhodes							  msg->buffer,
2000135446Strhodes							  msg->order,
2001135446Strhodes							  msg->order_arg,
2002135446Strhodes							  rd_options,
2003135446Strhodes							  &count);
2004135446Strhodes
2005135446Strhodes				total += count;
2006135446Strhodes
2007135446Strhodes				/*
2008135446Strhodes				 * If out of space, record stats on what we
2009135446Strhodes				 * rendered so far, and return that status.
2010135446Strhodes				 *
2011135446Strhodes				 * XXXMLG Need to change this when
2012135446Strhodes				 * dns_rdataset_towire() can render partial
2013193149Sdougb				 * sets starting at some arbitrary point in the
2014135446Strhodes				 * set.  This will include setting a bit in the
2015135446Strhodes				 * rdataset to indicate that a partial
2016135446Strhodes				 * rendering was done, and some state saved
2017135446Strhodes				 * somewhere (probably in the message struct)
2018135446Strhodes				 * to indicate where to continue from.
2019135446Strhodes				 */
2020135446Strhodes				if (partial && result == ISC_R_NOSPACE) {
2021135446Strhodes					msg->buffer->length += msg->reserved;
2022135446Strhodes					msg->counts[sectionid] += total;
2023135446Strhodes					return (result);
2024135446Strhodes				}
2025135446Strhodes				if (result != ISC_R_SUCCESS) {
2026135446Strhodes					INSIST(st.used < 65536);
2027135446Strhodes					dns_compress_rollback(msg->cctx,
2028135446Strhodes							(isc_uint16_t)st.used);
2029135446Strhodes					*(msg->buffer) = st;  /* rollback */
2030135446Strhodes					msg->buffer->length += msg->reserved;
2031135446Strhodes					msg->counts[sectionid] += total;
2032135446Strhodes					return (result);
2033135446Strhodes				}
2034135446Strhodes
2035135446Strhodes				/*
2036135446Strhodes				 * If we have rendered non-validated data,
2037135446Strhodes				 * ensure that the AD bit is not set.
2038135446Strhodes				 */
2039135446Strhodes				if (rdataset->trust != dns_trust_secure &&
2040135446Strhodes				    (sectionid == DNS_SECTION_ANSWER ||
2041135446Strhodes				     sectionid == DNS_SECTION_AUTHORITY))
2042135446Strhodes					msg->flags &= ~DNS_MESSAGEFLAG_AD;
2043193149Sdougb				if (OPTOUT(rdataset))
2044193149Sdougb					msg->flags &= ~DNS_MESSAGEFLAG_AD;
2045135446Strhodes
2046135446Strhodes				rdataset->attributes |=
2047135446Strhodes					DNS_RDATASETATTR_RENDERED;
2048135446Strhodes
2049135446Strhodes			next:
2050135446Strhodes				rdataset = next_rdataset;
2051135446Strhodes			}
2052135446Strhodes
2053135446Strhodes			name = next_name;
2054135446Strhodes		}
2055135446Strhodes	} while (--pass != 0);
2056135446Strhodes
2057135446Strhodes	msg->buffer->length += msg->reserved;
2058135446Strhodes	msg->counts[sectionid] += total;
2059135446Strhodes
2060135446Strhodes	return (ISC_R_SUCCESS);
2061135446Strhodes}
2062135446Strhodes
2063135446Strhodesvoid
2064135446Strhodesdns_message_renderheader(dns_message_t *msg, isc_buffer_t *target) {
2065135446Strhodes	isc_uint16_t tmp;
2066135446Strhodes	isc_region_t r;
2067135446Strhodes
2068135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2069135446Strhodes	REQUIRE(target != NULL);
2070135446Strhodes
2071135446Strhodes	isc_buffer_availableregion(target, &r);
2072135446Strhodes	REQUIRE(r.length >= DNS_MESSAGE_HEADERLEN);
2073135446Strhodes
2074135446Strhodes	isc_buffer_putuint16(target, msg->id);
2075135446Strhodes
2076135446Strhodes	tmp = ((msg->opcode << DNS_MESSAGE_OPCODE_SHIFT)
2077135446Strhodes	       & DNS_MESSAGE_OPCODE_MASK);
2078135446Strhodes	tmp |= (msg->rcode & DNS_MESSAGE_RCODE_MASK);
2079135446Strhodes	tmp |= (msg->flags & DNS_MESSAGE_FLAG_MASK);
2080135446Strhodes
2081135446Strhodes	INSIST(msg->counts[DNS_SECTION_QUESTION]  < 65536 &&
2082135446Strhodes	       msg->counts[DNS_SECTION_ANSWER]    < 65536 &&
2083135446Strhodes	       msg->counts[DNS_SECTION_AUTHORITY] < 65536 &&
2084135446Strhodes	       msg->counts[DNS_SECTION_ADDITIONAL] < 65536);
2085135446Strhodes
2086135446Strhodes	isc_buffer_putuint16(target, tmp);
2087135446Strhodes	isc_buffer_putuint16(target,
2088135446Strhodes			    (isc_uint16_t)msg->counts[DNS_SECTION_QUESTION]);
2089135446Strhodes	isc_buffer_putuint16(target,
2090135446Strhodes			    (isc_uint16_t)msg->counts[DNS_SECTION_ANSWER]);
2091135446Strhodes	isc_buffer_putuint16(target,
2092135446Strhodes			    (isc_uint16_t)msg->counts[DNS_SECTION_AUTHORITY]);
2093135446Strhodes	isc_buffer_putuint16(target,
2094135446Strhodes			    (isc_uint16_t)msg->counts[DNS_SECTION_ADDITIONAL]);
2095135446Strhodes}
2096135446Strhodes
2097135446Strhodesisc_result_t
2098135446Strhodesdns_message_renderend(dns_message_t *msg) {
2099135446Strhodes	isc_buffer_t tmpbuf;
2100135446Strhodes	isc_region_t r;
2101135446Strhodes	int result;
2102135446Strhodes	unsigned int count;
2103135446Strhodes
2104135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2105135446Strhodes	REQUIRE(msg->buffer != NULL);
2106135446Strhodes
2107135446Strhodes	if ((msg->rcode & ~DNS_MESSAGE_RCODE_MASK) != 0 && msg->opt == NULL) {
2108135446Strhodes		/*
2109135446Strhodes		 * We have an extended rcode but are not using EDNS.
2110135446Strhodes		 */
2111135446Strhodes		return (DNS_R_FORMERR);
2112135446Strhodes	}
2113135446Strhodes
2114135446Strhodes	/*
2115135446Strhodes	 * If we've got an OPT record, render it.
2116135446Strhodes	 */
2117135446Strhodes	if (msg->opt != NULL) {
2118135446Strhodes		dns_message_renderrelease(msg, msg->opt_reserved);
2119135446Strhodes		msg->opt_reserved = 0;
2120135446Strhodes		/*
2121135446Strhodes		 * Set the extended rcode.
2122135446Strhodes		 */
2123135446Strhodes		msg->opt->ttl &= ~DNS_MESSAGE_EDNSRCODE_MASK;
2124135446Strhodes		msg->opt->ttl |= ((msg->rcode << 20) &
2125135446Strhodes				  DNS_MESSAGE_EDNSRCODE_MASK);
2126135446Strhodes		/*
2127135446Strhodes		 * Render.
2128135446Strhodes		 */
2129135446Strhodes		count = 0;
2130135446Strhodes		result = dns_rdataset_towire(msg->opt, dns_rootname,
2131135446Strhodes					     msg->cctx, msg->buffer, 0,
2132135446Strhodes					     &count);
2133135446Strhodes		msg->counts[DNS_SECTION_ADDITIONAL] += count;
2134135446Strhodes		if (result != ISC_R_SUCCESS)
2135135446Strhodes			return (result);
2136135446Strhodes	}
2137135446Strhodes
2138135446Strhodes	/*
2139135446Strhodes	 * If we're adding a TSIG or SIG(0) to a truncated message,
2140135446Strhodes	 * clear all rdatasets from the message except for the question
2141135446Strhodes	 * before adding the TSIG or SIG(0).  If the question doesn't fit,
2142135446Strhodes	 * don't include it.
2143135446Strhodes	 */
2144135446Strhodes	if ((msg->tsigkey != NULL || msg->sig0key != NULL) &&
2145135446Strhodes	    (msg->flags & DNS_MESSAGEFLAG_TC) != 0)
2146135446Strhodes	{
2147135446Strhodes		isc_buffer_t *buf;
2148135446Strhodes
2149135446Strhodes		msgresetnames(msg, DNS_SECTION_ANSWER);
2150135446Strhodes		buf = msg->buffer;
2151135446Strhodes		dns_message_renderreset(msg);
2152135446Strhodes		msg->buffer = buf;
2153135446Strhodes		isc_buffer_clear(msg->buffer);
2154135446Strhodes		isc_buffer_add(msg->buffer, DNS_MESSAGE_HEADERLEN);
2155135446Strhodes		dns_compress_rollback(msg->cctx, 0);
2156135446Strhodes		result = dns_message_rendersection(msg, DNS_SECTION_QUESTION,
2157135446Strhodes						   0);
2158135446Strhodes		if (result != ISC_R_SUCCESS && result != ISC_R_NOSPACE)
2159135446Strhodes			return (result);
2160135446Strhodes	}
2161135446Strhodes
2162135446Strhodes	/*
2163135446Strhodes	 * If we're adding a TSIG record, generate and render it.
2164135446Strhodes	 */
2165135446Strhodes	if (msg->tsigkey != NULL) {
2166135446Strhodes		dns_message_renderrelease(msg, msg->sig_reserved);
2167135446Strhodes		msg->sig_reserved = 0;
2168135446Strhodes		result = dns_tsig_sign(msg);
2169135446Strhodes		if (result != ISC_R_SUCCESS)
2170135446Strhodes			return (result);
2171135446Strhodes		count = 0;
2172135446Strhodes		result = dns_rdataset_towire(msg->tsig, msg->tsigname,
2173135446Strhodes					     msg->cctx, msg->buffer, 0,
2174135446Strhodes					     &count);
2175135446Strhodes		msg->counts[DNS_SECTION_ADDITIONAL] += count;
2176135446Strhodes		if (result != ISC_R_SUCCESS)
2177135446Strhodes			return (result);
2178135446Strhodes	}
2179135446Strhodes
2180135446Strhodes	/*
2181135446Strhodes	 * If we're adding a SIG(0) record, generate and render it.
2182135446Strhodes	 */
2183135446Strhodes	if (msg->sig0key != NULL) {
2184135446Strhodes		dns_message_renderrelease(msg, msg->sig_reserved);
2185135446Strhodes		msg->sig_reserved = 0;
2186135446Strhodes		result = dns_dnssec_signmessage(msg, msg->sig0key);
2187135446Strhodes		if (result != ISC_R_SUCCESS)
2188135446Strhodes			return (result);
2189135446Strhodes		count = 0;
2190135446Strhodes		/*
2191135446Strhodes		 * Note: dns_rootname is used here, not msg->sig0name, since
2192135446Strhodes		 * the owner name of a SIG(0) is irrelevant, and will not
2193135446Strhodes		 * be set in a message being rendered.
2194135446Strhodes		 */
2195135446Strhodes		result = dns_rdataset_towire(msg->sig0, dns_rootname,
2196135446Strhodes					     msg->cctx, msg->buffer, 0,
2197135446Strhodes					     &count);
2198135446Strhodes		msg->counts[DNS_SECTION_ADDITIONAL] += count;
2199135446Strhodes		if (result != ISC_R_SUCCESS)
2200135446Strhodes			return (result);
2201135446Strhodes	}
2202135446Strhodes
2203135446Strhodes	isc_buffer_usedregion(msg->buffer, &r);
2204135446Strhodes	isc_buffer_init(&tmpbuf, r.base, r.length);
2205135446Strhodes
2206135446Strhodes	dns_message_renderheader(msg, &tmpbuf);
2207135446Strhodes
2208135446Strhodes	msg->buffer = NULL;  /* forget about this buffer only on success XXX */
2209135446Strhodes
2210135446Strhodes	return (ISC_R_SUCCESS);
2211135446Strhodes}
2212135446Strhodes
2213135446Strhodesvoid
2214135446Strhodesdns_message_renderreset(dns_message_t *msg) {
2215135446Strhodes	unsigned int i;
2216135446Strhodes	dns_name_t *name;
2217135446Strhodes	dns_rdataset_t *rds;
2218135446Strhodes
2219135446Strhodes	/*
2220135446Strhodes	 * Reset the message so that it may be rendered again.
2221135446Strhodes	 */
2222135446Strhodes
2223135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2224135446Strhodes	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
2225135446Strhodes
2226135446Strhodes	msg->buffer = NULL;
2227135446Strhodes
2228135446Strhodes	for (i = 0; i < DNS_SECTION_MAX; i++) {
2229135446Strhodes		msg->cursors[i] = NULL;
2230135446Strhodes		msg->counts[i] = 0;
2231135446Strhodes		for (name = ISC_LIST_HEAD(msg->sections[i]);
2232135446Strhodes		     name != NULL;
2233135446Strhodes		     name = ISC_LIST_NEXT(name, link)) {
2234135446Strhodes			for (rds = ISC_LIST_HEAD(name->list);
2235135446Strhodes			     rds != NULL;
2236135446Strhodes			     rds = ISC_LIST_NEXT(rds, link)) {
2237135446Strhodes				rds->attributes &= ~DNS_RDATASETATTR_RENDERED;
2238135446Strhodes			}
2239135446Strhodes		}
2240135446Strhodes	}
2241135446Strhodes	if (msg->tsigname != NULL)
2242135446Strhodes		dns_message_puttempname(msg, &msg->tsigname);
2243135446Strhodes	if (msg->tsig != NULL) {
2244135446Strhodes		dns_rdataset_disassociate(msg->tsig);
2245135446Strhodes		dns_message_puttemprdataset(msg, &msg->tsig);
2246135446Strhodes	}
2247135446Strhodes	if (msg->sig0 != NULL) {
2248135446Strhodes		dns_rdataset_disassociate(msg->sig0);
2249135446Strhodes		dns_message_puttemprdataset(msg, &msg->sig0);
2250135446Strhodes	}
2251135446Strhodes}
2252135446Strhodes
2253135446Strhodesisc_result_t
2254135446Strhodesdns_message_firstname(dns_message_t *msg, dns_section_t section) {
2255135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2256135446Strhodes	REQUIRE(VALID_NAMED_SECTION(section));
2257135446Strhodes
2258135446Strhodes	msg->cursors[section] = ISC_LIST_HEAD(msg->sections[section]);
2259135446Strhodes
2260135446Strhodes	if (msg->cursors[section] == NULL)
2261135446Strhodes		return (ISC_R_NOMORE);
2262135446Strhodes
2263135446Strhodes	return (ISC_R_SUCCESS);
2264135446Strhodes}
2265135446Strhodes
2266135446Strhodesisc_result_t
2267135446Strhodesdns_message_nextname(dns_message_t *msg, dns_section_t section) {
2268135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2269135446Strhodes	REQUIRE(VALID_NAMED_SECTION(section));
2270135446Strhodes	REQUIRE(msg->cursors[section] != NULL);
2271135446Strhodes
2272135446Strhodes	msg->cursors[section] = ISC_LIST_NEXT(msg->cursors[section], link);
2273135446Strhodes
2274135446Strhodes	if (msg->cursors[section] == NULL)
2275135446Strhodes		return (ISC_R_NOMORE);
2276135446Strhodes
2277135446Strhodes	return (ISC_R_SUCCESS);
2278135446Strhodes}
2279135446Strhodes
2280135446Strhodesvoid
2281135446Strhodesdns_message_currentname(dns_message_t *msg, dns_section_t section,
2282135446Strhodes			dns_name_t **name)
2283135446Strhodes{
2284135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2285135446Strhodes	REQUIRE(VALID_NAMED_SECTION(section));
2286135446Strhodes	REQUIRE(name != NULL && *name == NULL);
2287135446Strhodes	REQUIRE(msg->cursors[section] != NULL);
2288135446Strhodes
2289135446Strhodes	*name = msg->cursors[section];
2290135446Strhodes}
2291135446Strhodes
2292135446Strhodesisc_result_t
2293135446Strhodesdns_message_findname(dns_message_t *msg, dns_section_t section,
2294135446Strhodes		     dns_name_t *target, dns_rdatatype_t type,
2295135446Strhodes		     dns_rdatatype_t covers, dns_name_t **name,
2296135446Strhodes		     dns_rdataset_t **rdataset)
2297135446Strhodes{
2298135446Strhodes	dns_name_t *foundname;
2299135446Strhodes	isc_result_t result;
2300135446Strhodes
2301135446Strhodes	/*
2302135446Strhodes	 * XXX These requirements are probably too intensive, especially
2303135446Strhodes	 * where things can be NULL, but as they are they ensure that if
2304135446Strhodes	 * something is NON-NULL, indicating that the caller expects it
2305135446Strhodes	 * to be filled in, that we can in fact fill it in.
2306135446Strhodes	 */
2307135446Strhodes	REQUIRE(msg != NULL);
2308135446Strhodes	REQUIRE(VALID_SECTION(section));
2309135446Strhodes	REQUIRE(target != NULL);
2310135446Strhodes	if (name != NULL)
2311135446Strhodes		REQUIRE(*name == NULL);
2312135446Strhodes	if (type == dns_rdatatype_any) {
2313135446Strhodes		REQUIRE(rdataset == NULL);
2314135446Strhodes	} else {
2315135446Strhodes		if (rdataset != NULL)
2316135446Strhodes			REQUIRE(*rdataset == NULL);
2317135446Strhodes	}
2318135446Strhodes
2319135446Strhodes	result = findname(&foundname, target,
2320135446Strhodes			  &msg->sections[section]);
2321135446Strhodes
2322135446Strhodes	if (result == ISC_R_NOTFOUND)
2323135446Strhodes		return (DNS_R_NXDOMAIN);
2324135446Strhodes	else if (result != ISC_R_SUCCESS)
2325135446Strhodes		return (result);
2326135446Strhodes
2327135446Strhodes	if (name != NULL)
2328135446Strhodes		*name = foundname;
2329135446Strhodes
2330135446Strhodes	/*
2331135446Strhodes	 * And now look for the type.
2332135446Strhodes	 */
2333135446Strhodes	if (type == dns_rdatatype_any)
2334135446Strhodes		return (ISC_R_SUCCESS);
2335135446Strhodes
2336135446Strhodes	result = dns_message_findtype(foundname, type, covers, rdataset);
2337135446Strhodes	if (result == ISC_R_NOTFOUND)
2338135446Strhodes		return (DNS_R_NXRRSET);
2339135446Strhodes
2340135446Strhodes	return (result);
2341135446Strhodes}
2342135446Strhodes
2343135446Strhodesvoid
2344135446Strhodesdns_message_movename(dns_message_t *msg, dns_name_t *name,
2345135446Strhodes		     dns_section_t fromsection,
2346135446Strhodes		     dns_section_t tosection)
2347135446Strhodes{
2348135446Strhodes	REQUIRE(msg != NULL);
2349135446Strhodes	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
2350135446Strhodes	REQUIRE(name != NULL);
2351135446Strhodes	REQUIRE(VALID_NAMED_SECTION(fromsection));
2352135446Strhodes	REQUIRE(VALID_NAMED_SECTION(tosection));
2353135446Strhodes
2354135446Strhodes	/*
2355135446Strhodes	 * Unlink the name from the old section
2356135446Strhodes	 */
2357135446Strhodes	ISC_LIST_UNLINK(msg->sections[fromsection], name, link);
2358135446Strhodes	ISC_LIST_APPEND(msg->sections[tosection], name, link);
2359135446Strhodes}
2360135446Strhodes
2361135446Strhodesvoid
2362135446Strhodesdns_message_addname(dns_message_t *msg, dns_name_t *name,
2363135446Strhodes		    dns_section_t section)
2364135446Strhodes{
2365135446Strhodes	REQUIRE(msg != NULL);
2366135446Strhodes	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
2367135446Strhodes	REQUIRE(name != NULL);
2368135446Strhodes	REQUIRE(VALID_NAMED_SECTION(section));
2369135446Strhodes
2370135446Strhodes	ISC_LIST_APPEND(msg->sections[section], name, link);
2371135446Strhodes}
2372135446Strhodes
2373170222Sdougbvoid
2374170222Sdougbdns_message_removename(dns_message_t *msg, dns_name_t *name,
2375170222Sdougb		       dns_section_t section)
2376170222Sdougb{
2377170222Sdougb	REQUIRE(msg != NULL);
2378170222Sdougb	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
2379170222Sdougb	REQUIRE(name != NULL);
2380170222Sdougb	REQUIRE(VALID_NAMED_SECTION(section));
2381170222Sdougb
2382170222Sdougb	ISC_LIST_UNLINK(msg->sections[section], name, link);
2383170222Sdougb}
2384170222Sdougb
2385135446Strhodesisc_result_t
2386135446Strhodesdns_message_gettempname(dns_message_t *msg, dns_name_t **item) {
2387135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2388135446Strhodes	REQUIRE(item != NULL && *item == NULL);
2389135446Strhodes
2390135446Strhodes	*item = isc_mempool_get(msg->namepool);
2391135446Strhodes	if (*item == NULL)
2392135446Strhodes		return (ISC_R_NOMEMORY);
2393135446Strhodes	dns_name_init(*item, NULL);
2394135446Strhodes
2395135446Strhodes	return (ISC_R_SUCCESS);
2396135446Strhodes}
2397135446Strhodes
2398135446Strhodesisc_result_t
2399135446Strhodesdns_message_gettempoffsets(dns_message_t *msg, dns_offsets_t **item) {
2400135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2401135446Strhodes	REQUIRE(item != NULL && *item == NULL);
2402135446Strhodes
2403135446Strhodes	*item = newoffsets(msg);
2404135446Strhodes	if (*item == NULL)
2405135446Strhodes		return (ISC_R_NOMEMORY);
2406135446Strhodes
2407135446Strhodes	return (ISC_R_SUCCESS);
2408135446Strhodes}
2409135446Strhodes
2410135446Strhodesisc_result_t
2411135446Strhodesdns_message_gettemprdata(dns_message_t *msg, dns_rdata_t **item) {
2412135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2413135446Strhodes	REQUIRE(item != NULL && *item == NULL);
2414135446Strhodes
2415135446Strhodes	*item = newrdata(msg);
2416135446Strhodes	if (*item == NULL)
2417135446Strhodes		return (ISC_R_NOMEMORY);
2418135446Strhodes
2419135446Strhodes	return (ISC_R_SUCCESS);
2420135446Strhodes}
2421135446Strhodes
2422135446Strhodesisc_result_t
2423135446Strhodesdns_message_gettemprdataset(dns_message_t *msg, dns_rdataset_t **item) {
2424135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2425135446Strhodes	REQUIRE(item != NULL && *item == NULL);
2426135446Strhodes
2427135446Strhodes	*item = isc_mempool_get(msg->rdspool);
2428135446Strhodes	if (*item == NULL)
2429135446Strhodes		return (ISC_R_NOMEMORY);
2430135446Strhodes
2431135446Strhodes	dns_rdataset_init(*item);
2432135446Strhodes
2433135446Strhodes	return (ISC_R_SUCCESS);
2434135446Strhodes}
2435135446Strhodes
2436135446Strhodesisc_result_t
2437135446Strhodesdns_message_gettemprdatalist(dns_message_t *msg, dns_rdatalist_t **item) {
2438135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2439135446Strhodes	REQUIRE(item != NULL && *item == NULL);
2440135446Strhodes
2441135446Strhodes	*item = newrdatalist(msg);
2442135446Strhodes	if (*item == NULL)
2443135446Strhodes		return (ISC_R_NOMEMORY);
2444135446Strhodes
2445135446Strhodes	return (ISC_R_SUCCESS);
2446135446Strhodes}
2447135446Strhodes
2448135446Strhodesvoid
2449135446Strhodesdns_message_puttempname(dns_message_t *msg, dns_name_t **item) {
2450135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2451135446Strhodes	REQUIRE(item != NULL && *item != NULL);
2452135446Strhodes
2453135446Strhodes	if (dns_name_dynamic(*item))
2454135446Strhodes		dns_name_free(*item, msg->mctx);
2455135446Strhodes	isc_mempool_put(msg->namepool, *item);
2456135446Strhodes	*item = NULL;
2457135446Strhodes}
2458135446Strhodes
2459135446Strhodesvoid
2460135446Strhodesdns_message_puttemprdata(dns_message_t *msg, dns_rdata_t **item) {
2461135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2462135446Strhodes	REQUIRE(item != NULL && *item != NULL);
2463135446Strhodes
2464135446Strhodes	releaserdata(msg, *item);
2465135446Strhodes	*item = NULL;
2466135446Strhodes}
2467135446Strhodes
2468135446Strhodesvoid
2469135446Strhodesdns_message_puttemprdataset(dns_message_t *msg, dns_rdataset_t **item) {
2470135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2471135446Strhodes	REQUIRE(item != NULL && *item != NULL);
2472135446Strhodes
2473135446Strhodes	REQUIRE(!dns_rdataset_isassociated(*item));
2474135446Strhodes	isc_mempool_put(msg->rdspool, *item);
2475135446Strhodes	*item = NULL;
2476135446Strhodes}
2477135446Strhodes
2478135446Strhodesvoid
2479135446Strhodesdns_message_puttemprdatalist(dns_message_t *msg, dns_rdatalist_t **item) {
2480135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2481135446Strhodes	REQUIRE(item != NULL && *item != NULL);
2482135446Strhodes
2483135446Strhodes	releaserdatalist(msg, *item);
2484135446Strhodes	*item = NULL;
2485135446Strhodes}
2486135446Strhodes
2487135446Strhodesisc_result_t
2488135446Strhodesdns_message_peekheader(isc_buffer_t *source, dns_messageid_t *idp,
2489135446Strhodes		       unsigned int *flagsp)
2490135446Strhodes{
2491135446Strhodes	isc_region_t r;
2492135446Strhodes	isc_buffer_t buffer;
2493135446Strhodes	dns_messageid_t id;
2494135446Strhodes	unsigned int flags;
2495135446Strhodes
2496135446Strhodes	REQUIRE(source != NULL);
2497135446Strhodes
2498135446Strhodes	buffer = *source;
2499135446Strhodes
2500135446Strhodes	isc_buffer_remainingregion(&buffer, &r);
2501135446Strhodes	if (r.length < DNS_MESSAGE_HEADERLEN)
2502135446Strhodes		return (ISC_R_UNEXPECTEDEND);
2503135446Strhodes
2504135446Strhodes	id = isc_buffer_getuint16(&buffer);
2505135446Strhodes	flags = isc_buffer_getuint16(&buffer);
2506135446Strhodes	flags &= DNS_MESSAGE_FLAG_MASK;
2507135446Strhodes
2508135446Strhodes	if (flagsp != NULL)
2509135446Strhodes		*flagsp = flags;
2510135446Strhodes	if (idp != NULL)
2511135446Strhodes		*idp = id;
2512135446Strhodes
2513135446Strhodes	return (ISC_R_SUCCESS);
2514135446Strhodes}
2515135446Strhodes
2516135446Strhodesisc_result_t
2517135446Strhodesdns_message_reply(dns_message_t *msg, isc_boolean_t want_question_section) {
2518223812Sdougb	unsigned int clear_after;
2519135446Strhodes	isc_result_t result;
2520135446Strhodes
2521135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2522135446Strhodes	REQUIRE((msg->flags & DNS_MESSAGEFLAG_QR) == 0);
2523135446Strhodes
2524135446Strhodes	if (!msg->header_ok)
2525135446Strhodes		return (DNS_R_FORMERR);
2526135446Strhodes	if (msg->opcode != dns_opcode_query &&
2527135446Strhodes	    msg->opcode != dns_opcode_notify)
2528135446Strhodes		want_question_section = ISC_FALSE;
2529218384Sdougb	if (msg->opcode == dns_opcode_update)
2530223812Sdougb		clear_after = DNS_SECTION_PREREQUISITE;
2531218384Sdougb	else if (want_question_section) {
2532135446Strhodes		if (!msg->question_ok)
2533135446Strhodes			return (DNS_R_FORMERR);
2534223812Sdougb		clear_after = DNS_SECTION_ANSWER;
2535135446Strhodes	} else
2536223812Sdougb		clear_after = DNS_SECTION_QUESTION;
2537135446Strhodes	msg->from_to_wire = DNS_MESSAGE_INTENTRENDER;
2538223812Sdougb	msgresetnames(msg, clear_after);
2539135446Strhodes	msgresetopt(msg);
2540135446Strhodes	msgresetsigs(msg, ISC_TRUE);
2541135446Strhodes	msginitprivate(msg);
2542135446Strhodes	/*
2543135446Strhodes	 * We now clear most flags and then set QR, ensuring that the
2544135446Strhodes	 * reply's flags will be in a reasonable state.
2545135446Strhodes	 */
2546135446Strhodes	msg->flags &= DNS_MESSAGE_REPLYPRESERVE;
2547135446Strhodes	msg->flags |= DNS_MESSAGEFLAG_QR;
2548135446Strhodes
2549135446Strhodes	/*
2550135446Strhodes	 * This saves the query TSIG status, if the query was signed, and
2551135446Strhodes	 * reserves space in the reply for the TSIG.
2552135446Strhodes	 */
2553135446Strhodes	if (msg->tsigkey != NULL) {
2554135446Strhodes		unsigned int otherlen = 0;
2555135446Strhodes		msg->querytsigstatus = msg->tsigstatus;
2556135446Strhodes		msg->tsigstatus = dns_rcode_noerror;
2557135446Strhodes		if (msg->querytsigstatus == dns_tsigerror_badtime)
2558135446Strhodes			otherlen = 6;
2559135446Strhodes		msg->sig_reserved = spacefortsig(msg->tsigkey, otherlen);
2560135446Strhodes		result = dns_message_renderreserve(msg, msg->sig_reserved);
2561135446Strhodes		if (result != ISC_R_SUCCESS) {
2562135446Strhodes			msg->sig_reserved = 0;
2563135446Strhodes			return (result);
2564135446Strhodes		}
2565135446Strhodes	}
2566135446Strhodes	if (msg->saved.base != NULL) {
2567135446Strhodes		msg->query.base = msg->saved.base;
2568135446Strhodes		msg->query.length = msg->saved.length;
2569135446Strhodes		msg->free_query = msg->free_saved;
2570135446Strhodes		msg->saved.base = NULL;
2571135446Strhodes		msg->saved.length = 0;
2572135446Strhodes		msg->free_saved = 0;
2573135446Strhodes	}
2574135446Strhodes
2575135446Strhodes	return (ISC_R_SUCCESS);
2576135446Strhodes}
2577135446Strhodes
2578135446Strhodesdns_rdataset_t *
2579135446Strhodesdns_message_getopt(dns_message_t *msg) {
2580135446Strhodes
2581135446Strhodes	/*
2582135446Strhodes	 * Get the OPT record for 'msg'.
2583135446Strhodes	 */
2584135446Strhodes
2585135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2586135446Strhodes
2587135446Strhodes	return (msg->opt);
2588135446Strhodes}
2589135446Strhodes
2590135446Strhodesisc_result_t
2591135446Strhodesdns_message_setopt(dns_message_t *msg, dns_rdataset_t *opt) {
2592135446Strhodes	isc_result_t result;
2593135446Strhodes	dns_rdata_t rdata = DNS_RDATA_INIT;
2594135446Strhodes
2595135446Strhodes	/*
2596135446Strhodes	 * Set the OPT record for 'msg'.
2597135446Strhodes	 */
2598135446Strhodes
2599135446Strhodes	/*
2600135446Strhodes	 * The space required for an OPT record is:
2601135446Strhodes	 *
2602135446Strhodes	 *	1 byte for the name
2603135446Strhodes	 *	2 bytes for the type
2604135446Strhodes	 *	2 bytes for the class
2605135446Strhodes	 *	4 bytes for the ttl
2606135446Strhodes	 *	2 bytes for the rdata length
2607135446Strhodes	 * ---------------------------------
2608135446Strhodes	 *     11 bytes
2609135446Strhodes	 *
2610135446Strhodes	 * plus the length of the rdata.
2611135446Strhodes	 */
2612135446Strhodes
2613135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2614135446Strhodes	REQUIRE(opt->type == dns_rdatatype_opt);
2615135446Strhodes	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
2616135446Strhodes	REQUIRE(msg->state == DNS_SECTION_ANY);
2617135446Strhodes
2618135446Strhodes	msgresetopt(msg);
2619135446Strhodes
2620135446Strhodes	result = dns_rdataset_first(opt);
2621135446Strhodes	if (result != ISC_R_SUCCESS)
2622135446Strhodes		goto cleanup;
2623135446Strhodes	dns_rdataset_current(opt, &rdata);
2624135446Strhodes	msg->opt_reserved = 11 + rdata.length;
2625135446Strhodes	result = dns_message_renderreserve(msg, msg->opt_reserved);
2626135446Strhodes	if (result != ISC_R_SUCCESS) {
2627135446Strhodes		msg->opt_reserved = 0;
2628135446Strhodes		goto cleanup;
2629135446Strhodes	}
2630135446Strhodes
2631135446Strhodes	msg->opt = opt;
2632135446Strhodes
2633135446Strhodes	return (ISC_R_SUCCESS);
2634135446Strhodes
2635135446Strhodes cleanup:
2636135446Strhodes	dns_message_puttemprdataset(msg, &opt);
2637135446Strhodes	return (result);
2638135446Strhodes
2639135446Strhodes}
2640135446Strhodes
2641135446Strhodesdns_rdataset_t *
2642135446Strhodesdns_message_gettsig(dns_message_t *msg, dns_name_t **owner) {
2643135446Strhodes
2644135446Strhodes	/*
2645135446Strhodes	 * Get the TSIG record and owner for 'msg'.
2646135446Strhodes	 */
2647135446Strhodes
2648135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2649135446Strhodes	REQUIRE(owner == NULL || *owner == NULL);
2650135446Strhodes
2651135446Strhodes	if (owner != NULL)
2652135446Strhodes		*owner = msg->tsigname;
2653135446Strhodes	return (msg->tsig);
2654135446Strhodes}
2655135446Strhodes
2656135446Strhodesisc_result_t
2657135446Strhodesdns_message_settsigkey(dns_message_t *msg, dns_tsigkey_t *key) {
2658135446Strhodes	isc_result_t result;
2659135446Strhodes
2660135446Strhodes	/*
2661135446Strhodes	 * Set the TSIG key for 'msg'
2662135446Strhodes	 */
2663135446Strhodes
2664135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2665135446Strhodes	REQUIRE(msg->state == DNS_SECTION_ANY);
2666135446Strhodes
2667135446Strhodes	if (key == NULL && msg->tsigkey != NULL) {
2668135446Strhodes		if (msg->sig_reserved != 0) {
2669135446Strhodes			dns_message_renderrelease(msg, msg->sig_reserved);
2670135446Strhodes			msg->sig_reserved = 0;
2671135446Strhodes		}
2672135446Strhodes		dns_tsigkey_detach(&msg->tsigkey);
2673135446Strhodes	}
2674135446Strhodes	if (key != NULL) {
2675135446Strhodes		REQUIRE(msg->tsigkey == NULL && msg->sig0key == NULL);
2676135446Strhodes		dns_tsigkey_attach(key, &msg->tsigkey);
2677135446Strhodes		if (msg->from_to_wire == DNS_MESSAGE_INTENTRENDER) {
2678135446Strhodes			msg->sig_reserved = spacefortsig(msg->tsigkey, 0);
2679135446Strhodes			result = dns_message_renderreserve(msg,
2680135446Strhodes							   msg->sig_reserved);
2681135446Strhodes			if (result != ISC_R_SUCCESS) {
2682135446Strhodes				dns_tsigkey_detach(&msg->tsigkey);
2683135446Strhodes				msg->sig_reserved = 0;
2684135446Strhodes				return (result);
2685135446Strhodes			}
2686135446Strhodes		}
2687135446Strhodes	}
2688135446Strhodes	return (ISC_R_SUCCESS);
2689135446Strhodes}
2690135446Strhodes
2691135446Strhodesdns_tsigkey_t *
2692135446Strhodesdns_message_gettsigkey(dns_message_t *msg) {
2693135446Strhodes
2694135446Strhodes	/*
2695135446Strhodes	 * Get the TSIG key for 'msg'
2696135446Strhodes	 */
2697135446Strhodes
2698135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2699135446Strhodes
2700135446Strhodes	return (msg->tsigkey);
2701135446Strhodes}
2702135446Strhodes
2703135446Strhodesisc_result_t
2704135446Strhodesdns_message_setquerytsig(dns_message_t *msg, isc_buffer_t *querytsig) {
2705135446Strhodes	dns_rdata_t *rdata = NULL;
2706135446Strhodes	dns_rdatalist_t *list = NULL;
2707135446Strhodes	dns_rdataset_t *set = NULL;
2708135446Strhodes	isc_buffer_t *buf = NULL;
2709135446Strhodes	isc_region_t r;
2710135446Strhodes	isc_result_t result;
2711135446Strhodes
2712135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2713135446Strhodes	REQUIRE(msg->querytsig == NULL);
2714135446Strhodes
2715135446Strhodes	if (querytsig == NULL)
2716135446Strhodes		return (ISC_R_SUCCESS);
2717135446Strhodes
2718135446Strhodes	result = dns_message_gettemprdata(msg, &rdata);
2719135446Strhodes	if (result != ISC_R_SUCCESS)
2720135446Strhodes		goto cleanup;
2721135446Strhodes
2722135446Strhodes	result = dns_message_gettemprdatalist(msg, &list);
2723135446Strhodes	if (result != ISC_R_SUCCESS)
2724135446Strhodes		goto cleanup;
2725135446Strhodes	result = dns_message_gettemprdataset(msg, &set);
2726135446Strhodes	if (result != ISC_R_SUCCESS)
2727135446Strhodes		goto cleanup;
2728135446Strhodes
2729135446Strhodes	isc_buffer_usedregion(querytsig, &r);
2730135446Strhodes	result = isc_buffer_allocate(msg->mctx, &buf, r.length);
2731135446Strhodes	if (result != ISC_R_SUCCESS)
2732135446Strhodes		goto cleanup;
2733135446Strhodes	isc_buffer_putmem(buf, r.base, r.length);
2734135446Strhodes	isc_buffer_usedregion(buf, &r);
2735135446Strhodes	dns_rdata_init(rdata);
2736135446Strhodes	dns_rdata_fromregion(rdata, dns_rdataclass_any, dns_rdatatype_tsig, &r);
2737135446Strhodes	dns_message_takebuffer(msg, &buf);
2738135446Strhodes	ISC_LIST_INIT(list->rdata);
2739135446Strhodes	ISC_LIST_APPEND(list->rdata, rdata, link);
2740135446Strhodes	result = dns_rdatalist_tordataset(list, set);
2741135446Strhodes	if (result != ISC_R_SUCCESS)
2742135446Strhodes		goto cleanup;
2743135446Strhodes
2744135446Strhodes	msg->querytsig = set;
2745135446Strhodes
2746135446Strhodes	return (result);
2747135446Strhodes
2748135446Strhodes cleanup:
2749135446Strhodes	if (rdata != NULL)
2750135446Strhodes		dns_message_puttemprdata(msg, &rdata);
2751135446Strhodes	if (list != NULL)
2752135446Strhodes		dns_message_puttemprdatalist(msg, &list);
2753135446Strhodes	if (set != NULL)
2754135446Strhodes		dns_message_puttemprdataset(msg, &set);
2755135446Strhodes	return (ISC_R_NOMEMORY);
2756135446Strhodes}
2757135446Strhodes
2758135446Strhodesisc_result_t
2759135446Strhodesdns_message_getquerytsig(dns_message_t *msg, isc_mem_t *mctx,
2760135446Strhodes			 isc_buffer_t **querytsig) {
2761135446Strhodes	isc_result_t result;
2762135446Strhodes	dns_rdata_t rdata = DNS_RDATA_INIT;
2763135446Strhodes	isc_region_t r;
2764135446Strhodes
2765135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2766135446Strhodes	REQUIRE(mctx != NULL);
2767135446Strhodes	REQUIRE(querytsig != NULL && *querytsig == NULL);
2768135446Strhodes
2769135446Strhodes	if (msg->tsig == NULL)
2770135446Strhodes		return (ISC_R_SUCCESS);
2771135446Strhodes
2772135446Strhodes	result = dns_rdataset_first(msg->tsig);
2773135446Strhodes	if (result != ISC_R_SUCCESS)
2774135446Strhodes		return (result);
2775135446Strhodes	dns_rdataset_current(msg->tsig, &rdata);
2776135446Strhodes	dns_rdata_toregion(&rdata, &r);
2777135446Strhodes
2778135446Strhodes	result = isc_buffer_allocate(mctx, querytsig, r.length);
2779135446Strhodes	if (result != ISC_R_SUCCESS)
2780135446Strhodes		return (result);
2781135446Strhodes	isc_buffer_putmem(*querytsig, r.base, r.length);
2782135446Strhodes	return (ISC_R_SUCCESS);
2783135446Strhodes}
2784135446Strhodes
2785135446Strhodesdns_rdataset_t *
2786135446Strhodesdns_message_getsig0(dns_message_t *msg, dns_name_t **owner) {
2787135446Strhodes
2788135446Strhodes	/*
2789135446Strhodes	 * Get the SIG(0) record for 'msg'.
2790135446Strhodes	 */
2791135446Strhodes
2792135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2793135446Strhodes	REQUIRE(owner == NULL || *owner == NULL);
2794135446Strhodes
2795135446Strhodes	if (msg->sig0 != NULL && owner != NULL) {
2796135446Strhodes		/* If dns_message_getsig0 is called on a rendered message
2797135446Strhodes		 * after the SIG(0) has been applied, we need to return the
2798135446Strhodes		 * root name, not NULL.
2799135446Strhodes		 */
2800135446Strhodes		if (msg->sig0name == NULL)
2801135446Strhodes			*owner = dns_rootname;
2802135446Strhodes		else
2803135446Strhodes			*owner = msg->sig0name;
2804135446Strhodes	}
2805135446Strhodes	return (msg->sig0);
2806135446Strhodes}
2807135446Strhodes
2808135446Strhodesisc_result_t
2809135446Strhodesdns_message_setsig0key(dns_message_t *msg, dst_key_t *key) {
2810135446Strhodes	isc_region_t r;
2811135446Strhodes	unsigned int x;
2812135446Strhodes	isc_result_t result;
2813135446Strhodes
2814135446Strhodes	/*
2815135446Strhodes	 * Set the SIG(0) key for 'msg'
2816135446Strhodes	 */
2817135446Strhodes
2818135446Strhodes	/*
2819135446Strhodes	 * The space required for an SIG(0) record is:
2820135446Strhodes	 *
2821135446Strhodes	 *	1 byte for the name
2822135446Strhodes	 *	2 bytes for the type
2823135446Strhodes	 *	2 bytes for the class
2824135446Strhodes	 *	4 bytes for the ttl
2825135446Strhodes	 *	2 bytes for the type covered
2826135446Strhodes	 *	1 byte for the algorithm
2827135446Strhodes	 *	1 bytes for the labels
2828135446Strhodes	 *	4 bytes for the original ttl
2829135446Strhodes	 *	4 bytes for the signature expiration
2830135446Strhodes	 *	4 bytes for the signature inception
2831135446Strhodes	 *	2 bytes for the key tag
2832135446Strhodes	 *	n bytes for the signer's name
2833135446Strhodes	 *	x bytes for the signature
2834135446Strhodes	 * ---------------------------------
2835135446Strhodes	 *     27 + n + x bytes
2836135446Strhodes	 */
2837135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2838135446Strhodes	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
2839135446Strhodes	REQUIRE(msg->state == DNS_SECTION_ANY);
2840135446Strhodes
2841135446Strhodes	if (key != NULL) {
2842135446Strhodes		REQUIRE(msg->sig0key == NULL && msg->tsigkey == NULL);
2843135446Strhodes		dns_name_toregion(dst_key_name(key), &r);
2844135446Strhodes		result = dst_key_sigsize(key, &x);
2845135446Strhodes		if (result != ISC_R_SUCCESS) {
2846135446Strhodes			msg->sig_reserved = 0;
2847135446Strhodes			return (result);
2848135446Strhodes		}
2849135446Strhodes		msg->sig_reserved = 27 + r.length + x;
2850135446Strhodes		result = dns_message_renderreserve(msg, msg->sig_reserved);
2851135446Strhodes		if (result != ISC_R_SUCCESS) {
2852135446Strhodes			msg->sig_reserved = 0;
2853135446Strhodes			return (result);
2854135446Strhodes		}
2855135446Strhodes		msg->sig0key = key;
2856135446Strhodes	}
2857135446Strhodes	return (ISC_R_SUCCESS);
2858135446Strhodes}
2859135446Strhodes
2860135446Strhodesdst_key_t *
2861135446Strhodesdns_message_getsig0key(dns_message_t *msg) {
2862135446Strhodes
2863135446Strhodes	/*
2864135446Strhodes	 * Get the SIG(0) key for 'msg'
2865135446Strhodes	 */
2866135446Strhodes
2867135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2868135446Strhodes
2869135446Strhodes	return (msg->sig0key);
2870135446Strhodes}
2871135446Strhodes
2872135446Strhodesvoid
2873135446Strhodesdns_message_takebuffer(dns_message_t *msg, isc_buffer_t **buffer) {
2874135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2875135446Strhodes	REQUIRE(buffer != NULL);
2876135446Strhodes	REQUIRE(ISC_BUFFER_VALID(*buffer));
2877135446Strhodes
2878135446Strhodes	ISC_LIST_APPEND(msg->cleanup, *buffer, link);
2879135446Strhodes	*buffer = NULL;
2880135446Strhodes}
2881135446Strhodes
2882135446Strhodesisc_result_t
2883135446Strhodesdns_message_signer(dns_message_t *msg, dns_name_t *signer) {
2884135446Strhodes	isc_result_t result = ISC_R_SUCCESS;
2885135446Strhodes	dns_rdata_t rdata = DNS_RDATA_INIT;
2886135446Strhodes
2887135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2888135446Strhodes	REQUIRE(signer != NULL);
2889135446Strhodes	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTPARSE);
2890135446Strhodes
2891135446Strhodes	if (msg->tsig == NULL && msg->sig0 == NULL)
2892135446Strhodes		return (ISC_R_NOTFOUND);
2893135446Strhodes
2894135446Strhodes	if (msg->verify_attempted == 0)
2895135446Strhodes		return (DNS_R_NOTVERIFIEDYET);
2896135446Strhodes
2897135446Strhodes	if (!dns_name_hasbuffer(signer)) {
2898135446Strhodes		isc_buffer_t *dynbuf = NULL;
2899135446Strhodes		result = isc_buffer_allocate(msg->mctx, &dynbuf, 512);
2900135446Strhodes		if (result != ISC_R_SUCCESS)
2901135446Strhodes			return (result);
2902135446Strhodes		dns_name_setbuffer(signer, dynbuf);
2903135446Strhodes		dns_message_takebuffer(msg, &dynbuf);
2904135446Strhodes	}
2905135446Strhodes
2906135446Strhodes	if (msg->sig0 != NULL) {
2907135446Strhodes		dns_rdata_sig_t sig;
2908135446Strhodes
2909135446Strhodes		result = dns_rdataset_first(msg->sig0);
2910135446Strhodes		INSIST(result == ISC_R_SUCCESS);
2911135446Strhodes		dns_rdataset_current(msg->sig0, &rdata);
2912135446Strhodes
2913135446Strhodes		result = dns_rdata_tostruct(&rdata, &sig, NULL);
2914135446Strhodes		if (result != ISC_R_SUCCESS)
2915135446Strhodes			return (result);
2916135446Strhodes
2917135446Strhodes		if (msg->verified_sig && msg->sig0status == dns_rcode_noerror)
2918135446Strhodes			result = ISC_R_SUCCESS;
2919135446Strhodes		else
2920135446Strhodes			result = DNS_R_SIGINVALID;
2921135446Strhodes		dns_name_clone(&sig.signer, signer);
2922135446Strhodes		dns_rdata_freestruct(&sig);
2923135446Strhodes	} else {
2924135446Strhodes		dns_name_t *identity;
2925135446Strhodes		dns_rdata_any_tsig_t tsig;
2926135446Strhodes
2927135446Strhodes		result = dns_rdataset_first(msg->tsig);
2928135446Strhodes		INSIST(result == ISC_R_SUCCESS);
2929135446Strhodes		dns_rdataset_current(msg->tsig, &rdata);
2930135446Strhodes
2931135446Strhodes		result = dns_rdata_tostruct(&rdata, &tsig, NULL);
2932225361Sdougb		INSIST(result == ISC_R_SUCCESS);
2933135446Strhodes		if (msg->tsigstatus != dns_rcode_noerror)
2934135446Strhodes			result = DNS_R_TSIGVERIFYFAILURE;
2935135446Strhodes		else if (tsig.error != dns_rcode_noerror)
2936135446Strhodes			result = DNS_R_TSIGERRORSET;
2937135446Strhodes		else
2938135446Strhodes			result = ISC_R_SUCCESS;
2939135446Strhodes		dns_rdata_freestruct(&tsig);
2940135446Strhodes
2941135446Strhodes		if (msg->tsigkey == NULL) {
2942135446Strhodes			/*
2943135446Strhodes			 * If msg->tsigstatus & tsig.error are both
2944135446Strhodes			 * dns_rcode_noerror, the message must have been
2945135446Strhodes			 * verified, which means msg->tsigkey will be
2946135446Strhodes			 * non-NULL.
2947135446Strhodes			 */
2948135446Strhodes			INSIST(result != ISC_R_SUCCESS);
2949135446Strhodes		} else {
2950135446Strhodes			identity = dns_tsigkey_identity(msg->tsigkey);
2951135446Strhodes			if (identity == NULL) {
2952135446Strhodes				if (result == ISC_R_SUCCESS)
2953135446Strhodes					result = DNS_R_NOIDENTITY;
2954135446Strhodes				identity = &msg->tsigkey->name;
2955135446Strhodes			}
2956135446Strhodes			dns_name_clone(identity, signer);
2957135446Strhodes		}
2958135446Strhodes	}
2959135446Strhodes
2960135446Strhodes	return (result);
2961135446Strhodes}
2962135446Strhodes
2963135446Strhodesvoid
2964135446Strhodesdns_message_resetsig(dns_message_t *msg) {
2965135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2966135446Strhodes	msg->verified_sig = 0;
2967135446Strhodes	msg->verify_attempted = 0;
2968135446Strhodes	msg->tsigstatus = dns_rcode_noerror;
2969135446Strhodes	msg->sig0status = dns_rcode_noerror;
2970135446Strhodes	msg->timeadjust = 0;
2971135446Strhodes	if (msg->tsigkey != NULL) {
2972135446Strhodes		dns_tsigkey_detach(&msg->tsigkey);
2973135446Strhodes		msg->tsigkey = NULL;
2974135446Strhodes	}
2975135446Strhodes}
2976135446Strhodes
2977135446Strhodesisc_result_t
2978135446Strhodesdns_message_rechecksig(dns_message_t *msg, dns_view_t *view) {
2979135446Strhodes	dns_message_resetsig(msg);
2980135446Strhodes	return (dns_message_checksig(msg, view));
2981135446Strhodes}
2982135446Strhodes
2983193149Sdougb#ifdef SKAN_MSG_DEBUG
2984193149Sdougbvoid
2985193149Sdougbdns_message_dumpsig(dns_message_t *msg, char *txt1) {
2986193149Sdougb	dns_rdata_t querytsigrdata = DNS_RDATA_INIT;
2987193149Sdougb	dns_rdata_any_tsig_t querytsig;
2988193149Sdougb	isc_result_t result;
2989193149Sdougb
2990193149Sdougb	if (msg->tsig != NULL) {
2991193149Sdougb		result = dns_rdataset_first(msg->tsig);
2992193149Sdougb		RUNTIME_CHECK(result == ISC_R_SUCCESS);
2993193149Sdougb		dns_rdataset_current(msg->tsig, &querytsigrdata);
2994193149Sdougb		result = dns_rdata_tostruct(&querytsigrdata, &querytsig, NULL);
2995193149Sdougb		RUNTIME_CHECK(result == ISC_R_SUCCESS);
2996193149Sdougb		hexdump(txt1, "TSIG", querytsig.signature,
2997193149Sdougb			querytsig.siglen);
2998193149Sdougb	}
2999193149Sdougb
3000193149Sdougb	if (msg->querytsig != NULL) {
3001193149Sdougb		result = dns_rdataset_first(msg->querytsig);
3002193149Sdougb		RUNTIME_CHECK(result == ISC_R_SUCCESS);
3003193149Sdougb		dns_rdataset_current(msg->querytsig, &querytsigrdata);
3004193149Sdougb		result = dns_rdata_tostruct(&querytsigrdata, &querytsig, NULL);
3005193149Sdougb		RUNTIME_CHECK(result == ISC_R_SUCCESS);
3006193149Sdougb		hexdump(txt1, "QUERYTSIG", querytsig.signature,
3007193149Sdougb			querytsig.siglen);
3008193149Sdougb	}
3009193149Sdougb}
3010193149Sdougb#endif
3011193149Sdougb
3012135446Strhodesisc_result_t
3013135446Strhodesdns_message_checksig(dns_message_t *msg, dns_view_t *view) {
3014135446Strhodes	isc_buffer_t b, msgb;
3015135446Strhodes
3016135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
3017135446Strhodes
3018135446Strhodes	if (msg->tsigkey == NULL && msg->tsig == NULL && msg->sig0 == NULL)
3019135446Strhodes		return (ISC_R_SUCCESS);
3020193149Sdougb
3021135446Strhodes	INSIST(msg->saved.base != NULL);
3022135446Strhodes	isc_buffer_init(&msgb, msg->saved.base, msg->saved.length);
3023135446Strhodes	isc_buffer_add(&msgb, msg->saved.length);
3024135446Strhodes	if (msg->tsigkey != NULL || msg->tsig != NULL) {
3025193149Sdougb#ifdef SKAN_MSG_DEBUG
3026193149Sdougb		dns_message_dumpsig(msg, "dns_message_checksig#1");
3027193149Sdougb#endif
3028135446Strhodes		if (view != NULL)
3029135446Strhodes			return (dns_view_checksig(view, &msgb, msg));
3030135446Strhodes		else
3031135446Strhodes			return (dns_tsig_verify(&msgb, msg, NULL, NULL));
3032135446Strhodes	} else {
3033135446Strhodes		dns_rdata_t rdata = DNS_RDATA_INIT;
3034135446Strhodes		dns_rdata_sig_t sig;
3035135446Strhodes		dns_rdataset_t keyset;
3036135446Strhodes		isc_result_t result;
3037135446Strhodes
3038135446Strhodes		result = dns_rdataset_first(msg->sig0);
3039135446Strhodes		INSIST(result == ISC_R_SUCCESS);
3040135446Strhodes		dns_rdataset_current(msg->sig0, &rdata);
3041135446Strhodes
3042135446Strhodes		/*
3043135446Strhodes		 * This can occur when the message is a dynamic update, since
3044135446Strhodes		 * the rdata length checking is relaxed.  This should not
3045135446Strhodes		 * happen in a well-formed message, since the SIG(0) is only
3046135446Strhodes		 * looked for in the additional section, and the dynamic update
3047135446Strhodes		 * meta-records are in the prerequisite and update sections.
3048135446Strhodes		 */
3049135446Strhodes		if (rdata.length == 0)
3050135446Strhodes			return (ISC_R_UNEXPECTEDEND);
3051135446Strhodes
3052135446Strhodes		result = dns_rdata_tostruct(&rdata, &sig, msg->mctx);
3053135446Strhodes		if (result != ISC_R_SUCCESS)
3054135446Strhodes			return (result);
3055135446Strhodes
3056135446Strhodes		dns_rdataset_init(&keyset);
3057135446Strhodes		if (view == NULL)
3058135446Strhodes			return (DNS_R_KEYUNAUTHORIZED);
3059135446Strhodes		result = dns_view_simplefind(view, &sig.signer,
3060135446Strhodes					     dns_rdatatype_key /* SIG(0) */,
3061135446Strhodes					     0, 0, ISC_FALSE, &keyset, NULL);
3062135446Strhodes
3063135446Strhodes		if (result != ISC_R_SUCCESS) {
3064135446Strhodes			/* XXXBEW Should possibly create a fetch here */
3065135446Strhodes			result = DNS_R_KEYUNAUTHORIZED;
3066135446Strhodes			goto freesig;
3067135446Strhodes		} else if (keyset.trust < dns_trust_secure) {
3068135446Strhodes			/* XXXBEW Should call a validator here */
3069135446Strhodes			result = DNS_R_KEYUNAUTHORIZED;
3070135446Strhodes			goto freesig;
3071135446Strhodes		}
3072135446Strhodes		result = dns_rdataset_first(&keyset);
3073135446Strhodes		INSIST(result == ISC_R_SUCCESS);
3074135446Strhodes		for (;
3075135446Strhodes		     result == ISC_R_SUCCESS;
3076135446Strhodes		     result = dns_rdataset_next(&keyset))
3077135446Strhodes		{
3078135446Strhodes			dst_key_t *key = NULL;
3079135446Strhodes
3080193149Sdougb			dns_rdata_reset(&rdata);
3081135446Strhodes			dns_rdataset_current(&keyset, &rdata);
3082135446Strhodes			isc_buffer_init(&b, rdata.data, rdata.length);
3083135446Strhodes			isc_buffer_add(&b, rdata.length);
3084135446Strhodes
3085135446Strhodes			result = dst_key_fromdns(&sig.signer, rdata.rdclass,
3086135446Strhodes						 &b, view->mctx, &key);
3087135446Strhodes			if (result != ISC_R_SUCCESS)
3088135446Strhodes				continue;
3089135446Strhodes			if (dst_key_alg(key) != sig.algorithm ||
3090135446Strhodes			    dst_key_id(key) != sig.keyid ||
3091135446Strhodes			    !(dst_key_proto(key) == DNS_KEYPROTO_DNSSEC ||
3092135446Strhodes			      dst_key_proto(key) == DNS_KEYPROTO_ANY))
3093135446Strhodes			{
3094135446Strhodes				dst_key_free(&key);
3095135446Strhodes				continue;
3096135446Strhodes			}
3097135446Strhodes			result = dns_dnssec_verifymessage(&msgb, msg, key);
3098135446Strhodes			dst_key_free(&key);
3099135446Strhodes			if (result == ISC_R_SUCCESS)
3100135446Strhodes				break;
3101135446Strhodes		}
3102135446Strhodes		if (result == ISC_R_NOMORE)
3103135446Strhodes			result = DNS_R_KEYUNAUTHORIZED;
3104135446Strhodes
3105135446Strhodes freesig:
3106135446Strhodes		if (dns_rdataset_isassociated(&keyset))
3107135446Strhodes			dns_rdataset_disassociate(&keyset);
3108135446Strhodes		dns_rdata_freestruct(&sig);
3109135446Strhodes		return (result);
3110135446Strhodes	}
3111135446Strhodes}
3112135446Strhodes
3113135446Strhodesisc_result_t
3114135446Strhodesdns_message_sectiontotext(dns_message_t *msg, dns_section_t section,
3115135446Strhodes			  const dns_master_style_t *style,
3116135446Strhodes			  dns_messagetextflag_t flags,
3117135446Strhodes			  isc_buffer_t *target) {
3118135446Strhodes	dns_name_t *name, empty_name;
3119135446Strhodes	dns_rdataset_t *rdataset;
3120135446Strhodes	isc_result_t result;
3121224092Sdougb	isc_boolean_t seensoa = ISC_FALSE;
3122135446Strhodes
3123135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
3124135446Strhodes	REQUIRE(target != NULL);
3125135446Strhodes	REQUIRE(VALID_SECTION(section));
3126135446Strhodes
3127135446Strhodes	if (ISC_LIST_EMPTY(msg->sections[section]))
3128135446Strhodes		return (ISC_R_SUCCESS);
3129135446Strhodes
3130135446Strhodes	if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) {
3131135446Strhodes		ADD_STRING(target, ";; ");
3132135446Strhodes		if (msg->opcode != dns_opcode_update) {
3133135446Strhodes			ADD_STRING(target, sectiontext[section]);
3134174187Sdougb		} else {
3135135446Strhodes			ADD_STRING(target, updsectiontext[section]);
3136135446Strhodes		}
3137135446Strhodes		ADD_STRING(target, " SECTION:\n");
3138135446Strhodes	}
3139135446Strhodes
3140135446Strhodes	dns_name_init(&empty_name, NULL);
3141135446Strhodes	result = dns_message_firstname(msg, section);
3142135446Strhodes	if (result != ISC_R_SUCCESS) {
3143135446Strhodes		return (result);
3144135446Strhodes	}
3145135446Strhodes	do {
3146135446Strhodes		name = NULL;
3147135446Strhodes		dns_message_currentname(msg, section, &name);
3148135446Strhodes		for (rdataset = ISC_LIST_HEAD(name->list);
3149135446Strhodes		     rdataset != NULL;
3150135446Strhodes		     rdataset = ISC_LIST_NEXT(rdataset, link)) {
3151224092Sdougb			if (section == DNS_SECTION_ANSWER &&
3152224092Sdougb			    rdataset->type == dns_rdatatype_soa) {
3153224092Sdougb				if ((flags & DNS_MESSAGETEXTFLAG_OMITSOA) != 0)
3154224092Sdougb					continue;
3155224092Sdougb				if (seensoa &&
3156224092Sdougb				    (flags & DNS_MESSAGETEXTFLAG_ONESOA) != 0)
3157224092Sdougb					continue;
3158224092Sdougb				seensoa = ISC_TRUE;
3159224092Sdougb			}
3160135446Strhodes			if (section == DNS_SECTION_QUESTION) {
3161135446Strhodes				ADD_STRING(target, ";");
3162135446Strhodes				result = dns_master_questiontotext(name,
3163135446Strhodes								   rdataset,
3164135446Strhodes								   style,
3165135446Strhodes								   target);
3166135446Strhodes			} else {
3167135446Strhodes				result = dns_master_rdatasettotext(name,
3168135446Strhodes								   rdataset,
3169135446Strhodes								   style,
3170135446Strhodes								   target);
3171135446Strhodes			}
3172135446Strhodes			if (result != ISC_R_SUCCESS)
3173135446Strhodes				return (result);
3174135446Strhodes		}
3175135446Strhodes		result = dns_message_nextname(msg, section);
3176135446Strhodes	} while (result == ISC_R_SUCCESS);
3177135446Strhodes	if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0 &&
3178135446Strhodes	    (flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
3179135446Strhodes		ADD_STRING(target, "\n");
3180135446Strhodes	if (result == ISC_R_NOMORE)
3181135446Strhodes		result = ISC_R_SUCCESS;
3182135446Strhodes	return (result);
3183135446Strhodes}
3184135446Strhodes
3185135446Strhodesisc_result_t
3186135446Strhodesdns_message_pseudosectiontotext(dns_message_t *msg,
3187135446Strhodes				dns_pseudosection_t section,
3188135446Strhodes				const dns_master_style_t *style,
3189135446Strhodes				dns_messagetextflag_t flags,
3190135446Strhodes				isc_buffer_t *target) {
3191135446Strhodes	dns_rdataset_t *ps = NULL;
3192135446Strhodes	dns_name_t *name = NULL;
3193135446Strhodes	isc_result_t result;
3194135446Strhodes	char buf[sizeof("1234567890")];
3195135446Strhodes	isc_uint32_t mbz;
3196193149Sdougb	dns_rdata_t rdata;
3197193149Sdougb	isc_buffer_t optbuf;
3198193149Sdougb	isc_uint16_t optcode, optlen;
3199193149Sdougb	unsigned char *optdata;
3200135446Strhodes
3201135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
3202135446Strhodes	REQUIRE(target != NULL);
3203135446Strhodes	REQUIRE(VALID_PSEUDOSECTION(section));
3204135446Strhodes
3205135446Strhodes	switch (section) {
3206135446Strhodes	case DNS_PSEUDOSECTION_OPT:
3207135446Strhodes		ps = dns_message_getopt(msg);
3208135446Strhodes		if (ps == NULL)
3209135446Strhodes			return (ISC_R_SUCCESS);
3210135446Strhodes		if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
3211135446Strhodes			ADD_STRING(target, ";; OPT PSEUDOSECTION:\n");
3212135446Strhodes		ADD_STRING(target, "; EDNS: version: ");
3213135446Strhodes		snprintf(buf, sizeof(buf), "%u",
3214135446Strhodes			 (unsigned int)((ps->ttl & 0x00ff0000) >> 16));
3215135446Strhodes		ADD_STRING(target, buf);
3216135446Strhodes		ADD_STRING(target, ", flags:");
3217135446Strhodes		if ((ps->ttl & DNS_MESSAGEEXTFLAG_DO) != 0)
3218135446Strhodes			ADD_STRING(target, " do");
3219218384Sdougb		mbz = ps->ttl & 0xffff;
3220218384Sdougb		mbz &= ~DNS_MESSAGEEXTFLAG_DO;		/* Known Flags. */
3221135446Strhodes		if (mbz != 0) {
3222135446Strhodes			ADD_STRING(target, "; MBZ: ");
3223135446Strhodes			snprintf(buf, sizeof(buf), "%.4x ", mbz);
3224135446Strhodes			ADD_STRING(target, buf);
3225135446Strhodes			ADD_STRING(target, ", udp: ");
3226135446Strhodes		} else
3227135446Strhodes			ADD_STRING(target, "; udp: ");
3228135446Strhodes		snprintf(buf, sizeof(buf), "%u\n", (unsigned int)ps->rdclass);
3229135446Strhodes		ADD_STRING(target, buf);
3230193149Sdougb
3231193149Sdougb		result = dns_rdataset_first(ps);
3232193149Sdougb		if (result != ISC_R_SUCCESS)
3233193149Sdougb			return (ISC_R_SUCCESS);
3234193149Sdougb
3235193149Sdougb		/* Print EDNS info, if any */
3236193149Sdougb		dns_rdata_init(&rdata);
3237193149Sdougb		dns_rdataset_current(ps, &rdata);
3238193149Sdougb
3239193149Sdougb		isc_buffer_init(&optbuf, rdata.data, rdata.length);
3240193149Sdougb		isc_buffer_add(&optbuf, rdata.length);
3241218384Sdougb		while (isc_buffer_remaininglength(&optbuf) != 0) {
3242218384Sdougb			INSIST(isc_buffer_remaininglength(&optbuf) >= 4U);
3243218384Sdougb			optcode = isc_buffer_getuint16(&optbuf);
3244218384Sdougb			optlen = isc_buffer_getuint16(&optbuf);
3245218384Sdougb			INSIST(isc_buffer_remaininglength(&optbuf) >= optlen);
3246193149Sdougb
3247218384Sdougb			if (optcode == DNS_OPT_NSID) {
3248218384Sdougb				ADD_STRING(target, "; NSID");
3249218384Sdougb			} else {
3250218384Sdougb				ADD_STRING(target, "; OPT=");
3251218384Sdougb				sprintf(buf, "%u", optcode);
3252218384Sdougb				ADD_STRING(target, buf);
3253218384Sdougb			}
3254193149Sdougb
3255218384Sdougb			if (optlen != 0) {
3256218384Sdougb				int i;
3257218384Sdougb				ADD_STRING(target, ": ");
3258193149Sdougb
3259218384Sdougb				optdata = isc_buffer_current(&optbuf);
3260218384Sdougb				for (i = 0; i < optlen; i++) {
3261218384Sdougb					sprintf(buf, "%02x ", optdata[i]);
3262218384Sdougb					ADD_STRING(target, buf);
3263218384Sdougb				}
3264218384Sdougb				for (i = 0; i < optlen; i++) {
3265218384Sdougb					ADD_STRING(target, " (");
3266218384Sdougb					if (isprint(optdata[i]))
3267218384Sdougb						isc_buffer_putmem(target,
3268218384Sdougb								  &optdata[i],
3269218384Sdougb								  1);
3270218384Sdougb					else
3271218384Sdougb						isc_buffer_putstr(target, ".");
3272218384Sdougb					ADD_STRING(target, ")");
3273218384Sdougb				}
3274218384Sdougb				isc_buffer_forward(&optbuf, optlen);
3275193149Sdougb			}
3276218384Sdougb			ADD_STRING(target, "\n");
3277193149Sdougb		}
3278135446Strhodes		return (ISC_R_SUCCESS);
3279135446Strhodes	case DNS_PSEUDOSECTION_TSIG:
3280135446Strhodes		ps = dns_message_gettsig(msg, &name);
3281135446Strhodes		if (ps == NULL)
3282135446Strhodes			return (ISC_R_SUCCESS);
3283135446Strhodes		if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
3284135446Strhodes			ADD_STRING(target, ";; TSIG PSEUDOSECTION:\n");
3285135446Strhodes		result = dns_master_rdatasettotext(name, ps, style, target);
3286135446Strhodes		if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0 &&
3287135446Strhodes		    (flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
3288135446Strhodes			ADD_STRING(target, "\n");
3289135446Strhodes		return (result);
3290135446Strhodes	case DNS_PSEUDOSECTION_SIG0:
3291135446Strhodes		ps = dns_message_getsig0(msg, &name);
3292135446Strhodes		if (ps == NULL)
3293135446Strhodes			return (ISC_R_SUCCESS);
3294135446Strhodes		if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
3295135446Strhodes			ADD_STRING(target, ";; SIG0 PSEUDOSECTION:\n");
3296135446Strhodes		result = dns_master_rdatasettotext(name, ps, style, target);
3297135446Strhodes		if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0 &&
3298135446Strhodes		    (flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
3299135446Strhodes			ADD_STRING(target, "\n");
3300135446Strhodes		return (result);
3301135446Strhodes	}
3302135446Strhodes	return (ISC_R_UNEXPECTED);
3303135446Strhodes}
3304135446Strhodes
3305135446Strhodesisc_result_t
3306135446Strhodesdns_message_totext(dns_message_t *msg, const dns_master_style_t *style,
3307135446Strhodes		   dns_messagetextflag_t flags, isc_buffer_t *target) {
3308135446Strhodes	char buf[sizeof("1234567890")];
3309135446Strhodes	isc_result_t result;
3310135446Strhodes
3311135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
3312135446Strhodes	REQUIRE(target != NULL);
3313135446Strhodes
3314135446Strhodes	if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0) {
3315135446Strhodes		ADD_STRING(target, ";; ->>HEADER<<- opcode: ");
3316135446Strhodes		ADD_STRING(target, opcodetext[msg->opcode]);
3317135446Strhodes		ADD_STRING(target, ", status: ");
3318174187Sdougb		if (msg->rcode < (sizeof(rcodetext)/sizeof(rcodetext[0]))) {
3319174187Sdougb			ADD_STRING(target, rcodetext[msg->rcode]);
3320174187Sdougb		} else {
3321174187Sdougb			snprintf(buf, sizeof(buf), "%4u", msg->rcode);
3322174187Sdougb			ADD_STRING(target, buf);
3323174187Sdougb		}
3324135446Strhodes		ADD_STRING(target, ", id: ");
3325135446Strhodes		snprintf(buf, sizeof(buf), "%6u", msg->id);
3326135446Strhodes		ADD_STRING(target, buf);
3327218384Sdougb		ADD_STRING(target, "\n;; flags:");
3328135446Strhodes		if ((msg->flags & DNS_MESSAGEFLAG_QR) != 0)
3329218384Sdougb			ADD_STRING(target, " qr");
3330135446Strhodes		if ((msg->flags & DNS_MESSAGEFLAG_AA) != 0)
3331218384Sdougb			ADD_STRING(target, " aa");
3332135446Strhodes		if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0)
3333218384Sdougb			ADD_STRING(target, " tc");
3334135446Strhodes		if ((msg->flags & DNS_MESSAGEFLAG_RD) != 0)
3335218384Sdougb			ADD_STRING(target, " rd");
3336135446Strhodes		if ((msg->flags & DNS_MESSAGEFLAG_RA) != 0)
3337218384Sdougb			ADD_STRING(target, " ra");
3338135446Strhodes		if ((msg->flags & DNS_MESSAGEFLAG_AD) != 0)
3339218384Sdougb			ADD_STRING(target, " ad");
3340135446Strhodes		if ((msg->flags & DNS_MESSAGEFLAG_CD) != 0)
3341218384Sdougb			ADD_STRING(target, " cd");
3342218384Sdougb		/*
3343218384Sdougb		 * The final unnamed flag must be zero.
3344218384Sdougb		 */
3345218384Sdougb		if ((msg->flags & 0x0040U) != 0)
3346218384Sdougb			ADD_STRING(target, "; MBZ: 0x4");
3347135446Strhodes		if (msg->opcode != dns_opcode_update) {
3348135446Strhodes			ADD_STRING(target, "; QUESTION: ");
3349135446Strhodes		} else {
3350135446Strhodes			ADD_STRING(target, "; ZONE: ");
3351135446Strhodes		}
3352135446Strhodes		snprintf(buf, sizeof(buf), "%1u",
3353135446Strhodes			 msg->counts[DNS_SECTION_QUESTION]);
3354135446Strhodes		ADD_STRING(target, buf);
3355135446Strhodes		if (msg->opcode != dns_opcode_update) {
3356135446Strhodes			ADD_STRING(target, ", ANSWER: ");
3357135446Strhodes		} else {
3358135446Strhodes			ADD_STRING(target, ", PREREQ: ");
3359135446Strhodes		}
3360135446Strhodes		snprintf(buf, sizeof(buf), "%1u",
3361135446Strhodes			 msg->counts[DNS_SECTION_ANSWER]);
3362135446Strhodes		ADD_STRING(target, buf);
3363135446Strhodes		if (msg->opcode != dns_opcode_update) {
3364135446Strhodes			ADD_STRING(target, ", AUTHORITY: ");
3365135446Strhodes		} else {
3366135446Strhodes			ADD_STRING(target, ", UPDATE: ");
3367135446Strhodes		}
3368135446Strhodes		snprintf(buf, sizeof(buf), "%1u",
3369135446Strhodes			msg->counts[DNS_SECTION_AUTHORITY]);
3370135446Strhodes		ADD_STRING(target, buf);
3371135446Strhodes		ADD_STRING(target, ", ADDITIONAL: ");
3372135446Strhodes		snprintf(buf, sizeof(buf), "%1u",
3373135446Strhodes			msg->counts[DNS_SECTION_ADDITIONAL]);
3374135446Strhodes		ADD_STRING(target, buf);
3375135446Strhodes		ADD_STRING(target, "\n");
3376135446Strhodes	}
3377135446Strhodes	result = dns_message_pseudosectiontotext(msg,
3378135446Strhodes						 DNS_PSEUDOSECTION_OPT,
3379135446Strhodes						 style, flags, target);
3380135446Strhodes	if (result != ISC_R_SUCCESS)
3381135446Strhodes		return (result);
3382135446Strhodes
3383135446Strhodes	result = dns_message_sectiontotext(msg, DNS_SECTION_QUESTION,
3384135446Strhodes					   style, flags, target);
3385135446Strhodes	if (result != ISC_R_SUCCESS)
3386135446Strhodes		return (result);
3387135446Strhodes	result = dns_message_sectiontotext(msg, DNS_SECTION_ANSWER,
3388135446Strhodes					   style, flags, target);
3389135446Strhodes	if (result != ISC_R_SUCCESS)
3390135446Strhodes		return (result);
3391135446Strhodes	result = dns_message_sectiontotext(msg, DNS_SECTION_AUTHORITY,
3392135446Strhodes					   style, flags, target);
3393135446Strhodes	if (result != ISC_R_SUCCESS)
3394135446Strhodes		return (result);
3395135446Strhodes	result = dns_message_sectiontotext(msg, DNS_SECTION_ADDITIONAL,
3396135446Strhodes					   style, flags, target);
3397135446Strhodes	if (result != ISC_R_SUCCESS)
3398135446Strhodes		return (result);
3399135446Strhodes
3400135446Strhodes	result = dns_message_pseudosectiontotext(msg,
3401135446Strhodes						 DNS_PSEUDOSECTION_TSIG,
3402135446Strhodes						 style, flags, target);
3403135446Strhodes	if (result != ISC_R_SUCCESS)
3404135446Strhodes		return (result);
3405135446Strhodes
3406135446Strhodes	result = dns_message_pseudosectiontotext(msg,
3407135446Strhodes						 DNS_PSEUDOSECTION_SIG0,
3408135446Strhodes						 style, flags, target);
3409135446Strhodes	if (result != ISC_R_SUCCESS)
3410135446Strhodes		return (result);
3411135446Strhodes
3412135446Strhodes	return (ISC_R_SUCCESS);
3413135446Strhodes}
3414135446Strhodes
3415135446Strhodesisc_region_t *
3416135446Strhodesdns_message_getrawmessage(dns_message_t *msg) {
3417135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
3418135446Strhodes	return (&msg->saved);
3419135446Strhodes}
3420135446Strhodes
3421135446Strhodesvoid
3422135446Strhodesdns_message_setsortorder(dns_message_t *msg, dns_rdatasetorderfunc_t order,
3423165071Sdougb			 const void *order_arg)
3424135446Strhodes{
3425135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
3426135446Strhodes	msg->order = order;
3427135446Strhodes	msg->order_arg = order_arg;
3428135446Strhodes}
3429135446Strhodes
3430135446Strhodesvoid
3431135446Strhodesdns_message_settimeadjust(dns_message_t *msg, int timeadjust) {
3432135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
3433135446Strhodes	msg->timeadjust = timeadjust;
3434135446Strhodes}
3435135446Strhodes
3436135446Strhodesint
3437135446Strhodesdns_message_gettimeadjust(dns_message_t *msg) {
3438135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
3439135446Strhodes	return (msg->timeadjust);
3440135446Strhodes}
3441135446Strhodes
3442135446Strhodesisc_result_t
3443135446Strhodesdns_opcode_totext(dns_opcode_t opcode, isc_buffer_t *target) {
3444135446Strhodes
3445135446Strhodes	REQUIRE(opcode < 16);
3446135446Strhodes
3447135446Strhodes	if (isc_buffer_availablelength(target) < strlen(opcodetext[opcode]))
3448135446Strhodes		return (ISC_R_NOSPACE);
3449135446Strhodes	isc_buffer_putstr(target, opcodetext[opcode]);
3450135446Strhodes	return (ISC_R_SUCCESS);
3451135446Strhodes}
3452