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

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