]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Move notify functions to notify source files
authorMatthijs Mekking <matthijs@isc.org>
Fri, 24 Oct 2025 12:21:25 +0000 (14:21 +0200)
committerMatthijs Mekking <matthijs@isc.org>
Fri, 31 Oct 2025 12:43:47 +0000 (13:43 +0100)
Move dns_notify_destroy, dns_notify_log, dns_notify_cancel,
dns_notify_queue, dns_notify_isqueued, dns_notify_find_address, and
notify related static functions over to the notify source files.

lib/dns/include/dns/notify.h
lib/dns/notify.c
lib/dns/zone.c

index fd62f527fe3ada5f3783e10af217a0b61c0887da..2db6028fada7750e958187efdd7506d5f41b0b83 100644 (file)
@@ -72,3 +72,69 @@ dns_notify_create(isc_mem_t *mctx, unsigned int flags, dns_notify_t **notifyp);
  *             'mctx' is not NULL.
  *             'notifyp' is not NULL and '*notifyp' is NULL.
  */
+
+void
+dns_notify_destroy(dns_notify_t *notify, bool zone_locked);
+/*%<
+ *     Destroy a notify structure. If 'zone_locked' is true, the attached
+ *     zone is already locked.
+ *
+ *     Requires:
+ *             'notify' is a valid notify.
+ */
+
+bool
+dns_notify_isqueued(dns_notifyctx_t *nctx, unsigned int flags, dns_name_t *name,
+                   isc_sockaddr_t *addr, dns_tsigkey_t *key,
+                   dns_transport_t *transport);
+/*%<
+ *     Check if we already have a notify queued matching name, destination
+ *     address, TSIG key, and transport. Will requeue on the normal notify
+ *     ratelimiter if the notify was enqueued on the startup ratelimiter and
+ *     this is not a startup notify.
+ *
+ *      Requires:
+ *             'nctx' is not NULL
+ *
+ *      Returns:
+ *             true if the notify matching the parameters is already enqueued
+ *             false otherwise
+ */
+
+isc_result_t
+dns_notify_queue(dns_notify_t *notify, bool startup);
+/*%<
+ *      Queue notify.
+ *
+ *      Requires:
+ *              'notify' is a valid notify.
+ */
+
+isc_result_t
+dns_notify_dequeue(dns_notify_t *notify, bool startup);
+/*%<
+ *      Dequeue notify.
+ *
+ *      Requires:
+ *              'notify' is a valid notify.
+ */
+
+void
+dns_notify_find_address(dns_notify_t *notify);
+/*%<
+ *     Find corresponding addresses for name server to send notify to.
+ *     Does a lookup into the ADB, then sends a notify to the found
+ *     addresses.
+ *
+ *     Requires:
+ *             'notify' is a valid notify.
+ */
+
+void
+dns_notify_cancel(dns_notifyctx_t *nctx);
+/*%<
+ *     Cancel all notifies. The corresponding zone must be locked.
+ *
+ *     Requires:
+ *             'nctx' is not NULL
+ */
index 53106a0df808a5a794e6e9dfcba77dcac528e5ba..f4d5cfb67033ea46701de01761cc17a7e9c84998 100644 (file)
 
 /*! \file */
 
+#include <isc/netmgr.h>
+#include <isc/ratelimiter.h>
 #include <isc/result.h>
 
+#include <dns/adb.h>
 #include <dns/notify.h>
+#include <dns/peer.h>
+#include <dns/rcode.h>
+#include <dns/rdatalist.h>
+#include <dns/request.h>
+#include <dns/stats.h>
+#include <dns/tsig.h>
+#include <dns/zone.h>
+
+#include "zone_p.h"
+
+static void
+notify_log(dns_notify_t *notify, int level, const char *fmt, ...) {
+       va_list ap;
+
+       va_start(ap, fmt);
+       dns_zone_logv(notify->zone, DNS_LOGCATEGORY_NOTIFY, level, NULL, fmt,
+                     ap);
+       va_end(ap);
+}
 
 void
 dns_notify_create(isc_mem_t *mctx, unsigned int flags, dns_notify_t **notifyp) {
@@ -36,3 +58,708 @@ dns_notify_create(isc_mem_t *mctx, unsigned int flags, dns_notify_t **notifyp) {
        notify->magic = NOTIFY_MAGIC;
        *notifyp = notify;
 }
+
+void
+dns_notify_destroy(dns_notify_t *notify, bool locked) {
+       REQUIRE(DNS_NOTIFY_VALID(notify));
+
+       isc_mem_t *mctx;
+       dns_notifyctx_t *nctx;
+
+       if (notify->zone != NULL) {
+               if (!locked) {
+                       dns__zone_lock(notify->zone);
+               }
+               REQUIRE(dns__zone_locked(notify->zone));
+               nctx = dns__zone_getnotifyctx(notify->zone);
+               if (ISC_LINK_LINKED(notify, link)) {
+                       ISC_LIST_UNLINK(nctx->notifies, notify, link);
+               }
+               if (!locked) {
+                       dns__zone_unlock(notify->zone);
+               }
+               if (locked) {
+                       dns__zone_idetach_locked(&notify->zone);
+               } else {
+                       dns_zone_idetach(&notify->zone);
+               }
+       }
+       if (notify->find != NULL) {
+               dns_adb_destroyfind(&notify->find);
+       }
+       if (notify->request != NULL) {
+               dns_request_destroy(&notify->request);
+       }
+       if (dns_name_dynamic(&notify->ns)) {
+               dns_name_free(&notify->ns, notify->mctx);
+       }
+       if (notify->key != NULL) {
+               dns_tsigkey_detach(&notify->key);
+       }
+       if (notify->transport != NULL) {
+               dns_transport_detach(&notify->transport);
+       }
+       mctx = notify->mctx;
+       isc_mem_put(notify->mctx, notify, sizeof(*notify));
+       isc_mem_detach(&mctx);
+}
+
+static void
+notify_done(void *arg) {
+       dns_request_t *request = (dns_request_t *)arg;
+       dns_notify_t *notify = dns_request_getarg(request);
+       isc_result_t result;
+       dns_message_t *message = NULL;
+       isc_buffer_t buf;
+       char rcode[128];
+       char addrbuf[ISC_SOCKADDR_FORMATSIZE];
+
+       REQUIRE(DNS_NOTIFY_VALID(notify));
+
+       isc_buffer_init(&buf, rcode, sizeof(rcode));
+       isc_sockaddr_format(&notify->dst, addrbuf, sizeof(addrbuf));
+       /* WMM: This is changing the mctx from zone to notify. */
+       dns_message_create(notify->mctx, NULL, NULL, DNS_MESSAGE_INTENTPARSE,
+                          &message);
+
+       result = dns_request_getresult(request);
+       if (result != ISC_R_SUCCESS) {
+               goto fail;
+       }
+
+       result = dns_request_getresponse(request, message,
+                                        DNS_MESSAGEPARSE_PRESERVEORDER);
+       if (result != ISC_R_SUCCESS) {
+               goto fail;
+       }
+
+       result = dns_rcode_totext(message->rcode, &buf);
+       if (result == ISC_R_SUCCESS) {
+               notify_log(notify, ISC_LOG_DEBUG(3),
+                          "notify response from %s: %.*s", addrbuf,
+                          (int)buf.used, rcode);
+       }
+fail:
+       dns_message_detach(&message);
+
+       if (result == ISC_R_SUCCESS) {
+               notify_log(notify, ISC_LOG_DEBUG(1), "notify to %s successful",
+                          addrbuf);
+       } else if (result == ISC_R_SHUTTINGDOWN || result == ISC_R_CANCELED) {
+               /* just destroy the notify */
+       } else if ((notify->flags & DNS_NOTIFY_TCP) == 0) {
+               notify_log(notify, ISC_LOG_NOTICE,
+                          "notify to %s failed: %s: retrying over TCP",
+                          addrbuf, isc_result_totext(result));
+               notify->flags |= DNS_NOTIFY_TCP;
+               dns_request_destroy(&notify->request);
+               dns_notify_queue(notify, notify->flags & DNS_NOTIFY_STARTUP);
+               return;
+       } else if (result == ISC_R_TIMEDOUT) {
+               notify_log(notify, ISC_LOG_WARNING,
+                          "notify to %s failed: %s: retries exceeded", addrbuf,
+                          isc_result_totext(result));
+       } else {
+               notify_log(notify, ISC_LOG_WARNING, "notify to %s failed: %s",
+                          addrbuf, isc_result_totext(result));
+       }
+       dns_notify_destroy(notify, false);
+}
+
+static isc_result_t
+notify_createmessage(dns_notify_t *notify, dns_message_t **messagep) {
+       dns_db_t *zonedb = NULL;
+       dns_dbnode_t *node = NULL;
+       dns_dbversion_t *version = NULL;
+       dns_message_t *message = NULL;
+       dns_rdataset_t rdataset;
+       dns_rdata_t rdata = DNS_RDATA_INIT;
+
+       dns_name_t *tempname = NULL;
+       dns_rdata_t *temprdata = NULL;
+       dns_rdatalist_t *temprdatalist = NULL;
+       dns_rdataset_t *temprdataset = NULL;
+
+       isc_result_t result;
+       isc_region_t r;
+       isc_buffer_t *b = NULL;
+
+       REQUIRE(DNS_NOTIFY_VALID(notify));
+       REQUIRE(messagep != NULL && *messagep == NULL);
+
+       /* WMM: This is changing the mctx from zone to notify. */
+       dns_message_create(notify->mctx, NULL, NULL, DNS_MESSAGE_INTENTRENDER,
+                          &message);
+
+       message->opcode = dns_opcode_notify;
+       message->flags |= DNS_MESSAGEFLAG_AA;
+       message->rdclass = dns_zone_getrdclass(notify->zone);
+
+       dns_message_gettempname(message, &tempname);
+       dns_message_gettemprdataset(message, &temprdataset);
+
+       /*
+        * Make question.
+        */
+       dns_name_clone(dns_zone_getorigin(notify->zone), tempname);
+       dns_rdataset_makequestion(temprdataset,
+                                 dns_zone_getrdclass(notify->zone),
+                                 dns_rdatatype_soa);
+       ISC_LIST_APPEND(tempname->list, temprdataset, link);
+       dns_message_addname(message, tempname, DNS_SECTION_QUESTION);
+       tempname = NULL;
+       temprdataset = NULL;
+
+       if ((notify->flags & DNS_NOTIFY_NOSOA) != 0) {
+               goto done;
+       }
+
+       dns_message_gettempname(message, &tempname);
+       dns_message_gettemprdata(message, &temprdata);
+       dns_message_gettemprdataset(message, &temprdataset);
+       dns_message_gettemprdatalist(message, &temprdatalist);
+
+       result = dns_zone_getdb(notify->zone, &zonedb);
+       INSIST(result == ISC_R_SUCCESS);
+       INSIST(zonedb != NULL); /* XXXJT: is this assumption correct? */
+
+       dns_name_clone(dns_zone_getorigin(notify->zone), tempname);
+       dns_db_currentversion(zonedb, &version);
+       result = dns_db_findnode(zonedb, tempname, false, &node);
+       if (result != ISC_R_SUCCESS) {
+               goto soa_cleanup;
+       }
+
+       dns_rdataset_init(&rdataset);
+       result = dns_db_findrdataset(zonedb, node, version, dns_rdatatype_soa,
+                                    dns_rdatatype_none, 0, &rdataset, NULL);
+       if (result != ISC_R_SUCCESS) {
+               goto soa_cleanup;
+       }
+       result = dns_rdataset_first(&rdataset);
+       if (result != ISC_R_SUCCESS) {
+               goto soa_cleanup;
+       }
+       dns_rdataset_current(&rdataset, &rdata);
+       dns_rdata_toregion(&rdata, &r);
+       /* WMM: This is changing the mctx from zone to notify. */
+       isc_buffer_allocate(notify->mctx, &b, r.length);
+       isc_buffer_putmem(b, r.base, r.length);
+       isc_buffer_usedregion(b, &r);
+       dns_rdata_fromregion(temprdata, rdata.rdclass, rdata.type, &r);
+       dns_message_takebuffer(message, &b);
+       result = dns_rdataset_next(&rdataset);
+       dns_rdataset_disassociate(&rdataset);
+       if (result != ISC_R_NOMORE) {
+               goto soa_cleanup;
+       }
+       temprdatalist->rdclass = rdata.rdclass;
+       temprdatalist->type = rdata.type;
+       temprdatalist->ttl = rdataset.ttl;
+       ISC_LIST_APPEND(temprdatalist->rdata, temprdata, link);
+
+       dns_rdatalist_tordataset(temprdatalist, temprdataset);
+
+       ISC_LIST_APPEND(tempname->list, temprdataset, link);
+       dns_message_addname(message, tempname, DNS_SECTION_ANSWER);
+       temprdatalist = NULL;
+       temprdataset = NULL;
+       temprdata = NULL;
+       tempname = NULL;
+
+soa_cleanup:
+       if (node != NULL) {
+               dns_db_detachnode(&node);
+       }
+       if (version != NULL) {
+               dns_db_closeversion(zonedb, &version, false);
+       }
+       if (zonedb != NULL) {
+               dns_db_detach(&zonedb);
+       }
+       if (tempname != NULL) {
+               dns_message_puttempname(message, &tempname);
+       }
+       if (temprdata != NULL) {
+               dns_message_puttemprdata(message, &temprdata);
+       }
+       if (temprdataset != NULL) {
+               dns_message_puttemprdataset(message, &temprdataset);
+       }
+       if (temprdatalist != NULL) {
+               dns_message_puttemprdatalist(message, &temprdatalist);
+       }
+
+done:
+       *messagep = message;
+       return ISC_R_SUCCESS;
+}
+
+static void
+notify_send_toaddr(void *arg) {
+       dns_notify_t *notify = (dns_notify_t *)arg;
+       dns_notifyctx_t *notifyctx = NULL;
+       isc_result_t result;
+       dns_db_t *zonedb = NULL;
+       dns_view_t *view = NULL;
+       isc_loop_t *loop = NULL;
+       dns_zonemgr_t *zmgr = NULL;
+       dns_message_t *message = NULL;
+       isc_netaddr_t dstip;
+       dns_tsigkey_t *key = NULL;
+       char addrbuf[ISC_SOCKADDR_FORMATSIZE];
+       isc_sockaddr_t src;
+       unsigned int options;
+       bool have_notifysource = false;
+       isc_tlsctx_cache_t *zmgr_tlsctx_cache = NULL;
+
+       REQUIRE(DNS_NOTIFY_VALID(notify));
+
+       dns__zone_lock(notify->zone);
+
+       notifyctx = dns__zone_getnotifyctx(notify->zone);
+       zmgr = dns_zone_getmgr(notify->zone);
+       view = dns_zone_getview(notify->zone);
+       loop = dns_zone_getloop(notify->zone);
+       result = dns_zone_getdb(notify->zone, &zonedb);
+       isc_sockaddr_format(&notify->dst, addrbuf, sizeof(addrbuf));
+
+       if (!dns__zone_loaded(notify->zone) || notify->rlevent->canceled ||
+           dns__zone_exiting(notify->zone) || zmgr == NULL || view == NULL ||
+           view->requestmgr == NULL || loop == NULL || zonedb == NULL ||
+           result != ISC_R_SUCCESS)
+       {
+               result = ISC_R_CANCELED;
+               goto cleanup;
+       }
+
+       /*
+        * The raw IPv4 address should also exist.  Don't send to the
+        * mapped form.
+        */
+       if (isc_sockaddr_pf(&notify->dst) == PF_INET6 &&
+           IN6_IS_ADDR_V4MAPPED(&notify->dst.type.sin6.sin6_addr))
+       {
+               notify_log(notify, ISC_LOG_DEBUG(3),
+                          "notify: ignoring IPv6 mapped IPV4 address: %s",
+                          addrbuf);
+               result = ISC_R_CANCELED;
+               goto cleanup;
+       }
+
+       result = notify_createmessage(notify, &message);
+       if (result != ISC_R_SUCCESS) {
+               goto cleanup;
+       }
+
+       if (notify->key != NULL) {
+               /* Transfer ownership of key */
+               key = notify->key;
+               notify->key = NULL;
+       } else {
+               isc_netaddr_fromsockaddr(&dstip, &notify->dst);
+               result = dns_view_getpeertsig(view, &dstip, &key);
+               if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) {
+                       notify_log(notify, ISC_LOG_ERROR,
+                                  "NOTIFY to %s not sent. "
+                                  "Peer TSIG key lookup failure.",
+                                  addrbuf);
+                       goto cleanup_message;
+               }
+       }
+
+       if (key != NULL) {
+               char namebuf[DNS_NAME_FORMATSIZE];
+
+               dns_name_format(key->name, namebuf, sizeof(namebuf));
+               notify_log(notify, ISC_LOG_INFO,
+                          "sending notify to %s : TSIG (%s)", addrbuf,
+                          namebuf);
+       } else {
+               notify_log(notify, ISC_LOG_INFO, "sending notify to %s",
+                          addrbuf);
+       }
+       options = 0;
+       if (view->peers != NULL) {
+               dns_peer_t *peer = NULL;
+               bool usetcp = false;
+               result = dns_peerlist_peerbyaddr(view->peers, &dstip, &peer);
+               if (result == ISC_R_SUCCESS) {
+                       result = dns_peer_getnotifysource(peer, &src);
+                       if (result == ISC_R_SUCCESS) {
+                               have_notifysource = true;
+                       }
+                       result = dns_peer_getforcetcp(peer, &usetcp);
+                       if (result == ISC_R_SUCCESS && usetcp) {
+                               options |= DNS_FETCHOPT_TCP;
+                       }
+               }
+       }
+       switch (isc_sockaddr_pf(&notify->dst)) {
+       case PF_INET:
+               if (!have_notifysource) {
+                       isc_sockaddr_t any;
+                       isc_sockaddr_any(&any);
+
+                       src = notify->src;
+                       if (isc_sockaddr_equal(&src, &any)) {
+                               src = notifyctx->notifysrc4;
+                       }
+               }
+               break;
+       case PF_INET6:
+               if (!have_notifysource) {
+                       isc_sockaddr_t any;
+                       isc_sockaddr_any6(&any);
+
+                       src = notify->src;
+                       if (isc_sockaddr_equal(&src, &any)) {
+                               src = notifyctx->notifysrc6;
+                       }
+               }
+               break;
+       default:
+               result = ISC_R_NOTIMPLEMENTED;
+               goto cleanup_key;
+       }
+
+again:
+       if ((notify->flags & DNS_NOTIFY_TCP) != 0) {
+               options |= DNS_REQUESTOPT_TCP;
+       }
+
+       dns__zonemgr_tlsctx_attach(zmgr, &zmgr_tlsctx_cache);
+
+       const unsigned int connect_timeout = isc_nm_getinitialtimeout() /
+                                            MS_PER_SEC;
+       result = dns_request_create(
+               view->requestmgr, message, &src, &notify->dst,
+               notify->transport, zmgr_tlsctx_cache, options, key,
+               connect_timeout, TCP_REQUEST_TIMEOUT, UDP_REQUEST_TIMEOUT,
+               UDP_REQUEST_RETRIES, loop, notify_done, notify,
+               &notify->request);
+
+       isc_tlsctx_cache_detach(&zmgr_tlsctx_cache);
+
+       if (result == ISC_R_SUCCESS) {
+               if (isc_sockaddr_pf(&notify->dst) == AF_INET) {
+                       dns__zone_stats_increment(
+                               notify->zone, dns_zonestatscounter_notifyoutv4);
+               } else {
+                       dns__zone_stats_increment(
+                               notify->zone, dns_zonestatscounter_notifyoutv6);
+               }
+       } else if (result == ISC_R_SHUTTINGDOWN || result == ISC_R_CANCELED) {
+               goto cleanup_key;
+       } else if ((notify->flags & DNS_NOTIFY_TCP) == 0) {
+               notify_log(notify, ISC_LOG_NOTICE,
+                          "notify to %s failed: %s: retrying over TCP",
+                          addrbuf, isc_result_totext(result));
+               notify->flags |= DNS_NOTIFY_TCP;
+               goto again;
+       }
+
+cleanup_key:
+       if (key != NULL) {
+               dns_tsigkey_detach(&key);
+       }
+cleanup_message:
+       dns_message_detach(&message);
+cleanup:
+       dns__zone_unlock(notify->zone);
+
+       if (zonedb != NULL) {
+               dns_db_detach(&zonedb);
+       }
+
+       if (notify->rlevent != NULL) {
+               isc_rlevent_free(&notify->rlevent);
+       }
+
+       if (result != ISC_R_SUCCESS) {
+               isc_sockaddr_format(&notify->dst, addrbuf, sizeof(addrbuf));
+               notify_log(notify, ISC_LOG_WARNING, "notify to %s failed: %s",
+                          addrbuf, isc_result_totext(result));
+               dns_notify_destroy(notify, false);
+       }
+}
+
+static isc_result_t
+notify_queue(dns_notify_t *notify, bool startup, bool dequeue) {
+       REQUIRE(DNS_NOTIFY_VALID(notify));
+
+       isc_loop_t *loop = dns_zone_getloop(notify->zone);
+       dns_zonemgr_t *zmgr = dns_zone_getmgr(notify->zone);
+       isc_ratelimiter_t *notifyrl = NULL;
+       isc_ratelimiter_t *startupnotifyrl = NULL;
+
+       INSIST(loop != NULL);
+       INSIST(zmgr != NULL);
+
+       dns__zonemgr_getnotifyrl(zmgr, &notifyrl);
+       dns__zonemgr_getstartupnotifyrl(zmgr, &startupnotifyrl);
+
+       if (dequeue) {
+               return isc_ratelimiter_dequeue(
+                       startup ? startupnotifyrl : notifyrl, &notify->rlevent);
+       }
+
+       return isc_ratelimiter_enqueue(startup ? startupnotifyrl : notifyrl,
+                                      loop, notify_send_toaddr, notify,
+                                      &notify->rlevent);
+}
+
+isc_result_t
+dns_notify_dequeue(dns_notify_t *notify, bool startup) {
+       return notify_queue(notify, startup, true);
+}
+
+isc_result_t
+dns_notify_queue(dns_notify_t *notify, bool startup) {
+       return notify_queue(notify, startup, false);
+}
+
+bool
+dns_notify_isqueued(dns_notifyctx_t *nctx, unsigned int flags, dns_name_t *name,
+                   isc_sockaddr_t *addr, dns_tsigkey_t *key,
+                   dns_transport_t *transport) {
+       dns_notify_t *notify = NULL;
+       isc_result_t result;
+
+       REQUIRE(nctx != NULL);
+
+       ISC_LIST_FOREACH(nctx->notifies, n, link) {
+               if (n->request != NULL) {
+                       continue;
+               }
+               if ((name != NULL && dns_name_dynamic(&n->ns) &&
+                    dns_name_equal(name, &n->ns)) ||
+                   (addr != NULL && isc_sockaddr_equal(addr, &n->dst) &&
+                    n->key == key && n->transport == transport))
+               {
+                       notify = n;
+                       goto requeue;
+               }
+       }
+       return false;
+requeue:
+       /*
+        * If we are enqueued on the startup ratelimiter and this is
+        * not a startup notify, re-enqueue on the normal notify
+        * ratelimiter.
+        */
+       if (notify->rlevent != NULL && (flags & DNS_NOTIFY_STARTUP) == 0 &&
+           (notify->flags & DNS_NOTIFY_STARTUP) != 0)
+       {
+               result = notify_queue(notify, true, true);
+               if (result != ISC_R_SUCCESS) {
+                       return true;
+               }
+
+               notify->flags &= ~DNS_NOTIFY_STARTUP;
+               result = notify_queue(notify, false, false);
+               if (result != ISC_R_SUCCESS) {
+                       return false;
+               }
+       }
+
+       return true;
+}
+
+static bool
+notify_isself(dns_notify_t *notify, isc_sockaddr_t *dst) {
+       dns_tsigkey_t *key = NULL;
+       isc_sockaddr_t src;
+       isc_sockaddr_t any;
+       bool isself;
+       isc_netaddr_t dstaddr;
+       isc_result_t result;
+       dns_notifyctx_t *notifyctx = NULL;
+       dns_view_t *view = NULL;
+       dns_isselffunc_t isselffunc;
+       void *isselfarg = NULL;
+
+       notifyctx = dns__zone_getnotifyctx(notify->zone);
+       view = dns_zone_getview(notify->zone);
+       dns__zone_getisself(notify->zone, &isselffunc, &isselfarg);
+       if (view == NULL || isselffunc == NULL) {
+               return false;
+       }
+
+       switch (isc_sockaddr_pf(dst)) {
+       case PF_INET:
+               src = notifyctx->notifysrc4;
+               isc_sockaddr_any(&any);
+               break;
+       case PF_INET6:
+               src = notifyctx->notifysrc6;
+               isc_sockaddr_any6(&any);
+               break;
+       default:
+               return false;
+       }
+
+       /*
+        * When sending from any the kernel will assign a source address
+        * that matches the destination address.
+        */
+       if (isc_sockaddr_eqaddr(&any, &src)) {
+               src = *dst;
+       }
+
+       isc_netaddr_fromsockaddr(&dstaddr, dst);
+       result = dns_view_getpeertsig(view, &dstaddr, &key);
+       if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) {
+               return false;
+       }
+       isself = (isselffunc)(view, key, &src, dst,
+                             dns_zone_getrdclass(notify->zone), isselfarg);
+       if (key != NULL) {
+               dns_tsigkey_detach(&key);
+       }
+       return isself;
+}
+
+static void
+notify_send(dns_notify_t *notify) {
+       isc_sockaddr_t dst;
+       isc_result_t result;
+       dns_notify_t *newnotify = NULL;
+       dns_notifyctx_t *notifyctx = NULL;
+       unsigned int flags;
+       bool startup;
+
+       /*
+        * Zone lock held by caller.
+        */
+       REQUIRE(DNS_NOTIFY_VALID(notify));
+       REQUIRE(dns__zone_locked(notify->zone));
+       if (dns__zone_exiting(notify->zone)) {
+               return;
+       }
+       notifyctx = dns__zone_getnotifyctx(notify->zone);
+
+       ISC_LIST_FOREACH(notify->find->list, ai, publink) {
+               dst = ai->sockaddr;
+               if (dns_notify_isqueued(notifyctx, notify->flags, NULL, &dst,
+                                       NULL, NULL))
+               {
+                       continue;
+               }
+               if (notify_isself(notify, &dst)) {
+                       continue;
+               }
+               newnotify = NULL;
+               flags = notify->flags & DNS_NOTIFY_NOSOA;
+               dns_notify_create(notify->mctx, flags, &newnotify);
+               dns__zone_iattach_locked(notify->zone, &newnotify->zone);
+               ISC_LIST_APPEND(notifyctx->notifies, newnotify, link);
+               newnotify->dst = dst;
+               if (isc_sockaddr_pf(&dst) == AF_INET6) {
+                       isc_sockaddr_any6(&newnotify->src);
+               }
+               startup = ((notify->flags & DNS_NOTIFY_STARTUP) != 0);
+               result = dns_notify_queue(newnotify, startup);
+               if (result != ISC_R_SUCCESS) {
+                       goto cleanup;
+               }
+               newnotify = NULL;
+       }
+
+cleanup:
+       if (newnotify != NULL) {
+               dns_notify_destroy(newnotify, true);
+       }
+}
+
+/*
+ * XXXAG should check for DNS_ZONEFLG_EXITING
+ */
+static void
+process_notify_adb_event(void *arg) {
+       dns_adbfind_t *find = (dns_adbfind_t *)arg;
+       dns_notify_t *notify = (dns_notify_t *)find->cbarg;
+       dns_adbstatus_t astat = find->status;
+
+       REQUIRE(DNS_NOTIFY_VALID(notify));
+       REQUIRE(find == notify->find);
+
+       switch (astat) {
+       case DNS_ADB_MOREADDRESSES:
+               dns_adb_destroyfind(&notify->find);
+               dns_notify_find_address(notify);
+               return;
+
+       case DNS_ADB_NOMOREADDRESSES:
+               dns__zone_lock(notify->zone);
+               notify_send(notify);
+               dns__zone_unlock(notify->zone);
+               break;
+
+       default:
+               break;
+       }
+
+       dns_notify_destroy(notify, false);
+}
+
+void
+dns_notify_find_address(dns_notify_t *notify) {
+       isc_result_t result;
+       unsigned int options;
+       dns_adb_t *adb = NULL;
+       dns_view_t *view = NULL;
+       isc_loop_t *loop = NULL;
+
+       REQUIRE(DNS_NOTIFY_VALID(notify));
+
+       options = DNS_ADBFIND_WANTEVENT;
+       if (isc_net_probeipv4() != ISC_R_DISABLED) {
+               options |= DNS_ADBFIND_INET;
+       }
+       if (isc_net_probeipv6() != ISC_R_DISABLED) {
+               options |= DNS_ADBFIND_INET6;
+       }
+
+       loop = dns_zone_getloop(notify->zone);
+       view = dns_zone_getview(notify->zone);
+       dns_view_getadb(view, &adb);
+       if (loop == NULL || view == NULL || adb == NULL) {
+               goto destroy;
+       }
+
+       result = dns_adb_createfind(adb, loop, process_notify_adb_event, notify,
+                                   &notify->ns, options, 0, view->dstport, 0,
+                                   NULL, NULL, &notify->find);
+       dns_adb_detach(&adb);
+
+       /* Something failed? */
+       if (result != ISC_R_SUCCESS) {
+               goto destroy;
+       }
+
+       /* More addresses pending? */
+       if ((notify->find->options & DNS_ADBFIND_WANTEVENT) != 0) {
+               return;
+       }
+
+       /* We have as many addresses as we can get. */
+       dns__zone_lock(notify->zone);
+       notify_send(notify);
+       dns__zone_unlock(notify->zone);
+destroy:
+       dns_notify_destroy(notify, false);
+}
+
+void
+dns_notify_cancel(dns_notifyctx_t *nctx) {
+       ISC_LIST_FOREACH(nctx->notifies, notify, link) {
+               INSIST(dns__zone_locked(notify->zone));
+               if (notify->find != NULL) {
+                       dns_adb_cancelfind(notify->find);
+               }
+               if (notify->request != NULL) {
+                       dns_request_cancel(notify->request);
+               }
+       }
+}
index e7ea558681b0623122660a40bcb3f5da060b3685..280cf2b79f7fbe83032839bf6205ceb7075e6166 100644 (file)
@@ -833,9 +833,6 @@ static void
 zone_debuglog(dns_zone_t *zone, const char *, int debuglevel, const char *msg,
              ...) ISC_FORMAT_PRINTF(4, 5);
 static void
-notify_log(dns_zone_t *zone, int level, const char *fmt, ...)
-       ISC_FORMAT_PRINTF(3, 4);
-static void
 dnssec_log(dns_zone_t *zone, int level, const char *fmt, ...)
        ISC_FORMAT_PRINTF(3, 4);
 static void
@@ -917,19 +914,6 @@ static void
 checkds_send_toaddr(void *arg);
 static void
 nsfetch_levelup(dns_nsfetch_t *nsfetch);
-static void
-notify_cancel(dns_zone_t *zone);
-static void
-notify_find_address(dns_notify_t *notify);
-static void
-notify_send(dns_notify_t *notify);
-static isc_result_t
-notify_createmessage(dns_zone_t *zone, unsigned int flags,
-                    dns_message_t **messagep);
-static void
-notify_done(void *arg);
-static void
-notify_send_toaddr(void *arg);
 static isc_result_t
 zone_dump(dns_zone_t *, bool);
 static void
@@ -12501,24 +12485,6 @@ dns_zone_unload(dns_zone_t *zone) {
        UNLOCK_ZONE(zone);
 }
 
-static void
-notify_cancel(dns_zone_t *zone) {
-       /*
-        * 'zone' locked by caller.
-        */
-
-       REQUIRE(LOCKED_ZONE(zone));
-
-       ISC_LIST_FOREACH(zone->notifyctx.notifies, notify, link) {
-               if (notify->find != NULL) {
-                       dns_adb_cancelfind(notify->find);
-               }
-               if (notify->request != NULL) {
-                       dns_request_cancel(notify->request);
-               }
-       }
-}
-
 static void
 checkds_cancel(dns_zone_t *zone) {
        /*
@@ -12646,458 +12612,6 @@ dns_zone_setmaxtypepername(dns_zone_t *zone, uint32_t val) {
        }
 }
 
-static bool
-notify_isqueued(dns_zone_t *zone, unsigned int flags, dns_name_t *name,
-               isc_sockaddr_t *addr, dns_tsigkey_t *key,
-               dns_transport_t *transport) {
-       dns_notify_t *notify = NULL;
-       dns_zonemgr_t *zmgr = NULL;
-       isc_result_t result;
-
-       ISC_LIST_FOREACH(zone->notifyctx.notifies, n, link) {
-               if (n->request != NULL) {
-                       continue;
-               }
-               if ((name != NULL && dns_name_dynamic(&n->ns) &&
-                    dns_name_equal(name, &n->ns)) ||
-                   (addr != NULL && isc_sockaddr_equal(addr, &n->dst) &&
-                    n->key == key && n->transport == transport))
-               {
-                       notify = n;
-                       goto requeue;
-               }
-       }
-       return false;
-
-requeue:
-       /*
-        * If we are enqueued on the startup ratelimiter and this is
-        * not a startup notify, re-enqueue on the normal notify
-        * ratelimiter.
-        */
-       if (notify->rlevent != NULL && (flags & DNS_NOTIFY_STARTUP) == 0 &&
-           (notify->flags & DNS_NOTIFY_STARTUP) != 0)
-       {
-               zmgr = notify->zone->zmgr;
-               result = isc_ratelimiter_dequeue(zmgr->startupnotifyrl,
-                                                &notify->rlevent);
-               if (result != ISC_R_SUCCESS) {
-                       return true;
-               }
-
-               notify->flags &= ~DNS_NOTIFY_STARTUP;
-               result = isc_ratelimiter_enqueue(
-                       notify->zone->zmgr->notifyrl, notify->zone->loop,
-                       notify_send_toaddr, notify, &notify->rlevent);
-               if (result != ISC_R_SUCCESS) {
-                       return false;
-               }
-       }
-
-       return true;
-}
-
-static bool
-notify_isself(dns_zone_t *zone, isc_sockaddr_t *dst) {
-       dns_tsigkey_t *key = NULL;
-       isc_sockaddr_t src;
-       isc_sockaddr_t any;
-       bool isself;
-       isc_netaddr_t dstaddr;
-       isc_result_t result;
-
-       if (zone->view == NULL || zone->isself == NULL) {
-               return false;
-       }
-
-       switch (isc_sockaddr_pf(dst)) {
-       case PF_INET:
-               src = zone->notifyctx.notifysrc4;
-               isc_sockaddr_any(&any);
-               break;
-       case PF_INET6:
-               src = zone->notifyctx.notifysrc6;
-               isc_sockaddr_any6(&any);
-               break;
-       default:
-               return false;
-       }
-
-       /*
-        * When sending from any the kernel will assign a source address
-        * that matches the destination address.
-        */
-       if (isc_sockaddr_eqaddr(&any, &src)) {
-               src = *dst;
-       }
-
-       isc_netaddr_fromsockaddr(&dstaddr, dst);
-       result = dns_view_getpeertsig(zone->view, &dstaddr, &key);
-       if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) {
-               return false;
-       }
-       isself = (zone->isself)(zone->view, key, &src, dst, zone->rdclass,
-                               zone->isselfarg);
-       if (key != NULL) {
-               dns_tsigkey_detach(&key);
-       }
-       return isself;
-}
-
-static void
-notify_destroy(dns_notify_t *notify, bool locked) {
-       isc_mem_t *mctx;
-
-       REQUIRE(DNS_NOTIFY_VALID(notify));
-
-       if (notify->zone != NULL) {
-               if (!locked) {
-                       LOCK_ZONE(notify->zone);
-               }
-               REQUIRE(LOCKED_ZONE(notify->zone));
-               if (ISC_LINK_LINKED(notify, link)) {
-                       ISC_LIST_UNLINK(notify->zone->notifyctx.notifies,
-                                       notify, link);
-               }
-               if (!locked) {
-                       UNLOCK_ZONE(notify->zone);
-               }
-               dns_zone_idetach(&notify->zone, locked);
-       }
-       if (notify->find != NULL) {
-               dns_adb_destroyfind(&notify->find);
-       }
-       if (notify->request != NULL) {
-               dns_request_destroy(&notify->request);
-       }
-       if (dns_name_dynamic(&notify->ns)) {
-               dns_name_free(&notify->ns, notify->mctx);
-       }
-       if (notify->key != NULL) {
-               dns_tsigkey_detach(&notify->key);
-       }
-       if (notify->transport != NULL) {
-               dns_transport_detach(&notify->transport);
-       }
-       mctx = notify->mctx;
-       isc_mem_put(notify->mctx, notify, sizeof(*notify));
-       isc_mem_detach(&mctx);
-}
-
-/*
- * XXXAG should check for DNS_ZONEFLG_EXITING
- */
-static void
-process_notify_adb_event(void *arg) {
-       dns_adbfind_t *find = (dns_adbfind_t *)arg;
-       dns_notify_t *notify = (dns_notify_t *)find->cbarg;
-       dns_adbstatus_t astat = find->status;
-
-       REQUIRE(DNS_NOTIFY_VALID(notify));
-       REQUIRE(find == notify->find);
-
-       switch (astat) {
-       case DNS_ADB_MOREADDRESSES:
-               dns_adb_destroyfind(&notify->find);
-               notify_find_address(notify);
-               return;
-
-       case DNS_ADB_NOMOREADDRESSES:
-               LOCK_ZONE(notify->zone);
-               notify_send(notify);
-               UNLOCK_ZONE(notify->zone);
-               break;
-
-       default:
-               break;
-       }
-
-       notify_destroy(notify, false);
-}
-
-static void
-notify_find_address(dns_notify_t *notify) {
-       isc_result_t result;
-       unsigned int options;
-       dns_adb_t *adb = NULL;
-
-       REQUIRE(DNS_NOTIFY_VALID(notify));
-
-       options = DNS_ADBFIND_WANTEVENT;
-       if (isc_net_probeipv4() != ISC_R_DISABLED) {
-               options |= DNS_ADBFIND_INET;
-       }
-       if (isc_net_probeipv6() != ISC_R_DISABLED) {
-               options |= DNS_ADBFIND_INET6;
-       }
-
-       dns_view_getadb(notify->zone->view, &adb);
-       if (adb == NULL) {
-               goto destroy;
-       }
-
-       result = dns_adb_createfind(
-               adb, notify->zone->loop, process_notify_adb_event, notify,
-               &notify->ns, options, 0, notify->zone->view->dstport, 0, NULL,
-               NULL, &notify->find);
-       dns_adb_detach(&adb);
-
-       /* Something failed? */
-       if (result != ISC_R_SUCCESS) {
-               goto destroy;
-       }
-
-       /* More addresses pending? */
-       if ((notify->find->options & DNS_ADBFIND_WANTEVENT) != 0) {
-               return;
-       }
-
-       /* We have as many addresses as we can get. */
-       LOCK_ZONE(notify->zone);
-       notify_send(notify);
-       UNLOCK_ZONE(notify->zone);
-
-destroy:
-       notify_destroy(notify, false);
-}
-
-static isc_result_t
-notify_send_queue(dns_notify_t *notify, bool startup) {
-       return isc_ratelimiter_enqueue(
-               startup ? notify->zone->zmgr->startupnotifyrl
-                       : notify->zone->zmgr->notifyrl,
-               notify->zone->loop, notify_send_toaddr, notify,
-               &notify->rlevent);
-}
-
-static void
-notify_send_toaddr(void *arg) {
-       dns_notify_t *notify = (dns_notify_t *)arg;
-       isc_result_t result;
-       dns_message_t *message = NULL;
-       isc_netaddr_t dstip;
-       dns_tsigkey_t *key = NULL;
-       char addrbuf[ISC_SOCKADDR_FORMATSIZE];
-       isc_sockaddr_t src;
-       unsigned int options;
-       bool have_notifysource = false;
-       isc_tlsctx_cache_t *zmgr_tlsctx_cache = NULL;
-
-       REQUIRE(DNS_NOTIFY_VALID(notify));
-
-       LOCK_ZONE(notify->zone);
-
-       isc_sockaddr_format(&notify->dst, addrbuf, sizeof(addrbuf));
-
-       if (DNS_ZONE_FLAG(notify->zone, DNS_ZONEFLG_LOADED) == 0 ||
-           notify->rlevent->canceled ||
-           DNS_ZONE_FLAG(notify->zone, DNS_ZONEFLG_EXITING) ||
-           notify->zone->view->requestmgr == NULL || notify->zone->db == NULL)
-       {
-               result = ISC_R_CANCELED;
-               goto cleanup;
-       }
-
-       /*
-        * The raw IPv4 address should also exist.  Don't send to the
-        * mapped form.
-        */
-       if (isc_sockaddr_pf(&notify->dst) == PF_INET6 &&
-           IN6_IS_ADDR_V4MAPPED(&notify->dst.type.sin6.sin6_addr))
-       {
-               notify_log(notify->zone, ISC_LOG_DEBUG(3),
-                          "notify: ignoring IPv6 mapped IPV4 address: %s",
-                          addrbuf);
-               result = ISC_R_CANCELED;
-               goto cleanup;
-       }
-
-       result = notify_createmessage(notify->zone, notify->flags, &message);
-       if (result != ISC_R_SUCCESS) {
-               goto cleanup;
-       }
-
-       if (notify->key != NULL) {
-               /* Transfer ownership of key */
-               key = notify->key;
-               notify->key = NULL;
-       } else {
-               isc_netaddr_fromsockaddr(&dstip, &notify->dst);
-               result = dns_view_getpeertsig(notify->zone->view, &dstip, &key);
-               if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) {
-                       notify_log(notify->zone, ISC_LOG_ERROR,
-                                  "NOTIFY to %s not sent. "
-                                  "Peer TSIG key lookup failure.",
-                                  addrbuf);
-                       goto cleanup_message;
-               }
-       }
-
-       if (key != NULL) {
-               char namebuf[DNS_NAME_FORMATSIZE];
-
-               dns_name_format(key->name, namebuf, sizeof(namebuf));
-               notify_log(notify->zone, ISC_LOG_INFO,
-                          "sending notify to %s : TSIG (%s)", addrbuf,
-                          namebuf);
-       } else {
-               notify_log(notify->zone, ISC_LOG_INFO, "sending notify to %s",
-                          addrbuf);
-       }
-       options = 0;
-       if (notify->zone->view->peers != NULL) {
-               dns_peer_t *peer = NULL;
-               bool usetcp = false;
-               result = dns_peerlist_peerbyaddr(notify->zone->view->peers,
-                                                &dstip, &peer);
-               if (result == ISC_R_SUCCESS) {
-                       result = dns_peer_getnotifysource(peer, &src);
-                       if (result == ISC_R_SUCCESS) {
-                               have_notifysource = true;
-                       }
-                       result = dns_peer_getforcetcp(peer, &usetcp);
-                       if (result == ISC_R_SUCCESS && usetcp) {
-                               options |= DNS_FETCHOPT_TCP;
-                       }
-               }
-       }
-       switch (isc_sockaddr_pf(&notify->dst)) {
-       case PF_INET:
-               if (!have_notifysource) {
-                       isc_sockaddr_t any;
-                       isc_sockaddr_any(&any);
-
-                       src = notify->src;
-                       if (isc_sockaddr_equal(&src, &any)) {
-                               src = notify->zone->notifyctx.notifysrc4;
-                       }
-               }
-               break;
-       case PF_INET6:
-               if (!have_notifysource) {
-                       isc_sockaddr_t any;
-                       isc_sockaddr_any6(&any);
-
-                       src = notify->src;
-                       if (isc_sockaddr_equal(&src, &any)) {
-                               src = notify->zone->notifyctx.notifysrc6;
-                       }
-               }
-               break;
-       default:
-               result = ISC_R_NOTIMPLEMENTED;
-               goto cleanup_key;
-       }
-
-again:
-       if ((notify->flags & DNS_NOTIFY_TCP) != 0) {
-               options |= DNS_REQUESTOPT_TCP;
-       }
-
-       dns_zonemgr_tlsctx_attach(notify->zone->zmgr, &zmgr_tlsctx_cache);
-
-       const unsigned int connect_timeout = isc_nm_getinitialtimeout() /
-                                            MS_PER_SEC;
-       result = dns_request_create(
-               notify->zone->view->requestmgr, message, &src, &notify->dst,
-               notify->transport, zmgr_tlsctx_cache, options, key,
-               connect_timeout, TCP_REQUEST_TIMEOUT, UDP_REQUEST_TIMEOUT,
-               UDP_REQUEST_RETRIES, notify->zone->loop, notify_done, notify,
-               &notify->request);
-
-       isc_tlsctx_cache_detach(&zmgr_tlsctx_cache);
-
-       if (result == ISC_R_SUCCESS) {
-               if (isc_sockaddr_pf(&notify->dst) == AF_INET) {
-                       inc_stats(notify->zone,
-                                 dns_zonestatscounter_notifyoutv4);
-               } else {
-                       inc_stats(notify->zone,
-                                 dns_zonestatscounter_notifyoutv6);
-               }
-       } else if (result == ISC_R_SHUTTINGDOWN || result == ISC_R_CANCELED) {
-               goto cleanup_key;
-       } else if ((notify->flags & DNS_NOTIFY_TCP) == 0) {
-               notify_log(notify->zone, ISC_LOG_NOTICE,
-                          "notify to %s failed: %s: retrying over TCP",
-                          addrbuf, isc_result_totext(result));
-               notify->flags |= DNS_NOTIFY_TCP;
-               goto again;
-       }
-
-cleanup_key:
-       if (key != NULL) {
-               dns_tsigkey_detach(&key);
-       }
-cleanup_message:
-       dns_message_detach(&message);
-cleanup:
-       UNLOCK_ZONE(notify->zone);
-       if (notify->rlevent != NULL) {
-               isc_rlevent_free(&notify->rlevent);
-       }
-
-       if (result != ISC_R_SUCCESS) {
-               isc_sockaddr_format(&notify->dst, addrbuf, sizeof(addrbuf));
-               notify_log(notify->zone, ISC_LOG_WARNING,
-                          "notify to %s failed: %s", addrbuf,
-                          isc_result_totext(result));
-               notify_destroy(notify, false);
-       }
-}
-
-static void
-notify_send(dns_notify_t *notify) {
-       isc_sockaddr_t dst;
-       isc_result_t result;
-       dns_notify_t *newnotify = NULL;
-       unsigned int flags;
-       bool startup;
-
-       /*
-        * Zone lock held by caller.
-        */
-       REQUIRE(DNS_NOTIFY_VALID(notify));
-       REQUIRE(LOCKED_ZONE(notify->zone));
-
-       if (DNS_ZONE_FLAG(notify->zone, DNS_ZONEFLG_EXITING)) {
-               return;
-       }
-
-       ISC_LIST_FOREACH(notify->find->list, ai, publink) {
-               dst = ai->sockaddr;
-               if (notify_isqueued(notify->zone, notify->flags, NULL, &dst,
-                                   NULL, NULL))
-               {
-                       continue;
-               }
-               if (notify_isself(notify->zone, &dst)) {
-                       continue;
-               }
-               newnotify = NULL;
-               flags = notify->flags & DNS_NOTIFY_NOSOA;
-               dns_notify_create(notify->mctx, flags, &newnotify);
-               zone_iattach(notify->zone, &newnotify->zone);
-               ISC_LIST_APPEND(newnotify->zone->notifyctx.notifies, newnotify,
-                               link);
-               newnotify->dst = dst;
-               if (isc_sockaddr_pf(&dst) == AF_INET6) {
-                       isc_sockaddr_any6(&newnotify->src);
-               }
-               startup = ((notify->flags & DNS_NOTIFY_STARTUP) != 0);
-               result = notify_send_queue(newnotify, startup);
-               if (result != ISC_R_SUCCESS) {
-                       goto cleanup;
-               }
-               newnotify = NULL;
-       }
-
-cleanup:
-       if (newnotify != NULL) {
-               notify_destroy(newnotify, true);
-       }
-}
-
 void
 dns_zone_notify(dns_zone_t *zone, bool nodefer) {
        isc_time_t now;
@@ -13248,8 +12762,9 @@ zone_notify(dns_zone_t *zone, isc_time_t *now) {
                                                       tlsname, &transport);
 
                        if (result == ISC_R_SUCCESS) {
-                               notify_log(
-                                       zone, ISC_LOG_INFO,
+                               dns_zone_logc(
+                                       zone, DNS_LOGCATEGORY_NOTIFY,
+                                       ISC_LOG_INFO,
                                        "got TLS configuration for a notify");
                        } else {
                                dns_zone_logc(zone, DNS_LOGCATEGORY_XFER_IN,
@@ -13273,7 +12788,9 @@ zone_notify(dns_zone_t *zone, isc_time_t *now) {
                        goto next;
                }
 
-               if (notify_isqueued(zone, flags, NULL, &dst, key, transport)) {
+               if (dns_notify_isqueued(&zone->notifyctx, flags, NULL, &dst,
+                                       key, transport))
+               {
                        if (key != NULL) {
                                dns_tsigkey_detach(&key);
                        }
@@ -13302,13 +12819,14 @@ zone_notify(dns_zone_t *zone, isc_time_t *now) {
                }
 
                ISC_LIST_APPEND(zone->notifyctx.notifies, notify, link);
-               result = notify_send_queue(notify, startup);
+               result = dns_notify_queue(notify, startup);
                if (result != ISC_R_SUCCESS) {
-                       notify_destroy(notify, true);
+                       dns_notify_destroy(notify, true);
                }
                if (!loggednotify) {
-                       notify_log(zone, ISC_LOG_INFO,
-                                  "sending notifies (serial %u)", serial);
+                       dns_zone_logc(zone, DNS_LOGCATEGORY_NOTIFY,
+                                     ISC_LOG_INFO,
+                                     "sending notifies (serial %u)", serial);
                        loggednotify = true;
                }
        next:
@@ -13352,14 +12870,15 @@ zone_notify(dns_zone_t *zone, isc_time_t *now) {
                }
 
                if (!loggednotify) {
-                       notify_log(zone, ISC_LOG_INFO,
-                                  "sending notifies (serial %u)", serial);
+                       dns_zone_logc(zone, DNS_LOGCATEGORY_NOTIFY,
+                                     ISC_LOG_INFO,
+                                     "sending notifies (serial %u)", serial);
                        loggednotify = true;
                }
 
                LOCK_ZONE(zone);
-               isqueued = notify_isqueued(zone, flags, &ns.name, NULL, NULL,
-                                          NULL);
+               isqueued = dns_notify_isqueued(&zone->notifyctx, flags,
+                                              &ns.name, NULL, NULL, NULL);
                UNLOCK_ZONE(zone);
                if (isqueued) {
                        continue;
@@ -13370,7 +12889,7 @@ zone_notify(dns_zone_t *zone, isc_time_t *now) {
                LOCK_ZONE(zone);
                ISC_LIST_APPEND(zone->notifyctx.notifies, notify, link);
                UNLOCK_ZONE(zone);
-               notify_find_address(notify);
+               dns_notify_find_address(notify);
        }
        dns_rdataset_disassociate(&nsrdset);
 
@@ -15311,7 +14830,7 @@ zone_shutdown(void *arg) {
 
        checkds_cancel(zone);
 
-       notify_cancel(zone);
+       dns_notify_cancel(&zone->notifyctx);
 
        forward_cancel(zone);
 
@@ -15596,135 +15115,6 @@ cancel_refresh(dns_zone_t *zone) {
        zone_settimer(zone, &now);
 }
 
-static isc_result_t
-notify_createmessage(dns_zone_t *zone, unsigned int flags,
-                    dns_message_t **messagep) {
-       dns_db_t *zonedb = NULL;
-       dns_dbnode_t *node = NULL;
-       dns_dbversion_t *version = NULL;
-       dns_message_t *message = NULL;
-       dns_rdataset_t rdataset;
-       dns_rdata_t rdata = DNS_RDATA_INIT;
-
-       dns_name_t *tempname = NULL;
-       dns_rdata_t *temprdata = NULL;
-       dns_rdatalist_t *temprdatalist = NULL;
-       dns_rdataset_t *temprdataset = NULL;
-
-       isc_result_t result;
-       isc_region_t r;
-       isc_buffer_t *b = NULL;
-
-       REQUIRE(DNS_ZONE_VALID(zone));
-       REQUIRE(messagep != NULL && *messagep == NULL);
-
-       dns_message_create(zone->mctx, NULL, NULL, DNS_MESSAGE_INTENTRENDER,
-                          &message);
-
-       message->opcode = dns_opcode_notify;
-       message->flags |= DNS_MESSAGEFLAG_AA;
-       message->rdclass = zone->rdclass;
-
-       dns_message_gettempname(message, &tempname);
-
-       dns_message_gettemprdataset(message, &temprdataset);
-
-       /*
-        * Make question.
-        */
-       dns_name_clone(&zone->origin, tempname);
-       dns_rdataset_makequestion(temprdataset, zone->rdclass,
-                                 dns_rdatatype_soa);
-       ISC_LIST_APPEND(tempname->list, temprdataset, link);
-       dns_message_addname(message, tempname, DNS_SECTION_QUESTION);
-       tempname = NULL;
-       temprdataset = NULL;
-
-       if ((flags & DNS_NOTIFY_NOSOA) != 0) {
-               goto done;
-       }
-
-       dns_message_gettempname(message, &tempname);
-       dns_message_gettemprdata(message, &temprdata);
-       dns_message_gettemprdataset(message, &temprdataset);
-       dns_message_gettemprdatalist(message, &temprdatalist);
-
-       ZONEDB_LOCK(&zone->dblock, isc_rwlocktype_read);
-       INSIST(zone->db != NULL); /* XXXJT: is this assumption correct? */
-       dns_db_attach(zone->db, &zonedb);
-       ZONEDB_UNLOCK(&zone->dblock, isc_rwlocktype_read);
-
-       dns_name_clone(&zone->origin, tempname);
-       dns_db_currentversion(zonedb, &version);
-       result = dns_db_findnode(zonedb, tempname, false, &node);
-       if (result != ISC_R_SUCCESS) {
-               goto soa_cleanup;
-       }
-
-       dns_rdataset_init(&rdataset);
-       result = dns_db_findrdataset(zonedb, node, version, dns_rdatatype_soa,
-                                    dns_rdatatype_none, 0, &rdataset, NULL);
-       if (result != ISC_R_SUCCESS) {
-               goto soa_cleanup;
-       }
-       result = dns_rdataset_first(&rdataset);
-       if (result != ISC_R_SUCCESS) {
-               goto soa_cleanup;
-       }
-       dns_rdataset_current(&rdataset, &rdata);
-       dns_rdata_toregion(&rdata, &r);
-       isc_buffer_allocate(zone->mctx, &b, r.length);
-       isc_buffer_putmem(b, r.base, r.length);
-       isc_buffer_usedregion(b, &r);
-       dns_rdata_fromregion(temprdata, rdata.rdclass, rdata.type, &r);
-       dns_message_takebuffer(message, &b);
-       result = dns_rdataset_next(&rdataset);
-       dns_rdataset_disassociate(&rdataset);
-       if (result != ISC_R_NOMORE) {
-               goto soa_cleanup;
-       }
-       temprdatalist->rdclass = rdata.rdclass;
-       temprdatalist->type = rdata.type;
-       temprdatalist->ttl = rdataset.ttl;
-       ISC_LIST_APPEND(temprdatalist->rdata, temprdata, link);
-
-       dns_rdatalist_tordataset(temprdatalist, temprdataset);
-
-       ISC_LIST_APPEND(tempname->list, temprdataset, link);
-       dns_message_addname(message, tempname, DNS_SECTION_ANSWER);
-       temprdatalist = NULL;
-       temprdataset = NULL;
-       temprdata = NULL;
-       tempname = NULL;
-
-soa_cleanup:
-       if (node != NULL) {
-               dns_db_detachnode(&node);
-       }
-       if (version != NULL) {
-               dns_db_closeversion(zonedb, &version, false);
-       }
-       if (zonedb != NULL) {
-               dns_db_detach(&zonedb);
-       }
-       if (tempname != NULL) {
-               dns_message_puttempname(message, &tempname);
-       }
-       if (temprdata != NULL) {
-               dns_message_puttemprdata(message, &temprdata);
-       }
-       if (temprdataset != NULL) {
-               dns_message_puttemprdataset(message, &temprdataset);
-       }
-       if (temprdatalist != NULL) {
-               dns_message_puttemprdatalist(message, &temprdatalist);
-       }
-
-done:
-       *messagep = message;
-       return ISC_R_SUCCESS;
-}
-
 isc_result_t
 dns_zone_notifyreceive(dns_zone_t *zone, isc_sockaddr_t *from,
                       isc_sockaddr_t *to, dns_message_t *msg) {
@@ -16329,15 +15719,6 @@ dns_zone_logv(dns_zone_t *zone, isc_logcategory_t category, int level,
                      zstr, zone->strnamerd, message);
 }
 
-static void
-notify_log(dns_zone_t *zone, int level, const char *fmt, ...) {
-       va_list ap;
-
-       va_start(ap, fmt);
-       dns_zone_logv(zone, DNS_LOGCATEGORY_NOTIFY, level, NULL, fmt, ap);
-       va_end(ap);
-}
-
 void
 dns_zone_logc(dns_zone_t *zone, isc_logcategory_t category, int level,
              const char *fmt, ...) {
@@ -16584,69 +15965,6 @@ dns__zone_exiting(dns_zone_t *zone) {
        return DNS_ZONE_FLAG(zone, DNS_ZONEFLG_EXITING) != 0;
 }
 
-static void
-notify_done(void *arg) {
-       dns_request_t *request = (dns_request_t *)arg;
-       dns_notify_t *notify = dns_request_getarg(request);
-       isc_result_t result;
-       dns_message_t *message = NULL;
-       isc_buffer_t buf;
-       char rcode[128];
-       char addrbuf[ISC_SOCKADDR_FORMATSIZE];
-
-       REQUIRE(DNS_NOTIFY_VALID(notify));
-
-       isc_buffer_init(&buf, rcode, sizeof(rcode));
-       isc_sockaddr_format(&notify->dst, addrbuf, sizeof(addrbuf));
-       dns_message_create(notify->zone->mctx, NULL, NULL,
-                          DNS_MESSAGE_INTENTPARSE, &message);
-
-       result = dns_request_getresult(request);
-       if (result != ISC_R_SUCCESS) {
-               goto fail;
-       }
-
-       result = dns_request_getresponse(request, message,
-                                        DNS_MESSAGEPARSE_PRESERVEORDER);
-       if (result != ISC_R_SUCCESS) {
-               goto fail;
-       }
-
-       result = dns_rcode_totext(message->rcode, &buf);
-       if (result == ISC_R_SUCCESS) {
-               notify_log(notify->zone, ISC_LOG_DEBUG(3),
-                          "notify response from %s: %.*s", addrbuf,
-                          (int)buf.used, rcode);
-       }
-
-fail:
-       dns_message_detach(&message);
-
-       if (result == ISC_R_SUCCESS) {
-               notify_log(notify->zone, ISC_LOG_DEBUG(1),
-                          "notify to %s successful", addrbuf);
-       } else if (result == ISC_R_SHUTTINGDOWN || result == ISC_R_CANCELED) {
-               /* just destroy the notify */
-       } else if ((notify->flags & DNS_NOTIFY_TCP) == 0) {
-               notify_log(notify->zone, ISC_LOG_NOTICE,
-                          "notify to %s failed: %s: retrying over TCP",
-                          addrbuf, isc_result_totext(result));
-               notify->flags |= DNS_NOTIFY_TCP;
-               dns_request_destroy(&notify->request);
-               notify_send_queue(notify, notify->flags & DNS_NOTIFY_STARTUP);
-               return;
-       } else if (result == ISC_R_TIMEDOUT) {
-               notify_log(notify->zone, ISC_LOG_WARNING,
-                          "notify to %s failed: %s: retries exceeded", addrbuf,
-                          isc_result_totext(result));
-       } else {
-               notify_log(notify->zone, ISC_LOG_WARNING,
-                          "notify to %s failed: %s", addrbuf,
-                          isc_result_totext(result));
-       }
-       notify_destroy(notify, false);
-}
-
 struct rss {
        dns_zone_t *zone;
        dns_db_t *db;