1135446Strhodes/*
2262706Serwin * Copyright (C) 2004-2014  Internet Systems Consortium, Inc. ("ISC")
3135446Strhodes * Copyright (C) 1999-2003  Internet Software Consortium.
4135446Strhodes *
5174187Sdougb * Permission to use, copy, modify, and/or distribute this software for any
6135446Strhodes * purpose with or without fee is hereby granted, provided that the above
7135446Strhodes * copyright notice and this permission notice appear in all copies.
8135446Strhodes *
9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11135446Strhodes * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15135446Strhodes * PERFORMANCE OF THIS SOFTWARE.
16135446Strhodes */
17135446Strhodes
18234010Sdougb/* $Id$ */
19135446Strhodes
20170222Sdougb/*! \file */
21170222Sdougb
22135446Strhodes/***
23135446Strhodes *** Imports
24135446Strhodes ***/
25135446Strhodes
26135446Strhodes#include <config.h>
27193149Sdougb#include <ctype.h>
28135446Strhodes
29135446Strhodes#include <isc/buffer.h>
30135446Strhodes#include <isc/mem.h>
31135446Strhodes#include <isc/print.h>
32135446Strhodes#include <isc/string.h>		/* Required for HP/UX (and others?) */
33135446Strhodes#include <isc/util.h>
34135446Strhodes
35135446Strhodes#include <dns/dnssec.h>
36135446Strhodes#include <dns/keyvalues.h>
37135446Strhodes#include <dns/log.h>
38135446Strhodes#include <dns/masterdump.h>
39135446Strhodes#include <dns/message.h>
40135446Strhodes#include <dns/opcode.h>
41135446Strhodes#include <dns/rdata.h>
42135446Strhodes#include <dns/rdatalist.h>
43135446Strhodes#include <dns/rdataset.h>
44135446Strhodes#include <dns/rdatastruct.h>
45135446Strhodes#include <dns/result.h>
46135446Strhodes#include <dns/tsig.h>
47135446Strhodes#include <dns/view.h>
48135446Strhodes
49193149Sdougb#ifdef SKAN_MSG_DEBUG
50193149Sdougbstatic void
51193149Sdougbhexdump(const char *msg, const char *msg2, void *base, size_t len) {
52193149Sdougb	unsigned char *p;
53193149Sdougb	unsigned int cnt;
54193149Sdougb
55193149Sdougb	p = base;
56193149Sdougb	cnt = 0;
57193149Sdougb
58193149Sdougb	printf("*** %s [%s] (%u bytes @ %p)\n", msg, msg2, len, base);
59193149Sdougb
60193149Sdougb	while (cnt < len) {
61193149Sdougb		if (cnt % 16 == 0)
62193149Sdougb			printf("%p: ", p);
63193149Sdougb		else if (cnt % 8 == 0)
64193149Sdougb			printf(" |");
65193149Sdougb		printf(" %02x %c", *p, (isprint(*p) ? *p : ' '));
66193149Sdougb		p++;
67193149Sdougb		cnt++;
68193149Sdougb
69193149Sdougb		if (cnt % 16 == 0)
70193149Sdougb			printf("\n");
71193149Sdougb	}
72193149Sdougb
73193149Sdougb	if (cnt % 16 != 0)
74193149Sdougb		printf("\n");
75193149Sdougb}
76193149Sdougb#endif
77193149Sdougb
78135446Strhodes#define DNS_MESSAGE_OPCODE_MASK		0x7800U
79135446Strhodes#define DNS_MESSAGE_OPCODE_SHIFT	11
80135446Strhodes#define DNS_MESSAGE_RCODE_MASK		0x000fU
81135446Strhodes#define DNS_MESSAGE_FLAG_MASK		0x8ff0U
82135446Strhodes#define DNS_MESSAGE_EDNSRCODE_MASK	0xff000000U
83135446Strhodes#define DNS_MESSAGE_EDNSRCODE_SHIFT	24
84135446Strhodes#define DNS_MESSAGE_EDNSVERSION_MASK	0x00ff0000U
85135446Strhodes#define DNS_MESSAGE_EDNSVERSION_SHIFT	16
86135446Strhodes
87135446Strhodes#define VALID_NAMED_SECTION(s)  (((s) > DNS_SECTION_ANY) \
88135446Strhodes				 && ((s) < DNS_SECTION_MAX))
89135446Strhodes#define VALID_SECTION(s)	(((s) >= DNS_SECTION_ANY) \
90135446Strhodes				 && ((s) < DNS_SECTION_MAX))
91135446Strhodes#define ADD_STRING(b, s)	{if (strlen(s) >= \
92135446Strhodes				   isc_buffer_availablelength(b)) \
93135446Strhodes				       return(ISC_R_NOSPACE); else \
94135446Strhodes				       isc_buffer_putstr(b, s);}
95135446Strhodes#define VALID_PSEUDOSECTION(s)	(((s) >= DNS_PSEUDOSECTION_ANY) \
96135446Strhodes				 && ((s) < DNS_PSEUDOSECTION_MAX))
97135446Strhodes
98193149Sdougb#define OPTOUT(x) (((x)->attributes & DNS_RDATASETATTR_OPTOUT) != 0)
99193149Sdougb
100170222Sdougb/*%
101135446Strhodes * This is the size of each individual scratchpad buffer, and the numbers
102135446Strhodes * of various block allocations used within the server.
103135446Strhodes * XXXMLG These should come from a config setting.
104135446Strhodes */
105135446Strhodes#define SCRATCHPAD_SIZE		512
106135446Strhodes#define NAME_COUNT		  8
107135446Strhodes#define OFFSET_COUNT		  4
108135446Strhodes#define RDATA_COUNT		  8
109135446Strhodes#define RDATALIST_COUNT		  8
110135446Strhodes#define RDATASET_COUNT		 RDATALIST_COUNT
111135446Strhodes
112170222Sdougb/*%
113135446Strhodes * Text representation of the different items, for message_totext
114135446Strhodes * functions.
115135446Strhodes */
116135446Strhodesstatic const char *sectiontext[] = {
117135446Strhodes	"QUESTION",
118135446Strhodes	"ANSWER",
119135446Strhodes	"AUTHORITY",
120135446Strhodes	"ADDITIONAL"
121135446Strhodes};
122135446Strhodes
123135446Strhodesstatic const char *updsectiontext[] = {
124135446Strhodes	"ZONE",
125135446Strhodes	"PREREQUISITE",
126135446Strhodes	"UPDATE",
127135446Strhodes	"ADDITIONAL"
128135446Strhodes};
129135446Strhodes
130135446Strhodesstatic const char *opcodetext[] = {
131135446Strhodes	"QUERY",
132135446Strhodes	"IQUERY",
133135446Strhodes	"STATUS",
134135446Strhodes	"RESERVED3",
135135446Strhodes	"NOTIFY",
136135446Strhodes	"UPDATE",
137135446Strhodes	"RESERVED6",
138135446Strhodes	"RESERVED7",
139135446Strhodes	"RESERVED8",
140135446Strhodes	"RESERVED9",
141135446Strhodes	"RESERVED10",
142135446Strhodes	"RESERVED11",
143135446Strhodes	"RESERVED12",
144135446Strhodes	"RESERVED13",
145135446Strhodes	"RESERVED14",
146135446Strhodes	"RESERVED15"
147135446Strhodes};
148135446Strhodes
149135446Strhodesstatic const char *rcodetext[] = {
150135446Strhodes	"NOERROR",
151135446Strhodes	"FORMERR",
152135446Strhodes	"SERVFAIL",
153135446Strhodes	"NXDOMAIN",
154135446Strhodes	"NOTIMP",
155135446Strhodes	"REFUSED",
156135446Strhodes	"YXDOMAIN",
157135446Strhodes	"YXRRSET",
158135446Strhodes	"NXRRSET",
159135446Strhodes	"NOTAUTH",
160135446Strhodes	"NOTZONE",
161135446Strhodes	"RESERVED11",
162135446Strhodes	"RESERVED12",
163135446Strhodes	"RESERVED13",
164135446Strhodes	"RESERVED14",
165135446Strhodes	"RESERVED15",
166135446Strhodes	"BADVERS"
167135446Strhodes};
168135446Strhodes
169135446Strhodes
170170222Sdougb/*%
171135446Strhodes * "helper" type, which consists of a block of some type, and is linkable.
172135446Strhodes * For it to work, sizeof(dns_msgblock_t) must be a multiple of the pointer
173193149Sdougb * size, or the allocated elements will not be aligned correctly.
174135446Strhodes */
175135446Strhodesstruct dns_msgblock {
176135446Strhodes	unsigned int			count;
177135446Strhodes	unsigned int			remaining;
178135446Strhodes	ISC_LINK(dns_msgblock_t)	link;
179135446Strhodes}; /* dynamically sized */
180135446Strhodes
181135446Strhodesstatic inline dns_msgblock_t *
182135446Strhodesmsgblock_allocate(isc_mem_t *, unsigned int, unsigned int);
183135446Strhodes
184135446Strhodes#define msgblock_get(block, type) \
185135446Strhodes	((type *)msgblock_internalget(block, sizeof(type)))
186135446Strhodes
187135446Strhodesstatic inline void *
188135446Strhodesmsgblock_internalget(dns_msgblock_t *, unsigned int);
189135446Strhodes
190135446Strhodesstatic inline void
191135446Strhodesmsgblock_reset(dns_msgblock_t *);
192135446Strhodes
193135446Strhodesstatic inline void
194135446Strhodesmsgblock_free(isc_mem_t *, dns_msgblock_t *, unsigned int);
195135446Strhodes
196135446Strhodes/*
197135446Strhodes * Allocate a new dns_msgblock_t, and return a pointer to it.  If no memory
198135446Strhodes * is free, return NULL.
199135446Strhodes */
200135446Strhodesstatic inline dns_msgblock_t *
201135446Strhodesmsgblock_allocate(isc_mem_t *mctx, unsigned int sizeof_type,
202135446Strhodes		  unsigned int count)
203135446Strhodes{
204135446Strhodes	dns_msgblock_t *block;
205135446Strhodes	unsigned int length;
206135446Strhodes
207135446Strhodes	length = sizeof(dns_msgblock_t) + (sizeof_type * count);
208135446Strhodes
209135446Strhodes	block = isc_mem_get(mctx, length);
210135446Strhodes	if (block == NULL)
211135446Strhodes		return (NULL);
212135446Strhodes
213135446Strhodes	block->count = count;
214135446Strhodes	block->remaining = count;
215135446Strhodes
216135446Strhodes	ISC_LINK_INIT(block, link);
217135446Strhodes
218135446Strhodes	return (block);
219135446Strhodes}
220135446Strhodes
221135446Strhodes/*
222135446Strhodes * Return an element from the msgblock.  If no more are available, return
223135446Strhodes * NULL.
224135446Strhodes */
225135446Strhodesstatic inline void *
226135446Strhodesmsgblock_internalget(dns_msgblock_t *block, unsigned int sizeof_type) {
227135446Strhodes	void *ptr;
228135446Strhodes
229135446Strhodes	if (block == NULL || block->remaining == 0)
230135446Strhodes		return (NULL);
231135446Strhodes
232135446Strhodes	block->remaining--;
233135446Strhodes
234135446Strhodes	ptr = (((unsigned char *)block)
235135446Strhodes	       + sizeof(dns_msgblock_t)
236135446Strhodes	       + (sizeof_type * block->remaining));
237135446Strhodes
238135446Strhodes	return (ptr);
239135446Strhodes}
240135446Strhodes
241135446Strhodesstatic inline void
242135446Strhodesmsgblock_reset(dns_msgblock_t *block) {
243135446Strhodes	block->remaining = block->count;
244135446Strhodes}
245135446Strhodes
246135446Strhodes/*
247135446Strhodes * Release memory associated with a message block.
248135446Strhodes */
249135446Strhodesstatic inline void
250135446Strhodesmsgblock_free(isc_mem_t *mctx, dns_msgblock_t *block, unsigned int sizeof_type)
251135446Strhodes{
252135446Strhodes	unsigned int length;
253135446Strhodes
254135446Strhodes	length = sizeof(dns_msgblock_t) + (sizeof_type * block->count);
255135446Strhodes
256135446Strhodes	isc_mem_put(mctx, block, length);
257135446Strhodes}
258135446Strhodes
259135446Strhodes/*
260135446Strhodes * Allocate a new dynamic buffer, and attach it to this message as the
261135446Strhodes * "current" buffer.  (which is always the last on the list, for our
262135446Strhodes * uses)
263135446Strhodes */
264135446Strhodesstatic inline isc_result_t
265135446Strhodesnewbuffer(dns_message_t *msg, unsigned int size) {
266135446Strhodes	isc_result_t result;
267135446Strhodes	isc_buffer_t *dynbuf;
268135446Strhodes
269135446Strhodes	dynbuf = NULL;
270135446Strhodes	result = isc_buffer_allocate(msg->mctx, &dynbuf, size);
271135446Strhodes	if (result != ISC_R_SUCCESS)
272135446Strhodes		return (ISC_R_NOMEMORY);
273135446Strhodes
274135446Strhodes	ISC_LIST_APPEND(msg->scratchpad, dynbuf, link);
275135446Strhodes	return (ISC_R_SUCCESS);
276135446Strhodes}
277135446Strhodes
278135446Strhodesstatic inline isc_buffer_t *
279135446Strhodescurrentbuffer(dns_message_t *msg) {
280135446Strhodes	isc_buffer_t *dynbuf;
281135446Strhodes
282135446Strhodes	dynbuf = ISC_LIST_TAIL(msg->scratchpad);
283135446Strhodes	INSIST(dynbuf != NULL);
284135446Strhodes
285135446Strhodes	return (dynbuf);
286135446Strhodes}
287135446Strhodes
288135446Strhodesstatic inline void
289135446Strhodesreleaserdata(dns_message_t *msg, dns_rdata_t *rdata) {
290135446Strhodes	ISC_LIST_PREPEND(msg->freerdata, rdata, link);
291135446Strhodes}
292135446Strhodes
293135446Strhodesstatic inline dns_rdata_t *
294135446Strhodesnewrdata(dns_message_t *msg) {
295135446Strhodes	dns_msgblock_t *msgblock;
296135446Strhodes	dns_rdata_t *rdata;
297135446Strhodes
298135446Strhodes	rdata = ISC_LIST_HEAD(msg->freerdata);
299135446Strhodes	if (rdata != NULL) {
300135446Strhodes		ISC_LIST_UNLINK(msg->freerdata, rdata, link);
301135446Strhodes		return (rdata);
302135446Strhodes	}
303135446Strhodes
304135446Strhodes	msgblock = ISC_LIST_TAIL(msg->rdatas);
305135446Strhodes	rdata = msgblock_get(msgblock, dns_rdata_t);
306135446Strhodes	if (rdata == NULL) {
307135446Strhodes		msgblock = msgblock_allocate(msg->mctx, sizeof(dns_rdata_t),
308135446Strhodes					     RDATA_COUNT);
309135446Strhodes		if (msgblock == NULL)
310135446Strhodes			return (NULL);
311135446Strhodes
312135446Strhodes		ISC_LIST_APPEND(msg->rdatas, msgblock, link);
313135446Strhodes
314135446Strhodes		rdata = msgblock_get(msgblock, dns_rdata_t);
315135446Strhodes	}
316135446Strhodes
317135446Strhodes	dns_rdata_init(rdata);
318135446Strhodes	return (rdata);
319135446Strhodes}
320135446Strhodes
321135446Strhodesstatic inline void
322135446Strhodesreleaserdatalist(dns_message_t *msg, dns_rdatalist_t *rdatalist) {
323135446Strhodes	ISC_LIST_PREPEND(msg->freerdatalist, rdatalist, link);
324135446Strhodes}
325135446Strhodes
326135446Strhodesstatic inline dns_rdatalist_t *
327135446Strhodesnewrdatalist(dns_message_t *msg) {
328135446Strhodes	dns_msgblock_t *msgblock;
329135446Strhodes	dns_rdatalist_t *rdatalist;
330135446Strhodes
331135446Strhodes	rdatalist = ISC_LIST_HEAD(msg->freerdatalist);
332135446Strhodes	if (rdatalist != NULL) {
333135446Strhodes		ISC_LIST_UNLINK(msg->freerdatalist, rdatalist, link);
334135446Strhodes		return (rdatalist);
335135446Strhodes	}
336135446Strhodes
337135446Strhodes	msgblock = ISC_LIST_TAIL(msg->rdatalists);
338135446Strhodes	rdatalist = msgblock_get(msgblock, dns_rdatalist_t);
339135446Strhodes	if (rdatalist == NULL) {
340135446Strhodes		msgblock = msgblock_allocate(msg->mctx,
341135446Strhodes					     sizeof(dns_rdatalist_t),
342135446Strhodes					     RDATALIST_COUNT);
343135446Strhodes		if (msgblock == NULL)
344135446Strhodes			return (NULL);
345135446Strhodes
346135446Strhodes		ISC_LIST_APPEND(msg->rdatalists, msgblock, link);
347135446Strhodes
348135446Strhodes		rdatalist = msgblock_get(msgblock, dns_rdatalist_t);
349135446Strhodes	}
350135446Strhodes
351135446Strhodes	return (rdatalist);
352135446Strhodes}
353135446Strhodes
354135446Strhodesstatic inline dns_offsets_t *
355135446Strhodesnewoffsets(dns_message_t *msg) {
356135446Strhodes	dns_msgblock_t *msgblock;
357135446Strhodes	dns_offsets_t *offsets;
358135446Strhodes
359135446Strhodes	msgblock = ISC_LIST_TAIL(msg->offsets);
360135446Strhodes	offsets = msgblock_get(msgblock, dns_offsets_t);
361135446Strhodes	if (offsets == NULL) {
362135446Strhodes		msgblock = msgblock_allocate(msg->mctx,
363135446Strhodes					     sizeof(dns_offsets_t),
364135446Strhodes					     OFFSET_COUNT);
365135446Strhodes		if (msgblock == NULL)
366135446Strhodes			return (NULL);
367135446Strhodes
368135446Strhodes		ISC_LIST_APPEND(msg->offsets, msgblock, link);
369135446Strhodes
370135446Strhodes		offsets = msgblock_get(msgblock, dns_offsets_t);
371135446Strhodes	}
372135446Strhodes
373135446Strhodes	return (offsets);
374135446Strhodes}
375135446Strhodes
376135446Strhodesstatic inline void
377135446Strhodesmsginitheader(dns_message_t *m) {
378135446Strhodes	m->id = 0;
379135446Strhodes	m->flags = 0;
380135446Strhodes	m->rcode = 0;
381135446Strhodes	m->opcode = 0;
382135446Strhodes	m->rdclass = 0;
383135446Strhodes}
384135446Strhodes
385135446Strhodesstatic inline void
386135446Strhodesmsginitprivate(dns_message_t *m) {
387135446Strhodes	unsigned int i;
388135446Strhodes
389135446Strhodes	for (i = 0; i < DNS_SECTION_MAX; i++) {
390135446Strhodes		m->cursors[i] = NULL;
391135446Strhodes		m->counts[i] = 0;
392135446Strhodes	}
393135446Strhodes	m->opt = NULL;
394135446Strhodes	m->sig0 = NULL;
395135446Strhodes	m->sig0name = NULL;
396135446Strhodes	m->tsig = NULL;
397135446Strhodes	m->tsigname = NULL;
398135446Strhodes	m->state = DNS_SECTION_ANY;  /* indicate nothing parsed or rendered */
399135446Strhodes	m->opt_reserved = 0;
400135446Strhodes	m->sig_reserved = 0;
401135446Strhodes	m->reserved = 0;
402135446Strhodes	m->buffer = NULL;
403135446Strhodes}
404135446Strhodes
405135446Strhodesstatic inline void
406135446Strhodesmsginittsig(dns_message_t *m) {
407135446Strhodes	m->tsigstatus = dns_rcode_noerror;
408135446Strhodes	m->querytsigstatus = dns_rcode_noerror;
409135446Strhodes	m->tsigkey = NULL;
410135446Strhodes	m->tsigctx = NULL;
411135446Strhodes	m->sigstart = -1;
412135446Strhodes	m->sig0key = NULL;
413135446Strhodes	m->sig0status = dns_rcode_noerror;
414135446Strhodes	m->timeadjust = 0;
415135446Strhodes}
416135446Strhodes
417135446Strhodes/*
418135446Strhodes * Init elements to default state.  Used both when allocating a new element
419135446Strhodes * and when resetting one.
420135446Strhodes */
421135446Strhodesstatic inline void
422135446Strhodesmsginit(dns_message_t *m) {
423135446Strhodes	msginitheader(m);
424135446Strhodes	msginitprivate(m);
425135446Strhodes	msginittsig(m);
426135446Strhodes	m->header_ok = 0;
427135446Strhodes	m->question_ok = 0;
428135446Strhodes	m->tcp_continuation = 0;
429135446Strhodes	m->verified_sig = 0;
430135446Strhodes	m->verify_attempted = 0;
431135446Strhodes	m->order = NULL;
432135446Strhodes	m->order_arg = NULL;
433135446Strhodes	m->query.base = NULL;
434135446Strhodes	m->query.length = 0;
435135446Strhodes	m->free_query = 0;
436135446Strhodes	m->saved.base = NULL;
437135446Strhodes	m->saved.length = 0;
438135446Strhodes	m->free_saved = 0;
439292321Sdelphij	m->tkey = 0;
440292321Sdelphij	m->rdclass_set = 0;
441135446Strhodes	m->querytsig = NULL;
442135446Strhodes}
443135446Strhodes
444135446Strhodesstatic inline void
445135446Strhodesmsgresetnames(dns_message_t *msg, unsigned int first_section) {
446135446Strhodes	unsigned int i;
447135446Strhodes	dns_name_t *name, *next_name;
448135446Strhodes	dns_rdataset_t *rds, *next_rds;
449135446Strhodes
450135446Strhodes	/*
451135446Strhodes	 * Clean up name lists by calling the rdataset disassociate function.
452135446Strhodes	 */
453135446Strhodes	for (i = first_section; i < DNS_SECTION_MAX; i++) {
454135446Strhodes		name = ISC_LIST_HEAD(msg->sections[i]);
455135446Strhodes		while (name != NULL) {
456135446Strhodes			next_name = ISC_LIST_NEXT(name, link);
457135446Strhodes			ISC_LIST_UNLINK(msg->sections[i], name, link);
458135446Strhodes
459135446Strhodes			rds = ISC_LIST_HEAD(name->list);
460135446Strhodes			while (rds != NULL) {
461135446Strhodes				next_rds = ISC_LIST_NEXT(rds, link);
462135446Strhodes				ISC_LIST_UNLINK(name->list, rds, link);
463135446Strhodes
464135446Strhodes				INSIST(dns_rdataset_isassociated(rds));
465135446Strhodes				dns_rdataset_disassociate(rds);
466135446Strhodes				isc_mempool_put(msg->rdspool, rds);
467135446Strhodes				rds = next_rds;
468135446Strhodes			}
469135446Strhodes			if (dns_name_dynamic(name))
470135446Strhodes				dns_name_free(name, msg->mctx);
471135446Strhodes			isc_mempool_put(msg->namepool, name);
472135446Strhodes			name = next_name;
473135446Strhodes		}
474135446Strhodes	}
475135446Strhodes}
476135446Strhodes
477135446Strhodesstatic void
478135446Strhodesmsgresetopt(dns_message_t *msg)
479135446Strhodes{
480135446Strhodes	if (msg->opt != NULL) {
481135446Strhodes		if (msg->opt_reserved > 0) {
482135446Strhodes			dns_message_renderrelease(msg, msg->opt_reserved);
483135446Strhodes			msg->opt_reserved = 0;
484135446Strhodes		}
485135446Strhodes		INSIST(dns_rdataset_isassociated(msg->opt));
486135446Strhodes		dns_rdataset_disassociate(msg->opt);
487135446Strhodes		isc_mempool_put(msg->rdspool, msg->opt);
488135446Strhodes		msg->opt = NULL;
489135446Strhodes	}
490135446Strhodes}
491135446Strhodes
492135446Strhodesstatic void
493135446Strhodesmsgresetsigs(dns_message_t *msg, isc_boolean_t replying) {
494135446Strhodes	if (msg->sig_reserved > 0) {
495135446Strhodes		dns_message_renderrelease(msg, msg->sig_reserved);
496135446Strhodes		msg->sig_reserved = 0;
497135446Strhodes	}
498135446Strhodes	if (msg->tsig != NULL) {
499135446Strhodes		INSIST(dns_rdataset_isassociated(msg->tsig));
500135446Strhodes		INSIST(msg->namepool != NULL);
501135446Strhodes		if (replying) {
502135446Strhodes			INSIST(msg->querytsig == NULL);
503135446Strhodes			msg->querytsig = msg->tsig;
504135446Strhodes		} else {
505135446Strhodes			dns_rdataset_disassociate(msg->tsig);
506135446Strhodes			isc_mempool_put(msg->rdspool, msg->tsig);
507135446Strhodes			if (msg->querytsig != NULL) {
508135446Strhodes				dns_rdataset_disassociate(msg->querytsig);
509135446Strhodes				isc_mempool_put(msg->rdspool, msg->querytsig);
510135446Strhodes			}
511135446Strhodes		}
512135446Strhodes		if (dns_name_dynamic(msg->tsigname))
513135446Strhodes			dns_name_free(msg->tsigname, msg->mctx);
514135446Strhodes		isc_mempool_put(msg->namepool, msg->tsigname);
515135446Strhodes		msg->tsig = NULL;
516135446Strhodes		msg->tsigname = NULL;
517135446Strhodes	} else if (msg->querytsig != NULL && !replying) {
518135446Strhodes		dns_rdataset_disassociate(msg->querytsig);
519135446Strhodes		isc_mempool_put(msg->rdspool, msg->querytsig);
520135446Strhodes		msg->querytsig = NULL;
521135446Strhodes	}
522135446Strhodes	if (msg->sig0 != NULL) {
523135446Strhodes		INSIST(dns_rdataset_isassociated(msg->sig0));
524135446Strhodes		dns_rdataset_disassociate(msg->sig0);
525135446Strhodes		isc_mempool_put(msg->rdspool, msg->sig0);
526135446Strhodes		if (msg->sig0name != NULL) {
527135446Strhodes			if (dns_name_dynamic(msg->sig0name))
528135446Strhodes				dns_name_free(msg->sig0name, msg->mctx);
529135446Strhodes			isc_mempool_put(msg->namepool, msg->sig0name);
530135446Strhodes		}
531135446Strhodes		msg->sig0 = NULL;
532135446Strhodes		msg->sig0name = NULL;
533135446Strhodes	}
534135446Strhodes}
535135446Strhodes
536135446Strhodes/*
537135446Strhodes * Free all but one (or everything) for this message.  This is used by
538135446Strhodes * both dns_message_reset() and dns_message_destroy().
539135446Strhodes */
540135446Strhodesstatic void
541135446Strhodesmsgreset(dns_message_t *msg, isc_boolean_t everything) {
542135446Strhodes	dns_msgblock_t *msgblock, *next_msgblock;
543135446Strhodes	isc_buffer_t *dynbuf, *next_dynbuf;
544135446Strhodes	dns_rdata_t *rdata;
545135446Strhodes	dns_rdatalist_t *rdatalist;
546135446Strhodes
547135446Strhodes	msgresetnames(msg, 0);
548135446Strhodes	msgresetopt(msg);
549135446Strhodes	msgresetsigs(msg, ISC_FALSE);
550135446Strhodes
551135446Strhodes	/*
552135446Strhodes	 * Clean up linked lists.
553135446Strhodes	 */
554135446Strhodes
555135446Strhodes	/*
556135446Strhodes	 * Run through the free lists, and just unlink anything found there.
557135446Strhodes	 * The memory isn't lost since these are part of message blocks we
558135446Strhodes	 * have allocated.
559135446Strhodes	 */
560135446Strhodes	rdata = ISC_LIST_HEAD(msg->freerdata);
561135446Strhodes	while (rdata != NULL) {
562135446Strhodes		ISC_LIST_UNLINK(msg->freerdata, rdata, link);
563135446Strhodes		rdata = ISC_LIST_HEAD(msg->freerdata);
564135446Strhodes	}
565135446Strhodes	rdatalist = ISC_LIST_HEAD(msg->freerdatalist);
566135446Strhodes	while (rdatalist != NULL) {
567135446Strhodes		ISC_LIST_UNLINK(msg->freerdatalist, rdatalist, link);
568135446Strhodes		rdatalist = ISC_LIST_HEAD(msg->freerdatalist);
569135446Strhodes	}
570135446Strhodes
571135446Strhodes	dynbuf = ISC_LIST_HEAD(msg->scratchpad);
572135446Strhodes	INSIST(dynbuf != NULL);
573135446Strhodes	if (!everything) {
574135446Strhodes		isc_buffer_clear(dynbuf);
575135446Strhodes		dynbuf = ISC_LIST_NEXT(dynbuf, link);
576135446Strhodes	}
577135446Strhodes	while (dynbuf != NULL) {
578135446Strhodes		next_dynbuf = ISC_LIST_NEXT(dynbuf, link);
579135446Strhodes		ISC_LIST_UNLINK(msg->scratchpad, dynbuf, link);
580135446Strhodes		isc_buffer_free(&dynbuf);
581135446Strhodes		dynbuf = next_dynbuf;
582135446Strhodes	}
583135446Strhodes
584135446Strhodes	msgblock = ISC_LIST_HEAD(msg->rdatas);
585135446Strhodes	if (!everything && msgblock != NULL) {
586135446Strhodes		msgblock_reset(msgblock);
587135446Strhodes		msgblock = ISC_LIST_NEXT(msgblock, link);
588135446Strhodes	}
589135446Strhodes	while (msgblock != NULL) {
590135446Strhodes		next_msgblock = ISC_LIST_NEXT(msgblock, link);
591135446Strhodes		ISC_LIST_UNLINK(msg->rdatas, msgblock, link);
592135446Strhodes		msgblock_free(msg->mctx, msgblock, sizeof(dns_rdata_t));
593135446Strhodes		msgblock = next_msgblock;
594135446Strhodes	}
595135446Strhodes
596135446Strhodes	/*
597135446Strhodes	 * rdatalists could be empty.
598135446Strhodes	 */
599135446Strhodes
600135446Strhodes	msgblock = ISC_LIST_HEAD(msg->rdatalists);
601135446Strhodes	if (!everything && msgblock != NULL) {
602135446Strhodes		msgblock_reset(msgblock);
603135446Strhodes		msgblock = ISC_LIST_NEXT(msgblock, link);
604135446Strhodes	}
605135446Strhodes	while (msgblock != NULL) {
606135446Strhodes		next_msgblock = ISC_LIST_NEXT(msgblock, link);
607135446Strhodes		ISC_LIST_UNLINK(msg->rdatalists, msgblock, link);
608135446Strhodes		msgblock_free(msg->mctx, msgblock, sizeof(dns_rdatalist_t));
609135446Strhodes		msgblock = next_msgblock;
610135446Strhodes	}
611135446Strhodes
612135446Strhodes	msgblock = ISC_LIST_HEAD(msg->offsets);
613135446Strhodes	if (!everything && msgblock != NULL) {
614135446Strhodes		msgblock_reset(msgblock);
615135446Strhodes		msgblock = ISC_LIST_NEXT(msgblock, link);
616135446Strhodes	}
617135446Strhodes	while (msgblock != NULL) {
618135446Strhodes		next_msgblock = ISC_LIST_NEXT(msgblock, link);
619135446Strhodes		ISC_LIST_UNLINK(msg->offsets, msgblock, link);
620135446Strhodes		msgblock_free(msg->mctx, msgblock, sizeof(dns_offsets_t));
621135446Strhodes		msgblock = next_msgblock;
622135446Strhodes	}
623135446Strhodes
624135446Strhodes	if (msg->tsigkey != NULL) {
625135446Strhodes		dns_tsigkey_detach(&msg->tsigkey);
626135446Strhodes		msg->tsigkey = NULL;
627135446Strhodes	}
628135446Strhodes
629186462Sdougb	if (msg->tsigctx != NULL)
630186462Sdougb		dst_context_destroy(&msg->tsigctx);
631186462Sdougb
632135446Strhodes	if (msg->query.base != NULL) {
633135446Strhodes		if (msg->free_query != 0)
634135446Strhodes			isc_mem_put(msg->mctx, msg->query.base,
635135446Strhodes				    msg->query.length);
636135446Strhodes		msg->query.base = NULL;
637135446Strhodes		msg->query.length = 0;
638135446Strhodes	}
639135446Strhodes
640135446Strhodes	if (msg->saved.base != NULL) {
641135446Strhodes		if (msg->free_saved != 0)
642135446Strhodes			isc_mem_put(msg->mctx, msg->saved.base,
643135446Strhodes				    msg->saved.length);
644135446Strhodes		msg->saved.base = NULL;
645135446Strhodes		msg->saved.length = 0;
646135446Strhodes	}
647135446Strhodes
648135446Strhodes	/*
649135446Strhodes	 * cleanup the buffer cleanup list
650135446Strhodes	 */
651135446Strhodes	dynbuf = ISC_LIST_HEAD(msg->cleanup);
652135446Strhodes	while (dynbuf != NULL) {
653135446Strhodes		next_dynbuf = ISC_LIST_NEXT(dynbuf, link);
654135446Strhodes		ISC_LIST_UNLINK(msg->cleanup, dynbuf, link);
655135446Strhodes		isc_buffer_free(&dynbuf);
656135446Strhodes		dynbuf = next_dynbuf;
657135446Strhodes	}
658135446Strhodes
659135446Strhodes	/*
660135446Strhodes	 * Set other bits to normal default values.
661135446Strhodes	 */
662135446Strhodes	if (!everything)
663135446Strhodes		msginit(msg);
664135446Strhodes
665135446Strhodes	ENSURE(isc_mempool_getallocated(msg->namepool) == 0);
666135446Strhodes	ENSURE(isc_mempool_getallocated(msg->rdspool) == 0);
667135446Strhodes}
668135446Strhodes
669135446Strhodesstatic unsigned int
670135446Strhodesspacefortsig(dns_tsigkey_t *key, int otherlen) {
671135446Strhodes	isc_region_t r1, r2;
672135446Strhodes	unsigned int x;
673135446Strhodes	isc_result_t result;
674135446Strhodes
675135446Strhodes	/*
676135446Strhodes	 * The space required for an TSIG record is:
677135446Strhodes	 *
678135446Strhodes	 *	n1 bytes for the name
679135446Strhodes	 *	2 bytes for the type
680135446Strhodes	 *	2 bytes for the class
681135446Strhodes	 *	4 bytes for the ttl
682135446Strhodes	 *	2 bytes for the rdlength
683135446Strhodes	 *	n2 bytes for the algorithm name
684135446Strhodes	 *	6 bytes for the time signed
685135446Strhodes	 *	2 bytes for the fudge
686135446Strhodes	 *	2 bytes for the MAC size
687135446Strhodes	 *	x bytes for the MAC
688135446Strhodes	 *	2 bytes for the original id
689135446Strhodes	 *	2 bytes for the error
690135446Strhodes	 *	2 bytes for the other data length
691135446Strhodes	 *	y bytes for the other data (at most)
692135446Strhodes	 * ---------------------------------
693135446Strhodes	 *     26 + n1 + n2 + x + y bytes
694135446Strhodes	 */
695135446Strhodes
696135446Strhodes	dns_name_toregion(&key->name, &r1);
697135446Strhodes	dns_name_toregion(key->algorithm, &r2);
698135446Strhodes	if (key->key == NULL)
699135446Strhodes		x = 0;
700135446Strhodes	else {
701135446Strhodes		result = dst_key_sigsize(key->key, &x);
702135446Strhodes		if (result != ISC_R_SUCCESS)
703135446Strhodes			x = 0;
704135446Strhodes	}
705135446Strhodes	return (26 + r1.length + r2.length + x + otherlen);
706135446Strhodes}
707135446Strhodes
708135446Strhodesisc_result_t
709135446Strhodesdns_message_create(isc_mem_t *mctx, unsigned int intent, dns_message_t **msgp)
710135446Strhodes{
711135446Strhodes	dns_message_t *m;
712135446Strhodes	isc_result_t result;
713135446Strhodes	isc_buffer_t *dynbuf;
714135446Strhodes	unsigned int i;
715135446Strhodes
716135446Strhodes	REQUIRE(mctx != NULL);
717135446Strhodes	REQUIRE(msgp != NULL);
718135446Strhodes	REQUIRE(*msgp == NULL);
719135446Strhodes	REQUIRE(intent == DNS_MESSAGE_INTENTPARSE
720135446Strhodes		|| intent == DNS_MESSAGE_INTENTRENDER);
721135446Strhodes
722135446Strhodes	m = isc_mem_get(mctx, sizeof(dns_message_t));
723135446Strhodes	if (m == NULL)
724135446Strhodes		return (ISC_R_NOMEMORY);
725135446Strhodes
726135446Strhodes	/*
727135446Strhodes	 * No allocations until further notice.  Just initialize all lists
728135446Strhodes	 * and other members that are freed in the cleanup phase here.
729135446Strhodes	 */
730135446Strhodes
731135446Strhodes	m->magic = DNS_MESSAGE_MAGIC;
732135446Strhodes	m->from_to_wire = intent;
733135446Strhodes	msginit(m);
734135446Strhodes
735135446Strhodes	for (i = 0; i < DNS_SECTION_MAX; i++)
736135446Strhodes		ISC_LIST_INIT(m->sections[i]);
737135446Strhodes
738254897Serwin	m->mctx = NULL;
739254897Serwin	isc_mem_attach(mctx, &m->mctx);
740254897Serwin
741135446Strhodes	ISC_LIST_INIT(m->scratchpad);
742135446Strhodes	ISC_LIST_INIT(m->cleanup);
743135446Strhodes	m->namepool = NULL;
744135446Strhodes	m->rdspool = NULL;
745135446Strhodes	ISC_LIST_INIT(m->rdatas);
746135446Strhodes	ISC_LIST_INIT(m->rdatalists);
747135446Strhodes	ISC_LIST_INIT(m->offsets);
748135446Strhodes	ISC_LIST_INIT(m->freerdata);
749135446Strhodes	ISC_LIST_INIT(m->freerdatalist);
750135446Strhodes
751135446Strhodes	/*
752135446Strhodes	 * Ok, it is safe to allocate (and then "goto cleanup" if failure)
753135446Strhodes	 */
754135446Strhodes
755135446Strhodes	result = isc_mempool_create(m->mctx, sizeof(dns_name_t), &m->namepool);
756135446Strhodes	if (result != ISC_R_SUCCESS)
757135446Strhodes		goto cleanup;
758135446Strhodes	isc_mempool_setfreemax(m->namepool, NAME_COUNT);
759135446Strhodes	isc_mempool_setname(m->namepool, "msg:names");
760135446Strhodes
761135446Strhodes	result = isc_mempool_create(m->mctx, sizeof(dns_rdataset_t),
762135446Strhodes				    &m->rdspool);
763135446Strhodes	if (result != ISC_R_SUCCESS)
764135446Strhodes		goto cleanup;
765135446Strhodes	isc_mempool_setfreemax(m->rdspool, NAME_COUNT);
766135446Strhodes	isc_mempool_setname(m->rdspool, "msg:rdataset");
767135446Strhodes
768135446Strhodes	dynbuf = NULL;
769135446Strhodes	result = isc_buffer_allocate(mctx, &dynbuf, SCRATCHPAD_SIZE);
770135446Strhodes	if (result != ISC_R_SUCCESS)
771135446Strhodes		goto cleanup;
772135446Strhodes	ISC_LIST_APPEND(m->scratchpad, dynbuf, link);
773135446Strhodes
774135446Strhodes	m->cctx = NULL;
775135446Strhodes
776135446Strhodes	*msgp = m;
777135446Strhodes	return (ISC_R_SUCCESS);
778135446Strhodes
779135446Strhodes	/*
780135446Strhodes	 * Cleanup for error returns.
781135446Strhodes	 */
782135446Strhodes cleanup:
783135446Strhodes	dynbuf = ISC_LIST_HEAD(m->scratchpad);
784135446Strhodes	if (dynbuf != NULL) {
785135446Strhodes		ISC_LIST_UNLINK(m->scratchpad, dynbuf, link);
786135446Strhodes		isc_buffer_free(&dynbuf);
787135446Strhodes	}
788135446Strhodes	if (m->namepool != NULL)
789135446Strhodes		isc_mempool_destroy(&m->namepool);
790135446Strhodes	if (m->rdspool != NULL)
791135446Strhodes		isc_mempool_destroy(&m->rdspool);
792135446Strhodes	m->magic = 0;
793254897Serwin	isc_mem_putanddetach(&mctx, m, sizeof(dns_message_t));
794135446Strhodes
795135446Strhodes	return (ISC_R_NOMEMORY);
796135446Strhodes}
797135446Strhodes
798135446Strhodesvoid
799135446Strhodesdns_message_reset(dns_message_t *msg, unsigned int intent) {
800135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
801135446Strhodes	REQUIRE(intent == DNS_MESSAGE_INTENTPARSE
802135446Strhodes		|| intent == DNS_MESSAGE_INTENTRENDER);
803135446Strhodes
804135446Strhodes	msgreset(msg, ISC_FALSE);
805135446Strhodes	msg->from_to_wire = intent;
806135446Strhodes}
807135446Strhodes
808135446Strhodesvoid
809135446Strhodesdns_message_destroy(dns_message_t **msgp) {
810135446Strhodes	dns_message_t *msg;
811135446Strhodes
812135446Strhodes	REQUIRE(msgp != NULL);
813135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(*msgp));
814135446Strhodes
815135446Strhodes	msg = *msgp;
816135446Strhodes	*msgp = NULL;
817135446Strhodes
818135446Strhodes	msgreset(msg, ISC_TRUE);
819135446Strhodes	isc_mempool_destroy(&msg->namepool);
820135446Strhodes	isc_mempool_destroy(&msg->rdspool);
821135446Strhodes	msg->magic = 0;
822254897Serwin	isc_mem_putanddetach(&msg->mctx, msg, sizeof(dns_message_t));
823135446Strhodes}
824135446Strhodes
825135446Strhodesstatic isc_result_t
826135446Strhodesfindname(dns_name_t **foundname, dns_name_t *target,
827135446Strhodes	 dns_namelist_t *section)
828135446Strhodes{
829135446Strhodes	dns_name_t *curr;
830135446Strhodes
831135446Strhodes	for (curr = ISC_LIST_TAIL(*section);
832135446Strhodes	     curr != NULL;
833135446Strhodes	     curr = ISC_LIST_PREV(curr, link)) {
834135446Strhodes		if (dns_name_equal(curr, target)) {
835135446Strhodes			if (foundname != NULL)
836135446Strhodes				*foundname = curr;
837135446Strhodes			return (ISC_R_SUCCESS);
838135446Strhodes		}
839135446Strhodes	}
840135446Strhodes
841135446Strhodes	return (ISC_R_NOTFOUND);
842135446Strhodes}
843135446Strhodes
844135446Strhodesisc_result_t
845165071Sdougbdns_message_find(dns_name_t *name, dns_rdataclass_t rdclass,
846165071Sdougb		 dns_rdatatype_t type, dns_rdatatype_t covers,
847165071Sdougb		 dns_rdataset_t **rdataset)
848165071Sdougb{
849165071Sdougb	dns_rdataset_t *curr;
850165071Sdougb
851165071Sdougb	if (rdataset != NULL) {
852165071Sdougb		REQUIRE(*rdataset == NULL);
853165071Sdougb	}
854165071Sdougb
855165071Sdougb	for (curr = ISC_LIST_TAIL(name->list);
856165071Sdougb	     curr != NULL;
857165071Sdougb	     curr = ISC_LIST_PREV(curr, link)) {
858165071Sdougb		if (curr->rdclass == rdclass &&
859165071Sdougb		    curr->type == type && curr->covers == covers) {
860165071Sdougb			if (rdataset != NULL)
861165071Sdougb				*rdataset = curr;
862165071Sdougb			return (ISC_R_SUCCESS);
863165071Sdougb		}
864165071Sdougb	}
865165071Sdougb
866165071Sdougb	return (ISC_R_NOTFOUND);
867165071Sdougb}
868165071Sdougb
869165071Sdougbisc_result_t
870135446Strhodesdns_message_findtype(dns_name_t *name, dns_rdatatype_t type,
871135446Strhodes		     dns_rdatatype_t covers, dns_rdataset_t **rdataset)
872135446Strhodes{
873135446Strhodes	dns_rdataset_t *curr;
874135446Strhodes
875165071Sdougb	REQUIRE(name != NULL);
876135446Strhodes	if (rdataset != NULL) {
877135446Strhodes		REQUIRE(*rdataset == NULL);
878135446Strhodes	}
879135446Strhodes
880135446Strhodes	for (curr = ISC_LIST_TAIL(name->list);
881135446Strhodes	     curr != NULL;
882135446Strhodes	     curr = ISC_LIST_PREV(curr, link)) {
883135446Strhodes		if (curr->type == type && curr->covers == covers) {
884135446Strhodes			if (rdataset != NULL)
885135446Strhodes				*rdataset = curr;
886135446Strhodes			return (ISC_R_SUCCESS);
887135446Strhodes		}
888135446Strhodes	}
889135446Strhodes
890135446Strhodes	return (ISC_R_NOTFOUND);
891135446Strhodes}
892135446Strhodes
893135446Strhodes/*
894135446Strhodes * Read a name from buffer "source".
895135446Strhodes */
896135446Strhodesstatic isc_result_t
897135446Strhodesgetname(dns_name_t *name, isc_buffer_t *source, dns_message_t *msg,
898135446Strhodes	dns_decompress_t *dctx)
899135446Strhodes{
900135446Strhodes	isc_buffer_t *scratch;
901135446Strhodes	isc_result_t result;
902135446Strhodes	unsigned int tries;
903135446Strhodes
904135446Strhodes	scratch = currentbuffer(msg);
905135446Strhodes
906135446Strhodes	/*
907135446Strhodes	 * First try:  use current buffer.
908135446Strhodes	 * Second try:  allocate a new buffer and use that.
909135446Strhodes	 */
910135446Strhodes	tries = 0;
911135446Strhodes	while (tries < 2) {
912135446Strhodes		result = dns_name_fromwire(name, source, dctx, ISC_FALSE,
913135446Strhodes					   scratch);
914135446Strhodes
915135446Strhodes		if (result == ISC_R_NOSPACE) {
916135446Strhodes			tries++;
917135446Strhodes
918135446Strhodes			result = newbuffer(msg, SCRATCHPAD_SIZE);
919135446Strhodes			if (result != ISC_R_SUCCESS)
920135446Strhodes				return (result);
921135446Strhodes
922135446Strhodes			scratch = currentbuffer(msg);
923135446Strhodes			dns_name_reset(name);
924135446Strhodes		} else {
925135446Strhodes			return (result);
926135446Strhodes		}
927135446Strhodes	}
928135446Strhodes
929135446Strhodes	INSIST(0);  /* Cannot get here... */
930135446Strhodes	return (ISC_R_UNEXPECTED);
931135446Strhodes}
932135446Strhodes
933135446Strhodesstatic isc_result_t
934135446Strhodesgetrdata(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
935135446Strhodes	 dns_rdataclass_t rdclass, dns_rdatatype_t rdtype,
936135446Strhodes	 unsigned int rdatalen, dns_rdata_t *rdata)
937135446Strhodes{
938135446Strhodes	isc_buffer_t *scratch;
939135446Strhodes	isc_result_t result;
940135446Strhodes	unsigned int tries;
941135446Strhodes	unsigned int trysize;
942135446Strhodes
943135446Strhodes	scratch = currentbuffer(msg);
944135446Strhodes
945135446Strhodes	isc_buffer_setactive(source, rdatalen);
946135446Strhodes
947135446Strhodes	/*
948135446Strhodes	 * First try:  use current buffer.
949135446Strhodes	 * Second try:  allocate a new buffer of size
950135446Strhodes	 *     max(SCRATCHPAD_SIZE, 2 * compressed_rdatalen)
951135446Strhodes	 *     (the data will fit if it was not more than 50% compressed)
952135446Strhodes	 * Subsequent tries: double buffer size on each try.
953135446Strhodes	 */
954135446Strhodes	tries = 0;
955135446Strhodes	trysize = 0;
956135446Strhodes	/* XXX possibly change this to a while (tries < 2) loop */
957135446Strhodes	for (;;) {
958135446Strhodes		result = dns_rdata_fromwire(rdata, rdclass, rdtype,
959135446Strhodes					    source, dctx, 0,
960135446Strhodes					    scratch);
961135446Strhodes
962135446Strhodes		if (result == ISC_R_NOSPACE) {
963135446Strhodes			if (tries == 0) {
964135446Strhodes				trysize = 2 * rdatalen;
965135446Strhodes				if (trysize < SCRATCHPAD_SIZE)
966135446Strhodes					trysize = SCRATCHPAD_SIZE;
967135446Strhodes			} else {
968135446Strhodes				INSIST(trysize != 0);
969135446Strhodes				if (trysize >= 65535)
970135446Strhodes					return (ISC_R_NOSPACE);
971135446Strhodes					/* XXX DNS_R_RRTOOLONG? */
972135446Strhodes				trysize *= 2;
973135446Strhodes			}
974135446Strhodes			tries++;
975135446Strhodes			result = newbuffer(msg, trysize);
976135446Strhodes			if (result != ISC_R_SUCCESS)
977135446Strhodes				return (result);
978135446Strhodes
979135446Strhodes			scratch = currentbuffer(msg);
980135446Strhodes		} else {
981135446Strhodes			return (result);
982135446Strhodes		}
983135446Strhodes	}
984135446Strhodes}
985135446Strhodes
986135446Strhodes#define DO_FORMERR					\
987135446Strhodes	do {						\
988135446Strhodes		if (best_effort)			\
989135446Strhodes			seen_problem = ISC_TRUE;	\
990135446Strhodes		else {					\
991135446Strhodes			result = DNS_R_FORMERR;		\
992135446Strhodes			goto cleanup;			\
993135446Strhodes		}					\
994135446Strhodes	} while (0)
995135446Strhodes
996135446Strhodesstatic isc_result_t
997135446Strhodesgetquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
998135446Strhodes	     unsigned int options)
999135446Strhodes{
1000135446Strhodes	isc_region_t r;
1001135446Strhodes	unsigned int count;
1002135446Strhodes	dns_name_t *name;
1003135446Strhodes	dns_name_t *name2;
1004135446Strhodes	dns_offsets_t *offsets;
1005135446Strhodes	dns_rdataset_t *rdataset;
1006135446Strhodes	dns_rdatalist_t *rdatalist;
1007135446Strhodes	isc_result_t result;
1008135446Strhodes	dns_rdatatype_t rdtype;
1009135446Strhodes	dns_rdataclass_t rdclass;
1010135446Strhodes	dns_namelist_t *section;
1011135446Strhodes	isc_boolean_t free_name;
1012135446Strhodes	isc_boolean_t best_effort;
1013135446Strhodes	isc_boolean_t seen_problem;
1014135446Strhodes
1015135446Strhodes	section = &msg->sections[DNS_SECTION_QUESTION];
1016135446Strhodes
1017135446Strhodes	best_effort = ISC_TF(options & DNS_MESSAGEPARSE_BESTEFFORT);
1018135446Strhodes	seen_problem = ISC_FALSE;
1019135446Strhodes
1020135446Strhodes	name = NULL;
1021135446Strhodes	rdataset = NULL;
1022135446Strhodes	rdatalist = NULL;
1023135446Strhodes
1024135446Strhodes	for (count = 0; count < msg->counts[DNS_SECTION_QUESTION]; count++) {
1025135446Strhodes		name = isc_mempool_get(msg->namepool);
1026135446Strhodes		if (name == NULL)
1027135446Strhodes			return (ISC_R_NOMEMORY);
1028135446Strhodes		free_name = ISC_TRUE;
1029186462Sdougb
1030135446Strhodes		offsets = newoffsets(msg);
1031135446Strhodes		if (offsets == NULL) {
1032135446Strhodes			result = ISC_R_NOMEMORY;
1033135446Strhodes			goto cleanup;
1034135446Strhodes		}
1035135446Strhodes		dns_name_init(name, *offsets);
1036135446Strhodes
1037135446Strhodes		/*
1038135446Strhodes		 * Parse the name out of this packet.
1039135446Strhodes		 */
1040135446Strhodes		isc_buffer_remainingregion(source, &r);
1041135446Strhodes		isc_buffer_setactive(source, r.length);
1042135446Strhodes		result = getname(name, source, msg, dctx);
1043135446Strhodes		if (result != ISC_R_SUCCESS)
1044135446Strhodes			goto cleanup;
1045135446Strhodes
1046135446Strhodes		/*
1047135446Strhodes		 * Run through the section, looking to see if this name
1048135446Strhodes		 * is already there.  If it is found, put back the allocated
1049135446Strhodes		 * name since we no longer need it, and set our name pointer
1050135446Strhodes		 * to point to the name we found.
1051135446Strhodes		 */
1052135446Strhodes		result = findname(&name2, name, section);
1053135446Strhodes
1054135446Strhodes		/*
1055135446Strhodes		 * If it is the first name in the section, accept it.
1056135446Strhodes		 *
1057135446Strhodes		 * If it is not, but is not the same as the name already
1058135446Strhodes		 * in the question section, append to the section.  Note that
1059135446Strhodes		 * here in the question section this is illegal, so return
1060135446Strhodes		 * FORMERR.  In the future, check the opcode to see if
1061135446Strhodes		 * this should be legal or not.  In either case we no longer
1062135446Strhodes		 * need this name pointer.
1063135446Strhodes		 */
1064135446Strhodes		if (result != ISC_R_SUCCESS) {
1065135446Strhodes			if (!ISC_LIST_EMPTY(*section))
1066135446Strhodes				DO_FORMERR;
1067135446Strhodes			ISC_LIST_APPEND(*section, name, link);
1068135446Strhodes			free_name = ISC_FALSE;
1069135446Strhodes		} else {
1070135446Strhodes			isc_mempool_put(msg->namepool, name);
1071135446Strhodes			name = name2;
1072135446Strhodes			name2 = NULL;
1073135446Strhodes			free_name = ISC_FALSE;
1074135446Strhodes		}
1075135446Strhodes
1076135446Strhodes		/*
1077135446Strhodes		 * Get type and class.
1078135446Strhodes		 */
1079135446Strhodes		isc_buffer_remainingregion(source, &r);
1080135446Strhodes		if (r.length < 4) {
1081135446Strhodes			result = ISC_R_UNEXPECTEDEND;
1082135446Strhodes			goto cleanup;
1083135446Strhodes		}
1084135446Strhodes		rdtype = isc_buffer_getuint16(source);
1085135446Strhodes		rdclass = isc_buffer_getuint16(source);
1086135446Strhodes
1087135446Strhodes		/*
1088135446Strhodes		 * If this class is different than the one we already read,
1089135446Strhodes		 * this is an error.
1090135446Strhodes		 */
1091292321Sdelphij		if (msg->rdclass_set == 0) {
1092135446Strhodes			msg->rdclass = rdclass;
1093292321Sdelphij			msg->rdclass_set = 1;
1094135446Strhodes		} else if (msg->rdclass != rdclass)
1095135446Strhodes			DO_FORMERR;
1096135446Strhodes
1097135446Strhodes		/*
1098292321Sdelphij		 * Is this a TKEY query?
1099292321Sdelphij		 */
1100292321Sdelphij		if (rdtype == dns_rdatatype_tkey)
1101292321Sdelphij			msg->tkey = 1;
1102292321Sdelphij
1103292321Sdelphij		/*
1104135446Strhodes		 * Can't ask the same question twice.
1105135446Strhodes		 */
1106165071Sdougb		result = dns_message_find(name, rdclass, rdtype, 0, NULL);
1107135446Strhodes		if (result == ISC_R_SUCCESS)
1108135446Strhodes			DO_FORMERR;
1109135446Strhodes
1110135446Strhodes		/*
1111135446Strhodes		 * Allocate a new rdatalist.
1112135446Strhodes		 */
1113135446Strhodes		rdatalist = newrdatalist(msg);
1114135446Strhodes		if (rdatalist == NULL) {
1115135446Strhodes			result = ISC_R_NOMEMORY;
1116135446Strhodes			goto cleanup;
1117135446Strhodes		}
1118135446Strhodes		rdataset =  isc_mempool_get(msg->rdspool);
1119135446Strhodes		if (rdataset == NULL) {
1120135446Strhodes			result = ISC_R_NOMEMORY;
1121135446Strhodes			goto cleanup;
1122135446Strhodes		}
1123135446Strhodes
1124135446Strhodes		/*
1125135446Strhodes		 * Convert rdatalist to rdataset, and attach the latter to
1126135446Strhodes		 * the name.
1127135446Strhodes		 */
1128135446Strhodes		rdatalist->type = rdtype;
1129135446Strhodes		rdatalist->covers = 0;
1130135446Strhodes		rdatalist->rdclass = rdclass;
1131135446Strhodes		rdatalist->ttl = 0;
1132135446Strhodes		ISC_LIST_INIT(rdatalist->rdata);
1133135446Strhodes
1134135446Strhodes		dns_rdataset_init(rdataset);
1135135446Strhodes		result = dns_rdatalist_tordataset(rdatalist, rdataset);
1136135446Strhodes		if (result != ISC_R_SUCCESS)
1137135446Strhodes			goto cleanup;
1138135446Strhodes
1139135446Strhodes		rdataset->attributes |= DNS_RDATASETATTR_QUESTION;
1140135446Strhodes
1141135446Strhodes		ISC_LIST_APPEND(name->list, rdataset, link);
1142135446Strhodes		rdataset = NULL;
1143135446Strhodes	}
1144135446Strhodes
1145135446Strhodes	if (seen_problem)
1146135446Strhodes		return (DNS_R_RECOVERABLE);
1147135446Strhodes	return (ISC_R_SUCCESS);
1148135446Strhodes
1149135446Strhodes cleanup:
1150135446Strhodes	if (rdataset != NULL) {
1151135446Strhodes		INSIST(!dns_rdataset_isassociated(rdataset));
1152135446Strhodes		isc_mempool_put(msg->rdspool, rdataset);
1153135446Strhodes	}
1154135446Strhodes#if 0
1155135446Strhodes	if (rdatalist != NULL)
1156135446Strhodes		isc_mempool_put(msg->rdlpool, rdatalist);
1157135446Strhodes#endif
1158135446Strhodes	if (free_name)
1159135446Strhodes		isc_mempool_put(msg->namepool, name);
1160135446Strhodes
1161135446Strhodes	return (result);
1162135446Strhodes}
1163135446Strhodes
1164135446Strhodesstatic isc_boolean_t
1165135446Strhodesupdate(dns_section_t section, dns_rdataclass_t rdclass) {
1166135446Strhodes	if (section == DNS_SECTION_PREREQUISITE)
1167135446Strhodes		return (ISC_TF(rdclass == dns_rdataclass_any ||
1168135446Strhodes			       rdclass == dns_rdataclass_none));
1169135446Strhodes	if (section == DNS_SECTION_UPDATE)
1170135446Strhodes		return (ISC_TF(rdclass == dns_rdataclass_any));
1171135446Strhodes	return (ISC_FALSE);
1172135446Strhodes}
1173135446Strhodes
1174135446Strhodesstatic isc_result_t
1175135446Strhodesgetsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
1176135446Strhodes	   dns_section_t sectionid, unsigned int options)
1177135446Strhodes{
1178135446Strhodes	isc_region_t r;
1179135446Strhodes	unsigned int count, rdatalen;
1180135446Strhodes	dns_name_t *name;
1181135446Strhodes	dns_name_t *name2;
1182135446Strhodes	dns_offsets_t *offsets;
1183135446Strhodes	dns_rdataset_t *rdataset;
1184135446Strhodes	dns_rdatalist_t *rdatalist;
1185135446Strhodes	isc_result_t result;
1186135446Strhodes	dns_rdatatype_t rdtype, covers;
1187135446Strhodes	dns_rdataclass_t rdclass;
1188135446Strhodes	dns_rdata_t *rdata;
1189135446Strhodes	dns_ttl_t ttl;
1190135446Strhodes	dns_namelist_t *section;
1191135446Strhodes	isc_boolean_t free_name, free_rdataset;
1192135446Strhodes	isc_boolean_t preserve_order, best_effort, seen_problem;
1193135446Strhodes	isc_boolean_t issigzero;
1194135446Strhodes
1195135446Strhodes	preserve_order = ISC_TF(options & DNS_MESSAGEPARSE_PRESERVEORDER);
1196135446Strhodes	best_effort = ISC_TF(options & DNS_MESSAGEPARSE_BESTEFFORT);
1197135446Strhodes	seen_problem = ISC_FALSE;
1198135446Strhodes
1199135446Strhodes	for (count = 0; count < msg->counts[sectionid]; count++) {
1200135446Strhodes		int recstart = source->current;
1201135446Strhodes		isc_boolean_t skip_name_search, skip_type_search;
1202135446Strhodes
1203135446Strhodes		section = &msg->sections[sectionid];
1204135446Strhodes
1205135446Strhodes		skip_name_search = ISC_FALSE;
1206135446Strhodes		skip_type_search = ISC_FALSE;
1207135446Strhodes		free_rdataset = ISC_FALSE;
1208135446Strhodes
1209135446Strhodes		name = isc_mempool_get(msg->namepool);
1210135446Strhodes		if (name == NULL)
1211135446Strhodes			return (ISC_R_NOMEMORY);
1212135446Strhodes		free_name = ISC_TRUE;
1213135446Strhodes
1214135446Strhodes		offsets = newoffsets(msg);
1215135446Strhodes		if (offsets == NULL) {
1216135446Strhodes			result = ISC_R_NOMEMORY;
1217135446Strhodes			goto cleanup;
1218135446Strhodes		}
1219135446Strhodes		dns_name_init(name, *offsets);
1220135446Strhodes
1221135446Strhodes		/*
1222135446Strhodes		 * Parse the name out of this packet.
1223135446Strhodes		 */
1224135446Strhodes		isc_buffer_remainingregion(source, &r);
1225135446Strhodes		isc_buffer_setactive(source, r.length);
1226135446Strhodes		result = getname(name, source, msg, dctx);
1227135446Strhodes		if (result != ISC_R_SUCCESS)
1228135446Strhodes			goto cleanup;
1229135446Strhodes
1230135446Strhodes		/*
1231135446Strhodes		 * Get type, class, ttl, and rdatalen.  Verify that at least
1232135446Strhodes		 * rdatalen bytes remain.  (Some of this is deferred to
1233135446Strhodes		 * later.)
1234135446Strhodes		 */
1235135446Strhodes		isc_buffer_remainingregion(source, &r);
1236135446Strhodes		if (r.length < 2 + 2 + 4 + 2) {
1237135446Strhodes			result = ISC_R_UNEXPECTEDEND;
1238135446Strhodes			goto cleanup;
1239135446Strhodes		}
1240135446Strhodes		rdtype = isc_buffer_getuint16(source);
1241135446Strhodes		rdclass = isc_buffer_getuint16(source);
1242135446Strhodes
1243135446Strhodes		/*
1244135446Strhodes		 * If there was no question section, we may not yet have
1245135446Strhodes		 * established a class.  Do so now.
1246135446Strhodes		 */
1247292321Sdelphij		if (msg->rdclass_set == 0 &&
1248135446Strhodes		    rdtype != dns_rdatatype_opt &&	/* class is UDP SIZE */
1249135446Strhodes		    rdtype != dns_rdatatype_tsig &&	/* class is ANY */
1250135446Strhodes		    rdtype != dns_rdatatype_tkey) {	/* class is undefined */
1251135446Strhodes			msg->rdclass = rdclass;
1252292321Sdelphij			msg->rdclass_set = 1;
1253135446Strhodes		}
1254135446Strhodes
1255135446Strhodes		/*
1256135446Strhodes		 * If this class is different than the one in the question
1257135446Strhodes		 * section, bail.
1258135446Strhodes		 */
1259135446Strhodes		if (msg->opcode != dns_opcode_update
1260135446Strhodes		    && rdtype != dns_rdatatype_tsig
1261135446Strhodes		    && rdtype != dns_rdatatype_opt
1262292321Sdelphij		    && rdtype != dns_rdatatype_key /* in a TKEY query */
1263135446Strhodes		    && rdtype != dns_rdatatype_sig /* SIG(0) */
1264135446Strhodes		    && rdtype != dns_rdatatype_tkey /* Win2000 TKEY */
1265165071Sdougb		    && msg->rdclass != dns_rdataclass_any
1266135446Strhodes		    && msg->rdclass != rdclass)
1267135446Strhodes			DO_FORMERR;
1268135446Strhodes
1269135446Strhodes		/*
1270292321Sdelphij		 * If this is not a TKEY query/response then the KEY
1271292321Sdelphij		 * record's class needs to match.
1272292321Sdelphij		 */
1273292321Sdelphij		if (msg->opcode != dns_opcode_update && !msg->tkey &&
1274292321Sdelphij		    rdtype == dns_rdatatype_key &&
1275292321Sdelphij		    msg->rdclass != dns_rdataclass_any &&
1276292321Sdelphij		    msg->rdclass != rdclass)
1277292321Sdelphij			DO_FORMERR;
1278292321Sdelphij
1279292321Sdelphij		/*
1280135446Strhodes		 * Special type handling for TSIG, OPT, and TKEY.
1281135446Strhodes		 */
1282135446Strhodes		if (rdtype == dns_rdatatype_tsig) {
1283135446Strhodes			/*
1284135446Strhodes			 * If it is a tsig, verify that it is in the
1285135446Strhodes			 * additional data section.
1286135446Strhodes			 */
1287135446Strhodes			if (sectionid != DNS_SECTION_ADDITIONAL ||
1288135446Strhodes			    rdclass != dns_rdataclass_any ||
1289135446Strhodes			    count != msg->counts[sectionid]  - 1)
1290135446Strhodes				DO_FORMERR;
1291135446Strhodes			msg->sigstart = recstart;
1292135446Strhodes			skip_name_search = ISC_TRUE;
1293135446Strhodes			skip_type_search = ISC_TRUE;
1294135446Strhodes		} else if (rdtype == dns_rdatatype_opt) {
1295135446Strhodes			/*
1296135446Strhodes			 * The name of an OPT record must be ".", it
1297135446Strhodes			 * must be in the additional data section, and
1298135446Strhodes			 * it must be the first OPT we've seen.
1299135446Strhodes			 */
1300135446Strhodes			if (!dns_name_equal(dns_rootname, name) ||
1301135446Strhodes			    msg->opt != NULL)
1302135446Strhodes				DO_FORMERR;
1303135446Strhodes			skip_name_search = ISC_TRUE;
1304135446Strhodes			skip_type_search = ISC_TRUE;
1305135446Strhodes		} else if (rdtype == dns_rdatatype_tkey) {
1306135446Strhodes			/*
1307135446Strhodes			 * A TKEY must be in the additional section if this
1308135446Strhodes			 * is a query, and the answer section if this is a
1309135446Strhodes			 * response.  Unless it's a Win2000 client.
1310135446Strhodes			 *
1311135446Strhodes			 * Its class is ignored.
1312135446Strhodes			 */
1313135446Strhodes			dns_section_t tkeysection;
1314135446Strhodes
1315135446Strhodes			if ((msg->flags & DNS_MESSAGEFLAG_QR) == 0)
1316135446Strhodes				tkeysection = DNS_SECTION_ADDITIONAL;
1317135446Strhodes			else
1318135446Strhodes				tkeysection = DNS_SECTION_ANSWER;
1319135446Strhodes			if (sectionid != tkeysection &&
1320135446Strhodes			    sectionid != DNS_SECTION_ANSWER)
1321135446Strhodes				DO_FORMERR;
1322135446Strhodes		}
1323135446Strhodes
1324135446Strhodes		/*
1325135446Strhodes		 * ... now get ttl and rdatalen, and check buffer.
1326135446Strhodes		 */
1327135446Strhodes		ttl = isc_buffer_getuint32(source);
1328135446Strhodes		rdatalen = isc_buffer_getuint16(source);
1329135446Strhodes		r.length -= (2 + 2 + 4 + 2);
1330135446Strhodes		if (r.length < rdatalen) {
1331135446Strhodes			result = ISC_R_UNEXPECTEDEND;
1332135446Strhodes			goto cleanup;
1333135446Strhodes		}
1334135446Strhodes
1335135446Strhodes		/*
1336135446Strhodes		 * Read the rdata from the wire format.  Interpret the
1337135446Strhodes		 * rdata according to its actual class, even if it had a
1338135446Strhodes		 * DynDNS meta-class in the packet (unless this is a TSIG).
1339135446Strhodes		 * Then put the meta-class back into the finished rdata.
1340135446Strhodes		 */
1341135446Strhodes		rdata = newrdata(msg);
1342135446Strhodes		if (rdata == NULL) {
1343135446Strhodes			result = ISC_R_NOMEMORY;
1344135446Strhodes			goto cleanup;
1345135446Strhodes		}
1346135446Strhodes		if (msg->opcode == dns_opcode_update &&
1347135446Strhodes		    update(sectionid, rdclass)) {
1348135446Strhodes			if (rdatalen != 0) {
1349135446Strhodes				result = DNS_R_FORMERR;
1350135446Strhodes				goto cleanup;
1351135446Strhodes			}
1352135446Strhodes			/*
1353135446Strhodes			 * When the rdata is empty, the data pointer is
1354186462Sdougb			 * never dereferenced, but it must still be non-NULL.
1355135446Strhodes			 * Casting 1 rather than "" avoids warnings about
1356135446Strhodes			 * discarding the const attribute of a string,
1357135446Strhodes			 * for compilers that would warn about such things.
1358135446Strhodes			 */
1359135446Strhodes			rdata->data = (unsigned char *)1;
1360135446Strhodes			rdata->length = 0;
1361135446Strhodes			rdata->rdclass = rdclass;
1362135446Strhodes			rdata->type = rdtype;
1363135446Strhodes			rdata->flags = DNS_RDATA_UPDATE;
1364135446Strhodes			result = ISC_R_SUCCESS;
1365174187Sdougb		} else if (rdclass == dns_rdataclass_none &&
1366174187Sdougb			   msg->opcode == dns_opcode_update &&
1367174187Sdougb			   sectionid == DNS_SECTION_UPDATE) {
1368174187Sdougb			result = getrdata(source, msg, dctx, msg->rdclass,
1369174187Sdougb					  rdtype, rdatalen, rdata);
1370165071Sdougb		} else
1371135446Strhodes			result = getrdata(source, msg, dctx, rdclass,
1372135446Strhodes					  rdtype, rdatalen, rdata);
1373135446Strhodes		if (result != ISC_R_SUCCESS)
1374135446Strhodes			goto cleanup;
1375135446Strhodes		rdata->rdclass = rdclass;
1376135446Strhodes		issigzero = ISC_FALSE;
1377135446Strhodes		if (rdtype == dns_rdatatype_rrsig  &&
1378135446Strhodes		    rdata->flags == 0) {
1379135446Strhodes			covers = dns_rdata_covers(rdata);
1380135446Strhodes			if (covers == 0)
1381135446Strhodes				DO_FORMERR;
1382135446Strhodes		} else if (rdtype == dns_rdatatype_sig /* SIG(0) */ &&
1383135446Strhodes			   rdata->flags == 0) {
1384135446Strhodes			covers = dns_rdata_covers(rdata);
1385135446Strhodes			if (covers == 0) {
1386135446Strhodes				if (sectionid != DNS_SECTION_ADDITIONAL ||
1387135446Strhodes				    count != msg->counts[sectionid]  - 1)
1388135446Strhodes					DO_FORMERR;
1389135446Strhodes				msg->sigstart = recstart;
1390135446Strhodes				skip_name_search = ISC_TRUE;
1391135446Strhodes				skip_type_search = ISC_TRUE;
1392135446Strhodes				issigzero = ISC_TRUE;
1393292321Sdelphij			} else {
1394292321Sdelphij				if (msg->rdclass != dns_rdataclass_any &&
1395292321Sdelphij				    msg->rdclass != rdclass)
1396292321Sdelphij					DO_FORMERR;
1397135446Strhodes			}
1398135446Strhodes		} else
1399135446Strhodes			covers = 0;
1400135446Strhodes
1401135446Strhodes		/*
1402135446Strhodes		 * If we are doing a dynamic update or this is a meta-type,
1403135446Strhodes		 * don't bother searching for a name, just append this one
1404135446Strhodes		 * to the end of the message.
1405135446Strhodes		 */
1406135446Strhodes		if (preserve_order || msg->opcode == dns_opcode_update ||
1407135446Strhodes		    skip_name_search) {
1408135446Strhodes			if (rdtype != dns_rdatatype_opt &&
1409135446Strhodes			    rdtype != dns_rdatatype_tsig &&
1410135446Strhodes			    !issigzero)
1411135446Strhodes			{
1412135446Strhodes				ISC_LIST_APPEND(*section, name, link);
1413135446Strhodes				free_name = ISC_FALSE;
1414135446Strhodes			}
1415135446Strhodes		} else {
1416135446Strhodes			/*
1417135446Strhodes			 * Run through the section, looking to see if this name
1418135446Strhodes			 * is already there.  If it is found, put back the
1419135446Strhodes			 * allocated name since we no longer need it, and set
1420135446Strhodes			 * our name pointer to point to the name we found.
1421135446Strhodes			 */
1422135446Strhodes			result = findname(&name2, name, section);
1423135446Strhodes
1424135446Strhodes			/*
1425135446Strhodes			 * If it is a new name, append to the section.
1426135446Strhodes			 */
1427135446Strhodes			if (result == ISC_R_SUCCESS) {
1428135446Strhodes				isc_mempool_put(msg->namepool, name);
1429135446Strhodes				name = name2;
1430135446Strhodes			} else {
1431135446Strhodes				ISC_LIST_APPEND(*section, name, link);
1432135446Strhodes			}
1433135446Strhodes			free_name = ISC_FALSE;
1434135446Strhodes		}
1435135446Strhodes
1436135446Strhodes		/*
1437135446Strhodes		 * Search name for the particular type and class.
1438135446Strhodes		 * Skip this stage if in update mode or this is a meta-type.
1439135446Strhodes		 */
1440135446Strhodes		if (preserve_order || msg->opcode == dns_opcode_update ||
1441135446Strhodes		    skip_type_search)
1442135446Strhodes			result = ISC_R_NOTFOUND;
1443135446Strhodes		else {
1444135446Strhodes			/*
1445135446Strhodes			 * If this is a type that can only occur in
1446135446Strhodes			 * the question section, fail.
1447135446Strhodes			 */
1448135446Strhodes			if (dns_rdatatype_questiononly(rdtype))
1449135446Strhodes				DO_FORMERR;
1450135446Strhodes
1451135446Strhodes			rdataset = NULL;
1452165071Sdougb			result = dns_message_find(name, rdclass, rdtype,
1453165071Sdougb						   covers, &rdataset);
1454135446Strhodes		}
1455135446Strhodes
1456135446Strhodes		/*
1457135446Strhodes		 * If we found an rdataset that matches, we need to
1458135446Strhodes		 * append this rdata to that set.  If we did not, we need
1459135446Strhodes		 * to create a new rdatalist, store the important bits there,
1460135446Strhodes		 * convert it to an rdataset, and link the latter to the name.
1461135446Strhodes		 * Yuck.  When appending, make certain that the type isn't
1462135446Strhodes		 * a singleton type, such as SOA or CNAME.
1463135446Strhodes		 *
1464135446Strhodes		 * Note that this check will be bypassed when preserving order,
1465135446Strhodes		 * the opcode is an update, or the type search is skipped.
1466135446Strhodes		 */
1467135446Strhodes		if (result == ISC_R_SUCCESS) {
1468254402Serwin			if (dns_rdatatype_issingleton(rdtype)) {
1469254402Serwin				dns_rdata_t *first;
1470254402Serwin				dns_rdatalist_fromrdataset(rdataset,
1471254402Serwin							   &rdatalist);
1472254402Serwin				first = ISC_LIST_HEAD(rdatalist->rdata);
1473254402Serwin				INSIST(first != NULL);
1474254402Serwin				if (dns_rdata_compare(rdata, first) != 0)
1475254402Serwin					DO_FORMERR;
1476254402Serwin			}
1477135446Strhodes		}
1478135446Strhodes
1479135446Strhodes		if (result == ISC_R_NOTFOUND) {
1480135446Strhodes			rdataset = isc_mempool_get(msg->rdspool);
1481135446Strhodes			if (rdataset == NULL) {
1482135446Strhodes				result = ISC_R_NOMEMORY;
1483135446Strhodes				goto cleanup;
1484135446Strhodes			}
1485135446Strhodes			free_rdataset = ISC_TRUE;
1486135446Strhodes
1487135446Strhodes			rdatalist = newrdatalist(msg);
1488135446Strhodes			if (rdatalist == NULL) {
1489135446Strhodes				result = ISC_R_NOMEMORY;
1490135446Strhodes				goto cleanup;
1491135446Strhodes			}
1492135446Strhodes
1493135446Strhodes			rdatalist->type = rdtype;
1494135446Strhodes			rdatalist->covers = covers;
1495135446Strhodes			rdatalist->rdclass = rdclass;
1496135446Strhodes			rdatalist->ttl = ttl;
1497135446Strhodes			ISC_LIST_INIT(rdatalist->rdata);
1498135446Strhodes
1499135446Strhodes			dns_rdataset_init(rdataset);
1500135446Strhodes			RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist,
1501135446Strhodes							       rdataset)
1502135446Strhodes				      == ISC_R_SUCCESS);
1503135446Strhodes
1504186462Sdougb			if (rdtype != dns_rdatatype_opt &&
1505135446Strhodes			    rdtype != dns_rdatatype_tsig &&
1506135446Strhodes			    !issigzero)
1507135446Strhodes			{
1508135446Strhodes				ISC_LIST_APPEND(name->list, rdataset, link);
1509135446Strhodes				free_rdataset = ISC_FALSE;
1510135446Strhodes			}
1511135446Strhodes		}
1512135446Strhodes
1513135446Strhodes		/*
1514135446Strhodes		 * Minimize TTLs.
1515135446Strhodes		 *
1516170222Sdougb		 * Section 5.2 of RFC2181 says we should drop
1517135446Strhodes		 * nonauthoritative rrsets where the TTLs differ, but we
1518135446Strhodes		 * currently treat them the as if they were authoritative and
1519135446Strhodes		 * minimize them.
1520135446Strhodes		 */
1521135446Strhodes		if (ttl != rdataset->ttl) {
1522135446Strhodes			rdataset->attributes |= DNS_RDATASETATTR_TTLADJUSTED;
1523135446Strhodes			if (ttl < rdataset->ttl)
1524135446Strhodes				rdataset->ttl = ttl;
1525135446Strhodes		}
1526135446Strhodes
1527193149Sdougb		/* Append this rdata to the rdataset. */
1528193149Sdougb		dns_rdatalist_fromrdataset(rdataset, &rdatalist);
1529135446Strhodes		ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
1530135446Strhodes
1531135446Strhodes		/*
1532135446Strhodes		 * If this is an OPT record, remember it.  Also, set
1533135446Strhodes		 * the extended rcode.  Note that msg->opt will only be set
1534135446Strhodes		 * if best-effort parsing is enabled.
1535135446Strhodes		 */
1536135446Strhodes		if (rdtype == dns_rdatatype_opt && msg->opt == NULL) {
1537135446Strhodes			dns_rcode_t ercode;
1538135446Strhodes
1539135446Strhodes			msg->opt = rdataset;
1540135446Strhodes			rdataset = NULL;
1541135446Strhodes			free_rdataset = ISC_FALSE;
1542135446Strhodes			ercode = (dns_rcode_t)
1543135446Strhodes				((msg->opt->ttl & DNS_MESSAGE_EDNSRCODE_MASK)
1544135446Strhodes				 >> 20);
1545135446Strhodes			msg->rcode |= ercode;
1546135446Strhodes			isc_mempool_put(msg->namepool, name);
1547135446Strhodes			free_name = ISC_FALSE;
1548135446Strhodes		}
1549135446Strhodes
1550135446Strhodes		/*
1551135446Strhodes		 * If this is an SIG(0) or TSIG record, remember it.  Note
1552135446Strhodes		 * that msg->sig0 or msg->tsig will only be set if best-effort
1553135446Strhodes		 * parsing is enabled.
1554135446Strhodes		 */
1555135446Strhodes		if (issigzero && msg->sig0 == NULL) {
1556135446Strhodes			msg->sig0 = rdataset;
1557135446Strhodes			msg->sig0name = name;
1558135446Strhodes			rdataset = NULL;
1559135446Strhodes			free_rdataset = ISC_FALSE;
1560135446Strhodes			free_name = ISC_FALSE;
1561135446Strhodes		} else if (rdtype == dns_rdatatype_tsig && msg->tsig == NULL) {
1562135446Strhodes			msg->tsig = rdataset;
1563135446Strhodes			msg->tsigname = name;
1564218384Sdougb			/* Windows doesn't like TSIG names to be compressed. */
1565218384Sdougb			msg->tsigname->attributes |= DNS_NAMEATTR_NOCOMPRESS;
1566135446Strhodes			rdataset = NULL;
1567135446Strhodes			free_rdataset = ISC_FALSE;
1568135446Strhodes			free_name = ISC_FALSE;
1569135446Strhodes		}
1570135446Strhodes
1571153816Sdougb		if (seen_problem) {
1572153816Sdougb			if (free_name)
1573153816Sdougb				isc_mempool_put(msg->namepool, name);
1574153816Sdougb			if (free_rdataset)
1575153816Sdougb				isc_mempool_put(msg->rdspool, rdataset);
1576153816Sdougb			free_name = free_rdataset = ISC_FALSE;
1577153816Sdougb		}
1578135446Strhodes		INSIST(free_name == ISC_FALSE);
1579135446Strhodes		INSIST(free_rdataset == ISC_FALSE);
1580135446Strhodes	}
1581135446Strhodes
1582135446Strhodes	if (seen_problem)
1583135446Strhodes		return (DNS_R_RECOVERABLE);
1584135446Strhodes	return (ISC_R_SUCCESS);
1585135446Strhodes
1586135446Strhodes cleanup:
1587135446Strhodes	if (free_name)
1588135446Strhodes		isc_mempool_put(msg->namepool, name);
1589135446Strhodes	if (free_rdataset)
1590135446Strhodes		isc_mempool_put(msg->rdspool, rdataset);
1591135446Strhodes
1592135446Strhodes	return (result);
1593135446Strhodes}
1594135446Strhodes
1595135446Strhodesisc_result_t
1596135446Strhodesdns_message_parse(dns_message_t *msg, isc_buffer_t *source,
1597135446Strhodes		  unsigned int options)
1598135446Strhodes{
1599135446Strhodes	isc_region_t r;
1600135446Strhodes	dns_decompress_t dctx;
1601135446Strhodes	isc_result_t ret;
1602135446Strhodes	isc_uint16_t tmpflags;
1603135446Strhodes	isc_buffer_t origsource;
1604135446Strhodes	isc_boolean_t seen_problem;
1605135446Strhodes	isc_boolean_t ignore_tc;
1606135446Strhodes
1607135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
1608135446Strhodes	REQUIRE(source != NULL);
1609135446Strhodes	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTPARSE);
1610135446Strhodes
1611135446Strhodes	seen_problem = ISC_FALSE;
1612135446Strhodes	ignore_tc = ISC_TF(options & DNS_MESSAGEPARSE_IGNORETRUNCATION);
1613135446Strhodes
1614135446Strhodes	origsource = *source;
1615135446Strhodes
1616135446Strhodes	msg->header_ok = 0;
1617135446Strhodes	msg->question_ok = 0;
1618135446Strhodes
1619135446Strhodes	isc_buffer_remainingregion(source, &r);
1620135446Strhodes	if (r.length < DNS_MESSAGE_HEADERLEN)
1621135446Strhodes		return (ISC_R_UNEXPECTEDEND);
1622135446Strhodes
1623135446Strhodes	msg->id = isc_buffer_getuint16(source);
1624135446Strhodes	tmpflags = isc_buffer_getuint16(source);
1625135446Strhodes	msg->opcode = ((tmpflags & DNS_MESSAGE_OPCODE_MASK)
1626135446Strhodes		       >> DNS_MESSAGE_OPCODE_SHIFT);
1627135446Strhodes	msg->rcode = (dns_rcode_t)(tmpflags & DNS_MESSAGE_RCODE_MASK);
1628135446Strhodes	msg->flags = (tmpflags & DNS_MESSAGE_FLAG_MASK);
1629135446Strhodes	msg->counts[DNS_SECTION_QUESTION] = isc_buffer_getuint16(source);
1630135446Strhodes	msg->counts[DNS_SECTION_ANSWER] = isc_buffer_getuint16(source);
1631135446Strhodes	msg->counts[DNS_SECTION_AUTHORITY] = isc_buffer_getuint16(source);
1632135446Strhodes	msg->counts[DNS_SECTION_ADDITIONAL] = isc_buffer_getuint16(source);
1633135446Strhodes
1634135446Strhodes	msg->header_ok = 1;
1635292321Sdelphij	msg->state = DNS_SECTION_QUESTION;
1636135446Strhodes
1637135446Strhodes	/*
1638135446Strhodes	 * -1 means no EDNS.
1639135446Strhodes	 */
1640135446Strhodes	dns_decompress_init(&dctx, -1, DNS_DECOMPRESS_ANY);
1641135446Strhodes
1642135446Strhodes	dns_decompress_setmethods(&dctx, DNS_COMPRESS_GLOBAL14);
1643135446Strhodes
1644135446Strhodes	ret = getquestions(source, msg, &dctx, options);
1645135446Strhodes	if (ret == ISC_R_UNEXPECTEDEND && ignore_tc)
1646135446Strhodes		goto truncated;
1647135446Strhodes	if (ret == DNS_R_RECOVERABLE) {
1648135446Strhodes		seen_problem = ISC_TRUE;
1649135446Strhodes		ret = ISC_R_SUCCESS;
1650135446Strhodes	}
1651135446Strhodes	if (ret != ISC_R_SUCCESS)
1652135446Strhodes		return (ret);
1653135446Strhodes	msg->question_ok = 1;
1654135446Strhodes
1655135446Strhodes	ret = getsection(source, msg, &dctx, DNS_SECTION_ANSWER, options);
1656135446Strhodes	if (ret == ISC_R_UNEXPECTEDEND && ignore_tc)
1657135446Strhodes		goto truncated;
1658135446Strhodes	if (ret == DNS_R_RECOVERABLE) {
1659135446Strhodes		seen_problem = ISC_TRUE;
1660135446Strhodes		ret = ISC_R_SUCCESS;
1661135446Strhodes	}
1662135446Strhodes	if (ret != ISC_R_SUCCESS)
1663135446Strhodes		return (ret);
1664135446Strhodes
1665135446Strhodes	ret = getsection(source, msg, &dctx, DNS_SECTION_AUTHORITY, options);
1666135446Strhodes	if (ret == ISC_R_UNEXPECTEDEND && ignore_tc)
1667135446Strhodes		goto truncated;
1668135446Strhodes	if (ret == DNS_R_RECOVERABLE) {
1669135446Strhodes		seen_problem = ISC_TRUE;
1670135446Strhodes		ret = ISC_R_SUCCESS;
1671135446Strhodes	}
1672135446Strhodes	if (ret != ISC_R_SUCCESS)
1673135446Strhodes		return (ret);
1674135446Strhodes
1675135446Strhodes	ret = getsection(source, msg, &dctx, DNS_SECTION_ADDITIONAL, options);
1676135446Strhodes	if (ret == ISC_R_UNEXPECTEDEND && ignore_tc)
1677135446Strhodes		goto truncated;
1678135446Strhodes	if (ret == DNS_R_RECOVERABLE) {
1679135446Strhodes		seen_problem = ISC_TRUE;
1680135446Strhodes		ret = ISC_R_SUCCESS;
1681135446Strhodes	}
1682135446Strhodes	if (ret != ISC_R_SUCCESS)
1683135446Strhodes		return (ret);
1684135446Strhodes
1685135446Strhodes	isc_buffer_remainingregion(source, &r);
1686135446Strhodes	if (r.length != 0) {
1687135446Strhodes		isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
1688135446Strhodes			      DNS_LOGMODULE_MESSAGE, ISC_LOG_DEBUG(3),
1689135446Strhodes			      "message has %u byte(s) of trailing garbage",
1690135446Strhodes			      r.length);
1691135446Strhodes	}
1692135446Strhodes
1693135446Strhodes truncated:
1694135446Strhodes	if ((options & DNS_MESSAGEPARSE_CLONEBUFFER) == 0)
1695135446Strhodes		isc_buffer_usedregion(&origsource, &msg->saved);
1696135446Strhodes	else {
1697135446Strhodes		msg->saved.length = isc_buffer_usedlength(&origsource);
1698135446Strhodes		msg->saved.base = isc_mem_get(msg->mctx, msg->saved.length);
1699135446Strhodes		if (msg->saved.base == NULL)
1700135446Strhodes			return (ISC_R_NOMEMORY);
1701262706Serwin		memmove(msg->saved.base, isc_buffer_base(&origsource),
1702262706Serwin			msg->saved.length);
1703135446Strhodes		msg->free_saved = 1;
1704135446Strhodes	}
1705135446Strhodes
1706135446Strhodes	if (ret == ISC_R_UNEXPECTEDEND && ignore_tc)
1707135446Strhodes		return (DNS_R_RECOVERABLE);
1708135446Strhodes	if (seen_problem == ISC_TRUE)
1709135446Strhodes		return (DNS_R_RECOVERABLE);
1710135446Strhodes	return (ISC_R_SUCCESS);
1711135446Strhodes}
1712135446Strhodes
1713135446Strhodesisc_result_t
1714135446Strhodesdns_message_renderbegin(dns_message_t *msg, dns_compress_t *cctx,
1715135446Strhodes			isc_buffer_t *buffer)
1716135446Strhodes{
1717135446Strhodes	isc_region_t r;
1718135446Strhodes
1719135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
1720135446Strhodes	REQUIRE(buffer != NULL);
1721135446Strhodes	REQUIRE(msg->buffer == NULL);
1722135446Strhodes	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
1723135446Strhodes
1724135446Strhodes	msg->cctx = cctx;
1725135446Strhodes
1726135446Strhodes	/*
1727135446Strhodes	 * Erase the contents of this buffer.
1728135446Strhodes	 */
1729135446Strhodes	isc_buffer_clear(buffer);
1730135446Strhodes
1731135446Strhodes	/*
1732135446Strhodes	 * Make certain there is enough for at least the header in this
1733135446Strhodes	 * buffer.
1734135446Strhodes	 */
1735135446Strhodes	isc_buffer_availableregion(buffer, &r);
1736135446Strhodes	if (r.length < DNS_MESSAGE_HEADERLEN)
1737135446Strhodes		return (ISC_R_NOSPACE);
1738135446Strhodes
1739306942Sdelphij	if (r.length - DNS_MESSAGE_HEADERLEN < msg->reserved)
1740135446Strhodes		return (ISC_R_NOSPACE);
1741135446Strhodes
1742135446Strhodes	/*
1743135446Strhodes	 * Reserve enough space for the header in this buffer.
1744135446Strhodes	 */
1745135446Strhodes	isc_buffer_add(buffer, DNS_MESSAGE_HEADERLEN);
1746135446Strhodes
1747135446Strhodes	msg->buffer = buffer;
1748135446Strhodes
1749135446Strhodes	return (ISC_R_SUCCESS);
1750135446Strhodes}
1751135446Strhodes
1752135446Strhodesisc_result_t
1753135446Strhodesdns_message_renderchangebuffer(dns_message_t *msg, isc_buffer_t *buffer) {
1754135446Strhodes	isc_region_t r, rn;
1755135446Strhodes
1756135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
1757135446Strhodes	REQUIRE(buffer != NULL);
1758135446Strhodes	REQUIRE(msg->buffer != NULL);
1759135446Strhodes
1760135446Strhodes	/*
1761135446Strhodes	 * Ensure that the new buffer is empty, and has enough space to
1762135446Strhodes	 * hold the current contents.
1763135446Strhodes	 */
1764135446Strhodes	isc_buffer_clear(buffer);
1765135446Strhodes
1766135446Strhodes	isc_buffer_availableregion(buffer, &rn);
1767135446Strhodes	isc_buffer_usedregion(msg->buffer, &r);
1768135446Strhodes	REQUIRE(rn.length > r.length);
1769135446Strhodes
1770135446Strhodes	/*
1771135446Strhodes	 * Copy the contents from the old to the new buffer.
1772135446Strhodes	 */
1773135446Strhodes	isc_buffer_add(buffer, r.length);
1774262706Serwin	memmove(rn.base, r.base, r.length);
1775135446Strhodes
1776135446Strhodes	msg->buffer = buffer;
1777135446Strhodes
1778135446Strhodes	return (ISC_R_SUCCESS);
1779135446Strhodes}
1780135446Strhodes
1781135446Strhodesvoid
1782135446Strhodesdns_message_renderrelease(dns_message_t *msg, unsigned int space) {
1783135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
1784135446Strhodes	REQUIRE(space <= msg->reserved);
1785135446Strhodes
1786135446Strhodes	msg->reserved -= space;
1787135446Strhodes}
1788135446Strhodes
1789135446Strhodesisc_result_t
1790135446Strhodesdns_message_renderreserve(dns_message_t *msg, unsigned int space) {
1791135446Strhodes	isc_region_t r;
1792135446Strhodes
1793135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
1794135446Strhodes
1795135446Strhodes	if (msg->buffer != NULL) {
1796135446Strhodes		isc_buffer_availableregion(msg->buffer, &r);
1797135446Strhodes		if (r.length < (space + msg->reserved))
1798135446Strhodes			return (ISC_R_NOSPACE);
1799135446Strhodes	}
1800135446Strhodes
1801135446Strhodes	msg->reserved += space;
1802135446Strhodes
1803135446Strhodes	return (ISC_R_SUCCESS);
1804135446Strhodes}
1805135446Strhodes
1806135446Strhodesstatic inline isc_boolean_t
1807135446Strhodeswrong_priority(dns_rdataset_t *rds, int pass, dns_rdatatype_t preferred_glue) {
1808135446Strhodes	int pass_needed;
1809135446Strhodes
1810135446Strhodes	/*
1811135446Strhodes	 * If we are not rendering class IN, this ordering is bogus.
1812135446Strhodes	 */
1813135446Strhodes	if (rds->rdclass != dns_rdataclass_in)
1814135446Strhodes		return (ISC_FALSE);
1815135446Strhodes
1816135446Strhodes	switch (rds->type) {
1817135446Strhodes	case dns_rdatatype_a:
1818135446Strhodes	case dns_rdatatype_aaaa:
1819135446Strhodes		if (preferred_glue == rds->type)
1820135446Strhodes			pass_needed = 4;
1821135446Strhodes		else
1822135446Strhodes			pass_needed = 3;
1823135446Strhodes		break;
1824135446Strhodes	case dns_rdatatype_rrsig:
1825135446Strhodes	case dns_rdatatype_dnskey:
1826135446Strhodes		pass_needed = 2;
1827135446Strhodes		break;
1828135446Strhodes	default:
1829135446Strhodes		pass_needed = 1;
1830135446Strhodes	}
1831135446Strhodes
1832135446Strhodes	if (pass_needed >= pass)
1833135446Strhodes		return (ISC_FALSE);
1834135446Strhodes
1835135446Strhodes	return (ISC_TRUE);
1836135446Strhodes}
1837135446Strhodes
1838224092Sdougb#ifdef ALLOW_FILTER_AAAA_ON_V4
1839224092Sdougb/*
1840224092Sdougb * Decide whether to not answer with an AAAA record and its RRSIG
1841224092Sdougb */
1842224092Sdougbstatic inline isc_boolean_t
1843224092Sdougbnorender_rdataset(const dns_rdataset_t *rdataset, unsigned int options)
1844224092Sdougb{
1845224092Sdougb	switch (rdataset->type) {
1846224092Sdougb	case dns_rdatatype_aaaa:
1847224092Sdougb		if ((options & DNS_MESSAGERENDER_FILTER_AAAA) == 0)
1848224092Sdougb			return (ISC_FALSE);
1849224092Sdougb		break;
1850224092Sdougb
1851224092Sdougb	case dns_rdatatype_rrsig:
1852224092Sdougb		if ((options & DNS_MESSAGERENDER_FILTER_AAAA) == 0 ||
1853224092Sdougb		    rdataset->covers != dns_rdatatype_aaaa)
1854224092Sdougb			return (ISC_FALSE);
1855224092Sdougb		break;
1856224092Sdougb
1857224092Sdougb	default:
1858224092Sdougb		return (ISC_FALSE);
1859224092Sdougb	}
1860224092Sdougb
1861224092Sdougb	if (rdataset->rdclass != dns_rdataclass_in)
1862224092Sdougb		return (ISC_FALSE);
1863224092Sdougb
1864224092Sdougb	return (ISC_TRUE);
1865224092Sdougb}
1866306942Sdelphij#endif
1867224092Sdougb
1868306942Sdelphijstatic isc_result_t
1869306942Sdelphijrenderset(dns_rdataset_t *rdataset, dns_name_t *owner_name,
1870306942Sdelphij	  dns_compress_t *cctx, isc_buffer_t *target,
1871306942Sdelphij	  unsigned int reserved, unsigned int options, unsigned int *countp)
1872306942Sdelphij{
1873306942Sdelphij	isc_result_t result;
1874306942Sdelphij
1875306942Sdelphij	/*
1876306942Sdelphij	 * Shrink the space in the buffer by the reserved amount.
1877306942Sdelphij	 */
1878306942Sdelphij	if (target->length - target->used < reserved)
1879306942Sdelphij		return (ISC_R_NOSPACE);
1880306942Sdelphij
1881306942Sdelphij	target->length -= reserved;
1882306942Sdelphij	result = dns_rdataset_towire(rdataset, owner_name,
1883306942Sdelphij				     cctx, target, options, countp);
1884306942Sdelphij	target->length += reserved;
1885306942Sdelphij
1886306942Sdelphij	return (result);
1887306942Sdelphij}
1888306942Sdelphij
1889135446Strhodesisc_result_t
1890135446Strhodesdns_message_rendersection(dns_message_t *msg, dns_section_t sectionid,
1891135446Strhodes			  unsigned int options)
1892135446Strhodes{
1893135446Strhodes	dns_namelist_t *section;
1894135446Strhodes	dns_name_t *name, *next_name;
1895135446Strhodes	dns_rdataset_t *rdataset, *next_rdataset;
1896135446Strhodes	unsigned int count, total;
1897135446Strhodes	isc_result_t result;
1898135446Strhodes	isc_buffer_t st; /* for rollbacks */
1899135446Strhodes	int pass;
1900135446Strhodes	isc_boolean_t partial = ISC_FALSE;
1901135446Strhodes	unsigned int rd_options;
1902135446Strhodes	dns_rdatatype_t preferred_glue = 0;
1903135446Strhodes
1904135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
1905135446Strhodes	REQUIRE(msg->buffer != NULL);
1906135446Strhodes	REQUIRE(VALID_NAMED_SECTION(sectionid));
1907135446Strhodes
1908135446Strhodes	section = &msg->sections[sectionid];
1909135446Strhodes
1910135446Strhodes	if ((sectionid == DNS_SECTION_ADDITIONAL)
1911135446Strhodes	    && (options & DNS_MESSAGERENDER_ORDERED) == 0) {
1912135446Strhodes		if ((options & DNS_MESSAGERENDER_PREFER_A) != 0) {
1913135446Strhodes			preferred_glue = dns_rdatatype_a;
1914135446Strhodes			pass = 4;
1915135446Strhodes		} else if ((options & DNS_MESSAGERENDER_PREFER_AAAA) != 0) {
1916135446Strhodes			preferred_glue = dns_rdatatype_aaaa;
1917135446Strhodes			pass = 4;
1918135446Strhodes		} else
1919135446Strhodes			pass = 3;
1920135446Strhodes	} else
1921135446Strhodes		pass = 1;
1922135446Strhodes
1923135446Strhodes	if ((options & DNS_MESSAGERENDER_OMITDNSSEC) == 0)
1924135446Strhodes		rd_options = 0;
1925135446Strhodes	else
1926135446Strhodes		rd_options = DNS_RDATASETTOWIRE_OMITDNSSEC;
1927135446Strhodes
1928135446Strhodes	/*
1929135446Strhodes	 * Shrink the space in the buffer by the reserved amount.
1930135446Strhodes	 */
1931306942Sdelphij	if (msg->buffer->length - msg->buffer->used < msg->reserved)
1932306942Sdelphij		return (ISC_R_NOSPACE);
1933135446Strhodes	msg->buffer->length -= msg->reserved;
1934135446Strhodes
1935135446Strhodes	total = 0;
1936135446Strhodes	if (msg->reserved == 0 && (options & DNS_MESSAGERENDER_PARTIAL) != 0)
1937135446Strhodes		partial = ISC_TRUE;
1938135446Strhodes
1939153816Sdougb	/*
1940153816Sdougb	 * Render required glue first.  Set TC if it won't fit.
1941153816Sdougb	 */
1942153816Sdougb	name = ISC_LIST_HEAD(*section);
1943153816Sdougb	if (name != NULL) {
1944153816Sdougb		rdataset = ISC_LIST_HEAD(name->list);
1945153816Sdougb		if (rdataset != NULL &&
1946153816Sdougb		    (rdataset->attributes & DNS_RDATASETATTR_REQUIREDGLUE) != 0 &&
1947153816Sdougb		    (rdataset->attributes & DNS_RDATASETATTR_RENDERED) == 0) {
1948165071Sdougb			const void *order_arg = msg->order_arg;
1949153816Sdougb			st = *(msg->buffer);
1950153816Sdougb			count = 0;
1951153816Sdougb			if (partial)
1952153816Sdougb				result = dns_rdataset_towirepartial(rdataset,
1953153816Sdougb								    name,
1954153816Sdougb								    msg->cctx,
1955153816Sdougb								    msg->buffer,
1956153816Sdougb								    msg->order,
1957153816Sdougb								    order_arg,
1958153816Sdougb								    rd_options,
1959153816Sdougb								    &count,
1960153816Sdougb								    NULL);
1961153816Sdougb			else
1962153816Sdougb				result = dns_rdataset_towiresorted(rdataset,
1963153816Sdougb								   name,
1964153816Sdougb								   msg->cctx,
1965153816Sdougb								   msg->buffer,
1966153816Sdougb								   msg->order,
1967153816Sdougb								   order_arg,
1968153816Sdougb								   rd_options,
1969153816Sdougb								   &count);
1970153816Sdougb			total += count;
1971153816Sdougb			if (partial && result == ISC_R_NOSPACE) {
1972153816Sdougb				msg->flags |= DNS_MESSAGEFLAG_TC;
1973153816Sdougb				msg->buffer->length += msg->reserved;
1974153816Sdougb				msg->counts[sectionid] += total;
1975153816Sdougb				return (result);
1976153816Sdougb			}
1977204619Sdougb			if (result == ISC_R_NOSPACE)
1978204619Sdougb				msg->flags |= DNS_MESSAGEFLAG_TC;
1979153816Sdougb			if (result != ISC_R_SUCCESS) {
1980153816Sdougb				INSIST(st.used < 65536);
1981153816Sdougb				dns_compress_rollback(msg->cctx,
1982153816Sdougb						      (isc_uint16_t)st.used);
1983153816Sdougb				*(msg->buffer) = st;  /* rollback */
1984153816Sdougb				msg->buffer->length += msg->reserved;
1985153816Sdougb				msg->counts[sectionid] += total;
1986153816Sdougb				return (result);
1987153816Sdougb			}
1988153816Sdougb			rdataset->attributes |= DNS_RDATASETATTR_RENDERED;
1989153816Sdougb		}
1990153816Sdougb	}
1991153816Sdougb
1992135446Strhodes	do {
1993135446Strhodes		name = ISC_LIST_HEAD(*section);
1994135446Strhodes		if (name == NULL) {
1995135446Strhodes			msg->buffer->length += msg->reserved;
1996135446Strhodes			msg->counts[sectionid] += total;
1997135446Strhodes			return (ISC_R_SUCCESS);
1998135446Strhodes		}
1999135446Strhodes
2000135446Strhodes		while (name != NULL) {
2001135446Strhodes			next_name = ISC_LIST_NEXT(name, link);
2002135446Strhodes
2003135446Strhodes			rdataset = ISC_LIST_HEAD(name->list);
2004135446Strhodes			while (rdataset != NULL) {
2005135446Strhodes				next_rdataset = ISC_LIST_NEXT(rdataset, link);
2006135446Strhodes
2007135446Strhodes				if ((rdataset->attributes &
2008135446Strhodes				     DNS_RDATASETATTR_RENDERED) != 0)
2009135446Strhodes					goto next;
2010135446Strhodes
2011135446Strhodes				if (((options & DNS_MESSAGERENDER_ORDERED)
2012135446Strhodes				     == 0)
2013135446Strhodes				    && (sectionid == DNS_SECTION_ADDITIONAL)
2014135446Strhodes				    && wrong_priority(rdataset, pass,
2015135446Strhodes						      preferred_glue))
2016135446Strhodes					goto next;
2017135446Strhodes
2018224092Sdougb#ifdef ALLOW_FILTER_AAAA_ON_V4
2019224092Sdougb				/*
2020224092Sdougb				 * Suppress AAAAs if asked and we are
2021224092Sdougb				 * not doing DNSSEC or are breaking DNSSEC.
2022224092Sdougb				 * Say so in the AD bit if we break DNSSEC.
2023224092Sdougb				 */
2024224092Sdougb				if (norender_rdataset(rdataset, options) &&
2025224092Sdougb				    sectionid != DNS_SECTION_QUESTION) {
2026224092Sdougb					if (sectionid == DNS_SECTION_ANSWER ||
2027224092Sdougb					    sectionid == DNS_SECTION_AUTHORITY)
2028224092Sdougb					    msg->flags &= ~DNS_MESSAGEFLAG_AD;
2029224092Sdougb					if (OPTOUT(rdataset))
2030224092Sdougb					    msg->flags &= ~DNS_MESSAGEFLAG_AD;
2031224092Sdougb					goto next;
2032224092Sdougb				}
2033224092Sdougb
2034224092Sdougb#endif
2035135446Strhodes				st = *(msg->buffer);
2036135446Strhodes
2037135446Strhodes				count = 0;
2038135446Strhodes				if (partial)
2039135446Strhodes					result = dns_rdataset_towirepartial(
2040135446Strhodes							  rdataset,
2041135446Strhodes							  name,
2042135446Strhodes							  msg->cctx,
2043135446Strhodes							  msg->buffer,
2044135446Strhodes							  msg->order,
2045135446Strhodes							  msg->order_arg,
2046135446Strhodes							  rd_options,
2047135446Strhodes							  &count,
2048135446Strhodes							  NULL);
2049135446Strhodes				else
2050135446Strhodes					result = dns_rdataset_towiresorted(
2051135446Strhodes							  rdataset,
2052135446Strhodes							  name,
2053135446Strhodes							  msg->cctx,
2054135446Strhodes							  msg->buffer,
2055135446Strhodes							  msg->order,
2056135446Strhodes							  msg->order_arg,
2057135446Strhodes							  rd_options,
2058135446Strhodes							  &count);
2059135446Strhodes
2060135446Strhodes				total += count;
2061135446Strhodes
2062135446Strhodes				/*
2063135446Strhodes				 * If out of space, record stats on what we
2064135446Strhodes				 * rendered so far, and return that status.
2065135446Strhodes				 *
2066135446Strhodes				 * XXXMLG Need to change this when
2067135446Strhodes				 * dns_rdataset_towire() can render partial
2068193149Sdougb				 * sets starting at some arbitrary point in the
2069135446Strhodes				 * set.  This will include setting a bit in the
2070135446Strhodes				 * rdataset to indicate that a partial
2071135446Strhodes				 * rendering was done, and some state saved
2072135446Strhodes				 * somewhere (probably in the message struct)
2073135446Strhodes				 * to indicate where to continue from.
2074135446Strhodes				 */
2075135446Strhodes				if (partial && result == ISC_R_NOSPACE) {
2076135446Strhodes					msg->buffer->length += msg->reserved;
2077135446Strhodes					msg->counts[sectionid] += total;
2078135446Strhodes					return (result);
2079135446Strhodes				}
2080135446Strhodes				if (result != ISC_R_SUCCESS) {
2081135446Strhodes					INSIST(st.used < 65536);
2082135446Strhodes					dns_compress_rollback(msg->cctx,
2083135446Strhodes							(isc_uint16_t)st.used);
2084135446Strhodes					*(msg->buffer) = st;  /* rollback */
2085135446Strhodes					msg->buffer->length += msg->reserved;
2086135446Strhodes					msg->counts[sectionid] += total;
2087135446Strhodes					return (result);
2088135446Strhodes				}
2089135446Strhodes
2090135446Strhodes				/*
2091135446Strhodes				 * If we have rendered non-validated data,
2092135446Strhodes				 * ensure that the AD bit is not set.
2093135446Strhodes				 */
2094135446Strhodes				if (rdataset->trust != dns_trust_secure &&
2095135446Strhodes				    (sectionid == DNS_SECTION_ANSWER ||
2096135446Strhodes				     sectionid == DNS_SECTION_AUTHORITY))
2097135446Strhodes					msg->flags &= ~DNS_MESSAGEFLAG_AD;
2098193149Sdougb				if (OPTOUT(rdataset))
2099193149Sdougb					msg->flags &= ~DNS_MESSAGEFLAG_AD;
2100135446Strhodes
2101135446Strhodes				rdataset->attributes |=
2102135446Strhodes					DNS_RDATASETATTR_RENDERED;
2103135446Strhodes
2104135446Strhodes			next:
2105135446Strhodes				rdataset = next_rdataset;
2106135446Strhodes			}
2107135446Strhodes
2108135446Strhodes			name = next_name;
2109135446Strhodes		}
2110135446Strhodes	} while (--pass != 0);
2111135446Strhodes
2112135446Strhodes	msg->buffer->length += msg->reserved;
2113135446Strhodes	msg->counts[sectionid] += total;
2114135446Strhodes
2115135446Strhodes	return (ISC_R_SUCCESS);
2116135446Strhodes}
2117135446Strhodes
2118135446Strhodesvoid
2119135446Strhodesdns_message_renderheader(dns_message_t *msg, isc_buffer_t *target) {
2120135446Strhodes	isc_uint16_t tmp;
2121135446Strhodes	isc_region_t r;
2122135446Strhodes
2123135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2124135446Strhodes	REQUIRE(target != NULL);
2125135446Strhodes
2126135446Strhodes	isc_buffer_availableregion(target, &r);
2127135446Strhodes	REQUIRE(r.length >= DNS_MESSAGE_HEADERLEN);
2128135446Strhodes
2129135446Strhodes	isc_buffer_putuint16(target, msg->id);
2130135446Strhodes
2131135446Strhodes	tmp = ((msg->opcode << DNS_MESSAGE_OPCODE_SHIFT)
2132135446Strhodes	       & DNS_MESSAGE_OPCODE_MASK);
2133135446Strhodes	tmp |= (msg->rcode & DNS_MESSAGE_RCODE_MASK);
2134135446Strhodes	tmp |= (msg->flags & DNS_MESSAGE_FLAG_MASK);
2135135446Strhodes
2136135446Strhodes	INSIST(msg->counts[DNS_SECTION_QUESTION]  < 65536 &&
2137135446Strhodes	       msg->counts[DNS_SECTION_ANSWER]    < 65536 &&
2138135446Strhodes	       msg->counts[DNS_SECTION_AUTHORITY] < 65536 &&
2139135446Strhodes	       msg->counts[DNS_SECTION_ADDITIONAL] < 65536);
2140135446Strhodes
2141135446Strhodes	isc_buffer_putuint16(target, tmp);
2142135446Strhodes	isc_buffer_putuint16(target,
2143135446Strhodes			    (isc_uint16_t)msg->counts[DNS_SECTION_QUESTION]);
2144135446Strhodes	isc_buffer_putuint16(target,
2145135446Strhodes			    (isc_uint16_t)msg->counts[DNS_SECTION_ANSWER]);
2146135446Strhodes	isc_buffer_putuint16(target,
2147135446Strhodes			    (isc_uint16_t)msg->counts[DNS_SECTION_AUTHORITY]);
2148135446Strhodes	isc_buffer_putuint16(target,
2149135446Strhodes			    (isc_uint16_t)msg->counts[DNS_SECTION_ADDITIONAL]);
2150135446Strhodes}
2151135446Strhodes
2152135446Strhodesisc_result_t
2153135446Strhodesdns_message_renderend(dns_message_t *msg) {
2154135446Strhodes	isc_buffer_t tmpbuf;
2155135446Strhodes	isc_region_t r;
2156135446Strhodes	int result;
2157135446Strhodes	unsigned int count;
2158135446Strhodes
2159135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2160135446Strhodes	REQUIRE(msg->buffer != NULL);
2161135446Strhodes
2162135446Strhodes	if ((msg->rcode & ~DNS_MESSAGE_RCODE_MASK) != 0 && msg->opt == NULL) {
2163135446Strhodes		/*
2164135446Strhodes		 * We have an extended rcode but are not using EDNS.
2165135446Strhodes		 */
2166135446Strhodes		return (DNS_R_FORMERR);
2167135446Strhodes	}
2168135446Strhodes
2169135446Strhodes	/*
2170254402Serwin	 * If we're adding a OPT, TSIG or SIG(0) to a truncated message,
2171254402Serwin	 * clear all rdatasets from the message except for the question
2172254402Serwin	 * before adding the OPT, TSIG or SIG(0).  If the question doesn't
2173254402Serwin	 * fit, don't include it.
2174254402Serwin	 */
2175254402Serwin	if ((msg->tsigkey != NULL || msg->sig0key != NULL || msg->opt) &&
2176254402Serwin	    (msg->flags & DNS_MESSAGEFLAG_TC) != 0)
2177254402Serwin	{
2178254402Serwin		isc_buffer_t *buf;
2179254402Serwin
2180254402Serwin		msgresetnames(msg, DNS_SECTION_ANSWER);
2181254402Serwin		buf = msg->buffer;
2182254402Serwin		dns_message_renderreset(msg);
2183254402Serwin		msg->buffer = buf;
2184254402Serwin		isc_buffer_clear(msg->buffer);
2185254402Serwin		isc_buffer_add(msg->buffer, DNS_MESSAGE_HEADERLEN);
2186254402Serwin		dns_compress_rollback(msg->cctx, 0);
2187254402Serwin		result = dns_message_rendersection(msg, DNS_SECTION_QUESTION,
2188254402Serwin						   0);
2189254402Serwin		if (result != ISC_R_SUCCESS && result != ISC_R_NOSPACE)
2190254402Serwin			return (result);
2191254402Serwin	}
2192254402Serwin
2193254402Serwin	/*
2194135446Strhodes	 * If we've got an OPT record, render it.
2195135446Strhodes	 */
2196135446Strhodes	if (msg->opt != NULL) {
2197135446Strhodes		dns_message_renderrelease(msg, msg->opt_reserved);
2198135446Strhodes		msg->opt_reserved = 0;
2199135446Strhodes		/*
2200135446Strhodes		 * Set the extended rcode.
2201135446Strhodes		 */
2202135446Strhodes		msg->opt->ttl &= ~DNS_MESSAGE_EDNSRCODE_MASK;
2203135446Strhodes		msg->opt->ttl |= ((msg->rcode << 20) &
2204135446Strhodes				  DNS_MESSAGE_EDNSRCODE_MASK);
2205135446Strhodes		/*
2206135446Strhodes		 * Render.
2207135446Strhodes		 */
2208135446Strhodes		count = 0;
2209306942Sdelphij		result = renderset(msg->opt, dns_rootname, msg->cctx,
2210306942Sdelphij				   msg->buffer, msg->reserved, 0, &count);
2211135446Strhodes		msg->counts[DNS_SECTION_ADDITIONAL] += count;
2212135446Strhodes		if (result != ISC_R_SUCCESS)
2213135446Strhodes			return (result);
2214135446Strhodes	}
2215135446Strhodes
2216135446Strhodes	/*
2217135446Strhodes	 * If we're adding a TSIG record, generate and render it.
2218135446Strhodes	 */
2219135446Strhodes	if (msg->tsigkey != NULL) {
2220135446Strhodes		dns_message_renderrelease(msg, msg->sig_reserved);
2221135446Strhodes		msg->sig_reserved = 0;
2222135446Strhodes		result = dns_tsig_sign(msg);
2223135446Strhodes		if (result != ISC_R_SUCCESS)
2224135446Strhodes			return (result);
2225135446Strhodes		count = 0;
2226306942Sdelphij		result = renderset(msg->tsig, msg->tsigname, msg->cctx,
2227306942Sdelphij				   msg->buffer, msg->reserved, 0, &count);
2228135446Strhodes		msg->counts[DNS_SECTION_ADDITIONAL] += count;
2229135446Strhodes		if (result != ISC_R_SUCCESS)
2230135446Strhodes			return (result);
2231135446Strhodes	}
2232135446Strhodes
2233135446Strhodes	/*
2234135446Strhodes	 * If we're adding a SIG(0) record, generate and render it.
2235135446Strhodes	 */
2236135446Strhodes	if (msg->sig0key != NULL) {
2237135446Strhodes		dns_message_renderrelease(msg, msg->sig_reserved);
2238135446Strhodes		msg->sig_reserved = 0;
2239135446Strhodes		result = dns_dnssec_signmessage(msg, msg->sig0key);
2240135446Strhodes		if (result != ISC_R_SUCCESS)
2241135446Strhodes			return (result);
2242135446Strhodes		count = 0;
2243135446Strhodes		/*
2244135446Strhodes		 * Note: dns_rootname is used here, not msg->sig0name, since
2245135446Strhodes		 * the owner name of a SIG(0) is irrelevant, and will not
2246135446Strhodes		 * be set in a message being rendered.
2247135446Strhodes		 */
2248306942Sdelphij		result = renderset(msg->sig0, dns_rootname, msg->cctx,
2249306942Sdelphij				   msg->buffer, msg->reserved, 0, &count);
2250135446Strhodes		msg->counts[DNS_SECTION_ADDITIONAL] += count;
2251135446Strhodes		if (result != ISC_R_SUCCESS)
2252135446Strhodes			return (result);
2253135446Strhodes	}
2254135446Strhodes
2255135446Strhodes	isc_buffer_usedregion(msg->buffer, &r);
2256135446Strhodes	isc_buffer_init(&tmpbuf, r.base, r.length);
2257135446Strhodes
2258135446Strhodes	dns_message_renderheader(msg, &tmpbuf);
2259135446Strhodes
2260135446Strhodes	msg->buffer = NULL;  /* forget about this buffer only on success XXX */
2261135446Strhodes
2262135446Strhodes	return (ISC_R_SUCCESS);
2263135446Strhodes}
2264135446Strhodes
2265135446Strhodesvoid
2266135446Strhodesdns_message_renderreset(dns_message_t *msg) {
2267135446Strhodes	unsigned int i;
2268135446Strhodes	dns_name_t *name;
2269135446Strhodes	dns_rdataset_t *rds;
2270135446Strhodes
2271135446Strhodes	/*
2272135446Strhodes	 * Reset the message so that it may be rendered again.
2273135446Strhodes	 */
2274135446Strhodes
2275135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2276135446Strhodes	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
2277135446Strhodes
2278135446Strhodes	msg->buffer = NULL;
2279135446Strhodes
2280135446Strhodes	for (i = 0; i < DNS_SECTION_MAX; i++) {
2281135446Strhodes		msg->cursors[i] = NULL;
2282135446Strhodes		msg->counts[i] = 0;
2283135446Strhodes		for (name = ISC_LIST_HEAD(msg->sections[i]);
2284135446Strhodes		     name != NULL;
2285135446Strhodes		     name = ISC_LIST_NEXT(name, link)) {
2286135446Strhodes			for (rds = ISC_LIST_HEAD(name->list);
2287135446Strhodes			     rds != NULL;
2288135446Strhodes			     rds = ISC_LIST_NEXT(rds, link)) {
2289135446Strhodes				rds->attributes &= ~DNS_RDATASETATTR_RENDERED;
2290135446Strhodes			}
2291135446Strhodes		}
2292135446Strhodes	}
2293135446Strhodes	if (msg->tsigname != NULL)
2294135446Strhodes		dns_message_puttempname(msg, &msg->tsigname);
2295135446Strhodes	if (msg->tsig != NULL) {
2296135446Strhodes		dns_rdataset_disassociate(msg->tsig);
2297135446Strhodes		dns_message_puttemprdataset(msg, &msg->tsig);
2298135446Strhodes	}
2299135446Strhodes	if (msg->sig0 != NULL) {
2300135446Strhodes		dns_rdataset_disassociate(msg->sig0);
2301135446Strhodes		dns_message_puttemprdataset(msg, &msg->sig0);
2302135446Strhodes	}
2303135446Strhodes}
2304135446Strhodes
2305135446Strhodesisc_result_t
2306135446Strhodesdns_message_firstname(dns_message_t *msg, dns_section_t section) {
2307135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2308135446Strhodes	REQUIRE(VALID_NAMED_SECTION(section));
2309135446Strhodes
2310135446Strhodes	msg->cursors[section] = ISC_LIST_HEAD(msg->sections[section]);
2311135446Strhodes
2312135446Strhodes	if (msg->cursors[section] == NULL)
2313135446Strhodes		return (ISC_R_NOMORE);
2314135446Strhodes
2315135446Strhodes	return (ISC_R_SUCCESS);
2316135446Strhodes}
2317135446Strhodes
2318135446Strhodesisc_result_t
2319135446Strhodesdns_message_nextname(dns_message_t *msg, dns_section_t section) {
2320135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2321135446Strhodes	REQUIRE(VALID_NAMED_SECTION(section));
2322135446Strhodes	REQUIRE(msg->cursors[section] != NULL);
2323135446Strhodes
2324135446Strhodes	msg->cursors[section] = ISC_LIST_NEXT(msg->cursors[section], link);
2325135446Strhodes
2326135446Strhodes	if (msg->cursors[section] == NULL)
2327135446Strhodes		return (ISC_R_NOMORE);
2328135446Strhodes
2329135446Strhodes	return (ISC_R_SUCCESS);
2330135446Strhodes}
2331135446Strhodes
2332135446Strhodesvoid
2333135446Strhodesdns_message_currentname(dns_message_t *msg, dns_section_t section,
2334135446Strhodes			dns_name_t **name)
2335135446Strhodes{
2336135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2337135446Strhodes	REQUIRE(VALID_NAMED_SECTION(section));
2338135446Strhodes	REQUIRE(name != NULL && *name == NULL);
2339135446Strhodes	REQUIRE(msg->cursors[section] != NULL);
2340135446Strhodes
2341135446Strhodes	*name = msg->cursors[section];
2342135446Strhodes}
2343135446Strhodes
2344135446Strhodesisc_result_t
2345135446Strhodesdns_message_findname(dns_message_t *msg, dns_section_t section,
2346135446Strhodes		     dns_name_t *target, dns_rdatatype_t type,
2347135446Strhodes		     dns_rdatatype_t covers, dns_name_t **name,
2348135446Strhodes		     dns_rdataset_t **rdataset)
2349135446Strhodes{
2350135446Strhodes	dns_name_t *foundname;
2351135446Strhodes	isc_result_t result;
2352135446Strhodes
2353135446Strhodes	/*
2354135446Strhodes	 * XXX These requirements are probably too intensive, especially
2355135446Strhodes	 * where things can be NULL, but as they are they ensure that if
2356135446Strhodes	 * something is NON-NULL, indicating that the caller expects it
2357135446Strhodes	 * to be filled in, that we can in fact fill it in.
2358135446Strhodes	 */
2359135446Strhodes	REQUIRE(msg != NULL);
2360135446Strhodes	REQUIRE(VALID_SECTION(section));
2361135446Strhodes	REQUIRE(target != NULL);
2362135446Strhodes	if (name != NULL)
2363135446Strhodes		REQUIRE(*name == NULL);
2364135446Strhodes	if (type == dns_rdatatype_any) {
2365135446Strhodes		REQUIRE(rdataset == NULL);
2366135446Strhodes	} else {
2367135446Strhodes		if (rdataset != NULL)
2368135446Strhodes			REQUIRE(*rdataset == NULL);
2369135446Strhodes	}
2370135446Strhodes
2371135446Strhodes	result = findname(&foundname, target,
2372135446Strhodes			  &msg->sections[section]);
2373135446Strhodes
2374135446Strhodes	if (result == ISC_R_NOTFOUND)
2375135446Strhodes		return (DNS_R_NXDOMAIN);
2376135446Strhodes	else if (result != ISC_R_SUCCESS)
2377135446Strhodes		return (result);
2378135446Strhodes
2379135446Strhodes	if (name != NULL)
2380135446Strhodes		*name = foundname;
2381135446Strhodes
2382135446Strhodes	/*
2383135446Strhodes	 * And now look for the type.
2384135446Strhodes	 */
2385135446Strhodes	if (type == dns_rdatatype_any)
2386135446Strhodes		return (ISC_R_SUCCESS);
2387135446Strhodes
2388135446Strhodes	result = dns_message_findtype(foundname, type, covers, rdataset);
2389135446Strhodes	if (result == ISC_R_NOTFOUND)
2390135446Strhodes		return (DNS_R_NXRRSET);
2391135446Strhodes
2392135446Strhodes	return (result);
2393135446Strhodes}
2394135446Strhodes
2395135446Strhodesvoid
2396135446Strhodesdns_message_movename(dns_message_t *msg, dns_name_t *name,
2397135446Strhodes		     dns_section_t fromsection,
2398135446Strhodes		     dns_section_t tosection)
2399135446Strhodes{
2400135446Strhodes	REQUIRE(msg != NULL);
2401135446Strhodes	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
2402135446Strhodes	REQUIRE(name != NULL);
2403135446Strhodes	REQUIRE(VALID_NAMED_SECTION(fromsection));
2404135446Strhodes	REQUIRE(VALID_NAMED_SECTION(tosection));
2405135446Strhodes
2406135446Strhodes	/*
2407135446Strhodes	 * Unlink the name from the old section
2408135446Strhodes	 */
2409135446Strhodes	ISC_LIST_UNLINK(msg->sections[fromsection], name, link);
2410135446Strhodes	ISC_LIST_APPEND(msg->sections[tosection], name, link);
2411135446Strhodes}
2412135446Strhodes
2413135446Strhodesvoid
2414135446Strhodesdns_message_addname(dns_message_t *msg, dns_name_t *name,
2415135446Strhodes		    dns_section_t section)
2416135446Strhodes{
2417135446Strhodes	REQUIRE(msg != NULL);
2418135446Strhodes	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
2419135446Strhodes	REQUIRE(name != NULL);
2420135446Strhodes	REQUIRE(VALID_NAMED_SECTION(section));
2421135446Strhodes
2422135446Strhodes	ISC_LIST_APPEND(msg->sections[section], name, link);
2423135446Strhodes}
2424135446Strhodes
2425170222Sdougbvoid
2426170222Sdougbdns_message_removename(dns_message_t *msg, dns_name_t *name,
2427170222Sdougb		       dns_section_t section)
2428170222Sdougb{
2429170222Sdougb	REQUIRE(msg != NULL);
2430170222Sdougb	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
2431170222Sdougb	REQUIRE(name != NULL);
2432170222Sdougb	REQUIRE(VALID_NAMED_SECTION(section));
2433170222Sdougb
2434170222Sdougb	ISC_LIST_UNLINK(msg->sections[section], name, link);
2435170222Sdougb}
2436170222Sdougb
2437135446Strhodesisc_result_t
2438135446Strhodesdns_message_gettempname(dns_message_t *msg, dns_name_t **item) {
2439135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2440135446Strhodes	REQUIRE(item != NULL && *item == NULL);
2441135446Strhodes
2442135446Strhodes	*item = isc_mempool_get(msg->namepool);
2443135446Strhodes	if (*item == NULL)
2444135446Strhodes		return (ISC_R_NOMEMORY);
2445135446Strhodes	dns_name_init(*item, NULL);
2446135446Strhodes
2447135446Strhodes	return (ISC_R_SUCCESS);
2448135446Strhodes}
2449135446Strhodes
2450135446Strhodesisc_result_t
2451135446Strhodesdns_message_gettempoffsets(dns_message_t *msg, dns_offsets_t **item) {
2452135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2453135446Strhodes	REQUIRE(item != NULL && *item == NULL);
2454135446Strhodes
2455135446Strhodes	*item = newoffsets(msg);
2456135446Strhodes	if (*item == NULL)
2457135446Strhodes		return (ISC_R_NOMEMORY);
2458135446Strhodes
2459135446Strhodes	return (ISC_R_SUCCESS);
2460135446Strhodes}
2461135446Strhodes
2462135446Strhodesisc_result_t
2463135446Strhodesdns_message_gettemprdata(dns_message_t *msg, dns_rdata_t **item) {
2464135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2465135446Strhodes	REQUIRE(item != NULL && *item == NULL);
2466135446Strhodes
2467135446Strhodes	*item = newrdata(msg);
2468135446Strhodes	if (*item == NULL)
2469135446Strhodes		return (ISC_R_NOMEMORY);
2470135446Strhodes
2471135446Strhodes	return (ISC_R_SUCCESS);
2472135446Strhodes}
2473135446Strhodes
2474135446Strhodesisc_result_t
2475135446Strhodesdns_message_gettemprdataset(dns_message_t *msg, dns_rdataset_t **item) {
2476135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2477135446Strhodes	REQUIRE(item != NULL && *item == NULL);
2478135446Strhodes
2479135446Strhodes	*item = isc_mempool_get(msg->rdspool);
2480135446Strhodes	if (*item == NULL)
2481135446Strhodes		return (ISC_R_NOMEMORY);
2482135446Strhodes
2483135446Strhodes	dns_rdataset_init(*item);
2484135446Strhodes
2485135446Strhodes	return (ISC_R_SUCCESS);
2486135446Strhodes}
2487135446Strhodes
2488135446Strhodesisc_result_t
2489135446Strhodesdns_message_gettemprdatalist(dns_message_t *msg, dns_rdatalist_t **item) {
2490135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2491135446Strhodes	REQUIRE(item != NULL && *item == NULL);
2492135446Strhodes
2493135446Strhodes	*item = newrdatalist(msg);
2494135446Strhodes	if (*item == NULL)
2495135446Strhodes		return (ISC_R_NOMEMORY);
2496135446Strhodes
2497135446Strhodes	return (ISC_R_SUCCESS);
2498135446Strhodes}
2499135446Strhodes
2500135446Strhodesvoid
2501135446Strhodesdns_message_puttempname(dns_message_t *msg, dns_name_t **item) {
2502135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2503135446Strhodes	REQUIRE(item != NULL && *item != NULL);
2504135446Strhodes
2505135446Strhodes	if (dns_name_dynamic(*item))
2506135446Strhodes		dns_name_free(*item, msg->mctx);
2507135446Strhodes	isc_mempool_put(msg->namepool, *item);
2508135446Strhodes	*item = NULL;
2509135446Strhodes}
2510135446Strhodes
2511135446Strhodesvoid
2512135446Strhodesdns_message_puttemprdata(dns_message_t *msg, dns_rdata_t **item) {
2513135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2514135446Strhodes	REQUIRE(item != NULL && *item != NULL);
2515135446Strhodes
2516135446Strhodes	releaserdata(msg, *item);
2517135446Strhodes	*item = NULL;
2518135446Strhodes}
2519135446Strhodes
2520135446Strhodesvoid
2521135446Strhodesdns_message_puttemprdataset(dns_message_t *msg, dns_rdataset_t **item) {
2522135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2523135446Strhodes	REQUIRE(item != NULL && *item != NULL);
2524135446Strhodes
2525135446Strhodes	REQUIRE(!dns_rdataset_isassociated(*item));
2526135446Strhodes	isc_mempool_put(msg->rdspool, *item);
2527135446Strhodes	*item = NULL;
2528135446Strhodes}
2529135446Strhodes
2530135446Strhodesvoid
2531135446Strhodesdns_message_puttemprdatalist(dns_message_t *msg, dns_rdatalist_t **item) {
2532135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2533135446Strhodes	REQUIRE(item != NULL && *item != NULL);
2534135446Strhodes
2535135446Strhodes	releaserdatalist(msg, *item);
2536135446Strhodes	*item = NULL;
2537135446Strhodes}
2538135446Strhodes
2539135446Strhodesisc_result_t
2540135446Strhodesdns_message_peekheader(isc_buffer_t *source, dns_messageid_t *idp,
2541135446Strhodes		       unsigned int *flagsp)
2542135446Strhodes{
2543135446Strhodes	isc_region_t r;
2544135446Strhodes	isc_buffer_t buffer;
2545135446Strhodes	dns_messageid_t id;
2546135446Strhodes	unsigned int flags;
2547135446Strhodes
2548135446Strhodes	REQUIRE(source != NULL);
2549135446Strhodes
2550135446Strhodes	buffer = *source;
2551135446Strhodes
2552135446Strhodes	isc_buffer_remainingregion(&buffer, &r);
2553135446Strhodes	if (r.length < DNS_MESSAGE_HEADERLEN)
2554135446Strhodes		return (ISC_R_UNEXPECTEDEND);
2555135446Strhodes
2556135446Strhodes	id = isc_buffer_getuint16(&buffer);
2557135446Strhodes	flags = isc_buffer_getuint16(&buffer);
2558135446Strhodes	flags &= DNS_MESSAGE_FLAG_MASK;
2559135446Strhodes
2560135446Strhodes	if (flagsp != NULL)
2561135446Strhodes		*flagsp = flags;
2562135446Strhodes	if (idp != NULL)
2563135446Strhodes		*idp = id;
2564135446Strhodes
2565135446Strhodes	return (ISC_R_SUCCESS);
2566135446Strhodes}
2567135446Strhodes
2568135446Strhodesisc_result_t
2569135446Strhodesdns_message_reply(dns_message_t *msg, isc_boolean_t want_question_section) {
2570223812Sdougb	unsigned int clear_after;
2571135446Strhodes	isc_result_t result;
2572135446Strhodes
2573135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2574135446Strhodes	REQUIRE((msg->flags & DNS_MESSAGEFLAG_QR) == 0);
2575135446Strhodes
2576135446Strhodes	if (!msg->header_ok)
2577135446Strhodes		return (DNS_R_FORMERR);
2578135446Strhodes	if (msg->opcode != dns_opcode_query &&
2579135446Strhodes	    msg->opcode != dns_opcode_notify)
2580135446Strhodes		want_question_section = ISC_FALSE;
2581218384Sdougb	if (msg->opcode == dns_opcode_update)
2582223812Sdougb		clear_after = DNS_SECTION_PREREQUISITE;
2583218384Sdougb	else if (want_question_section) {
2584135446Strhodes		if (!msg->question_ok)
2585135446Strhodes			return (DNS_R_FORMERR);
2586223812Sdougb		clear_after = DNS_SECTION_ANSWER;
2587135446Strhodes	} else
2588223812Sdougb		clear_after = DNS_SECTION_QUESTION;
2589135446Strhodes	msg->from_to_wire = DNS_MESSAGE_INTENTRENDER;
2590223812Sdougb	msgresetnames(msg, clear_after);
2591135446Strhodes	msgresetopt(msg);
2592135446Strhodes	msgresetsigs(msg, ISC_TRUE);
2593135446Strhodes	msginitprivate(msg);
2594135446Strhodes	/*
2595135446Strhodes	 * We now clear most flags and then set QR, ensuring that the
2596135446Strhodes	 * reply's flags will be in a reasonable state.
2597135446Strhodes	 */
2598135446Strhodes	msg->flags &= DNS_MESSAGE_REPLYPRESERVE;
2599135446Strhodes	msg->flags |= DNS_MESSAGEFLAG_QR;
2600135446Strhodes
2601135446Strhodes	/*
2602135446Strhodes	 * This saves the query TSIG status, if the query was signed, and
2603135446Strhodes	 * reserves space in the reply for the TSIG.
2604135446Strhodes	 */
2605135446Strhodes	if (msg->tsigkey != NULL) {
2606135446Strhodes		unsigned int otherlen = 0;
2607135446Strhodes		msg->querytsigstatus = msg->tsigstatus;
2608135446Strhodes		msg->tsigstatus = dns_rcode_noerror;
2609135446Strhodes		if (msg->querytsigstatus == dns_tsigerror_badtime)
2610135446Strhodes			otherlen = 6;
2611135446Strhodes		msg->sig_reserved = spacefortsig(msg->tsigkey, otherlen);
2612135446Strhodes		result = dns_message_renderreserve(msg, msg->sig_reserved);
2613135446Strhodes		if (result != ISC_R_SUCCESS) {
2614135446Strhodes			msg->sig_reserved = 0;
2615135446Strhodes			return (result);
2616135446Strhodes		}
2617135446Strhodes	}
2618135446Strhodes	if (msg->saved.base != NULL) {
2619135446Strhodes		msg->query.base = msg->saved.base;
2620135446Strhodes		msg->query.length = msg->saved.length;
2621135446Strhodes		msg->free_query = msg->free_saved;
2622135446Strhodes		msg->saved.base = NULL;
2623135446Strhodes		msg->saved.length = 0;
2624135446Strhodes		msg->free_saved = 0;
2625135446Strhodes	}
2626135446Strhodes
2627135446Strhodes	return (ISC_R_SUCCESS);
2628135446Strhodes}
2629135446Strhodes
2630135446Strhodesdns_rdataset_t *
2631135446Strhodesdns_message_getopt(dns_message_t *msg) {
2632135446Strhodes
2633135446Strhodes	/*
2634135446Strhodes	 * Get the OPT record for 'msg'.
2635135446Strhodes	 */
2636135446Strhodes
2637135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2638135446Strhodes
2639135446Strhodes	return (msg->opt);
2640135446Strhodes}
2641135446Strhodes
2642135446Strhodesisc_result_t
2643135446Strhodesdns_message_setopt(dns_message_t *msg, dns_rdataset_t *opt) {
2644135446Strhodes	isc_result_t result;
2645135446Strhodes	dns_rdata_t rdata = DNS_RDATA_INIT;
2646135446Strhodes
2647135446Strhodes	/*
2648135446Strhodes	 * Set the OPT record for 'msg'.
2649135446Strhodes	 */
2650135446Strhodes
2651135446Strhodes	/*
2652135446Strhodes	 * The space required for an OPT record is:
2653135446Strhodes	 *
2654135446Strhodes	 *	1 byte for the name
2655135446Strhodes	 *	2 bytes for the type
2656135446Strhodes	 *	2 bytes for the class
2657135446Strhodes	 *	4 bytes for the ttl
2658135446Strhodes	 *	2 bytes for the rdata length
2659135446Strhodes	 * ---------------------------------
2660135446Strhodes	 *     11 bytes
2661135446Strhodes	 *
2662135446Strhodes	 * plus the length of the rdata.
2663135446Strhodes	 */
2664135446Strhodes
2665135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2666135446Strhodes	REQUIRE(opt->type == dns_rdatatype_opt);
2667135446Strhodes	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
2668135446Strhodes	REQUIRE(msg->state == DNS_SECTION_ANY);
2669135446Strhodes
2670135446Strhodes	msgresetopt(msg);
2671135446Strhodes
2672135446Strhodes	result = dns_rdataset_first(opt);
2673135446Strhodes	if (result != ISC_R_SUCCESS)
2674135446Strhodes		goto cleanup;
2675135446Strhodes	dns_rdataset_current(opt, &rdata);
2676135446Strhodes	msg->opt_reserved = 11 + rdata.length;
2677135446Strhodes	result = dns_message_renderreserve(msg, msg->opt_reserved);
2678135446Strhodes	if (result != ISC_R_SUCCESS) {
2679135446Strhodes		msg->opt_reserved = 0;
2680135446Strhodes		goto cleanup;
2681135446Strhodes	}
2682135446Strhodes
2683135446Strhodes	msg->opt = opt;
2684135446Strhodes
2685135446Strhodes	return (ISC_R_SUCCESS);
2686135446Strhodes
2687135446Strhodes cleanup:
2688254402Serwin	dns_rdataset_disassociate(opt);
2689135446Strhodes	dns_message_puttemprdataset(msg, &opt);
2690135446Strhodes	return (result);
2691135446Strhodes}
2692135446Strhodes
2693135446Strhodesdns_rdataset_t *
2694135446Strhodesdns_message_gettsig(dns_message_t *msg, dns_name_t **owner) {
2695135446Strhodes
2696135446Strhodes	/*
2697135446Strhodes	 * Get the TSIG record and owner for 'msg'.
2698135446Strhodes	 */
2699135446Strhodes
2700135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2701135446Strhodes	REQUIRE(owner == NULL || *owner == NULL);
2702135446Strhodes
2703135446Strhodes	if (owner != NULL)
2704135446Strhodes		*owner = msg->tsigname;
2705135446Strhodes	return (msg->tsig);
2706135446Strhodes}
2707135446Strhodes
2708135446Strhodesisc_result_t
2709135446Strhodesdns_message_settsigkey(dns_message_t *msg, dns_tsigkey_t *key) {
2710135446Strhodes	isc_result_t result;
2711135446Strhodes
2712135446Strhodes	/*
2713135446Strhodes	 * Set the TSIG key for 'msg'
2714135446Strhodes	 */
2715135446Strhodes
2716135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2717135446Strhodes	REQUIRE(msg->state == DNS_SECTION_ANY);
2718135446Strhodes
2719135446Strhodes	if (key == NULL && msg->tsigkey != NULL) {
2720135446Strhodes		if (msg->sig_reserved != 0) {
2721135446Strhodes			dns_message_renderrelease(msg, msg->sig_reserved);
2722135446Strhodes			msg->sig_reserved = 0;
2723135446Strhodes		}
2724135446Strhodes		dns_tsigkey_detach(&msg->tsigkey);
2725135446Strhodes	}
2726135446Strhodes	if (key != NULL) {
2727135446Strhodes		REQUIRE(msg->tsigkey == NULL && msg->sig0key == NULL);
2728135446Strhodes		dns_tsigkey_attach(key, &msg->tsigkey);
2729135446Strhodes		if (msg->from_to_wire == DNS_MESSAGE_INTENTRENDER) {
2730135446Strhodes			msg->sig_reserved = spacefortsig(msg->tsigkey, 0);
2731135446Strhodes			result = dns_message_renderreserve(msg,
2732135446Strhodes							   msg->sig_reserved);
2733135446Strhodes			if (result != ISC_R_SUCCESS) {
2734135446Strhodes				dns_tsigkey_detach(&msg->tsigkey);
2735135446Strhodes				msg->sig_reserved = 0;
2736135446Strhodes				return (result);
2737135446Strhodes			}
2738135446Strhodes		}
2739135446Strhodes	}
2740135446Strhodes	return (ISC_R_SUCCESS);
2741135446Strhodes}
2742135446Strhodes
2743135446Strhodesdns_tsigkey_t *
2744135446Strhodesdns_message_gettsigkey(dns_message_t *msg) {
2745135446Strhodes
2746135446Strhodes	/*
2747135446Strhodes	 * Get the TSIG key for 'msg'
2748135446Strhodes	 */
2749135446Strhodes
2750135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2751135446Strhodes
2752135446Strhodes	return (msg->tsigkey);
2753135446Strhodes}
2754135446Strhodes
2755135446Strhodesisc_result_t
2756135446Strhodesdns_message_setquerytsig(dns_message_t *msg, isc_buffer_t *querytsig) {
2757135446Strhodes	dns_rdata_t *rdata = NULL;
2758135446Strhodes	dns_rdatalist_t *list = NULL;
2759135446Strhodes	dns_rdataset_t *set = NULL;
2760135446Strhodes	isc_buffer_t *buf = NULL;
2761135446Strhodes	isc_region_t r;
2762135446Strhodes	isc_result_t result;
2763135446Strhodes
2764135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2765135446Strhodes	REQUIRE(msg->querytsig == NULL);
2766135446Strhodes
2767135446Strhodes	if (querytsig == NULL)
2768135446Strhodes		return (ISC_R_SUCCESS);
2769135446Strhodes
2770135446Strhodes	result = dns_message_gettemprdata(msg, &rdata);
2771135446Strhodes	if (result != ISC_R_SUCCESS)
2772135446Strhodes		goto cleanup;
2773135446Strhodes
2774135446Strhodes	result = dns_message_gettemprdatalist(msg, &list);
2775135446Strhodes	if (result != ISC_R_SUCCESS)
2776135446Strhodes		goto cleanup;
2777135446Strhodes	result = dns_message_gettemprdataset(msg, &set);
2778135446Strhodes	if (result != ISC_R_SUCCESS)
2779135446Strhodes		goto cleanup;
2780135446Strhodes
2781135446Strhodes	isc_buffer_usedregion(querytsig, &r);
2782135446Strhodes	result = isc_buffer_allocate(msg->mctx, &buf, r.length);
2783135446Strhodes	if (result != ISC_R_SUCCESS)
2784135446Strhodes		goto cleanup;
2785135446Strhodes	isc_buffer_putmem(buf, r.base, r.length);
2786135446Strhodes	isc_buffer_usedregion(buf, &r);
2787135446Strhodes	dns_rdata_init(rdata);
2788135446Strhodes	dns_rdata_fromregion(rdata, dns_rdataclass_any, dns_rdatatype_tsig, &r);
2789135446Strhodes	dns_message_takebuffer(msg, &buf);
2790135446Strhodes	ISC_LIST_INIT(list->rdata);
2791135446Strhodes	ISC_LIST_APPEND(list->rdata, rdata, link);
2792135446Strhodes	result = dns_rdatalist_tordataset(list, set);
2793135446Strhodes	if (result != ISC_R_SUCCESS)
2794135446Strhodes		goto cleanup;
2795135446Strhodes
2796135446Strhodes	msg->querytsig = set;
2797135446Strhodes
2798135446Strhodes	return (result);
2799135446Strhodes
2800135446Strhodes cleanup:
2801135446Strhodes	if (rdata != NULL)
2802135446Strhodes		dns_message_puttemprdata(msg, &rdata);
2803135446Strhodes	if (list != NULL)
2804135446Strhodes		dns_message_puttemprdatalist(msg, &list);
2805135446Strhodes	if (set != NULL)
2806135446Strhodes		dns_message_puttemprdataset(msg, &set);
2807135446Strhodes	return (ISC_R_NOMEMORY);
2808135446Strhodes}
2809135446Strhodes
2810135446Strhodesisc_result_t
2811135446Strhodesdns_message_getquerytsig(dns_message_t *msg, isc_mem_t *mctx,
2812135446Strhodes			 isc_buffer_t **querytsig) {
2813135446Strhodes	isc_result_t result;
2814135446Strhodes	dns_rdata_t rdata = DNS_RDATA_INIT;
2815135446Strhodes	isc_region_t r;
2816135446Strhodes
2817135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2818135446Strhodes	REQUIRE(mctx != NULL);
2819135446Strhodes	REQUIRE(querytsig != NULL && *querytsig == NULL);
2820135446Strhodes
2821135446Strhodes	if (msg->tsig == NULL)
2822135446Strhodes		return (ISC_R_SUCCESS);
2823135446Strhodes
2824135446Strhodes	result = dns_rdataset_first(msg->tsig);
2825135446Strhodes	if (result != ISC_R_SUCCESS)
2826135446Strhodes		return (result);
2827135446Strhodes	dns_rdataset_current(msg->tsig, &rdata);
2828135446Strhodes	dns_rdata_toregion(&rdata, &r);
2829135446Strhodes
2830135446Strhodes	result = isc_buffer_allocate(mctx, querytsig, r.length);
2831135446Strhodes	if (result != ISC_R_SUCCESS)
2832135446Strhodes		return (result);
2833135446Strhodes	isc_buffer_putmem(*querytsig, r.base, r.length);
2834135446Strhodes	return (ISC_R_SUCCESS);
2835135446Strhodes}
2836135446Strhodes
2837135446Strhodesdns_rdataset_t *
2838135446Strhodesdns_message_getsig0(dns_message_t *msg, dns_name_t **owner) {
2839135446Strhodes
2840135446Strhodes	/*
2841135446Strhodes	 * Get the SIG(0) record for 'msg'.
2842135446Strhodes	 */
2843135446Strhodes
2844135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2845135446Strhodes	REQUIRE(owner == NULL || *owner == NULL);
2846135446Strhodes
2847135446Strhodes	if (msg->sig0 != NULL && owner != NULL) {
2848135446Strhodes		/* If dns_message_getsig0 is called on a rendered message
2849135446Strhodes		 * after the SIG(0) has been applied, we need to return the
2850135446Strhodes		 * root name, not NULL.
2851135446Strhodes		 */
2852135446Strhodes		if (msg->sig0name == NULL)
2853135446Strhodes			*owner = dns_rootname;
2854135446Strhodes		else
2855135446Strhodes			*owner = msg->sig0name;
2856135446Strhodes	}
2857135446Strhodes	return (msg->sig0);
2858135446Strhodes}
2859135446Strhodes
2860135446Strhodesisc_result_t
2861135446Strhodesdns_message_setsig0key(dns_message_t *msg, dst_key_t *key) {
2862135446Strhodes	isc_region_t r;
2863135446Strhodes	unsigned int x;
2864135446Strhodes	isc_result_t result;
2865135446Strhodes
2866135446Strhodes	/*
2867135446Strhodes	 * Set the SIG(0) key for 'msg'
2868135446Strhodes	 */
2869135446Strhodes
2870135446Strhodes	/*
2871135446Strhodes	 * The space required for an SIG(0) record is:
2872135446Strhodes	 *
2873135446Strhodes	 *	1 byte for the name
2874135446Strhodes	 *	2 bytes for the type
2875135446Strhodes	 *	2 bytes for the class
2876135446Strhodes	 *	4 bytes for the ttl
2877135446Strhodes	 *	2 bytes for the type covered
2878135446Strhodes	 *	1 byte for the algorithm
2879135446Strhodes	 *	1 bytes for the labels
2880135446Strhodes	 *	4 bytes for the original ttl
2881135446Strhodes	 *	4 bytes for the signature expiration
2882135446Strhodes	 *	4 bytes for the signature inception
2883135446Strhodes	 *	2 bytes for the key tag
2884135446Strhodes	 *	n bytes for the signer's name
2885135446Strhodes	 *	x bytes for the signature
2886135446Strhodes	 * ---------------------------------
2887135446Strhodes	 *     27 + n + x bytes
2888135446Strhodes	 */
2889135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2890135446Strhodes	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
2891135446Strhodes	REQUIRE(msg->state == DNS_SECTION_ANY);
2892135446Strhodes
2893135446Strhodes	if (key != NULL) {
2894135446Strhodes		REQUIRE(msg->sig0key == NULL && msg->tsigkey == NULL);
2895135446Strhodes		dns_name_toregion(dst_key_name(key), &r);
2896135446Strhodes		result = dst_key_sigsize(key, &x);
2897135446Strhodes		if (result != ISC_R_SUCCESS) {
2898135446Strhodes			msg->sig_reserved = 0;
2899135446Strhodes			return (result);
2900135446Strhodes		}
2901135446Strhodes		msg->sig_reserved = 27 + r.length + x;
2902135446Strhodes		result = dns_message_renderreserve(msg, msg->sig_reserved);
2903135446Strhodes		if (result != ISC_R_SUCCESS) {
2904135446Strhodes			msg->sig_reserved = 0;
2905135446Strhodes			return (result);
2906135446Strhodes		}
2907135446Strhodes		msg->sig0key = key;
2908135446Strhodes	}
2909135446Strhodes	return (ISC_R_SUCCESS);
2910135446Strhodes}
2911135446Strhodes
2912135446Strhodesdst_key_t *
2913135446Strhodesdns_message_getsig0key(dns_message_t *msg) {
2914135446Strhodes
2915135446Strhodes	/*
2916135446Strhodes	 * Get the SIG(0) key for 'msg'
2917135446Strhodes	 */
2918135446Strhodes
2919135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2920135446Strhodes
2921135446Strhodes	return (msg->sig0key);
2922135446Strhodes}
2923135446Strhodes
2924135446Strhodesvoid
2925135446Strhodesdns_message_takebuffer(dns_message_t *msg, isc_buffer_t **buffer) {
2926135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2927135446Strhodes	REQUIRE(buffer != NULL);
2928135446Strhodes	REQUIRE(ISC_BUFFER_VALID(*buffer));
2929135446Strhodes
2930135446Strhodes	ISC_LIST_APPEND(msg->cleanup, *buffer, link);
2931135446Strhodes	*buffer = NULL;
2932135446Strhodes}
2933135446Strhodes
2934135446Strhodesisc_result_t
2935135446Strhodesdns_message_signer(dns_message_t *msg, dns_name_t *signer) {
2936135446Strhodes	isc_result_t result = ISC_R_SUCCESS;
2937135446Strhodes	dns_rdata_t rdata = DNS_RDATA_INIT;
2938135446Strhodes
2939135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
2940135446Strhodes	REQUIRE(signer != NULL);
2941135446Strhodes	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTPARSE);
2942135446Strhodes
2943135446Strhodes	if (msg->tsig == NULL && msg->sig0 == NULL)
2944135446Strhodes		return (ISC_R_NOTFOUND);
2945135446Strhodes
2946135446Strhodes	if (msg->verify_attempted == 0)
2947135446Strhodes		return (DNS_R_NOTVERIFIEDYET);
2948135446Strhodes
2949135446Strhodes	if (!dns_name_hasbuffer(signer)) {
2950135446Strhodes		isc_buffer_t *dynbuf = NULL;
2951135446Strhodes		result = isc_buffer_allocate(msg->mctx, &dynbuf, 512);
2952135446Strhodes		if (result != ISC_R_SUCCESS)
2953135446Strhodes			return (result);
2954135446Strhodes		dns_name_setbuffer(signer, dynbuf);
2955135446Strhodes		dns_message_takebuffer(msg, &dynbuf);
2956135446Strhodes	}
2957135446Strhodes
2958135446Strhodes	if (msg->sig0 != NULL) {
2959135446Strhodes		dns_rdata_sig_t sig;
2960135446Strhodes
2961135446Strhodes		result = dns_rdataset_first(msg->sig0);
2962135446Strhodes		INSIST(result == ISC_R_SUCCESS);
2963135446Strhodes		dns_rdataset_current(msg->sig0, &rdata);
2964135446Strhodes
2965135446Strhodes		result = dns_rdata_tostruct(&rdata, &sig, NULL);
2966135446Strhodes		if (result != ISC_R_SUCCESS)
2967135446Strhodes			return (result);
2968135446Strhodes
2969135446Strhodes		if (msg->verified_sig && msg->sig0status == dns_rcode_noerror)
2970135446Strhodes			result = ISC_R_SUCCESS;
2971135446Strhodes		else
2972135446Strhodes			result = DNS_R_SIGINVALID;
2973135446Strhodes		dns_name_clone(&sig.signer, signer);
2974135446Strhodes		dns_rdata_freestruct(&sig);
2975135446Strhodes	} else {
2976135446Strhodes		dns_name_t *identity;
2977135446Strhodes		dns_rdata_any_tsig_t tsig;
2978135446Strhodes
2979135446Strhodes		result = dns_rdataset_first(msg->tsig);
2980135446Strhodes		INSIST(result == ISC_R_SUCCESS);
2981135446Strhodes		dns_rdataset_current(msg->tsig, &rdata);
2982135446Strhodes
2983135446Strhodes		result = dns_rdata_tostruct(&rdata, &tsig, NULL);
2984225361Sdougb		INSIST(result == ISC_R_SUCCESS);
2985135446Strhodes		if (msg->tsigstatus != dns_rcode_noerror)
2986135446Strhodes			result = DNS_R_TSIGVERIFYFAILURE;
2987135446Strhodes		else if (tsig.error != dns_rcode_noerror)
2988135446Strhodes			result = DNS_R_TSIGERRORSET;
2989135446Strhodes		else
2990135446Strhodes			result = ISC_R_SUCCESS;
2991135446Strhodes		dns_rdata_freestruct(&tsig);
2992135446Strhodes
2993135446Strhodes		if (msg->tsigkey == NULL) {
2994135446Strhodes			/*
2995135446Strhodes			 * If msg->tsigstatus & tsig.error are both
2996135446Strhodes			 * dns_rcode_noerror, the message must have been
2997135446Strhodes			 * verified, which means msg->tsigkey will be
2998135446Strhodes			 * non-NULL.
2999135446Strhodes			 */
3000135446Strhodes			INSIST(result != ISC_R_SUCCESS);
3001135446Strhodes		} else {
3002135446Strhodes			identity = dns_tsigkey_identity(msg->tsigkey);
3003135446Strhodes			if (identity == NULL) {
3004135446Strhodes				if (result == ISC_R_SUCCESS)
3005135446Strhodes					result = DNS_R_NOIDENTITY;
3006135446Strhodes				identity = &msg->tsigkey->name;
3007135446Strhodes			}
3008135446Strhodes			dns_name_clone(identity, signer);
3009135446Strhodes		}
3010135446Strhodes	}
3011135446Strhodes
3012135446Strhodes	return (result);
3013135446Strhodes}
3014135446Strhodes
3015135446Strhodesvoid
3016135446Strhodesdns_message_resetsig(dns_message_t *msg) {
3017135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
3018135446Strhodes	msg->verified_sig = 0;
3019135446Strhodes	msg->verify_attempted = 0;
3020135446Strhodes	msg->tsigstatus = dns_rcode_noerror;
3021135446Strhodes	msg->sig0status = dns_rcode_noerror;
3022135446Strhodes	msg->timeadjust = 0;
3023135446Strhodes	if (msg->tsigkey != NULL) {
3024135446Strhodes		dns_tsigkey_detach(&msg->tsigkey);
3025135446Strhodes		msg->tsigkey = NULL;
3026135446Strhodes	}
3027135446Strhodes}
3028135446Strhodes
3029135446Strhodesisc_result_t
3030135446Strhodesdns_message_rechecksig(dns_message_t *msg, dns_view_t *view) {
3031135446Strhodes	dns_message_resetsig(msg);
3032135446Strhodes	return (dns_message_checksig(msg, view));
3033135446Strhodes}
3034135446Strhodes
3035193149Sdougb#ifdef SKAN_MSG_DEBUG
3036193149Sdougbvoid
3037193149Sdougbdns_message_dumpsig(dns_message_t *msg, char *txt1) {
3038193149Sdougb	dns_rdata_t querytsigrdata = DNS_RDATA_INIT;
3039193149Sdougb	dns_rdata_any_tsig_t querytsig;
3040193149Sdougb	isc_result_t result;
3041193149Sdougb
3042193149Sdougb	if (msg->tsig != NULL) {
3043193149Sdougb		result = dns_rdataset_first(msg->tsig);
3044193149Sdougb		RUNTIME_CHECK(result == ISC_R_SUCCESS);
3045193149Sdougb		dns_rdataset_current(msg->tsig, &querytsigrdata);
3046193149Sdougb		result = dns_rdata_tostruct(&querytsigrdata, &querytsig, NULL);
3047193149Sdougb		RUNTIME_CHECK(result == ISC_R_SUCCESS);
3048193149Sdougb		hexdump(txt1, "TSIG", querytsig.signature,
3049193149Sdougb			querytsig.siglen);
3050193149Sdougb	}
3051193149Sdougb
3052193149Sdougb	if (msg->querytsig != NULL) {
3053193149Sdougb		result = dns_rdataset_first(msg->querytsig);
3054193149Sdougb		RUNTIME_CHECK(result == ISC_R_SUCCESS);
3055193149Sdougb		dns_rdataset_current(msg->querytsig, &querytsigrdata);
3056193149Sdougb		result = dns_rdata_tostruct(&querytsigrdata, &querytsig, NULL);
3057193149Sdougb		RUNTIME_CHECK(result == ISC_R_SUCCESS);
3058193149Sdougb		hexdump(txt1, "QUERYTSIG", querytsig.signature,
3059193149Sdougb			querytsig.siglen);
3060193149Sdougb	}
3061193149Sdougb}
3062193149Sdougb#endif
3063193149Sdougb
3064135446Strhodesisc_result_t
3065135446Strhodesdns_message_checksig(dns_message_t *msg, dns_view_t *view) {
3066135446Strhodes	isc_buffer_t b, msgb;
3067135446Strhodes
3068135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
3069135446Strhodes
3070135446Strhodes	if (msg->tsigkey == NULL && msg->tsig == NULL && msg->sig0 == NULL)
3071135446Strhodes		return (ISC_R_SUCCESS);
3072193149Sdougb
3073135446Strhodes	INSIST(msg->saved.base != NULL);
3074135446Strhodes	isc_buffer_init(&msgb, msg->saved.base, msg->saved.length);
3075135446Strhodes	isc_buffer_add(&msgb, msg->saved.length);
3076135446Strhodes	if (msg->tsigkey != NULL || msg->tsig != NULL) {
3077193149Sdougb#ifdef SKAN_MSG_DEBUG
3078193149Sdougb		dns_message_dumpsig(msg, "dns_message_checksig#1");
3079193149Sdougb#endif
3080135446Strhodes		if (view != NULL)
3081135446Strhodes			return (dns_view_checksig(view, &msgb, msg));
3082135446Strhodes		else
3083135446Strhodes			return (dns_tsig_verify(&msgb, msg, NULL, NULL));
3084135446Strhodes	} else {
3085135446Strhodes		dns_rdata_t rdata = DNS_RDATA_INIT;
3086135446Strhodes		dns_rdata_sig_t sig;
3087135446Strhodes		dns_rdataset_t keyset;
3088135446Strhodes		isc_result_t result;
3089135446Strhodes
3090135446Strhodes		result = dns_rdataset_first(msg->sig0);
3091135446Strhodes		INSIST(result == ISC_R_SUCCESS);
3092135446Strhodes		dns_rdataset_current(msg->sig0, &rdata);
3093135446Strhodes
3094135446Strhodes		/*
3095135446Strhodes		 * This can occur when the message is a dynamic update, since
3096135446Strhodes		 * the rdata length checking is relaxed.  This should not
3097135446Strhodes		 * happen in a well-formed message, since the SIG(0) is only
3098135446Strhodes		 * looked for in the additional section, and the dynamic update
3099135446Strhodes		 * meta-records are in the prerequisite and update sections.
3100135446Strhodes		 */
3101135446Strhodes		if (rdata.length == 0)
3102135446Strhodes			return (ISC_R_UNEXPECTEDEND);
3103135446Strhodes
3104135446Strhodes		result = dns_rdata_tostruct(&rdata, &sig, msg->mctx);
3105135446Strhodes		if (result != ISC_R_SUCCESS)
3106135446Strhodes			return (result);
3107135446Strhodes
3108135446Strhodes		dns_rdataset_init(&keyset);
3109135446Strhodes		if (view == NULL)
3110135446Strhodes			return (DNS_R_KEYUNAUTHORIZED);
3111135446Strhodes		result = dns_view_simplefind(view, &sig.signer,
3112135446Strhodes					     dns_rdatatype_key /* SIG(0) */,
3113135446Strhodes					     0, 0, ISC_FALSE, &keyset, NULL);
3114135446Strhodes
3115135446Strhodes		if (result != ISC_R_SUCCESS) {
3116135446Strhodes			/* XXXBEW Should possibly create a fetch here */
3117135446Strhodes			result = DNS_R_KEYUNAUTHORIZED;
3118135446Strhodes			goto freesig;
3119135446Strhodes		} else if (keyset.trust < dns_trust_secure) {
3120135446Strhodes			/* XXXBEW Should call a validator here */
3121135446Strhodes			result = DNS_R_KEYUNAUTHORIZED;
3122135446Strhodes			goto freesig;
3123135446Strhodes		}
3124135446Strhodes		result = dns_rdataset_first(&keyset);
3125135446Strhodes		INSIST(result == ISC_R_SUCCESS);
3126135446Strhodes		for (;
3127135446Strhodes		     result == ISC_R_SUCCESS;
3128135446Strhodes		     result = dns_rdataset_next(&keyset))
3129135446Strhodes		{
3130135446Strhodes			dst_key_t *key = NULL;
3131135446Strhodes
3132193149Sdougb			dns_rdata_reset(&rdata);
3133135446Strhodes			dns_rdataset_current(&keyset, &rdata);
3134135446Strhodes			isc_buffer_init(&b, rdata.data, rdata.length);
3135135446Strhodes			isc_buffer_add(&b, rdata.length);
3136135446Strhodes
3137135446Strhodes			result = dst_key_fromdns(&sig.signer, rdata.rdclass,
3138135446Strhodes						 &b, view->mctx, &key);
3139135446Strhodes			if (result != ISC_R_SUCCESS)
3140135446Strhodes				continue;
3141135446Strhodes			if (dst_key_alg(key) != sig.algorithm ||
3142135446Strhodes			    dst_key_id(key) != sig.keyid ||
3143135446Strhodes			    !(dst_key_proto(key) == DNS_KEYPROTO_DNSSEC ||
3144135446Strhodes			      dst_key_proto(key) == DNS_KEYPROTO_ANY))
3145135446Strhodes			{
3146135446Strhodes				dst_key_free(&key);
3147135446Strhodes				continue;
3148135446Strhodes			}
3149135446Strhodes			result = dns_dnssec_verifymessage(&msgb, msg, key);
3150135446Strhodes			dst_key_free(&key);
3151135446Strhodes			if (result == ISC_R_SUCCESS)
3152135446Strhodes				break;
3153135446Strhodes		}
3154135446Strhodes		if (result == ISC_R_NOMORE)
3155135446Strhodes			result = DNS_R_KEYUNAUTHORIZED;
3156135446Strhodes
3157135446Strhodes freesig:
3158135446Strhodes		if (dns_rdataset_isassociated(&keyset))
3159135446Strhodes			dns_rdataset_disassociate(&keyset);
3160135446Strhodes		dns_rdata_freestruct(&sig);
3161135446Strhodes		return (result);
3162135446Strhodes	}
3163135446Strhodes}
3164135446Strhodes
3165135446Strhodesisc_result_t
3166135446Strhodesdns_message_sectiontotext(dns_message_t *msg, dns_section_t section,
3167135446Strhodes			  const dns_master_style_t *style,
3168135446Strhodes			  dns_messagetextflag_t flags,
3169135446Strhodes			  isc_buffer_t *target) {
3170135446Strhodes	dns_name_t *name, empty_name;
3171135446Strhodes	dns_rdataset_t *rdataset;
3172135446Strhodes	isc_result_t result;
3173224092Sdougb	isc_boolean_t seensoa = ISC_FALSE;
3174135446Strhodes
3175135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
3176135446Strhodes	REQUIRE(target != NULL);
3177135446Strhodes	REQUIRE(VALID_SECTION(section));
3178135446Strhodes
3179135446Strhodes	if (ISC_LIST_EMPTY(msg->sections[section]))
3180135446Strhodes		return (ISC_R_SUCCESS);
3181135446Strhodes
3182135446Strhodes	if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) {
3183135446Strhodes		ADD_STRING(target, ";; ");
3184135446Strhodes		if (msg->opcode != dns_opcode_update) {
3185135446Strhodes			ADD_STRING(target, sectiontext[section]);
3186174187Sdougb		} else {
3187135446Strhodes			ADD_STRING(target, updsectiontext[section]);
3188135446Strhodes		}
3189135446Strhodes		ADD_STRING(target, " SECTION:\n");
3190135446Strhodes	}
3191135446Strhodes
3192135446Strhodes	dns_name_init(&empty_name, NULL);
3193135446Strhodes	result = dns_message_firstname(msg, section);
3194135446Strhodes	if (result != ISC_R_SUCCESS) {
3195135446Strhodes		return (result);
3196135446Strhodes	}
3197135446Strhodes	do {
3198135446Strhodes		name = NULL;
3199135446Strhodes		dns_message_currentname(msg, section, &name);
3200135446Strhodes		for (rdataset = ISC_LIST_HEAD(name->list);
3201135446Strhodes		     rdataset != NULL;
3202135446Strhodes		     rdataset = ISC_LIST_NEXT(rdataset, link)) {
3203224092Sdougb			if (section == DNS_SECTION_ANSWER &&
3204224092Sdougb			    rdataset->type == dns_rdatatype_soa) {
3205224092Sdougb				if ((flags & DNS_MESSAGETEXTFLAG_OMITSOA) != 0)
3206224092Sdougb					continue;
3207224092Sdougb				if (seensoa &&
3208224092Sdougb				    (flags & DNS_MESSAGETEXTFLAG_ONESOA) != 0)
3209224092Sdougb					continue;
3210224092Sdougb				seensoa = ISC_TRUE;
3211224092Sdougb			}
3212135446Strhodes			if (section == DNS_SECTION_QUESTION) {
3213135446Strhodes				ADD_STRING(target, ";");
3214135446Strhodes				result = dns_master_questiontotext(name,
3215135446Strhodes								   rdataset,
3216135446Strhodes								   style,
3217135446Strhodes								   target);
3218135446Strhodes			} else {
3219135446Strhodes				result = dns_master_rdatasettotext(name,
3220135446Strhodes								   rdataset,
3221135446Strhodes								   style,
3222135446Strhodes								   target);
3223135446Strhodes			}
3224135446Strhodes			if (result != ISC_R_SUCCESS)
3225135446Strhodes				return (result);
3226135446Strhodes		}
3227135446Strhodes		result = dns_message_nextname(msg, section);
3228135446Strhodes	} while (result == ISC_R_SUCCESS);
3229135446Strhodes	if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0 &&
3230135446Strhodes	    (flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
3231135446Strhodes		ADD_STRING(target, "\n");
3232135446Strhodes	if (result == ISC_R_NOMORE)
3233135446Strhodes		result = ISC_R_SUCCESS;
3234135446Strhodes	return (result);
3235135446Strhodes}
3236135446Strhodes
3237135446Strhodesisc_result_t
3238135446Strhodesdns_message_pseudosectiontotext(dns_message_t *msg,
3239135446Strhodes				dns_pseudosection_t section,
3240135446Strhodes				const dns_master_style_t *style,
3241135446Strhodes				dns_messagetextflag_t flags,
3242135446Strhodes				isc_buffer_t *target) {
3243135446Strhodes	dns_rdataset_t *ps = NULL;
3244135446Strhodes	dns_name_t *name = NULL;
3245135446Strhodes	isc_result_t result;
3246135446Strhodes	char buf[sizeof("1234567890")];
3247135446Strhodes	isc_uint32_t mbz;
3248193149Sdougb	dns_rdata_t rdata;
3249193149Sdougb	isc_buffer_t optbuf;
3250193149Sdougb	isc_uint16_t optcode, optlen;
3251193149Sdougb	unsigned char *optdata;
3252135446Strhodes
3253135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
3254135446Strhodes	REQUIRE(target != NULL);
3255135446Strhodes	REQUIRE(VALID_PSEUDOSECTION(section));
3256135446Strhodes
3257135446Strhodes	switch (section) {
3258135446Strhodes	case DNS_PSEUDOSECTION_OPT:
3259135446Strhodes		ps = dns_message_getopt(msg);
3260135446Strhodes		if (ps == NULL)
3261135446Strhodes			return (ISC_R_SUCCESS);
3262135446Strhodes		if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
3263135446Strhodes			ADD_STRING(target, ";; OPT PSEUDOSECTION:\n");
3264135446Strhodes		ADD_STRING(target, "; EDNS: version: ");
3265135446Strhodes		snprintf(buf, sizeof(buf), "%u",
3266135446Strhodes			 (unsigned int)((ps->ttl & 0x00ff0000) >> 16));
3267135446Strhodes		ADD_STRING(target, buf);
3268135446Strhodes		ADD_STRING(target, ", flags:");
3269135446Strhodes		if ((ps->ttl & DNS_MESSAGEEXTFLAG_DO) != 0)
3270135446Strhodes			ADD_STRING(target, " do");
3271218384Sdougb		mbz = ps->ttl & 0xffff;
3272218384Sdougb		mbz &= ~DNS_MESSAGEEXTFLAG_DO;		/* Known Flags. */
3273135446Strhodes		if (mbz != 0) {
3274135446Strhodes			ADD_STRING(target, "; MBZ: ");
3275135446Strhodes			snprintf(buf, sizeof(buf), "%.4x ", mbz);
3276135446Strhodes			ADD_STRING(target, buf);
3277135446Strhodes			ADD_STRING(target, ", udp: ");
3278135446Strhodes		} else
3279135446Strhodes			ADD_STRING(target, "; udp: ");
3280135446Strhodes		snprintf(buf, sizeof(buf), "%u\n", (unsigned int)ps->rdclass);
3281135446Strhodes		ADD_STRING(target, buf);
3282193149Sdougb
3283193149Sdougb		result = dns_rdataset_first(ps);
3284193149Sdougb		if (result != ISC_R_SUCCESS)
3285193149Sdougb			return (ISC_R_SUCCESS);
3286193149Sdougb
3287193149Sdougb		/* Print EDNS info, if any */
3288193149Sdougb		dns_rdata_init(&rdata);
3289193149Sdougb		dns_rdataset_current(ps, &rdata);
3290193149Sdougb
3291193149Sdougb		isc_buffer_init(&optbuf, rdata.data, rdata.length);
3292193149Sdougb		isc_buffer_add(&optbuf, rdata.length);
3293218384Sdougb		while (isc_buffer_remaininglength(&optbuf) != 0) {
3294218384Sdougb			INSIST(isc_buffer_remaininglength(&optbuf) >= 4U);
3295218384Sdougb			optcode = isc_buffer_getuint16(&optbuf);
3296218384Sdougb			optlen = isc_buffer_getuint16(&optbuf);
3297218384Sdougb			INSIST(isc_buffer_remaininglength(&optbuf) >= optlen);
3298193149Sdougb
3299218384Sdougb			if (optcode == DNS_OPT_NSID) {
3300218384Sdougb				ADD_STRING(target, "; NSID");
3301218384Sdougb			} else {
3302218384Sdougb				ADD_STRING(target, "; OPT=");
3303218384Sdougb				sprintf(buf, "%u", optcode);
3304218384Sdougb				ADD_STRING(target, buf);
3305218384Sdougb			}
3306193149Sdougb
3307218384Sdougb			if (optlen != 0) {
3308218384Sdougb				int i;
3309218384Sdougb				ADD_STRING(target, ": ");
3310193149Sdougb
3311218384Sdougb				optdata = isc_buffer_current(&optbuf);
3312218384Sdougb				for (i = 0; i < optlen; i++) {
3313218384Sdougb					sprintf(buf, "%02x ", optdata[i]);
3314218384Sdougb					ADD_STRING(target, buf);
3315218384Sdougb				}
3316218384Sdougb				for (i = 0; i < optlen; i++) {
3317218384Sdougb					ADD_STRING(target, " (");
3318218384Sdougb					if (isprint(optdata[i]))
3319218384Sdougb						isc_buffer_putmem(target,
3320218384Sdougb								  &optdata[i],
3321218384Sdougb								  1);
3322218384Sdougb					else
3323218384Sdougb						isc_buffer_putstr(target, ".");
3324218384Sdougb					ADD_STRING(target, ")");
3325218384Sdougb				}
3326218384Sdougb				isc_buffer_forward(&optbuf, optlen);
3327193149Sdougb			}
3328218384Sdougb			ADD_STRING(target, "\n");
3329193149Sdougb		}
3330135446Strhodes		return (ISC_R_SUCCESS);
3331135446Strhodes	case DNS_PSEUDOSECTION_TSIG:
3332135446Strhodes		ps = dns_message_gettsig(msg, &name);
3333135446Strhodes		if (ps == NULL)
3334135446Strhodes			return (ISC_R_SUCCESS);
3335135446Strhodes		if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
3336135446Strhodes			ADD_STRING(target, ";; TSIG PSEUDOSECTION:\n");
3337135446Strhodes		result = dns_master_rdatasettotext(name, ps, style, target);
3338135446Strhodes		if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0 &&
3339135446Strhodes		    (flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
3340135446Strhodes			ADD_STRING(target, "\n");
3341135446Strhodes		return (result);
3342135446Strhodes	case DNS_PSEUDOSECTION_SIG0:
3343135446Strhodes		ps = dns_message_getsig0(msg, &name);
3344135446Strhodes		if (ps == NULL)
3345135446Strhodes			return (ISC_R_SUCCESS);
3346135446Strhodes		if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
3347135446Strhodes			ADD_STRING(target, ";; SIG0 PSEUDOSECTION:\n");
3348135446Strhodes		result = dns_master_rdatasettotext(name, ps, style, target);
3349135446Strhodes		if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0 &&
3350135446Strhodes		    (flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
3351135446Strhodes			ADD_STRING(target, "\n");
3352135446Strhodes		return (result);
3353135446Strhodes	}
3354135446Strhodes	return (ISC_R_UNEXPECTED);
3355135446Strhodes}
3356135446Strhodes
3357135446Strhodesisc_result_t
3358135446Strhodesdns_message_totext(dns_message_t *msg, const dns_master_style_t *style,
3359135446Strhodes		   dns_messagetextflag_t flags, isc_buffer_t *target) {
3360135446Strhodes	char buf[sizeof("1234567890")];
3361135446Strhodes	isc_result_t result;
3362135446Strhodes
3363135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
3364135446Strhodes	REQUIRE(target != NULL);
3365135446Strhodes
3366135446Strhodes	if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0) {
3367135446Strhodes		ADD_STRING(target, ";; ->>HEADER<<- opcode: ");
3368135446Strhodes		ADD_STRING(target, opcodetext[msg->opcode]);
3369135446Strhodes		ADD_STRING(target, ", status: ");
3370174187Sdougb		if (msg->rcode < (sizeof(rcodetext)/sizeof(rcodetext[0]))) {
3371174187Sdougb			ADD_STRING(target, rcodetext[msg->rcode]);
3372174187Sdougb		} else {
3373174187Sdougb			snprintf(buf, sizeof(buf), "%4u", msg->rcode);
3374174187Sdougb			ADD_STRING(target, buf);
3375174187Sdougb		}
3376135446Strhodes		ADD_STRING(target, ", id: ");
3377135446Strhodes		snprintf(buf, sizeof(buf), "%6u", msg->id);
3378135446Strhodes		ADD_STRING(target, buf);
3379218384Sdougb		ADD_STRING(target, "\n;; flags:");
3380135446Strhodes		if ((msg->flags & DNS_MESSAGEFLAG_QR) != 0)
3381218384Sdougb			ADD_STRING(target, " qr");
3382135446Strhodes		if ((msg->flags & DNS_MESSAGEFLAG_AA) != 0)
3383218384Sdougb			ADD_STRING(target, " aa");
3384135446Strhodes		if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0)
3385218384Sdougb			ADD_STRING(target, " tc");
3386135446Strhodes		if ((msg->flags & DNS_MESSAGEFLAG_RD) != 0)
3387218384Sdougb			ADD_STRING(target, " rd");
3388135446Strhodes		if ((msg->flags & DNS_MESSAGEFLAG_RA) != 0)
3389218384Sdougb			ADD_STRING(target, " ra");
3390135446Strhodes		if ((msg->flags & DNS_MESSAGEFLAG_AD) != 0)
3391218384Sdougb			ADD_STRING(target, " ad");
3392135446Strhodes		if ((msg->flags & DNS_MESSAGEFLAG_CD) != 0)
3393218384Sdougb			ADD_STRING(target, " cd");
3394218384Sdougb		/*
3395218384Sdougb		 * The final unnamed flag must be zero.
3396218384Sdougb		 */
3397218384Sdougb		if ((msg->flags & 0x0040U) != 0)
3398218384Sdougb			ADD_STRING(target, "; MBZ: 0x4");
3399135446Strhodes		if (msg->opcode != dns_opcode_update) {
3400135446Strhodes			ADD_STRING(target, "; QUESTION: ");
3401135446Strhodes		} else {
3402135446Strhodes			ADD_STRING(target, "; ZONE: ");
3403135446Strhodes		}
3404135446Strhodes		snprintf(buf, sizeof(buf), "%1u",
3405135446Strhodes			 msg->counts[DNS_SECTION_QUESTION]);
3406135446Strhodes		ADD_STRING(target, buf);
3407135446Strhodes		if (msg->opcode != dns_opcode_update) {
3408135446Strhodes			ADD_STRING(target, ", ANSWER: ");
3409135446Strhodes		} else {
3410135446Strhodes			ADD_STRING(target, ", PREREQ: ");
3411135446Strhodes		}
3412135446Strhodes		snprintf(buf, sizeof(buf), "%1u",
3413135446Strhodes			 msg->counts[DNS_SECTION_ANSWER]);
3414135446Strhodes		ADD_STRING(target, buf);
3415135446Strhodes		if (msg->opcode != dns_opcode_update) {
3416135446Strhodes			ADD_STRING(target, ", AUTHORITY: ");
3417135446Strhodes		} else {
3418135446Strhodes			ADD_STRING(target, ", UPDATE: ");
3419135446Strhodes		}
3420135446Strhodes		snprintf(buf, sizeof(buf), "%1u",
3421135446Strhodes			msg->counts[DNS_SECTION_AUTHORITY]);
3422135446Strhodes		ADD_STRING(target, buf);
3423135446Strhodes		ADD_STRING(target, ", ADDITIONAL: ");
3424135446Strhodes		snprintf(buf, sizeof(buf), "%1u",
3425135446Strhodes			msg->counts[DNS_SECTION_ADDITIONAL]);
3426135446Strhodes		ADD_STRING(target, buf);
3427135446Strhodes		ADD_STRING(target, "\n");
3428135446Strhodes	}
3429135446Strhodes	result = dns_message_pseudosectiontotext(msg,
3430135446Strhodes						 DNS_PSEUDOSECTION_OPT,
3431135446Strhodes						 style, flags, target);
3432135446Strhodes	if (result != ISC_R_SUCCESS)
3433135446Strhodes		return (result);
3434135446Strhodes
3435135446Strhodes	result = dns_message_sectiontotext(msg, DNS_SECTION_QUESTION,
3436135446Strhodes					   style, flags, target);
3437135446Strhodes	if (result != ISC_R_SUCCESS)
3438135446Strhodes		return (result);
3439135446Strhodes	result = dns_message_sectiontotext(msg, DNS_SECTION_ANSWER,
3440135446Strhodes					   style, flags, target);
3441135446Strhodes	if (result != ISC_R_SUCCESS)
3442135446Strhodes		return (result);
3443135446Strhodes	result = dns_message_sectiontotext(msg, DNS_SECTION_AUTHORITY,
3444135446Strhodes					   style, flags, target);
3445135446Strhodes	if (result != ISC_R_SUCCESS)
3446135446Strhodes		return (result);
3447135446Strhodes	result = dns_message_sectiontotext(msg, DNS_SECTION_ADDITIONAL,
3448135446Strhodes					   style, flags, target);
3449135446Strhodes	if (result != ISC_R_SUCCESS)
3450135446Strhodes		return (result);
3451135446Strhodes
3452135446Strhodes	result = dns_message_pseudosectiontotext(msg,
3453135446Strhodes						 DNS_PSEUDOSECTION_TSIG,
3454135446Strhodes						 style, flags, target);
3455135446Strhodes	if (result != ISC_R_SUCCESS)
3456135446Strhodes		return (result);
3457135446Strhodes
3458135446Strhodes	result = dns_message_pseudosectiontotext(msg,
3459135446Strhodes						 DNS_PSEUDOSECTION_SIG0,
3460135446Strhodes						 style, flags, target);
3461135446Strhodes	if (result != ISC_R_SUCCESS)
3462135446Strhodes		return (result);
3463135446Strhodes
3464135446Strhodes	return (ISC_R_SUCCESS);
3465135446Strhodes}
3466135446Strhodes
3467135446Strhodesisc_region_t *
3468135446Strhodesdns_message_getrawmessage(dns_message_t *msg) {
3469135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
3470135446Strhodes	return (&msg->saved);
3471135446Strhodes}
3472135446Strhodes
3473135446Strhodesvoid
3474135446Strhodesdns_message_setsortorder(dns_message_t *msg, dns_rdatasetorderfunc_t order,
3475165071Sdougb			 const void *order_arg)
3476135446Strhodes{
3477135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
3478135446Strhodes	msg->order = order;
3479135446Strhodes	msg->order_arg = order_arg;
3480135446Strhodes}
3481135446Strhodes
3482135446Strhodesvoid
3483135446Strhodesdns_message_settimeadjust(dns_message_t *msg, int timeadjust) {
3484135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
3485135446Strhodes	msg->timeadjust = timeadjust;
3486135446Strhodes}
3487135446Strhodes
3488135446Strhodesint
3489135446Strhodesdns_message_gettimeadjust(dns_message_t *msg) {
3490135446Strhodes	REQUIRE(DNS_MESSAGE_VALID(msg));
3491135446Strhodes	return (msg->timeadjust);
3492135446Strhodes}
3493135446Strhodes
3494135446Strhodesisc_result_t
3495135446Strhodesdns_opcode_totext(dns_opcode_t opcode, isc_buffer_t *target) {
3496135446Strhodes
3497135446Strhodes	REQUIRE(opcode < 16);
3498135446Strhodes
3499135446Strhodes	if (isc_buffer_availablelength(target) < strlen(opcodetext[opcode]))
3500135446Strhodes		return (ISC_R_NOSPACE);
3501135446Strhodes	isc_buffer_putstr(target, opcodetext[opcode]);
3502135446Strhodes	return (ISC_R_SUCCESS);
3503135446Strhodes}
3504254402Serwin
3505254402Serwinisc_result_t
3506254402Serwindns_message_buildopt(dns_message_t *message, dns_rdataset_t **rdatasetp,
3507254402Serwin		     unsigned int version, isc_uint16_t udpsize,
3508254402Serwin		     unsigned int flags, dns_ednsopt_t *ednsopts, size_t count)
3509254402Serwin{
3510254402Serwin	dns_rdataset_t *rdataset = NULL;
3511254402Serwin	dns_rdatalist_t *rdatalist = NULL;
3512254402Serwin	dns_rdata_t *rdata = NULL;
3513254402Serwin	isc_result_t result;
3514262706Serwin	unsigned int len = 0, i;
3515254402Serwin
3516254402Serwin	REQUIRE(DNS_MESSAGE_VALID(message));
3517254402Serwin	REQUIRE(rdatasetp != NULL && *rdatasetp == NULL);
3518254402Serwin
3519254402Serwin	result = dns_message_gettemprdatalist(message, &rdatalist);
3520254402Serwin	if (result != ISC_R_SUCCESS)
3521254402Serwin		return (result);
3522254402Serwin	result = dns_message_gettemprdata(message, &rdata);
3523254402Serwin	if (result != ISC_R_SUCCESS)
3524254402Serwin		goto cleanup;
3525254402Serwin	result = dns_message_gettemprdataset(message, &rdataset);
3526254402Serwin	if (result != ISC_R_SUCCESS)
3527254402Serwin		goto cleanup;
3528254402Serwin	dns_rdataset_init(rdataset);
3529254402Serwin
3530254402Serwin	rdatalist->type = dns_rdatatype_opt;
3531254402Serwin	rdatalist->covers = 0;
3532254402Serwin
3533254402Serwin	/*
3534254402Serwin	 * Set Maximum UDP buffer size.
3535254402Serwin	 */
3536254402Serwin	rdatalist->rdclass = udpsize;
3537254402Serwin
3538254402Serwin	/*
3539254402Serwin	 * Set EXTENDED-RCODE and Z to 0.
3540254402Serwin	 */
3541254402Serwin	rdatalist->ttl = (version << 16);
3542254402Serwin	rdatalist->ttl |= (flags & 0xffff);
3543254402Serwin
3544254402Serwin	/*
3545254402Serwin	 * Set EDNS options if applicable
3546254402Serwin	 */
3547254402Serwin	if (count != 0U) {
3548254402Serwin		isc_buffer_t *buf = NULL;
3549254402Serwin		for (i = 0; i < count; i++)
3550254402Serwin			len += ednsopts[i].length + 4;
3551254402Serwin
3552254402Serwin		if (len > 0xffffU) {
3553254402Serwin			result = ISC_R_NOSPACE;
3554254402Serwin			goto cleanup;
3555254402Serwin		}
3556254402Serwin
3557254402Serwin		result = isc_buffer_allocate(message->mctx, &buf, len);
3558254402Serwin		if (result != ISC_R_SUCCESS)
3559254402Serwin			goto cleanup;
3560254402Serwin
3561254402Serwin		for (i = 0; i < count; i++)  {
3562254402Serwin			isc_buffer_putuint16(buf, ednsopts[i].code);
3563254402Serwin			isc_buffer_putuint16(buf, ednsopts[i].length);
3564254402Serwin			isc_buffer_putmem(buf, ednsopts[i].value,
3565254402Serwin					  ednsopts[i].length);
3566254402Serwin		}
3567254402Serwin		rdata->data = isc_buffer_base(buf);
3568254402Serwin		rdata->length = len;
3569254402Serwin		dns_message_takebuffer(message, &buf);
3570254402Serwin	} else {
3571254402Serwin		rdata->data = NULL;
3572254402Serwin		rdata->length = 0;
3573254402Serwin	}
3574254402Serwin
3575254402Serwin	rdata->rdclass = rdatalist->rdclass;
3576254402Serwin	rdata->type = rdatalist->type;
3577254402Serwin	rdata->flags = 0;
3578254402Serwin
3579254402Serwin	ISC_LIST_INIT(rdatalist->rdata);
3580254402Serwin	ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
3581254402Serwin	result = dns_rdatalist_tordataset(rdatalist, rdataset);
3582254402Serwin	RUNTIME_CHECK(result == ISC_R_SUCCESS);
3583254402Serwin
3584254402Serwin	*rdatasetp = rdataset;
3585254402Serwin	return (ISC_R_SUCCESS);
3586254402Serwin
3587254402Serwin cleanup:
3588254402Serwin	if (rdata != NULL)
3589254402Serwin		dns_message_puttemprdata(message, &rdata);
3590254402Serwin	if (rdataset != NULL)
3591254402Serwin		dns_message_puttemprdataset(message, &rdataset);
3592254402Serwin	if (rdatalist != NULL)
3593254402Serwin		dns_message_puttemprdatalist(message, &rdatalist);
3594254402Serwin	return (result);
3595254402Serwin}
3596292321Sdelphij
3597292321Sdelphijvoid
3598292321Sdelphijdns_message_setclass(dns_message_t *msg, dns_rdataclass_t rdclass) {
3599292321Sdelphij
3600292321Sdelphij	REQUIRE(DNS_MESSAGE_VALID(msg));
3601292321Sdelphij	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTPARSE);
3602292321Sdelphij	REQUIRE(msg->state == DNS_SECTION_ANY);
3603292321Sdelphij	REQUIRE(msg->rdclass_set == 0);
3604292321Sdelphij
3605292321Sdelphij	msg->rdclass = rdclass;
3606292321Sdelphij	msg->rdclass_set = 1;
3607292321Sdelphij}
3608