lib: addns: Add code for asynchronously looking up A records.
authorJeremy Allison <jra@samba.org>
Fri, 17 Jul 2020 21:21:09 +0000 (14:21 -0700)
committerAndreas Schneider <asn@cryptomilk.org>
Fri, 7 Aug 2020 06:34:36 +0000 (06:34 +0000)
Returns an array of struct samba_sockaddr.

Signed-off-by: Jeremy Allison <jra@samba.org>
Reviewed-by: Andreas Schneider <asn@samba.org>
lib/addns/dnsquery.c
lib/addns/dnsquery.h

index 90e4de9a0534a19a47cec779d1ed7bea018b148f..77bf1b2e86dc54a2eb4f815f71f821feb70894e3 100644 (file)
@@ -371,6 +371,208 @@ fail:
        return status;
 }
 
+/*********************************************************************
+ Async A record lookup.
+*********************************************************************/
+
+struct ads_dns_lookup_a_state {
+       uint8_t rcode;
+       size_t num_names;
+       char **hostnames;
+       struct samba_sockaddr *addrs;
+};
+
+static void ads_dns_lookup_a_done(struct tevent_req *subreq);
+
+struct tevent_req *ads_dns_lookup_a_send(TALLOC_CTX *mem_ctx,
+                                        struct tevent_context *ev,
+                                        const char *name)
+{
+       struct tevent_req *req = NULL, *subreq = NULL;
+       struct ads_dns_lookup_a_state *state = NULL;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct ads_dns_lookup_a_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       subreq = dns_lookup_send(
+               state,
+               ev,
+               NULL,
+               name,
+               DNS_QCLASS_IN,
+               DNS_QTYPE_A);
+
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, ads_dns_lookup_a_done, req);
+       return req;
+}
+
+static void ads_dns_lookup_a_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct ads_dns_lookup_a_state *state = tevent_req_data(
+               req, struct ads_dns_lookup_a_state);
+       int ret;
+       struct dns_name_packet *reply = NULL;
+       uint16_t i;
+
+       ret = dns_lookup_recv(subreq, state, &reply);
+       TALLOC_FREE(subreq);
+       if (ret != 0) {
+               tevent_req_nterror(req, map_nt_error_from_unix_common(ret));
+               return;
+       }
+
+       state->rcode = (reply->operation & DNS_RCODE);
+       if (state->rcode != DNS_RCODE_OK) {
+               /* Don't bother looking for answers. */
+               tevent_req_done(req);
+               return;
+       }
+
+       /*
+        * We don't care about CNAME answers here. We're
+        * just wanting an async name -> IPv4 lookup.
+        */
+       for (i = 0; i < reply->ancount; i++) {
+               if (reply->answers[i].rr_type == DNS_QTYPE_A) {
+                       state->num_names += 1;
+               }
+       }
+
+       state->hostnames = talloc_zero_array(state,
+                                            char *,
+                                            state->num_names);
+       if (tevent_req_nomem(state->hostnames, req)) {
+               return;
+       }
+       state->addrs = talloc_zero_array(state,
+                                        struct samba_sockaddr,
+                                        state->num_names);
+       if (tevent_req_nomem(state->addrs, req)) {
+               return;
+       }
+
+       state->num_names = 0;
+
+       for (i = 0; i < reply->ancount; i++) {
+               bool ok;
+               struct sockaddr_storage ss = {0};
+               struct dns_res_rec *an = &reply->answers[i];
+
+               if (an->rr_type != DNS_QTYPE_A) {
+                       continue;
+               }
+               if (an->name == NULL) {
+                       /* Can this happen? */
+                       continue;
+               }
+               if (an->rdata.ipv4_record == NULL) {
+                       /* Can this happen? */
+                       continue;
+               }
+               ok = dns_res_rec_get_sockaddr(an,
+                                             &ss);
+               if (!ok) {
+                       continue;
+               }
+               if (is_zero_addr(&ss)) {
+                       continue;
+               }
+               state->addrs[state->num_names].u.ss = ss;
+               state->addrs[state->num_names].sa_socklen =
+                                       sizeof(struct sockaddr_in);
+               state->hostnames[state->num_names] = talloc_strdup(
+                                                       state->hostnames,
+                                                       an->name);
+               if (tevent_req_nomem(state->hostnames[state->num_names], req)) {
+                       return;
+               }
+               state->num_names += 1;
+       }
+
+       tevent_req_done(req);
+}
+
+NTSTATUS ads_dns_lookup_a_recv(struct tevent_req *req,
+                              TALLOC_CTX *mem_ctx,
+                              uint8_t *rcode_out,
+                              size_t *num_names_out,
+                              char ***hostnames_out,
+                              struct samba_sockaddr **addrs_out)
+{
+       struct ads_dns_lookup_a_state *state = tevent_req_data(
+               req, struct ads_dns_lookup_a_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               return status;
+       }
+       if (rcode_out != NULL) {
+               /*
+                * If we got no names, an upper layer may
+                * want to print a debug message.
+                */
+               *rcode_out = state->rcode;
+       }
+       if (hostnames_out != NULL) {
+               *hostnames_out = talloc_move(mem_ctx,
+                                            &state->hostnames);
+       }
+       if (addrs_out != NULL) {
+               *addrs_out = talloc_move(mem_ctx,
+                                        &state->addrs);
+       }
+       *num_names_out = state->num_names;
+       tevent_req_received(req);
+       return NT_STATUS_OK;
+}
+
+/*********************************************************************
+ Simple wrapper for a DNS A query
+*********************************************************************/
+
+NTSTATUS ads_dns_lookup_a(TALLOC_CTX *ctx,
+                         const char *name_in,
+                         size_t *num_names_out,
+                         char ***hostnames_out,
+                         struct samba_sockaddr **addrs_out)
+{
+       struct tevent_context *ev;
+       struct tevent_req *req;
+       NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+       ev = samba_tevent_context_init(ctx);
+       if (ev == NULL) {
+               goto fail;
+       }
+       req = ads_dns_lookup_a_send(ev, ev, name_in);
+       if (req == NULL) {
+               goto fail;
+       }
+       if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+               goto fail;
+       }
+       /*
+        * Sychronous doesn't need to care about the rcode or
+        * a copy of the name_in.
+        */
+       status = ads_dns_lookup_a_recv(req,
+                                      ctx,
+                                      NULL,
+                                      num_names_out,
+                                      hostnames_out,
+                                      addrs_out);
+fail:
+       TALLOC_FREE(ev);
+       return status;
+}
 
 /********************************************************************
  Query with optional sitename.
index bb691f5d55a6ec6ffbfda55974c3fa08cf148106..6083852e9834890f66fdb7de9e86bef97760c114 100644 (file)
@@ -48,6 +48,21 @@ NTSTATUS ads_dns_lookup_ns(TALLOC_CTX *ctx,
                                const char *dnsdomain,
                                struct dns_rr_ns **nslist,
                                int *numns);
+struct tevent_req *ads_dns_lookup_a_send(TALLOC_CTX *mem_ctx,
+                               struct tevent_context *ev,
+                               const char *name);
+NTSTATUS ads_dns_lookup_a_recv(struct tevent_req *req,
+                               TALLOC_CTX *mem_ctx,
+                               uint8_t *rcode_out,
+                               size_t *num_names_out,
+                               char ***hostnames_out,
+                               struct samba_sockaddr **addrs_out);
+NTSTATUS ads_dns_lookup_a(TALLOC_CTX *ctx,
+                       const char *name_in,
+                       size_t *num_names_out,
+                       char ***hostnames_out,
+                       struct samba_sockaddr **addrs_out);
+
 NTSTATUS ads_dns_query_dcs(TALLOC_CTX *ctx,
                           const char *realm,
                           const char *sitename,