--- old/usr/src/lib/smbsrv/libsmb/common/smb_domain.c	Mon Aug  5 17:17:15 2019
+++ new/usr/src/lib/smbsrv/libsmb/common/smb_domain.c	Mon Aug  5 17:17:15 2019
@@ -22,7 +22,7 @@
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  *
- * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
  */
 
 /*
@@ -37,6 +37,7 @@
 #include <stdio.h>
 #include <strings.h>
 #include <string.h>
+#include <syslog.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <synch.h>
@@ -70,10 +71,13 @@
 typedef struct smb_domain_cache {
 	list_t		dc_cache;
 	rwlock_t	dc_cache_lck;
-	mutex_t		dc_mtx;
-	cond_t		dc_cv;
 	uint32_t	dc_state;
 	uint32_t	dc_nops;
+	mutex_t		dc_mtx;
+	cond_t		dc_cv;
+	/* domain controller information */
+	cond_t		dc_dci_cv;
+	boolean_t	dc_dci_valid;
 	smb_dcinfo_t	dc_dci;
 } smb_domain_cache_t;
 
@@ -90,7 +94,7 @@
 static void smb_dcache_unlock(void);
 static void smb_dcache_remove(smb_domain_t *);
 static uint32_t smb_dcache_add(smb_domain_t *);
-static boolean_t smb_dcache_getdc(smb_dcinfo_t *);
+static boolean_t smb_dcache_getdc(smb_dcinfo_t *, boolean_t);
 static void smb_dcache_setdc(const smb_dcinfo_t *);
 static boolean_t smb_dcache_wait(void);
 static uint32_t smb_dcache_updating(void);
@@ -113,6 +117,7 @@
 	if ((rc = smb_domain_add_local()) != 0)
 		return (rc);
 
+	bzero(&di, sizeof (di));
 	smb_domain_set_basic_info(NT_BUILTIN_DOMAIN_SIDSTR, "BUILTIN", "", &di);
 	(void) smb_domain_add(SMB_DOMAIN_BUILTIN, &di);
 
@@ -289,6 +294,8 @@
 /*
  * Returns primary domain information plus the name of
  * the selected domain controller.
+ *
+ * Returns TRUE on success.
  */
 boolean_t
 smb_domain_getinfo(smb_domainex_t *dxi)
@@ -297,10 +304,27 @@
 
 	/* Note: this waits for the dcache lock. */
 	rv = smb_domain_lookup_type(SMB_DOMAIN_PRIMARY, &dxi->d_primary);
-	if (rv)
-		rv = smb_dcache_getdc(&dxi->d_dci);
+	if (!rv) {
+		syslog(LOG_ERR, "smb_domain_getinfo: no primary domain");
+		return (B_FALSE);
+	}
 
-	return (rv);
+	/*
+	 * The 2nd arg TRUE means this will wait for DC info.
+	 *
+	 * Note that we do NOT hold the dcache rwlock here
+	 * (not even as reader) because we already have what we
+	 * need from the dcache (our primary domain) and we don't
+	 * want to interfere with the DC locator which will take
+	 * the dcache lock as write to update the domain info.
+	 */
+	rv = smb_dcache_getdc(&dxi->d_dci, B_TRUE);
+	if (!rv) {
+		syslog(LOG_ERR, "smb_domain_getinfo: no DC info");
+		return (B_FALSE);
+	}
+
+	return (B_TRUE);
 }
 
 /*
@@ -310,7 +334,7 @@
 void
 smb_domain_current_dc(smb_dcinfo_t *dci)
 {
-	(void) smb_dcache_getdc(dci);
+	(void) smb_dcache_getdc(dci, B_FALSE);
 }
 
 /*
@@ -334,6 +358,18 @@
 }
 
 /*
+ * Mark the current domain controller (DC) info invalid
+ * until the DC locator call smb_domain_update().
+ */
+void
+smb_domain_bad_dc(void)
+{
+	(void) mutex_lock(&smb_dcache.dc_mtx);
+	smb_dcache.dc_dci_valid = B_FALSE;
+	(void) mutex_unlock(&smb_dcache.dc_mtx);
+}
+
+/*
  * Updates the cache with given information for the primary
  * domain, possible trusted domains and the selected domain
  * controller.
@@ -484,6 +520,7 @@
 	if (di == NULL || forest == NULL || guid == NULL)
 		return;
 
+	/* Caller zeros out *di before this. */
 	smb_domain_set_basic_info(sid, nb_domain, fq_domain, di);
 	(void) strlcpy(di->di_u.di_dns.ddi_forest, forest, MAXHOSTNAMELEN);
 	(void) strlcpy(di->di_u.di_dns.ddi_guid, guid,
@@ -500,6 +537,7 @@
 	if (di == NULL)
 		return;
 
+	/* Caller zeros out *di before this. */
 	di->di_type = SMB_DOMAIN_TRUSTED;
 	ti = &di->di_u.di_trust;
 	smb_domain_set_basic_info(sid, nb_domain, fq_domain, di);
@@ -540,6 +578,7 @@
 		return (SMB_DOMAIN_NOMACHINE_SID);
 	}
 
+	bzero(&di, sizeof (di));
 	*fq_name = '\0';
 	(void) smb_getfqhostname(fq_name, MAXHOSTNAMELEN);
 	smb_domain_set_basic_info(lsidstr, hostname, fq_name, &di);
@@ -572,6 +611,7 @@
 	if ((rc != SMBD_SMF_OK) || (*nb_name == '\0'))
 		return (SMB_DOMAIN_NODOMAIN_NAME);
 
+	bzero(&di, sizeof (di));
 	(void) smb_getfqdomainname(fq_name, MAXHOSTNAMELEN);
 	smb_domain_set_basic_info(sidstr, nb_name, fq_name, &di);
 	(void) smb_domain_add(SMB_DOMAIN_PRIMARY, &di);
@@ -596,6 +636,7 @@
 
 	smb_dcache.dc_nops = 0;
 	bzero(&smb_dcache.dc_dci, sizeof (smb_dcache.dc_dci));
+	smb_dcache.dc_dci_valid = B_FALSE;
 	smb_dcache.dc_state = SMB_DCACHE_STATE_READY;
 	(void) mutex_unlock(&smb_dcache.dc_mtx);
 }
@@ -652,6 +693,7 @@
 	switch (smb_dcache.dc_state) {
 	case SMB_DCACHE_STATE_NONE:
 	case SMB_DCACHE_STATE_DESTROYING:
+	default:
 		(void) mutex_unlock(&smb_dcache.dc_mtx);
 		return (SMB_DOMAIN_INTERNAL_ERR);
 
@@ -666,8 +708,9 @@
 				return (SMB_DOMAIN_INTERNAL_ERR);
 			}
 		}
+		/* FALLTHROUGH */
 
-	default:
+	case SMB_DCACHE_STATE_READY:
 		smb_dcache.dc_nops++;
 		break;
 	}
@@ -732,19 +775,40 @@
 {
 	(void) mutex_lock(&smb_dcache.dc_mtx);
 	smb_dcache.dc_dci = *dci; /* struct assignment! */
+	smb_dcache.dc_dci_valid = B_TRUE;
+	(void) cond_broadcast(&smb_dcache.dc_dci_cv);
 	(void) mutex_unlock(&smb_dcache.dc_mtx);
 }
 
 /*
- * Return B_TRUE if we have DC information.
+ * Get information about our domain controller.  If the wait arg
+ * is true, wait for the DC locator to finish before copying.
+ * Returns TRUE on success (have DC info).
  */
 static boolean_t
-smb_dcache_getdc(smb_dcinfo_t *dci)
+smb_dcache_getdc(smb_dcinfo_t *dci, boolean_t wait)
 {
+	timestruc_t to;
+	boolean_t rv;
+	int err;
+
+	to.tv_sec = time(NULL) + SMB_DCACHE_UPDATE_WAIT;
+	to.tv_nsec = 0;
+
 	(void) mutex_lock(&smb_dcache.dc_mtx);
+
+	while (wait && !smb_dcache.dc_dci_valid) {
+		err = cond_timedwait(&smb_dcache.dc_dci_cv,
+		    &smb_dcache.dc_mtx, &to);
+		if (err == ETIME)
+			break;
+	}
 	*dci = smb_dcache.dc_dci; /* struct assignment! */
+	rv = smb_dcache.dc_dci_valid;
+
 	(void) mutex_unlock(&smb_dcache.dc_mtx);
-	return (dci->dc_name[0] != '\0');
+
+	return (rv);
 }
 
 /*
@@ -758,10 +822,12 @@
 	timestruc_t to;
 	int err;
 
-	to.tv_sec = SMB_DCACHE_UPDATE_WAIT;
+	assert(MUTEX_HELD(&smb_dcache.dc_mtx));
+
+	to.tv_sec = time(NULL) + SMB_DCACHE_UPDATE_WAIT;
 	to.tv_nsec = 0;
 	while (smb_dcache.dc_state == SMB_DCACHE_STATE_UPDATING) {
-		err = cond_reltimedwait(&smb_dcache.dc_cv,
+		err = cond_timedwait(&smb_dcache.dc_cv,
 		    &smb_dcache.dc_mtx, &to);
 		if (err == ETIME)
 			break;
