message.c revision 153816
1135446Strhodes/*
2245163Serwin * Copyright (C) 2004, 2005  Internet Systems Consortium, Inc. ("ISC")
3135446Strhodes * Copyright (C) 1999-2003  Internet Software Consortium.
4135446Strhodes *
5174187Sdougb * Permission to use, copy, modify, and distribute this software for any
6135446Strhodes * purpose with or without fee is hereby granted, provided that the above
7135446Strhodes * copyright notice and this permission notice appear in all copies.
8135446Strhodes *
9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11135446Strhodes * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15135446Strhodes * PERFORMANCE OF THIS SOFTWARE.
16135446Strhodes */
17135446Strhodes
18234010Sdougb/* $Id: message.c,v 1.194.2.10.2.20 2005/06/07 01:42:23 marka Exp $ */
19135446Strhodes
20135446Strhodes/***
21135446Strhodes *** Imports
22135446Strhodes ***/
23135446Strhodes
24135446Strhodes#include <config.h>
25135446Strhodes
26135446Strhodes#include <isc/buffer.h>
27135446Strhodes#include <isc/mem.h>
28135446Strhodes#include <isc/print.h>
29193149Sdougb#include <isc/string.h>		/* Required for HP/UX (and others?) */
30135446Strhodes#include <isc/util.h>
31135446Strhodes
32135446Strhodes#include <dns/dnssec.h>
33135446Strhodes#include <dns/keyvalues.h>
34135446Strhodes#include <dns/log.h>
35135446Strhodes#include <dns/masterdump.h>
36135446Strhodes#include <dns/message.h>
37135446Strhodes#include <dns/opcode.h>
38135446Strhodes#include <dns/rdata.h>
39135446Strhodes#include <dns/rdatalist.h>
40135446Strhodes#include <dns/rdataset.h>
41135446Strhodes#include <dns/rdatastruct.h>
42135446Strhodes#include <dns/result.h>
43135446Strhodes#include <dns/tsig.h>
44135446Strhodes#include <dns/view.h>
45135446Strhodes
46135446Strhodes#define DNS_MESSAGE_OPCODE_MASK		0x7800U
47135446Strhodes#define DNS_MESSAGE_OPCODE_SHIFT	11
48135446Strhodes#define DNS_MESSAGE_RCODE_MASK		0x000fU
49135446Strhodes#define DNS_MESSAGE_FLAG_MASK		0x8ff0U
50135446Strhodes#define DNS_MESSAGE_EDNSRCODE_MASK	0xff000000U
51135446Strhodes#define DNS_MESSAGE_EDNSRCODE_SHIFT	24
52135446Strhodes#define DNS_MESSAGE_EDNSVERSION_MASK	0x00ff0000U
53135446Strhodes#define DNS_MESSAGE_EDNSVERSION_SHIFT	16
54170222Sdougb
55170222Sdougb#define VALID_NAMED_SECTION(s)  (((s) > DNS_SECTION_ANY) \
56135446Strhodes				 && ((s) < DNS_SECTION_MAX))
57135446Strhodes#define VALID_SECTION(s)	(((s) >= DNS_SECTION_ANY) \
58135446Strhodes				 && ((s) < DNS_SECTION_MAX))
59135446Strhodes#define ADD_STRING(b, s)	{if (strlen(s) >= \
60245163Serwin				   isc_buffer_availablelength(b)) \
61135446Strhodes				       return(ISC_R_NOSPACE); else \
62135446Strhodes				       isc_buffer_putstr(b, s);}
63135446Strhodes#define VALID_PSEUDOSECTION(s)	(((s) >= DNS_PSEUDOSECTION_ANY) \
64135446Strhodes				 && ((s) < DNS_PSEUDOSECTION_MAX))
65135446Strhodes
66135446Strhodes/*
67135446Strhodes * This is the size of each individual scratchpad buffer, and the numbers
68135446Strhodes * of various block allocations used within the server.
69135446Strhodes * XXXMLG These should come from a config setting.
70135446Strhodes */
71135446Strhodes#define SCRATCHPAD_SIZE		512
72135446Strhodes#define NAME_COUNT		  8
73135446Strhodes#define OFFSET_COUNT		  4
74135446Strhodes#define RDATA_COUNT		  8
75135446Strhodes#define RDATALIST_COUNT		  8
76135446Strhodes#define RDATASET_COUNT		 RDATALIST_COUNT
77135446Strhodes
78135446Strhodes/*
79135446Strhodes * Text representation of the different items, for message_totext
80135446Strhodes * functions.
81135446Strhodes */
82135446Strhodesstatic const char *sectiontext[] = {
83135446Strhodes	"QUESTION",
84135446Strhodes	"ANSWER",
85135446Strhodes	"AUTHORITY",
86135446Strhodes	"ADDITIONAL"
87135446Strhodes};
88135446Strhodes
89135446Strhodesstatic const char *updsectiontext[] = {
90135446Strhodes	"ZONE",
91135446Strhodes	"PREREQUISITE",
92135446Strhodes	"UPDATE",
93135446Strhodes	"ADDITIONAL"
94135446Strhodes};
95135446Strhodes
96135446Strhodesstatic const char *opcodetext[] = {
97135446Strhodes	"QUERY",
98135446Strhodes	"IQUERY",
99135446Strhodes	"STATUS",
100135446Strhodes	"RESERVED3",
101135446Strhodes	"NOTIFY",
102135446Strhodes	"UPDATE",
103135446Strhodes	"RESERVED6",
104135446Strhodes	"RESERVED7",
105135446Strhodes	"RESERVED8",
106135446Strhodes	"RESERVED9",
107135446Strhodes	"RESERVED10",
108135446Strhodes	"RESERVED11",
109135446Strhodes	"RESERVED12",
110135446Strhodes	"RESERVED13",
111135446Strhodes	"RESERVED14",
112135446Strhodes	"RESERVED15"
113135446Strhodes};
114135446Strhodes
115135446Strhodesstatic const char *rcodetext[] = {
116135446Strhodes	"NOERROR",
117135446Strhodes	"FORMERR",
118135446Strhodes	"SERVFAIL",
119135446Strhodes	"NXDOMAIN",
120135446Strhodes	"NOTIMP",
121135446Strhodes	"REFUSED",
122135446Strhodes	"YXDOMAIN",
123135446Strhodes	"YXRRSET",
124135446Strhodes	"NXRRSET",
125135446Strhodes	"NOTAUTH",
126135446Strhodes	"NOTZONE",
127135446Strhodes	"RESERVED11",
128135446Strhodes	"RESERVED12",
129135446Strhodes	"RESERVED13",
130135446Strhodes	"RESERVED14",
131135446Strhodes	"RESERVED15",
132135446Strhodes	"BADVERS"
133135446Strhodes};
134193149Sdougb
135193149Sdougb
136193149Sdougb/*
137193149Sdougb * "helper" type, which consists of a block of some type, and is linkable.
138193149Sdougb * For it to work, sizeof(dns_msgblock_t) must be a multiple of the pointer
139193149Sdougb * size, or the allocated elements will not be alligned correctly.
140193149Sdougb */
141193149Sdougbstruct dns_msgblock {
142193149Sdougb	unsigned int			count;
143193149Sdougb	unsigned int			remaining;
144193149Sdougb	ISC_LINK(dns_msgblock_t)	link;
145193149Sdougb}; /* dynamically sized */
146193149Sdougb
147193149Sdougbstatic inline dns_msgblock_t *
148193149Sdougbmsgblock_allocate(isc_mem_t *, unsigned int, unsigned int);
149193149Sdougb
150193149Sdougb#define msgblock_get(block, type) \
151135446Strhodes	((type *)msgblock_internalget(block, sizeof(type)))
152135446Strhodes
153135446Strhodesstatic inline void *
154135446Strhodesmsgblock_internalget(dns_msgblock_t *, unsigned int);
155135446Strhodes
156135446Strhodesstatic inline void
157135446Strhodesmsgblock_reset(dns_msgblock_t *);
158135446Strhodes
159135446Strhodesstatic inline void
160135446Strhodesmsgblock_free(isc_mem_t *, dns_msgblock_t *, unsigned int);
161135446Strhodes
162135446Strhodes/*
163135446Strhodes * Allocate a new dns_msgblock_t, and return a pointer to it.  If no memory
164135446Strhodes * is free, return NULL.
165135446Strhodes */
166135446Strhodesstatic inline dns_msgblock_t *
167135446Strhodesmsgblock_allocate(isc_mem_t *mctx, unsigned int sizeof_type,
168135446Strhodes		  unsigned int count)
169135446Strhodes{
170135446Strhodes	dns_msgblock_t *block;
171135446Strhodes	unsigned int length;
172135446Strhodes
173135446Strhodes	length = sizeof(dns_msgblock_t) + (sizeof_type * count);
174135446Strhodes
175135446Strhodes	block = isc_mem_get(mctx, length);
176135446Strhodes	if (block == NULL)
177135446Strhodes		return (NULL);
178135446Strhodes
179135446Strhodes	block->count = count;
180135446Strhodes	block->remaining = count;
181135446Strhodes
182135446Strhodes	ISC_LINK_INIT(block, link);
183135446Strhodes
184135446Strhodes	return (block);
185135446Strhodes}
186135446Strhodes
187135446Strhodes/*
188135446Strhodes * Return an element from the msgblock.  If no more are available, return
189135446Strhodes * NULL.
190135446Strhodes */
191135446Strhodesstatic inline void *
192135446Strhodesmsgblock_internalget(dns_msgblock_t *block, unsigned int sizeof_type) {
193135446Strhodes	void *ptr;
194135446Strhodes
195135446Strhodes	if (block == NULL || block->remaining == 0)
196135446Strhodes		return (NULL);
197135446Strhodes
198135446Strhodes	block->remaining--;
199135446Strhodes
200135446Strhodes	ptr = (((unsigned char *)block)
201135446Strhodes	       + sizeof(dns_msgblock_t)
202135446Strhodes	       + (sizeof_type * block->remaining));
203135446Strhodes
204135446Strhodes	return (ptr);
205135446Strhodes}
206135446Strhodes
207135446Strhodesstatic inline void
208135446Strhodesmsgblock_reset(dns_msgblock_t *block) {
209135446Strhodes	block->remaining = block->count;
210135446Strhodes}
211135446Strhodes
212135446Strhodes/*
213135446Strhodes * Release memory associated with a message block.
214135446Strhodes */
215135446Strhodesstatic inline void
216135446Strhodesmsgblock_free(isc_mem_t *mctx, dns_msgblock_t *block, unsigned int sizeof_type)
217135446Strhodes{
218135446Strhodes	unsigned int length;
219135446Strhodes
220135446Strhodes	length = sizeof(dns_msgblock_t) + (sizeof_type * block->count);
221135446Strhodes
222135446Strhodes	isc_mem_put(mctx, block, length);
223135446Strhodes}
224135446Strhodes
225135446Strhodes/*
226135446Strhodes * Allocate a new dynamic buffer, and attach it to this message as the
227135446Strhodes * "current" buffer.  (which is always the last on the list, for our
228135446Strhodes * uses)
229135446Strhodes */
230135446Strhodesstatic inline isc_result_t
231135446Strhodesnewbuffer(dns_message_t *msg, unsigned int size) {
232135446Strhodes	isc_result_t result;
233135446Strhodes	isc_buffer_t *dynbuf;
234135446Strhodes
235135446Strhodes	dynbuf = NULL;
236135446Strhodes	result = isc_buffer_allocate(msg->mctx, &dynbuf, size);
237135446Strhodes	if (result != ISC_R_SUCCESS)
238135446Strhodes		return (ISC_R_NOMEMORY);
239135446Strhodes
240135446Strhodes	ISC_LIST_APPEND(msg->scratchpad, dynbuf, link);
241135446Strhodes	return (ISC_R_SUCCESS);
242135446Strhodes}
243135446Strhodes
244135446Strhodesstatic inline isc_buffer_t *
245135446Strhodescurrentbuffer(dns_message_t *msg) {
246135446Strhodes	isc_buffer_t *dynbuf;
247135446Strhodes
248135446Strhodes	dynbuf = ISC_LIST_TAIL(msg->scratchpad);
249135446Strhodes	INSIST(dynbuf != NULL);
250135446Strhodes
251135446Strhodes	return (dynbuf);
252135446Strhodes}
253135446Strhodes
254135446Strhodesstatic inline void
255135446Strhodesreleaserdata(dns_message_t *msg, dns_rdata_t *rdata) {
256135446Strhodes	ISC_LIST_PREPEND(msg->freerdata, rdata, link);
257135446Strhodes}
258135446Strhodes
259135446Strhodesstatic inline dns_rdata_t *
260135446Strhodesnewrdata(dns_message_t *msg) {
261135446Strhodes	dns_msgblock_t *msgblock;
262135446Strhodes	dns_rdata_t *rdata;
263135446Strhodes
264135446Strhodes	rdata = ISC_LIST_HEAD(msg->freerdata);
265135446Strhodes	if (rdata != NULL) {
266135446Strhodes		ISC_LIST_UNLINK(msg->freerdata, rdata, link);
267135446Strhodes		return (rdata);
268135446Strhodes	}
269135446Strhodes
270135446Strhodes	msgblock = ISC_LIST_TAIL(msg->rdatas);
271135446Strhodes	rdata = msgblock_get(msgblock, dns_rdata_t);
272135446Strhodes	if (rdata == NULL) {
273135446Strhodes		msgblock = msgblock_allocate(msg->mctx, sizeof(dns_rdata_t),
274135446Strhodes					     RDATA_COUNT);
275135446Strhodes		if (msgblock == NULL)
276135446Strhodes			return (NULL);
277135446Strhodes
278135446Strhodes		ISC_LIST_APPEND(msg->rdatas, msgblock, link);
279135446Strhodes
280135446Strhodes		rdata = msgblock_get(msgblock, dns_rdata_t);
281135446Strhodes	}
282135446Strhodes
283135446Strhodes	dns_rdata_init(rdata);
284135446Strhodes	return (rdata);
285135446Strhodes}
286135446Strhodes
287135446Strhodesstatic inline void
288135446Strhodesreleaserdatalist(dns_message_t *msg, dns_rdatalist_t *rdatalist) {
289135446Strhodes	ISC_LIST_PREPEND(msg->freerdatalist, rdatalist, link);
290135446Strhodes}
291135446Strhodes
292135446Strhodesstatic inline dns_rdatalist_t *
293135446Strhodesnewrdatalist(dns_message_t *msg) {
294135446Strhodes	dns_msgblock_t *msgblock;
295135446Strhodes	dns_rdatalist_t *rdatalist;
296135446Strhodes
297135446Strhodes	rdatalist = ISC_LIST_HEAD(msg->freerdatalist);
298135446Strhodes	if (rdatalist != NULL) {
299135446Strhodes		ISC_LIST_UNLINK(msg->freerdatalist, rdatalist, link);
300135446Strhodes		return (rdatalist);
301135446Strhodes	}
302135446Strhodes
303135446Strhodes	msgblock = ISC_LIST_TAIL(msg->rdatalists);
304135446Strhodes	rdatalist = msgblock_get(msgblock, dns_rdatalist_t);
305135446Strhodes	if (rdatalist == NULL) {
306135446Strhodes		msgblock = msgblock_allocate(msg->mctx,
307135446Strhodes					     sizeof(dns_rdatalist_t),
308135446Strhodes					     RDATALIST_COUNT);
309135446Strhodes		if (msgblock == NULL)
310135446Strhodes			return (NULL);
311135446Strhodes
312135446Strhodes		ISC_LIST_APPEND(msg->rdatalists, msgblock, link);
313135446Strhodes
314135446Strhodes		rdatalist = msgblock_get(msgblock, dns_rdatalist_t);
315135446Strhodes	}
316135446Strhodes
317135446Strhodes	return (rdatalist);
318135446Strhodes}
319135446Strhodes
320135446Strhodesstatic inline dns_offsets_t *
321135446Strhodesnewoffsets(dns_message_t *msg) {
322135446Strhodes	dns_msgblock_t *msgblock;
323135446Strhodes	dns_offsets_t *offsets;
324135446Strhodes
325135446Strhodes	msgblock = ISC_LIST_TAIL(msg->offsets);
326135446Strhodes	offsets = msgblock_get(msgblock, dns_offsets_t);
327135446Strhodes	if (offsets == NULL) {
328135446Strhodes		msgblock = msgblock_allocate(msg->mctx,
329135446Strhodes					     sizeof(dns_offsets_t),
330135446Strhodes					     OFFSET_COUNT);
331135446Strhodes		if (msgblock == NULL)
332135446Strhodes			return (NULL);
333135446Strhodes
334135446Strhodes		ISC_LIST_APPEND(msg->offsets, msgblock, link);
335135446Strhodes
336135446Strhodes		offsets = msgblock_get(msgblock, dns_offsets_t);
337135446Strhodes	}
338135446Strhodes
339135446Strhodes	return (offsets);
340135446Strhodes}
341135446Strhodes
342135446Strhodesstatic inline void
343135446Strhodesmsginitheader(dns_message_t *m) {
344135446Strhodes	m->id = 0;
345135446Strhodes	m->flags = 0;
346135446Strhodes	m->rcode = 0;
347135446Strhodes	m->opcode = 0;
348135446Strhodes	m->rdclass = 0;
349135446Strhodes}
350135446Strhodes
351135446Strhodesstatic inline void
352135446Strhodesmsginitprivate(dns_message_t *m) {
353135446Strhodes	unsigned int i;
354135446Strhodes
355135446Strhodes	for (i = 0; i < DNS_SECTION_MAX; i++) {
356135446Strhodes		m->cursors[i] = NULL;
357135446Strhodes		m->counts[i] = 0;
358135446Strhodes	}
359135446Strhodes	m->opt = NULL;
360135446Strhodes	m->sig0 = NULL;
361135446Strhodes	m->sig0name = NULL;
362135446Strhodes	m->tsig = NULL;
363135446Strhodes	m->tsigname = NULL;
364135446Strhodes	m->state = DNS_SECTION_ANY;  /* indicate nothing parsed or rendered */
365135446Strhodes	m->opt_reserved = 0;
366135446Strhodes	m->sig_reserved = 0;
367135446Strhodes	m->reserved = 0;
368135446Strhodes	m->buffer = NULL;
369135446Strhodes}
370135446Strhodes
371135446Strhodesstatic inline void
372135446Strhodesmsginittsig(dns_message_t *m) {
373135446Strhodes	m->tsigstatus = dns_rcode_noerror;
374135446Strhodes	m->querytsigstatus = dns_rcode_noerror;
375135446Strhodes	m->tsigkey = NULL;
376135446Strhodes	m->tsigctx = NULL;
377204619Sdougb	m->sigstart = -1;
378135446Strhodes	m->sig0key = NULL;
379135446Strhodes	m->sig0status = dns_rcode_noerror;
380135446Strhodes	m->timeadjust = 0;
381135446Strhodes}
382135446Strhodes
383135446Strhodes/*
384135446Strhodes * Init elements to default state.  Used both when allocating a new element
385135446Strhodes * and when resetting one.
386135446Strhodes */
387135446Strhodesstatic inline void
388135446Strhodesmsginit(dns_message_t *m) {
389135446Strhodes	msginitheader(m);
390135446Strhodes	msginitprivate(m);
391135446Strhodes	msginittsig(m);
392135446Strhodes	m->header_ok = 0;
393135446Strhodes	m->question_ok = 0;
394135446Strhodes	m->tcp_continuation = 0;
395135446Strhodes	m->verified_sig = 0;
396135446Strhodes	m->verify_attempted = 0;
397135446Strhodes	m->order = NULL;
398135446Strhodes	m->order_arg = NULL;
399135446Strhodes	m->query.base = NULL;
400135446Strhodes	m->query.length = 0;
401135446Strhodes	m->free_query = 0;
402135446Strhodes	m->saved.base = NULL;
403135446Strhodes	m->saved.length = 0;
404135446Strhodes	m->free_saved = 0;
405135446Strhodes	m->querytsig = NULL;
406135446Strhodes}
407135446Strhodes
408193149Sdougbstatic inline void
409135446Strhodesmsgresetnames(dns_message_t *msg, unsigned int first_section) {
410245163Serwin	unsigned int i;
411245163Serwin	dns_name_t *name, *next_name;
412245163Serwin	dns_rdataset_t *rds, *next_rds;
413135446Strhodes
414135446Strhodes	/*
415135446Strhodes	 * Clean up name lists by calling the rdataset disassociate function.
416143731Sdougb	 */
417135446Strhodes	for (i = first_section; i < DNS_SECTION_MAX; i++) {
418193149Sdougb		name = ISC_LIST_HEAD(msg->sections[i]);
419135446Strhodes		while (name != NULL) {
420135446Strhodes			next_name = ISC_LIST_NEXT(name, link);
421135446Strhodes			ISC_LIST_UNLINK(msg->sections[i], name, link);
422135446Strhodes
423135446Strhodes			rds = ISC_LIST_HEAD(name->list);
424135446Strhodes			while (rds != NULL) {
425135446Strhodes				next_rds = ISC_LIST_NEXT(rds, link);
426135446Strhodes				ISC_LIST_UNLINK(name->list, rds, link);
427135446Strhodes
428135446Strhodes				INSIST(dns_rdataset_isassociated(rds));
429135446Strhodes				dns_rdataset_disassociate(rds);
430135446Strhodes				isc_mempool_put(msg->rdspool, rds);
431135446Strhodes				rds = next_rds;
432135446Strhodes			}
433135446Strhodes			if (dns_name_dynamic(name))
434135446Strhodes				dns_name_free(name, msg->mctx);
435135446Strhodes			isc_mempool_put(msg->namepool, name);
436174187Sdougb			name = next_name;
437174187Sdougb		}
438193149Sdougb	}
439135446Strhodes}
440245163Serwin
441245163Serwinstatic void
442245163Serwinmsgresetopt(dns_message_t *msg)
443135446Strhodes{
444135446Strhodes	if (msg->opt != NULL) {
445135446Strhodes		if (msg->opt_reserved > 0) {
446135446Strhodes			dns_message_renderrelease(msg, msg->opt_reserved);
447135446Strhodes			msg->opt_reserved = 0;
448135446Strhodes		}
449135446Strhodes		INSIST(dns_rdataset_isassociated(msg->opt));
450135446Strhodes		dns_rdataset_disassociate(msg->opt);
451135446Strhodes		isc_mempool_put(msg->rdspool, msg->opt);
452135446Strhodes		msg->opt = NULL;
453135446Strhodes	}
454135446Strhodes}
455135446Strhodes
456135446Strhodesstatic void
457135446Strhodesmsgresetsigs(dns_message_t *msg, isc_boolean_t replying) {
458135446Strhodes	if (msg->sig_reserved > 0) {
459135446Strhodes		dns_message_renderrelease(msg, msg->sig_reserved);
460135446Strhodes		msg->sig_reserved = 0;
461135446Strhodes	}
462135446Strhodes	if (msg->tsig != NULL) {
463135446Strhodes		INSIST(dns_rdataset_isassociated(msg->tsig));
464135446Strhodes		INSIST(msg->namepool != NULL);
465135446Strhodes		if (replying) {
466135446Strhodes			INSIST(msg->querytsig == NULL);
467135446Strhodes			msg->querytsig = msg->tsig;
468135446Strhodes		} else {
469135446Strhodes			dns_rdataset_disassociate(msg->tsig);
470193149Sdougb			isc_mempool_put(msg->rdspool, msg->tsig);
471135446Strhodes			if (msg->querytsig != NULL) {
472135446Strhodes				dns_rdataset_disassociate(msg->querytsig);
473135446Strhodes				isc_mempool_put(msg->rdspool, msg->querytsig);
474135446Strhodes			}
475135446Strhodes		}
476135446Strhodes		if (dns_name_dynamic(msg->tsigname))
477193149Sdougb			dns_name_free(msg->tsigname, msg->mctx);
478193149Sdougb		isc_mempool_put(msg->namepool, msg->tsigname);
479193149Sdougb		msg->tsig = NULL;
480135446Strhodes		msg->tsigname = NULL;
481135446Strhodes	} else if (msg->querytsig != NULL && !replying) {
482143731Sdougb		dns_rdataset_disassociate(msg->querytsig);
483135446Strhodes		isc_mempool_put(msg->rdspool, msg->querytsig);
484135446Strhodes		msg->querytsig = NULL;
485135446Strhodes	}
486135446Strhodes	if (msg->sig0 != NULL) {
487135446Strhodes		INSIST(dns_rdataset_isassociated(msg->sig0));
488135446Strhodes		dns_rdataset_disassociate(msg->sig0);
489135446Strhodes		isc_mempool_put(msg->rdspool, msg->sig0);
490135446Strhodes		if (msg->sig0name != NULL) {
491135446Strhodes			if (dns_name_dynamic(msg->sig0name))
492135446Strhodes				dns_name_free(msg->sig0name, msg->mctx);
493135446Strhodes			isc_mempool_put(msg->namepool, msg->sig0name);
494135446Strhodes		}
495135446Strhodes		msg->sig0 = NULL;
496135446Strhodes		msg->sig0name = NULL;
497135446Strhodes	}
498135446Strhodes}
499135446Strhodes
500135446Strhodes/*
501135446Strhodes * Free all but one (or everything) for this message.  This is used by
502135446Strhodes * both dns_message_reset() and dns_message_destroy().
503135446Strhodes */
504135446Strhodesstatic void
505135446Strhodesmsgreset(dns_message_t *msg, isc_boolean_t everything) {
506135446Strhodes	dns_msgblock_t *msgblock, *next_msgblock;
507135446Strhodes	isc_buffer_t *dynbuf, *next_dynbuf;
508135446Strhodes	dns_rdata_t *rdata;
509135446Strhodes	dns_rdatalist_t *rdatalist;
510135446Strhodes
511135446Strhodes	msgresetnames(msg, 0);
512135446Strhodes	msgresetopt(msg);
513135446Strhodes	msgresetsigs(msg, ISC_FALSE);
514135446Strhodes
515135446Strhodes	/*
516135446Strhodes	 * Clean up linked lists.
517135446Strhodes	 */
518135446Strhodes
519135446Strhodes	/*
520135446Strhodes	 * Run through the free lists, and just unlink anything found there.
521135446Strhodes	 * The memory isn't lost since these are part of message blocks we
522135446Strhodes	 * have allocated.
523135446Strhodes	 */
524135446Strhodes	rdata = ISC_LIST_HEAD(msg->freerdata);
525135446Strhodes	while (rdata != NULL) {
526135446Strhodes		ISC_LIST_UNLINK(msg->freerdata, rdata, link);
527135446Strhodes		rdata = ISC_LIST_HEAD(msg->freerdata);
528135446Strhodes	}
529135446Strhodes	rdatalist = ISC_LIST_HEAD(msg->freerdatalist);
530135446Strhodes	while (rdatalist != NULL) {
531135446Strhodes		ISC_LIST_UNLINK(msg->freerdatalist, rdatalist, link);
532135446Strhodes		rdatalist = ISC_LIST_HEAD(msg->freerdatalist);
533135446Strhodes	}
534135446Strhodes
535135446Strhodes	dynbuf = ISC_LIST_HEAD(msg->scratchpad);
536135446Strhodes	INSIST(dynbuf != NULL);
537193149Sdougb	if (!everything) {
538135446Strhodes		isc_buffer_clear(dynbuf);
539135446Strhodes		dynbuf = ISC_LIST_NEXT(dynbuf, link);
540135446Strhodes	}
541135446Strhodes	while (dynbuf != NULL) {
542135446Strhodes		next_dynbuf = ISC_LIST_NEXT(dynbuf, link);
543135446Strhodes		ISC_LIST_UNLINK(msg->scratchpad, dynbuf, link);
544135446Strhodes		isc_buffer_free(&dynbuf);
545135446Strhodes		dynbuf = next_dynbuf;
546135446Strhodes	}
547135446Strhodes
548135446Strhodes	msgblock = ISC_LIST_HEAD(msg->rdatas);
549135446Strhodes	if (!everything && msgblock != NULL) {
550135446Strhodes		msgblock_reset(msgblock);
551135446Strhodes		msgblock = ISC_LIST_NEXT(msgblock, link);
552135446Strhodes	}
553135446Strhodes	while (msgblock != NULL) {
554135446Strhodes		next_msgblock = ISC_LIST_NEXT(msgblock, link);
555135446Strhodes		ISC_LIST_UNLINK(msg->rdatas, msgblock, link);
556135446Strhodes		msgblock_free(msg->mctx, msgblock, sizeof(dns_rdata_t));
557135446Strhodes		msgblock = next_msgblock;
558135446Strhodes	}
559135446Strhodes
560135446Strhodes	/*
561135446Strhodes	 * rdatalists could be empty.
562135446Strhodes	 */
563135446Strhodes
564135446Strhodes	msgblock = ISC_LIST_HEAD(msg->rdatalists);
565135446Strhodes	if (!everything && msgblock != NULL) {
566135446Strhodes		msgblock_reset(msgblock);
567135446Strhodes		msgblock = ISC_LIST_NEXT(msgblock, link);
568135446Strhodes	}
569135446Strhodes	while (msgblock != NULL) {
570135446Strhodes		next_msgblock = ISC_LIST_NEXT(msgblock, link);
571135446Strhodes		ISC_LIST_UNLINK(msg->rdatalists, msgblock, link);
572135446Strhodes		msgblock_free(msg->mctx, msgblock, sizeof(dns_rdatalist_t));
573135446Strhodes		msgblock = next_msgblock;
574135446Strhodes	}
575225361Sdougb
576135446Strhodes	msgblock = ISC_LIST_HEAD(msg->offsets);
577135446Strhodes	if (!everything && msgblock != NULL) {
578225361Sdougb		msgblock_reset(msgblock);
579135446Strhodes		msgblock = ISC_LIST_NEXT(msgblock, link);
580135446Strhodes	}
581225361Sdougb	while (msgblock != NULL) {
582135446Strhodes		next_msgblock = ISC_LIST_NEXT(msgblock, link);
583135446Strhodes		ISC_LIST_UNLINK(msg->offsets, msgblock, link);
584225361Sdougb		msgblock_free(msg->mctx, msgblock, sizeof(dns_offsets_t));
585135446Strhodes		msgblock = next_msgblock;
586135446Strhodes	}
587225361Sdougb
588135446Strhodes	if (msg->tsigkey != NULL) {
589135446Strhodes		dns_tsigkey_detach(&msg->tsigkey);
590225361Sdougb		msg->tsigkey = NULL;
591135446Strhodes	}
592135446Strhodes
593225361Sdougb	if (msg->query.base != NULL) {
594135446Strhodes		if (msg->free_query != 0)
595135446Strhodes			isc_mem_put(msg->mctx, msg->query.base,
596225361Sdougb				    msg->query.length);
597135446Strhodes		msg->query.base = NULL;
598225361Sdougb		msg->query.length = 0;
599135446Strhodes	}
600135446Strhodes
601135446Strhodes	if (msg->saved.base != NULL) {
602225361Sdougb		if (msg->free_saved != 0)
603135446Strhodes			isc_mem_put(msg->mctx, msg->saved.base,
604135446Strhodes				    msg->saved.length);
605135446Strhodes		msg->saved.base = NULL;
606135446Strhodes		msg->saved.length = 0;
607135446Strhodes	}
608135446Strhodes
609135446Strhodes	/*
610135446Strhodes	 * cleanup the buffer cleanup list
611135446Strhodes	 */
612135446Strhodes	dynbuf = ISC_LIST_HEAD(msg->cleanup);
613193149Sdougb	while (dynbuf != NULL) {
614135446Strhodes		next_dynbuf = ISC_LIST_NEXT(dynbuf, link);
615135446Strhodes		ISC_LIST_UNLINK(msg->cleanup, dynbuf, link);
616135446Strhodes		isc_buffer_free(&dynbuf);
617135446Strhodes		dynbuf = next_dynbuf;
618135446Strhodes	}
619135446Strhodes
620135446Strhodes	/*
621193149Sdougb	 * Set other bits to normal default values.
622135446Strhodes	 */
623135446Strhodes	if (!everything)
624135446Strhodes		msginit(msg);
625193149Sdougb
626135446Strhodes	ENSURE(isc_mempool_getallocated(msg->namepool) == 0);
627135446Strhodes	ENSURE(isc_mempool_getallocated(msg->rdspool) == 0);
628135446Strhodes}
629193149Sdougb
630135446Strhodesstatic unsigned int
631170222Sdougbspacefortsig(dns_tsigkey_t *key, int otherlen) {
632135446Strhodes	isc_region_t r1, r2;
633135446Strhodes	unsigned int x;
634170222Sdougb	isc_result_t result;
635193149Sdougb
636135446Strhodes	/*
637135446Strhodes	 * The space required for an TSIG record is:
638135446Strhodes	 *
639135446Strhodes	 *	n1 bytes for the name
640135446Strhodes	 *	2 bytes for the type
641135446Strhodes	 *	2 bytes for the class
642135446Strhodes	 *	4 bytes for the ttl
643135446Strhodes	 *	2 bytes for the rdlength
644135446Strhodes	 *	n2 bytes for the algorithm name
645170222Sdougb	 *	6 bytes for the time signed
646170222Sdougb	 *	2 bytes for the fudge
647170222Sdougb	 *	2 bytes for the MAC size
648170222Sdougb	 *	x bytes for the MAC
649135446Strhodes	 *	2 bytes for the original id
650193149Sdougb	 *	2 bytes for the error
651135446Strhodes	 *	2 bytes for the other data length
652135446Strhodes	 *	y bytes for the other data (at most)
653135446Strhodes	 * ---------------------------------
654135446Strhodes	 *     26 + n1 + n2 + x + y bytes
655135446Strhodes	 */
656135446Strhodes
657135446Strhodes	dns_name_toregion(&key->name, &r1);
658135446Strhodes	dns_name_toregion(key->algorithm, &r2);
659135446Strhodes	if (key->key == NULL)
660135446Strhodes		x = 0;
661135446Strhodes	else {
662135446Strhodes		result = dst_key_sigsize(key->key, &x);
663135446Strhodes		if (result != ISC_R_SUCCESS)
664135446Strhodes			x = 0;
665135446Strhodes	}
666135446Strhodes	return (26 + r1.length + r2.length + x + otherlen);
667135446Strhodes}
668135446Strhodes
669135446Strhodesisc_result_t
670135446Strhodesdns_message_create(isc_mem_t *mctx, unsigned int intent, dns_message_t **msgp)
671135446Strhodes{
672135446Strhodes	dns_message_t *m;
673135446Strhodes	isc_result_t result;
674135446Strhodes	isc_buffer_t *dynbuf;
675135446Strhodes	unsigned int i;
676135446Strhodes
677135446Strhodes	REQUIRE(mctx != NULL);
678135446Strhodes	REQUIRE(msgp != NULL);
679135446Strhodes	REQUIRE(*msgp == NULL);
680135446Strhodes	REQUIRE(intent == DNS_MESSAGE_INTENTPARSE
681225361Sdougb		|| intent == DNS_MESSAGE_INTENTRENDER);
682135446Strhodes
683135446Strhodes	m = isc_mem_get(mctx, sizeof(dns_message_t));
684135446Strhodes	if (m == NULL)
685225361Sdougb		return (ISC_R_NOMEMORY);
686135446Strhodes
687135446Strhodes	/*
688135446Strhodes	 * No allocations until further notice.  Just initialize all lists
689135446Strhodes	 * and other members that are freed in the cleanup phase here.
690135446Strhodes	 */
691135446Strhodes
692135446Strhodes	m->magic = DNS_MESSAGE_MAGIC;
693135446Strhodes	m->from_to_wire = intent;
694135446Strhodes	msginit(m);
695135446Strhodes
696135446Strhodes	for (i = 0; i < DNS_SECTION_MAX; i++)
697135446Strhodes		ISC_LIST_INIT(m->sections[i]);
698135446Strhodes	m->mctx = mctx;
699135446Strhodes
700135446Strhodes	ISC_LIST_INIT(m->scratchpad);
701135446Strhodes	ISC_LIST_INIT(m->cleanup);
702135446Strhodes	m->namepool = NULL;
703135446Strhodes	m->rdspool = NULL;
704135446Strhodes	ISC_LIST_INIT(m->rdatas);
705135446Strhodes	ISC_LIST_INIT(m->rdatalists);
706135446Strhodes	ISC_LIST_INIT(m->offsets);
707170222Sdougb	ISC_LIST_INIT(m->freerdata);
708170222Sdougb	ISC_LIST_INIT(m->freerdatalist);
709135446Strhodes
710135446Strhodes	/*
711135446Strhodes	 * Ok, it is safe to allocate (and then "goto cleanup" if failure)
712135446Strhodes	 */
713135446Strhodes
714135446Strhodes	result = isc_mempool_create(m->mctx, sizeof(dns_name_t), &m->namepool);
715135446Strhodes	if (result != ISC_R_SUCCESS)
716135446Strhodes		goto cleanup;
717135446Strhodes	isc_mempool_setfreemax(m->namepool, NAME_COUNT);
718135446Strhodes	isc_mempool_setname(m->namepool, "msg:names");
719135446Strhodes
720135446Strhodes	result = isc_mempool_create(m->mctx, sizeof(dns_rdataset_t),
721135446Strhodes				    &m->rdspool);
722135446Strhodes	if (result != ISC_R_SUCCESS)
723135446Strhodes		goto cleanup;
724135446Strhodes	isc_mempool_setfreemax(m->rdspool, NAME_COUNT);
725135446Strhodes	isc_mempool_setname(m->rdspool, "msg:rdataset");
726135446Strhodes
727135446Strhodes	dynbuf = NULL;
728165071Sdougb	result = isc_buffer_allocate(mctx, &dynbuf, SCRATCHPAD_SIZE);
729135446Strhodes	if (result != ISC_R_SUCCESS)
730135446Strhodes		goto cleanup;
731135446Strhodes	ISC_LIST_APPEND(m->scratchpad, dynbuf, link);
732135446Strhodes
733135446Strhodes	m->cctx = NULL;
734135446Strhodes
735135446Strhodes	*msgp = m;
736135446Strhodes	return (ISC_R_SUCCESS);
737135446Strhodes
738135446Strhodes	/*
739135446Strhodes	 * Cleanup for error returns.
740135446Strhodes	 */
741135446Strhodes cleanup:
742135446Strhodes	dynbuf = ISC_LIST_HEAD(m->scratchpad);
743135446Strhodes	if (dynbuf != NULL) {
744135446Strhodes		ISC_LIST_UNLINK(m->scratchpad, dynbuf, link);
745135446Strhodes		isc_buffer_free(&dynbuf);
746143731Sdougb	}
747135446Strhodes	if (m->namepool != NULL)
748170222Sdougb		isc_mempool_destroy(&m->namepool);
749143731Sdougb	if (m->rdspool != NULL)
750135446Strhodes		isc_mempool_destroy(&m->rdspool);
751135446Strhodes	m->magic = 0;
752135446Strhodes	isc_mem_put(mctx, m, sizeof(dns_message_t));
753135446Strhodes
754135446Strhodes	return (ISC_R_NOMEMORY);
755135446Strhodes}
756135446Strhodes
757135446Strhodesvoid
758135446Strhodesdns_message_reset(dns_message_t *msg, unsigned int intent) {
759135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
760135446Strhodes	REQUIRE(intent == DNS_MESSAGE_INTENTPARSE
761135446Strhodes		|| intent == DNS_MESSAGE_INTENTRENDER);
762135446Strhodes
763135446Strhodes	msgreset(msg, ISC_FALSE);
764135446Strhodes	msg->from_to_wire = intent;
765135446Strhodes}
766135446Strhodes
767135446Strhodesvoid
768135446Strhodesdns_message_destroy(dns_message_t **msgp) {
769135446Strhodes	dns_message_t *msg;
770135446Strhodes
771135446Strhodes	REQUIRE(msgp != NULL);
772135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(*msgp));
773135446Strhodes
774135446Strhodes	msg = *msgp;
775135446Strhodes	*msgp = NULL;
776135446Strhodes
777135446Strhodes	msgreset(msg, ISC_TRUE);
778135446Strhodes	isc_mempool_destroy(&msg->namepool);
779135446Strhodes	isc_mempool_destroy(&msg->rdspool);
780135446Strhodes	msg->magic = 0;
781135446Strhodes	isc_mem_put(msg->mctx, msg, sizeof(dns_message_t));
782135446Strhodes}
783135446Strhodes
784135446Strhodesstatic isc_result_t
785135446Strhodesfindname(dns_name_t **foundname, dns_name_t *target,
786135446Strhodes	 dns_namelist_t *section)
787170222Sdougb{
788170222Sdougb	dns_name_t *curr;
789170222Sdougb
790135446Strhodes	for (curr = ISC_LIST_TAIL(*section);
791135446Strhodes	     curr != NULL;
792135446Strhodes	     curr = ISC_LIST_PREV(curr, link)) {
793135446Strhodes		if (dns_name_equal(curr, target)) {
794135446Strhodes			if (foundname != NULL)
795135446Strhodes				*foundname = curr;
796135446Strhodes			return (ISC_R_SUCCESS);
797135446Strhodes		}
798135446Strhodes	}
799135446Strhodes
800135446Strhodes	return (ISC_R_NOTFOUND);
801135446Strhodes}
802135446Strhodes
803135446Strhodesisc_result_t
804135446Strhodesdns_message_findtype(dns_name_t *name, dns_rdatatype_t type,
805135446Strhodes		     dns_rdatatype_t covers, dns_rdataset_t **rdataset)
806135446Strhodes{
807135446Strhodes	dns_rdataset_t *curr;
808135446Strhodes
809135446Strhodes	if (rdataset != NULL) {
810135446Strhodes		REQUIRE(*rdataset == NULL);
811135446Strhodes	}
812135446Strhodes
813135446Strhodes	for (curr = ISC_LIST_TAIL(name->list);
814135446Strhodes	     curr != NULL;
815135446Strhodes	     curr = ISC_LIST_PREV(curr, link)) {
816135446Strhodes		if (curr->type == type && curr->covers == covers) {
817135446Strhodes			if (rdataset != NULL)
818135446Strhodes				*rdataset = curr;
819135446Strhodes			return (ISC_R_SUCCESS);
820135446Strhodes		}
821135446Strhodes	}
822135446Strhodes
823135446Strhodes	return (ISC_R_NOTFOUND);
824135446Strhodes}
825135446Strhodes
826135446Strhodes/*
827135446Strhodes * Read a name from buffer "source".
828135446Strhodes */
829135446Strhodesstatic isc_result_t
830135446Strhodesgetname(dns_name_t *name, isc_buffer_t *source, dns_message_t *msg,
831135446Strhodes	dns_decompress_t *dctx)
832135446Strhodes{
833135446Strhodes	isc_buffer_t *scratch;
834135446Strhodes	isc_result_t result;
835135446Strhodes	unsigned int tries;
836135446Strhodes
837135446Strhodes	scratch = currentbuffer(msg);
838135446Strhodes
839135446Strhodes	/*
840135446Strhodes	 * First try:  use current buffer.
841135446Strhodes	 * Second try:  allocate a new buffer and use that.
842135446Strhodes	 */
843135446Strhodes	tries = 0;
844135446Strhodes	while (tries < 2) {
845135446Strhodes		result = dns_name_fromwire(name, source, dctx, ISC_FALSE,
846135446Strhodes					   scratch);
847135446Strhodes
848135446Strhodes		if (result == ISC_R_NOSPACE) {
849135446Strhodes			tries++;
850135446Strhodes
851135446Strhodes			result = newbuffer(msg, SCRATCHPAD_SIZE);
852135446Strhodes			if (result != ISC_R_SUCCESS)
853135446Strhodes				return (result);
854135446Strhodes
855135446Strhodes			scratch = currentbuffer(msg);
856135446Strhodes			dns_name_reset(name);
857135446Strhodes		} else {
858135446Strhodes			return (result);
859135446Strhodes		}
860135446Strhodes	}
861135446Strhodes
862135446Strhodes	INSIST(0);  /* Cannot get here... */
863135446Strhodes	return (ISC_R_UNEXPECTED);
864135446Strhodes}
865135446Strhodes
866170222Sdougbstatic isc_result_t
867170222Sdougbgetrdata(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
868135446Strhodes	 dns_rdataclass_t rdclass, dns_rdatatype_t rdtype,
869135446Strhodes	 unsigned int rdatalen, dns_rdata_t *rdata)
870135446Strhodes{
871135446Strhodes	isc_buffer_t *scratch;
872135446Strhodes	isc_result_t result;
873135446Strhodes	unsigned int tries;
874135446Strhodes	unsigned int trysize;
875135446Strhodes
876135446Strhodes	scratch = currentbuffer(msg);
877135446Strhodes
878135446Strhodes	isc_buffer_setactive(source, rdatalen);
879135446Strhodes
880135446Strhodes	/*
881135446Strhodes	 * First try:  use current buffer.
882135446Strhodes	 * Second try:  allocate a new buffer of size
883135446Strhodes	 *     max(SCRATCHPAD_SIZE, 2 * compressed_rdatalen)
884135446Strhodes	 *     (the data will fit if it was not more than 50% compressed)
885135446Strhodes	 * Subsequent tries: double buffer size on each try.
886135446Strhodes	 */
887135446Strhodes	tries = 0;
888135446Strhodes	trysize = 0;
889135446Strhodes	/* XXX possibly change this to a while (tries < 2) loop */
890135446Strhodes	for (;;) {
891135446Strhodes		result = dns_rdata_fromwire(rdata, rdclass, rdtype,
892135446Strhodes					    source, dctx, 0,
893135446Strhodes					    scratch);
894135446Strhodes
895135446Strhodes		if (result == ISC_R_NOSPACE) {
896135446Strhodes			if (tries == 0) {
897245163Serwin				trysize = 2 * rdatalen;
898135446Strhodes				if (trysize < SCRATCHPAD_SIZE)
899					trysize = SCRATCHPAD_SIZE;
900			} else {
901				INSIST(trysize != 0);
902				if (trysize >= 65535)
903					return (ISC_R_NOSPACE);
904					/* XXX DNS_R_RRTOOLONG? */
905				trysize *= 2;
906			}
907			tries++;
908			result = newbuffer(msg, trysize);
909			if (result != ISC_R_SUCCESS)
910				return (result);
911
912			scratch = currentbuffer(msg);
913		} else {
914			return (result);
915		}
916	}
917}
918
919#define DO_FORMERR					\
920	do {						\
921		if (best_effort)			\
922			seen_problem = ISC_TRUE;	\
923		else {					\
924			result = DNS_R_FORMERR;		\
925			goto cleanup;			\
926		}					\
927	} while (0)
928
929static isc_result_t
930getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
931	     unsigned int options)
932{
933	isc_region_t r;
934	unsigned int count;
935	dns_name_t *name;
936	dns_name_t *name2;
937	dns_offsets_t *offsets;
938	dns_rdataset_t *rdataset;
939	dns_rdatalist_t *rdatalist;
940	isc_result_t result;
941	dns_rdatatype_t rdtype;
942	dns_rdataclass_t rdclass;
943	dns_namelist_t *section;
944	isc_boolean_t free_name;
945	isc_boolean_t best_effort;
946	isc_boolean_t seen_problem;
947
948	section = &msg->sections[DNS_SECTION_QUESTION];
949
950	best_effort = ISC_TF(options & DNS_MESSAGEPARSE_BESTEFFORT);
951	seen_problem = ISC_FALSE;
952
953	name = NULL;
954	rdataset = NULL;
955	rdatalist = NULL;
956
957	for (count = 0; count < msg->counts[DNS_SECTION_QUESTION]; count++) {
958		name = isc_mempool_get(msg->namepool);
959		if (name == NULL)
960			return (ISC_R_NOMEMORY);
961		free_name = ISC_TRUE;
962
963		offsets = newoffsets(msg);
964		if (offsets == NULL) {
965			result = ISC_R_NOMEMORY;
966			goto cleanup;
967		}
968		dns_name_init(name, *offsets);
969
970		/*
971		 * Parse the name out of this packet.
972		 */
973		isc_buffer_remainingregion(source, &r);
974		isc_buffer_setactive(source, r.length);
975		result = getname(name, source, msg, dctx);
976		if (result != ISC_R_SUCCESS)
977			goto cleanup;
978
979		/*
980		 * Run through the section, looking to see if this name
981		 * is already there.  If it is found, put back the allocated
982		 * name since we no longer need it, and set our name pointer
983		 * to point to the name we found.
984		 */
985		result = findname(&name2, name, section);
986
987		/*
988		 * If it is the first name in the section, accept it.
989		 *
990		 * If it is not, but is not the same as the name already
991		 * in the question section, append to the section.  Note that
992		 * here in the question section this is illegal, so return
993		 * FORMERR.  In the future, check the opcode to see if
994		 * this should be legal or not.  In either case we no longer
995		 * need this name pointer.
996		 */
997		if (result != ISC_R_SUCCESS) {
998			if (!ISC_LIST_EMPTY(*section))
999				DO_FORMERR;
1000			ISC_LIST_APPEND(*section, name, link);
1001			free_name = ISC_FALSE;
1002		} else {
1003			isc_mempool_put(msg->namepool, name);
1004			name = name2;
1005			name2 = NULL;
1006			free_name = ISC_FALSE;
1007		}
1008
1009		/*
1010		 * Get type and class.
1011		 */
1012		isc_buffer_remainingregion(source, &r);
1013		if (r.length < 4) {
1014			result = ISC_R_UNEXPECTEDEND;
1015			goto cleanup;
1016		}
1017		rdtype = isc_buffer_getuint16(source);
1018		rdclass = isc_buffer_getuint16(source);
1019
1020		/*
1021		 * If this class is different than the one we already read,
1022		 * this is an error.
1023		 */
1024		if (msg->state == DNS_SECTION_ANY) {
1025			msg->state = DNS_SECTION_QUESTION;
1026			msg->rdclass = rdclass;
1027		} else if (msg->rdclass != rdclass)
1028			DO_FORMERR;
1029
1030		/*
1031		 * Can't ask the same question twice.
1032		 */
1033		result = dns_message_findtype(name, rdtype, 0, NULL);
1034		if (result == ISC_R_SUCCESS)
1035			DO_FORMERR;
1036
1037		/*
1038		 * Allocate a new rdatalist.
1039		 */
1040		rdatalist = newrdatalist(msg);
1041		if (rdatalist == NULL) {
1042			result = ISC_R_NOMEMORY;
1043			goto cleanup;
1044		}
1045		rdataset =  isc_mempool_get(msg->rdspool);
1046		if (rdataset == NULL) {
1047			result = ISC_R_NOMEMORY;
1048			goto cleanup;
1049		}
1050
1051		/*
1052		 * Convert rdatalist to rdataset, and attach the latter to
1053		 * the name.
1054		 */
1055		rdatalist->type = rdtype;
1056		rdatalist->covers = 0;
1057		rdatalist->rdclass = rdclass;
1058		rdatalist->ttl = 0;
1059		ISC_LIST_INIT(rdatalist->rdata);
1060
1061		dns_rdataset_init(rdataset);
1062		result = dns_rdatalist_tordataset(rdatalist, rdataset);
1063		if (result != ISC_R_SUCCESS)
1064			goto cleanup;
1065
1066		rdataset->attributes |= DNS_RDATASETATTR_QUESTION;
1067
1068		ISC_LIST_APPEND(name->list, rdataset, link);
1069		rdataset = NULL;
1070	}
1071
1072	if (seen_problem)
1073		return (DNS_R_RECOVERABLE);
1074	return (ISC_R_SUCCESS);
1075
1076 cleanup:
1077	if (rdataset != NULL) {
1078		INSIST(!dns_rdataset_isassociated(rdataset));
1079		isc_mempool_put(msg->rdspool, rdataset);
1080	}
1081#if 0
1082	if (rdatalist != NULL)
1083		isc_mempool_put(msg->rdlpool, rdatalist);
1084#endif
1085	if (free_name)
1086		isc_mempool_put(msg->namepool, name);
1087
1088	return (result);
1089}
1090
1091static isc_boolean_t
1092update(dns_section_t section, dns_rdataclass_t rdclass) {
1093	if (section == DNS_SECTION_PREREQUISITE)
1094		return (ISC_TF(rdclass == dns_rdataclass_any ||
1095			       rdclass == dns_rdataclass_none));
1096	if (section == DNS_SECTION_UPDATE)
1097		return (ISC_TF(rdclass == dns_rdataclass_any));
1098	return (ISC_FALSE);
1099}
1100
1101static isc_result_t
1102getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
1103	   dns_section_t sectionid, unsigned int options)
1104{
1105	isc_region_t r;
1106	unsigned int count, rdatalen;
1107	dns_name_t *name;
1108	dns_name_t *name2;
1109	dns_offsets_t *offsets;
1110	dns_rdataset_t *rdataset;
1111	dns_rdatalist_t *rdatalist;
1112	isc_result_t result;
1113	dns_rdatatype_t rdtype, covers;
1114	dns_rdataclass_t rdclass;
1115	dns_rdata_t *rdata;
1116	dns_ttl_t ttl;
1117	dns_namelist_t *section;
1118	isc_boolean_t free_name, free_rdataset;
1119	isc_boolean_t preserve_order, best_effort, seen_problem;
1120	isc_boolean_t issigzero;
1121
1122	preserve_order = ISC_TF(options & DNS_MESSAGEPARSE_PRESERVEORDER);
1123	best_effort = ISC_TF(options & DNS_MESSAGEPARSE_BESTEFFORT);
1124	seen_problem = ISC_FALSE;
1125
1126	for (count = 0; count < msg->counts[sectionid]; count++) {
1127		int recstart = source->current;
1128		isc_boolean_t skip_name_search, skip_type_search;
1129
1130		section = &msg->sections[sectionid];
1131
1132		skip_name_search = ISC_FALSE;
1133		skip_type_search = ISC_FALSE;
1134		free_name = ISC_FALSE;
1135		free_rdataset = ISC_FALSE;
1136
1137		name = isc_mempool_get(msg->namepool);
1138		if (name == NULL)
1139			return (ISC_R_NOMEMORY);
1140		free_name = ISC_TRUE;
1141
1142		offsets = newoffsets(msg);
1143		if (offsets == NULL) {
1144			result = ISC_R_NOMEMORY;
1145			goto cleanup;
1146		}
1147		dns_name_init(name, *offsets);
1148
1149		/*
1150		 * Parse the name out of this packet.
1151		 */
1152		isc_buffer_remainingregion(source, &r);
1153		isc_buffer_setactive(source, r.length);
1154		result = getname(name, source, msg, dctx);
1155		if (result != ISC_R_SUCCESS)
1156			goto cleanup;
1157
1158		/*
1159		 * Get type, class, ttl, and rdatalen.  Verify that at least
1160		 * rdatalen bytes remain.  (Some of this is deferred to
1161		 * later.)
1162		 */
1163		isc_buffer_remainingregion(source, &r);
1164		if (r.length < 2 + 2 + 4 + 2) {
1165			result = ISC_R_UNEXPECTEDEND;
1166			goto cleanup;
1167		}
1168		rdtype = isc_buffer_getuint16(source);
1169		rdclass = isc_buffer_getuint16(source);
1170
1171		/*
1172		 * If there was no question section, we may not yet have
1173		 * established a class.  Do so now.
1174		 */
1175		if (msg->state == DNS_SECTION_ANY &&
1176		    rdtype != dns_rdatatype_opt &&	/* class is UDP SIZE */
1177		    rdtype != dns_rdatatype_tsig &&	/* class is ANY */
1178		    rdtype != dns_rdatatype_tkey) {	/* class is undefined */
1179			msg->rdclass = rdclass;
1180			msg->state = DNS_SECTION_QUESTION;
1181		}
1182
1183		/*
1184		 * If this class is different than the one in the question
1185		 * section, bail.
1186		 */
1187		if (msg->opcode != dns_opcode_update
1188		    && rdtype != dns_rdatatype_tsig
1189		    && rdtype != dns_rdatatype_opt
1190		    && rdtype != dns_rdatatype_dnskey /* in a TKEY query */
1191		    && rdtype != dns_rdatatype_sig /* SIG(0) */
1192		    && rdtype != dns_rdatatype_tkey /* Win2000 TKEY */
1193		    && msg->rdclass != rdclass)
1194			DO_FORMERR;
1195
1196		/*
1197		 * Special type handling for TSIG, OPT, and TKEY.
1198		 */
1199		if (rdtype == dns_rdatatype_tsig) {
1200			/*
1201			 * If it is a tsig, verify that it is in the
1202			 * additional data section.
1203			 */
1204			if (sectionid != DNS_SECTION_ADDITIONAL ||
1205			    rdclass != dns_rdataclass_any ||
1206			    count != msg->counts[sectionid]  - 1)
1207				DO_FORMERR;
1208			msg->sigstart = recstart;
1209			skip_name_search = ISC_TRUE;
1210			skip_type_search = ISC_TRUE;
1211		} else if (rdtype == dns_rdatatype_opt) {
1212			/*
1213			 * The name of an OPT record must be ".", it
1214			 * must be in the additional data section, and
1215			 * it must be the first OPT we've seen.
1216			 */
1217			if (!dns_name_equal(dns_rootname, name) ||
1218			    msg->opt != NULL)
1219				DO_FORMERR;
1220			skip_name_search = ISC_TRUE;
1221			skip_type_search = ISC_TRUE;
1222		} else if (rdtype == dns_rdatatype_tkey) {
1223			/*
1224			 * A TKEY must be in the additional section if this
1225			 * is a query, and the answer section if this is a
1226			 * response.  Unless it's a Win2000 client.
1227			 *
1228			 * Its class is ignored.
1229			 */
1230			dns_section_t tkeysection;
1231
1232			if ((msg->flags & DNS_MESSAGEFLAG_QR) == 0)
1233				tkeysection = DNS_SECTION_ADDITIONAL;
1234			else
1235				tkeysection = DNS_SECTION_ANSWER;
1236			if (sectionid != tkeysection &&
1237			    sectionid != DNS_SECTION_ANSWER)
1238				DO_FORMERR;
1239		}
1240
1241		/*
1242		 * ... now get ttl and rdatalen, and check buffer.
1243		 */
1244		ttl = isc_buffer_getuint32(source);
1245		rdatalen = isc_buffer_getuint16(source);
1246		r.length -= (2 + 2 + 4 + 2);
1247		if (r.length < rdatalen) {
1248			result = ISC_R_UNEXPECTEDEND;
1249			goto cleanup;
1250		}
1251
1252		/*
1253		 * Read the rdata from the wire format.  Interpret the
1254		 * rdata according to its actual class, even if it had a
1255		 * DynDNS meta-class in the packet (unless this is a TSIG).
1256		 * Then put the meta-class back into the finished rdata.
1257		 */
1258		rdata = newrdata(msg);
1259		if (rdata == NULL) {
1260			result = ISC_R_NOMEMORY;
1261			goto cleanup;
1262		}
1263		if (msg->opcode == dns_opcode_update &&
1264		    update(sectionid, rdclass)) {
1265			if (rdatalen != 0) {
1266				result = DNS_R_FORMERR;
1267				goto cleanup;
1268			}
1269			/*
1270			 * When the rdata is empty, the data pointer is
1271			 * never dereferenced, but it must still be non-NULL.
1272			 * Casting 1 rather than "" avoids warnings about
1273			 * discarding the const attribute of a string,
1274			 * for compilers that would warn about such things.
1275			 */
1276			rdata->data = (unsigned char *)1;
1277			rdata->length = 0;
1278			rdata->rdclass = rdclass;
1279			rdata->type = rdtype;
1280			rdata->flags = DNS_RDATA_UPDATE;
1281			result = ISC_R_SUCCESS;
1282		} else if (rdtype == dns_rdatatype_tsig)
1283			result = getrdata(source, msg, dctx, rdclass,
1284					  rdtype, rdatalen, rdata);
1285		else
1286			result = getrdata(source, msg, dctx, msg->rdclass,
1287					  rdtype, rdatalen, rdata);
1288		if (result != ISC_R_SUCCESS)
1289			goto cleanup;
1290		rdata->rdclass = rdclass;
1291		issigzero = ISC_FALSE;
1292		if (rdtype == dns_rdatatype_rrsig  &&
1293		    rdata->flags == 0) {
1294			covers = dns_rdata_covers(rdata);
1295			if (covers == 0)
1296				DO_FORMERR;
1297		} else if (rdtype == dns_rdatatype_sig /* SIG(0) */ &&
1298			   rdata->flags == 0) {
1299			covers = dns_rdata_covers(rdata);
1300			if (covers == 0) {
1301				if (sectionid != DNS_SECTION_ADDITIONAL ||
1302				    count != msg->counts[sectionid]  - 1)
1303					DO_FORMERR;
1304				msg->sigstart = recstart;
1305				skip_name_search = ISC_TRUE;
1306				skip_type_search = ISC_TRUE;
1307				issigzero = ISC_TRUE;
1308			}
1309		} else
1310			covers = 0;
1311
1312		/*
1313		 * If we are doing a dynamic update or this is a meta-type,
1314		 * don't bother searching for a name, just append this one
1315		 * to the end of the message.
1316		 */
1317		if (preserve_order || msg->opcode == dns_opcode_update ||
1318		    skip_name_search) {
1319			if (rdtype != dns_rdatatype_opt &&
1320			    rdtype != dns_rdatatype_tsig &&
1321			    !issigzero)
1322			{
1323				ISC_LIST_APPEND(*section, name, link);
1324				free_name = ISC_FALSE;
1325			}
1326		} else {
1327			/*
1328			 * Run through the section, looking to see if this name
1329			 * is already there.  If it is found, put back the
1330			 * allocated name since we no longer need it, and set
1331			 * our name pointer to point to the name we found.
1332			 */
1333			result = findname(&name2, name, section);
1334
1335			/*
1336			 * If it is a new name, append to the section.
1337			 */
1338			if (result == ISC_R_SUCCESS) {
1339				isc_mempool_put(msg->namepool, name);
1340				name = name2;
1341			} else {
1342				ISC_LIST_APPEND(*section, name, link);
1343			}
1344			free_name = ISC_FALSE;
1345		}
1346
1347		/*
1348		 * Search name for the particular type and class.
1349		 * Skip this stage if in update mode or this is a meta-type.
1350		 */
1351		if (preserve_order || msg->opcode == dns_opcode_update ||
1352		    skip_type_search)
1353			result = ISC_R_NOTFOUND;
1354		else {
1355			/*
1356			 * If this is a type that can only occur in
1357			 * the question section, fail.
1358			 */
1359			if (dns_rdatatype_questiononly(rdtype))
1360				DO_FORMERR;
1361
1362			rdataset = NULL;
1363			result = dns_message_findtype(name, rdtype, covers,
1364						      &rdataset);
1365		}
1366
1367		/*
1368		 * If we found an rdataset that matches, we need to
1369		 * append this rdata to that set.  If we did not, we need
1370		 * to create a new rdatalist, store the important bits there,
1371		 * convert it to an rdataset, and link the latter to the name.
1372		 * Yuck.  When appending, make certain that the type isn't
1373		 * a singleton type, such as SOA or CNAME.
1374		 *
1375		 * Note that this check will be bypassed when preserving order,
1376		 * the opcode is an update, or the type search is skipped.
1377		 */
1378		if (result == ISC_R_SUCCESS) {
1379			if (dns_rdatatype_issingleton(rdtype))
1380				DO_FORMERR;
1381		}
1382
1383		if (result == ISC_R_NOTFOUND) {
1384			rdataset = isc_mempool_get(msg->rdspool);
1385			if (rdataset == NULL) {
1386				result = ISC_R_NOMEMORY;
1387				goto cleanup;
1388			}
1389			free_rdataset = ISC_TRUE;
1390
1391			rdatalist = newrdatalist(msg);
1392			if (rdatalist == NULL) {
1393				result = ISC_R_NOMEMORY;
1394				goto cleanup;
1395			}
1396
1397			rdatalist->type = rdtype;
1398			rdatalist->covers = covers;
1399			rdatalist->rdclass = rdclass;
1400			rdatalist->ttl = ttl;
1401			ISC_LIST_INIT(rdatalist->rdata);
1402
1403			dns_rdataset_init(rdataset);
1404			RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist,
1405							       rdataset)
1406				      == ISC_R_SUCCESS);
1407
1408			if (rdtype != dns_rdatatype_opt &&
1409			    rdtype != dns_rdatatype_tsig &&
1410			    !issigzero)
1411			{
1412				ISC_LIST_APPEND(name->list, rdataset, link);
1413				free_rdataset = ISC_FALSE;
1414			}
1415		}
1416
1417		/*
1418		 * Minimize TTLs.
1419		 *
1420		 * Section 5.2 of RFC 2181 says we should drop
1421		 * nonauthoritative rrsets where the TTLs differ, but we
1422		 * currently treat them the as if they were authoritative and
1423		 * minimize them.
1424		 */
1425		if (ttl != rdataset->ttl) {
1426			rdataset->attributes |= DNS_RDATASETATTR_TTLADJUSTED;
1427			if (ttl < rdataset->ttl)
1428				rdataset->ttl = ttl;
1429		}
1430
1431		/*
1432		 * XXXMLG Perform a totally ugly hack here to pull
1433		 * the rdatalist out of the private field in the rdataset,
1434		 * and append this rdata to the rdatalist's linked list
1435		 * of rdata.
1436		 */
1437		rdatalist = (dns_rdatalist_t *)(rdataset->private1);
1438
1439		ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
1440
1441		/*
1442		 * If this is an OPT record, remember it.  Also, set
1443		 * the extended rcode.  Note that msg->opt will only be set
1444		 * if best-effort parsing is enabled.
1445		 */
1446		if (rdtype == dns_rdatatype_opt && msg->opt == NULL) {
1447			dns_rcode_t ercode;
1448
1449			msg->opt = rdataset;
1450			rdataset = NULL;
1451			free_rdataset = ISC_FALSE;
1452			ercode = (dns_rcode_t)
1453				((msg->opt->ttl & DNS_MESSAGE_EDNSRCODE_MASK)
1454				 >> 20);
1455			msg->rcode |= ercode;
1456			isc_mempool_put(msg->namepool, name);
1457			free_name = ISC_FALSE;
1458		}
1459
1460		/*
1461		 * If this is an SIG(0) or TSIG record, remember it.  Note
1462		 * that msg->sig0 or msg->tsig will only be set if best-effort
1463		 * parsing is enabled.
1464		 */
1465		if (issigzero && msg->sig0 == NULL) {
1466			msg->sig0 = rdataset;
1467			msg->sig0name = name;
1468			rdataset = NULL;
1469			free_rdataset = ISC_FALSE;
1470			free_name = ISC_FALSE;
1471		} else if (rdtype == dns_rdatatype_tsig && msg->tsig == NULL) {
1472			msg->tsig = rdataset;
1473			msg->tsigname = name;
1474			rdataset = NULL;
1475			free_rdataset = ISC_FALSE;
1476			free_name = ISC_FALSE;
1477		}
1478
1479		if (seen_problem) {
1480			if (free_name)
1481				isc_mempool_put(msg->namepool, name);
1482			if (free_rdataset)
1483				isc_mempool_put(msg->rdspool, rdataset);
1484			free_name = free_rdataset = ISC_FALSE;
1485		}
1486		INSIST(free_name == ISC_FALSE);
1487		INSIST(free_rdataset == ISC_FALSE);
1488	}
1489
1490	if (seen_problem)
1491		return (DNS_R_RECOVERABLE);
1492	return (ISC_R_SUCCESS);
1493
1494 cleanup:
1495	if (free_name)
1496		isc_mempool_put(msg->namepool, name);
1497	if (free_rdataset)
1498		isc_mempool_put(msg->rdspool, rdataset);
1499
1500	return (result);
1501}
1502
1503isc_result_t
1504dns_message_parse(dns_message_t *msg, isc_buffer_t *source,
1505		  unsigned int options)
1506{
1507	isc_region_t r;
1508	dns_decompress_t dctx;
1509	isc_result_t ret;
1510	isc_uint16_t tmpflags;
1511	isc_buffer_t origsource;
1512	isc_boolean_t seen_problem;
1513	isc_boolean_t ignore_tc;
1514
1515	REQUIRE(DNS_MESSAGE_VALID(msg));
1516	REQUIRE(source != NULL);
1517	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTPARSE);
1518
1519	seen_problem = ISC_FALSE;
1520	ignore_tc = ISC_TF(options & DNS_MESSAGEPARSE_IGNORETRUNCATION);
1521
1522	origsource = *source;
1523
1524	msg->header_ok = 0;
1525	msg->question_ok = 0;
1526
1527	isc_buffer_remainingregion(source, &r);
1528	if (r.length < DNS_MESSAGE_HEADERLEN)
1529		return (ISC_R_UNEXPECTEDEND);
1530
1531	msg->id = isc_buffer_getuint16(source);
1532	tmpflags = isc_buffer_getuint16(source);
1533	msg->opcode = ((tmpflags & DNS_MESSAGE_OPCODE_MASK)
1534		       >> DNS_MESSAGE_OPCODE_SHIFT);
1535	msg->rcode = (dns_rcode_t)(tmpflags & DNS_MESSAGE_RCODE_MASK);
1536	msg->flags = (tmpflags & DNS_MESSAGE_FLAG_MASK);
1537	msg->counts[DNS_SECTION_QUESTION] = isc_buffer_getuint16(source);
1538	msg->counts[DNS_SECTION_ANSWER] = isc_buffer_getuint16(source);
1539	msg->counts[DNS_SECTION_AUTHORITY] = isc_buffer_getuint16(source);
1540	msg->counts[DNS_SECTION_ADDITIONAL] = isc_buffer_getuint16(source);
1541
1542	msg->header_ok = 1;
1543
1544	/*
1545	 * -1 means no EDNS.
1546	 */
1547	dns_decompress_init(&dctx, -1, DNS_DECOMPRESS_ANY);
1548
1549	dns_decompress_setmethods(&dctx, DNS_COMPRESS_GLOBAL14);
1550
1551	ret = getquestions(source, msg, &dctx, options);
1552	if (ret == ISC_R_UNEXPECTEDEND && ignore_tc)
1553		goto truncated;
1554	if (ret == DNS_R_RECOVERABLE) {
1555		seen_problem = ISC_TRUE;
1556		ret = ISC_R_SUCCESS;
1557	}
1558	if (ret != ISC_R_SUCCESS)
1559		return (ret);
1560	msg->question_ok = 1;
1561
1562	ret = getsection(source, msg, &dctx, DNS_SECTION_ANSWER, options);
1563	if (ret == ISC_R_UNEXPECTEDEND && ignore_tc)
1564		goto truncated;
1565	if (ret == DNS_R_RECOVERABLE) {
1566		seen_problem = ISC_TRUE;
1567		ret = ISC_R_SUCCESS;
1568	}
1569	if (ret != ISC_R_SUCCESS)
1570		return (ret);
1571
1572	ret = getsection(source, msg, &dctx, DNS_SECTION_AUTHORITY, options);
1573	if (ret == ISC_R_UNEXPECTEDEND && ignore_tc)
1574		goto truncated;
1575	if (ret == DNS_R_RECOVERABLE) {
1576		seen_problem = ISC_TRUE;
1577		ret = ISC_R_SUCCESS;
1578	}
1579	if (ret != ISC_R_SUCCESS)
1580		return (ret);
1581
1582	ret = getsection(source, msg, &dctx, DNS_SECTION_ADDITIONAL, options);
1583	if (ret == ISC_R_UNEXPECTEDEND && ignore_tc)
1584		goto truncated;
1585	if (ret == DNS_R_RECOVERABLE) {
1586		seen_problem = ISC_TRUE;
1587		ret = ISC_R_SUCCESS;
1588	}
1589	if (ret != ISC_R_SUCCESS)
1590		return (ret);
1591
1592	isc_buffer_remainingregion(source, &r);
1593	if (r.length != 0) {
1594		isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
1595			      DNS_LOGMODULE_MESSAGE, ISC_LOG_DEBUG(3),
1596			      "message has %u byte(s) of trailing garbage",
1597			      r.length);
1598	}
1599
1600 truncated:
1601	if ((options & DNS_MESSAGEPARSE_CLONEBUFFER) == 0)
1602		isc_buffer_usedregion(&origsource, &msg->saved);
1603	else {
1604		msg->saved.length = isc_buffer_usedlength(&origsource);
1605		msg->saved.base = isc_mem_get(msg->mctx, msg->saved.length);
1606		if (msg->saved.base == NULL)
1607			return (ISC_R_NOMEMORY);
1608		memcpy(msg->saved.base, isc_buffer_base(&origsource),
1609		       msg->saved.length);
1610		msg->free_saved = 1;
1611	}
1612
1613	if (ret == ISC_R_UNEXPECTEDEND && ignore_tc)
1614		return (DNS_R_RECOVERABLE);
1615	if (seen_problem == ISC_TRUE)
1616		return (DNS_R_RECOVERABLE);
1617	return (ISC_R_SUCCESS);
1618}
1619
1620isc_result_t
1621dns_message_renderbegin(dns_message_t *msg, dns_compress_t *cctx,
1622			isc_buffer_t *buffer)
1623{
1624	isc_region_t r;
1625
1626	REQUIRE(DNS_MESSAGE_VALID(msg));
1627	REQUIRE(buffer != NULL);
1628	REQUIRE(msg->buffer == NULL);
1629	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
1630
1631	msg->cctx = cctx;
1632
1633	/*
1634	 * Erase the contents of this buffer.
1635	 */
1636	isc_buffer_clear(buffer);
1637
1638	/*
1639	 * Make certain there is enough for at least the header in this
1640	 * buffer.
1641	 */
1642	isc_buffer_availableregion(buffer, &r);
1643	if (r.length < DNS_MESSAGE_HEADERLEN)
1644		return (ISC_R_NOSPACE);
1645
1646	if (r.length < msg->reserved)
1647		return (ISC_R_NOSPACE);
1648
1649	/*
1650	 * Reserve enough space for the header in this buffer.
1651	 */
1652	isc_buffer_add(buffer, DNS_MESSAGE_HEADERLEN);
1653
1654	msg->buffer = buffer;
1655
1656	return (ISC_R_SUCCESS);
1657}
1658
1659isc_result_t
1660dns_message_renderchangebuffer(dns_message_t *msg, isc_buffer_t *buffer) {
1661	isc_region_t r, rn;
1662
1663	REQUIRE(DNS_MESSAGE_VALID(msg));
1664	REQUIRE(buffer != NULL);
1665	REQUIRE(msg->buffer != NULL);
1666
1667	/*
1668	 * Ensure that the new buffer is empty, and has enough space to
1669	 * hold the current contents.
1670	 */
1671	isc_buffer_clear(buffer);
1672
1673	isc_buffer_availableregion(buffer, &rn);
1674	isc_buffer_usedregion(msg->buffer, &r);
1675	REQUIRE(rn.length > r.length);
1676
1677	/*
1678	 * Copy the contents from the old to the new buffer.
1679	 */
1680	isc_buffer_add(buffer, r.length);
1681	memcpy(rn.base, r.base, r.length);
1682
1683	msg->buffer = buffer;
1684
1685	return (ISC_R_SUCCESS);
1686}
1687
1688void
1689dns_message_renderrelease(dns_message_t *msg, unsigned int space) {
1690	REQUIRE(DNS_MESSAGE_VALID(msg));
1691	REQUIRE(space <= msg->reserved);
1692
1693	msg->reserved -= space;
1694}
1695
1696isc_result_t
1697dns_message_renderreserve(dns_message_t *msg, unsigned int space) {
1698	isc_region_t r;
1699
1700	REQUIRE(DNS_MESSAGE_VALID(msg));
1701
1702	if (msg->buffer != NULL) {
1703		isc_buffer_availableregion(msg->buffer, &r);
1704		if (r.length < (space + msg->reserved))
1705			return (ISC_R_NOSPACE);
1706	}
1707
1708	msg->reserved += space;
1709
1710	return (ISC_R_SUCCESS);
1711}
1712
1713static inline isc_boolean_t
1714wrong_priority(dns_rdataset_t *rds, int pass, dns_rdatatype_t preferred_glue) {
1715	int pass_needed;
1716
1717	/*
1718	 * If we are not rendering class IN, this ordering is bogus.
1719	 */
1720	if (rds->rdclass != dns_rdataclass_in)
1721		return (ISC_FALSE);
1722
1723	switch (rds->type) {
1724	case dns_rdatatype_a:
1725	case dns_rdatatype_aaaa:
1726		if (preferred_glue == rds->type)
1727			pass_needed = 4;
1728		else
1729			pass_needed = 3;
1730		break;
1731	case dns_rdatatype_rrsig:
1732	case dns_rdatatype_dnskey:
1733		pass_needed = 2;
1734		break;
1735	default:
1736		pass_needed = 1;
1737	}
1738
1739	if (pass_needed >= pass)
1740		return (ISC_FALSE);
1741
1742	return (ISC_TRUE);
1743}
1744
1745isc_result_t
1746dns_message_rendersection(dns_message_t *msg, dns_section_t sectionid,
1747			  unsigned int options)
1748{
1749	dns_namelist_t *section;
1750	dns_name_t *name, *next_name;
1751	dns_rdataset_t *rdataset, *next_rdataset;
1752	unsigned int count, total;
1753	isc_result_t result;
1754	isc_buffer_t st; /* for rollbacks */
1755	int pass;
1756	isc_boolean_t partial = ISC_FALSE;
1757	unsigned int rd_options;
1758	dns_rdatatype_t preferred_glue = 0;
1759
1760	REQUIRE(DNS_MESSAGE_VALID(msg));
1761	REQUIRE(msg->buffer != NULL);
1762	REQUIRE(VALID_NAMED_SECTION(sectionid));
1763
1764	section = &msg->sections[sectionid];
1765
1766	if ((sectionid == DNS_SECTION_ADDITIONAL)
1767	    && (options & DNS_MESSAGERENDER_ORDERED) == 0) {
1768		if ((options & DNS_MESSAGERENDER_PREFER_A) != 0) {
1769			preferred_glue = dns_rdatatype_a;
1770			pass = 4;
1771		} else if ((options & DNS_MESSAGERENDER_PREFER_AAAA) != 0) {
1772			preferred_glue = dns_rdatatype_aaaa;
1773			pass = 4;
1774		} else
1775			pass = 3;
1776	} else
1777		pass = 1;
1778
1779	if ((options & DNS_MESSAGERENDER_OMITDNSSEC) == 0)
1780		rd_options = 0;
1781	else
1782		rd_options = DNS_RDATASETTOWIRE_OMITDNSSEC;
1783
1784	/*
1785	 * Shrink the space in the buffer by the reserved amount.
1786	 */
1787	msg->buffer->length -= msg->reserved;
1788
1789	total = 0;
1790	if (msg->reserved == 0 && (options & DNS_MESSAGERENDER_PARTIAL) != 0)
1791		partial = ISC_TRUE;
1792
1793	/*
1794	 * Render required glue first.  Set TC if it won't fit.
1795	 */
1796	name = ISC_LIST_HEAD(*section);
1797	if (name != NULL) {
1798		rdataset = ISC_LIST_HEAD(name->list);
1799		if (rdataset != NULL &&
1800		    (rdataset->attributes & DNS_RDATASETATTR_REQUIREDGLUE) != 0 &&
1801		    (rdataset->attributes & DNS_RDATASETATTR_RENDERED) == 0) {
1802			void *order_arg = msg->order_arg;
1803			st = *(msg->buffer);
1804			count = 0;
1805			if (partial)
1806				result = dns_rdataset_towirepartial(rdataset,
1807								    name,
1808								    msg->cctx,
1809								    msg->buffer,
1810								    msg->order,
1811								    order_arg,
1812								    rd_options,
1813								    &count,
1814								    NULL);
1815			else
1816				result = dns_rdataset_towiresorted(rdataset,
1817								   name,
1818								   msg->cctx,
1819								   msg->buffer,
1820								   msg->order,
1821								   order_arg,
1822								   rd_options,
1823								   &count);
1824			total += count;
1825			if (partial && result == ISC_R_NOSPACE) {
1826				msg->flags |= DNS_MESSAGEFLAG_TC;
1827				msg->buffer->length += msg->reserved;
1828				msg->counts[sectionid] += total;
1829				return (result);
1830			}
1831			if (result != ISC_R_SUCCESS) {
1832				INSIST(st.used < 65536);
1833				dns_compress_rollback(msg->cctx,
1834						      (isc_uint16_t)st.used);
1835				*(msg->buffer) = st;  /* rollback */
1836				msg->buffer->length += msg->reserved;
1837				msg->counts[sectionid] += total;
1838				return (result);
1839			}
1840			rdataset->attributes |= DNS_RDATASETATTR_RENDERED;
1841		}
1842	}
1843
1844	do {
1845		name = ISC_LIST_HEAD(*section);
1846		if (name == NULL) {
1847			msg->buffer->length += msg->reserved;
1848			msg->counts[sectionid] += total;
1849			return (ISC_R_SUCCESS);
1850		}
1851
1852		while (name != NULL) {
1853			next_name = ISC_LIST_NEXT(name, link);
1854
1855			rdataset = ISC_LIST_HEAD(name->list);
1856			while (rdataset != NULL) {
1857				next_rdataset = ISC_LIST_NEXT(rdataset, link);
1858
1859				if ((rdataset->attributes &
1860				     DNS_RDATASETATTR_RENDERED) != 0)
1861					goto next;
1862
1863				if (((options & DNS_MESSAGERENDER_ORDERED)
1864				     == 0)
1865				    && (sectionid == DNS_SECTION_ADDITIONAL)
1866				    && wrong_priority(rdataset, pass,
1867						      preferred_glue))
1868					goto next;
1869
1870				st = *(msg->buffer);
1871
1872				count = 0;
1873				if (partial)
1874					result = dns_rdataset_towirepartial(
1875							  rdataset,
1876							  name,
1877							  msg->cctx,
1878							  msg->buffer,
1879							  msg->order,
1880							  msg->order_arg,
1881							  rd_options,
1882							  &count,
1883							  NULL);
1884				else
1885					result = dns_rdataset_towiresorted(
1886							  rdataset,
1887							  name,
1888							  msg->cctx,
1889							  msg->buffer,
1890							  msg->order,
1891							  msg->order_arg,
1892							  rd_options,
1893							  &count);
1894
1895				total += count;
1896
1897				/*
1898				 * If out of space, record stats on what we
1899				 * rendered so far, and return that status.
1900				 *
1901				 * XXXMLG Need to change this when
1902				 * dns_rdataset_towire() can render partial
1903				 * sets starting at some arbitary point in the
1904				 * set.  This will include setting a bit in the
1905				 * rdataset to indicate that a partial
1906				 * rendering was done, and some state saved
1907				 * somewhere (probably in the message struct)
1908				 * to indicate where to continue from.
1909				 */
1910				if (partial && result == ISC_R_NOSPACE) {
1911					msg->buffer->length += msg->reserved;
1912					msg->counts[sectionid] += total;
1913					return (result);
1914				}
1915				if (result != ISC_R_SUCCESS) {
1916					INSIST(st.used < 65536);
1917					dns_compress_rollback(msg->cctx,
1918							(isc_uint16_t)st.used);
1919					*(msg->buffer) = st;  /* rollback */
1920					msg->buffer->length += msg->reserved;
1921					msg->counts[sectionid] += total;
1922					return (result);
1923				}
1924
1925				/*
1926				 * If we have rendered non-validated data,
1927				 * ensure that the AD bit is not set.
1928				 */
1929				if (rdataset->trust != dns_trust_secure &&
1930				    (sectionid == DNS_SECTION_ANSWER ||
1931				     sectionid == DNS_SECTION_AUTHORITY))
1932					msg->flags &= ~DNS_MESSAGEFLAG_AD;
1933
1934				rdataset->attributes |=
1935					DNS_RDATASETATTR_RENDERED;
1936
1937			next:
1938				rdataset = next_rdataset;
1939			}
1940
1941			name = next_name;
1942		}
1943	} while (--pass != 0);
1944
1945	msg->buffer->length += msg->reserved;
1946	msg->counts[sectionid] += total;
1947
1948	return (ISC_R_SUCCESS);
1949}
1950
1951void
1952dns_message_renderheader(dns_message_t *msg, isc_buffer_t *target) {
1953	isc_uint16_t tmp;
1954	isc_region_t r;
1955
1956	REQUIRE(DNS_MESSAGE_VALID(msg));
1957	REQUIRE(target != NULL);
1958
1959	isc_buffer_availableregion(target, &r);
1960	REQUIRE(r.length >= DNS_MESSAGE_HEADERLEN);
1961
1962	isc_buffer_putuint16(target, msg->id);
1963
1964	tmp = ((msg->opcode << DNS_MESSAGE_OPCODE_SHIFT)
1965	       & DNS_MESSAGE_OPCODE_MASK);
1966	tmp |= (msg->rcode & DNS_MESSAGE_RCODE_MASK);
1967	tmp |= (msg->flags & DNS_MESSAGE_FLAG_MASK);
1968
1969	INSIST(msg->counts[DNS_SECTION_QUESTION]  < 65536 &&
1970	       msg->counts[DNS_SECTION_ANSWER]    < 65536 &&
1971	       msg->counts[DNS_SECTION_AUTHORITY] < 65536 &&
1972	       msg->counts[DNS_SECTION_ADDITIONAL] < 65536);
1973
1974	isc_buffer_putuint16(target, tmp);
1975	isc_buffer_putuint16(target,
1976			    (isc_uint16_t)msg->counts[DNS_SECTION_QUESTION]);
1977	isc_buffer_putuint16(target,
1978			    (isc_uint16_t)msg->counts[DNS_SECTION_ANSWER]);
1979	isc_buffer_putuint16(target,
1980			    (isc_uint16_t)msg->counts[DNS_SECTION_AUTHORITY]);
1981	isc_buffer_putuint16(target,
1982			    (isc_uint16_t)msg->counts[DNS_SECTION_ADDITIONAL]);
1983}
1984
1985isc_result_t
1986dns_message_renderend(dns_message_t *msg) {
1987	isc_buffer_t tmpbuf;
1988	isc_region_t r;
1989	int result;
1990	unsigned int count;
1991
1992	REQUIRE(DNS_MESSAGE_VALID(msg));
1993	REQUIRE(msg->buffer != NULL);
1994
1995	if ((msg->rcode & ~DNS_MESSAGE_RCODE_MASK) != 0 && msg->opt == NULL) {
1996		/*
1997		 * We have an extended rcode but are not using EDNS.
1998		 */
1999		return (DNS_R_FORMERR);
2000	}
2001
2002	/*
2003	 * If we've got an OPT record, render it.
2004	 */
2005	if (msg->opt != NULL) {
2006		dns_message_renderrelease(msg, msg->opt_reserved);
2007		msg->opt_reserved = 0;
2008		/*
2009		 * Set the extended rcode.
2010		 */
2011		msg->opt->ttl &= ~DNS_MESSAGE_EDNSRCODE_MASK;
2012		msg->opt->ttl |= ((msg->rcode << 20) &
2013				  DNS_MESSAGE_EDNSRCODE_MASK);
2014		/*
2015		 * Render.
2016		 */
2017		count = 0;
2018		result = dns_rdataset_towire(msg->opt, dns_rootname,
2019					     msg->cctx, msg->buffer, 0,
2020					     &count);
2021		msg->counts[DNS_SECTION_ADDITIONAL] += count;
2022		if (result != ISC_R_SUCCESS)
2023			return (result);
2024	}
2025
2026	/*
2027	 * If we're adding a TSIG or SIG(0) to a truncated message,
2028	 * clear all rdatasets from the message except for the question
2029	 * before adding the TSIG or SIG(0).  If the question doesn't fit,
2030	 * don't include it.
2031	 */
2032	if ((msg->tsigkey != NULL || msg->sig0key != NULL) &&
2033	    (msg->flags & DNS_MESSAGEFLAG_TC) != 0)
2034	{
2035		isc_buffer_t *buf;
2036
2037		msgresetnames(msg, DNS_SECTION_ANSWER);
2038		buf = msg->buffer;
2039		dns_message_renderreset(msg);
2040		msg->buffer = buf;
2041		isc_buffer_clear(msg->buffer);
2042		isc_buffer_add(msg->buffer, DNS_MESSAGE_HEADERLEN);
2043		dns_compress_rollback(msg->cctx, 0);
2044		result = dns_message_rendersection(msg, DNS_SECTION_QUESTION,
2045						   0);
2046		if (result != ISC_R_SUCCESS && result != ISC_R_NOSPACE)
2047			return (result);
2048	}
2049
2050	/*
2051	 * If we're adding a TSIG record, generate and render it.
2052	 */
2053	if (msg->tsigkey != NULL) {
2054		dns_message_renderrelease(msg, msg->sig_reserved);
2055		msg->sig_reserved = 0;
2056		result = dns_tsig_sign(msg);
2057		if (result != ISC_R_SUCCESS)
2058			return (result);
2059		count = 0;
2060		result = dns_rdataset_towire(msg->tsig, msg->tsigname,
2061					     msg->cctx, msg->buffer, 0,
2062					     &count);
2063		msg->counts[DNS_SECTION_ADDITIONAL] += count;
2064		if (result != ISC_R_SUCCESS)
2065			return (result);
2066	}
2067
2068	/*
2069	 * If we're adding a SIG(0) record, generate and render it.
2070	 */
2071	if (msg->sig0key != NULL) {
2072		dns_message_renderrelease(msg, msg->sig_reserved);
2073		msg->sig_reserved = 0;
2074		result = dns_dnssec_signmessage(msg, msg->sig0key);
2075		if (result != ISC_R_SUCCESS)
2076			return (result);
2077		count = 0;
2078		/*
2079		 * Note: dns_rootname is used here, not msg->sig0name, since
2080		 * the owner name of a SIG(0) is irrelevant, and will not
2081		 * be set in a message being rendered.
2082		 */
2083		result = dns_rdataset_towire(msg->sig0, dns_rootname,
2084					     msg->cctx, msg->buffer, 0,
2085					     &count);
2086		msg->counts[DNS_SECTION_ADDITIONAL] += count;
2087		if (result != ISC_R_SUCCESS)
2088			return (result);
2089	}
2090
2091	isc_buffer_usedregion(msg->buffer, &r);
2092	isc_buffer_init(&tmpbuf, r.base, r.length);
2093
2094	dns_message_renderheader(msg, &tmpbuf);
2095
2096	msg->buffer = NULL;  /* forget about this buffer only on success XXX */
2097
2098	return (ISC_R_SUCCESS);
2099}
2100
2101void
2102dns_message_renderreset(dns_message_t *msg) {
2103	unsigned int i;
2104	dns_name_t *name;
2105	dns_rdataset_t *rds;
2106
2107	/*
2108	 * Reset the message so that it may be rendered again.
2109	 */
2110
2111	REQUIRE(DNS_MESSAGE_VALID(msg));
2112	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
2113
2114	msg->buffer = NULL;
2115
2116	for (i = 0; i < DNS_SECTION_MAX; i++) {
2117		msg->cursors[i] = NULL;
2118		msg->counts[i] = 0;
2119		for (name = ISC_LIST_HEAD(msg->sections[i]);
2120		     name != NULL;
2121		     name = ISC_LIST_NEXT(name, link)) {
2122			for (rds = ISC_LIST_HEAD(name->list);
2123			     rds != NULL;
2124			     rds = ISC_LIST_NEXT(rds, link)) {
2125				rds->attributes &= ~DNS_RDATASETATTR_RENDERED;
2126			}
2127		}
2128	}
2129	if (msg->tsigname != NULL)
2130		dns_message_puttempname(msg, &msg->tsigname);
2131	if (msg->tsig != NULL) {
2132		dns_rdataset_disassociate(msg->tsig);
2133		dns_message_puttemprdataset(msg, &msg->tsig);
2134	}
2135	if (msg->sig0 != NULL) {
2136		dns_rdataset_disassociate(msg->sig0);
2137		dns_message_puttemprdataset(msg, &msg->sig0);
2138	}
2139}
2140
2141isc_result_t
2142dns_message_firstname(dns_message_t *msg, dns_section_t section) {
2143	REQUIRE(DNS_MESSAGE_VALID(msg));
2144	REQUIRE(VALID_NAMED_SECTION(section));
2145
2146	msg->cursors[section] = ISC_LIST_HEAD(msg->sections[section]);
2147
2148	if (msg->cursors[section] == NULL)
2149		return (ISC_R_NOMORE);
2150
2151	return (ISC_R_SUCCESS);
2152}
2153
2154isc_result_t
2155dns_message_nextname(dns_message_t *msg, dns_section_t section) {
2156	REQUIRE(DNS_MESSAGE_VALID(msg));
2157	REQUIRE(VALID_NAMED_SECTION(section));
2158	REQUIRE(msg->cursors[section] != NULL);
2159
2160	msg->cursors[section] = ISC_LIST_NEXT(msg->cursors[section], link);
2161
2162	if (msg->cursors[section] == NULL)
2163		return (ISC_R_NOMORE);
2164
2165	return (ISC_R_SUCCESS);
2166}
2167
2168void
2169dns_message_currentname(dns_message_t *msg, dns_section_t section,
2170			dns_name_t **name)
2171{
2172	REQUIRE(DNS_MESSAGE_VALID(msg));
2173	REQUIRE(VALID_NAMED_SECTION(section));
2174	REQUIRE(name != NULL && *name == NULL);
2175	REQUIRE(msg->cursors[section] != NULL);
2176
2177	*name = msg->cursors[section];
2178}
2179
2180isc_result_t
2181dns_message_findname(dns_message_t *msg, dns_section_t section,
2182		     dns_name_t *target, dns_rdatatype_t type,
2183		     dns_rdatatype_t covers, dns_name_t **name,
2184		     dns_rdataset_t **rdataset)
2185{
2186	dns_name_t *foundname;
2187	isc_result_t result;
2188
2189	/*
2190	 * XXX These requirements are probably too intensive, especially
2191	 * where things can be NULL, but as they are they ensure that if
2192	 * something is NON-NULL, indicating that the caller expects it
2193	 * to be filled in, that we can in fact fill it in.
2194	 */
2195	REQUIRE(msg != NULL);
2196	REQUIRE(VALID_SECTION(section));
2197	REQUIRE(target != NULL);
2198	if (name != NULL)
2199		REQUIRE(*name == NULL);
2200	if (type == dns_rdatatype_any) {
2201		REQUIRE(rdataset == NULL);
2202	} else {
2203		if (rdataset != NULL)
2204			REQUIRE(*rdataset == NULL);
2205	}
2206
2207	result = findname(&foundname, target,
2208			  &msg->sections[section]);
2209
2210	if (result == ISC_R_NOTFOUND)
2211		return (DNS_R_NXDOMAIN);
2212	else if (result != ISC_R_SUCCESS)
2213		return (result);
2214
2215	if (name != NULL)
2216		*name = foundname;
2217
2218	/*
2219	 * And now look for the type.
2220	 */
2221	if (type == dns_rdatatype_any)
2222		return (ISC_R_SUCCESS);
2223
2224	result = dns_message_findtype(foundname, type, covers, rdataset);
2225	if (result == ISC_R_NOTFOUND)
2226		return (DNS_R_NXRRSET);
2227
2228	return (result);
2229}
2230
2231void
2232dns_message_movename(dns_message_t *msg, dns_name_t *name,
2233		     dns_section_t fromsection,
2234		     dns_section_t tosection)
2235{
2236	REQUIRE(msg != NULL);
2237	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
2238	REQUIRE(name != NULL);
2239	REQUIRE(VALID_NAMED_SECTION(fromsection));
2240	REQUIRE(VALID_NAMED_SECTION(tosection));
2241
2242	/*
2243	 * Unlink the name from the old section
2244	 */
2245	ISC_LIST_UNLINK(msg->sections[fromsection], name, link);
2246	ISC_LIST_APPEND(msg->sections[tosection], name, link);
2247}
2248
2249void
2250dns_message_addname(dns_message_t *msg, dns_name_t *name,
2251		    dns_section_t section)
2252{
2253	REQUIRE(msg != NULL);
2254	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
2255	REQUIRE(name != NULL);
2256	REQUIRE(VALID_NAMED_SECTION(section));
2257
2258	ISC_LIST_APPEND(msg->sections[section], name, link);
2259}
2260
2261isc_result_t
2262dns_message_gettempname(dns_message_t *msg, dns_name_t **item) {
2263	REQUIRE(DNS_MESSAGE_VALID(msg));
2264	REQUIRE(item != NULL && *item == NULL);
2265
2266	*item = isc_mempool_get(msg->namepool);
2267	if (*item == NULL)
2268		return (ISC_R_NOMEMORY);
2269	dns_name_init(*item, NULL);
2270
2271	return (ISC_R_SUCCESS);
2272}
2273
2274isc_result_t
2275dns_message_gettempoffsets(dns_message_t *msg, dns_offsets_t **item) {
2276	REQUIRE(DNS_MESSAGE_VALID(msg));
2277	REQUIRE(item != NULL && *item == NULL);
2278
2279	*item = newoffsets(msg);
2280	if (*item == NULL)
2281		return (ISC_R_NOMEMORY);
2282
2283	return (ISC_R_SUCCESS);
2284}
2285
2286isc_result_t
2287dns_message_gettemprdata(dns_message_t *msg, dns_rdata_t **item) {
2288	REQUIRE(DNS_MESSAGE_VALID(msg));
2289	REQUIRE(item != NULL && *item == NULL);
2290
2291	*item = newrdata(msg);
2292	if (*item == NULL)
2293		return (ISC_R_NOMEMORY);
2294
2295	return (ISC_R_SUCCESS);
2296}
2297
2298isc_result_t
2299dns_message_gettemprdataset(dns_message_t *msg, dns_rdataset_t **item) {
2300	REQUIRE(DNS_MESSAGE_VALID(msg));
2301	REQUIRE(item != NULL && *item == NULL);
2302
2303	*item = isc_mempool_get(msg->rdspool);
2304	if (*item == NULL)
2305		return (ISC_R_NOMEMORY);
2306
2307	dns_rdataset_init(*item);
2308
2309	return (ISC_R_SUCCESS);
2310}
2311
2312isc_result_t
2313dns_message_gettemprdatalist(dns_message_t *msg, dns_rdatalist_t **item) {
2314	REQUIRE(DNS_MESSAGE_VALID(msg));
2315	REQUIRE(item != NULL && *item == NULL);
2316
2317	*item = newrdatalist(msg);
2318	if (*item == NULL)
2319		return (ISC_R_NOMEMORY);
2320
2321	return (ISC_R_SUCCESS);
2322}
2323
2324void
2325dns_message_puttempname(dns_message_t *msg, dns_name_t **item) {
2326	REQUIRE(DNS_MESSAGE_VALID(msg));
2327	REQUIRE(item != NULL && *item != NULL);
2328
2329	if (dns_name_dynamic(*item))
2330		dns_name_free(*item, msg->mctx);
2331	isc_mempool_put(msg->namepool, *item);
2332	*item = NULL;
2333}
2334
2335void
2336dns_message_puttemprdata(dns_message_t *msg, dns_rdata_t **item) {
2337	REQUIRE(DNS_MESSAGE_VALID(msg));
2338	REQUIRE(item != NULL && *item != NULL);
2339
2340	releaserdata(msg, *item);
2341	*item = NULL;
2342}
2343
2344void
2345dns_message_puttemprdataset(dns_message_t *msg, dns_rdataset_t **item) {
2346	REQUIRE(DNS_MESSAGE_VALID(msg));
2347	REQUIRE(item != NULL && *item != NULL);
2348
2349	REQUIRE(!dns_rdataset_isassociated(*item));
2350	isc_mempool_put(msg->rdspool, *item);
2351	*item = NULL;
2352}
2353
2354void
2355dns_message_puttemprdatalist(dns_message_t *msg, dns_rdatalist_t **item) {
2356	REQUIRE(DNS_MESSAGE_VALID(msg));
2357	REQUIRE(item != NULL && *item != NULL);
2358
2359	releaserdatalist(msg, *item);
2360	*item = NULL;
2361}
2362
2363isc_result_t
2364dns_message_peekheader(isc_buffer_t *source, dns_messageid_t *idp,
2365		       unsigned int *flagsp)
2366{
2367	isc_region_t r;
2368	isc_buffer_t buffer;
2369	dns_messageid_t id;
2370	unsigned int flags;
2371
2372	REQUIRE(source != NULL);
2373
2374	buffer = *source;
2375
2376	isc_buffer_remainingregion(&buffer, &r);
2377	if (r.length < DNS_MESSAGE_HEADERLEN)
2378		return (ISC_R_UNEXPECTEDEND);
2379
2380	id = isc_buffer_getuint16(&buffer);
2381	flags = isc_buffer_getuint16(&buffer);
2382	flags &= DNS_MESSAGE_FLAG_MASK;
2383
2384	if (flagsp != NULL)
2385		*flagsp = flags;
2386	if (idp != NULL)
2387		*idp = id;
2388
2389	return (ISC_R_SUCCESS);
2390}
2391
2392isc_result_t
2393dns_message_reply(dns_message_t *msg, isc_boolean_t want_question_section) {
2394	unsigned int first_section;
2395	isc_result_t result;
2396
2397	REQUIRE(DNS_MESSAGE_VALID(msg));
2398	REQUIRE((msg->flags & DNS_MESSAGEFLAG_QR) == 0);
2399
2400	if (!msg->header_ok)
2401		return (DNS_R_FORMERR);
2402	if (msg->opcode != dns_opcode_query &&
2403	    msg->opcode != dns_opcode_notify)
2404		want_question_section = ISC_FALSE;
2405	if (want_question_section) {
2406		if (!msg->question_ok)
2407			return (DNS_R_FORMERR);
2408		first_section = DNS_SECTION_ANSWER;
2409	} else
2410		first_section = DNS_SECTION_QUESTION;
2411	msg->from_to_wire = DNS_MESSAGE_INTENTRENDER;
2412	msgresetnames(msg, first_section);
2413	msgresetopt(msg);
2414	msgresetsigs(msg, ISC_TRUE);
2415	msginitprivate(msg);
2416	/*
2417	 * We now clear most flags and then set QR, ensuring that the
2418	 * reply's flags will be in a reasonable state.
2419	 */
2420	msg->flags &= DNS_MESSAGE_REPLYPRESERVE;
2421	msg->flags |= DNS_MESSAGEFLAG_QR;
2422
2423	/*
2424	 * This saves the query TSIG status, if the query was signed, and
2425	 * reserves space in the reply for the TSIG.
2426	 */
2427	if (msg->tsigkey != NULL) {
2428		unsigned int otherlen = 0;
2429		msg->querytsigstatus = msg->tsigstatus;
2430		msg->tsigstatus = dns_rcode_noerror;
2431		if (msg->querytsigstatus == dns_tsigerror_badtime)
2432			otherlen = 6;
2433		msg->sig_reserved = spacefortsig(msg->tsigkey, otherlen);
2434		result = dns_message_renderreserve(msg, msg->sig_reserved);
2435		if (result != ISC_R_SUCCESS) {
2436			msg->sig_reserved = 0;
2437			return (result);
2438		}
2439	}
2440	if (msg->saved.base != NULL) {
2441		msg->query.base = msg->saved.base;
2442		msg->query.length = msg->saved.length;
2443		msg->free_query = msg->free_saved;
2444		msg->saved.base = NULL;
2445		msg->saved.length = 0;
2446		msg->free_saved = 0;
2447	}
2448
2449	return (ISC_R_SUCCESS);
2450}
2451
2452dns_rdataset_t *
2453dns_message_getopt(dns_message_t *msg) {
2454
2455	/*
2456	 * Get the OPT record for 'msg'.
2457	 */
2458
2459	REQUIRE(DNS_MESSAGE_VALID(msg));
2460
2461	return (msg->opt);
2462}
2463
2464isc_result_t
2465dns_message_setopt(dns_message_t *msg, dns_rdataset_t *opt) {
2466	isc_result_t result;
2467	dns_rdata_t rdata = DNS_RDATA_INIT;
2468
2469	/*
2470	 * Set the OPT record for 'msg'.
2471	 */
2472
2473	/*
2474	 * The space required for an OPT record is:
2475	 *
2476	 *	1 byte for the name
2477	 *	2 bytes for the type
2478	 *	2 bytes for the class
2479	 *	4 bytes for the ttl
2480	 *	2 bytes for the rdata length
2481	 * ---------------------------------
2482	 *     11 bytes
2483	 *
2484	 * plus the length of the rdata.
2485	 */
2486
2487	REQUIRE(DNS_MESSAGE_VALID(msg));
2488	REQUIRE(opt->type == dns_rdatatype_opt);
2489	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
2490	REQUIRE(msg->state == DNS_SECTION_ANY);
2491
2492	msgresetopt(msg);
2493
2494	result = dns_rdataset_first(opt);
2495	if (result != ISC_R_SUCCESS)
2496		goto cleanup;
2497	dns_rdataset_current(opt, &rdata);
2498	msg->opt_reserved = 11 + rdata.length;
2499	result = dns_message_renderreserve(msg, msg->opt_reserved);
2500	if (result != ISC_R_SUCCESS) {
2501		msg->opt_reserved = 0;
2502		goto cleanup;
2503	}
2504
2505	msg->opt = opt;
2506
2507	return (ISC_R_SUCCESS);
2508
2509 cleanup:
2510	dns_message_puttemprdataset(msg, &opt);
2511	return (result);
2512
2513}
2514
2515dns_rdataset_t *
2516dns_message_gettsig(dns_message_t *msg, dns_name_t **owner) {
2517
2518	/*
2519	 * Get the TSIG record and owner for 'msg'.
2520	 */
2521
2522	REQUIRE(DNS_MESSAGE_VALID(msg));
2523	REQUIRE(owner == NULL || *owner == NULL);
2524
2525	if (owner != NULL)
2526		*owner = msg->tsigname;
2527	return (msg->tsig);
2528}
2529
2530isc_result_t
2531dns_message_settsigkey(dns_message_t *msg, dns_tsigkey_t *key) {
2532	isc_result_t result;
2533
2534	/*
2535	 * Set the TSIG key for 'msg'
2536	 */
2537
2538	REQUIRE(DNS_MESSAGE_VALID(msg));
2539	REQUIRE(msg->state == DNS_SECTION_ANY);
2540
2541	if (key == NULL && msg->tsigkey != NULL) {
2542		if (msg->sig_reserved != 0) {
2543			dns_message_renderrelease(msg, msg->sig_reserved);
2544			msg->sig_reserved = 0;
2545		}
2546		dns_tsigkey_detach(&msg->tsigkey);
2547	}
2548	if (key != NULL) {
2549		REQUIRE(msg->tsigkey == NULL && msg->sig0key == NULL);
2550		dns_tsigkey_attach(key, &msg->tsigkey);
2551		if (msg->from_to_wire == DNS_MESSAGE_INTENTRENDER) {
2552			msg->sig_reserved = spacefortsig(msg->tsigkey, 0);
2553			result = dns_message_renderreserve(msg,
2554							   msg->sig_reserved);
2555			if (result != ISC_R_SUCCESS) {
2556				dns_tsigkey_detach(&msg->tsigkey);
2557				msg->sig_reserved = 0;
2558				return (result);
2559			}
2560		}
2561	}
2562	return (ISC_R_SUCCESS);
2563}
2564
2565dns_tsigkey_t *
2566dns_message_gettsigkey(dns_message_t *msg) {
2567
2568	/*
2569	 * Get the TSIG key for 'msg'
2570	 */
2571
2572	REQUIRE(DNS_MESSAGE_VALID(msg));
2573
2574	return (msg->tsigkey);
2575}
2576
2577isc_result_t
2578dns_message_setquerytsig(dns_message_t *msg, isc_buffer_t *querytsig) {
2579	dns_rdata_t *rdata = NULL;
2580	dns_rdatalist_t *list = NULL;
2581	dns_rdataset_t *set = NULL;
2582	isc_buffer_t *buf = NULL;
2583	isc_region_t r;
2584	isc_result_t result;
2585
2586	REQUIRE(DNS_MESSAGE_VALID(msg));
2587	REQUIRE(msg->querytsig == NULL);
2588
2589	if (querytsig == NULL)
2590		return (ISC_R_SUCCESS);
2591
2592	result = dns_message_gettemprdata(msg, &rdata);
2593	if (result != ISC_R_SUCCESS)
2594		goto cleanup;
2595
2596	result = dns_message_gettemprdatalist(msg, &list);
2597	if (result != ISC_R_SUCCESS)
2598		goto cleanup;
2599	result = dns_message_gettemprdataset(msg, &set);
2600	if (result != ISC_R_SUCCESS)
2601		goto cleanup;
2602
2603	isc_buffer_usedregion(querytsig, &r);
2604	result = isc_buffer_allocate(msg->mctx, &buf, r.length);
2605	if (result != ISC_R_SUCCESS)
2606		goto cleanup;
2607	isc_buffer_putmem(buf, r.base, r.length);
2608	isc_buffer_usedregion(buf, &r);
2609	dns_rdata_init(rdata);
2610	dns_rdata_fromregion(rdata, dns_rdataclass_any, dns_rdatatype_tsig, &r);
2611	dns_message_takebuffer(msg, &buf);
2612	ISC_LIST_INIT(list->rdata);
2613	ISC_LIST_APPEND(list->rdata, rdata, link);
2614	result = dns_rdatalist_tordataset(list, set);
2615	if (result != ISC_R_SUCCESS)
2616		goto cleanup;
2617
2618	msg->querytsig = set;
2619
2620	return (result);
2621
2622 cleanup:
2623	if (rdata != NULL)
2624		dns_message_puttemprdata(msg, &rdata);
2625	if (list != NULL)
2626		dns_message_puttemprdatalist(msg, &list);
2627	if (set != NULL)
2628		dns_message_puttemprdataset(msg, &set);
2629	return (ISC_R_NOMEMORY);
2630}
2631
2632isc_result_t
2633dns_message_getquerytsig(dns_message_t *msg, isc_mem_t *mctx,
2634			 isc_buffer_t **querytsig) {
2635	isc_result_t result;
2636	dns_rdata_t rdata = DNS_RDATA_INIT;
2637	isc_region_t r;
2638
2639	REQUIRE(DNS_MESSAGE_VALID(msg));
2640	REQUIRE(mctx != NULL);
2641	REQUIRE(querytsig != NULL && *querytsig == NULL);
2642
2643	if (msg->tsig == NULL)
2644		return (ISC_R_SUCCESS);
2645
2646	result = dns_rdataset_first(msg->tsig);
2647	if (result != ISC_R_SUCCESS)
2648		return (result);
2649	dns_rdataset_current(msg->tsig, &rdata);
2650	dns_rdata_toregion(&rdata, &r);
2651
2652	result = isc_buffer_allocate(mctx, querytsig, r.length);
2653	if (result != ISC_R_SUCCESS)
2654		return (result);
2655	isc_buffer_putmem(*querytsig, r.base, r.length);
2656	return (ISC_R_SUCCESS);
2657}
2658
2659dns_rdataset_t *
2660dns_message_getsig0(dns_message_t *msg, dns_name_t **owner) {
2661
2662	/*
2663	 * Get the SIG(0) record for 'msg'.
2664	 */
2665
2666	REQUIRE(DNS_MESSAGE_VALID(msg));
2667	REQUIRE(owner == NULL || *owner == NULL);
2668
2669	if (msg->sig0 != NULL && owner != NULL) {
2670		/* If dns_message_getsig0 is called on a rendered message
2671		 * after the SIG(0) has been applied, we need to return the
2672		 * root name, not NULL.
2673		 */
2674		if (msg->sig0name == NULL)
2675			*owner = dns_rootname;
2676		else
2677			*owner = msg->sig0name;
2678	}
2679	return (msg->sig0);
2680}
2681
2682isc_result_t
2683dns_message_setsig0key(dns_message_t *msg, dst_key_t *key) {
2684	isc_region_t r;
2685	unsigned int x;
2686	isc_result_t result;
2687
2688	/*
2689	 * Set the SIG(0) key for 'msg'
2690	 */
2691
2692	/*
2693	 * The space required for an SIG(0) record is:
2694	 *
2695	 *	1 byte for the name
2696	 *	2 bytes for the type
2697	 *	2 bytes for the class
2698	 *	4 bytes for the ttl
2699	 *	2 bytes for the type covered
2700	 *	1 byte for the algorithm
2701	 *	1 bytes for the labels
2702	 *	4 bytes for the original ttl
2703	 *	4 bytes for the signature expiration
2704	 *	4 bytes for the signature inception
2705	 *	2 bytes for the key tag
2706	 *	n bytes for the signer's name
2707	 *	x bytes for the signature
2708	 * ---------------------------------
2709	 *     27 + n + x bytes
2710	 */
2711	REQUIRE(DNS_MESSAGE_VALID(msg));
2712	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
2713	REQUIRE(msg->state == DNS_SECTION_ANY);
2714
2715	if (key != NULL) {
2716		REQUIRE(msg->sig0key == NULL && msg->tsigkey == NULL);
2717		dns_name_toregion(dst_key_name(key), &r);
2718		result = dst_key_sigsize(key, &x);
2719		if (result != ISC_R_SUCCESS) {
2720			msg->sig_reserved = 0;
2721			return (result);
2722		}
2723		msg->sig_reserved = 27 + r.length + x;
2724		result = dns_message_renderreserve(msg, msg->sig_reserved);
2725		if (result != ISC_R_SUCCESS) {
2726			msg->sig_reserved = 0;
2727			return (result);
2728		}
2729		msg->sig0key = key;
2730	}
2731	return (ISC_R_SUCCESS);
2732}
2733
2734dst_key_t *
2735dns_message_getsig0key(dns_message_t *msg) {
2736
2737	/*
2738	 * Get the SIG(0) key for 'msg'
2739	 */
2740
2741	REQUIRE(DNS_MESSAGE_VALID(msg));
2742
2743	return (msg->sig0key);
2744}
2745
2746void
2747dns_message_takebuffer(dns_message_t *msg, isc_buffer_t **buffer) {
2748	REQUIRE(DNS_MESSAGE_VALID(msg));
2749	REQUIRE(buffer != NULL);
2750	REQUIRE(ISC_BUFFER_VALID(*buffer));
2751
2752	ISC_LIST_APPEND(msg->cleanup, *buffer, link);
2753	*buffer = NULL;
2754}
2755
2756isc_result_t
2757dns_message_signer(dns_message_t *msg, dns_name_t *signer) {
2758	isc_result_t result = ISC_R_SUCCESS;
2759	dns_rdata_t rdata = DNS_RDATA_INIT;
2760
2761	REQUIRE(DNS_MESSAGE_VALID(msg));
2762	REQUIRE(signer != NULL);
2763	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTPARSE);
2764
2765	if (msg->tsig == NULL && msg->sig0 == NULL)
2766		return (ISC_R_NOTFOUND);
2767
2768	if (msg->verify_attempted == 0)
2769		return (DNS_R_NOTVERIFIEDYET);
2770
2771	if (!dns_name_hasbuffer(signer)) {
2772		isc_buffer_t *dynbuf = NULL;
2773		result = isc_buffer_allocate(msg->mctx, &dynbuf, 512);
2774		if (result != ISC_R_SUCCESS)
2775			return (result);
2776		dns_name_setbuffer(signer, dynbuf);
2777		dns_message_takebuffer(msg, &dynbuf);
2778	}
2779
2780	if (msg->sig0 != NULL) {
2781		dns_rdata_sig_t sig;
2782
2783		result = dns_rdataset_first(msg->sig0);
2784		INSIST(result == ISC_R_SUCCESS);
2785		dns_rdataset_current(msg->sig0, &rdata);
2786
2787		result = dns_rdata_tostruct(&rdata, &sig, NULL);
2788		if (result != ISC_R_SUCCESS)
2789			return (result);
2790
2791		if (msg->verified_sig && msg->sig0status == dns_rcode_noerror)
2792			result = ISC_R_SUCCESS;
2793		else
2794			result = DNS_R_SIGINVALID;
2795		dns_name_clone(&sig.signer, signer);
2796		dns_rdata_freestruct(&sig);
2797	} else {
2798		dns_name_t *identity;
2799		dns_rdata_any_tsig_t tsig;
2800
2801		result = dns_rdataset_first(msg->tsig);
2802		INSIST(result == ISC_R_SUCCESS);
2803		dns_rdataset_current(msg->tsig, &rdata);
2804
2805		result = dns_rdata_tostruct(&rdata, &tsig, NULL);
2806		if (msg->tsigstatus != dns_rcode_noerror)
2807			result = DNS_R_TSIGVERIFYFAILURE;
2808		else if (tsig.error != dns_rcode_noerror)
2809			result = DNS_R_TSIGERRORSET;
2810		else
2811			result = ISC_R_SUCCESS;
2812		dns_rdata_freestruct(&tsig);
2813
2814		if (msg->tsigkey == NULL) {
2815			/*
2816			 * If msg->tsigstatus & tsig.error are both
2817			 * dns_rcode_noerror, the message must have been
2818			 * verified, which means msg->tsigkey will be
2819			 * non-NULL.
2820			 */
2821			INSIST(result != ISC_R_SUCCESS);
2822		} else {
2823			identity = dns_tsigkey_identity(msg->tsigkey);
2824			if (identity == NULL) {
2825				if (result == ISC_R_SUCCESS)
2826					result = DNS_R_NOIDENTITY;
2827				identity = &msg->tsigkey->name;
2828			}
2829			dns_name_clone(identity, signer);
2830		}
2831	}
2832
2833	return (result);
2834}
2835
2836void
2837dns_message_resetsig(dns_message_t *msg) {
2838	REQUIRE(DNS_MESSAGE_VALID(msg));
2839	msg->verified_sig = 0;
2840	msg->verify_attempted = 0;
2841	msg->tsigstatus = dns_rcode_noerror;
2842	msg->sig0status = dns_rcode_noerror;
2843	msg->timeadjust = 0;
2844	if (msg->tsigkey != NULL) {
2845		dns_tsigkey_detach(&msg->tsigkey);
2846		msg->tsigkey = NULL;
2847	}
2848}
2849
2850isc_result_t
2851dns_message_rechecksig(dns_message_t *msg, dns_view_t *view) {
2852	dns_message_resetsig(msg);
2853	return (dns_message_checksig(msg, view));
2854}
2855
2856isc_result_t
2857dns_message_checksig(dns_message_t *msg, dns_view_t *view) {
2858	isc_buffer_t b, msgb;
2859
2860	REQUIRE(DNS_MESSAGE_VALID(msg));
2861
2862	if (msg->tsigkey == NULL && msg->tsig == NULL && msg->sig0 == NULL)
2863		return (ISC_R_SUCCESS);
2864	INSIST(msg->saved.base != NULL);
2865	isc_buffer_init(&msgb, msg->saved.base, msg->saved.length);
2866	isc_buffer_add(&msgb, msg->saved.length);
2867	if (msg->tsigkey != NULL || msg->tsig != NULL) {
2868		if (view != NULL)
2869			return (dns_view_checksig(view, &msgb, msg));
2870		else
2871			return (dns_tsig_verify(&msgb, msg, NULL, NULL));
2872	} else {
2873		dns_rdata_t rdata = DNS_RDATA_INIT;
2874		dns_rdata_sig_t sig;
2875		dns_rdataset_t keyset;
2876		isc_result_t result;
2877
2878		result = dns_rdataset_first(msg->sig0);
2879		INSIST(result == ISC_R_SUCCESS);
2880		dns_rdataset_current(msg->sig0, &rdata);
2881
2882		/*
2883		 * This can occur when the message is a dynamic update, since
2884		 * the rdata length checking is relaxed.  This should not
2885		 * happen in a well-formed message, since the SIG(0) is only
2886		 * looked for in the additional section, and the dynamic update
2887		 * meta-records are in the prerequisite and update sections.
2888		 */
2889		if (rdata.length == 0)
2890			return (ISC_R_UNEXPECTEDEND);
2891
2892		result = dns_rdata_tostruct(&rdata, &sig, msg->mctx);
2893		if (result != ISC_R_SUCCESS)
2894			return (result);
2895
2896		dns_rdataset_init(&keyset);
2897		if (view == NULL)
2898			return (DNS_R_KEYUNAUTHORIZED);
2899		result = dns_view_simplefind(view, &sig.signer,
2900					     dns_rdatatype_key /* SIG(0) */,
2901					     0, 0, ISC_FALSE, &keyset, NULL);
2902
2903		if (result != ISC_R_SUCCESS) {
2904			/* XXXBEW Should possibly create a fetch here */
2905			result = DNS_R_KEYUNAUTHORIZED;
2906			goto freesig;
2907		} else if (keyset.trust < dns_trust_secure) {
2908			/* XXXBEW Should call a validator here */
2909			result = DNS_R_KEYUNAUTHORIZED;
2910			goto freesig;
2911		}
2912		result = dns_rdataset_first(&keyset);
2913		INSIST(result == ISC_R_SUCCESS);
2914		for (;
2915		     result == ISC_R_SUCCESS;
2916		     result = dns_rdataset_next(&keyset))
2917		{
2918			dst_key_t *key = NULL;
2919
2920			dns_rdataset_current(&keyset, &rdata);
2921			isc_buffer_init(&b, rdata.data, rdata.length);
2922			isc_buffer_add(&b, rdata.length);
2923
2924			result = dst_key_fromdns(&sig.signer, rdata.rdclass,
2925						 &b, view->mctx, &key);
2926			if (result != ISC_R_SUCCESS)
2927				continue;
2928			if (dst_key_alg(key) != sig.algorithm ||
2929			    dst_key_id(key) != sig.keyid ||
2930			    !(dst_key_proto(key) == DNS_KEYPROTO_DNSSEC ||
2931			      dst_key_proto(key) == DNS_KEYPROTO_ANY))
2932			{
2933				dst_key_free(&key);
2934				continue;
2935			}
2936			result = dns_dnssec_verifymessage(&msgb, msg, key);
2937			dst_key_free(&key);
2938			if (result == ISC_R_SUCCESS)
2939				break;
2940		}
2941		if (result == ISC_R_NOMORE)
2942			result = DNS_R_KEYUNAUTHORIZED;
2943
2944 freesig:
2945		if (dns_rdataset_isassociated(&keyset))
2946			dns_rdataset_disassociate(&keyset);
2947		dns_rdata_freestruct(&sig);
2948		return (result);
2949	}
2950}
2951
2952isc_result_t
2953dns_message_sectiontotext(dns_message_t *msg, dns_section_t section,
2954			  const dns_master_style_t *style,
2955			  dns_messagetextflag_t flags,
2956			  isc_buffer_t *target) {
2957	dns_name_t *name, empty_name;
2958	dns_rdataset_t *rdataset;
2959	isc_result_t result;
2960
2961	REQUIRE(DNS_MESSAGE_VALID(msg));
2962	REQUIRE(target != NULL);
2963	REQUIRE(VALID_SECTION(section));
2964
2965	if (ISC_LIST_EMPTY(msg->sections[section]))
2966		return (ISC_R_SUCCESS);
2967
2968	if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) {
2969		ADD_STRING(target, ";; ");
2970		if (msg->opcode != dns_opcode_update) {
2971			ADD_STRING(target, sectiontext[section]);
2972		}
2973		else {
2974			ADD_STRING(target, updsectiontext[section]);
2975		}
2976		ADD_STRING(target, " SECTION:\n");
2977	}
2978
2979	dns_name_init(&empty_name, NULL);
2980	result = dns_message_firstname(msg, section);
2981	if (result != ISC_R_SUCCESS) {
2982		return (result);
2983	}
2984	do {
2985		name = NULL;
2986		dns_message_currentname(msg, section, &name);
2987		for (rdataset = ISC_LIST_HEAD(name->list);
2988		     rdataset != NULL;
2989		     rdataset = ISC_LIST_NEXT(rdataset, link)) {
2990			if (section == DNS_SECTION_QUESTION) {
2991				ADD_STRING(target, ";");
2992				result = dns_master_questiontotext(name,
2993								   rdataset,
2994								   style,
2995								   target);
2996			} else {
2997				result = dns_master_rdatasettotext(name,
2998								   rdataset,
2999								   style,
3000								   target);
3001			}
3002			if (result != ISC_R_SUCCESS)
3003				return (result);
3004		}
3005		result = dns_message_nextname(msg, section);
3006	} while (result == ISC_R_SUCCESS);
3007	if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0 &&
3008	    (flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
3009		ADD_STRING(target, "\n");
3010	if (result == ISC_R_NOMORE)
3011		result = ISC_R_SUCCESS;
3012	return (result);
3013}
3014
3015isc_result_t
3016dns_message_pseudosectiontotext(dns_message_t *msg,
3017				dns_pseudosection_t section,
3018				const dns_master_style_t *style,
3019				dns_messagetextflag_t flags,
3020				isc_buffer_t *target) {
3021	dns_rdataset_t *ps = NULL;
3022	dns_name_t *name = NULL;
3023	isc_result_t result;
3024	char buf[sizeof("1234567890")];
3025	isc_uint32_t mbz;
3026
3027	REQUIRE(DNS_MESSAGE_VALID(msg));
3028	REQUIRE(target != NULL);
3029	REQUIRE(VALID_PSEUDOSECTION(section));
3030
3031	switch (section) {
3032	case DNS_PSEUDOSECTION_OPT:
3033		ps = dns_message_getopt(msg);
3034		if (ps == NULL)
3035			return (ISC_R_SUCCESS);
3036		if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
3037			ADD_STRING(target, ";; OPT PSEUDOSECTION:\n");
3038		ADD_STRING(target, "; EDNS: version: ");
3039		snprintf(buf, sizeof(buf), "%u",
3040			 (unsigned int)((ps->ttl & 0x00ff0000) >> 16));
3041		ADD_STRING(target, buf);
3042		ADD_STRING(target, ", flags:");
3043		if ((ps->ttl & DNS_MESSAGEEXTFLAG_DO) != 0)
3044			ADD_STRING(target, " do");
3045		mbz = ps->ttl & ~DNS_MESSAGEEXTFLAG_DO & 0xffff;
3046		if (mbz != 0) {
3047			ADD_STRING(target, "; MBZ: ");
3048			snprintf(buf, sizeof(buf), "%.4x ", mbz);
3049			ADD_STRING(target, buf);
3050			ADD_STRING(target, ", udp: ");
3051		} else
3052			ADD_STRING(target, "; udp: ");
3053		snprintf(buf, sizeof(buf), "%u\n", (unsigned int)ps->rdclass);
3054		ADD_STRING(target, buf);
3055		return (ISC_R_SUCCESS);
3056	case DNS_PSEUDOSECTION_TSIG:
3057		ps = dns_message_gettsig(msg, &name);
3058		if (ps == NULL)
3059			return (ISC_R_SUCCESS);
3060		if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
3061			ADD_STRING(target, ";; TSIG PSEUDOSECTION:\n");
3062		result = dns_master_rdatasettotext(name, ps, style, target);
3063		if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0 &&
3064		    (flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
3065			ADD_STRING(target, "\n");
3066		return (result);
3067	case DNS_PSEUDOSECTION_SIG0:
3068		ps = dns_message_getsig0(msg, &name);
3069		if (ps == NULL)
3070			return (ISC_R_SUCCESS);
3071		if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
3072			ADD_STRING(target, ";; SIG0 PSEUDOSECTION:\n");
3073		result = dns_master_rdatasettotext(name, ps, style, target);
3074		if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0 &&
3075		    (flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
3076			ADD_STRING(target, "\n");
3077		return (result);
3078	}
3079	return (ISC_R_UNEXPECTED);
3080}
3081
3082isc_result_t
3083dns_message_totext(dns_message_t *msg, const dns_master_style_t *style,
3084		   dns_messagetextflag_t flags, isc_buffer_t *target) {
3085	char buf[sizeof("1234567890")];
3086	isc_result_t result;
3087
3088	REQUIRE(DNS_MESSAGE_VALID(msg));
3089	REQUIRE(target != NULL);
3090
3091	if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0) {
3092		ADD_STRING(target, ";; ->>HEADER<<- opcode: ");
3093		ADD_STRING(target, opcodetext[msg->opcode]);
3094		ADD_STRING(target, ", status: ");
3095		ADD_STRING(target, rcodetext[msg->rcode]);
3096		ADD_STRING(target, ", id: ");
3097		snprintf(buf, sizeof(buf), "%6u", msg->id);
3098		ADD_STRING(target, buf);
3099		ADD_STRING(target, "\n;; flags: ");
3100		if ((msg->flags & DNS_MESSAGEFLAG_QR) != 0)
3101			ADD_STRING(target, "qr ");
3102		if ((msg->flags & DNS_MESSAGEFLAG_AA) != 0)
3103			ADD_STRING(target, "aa ");
3104		if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0)
3105			ADD_STRING(target, "tc ");
3106		if ((msg->flags & DNS_MESSAGEFLAG_RD) != 0)
3107			ADD_STRING(target, "rd ");
3108		if ((msg->flags & DNS_MESSAGEFLAG_RA) != 0)
3109			ADD_STRING(target, "ra ");
3110		if ((msg->flags & DNS_MESSAGEFLAG_AD) != 0)
3111			ADD_STRING(target, "ad ");
3112		if ((msg->flags & DNS_MESSAGEFLAG_CD) != 0)
3113			ADD_STRING(target, "cd ");
3114		if (msg->opcode != dns_opcode_update) {
3115			ADD_STRING(target, "; QUESTION: ");
3116		} else {
3117			ADD_STRING(target, "; ZONE: ");
3118		}
3119		snprintf(buf, sizeof(buf), "%1u",
3120			 msg->counts[DNS_SECTION_QUESTION]);
3121		ADD_STRING(target, buf);
3122		if (msg->opcode != dns_opcode_update) {
3123			ADD_STRING(target, ", ANSWER: ");
3124		} else {
3125			ADD_STRING(target, ", PREREQ: ");
3126		}
3127		snprintf(buf, sizeof(buf), "%1u",
3128			 msg->counts[DNS_SECTION_ANSWER]);
3129		ADD_STRING(target, buf);
3130		if (msg->opcode != dns_opcode_update) {
3131			ADD_STRING(target, ", AUTHORITY: ");
3132		} else {
3133			ADD_STRING(target, ", UPDATE: ");
3134		}
3135		snprintf(buf, sizeof(buf), "%1u",
3136			msg->counts[DNS_SECTION_AUTHORITY]);
3137		ADD_STRING(target, buf);
3138		ADD_STRING(target, ", ADDITIONAL: ");
3139		snprintf(buf, sizeof(buf), "%1u",
3140			msg->counts[DNS_SECTION_ADDITIONAL]);
3141		ADD_STRING(target, buf);
3142		ADD_STRING(target, "\n");
3143	}
3144	result = dns_message_pseudosectiontotext(msg,
3145						 DNS_PSEUDOSECTION_OPT,
3146						 style, flags, target);
3147	if (result != ISC_R_SUCCESS)
3148		return (result);
3149
3150	result = dns_message_sectiontotext(msg, DNS_SECTION_QUESTION,
3151					   style, flags, target);
3152	if (result != ISC_R_SUCCESS)
3153		return (result);
3154	result = dns_message_sectiontotext(msg, DNS_SECTION_ANSWER,
3155					   style, flags, target);
3156	if (result != ISC_R_SUCCESS)
3157		return (result);
3158	result = dns_message_sectiontotext(msg, DNS_SECTION_AUTHORITY,
3159					   style, flags, target);
3160	if (result != ISC_R_SUCCESS)
3161		return (result);
3162	result = dns_message_sectiontotext(msg, DNS_SECTION_ADDITIONAL,
3163					   style, flags, target);
3164	if (result != ISC_R_SUCCESS)
3165		return (result);
3166
3167	result = dns_message_pseudosectiontotext(msg,
3168						 DNS_PSEUDOSECTION_TSIG,
3169						 style, flags, target);
3170	if (result != ISC_R_SUCCESS)
3171		return (result);
3172
3173	result = dns_message_pseudosectiontotext(msg,
3174						 DNS_PSEUDOSECTION_SIG0,
3175						 style, flags, target);
3176	if (result != ISC_R_SUCCESS)
3177		return (result);
3178
3179	return (ISC_R_SUCCESS);
3180}
3181
3182isc_region_t *
3183dns_message_getrawmessage(dns_message_t *msg) {
3184	REQUIRE(DNS_MESSAGE_VALID(msg));
3185	return (&msg->saved);
3186}
3187
3188void
3189dns_message_setsortorder(dns_message_t *msg, dns_rdatasetorderfunc_t order,
3190			 void *order_arg)
3191{
3192	REQUIRE(DNS_MESSAGE_VALID(msg));
3193	msg->order = order;
3194	msg->order_arg = order_arg;
3195}
3196
3197void
3198dns_message_settimeadjust(dns_message_t *msg, int timeadjust) {
3199	REQUIRE(DNS_MESSAGE_VALID(msg));
3200	msg->timeadjust = timeadjust;
3201}
3202
3203int
3204dns_message_gettimeadjust(dns_message_t *msg) {
3205	REQUIRE(DNS_MESSAGE_VALID(msg));
3206	return (msg->timeadjust);
3207}
3208
3209isc_result_t
3210dns_opcode_totext(dns_opcode_t opcode, isc_buffer_t *target) {
3211
3212	REQUIRE(opcode < 16);
3213
3214	if (isc_buffer_availablelength(target) < strlen(opcodetext[opcode]))
3215		return (ISC_R_NOSPACE);
3216	isc_buffer_putstr(target, opcodetext[opcode]);
3217	return (ISC_R_SUCCESS);
3218}
3219