From 8781aef52e6d5498d6007df3ca466d7e23641836 Mon Sep 17 00:00:00 2001
From: Diego Fronza <diego@isc.org>
Date: Mon, 21 Sep 2020 16:16:15 -0300
Subject: [PATCH] Refactored dns_message_t for using attach/detach semantics

This commit will be used as a base for the next code updates in order
to have a better control of dns_message_t objects' lifetime.
Conflict: NA
Reference: https://gitlab.isc.org/isc-projects/bind9/-/commit/8781aef52e6d5498d6007df3ca466d7e23641836
---
 bin/dig/dighost.c                        | 28 ++++++++---------
 bin/dig/nslookup.c                       |  2 +-
 bin/named/client.c                       |  4 +--
 bin/named/update.c                       |  2 +-
 bin/named/xfrout.c                       |  2 +-
 bin/nsupdate/nsupdate.c                  | 32 +++++++++----------
 bin/tests/optional/gsstest.c             | 10 +++---
 bin/tests/optional/sig0_test.c           |  4 +--
 bin/tests/system/pipelined/pipequeries.c |  4 +--
 bin/tests/system/tkey/keycreate.c        |  4 +--
 bin/tests/system/tkey/keydelete.c        |  4 +--
 bin/tests/wire_test.c                    |  4 +--
 bin/tools/dnstap-read.c                  |  2 +-
 bin/tools/mdig.c                         |  4 +--
 lib/dns/client.c                         | 14 ++++-----
 lib/dns/dnstap.c                         |  4 +--
 lib/dns/include/dns/message.h            | 25 ++++++++++-----
 lib/dns/message.c                        | 40 ++++++++++++++++++------
 lib/dns/resolver.c                       |  8 ++---
 lib/dns/tests/tsig_test.c                |  8 ++---
 lib/dns/win32/libdns.def.in              |  3 +-
 lib/dns/xfrin.c                          |  8 ++---
 lib/dns/zone.c                           | 32 +++++++++----------
 lib/samples/nsprobe.c                    |  4 +--
 lib/samples/sample-request.c             |  6 ++--
 25 files changed, 144 insertions(+), 114 deletions(-)

diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c
index 4ec6c13a4e..4043aa3bf1 100644
--- a/bin/dig/dighost.c
+++ b/bin/dig/dighost.c
@@ -1907,7 +1907,7 @@ destroy_lookup(dig_lookup_t *lookup) {
 		isc_mem_free(mctx, ptr);
 	}
 	if (lookup->sendmsg != NULL)
-		dns_message_destroy(&lookup->sendmsg);
+		dns_message_detach(&lookup->sendmsg);
 	if (lookup->querysig != NULL) {
 		debug("freeing buffer %p", lookup->querysig);
 		isc_buffer_free(&lookup->querysig);
@@ -4013,7 +4013,7 @@ recv_done(isc_task_t *task, isc_event_t *event) {
 		printf(";; Got bad packet: %s\n", isc_result_totext(result));
 		hex_dump(b);
 		query->waiting_connect = false;
-		dns_message_destroy(&msg);
+		dns_message_detach(&msg);
 		isc_event_free(&event);
 		clear_query(query);
 		cancel_lookup(l);
@@ -4036,7 +4036,7 @@ recv_done(isc_task_t *task, isc_event_t *event) {
 		printf(";; Warning: Opcode mismatch: expected %s, got %s",
 		       expect, got);
 
-		dns_message_destroy(&msg);
+		dns_message_detach(&msg);
 		if (l->tcp_mode) {
 			isc_event_free(&event);
 			clear_query(query);
@@ -4083,7 +4083,7 @@ recv_done(isc_task_t *task, isc_event_t *event) {
 			}
 		}
 		if (!match) {
-			dns_message_destroy(&msg);
+			dns_message_detach(&msg);
 			if (l->tcp_mode) {
 				isc_event_free(&event);
 				clear_query(query);
@@ -4107,7 +4107,7 @@ recv_done(isc_task_t *task, isc_event_t *event) {
 		n = requeue_lookup(l, true);
 		if (l->trace && l->trace_root)
 			n->rdtype = l->qrdtype;
-		dns_message_destroy(&msg);
+		dns_message_detach(&msg);
 		isc_event_free(&event);
 		clear_query(query);
 		cancel_lookup(l);
@@ -4125,7 +4125,7 @@ recv_done(isc_task_t *task, isc_event_t *event) {
 		n->tcp_mode = true;
 		if (l->trace && l->trace_root)
 			n->rdtype = l->qrdtype;
-		dns_message_destroy(&msg);
+		dns_message_detach(&msg);
 		isc_event_free(&event);
 		clear_query(query);
 		cancel_lookup(l);
@@ -4146,7 +4146,7 @@ recv_done(isc_task_t *task, isc_event_t *event) {
 			n->seenbadcookie = true;
 			if (l->trace && l->trace_root)
 				n->rdtype = l->qrdtype;
-			dns_message_destroy(&msg);
+			dns_message_detach(&msg);
 			isc_event_free(&event);
 			clear_query(query);
 			cancel_lookup(l);
@@ -4185,7 +4185,7 @@ recv_done(isc_task_t *task, isc_event_t *event) {
 				       query->servname);
 			clear_query(query);
 			check_next_lookup(l);
-			dns_message_destroy(&msg);
+			dns_message_detach(&msg);
 			isc_event_free(&event);
 			UNLOCK_LOOKUP;
 			return;
@@ -4360,7 +4360,7 @@ recv_done(isc_task_t *task, isc_event_t *event) {
 		debug("still pending.");
 	if (l->doing_xfr) {
 		if (query != l->xfr_q) {
-			dns_message_destroy(&msg);
+			dns_message_detach(&msg);
 			isc_event_free(&event);
 			query->waiting_connect = false;
 			UNLOCK_LOOKUP;
@@ -4369,7 +4369,7 @@ recv_done(isc_task_t *task, isc_event_t *event) {
 		if (!docancel)
 			docancel = check_for_more_data(query, msg, sevent);
 		if (docancel) {
-			dns_message_destroy(&msg);
+			dns_message_detach(&msg);
 			clear_query(query);
 			cancel_lookup(l);
 			check_next_lookup(l);
@@ -4391,7 +4391,7 @@ recv_done(isc_task_t *task, isc_event_t *event) {
 #ifdef DIG_SIGCHASE
 			if (!do_sigchase)
 #endif
-				dns_message_destroy(&msg);
+				dns_message_detach(&msg);
 
 			cancel_lookup(l);
 		}
@@ -4404,7 +4404,7 @@ recv_done(isc_task_t *task, isc_event_t *event) {
 			msg = NULL;
 		else
 #endif
-			dns_message_destroy(&msg);
+			dns_message_detach(&msg);
 	}
 	isc_event_free(&event);
 	UNLOCK_LOOKUP;
@@ -4658,7 +4658,7 @@ destroy_libs(void) {
 
 	while (chase_msg != NULL) {
 		INSIST(chase_msg->msg != NULL);
-		dns_message_destroy(&(chase_msg->msg));
+		dns_message_detach(&(chase_msg->msg));
 		ptr = chase_msg;
 		chase_msg = ISC_LIST_NEXT(chase_msg, link);
 		isc_mem_free(mctx, ptr);
@@ -4668,7 +4668,7 @@ destroy_libs(void) {
 
 	while (chase_msg != NULL) {
 		INSIST(chase_msg->msg != NULL);
-		dns_message_destroy(&(chase_msg->msg));
+		dns_message_detach(&(chase_msg->msg));
 		ptr = chase_msg;
 		chase_msg = ISC_LIST_NEXT(chase_msg, link);
 		isc_mem_free(mctx, ptr);
diff --git a/bin/dig/nslookup.c b/bin/dig/nslookup.c
index 647ed8ce50..c35283177d 100644
--- a/bin/dig/nslookup.c
+++ b/bin/dig/nslookup.c
@@ -983,7 +983,7 @@ flush_lookup_list(void) {
 
 		}
 		if (l->sendmsg != NULL)
-			dns_message_destroy(&l->sendmsg);
+			dns_message_detach(&l->sendmsg);
 		lp = l;
 		l = ISC_LIST_NEXT(l, link);
 		ISC_LIST_DEQUEUE(lookup_list, lp, link);
diff --git a/bin/named/client.c b/bin/named/client.c
index 95dabbf53b..4a50ad9bae 100644
--- a/bin/named/client.c
+++ b/bin/named/client.c
@@ -758,7 +758,7 @@ exit_check(ns_client_t *client) {
 			client->keytag_len = 0;
 		}
 
-		dns_message_destroy(&client->message);
+		dns_message_detach(&client->message);
 
 		/*
 		 * Detaching the task must be done after unlinking from
@@ -3375,7 +3375,7 @@ client_create(ns_clientmgr_t *manager, ns_client_t **clientp) {
 	client->magic = 0;
 
  cleanup_message:
-	dns_message_destroy(&client->message);
+	dns_message_detach(&client->message);
 
  cleanup_timer:
 	isc_timer_detach(&client->timer);
diff --git a/bin/named/update.c b/bin/named/update.c
index 82f9d4a13d..ffd2caf7e4 100644
--- a/bin/named/update.c
+++ b/bin/named/update.c
@@ -3440,7 +3440,7 @@ forward_done(isc_task_t *task, isc_event_t *event) {
 	INSIST(client->nupdates > 0);
 	client->nupdates--;
 	ns_client_sendraw(client, uev->answer);
-	dns_message_destroy(&uev->answer);
+	dns_message_detach(&uev->answer);
 	isc_event_free(&event);
 	ns_client_detach(&client);
 }
diff --git a/bin/named/xfrout.c b/bin/named/xfrout.c
index 1e139f7a34..7149825a3c 100644
--- a/bin/named/xfrout.c
+++ b/bin/named/xfrout.c
@@ -1580,7 +1580,7 @@ sendstream(xfrout_ctx_t *xfr) {
 	}
 
 	if (tcpmsg != NULL)
-		dns_message_destroy(&tcpmsg);
+		dns_message_detach(&tcpmsg);
 
 	if (cleanup_cctx)
 		dns_compress_invalidate(&cctx);
diff --git a/bin/nsupdate/nsupdate.c b/bin/nsupdate/nsupdate.c
index bf5ec8fe6b..4993265f58 100644
--- a/bin/nsupdate/nsupdate.c
+++ b/bin/nsupdate/nsupdate.c
@@ -807,7 +807,7 @@ doshutdown(void) {
 	}
 
 	if (updatemsg != NULL)
-		dns_message_destroy(&updatemsg);
+		dns_message_detach(&updatemsg);
 
 	if (is_dst_up) {
 		ddebug("Destroy DST lib");
@@ -2556,7 +2556,7 @@ recvsoa(isc_task_t *task, isc_event_t *event) {
 
 	if (shuttingdown) {
 		dns_request_destroy(&request);
-		dns_message_destroy(&soaquery);
+		dns_message_detach(&soaquery);
 		isc_mem_put(gmctx, reqinfo, sizeof(nsu_requestinfo_t));
 		isc_event_free(&event);
 		maybeshutdown();
@@ -2587,7 +2587,7 @@ recvsoa(isc_task_t *task, isc_event_t *event) {
 	result = dns_request_getresponse(request, rcvmsg,
 					 DNS_MESSAGEPARSE_PRESERVEORDER);
 	if (result == DNS_R_TSIGERRORSET && servers != NULL) {
-		dns_message_destroy(&rcvmsg);
+		dns_message_detach(&rcvmsg);
 		ddebug("Destroying request [%p]", request);
 		dns_request_destroy(&request);
 		reqinfo = isc_mem_get(gmctx, sizeof(nsu_requestinfo_t));
@@ -2628,9 +2628,9 @@ recvsoa(isc_task_t *task, isc_event_t *event) {
 		dns_name_format(userzone, namebuf, sizeof(namebuf));
 		error("specified zone '%s' does not exist (NXDOMAIN)",
 		      namebuf);
-		dns_message_destroy(&rcvmsg);
+		dns_message_detach(&rcvmsg);
 		dns_request_destroy(&request);
-		dns_message_destroy(&soaquery);
+		dns_message_detach(&soaquery);
 		ddebug("Out of recvsoa");
 		done_update();
 		seenerror = true;
@@ -2760,11 +2760,11 @@ recvsoa(isc_task_t *task, isc_event_t *event) {
 	setzoneclass(dns_rdataclass_none);
 #endif
 
-	dns_message_destroy(&soaquery);
+	dns_message_detach(&soaquery);
 	dns_request_destroy(&request);
 
  out:
-	dns_message_destroy(&rcvmsg);
+	dns_message_detach(&rcvmsg);
 	ddebug("Out of recvsoa");
 	return;
 
@@ -2973,7 +2973,7 @@ start_gssrequest(dns_name_t *master) {
 
 failure:
 	if (rmsg != NULL)
-		dns_message_destroy(&rmsg);
+		dns_message_detach(&rmsg);
 	if (err_message != NULL)
 		isc_mem_free(gmctx, err_message);
 	failed_gssrequest();
@@ -3047,7 +3047,7 @@ recvgss(isc_task_t *task, isc_event_t *event) {
 
 	if (shuttingdown) {
 		dns_request_destroy(&request);
-		dns_message_destroy(&tsigquery);
+		dns_message_detach(&tsigquery);
 		isc_mem_put(gmctx, reqinfo, sizeof(nsu_gssinfo_t));
 		isc_event_free(&event);
 		maybeshutdown();
@@ -3058,7 +3058,7 @@ recvgss(isc_task_t *task, isc_event_t *event) {
 		ddebug("Destroying request [%p]", request);
 		dns_request_destroy(&request);
 		if (!next_master("recvgss", addr, eresult)) {
-			dns_message_destroy(&tsigquery);
+			dns_message_detach(&tsigquery);
 			failed_gssrequest();
 		} else {
 			dns_message_renderreset(tsigquery);
@@ -3117,7 +3117,7 @@ recvgss(isc_task_t *task, isc_event_t *event) {
 	switch (result) {
 
 	case DNS_R_CONTINUE:
-		dns_message_destroy(&rcvmsg);
+		dns_message_detach(&rcvmsg);
 		dns_request_destroy(&request);
 		send_gssrequest(kserver, tsigquery, &request, context);
 		ddebug("Out of recvgss");
@@ -3165,9 +3165,9 @@ recvgss(isc_task_t *task, isc_event_t *event) {
 
  done:
 	dns_request_destroy(&request);
-	dns_message_destroy(&tsigquery);
+	dns_message_detach(&tsigquery);
 
-	dns_message_destroy(&rcvmsg);
+	dns_message_detach(&rcvmsg);
 	ddebug("Out of recvgss");
 }
 #endif
@@ -3186,7 +3186,7 @@ start_update(void) {
 
 	LOCK(&answer_lock);
 	if (answer != NULL) {
-		dns_message_destroy(&answer);
+		dns_message_detach(&answer);
 	}
 	UNLOCK(&answer_lock);
 
@@ -3231,7 +3231,7 @@ start_update(void) {
 			dns_message_puttempname(soaquery, &name);
 			dns_rdataset_disassociate(rdataset);
 			dns_message_puttemprdataset(soaquery, &rdataset);
-			dns_message_destroy(&soaquery);
+			dns_message_detach(&soaquery);
 			done_update();
 			return;
 		}
@@ -3268,7 +3268,7 @@ cleanup(void) {
 
 	LOCK(&answer_lock);
 	if (answer != NULL) {
-		dns_message_destroy(&answer);
+		dns_message_detach(&answer);
 	}
 	UNLOCK(&answer_lock);
 
diff --git a/bin/tests/optional/gsstest.c b/bin/tests/optional/gsstest.c
index 9692c6f42e..901c2bbb91 100644
--- a/bin/tests/optional/gsstest.c
+++ b/bin/tests/optional/gsstest.c
@@ -157,11 +157,11 @@ recvresponse(isc_task_t *task, isc_event_t *event) {
 	CHECK("dns_request_getresponse", result2);
 
 	if (response != NULL)
-		dns_message_destroy(&response);
+		dns_message_detach(&response);
 
  end:
 	if (query != NULL)
-		dns_message_destroy(&query);
+		dns_message_detach(&query);
 
 	if (reqev->request != NULL)
 		dns_request_destroy(&reqev->request);
@@ -248,7 +248,7 @@ sendquery(isc_task_t *task, isc_event_t *event)
 	if (qrdataset != NULL)
 		dns_message_puttemprdataset(message, &qrdataset);
 	if (message != NULL)
-		dns_message_destroy(&message);
+		dns_message_detach(&message);
 }
 
 static void
@@ -314,11 +314,11 @@ initctx2(isc_task_t *task, isc_event_t *event) {
 		tsigkey = NULL;
 	}
 
-	dns_message_destroy(&response);
+	dns_message_detach(&response);
 
  end:
 	if (query != NULL)
-		dns_message_destroy(&query);
+		dns_message_detach(&query);
 
 	if (reqev->request != NULL)
 		dns_request_destroy(&reqev->request);
diff --git a/bin/tests/optional/sig0_test.c b/bin/tests/optional/sig0_test.c
index 4084f226e5..a74bea1ba2 100644
--- a/bin/tests/optional/sig0_test.c
+++ b/bin/tests/optional/sig0_test.c
@@ -114,7 +114,7 @@ recvdone(isc_task_t *task, isc_event_t *event) {
 	printf("%.*s\n", (int)isc_buffer_usedlength(&outbuf),
 	       (char *)isc_buffer_base(&outbuf));
 
-	dns_message_destroy(&response);
+	dns_message_detach(&response);
 	isc_event_free(&event);
 
 	isc_app_shutdown();
@@ -190,7 +190,7 @@ buildquery(void) {
 	inr.length = sizeof(rdata);
 	result = isc_socket_recv(s, &inr, 1, task1, recvdone, NULL);
 	CHECK("isc_socket_recv", result);
-	dns_message_destroy(&query);
+	dns_message_detach(&query);
 }
 
 int
diff --git a/bin/tests/system/pipelined/pipequeries.c b/bin/tests/system/pipelined/pipequeries.c
index 63ee7e9d2f..e16ec11681 100644
--- a/bin/tests/system/pipelined/pipequeries.c
+++ b/bin/tests/system/pipelined/pipequeries.c
@@ -117,8 +117,8 @@ recvresponse(isc_task_t *task, isc_event_t *event) {
 	       (char *)isc_buffer_base(&outbuf));
 	fflush(stdout);
 
-	dns_message_destroy(&query);
-	dns_message_destroy(&response);
+	dns_message_detach(&query);
+	dns_message_detach(&response);
 	dns_request_destroy(&reqev->request);
 	isc_event_free(&event);
 
diff --git a/bin/tests/system/tkey/keycreate.c b/bin/tests/system/tkey/keycreate.c
index 0c0b08a90b..85b497f280 100644
--- a/bin/tests/system/tkey/keycreate.c
+++ b/bin/tests/system/tkey/keycreate.c
@@ -117,8 +117,8 @@ recvquery(isc_task_t *task, isc_event_t *event) {
 	result = dst_key_tofile(tsigkey->key, type, "");
 	CHECK("dst_key_tofile", result);
 
-	dns_message_destroy(&query);
-	dns_message_destroy(&response);
+	dns_message_detach(&query);
+	dns_message_detach(&response);
 	dns_request_destroy(&reqev->request);
 	isc_event_free(&event);
 	isc_app_shutdown();
diff --git a/bin/tests/system/tkey/keydelete.c b/bin/tests/system/tkey/keydelete.c
index ae26675755..ea7a09b014 100644
--- a/bin/tests/system/tkey/keydelete.c
+++ b/bin/tests/system/tkey/keydelete.c
@@ -96,8 +96,8 @@ recvquery(isc_task_t *task, isc_event_t *event) {
 	result = dns_tkey_processdeleteresponse(query, response, ring);
 	CHECK("dns_tkey_processdhresponse", result);
 
-	dns_message_destroy(&query);
-	dns_message_destroy(&response);
+	dns_message_detach(&query);
+	dns_message_detach(&response);
 	dns_request_destroy(&reqev->request);
 	isc_event_free(&event);
 	isc_app_shutdown();
diff --git a/bin/tests/wire_test.c b/bin/tests/wire_test.c
index 0d001a2f77..7fb8446375 100644
--- a/bin/tests/wire_test.c
+++ b/bin/tests/wire_test.c
@@ -328,7 +328,7 @@ process_message(isc_buffer_t *source) {
 		dns_compress_invalidate(&cctx);
 
 		message->from_to_wire = DNS_MESSAGE_INTENTPARSE;
-		dns_message_destroy(&message);
+		dns_message_detach(&message);
 
 		printf("Message rendered.\n");
 		if (printmemstats)
@@ -344,5 +344,5 @@ process_message(isc_buffer_t *source) {
 		result = printmessage(message);
 		CHECKRESULT(result, "printmessage() failed");
 	}
-	dns_message_destroy(&message);
+	dns_message_detach(&message);
 }
diff --git a/bin/tools/dnstap-read.c b/bin/tools/dnstap-read.c
index 97c2f71346..cf0c884c02 100644
--- a/bin/tools/dnstap-read.c
+++ b/bin/tools/dnstap-read.c
@@ -362,7 +362,7 @@ main(int argc, char *argv[]) {
 	if (handle != NULL)
 		dns_dt_close(&handle);
 	if (message != NULL)
-		dns_message_destroy(&message);
+		dns_message_detach(&message);
 	if (b != NULL)
 		isc_buffer_free(&b);
 	isc_mem_destroy(&mctx);
diff --git a/bin/tools/mdig.c b/bin/tools/mdig.c
index 9a105cbd83..4dd12ee6f4 100644
--- a/bin/tools/mdig.c
+++ b/bin/tools/mdig.c
@@ -478,9 +478,9 @@ cleanup:
 	if (style != NULL)
 		dns_master_styledestroy(&style, mctx);
 	if (query != NULL)
-		dns_message_destroy(&query);
+		dns_message_detach(&query);
 	if (response != NULL)
-		dns_message_destroy(&response);
+		dns_message_detach(&response);
 	dns_request_destroy(&reqev->request);
 	isc_event_free(&event);
 
diff --git a/lib/dns/client.c b/lib/dns/client.c
index 6e2b1fe348..c2dca94bad 100644
--- a/lib/dns/client.c
+++ b/lib/dns/client.c
@@ -1961,7 +1961,7 @@ static void
 update_sendevent(updatectx_t *uctx, isc_result_t result) {
 	isc_task_t *task;
 
-	dns_message_destroy(&uctx->updatemsg);
+	dns_message_detach(&uctx->updatemsg);
 	if (uctx->tsigkey != NULL)
 		dns_tsigkey_detach(&uctx->tsigkey);
 	if (uctx->sig0key != NULL)
@@ -2012,7 +2012,7 @@ update_done(isc_task_t *task, isc_event_t *event) {
 
  out:
 	if (answer != NULL)
-		dns_message_destroy(&answer);
+		dns_message_detach(&answer);
 	isc_event_free(&event);
 
 	LOCK(&uctx->lock);
@@ -2354,7 +2354,7 @@ receive_soa(isc_task_t *task, isc_event_t *event) {
 		dns_request_t *newrequest = NULL;
 
 		/* Retry SOA request without TSIG */
-		dns_message_destroy(&rcvmsg);
+		dns_message_detach(&rcvmsg);
 		dns_message_renderreset(uctx->soaquery);
 		reqoptions = 0;
 		if (uctx->want_tcp)
@@ -2477,14 +2477,14 @@ receive_soa(isc_task_t *task, isc_event_t *event) {
 	}
 
 	if (!droplabel || result != ISC_R_SUCCESS) {
-		dns_message_destroy(&uctx->soaquery);
+		dns_message_detach(&uctx->soaquery);
 		LOCK(&uctx->lock);
 		dns_request_destroy(&uctx->soareq);
 		UNLOCK(&uctx->lock);
 	}
 
 	if (rcvmsg != NULL)
-		dns_message_destroy(&rcvmsg);
+		dns_message_detach(&rcvmsg);
 
 	if (result != ISC_R_SUCCESS)
 		update_sendevent(uctx, result);
@@ -2541,7 +2541,7 @@ request_soa(updatectx_t *uctx) {
 	}
 	if (name != NULL)
 		dns_message_puttempname(soaquery, &name);
-	dns_message_destroy(&soaquery);
+	dns_message_detach(&soaquery);
 
 	return (result);
 }
@@ -3040,7 +3040,7 @@ dns_client_startupdate(dns_client_t *client, dns_rdataclass_t rdclass,
 		UNLOCK(&client->lock);
 	}
 	if (uctx->updatemsg != NULL)
-		dns_message_destroy(&uctx->updatemsg);
+		dns_message_detach(&uctx->updatemsg);
 	while ((sa = ISC_LIST_HEAD(uctx->servers)) != NULL) {
 		ISC_LIST_UNLINK(uctx->servers, sa, link);
 		isc_mem_put(client->mctx, sa, sizeof(*sa));
diff --git a/lib/dns/dnstap.c b/lib/dns/dnstap.c
index 4b289a9ea7..fad1e3b040 100644
--- a/lib/dns/dnstap.c
+++ b/lib/dns/dnstap.c
@@ -1041,7 +1041,7 @@ dns_dt_parse(isc_mem_t *mctx, isc_region_t *src, dns_dtdata_t **destp) {
 	result = dns_message_parse(d->msg, &b, 0);
 	if (result != ISC_R_SUCCESS) {
 		if (result != DNS_R_RECOVERABLE)
-			dns_message_destroy(&d->msg);
+			dns_message_detach(&d->msg);
 		result = ISC_R_SUCCESS;
 	}
 
@@ -1248,7 +1248,7 @@ dns_dtdata_free(dns_dtdata_t **dp) {
 	d = *dp;
 
 	if (d->msg != NULL)
-		dns_message_destroy(&d->msg);
+		dns_message_detach(&d->msg);
 	if (d->frame != NULL)
 		dnstap__dnstap__free_unpacked(d->frame, NULL);
 
diff --git a/lib/dns/include/dns/message.h b/lib/dns/include/dns/message.h
index 58de67a8af..f64522b43a 100644
--- a/lib/dns/include/dns/message.h
+++ b/lib/dns/include/dns/message.h
@@ -21,6 +21,7 @@
 
 #include <isc/lang.h>
 #include <isc/magic.h>
+#include <isc/refcount.h>
 
 #include <dns/compress.h>
 #include <dns/masterdump.h>
@@ -194,6 +195,7 @@ typedef struct dns_msgblock dns_msgblock_t;
 struct dns_message {
 	/* public from here down */
 	unsigned int			magic;
+	isc_refcount_t			refcount;
 
 	dns_messageid_t			id;
 	unsigned int			flags;
@@ -310,8 +312,8 @@ dns_message_reset(dns_message_t *msg, unsigned int intent);
 /*%<
  * Reset a message structure to default state.  All internal lists are freed
  * or reset to a default state as well.  This is simply a more efficient
- * way to call dns_message_destroy() followed by dns_message_allocate(),
- * since it avoid many memory allocations.
+ * way to call dns_message_detach() (assuming last reference is hold),
+ * followed by dns_message_create(), since it avoid many memory allocations.
  *
  * If any data loanouts (buffers, names, rdatas, etc) were requested,
  * the caller must no longer use them after this call.
@@ -326,16 +328,23 @@ dns_message_reset(dns_message_t *msg, unsigned int intent);
  */
 
 void
-dns_message_destroy(dns_message_t **msgp);
+dns_message_attach(dns_message_t *source, dns_message_t **target);
 /*%<
- * Destroy all state in the message.
+ * Attach to message 'source'.
  *
  * Requires:
+ *\li	'source' to be a valid message.
+ *\li	'target' to be non NULL and '*target' to be NULL.
+ */
+
+void
+dns_message_detach(dns_message_t **messagep);
+/*%<
+ * Detach *messagep from its message.
+ * list.
  *
- *\li	'msgp' be valid.
- *
- * Ensures:
- *\li	'*msgp' == NULL
+ * Requires:
+ *\li	'*messagep' to be a valid message.
  */
 
 isc_result_t
diff --git a/lib/dns/message.c b/lib/dns/message.c
index 6a6d09e219..2812ab5a37 100644
--- a/lib/dns/message.c
+++ b/lib/dns/message.c
@@ -23,6 +23,7 @@
 #include <isc/buffer.h>
 #include <isc/mem.h>
 #include <isc/print.h>
+#include <isc/refcount.h>
 #include <isc/string.h> /* Required for HP/UX (and others?) */
 #include <isc/utf8.h>
 #include <isc/util.h>
@@ -554,7 +555,7 @@ msgresetsigs(dns_message_t *msg, bool replying) {
 
 /*
  * Free all but one (or everything) for this message.  This is used by
- * both dns_message_reset() and dns_message_destroy().
+ * both dns_message_reset() and dns__message_destroy().
  */
 static void
 msgreset(dns_message_t *msg, bool everything) {
@@ -794,6 +795,8 @@ dns_message_create(isc_mem_t *mctx, unsigned int intent, dns_message_t **msgp)
 
 	m->cctx = NULL;
 
+	isc_refcount_init(&m->refcount, 1);
+
 	*msgp = m;
 	return (ISC_R_SUCCESS);
 
@@ -826,23 +829,40 @@ dns_message_reset(dns_message_t *msg, unsigned int intent) {
 	msg->from_to_wire = intent;
 }
 
-void
-dns_message_destroy(dns_message_t **msgp) {
-	dns_message_t *msg;
-
-	REQUIRE(msgp != NULL);
-	REQUIRE(DNS_MESSAGE_VALID(*msgp));
-
-	msg = *msgp;
-	*msgp = NULL;
+static void
+dns__message_destroy(dns_message_t *msg) {
+	REQUIRE(msg != NULL);
+	REQUIRE(DNS_MESSAGE_VALID(msg));
 
 	msgreset(msg, true);
 	isc_mempool_destroy(&msg->namepool);
 	isc_mempool_destroy(&msg->rdspool);
+	isc_refcount_destroy(&msg->refcount);
 	msg->magic = 0;
 	isc_mem_putanddetach(&msg->mctx, msg, sizeof(dns_message_t));
 }
 
+void
+dns_message_attach(dns_message_t *source, dns_message_t **target) {
+	REQUIRE(DNS_MESSAGE_VALID(source));
+
+	isc_refcount_increment(&source->refcount, NULL);
+	*target = source;
+}
+
+void
+dns_message_detach(dns_message_t **messagep) {
+	REQUIRE(messagep != NULL && DNS_MESSAGE_VALID(*messagep));
+	dns_message_t *msg = *messagep;
+	*messagep = NULL;
+	int32_t refs;
+
+	isc_refcount_decrement(&msg->refcount, &refs);
+	if (refs == 0) {
+		dns__message_destroy(msg);
+	}
+}
+
 static isc_result_t
 findname(dns_name_t **foundname, dns_name_t *target,
 	 dns_namelist_t *section)
diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c
index e67393aba9..daf9a277ec 100644
--- a/lib/dns/resolver.c
+++ b/lib/dns/resolver.c
@@ -3966,8 +3966,8 @@ fctx_destroy(fetchctx_t *fctx) {
 	isc_counter_detach(&fctx->qc);
 	fcount_decr(fctx);
 	isc_timer_detach(&fctx->timer);
-	dns_message_destroy(&fctx->rmessage);
-	dns_message_destroy(&fctx->qmessage);
+	dns_message_detach(&fctx->rmessage);
+	dns_message_detach(&fctx->qmessage);
 	if (dns_name_countlabels(&fctx->domain) > 0)
 		dns_name_free(&fctx->domain, fctx->mctx);
 	if (dns_rdataset_isassociated(&fctx->nameservers))
@@ -4578,10 +4578,10 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type,
 	return (ISC_R_SUCCESS);
 
  cleanup_rmessage:
-	dns_message_destroy(&fctx->rmessage);
+	dns_message_detach(&fctx->rmessage);
 
  cleanup_qmessage:
-	dns_message_destroy(&fctx->qmessage);
+	dns_message_detach(&fctx->qmessage);
 
  cleanup_fcount:
 	fcount_decr(fctx);
diff --git a/lib/dns/tests/tsig_test.c b/lib/dns/tests/tsig_test.c
index 112a0467f8..ff0c99f7f6 100644
--- a/lib/dns/tests/tsig_test.c
+++ b/lib/dns/tests/tsig_test.c
@@ -261,7 +261,7 @@ render(isc_buffer_t *buf, unsigned flags, dns_tsigkey_t *key,
 	}
 
 	dns_compress_invalidate(&cctx);
-	dns_message_destroy(&msg);
+	dns_message_detach(&msg);
 }
 
 /*
@@ -354,7 +354,7 @@ tsig_tcp_test(void **state) {
 	tsigctx = msg->tsigctx;
 	msg->tsigctx = NULL;
 	isc_buffer_free(&buf);
-	dns_message_destroy(&msg);
+	dns_message_detach(&msg);
 
 	result = dst_context_create3(key->key, mctx, DNS_LOGCATEGORY_DNSSEC,
 				     false, &outctx);
@@ -412,7 +412,7 @@ tsig_tcp_test(void **state) {
 	tsigctx = msg->tsigctx;
 	msg->tsigctx = NULL;
 	isc_buffer_free(&buf);
-	dns_message_destroy(&msg);
+	dns_message_detach(&msg);
 
 	/*
 	 * Create response message 3.
@@ -464,7 +464,7 @@ tsig_tcp_test(void **state) {
 	assert_int_equal(result, ISC_R_SUCCESS);
 
 	isc_buffer_free(&buf);
-	dns_message_destroy(&msg);
+	dns_message_detach(&msg);
 
 	if (outctx != NULL) {
 		dst_context_destroy(&outctx);
diff --git a/lib/dns/win32/libdns.def.in b/lib/dns/win32/libdns.def.in
index b426f150dd..9c2ef79479 100644
--- a/lib/dns/win32/libdns.def.in
+++ b/lib/dns/win32/libdns.def.in
@@ -514,11 +514,12 @@ dns_master_stylecreate2
 dns_master_styledestroy
 dns_master_styleflags
 dns_message_addname
+dns_message_attach
 dns_message_buildopt
 dns_message_checksig
 dns_message_create
 dns_message_currentname
-dns_message_destroy
+dns_message_detach
 dns_message_find
 dns_message_findname
 dns_message_findtype
diff --git a/lib/dns/xfrin.c b/lib/dns/xfrin.c
index 1419d523b0..3a3f407289 100644
--- a/lib/dns/xfrin.c
+++ b/lib/dns/xfrin.c
@@ -1198,7 +1198,7 @@ xfrin_send_request(dns_xfrin_ctx_t *xfr) {
 	if (qrdataset != NULL)
 		dns_message_puttemprdataset(msg, &qrdataset);
 	if (msg != NULL)
-		dns_message_destroy(&msg);
+		dns_message_detach(&msg);
 	if (soatuple != NULL)
 		dns_difftuple_free(&soatuple);
 	if (ver != NULL)
@@ -1307,7 +1307,7 @@ xfrin_recv_done(isc_task_t *task, isc_event_t *ev) {
 		xfrin_log(xfr, ISC_LOG_DEBUG(3), "got %s, retrying with AXFR",
 		       isc_result_totext(result));
  try_axfr:
-		dns_message_destroy(&msg);
+		dns_message_detach(&msg);
 		xfrin_reset(xfr);
 		xfr->reqtype = dns_rdatatype_soa;
 		xfr->state = XFRST_SOAQUERY;
@@ -1419,7 +1419,7 @@ xfrin_recv_done(isc_task_t *task, isc_event_t *ev) {
 	xfr->tsigctx = msg->tsigctx;
 	msg->tsigctx = NULL;
 
-	dns_message_destroy(&msg);
+	dns_message_detach(&msg);
 
 	switch (xfr->state) {
 	case XFRST_GOTSOA:
@@ -1464,7 +1464,7 @@ xfrin_recv_done(isc_task_t *task, isc_event_t *ev) {
 
  failure:
 	if (msg != NULL)
-		dns_message_destroy(&msg);
+		dns_message_detach(&msg);
 	if (result != ISC_R_SUCCESS)
 		xfrin_fail(xfr, result, "failed while receiving responses");
 }
diff --git a/lib/dns/zone.c b/lib/dns/zone.c
index fd634af65f..6f40ebd636 100644
--- a/lib/dns/zone.c
+++ b/lib/dns/zone.c
@@ -11338,7 +11338,7 @@ notify_send_toaddr(isc_task_t *task, isc_event_t *event) {
 	if (key != NULL)
 		dns_tsigkey_detach(&key);
  cleanup_message:
-	dns_message_destroy(&message);
+	dns_message_detach(&message);
  cleanup:
 	UNLOCK_ZONE(notify->zone);
 	isc_event_free(&event);
@@ -11887,7 +11887,7 @@ stub_callback(isc_task_t *task, isc_event_t *event) {
 	ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_write);
 	dns_db_detach(&stub->db);
 
-	dns_message_destroy(&msg);
+	dns_message_detach(&msg);
 	isc_event_free(&event);
 	dns_request_destroy(&zone->request);
 
@@ -11909,7 +11909,7 @@ stub_callback(isc_task_t *task, isc_event_t *event) {
 	if (stub->db != NULL)
 		dns_db_detach(&stub->db);
 	if (msg != NULL)
-		dns_message_destroy(&msg);
+		dns_message_detach(&msg);
 	isc_event_free(&event);
 	dns_request_destroy(&zone->request);
 	/*
@@ -11956,7 +11956,7 @@ stub_callback(isc_task_t *task, isc_event_t *event) {
 
  same_master:
 	if (msg != NULL)
-		dns_message_destroy(&msg);
+		dns_message_detach(&msg);
 	isc_event_free(&event);
 	dns_request_destroy(&zone->request);
 	ns_query(zone, NULL, stub);
@@ -12352,7 +12352,7 @@ refresh_callback(isc_task_t *task, isc_event_t *event) {
 			ns_query(zone, rdataset, NULL);
 		}
 		if (msg != NULL)
-			dns_message_destroy(&msg);
+			dns_message_detach(&msg);
 	} else if (isc_serial_eq(soa.serial, oldserial)) {
 		isc_time_t expiretime;
 		uint32_t expire;
@@ -12387,12 +12387,12 @@ refresh_callback(isc_task_t *task, isc_event_t *event) {
 		goto next_master;
 	}
 	if (msg != NULL)
-		dns_message_destroy(&msg);
+		dns_message_detach(&msg);
 	goto detach;
 
  next_master:
 	if (msg != NULL)
-		dns_message_destroy(&msg);
+		dns_message_detach(&msg);
 	isc_event_free(&event);
 	dns_request_destroy(&zone->request);
 	/*
@@ -12444,7 +12444,7 @@ refresh_callback(isc_task_t *task, isc_event_t *event) {
 
  same_master:
 	if (msg != NULL)
-		dns_message_destroy(&msg);
+		dns_message_detach(&msg);
 	isc_event_free(&event);
 	dns_request_destroy(&zone->request);
 	queue_soa_query(zone);
@@ -12541,7 +12541,7 @@ create_query(dns_zone_t *zone, dns_rdatatype_t rdtype,
 	if (qrdataset != NULL)
 		dns_message_puttemprdataset(message, &qrdataset);
 	if (message != NULL)
-		dns_message_destroy(&message);
+		dns_message_detach(&message);
 	return (result);
 }
 
@@ -12751,7 +12751,7 @@ soa_query(isc_task_t *task, isc_event_t *event) {
 	if (result != ISC_R_SUCCESS)
 		DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_REFRESH);
 	if (message != NULL)
-		dns_message_destroy(&message);
+		dns_message_detach(&message);
 	if (cancel)
 		cancel_refresh(zone);
 	isc_event_free(&event);
@@ -12762,7 +12762,7 @@ soa_query(isc_task_t *task, isc_event_t *event) {
  skip_master:
 	if (key != NULL)
 		dns_tsigkey_detach(&key);
-	dns_message_destroy(&message);
+	dns_message_detach(&message);
 	/*
 	 * Skip to next failed / untried master.
 	 */
@@ -12983,7 +12983,7 @@ ns_query(dns_zone_t *zone, dns_rdataset_t *soardataset, dns_stub_t *stub) {
 			      dns_result_totext(result));
 		goto cleanup;
 	}
-	dns_message_destroy(&message);
+	dns_message_detach(&message);
 	goto unlock;
 
  cleanup:
@@ -13000,7 +13000,7 @@ ns_query(dns_zone_t *zone, dns_rdataset_t *soardataset, dns_stub_t *stub) {
 		isc_mem_put(stub->mctx, stub, sizeof(*stub));
 	}
 	if (message != NULL)
-		dns_message_destroy(&message);
+		dns_message_detach(&message);
  unlock:
 	if (key != NULL)
 		dns_tsigkey_detach(&key);
@@ -13436,7 +13436,7 @@ notify_createmessage(dns_zone_t *zone, unsigned int flags,
 		dns_message_puttempname(message, &tempname);
 	if (temprdataset != NULL)
 		dns_message_puttemprdataset(message, &temprdataset);
-	dns_message_destroy(&message);
+	dns_message_detach(&message);
 	return (result);
 }
 
@@ -14251,7 +14251,7 @@ notify_done(isc_task_t *task, isc_event_t *event) {
 		notify_destroy(notify, false);
 	}
 	if (message != NULL)
-		dns_message_destroy(&message);
+		dns_message_detach(&message);
 }
 
 struct secure_event {
@@ -16247,7 +16247,7 @@ forward_callback(isc_task_t *task, isc_event_t *event) {
 
  next_master:
 	if (msg != NULL)
-		dns_message_destroy(&msg);
+		dns_message_detach(&msg);
 	isc_event_free(&event);
 	forward->which++;
 	dns_request_destroy(&forward->request);
diff --git a/lib/samples/nsprobe.c b/lib/samples/nsprobe.c
index f5cc6d6feb..9bf0e65e01 100644
--- a/lib/samples/nsprobe.c
+++ b/lib/samples/nsprobe.c
@@ -1203,8 +1203,8 @@ main(int argc, char *argv[]) {
 
 	/* Cleanup */
 	for (i = 0; i < MAX_PROBES; i++) {
-		dns_message_destroy(&probes[i].qmessage);
-		dns_message_destroy(&probes[i].rmessage);
+		dns_message_detach(&probes[i].qmessage);
+		dns_message_detach(&probes[i].rmessage);
 	}
 	isc_task_detach(&probe_task);
 	dns_client_destroy(&client);
diff --git a/lib/samples/sample-request.c b/lib/samples/sample-request.c
index bfd806d28e..3a4123e244 100644
--- a/lib/samples/sample-request.c
+++ b/lib/samples/sample-request.c
@@ -115,7 +115,7 @@ make_querymessage(dns_message_t *message, const char *namestr,
 		dns_message_puttempname(message, &qname);
 	if (qrdataset != NULL)
 		dns_message_puttemprdataset(message, &qrdataset);
-	dns_message_destroy(&message);
+	dns_message_detach(&message);
 	return (result);
 }
 
@@ -255,8 +255,8 @@ main(int argc, char *argv[]) {
 	isc_buffer_free(&outputbuf);
 
 	/* Cleanup */
-	dns_message_destroy(&qmessage);
-	dns_message_destroy(&rmessage);
+	dns_message_detach(&qmessage);
+	dns_message_detach(&rmessage);
 	isc_mem_destroy(&mctx);
 	dns_client_destroy(&client);
 	dns_lib_shutdown();
-- 
2.23.0

