]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
1412. [func] You can now specify servers to be tried if a nameserver
authorMark Andrews <marka@isc.org>
Thu, 16 Jan 2003 03:59:28 +0000 (03:59 +0000)
committerMark Andrews <marka@isc.org>
Thu, 16 Jan 2003 03:59:28 +0000 (03:59 +0000)
                       has IPv6 address and you only support IPv4 or the
                       reverse. See dual-stack-servers.

15 files changed:
CHANGES
bin/named/main.c
bin/named/named.docbook
bin/named/server.c
doc/arm/Bv9ARM-book.xml
lib/bind9/check.c
lib/dns/include/dns/resolver.h
lib/dns/resolver.c
lib/isc/include/isc/result.h
lib/isc/result.c
lib/isc/unix/include/isc/net.h
lib/isc/unix/net.c
lib/isc/win32/include/isc/net.h
lib/isc/win32/net.c
lib/isccfg/namedconf.c

diff --git a/CHANGES b/CHANGES
index fa223fcce4b250bc3b0c4d44546bba7d15c433df..005667c306828c8da9890695a3d781256ead0ff8 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,7 @@
+1412.  [func]          You can now specify servers to be tried if a nameserver
+                       has IPv6 address and you only support IPv4 or the
+                       reverse. See dual-stack-servers.
+                       
 1411.  [bug]           empty nodes should stop wildcard matches. [RT #4802]
 
 1410.  [func]          handle records that live in the parent zone, e.g. DS.
index 8239bbc3ffaf04513c83eaac92c2437726512d48..ac7bae91e856f78305e67af949aa475062596201 100644 (file)
@@ -15,7 +15,7 @@
  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: main.c,v 1.128 2002/05/03 05:28:19 marka Exp $ */
+/* $Id: main.c,v 1.129 2003/01/16 03:59:23 marka Exp $ */
 
 #include <config.h>
 
@@ -329,14 +329,31 @@ static void
 parse_command_line(int argc, char *argv[]) {
        int ch;
        int port;
+       isc_boolean_t disable6 = ISC_FALSE;
+       isc_boolean_t disable4 = ISC_FALSE;
 
        save_command_line(argc, argv);
 
        isc_commandline_errprint = ISC_FALSE;
        while ((ch = isc_commandline_parse(argc, argv,
-                                          "c:C:d:fgi:lm:n:N:p:P:st:u:vx:")) !=
-              -1) {
+                                  "46c:C:d:fgi:lm:n:N:p:P:st:u:vx:")) != -1) {
                switch (ch) {
+               case '4':
+                       if (disable4)
+                               ns_main_earlyfatal("cannot specify -4 and -6");
+                       if (isc_net_probeipv4() != ISC_R_SUCCESS)
+                               ns_main_earlyfatal("IPv4 not supported by OS");
+                       isc_net_disableipv6();
+                       disable6 = ISC_TRUE;
+                       break;
+               case '6':
+                       if (disable6)
+                               ns_main_earlyfatal("cannot specify -4 and -6");
+                       if (isc_net_probeipv6() != ISC_R_SUCCESS)
+                               ns_main_earlyfatal("IPv6 not supported by OS");
+                       isc_net_disableipv4();
+                       disable4 = ISC_TRUE;
+                       break;
                case 'c':
                        ns_g_conffile = isc_commandline_argument;
                        lwresd_g_conffile = isc_commandline_argument;
index fc0099f642429bb7dadec4200e5a713e4c7f7d4a..5483691787d91ed70964e90a8b3a4709a72d74e0 100644 (file)
@@ -16,7 +16,7 @@
  - WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 -->
 
-<!-- $Id: named.docbook,v 1.5 2001/06/10 13:57:51 tale Exp $ -->
+<!-- $Id: named.docbook,v 1.6 2003/01/16 03:59:23 marka Exp $ -->
 
 <refentry>
   <refentryinfo>
@@ -37,6 +37,8 @@
   <refsynopsisdiv>
     <cmdsynopsis>
       <command>named</command>
+      <arg><option>-4</option></arg>
+      <arg><option>-6</option></arg>
       <arg><option>-c <replaceable class="parameter">config-file</replaceable></option></arg>
       <arg><option>-d <replaceable class="parameter">debug-level</replaceable></option></arg>
       <arg><option>-f</option></arg>
     <title>OPTIONS</title>
 
     <variablelist>
+      <varlistentry>
+       <term>-4</term>
+       <listitem>
+         <para>
+               Use IPv4 only even if the host machine is capable of IPv6.
+               <option>-4</option> and <option>-6</option> are mutually
+               exclusive.
+          </para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-6</term>
+       <listitem>
+         <para>
+               Use IPv6 only even if the host machine is capable of IPv4.
+               <option>-4</option> and <option>-6</option> are mutually
+               exclusive.
+          </para>
+       </listitem>
+      </varlistentry>
       <varlistentry>
        <term>-c <replaceable class="parameter">config-file</replaceable></term>
        <listitem>
index dc13eaef4ebb39c55e377472970ef51a26f552d5..291ee84bd05860cb4d037cd2a1918129fe45532d 100644 (file)
@@ -15,7 +15,7 @@
  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: server.c,v 1.391 2002/11/27 09:52:45 marka Exp $ */
+/* $Id: server.c,v 1.392 2003/01/16 03:59:23 marka Exp $ */
 
 #include <config.h>
 
@@ -137,6 +137,10 @@ static isc_result_t
 configure_forward(cfg_obj_t *config, dns_view_t *view, dns_name_t *origin,
                  cfg_obj_t *forwarders, cfg_obj_t *forwardtype);
 
+static isc_result_t
+configure_alternates(cfg_obj_t *config, dns_view_t *view,
+                    cfg_obj_t *alternates);
+
 static isc_result_t
 configure_zone(cfg_obj_t *config, cfg_obj_t *zconfig, cfg_obj_t *vconfig,
               isc_mem_t *mctx, dns_view_t *view,
@@ -558,6 +562,7 @@ configure_view(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
        cfg_obj_t *voptions = NULL;
        cfg_obj_t *forwardtype;
        cfg_obj_t *forwarders;
+       cfg_obj_t *alternates;
        cfg_obj_t *zonelist;
        cfg_obj_t *obj;
        cfg_listelt_t *element;
@@ -748,6 +753,14 @@ configure_view(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
                CHECK(configure_forward(config, view, dns_rootname, 
                                        forwarders, forwardtype));
 
+       /*
+        * Dual Stack Servers.
+        */
+       alternates = NULL;
+       (void)ns_config_get(maps, "dual-stack-servers", &alternates);
+       if (alternates != NULL)
+               CHECK(configure_alternates(config, view, alternates));
+
        /*
         * We have default hints for class IN if we need them.
         */
@@ -987,6 +1000,91 @@ configure_hints(dns_view_t *view, const char *filename) {
        return (result);
 }
 
+static isc_result_t
+configure_alternates(cfg_obj_t *config, dns_view_t *view,
+                    cfg_obj_t *alternates)
+{
+       cfg_obj_t *portobj;
+       cfg_obj_t *addresses;
+       cfg_listelt_t *element;
+       isc_result_t result = ISC_R_SUCCESS;
+       in_port_t port;
+
+       /*
+        * Determine which port to send requests to.
+        */
+       if (ns_g_lwresdonly && ns_g_port != 0)
+               port = ns_g_port;
+       else
+               CHECKM(ns_config_getport(config, &port), "port");
+
+       if (alternates != NULL) {
+               portobj = cfg_tuple_get(alternates, "port");
+               if (cfg_obj_isuint32(portobj)) {
+                       isc_uint32_t val = cfg_obj_asuint32(portobj);
+                       if (val > ISC_UINT16_MAX) {
+                               cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR,
+                                           "port '%u' out of range", val);
+                               return (ISC_R_RANGE);
+                       }
+                       port = (in_port_t) val;
+               }
+       }
+
+       addresses = NULL;
+       if (alternates != NULL)
+               addresses = cfg_tuple_get(alternates, "addresses");
+
+       for (element = cfg_list_first(addresses);
+            element != NULL;
+            element = cfg_list_next(element))
+       {
+               cfg_obj_t *alternate = cfg_listelt_value(element);
+               isc_sockaddr_t sa;
+
+               if (!cfg_obj_issockaddr(alternate)) {
+                       dns_fixedname_t fixed;
+                       dns_name_t *name;
+                       char *str = cfg_obj_asstring(cfg_tuple_get(alternate,
+                                                                  "name"));
+                       isc_buffer_t buffer;
+                       in_port_t myport = port;
+
+                       isc_buffer_init(&buffer, str, strlen(str));
+                       isc_buffer_add(&buffer, strlen(str));
+                       dns_fixedname_init(&fixed);
+                       name = dns_fixedname_name(&fixed);
+                       CHECK(dns_name_fromtext(name, &buffer, dns_rootname,
+                                               ISC_FALSE, NULL));
+
+                       portobj = cfg_tuple_get(alternates, "port");
+                       if (cfg_obj_isuint32(portobj)) {
+                               isc_uint32_t val = cfg_obj_asuint32(portobj);
+                               if (val > ISC_UINT16_MAX) {
+                                       cfg_obj_log(portobj, ns_g_lctx,
+                                                   ISC_LOG_ERROR,
+                                                   "port '%u' out of range",
+                                                    val);
+                                       return (ISC_R_RANGE);
+                               }
+                               myport = (in_port_t) val;
+                       }
+                       CHECK(dns_resolver_addalternate(view->resolver, NULL,
+                                                       name, myport));
+                       continue;
+               }
+
+               sa = *cfg_obj_assockaddr(alternate);
+               if (isc_sockaddr_getport(&sa) == 0)
+                       isc_sockaddr_setport(&sa, port);
+               CHECK(dns_resolver_addalternate(view->resolver, &sa,
+                                               NULL, 0));
+       }
+
+ cleanup:
+       return (result);
+}
+
 static isc_result_t
 configure_forward(cfg_obj_t *config, dns_view_t *view, dns_name_t *origin,
                  cfg_obj_t *forwarders, cfg_obj_t *forwardtype)
index 48d547033c3908c8ddddb5010f0480a387331deb..c2b07fbdadcecaac4d1aea43f17a693722a9d2a9 100644 (file)
@@ -2,7 +2,7 @@
 <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.0//EN"
                "http://www.oasis-open.org/docbook/xml/4.0/docbookx.dtd">
 
-<!-- File: $Id: Bv9ARM-book.xml,v 1.211 2003/01/13 04:24:27 marka Exp $ -->
+<!-- File: $Id: Bv9ARM-book.xml,v 1.212 2003/01/16 03:59:24 marka Exp $ -->
 
 <book>
 <title>BIND 9 Administrator Reference Manual</title>
@@ -2717,6 +2717,7 @@ statement in the <filename>named.conf</filename> file:</para>
     <optional> maintain-ixfr-base <replaceable>yes_or_no</replaceable>; </optional>
     <optional> forward ( <replaceable>only</replaceable> | <replaceable>first</replaceable> ); </optional>
     <optional> forwarders { <replaceable>ip_addr</replaceable> <optional>port <replaceable>ip_port</replaceable></optional> ; <optional> <replaceable>ip_addr</replaceable> <optional>port <replaceable>ip_port</replaceable></optional> ; ... </optional> }; </optional>
+    <optional> dual-stack-servers <optional>port <replaceable>ip_port</replaceable></optional> { ( <replaceable>domain_name</replaceable> <optional>port <replaceable>ip_port</replaceable></optional> | <replaceable>ip_addr</replaceable> <optional>port <replaceable>ip_port</replaceable></optional> ) ; ... }; </optional>
     <optional> check-names ( <replaceable>master</replaceable> | <replaceable>slave</replaceable> | <replaceable> response</replaceable> )( <replaceable>warn</replaceable> | <replaceable>fail</replaceable> | <replaceable>ignore</replaceable> ); </optional>
     <optional> allow-notify { <replaceable>address_match_list</replaceable> }; </optional>
     <optional> allow-query { <replaceable>address_match_list</replaceable> }; </optional>
@@ -3303,6 +3304,23 @@ or have a different <command>forward only/first</command> behavior,
 or not forward at all, see <xref linkend="zone_statement_grammar"/>.</para>
 </sect3>
 
+<sect3><title>6 to 4 Servers</title>
+<para>6 to 4 servers are used as servers of last resort to work around
+problems in reachability due the lack of support for either IPv4 or IPv6
+on the host machine.</para>
+
+<variablelist>
+<varlistentry><term><command>dual-stack-servers</command></term>
+<listitem><para>Specifies host names / addresses of machines with access to
+both IPv4 and IPv6 transports. If a hostname is used the server must be able
+to resolve the name using only the transport it has.  If the machine is dual
+stacked then the <command>dual-stack-servers</command> have no effect unless
+access to a transport has been disabled on the command line
+(e.g. <command>named -4</command>).</para></listitem>
+</varlistentry>
+</variablelist>
+</sect3>
+
 <sect3 id="access_control"><title>Access Control</title>
 
 <para>Access to the server can be restricted based on the IP address
index 2fa2c78c4208536464249610a801701cc5b575d0..ee1310749904809bb6dbfac0df02810516280670 100644 (file)
@@ -15,7 +15,7 @@
  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: check.c,v 1.33 2002/04/26 00:40:28 marka Exp $ */
+/* $Id: check.c,v 1.34 2003/01/16 03:59:24 marka Exp $ */
 
 #include <config.h>
 
@@ -143,6 +143,66 @@ check_order(cfg_obj_t *options, isc_log_t *logctx) {
        return (result);
 }
 
+static isc_result_t
+check_dual_stack(cfg_obj_t *options, isc_log_t *logctx) {
+       cfg_listelt_t *element;
+       cfg_obj_t *alternates = NULL;
+       cfg_obj_t *value;
+       cfg_obj_t *obj;
+       char *str;
+       dns_fixedname_t fixed;
+       dns_name_t *name;
+       isc_buffer_t buffer;
+       isc_result_t result = ISC_R_SUCCESS;
+       isc_result_t tresult;
+
+       (void)cfg_map_get(options, "dual-stack-servers", &alternates);
+
+       if (alternates == NULL)
+               return (ISC_R_SUCCESS);
+
+       obj = cfg_tuple_get(alternates, "port");
+       if (cfg_obj_isuint32(obj)) {
+               isc_uint32_t val = cfg_obj_asuint32(obj);
+               if (val > ISC_UINT16_MAX) {
+                       cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
+                                   "port '%u' out of range", val);
+                       result = ISC_R_FAILURE;
+               }
+       }
+       obj = cfg_tuple_get(alternates, "addresses");
+       for (element = cfg_list_first(obj);
+            element != NULL;
+            element = cfg_list_next(element)) {
+               value = cfg_listelt_value(element);
+               if (cfg_obj_issockaddr(value))
+                       continue;
+               obj = cfg_tuple_get(value, "name");
+               str = cfg_obj_asstring(obj);
+               isc_buffer_init(&buffer, str, strlen(str));
+               isc_buffer_add(&buffer, strlen(str));
+               dns_fixedname_init(&fixed);
+               name = dns_fixedname_name(&fixed);
+               tresult = dns_name_fromtext(name, &buffer, dns_rootname,
+                                          ISC_FALSE, NULL);
+               if (tresult != ISC_R_SUCCESS) {
+                       cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
+                                   "bad name '%s'", str);
+                       result = ISC_R_FAILURE;
+               }
+               obj = cfg_tuple_get(value, "port");
+               if (cfg_obj_isuint32(obj)) {
+                       isc_uint32_t val = cfg_obj_asuint32(obj);
+                       if (val > ISC_UINT16_MAX) {
+                               cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
+                                           "port '%u' out of range", val);
+                               result = ISC_R_FAILURE;
+                       }
+               }
+       }
+       return (result);
+}
+
 static isc_result_t
 check_forward(cfg_obj_t *options, isc_log_t *logctx) {
        cfg_obj_t *forward = NULL;
@@ -697,6 +757,19 @@ check_viewconf(cfg_obj_t *config, cfg_obj_t *vconfig, dns_rdataclass_t vclass,
                if (check_forward(vconfig, logctx) != ISC_R_SUCCESS)
                        result = ISC_R_FAILURE;
        }
+       /*
+        * Check that dual-stack-servers is reasonable.
+        */
+       if (vconfig == NULL) {
+               cfg_obj_t *options = NULL;
+               (void)cfg_map_get(config, "options", &options);
+               if (options != NULL)
+                       if (check_dual_stack(options, logctx) != ISC_R_SUCCESS)
+                               result = ISC_R_FAILURE;
+       } else {
+               if (check_dual_stack(vconfig, logctx) != ISC_R_SUCCESS)
+                       result = ISC_R_FAILURE;
+       }
 
        /*
         * Check that rrset-order is reasonable.
@@ -755,6 +828,10 @@ bind9_check_namedconf(cfg_obj_t *config, isc_log_t *logctx, isc_mem_t *mctx) {
 
        (void)cfg_map_get(config, "view", &views);
 
+       if (views != NULL && options != NULL)
+               if (check_dual_stack(options, logctx) != ISC_R_SUCCESS)
+                       result = ISC_R_FAILURE;
+
        if (views == NULL) {
                if (check_viewconf(config, NULL, dns_rdataclass_in,
                                   logctx, mctx) != ISC_R_SUCCESS)
index 13483bcc7c854d64fb5ab805288a12239b101539..076aea9dc18945426d87a6118fe285f9ce93c60d 100644 (file)
@@ -15,7 +15,7 @@
  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: resolver.h,v 1.35 2001/10/29 19:02:46 gson Exp $ */
+/* $Id: resolver.h,v 1.36 2003/01/16 03:59:25 marka Exp $ */
 
 #ifndef DNS_RESOLVER_H
 #define DNS_RESOLVER_H 1
@@ -359,6 +359,18 @@ dns_resolver_nrunning(dns_resolver_t *resolver);
  * can continue even though a fetch has been canceled.
  */
 
+isc_result_t
+dns_resolver_addalternate(dns_resolver_t *resolver, isc_sockaddr_t *alt,
+                         dns_name_t *name, in_port_t port);
+/*
+ * Add alternate addresses to be tried in the event that the nameservers
+ * for a zone are not available in the address families supported by the
+ * operating system.
+ *
+ * Require:
+ *     only one of 'name' or 'alt' to be valid.
+ */
+
 ISC_LANG_ENDDECLS
 
 #endif /* DNS_RESOLVER_H */
index 21b8cba99cd5190a53271d42cbe8e647041856ac..77c725f378c32f76c5f04aa04e4af7b1615ad63a 100644 (file)
@@ -15,7 +15,7 @@
  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: resolver.c,v 1.258 2003/01/15 05:05:13 marka Exp $ */
+/* $Id: resolver.c,v 1.259 2003/01/16 03:59:25 marka Exp $ */
 
 #include <config.h>
 
@@ -170,7 +170,10 @@ struct fetchctx {
        ISC_LIST(resquery_t)            queries;
        dns_adbfindlist_t               finds;
        dns_adbfind_t *                 find;
+       dns_adbfindlist_t               altfinds;
+       dns_adbfind_t *                 altfind;
        dns_adbaddrinfolist_t           forwaddrs;
+       dns_adbaddrinfolist_t           altaddrs;
        isc_sockaddrlist_t              forwarders;
        dns_fwdpolicy_t                 fwdpolicy;
        isc_sockaddrlist_t              bad;
@@ -210,14 +213,15 @@ struct fetchctx {
 #define FCTX_MAGIC                     ISC_MAGIC('F', '!', '!', '!')
 #define VALID_FCTX(fctx)               ISC_MAGIC_VALID(fctx, FCTX_MAGIC)
 
-#define FCTX_ATTR_HAVEANSWER           0x01
-#define FCTX_ATTR_GLUING               0x02
-#define FCTX_ATTR_ADDRWAIT             0x04
-#define FCTX_ATTR_SHUTTINGDOWN         0x08
-#define FCTX_ATTR_WANTCACHE            0x10
-#define FCTX_ATTR_WANTNCACHE           0x20
-#define FCTX_ATTR_NEEDEDNS0            0x40
-#define FCTX_ATTR_TRIEDFIND            0x80
+#define FCTX_ATTR_HAVEANSWER           0x0001
+#define FCTX_ATTR_GLUING               0x0002
+#define FCTX_ATTR_ADDRWAIT             0x0004
+#define FCTX_ATTR_SHUTTINGDOWN         0x0008
+#define FCTX_ATTR_WANTCACHE            0x0010
+#define FCTX_ATTR_WANTNCACHE           0x0020
+#define FCTX_ATTR_NEEDEDNS0            0x0040
+#define FCTX_ATTR_TRIEDFIND            0x0080
+#define FCTX_ATTR_TRIEDALT             0x0100
 
 #define HAVE_ANSWER(f)         (((f)->attributes & FCTX_ATTR_HAVEANSWER) != \
                                 0)
@@ -231,6 +235,7 @@ struct fetchctx {
 #define WANTNCACHE(f)          (((f)->attributes & FCTX_ATTR_WANTNCACHE) != 0)
 #define NEEDEDNS0(f)           (((f)->attributes & FCTX_ATTR_NEEDEDNS0) != 0)
 #define TRIEDFIND(f)           (((f)->attributes & FCTX_ATTR_TRIEDFIND) != 0)
+#define TRIEDALT(f)            (((f)->attributes & FCTX_ATTR_TRIEDALT) != 0)
 
 struct dns_fetch {
        unsigned int                    magic;
@@ -247,6 +252,18 @@ typedef struct fctxbucket {
        isc_boolean_t                   exiting;
 } fctxbucket_t;
 
+typedef struct alternate {
+       isc_boolean_t                   isaddress;
+       union   {
+               isc_sockaddr_t          addr;
+               struct {
+                       dns_name_t      name;
+                       in_port_t       port;
+               } _n;
+       } _u;
+       ISC_LINK(struct alternate)      link;
+} alternate_t;
+
 struct dns_resolver {
        /* Unlocked. */
        unsigned int                    magic;
@@ -266,6 +283,7 @@ struct dns_resolver {
        unsigned int                    nbuckets;
        fctxbucket_t *                  buckets;
        isc_uint32_t                    lame_ttl;
+       ISC_LIST(alternate_t)           alternates;
        /* Locked by lock. */
        unsigned int                    references;
        isc_boolean_t                   exiting;
@@ -434,6 +452,7 @@ fctx_cancelquery(resquery_t **queryp, dns_dispatchevent_t **deventp,
                        if (UNMARKED(addrinfo))
                                dns_adb_adjustsrtt(fctx->adb, addrinfo,
                                                   0, factor);
+
        if (finish != NULL && TRIEDFIND(fctx))
                 for (find = ISC_LIST_HEAD(fctx->finds);
                     find != NULL;
@@ -445,6 +464,24 @@ fctx_cancelquery(resquery_t **queryp, dns_dispatchevent_t **deventp,
                                        dns_adb_adjustsrtt(fctx->adb, addrinfo,
                                                           0, factor);
 
+       if (finish != NULL && TRIEDALT(fctx)) {
+               for (addrinfo = ISC_LIST_HEAD(fctx->altaddrs);
+                    addrinfo != NULL;
+                    addrinfo = ISC_LIST_NEXT(addrinfo, publink))
+                       if (UNMARKED(addrinfo))
+                               dns_adb_adjustsrtt(fctx->adb, addrinfo,
+                                                  0, factor);
+                for (find = ISC_LIST_HEAD(fctx->altfinds);
+                    find != NULL;
+                    find = ISC_LIST_NEXT(find, publink))
+                       for (addrinfo = ISC_LIST_HEAD(find->list);
+                            addrinfo != NULL;
+                            addrinfo = ISC_LIST_NEXT(addrinfo, publink))
+                               if (UNMARKED(addrinfo))
+                                       dns_adb_adjustsrtt(fctx->adb, addrinfo,
+                                                          0, factor);
+       }
+
        if (query->dispentry != NULL)
                dns_dispatch_removeresponse(&query->dispentry, deventp);
 
@@ -515,6 +552,22 @@ fctx_cleanupfinds(fetchctx_t *fctx) {
        fctx->find = NULL;
 }
 
+static void
+fctx_cleanupaltfinds(fetchctx_t *fctx) {
+       dns_adbfind_t *find, *next_find;
+
+       REQUIRE(ISC_LIST_EMPTY(fctx->queries));
+
+       for (find = ISC_LIST_HEAD(fctx->altfinds);
+            find != NULL;
+            find = next_find) {
+               next_find = ISC_LIST_NEXT(find, publink);
+               ISC_LIST_UNLINK(fctx->altfinds, find, publink);
+               dns_adb_destroyfind(&find);
+       }
+       fctx->altfind = NULL;
+}
+
 static void
 fctx_cleanupforwaddrs(fetchctx_t *fctx) {
        dns_adbaddrinfo_t *addr, *next_addr;
@@ -530,12 +583,29 @@ fctx_cleanupforwaddrs(fetchctx_t *fctx) {
        }
 }
 
+static void
+fctx_cleanupaltaddrs(fetchctx_t *fctx) {
+       dns_adbaddrinfo_t *addr, *next_addr;
+
+       REQUIRE(ISC_LIST_EMPTY(fctx->queries));
+
+       for (addr = ISC_LIST_HEAD(fctx->altaddrs);
+            addr != NULL;
+            addr = next_addr) {
+               next_addr = ISC_LIST_NEXT(addr, publink);
+               ISC_LIST_UNLINK(fctx->altaddrs, addr, publink);
+               dns_adb_freeaddrinfo(fctx->adb, &addr);
+       }
+}
+
 static inline void
 fctx_stopeverything(fetchctx_t *fctx, isc_boolean_t no_response) {
        FCTXTRACE("stopeverything");
        fctx_cancelqueries(fctx, no_response);
        fctx_cleanupfinds(fctx);
+       fctx_cleanupaltfinds(fctx);
        fctx_cleanupforwaddrs(fctx);
+       fctx_cleanupaltaddrs(fctx);
        fctx_stoptimer(fctx);
 }
 
@@ -1331,6 +1401,31 @@ mark_bad(fetchctx_t *fctx) {
                        all_bad = ISC_FALSE;
        }
 
+       /*
+        * Mark any bad alternates.
+        */
+       for (curr = ISC_LIST_HEAD(fctx->altfinds);
+            curr != NULL;
+            curr = ISC_LIST_NEXT(curr, publink)) {
+               for (addrinfo = ISC_LIST_HEAD(curr->list);
+                    addrinfo != NULL;
+                    addrinfo = ISC_LIST_NEXT(addrinfo, publink)) {
+                       if (bad_server(fctx, &addrinfo->sockaddr))
+                               addrinfo->flags |= FCTX_ADDRINFO_MARK;
+                       else
+                               all_bad = ISC_FALSE;
+               }
+       }
+
+       for (addrinfo = ISC_LIST_HEAD(fctx->altaddrs);
+            addrinfo != NULL;
+            addrinfo = ISC_LIST_NEXT(addrinfo, publink)) {
+               if (bad_server(fctx, &addrinfo->sockaddr))
+                       addrinfo->flags |= FCTX_ADDRINFO_MARK;
+               else
+                       all_bad = ISC_FALSE;
+       }
+
        return (all_bad);
 }
 
@@ -1444,6 +1539,146 @@ sort_finds(fetchctx_t *fctx) {
                ISC_LIST_APPEND(sorted, best, publink);
        }
        fctx->finds = sorted;
+
+       ISC_LIST_INIT(sorted);
+       while (!ISC_LIST_EMPTY(fctx->altfinds)) {
+               best = ISC_LIST_HEAD(fctx->altfinds);
+               bestaddrinfo = ISC_LIST_HEAD(best->list);
+               INSIST(bestaddrinfo != NULL);
+               curr = ISC_LIST_NEXT(best, publink);
+               while (curr != NULL) {
+                       addrinfo = ISC_LIST_HEAD(curr->list);
+                       INSIST(addrinfo != NULL);
+                       if (addrinfo->srtt < bestaddrinfo->srtt) {
+                               best = curr;
+                               bestaddrinfo = addrinfo;
+                       }
+                       curr = ISC_LIST_NEXT(curr, publink);
+               }
+               ISC_LIST_UNLINK(fctx->altfinds, best, publink);
+               ISC_LIST_APPEND(sorted, best, publink);
+       }
+       fctx->altfinds = sorted;
+}
+
+static void
+findname(fetchctx_t *fctx, dns_name_t *name, in_port_t port,
+        unsigned int options, unsigned int flags, isc_stdtime_t now,
+        isc_boolean_t *pruned, isc_boolean_t *need_alternate)
+{
+       dns_adbaddrinfo_t *ai;
+       dns_adbfind_t *find;
+       dns_resolver_t *res;
+       isc_boolean_t unshared;
+       isc_result_t result;
+
+       res = fctx->res;
+       unshared = ISC_TF((fctx->options | DNS_FETCHOPT_UNSHARED) != 0);
+       /*
+        * If this name is a subdomain of the query domain, tell
+        * the ADB to start looking at "." if it doesn't know the
+        * address.  This keeps us from getting stuck if the
+        * nameserver is beneath the zone cut and we don't know its
+        * address (e.g. because the A record has expired).
+        * By restarting from ".", we ensure that any missing glue
+        * will be reestablished.
+        *
+        * A further optimization would be to get the ADB to start
+        * looking at the most enclosing zone cut above fctx->domain.
+        * We don't expect this situation to happen very frequently,
+        * so we've chosen the simple solution.
+        */
+       if (dns_name_issubdomain(name, &fctx->domain))
+               options |= DNS_ADBFIND_STARTATROOT;
+       options |= DNS_ADBFIND_GLUEOK;
+       options |= DNS_ADBFIND_HINTOK;
+
+       /*
+        * See what we know about this address.
+        */
+       find = NULL;
+       result = dns_adb_createfind(fctx->adb,
+                                   res->buckets[fctx->bucketnum].task,
+                                   fctx_finddone, fctx, name,
+                                   &fctx->domain, options, now, NULL,
+                                   res->view->dstport, &find);
+       if (result != ISC_R_SUCCESS) {
+               if (result == DNS_R_ALIAS) {
+                       /*
+                        * XXXRTH  Follow the CNAME/DNAME chain?
+                        */
+                       dns_adb_destroyfind(&find);
+               }
+       } else if (!ISC_LIST_EMPTY(find->list)) {
+               /*
+                * We have at least some of the addresses for the
+                * name.
+                */
+               INSIST((find->options & DNS_ADBFIND_WANTEVENT) == 0);
+               sort_adbfind(find);
+               if (flags != 0 || port != 0) {
+                       for (ai = ISC_LIST_HEAD(find->list);
+                            ai != NULL;
+                            ai = ISC_LIST_NEXT(ai, publink)) {
+                               ai->flags |= flags;
+                               if (port != 0)
+                                       isc_sockaddr_setport(&ai->sockaddr,
+                                                            port);
+                       }
+               }
+               if ((flags & FCTX_ADDRINFO_FORWARDER) != 0)
+                       ISC_LIST_APPEND(fctx->altfinds, find, publink);
+               else
+                       ISC_LIST_APPEND(fctx->finds, find, publink);
+       } else {
+               /*
+                * We don't know any of the addresses for this
+                * name.
+                */
+               if ((find->options & DNS_ADBFIND_WANTEVENT) != 0) {
+                       /*
+                        * We're looking for them and will get an
+                        * event about it later.
+                        */
+                       fctx->pending++;
+                       /*
+                        * Bootstrap.
+                        */
+                       if (need_alternate != NULL &&
+                           !*need_alternate && unshared &&
+                           ((res->dispatchv4 == NULL &&
+                             find->result_v6 != DNS_R_NXDOMAIN) ||
+                            (res->dispatchv6 == NULL &&
+                             find->result_v4 != DNS_R_NXDOMAIN)))
+                               *need_alternate = ISC_TRUE;
+               } else {
+                       /*
+                        * If we know there are no addresses for
+                        * the family we are using then try to add
+                        * an alternative server.
+                        */
+                       if (need_alternate != NULL && !*need_alternate &&
+                           ((res->dispatchv4 == NULL &&
+                             find->result_v6 == DNS_R_NXRRSET) ||
+                            (res->dispatchv6 == NULL &&
+                             find->result_v4 == DNS_R_NXRRSET)))
+                               *need_alternate = ISC_TRUE;
+                       /*
+                        * And ADB isn't going to send us any events
+                        * either.  This find loses.
+                        */
+                       if ((find->options & DNS_ADBFIND_LAMEPRUNED)
+                           != 0) {
+                               /*
+                                * The ADB pruned lame servers for
+                                * this name.  Remember that in case
+                                * we get desperate later on.
+                                */
+                               *pruned = ISC_TRUE;
+                       }
+                       dns_adb_destroyfind(&find);
+               }
+       }
 }
 
 static isc_result_t
@@ -1452,12 +1687,13 @@ fctx_getaddresses(fetchctx_t *fctx) {
        isc_result_t result;
        dns_resolver_t *res;
        isc_stdtime_t now;
-       dns_adbfind_t *find;
-       unsigned int stdoptions, options;
+       unsigned int stdoptions;
        isc_sockaddr_t *sa;
        dns_adbaddrinfo_t *ai;
        isc_boolean_t pruned, all_bad;
        dns_rdata_ns_t ns;
+       isc_boolean_t need_alternate = ISC_FALSE;
+       isc_boolean_t unshared;
 
        FCTXTRACE("getaddresses");
 
@@ -1473,12 +1709,14 @@ fctx_getaddresses(fetchctx_t *fctx) {
        res = fctx->res;
        pruned = ISC_FALSE;
        stdoptions = 0;         /* Keep compiler happy. */
+       unshared = ISC_TF((fctx->options | DNS_FETCHOPT_UNSHARED) != 0);
 
        /*
         * Forwarders.
         */
 
        INSIST(ISC_LIST_EMPTY(fctx->forwaddrs));
+       INSIST(ISC_LIST_EMPTY(fctx->altaddrs));
 
        /*
         * If this fctx has forwarders, use them; otherwise use any
@@ -1495,7 +1733,7 @@ fctx_getaddresses(fetchctx_t *fctx) {
                        fctx->fwdpolicy = forwarders->fwdpolicy;
                }
        }
-
+       
        while (sa != NULL) {
                ai = NULL;
                result = dns_adb_findaddrinfo(fctx->adb,
@@ -1549,6 +1787,7 @@ fctx_getaddresses(fetchctx_t *fctx) {
 
  restart:
        INSIST(ISC_LIST_EMPTY(fctx->finds));
+       INSIST(ISC_LIST_EMPTY(fctx->altfinds));
 
        for (result = dns_rdataset_first(&fctx->nameservers);
             result == ISC_R_SUCCESS;
@@ -1562,84 +1801,51 @@ fctx_getaddresses(fetchctx_t *fctx) {
                if (result != ISC_R_SUCCESS)
                        continue;
 
-               options = stdoptions;
-               /*
-                * If this name is a subdomain of the query domain, tell
-                * the ADB to start looking at "." if it doesn't know the
-                * address.  This keeps us from getting stuck if the
-                * nameserver is beneath the zone cut and we don't know its
-                * address (e.g. because the A record has expired).
-                * By restarting from ".", we ensure that any missing glue
-                * will be reestablished.
-                *
-                * A further optimization would be to get the ADB to start
-                * looking at the most enclosing zone cut above fctx->domain.
-                * We don't expect this situation to happen very frequently,
-                * so we've chosen the simple solution.
-                */
-               if (dns_name_issubdomain(&ns.name, &fctx->domain))
-                       options |= DNS_ADBFIND_STARTATROOT;
-               options |= DNS_ADBFIND_GLUEOK;
-               options |= DNS_ADBFIND_HINTOK;
-
-               /*
-                * See what we know about this address.
-                */
-               find = NULL;
-               result = dns_adb_createfind(fctx->adb,
-                                           res->buckets[fctx->bucketnum].task,
-                                           fctx_finddone, fctx, &ns.name,
-                                           &fctx->domain, options, now, NULL,
-                                           res->view->dstport, &find);
-               if (result != ISC_R_SUCCESS) {
-                       if (result == DNS_R_ALIAS) {
-                               /*
-                                * XXXRTH  Follow the CNAME/DNAME chain?
-                                */
-                               dns_adb_destroyfind(&find);
-                       }
-               } else if (!ISC_LIST_EMPTY(find->list)) {
-                       /*
-                        * We have at least some of the addresses for the
-                        * name.
-                        */
-                       INSIST((find->options & DNS_ADBFIND_WANTEVENT) == 0);
-                       sort_adbfind(find);
-                       ISC_LIST_APPEND(fctx->finds, find, publink);
-               } else {
-                       /*
-                        * We don't know any of the addresses for this
-                        * name.
-                        */
-                       if ((find->options & DNS_ADBFIND_WANTEVENT) != 0) {
-                               /*
-                                * We're looking for them and will get an
-                                * event about it later.
-                                */
-                               fctx->pending++;
-                       } else {
-                               /*
-                                * And ADB isn't going to send us any events
-                                * either.  This find loses.
-                                */
-                               if ((find->options & DNS_ADBFIND_LAMEPRUNED)
-                                   != 0) {
-                                       /*
-                                        * The ADB pruned lame servers for
-                                        * this name.  Remember that in case
-                                        * we get desperate later on.
-                                        */
-                                       pruned = ISC_TRUE;
-                               }
-                               dns_adb_destroyfind(&find);
-                       }
-               }
+               findname(fctx, &ns.name, 0, stdoptions, 0, now,
+                        &pruned, &need_alternate);
                dns_rdata_reset(&rdata);
                dns_rdata_freestruct(&ns);
        }
        if (result != ISC_R_NOMORE)
                return (result);
 
+       /*
+        * Do we need to use 6 to 4?
+        */
+       if (need_alternate) {
+               int family;
+               alternate_t *a;
+               family = (res->dispatchv6 != NULL) ? AF_INET6 : AF_INET;
+               for (a = ISC_LIST_HEAD(fctx->res->alternates);
+                    a != NULL;
+                    a = ISC_LIST_NEXT(a, link)) {
+                       if (!a->isaddress) {
+                               findname(fctx, &a->_u._n.name, a->_u._n.port,
+                                        stdoptions, FCTX_ADDRINFO_FORWARDER,
+                                        now, &pruned, NULL);
+                               continue;
+                       }
+                       if (isc_sockaddr_pf(&a->_u.addr) != family)
+                               continue;
+                       ai = NULL;
+                       result = dns_adb_findaddrinfo(fctx->adb, &a->_u.addr,
+                                                     &ai, 0);
+                       if (result == ISC_R_SUCCESS) {
+                               dns_adbaddrinfo_t *cur;
+                               ai->flags |= FCTX_ADDRINFO_FORWARDER;
+                               cur = ISC_LIST_HEAD(fctx->altaddrs);
+                               while (cur != NULL && cur->srtt < ai->srtt)
+                                       cur = ISC_LIST_NEXT(cur, publink);
+                               if (cur != NULL)
+                                       ISC_LIST_INSERTBEFORE(fctx->altaddrs,
+                                                             cur, ai, publink);
+                               else
+                                       ISC_LIST_APPEND(fctx->altaddrs, ai,
+                                                       publink);
+                       }
+               }
+       }
+
  out:
        /*
         * Mark all known bad servers.
@@ -1668,6 +1874,7 @@ fctx_getaddresses(fetchctx_t *fctx) {
                        INSIST((stdoptions & DNS_ADBFIND_RETURNLAME) == 0);
                        stdoptions |= DNS_ADBFIND_RETURNLAME;
                        pruned = ISC_FALSE;
+                       fctx_cleanupaltfinds(fctx);
                        fctx_cleanupfinds(fctx);
                        goto restart;
                } else {
@@ -1756,6 +1963,7 @@ static inline dns_adbaddrinfo_t *
 fctx_nextaddress(fetchctx_t *fctx) {
        dns_adbfind_t *find;
        dns_adbaddrinfo_t *addrinfo;
+       dns_adbaddrinfo_t *faddrinfo;
 
        /*
         * Return the next untried address, if any.
@@ -1767,6 +1975,8 @@ fctx_nextaddress(fetchctx_t *fctx) {
        for (addrinfo = ISC_LIST_HEAD(fctx->forwaddrs);
             addrinfo != NULL;
             addrinfo = ISC_LIST_NEXT(addrinfo, publink)) {
+               if (!UNMARKED(addrinfo))
+                       continue;
                possibly_mark(fctx, addrinfo);
                if (UNMARKED(addrinfo)) {
                        addrinfo->flags |= FCTX_ADDRINFO_MARK;
@@ -1775,11 +1985,12 @@ fctx_nextaddress(fetchctx_t *fctx) {
                }
        }
 
-       fctx->attributes |= FCTX_ATTR_TRIEDFIND;
-
        /*
         * No forwarders.  Move to the next find.
         */
+
+       fctx->attributes |= FCTX_ATTR_TRIEDFIND;
+
        find = fctx->find;
        if (find == NULL)
                find = ISC_LIST_HEAD(fctx->finds);
@@ -1797,6 +2008,8 @@ fctx_nextaddress(fetchctx_t *fctx) {
                for (addrinfo = ISC_LIST_HEAD(find->list);
                     addrinfo != NULL;
                     addrinfo = ISC_LIST_NEXT(addrinfo, publink)) {
+                       if (!UNMARKED(addrinfo))
+                               continue;
                        possibly_mark(fctx, addrinfo);
                        if (UNMARKED(addrinfo)) {
                                addrinfo->flags |= FCTX_ADDRINFO_MARK;
@@ -1811,6 +2024,73 @@ fctx_nextaddress(fetchctx_t *fctx) {
        }
 
        fctx->find = find;
+       if (addrinfo != NULL)
+               return (addrinfo);
+
+       /*
+        * No nameservers left.  Try alternates.
+        */
+
+       fctx->attributes |= FCTX_ATTR_TRIEDALT;
+
+       find = fctx->altfind;
+       if (find == NULL)
+               find = ISC_LIST_HEAD(fctx->altfinds);
+       else {
+               find = ISC_LIST_NEXT(find, publink);
+               if (find == NULL)
+                       find = ISC_LIST_HEAD(fctx->altfinds);
+       }
+
+       /*
+        * Find the first unmarked addrinfo.
+        */
+       addrinfo = NULL;
+       while (find != fctx->altfind) {
+               for (addrinfo = ISC_LIST_HEAD(find->list);
+                    addrinfo != NULL;
+                    addrinfo = ISC_LIST_NEXT(addrinfo, publink)) {
+                       if (!UNMARKED(addrinfo))
+                               continue;
+                       possibly_mark(fctx, addrinfo);
+                       if (UNMARKED(addrinfo)) {
+                               addrinfo->flags |= FCTX_ADDRINFO_MARK;
+                               break;
+                       }
+               }
+               if (addrinfo != NULL)
+                       break;
+               find = ISC_LIST_NEXT(find, publink);
+               if (find != fctx->altfind && find == NULL)
+                       find = ISC_LIST_HEAD(fctx->altfinds);
+       }
+
+       faddrinfo = addrinfo;
+
+       /*
+        * See if we have a better alternate server by address.
+        */
+
+       for (addrinfo = ISC_LIST_HEAD(fctx->altaddrs);
+            addrinfo != NULL;
+            addrinfo = ISC_LIST_NEXT(addrinfo, publink)) {
+               if (!UNMARKED(addrinfo))
+                       continue;
+               possibly_mark(fctx, addrinfo);
+               if (UNMARKED(addrinfo) && 
+                   (faddrinfo == NULL ||
+                    addrinfo->srtt < faddrinfo->srtt)) {
+                       if (faddrinfo != NULL)
+                               faddrinfo->flags &= ~FCTX_ADDRINFO_MARK;
+                       addrinfo->flags |= FCTX_ADDRINFO_MARK;
+                       break;
+               }
+       }
+
+       if (addrinfo == NULL) {
+               addrinfo = faddrinfo;
+               fctx->altfind = find;
+       }
 
        return (addrinfo);
 }
@@ -1831,7 +2111,9 @@ fctx_try(fetchctx_t *fctx) {
                 */
                fctx_cancelqueries(fctx, ISC_TRUE);
                fctx_cleanupfinds(fctx);
+               fctx_cleanupaltfinds(fctx);
                fctx_cleanupforwaddrs(fctx);
+               fctx_cleanupaltaddrs(fctx);
                result = fctx_getaddresses(fctx);
                if (result == DNS_R_WAIT) {
                        /*
@@ -1880,6 +2162,7 @@ fctx_destroy(fetchctx_t *fctx) {
        REQUIRE(ISC_LIST_EMPTY(fctx->events));
        REQUIRE(ISC_LIST_EMPTY(fctx->queries));
        REQUIRE(ISC_LIST_EMPTY(fctx->finds));
+       REQUIRE(ISC_LIST_EMPTY(fctx->altfinds));
        REQUIRE(fctx->pending == 0);
        REQUIRE(ISC_LIST_EMPTY(fctx->validators));
        REQUIRE(fctx->references == 0);
@@ -2232,12 +2515,15 @@ fctx_create(dns_resolver_t *res, dns_name_t *name, dns_rdatatype_t type,
        fctx->cloned = ISC_FALSE;
        ISC_LIST_INIT(fctx->queries);
        ISC_LIST_INIT(fctx->finds);
+       ISC_LIST_INIT(fctx->altfinds);
        ISC_LIST_INIT(fctx->forwaddrs);
+       ISC_LIST_INIT(fctx->altaddrs);
        ISC_LIST_INIT(fctx->forwarders);
        fctx->fwdpolicy = dns_fwdpolicy_none;
        ISC_LIST_INIT(fctx->bad);
        ISC_LIST_INIT(fctx->validators);
        fctx->find = NULL;
+       fctx->altfind = NULL;
        fctx->pending = 0;
        fctx->restarts = 0;
        fctx->timeouts = 0;
@@ -4814,7 +5100,9 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
                        }
                        fctx_cancelqueries(fctx, ISC_TRUE);
                        fctx_cleanupfinds(fctx);
+                       fctx_cleanupaltfinds(fctx);
                        fctx_cleanupforwaddrs(fctx);
+                       fctx_cleanupaltaddrs(fctx);
                }
                /*
                 * Try again.
@@ -4884,6 +5172,7 @@ resquery_response(isc_task_t *task, isc_event_t *event) {
 static void
 destroy(dns_resolver_t *res) {
        unsigned int i;
+       alternate_t *a;
 
        REQUIRE(res->references == 0);
        REQUIRE(!res->priming);
@@ -4907,6 +5196,12 @@ destroy(dns_resolver_t *res) {
                dns_dispatch_detach(&res->dispatchv4);
        if (res->dispatchv6 != NULL)
                dns_dispatch_detach(&res->dispatchv6);
+       while ((a = ISC_LIST_HEAD(res->alternates)) != NULL) {
+               ISC_LIST_UNLINK(res->alternates, a, link);
+               if (!a->isaddress)
+                       dns_name_free(&a->_u._n.name, res->mctx);
+               isc_mem_put(res->mctx, a, sizeof(*a));
+       }
        res->magic = 0;
        isc_mem_put(res->mctx, res, sizeof(*res));
 }
@@ -4984,6 +5279,7 @@ dns_resolver_create(dns_view_t *view,
        res->view = view;
        res->options = options;
        res->lame_ttl = 0;
+       ISC_LIST_INIT(res->alternates);
 
        res->nbuckets = ntasks;
        res->activebuckets = ntasks;
@@ -5596,3 +5892,35 @@ dns_resolver_nrunning(dns_resolver_t *resolver) {
        UNLOCK(&resolver->nlock);
        return (n);
 }
+
+isc_result_t
+dns_resolver_addalternate(dns_resolver_t *resolver, isc_sockaddr_t *alt,
+                         dns_name_t *name, in_port_t port) {
+       alternate_t *a;
+       isc_result_t result;
+
+       REQUIRE(VALID_RESOLVER(resolver));
+       REQUIRE(!resolver->frozen);
+       REQUIRE((alt == NULL) ^ (name == NULL));
+
+       a = isc_mem_get(resolver->mctx, sizeof(*a));
+       if (a == NULL)
+               return (ISC_R_NOMEMORY);
+       if (alt != NULL) {
+               a->isaddress = ISC_TRUE;
+               a->_u.addr = *alt;
+       } else {
+               a->isaddress = ISC_FALSE;
+               a->_u._n.port = port;
+               dns_name_init(&a->_u._n.name, NULL);
+               result = dns_name_dup(name, resolver->mctx, &a->_u._n.name);
+               if (result != ISC_R_SUCCESS) {
+                       isc_mem_put(resolver->mctx, a, sizeof(*a));
+                       return (result);
+               }
+       }
+       ISC_LINK_INIT(a, link);
+       ISC_LIST_APPEND(resolver->alternates, a, link);
+
+       return (ISC_R_SUCCESS);
+}
index bf6b99258732293e22031f753e39d5165bf3f9ae..bc4769a320523220869d9fdf7b1b7f57fa4c9e3f 100644 (file)
@@ -15,7 +15,7 @@
  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: result.h,v 1.59 2001/11/30 01:02:17 gson Exp $ */
+/* $Id: result.h,v 1.60 2003/01/16 03:59:26 marka Exp $ */
 
 #ifndef ISC_RESULT_H
 #define ISC_RESULT_H 1
 #define ISC_R_CONNECTIONRESET          54      /* connection reset */
 #define ISC_R_SOFTQUOTA                        55      /* soft quota reached */
 #define ISC_R_BADNUMBER                        56      /* not a valid number */
+#define ISC_R_DISABLED                 57      /* disabled */
 
 /*
  * Not a result code: the number of results.
  */
-#define ISC_R_NRESULTS                         57
+#define ISC_R_NRESULTS                         58
 
 ISC_LANG_BEGINDECLS
 
index 397bfad50d12f3e2ae0181e74b2e7d5708cd68d1..53cf8b39355050b42b3ae793b63998e09d60986f 100644 (file)
@@ -15,7 +15,7 @@
  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: result.c,v 1.59 2001/11/30 01:02:14 gson Exp $ */
+/* $Id: result.c,v 1.60 2003/01/16 03:59:25 marka Exp $ */
 
 #include <config.h>
 
@@ -94,7 +94,8 @@ static const char *text[ISC_R_NRESULTS] = {
        "operation in progress",                /* 53 */
        "connection reset",                     /* 54 */
        "soft quota reached",                   /* 55 */
-       "not a valid number"                    /* 56 */
+       "not a valid number",                   /* 56 */
+       "disabled"                              /* 57 */
 };
 
 #define ISC_RESULT_RESULTSET                   2
index 2997588eae3edc451ceacde9dc3b9ac02f08ee4e..7d6e3f9ed36f191fe6b887c1878b690665225dc9 100644 (file)
@@ -15,7 +15,7 @@
  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: net.h,v 1.36 2002/10/29 04:40:25 marka Exp $ */
+/* $Id: net.h,v 1.37 2003/01/16 03:59:27 marka Exp $ */
 
 #ifndef ISC_NET_H
 #define ISC_NET_H 1
@@ -245,6 +245,7 @@ isc_net_probeipv4(void);
  *
  *     ISC_R_SUCCESS           IPv4 is supported.
  *     ISC_R_NOTFOUND          IPv4 is not supported.
+ *     ISC_R_DISABLED          IPv4 is disabled.
  *     ISC_R_UNEXPECTED
  */
 
@@ -257,6 +258,7 @@ isc_net_probeipv6(void);
  *
  *     ISC_R_SUCCESS           IPv6 is supported.
  *     ISC_R_NOTFOUND          IPv6 is not supported.
+ *     ISC_R_DISABLED          IPv6 is disabled.
  *     ISC_R_UNEXPECTED
  */
 
@@ -272,6 +274,18 @@ isc_net_probe_ipv6only(void);
  *     ISC_R_UNEXPECTED
  */
 
+void
+isc_net_disableipv4(void);
+
+void
+isc_net_disableipv6(void);
+
+void
+isc_net_enableipv4(void);
+
+void
+isc_net_enableipv6(void);
+
 #ifdef ISC_PLATFORM_NEEDNTOP
 const char *
 isc_net_ntop(int af, const void *src, char *dst, size_t size);
index 61934683674da32452baaf0935ec097d4380be51..d27d6ee1cdf9aa277cee6e5ec9d6a5e62ef7f74c 100644 (file)
@@ -15,7 +15,7 @@
  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: net.c,v 1.27 2002/12/24 05:12:50 marka Exp $ */
+/* $Id: net.c,v 1.28 2003/01/16 03:59:27 marka Exp $ */
 
 #include <config.h>
 
@@ -95,7 +95,7 @@ try_proto(int domain) {
                                      "socket from the kernel failed.");
                        isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
                                      ISC_LOGMODULE_SOCKET, ISC_LOG_ERROR,
-                                     "IPv6 support is disabled.");
+                                     "IPv6 is not supported.");
                        result = ISC_R_NOTFOUND;
                } else {
                        if (len == sizeof(struct sockaddr_in6))
@@ -111,7 +111,7 @@ try_proto(int domain) {
                                              ISC_LOGCATEGORY_GENERAL,
                                              ISC_LOGMODULE_SOCKET,
                                              ISC_LOG_ERROR,
-                                             "IPv6 support is disabled.");
+                                             "IPv6 is not supported.");
                                result = ISC_R_NOTFOUND;
                        }
                }
@@ -247,3 +247,31 @@ isc_net_probe_ipv6only(void) {
 #endif
        return (ipv6only_result);
 }
+
+void
+isc_net_disableipv4(void) {
+       initialize();
+       if (ipv4_result == ISC_R_SUCCESS)
+               ipv4_result = ISC_R_DISABLED;
+}
+
+void
+isc_net_disableipv6(void) {
+       initialize();
+       if (ipv6_result == ISC_R_SUCCESS)
+               ipv6_result = ISC_R_DISABLED;
+}
+
+void
+isc_net_enableipv4(void) {
+       initialize();
+       if (ipv4_result == ISC_R_DISABLED)
+               ipv4_result = ISC_R_SUCCESS;
+}
+
+void
+isc_net_enableipv6(void) {
+       initialize();
+       if (ipv6_result == ISC_R_DISABLED)
+               ipv6_result = ISC_R_SUCCESS;
+}
index 1f909362fde84bdd183c0e247bad1d15a0a0cd5a..ced83e5d79e20b3e199a0656aab5fd65cebee8d4 100644 (file)
@@ -15,7 +15,7 @@
  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: net.h,v 1.18 2002/10/29 04:40:26 marka Exp $ */
+/* $Id: net.h,v 1.19 2003/01/16 03:59:28 marka Exp $ */
 
 #ifndef ISC_NET_H
 #define ISC_NET_H 1
@@ -232,6 +232,7 @@ isc_net_probeipv4(void);
  *
  *     ISC_R_SUCCESS           IPv4 is supported.
  *     ISC_R_NOTFOUND          IPv4 is not supported.
+ *     ISC_R_DISABLED          IPv4 is disabled.
  *     ISC_R_UNEXPECTED
  */
 
@@ -244,6 +245,7 @@ isc_net_probeipv6(void);
  *
  *     ISC_R_SUCCESS           IPv6 is supported.
  *     ISC_R_NOTFOUND          IPv6 is not supported.
+ *     ISC_R_DISABLED          IPv6 is disabled.
  *     ISC_R_UNEXPECTED
  */
 
@@ -259,6 +261,18 @@ isc_net_probe_ipv6only(void);
  *     ISC_R_UNEXPECTED
  */
 
+void
+isc_net_disableipv4(void);
+
+void
+isc_net_disableipv6(void);
+
+void
+isc_net_enableipv4(void);
+
+void
+isc_net_enableipv6(void);
+
 #ifdef ISC_PLATFORM_NEEDNTOP
 const char *
 isc_net_ntop(int af, const void *src, char *dst, size_t size);
index b5e0d4f685a4c8268fe42c482453dcf498d3bc3d..29c90f425a3f254db0274110b74280f2c472890f 100644 (file)
@@ -15,7 +15,7 @@
  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: net.c,v 1.6 2003/01/14 23:37:06 marka Exp $ */
+/* $Id: net.c,v 1.7 2003/01/16 03:59:27 marka Exp $ */
 
 #include <config.h>
 
@@ -240,3 +240,31 @@ isc_net_probe_ipv6only(void) {
 #endif
        return (ipv6only_result);
 }
+
+void
+isc_net_disableipv4(void) {
+       initialize();
+       if (ipv4_result == ISC_R_SUCCESS)
+               ipv4_result = ISC_R_DISABLED;
+}
+
+void
+isc_net_disableipv6(void) {
+       initialize();
+       if (ipv6_result == ISC_R_SUCCESS)
+               ipv6_result = ISC_R_DISABLED;
+}
+
+void
+isc_net_enableipv4(void) {
+       initialize();
+       if (ipv4_result == ISC_R_DISABLED)
+               ipv4_result = ISC_R_SUCCESS;
+}
+
+void
+isc_net_enableipv6(void) {
+       initialize();
+       if (ipv6_result == ISC_R_DISABLED)
+               ipv6_result = ISC_R_SUCCESS;
+}
index 691e601479ce01ac377504411e2d271e5cd58992..5c7e7fece8c4e8fe6cbb011400720bf9a44d3520 100644 (file)
@@ -15,7 +15,7 @@
  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: namedconf.c,v 1.11 2002/11/27 09:52:58 marka Exp $ */
+/* $Id: namedconf.c,v 1.12 2003/01/16 03:59:28 marka Exp $ */
 
 #include <config.h>
 
@@ -66,42 +66,45 @@ doc_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type);
 static void
 doc_optional_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type);
 
-static cfg_type_t cfg_type_optional_port;
-static cfg_type_t cfg_type_bracketed_aml;
 static cfg_type_t cfg_type_acl;
-static cfg_type_t cfg_type_portiplist;
+static cfg_type_t cfg_type_addrmatchelt;
+static cfg_type_t cfg_type_bracketed_aml;
+static cfg_type_t cfg_type_bracketed_namesockaddrlist;
+static cfg_type_t cfg_type_bracketed_sockaddrkeylist;
 static cfg_type_t cfg_type_bracketed_sockaddrlist;
+static cfg_type_t cfg_type_controls;
+static cfg_type_t cfg_type_controls_sockaddr;
+static cfg_type_t cfg_type_destinationlist;
+static cfg_type_t cfg_type_dialuptype;
+static cfg_type_t cfg_type_key;
+static cfg_type_t cfg_type_logfile;
+static cfg_type_t cfg_type_logging;
+static cfg_type_t cfg_type_logseverity;
+static cfg_type_t cfg_type_lwres;
+static cfg_type_t cfg_type_nameportiplist;
+static cfg_type_t cfg_type_namesockaddr;
+static cfg_type_t cfg_type_negated;
+static cfg_type_t cfg_type_notifytype;
+static cfg_type_t cfg_type_optional_class;
+static cfg_type_t cfg_type_optional_facility;
+static cfg_type_t cfg_type_optional_facility;
 static cfg_type_t cfg_type_optional_keyref;
+static cfg_type_t cfg_type_optional_port;
 static cfg_type_t cfg_type_options;
-static cfg_type_t cfg_type_view;
-static cfg_type_t cfg_type_viewopts;
-static cfg_type_t cfg_type_key;
-static cfg_type_t cfg_type_server;
-static cfg_type_t cfg_type_controls;
-static cfg_type_t cfg_type_bracketed_sockaddrkeylist;
+static cfg_type_t cfg_type_portiplist;
 static cfg_type_t cfg_type_querysource4;
 static cfg_type_t cfg_type_querysource6;
 static cfg_type_t cfg_type_querysource;
+static cfg_type_t cfg_type_server;
+static cfg_type_t cfg_type_server_key_kludge;
+static cfg_type_t cfg_type_size;
+static cfg_type_t cfg_type_sizenodefault;
 static cfg_type_t cfg_type_sockaddr4wild;
 static cfg_type_t cfg_type_sockaddr6wild;
+static cfg_type_t cfg_type_view;
+static cfg_type_t cfg_type_viewopts;
 static cfg_type_t cfg_type_zone;
 static cfg_type_t cfg_type_zoneopts;
-static cfg_type_t cfg_type_logging;
-static cfg_type_t cfg_type_optional_facility;
-static cfg_type_t cfg_type_optional_class;
-static cfg_type_t cfg_type_destinationlist;
-static cfg_type_t cfg_type_size;
-static cfg_type_t cfg_type_sizenodefault;
-static cfg_type_t cfg_type_negated;
-static cfg_type_t cfg_type_addrmatchelt;
-static cfg_type_t cfg_type_server_key_kludge;
-static cfg_type_t cfg_type_optional_facility;
-static cfg_type_t cfg_type_logseverity;
-static cfg_type_t cfg_type_logfile;
-static cfg_type_t cfg_type_lwres;
-static cfg_type_t cfg_type_controls_sockaddr;
-static cfg_type_t cfg_type_notifytype;
-static cfg_type_t cfg_type_dialuptype;
 
 /* tkey-dhkey */
 
@@ -556,6 +559,7 @@ view_clauses[] = {
        { "cache-file", &cfg_type_qstring, 0 },
        { "suppress-initial-notify", &cfg_type_boolean, CFG_CLAUSEFLAG_NYI },
        { "preferred-glue", &cfg_type_astring, 0 },
+       { "dual-stack-servers", &cfg_type_nameportiplist, 0 },
        { NULL, NULL, 0 }
 };
 
@@ -1259,7 +1263,6 @@ static cfg_type_t cfg_type_controls_sockaddr = {
        cfg_doc_sockaddr, &cfg_rep_sockaddr, &controls_sockaddr_flags
 };
 
-
 /*
  * Handle the special kludge syntax of the "keys" clause in the "server"
  * statement, which takes a single key with or without braces and semicolon.
@@ -1582,3 +1585,92 @@ LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_rndckey = {
        "rndckey", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody,
        &cfg_rep_map, rndckey_clausesets
 };
+
+static cfg_tuplefielddef_t nameport_fields[] = {
+       { "name", &cfg_type_astring, 0 },
+       { "port", &cfg_type_optional_port, 0 },
+       { NULL, NULL, 0 }
+};
+static cfg_type_t cfg_type_nameport = {
+       "nameport", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
+       &cfg_rep_tuple, nameport_fields
+};
+
+static void
+doc_namesockaddr(cfg_printer_t *pctx, const cfg_type_t *type) {
+       UNUSED(type);
+       cfg_print_chars(pctx, "( ", 2);
+       cfg_print_cstr(pctx, "<quoted_string>");
+       cfg_print_chars(pctx, " ", 1);
+       cfg_print_cstr(pctx, "[port <integer>]");
+       cfg_print_chars(pctx, " | ", 3);
+       cfg_print_cstr(pctx, "<ipv4_address>");
+       cfg_print_chars(pctx, " ", 1);
+       cfg_print_cstr(pctx, "[port <integer>]");
+       cfg_print_chars(pctx, " | ", 3);
+       cfg_print_cstr(pctx, "<ipv6_address>");
+       cfg_print_chars(pctx, " ", 1);
+       cfg_print_cstr(pctx, "[port <integer>]");
+       cfg_print_chars(pctx, " )", 2);
+}
+
+static isc_result_t
+parse_namesockaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
+{
+        isc_result_t result;
+       cfg_obj_t *obj = NULL;
+       UNUSED(type);
+
+       CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING));
+       if (pctx->token.type == isc_tokentype_string ||
+           pctx->token.type == isc_tokentype_qstring) {
+               if (cfg_lookingat_netaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V6OK))
+                       CHECK(cfg_parse_sockaddr(pctx, &cfg_type_sockaddr, ret));
+               else {
+                       const cfg_tuplefielddef_t *fields =
+                                                  cfg_type_nameport.of;        
+                       CHECK(cfg_create_tuple(pctx, &cfg_type_nameport,
+                                              &obj));  
+                       CHECK(cfg_parse_obj(pctx, fields[0].type,
+                                           &obj->value.tuple[0]));
+                       CHECK(cfg_parse_obj(pctx, fields[1].type,
+                                           &obj->value.tuple[1]));
+                       *ret = obj;
+                       obj = NULL;
+               }
+       } else {
+               cfg_parser_error(pctx, CFG_LOG_NEAR,
+                            "expected IP match list element");
+               return (ISC_R_UNEXPECTEDTOKEN);
+       }
+ cleanup:
+       CLEANUP_OBJ(obj);       
+       return (result);
+}
+
+static cfg_type_t cfg_type_namesockaddr = {
+       "namesockaddr_element", parse_namesockaddr, NULL, doc_namesockaddr,
+       NULL, NULL
+};
+
+static cfg_type_t cfg_type_bracketed_namesockaddrlist = {
+       "bracketed_namesockaddrlist", cfg_parse_bracketed_list,
+       cfg_print_bracketed_list, cfg_doc_bracketed_list,
+       &cfg_rep_list, &cfg_type_namesockaddr
+};
+
+/*
+ * A list of socket addresses or named with an optional default port,
+ * as used in the dual-stack-servers option.  E.g.,
+ * "port 1234 { dual-stack-servers.net; 10.0.0.1; 1::2 port 69; }"
+ */
+static cfg_tuplefielddef_t nameportiplist_fields[] = {
+       { "port", &cfg_type_optional_port, 0 },
+       { "addresses", &cfg_type_bracketed_namesockaddrlist, 0 },
+       { NULL, NULL, 0 }
+};
+
+static cfg_type_t cfg_type_nameportiplist = {
+       "nameportiplist", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
+       &cfg_rep_tuple, nameportiplist_fields
+};