Tested and can confirm that all queries go to site local DC when said DC is properly configured, e.g. NetLogon running. Great feature! On Wed, Apr 3, 2024 at 1:25 AM David Voit <david.voit@xxxxxxxxx> wrote: > > For domain based DFS we always need to contact the domain controllers. > On setups, which are using bigger AD installations you could get random dc on the other side of the world, > if you don't have site support. This can lead to network timeouts and other problems. > > CLDAP-Ping uses ASN.1 + UDP (CLDAP) and custom-DCE encoding including DName compressions without > field separation. Finally after finding the sitename we now need to do a DNS SRV lookups to find > the correct IPs to our closest site and fill up the remaining IPs from the global list. > > Signed-off-by: David Voit <david.voit@xxxxxxxxx> > --- > Makefile.am | 15 ++- > cldap_ping.c | 346 +++++++++++++++++++++++++++++++++++++++++++++++++ > cldap_ping.h | 14 ++ > mount.cifs.c | 5 +- > resolve_host.c | 258 +++++++++++++++++++++++++++++++----- > resolve_host.h | 6 +- > 6 files changed, 606 insertions(+), 38 deletions(-) > create mode 100644 cldap_ping.c > create mode 100644 cldap_ping.h > > diff --git a/Makefile.am b/Makefile.am > index 1a22266..7877823 100644 > --- a/Makefile.am > +++ b/Makefile.am > @@ -3,8 +3,8 @@ ACLOCAL_AMFLAGS = -I aclocal > > root_exec_sbindir = $(ROOTSBINDIR) > root_exec_sbin_PROGRAMS = mount.cifs > -mount_cifs_SOURCES = mount.cifs.c mtab.c resolve_host.c util.c > -mount_cifs_LDADD = $(LIBCAP) $(CAPNG_LDADD) $(RT_LDADD) > +mount_cifs_SOURCES = mount.cifs.c mtab.c $(resolve_hosts_SOURCES) util.c > +mount_cifs_LDADD = $(LIBCAP) $(CAPNG_LDADD) $(RT_LDADD) $(resolve_hosts_LDADD) > include_HEADERS = cifsidmap.h > rst_man_pages = mount.cifs.8 > > @@ -28,6 +28,9 @@ bin_PROGRAMS = > bin_SCRIPTS = > sbin_PROGRAMS = > > +resolve_hosts_SOURCES = data_blob.c asn1.c cldap_ping.c resolve_host.c > +resolve_hosts_LDADD = -ltalloc -lresolv > + > if CONFIG_CIFSUPCALL > sbin_PROGRAMS += cifs.upcall > cifs_upcall_SOURCES = cifs.upcall.c data_blob.c asn1.c spnego.c > @@ -43,8 +46,8 @@ endif > > if CONFIG_CIFSCREDS > bin_PROGRAMS += cifscreds > -cifscreds_SOURCES = cifscreds.c cifskey.c resolve_host.c util.c > -cifscreds_LDADD = -lkeyutils > +cifscreds_SOURCES = cifscreds.c cifskey.c $(resolve_hosts_SOURCES) util.c > +cifscreds_LDADD = -lkeyutils $(resolve_hosts_LDADD) > > rst_man_pages += cifscreds.1 > > @@ -105,8 +108,8 @@ endif > if CONFIG_PAM > pam_PROGRAMS = pam_cifscreds.so > rst_man_pages += pam_cifscreds.8 > -pam_cifscreds.so: pam_cifscreds.c cifskey.c resolve_host.c util.c > - $(CC) $(DEFS) $(CFLAGS) $(AM_CFLAGS) $(LDFLAGS) -shared -fpic -o $@ $+ -lpam -lkeyutils > +pam_cifscreds.so: pam_cifscreds.c cifskey.c $(resolve_hosts_SOURCES) util.c > + $(CC) $(DEFS) $(CFLAGS) $(AM_CFLAGS) $(LDFLAGS) -shared -fpic -o $@ $+ -lpam -lkeyutils $(resolve_hosts_LDADD) > > endif > > diff --git a/cldap_ping.c b/cldap_ping.c > new file mode 100644 > index 0000000..725ba2c > --- /dev/null > +++ b/cldap_ping.c > @@ -0,0 +1,346 @@ > +/* > + * CLDAP Ping to find closest ClientSiteName > + * > + * Copyright (C) 2024 David Voit (david.voit@xxxxxxxxx) > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 3 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program. If not, see <http://www.gnu.org/licenses/>. > + */ > + > +#include <talloc.h> > +#include <string.h> > +#include <sys/socket.h> > +#include <arpa/inet.h> > +#include <unistd.h> > +#include <resolv.h> > +#include <stdbool.h> > +#include "data_blob.h" > +#include "asn1.h" > +#include "cldap_ping.h" > + > +#define LDAP_DNS_DOMAIN "DnsDomain" > +#define LDAP_DNS_DOMAIN_LEN strlen(LDAP_DNS_DOMAIN) > +#define LDAP_NT_VERSION "NtVer" > +#define LDAP_NT_VERSION_LEN strlen(LDAP_NT_VERSION) > +#define LDAP_ATTRIBUTE_NETLOGON "NetLogon" > +#define LDAP_ATTRIBUTE_NETLOGON_LEN strlen(LDAP_ATTRIBUTE_NETLOGON) > + > + > +// Parse a ASN.1 BER tag size-field, returns start of payload of tag > +char *parse_ber_size(char *buf, size_t *tag_size) { > + size_t size = *buf & 0xff; > + char *ret = (buf + 1); > + if (size >= 0x81) { > + switch (size) { > + case 0x81: > + size = *ret & 0xff; > + ret += 1; > + break; > + case 0x82: > + size = (*ret << 8) | (*(ret + 1) & 0xff); > + ret += 2; > + break; > + case 0x83: > + size = (*ret << 16) | (*(ret + 1) << 8) | (*(ret + 2) & 0xff); > + ret += 3; > + break; > + case 0x84: > + size = (*ret << 24) | (*(ret + 1) << 16) | (*(ret + 2) << 8) | (*(ret + 3) & 0xff); > + ret += 4; > + break; > + default: > + return NULL; > + } > + } > + > + *tag_size = size; > + return ret; > +} > + > +// simple wrapper over dn_expand which also calculates the new offset for the next compressed dn > +int read_dns_string(char *buf, size_t buf_size, char *dest, size_t dest_size, size_t *offset) { > + int compressed_length = dn_expand((u_char *)buf, (u_char *)buf+buf_size, (u_char *)buf + *offset, dest, (int)dest_size); > + if (compressed_length < 0) { > + return -1; > + } > + > + *offset = *offset+compressed_length; > + > + return 0; > +} > + > +// LDAP request for: (&(DnsDomain=DOMAIN_HERE)(NtVer=\\06\\00\\00\\00)) > +ASN1_DATA *generate_cldap_query(char *domain) { > + ASN1_DATA *data; > + TALLOC_CTX *mem_ctx = talloc_init("cldap"); > + > + data = asn1_init(mem_ctx); > + asn1_push_tag(data, ASN1_SEQUENCE(0)); > + > + // Message id > + asn1_push_tag(data, ASN1_INTEGER); > + asn1_write_uint8(data, 1); > + asn1_pop_tag(data); > + > + // SearchRequest > + asn1_push_tag(data, ASN1_APPLICATION(3)); > + > + // empty baseObject > + asn1_push_tag(data, ASN1_OCTET_STRING); > + asn1_pop_tag(data); > + > + // scope 0 = baseObject > + asn1_push_tag(data, ASN1_ENUMERATED); > + asn1_write_uint8(data, 0); > + asn1_pop_tag(data); > + > + // derefAliasses 0=neverDerefAlias > + asn1_push_tag(data, ASN1_ENUMERATED); > + asn1_write_uint8(data, 0); > + asn1_pop_tag(data); > + > + // sizeLimit > + asn1_push_tag(data, ASN1_INTEGER); > + asn1_write_uint8(data, 0); > + asn1_pop_tag(data); > + > + // timeLimit > + asn1_push_tag(data, ASN1_INTEGER); > + asn1_write_uint8(data, 0); > + asn1_pop_tag(data); > + > + // typesOnly > + asn1_push_tag(data, ASN1_BOOLEAN); > + asn1_write_uint8(data, 0); > + asn1_pop_tag(data); > + > + // AND > + asn1_push_tag(data, ASN1_CONTEXT(0)); > + // equalityMatch > + asn1_push_tag(data, ASN1_CONTEXT(3)); > + asn1_write_OctetString(data, LDAP_DNS_DOMAIN, LDAP_DNS_DOMAIN_LEN); > + asn1_write_OctetString(data, domain, strlen(domain)); > + asn1_pop_tag(data); > + > + // equalityMatch > + asn1_push_tag(data, ASN1_CONTEXT(3)); > + asn1_write_OctetString(data, LDAP_NT_VERSION, LDAP_NT_VERSION_LEN); > + // Bitmask NETLOGON_NT_VERSION_5 & NETLOGON_NT_VERSION_5EX -> To get NETLOGON_SAM_LOGON_RESPONSE_EX as response > + asn1_write_OctetString(data, "\x06\x00\x00\x00", 4); > + asn1_pop_tag(data); > + > + // End AND > + asn1_pop_tag(data); > + > + asn1_push_tag(data, ASN1_SEQUENCE(0)); > + asn1_write_OctetString(data, LDAP_ATTRIBUTE_NETLOGON, LDAP_ATTRIBUTE_NETLOGON_LEN); > + asn1_pop_tag(data); > + > + // End SearchRequest > + asn1_pop_tag(data); > + // End Sequence > + asn1_pop_tag(data); > + > + return data; > +} > + > +// Input is a cldap response, output is a pointer to the NETLOGON_SAM_LOGON_RESPONSE_EX payload > +ssize_t extract_netlogon_section(char *buffer, size_t buffer_size, char **netlogon_payload) { > + size_t ber_size; > + size_t netlogon_payload_size; > + // Not enough space to read initial sequence - not an correct cldap response > + if (buffer_size < 7) { > + return CLDAP_PING_PARSE_ERROR_LDAP; > + } > + > + // Sequence tag > + if (*buffer != 0x30) { > + return CLDAP_PING_PARSE_ERROR_LDAP; > + } > + > + char *message_id_tag = parse_ber_size(buffer + 1, &ber_size); > + > + if (ber_size > buffer_size) { > + return CLDAP_PING_PARSE_ERROR_LDAP; > + } > + > + if (*message_id_tag != 0x02) { > + return CLDAP_PING_PARSE_ERROR_LDAP; > + } > + > + char *message_id = parse_ber_size(message_id_tag + 1, &ber_size); > + > + if (ber_size != 1 || *message_id != 1) { > + return CLDAP_PING_PARSE_ERROR_LDAP; > + } > + > + // SearchResultEntry > + if (*(message_id+1) != 0x64) { > + return CLDAP_PING_PARSE_ERROR_LDAP; > + } > + > + char *object_name_tag = parse_ber_size(message_id+2, &ber_size); > + if (object_name_tag == NULL) { > + return CLDAP_PING_PARSE_ERROR_LDAP; > + } > + > + char *object_name = parse_ber_size(object_name_tag+1, &ber_size); > + if (object_name == NULL) { > + return CLDAP_PING_PARSE_ERROR_LDAP; > + } > + > + if (*object_name_tag != 4 || ber_size != 0) { > + return CLDAP_PING_PARSE_ERROR_LDAP; > + } > + > + char *partial_attribute_list_tag = parse_ber_size(object_name+1, &ber_size); > + if (partial_attribute_list_tag == NULL) { > + return CLDAP_PING_PARSE_ERROR_LDAP; > + } > + > + if (*partial_attribute_list_tag != 0x30) { > + return CLDAP_PING_PARSE_ERROR_LDAP; > + } > + > + > + char *partial_attribute_tag = parse_ber_size(partial_attribute_list_tag+1, &ber_size); > + if (partial_attribute_tag == NULL) { > + return CLDAP_PING_PARSE_ERROR_LDAP; > + } > + > + char *attribute_name = parse_ber_size(partial_attribute_tag+1, &ber_size); > + if (attribute_name == NULL) { > + return CLDAP_PING_PARSE_ERROR_LDAP; > + } > + > + if (ber_size != LDAP_ATTRIBUTE_NETLOGON_LEN) { > + return CLDAP_PING_PARSE_ERROR_LDAP; > + } > + > + if (strncasecmp(LDAP_ATTRIBUTE_NETLOGON, attribute_name, LDAP_ATTRIBUTE_NETLOGON_LEN) != 0) { > + return CLDAP_PING_PARSE_ERROR_LDAP; > + } > + > + // SET > + if (*(attribute_name+LDAP_ATTRIBUTE_NETLOGON_LEN) != 0x31) { > + return CLDAP_PING_PARSE_ERROR_LDAP; > + } > + > + char *start_of_data = parse_ber_size(attribute_name+LDAP_ATTRIBUTE_NETLOGON_LEN+1, &ber_size); > + if (start_of_data == NULL) { > + return CLDAP_PING_PARSE_ERROR_LDAP; > + } > + > + // octat-string of NetLogon data > + if (*start_of_data != '\x04') { > + return CLDAP_PING_PARSE_ERROR_LDAP; > + } > + > + *netlogon_payload = parse_ber_size(start_of_data + 1, &netlogon_payload_size); > + > + if (*netlogon_payload == NULL) { > + *netlogon_payload = NULL; > + return CLDAP_PING_PARSE_ERROR_LDAP; > + } > + > + return (ssize_t)netlogon_payload_size; > +} > + > +int netlogon_get_client_site(char *netlogon_response, size_t netlogon_size, char *sitename) { > + // 24 mandatory bytes > + if (netlogon_size < 25) { > + return CLDAP_PING_PARSE_ERROR_NETLOGON; > + } > + > + // LOGON_SAM_PAUSE_RESPONSE_EX -> Netlogon service is not in-sync try next dc instead > + if (*netlogon_response == 0x18 && *(netlogon_response + 1) == 0x00) { > + return CLDAP_PING_TRYNEXT; > + } > + > + // NETLOGON_SAM_LOGON_RESPONSE_EX Opcode: 0x17 > + if (*netlogon_response != 0x17 || *(netlogon_response + 1) != 0x00) { > + return CLDAP_PING_PARSE_ERROR_NETLOGON; > + } > + > + // skip over sbz, ds_flags and domain_guid > + // and start directly at variable string portion of NETLOGON_SAM_LOGON_RESPONSE_EX > + size_t offset = 24; > + > + for (int i=0; i < 8; i++) { > + // iterate over DnsForestName, DnsDomainName, NetbiosDomainName, NetbiosComputerName, UserName, DcSiteName > + // to finally get to our desired ClientSiteName field > + if (read_dns_string(netlogon_response, netlogon_size, sitename, MAXCDNAME, &offset) < 0) { > + return CLDAP_PING_PARSE_ERROR_NETLOGON; > + } > + } > + > + return 0; > +} > + > +int cldap_ping(char *domain, sa_family_t family, void *addr, char *site_name) { > + char buffer[8196]; > + ssize_t response_size; > + char *netlogon_response; > + ssize_t netlogon_size; > + struct sockaddr_storage socketaddr; > + size_t addr_size; > + int sock = socket(family, SOCK_DGRAM, 0); > + if (sock < 0) { > + return CLDAP_PING_NETWORK_ERROR; > + } > + > + ASN1_DATA *data = generate_cldap_query(domain); > + > + if (family == AF_INET6) { > + addr_size = sizeof(struct sockaddr_in6); > + bzero((void *) &socketaddr, addr_size); > + socketaddr.ss_family = AF_INET6; > + ((struct sockaddr_in6 *)&socketaddr)->sin6_addr = *((struct in6_addr*)addr); > + ((struct sockaddr_in6 *)&socketaddr)->sin6_port = htons(389); > + } else { > + addr_size = sizeof(struct sockaddr_in); > + bzero((void *) &socketaddr, addr_size); > + socketaddr.ss_family = AF_INET; > + ((struct sockaddr_in *)&socketaddr)->sin_addr = *((struct in_addr*)addr); > + ((struct sockaddr_in *)&socketaddr)->sin_port = htons(389); > + } > + > + struct timeval timeout = {.tv_sec = 2, .tv_usec = 0}; > + if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) < 0) { > + return CLDAP_PING_NETWORK_ERROR; > + } > + if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) { > + return CLDAP_PING_NETWORK_ERROR; > + } > + > + if (sendto(sock, data->data, data->length, 0, (struct sockaddr *)&socketaddr, addr_size) < 0) { > + close(sock); > + return CLDAP_PING_TRYNEXT; > + } > + > + asn1_free(data); > + response_size = recv(sock, buffer, sizeof(buffer), 0); > + close(sock); > + > + if (response_size < 0) { > + return CLDAP_PING_TRYNEXT; > + } > + > + netlogon_size = extract_netlogon_section(buffer, response_size, &netlogon_response); > + if (netlogon_size < 0) { > + return (int)netlogon_size; > + } > + > + return netlogon_get_client_site(netlogon_response, netlogon_size, site_name); > +} > + > diff --git a/cldap_ping.h b/cldap_ping.h > new file mode 100644 > index 0000000..9a23e72 > --- /dev/null > +++ b/cldap_ping.h > @@ -0,0 +1,14 @@ > +#ifndef _CLDAP_PING_H_ > +#define _CLDAP_PING_H_ > + > +#define CLDAP_PING_NETWORK_ERROR -1 > +#define CLDAP_PING_TRYNEXT -2 > +#define CLDAP_PING_PARSE_ERROR_LDAP -3 > +#define CLDAP_PING_PARSE_ERROR_NETLOGON -4 > + > +// returns CLDAP_PING_TRYNEXT if you should use another dc > +// any other error code < 0 is a fatal error > +// site_name must be of MAXCDNAME size! > +int cldap_ping(char *domain, sa_family_t family, void *addr, char *site_name); > + > +#endif /* _CLDAP_PING_H_ */ > diff --git a/mount.cifs.c b/mount.cifs.c > index 2278995..3b7a6b3 100644 > --- a/mount.cifs.c > +++ b/mount.cifs.c > @@ -1889,8 +1889,11 @@ assemble_mountinfo(struct parsed_mount_info *parsed_info, > if (rc) > goto assemble_exit; > > - if (parsed_info->addrlist[0] == '\0') > + if (parsed_info->addrlist[0] == '\0') { > rc = resolve_host(parsed_info->host, parsed_info->addrlist); > + if (rc == 0 && parsed_info->verboseflag) > + fprintf(stderr, "Host \"%s\" resolved to the following IP addresses: %s\n", parsed_info->host, parsed_info->addrlist); > + } > > switch (rc) { > case EX_USAGE: > diff --git a/resolve_host.c b/resolve_host.c > index 17cbd10..8c0303f 100644 > --- a/resolve_host.c > +++ b/resolve_host.c > @@ -3,6 +3,7 @@ > * > * Copyright (C) 2010 Jeff Layton (jlayton@xxxxxxxxx) > * Copyright (C) 2010 Igor Druzhinin (jaxbrigs@xxxxxxxxx) > + * Copyright (C) 2024 David Voit (david.voit@xxxxxxxxx) > * > * This program is free software; you can redistribute it and/or modify > * it under the terms of the GNU General Public License as published by > @@ -27,15 +28,16 @@ > #include <sys/socket.h> > #include <arpa/inet.h> > #include <netdb.h> > +#include <resolv.h> > #include "mount.h" > #include "util.h" > +#include "cldap_ping.h" > #include "resolve_host.h" > > /* > * resolve hostname to comma-separated list of address(es) > */ > -int resolve_host(const char *host, char *addrstr) > -{ > +int resolve_host(const char *host, char *addrstr) { > int rc; > /* 10 for max width of decimal scopeid */ > char tmpbuf[NI_MAXHOST + 1 + 10 + 1]; > @@ -44,6 +46,7 @@ int resolve_host(const char *host, char *addrstr) > struct addrinfo *addrlist, *addr; > struct sockaddr_in *sin; > struct sockaddr_in6 *sin6; > + size_t count_v4 = 0, count_v6 = 0; > > rc = getaddrinfo(host, NULL, NULL, &addrlist); > if (rc != 0) > @@ -53,40 +56,51 @@ int resolve_host(const char *host, char *addrstr) > while (addr) { > /* skip non-TCP entries */ > if (addr->ai_socktype != SOCK_STREAM || > - addr->ai_protocol != IPPROTO_TCP) { > + addr->ai_protocol != IPPROTO_TCP) { > addr = addr->ai_next; > continue; > } > > switch (addr->ai_addr->sa_family) { > - case AF_INET6: > - sin6 = (struct sockaddr_in6 *)addr->ai_addr; > - ipaddr = inet_ntop(AF_INET6, &sin6->sin6_addr, tmpbuf, > - sizeof(tmpbuf)); > - if (!ipaddr) { > - rc = EX_SYSERR; > - goto resolve_host_out; > - } > + case AF_INET6: > + count_v6++; > + if (count_v6 + count_v4 > MAX_ADDRESSES) { > + addr = addr->ai_next; > + continue; > + } > > - if (sin6->sin6_scope_id) { > - len = strnlen(tmpbuf, sizeof(tmpbuf)); > - snprintf(tmpbuf + len, sizeof(tmpbuf) - len, "%%%u", > - sin6->sin6_scope_id); > - } > - break; > - case AF_INET: > - sin = (struct sockaddr_in *)addr->ai_addr; > - ipaddr = inet_ntop(AF_INET, &sin->sin_addr, tmpbuf, > - sizeof(tmpbuf)); > - if (!ipaddr) { > - rc = EX_SYSERR; > - goto resolve_host_out; > - } > + sin6 = (struct sockaddr_in6 *) addr->ai_addr; > + ipaddr = inet_ntop(AF_INET6, &sin6->sin6_addr, tmpbuf, > + sizeof(tmpbuf)); > + if (!ipaddr) { > + rc = EX_SYSERR; > + goto resolve_host_out; > + } > > - break; > - default: > - addr = addr->ai_next; > - continue; > + > + if (sin6->sin6_scope_id) { > + len = strnlen(tmpbuf, sizeof(tmpbuf)); > + snprintf(tmpbuf + len, sizeof(tmpbuf) - len, "%%%u", > + sin6->sin6_scope_id); > + } > + break; > + case AF_INET: > + count_v4++; > + if (count_v6 + count_v4 > MAX_ADDRESSES) { > + addr = addr->ai_next; > + continue; > + } > + sin = (struct sockaddr_in *) addr->ai_addr; > + ipaddr = inet_ntop(AF_INET, &sin->sin_addr, tmpbuf, > + sizeof(tmpbuf)); > + if (!ipaddr) { > + rc = EX_SYSERR; > + goto resolve_host_out; > + } > + break; > + default: > + addr = addr->ai_next; > + continue; > } > > if (addr == addrlist) > @@ -98,6 +112,192 @@ int resolve_host(const char *host, char *addrstr) > addr = addr->ai_next; > } > > + > + // Is this a DFS domain where we need to do a cldap ping to find the closest node? > + if (count_v4 > 1 || count_v6 > 1) { > + int res; > + ns_msg global_domain_handle; > + unsigned char global_domain_lookup[4096]; > + ns_msg site_domain_handle; > + unsigned char site_domain_lookup[4096]; > + char dname[MAXCDNAME]; > + int srv_cnt; > + > + res = res_init(); > + if (res != 0) > + goto resolve_host_out; > + > + res = snprintf(dname, MAXCDNAME, "_ldap._tcp.dc._msdcs.%s", host); > + if (res < 0) > + goto resolve_host_out; > + > + res = res_query(dname, C_IN, ns_t_srv, global_domain_lookup, sizeof(global_domain_lookup)); > + if (res < 0) > + goto resolve_host_out; > + > + // res is also the size of the response_buffer > + res = ns_initparse(global_domain_lookup, res, &global_domain_handle); > + if (res < 0) > + goto resolve_host_out; > + > + srv_cnt = ns_msg_count (global_domain_handle, ns_s_an); > + > + // No or just one DC we are done > + if (srv_cnt < 2) > + goto resolve_host_out; > + > + char site_name[MAXCDNAME]; > + // We assume that AD always sends the ip addresses in the addtional data block > + for (int i = 0; i < ns_msg_count(global_domain_handle, ns_s_ar); i++) { > + ns_rr rr; > + res = ns_parserr(&global_domain_handle, ns_s_ar, i, &rr); > + if (res < 0) > + goto resolve_host_out; > + > + switch (ns_rr_type(rr)) { > + case ns_t_aaaa: > + if (ns_rr_rdlen(rr) != NS_IN6ADDRSZ) > + continue; > + res = cldap_ping((char *) host, AF_INET6, (void *)ns_rr_rdata(rr), site_name); > + break; > + case ns_t_a: > + if (ns_rr_rdlen(rr) != NS_INADDRSZ) > + continue; > + res = cldap_ping((char *) host, AF_INET, (void *)ns_rr_rdata(rr), site_name); > + break; > + default: > + continue; > + } > + > + if (res == CLDAP_PING_TRYNEXT) { > + continue; > + } > + > + if (res < 0) { > + goto resolve_host_out; > + } > + > + if (site_name[0] == '\0') { > + goto resolve_host_out; > + } else { > + // site found - leave loop > + break; > + } > + } > + > + res = snprintf(dname, MAXCDNAME, "_ldap._tcp.%s._sites.dc._msdcs.%s", site_name, host); > + if (res < 0) { > + goto resolve_host_out; > + } > + > + res = res_query(dname, C_IN, ns_t_srv, site_domain_lookup, sizeof(site_domain_lookup)); > + if (res < 0) > + goto resolve_host_out; > + > + // res is also the size of the response_buffer > + res = ns_initparse(site_domain_lookup, res, &site_domain_handle); > + if (res < 0) > + goto resolve_host_out; > + > + int number_addresses = 0; > + for (int i = 0; i < ns_msg_count(site_domain_handle, ns_s_ar); i++) { > + if (i > MAX_ADDRESSES) > + break; > + > + ns_rr rr; > + res = ns_parserr(&site_domain_handle, ns_s_ar, i, &rr); > + if (res < 0) > + goto resolve_host_out; > + > + switch (ns_rr_type(rr)) { > + case ns_t_aaaa: > + if (ns_rr_rdlen(rr) != NS_IN6ADDRSZ) > + continue; > + ipaddr = inet_ntop(AF_INET6, ns_rr_rdata(rr), tmpbuf, > + sizeof(tmpbuf)); > + if (!ipaddr) { > + rc = EX_SYSERR; > + goto resolve_host_out; > + } > + break; > + case ns_t_a: > + if (ns_rr_rdlen(rr) != NS_INADDRSZ) > + continue; > + ipaddr = inet_ntop(AF_INET, ns_rr_rdata(rr), tmpbuf, > + sizeof(tmpbuf)); > + if (!ipaddr) { > + rc = EX_SYSERR; > + goto resolve_host_out; > + } > + break; > + default: > + continue; > + } > + > + number_addresses++; > + > + if (i == 0) > + *addrstr = '\0'; > + else > + strlcat(addrstr, ",", MAX_ADDR_LIST_LEN); > + > + strlcat(addrstr, tmpbuf, MAX_ADDR_LIST_LEN); > + } > + > + // Preferred site ips is now the first entry in addrstr, fill up with other sites till MAX_ADDRESS > + for (int i = 0; i < ns_msg_count(global_domain_handle, ns_s_ar); i++) { > + if (number_addresses > MAX_ADDRESSES) > + break; > + > + ns_rr rr; > + res = ns_parserr(&global_domain_handle, ns_s_ar, i, &rr); > + if (res < 0) > + goto resolve_host_out; > + > + switch (ns_rr_type(rr)) { > + case ns_t_aaaa: > + if (ns_rr_rdlen(rr) != NS_IN6ADDRSZ) > + continue; > + ipaddr = inet_ntop(AF_INET6, ns_rr_rdata(rr), tmpbuf, > + sizeof(tmpbuf)); > + if (!ipaddr) { > + rc = EX_SYSERR; > + goto resolve_host_out; > + } > + break; > + case ns_t_a: > + if (ns_rr_rdlen(rr) != NS_INADDRSZ) > + continue; > + ipaddr = inet_ntop(AF_INET, ns_rr_rdata(rr), tmpbuf, > + sizeof(tmpbuf)); > + if (!ipaddr) { > + rc = EX_SYSERR; > + goto resolve_host_out; > + } > + break; > + default: > + continue; > + } > + > + char *found = strstr(addrstr, tmpbuf); > + > + if (found) { > + // We only have a real match if the substring is between ',' or it's the last/first entry in the list > + char previous_seperator = found > addrstr ? *(found-1) : '\0'; > + char next_seperator = *(found+strlen(tmpbuf)); > + > + if ((next_seperator == ',' || next_seperator == '\0') > + && (previous_seperator == ',' || previous_seperator == '\0')) { > + continue; > + } > + } > + > + number_addresses++; > + strlcat(addrstr, ",", MAX_ADDR_LIST_LEN); > + strlcat(addrstr, tmpbuf, MAX_ADDR_LIST_LEN); > + } > + } > + > resolve_host_out: > freeaddrinfo(addrlist); > return rc; > diff --git a/resolve_host.h b/resolve_host.h > index b949245..f2b19e6 100644 > --- a/resolve_host.h > +++ b/resolve_host.h > @@ -26,8 +26,10 @@ > /* currently maximum length of IPv6 address string */ > #define MAX_ADDRESS_LEN INET6_ADDRSTRLEN > > -/* limit list of addresses to 16 max-size addrs */ > -#define MAX_ADDR_LIST_LEN ((MAX_ADDRESS_LEN + 1) * 16) > +#define MAX_ADDRESSES 16 > + > +/* limit list of addresses to MAX_ADDRESSES max-size addrs */ > +#define MAX_ADDR_LIST_LEN ((MAX_ADDRESS_LEN + 1) * MAX_ADDRESSES) > > extern int resolve_host(const char *host, char *addrstr); > > -- > 2.44.0 > >