]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
s3: libsmb: Make discover_dc_dns() use async DNS.
authorJeremy Allison <jra@samba.org>
Wed, 22 Jul 2020 05:09:27 +0000 (22:09 -0700)
committerAndreas Schneider <asn@cryptomilk.org>
Fri, 7 Aug 2020 06:34:37 +0000 (06:34 +0000)
Change to call dns_lookup_list_async(). This is
doing the samba SRV lookup followed by A and AAAA
record host lookup as resolve_ads() does and so
benefits from the same changes to make it async.

Signed-off-by: Jeremy Allison <jra@samba.org>
Reviewed-by: Andreas Schneider <asn@samba.org>
source3/libsmb/dsgetdcname.c

index 06119471c2b5d2a593e8443512e57b0bf1092c36..7dd3b3dc0b08de446af79b8f31dd276ca81f696d 100644 (file)
@@ -504,14 +504,17 @@ static NTSTATUS discover_dc_dns(TALLOC_CTX *mem_ctx,
                                struct ip_service_name **returned_dclist,
                                int *return_count)
 {
-       int i;
-       size_t j;
+       size_t i;
        NTSTATUS status;
        struct dns_rr_srv *dcs = NULL;
        int numdcs = 0;
-       int numaddrs = 0;
        struct ip_service_name *dclist = NULL;
-       int count = 0;
+       size_t ret_count = 0;
+       size_t num_dns_lookups = 0;
+       const char **dns_lookups = NULL;
+       size_t num_lookups_ret = 0;
+       struct sockaddr_storage *dns_addrs_ret = NULL;
+       char **dns_lookups_ret = NULL;
 
        if (flags & DS_PDC_REQUIRED) {
                status = ads_dns_query_pdc(mem_ctx,
@@ -554,74 +557,204 @@ static NTSTATUS discover_dc_dns(TALLOC_CTX *mem_ctx,
        }
 
        if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(dcs);
                return status;
        }
 
-       if (numdcs == 0) {
+       /* Wrap protect. */
+       if (numdcs < 0) {
+               TALLOC_FREE(dcs);
                return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
        }
 
-       for (i=0;i<numdcs;i++) {
-               numaddrs += MAX(dcs[i].num_ips,1);
+       if (numdcs == 0) {
+               TALLOC_FREE(dcs);
+               return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
        }
 
+       /*
+        * We're only returning one address per
+        * DC name, so just allocate size numdcs.
+        */
+
        dclist = talloc_zero_array(mem_ctx,
                                   struct ip_service_name,
-                                  numaddrs);
+                                  numdcs);
        if (!dclist) {
+               TALLOC_FREE(dcs);
                return NT_STATUS_NO_MEMORY;
        }
 
-       /* now unroll the list of IP addresses */
-
-       *return_count = 0;
-       i = 0;
-       j = 0;
-
-       while ((i < numdcs) && (count < numaddrs)) {
-
-               struct ip_service_name *r = &dclist[count];
+       /*
+        * First, copy the SRV record replies that
+        * have IP addresses returned with them.
+        */
+       ret_count = 0;
+       for (i = 0; i < numdcs; i++) {
+               size_t j;
+
+               if (dcs[i].num_ips == 0) {
+                       /*
+                        * No addresses returned in the SRV
+                        * reply, we must look this up via
+                        * DNS.
+                        */
+                       num_dns_lookups++;
+                       continue;
+               }
 
-               r->hostname = dcs[i].hostname;
+               dclist[ret_count].hostname =
+                       talloc_move(mem_ctx, &dcs[i].hostname);
+
+               /*
+                * Pick the first IPv4 address,
+                * if none pick the first address.
+                *
+                * This is different from the previous
+                * code which picked a 'next ip' address
+                * each time, incrementing an index.
+                * Too complex to maintain :-(.
+                */
+               for (j = 0; j < dcs[i].num_ips; j++) {
+                       if (dcs[i].ss_s[j].ss_family == AF_INET) {
+                               dclist[ret_count].ss = dcs[i].ss_s[j];
+                               break;
+                       }
+               }
+               if (j == dcs[i].num_ips) {
+                       /* No IPv4- use the first IPv6 addr. */
+                       dclist[ret_count].ss = dcs[i].ss_s[0];
+               }
+               ret_count++;
+       }
+
+       /*
+        * Now, create an array of hostnames we
+        * will asynchronously look up in DNS.
+        */
+       dns_lookups = talloc_zero_array(mem_ctx,
+                                       const char *,
+                                       num_dns_lookups);
+       if (dns_lookups == NULL) {
+               TALLOC_FREE(dcs);
+               TALLOC_FREE(dclist);
+               return NT_STATUS_NO_MEMORY;
+       }
 
-               /* If we don't have an IP list for a name, lookup it up */
+       num_dns_lookups = 0;
+       for (i = 0; i < numdcs; i++) {
+               if (dcs[i].num_ips != 0) {
+                       /*
+                        * We already got an address
+                        * for this via SRV return.
+                        */
+                       continue;
+               }
+               dns_lookups[num_dns_lookups] =
+                       talloc_strdup(mem_ctx, dcs[i].hostname);
+               if (dns_lookups[num_dns_lookups] == NULL) {
+                       TALLOC_FREE(dcs);
+                       TALLOC_FREE(dclist);
+                       TALLOC_FREE(dns_lookups);
+               }
+               num_dns_lookups++;
+       }
 
-               if (!dcs[i].ss_s) {
-                       interpret_string_addr_prefer_ipv4(&r->ss,
-                                               dcs[i].hostname, 0);
-                       i++;
-                       j = 0;
-               } else {
-                       /* use the IP addresses from the SRV response */
+       status = dns_lookup_list_async(mem_ctx,
+                                      num_dns_lookups,
+                                      dns_lookups,
+                                      &num_lookups_ret,
+                                      &dns_addrs_ret,
+                                      &dns_lookups_ret);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(dcs);
+               TALLOC_FREE(dclist);
+               TALLOC_FREE(dns_lookups);
+               return status;
+       }
 
-                       if (j >= dcs[i].num_ips) {
-                               i++;
-                               j = 0;
+       /*
+        * Remember, if we timed out or some
+        * addresses didn't look up then
+        * num_dns_lookups > num_lookups_ret,
+        * so there isn't a one to one correspondence.
+        *
+        * The order of the requests is preserved
+        * in the replies, but there may be requests
+        * for which there are no replies if we
+        * timed out.
+        *
+        * For example this means for looking up
+        * names:
+        *     NAME1
+        *     NAME2
+        *     NAME3
+        *
+        * The replies might look like:
+        *     NAME1 -> IPv4
+        *     NAME1 -> IPv4
+        *     NAME1 -> IPv6
+        *     NAME1 -> IPv6
+        *     NAME3 -> IPv6
+        *
+        * But they will never be in the order:
+        *
+        *     NAME2
+        *     NAME3
+        *     NAME1
+        *
+        * Also in dns_lookup_list_async()
+        * we arrange the requests/replies in
+        * the order IPv4 followed by IPv6, so in
+        * the replies, we can always pick the first
+        * reply - we know it will be IPv4 if there
+        * is an IPv4 for that name.
+        *
+        * Here ret_count is the index into the
+        * next entry in dclist we must fill (may
+        * be zero).
+        */
+
+       for (i = 0; i < num_lookups_ret; i++) {
+               dclist[ret_count].hostname =
+                       talloc_move(mem_ctx, &dns_lookups_ret[i]);
+               dclist[ret_count].ss = dns_addrs_ret[i];
+               /*
+                * Is this a duplicate name return.
+                * Remember we can look up both A and
+                * AAAA records which can return multiple
+                * addresses and the order of names
+                * is preserved.
+                */
+               if (ret_count > 0) {
+                       if (strcmp(dclist[ret_count-1].hostname,
+                                  dclist[ret_count].hostname) == 0) {
+                               /*
+                                * Same name. Keep the first address
+                                * which is IPv4 by preference.
+                                */
                                continue;
                        }
-
-                       r->ss = dcs[i].ss_s[j];
-                       j++;
                }
+               ret_count++;
 
-               /* make sure it is a valid IP.  I considered checking the
-                * negative connection cache, but this is the wrong place for
-                * it.  Maybe only as a hack. After think about it, if all of
-                * the IP addresses returned from DNS are dead, what hope does a
-                * netbios name lookup have?  The standard reason for falling
-                * back to netbios lookups is that our DNS server doesn't know
-                * anything about the DC's   -- jerry */
-
-               if (!is_zero_addr(&r->ss)) {
-                       count++;
-                       continue;
+               if (ret_count == numdcs) {
+                       /* We've filled the return array. */
+                       break;
                }
        }
 
        *returned_dclist = dclist;
-       *return_count = count;
+       *return_count = (int)ret_count;
+
+       if (*return_count < 0) {
+               TALLOC_FREE(dcs);
+               TALLOC_FREE(dclist);
+               TALLOC_FREE(dns_lookups);
+               return NT_STATUS_INTERNAL_ERROR;
+       }
 
-       if (count > 0) {
+       if (ret_count > 0) {
                return NT_STATUS_OK;
        }