linux-cifs.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v4 0/1] cifs-utils: CLDAP-Ping to find the correct site
@ 2024-04-03  5:24 David Voit
  2024-04-03  5:24 ` [PATCH v4 1/1] Implement CLDAP Ping to find the closest site David Voit
  0 siblings, 1 reply; 9+ messages in thread
From: David Voit @ 2024-04-03  5:24 UTC (permalink / raw)
  To: linux-cifs; +Cc: David Voit

Hi all,

after some off-list discussing and debugging the code now also
handles out-of-sync dc nodes. Other changes are minor style changes.
Also the error codes are now named and should show in which section the
error occured.

You can see a diff between the versions on my github repo:
https://github.com/davidvoit/cifs-utils/compare/linux-cifs-v3..linux-cifs-v4

Kind Regards,
David

David Voit (1):
  Implement CLDAP Ping to find the closest site

 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

-- 
2.44.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v4 1/1] Implement CLDAP Ping to find the closest site
  2024-04-03  5:24 [PATCH v4 0/1] cifs-utils: CLDAP-Ping to find the correct site David Voit
@ 2024-04-03  5:24 ` David Voit
  2024-04-04  2:10   ` Jacob Shivers
  2024-04-10 19:08   ` Paulo Alcantara
  0 siblings, 2 replies; 9+ messages in thread
From: David Voit @ 2024-04-03  5:24 UTC (permalink / raw)
  To: linux-cifs; +Cc: David Voit

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@gmail.com>
---
 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@gmail.com)
+ *
+ * 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@samba.org)
  * Copyright (C) 2010 Igor Druzhinin (jaxbrigs@gmail.com)
+ * Copyright (C) 2024 David Voit (david.voit@gmail.com)
  *
  * 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


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH v4 1/1] Implement CLDAP Ping to find the closest site
  2024-04-03  5:24 ` [PATCH v4 1/1] Implement CLDAP Ping to find the closest site David Voit
@ 2024-04-04  2:10   ` Jacob Shivers
  2024-04-10 19:08   ` Paulo Alcantara
  1 sibling, 0 replies; 9+ messages in thread
From: Jacob Shivers @ 2024-04-04  2:10 UTC (permalink / raw)
  To: David Voit; +Cc: linux-cifs

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@gmail.com> 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@gmail.com>
> ---
>  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@gmail.com)
> + *
> + * 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@samba.org)
>   * Copyright (C) 2010 Igor Druzhinin (jaxbrigs@gmail.com)
> + * Copyright (C) 2024 David Voit (david.voit@gmail.com)
>   *
>   * 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
>
>


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v4 1/1] Implement CLDAP Ping to find the closest site
  2024-04-03  5:24 ` [PATCH v4 1/1] Implement CLDAP Ping to find the closest site David Voit
  2024-04-04  2:10   ` Jacob Shivers
@ 2024-04-10 19:08   ` Paulo Alcantara
       [not found]     ` <CAPmGGo7XogXA8RppeqOtidWsgL84u+J4SUB7-st=A9a2ooPriQ@mail.gmail.com>
  1 sibling, 1 reply; 9+ messages in thread
From: Paulo Alcantara @ 2024-04-10 19:08 UTC (permalink / raw)
  To: David Voit, linux-cifs; +Cc: David Voit, Jacob Shivers, Pavel Shilovsky

David Voit <david.voit@gmail.com> writes:

> 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@gmail.com>
> ---
>  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

Great work!  I've tested it and found no issues.

Most of the cifs-utils code seems to follow the Linux kernel coding
style, but I don't see it being mentioned anywhere, so the patch looks
good.

Thanks!

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v4 1/1] Implement CLDAP Ping to find the closest site
       [not found]     ` <CAPmGGo7XogXA8RppeqOtidWsgL84u+J4SUB7-st=A9a2ooPriQ@mail.gmail.com>
@ 2024-04-20 20:55       ` Pavel Shilovsky
  2024-04-22 15:10         ` Paulo Alcantara
  2024-04-22 15:10         ` David Voit
  0 siblings, 2 replies; 9+ messages in thread
From: Pavel Shilovsky @ 2024-04-20 20:55 UTC (permalink / raw)
  To: David Voit; +Cc: Paulo Alcantara, linux-cifs, Jacob Shivers

David,
Thanks for the patch!
Merged it to https://github.com/piastry/cifs-utils/commit/770e891a8b7ad53d4d700e08cf8d3154028b4588.
I removed a blank line and a style change around addr->ai_protocol !=
IPPROTO_TCP check in resolve_host.c. Let me know if it is ok with you.

Jacob, Paulo,
Thanks for the testing! Let me know if you want me to add
"Reviewed-and-Tested-by: " tags to the commit.

--
Best regards,
Pavel Shilovsky

вт, 16 апр. 2024 г. в 06:09, David Voit <david.voit@gmail.com>:
>
> Thanks for all the reviews! Please let me know If you need any changes. I didn't got any other off-list feedback and in my point of view this is now in a ready for merge state.
>
> Paulo Alcantara <pc@manguebit.com> schrieb am Mi., 10. Apr. 2024, 21:08:
>>
>> David Voit <david.voit@gmail.com> writes:
>>
>> > 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@gmail.com>
>> > ---
>> >  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
>>
>> Great work!  I've tested it and found no issues.
>>
>> Most of the cifs-utils code seems to follow the Linux kernel coding
>> style, but I don't see it being mentioned anywhere, so the patch looks
>> good.
>>
>> Thanks!

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v4 1/1] Implement CLDAP Ping to find the closest site
  2024-04-20 20:55       ` Pavel Shilovsky
@ 2024-04-22 15:10         ` Paulo Alcantara
  2024-04-23 11:40           ` Jacob Shivers
  2024-04-22 15:10         ` David Voit
  1 sibling, 1 reply; 9+ messages in thread
From: Paulo Alcantara @ 2024-04-22 15:10 UTC (permalink / raw)
  To: Pavel Shilovsky, David Voit; +Cc: linux-cifs, Jacob Shivers

Pavel Shilovsky <piastryyy@gmail.com> writes:

> Merged it to https://github.com/piastry/cifs-utils/commit/770e891a8b7ad53d4d700e08cf8d3154028b4588.
> I removed a blank line and a style change around addr->ai_protocol !=
> IPPROTO_TCP check in resolve_host.c. Let me know if it is ok with you.

Thanks!

> Jacob, Paulo,
> Thanks for the testing! Let me know if you want me to add
> "Reviewed-and-Tested-by: " tags to the commit.

Please feel free to add:

Reviewed-by: Paulo Alcantara (Red Hat) <pc@manguebit.com>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v4 1/1] Implement CLDAP Ping to find the closest site
  2024-04-20 20:55       ` Pavel Shilovsky
  2024-04-22 15:10         ` Paulo Alcantara
@ 2024-04-22 15:10         ` David Voit
  1 sibling, 0 replies; 9+ messages in thread
From: David Voit @ 2024-04-22 15:10 UTC (permalink / raw)
  To: Pavel Shilovsky; +Cc: Paulo Alcantara, linux-cifs, Jacob Shivers

> IPPROTO_TCP check in resolve_host.c. Let me know if it is ok with you.

Sure that's fine with me, I think this change isn't coming from my change. :-)

Thanks for merging it and all the feedback off list before!

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v4 1/1] Implement CLDAP Ping to find the closest site
  2024-04-22 15:10         ` Paulo Alcantara
@ 2024-04-23 11:40           ` Jacob Shivers
  2024-04-28  0:10             ` Pavel Shilovsky
  0 siblings, 1 reply; 9+ messages in thread
From: Jacob Shivers @ 2024-04-23 11:40 UTC (permalink / raw)
  To: Paulo Alcantara; +Cc: Pavel Shilovsky, David Voit, linux-cifs

On Mon, Apr 22, 2024 at 11:10 AM Paulo Alcantara <pc@manguebit.com> wrote:
>
> Pavel Shilovsky <piastryyy@gmail.com> writes:

> > Jacob, Paulo,
> > Thanks for the testing! Let me know if you want me to add
> > "Reviewed-and-Tested-by: " tags to the commit.
>
> Please feel free to add:
>
> Reviewed-by: Paulo Alcantara (Red Hat) <pc@manguebit.com>
>
Please add:

Reviewed-by: Jacob Shivers <jshivers@redhat.com>

Thank you!


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v4 1/1] Implement CLDAP Ping to find the closest site
  2024-04-23 11:40           ` Jacob Shivers
@ 2024-04-28  0:10             ` Pavel Shilovsky
  0 siblings, 0 replies; 9+ messages in thread
From: Pavel Shilovsky @ 2024-04-28  0:10 UTC (permalink / raw)
  To: Jacob Shivers; +Cc: Paulo Alcantara, David Voit, linux-cifs

вт, 23 апр. 2024 г. в 04:41, Jacob Shivers <jshivers@redhat.com>:
>
> On Mon, Apr 22, 2024 at 11:10 AM Paulo Alcantara <pc@manguebit.com> wrote:
> >
> > Pavel Shilovsky <piastryyy@gmail.com> writes:
>
> > > Jacob, Paulo,
> > > Thanks for the testing! Let me know if you want me to add
> > > "Reviewed-and-Tested-by: " tags to the commit.
> >
> > Please feel free to add:
> >
> > Reviewed-by: Paulo Alcantara (Red Hat) <pc@manguebit.com>
> >
> Please add:
>
> Reviewed-by: Jacob Shivers <jshivers@redhat.com>

Great, thanks!

The patch is updated with reviewed-by tags and pushed to both repos:
https://git.samba.org/?p=cifs-utils.git;a=commitdiff;h=c6bf4d9a59809fbb0c22ef9eb167c099ab8089fb
https://github.com/piastry/cifs-utils/commit/c6bf4d9a59809fbb0c22ef9eb167c099ab8089fb

--
Best regards,
Pavel Shilovsky

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2024-04-28  0:10 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-04-03  5:24 [PATCH v4 0/1] cifs-utils: CLDAP-Ping to find the correct site David Voit
2024-04-03  5:24 ` [PATCH v4 1/1] Implement CLDAP Ping to find the closest site David Voit
2024-04-04  2:10   ` Jacob Shivers
2024-04-10 19:08   ` Paulo Alcantara
     [not found]     ` <CAPmGGo7XogXA8RppeqOtidWsgL84u+J4SUB7-st=A9a2ooPriQ@mail.gmail.com>
2024-04-20 20:55       ` Pavel Shilovsky
2024-04-22 15:10         ` Paulo Alcantara
2024-04-23 11:40           ` Jacob Shivers
2024-04-28  0:10             ` Pavel Shilovsky
2024-04-22 15:10         ` David Voit

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).