xref: /openbsd-src/usr.bin/dig/lib/dns/message.c (revision 9835a5e128ac1db1a0a5e36c27d1a17b7926c0b3)
15185a700Sflorian /*
25185a700Sflorian  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
35185a700Sflorian  *
45185a700Sflorian  * Permission to use, copy, modify, and/or distribute this software for any
55185a700Sflorian  * purpose with or without fee is hereby granted, provided that the above
65185a700Sflorian  * copyright notice and this permission notice appear in all copies.
75185a700Sflorian  *
85185a700Sflorian  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
95185a700Sflorian  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
105185a700Sflorian  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
115185a700Sflorian  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
125185a700Sflorian  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
135185a700Sflorian  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
145185a700Sflorian  * PERFORMANCE OF THIS SOFTWARE.
155185a700Sflorian  */
165185a700Sflorian 
17*9835a5e1Sflorian /* $Id: message.c,v 1.22 2024/12/27 09:04:48 florian Exp $ */
185185a700Sflorian 
195185a700Sflorian /*! \file */
205185a700Sflorian 
215185a700Sflorian /***
225185a700Sflorian  *** Imports
235185a700Sflorian  ***/
245185a700Sflorian 
2524ff385aSflorian #include <sys/socket.h>
2624ff385aSflorian #include <arpa/inet.h>
275185a700Sflorian 
285185a700Sflorian #include <ctype.h>
295185a700Sflorian #include <stdlib.h>
3024ff385aSflorian #include <string.h>
315185a700Sflorian 
325185a700Sflorian #include <isc/buffer.h>
335185a700Sflorian #include <isc/util.h>
345185a700Sflorian 
355185a700Sflorian #include <dns/log.h>
365185a700Sflorian #include <dns/masterdump.h>
375185a700Sflorian #include <dns/message.h>
385185a700Sflorian #include <dns/rdata.h>
395185a700Sflorian #include <dns/rdatalist.h>
405185a700Sflorian #include <dns/rdataset.h>
415185a700Sflorian #include <dns/result.h>
425185a700Sflorian #include <dns/tsig.h>
435185a700Sflorian #include <dns/ttl.h>
445185a700Sflorian 
455185a700Sflorian #define DNS_MESSAGE_OPCODE_MASK		0x7800U
465185a700Sflorian #define DNS_MESSAGE_OPCODE_SHIFT	11
475185a700Sflorian #define DNS_MESSAGE_RCODE_MASK		0x000fU
485185a700Sflorian #define DNS_MESSAGE_FLAG_MASK		0x8ff0U
495185a700Sflorian #define DNS_MESSAGE_EDNSRCODE_MASK	0xff000000U
505185a700Sflorian 
515185a700Sflorian #define VALID_NAMED_SECTION(s)  (((s) > DNS_SECTION_ANY) \
525185a700Sflorian 				 && ((s) < DNS_SECTION_MAX))
535185a700Sflorian #define VALID_SECTION(s)	(((s) >= DNS_SECTION_ANY) \
545185a700Sflorian 				 && ((s) < DNS_SECTION_MAX))
555185a700Sflorian #define ADD_STRING(b, s)	{if (strlen(s) >= \
565185a700Sflorian 				   isc_buffer_availablelength(b)) \
575185a700Sflorian 				       return(ISC_R_NOSPACE); else \
585185a700Sflorian 				       isc_buffer_putstr(b, s);}
595185a700Sflorian #define VALID_PSEUDOSECTION(s)	(((s) >= DNS_PSEUDOSECTION_ANY) \
605185a700Sflorian 				 && ((s) < DNS_PSEUDOSECTION_MAX))
615185a700Sflorian 
625185a700Sflorian /*%
635185a700Sflorian  * This is the size of each individual scratchpad buffer, and the numbers
645185a700Sflorian  * of various block allocations used within the server.
655185a700Sflorian  * XXXMLG These should come from a config setting.
665185a700Sflorian  */
675185a700Sflorian #define SCRATCHPAD_SIZE		512
685185a700Sflorian #define OFFSET_COUNT		  4
695185a700Sflorian #define RDATA_COUNT		  8
705185a700Sflorian #define RDATALIST_COUNT		  8
715185a700Sflorian 
725185a700Sflorian /*%
735185a700Sflorian  * Text representation of the different items, for message_totext
745185a700Sflorian  * functions.
755185a700Sflorian  */
765185a700Sflorian static const char *sectiontext[] = {
775185a700Sflorian 	"QUESTION",
785185a700Sflorian 	"ANSWER",
795185a700Sflorian 	"AUTHORITY",
805185a700Sflorian 	"ADDITIONAL"
815185a700Sflorian };
825185a700Sflorian 
835185a700Sflorian static const char *updsectiontext[] = {
845185a700Sflorian 	"ZONE",
855185a700Sflorian 	"PREREQUISITE",
865185a700Sflorian 	"UPDATE",
875185a700Sflorian 	"ADDITIONAL"
885185a700Sflorian };
895185a700Sflorian 
905185a700Sflorian /*%
915185a700Sflorian  * "helper" type, which consists of a block of some type, and is linkable.
925185a700Sflorian  * For it to work, sizeof(dns_msgblock_t) must be a multiple of the pointer
935185a700Sflorian  * size, or the allocated elements will not be aligned correctly.
945185a700Sflorian  */
955185a700Sflorian struct dns_msgblock {
965185a700Sflorian 	unsigned int			count;
975185a700Sflorian 	unsigned int			remaining;
985185a700Sflorian 	ISC_LINK(dns_msgblock_t)	link;
995185a700Sflorian }; /* dynamically sized */
1005185a700Sflorian 
1015185a700Sflorian static inline dns_msgblock_t *
1025185a700Sflorian msgblock_allocate(unsigned int, unsigned int);
1035185a700Sflorian 
1045185a700Sflorian #define msgblock_get(block, type) \
1055185a700Sflorian 	((type *)msgblock_internalget(block, sizeof(type)))
1065185a700Sflorian 
1075185a700Sflorian static inline void *
1085185a700Sflorian msgblock_internalget(dns_msgblock_t *, unsigned int);
1095185a700Sflorian 
1105185a700Sflorian static inline void
1115185a700Sflorian msgblock_reset(dns_msgblock_t *);
1125185a700Sflorian 
1135185a700Sflorian /*
1145185a700Sflorian  * Allocate a new dns_msgblock_t, and return a pointer to it.  If no memory
1155185a700Sflorian  * is free, return NULL.
1165185a700Sflorian  */
1175185a700Sflorian static inline dns_msgblock_t *
1185185a700Sflorian msgblock_allocate(unsigned int sizeof_type,
1195185a700Sflorian 		  unsigned int count)
1205185a700Sflorian {
1215185a700Sflorian 	dns_msgblock_t *block;
1225185a700Sflorian 	unsigned int length;
1235185a700Sflorian 
1245185a700Sflorian 	length = sizeof(dns_msgblock_t) + (sizeof_type * count);
1255185a700Sflorian 
1265185a700Sflorian 	block = malloc(length);
1275185a700Sflorian 	if (block == NULL)
1285185a700Sflorian 		return (NULL);
1295185a700Sflorian 
1305185a700Sflorian 	block->count = count;
1315185a700Sflorian 	block->remaining = count;
1325185a700Sflorian 
1335185a700Sflorian 	ISC_LINK_INIT(block, link);
1345185a700Sflorian 
1355185a700Sflorian 	return (block);
1365185a700Sflorian }
1375185a700Sflorian 
1385185a700Sflorian /*
1395185a700Sflorian  * Return an element from the msgblock.  If no more are available, return
1405185a700Sflorian  * NULL.
1415185a700Sflorian  */
1425185a700Sflorian static inline void *
1435185a700Sflorian msgblock_internalget(dns_msgblock_t *block, unsigned int sizeof_type) {
1445185a700Sflorian 	void *ptr;
1455185a700Sflorian 
1465185a700Sflorian 	if (block == NULL || block->remaining == 0)
1475185a700Sflorian 		return (NULL);
1485185a700Sflorian 
1495185a700Sflorian 	block->remaining--;
1505185a700Sflorian 
1515185a700Sflorian 	ptr = (((unsigned char *)block)
1525185a700Sflorian 	       + sizeof(dns_msgblock_t)
1535185a700Sflorian 	       + (sizeof_type * block->remaining));
1545185a700Sflorian 
1555185a700Sflorian 	return (ptr);
1565185a700Sflorian }
1575185a700Sflorian 
1585185a700Sflorian static inline void
1595185a700Sflorian msgblock_reset(dns_msgblock_t *block) {
1605185a700Sflorian 	block->remaining = block->count;
1615185a700Sflorian }
1625185a700Sflorian 
1635185a700Sflorian /*
1645185a700Sflorian  * Allocate a new dynamic buffer, and attach it to this message as the
1655185a700Sflorian  * "current" buffer.  (which is always the last on the list, for our
1665185a700Sflorian  * uses)
1675185a700Sflorian  */
1685185a700Sflorian static inline isc_result_t
1695185a700Sflorian newbuffer(dns_message_t *msg, unsigned int size) {
1705185a700Sflorian 	isc_result_t result;
1715185a700Sflorian 	isc_buffer_t *dynbuf;
1725185a700Sflorian 
1735185a700Sflorian 	dynbuf = NULL;
1745185a700Sflorian 	result = isc_buffer_allocate(&dynbuf, size);
1755185a700Sflorian 	if (result != ISC_R_SUCCESS)
1765185a700Sflorian 		return (ISC_R_NOMEMORY);
1775185a700Sflorian 
1785185a700Sflorian 	ISC_LIST_APPEND(msg->scratchpad, dynbuf, link);
1795185a700Sflorian 	return (ISC_R_SUCCESS);
1805185a700Sflorian }
1815185a700Sflorian 
1825185a700Sflorian static inline isc_buffer_t *
1835185a700Sflorian currentbuffer(dns_message_t *msg) {
1845185a700Sflorian 	isc_buffer_t *dynbuf;
1855185a700Sflorian 
1865185a700Sflorian 	dynbuf = ISC_LIST_TAIL(msg->scratchpad);
1875185a700Sflorian 	INSIST(dynbuf != NULL);
1885185a700Sflorian 
1895185a700Sflorian 	return (dynbuf);
1905185a700Sflorian }
1915185a700Sflorian 
1925185a700Sflorian static inline void
1935185a700Sflorian releaserdata(dns_message_t *msg, dns_rdata_t *rdata) {
1945185a700Sflorian 	ISC_LIST_PREPEND(msg->freerdata, rdata, link);
1955185a700Sflorian }
1965185a700Sflorian 
1975185a700Sflorian static inline dns_rdata_t *
1985185a700Sflorian newrdata(dns_message_t *msg) {
1995185a700Sflorian 	dns_msgblock_t *msgblock;
2005185a700Sflorian 	dns_rdata_t *rdata;
2015185a700Sflorian 
2025185a700Sflorian 	rdata = ISC_LIST_HEAD(msg->freerdata);
2035185a700Sflorian 	if (rdata != NULL) {
2045185a700Sflorian 		ISC_LIST_UNLINK(msg->freerdata, rdata, link);
2055185a700Sflorian 		return (rdata);
2065185a700Sflorian 	}
2075185a700Sflorian 
2085185a700Sflorian 	msgblock = ISC_LIST_TAIL(msg->rdatas);
2095185a700Sflorian 	rdata = msgblock_get(msgblock, dns_rdata_t);
2105185a700Sflorian 	if (rdata == NULL) {
2115185a700Sflorian 		msgblock = msgblock_allocate(sizeof(dns_rdata_t), RDATA_COUNT);
2125185a700Sflorian 		if (msgblock == NULL)
2135185a700Sflorian 			return (NULL);
2145185a700Sflorian 
2155185a700Sflorian 		ISC_LIST_APPEND(msg->rdatas, msgblock, link);
2165185a700Sflorian 
2175185a700Sflorian 		rdata = msgblock_get(msgblock, dns_rdata_t);
2185185a700Sflorian 	}
2195185a700Sflorian 
2205185a700Sflorian 	dns_rdata_init(rdata);
2215185a700Sflorian 	return (rdata);
2225185a700Sflorian }
2235185a700Sflorian 
2245185a700Sflorian static inline void
2255185a700Sflorian releaserdatalist(dns_message_t *msg, dns_rdatalist_t *rdatalist) {
2265185a700Sflorian 	ISC_LIST_PREPEND(msg->freerdatalist, rdatalist, link);
2275185a700Sflorian }
2285185a700Sflorian 
2295185a700Sflorian static inline dns_rdatalist_t *
2305185a700Sflorian newrdatalist(dns_message_t *msg) {
2315185a700Sflorian 	dns_msgblock_t *msgblock;
2325185a700Sflorian 	dns_rdatalist_t *rdatalist;
2335185a700Sflorian 
2345185a700Sflorian 	rdatalist = ISC_LIST_HEAD(msg->freerdatalist);
2355185a700Sflorian 	if (rdatalist != NULL) {
2365185a700Sflorian 		ISC_LIST_UNLINK(msg->freerdatalist, rdatalist, link);
2375185a700Sflorian 		goto out;
2385185a700Sflorian 	}
2395185a700Sflorian 
2405185a700Sflorian 	msgblock = ISC_LIST_TAIL(msg->rdatalists);
2415185a700Sflorian 	rdatalist = msgblock_get(msgblock, dns_rdatalist_t);
2425185a700Sflorian 	if (rdatalist == NULL) {
2435185a700Sflorian 		msgblock = msgblock_allocate(sizeof(dns_rdatalist_t),
2445185a700Sflorian 					     RDATALIST_COUNT);
2455185a700Sflorian 		if (msgblock == NULL)
2465185a700Sflorian 			return (NULL);
2475185a700Sflorian 
2485185a700Sflorian 		ISC_LIST_APPEND(msg->rdatalists, msgblock, link);
2495185a700Sflorian 
2505185a700Sflorian 		rdatalist = msgblock_get(msgblock, dns_rdatalist_t);
2515185a700Sflorian 	}
2525185a700Sflorian  out:
2535185a700Sflorian 	if (rdatalist != NULL)
2545185a700Sflorian 		dns_rdatalist_init(rdatalist);
2555185a700Sflorian 
2565185a700Sflorian 	return (rdatalist);
2575185a700Sflorian }
2585185a700Sflorian 
2595185a700Sflorian static inline dns_offsets_t *
2605185a700Sflorian newoffsets(dns_message_t *msg) {
2615185a700Sflorian 	dns_msgblock_t *msgblock;
2625185a700Sflorian 	dns_offsets_t *offsets;
2635185a700Sflorian 
2645185a700Sflorian 	msgblock = ISC_LIST_TAIL(msg->offsets);
2655185a700Sflorian 	offsets = msgblock_get(msgblock, dns_offsets_t);
2665185a700Sflorian 	if (offsets == NULL) {
2675185a700Sflorian 		msgblock = msgblock_allocate(sizeof(dns_offsets_t),
2685185a700Sflorian 					     OFFSET_COUNT);
2695185a700Sflorian 		if (msgblock == NULL)
2705185a700Sflorian 			return (NULL);
2715185a700Sflorian 
2725185a700Sflorian 		ISC_LIST_APPEND(msg->offsets, msgblock, link);
2735185a700Sflorian 
2745185a700Sflorian 		offsets = msgblock_get(msgblock, dns_offsets_t);
2755185a700Sflorian 	}
2765185a700Sflorian 
2775185a700Sflorian 	return (offsets);
2785185a700Sflorian }
2795185a700Sflorian 
2805185a700Sflorian static inline void
2815185a700Sflorian msginitheader(dns_message_t *m) {
2825185a700Sflorian 	m->id = 0;
2835185a700Sflorian 	m->flags = 0;
2845185a700Sflorian 	m->rcode = 0;
2855185a700Sflorian 	m->opcode = 0;
2865185a700Sflorian 	m->rdclass = 0;
2875185a700Sflorian }
2885185a700Sflorian 
2895185a700Sflorian static inline void
2905185a700Sflorian msginitprivate(dns_message_t *m) {
2915185a700Sflorian 	unsigned int i;
2925185a700Sflorian 
2935185a700Sflorian 	for (i = 0; i < DNS_SECTION_MAX; i++) {
2945185a700Sflorian 		m->cursors[i] = NULL;
2955185a700Sflorian 		m->counts[i] = 0;
2965185a700Sflorian 	}
2975185a700Sflorian 	m->opt = NULL;
2985185a700Sflorian 	m->sig0 = NULL;
2995185a700Sflorian 	m->sig0name = NULL;
3005185a700Sflorian 	m->tsig = NULL;
3015185a700Sflorian 	m->tsigname = NULL;
3025185a700Sflorian 	m->state = DNS_SECTION_ANY;  /* indicate nothing parsed or rendered */
3035185a700Sflorian 	m->opt_reserved = 0;
3045185a700Sflorian 	m->sig_reserved = 0;
3055185a700Sflorian 	m->reserved = 0;
3065185a700Sflorian 	m->buffer = NULL;
3075185a700Sflorian }
3085185a700Sflorian 
3095185a700Sflorian static inline void
3105185a700Sflorian msginittsig(dns_message_t *m) {
3115185a700Sflorian 	m->tsigstatus = dns_rcode_noerror;
3125185a700Sflorian 	m->querytsigstatus = dns_rcode_noerror;
3135185a700Sflorian 	m->tsigkey = NULL;
3145185a700Sflorian 	m->tsigctx = NULL;
3155185a700Sflorian 	m->sigstart = -1;
3165185a700Sflorian 	m->sig0status = dns_rcode_noerror;
3175185a700Sflorian 	m->timeadjust = 0;
3185185a700Sflorian }
3195185a700Sflorian 
3205185a700Sflorian /*
3215185a700Sflorian  * Init elements to default state.  Used both when allocating a new element
3225185a700Sflorian  * and when resetting one.
3235185a700Sflorian  */
3245185a700Sflorian static inline void
3255185a700Sflorian msginit(dns_message_t *m) {
3265185a700Sflorian 	msginitheader(m);
3275185a700Sflorian 	msginitprivate(m);
3285185a700Sflorian 	msginittsig(m);
3295185a700Sflorian 	m->header_ok = 0;
3305185a700Sflorian 	m->question_ok = 0;
3315185a700Sflorian 	m->tcp_continuation = 0;
3325185a700Sflorian 	m->verified_sig = 0;
3335185a700Sflorian 	m->verify_attempted = 0;
3345185a700Sflorian 	m->query.base = NULL;
3355185a700Sflorian 	m->query.length = 0;
3365185a700Sflorian 	m->free_query = 0;
3375185a700Sflorian 	m->saved.base = NULL;
3385185a700Sflorian 	m->saved.length = 0;
3395185a700Sflorian 	m->free_saved = 0;
3405185a700Sflorian 	m->sitok = 0;
3415185a700Sflorian 	m->sitbad = 0;
3425185a700Sflorian 	m->tkey = 0;
3435185a700Sflorian 	m->rdclass_set = 0;
3445185a700Sflorian 	m->querytsig = NULL;
3455185a700Sflorian }
3465185a700Sflorian 
3475185a700Sflorian static inline void
3485185a700Sflorian msgresetnames(dns_message_t *msg, unsigned int first_section) {
3495185a700Sflorian 	unsigned int i;
3505185a700Sflorian 	dns_name_t *name, *next_name;
3515185a700Sflorian 	dns_rdataset_t *rds, *next_rds;
3525185a700Sflorian 
3535185a700Sflorian 	/*
3545185a700Sflorian 	 * Clean up name lists by calling the rdataset disassociate function.
3555185a700Sflorian 	 */
3565185a700Sflorian 	for (i = first_section; i < DNS_SECTION_MAX; i++) {
3575185a700Sflorian 		name = ISC_LIST_HEAD(msg->sections[i]);
3585185a700Sflorian 		while (name != NULL) {
3595185a700Sflorian 			next_name = ISC_LIST_NEXT(name, link);
3605185a700Sflorian 			ISC_LIST_UNLINK(msg->sections[i], name, link);
3615185a700Sflorian 
3625185a700Sflorian 			rds = ISC_LIST_HEAD(name->list);
3635185a700Sflorian 			while (rds != NULL) {
3645185a700Sflorian 				next_rds = ISC_LIST_NEXT(rds, link);
3655185a700Sflorian 				ISC_LIST_UNLINK(name->list, rds, link);
3665185a700Sflorian 
3675185a700Sflorian 				INSIST(dns_rdataset_isassociated(rds));
3685185a700Sflorian 				dns_rdataset_disassociate(rds);
3695185a700Sflorian 				free(rds);
3705185a700Sflorian 				rds = next_rds;
3715185a700Sflorian 			}
3725185a700Sflorian 			if (dns_name_dynamic(name))
3735185a700Sflorian 				dns_name_free(name);
3745185a700Sflorian 			free(name);
3755185a700Sflorian 			name = next_name;
3765185a700Sflorian 		}
3775185a700Sflorian 	}
3785185a700Sflorian }
3795185a700Sflorian 
3805185a700Sflorian static void
3815185a700Sflorian msgresetopt(dns_message_t *msg)
3825185a700Sflorian {
3835185a700Sflorian 	if (msg->opt != NULL) {
3845185a700Sflorian 		if (msg->opt_reserved > 0) {
3855185a700Sflorian 			dns_message_renderrelease(msg, msg->opt_reserved);
3865185a700Sflorian 			msg->opt_reserved = 0;
3875185a700Sflorian 		}
3885185a700Sflorian 		INSIST(dns_rdataset_isassociated(msg->opt));
3895185a700Sflorian 		dns_rdataset_disassociate(msg->opt);
3905185a700Sflorian 		free(msg->opt);
3915185a700Sflorian 		msg->opt = NULL;
3925185a700Sflorian 		msg->sitok = 0;
3935185a700Sflorian 		msg->sitbad = 0;
3945185a700Sflorian 	}
3955185a700Sflorian }
3965185a700Sflorian 
3975185a700Sflorian static void
3981fb015a8Sflorian msgresetsigs(dns_message_t *msg, int replying) {
3995185a700Sflorian 	if (msg->sig_reserved > 0) {
4005185a700Sflorian 		dns_message_renderrelease(msg, msg->sig_reserved);
4015185a700Sflorian 		msg->sig_reserved = 0;
4025185a700Sflorian 	}
4035185a700Sflorian 	if (msg->tsig != NULL) {
4045185a700Sflorian 		INSIST(dns_rdataset_isassociated(msg->tsig));
4055185a700Sflorian 		if (replying) {
4065185a700Sflorian 			INSIST(msg->querytsig == NULL);
4075185a700Sflorian 			msg->querytsig = msg->tsig;
4085185a700Sflorian 		} else {
4095185a700Sflorian 			dns_rdataset_disassociate(msg->tsig);
4105185a700Sflorian 			free(msg->tsig);
4115185a700Sflorian 			if (msg->querytsig != NULL) {
4125185a700Sflorian 				dns_rdataset_disassociate(msg->querytsig);
4135185a700Sflorian 				free(msg->querytsig);
4145185a700Sflorian 			}
4155185a700Sflorian 		}
4165185a700Sflorian 		if (dns_name_dynamic(msg->tsigname))
4175185a700Sflorian 			dns_name_free(msg->tsigname);
4185185a700Sflorian 		free(msg->tsigname);
4195185a700Sflorian 		msg->tsig = NULL;
4205185a700Sflorian 		msg->tsigname = NULL;
4215185a700Sflorian 	} else if (msg->querytsig != NULL && !replying) {
4225185a700Sflorian 		dns_rdataset_disassociate(msg->querytsig);
4235185a700Sflorian 		free(msg->querytsig);
4245185a700Sflorian 		msg->querytsig = NULL;
4255185a700Sflorian 	}
4265185a700Sflorian 	if (msg->sig0 != NULL) {
4275185a700Sflorian 		INSIST(dns_rdataset_isassociated(msg->sig0));
4285185a700Sflorian 		dns_rdataset_disassociate(msg->sig0);
4295185a700Sflorian 		free(msg->sig0);
4305185a700Sflorian 		if (msg->sig0name != NULL) {
4315185a700Sflorian 			if (dns_name_dynamic(msg->sig0name))
4325185a700Sflorian 				dns_name_free(msg->sig0name);
4335185a700Sflorian 			free(msg->sig0name);
4345185a700Sflorian 		}
4355185a700Sflorian 		msg->sig0 = NULL;
4365185a700Sflorian 		msg->sig0name = NULL;
4375185a700Sflorian 	}
4385185a700Sflorian }
4395185a700Sflorian 
4405185a700Sflorian /*
4415185a700Sflorian  * Free all but one (or everything) for this message.  This is used by
4425185a700Sflorian  * both dns_message_reset() and dns_message_destroy().
4435185a700Sflorian  */
4445185a700Sflorian static void
4451fb015a8Sflorian msgreset(dns_message_t *msg, int everything) {
4465185a700Sflorian 	dns_msgblock_t *msgblock, *next_msgblock;
4475185a700Sflorian 	isc_buffer_t *dynbuf, *next_dynbuf;
4485185a700Sflorian 	dns_rdata_t *rdata;
4495185a700Sflorian 	dns_rdatalist_t *rdatalist;
4505185a700Sflorian 
4515185a700Sflorian 	msgresetnames(msg, 0);
4525185a700Sflorian 	msgresetopt(msg);
4531fb015a8Sflorian 	msgresetsigs(msg, 0);
4545185a700Sflorian 
4555185a700Sflorian 	/*
4565185a700Sflorian 	 * Clean up linked lists.
4575185a700Sflorian 	 */
4585185a700Sflorian 
4595185a700Sflorian 	/*
4605185a700Sflorian 	 * Run through the free lists, and just unlink anything found there.
4615185a700Sflorian 	 * The memory isn't lost since these are part of message blocks we
4625185a700Sflorian 	 * have allocated.
4635185a700Sflorian 	 */
4645185a700Sflorian 	rdata = ISC_LIST_HEAD(msg->freerdata);
4655185a700Sflorian 	while (rdata != NULL) {
4665185a700Sflorian 		ISC_LIST_UNLINK(msg->freerdata, rdata, link);
4675185a700Sflorian 		rdata = ISC_LIST_HEAD(msg->freerdata);
4685185a700Sflorian 	}
4695185a700Sflorian 	rdatalist = ISC_LIST_HEAD(msg->freerdatalist);
4705185a700Sflorian 	while (rdatalist != NULL) {
4715185a700Sflorian 		ISC_LIST_UNLINK(msg->freerdatalist, rdatalist, link);
4725185a700Sflorian 		rdatalist = ISC_LIST_HEAD(msg->freerdatalist);
4735185a700Sflorian 	}
4745185a700Sflorian 
4755185a700Sflorian 	dynbuf = ISC_LIST_HEAD(msg->scratchpad);
4765185a700Sflorian 	INSIST(dynbuf != NULL);
4775185a700Sflorian 	if (!everything) {
4785185a700Sflorian 		isc_buffer_clear(dynbuf);
4795185a700Sflorian 		dynbuf = ISC_LIST_NEXT(dynbuf, link);
4805185a700Sflorian 	}
4815185a700Sflorian 	while (dynbuf != NULL) {
4825185a700Sflorian 		next_dynbuf = ISC_LIST_NEXT(dynbuf, link);
4835185a700Sflorian 		ISC_LIST_UNLINK(msg->scratchpad, dynbuf, link);
4845185a700Sflorian 		isc_buffer_free(&dynbuf);
4855185a700Sflorian 		dynbuf = next_dynbuf;
4865185a700Sflorian 	}
4875185a700Sflorian 
4885185a700Sflorian 	msgblock = ISC_LIST_HEAD(msg->rdatas);
4895185a700Sflorian 	if (!everything && msgblock != NULL) {
4905185a700Sflorian 		msgblock_reset(msgblock);
4915185a700Sflorian 		msgblock = ISC_LIST_NEXT(msgblock, link);
4925185a700Sflorian 	}
4935185a700Sflorian 	while (msgblock != NULL) {
4945185a700Sflorian 		next_msgblock = ISC_LIST_NEXT(msgblock, link);
4955185a700Sflorian 		ISC_LIST_UNLINK(msg->rdatas, msgblock, link);
496ad5cf538Sjung 		free(msgblock);
4975185a700Sflorian 		msgblock = next_msgblock;
4985185a700Sflorian 	}
4995185a700Sflorian 
5005185a700Sflorian 	/*
5015185a700Sflorian 	 * rdatalists could be empty.
5025185a700Sflorian 	 */
5035185a700Sflorian 
5045185a700Sflorian 	msgblock = ISC_LIST_HEAD(msg->rdatalists);
5055185a700Sflorian 	if (!everything && msgblock != NULL) {
5065185a700Sflorian 		msgblock_reset(msgblock);
5075185a700Sflorian 		msgblock = ISC_LIST_NEXT(msgblock, link);
5085185a700Sflorian 	}
5095185a700Sflorian 	while (msgblock != NULL) {
5105185a700Sflorian 		next_msgblock = ISC_LIST_NEXT(msgblock, link);
5115185a700Sflorian 		ISC_LIST_UNLINK(msg->rdatalists, msgblock, link);
512ad5cf538Sjung 		free(msgblock);
5135185a700Sflorian 		msgblock = next_msgblock;
5145185a700Sflorian 	}
5155185a700Sflorian 
5165185a700Sflorian 	msgblock = ISC_LIST_HEAD(msg->offsets);
5175185a700Sflorian 	if (!everything && msgblock != NULL) {
5185185a700Sflorian 		msgblock_reset(msgblock);
5195185a700Sflorian 		msgblock = ISC_LIST_NEXT(msgblock, link);
5205185a700Sflorian 	}
5215185a700Sflorian 	while (msgblock != NULL) {
5225185a700Sflorian 		next_msgblock = ISC_LIST_NEXT(msgblock, link);
5235185a700Sflorian 		ISC_LIST_UNLINK(msg->offsets, msgblock, link);
524ad5cf538Sjung 		free(msgblock);
5255185a700Sflorian 		msgblock = next_msgblock;
5265185a700Sflorian 	}
5275185a700Sflorian 
5285185a700Sflorian 	if (msg->tsigkey != NULL) {
5295185a700Sflorian 		dns_tsigkey_detach(&msg->tsigkey);
5305185a700Sflorian 		msg->tsigkey = NULL;
5315185a700Sflorian 	}
5325185a700Sflorian 
5335185a700Sflorian 	if (msg->tsigctx != NULL)
5345185a700Sflorian 		dst_context_destroy(&msg->tsigctx);
5355185a700Sflorian 
5365185a700Sflorian 	if (msg->query.base != NULL) {
5375185a700Sflorian 		if (msg->free_query != 0)
5385185a700Sflorian 			free(msg->query.base);
5395185a700Sflorian 		msg->query.base = NULL;
5405185a700Sflorian 		msg->query.length = 0;
5415185a700Sflorian 	}
5425185a700Sflorian 
5435185a700Sflorian 	if (msg->saved.base != NULL) {
5445185a700Sflorian 		if (msg->free_saved != 0)
5455185a700Sflorian 			free(msg->saved.base);
5465185a700Sflorian 		msg->saved.base = NULL;
5475185a700Sflorian 		msg->saved.length = 0;
5485185a700Sflorian 	}
5495185a700Sflorian 
5505185a700Sflorian 	/*
5515185a700Sflorian 	 * cleanup the buffer cleanup list
5525185a700Sflorian 	 */
5535185a700Sflorian 	dynbuf = ISC_LIST_HEAD(msg->cleanup);
5545185a700Sflorian 	while (dynbuf != NULL) {
5555185a700Sflorian 		next_dynbuf = ISC_LIST_NEXT(dynbuf, link);
5565185a700Sflorian 		ISC_LIST_UNLINK(msg->cleanup, dynbuf, link);
5575185a700Sflorian 		isc_buffer_free(&dynbuf);
5585185a700Sflorian 		dynbuf = next_dynbuf;
5595185a700Sflorian 	}
5605185a700Sflorian 
5615185a700Sflorian 	/*
5625185a700Sflorian 	 * Set other bits to normal default values.
5635185a700Sflorian 	 */
5645185a700Sflorian 	if (!everything)
5655185a700Sflorian 		msginit(msg);
5665185a700Sflorian }
5675185a700Sflorian 
5685185a700Sflorian static unsigned int
5695185a700Sflorian spacefortsig(dns_tsigkey_t *key, int otherlen) {
5705185a700Sflorian 	isc_region_t r1, r2;
5715185a700Sflorian 	unsigned int x;
5725185a700Sflorian 	isc_result_t result;
5735185a700Sflorian 
5745185a700Sflorian 	/*
5755185a700Sflorian 	 * The space required for an TSIG record is:
5765185a700Sflorian 	 *
5775185a700Sflorian 	 *	n1 bytes for the name
5785185a700Sflorian 	 *	2 bytes for the type
5795185a700Sflorian 	 *	2 bytes for the class
5805185a700Sflorian 	 *	4 bytes for the ttl
5815185a700Sflorian 	 *	2 bytes for the rdlength
5825185a700Sflorian 	 *	n2 bytes for the algorithm name
5835185a700Sflorian 	 *	6 bytes for the time signed
5845185a700Sflorian 	 *	2 bytes for the fudge
5855185a700Sflorian 	 *	2 bytes for the MAC size
5865185a700Sflorian 	 *	x bytes for the MAC
5875185a700Sflorian 	 *	2 bytes for the original id
5885185a700Sflorian 	 *	2 bytes for the error
5895185a700Sflorian 	 *	2 bytes for the other data length
5905185a700Sflorian 	 *	y bytes for the other data (at most)
5915185a700Sflorian 	 * ---------------------------------
5925185a700Sflorian 	 *     26 + n1 + n2 + x + y bytes
5935185a700Sflorian 	 */
5945185a700Sflorian 
5955185a700Sflorian 	dns_name_toregion(&key->name, &r1);
5965185a700Sflorian 	dns_name_toregion(key->algorithm, &r2);
5975185a700Sflorian 	if (key->key == NULL)
5985185a700Sflorian 		x = 0;
5995185a700Sflorian 	else {
6005185a700Sflorian 		result = dst_key_sigsize(key->key, &x);
6015185a700Sflorian 		if (result != ISC_R_SUCCESS)
6025185a700Sflorian 			x = 0;
6035185a700Sflorian 	}
6045185a700Sflorian 	return (26 + r1.length + r2.length + x + otherlen);
6055185a700Sflorian }
6065185a700Sflorian 
6075185a700Sflorian isc_result_t
6085185a700Sflorian dns_message_create(unsigned int intent, dns_message_t **msgp)
6095185a700Sflorian {
6105185a700Sflorian 	dns_message_t *m;
6115185a700Sflorian 	isc_result_t result;
6125185a700Sflorian 	isc_buffer_t *dynbuf;
6135185a700Sflorian 	unsigned int i;
6145185a700Sflorian 
6155185a700Sflorian 	REQUIRE(msgp != NULL);
6165185a700Sflorian 	REQUIRE(*msgp == NULL);
6175185a700Sflorian 	REQUIRE(intent == DNS_MESSAGE_INTENTPARSE
6185185a700Sflorian 		|| intent == DNS_MESSAGE_INTENTRENDER);
6195185a700Sflorian 
6205185a700Sflorian 	m = malloc(sizeof(dns_message_t));
6215185a700Sflorian 	if (m == NULL)
6225185a700Sflorian 		return (ISC_R_NOMEMORY);
6235185a700Sflorian 
6245185a700Sflorian 	/*
6255185a700Sflorian 	 * No allocations until further notice.  Just initialize all lists
6265185a700Sflorian 	 * and other members that are freed in the cleanup phase here.
6275185a700Sflorian 	 */
6285185a700Sflorian 
6295185a700Sflorian 	m->from_to_wire = intent;
6305185a700Sflorian 	msginit(m);
6315185a700Sflorian 
6325185a700Sflorian 	for (i = 0; i < DNS_SECTION_MAX; i++)
6335185a700Sflorian 		ISC_LIST_INIT(m->sections[i]);
6345185a700Sflorian 
6355185a700Sflorian 	ISC_LIST_INIT(m->scratchpad);
6365185a700Sflorian 	ISC_LIST_INIT(m->cleanup);
6375185a700Sflorian 	ISC_LIST_INIT(m->rdatas);
6385185a700Sflorian 	ISC_LIST_INIT(m->rdatalists);
6395185a700Sflorian 	ISC_LIST_INIT(m->offsets);
6405185a700Sflorian 	ISC_LIST_INIT(m->freerdata);
6415185a700Sflorian 	ISC_LIST_INIT(m->freerdatalist);
6425185a700Sflorian 
6435185a700Sflorian 	/*
6445185a700Sflorian 	 * Ok, it is safe to allocate (and then "goto cleanup" if failure)
6455185a700Sflorian 	 */
6465185a700Sflorian 
6475185a700Sflorian 	dynbuf = NULL;
6485185a700Sflorian 	result = isc_buffer_allocate(&dynbuf, SCRATCHPAD_SIZE);
6495185a700Sflorian 	if (result != ISC_R_SUCCESS)
6505185a700Sflorian 		goto cleanup;
6515185a700Sflorian 	ISC_LIST_APPEND(m->scratchpad, dynbuf, link);
6525185a700Sflorian 
6535185a700Sflorian 	m->cctx = NULL;
6545185a700Sflorian 
6555185a700Sflorian 	*msgp = m;
6565185a700Sflorian 	return (ISC_R_SUCCESS);
6575185a700Sflorian 
6585185a700Sflorian 	/*
6595185a700Sflorian 	 * Cleanup for error returns.
6605185a700Sflorian 	 */
6615185a700Sflorian  cleanup:
6625185a700Sflorian 	dynbuf = ISC_LIST_HEAD(m->scratchpad);
6635185a700Sflorian 	if (dynbuf != NULL) {
6645185a700Sflorian 		ISC_LIST_UNLINK(m->scratchpad, dynbuf, link);
6655185a700Sflorian 		isc_buffer_free(&dynbuf);
6665185a700Sflorian 	}
6675185a700Sflorian 	free(m);
6685185a700Sflorian 
6695185a700Sflorian 	return (ISC_R_NOMEMORY);
6705185a700Sflorian }
6715185a700Sflorian 
6725185a700Sflorian void
6735185a700Sflorian dns_message_destroy(dns_message_t **msgp) {
6745185a700Sflorian 	dns_message_t *msg;
6755185a700Sflorian 
6765185a700Sflorian 	REQUIRE(msgp != NULL);
6775185a700Sflorian 
6785185a700Sflorian 	msg = *msgp;
6795185a700Sflorian 	*msgp = NULL;
6805185a700Sflorian 
6811fb015a8Sflorian 	msgreset(msg, 1);
6825185a700Sflorian 	free(msg);
6835185a700Sflorian }
6845185a700Sflorian 
6855185a700Sflorian static isc_result_t
6865185a700Sflorian findname(dns_name_t **foundname, dns_name_t *target,
6875185a700Sflorian 	 dns_namelist_t *section)
6885185a700Sflorian {
6895185a700Sflorian 	dns_name_t *curr;
6905185a700Sflorian 
6915185a700Sflorian 	for (curr = ISC_LIST_TAIL(*section);
6925185a700Sflorian 	     curr != NULL;
6935185a700Sflorian 	     curr = ISC_LIST_PREV(curr, link)) {
6945185a700Sflorian 		if (dns_name_equal(curr, target)) {
6955185a700Sflorian 			if (foundname != NULL)
6965185a700Sflorian 				*foundname = curr;
6975185a700Sflorian 			return (ISC_R_SUCCESS);
6985185a700Sflorian 		}
6995185a700Sflorian 	}
7005185a700Sflorian 
7015185a700Sflorian 	return (ISC_R_NOTFOUND);
7025185a700Sflorian }
7035185a700Sflorian 
7045185a700Sflorian isc_result_t
7055185a700Sflorian dns_message_find(dns_name_t *name, dns_rdataclass_t rdclass,
7065185a700Sflorian 		 dns_rdatatype_t type, dns_rdatatype_t covers,
7075185a700Sflorian 		 dns_rdataset_t **rdataset)
7085185a700Sflorian {
7095185a700Sflorian 	dns_rdataset_t *curr;
7105185a700Sflorian 
7115185a700Sflorian 	REQUIRE(name != NULL);
7125185a700Sflorian 	REQUIRE(rdataset == NULL || *rdataset == NULL);
7135185a700Sflorian 
7145185a700Sflorian 	for (curr = ISC_LIST_TAIL(name->list);
7155185a700Sflorian 	     curr != NULL;
7165185a700Sflorian 	     curr = ISC_LIST_PREV(curr, link)) {
7175185a700Sflorian 		if (curr->rdclass == rdclass &&
7185185a700Sflorian 		    curr->type == type && curr->covers == covers) {
7195185a700Sflorian 			if (rdataset != NULL)
7205185a700Sflorian 				*rdataset = curr;
7215185a700Sflorian 			return (ISC_R_SUCCESS);
7225185a700Sflorian 		}
7235185a700Sflorian 	}
7245185a700Sflorian 
7255185a700Sflorian 	return (ISC_R_NOTFOUND);
7265185a700Sflorian }
7275185a700Sflorian 
7285185a700Sflorian isc_result_t
7295185a700Sflorian dns_message_findtype(dns_name_t *name, dns_rdatatype_t type,
7305185a700Sflorian 		     dns_rdatatype_t covers, dns_rdataset_t **rdataset)
7315185a700Sflorian {
7325185a700Sflorian 	dns_rdataset_t *curr;
7335185a700Sflorian 
7345185a700Sflorian 	REQUIRE(name != NULL);
7355185a700Sflorian 	REQUIRE(rdataset == NULL || *rdataset == NULL);
7365185a700Sflorian 
7375185a700Sflorian 	for (curr = ISC_LIST_TAIL(name->list);
7385185a700Sflorian 	     curr != NULL;
7395185a700Sflorian 	     curr = ISC_LIST_PREV(curr, link)) {
7405185a700Sflorian 		if (curr->type == type && curr->covers == covers) {
7415185a700Sflorian 			if (rdataset != NULL)
7425185a700Sflorian 				*rdataset = curr;
7435185a700Sflorian 			return (ISC_R_SUCCESS);
7445185a700Sflorian 		}
7455185a700Sflorian 	}
7465185a700Sflorian 
7475185a700Sflorian 	return (ISC_R_NOTFOUND);
7485185a700Sflorian }
7495185a700Sflorian 
7505185a700Sflorian /*
7515185a700Sflorian  * Read a name from buffer "source".
7525185a700Sflorian  */
7535185a700Sflorian static isc_result_t
7545185a700Sflorian getname(dns_name_t *name, isc_buffer_t *source, dns_message_t *msg,
7555185a700Sflorian 	dns_decompress_t *dctx)
7565185a700Sflorian {
7575185a700Sflorian 	isc_buffer_t *scratch;
7585185a700Sflorian 	isc_result_t result;
7595185a700Sflorian 	unsigned int tries;
7605185a700Sflorian 
7615185a700Sflorian 	scratch = currentbuffer(msg);
7625185a700Sflorian 
7635185a700Sflorian 	/*
7645185a700Sflorian 	 * First try:  use current buffer.
7655185a700Sflorian 	 * Second try:  allocate a new buffer and use that.
7665185a700Sflorian 	 */
7675185a700Sflorian 	tries = 0;
7685185a700Sflorian 	while (tries < 2) {
7691fb015a8Sflorian 		result = dns_name_fromwire(name, source, dctx, 0,
7705185a700Sflorian 					   scratch);
7715185a700Sflorian 
7725185a700Sflorian 		if (result == ISC_R_NOSPACE) {
7735185a700Sflorian 			tries++;
7745185a700Sflorian 
7755185a700Sflorian 			result = newbuffer(msg, SCRATCHPAD_SIZE);
7765185a700Sflorian 			if (result != ISC_R_SUCCESS)
7775185a700Sflorian 				return (result);
7785185a700Sflorian 
7795185a700Sflorian 			scratch = currentbuffer(msg);
7805185a700Sflorian 			dns_name_reset(name);
7815185a700Sflorian 		} else {
7825185a700Sflorian 			return (result);
7835185a700Sflorian 		}
7845185a700Sflorian 	}
7855185a700Sflorian 
7865185a700Sflorian 	INSIST(0);  /* Cannot get here... */
7875185a700Sflorian 	return (ISC_R_UNEXPECTED);
7885185a700Sflorian }
7895185a700Sflorian 
7905185a700Sflorian static isc_result_t
7915185a700Sflorian getrdata(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
7925185a700Sflorian 	 dns_rdataclass_t rdclass, dns_rdatatype_t rdtype,
7935185a700Sflorian 	 unsigned int rdatalen, dns_rdata_t *rdata)
7945185a700Sflorian {
7955185a700Sflorian 	isc_buffer_t *scratch;
7965185a700Sflorian 	isc_result_t result;
7975185a700Sflorian 	unsigned int tries;
7985185a700Sflorian 	unsigned int trysize;
7995185a700Sflorian 
8005185a700Sflorian 	scratch = currentbuffer(msg);
8015185a700Sflorian 
8025185a700Sflorian 	isc_buffer_setactive(source, rdatalen);
8035185a700Sflorian 
8045185a700Sflorian 	/*
8055185a700Sflorian 	 * First try:  use current buffer.
8065185a700Sflorian 	 * Second try:  allocate a new buffer of size
8075185a700Sflorian 	 *     max(SCRATCHPAD_SIZE, 2 * compressed_rdatalen)
8085185a700Sflorian 	 *     (the data will fit if it was not more than 50% compressed)
8095185a700Sflorian 	 * Subsequent tries: double buffer size on each try.
8105185a700Sflorian 	 */
8115185a700Sflorian 	tries = 0;
8125185a700Sflorian 	trysize = 0;
8135185a700Sflorian 	/* XXX possibly change this to a while (tries < 2) loop */
8145185a700Sflorian 	for (;;) {
8155185a700Sflorian 		result = dns_rdata_fromwire(rdata, rdclass, rdtype,
8165185a700Sflorian 					    source, dctx, 0,
8175185a700Sflorian 					    scratch);
8185185a700Sflorian 
8195185a700Sflorian 		if (result == ISC_R_NOSPACE) {
8205185a700Sflorian 			if (tries == 0) {
8215185a700Sflorian 				trysize = 2 * rdatalen;
8225185a700Sflorian 				if (trysize < SCRATCHPAD_SIZE)
8235185a700Sflorian 					trysize = SCRATCHPAD_SIZE;
8245185a700Sflorian 			} else {
8255185a700Sflorian 				INSIST(trysize != 0);
8265185a700Sflorian 				if (trysize >= 65535)
8275185a700Sflorian 					return (ISC_R_NOSPACE);
8285185a700Sflorian 					/* XXX DNS_R_RRTOOLONG? */
8295185a700Sflorian 				trysize *= 2;
8305185a700Sflorian 			}
8315185a700Sflorian 			tries++;
8325185a700Sflorian 			result = newbuffer(msg, trysize);
8335185a700Sflorian 			if (result != ISC_R_SUCCESS)
8345185a700Sflorian 				return (result);
8355185a700Sflorian 
8365185a700Sflorian 			scratch = currentbuffer(msg);
8375185a700Sflorian 		} else {
8385185a700Sflorian 			return (result);
8395185a700Sflorian 		}
8405185a700Sflorian 	}
8415185a700Sflorian }
8425185a700Sflorian 
8435185a700Sflorian #define DO_FORMERR					\
8445185a700Sflorian 	do {						\
8455185a700Sflorian 		if (best_effort)			\
8461fb015a8Sflorian 			seen_problem = 1;	\
8475185a700Sflorian 		else {					\
8485185a700Sflorian 			result = DNS_R_FORMERR;		\
8495185a700Sflorian 			goto cleanup;			\
8505185a700Sflorian 		}					\
8515185a700Sflorian 	} while (0)
8525185a700Sflorian 
8535185a700Sflorian static isc_result_t
8545185a700Sflorian getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
8555185a700Sflorian 	     unsigned int options)
8565185a700Sflorian {
8575185a700Sflorian 	isc_region_t r;
8585185a700Sflorian 	unsigned int count;
8595185a700Sflorian 	dns_name_t *name;
8605185a700Sflorian 	dns_name_t *name2;
8615185a700Sflorian 	dns_offsets_t *offsets;
8625185a700Sflorian 	dns_rdataset_t *rdataset;
8635185a700Sflorian 	dns_rdatalist_t *rdatalist;
8645185a700Sflorian 	isc_result_t result;
8655185a700Sflorian 	dns_rdatatype_t rdtype;
8665185a700Sflorian 	dns_rdataclass_t rdclass;
8675185a700Sflorian 	dns_namelist_t *section;
8681fb015a8Sflorian 	int free_name;
8691fb015a8Sflorian 	int best_effort;
8701fb015a8Sflorian 	int seen_problem;
8715185a700Sflorian 
8725185a700Sflorian 	section = &msg->sections[DNS_SECTION_QUESTION];
8735185a700Sflorian 
8741fb015a8Sflorian 	best_effort = options & DNS_MESSAGEPARSE_BESTEFFORT;
8751fb015a8Sflorian 	seen_problem = 0;
8765185a700Sflorian 
8775185a700Sflorian 	name = NULL;
8785185a700Sflorian 	rdataset = NULL;
8795185a700Sflorian 	rdatalist = NULL;
8805185a700Sflorian 
8815185a700Sflorian 	for (count = 0; count < msg->counts[DNS_SECTION_QUESTION]; count++) {
8825185a700Sflorian 		name = malloc(sizeof(dns_name_t));
8835185a700Sflorian 		if (name == NULL)
8845185a700Sflorian 			return (ISC_R_NOMEMORY);
8851fb015a8Sflorian 		free_name = 1;
8865185a700Sflorian 
8875185a700Sflorian 		offsets = newoffsets(msg);
8885185a700Sflorian 		if (offsets == NULL) {
8895185a700Sflorian 			result = ISC_R_NOMEMORY;
8905185a700Sflorian 			goto cleanup;
8915185a700Sflorian 		}
8925185a700Sflorian 		dns_name_init(name, *offsets);
8935185a700Sflorian 
8945185a700Sflorian 		/*
8955185a700Sflorian 		 * Parse the name out of this packet.
8965185a700Sflorian 		 */
8975185a700Sflorian 		isc_buffer_remainingregion(source, &r);
8985185a700Sflorian 		isc_buffer_setactive(source, r.length);
8995185a700Sflorian 		result = getname(name, source, msg, dctx);
9005185a700Sflorian 		if (result != ISC_R_SUCCESS)
9015185a700Sflorian 			goto cleanup;
9025185a700Sflorian 
9035185a700Sflorian 		/*
9045185a700Sflorian 		 * Run through the section, looking to see if this name
9055185a700Sflorian 		 * is already there.  If it is found, put back the allocated
9065185a700Sflorian 		 * name since we no longer need it, and set our name pointer
9075185a700Sflorian 		 * to point to the name we found.
9085185a700Sflorian 		 */
9095185a700Sflorian 		result = findname(&name2, name, section);
9105185a700Sflorian 
9115185a700Sflorian 		/*
9125185a700Sflorian 		 * If it is the first name in the section, accept it.
9135185a700Sflorian 		 *
9145185a700Sflorian 		 * If it is not, but is not the same as the name already
9155185a700Sflorian 		 * in the question section, append to the section.  Note that
9165185a700Sflorian 		 * here in the question section this is illegal, so return
9175185a700Sflorian 		 * FORMERR.  In the future, check the opcode to see if
9185185a700Sflorian 		 * this should be legal or not.  In either case we no longer
9195185a700Sflorian 		 * need this name pointer.
9205185a700Sflorian 		 */
9215185a700Sflorian 		if (result != ISC_R_SUCCESS) {
9225185a700Sflorian 			if (!ISC_LIST_EMPTY(*section))
9235185a700Sflorian 				DO_FORMERR;
9245185a700Sflorian 			ISC_LIST_APPEND(*section, name, link);
9251fb015a8Sflorian 			free_name = 0;
9265185a700Sflorian 		} else {
9275185a700Sflorian 			free(name);
9285185a700Sflorian 			name = name2;
9295185a700Sflorian 			name2 = NULL;
9301fb015a8Sflorian 			free_name = 0;
9315185a700Sflorian 		}
9325185a700Sflorian 
9335185a700Sflorian 		/*
9345185a700Sflorian 		 * Get type and class.
9355185a700Sflorian 		 */
9365185a700Sflorian 		isc_buffer_remainingregion(source, &r);
9375185a700Sflorian 		if (r.length < 4) {
9385185a700Sflorian 			result = ISC_R_UNEXPECTEDEND;
9395185a700Sflorian 			goto cleanup;
9405185a700Sflorian 		}
9415185a700Sflorian 		rdtype = isc_buffer_getuint16(source);
9425185a700Sflorian 		rdclass = isc_buffer_getuint16(source);
9435185a700Sflorian 
9445185a700Sflorian 		/*
9455185a700Sflorian 		 * If this class is different than the one we already read,
9465185a700Sflorian 		 * this is an error.
9475185a700Sflorian 		 */
9485185a700Sflorian 		if (msg->rdclass_set == 0) {
9495185a700Sflorian 			msg->rdclass = rdclass;
9505185a700Sflorian 			msg->rdclass_set = 1;
9515185a700Sflorian 		} else if (msg->rdclass != rdclass)
9525185a700Sflorian 			DO_FORMERR;
9535185a700Sflorian 
9545185a700Sflorian 		/*
9555185a700Sflorian 		 * Is this a TKEY query?
9565185a700Sflorian 		 */
9575185a700Sflorian 		if (rdtype == dns_rdatatype_tkey)
9585185a700Sflorian 			msg->tkey = 1;
9595185a700Sflorian 
9605185a700Sflorian 		/*
9615185a700Sflorian 		 * Can't ask the same question twice.
9625185a700Sflorian 		 */
9635185a700Sflorian 		result = dns_message_find(name, rdclass, rdtype, 0, NULL);
9645185a700Sflorian 		if (result == ISC_R_SUCCESS)
9655185a700Sflorian 			DO_FORMERR;
9665185a700Sflorian 
9675185a700Sflorian 		/*
9685185a700Sflorian 		 * Allocate a new rdatalist.
9695185a700Sflorian 		 */
9705185a700Sflorian 		rdatalist = newrdatalist(msg);
9715185a700Sflorian 		if (rdatalist == NULL) {
9725185a700Sflorian 			result = ISC_R_NOMEMORY;
9735185a700Sflorian 			goto cleanup;
9745185a700Sflorian 		}
9755185a700Sflorian 		rdataset =  malloc(sizeof(dns_rdataset_t));
9765185a700Sflorian 		if (rdataset == NULL) {
9775185a700Sflorian 			result = ISC_R_NOMEMORY;
9785185a700Sflorian 			goto cleanup;
9795185a700Sflorian 		}
9805185a700Sflorian 
9815185a700Sflorian 		/*
9825185a700Sflorian 		 * Convert rdatalist to rdataset, and attach the latter to
9835185a700Sflorian 		 * the name.
9845185a700Sflorian 		 */
9855185a700Sflorian 		rdatalist->type = rdtype;
9865185a700Sflorian 		rdatalist->covers = 0;
9875185a700Sflorian 		rdatalist->rdclass = rdclass;
9885185a700Sflorian 		rdatalist->ttl = 0;
9895185a700Sflorian 		ISC_LIST_INIT(rdatalist->rdata);
9905185a700Sflorian 
9915185a700Sflorian 		dns_rdataset_init(rdataset);
9925185a700Sflorian 		result = dns_rdatalist_tordataset(rdatalist, rdataset);
9935185a700Sflorian 		if (result != ISC_R_SUCCESS)
9945185a700Sflorian 			goto cleanup;
9955185a700Sflorian 
9965185a700Sflorian 		rdataset->attributes |= DNS_RDATASETATTR_QUESTION;
9975185a700Sflorian 
9985185a700Sflorian 		ISC_LIST_APPEND(name->list, rdataset, link);
9995185a700Sflorian 		rdataset = NULL;
10005185a700Sflorian 	}
10015185a700Sflorian 
10025185a700Sflorian 	if (seen_problem)
10035185a700Sflorian 		return (DNS_R_RECOVERABLE);
10045185a700Sflorian 	return (ISC_R_SUCCESS);
10055185a700Sflorian 
10065185a700Sflorian  cleanup:
10075185a700Sflorian 	if (rdataset != NULL) {
10085185a700Sflorian 		INSIST(!dns_rdataset_isassociated(rdataset));
10095185a700Sflorian 		free(rdataset);
10105185a700Sflorian 	}
10115185a700Sflorian 	if (free_name)
10125185a700Sflorian 		free(name);
10135185a700Sflorian 
10145185a700Sflorian 	return (result);
10155185a700Sflorian }
10165185a700Sflorian 
10171fb015a8Sflorian static int
10185185a700Sflorian update(dns_section_t section, dns_rdataclass_t rdclass) {
10195185a700Sflorian 	if (section == DNS_SECTION_PREREQUISITE)
10201fb015a8Sflorian 		return (rdclass == dns_rdataclass_any ||
10211fb015a8Sflorian 			       rdclass == dns_rdataclass_none);
10225185a700Sflorian 	if (section == DNS_SECTION_UPDATE)
10231fb015a8Sflorian 		return (rdclass == dns_rdataclass_any);
10241fb015a8Sflorian 	return (0);
10255185a700Sflorian }
10265185a700Sflorian 
10275185a700Sflorian static isc_result_t
10285185a700Sflorian getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
10295185a700Sflorian 	   dns_section_t sectionid, unsigned int options)
10305185a700Sflorian {
10315185a700Sflorian 	isc_region_t r;
10325185a700Sflorian 	unsigned int count, rdatalen;
10335185a700Sflorian 	dns_name_t *name = NULL;
10345185a700Sflorian 	dns_offsets_t *offsets;
10355185a700Sflorian 	dns_rdataset_t *rdataset;
10365185a700Sflorian 	dns_rdatalist_t *rdatalist;
10375185a700Sflorian 	isc_result_t result;
10385185a700Sflorian 	dns_rdatatype_t rdtype, covers;
10395185a700Sflorian 	dns_rdataclass_t rdclass;
10405185a700Sflorian 	dns_rdata_t *rdata;
10415185a700Sflorian 	dns_ttl_t ttl;
10425185a700Sflorian 	dns_namelist_t *section;
10431fb015a8Sflorian 	int free_name = 0, free_rdataset = 0;
10441fb015a8Sflorian 	int best_effort, seen_problem;
10451fb015a8Sflorian 	int issigzero;
10465185a700Sflorian 
10471fb015a8Sflorian 	best_effort = options & DNS_MESSAGEPARSE_BESTEFFORT;
10481fb015a8Sflorian 	seen_problem = 0;
10495185a700Sflorian 
10505185a700Sflorian 	section = &msg->sections[sectionid];
10515185a700Sflorian 
10525185a700Sflorian 	for (count = 0; count < msg->counts[sectionid]; count++) {
10535185a700Sflorian 		int recstart = source->current;
10541fb015a8Sflorian 		free_rdataset = 0;
10555185a700Sflorian 
10565185a700Sflorian 		name = malloc(sizeof(dns_name_t));
10575185a700Sflorian 		if (name == NULL)
10585185a700Sflorian 			return (ISC_R_NOMEMORY);
10591fb015a8Sflorian 		free_name = 1;
10605185a700Sflorian 
10615185a700Sflorian 		offsets = newoffsets(msg);
10625185a700Sflorian 		if (offsets == NULL) {
10635185a700Sflorian 			result = ISC_R_NOMEMORY;
10645185a700Sflorian 			goto cleanup;
10655185a700Sflorian 		}
10665185a700Sflorian 		dns_name_init(name, *offsets);
10675185a700Sflorian 
10685185a700Sflorian 		/*
10695185a700Sflorian 		 * Parse the name out of this packet.
10705185a700Sflorian 		 */
10715185a700Sflorian 		isc_buffer_remainingregion(source, &r);
10725185a700Sflorian 		isc_buffer_setactive(source, r.length);
10735185a700Sflorian 		result = getname(name, source, msg, dctx);
10745185a700Sflorian 		if (result != ISC_R_SUCCESS)
10755185a700Sflorian 			goto cleanup;
10765185a700Sflorian 
10775185a700Sflorian 		/*
10785185a700Sflorian 		 * Get type, class, ttl, and rdatalen.  Verify that at least
10795185a700Sflorian 		 * rdatalen bytes remain.  (Some of this is deferred to
10805185a700Sflorian 		 * later.)
10815185a700Sflorian 		 */
10825185a700Sflorian 		isc_buffer_remainingregion(source, &r);
10835185a700Sflorian 		if (r.length < 2 + 2 + 4 + 2) {
10845185a700Sflorian 			result = ISC_R_UNEXPECTEDEND;
10855185a700Sflorian 			goto cleanup;
10865185a700Sflorian 		}
10875185a700Sflorian 		rdtype = isc_buffer_getuint16(source);
10885185a700Sflorian 		rdclass = isc_buffer_getuint16(source);
10895185a700Sflorian 
10905185a700Sflorian 		/*
10915185a700Sflorian 		 * If there was no question section, we may not yet have
10925185a700Sflorian 		 * established a class.  Do so now.
10935185a700Sflorian 		 */
10945185a700Sflorian 		if (msg->rdclass_set == 0 &&
10955185a700Sflorian 		    rdtype != dns_rdatatype_opt &&	/* class is UDP SIZE */
10965185a700Sflorian 		    rdtype != dns_rdatatype_tsig &&	/* class is ANY */
10975185a700Sflorian 		    rdtype != dns_rdatatype_tkey) {	/* class is undefined */
10985185a700Sflorian 			msg->rdclass = rdclass;
10995185a700Sflorian 			msg->rdclass_set = 1;
11005185a700Sflorian 		}
11015185a700Sflorian 
11025185a700Sflorian 		/*
11035185a700Sflorian 		 * If this class is different than the one in the question
11045185a700Sflorian 		 * section, bail.
11055185a700Sflorian 		 */
11065185a700Sflorian 		if (msg->opcode != dns_opcode_update
11075185a700Sflorian 		    && rdtype != dns_rdatatype_tsig
11085185a700Sflorian 		    && rdtype != dns_rdatatype_opt
11095185a700Sflorian 		    && rdtype != dns_rdatatype_key /* in a TKEY query */
11105185a700Sflorian 		    && rdtype != dns_rdatatype_sig /* SIG(0) */
11115185a700Sflorian 		    && rdtype != dns_rdatatype_tkey /* Win2000 TKEY */
11125185a700Sflorian 		    && msg->rdclass != dns_rdataclass_any
11135185a700Sflorian 		    && msg->rdclass != rdclass)
11145185a700Sflorian 			DO_FORMERR;
11155185a700Sflorian 
11165185a700Sflorian 		/*
11175185a700Sflorian 		 * If this is not a TKEY query/response then the KEY
11185185a700Sflorian 		 * record's class needs to match.
11195185a700Sflorian 		 */
11205185a700Sflorian 		if (msg->opcode != dns_opcode_update && !msg->tkey &&
11215185a700Sflorian 		    rdtype == dns_rdatatype_key &&
11225185a700Sflorian 		    msg->rdclass != dns_rdataclass_any &&
11235185a700Sflorian 		    msg->rdclass != rdclass)
11245185a700Sflorian 			DO_FORMERR;
11255185a700Sflorian 
11265185a700Sflorian 		/*
11275185a700Sflorian 		 * Special type handling for TSIG, OPT, and TKEY.
11285185a700Sflorian 		 */
11295185a700Sflorian 		if (rdtype == dns_rdatatype_tsig) {
11305185a700Sflorian 			/*
11315185a700Sflorian 			 * If it is a tsig, verify that it is in the
11325185a700Sflorian 			 * additional data section.
11335185a700Sflorian 			 */
11345185a700Sflorian 			if (sectionid != DNS_SECTION_ADDITIONAL ||
11355185a700Sflorian 			    rdclass != dns_rdataclass_any ||
11365185a700Sflorian 			    count != msg->counts[sectionid]  - 1)
11375185a700Sflorian 				DO_FORMERR;
11385185a700Sflorian 			msg->sigstart = recstart;
11395185a700Sflorian 		} else if (rdtype == dns_rdatatype_opt) {
11405185a700Sflorian 			/*
11415185a700Sflorian 			 * The name of an OPT record must be ".", it
11425185a700Sflorian 			 * must be in the additional data section, and
11435185a700Sflorian 			 * it must be the first OPT we've seen.
11445185a700Sflorian 			 */
11455185a700Sflorian 			if (!dns_name_equal(dns_rootname, name) ||
11465185a700Sflorian 			    sectionid != DNS_SECTION_ADDITIONAL ||
11475185a700Sflorian 			    msg->opt != NULL)
11485185a700Sflorian 				DO_FORMERR;
11495185a700Sflorian 		} else if (rdtype == dns_rdatatype_tkey) {
11505185a700Sflorian 			/*
11515185a700Sflorian 			 * A TKEY must be in the additional section if this
11525185a700Sflorian 			 * is a query, and the answer section if this is a
11535185a700Sflorian 			 * response.  Unless it's a Win2000 client.
11545185a700Sflorian 			 *
11555185a700Sflorian 			 * Its class is ignored.
11565185a700Sflorian 			 */
11575185a700Sflorian 			dns_section_t tkeysection;
11585185a700Sflorian 
11595185a700Sflorian 			if ((msg->flags & DNS_MESSAGEFLAG_QR) == 0)
11605185a700Sflorian 				tkeysection = DNS_SECTION_ADDITIONAL;
11615185a700Sflorian 			else
11625185a700Sflorian 				tkeysection = DNS_SECTION_ANSWER;
11635185a700Sflorian 			if (sectionid != tkeysection &&
11645185a700Sflorian 			    sectionid != DNS_SECTION_ANSWER)
11655185a700Sflorian 				DO_FORMERR;
11665185a700Sflorian 		}
11675185a700Sflorian 
11685185a700Sflorian 		/*
11695185a700Sflorian 		 * ... now get ttl and rdatalen, and check buffer.
11705185a700Sflorian 		 */
11715185a700Sflorian 		ttl = isc_buffer_getuint32(source);
11725185a700Sflorian 		rdatalen = isc_buffer_getuint16(source);
11735185a700Sflorian 		r.length -= (2 + 2 + 4 + 2);
11745185a700Sflorian 		if (r.length < rdatalen) {
11755185a700Sflorian 			result = ISC_R_UNEXPECTEDEND;
11765185a700Sflorian 			goto cleanup;
11775185a700Sflorian 		}
11785185a700Sflorian 
11795185a700Sflorian 		/*
11805185a700Sflorian 		 * Read the rdata from the wire format.  Interpret the
11815185a700Sflorian 		 * rdata according to its actual class, even if it had a
11825185a700Sflorian 		 * DynDNS meta-class in the packet (unless this is a TSIG).
11835185a700Sflorian 		 * Then put the meta-class back into the finished rdata.
11845185a700Sflorian 		 */
11855185a700Sflorian 		rdata = newrdata(msg);
11865185a700Sflorian 		if (rdata == NULL) {
11875185a700Sflorian 			result = ISC_R_NOMEMORY;
11885185a700Sflorian 			goto cleanup;
11895185a700Sflorian 		}
11905185a700Sflorian 		if (msg->opcode == dns_opcode_update &&
11915185a700Sflorian 		    update(sectionid, rdclass)) {
11925185a700Sflorian 			if (rdatalen != 0) {
11935185a700Sflorian 				result = DNS_R_FORMERR;
11945185a700Sflorian 				goto cleanup;
11955185a700Sflorian 			}
11965185a700Sflorian 			/*
11975185a700Sflorian 			 * When the rdata is empty, the data pointer is
11985185a700Sflorian 			 * never dereferenced, but it must still be non-NULL.
11995185a700Sflorian 			 * Casting 1 rather than "" avoids warnings about
12005185a700Sflorian 			 * discarding the const attribute of a string,
12015185a700Sflorian 			 * for compilers that would warn about such things.
12025185a700Sflorian 			 */
12035185a700Sflorian 			rdata->data = (unsigned char *)1;
12045185a700Sflorian 			rdata->length = 0;
12055185a700Sflorian 			rdata->rdclass = rdclass;
12065185a700Sflorian 			rdata->type = rdtype;
12075185a700Sflorian 			rdata->flags = DNS_RDATA_UPDATE;
12085185a700Sflorian 			result = ISC_R_SUCCESS;
12095185a700Sflorian 		} else if (rdclass == dns_rdataclass_none &&
12105185a700Sflorian 			   msg->opcode == dns_opcode_update &&
12115185a700Sflorian 			   sectionid == DNS_SECTION_UPDATE) {
12125185a700Sflorian 			result = getrdata(source, msg, dctx, msg->rdclass,
12135185a700Sflorian 					  rdtype, rdatalen, rdata);
12145185a700Sflorian 		} else
12155185a700Sflorian 			result = getrdata(source, msg, dctx, rdclass,
12165185a700Sflorian 					  rdtype, rdatalen, rdata);
12175185a700Sflorian 		if (result != ISC_R_SUCCESS)
12185185a700Sflorian 			goto cleanup;
12195185a700Sflorian 		rdata->rdclass = rdclass;
12201fb015a8Sflorian 		issigzero = 0;
12215185a700Sflorian 		if (rdtype == dns_rdatatype_rrsig &&
12225185a700Sflorian 		    rdata->flags == 0) {
12235185a700Sflorian 			covers = dns_rdata_covers(rdata);
12245185a700Sflorian 			if (covers == 0)
12255185a700Sflorian 				DO_FORMERR;
12265185a700Sflorian 		} else if (rdtype == dns_rdatatype_sig /* SIG(0) */ &&
12275185a700Sflorian 			   rdata->flags == 0) {
12285185a700Sflorian 			covers = dns_rdata_covers(rdata);
12295185a700Sflorian 			if (covers == 0) {
12305185a700Sflorian 				if (sectionid != DNS_SECTION_ADDITIONAL ||
12315185a700Sflorian 				    count != msg->counts[sectionid]  - 1)
12325185a700Sflorian 					DO_FORMERR;
12335185a700Sflorian 				msg->sigstart = recstart;
12341fb015a8Sflorian 				issigzero = 1;
12355185a700Sflorian 			} else {
12365185a700Sflorian 				if (msg->rdclass != dns_rdataclass_any &&
12375185a700Sflorian 				    msg->rdclass != rdclass)
12385185a700Sflorian 					DO_FORMERR;
12395185a700Sflorian 			}
12405185a700Sflorian 		} else
12415185a700Sflorian 			covers = 0;
12425185a700Sflorian 
12435185a700Sflorian 		/*
12445185a700Sflorian 		 * Check the ownername of NSEC3 records
12455185a700Sflorian 		 */
12465185a700Sflorian 		if (rdtype == dns_rdatatype_nsec3 &&
124766d1673bSflorian 		    !dns_rdata_checkowner_nsec3(name, msg->rdclass, rdtype,
12481fb015a8Sflorian 					  0)) {
12495185a700Sflorian 			result = DNS_R_BADOWNERNAME;
12505185a700Sflorian 			goto cleanup;
12515185a700Sflorian 		}
12525185a700Sflorian 
12535185a700Sflorian 		if (rdtype != dns_rdatatype_opt &&
1254a9a8f5b3Sflorian 		    rdtype != dns_rdatatype_tsig && !issigzero) {
12555185a700Sflorian 			ISC_LIST_APPEND(*section, name, link);
12561fb015a8Sflorian 			free_name = 0;
12575185a700Sflorian 		}
12585185a700Sflorian 
12595185a700Sflorian 		rdataset = malloc(sizeof(dns_rdataset_t));
12605185a700Sflorian 		if (rdataset == NULL) {
12615185a700Sflorian 			result = ISC_R_NOMEMORY;
12625185a700Sflorian 			goto cleanup;
12635185a700Sflorian 		}
12641fb015a8Sflorian 		free_rdataset = 1;
12655185a700Sflorian 
12665185a700Sflorian 		rdatalist = newrdatalist(msg);
12675185a700Sflorian 		if (rdatalist == NULL) {
12685185a700Sflorian 			result = ISC_R_NOMEMORY;
12695185a700Sflorian 			goto cleanup;
12705185a700Sflorian 		}
12715185a700Sflorian 
12725185a700Sflorian 		rdatalist->type = rdtype;
12735185a700Sflorian 		rdatalist->covers = covers;
12745185a700Sflorian 		rdatalist->rdclass = rdclass;
12755185a700Sflorian 		rdatalist->ttl = ttl;
12765185a700Sflorian 		ISC_LIST_INIT(rdatalist->rdata);
12775185a700Sflorian 
12785185a700Sflorian 		dns_rdataset_init(rdataset);
12795185a700Sflorian 		RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist,
12805185a700Sflorian 							       rdataset)
12815185a700Sflorian 			      == ISC_R_SUCCESS);
12825185a700Sflorian 
12835185a700Sflorian 		if (rdtype != dns_rdatatype_opt &&
12845185a700Sflorian 		    rdtype != dns_rdatatype_tsig &&
12855185a700Sflorian 		    !issigzero)
12865185a700Sflorian 		{
12875185a700Sflorian 			ISC_LIST_APPEND(name->list, rdataset, link);
12881fb015a8Sflorian 			free_rdataset = 0;
12895185a700Sflorian 		}
12905185a700Sflorian 
12915185a700Sflorian 		/*
12925185a700Sflorian 		 * Minimize TTLs.
12935185a700Sflorian 		 *
12945185a700Sflorian 		 * Section 5.2 of RFC2181 says we should drop
12955185a700Sflorian 		 * nonauthoritative rrsets where the TTLs differ, but we
12965185a700Sflorian 		 * currently treat them the as if they were authoritative and
12975185a700Sflorian 		 * minimize them.
12985185a700Sflorian 		 */
12995185a700Sflorian 		if (ttl < rdataset->ttl)
13005185a700Sflorian 			rdataset->ttl = ttl;
13015185a700Sflorian 
13025185a700Sflorian 		/* Append this rdata to the rdataset. */
13035185a700Sflorian 		dns_rdatalist_fromrdataset(rdataset, &rdatalist);
13045185a700Sflorian 		ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
13055185a700Sflorian 
13065185a700Sflorian 		/*
13075185a700Sflorian 		 * If this is an OPT, SIG(0) or TSIG record, remember it.
13085185a700Sflorian 		 * Also, set the extended rcode for TSIG.
13095185a700Sflorian 		 *
13105185a700Sflorian 		 * Note msg->opt, msg->sig0 and msg->tsig will only be
13115185a700Sflorian 		 * already set if best-effort parsing is enabled otherwise
13125185a700Sflorian 		 * there will only be at most one of each.
13135185a700Sflorian 		 */
13145185a700Sflorian 		if (rdtype == dns_rdatatype_opt && msg->opt == NULL) {
13155185a700Sflorian 			dns_rcode_t ercode;
13165185a700Sflorian 
13175185a700Sflorian 			msg->opt = rdataset;
13185185a700Sflorian 			rdataset = NULL;
13191fb015a8Sflorian 			free_rdataset = 0;
13205185a700Sflorian 			ercode = (dns_rcode_t)
13215185a700Sflorian 				((msg->opt->ttl & DNS_MESSAGE_EDNSRCODE_MASK)
13225185a700Sflorian 				 >> 20);
13235185a700Sflorian 			msg->rcode |= ercode;
13245185a700Sflorian 			free(name);
13251fb015a8Sflorian 			free_name = 0;
13265185a700Sflorian 		} else if (issigzero && msg->sig0 == NULL) {
13275185a700Sflorian 			msg->sig0 = rdataset;
13285185a700Sflorian 			msg->sig0name = name;
13295185a700Sflorian 			rdataset = NULL;
13301fb015a8Sflorian 			free_rdataset = 0;
13311fb015a8Sflorian 			free_name = 0;
13325185a700Sflorian 		} else if (rdtype == dns_rdatatype_tsig && msg->tsig == NULL) {
13335185a700Sflorian 			msg->tsig = rdataset;
13345185a700Sflorian 			msg->tsigname = name;
13355185a700Sflorian 			/* Windows doesn't like TSIG names to be compressed. */
13365185a700Sflorian 			msg->tsigname->attributes |= DNS_NAMEATTR_NOCOMPRESS;
13375185a700Sflorian 			rdataset = NULL;
13381fb015a8Sflorian 			free_rdataset = 0;
13391fb015a8Sflorian 			free_name = 0;
13405185a700Sflorian 		}
13415185a700Sflorian 
13425185a700Sflorian 		if (seen_problem) {
13435185a700Sflorian 			if (free_name)
13445185a700Sflorian 				free(name);
13455185a700Sflorian 			if (free_rdataset)
13465185a700Sflorian 				free(rdataset);
13471fb015a8Sflorian 			free_name = free_rdataset = 0;
13485185a700Sflorian 		}
13491fb015a8Sflorian 		INSIST(!free_name);
13501fb015a8Sflorian 		INSIST(!free_rdataset);
13515185a700Sflorian 	}
13525185a700Sflorian 
13535185a700Sflorian 	if (seen_problem)
13545185a700Sflorian 		return (DNS_R_RECOVERABLE);
13555185a700Sflorian 	return (ISC_R_SUCCESS);
13565185a700Sflorian 
13575185a700Sflorian  cleanup:
13585185a700Sflorian 	if (free_name)
13595185a700Sflorian 		free(name);
13605185a700Sflorian 	if (free_rdataset)
13615185a700Sflorian 		free(rdataset);
13625185a700Sflorian 
13635185a700Sflorian 	return (result);
13645185a700Sflorian }
13655185a700Sflorian 
13665185a700Sflorian isc_result_t
13675185a700Sflorian dns_message_parse(dns_message_t *msg, isc_buffer_t *source,
13685185a700Sflorian 		  unsigned int options)
13695185a700Sflorian {
13705185a700Sflorian 	isc_region_t r;
13715185a700Sflorian 	dns_decompress_t dctx;
13725185a700Sflorian 	isc_result_t ret;
13735185a700Sflorian 	uint16_t tmpflags;
13745185a700Sflorian 	isc_buffer_t origsource;
13751fb015a8Sflorian 	int seen_problem;
13761fb015a8Sflorian 	int ignore_tc;
13775185a700Sflorian 
13785185a700Sflorian 	REQUIRE(source != NULL);
13795185a700Sflorian 	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTPARSE);
13805185a700Sflorian 
13811fb015a8Sflorian 	seen_problem = 0;
13821fb015a8Sflorian 	ignore_tc = options & DNS_MESSAGEPARSE_IGNORETRUNCATION;
13835185a700Sflorian 
13845185a700Sflorian 	origsource = *source;
13855185a700Sflorian 
13865185a700Sflorian 	msg->header_ok = 0;
13875185a700Sflorian 	msg->question_ok = 0;
13885185a700Sflorian 
13895185a700Sflorian 	isc_buffer_remainingregion(source, &r);
13905185a700Sflorian 	if (r.length < DNS_MESSAGE_HEADERLEN)
13915185a700Sflorian 		return (ISC_R_UNEXPECTEDEND);
13925185a700Sflorian 
13935185a700Sflorian 	msg->id = isc_buffer_getuint16(source);
13945185a700Sflorian 	tmpflags = isc_buffer_getuint16(source);
13955185a700Sflorian 	msg->opcode = ((tmpflags & DNS_MESSAGE_OPCODE_MASK)
13965185a700Sflorian 		       >> DNS_MESSAGE_OPCODE_SHIFT);
13975185a700Sflorian 	msg->rcode = (dns_rcode_t)(tmpflags & DNS_MESSAGE_RCODE_MASK);
13985185a700Sflorian 	msg->flags = (tmpflags & DNS_MESSAGE_FLAG_MASK);
13995185a700Sflorian 	msg->counts[DNS_SECTION_QUESTION] = isc_buffer_getuint16(source);
14005185a700Sflorian 	msg->counts[DNS_SECTION_ANSWER] = isc_buffer_getuint16(source);
14015185a700Sflorian 	msg->counts[DNS_SECTION_AUTHORITY] = isc_buffer_getuint16(source);
14025185a700Sflorian 	msg->counts[DNS_SECTION_ADDITIONAL] = isc_buffer_getuint16(source);
14035185a700Sflorian 
14045185a700Sflorian 	msg->header_ok = 1;
14055185a700Sflorian 	msg->state = DNS_SECTION_QUESTION;
14065185a700Sflorian 
14075185a700Sflorian 	/*
14085185a700Sflorian 	 * -1 means no EDNS.
14095185a700Sflorian 	 */
14105185a700Sflorian 	dns_decompress_init(&dctx, -1, DNS_DECOMPRESS_ANY);
14115185a700Sflorian 
14125185a700Sflorian 	dns_decompress_setmethods(&dctx, DNS_COMPRESS_GLOBAL14);
14135185a700Sflorian 
14145185a700Sflorian 	ret = getquestions(source, msg, &dctx, options);
14155185a700Sflorian 	if (ret == ISC_R_UNEXPECTEDEND && ignore_tc)
14165185a700Sflorian 		goto truncated;
14175185a700Sflorian 	if (ret == DNS_R_RECOVERABLE) {
14181fb015a8Sflorian 		seen_problem = 1;
14195185a700Sflorian 		ret = ISC_R_SUCCESS;
14205185a700Sflorian 	}
14215185a700Sflorian 	if (ret != ISC_R_SUCCESS)
14225185a700Sflorian 		return (ret);
14235185a700Sflorian 	msg->question_ok = 1;
14245185a700Sflorian 
14255185a700Sflorian 	ret = getsection(source, msg, &dctx, DNS_SECTION_ANSWER, options);
14265185a700Sflorian 	if (ret == ISC_R_UNEXPECTEDEND && ignore_tc)
14275185a700Sflorian 		goto truncated;
14285185a700Sflorian 	if (ret == DNS_R_RECOVERABLE) {
14291fb015a8Sflorian 		seen_problem = 1;
14305185a700Sflorian 		ret = ISC_R_SUCCESS;
14315185a700Sflorian 	}
14325185a700Sflorian 	if (ret != ISC_R_SUCCESS)
14335185a700Sflorian 		return (ret);
14345185a700Sflorian 
14355185a700Sflorian 	ret = getsection(source, msg, &dctx, DNS_SECTION_AUTHORITY, options);
14365185a700Sflorian 	if (ret == ISC_R_UNEXPECTEDEND && ignore_tc)
14375185a700Sflorian 		goto truncated;
14385185a700Sflorian 	if (ret == DNS_R_RECOVERABLE) {
14391fb015a8Sflorian 		seen_problem = 1;
14405185a700Sflorian 		ret = ISC_R_SUCCESS;
14415185a700Sflorian 	}
14425185a700Sflorian 	if (ret != ISC_R_SUCCESS)
14435185a700Sflorian 		return (ret);
14445185a700Sflorian 
14455185a700Sflorian 	ret = getsection(source, msg, &dctx, DNS_SECTION_ADDITIONAL, options);
14465185a700Sflorian 	if (ret == ISC_R_UNEXPECTEDEND && ignore_tc)
14475185a700Sflorian 		goto truncated;
14485185a700Sflorian 	if (ret == DNS_R_RECOVERABLE) {
14491fb015a8Sflorian 		seen_problem = 1;
14505185a700Sflorian 		ret = ISC_R_SUCCESS;
14515185a700Sflorian 	}
14525185a700Sflorian 	if (ret != ISC_R_SUCCESS)
14535185a700Sflorian 		return (ret);
14545185a700Sflorian 
14555185a700Sflorian 	isc_buffer_remainingregion(source, &r);
14565185a700Sflorian 	if (r.length != 0) {
14575185a700Sflorian 		isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
14585185a700Sflorian 			      DNS_LOGMODULE_MESSAGE, ISC_LOG_DEBUG(3),
14595185a700Sflorian 			      "message has %u byte(s) of trailing garbage",
14605185a700Sflorian 			      r.length);
14615185a700Sflorian 	}
14625185a700Sflorian 
14635185a700Sflorian  truncated:
14645185a700Sflorian 	isc_buffer_usedregion(&origsource, &msg->saved);
14655185a700Sflorian 
14665185a700Sflorian 	if (ret == ISC_R_UNEXPECTEDEND && ignore_tc)
14675185a700Sflorian 		return (DNS_R_RECOVERABLE);
14681fb015a8Sflorian 	if (seen_problem)
14695185a700Sflorian 		return (DNS_R_RECOVERABLE);
14705185a700Sflorian 	return (ISC_R_SUCCESS);
14715185a700Sflorian }
14725185a700Sflorian 
14735185a700Sflorian isc_result_t
14745185a700Sflorian dns_message_renderbegin(dns_message_t *msg, dns_compress_t *cctx,
14755185a700Sflorian 			isc_buffer_t *buffer)
14765185a700Sflorian {
14775185a700Sflorian 	isc_region_t r;
14785185a700Sflorian 
14795185a700Sflorian 	REQUIRE(buffer != NULL);
14805185a700Sflorian 	REQUIRE(msg->buffer == NULL);
14815185a700Sflorian 	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
14825185a700Sflorian 
14835185a700Sflorian 	msg->cctx = cctx;
14845185a700Sflorian 
14855185a700Sflorian 	/*
14865185a700Sflorian 	 * Erase the contents of this buffer.
14875185a700Sflorian 	 */
14885185a700Sflorian 	isc_buffer_clear(buffer);
14895185a700Sflorian 
14905185a700Sflorian 	/*
14915185a700Sflorian 	 * Make certain there is enough for at least the header in this
14925185a700Sflorian 	 * buffer.
14935185a700Sflorian 	 */
14945185a700Sflorian 	isc_buffer_availableregion(buffer, &r);
14955185a700Sflorian 	if (r.length < DNS_MESSAGE_HEADERLEN)
14965185a700Sflorian 		return (ISC_R_NOSPACE);
14975185a700Sflorian 
14985185a700Sflorian 	if (r.length - DNS_MESSAGE_HEADERLEN < msg->reserved)
14995185a700Sflorian 		return (ISC_R_NOSPACE);
15005185a700Sflorian 
15015185a700Sflorian 	/*
15025185a700Sflorian 	 * Reserve enough space for the header in this buffer.
15035185a700Sflorian 	 */
15045185a700Sflorian 	isc_buffer_add(buffer, DNS_MESSAGE_HEADERLEN);
15055185a700Sflorian 
15065185a700Sflorian 	msg->buffer = buffer;
15075185a700Sflorian 
15085185a700Sflorian 	return (ISC_R_SUCCESS);
15095185a700Sflorian }
15105185a700Sflorian 
15115185a700Sflorian void
15125185a700Sflorian dns_message_renderrelease(dns_message_t *msg, unsigned int space) {
15135185a700Sflorian 	REQUIRE(space <= msg->reserved);
15145185a700Sflorian 
15155185a700Sflorian 	msg->reserved -= space;
15165185a700Sflorian }
15175185a700Sflorian 
15185185a700Sflorian isc_result_t
15195185a700Sflorian dns_message_renderreserve(dns_message_t *msg, unsigned int space) {
15205185a700Sflorian 	isc_region_t r;
15215185a700Sflorian 
15225185a700Sflorian 	if (msg->buffer != NULL) {
15235185a700Sflorian 		isc_buffer_availableregion(msg->buffer, &r);
15245185a700Sflorian 		if (r.length < (space + msg->reserved))
15255185a700Sflorian 			return (ISC_R_NOSPACE);
15265185a700Sflorian 	}
15275185a700Sflorian 
15285185a700Sflorian 	msg->reserved += space;
15295185a700Sflorian 
15305185a700Sflorian 	return (ISC_R_SUCCESS);
15315185a700Sflorian }
15325185a700Sflorian 
15331fb015a8Sflorian static inline int
15347d5fc286Sflorian wrong_priority(dns_rdataset_t *rds, int pass) {
15355185a700Sflorian 	int pass_needed;
15365185a700Sflorian 
15375185a700Sflorian 	/*
15385185a700Sflorian 	 * If we are not rendering class IN, this ordering is bogus.
15395185a700Sflorian 	 */
15405185a700Sflorian 	if (rds->rdclass != dns_rdataclass_in)
15411fb015a8Sflorian 		return (0);
15425185a700Sflorian 
15435185a700Sflorian 	switch (rds->type) {
15445185a700Sflorian 	case dns_rdatatype_a:
15455185a700Sflorian 	case dns_rdatatype_aaaa:
15465185a700Sflorian 		pass_needed = 3;
15475185a700Sflorian 		break;
15485185a700Sflorian 	case dns_rdatatype_rrsig:
15495185a700Sflorian 	case dns_rdatatype_dnskey:
15505185a700Sflorian 		pass_needed = 2;
15515185a700Sflorian 		break;
15525185a700Sflorian 	default:
15535185a700Sflorian 		pass_needed = 1;
15545185a700Sflorian 	}
15555185a700Sflorian 
15565185a700Sflorian 	if (pass_needed >= pass)
15571fb015a8Sflorian 		return (0);
15585185a700Sflorian 
15591fb015a8Sflorian 	return (1);
15605185a700Sflorian }
15615185a700Sflorian 
15625185a700Sflorian static isc_result_t
15635185a700Sflorian renderset(dns_rdataset_t *rdataset, dns_name_t *owner_name,
15645185a700Sflorian 	  dns_compress_t *cctx, isc_buffer_t *target,
1565ad5cf538Sjung 	  unsigned int reserved, unsigned int *countp)
15665185a700Sflorian {
15675185a700Sflorian 	isc_result_t result;
15685185a700Sflorian 
15695185a700Sflorian 	/*
15705185a700Sflorian 	 * Shrink the space in the buffer by the reserved amount.
15715185a700Sflorian 	 */
15725185a700Sflorian 	if (target->length - target->used < reserved)
15735185a700Sflorian 		return (ISC_R_NOSPACE);
15745185a700Sflorian 
15755185a700Sflorian 	target->length -= reserved;
15765185a700Sflorian 	result = dns_rdataset_towire(rdataset, owner_name,
1577ad5cf538Sjung 				     cctx, target, countp);
15785185a700Sflorian 	target->length += reserved;
15795185a700Sflorian 
15805185a700Sflorian 	return (result);
15815185a700Sflorian }
15825185a700Sflorian 
15835185a700Sflorian static void
15845185a700Sflorian maybe_clear_ad(dns_message_t *msg, dns_section_t sectionid) {
15855185a700Sflorian 	if (msg->counts[sectionid] == 0 &&
15865185a700Sflorian 	    (sectionid == DNS_SECTION_ANSWER ||
15875185a700Sflorian 	     (sectionid == DNS_SECTION_AUTHORITY &&
15885185a700Sflorian 	      msg->counts[DNS_SECTION_ANSWER] == 0)))
15895185a700Sflorian 		msg->flags &= ~DNS_MESSAGEFLAG_AD;
15905185a700Sflorian }
15915185a700Sflorian 
15925185a700Sflorian isc_result_t
15937d5fc286Sflorian dns_message_rendersection(dns_message_t *msg, dns_section_t sectionid)
15945185a700Sflorian {
15955185a700Sflorian 	dns_namelist_t *section;
15965185a700Sflorian 	dns_name_t *name, *next_name;
15975185a700Sflorian 	dns_rdataset_t *rdataset, *next_rdataset;
15985185a700Sflorian 	unsigned int count, total;
15995185a700Sflorian 	isc_result_t result;
16005185a700Sflorian 	isc_buffer_t st; /* for rollbacks */
16015185a700Sflorian 	int pass;
16025185a700Sflorian 
16035185a700Sflorian 	REQUIRE(msg->buffer != NULL);
16045185a700Sflorian 	REQUIRE(VALID_NAMED_SECTION(sectionid));
16055185a700Sflorian 
16065185a700Sflorian 	section = &msg->sections[sectionid];
16075185a700Sflorian 
16087d5fc286Sflorian 	if (sectionid == DNS_SECTION_ADDITIONAL)
16095185a700Sflorian 		pass = 3;
16107d5fc286Sflorian 	else
16115185a700Sflorian 		pass = 1;
16125185a700Sflorian 
16135185a700Sflorian 	/*
16145185a700Sflorian 	 * Shrink the space in the buffer by the reserved amount.
16155185a700Sflorian 	 */
16165185a700Sflorian 	if (msg->buffer->length - msg->buffer->used < msg->reserved)
16175185a700Sflorian 		return (ISC_R_NOSPACE);
16185185a700Sflorian 	msg->buffer->length -= msg->reserved;
16195185a700Sflorian 
16205185a700Sflorian 	total = 0;
16215185a700Sflorian 
16225185a700Sflorian 	do {
16235185a700Sflorian 		name = ISC_LIST_HEAD(*section);
16245185a700Sflorian 		if (name == NULL) {
16255185a700Sflorian 			msg->buffer->length += msg->reserved;
16265185a700Sflorian 			msg->counts[sectionid] += total;
16275185a700Sflorian 			return (ISC_R_SUCCESS);
16285185a700Sflorian 		}
16295185a700Sflorian 
16305185a700Sflorian 		while (name != NULL) {
16315185a700Sflorian 			next_name = ISC_LIST_NEXT(name, link);
16325185a700Sflorian 
16335185a700Sflorian 			rdataset = ISC_LIST_HEAD(name->list);
16345185a700Sflorian 			while (rdataset != NULL) {
16355185a700Sflorian 				next_rdataset = ISC_LIST_NEXT(rdataset, link);
16365185a700Sflorian 
16375185a700Sflorian 				if ((rdataset->attributes &
16385185a700Sflorian 				     DNS_RDATASETATTR_RENDERED) != 0)
16395185a700Sflorian 					goto next;
16405185a700Sflorian 
16417d5fc286Sflorian 				if ((sectionid == DNS_SECTION_ADDITIONAL)
16427d5fc286Sflorian 				    && wrong_priority(rdataset, pass))
16435185a700Sflorian 					goto next;
16445185a700Sflorian 
16455185a700Sflorian 				st = *(msg->buffer);
16465185a700Sflorian 
16475185a700Sflorian 				count = 0;
16485185a700Sflorian 				result = dns_rdataset_towiresorted(
16495185a700Sflorian 						  rdataset,
16505185a700Sflorian 						  name,
16515185a700Sflorian 						  msg->cctx,
16525185a700Sflorian 						  msg->buffer,
16535185a700Sflorian 						  &count);
16545185a700Sflorian 
16555185a700Sflorian 				total += count;
16565185a700Sflorian 
16575185a700Sflorian 				/*
16585185a700Sflorian 				 * If out of space, record stats on what we
16595185a700Sflorian 				 * rendered so far, and return that status.
16605185a700Sflorian 				 *
16615185a700Sflorian 				 */
16625185a700Sflorian 				if (result != ISC_R_SUCCESS) {
16635185a700Sflorian 					INSIST(st.used < 65536);
16645185a700Sflorian 					dns_compress_rollback(msg->cctx,
16655185a700Sflorian 							(uint16_t)st.used);
16665185a700Sflorian 					*(msg->buffer) = st;  /* rollback */
16675185a700Sflorian 					msg->buffer->length += msg->reserved;
16685185a700Sflorian 					msg->counts[sectionid] += total;
16695185a700Sflorian 					maybe_clear_ad(msg, sectionid);
16705185a700Sflorian 					return (result);
16715185a700Sflorian 				}
16725185a700Sflorian 
16735185a700Sflorian 				/*
16745185a700Sflorian 				 * If we have rendered non-validated data,
16755185a700Sflorian 				 * ensure that the AD bit is not set.
16765185a700Sflorian 				 */
167712363972Sflorian 				if ((sectionid == DNS_SECTION_ANSWER ||
16785185a700Sflorian 				     sectionid == DNS_SECTION_AUTHORITY))
16795185a700Sflorian 					msg->flags &= ~DNS_MESSAGEFLAG_AD;
16805185a700Sflorian 
16815185a700Sflorian 				rdataset->attributes |=
16825185a700Sflorian 					DNS_RDATASETATTR_RENDERED;
16835185a700Sflorian 
16845185a700Sflorian 			next:
16855185a700Sflorian 				rdataset = next_rdataset;
16865185a700Sflorian 			}
16875185a700Sflorian 
16885185a700Sflorian 			name = next_name;
16895185a700Sflorian 		}
16905185a700Sflorian 	} while (--pass != 0);
16915185a700Sflorian 
16925185a700Sflorian 	msg->buffer->length += msg->reserved;
16935185a700Sflorian 	msg->counts[sectionid] += total;
16945185a700Sflorian 
16955185a700Sflorian 	return (ISC_R_SUCCESS);
16965185a700Sflorian }
16975185a700Sflorian 
16985185a700Sflorian void
16995185a700Sflorian dns_message_renderheader(dns_message_t *msg, isc_buffer_t *target) {
17005185a700Sflorian 	uint16_t tmp;
17015185a700Sflorian 	isc_region_t r;
17025185a700Sflorian 
17035185a700Sflorian 	REQUIRE(target != NULL);
17045185a700Sflorian 
17055185a700Sflorian 	isc_buffer_availableregion(target, &r);
17065185a700Sflorian 	REQUIRE(r.length >= DNS_MESSAGE_HEADERLEN);
17075185a700Sflorian 
17085185a700Sflorian 	isc_buffer_putuint16(target, msg->id);
17095185a700Sflorian 
17105185a700Sflorian 	tmp = ((msg->opcode << DNS_MESSAGE_OPCODE_SHIFT)
17115185a700Sflorian 	       & DNS_MESSAGE_OPCODE_MASK);
17125185a700Sflorian 	tmp |= (msg->rcode & DNS_MESSAGE_RCODE_MASK);
17135185a700Sflorian 	tmp |= (msg->flags & DNS_MESSAGE_FLAG_MASK);
17145185a700Sflorian 
17155185a700Sflorian 	INSIST(msg->counts[DNS_SECTION_QUESTION]  < 65536 &&
17165185a700Sflorian 	       msg->counts[DNS_SECTION_ANSWER]    < 65536 &&
17175185a700Sflorian 	       msg->counts[DNS_SECTION_AUTHORITY] < 65536 &&
17185185a700Sflorian 	       msg->counts[DNS_SECTION_ADDITIONAL] < 65536);
17195185a700Sflorian 
17205185a700Sflorian 	isc_buffer_putuint16(target, tmp);
17215185a700Sflorian 	isc_buffer_putuint16(target,
17225185a700Sflorian 			    (uint16_t)msg->counts[DNS_SECTION_QUESTION]);
17235185a700Sflorian 	isc_buffer_putuint16(target,
17245185a700Sflorian 			    (uint16_t)msg->counts[DNS_SECTION_ANSWER]);
17255185a700Sflorian 	isc_buffer_putuint16(target,
17265185a700Sflorian 			    (uint16_t)msg->counts[DNS_SECTION_AUTHORITY]);
17275185a700Sflorian 	isc_buffer_putuint16(target,
17285185a700Sflorian 			    (uint16_t)msg->counts[DNS_SECTION_ADDITIONAL]);
17295185a700Sflorian }
17305185a700Sflorian 
17315185a700Sflorian isc_result_t
17325185a700Sflorian dns_message_renderend(dns_message_t *msg) {
17335185a700Sflorian 	isc_buffer_t tmpbuf;
17345185a700Sflorian 	isc_region_t r;
17355185a700Sflorian 	int result;
17365185a700Sflorian 	unsigned int count;
17375185a700Sflorian 
17385185a700Sflorian 	REQUIRE(msg->buffer != NULL);
17395185a700Sflorian 
17405185a700Sflorian 	if ((msg->rcode & ~DNS_MESSAGE_RCODE_MASK) != 0 && msg->opt == NULL) {
17415185a700Sflorian 		/*
17425185a700Sflorian 		 * We have an extended rcode but are not using EDNS.
17435185a700Sflorian 		 */
17445185a700Sflorian 		return (DNS_R_FORMERR);
17455185a700Sflorian 	}
17465185a700Sflorian 
17475185a700Sflorian 	/*
17485185a700Sflorian 	 * If we're adding a OPT, TSIG or SIG(0) to a truncated message,
17495185a700Sflorian 	 * clear all rdatasets from the message except for the question
17505185a700Sflorian 	 * before adding the OPT, TSIG or SIG(0).  If the question doesn't
17515185a700Sflorian 	 * fit, don't include it.
17525185a700Sflorian 	 */
17535185a700Sflorian 	if ((msg->tsigkey != NULL || msg->opt) &&
17545185a700Sflorian 	    (msg->flags & DNS_MESSAGEFLAG_TC) != 0)
17555185a700Sflorian 	{
17565185a700Sflorian 		isc_buffer_t *buf;
17575185a700Sflorian 
17585185a700Sflorian 		msgresetnames(msg, DNS_SECTION_ANSWER);
17595185a700Sflorian 		buf = msg->buffer;
17605185a700Sflorian 		dns_message_renderreset(msg);
17615185a700Sflorian 		msg->buffer = buf;
17625185a700Sflorian 		isc_buffer_clear(msg->buffer);
17635185a700Sflorian 		isc_buffer_add(msg->buffer, DNS_MESSAGE_HEADERLEN);
17645185a700Sflorian 		dns_compress_rollback(msg->cctx, 0);
17657d5fc286Sflorian 		result = dns_message_rendersection(msg, DNS_SECTION_QUESTION);
17665185a700Sflorian 		if (result != ISC_R_SUCCESS && result != ISC_R_NOSPACE)
17675185a700Sflorian 			return (result);
17685185a700Sflorian 	}
17695185a700Sflorian 
17705185a700Sflorian 	/*
17715185a700Sflorian 	 * If we've got an OPT record, render it.
17725185a700Sflorian 	 */
17735185a700Sflorian 	if (msg->opt != NULL) {
17745185a700Sflorian 		dns_message_renderrelease(msg, msg->opt_reserved);
17755185a700Sflorian 		msg->opt_reserved = 0;
17765185a700Sflorian 		/*
17775185a700Sflorian 		 * Set the extended rcode.
17785185a700Sflorian 		 */
17795185a700Sflorian 		msg->opt->ttl &= ~DNS_MESSAGE_EDNSRCODE_MASK;
17805185a700Sflorian 		msg->opt->ttl |= ((msg->rcode << 20) &
17815185a700Sflorian 				  DNS_MESSAGE_EDNSRCODE_MASK);
17825185a700Sflorian 		/*
17835185a700Sflorian 		 * Render.
17845185a700Sflorian 		 */
17855185a700Sflorian 		count = 0;
17865185a700Sflorian 		result = renderset(msg->opt, dns_rootname, msg->cctx,
1787ad5cf538Sjung 				   msg->buffer, msg->reserved, &count);
17885185a700Sflorian 		msg->counts[DNS_SECTION_ADDITIONAL] += count;
17895185a700Sflorian 		if (result != ISC_R_SUCCESS)
17905185a700Sflorian 			return (result);
17915185a700Sflorian 	}
17925185a700Sflorian 
17935185a700Sflorian 	/*
17945185a700Sflorian 	 * If we're adding a TSIG record, generate and render it.
17955185a700Sflorian 	 */
17965185a700Sflorian 	if (msg->tsigkey != NULL) {
17975185a700Sflorian 		dns_message_renderrelease(msg, msg->sig_reserved);
17985185a700Sflorian 		msg->sig_reserved = 0;
17995185a700Sflorian 		result = dns_tsig_sign(msg);
18005185a700Sflorian 		if (result != ISC_R_SUCCESS)
18015185a700Sflorian 			return (result);
18025185a700Sflorian 		count = 0;
18035185a700Sflorian 		result = renderset(msg->tsig, msg->tsigname, msg->cctx,
1804ad5cf538Sjung 				   msg->buffer, msg->reserved, &count);
18055185a700Sflorian 		msg->counts[DNS_SECTION_ADDITIONAL] += count;
18065185a700Sflorian 		if (result != ISC_R_SUCCESS)
18075185a700Sflorian 			return (result);
18085185a700Sflorian 	}
18095185a700Sflorian 
18105185a700Sflorian 	isc_buffer_usedregion(msg->buffer, &r);
18115185a700Sflorian 	isc_buffer_init(&tmpbuf, r.base, r.length);
18125185a700Sflorian 
18135185a700Sflorian 	dns_message_renderheader(msg, &tmpbuf);
18145185a700Sflorian 
18155185a700Sflorian 	msg->buffer = NULL;  /* forget about this buffer only on success XXX */
18165185a700Sflorian 
18175185a700Sflorian 	return (ISC_R_SUCCESS);
18185185a700Sflorian }
18195185a700Sflorian 
18205185a700Sflorian void
18215185a700Sflorian dns_message_renderreset(dns_message_t *msg) {
18225185a700Sflorian 	unsigned int i;
18235185a700Sflorian 	dns_name_t *name;
18245185a700Sflorian 	dns_rdataset_t *rds;
18255185a700Sflorian 
18265185a700Sflorian 	/*
18275185a700Sflorian 	 * Reset the message so that it may be rendered again.
18285185a700Sflorian 	 */
18295185a700Sflorian 
18305185a700Sflorian 	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
18315185a700Sflorian 
18325185a700Sflorian 	msg->buffer = NULL;
18335185a700Sflorian 
18345185a700Sflorian 	for (i = 0; i < DNS_SECTION_MAX; i++) {
18355185a700Sflorian 		msg->cursors[i] = NULL;
18365185a700Sflorian 		msg->counts[i] = 0;
18375185a700Sflorian 		for (name = ISC_LIST_HEAD(msg->sections[i]);
18385185a700Sflorian 		     name != NULL;
18395185a700Sflorian 		     name = ISC_LIST_NEXT(name, link)) {
18405185a700Sflorian 			for (rds = ISC_LIST_HEAD(name->list);
18415185a700Sflorian 			     rds != NULL;
18425185a700Sflorian 			     rds = ISC_LIST_NEXT(rds, link)) {
18435185a700Sflorian 				rds->attributes &= ~DNS_RDATASETATTR_RENDERED;
18445185a700Sflorian 			}
18455185a700Sflorian 		}
18465185a700Sflorian 	}
18475185a700Sflorian 	if (msg->tsigname != NULL)
18485185a700Sflorian 		dns_message_puttempname(msg, &msg->tsigname);
18495185a700Sflorian 	if (msg->tsig != NULL) {
18505185a700Sflorian 		dns_rdataset_disassociate(msg->tsig);
18515185a700Sflorian 		dns_message_puttemprdataset(msg, &msg->tsig);
18525185a700Sflorian 	}
18535185a700Sflorian 	if (msg->sig0 != NULL) {
18545185a700Sflorian 		dns_rdataset_disassociate(msg->sig0);
18555185a700Sflorian 		dns_message_puttemprdataset(msg, &msg->sig0);
18565185a700Sflorian 	}
18575185a700Sflorian }
18585185a700Sflorian 
18595185a700Sflorian isc_result_t
18605185a700Sflorian dns_message_firstname(dns_message_t *msg, dns_section_t section) {
18615185a700Sflorian 	REQUIRE(VALID_NAMED_SECTION(section));
18625185a700Sflorian 
18635185a700Sflorian 	msg->cursors[section] = ISC_LIST_HEAD(msg->sections[section]);
18645185a700Sflorian 
18655185a700Sflorian 	if (msg->cursors[section] == NULL)
18665185a700Sflorian 		return (ISC_R_NOMORE);
18675185a700Sflorian 
18685185a700Sflorian 	return (ISC_R_SUCCESS);
18695185a700Sflorian }
18705185a700Sflorian 
18715185a700Sflorian isc_result_t
18725185a700Sflorian dns_message_nextname(dns_message_t *msg, dns_section_t section) {
18735185a700Sflorian 	REQUIRE(VALID_NAMED_SECTION(section));
18745185a700Sflorian 	REQUIRE(msg->cursors[section] != NULL);
18755185a700Sflorian 
18765185a700Sflorian 	msg->cursors[section] = ISC_LIST_NEXT(msg->cursors[section], link);
18775185a700Sflorian 
18785185a700Sflorian 	if (msg->cursors[section] == NULL)
18795185a700Sflorian 		return (ISC_R_NOMORE);
18805185a700Sflorian 
18815185a700Sflorian 	return (ISC_R_SUCCESS);
18825185a700Sflorian }
18835185a700Sflorian 
18845185a700Sflorian void
18855185a700Sflorian dns_message_currentname(dns_message_t *msg, dns_section_t section,
18865185a700Sflorian 			dns_name_t **name)
18875185a700Sflorian {
18885185a700Sflorian 	REQUIRE(VALID_NAMED_SECTION(section));
18895185a700Sflorian 	REQUIRE(name != NULL && *name == NULL);
18905185a700Sflorian 	REQUIRE(msg->cursors[section] != NULL);
18915185a700Sflorian 
18925185a700Sflorian 	*name = msg->cursors[section];
18935185a700Sflorian }
18945185a700Sflorian 
18955185a700Sflorian isc_result_t
18965185a700Sflorian dns_message_findname(dns_message_t *msg, dns_section_t section,
18975185a700Sflorian 		     dns_name_t *target, dns_rdatatype_t type,
18985185a700Sflorian 		     dns_rdatatype_t covers, dns_name_t **name,
18995185a700Sflorian 		     dns_rdataset_t **rdataset)
19005185a700Sflorian {
19015185a700Sflorian 	dns_name_t *foundname;
19025185a700Sflorian 	isc_result_t result;
19035185a700Sflorian 
19045185a700Sflorian 	/*
19055185a700Sflorian 	 * XXX These requirements are probably too intensive, especially
19065185a700Sflorian 	 * where things can be NULL, but as they are they ensure that if
19075185a700Sflorian 	 * something is NON-NULL, indicating that the caller expects it
19085185a700Sflorian 	 * to be filled in, that we can in fact fill it in.
19095185a700Sflorian 	 */
19105185a700Sflorian 	REQUIRE(msg != NULL);
19115185a700Sflorian 	REQUIRE(VALID_SECTION(section));
19125185a700Sflorian 	REQUIRE(target != NULL);
19135185a700Sflorian 	REQUIRE(name == NULL || *name == NULL);
19145185a700Sflorian 
19155185a700Sflorian 	if (type == dns_rdatatype_any) {
19165185a700Sflorian 		REQUIRE(rdataset == NULL);
19175185a700Sflorian 	} else {
19185185a700Sflorian 		REQUIRE(rdataset == NULL || *rdataset == NULL);
19195185a700Sflorian 	}
19205185a700Sflorian 
19215185a700Sflorian 	result = findname(&foundname, target,
19225185a700Sflorian 			  &msg->sections[section]);
19235185a700Sflorian 
19245185a700Sflorian 	if (result == ISC_R_NOTFOUND)
19255185a700Sflorian 		return (DNS_R_NXDOMAIN);
19265185a700Sflorian 	else if (result != ISC_R_SUCCESS)
19275185a700Sflorian 		return (result);
19285185a700Sflorian 
19295185a700Sflorian 	if (name != NULL)
19305185a700Sflorian 		*name = foundname;
19315185a700Sflorian 
19325185a700Sflorian 	/*
19335185a700Sflorian 	 * And now look for the type.
19345185a700Sflorian 	 */
19355185a700Sflorian 	if (type == dns_rdatatype_any)
19365185a700Sflorian 		return (ISC_R_SUCCESS);
19375185a700Sflorian 
19385185a700Sflorian 	result = dns_message_findtype(foundname, type, covers, rdataset);
19395185a700Sflorian 	if (result == ISC_R_NOTFOUND)
19405185a700Sflorian 		return (DNS_R_NXRRSET);
19415185a700Sflorian 
19425185a700Sflorian 	return (result);
19435185a700Sflorian }
19445185a700Sflorian 
19455185a700Sflorian void
19465185a700Sflorian dns_message_addname(dns_message_t *msg, dns_name_t *name,
19475185a700Sflorian 		    dns_section_t section)
19485185a700Sflorian {
19495185a700Sflorian 	REQUIRE(msg != NULL);
19505185a700Sflorian 	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
19515185a700Sflorian 	REQUIRE(name != NULL);
19525185a700Sflorian 	REQUIRE(VALID_NAMED_SECTION(section));
19535185a700Sflorian 
19545185a700Sflorian 	ISC_LIST_APPEND(msg->sections[section], name, link);
19555185a700Sflorian }
19565185a700Sflorian 
19575185a700Sflorian isc_result_t
19585185a700Sflorian dns_message_gettempname(dns_message_t *msg, dns_name_t **item) {
19595185a700Sflorian 	REQUIRE(item != NULL && *item == NULL);
19605185a700Sflorian 
1961ad5cf538Sjung 	UNUSED(msg);
1962ad5cf538Sjung 
19635185a700Sflorian 	*item = malloc(sizeof(dns_name_t));
19645185a700Sflorian 	if (*item == NULL)
19655185a700Sflorian 		return (ISC_R_NOMEMORY);
19665185a700Sflorian 	dns_name_init(*item, NULL);
19675185a700Sflorian 
19685185a700Sflorian 	return (ISC_R_SUCCESS);
19695185a700Sflorian }
19705185a700Sflorian 
19715185a700Sflorian isc_result_t
19725185a700Sflorian dns_message_gettemprdata(dns_message_t *msg, dns_rdata_t **item) {
19735185a700Sflorian 	REQUIRE(item != NULL && *item == NULL);
19745185a700Sflorian 
19755185a700Sflorian 	*item = newrdata(msg);
19765185a700Sflorian 	if (*item == NULL)
19775185a700Sflorian 		return (ISC_R_NOMEMORY);
19785185a700Sflorian 
19795185a700Sflorian 	return (ISC_R_SUCCESS);
19805185a700Sflorian }
19815185a700Sflorian 
19825185a700Sflorian isc_result_t
19835185a700Sflorian dns_message_gettemprdataset(dns_message_t *msg, dns_rdataset_t **item) {
19845185a700Sflorian 	REQUIRE(item != NULL && *item == NULL);
19855185a700Sflorian 
1986ad5cf538Sjung 	UNUSED(msg);
1987ad5cf538Sjung 
19885185a700Sflorian 	*item = malloc(sizeof(dns_rdataset_t));
19895185a700Sflorian 	if (*item == NULL)
19905185a700Sflorian 		return (ISC_R_NOMEMORY);
19915185a700Sflorian 
19925185a700Sflorian 	dns_rdataset_init(*item);
19935185a700Sflorian 
19945185a700Sflorian 	return (ISC_R_SUCCESS);
19955185a700Sflorian }
19965185a700Sflorian 
19975185a700Sflorian isc_result_t
19985185a700Sflorian dns_message_gettemprdatalist(dns_message_t *msg, dns_rdatalist_t **item) {
19995185a700Sflorian 	REQUIRE(item != NULL && *item == NULL);
20005185a700Sflorian 
20015185a700Sflorian 	*item = newrdatalist(msg);
20025185a700Sflorian 	if (*item == NULL)
20035185a700Sflorian 		return (ISC_R_NOMEMORY);
20045185a700Sflorian 
20055185a700Sflorian 	return (ISC_R_SUCCESS);
20065185a700Sflorian }
20075185a700Sflorian 
20085185a700Sflorian void
20095185a700Sflorian dns_message_puttempname(dns_message_t *msg, dns_name_t **item) {
20105185a700Sflorian 	REQUIRE(item != NULL && *item != NULL);
20115185a700Sflorian 
2012ad5cf538Sjung 	UNUSED(msg);
2013ad5cf538Sjung 
20145185a700Sflorian 	if (dns_name_dynamic(*item))
20155185a700Sflorian 		dns_name_free(*item);
20165185a700Sflorian 	free(*item);
20175185a700Sflorian 	*item = NULL;
20185185a700Sflorian }
20195185a700Sflorian 
20205185a700Sflorian void
20215185a700Sflorian dns_message_puttemprdata(dns_message_t *msg, dns_rdata_t **item) {
20225185a700Sflorian 	REQUIRE(item != NULL && *item != NULL);
20235185a700Sflorian 
20245185a700Sflorian 	releaserdata(msg, *item);
20255185a700Sflorian 	*item = NULL;
20265185a700Sflorian }
20275185a700Sflorian 
20285185a700Sflorian void
20295185a700Sflorian dns_message_puttemprdataset(dns_message_t *msg, dns_rdataset_t **item) {
20305185a700Sflorian 	REQUIRE(item != NULL && *item != NULL);
20315185a700Sflorian 	REQUIRE(!dns_rdataset_isassociated(*item));
2032ad5cf538Sjung 
2033ad5cf538Sjung 	UNUSED(msg);
2034ad5cf538Sjung 
20355185a700Sflorian 	free(*item);
20365185a700Sflorian 	*item = NULL;
20375185a700Sflorian }
20385185a700Sflorian 
20395185a700Sflorian void
20405185a700Sflorian dns_message_puttemprdatalist(dns_message_t *msg, dns_rdatalist_t **item) {
20415185a700Sflorian 	REQUIRE(item != NULL && *item != NULL);
20425185a700Sflorian 
20435185a700Sflorian 	releaserdatalist(msg, *item);
20445185a700Sflorian 	*item = NULL;
20455185a700Sflorian }
20465185a700Sflorian 
20475185a700Sflorian isc_result_t
20485185a700Sflorian dns_message_peekheader(isc_buffer_t *source, dns_messageid_t *idp,
20495185a700Sflorian 		       unsigned int *flagsp)
20505185a700Sflorian {
20515185a700Sflorian 	isc_region_t r;
20525185a700Sflorian 	isc_buffer_t buffer;
20535185a700Sflorian 	dns_messageid_t id;
20545185a700Sflorian 	unsigned int flags;
20555185a700Sflorian 
20565185a700Sflorian 	REQUIRE(source != NULL);
20575185a700Sflorian 
20585185a700Sflorian 	buffer = *source;
20595185a700Sflorian 
20605185a700Sflorian 	isc_buffer_remainingregion(&buffer, &r);
20615185a700Sflorian 	if (r.length < DNS_MESSAGE_HEADERLEN)
20625185a700Sflorian 		return (ISC_R_UNEXPECTEDEND);
20635185a700Sflorian 
20645185a700Sflorian 	id = isc_buffer_getuint16(&buffer);
20655185a700Sflorian 	flags = isc_buffer_getuint16(&buffer);
20665185a700Sflorian 	flags &= DNS_MESSAGE_FLAG_MASK;
20675185a700Sflorian 
20685185a700Sflorian 	if (flagsp != NULL)
20695185a700Sflorian 		*flagsp = flags;
20705185a700Sflorian 	if (idp != NULL)
20715185a700Sflorian 		*idp = id;
20725185a700Sflorian 
20735185a700Sflorian 	return (ISC_R_SUCCESS);
20745185a700Sflorian }
20755185a700Sflorian 
20765185a700Sflorian dns_rdataset_t *
20775185a700Sflorian dns_message_getopt(dns_message_t *msg) {
20785185a700Sflorian 
20795185a700Sflorian 	/*
20805185a700Sflorian 	 * Get the OPT record for 'msg'.
20815185a700Sflorian 	 */
20825185a700Sflorian 	return (msg->opt);
20835185a700Sflorian }
20845185a700Sflorian 
20855185a700Sflorian isc_result_t
20865185a700Sflorian dns_message_setopt(dns_message_t *msg, dns_rdataset_t *opt) {
20875185a700Sflorian 	isc_result_t result;
20885185a700Sflorian 	dns_rdata_t rdata = DNS_RDATA_INIT;
20895185a700Sflorian 
20905185a700Sflorian 	/*
20915185a700Sflorian 	 * Set the OPT record for 'msg'.
20925185a700Sflorian 	 */
20935185a700Sflorian 
20945185a700Sflorian 	/*
20955185a700Sflorian 	 * The space required for an OPT record is:
20965185a700Sflorian 	 *
20975185a700Sflorian 	 *	1 byte for the name
20985185a700Sflorian 	 *	2 bytes for the type
20995185a700Sflorian 	 *	2 bytes for the class
21005185a700Sflorian 	 *	4 bytes for the ttl
21015185a700Sflorian 	 *	2 bytes for the rdata length
21025185a700Sflorian 	 * ---------------------------------
21035185a700Sflorian 	 *     11 bytes
21045185a700Sflorian 	 *
21055185a700Sflorian 	 * plus the length of the rdata.
21065185a700Sflorian 	 */
21075185a700Sflorian 
21085185a700Sflorian 	REQUIRE(opt->type == dns_rdatatype_opt);
21095185a700Sflorian 	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
21105185a700Sflorian 	REQUIRE(msg->state == DNS_SECTION_ANY);
21115185a700Sflorian 
21125185a700Sflorian 	msgresetopt(msg);
21135185a700Sflorian 
21145185a700Sflorian 	result = dns_rdataset_first(opt);
21155185a700Sflorian 	if (result != ISC_R_SUCCESS)
21165185a700Sflorian 		goto cleanup;
21175185a700Sflorian 	dns_rdataset_current(opt, &rdata);
21185185a700Sflorian 	msg->opt_reserved = 11 + rdata.length;
21195185a700Sflorian 	result = dns_message_renderreserve(msg, msg->opt_reserved);
21205185a700Sflorian 	if (result != ISC_R_SUCCESS) {
21215185a700Sflorian 		msg->opt_reserved = 0;
21225185a700Sflorian 		goto cleanup;
21235185a700Sflorian 	}
21245185a700Sflorian 
21255185a700Sflorian 	msg->opt = opt;
21265185a700Sflorian 
21275185a700Sflorian 	return (ISC_R_SUCCESS);
21285185a700Sflorian 
21295185a700Sflorian  cleanup:
21305185a700Sflorian 	dns_rdataset_disassociate(opt);
21315185a700Sflorian 	dns_message_puttemprdataset(msg, &opt);
21325185a700Sflorian 	return (result);
21335185a700Sflorian }
21345185a700Sflorian 
21355185a700Sflorian dns_rdataset_t *
21365185a700Sflorian dns_message_gettsig(dns_message_t *msg, dns_name_t **owner) {
21375185a700Sflorian 
21385185a700Sflorian 	/*
21395185a700Sflorian 	 * Get the TSIG record and owner for 'msg'.
21405185a700Sflorian 	 */
21415185a700Sflorian 
21425185a700Sflorian 	REQUIRE(owner == NULL || *owner == NULL);
21435185a700Sflorian 
21445185a700Sflorian 	if (owner != NULL)
21455185a700Sflorian 		*owner = msg->tsigname;
21465185a700Sflorian 	return (msg->tsig);
21475185a700Sflorian }
21485185a700Sflorian 
21495185a700Sflorian isc_result_t
21505185a700Sflorian dns_message_settsigkey(dns_message_t *msg, dns_tsigkey_t *key) {
21515185a700Sflorian 	isc_result_t result;
21525185a700Sflorian 
21535185a700Sflorian 	/*
21545185a700Sflorian 	 * Set the TSIG key for 'msg'
21555185a700Sflorian 	 */
21565185a700Sflorian 
21575185a700Sflorian 	REQUIRE(msg->state == DNS_SECTION_ANY);
21585185a700Sflorian 
21595185a700Sflorian 	if (key == NULL && msg->tsigkey != NULL) {
21605185a700Sflorian 		if (msg->sig_reserved != 0) {
21615185a700Sflorian 			dns_message_renderrelease(msg, msg->sig_reserved);
21625185a700Sflorian 			msg->sig_reserved = 0;
21635185a700Sflorian 		}
21645185a700Sflorian 		dns_tsigkey_detach(&msg->tsigkey);
21655185a700Sflorian 	}
21665185a700Sflorian 	if (key != NULL) {
21675185a700Sflorian 		REQUIRE(msg->tsigkey == NULL);
21685185a700Sflorian 		dns_tsigkey_attach(key, &msg->tsigkey);
21695185a700Sflorian 		if (msg->from_to_wire == DNS_MESSAGE_INTENTRENDER) {
21705185a700Sflorian 			msg->sig_reserved = spacefortsig(msg->tsigkey, 0);
21715185a700Sflorian 			result = dns_message_renderreserve(msg,
21725185a700Sflorian 							   msg->sig_reserved);
21735185a700Sflorian 			if (result != ISC_R_SUCCESS) {
21745185a700Sflorian 				dns_tsigkey_detach(&msg->tsigkey);
21755185a700Sflorian 				msg->sig_reserved = 0;
21765185a700Sflorian 				return (result);
21775185a700Sflorian 			}
21785185a700Sflorian 		}
21795185a700Sflorian 	}
21805185a700Sflorian 	return (ISC_R_SUCCESS);
21815185a700Sflorian }
21825185a700Sflorian 
21835185a700Sflorian dns_tsigkey_t *
21845185a700Sflorian dns_message_gettsigkey(dns_message_t *msg) {
21855185a700Sflorian 
21865185a700Sflorian 	/*
21875185a700Sflorian 	 * Get the TSIG key for 'msg'
21885185a700Sflorian 	 */
21895185a700Sflorian 	return (msg->tsigkey);
21905185a700Sflorian }
21915185a700Sflorian 
21925185a700Sflorian isc_result_t
21935185a700Sflorian dns_message_setquerytsig(dns_message_t *msg, isc_buffer_t *querytsig) {
21945185a700Sflorian 	dns_rdata_t *rdata = NULL;
21955185a700Sflorian 	dns_rdatalist_t *list = NULL;
21965185a700Sflorian 	dns_rdataset_t *set = NULL;
21975185a700Sflorian 	isc_buffer_t *buf = NULL;
21985185a700Sflorian 	isc_region_t r;
21995185a700Sflorian 	isc_result_t result;
22005185a700Sflorian 
22015185a700Sflorian 	REQUIRE(msg->querytsig == NULL);
22025185a700Sflorian 
22035185a700Sflorian 	if (querytsig == NULL)
22045185a700Sflorian 		return (ISC_R_SUCCESS);
22055185a700Sflorian 
22065185a700Sflorian 	result = dns_message_gettemprdata(msg, &rdata);
22075185a700Sflorian 	if (result != ISC_R_SUCCESS)
22085185a700Sflorian 		goto cleanup;
22095185a700Sflorian 
22105185a700Sflorian 	result = dns_message_gettemprdatalist(msg, &list);
22115185a700Sflorian 	if (result != ISC_R_SUCCESS)
22125185a700Sflorian 		goto cleanup;
22135185a700Sflorian 	result = dns_message_gettemprdataset(msg, &set);
22145185a700Sflorian 	if (result != ISC_R_SUCCESS)
22155185a700Sflorian 		goto cleanup;
22165185a700Sflorian 
22175185a700Sflorian 	isc_buffer_usedregion(querytsig, &r);
22185185a700Sflorian 	result = isc_buffer_allocate(&buf, r.length);
22195185a700Sflorian 	if (result != ISC_R_SUCCESS)
22205185a700Sflorian 		goto cleanup;
22215185a700Sflorian 	isc_buffer_putmem(buf, r.base, r.length);
22225185a700Sflorian 	isc_buffer_usedregion(buf, &r);
22235185a700Sflorian 	dns_rdata_init(rdata);
22245185a700Sflorian 	dns_rdata_fromregion(rdata, dns_rdataclass_any, dns_rdatatype_tsig, &r);
22255185a700Sflorian 	dns_message_takebuffer(msg, &buf);
22265185a700Sflorian 	ISC_LIST_APPEND(list->rdata, rdata, link);
22275185a700Sflorian 	result = dns_rdatalist_tordataset(list, set);
22285185a700Sflorian 	if (result != ISC_R_SUCCESS)
22295185a700Sflorian 		goto cleanup;
22305185a700Sflorian 
22315185a700Sflorian 	msg->querytsig = set;
22325185a700Sflorian 
22335185a700Sflorian 	return (result);
22345185a700Sflorian 
22355185a700Sflorian  cleanup:
22365185a700Sflorian 	if (rdata != NULL)
22375185a700Sflorian 		dns_message_puttemprdata(msg, &rdata);
22385185a700Sflorian 	if (list != NULL)
22395185a700Sflorian 		dns_message_puttemprdatalist(msg, &list);
22405185a700Sflorian 	if (set != NULL)
22415185a700Sflorian 		dns_message_puttemprdataset(msg, &set);
22425185a700Sflorian 	return (ISC_R_NOMEMORY);
22435185a700Sflorian }
22445185a700Sflorian 
22455185a700Sflorian isc_result_t
22465185a700Sflorian dns_message_getquerytsig(dns_message_t *msg, isc_buffer_t **querytsig) {
22475185a700Sflorian 	isc_result_t result;
22485185a700Sflorian 	dns_rdata_t rdata = DNS_RDATA_INIT;
22495185a700Sflorian 	isc_region_t r;
22505185a700Sflorian 
22515185a700Sflorian 	REQUIRE(querytsig != NULL && *querytsig == NULL);
22525185a700Sflorian 
22535185a700Sflorian 	if (msg->tsig == NULL)
22545185a700Sflorian 		return (ISC_R_SUCCESS);
22555185a700Sflorian 
22565185a700Sflorian 	result = dns_rdataset_first(msg->tsig);
22575185a700Sflorian 	if (result != ISC_R_SUCCESS)
22585185a700Sflorian 		return (result);
22595185a700Sflorian 	dns_rdataset_current(msg->tsig, &rdata);
22605185a700Sflorian 	dns_rdata_toregion(&rdata, &r);
22615185a700Sflorian 
22625185a700Sflorian 	result = isc_buffer_allocate(querytsig, r.length);
22635185a700Sflorian 	if (result != ISC_R_SUCCESS)
22645185a700Sflorian 		return (result);
22655185a700Sflorian 	isc_buffer_putmem(*querytsig, r.base, r.length);
22665185a700Sflorian 	return (ISC_R_SUCCESS);
22675185a700Sflorian }
22685185a700Sflorian 
22695185a700Sflorian dns_rdataset_t *
22705185a700Sflorian dns_message_getsig0(dns_message_t *msg, dns_name_t **owner) {
22715185a700Sflorian 
22725185a700Sflorian 	/*
22735185a700Sflorian 	 * Get the SIG(0) record for 'msg'.
22745185a700Sflorian 	 */
22755185a700Sflorian 
22765185a700Sflorian 	REQUIRE(owner == NULL || *owner == NULL);
22775185a700Sflorian 
22785185a700Sflorian 	if (msg->sig0 != NULL && owner != NULL) {
22795185a700Sflorian 		/* If dns_message_getsig0 is called on a rendered message
22805185a700Sflorian 		 * after the SIG(0) has been applied, we need to return the
22815185a700Sflorian 		 * root name, not NULL.
22825185a700Sflorian 		 */
22835185a700Sflorian 		if (msg->sig0name == NULL)
22845185a700Sflorian 			*owner = dns_rootname;
22855185a700Sflorian 		else
22865185a700Sflorian 			*owner = msg->sig0name;
22875185a700Sflorian 	}
22885185a700Sflorian 	return (msg->sig0);
22895185a700Sflorian }
22905185a700Sflorian 
22915185a700Sflorian void
22925185a700Sflorian dns_message_takebuffer(dns_message_t *msg, isc_buffer_t **buffer) {
22935185a700Sflorian 	REQUIRE(buffer != NULL);
22945185a700Sflorian 
22955185a700Sflorian 	ISC_LIST_APPEND(msg->cleanup, *buffer, link);
22965185a700Sflorian 	*buffer = NULL;
22975185a700Sflorian }
22985185a700Sflorian 
22995185a700Sflorian isc_result_t
23005185a700Sflorian dns_message_sectiontotext(dns_message_t *msg, dns_section_t section,
23015185a700Sflorian 			  const dns_master_style_t *style,
23025185a700Sflorian 			  dns_messagetextflag_t flags,
23035185a700Sflorian 			  isc_buffer_t *target) {
23045185a700Sflorian 	dns_name_t *name, empty_name;
23055185a700Sflorian 	dns_rdataset_t *rdataset;
23065185a700Sflorian 	isc_result_t result;
23071fb015a8Sflorian 	int seensoa = 0;
23085185a700Sflorian 
23095185a700Sflorian 	REQUIRE(target != NULL);
23105185a700Sflorian 	REQUIRE(VALID_SECTION(section));
23115185a700Sflorian 
23125185a700Sflorian 	if (ISC_LIST_EMPTY(msg->sections[section]))
23135185a700Sflorian 		return (ISC_R_SUCCESS);
23145185a700Sflorian 
23155185a700Sflorian 	if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) {
23165185a700Sflorian 		ADD_STRING(target, ";; ");
23175185a700Sflorian 		if (msg->opcode != dns_opcode_update) {
23185185a700Sflorian 			ADD_STRING(target, sectiontext[section]);
23195185a700Sflorian 		} else {
23205185a700Sflorian 			ADD_STRING(target, updsectiontext[section]);
23215185a700Sflorian 		}
23225185a700Sflorian 		ADD_STRING(target, " SECTION:\n");
23235185a700Sflorian 	}
23245185a700Sflorian 
23255185a700Sflorian 	dns_name_init(&empty_name, NULL);
23265185a700Sflorian 	result = dns_message_firstname(msg, section);
23275185a700Sflorian 	if (result != ISC_R_SUCCESS) {
23285185a700Sflorian 		return (result);
23295185a700Sflorian 	}
23305185a700Sflorian 	do {
23315185a700Sflorian 		name = NULL;
23325185a700Sflorian 		dns_message_currentname(msg, section, &name);
23335185a700Sflorian 		for (rdataset = ISC_LIST_HEAD(name->list);
23345185a700Sflorian 		     rdataset != NULL;
23355185a700Sflorian 		     rdataset = ISC_LIST_NEXT(rdataset, link)) {
23365185a700Sflorian 			if (section == DNS_SECTION_ANSWER &&
23375185a700Sflorian 			    rdataset->type == dns_rdatatype_soa) {
23385185a700Sflorian 				if ((flags & DNS_MESSAGETEXTFLAG_OMITSOA) != 0)
23395185a700Sflorian 					continue;
23405185a700Sflorian 				if (seensoa &&
23415185a700Sflorian 				    (flags & DNS_MESSAGETEXTFLAG_ONESOA) != 0)
23425185a700Sflorian 					continue;
23431fb015a8Sflorian 				seensoa = 1;
23445185a700Sflorian 			}
23455185a700Sflorian 			if (section == DNS_SECTION_QUESTION) {
23465185a700Sflorian 				ADD_STRING(target, ";");
23475185a700Sflorian 				result = dns_master_questiontotext(name,
23485185a700Sflorian 								   rdataset,
23495185a700Sflorian 								   style,
23505185a700Sflorian 								   target);
23515185a700Sflorian 			} else {
23525185a700Sflorian 				result = dns_master_rdatasettotext(name,
23535185a700Sflorian 								   rdataset,
23545185a700Sflorian 								   style,
23555185a700Sflorian 								   target);
23565185a700Sflorian 			}
23575185a700Sflorian 			if (result != ISC_R_SUCCESS)
23585185a700Sflorian 				return (result);
23595185a700Sflorian 		}
23605185a700Sflorian 		result = dns_message_nextname(msg, section);
23615185a700Sflorian 	} while (result == ISC_R_SUCCESS);
23625185a700Sflorian 	if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0 &&
23635185a700Sflorian 	    (flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
23645185a700Sflorian 		ADD_STRING(target, "\n");
23655185a700Sflorian 	if (result == ISC_R_NOMORE)
23665185a700Sflorian 		result = ISC_R_SUCCESS;
23675185a700Sflorian 	return (result);
23685185a700Sflorian }
23695185a700Sflorian 
23705185a700Sflorian static isc_result_t
23715185a700Sflorian render_ecs(isc_buffer_t *ecsbuf, isc_buffer_t *target) {
23725185a700Sflorian 	int i;
23735185a700Sflorian 	char addr[16], addr_text[64];
23745185a700Sflorian 	uint16_t family;
23755185a700Sflorian 	uint8_t addrlen, addrbytes, scopelen;
23765185a700Sflorian 
23775185a700Sflorian 	/*
23785185a700Sflorian 	 * Note: This routine needs to handle malformed ECS options.
23795185a700Sflorian 	 */
23805185a700Sflorian 
23815185a700Sflorian 	if (isc_buffer_remaininglength(ecsbuf) < 4)
23825185a700Sflorian 		return (DNS_R_OPTERR);
23835185a700Sflorian 	family = isc_buffer_getuint16(ecsbuf);
23845185a700Sflorian 	addrlen = isc_buffer_getuint8(ecsbuf);
23855185a700Sflorian 	scopelen = isc_buffer_getuint8(ecsbuf);
23865185a700Sflorian 
23875185a700Sflorian 	addrbytes = (addrlen + 7) / 8;
23885185a700Sflorian 	if (isc_buffer_remaininglength(ecsbuf) < addrbytes)
23895185a700Sflorian 		return (DNS_R_OPTERR);
23905185a700Sflorian 
23915185a700Sflorian 	if (addrbytes > sizeof(addr))
23925185a700Sflorian 		return (DNS_R_OPTERR);
23935185a700Sflorian 
23945185a700Sflorian 	memset(addr, 0, sizeof(addr));
23955185a700Sflorian 	for (i = 0; i < addrbytes; i ++)
23965185a700Sflorian 		addr[i] = isc_buffer_getuint8(ecsbuf);
23975185a700Sflorian 
23985185a700Sflorian 	switch (family) {
23995185a700Sflorian 	case 0:
24005185a700Sflorian 		if (addrlen != 0U || scopelen != 0U)
24015185a700Sflorian 			return (DNS_R_OPTERR);
24025185a700Sflorian 		strlcpy(addr_text, "0", sizeof(addr_text));
24035185a700Sflorian 		break;
24045185a700Sflorian 	case 1:
24055185a700Sflorian 		if (addrlen > 32 || scopelen > 32)
24065185a700Sflorian 			return (DNS_R_OPTERR);
24075185a700Sflorian 		inet_ntop(AF_INET, addr, addr_text, sizeof(addr_text));
24085185a700Sflorian 		break;
24095185a700Sflorian 	case 2:
24105185a700Sflorian 		if (addrlen > 128 || scopelen > 128)
24115185a700Sflorian 			return (DNS_R_OPTERR);
24125185a700Sflorian 		inet_ntop(AF_INET6, addr, addr_text, sizeof(addr_text));
24135185a700Sflorian 		break;
24145185a700Sflorian 	default:
24155185a700Sflorian 		return (DNS_R_OPTERR);
24165185a700Sflorian 	}
24175185a700Sflorian 
24185185a700Sflorian 	ADD_STRING(target, ": ");
24195185a700Sflorian 	ADD_STRING(target, addr_text);
24205185a700Sflorian 	snprintf(addr_text, sizeof(addr_text), "/%d/%d", addrlen, scopelen);
24215185a700Sflorian 	ADD_STRING(target, addr_text);
24225185a700Sflorian 	return (ISC_R_SUCCESS);
24235185a700Sflorian }
24245185a700Sflorian 
2425fabbbca0Sflorian static const char *
2426fabbbca0Sflorian ede_info_code2str(uint16_t info_code)
2427fabbbca0Sflorian {
2428fabbbca0Sflorian 	if (info_code > 49151)
2429fabbbca0Sflorian 		return "Private Use";
2430fabbbca0Sflorian 
2431fabbbca0Sflorian 	switch (info_code) {
2432fabbbca0Sflorian 	case 0:
2433fabbbca0Sflorian 		return "Other Error";
2434fabbbca0Sflorian 	case 1:
2435fabbbca0Sflorian 		return "Unsupported DNSKEY Algorithm";
2436fabbbca0Sflorian 	case 2:
2437fabbbca0Sflorian 		return "Unsupported DS Digest Type";
2438fabbbca0Sflorian 	case 3:
2439fabbbca0Sflorian 		return "Stale Answer";
2440fabbbca0Sflorian 	case 4:
2441fabbbca0Sflorian 		return "Forged Answer";
2442fabbbca0Sflorian 	case 5:
2443fabbbca0Sflorian 		return "DNSSEC Indeterminate";
2444fabbbca0Sflorian 	case 6:
2445fabbbca0Sflorian 		return "DNSSEC Bogus";
2446fabbbca0Sflorian 	case 7:
2447fabbbca0Sflorian 		return "Signature Expired";
2448fabbbca0Sflorian 	case 8:
2449fabbbca0Sflorian 		return "Signature Not Yet Valid";
2450fabbbca0Sflorian 	case 9:
2451fabbbca0Sflorian 		return "DNSKEY Missing";
2452fabbbca0Sflorian 	case 10:
2453fabbbca0Sflorian 		return "RRSIGs Missing";
2454fabbbca0Sflorian 	case 11:
2455fabbbca0Sflorian 		return "No Zone Key Bit Set";
2456fabbbca0Sflorian 	case 12:
2457fabbbca0Sflorian 		return "NSEC Missing";
2458fabbbca0Sflorian 	case 13:
2459fabbbca0Sflorian 		return "Cached Error";
2460fabbbca0Sflorian 	case 14:
2461fabbbca0Sflorian 		return "Not Ready";
2462fabbbca0Sflorian 	case 15:
2463fabbbca0Sflorian 		return "Blocked";
2464fabbbca0Sflorian 	case 16:
2465fabbbca0Sflorian 		return "Censored";
2466fabbbca0Sflorian 	case 17:
2467fabbbca0Sflorian 		return "Filtered";
2468fabbbca0Sflorian 	case 18:
2469fabbbca0Sflorian 		return "Prohibited";
2470fabbbca0Sflorian 	case 19:
2471fabbbca0Sflorian 		return "Stale NXDomain Answer";
2472fabbbca0Sflorian 	case 20:
2473fabbbca0Sflorian 		return "Not Authoritative";
2474fabbbca0Sflorian 	case 21:
2475fabbbca0Sflorian 		return "Not Supported";
2476fabbbca0Sflorian 	case 22:
2477fabbbca0Sflorian 		return "No Reachable Authority";
2478fabbbca0Sflorian 	case 23:
2479fabbbca0Sflorian 		return "Network Error";
2480fabbbca0Sflorian 	case 24:
2481fabbbca0Sflorian 		return "Invalid Data";
2482fabbbca0Sflorian 	default:
2483fabbbca0Sflorian 		return "Unassigned";
2484fabbbca0Sflorian 	}
2485fabbbca0Sflorian }
2486fabbbca0Sflorian 
2487*9835a5e1Sflorian static const char *
2488*9835a5e1Sflorian zoneversion_zone(const char *zone, int labelcount)
2489*9835a5e1Sflorian {
2490*9835a5e1Sflorian 	size_t pos;
2491*9835a5e1Sflorian 
2492*9835a5e1Sflorian 	if (zone == NULL || labelcount == 0)
2493*9835a5e1Sflorian 		return ".";
2494*9835a5e1Sflorian 
2495*9835a5e1Sflorian 	pos = strlen(zone);
2496*9835a5e1Sflorian 	if (pos == 0)
2497*9835a5e1Sflorian 		return ".";
2498*9835a5e1Sflorian 
2499*9835a5e1Sflorian 	pos--; /* go to last char in string */
2500*9835a5e1Sflorian 	if (zone[pos] == '.')
2501*9835a5e1Sflorian 		pos--; /* the labelcount does not count the empty root label */
2502*9835a5e1Sflorian 
2503*9835a5e1Sflorian 	for (; pos > 0; pos--) {
2504*9835a5e1Sflorian 		if (zone[pos] == '.') {
2505*9835a5e1Sflorian 			labelcount--;
2506*9835a5e1Sflorian 
2507*9835a5e1Sflorian 			if (labelcount == 0) {
2508*9835a5e1Sflorian 				pos++;
2509*9835a5e1Sflorian 				break;
2510*9835a5e1Sflorian 			}
2511*9835a5e1Sflorian 		}
2512*9835a5e1Sflorian 	}
2513*9835a5e1Sflorian 
2514*9835a5e1Sflorian 	return (zone + pos);
2515*9835a5e1Sflorian }
2516*9835a5e1Sflorian 
25175185a700Sflorian isc_result_t
25185185a700Sflorian dns_message_pseudosectiontotext(dns_message_t *msg,
25195185a700Sflorian 				dns_pseudosection_t section,
25205185a700Sflorian 				const dns_master_style_t *style,
25215185a700Sflorian 				dns_messagetextflag_t flags,
2522*9835a5e1Sflorian 				const char *textname,
25235185a700Sflorian 				isc_buffer_t *target)
25245185a700Sflorian {
25255185a700Sflorian 	dns_rdataset_t *ps = NULL;
25265185a700Sflorian 	dns_name_t *name = NULL;
25275185a700Sflorian 	isc_result_t result;
25285185a700Sflorian 	char buf[sizeof("1234567890")];
25295185a700Sflorian 	uint32_t mbz;
25305185a700Sflorian 	dns_rdata_t rdata;
25315185a700Sflorian 	isc_buffer_t optbuf;
25325185a700Sflorian 	uint16_t optcode, optlen;
25335185a700Sflorian 	unsigned char *optdata;
25345185a700Sflorian 
25355185a700Sflorian 	REQUIRE(target != NULL);
25365185a700Sflorian 	REQUIRE(VALID_PSEUDOSECTION(section));
25375185a700Sflorian 
25385185a700Sflorian 	switch (section) {
25395185a700Sflorian 	case DNS_PSEUDOSECTION_OPT:
25405185a700Sflorian 		ps = dns_message_getopt(msg);
25415185a700Sflorian 		if (ps == NULL)
25425185a700Sflorian 			return (ISC_R_SUCCESS);
25435185a700Sflorian 		if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
25445185a700Sflorian 			ADD_STRING(target, ";; OPT PSEUDOSECTION:\n");
25455185a700Sflorian 		ADD_STRING(target, "; EDNS: version: ");
25465185a700Sflorian 		snprintf(buf, sizeof(buf), "%u",
25475185a700Sflorian 			 (unsigned int)((ps->ttl & 0x00ff0000) >> 16));
25485185a700Sflorian 		ADD_STRING(target, buf);
25495185a700Sflorian 		ADD_STRING(target, ", flags:");
25505185a700Sflorian 		if ((ps->ttl & DNS_MESSAGEEXTFLAG_DO) != 0)
25515185a700Sflorian 			ADD_STRING(target, " do");
25525185a700Sflorian 		mbz = ps->ttl & 0xffff;
25535185a700Sflorian 		mbz &= ~DNS_MESSAGEEXTFLAG_DO;		/* Known Flags. */
25545185a700Sflorian 		if (mbz != 0) {
25555185a700Sflorian 			ADD_STRING(target, "; MBZ: ");
25565185a700Sflorian 			snprintf(buf, sizeof(buf), "0x%.4x", mbz);
25575185a700Sflorian 			ADD_STRING(target, buf);
25585185a700Sflorian 			ADD_STRING(target, ", udp: ");
25595185a700Sflorian 		} else
25605185a700Sflorian 			ADD_STRING(target, "; udp: ");
25615185a700Sflorian 		snprintf(buf, sizeof(buf), "%u\n", (unsigned int)ps->rdclass);
25625185a700Sflorian 		ADD_STRING(target, buf);
25635185a700Sflorian 
25645185a700Sflorian 		result = dns_rdataset_first(ps);
25655185a700Sflorian 		if (result != ISC_R_SUCCESS)
25665185a700Sflorian 			return (ISC_R_SUCCESS);
25675185a700Sflorian 
25685185a700Sflorian 		/*
25695185a700Sflorian 		 * Print EDNS info, if any.
25705185a700Sflorian 		 *
25715185a700Sflorian 		 * WARNING: The option contents may be malformed as
25725185a700Sflorian 		 * dig +ednsopt=value:<content> does not validity
25735185a700Sflorian 		 * checking.
25745185a700Sflorian 		 */
25755185a700Sflorian 		dns_rdata_init(&rdata);
25765185a700Sflorian 		dns_rdataset_current(ps, &rdata);
25775185a700Sflorian 
25785185a700Sflorian 		isc_buffer_init(&optbuf, rdata.data, rdata.length);
25795185a700Sflorian 		isc_buffer_add(&optbuf, rdata.length);
25805185a700Sflorian 		while (isc_buffer_remaininglength(&optbuf) != 0) {
25815185a700Sflorian 			INSIST(isc_buffer_remaininglength(&optbuf) >= 4U);
25825185a700Sflorian 			optcode = isc_buffer_getuint16(&optbuf);
25835185a700Sflorian 			optlen = isc_buffer_getuint16(&optbuf);
25845185a700Sflorian 			INSIST(isc_buffer_remaininglength(&optbuf) >= optlen);
25855185a700Sflorian 
25865185a700Sflorian 			if (optcode == DNS_OPT_NSID) {
25875185a700Sflorian 				ADD_STRING(target, "; NSID");
25885185a700Sflorian 			} else if (optcode == DNS_OPT_COOKIE) {
25895185a700Sflorian 				ADD_STRING(target, "; COOKIE");
25905185a700Sflorian 			} else if (optcode == DNS_OPT_CLIENT_SUBNET) {
25915185a700Sflorian 				isc_buffer_t ecsbuf;
25925185a700Sflorian 
25935185a700Sflorian 				ADD_STRING(target, "; CLIENT-SUBNET");
25945185a700Sflorian 				isc_buffer_init(&ecsbuf,
25955185a700Sflorian 						isc_buffer_current(&optbuf),
25965185a700Sflorian 						optlen);
25975185a700Sflorian 				isc_buffer_add(&ecsbuf, optlen);
25985185a700Sflorian 				result = render_ecs(&ecsbuf, target);
25995185a700Sflorian 				if (result == ISC_R_NOSPACE)
26005185a700Sflorian 					return (result);
26015185a700Sflorian 				if (result == ISC_R_SUCCESS) {
26025185a700Sflorian 					isc_buffer_forward(&optbuf, optlen);
26035185a700Sflorian 					ADD_STRING(target, "\n");
26045185a700Sflorian 					continue;
26055185a700Sflorian 				}
26065185a700Sflorian 			} else if (optcode == DNS_OPT_EXPIRE) {
26075185a700Sflorian 				if (optlen == 4) {
26085185a700Sflorian 					uint32_t secs;
26095185a700Sflorian 					secs = isc_buffer_getuint32(&optbuf);
26105185a700Sflorian 					ADD_STRING(target, "; EXPIRE: ");
26115185a700Sflorian 					snprintf(buf, sizeof(buf), "%u", secs);
26125185a700Sflorian 					ADD_STRING(target, buf);
26135185a700Sflorian 					ADD_STRING(target, " (");
26145185a700Sflorian 					result = dns_ttl_totext(secs,
26151fb015a8Sflorian 								1,
26165185a700Sflorian 								target);
26175185a700Sflorian 					if (result != ISC_R_SUCCESS)
26185185a700Sflorian 						return (result);
26195185a700Sflorian 					ADD_STRING(target, ")\n");
26205185a700Sflorian 					continue;
26215185a700Sflorian 				}
26225185a700Sflorian 				ADD_STRING(target, "; EXPIRE");
26235185a700Sflorian 			} else if (optcode == DNS_OPT_PAD) {
26245185a700Sflorian 				ADD_STRING(target, "; PAD");
26255185a700Sflorian 			} else if (optcode == DNS_OPT_KEY_TAG) {
26265185a700Sflorian 				ADD_STRING(target, "; KEY-TAG");
26275185a700Sflorian 				if (optlen > 0U && (optlen % 2U) == 0U) {
26285185a700Sflorian 					const char *sep = ": ";
26295185a700Sflorian 					uint16_t id;
26305185a700Sflorian 					while (optlen > 0U) {
26315185a700Sflorian 					    id = isc_buffer_getuint16(&optbuf);
26325185a700Sflorian 					    snprintf(buf, sizeof(buf), "%s%u",
26335185a700Sflorian 						     sep, id);
26345185a700Sflorian 					    ADD_STRING(target, buf);
26355185a700Sflorian 					    sep = ", ";
26365185a700Sflorian 					    optlen -= 2;
26375185a700Sflorian 					}
26385185a700Sflorian 					ADD_STRING(target, "\n");
26395185a700Sflorian 					continue;
26405185a700Sflorian 				}
2641fabbbca0Sflorian 			} else if (optcode == DNS_OPT_EDE) {
2642fabbbca0Sflorian 				uint16_t info_code;
2643fabbbca0Sflorian 				ADD_STRING(target, "; EDE");
2644fabbbca0Sflorian 				if (optlen >= 2) {
2645fabbbca0Sflorian 					info_code =
2646fabbbca0Sflorian 					    isc_buffer_getuint16(&optbuf);
2647fabbbca0Sflorian 					optlen -= 2;
2648fabbbca0Sflorian 					snprintf(buf, sizeof(buf), ": %u (",
2649fabbbca0Sflorian 					    info_code);
2650fabbbca0Sflorian 					ADD_STRING(target, buf);
2651fabbbca0Sflorian 					ADD_STRING(target,
2652fabbbca0Sflorian 					    ede_info_code2str(info_code));
2653fabbbca0Sflorian 					ADD_STRING(target, ")");
2654fabbbca0Sflorian 				}
2655*9835a5e1Sflorian 			} else if (optcode == DNS_OPT_ZONEVERSION) {
2656*9835a5e1Sflorian 				int i;
2657*9835a5e1Sflorian 
2658*9835a5e1Sflorian 				ADD_STRING(target, "; ZONEVERSION: ");
2659*9835a5e1Sflorian 				optdata = isc_buffer_current(&optbuf);
2660*9835a5e1Sflorian 				for (i = 0; i < optlen; i++) {
2661*9835a5e1Sflorian 					snprintf(buf, sizeof(buf), "%02x ",
2662*9835a5e1Sflorian 						 optdata[i]);
2663*9835a5e1Sflorian 					ADD_STRING(target, buf);
2664*9835a5e1Sflorian 				}
2665*9835a5e1Sflorian 
2666*9835a5e1Sflorian 				if (optlen >= 2) {
2667*9835a5e1Sflorian 					uint8_t labelcount, type;
2668*9835a5e1Sflorian 					const char *zone;
2669*9835a5e1Sflorian 
2670*9835a5e1Sflorian 					labelcount =
2671*9835a5e1Sflorian 					    isc_buffer_getuint8(&optbuf);
2672*9835a5e1Sflorian 					optlen -= 1;
2673*9835a5e1Sflorian 					type = isc_buffer_getuint8(&optbuf);
2674*9835a5e1Sflorian 					optlen -= 1;
2675*9835a5e1Sflorian 					zone = zoneversion_zone(textname,
2676*9835a5e1Sflorian 					    labelcount);
2677*9835a5e1Sflorian 
2678*9835a5e1Sflorian 					if (type == 0 && optlen == 4) {
2679*9835a5e1Sflorian 						uint32_t serial;
2680*9835a5e1Sflorian 
2681*9835a5e1Sflorian 						serial = isc_buffer_getuint32(
2682*9835a5e1Sflorian 						    &optbuf);
2683*9835a5e1Sflorian 						optlen -= 4;
2684*9835a5e1Sflorian 						ADD_STRING(target,
2685*9835a5e1Sflorian 						    "(\"SOA-SERIAL: ");
2686*9835a5e1Sflorian 						snprintf(buf, sizeof(buf), "%u",
2687*9835a5e1Sflorian 						    serial);
2688*9835a5e1Sflorian 						ADD_STRING(target, buf);
2689*9835a5e1Sflorian 						ADD_STRING(target, " (");
2690*9835a5e1Sflorian 						ADD_STRING(target, zone);
2691*9835a5e1Sflorian 						ADD_STRING(target, ")");
2692*9835a5e1Sflorian 						ADD_STRING(target, "\")");
2693*9835a5e1Sflorian 					}
2694*9835a5e1Sflorian 				}
26955185a700Sflorian 			} else {
26965185a700Sflorian 				ADD_STRING(target, "; OPT=");
26975185a700Sflorian 				snprintf(buf, sizeof(buf), "%u", optcode);
26985185a700Sflorian 				ADD_STRING(target, buf);
26995185a700Sflorian 			}
27005185a700Sflorian 
27015185a700Sflorian 			if (optlen != 0) {
27025185a700Sflorian 				int i;
27035185a700Sflorian 				ADD_STRING(target, ": ");
27045185a700Sflorian 
27055185a700Sflorian 				optdata = isc_buffer_current(&optbuf);
27065185a700Sflorian 				for (i = 0; i < optlen; i++) {
27075185a700Sflorian 					const char *sep;
27085185a700Sflorian 					switch (optcode) {
27095185a700Sflorian 					case DNS_OPT_COOKIE:
27105185a700Sflorian 						sep = "";
27115185a700Sflorian 						break;
27125185a700Sflorian 					default:
27135185a700Sflorian 						sep = " ";
27145185a700Sflorian 						break;
27155185a700Sflorian 					}
27165185a700Sflorian 					snprintf(buf, sizeof(buf), "%02x%s",
27175185a700Sflorian 						 optdata[i], sep);
27185185a700Sflorian 					ADD_STRING(target, buf);
27195185a700Sflorian 				}
27205185a700Sflorian 
27215185a700Sflorian 				isc_buffer_forward(&optbuf, optlen);
27225185a700Sflorian 
27235185a700Sflorian 				if (optcode == DNS_OPT_COOKIE) {
27245185a700Sflorian 					if (msg->sitok)
27255185a700Sflorian 						ADD_STRING(target, " (good)");
27265185a700Sflorian 					if (msg->sitbad)
27275185a700Sflorian 						ADD_STRING(target, " (bad)");
27285185a700Sflorian 					ADD_STRING(target, "\n");
27295185a700Sflorian 					continue;
27305185a700Sflorian 				}
27315185a700Sflorian 
27325185a700Sflorian 				if (optcode == DNS_OPT_CLIENT_SUBNET) {
27335185a700Sflorian 					ADD_STRING(target, "\n");
27345185a700Sflorian 					continue;
27355185a700Sflorian 				}
27365185a700Sflorian 
27375185a700Sflorian 				/*
27385185a700Sflorian 				 * For non-SIT options, add a printable
27395185a700Sflorian 				 * version
27405185a700Sflorian 				 */
27415185a700Sflorian 				ADD_STRING(target, "(\"");
27425185a700Sflorian 				if (isc_buffer_availablelength(target) < optlen)
27435185a700Sflorian 					return (ISC_R_NOSPACE);
27445185a700Sflorian 				for (i = 0; i < optlen; i++) {
27455185a700Sflorian 					if (isprint(optdata[i]))
27465185a700Sflorian 						isc_buffer_putmem(target,
27475185a700Sflorian 								  &optdata[i],
27485185a700Sflorian 								  1);
27495185a700Sflorian 					else
27505185a700Sflorian 						isc_buffer_putstr(target, ".");
27515185a700Sflorian 				}
27525185a700Sflorian 				ADD_STRING(target, "\")");
27535185a700Sflorian 			}
27545185a700Sflorian 			ADD_STRING(target, "\n");
27555185a700Sflorian 		}
27565185a700Sflorian 		return (ISC_R_SUCCESS);
27575185a700Sflorian 	case DNS_PSEUDOSECTION_TSIG:
27585185a700Sflorian 		ps = dns_message_gettsig(msg, &name);
27595185a700Sflorian 		if (ps == NULL)
27605185a700Sflorian 			return (ISC_R_SUCCESS);
27615185a700Sflorian 		if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
27625185a700Sflorian 			ADD_STRING(target, ";; TSIG PSEUDOSECTION:\n");
27635185a700Sflorian 		result = dns_master_rdatasettotext(name, ps, style, target);
27645185a700Sflorian 		if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0 &&
27655185a700Sflorian 		    (flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
27665185a700Sflorian 			ADD_STRING(target, "\n");
27675185a700Sflorian 		return (result);
27685185a700Sflorian 	case DNS_PSEUDOSECTION_SIG0:
27695185a700Sflorian 		ps = dns_message_getsig0(msg, &name);
27705185a700Sflorian 		if (ps == NULL)
27715185a700Sflorian 			return (ISC_R_SUCCESS);
27725185a700Sflorian 		if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
27735185a700Sflorian 			ADD_STRING(target, ";; SIG0 PSEUDOSECTION:\n");
27745185a700Sflorian 		result = dns_master_rdatasettotext(name, ps, style, target);
27755185a700Sflorian 		if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0 &&
27765185a700Sflorian 		    (flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
27775185a700Sflorian 			ADD_STRING(target, "\n");
27785185a700Sflorian 		return (result);
27795185a700Sflorian 	}
27805185a700Sflorian 	return (ISC_R_UNEXPECTED);
27815185a700Sflorian }
27825185a700Sflorian 
27835185a700Sflorian isc_result_t
27845185a700Sflorian dns_message_buildopt(dns_message_t *message, dns_rdataset_t **rdatasetp,
27855185a700Sflorian 		     unsigned int version, uint16_t udpsize,
27865185a700Sflorian 		     unsigned int flags, dns_ednsopt_t *ednsopts, size_t count)
27875185a700Sflorian {
27885185a700Sflorian 	dns_rdataset_t *rdataset = NULL;
27895185a700Sflorian 	dns_rdatalist_t *rdatalist = NULL;
27905185a700Sflorian 	dns_rdata_t *rdata = NULL;
27915185a700Sflorian 	isc_result_t result;
27925185a700Sflorian 	unsigned int len = 0, i;
27935185a700Sflorian 
27945185a700Sflorian 	REQUIRE(rdatasetp != NULL && *rdatasetp == NULL);
27955185a700Sflorian 
27965185a700Sflorian 	result = dns_message_gettemprdatalist(message, &rdatalist);
27975185a700Sflorian 	if (result != ISC_R_SUCCESS)
27985185a700Sflorian 		return (result);
27995185a700Sflorian 	result = dns_message_gettemprdata(message, &rdata);
28005185a700Sflorian 	if (result != ISC_R_SUCCESS)
28015185a700Sflorian 		goto cleanup;
28025185a700Sflorian 	result = dns_message_gettemprdataset(message, &rdataset);
28035185a700Sflorian 	if (result != ISC_R_SUCCESS)
28045185a700Sflorian 		goto cleanup;
28055185a700Sflorian 
28065185a700Sflorian 	rdatalist->type = dns_rdatatype_opt;
28075185a700Sflorian 
28085185a700Sflorian 	/*
28095185a700Sflorian 	 * Set Maximum UDP buffer size.
28105185a700Sflorian 	 */
28115185a700Sflorian 	rdatalist->rdclass = udpsize;
28125185a700Sflorian 
28135185a700Sflorian 	/*
28145185a700Sflorian 	 * Set EXTENDED-RCODE and Z to 0.
28155185a700Sflorian 	 */
28165185a700Sflorian 	rdatalist->ttl = (version << 16);
28175185a700Sflorian 	rdatalist->ttl |= (flags & 0xffff);
28185185a700Sflorian 
28195185a700Sflorian 	/*
28205185a700Sflorian 	 * Set EDNS options if applicable
28215185a700Sflorian 	 */
28225185a700Sflorian 	if (count != 0U) {
28235185a700Sflorian 		isc_buffer_t *buf = NULL;
28245185a700Sflorian 		for (i = 0; i < count; i++)
28255185a700Sflorian 			len += ednsopts[i].length + 4;
28265185a700Sflorian 
28275185a700Sflorian 		if (len > 0xffffU) {
28285185a700Sflorian 			result = ISC_R_NOSPACE;
28295185a700Sflorian 			goto cleanup;
28305185a700Sflorian 		}
28315185a700Sflorian 
28325185a700Sflorian 		result = isc_buffer_allocate(&buf, len);
28335185a700Sflorian 		if (result != ISC_R_SUCCESS)
28345185a700Sflorian 			goto cleanup;
28355185a700Sflorian 
28365185a700Sflorian 		for (i = 0; i < count; i++)  {
28375185a700Sflorian 			isc_buffer_putuint16(buf, ednsopts[i].code);
28385185a700Sflorian 			isc_buffer_putuint16(buf, ednsopts[i].length);
28395185a700Sflorian 			if (ednsopts[i].length != 0) {
28405185a700Sflorian 				isc_buffer_putmem(buf, ednsopts[i].value,
28415185a700Sflorian 						  ednsopts[i].length);
28425185a700Sflorian 			}
28435185a700Sflorian 		}
28445185a700Sflorian 		rdata->data = isc_buffer_base(buf);
28455185a700Sflorian 		rdata->length = len;
28465185a700Sflorian 		dns_message_takebuffer(message, &buf);
28475185a700Sflorian 	} else {
28485185a700Sflorian 		rdata->data = NULL;
28495185a700Sflorian 		rdata->length = 0;
28505185a700Sflorian 	}
28515185a700Sflorian 
28525185a700Sflorian 	rdata->rdclass = rdatalist->rdclass;
28535185a700Sflorian 	rdata->type = rdatalist->type;
28545185a700Sflorian 	rdata->flags = 0;
28555185a700Sflorian 
28565185a700Sflorian 	ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
28575185a700Sflorian 	result = dns_rdatalist_tordataset(rdatalist, rdataset);
28585185a700Sflorian 	RUNTIME_CHECK(result == ISC_R_SUCCESS);
28595185a700Sflorian 
28605185a700Sflorian 	*rdatasetp = rdataset;
28615185a700Sflorian 	return (ISC_R_SUCCESS);
28625185a700Sflorian 
28635185a700Sflorian  cleanup:
28645185a700Sflorian 	if (rdata != NULL)
28655185a700Sflorian 		dns_message_puttemprdata(message, &rdata);
28665185a700Sflorian 	if (rdataset != NULL)
28675185a700Sflorian 		dns_message_puttemprdataset(message, &rdataset);
28685185a700Sflorian 	if (rdatalist != NULL)
28695185a700Sflorian 		dns_message_puttemprdatalist(message, &rdatalist);
28705185a700Sflorian 	return (result);
28715185a700Sflorian }
2872