From d882add695bbd5ea03961c08ac2c0380fbf2947b Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 21 Jul 2020 22:09:27 -0700 Subject: [PATCH] s3: libsmb: Make discover_dc_dns() use async DNS. 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 Reviewed-by: Andreas Schneider --- source3/libsmb/dsgetdcname.c | 223 ++++++++++++++++++++++++++++------- 1 file changed, 178 insertions(+), 45 deletions(-) diff --git a/source3/libsmb/dsgetdcname.c b/source3/libsmb/dsgetdcname.c index 06119471c2b..7dd3b3dc0b0 100644 --- a/source3/libsmb/dsgetdcname.c +++ b/source3/libsmb/dsgetdcname.c @@ -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;ihostname = 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; } -- 2.47.3