Question on ntlm_auth tool

Volker Lendecke Volker.Lendecke at SerNet.DE
Thu Sep 16 13:47:28 GMT 2004


On Thu, Sep 16, 2004 at 03:40:09PM +0200, Henrik Nordstrom wrote:
> Is there any thoughts about addressing this "minor" detail in the winbindd 
> architecture? Having an central authentication service single-threaded 
> blocking on external resources does not scale too well..

Look at trunk winbind. This has the architecture to have a configurable number
of worker-processes. It's not finished, but looks very promising. The only
problem is: I need a bit help to convert and test the rest of the requests.

Attached find the essence of that patch in its current form, with some torture
code in.

Volker
-------------- next part --------------
diff -Npur --exclude=CVS --exclude='*.bak' --exclude='*.o' --exclude='*.po' --exclude='*.so' --exclude='.#*' --exclude=Makefile --exclude=stamp-h --exclude=configure --exclude=findsmb --exclude='*proto*.h' --exclude=build_env.h --exclude=tdbsam2_parse_info.h --exclude='config.*' --exclude=bin --exclude='*.configure' --exclude=autom4te.cache --exclude='build_options.c*' --exclude=TAGS --exclude='*~' --exclude=gen-8bit-gap.sh --exclude=smbadduser --exclude=version.h --exclude=.svn --exclude=.cvsignore --exclude='*.rej' --exclude='*.orig' ../../samba-3.0.7/source/include/idmap.h ./include/idmap.h
--- ../../samba-3.0.7/source/include/idmap.h	2004-04-04 09:37:23.000000000 +0200
+++ ./source/include/idmap.h	2004-09-15 18:15:38.000000000 +0200
@@ -35,6 +35,7 @@
 #define ID_TYPEMASK	0x0f
 
 #define ID_QUERY_ONLY	0x10
+#define ID_CACHE_ONLY	0x20
 
 /* Filled out by IDMAP backends */
 struct idmap_methods {
diff -Npur --exclude=CVS --exclude='*.bak' --exclude='*.o' --exclude='*.po' --exclude='*.so' --exclude='.#*' --exclude=Makefile --exclude=stamp-h --exclude=configure --exclude=findsmb --exclude='*proto*.h' --exclude=build_env.h --exclude=tdbsam2_parse_info.h --exclude='config.*' --exclude=bin --exclude='*.configure' --exclude=autom4te.cache --exclude='build_options.c*' --exclude=TAGS --exclude='*~' --exclude=gen-8bit-gap.sh --exclude=smbadduser --exclude=version.h --exclude=.svn --exclude=.cvsignore --exclude='*.rej' --exclude='*.orig' ../../samba-3.0.7/source/include/messages.h ./include/messages.h
--- ../../samba-3.0.7/source/include/messages.h	2004-08-19 15:39:13.000000000 +0200
+++ ./source/include/messages.h	2004-09-15 18:15:38.000000000 +0200
@@ -63,6 +63,9 @@
 #define MSG_SMB_SAM_REPL     3004
 #define MSG_SMB_UNLOCK       3005
 
+/* winbind messages */
+#define MSG_WINBIND_FINISHED 4001
+
 /* Flags to classify messages - used in message_send_all() */
 /* Sender will filter by flag. */
 
diff -Npur --exclude=CVS --exclude='*.bak' --exclude='*.o' --exclude='*.po' --exclude='*.so' --exclude='.#*' --exclude=Makefile --exclude=stamp-h --exclude=configure --exclude=findsmb --exclude='*proto*.h' --exclude=build_env.h --exclude=tdbsam2_parse_info.h --exclude='config.*' --exclude=bin --exclude='*.configure' --exclude=autom4te.cache --exclude='build_options.c*' --exclude=TAGS --exclude='*~' --exclude=gen-8bit-gap.sh --exclude=smbadduser --exclude=version.h --exclude=.svn --exclude=.cvsignore --exclude='*.rej' --exclude='*.orig' ../../samba-3.0.7/source/nsswitch/wbinfo.c ./nsswitch/wbinfo.c
--- ../../samba-3.0.7/source/nsswitch/wbinfo.c	2004-09-16 14:30:16.000000000 +0200
+++ ./source/nsswitch/wbinfo.c	2004-09-15 18:15:38.000000000 +0200
@@ -85,6 +85,26 @@ static const char *get_winbind_domain(vo
 
 }
 
+static BOOL wbinfo_info(void)
+{
+	struct winbindd_response response;
+
+	ZERO_STRUCT(response);
+
+	/* Send off request */
+
+	if (winbindd_request(WINBINDD_INFO, NULL, &response)
+	    != NSS_STATUS_SUCCESS)
+		return False;
+
+	d_printf("Separator: [%c]\nVersion: %s\nmax_busy: %d\n",
+		 response.data.info.winbind_separator,
+		 response.data.info.samba_version,
+		 response.data.info.max_busy_children);
+	
+	return True;
+}
+
 /* Copy of parse_domain_user from winbindd_util.c.  Parse a string of the
    form DOMAIN/user into a domain and a user */
 
@@ -1072,6 +1092,7 @@ int main(int argc, char **argv)
 		{ "get-auth-user", 0, POPT_ARG_NONE, NULL, OPT_GET_AUTH_USER, "Retrieve user and password used by winbindd (root only)", NULL },
 		{ "ping", 'p', POPT_ARG_NONE, 0, 'p', "Ping winbindd to see if it is alive" },
 		{ "domain", 0, POPT_ARG_STRING, &opt_domain_name, OPT_DOMAIN_NAME, "Define to the domain to restrict operation", "domain" },
+		{ "get-info", 'i', POPT_ARG_NONE, 0, 'i', "Get misc info" },
 #ifdef WITH_FAKE_KASERVER
  		{ "klog", 'k', POPT_ARG_STRING, &string_arg, 'k', "set an AFS token from winbind", "user%password" },
 #endif
@@ -1288,6 +1309,12 @@ int main(int argc, char **argv)
 				goto done;
 			}
 			break;
+		case 'i':
+			if (!wbinfo_info()) {
+				d_printf("could not get winbind info!\n");
+				goto done;
+			}
+			break;
 		case OPT_SET_AUTH_USER:
 			wbinfo_set_auth_user(string_arg);
 			break;
diff -Npur --exclude=CVS --exclude='*.bak' --exclude='*.o' --exclude='*.po' --exclude='*.so' --exclude='.#*' --exclude=Makefile --exclude=stamp-h --exclude=configure --exclude=findsmb --exclude='*proto*.h' --exclude=build_env.h --exclude=tdbsam2_parse_info.h --exclude='config.*' --exclude=bin --exclude='*.configure' --exclude=autom4te.cache --exclude='build_options.c*' --exclude=TAGS --exclude='*~' --exclude=gen-8bit-gap.sh --exclude=smbadduser --exclude=version.h --exclude=.svn --exclude=.cvsignore --exclude='*.rej' --exclude='*.orig' ../../samba-3.0.7/source/nsswitch/winbindd.c ./nsswitch/winbindd.c
--- ../../samba-3.0.7/source/nsswitch/winbindd.c	2004-08-06 23:38:19.000000000 +0200
+++ ./source/nsswitch/winbindd.c	2004-09-15 18:15:38.000000000 +0200
@@ -27,6 +27,7 @@
 
 BOOL opt_nocache = False;
 BOOL opt_dual_daemon = True;
+int max_busy_children = 0;
 
 /* Reload configuration */
 
@@ -213,12 +214,133 @@ static void msg_shutdown(int msg_type, p
 	terminate();
 }
 
+/* Dual daemon finished a request */
+static void msg_finished(int msg_type, pid_t src, void *buf, size_t len)
+{
+	struct winbindd_cli_state *state;
+	int *msgid;
+
+	DEBUG(5, ("Got finished message\n"));
+
+	if (len != sizeof(*msgid)) {
+		DEBUG(0, ("Wrong buffer size in message: %d\n", len));
+		return;
+	}
+
+	dual_finished(src);
+
+	msgid = (int *)buf;
+
+	DEBUGADD(10, ("got msgid %d\n", *msgid));
+
+	for (state = winbindd_client_list(); state; state = state->next) {
+		if (state->response.result != WINBINDD_PENDING)
+			continue;
+
+		if (state->msgid != *msgid)
+			continue;
+
+		state->response.result = state->continuation(state, src);
+
+		if (state->response.result == WINBINDD_PENDING) {
+			if (state->send_to_background) {
+				extern BOOL background_process;
+				background_process = True;
+				dual_send_request(state);
+			}
+			return;
+		}
+
+		state->read_buf_len = 0;
+		state->write_buf_len = sizeof(struct winbindd_response);
+	}
+}
+
 struct dispatch_table {
 	enum winbindd_cmd cmd;
 	enum winbindd_result (*fn)(struct winbindd_cli_state *state);
 	const char *winbindd_cmd_name;
 };
 
+static struct dispatch_table cache_table[] = {
+	
+	/* User functions */
+
+	{ WINBINDD_GETPWNAM, dual_request, "GETPWNAM" },
+	{ WINBINDD_GETPWUID, dual_request, "GETPWUID" },
+
+	{ WINBINDD_SETPWENT, winbindd_setpwent, "SETPWENT" },
+	{ WINBINDD_ENDPWENT, winbindd_endpwent, "ENDPWENT" },
+	{ WINBINDD_GETPWENT, winbindd_getpwent, "GETPWENT" },
+
+	{ WINBINDD_GETGROUPS, winbindd_getgroups, "GETGROUPS" },
+	{ WINBINDD_GETUSERSIDS, cache_getusersids, "GETUSERSIDS" },
+
+	/* Group functions */
+
+	{ WINBINDD_GETGRNAM, dual_request, "GETGRNAM" },
+	{ WINBINDD_GETGRGID, dual_request, "GETGRGID" },
+	{ WINBINDD_SETGRENT, winbindd_setgrent, "SETGRENT" },
+	{ WINBINDD_ENDGRENT, winbindd_endgrent, "ENDGRENT" },
+	{ WINBINDD_GETGRENT, winbindd_getgrent, "GETGRENT" },
+	{ WINBINDD_GETGRLST, winbindd_getgrent, "GETGRLST" },
+
+	/* PAM auth functions */
+
+	{ WINBINDD_PAM_AUTH, winbindd_pam_auth, "PAM_AUTH" },
+	{ WINBINDD_PAM_AUTH_CRAP, winbindd_pam_auth_crap, "AUTH_CRAP" },
+	{ WINBINDD_PAM_CHAUTHTOK, winbindd_pam_chauthtok, "CHAUTHTOK" },
+
+	/* Enumeration functions */
+
+	{ WINBINDD_LIST_USERS, cache_list_users, "LIST_USERS" },
+	{ WINBINDD_LIST_GROUPS, cache_list_groups, "LIST_GROUPS" },
+	{ WINBINDD_LIST_TRUSTDOM, winbindd_list_trusted_domains, "LIST_TRUSTDOM" },
+	{ WINBINDD_SHOW_SEQUENCE, winbindd_show_sequence, "SHOW_SEQUENCE" },
+
+	/* SID related functions */
+
+	{ WINBINDD_LOOKUPSID, cache_lookupsid, "LOOKUPSID" },
+	{ WINBINDD_LOOKUPNAME, cache_lookupname, "LOOKUPNAME" },
+
+	/* Lookup related functions */
+
+	{ WINBINDD_SID_TO_UID, cache_sid_to_uid, "SID_TO_UID" },
+	{ WINBINDD_SID_TO_GID, cache_sid_to_gid, "SID_TO_GID" },
+	{ WINBINDD_GID_TO_SID, cache_gid_to_sid, "GID_TO_SID" },
+	{ WINBINDD_UID_TO_SID, cache_uid_to_sid, "UID_TO_SID" },
+	{ WINBINDD_ALLOCATE_RID, winbindd_allocate_rid, "ALLOCATE_RID" },
+
+	/* Miscellaneous */
+
+	{ WINBINDD_CHECK_MACHACC, winbindd_check_machine_acct, "CHECK_MACHACC" },
+	{ WINBINDD_PING, winbindd_ping, "PING" },
+	{ WINBINDD_INFO, winbindd_info, "INFO" },
+	{ WINBINDD_INTERFACE_VERSION, winbindd_interface_version, "INTERFACE_VERSION" },
+	{ WINBINDD_DOMAIN_NAME, winbindd_domain_name, "DOMAIN_NAME" },
+	{ WINBINDD_DOMAIN_INFO, winbindd_domain_info, "DOMAIN_INFO" },
+	{ WINBINDD_NETBIOS_NAME, winbindd_netbios_name, "NETBIOS_NAME" },
+	{ WINBINDD_PRIV_PIPE_DIR, winbindd_priv_pipe_dir, "WINBINDD_PRIV_PIPE_DIR" },
+
+	/* WINS functions */
+
+	{ WINBINDD_WINS_BYNAME, winbindd_wins_byname, "WINS_BYNAME" },
+	{ WINBINDD_WINS_BYIP, winbindd_wins_byip, "WINS_BYIP" },
+	
+	/* UNIX account management functions */
+	{ WINBINDD_CREATE_USER, 		winbindd_create_user, 		"CREATE_USER" 		},
+	{ WINBINDD_CREATE_GROUP,		winbindd_create_group, 		"CREATE_GROUP" 		},
+	{ WINBINDD_ADD_USER_TO_GROUP, 		winbindd_add_user_to_group, 	"ADD_USER_TO_GROUP" 	},
+	{ WINBINDD_REMOVE_USER_FROM_GROUP, 	winbindd_remove_user_from_group,"REMOVE_USER_FROM_GROUP"},
+	{ WINBINDD_SET_USER_PRIMARY_GROUP, 	winbindd_set_user_primary_group,"SET_USER_PRIMARY_GROUP"},
+	{ WINBINDD_DELETE_USER,	 		winbindd_delete_user,		"DELETE_USER"		},
+	{ WINBINDD_DELETE_GROUP, 		winbindd_delete_group,		"DELETE_GROUP"		},
+	
+	/* End of list */
+
+	{ WINBINDD_NUM_CMDS, NULL, "NONE" }
+};
+
 static struct dispatch_table dispatch_table[] = {
 	
 	/* User functions */
@@ -300,7 +422,7 @@ static struct dispatch_table dispatch_ta
 
 static void process_request(struct winbindd_cli_state *state)
 {
-	struct dispatch_table *table = dispatch_table;
+	struct dispatch_table *table;
 
 	/* Free response data - we may be interrupted and receive another
 	   command before being able to send this data off. */
@@ -314,7 +436,8 @@ static void process_request(struct winbi
 
 	/* Process command */
 
-	for (table = dispatch_table; table->fn; table++) {
+	for (table = opt_dual_daemon ? cache_table : dispatch_table;
+	     table->fn; table++) {
 		if (state->request.cmd == table->cmd) {
 			DEBUG(10,("process_request: request fn %s\n", table->winbindd_cmd_name ));
 			state->response.result = table->fn(state);
@@ -440,16 +563,22 @@ void winbind_process_packet(struct winbi
 	state->request.null_term = '\0';
 
 	state->pid = state->request.pid;
+
+	state->continuation = NULL;
 	
 	process_request(state);
 
 	/* Update client state */
-	
-	state->read_buf_len = 0;
-	state->write_buf_len = sizeof(struct winbindd_response);
+
+	if (state->response.result != WINBINDD_PENDING) {
+		state->read_buf_len = 0;
+		state->write_buf_len = sizeof(struct winbindd_response);
+	}
 
 	/* we might need to send it to the dual daemon */
-	if (opt_dual_daemon) {
+	if (opt_dual_daemon && state->send_to_background) {
+		extern BOOL background_process;
+		background_process = True;
 		dual_send_request(state);
 	}
 }
@@ -587,6 +716,7 @@ static void process_loop(void)
 		int maxfd, listen_sock, listen_priv_sock, selret;
 		struct timeval timeout;
 
+	again:
 		/* Handle messages */
 
 		message_dispatch();
@@ -680,6 +810,28 @@ static void process_loop(void)
 
 			if (opt_dual_daemon) {
 				dual_select(&w_fds);
+
+				if (!opt_dual_daemon) {
+					/* All children died */
+
+					state = winbindd_client_list();
+
+					while (state != NULL) {
+						struct winbindd_cli_state *next;
+						next = state->next;
+
+						/* In theory we should re-do
+						 * the whole request, but this
+						 * looks rather complicated as
+						 * the client's state machines
+						 * might have changed
+						 * state->request. Is it worth
+						 * the effort? */
+						remove_client(state);
+
+						state = next;
+					}
+				}
 			}
 
 			if (FD_ISSET(listen_sock, &r_fds)) {
@@ -715,6 +867,15 @@ static void process_loop(void)
 			for (state = winbindd_client_list(); state; 
 			     state = state->next) {
                 
+				/* Data available for writing */
+                
+				if (FD_ISSET(state->sock, &w_fds))
+					client_write(state);
+			}
+                
+			for (state = winbindd_client_list(); state; 
+			     state = state->next) {
+                
 				/* Data available for reading */
                 
 				if (FD_ISSET(state->sock, &r_fds)) {
@@ -746,14 +907,15 @@ static void process_loop(void)
                     
 					if (state->read_buf_len == 
 					    sizeof(state->request)) {
+						static int msgid;
+						msgid += 1;
+						state->msgid = msgid;
+						state->request.msgid = msgid;
 						winbind_process_packet(state);
+						winbindd_demote_client(state);
+						goto again;
 					}
 				}
-                
-				/* Data available for writing */
-                
-				if (FD_ISSET(state->sock, &w_fds))
-					client_write(state);
 			}
 		}
 
@@ -788,6 +950,7 @@ struct winbindd_state server_state;   /*
 int main(int argc, char **argv)
 {
 	pstring logfile;
+	int num_children = 1;
 	static BOOL interactive = False;
 	static BOOL Fork = True;
 	static BOOL log_stdout = False;
@@ -798,6 +961,7 @@ int main(int argc, char **argv)
 		{ "interactive", 'i', POPT_ARG_NONE, NULL, 'i', "Interactive mode" },
 		{ "single-daemon", 'Y', POPT_ARG_VAL, &opt_dual_daemon, False, "Single daemon mode" },
 		{ "no-caching", 'n', POPT_ARG_VAL, &opt_nocache, True, "Disable caching" },
+		{ "num-clients", 'c', POPT_ARG_INT, &num_children, True, "Number of winbind children" },
 		POPT_COMMON_SAMBA
 		POPT_TABLEEND
 	};
@@ -928,7 +1092,10 @@ int main(int argc, char **argv)
 #endif
 
 	if (opt_dual_daemon) {
-		do_dual_daemon();
+		int i;
+
+		for (i=0; i<num_children; i++)
+			do_dual_daemon();
 	}
 
 	/* Initialise messaging system */
@@ -942,6 +1109,7 @@ int main(int argc, char **argv)
 	   as to SIGHUP signal */
 	message_register(MSG_SMB_CONF_UPDATED, msg_reload_services);
 	message_register(MSG_SHUTDOWN, msg_shutdown);
+	message_register(MSG_WINBIND_FINISHED, msg_finished);
 	
 	poptFreeContext(pc);
 
diff -Npur --exclude=CVS --exclude='*.bak' --exclude='*.o' --exclude='*.po' --exclude='*.so' --exclude='.#*' --exclude=Makefile --exclude=stamp-h --exclude=configure --exclude=findsmb --exclude='*proto*.h' --exclude=build_env.h --exclude=tdbsam2_parse_info.h --exclude='config.*' --exclude=bin --exclude='*.configure' --exclude=autom4te.cache --exclude='build_options.c*' --exclude=TAGS --exclude='*~' --exclude=gen-8bit-gap.sh --exclude=smbadduser --exclude=version.h --exclude=.svn --exclude=.cvsignore --exclude='*.rej' --exclude='*.orig' ../../samba-3.0.7/source/nsswitch/winbindd.h ./nsswitch/winbindd.h
--- ../../samba-3.0.7/source/nsswitch/winbindd.h	2004-09-16 14:30:16.000000000 +0200
+++ ./source/nsswitch/winbindd.h	2004-09-15 18:15:38.000000000 +0200
@@ -44,6 +44,12 @@ struct winbindd_cli_state {
 	time_t last_access;                       /* Time of last access (read or write) */
 	BOOL privileged;                           /* Is the client 'privileged' */
 
+	int msgid;				  /* message id to expect */
+	BOOL send_to_background;
+	enum winbindd_result (*continuation)(struct winbindd_cli_state *cli,
+					     pid_t dual_daemon);
+	void *continuation_private;
+
 	struct winbindd_request request;          /* Request from client */
 	struct winbindd_response response;        /* Respose to client */
 	BOOL getpwent_initialized;                /* Has getpwent_state been initialized? */
diff -Npur --exclude=CVS --exclude='*.bak' --exclude='*.o' --exclude='*.po' --exclude='*.so' --exclude='.#*' --exclude=Makefile --exclude=stamp-h --exclude=configure --exclude=findsmb --exclude='*proto*.h' --exclude=build_env.h --exclude=tdbsam2_parse_info.h --exclude='config.*' --exclude=bin --exclude='*.configure' --exclude=autom4te.cache --exclude='build_options.c*' --exclude=TAGS --exclude='*~' --exclude=gen-8bit-gap.sh --exclude=smbadduser --exclude=version.h --exclude=.svn --exclude=.cvsignore --exclude='*.rej' --exclude='*.orig' ../../samba-3.0.7/source/nsswitch/winbindd_cache.c ./nsswitch/winbindd_cache.c
--- ../../samba-3.0.7/source/nsswitch/winbindd_cache.c	2004-09-12 05:47:17.000000000 +0200
+++ ./source/nsswitch/winbindd_cache.c	2004-09-16 14:04:32.000000000 +0200
@@ -66,6 +66,27 @@ void wcache_flush_cache(void)
 	DEBUG(10,("wcache_flush_cache success\n"));
 }
 
+static BOOL init_wcache(void)
+{
+	if (wcache == NULL) {
+		wcache = smb_xmalloc(sizeof(*wcache));
+		ZERO_STRUCTP(wcache);
+	}
+
+	if (wcache->tdb != NULL)
+		return True;
+
+	wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), 5000, 
+				   TDB_CLEAR_IF_FIRST, O_RDWR|O_CREAT, 0600);
+
+	if (wcache->tdb == NULL) {
+		DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
+		return False;
+	}
+
+	return True;
+}
+
 void winbindd_check_cache_size(time_t t)
 {
 	static time_t last_check_time;
@@ -391,14 +412,15 @@ done:
 /*
   decide if a cache entry has expired
 */
-static BOOL centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
+static BOOL centry_expired(struct winbindd_domain *domain,
+			   struct cache_entry *centry)
 {
-	/* if the server is OK and our cache entry came from when it was down then
-	   the entry is invalid */
+	/* if the server is OK and our cache entry came from when it was down
+	   then the entry is invalid */
 	if (domain->sequence_number != DOM_SEQUENCE_NONE && 
 	    centry->sequence_number == DOM_SEQUENCE_NONE) {
-		DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
-			keystr, domain->name ));
+		DEBUG(10,("centry_expired: Key for domain %s invalid "
+			  "sequence.\n", domain->name ));
 		return True;
 	}
 
@@ -406,13 +428,13 @@ static BOOL centry_expired(struct winbin
 	   current sequence number then it is OK */
 	if (wcache_server_down(domain) || 
 	    centry->sequence_number == domain->sequence_number) {
-		DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
-			keystr, domain->name ));
+		DEBUG(10,("centry_expired: Key for domain %s is good.\n",
+			  domain->name ));
 		return False;
 	}
 
-	DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
-		keystr, domain->name ));
+	DEBUG(10,("centry_expired: Key for domain %s expired\n",
+		  domain->name ));
 
 	/* it's expired */
 	return True;
@@ -467,7 +489,7 @@ static struct cache_entry *wcache_fetch(
 	centry->status = NT_STATUS(centry_uint32(centry));
 	centry->sequence_number = centry_uint32(centry);
 
-	if (centry_expired(domain, kstr, centry)) {
+	if (centry_expired(domain, centry)) {
 		extern BOOL opt_dual_daemon;
 
 		DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
@@ -609,7 +631,6 @@ static void wcache_save_name_to_sid(stru
 {
 	struct cache_entry *centry;
 	fstring uname;
-	fstring sid_string;
 
 	centry = centry_start(domain, status);
 	if (!centry)
@@ -619,7 +640,8 @@ static void wcache_save_name_to_sid(stru
 	fstrcpy(uname, name);
 	strupper_m(uname);
 	centry_end(centry, "NS/%s/%s", domain_name, uname);
-	DEBUG(10,("wcache_save_name_to_sid: %s -> %s\n", uname, sid_string));
+	DEBUG(10,("wcache_save_name_to_sid: %s -> %s\n", uname,
+		  sid_string_static(sid)));
 	centry_free(centry);
 }
 
@@ -1134,6 +1156,10 @@ static NTSTATUS lookup_usergroups(struct
 	if (!cache->tdb)
 		goto do_query;
 
+#if 1
+	goto do_query;
+#endif
+
 	centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid));
 	
 	/* If we have an access denied cache entry and a cached info3 in the
@@ -1393,3 +1419,678 @@ struct winbindd_methods cache_methods = 
 	domain_sid,
 	alternate_name
 };
+
+static struct cache_entry *
+wcache_fetch_only(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
+
+static struct cache_entry *wcache_fetch_only(const char *format, ...)
+{
+	va_list ap;
+	char *kstr;
+	TDB_DATA data;
+	struct cache_entry *centry;
+	TDB_DATA key;
+
+	if (!init_wcache())
+		return NULL;
+
+	va_start(ap, format);
+	smb_xvasprintf(&kstr, format, ap);
+	va_end(ap);
+	
+	key.dptr = kstr;
+	key.dsize = strlen(kstr);
+	data = tdb_fetch(wcache->tdb, key);
+	if (!data.dptr) {
+		/* a cache miss */
+		free(kstr);
+		return NULL;
+	}
+
+	centry = smb_xmalloc(sizeof(*centry));
+	centry->data = (unsigned char *)data.dptr;
+	centry->len = data.dsize;
+	centry->ofs = 0;
+
+	if (centry->len < 8) {
+		/* huh? corrupt cache? */
+		DEBUG(10,("wcache_fetch: Corrupt cache for key %s "
+			  "(len < 8) ?\n", kstr));
+		centry_free(centry);
+		free(kstr);
+		return NULL;
+	}
+	
+	centry->status = NT_STATUS(centry_uint32(centry));
+	centry->sequence_number = centry_uint32(centry);
+
+	DEBUG(10,("wcache_fetch: returning entry %s\n", kstr));
+
+	free(kstr);
+	return centry;
+}
+
+static enum winbindd_result
+cache_lookupsid_cont(struct winbindd_cli_state *state, pid_t dual)
+{
+	return cache_lookupsid(state);
+}
+
+enum winbindd_result cache_lookupsid(struct winbindd_cli_state *state)
+{
+	struct winbindd_domain *domain;
+	DOM_SID sid;
+	struct cache_entry *centry = NULL;
+	char *dom_name, *name;
+	TALLOC_CTX *mem_ctx;
+
+	/* Ensure null termination */
+	state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
+
+	DEBUG(3, ("[%5lu]: lookupsid %s\n", (unsigned long)state->pid, 
+		  state->request.data.sid));
+
+	/* Lookup sid from PDC using lsa_lookup_sids() */
+
+	if (!string_to_sid(&sid, state->request.data.sid)) {
+		DEBUG(5, ("%s not a SID\n", state->request.data.sid));
+		return WINBINDD_ERROR;
+	}
+
+	domain = find_lookup_domain_from_sid(&sid);
+
+	if (!domain) {
+		DEBUG(1,("Can't find domain from sid\n"));
+		return WINBINDD_ERROR;
+	}
+
+	/* Lookup the sid */
+
+	centry = wcache_fetch_only("SN/%s", sid_string_static(&sid));
+
+	if ((state->continuation != NULL) && /* Here the second time */
+	    (centry == NULL)) {
+		DEBUG(1, ("Internal error, no centry second time\n"));
+		return WINBINDD_ERROR;
+	}
+
+	if ((centry == NULL) || centry_expired(domain, centry))
+		state->send_to_background = True;
+
+	if (centry == NULL) {
+		state->continuation = cache_lookupsid_cont;
+		return WINBINDD_PENDING;
+	}
+
+	if (!NT_STATUS_IS_OK(centry->status)) {
+		centry_free(centry);
+		return WINBINDD_ERROR;
+	}
+
+	mem_ctx = talloc_init("winbindd_sid_to_name");
+	if (mem_ctx == NULL) {
+		centry_free(centry);
+		return WINBINDD_ERROR;
+	}
+
+	state->response.data.name.type =
+		(enum SID_NAME_USE)centry_uint32(centry);
+	dom_name = centry_string(centry, mem_ctx);
+	name = centry_string(centry, mem_ctx);
+	centry_free(centry);
+
+	fstrcpy(state->response.data.name.dom_name, dom_name);
+	fstrcpy(state->response.data.name.name, name);
+
+	talloc_destroy(mem_ctx);
+
+	return WINBINDD_OK;
+}
+
+static enum winbindd_result
+cache_lookupname_cont(struct winbindd_cli_state *state, pid_t dual)
+{
+	return cache_lookupname(state);
+}
+
+enum winbindd_result cache_lookupname(struct winbindd_cli_state *state)
+{
+	fstring domain_name, user_name;
+	DOM_SID *sid;
+	struct winbindd_domain *domain;
+	char *p;
+	struct cache_entry *centry = NULL;
+	TALLOC_CTX *mem_ctx;
+
+	/* Ensure null termination */
+	state->request.data.sid[sizeof(state->request.data.name.dom_name)-1]='\0';
+
+	/* Ensure null termination */
+	state->request.data.sid[sizeof(state->request.data.name.name)-1]='\0';
+
+	/* cope with the name being a fully qualified name */
+	p = strstr(state->request.data.name.name, lp_winbind_separator());
+	if (p != NULL) {
+		*p = 0;
+		fstrcpy(domain_name, state->request.data.name.name);
+		fstrcpy(user_name, p+1);
+	} else {
+		fstrcpy(domain_name, state->request.data.name.dom_name);
+		fstrcpy(user_name,state->request.data.name.name);
+	}
+
+	strupper_m(domain_name);
+	strupper_m(user_name);
+
+	DEBUG(3, ("[%5lu]: lookupname %s%s%s\n", (unsigned long)state->pid,
+		  domain_name, lp_winbind_separator(), user_name));
+
+	if ((domain = find_lookup_domain_from_name(domain_name)) == NULL) {
+		DEBUG(0, ("could not find domain entry for domain %s\n", 
+			  domain_name));
+		return WINBINDD_ERROR;
+	}
+
+	centry = wcache_fetch_only("NS/%s/%s", domain_name, user_name);
+
+	if ((state->continuation != NULL) && /* Here the second time */
+	    (centry == NULL)) {
+		DEBUG(1, ("Internal error, no centry second time\n"));
+		return WINBINDD_ERROR;
+	}
+
+	if ((centry == NULL) || centry_expired(domain, centry))
+		state->send_to_background = True;
+
+	if (centry == NULL) {
+		state->continuation = cache_lookupname_cont;
+		return WINBINDD_PENDING;
+	}
+
+	if (!NT_STATUS_IS_OK(centry->status)) {
+		centry_free(centry);
+		return WINBINDD_ERROR;
+	}
+
+	mem_ctx = talloc_init("winbindd_name_to_sid");
+	if (mem_ctx == NULL) {
+		centry_free(centry);
+		return WINBINDD_ERROR;
+	}
+
+	state->response.data.sid.type =
+		(enum SID_NAME_USE)centry_uint32(centry);
+
+	sid = centry_sid(centry, mem_ctx);
+	if (sid == NULL)
+		ZERO_STRUCTP(sid);
+	centry_free(centry);
+
+	sid_to_string(state->response.data.sid.sid, sid);
+
+	talloc_destroy(mem_ctx);
+
+	return WINBINDD_OK;
+}
+
+/* State machine for listing users
+   Look into the cache. 3 cases can happen:
+
+   * Data exist and is fresh
+     -> Fill buffer and directly go to next domain
+   * Data exist and is stale
+     -> Fill buffer, send to dual. When called again check next domain
+   * Data don't exist
+     -> Send to dual. When called again goto next if not there.
+
+*/
+
+struct list_entries_private {
+	struct winbindd_domain *domain;
+	BOOL expect_data;
+	char *extra_data;
+	int extra_data_len;
+};
+
+static enum winbindd_result
+cache_list_users_next(struct winbindd_cli_state *state, pid_t dual);
+
+enum winbindd_result cache_list_users(struct winbindd_cli_state *state)
+{
+	struct list_entries_private *priv;
+
+	if (state->continuation_private != NULL) {
+		DEBUG(0, ("Internal error, continuation_private != NULL\n"));
+		return WINBINDD_ERROR;
+	}
+
+	state->continuation = cache_list_users_next;
+	state->continuation_private = priv =
+		malloc(sizeof(struct list_entries_private));
+
+	priv->domain = domain_list();
+	priv->expect_data = False;
+	priv->extra_data = NULL;
+	priv->extra_data_len = 0;
+
+	return cache_list_users_next(state, -1);
+}
+
+static enum winbindd_result
+cache_list_users_next(struct winbindd_cli_state *state, pid_t dual)
+{
+	struct cache_entry *centry;
+	struct list_entries_private *priv =
+		(struct list_entries_private *)state->continuation_private;
+	int i, num_entries;
+	TALLOC_CTX *mem_ctx;
+	BOOL expired = False;
+
+ next_domain:
+
+	centry = wcache_fetch_only("UL/%s", priv->domain->name);
+
+	if ((centry == NULL) && (priv->expect_data)) {
+		/* We've been called again after the dual had been asked to
+		 * query the data */
+		priv->expect_data = False;
+		goto no_entries;
+	}
+
+	priv->expect_data = False;
+
+	if (centry == NULL) {
+		priv->expect_data = True;
+		state->send_to_background = True;
+		fstrcpy(state->request.domain_name, priv->domain->name);
+		return WINBINDD_PENDING;
+	}
+
+	num_entries = centry_uint32(centry);
+
+	priv->extra_data = Realloc(priv->extra_data,
+				   priv->extra_data_len +
+				   sizeof(fstring) * num_entries);
+
+	if (priv->extra_data == NULL) {
+		return WINBINDD_ERROR;
+	}
+
+	mem_ctx = talloc_init("cache_list_users_next");
+
+	for (i=0; i<num_entries; i++) {
+		char *p;
+		fstring acct_name, name;
+
+		p = centry_string(centry, mem_ctx);
+
+		if ((p == NULL) || (*p == '\0'))
+			fstrcpy(acct_name, "");
+		else
+			fstrcpy(acct_name, p);
+
+		fill_domain_username(name, priv->domain->name, acct_name);
+
+		memcpy(&priv->extra_data[priv->extra_data_len], name, 
+		       strlen(name));
+		priv->extra_data_len += strlen(name);
+		priv->extra_data[priv->extra_data_len++] = ',';
+
+		/* Dump full_name, user_sid and group_sid */
+		centry_string(centry, mem_ctx);
+		centry_string(centry, mem_ctx);
+		centry_string(centry, mem_ctx);
+	}
+
+	expired = centry_expired(priv->domain, centry);
+
+	centry_free(centry);
+	talloc_destroy(mem_ctx);
+
+ no_entries:
+
+	priv->domain = priv->domain->next;
+
+	if (priv->domain == NULL) {
+		state->response.length = sizeof(state->response);
+		if (priv->extra_data != NULL) {
+			priv->extra_data[priv->extra_data_len - 1] = '\0';
+			state->response.extra_data = priv->extra_data;
+			state->response.length += priv->extra_data_len;
+		}
+		SAFE_FREE(state->continuation_private);
+		return WINBINDD_OK;
+	}
+
+	if (expired) {
+		state->send_to_background = True;
+		fstrcpy(state->request.domain_name, priv->domain->name);
+		return WINBINDD_PENDING;
+	}
+
+	/* If we had tail recursion, we could call ourselves :-) */
+	goto next_domain;
+}	
+
+static enum winbindd_result
+cache_list_groups_next(struct winbindd_cli_state *state, pid_t dual);
+
+enum winbindd_result cache_list_groups(struct winbindd_cli_state *state)
+{
+	struct list_entries_private *priv;
+
+	if (state->continuation_private != NULL) {
+		DEBUG(0, ("Internal error, continuation_private != NULL\n"));
+		return WINBINDD_ERROR;
+	}
+
+	state->continuation = cache_list_groups_next;
+	state->continuation_private = priv =
+		malloc(sizeof(struct list_entries_private));
+
+	priv->domain = domain_list();
+	priv->expect_data = False;
+	priv->extra_data = NULL;
+	priv->extra_data_len = 0;
+
+	return cache_list_groups_next(state, -1);
+}
+
+static enum winbindd_result
+cache_list_groups_next(struct winbindd_cli_state *state, pid_t dual)
+{
+	struct cache_entry *centry;
+	struct list_entries_private *priv =
+		(struct list_entries_private *)state->continuation_private;
+	int i, num_entries;
+	TALLOC_CTX *mem_ctx;
+	BOOL expired = False;
+
+ next_domain:
+
+	centry = wcache_fetch_only("GL/%s/domain", priv->domain->name);
+
+	if ((centry == NULL) && (priv->expect_data)) {
+		/* We've been called again after the dual had been asked to
+		 * query the data */
+		priv->expect_data = False;
+		goto no_entries;
+	}
+
+	priv->expect_data = False;
+
+	if (centry == NULL) {
+		priv->expect_data = True;
+		state->send_to_background = True;
+		fstrcpy(state->request.domain_name, priv->domain->name);
+		return WINBINDD_PENDING;
+	}
+
+	num_entries = centry_uint32(centry);
+
+	priv->extra_data = Realloc(priv->extra_data,
+				   priv->extra_data_len +
+				   sizeof(fstring) * num_entries);
+
+	if (priv->extra_data == NULL) {
+		return WINBINDD_ERROR;
+	}
+
+	mem_ctx = talloc_init("cache_list_groups_next");
+
+	for (i=0; i<num_entries; i++) {
+		char *p;
+		fstring acct_name, name;
+
+		p = centry_string(centry, mem_ctx);
+
+		if ((p == NULL) || (*p == '\0'))
+			fstrcpy(acct_name, "");
+		else
+			fstrcpy(acct_name, p);
+
+		fill_domain_username(name, priv->domain->name, acct_name);
+
+		memcpy(&priv->extra_data[priv->extra_data_len], name, 
+		       strlen(name));
+		priv->extra_data_len += strlen(name);
+		priv->extra_data[priv->extra_data_len++] = ',';
+
+		/* Dump acct_desc and group_rid */
+		centry_string(centry, mem_ctx);
+		centry_uint32(centry);
+	}
+
+	expired = centry_expired(priv->domain, centry);
+
+	centry_free(centry);
+	talloc_destroy(mem_ctx);
+
+ no_entries:
+
+	priv->domain = priv->domain->next;
+
+	if (priv->domain == NULL) {
+		state->response.length = sizeof(state->response);
+		if (priv->extra_data != NULL) {
+			priv->extra_data[priv->extra_data_len - 1] = '\0';
+			state->response.extra_data = priv->extra_data;
+			state->response.length += priv->extra_data_len;
+		}
+		SAFE_FREE(state->continuation_private);
+		return WINBINDD_OK;
+	}
+
+	if (expired) {
+		state->send_to_background = True;
+		fstrcpy(state->request.domain_name, priv->domain->name);
+		return WINBINDD_PENDING;
+	}
+
+	/* If we had tail recursion, we could call ourselves :-) */
+	goto next_domain;
+}	
+
+static enum winbindd_result
+cache_getusersids_cont(struct winbindd_cli_state *state, pid_t dual)
+{
+	return cache_getusersids(state);
+}
+
+enum winbindd_result cache_getusersids(struct winbindd_cli_state *state)
+{
+	DOM_SID user_sid;
+	DOM_SID **user_sids;
+	struct winbindd_domain *domain;
+	unsigned int i;
+	TALLOC_CTX *mem_ctx;
+	char *ret = NULL;
+	uint32 num_groups;
+	unsigned ofs, ret_size = 0;
+	struct cache_entry *centry = NULL;
+
+	/* Ensure null termination */
+	state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
+
+	if (!string_to_sid(&user_sid, state->request.data.sid)) {
+		DEBUG(1, ("Could not convert sid %s from string\n",
+			  state->request.data.sid));
+		return WINBINDD_ERROR;
+	}
+
+	DEBUG(10, ("cache_getusersids called for sid %s, cont=%x\n",
+		   sid_string_static(&user_sid), state->continuation));
+
+	domain = find_lookup_domain_from_sid(&user_sid);
+
+	if (!domain) {
+		DEBUG(1,("Can't find domain from sid\n"));
+		return WINBINDD_ERROR;
+	}
+
+	centry = wcache_fetch_only("UG/%s", sid_string_static(&user_sid));
+
+#if 1
+	/* Torture code ... */
+	if (state->continuation == NULL) {
+		centry_free(centry);
+		centry = NULL;
+	}
+#endif
+
+	if ((state->continuation != NULL) && /* Here the second time */
+	    (centry == NULL)) {
+		DEBUG(1, ("Internal error, no centry second time\n"));
+		return WINBINDD_ERROR;
+	}
+
+	if ((centry == NULL) || centry_expired(domain, centry))
+		state->send_to_background = True;
+
+	if (centry == NULL) {
+		state->continuation = cache_getusersids_cont;
+		return WINBINDD_PENDING;
+	}
+
+	mem_ctx = talloc_init("winbindd_getusersids(%s)",
+			      state->request.data.username);
+
+	if (mem_ctx == NULL) {
+		centry_free(centry);
+		return WINBINDD_ERROR;
+	}
+
+	num_groups = centry_uint32(centry);
+
+	user_sids = talloc(mem_ctx, sizeof(*user_sids) * num_groups);
+	if ((num_groups != 0) && (user_sids == NULL))
+		smb_panic("cache_getusersids out of memory");
+
+	for (i=0; i<num_groups; i++)
+		user_sids[i] = centry_sid(centry, mem_ctx);
+
+	centry_free(centry);
+
+	/* work out the response size */
+	for (i = 0; i < num_groups; i++) {
+		const char *s = sid_string_static(user_sids[i]);
+		ret_size += strlen(s) + 1;
+	}
+
+	ret = malloc(ret_size);
+	if ((ret_size != 0) && (ret == NULL))
+		smb_panic("cache_getusersids out of memory");
+
+	ofs = 0;
+	for (i = 0; i < num_groups; i++) {
+		const char *s = sid_string_static(user_sids[i]);
+		safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
+		ofs += strlen(ret+ofs) + 1;
+	}
+
+	/* Send data back to client */
+	state->response.data.num_entries = num_groups;
+	state->response.extra_data = ret;
+	state->response.length += ret_size;
+
+	talloc_destroy(mem_ctx);
+
+	return WINBINDD_OK;
+}
+
+void cache_store_response(pid_t pid, struct winbindd_response *response)
+{
+	TDB_DATA key, data;
+	fstring key_str;
+
+	fstr_sprintf(key_str, "DR/%d", pid);
+	key.dptr = key_str;
+	key.dsize = strlen(key_str);
+	data.dptr = (void *)response;
+	data.dsize = sizeof(*response);
+	if (tdb_store(wcache->tdb, key, data, TDB_REPLACE) == -1)
+		return;
+
+	if (response->length == sizeof(*response))
+		return;
+
+	/* There's extra data */
+
+	fstr_sprintf(key_str, "DE/%d", pid);
+	key.dptr = key_str;
+	key.dsize = strlen(key_str);
+	data.dptr = response->extra_data;
+	data.dsize = response->length - sizeof(*response);
+	if (tdb_store(wcache->tdb, key, data, TDB_REPLACE) == 0)
+		return;
+
+	/* We could not store the extra data, make sure the tdb does not
+	 * contain a main record with wrong dangling extra data */
+
+	fstr_sprintf(key_str, "DR/%d", pid);
+	key.dptr = key_str;
+	key.dsize = strlen(key_str);
+	tdb_delete(wcache->tdb, key);
+
+	return;
+}
+
+static BOOL cache_retrieve_response(pid_t pid,
+				    struct winbindd_response * response)
+{
+	TDB_DATA key, data;
+	fstring key_str;
+
+	fstr_sprintf(key_str, "DR/%d", pid);
+	key.dptr = key_str;
+	key.dsize = strlen(key_str);
+
+	data = tdb_fetch(wcache->tdb, key);
+
+	if (data.dptr == NULL)
+		return False;
+
+	if (data.dsize != sizeof(*response))
+		return False;
+
+	memcpy(response, data.dptr, data.dsize);
+	SAFE_FREE(data.dptr);
+
+	if (response->length == sizeof(*response))
+		return True;
+
+	/* There's extra data */
+
+	fstr_sprintf(key_str, "DE/%d", pid);
+	key.dptr = key_str;
+	key.dsize = strlen(key_str);
+
+	data = tdb_fetch(wcache->tdb, key);
+
+	if (data.dptr == NULL)
+		return False;
+
+	if (data.dsize != (response->length - sizeof(*response))) {
+		SAFE_FREE(data.dptr);
+		return False;
+	}
+
+	response->extra_data = data.dptr;
+	return True;
+}
+
+static enum winbindd_result dual_response(struct winbindd_cli_state *state,
+					  pid_t dual)
+{
+	if (!cache_retrieve_response(dual, &state->response))
+		return WINBINDD_ERROR;
+	return state->response.result;
+}
+
+enum winbindd_result dual_request(struct winbindd_cli_state *state)
+{
+	state->send_to_background = True;
+	state->request.flags |= WBFLAG_CACHE_RESPONSE;
+	state->continuation = dual_response;
+	return WINBINDD_PENDING;
+}
diff -Npur --exclude=CVS --exclude='*.bak' --exclude='*.o' --exclude='*.po' --exclude='*.so' --exclude='.#*' --exclude=Makefile --exclude=stamp-h --exclude=configure --exclude=findsmb --exclude='*proto*.h' --exclude=build_env.h --exclude=tdbsam2_parse_info.h --exclude='config.*' --exclude=bin --exclude='*.configure' --exclude=autom4te.cache --exclude='build_options.c*' --exclude=TAGS --exclude='*~' --exclude=gen-8bit-gap.sh --exclude=smbadduser --exclude=version.h --exclude=.svn --exclude=.cvsignore --exclude='*.rej' --exclude='*.orig' ../../samba-3.0.7/source/nsswitch/winbindd_cm.c ./nsswitch/winbindd_cm.c
--- ../../samba-3.0.7/source/nsswitch/winbindd_cm.c	2004-09-16 14:30:16.000000000 +0200
+++ ./source/nsswitch/winbindd_cm.c	2004-09-15 18:15:38.000000000 +0200
@@ -505,6 +505,8 @@ static BOOL get_dcs_1c(TALLOC_CTX *mem_c
 		}
 	}
 
+	SAFE_FREE(iplist);
+
 	return True;
 }
 
diff -Npur --exclude=CVS --exclude='*.bak' --exclude='*.o' --exclude='*.po' --exclude='*.so' --exclude='.#*' --exclude=Makefile --exclude=stamp-h --exclude=configure --exclude=findsmb --exclude='*proto*.h' --exclude=build_env.h --exclude=tdbsam2_parse_info.h --exclude='config.*' --exclude=bin --exclude='*.configure' --exclude=autom4te.cache --exclude='build_options.c*' --exclude=TAGS --exclude='*~' --exclude=gen-8bit-gap.sh --exclude=smbadduser --exclude=version.h --exclude=.svn --exclude=.cvsignore --exclude='*.rej' --exclude='*.orig' ../../samba-3.0.7/source/nsswitch/winbindd_dual.c ./nsswitch/winbindd_dual.c
--- ../../samba-3.0.7/source/nsswitch/winbindd_dual.c	2004-04-04 09:37:18.000000000 +0200
+++ ./source/nsswitch/winbindd_dual.c	2004-09-15 18:15:38.000000000 +0200
@@ -42,32 +42,107 @@ int dual_daemon_pipe = -1;
 
 /* a list of requests ready to be sent to the dual daemon */
 struct dual_list {
-	struct dual_list *next;
+	struct dual_list *next, *prev;
+	char *data;
+	int length;
+};
+
+struct dual_child {
+	struct dual_child *next, *prev;
+	BOOL busy;
+	pid_t pid;
+	int fd;
 	char *data;
 	int length;
 	int offset;
 };
 
+static struct dual_child *child_list;
+
 static struct dual_list *dual_list;
-static struct dual_list *dual_list_end;
+
+static BOOL dual_schedule_request(void)
+{
+	struct dual_child *child;
+	int busy_children = 0;
+
+	if (dual_list == NULL)
+		return False;
+
+	for (child = child_list; child != NULL; child = child->next) {
+		struct dual_list *this;
+
+		if (child->busy) {
+			extern int max_busy_children;
+			busy_children += 1;
+			if (busy_children > max_busy_children)
+				max_busy_children = busy_children;
+			continue;
+		}
+
+		SMB_ASSERT(child->data == NULL);
+
+		DEBUG(10, ("scheduling %d\n",
+			   ((struct winbindd_request *)(dual_list->data))->cmd));
+
+		child->data = dual_list->data;
+		child->length = dual_list->length;
+		child->offset = 0;
+		child->busy = True;
+
+		this = dual_list;
+
+		DLIST_REMOVE(dual_list, this);
+		free(this);
+
+		return True;
+	}
+	return False;
+}
+
+void dual_finished(pid_t pid)
+{
+	struct dual_child *child;
+
+	for (child = child_list; child != NULL; child = child->next) {
+		if (child->pid == pid) {
+			child->busy = False;
+			return;
+		}
+	}
+}
 
 /*
   setup a select() including the dual daemon pipe
  */
 int dual_select_setup(fd_set *fds, int maxfd)
 {
-	if (dual_daemon_pipe == -1 ||
-	    !dual_list) {
-		return maxfd;
-	}
+	struct dual_child *child;
 
-	FD_SET(dual_daemon_pipe, fds);
-	if (dual_daemon_pipe > maxfd) {
-		maxfd = dual_daemon_pipe;
+	while (dual_schedule_request())
+		;
+
+	for (child = child_list; child != NULL; child = child->next) {
+		if (child->length == 0)
+			continue;
+
+		FD_SET(child->fd, fds);
+		if (child->fd > maxfd)
+			maxfd = child->fd;
 	}
+
 	return maxfd;
 }
 
+static void resend_request(char *data, int length)
+{
+	struct dual_list *req;
+
+	req = malloc(sizeof(*req));
+	req->data = data;
+	req->length = length;
+	DLIST_ADD(dual_list, req);
+}
 
 /*
   a hook called from the main winbindd select() loop to handle writes
@@ -76,34 +151,54 @@ int dual_select_setup(fd_set *fds, int m
 void dual_select(fd_set *fds)
 {
 	int n;
+	struct dual_child *child;
 
-	if (dual_daemon_pipe == -1 ||
-	    !dual_list ||
-	    !FD_ISSET(dual_daemon_pipe, fds)) {
-		return;
-	}
+	for (child = child_list; child != NULL; child = child->next) {
+		if (child->length == 0)
+			continue;
+
+		if (!FD_ISSET(child->fd, fds))
+			continue;
+
+		n = sys_write(child->fd,
+			      &child->data[child->offset],
+			      child->length - child->offset);
+
+		if (n <= 0) {
+			/* the pipe is dead! */
+			resend_request(child->data, child->length);
+			child->fd = -1;
+			continue;
+		}
 
-	n = sys_write(dual_daemon_pipe, 
-		  &dual_list->data[dual_list->offset],
-		  dual_list->length - dual_list->offset);
-
-	if (n <= 0) {
-		/* the pipe is dead! fall back to normal operation */
-		dual_daemon_pipe = -1;
-		return;
+		child->offset += n;
+
+		if (child->offset < child->length)
+			continue;
+
+		/* Data fully sent, discard it */
+
+		SAFE_FREE(child->data);
+		child->length = 0;
 	}
 
-	dual_list->offset += n;
+	/* Remove dead children */
+	child = child_list;
 
-	if (dual_list->offset == dual_list->length) {
-		struct dual_list *next;
-		next = dual_list->next;
-		free(dual_list->data);
-		free(dual_list);
-		dual_list = next;
-		if (!dual_list) {
-			dual_list_end = NULL;
+	while (child != NULL) {
+		struct dual_child *next;
+		next = child->next;
+		if (child->fd == -1) {
+			DLIST_REMOVE(child_list, child);
+			free(child);
 		}
+		child = next;
+	}
+
+	if (child_list == NULL) {
+		extern BOOL opt_dual_daemon;
+		DEBUG(0, ("All children died -- normal operation\n"));
+		opt_dual_daemon = False;
 	}
 }
 
@@ -113,25 +208,20 @@ void dual_select(fd_set *fds)
 */
 void dual_send_request(struct winbindd_cli_state *state)
 {
-	struct dual_list *list;
+	struct dual_list *req, *tmp;
 
 	if (!background_process) return;
 
-	list = malloc(sizeof(*list));
-	if (!list) return;
+	DEBUG(10, ("dual_send_request: cmd=%d, msgid=%d\n",
+		   state->request.cmd, state->request.msgid));
 
-	list->next = NULL;
-	list->data = memdup(&state->request, sizeof(state->request));
-	list->length = sizeof(state->request);
-	list->offset = 0;
-	
-	if (!dual_list_end) {
-		dual_list = list;
-		dual_list_end = list;
-	} else {
-		dual_list_end->next = list;
-		dual_list_end = list;
-	}
+	req = malloc(sizeof(*req));
+	if (!req) return;
+
+	req->next = NULL;
+	req->data = memdup(&state->request, sizeof(state->request));
+	req->length = sizeof(state->request);
+	DLIST_ADD_END(dual_list, req, tmp);
 
 	background_process = False;
 }
@@ -144,6 +234,7 @@ void do_dual_daemon(void)
 {
 	int fdpair[2];
 	struct winbindd_cli_state state;
+	struct dual_child *child;
 	
 	if (pipe(fdpair) != 0) {
 		return;
@@ -152,10 +243,23 @@ void do_dual_daemon(void)
 	ZERO_STRUCT(state);
 	state.pid = getpid();
 
-	dual_daemon_pipe = fdpair[1];
+	child = malloc(sizeof(*child));
+
+	if (child == NULL)
+		return;
+
+	child->busy = False;
+	child->data = NULL;
+	child->length = 0;
+	child->offset = 0;
+
+	child->fd = fdpair[1];
 	state.sock = fdpair[0];
+	child->pid = sys_fork();
+
+	DLIST_ADD(child_list, child)
 
-	if (fork() != 0) {
+	if (child->pid != 0) {
 		close(fdpair[0]);
 		return;
 	}
@@ -166,6 +270,11 @@ void do_dual_daemon(void)
 		DEBUG(0,("tdb_reopen_all failed.\n"));
 		_exit(0);
 	}
+
+	if (!message_init()) {
+		DEBUG(0, ("message_init failed\n"));
+		_exit(0);
+	}
 	
 	dual_daemon_pipe = -1;
 	opt_dual_daemon = False;
@@ -202,6 +311,15 @@ void do_dual_daemon(void)
 			}
 
 			winbind_process_packet(&state);
+
+			if (state.request.flags & WBFLAG_CACHE_RESPONSE)
+				cache_store_response(getpid(),
+						     &state.response);
+
+			message_send_pid(getppid(), MSG_WINBIND_FINISHED,
+					 &state.request.msgid,
+					 sizeof(state.request.msgid),
+					 True);
 			SAFE_FREE(state.response.extra_data);
 
 			free_getent_state(state.getpwent_state);
diff -Npur --exclude=CVS --exclude='*.bak' --exclude='*.o' --exclude='*.po' --exclude='*.so' --exclude='.#*' --exclude=Makefile --exclude=stamp-h --exclude=configure --exclude=findsmb --exclude='*proto*.h' --exclude=build_env.h --exclude=tdbsam2_parse_info.h --exclude='config.*' --exclude=bin --exclude='*.configure' --exclude=autom4te.cache --exclude='build_options.c*' --exclude=TAGS --exclude='*~' --exclude=gen-8bit-gap.sh --exclude=smbadduser --exclude=version.h --exclude=.svn --exclude=.cvsignore --exclude='*.rej' --exclude='*.orig' ../../samba-3.0.7/source/nsswitch/winbindd_misc.c ./nsswitch/winbindd_misc.c
--- ../../samba-3.0.7/source/nsswitch/winbindd_misc.c	2004-04-04 09:37:17.000000000 +0200
+++ ./source/nsswitch/winbindd_misc.c	2004-09-15 18:15:38.000000000 +0200
@@ -244,11 +244,13 @@ enum winbindd_result winbindd_ping(struc
 
 enum winbindd_result winbindd_info(struct winbindd_cli_state *state)
 {
+	extern int max_busy_children;
 
 	DEBUG(3, ("[%5lu]: request misc info\n", (unsigned long)state->pid));
 
 	state->response.data.info.winbind_separator = *lp_winbind_separator();
 	fstrcpy(state->response.data.info.samba_version, SAMBA_VERSION_STRING);
+	state->response.data.info.max_busy_children = max_busy_children;
 
 	return WINBINDD_OK;
 }
diff -Npur --exclude=CVS --exclude='*.bak' --exclude='*.o' --exclude='*.po' --exclude='*.so' --exclude='.#*' --exclude=Makefile --exclude=stamp-h --exclude=configure --exclude=findsmb --exclude='*proto*.h' --exclude=build_env.h --exclude=tdbsam2_parse_info.h --exclude='config.*' --exclude=bin --exclude='*.configure' --exclude=autom4te.cache --exclude='build_options.c*' --exclude=TAGS --exclude='*~' --exclude=gen-8bit-gap.sh --exclude=smbadduser --exclude=version.h --exclude=.svn --exclude=.cvsignore --exclude='*.rej' --exclude='*.orig' ../../samba-3.0.7/source/nsswitch/winbindd_nss.h ./nsswitch/winbindd_nss.h
--- ../../samba-3.0.7/source/nsswitch/winbindd_nss.h	2004-04-20 22:42:55.000000000 +0200
+++ ./source/nsswitch/winbindd_nss.h	2004-09-15 18:15:38.000000000 +0200
@@ -159,6 +159,7 @@ typedef struct winbindd_gr {
 #define WBFLAG_PAM_UNIX_NAME            0x0080
 #define WBFLAG_PAM_AFS_TOKEN            0x0100
 #define WBFLAG_PAM_NT_STATUS_SQUASH     0x0200
+#define WBFLAG_CACHE_RESPONSE           0x0400
 
 /* Winbind request structure */
 
@@ -169,6 +170,10 @@ struct winbindd_request {
 	uint32 flags;            /* flags relavant to a given request */
 	fstring domain_name;	/* name of domain for which the request applies */
 
+	int msgid; 		/* Uniquely identify a request, used in
+				 * MSG_WINBINDD_FINISHED from dual daemon to
+				 * parent. */
+
 	union {
 		fstring winsreq;     /* WINS request */
 		fstring username;    /* getpwnam */
@@ -217,7 +222,8 @@ struct winbindd_request {
 
 enum winbindd_result {
 	WINBINDD_ERROR,
-	WINBINDD_OK
+	WINBINDD_OK,
+	WINBINDD_PENDING
 };
 
 /* Winbind response structure */
@@ -259,6 +265,7 @@ struct winbindd_response {
 		struct winbindd_info {
 			char winbind_separator;
 			fstring samba_version;
+			int max_busy_children;
 		} info;
 		fstring domain_name;
 		fstring netbios_name;
diff -Npur --exclude=CVS --exclude='*.bak' --exclude='*.o' --exclude='*.po' --exclude='*.so' --exclude='.#*' --exclude=Makefile --exclude=stamp-h --exclude=configure --exclude=findsmb --exclude='*proto*.h' --exclude=build_env.h --exclude=tdbsam2_parse_info.h --exclude='config.*' --exclude=bin --exclude='*.configure' --exclude=autom4te.cache --exclude='build_options.c*' --exclude=TAGS --exclude='*~' --exclude=gen-8bit-gap.sh --exclude=smbadduser --exclude=version.h --exclude=.svn --exclude=.cvsignore --exclude='*.rej' --exclude='*.orig' ../../samba-3.0.7/source/nsswitch/winbindd_sid.c ./nsswitch/winbindd_sid.c
--- ../../samba-3.0.7/source/nsswitch/winbindd_sid.c	2004-09-16 14:30:16.000000000 +0200
+++ ./source/nsswitch/winbindd_sid.c	2004-09-16 12:40:22.000000000 +0200
@@ -494,3 +494,147 @@ enum winbindd_result winbindd_allocate_r
 
 	return WINBINDD_OK;
 }
+
+enum winbindd_result cache_sid_to_uid(struct winbindd_cli_state *state)
+{
+	DOM_SID sid;
+	uint32 flags = ID_CACHE_ONLY;
+	NTSTATUS result;
+
+	/* Ensure null termination */
+	state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
+
+	DEBUG(3, ("[%5lu]: sid to uid %s\n", (unsigned long)state->pid,
+		  state->request.data.sid));
+
+	if (!string_to_sid(&sid, state->request.data.sid)) {
+		DEBUG(1, ("Could not convert sid %s from string\n",
+			  state->request.data.sid));
+		return WINBINDD_ERROR;
+	}
+	
+	if ( state->request.flags & WBFLAG_QUERY_ONLY ) 
+		flags |= ID_QUERY_ONLY;
+	
+	/* Find uid for this sid and return it */
+
+	result = idmap_sid_to_uid(&sid, &(state->response.data.uid), flags);
+
+	if (NT_STATUS_IS_OK(result))
+		return WINBINDD_OK;
+
+	if (state->continuation != NULL) {
+		/* Here the second time, the SID could not be mapped by the
+		 * dual daemon */
+		return WINBINDD_ERROR;
+	}
+
+	state->send_to_background = True;
+	state->continuation = cache_sid_to_uid;
+	return WINBINDD_PENDING;
+}
+
+enum winbindd_result cache_sid_to_gid(struct winbindd_cli_state *state)
+{
+	DOM_SID sid;
+	uint32 flags = ID_CACHE_ONLY;
+	NTSTATUS result;
+
+	/* Ensure null termination */
+	state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
+
+	DEBUG(3, ("[%5lu]: sid to uid %s\n", (unsigned long)state->pid,
+		  state->request.data.sid));
+
+	if (!string_to_sid(&sid, state->request.data.sid)) {
+		DEBUG(1, ("Could not convert sid %s from string\n",
+			  state->request.data.sid));
+		return WINBINDD_ERROR;
+	}
+	
+	if ( state->request.flags & WBFLAG_QUERY_ONLY ) 
+		flags |= ID_QUERY_ONLY;
+	
+	/* Find uid for this sid and return it */
+
+	result = idmap_sid_to_gid(&sid, &(state->response.data.uid), flags);
+
+	if (NT_STATUS_IS_OK(result))
+		return WINBINDD_OK;
+
+	if (state->continuation != NULL) {
+		/* Here the second time, the SID could not be mapped by the
+		 * dual daemon */
+		return WINBINDD_ERROR;
+	}
+
+	state->send_to_background = True;
+	state->continuation = cache_sid_to_gid;
+	return WINBINDD_PENDING;
+}
+
+enum winbindd_result cache_uid_to_sid(struct winbindd_cli_state *state)
+{
+	DOM_SID sid;
+	unid_t id;
+	NTSTATUS result;
+
+	DEBUG(3, ("[%5lu]: uid to sid %lu\n", (unsigned long)state->pid, 
+		  (unsigned long)state->request.data.uid));
+
+	/* Lookup rid for this uid */
+
+	id.uid = state->request.data.uid;
+
+	result = idmap_get_sid_from_id(&sid, id,
+				       ID_USERID|ID_QUERY_ONLY|ID_CACHE_ONLY);
+
+	if (NT_STATUS_IS_OK(result)) {
+		sid_to_string(state->response.data.sid.sid, &sid);
+		state->response.data.sid.type = SID_NAME_USER;
+		return WINBINDD_OK;
+	}
+
+	if (state->continuation != NULL) {
+		/* Here the second time, the SID could not be mapped by the
+		 * dual daemon */
+		return WINBINDD_ERROR;
+	}
+
+	state->send_to_background = True;
+	state->continuation = cache_uid_to_sid;
+	return WINBINDD_PENDING;
+}
+
+enum winbindd_result cache_gid_to_sid(struct winbindd_cli_state *state)
+{
+	DOM_SID sid;
+	unid_t id;
+	NTSTATUS result;
+
+	DEBUG(3, ("[%5lu]: gid to sid %lu\n", (unsigned long)state->pid, 
+		  (unsigned long)state->request.data.uid));
+
+	/* Lookup rid for this gid */
+
+	id.uid = state->request.data.gid;
+
+	result = idmap_get_sid_from_id(&sid, id,
+				       ID_GROUPID|ID_QUERY_ONLY|ID_CACHE_ONLY);
+
+	if (NT_STATUS_IS_OK(result)) {
+		sid_to_string(state->response.data.sid.sid, &sid);
+		state->response.data.sid.type = SID_NAME_DOM_GRP;
+		return WINBINDD_OK;
+	}
+
+	if (state->continuation != NULL) {
+		/* Here the second time, the SID could not be mapped by the
+		 * dual daemon */
+		return WINBINDD_ERROR;
+	}
+
+	state->send_to_background = True;
+	state->continuation = cache_gid_to_sid;
+	return WINBINDD_PENDING;
+}
diff -Npur --exclude=CVS --exclude='*.bak' --exclude='*.o' --exclude='*.po' --exclude='*.so' --exclude='.#*' --exclude=Makefile --exclude=stamp-h --exclude=configure --exclude=findsmb --exclude='*proto*.h' --exclude=build_env.h --exclude=tdbsam2_parse_info.h --exclude='config.*' --exclude=bin --exclude='*.configure' --exclude=autom4te.cache --exclude='build_options.c*' --exclude=TAGS --exclude='*~' --exclude=gen-8bit-gap.sh --exclude=smbadduser --exclude=version.h --exclude=.svn --exclude=.cvsignore --exclude='*.rej' --exclude='*.orig' ../../samba-3.0.7/source/nsswitch/winbindd_util.c ./nsswitch/winbindd_util.c
--- ../../samba-3.0.7/source/nsswitch/winbindd_util.c	2004-07-08 19:06:11.000000000 +0200
+++ ./source/nsswitch/winbindd_util.c	2004-09-15 18:15:38.000000000 +0200
@@ -175,7 +175,7 @@ static struct winbindd_domain *add_trust
 	/* Link to domain list */
 	DLIST_ADD(_domain_list, domain);
         
-	DEBUG(2,("Added domain %s %s %s\n", 
+	DEBUG(1,("Added domain %s %s %s\n", 
 		 domain->name, domain->alt_name,
 		 &domain->sid?sid_string_static(&domain->sid):""));
         
@@ -303,9 +303,16 @@ BOOL init_domain_list(void)
 		domain = add_trusted_domain(get_global_sam_name(), NULL,
 					    &passdb_methods, get_global_sam_sid());
 	} else {
+
+		DOM_SID our_sid;
+
+		if (!secrets_fetch_domain_sid(lp_workgroup(), &our_sid)) {
+			DEBUG(0, ("Could not fetch our SID - did we join?\n"));
+			return False;
+		}
 	
 		domain = add_trusted_domain( lp_workgroup(), lp_realm(),
-					     &cache_methods, NULL);
+					     &cache_methods, &our_sid);
 	
 		/* set flags about native_mode, active_directory */
 		set_dc_type_and_flags(domain);
@@ -736,6 +743,14 @@ void winbindd_remove_client(struct winbi
 	_num_clients--;
 }
 
+/* Demote a client to be the last in the list */
+
+void winbindd_demote_client(struct winbindd_cli_state *cli)
+{
+	struct winbindd_cli_state *tmp;
+	DLIST_DEMOTE(_client_list, cli, tmp);
+}
+
 /* Close all open clients */
 
 void winbindd_kill_all_clients(void)
Binary files ../../samba-3.0.7/source/tdb/tdbdump and ./tdb/tdbdump differ
Binary files ../../samba-3.0.7/source/tdb/tdbtest and ./tdb/tdbtest differ
Binary files ../../samba-3.0.7/source/tdb/tdbtool and ./tdb/tdbtool differ
Binary files ../../samba-3.0.7/source/tdb/tdbtorture and ./tdb/tdbtorture differ


More information about the samba-technical mailing list