dns_server: Add handle_authoritative_send()
authorVolker Lendecke <vl@samba.org>
Tue, 11 Aug 2015 05:39:31 +0000 (07:39 +0200)
committerKai Blin <kai@samba.org>
Tue, 15 Dec 2015 13:43:09 +0000 (14:43 +0100)
An async version of handle_question

Bug: https://bugzilla.samba.org/show_bug.cgi?id=9409
Signed-off-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Kai Blin <kai@samba.org>
source4/dns_server/dns_query.c

index 4e5382582a47232476307a7b9b741802406c877e..e46e68bbe49c7de373d7dbb27e7a601b72af7f08 100644 (file)
@@ -520,6 +520,271 @@ done:
        return werror_return;
 }
 
+static struct tevent_req *handle_authoritative_send(
+       TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+       struct dns_server *dns, const char *forwarder,
+       struct dns_name_question *question,
+       struct dns_res_rec **answers, struct dns_res_rec **nsrecs);
+static WERROR handle_authoritative_recv(struct tevent_req *req);
+
+struct handle_dnsrpcrec_state {
+       struct dns_res_rec **answers;
+       struct dns_res_rec **nsrecs;
+};
+
+static void handle_dnsrpcrec_gotauth(struct tevent_req *subreq);
+static void handle_dnsrpcrec_gotforwarded(struct tevent_req *subreq);
+
+static struct tevent_req *handle_dnsrpcrec_send(
+       TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+       struct dns_server *dns, const char *forwarder,
+       const struct dns_name_question *question,
+       struct dnsp_DnssrvRpcRecord *rec,
+       struct dns_res_rec **answers, struct dns_res_rec **nsrecs)
+{
+       struct tevent_req *req, *subreq;
+       struct handle_dnsrpcrec_state *state;
+       struct dns_name_question *new_q;
+       bool resolve_cname;
+       WERROR werr;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct handle_dnsrpcrec_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->answers = answers;
+       state->nsrecs = nsrecs;
+
+       resolve_cname = ((rec->wType == DNS_TYPE_CNAME) &&
+                        ((question->question_type == DNS_QTYPE_A) ||
+                         (question->question_type == DNS_QTYPE_AAAA)));
+
+       if (!resolve_cname) {
+               if ((question->question_type != DNS_QTYPE_ALL) &&
+                   (rec->wType !=
+                    (enum dns_record_type) question->question_type)) {
+                       tevent_req_done(req);
+                       return tevent_req_post(req, ev);
+               }
+
+               werr = add_response_rr(question->name, rec, state->answers);
+               if (tevent_req_werror(req, werr)) {
+                       return tevent_req_post(req, ev);
+               }
+
+               tevent_req_done(req);
+               return tevent_req_post(req, ev);
+       }
+
+       werr = add_response_rr(question->name, rec, state->answers);
+       if (tevent_req_werror(req, werr)) {
+               return tevent_req_post(req, ev);
+       }
+
+       new_q = talloc(state, struct dns_name_question);
+       if (tevent_req_nomem(new_q, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       *new_q = (struct dns_name_question) {
+               .question_type = question->question_type,
+               .question_class = question->question_class,
+               .name = rec->data.cname
+       };
+
+       if (dns_authorative_for_zone(dns, new_q->name)) {
+               subreq = handle_authoritative_send(
+                       state, ev, dns, forwarder, new_q,
+                       state->answers, state->nsrecs);
+               if (tevent_req_nomem(subreq, req)) {
+                       return tevent_req_post(req, ev);
+               }
+               tevent_req_set_callback(subreq, handle_dnsrpcrec_gotauth, req);
+               return req;
+       }
+
+       subreq = ask_forwarder_send(state, ev, dns, forwarder, new_q);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, handle_dnsrpcrec_gotforwarded, req);
+
+       return req;
+}
+
+static void handle_dnsrpcrec_gotauth(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       WERROR werr;
+
+       werr = handle_authoritative_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (tevent_req_werror(req, werr)) {
+               return;
+       }
+       tevent_req_done(req);
+}
+
+static void handle_dnsrpcrec_gotforwarded(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct handle_dnsrpcrec_state *state = tevent_req_data(
+               req, struct handle_dnsrpcrec_state);
+       struct dns_res_rec *answers, *nsrecs, *additional;
+       uint16_t ancount = 0;
+       uint16_t nscount = 0;
+       uint16_t arcount = 0;
+       uint16_t i;
+       WERROR werr;
+
+       werr = ask_forwarder_recv(subreq, state, &answers, &ancount,
+                                 &nsrecs, &nscount, &additional, &arcount);
+       if (tevent_req_werror(req, werr)) {
+               return;
+       }
+
+       for (i=0; i<ancount; i++) {
+               werr = add_dns_res_rec(state->answers, &answers[i]);
+               if (tevent_req_werror(req, werr)) {
+                       return;
+               }
+       }
+
+       for (i=0; i<nscount; i++) {
+               werr = add_dns_res_rec(state->nsrecs, &nsrecs[i]);
+               if (tevent_req_werror(req, werr)) {
+                       return;
+               }
+       }
+
+       tevent_req_done(req);
+}
+
+static WERROR handle_dnsrpcrec_recv(struct tevent_req *req)
+{
+       return tevent_req_simple_recv_werror(req);
+}
+
+struct handle_authoritative_state {
+       struct tevent_context *ev;
+       struct dns_server *dns;
+       struct dns_name_question *question;
+       const char *forwarder;
+
+       struct dnsp_DnssrvRpcRecord *recs;
+       uint16_t rec_count;
+       uint16_t recs_done;
+
+       struct dns_res_rec **answers;
+       struct dns_res_rec **nsrecs;
+};
+
+static void handle_authoritative_done(struct tevent_req *subreq);
+
+static struct tevent_req *handle_authoritative_send(
+       TALLOC_CTX *mem_ctx, struct tevent_context *ev,
+       struct dns_server *dns, const char *forwarder,
+       struct dns_name_question *question,
+       struct dns_res_rec **answers, struct dns_res_rec **nsrecs)
+{
+       struct tevent_req *req, *subreq;
+       struct handle_authoritative_state *state;
+       struct ldb_dn *dn = NULL;
+       WERROR werr;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct handle_authoritative_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->ev = ev;
+       state->dns = dns;
+       state->question = question;
+       state->forwarder = forwarder;
+       state->answers = answers;
+       state->nsrecs = nsrecs;
+
+       werr = dns_name2dn(dns, state, question->name, &dn);
+       if (tevent_req_werror(req, werr)) {
+               return tevent_req_post(req, ev);
+       }
+
+       werr = dns_lookup_records(dns, state, dn, &state->recs,
+                                 &state->rec_count);
+       TALLOC_FREE(dn);
+       if (tevent_req_werror(req, werr)) {
+               return tevent_req_post(req, ev);
+       }
+
+       if (state->rec_count == 0) {
+               tevent_req_werror(req, DNS_ERR(NAME_ERROR));
+               return tevent_req_post(req, ev);
+       }
+
+       subreq = handle_dnsrpcrec_send(
+               state, state->ev, state->dns, state->forwarder,
+               state->question, &state->recs[state->recs_done],
+               state->answers, state->nsrecs);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, handle_authoritative_done, req);
+       return req;
+}
+
+static void handle_authoritative_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct handle_authoritative_state *state = tevent_req_data(
+               req, struct handle_authoritative_state);
+       WERROR werr;
+
+       werr = handle_dnsrpcrec_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (tevent_req_werror(req, werr)) {
+               return;
+       }
+
+       state->recs_done += 1;
+
+       if (state->recs_done == state->rec_count) {
+               tevent_req_done(req);
+               return;
+       }
+
+       subreq = handle_dnsrpcrec_send(
+               state, state->ev, state->dns, state->forwarder,
+               state->question, &state->recs[state->recs_done],
+               state->answers, state->nsrecs);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, handle_authoritative_done, req);
+}
+
+static WERROR handle_authoritative_recv(struct tevent_req *req)
+{
+       struct handle_authoritative_state *state = tevent_req_data(
+               req, struct handle_authoritative_state);
+       WERROR werr;
+
+       if (tevent_req_is_werror(req, &werr)) {
+               return werr;
+       }
+
+       werr = add_zone_authority_record(state->dns, state, state->question,
+                                        state->nsrecs);
+       if (!W_ERROR_IS_OK(werr)) {
+               return werr;
+       }
+
+       return WERR_OK;
+}
+
 static NTSTATUS create_tkey(struct dns_server *dns,
                            const char* name,
                            const char* algorithm,
@@ -747,6 +1012,7 @@ struct dns_server_process_query_state {
        uint16_t arcount;
 };
 
+static void dns_server_process_query_got_auth(struct tevent_req *subreq);
 static void dns_server_process_query_got_response(struct tevent_req *subreq);
 
 struct tevent_req *dns_server_process_query_send(
@@ -786,7 +1052,6 @@ struct tevent_req *dns_server_process_query_send(
        }
 
        if (dns_authorative_for_zone(dns, in->questions[0].name)) {
-               WERROR err;
 
                req_state->flags |= DNS_FLAG_AUTHORITATIVE;
 
@@ -804,22 +1069,15 @@ struct tevent_req *dns_server_process_query_send(
                        return tevent_req_post(req, ev);
                }
 
-               err = handle_question(dns, state, &in->questions[0],
-                                     &state->answers, &state->nsrecs);
-
-               if (W_ERROR_EQUAL(err, DNS_ERR(NAME_ERROR))) {
-                       err = WERR_OK;
-               }
-
-               if (tevent_req_werror(req, err)) {
+               subreq = handle_authoritative_send(
+                       state, ev, dns, lpcfg_dns_forwarder(dns->task->lp_ctx),
+                       &in->questions[0], &state->answers, &state->nsrecs);
+               if (tevent_req_nomem(subreq, req)) {
                        return tevent_req_post(req, ev);
                }
-
-               state->ancount = talloc_array_length(state->answers);
-               state->nscount = talloc_array_length(state->nsrecs);
-
-               tevent_req_done(req);
-               return tevent_req_post(req, ev);
+               tevent_req_set_callback(
+                       subreq, dns_server_process_query_got_auth, req);
+               return req;
        }
 
        if ((req_state->flags & DNS_FLAG_RECURSION_DESIRED) &&
@@ -861,6 +1119,26 @@ static void dns_server_process_query_got_response(struct tevent_req *subreq)
        tevent_req_done(req);
 }
 
+static void dns_server_process_query_got_auth(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct dns_server_process_query_state *state = tevent_req_data(
+               req, struct dns_server_process_query_state);
+       WERROR werr;
+
+       werr = handle_authoritative_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (tevent_req_werror(req, werr)) {
+               return;
+       }
+       state->ancount = talloc_array_length(state->answers);
+       state->nscount = talloc_array_length(state->nsrecs);
+       state->arcount = talloc_array_length(state->additional);
+
+       tevent_req_done(req);
+}
+
 WERROR dns_server_process_query_recv(
        struct tevent_req *req, TALLOC_CTX *mem_ctx,
        struct dns_res_rec **answers,    uint16_t *ancount,