Home | History | Annotate | Line # | Download | only in dns
      1  1.21  christos /*	$NetBSD: message.c,v 1.22 2026/01/29 18:37:49 christos Exp $	*/
      2   1.1  christos 
      3   1.1  christos /*
      4   1.1  christos  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5   1.1  christos  *
      6  1.14  christos  * SPDX-License-Identifier: MPL-2.0
      7  1.14  christos  *
      8   1.1  christos  * This Source Code Form is subject to the terms of the Mozilla Public
      9   1.1  christos  * License, v. 2.0. If a copy of the MPL was not distributed with this
     10  1.11  christos  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
     11   1.1  christos  *
     12   1.1  christos  * See the COPYRIGHT file distributed with this work for additional
     13   1.1  christos  * information regarding copyright ownership.
     14   1.1  christos  */
     15   1.1  christos 
     16   1.1  christos /*! \file */
     17   1.1  christos 
     18   1.1  christos /***
     19   1.1  christos  *** Imports
     20   1.1  christos  ***/
     21   1.1  christos 
     22   1.1  christos #include <ctype.h>
     23   1.3  christos #include <inttypes.h>
     24   1.3  christos #include <stdbool.h>
     25   1.1  christos 
     26  1.19  christos #include <isc/async.h>
     27   1.1  christos #include <isc/buffer.h>
     28  1.16  christos #include <isc/hash.h>
     29  1.19  christos #include <isc/hashmap.h>
     30  1.19  christos #include <isc/helper.h>
     31  1.19  christos #include <isc/log.h>
     32   1.1  christos #include <isc/mem.h>
     33  1.17  christos #include <isc/result.h>
     34  1.19  christos #include <isc/string.h>
     35  1.10  christos #include <isc/utf8.h>
     36   1.1  christos #include <isc/util.h>
     37  1.19  christos #include <isc/work.h>
     38   1.1  christos 
     39   1.1  christos #include <dns/dnssec.h>
     40   1.1  christos #include <dns/keyvalues.h>
     41   1.1  christos #include <dns/log.h>
     42   1.1  christos #include <dns/masterdump.h>
     43   1.1  christos #include <dns/message.h>
     44   1.1  christos #include <dns/opcode.h>
     45   1.1  christos #include <dns/rcode.h>
     46   1.1  christos #include <dns/rdata.h>
     47   1.1  christos #include <dns/rdatalist.h>
     48   1.1  christos #include <dns/rdataset.h>
     49   1.1  christos #include <dns/rdatastruct.h>
     50  1.17  christos #include <dns/soa.h>
     51   1.1  christos #include <dns/tsig.h>
     52   1.1  christos #include <dns/ttl.h>
     53   1.1  christos #include <dns/view.h>
     54   1.1  christos 
     55   1.1  christos #ifdef SKAN_MSG_DEBUG
     56   1.1  christos static void
     57   1.1  christos hexdump(const char *msg, const char *msg2, void *base, size_t len) {
     58   1.1  christos 	unsigned char *p;
     59   1.1  christos 	unsigned int cnt;
     60   1.1  christos 
     61   1.1  christos 	p = base;
     62   1.1  christos 	cnt = 0;
     63   1.1  christos 
     64  1.19  christos 	printf("*** %s [%s] (%u bytes @ %p)\n", msg, msg2, (unsigned int)len,
     65  1.19  christos 	       base);
     66   1.1  christos 
     67   1.1  christos 	while (cnt < len) {
     68   1.9  christos 		if (cnt % 16 == 0) {
     69   1.1  christos 			printf("%p: ", p);
     70   1.9  christos 		} else if (cnt % 8 == 0) {
     71   1.1  christos 			printf(" |");
     72   1.9  christos 		}
     73  1.21  christos 		printf(" %02x %c", *p, isprint(*p) ? *p : ' ');
     74   1.1  christos 		p++;
     75   1.1  christos 		cnt++;
     76   1.1  christos 
     77   1.9  christos 		if (cnt % 16 == 0) {
     78   1.1  christos 			printf("\n");
     79   1.9  christos 		}
     80   1.1  christos 	}
     81   1.1  christos 
     82   1.9  christos 	if (cnt % 16 != 0) {
     83   1.1  christos 		printf("\n");
     84   1.9  christos 	}
     85   1.1  christos }
     86   1.9  christos #endif /* ifdef SKAN_MSG_DEBUG */
     87   1.1  christos 
     88   1.9  christos #define DNS_MESSAGE_OPCODE_MASK	      0x7800U
     89   1.9  christos #define DNS_MESSAGE_OPCODE_SHIFT      11
     90   1.9  christos #define DNS_MESSAGE_RCODE_MASK	      0x000fU
     91   1.9  christos #define DNS_MESSAGE_FLAG_MASK	      0x8ff0U
     92   1.9  christos #define DNS_MESSAGE_EDNSRCODE_MASK    0xff000000U
     93   1.9  christos #define DNS_MESSAGE_EDNSRCODE_SHIFT   24
     94   1.9  christos #define DNS_MESSAGE_EDNSVERSION_MASK  0x00ff0000U
     95   1.9  christos #define DNS_MESSAGE_EDNSVERSION_SHIFT 16
     96   1.9  christos 
     97   1.9  christos #define VALID_NAMED_SECTION(s) \
     98   1.9  christos 	(((s) > DNS_SECTION_ANY) && ((s) < DNS_SECTION_MAX))
     99   1.9  christos #define VALID_SECTION(s) (((s) >= DNS_SECTION_ANY) && ((s) < DNS_SECTION_MAX))
    100   1.9  christos #define ADD_STRING(b, s)                                          \
    101   1.9  christos 	{                                                         \
    102   1.9  christos 		if (strlen(s) >= isc_buffer_availablelength(b)) { \
    103   1.9  christos 			result = ISC_R_NOSPACE;                   \
    104   1.9  christos 			goto cleanup;                             \
    105   1.9  christos 		} else                                            \
    106   1.9  christos 			isc_buffer_putstr(b, s);                  \
    107   1.9  christos 	}
    108  1.20  christos #define PUT_YAMLSTR(target, namebuf, len, utfok)                   \
    109  1.20  christos 	{                                                          \
    110  1.20  christos 		result = put_yamlstr(target, namebuf, len, utfok); \
    111  1.20  christos 		if (result != ISC_R_SUCCESS) {                     \
    112  1.20  christos 			goto cleanup;                              \
    113  1.20  christos 		}                                                  \
    114  1.20  christos 	}
    115  1.19  christos #define VALID_NAMED_PSEUDOSECTION(s) \
    116  1.19  christos 	(((s) > DNS_PSEUDOSECTION_ANY) && ((s) < DNS_PSEUDOSECTION_MAX))
    117   1.9  christos #define VALID_PSEUDOSECTION(s) \
    118   1.9  christos 	(((s) >= DNS_PSEUDOSECTION_ANY) && ((s) < DNS_PSEUDOSECTION_MAX))
    119   1.1  christos 
    120   1.1  christos #define OPTOUT(x) (((x)->attributes & DNS_RDATASETATTR_OPTOUT) != 0)
    121   1.1  christos 
    122   1.1  christos /*%
    123   1.1  christos  * This is the size of each individual scratchpad buffer, and the numbers
    124   1.1  christos  * of various block allocations used within the server.
    125   1.1  christos  * XXXMLG These should come from a config setting.
    126   1.1  christos  */
    127  1.14  christos #define SCRATCHPAD_SIZE	   1232
    128  1.19  christos #define NAME_FILLCOUNT	   1024
    129  1.14  christos #define NAME_FREEMAX	   8 * NAME_FILLCOUNT
    130  1.14  christos #define OFFSET_COUNT	   4
    131  1.14  christos #define RDATA_COUNT	   8
    132  1.14  christos #define RDATALIST_COUNT	   8
    133  1.19  christos #define RDATASET_FILLCOUNT 1024
    134  1.14  christos #define RDATASET_FREEMAX   8 * RDATASET_FILLCOUNT
    135   1.1  christos 
    136   1.1  christos /*%
    137   1.1  christos  * Text representation of the different items, for message_totext
    138   1.1  christos  * functions.
    139   1.1  christos  */
    140   1.9  christos static const char *sectiontext[] = { "QUESTION", "ANSWER", "AUTHORITY",
    141   1.9  christos 				     "ADDITIONAL" };
    142   1.9  christos 
    143   1.9  christos static const char *updsectiontext[] = { "ZONE", "PREREQUISITE", "UPDATE",
    144   1.9  christos 					"ADDITIONAL" };
    145   1.9  christos 
    146   1.9  christos static const char *opcodetext[] = { "QUERY",	  "IQUERY",	"STATUS",
    147   1.9  christos 				    "RESERVED3",  "NOTIFY",	"UPDATE",
    148   1.9  christos 				    "RESERVED6",  "RESERVED7",	"RESERVED8",
    149   1.9  christos 				    "RESERVED9",  "RESERVED10", "RESERVED11",
    150   1.9  christos 				    "RESERVED12", "RESERVED13", "RESERVED14",
    151   1.9  christos 				    "RESERVED15" };
    152   1.1  christos 
    153  1.10  christos static const char *edetext[] = { "Other",
    154  1.10  christos 				 "Unsupported DNSKEY Algorithm",
    155  1.10  christos 				 "Unsupported DS Digest Type",
    156  1.10  christos 				 "Stale Answer",
    157  1.10  christos 				 "Forged Answer",
    158  1.10  christos 				 "DNSSEC Indeterminate",
    159  1.10  christos 				 "DNSSEC Bogus",
    160  1.10  christos 				 "Signature Expired",
    161  1.10  christos 				 "Signature Not Yet Valid",
    162  1.10  christos 				 "DNSKEY Missing",
    163  1.10  christos 				 "RRSIGs Missing",
    164  1.10  christos 				 "No Zone Key Bit Set",
    165  1.10  christos 				 "NSEC Missing",
    166  1.10  christos 				 "Cached Error",
    167  1.10  christos 				 "Not Ready",
    168  1.10  christos 				 "Blocked",
    169  1.10  christos 				 "Censored",
    170  1.10  christos 				 "Filtered",
    171  1.10  christos 				 "Prohibited",
    172  1.10  christos 				 "Stale NXDOMAIN Answer",
    173  1.10  christos 				 "Not Authoritative",
    174  1.10  christos 				 "Not Supported",
    175  1.10  christos 				 "No Reachable Authority",
    176  1.10  christos 				 "Network Error",
    177  1.10  christos 				 "Invalid Data" };
    178  1.10  christos 
    179   1.1  christos /*%
    180   1.1  christos  * "helper" type, which consists of a block of some type, and is linkable.
    181   1.1  christos  * For it to work, sizeof(dns_msgblock_t) must be a multiple of the pointer
    182   1.1  christos  * size, or the allocated elements will not be aligned correctly.
    183   1.1  christos  */
    184   1.1  christos struct dns_msgblock {
    185   1.9  christos 	unsigned int count;
    186   1.9  christos 	unsigned int remaining;
    187   1.9  christos 	ISC_LINK(dns_msgblock_t) link;
    188   1.1  christos }; /* dynamically sized */
    189   1.1  christos 
    190  1.14  christos static dns_msgblock_t *
    191   1.1  christos msgblock_allocate(isc_mem_t *, unsigned int, unsigned int);
    192   1.1  christos 
    193   1.1  christos #define msgblock_get(block, type) \
    194   1.1  christos 	((type *)msgblock_internalget(block, sizeof(type)))
    195   1.1  christos 
    196  1.19  christos /*
    197  1.19  christos  * A context type to pass information when checking a message signature
    198  1.19  christos  * asynchronously.
    199  1.19  christos  */
    200  1.19  christos typedef struct checksig_ctx {
    201  1.19  christos 	isc_loop_t *loop;
    202  1.19  christos 	dns_message_t *msg;
    203  1.19  christos 	dns_view_t *view;
    204  1.19  christos 	dns_message_cb_t cb;
    205  1.19  christos 	void *cbarg;
    206  1.19  christos 	isc_result_t result;
    207  1.19  christos } checksig_ctx_t;
    208  1.19  christos 
    209  1.19  christos /*
    210  1.19  christos  * This function differs from public dns_message_puttemprdataset() that it
    211  1.19  christos  * requires the *rdatasetp to be associated, and it will disassociate and
    212  1.19  christos  * put it back to the memory pool.
    213  1.19  christos  */
    214  1.19  christos static void
    215  1.19  christos dns__message_putassociatedrdataset(dns_message_t *msg,
    216  1.19  christos 				   dns_rdataset_t **rdatasetp);
    217  1.19  christos 
    218  1.14  christos static void *
    219   1.1  christos msgblock_internalget(dns_msgblock_t *, unsigned int);
    220   1.1  christos 
    221  1.14  christos static void
    222   1.1  christos msgblock_reset(dns_msgblock_t *);
    223   1.1  christos 
    224  1.14  christos static void
    225   1.1  christos msgblock_free(isc_mem_t *, dns_msgblock_t *, unsigned int);
    226   1.1  christos 
    227   1.1  christos static void
    228   1.1  christos logfmtpacket(dns_message_t *message, const char *description,
    229   1.1  christos 	     const isc_sockaddr_t *address, isc_logcategory_t *category,
    230   1.1  christos 	     isc_logmodule_t *module, const dns_master_style_t *style,
    231   1.1  christos 	     int level, isc_mem_t *mctx);
    232   1.1  christos 
    233   1.1  christos /*
    234   1.1  christos  * Allocate a new dns_msgblock_t, and return a pointer to it.  If no memory
    235   1.1  christos  * is free, return NULL.
    236   1.1  christos  */
    237  1.14  christos static dns_msgblock_t *
    238   1.1  christos msgblock_allocate(isc_mem_t *mctx, unsigned int sizeof_type,
    239   1.9  christos 		  unsigned int count) {
    240   1.1  christos 	dns_msgblock_t *block;
    241   1.1  christos 	unsigned int length;
    242   1.1  christos 
    243   1.1  christos 	length = sizeof(dns_msgblock_t) + (sizeof_type * count);
    244   1.1  christos 
    245   1.1  christos 	block = isc_mem_get(mctx, length);
    246   1.1  christos 
    247   1.1  christos 	block->count = count;
    248   1.1  christos 	block->remaining = count;
    249   1.1  christos 
    250   1.1  christos 	ISC_LINK_INIT(block, link);
    251   1.1  christos 
    252  1.19  christos 	return block;
    253   1.1  christos }
    254   1.1  christos 
    255   1.1  christos /*
    256   1.1  christos  * Return an element from the msgblock.  If no more are available, return
    257   1.1  christos  * NULL.
    258   1.1  christos  */
    259  1.14  christos static void *
    260   1.1  christos msgblock_internalget(dns_msgblock_t *block, unsigned int sizeof_type) {
    261   1.1  christos 	void *ptr;
    262   1.1  christos 
    263   1.9  christos 	if (block == NULL || block->remaining == 0) {
    264  1.19  christos 		return NULL;
    265   1.9  christos 	}
    266   1.1  christos 
    267   1.1  christos 	block->remaining--;
    268   1.1  christos 
    269   1.9  christos 	ptr = (((unsigned char *)block) + sizeof(dns_msgblock_t) +
    270   1.9  christos 	       (sizeof_type * block->remaining));
    271   1.1  christos 
    272  1.19  christos 	return ptr;
    273   1.1  christos }
    274   1.1  christos 
    275  1.14  christos static void
    276   1.1  christos msgblock_reset(dns_msgblock_t *block) {
    277   1.1  christos 	block->remaining = block->count;
    278   1.1  christos }
    279   1.1  christos 
    280   1.1  christos /*
    281   1.1  christos  * Release memory associated with a message block.
    282   1.1  christos  */
    283  1.14  christos static void
    284   1.9  christos msgblock_free(isc_mem_t *mctx, dns_msgblock_t *block,
    285   1.9  christos 	      unsigned int sizeof_type) {
    286   1.1  christos 	unsigned int length;
    287   1.1  christos 
    288   1.1  christos 	length = sizeof(dns_msgblock_t) + (sizeof_type * block->count);
    289   1.1  christos 
    290   1.1  christos 	isc_mem_put(mctx, block, length);
    291   1.1  christos }
    292   1.1  christos 
    293   1.1  christos /*
    294   1.1  christos  * Allocate a new dynamic buffer, and attach it to this message as the
    295   1.1  christos  * "current" buffer.  (which is always the last on the list, for our
    296   1.1  christos  * uses)
    297   1.1  christos  */
    298  1.14  christos static isc_result_t
    299   1.1  christos newbuffer(dns_message_t *msg, unsigned int size) {
    300   1.1  christos 	isc_buffer_t *dynbuf;
    301   1.1  christos 
    302   1.1  christos 	dynbuf = NULL;
    303   1.9  christos 	isc_buffer_allocate(msg->mctx, &dynbuf, size);
    304   1.1  christos 
    305   1.1  christos 	ISC_LIST_APPEND(msg->scratchpad, dynbuf, link);
    306  1.19  christos 	return ISC_R_SUCCESS;
    307   1.1  christos }
    308   1.1  christos 
    309  1.14  christos static isc_buffer_t *
    310   1.1  christos currentbuffer(dns_message_t *msg) {
    311   1.1  christos 	isc_buffer_t *dynbuf;
    312   1.1  christos 
    313   1.1  christos 	dynbuf = ISC_LIST_TAIL(msg->scratchpad);
    314   1.1  christos 	INSIST(dynbuf != NULL);
    315   1.1  christos 
    316  1.19  christos 	return dynbuf;
    317   1.1  christos }
    318   1.1  christos 
    319  1.14  christos static void
    320   1.1  christos releaserdata(dns_message_t *msg, dns_rdata_t *rdata) {
    321   1.1  christos 	ISC_LIST_PREPEND(msg->freerdata, rdata, link);
    322   1.1  christos }
    323   1.1  christos 
    324  1.14  christos static dns_rdata_t *
    325   1.1  christos newrdata(dns_message_t *msg) {
    326   1.1  christos 	dns_msgblock_t *msgblock;
    327   1.1  christos 	dns_rdata_t *rdata;
    328   1.1  christos 
    329   1.1  christos 	rdata = ISC_LIST_HEAD(msg->freerdata);
    330   1.1  christos 	if (rdata != NULL) {
    331   1.1  christos 		ISC_LIST_UNLINK(msg->freerdata, rdata, link);
    332  1.19  christos 		return rdata;
    333   1.1  christos 	}
    334   1.1  christos 
    335   1.1  christos 	msgblock = ISC_LIST_TAIL(msg->rdatas);
    336   1.1  christos 	rdata = msgblock_get(msgblock, dns_rdata_t);
    337   1.1  christos 	if (rdata == NULL) {
    338   1.1  christos 		msgblock = msgblock_allocate(msg->mctx, sizeof(dns_rdata_t),
    339   1.1  christos 					     RDATA_COUNT);
    340   1.1  christos 		ISC_LIST_APPEND(msg->rdatas, msgblock, link);
    341   1.1  christos 
    342   1.1  christos 		rdata = msgblock_get(msgblock, dns_rdata_t);
    343   1.1  christos 	}
    344   1.1  christos 
    345   1.1  christos 	dns_rdata_init(rdata);
    346  1.19  christos 	return rdata;
    347   1.1  christos }
    348   1.1  christos 
    349  1.14  christos static void
    350   1.1  christos releaserdatalist(dns_message_t *msg, dns_rdatalist_t *rdatalist) {
    351   1.1  christos 	ISC_LIST_PREPEND(msg->freerdatalist, rdatalist, link);
    352   1.1  christos }
    353   1.1  christos 
    354  1.14  christos static dns_rdatalist_t *
    355   1.1  christos newrdatalist(dns_message_t *msg) {
    356   1.1  christos 	dns_msgblock_t *msgblock;
    357   1.1  christos 	dns_rdatalist_t *rdatalist;
    358   1.1  christos 
    359   1.1  christos 	rdatalist = ISC_LIST_HEAD(msg->freerdatalist);
    360   1.1  christos 	if (rdatalist != NULL) {
    361   1.1  christos 		ISC_LIST_UNLINK(msg->freerdatalist, rdatalist, link);
    362   1.1  christos 		goto out;
    363   1.1  christos 	}
    364   1.1  christos 
    365   1.1  christos 	msgblock = ISC_LIST_TAIL(msg->rdatalists);
    366   1.1  christos 	rdatalist = msgblock_get(msgblock, dns_rdatalist_t);
    367   1.1  christos 	if (rdatalist == NULL) {
    368   1.9  christos 		msgblock = msgblock_allocate(msg->mctx, sizeof(dns_rdatalist_t),
    369   1.1  christos 					     RDATALIST_COUNT);
    370   1.1  christos 		ISC_LIST_APPEND(msg->rdatalists, msgblock, link);
    371   1.1  christos 
    372   1.1  christos 		rdatalist = msgblock_get(msgblock, dns_rdatalist_t);
    373   1.1  christos 	}
    374   1.9  christos out:
    375  1.17  christos 	dns_rdatalist_init(rdatalist);
    376  1.19  christos 	return rdatalist;
    377   1.1  christos }
    378   1.1  christos 
    379  1.14  christos static dns_offsets_t *
    380   1.1  christos newoffsets(dns_message_t *msg) {
    381   1.1  christos 	dns_msgblock_t *msgblock;
    382   1.1  christos 	dns_offsets_t *offsets;
    383   1.1  christos 
    384   1.1  christos 	msgblock = ISC_LIST_TAIL(msg->offsets);
    385   1.1  christos 	offsets = msgblock_get(msgblock, dns_offsets_t);
    386   1.1  christos 	if (offsets == NULL) {
    387   1.9  christos 		msgblock = msgblock_allocate(msg->mctx, sizeof(dns_offsets_t),
    388   1.1  christos 					     OFFSET_COUNT);
    389   1.1  christos 		ISC_LIST_APPEND(msg->offsets, msgblock, link);
    390   1.1  christos 
    391   1.1  christos 		offsets = msgblock_get(msgblock, dns_offsets_t);
    392   1.1  christos 	}
    393   1.1  christos 
    394  1.19  christos 	return offsets;
    395   1.1  christos }
    396   1.1  christos 
    397  1.14  christos static void
    398   1.1  christos msginitheader(dns_message_t *m) {
    399   1.1  christos 	m->id = 0;
    400   1.1  christos 	m->flags = 0;
    401   1.1  christos 	m->rcode = 0;
    402   1.1  christos 	m->opcode = 0;
    403   1.1  christos 	m->rdclass = 0;
    404   1.1  christos }
    405   1.1  christos 
    406  1.14  christos static void
    407   1.1  christos msginitprivate(dns_message_t *m) {
    408   1.1  christos 	unsigned int i;
    409   1.1  christos 
    410   1.1  christos 	for (i = 0; i < DNS_SECTION_MAX; i++) {
    411   1.1  christos 		m->cursors[i] = NULL;
    412   1.1  christos 		m->counts[i] = 0;
    413   1.1  christos 	}
    414   1.1  christos 	m->opt = NULL;
    415   1.1  christos 	m->sig0 = NULL;
    416   1.1  christos 	m->sig0name = NULL;
    417   1.1  christos 	m->tsig = NULL;
    418   1.1  christos 	m->tsigname = NULL;
    419   1.9  christos 	m->state = DNS_SECTION_ANY; /* indicate nothing parsed or rendered */
    420   1.1  christos 	m->opt_reserved = 0;
    421   1.1  christos 	m->sig_reserved = 0;
    422   1.1  christos 	m->reserved = 0;
    423   1.1  christos 	m->padding = 0;
    424   1.1  christos 	m->padding_off = 0;
    425   1.1  christos 	m->buffer = NULL;
    426   1.1  christos }
    427   1.1  christos 
    428  1.14  christos static void
    429   1.1  christos msginittsig(dns_message_t *m) {
    430   1.1  christos 	m->tsigstatus = dns_rcode_noerror;
    431   1.1  christos 	m->querytsigstatus = dns_rcode_noerror;
    432   1.1  christos 	m->tsigkey = NULL;
    433   1.1  christos 	m->tsigctx = NULL;
    434   1.1  christos 	m->sigstart = -1;
    435   1.1  christos 	m->sig0key = NULL;
    436   1.1  christos 	m->sig0status = dns_rcode_noerror;
    437   1.1  christos 	m->timeadjust = 0;
    438   1.1  christos }
    439   1.1  christos 
    440   1.1  christos /*
    441   1.1  christos  * Init elements to default state.  Used both when allocating a new element
    442   1.1  christos  * and when resetting one.
    443   1.1  christos  */
    444  1.14  christos static void
    445   1.1  christos msginit(dns_message_t *m) {
    446   1.1  christos 	msginitheader(m);
    447   1.1  christos 	msginitprivate(m);
    448   1.1  christos 	msginittsig(m);
    449   1.1  christos 	m->header_ok = 0;
    450   1.1  christos 	m->question_ok = 0;
    451   1.1  christos 	m->tcp_continuation = 0;
    452   1.1  christos 	m->verified_sig = 0;
    453   1.1  christos 	m->verify_attempted = 0;
    454   1.1  christos 	m->order = NULL;
    455   1.1  christos 	m->order_arg.env = NULL;
    456   1.1  christos 	m->order_arg.acl = NULL;
    457   1.1  christos 	m->order_arg.element = NULL;
    458   1.1  christos 	m->query.base = NULL;
    459   1.1  christos 	m->query.length = 0;
    460   1.1  christos 	m->free_query = 0;
    461   1.1  christos 	m->saved.base = NULL;
    462   1.1  christos 	m->saved.length = 0;
    463   1.1  christos 	m->free_saved = 0;
    464   1.1  christos 	m->cc_ok = 0;
    465   1.1  christos 	m->cc_bad = 0;
    466   1.1  christos 	m->tkey = 0;
    467   1.1  christos 	m->rdclass_set = 0;
    468  1.22  christos 	m->has_dname = 0;
    469   1.1  christos 	m->querytsig = NULL;
    470   1.9  christos 	m->indent.string = "\t";
    471   1.9  christos 	m->indent.count = 0;
    472   1.1  christos }
    473   1.1  christos 
    474  1.14  christos static void
    475  1.19  christos msgresetname(dns_message_t *msg, dns_name_t *name) {
    476  1.19  christos 	dns_rdataset_t *rds = NULL, *next_rds = NULL;
    477  1.19  christos 
    478  1.22  christos 	ISC_LIST_FOREACH_SAFE(name->list, rds, link, next_rds) {
    479  1.19  christos 		ISC_LIST_UNLINK(name->list, rds, link);
    480  1.19  christos 
    481  1.19  christos 		dns__message_putassociatedrdataset(msg, &rds);
    482  1.19  christos 	}
    483  1.19  christos }
    484  1.19  christos 
    485  1.19  christos static void
    486   1.1  christos msgresetnames(dns_message_t *msg, unsigned int first_section) {
    487  1.19  christos 	/* Clean up name lists. */
    488  1.19  christos 	for (size_t i = first_section; i < DNS_SECTION_MAX; i++) {
    489  1.19  christos 		dns_name_t *name = NULL, *next_name = NULL;
    490   1.1  christos 
    491  1.22  christos 		ISC_LIST_FOREACH_SAFE(msg->sections[i], name, link, next_name) {
    492   1.1  christos 			ISC_LIST_UNLINK(msg->sections[i], name, link);
    493   1.1  christos 
    494  1.19  christos 			msgresetname(msg, name);
    495  1.19  christos 
    496  1.13  christos 			dns_message_puttempname(msg, &name);
    497   1.1  christos 		}
    498   1.1  christos 	}
    499   1.1  christos }
    500   1.1  christos 
    501   1.1  christos static void
    502   1.9  christos msgresetopt(dns_message_t *msg) {
    503   1.1  christos 	if (msg->opt != NULL) {
    504   1.1  christos 		if (msg->opt_reserved > 0) {
    505   1.1  christos 			dns_message_renderrelease(msg, msg->opt_reserved);
    506   1.1  christos 			msg->opt_reserved = 0;
    507   1.1  christos 		}
    508  1.19  christos 		dns__message_putassociatedrdataset(msg, &msg->opt);
    509   1.1  christos 		msg->opt = NULL;
    510   1.1  christos 		msg->cc_ok = 0;
    511   1.1  christos 		msg->cc_bad = 0;
    512   1.1  christos 	}
    513   1.1  christos }
    514   1.1  christos 
    515   1.1  christos static void
    516   1.3  christos msgresetsigs(dns_message_t *msg, bool replying) {
    517   1.1  christos 	if (msg->sig_reserved > 0) {
    518   1.1  christos 		dns_message_renderrelease(msg, msg->sig_reserved);
    519   1.1  christos 		msg->sig_reserved = 0;
    520   1.1  christos 	}
    521   1.1  christos 	if (msg->tsig != NULL) {
    522   1.1  christos 		INSIST(dns_rdataset_isassociated(msg->tsig));
    523   1.1  christos 		INSIST(msg->namepool != NULL);
    524   1.1  christos 		if (replying) {
    525   1.1  christos 			INSIST(msg->querytsig == NULL);
    526   1.1  christos 			msg->querytsig = msg->tsig;
    527   1.1  christos 		} else {
    528  1.19  christos 			dns__message_putassociatedrdataset(msg, &msg->tsig);
    529   1.1  christos 			if (msg->querytsig != NULL) {
    530  1.19  christos 				dns__message_putassociatedrdataset(
    531  1.19  christos 					msg, &msg->querytsig);
    532   1.1  christos 			}
    533   1.1  christos 		}
    534  1.13  christos 		dns_message_puttempname(msg, &msg->tsigname);
    535   1.1  christos 		msg->tsig = NULL;
    536   1.1  christos 	} else if (msg->querytsig != NULL && !replying) {
    537  1.19  christos 		dns__message_putassociatedrdataset(msg, &msg->querytsig);
    538   1.1  christos 		msg->querytsig = NULL;
    539   1.1  christos 	}
    540   1.1  christos 	if (msg->sig0 != NULL) {
    541  1.19  christos 		dns__message_putassociatedrdataset(msg, &msg->sig0);
    542   1.1  christos 		msg->sig0 = NULL;
    543  1.17  christos 	}
    544  1.17  christos 	if (msg->sig0name != NULL) {
    545  1.17  christos 		dns_message_puttempname(msg, &msg->sig0name);
    546   1.1  christos 	}
    547   1.1  christos }
    548   1.1  christos 
    549   1.1  christos /*
    550   1.1  christos  * Free all but one (or everything) for this message.  This is used by
    551  1.11  christos  * both dns_message_reset() and dns__message_destroy().
    552   1.1  christos  */
    553   1.1  christos static void
    554   1.3  christos msgreset(dns_message_t *msg, bool everything) {
    555  1.19  christos 	dns_msgblock_t *msgblock = NULL, *next_msgblock = NULL;
    556  1.19  christos 	isc_buffer_t *dynbuf = NULL, *next_dynbuf = NULL;
    557  1.19  christos 	dns_rdata_t *rdata = NULL;
    558  1.19  christos 	dns_rdatalist_t *rdatalist = NULL;
    559   1.1  christos 
    560   1.1  christos 	msgresetnames(msg, 0);
    561   1.1  christos 	msgresetopt(msg);
    562   1.3  christos 	msgresetsigs(msg, false);
    563   1.1  christos 
    564   1.1  christos 	/*
    565   1.1  christos 	 * Clean up linked lists.
    566   1.1  christos 	 */
    567   1.1  christos 
    568   1.1  christos 	/*
    569   1.1  christos 	 * Run through the free lists, and just unlink anything found there.
    570   1.1  christos 	 * The memory isn't lost since these are part of message blocks we
    571   1.1  christos 	 * have allocated.
    572   1.1  christos 	 */
    573   1.1  christos 	rdata = ISC_LIST_HEAD(msg->freerdata);
    574   1.1  christos 	while (rdata != NULL) {
    575   1.1  christos 		ISC_LIST_UNLINK(msg->freerdata, rdata, link);
    576   1.1  christos 		rdata = ISC_LIST_HEAD(msg->freerdata);
    577   1.1  christos 	}
    578   1.1  christos 	rdatalist = ISC_LIST_HEAD(msg->freerdatalist);
    579   1.1  christos 	while (rdatalist != NULL) {
    580   1.1  christos 		ISC_LIST_UNLINK(msg->freerdatalist, rdatalist, link);
    581   1.1  christos 		rdatalist = ISC_LIST_HEAD(msg->freerdatalist);
    582   1.1  christos 	}
    583   1.1  christos 
    584   1.1  christos 	dynbuf = ISC_LIST_HEAD(msg->scratchpad);
    585   1.1  christos 	INSIST(dynbuf != NULL);
    586   1.1  christos 	if (!everything) {
    587   1.1  christos 		isc_buffer_clear(dynbuf);
    588   1.1  christos 		dynbuf = ISC_LIST_NEXT(dynbuf, link);
    589   1.1  christos 	}
    590   1.1  christos 	while (dynbuf != NULL) {
    591   1.1  christos 		next_dynbuf = ISC_LIST_NEXT(dynbuf, link);
    592   1.1  christos 		ISC_LIST_UNLINK(msg->scratchpad, dynbuf, link);
    593   1.1  christos 		isc_buffer_free(&dynbuf);
    594   1.1  christos 		dynbuf = next_dynbuf;
    595   1.1  christos 	}
    596   1.1  christos 
    597   1.1  christos 	msgblock = ISC_LIST_HEAD(msg->rdatas);
    598   1.1  christos 	if (!everything && msgblock != NULL) {
    599   1.1  christos 		msgblock_reset(msgblock);
    600   1.1  christos 		msgblock = ISC_LIST_NEXT(msgblock, link);
    601   1.1  christos 	}
    602   1.1  christos 	while (msgblock != NULL) {
    603   1.1  christos 		next_msgblock = ISC_LIST_NEXT(msgblock, link);
    604   1.1  christos 		ISC_LIST_UNLINK(msg->rdatas, msgblock, link);
    605   1.1  christos 		msgblock_free(msg->mctx, msgblock, sizeof(dns_rdata_t));
    606   1.1  christos 		msgblock = next_msgblock;
    607   1.1  christos 	}
    608   1.1  christos 
    609   1.1  christos 	/*
    610   1.1  christos 	 * rdatalists could be empty.
    611   1.1  christos 	 */
    612   1.1  christos 
    613   1.1  christos 	msgblock = ISC_LIST_HEAD(msg->rdatalists);
    614   1.1  christos 	if (!everything && msgblock != NULL) {
    615   1.1  christos 		msgblock_reset(msgblock);
    616   1.1  christos 		msgblock = ISC_LIST_NEXT(msgblock, link);
    617   1.1  christos 	}
    618   1.1  christos 	while (msgblock != NULL) {
    619   1.1  christos 		next_msgblock = ISC_LIST_NEXT(msgblock, link);
    620   1.1  christos 		ISC_LIST_UNLINK(msg->rdatalists, msgblock, link);
    621   1.1  christos 		msgblock_free(msg->mctx, msgblock, sizeof(dns_rdatalist_t));
    622   1.1  christos 		msgblock = next_msgblock;
    623   1.1  christos 	}
    624   1.1  christos 
    625   1.1  christos 	msgblock = ISC_LIST_HEAD(msg->offsets);
    626   1.1  christos 	if (!everything && msgblock != NULL) {
    627   1.1  christos 		msgblock_reset(msgblock);
    628   1.1  christos 		msgblock = ISC_LIST_NEXT(msgblock, link);
    629   1.1  christos 	}
    630   1.1  christos 	while (msgblock != NULL) {
    631   1.1  christos 		next_msgblock = ISC_LIST_NEXT(msgblock, link);
    632   1.1  christos 		ISC_LIST_UNLINK(msg->offsets, msgblock, link);
    633   1.1  christos 		msgblock_free(msg->mctx, msgblock, sizeof(dns_offsets_t));
    634   1.1  christos 		msgblock = next_msgblock;
    635   1.1  christos 	}
    636   1.1  christos 
    637   1.1  christos 	if (msg->tsigkey != NULL) {
    638   1.1  christos 		dns_tsigkey_detach(&msg->tsigkey);
    639   1.1  christos 		msg->tsigkey = NULL;
    640   1.1  christos 	}
    641   1.1  christos 
    642   1.9  christos 	if (msg->tsigctx != NULL) {
    643   1.1  christos 		dst_context_destroy(&msg->tsigctx);
    644   1.9  christos 	}
    645   1.1  christos 
    646   1.1  christos 	if (msg->query.base != NULL) {
    647   1.9  christos 		if (msg->free_query != 0) {
    648   1.1  christos 			isc_mem_put(msg->mctx, msg->query.base,
    649   1.1  christos 				    msg->query.length);
    650   1.9  christos 		}
    651   1.1  christos 		msg->query.base = NULL;
    652   1.1  christos 		msg->query.length = 0;
    653   1.1  christos 	}
    654   1.1  christos 
    655   1.1  christos 	if (msg->saved.base != NULL) {
    656   1.9  christos 		if (msg->free_saved != 0) {
    657   1.1  christos 			isc_mem_put(msg->mctx, msg->saved.base,
    658   1.1  christos 				    msg->saved.length);
    659   1.9  christos 		}
    660   1.1  christos 		msg->saved.base = NULL;
    661   1.1  christos 		msg->saved.length = 0;
    662   1.1  christos 	}
    663   1.1  christos 
    664   1.1  christos 	/*
    665   1.1  christos 	 * cleanup the buffer cleanup list
    666   1.1  christos 	 */
    667   1.1  christos 	dynbuf = ISC_LIST_HEAD(msg->cleanup);
    668   1.1  christos 	while (dynbuf != NULL) {
    669   1.1  christos 		next_dynbuf = ISC_LIST_NEXT(dynbuf, link);
    670   1.1  christos 		ISC_LIST_UNLINK(msg->cleanup, dynbuf, link);
    671   1.1  christos 		isc_buffer_free(&dynbuf);
    672   1.1  christos 		dynbuf = next_dynbuf;
    673   1.1  christos 	}
    674   1.1  christos 
    675  1.17  christos 	if (msg->order_arg.env != NULL) {
    676  1.17  christos 		dns_aclenv_detach(&msg->order_arg.env);
    677  1.17  christos 	}
    678  1.17  christos 	if (msg->order_arg.acl != NULL) {
    679  1.17  christos 		dns_acl_detach(&msg->order_arg.acl);
    680  1.17  christos 	}
    681  1.17  christos 
    682   1.1  christos 	/*
    683   1.1  christos 	 * Set other bits to normal default values.
    684   1.1  christos 	 */
    685   1.9  christos 	if (!everything) {
    686   1.1  christos 		msginit(msg);
    687   1.9  christos 	}
    688   1.1  christos }
    689   1.1  christos 
    690   1.1  christos static unsigned int
    691   1.1  christos spacefortsig(dns_tsigkey_t *key, int otherlen) {
    692  1.20  christos 	isc_region_t r1 = { 0 }, r2 = { 0 };
    693  1.20  christos 	unsigned int x = 0;
    694   1.1  christos 
    695   1.1  christos 	/*
    696  1.20  christos 	 * The space required for a TSIG record is:
    697   1.1  christos 	 *
    698   1.1  christos 	 *	n1 bytes for the name
    699   1.1  christos 	 *	2 bytes for the type
    700   1.1  christos 	 *	2 bytes for the class
    701   1.1  christos 	 *	4 bytes for the ttl
    702   1.1  christos 	 *	2 bytes for the rdlength
    703   1.1  christos 	 *	n2 bytes for the algorithm name
    704   1.1  christos 	 *	6 bytes for the time signed
    705   1.1  christos 	 *	2 bytes for the fudge
    706   1.1  christos 	 *	2 bytes for the MAC size
    707   1.1  christos 	 *	x bytes for the MAC
    708   1.1  christos 	 *	2 bytes for the original id
    709   1.1  christos 	 *	2 bytes for the error
    710   1.1  christos 	 *	2 bytes for the other data length
    711   1.1  christos 	 *	y bytes for the other data (at most)
    712   1.1  christos 	 * ---------------------------------
    713   1.1  christos 	 *     26 + n1 + n2 + x + y bytes
    714   1.1  christos 	 */
    715   1.1  christos 
    716  1.19  christos 	dns_name_toregion(key->name, &r1);
    717  1.20  christos 	if (key->alg != DST_ALG_UNKNOWN) {
    718  1.20  christos 		dns_name_toregion(dns_tsigkey_algorithm(key), &r2);
    719  1.20  christos 	}
    720  1.20  christos 	if (key->key != NULL) {
    721  1.20  christos 		isc_result_t result = dst_key_sigsize(key->key, &x);
    722   1.9  christos 		if (result != ISC_R_SUCCESS) {
    723   1.1  christos 			x = 0;
    724   1.9  christos 		}
    725   1.1  christos 	}
    726  1.19  christos 	return 26 + r1.length + r2.length + x + otherlen;
    727   1.1  christos }
    728   1.1  christos 
    729  1.11  christos void
    730  1.19  christos dns_message_create(isc_mem_t *mctx, isc_mempool_t *namepool,
    731  1.19  christos 		   isc_mempool_t *rdspool, dns_message_intent_t intent,
    732  1.19  christos 		   dns_message_t **msgp) {
    733   1.1  christos 	REQUIRE(mctx != NULL);
    734   1.1  christos 	REQUIRE(msgp != NULL);
    735   1.1  christos 	REQUIRE(*msgp == NULL);
    736   1.9  christos 	REQUIRE(intent == DNS_MESSAGE_INTENTPARSE ||
    737   1.9  christos 		intent == DNS_MESSAGE_INTENTRENDER);
    738  1.19  christos 	REQUIRE((namepool != NULL && rdspool != NULL) ||
    739  1.19  christos 		(namepool == NULL && rdspool == NULL));
    740   1.1  christos 
    741  1.19  christos 	dns_message_t *msg = isc_mem_get(mctx, sizeof(dns_message_t));
    742  1.19  christos 	*msg = (dns_message_t){
    743  1.19  christos 		.from_to_wire = intent,
    744  1.19  christos 		.references = ISC_REFCOUNT_INITIALIZER(1),
    745  1.19  christos 		.scratchpad = ISC_LIST_INITIALIZER,
    746  1.19  christos 		.cleanup = ISC_LIST_INITIALIZER,
    747  1.19  christos 		.rdatas = ISC_LIST_INITIALIZER,
    748  1.19  christos 		.rdatalists = ISC_LIST_INITIALIZER,
    749  1.19  christos 		.offsets = ISC_LIST_INITIALIZER,
    750  1.19  christos 		.freerdata = ISC_LIST_INITIALIZER,
    751  1.19  christos 		.freerdatalist = ISC_LIST_INITIALIZER,
    752  1.19  christos 		.magic = DNS_MESSAGE_MAGIC,
    753  1.19  christos 		.namepool = namepool,
    754  1.19  christos 		.rdspool = rdspool,
    755  1.19  christos 		.free_pools = (namepool == NULL && rdspool == NULL),
    756  1.19  christos 	};
    757  1.19  christos 
    758  1.19  christos 	isc_mem_attach(mctx, &msg->mctx);
    759   1.1  christos 
    760  1.19  christos 	if (msg->free_pools) {
    761  1.19  christos 		dns_message_createpools(mctx, &msg->namepool, &msg->rdspool);
    762   1.9  christos 	}
    763   1.1  christos 
    764  1.19  christos 	msginit(msg);
    765  1.19  christos 
    766  1.19  christos 	for (size_t i = 0; i < DNS_SECTION_MAX; i++) {
    767  1.19  christos 		ISC_LIST_INIT(msg->sections[i]);
    768  1.19  christos 	}
    769   1.1  christos 
    770  1.19  christos 	isc_buffer_t *dynbuf = NULL;
    771   1.9  christos 	isc_buffer_allocate(mctx, &dynbuf, SCRATCHPAD_SIZE);
    772  1.19  christos 	ISC_LIST_APPEND(msg->scratchpad, dynbuf, link);
    773  1.11  christos 
    774  1.19  christos 	*msgp = msg;
    775   1.1  christos }
    776   1.1  christos 
    777   1.1  christos void
    778  1.19  christos dns_message_reset(dns_message_t *msg, dns_message_intent_t intent) {
    779   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
    780   1.9  christos 	REQUIRE(intent == DNS_MESSAGE_INTENTPARSE ||
    781   1.9  christos 		intent == DNS_MESSAGE_INTENTRENDER);
    782   1.1  christos 
    783   1.3  christos 	msgreset(msg, false);
    784   1.1  christos 	msg->from_to_wire = intent;
    785   1.1  christos }
    786   1.1  christos 
    787  1.11  christos static void
    788  1.11  christos dns__message_destroy(dns_message_t *msg) {
    789  1.11  christos 	REQUIRE(msg != NULL);
    790  1.11  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
    791   1.1  christos 
    792   1.3  christos 	msgreset(msg, true);
    793  1.19  christos 
    794   1.1  christos 	msg->magic = 0;
    795   1.1  christos 
    796  1.19  christos 	if (msg->free_pools) {
    797  1.19  christos 		dns_message_destroypools(&msg->namepool, &msg->rdspool);
    798  1.19  christos 	}
    799  1.11  christos 
    800  1.19  christos 	isc_mem_putanddetach(&msg->mctx, msg, sizeof(dns_message_t));
    801  1.11  christos }
    802  1.11  christos 
    803  1.19  christos #if DNS_MESSAGE_TRACE
    804  1.19  christos ISC_REFCOUNT_TRACE_IMPL(dns_message, dns__message_destroy);
    805  1.19  christos #else
    806  1.19  christos ISC_REFCOUNT_IMPL(dns_message, dns__message_destroy);
    807  1.19  christos #endif
    808  1.11  christos 
    809  1.19  christos static bool
    810  1.19  christos name_match(void *node, const void *key) {
    811  1.19  christos 	return dns_name_equal(node, key);
    812  1.16  christos }
    813  1.16  christos 
    814  1.16  christos static isc_result_t
    815   1.1  christos findname(dns_name_t **foundname, const dns_name_t *target,
    816   1.9  christos 	 dns_namelist_t *section) {
    817  1.19  christos 	dns_name_t *name = NULL;
    818   1.1  christos 
    819  1.22  christos 	ISC_LIST_FOREACH_REV(*section, name, link) {
    820  1.19  christos 		if (dns_name_equal(name, target)) {
    821   1.9  christos 			if (foundname != NULL) {
    822  1.19  christos 				*foundname = name;
    823   1.9  christos 			}
    824  1.19  christos 			return ISC_R_SUCCESS;
    825   1.1  christos 		}
    826   1.1  christos 	}
    827   1.1  christos 
    828  1.19  christos 	return ISC_R_NOTFOUND;
    829  1.19  christos }
    830  1.19  christos 
    831  1.19  christos static uint32_t
    832  1.19  christos rds_hash(dns_rdataset_t *rds) {
    833  1.19  christos 	isc_hash32_t state;
    834  1.19  christos 
    835  1.19  christos 	isc_hash32_init(&state);
    836  1.19  christos 	isc_hash32_hash(&state, &rds->rdclass, sizeof(rds->rdclass), true);
    837  1.19  christos 	isc_hash32_hash(&state, &rds->type, sizeof(rds->type), true);
    838  1.19  christos 	isc_hash32_hash(&state, &rds->covers, sizeof(rds->covers), true);
    839  1.19  christos 
    840  1.19  christos 	return isc_hash32_finalize(&state);
    841   1.1  christos }
    842   1.1  christos 
    843  1.19  christos static bool
    844  1.19  christos rds_match(void *node, const void *key0) {
    845  1.19  christos 	const dns_rdataset_t *rds = node;
    846  1.19  christos 	const dns_rdataset_t *key = key0;
    847   1.1  christos 
    848  1.19  christos 	return rds->rdclass == key->rdclass && rds->type == key->type &&
    849  1.19  christos 	       rds->covers == key->covers;
    850   1.1  christos }
    851   1.1  christos 
    852   1.1  christos isc_result_t
    853   1.1  christos dns_message_findtype(const dns_name_t *name, dns_rdatatype_t type,
    854  1.19  christos 		     dns_rdatatype_t covers, dns_rdataset_t **rdatasetp) {
    855  1.19  christos 	dns_rdataset_t *rds = NULL;
    856   1.1  christos 
    857   1.1  christos 	REQUIRE(name != NULL);
    858  1.19  christos 	REQUIRE(rdatasetp == NULL || *rdatasetp == NULL);
    859  1.19  christos 
    860  1.22  christos 	ISC_LIST_FOREACH_REV(name->list, rds, link) {
    861  1.19  christos 		if (rds->type == type && rds->covers == covers) {
    862  1.19  christos 			SET_IF_NOT_NULL(rdatasetp, rds);
    863   1.1  christos 
    864  1.19  christos 			return ISC_R_SUCCESS;
    865   1.1  christos 		}
    866   1.1  christos 	}
    867   1.1  christos 
    868  1.19  christos 	return ISC_R_NOTFOUND;
    869   1.1  christos }
    870   1.1  christos 
    871   1.1  christos /*
    872   1.1  christos  * Read a name from buffer "source".
    873   1.1  christos  */
    874   1.1  christos static isc_result_t
    875   1.1  christos getname(dns_name_t *name, isc_buffer_t *source, dns_message_t *msg,
    876  1.19  christos 	dns_decompress_t dctx) {
    877   1.1  christos 	isc_buffer_t *scratch;
    878   1.1  christos 	isc_result_t result;
    879   1.1  christos 	unsigned int tries;
    880   1.1  christos 
    881   1.1  christos 	scratch = currentbuffer(msg);
    882   1.1  christos 
    883   1.1  christos 	/*
    884   1.1  christos 	 * First try:  use current buffer.
    885   1.1  christos 	 * Second try:  allocate a new buffer and use that.
    886   1.1  christos 	 */
    887   1.1  christos 	tries = 0;
    888   1.1  christos 	while (tries < 2) {
    889  1.19  christos 		result = dns_name_fromwire(name, source, dctx, scratch);
    890   1.1  christos 
    891   1.1  christos 		if (result == ISC_R_NOSPACE) {
    892   1.1  christos 			tries++;
    893   1.1  christos 
    894   1.1  christos 			result = newbuffer(msg, SCRATCHPAD_SIZE);
    895   1.9  christos 			if (result != ISC_R_SUCCESS) {
    896  1.19  christos 				return result;
    897   1.9  christos 			}
    898   1.1  christos 
    899   1.1  christos 			scratch = currentbuffer(msg);
    900   1.1  christos 			dns_name_reset(name);
    901   1.1  christos 		} else {
    902  1.19  christos 			return result;
    903   1.1  christos 		}
    904   1.1  christos 	}
    905   1.1  christos 
    906  1.14  christos 	UNREACHABLE();
    907   1.1  christos }
    908   1.1  christos 
    909   1.1  christos static isc_result_t
    910  1.19  christos getrdata(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t dctx,
    911   1.1  christos 	 dns_rdataclass_t rdclass, dns_rdatatype_t rdtype,
    912   1.9  christos 	 unsigned int rdatalen, dns_rdata_t *rdata) {
    913   1.1  christos 	isc_buffer_t *scratch;
    914   1.1  christos 	isc_result_t result;
    915   1.1  christos 	unsigned int tries;
    916   1.1  christos 	unsigned int trysize;
    917   1.1  christos 
    918   1.1  christos 	scratch = currentbuffer(msg);
    919   1.1  christos 
    920   1.1  christos 	isc_buffer_setactive(source, rdatalen);
    921   1.1  christos 
    922   1.1  christos 	/*
    923   1.1  christos 	 * First try:  use current buffer.
    924   1.1  christos 	 * Second try:  allocate a new buffer of size
    925   1.1  christos 	 *     max(SCRATCHPAD_SIZE, 2 * compressed_rdatalen)
    926   1.1  christos 	 *     (the data will fit if it was not more than 50% compressed)
    927   1.1  christos 	 * Subsequent tries: double buffer size on each try.
    928   1.1  christos 	 */
    929   1.1  christos 	tries = 0;
    930   1.1  christos 	trysize = 0;
    931   1.1  christos 	/* XXX possibly change this to a while (tries < 2) loop */
    932   1.1  christos 	for (;;) {
    933   1.9  christos 		result = dns_rdata_fromwire(rdata, rdclass, rdtype, source,
    934  1.19  christos 					    dctx, scratch);
    935   1.1  christos 
    936   1.1  christos 		if (result == ISC_R_NOSPACE) {
    937   1.1  christos 			if (tries == 0) {
    938   1.1  christos 				trysize = 2 * rdatalen;
    939   1.9  christos 				if (trysize < SCRATCHPAD_SIZE) {
    940   1.1  christos 					trysize = SCRATCHPAD_SIZE;
    941   1.9  christos 				}
    942   1.1  christos 			} else {
    943   1.1  christos 				INSIST(trysize != 0);
    944   1.9  christos 				if (trysize >= 65535) {
    945  1.19  christos 					return ISC_R_NOSPACE;
    946   1.9  christos 				}
    947   1.9  christos 				/* XXX DNS_R_RRTOOLONG? */
    948   1.1  christos 				trysize *= 2;
    949   1.1  christos 			}
    950   1.1  christos 			tries++;
    951   1.1  christos 			result = newbuffer(msg, trysize);
    952   1.9  christos 			if (result != ISC_R_SUCCESS) {
    953  1.19  christos 				return result;
    954   1.9  christos 			}
    955   1.1  christos 
    956   1.1  christos 			scratch = currentbuffer(msg);
    957   1.1  christos 		} else {
    958  1.19  christos 			return result;
    959   1.1  christos 		}
    960   1.1  christos 	}
    961   1.1  christos }
    962   1.1  christos 
    963   1.9  christos #define DO_ERROR(r)                          \
    964   1.9  christos 	do {                                 \
    965   1.9  christos 		if (best_effort) {           \
    966   1.9  christos 			seen_problem = true; \
    967   1.9  christos 		} else {                     \
    968   1.9  christos 			result = r;          \
    969   1.9  christos 			goto cleanup;        \
    970   1.9  christos 		}                            \
    971  1.12    rillig 	} while (0)
    972   1.1  christos 
    973  1.16  christos static void
    974  1.16  christos cleanup_name_hashmaps(dns_namelist_t *section) {
    975  1.16  christos 	dns_name_t *name = NULL;
    976  1.22  christos 	ISC_LIST_FOREACH(*section, name, link) {
    977  1.19  christos 		if (name->hashmap != NULL) {
    978  1.19  christos 			isc_hashmap_destroy(&name->hashmap);
    979  1.16  christos 		}
    980  1.16  christos 	}
    981  1.16  christos }
    982  1.16  christos 
    983   1.1  christos static isc_result_t
    984  1.19  christos getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t dctx,
    985   1.9  christos 	     unsigned int options) {
    986   1.1  christos 	isc_region_t r;
    987   1.1  christos 	unsigned int count;
    988  1.13  christos 	dns_name_t *name = NULL;
    989  1.19  christos 	dns_name_t *found_name = NULL;
    990  1.13  christos 	dns_rdataset_t *rdataset = NULL;
    991  1.13  christos 	dns_rdatalist_t *rdatalist = NULL;
    992  1.16  christos 	isc_result_t result = ISC_R_SUCCESS;
    993   1.1  christos 	dns_rdatatype_t rdtype;
    994   1.1  christos 	dns_rdataclass_t rdclass;
    995  1.13  christos 	dns_namelist_t *section = &msg->sections[DNS_SECTION_QUESTION];
    996  1.13  christos 	bool best_effort = ((options & DNS_MESSAGEPARSE_BESTEFFORT) != 0);
    997  1.13  christos 	bool seen_problem = false;
    998  1.13  christos 	bool free_name = false;
    999  1.19  christos 	bool free_hashmaps = false;
   1000  1.19  christos 	isc_hashmap_t *name_map = NULL;
   1001  1.16  christos 
   1002  1.16  christos 	if (msg->counts[DNS_SECTION_QUESTION] > 1) {
   1003  1.19  christos 		isc_hashmap_create(msg->mctx, 1, &name_map);
   1004  1.16  christos 	}
   1005   1.1  christos 
   1006   1.1  christos 	for (count = 0; count < msg->counts[DNS_SECTION_QUESTION]; count++) {
   1007  1.13  christos 		name = NULL;
   1008  1.19  christos 		dns_message_gettempname(msg, &name);
   1009  1.13  christos 		name->offsets = (unsigned char *)newoffsets(msg);
   1010   1.3  christos 		free_name = true;
   1011   1.1  christos 
   1012   1.1  christos 		/*
   1013   1.1  christos 		 * Parse the name out of this packet.
   1014   1.1  christos 		 */
   1015   1.1  christos 		isc_buffer_remainingregion(source, &r);
   1016   1.1  christos 		isc_buffer_setactive(source, r.length);
   1017   1.1  christos 		result = getname(name, source, msg, dctx);
   1018   1.9  christos 		if (result != ISC_R_SUCCESS) {
   1019   1.1  christos 			goto cleanup;
   1020   1.9  christos 		}
   1021   1.1  christos 
   1022  1.16  christos 		/* If there is only one QNAME, skip the duplicity checks */
   1023  1.16  christos 		if (name_map == NULL) {
   1024  1.16  christos 			result = ISC_R_SUCCESS;
   1025  1.16  christos 			goto skip_name_check;
   1026  1.16  christos 		}
   1027  1.16  christos 
   1028   1.1  christos 		/*
   1029   1.1  christos 		 * Run through the section, looking to see if this name
   1030   1.1  christos 		 * is already there.  If it is found, put back the allocated
   1031   1.1  christos 		 * name since we no longer need it, and set our name pointer
   1032   1.1  christos 		 * to point to the name we found.
   1033   1.1  christos 		 */
   1034  1.19  christos 		result = isc_hashmap_add(name_map, dns_name_hash(name),
   1035  1.19  christos 					 name_match, name, name,
   1036  1.19  christos 					 (void **)&found_name);
   1037   1.1  christos 
   1038   1.1  christos 		/*
   1039   1.1  christos 		 * If it is the first name in the section, accept it.
   1040   1.1  christos 		 *
   1041   1.1  christos 		 * If it is not, but is not the same as the name already
   1042   1.1  christos 		 * in the question section, append to the section.  Note that
   1043   1.1  christos 		 * here in the question section this is illegal, so return
   1044   1.1  christos 		 * FORMERR.  In the future, check the opcode to see if
   1045   1.1  christos 		 * this should be legal or not.  In either case we no longer
   1046   1.1  christos 		 * need this name pointer.
   1047   1.1  christos 		 */
   1048  1.16  christos 	skip_name_check:
   1049  1.16  christos 		switch (result) {
   1050  1.16  christos 		case ISC_R_SUCCESS:
   1051   1.9  christos 			if (!ISC_LIST_EMPTY(*section)) {
   1052   1.1  christos 				DO_ERROR(DNS_R_FORMERR);
   1053   1.9  christos 			}
   1054   1.1  christos 			ISC_LIST_APPEND(*section, name, link);
   1055  1.16  christos 			break;
   1056  1.16  christos 		case ISC_R_EXISTS:
   1057  1.13  christos 			dns_message_puttempname(msg, &name);
   1058  1.19  christos 			name = found_name;
   1059  1.19  christos 			found_name = NULL;
   1060  1.16  christos 			break;
   1061  1.16  christos 		default:
   1062  1.16  christos 			UNREACHABLE();
   1063   1.1  christos 		}
   1064   1.1  christos 
   1065  1.16  christos 		free_name = false;
   1066  1.16  christos 
   1067   1.1  christos 		/*
   1068   1.1  christos 		 * Get type and class.
   1069   1.1  christos 		 */
   1070   1.1  christos 		isc_buffer_remainingregion(source, &r);
   1071   1.1  christos 		if (r.length < 4) {
   1072   1.1  christos 			result = ISC_R_UNEXPECTEDEND;
   1073   1.1  christos 			goto cleanup;
   1074   1.1  christos 		}
   1075   1.1  christos 		rdtype = isc_buffer_getuint16(source);
   1076   1.1  christos 		rdclass = isc_buffer_getuint16(source);
   1077   1.1  christos 
   1078   1.1  christos 		/*
   1079   1.1  christos 		 * If this class is different than the one we already read,
   1080   1.1  christos 		 * this is an error.
   1081   1.1  christos 		 */
   1082   1.1  christos 		if (msg->rdclass_set == 0) {
   1083   1.1  christos 			msg->rdclass = rdclass;
   1084   1.1  christos 			msg->rdclass_set = 1;
   1085   1.9  christos 		} else if (msg->rdclass != rdclass) {
   1086   1.1  christos 			DO_ERROR(DNS_R_FORMERR);
   1087   1.9  christos 		}
   1088   1.1  christos 
   1089   1.1  christos 		/*
   1090   1.1  christos 		 * Is this a TKEY query?
   1091   1.1  christos 		 */
   1092   1.9  christos 		if (rdtype == dns_rdatatype_tkey) {
   1093   1.1  christos 			msg->tkey = 1;
   1094   1.9  christos 		}
   1095   1.1  christos 
   1096   1.1  christos 		/*
   1097   1.1  christos 		 * Allocate a new rdatalist.
   1098   1.1  christos 		 */
   1099   1.1  christos 		rdatalist = newrdatalist(msg);
   1100  1.19  christos 		rdatalist->type = rdtype;
   1101  1.19  christos 		rdatalist->rdclass = rdclass;
   1102  1.19  christos 		rdatalist->covers = 0;
   1103   1.1  christos 
   1104   1.1  christos 		/*
   1105   1.1  christos 		 * Convert rdatalist to rdataset, and attach the latter to
   1106   1.1  christos 		 * the name.
   1107   1.1  christos 		 */
   1108  1.19  christos 		dns_message_gettemprdataset(msg, &rdataset);
   1109  1.19  christos 		dns_rdatalist_tordataset(rdatalist, rdataset);
   1110  1.16  christos 
   1111  1.16  christos 		rdataset->attributes |= DNS_RDATASETATTR_QUESTION;
   1112  1.16  christos 
   1113  1.16  christos 		/*
   1114  1.16  christos 		 * Skip the duplicity check for first rdataset
   1115  1.16  christos 		 */
   1116  1.16  christos 		if (ISC_LIST_EMPTY(name->list)) {
   1117  1.16  christos 			result = ISC_R_SUCCESS;
   1118  1.16  christos 			goto skip_rds_check;
   1119   1.9  christos 		}
   1120   1.1  christos 
   1121  1.16  christos 		/*
   1122  1.16  christos 		 * Can't ask the same question twice.
   1123  1.16  christos 		 */
   1124  1.19  christos 		if (name->hashmap == NULL) {
   1125  1.19  christos 			isc_hashmap_create(msg->mctx, 1, &name->hashmap);
   1126  1.19  christos 			free_hashmaps = true;
   1127  1.16  christos 
   1128  1.16  christos 			INSIST(ISC_LIST_HEAD(name->list) ==
   1129  1.16  christos 			       ISC_LIST_TAIL(name->list));
   1130  1.16  christos 
   1131  1.16  christos 			dns_rdataset_t *old_rdataset =
   1132  1.16  christos 				ISC_LIST_HEAD(name->list);
   1133  1.16  christos 
   1134  1.19  christos 			result = isc_hashmap_add(
   1135  1.19  christos 				name->hashmap, rds_hash(old_rdataset),
   1136  1.19  christos 				rds_match, old_rdataset, old_rdataset, NULL);
   1137  1.16  christos 
   1138  1.16  christos 			INSIST(result == ISC_R_SUCCESS);
   1139  1.16  christos 		}
   1140  1.19  christos 		result = isc_hashmap_add(name->hashmap, rds_hash(rdataset),
   1141  1.19  christos 					 rds_match, rdataset, rdataset, NULL);
   1142  1.17  christos 		if (result == ISC_R_EXISTS) {
   1143  1.17  christos 			DO_ERROR(DNS_R_FORMERR);
   1144  1.17  christos 		}
   1145  1.17  christos 
   1146  1.16  christos 	skip_rds_check:
   1147   1.1  christos 		ISC_LIST_APPEND(name->list, rdataset, link);
   1148  1.16  christos 
   1149   1.1  christos 		rdataset = NULL;
   1150   1.1  christos 	}
   1151   1.1  christos 
   1152   1.9  christos 	if (seen_problem) {
   1153  1.18  christos 		/* XXX test coverage */
   1154  1.16  christos 		result = DNS_R_RECOVERABLE;
   1155   1.9  christos 	}
   1156   1.1  christos 
   1157   1.9  christos cleanup:
   1158   1.1  christos 	if (rdataset != NULL) {
   1159  1.16  christos 		if (dns_rdataset_isassociated(rdataset)) {
   1160  1.16  christos 			dns_rdataset_disassociate(rdataset);
   1161  1.16  christos 		}
   1162  1.19  christos 		dns_message_puttemprdataset(msg, &rdataset);
   1163   1.1  christos 	}
   1164  1.19  christos 
   1165   1.9  christos 	if (free_name) {
   1166  1.13  christos 		dns_message_puttempname(msg, &name);
   1167   1.9  christos 	}
   1168   1.1  christos 
   1169  1.19  christos 	if (free_hashmaps) {
   1170  1.16  christos 		cleanup_name_hashmaps(section);
   1171  1.16  christos 	}
   1172  1.16  christos 
   1173  1.16  christos 	if (name_map != NULL) {
   1174  1.19  christos 		isc_hashmap_destroy(&name_map);
   1175  1.16  christos 	}
   1176  1.16  christos 
   1177  1.19  christos 	return result;
   1178   1.1  christos }
   1179   1.1  christos 
   1180   1.3  christos static bool
   1181   1.1  christos update(dns_section_t section, dns_rdataclass_t rdclass) {
   1182   1.9  christos 	if (section == DNS_SECTION_PREREQUISITE) {
   1183  1.19  christos 		return rdclass == dns_rdataclass_any ||
   1184  1.19  christos 		       rdclass == dns_rdataclass_none;
   1185   1.9  christos 	}
   1186   1.9  christos 	if (section == DNS_SECTION_UPDATE) {
   1187  1.19  christos 		return rdclass == dns_rdataclass_any;
   1188   1.9  christos 	}
   1189  1.19  christos 	return false;
   1190   1.1  christos }
   1191   1.1  christos 
   1192   1.1  christos static isc_result_t
   1193  1.19  christos getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t dctx,
   1194   1.9  christos 	   dns_section_t sectionid, unsigned int options) {
   1195   1.1  christos 	isc_region_t r;
   1196   1.1  christos 	unsigned int count, rdatalen;
   1197   1.1  christos 	dns_name_t *name = NULL;
   1198  1.19  christos 	dns_name_t *found_name = NULL;
   1199   1.2  christos 	dns_rdataset_t *rdataset = NULL;
   1200  1.16  christos 	dns_rdataset_t *found_rdataset = NULL;
   1201  1.13  christos 	dns_rdatalist_t *rdatalist = NULL;
   1202  1.16  christos 	isc_result_t result = ISC_R_SUCCESS;
   1203   1.1  christos 	dns_rdatatype_t rdtype, covers;
   1204   1.1  christos 	dns_rdataclass_t rdclass;
   1205  1.13  christos 	dns_rdata_t *rdata = NULL;
   1206   1.1  christos 	dns_ttl_t ttl;
   1207  1.13  christos 	dns_namelist_t *section = &msg->sections[sectionid];
   1208  1.16  christos 	bool free_name = false, seen_problem = false;
   1209  1.19  christos 	bool free_hashmaps = false;
   1210  1.13  christos 	bool preserve_order = ((options & DNS_MESSAGEPARSE_PRESERVEORDER) != 0);
   1211  1.13  christos 	bool best_effort = ((options & DNS_MESSAGEPARSE_BESTEFFORT) != 0);
   1212   1.5  christos 	bool isedns, issigzero, istsig;
   1213  1.19  christos 	isc_hashmap_t *name_map = NULL;
   1214  1.16  christos 
   1215  1.16  christos 	if (msg->counts[sectionid] > 1) {
   1216  1.19  christos 		isc_hashmap_create(msg->mctx, 1, &name_map);
   1217  1.16  christos 	}
   1218   1.3  christos 
   1219   1.1  christos 	for (count = 0; count < msg->counts[sectionid]; count++) {
   1220   1.1  christos 		int recstart = source->current;
   1221   1.3  christos 		bool skip_name_search, skip_type_search;
   1222   1.1  christos 
   1223   1.3  christos 		skip_name_search = false;
   1224   1.3  christos 		skip_type_search = false;
   1225   1.5  christos 		isedns = false;
   1226   1.5  christos 		issigzero = false;
   1227   1.5  christos 		istsig = false;
   1228  1.16  christos 		found_rdataset = NULL;
   1229   1.1  christos 
   1230  1.13  christos 		name = NULL;
   1231  1.19  christos 		dns_message_gettempname(msg, &name);
   1232  1.13  christos 		name->offsets = (unsigned char *)newoffsets(msg);
   1233   1.3  christos 		free_name = true;
   1234   1.1  christos 
   1235   1.1  christos 		/*
   1236   1.1  christos 		 * Parse the name out of this packet.
   1237   1.1  christos 		 */
   1238   1.1  christos 		isc_buffer_remainingregion(source, &r);
   1239   1.1  christos 		isc_buffer_setactive(source, r.length);
   1240   1.1  christos 		result = getname(name, source, msg, dctx);
   1241   1.9  christos 		if (result != ISC_R_SUCCESS) {
   1242   1.1  christos 			goto cleanup;
   1243   1.9  christos 		}
   1244   1.1  christos 
   1245   1.1  christos 		/*
   1246   1.1  christos 		 * Get type, class, ttl, and rdatalen.  Verify that at least
   1247   1.1  christos 		 * rdatalen bytes remain.  (Some of this is deferred to
   1248   1.1  christos 		 * later.)
   1249   1.1  christos 		 */
   1250   1.1  christos 		isc_buffer_remainingregion(source, &r);
   1251   1.1  christos 		if (r.length < 2 + 2 + 4 + 2) {
   1252   1.1  christos 			result = ISC_R_UNEXPECTEDEND;
   1253   1.1  christos 			goto cleanup;
   1254   1.1  christos 		}
   1255   1.1  christos 		rdtype = isc_buffer_getuint16(source);
   1256   1.1  christos 		rdclass = isc_buffer_getuint16(source);
   1257   1.1  christos 
   1258   1.1  christos 		/*
   1259   1.1  christos 		 * If there was no question section, we may not yet have
   1260   1.1  christos 		 * established a class.  Do so now.
   1261   1.1  christos 		 */
   1262   1.1  christos 		if (msg->rdclass_set == 0 &&
   1263   1.9  christos 		    rdtype != dns_rdatatype_opt &&  /* class is UDP SIZE */
   1264   1.9  christos 		    rdtype != dns_rdatatype_tsig && /* class is ANY */
   1265  1.16  christos 		    rdtype != dns_rdatatype_tkey)   /* class is undefined */
   1266  1.16  christos 		{
   1267   1.1  christos 			msg->rdclass = rdclass;
   1268   1.1  christos 			msg->rdclass_set = 1;
   1269   1.1  christos 		}
   1270   1.1  christos 
   1271   1.1  christos 		/*
   1272   1.1  christos 		 * If this class is different than the one in the question
   1273   1.1  christos 		 * section, bail.
   1274   1.1  christos 		 */
   1275   1.9  christos 		if (msg->opcode != dns_opcode_update &&
   1276   1.9  christos 		    rdtype != dns_rdatatype_tsig &&
   1277   1.9  christos 		    rdtype != dns_rdatatype_opt &&
   1278   1.9  christos 		    rdtype != dns_rdatatype_key &&  /* in a TKEY query */
   1279   1.9  christos 		    rdtype != dns_rdatatype_sig &&  /* SIG(0) */
   1280   1.9  christos 		    rdtype != dns_rdatatype_tkey && /* Win2000 TKEY */
   1281   1.9  christos 		    msg->rdclass != dns_rdataclass_any &&
   1282   1.9  christos 		    msg->rdclass != rdclass)
   1283   1.9  christos 		{
   1284   1.1  christos 			DO_ERROR(DNS_R_FORMERR);
   1285   1.9  christos 		}
   1286   1.1  christos 
   1287   1.1  christos 		/*
   1288   1.1  christos 		 * If this is not a TKEY query/response then the KEY
   1289   1.1  christos 		 * record's class needs to match.
   1290   1.1  christos 		 */
   1291   1.1  christos 		if (msg->opcode != dns_opcode_update && !msg->tkey &&
   1292   1.1  christos 		    rdtype == dns_rdatatype_key &&
   1293   1.1  christos 		    msg->rdclass != dns_rdataclass_any &&
   1294   1.1  christos 		    msg->rdclass != rdclass)
   1295   1.9  christos 		{
   1296   1.1  christos 			DO_ERROR(DNS_R_FORMERR);
   1297   1.9  christos 		}
   1298   1.1  christos 
   1299   1.1  christos 		/*
   1300   1.1  christos 		 * Special type handling for TSIG, OPT, and TKEY.
   1301   1.1  christos 		 */
   1302   1.1  christos 		if (rdtype == dns_rdatatype_tsig) {
   1303   1.1  christos 			/*
   1304   1.1  christos 			 * If it is a tsig, verify that it is in the
   1305   1.1  christos 			 * additional data section.
   1306   1.1  christos 			 */
   1307   1.1  christos 			if (sectionid != DNS_SECTION_ADDITIONAL ||
   1308   1.1  christos 			    rdclass != dns_rdataclass_any ||
   1309   1.9  christos 			    count != msg->counts[sectionid] - 1)
   1310   1.9  christos 			{
   1311   1.1  christos 				DO_ERROR(DNS_R_BADTSIG);
   1312   1.5  christos 			} else {
   1313   1.5  christos 				skip_name_search = true;
   1314   1.5  christos 				skip_type_search = true;
   1315   1.5  christos 				istsig = true;
   1316   1.5  christos 			}
   1317   1.1  christos 		} else if (rdtype == dns_rdatatype_opt) {
   1318   1.1  christos 			/*
   1319   1.1  christos 			 * The name of an OPT record must be ".", it
   1320   1.1  christos 			 * must be in the additional data section, and
   1321   1.1  christos 			 * it must be the first OPT we've seen.
   1322   1.1  christos 			 */
   1323   1.1  christos 			if (!dns_name_equal(dns_rootname, name) ||
   1324   1.1  christos 			    sectionid != DNS_SECTION_ADDITIONAL ||
   1325   1.9  christos 			    msg->opt != NULL)
   1326   1.9  christos 			{
   1327   1.1  christos 				DO_ERROR(DNS_R_FORMERR);
   1328   1.5  christos 			} else {
   1329   1.5  christos 				skip_name_search = true;
   1330   1.5  christos 				skip_type_search = true;
   1331   1.5  christos 				isedns = true;
   1332   1.5  christos 			}
   1333   1.1  christos 		} else if (rdtype == dns_rdatatype_tkey) {
   1334   1.1  christos 			/*
   1335   1.1  christos 			 * A TKEY must be in the additional section if this
   1336   1.1  christos 			 * is a query, and the answer section if this is a
   1337   1.1  christos 			 * response.  Unless it's a Win2000 client.
   1338   1.1  christos 			 *
   1339   1.1  christos 			 * Its class is ignored.
   1340   1.1  christos 			 */
   1341   1.1  christos 			dns_section_t tkeysection;
   1342   1.1  christos 
   1343   1.9  christos 			if ((msg->flags & DNS_MESSAGEFLAG_QR) == 0) {
   1344   1.1  christos 				tkeysection = DNS_SECTION_ADDITIONAL;
   1345   1.9  christos 			} else {
   1346   1.1  christos 				tkeysection = DNS_SECTION_ANSWER;
   1347   1.9  christos 			}
   1348   1.1  christos 			if (sectionid != tkeysection &&
   1349  1.15  christos 			    sectionid != DNS_SECTION_ANSWER)
   1350  1.15  christos 			{
   1351   1.1  christos 				DO_ERROR(DNS_R_FORMERR);
   1352   1.9  christos 			}
   1353   1.1  christos 		}
   1354   1.1  christos 
   1355   1.1  christos 		/*
   1356   1.1  christos 		 * ... now get ttl and rdatalen, and check buffer.
   1357   1.1  christos 		 */
   1358   1.1  christos 		ttl = isc_buffer_getuint32(source);
   1359   1.1  christos 		rdatalen = isc_buffer_getuint16(source);
   1360   1.1  christos 		r.length -= (2 + 2 + 4 + 2);
   1361   1.1  christos 		if (r.length < rdatalen) {
   1362   1.1  christos 			result = ISC_R_UNEXPECTEDEND;
   1363   1.1  christos 			goto cleanup;
   1364   1.1  christos 		}
   1365   1.1  christos 
   1366   1.1  christos 		/*
   1367   1.1  christos 		 * Read the rdata from the wire format.  Interpret the
   1368   1.1  christos 		 * rdata according to its actual class, even if it had a
   1369   1.1  christos 		 * DynDNS meta-class in the packet (unless this is a TSIG).
   1370   1.1  christos 		 * Then put the meta-class back into the finished rdata.
   1371   1.1  christos 		 */
   1372   1.1  christos 		rdata = newrdata(msg);
   1373   1.1  christos 		if (msg->opcode == dns_opcode_update &&
   1374  1.15  christos 		    update(sectionid, rdclass))
   1375  1.15  christos 		{
   1376   1.1  christos 			if (rdatalen != 0) {
   1377   1.1  christos 				result = DNS_R_FORMERR;
   1378   1.1  christos 				goto cleanup;
   1379   1.1  christos 			}
   1380   1.1  christos 			/*
   1381   1.1  christos 			 * When the rdata is empty, the data pointer is
   1382   1.1  christos 			 * never dereferenced, but it must still be non-NULL.
   1383   1.1  christos 			 * Casting 1 rather than "" avoids warnings about
   1384   1.1  christos 			 * discarding the const attribute of a string,
   1385   1.1  christos 			 * for compilers that would warn about such things.
   1386   1.1  christos 			 */
   1387   1.1  christos 			rdata->data = (unsigned char *)1;
   1388   1.1  christos 			rdata->length = 0;
   1389   1.1  christos 			rdata->rdclass = rdclass;
   1390   1.1  christos 			rdata->type = rdtype;
   1391   1.1  christos 			rdata->flags = DNS_RDATA_UPDATE;
   1392   1.1  christos 			result = ISC_R_SUCCESS;
   1393   1.1  christos 		} else if (rdclass == dns_rdataclass_none &&
   1394   1.1  christos 			   msg->opcode == dns_opcode_update &&
   1395   1.9  christos 			   sectionid == DNS_SECTION_UPDATE)
   1396   1.9  christos 		{
   1397   1.1  christos 			result = getrdata(source, msg, dctx, msg->rdclass,
   1398   1.1  christos 					  rdtype, rdatalen, rdata);
   1399   1.9  christos 		} else {
   1400   1.9  christos 			result = getrdata(source, msg, dctx, rdclass, rdtype,
   1401   1.9  christos 					  rdatalen, rdata);
   1402   1.9  christos 		}
   1403   1.9  christos 		if (result != ISC_R_SUCCESS) {
   1404   1.1  christos 			goto cleanup;
   1405   1.9  christos 		}
   1406   1.1  christos 		rdata->rdclass = rdclass;
   1407   1.9  christos 		if (rdtype == dns_rdatatype_rrsig && rdata->flags == 0) {
   1408   1.1  christos 			covers = dns_rdata_covers(rdata);
   1409   1.9  christos 			if (covers == 0) {
   1410   1.1  christos 				DO_ERROR(DNS_R_FORMERR);
   1411   1.9  christos 			}
   1412   1.1  christos 		} else if (rdtype == dns_rdatatype_sig /* SIG(0) */ &&
   1413  1.15  christos 			   rdata->flags == 0)
   1414  1.15  christos 		{
   1415   1.1  christos 			covers = dns_rdata_covers(rdata);
   1416   1.1  christos 			if (covers == 0) {
   1417   1.1  christos 				if (sectionid != DNS_SECTION_ADDITIONAL ||
   1418  1.17  christos 				    count != msg->counts[sectionid] - 1 ||
   1419  1.17  christos 				    !dns_name_equal(name, dns_rootname))
   1420  1.15  christos 				{
   1421   1.1  christos 					DO_ERROR(DNS_R_BADSIG0);
   1422   1.5  christos 				} else {
   1423   1.5  christos 					skip_name_search = true;
   1424   1.5  christos 					skip_type_search = true;
   1425   1.5  christos 					issigzero = true;
   1426   1.5  christos 				}
   1427   1.1  christos 			} else {
   1428   1.1  christos 				if (msg->rdclass != dns_rdataclass_any &&
   1429  1.15  christos 				    msg->rdclass != rdclass)
   1430  1.15  christos 				{
   1431  1.18  christos 					/* XXX test coverage */
   1432   1.1  christos 					DO_ERROR(DNS_R_FORMERR);
   1433   1.9  christos 				}
   1434   1.1  christos 			}
   1435   1.9  christos 		} else {
   1436   1.1  christos 			covers = 0;
   1437   1.9  christos 		}
   1438   1.1  christos 
   1439   1.1  christos 		/*
   1440   1.1  christos 		 * Check the ownername of NSEC3 records
   1441   1.1  christos 		 */
   1442   1.1  christos 		if (rdtype == dns_rdatatype_nsec3 &&
   1443   1.9  christos 		    !dns_rdata_checkowner(name, msg->rdclass, rdtype, false))
   1444   1.9  christos 		{
   1445   1.1  christos 			result = DNS_R_BADOWNERNAME;
   1446   1.1  christos 			goto cleanup;
   1447   1.1  christos 		}
   1448   1.1  christos 
   1449   1.1  christos 		/*
   1450   1.1  christos 		 * If we are doing a dynamic update or this is a meta-type,
   1451   1.1  christos 		 * don't bother searching for a name, just append this one
   1452   1.1  christos 		 * to the end of the message.
   1453   1.1  christos 		 */
   1454   1.1  christos 		if (preserve_order || msg->opcode == dns_opcode_update ||
   1455  1.15  christos 		    skip_name_search)
   1456  1.15  christos 		{
   1457   1.5  christos 			if (!isedns && !istsig && !issigzero) {
   1458   1.1  christos 				ISC_LIST_APPEND(*section, name, link);
   1459   1.3  christos 				free_name = false;
   1460   1.1  christos 			}
   1461   1.1  christos 		} else {
   1462  1.16  christos 			if (name_map == NULL) {
   1463  1.16  christos 				result = ISC_R_SUCCESS;
   1464  1.16  christos 				goto skip_name_check;
   1465  1.16  christos 			}
   1466  1.16  christos 
   1467   1.1  christos 			/*
   1468   1.1  christos 			 * Run through the section, looking to see if this name
   1469   1.1  christos 			 * is already there.  If it is found, put back the
   1470   1.1  christos 			 * allocated name since we no longer need it, and set
   1471   1.1  christos 			 * our name pointer to point to the name we found.
   1472   1.1  christos 			 */
   1473  1.19  christos 			result = isc_hashmap_add(name_map, dns_name_hash(name),
   1474  1.19  christos 						 name_match, name, name,
   1475  1.19  christos 						 (void **)&found_name);
   1476   1.1  christos 
   1477   1.1  christos 			/*
   1478   1.1  christos 			 * If it is a new name, append to the section.
   1479   1.1  christos 			 */
   1480  1.16  christos 		skip_name_check:
   1481  1.16  christos 			switch (result) {
   1482  1.16  christos 			case ISC_R_SUCCESS:
   1483  1.16  christos 				ISC_LIST_APPEND(*section, name, link);
   1484  1.16  christos 				break;
   1485  1.16  christos 			case ISC_R_EXISTS:
   1486  1.13  christos 				dns_message_puttempname(msg, &name);
   1487  1.19  christos 				name = found_name;
   1488  1.19  christos 				found_name = NULL;
   1489  1.16  christos 				break;
   1490  1.16  christos 			default:
   1491  1.16  christos 				UNREACHABLE();
   1492   1.1  christos 			}
   1493   1.3  christos 			free_name = false;
   1494   1.1  christos 		}
   1495   1.1  christos 
   1496  1.16  christos 		rdatalist = newrdatalist(msg);
   1497  1.16  christos 		rdatalist->type = rdtype;
   1498  1.16  christos 		rdatalist->covers = covers;
   1499  1.16  christos 		rdatalist->rdclass = rdclass;
   1500  1.16  christos 		rdatalist->ttl = ttl;
   1501  1.16  christos 
   1502  1.17  christos 		dns_message_gettemprdataset(msg, &rdataset);
   1503  1.19  christos 		dns_rdatalist_tordataset(rdatalist, rdataset);
   1504  1.16  christos 		dns_rdataset_setownercase(rdataset, name);
   1505  1.16  christos 		rdatalist = NULL;
   1506  1.16  christos 
   1507   1.1  christos 		/*
   1508   1.1  christos 		 * Search name for the particular type and class.
   1509   1.1  christos 		 * Skip this stage if in update mode or this is a meta-type.
   1510   1.1  christos 		 */
   1511  1.16  christos 		if (isedns || istsig || issigzero) {
   1512  1.16  christos 			/* Skip adding the rdataset to the tables */
   1513  1.16  christos 		} else if (preserve_order || msg->opcode == dns_opcode_update ||
   1514  1.16  christos 			   skip_type_search)
   1515  1.15  christos 		{
   1516  1.16  christos 			result = ISC_R_SUCCESS;
   1517  1.16  christos 
   1518  1.16  christos 			ISC_LIST_APPEND(name->list, rdataset, link);
   1519   1.9  christos 		} else {
   1520   1.1  christos 			/*
   1521   1.1  christos 			 * If this is a type that can only occur in
   1522   1.1  christos 			 * the question section, fail.
   1523   1.1  christos 			 */
   1524   1.9  christos 			if (dns_rdatatype_questiononly(rdtype)) {
   1525   1.1  christos 				DO_ERROR(DNS_R_FORMERR);
   1526   1.9  christos 			}
   1527   1.1  christos 
   1528  1.16  christos 			if (ISC_LIST_EMPTY(name->list)) {
   1529  1.16  christos 				result = ISC_R_SUCCESS;
   1530  1.16  christos 				goto skip_rds_check;
   1531  1.16  christos 			}
   1532  1.16  christos 
   1533  1.19  christos 			if (name->hashmap == NULL) {
   1534  1.19  christos 				isc_hashmap_create(msg->mctx, 1,
   1535  1.19  christos 						   &name->hashmap);
   1536  1.19  christos 				free_hashmaps = true;
   1537  1.16  christos 
   1538  1.16  christos 				INSIST(ISC_LIST_HEAD(name->list) ==
   1539  1.16  christos 				       ISC_LIST_TAIL(name->list));
   1540  1.16  christos 
   1541  1.16  christos 				dns_rdataset_t *old_rdataset =
   1542  1.16  christos 					ISC_LIST_HEAD(name->list);
   1543  1.16  christos 
   1544  1.19  christos 				result = isc_hashmap_add(
   1545  1.19  christos 					name->hashmap, rds_hash(old_rdataset),
   1546  1.19  christos 					rds_match, old_rdataset, old_rdataset,
   1547  1.19  christos 					NULL);
   1548  1.16  christos 
   1549  1.16  christos 				INSIST(result == ISC_R_SUCCESS);
   1550  1.16  christos 			}
   1551  1.19  christos 
   1552  1.19  christos 			result = isc_hashmap_add(
   1553  1.19  christos 				name->hashmap, rds_hash(rdataset), rds_match,
   1554  1.19  christos 				rdataset, rdataset, (void **)&found_rdataset);
   1555  1.16  christos 
   1556  1.16  christos 			/*
   1557  1.16  christos 			 * If we found an rdataset that matches, we need to
   1558  1.16  christos 			 * append this rdata to that set.  If we did not, we
   1559  1.16  christos 			 * need to create a new rdatalist, store the important
   1560  1.16  christos 			 * bits there, convert it to an rdataset, and link the
   1561  1.16  christos 			 * latter to the name. Yuck.  When appending, make
   1562  1.16  christos 			 * certain that the type isn't a singleton type, such as
   1563  1.16  christos 			 * SOA or CNAME.
   1564  1.16  christos 			 *
   1565  1.16  christos 			 * Note that this check will be bypassed when preserving
   1566  1.16  christos 			 * order, the opcode is an update, or the type search is
   1567  1.16  christos 			 * skipped.
   1568  1.16  christos 			 */
   1569  1.16  christos 		skip_rds_check:
   1570  1.16  christos 			switch (result) {
   1571  1.16  christos 			case ISC_R_EXISTS:
   1572  1.16  christos 				/* Free the rdataset we used as the key */
   1573  1.19  christos 				dns__message_putassociatedrdataset(msg,
   1574  1.19  christos 								   &rdataset);
   1575  1.16  christos 				result = ISC_R_SUCCESS;
   1576  1.16  christos 				rdataset = found_rdataset;
   1577  1.16  christos 
   1578  1.16  christos 				if (!dns_rdatatype_issingleton(rdtype)) {
   1579  1.16  christos 					break;
   1580  1.16  christos 				}
   1581   1.1  christos 
   1582   1.1  christos 				dns_rdatalist_fromrdataset(rdataset,
   1583   1.1  christos 							   &rdatalist);
   1584  1.16  christos 				dns_rdata_t *first =
   1585  1.16  christos 					ISC_LIST_HEAD(rdatalist->rdata);
   1586   1.1  christos 				INSIST(first != NULL);
   1587   1.9  christos 				if (dns_rdata_compare(rdata, first) != 0) {
   1588   1.1  christos 					DO_ERROR(DNS_R_FORMERR);
   1589   1.9  christos 				}
   1590  1.16  christos 				break;
   1591  1.16  christos 			case ISC_R_SUCCESS:
   1592   1.1  christos 				ISC_LIST_APPEND(name->list, rdataset, link);
   1593  1.16  christos 				break;
   1594  1.16  christos 			default:
   1595  1.16  christos 				UNREACHABLE();
   1596   1.1  christos 			}
   1597   1.1  christos 		}
   1598   1.1  christos 
   1599   1.1  christos 		/*
   1600   1.1  christos 		 * Minimize TTLs.
   1601   1.1  christos 		 *
   1602   1.1  christos 		 * Section 5.2 of RFC2181 says we should drop
   1603   1.1  christos 		 * nonauthoritative rrsets where the TTLs differ, but we
   1604   1.1  christos 		 * currently treat them the as if they were authoritative and
   1605   1.1  christos 		 * minimize them.
   1606   1.1  christos 		 */
   1607   1.1  christos 		if (ttl != rdataset->ttl) {
   1608   1.1  christos 			rdataset->attributes |= DNS_RDATASETATTR_TTLADJUSTED;
   1609   1.9  christos 			if (ttl < rdataset->ttl) {
   1610   1.1  christos 				rdataset->ttl = ttl;
   1611   1.9  christos 			}
   1612   1.1  christos 		}
   1613   1.1  christos 
   1614   1.1  christos 		/* Append this rdata to the rdataset. */
   1615   1.1  christos 		dns_rdatalist_fromrdataset(rdataset, &rdatalist);
   1616   1.1  christos 		ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
   1617   1.1  christos 
   1618   1.1  christos 		/*
   1619   1.1  christos 		 * If this is an OPT, SIG(0) or TSIG record, remember it.
   1620   1.1  christos 		 * Also, set the extended rcode for TSIG.
   1621   1.1  christos 		 *
   1622   1.1  christos 		 * Note msg->opt, msg->sig0 and msg->tsig will only be
   1623   1.1  christos 		 * already set if best-effort parsing is enabled otherwise
   1624   1.1  christos 		 * there will only be at most one of each.
   1625   1.1  christos 		 */
   1626   1.5  christos 		if (isedns) {
   1627   1.1  christos 			dns_rcode_t ercode;
   1628   1.1  christos 
   1629   1.1  christos 			msg->opt = rdataset;
   1630  1.13  christos 			ercode = (dns_rcode_t)((msg->opt->ttl &
   1631  1.13  christos 						DNS_MESSAGE_EDNSRCODE_MASK) >>
   1632  1.13  christos 					       20);
   1633   1.1  christos 			msg->rcode |= ercode;
   1634  1.13  christos 			dns_message_puttempname(msg, &name);
   1635   1.3  christos 			free_name = false;
   1636   1.5  christos 		} else if (issigzero) {
   1637   1.1  christos 			msg->sig0 = rdataset;
   1638   1.1  christos 			msg->sig0name = name;
   1639   1.5  christos 			msg->sigstart = recstart;
   1640   1.3  christos 			free_name = false;
   1641   1.5  christos 		} else if (istsig) {
   1642   1.1  christos 			msg->tsig = rdataset;
   1643   1.1  christos 			msg->tsigname = name;
   1644   1.5  christos 			msg->sigstart = recstart;
   1645   1.5  christos 			/*
   1646   1.5  christos 			 * Windows doesn't like TSIG names to be compressed.
   1647   1.5  christos 			 */
   1648  1.19  christos 			msg->tsigname->attributes.nocompress = true;
   1649   1.3  christos 			free_name = false;
   1650  1.22  christos 		} else if (rdtype == dns_rdatatype_dname &&
   1651  1.22  christos 			   sectionid == DNS_SECTION_ANSWER &&
   1652  1.22  christos 			   msg->opcode == dns_opcode_query)
   1653  1.22  christos 		{
   1654  1.22  christos 			msg->has_dname = 1;
   1655   1.1  christos 		}
   1656  1.16  christos 		rdataset = NULL;
   1657   1.1  christos 
   1658   1.1  christos 		if (seen_problem) {
   1659   1.9  christos 			if (free_name) {
   1660  1.18  christos 				/* XXX test coverage */
   1661  1.13  christos 				dns_message_puttempname(msg, &name);
   1662   1.9  christos 			}
   1663  1.16  christos 			free_name = false;
   1664   1.1  christos 		}
   1665  1.10  christos 		INSIST(!free_name);
   1666   1.1  christos 	}
   1667   1.1  christos 
   1668   1.9  christos 	if (seen_problem) {
   1669  1.16  christos 		result = DNS_R_RECOVERABLE;
   1670   1.9  christos 	}
   1671   1.1  christos 
   1672   1.9  christos cleanup:
   1673  1.16  christos 	if (rdataset != NULL && rdataset != found_rdataset) {
   1674  1.19  christos 		dns__message_putassociatedrdataset(msg, &rdataset);
   1675  1.16  christos 	}
   1676   1.9  christos 	if (free_name) {
   1677  1.13  christos 		dns_message_puttempname(msg, &name);
   1678   1.9  christos 	}
   1679  1.16  christos 
   1680  1.19  christos 	if (free_hashmaps) {
   1681  1.16  christos 		cleanup_name_hashmaps(section);
   1682  1.16  christos 	}
   1683  1.16  christos 
   1684  1.16  christos 	if (name_map != NULL) {
   1685  1.19  christos 		isc_hashmap_destroy(&name_map);
   1686   1.9  christos 	}
   1687   1.1  christos 
   1688  1.19  christos 	return result;
   1689   1.1  christos }
   1690   1.1  christos 
   1691   1.1  christos isc_result_t
   1692   1.1  christos dns_message_parse(dns_message_t *msg, isc_buffer_t *source,
   1693   1.9  christos 		  unsigned int options) {
   1694   1.1  christos 	isc_region_t r;
   1695   1.1  christos 	dns_decompress_t dctx;
   1696   1.1  christos 	isc_result_t ret;
   1697   1.3  christos 	uint16_t tmpflags;
   1698   1.1  christos 	isc_buffer_t origsource;
   1699   1.3  christos 	bool seen_problem;
   1700   1.3  christos 	bool ignore_tc;
   1701   1.1  christos 
   1702   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   1703   1.1  christos 	REQUIRE(source != NULL);
   1704   1.1  christos 	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTPARSE);
   1705   1.1  christos 
   1706   1.3  christos 	seen_problem = false;
   1707   1.3  christos 	ignore_tc = ((options & DNS_MESSAGEPARSE_IGNORETRUNCATION) != 0);
   1708   1.1  christos 
   1709   1.1  christos 	origsource = *source;
   1710   1.1  christos 
   1711   1.1  christos 	msg->header_ok = 0;
   1712   1.1  christos 	msg->question_ok = 0;
   1713   1.1  christos 
   1714  1.11  christos 	if ((options & DNS_MESSAGEPARSE_CLONEBUFFER) == 0) {
   1715  1.11  christos 		isc_buffer_usedregion(&origsource, &msg->saved);
   1716  1.11  christos 	} else {
   1717  1.11  christos 		msg->saved.length = isc_buffer_usedlength(&origsource);
   1718  1.11  christos 		msg->saved.base = isc_mem_get(msg->mctx, msg->saved.length);
   1719  1.11  christos 		memmove(msg->saved.base, isc_buffer_base(&origsource),
   1720  1.11  christos 			msg->saved.length);
   1721  1.11  christos 		msg->free_saved = 1;
   1722  1.11  christos 	}
   1723  1.11  christos 
   1724   1.1  christos 	isc_buffer_remainingregion(source, &r);
   1725   1.9  christos 	if (r.length < DNS_MESSAGE_HEADERLEN) {
   1726  1.19  christos 		return ISC_R_UNEXPECTEDEND;
   1727   1.9  christos 	}
   1728   1.1  christos 
   1729   1.1  christos 	msg->id = isc_buffer_getuint16(source);
   1730   1.1  christos 	tmpflags = isc_buffer_getuint16(source);
   1731   1.9  christos 	msg->opcode = ((tmpflags & DNS_MESSAGE_OPCODE_MASK) >>
   1732   1.9  christos 		       DNS_MESSAGE_OPCODE_SHIFT);
   1733   1.1  christos 	msg->rcode = (dns_rcode_t)(tmpflags & DNS_MESSAGE_RCODE_MASK);
   1734   1.1  christos 	msg->flags = (tmpflags & DNS_MESSAGE_FLAG_MASK);
   1735   1.1  christos 	msg->counts[DNS_SECTION_QUESTION] = isc_buffer_getuint16(source);
   1736   1.1  christos 	msg->counts[DNS_SECTION_ANSWER] = isc_buffer_getuint16(source);
   1737   1.1  christos 	msg->counts[DNS_SECTION_AUTHORITY] = isc_buffer_getuint16(source);
   1738   1.1  christos 	msg->counts[DNS_SECTION_ADDITIONAL] = isc_buffer_getuint16(source);
   1739   1.1  christos 
   1740   1.1  christos 	msg->header_ok = 1;
   1741   1.1  christos 	msg->state = DNS_SECTION_QUESTION;
   1742   1.1  christos 
   1743  1.19  christos 	dctx = DNS_DECOMPRESS_ALWAYS;
   1744   1.1  christos 
   1745  1.19  christos 	ret = getquestions(source, msg, dctx, options);
   1746   1.1  christos 
   1747   1.9  christos 	if (ret == ISC_R_UNEXPECTEDEND && ignore_tc) {
   1748   1.1  christos 		goto truncated;
   1749   1.9  christos 	}
   1750   1.1  christos 	if (ret == DNS_R_RECOVERABLE) {
   1751   1.3  christos 		seen_problem = true;
   1752   1.1  christos 		ret = ISC_R_SUCCESS;
   1753   1.1  christos 	}
   1754   1.9  christos 	if (ret != ISC_R_SUCCESS) {
   1755  1.19  christos 		return ret;
   1756   1.9  christos 	}
   1757   1.1  christos 	msg->question_ok = 1;
   1758   1.1  christos 
   1759  1.19  christos 	ret = getsection(source, msg, dctx, DNS_SECTION_ANSWER, options);
   1760   1.9  christos 	if (ret == ISC_R_UNEXPECTEDEND && ignore_tc) {
   1761   1.1  christos 		goto truncated;
   1762   1.9  christos 	}
   1763   1.1  christos 	if (ret == DNS_R_RECOVERABLE) {
   1764   1.3  christos 		seen_problem = true;
   1765   1.1  christos 		ret = ISC_R_SUCCESS;
   1766   1.1  christos 	}
   1767   1.9  christos 	if (ret != ISC_R_SUCCESS) {
   1768  1.19  christos 		return ret;
   1769   1.9  christos 	}
   1770   1.1  christos 
   1771  1.19  christos 	ret = getsection(source, msg, dctx, DNS_SECTION_AUTHORITY, options);
   1772   1.9  christos 	if (ret == ISC_R_UNEXPECTEDEND && ignore_tc) {
   1773   1.1  christos 		goto truncated;
   1774   1.9  christos 	}
   1775   1.1  christos 	if (ret == DNS_R_RECOVERABLE) {
   1776   1.3  christos 		seen_problem = true;
   1777   1.1  christos 		ret = ISC_R_SUCCESS;
   1778   1.1  christos 	}
   1779   1.9  christos 	if (ret != ISC_R_SUCCESS) {
   1780  1.19  christos 		return ret;
   1781   1.9  christos 	}
   1782   1.1  christos 
   1783  1.19  christos 	ret = getsection(source, msg, dctx, DNS_SECTION_ADDITIONAL, options);
   1784   1.9  christos 	if (ret == ISC_R_UNEXPECTEDEND && ignore_tc) {
   1785   1.1  christos 		goto truncated;
   1786   1.9  christos 	}
   1787   1.1  christos 	if (ret == DNS_R_RECOVERABLE) {
   1788   1.3  christos 		seen_problem = true;
   1789   1.1  christos 		ret = ISC_R_SUCCESS;
   1790   1.1  christos 	}
   1791   1.9  christos 	if (ret != ISC_R_SUCCESS) {
   1792  1.19  christos 		return ret;
   1793   1.9  christos 	}
   1794   1.1  christos 
   1795   1.1  christos 	isc_buffer_remainingregion(source, &r);
   1796   1.1  christos 	if (r.length != 0) {
   1797   1.1  christos 		isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
   1798   1.1  christos 			      DNS_LOGMODULE_MESSAGE, ISC_LOG_DEBUG(3),
   1799   1.1  christos 			      "message has %u byte(s) of trailing garbage",
   1800   1.1  christos 			      r.length);
   1801   1.1  christos 	}
   1802   1.1  christos 
   1803   1.9  christos truncated:
   1804   1.1  christos 
   1805   1.9  christos 	if (ret == ISC_R_UNEXPECTEDEND && ignore_tc) {
   1806  1.19  christos 		return DNS_R_RECOVERABLE;
   1807   1.9  christos 	}
   1808  1.10  christos 	if (seen_problem) {
   1809  1.19  christos 		return DNS_R_RECOVERABLE;
   1810   1.9  christos 	}
   1811  1.19  christos 	return ISC_R_SUCCESS;
   1812   1.1  christos }
   1813   1.1  christos 
   1814   1.1  christos isc_result_t
   1815   1.1  christos dns_message_renderbegin(dns_message_t *msg, dns_compress_t *cctx,
   1816   1.9  christos 			isc_buffer_t *buffer) {
   1817   1.1  christos 	isc_region_t r;
   1818   1.1  christos 
   1819   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   1820   1.1  christos 	REQUIRE(buffer != NULL);
   1821  1.17  christos 	REQUIRE(isc_buffer_length(buffer) < 65536);
   1822   1.1  christos 	REQUIRE(msg->buffer == NULL);
   1823   1.1  christos 	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
   1824   1.1  christos 
   1825   1.1  christos 	msg->cctx = cctx;
   1826   1.1  christos 
   1827   1.1  christos 	/*
   1828   1.1  christos 	 * Erase the contents of this buffer.
   1829   1.1  christos 	 */
   1830   1.1  christos 	isc_buffer_clear(buffer);
   1831   1.1  christos 
   1832   1.1  christos 	/*
   1833   1.1  christos 	 * Make certain there is enough for at least the header in this
   1834   1.1  christos 	 * buffer.
   1835   1.1  christos 	 */
   1836   1.1  christos 	isc_buffer_availableregion(buffer, &r);
   1837   1.9  christos 	if (r.length < DNS_MESSAGE_HEADERLEN) {
   1838  1.19  christos 		return ISC_R_NOSPACE;
   1839   1.9  christos 	}
   1840   1.1  christos 
   1841   1.9  christos 	if (r.length - DNS_MESSAGE_HEADERLEN < msg->reserved) {
   1842  1.19  christos 		return ISC_R_NOSPACE;
   1843   1.9  christos 	}
   1844   1.1  christos 
   1845   1.1  christos 	/*
   1846   1.1  christos 	 * Reserve enough space for the header in this buffer.
   1847   1.1  christos 	 */
   1848   1.1  christos 	isc_buffer_add(buffer, DNS_MESSAGE_HEADERLEN);
   1849   1.1  christos 
   1850   1.1  christos 	msg->buffer = buffer;
   1851   1.1  christos 
   1852  1.19  christos 	return ISC_R_SUCCESS;
   1853   1.1  christos }
   1854   1.1  christos 
   1855   1.1  christos isc_result_t
   1856   1.1  christos dns_message_renderchangebuffer(dns_message_t *msg, isc_buffer_t *buffer) {
   1857   1.1  christos 	isc_region_t r, rn;
   1858   1.1  christos 
   1859   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   1860   1.1  christos 	REQUIRE(buffer != NULL);
   1861   1.1  christos 	REQUIRE(msg->buffer != NULL);
   1862   1.1  christos 
   1863   1.1  christos 	/*
   1864   1.1  christos 	 * Ensure that the new buffer is empty, and has enough space to
   1865   1.1  christos 	 * hold the current contents.
   1866   1.1  christos 	 */
   1867   1.1  christos 	isc_buffer_clear(buffer);
   1868   1.1  christos 
   1869   1.1  christos 	isc_buffer_availableregion(buffer, &rn);
   1870   1.1  christos 	isc_buffer_usedregion(msg->buffer, &r);
   1871   1.1  christos 	REQUIRE(rn.length > r.length);
   1872   1.1  christos 
   1873   1.1  christos 	/*
   1874   1.1  christos 	 * Copy the contents from the old to the new buffer.
   1875   1.1  christos 	 */
   1876   1.1  christos 	isc_buffer_add(buffer, r.length);
   1877   1.1  christos 	memmove(rn.base, r.base, r.length);
   1878   1.1  christos 
   1879   1.1  christos 	msg->buffer = buffer;
   1880   1.1  christos 
   1881  1.19  christos 	return ISC_R_SUCCESS;
   1882   1.1  christos }
   1883   1.1  christos 
   1884   1.1  christos void
   1885   1.1  christos dns_message_renderrelease(dns_message_t *msg, unsigned int space) {
   1886   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   1887   1.1  christos 	REQUIRE(space <= msg->reserved);
   1888   1.1  christos 
   1889   1.1  christos 	msg->reserved -= space;
   1890   1.1  christos }
   1891   1.1  christos 
   1892   1.1  christos isc_result_t
   1893   1.1  christos dns_message_renderreserve(dns_message_t *msg, unsigned int space) {
   1894   1.1  christos 	isc_region_t r;
   1895   1.1  christos 
   1896   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   1897   1.1  christos 
   1898   1.1  christos 	if (msg->buffer != NULL) {
   1899   1.1  christos 		isc_buffer_availableregion(msg->buffer, &r);
   1900   1.9  christos 		if (r.length < (space + msg->reserved)) {
   1901  1.19  christos 			return ISC_R_NOSPACE;
   1902   1.9  christos 		}
   1903   1.1  christos 	}
   1904   1.1  christos 
   1905   1.1  christos 	msg->reserved += space;
   1906   1.1  christos 
   1907  1.19  christos 	return ISC_R_SUCCESS;
   1908   1.1  christos }
   1909   1.1  christos 
   1910  1.14  christos static bool
   1911   1.1  christos wrong_priority(dns_rdataset_t *rds, int pass, dns_rdatatype_t preferred_glue) {
   1912   1.1  christos 	int pass_needed;
   1913   1.1  christos 
   1914   1.1  christos 	/*
   1915   1.1  christos 	 * If we are not rendering class IN, this ordering is bogus.
   1916   1.1  christos 	 */
   1917   1.9  christos 	if (rds->rdclass != dns_rdataclass_in) {
   1918  1.19  christos 		return false;
   1919   1.9  christos 	}
   1920   1.1  christos 
   1921   1.1  christos 	switch (rds->type) {
   1922   1.1  christos 	case dns_rdatatype_a:
   1923   1.1  christos 	case dns_rdatatype_aaaa:
   1924   1.9  christos 		if (preferred_glue == rds->type) {
   1925   1.1  christos 			pass_needed = 4;
   1926   1.9  christos 		} else {
   1927   1.1  christos 			pass_needed = 3;
   1928   1.9  christos 		}
   1929   1.1  christos 		break;
   1930   1.1  christos 	case dns_rdatatype_rrsig:
   1931   1.1  christos 	case dns_rdatatype_dnskey:
   1932   1.1  christos 		pass_needed = 2;
   1933   1.1  christos 		break;
   1934   1.1  christos 	default:
   1935   1.1  christos 		pass_needed = 1;
   1936   1.1  christos 	}
   1937   1.1  christos 
   1938   1.9  christos 	if (pass_needed >= pass) {
   1939  1.19  christos 		return false;
   1940   1.9  christos 	}
   1941   1.1  christos 
   1942  1.19  christos 	return true;
   1943   1.1  christos }
   1944   1.1  christos 
   1945   1.1  christos static isc_result_t
   1946   1.1  christos renderset(dns_rdataset_t *rdataset, const dns_name_t *owner_name,
   1947   1.9  christos 	  dns_compress_t *cctx, isc_buffer_t *target, unsigned int reserved,
   1948   1.9  christos 	  unsigned int options, unsigned int *countp) {
   1949   1.1  christos 	isc_result_t result;
   1950   1.1  christos 
   1951   1.1  christos 	/*
   1952   1.1  christos 	 * Shrink the space in the buffer by the reserved amount.
   1953   1.1  christos 	 */
   1954   1.9  christos 	if (target->length - target->used < reserved) {
   1955  1.19  christos 		return ISC_R_NOSPACE;
   1956   1.9  christos 	}
   1957   1.1  christos 
   1958   1.1  christos 	target->length -= reserved;
   1959   1.9  christos 	result = dns_rdataset_towire(rdataset, owner_name, cctx, target,
   1960   1.9  christos 				     options, countp);
   1961   1.1  christos 	target->length += reserved;
   1962   1.1  christos 
   1963  1.19  christos 	return result;
   1964   1.1  christos }
   1965   1.1  christos 
   1966   1.1  christos static void
   1967   1.1  christos maybe_clear_ad(dns_message_t *msg, dns_section_t sectionid) {
   1968   1.1  christos 	if (msg->counts[sectionid] == 0 &&
   1969   1.1  christos 	    (sectionid == DNS_SECTION_ANSWER ||
   1970   1.1  christos 	     (sectionid == DNS_SECTION_AUTHORITY &&
   1971   1.1  christos 	      msg->counts[DNS_SECTION_ANSWER] == 0)))
   1972   1.9  christos 	{
   1973   1.1  christos 		msg->flags &= ~DNS_MESSAGEFLAG_AD;
   1974   1.9  christos 	}
   1975   1.1  christos }
   1976   1.1  christos 
   1977  1.17  christos static void
   1978  1.17  christos update_min_section_ttl(dns_message_t *restrict msg,
   1979  1.17  christos 		       const dns_section_t sectionid,
   1980  1.17  christos 		       dns_rdataset_t *restrict rdataset) {
   1981  1.17  christos 	if (!msg->minttl[sectionid].is_set ||
   1982  1.17  christos 	    rdataset->ttl < msg->minttl[sectionid].ttl)
   1983  1.17  christos 	{
   1984  1.17  christos 		msg->minttl[sectionid].is_set = true;
   1985  1.17  christos 		msg->minttl[sectionid].ttl = rdataset->ttl;
   1986  1.17  christos 	}
   1987  1.17  christos }
   1988  1.17  christos 
   1989   1.1  christos isc_result_t
   1990   1.1  christos dns_message_rendersection(dns_message_t *msg, dns_section_t sectionid,
   1991   1.9  christos 			  unsigned int options) {
   1992   1.1  christos 	dns_namelist_t *section;
   1993   1.1  christos 	dns_name_t *name, *next_name;
   1994   1.1  christos 	dns_rdataset_t *rdataset, *next_rdataset;
   1995   1.1  christos 	unsigned int count, total;
   1996   1.1  christos 	isc_result_t result;
   1997   1.1  christos 	isc_buffer_t st; /* for rollbacks */
   1998   1.1  christos 	int pass;
   1999   1.3  christos 	bool partial = false;
   2000   1.1  christos 	unsigned int rd_options;
   2001   1.1  christos 	dns_rdatatype_t preferred_glue = 0;
   2002   1.1  christos 
   2003   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   2004   1.1  christos 	REQUIRE(msg->buffer != NULL);
   2005   1.1  christos 	REQUIRE(VALID_NAMED_SECTION(sectionid));
   2006   1.1  christos 
   2007   1.1  christos 	section = &msg->sections[sectionid];
   2008   1.1  christos 
   2009   1.9  christos 	if ((sectionid == DNS_SECTION_ADDITIONAL) &&
   2010   1.9  christos 	    (options & DNS_MESSAGERENDER_ORDERED) == 0)
   2011   1.9  christos 	{
   2012   1.1  christos 		if ((options & DNS_MESSAGERENDER_PREFER_A) != 0) {
   2013   1.1  christos 			preferred_glue = dns_rdatatype_a;
   2014   1.1  christos 			pass = 4;
   2015   1.1  christos 		} else if ((options & DNS_MESSAGERENDER_PREFER_AAAA) != 0) {
   2016   1.1  christos 			preferred_glue = dns_rdatatype_aaaa;
   2017   1.1  christos 			pass = 4;
   2018   1.9  christos 		} else {
   2019   1.1  christos 			pass = 3;
   2020   1.9  christos 		}
   2021   1.9  christos 	} else {
   2022   1.1  christos 		pass = 1;
   2023   1.9  christos 	}
   2024   1.1  christos 
   2025   1.9  christos 	if ((options & DNS_MESSAGERENDER_OMITDNSSEC) == 0) {
   2026   1.1  christos 		rd_options = 0;
   2027   1.9  christos 	} else {
   2028   1.1  christos 		rd_options = DNS_RDATASETTOWIRE_OMITDNSSEC;
   2029   1.9  christos 	}
   2030   1.1  christos 
   2031   1.1  christos 	/*
   2032   1.1  christos 	 * Shrink the space in the buffer by the reserved amount.
   2033   1.1  christos 	 */
   2034   1.9  christos 	if (msg->buffer->length - msg->buffer->used < msg->reserved) {
   2035  1.19  christos 		return ISC_R_NOSPACE;
   2036   1.9  christos 	}
   2037   1.1  christos 	msg->buffer->length -= msg->reserved;
   2038   1.1  christos 
   2039   1.1  christos 	total = 0;
   2040   1.9  christos 	if (msg->reserved == 0 && (options & DNS_MESSAGERENDER_PARTIAL) != 0) {
   2041   1.3  christos 		partial = true;
   2042   1.9  christos 	}
   2043   1.1  christos 
   2044   1.1  christos 	/*
   2045   1.1  christos 	 * Render required glue first.  Set TC if it won't fit.
   2046   1.1  christos 	 */
   2047   1.1  christos 	name = ISC_LIST_HEAD(*section);
   2048   1.1  christos 	if (name != NULL) {
   2049   1.1  christos 		rdataset = ISC_LIST_HEAD(name->list);
   2050   1.1  christos 		if (rdataset != NULL &&
   2051   1.9  christos 		    (rdataset->attributes & DNS_RDATASETATTR_REQUIREDGLUE) !=
   2052   1.9  christos 			    0 &&
   2053   1.9  christos 		    (rdataset->attributes & DNS_RDATASETATTR_RENDERED) == 0)
   2054   1.9  christos 		{
   2055   1.1  christos 			const void *order_arg = &msg->order_arg;
   2056   1.1  christos 			st = *(msg->buffer);
   2057   1.1  christos 			count = 0;
   2058   1.9  christos 			if (partial) {
   2059   1.9  christos 				result = dns_rdataset_towirepartial(
   2060   1.9  christos 					rdataset, name, msg->cctx, msg->buffer,
   2061   1.9  christos 					msg->order, order_arg, rd_options,
   2062   1.9  christos 					&count, NULL);
   2063   1.9  christos 			} else {
   2064   1.9  christos 				result = dns_rdataset_towiresorted(
   2065   1.9  christos 					rdataset, name, msg->cctx, msg->buffer,
   2066   1.9  christos 					msg->order, order_arg, rd_options,
   2067   1.9  christos 					&count);
   2068   1.9  christos 			}
   2069   1.1  christos 			total += count;
   2070   1.1  christos 			if (partial && result == ISC_R_NOSPACE) {
   2071   1.1  christos 				msg->flags |= DNS_MESSAGEFLAG_TC;
   2072   1.1  christos 				msg->buffer->length += msg->reserved;
   2073   1.1  christos 				msg->counts[sectionid] += total;
   2074  1.19  christos 				return result;
   2075   1.1  christos 			}
   2076   1.9  christos 			if (result == ISC_R_NOSPACE) {
   2077   1.1  christos 				msg->flags |= DNS_MESSAGEFLAG_TC;
   2078   1.9  christos 			}
   2079   1.1  christos 			if (result != ISC_R_SUCCESS) {
   2080  1.19  christos 				dns_compress_rollback(msg->cctx, st.used);
   2081   1.9  christos 				*(msg->buffer) = st; /* rollback */
   2082   1.1  christos 				msg->buffer->length += msg->reserved;
   2083   1.1  christos 				msg->counts[sectionid] += total;
   2084  1.19  christos 				return result;
   2085   1.1  christos 			}
   2086  1.17  christos 
   2087  1.17  christos 			update_min_section_ttl(msg, sectionid, rdataset);
   2088  1.17  christos 
   2089   1.1  christos 			rdataset->attributes |= DNS_RDATASETATTR_RENDERED;
   2090   1.1  christos 		}
   2091   1.1  christos 	}
   2092   1.1  christos 
   2093   1.1  christos 	do {
   2094   1.1  christos 		name = ISC_LIST_HEAD(*section);
   2095   1.1  christos 		if (name == NULL) {
   2096   1.1  christos 			msg->buffer->length += msg->reserved;
   2097   1.1  christos 			msg->counts[sectionid] += total;
   2098  1.19  christos 			return ISC_R_SUCCESS;
   2099   1.1  christos 		}
   2100   1.1  christos 
   2101   1.1  christos 		while (name != NULL) {
   2102   1.1  christos 			next_name = ISC_LIST_NEXT(name, link);
   2103   1.1  christos 
   2104   1.1  christos 			rdataset = ISC_LIST_HEAD(name->list);
   2105   1.1  christos 			while (rdataset != NULL) {
   2106   1.1  christos 				next_rdataset = ISC_LIST_NEXT(rdataset, link);
   2107   1.1  christos 
   2108   1.1  christos 				if ((rdataset->attributes &
   2109  1.15  christos 				     DNS_RDATASETATTR_RENDERED) != 0)
   2110  1.15  christos 				{
   2111   1.1  christos 					goto next;
   2112   1.9  christos 				}
   2113   1.1  christos 
   2114   1.9  christos 				if (((options & DNS_MESSAGERENDER_ORDERED) ==
   2115   1.9  christos 				     0) &&
   2116   1.9  christos 				    (sectionid == DNS_SECTION_ADDITIONAL) &&
   2117   1.9  christos 				    wrong_priority(rdataset, pass,
   2118   1.9  christos 						   preferred_glue))
   2119   1.9  christos 				{
   2120   1.1  christos 					goto next;
   2121   1.9  christos 				}
   2122   1.1  christos 
   2123   1.1  christos 				st = *(msg->buffer);
   2124   1.1  christos 
   2125   1.1  christos 				count = 0;
   2126   1.9  christos 				if (partial) {
   2127   1.1  christos 					result = dns_rdataset_towirepartial(
   2128   1.9  christos 						rdataset, name, msg->cctx,
   2129   1.9  christos 						msg->buffer, msg->order,
   2130   1.9  christos 						&msg->order_arg, rd_options,
   2131   1.9  christos 						&count, NULL);
   2132   1.9  christos 				} else {
   2133   1.1  christos 					result = dns_rdataset_towiresorted(
   2134   1.9  christos 						rdataset, name, msg->cctx,
   2135   1.9  christos 						msg->buffer, msg->order,
   2136   1.9  christos 						&msg->order_arg, rd_options,
   2137   1.9  christos 						&count);
   2138   1.9  christos 				}
   2139   1.1  christos 
   2140   1.1  christos 				total += count;
   2141   1.1  christos 
   2142   1.1  christos 				/*
   2143   1.1  christos 				 * If out of space, record stats on what we
   2144   1.1  christos 				 * rendered so far, and return that status.
   2145   1.1  christos 				 *
   2146   1.1  christos 				 * XXXMLG Need to change this when
   2147   1.1  christos 				 * dns_rdataset_towire() can render partial
   2148   1.1  christos 				 * sets starting at some arbitrary point in the
   2149   1.1  christos 				 * set.  This will include setting a bit in the
   2150   1.1  christos 				 * rdataset to indicate that a partial
   2151   1.1  christos 				 * rendering was done, and some state saved
   2152   1.1  christos 				 * somewhere (probably in the message struct)
   2153   1.1  christos 				 * to indicate where to continue from.
   2154   1.1  christos 				 */
   2155   1.1  christos 				if (partial && result == ISC_R_NOSPACE) {
   2156   1.1  christos 					msg->buffer->length += msg->reserved;
   2157   1.1  christos 					msg->counts[sectionid] += total;
   2158  1.19  christos 					return result;
   2159   1.1  christos 				}
   2160   1.1  christos 				if (result != ISC_R_SUCCESS) {
   2161   1.1  christos 					INSIST(st.used < 65536);
   2162   1.9  christos 					dns_compress_rollback(
   2163   1.9  christos 						msg->cctx, (uint16_t)st.used);
   2164   1.9  christos 					*(msg->buffer) = st; /* rollback */
   2165   1.1  christos 					msg->buffer->length += msg->reserved;
   2166   1.1  christos 					msg->counts[sectionid] += total;
   2167   1.1  christos 					maybe_clear_ad(msg, sectionid);
   2168  1.19  christos 					return result;
   2169   1.1  christos 				}
   2170   1.1  christos 
   2171   1.1  christos 				/*
   2172   1.1  christos 				 * If we have rendered non-validated data,
   2173   1.1  christos 				 * ensure that the AD bit is not set.
   2174   1.1  christos 				 */
   2175   1.1  christos 				if (rdataset->trust != dns_trust_secure &&
   2176   1.1  christos 				    (sectionid == DNS_SECTION_ANSWER ||
   2177   1.1  christos 				     sectionid == DNS_SECTION_AUTHORITY))
   2178   1.9  christos 				{
   2179   1.1  christos 					msg->flags &= ~DNS_MESSAGEFLAG_AD;
   2180   1.9  christos 				}
   2181   1.9  christos 				if (OPTOUT(rdataset)) {
   2182   1.1  christos 					msg->flags &= ~DNS_MESSAGEFLAG_AD;
   2183   1.9  christos 				}
   2184   1.1  christos 
   2185  1.17  christos 				update_min_section_ttl(msg, sectionid,
   2186  1.17  christos 						       rdataset);
   2187  1.17  christos 
   2188   1.1  christos 				rdataset->attributes |=
   2189   1.1  christos 					DNS_RDATASETATTR_RENDERED;
   2190   1.1  christos 
   2191   1.1  christos 			next:
   2192   1.1  christos 				rdataset = next_rdataset;
   2193   1.1  christos 			}
   2194   1.1  christos 
   2195   1.1  christos 			name = next_name;
   2196   1.1  christos 		}
   2197   1.1  christos 	} while (--pass != 0);
   2198   1.1  christos 
   2199   1.1  christos 	msg->buffer->length += msg->reserved;
   2200   1.1  christos 	msg->counts[sectionid] += total;
   2201   1.1  christos 
   2202  1.19  christos 	return ISC_R_SUCCESS;
   2203   1.1  christos }
   2204   1.1  christos 
   2205   1.1  christos void
   2206   1.1  christos dns_message_renderheader(dns_message_t *msg, isc_buffer_t *target) {
   2207   1.3  christos 	uint16_t tmp;
   2208   1.1  christos 	isc_region_t r;
   2209   1.1  christos 
   2210   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   2211  1.21  christos 	REQUIRE(msg->buffer != NULL);
   2212   1.1  christos 	REQUIRE(target != NULL);
   2213   1.1  christos 
   2214   1.1  christos 	isc_buffer_availableregion(target, &r);
   2215   1.1  christos 	REQUIRE(r.length >= DNS_MESSAGE_HEADERLEN);
   2216   1.1  christos 
   2217   1.1  christos 	isc_buffer_putuint16(target, msg->id);
   2218   1.1  christos 
   2219   1.9  christos 	tmp = ((msg->opcode << DNS_MESSAGE_OPCODE_SHIFT) &
   2220   1.9  christos 	       DNS_MESSAGE_OPCODE_MASK);
   2221   1.1  christos 	tmp |= (msg->rcode & DNS_MESSAGE_RCODE_MASK);
   2222   1.1  christos 	tmp |= (msg->flags & DNS_MESSAGE_FLAG_MASK);
   2223   1.1  christos 
   2224   1.9  christos 	INSIST(msg->counts[DNS_SECTION_QUESTION] < 65536 &&
   2225   1.9  christos 	       msg->counts[DNS_SECTION_ANSWER] < 65536 &&
   2226   1.1  christos 	       msg->counts[DNS_SECTION_AUTHORITY] < 65536 &&
   2227   1.1  christos 	       msg->counts[DNS_SECTION_ADDITIONAL] < 65536);
   2228   1.1  christos 
   2229   1.1  christos 	isc_buffer_putuint16(target, tmp);
   2230   1.1  christos 	isc_buffer_putuint16(target,
   2231   1.9  christos 			     (uint16_t)msg->counts[DNS_SECTION_QUESTION]);
   2232   1.9  christos 	isc_buffer_putuint16(target, (uint16_t)msg->counts[DNS_SECTION_ANSWER]);
   2233   1.1  christos 	isc_buffer_putuint16(target,
   2234   1.9  christos 			     (uint16_t)msg->counts[DNS_SECTION_AUTHORITY]);
   2235   1.1  christos 	isc_buffer_putuint16(target,
   2236   1.9  christos 			     (uint16_t)msg->counts[DNS_SECTION_ADDITIONAL]);
   2237   1.1  christos }
   2238   1.1  christos 
   2239   1.1  christos isc_result_t
   2240   1.1  christos dns_message_renderend(dns_message_t *msg) {
   2241   1.1  christos 	isc_buffer_t tmpbuf;
   2242   1.1  christos 	isc_region_t r;
   2243   1.1  christos 	int result;
   2244   1.1  christos 	unsigned int count;
   2245   1.1  christos 
   2246   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   2247   1.1  christos 	REQUIRE(msg->buffer != NULL);
   2248   1.1  christos 
   2249   1.1  christos 	if ((msg->rcode & ~DNS_MESSAGE_RCODE_MASK) != 0 && msg->opt == NULL) {
   2250   1.1  christos 		/*
   2251   1.1  christos 		 * We have an extended rcode but are not using EDNS.
   2252   1.1  christos 		 */
   2253  1.19  christos 		return DNS_R_FORMERR;
   2254   1.1  christos 	}
   2255   1.1  christos 
   2256   1.1  christos 	/*
   2257   1.1  christos 	 * If we're adding a OPT, TSIG or SIG(0) to a truncated message,
   2258   1.1  christos 	 * clear all rdatasets from the message except for the question
   2259   1.1  christos 	 * before adding the OPT, TSIG or SIG(0).  If the question doesn't
   2260   1.1  christos 	 * fit, don't include it.
   2261   1.1  christos 	 */
   2262   1.1  christos 	if ((msg->tsigkey != NULL || msg->sig0key != NULL || msg->opt) &&
   2263   1.1  christos 	    (msg->flags & DNS_MESSAGEFLAG_TC) != 0)
   2264   1.1  christos 	{
   2265   1.1  christos 		isc_buffer_t *buf;
   2266   1.1  christos 
   2267   1.1  christos 		msgresetnames(msg, DNS_SECTION_ANSWER);
   2268   1.1  christos 		buf = msg->buffer;
   2269   1.1  christos 		dns_message_renderreset(msg);
   2270   1.1  christos 		msg->buffer = buf;
   2271   1.1  christos 		isc_buffer_clear(msg->buffer);
   2272   1.1  christos 		isc_buffer_add(msg->buffer, DNS_MESSAGE_HEADERLEN);
   2273   1.1  christos 		dns_compress_rollback(msg->cctx, 0);
   2274   1.1  christos 		result = dns_message_rendersection(msg, DNS_SECTION_QUESTION,
   2275   1.1  christos 						   0);
   2276   1.9  christos 		if (result != ISC_R_SUCCESS && result != ISC_R_NOSPACE) {
   2277  1.19  christos 			return result;
   2278   1.9  christos 		}
   2279   1.1  christos 	}
   2280   1.1  christos 
   2281   1.1  christos 	/*
   2282   1.1  christos 	 * If we've got an OPT record, render it.
   2283   1.1  christos 	 */
   2284   1.1  christos 	if (msg->opt != NULL) {
   2285   1.1  christos 		dns_message_renderrelease(msg, msg->opt_reserved);
   2286   1.1  christos 		msg->opt_reserved = 0;
   2287   1.1  christos 		/*
   2288  1.11  christos 		 * Set the extended rcode.  Cast msg->rcode to dns_ttl_t
   2289  1.11  christos 		 * so that we do a unsigned shift.
   2290   1.1  christos 		 */
   2291   1.1  christos 		msg->opt->ttl &= ~DNS_MESSAGE_EDNSRCODE_MASK;
   2292  1.11  christos 		msg->opt->ttl |= (((dns_ttl_t)(msg->rcode) << 20) &
   2293   1.1  christos 				  DNS_MESSAGE_EDNSRCODE_MASK);
   2294   1.1  christos 		/*
   2295   1.1  christos 		 * Render.
   2296   1.1  christos 		 */
   2297   1.1  christos 		count = 0;
   2298   1.1  christos 		result = renderset(msg->opt, dns_rootname, msg->cctx,
   2299   1.1  christos 				   msg->buffer, msg->reserved, 0, &count);
   2300   1.1  christos 		msg->counts[DNS_SECTION_ADDITIONAL] += count;
   2301   1.9  christos 		if (result != ISC_R_SUCCESS) {
   2302  1.19  christos 			return result;
   2303   1.9  christos 		}
   2304   1.1  christos 	}
   2305   1.1  christos 
   2306   1.1  christos 	/*
   2307   1.1  christos 	 * Deal with EDNS padding.
   2308   1.1  christos 	 *
   2309   1.1  christos 	 * padding_off is the length of the OPT with the 0-length PAD
   2310   1.1  christos 	 * at the end.
   2311   1.1  christos 	 */
   2312   1.1  christos 	if (msg->padding_off > 0) {
   2313   1.1  christos 		unsigned char *cp = isc_buffer_used(msg->buffer);
   2314   1.1  christos 		unsigned int used, remaining;
   2315   1.3  christos 		uint16_t len, padsize = 0;
   2316   1.1  christos 
   2317   1.1  christos 		/* Check PAD */
   2318   1.9  christos 		if ((cp[-4] != 0) || (cp[-3] != DNS_OPT_PAD) || (cp[-2] != 0) ||
   2319  1.15  christos 		    (cp[-1] != 0))
   2320  1.15  christos 		{
   2321  1.19  christos 			return ISC_R_UNEXPECTED;
   2322   1.9  christos 		}
   2323   1.1  christos 
   2324   1.1  christos 		/*
   2325   1.1  christos 		 * Zero-fill the PAD to the computed size;
   2326   1.1  christos 		 * patch PAD length and OPT rdlength
   2327   1.1  christos 		 */
   2328   1.1  christos 
   2329   1.1  christos 		/* Aligned used length + reserved to padding block */
   2330   1.1  christos 		used = isc_buffer_usedlength(msg->buffer);
   2331   1.1  christos 		if (msg->padding != 0) {
   2332   1.9  christos 			padsize = ((uint16_t)used + msg->reserved) %
   2333   1.9  christos 				  msg->padding;
   2334   1.1  christos 		}
   2335   1.1  christos 		if (padsize != 0) {
   2336   1.1  christos 			padsize = msg->padding - padsize;
   2337   1.1  christos 		}
   2338   1.1  christos 		/* Stay below the available length */
   2339   1.1  christos 		remaining = isc_buffer_availablelength(msg->buffer);
   2340   1.9  christos 		if (padsize > remaining) {
   2341   1.1  christos 			padsize = remaining;
   2342   1.9  christos 		}
   2343   1.1  christos 
   2344   1.1  christos 		isc_buffer_add(msg->buffer, padsize);
   2345   1.1  christos 		memset(cp, 0, padsize);
   2346   1.1  christos 		cp[-2] = (unsigned char)((padsize & 0xff00U) >> 8);
   2347   1.1  christos 		cp[-1] = (unsigned char)(padsize & 0x00ffU);
   2348   1.1  christos 		cp -= msg->padding_off;
   2349   1.3  christos 		len = ((uint16_t)(cp[-2])) << 8;
   2350   1.3  christos 		len |= ((uint16_t)(cp[-1]));
   2351   1.1  christos 		len += padsize;
   2352   1.1  christos 		cp[-2] = (unsigned char)((len & 0xff00U) >> 8);
   2353   1.1  christos 		cp[-1] = (unsigned char)(len & 0x00ffU);
   2354   1.1  christos 	}
   2355   1.1  christos 
   2356   1.1  christos 	/*
   2357   1.1  christos 	 * If we're adding a TSIG record, generate and render it.
   2358   1.1  christos 	 */
   2359   1.1  christos 	if (msg->tsigkey != NULL) {
   2360   1.1  christos 		dns_message_renderrelease(msg, msg->sig_reserved);
   2361   1.1  christos 		msg->sig_reserved = 0;
   2362   1.1  christos 		result = dns_tsig_sign(msg);
   2363   1.9  christos 		if (result != ISC_R_SUCCESS) {
   2364  1.19  christos 			return result;
   2365   1.9  christos 		}
   2366   1.1  christos 		count = 0;
   2367   1.1  christos 		result = renderset(msg->tsig, msg->tsigname, msg->cctx,
   2368   1.1  christos 				   msg->buffer, msg->reserved, 0, &count);
   2369   1.1  christos 		msg->counts[DNS_SECTION_ADDITIONAL] += count;
   2370   1.9  christos 		if (result != ISC_R_SUCCESS) {
   2371  1.19  christos 			return result;
   2372   1.9  christos 		}
   2373   1.1  christos 	}
   2374   1.1  christos 
   2375   1.1  christos 	/*
   2376   1.1  christos 	 * If we're adding a SIG(0) record, generate and render it.
   2377   1.1  christos 	 */
   2378   1.1  christos 	if (msg->sig0key != NULL) {
   2379   1.1  christos 		dns_message_renderrelease(msg, msg->sig_reserved);
   2380   1.1  christos 		msg->sig_reserved = 0;
   2381   1.1  christos 		result = dns_dnssec_signmessage(msg, msg->sig0key);
   2382   1.9  christos 		if (result != ISC_R_SUCCESS) {
   2383  1.19  christos 			return result;
   2384   1.9  christos 		}
   2385   1.1  christos 		count = 0;
   2386   1.1  christos 		/*
   2387   1.1  christos 		 * Note: dns_rootname is used here, not msg->sig0name, since
   2388   1.1  christos 		 * the owner name of a SIG(0) is irrelevant, and will not
   2389   1.1  christos 		 * be set in a message being rendered.
   2390   1.1  christos 		 */
   2391   1.1  christos 		result = renderset(msg->sig0, dns_rootname, msg->cctx,
   2392   1.1  christos 				   msg->buffer, msg->reserved, 0, &count);
   2393   1.1  christos 		msg->counts[DNS_SECTION_ADDITIONAL] += count;
   2394   1.9  christos 		if (result != ISC_R_SUCCESS) {
   2395  1.19  christos 			return result;
   2396   1.9  christos 		}
   2397   1.1  christos 	}
   2398   1.1  christos 
   2399   1.1  christos 	isc_buffer_usedregion(msg->buffer, &r);
   2400   1.1  christos 	isc_buffer_init(&tmpbuf, r.base, r.length);
   2401   1.1  christos 
   2402   1.1  christos 	dns_message_renderheader(msg, &tmpbuf);
   2403   1.1  christos 
   2404   1.9  christos 	msg->buffer = NULL; /* forget about this buffer only on success XXX */
   2405   1.1  christos 
   2406  1.19  christos 	return ISC_R_SUCCESS;
   2407   1.1  christos }
   2408   1.1  christos 
   2409   1.1  christos void
   2410   1.1  christos dns_message_renderreset(dns_message_t *msg) {
   2411   1.1  christos 	/*
   2412   1.1  christos 	 * Reset the message so that it may be rendered again.
   2413   1.1  christos 	 */
   2414   1.1  christos 
   2415   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   2416   1.1  christos 	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
   2417   1.1  christos 
   2418   1.1  christos 	msg->buffer = NULL;
   2419   1.1  christos 
   2420  1.19  christos 	for (size_t i = 0; i < DNS_SECTION_MAX; i++) {
   2421  1.19  christos 		dns_name_t *name = NULL;
   2422  1.19  christos 
   2423   1.1  christos 		msg->cursors[i] = NULL;
   2424   1.1  christos 		msg->counts[i] = 0;
   2425  1.22  christos 		ISC_LIST_FOREACH(msg->sections[i], name, link) {
   2426  1.19  christos 			dns_rdataset_t *rds = NULL;
   2427  1.22  christos 			ISC_LIST_FOREACH(name->list, rds, link) {
   2428   1.1  christos 				rds->attributes &= ~DNS_RDATASETATTR_RENDERED;
   2429   1.1  christos 			}
   2430   1.1  christos 		}
   2431   1.1  christos 	}
   2432   1.9  christos 	if (msg->tsigname != NULL) {
   2433   1.1  christos 		dns_message_puttempname(msg, &msg->tsigname);
   2434   1.9  christos 	}
   2435   1.1  christos 	if (msg->tsig != NULL) {
   2436  1.19  christos 		dns__message_putassociatedrdataset(msg, &msg->tsig);
   2437   1.1  christos 	}
   2438  1.17  christos 	if (msg->sig0name != NULL) {
   2439  1.17  christos 		dns_message_puttempname(msg, &msg->sig0name);
   2440  1.17  christos 	}
   2441   1.1  christos 	if (msg->sig0 != NULL) {
   2442  1.19  christos 		dns__message_putassociatedrdataset(msg, &msg->sig0);
   2443   1.1  christos 	}
   2444   1.1  christos }
   2445   1.1  christos 
   2446   1.1  christos isc_result_t
   2447   1.1  christos dns_message_firstname(dns_message_t *msg, dns_section_t section) {
   2448   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   2449   1.1  christos 	REQUIRE(VALID_NAMED_SECTION(section));
   2450   1.1  christos 
   2451   1.1  christos 	msg->cursors[section] = ISC_LIST_HEAD(msg->sections[section]);
   2452   1.1  christos 
   2453   1.9  christos 	if (msg->cursors[section] == NULL) {
   2454  1.19  christos 		return ISC_R_NOMORE;
   2455   1.9  christos 	}
   2456   1.1  christos 
   2457  1.19  christos 	return ISC_R_SUCCESS;
   2458   1.1  christos }
   2459   1.1  christos 
   2460   1.1  christos isc_result_t
   2461   1.1  christos dns_message_nextname(dns_message_t *msg, dns_section_t section) {
   2462   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   2463   1.1  christos 	REQUIRE(VALID_NAMED_SECTION(section));
   2464   1.1  christos 	REQUIRE(msg->cursors[section] != NULL);
   2465   1.1  christos 
   2466   1.1  christos 	msg->cursors[section] = ISC_LIST_NEXT(msg->cursors[section], link);
   2467   1.1  christos 
   2468   1.9  christos 	if (msg->cursors[section] == NULL) {
   2469  1.19  christos 		return ISC_R_NOMORE;
   2470   1.9  christos 	}
   2471   1.1  christos 
   2472  1.19  christos 	return ISC_R_SUCCESS;
   2473   1.1  christos }
   2474   1.1  christos 
   2475   1.1  christos void
   2476   1.1  christos dns_message_currentname(dns_message_t *msg, dns_section_t section,
   2477   1.9  christos 			dns_name_t **name) {
   2478   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   2479   1.1  christos 	REQUIRE(VALID_NAMED_SECTION(section));
   2480   1.1  christos 	REQUIRE(name != NULL && *name == NULL);
   2481   1.1  christos 	REQUIRE(msg->cursors[section] != NULL);
   2482   1.1  christos 
   2483   1.1  christos 	*name = msg->cursors[section];
   2484   1.1  christos }
   2485   1.1  christos 
   2486   1.1  christos isc_result_t
   2487   1.1  christos dns_message_findname(dns_message_t *msg, dns_section_t section,
   2488   1.1  christos 		     const dns_name_t *target, dns_rdatatype_t type,
   2489   1.1  christos 		     dns_rdatatype_t covers, dns_name_t **name,
   2490   1.9  christos 		     dns_rdataset_t **rdataset) {
   2491  1.16  christos 	dns_name_t *foundname = NULL;
   2492   1.1  christos 	isc_result_t result;
   2493   1.1  christos 
   2494   1.1  christos 	/*
   2495   1.1  christos 	 * XXX These requirements are probably too intensive, especially
   2496   1.1  christos 	 * where things can be NULL, but as they are they ensure that if
   2497   1.1  christos 	 * something is NON-NULL, indicating that the caller expects it
   2498   1.1  christos 	 * to be filled in, that we can in fact fill it in.
   2499   1.1  christos 	 */
   2500   1.1  christos 	REQUIRE(msg != NULL);
   2501  1.19  christos 	REQUIRE(VALID_NAMED_SECTION(section));
   2502   1.1  christos 	REQUIRE(target != NULL);
   2503   1.1  christos 	REQUIRE(name == NULL || *name == NULL);
   2504   1.1  christos 
   2505   1.1  christos 	if (type == dns_rdatatype_any) {
   2506   1.1  christos 		REQUIRE(rdataset == NULL);
   2507   1.1  christos 	} else {
   2508   1.1  christos 		REQUIRE(rdataset == NULL || *rdataset == NULL);
   2509   1.1  christos 	}
   2510   1.1  christos 
   2511   1.9  christos 	result = findname(&foundname, target, &msg->sections[section]);
   2512   1.1  christos 
   2513   1.9  christos 	if (result == ISC_R_NOTFOUND) {
   2514  1.19  christos 		return DNS_R_NXDOMAIN;
   2515   1.9  christos 	} else if (result != ISC_R_SUCCESS) {
   2516  1.19  christos 		return result;
   2517   1.9  christos 	}
   2518   1.1  christos 
   2519  1.20  christos 	SET_IF_NOT_NULL(name, foundname);
   2520   1.1  christos 
   2521   1.1  christos 	/*
   2522   1.1  christos 	 * And now look for the type.
   2523   1.1  christos 	 */
   2524  1.17  christos 	if (type == dns_rdatatype_any) {
   2525  1.19  christos 		return ISC_R_SUCCESS;
   2526   1.9  christos 	}
   2527   1.1  christos 
   2528   1.1  christos 	result = dns_message_findtype(foundname, type, covers, rdataset);
   2529   1.9  christos 	if (result == ISC_R_NOTFOUND) {
   2530  1.19  christos 		return DNS_R_NXRRSET;
   2531   1.9  christos 	}
   2532   1.1  christos 
   2533  1.19  christos 	return result;
   2534   1.1  christos }
   2535   1.1  christos 
   2536   1.1  christos void
   2537   1.1  christos dns_message_addname(dns_message_t *msg, dns_name_t *name,
   2538   1.9  christos 		    dns_section_t section) {
   2539   1.1  christos 	REQUIRE(msg != NULL);
   2540   1.1  christos 	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
   2541  1.21  christos 	REQUIRE(dns_name_isabsolute(name));
   2542   1.1  christos 	REQUIRE(VALID_NAMED_SECTION(section));
   2543   1.1  christos 
   2544   1.1  christos 	ISC_LIST_APPEND(msg->sections[section], name, link);
   2545   1.1  christos }
   2546   1.1  christos 
   2547   1.1  christos void
   2548   1.1  christos dns_message_removename(dns_message_t *msg, dns_name_t *name,
   2549   1.9  christos 		       dns_section_t section) {
   2550   1.1  christos 	REQUIRE(msg != NULL);
   2551   1.1  christos 	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
   2552  1.21  christos 	REQUIRE(dns_name_isabsolute(name));
   2553   1.1  christos 	REQUIRE(VALID_NAMED_SECTION(section));
   2554   1.1  christos 
   2555   1.1  christos 	ISC_LIST_UNLINK(msg->sections[section], name, link);
   2556   1.1  christos }
   2557   1.1  christos 
   2558  1.19  christos void
   2559   1.1  christos dns_message_gettempname(dns_message_t *msg, dns_name_t **item) {
   2560  1.13  christos 	dns_fixedname_t *fn = NULL;
   2561   1.1  christos 
   2562   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   2563   1.1  christos 	REQUIRE(item != NULL && *item == NULL);
   2564   1.1  christos 
   2565  1.13  christos 	fn = isc_mempool_get(msg->namepool);
   2566  1.13  christos 	*item = dns_fixedname_initname(fn);
   2567   1.1  christos }
   2568   1.1  christos 
   2569  1.19  christos void
   2570   1.1  christos dns_message_gettemprdata(dns_message_t *msg, dns_rdata_t **item) {
   2571   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   2572   1.1  christos 	REQUIRE(item != NULL && *item == NULL);
   2573   1.1  christos 
   2574   1.1  christos 	*item = newrdata(msg);
   2575   1.1  christos }
   2576   1.1  christos 
   2577  1.19  christos void
   2578   1.1  christos dns_message_gettemprdataset(dns_message_t *msg, dns_rdataset_t **item) {
   2579   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   2580   1.1  christos 	REQUIRE(item != NULL && *item == NULL);
   2581   1.1  christos 
   2582   1.1  christos 	*item = isc_mempool_get(msg->rdspool);
   2583   1.1  christos 	dns_rdataset_init(*item);
   2584   1.1  christos }
   2585   1.1  christos 
   2586  1.19  christos void
   2587   1.1  christos dns_message_gettemprdatalist(dns_message_t *msg, dns_rdatalist_t **item) {
   2588   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   2589   1.1  christos 	REQUIRE(item != NULL && *item == NULL);
   2590   1.1  christos 
   2591   1.1  christos 	*item = newrdatalist(msg);
   2592   1.1  christos }
   2593   1.1  christos 
   2594   1.1  christos void
   2595   1.1  christos dns_message_puttempname(dns_message_t *msg, dns_name_t **itemp) {
   2596  1.13  christos 	dns_name_t *item = NULL;
   2597   1.1  christos 
   2598   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   2599   1.1  christos 	REQUIRE(itemp != NULL && *itemp != NULL);
   2600  1.13  christos 
   2601   1.1  christos 	item = *itemp;
   2602   1.9  christos 	*itemp = NULL;
   2603  1.13  christos 
   2604   1.1  christos 	REQUIRE(!ISC_LINK_LINKED(item, link));
   2605   1.1  christos 	REQUIRE(ISC_LIST_HEAD(item->list) == NULL);
   2606   1.1  christos 
   2607  1.19  christos 	if (item->hashmap != NULL) {
   2608  1.19  christos 		isc_hashmap_destroy(&item->hashmap);
   2609  1.16  christos 	}
   2610  1.16  christos 
   2611  1.13  christos 	/*
   2612  1.13  christos 	 * we need to check this in case dns_name_dup() was used.
   2613  1.13  christos 	 */
   2614   1.9  christos 	if (dns_name_dynamic(item)) {
   2615   1.1  christos 		dns_name_free(item, msg->mctx);
   2616   1.9  christos 	}
   2617  1.13  christos 
   2618  1.13  christos 	/*
   2619  1.13  christos 	 * 'name' is the first field in dns_fixedname_t, so putting
   2620  1.13  christos 	 * back the address of name is the same as putting back
   2621  1.13  christos 	 * the fixedname.
   2622  1.13  christos 	 */
   2623   1.1  christos 	isc_mempool_put(msg->namepool, item);
   2624   1.1  christos }
   2625   1.1  christos 
   2626   1.1  christos void
   2627   1.1  christos dns_message_puttemprdata(dns_message_t *msg, dns_rdata_t **item) {
   2628   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   2629   1.1  christos 	REQUIRE(item != NULL && *item != NULL);
   2630   1.1  christos 
   2631   1.1  christos 	releaserdata(msg, *item);
   2632   1.1  christos 	*item = NULL;
   2633   1.1  christos }
   2634   1.1  christos 
   2635  1.19  christos static void
   2636  1.19  christos dns__message_putassociatedrdataset(dns_message_t *msg, dns_rdataset_t **item) {
   2637  1.19  christos 	dns_rdataset_disassociate(*item);
   2638  1.19  christos 	dns_message_puttemprdataset(msg, item);
   2639  1.19  christos }
   2640  1.19  christos 
   2641   1.1  christos void
   2642   1.1  christos dns_message_puttemprdataset(dns_message_t *msg, dns_rdataset_t **item) {
   2643   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   2644   1.1  christos 	REQUIRE(item != NULL && *item != NULL);
   2645   1.1  christos 
   2646   1.1  christos 	REQUIRE(!dns_rdataset_isassociated(*item));
   2647   1.1  christos 	isc_mempool_put(msg->rdspool, *item);
   2648   1.1  christos 	*item = NULL;
   2649   1.1  christos }
   2650   1.1  christos 
   2651   1.1  christos void
   2652   1.1  christos dns_message_puttemprdatalist(dns_message_t *msg, dns_rdatalist_t **item) {
   2653   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   2654   1.1  christos 	REQUIRE(item != NULL && *item != NULL);
   2655   1.1  christos 
   2656   1.1  christos 	releaserdatalist(msg, *item);
   2657   1.1  christos 	*item = NULL;
   2658   1.1  christos }
   2659   1.1  christos 
   2660   1.1  christos isc_result_t
   2661   1.1  christos dns_message_peekheader(isc_buffer_t *source, dns_messageid_t *idp,
   2662   1.9  christos 		       unsigned int *flagsp) {
   2663   1.1  christos 	isc_region_t r;
   2664   1.1  christos 	isc_buffer_t buffer;
   2665   1.1  christos 	dns_messageid_t id;
   2666   1.1  christos 	unsigned int flags;
   2667   1.1  christos 
   2668   1.1  christos 	REQUIRE(source != NULL);
   2669   1.1  christos 
   2670   1.1  christos 	buffer = *source;
   2671   1.1  christos 
   2672   1.1  christos 	isc_buffer_remainingregion(&buffer, &r);
   2673   1.9  christos 	if (r.length < DNS_MESSAGE_HEADERLEN) {
   2674  1.19  christos 		return ISC_R_UNEXPECTEDEND;
   2675   1.9  christos 	}
   2676   1.1  christos 
   2677   1.1  christos 	id = isc_buffer_getuint16(&buffer);
   2678   1.1  christos 	flags = isc_buffer_getuint16(&buffer);
   2679   1.1  christos 	flags &= DNS_MESSAGE_FLAG_MASK;
   2680   1.1  christos 
   2681  1.19  christos 	SET_IF_NOT_NULL(flagsp, flags);
   2682  1.19  christos 	SET_IF_NOT_NULL(idp, id);
   2683   1.1  christos 
   2684  1.19  christos 	return ISC_R_SUCCESS;
   2685   1.1  christos }
   2686   1.1  christos 
   2687   1.1  christos isc_result_t
   2688   1.3  christos dns_message_reply(dns_message_t *msg, bool want_question_section) {
   2689   1.1  christos 	unsigned int clear_from;
   2690   1.1  christos 	isc_result_t result;
   2691   1.1  christos 
   2692   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   2693   1.1  christos 	REQUIRE((msg->flags & DNS_MESSAGEFLAG_QR) == 0);
   2694   1.1  christos 
   2695   1.9  christos 	if (!msg->header_ok) {
   2696  1.19  christos 		return DNS_R_FORMERR;
   2697   1.9  christos 	}
   2698   1.9  christos 	if (msg->opcode != dns_opcode_query && msg->opcode != dns_opcode_notify)
   2699   1.9  christos 	{
   2700   1.3  christos 		want_question_section = false;
   2701   1.9  christos 	}
   2702   1.9  christos 	if (msg->opcode == dns_opcode_update) {
   2703   1.1  christos 		clear_from = DNS_SECTION_PREREQUISITE;
   2704   1.9  christos 	} else if (want_question_section) {
   2705   1.9  christos 		if (!msg->question_ok) {
   2706  1.19  christos 			return DNS_R_FORMERR;
   2707   1.9  christos 		}
   2708   1.1  christos 		clear_from = DNS_SECTION_ANSWER;
   2709   1.9  christos 	} else {
   2710   1.1  christos 		clear_from = DNS_SECTION_QUESTION;
   2711   1.9  christos 	}
   2712   1.1  christos 	msg->from_to_wire = DNS_MESSAGE_INTENTRENDER;
   2713   1.1  christos 	msgresetnames(msg, clear_from);
   2714   1.1  christos 	msgresetopt(msg);
   2715   1.3  christos 	msgresetsigs(msg, true);
   2716   1.1  christos 	msginitprivate(msg);
   2717   1.1  christos 	/*
   2718   1.1  christos 	 * We now clear most flags and then set QR, ensuring that the
   2719   1.1  christos 	 * reply's flags will be in a reasonable state.
   2720   1.1  christos 	 */
   2721   1.9  christos 	if (msg->opcode == dns_opcode_query) {
   2722   1.1  christos 		msg->flags &= DNS_MESSAGE_REPLYPRESERVE;
   2723   1.9  christos 	} else {
   2724   1.1  christos 		msg->flags = 0;
   2725   1.9  christos 	}
   2726   1.1  christos 	msg->flags |= DNS_MESSAGEFLAG_QR;
   2727   1.1  christos 
   2728   1.1  christos 	/*
   2729   1.1  christos 	 * This saves the query TSIG status, if the query was signed, and
   2730   1.1  christos 	 * reserves space in the reply for the TSIG.
   2731   1.1  christos 	 */
   2732   1.1  christos 	if (msg->tsigkey != NULL) {
   2733   1.1  christos 		unsigned int otherlen = 0;
   2734   1.1  christos 		msg->querytsigstatus = msg->tsigstatus;
   2735   1.1  christos 		msg->tsigstatus = dns_rcode_noerror;
   2736   1.9  christos 		if (msg->querytsigstatus == dns_tsigerror_badtime) {
   2737   1.1  christos 			otherlen = 6;
   2738   1.9  christos 		}
   2739   1.1  christos 		msg->sig_reserved = spacefortsig(msg->tsigkey, otherlen);
   2740   1.1  christos 		result = dns_message_renderreserve(msg, msg->sig_reserved);
   2741   1.1  christos 		if (result != ISC_R_SUCCESS) {
   2742   1.1  christos 			msg->sig_reserved = 0;
   2743  1.19  christos 			return result;
   2744   1.1  christos 		}
   2745   1.1  christos 	}
   2746   1.1  christos 	if (msg->saved.base != NULL) {
   2747   1.1  christos 		msg->query.base = msg->saved.base;
   2748   1.1  christos 		msg->query.length = msg->saved.length;
   2749   1.1  christos 		msg->free_query = msg->free_saved;
   2750   1.1  christos 		msg->saved.base = NULL;
   2751   1.1  christos 		msg->saved.length = 0;
   2752   1.1  christos 		msg->free_saved = 0;
   2753   1.1  christos 	}
   2754   1.1  christos 
   2755  1.19  christos 	return ISC_R_SUCCESS;
   2756   1.1  christos }
   2757   1.1  christos 
   2758   1.1  christos dns_rdataset_t *
   2759   1.1  christos dns_message_getopt(dns_message_t *msg) {
   2760   1.1  christos 	/*
   2761   1.1  christos 	 * Get the OPT record for 'msg'.
   2762   1.1  christos 	 */
   2763   1.1  christos 
   2764   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   2765   1.1  christos 
   2766  1.19  christos 	return msg->opt;
   2767   1.1  christos }
   2768   1.1  christos 
   2769   1.1  christos isc_result_t
   2770   1.1  christos dns_message_setopt(dns_message_t *msg, dns_rdataset_t *opt) {
   2771   1.1  christos 	isc_result_t result;
   2772   1.1  christos 	dns_rdata_t rdata = DNS_RDATA_INIT;
   2773   1.1  christos 
   2774   1.1  christos 	/*
   2775   1.1  christos 	 * Set the OPT record for 'msg'.
   2776   1.1  christos 	 */
   2777   1.1  christos 
   2778   1.1  christos 	/*
   2779   1.1  christos 	 * The space required for an OPT record is:
   2780   1.1  christos 	 *
   2781   1.1  christos 	 *	1 byte for the name
   2782   1.1  christos 	 *	2 bytes for the type
   2783   1.1  christos 	 *	2 bytes for the class
   2784   1.1  christos 	 *	4 bytes for the ttl
   2785   1.1  christos 	 *	2 bytes for the rdata length
   2786   1.1  christos 	 * ---------------------------------
   2787   1.1  christos 	 *     11 bytes
   2788   1.1  christos 	 *
   2789   1.1  christos 	 * plus the length of the rdata.
   2790   1.1  christos 	 */
   2791   1.1  christos 
   2792   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   2793  1.19  christos 	REQUIRE(opt == NULL || DNS_RDATASET_VALID(opt));
   2794  1.19  christos 	REQUIRE(opt == NULL || opt->type == dns_rdatatype_opt);
   2795   1.1  christos 	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
   2796   1.1  christos 	REQUIRE(msg->state == DNS_SECTION_ANY);
   2797   1.1  christos 
   2798   1.1  christos 	msgresetopt(msg);
   2799   1.1  christos 
   2800  1.19  christos 	if (opt == NULL) {
   2801  1.19  christos 		return ISC_R_SUCCESS;
   2802  1.19  christos 	}
   2803  1.19  christos 
   2804   1.1  christos 	result = dns_rdataset_first(opt);
   2805   1.9  christos 	if (result != ISC_R_SUCCESS) {
   2806   1.1  christos 		goto cleanup;
   2807   1.9  christos 	}
   2808   1.1  christos 	dns_rdataset_current(opt, &rdata);
   2809   1.1  christos 	msg->opt_reserved = 11 + rdata.length;
   2810   1.1  christos 	result = dns_message_renderreserve(msg, msg->opt_reserved);
   2811   1.1  christos 	if (result != ISC_R_SUCCESS) {
   2812   1.1  christos 		msg->opt_reserved = 0;
   2813   1.1  christos 		goto cleanup;
   2814   1.1  christos 	}
   2815   1.1  christos 
   2816   1.1  christos 	msg->opt = opt;
   2817   1.1  christos 
   2818  1.19  christos 	return ISC_R_SUCCESS;
   2819   1.1  christos 
   2820   1.9  christos cleanup:
   2821  1.19  christos 	dns__message_putassociatedrdataset(msg, &opt);
   2822  1.19  christos 	return result;
   2823   1.1  christos }
   2824   1.1  christos 
   2825   1.1  christos dns_rdataset_t *
   2826   1.1  christos dns_message_gettsig(dns_message_t *msg, const dns_name_t **owner) {
   2827   1.1  christos 	/*
   2828   1.1  christos 	 * Get the TSIG record and owner for 'msg'.
   2829   1.1  christos 	 */
   2830   1.1  christos 
   2831   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   2832   1.1  christos 	REQUIRE(owner == NULL || *owner == NULL);
   2833   1.1  christos 
   2834  1.19  christos 	SET_IF_NOT_NULL(owner, msg->tsigname);
   2835  1.19  christos 	return msg->tsig;
   2836   1.1  christos }
   2837   1.1  christos 
   2838   1.1  christos isc_result_t
   2839   1.1  christos dns_message_settsigkey(dns_message_t *msg, dns_tsigkey_t *key) {
   2840   1.1  christos 	isc_result_t result;
   2841   1.1  christos 
   2842   1.1  christos 	/*
   2843   1.1  christos 	 * Set the TSIG key for 'msg'
   2844   1.1  christos 	 */
   2845   1.1  christos 
   2846   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   2847   1.1  christos 
   2848   1.1  christos 	if (key == NULL && msg->tsigkey != NULL) {
   2849   1.1  christos 		if (msg->sig_reserved != 0) {
   2850   1.1  christos 			dns_message_renderrelease(msg, msg->sig_reserved);
   2851   1.1  christos 			msg->sig_reserved = 0;
   2852   1.1  christos 		}
   2853   1.1  christos 		dns_tsigkey_detach(&msg->tsigkey);
   2854   1.1  christos 	}
   2855   1.1  christos 	if (key != NULL) {
   2856   1.1  christos 		REQUIRE(msg->tsigkey == NULL && msg->sig0key == NULL);
   2857   1.1  christos 		dns_tsigkey_attach(key, &msg->tsigkey);
   2858   1.1  christos 		if (msg->from_to_wire == DNS_MESSAGE_INTENTRENDER) {
   2859   1.1  christos 			msg->sig_reserved = spacefortsig(msg->tsigkey, 0);
   2860   1.1  christos 			result = dns_message_renderreserve(msg,
   2861   1.1  christos 							   msg->sig_reserved);
   2862   1.1  christos 			if (result != ISC_R_SUCCESS) {
   2863   1.1  christos 				dns_tsigkey_detach(&msg->tsigkey);
   2864   1.1  christos 				msg->sig_reserved = 0;
   2865  1.19  christos 				return result;
   2866   1.1  christos 			}
   2867   1.1  christos 		}
   2868   1.1  christos 	}
   2869  1.19  christos 	return ISC_R_SUCCESS;
   2870   1.1  christos }
   2871   1.1  christos 
   2872   1.1  christos dns_tsigkey_t *
   2873   1.1  christos dns_message_gettsigkey(dns_message_t *msg) {
   2874   1.1  christos 	/*
   2875   1.1  christos 	 * Get the TSIG key for 'msg'
   2876   1.1  christos 	 */
   2877   1.1  christos 
   2878   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   2879   1.1  christos 
   2880  1.19  christos 	return msg->tsigkey;
   2881   1.1  christos }
   2882   1.1  christos 
   2883  1.19  christos void
   2884   1.1  christos dns_message_setquerytsig(dns_message_t *msg, isc_buffer_t *querytsig) {
   2885   1.1  christos 	dns_rdata_t *rdata = NULL;
   2886   1.1  christos 	dns_rdatalist_t *list = NULL;
   2887   1.1  christos 	dns_rdataset_t *set = NULL;
   2888   1.1  christos 	isc_buffer_t *buf = NULL;
   2889   1.1  christos 	isc_region_t r;
   2890   1.1  christos 
   2891   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   2892   1.1  christos 	REQUIRE(msg->querytsig == NULL);
   2893   1.1  christos 
   2894   1.9  christos 	if (querytsig == NULL) {
   2895  1.19  christos 		return;
   2896   1.9  christos 	}
   2897   1.1  christos 
   2898  1.19  christos 	dns_message_gettemprdata(msg, &rdata);
   2899   1.1  christos 
   2900  1.19  christos 	dns_message_gettemprdatalist(msg, &list);
   2901  1.19  christos 	dns_message_gettemprdataset(msg, &set);
   2902   1.1  christos 
   2903   1.1  christos 	isc_buffer_usedregion(querytsig, &r);
   2904   1.9  christos 	isc_buffer_allocate(msg->mctx, &buf, r.length);
   2905   1.1  christos 	isc_buffer_putmem(buf, r.base, r.length);
   2906   1.1  christos 	isc_buffer_usedregion(buf, &r);
   2907   1.1  christos 	dns_rdata_init(rdata);
   2908   1.1  christos 	dns_rdata_fromregion(rdata, dns_rdataclass_any, dns_rdatatype_tsig, &r);
   2909   1.1  christos 	dns_message_takebuffer(msg, &buf);
   2910   1.1  christos 	ISC_LIST_APPEND(list->rdata, rdata, link);
   2911  1.19  christos 	dns_rdatalist_tordataset(list, set);
   2912   1.1  christos 
   2913   1.1  christos 	msg->querytsig = set;
   2914   1.1  christos }
   2915   1.1  christos 
   2916   1.1  christos isc_result_t
   2917   1.1  christos dns_message_getquerytsig(dns_message_t *msg, isc_mem_t *mctx,
   2918   1.1  christos 			 isc_buffer_t **querytsig) {
   2919   1.1  christos 	isc_result_t result;
   2920   1.1  christos 	dns_rdata_t rdata = DNS_RDATA_INIT;
   2921   1.1  christos 	isc_region_t r;
   2922   1.1  christos 
   2923   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   2924   1.1  christos 	REQUIRE(mctx != NULL);
   2925   1.1  christos 	REQUIRE(querytsig != NULL && *querytsig == NULL);
   2926   1.1  christos 
   2927   1.9  christos 	if (msg->tsig == NULL) {
   2928  1.19  christos 		return ISC_R_SUCCESS;
   2929   1.9  christos 	}
   2930   1.1  christos 
   2931   1.1  christos 	result = dns_rdataset_first(msg->tsig);
   2932   1.9  christos 	if (result != ISC_R_SUCCESS) {
   2933  1.19  christos 		return result;
   2934   1.9  christos 	}
   2935   1.1  christos 	dns_rdataset_current(msg->tsig, &rdata);
   2936   1.1  christos 	dns_rdata_toregion(&rdata, &r);
   2937   1.1  christos 
   2938   1.9  christos 	isc_buffer_allocate(mctx, querytsig, r.length);
   2939   1.1  christos 	isc_buffer_putmem(*querytsig, r.base, r.length);
   2940  1.19  christos 	return ISC_R_SUCCESS;
   2941   1.1  christos }
   2942   1.1  christos 
   2943   1.1  christos dns_rdataset_t *
   2944   1.1  christos dns_message_getsig0(dns_message_t *msg, const dns_name_t **owner) {
   2945   1.1  christos 	/*
   2946   1.1  christos 	 * Get the SIG(0) record for 'msg'.
   2947   1.1  christos 	 */
   2948   1.1  christos 
   2949   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   2950   1.1  christos 	REQUIRE(owner == NULL || *owner == NULL);
   2951   1.1  christos 
   2952   1.1  christos 	if (msg->sig0 != NULL && owner != NULL) {
   2953   1.1  christos 		/* If dns_message_getsig0 is called on a rendered message
   2954   1.1  christos 		 * after the SIG(0) has been applied, we need to return the
   2955   1.1  christos 		 * root name, not NULL.
   2956   1.1  christos 		 */
   2957   1.9  christos 		if (msg->sig0name == NULL) {
   2958   1.1  christos 			*owner = dns_rootname;
   2959   1.9  christos 		} else {
   2960   1.1  christos 			*owner = msg->sig0name;
   2961   1.9  christos 		}
   2962   1.1  christos 	}
   2963  1.19  christos 	return msg->sig0;
   2964   1.1  christos }
   2965   1.1  christos 
   2966   1.1  christos isc_result_t
   2967   1.1  christos dns_message_setsig0key(dns_message_t *msg, dst_key_t *key) {
   2968   1.1  christos 	isc_region_t r;
   2969   1.1  christos 	unsigned int x;
   2970   1.1  christos 	isc_result_t result;
   2971   1.1  christos 
   2972   1.1  christos 	/*
   2973   1.1  christos 	 * Set the SIG(0) key for 'msg'
   2974   1.1  christos 	 */
   2975   1.1  christos 
   2976   1.1  christos 	/*
   2977   1.1  christos 	 * The space required for an SIG(0) record is:
   2978   1.1  christos 	 *
   2979   1.1  christos 	 *	1 byte for the name
   2980   1.1  christos 	 *	2 bytes for the type
   2981   1.1  christos 	 *	2 bytes for the class
   2982   1.1  christos 	 *	4 bytes for the ttl
   2983   1.1  christos 	 *	2 bytes for the type covered
   2984   1.1  christos 	 *	1 byte for the algorithm
   2985   1.1  christos 	 *	1 bytes for the labels
   2986   1.1  christos 	 *	4 bytes for the original ttl
   2987   1.1  christos 	 *	4 bytes for the signature expiration
   2988   1.1  christos 	 *	4 bytes for the signature inception
   2989   1.1  christos 	 *	2 bytes for the key tag
   2990   1.1  christos 	 *	n bytes for the signer's name
   2991   1.1  christos 	 *	x bytes for the signature
   2992   1.1  christos 	 * ---------------------------------
   2993   1.1  christos 	 *     27 + n + x bytes
   2994   1.1  christos 	 */
   2995   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   2996   1.1  christos 	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
   2997   1.1  christos 	REQUIRE(msg->state == DNS_SECTION_ANY);
   2998   1.1  christos 
   2999   1.1  christos 	if (key != NULL) {
   3000   1.1  christos 		REQUIRE(msg->sig0key == NULL && msg->tsigkey == NULL);
   3001   1.1  christos 		dns_name_toregion(dst_key_name(key), &r);
   3002   1.1  christos 		result = dst_key_sigsize(key, &x);
   3003   1.1  christos 		if (result != ISC_R_SUCCESS) {
   3004   1.1  christos 			msg->sig_reserved = 0;
   3005  1.19  christos 			return result;
   3006   1.1  christos 		}
   3007   1.1  christos 		msg->sig_reserved = 27 + r.length + x;
   3008   1.1  christos 		result = dns_message_renderreserve(msg, msg->sig_reserved);
   3009   1.1  christos 		if (result != ISC_R_SUCCESS) {
   3010   1.1  christos 			msg->sig_reserved = 0;
   3011  1.19  christos 			return result;
   3012   1.1  christos 		}
   3013   1.1  christos 		msg->sig0key = key;
   3014   1.1  christos 	}
   3015  1.19  christos 	return ISC_R_SUCCESS;
   3016   1.1  christos }
   3017   1.1  christos 
   3018   1.1  christos dst_key_t *
   3019   1.1  christos dns_message_getsig0key(dns_message_t *msg) {
   3020   1.1  christos 	/*
   3021   1.1  christos 	 * Get the SIG(0) key for 'msg'
   3022   1.1  christos 	 */
   3023   1.1  christos 
   3024   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   3025   1.1  christos 
   3026  1.19  christos 	return msg->sig0key;
   3027   1.1  christos }
   3028   1.1  christos 
   3029   1.1  christos void
   3030   1.1  christos dns_message_takebuffer(dns_message_t *msg, isc_buffer_t **buffer) {
   3031   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   3032   1.1  christos 	REQUIRE(buffer != NULL);
   3033   1.1  christos 	REQUIRE(ISC_BUFFER_VALID(*buffer));
   3034   1.1  christos 
   3035   1.1  christos 	ISC_LIST_APPEND(msg->cleanup, *buffer, link);
   3036   1.1  christos 	*buffer = NULL;
   3037   1.1  christos }
   3038   1.1  christos 
   3039   1.1  christos isc_result_t
   3040   1.1  christos dns_message_signer(dns_message_t *msg, dns_name_t *signer) {
   3041   1.1  christos 	isc_result_t result = ISC_R_SUCCESS;
   3042   1.1  christos 	dns_rdata_t rdata = DNS_RDATA_INIT;
   3043   1.1  christos 
   3044   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   3045   1.1  christos 	REQUIRE(signer != NULL);
   3046   1.1  christos 	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTPARSE);
   3047   1.1  christos 
   3048   1.9  christos 	if (msg->tsig == NULL && msg->sig0 == NULL) {
   3049  1.19  christos 		return ISC_R_NOTFOUND;
   3050   1.9  christos 	}
   3051   1.1  christos 
   3052   1.9  christos 	if (msg->verify_attempted == 0) {
   3053  1.19  christos 		return DNS_R_NOTVERIFIEDYET;
   3054   1.9  christos 	}
   3055   1.1  christos 
   3056   1.1  christos 	if (!dns_name_hasbuffer(signer)) {
   3057   1.1  christos 		isc_buffer_t *dynbuf = NULL;
   3058   1.9  christos 		isc_buffer_allocate(msg->mctx, &dynbuf, 512);
   3059   1.1  christos 		dns_name_setbuffer(signer, dynbuf);
   3060   1.1  christos 		dns_message_takebuffer(msg, &dynbuf);
   3061   1.1  christos 	}
   3062   1.1  christos 
   3063   1.1  christos 	if (msg->sig0 != NULL) {
   3064   1.1  christos 		dns_rdata_sig_t sig;
   3065   1.1  christos 
   3066   1.1  christos 		result = dns_rdataset_first(msg->sig0);
   3067   1.1  christos 		INSIST(result == ISC_R_SUCCESS);
   3068   1.1  christos 		dns_rdataset_current(msg->sig0, &rdata);
   3069   1.1  christos 
   3070   1.1  christos 		result = dns_rdata_tostruct(&rdata, &sig, NULL);
   3071   1.9  christos 		if (result != ISC_R_SUCCESS) {
   3072  1.19  christos 			return result;
   3073   1.9  christos 		}
   3074   1.1  christos 
   3075   1.9  christos 		if (msg->verified_sig && msg->sig0status == dns_rcode_noerror) {
   3076   1.1  christos 			result = ISC_R_SUCCESS;
   3077   1.9  christos 		} else {
   3078   1.1  christos 			result = DNS_R_SIGINVALID;
   3079   1.9  christos 		}
   3080   1.1  christos 		dns_name_clone(&sig.signer, signer);
   3081   1.1  christos 		dns_rdata_freestruct(&sig);
   3082   1.1  christos 	} else {
   3083   1.8  christos 		const dns_name_t *identity;
   3084   1.1  christos 		dns_rdata_any_tsig_t tsig;
   3085   1.1  christos 
   3086   1.1  christos 		result = dns_rdataset_first(msg->tsig);
   3087   1.1  christos 		INSIST(result == ISC_R_SUCCESS);
   3088   1.1  christos 		dns_rdataset_current(msg->tsig, &rdata);
   3089   1.1  christos 
   3090   1.1  christos 		result = dns_rdata_tostruct(&rdata, &tsig, NULL);
   3091   1.1  christos 		INSIST(result == ISC_R_SUCCESS);
   3092   1.9  christos 		if (msg->verified_sig && msg->tsigstatus == dns_rcode_noerror &&
   3093   1.1  christos 		    tsig.error == dns_rcode_noerror)
   3094   1.1  christos 		{
   3095   1.1  christos 			result = ISC_R_SUCCESS;
   3096   1.1  christos 		} else if ((!msg->verified_sig) ||
   3097  1.15  christos 			   (msg->tsigstatus != dns_rcode_noerror))
   3098  1.15  christos 		{
   3099   1.1  christos 			result = DNS_R_TSIGVERIFYFAILURE;
   3100   1.1  christos 		} else {
   3101   1.1  christos 			INSIST(tsig.error != dns_rcode_noerror);
   3102   1.1  christos 			result = DNS_R_TSIGERRORSET;
   3103   1.1  christos 		}
   3104   1.1  christos 		dns_rdata_freestruct(&tsig);
   3105   1.1  christos 
   3106   1.1  christos 		if (msg->tsigkey == NULL) {
   3107   1.1  christos 			/*
   3108   1.1  christos 			 * If msg->tsigstatus & tsig.error are both
   3109   1.1  christos 			 * dns_rcode_noerror, the message must have been
   3110   1.1  christos 			 * verified, which means msg->tsigkey will be
   3111   1.1  christos 			 * non-NULL.
   3112   1.1  christos 			 */
   3113   1.1  christos 			INSIST(result != ISC_R_SUCCESS);
   3114   1.1  christos 		} else {
   3115   1.1  christos 			identity = dns_tsigkey_identity(msg->tsigkey);
   3116   1.1  christos 			if (identity == NULL) {
   3117   1.9  christos 				if (result == ISC_R_SUCCESS) {
   3118   1.1  christos 					result = DNS_R_NOIDENTITY;
   3119   1.9  christos 				}
   3120  1.19  christos 				identity = msg->tsigkey->name;
   3121   1.1  christos 			}
   3122   1.1  christos 			dns_name_clone(identity, signer);
   3123   1.1  christos 		}
   3124   1.1  christos 	}
   3125   1.1  christos 
   3126  1.19  christos 	return result;
   3127   1.1  christos }
   3128   1.1  christos 
   3129   1.1  christos void
   3130   1.1  christos dns_message_resetsig(dns_message_t *msg) {
   3131   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   3132   1.1  christos 	msg->verified_sig = 0;
   3133   1.1  christos 	msg->verify_attempted = 0;
   3134   1.1  christos 	msg->tsigstatus = dns_rcode_noerror;
   3135   1.1  christos 	msg->sig0status = dns_rcode_noerror;
   3136   1.1  christos 	msg->timeadjust = 0;
   3137   1.1  christos 	if (msg->tsigkey != NULL) {
   3138   1.1  christos 		dns_tsigkey_detach(&msg->tsigkey);
   3139   1.1  christos 		msg->tsigkey = NULL;
   3140   1.1  christos 	}
   3141   1.1  christos }
   3142   1.1  christos 
   3143   1.1  christos #ifdef SKAN_MSG_DEBUG
   3144   1.1  christos void
   3145   1.1  christos dns_message_dumpsig(dns_message_t *msg, char *txt1) {
   3146   1.1  christos 	dns_rdata_t querytsigrdata = DNS_RDATA_INIT;
   3147   1.1  christos 	dns_rdata_any_tsig_t querytsig;
   3148   1.1  christos 	isc_result_t result;
   3149   1.1  christos 
   3150   1.1  christos 	if (msg->tsig != NULL) {
   3151   1.1  christos 		result = dns_rdataset_first(msg->tsig);
   3152   1.1  christos 		RUNTIME_CHECK(result == ISC_R_SUCCESS);
   3153   1.1  christos 		dns_rdataset_current(msg->tsig, &querytsigrdata);
   3154   1.1  christos 		result = dns_rdata_tostruct(&querytsigrdata, &querytsig, NULL);
   3155   1.1  christos 		RUNTIME_CHECK(result == ISC_R_SUCCESS);
   3156   1.9  christos 		hexdump(txt1, "TSIG", querytsig.signature, querytsig.siglen);
   3157   1.1  christos 	}
   3158   1.1  christos 
   3159   1.1  christos 	if (msg->querytsig != NULL) {
   3160   1.1  christos 		result = dns_rdataset_first(msg->querytsig);
   3161   1.1  christos 		RUNTIME_CHECK(result == ISC_R_SUCCESS);
   3162   1.1  christos 		dns_rdataset_current(msg->querytsig, &querytsigrdata);
   3163   1.1  christos 		result = dns_rdata_tostruct(&querytsigrdata, &querytsig, NULL);
   3164   1.1  christos 		RUNTIME_CHECK(result == ISC_R_SUCCESS);
   3165   1.1  christos 		hexdump(txt1, "QUERYTSIG", querytsig.signature,
   3166   1.1  christos 			querytsig.siglen);
   3167   1.1  christos 	}
   3168   1.1  christos }
   3169   1.9  christos #endif /* ifdef SKAN_MSG_DEBUG */
   3170   1.1  christos 
   3171  1.19  christos static void
   3172  1.19  christos checksig_done(void *arg);
   3173  1.19  christos 
   3174  1.19  christos static void
   3175  1.19  christos checksig_run(void *arg) {
   3176  1.19  christos 	checksig_ctx_t *chsigctx = arg;
   3177  1.19  christos 
   3178  1.19  christos 	chsigctx->result = dns_message_checksig(chsigctx->msg, chsigctx->view);
   3179  1.19  christos 
   3180  1.19  christos 	isc_async_run(chsigctx->loop, checksig_done, chsigctx);
   3181  1.19  christos }
   3182  1.19  christos 
   3183  1.19  christos static void
   3184  1.19  christos checksig_done(void *arg) {
   3185  1.19  christos 	checksig_ctx_t *chsigctx = arg;
   3186  1.19  christos 	dns_message_t *msg = chsigctx->msg;
   3187  1.19  christos 
   3188  1.19  christos 	chsigctx->cb(chsigctx->cbarg, chsigctx->result);
   3189  1.19  christos 
   3190  1.19  christos 	dns_view_detach(&chsigctx->view);
   3191  1.19  christos 	isc_loop_detach(&chsigctx->loop);
   3192  1.19  christos 	isc_mem_put(msg->mctx, chsigctx, sizeof(*chsigctx));
   3193  1.19  christos 	dns_message_detach(&msg);
   3194  1.19  christos }
   3195  1.19  christos 
   3196  1.19  christos isc_result_t
   3197  1.19  christos dns_message_checksig_async(dns_message_t *msg, dns_view_t *view,
   3198  1.19  christos 			   isc_loop_t *loop, dns_message_cb_t cb, void *cbarg) {
   3199  1.19  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   3200  1.19  christos 	REQUIRE(view != NULL);
   3201  1.19  christos 	REQUIRE(loop != NULL);
   3202  1.19  christos 	REQUIRE(cb != NULL);
   3203  1.19  christos 
   3204  1.19  christos 	checksig_ctx_t *chsigctx = isc_mem_get(msg->mctx, sizeof(*chsigctx));
   3205  1.19  christos 	*chsigctx = (checksig_ctx_t){
   3206  1.19  christos 		.cb = cb,
   3207  1.19  christos 		.cbarg = cbarg,
   3208  1.19  christos 		.result = ISC_R_UNSET,
   3209  1.19  christos 		.loop = isc_loop_ref(loop),
   3210  1.19  christos 	};
   3211  1.19  christos 	dns_message_attach(msg, &chsigctx->msg);
   3212  1.19  christos 	dns_view_attach(view, &chsigctx->view);
   3213  1.19  christos 
   3214  1.19  christos 	dns_message_clonebuffer(msg);
   3215  1.19  christos 	isc_helper_run(loop, checksig_run, chsigctx);
   3216  1.19  christos 
   3217  1.19  christos 	return DNS_R_WAIT;
   3218  1.19  christos }
   3219  1.19  christos 
   3220   1.1  christos isc_result_t
   3221   1.1  christos dns_message_checksig(dns_message_t *msg, dns_view_t *view) {
   3222  1.19  christos 	isc_buffer_t b, msgb;
   3223   1.1  christos 
   3224   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   3225   1.1  christos 
   3226  1.19  christos 	if (msg->tsigkey == NULL && msg->tsig == NULL && msg->sig0 == NULL) {
   3227  1.19  christos 		return ISC_R_SUCCESS;
   3228   1.9  christos 	}
   3229   1.1  christos 
   3230   1.1  christos 	INSIST(msg->saved.base != NULL);
   3231   1.1  christos 	isc_buffer_init(&msgb, msg->saved.base, msg->saved.length);
   3232   1.1  christos 	isc_buffer_add(&msgb, msg->saved.length);
   3233  1.19  christos 	if (msg->tsigkey != NULL || msg->tsig != NULL) {
   3234   1.1  christos #ifdef SKAN_MSG_DEBUG
   3235  1.19  christos 		dns_message_dumpsig(msg, "dns_message_checksig#1");
   3236   1.9  christos #endif /* ifdef SKAN_MSG_DEBUG */
   3237  1.19  christos 		if (view != NULL) {
   3238  1.19  christos 			return dns_view_checksig(view, &msgb, msg);
   3239  1.19  christos 		} else {
   3240  1.19  christos 			return dns_tsig_verify(&msgb, msg, NULL, NULL);
   3241  1.19  christos 		}
   3242   1.1  christos 	} else {
   3243  1.19  christos 		dns_rdata_t rdata = DNS_RDATA_INIT;
   3244  1.19  christos 		dns_rdata_sig_t sig;
   3245  1.19  christos 		dns_rdataset_t keyset;
   3246  1.19  christos 		isc_result_t result;
   3247  1.20  christos 		uint32_t key_checks, message_checks;
   3248  1.19  christos 
   3249  1.19  christos 		result = dns_rdataset_first(msg->sig0);
   3250  1.19  christos 		INSIST(result == ISC_R_SUCCESS);
   3251  1.19  christos 		dns_rdataset_current(msg->sig0, &rdata);
   3252  1.19  christos 
   3253  1.19  christos 		/*
   3254  1.19  christos 		 * This can occur when the message is a dynamic update, since
   3255  1.19  christos 		 * the rdata length checking is relaxed.  This should not
   3256  1.19  christos 		 * happen in a well-formed message, since the SIG(0) is only
   3257  1.19  christos 		 * looked for in the additional section, and the dynamic update
   3258  1.19  christos 		 * meta-records are in the prerequisite and update sections.
   3259  1.19  christos 		 */
   3260  1.19  christos 		if (rdata.length == 0) {
   3261  1.19  christos 			return ISC_R_UNEXPECTEDEND;
   3262  1.19  christos 		}
   3263  1.19  christos 
   3264  1.19  christos 		result = dns_rdata_tostruct(&rdata, &sig, NULL);
   3265  1.19  christos 		if (result != ISC_R_SUCCESS) {
   3266  1.19  christos 			return result;
   3267  1.19  christos 		}
   3268  1.19  christos 
   3269  1.19  christos 		dns_rdataset_init(&keyset);
   3270  1.19  christos 		if (view == NULL) {
   3271  1.19  christos 			result = DNS_R_KEYUNAUTHORIZED;
   3272  1.19  christos 			goto freesig;
   3273  1.19  christos 		}
   3274  1.19  christos 		result = dns_view_simplefind(view, &sig.signer,
   3275  1.19  christos 					     dns_rdatatype_key /* SIG(0) */, 0,
   3276  1.19  christos 					     0, false, &keyset, NULL);
   3277  1.19  christos 
   3278  1.19  christos 		if (result != ISC_R_SUCCESS) {
   3279  1.19  christos 			result = DNS_R_KEYUNAUTHORIZED;
   3280  1.19  christos 			goto freesig;
   3281  1.19  christos 		} else if (keyset.trust < dns_trust_ultimate) {
   3282  1.19  christos 			result = DNS_R_KEYUNAUTHORIZED;
   3283  1.19  christos 			goto freesig;
   3284  1.19  christos 		}
   3285  1.19  christos 		result = dns_rdataset_first(&keyset);
   3286  1.19  christos 		INSIST(result == ISC_R_SUCCESS);
   3287  1.19  christos 
   3288  1.20  christos 		/*
   3289  1.20  christos 		 * In order to protect from a possible DoS attack, this function
   3290  1.20  christos 		 * supports limitations on how many keyid checks and how many
   3291  1.20  christos 		 * key checks (message verifications using a matched key) are
   3292  1.20  christos 		 * going to be allowed.
   3293  1.20  christos 		 */
   3294  1.20  christos 		const uint32_t max_key_checks =
   3295  1.20  christos 			view->sig0key_checks_limit > 0
   3296  1.20  christos 				? view->sig0key_checks_limit
   3297  1.20  christos 				: UINT32_MAX;
   3298  1.20  christos 		const uint32_t max_message_checks =
   3299  1.20  christos 			view->sig0message_checks_limit > 0
   3300  1.20  christos 				? view->sig0message_checks_limit
   3301  1.20  christos 				: UINT32_MAX;
   3302  1.20  christos 
   3303  1.20  christos 		for (key_checks = 0, message_checks = 0;
   3304  1.20  christos 		     result == ISC_R_SUCCESS && key_checks < max_key_checks &&
   3305  1.20  christos 		     message_checks < max_message_checks;
   3306  1.20  christos 		     key_checks++, result = dns_rdataset_next(&keyset))
   3307  1.19  christos 		{
   3308  1.19  christos 			dst_key_t *key = NULL;
   3309  1.19  christos 
   3310  1.19  christos 			dns_rdata_reset(&rdata);
   3311  1.19  christos 			dns_rdataset_current(&keyset, &rdata);
   3312  1.19  christos 			isc_buffer_init(&b, rdata.data, rdata.length);
   3313  1.19  christos 			isc_buffer_add(&b, rdata.length);
   3314  1.19  christos 
   3315  1.19  christos 			result = dst_key_fromdns(&sig.signer, rdata.rdclass, &b,
   3316  1.19  christos 						 view->mctx, &key);
   3317  1.19  christos 			if (result != ISC_R_SUCCESS) {
   3318  1.19  christos 				continue;
   3319  1.19  christos 			}
   3320  1.19  christos 			if (dst_key_alg(key) != sig.algorithm ||
   3321  1.19  christos 			    dst_key_id(key) != sig.keyid ||
   3322  1.19  christos 			    !(dst_key_proto(key) == DNS_KEYPROTO_DNSSEC ||
   3323  1.19  christos 			      dst_key_proto(key) == DNS_KEYPROTO_ANY))
   3324  1.19  christos 			{
   3325  1.19  christos 				dst_key_free(&key);
   3326  1.19  christos 				continue;
   3327  1.19  christos 			}
   3328  1.19  christos 			result = dns_dnssec_verifymessage(&msgb, msg, key);
   3329  1.19  christos 			dst_key_free(&key);
   3330  1.19  christos 			if (result == ISC_R_SUCCESS) {
   3331  1.19  christos 				break;
   3332  1.19  christos 			}
   3333  1.20  christos 			message_checks++;
   3334  1.19  christos 		}
   3335  1.20  christos 		if (result == ISC_R_NOMORE) {
   3336  1.20  christos 			result = DNS_R_KEYUNAUTHORIZED;
   3337  1.20  christos 		} else if (key_checks == max_key_checks) {
   3338  1.20  christos 			isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
   3339  1.20  christos 				      DNS_LOGMODULE_MESSAGE, ISC_LOG_DEBUG(3),
   3340  1.20  christos 				      "sig0key-checks-limit reached when "
   3341  1.20  christos 				      "trying to check a message signature");
   3342  1.20  christos 			result = DNS_R_KEYUNAUTHORIZED;
   3343  1.20  christos 		} else if (message_checks == max_message_checks) {
   3344  1.20  christos 			isc_log_write(dns_lctx, ISC_LOGCATEGORY_GENERAL,
   3345  1.20  christos 				      DNS_LOGMODULE_MESSAGE, ISC_LOG_DEBUG(3),
   3346  1.20  christos 				      "sig0message-checks-limit reached when "
   3347  1.20  christos 				      "trying to check a message signature");
   3348  1.19  christos 			result = DNS_R_KEYUNAUTHORIZED;
   3349  1.19  christos 		}
   3350  1.19  christos 
   3351  1.19  christos 	freesig:
   3352  1.19  christos 		if (dns_rdataset_isassociated(&keyset)) {
   3353  1.19  christos 			dns_rdataset_disassociate(&keyset);
   3354  1.19  christos 		}
   3355  1.19  christos 		dns_rdata_freestruct(&sig);
   3356  1.19  christos 		return result;
   3357   1.1  christos 	}
   3358   1.1  christos }
   3359   1.1  christos 
   3360   1.9  christos #define INDENT(sp)                                                           \
   3361   1.9  christos 	do {                                                                 \
   3362   1.9  christos 		unsigned int __i;                                            \
   3363   1.9  christos 		dns_masterstyle_flags_t __flags = dns_master_styleflags(sp); \
   3364   1.9  christos 		if ((__flags & DNS_STYLEFLAG_INDENT) == 0ULL &&              \
   3365   1.9  christos 		    (__flags & DNS_STYLEFLAG_YAML) == 0ULL)                  \
   3366  1.19  christos 		{                                                            \
   3367   1.9  christos 			break;                                               \
   3368  1.19  christos 		}                                                            \
   3369   1.9  christos 		for (__i = 0; __i < msg->indent.count; __i++) {              \
   3370   1.9  christos 			ADD_STRING(target, msg->indent.string);              \
   3371   1.9  christos 		}                                                            \
   3372  1.12    rillig 	} while (0)
   3373   1.1  christos 
   3374   1.1  christos isc_result_t
   3375   1.1  christos dns_message_sectiontotext(dns_message_t *msg, dns_section_t section,
   3376   1.1  christos 			  const dns_master_style_t *style,
   3377   1.9  christos 			  dns_messagetextflag_t flags, isc_buffer_t *target) {
   3378  1.19  christos 	dns_name_t empty_name;
   3379   1.1  christos 	isc_result_t result = ISC_R_SUCCESS;
   3380   1.3  christos 	bool seensoa = false;
   3381   1.9  christos 	size_t saved_count;
   3382   1.1  christos 	dns_masterstyle_flags_t sflags;
   3383   1.1  christos 
   3384   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   3385   1.1  christos 	REQUIRE(target != NULL);
   3386  1.19  christos 	REQUIRE(VALID_NAMED_SECTION(section));
   3387   1.1  christos 
   3388   1.9  christos 	saved_count = msg->indent.count;
   3389   1.9  christos 
   3390   1.9  christos 	if (ISC_LIST_EMPTY(msg->sections[section])) {
   3391   1.1  christos 		goto cleanup;
   3392   1.9  christos 	}
   3393   1.1  christos 
   3394   1.9  christos 	sflags = dns_master_styleflags(style);
   3395   1.1  christos 
   3396   1.1  christos 	INDENT(style);
   3397   1.1  christos 	if ((sflags & DNS_STYLEFLAG_YAML) != 0) {
   3398   1.1  christos 		if (msg->opcode != dns_opcode_update) {
   3399   1.1  christos 			ADD_STRING(target, sectiontext[section]);
   3400   1.1  christos 		} else {
   3401   1.1  christos 			ADD_STRING(target, updsectiontext[section]);
   3402   1.1  christos 		}
   3403   1.1  christos 		ADD_STRING(target, "_SECTION:\n");
   3404   1.1  christos 	} else if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) {
   3405   1.1  christos 		ADD_STRING(target, ";; ");
   3406   1.1  christos 		if (msg->opcode != dns_opcode_update) {
   3407   1.1  christos 			ADD_STRING(target, sectiontext[section]);
   3408   1.1  christos 		} else {
   3409   1.1  christos 			ADD_STRING(target, updsectiontext[section]);
   3410   1.1  christos 		}
   3411   1.1  christos 		ADD_STRING(target, " SECTION:\n");
   3412   1.1  christos 	}
   3413   1.1  christos 
   3414   1.1  christos 	dns_name_init(&empty_name, NULL);
   3415   1.1  christos 	result = dns_message_firstname(msg, section);
   3416   1.1  christos 	if (result != ISC_R_SUCCESS) {
   3417   1.1  christos 		goto cleanup;
   3418   1.1  christos 	}
   3419   1.1  christos 	if ((sflags & DNS_STYLEFLAG_YAML) != 0) {
   3420   1.9  christos 		msg->indent.count++;
   3421   1.1  christos 	}
   3422   1.1  christos 	do {
   3423  1.19  christos 		dns_name_t *name = NULL;
   3424   1.1  christos 		dns_message_currentname(msg, section, &name);
   3425  1.19  christos 
   3426  1.19  christos 		dns_rdataset_t *rds = NULL;
   3427  1.22  christos 		ISC_LIST_FOREACH(name->list, rds, link) {
   3428   1.1  christos 			if (section == DNS_SECTION_ANSWER &&
   3429  1.19  christos 			    rds->type == dns_rdatatype_soa)
   3430  1.15  christos 			{
   3431   1.1  christos 				if ((flags & DNS_MESSAGETEXTFLAG_OMITSOA) != 0)
   3432   1.9  christos 				{
   3433   1.1  christos 					continue;
   3434   1.9  christos 				}
   3435   1.1  christos 				if (seensoa &&
   3436  1.15  christos 				    (flags & DNS_MESSAGETEXTFLAG_ONESOA) != 0)
   3437  1.15  christos 				{
   3438   1.1  christos 					continue;
   3439   1.9  christos 				}
   3440   1.3  christos 				seensoa = true;
   3441   1.1  christos 			}
   3442   1.1  christos 			if (section == DNS_SECTION_QUESTION) {
   3443   1.1  christos 				INDENT(style);
   3444  1.18  christos 				if ((sflags & DNS_STYLEFLAG_YAML) == 0) {
   3445   1.1  christos 					ADD_STRING(target, ";");
   3446   1.1  christos 				}
   3447   1.9  christos 				result = dns_master_questiontotext(
   3448  1.19  christos 					name, rds, style, target);
   3449   1.1  christos 			} else {
   3450   1.9  christos 				result = dns_master_rdatasettotext(
   3451  1.19  christos 					name, rds, style, &msg->indent, target);
   3452   1.1  christos 			}
   3453   1.9  christos 			if (result != ISC_R_SUCCESS) {
   3454   1.1  christos 				goto cleanup;
   3455   1.9  christos 			}
   3456   1.1  christos 		}
   3457   1.1  christos 		result = dns_message_nextname(msg, section);
   3458   1.1  christos 	} while (result == ISC_R_SUCCESS);
   3459   1.1  christos 	if ((sflags & DNS_STYLEFLAG_YAML) != 0) {
   3460   1.9  christos 		msg->indent.count--;
   3461   1.1  christos 	}
   3462   1.1  christos 	if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0 &&
   3463   1.1  christos 	    (flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0 &&
   3464   1.1  christos 	    (sflags & DNS_STYLEFLAG_YAML) == 0)
   3465   1.1  christos 	{
   3466   1.1  christos 		INDENT(style);
   3467   1.1  christos 		ADD_STRING(target, "\n");
   3468   1.1  christos 	}
   3469   1.9  christos 	if (result == ISC_R_NOMORE) {
   3470   1.1  christos 		result = ISC_R_SUCCESS;
   3471   1.9  christos 	}
   3472   1.1  christos 
   3473   1.9  christos cleanup:
   3474   1.9  christos 	msg->indent.count = saved_count;
   3475  1.19  christos 	return result;
   3476   1.1  christos }
   3477   1.1  christos 
   3478   1.1  christos static isc_result_t
   3479   1.1  christos render_ecs(isc_buffer_t *ecsbuf, isc_buffer_t *target) {
   3480   1.1  christos 	int i;
   3481  1.19  christos 	char addr[16] = { 0 }, addr_text[64];
   3482   1.3  christos 	uint16_t family;
   3483   1.3  christos 	uint8_t addrlen, addrbytes, scopelen;
   3484   1.1  christos 	isc_result_t result;
   3485   1.1  christos 
   3486   1.1  christos 	/*
   3487   1.1  christos 	 * Note: This routine needs to handle malformed ECS options.
   3488   1.1  christos 	 */
   3489   1.1  christos 
   3490   1.9  christos 	if (isc_buffer_remaininglength(ecsbuf) < 4) {
   3491  1.19  christos 		return DNS_R_OPTERR;
   3492   1.9  christos 	}
   3493   1.1  christos 	family = isc_buffer_getuint16(ecsbuf);
   3494   1.1  christos 	addrlen = isc_buffer_getuint8(ecsbuf);
   3495   1.1  christos 	scopelen = isc_buffer_getuint8(ecsbuf);
   3496   1.1  christos 
   3497   1.1  christos 	addrbytes = (addrlen + 7) / 8;
   3498   1.9  christos 	if (isc_buffer_remaininglength(ecsbuf) < addrbytes) {
   3499  1.19  christos 		return DNS_R_OPTERR;
   3500   1.9  christos 	}
   3501   1.1  christos 
   3502   1.9  christos 	if (addrbytes > sizeof(addr)) {
   3503  1.19  christos 		return DNS_R_OPTERR;
   3504   1.9  christos 	}
   3505   1.1  christos 
   3506   1.9  christos 	for (i = 0; i < addrbytes; i++) {
   3507   1.1  christos 		addr[i] = isc_buffer_getuint8(ecsbuf);
   3508   1.9  christos 	}
   3509   1.1  christos 
   3510   1.1  christos 	switch (family) {
   3511   1.1  christos 	case 0:
   3512   1.9  christos 		if (addrlen != 0U || scopelen != 0U) {
   3513  1.19  christos 			return DNS_R_OPTERR;
   3514   1.9  christos 		}
   3515   1.1  christos 		strlcpy(addr_text, "0", sizeof(addr_text));
   3516   1.1  christos 		break;
   3517   1.1  christos 	case 1:
   3518   1.9  christos 		if (addrlen > 32 || scopelen > 32) {
   3519  1.19  christos 			return DNS_R_OPTERR;
   3520   1.9  christos 		}
   3521   1.1  christos 		inet_ntop(AF_INET, addr, addr_text, sizeof(addr_text));
   3522   1.1  christos 		break;
   3523   1.1  christos 	case 2:
   3524   1.9  christos 		if (addrlen > 128 || scopelen > 128) {
   3525  1.19  christos 			return DNS_R_OPTERR;
   3526   1.9  christos 		}
   3527   1.1  christos 		inet_ntop(AF_INET6, addr, addr_text, sizeof(addr_text));
   3528   1.1  christos 		break;
   3529   1.1  christos 	default:
   3530  1.19  christos 		return DNS_R_OPTERR;
   3531   1.1  christos 	}
   3532   1.1  christos 
   3533   1.9  christos 	ADD_STRING(target, " ");
   3534   1.1  christos 	ADD_STRING(target, addr_text);
   3535   1.1  christos 	snprintf(addr_text, sizeof(addr_text), "/%d/%d", addrlen, scopelen);
   3536   1.1  christos 	ADD_STRING(target, addr_text);
   3537   1.1  christos 
   3538   1.1  christos 	result = ISC_R_SUCCESS;
   3539   1.1  christos 
   3540   1.9  christos cleanup:
   3541  1.19  christos 	return result;
   3542   1.1  christos }
   3543   1.1  christos 
   3544   1.7  christos static isc_result_t
   3545  1.20  christos render_llq(isc_buffer_t *optbuf, dns_message_t *msg,
   3546  1.20  christos 	   const dns_master_style_t *style, isc_buffer_t *target) {
   3547   1.7  christos 	char buf[sizeof("18446744073709551615")]; /* 2^64-1 */
   3548   1.7  christos 	isc_result_t result = ISC_R_SUCCESS;
   3549   1.7  christos 	uint32_t u;
   3550   1.7  christos 	uint64_t q;
   3551  1.20  christos 	const char *sep1 = " ", *sep2 = ", ";
   3552  1.20  christos 	size_t count = msg->indent.count;
   3553  1.20  christos 	bool yaml = false;
   3554  1.20  christos 
   3555  1.20  christos 	if ((dns_master_styleflags(style) & DNS_STYLEFLAG_YAML) != 0) {
   3556  1.20  christos 		sep1 = sep2 = "\n";
   3557  1.20  christos 		msg->indent.count++;
   3558  1.20  christos 		yaml = true;
   3559  1.20  christos 	}
   3560   1.7  christos 
   3561   1.7  christos 	u = isc_buffer_getuint16(optbuf);
   3562  1.20  christos 	ADD_STRING(target, sep1);
   3563  1.20  christos 	INDENT(style);
   3564  1.20  christos 	if (yaml) {
   3565  1.20  christos 		ADD_STRING(target, "LLQ-VERSION: ");
   3566  1.20  christos 	} else {
   3567  1.20  christos 		ADD_STRING(target, "Version: ");
   3568  1.20  christos 	}
   3569   1.7  christos 	snprintf(buf, sizeof(buf), "%u", u);
   3570   1.7  christos 	ADD_STRING(target, buf);
   3571   1.7  christos 
   3572   1.7  christos 	u = isc_buffer_getuint16(optbuf);
   3573  1.20  christos 	ADD_STRING(target, sep2);
   3574  1.20  christos 	INDENT(style);
   3575  1.20  christos 	if (yaml) {
   3576  1.20  christos 		ADD_STRING(target, "LLQ-OPCODE: ");
   3577  1.20  christos 	} else {
   3578  1.20  christos 		ADD_STRING(target, "Opcode: ");
   3579  1.20  christos 	}
   3580   1.7  christos 	snprintf(buf, sizeof(buf), "%u", u);
   3581   1.7  christos 	ADD_STRING(target, buf);
   3582   1.7  christos 
   3583   1.7  christos 	u = isc_buffer_getuint16(optbuf);
   3584  1.20  christos 	ADD_STRING(target, sep2);
   3585  1.20  christos 	INDENT(style);
   3586  1.20  christos 	if (yaml) {
   3587  1.20  christos 		ADD_STRING(target, "LLQ-ERROR: ");
   3588  1.20  christos 	} else {
   3589  1.20  christos 		ADD_STRING(target, "Error: ");
   3590  1.20  christos 	}
   3591   1.7  christos 	snprintf(buf, sizeof(buf), "%u", u);
   3592   1.7  christos 	ADD_STRING(target, buf);
   3593   1.7  christos 
   3594   1.7  christos 	q = isc_buffer_getuint32(optbuf);
   3595   1.7  christos 	q <<= 32;
   3596   1.7  christos 	q |= isc_buffer_getuint32(optbuf);
   3597  1.20  christos 	ADD_STRING(target, sep2);
   3598  1.20  christos 	INDENT(style);
   3599  1.20  christos 	if (yaml) {
   3600  1.20  christos 		ADD_STRING(target, "LLQ-ID: ");
   3601  1.20  christos 	} else {
   3602  1.20  christos 		ADD_STRING(target, "Identifier: ");
   3603  1.20  christos 	}
   3604   1.7  christos 	snprintf(buf, sizeof(buf), "%" PRIu64, q);
   3605   1.7  christos 	ADD_STRING(target, buf);
   3606   1.7  christos 
   3607   1.7  christos 	u = isc_buffer_getuint32(optbuf);
   3608  1.20  christos 	ADD_STRING(target, sep2);
   3609  1.20  christos 	INDENT(style);
   3610  1.20  christos 	if (yaml) {
   3611  1.20  christos 		ADD_STRING(target, "LLQ-LEASE: ");
   3612  1.20  christos 	} else {
   3613  1.20  christos 		ADD_STRING(target, "Lifetime: ");
   3614  1.20  christos 	}
   3615   1.7  christos 	snprintf(buf, sizeof(buf), "%u", u);
   3616   1.7  christos 	ADD_STRING(target, buf);
   3617  1.20  christos 
   3618   1.9  christos cleanup:
   3619  1.20  christos 	msg->indent.count = count;
   3620  1.19  christos 	return result;
   3621   1.7  christos }
   3622   1.1  christos 
   3623   1.1  christos static isc_result_t
   3624  1.20  christos put_yamlstr(isc_buffer_t *target, unsigned char *namebuf, size_t len,
   3625  1.20  christos 	    bool utfok) {
   3626  1.20  christos 	isc_result_t result = ISC_R_SUCCESS;
   3627  1.20  christos 
   3628  1.20  christos 	for (size_t i = 0; i < len; i++) {
   3629  1.20  christos 		if (isprint(namebuf[i]) || (utfok && namebuf[i] > 127)) {
   3630  1.20  christos 			if (namebuf[i] == '\\' || namebuf[i] == '"') {
   3631  1.20  christos 				ADD_STRING(target, "\\");
   3632  1.20  christos 			}
   3633  1.20  christos 			if (isc_buffer_availablelength(target) < 1) {
   3634  1.20  christos 				return ISC_R_NOSPACE;
   3635  1.20  christos 			}
   3636  1.20  christos 			isc_buffer_putmem(target, &namebuf[i], 1);
   3637  1.20  christos 		} else {
   3638  1.20  christos 			ADD_STRING(target, ".");
   3639  1.20  christos 		}
   3640  1.20  christos 	}
   3641  1.20  christos cleanup:
   3642  1.20  christos 	return result;
   3643  1.20  christos }
   3644  1.20  christos 
   3645  1.20  christos static isc_result_t
   3646  1.20  christos render_nameopt(isc_buffer_t *optbuf, bool yaml, isc_buffer_t *target) {
   3647  1.20  christos 	dns_decompress_t dctx = DNS_DECOMPRESS_NEVER;
   3648  1.20  christos 	dns_fixedname_t fixed;
   3649  1.20  christos 	dns_name_t *name = dns_fixedname_initname(&fixed);
   3650  1.20  christos 	char namebuf[DNS_NAME_FORMATSIZE];
   3651  1.20  christos 	isc_result_t result;
   3652  1.20  christos 
   3653  1.20  christos 	result = dns_name_fromwire(name, optbuf, dctx, NULL);
   3654  1.20  christos 	if (result == ISC_R_SUCCESS && isc_buffer_activelength(optbuf) == 0) {
   3655  1.20  christos 		dns_name_format(name, namebuf, sizeof(namebuf));
   3656  1.20  christos 		ADD_STRING(target, " \"");
   3657  1.20  christos 		if (yaml) {
   3658  1.20  christos 			PUT_YAMLSTR(target, (unsigned char *)namebuf,
   3659  1.20  christos 				    strlen(namebuf), false);
   3660  1.20  christos 		} else {
   3661  1.20  christos 			ADD_STRING(target, namebuf);
   3662  1.20  christos 		}
   3663  1.20  christos 		ADD_STRING(target, "\"");
   3664  1.20  christos 		return result;
   3665  1.20  christos 	}
   3666  1.20  christos 	result = ISC_R_FAILURE;
   3667  1.20  christos cleanup:
   3668  1.20  christos 	return result;
   3669  1.20  christos }
   3670  1.20  christos 
   3671  1.20  christos static const char *option_names[] = {
   3672  1.20  christos 	[DNS_OPT_LLQ] = "LLQ",
   3673  1.20  christos 	[DNS_OPT_UL] = "UPDATE-LEASE",
   3674  1.20  christos 	[DNS_OPT_NSID] = "NSID",
   3675  1.20  christos 	[DNS_OPT_DAU] = "DAU",
   3676  1.20  christos 	[DNS_OPT_DHU] = "DHU",
   3677  1.20  christos 	[DNS_OPT_N3U] = "N3U",
   3678  1.20  christos 	[DNS_OPT_CLIENT_SUBNET] = "CLIENT-SUBNET",
   3679  1.20  christos 	[DNS_OPT_EXPIRE] = "EXPIRE",
   3680  1.20  christos 	[DNS_OPT_COOKIE] = "COOKIE",
   3681  1.20  christos 	[DNS_OPT_TCP_KEEPALIVE] = "TCP-KEEPALIVE",
   3682  1.20  christos 	[DNS_OPT_PAD] = "PADDING",
   3683  1.20  christos 	[DNS_OPT_CHAIN] = "CHAIN",
   3684  1.20  christos 	[DNS_OPT_KEY_TAG] = "KEY-TAG",
   3685  1.20  christos 	[DNS_OPT_EDE] = "EDE",
   3686  1.20  christos 	[DNS_OPT_CLIENT_TAG] = "CLIENT-TAG",
   3687  1.20  christos 	[DNS_OPT_SERVER_TAG] = "SERVER-TAG",
   3688  1.20  christos 	[DNS_OPT_REPORT_CHANNEL] = "Report-Channel",
   3689  1.20  christos 	[DNS_OPT_ZONEVERSION] = "ZONEVERSION",
   3690  1.20  christos };
   3691  1.20  christos 
   3692  1.20  christos static isc_result_t
   3693   1.9  christos dns_message_pseudosectiontoyaml(dns_message_t *msg, dns_pseudosection_t section,
   3694   1.1  christos 				const dns_master_style_t *style,
   3695   1.1  christos 				dns_messagetextflag_t flags,
   3696   1.9  christos 				isc_buffer_t *target) {
   3697   1.1  christos 	dns_rdataset_t *ps = NULL;
   3698   1.1  christos 	const dns_name_t *name = NULL;
   3699   1.1  christos 	isc_result_t result = ISC_R_SUCCESS;
   3700  1.19  christos 	char buf[sizeof("/1234567890")];
   3701   1.3  christos 	uint32_t mbz;
   3702   1.1  christos 	dns_rdata_t rdata;
   3703   1.1  christos 	isc_buffer_t optbuf;
   3704   1.3  christos 	uint16_t optcode, optlen;
   3705   1.9  christos 	size_t saved_count;
   3706  1.20  christos 	unsigned char *optdata = NULL;
   3707  1.10  christos 	unsigned int indent;
   3708  1.20  christos 	isc_buffer_t ecsbuf;
   3709   1.1  christos 
   3710   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   3711   1.1  christos 	REQUIRE(target != NULL);
   3712  1.19  christos 	REQUIRE(VALID_NAMED_PSEUDOSECTION(section));
   3713   1.1  christos 
   3714   1.9  christos 	saved_count = msg->indent.count;
   3715   1.9  christos 
   3716   1.1  christos 	switch (section) {
   3717   1.1  christos 	case DNS_PSEUDOSECTION_OPT:
   3718   1.1  christos 		ps = dns_message_getopt(msg);
   3719   1.1  christos 		if (ps == NULL) {
   3720   1.1  christos 			goto cleanup;
   3721   1.1  christos 		}
   3722   1.1  christos 
   3723   1.1  christos 		INDENT(style);
   3724   1.1  christos 		ADD_STRING(target, "OPT_PSEUDOSECTION:\n");
   3725   1.9  christos 		msg->indent.count++;
   3726   1.1  christos 
   3727   1.1  christos 		INDENT(style);
   3728   1.1  christos 		ADD_STRING(target, "EDNS:\n");
   3729  1.10  christos 		indent = ++msg->indent.count;
   3730   1.1  christos 
   3731   1.1  christos 		INDENT(style);
   3732   1.1  christos 		ADD_STRING(target, "version: ");
   3733   1.1  christos 		snprintf(buf, sizeof(buf), "%u",
   3734   1.1  christos 			 (unsigned int)((ps->ttl & 0x00ff0000) >> 16));
   3735   1.1  christos 		ADD_STRING(target, buf);
   3736   1.1  christos 		ADD_STRING(target, "\n");
   3737   1.1  christos 		INDENT(style);
   3738   1.1  christos 		ADD_STRING(target, "flags:");
   3739   1.9  christos 		if ((ps->ttl & DNS_MESSAGEEXTFLAG_DO) != 0) {
   3740   1.1  christos 			ADD_STRING(target, " do");
   3741   1.9  christos 		}
   3742  1.21  christos 		if ((ps->ttl & DNS_MESSAGEEXTFLAG_CO) != 0) {
   3743  1.21  christos 			ADD_STRING(target, " co");
   3744  1.21  christos 		}
   3745   1.1  christos 		ADD_STRING(target, "\n");
   3746   1.1  christos 		mbz = ps->ttl & 0xffff;
   3747  1.21  christos 		/* Exclude Known Flags. */
   3748  1.21  christos 		mbz &= ~(DNS_MESSAGEEXTFLAG_DO | DNS_MESSAGEEXTFLAG_CO);
   3749   1.1  christos 		if (mbz != 0) {
   3750   1.1  christos 			INDENT(style);
   3751   1.1  christos 			ADD_STRING(target, "MBZ: ");
   3752   1.1  christos 			snprintf(buf, sizeof(buf), "0x%.4x", mbz);
   3753   1.1  christos 			ADD_STRING(target, buf);
   3754   1.1  christos 			ADD_STRING(target, "\n");
   3755   1.1  christos 		}
   3756   1.1  christos 		INDENT(style);
   3757   1.1  christos 		ADD_STRING(target, "udp: ");
   3758   1.1  christos 		snprintf(buf, sizeof(buf), "%u\n", (unsigned int)ps->rdclass);
   3759   1.1  christos 		ADD_STRING(target, buf);
   3760   1.1  christos 		result = dns_rdataset_first(ps);
   3761   1.1  christos 		if (result != ISC_R_SUCCESS) {
   3762   1.1  christos 			result = ISC_R_SUCCESS;
   3763   1.1  christos 			goto cleanup;
   3764   1.1  christos 		}
   3765   1.1  christos 
   3766   1.1  christos 		/*
   3767   1.1  christos 		 * Print EDNS info, if any.
   3768   1.1  christos 		 *
   3769   1.1  christos 		 * WARNING: The option contents may be malformed as
   3770  1.10  christos 		 * dig +ednsopt=value:<content> does not perform validity
   3771   1.1  christos 		 * checking.
   3772   1.1  christos 		 */
   3773   1.1  christos 		dns_rdata_init(&rdata);
   3774   1.1  christos 		dns_rdataset_current(ps, &rdata);
   3775   1.1  christos 
   3776   1.1  christos 		isc_buffer_init(&optbuf, rdata.data, rdata.length);
   3777   1.1  christos 		isc_buffer_add(&optbuf, rdata.length);
   3778   1.1  christos 		while (isc_buffer_remaininglength(&optbuf) != 0) {
   3779  1.10  christos 			bool extra_text = false;
   3780  1.20  christos 			const char *option_name = NULL;
   3781  1.20  christos 
   3782  1.10  christos 			msg->indent.count = indent;
   3783   1.1  christos 			INSIST(isc_buffer_remaininglength(&optbuf) >= 4U);
   3784   1.1  christos 			optcode = isc_buffer_getuint16(&optbuf);
   3785   1.1  christos 			optlen = isc_buffer_getuint16(&optbuf);
   3786   1.1  christos 			INSIST(isc_buffer_remaininglength(&optbuf) >= optlen);
   3787   1.1  christos 
   3788  1.20  christos 			INDENT(style);
   3789  1.20  christos 			if (optcode < ARRAY_SIZE(option_names)) {
   3790  1.20  christos 				option_name = option_names[optcode];
   3791  1.20  christos 			}
   3792  1.20  christos 			if (option_name != NULL) {
   3793  1.20  christos 				ADD_STRING(target, option_names[optcode])
   3794  1.20  christos 			} else {
   3795  1.20  christos 				snprintf(buf, sizeof(buf), "OPT=%u", optcode);
   3796  1.20  christos 				ADD_STRING(target, buf);
   3797  1.20  christos 			}
   3798  1.20  christos 			ADD_STRING(target, ":");
   3799  1.20  christos 
   3800  1.20  christos 			switch (optcode) {
   3801  1.20  christos 			case DNS_OPT_LLQ:
   3802   1.7  christos 				if (optlen == 18U) {
   3803  1.20  christos 					result = render_llq(&optbuf, msg, style,
   3804  1.20  christos 							    target);
   3805   1.7  christos 					if (result != ISC_R_SUCCESS) {
   3806   1.7  christos 						goto cleanup;
   3807   1.7  christos 					}
   3808   1.7  christos 					ADD_STRING(target, "\n");
   3809   1.7  christos 					continue;
   3810   1.7  christos 				}
   3811  1.20  christos 				break;
   3812  1.20  christos 			case DNS_OPT_UL:
   3813  1.19  christos 				if (optlen == 4U || optlen == 8U) {
   3814  1.19  christos 					uint32_t secs, key = 0;
   3815  1.20  christos 					msg->indent.count++;
   3816  1.20  christos 
   3817  1.19  christos 					secs = isc_buffer_getuint32(&optbuf);
   3818  1.20  christos 					ADD_STRING(target, "\n");
   3819  1.20  christos 					INDENT(style);
   3820  1.20  christos 					ADD_STRING(target, "LEASE:");
   3821  1.19  christos 					snprintf(buf, sizeof(buf), " %u", secs);
   3822  1.19  christos 					ADD_STRING(target, buf);
   3823  1.20  christos 
   3824  1.20  christos 					ADD_STRING(target, " # ");
   3825  1.19  christos 					result = dns_ttl_totext(secs, true,
   3826  1.19  christos 								true, target);
   3827  1.19  christos 					if (result != ISC_R_SUCCESS) {
   3828  1.19  christos 						goto cleanup;
   3829  1.19  christos 					}
   3830  1.20  christos 					ADD_STRING(target, "\n");
   3831  1.20  christos 
   3832  1.19  christos 					if (optlen == 8U) {
   3833  1.20  christos 						key = isc_buffer_getuint32(
   3834  1.20  christos 							&optbuf);
   3835  1.20  christos 						INDENT(style);
   3836  1.20  christos 						ADD_STRING(target,
   3837  1.20  christos 							   "KEY-LEASE:");
   3838  1.20  christos 						snprintf(buf, sizeof(buf),
   3839  1.20  christos 							 " %u", key);
   3840  1.20  christos 						ADD_STRING(target, buf);
   3841  1.20  christos 
   3842  1.20  christos 						ADD_STRING(target, " # ");
   3843  1.19  christos 						result = dns_ttl_totext(
   3844  1.19  christos 							key, true, true,
   3845  1.19  christos 							target);
   3846  1.19  christos 						if (result != ISC_R_SUCCESS) {
   3847  1.19  christos 							goto cleanup;
   3848  1.19  christos 						}
   3849  1.20  christos 						ADD_STRING(target, "\n");
   3850  1.19  christos 					}
   3851  1.19  christos 					continue;
   3852  1.19  christos 				}
   3853  1.20  christos 				break;
   3854  1.20  christos 			case DNS_OPT_CLIENT_SUBNET:
   3855   1.1  christos 				isc_buffer_init(&ecsbuf,
   3856   1.1  christos 						isc_buffer_current(&optbuf),
   3857   1.1  christos 						optlen);
   3858   1.1  christos 				isc_buffer_add(&ecsbuf, optlen);
   3859   1.1  christos 				result = render_ecs(&ecsbuf, target);
   3860   1.9  christos 				if (result == ISC_R_NOSPACE) {
   3861   1.1  christos 					goto cleanup;
   3862   1.9  christos 				}
   3863   1.1  christos 				if (result == ISC_R_SUCCESS) {
   3864   1.1  christos 					isc_buffer_forward(&optbuf, optlen);
   3865   1.1  christos 					ADD_STRING(target, "\n");
   3866   1.1  christos 					continue;
   3867   1.1  christos 				}
   3868   1.1  christos 				ADD_STRING(target, "\n");
   3869  1.20  christos 				break;
   3870  1.20  christos 			case DNS_OPT_EXPIRE:
   3871   1.1  christos 				if (optlen == 4) {
   3872   1.3  christos 					uint32_t secs;
   3873   1.1  christos 					secs = isc_buffer_getuint32(&optbuf);
   3874   1.9  christos 					snprintf(buf, sizeof(buf), " %u", secs);
   3875   1.1  christos 					ADD_STRING(target, buf);
   3876  1.20  christos 					ADD_STRING(target, " # ");
   3877   1.9  christos 					result = dns_ttl_totext(secs, true,
   3878   1.9  christos 								true, target);
   3879   1.9  christos 					if (result != ISC_R_SUCCESS) {
   3880   1.1  christos 						goto cleanup;
   3881   1.9  christos 					}
   3882  1.20  christos 					ADD_STRING(target, "\n");
   3883   1.1  christos 					continue;
   3884   1.1  christos 				}
   3885  1.20  christos 				break;
   3886  1.20  christos 			case DNS_OPT_TCP_KEEPALIVE:
   3887   1.9  christos 				if (optlen == 2) {
   3888   1.9  christos 					unsigned int dsecs;
   3889   1.9  christos 					dsecs = isc_buffer_getuint16(&optbuf);
   3890  1.20  christos 					snprintf(buf, sizeof(buf), " %u.%u",
   3891   1.9  christos 						 dsecs / 10U, dsecs % 10U);
   3892   1.9  christos 					ADD_STRING(target, buf);
   3893   1.9  christos 					ADD_STRING(target, " secs\n");
   3894   1.9  christos 					continue;
   3895   1.9  christos 				}
   3896  1.20  christos 				break;
   3897  1.20  christos 			case DNS_OPT_CHAIN:
   3898  1.20  christos 			case DNS_OPT_REPORT_CHANNEL:
   3899  1.20  christos 				if (optlen > 0U) {
   3900  1.20  christos 					isc_buffer_t sb = optbuf;
   3901  1.20  christos 					isc_buffer_setactive(&optbuf, optlen);
   3902  1.20  christos 					result = render_nameopt(&optbuf, true,
   3903  1.20  christos 								target);
   3904  1.20  christos 					if (result == ISC_R_SUCCESS) {
   3905  1.20  christos 						ADD_STRING(target, "\n");
   3906  1.20  christos 						continue;
   3907  1.20  christos 					}
   3908  1.20  christos 					optbuf = sb;
   3909  1.20  christos 				}
   3910  1.20  christos 				break;
   3911  1.20  christos 			case DNS_OPT_KEY_TAG:
   3912   1.1  christos 				if (optlen > 0U && (optlen % 2U) == 0U) {
   3913  1.20  christos 					const char *sep = " [";
   3914   1.1  christos 					while (optlen > 0U) {
   3915  1.20  christos 						uint16_t id =
   3916  1.20  christos 							isc_buffer_getuint16(
   3917  1.20  christos 								&optbuf);
   3918   1.9  christos 						snprintf(buf, sizeof(buf),
   3919   1.9  christos 							 "%s %u", sep, id);
   3920   1.9  christos 						ADD_STRING(target, buf);
   3921   1.9  christos 						sep = ",";
   3922   1.9  christos 						optlen -= 2;
   3923   1.1  christos 					}
   3924  1.20  christos 					ADD_STRING(target, " ]\n");
   3925   1.1  christos 					continue;
   3926   1.1  christos 				}
   3927  1.20  christos 				break;
   3928  1.20  christos 			case DNS_OPT_EDE:
   3929  1.10  christos 				if (optlen >= 2U) {
   3930  1.10  christos 					uint16_t ede;
   3931  1.10  christos 					ADD_STRING(target, "\n");
   3932  1.10  christos 					msg->indent.count++;
   3933  1.10  christos 					INDENT(style);
   3934  1.10  christos 					ADD_STRING(target, "INFO-CODE:");
   3935  1.10  christos 					ede = isc_buffer_getuint16(&optbuf);
   3936  1.10  christos 					snprintf(buf, sizeof(buf), " %u", ede);
   3937  1.10  christos 					ADD_STRING(target, buf);
   3938  1.10  christos 					if (ede < ARRAY_SIZE(edetext)) {
   3939  1.10  christos 						ADD_STRING(target, " (");
   3940  1.10  christos 						ADD_STRING(target,
   3941  1.10  christos 							   edetext[ede]);
   3942  1.10  christos 						ADD_STRING(target, ")");
   3943  1.10  christos 					}
   3944  1.10  christos 					ADD_STRING(target, "\n");
   3945  1.10  christos 					optlen -= 2;
   3946  1.10  christos 					if (optlen != 0) {
   3947  1.10  christos 						INDENT(style);
   3948  1.10  christos 						ADD_STRING(target,
   3949  1.10  christos 							   "EXTRA-TEXT:");
   3950  1.10  christos 						extra_text = true;
   3951  1.10  christos 					}
   3952  1.10  christos 				}
   3953  1.20  christos 				break;
   3954  1.20  christos 			case DNS_OPT_CLIENT_TAG:
   3955  1.20  christos 			case DNS_OPT_SERVER_TAG:
   3956   1.6  christos 				if (optlen == 2U) {
   3957  1.20  christos 					uint16_t id =
   3958  1.20  christos 						isc_buffer_getuint16(&optbuf);
   3959   1.9  christos 					snprintf(buf, sizeof(buf), " %u\n", id);
   3960   1.6  christos 					ADD_STRING(target, buf);
   3961   1.6  christos 					continue;
   3962   1.6  christos 				}
   3963  1.20  christos 				break;
   3964  1.20  christos 			case DNS_OPT_COOKIE:
   3965  1.20  christos 				if (optlen == 8 ||
   3966  1.20  christos 				    (optlen >= 16 && optlen < 40))
   3967  1.20  christos 				{
   3968  1.20  christos 					size_t i;
   3969  1.20  christos 
   3970  1.20  christos 					msg->indent.count++;
   3971  1.20  christos 					optdata = isc_buffer_current(&optbuf);
   3972  1.20  christos 
   3973  1.20  christos 					ADD_STRING(target, "\n");
   3974  1.20  christos 					INDENT(style);
   3975  1.20  christos 					ADD_STRING(target, "CLIENT: ");
   3976  1.20  christos 					for (i = 0; i < 8; i++) {
   3977  1.20  christos 						snprintf(buf, sizeof(buf),
   3978  1.20  christos 							 "%02x", optdata[i]);
   3979  1.20  christos 						ADD_STRING(target, buf);
   3980  1.20  christos 					}
   3981  1.20  christos 					ADD_STRING(target, "\n");
   3982  1.20  christos 
   3983  1.20  christos 					if (optlen >= 16) {
   3984  1.20  christos 						INDENT(style);
   3985  1.20  christos 						ADD_STRING(target, "SERVER: ");
   3986  1.20  christos 						for (; i < optlen; i++) {
   3987  1.20  christos 							snprintf(buf,
   3988  1.20  christos 								 sizeof(buf),
   3989  1.20  christos 								 "%02x",
   3990  1.20  christos 								 optdata[i]);
   3991  1.20  christos 							ADD_STRING(target, buf);
   3992  1.20  christos 						}
   3993  1.20  christos 						ADD_STRING(target, "\n");
   3994  1.20  christos 					}
   3995  1.20  christos 
   3996  1.20  christos 					/*
   3997  1.20  christos 					 * Valid server cookie?
   3998  1.20  christos 					 */
   3999  1.20  christos 					if (msg->cc_ok && optlen >= 16) {
   4000  1.20  christos 						INDENT(style);
   4001  1.20  christos 						ADD_STRING(target,
   4002  1.20  christos 							   "STATUS: good\n");
   4003  1.20  christos 					}
   4004  1.20  christos 					/*
   4005  1.20  christos 					 * Server cookie is not valid but
   4006  1.20  christos 					 * we had our cookie echoed back.
   4007  1.20  christos 					 */
   4008  1.20  christos 					if (msg->cc_ok && optlen < 16) {
   4009  1.20  christos 						INDENT(style);
   4010  1.20  christos 						ADD_STRING(target,
   4011  1.20  christos 							   "STATUS: echoed\n");
   4012  1.20  christos 					}
   4013  1.20  christos 					/*
   4014  1.20  christos 					 * We didn't get our cookie echoed
   4015  1.20  christos 					 * back.
   4016  1.20  christos 					 */
   4017  1.20  christos 					if (msg->cc_bad) {
   4018  1.20  christos 						INDENT(style);
   4019  1.20  christos 						ADD_STRING(target,
   4020  1.20  christos 							   "STATUS: bad\n)");
   4021  1.20  christos 					}
   4022  1.20  christos 					isc_buffer_forward(&optbuf, optlen);
   4023   1.6  christos 					continue;
   4024   1.6  christos 				}
   4025  1.20  christos 				break;
   4026  1.20  christos 			default:
   4027  1.20  christos 				break;
   4028   1.1  christos 			}
   4029   1.1  christos 
   4030   1.1  christos 			if (optlen != 0) {
   4031   1.1  christos 				int i;
   4032  1.10  christos 				bool utf8ok = false;
   4033   1.9  christos 
   4034   1.9  christos 				ADD_STRING(target, " ");
   4035   1.1  christos 
   4036   1.1  christos 				optdata = isc_buffer_current(&optbuf);
   4037  1.10  christos 				if (extra_text) {
   4038  1.10  christos 					utf8ok = isc_utf8_valid(optdata,
   4039  1.10  christos 								optlen);
   4040  1.10  christos 				}
   4041  1.10  christos 				if (!utf8ok) {
   4042  1.10  christos 					for (i = 0; i < optlen; i++) {
   4043  1.10  christos 						const char *sep;
   4044  1.10  christos 						switch (optcode) {
   4045  1.10  christos 						case DNS_OPT_COOKIE:
   4046  1.10  christos 							sep = "";
   4047  1.10  christos 							break;
   4048  1.10  christos 						default:
   4049  1.10  christos 							sep = " ";
   4050  1.10  christos 							break;
   4051  1.10  christos 						}
   4052  1.10  christos 						snprintf(buf, sizeof(buf),
   4053  1.10  christos 							 "%02x%s", optdata[i],
   4054  1.10  christos 							 sep);
   4055  1.10  christos 						ADD_STRING(target, buf);
   4056   1.1  christos 					}
   4057   1.1  christos 				}
   4058   1.1  christos 
   4059   1.1  christos 				isc_buffer_forward(&optbuf, optlen);
   4060   1.1  christos 
   4061  1.20  christos 				if (optcode == DNS_OPT_COOKIE ||
   4062  1.20  christos 				    optcode == DNS_OPT_CLIENT_SUBNET)
   4063  1.20  christos 				{
   4064   1.1  christos 					ADD_STRING(target, "\n");
   4065   1.1  christos 					continue;
   4066   1.1  christos 				}
   4067   1.1  christos 
   4068   1.1  christos 				/*
   4069   1.1  christos 				 * For non-COOKIE options, add a printable
   4070   1.1  christos 				 * version
   4071   1.1  christos 				 */
   4072  1.10  christos 				if (!extra_text) {
   4073  1.10  christos 					ADD_STRING(target, "(\"");
   4074  1.10  christos 				} else {
   4075  1.10  christos 					ADD_STRING(target, "\"");
   4076  1.10  christos 				}
   4077  1.20  christos 				PUT_YAMLSTR(target, optdata, optlen, utf8ok);
   4078  1.10  christos 				if (!extra_text) {
   4079  1.10  christos 					ADD_STRING(target, "\")");
   4080  1.10  christos 				} else {
   4081  1.10  christos 					ADD_STRING(target, "\"");
   4082  1.10  christos 				}
   4083   1.1  christos 			}
   4084   1.1  christos 			ADD_STRING(target, "\n");
   4085   1.1  christos 		}
   4086  1.10  christos 		msg->indent.count = indent;
   4087   1.1  christos 		result = ISC_R_SUCCESS;
   4088   1.1  christos 		goto cleanup;
   4089   1.1  christos 	case DNS_PSEUDOSECTION_TSIG:
   4090   1.1  christos 		ps = dns_message_gettsig(msg, &name);
   4091   1.1  christos 		if (ps == NULL) {
   4092   1.1  christos 			result = ISC_R_SUCCESS;
   4093   1.1  christos 			goto cleanup;
   4094   1.1  christos 		}
   4095   1.1  christos 		INDENT(style);
   4096   1.1  christos 		ADD_STRING(target, "TSIG_PSEUDOSECTION:\n");
   4097   1.9  christos 		result = dns_master_rdatasettotext(name, ps, style,
   4098   1.9  christos 						   &msg->indent, target);
   4099   1.1  christos 		ADD_STRING(target, "\n");
   4100   1.1  christos 		goto cleanup;
   4101   1.1  christos 	case DNS_PSEUDOSECTION_SIG0:
   4102   1.1  christos 		ps = dns_message_getsig0(msg, &name);
   4103   1.1  christos 		if (ps == NULL) {
   4104   1.1  christos 			result = ISC_R_SUCCESS;
   4105   1.1  christos 			goto cleanup;
   4106   1.1  christos 		}
   4107   1.1  christos 		INDENT(style);
   4108   1.1  christos 		ADD_STRING(target, "SIG0_PSEUDOSECTION:\n");
   4109   1.9  christos 		result = dns_master_rdatasettotext(name, ps, style,
   4110   1.9  christos 						   &msg->indent, target);
   4111   1.1  christos 		if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0 &&
   4112   1.1  christos 		    (flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
   4113   1.9  christos 		{
   4114   1.1  christos 			ADD_STRING(target, "\n");
   4115   1.9  christos 		}
   4116   1.1  christos 		goto cleanup;
   4117   1.1  christos 	}
   4118   1.1  christos 
   4119   1.1  christos 	result = ISC_R_UNEXPECTED;
   4120   1.1  christos 
   4121   1.9  christos cleanup:
   4122   1.9  christos 	msg->indent.count = saved_count;
   4123  1.19  christos 	return result;
   4124   1.1  christos }
   4125   1.1  christos 
   4126   1.1  christos isc_result_t
   4127   1.9  christos dns_message_pseudosectiontotext(dns_message_t *msg, dns_pseudosection_t section,
   4128   1.1  christos 				const dns_master_style_t *style,
   4129   1.1  christos 				dns_messagetextflag_t flags,
   4130   1.9  christos 				isc_buffer_t *target) {
   4131   1.1  christos 	dns_rdataset_t *ps = NULL;
   4132   1.1  christos 	const dns_name_t *name = NULL;
   4133   1.1  christos 	isc_result_t result;
   4134   1.1  christos 	char buf[sizeof(" (65000 bytes)")];
   4135   1.3  christos 	uint32_t mbz;
   4136   1.1  christos 	dns_rdata_t rdata;
   4137   1.1  christos 	isc_buffer_t optbuf;
   4138   1.3  christos 	uint16_t optcode, optlen;
   4139  1.20  christos 	unsigned char *optdata = NULL;
   4140  1.20  christos 	isc_buffer_t ecsbuf;
   4141   1.1  christos 
   4142   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   4143   1.1  christos 	REQUIRE(target != NULL);
   4144  1.19  christos 	REQUIRE(VALID_NAMED_PSEUDOSECTION(section));
   4145   1.1  christos 
   4146   1.9  christos 	if ((dns_master_styleflags(style) & DNS_STYLEFLAG_YAML) != 0) {
   4147  1.19  christos 		return dns_message_pseudosectiontoyaml(msg, section, style,
   4148  1.19  christos 						       flags, target);
   4149   1.9  christos 	}
   4150   1.9  christos 
   4151   1.1  christos 	switch (section) {
   4152   1.1  christos 	case DNS_PSEUDOSECTION_OPT:
   4153   1.1  christos 		ps = dns_message_getopt(msg);
   4154   1.9  christos 		if (ps == NULL) {
   4155  1.19  christos 			return ISC_R_SUCCESS;
   4156   1.9  christos 		}
   4157   1.1  christos 		if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) {
   4158   1.1  christos 			INDENT(style);
   4159   1.1  christos 			ADD_STRING(target, ";; OPT PSEUDOSECTION:\n");
   4160   1.1  christos 		}
   4161   1.1  christos 
   4162   1.1  christos 		INDENT(style);
   4163   1.1  christos 		ADD_STRING(target, "; EDNS: version: ");
   4164   1.1  christos 		snprintf(buf, sizeof(buf), "%u",
   4165   1.1  christos 			 (unsigned int)((ps->ttl & 0x00ff0000) >> 16));
   4166   1.1  christos 		ADD_STRING(target, buf);
   4167   1.1  christos 		ADD_STRING(target, ", flags:");
   4168   1.9  christos 		if ((ps->ttl & DNS_MESSAGEEXTFLAG_DO) != 0) {
   4169   1.1  christos 			ADD_STRING(target, " do");
   4170   1.9  christos 		}
   4171  1.21  christos 		if ((ps->ttl & DNS_MESSAGEEXTFLAG_CO) != 0) {
   4172  1.21  christos 			ADD_STRING(target, " co");
   4173  1.21  christos 		}
   4174   1.1  christos 		mbz = ps->ttl & 0xffff;
   4175  1.21  christos 		/* Exclude Known Flags. */
   4176  1.21  christos 		mbz &= ~(DNS_MESSAGEEXTFLAG_DO | DNS_MESSAGEEXTFLAG_CO);
   4177   1.1  christos 		if (mbz != 0) {
   4178   1.1  christos 			ADD_STRING(target, "; MBZ: ");
   4179   1.1  christos 			snprintf(buf, sizeof(buf), "0x%.4x", mbz);
   4180   1.1  christos 			ADD_STRING(target, buf);
   4181   1.1  christos 			ADD_STRING(target, ", udp: ");
   4182   1.9  christos 		} else {
   4183   1.1  christos 			ADD_STRING(target, "; udp: ");
   4184   1.9  christos 		}
   4185   1.1  christos 		snprintf(buf, sizeof(buf), "%u\n", (unsigned int)ps->rdclass);
   4186   1.1  christos 		ADD_STRING(target, buf);
   4187   1.1  christos 
   4188   1.1  christos 		result = dns_rdataset_first(ps);
   4189   1.9  christos 		if (result != ISC_R_SUCCESS) {
   4190  1.19  christos 			return ISC_R_SUCCESS;
   4191   1.9  christos 		}
   4192   1.1  christos 
   4193   1.1  christos 		/*
   4194   1.1  christos 		 * Print EDNS info, if any.
   4195   1.1  christos 		 *
   4196   1.1  christos 		 * WARNING: The option contents may be malformed as
   4197   1.1  christos 		 * dig +ednsopt=value:<content> does no validity
   4198   1.1  christos 		 * checking.
   4199   1.1  christos 		 */
   4200   1.1  christos 		dns_rdata_init(&rdata);
   4201   1.1  christos 		dns_rdataset_current(ps, &rdata);
   4202   1.1  christos 
   4203   1.1  christos 		isc_buffer_init(&optbuf, rdata.data, rdata.length);
   4204   1.1  christos 		isc_buffer_add(&optbuf, rdata.length);
   4205   1.1  christos 		while (isc_buffer_remaininglength(&optbuf) != 0) {
   4206  1.20  christos 			const char *option_name = NULL;
   4207  1.20  christos 
   4208   1.1  christos 			INSIST(isc_buffer_remaininglength(&optbuf) >= 4U);
   4209   1.1  christos 			optcode = isc_buffer_getuint16(&optbuf);
   4210   1.1  christos 			optlen = isc_buffer_getuint16(&optbuf);
   4211   1.1  christos 
   4212   1.1  christos 			INSIST(isc_buffer_remaininglength(&optbuf) >= optlen);
   4213   1.1  christos 
   4214   1.1  christos 			INDENT(style);
   4215  1.20  christos 			ADD_STRING(target, "; ");
   4216  1.20  christos 			if (optcode < ARRAY_SIZE(option_names)) {
   4217  1.20  christos 				option_name = option_names[optcode];
   4218  1.20  christos 			}
   4219  1.20  christos 			if (option_name != NULL) {
   4220  1.20  christos 				ADD_STRING(target, option_names[optcode])
   4221  1.20  christos 			} else {
   4222  1.20  christos 				snprintf(buf, sizeof(buf), "OPT=%u", optcode);
   4223  1.20  christos 				ADD_STRING(target, buf);
   4224  1.20  christos 			}
   4225  1.20  christos 			ADD_STRING(target, ":");
   4226   1.1  christos 
   4227  1.20  christos 			switch (optcode) {
   4228  1.20  christos 			case DNS_OPT_LLQ:
   4229   1.7  christos 				if (optlen == 18U) {
   4230  1.20  christos 					result = render_llq(&optbuf, msg, style,
   4231  1.20  christos 							    target);
   4232   1.7  christos 					if (result != ISC_R_SUCCESS) {
   4233  1.19  christos 						return result;
   4234   1.7  christos 					}
   4235   1.7  christos 					ADD_STRING(target, "\n");
   4236   1.7  christos 					continue;
   4237   1.7  christos 				}
   4238  1.20  christos 				break;
   4239  1.20  christos 			case DNS_OPT_UL:
   4240  1.19  christos 				if (optlen == 4U || optlen == 8U) {
   4241  1.19  christos 					uint32_t secs, key = 0;
   4242  1.19  christos 					secs = isc_buffer_getuint32(&optbuf);
   4243  1.19  christos 					snprintf(buf, sizeof(buf), " %u", secs);
   4244  1.19  christos 					ADD_STRING(target, buf);
   4245  1.19  christos 					if (optlen == 8U) {
   4246  1.19  christos 						key = isc_buffer_getuint32(
   4247  1.19  christos 							&optbuf);
   4248  1.19  christos 						snprintf(buf, sizeof(buf),
   4249  1.19  christos 							 "/%u", key);
   4250  1.19  christos 						ADD_STRING(target, buf);
   4251  1.19  christos 					}
   4252  1.19  christos 					ADD_STRING(target, " (");
   4253  1.19  christos 					result = dns_ttl_totext(secs, true,
   4254  1.19  christos 								true, target);
   4255  1.19  christos 					if (result != ISC_R_SUCCESS) {
   4256  1.19  christos 						goto cleanup;
   4257  1.19  christos 					}
   4258  1.19  christos 					if (optlen == 8U) {
   4259  1.19  christos 						ADD_STRING(target, "/");
   4260  1.19  christos 						result = dns_ttl_totext(
   4261  1.19  christos 							key, true, true,
   4262  1.19  christos 							target);
   4263  1.19  christos 						if (result != ISC_R_SUCCESS) {
   4264  1.19  christos 							goto cleanup;
   4265  1.19  christos 						}
   4266  1.19  christos 					}
   4267  1.19  christos 					ADD_STRING(target, ")\n");
   4268  1.19  christos 					continue;
   4269  1.19  christos 				}
   4270  1.20  christos 				break;
   4271  1.20  christos 			case DNS_OPT_CLIENT_SUBNET:
   4272   1.1  christos 				isc_buffer_init(&ecsbuf,
   4273   1.1  christos 						isc_buffer_current(&optbuf),
   4274   1.1  christos 						optlen);
   4275   1.1  christos 				isc_buffer_add(&ecsbuf, optlen);
   4276   1.1  christos 				result = render_ecs(&ecsbuf, target);
   4277   1.9  christos 				if (result == ISC_R_NOSPACE) {
   4278  1.19  christos 					return result;
   4279   1.9  christos 				}
   4280   1.1  christos 				if (result == ISC_R_SUCCESS) {
   4281   1.1  christos 					isc_buffer_forward(&optbuf, optlen);
   4282   1.1  christos 					ADD_STRING(target, "\n");
   4283   1.1  christos 					continue;
   4284   1.1  christos 				}
   4285  1.20  christos 				break;
   4286  1.20  christos 			case DNS_OPT_EXPIRE:
   4287   1.1  christos 				if (optlen == 4) {
   4288   1.3  christos 					uint32_t secs;
   4289   1.1  christos 					secs = isc_buffer_getuint32(&optbuf);
   4290   1.9  christos 					snprintf(buf, sizeof(buf), " %u", secs);
   4291   1.1  christos 					ADD_STRING(target, buf);
   4292   1.1  christos 					ADD_STRING(target, " (");
   4293   1.9  christos 					result = dns_ttl_totext(secs, true,
   4294   1.9  christos 								true, target);
   4295   1.9  christos 					if (result != ISC_R_SUCCESS) {
   4296  1.19  christos 						return result;
   4297   1.9  christos 					}
   4298   1.1  christos 					ADD_STRING(target, ")\n");
   4299   1.1  christos 					continue;
   4300   1.1  christos 				}
   4301  1.20  christos 				break;
   4302  1.20  christos 			case DNS_OPT_TCP_KEEPALIVE:
   4303   1.1  christos 				if (optlen == 2) {
   4304   1.1  christos 					unsigned int dsecs;
   4305   1.1  christos 					dsecs = isc_buffer_getuint16(&optbuf);
   4306   1.1  christos 					snprintf(buf, sizeof(buf), " %u.%u",
   4307   1.1  christos 						 dsecs / 10U, dsecs % 10U);
   4308   1.1  christos 					ADD_STRING(target, buf);
   4309   1.1  christos 					ADD_STRING(target, " secs\n");
   4310   1.1  christos 					continue;
   4311   1.1  christos 				}
   4312  1.20  christos 				break;
   4313  1.20  christos 			case DNS_OPT_PAD:
   4314   1.1  christos 				if (optlen > 0U) {
   4315   1.1  christos 					snprintf(buf, sizeof(buf),
   4316   1.1  christos 						 " (%u bytes)", optlen);
   4317   1.1  christos 					ADD_STRING(target, buf);
   4318   1.1  christos 					isc_buffer_forward(&optbuf, optlen);
   4319   1.1  christos 				}
   4320   1.1  christos 				ADD_STRING(target, "\n");
   4321   1.1  christos 				continue;
   4322  1.20  christos 			case DNS_OPT_CHAIN:
   4323  1.20  christos 			case DNS_OPT_REPORT_CHANNEL:
   4324  1.20  christos 				if (optlen > 0U) {
   4325  1.20  christos 					isc_buffer_t sb = optbuf;
   4326  1.20  christos 					isc_buffer_setactive(&optbuf, optlen);
   4327  1.20  christos 					result = render_nameopt(&optbuf, false,
   4328  1.20  christos 								target);
   4329  1.20  christos 					if (result == ISC_R_SUCCESS) {
   4330  1.20  christos 						ADD_STRING(target, "\n");
   4331  1.20  christos 						continue;
   4332  1.20  christos 					}
   4333  1.20  christos 					optbuf = sb;
   4334  1.20  christos 				}
   4335  1.20  christos 				ADD_STRING(target, "\n");
   4336  1.20  christos 				break;
   4337  1.20  christos 			case DNS_OPT_KEY_TAG:
   4338   1.1  christos 				if (optlen > 0U && (optlen % 2U) == 0U) {
   4339   1.9  christos 					const char *sep = "";
   4340   1.1  christos 					while (optlen > 0U) {
   4341  1.20  christos 						uint16_t id =
   4342  1.20  christos 							isc_buffer_getuint16(
   4343  1.20  christos 								&optbuf);
   4344   1.9  christos 						snprintf(buf, sizeof(buf),
   4345   1.9  christos 							 "%s %u", sep, id);
   4346   1.9  christos 						ADD_STRING(target, buf);
   4347   1.9  christos 						sep = ",";
   4348   1.9  christos 						optlen -= 2;
   4349   1.1  christos 					}
   4350   1.1  christos 					ADD_STRING(target, "\n");
   4351   1.1  christos 					continue;
   4352   1.1  christos 				}
   4353  1.20  christos 				break;
   4354  1.20  christos 			case DNS_OPT_EDE:
   4355  1.10  christos 				if (optlen >= 2U) {
   4356  1.10  christos 					uint16_t ede;
   4357  1.10  christos 					ede = isc_buffer_getuint16(&optbuf);
   4358  1.10  christos 					snprintf(buf, sizeof(buf), " %u", ede);
   4359  1.10  christos 					ADD_STRING(target, buf);
   4360  1.10  christos 					if (ede < ARRAY_SIZE(edetext)) {
   4361  1.10  christos 						ADD_STRING(target, " (");
   4362  1.10  christos 						ADD_STRING(target,
   4363  1.10  christos 							   edetext[ede]);
   4364  1.10  christos 						ADD_STRING(target, ")");
   4365  1.10  christos 					}
   4366  1.10  christos 					optlen -= 2;
   4367  1.10  christos 					if (optlen != 0) {
   4368  1.10  christos 						ADD_STRING(target, ":");
   4369  1.10  christos 					}
   4370  1.10  christos 				} else if (optlen == 1U) {
   4371  1.10  christos 					/* Malformed */
   4372  1.10  christos 					optdata = isc_buffer_current(&optbuf);
   4373  1.10  christos 					snprintf(buf, sizeof(buf),
   4374  1.10  christos 						 " %02x (\"%c\")\n", optdata[0],
   4375  1.10  christos 						 isprint(optdata[0])
   4376  1.10  christos 							 ? optdata[0]
   4377  1.10  christos 							 : '.');
   4378  1.10  christos 					isc_buffer_forward(&optbuf, optlen);
   4379  1.10  christos 					ADD_STRING(target, buf);
   4380  1.10  christos 					continue;
   4381  1.10  christos 				}
   4382  1.20  christos 				break;
   4383  1.20  christos 			case DNS_OPT_CLIENT_TAG:
   4384  1.20  christos 			case DNS_OPT_SERVER_TAG:
   4385   1.6  christos 				if (optlen == 2U) {
   4386  1.20  christos 					uint16_t id =
   4387  1.20  christos 						isc_buffer_getuint16(&optbuf);
   4388   1.9  christos 					snprintf(buf, sizeof(buf), " %u\n", id);
   4389   1.6  christos 					ADD_STRING(target, buf);
   4390   1.6  christos 					continue;
   4391   1.6  christos 				}
   4392  1.20  christos 				break;
   4393  1.20  christos 			default:
   4394  1.20  christos 				break;
   4395   1.1  christos 			}
   4396   1.1  christos 
   4397   1.1  christos 			if (optlen != 0) {
   4398   1.1  christos 				int i;
   4399  1.10  christos 				bool utf8ok = false;
   4400   1.9  christos 
   4401   1.9  christos 				ADD_STRING(target, " ");
   4402   1.1  christos 
   4403   1.1  christos 				optdata = isc_buffer_current(&optbuf);
   4404  1.10  christos 				if (optcode == DNS_OPT_EDE) {
   4405  1.10  christos 					utf8ok = isc_utf8_valid(optdata,
   4406  1.10  christos 								optlen);
   4407  1.10  christos 				}
   4408  1.10  christos 				if (!utf8ok) {
   4409  1.10  christos 					for (i = 0; i < optlen; i++) {
   4410  1.10  christos 						const char *sep;
   4411  1.10  christos 						switch (optcode) {
   4412  1.10  christos 						case DNS_OPT_COOKIE:
   4413  1.10  christos 							sep = "";
   4414  1.10  christos 							break;
   4415  1.10  christos 						default:
   4416  1.10  christos 							sep = " ";
   4417  1.10  christos 							break;
   4418  1.10  christos 						}
   4419  1.10  christos 						snprintf(buf, sizeof(buf),
   4420  1.10  christos 							 "%02x%s", optdata[i],
   4421  1.10  christos 							 sep);
   4422  1.10  christos 						ADD_STRING(target, buf);
   4423   1.1  christos 					}
   4424   1.1  christos 				}
   4425   1.1  christos 
   4426   1.1  christos 				isc_buffer_forward(&optbuf, optlen);
   4427   1.1  christos 
   4428   1.1  christos 				if (optcode == DNS_OPT_COOKIE) {
   4429   1.1  christos 					/*
   4430   1.1  christos 					 * Valid server cookie?
   4431   1.1  christos 					 */
   4432   1.9  christos 					if (msg->cc_ok && optlen >= 16) {
   4433   1.1  christos 						ADD_STRING(target, " (good)");
   4434   1.9  christos 					}
   4435   1.1  christos 					/*
   4436   1.1  christos 					 * Server cookie is not valid but
   4437   1.1  christos 					 * we had our cookie echoed back.
   4438   1.1  christos 					 */
   4439   1.9  christos 					if (msg->cc_ok && optlen < 16) {
   4440   1.1  christos 						ADD_STRING(target, " (echoed)");
   4441   1.9  christos 					}
   4442   1.1  christos 					/*
   4443   1.1  christos 					 * We didn't get our cookie echoed
   4444   1.1  christos 					 * back.
   4445   1.1  christos 					 */
   4446   1.9  christos 					if (msg->cc_bad) {
   4447   1.1  christos 						ADD_STRING(target, " (bad)");
   4448   1.9  christos 					}
   4449   1.1  christos 					ADD_STRING(target, "\n");
   4450   1.1  christos 					continue;
   4451   1.1  christos 				}
   4452   1.1  christos 
   4453   1.1  christos 				if (optcode == DNS_OPT_CLIENT_SUBNET) {
   4454   1.1  christos 					ADD_STRING(target, "\n");
   4455   1.1  christos 					continue;
   4456   1.1  christos 				}
   4457   1.1  christos 
   4458   1.1  christos 				/*
   4459   1.1  christos 				 * For non-COOKIE options, add a printable
   4460  1.10  christos 				 * version.
   4461   1.1  christos 				 */
   4462  1.10  christos 				if (optcode != DNS_OPT_EDE) {
   4463  1.10  christos 					ADD_STRING(target, "(\"");
   4464  1.10  christos 				} else {
   4465  1.10  christos 					ADD_STRING(target, "(");
   4466  1.10  christos 				}
   4467   1.1  christos 				if (isc_buffer_availablelength(target) < optlen)
   4468   1.9  christos 				{
   4469  1.19  christos 					return ISC_R_NOSPACE;
   4470   1.9  christos 				}
   4471   1.1  christos 				for (i = 0; i < optlen; i++) {
   4472  1.10  christos 					if (isprint(optdata[i]) ||
   4473  1.15  christos 					    (utf8ok && optdata[i] > 127))
   4474  1.15  christos 					{
   4475   1.9  christos 						isc_buffer_putmem(
   4476   1.9  christos 							target, &optdata[i], 1);
   4477   1.9  christos 					} else {
   4478   1.1  christos 						isc_buffer_putstr(target, ".");
   4479   1.9  christos 					}
   4480   1.1  christos 				}
   4481  1.10  christos 				if (optcode != DNS_OPT_EDE) {
   4482  1.10  christos 					ADD_STRING(target, "\")");
   4483  1.10  christos 				} else {
   4484  1.10  christos 					ADD_STRING(target, ")");
   4485  1.10  christos 				}
   4486   1.1  christos 			}
   4487   1.1  christos 			ADD_STRING(target, "\n");
   4488   1.1  christos 		}
   4489  1.19  christos 		return ISC_R_SUCCESS;
   4490   1.1  christos 	case DNS_PSEUDOSECTION_TSIG:
   4491   1.1  christos 		ps = dns_message_gettsig(msg, &name);
   4492   1.9  christos 		if (ps == NULL) {
   4493  1.19  christos 			return ISC_R_SUCCESS;
   4494   1.9  christos 		}
   4495   1.1  christos 		INDENT(style);
   4496   1.9  christos 		if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) {
   4497   1.1  christos 			ADD_STRING(target, ";; TSIG PSEUDOSECTION:\n");
   4498   1.9  christos 		}
   4499   1.9  christos 		result = dns_master_rdatasettotext(name, ps, style,
   4500   1.9  christos 						   &msg->indent, target);
   4501   1.1  christos 		if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0 &&
   4502   1.1  christos 		    (flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
   4503   1.9  christos 		{
   4504   1.1  christos 			ADD_STRING(target, "\n");
   4505   1.9  christos 		}
   4506  1.19  christos 		return result;
   4507   1.1  christos 	case DNS_PSEUDOSECTION_SIG0:
   4508   1.1  christos 		ps = dns_message_getsig0(msg, &name);
   4509   1.9  christos 		if (ps == NULL) {
   4510  1.19  christos 			return ISC_R_SUCCESS;
   4511   1.9  christos 		}
   4512   1.1  christos 		INDENT(style);
   4513   1.9  christos 		if ((flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0) {
   4514   1.1  christos 			ADD_STRING(target, ";; SIG0 PSEUDOSECTION:\n");
   4515   1.9  christos 		}
   4516   1.9  christos 		result = dns_master_rdatasettotext(name, ps, style,
   4517   1.9  christos 						   &msg->indent, target);
   4518   1.1  christos 		if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) == 0 &&
   4519   1.1  christos 		    (flags & DNS_MESSAGETEXTFLAG_NOCOMMENTS) == 0)
   4520   1.9  christos 		{
   4521   1.1  christos 			ADD_STRING(target, "\n");
   4522   1.9  christos 		}
   4523  1.19  christos 		return result;
   4524   1.1  christos 	}
   4525   1.1  christos 	result = ISC_R_UNEXPECTED;
   4526   1.9  christos cleanup:
   4527  1.19  christos 	return result;
   4528   1.1  christos }
   4529   1.1  christos 
   4530   1.1  christos isc_result_t
   4531   1.9  christos dns_message_headertotext(dns_message_t *msg, const dns_master_style_t *style,
   4532   1.9  christos 			 dns_messagetextflag_t flags, isc_buffer_t *target) {
   4533   1.1  christos 	char buf[sizeof("1234567890")];
   4534   1.1  christos 	isc_result_t result;
   4535   1.1  christos 
   4536   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   4537   1.1  christos 	REQUIRE(target != NULL);
   4538   1.1  christos 
   4539   1.9  christos 	if ((flags & DNS_MESSAGETEXTFLAG_NOHEADERS) != 0) {
   4540  1.19  christos 		return ISC_R_SUCCESS;
   4541   1.9  christos 	}
   4542   1.9  christos 
   4543   1.9  christos 	if (dns_master_styleflags(style) & DNS_STYLEFLAG_YAML) {
   4544   1.1  christos 		INDENT(style);
   4545   1.1  christos 		ADD_STRING(target, "opcode: ");
   4546   1.1  christos 		ADD_STRING(target, opcodetext[msg->opcode]);
   4547   1.1  christos 		ADD_STRING(target, "\n");
   4548   1.1  christos 		INDENT(style);
   4549   1.1  christos 		ADD_STRING(target, "status: ");
   4550   1.1  christos 		result = dns_rcode_totext(msg->rcode, target);
   4551   1.9  christos 		if (result != ISC_R_SUCCESS) {
   4552  1.19  christos 			return result;
   4553   1.9  christos 		}
   4554   1.1  christos 		ADD_STRING(target, "\n");
   4555   1.1  christos 		INDENT(style);
   4556   1.1  christos 		ADD_STRING(target, "id: ");
   4557   1.9  christos 		snprintf(buf, sizeof(buf), "%u", msg->id);
   4558   1.1  christos 		ADD_STRING(target, buf);
   4559   1.1  christos 		ADD_STRING(target, "\n");
   4560   1.1  christos 		INDENT(style);
   4561   1.1  christos 		ADD_STRING(target, "flags:");
   4562   1.9  christos 		if ((msg->flags & DNS_MESSAGEFLAG_QR) != 0) {
   4563   1.1  christos 			ADD_STRING(target, " qr");
   4564   1.9  christos 		}
   4565   1.9  christos 		if ((msg->flags & DNS_MESSAGEFLAG_AA) != 0) {
   4566   1.1  christos 			ADD_STRING(target, " aa");
   4567   1.9  christos 		}
   4568   1.9  christos 		if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0) {
   4569   1.1  christos 			ADD_STRING(target, " tc");
   4570   1.9  christos 		}
   4571   1.9  christos 		if ((msg->flags & DNS_MESSAGEFLAG_RD) != 0) {
   4572   1.1  christos 			ADD_STRING(target, " rd");
   4573   1.9  christos 		}
   4574   1.9  christos 		if ((msg->flags & DNS_MESSAGEFLAG_RA) != 0) {
   4575   1.1  christos 			ADD_STRING(target, " ra");
   4576   1.9  christos 		}
   4577   1.9  christos 		if ((msg->flags & DNS_MESSAGEFLAG_AD) != 0) {
   4578   1.1  christos 			ADD_STRING(target, " ad");
   4579   1.9  christos 		}
   4580   1.9  christos 		if ((msg->flags & DNS_MESSAGEFLAG_CD) != 0) {
   4581   1.1  christos 			ADD_STRING(target, " cd");
   4582   1.9  christos 		}
   4583   1.1  christos 		ADD_STRING(target, "\n");
   4584   1.1  christos 		/*
   4585   1.1  christos 		 * The final unnamed flag must be zero.
   4586   1.1  christos 		 */
   4587   1.1  christos 		if ((msg->flags & 0x0040U) != 0) {
   4588   1.1  christos 			INDENT(style);
   4589   1.1  christos 			ADD_STRING(target, "MBZ: 0x4");
   4590   1.1  christos 			ADD_STRING(target, "\n");
   4591   1.1  christos 		}
   4592   1.1  christos 		if (msg->opcode != dns_opcode_update) {
   4593   1.1  christos 			INDENT(style);
   4594   1.1  christos 			ADD_STRING(target, "QUESTION: ");
   4595   1.1  christos 		} else {
   4596  1.14  christos 			INDENT(style);
   4597   1.1  christos 			ADD_STRING(target, "ZONE: ");
   4598   1.1  christos 		}
   4599   1.1  christos 		snprintf(buf, sizeof(buf), "%1u",
   4600   1.1  christos 			 msg->counts[DNS_SECTION_QUESTION]);
   4601   1.1  christos 		ADD_STRING(target, buf);
   4602   1.1  christos 		ADD_STRING(target, "\n");
   4603   1.1  christos 		if (msg->opcode != dns_opcode_update) {
   4604   1.1  christos 			INDENT(style);
   4605   1.1  christos 			ADD_STRING(target, "ANSWER: ");
   4606   1.1  christos 		} else {
   4607   1.1  christos 			INDENT(style);
   4608   1.1  christos 			ADD_STRING(target, "PREREQ: ");
   4609   1.1  christos 		}
   4610   1.1  christos 		snprintf(buf, sizeof(buf), "%1u",
   4611   1.1  christos 			 msg->counts[DNS_SECTION_ANSWER]);
   4612   1.1  christos 		ADD_STRING(target, buf);
   4613   1.1  christos 		ADD_STRING(target, "\n");
   4614   1.1  christos 		if (msg->opcode != dns_opcode_update) {
   4615   1.1  christos 			INDENT(style);
   4616   1.1  christos 			ADD_STRING(target, "AUTHORITY: ");
   4617   1.1  christos 		} else {
   4618   1.1  christos 			INDENT(style);
   4619   1.1  christos 			ADD_STRING(target, "UPDATE: ");
   4620   1.1  christos 		}
   4621   1.1  christos 		snprintf(buf, sizeof(buf), "%1u",
   4622   1.9  christos 			 msg->counts[DNS_SECTION_AUTHORITY]);
   4623   1.1  christos 		ADD_STRING(target, buf);
   4624   1.1  christos 		ADD_STRING(target, "\n");
   4625   1.1  christos 		INDENT(style);
   4626   1.1  christos 		ADD_STRING(target, "ADDITIONAL: ");
   4627   1.1  christos 		snprintf(buf, sizeof(buf), "%1u",
   4628   1.9  christos 			 msg->counts[DNS_SECTION_ADDITIONAL]);
   4629   1.1  christos 		ADD_STRING(target, buf);
   4630   1.1  christos 		ADD_STRING(target, "\n");
   4631   1.9  christos 	} else {
   4632   1.1  christos 		INDENT(style);
   4633   1.1  christos 		ADD_STRING(target, ";; ->>HEADER<<- opcode: ");
   4634   1.1  christos 		ADD_STRING(target, opcodetext[msg->opcode]);
   4635   1.1  christos 		ADD_STRING(target, ", status: ");
   4636   1.1  christos 		result = dns_rcode_totext(msg->rcode, target);
   4637   1.9  christos 		if (result != ISC_R_SUCCESS) {
   4638  1.19  christos 			return result;
   4639   1.9  christos 		}
   4640   1.1  christos 		ADD_STRING(target, ", id: ");
   4641   1.1  christos 		snprintf(buf, sizeof(buf), "%6u", msg->id);
   4642   1.1  christos 		ADD_STRING(target, buf);
   4643   1.1  christos 		ADD_STRING(target, "\n");
   4644   1.1  christos 		INDENT(style);
   4645   1.1  christos 		ADD_STRING(target, ";; flags:");
   4646   1.9  christos 		if ((msg->flags & DNS_MESSAGEFLAG_QR) != 0) {
   4647   1.1  christos 			ADD_STRING(target, " qr");
   4648   1.9  christos 		}
   4649   1.9  christos 		if ((msg->flags & DNS_MESSAGEFLAG_AA) != 0) {
   4650   1.1  christos 			ADD_STRING(target, " aa");
   4651   1.9  christos 		}
   4652   1.9  christos 		if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0) {
   4653   1.1  christos 			ADD_STRING(target, " tc");
   4654   1.9  christos 		}
   4655   1.9  christos 		if ((msg->flags & DNS_MESSAGEFLAG_RD) != 0) {
   4656   1.1  christos 			ADD_STRING(target, " rd");
   4657   1.9  christos 		}
   4658   1.9  christos 		if ((msg->flags & DNS_MESSAGEFLAG_RA) != 0) {
   4659   1.1  christos 			ADD_STRING(target, " ra");
   4660   1.9  christos 		}
   4661   1.9  christos 		if ((msg->flags & DNS_MESSAGEFLAG_AD) != 0) {
   4662   1.1  christos 			ADD_STRING(target, " ad");
   4663   1.9  christos 		}
   4664   1.9  christos 		if ((msg->flags & DNS_MESSAGEFLAG_CD) != 0) {
   4665   1.1  christos 			ADD_STRING(target, " cd");
   4666   1.9  christos 		}
   4667   1.1  christos 		/*
   4668   1.1  christos 		 * The final unnamed flag must be zero.
   4669   1.1  christos 		 */
   4670   1.1  christos 		if ((msg->flags & 0x0040U) != 0) {
   4671   1.1  christos 			INDENT(style);
   4672   1.1  christos 			ADD_STRING(target, "; MBZ: 0x4");
   4673   1.1  christos 		}
   4674   1.1  christos 		if (msg->opcode != dns_opcode_update) {
   4675   1.1  christos 			INDENT(style);
   4676   1.1  christos 			ADD_STRING(target, "; QUESTION: ");
   4677   1.1  christos 		} else {
   4678   1.1  christos 			INDENT(style);
   4679   1.1  christos 			ADD_STRING(target, "; ZONE: ");
   4680   1.1  christos 		}
   4681   1.1  christos 		snprintf(buf, sizeof(buf), "%1u",
   4682   1.1  christos 			 msg->counts[DNS_SECTION_QUESTION]);
   4683   1.1  christos 		ADD_STRING(target, buf);
   4684   1.1  christos 		if (msg->opcode != dns_opcode_update) {
   4685   1.1  christos 			ADD_STRING(target, ", ANSWER: ");
   4686   1.1  christos 		} else {
   4687   1.1  christos 			ADD_STRING(target, ", PREREQ: ");
   4688   1.1  christos 		}
   4689   1.1  christos 		snprintf(buf, sizeof(buf), "%1u",
   4690   1.1  christos 			 msg->counts[DNS_SECTION_ANSWER]);
   4691   1.1  christos 		ADD_STRING(target, buf);
   4692   1.1  christos 		if (msg->opcode != dns_opcode_update) {
   4693   1.1  christos 			ADD_STRING(target, ", AUTHORITY: ");
   4694   1.1  christos 		} else {
   4695   1.1  christos 			ADD_STRING(target, ", UPDATE: ");
   4696   1.1  christos 		}
   4697   1.1  christos 		snprintf(buf, sizeof(buf), "%1u",
   4698   1.9  christos 			 msg->counts[DNS_SECTION_AUTHORITY]);
   4699   1.1  christos 		ADD_STRING(target, buf);
   4700   1.1  christos 		ADD_STRING(target, ", ADDITIONAL: ");
   4701   1.1  christos 		snprintf(buf, sizeof(buf), "%1u",
   4702   1.9  christos 			 msg->counts[DNS_SECTION_ADDITIONAL]);
   4703   1.1  christos 		ADD_STRING(target, buf);
   4704   1.1  christos 		ADD_STRING(target, "\n");
   4705   1.1  christos 	}
   4706   1.9  christos 
   4707   1.9  christos cleanup:
   4708  1.19  christos 	return result;
   4709   1.9  christos }
   4710   1.9  christos 
   4711   1.9  christos isc_result_t
   4712   1.9  christos dns_message_totext(dns_message_t *msg, const dns_master_style_t *style,
   4713   1.9  christos 		   dns_messagetextflag_t flags, isc_buffer_t *target) {
   4714   1.9  christos 	isc_result_t result;
   4715   1.9  christos 
   4716   1.9  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   4717   1.9  christos 	REQUIRE(target != NULL);
   4718   1.9  christos 
   4719   1.9  christos 	result = dns_message_headertotext(msg, style, flags, target);
   4720   1.9  christos 	if (result != ISC_R_SUCCESS) {
   4721  1.19  christos 		return result;
   4722   1.9  christos 	}
   4723   1.9  christos 
   4724   1.9  christos 	result = dns_message_pseudosectiontotext(msg, DNS_PSEUDOSECTION_OPT,
   4725   1.1  christos 						 style, flags, target);
   4726   1.9  christos 	if (result != ISC_R_SUCCESS) {
   4727  1.19  christos 		return result;
   4728   1.9  christos 	}
   4729   1.1  christos 
   4730   1.9  christos 	result = dns_message_sectiontotext(msg, DNS_SECTION_QUESTION, style,
   4731   1.9  christos 					   flags, target);
   4732   1.9  christos 	if (result != ISC_R_SUCCESS) {
   4733  1.19  christos 		return result;
   4734   1.9  christos 	}
   4735   1.9  christos 
   4736   1.9  christos 	result = dns_message_sectiontotext(msg, DNS_SECTION_ANSWER, style,
   4737   1.9  christos 					   flags, target);
   4738   1.9  christos 	if (result != ISC_R_SUCCESS) {
   4739  1.19  christos 		return result;
   4740   1.9  christos 	}
   4741   1.9  christos 
   4742   1.9  christos 	result = dns_message_sectiontotext(msg, DNS_SECTION_AUTHORITY, style,
   4743   1.9  christos 					   flags, target);
   4744   1.9  christos 	if (result != ISC_R_SUCCESS) {
   4745  1.19  christos 		return result;
   4746   1.9  christos 	}
   4747   1.9  christos 
   4748   1.9  christos 	result = dns_message_sectiontotext(msg, DNS_SECTION_ADDITIONAL, style,
   4749   1.9  christos 					   flags, target);
   4750   1.9  christos 	if (result != ISC_R_SUCCESS) {
   4751  1.19  christos 		return result;
   4752   1.9  christos 	}
   4753   1.1  christos 
   4754   1.9  christos 	result = dns_message_pseudosectiontotext(msg, DNS_PSEUDOSECTION_TSIG,
   4755   1.1  christos 						 style, flags, target);
   4756   1.9  christos 	if (result != ISC_R_SUCCESS) {
   4757  1.19  christos 		return result;
   4758   1.9  christos 	}
   4759   1.1  christos 
   4760   1.9  christos 	result = dns_message_pseudosectiontotext(msg, DNS_PSEUDOSECTION_SIG0,
   4761   1.1  christos 						 style, flags, target);
   4762  1.19  christos 	return result;
   4763   1.1  christos }
   4764   1.1  christos 
   4765   1.1  christos isc_region_t *
   4766   1.1  christos dns_message_getrawmessage(dns_message_t *msg) {
   4767   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   4768  1.19  christos 	return &msg->saved;
   4769   1.1  christos }
   4770   1.1  christos 
   4771   1.1  christos void
   4772   1.1  christos dns_message_setsortorder(dns_message_t *msg, dns_rdatasetorderfunc_t order,
   4773  1.17  christos 			 dns_aclenv_t *env, dns_acl_t *acl,
   4774   1.9  christos 			 const dns_aclelement_t *elem) {
   4775   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   4776   1.1  christos 	REQUIRE((order == NULL) == (env == NULL));
   4777   1.1  christos 	REQUIRE(env == NULL || (acl != NULL || elem != NULL));
   4778   1.1  christos 
   4779   1.1  christos 	msg->order = order;
   4780  1.17  christos 	if (env != NULL) {
   4781  1.17  christos 		dns_aclenv_attach(env, &msg->order_arg.env);
   4782  1.17  christos 	}
   4783  1.17  christos 	if (acl != NULL) {
   4784  1.17  christos 		dns_acl_attach(acl, &msg->order_arg.acl);
   4785  1.17  christos 	}
   4786   1.1  christos 	msg->order_arg.element = elem;
   4787   1.1  christos }
   4788   1.1  christos 
   4789   1.1  christos void
   4790   1.1  christos dns_message_settimeadjust(dns_message_t *msg, int timeadjust) {
   4791   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   4792   1.1  christos 	msg->timeadjust = timeadjust;
   4793   1.1  christos }
   4794   1.1  christos 
   4795   1.1  christos int
   4796   1.1  christos dns_message_gettimeadjust(dns_message_t *msg) {
   4797   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   4798  1.19  christos 	return msg->timeadjust;
   4799   1.1  christos }
   4800   1.1  christos 
   4801   1.1  christos isc_result_t
   4802   1.1  christos dns_opcode_totext(dns_opcode_t opcode, isc_buffer_t *target) {
   4803   1.1  christos 	REQUIRE(opcode < 16);
   4804   1.1  christos 
   4805   1.9  christos 	if (isc_buffer_availablelength(target) < strlen(opcodetext[opcode])) {
   4806  1.19  christos 		return ISC_R_NOSPACE;
   4807   1.9  christos 	}
   4808   1.1  christos 	isc_buffer_putstr(target, opcodetext[opcode]);
   4809  1.19  christos 	return ISC_R_SUCCESS;
   4810   1.1  christos }
   4811   1.1  christos 
   4812   1.1  christos void
   4813   1.9  christos dns_message_logpacket(dns_message_t *message, const char *description,
   4814   1.9  christos 		      const isc_sockaddr_t *address,
   4815   1.1  christos 		      isc_logcategory_t *category, isc_logmodule_t *module,
   4816   1.9  christos 		      int level, isc_mem_t *mctx) {
   4817   1.1  christos 	REQUIRE(address != NULL);
   4818   1.1  christos 
   4819   1.1  christos 	logfmtpacket(message, description, address, category, module,
   4820   1.1  christos 		     &dns_master_style_debug, level, mctx);
   4821   1.1  christos }
   4822   1.1  christos 
   4823   1.1  christos void
   4824   1.9  christos dns_message_logfmtpacket(dns_message_t *message, const char *description,
   4825   1.3  christos 			 const isc_sockaddr_t *address,
   4826   1.1  christos 			 isc_logcategory_t *category, isc_logmodule_t *module,
   4827   1.1  christos 			 const dns_master_style_t *style, int level,
   4828   1.9  christos 			 isc_mem_t *mctx) {
   4829   1.1  christos 	REQUIRE(address != NULL);
   4830   1.1  christos 
   4831   1.1  christos 	logfmtpacket(message, description, address, category, module, style,
   4832   1.1  christos 		     level, mctx);
   4833   1.1  christos }
   4834   1.1  christos 
   4835   1.1  christos static void
   4836   1.1  christos logfmtpacket(dns_message_t *message, const char *description,
   4837   1.1  christos 	     const isc_sockaddr_t *address, isc_logcategory_t *category,
   4838   1.1  christos 	     isc_logmodule_t *module, const dns_master_style_t *style,
   4839   1.9  christos 	     int level, isc_mem_t *mctx) {
   4840   1.1  christos 	char addrbuf[ISC_SOCKADDR_FORMATSIZE] = { 0 };
   4841   1.1  christos 	const char *newline = "\n";
   4842   1.1  christos 	const char *space = " ";
   4843   1.1  christos 	isc_buffer_t buffer;
   4844   1.1  christos 	char *buf = NULL;
   4845   1.1  christos 	int len = 1024;
   4846   1.1  christos 	isc_result_t result;
   4847   1.1  christos 
   4848   1.9  christos 	if (!isc_log_wouldlog(dns_lctx, level)) {
   4849   1.1  christos 		return;
   4850   1.9  christos 	}
   4851   1.1  christos 
   4852   1.1  christos 	/*
   4853   1.1  christos 	 * Note that these are multiline debug messages.  We want a newline
   4854   1.1  christos 	 * to appear in the log after each message.
   4855   1.1  christos 	 */
   4856   1.1  christos 
   4857   1.9  christos 	if (address != NULL) {
   4858   1.1  christos 		isc_sockaddr_format(address, addrbuf, sizeof(addrbuf));
   4859   1.9  christos 	} else {
   4860   1.1  christos 		newline = space = "";
   4861   1.9  christos 	}
   4862   1.1  christos 
   4863   1.1  christos 	do {
   4864   1.1  christos 		buf = isc_mem_get(mctx, len);
   4865   1.1  christos 		isc_buffer_init(&buffer, buf, len);
   4866   1.1  christos 		result = dns_message_totext(message, style, 0, &buffer);
   4867   1.1  christos 		if (result == ISC_R_NOSPACE) {
   4868   1.1  christos 			isc_mem_put(mctx, buf, len);
   4869   1.1  christos 			len += 1024;
   4870   1.9  christos 		} else if (result == ISC_R_SUCCESS) {
   4871   1.1  christos 			isc_log_write(dns_lctx, category, module, level,
   4872   1.1  christos 				      "%s%s%s%s%.*s", description, space,
   4873   1.1  christos 				      addrbuf, newline,
   4874   1.9  christos 				      (int)isc_buffer_usedlength(&buffer), buf);
   4875   1.9  christos 		}
   4876   1.1  christos 	} while (result == ISC_R_NOSPACE);
   4877   1.1  christos 
   4878   1.9  christos 	if (buf != NULL) {
   4879   1.1  christos 		isc_mem_put(mctx, buf, len);
   4880   1.9  christos 	}
   4881   1.1  christos }
   4882   1.1  christos 
   4883   1.1  christos isc_result_t
   4884   1.1  christos dns_message_buildopt(dns_message_t *message, dns_rdataset_t **rdatasetp,
   4885   1.9  christos 		     unsigned int version, uint16_t udpsize, unsigned int flags,
   4886   1.9  christos 		     dns_ednsopt_t *ednsopts, size_t count) {
   4887   1.1  christos 	dns_rdataset_t *rdataset = NULL;
   4888   1.1  christos 	dns_rdatalist_t *rdatalist = NULL;
   4889   1.1  christos 	dns_rdata_t *rdata = NULL;
   4890   1.1  christos 	isc_result_t result;
   4891   1.1  christos 	unsigned int len = 0, i;
   4892   1.1  christos 
   4893   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(message));
   4894   1.1  christos 	REQUIRE(rdatasetp != NULL && *rdatasetp == NULL);
   4895   1.1  christos 
   4896  1.19  christos 	dns_message_gettemprdatalist(message, &rdatalist);
   4897  1.19  christos 	dns_message_gettemprdata(message, &rdata);
   4898  1.19  christos 	dns_message_gettemprdataset(message, &rdataset);
   4899   1.1  christos 
   4900   1.1  christos 	rdatalist->type = dns_rdatatype_opt;
   4901   1.1  christos 
   4902   1.1  christos 	/*
   4903   1.1  christos 	 * Set Maximum UDP buffer size.
   4904   1.1  christos 	 */
   4905   1.1  christos 	rdatalist->rdclass = udpsize;
   4906   1.1  christos 
   4907   1.1  christos 	/*
   4908   1.1  christos 	 * Set EXTENDED-RCODE and Z to 0.
   4909   1.1  christos 	 */
   4910   1.1  christos 	rdatalist->ttl = (version << 16);
   4911   1.1  christos 	rdatalist->ttl |= (flags & 0xffff);
   4912   1.1  christos 
   4913   1.1  christos 	/*
   4914   1.1  christos 	 * Set EDNS options if applicable
   4915   1.1  christos 	 */
   4916   1.1  christos 	if (count != 0U) {
   4917   1.1  christos 		isc_buffer_t *buf = NULL;
   4918   1.3  christos 		bool seenpad = false;
   4919   1.9  christos 		for (i = 0; i < count; i++) {
   4920   1.1  christos 			len += ednsopts[i].length + 4;
   4921   1.9  christos 		}
   4922   1.1  christos 
   4923   1.1  christos 		if (len > 0xffffU) {
   4924   1.1  christos 			result = ISC_R_NOSPACE;
   4925   1.1  christos 			goto cleanup;
   4926   1.1  christos 		}
   4927   1.1  christos 
   4928   1.9  christos 		isc_buffer_allocate(message->mctx, &buf, len);
   4929   1.1  christos 
   4930   1.9  christos 		for (i = 0; i < count; i++) {
   4931   1.1  christos 			if (ednsopts[i].code == DNS_OPT_PAD &&
   4932  1.15  christos 			    ednsopts[i].length == 0U && !seenpad)
   4933  1.15  christos 			{
   4934   1.3  christos 				seenpad = true;
   4935   1.1  christos 				continue;
   4936   1.1  christos 			}
   4937   1.1  christos 			isc_buffer_putuint16(buf, ednsopts[i].code);
   4938   1.1  christos 			isc_buffer_putuint16(buf, ednsopts[i].length);
   4939   1.1  christos 			if (ednsopts[i].length != 0) {
   4940   1.1  christos 				isc_buffer_putmem(buf, ednsopts[i].value,
   4941   1.1  christos 						  ednsopts[i].length);
   4942   1.1  christos 			}
   4943   1.1  christos 		}
   4944   1.1  christos 
   4945   1.1  christos 		/* Padding must be the final option */
   4946   1.1  christos 		if (seenpad) {
   4947   1.1  christos 			isc_buffer_putuint16(buf, DNS_OPT_PAD);
   4948   1.1  christos 			isc_buffer_putuint16(buf, 0);
   4949   1.1  christos 		}
   4950   1.1  christos 		rdata->data = isc_buffer_base(buf);
   4951   1.1  christos 		rdata->length = len;
   4952   1.1  christos 		dns_message_takebuffer(message, &buf);
   4953   1.9  christos 		if (seenpad) {
   4954   1.1  christos 			message->padding_off = len;
   4955   1.9  christos 		}
   4956   1.1  christos 	} else {
   4957   1.1  christos 		rdata->data = NULL;
   4958   1.1  christos 		rdata->length = 0;
   4959   1.1  christos 	}
   4960   1.1  christos 
   4961   1.1  christos 	rdata->rdclass = rdatalist->rdclass;
   4962   1.1  christos 	rdata->type = rdatalist->type;
   4963   1.1  christos 	rdata->flags = 0;
   4964   1.1  christos 
   4965   1.1  christos 	ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
   4966  1.19  christos 	dns_rdatalist_tordataset(rdatalist, rdataset);
   4967   1.1  christos 
   4968   1.1  christos 	*rdatasetp = rdataset;
   4969  1.19  christos 	return ISC_R_SUCCESS;
   4970   1.1  christos 
   4971   1.9  christos cleanup:
   4972  1.19  christos 	dns_message_puttemprdata(message, &rdata);
   4973  1.19  christos 	dns_message_puttemprdataset(message, &rdataset);
   4974  1.19  christos 	dns_message_puttemprdatalist(message, &rdatalist);
   4975  1.19  christos 	return result;
   4976   1.1  christos }
   4977   1.1  christos 
   4978   1.1  christos void
   4979   1.1  christos dns_message_setclass(dns_message_t *msg, dns_rdataclass_t rdclass) {
   4980   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   4981   1.1  christos 	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTPARSE);
   4982   1.1  christos 	REQUIRE(msg->state == DNS_SECTION_ANY);
   4983   1.1  christos 	REQUIRE(msg->rdclass_set == 0);
   4984   1.1  christos 
   4985   1.1  christos 	msg->rdclass = rdclass;
   4986   1.1  christos 	msg->rdclass_set = 1;
   4987   1.1  christos }
   4988   1.1  christos 
   4989   1.1  christos void
   4990   1.3  christos dns_message_setpadding(dns_message_t *msg, uint16_t padding) {
   4991   1.1  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   4992   1.1  christos 
   4993   1.1  christos 	/* Avoid silly large padding */
   4994   1.9  christos 	if (padding > 512) {
   4995   1.1  christos 		padding = 512;
   4996   1.9  christos 	}
   4997   1.1  christos 	msg->padding = padding;
   4998   1.1  christos }
   4999  1.11  christos 
   5000  1.11  christos void
   5001  1.11  christos dns_message_clonebuffer(dns_message_t *msg) {
   5002  1.11  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   5003  1.11  christos 
   5004  1.11  christos 	if (msg->free_saved == 0 && msg->saved.base != NULL) {
   5005  1.11  christos 		msg->saved.base =
   5006  1.11  christos 			memmove(isc_mem_get(msg->mctx, msg->saved.length),
   5007  1.11  christos 				msg->saved.base, msg->saved.length);
   5008  1.11  christos 		msg->free_saved = 1;
   5009  1.11  christos 	}
   5010  1.11  christos 	if (msg->free_query == 0 && msg->query.base != NULL) {
   5011  1.11  christos 		msg->query.base =
   5012  1.11  christos 			memmove(isc_mem_get(msg->mctx, msg->query.length),
   5013  1.11  christos 				msg->query.base, msg->query.length);
   5014  1.11  christos 		msg->free_query = 1;
   5015  1.11  christos 	}
   5016  1.11  christos }
   5017  1.17  christos 
   5018  1.17  christos static isc_result_t
   5019  1.19  christos rdataset_soa_min(dns_rdataset_t *rds, dns_ttl_t *ttlp) {
   5020  1.19  christos 	isc_result_t result;
   5021  1.19  christos 	/* loop over the rdatas */
   5022  1.19  christos 	for (result = dns_rdataset_first(rds); result == ISC_R_SUCCESS;
   5023  1.19  christos 	     result = dns_rdataset_next(rds))
   5024  1.19  christos 	{
   5025  1.19  christos 		dns_name_t tmp;
   5026  1.19  christos 		isc_region_t r = { 0 };
   5027  1.19  christos 		dns_rdata_t rdata = DNS_RDATA_INIT;
   5028  1.19  christos 
   5029  1.19  christos 		dns_rdataset_current(rds, &rdata);
   5030  1.19  christos 
   5031  1.19  christos 		switch (rdata.type) {
   5032  1.19  christos 		case dns_rdatatype_soa:
   5033  1.19  christos 			/* SOA rdataset */
   5034  1.19  christos 			break;
   5035  1.19  christos 		case dns_rdatatype_none:
   5036  1.19  christos 			/*
   5037  1.19  christos 			 * Negative cache rdataset: we need
   5038  1.19  christos 			 * to inspect the rdata to determine
   5039  1.19  christos 			 * whether it's an SOA.
   5040  1.19  christos 			 */
   5041  1.19  christos 			dns_rdata_toregion(&rdata, &r);
   5042  1.19  christos 			dns_name_init(&tmp, NULL);
   5043  1.19  christos 			dns_name_fromregion(&tmp, &r);
   5044  1.19  christos 			isc_region_consume(&r, tmp.length);
   5045  1.19  christos 			if (r.length < 2) {
   5046  1.19  christos 				continue;
   5047  1.19  christos 			}
   5048  1.19  christos 			rdata.type = r.base[0] << 8 | r.base[1];
   5049  1.19  christos 			if (rdata.type != dns_rdatatype_soa) {
   5050  1.19  christos 				continue;
   5051  1.19  christos 			}
   5052  1.19  christos 			break;
   5053  1.19  christos 		default:
   5054  1.19  christos 			continue;
   5055  1.19  christos 		}
   5056  1.19  christos 
   5057  1.19  christos 		if (rdata.type == dns_rdatatype_soa) {
   5058  1.19  christos 			*ttlp = ISC_MIN(rds->ttl, dns_soa_getminimum(&rdata));
   5059  1.19  christos 			return ISC_R_SUCCESS;
   5060  1.19  christos 		}
   5061  1.19  christos 	}
   5062  1.19  christos 
   5063  1.19  christos 	return ISC_R_NOTFOUND;
   5064  1.19  christos }
   5065  1.19  christos 
   5066  1.19  christos static isc_result_t
   5067  1.19  christos message_authority_soa_min(dns_message_t *msg, dns_ttl_t *ttlp) {
   5068  1.17  christos 	isc_result_t result;
   5069  1.17  christos 
   5070  1.17  christos 	if (msg->counts[DNS_SECTION_AUTHORITY] == 0) {
   5071  1.19  christos 		return ISC_R_NOTFOUND;
   5072  1.17  christos 	}
   5073  1.17  christos 
   5074  1.17  christos 	for (result = dns_message_firstname(msg, DNS_SECTION_AUTHORITY);
   5075  1.17  christos 	     result == ISC_R_SUCCESS;
   5076  1.17  christos 	     result = dns_message_nextname(msg, DNS_SECTION_AUTHORITY))
   5077  1.17  christos 	{
   5078  1.19  christos 		dns_name_t *name = NULL;
   5079  1.17  christos 		dns_message_currentname(msg, DNS_SECTION_AUTHORITY, &name);
   5080  1.17  christos 
   5081  1.19  christos 		dns_rdataset_t *rds = NULL;
   5082  1.22  christos 		ISC_LIST_FOREACH(name->list, rds, link) {
   5083  1.19  christos 			if ((rds->attributes & DNS_RDATASETATTR_RENDERED) == 0)
   5084  1.17  christos 			{
   5085  1.17  christos 				continue;
   5086  1.17  christos 			}
   5087  1.17  christos 
   5088  1.19  christos 			result = rdataset_soa_min(rds, ttlp);
   5089  1.19  christos 			if (result == ISC_R_SUCCESS) {
   5090  1.19  christos 				return ISC_R_SUCCESS;
   5091  1.17  christos 			}
   5092  1.17  christos 		}
   5093  1.17  christos 	}
   5094  1.17  christos 
   5095  1.19  christos 	return ISC_R_NOTFOUND;
   5096  1.17  christos }
   5097  1.17  christos 
   5098  1.17  christos isc_result_t
   5099  1.17  christos dns_message_minttl(dns_message_t *msg, const dns_section_t sectionid,
   5100  1.17  christos 		   dns_ttl_t *pttl) {
   5101  1.17  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   5102  1.17  christos 	REQUIRE(pttl != NULL);
   5103  1.17  christos 
   5104  1.17  christos 	if (!msg->minttl[sectionid].is_set) {
   5105  1.19  christos 		return ISC_R_NOTFOUND;
   5106  1.17  christos 	}
   5107  1.17  christos 
   5108  1.17  christos 	*pttl = msg->minttl[sectionid].ttl;
   5109  1.19  christos 	return ISC_R_SUCCESS;
   5110  1.17  christos }
   5111  1.17  christos 
   5112  1.17  christos isc_result_t
   5113  1.17  christos dns_message_response_minttl(dns_message_t *msg, dns_ttl_t *pttl) {
   5114  1.17  christos 	isc_result_t result;
   5115  1.17  christos 
   5116  1.17  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   5117  1.17  christos 	REQUIRE(pttl != NULL);
   5118  1.17  christos 
   5119  1.17  christos 	result = dns_message_minttl(msg, DNS_SECTION_ANSWER, pttl);
   5120  1.17  christos 	if (result != ISC_R_SUCCESS) {
   5121  1.19  christos 		return message_authority_soa_min(msg, pttl);
   5122  1.17  christos 	}
   5123  1.17  christos 
   5124  1.19  christos 	return ISC_R_SUCCESS;
   5125  1.19  christos }
   5126  1.19  christos 
   5127  1.19  christos void
   5128  1.19  christos dns_message_createpools(isc_mem_t *mctx, isc_mempool_t **namepoolp,
   5129  1.19  christos 			isc_mempool_t **rdspoolp) {
   5130  1.19  christos 	REQUIRE(mctx != NULL);
   5131  1.19  christos 	REQUIRE(namepoolp != NULL && *namepoolp == NULL);
   5132  1.19  christos 	REQUIRE(rdspoolp != NULL && *rdspoolp == NULL);
   5133  1.19  christos 
   5134  1.19  christos 	isc_mempool_create(mctx, sizeof(dns_fixedname_t), namepoolp);
   5135  1.19  christos 	isc_mempool_setfillcount(*namepoolp, NAME_FILLCOUNT);
   5136  1.19  christos 	isc_mempool_setfreemax(*namepoolp, NAME_FREEMAX);
   5137  1.19  christos 	isc_mempool_setname(*namepoolp, "dns_fixedname_pool");
   5138  1.19  christos 
   5139  1.19  christos 	isc_mempool_create(mctx, sizeof(dns_rdataset_t), rdspoolp);
   5140  1.19  christos 	isc_mempool_setfillcount(*rdspoolp, RDATASET_FILLCOUNT);
   5141  1.19  christos 	isc_mempool_setfreemax(*rdspoolp, RDATASET_FREEMAX);
   5142  1.19  christos 	isc_mempool_setname(*rdspoolp, "dns_rdataset_pool");
   5143  1.19  christos }
   5144  1.19  christos 
   5145  1.19  christos void
   5146  1.19  christos dns_message_destroypools(isc_mempool_t **namepoolp, isc_mempool_t **rdspoolp) {
   5147  1.19  christos 	REQUIRE(namepoolp != NULL && *namepoolp != NULL);
   5148  1.19  christos 	REQUIRE(rdspoolp != NULL && *rdspoolp != NULL);
   5149  1.19  christos 
   5150  1.19  christos 	ENSURE(isc_mempool_getallocated(*namepoolp) == 0);
   5151  1.19  christos 	ENSURE(isc_mempool_getallocated(*rdspoolp) == 0);
   5152  1.19  christos 
   5153  1.19  christos 	isc_mempool_destroy(rdspoolp);
   5154  1.19  christos 	isc_mempool_destroy(namepoolp);
   5155  1.17  christos }
   5156  1.22  christos 
   5157  1.22  christos bool
   5158  1.22  christos dns_message_hasdname(dns_message_t *msg) {
   5159  1.22  christos 	REQUIRE(DNS_MESSAGE_VALID(msg));
   5160  1.22  christos 	return msg->has_dname;
   5161  1.22  christos }
   5162