]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Refactor zone.c, use dns_remote_t structure
authorMatthijs Mekking <matthijs@isc.org>
Wed, 26 Oct 2022 12:40:30 +0000 (14:40 +0200)
committerMatthijs Mekking <matthijs@isc.org>
Fri, 23 Dec 2022 13:36:50 +0000 (13:36 +0000)
Use the new dns_remote_t structure for remote server communication to
primaries, parental agents, etc.

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

index a029c6e43fd2241c24fd508afd5efbd46dc87f5a..d077806e071762c639492c0ba1887cf25336109d 100644 (file)
@@ -634,7 +634,7 @@ dns_zone_dumptostream(dns_zone_t *zone, FILE *fd, dns_masterformat_t format,
  */
 
 void
-dns_zone_setprimaries(dns_zone_t *zone, const isc_sockaddr_t *primaries,
+dns_zone_setprimaries(dns_zone_t *zone, isc_sockaddr_t *addresses,
                      dns_name_t **keynames, dns_name_t **tlsnames,
                      uint32_t count);
 /*%<
@@ -642,11 +642,11 @@ dns_zone_setprimaries(dns_zone_t *zone, const isc_sockaddr_t *primaries,
  *
  * Require:
  *\li  'zone' to be a valid zone.
- *\li  'primaries' array of isc_sockaddr_t with port set or NULL.
+ *\li  'addresses' array of isc_sockaddr_t with port set or NULL.
  *\li  'count' the number of primaries.
  *\li  'keynames' array of dns_name_t's for tsig keys or NULL.
  *
- *\li  If 'primaries' is NULL then 'count' must be zero.
+ *\li  If 'addresses' is NULL then 'count' must be zero.
  *
  * Returns:
  *\li  #ISC_R_SUCCESS
@@ -655,7 +655,7 @@ dns_zone_setprimaries(dns_zone_t *zone, const isc_sockaddr_t *primaries,
  */
 
 void
-dns_zone_setparentals(dns_zone_t *zone, const isc_sockaddr_t *parentals,
+dns_zone_setparentals(dns_zone_t *zone, isc_sockaddr_t *addresses,
                      dns_name_t **keynames, dns_name_t **tlsnames,
                      uint32_t count);
 /*%<
@@ -663,11 +663,11 @@ dns_zone_setparentals(dns_zone_t *zone, const isc_sockaddr_t *parentals,
  *
  * Require:
  *\li  'zone' to be a valid zone.
- *\li  'parentals' array of isc_sockaddr_t with port set or NULL.
+ *\li  'addresses' array of isc_sockaddr_t with port set or NULL.
  *\li  'count' the number of primaries.
  *\li  'keynames' array of dns_name_t's for tsig keys or NULL.
  *
- *\li  If 'parentals' is NULL then 'count' must be zero.
+ *\li  If 'addresses' is NULL then 'count' must be zero.
  *
  * Returns:
  *\li  #ISC_R_SUCCESS
@@ -676,29 +676,8 @@ dns_zone_setparentals(dns_zone_t *zone, const isc_sockaddr_t *parentals,
  */
 
 void
-dns_zone_setparentals(dns_zone_t *zone, const isc_sockaddr_t *parentals,
-                     dns_name_t **keynames, dns_name_t **tlsnames,
-                     uint32_t count);
-/*%<
- *     Set the list of parental agents for the zone.
- *
- * Require:
- *\li  'zone' to be a valid zone.
- *\li  'parentals' array of isc_sockaddr_t with port set or NULL.
- *\li  'count' the number of parentals.
- *\li  'keynames' array of dns_name_t's for tsig keys or NULL.
- *
- *\li  If 'parentals' is NULL then 'count' must be zero.
- *
- * Returns:
- *\li  #ISC_R_SUCCESS
- *\li  #ISC_R_NOMEMORY
- *\li      Any result dns_name_dup() can return, if keynames!=NULL
- */
-
-void
-dns_zone_setalsonotify(dns_zone_t *zone, const isc_sockaddr_t *notify,
-                      const isc_dscp_t *dscps, dns_name_t **keynames,
+dns_zone_setalsonotify(dns_zone_t *zone, isc_sockaddr_t *addresses,
+                      isc_dscp_t *dscps, dns_name_t **keynames,
                       dns_name_t **tlsnames, uint32_t count);
 /*%<
  *     Set the list of additional servers to be notified when
@@ -709,7 +688,7 @@ dns_zone_setalsonotify(dns_zone_t *zone, const isc_sockaddr_t *notify,
  *
  * Require:
  *\li  'zone' to be a valid zone.
- *\li  'notify' to be non-NULL if count != 0.
+ *\li  'addresses' to be non-NULL if count != 0.
  *\li  'count' to be the number of notifiees.
  *
  * Returns:
index 9b4455d3cff2ad5eae9040bca3099c0221363e0e..898c1b2673b50fc8c095288182ba46f4d5cae20c 100644 (file)
@@ -77,6 +77,7 @@
 #include <dns/rdatasetiter.h>
 #include <dns/rdatastruct.h>
 #include <dns/rdatatype.h>
+#include <dns/remote.h>
 #include <dns/request.h>
 #include <dns/resolver.h>
 #include <dns/rriterator.h>
@@ -315,32 +316,18 @@ struct dns_zone {
 
        uint32_t maxrecords;
 
-       isc_sockaddr_t *primaries;
-       isc_dscp_t *primarydscps;
-       dns_name_t **primarykeynames;
-       dns_name_t **primarytlsnames;
-       bool *primariesok;
-       unsigned int primariescnt;
-       unsigned int curprimary;
-       isc_sockaddr_t primaryaddr;
+       dns_remote_t primaries;
 
-       isc_sockaddr_t *parentals;
-       isc_dscp_t *parentaldscps;
-       dns_name_t **parentalkeynames;
-       dns_name_t **parentaltlsnames;
+       dns_remote_t parentals;
        dns_dnsseckeylist_t checkds_ok;
-       unsigned int parentalscnt;
-       isc_sockaddr_t parentaladdr;
 
+       dns_remote_t notify;
        dns_notifytype_t notifytype;
-       isc_sockaddr_t *notify;
-       dns_name_t **notifykeynames;
-       dns_name_t **notifytlsnames;
-       isc_dscp_t *notifydscp;
-       unsigned int notifycnt;
        isc_sockaddr_t notifyfrom;
+
        isc_task_t *task;
        isc_task_t *loadtask;
+
        isc_sockaddr_t notifysrc4;
        isc_sockaddr_t notifysrc6;
        isc_sockaddr_t parentalsrc4;
@@ -358,6 +345,7 @@ struct dns_zone {
        isc_dscp_t xfrsource6dscp;
        isc_dscp_t altxfrsource4dscp;
        isc_dscp_t altxfrsource6dscp;
+
        dns_xfrin_ctx_t *xfr;       /* task locked */
        dns_tsigkey_t *tsigkey;     /* key used for xfr */
        dns_transport_t *transport; /* transport used for xfr */
@@ -1156,6 +1144,9 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx, unsigned int tid) {
                .updatemethod = dns_updatemethod_increment,
                .tid = tid,
        };
+       dns_remote_t r = {
+               .magic = DNS_REMOTE_MAGIC,
+       };
 
        REQUIRE(zonep != NULL && *zonep == NULL);
        REQUIRE(mctx != NULL);
@@ -1211,6 +1202,10 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx, unsigned int tid) {
        ISC_LIST_INIT(zone->rss_events);
        ISC_LIST_INIT(zone->rss_post);
 
+       zone->primaries = r;
+       zone->parentals = r;
+       zone->notify = r;
+
        result = isc_stats_create(mctx, &zone->gluecachestats,
                                  dns_gluecachestatscounter_max);
        if (result != ISC_R_SUCCESS) {
@@ -1371,9 +1366,11 @@ zone_free(dns_zone_t *zone) {
                dns_catz_catzs_detach(&zone->catzs);
        }
        zone_freedbargs(zone);
-       dns_zone_setparentals(zone, NULL, NULL, NULL, 0);
-       dns_zone_setprimaries(zone, NULL, NULL, NULL, 0);
-       dns_zone_setalsonotify(zone, NULL, NULL, NULL, NULL, 0);
+
+       dns_zone_setparentals(zone, NULL, NULL, NULL, NULL, 0);
+       dns_zone_setprimaries(zone, NULL, NULL, NULL, NULL, 0);
+       dns_zone_setalsonotify(zone, NULL, NULL, NULL, NULL, NULL, 0);
+
        zone->check_names = dns_severity_ignore;
        if (zone->update_acl != NULL) {
                dns_acl_detach(&zone->update_acl);
@@ -1900,7 +1897,8 @@ dns_zone_isdynamic(dns_zone_t *zone, bool ignore_freeze) {
 
        if (zone->type == dns_zone_secondary || zone->type == dns_zone_mirror ||
            zone->type == dns_zone_stub || zone->type == dns_zone_key ||
-           (zone->type == dns_zone_redirect && zone->primaries != NULL))
+           (zone->type == dns_zone_redirect &&
+            dns_remote_addresses(&zone->primaries) != NULL))
        {
                return (true);
        }
@@ -2326,7 +2324,8 @@ zone_load(dns_zone_t *zone, unsigned int flags, bool locked) {
 
        if ((zone->type == dns_zone_secondary ||
             zone->type == dns_zone_mirror || zone->type == dns_zone_stub ||
-            (zone->type == dns_zone_redirect && zone->primaries != NULL)) &&
+            (zone->type == dns_zone_redirect &&
+             dns_remote_addresses(&zone->primaries) != NULL)) &&
            rbt)
        {
                if (zone->stream == NULL &&
@@ -2383,7 +2382,7 @@ zone_load(dns_zone_t *zone, unsigned int flags, bool locked) {
                        result = DNS_R_NOMASTERFILE;
                        if (zone->type == dns_zone_primary ||
                            (zone->type == dns_zone_redirect &&
-                            zone->primaries == NULL))
+                            dns_remote_addresses(&zone->primaries) == NULL))
                        {
                                dns_zone_logc(zone, DNS_LOGCATEGORY_ZONELOAD,
                                              ISC_LOG_ERROR,
@@ -2535,7 +2534,8 @@ get_primary_options(dns_zone_t *zone) {
 
        options = DNS_MASTER_ZONE | DNS_MASTER_RESIGN;
        if (zone->type == dns_zone_secondary || zone->type == dns_zone_mirror ||
-           (zone->type == dns_zone_redirect && zone->primaries == NULL))
+           (zone->type == dns_zone_redirect &&
+            dns_remote_addresses(&zone->primaries) == NULL))
        {
                options |= DNS_MASTER_SECONDARY;
        }
@@ -4938,7 +4938,7 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime,
                    zone->type == dns_zone_mirror ||
                    zone->type == dns_zone_stub ||
                    (zone->type == dns_zone_redirect &&
-                    zone->primaries == NULL))
+                    dns_remote_addresses(&zone->primaries) == NULL))
                {
                        if (result == ISC_R_FILENOTFOUND) {
                                dns_zone_logc(zone, DNS_LOGCATEGORY_ZONELOAD,
@@ -5247,7 +5247,7 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime,
                    zone->type == dns_zone_mirror ||
                    zone->type == dns_zone_stub ||
                    (zone->type == dns_zone_redirect &&
-                    zone->primaries != NULL))
+                    dns_remote_addresses(&zone->primaries) != NULL))
                {
                        isc_time_t t;
                        uint32_t delay;
@@ -5459,7 +5459,8 @@ cleanup:
        }
        if (zone->type == dns_zone_secondary || zone->type == dns_zone_mirror ||
            zone->type == dns_zone_stub || zone->type == dns_zone_key ||
-           (zone->type == dns_zone_redirect && zone->primaries != NULL))
+           (zone->type == dns_zone_redirect &&
+            dns_remote_addresses(&zone->primaries) != NULL))
        {
                if (result != ISC_R_NOMEMORY) {
                        if (zone->journal != NULL) {
@@ -6257,249 +6258,70 @@ dns_zone_getnotifysrc6dscp(dns_zone_t *zone) {
        return (zone->notifysrc6dscp);
 }
 
-static bool
-same_addrs(isc_sockaddr_t const *oldlist, isc_sockaddr_t const *newlist,
-          uint32_t count) {
-       unsigned int i;
-
-       for (i = 0; i < count; i++) {
-               if (!isc_sockaddr_equal(&oldlist[i], &newlist[i])) {
-                       return (false);
-               }
-       }
-       return (true);
-}
-
-static bool
-same_names(dns_name_t *const *oldlist, dns_name_t *const *newlist,
-          uint32_t count) {
-       unsigned int i;
-
-       if (oldlist == NULL && newlist == NULL) {
-               return (true);
-       }
-       if (oldlist == NULL || newlist == NULL) {
-               return (false);
-       }
-
-       for (i = 0; i < count; i++) {
-               if (oldlist[i] == NULL && newlist[i] == NULL) {
-                       continue;
-               }
-               if (oldlist[i] == NULL || newlist[i] == NULL ||
-                   !dns_name_equal(oldlist[i], newlist[i]))
-               {
-                       return (false);
-               }
-       }
-       return (true);
-}
-
-static void
-clear_serverslist(isc_sockaddr_t **addrsp, isc_dscp_t **dscpsp,
-                 dns_name_t ***keynamesp, dns_name_t ***tlsnamesp,
-                 unsigned int *countp, isc_mem_t *mctx) {
-       unsigned int count;
-       isc_sockaddr_t *addrs;
-       isc_dscp_t *dscps;
-       dns_name_t **keynames;
-       dns_name_t **tlsnames;
-
-       REQUIRE(countp != NULL);
-       REQUIRE(addrsp != NULL);
-       REQUIRE(dscpsp != NULL);
-       REQUIRE(keynamesp != NULL);
-       REQUIRE(tlsnamesp != NULL);
-
-       count = *countp;
-       *countp = 0;
-       addrs = *addrsp;
-       *addrsp = NULL;
-       dscps = *dscpsp;
-       *dscpsp = NULL;
-       keynames = *keynamesp;
-       *keynamesp = NULL;
-       tlsnames = *tlsnamesp;
-       *tlsnamesp = NULL;
-
-       if (addrs != NULL) {
-               isc_mem_put(mctx, addrs, count * sizeof(isc_sockaddr_t));
-       }
-
-       if (dscps != NULL) {
-               isc_mem_put(mctx, dscps, count * sizeof(isc_dscp_t));
-       }
-
-       if (keynames != NULL) {
-               unsigned int i;
-               for (i = 0; i < count; i++) {
-                       if (keynames[i] != NULL) {
-                               dns_name_free(keynames[i], mctx);
-                               isc_mem_put(mctx, keynames[i],
-                                           sizeof(dns_name_t));
-                               keynames[i] = NULL;
-                       }
-               }
-               isc_mem_put(mctx, keynames, count * sizeof(dns_name_t *));
-       }
-
-       if (tlsnames != NULL) {
-               unsigned int i;
-               for (i = 0; i < count; i++) {
-                       if (tlsnames[i] != NULL) {
-                               dns_name_free(tlsnames[i], mctx);
-                               isc_mem_put(mctx, tlsnames[i],
-                                           sizeof(dns_name_t));
-                               tlsnames[i] = NULL;
-                       }
-               }
-               isc_mem_put(mctx, tlsnames, count * sizeof(dns_name_t *));
-       }
-}
-
-static void
-set_serverslist(unsigned int count, const isc_sockaddr_t *addrs,
-               isc_sockaddr_t **newaddrsp, const isc_dscp_t *dscp,
-               isc_dscp_t **newdscpp, dns_name_t **keynames,
-               dns_name_t ***newkeynamesp, dns_name_t **tlsnames,
-               dns_name_t ***newtlsnamesp, isc_mem_t *mctx) {
-       isc_sockaddr_t *newaddrs = NULL;
-       isc_dscp_t *newdscp = NULL;
-       dns_name_t **newkeynames = NULL;
-       dns_name_t **newtlsnames = NULL;
-       unsigned int i;
-
-       REQUIRE(newaddrsp != NULL && *newaddrsp == NULL);
-       REQUIRE(newdscpp != NULL && *newdscpp == NULL);
-       REQUIRE(newkeynamesp != NULL && *newkeynamesp == NULL);
-       REQUIRE(newtlsnamesp != NULL && *newtlsnamesp == NULL);
-
-       newaddrs = isc_mem_get(mctx, count * sizeof(*newaddrs));
-       memmove(newaddrs, addrs, count * sizeof(*newaddrs));
-
-       if (dscp != NULL) {
-               newdscp = isc_mem_get(mctx, count * sizeof(*newdscp));
-               memmove(newdscp, dscp, count * sizeof(*newdscp));
-       } else {
-               newdscp = NULL;
-       }
-
-       if (keynames != NULL) {
-               newkeynames = isc_mem_get(mctx, count * sizeof(*newkeynames));
-               for (i = 0; i < count; i++) {
-                       newkeynames[i] = NULL;
-               }
-               for (i = 0; i < count; i++) {
-                       if (keynames[i] != NULL) {
-                               newkeynames[i] =
-                                       isc_mem_get(mctx, sizeof(dns_name_t));
-                               dns_name_init(newkeynames[i], NULL);
-                               dns_name_dup(keynames[i], mctx, newkeynames[i]);
-                       }
-               }
-       }
-
-       if (tlsnames != NULL) {
-               newtlsnames = isc_mem_get(mctx, count * sizeof(*newtlsnames));
-               for (i = 0; i < count; i++) {
-                       newtlsnames[i] = NULL;
-               }
-               for (i = 0; i < count; i++) {
-                       if (tlsnames[i] != NULL) {
-                               newtlsnames[i] =
-                                       isc_mem_get(mctx, sizeof(dns_name_t));
-                               dns_name_init(newtlsnames[i], NULL);
-                               dns_name_dup(tlsnames[i], mctx, newtlsnames[i]);
-                       }
-               }
-       }
-
-       *newdscpp = newdscp;
-       *newaddrsp = newaddrs;
-       *newkeynamesp = newkeynames;
-       *newtlsnamesp = newtlsnames;
-}
-
 void
-dns_zone_setalsonotify(dns_zone_t *zone, const isc_sockaddr_t *notify,
-                      const isc_dscp_t *dscps, dns_name_t **keynames,
+dns_zone_setalsonotify(dns_zone_t *zone, isc_sockaddr_t *addresses,
+                      isc_dscp_t *dscps, dns_name_t **keynames,
                       dns_name_t **tlsnames, uint32_t count) {
-       isc_sockaddr_t *newaddrs = NULL;
-       isc_dscp_t *newdscps = NULL;
-       dns_name_t **newkeynames = NULL;
-       dns_name_t **newtlsnames = NULL;
+       dns_remote_t remote;
 
        REQUIRE(DNS_ZONE_VALID(zone));
-       REQUIRE(count == 0 || notify != NULL);
-       if (keynames != NULL) {
-               REQUIRE(count != 0);
-       }
 
        LOCK_ZONE(zone);
 
-       if (count == zone->notifycnt &&
-           same_addrs(zone->notify, notify, count) &&
-           same_names(zone->notifykeynames, keynames, count) &&
-           same_names(zone->notifytlsnames, tlsnames, count))
-       {
+       remote.magic = DNS_REMOTE_MAGIC;
+       remote.addresses = addresses;
+       remote.dscps = dscps;
+       remote.keynames = keynames;
+       remote.tlsnames = tlsnames;
+       remote.addrcnt = count;
+
+       if (dns_remote_equal(&zone->notify, &remote)) {
                goto unlock;
        }
 
-       clear_serverslist(&zone->notify, &zone->notifydscp,
-                         &zone->notifykeynames, &zone->notifytlsnames,
-                         &zone->notifycnt, zone->mctx);
+       dns_remote_clear(&zone->notify);
 
+       /*
+        * If count == 0, don't allocate any space for servers to notify.
+        */
        if (count == 0) {
                goto unlock;
        }
 
        /*
-        * Set up the notify and notifykey lists
+        * Now set up the notify address and key lists.
         */
-       set_serverslist(count, notify, &newaddrs, dscps, &newdscps, keynames,
-                       &newkeynames, tlsnames, &newtlsnames, zone->mctx);
+       dns_remote_init(&zone->notify, count, addresses, dscps, keynames,
+                       tlsnames, true, zone->mctx);
 
-       /*
-        * Everything is ok so attach to the zone.
-        */
-       zone->notify = newaddrs;
-       zone->notifydscp = newdscps;
-       zone->notifykeynames = newkeynames;
-       zone->notifytlsnames = newtlsnames;
-       zone->notifycnt = count;
 unlock:
        UNLOCK_ZONE(zone);
 }
 
 void
-dns_zone_setprimaries(dns_zone_t *zone, const isc_sockaddr_t *primaries,
+dns_zone_setprimaries(dns_zone_t *zone, isc_sockaddr_t *addresses,
                      dns_name_t **keynames, dns_name_t **tlsnames,
                      uint32_t count) {
-       isc_sockaddr_t *newaddrs = NULL;
-       isc_dscp_t *newdscps = NULL;
-       dns_name_t **newkeynames = NULL;
-       dns_name_t **newtlsnames = NULL;
-       bool *newok;
-       unsigned int i;
+       dns_remote_t remote;
 
        REQUIRE(DNS_ZONE_VALID(zone));
-       REQUIRE(count == 0 || primaries != NULL);
-       if (keynames != NULL || tlsnames != NULL) {
-               REQUIRE(count != 0);
-       }
 
        LOCK_ZONE(zone);
+
+       remote.magic = DNS_REMOTE_MAGIC;
+       remote.addresses = addresses;
+       remote.dscps = NULL;
+       remote.keynames = keynames;
+       remote.tlsnames = tlsnames;
+       remote.addrcnt = count;
+
        /*
         * The refresh code assumes that 'primaries' wouldn't change under it.
         * If it will change then kill off any current refresh in progress
         * and update the primaries info.  If it won't change then we can just
         * unlock and exit.
         */
-       if (count != zone->primariescnt ||
-           !same_addrs(zone->primaries, primaries, count) ||
-           !same_names(zone->primarykeynames, keynames, count) ||
-           !same_names(zone->primarytlsnames, tlsnames, count))
-       {
+       if (!dns_remote_equal(&zone->primaries, &remote)) {
                if (zone->request != NULL) {
                        dns_request_cancel(zone->request);
                }
@@ -6507,51 +6329,21 @@ dns_zone_setprimaries(dns_zone_t *zone, const isc_sockaddr_t *primaries,
                goto unlock;
        }
 
+       dns_remote_clear(&zone->primaries);
+
        /*
-        * This needs to happen before clear_addresskeylist() sets
-        * zone->primariescnt to 0:
-        */
-       if (zone->primariesok != NULL) {
-               isc_mem_put(zone->mctx, zone->primariesok,
-                           zone->primariescnt * sizeof(bool));
-               zone->primariesok = NULL;
-       }
-       clear_serverslist(&zone->primaries, &zone->primarydscps,
-                         &zone->primarykeynames, &zone->primarytlsnames,
-                         &zone->primariescnt, zone->mctx);
-       /*
-        * If count == 0, don't allocate any space for primaries, primariesok or
-        * keynames so internally, those pointers are NULL if count == 0
+        * If count == 0, don't allocate any space for primaries.
         */
        if (count == 0) {
                goto unlock;
        }
 
        /*
-        * primariesok must contain count elements
+        * Now set up the primaries and primary key lists.
         */
-       newok = isc_mem_get(zone->mctx, count * sizeof(*newok));
-       for (i = 0; i < count; i++) {
-               newok[i] = false;
-       }
+       dns_remote_init(&zone->primaries, count, addresses, NULL, keynames,
+                       tlsnames, true, zone->mctx);
 
-       /*
-        * Now set up the primaries and primary key lists
-        */
-       set_serverslist(count, primaries, &newaddrs, NULL, &newdscps, keynames,
-                       &newkeynames, tlsnames, &newtlsnames, zone->mctx);
-       INSIST(newdscps == NULL);
-
-       /*
-        * Everything is ok so attach to the zone.
-        */
-       zone->curprimary = 0;
-       zone->primariesok = newok;
-       zone->primaries = newaddrs;
-       zone->primarydscps = newdscps;
-       zone->primarykeynames = newkeynames;
-       zone->primarytlsnames = newtlsnames;
-       zone->primariescnt = count;
        DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NOPRIMARIES);
 
 unlock:
@@ -6559,48 +6351,40 @@ unlock:
 }
 
 void
-dns_zone_setparentals(dns_zone_t *zone, const isc_sockaddr_t *parentals,
+dns_zone_setparentals(dns_zone_t *zone, isc_sockaddr_t *addresses,
                      dns_name_t **keynames, dns_name_t **tlsnames,
                      uint32_t count) {
-       isc_sockaddr_t *newaddrs = NULL;
-       isc_dscp_t *newdscps = NULL;
-       dns_name_t **newkeynames = NULL;
-       dns_name_t **newtlsnames = NULL;
+       dns_remote_t remote;
 
        REQUIRE(DNS_ZONE_VALID(zone));
-       REQUIRE(count == 0 || parentals != NULL);
-       if (keynames != NULL || tlsnames != NULL) {
-               REQUIRE(count != 0);
-       }
 
        LOCK_ZONE(zone);
 
-       clear_serverslist(&zone->parentals, &zone->parentaldscps,
-                         &zone->parentalkeynames, &zone->parentaltlsnames,
-                         &zone->parentalscnt, zone->mctx);
-       /*
-        * If count == 0, don't allocate any space for parentals, or keynames
-        * so internally, those pointers are NULL if count == 0
-        */
-       if (count == 0) {
+       remote.magic = DNS_REMOTE_MAGIC;
+       remote.addresses = addresses;
+       remote.dscps = NULL;
+       remote.keynames = keynames;
+       remote.tlsnames = tlsnames;
+       remote.addrcnt = count;
+
+       if (dns_remote_equal(&zone->parentals, &remote)) {
                goto unlock;
        }
 
+       dns_remote_clear(&zone->parentals);
+
        /*
-        * Now set up the parentals and parental key lists
+        * If count == 0, don't allocate any space for parentals.
         */
-       set_serverslist(count, parentals, &newaddrs, NULL, &newdscps, keynames,
-                       &newkeynames, tlsnames, &newtlsnames, zone->mctx);
-       INSIST(newdscps == NULL);
+       if (count == 0) {
+               goto unlock;
+       }
 
        /*
-        * Everything is ok so attach to the zone.
+        * Now set up the parentals and parental key lists.
         */
-       zone->parentals = newaddrs;
-       zone->parentaldscps = newdscps;
-       zone->parentalkeynames = newkeynames;
-       zone->parentaltlsnames = newtlsnames;
-       zone->parentalscnt = count;
+       dns_remote_init(&zone->parentals, count, addresses, NULL, keynames,
+                       tlsnames, true, zone->mctx);
 
        dns_zone_log(zone, ISC_LOG_NOTICE, "checkds: set %u parentals", count);
 
@@ -11519,7 +11303,7 @@ zone_maintenance(dns_zone_t *zone) {
         */
        switch (zone->type) {
        case dns_zone_redirect:
-               if (zone->primaries == NULL) {
+               if (dns_remote_addresses(&zone->primaries) == NULL) {
                        break;
                }
                FALLTHROUGH;
@@ -11544,7 +11328,7 @@ zone_maintenance(dns_zone_t *zone) {
         */
        switch (zone->type) {
        case dns_zone_redirect:
-               if (zone->primaries == NULL) {
+               if (dns_remote_addresses(&zone->primaries) == NULL) {
                        break;
                }
                FALLTHROUGH;
@@ -11829,7 +11613,6 @@ static void
 zone_refresh(dns_zone_t *zone) {
        isc_interval_t i;
        uint32_t oldflags;
-       unsigned int j;
        isc_result_t result;
 
        REQUIRE(DNS_ZONE_VALID(zone));
@@ -11845,7 +11628,7 @@ zone_refresh(dns_zone_t *zone) {
         */
 
        oldflags = atomic_load(&zone->flags);
-       if (zone->primariescnt == 0) {
+       if (dns_remote_addresses(&zone->primaries) == NULL) {
                DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_NOPRIMARIES);
                if ((oldflags & DNS_ZONEFLG_NOPRIMARIES) == 0) {
                        dns_zone_log(zone, ISC_LOG_ERROR,
@@ -11883,10 +11666,8 @@ zone_refresh(dns_zone_t *zone) {
                zone->retry = ISC_MIN(zone->retry * 2, 6 * 3600);
        }
 
-       zone->curprimary = 0;
-       for (j = 0; j < zone->primariescnt; j++) {
-               zone->primariesok[j] = false;
-       }
+       dns_remote_reset(&zone->primaries, true);
+
        /* initiate soa query */
        queue_soa_query(zone);
 }
@@ -13021,7 +12802,6 @@ zone_notify(dns_zone_t *zone, isc_time_t *now) {
        dns_rdataset_t nsrdset;
        dns_rdataset_t soardset;
        isc_result_t result;
-       unsigned int i;
        isc_sockaddr_t dst;
        bool isqueued;
        dns_notifytype_t notifytype;
@@ -13116,23 +12896,21 @@ zone_notify(dns_zone_t *zone, isc_time_t *now) {
         * Enqueue notify requests for 'also-notify' servers.
         */
        LOCK_ZONE(zone);
-       for (i = 0; i < zone->notifycnt; i++) {
+
+       dns_remote_reset(&zone->notify, false);
+       while (!dns_remote_done(&zone->notify)) {
                dns_tsigkey_t *key = NULL;
                dns_transport_t *transport = NULL;
                dns_notify_t *notify = NULL;
                dns_view_t *view = dns_zone_getview(zone);
 
-               if ((zone->notifykeynames != NULL) &&
-                   (zone->notifykeynames[i] != NULL))
-               {
-                       dns_name_t *keyname = zone->notifykeynames[i];
+               if (dns_remote_keyname(&zone->notify) != NULL) {
+                       dns_name_t *keyname = dns_remote_keyname(&zone->notify);
                        (void)dns_view_gettsig(view, keyname, &key);
                }
 
-               if ((zone->notifytlsnames != NULL) &&
-                   (zone->notifytlsnames[i] != NULL))
-               {
-                       dns_name_t *tlsname = zone->notifytlsnames[i];
+               if (dns_remote_tlsname(&zone->notify) != NULL) {
+                       dns_name_t *tlsname = dns_remote_tlsname(&zone->notify);
                        (void)dns_view_gettransport(view, DNS_TRANSPORT_TLS,
                                                    tlsname, &transport);
 
@@ -13143,7 +12921,7 @@ zone_notify(dns_zone_t *zone, isc_time_t *now) {
 
                /* TODO: glue the transport to the notify */
 
-               dst = zone->notify[i];
+               dst = dns_remote_curraddr(&zone->notify);
                if (notify_isqueued(zone, flags, NULL, &dst, key, transport)) {
                        if (key != NULL) {
                                dns_tsigkey_detach(&key);
@@ -13151,7 +12929,7 @@ zone_notify(dns_zone_t *zone, isc_time_t *now) {
                        if (transport != NULL) {
                                dns_transport_detach(&transport);
                        }
-                       continue;
+                       goto next;
                }
 
                result = notify_create(zone->mctx, flags, &notify);
@@ -13162,7 +12940,7 @@ zone_notify(dns_zone_t *zone, isc_time_t *now) {
                        if (transport != NULL) {
                                dns_transport_detach(&transport);
                        }
-                       continue;
+                       goto next;
                }
 
                zone_iattach(zone, &notify->zone);
@@ -13191,6 +12969,8 @@ zone_notify(dns_zone_t *zone, isc_time_t *now) {
                                   "sending notifies (serial %u)", serial);
                        loggednotify = true;
                }
+       next:
+               dns_remote_next(&zone->notify, false);
        }
        UNLOCK_ZONE(zone);
 
@@ -13392,6 +13172,7 @@ stub_glue_response_cb(isc_task_t *task, isc_event_t *event) {
        char source[ISC_SOCKADDR_FORMATSIZE];
        uint32_t addr_count, cnamecnt;
        isc_result_t result;
+       isc_sockaddr_t curraddr;
        isc_time_t now;
        struct stub_glue_request *request;
        struct stub_cb_args *cb_args;
@@ -13418,11 +13199,12 @@ stub_glue_response_cb(isc_task_t *task, isc_event_t *event) {
                goto cleanup;
        }
 
-       isc_sockaddr_format(&zone->primaryaddr, primary, sizeof(primary));
+       curraddr = dns_remote_curraddr(&zone->primaries);
+       isc_sockaddr_format(&curraddr, primary, sizeof(primary));
        isc_sockaddr_format(&zone->sourceaddr, source, sizeof(source));
 
        if (revent->result != ISC_R_SUCCESS) {
-               dns_zonemgr_unreachableadd(zone->zmgr, &zone->primaryaddr,
+               dns_zonemgr_unreachableadd(zone->zmgr, &curraddr,
                                           &zone->sourceaddr, &now);
                dns_zone_log(zone, ISC_LOG_INFO,
                             "could not refresh stub from primary %s"
@@ -13599,6 +13381,7 @@ stub_request_nameserver_address(struct stub_cb_args *args, bool ipv4,
        dns_zone_t *zone;
        isc_result_t result;
        struct stub_glue_request *request;
+       isc_sockaddr_t curraddr;
 
        zone = args->stub->zone;
        request = isc_mem_get(zone->mctx, sizeof(*request));
@@ -13623,10 +13406,11 @@ stub_request_nameserver_address(struct stub_cb_args *args, bool ipv4,
 
        atomic_fetch_add_release(&args->stub->pending_requests, 1);
 
+       curraddr = dns_remote_curraddr(&zone->primaries);
        result = dns_request_create(
-               zone->view->requestmgr, message, &zone->sourceaddr,
-               &zone->primaryaddr, NULL, NULL, args->dscp, DNS_REQUESTOPT_TCP,
-               args->tsig_key, args->timeout * 3, args->timeout, 2, zone->task,
+               zone->view->requestmgr, message, &zone->sourceaddr, &curraddr,
+               NULL, NULL, args->dscp, DNS_REQUESTOPT_TCP, args->tsig_key,
+               args->timeout * 3, args->timeout, 2, zone->task,
                stub_glue_response_cb, request, &request->request);
 
        if (result != ISC_R_SUCCESS) {
@@ -13814,9 +13598,9 @@ stub_callback(isc_task_t *task, isc_event_t *event) {
        char source[ISC_SOCKADDR_FORMATSIZE];
        uint32_t nscnt, cnamecnt;
        isc_result_t result;
+       isc_sockaddr_t curraddr;
        isc_time_t now;
        bool exiting = false;
-       unsigned int j;
        struct stub_cb_args *cb_args;
 
        cb_args = revent->ev_arg;
@@ -13837,7 +13621,8 @@ stub_callback(isc_task_t *task, isc_event_t *event) {
                goto exiting;
        }
 
-       isc_sockaddr_format(&zone->primaryaddr, primary, sizeof(primary));
+       curraddr = dns_remote_curraddr(&zone->primaries);
+       isc_sockaddr_format(&curraddr, primary, sizeof(primary));
        isc_sockaddr_format(&zone->sourceaddr, source, sizeof(source));
 
        switch (revent->result) {
@@ -13856,7 +13641,7 @@ stub_callback(isc_task_t *task, isc_event_t *event) {
                }
                FALLTHROUGH;
        default:
-               dns_zonemgr_unreachableadd(zone->zmgr, &zone->primaryaddr,
+               dns_zonemgr_unreachableadd(zone->zmgr, &curraddr,
                                           &zone->sourceaddr, &now);
                dns_zone_log(zone, ISC_LOG_INFO,
                             "could not refresh stub from primary "
@@ -14021,12 +13806,9 @@ next_primary:
        /*
         * Skip to next failed / untried primary.
         */
-       do {
-               zone->curprimary++;
-       } while (zone->curprimary < zone->primariescnt &&
-                zone->primariesok[zone->curprimary]);
+       dns_remote_next(&zone->primaries, true);
        DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NOEDNS);
-       if (exiting || zone->curprimary >= zone->primariescnt) {
+       if (exiting || dns_remote_done(&zone->primaries)) {
                bool done = true;
                if (!exiting &&
                    DNS_ZONE_OPTION(zone, DNS_ZONEOPT_USEALTXFRSRC) &&
@@ -14035,26 +13817,16 @@ next_primary:
                        /*
                         * Did we get a good answer from all the primaries?
                         */
-                       for (j = 0; j < zone->primariescnt; j++) {
-                               if (!zone->primariesok[j]) {
-                                       {
-                                               done = false;
-                                               break;
-                                       }
-                               }
-                       }
-               } else {
-                       done = true;
+                       done = dns_remote_allgood(&zone->primaries);
                }
                if (!done) {
-                       zone->curprimary = 0;
+                       dns_remote_reset(&zone->primaries, false);
+
                        /*
                         * Find the next failed primary.
                         */
-                       while (zone->curprimary < zone->primariescnt &&
-                              zone->primariesok[zone->curprimary])
-                       {
-                               zone->curprimary++;
+                       if (dns_remote_addrok(&zone->primaries)) {
+                               dns_remote_next(&zone->primaries, true);
                        }
                        DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_USEALTXFRSRC);
                } else {
@@ -14197,8 +13969,8 @@ refresh_callback(isc_task_t *task, isc_event_t *event) {
        dns_rdata_t rdata = DNS_RDATA_INIT;
        dns_rdata_soa_t soa;
        isc_result_t result;
+       isc_sockaddr_t curraddr;
        uint32_t serial, oldserial = 0;
-       unsigned int j;
        bool do_queue_xfrin = false;
 
        zone = revent->ev_arg;
@@ -14219,7 +13991,8 @@ refresh_callback(isc_task_t *task, isc_event_t *event) {
        /*
         * If timeout, log and try the next primary
         */
-       isc_sockaddr_format(&zone->primaryaddr, primary, sizeof(primary));
+       curraddr = dns_remote_curraddr(&zone->primaries);
+       isc_sockaddr_format(&curraddr, primary, sizeof(primary));
        isc_sockaddr_format(&zone->sourceaddr, source, sizeof(source));
 
        switch (revent->result) {
@@ -14247,7 +14020,7 @@ refresh_callback(isc_task_t *task, isc_event_t *event) {
                            DNS_ZONE_OPTION(zone, DNS_ZONEOPT_TRYTCPREFRESH))
                        {
                                if (!dns_zonemgr_unreachable(
-                                           zone->zmgr, &zone->primaryaddr,
+                                           zone->zmgr, &curraddr,
                                            &zone->sourceaddr, &now))
                                {
                                        DNS_ZONE_SETFLAG(
@@ -14484,7 +14257,7 @@ refresh_callback(isc_task_t *task, isc_event_t *event) {
            DNS_ZONE_FLAG(zone, DNS_ZONEFLG_FORCEXFER) ||
            isc_serial_gt(serial, oldserial))
        {
-               if (dns_zonemgr_unreachable(zone->zmgr, &zone->primaryaddr,
+               if (dns_zonemgr_unreachable(zone->zmgr, &curraddr,
                                            &zone->sourceaddr, &now))
                {
                        dns_zone_log(zone, ISC_LOG_INFO,
@@ -14535,7 +14308,7 @@ refresh_callback(isc_task_t *task, isc_event_t *event) {
                }
 
                DNS_ZONE_JITTER_ADD(&now, zone->refresh, &zone->refreshtime);
-               zone->primariesok[zone->curprimary] = true;
+               dns_remote_mark(&zone->primaries, true);
                goto next_primary;
        } else {
                if (!DNS_ZONE_OPTION(zone, DNS_ZONEOPT_MULTIMASTER)) {
@@ -14546,7 +14319,7 @@ refresh_callback(isc_task_t *task, isc_event_t *event) {
                } else {
                        zone_debuglog(zone, __func__, 1, "ahead");
                }
-               zone->primariesok[zone->curprimary] = true;
+               dns_remote_mark(&zone->primaries, true);
                goto next_primary;
        }
        if (msg != NULL) {
@@ -14563,12 +14336,9 @@ next_primary:
        /*
         * Skip to next failed / untried primary.
         */
-       do {
-               zone->curprimary++;
-       } while (zone->curprimary < zone->primariescnt &&
-                zone->primariesok[zone->curprimary]);
+       dns_remote_next(&zone->primaries, true);
        DNS_ZONE_CLRFLAG(zone, DNS_ZONEFLG_NOEDNS);
-       if (zone->curprimary >= zone->primariescnt) {
+       if (dns_remote_done(&zone->primaries)) {
                bool done = true;
                if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_USEALTXFRSRC) &&
                    !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_USEALTXFRSRC))
@@ -14576,27 +14346,16 @@ next_primary:
                        /*
                         * Did we get a good answer from all the primaries?
                         */
-                       for (j = 0; j < zone->primariescnt; j++) {
-                               if (!zone->primariesok[j]) {
-                                       {
-                                               done = false;
-                                               break;
-                                       }
-                               }
-                       }
-               } else {
-                       done = true;
+                       done = dns_remote_allgood(&zone->primaries);
                }
                if (!done) {
                        DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_USEALTXFRSRC);
-                       zone->curprimary = 0;
+                       dns_remote_reset(&zone->primaries, false);
                        /*
                         * Find the next failed primary.
                         */
-                       while (zone->curprimary < zone->primariescnt &&
-                              zone->primariesok[zone->curprimary])
-                       {
-                               zone->curprimary++;
+                       if (dns_remote_addrok(&zone->primaries)) {
+                               dns_remote_next(&zone->primaries, true);
                        }
                        goto requeue;
                }
@@ -14687,6 +14446,7 @@ soa_query(isc_task_t *task, isc_event_t *event) {
        bool have_xfrsource, have_xfrdscp, reqnsid, reqexpire;
        uint16_t udpsize = SEND_BUFFER_SIZE;
        isc_dscp_t dscp = -1;
+       isc_sockaddr_t curraddr;
        bool do_queue_xfrin = false;
 
        REQUIRE(DNS_ZONE_VALID(zone));
@@ -14707,21 +14467,23 @@ soa_query(isc_task_t *task, isc_event_t *event) {
        }
 
 again:
-       INSIST(zone->primariescnt > 0);
-       INSIST(zone->curprimary < zone->primariescnt);
+       dns_zone_log(
+               zone, ISC_LOG_DEBUG(3),
+               "soa_query: remote server current address index %d count %d",
+               zone->primaries.curraddr, zone->primaries.addrcnt);
+       INSIST(dns_remote_count(&zone->primaries) > 0);
+       INSIST(!dns_remote_done(&zone->primaries));
 
-       zone->primaryaddr = zone->primaries[zone->curprimary];
+       curraddr = dns_remote_curraddr(&zone->primaries);
+       isc_netaddr_fromsockaddr(&primaryip, &curraddr);
 
-       isc_netaddr_fromsockaddr(&primaryip, &zone->primaryaddr);
        /*
         * First, look for a tsig key in the primaries statement, then
         * try for a server key.
         */
-       if ((zone->primarykeynames != NULL) &&
-           (zone->primarykeynames[zone->curprimary] != NULL))
-       {
+       if (dns_remote_keyname(&zone->primaries) != NULL) {
                dns_view_t *view = dns_zone_getview(zone);
-               dns_name_t *keyname = zone->primarykeynames[zone->curprimary];
+               dns_name_t *keyname = dns_remote_keyname(&zone->primaries);
                result = dns_view_gettsig(view, keyname, &key);
                if (result != ISC_R_SUCCESS) {
                        char namebuf[DNS_NAME_FORMATSIZE];
@@ -14743,11 +14505,9 @@ again:
                }
        }
 
-       if ((zone->primarytlsnames != NULL) &&
-           (zone->primarytlsnames[zone->curprimary] != NULL))
-       {
+       if (dns_remote_tlsname(&zone->primaries) != NULL) {
                dns_view_t *view = dns_zone_getview(zone);
-               dns_name_t *tlsname = zone->primarytlsnames[zone->curprimary];
+               dns_name_t *tlsname = dns_remote_tlsname(&zone->primaries);
                result = dns_view_gettransport(view, DNS_TRANSPORT_TLS, tlsname,
                                               &transport);
                if (result != ISC_R_SUCCESS) {
@@ -14795,7 +14555,7 @@ again:
                }
        }
 
-       switch (isc_sockaddr_pf(&zone->primaryaddr)) {
+       switch (isc_sockaddr_pf(&curraddr)) {
        case PF_INET:
                if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_USEALTXFRSRC)) {
                        if (isc_sockaddr_equal(&zone->altxfrsource4,
@@ -14867,9 +14627,9 @@ again:
                timeout = 30;
        }
        result = dns_request_create(
-               zone->view->requestmgr, message, &zone->sourceaddr,
-               &zone->primaryaddr, NULL, NULL, dscp, options, key, timeout * 3,
-               timeout, 2, zone->task, refresh_callback, zone, &zone->request);
+               zone->view->requestmgr, message, &zone->sourceaddr, &curraddr,
+               NULL, NULL, dscp, options, key, timeout * 3, timeout, 2,
+               zone->task, refresh_callback, zone, &zone->request);
        if (result != ISC_R_SUCCESS) {
                zone_idetach(&dummy);
                zone_debuglog(zone, __func__, 1,
@@ -14877,7 +14637,7 @@ again:
                              isc_result_totext(result));
                goto skip_primary;
        } else {
-               if (isc_sockaddr_pf(&zone->primaryaddr) == PF_INET) {
+               if (isc_sockaddr_pf(&curraddr) == PF_INET) {
                        inc_stats(zone, dns_zonestatscounter_soaoutv4);
                } else {
                        inc_stats(zone, dns_zonestatscounter_soaoutv6);
@@ -14921,14 +14681,11 @@ skip_primary:
        /*
         * Skip to next failed / untried primary.
         */
-       do {
-               zone->curprimary++;
-       } while (zone->curprimary < zone->primariescnt &&
-                zone->primariesok[zone->curprimary]);
-       if (zone->curprimary < zone->primariescnt) {
+       dns_remote_next(&zone->primaries, true);
+       if (!dns_remote_done(&zone->primaries)) {
                goto again;
        }
-       zone->curprimary = 0;
+       dns_remote_reset(&zone->primaries, false);
        goto cleanup;
 }
 
@@ -14944,6 +14701,7 @@ ns_query(dns_zone_t *zone, dns_rdataset_t *soardataset, dns_stub_t *stub) {
        bool reqnsid;
        uint16_t udpsize = SEND_BUFFER_SIZE;
        isc_dscp_t dscp = -1;
+       isc_sockaddr_t curraddr;
        struct stub_cb_args *cb_args;
 
        REQUIRE(DNS_ZONE_VALID(zone));
@@ -15034,20 +14792,18 @@ ns_query(dns_zone_t *zone, dns_rdataset_t *soardataset, dns_stub_t *stub) {
         */
        create_query(zone, dns_rdatatype_ns, &zone->origin, &message);
 
-       INSIST(zone->primariescnt > 0);
-       INSIST(zone->curprimary < zone->primariescnt);
-       zone->primaryaddr = zone->primaries[zone->curprimary];
+       INSIST(dns_remote_count(&zone->primaries) > 0);
+       INSIST(!dns_remote_done(&zone->primaries));
 
-       isc_netaddr_fromsockaddr(&primaryip, &zone->primaryaddr);
+       curraddr = dns_remote_curraddr(&zone->primaries);
+       isc_netaddr_fromsockaddr(&primaryip, &curraddr);
        /*
         * First, look for a tsig key in the primaries statement, then
         * try for a server key.
         */
-       if ((zone->primarykeynames != NULL) &&
-           (zone->primarykeynames[zone->curprimary] != NULL))
-       {
+       if (dns_remote_keyname(&zone->primaries) != NULL) {
                dns_view_t *view = dns_zone_getview(zone);
-               dns_name_t *keyname = zone->primarykeynames[zone->curprimary];
+               dns_name_t *keyname = dns_remote_keyname(&zone->primaries);
                result = dns_view_gettsig(view, keyname, &key);
                if (result != ISC_R_SUCCESS) {
                        char namebuf[DNS_NAME_FORMATSIZE];
@@ -15099,7 +14855,7 @@ ns_query(dns_zone_t *zone, dns_rdataset_t *soardataset, dns_stub_t *stub) {
        /*
         * Always use TCP so that we shouldn't truncate in additional section.
         */
-       switch (isc_sockaddr_pf(&zone->primaryaddr)) {
+       switch (isc_sockaddr_pf(&curraddr)) {
        case PF_INET:
                if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_USEALTXFRSRC)) {
                        zone->sourceaddr = zone->altxfrsource4;
@@ -15148,11 +14904,10 @@ ns_query(dns_zone_t *zone, dns_rdataset_t *soardataset, dns_stub_t *stub) {
        cb_args->timeout = timeout;
        cb_args->reqnsid = reqnsid;
 
-       result = dns_request_create(zone->view->requestmgr, message,
-                                   &zone->sourceaddr, &zone->primaryaddr, NULL,
-                                   NULL, dscp, DNS_REQUESTOPT_TCP, key,
-                                   timeout * 3, timeout, 2, zone->task,
-                                   stub_callback, cb_args, &zone->request);
+       result = dns_request_create(
+               zone->view->requestmgr, message, &zone->sourceaddr, &curraddr,
+               NULL, NULL, dscp, DNS_REQUESTOPT_TCP, key, timeout * 3, timeout,
+               2, zone->task, stub_callback, cb_args, &zone->request);
        if (result != ISC_R_SUCCESS) {
                zone_debuglog(zone, __func__, 1,
                              "dns_request_create() failed: %s",
@@ -15362,7 +15117,7 @@ zone__settimer(void *arg) {
 
        switch (zone->type) {
        case dns_zone_redirect:
-               if (zone->primaries != NULL) {
+               if (dns_remote_addresses(&zone->primaries) != NULL) {
                        goto treat_as_secondary;
                }
                FALLTHROUGH;
@@ -15755,17 +15510,18 @@ dns_zone_notifyreceive(dns_zone_t *zone, isc_sockaddr_t *from,
        }
 
        isc_netaddr_fromsockaddr(&netaddr, from);
-       for (i = 0; i < zone->primariescnt; i++) {
-               if (isc_sockaddr_eqaddr(from, &zone->primaries[i])) {
+       for (i = 0; i < dns_remote_count(&zone->primaries); i++) {
+               isc_sockaddr_t sockaddr = dns_remote_addr(&zone->primaries, i);
+               if (isc_sockaddr_eqaddr(from, &sockaddr)) {
                        break;
                }
                if (zone->view->aclenv->match_mapped &&
                    IN6_IS_ADDR_V4MAPPED(&from->type.sin6.sin6_addr) &&
-                   isc_sockaddr_pf(&zone->primaries[i]) == AF_INET)
+                   isc_sockaddr_pf(&sockaddr) == AF_INET)
                {
                        isc_netaddr_t na1, na2;
                        isc_netaddr_fromv4mapped(&na1, &netaddr);
-                       isc_netaddr_fromsockaddr(&na2, &zone->primaries[i]);
+                       isc_netaddr_fromsockaddr(&na2, &sockaddr);
                        if (isc_netaddr_equal(&na1, &na2)) {
                                break;
                        }
@@ -15778,13 +15534,14 @@ dns_zone_notifyreceive(dns_zone_t *zone, isc_sockaddr_t *from,
         */
        tsigkey = dns_message_gettsigkey(msg);
        tsig = dns_tsigkey_identity(tsigkey);
-       if (i >= zone->primariescnt && zone->notify_acl != NULL &&
+       if (i >= dns_remote_count(&zone->primaries) &&
+           zone->notify_acl != NULL &&
            (dns_acl_match(&netaddr, tsig, zone->notify_acl, zone->view->aclenv,
                           &match, NULL) == ISC_R_SUCCESS) &&
            match > 0)
        {
                /* Accept notify. */
-       } else if (i >= zone->primariescnt) {
+       } else if (i >= dns_remote_count(&zone->primaries)) {
                UNLOCK_ZONE(zone);
                dns_zone_log(zone, ISC_LOG_INFO,
                             "refused notify from non-primary: %s", fromtext);
@@ -16405,8 +16162,9 @@ dns_zone_getredirecttype(dns_zone_t *zone) {
        REQUIRE(DNS_ZONE_VALID(zone));
        REQUIRE(zone->type == dns_zone_redirect);
 
-       return (zone->primaries == NULL ? dns_zone_primary
-                                       : dns_zone_secondary);
+       return (dns_remote_addresses(&zone->primaries) == NULL
+                       ? dns_zone_primary
+                       : dns_zone_secondary);
 }
 
 dns_name_t *
@@ -17583,7 +17341,7 @@ zone_replacedb(dns_zone_t *zone, dns_db_t *db, bool dump) {
                RUNTIME_CHECK(soacount > 0U);
                if ((zone->type == dns_zone_secondary ||
                     (zone->type == dns_zone_redirect &&
-                     zone->primaries != NULL)) &&
+                     dns_remote_addresses(&zone->primaries) != NULL)) &&
                    !isc_serial_gt(serial, oldserial))
                {
                        uint32_t serialmin, serialmax;
@@ -17905,23 +17663,18 @@ again:
                /*
                 * Skip to next failed / untried primary.
                 */
-               do {
-                       zone->curprimary++;
-               } while (zone->curprimary < zone->primariescnt &&
-                        zone->primariesok[zone->curprimary]);
+               dns_remote_next(&zone->primaries, true);
        same_primary:
-               if (zone->curprimary >= zone->primariescnt) {
-                       zone->curprimary = 0;
+               if (dns_remote_done(&zone->primaries)) {
+                       dns_remote_reset(&zone->primaries, false);
                        if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_USEALTXFRSRC) &&
                            !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_USEALTXFRSRC))
                        {
                                DNS_ZONE_SETFLAG(zone, DNS_ZONEFLG_REFRESH);
                                DNS_ZONE_SETFLAG(zone,
                                                 DNS_ZONEFLG_USEALTXFRSRC);
-                               while (zone->curprimary < zone->primariescnt &&
-                                      zone->primariesok[zone->curprimary])
-                               {
-                                       zone->curprimary++;
+                               if (dns_remote_addrok(&zone->primaries)) {
+                                       dns_remote_next(&zone->primaries, true);
                                }
                                again = true;
                        } else {
@@ -18195,8 +17948,8 @@ got_transfer_quota(isc_task_t *task, isc_event_t *event) {
        dns_rdatatype_t xfrtype;
        dns_zone_t *zone = event->ev_arg;
        isc_netaddr_t primaryip;
-       isc_sockaddr_t sourceaddr;
        isc_sockaddr_t primaryaddr;
+       isc_sockaddr_t sourceaddr;
        isc_time_t now;
        const char *soa_before = "";
        isc_dscp_t dscp = -1;
@@ -18212,9 +17965,10 @@ got_transfer_quota(isc_task_t *task, isc_event_t *event) {
 
        TIME_NOW(&now);
 
-       isc_sockaddr_format(&zone->primaryaddr, primary, sizeof(primary));
-       if (dns_zonemgr_unreachable(zone->zmgr, &zone->primaryaddr,
-                                   &zone->sourceaddr, &now))
+       primaryaddr = dns_remote_curraddr(&zone->primaries);
+       isc_sockaddr_format(&primaryaddr, primary, sizeof(primary));
+       if (dns_zonemgr_unreachable(zone->zmgr, &primaryaddr, &zone->sourceaddr,
+                                   &now))
        {
                isc_sockaddr_format(&zone->sourceaddr, source, sizeof(source));
                dns_zone_logc(zone, DNS_LOGCATEGORY_XFER_IN, ISC_LOG_INFO,
@@ -18224,7 +17978,7 @@ got_transfer_quota(isc_task_t *task, isc_event_t *event) {
                CHECK(ISC_R_CANCELED);
        }
 
-       isc_netaddr_fromsockaddr(&primaryip, &zone->primaryaddr);
+       isc_netaddr_fromsockaddr(&primaryip, &primaryaddr);
        (void)dns_peerlist_peerbyaddr(zone->view->peers, &primaryip, &peer);
 
        if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_SOABEFOREAXFR)) {
@@ -18294,11 +18048,9 @@ got_transfer_quota(isc_task_t *task, isc_event_t *event) {
         * First, look for a tsig key in the primaries statement, then
         * try for a server key.
         */
-       if ((zone->primarykeynames != NULL) &&
-           (zone->primarykeynames[zone->curprimary] != NULL))
-       {
+       if (dns_remote_keyname(&zone->primaries) != NULL) {
                dns_view_t *view = dns_zone_getview(zone);
-               dns_name_t *keyname = zone->primarykeynames[zone->curprimary];
+               dns_name_t *keyname = dns_remote_keyname(&zone->primaries);
                result = dns_view_gettsig(view, keyname, &zone->tsigkey);
        }
        if (zone->tsigkey == NULL) {
@@ -18312,11 +18064,9 @@ got_transfer_quota(isc_task_t *task, isc_event_t *event) {
                              isc_result_totext(result));
        }
 
-       if ((zone->primarytlsnames != NULL) &&
-           (zone->primarytlsnames[zone->curprimary] != NULL))
-       {
+       if (dns_remote_tlsname(&zone->primaries) != NULL) {
                dns_view_t *view = dns_zone_getview(zone);
-               dns_name_t *tlsname = zone->primarytlsnames[zone->curprimary];
+               dns_name_t *tlsname = dns_remote_tlsname(&zone->primaries);
                result = dns_view_gettransport(view, DNS_TRANSPORT_TLS, tlsname,
                                               &zone->transport);
 
@@ -18332,12 +18082,9 @@ got_transfer_quota(isc_task_t *task, isc_event_t *event) {
                        isc_result_totext(result));
        }
 
-       if (zone->primarydscps != NULL) {
-               dscp = zone->primarydscps[zone->curprimary];
-       }
+       dscp = dns_remote_dscp(&zone->primaries);
 
        LOCK_ZONE(zone);
-       primaryaddr = zone->primaryaddr;
        sourceaddr = zone->sourceaddr;
        switch (isc_sockaddr_pf(&primaryaddr)) {
        case PF_INET:
@@ -18435,12 +18182,12 @@ sendtoprimary(dns_forward_t *forward) {
                return (ISC_R_CANCELED);
        }
 
-       if (forward->which >= zone->primariescnt) {
+       if (forward->which >= dns_remote_count(&forward->zone->primaries)) {
                UNLOCK_ZONE(zone);
                return (ISC_R_NOMORE);
        }
 
-       forward->addr = zone->primaries[forward->which];
+       forward->addr = dns_remote_addr(&zone->primaries, forward->which);
        /*
         * Always use TCP regardless of whether the original update
         * used TCP.
@@ -18465,11 +18212,11 @@ sendtoprimary(dns_forward_t *forward) {
                dns_transport_detach(&forward->transport);
        }
 
-       if (zone->primarytlsnames != NULL &&
-           zone->primarytlsnames[forward->which] != NULL)
+       if (dns_remote_tlsname(&zone->primaries) != NULL &&
+           zone->primaries.tlsnames[forward->which] != NULL)
        {
                dns_view_t *view = dns_zone_getview(zone);
-               dns_name_t *tlsname = zone->primarytlsnames[forward->which];
+               dns_name_t *tlsname = zone->primaries.tlsnames[forward->which];
 
                result = dns_view_gettransport(view, DNS_TRANSPORT_TLS, tlsname,
                                               &forward->transport);
@@ -19288,6 +19035,7 @@ static isc_result_t
 zmgr_start_xfrin_ifquota(dns_zonemgr_t *zmgr, dns_zone_t *zone) {
        dns_peer_t *peer = NULL;
        isc_netaddr_t primaryip;
+       isc_sockaddr_t curraddr;
        uint32_t nxfrsin, nxfrsperns;
        dns_zone_t *x;
        uint32_t maxtransfersin, maxtransfersperns;
@@ -19307,7 +19055,8 @@ zmgr_start_xfrin_ifquota(dns_zonemgr_t *zmgr, dns_zone_t *zone) {
         * Find any configured information about the server we'd
         * like to transfer this zone from.
         */
-       isc_netaddr_fromsockaddr(&primaryip, &zone->primaryaddr);
+       curraddr = dns_remote_curraddr(&zone->primaries);
+       isc_netaddr_fromsockaddr(&primaryip, &curraddr);
        (void)dns_peerlist_peerbyaddr(zone->view->peers, &primaryip, &peer);
        UNLOCK_ZONE(zone);
 
@@ -19333,9 +19082,11 @@ zmgr_start_xfrin_ifquota(dns_zonemgr_t *zmgr, dns_zone_t *zone) {
             x = ISC_LIST_NEXT(x, statelink))
        {
                isc_netaddr_t xip;
+               isc_sockaddr_t xaddr;
 
                LOCK_ZONE(x);
-               isc_netaddr_fromsockaddr(&xip, &x->primaryaddr);
+               xaddr = dns_remote_curraddr(&x->primaries);
+               isc_netaddr_fromsockaddr(&xip, &xaddr);
                UNLOCK_ZONE(x);
 
                nxfrsin++;
@@ -19730,7 +19481,8 @@ dns_zone_forcereload(dns_zone_t *zone) {
        REQUIRE(DNS_ZONE_VALID(zone));
 
        if (zone->type == dns_zone_primary ||
-           (zone->type == dns_zone_redirect && zone->primaries == NULL))
+           (zone->type == dns_zone_redirect &&
+            dns_remote_addresses(&zone->primaries) == NULL))
        {
                return;
        }
@@ -19867,7 +19619,8 @@ dns_zone_dialup(dns_zone_t *zone) {
        if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DIALNOTIFY)) {
                dns_zone_notify(zone);
        }
-       if (zone->type != dns_zone_primary && zone->primaries != NULL &&
+       if (zone->type != dns_zone_primary &&
+           dns_remote_addresses(&zone->primaries) != NULL &&
            DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DIALREFRESH))
        {
                dns_zone_refresh(zone);
@@ -20717,7 +20470,7 @@ do_checkds(dns_zone_t *zone, dst_key_t *key, isc_stdtime_t now,
                             "for key %u",
                             count, dst_key_id(key));
 
-               if (count != zone->parentalscnt) {
+               if (count != dns_remote_count(&zone->parentals)) {
                        return false;
                }
        } else {
@@ -20729,7 +20482,7 @@ do_checkds(dns_zone_t *zone, dst_key_t *key, isc_stdtime_t now,
                             "for key %u",
                             count, dst_key_id(key));
 
-               if (count != zone->parentalscnt) {
+               if (count != dns_remote_count(&zone->parentals)) {
                        return false;
                }
        }
@@ -21255,6 +21008,7 @@ checkds_send(dns_zone_t *zone) {
        dns_view_t *view = dns_zone_getview(zone);
        isc_result_t result;
        unsigned int flags = 0;
+       unsigned int i = 0;
 
        /*
         * Zone lock held by caller.
@@ -21263,7 +21017,7 @@ checkds_send(dns_zone_t *zone) {
 
        dns_zone_log(zone, ISC_LOG_DEBUG(3),
                     "checkds: start sending DS queries to %u parentals",
-                    zone->parentalscnt);
+                    dns_remote_count(&zone->parentals));
 
        if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_EXITING)) {
                dns_zone_log(zone, ISC_LOG_DEBUG(3),
@@ -21271,23 +21025,24 @@ checkds_send(dns_zone_t *zone) {
                return;
        }
 
-       for (unsigned int i = 0; i < zone->parentalscnt; i++) {
+       dns_remote_reset(&zone->parentals, false);
+       while (!dns_remote_done(&zone->parentals)) {
                dns_tsigkey_t *key = NULL;
                dns_transport_t *transport = NULL;
                isc_sockaddr_t dst;
                dns_checkds_t *checkds = NULL;
 
-               if ((zone->parentalkeynames != NULL) &&
-                   (zone->parentalkeynames[i] != NULL))
-               {
-                       dns_name_t *keyname = zone->parentalkeynames[i];
+               i++;
+
+               if (dns_remote_keyname(&zone->parentals) != NULL) {
+                       dns_name_t *keyname =
+                               dns_remote_keyname(&zone->parentals);
                        (void)dns_view_gettsig(view, keyname, &key);
                }
 
-               if ((zone->parentaltlsnames != NULL) &&
-                   (zone->parentaltlsnames[i] != NULL))
-               {
-                       dns_name_t *tlsname = zone->parentaltlsnames[i];
+               if (dns_remote_tlsname(&zone->parentals) != NULL) {
+                       dns_name_t *tlsname =
+                               dns_remote_tlsname(&zone->parentals);
                        (void)dns_view_gettransport(view, DNS_TRANSPORT_TLS,
                                                    tlsname, &transport);
                        dns_zone_logc(
@@ -21295,7 +21050,7 @@ checkds_send(dns_zone_t *zone) {
                                "got TLS configuration for zone transfer");
                }
 
-               dst = zone->parentals[i];
+               dst = dns_remote_curraddr(&zone->parentals);
 
                /* TODO: glue the transport to the checkds request */
 
@@ -21310,7 +21065,7 @@ checkds_send(dns_zone_t *zone) {
                        if (transport != NULL) {
                                dns_transport_detach(&transport);
                        }
-                       continue;
+                       goto next;
                }
 
                dns_zone_log(zone, ISC_LOG_DEBUG(3),
@@ -21324,7 +21079,7 @@ checkds_send(dns_zone_t *zone) {
                                     "checkds: create DS query for "
                                     "parent %d failed",
                                     i);
-                       continue;
+                       goto next;
                }
                zone_iattach(zone, &checkds->zone);
                checkds->dst = dst;
@@ -21350,6 +21105,9 @@ checkds_send(dns_zone_t *zone) {
                                     i);
                        checkds_destroy(checkds, true);
                }
+
+       next:
+               dns_remote_next(&zone->parentals, false);
        }
 }