message.c revision 174187
1135446Strhodes/*
2174187Sdougb * Copyright (C) 2004-2007  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
18174187Sdougb/* $Id: message.c,v 1.222.18.14 2007/08/28 07:20:04 tbox Exp $ */
19135446Strhodes
20170222Sdougb/*! \file */
21170222Sdougb
22135446Strhodes/***
23135446Strhodes *** Imports
24135446Strhodes ***/
25135446Strhodes
26135446Strhodes#include <config.h>
27135446Strhodes
28135446Strhodes#include <isc/buffer.h>
29135446Strhodes#include <isc/mem.h>
30135446Strhodes#include <isc/print.h>
31135446Strhodes#include <isc/string.h>		/* Required for HP/UX (and others?) */
32135446Strhodes#include <isc/util.h>
33135446Strhodes
34135446Strhodes#include <dns/dnssec.h>
35135446Strhodes#include <dns/keyvalues.h>
36135446Strhodes#include <dns/log.h>
37135446Strhodes#include <dns/masterdump.h>
38135446Strhodes#include <dns/message.h>
39135446Strhodes#include <dns/opcode.h>
40135446Strhodes#include <dns/rdata.h>
41135446Strhodes#include <dns/rdatalist.h>
42135446Strhodes#include <dns/rdataset.h>
43135446Strhodes#include <dns/rdatastruct.h>
44135446Strhodes#include <dns/result.h>
45135446Strhodes#include <dns/tsig.h>
46135446Strhodes#include <dns/view.h>
47135446Strhodes
48135446Strhodes#define DNS_MESSAGE_OPCODE_MASK		0x7800U
49135446Strhodes#define DNS_MESSAGE_OPCODE_SHIFT	11
50135446Strhodes#define DNS_MESSAGE_RCODE_MASK		0x000fU
51135446Strhodes#define DNS_MESSAGE_FLAG_MASK		0x8ff0U
52135446Strhodes#define DNS_MESSAGE_EDNSRCODE_MASK	0xff000000U
53135446Strhodes#define DNS_MESSAGE_EDNSRCODE_SHIFT	24
54135446Strhodes#define DNS_MESSAGE_EDNSVERSION_MASK	0x00ff0000U
55135446Strhodes#define DNS_MESSAGE_EDNSVERSION_SHIFT	16
56135446Strhodes
57135446Strhodes#define VALID_NAMED_SECTION(s)  (((s) > DNS_SECTION_ANY) \
58135446Strhodes				 && ((s) < DNS_SECTION_MAX))
59135446Strhodes#define VALID_SECTION(s)	(((s) >= DNS_SECTION_ANY) \
60135446Strhodes				 && ((s) < DNS_SECTION_MAX))
61135446Strhodes#define ADD_STRING(b, s)	{if (strlen(s) >= \
62135446Strhodes				   isc_buffer_availablelength(b)) \
63135446Strhodes				       return(ISC_R_NOSPACE); else \
64135446Strhodes				       isc_buffer_putstr(b, s);}
65135446Strhodes#define VALID_PSEUDOSECTION(s)	(((s) >= DNS_PSEUDOSECTION_ANY) \
66135446Strhodes				 && ((s) < DNS_PSEUDOSECTION_MAX))
67135446Strhodes
68170222Sdougb/*%
69135446Strhodes * This is the size of each individual scratchpad buffer, and the numbers
70135446Strhodes * of various block allocations used within the server.
71135446Strhodes * XXXMLG These should come from a config setting.
72135446Strhodes */
73135446Strhodes#define SCRATCHPAD_SIZE		512
74135446Strhodes#define NAME_COUNT		  8
75135446Strhodes#define OFFSET_COUNT		  4
76135446Strhodes#define RDATA_COUNT		  8
77135446Strhodes#define RDATALIST_COUNT		  8
78135446Strhodes#define RDATASET_COUNT		 RDATALIST_COUNT
79135446Strhodes
80170222Sdougb/*%
81135446Strhodes * Text representation of the different items, for message_totext
82135446Strhodes * functions.
83135446Strhodes */
84135446Strhodesstatic const char *sectiontext[] = {
85135446Strhodes	"QUESTION",
86135446Strhodes	"ANSWER",
87135446Strhodes	"AUTHORITY",
88135446Strhodes	"ADDITIONAL"
89135446Strhodes};
90135446Strhodes
91135446Strhodesstatic const char *updsectiontext[] = {
92135446Strhodes	"ZONE",
93135446Strhodes	"PREREQUISITE",
94135446Strhodes	"UPDATE",
95135446Strhodes	"ADDITIONAL"
96135446Strhodes};
97135446Strhodes
98135446Strhodesstatic const char *opcodetext[] = {
99135446Strhodes	"QUERY",
100135446Strhodes	"IQUERY",
101135446Strhodes	"STATUS",
102135446Strhodes	"RESERVED3",
103135446Strhodes	"NOTIFY",
104135446Strhodes	"UPDATE",
105135446Strhodes	"RESERVED6",
106135446Strhodes	"RESERVED7",
107135446Strhodes	"RESERVED8",
108135446Strhodes	"RESERVED9",
109135446Strhodes	"RESERVED10",
110135446Strhodes	"RESERVED11",
111135446Strhodes	"RESERVED12",
112135446Strhodes	"RESERVED13",
113135446Strhodes	"RESERVED14",
114135446Strhodes	"RESERVED15"
115135446Strhodes};
116135446Strhodes
117135446Strhodesstatic const char *rcodetext[] = {
118135446Strhodes	"NOERROR",
119135446Strhodes	"FORMERR",
120135446Strhodes	"SERVFAIL",
121135446Strhodes	"NXDOMAIN",
122135446Strhodes	"NOTIMP",
123135446Strhodes	"REFUSED",
124135446Strhodes	"YXDOMAIN",
125135446Strhodes	"YXRRSET",
126135446Strhodes	"NXRRSET",
127135446Strhodes	"NOTAUTH",
128135446Strhodes	"NOTZONE",
129135446Strhodes	"RESERVED11",
130135446Strhodes	"RESERVED12",
131135446Strhodes	"RESERVED13",
132135446Strhodes	"RESERVED14",
133135446Strhodes	"RESERVED15",
134135446Strhodes	"BADVERS"
135135446Strhodes};
136135446Strhodes
137135446Strhodes
138170222Sdougb/*%
139135446Strhodes * "helper" type, which consists of a block of some type, and is linkable.
140135446Strhodes * For it to work, sizeof(dns_msgblock_t) must be a multiple of the pointer
141135446Strhodes * size, or the allocated elements will not be alligned correctly.
142135446Strhodes */
143135446Strhodesstruct dns_msgblock {
144135446Strhodes	unsigned int			count;
145135446Strhodes	unsigned int			remaining;
146135446Strhodes	ISC_LINK(dns_msgblock_t)	link;
147135446Strhodes}; /* dynamically sized */
148135446Strhodes
149135446Strhodesstatic inline dns_msgblock_t *
150135446Strhodesmsgblock_allocate(isc_mem_t *, unsigned int, unsigned int);
151135446Strhodes
152135446Strhodes#define msgblock_get(block, type) \
153135446Strhodes	((type *)msgblock_internalget(block, sizeof(type)))
154135446Strhodes
155135446Strhodesstatic inline void *
156135446Strhodesmsgblock_internalget(dns_msgblock_t *, unsigned int);
157135446Strhodes
158135446Strhodesstatic inline void
159135446Strhodesmsgblock_reset(dns_msgblock_t *);
160135446Strhodes
161135446Strhodesstatic inline void
162135446Strhodesmsgblock_free(isc_mem_t *, dns_msgblock_t *, unsigned int);
163135446Strhodes
164135446Strhodes/*
165135446Strhodes * Allocate a new dns_msgblock_t, and return a pointer to it.  If no memory
166135446Strhodes * is free, return NULL.
167135446Strhodes */
168135446Strhodesstatic inline dns_msgblock_t *
169135446Strhodesmsgblock_allocate(isc_mem_t *mctx, unsigned int sizeof_type,
170135446Strhodes		  unsigned int count)
171135446Strhodes{
172135446Strhodes	dns_msgblock_t *block;
173135446Strhodes	unsigned int length;
174135446Strhodes
175135446Strhodes	length = sizeof(dns_msgblock_t) + (sizeof_type * count);
176135446Strhodes
177135446Strhodes	block = isc_mem_get(mctx, length);
178135446Strhodes	if (block == NULL)
179135446Strhodes		return (NULL);
180135446Strhodes
181135446Strhodes	block->count = count;
182135446Strhodes	block->remaining = count;
183135446Strhodes
184135446Strhodes	ISC_LINK_INIT(block, link);
185135446Strhodes
186135446Strhodes	return (block);
187135446Strhodes}
188135446Strhodes
189135446Strhodes/*
190135446Strhodes * Return an element from the msgblock.  If no more are available, return
191135446Strhodes * NULL.
192135446Strhodes */
193135446Strhodesstatic inline void *
194135446Strhodesmsgblock_internalget(dns_msgblock_t *block, unsigned int sizeof_type) {
195135446Strhodes	void *ptr;
196135446Strhodes
197135446Strhodes	if (block == NULL || block->remaining == 0)
198135446Strhodes		return (NULL);
199135446Strhodes
200135446Strhodes	block->remaining--;
201135446Strhodes
202135446Strhodes	ptr = (((unsigned char *)block)
203135446Strhodes	       + sizeof(dns_msgblock_t)
204135446Strhodes	       + (sizeof_type * block->remaining));
205135446Strhodes
206135446Strhodes	return (ptr);
207135446Strhodes}
208135446Strhodes
209135446Strhodesstatic inline void
210135446Strhodesmsgblock_reset(dns_msgblock_t *block) {
211135446Strhodes	block->remaining = block->count;
212135446Strhodes}
213135446Strhodes
214135446Strhodes/*
215135446Strhodes * Release memory associated with a message block.
216135446Strhodes */
217135446Strhodesstatic inline void
218135446Strhodesmsgblock_free(isc_mem_t *mctx, dns_msgblock_t *block, unsigned int sizeof_type)
219135446Strhodes{
220135446Strhodes	unsigned int length;
221135446Strhodes
222135446Strhodes	length = sizeof(dns_msgblock_t) + (sizeof_type * block->count);
223135446Strhodes
224135446Strhodes	isc_mem_put(mctx, block, length);
225135446Strhodes}
226135446Strhodes
227135446Strhodes/*
228135446Strhodes * Allocate a new dynamic buffer, and attach it to this message as the
229135446Strhodes * "current" buffer.  (which is always the last on the list, for our
230135446Strhodes * uses)
231135446Strhodes */
232135446Strhodesstatic inline isc_result_t
233135446Strhodesnewbuffer(dns_message_t *msg, unsigned int size) {
234135446Strhodes	isc_result_t result;
235135446Strhodes	isc_buffer_t *dynbuf;
236135446Strhodes
237135446Strhodes	dynbuf = NULL;
238135446Strhodes	result = isc_buffer_allocate(msg->mctx, &dynbuf, size);
239135446Strhodes	if (result != ISC_R_SUCCESS)
240135446Strhodes		return (ISC_R_NOMEMORY);
241135446Strhodes
242135446Strhodes	ISC_LIST_APPEND(msg->scratchpad, dynbuf, link);
243135446Strhodes	return (ISC_R_SUCCESS);
244135446Strhodes}
245135446Strhodes
246135446Strhodesstatic inline isc_buffer_t *
247135446Strhodescurrentbuffer(dns_message_t *msg) {
248135446Strhodes	isc_buffer_t *dynbuf;
249135446Strhodes
250135446Strhodes	dynbuf = ISC_LIST_TAIL(msg->scratchpad);
251135446Strhodes	INSIST(dynbuf != NULL);
252135446Strhodes
253135446Strhodes	return (dynbuf);
254135446Strhodes}
255135446Strhodes
256135446Strhodesstatic inline void
257135446Strhodesreleaserdata(dns_message_t *msg, dns_rdata_t *rdata) {
258135446Strhodes	ISC_LIST_PREPEND(msg->freerdata, rdata, link);
259135446Strhodes}
260135446Strhodes
261135446Strhodesstatic inline dns_rdata_t *
262135446Strhodesnewrdata(dns_message_t *msg) {
263135446Strhodes	dns_msgblock_t *msgblock;
264135446Strhodes	dns_rdata_t *rdata;
265135446Strhodes
266135446Strhodes	rdata = ISC_LIST_HEAD(msg->freerdata);
267135446Strhodes	if (rdata != NULL) {
268135446Strhodes		ISC_LIST_UNLINK(msg->freerdata, rdata, link);
269135446Strhodes		return (rdata);
270135446Strhodes	}
271135446Strhodes
272135446Strhodes	msgblock = ISC_LIST_TAIL(msg->rdatas);
273135446Strhodes	rdata = msgblock_get(msgblock, dns_rdata_t);
274135446Strhodes	if (rdata == NULL) {
275135446Strhodes		msgblock = msgblock_allocate(msg->mctx, sizeof(dns_rdata_t),
276135446Strhodes					     RDATA_COUNT);
277135446Strhodes		if (msgblock == NULL)
278135446Strhodes			return (NULL);
279135446Strhodes
280135446Strhodes		ISC_LIST_APPEND(msg->rdatas, msgblock, link);
281135446Strhodes
282135446Strhodes		rdata = msgblock_get(msgblock, dns_rdata_t);
283135446Strhodes	}
284135446Strhodes
285135446Strhodes	dns_rdata_init(rdata);
286135446Strhodes	return (rdata);
287135446Strhodes}
288135446Strhodes
289135446Strhodesstatic inline void
290135446Strhodesreleaserdatalist(dns_message_t *msg, dns_rdatalist_t *rdatalist) {
291135446Strhodes	ISC_LIST_PREPEND(msg->freerdatalist, rdatalist, link);
292135446Strhodes}
293135446Strhodes
294135446Strhodesstatic inline dns_rdatalist_t *
295135446Strhodesnewrdatalist(dns_message_t *msg) {
296135446Strhodes	dns_msgblock_t *msgblock;
297135446Strhodes	dns_rdatalist_t *rdatalist;
298135446Strhodes
299135446Strhodes	rdatalist = ISC_LIST_HEAD(msg->freerdatalist);
300135446Strhodes	if (rdatalist != NULL) {
301135446Strhodes		ISC_LIST_UNLINK(msg->freerdatalist, rdatalist, link);
302135446Strhodes		return (rdatalist);
303135446Strhodes	}
304135446Strhodes
305135446Strhodes	msgblock = ISC_LIST_TAIL(msg->rdatalists);
306135446Strhodes	rdatalist = msgblock_get(msgblock, dns_rdatalist_t);
307135446Strhodes	if (rdatalist == NULL) {
308135446Strhodes		msgblock = msgblock_allocate(msg->mctx,
309135446Strhodes					     sizeof(dns_rdatalist_t),
310135446Strhodes					     RDATALIST_COUNT);
311135446Strhodes		if (msgblock == NULL)
312135446Strhodes			return (NULL);
313135446Strhodes
314135446Strhodes		ISC_LIST_APPEND(msg->rdatalists, msgblock, link);
315135446Strhodes
316135446Strhodes		rdatalist = msgblock_get(msgblock, dns_rdatalist_t);
317135446Strhodes	}
318135446Strhodes
319135446Strhodes	return (rdatalist);
320135446Strhodes}
321135446Strhodes
322135446Strhodesstatic inline dns_offsets_t *
323135446Strhodesnewoffsets(dns_message_t *msg) {
324135446Strhodes	dns_msgblock_t *msgblock;
325135446Strhodes	dns_offsets_t *offsets;
326135446Strhodes
327135446Strhodes	msgblock = ISC_LIST_TAIL(msg->offsets);
328135446Strhodes	offsets = msgblock_get(msgblock, dns_offsets_t);
329135446Strhodes	if (offsets == NULL) {
330135446Strhodes		msgblock = msgblock_allocate(msg->mctx,
331135446Strhodes					     sizeof(dns_offsets_t),
332135446Strhodes					     OFFSET_COUNT);
333135446Strhodes		if (msgblock == NULL)
334135446Strhodes			return (NULL);
335135446Strhodes
336135446Strhodes		ISC_LIST_APPEND(msg->offsets, msgblock, link);
337135446Strhodes
338135446Strhodes		offsets = msgblock_get(msgblock, dns_offsets_t);
339135446Strhodes	}
340135446Strhodes
341135446Strhodes	return (offsets);
342135446Strhodes}
343135446Strhodes
344135446Strhodesstatic inline void
345135446Strhodesmsginitheader(dns_message_t *m) {
346135446Strhodes	m->id = 0;
347135446Strhodes	m->flags = 0;
348135446Strhodes	m->rcode = 0;
349135446Strhodes	m->opcode = 0;
350135446Strhodes	m->rdclass = 0;
351135446Strhodes}
352135446Strhodes
353135446Strhodesstatic inline void
354135446Strhodesmsginitprivate(dns_message_t *m) {
355135446Strhodes	unsigned int i;
356135446Strhodes
357135446Strhodes	for (i = 0; i < DNS_SECTION_MAX; i++) {
358135446Strhodes		m->cursors[i] = NULL;
359135446Strhodes		m->counts[i] = 0;
360135446Strhodes	}
361135446Strhodes	m->opt = NULL;
362135446Strhodes	m->sig0 = NULL;
363135446Strhodes	m->sig0name = NULL;
364135446Strhodes	m->tsig = NULL;
365135446Strhodes	m->tsigname = NULL;
366135446Strhodes	m->state = DNS_SECTION_ANY;  /* indicate nothing parsed or rendered */
367135446Strhodes	m->opt_reserved = 0;
368135446Strhodes	m->sig_reserved = 0;
369135446Strhodes	m->reserved = 0;
370135446Strhodes	m->buffer = NULL;
371135446Strhodes}
372135446Strhodes
373135446Strhodesstatic inline void
374135446Strhodesmsginittsig(dns_message_t *m) {
375135446Strhodes	m->tsigstatus = dns_rcode_noerror;
376135446Strhodes	m->querytsigstatus = dns_rcode_noerror;
377135446Strhodes	m->tsigkey = NULL;
378135446Strhodes	m->tsigctx = NULL;
379135446Strhodes	m->sigstart = -1;
380135446Strhodes	m->sig0key = NULL;
381135446Strhodes	m->sig0status = dns_rcode_noerror;
382135446Strhodes	m->timeadjust = 0;
383135446Strhodes}
384135446Strhodes
385135446Strhodes/*
386135446Strhodes * Init elements to default state.  Used both when allocating a new element
387135446Strhodes * and when resetting one.
388135446Strhodes */
389135446Strhodesstatic inline void
390135446Strhodesmsginit(dns_message_t *m) {
391135446Strhodes	msginitheader(m);
392135446Strhodes	msginitprivate(m);
393135446Strhodes	msginittsig(m);
394135446Strhodes	m->header_ok = 0;
395135446Strhodes	m->question_ok = 0;
396135446Strhodes	m->tcp_continuation = 0;
397135446Strhodes	m->verified_sig = 0;
398135446Strhodes	m->verify_attempted = 0;
399135446Strhodes	m->order = NULL;
400135446Strhodes	m->order_arg = NULL;
401135446Strhodes	m->query.base = NULL;
402135446Strhodes	m->query.length = 0;
403135446Strhodes	m->free_query = 0;
404135446Strhodes	m->saved.base = NULL;
405135446Strhodes	m->saved.length = 0;
406135446Strhodes	m->free_saved = 0;
407135446Strhodes	m->querytsig = NULL;
408135446Strhodes}
409135446Strhodes
410135446Strhodesstatic inline void
411135446Strhodesmsgresetnames(dns_message_t *msg, unsigned int first_section) {
412135446Strhodes	unsigned int i;
413135446Strhodes	dns_name_t *name, *next_name;
414135446Strhodes	dns_rdataset_t *rds, *next_rds;
415135446Strhodes
416135446Strhodes	/*
417135446Strhodes	 * Clean up name lists by calling the rdataset disassociate function.
418135446Strhodes	 */
419135446Strhodes	for (i = first_section; i < DNS_SECTION_MAX; i++) {
420135446Strhodes		name = ISC_LIST_HEAD(msg->sections[i]);
421135446Strhodes		while (name != NULL) {
422135446Strhodes			next_name = ISC_LIST_NEXT(name, link);
423135446Strhodes			ISC_LIST_UNLINK(msg->sections[i], name, link);
424135446Strhodes
425135446Strhodes			rds = ISC_LIST_HEAD(name->list);
426135446Strhodes			while (rds != NULL) {
427135446Strhodes				next_rds = ISC_LIST_NEXT(rds, link);
428135446Strhodes				ISC_LIST_UNLINK(name->list, rds, link);
429135446Strhodes
430135446Strhodes				INSIST(dns_rdataset_isassociated(rds));
431135446Strhodes				dns_rdataset_disassociate(rds);
432135446Strhodes				isc_mempool_put(msg->rdspool, rds);
433135446Strhodes				rds = next_rds;
434135446Strhodes			}
435135446Strhodes			if (dns_name_dynamic(name))
436135446Strhodes				dns_name_free(name, msg->mctx);
437135446Strhodes			isc_mempool_put(msg->namepool, name);
438135446Strhodes			name = next_name;
439135446Strhodes		}
440135446Strhodes	}
441135446Strhodes}
442135446Strhodes
443135446Strhodesstatic void
444135446Strhodesmsgresetopt(dns_message_t *msg)
445135446Strhodes{
446135446Strhodes	if (msg->opt != NULL) {
447135446Strhodes		if (msg->opt_reserved > 0) {
448135446Strhodes			dns_message_renderrelease(msg, msg->opt_reserved);
449135446Strhodes			msg->opt_reserved = 0;
450135446Strhodes		}
451135446Strhodes		INSIST(dns_rdataset_isassociated(msg->opt));
452135446Strhodes		dns_rdataset_disassociate(msg->opt);
453135446Strhodes		isc_mempool_put(msg->rdspool, msg->opt);
454135446Strhodes		msg->opt = NULL;
455135446Strhodes	}
456135446Strhodes}
457135446Strhodes
458135446Strhodesstatic void
459135446Strhodesmsgresetsigs(dns_message_t *msg, isc_boolean_t replying) {
460135446Strhodes	if (msg->sig_reserved > 0) {
461135446Strhodes		dns_message_renderrelease(msg, msg->sig_reserved);
462135446Strhodes		msg->sig_reserved = 0;
463135446Strhodes	}
464135446Strhodes	if (msg->tsig != NULL) {
465135446Strhodes		INSIST(dns_rdataset_isassociated(msg->tsig));
466135446Strhodes		INSIST(msg->namepool != NULL);
467135446Strhodes		if (replying) {
468135446Strhodes			INSIST(msg->querytsig == NULL);
469135446Strhodes			msg->querytsig = msg->tsig;
470135446Strhodes		} else {
471135446Strhodes			dns_rdataset_disassociate(msg->tsig);
472135446Strhodes			isc_mempool_put(msg->rdspool, msg->tsig);
473135446Strhodes			if (msg->querytsig != NULL) {
474135446Strhodes				dns_rdataset_disassociate(msg->querytsig);
475135446Strhodes				isc_mempool_put(msg->rdspool, msg->querytsig);
476135446Strhodes			}
477135446Strhodes		}
478135446Strhodes		if (dns_name_dynamic(msg->tsigname))
479135446Strhodes			dns_name_free(msg->tsigname, msg->mctx);
480135446Strhodes		isc_mempool_put(msg->namepool, msg->tsigname);
481135446Strhodes		msg->tsig = NULL;
482135446Strhodes		msg->tsigname = NULL;
483135446Strhodes	} else if (msg->querytsig != NULL && !replying) {
484135446Strhodes		dns_rdataset_disassociate(msg->querytsig);
485135446Strhodes		isc_mempool_put(msg->rdspool, msg->querytsig);
486135446Strhodes		msg->querytsig = NULL;
487135446Strhodes	}
488135446Strhodes	if (msg->sig0 != NULL) {
489135446Strhodes		INSIST(dns_rdataset_isassociated(msg->sig0));
490135446Strhodes		dns_rdataset_disassociate(msg->sig0);
491135446Strhodes		isc_mempool_put(msg->rdspool, msg->sig0);
492135446Strhodes		if (msg->sig0name != NULL) {
493135446Strhodes			if (dns_name_dynamic(msg->sig0name))
494135446Strhodes				dns_name_free(msg->sig0name, msg->mctx);
495135446Strhodes			isc_mempool_put(msg->namepool, msg->sig0name);
496135446Strhodes		}
497135446Strhodes		msg->sig0 = NULL;
498135446Strhodes		msg->sig0name = NULL;
499135446Strhodes	}
500135446Strhodes}
501135446Strhodes
502135446Strhodes/*
503135446Strhodes * Free all but one (or everything) for this message.  This is used by
504135446Strhodes * both dns_message_reset() and dns_message_destroy().
505135446Strhodes */
506135446Strhodesstatic void
507135446Strhodesmsgreset(dns_message_t *msg, isc_boolean_t everything) {
508135446Strhodes	dns_msgblock_t *msgblock, *next_msgblock;
509135446Strhodes	isc_buffer_t *dynbuf, *next_dynbuf;
510135446Strhodes	dns_rdata_t *rdata;
511135446Strhodes	dns_rdatalist_t *rdatalist;
512135446Strhodes
513135446Strhodes	msgresetnames(msg, 0);
514135446Strhodes	msgresetopt(msg);
515135446Strhodes	msgresetsigs(msg, ISC_FALSE);
516135446Strhodes
517135446Strhodes	/*
518135446Strhodes	 * Clean up linked lists.
519135446Strhodes	 */
520135446Strhodes
521135446Strhodes	/*
522135446Strhodes	 * Run through the free lists, and just unlink anything found there.
523135446Strhodes	 * The memory isn't lost since these are part of message blocks we
524135446Strhodes	 * have allocated.
525135446Strhodes	 */
526135446Strhodes	rdata = ISC_LIST_HEAD(msg->freerdata);
527135446Strhodes	while (rdata != NULL) {
528135446Strhodes		ISC_LIST_UNLINK(msg->freerdata, rdata, link);
529135446Strhodes		rdata = ISC_LIST_HEAD(msg->freerdata);
530135446Strhodes	}
531135446Strhodes	rdatalist = ISC_LIST_HEAD(msg->freerdatalist);
532135446Strhodes	while (rdatalist != NULL) {
533135446Strhodes		ISC_LIST_UNLINK(msg->freerdatalist, rdatalist, link);
534135446Strhodes		rdatalist = ISC_LIST_HEAD(msg->freerdatalist);
535135446Strhodes	}
536135446Strhodes
537135446Strhodes	dynbuf = ISC_LIST_HEAD(msg->scratchpad);
538135446Strhodes	INSIST(dynbuf != NULL);
539135446Strhodes	if (!everything) {
540135446Strhodes		isc_buffer_clear(dynbuf);
541135446Strhodes		dynbuf = ISC_LIST_NEXT(dynbuf, link);
542135446Strhodes	}
543135446Strhodes	while (dynbuf != NULL) {
544135446Strhodes		next_dynbuf = ISC_LIST_NEXT(dynbuf, link);
545135446Strhodes		ISC_LIST_UNLINK(msg->scratchpad, dynbuf, link);
546135446Strhodes		isc_buffer_free(&dynbuf);
547135446Strhodes		dynbuf = next_dynbuf;
548135446Strhodes	}
549135446Strhodes
550135446Strhodes	msgblock = ISC_LIST_HEAD(msg->rdatas);
551135446Strhodes	if (!everything && msgblock != NULL) {
552135446Strhodes		msgblock_reset(msgblock);
553135446Strhodes		msgblock = ISC_LIST_NEXT(msgblock, link);
554135446Strhodes	}
555135446Strhodes	while (msgblock != NULL) {
556135446Strhodes		next_msgblock = ISC_LIST_NEXT(msgblock, link);
557135446Strhodes		ISC_LIST_UNLINK(msg->rdatas, msgblock, link);
558135446Strhodes		msgblock_free(msg->mctx, msgblock, sizeof(dns_rdata_t));
559135446Strhodes		msgblock = next_msgblock;
560135446Strhodes	}
561135446Strhodes
562135446Strhodes	/*
563135446Strhodes	 * rdatalists could be empty.
564135446Strhodes	 */
565135446Strhodes
566135446Strhodes	msgblock = ISC_LIST_HEAD(msg->rdatalists);
567135446Strhodes	if (!everything && msgblock != NULL) {
568135446Strhodes		msgblock_reset(msgblock);
569135446Strhodes		msgblock = ISC_LIST_NEXT(msgblock, link);
570135446Strhodes	}
571135446Strhodes	while (msgblock != NULL) {
572135446Strhodes		next_msgblock = ISC_LIST_NEXT(msgblock, link);
573135446Strhodes		ISC_LIST_UNLINK(msg->rdatalists, msgblock, link);
574135446Strhodes		msgblock_free(msg->mctx, msgblock, sizeof(dns_rdatalist_t));
575135446Strhodes		msgblock = next_msgblock;
576135446Strhodes	}
577135446Strhodes
578135446Strhodes	msgblock = ISC_LIST_HEAD(msg->offsets);
579135446Strhodes	if (!everything && msgblock != NULL) {
580135446Strhodes		msgblock_reset(msgblock);
581135446Strhodes		msgblock = ISC_LIST_NEXT(msgblock, link);
582135446Strhodes	}
583135446Strhodes	while (msgblock != NULL) {
584135446Strhodes		next_msgblock = ISC_LIST_NEXT(msgblock, link);
585135446Strhodes		ISC_LIST_UNLINK(msg->offsets, msgblock, link);
586135446Strhodes		msgblock_free(msg->mctx, msgblock, sizeof(dns_offsets_t));
587135446Strhodes		msgblock = next_msgblock;
588135446Strhodes	}
589135446Strhodes
590135446Strhodes	if (msg->tsigkey != NULL) {
591135446Strhodes		dns_tsigkey_detach(&msg->tsigkey);
592135446Strhodes		msg->tsigkey = NULL;
593135446Strhodes	}
594135446Strhodes
595135446Strhodes	if (msg->query.base != NULL) {
596135446Strhodes		if (msg->free_query != 0)
597135446Strhodes			isc_mem_put(msg->mctx, msg->query.base,
598135446Strhodes				    msg->query.length);
599135446Strhodes		msg->query.base = NULL;
600135446Strhodes		msg->query.length = 0;
601135446Strhodes	}
602135446Strhodes
603135446Strhodes	if (msg->saved.base != NULL) {
604135446Strhodes		if (msg->free_saved != 0)
605135446Strhodes			isc_mem_put(msg->mctx, msg->saved.base,
606135446Strhodes				    msg->saved.length);
607135446Strhodes		msg->saved.base = NULL;
608135446Strhodes		msg->saved.length = 0;
609135446Strhodes	}
610135446Strhodes
611135446Strhodes	/*
612135446Strhodes	 * cleanup the buffer cleanup list
613135446Strhodes	 */
614135446Strhodes	dynbuf = ISC_LIST_HEAD(msg->cleanup);
615135446Strhodes	while (dynbuf != NULL) {
616135446Strhodes		next_dynbuf = ISC_LIST_NEXT(dynbuf, link);
617135446Strhodes		ISC_LIST_UNLINK(msg->cleanup, dynbuf, link);
618135446Strhodes		isc_buffer_free(&dynbuf);
619135446Strhodes		dynbuf = next_dynbuf;
620135446Strhodes	}
621135446Strhodes
622135446Strhodes	/*
623135446Strhodes	 * Set other bits to normal default values.
624135446Strhodes	 */
625135446Strhodes	if (!everything)
626135446Strhodes		msginit(msg);
627135446Strhodes
628135446Strhodes	ENSURE(isc_mempool_getallocated(msg->namepool) == 0);
629135446Strhodes	ENSURE(isc_mempool_getallocated(msg->rdspool) == 0);
630135446Strhodes}
631135446Strhodes
632135446Strhodesstatic unsigned int
633135446Strhodesspacefortsig(dns_tsigkey_t *key, int otherlen) {
634135446Strhodes	isc_region_t r1, r2;
635135446Strhodes	unsigned int x;
636135446Strhodes	isc_result_t result;
637135446Strhodes
638135446Strhodes	/*
639135446Strhodes	 * The space required for an TSIG record is:
640135446Strhodes	 *
641135446Strhodes	 *	n1 bytes for the name
642135446Strhodes	 *	2 bytes for the type
643135446Strhodes	 *	2 bytes for the class
644135446Strhodes	 *	4 bytes for the ttl
645135446Strhodes	 *	2 bytes for the rdlength
646135446Strhodes	 *	n2 bytes for the algorithm name
647135446Strhodes	 *	6 bytes for the time signed
648135446Strhodes	 *	2 bytes for the fudge
649135446Strhodes	 *	2 bytes for the MAC size
650135446Strhodes	 *	x bytes for the MAC
651135446Strhodes	 *	2 bytes for the original id
652135446Strhodes	 *	2 bytes for the error
653135446Strhodes	 *	2 bytes for the other data length
654135446Strhodes	 *	y bytes for the other data (at most)
655135446Strhodes	 * ---------------------------------
656135446Strhodes	 *     26 + n1 + n2 + x + y bytes
657135446Strhodes	 */
658135446Strhodes
659135446Strhodes	dns_name_toregion(&key->name, &r1);
660135446Strhodes	dns_name_toregion(key->algorithm, &r2);
661135446Strhodes	if (key->key == NULL)
662135446Strhodes		x = 0;
663135446Strhodes	else {
664135446Strhodes		result = dst_key_sigsize(key->key, &x);
665135446Strhodes		if (result != ISC_R_SUCCESS)
666135446Strhodes			x = 0;
667135446Strhodes	}
668135446Strhodes	return (26 + r1.length + r2.length + x + otherlen);
669135446Strhodes}
670135446Strhodes
671135446Strhodesisc_result_t
672135446Strhodesdns_message_create(isc_mem_t *mctx, unsigned int intent, dns_message_t **msgp)
673135446Strhodes{
674135446Strhodes	dns_message_t *m;
675135446Strhodes	isc_result_t result;
676135446Strhodes	isc_buffer_t *dynbuf;
677135446Strhodes	unsigned int i;
678135446Strhodes
679135446Strhodes	REQUIRE(mctx != NULL);
680135446Strhodes	REQUIRE(msgp != NULL);
681135446Strhodes	REQUIRE(*msgp == NULL);
682135446Strhodes	REQUIRE(intent == DNS_MESSAGE_INTENTPARSE
683135446Strhodes		|| intent == DNS_MESSAGE_INTENTRENDER);
684135446Strhodes
685135446Strhodes	m = isc_mem_get(mctx, sizeof(dns_message_t));
686135446Strhodes	if (m == NULL)
687135446Strhodes		return (ISC_R_NOMEMORY);
688135446Strhodes
689135446Strhodes	/*
690135446Strhodes	 * No allocations until further notice.  Just initialize all lists
691135446Strhodes	 * and other members that are freed in the cleanup phase here.
692135446Strhodes	 */
693135446Strhodes
694135446Strhodes	m->magic = DNS_MESSAGE_MAGIC;
695135446Strhodes	m->from_to_wire = intent;
696135446Strhodes	msginit(m);
697135446Strhodes
698135446Strhodes	for (i = 0; i < DNS_SECTION_MAX; i++)
699135446Strhodes		ISC_LIST_INIT(m->sections[i]);
700135446Strhodes	m->mctx = mctx;
701135446Strhodes
702135446Strhodes	ISC_LIST_INIT(m->scratchpad);
703135446Strhodes	ISC_LIST_INIT(m->cleanup);
704135446Strhodes	m->namepool = NULL;
705135446Strhodes	m->rdspool = NULL;
706135446Strhodes	ISC_LIST_INIT(m->rdatas);
707135446Strhodes	ISC_LIST_INIT(m->rdatalists);
708135446Strhodes	ISC_LIST_INIT(m->offsets);
709135446Strhodes	ISC_LIST_INIT(m->freerdata);
710135446Strhodes	ISC_LIST_INIT(m->freerdatalist);
711135446Strhodes
712135446Strhodes	/*
713135446Strhodes	 * Ok, it is safe to allocate (and then "goto cleanup" if failure)
714135446Strhodes	 */
715135446Strhodes
716135446Strhodes	result = isc_mempool_create(m->mctx, sizeof(dns_name_t), &m->namepool);
717135446Strhodes	if (result != ISC_R_SUCCESS)
718135446Strhodes		goto cleanup;
719135446Strhodes	isc_mempool_setfreemax(m->namepool, NAME_COUNT);
720135446Strhodes	isc_mempool_setname(m->namepool, "msg:names");
721135446Strhodes
722135446Strhodes	result = isc_mempool_create(m->mctx, sizeof(dns_rdataset_t),
723135446Strhodes				    &m->rdspool);
724135446Strhodes	if (result != ISC_R_SUCCESS)
725135446Strhodes		goto cleanup;
726135446Strhodes	isc_mempool_setfreemax(m->rdspool, NAME_COUNT);
727135446Strhodes	isc_mempool_setname(m->rdspool, "msg:rdataset");
728135446Strhodes
729135446Strhodes	dynbuf = NULL;
730135446Strhodes	result = isc_buffer_allocate(mctx, &dynbuf, SCRATCHPAD_SIZE);
731135446Strhodes	if (result != ISC_R_SUCCESS)
732135446Strhodes		goto cleanup;
733135446Strhodes	ISC_LIST_APPEND(m->scratchpad, dynbuf, link);
734135446Strhodes
735135446Strhodes	m->cctx = NULL;
736135446Strhodes
737135446Strhodes	*msgp = m;
738135446Strhodes	return (ISC_R_SUCCESS);
739135446Strhodes
740135446Strhodes	/*
741135446Strhodes	 * Cleanup for error returns.
742135446Strhodes	 */
743135446Strhodes cleanup:
744135446Strhodes	dynbuf = ISC_LIST_HEAD(m->scratchpad);
745135446Strhodes	if (dynbuf != NULL) {
746135446Strhodes		ISC_LIST_UNLINK(m->scratchpad, dynbuf, link);
747135446Strhodes		isc_buffer_free(&dynbuf);
748135446Strhodes	}
749135446Strhodes	if (m->namepool != NULL)
750135446Strhodes		isc_mempool_destroy(&m->namepool);
751135446Strhodes	if (m->rdspool != NULL)
752135446Strhodes		isc_mempool_destroy(&m->rdspool);
753135446Strhodes	m->magic = 0;
754135446Strhodes	isc_mem_put(mctx, m, sizeof(dns_message_t));
755135446Strhodes
756135446Strhodes	return (ISC_R_NOMEMORY);
757135446Strhodes}
758135446Strhodes
759135446Strhodesvoid
760135446Strhodesdns_message_reset(dns_message_t *msg, unsigned int intent) {
761135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
762135446Strhodes	REQUIRE(intent == DNS_MESSAGE_INTENTPARSE
763135446Strhodes		|| intent == DNS_MESSAGE_INTENTRENDER);
764135446Strhodes
765135446Strhodes	msgreset(msg, ISC_FALSE);
766135446Strhodes	msg->from_to_wire = intent;
767135446Strhodes}
768135446Strhodes
769135446Strhodesvoid
770135446Strhodesdns_message_destroy(dns_message_t **msgp) {
771135446Strhodes	dns_message_t *msg;
772135446Strhodes
773135446Strhodes	REQUIRE(msgp != NULL);
774135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(*msgp));
775135446Strhodes
776135446Strhodes	msg = *msgp;
777135446Strhodes	*msgp = NULL;
778135446Strhodes
779135446Strhodes	msgreset(msg, ISC_TRUE);
780135446Strhodes	isc_mempool_destroy(&msg->namepool);
781135446Strhodes	isc_mempool_destroy(&msg->rdspool);
782135446Strhodes	msg->magic = 0;
783135446Strhodes	isc_mem_put(msg->mctx, msg, sizeof(dns_message_t));
784135446Strhodes}
785135446Strhodes
786135446Strhodesstatic isc_result_t
787135446Strhodesfindname(dns_name_t **foundname, dns_name_t *target,
788135446Strhodes	 dns_namelist_t *section)
789135446Strhodes{
790135446Strhodes	dns_name_t *curr;
791135446Strhodes
792135446Strhodes	for (curr = ISC_LIST_TAIL(*section);
793135446Strhodes	     curr != NULL;
794135446Strhodes	     curr = ISC_LIST_PREV(curr, link)) {
795135446Strhodes		if (dns_name_equal(curr, target)) {
796135446Strhodes			if (foundname != NULL)
797135446Strhodes				*foundname = curr;
798135446Strhodes			return (ISC_R_SUCCESS);
799135446Strhodes		}
800135446Strhodes	}
801135446Strhodes
802135446Strhodes	return (ISC_R_NOTFOUND);
803135446Strhodes}
804135446Strhodes
805135446Strhodesisc_result_t
806165071Sdougbdns_message_find(dns_name_t *name, dns_rdataclass_t rdclass,
807165071Sdougb		 dns_rdatatype_t type, dns_rdatatype_t covers,
808165071Sdougb		 dns_rdataset_t **rdataset)
809165071Sdougb{
810165071Sdougb	dns_rdataset_t *curr;
811165071Sdougb
812165071Sdougb	if (rdataset != NULL) {
813165071Sdougb		REQUIRE(*rdataset == NULL);
814165071Sdougb	}
815165071Sdougb
816165071Sdougb	for (curr = ISC_LIST_TAIL(name->list);
817165071Sdougb	     curr != NULL;
818165071Sdougb	     curr = ISC_LIST_PREV(curr, link)) {
819165071Sdougb		if (curr->rdclass == rdclass &&
820165071Sdougb		    curr->type == type && curr->covers == covers) {
821165071Sdougb			if (rdataset != NULL)
822165071Sdougb				*rdataset = curr;
823165071Sdougb			return (ISC_R_SUCCESS);
824165071Sdougb		}
825165071Sdougb	}
826165071Sdougb
827165071Sdougb	return (ISC_R_NOTFOUND);
828165071Sdougb}
829165071Sdougb
830165071Sdougbisc_result_t
831135446Strhodesdns_message_findtype(dns_name_t *name, dns_rdatatype_t type,
832135446Strhodes		     dns_rdatatype_t covers, dns_rdataset_t **rdataset)
833135446Strhodes{
834135446Strhodes	dns_rdataset_t *curr;
835135446Strhodes
836165071Sdougb	REQUIRE(name != NULL);
837135446Strhodes	if (rdataset != NULL) {
838135446Strhodes		REQUIRE(*rdataset == NULL);
839135446Strhodes	}
840135446Strhodes
841135446Strhodes	for (curr = ISC_LIST_TAIL(name->list);
842135446Strhodes	     curr != NULL;
843135446Strhodes	     curr = ISC_LIST_PREV(curr, link)) {
844135446Strhodes		if (curr->type == type && curr->covers == covers) {
845135446Strhodes			if (rdataset != NULL)
846135446Strhodes				*rdataset = curr;
847135446Strhodes			return (ISC_R_SUCCESS);
848135446Strhodes		}
849135446Strhodes	}
850135446Strhodes
851135446Strhodes	return (ISC_R_NOTFOUND);
852135446Strhodes}
853135446Strhodes
854135446Strhodes/*
855135446Strhodes * Read a name from buffer "source".
856135446Strhodes */
857135446Strhodesstatic isc_result_t
858135446Strhodesgetname(dns_name_t *name, isc_buffer_t *source, dns_message_t *msg,
859135446Strhodes	dns_decompress_t *dctx)
860135446Strhodes{
861135446Strhodes	isc_buffer_t *scratch;
862135446Strhodes	isc_result_t result;
863135446Strhodes	unsigned int tries;
864135446Strhodes
865135446Strhodes	scratch = currentbuffer(msg);
866135446Strhodes
867135446Strhodes	/*
868135446Strhodes	 * First try:  use current buffer.
869135446Strhodes	 * Second try:  allocate a new buffer and use that.
870135446Strhodes	 */
871135446Strhodes	tries = 0;
872135446Strhodes	while (tries < 2) {
873135446Strhodes		result = dns_name_fromwire(name, source, dctx, ISC_FALSE,
874135446Strhodes					   scratch);
875135446Strhodes
876135446Strhodes		if (result == ISC_R_NOSPACE) {
877135446Strhodes			tries++;
878135446Strhodes
879135446Strhodes			result = newbuffer(msg, SCRATCHPAD_SIZE);
880135446Strhodes			if (result != ISC_R_SUCCESS)
881135446Strhodes				return (result);
882135446Strhodes
883135446Strhodes			scratch = currentbuffer(msg);
884135446Strhodes			dns_name_reset(name);
885135446Strhodes		} else {
886135446Strhodes			return (result);
887135446Strhodes		}
888135446Strhodes	}
889135446Strhodes
890135446Strhodes	INSIST(0);  /* Cannot get here... */
891135446Strhodes	return (ISC_R_UNEXPECTED);
892135446Strhodes}
893135446Strhodes
894135446Strhodesstatic isc_result_t
895135446Strhodesgetrdata(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
896135446Strhodes	 dns_rdataclass_t rdclass, dns_rdatatype_t rdtype,
897135446Strhodes	 unsigned int rdatalen, dns_rdata_t *rdata)
898135446Strhodes{
899135446Strhodes	isc_buffer_t *scratch;
900135446Strhodes	isc_result_t result;
901135446Strhodes	unsigned int tries;
902135446Strhodes	unsigned int trysize;
903135446Strhodes
904135446Strhodes	scratch = currentbuffer(msg);
905135446Strhodes
906135446Strhodes	isc_buffer_setactive(source, rdatalen);
907135446Strhodes
908135446Strhodes	/*
909135446Strhodes	 * First try:  use current buffer.
910135446Strhodes	 * Second try:  allocate a new buffer of size
911135446Strhodes	 *     max(SCRATCHPAD_SIZE, 2 * compressed_rdatalen)
912135446Strhodes	 *     (the data will fit if it was not more than 50% compressed)
913135446Strhodes	 * Subsequent tries: double buffer size on each try.
914135446Strhodes	 */
915135446Strhodes	tries = 0;
916135446Strhodes	trysize = 0;
917135446Strhodes	/* XXX possibly change this to a while (tries < 2) loop */
918135446Strhodes	for (;;) {
919135446Strhodes		result = dns_rdata_fromwire(rdata, rdclass, rdtype,
920135446Strhodes					    source, dctx, 0,
921135446Strhodes					    scratch);
922135446Strhodes
923135446Strhodes		if (result == ISC_R_NOSPACE) {
924135446Strhodes			if (tries == 0) {
925135446Strhodes				trysize = 2 * rdatalen;
926135446Strhodes				if (trysize < SCRATCHPAD_SIZE)
927135446Strhodes					trysize = SCRATCHPAD_SIZE;
928135446Strhodes			} else {
929135446Strhodes				INSIST(trysize != 0);
930135446Strhodes				if (trysize >= 65535)
931135446Strhodes					return (ISC_R_NOSPACE);
932135446Strhodes					/* XXX DNS_R_RRTOOLONG? */
933135446Strhodes				trysize *= 2;
934135446Strhodes			}
935135446Strhodes			tries++;
936135446Strhodes			result = newbuffer(msg, trysize);
937135446Strhodes			if (result != ISC_R_SUCCESS)
938135446Strhodes				return (result);
939135446Strhodes
940135446Strhodes			scratch = currentbuffer(msg);
941135446Strhodes		} else {
942135446Strhodes			return (result);
943135446Strhodes		}
944135446Strhodes	}
945135446Strhodes}
946135446Strhodes
947135446Strhodes#define DO_FORMERR					\
948135446Strhodes	do {						\
949135446Strhodes		if (best_effort)			\
950135446Strhodes			seen_problem = ISC_TRUE;	\
951135446Strhodes		else {					\
952135446Strhodes			result = DNS_R_FORMERR;		\
953135446Strhodes			goto cleanup;			\
954135446Strhodes		}					\
955135446Strhodes	} while (0)
956135446Strhodes
957135446Strhodesstatic isc_result_t
958135446Strhodesgetquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
959135446Strhodes	     unsigned int options)
960135446Strhodes{
961135446Strhodes	isc_region_t r;
962135446Strhodes	unsigned int count;
963135446Strhodes	dns_name_t *name;
964135446Strhodes	dns_name_t *name2;
965135446Strhodes	dns_offsets_t *offsets;
966135446Strhodes	dns_rdataset_t *rdataset;
967135446Strhodes	dns_rdatalist_t *rdatalist;
968135446Strhodes	isc_result_t result;
969135446Strhodes	dns_rdatatype_t rdtype;
970135446Strhodes	dns_rdataclass_t rdclass;
971135446Strhodes	dns_namelist_t *section;
972135446Strhodes	isc_boolean_t free_name;
973135446Strhodes	isc_boolean_t best_effort;
974135446Strhodes	isc_boolean_t seen_problem;
975135446Strhodes
976135446Strhodes	section = &msg->sections[DNS_SECTION_QUESTION];
977135446Strhodes
978135446Strhodes	best_effort = ISC_TF(options & DNS_MESSAGEPARSE_BESTEFFORT);
979135446Strhodes	seen_problem = ISC_FALSE;
980135446Strhodes
981135446Strhodes	name = NULL;
982135446Strhodes	rdataset = NULL;
983135446Strhodes	rdatalist = NULL;
984135446Strhodes
985135446Strhodes	for (count = 0; count < msg->counts[DNS_SECTION_QUESTION]; count++) {
986135446Strhodes		name = isc_mempool_get(msg->namepool);
987135446Strhodes		if (name == NULL)
988135446Strhodes			return (ISC_R_NOMEMORY);
989135446Strhodes		free_name = ISC_TRUE;
990135446Strhodes
991135446Strhodes		offsets = newoffsets(msg);
992135446Strhodes		if (offsets == NULL) {
993135446Strhodes			result = ISC_R_NOMEMORY;
994135446Strhodes			goto cleanup;
995135446Strhodes		}
996135446Strhodes		dns_name_init(name, *offsets);
997135446Strhodes
998135446Strhodes		/*
999135446Strhodes		 * Parse the name out of this packet.
1000135446Strhodes		 */
1001135446Strhodes		isc_buffer_remainingregion(source, &r);
1002135446Strhodes		isc_buffer_setactive(source, r.length);
1003135446Strhodes		result = getname(name, source, msg, dctx);
1004135446Strhodes		if (result != ISC_R_SUCCESS)
1005135446Strhodes			goto cleanup;
1006135446Strhodes
1007135446Strhodes		/*
1008135446Strhodes		 * Run through the section, looking to see if this name
1009135446Strhodes		 * is already there.  If it is found, put back the allocated
1010135446Strhodes		 * name since we no longer need it, and set our name pointer
1011135446Strhodes		 * to point to the name we found.
1012135446Strhodes		 */
1013135446Strhodes		result = findname(&name2, name, section);
1014135446Strhodes
1015135446Strhodes		/*
1016135446Strhodes		 * If it is the first name in the section, accept it.
1017135446Strhodes		 *
1018135446Strhodes		 * If it is not, but is not the same as the name already
1019135446Strhodes		 * in the question section, append to the section.  Note that
1020135446Strhodes		 * here in the question section this is illegal, so return
1021135446Strhodes		 * FORMERR.  In the future, check the opcode to see if
1022135446Strhodes		 * this should be legal or not.  In either case we no longer
1023135446Strhodes		 * need this name pointer.
1024135446Strhodes		 */
1025135446Strhodes		if (result != ISC_R_SUCCESS) {
1026135446Strhodes			if (!ISC_LIST_EMPTY(*section))
1027135446Strhodes				DO_FORMERR;
1028135446Strhodes			ISC_LIST_APPEND(*section, name, link);
1029135446Strhodes			free_name = ISC_FALSE;
1030135446Strhodes		} else {
1031135446Strhodes			isc_mempool_put(msg->namepool, name);
1032135446Strhodes			name = name2;
1033135446Strhodes			name2 = NULL;
1034135446Strhodes			free_name = ISC_FALSE;
1035135446Strhodes		}
1036135446Strhodes
1037135446Strhodes		/*
1038135446Strhodes		 * Get type and class.
1039135446Strhodes		 */
1040135446Strhodes		isc_buffer_remainingregion(source, &r);
1041135446Strhodes		if (r.length < 4) {
1042135446Strhodes			result = ISC_R_UNEXPECTEDEND;
1043135446Strhodes			goto cleanup;
1044135446Strhodes		}
1045135446Strhodes		rdtype = isc_buffer_getuint16(source);
1046135446Strhodes		rdclass = isc_buffer_getuint16(source);
1047135446Strhodes
1048135446Strhodes		/*
1049135446Strhodes		 * If this class is different than the one we already read,
1050135446Strhodes		 * this is an error.
1051135446Strhodes		 */
1052135446Strhodes		if (msg->state == DNS_SECTION_ANY) {
1053135446Strhodes			msg->state = DNS_SECTION_QUESTION;
1054135446Strhodes			msg->rdclass = rdclass;
1055135446Strhodes		} else if (msg->rdclass != rdclass)
1056135446Strhodes			DO_FORMERR;
1057135446Strhodes
1058135446Strhodes		/*
1059135446Strhodes		 * Can't ask the same question twice.
1060135446Strhodes		 */
1061165071Sdougb		result = dns_message_find(name, rdclass, rdtype, 0, NULL);
1062135446Strhodes		if (result == ISC_R_SUCCESS)
1063135446Strhodes			DO_FORMERR;
1064135446Strhodes
1065135446Strhodes		/*
1066135446Strhodes		 * Allocate a new rdatalist.
1067135446Strhodes		 */
1068135446Strhodes		rdatalist = newrdatalist(msg);
1069135446Strhodes		if (rdatalist == NULL) {
1070135446Strhodes			result = ISC_R_NOMEMORY;
1071135446Strhodes			goto cleanup;
1072135446Strhodes		}
1073135446Strhodes		rdataset =  isc_mempool_get(msg->rdspool);
1074135446Strhodes		if (rdataset == NULL) {
1075135446Strhodes			result = ISC_R_NOMEMORY;
1076135446Strhodes			goto cleanup;
1077135446Strhodes		}
1078135446Strhodes
1079135446Strhodes		/*
1080135446Strhodes		 * Convert rdatalist to rdataset, and attach the latter to
1081135446Strhodes		 * the name.
1082135446Strhodes		 */
1083135446Strhodes		rdatalist->type = rdtype;
1084135446Strhodes		rdatalist->covers = 0;
1085135446Strhodes		rdatalist->rdclass = rdclass;
1086135446Strhodes		rdatalist->ttl = 0;
1087135446Strhodes		ISC_LIST_INIT(rdatalist->rdata);
1088135446Strhodes
1089135446Strhodes		dns_rdataset_init(rdataset);
1090135446Strhodes		result = dns_rdatalist_tordataset(rdatalist, rdataset);
1091135446Strhodes		if (result != ISC_R_SUCCESS)
1092135446Strhodes			goto cleanup;
1093135446Strhodes
1094135446Strhodes		rdataset->attributes |= DNS_RDATASETATTR_QUESTION;
1095135446Strhodes
1096135446Strhodes		ISC_LIST_APPEND(name->list, rdataset, link);
1097135446Strhodes		rdataset = NULL;
1098135446Strhodes	}
1099135446Strhodes
1100135446Strhodes	if (seen_problem)
1101135446Strhodes		return (DNS_R_RECOVERABLE);
1102135446Strhodes	return (ISC_R_SUCCESS);
1103135446Strhodes
1104135446Strhodes cleanup:
1105135446Strhodes	if (rdataset != NULL) {
1106135446Strhodes		INSIST(!dns_rdataset_isassociated(rdataset));
1107135446Strhodes		isc_mempool_put(msg->rdspool, rdataset);
1108135446Strhodes	}
1109135446Strhodes#if 0
1110135446Strhodes	if (rdatalist != NULL)
1111135446Strhodes		isc_mempool_put(msg->rdlpool, rdatalist);
1112135446Strhodes#endif
1113135446Strhodes	if (free_name)
1114135446Strhodes		isc_mempool_put(msg->namepool, name);
1115135446Strhodes
1116135446Strhodes	return (result);
1117135446Strhodes}
1118135446Strhodes
1119135446Strhodesstatic isc_boolean_t
1120135446Strhodesupdate(dns_section_t section, dns_rdataclass_t rdclass) {
1121135446Strhodes	if (section == DNS_SECTION_PREREQUISITE)
1122135446Strhodes		return (ISC_TF(rdclass == dns_rdataclass_any ||
1123135446Strhodes			       rdclass == dns_rdataclass_none));
1124135446Strhodes	if (section == DNS_SECTION_UPDATE)
1125135446Strhodes		return (ISC_TF(rdclass == dns_rdataclass_any));
1126135446Strhodes	return (ISC_FALSE);
1127135446Strhodes}
1128135446Strhodes
1129135446Strhodesstatic isc_result_t
1130135446Strhodesgetsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
1131135446Strhodes	   dns_section_t sectionid, unsigned int options)
1132135446Strhodes{
1133135446Strhodes	isc_region_t r;
1134135446Strhodes	unsigned int count, rdatalen;
1135135446Strhodes	dns_name_t *name;
1136135446Strhodes	dns_name_t *name2;
1137135446Strhodes	dns_offsets_t *offsets;
1138135446Strhodes	dns_rdataset_t *rdataset;
1139135446Strhodes	dns_rdatalist_t *rdatalist;
1140135446Strhodes	isc_result_t result;
1141135446Strhodes	dns_rdatatype_t rdtype, covers;
1142135446Strhodes	dns_rdataclass_t rdclass;
1143135446Strhodes	dns_rdata_t *rdata;
1144135446Strhodes	dns_ttl_t ttl;
1145135446Strhodes	dns_namelist_t *section;
1146135446Strhodes	isc_boolean_t free_name, free_rdataset;
1147135446Strhodes	isc_boolean_t preserve_order, best_effort, seen_problem;
1148135446Strhodes	isc_boolean_t issigzero;
1149135446Strhodes
1150135446Strhodes	preserve_order = ISC_TF(options & DNS_MESSAGEPARSE_PRESERVEORDER);
1151135446Strhodes	best_effort = ISC_TF(options & DNS_MESSAGEPARSE_BESTEFFORT);
1152135446Strhodes	seen_problem = ISC_FALSE;
1153135446Strhodes
1154135446Strhodes	for (count = 0; count < msg->counts[sectionid]; count++) {
1155135446Strhodes		int recstart = source->current;
1156135446Strhodes		isc_boolean_t skip_name_search, skip_type_search;
1157135446Strhodes
1158135446Strhodes		section = &msg->sections[sectionid];
1159135446Strhodes
1160135446Strhodes		skip_name_search = ISC_FALSE;
1161135446Strhodes		skip_type_search = ISC_FALSE;
1162135446Strhodes		free_name = ISC_FALSE;
1163135446Strhodes		free_rdataset = ISC_FALSE;
1164135446Strhodes
1165135446Strhodes		name = isc_mempool_get(msg->namepool);
1166135446Strhodes		if (name == NULL)
1167135446Strhodes			return (ISC_R_NOMEMORY);
1168135446Strhodes		free_name = ISC_TRUE;
1169135446Strhodes
1170135446Strhodes		offsets = newoffsets(msg);
1171135446Strhodes		if (offsets == NULL) {
1172135446Strhodes			result = ISC_R_NOMEMORY;
1173135446Strhodes			goto cleanup;
1174135446Strhodes		}
1175135446Strhodes		dns_name_init(name, *offsets);
1176135446Strhodes
1177135446Strhodes		/*
1178135446Strhodes		 * Parse the name out of this packet.
1179135446Strhodes		 */
1180135446Strhodes		isc_buffer_remainingregion(source, &r);
1181135446Strhodes		isc_buffer_setactive(source, r.length);
1182135446Strhodes		result = getname(name, source, msg, dctx);
1183135446Strhodes		if (result != ISC_R_SUCCESS)
1184135446Strhodes			goto cleanup;
1185135446Strhodes
1186135446Strhodes		/*
1187135446Strhodes		 * Get type, class, ttl, and rdatalen.  Verify that at least
1188135446Strhodes		 * rdatalen bytes remain.  (Some of this is deferred to
1189135446Strhodes		 * later.)
1190135446Strhodes		 */
1191135446Strhodes		isc_buffer_remainingregion(source, &r);
1192135446Strhodes		if (r.length < 2 + 2 + 4 + 2) {
1193135446Strhodes			result = ISC_R_UNEXPECTEDEND;
1194135446Strhodes			goto cleanup;
1195135446Strhodes		}
1196135446Strhodes		rdtype = isc_buffer_getuint16(source);
1197135446Strhodes		rdclass = isc_buffer_getuint16(source);
1198135446Strhodes
1199135446Strhodes		/*
1200135446Strhodes		 * If there was no question section, we may not yet have
1201135446Strhodes		 * established a class.  Do so now.
1202135446Strhodes		 */
1203135446Strhodes		if (msg->state == DNS_SECTION_ANY &&
1204135446Strhodes		    rdtype != dns_rdatatype_opt &&	/* class is UDP SIZE */
1205135446Strhodes		    rdtype != dns_rdatatype_tsig &&	/* class is ANY */
1206135446Strhodes		    rdtype != dns_rdatatype_tkey) {	/* class is undefined */
1207135446Strhodes			msg->rdclass = rdclass;
1208135446Strhodes			msg->state = DNS_SECTION_QUESTION;
1209135446Strhodes		}
1210135446Strhodes
1211135446Strhodes		/*
1212135446Strhodes		 * If this class is different than the one in the question
1213135446Strhodes		 * section, bail.
1214135446Strhodes		 */
1215135446Strhodes		if (msg->opcode != dns_opcode_update
1216135446Strhodes		    && rdtype != dns_rdatatype_tsig
1217135446Strhodes		    && rdtype != dns_rdatatype_opt
1218135446Strhodes		    && rdtype != dns_rdatatype_dnskey /* in a TKEY query */
1219135446Strhodes		    && rdtype != dns_rdatatype_sig /* SIG(0) */
1220135446Strhodes		    && rdtype != dns_rdatatype_tkey /* Win2000 TKEY */
1221165071Sdougb		    && msg->rdclass != dns_rdataclass_any
1222135446Strhodes		    && msg->rdclass != rdclass)
1223135446Strhodes			DO_FORMERR;
1224135446Strhodes
1225135446Strhodes		/*
1226135446Strhodes		 * Special type handling for TSIG, OPT, and TKEY.
1227135446Strhodes		 */
1228135446Strhodes		if (rdtype == dns_rdatatype_tsig) {
1229135446Strhodes			/*
1230135446Strhodes			 * If it is a tsig, verify that it is in the
1231135446Strhodes			 * additional data section.
1232135446Strhodes			 */
1233135446Strhodes			if (sectionid != DNS_SECTION_ADDITIONAL ||
1234135446Strhodes			    rdclass != dns_rdataclass_any ||
1235135446Strhodes			    count != msg->counts[sectionid]  - 1)
1236135446Strhodes				DO_FORMERR;
1237135446Strhodes			msg->sigstart = recstart;
1238135446Strhodes			skip_name_search = ISC_TRUE;
1239135446Strhodes			skip_type_search = ISC_TRUE;
1240135446Strhodes		} else if (rdtype == dns_rdatatype_opt) {
1241135446Strhodes			/*
1242135446Strhodes			 * The name of an OPT record must be ".", it
1243135446Strhodes			 * must be in the additional data section, and
1244135446Strhodes			 * it must be the first OPT we've seen.
1245135446Strhodes			 */
1246135446Strhodes			if (!dns_name_equal(dns_rootname, name) ||
1247135446Strhodes			    msg->opt != NULL)
1248135446Strhodes				DO_FORMERR;
1249135446Strhodes			skip_name_search = ISC_TRUE;
1250135446Strhodes			skip_type_search = ISC_TRUE;
1251135446Strhodes		} else if (rdtype == dns_rdatatype_tkey) {
1252135446Strhodes			/*
1253135446Strhodes			 * A TKEY must be in the additional section if this
1254135446Strhodes			 * is a query, and the answer section if this is a
1255135446Strhodes			 * response.  Unless it's a Win2000 client.
1256135446Strhodes			 *
1257135446Strhodes			 * Its class is ignored.
1258135446Strhodes			 */
1259135446Strhodes			dns_section_t tkeysection;
1260135446Strhodes
1261135446Strhodes			if ((msg->flags & DNS_MESSAGEFLAG_QR) == 0)
1262135446Strhodes				tkeysection = DNS_SECTION_ADDITIONAL;
1263135446Strhodes			else
1264135446Strhodes				tkeysection = DNS_SECTION_ANSWER;
1265135446Strhodes			if (sectionid != tkeysection &&
1266135446Strhodes			    sectionid != DNS_SECTION_ANSWER)
1267135446Strhodes				DO_FORMERR;
1268135446Strhodes		}
1269135446Strhodes
1270135446Strhodes		/*
1271135446Strhodes		 * ... now get ttl and rdatalen, and check buffer.
1272135446Strhodes		 */
1273135446Strhodes		ttl = isc_buffer_getuint32(source);
1274135446Strhodes		rdatalen = isc_buffer_getuint16(source);
1275135446Strhodes		r.length -= (2 + 2 + 4 + 2);
1276135446Strhodes		if (r.length < rdatalen) {
1277135446Strhodes			result = ISC_R_UNEXPECTEDEND;
1278135446Strhodes			goto cleanup;
1279135446Strhodes		}
1280135446Strhodes
1281135446Strhodes		/*
1282135446Strhodes		 * Read the rdata from the wire format.  Interpret the
1283135446Strhodes		 * rdata according to its actual class, even if it had a
1284135446Strhodes		 * DynDNS meta-class in the packet (unless this is a TSIG).
1285135446Strhodes		 * Then put the meta-class back into the finished rdata.
1286135446Strhodes		 */
1287135446Strhodes		rdata = newrdata(msg);
1288135446Strhodes		if (rdata == NULL) {
1289135446Strhodes			result = ISC_R_NOMEMORY;
1290135446Strhodes			goto cleanup;
1291135446Strhodes		}
1292135446Strhodes		if (msg->opcode == dns_opcode_update &&
1293135446Strhodes		    update(sectionid, rdclass)) {
1294135446Strhodes			if (rdatalen != 0) {
1295135446Strhodes				result = DNS_R_FORMERR;
1296135446Strhodes				goto cleanup;
1297135446Strhodes			}
1298135446Strhodes			/*
1299135446Strhodes			 * When the rdata is empty, the data pointer is
1300135446Strhodes			 * never dereferenced, but it must still be non-NULL.
1301135446Strhodes			 * Casting 1 rather than "" avoids warnings about
1302135446Strhodes			 * discarding the const attribute of a string,
1303135446Strhodes			 * for compilers that would warn about such things.
1304135446Strhodes			 */
1305135446Strhodes			rdata->data = (unsigned char *)1;
1306135446Strhodes			rdata->length = 0;
1307135446Strhodes			rdata->rdclass = rdclass;
1308135446Strhodes			rdata->type = rdtype;
1309135446Strhodes			rdata->flags = DNS_RDATA_UPDATE;
1310135446Strhodes			result = ISC_R_SUCCESS;
1311174187Sdougb		} else if (rdclass == dns_rdataclass_none &&
1312174187Sdougb			   msg->opcode == dns_opcode_update &&
1313174187Sdougb			   sectionid == DNS_SECTION_UPDATE) {
1314174187Sdougb			result = getrdata(source, msg, dctx, msg->rdclass,
1315174187Sdougb					  rdtype, rdatalen, rdata);
1316165071Sdougb		} else
1317135446Strhodes			result = getrdata(source, msg, dctx, rdclass,
1318135446Strhodes					  rdtype, rdatalen, rdata);
1319135446Strhodes		if (result != ISC_R_SUCCESS)
1320135446Strhodes			goto cleanup;
1321135446Strhodes		rdata->rdclass = rdclass;
1322135446Strhodes		issigzero = ISC_FALSE;
1323135446Strhodes		if (rdtype == dns_rdatatype_rrsig  &&
1324135446Strhodes		    rdata->flags == 0) {
1325135446Strhodes			covers = dns_rdata_covers(rdata);
1326135446Strhodes			if (covers == 0)
1327135446Strhodes				DO_FORMERR;
1328135446Strhodes		} else if (rdtype == dns_rdatatype_sig /* SIG(0) */ &&
1329135446Strhodes			   rdata->flags == 0) {
1330135446Strhodes			covers = dns_rdata_covers(rdata);
1331135446Strhodes			if (covers == 0) {
1332135446Strhodes				if (sectionid != DNS_SECTION_ADDITIONAL ||
1333135446Strhodes				    count != msg->counts[sectionid]  - 1)
1334135446Strhodes					DO_FORMERR;
1335135446Strhodes				msg->sigstart = recstart;
1336135446Strhodes				skip_name_search = ISC_TRUE;
1337135446Strhodes				skip_type_search = ISC_TRUE;
1338135446Strhodes				issigzero = ISC_TRUE;
1339135446Strhodes			}
1340135446Strhodes		} else
1341135446Strhodes			covers = 0;
1342135446Strhodes
1343135446Strhodes		/*
1344135446Strhodes		 * If we are doing a dynamic update or this is a meta-type,
1345135446Strhodes		 * don't bother searching for a name, just append this one
1346135446Strhodes		 * to the end of the message.
1347135446Strhodes		 */
1348135446Strhodes		if (preserve_order || msg->opcode == dns_opcode_update ||
1349135446Strhodes		    skip_name_search) {
1350135446Strhodes			if (rdtype != dns_rdatatype_opt &&
1351135446Strhodes			    rdtype != dns_rdatatype_tsig &&
1352135446Strhodes			    !issigzero)
1353135446Strhodes			{
1354135446Strhodes				ISC_LIST_APPEND(*section, name, link);
1355135446Strhodes				free_name = ISC_FALSE;
1356135446Strhodes			}
1357135446Strhodes		} else {
1358135446Strhodes			/*
1359135446Strhodes			 * Run through the section, looking to see if this name
1360135446Strhodes			 * is already there.  If it is found, put back the
1361135446Strhodes			 * allocated name since we no longer need it, and set
1362135446Strhodes			 * our name pointer to point to the name we found.
1363135446Strhodes			 */
1364135446Strhodes			result = findname(&name2, name, section);
1365135446Strhodes
1366135446Strhodes			/*
1367135446Strhodes			 * If it is a new name, append to the section.
1368135446Strhodes			 */
1369135446Strhodes			if (result == ISC_R_SUCCESS) {
1370135446Strhodes				isc_mempool_put(msg->namepool, name);
1371135446Strhodes				name = name2;
1372135446Strhodes			} else {
1373135446Strhodes				ISC_LIST_APPEND(*section, name, link);
1374135446Strhodes			}
1375135446Strhodes			free_name = ISC_FALSE;
1376135446Strhodes		}
1377135446Strhodes
1378135446Strhodes		/*
1379135446Strhodes		 * Search name for the particular type and class.
1380135446Strhodes		 * Skip this stage if in update mode or this is a meta-type.
1381135446Strhodes		 */
1382135446Strhodes		if (preserve_order || msg->opcode == dns_opcode_update ||
1383135446Strhodes		    skip_type_search)
1384135446Strhodes			result = ISC_R_NOTFOUND;
1385135446Strhodes		else {
1386135446Strhodes			/*
1387135446Strhodes			 * If this is a type that can only occur in
1388135446Strhodes			 * the question section, fail.
1389135446Strhodes			 */
1390135446Strhodes			if (dns_rdatatype_questiononly(rdtype))
1391135446Strhodes				DO_FORMERR;
1392135446Strhodes
1393135446Strhodes			rdataset = NULL;
1394165071Sdougb			result = dns_message_find(name, rdclass, rdtype,
1395165071Sdougb						   covers, &rdataset);
1396135446Strhodes		}
1397135446Strhodes
1398135446Strhodes		/*
1399135446Strhodes		 * If we found an rdataset that matches, we need to
1400135446Strhodes		 * append this rdata to that set.  If we did not, we need
1401135446Strhodes		 * to create a new rdatalist, store the important bits there,
1402135446Strhodes		 * convert it to an rdataset, and link the latter to the name.
1403135446Strhodes		 * Yuck.  When appending, make certain that the type isn't
1404135446Strhodes		 * a singleton type, such as SOA or CNAME.
1405135446Strhodes		 *
1406135446Strhodes		 * Note that this check will be bypassed when preserving order,
1407135446Strhodes		 * the opcode is an update, or the type search is skipped.
1408135446Strhodes		 */
1409135446Strhodes		if (result == ISC_R_SUCCESS) {
1410135446Strhodes			if (dns_rdatatype_issingleton(rdtype))
1411135446Strhodes				DO_FORMERR;
1412135446Strhodes		}
1413135446Strhodes
1414135446Strhodes		if (result == ISC_R_NOTFOUND) {
1415135446Strhodes			rdataset = isc_mempool_get(msg->rdspool);
1416135446Strhodes			if (rdataset == NULL) {
1417135446Strhodes				result = ISC_R_NOMEMORY;
1418135446Strhodes				goto cleanup;
1419135446Strhodes			}
1420135446Strhodes			free_rdataset = ISC_TRUE;
1421135446Strhodes
1422135446Strhodes			rdatalist = newrdatalist(msg);
1423135446Strhodes			if (rdatalist == NULL) {
1424135446Strhodes				result = ISC_R_NOMEMORY;
1425135446Strhodes				goto cleanup;
1426135446Strhodes			}
1427135446Strhodes
1428135446Strhodes			rdatalist->type = rdtype;
1429135446Strhodes			rdatalist->covers = covers;
1430135446Strhodes			rdatalist->rdclass = rdclass;
1431135446Strhodes			rdatalist->ttl = ttl;
1432135446Strhodes			ISC_LIST_INIT(rdatalist->rdata);
1433135446Strhodes
1434135446Strhodes			dns_rdataset_init(rdataset);
1435135446Strhodes			RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist,
1436135446Strhodes							       rdataset)
1437135446Strhodes				      == ISC_R_SUCCESS);
1438135446Strhodes
1439135446Strhodes			if (rdtype != dns_rdatatype_opt &&
1440135446Strhodes			    rdtype != dns_rdatatype_tsig &&
1441135446Strhodes			    !issigzero)
1442135446Strhodes			{
1443135446Strhodes				ISC_LIST_APPEND(name->list, rdataset, link);
1444135446Strhodes				free_rdataset = ISC_FALSE;
1445135446Strhodes			}
1446135446Strhodes		}
1447135446Strhodes
1448135446Strhodes		/*
1449135446Strhodes		 * Minimize TTLs.
1450135446Strhodes		 *
1451170222Sdougb		 * Section 5.2 of RFC2181 says we should drop
1452135446Strhodes		 * nonauthoritative rrsets where the TTLs differ, but we
1453135446Strhodes		 * currently treat them the as if they were authoritative and
1454135446Strhodes		 * minimize them.
1455135446Strhodes		 */
1456135446Strhodes		if (ttl != rdataset->ttl) {
1457135446Strhodes			rdataset->attributes |= DNS_RDATASETATTR_TTLADJUSTED;
1458135446Strhodes			if (ttl < rdataset->ttl)
1459135446Strhodes				rdataset->ttl = ttl;
1460135446Strhodes		}
1461135446Strhodes
1462135446Strhodes		/*
1463135446Strhodes		 * XXXMLG Perform a totally ugly hack here to pull
1464135446Strhodes		 * the rdatalist out of the private field in the rdataset,
1465135446Strhodes		 * and append this rdata to the rdatalist's linked list
1466135446Strhodes		 * of rdata.
1467135446Strhodes		 */
1468135446Strhodes		rdatalist = (dns_rdatalist_t *)(rdataset->private1);
1469135446Strhodes
1470135446Strhodes		ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
1471135446Strhodes
1472135446Strhodes		/*
1473135446Strhodes		 * If this is an OPT record, remember it.  Also, set
1474135446Strhodes		 * the extended rcode.  Note that msg->opt will only be set
1475135446Strhodes		 * if best-effort parsing is enabled.
1476135446Strhodes		 */
1477135446Strhodes		if (rdtype == dns_rdatatype_opt && msg->opt == NULL) {
1478135446Strhodes			dns_rcode_t ercode;
1479135446Strhodes
1480135446Strhodes			msg->opt = rdataset;
1481135446Strhodes			rdataset = NULL;
1482135446Strhodes			free_rdataset = ISC_FALSE;
1483135446Strhodes			ercode = (dns_rcode_t)
1484135446Strhodes				((msg->opt->ttl & DNS_MESSAGE_EDNSRCODE_MASK)
1485135446Strhodes				 >> 20);
1486135446Strhodes			msg->rcode |= ercode;
1487135446Strhodes			isc_mempool_put(msg->namepool, name);
1488135446Strhodes			free_name = ISC_FALSE;
1489135446Strhodes		}
1490135446Strhodes
1491135446Strhodes		/*
1492135446Strhodes		 * If this is an SIG(0) or TSIG record, remember it.  Note
1493135446Strhodes		 * that msg->sig0 or msg->tsig will only be set if best-effort
1494135446Strhodes		 * parsing is enabled.
1495135446Strhodes		 */
1496135446Strhodes		if (issigzero && msg->sig0 == NULL) {
1497135446Strhodes			msg->sig0 = rdataset;
1498135446Strhodes			msg->sig0name = name;
1499135446Strhodes			rdataset = NULL;
1500135446Strhodes			free_rdataset = ISC_FALSE;
1501135446Strhodes			free_name = ISC_FALSE;
1502135446Strhodes		} else if (rdtype == dns_rdatatype_tsig && msg->tsig == NULL) {
1503135446Strhodes			msg->tsig = rdataset;
1504135446Strhodes			msg->tsigname = name;
1505135446Strhodes			rdataset = NULL;
1506135446Strhodes			free_rdataset = ISC_FALSE;
1507135446Strhodes			free_name = ISC_FALSE;
1508135446Strhodes		}
1509135446Strhodes
1510153816Sdougb		if (seen_problem) {
1511153816Sdougb			if (free_name)
1512153816Sdougb				isc_mempool_put(msg->namepool, name);
1513153816Sdougb			if (free_rdataset)
1514153816Sdougb				isc_mempool_put(msg->rdspool, rdataset);
1515153816Sdougb			free_name = free_rdataset = ISC_FALSE;
1516153816Sdougb		}
1517135446Strhodes		INSIST(free_name == ISC_FALSE);
1518135446Strhodes		INSIST(free_rdataset == ISC_FALSE);
1519135446Strhodes	}
1520135446Strhodes
1521135446Strhodes	if (seen_problem)
1522135446Strhodes		return (DNS_R_RECOVERABLE);
1523135446Strhodes	return (ISC_R_SUCCESS);
1524135446Strhodes
1525135446Strhodes cleanup:
1526135446Strhodes	if (free_name)
1527135446Strhodes		isc_mempool_put(msg->namepool, name);
1528135446Strhodes	if (free_rdataset)
1529135446Strhodes		isc_mempool_put(msg->rdspool, rdataset);
1530135446Strhodes
1531135446Strhodes	return (result);
1532135446Strhodes}
1533135446Strhodes
1534135446Strhodesisc_result_t
1535135446Strhodesdns_message_parse(dns_message_t *msg, isc_buffer_t *source,
1536135446Strhodes		  unsigned int options)
1537135446Strhodes{
1538135446Strhodes	isc_region_t r;
1539135446Strhodes	dns_decompress_t dctx;
1540135446Strhodes	isc_result_t ret;
1541135446Strhodes	isc_uint16_t tmpflags;
1542135446Strhodes	isc_buffer_t origsource;
1543135446Strhodes	isc_boolean_t seen_problem;
1544135446Strhodes	isc_boolean_t ignore_tc;
1545135446Strhodes
1546135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
1547135446Strhodes	REQUIRE(source != NULL);
1548135446Strhodes	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTPARSE);
1549135446Strhodes
1550135446Strhodes	seen_problem = ISC_FALSE;
1551135446Strhodes	ignore_tc = ISC_TF(options & DNS_MESSAGEPARSE_IGNORETRUNCATION);
1552135446Strhodes
1553135446Strhodes	origsource = *source;
1554135446Strhodes
1555135446Strhodes	msg->header_ok = 0;
1556135446Strhodes	msg->question_ok = 0;
1557135446Strhodes
1558135446Strhodes	isc_buffer_remainingregion(source, &r);
1559135446Strhodes	if (r.length < DNS_MESSAGE_HEADERLEN)
1560135446Strhodes		return (ISC_R_UNEXPECTEDEND);
1561135446Strhodes
1562135446Strhodes	msg->id = isc_buffer_getuint16(source);
1563135446Strhodes	tmpflags = isc_buffer_getuint16(source);
1564135446Strhodes	msg->opcode = ((tmpflags & DNS_MESSAGE_OPCODE_MASK)
1565135446Strhodes		       >> DNS_MESSAGE_OPCODE_SHIFT);
1566135446Strhodes	msg->rcode = (dns_rcode_t)(tmpflags & DNS_MESSAGE_RCODE_MASK);
1567135446Strhodes	msg->flags = (tmpflags & DNS_MESSAGE_FLAG_MASK);
1568135446Strhodes	msg->counts[DNS_SECTION_QUESTION] = isc_buffer_getuint16(source);
1569135446Strhodes	msg->counts[DNS_SECTION_ANSWER] = isc_buffer_getuint16(source);
1570135446Strhodes	msg->counts[DNS_SECTION_AUTHORITY] = isc_buffer_getuint16(source);
1571135446Strhodes	msg->counts[DNS_SECTION_ADDITIONAL] = isc_buffer_getuint16(source);
1572135446Strhodes
1573135446Strhodes	msg->header_ok = 1;
1574135446Strhodes
1575135446Strhodes	/*
1576135446Strhodes	 * -1 means no EDNS.
1577135446Strhodes	 */
1578135446Strhodes	dns_decompress_init(&dctx, -1, DNS_DECOMPRESS_ANY);
1579135446Strhodes
1580135446Strhodes	dns_decompress_setmethods(&dctx, DNS_COMPRESS_GLOBAL14);
1581135446Strhodes
1582135446Strhodes	ret = getquestions(source, msg, &dctx, options);
1583135446Strhodes	if (ret == ISC_R_UNEXPECTEDEND && ignore_tc)
1584135446Strhodes		goto truncated;
1585135446Strhodes	if (ret == DNS_R_RECOVERABLE) {
1586135446Strhodes		seen_problem = ISC_TRUE;
1587135446Strhodes		ret = ISC_R_SUCCESS;
1588135446Strhodes	}
1589135446Strhodes	if (ret != ISC_R_SUCCESS)
1590135446Strhodes		return (ret);
1591135446Strhodes	msg->question_ok = 1;
1592135446Strhodes
1593135446Strhodes	ret = getsection(source, msg, &dctx, DNS_SECTION_ANSWER, options);
1594135446Strhodes	if (ret == ISC_R_UNEXPECTEDEND && ignore_tc)
1595135446Strhodes		goto truncated;
1596135446Strhodes	if (ret == DNS_R_RECOVERABLE) {
1597135446Strhodes		seen_problem = ISC_TRUE;
1598135446Strhodes		ret = ISC_R_SUCCESS;
1599135446Strhodes	}
1600135446Strhodes	if (ret != ISC_R_SUCCESS)
1601135446Strhodes		return (ret);
1602135446Strhodes
1603135446Strhodes	ret = getsection(source, msg, &dctx, DNS_SECTION_AUTHORITY, options);
1604135446Strhodes	if (ret == ISC_R_UNEXPECTEDEND && ignore_tc)
1605135446Strhodes		goto truncated;
1606135446Strhodes	if (ret == DNS_R_RECOVERABLE) {
1607135446Strhodes		seen_problem = ISC_TRUE;
1608135446Strhodes		ret = ISC_R_SUCCESS;
1609135446Strhodes	}
1610135446Strhodes	if (ret != ISC_R_SUCCESS)
1611135446Strhodes		return (ret);
1612135446Strhodes
1613135446Strhodes	ret = getsection(source, msg, &dctx, DNS_SECTION_ADDITIONAL, options);
1614135446Strhodes	if (ret == ISC_R_UNEXPECTEDEND && ignore_tc)
1615135446Strhodes		goto truncated;
1616135446Strhodes	if (ret == DNS_R_RECOVERABLE) {
1617135446Strhodes		seen_problem = ISC_TRUE;
1618135446Strhodes		ret = ISC_R_SUCCESS;
1619135446Strhodes	}
1620135446Strhodes	if (ret != ISC_R_SUCCESS)
1621135446Strhodes		return (ret);
1622135446Strhodes
1623135446Strhodes	isc_buffer_remainingregion(source, &r);
1624135446Strhodes	if (r.length != 0) {
1625135446Strhodes		isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
1626135446Strhodes			      DNS_LOGMODULE_MESSAGE, ISC_LOG_DEBUG(3),
1627135446Strhodes			      "message has %u byte(s) of trailing garbage",
1628135446Strhodes			      r.length);
1629135446Strhodes	}
1630135446Strhodes
1631135446Strhodes truncated:
1632135446Strhodes	if ((options & DNS_MESSAGEPARSE_CLONEBUFFER) == 0)
1633135446Strhodes		isc_buffer_usedregion(&origsource, &msg->saved);
1634135446Strhodes	else {
1635135446Strhodes		msg->saved.length = isc_buffer_usedlength(&origsource);
1636135446Strhodes		msg->saved.base = isc_mem_get(msg->mctx, msg->saved.length);
1637135446Strhodes		if (msg->saved.base == NULL)
1638135446Strhodes			return (ISC_R_NOMEMORY);
1639135446Strhodes		memcpy(msg->saved.base, isc_buffer_base(&origsource),
1640135446Strhodes		       msg->saved.length);
1641135446Strhodes		msg->free_saved = 1;
1642135446Strhodes	}
1643135446Strhodes
1644135446Strhodes	if (ret == ISC_R_UNEXPECTEDEND && ignore_tc)
1645135446Strhodes		return (DNS_R_RECOVERABLE);
1646135446Strhodes	if (seen_problem == ISC_TRUE)
1647135446Strhodes		return (DNS_R_RECOVERABLE);
1648135446Strhodes	return (ISC_R_SUCCESS);
1649135446Strhodes}
1650135446Strhodes
1651135446Strhodesisc_result_t
1652135446Strhodesdns_message_renderbegin(dns_message_t *msg, dns_compress_t *cctx,
1653135446Strhodes			isc_buffer_t *buffer)
1654135446Strhodes{
1655135446Strhodes	isc_region_t r;
1656135446Strhodes
1657135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
1658135446Strhodes	REQUIRE(buffer != NULL);
1659135446Strhodes	REQUIRE(msg->buffer == NULL);
1660135446Strhodes	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
1661135446Strhodes
1662135446Strhodes	msg->cctx = cctx;
1663135446Strhodes
1664135446Strhodes	/*
1665135446Strhodes	 * Erase the contents of this buffer.
1666135446Strhodes	 */
1667135446Strhodes	isc_buffer_clear(buffer);
1668135446Strhodes
1669135446Strhodes	/*
1670135446Strhodes	 * Make certain there is enough for at least the header in this
1671135446Strhodes	 * buffer.
1672135446Strhodes	 */
1673135446Strhodes	isc_buffer_availableregion(buffer, &r);
1674135446Strhodes	if (r.length < DNS_MESSAGE_HEADERLEN)
1675135446Strhodes		return (ISC_R_NOSPACE);
1676135446Strhodes
1677135446Strhodes	if (r.length < msg->reserved)
1678135446Strhodes		return (ISC_R_NOSPACE);
1679135446Strhodes
1680135446Strhodes	/*
1681135446Strhodes	 * Reserve enough space for the header in this buffer.
1682135446Strhodes	 */
1683135446Strhodes	isc_buffer_add(buffer, DNS_MESSAGE_HEADERLEN);
1684135446Strhodes
1685135446Strhodes	msg->buffer = buffer;
1686135446Strhodes
1687135446Strhodes	return (ISC_R_SUCCESS);
1688135446Strhodes}
1689135446Strhodes
1690135446Strhodesisc_result_t
1691135446Strhodesdns_message_renderchangebuffer(dns_message_t *msg, isc_buffer_t *buffer) {
1692135446Strhodes	isc_region_t r, rn;
1693135446Strhodes
1694135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
1695135446Strhodes	REQUIRE(buffer != NULL);
1696135446Strhodes	REQUIRE(msg->buffer != NULL);
1697135446Strhodes
1698135446Strhodes	/*
1699135446Strhodes	 * Ensure that the new buffer is empty, and has enough space to
1700135446Strhodes	 * hold the current contents.
1701135446Strhodes	 */
1702135446Strhodes	isc_buffer_clear(buffer);
1703135446Strhodes
1704135446Strhodes	isc_buffer_availableregion(buffer, &rn);
1705135446Strhodes	isc_buffer_usedregion(msg->buffer, &r);
1706135446Strhodes	REQUIRE(rn.length > r.length);
1707135446Strhodes
1708135446Strhodes	/*
1709135446Strhodes	 * Copy the contents from the old to the new buffer.
1710135446Strhodes	 */
1711135446Strhodes	isc_buffer_add(buffer, r.length);
1712135446Strhodes	memcpy(rn.base, r.base, r.length);
1713135446Strhodes
1714135446Strhodes	msg->buffer = buffer;
1715135446Strhodes
1716135446Strhodes	return (ISC_R_SUCCESS);
1717135446Strhodes}
1718135446Strhodes
1719135446Strhodesvoid
1720135446Strhodesdns_message_renderrelease(dns_message_t *msg, unsigned int space) {
1721135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
1722135446Strhodes	REQUIRE(space <= msg->reserved);
1723135446Strhodes
1724135446Strhodes	msg->reserved -= space;
1725135446Strhodes}
1726135446Strhodes
1727135446Strhodesisc_result_t
1728135446Strhodesdns_message_renderreserve(dns_message_t *msg, unsigned int space) {
1729135446Strhodes	isc_region_t r;
1730135446Strhodes
1731135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
1732135446Strhodes
1733135446Strhodes	if (msg->buffer != NULL) {
1734135446Strhodes		isc_buffer_availableregion(msg->buffer, &r);
1735135446Strhodes		if (r.length < (space + msg->reserved))
1736135446Strhodes			return (ISC_R_NOSPACE);
1737135446Strhodes	}
1738135446Strhodes
1739135446Strhodes	msg->reserved += space;
1740135446Strhodes
1741135446Strhodes	return (ISC_R_SUCCESS);
1742135446Strhodes}
1743135446Strhodes
1744135446Strhodesstatic inline isc_boolean_t
1745135446Strhodeswrong_priority(dns_rdataset_t *rds, int pass, dns_rdatatype_t preferred_glue) {
1746135446Strhodes	int pass_needed;
1747135446Strhodes
1748135446Strhodes	/*
1749135446Strhodes	 * If we are not rendering class IN, this ordering is bogus.
1750135446Strhodes	 */
1751135446Strhodes	if (rds->rdclass != dns_rdataclass_in)
1752135446Strhodes		return (ISC_FALSE);
1753135446Strhodes
1754135446Strhodes	switch (rds->type) {
1755135446Strhodes	case dns_rdatatype_a:
1756135446Strhodes	case dns_rdatatype_aaaa:
1757135446Strhodes		if (preferred_glue == rds->type)
1758135446Strhodes			pass_needed = 4;
1759135446Strhodes		else
1760135446Strhodes			pass_needed = 3;
1761135446Strhodes		break;
1762135446Strhodes	case dns_rdatatype_rrsig:
1763135446Strhodes	case dns_rdatatype_dnskey:
1764135446Strhodes		pass_needed = 2;
1765135446Strhodes		break;
1766135446Strhodes	default:
1767135446Strhodes		pass_needed = 1;
1768135446Strhodes	}
1769135446Strhodes
1770135446Strhodes	if (pass_needed >= pass)
1771135446Strhodes		return (ISC_FALSE);
1772135446Strhodes
1773135446Strhodes	return (ISC_TRUE);
1774135446Strhodes}
1775135446Strhodes
1776135446Strhodesisc_result_t
1777135446Strhodesdns_message_rendersection(dns_message_t *msg, dns_section_t sectionid,
1778135446Strhodes			  unsigned int options)
1779135446Strhodes{
1780135446Strhodes	dns_namelist_t *section;
1781135446Strhodes	dns_name_t *name, *next_name;
1782135446Strhodes	dns_rdataset_t *rdataset, *next_rdataset;
1783135446Strhodes	unsigned int count, total;
1784135446Strhodes	isc_result_t result;
1785135446Strhodes	isc_buffer_t st; /* for rollbacks */
1786135446Strhodes	int pass;
1787135446Strhodes	isc_boolean_t partial = ISC_FALSE;
1788135446Strhodes	unsigned int rd_options;
1789135446Strhodes	dns_rdatatype_t preferred_glue = 0;
1790135446Strhodes
1791135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
1792135446Strhodes	REQUIRE(msg->buffer != NULL);
1793135446Strhodes	REQUIRE(VALID_NAMED_SECTION(sectionid));
1794135446Strhodes
1795135446Strhodes	section = &msg->sections[sectionid];
1796135446Strhodes
1797135446Strhodes	if ((sectionid == DNS_SECTION_ADDITIONAL)
1798135446Strhodes	    && (options & DNS_MESSAGERENDER_ORDERED) == 0) {
1799135446Strhodes		if ((options & DNS_MESSAGERENDER_PREFER_A) != 0) {
1800135446Strhodes			preferred_glue = dns_rdatatype_a;
1801135446Strhodes			pass = 4;
1802135446Strhodes		} else if ((options & DNS_MESSAGERENDER_PREFER_AAAA) != 0) {
1803135446Strhodes			preferred_glue = dns_rdatatype_aaaa;
1804135446Strhodes			pass = 4;
1805135446Strhodes		} else
1806135446Strhodes			pass = 3;
1807135446Strhodes	} else
1808135446Strhodes		pass = 1;
1809135446Strhodes
1810135446Strhodes	if ((options & DNS_MESSAGERENDER_OMITDNSSEC) == 0)
1811135446Strhodes		rd_options = 0;
1812135446Strhodes	else
1813135446Strhodes		rd_options = DNS_RDATASETTOWIRE_OMITDNSSEC;
1814135446Strhodes
1815135446Strhodes	/*
1816135446Strhodes	 * Shrink the space in the buffer by the reserved amount.
1817135446Strhodes	 */
1818135446Strhodes	msg->buffer->length -= msg->reserved;
1819135446Strhodes
1820135446Strhodes	total = 0;
1821135446Strhodes	if (msg->reserved == 0 && (options & DNS_MESSAGERENDER_PARTIAL) != 0)
1822135446Strhodes		partial = ISC_TRUE;
1823135446Strhodes
1824153816Sdougb	/*
1825153816Sdougb	 * Render required glue first.  Set TC if it won't fit.
1826153816Sdougb	 */
1827153816Sdougb	name = ISC_LIST_HEAD(*section);
1828153816Sdougb	if (name != NULL) {
1829153816Sdougb		rdataset = ISC_LIST_HEAD(name->list);
1830153816Sdougb		if (rdataset != NULL &&
1831153816Sdougb		    (rdataset->attributes & DNS_RDATASETATTR_REQUIREDGLUE) != 0 &&
1832153816Sdougb		    (rdataset->attributes & DNS_RDATASETATTR_RENDERED) == 0) {
1833165071Sdougb			const void *order_arg = msg->order_arg;
1834153816Sdougb			st = *(msg->buffer);
1835153816Sdougb			count = 0;
1836153816Sdougb			if (partial)
1837153816Sdougb				result = dns_rdataset_towirepartial(rdataset,
1838153816Sdougb								    name,
1839153816Sdougb								    msg->cctx,
1840153816Sdougb								    msg->buffer,
1841153816Sdougb								    msg->order,
1842153816Sdougb								    order_arg,
1843153816Sdougb								    rd_options,
1844153816Sdougb								    &count,
1845153816Sdougb								    NULL);
1846153816Sdougb			else
1847153816Sdougb				result = dns_rdataset_towiresorted(rdataset,
1848153816Sdougb								   name,
1849153816Sdougb								   msg->cctx,
1850153816Sdougb								   msg->buffer,
1851153816Sdougb								   msg->order,
1852153816Sdougb								   order_arg,
1853153816Sdougb								   rd_options,
1854153816Sdougb								   &count);
1855153816Sdougb			total += count;
1856153816Sdougb			if (partial && result == ISC_R_NOSPACE) {
1857153816Sdougb				msg->flags |= DNS_MESSAGEFLAG_TC;
1858153816Sdougb				msg->buffer->length += msg->reserved;
1859153816Sdougb				msg->counts[sectionid] += total;
1860153816Sdougb				return (result);
1861153816Sdougb			}
1862153816Sdougb			if (result != ISC_R_SUCCESS) {
1863153816Sdougb				INSIST(st.used < 65536);
1864153816Sdougb				dns_compress_rollback(msg->cctx,
1865153816Sdougb						      (isc_uint16_t)st.used);
1866153816Sdougb				*(msg->buffer) = st;  /* rollback */
1867153816Sdougb				msg->buffer->length += msg->reserved;
1868153816Sdougb				msg->counts[sectionid] += total;
1869153816Sdougb				return (result);
1870153816Sdougb			}
1871153816Sdougb			rdataset->attributes |= DNS_RDATASETATTR_RENDERED;
1872153816Sdougb		}
1873153816Sdougb	}
1874153816Sdougb
1875135446Strhodes	do {
1876135446Strhodes		name = ISC_LIST_HEAD(*section);
1877135446Strhodes		if (name == NULL) {
1878135446Strhodes			msg->buffer->length += msg->reserved;
1879135446Strhodes			msg->counts[sectionid] += total;
1880135446Strhodes			return (ISC_R_SUCCESS);
1881135446Strhodes		}
1882135446Strhodes
1883135446Strhodes		while (name != NULL) {
1884135446Strhodes			next_name = ISC_LIST_NEXT(name, link);
1885135446Strhodes
1886135446Strhodes			rdataset = ISC_LIST_HEAD(name->list);
1887135446Strhodes			while (rdataset != NULL) {
1888135446Strhodes				next_rdataset = ISC_LIST_NEXT(rdataset, link);
1889135446Strhodes
1890135446Strhodes				if ((rdataset->attributes &
1891135446Strhodes				     DNS_RDATASETATTR_RENDERED) != 0)
1892135446Strhodes					goto next;
1893135446Strhodes
1894135446Strhodes				if (((options & DNS_MESSAGERENDER_ORDERED)
1895135446Strhodes				     == 0)
1896135446Strhodes				    && (sectionid == DNS_SECTION_ADDITIONAL)
1897135446Strhodes				    && wrong_priority(rdataset, pass,
1898135446Strhodes						      preferred_glue))
1899135446Strhodes					goto next;
1900135446Strhodes
1901135446Strhodes				st = *(msg->buffer);
1902135446Strhodes
1903135446Strhodes				count = 0;
1904135446Strhodes				if (partial)
1905135446Strhodes					result = dns_rdataset_towirepartial(
1906135446Strhodes							  rdataset,
1907135446Strhodes							  name,
1908135446Strhodes							  msg->cctx,
1909135446Strhodes							  msg->buffer,
1910135446Strhodes							  msg->order,
1911135446Strhodes							  msg->order_arg,
1912135446Strhodes							  rd_options,
1913135446Strhodes							  &count,
1914135446Strhodes							  NULL);
1915135446Strhodes				else
1916135446Strhodes					result = dns_rdataset_towiresorted(
1917135446Strhodes							  rdataset,
1918135446Strhodes							  name,
1919135446Strhodes							  msg->cctx,
1920135446Strhodes							  msg->buffer,
1921135446Strhodes							  msg->order,
1922135446Strhodes							  msg->order_arg,
1923135446Strhodes							  rd_options,
1924135446Strhodes							  &count);
1925135446Strhodes
1926135446Strhodes				total += count;
1927135446Strhodes
1928135446Strhodes				/*
1929135446Strhodes				 * If out of space, record stats on what we
1930135446Strhodes				 * rendered so far, and return that status.
1931135446Strhodes				 *
1932135446Strhodes				 * XXXMLG Need to change this when
1933135446Strhodes				 * dns_rdataset_towire() can render partial
1934135446Strhodes				 * sets starting at some arbitary point in the
1935135446Strhodes				 * set.  This will include setting a bit in the
1936135446Strhodes				 * rdataset to indicate that a partial
1937135446Strhodes				 * rendering was done, and some state saved
1938135446Strhodes				 * somewhere (probably in the message struct)
1939135446Strhodes				 * to indicate where to continue from.
1940135446Strhodes				 */
1941135446Strhodes				if (partial && result == ISC_R_NOSPACE) {
1942135446Strhodes					msg->buffer->length += msg->reserved;
1943135446Strhodes					msg->counts[sectionid] += total;
1944135446Strhodes					return (result);
1945135446Strhodes				}
1946135446Strhodes				if (result != ISC_R_SUCCESS) {
1947135446Strhodes					INSIST(st.used < 65536);
1948135446Strhodes					dns_compress_rollback(msg->cctx,
1949135446Strhodes							(isc_uint16_t)st.used);
1950135446Strhodes					*(msg->buffer) = st;  /* rollback */
1951135446Strhodes					msg->buffer->length += msg->reserved;
1952135446Strhodes					msg->counts[sectionid] += total;
1953135446Strhodes					return (result);
1954135446Strhodes				}
1955135446Strhodes
1956135446Strhodes				/*
1957135446Strhodes				 * If we have rendered non-validated data,
1958135446Strhodes				 * ensure that the AD bit is not set.
1959135446Strhodes				 */
1960135446Strhodes				if (rdataset->trust != dns_trust_secure &&
1961135446Strhodes				    (sectionid == DNS_SECTION_ANSWER ||
1962135446Strhodes				     sectionid == DNS_SECTION_AUTHORITY))
1963135446Strhodes					msg->flags &= ~DNS_MESSAGEFLAG_AD;
1964135446Strhodes
1965135446Strhodes				rdataset->attributes |=
1966135446Strhodes					DNS_RDATASETATTR_RENDERED;
1967135446Strhodes
1968135446Strhodes			next:
1969135446Strhodes				rdataset = next_rdataset;
1970135446Strhodes			}
1971135446Strhodes
1972135446Strhodes			name = next_name;
1973135446Strhodes		}
1974135446Strhodes	} while (--pass != 0);
1975135446Strhodes
1976135446Strhodes	msg->buffer->length += msg->reserved;
1977135446Strhodes	msg->counts[sectionid] += total;
1978135446Strhodes
1979135446Strhodes	return (ISC_R_SUCCESS);
1980135446Strhodes}
1981135446Strhodes
1982135446Strhodesvoid
1983135446Strhodesdns_message_renderheader(dns_message_t *msg, isc_buffer_t *target) {
1984135446Strhodes	isc_uint16_t tmp;
1985135446Strhodes	isc_region_t r;
1986135446Strhodes
1987135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
1988135446Strhodes	REQUIRE(target != NULL);
1989135446Strhodes
1990135446Strhodes	isc_buffer_availableregion(target, &r);
1991135446Strhodes	REQUIRE(r.length >= DNS_MESSAGE_HEADERLEN);
1992135446Strhodes
1993135446Strhodes	isc_buffer_putuint16(target, msg->id);
1994135446Strhodes
1995135446Strhodes	tmp = ((msg->opcode << DNS_MESSAGE_OPCODE_SHIFT)
1996135446Strhodes	       & DNS_MESSAGE_OPCODE_MASK);
1997135446Strhodes	tmp |= (msg->rcode & DNS_MESSAGE_RCODE_MASK);
1998135446Strhodes	tmp |= (msg->flags & DNS_MESSAGE_FLAG_MASK);
1999135446Strhodes
2000135446Strhodes	INSIST(msg->counts[DNS_SECTION_QUESTION]  < 65536 &&
2001135446Strhodes	       msg->counts[DNS_SECTION_ANSWER]    < 65536 &&
2002135446Strhodes	       msg->counts[DNS_SECTION_AUTHORITY] < 65536 &&
2003135446Strhodes	       msg->counts[DNS_SECTION_ADDITIONAL] < 65536);
2004135446Strhodes
2005135446Strhodes	isc_buffer_putuint16(target, tmp);
2006135446Strhodes	isc_buffer_putuint16(target,
2007135446Strhodes			    (isc_uint16_t)msg->counts[DNS_SECTION_QUESTION]);
2008135446Strhodes	isc_buffer_putuint16(target,
2009135446Strhodes			    (isc_uint16_t)msg->counts[DNS_SECTION_ANSWER]);
2010135446Strhodes	isc_buffer_putuint16(target,
2011135446Strhodes			    (isc_uint16_t)msg->counts[DNS_SECTION_AUTHORITY]);
2012135446Strhodes	isc_buffer_putuint16(target,
2013135446Strhodes			    (isc_uint16_t)msg->counts[DNS_SECTION_ADDITIONAL]);
2014135446Strhodes}
2015135446Strhodes
2016135446Strhodesisc_result_t
2017135446Strhodesdns_message_renderend(dns_message_t *msg) {
2018135446Strhodes	isc_buffer_t tmpbuf;
2019135446Strhodes	isc_region_t r;
2020135446Strhodes	int result;
2021135446Strhodes	unsigned int count;
2022135446Strhodes
2023135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2024135446Strhodes	REQUIRE(msg->buffer != NULL);
2025135446Strhodes
2026135446Strhodes	if ((msg->rcode & ~DNS_MESSAGE_RCODE_MASK) != 0 && msg->opt == NULL) {
2027135446Strhodes		/*
2028135446Strhodes		 * We have an extended rcode but are not using EDNS.
2029135446Strhodes		 */
2030135446Strhodes		return (DNS_R_FORMERR);
2031135446Strhodes	}
2032135446Strhodes
2033135446Strhodes	/*
2034135446Strhodes	 * If we've got an OPT record, render it.
2035135446Strhodes	 */
2036135446Strhodes	if (msg->opt != NULL) {
2037135446Strhodes		dns_message_renderrelease(msg, msg->opt_reserved);
2038135446Strhodes		msg->opt_reserved = 0;
2039135446Strhodes		/*
2040135446Strhodes		 * Set the extended rcode.
2041135446Strhodes		 */
2042135446Strhodes		msg->opt->ttl &= ~DNS_MESSAGE_EDNSRCODE_MASK;
2043135446Strhodes		msg->opt->ttl |= ((msg->rcode << 20) &
2044135446Strhodes				  DNS_MESSAGE_EDNSRCODE_MASK);
2045135446Strhodes		/*
2046135446Strhodes		 * Render.
2047135446Strhodes		 */
2048135446Strhodes		count = 0;
2049135446Strhodes		result = dns_rdataset_towire(msg->opt, dns_rootname,
2050135446Strhodes					     msg->cctx, msg->buffer, 0,
2051135446Strhodes					     &count);
2052135446Strhodes		msg->counts[DNS_SECTION_ADDITIONAL] += count;
2053135446Strhodes		if (result != ISC_R_SUCCESS)
2054135446Strhodes			return (result);
2055135446Strhodes	}
2056135446Strhodes
2057135446Strhodes	/*
2058135446Strhodes	 * If we're adding a TSIG or SIG(0) to a truncated message,
2059135446Strhodes	 * clear all rdatasets from the message except for the question
2060135446Strhodes	 * before adding the TSIG or SIG(0).  If the question doesn't fit,
2061135446Strhodes	 * don't include it.
2062135446Strhodes	 */
2063135446Strhodes	if ((msg->tsigkey != NULL || msg->sig0key != NULL) &&
2064135446Strhodes	    (msg->flags & DNS_MESSAGEFLAG_TC) != 0)
2065135446Strhodes	{
2066135446Strhodes		isc_buffer_t *buf;
2067135446Strhodes
2068135446Strhodes		msgresetnames(msg, DNS_SECTION_ANSWER);
2069135446Strhodes		buf = msg->buffer;
2070135446Strhodes		dns_message_renderreset(msg);
2071135446Strhodes		msg->buffer = buf;
2072135446Strhodes		isc_buffer_clear(msg->buffer);
2073135446Strhodes		isc_buffer_add(msg->buffer, DNS_MESSAGE_HEADERLEN);
2074135446Strhodes		dns_compress_rollback(msg->cctx, 0);
2075135446Strhodes		result = dns_message_rendersection(msg, DNS_SECTION_QUESTION,
2076135446Strhodes						   0);
2077135446Strhodes		if (result != ISC_R_SUCCESS && result != ISC_R_NOSPACE)
2078135446Strhodes			return (result);
2079135446Strhodes	}
2080135446Strhodes
2081135446Strhodes	/*
2082135446Strhodes	 * If we're adding a TSIG record, generate and render it.
2083135446Strhodes	 */
2084135446Strhodes	if (msg->tsigkey != NULL) {
2085135446Strhodes		dns_message_renderrelease(msg, msg->sig_reserved);
2086135446Strhodes		msg->sig_reserved = 0;
2087135446Strhodes		result = dns_tsig_sign(msg);
2088135446Strhodes		if (result != ISC_R_SUCCESS)
2089135446Strhodes			return (result);
2090135446Strhodes		count = 0;
2091135446Strhodes		result = dns_rdataset_towire(msg->tsig, msg->tsigname,
2092135446Strhodes					     msg->cctx, msg->buffer, 0,
2093135446Strhodes					     &count);
2094135446Strhodes		msg->counts[DNS_SECTION_ADDITIONAL] += count;
2095135446Strhodes		if (result != ISC_R_SUCCESS)
2096135446Strhodes			return (result);
2097135446Strhodes	}
2098135446Strhodes
2099135446Strhodes	/*
2100135446Strhodes	 * If we're adding a SIG(0) record, generate and render it.
2101135446Strhodes	 */
2102135446Strhodes	if (msg->sig0key != NULL) {
2103135446Strhodes		dns_message_renderrelease(msg, msg->sig_reserved);
2104135446Strhodes		msg->sig_reserved = 0;
2105135446Strhodes		result = dns_dnssec_signmessage(msg, msg->sig0key);
2106135446Strhodes		if (result != ISC_R_SUCCESS)
2107135446Strhodes			return (result);
2108135446Strhodes		count = 0;
2109135446Strhodes		/*
2110135446Strhodes		 * Note: dns_rootname is used here, not msg->sig0name, since
2111135446Strhodes		 * the owner name of a SIG(0) is irrelevant, and will not
2112135446Strhodes		 * be set in a message being rendered.
2113135446Strhodes		 */
2114135446Strhodes		result = dns_rdataset_towire(msg->sig0, dns_rootname,
2115135446Strhodes					     msg->cctx, msg->buffer, 0,
2116135446Strhodes					     &count);
2117135446Strhodes		msg->counts[DNS_SECTION_ADDITIONAL] += count;
2118135446Strhodes		if (result != ISC_R_SUCCESS)
2119135446Strhodes			return (result);
2120135446Strhodes	}
2121135446Strhodes
2122135446Strhodes	isc_buffer_usedregion(msg->buffer, &r);
2123135446Strhodes	isc_buffer_init(&tmpbuf, r.base, r.length);
2124135446Strhodes
2125135446Strhodes	dns_message_renderheader(msg, &tmpbuf);
2126135446Strhodes
2127135446Strhodes	msg->buffer = NULL;  /* forget about this buffer only on success XXX */
2128135446Strhodes
2129135446Strhodes	return (ISC_R_SUCCESS);
2130135446Strhodes}
2131135446Strhodes
2132135446Strhodesvoid
2133135446Strhodesdns_message_renderreset(dns_message_t *msg) {
2134135446Strhodes	unsigned int i;
2135135446Strhodes	dns_name_t *name;
2136135446Strhodes	dns_rdataset_t *rds;
2137135446Strhodes
2138135446Strhodes	/*
2139135446Strhodes	 * Reset the message so that it may be rendered again.
2140135446Strhodes	 */
2141135446Strhodes
2142135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2143135446Strhodes	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
2144135446Strhodes
2145135446Strhodes	msg->buffer = NULL;
2146135446Strhodes
2147135446Strhodes	for (i = 0; i < DNS_SECTION_MAX; i++) {
2148135446Strhodes		msg->cursors[i] = NULL;
2149135446Strhodes		msg->counts[i] = 0;
2150135446Strhodes		for (name = ISC_LIST_HEAD(msg->sections[i]);
2151135446Strhodes		     name != NULL;
2152135446Strhodes		     name = ISC_LIST_NEXT(name, link)) {
2153135446Strhodes			for (rds = ISC_LIST_HEAD(name->list);
2154135446Strhodes			     rds != NULL;
2155135446Strhodes			     rds = ISC_LIST_NEXT(rds, link)) {
2156135446Strhodes				rds->attributes &= ~DNS_RDATASETATTR_RENDERED;
2157135446Strhodes			}
2158135446Strhodes		}
2159135446Strhodes	}
2160135446Strhodes	if (msg->tsigname != NULL)
2161135446Strhodes		dns_message_puttempname(msg, &msg->tsigname);
2162135446Strhodes	if (msg->tsig != NULL) {
2163135446Strhodes		dns_rdataset_disassociate(msg->tsig);
2164135446Strhodes		dns_message_puttemprdataset(msg, &msg->tsig);
2165135446Strhodes	}
2166135446Strhodes	if (msg->sig0 != NULL) {
2167135446Strhodes		dns_rdataset_disassociate(msg->sig0);
2168135446Strhodes		dns_message_puttemprdataset(msg, &msg->sig0);
2169135446Strhodes	}
2170135446Strhodes}
2171135446Strhodes
2172135446Strhodesisc_result_t
2173135446Strhodesdns_message_firstname(dns_message_t *msg, dns_section_t section) {
2174135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2175135446Strhodes	REQUIRE(VALID_NAMED_SECTION(section));
2176135446Strhodes
2177135446Strhodes	msg->cursors[section] = ISC_LIST_HEAD(msg->sections[section]);
2178135446Strhodes
2179135446Strhodes	if (msg->cursors[section] == NULL)
2180135446Strhodes		return (ISC_R_NOMORE);
2181135446Strhodes
2182135446Strhodes	return (ISC_R_SUCCESS);
2183135446Strhodes}
2184135446Strhodes
2185135446Strhodesisc_result_t
2186135446Strhodesdns_message_nextname(dns_message_t *msg, dns_section_t section) {
2187135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2188135446Strhodes	REQUIRE(VALID_NAMED_SECTION(section));
2189135446Strhodes	REQUIRE(msg->cursors[section] != NULL);
2190135446Strhodes
2191135446Strhodes	msg->cursors[section] = ISC_LIST_NEXT(msg->cursors[section], link);
2192135446Strhodes
2193135446Strhodes	if (msg->cursors[section] == NULL)
2194135446Strhodes		return (ISC_R_NOMORE);
2195135446Strhodes
2196135446Strhodes	return (ISC_R_SUCCESS);
2197135446Strhodes}
2198135446Strhodes
2199135446Strhodesvoid
2200135446Strhodesdns_message_currentname(dns_message_t *msg, dns_section_t section,
2201135446Strhodes			dns_name_t **name)
2202135446Strhodes{
2203135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2204135446Strhodes	REQUIRE(VALID_NAMED_SECTION(section));
2205135446Strhodes	REQUIRE(name != NULL && *name == NULL);
2206135446Strhodes	REQUIRE(msg->cursors[section] != NULL);
2207135446Strhodes
2208135446Strhodes	*name = msg->cursors[section];
2209135446Strhodes}
2210135446Strhodes
2211135446Strhodesisc_result_t
2212135446Strhodesdns_message_findname(dns_message_t *msg, dns_section_t section,
2213135446Strhodes		     dns_name_t *target, dns_rdatatype_t type,
2214135446Strhodes		     dns_rdatatype_t covers, dns_name_t **name,
2215135446Strhodes		     dns_rdataset_t **rdataset)
2216135446Strhodes{
2217135446Strhodes	dns_name_t *foundname;
2218135446Strhodes	isc_result_t result;
2219135446Strhodes
2220135446Strhodes	/*
2221135446Strhodes	 * XXX These requirements are probably too intensive, especially
2222135446Strhodes	 * where things can be NULL, but as they are they ensure that if
2223135446Strhodes	 * something is NON-NULL, indicating that the caller expects it
2224135446Strhodes	 * to be filled in, that we can in fact fill it in.
2225135446Strhodes	 */
2226135446Strhodes	REQUIRE(msg != NULL);
2227135446Strhodes	REQUIRE(VALID_SECTION(section));
2228135446Strhodes	REQUIRE(target != NULL);
2229135446Strhodes	if (name != NULL)
2230135446Strhodes		REQUIRE(*name == NULL);
2231135446Strhodes	if (type == dns_rdatatype_any) {
2232135446Strhodes		REQUIRE(rdataset == NULL);
2233135446Strhodes	} else {
2234135446Strhodes		if (rdataset != NULL)
2235135446Strhodes			REQUIRE(*rdataset == NULL);
2236135446Strhodes	}
2237135446Strhodes
2238135446Strhodes	result = findname(&foundname, target,
2239135446Strhodes			  &msg->sections[section]);
2240135446Strhodes
2241135446Strhodes	if (result == ISC_R_NOTFOUND)
2242135446Strhodes		return (DNS_R_NXDOMAIN);
2243135446Strhodes	else if (result != ISC_R_SUCCESS)
2244135446Strhodes		return (result);
2245135446Strhodes
2246135446Strhodes	if (name != NULL)
2247135446Strhodes		*name = foundname;
2248135446Strhodes
2249135446Strhodes	/*
2250135446Strhodes	 * And now look for the type.
2251135446Strhodes	 */
2252135446Strhodes	if (type == dns_rdatatype_any)
2253135446Strhodes		return (ISC_R_SUCCESS);
2254135446Strhodes
2255135446Strhodes	result = dns_message_findtype(foundname, type, covers, rdataset);
2256135446Strhodes	if (result == ISC_R_NOTFOUND)
2257135446Strhodes		return (DNS_R_NXRRSET);
2258135446Strhodes
2259135446Strhodes	return (result);
2260135446Strhodes}
2261135446Strhodes
2262135446Strhodesvoid
2263135446Strhodesdns_message_movename(dns_message_t *msg, dns_name_t *name,
2264135446Strhodes		     dns_section_t fromsection,
2265135446Strhodes		     dns_section_t tosection)
2266135446Strhodes{
2267135446Strhodes	REQUIRE(msg != NULL);
2268135446Strhodes	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
2269135446Strhodes	REQUIRE(name != NULL);
2270135446Strhodes	REQUIRE(VALID_NAMED_SECTION(fromsection));
2271135446Strhodes	REQUIRE(VALID_NAMED_SECTION(tosection));
2272135446Strhodes
2273135446Strhodes	/*
2274135446Strhodes	 * Unlink the name from the old section
2275135446Strhodes	 */
2276135446Strhodes	ISC_LIST_UNLINK(msg->sections[fromsection], name, link);
2277135446Strhodes	ISC_LIST_APPEND(msg->sections[tosection], name, link);
2278135446Strhodes}
2279135446Strhodes
2280135446Strhodesvoid
2281135446Strhodesdns_message_addname(dns_message_t *msg, dns_name_t *name,
2282135446Strhodes		    dns_section_t section)
2283135446Strhodes{
2284135446Strhodes	REQUIRE(msg != NULL);
2285135446Strhodes	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
2286135446Strhodes	REQUIRE(name != NULL);
2287135446Strhodes	REQUIRE(VALID_NAMED_SECTION(section));
2288135446Strhodes
2289135446Strhodes	ISC_LIST_APPEND(msg->sections[section], name, link);
2290135446Strhodes}
2291135446Strhodes
2292170222Sdougbvoid
2293170222Sdougbdns_message_removename(dns_message_t *msg, dns_name_t *name,
2294170222Sdougb		       dns_section_t section)
2295170222Sdougb{
2296170222Sdougb	REQUIRE(msg != NULL);
2297170222Sdougb	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
2298170222Sdougb	REQUIRE(name != NULL);
2299170222Sdougb	REQUIRE(VALID_NAMED_SECTION(section));
2300170222Sdougb
2301170222Sdougb	ISC_LIST_UNLINK(msg->sections[section], name, link);
2302170222Sdougb}
2303170222Sdougb
2304135446Strhodesisc_result_t
2305135446Strhodesdns_message_gettempname(dns_message_t *msg, dns_name_t **item) {
2306135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2307135446Strhodes	REQUIRE(item != NULL && *item == NULL);
2308135446Strhodes
2309135446Strhodes	*item = isc_mempool_get(msg->namepool);
2310135446Strhodes	if (*item == NULL)
2311135446Strhodes		return (ISC_R_NOMEMORY);
2312135446Strhodes	dns_name_init(*item, NULL);
2313135446Strhodes
2314135446Strhodes	return (ISC_R_SUCCESS);
2315135446Strhodes}
2316135446Strhodes
2317135446Strhodesisc_result_t
2318135446Strhodesdns_message_gettempoffsets(dns_message_t *msg, dns_offsets_t **item) {
2319135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2320135446Strhodes	REQUIRE(item != NULL && *item == NULL);
2321135446Strhodes
2322135446Strhodes	*item = newoffsets(msg);
2323135446Strhodes	if (*item == NULL)
2324135446Strhodes		return (ISC_R_NOMEMORY);
2325135446Strhodes
2326135446Strhodes	return (ISC_R_SUCCESS);
2327135446Strhodes}
2328135446Strhodes
2329135446Strhodesisc_result_t
2330135446Strhodesdns_message_gettemprdata(dns_message_t *msg, dns_rdata_t **item) {
2331135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2332135446Strhodes	REQUIRE(item != NULL && *item == NULL);
2333135446Strhodes
2334135446Strhodes	*item = newrdata(msg);
2335135446Strhodes	if (*item == NULL)
2336135446Strhodes		return (ISC_R_NOMEMORY);
2337135446Strhodes
2338135446Strhodes	return (ISC_R_SUCCESS);
2339135446Strhodes}
2340135446Strhodes
2341135446Strhodesisc_result_t
2342135446Strhodesdns_message_gettemprdataset(dns_message_t *msg, dns_rdataset_t **item) {
2343135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2344135446Strhodes	REQUIRE(item != NULL && *item == NULL);
2345135446Strhodes
2346135446Strhodes	*item = isc_mempool_get(msg->rdspool);
2347135446Strhodes	if (*item == NULL)
2348135446Strhodes		return (ISC_R_NOMEMORY);
2349135446Strhodes
2350135446Strhodes	dns_rdataset_init(*item);
2351135446Strhodes
2352135446Strhodes	return (ISC_R_SUCCESS);
2353135446Strhodes}
2354135446Strhodes
2355135446Strhodesisc_result_t
2356135446Strhodesdns_message_gettemprdatalist(dns_message_t *msg, dns_rdatalist_t **item) {
2357135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2358135446Strhodes	REQUIRE(item != NULL && *item == NULL);
2359135446Strhodes
2360135446Strhodes	*item = newrdatalist(msg);
2361135446Strhodes	if (*item == NULL)
2362135446Strhodes		return (ISC_R_NOMEMORY);
2363135446Strhodes
2364135446Strhodes	return (ISC_R_SUCCESS);
2365135446Strhodes}
2366135446Strhodes
2367135446Strhodesvoid
2368135446Strhodesdns_message_puttempname(dns_message_t *msg, dns_name_t **item) {
2369135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2370135446Strhodes	REQUIRE(item != NULL && *item != NULL);
2371135446Strhodes
2372135446Strhodes	if (dns_name_dynamic(*item))
2373135446Strhodes		dns_name_free(*item, msg->mctx);
2374135446Strhodes	isc_mempool_put(msg->namepool, *item);
2375135446Strhodes	*item = NULL;
2376135446Strhodes}
2377135446Strhodes
2378135446Strhodesvoid
2379135446Strhodesdns_message_puttemprdata(dns_message_t *msg, dns_rdata_t **item) {
2380135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2381135446Strhodes	REQUIRE(item != NULL && *item != NULL);
2382135446Strhodes
2383135446Strhodes	releaserdata(msg, *item);
2384135446Strhodes	*item = NULL;
2385135446Strhodes}
2386135446Strhodes
2387135446Strhodesvoid
2388135446Strhodesdns_message_puttemprdataset(dns_message_t *msg, dns_rdataset_t **item) {
2389135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2390135446Strhodes	REQUIRE(item != NULL && *item != NULL);
2391135446Strhodes
2392135446Strhodes	REQUIRE(!dns_rdataset_isassociated(*item));
2393135446Strhodes	isc_mempool_put(msg->rdspool, *item);
2394135446Strhodes	*item = NULL;
2395135446Strhodes}
2396135446Strhodes
2397135446Strhodesvoid
2398135446Strhodesdns_message_puttemprdatalist(dns_message_t *msg, dns_rdatalist_t **item) {
2399135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2400135446Strhodes	REQUIRE(item != NULL && *item != NULL);
2401135446Strhodes
2402135446Strhodes	releaserdatalist(msg, *item);
2403135446Strhodes	*item = NULL;
2404135446Strhodes}
2405135446Strhodes
2406135446Strhodesisc_result_t
2407135446Strhodesdns_message_peekheader(isc_buffer_t *source, dns_messageid_t *idp,
2408135446Strhodes		       unsigned int *flagsp)
2409135446Strhodes{
2410135446Strhodes	isc_region_t r;
2411135446Strhodes	isc_buffer_t buffer;
2412135446Strhodes	dns_messageid_t id;
2413135446Strhodes	unsigned int flags;
2414135446Strhodes
2415135446Strhodes	REQUIRE(source != NULL);
2416135446Strhodes
2417135446Strhodes	buffer = *source;
2418135446Strhodes
2419135446Strhodes	isc_buffer_remainingregion(&buffer, &r);
2420135446Strhodes	if (r.length < DNS_MESSAGE_HEADERLEN)
2421135446Strhodes		return (ISC_R_UNEXPECTEDEND);
2422135446Strhodes
2423135446Strhodes	id = isc_buffer_getuint16(&buffer);
2424135446Strhodes	flags = isc_buffer_getuint16(&buffer);
2425135446Strhodes	flags &= DNS_MESSAGE_FLAG_MASK;
2426135446Strhodes
2427135446Strhodes	if (flagsp != NULL)
2428135446Strhodes		*flagsp = flags;
2429135446Strhodes	if (idp != NULL)
2430135446Strhodes		*idp = id;
2431135446Strhodes
2432135446Strhodes	return (ISC_R_SUCCESS);
2433135446Strhodes}
2434135446Strhodes
2435135446Strhodesisc_result_t
2436135446Strhodesdns_message_reply(dns_message_t *msg, isc_boolean_t want_question_section) {
2437135446Strhodes	unsigned int first_section;
2438135446Strhodes	isc_result_t result;
2439135446Strhodes
2440135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2441135446Strhodes	REQUIRE((msg->flags & DNS_MESSAGEFLAG_QR) == 0);
2442135446Strhodes
2443135446Strhodes	if (!msg->header_ok)
2444135446Strhodes		return (DNS_R_FORMERR);
2445135446Strhodes	if (msg->opcode != dns_opcode_query &&
2446135446Strhodes	    msg->opcode != dns_opcode_notify)
2447135446Strhodes		want_question_section = ISC_FALSE;
2448135446Strhodes	if (want_question_section) {
2449135446Strhodes		if (!msg->question_ok)
2450135446Strhodes			return (DNS_R_FORMERR);
2451135446Strhodes		first_section = DNS_SECTION_ANSWER;
2452135446Strhodes	} else
2453135446Strhodes		first_section = DNS_SECTION_QUESTION;
2454135446Strhodes	msg->from_to_wire = DNS_MESSAGE_INTENTRENDER;
2455135446Strhodes	msgresetnames(msg, first_section);
2456135446Strhodes	msgresetopt(msg);
2457135446Strhodes	msgresetsigs(msg, ISC_TRUE);
2458135446Strhodes	msginitprivate(msg);
2459135446Strhodes	/*
2460135446Strhodes	 * We now clear most flags and then set QR, ensuring that the
2461135446Strhodes	 * reply's flags will be in a reasonable state.
2462135446Strhodes	 */
2463135446Strhodes	msg->flags &= DNS_MESSAGE_REPLYPRESERVE;
2464135446Strhodes	msg->flags |= DNS_MESSAGEFLAG_QR;
2465135446Strhodes
2466135446Strhodes	/*
2467135446Strhodes	 * This saves the query TSIG status, if the query was signed, and
2468135446Strhodes	 * reserves space in the reply for the TSIG.
2469135446Strhodes	 */
2470135446Strhodes	if (msg->tsigkey != NULL) {
2471135446Strhodes		unsigned int otherlen = 0;
2472135446Strhodes		msg->querytsigstatus = msg->tsigstatus;
2473135446Strhodes		msg->tsigstatus = dns_rcode_noerror;
2474135446Strhodes		if (msg->querytsigstatus == dns_tsigerror_badtime)
2475135446Strhodes			otherlen = 6;
2476135446Strhodes		msg->sig_reserved = spacefortsig(msg->tsigkey, otherlen);
2477135446Strhodes		result = dns_message_renderreserve(msg, msg->sig_reserved);
2478135446Strhodes		if (result != ISC_R_SUCCESS) {
2479135446Strhodes			msg->sig_reserved = 0;
2480135446Strhodes			return (result);
2481135446Strhodes		}
2482135446Strhodes	}
2483135446Strhodes	if (msg->saved.base != NULL) {
2484135446Strhodes		msg->query.base = msg->saved.base;
2485135446Strhodes		msg->query.length = msg->saved.length;
2486135446Strhodes		msg->free_query = msg->free_saved;
2487135446Strhodes		msg->saved.base = NULL;
2488135446Strhodes		msg->saved.length = 0;
2489135446Strhodes		msg->free_saved = 0;
2490135446Strhodes	}
2491135446Strhodes
2492135446Strhodes	return (ISC_R_SUCCESS);
2493135446Strhodes}
2494135446Strhodes
2495135446Strhodesdns_rdataset_t *
2496135446Strhodesdns_message_getopt(dns_message_t *msg) {
2497135446Strhodes
2498135446Strhodes	/*
2499135446Strhodes	 * Get the OPT record for 'msg'.
2500135446Strhodes	 */
2501135446Strhodes
2502135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2503135446Strhodes
2504135446Strhodes	return (msg->opt);
2505135446Strhodes}
2506135446Strhodes
2507135446Strhodesisc_result_t
2508135446Strhodesdns_message_setopt(dns_message_t *msg, dns_rdataset_t *opt) {
2509135446Strhodes	isc_result_t result;
2510135446Strhodes	dns_rdata_t rdata = DNS_RDATA_INIT;
2511135446Strhodes
2512135446Strhodes	/*
2513135446Strhodes	 * Set the OPT record for 'msg'.
2514135446Strhodes	 */
2515135446Strhodes
2516135446Strhodes	/*
2517135446Strhodes	 * The space required for an OPT record is:
2518135446Strhodes	 *
2519135446Strhodes	 *	1 byte for the name
2520135446Strhodes	 *	2 bytes for the type
2521135446Strhodes	 *	2 bytes for the class
2522135446Strhodes	 *	4 bytes for the ttl
2523135446Strhodes	 *	2 bytes for the rdata length
2524135446Strhodes	 * ---------------------------------
2525135446Strhodes	 *     11 bytes
2526135446Strhodes	 *
2527135446Strhodes	 * plus the length of the rdata.
2528135446Strhodes	 */
2529135446Strhodes
2530135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2531135446Strhodes	REQUIRE(opt->type == dns_rdatatype_opt);
2532135446Strhodes	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
2533135446Strhodes	REQUIRE(msg->state == DNS_SECTION_ANY);
2534135446Strhodes
2535135446Strhodes	msgresetopt(msg);
2536135446Strhodes
2537135446Strhodes	result = dns_rdataset_first(opt);
2538135446Strhodes	if (result != ISC_R_SUCCESS)
2539135446Strhodes		goto cleanup;
2540135446Strhodes	dns_rdataset_current(opt, &rdata);
2541135446Strhodes	msg->opt_reserved = 11 + rdata.length;
2542135446Strhodes	result = dns_message_renderreserve(msg, msg->opt_reserved);
2543135446Strhodes	if (result != ISC_R_SUCCESS) {
2544135446Strhodes		msg->opt_reserved = 0;
2545135446Strhodes		goto cleanup;
2546135446Strhodes	}
2547135446Strhodes
2548135446Strhodes	msg->opt = opt;
2549135446Strhodes
2550135446Strhodes	return (ISC_R_SUCCESS);
2551135446Strhodes
2552135446Strhodes cleanup:
2553135446Strhodes	dns_message_puttemprdataset(msg, &opt);
2554135446Strhodes	return (result);
2555135446Strhodes
2556135446Strhodes}
2557135446Strhodes
2558135446Strhodesdns_rdataset_t *
2559135446Strhodesdns_message_gettsig(dns_message_t *msg, dns_name_t **owner) {
2560135446Strhodes
2561135446Strhodes	/*
2562135446Strhodes	 * Get the TSIG record and owner for 'msg'.
2563135446Strhodes	 */
2564135446Strhodes
2565135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2566135446Strhodes	REQUIRE(owner == NULL || *owner == NULL);
2567135446Strhodes
2568135446Strhodes	if (owner != NULL)
2569135446Strhodes		*owner = msg->tsigname;
2570135446Strhodes	return (msg->tsig);
2571135446Strhodes}
2572135446Strhodes
2573135446Strhodesisc_result_t
2574135446Strhodesdns_message_settsigkey(dns_message_t *msg, dns_tsigkey_t *key) {
2575135446Strhodes	isc_result_t result;
2576135446Strhodes
2577135446Strhodes	/*
2578135446Strhodes	 * Set the TSIG key for 'msg'
2579135446Strhodes	 */
2580135446Strhodes
2581135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2582135446Strhodes	REQUIRE(msg->state == DNS_SECTION_ANY);
2583135446Strhodes
2584135446Strhodes	if (key == NULL && msg->tsigkey != NULL) {
2585135446Strhodes		if (msg->sig_reserved != 0) {
2586135446Strhodes			dns_message_renderrelease(msg, msg->sig_reserved);
2587135446Strhodes			msg->sig_reserved = 0;
2588135446Strhodes		}
2589135446Strhodes		dns_tsigkey_detach(&msg->tsigkey);
2590135446Strhodes	}
2591135446Strhodes	if (key != NULL) {
2592135446Strhodes		REQUIRE(msg->tsigkey == NULL && msg->sig0key == NULL);
2593135446Strhodes		dns_tsigkey_attach(key, &msg->tsigkey);
2594135446Strhodes		if (msg->from_to_wire == DNS_MESSAGE_INTENTRENDER) {
2595135446Strhodes			msg->sig_reserved = spacefortsig(msg->tsigkey, 0);
2596135446Strhodes			result = dns_message_renderreserve(msg,
2597135446Strhodes							   msg->sig_reserved);
2598135446Strhodes			if (result != ISC_R_SUCCESS) {
2599135446Strhodes				dns_tsigkey_detach(&msg->tsigkey);
2600135446Strhodes				msg->sig_reserved = 0;
2601135446Strhodes				return (result);
2602135446Strhodes			}
2603135446Strhodes		}
2604135446Strhodes	}
2605135446Strhodes	return (ISC_R_SUCCESS);
2606135446Strhodes}
2607135446Strhodes
2608135446Strhodesdns_tsigkey_t *
2609135446Strhodesdns_message_gettsigkey(dns_message_t *msg) {
2610135446Strhodes
2611135446Strhodes	/*
2612135446Strhodes	 * Get the TSIG key for 'msg'
2613135446Strhodes	 */
2614135446Strhodes
2615135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2616135446Strhodes
2617135446Strhodes	return (msg->tsigkey);
2618135446Strhodes}
2619135446Strhodes
2620135446Strhodesisc_result_t
2621135446Strhodesdns_message_setquerytsig(dns_message_t *msg, isc_buffer_t *querytsig) {
2622135446Strhodes	dns_rdata_t *rdata = NULL;
2623135446Strhodes	dns_rdatalist_t *list = NULL;
2624135446Strhodes	dns_rdataset_t *set = NULL;
2625135446Strhodes	isc_buffer_t *buf = NULL;
2626135446Strhodes	isc_region_t r;
2627135446Strhodes	isc_result_t result;
2628135446Strhodes
2629135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2630135446Strhodes	REQUIRE(msg->querytsig == NULL);
2631135446Strhodes
2632135446Strhodes	if (querytsig == NULL)
2633135446Strhodes		return (ISC_R_SUCCESS);
2634135446Strhodes
2635135446Strhodes	result = dns_message_gettemprdata(msg, &rdata);
2636135446Strhodes	if (result != ISC_R_SUCCESS)
2637135446Strhodes		goto cleanup;
2638135446Strhodes
2639135446Strhodes	result = dns_message_gettemprdatalist(msg, &list);
2640135446Strhodes	if (result != ISC_R_SUCCESS)
2641135446Strhodes		goto cleanup;
2642135446Strhodes	result = dns_message_gettemprdataset(msg, &set);
2643135446Strhodes	if (result != ISC_R_SUCCESS)
2644135446Strhodes		goto cleanup;
2645135446Strhodes
2646135446Strhodes	isc_buffer_usedregion(querytsig, &r);
2647135446Strhodes	result = isc_buffer_allocate(msg->mctx, &buf, r.length);
2648135446Strhodes	if (result != ISC_R_SUCCESS)
2649135446Strhodes		goto cleanup;
2650135446Strhodes	isc_buffer_putmem(buf, r.base, r.length);
2651135446Strhodes	isc_buffer_usedregion(buf, &r);
2652135446Strhodes	dns_rdata_init(rdata);
2653135446Strhodes	dns_rdata_fromregion(rdata, dns_rdataclass_any, dns_rdatatype_tsig, &r);
2654135446Strhodes	dns_message_takebuffer(msg, &buf);
2655135446Strhodes	ISC_LIST_INIT(list->rdata);
2656135446Strhodes	ISC_LIST_APPEND(list->rdata, rdata, link);
2657135446Strhodes	result = dns_rdatalist_tordataset(list, set);
2658135446Strhodes	if (result != ISC_R_SUCCESS)
2659135446Strhodes		goto cleanup;
2660135446Strhodes
2661135446Strhodes	msg->querytsig = set;
2662135446Strhodes
2663135446Strhodes	return (result);
2664135446Strhodes
2665135446Strhodes cleanup:
2666135446Strhodes	if (rdata != NULL)
2667135446Strhodes		dns_message_puttemprdata(msg, &rdata);
2668135446Strhodes	if (list != NULL)
2669135446Strhodes		dns_message_puttemprdatalist(msg, &list);
2670135446Strhodes	if (set != NULL)
2671135446Strhodes		dns_message_puttemprdataset(msg, &set);
2672135446Strhodes	return (ISC_R_NOMEMORY);
2673135446Strhodes}
2674135446Strhodes
2675135446Strhodesisc_result_t
2676135446Strhodesdns_message_getquerytsig(dns_message_t *msg, isc_mem_t *mctx,
2677135446Strhodes			 isc_buffer_t **querytsig) {
2678135446Strhodes	isc_result_t result;
2679135446Strhodes	dns_rdata_t rdata = DNS_RDATA_INIT;
2680135446Strhodes	isc_region_t r;
2681135446Strhodes
2682135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2683135446Strhodes	REQUIRE(mctx != NULL);
2684135446Strhodes	REQUIRE(querytsig != NULL && *querytsig == NULL);
2685135446Strhodes
2686135446Strhodes	if (msg->tsig == NULL)
2687135446Strhodes		return (ISC_R_SUCCESS);
2688135446Strhodes
2689135446Strhodes	result = dns_rdataset_first(msg->tsig);
2690135446Strhodes	if (result != ISC_R_SUCCESS)
2691135446Strhodes		return (result);
2692135446Strhodes	dns_rdataset_current(msg->tsig, &rdata);
2693135446Strhodes	dns_rdata_toregion(&rdata, &r);
2694135446Strhodes
2695135446Strhodes	result = isc_buffer_allocate(mctx, querytsig, r.length);
2696135446Strhodes	if (result != ISC_R_SUCCESS)
2697135446Strhodes		return (result);
2698135446Strhodes	isc_buffer_putmem(*querytsig, r.base, r.length);
2699135446Strhodes	return (ISC_R_SUCCESS);
2700135446Strhodes}
2701135446Strhodes
2702135446Strhodesdns_rdataset_t *
2703135446Strhodesdns_message_getsig0(dns_message_t *msg, dns_name_t **owner) {
2704135446Strhodes
2705135446Strhodes	/*
2706135446Strhodes	 * Get the SIG(0) record for 'msg'.
2707135446Strhodes	 */
2708135446Strhodes
2709135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2710135446Strhodes	REQUIRE(owner == NULL || *owner == NULL);
2711135446Strhodes
2712135446Strhodes	if (msg->sig0 != NULL && owner != NULL) {
2713135446Strhodes		/* If dns_message_getsig0 is called on a rendered message
2714135446Strhodes		 * after the SIG(0) has been applied, we need to return the
2715135446Strhodes		 * root name, not NULL.
2716135446Strhodes		 */
2717135446Strhodes		if (msg->sig0name == NULL)
2718135446Strhodes			*owner = dns_rootname;
2719135446Strhodes		else
2720135446Strhodes			*owner = msg->sig0name;
2721135446Strhodes	}
2722135446Strhodes	return (msg->sig0);
2723135446Strhodes}
2724135446Strhodes
2725135446Strhodesisc_result_t
2726135446Strhodesdns_message_setsig0key(dns_message_t *msg, dst_key_t *key) {
2727135446Strhodes	isc_region_t r;
2728135446Strhodes	unsigned int x;
2729135446Strhodes	isc_result_t result;
2730135446Strhodes
2731135446Strhodes	/*
2732135446Strhodes	 * Set the SIG(0) key for 'msg'
2733135446Strhodes	 */
2734135446Strhodes
2735135446Strhodes	/*
2736135446Strhodes	 * The space required for an SIG(0) record is:
2737135446Strhodes	 *
2738135446Strhodes	 *	1 byte for the name
2739135446Strhodes	 *	2 bytes for the type
2740135446Strhodes	 *	2 bytes for the class
2741135446Strhodes	 *	4 bytes for the ttl
2742135446Strhodes	 *	2 bytes for the type covered
2743135446Strhodes	 *	1 byte for the algorithm
2744135446Strhodes	 *	1 bytes for the labels
2745135446Strhodes	 *	4 bytes for the original ttl
2746135446Strhodes	 *	4 bytes for the signature expiration
2747135446Strhodes	 *	4 bytes for the signature inception
2748135446Strhodes	 *	2 bytes for the key tag
2749135446Strhodes	 *	n bytes for the signer's name
2750135446Strhodes	 *	x bytes for the signature
2751135446Strhodes	 * ---------------------------------
2752135446Strhodes	 *     27 + n + x bytes
2753135446Strhodes	 */
2754135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2755135446Strhodes	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
2756135446Strhodes	REQUIRE(msg->state == DNS_SECTION_ANY);
2757135446Strhodes
2758135446Strhodes	if (key != NULL) {
2759135446Strhodes		REQUIRE(msg->sig0key == NULL && msg->tsigkey == NULL);
2760135446Strhodes		dns_name_toregion(dst_key_name(key), &r);
2761135446Strhodes		result = dst_key_sigsize(key, &x);
2762135446Strhodes		if (result != ISC_R_SUCCESS) {
2763135446Strhodes			msg->sig_reserved = 0;
2764135446Strhodes			return (result);
2765135446Strhodes		}
2766135446Strhodes		msg->sig_reserved = 27 + r.length + x;
2767135446Strhodes		result = dns_message_renderreserve(msg, msg->sig_reserved);
2768135446Strhodes		if (result != ISC_R_SUCCESS) {
2769135446Strhodes			msg->sig_reserved = 0;
2770135446Strhodes			return (result);
2771135446Strhodes		}
2772135446Strhodes		msg->sig0key = key;
2773135446Strhodes	}
2774135446Strhodes	return (ISC_R_SUCCESS);
2775135446Strhodes}
2776135446Strhodes
2777135446Strhodesdst_key_t *
2778135446Strhodesdns_message_getsig0key(dns_message_t *msg) {
2779135446Strhodes
2780135446Strhodes	/*
2781135446Strhodes	 * Get the SIG(0) key for 'msg'
2782135446Strhodes	 */
2783135446Strhodes
2784135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2785135446Strhodes
2786135446Strhodes	return (msg->sig0key);
2787135446Strhodes}
2788135446Strhodes
2789135446Strhodesvoid
2790135446Strhodesdns_message_takebuffer(dns_message_t *msg, isc_buffer_t **buffer) {
2791135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2792135446Strhodes	REQUIRE(buffer != NULL);
2793135446Strhodes	REQUIRE(ISC_BUFFER_VALID(*buffer));
2794135446Strhodes
2795135446Strhodes	ISC_LIST_APPEND(msg->cleanup, *buffer, link);
2796135446Strhodes	*buffer = NULL;
2797135446Strhodes}
2798135446Strhodes
2799135446Strhodesisc_result_t
2800135446Strhodesdns_message_signer(dns_message_t *msg, dns_name_t *signer) {
2801135446Strhodes	isc_result_t result = ISC_R_SUCCESS;
2802135446Strhodes	dns_rdata_t rdata = DNS_RDATA_INIT;
2803135446Strhodes
2804135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2805135446Strhodes	REQUIRE(signer != NULL);
2806135446Strhodes	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTPARSE);
2807135446Strhodes
2808135446Strhodes	if (msg->tsig == NULL && msg->sig0 == NULL)
2809135446Strhodes		return (ISC_R_NOTFOUND);
2810135446Strhodes
2811135446Strhodes	if (msg->verify_attempted == 0)
2812135446Strhodes		return (DNS_R_NOTVERIFIEDYET);
2813135446Strhodes
2814135446Strhodes	if (!dns_name_hasbuffer(signer)) {
2815135446Strhodes		isc_buffer_t *dynbuf = NULL;
2816135446Strhodes		result = isc_buffer_allocate(msg->mctx, &dynbuf, 512);
2817135446Strhodes		if (result != ISC_R_SUCCESS)
2818135446Strhodes			return (result);
2819135446Strhodes		dns_name_setbuffer(signer, dynbuf);
2820135446Strhodes		dns_message_takebuffer(msg, &dynbuf);
2821135446Strhodes	}
2822135446Strhodes
2823135446Strhodes	if (msg->sig0 != NULL) {
2824135446Strhodes		dns_rdata_sig_t sig;
2825135446Strhodes
2826135446Strhodes		result = dns_rdataset_first(msg->sig0);
2827135446Strhodes		INSIST(result == ISC_R_SUCCESS);
2828135446Strhodes		dns_rdataset_current(msg->sig0, &rdata);
2829135446Strhodes
2830135446Strhodes		result = dns_rdata_tostruct(&rdata, &sig, NULL);
2831135446Strhodes		if (result != ISC_R_SUCCESS)
2832135446Strhodes			return (result);
2833135446Strhodes
2834135446Strhodes		if (msg->verified_sig && msg->sig0status == dns_rcode_noerror)
2835135446Strhodes			result = ISC_R_SUCCESS;
2836135446Strhodes		else
2837135446Strhodes			result = DNS_R_SIGINVALID;
2838135446Strhodes		dns_name_clone(&sig.signer, signer);
2839135446Strhodes		dns_rdata_freestruct(&sig);
2840135446Strhodes	} else {
2841135446Strhodes		dns_name_t *identity;
2842135446Strhodes		dns_rdata_any_tsig_t tsig;
2843135446Strhodes
2844135446Strhodes		result = dns_rdataset_first(msg->tsig);
2845135446Strhodes		INSIST(result == ISC_R_SUCCESS);
2846135446Strhodes		dns_rdataset_current(msg->tsig, &rdata);
2847135446Strhodes
2848135446Strhodes		result = dns_rdata_tostruct(&rdata, &tsig, NULL);
2849135446Strhodes		if (msg->tsigstatus != dns_rcode_noerror)
2850135446Strhodes			result = DNS_R_TSIGVERIFYFAILURE;
2851135446Strhodes		else if (tsig.error != dns_rcode_noerror)
2852135446Strhodes			result = DNS_R_TSIGERRORSET;
2853135446Strhodes		else
2854135446Strhodes			result = ISC_R_SUCCESS;
2855135446Strhodes		dns_rdata_freestruct(&tsig);
2856135446Strhodes
2857135446Strhodes		if (msg->tsigkey == NULL) {
2858135446Strhodes			/*
2859135446Strhodes			 * If msg->tsigstatus & tsig.error are both
2860135446Strhodes			 * dns_rcode_noerror, the message must have been
2861135446Strhodes			 * verified, which means msg->tsigkey will be
2862135446Strhodes			 * non-NULL.
2863135446Strhodes			 */
2864135446Strhodes			INSIST(result != ISC_R_SUCCESS);
2865135446Strhodes		} else {
2866135446Strhodes			identity = dns_tsigkey_identity(msg->tsigkey);
2867135446Strhodes			if (identity == NULL) {
2868135446Strhodes				if (result == ISC_R_SUCCESS)
2869135446Strhodes					result = DNS_R_NOIDENTITY;
2870135446Strhodes				identity = &msg->tsigkey->name;
2871135446Strhodes			}
2872135446Strhodes			dns_name_clone(identity, signer);
2873135446Strhodes		}
2874135446Strhodes	}
2875135446Strhodes
2876135446Strhodes	return (result);
2877135446Strhodes}
2878135446Strhodes
2879135446Strhodesvoid
2880135446Strhodesdns_message_resetsig(dns_message_t *msg) {
2881135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2882135446Strhodes	msg->verified_sig = 0;
2883135446Strhodes	msg->verify_attempted = 0;
2884135446Strhodes	msg->tsigstatus = dns_rcode_noerror;
2885135446Strhodes	msg->sig0status = dns_rcode_noerror;
2886135446Strhodes	msg->timeadjust = 0;
2887135446Strhodes	if (msg->tsigkey != NULL) {
2888135446Strhodes		dns_tsigkey_detach(&msg->tsigkey);
2889135446Strhodes		msg->tsigkey = NULL;
2890135446Strhodes	}
2891135446Strhodes}
2892135446Strhodes
2893135446Strhodesisc_result_t
2894135446Strhodesdns_message_rechecksig(dns_message_t *msg, dns_view_t *view) {
2895135446Strhodes	dns_message_resetsig(msg);
2896135446Strhodes	return (dns_message_checksig(msg, view));
2897135446Strhodes}
2898135446Strhodes
2899135446Strhodesisc_result_t
2900135446Strhodesdns_message_checksig(dns_message_t *msg, dns_view_t *view) {
2901135446Strhodes	isc_buffer_t b, msgb;
2902135446Strhodes
2903135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2904135446Strhodes
2905135446Strhodes	if (msg->tsigkey == NULL && msg->tsig == NULL && msg->sig0 == NULL)
2906135446Strhodes		return (ISC_R_SUCCESS);
2907135446Strhodes	INSIST(msg->saved.base != NULL);
2908135446Strhodes	isc_buffer_init(&msgb, msg->saved.base, msg->saved.length);
2909135446Strhodes	isc_buffer_add(&msgb, msg->saved.length);
2910135446Strhodes	if (msg->tsigkey != NULL || msg->tsig != NULL) {
2911135446Strhodes		if (view != NULL)
2912135446Strhodes			return (dns_view_checksig(view, &msgb, msg));
2913135446Strhodes		else
2914135446Strhodes			return (dns_tsig_verify(&msgb, msg, NULL, NULL));
2915135446Strhodes	} else {
2916135446Strhodes		dns_rdata_t rdata = DNS_RDATA_INIT;
2917135446Strhodes		dns_rdata_sig_t sig;
2918135446Strhodes		dns_rdataset_t keyset;
2919135446Strhodes		isc_result_t result;
2920135446Strhodes
2921135446Strhodes		result = dns_rdataset_first(msg->sig0);
2922135446Strhodes		INSIST(result == ISC_R_SUCCESS);
2923135446Strhodes		dns_rdataset_current(msg->sig0, &rdata);
2924135446Strhodes
2925135446Strhodes		/*
2926135446Strhodes		 * This can occur when the message is a dynamic update, since
2927135446Strhodes		 * the rdata length checking is relaxed.  This should not
2928135446Strhodes		 * happen in a well-formed message, since the SIG(0) is only
2929135446Strhodes		 * looked for in the additional section, and the dynamic update
2930135446Strhodes		 * meta-records are in the prerequisite and update sections.
2931135446Strhodes		 */
2932135446Strhodes		if (rdata.length == 0)
2933135446Strhodes			return (ISC_R_UNEXPECTEDEND);
2934135446Strhodes
2935135446Strhodes		result = dns_rdata_tostruct(&rdata, &sig, msg->mctx);
2936135446Strhodes		if (result != ISC_R_SUCCESS)
2937135446Strhodes			return (result);
2938135446Strhodes
2939135446Strhodes		dns_rdataset_init(&keyset);
2940135446Strhodes		if (view == NULL)
2941135446Strhodes			return (DNS_R_KEYUNAUTHORIZED);
2942135446Strhodes		result = dns_view_simplefind(view, &sig.signer,
2943135446Strhodes					     dns_rdatatype_key /* SIG(0) */,
2944135446Strhodes					     0, 0, ISC_FALSE, &keyset, NULL);
2945135446Strhodes
2946135446Strhodes		if (result != ISC_R_SUCCESS) {
2947135446Strhodes			/* XXXBEW Should possibly create a fetch here */
2948135446Strhodes			result = DNS_R_KEYUNAUTHORIZED;
2949135446Strhodes			goto freesig;
2950135446Strhodes		} else if (keyset.trust < dns_trust_secure) {
2951135446Strhodes			/* XXXBEW Should call a validator here */
2952135446Strhodes			result = DNS_R_KEYUNAUTHORIZED;
2953135446Strhodes			goto freesig;
2954135446Strhodes		}
2955135446Strhodes		result = dns_rdataset_first(&keyset);
2956135446Strhodes		INSIST(result == ISC_R_SUCCESS);
2957135446Strhodes		for (;
2958135446Strhodes		     result == ISC_R_SUCCESS;
2959135446Strhodes		     result = dns_rdataset_next(&keyset))
2960135446Strhodes		{
2961135446Strhodes			dst_key_t *key = NULL;
2962135446Strhodes
2963135446Strhodes			dns_rdataset_current(&keyset, &rdata);
2964135446Strhodes			isc_buffer_init(&b, rdata.data, rdata.length);
2965135446Strhodes			isc_buffer_add(&b, rdata.length);
2966135446Strhodes
2967135446Strhodes			result = dst_key_fromdns(&sig.signer, rdata.rdclass,
2968135446Strhodes						 &b, view->mctx, &key);
2969135446Strhodes			if (result != ISC_R_SUCCESS)
2970135446Strhodes				continue;
2971135446Strhodes			if (dst_key_alg(key) != sig.algorithm ||
2972135446Strhodes			    dst_key_id(key) != sig.keyid ||
2973135446Strhodes			    !(dst_key_proto(key) == DNS_KEYPROTO_DNSSEC ||
2974135446Strhodes			      dst_key_proto(key) == DNS_KEYPROTO_ANY))
2975135446Strhodes			{
2976135446Strhodes				dst_key_free(&key);
2977135446Strhodes				continue;
2978135446Strhodes			}
2979135446Strhodes			result = dns_dnssec_verifymessage(&msgb, msg, key);
2980135446Strhodes			dst_key_free(&key);
2981135446Strhodes			if (result == ISC_R_SUCCESS)
2982135446Strhodes				break;
2983135446Strhodes		}
2984135446Strhodes		if (result == ISC_R_NOMORE)
2985135446Strhodes			result = DNS_R_KEYUNAUTHORIZED;
2986135446Strhodes
2987135446Strhodes freesig:
2988135446Strhodes		if (dns_rdataset_isassociated(&keyset))
2989135446Strhodes			dns_rdataset_disassociate(&keyset);
2990135446Strhodes		dns_rdata_freestruct(&sig);
2991135446Strhodes		return (result);
2992135446Strhodes	}
2993135446Strhodes}
2994135446Strhodes
2995135446Strhodesisc_result_t
2996135446Strhodesdns_message_sectiontotext(dns_message_t *msg, dns_section_t section,
2997135446Strhodes			  const dns_master_style_t *style,
2998135446Strhodes			  dns_messagetextflag_t flags,
2999135446Strhodes			  isc_buffer_t *target) {
3000135446Strhodes	dns_name_t *name, empty_name;
3001135446Strhodes	dns_rdataset_t *rdataset;
3002135446Strhodes	isc_result_t result;
3003135446Strhodes
3004135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
3005135446Strhodes	REQUIRE(target != NULL);
3006135446Strhodes	REQUIRE(VALID_SECTION(section));
3007135446Strhodes
3008135446Strhodes	if (ISC_LIST_EMPTY(msg->sections[section]))
3009135446Strhodes		return (ISC_R_SUCCESS);
3010135446Strhodes
3011135446Strhodes	if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) {
3012135446Strhodes		ADD_STRING(target, ";; ");
3013135446Strhodes		if (msg->opcode != dns_opcode_update) {
3014135446Strhodes			ADD_STRING(target, sectiontext[section]);
3015174187Sdougb		} else {
3016135446Strhodes			ADD_STRING(target, updsectiontext[section]);
3017135446Strhodes		}
3018135446Strhodes		ADD_STRING(target, " SECTION:\n");
3019135446Strhodes	}
3020135446Strhodes
3021135446Strhodes	dns_name_init(&empty_name, NULL);
3022135446Strhodes	result = dns_message_firstname(msg, section);
3023135446Strhodes	if (result != ISC_R_SUCCESS) {
3024135446Strhodes		return (result);
3025135446Strhodes	}
3026135446Strhodes	do {
3027135446Strhodes		name = NULL;
3028135446Strhodes		dns_message_currentname(msg, section, &name);
3029135446Strhodes		for (rdataset = ISC_LIST_HEAD(name->list);
3030135446Strhodes		     rdataset != NULL;
3031135446Strhodes		     rdataset = ISC_LIST_NEXT(rdataset, link)) {
3032135446Strhodes			if (section == DNS_SECTION_QUESTION) {
3033135446Strhodes				ADD_STRING(target, ";");
3034135446Strhodes				result = dns_master_questiontotext(name,
3035135446Strhodes								   rdataset,
3036135446Strhodes								   style,
3037135446Strhodes								   target);
3038135446Strhodes			} else {
3039135446Strhodes				result = dns_master_rdatasettotext(name,
3040135446Strhodes								   rdataset,
3041135446Strhodes								   style,
3042135446Strhodes								   target);
3043135446Strhodes			}
3044135446Strhodes			if (result != ISC_R_SUCCESS)
3045135446Strhodes				return (result);
3046135446Strhodes		}
3047135446Strhodes		result = dns_message_nextname(msg, section);
3048135446Strhodes	} while (result == ISC_R_SUCCESS);
3049135446Strhodes	if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0 &&
3050135446Strhodes	    (flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
3051135446Strhodes		ADD_STRING(target, "\n");
3052135446Strhodes	if (result == ISC_R_NOMORE)
3053135446Strhodes		result = ISC_R_SUCCESS;
3054135446Strhodes	return (result);
3055135446Strhodes}
3056135446Strhodes
3057135446Strhodesisc_result_t
3058135446Strhodesdns_message_pseudosectiontotext(dns_message_t *msg,
3059135446Strhodes				dns_pseudosection_t section,
3060135446Strhodes				const dns_master_style_t *style,
3061135446Strhodes				dns_messagetextflag_t flags,
3062135446Strhodes				isc_buffer_t *target) {
3063135446Strhodes	dns_rdataset_t *ps = NULL;
3064135446Strhodes	dns_name_t *name = NULL;
3065135446Strhodes	isc_result_t result;
3066135446Strhodes	char buf[sizeof("1234567890")];
3067135446Strhodes	isc_uint32_t mbz;
3068135446Strhodes
3069135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
3070135446Strhodes	REQUIRE(target != NULL);
3071135446Strhodes	REQUIRE(VALID_PSEUDOSECTION(section));
3072135446Strhodes
3073135446Strhodes	switch (section) {
3074135446Strhodes	case DNS_PSEUDOSECTION_OPT:
3075135446Strhodes		ps = dns_message_getopt(msg);
3076135446Strhodes		if (ps == NULL)
3077135446Strhodes			return (ISC_R_SUCCESS);
3078135446Strhodes		if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
3079135446Strhodes			ADD_STRING(target, ";; OPT PSEUDOSECTION:\n");
3080135446Strhodes		ADD_STRING(target, "; EDNS: version: ");
3081135446Strhodes		snprintf(buf, sizeof(buf), "%u",
3082135446Strhodes			 (unsigned int)((ps->ttl & 0x00ff0000) >> 16));
3083135446Strhodes		ADD_STRING(target, buf);
3084135446Strhodes		ADD_STRING(target, ", flags:");
3085135446Strhodes		if ((ps->ttl & DNS_MESSAGEEXTFLAG_DO) != 0)
3086135446Strhodes			ADD_STRING(target, " do");
3087135446Strhodes		mbz = ps->ttl & ~DNS_MESSAGEEXTFLAG_DO & 0xffff;
3088135446Strhodes		if (mbz != 0) {
3089135446Strhodes			ADD_STRING(target, "; MBZ: ");
3090135446Strhodes			snprintf(buf, sizeof(buf), "%.4x ", mbz);
3091135446Strhodes			ADD_STRING(target, buf);
3092135446Strhodes			ADD_STRING(target, ", udp: ");
3093135446Strhodes		} else
3094135446Strhodes			ADD_STRING(target, "; udp: ");
3095135446Strhodes		snprintf(buf, sizeof(buf), "%u\n", (unsigned int)ps->rdclass);
3096135446Strhodes		ADD_STRING(target, buf);
3097135446Strhodes		return (ISC_R_SUCCESS);
3098135446Strhodes	case DNS_PSEUDOSECTION_TSIG:
3099135446Strhodes		ps = dns_message_gettsig(msg, &name);
3100135446Strhodes		if (ps == NULL)
3101135446Strhodes			return (ISC_R_SUCCESS);
3102135446Strhodes		if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
3103135446Strhodes			ADD_STRING(target, ";; TSIG PSEUDOSECTION:\n");
3104135446Strhodes		result = dns_master_rdatasettotext(name, ps, style, target);
3105135446Strhodes		if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0 &&
3106135446Strhodes		    (flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
3107135446Strhodes			ADD_STRING(target, "\n");
3108135446Strhodes		return (result);
3109135446Strhodes	case DNS_PSEUDOSECTION_SIG0:
3110135446Strhodes		ps = dns_message_getsig0(msg, &name);
3111135446Strhodes		if (ps == NULL)
3112135446Strhodes			return (ISC_R_SUCCESS);
3113135446Strhodes		if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
3114135446Strhodes			ADD_STRING(target, ";; SIG0 PSEUDOSECTION:\n");
3115135446Strhodes		result = dns_master_rdatasettotext(name, ps, style, target);
3116135446Strhodes		if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0 &&
3117135446Strhodes		    (flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
3118135446Strhodes			ADD_STRING(target, "\n");
3119135446Strhodes		return (result);
3120135446Strhodes	}
3121135446Strhodes	return (ISC_R_UNEXPECTED);
3122135446Strhodes}
3123135446Strhodes
3124135446Strhodesisc_result_t
3125135446Strhodesdns_message_totext(dns_message_t *msg, const dns_master_style_t *style,
3126135446Strhodes		   dns_messagetextflag_t flags, isc_buffer_t *target) {
3127135446Strhodes	char buf[sizeof("1234567890")];
3128135446Strhodes	isc_result_t result;
3129135446Strhodes
3130135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
3131135446Strhodes	REQUIRE(target != NULL);
3132135446Strhodes
3133135446Strhodes	if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0) {
3134135446Strhodes		ADD_STRING(target, ";; ->>HEADER<<- opcode: ");
3135135446Strhodes		ADD_STRING(target, opcodetext[msg->opcode]);
3136135446Strhodes		ADD_STRING(target, ", status: ");
3137174187Sdougb		if (msg->rcode < (sizeof(rcodetext)/sizeof(rcodetext[0]))) {
3138174187Sdougb			ADD_STRING(target, rcodetext[msg->rcode]);
3139174187Sdougb		} else {
3140174187Sdougb			snprintf(buf, sizeof(buf), "%4u", msg->rcode);
3141174187Sdougb			ADD_STRING(target, buf);
3142174187Sdougb		}
3143135446Strhodes		ADD_STRING(target, ", id: ");
3144135446Strhodes		snprintf(buf, sizeof(buf), "%6u", msg->id);
3145135446Strhodes		ADD_STRING(target, buf);
3146135446Strhodes		ADD_STRING(target, "\n;; flags: ");
3147135446Strhodes		if ((msg->flags & DNS_MESSAGEFLAG_QR) != 0)
3148135446Strhodes			ADD_STRING(target, "qr ");
3149135446Strhodes		if ((msg->flags & DNS_MESSAGEFLAG_AA) != 0)
3150135446Strhodes			ADD_STRING(target, "aa ");
3151135446Strhodes		if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0)
3152135446Strhodes			ADD_STRING(target, "tc ");
3153135446Strhodes		if ((msg->flags & DNS_MESSAGEFLAG_RD) != 0)
3154135446Strhodes			ADD_STRING(target, "rd ");
3155135446Strhodes		if ((msg->flags & DNS_MESSAGEFLAG_RA) != 0)
3156135446Strhodes			ADD_STRING(target, "ra ");
3157135446Strhodes		if ((msg->flags & DNS_MESSAGEFLAG_AD) != 0)
3158135446Strhodes			ADD_STRING(target, "ad ");
3159135446Strhodes		if ((msg->flags & DNS_MESSAGEFLAG_CD) != 0)
3160135446Strhodes			ADD_STRING(target, "cd ");
3161135446Strhodes		if (msg->opcode != dns_opcode_update) {
3162135446Strhodes			ADD_STRING(target, "; QUESTION: ");
3163135446Strhodes		} else {
3164135446Strhodes			ADD_STRING(target, "; ZONE: ");
3165135446Strhodes		}
3166135446Strhodes		snprintf(buf, sizeof(buf), "%1u",
3167135446Strhodes			 msg->counts[DNS_SECTION_QUESTION]);
3168135446Strhodes		ADD_STRING(target, buf);
3169135446Strhodes		if (msg->opcode != dns_opcode_update) {
3170135446Strhodes			ADD_STRING(target, ", ANSWER: ");
3171135446Strhodes		} else {
3172135446Strhodes			ADD_STRING(target, ", PREREQ: ");
3173135446Strhodes		}
3174135446Strhodes		snprintf(buf, sizeof(buf), "%1u",
3175135446Strhodes			 msg->counts[DNS_SECTION_ANSWER]);
3176135446Strhodes		ADD_STRING(target, buf);
3177135446Strhodes		if (msg->opcode != dns_opcode_update) {
3178135446Strhodes			ADD_STRING(target, ", AUTHORITY: ");
3179135446Strhodes		} else {
3180135446Strhodes			ADD_STRING(target, ", UPDATE: ");
3181135446Strhodes		}
3182135446Strhodes		snprintf(buf, sizeof(buf), "%1u",
3183135446Strhodes			msg->counts[DNS_SECTION_AUTHORITY]);
3184135446Strhodes		ADD_STRING(target, buf);
3185135446Strhodes		ADD_STRING(target, ", ADDITIONAL: ");
3186135446Strhodes		snprintf(buf, sizeof(buf), "%1u",
3187135446Strhodes			msg->counts[DNS_SECTION_ADDITIONAL]);
3188135446Strhodes		ADD_STRING(target, buf);
3189135446Strhodes		ADD_STRING(target, "\n");
3190135446Strhodes	}
3191135446Strhodes	result = dns_message_pseudosectiontotext(msg,
3192135446Strhodes						 DNS_PSEUDOSECTION_OPT,
3193135446Strhodes						 style, flags, target);
3194135446Strhodes	if (result != ISC_R_SUCCESS)
3195135446Strhodes		return (result);
3196135446Strhodes
3197135446Strhodes	result = dns_message_sectiontotext(msg, DNS_SECTION_QUESTION,
3198135446Strhodes					   style, flags, target);
3199135446Strhodes	if (result != ISC_R_SUCCESS)
3200135446Strhodes		return (result);
3201135446Strhodes	result = dns_message_sectiontotext(msg, DNS_SECTION_ANSWER,
3202135446Strhodes					   style, flags, target);
3203135446Strhodes	if (result != ISC_R_SUCCESS)
3204135446Strhodes		return (result);
3205135446Strhodes	result = dns_message_sectiontotext(msg, DNS_SECTION_AUTHORITY,
3206135446Strhodes					   style, flags, target);
3207135446Strhodes	if (result != ISC_R_SUCCESS)
3208135446Strhodes		return (result);
3209135446Strhodes	result = dns_message_sectiontotext(msg, DNS_SECTION_ADDITIONAL,
3210135446Strhodes					   style, flags, target);
3211135446Strhodes	if (result != ISC_R_SUCCESS)
3212135446Strhodes		return (result);
3213135446Strhodes
3214135446Strhodes	result = dns_message_pseudosectiontotext(msg,
3215135446Strhodes						 DNS_PSEUDOSECTION_TSIG,
3216135446Strhodes						 style, flags, target);
3217135446Strhodes	if (result != ISC_R_SUCCESS)
3218135446Strhodes		return (result);
3219135446Strhodes
3220135446Strhodes	result = dns_message_pseudosectiontotext(msg,
3221135446Strhodes						 DNS_PSEUDOSECTION_SIG0,
3222135446Strhodes						 style, flags, target);
3223135446Strhodes	if (result != ISC_R_SUCCESS)
3224135446Strhodes		return (result);
3225135446Strhodes
3226135446Strhodes	return (ISC_R_SUCCESS);
3227135446Strhodes}
3228135446Strhodes
3229135446Strhodesisc_region_t *
3230135446Strhodesdns_message_getrawmessage(dns_message_t *msg) {
3231135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
3232135446Strhodes	return (&msg->saved);
3233135446Strhodes}
3234135446Strhodes
3235135446Strhodesvoid
3236135446Strhodesdns_message_setsortorder(dns_message_t *msg, dns_rdatasetorderfunc_t order,
3237165071Sdougb			 const void *order_arg)
3238135446Strhodes{
3239135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
3240135446Strhodes	msg->order = order;
3241135446Strhodes	msg->order_arg = order_arg;
3242135446Strhodes}
3243135446Strhodes
3244135446Strhodesvoid
3245135446Strhodesdns_message_settimeadjust(dns_message_t *msg, int timeadjust) {
3246135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
3247135446Strhodes	msg->timeadjust = timeadjust;
3248135446Strhodes}
3249135446Strhodes
3250135446Strhodesint
3251135446Strhodesdns_message_gettimeadjust(dns_message_t *msg) {
3252135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
3253135446Strhodes	return (msg->timeadjust);
3254135446Strhodes}
3255135446Strhodes
3256135446Strhodesisc_result_t
3257135446Strhodesdns_opcode_totext(dns_opcode_t opcode, isc_buffer_t *target) {
3258135446Strhodes
3259135446Strhodes	REQUIRE(opcode < 16);
3260135446Strhodes
3261135446Strhodes	if (isc_buffer_availablelength(target) < strlen(opcodetext[opcode]))
3262135446Strhodes		return (ISC_R_NOSPACE);
3263135446Strhodes	isc_buffer_putstr(target, opcodetext[opcode]);
3264135446Strhodes	return (ISC_R_SUCCESS);
3265135446Strhodes}
3266