Blame SOURCES/0001-Make-adcli-info-DC-location-mechanism-more-compliant.patch

59dcbd
From 0a0d0f66409eb83e06b7dc50543c2f6c15a36bc4 Mon Sep 17 00:00:00 2001
59dcbd
From: Alexey A Nikitin <nikitin@amazon.com>
59dcbd
Date: Mon, 29 Oct 2018 20:40:36 -0700
59dcbd
Subject: [PATCH] Make 'adcli info' DC location mechanism more compliant with
59dcbd
 [MS-ADTS] and [MS-NRPC]
59dcbd
59dcbd
AD specifications say that DC locator must attempt to find a suitable DC for the client. That means going through all of the DCs in SRV RRs one by one until one of them answers.
59dcbd
59dcbd
The problem with adcli's original behavior is that it queries only five DCs from SRV, ever. This becomes a problem if for any reason there is a large number of DCs in the domain from which the client cannot get a CLDAP response.
59dcbd
---
59dcbd
 library/addisco.c | 146 +++++++++++++++++++++++++++++-----------------
59dcbd
 1 file changed, 94 insertions(+), 52 deletions(-)
59dcbd
59dcbd
diff --git a/library/addisco.c b/library/addisco.c
59dcbd
index 8cc5bf0..6e73ead 100644
59dcbd
--- a/library/addisco.c
59dcbd
+++ b/library/addisco.c
59dcbd
@@ -41,8 +41,10 @@
59dcbd
 #include <string.h>
59dcbd
 #include <time.h>
59dcbd
 
59dcbd
-/* Number of servers to do discovery against */
59dcbd
-#define DISCO_COUNT 5
59dcbd
+/* Number of servers to do discovery against.
59dcbd
+ * For AD DS maximum number of DCs is 1200.
59dcbd
+ */
59dcbd
+#define DISCO_COUNT 1200
59dcbd
 
59dcbd
 /* The time period in which to do rapid requests */
59dcbd
 #define DISCO_FEVER  1
59dcbd
@@ -453,6 +455,51 @@ parse_disco (LDAP *ldap,
59dcbd
 	return usability;
59dcbd
 }
59dcbd
 
59dcbd
+static int
59dcbd
+ldap_disco_poller (LDAP **ldap,
59dcbd
+                   LDAPMessage **message,
59dcbd
+                   adcli_disco **results,
59dcbd
+                   const char **addrs)
59dcbd
+{
59dcbd
+	int found = ADCLI_DISCO_UNUSABLE;
59dcbd
+	int close_ldap;
59dcbd
+	int parsed;
59dcbd
+	int ret = 0;
59dcbd
+	struct timeval tvpoll = { 0, 0 };
59dcbd
+
59dcbd
+	switch (ldap_result (*ldap, LDAP_RES_ANY, 1, &tvpoll, message)) {
59dcbd
+		case LDAP_RES_SEARCH_ENTRY:
59dcbd
+		case LDAP_RES_SEARCH_RESULT:
59dcbd
+			parsed = parse_disco (*ldap, *addrs, *message, results);
59dcbd
+			if (parsed > found)
59dcbd
+				found = parsed;
59dcbd
+			ldap_msgfree (*message);
59dcbd
+			close_ldap = 1;
59dcbd
+			break;
59dcbd
+		case -1:
59dcbd
+			ldap_get_option (*ldap, LDAP_OPT_RESULT_CODE, &ret;;
59dcbd
+			close_ldap = 1;
59dcbd
+			break;
59dcbd
+		default:
59dcbd
+			ldap_msgfree (*message);
59dcbd
+			close_ldap = 0;
59dcbd
+			break;
59dcbd
+	}
59dcbd
+
59dcbd
+	if (ret != LDAP_SUCCESS) {
59dcbd
+		_adcli_ldap_handle_failure (*ldap, ADCLI_ERR_CONFIG,
59dcbd
+		                            "Couldn't perform discovery search");
59dcbd
+	}
59dcbd
+
59dcbd
+	/* Done with this connection */
59dcbd
+	if (close_ldap) {
59dcbd
+		ldap_unbind_ext_s (*ldap, NULL, NULL);
59dcbd
+		*ldap = NULL;
59dcbd
+	}
59dcbd
+
59dcbd
+	return found;
59dcbd
+}
59dcbd
+
59dcbd
 static int
59dcbd
 ldap_disco (const char *domain,
59dcbd
             srvinfo *srv,
59dcbd
@@ -477,6 +524,7 @@ ldap_disco (const char *domain,
59dcbd
 	int num, i;
59dcbd
 	int ret;
59dcbd
 	int have_any = 0;
59dcbd
+	struct timeval interval;
59dcbd
 
59dcbd
 	if (domain) {
59dcbd
 		value = _adcli_ldap_escape_filter (domain);
59dcbd
@@ -540,7 +588,6 @@ ldap_disco (const char *domain,
59dcbd
 				version = LDAP_VERSION3;
59dcbd
 				ldap_set_option (ldap[num], LDAP_OPT_PROTOCOL_VERSION, &version);
59dcbd
 				ldap_set_option (ldap[num], LDAP_OPT_REFERRALS , 0);
59dcbd
-				_adcli_info ("Sending netlogon pings to domain controller: %s", url);
59dcbd
 				addrs[num] = srv->hostname;
59dcbd
 				have_any = 1;
59dcbd
 				num++;
59dcbd
@@ -555,70 +602,65 @@ ldap_disco (const char *domain,
59dcbd
 		freeaddrinfo (res);
59dcbd
 	}
59dcbd
 
59dcbd
-	/* Wait for the first response. Poor mans fd watch */
59dcbd
-	for (started = now = time (NULL);
59dcbd
-	     have_any && found != ADCLI_DISCO_USABLE && now < started + DISCO_TIME;
59dcbd
-	     now = time (NULL)) {
59dcbd
+	/* Initial send and short time wait */
59dcbd
+	interval.tv_sec = 0;
59dcbd
+	for (i = 0; ADCLI_DISCO_UNUSABLE == found && i < num; ++i) {
59dcbd
+		int parsed;
59dcbd
+
59dcbd
+		if (NULL == ldap[i])
59dcbd
+			continue;
59dcbd
 
59dcbd
-		struct timeval tvpoll = { 0, 0 };
59dcbd
-		struct timeval interval;
59dcbd
+		have_any = 1;
59dcbd
+		_adcli_info ("Sending NetLogon ping to domain controller: %s", addrs[i]);
59dcbd
 
59dcbd
-		/* If in the initial period, send feverishly */
59dcbd
-		if (now < started + DISCO_FEVER) {
59dcbd
-			interval.tv_sec = 0;
59dcbd
-			interval.tv_usec = 100 * 1000;
59dcbd
+		ret = ldap_search_ext (ldap[i], "", LDAP_SCOPE_BASE,
59dcbd
+		                       filter, attrs, 0, NULL, NULL, NULL,
59dcbd
+		                       -1, &msgidp);
59dcbd
+
59dcbd
+		if (ret != LDAP_SUCCESS) {
59dcbd
+			_adcli_ldap_handle_failure (ldap[i], ADCLI_ERR_CONFIG,
59dcbd
+			                            "Couldn't perform discovery search");
59dcbd
+			ldap_unbind_ext_s (ldap[i], NULL, NULL);
59dcbd
+			ldap[i] = NULL;
59dcbd
+		}
59dcbd
+
59dcbd
+		/* From https://msdn.microsoft.com/en-us/library/ff718294.aspx first
59dcbd
+		 * five DCs are given 0.4 seconds timeout, next five are given 0.2
59dcbd
+		 * seconds, and the rest are given 0.1 seconds
59dcbd
+		 */
59dcbd
+		if (i < 5) {
59dcbd
+			interval.tv_usec = 400000;
59dcbd
+		} else if (i < 10) {
59dcbd
+			interval.tv_usec = 200000;
59dcbd
 		} else {
59dcbd
-			interval.tv_sec = 1;
59dcbd
-			interval.tv_usec = 0;
59dcbd
+			interval.tv_usec = 100000;
59dcbd
 		}
59dcbd
+		select (0, NULL, NULL, NULL, &interval);
59dcbd
+
59dcbd
+		parsed = ldap_disco_poller (&(ldap[i]), &message, results, &(addrs[i]));
59dcbd
+		if (parsed > found)
59dcbd
+			found = parsed;
59dcbd
+	}
59dcbd
+
59dcbd
+	/* Wait some more until LDAP timeout (DISCO_TIME) */
59dcbd
+	for (started = now = time (NULL);
59dcbd
+	     have_any && ADCLI_DISCO_UNUSABLE == found && now < started + DISCO_TIME;
59dcbd
+	     now = time (NULL)) {
59dcbd
 
59dcbd
 		select (0, NULL, NULL, NULL, &interval);
59dcbd
 
59dcbd
 		have_any = 0;
59dcbd
-		for (i = 0; found != ADCLI_DISCO_USABLE && i < num; i++) {
59dcbd
-			int close_ldap;
59dcbd
+		for (i = 0; ADCLI_DISCO_UNUSABLE == found && i < num; ++i) {
59dcbd
 			int parsed;
59dcbd
 
59dcbd
 			if (ldap[i] == NULL)
59dcbd
 				continue;
59dcbd
 
59dcbd
-			ret = 0;
59dcbd
 			have_any = 1;
59dcbd
-			switch (ldap_result (ldap[i], LDAP_RES_ANY, 1, &tvpoll, &message)) {
59dcbd
-			case LDAP_RES_SEARCH_ENTRY:
59dcbd
-			case LDAP_RES_SEARCH_RESULT:
59dcbd
-				parsed = parse_disco (ldap[i], addrs[i], message, results);
59dcbd
-				if (parsed > found)
59dcbd
-					found = parsed;
59dcbd
-				ldap_msgfree (message);
59dcbd
-				close_ldap = 1;
59dcbd
-				break;
59dcbd
-			case 0:
59dcbd
-				ret = ldap_search_ext (ldap[i], "", LDAP_SCOPE_BASE,
59dcbd
-				                       filter, attrs, 0, NULL, NULL, NULL,
59dcbd
-				                       -1, &msgidp);
59dcbd
-				close_ldap = (ret != 0);
59dcbd
-				break;
59dcbd
-			case -1:
59dcbd
-				ldap_get_option (ldap[i], LDAP_OPT_RESULT_CODE, &ret;;
59dcbd
-				close_ldap = 1;
59dcbd
-				break;
59dcbd
-			default:
59dcbd
-				ldap_msgfree (message);
59dcbd
-				close_ldap = 0;
59dcbd
-				break;
59dcbd
-			}
59dcbd
-
59dcbd
-			if (ret != LDAP_SUCCESS) {
59dcbd
-				_adcli_ldap_handle_failure (ldap[i], ADCLI_ERR_CONFIG,
59dcbd
-				                            "Couldn't perform discovery search");
59dcbd
-			}
59dcbd
 
59dcbd
-			/* Done with this connection */
59dcbd
-			if (close_ldap) {
59dcbd
-				ldap_unbind_ext_s (ldap[i], NULL, NULL);
59dcbd
-				ldap[i] = NULL;
59dcbd
-			}
59dcbd
+			parsed = ldap_disco_poller (&(ldap[i]), &message, results, &(addrs[i]));
59dcbd
+			if (parsed > found)
59dcbd
+				found = parsed;
59dcbd
 		}
59dcbd
 	}
59dcbd
 
59dcbd
-- 
59dcbd
2.21.0
59dcbd