dns: modify dns forwarder param to be multi-valued
authorGarming Sam <garming@catalyst.net.nz>
Tue, 16 Feb 2016 22:30:21 +0000 (11:30 +1300)
committerGarming Sam <garming@samba.org>
Tue, 3 May 2016 06:10:09 +0000 (08:10 +0200)
This allows a secondary DNS forwarder for a trivial failover. Requests
which fail/timeout at the primary DNS forwarder will be restarted
entirely with the next forwarder in the list.

Signed-off-by: Garming Sam <garming@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
docs-xml/smbdotconf/domain/dnsforwarder.xml
source4/dns_server/dns_query.c
source4/dns_server/dns_server.c

index 4147ef8524f2a1e73b67e3a2bd987894be951f36..d3c8b7684951ad0abe2d204360e0a4855bdf9849 100644 (file)
@@ -1,10 +1,9 @@
 <samba:parameter name="dns forwarder"
                  context="G"
-                 type="string"
-                 constant="1"
+                 type="cmdlist"
                  xmlns:samba="http://www.samba.org/samba/DTD/samba-doc">
 <description>
-       <para>This option specifies the DNS server that DNS requests will be 
+       <para>This option specifies the list of DNS servers that DNS requests will be
                forwarded to if they can not be handled by Samba itself.
        </para>
 
index c251430a5ef8c65c1633776919b02d90bc54bfd1..146054bf4deab3b8af26aa472f52136d32d17f96 100644 (file)
@@ -31,6 +31,7 @@
 #include "dsdb/common/util.h"
 #include "dns_server/dns_server.h"
 #include "libcli/dns/libdns.h"
+#include "lib/util/dlinklist.h"
 #include "lib/util/util_net.h"
 #include "lib/util/tevent_werror.h"
 #include "auth/auth.h"
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_DNS
 
+struct forwarder_string {
+       const char *forwarder;
+       struct forwarder_string *prev, *next;
+};
+
 static WERROR add_response_rr(const char *name,
                              const struct dnsp_DnssrvRpcRecord *rec,
                              struct dns_res_rec **answers)
@@ -912,12 +918,17 @@ static WERROR handle_tkey(struct dns_server *dns,
 }
 
 struct dns_server_process_query_state {
+       struct tevent_context *ev;
+       struct dns_server *dns;
+       struct dns_name_question *question;
+
        struct dns_res_rec *answers;
        uint16_t ancount;
        struct dns_res_rec *nsrecs;
        uint16_t nscount;
        struct dns_res_rec *additional;
        uint16_t arcount;
+       struct forwarder_string *forwarders;
 };
 
 static void dns_server_process_query_got_auth(struct tevent_req *subreq);
@@ -930,6 +941,8 @@ struct tevent_req *dns_server_process_query_send(
 {
        struct tevent_req *req, *subreq;
        struct dns_server_process_query_state *state;
+       const char **forwarders = NULL;
+       unsigned int i;
 
        req = tevent_req_create(mem_ctx, &state,
                                struct dns_server_process_query_state);
@@ -959,6 +972,18 @@ struct tevent_req *dns_server_process_query_send(
                return tevent_req_post(req, ev);
        }
 
+       state->dns = dns;
+       state->ev = ev;
+       state->question = &in->questions[0];
+
+       forwarders = lpcfg_dns_forwarder(dns->task->lp_ctx);
+       for (i = 0; forwarders != NULL && forwarders[i] != NULL; i++) {
+               struct forwarder_string *f = talloc_zero(state,
+                                                        struct forwarder_string);
+               f->forwarder = forwarders[i];
+               DLIST_ADD_END(state->forwarders, f);
+       }
+
        if (dns_authorative_for_zone(dns, in->questions[0].name)) {
 
                req_state->flags |= DNS_FLAG_AUTHORITATIVE;
@@ -978,7 +1003,7 @@ struct tevent_req *dns_server_process_query_send(
                }
 
                subreq = handle_authoritative_send(
-                       state, ev, dns, lpcfg_dns_forwarder(dns->task->lp_ctx),
+                       state, ev, dns, (forwarders == NULL ? NULL : forwarders[0]),
                        &in->questions[0], &state->answers, &state->nsrecs);
                if (tevent_req_nomem(subreq, req)) {
                        return tevent_req_post(req, ev);
@@ -993,9 +1018,9 @@ struct tevent_req *dns_server_process_query_send(
                DEBUG(2, ("Not authoritative for '%s', forwarding\n",
                          in->questions[0].name));
 
-               subreq = ask_forwarder_send(
-                       state, ev, dns, lpcfg_dns_forwarder(dns->task->lp_ctx),
-                       &in->questions[0]);
+               subreq = ask_forwarder_send(state, ev, dns,
+                                           (forwarders == NULL ? NULL : forwarders[0]),
+                                           &in->questions[0]);
                if (tevent_req_nomem(subreq, req)) {
                        return tevent_req_post(req, ev);
                }
@@ -1014,16 +1039,42 @@ static void dns_server_process_query_got_response(struct tevent_req *subreq)
                subreq, struct tevent_req);
        struct dns_server_process_query_state *state = tevent_req_data(
                req, struct dns_server_process_query_state);
-       WERROR err;
+       WERROR werr;
 
-       err = ask_forwarder_recv(subreq, state,
-                                &state->answers, &state->ancount,
-                                &state->nsrecs, &state->nscount,
-                                &state->additional, &state->arcount);
+       werr = ask_forwarder_recv(subreq, state,
+                                 &state->answers, &state->ancount,
+                                 &state->nsrecs, &state->nscount,
+                                 &state->additional, &state->arcount);
        TALLOC_FREE(subreq);
-       if (tevent_req_werror(req, err)) {
+
+       /* If you get an error, attempt a different forwarder */
+       if (!W_ERROR_IS_OK(werr)) {
+               if (state->forwarders != NULL) {
+                       DLIST_REMOVE(state->forwarders, state->forwarders);
+               }
+
+               /* If you have run out of forwarders, simply finish */
+               if (state->forwarders == NULL) {
+                       tevent_req_werror(req, werr);
+                       return;
+               }
+
+               DEBUG(5, ("DNS query returned %s, trying another forwarder.\n",
+                         win_errstr(werr)));
+               subreq = ask_forwarder_send(state, state->ev, state->dns,
+                                           state->forwarders->forwarder,
+                                           state->question);
+
+               if (tevent_req_nomem(subreq, req)) {
+                       return;
+               }
+
+               tevent_req_set_callback(subreq,
+                                       dns_server_process_query_got_response,
+                                       req);
                return;
        }
+
        tevent_req_done(req);
 }
 
@@ -1037,9 +1088,36 @@ static void dns_server_process_query_got_auth(struct tevent_req *subreq)
 
        werr = handle_authoritative_recv(subreq);
        TALLOC_FREE(subreq);
-       if (tevent_req_werror(req, werr)) {
+
+       /* If you get an error, attempt a different forwarder */
+       if (!W_ERROR_IS_OK(werr)) {
+               if (state->forwarders != NULL) {
+                       DLIST_REMOVE(state->forwarders, state->forwarders);
+               }
+
+               /* If you have run out of forwarders, simply finish */
+               if (state->forwarders == NULL) {
+                       tevent_req_werror(req, werr);
+                       return;
+               }
+
+               DEBUG(5, ("Error: %s, trying a different forwarder.\n",
+                         win_errstr(werr)));
+               subreq = handle_authoritative_send(state, state->ev, state->dns,
+                                                  state->forwarders->forwarder,
+                                                  state->question, &state->answers,
+                                                  &state->nsrecs);
+
+               if (tevent_req_nomem(subreq, req)) {
+                       return;
+               }
+
+               tevent_req_set_callback(subreq,
+                                       dns_server_process_query_got_auth,
+                                       req);
                return;
        }
+
        state->ancount = talloc_array_length(state->answers);
        state->nscount = talloc_array_length(state->nsrecs);
        state->arcount = talloc_array_length(state->additional);
index a2dc15195bf7e8ddd6d6703b7bd34900e789289b..ae7ec7aad6f084b398aee6a73f44af20277ebbe8 100644 (file)
@@ -123,7 +123,7 @@ static struct tevent_req *dns_process_send(TALLOC_CTX *mem_ctx,
        struct dns_process_state *state;
        enum ndr_err_code ndr_err;
        WERROR ret;
-       const char *forwarder = lpcfg_dns_forwarder(dns->task->lp_ctx);
+       const char **forwarder = lpcfg_dns_forwarder(dns->task->lp_ctx);
        req = tevent_req_create(mem_ctx, &state, struct dns_process_state);
        if (req == NULL) {
                return NULL;
@@ -169,8 +169,8 @@ static struct tevent_req *dns_process_send(TALLOC_CTX *mem_ctx,
        state->state.flags = state->in_packet.operation;
        state->state.flags |= DNS_FLAG_REPLY;
 
-       
-       if (forwarder && *forwarder) {
+
+       if (forwarder && *forwarder && **forwarder) {
                state->state.flags |= DNS_FLAG_RECURSION_AVAIL;
        }