[PATCHES] SMB3 Multi-Channel: connection passing and enablement

Michael Adam obnox at samba.org
Tue Jan 26 09:30:48 UTC 2016


Hi all,

based on the previous patches
(already upstream or currently in autobuild),

here are the somwhat polished patchsets that allow one to start
playing with multi-channel:

The first patchset implements connection passing based
on the client guid. There is one TODO on the patch that
changes smbd_server_connection_terminate() to only
exit the server when the last connection is terminated.
(There is more stuff to do, but otherwise pretty clean
already, imho.)

The second patchset is just the addition of the
'server multi channel support' option to loadparm
and the enablement of MC if this is set to yes.

Patches for tests and some server subtleties will follow.

Cheers - Michael
-------------- next part --------------
From e458636dd6f6310111fc5f5b3d2fb5a7377f5210 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 24 Jun 2014 07:43:27 +0200
Subject: [PATCH 1/9] idl:messagaing: add MSG_SMBXSRV_CONNECTION_PASS

---
 librpc/idl/messaging.idl | 1 +
 1 file changed, 1 insertion(+)

diff --git a/librpc/idl/messaging.idl b/librpc/idl/messaging.idl
index ca99f41..6232322 100644
--- a/librpc/idl/messaging.idl
+++ b/librpc/idl/messaging.idl
@@ -126,6 +126,7 @@ interface messaging
 
 		/* smbXsrv messages */
 		MSG_SMBXSRV_SESSION_CLOSE	= 0x0600,
+		MSG_SMBXSRV_CONNECTION_PASS	= 0x0601,
 
 		/* source4 and NTVFS smb server messages */
 		MSG_BRL_RETRY                   = 0x0700,
-- 
2.5.0


From 3a4ad69782f4afcffefba527a3973e686f686b1b Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Mon, 23 Jun 2014 12:17:04 +0200
Subject: [PATCH 2/9] idl:smbXsrv: add smbXsrv_client_global structures

This is for marshalling smbXsrv_client.

Pair-Programmed-With: Michael Adam <obnox at samba.org>

Signed-off-by: Michael Adam <obnox at samba.org>
---
 source3/librpc/idl/smbXsrv.idl | 44 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 44 insertions(+)

diff --git a/source3/librpc/idl/smbXsrv.idl b/source3/librpc/idl/smbXsrv.idl
index 5fc3603..69aa8fe 100644
--- a/source3/librpc/idl/smbXsrv.idl
+++ b/source3/librpc/idl/smbXsrv.idl
@@ -79,10 +79,39 @@ interface smbXsrv
 
 	/* client */
 
+	typedef struct {
+		[ignore] db_record 			*db_rec;
+		server_id				server_id;
+		[charset(UTF8),string] char		local_address[];
+		[charset(UTF8),string] char		remote_address[];
+		[charset(UTF8),string] char		remote_name[];
+		NTTIME					initial_connect_time;
+		GUID					client_guid;
+		boolean8				stored;
+	} smbXsrv_client_global0;
+
+	typedef union {
+		[case(0)] smbXsrv_client_global0	*info0;
+		[default] hyper				*dummy;
+	} smbXsrv_client_globalU;
+
 	typedef [public] struct {
+		smbXsrv_version_values			version;
+		uint32					seqnum;
+		[switch_is(version)] smbXsrv_client_globalU info;
+	} smbXsrv_client_globalB;
+
+	void smbXsrv_client_global_decode(
+		[in] smbXsrv_client_globalB blob
+		);
+
+	typedef [public] struct {
+		[ignore] smbXsrv_client_table		*table;
 		[ignore] struct tevent_context		*ev_ctx;
 		[ignore] struct messaging_context	*msg_ctx;
 
+		[ref] smbXsrv_client_global0		*global;
+
 		/*
 		 * There's just one 'sconn' per client.
 		 * It holds the FSA layer details, which are global
@@ -115,6 +144,21 @@ interface smbXsrv
 		boolean8		server_multi_channel_enabled;
 	} smbXsrv_client;
 
+	typedef union {
+		[case(0)] smbXsrv_client		*info0;
+		[default] hyper				*dummy;
+	} smbXsrv_clientU;
+
+	typedef [public] struct {
+		smbXsrv_version_values			version;
+		[value(0)] uint32			reserved;
+		[switch_is(version)] smbXsrv_clientU	info;
+	} smbXsrv_clientB;
+
+	void smbXsrv_client_decode(
+		[in] smbXsrv_clientB blob
+		);
+
 	/* sessions */
 
 	typedef [public,bitmap8bit] bitmap {
-- 
2.5.0


From e5540d5153a9ad25f123f9656134f135ca22919d Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 26 Jan 2016 00:39:35 +0100
Subject: [PATCH 3/9] idl:smbXsrv: add smbXsrv_connection_pass structures.

To be used for the connection passing message.

Pair-Programmed-With: Michael Adam <obnox at samba.org>

Signed-off-by: Michael Adam <obnox at samba.org>
---
 source3/librpc/idl/smbXsrv.idl | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/source3/librpc/idl/smbXsrv.idl b/source3/librpc/idl/smbXsrv.idl
index 69aa8fe..fe86545 100644
--- a/source3/librpc/idl/smbXsrv.idl
+++ b/source3/librpc/idl/smbXsrv.idl
@@ -159,6 +159,31 @@ interface smbXsrv
 		[in] smbXsrv_clientB blob
 		);
 
+	/*
+	 * smbXsrv_connection_pass is used in the MSG_SMBXSRV_CONNECTION_PASS
+	 * message
+	 */
+	typedef struct {
+		NTTIME					initial_connect_time;
+		GUID					client_guid;
+		DATA_BLOB 				negotiate_request;
+	} smbXsrv_connection_pass0;
+
+	typedef union {
+		[case(0)] smbXsrv_connection_pass0	*info0;
+		[default] hyper				*dummy;
+	} smbXsrv_connection_passU;
+
+	typedef [public] struct {
+		smbXsrv_version_values			version;
+		[value(0)] uint32			reserved;
+		[switch_is(version)] smbXsrv_connection_passU	info;
+	} smbXsrv_connection_passB;
+
+	void smbXsrv_connection_pass_decode(
+		[in] smbXsrv_connection_passB blob
+		);
+
 	/* sessions */
 
 	typedef [public,bitmap8bit] bitmap {
-- 
2.5.0


From 109f9b2bb9977f75be510526ae432cc8f88c02aa Mon Sep 17 00:00:00 2001
From: Michael Adam <obnox at samba.org>
Date: Mon, 25 Jan 2016 22:50:28 +0100
Subject: [PATCH 4/9] smbd:globals.h: add guid_verified to
 smbXsrvi_connection.smb2.client

Signed-off-by: Michael Adam <obnox at samba.org>
---
 source3/smbd/globals.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h
index 90d8dcc..7af0975 100644
--- a/source3/smbd/globals.h
+++ b/source3/smbd/globals.h
@@ -501,6 +501,7 @@ struct smbXsrv_connection {
 		struct {
 			uint32_t capabilities;
 			struct GUID guid;
+			bool guid_verified;
 			uint16_t security_mode;
 			uint16_t num_dialects;
 			uint16_t *dialects;
-- 
2.5.0


From 66172529badfade4a886ba2074efc046a059d273 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Tue, 26 Jan 2016 01:10:25 +0100
Subject: [PATCH 5/9] smbd: add smbXsrv_client.c

Pair-Programmed-With: Michael Adam <obnox at samba.org>

Signed-off-by: Michael Adam <obnox at samba.org>
---
 source3/smbd/globals.h        |  16 +
 source3/smbd/smbXsrv_client.c | 770 ++++++++++++++++++++++++++++++++++++++++++
 source3/wscript_build         |   1 +
 3 files changed, 787 insertions(+)
 create mode 100644 source3/smbd/smbXsrv_client.c

diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h
index 7af0975..1ca1389 100644
--- a/source3/smbd/globals.h
+++ b/source3/smbd/globals.h
@@ -528,6 +528,22 @@ const char *smbXsrv_connection_dbg(const struct smbXsrv_connection *xconn);
 NTSTATUS smbXsrv_version_global_init(const struct server_id *server_id);
 uint32_t smbXsrv_version_global_current(void);
 
+struct smbXsrv_client_table;
+NTSTATUS smbXsrv_client_global_init(void);
+NTSTATUS smbXsrv_client_create(TALLOC_CTX *mem_ctx,
+			       struct tevent_context *ev_ctx,
+			       struct messaging_context *msg_ctx,
+			       NTTIME now,
+			       struct smbXsrv_client **_client);
+NTSTATUS smbXsrv_client_update(struct smbXsrv_client *client);
+NTSTATUS smbXsrv_client_remove(struct smbXsrv_client *client);
+NTSTATUS smb2srv_client_lookup_global(struct smbXsrv_client *client,
+				      struct GUID client_guid,
+				      TALLOC_CTX *mem_ctx,
+				      struct smbXsrv_client_global0 **_pass);
+NTSTATUS smb2srv_client_connection_pass(struct smbd_smb2_request *smb2req,
+					struct smbXsrv_client_global0 *global);
+
 NTSTATUS smbXsrv_connection_init_tables(struct smbXsrv_connection *conn,
 					enum protocol_types protocol);
 
diff --git a/source3/smbd/smbXsrv_client.c b/source3/smbd/smbXsrv_client.c
new file mode 100644
index 0000000..87cc307
--- /dev/null
+++ b/source3/smbd/smbXsrv_client.c
@@ -0,0 +1,770 @@
+/*
+   Unix SMB/CIFS implementation.
+
+   Copyright (C) Stefan Metzmacher 2014
+
+   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 "includes.h"
+#include "system/filesys.h"
+#include <tevent.h>
+#include "smbd/smbd.h"
+#include "smbd/globals.h"
+#include "dbwrap/dbwrap.h"
+#include "dbwrap/dbwrap_rbt.h"
+#include "dbwrap/dbwrap_open.h"
+#include "dbwrap/dbwrap_watch.h"
+#include "session.h"
+#include "auth.h"
+#include "auth/gensec/gensec.h"
+#include "../lib/tsocket/tsocket.h"
+#include "../libcli/security/security.h"
+#include "messages.h"
+#include "lib/util/util_tdb.h"
+#include "librpc/gen_ndr/ndr_smbXsrv.h"
+#include "serverid.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "lib/util/iov_buf.h"
+
+struct smbXsrv_client_table {
+	struct {
+		uint32_t max_clients;
+		uint32_t num_clients;
+	} local;
+	struct {
+		struct db_context *db_ctx;
+	} global;
+};
+
+static struct db_context *smbXsrv_client_global_db_ctx = NULL;
+
+NTSTATUS smbXsrv_client_global_init(void)
+{
+	const char *global_path = NULL;
+	struct db_context *db_ctx = NULL;
+
+	if (smbXsrv_client_global_db_ctx != NULL) {
+		return NT_STATUS_OK;
+	}
+
+	/*
+	 * This contains secret information like client keys!
+	 */
+	global_path = lock_path("smbXsrv_client_global.tdb");
+
+	db_ctx = db_open(NULL, global_path,
+			 0, /* hash_size */
+			 TDB_DEFAULT |
+			 TDB_CLEAR_IF_FIRST |
+			 TDB_INCOMPATIBLE_HASH,
+			 O_RDWR | O_CREAT, 0600,
+			 DBWRAP_LOCK_ORDER_1,
+			 DBWRAP_FLAG_NONE);
+	if (db_ctx == NULL) {
+		NTSTATUS status;
+
+		status = map_nt_error_from_unix_common(errno);
+
+		return status;
+	}
+
+	smbXsrv_client_global_db_ctx = db_ctx;
+
+	return NT_STATUS_OK;
+}
+
+/*
+ * NOTE:
+ * We need to store the keys in big endian so that dbwrap_rbt's memcmp
+ * has the same result as integer comparison between the uint32_t
+ * values.
+ *
+ * TODO: implement string based key
+ */
+
+#define SMBXSRV_CLIENT_GLOBAL_TDB_KEY_SIZE 16
+
+static TDB_DATA smbXsrv_client_global_id_to_key(const struct GUID *client_guid,
+						uint8_t *key_buf)
+{
+	TDB_DATA key = { .dsize = 0, };
+	NTSTATUS status;
+	DATA_BLOB b;
+
+	status = GUID_to_ndr_blob(client_guid, talloc_tos(), &b);
+	if (!NT_STATUS_IS_OK(status)) {
+		return key;
+	}
+	memcpy(key_buf, b.data, SMBXSRV_CLIENT_GLOBAL_TDB_KEY_SIZE);
+	data_blob_free(&b);
+
+	key = make_tdb_data(key_buf, SMBXSRV_CLIENT_GLOBAL_TDB_KEY_SIZE);
+
+	return key;
+}
+
+static NTSTATUS smbXsrv_client_table_create(TALLOC_CTX *mem_ctx,
+					    struct messaging_context *msg_ctx,
+					    uint32_t max_clients,
+					    struct smbXsrv_client_table **_table)
+{
+	struct smbXsrv_client_table *table;
+	NTSTATUS status;
+
+	if (max_clients > 1) {
+		return NT_STATUS_INTERNAL_ERROR;
+	}
+
+	table = talloc_zero(mem_ctx, struct smbXsrv_client_table);
+	if (table == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+
+	table->local.max_clients = max_clients;
+
+	status = smbXsrv_client_global_init();
+	if (!NT_STATUS_IS_OK(status)) {
+		TALLOC_FREE(table);
+		return status;
+	}
+
+	table->global.db_ctx = smbXsrv_client_global_db_ctx;
+
+	dbwrap_watch_db(table->global.db_ctx, msg_ctx);
+
+	*_table = table;
+	return NT_STATUS_OK;
+}
+
+static int smbXsrv_client_global_destructor(struct smbXsrv_client_global0 *global)
+{
+	return 0;
+}
+
+static void smbXsrv_client_global_verify_record(struct db_record *db_rec,
+					bool *is_free,
+					bool *was_free,
+					TALLOC_CTX *mem_ctx,
+					struct smbXsrv_client_global0 **_g)
+{
+	TDB_DATA key;
+	TDB_DATA val;
+	DATA_BLOB blob;
+	struct smbXsrv_client_globalB global_blob;
+	enum ndr_err_code ndr_err;
+	struct smbXsrv_client_global0 *global = NULL;
+	bool exists;
+	TALLOC_CTX *frame = talloc_stackframe();
+
+	*is_free = false;
+
+	if (was_free) {
+		*was_free = false;
+	}
+	if (_g) {
+		*_g = NULL;
+	}
+
+	key = dbwrap_record_get_key(db_rec);
+
+	val = dbwrap_record_get_value(db_rec);
+	if (val.dsize == 0) {
+		TALLOC_FREE(frame);
+		*is_free = true;
+		if (was_free) {
+			*was_free = true;
+		}
+		return;
+	}
+
+	blob = data_blob_const(val.dptr, val.dsize);
+
+	ndr_err = ndr_pull_struct_blob(&blob, frame, &global_blob,
+			(ndr_pull_flags_fn_t)ndr_pull_smbXsrv_client_globalB);
+	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+		NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+		DBG_WARNING("smbXsrv_client_global_verify_record: "
+			    "key '%s' ndr_pull_struct_blob - %s\n",
+			    hex_encode_talloc(frame, key.dptr, key.dsize),
+			    nt_errstr(status));
+		TALLOC_FREE(frame);
+		return;
+	}
+
+	DBG_DEBUG("client_global:\n");
+	if (DEBUGLVL(DBGLVL_DEBUG)) {
+		NDR_PRINT_DEBUG(smbXsrv_client_globalB, &global_blob);
+	}
+
+	if (global_blob.version != SMBXSRV_VERSION_0) {
+		DBG_ERR("key '%s' use unsupported version %u\n",
+			hex_encode_talloc(frame, key.dptr, key.dsize),
+			global_blob.version);
+		NDR_PRINT_DEBUG(smbXsrv_client_globalB, &global_blob);
+		TALLOC_FREE(frame);
+		return;
+	}
+
+	global = global_blob.info.info0;
+
+	exists = serverid_exists(&global->server_id);
+	if (!exists) {
+		struct server_id_buf tmp;
+
+		DBG_NOTICE("key '%s' server_id %s does not exist.\n",
+			   hex_encode_talloc(frame, key.dptr, key.dsize),
+			   server_id_str_buf(global->server_id, &tmp));
+		if (DEBUGLVL(DBGLVL_NOTICE)) {
+			NDR_PRINT_DEBUG(smbXsrv_client_globalB, &global_blob);
+		}
+		TALLOC_FREE(frame);
+		dbwrap_record_delete(db_rec);
+		*is_free = true;
+		return;
+	}
+
+	if (_g) {
+		*_g = talloc_move(mem_ctx, &global);
+	}
+	TALLOC_FREE(frame);
+}
+
+NTSTATUS smb2srv_client_lookup_global(struct smbXsrv_client *client,
+				      struct GUID client_guid,
+				      TALLOC_CTX *mem_ctx,
+				      struct smbXsrv_client_global0 **_global)
+{
+	struct smbXsrv_client_table *table = client->table;
+	struct smbXsrv_client_global0 *global = NULL;
+	bool is_free = false;
+	uint8_t key_buf[SMBXSRV_CLIENT_GLOBAL_TDB_KEY_SIZE];
+	TDB_DATA key;
+	struct db_record *db_rec;
+
+	key = smbXsrv_client_global_id_to_key(&client_guid, key_buf);
+
+	db_rec = dbwrap_fetch_locked(table->global.db_ctx,
+				     talloc_tos(), key);
+	if (db_rec == NULL) {
+		DBG_ERR("guid [%s]: Failed to lock key '%s'\n",
+			GUID_string(talloc_tos(), &client_guid),
+			hex_encode_talloc(talloc_tos(), key.dptr, key.dsize));
+		return NT_STATUS_INTERNAL_DB_ERROR;
+	}
+
+	smbXsrv_client_global_verify_record(db_rec,
+					    &is_free,
+					    NULL,
+					    mem_ctx,
+					    &global);
+	TALLOC_FREE(db_rec);
+
+	if (is_free) {
+		return NT_STATUS_OBJECTID_NOT_FOUND;
+	}
+
+	*_global = global;
+	return NT_STATUS_OK;
+}
+
+NTSTATUS smb2srv_client_connection_pass(struct smbd_smb2_request *smb2req,
+					struct smbXsrv_client_global0 *global)
+{
+	DATA_BLOB blob;
+	enum ndr_err_code ndr_err;
+	NTSTATUS status;
+	struct smbXsrv_connection_pass0 pass_info0;
+	struct smbXsrv_connection_passB pass_blob;
+	struct iovec iov;
+
+	pass_info0.initial_connect_time = global->initial_connect_time;
+	pass_info0.client_guid = global->client_guid;
+	pass_info0.negotiate_request.length = iov_buflen(smb2req->in.vector,
+							 smb2req->in.vector_count);
+	pass_info0.negotiate_request.data = talloc_array(talloc_tos(), uint8_t,
+					pass_info0.negotiate_request.length);
+	if (pass_info0.negotiate_request.data == NULL) {
+		return NT_STATUS_NO_MEMORY;
+	}
+	iov_buf(smb2req->in.vector, smb2req->in.vector_count,
+		pass_info0.negotiate_request.data,
+		pass_info0.negotiate_request.length);
+
+	ZERO_STRUCT(pass_blob);
+	pass_blob.version = smbXsrv_version_global_current();
+	pass_blob.info.info0 = &pass_info0;
+
+	if (DEBUGLVL(DBGLVL_DEBUG)) {
+		NDR_PRINT_DEBUG(smbXsrv_connection_passB, &pass_blob);
+	}
+
+	ndr_err = ndr_push_struct_blob(&blob, talloc_tos(), &pass_blob,
+			(ndr_push_flags_fn_t)ndr_push_smbXsrv_connection_passB);
+	data_blob_free(&pass_info0.negotiate_request);
+	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+		status = ndr_map_error2ntstatus(ndr_err);
+		return status;
+	}
+
+	iov.iov_base = blob.data;
+	iov.iov_len = blob.length;
+
+	status = messaging_send_iov(smb2req->xconn->msg_ctx,
+				    global->server_id,
+				    MSG_SMBXSRV_CONNECTION_PASS,
+				    &iov, 1,
+				    &smb2req->xconn->transport.sock, 1);
+	data_blob_free(&blob);
+	if (!NT_STATUS_IS_OK(status)) {
+		return status;
+	}
+
+	return NT_STATUS_OK;
+}
+
+static NTSTATUS smbXsrv_client_global_store(struct smbXsrv_client_global0 *global)
+{
+	struct smbXsrv_client_globalB global_blob;
+	DATA_BLOB blob = data_blob_null;
+	TDB_DATA key;
+	TDB_DATA val;
+	NTSTATUS status;
+	enum ndr_err_code ndr_err;
+	bool saved_stored = global->stored;
+
+	/*
+	 * TODO: if we use other versions than '0'
+	 * we would add glue code here, that would be able to
+	 * store the information in the old format.
+	 */
+
+	if (global->db_rec == NULL) {
+		return NT_STATUS_INTERNAL_ERROR;
+	}
+
+	key = dbwrap_record_get_key(global->db_rec);
+	val = dbwrap_record_get_value(global->db_rec);
+
+	ZERO_STRUCT(global_blob);
+	global_blob.version = smbXsrv_version_global_current();
+	if (val.dsize >= 8) {
+		global_blob.seqnum = IVAL(val.dptr, 4);
+	}
+	global_blob.seqnum += 1;
+	global_blob.info.info0 = global;
+
+	global->stored = true;
+	ndr_err = ndr_push_struct_blob(&blob, global->db_rec, &global_blob,
+			(ndr_push_flags_fn_t)ndr_push_smbXsrv_client_globalB);
+	global->stored = saved_stored;
+	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+		status = ndr_map_error2ntstatus(ndr_err);
+		DBG_WARNING("key '%s' ndr_push - %s\n",
+			hex_encode_talloc(global->db_rec, key.dptr, key.dsize),
+			nt_errstr(status));
+		TALLOC_FREE(global->db_rec);
+		return status;
+	}
+
+	val = make_tdb_data(blob.data, blob.length);
+	status = dbwrap_record_store(global->db_rec, val, TDB_REPLACE);
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_WARNING("key '%s' store - %s\n",
+			hex_encode_talloc(global->db_rec, key.dptr, key.dsize),
+			nt_errstr(status));
+		TALLOC_FREE(global->db_rec);
+		return status;
+	}
+
+	global->stored = true;
+
+	if (DEBUGLVL(DBGLVL_DEBUG)) {
+		DBG_DEBUG("key '%s' stored\n",
+			hex_encode_talloc(global->db_rec, key.dptr, key.dsize));
+		NDR_PRINT_DEBUG(smbXsrv_client_globalB, &global_blob);
+	}
+
+	TALLOC_FREE(global->db_rec);
+
+	return NT_STATUS_OK;
+}
+
+static NTSTATUS smbXsrv_client_global_remove(struct smbXsrv_client_global0 *global)
+{
+	struct smbXsrv_client_globalB global_blob;
+	TDB_DATA key;
+	NTSTATUS status;
+
+	/*
+	 * TODO: if we use other versions than '0'
+	 * we would add glue code here, that would be able to
+	 * store the information in the old format.
+	 */
+
+	if (global->db_rec == NULL) {
+		return NT_STATUS_INTERNAL_ERROR;
+	}
+
+	key = dbwrap_record_get_key(global->db_rec);
+
+	status = dbwrap_record_delete(global->db_rec);
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_WARNING("key '%s' delete - %s\n",
+			hex_encode_talloc(global->db_rec, key.dptr, key.dsize),
+			nt_errstr(status));
+		TALLOC_FREE(global->db_rec);
+		return status;
+	}
+	global->stored = false;
+	if (DEBUGLVL(DBGLVL_DEBUG)) {
+		DBG_DEBUG("key '%s' delete\n",
+			hex_encode_talloc(global->db_rec, key.dptr, key.dsize));
+		NDR_PRINT_DEBUG(smbXsrv_client_globalB, &global_blob);
+	}
+
+	TALLOC_FREE(global->db_rec);
+
+	return NT_STATUS_OK;
+}
+
+static int smbXsrv_client_destructor(struct smbXsrv_client *client)
+{
+	NTSTATUS status;
+
+	status = smbXsrv_client_remove(client);
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_ERR("smbXsrv_client_remove() failed: %s\n",
+			nt_errstr(status));
+	}
+
+	TALLOC_FREE(client->global);
+
+	return 0;
+}
+
+static bool smbXsrv_client_connection_pass_filter(struct messaging_rec *rec, void *private_data);
+static void smbXsrv_client_connection_pass_loop(struct tevent_req *subreq);
+
+NTSTATUS smbXsrv_client_create(TALLOC_CTX *mem_ctx,
+			       struct tevent_context *ev_ctx,
+			       struct messaging_context *msg_ctx,
+			       NTTIME now,
+			       struct smbXsrv_client **_client)
+{
+	struct smbXsrv_client_table *table;
+	struct smbXsrv_client *client = NULL;
+	struct smbXsrv_client_global0 *global = NULL;
+	NTSTATUS status;
+	struct tevent_req *subreq = NULL;
+
+	status = smbXsrv_client_table_create(mem_ctx,
+					     msg_ctx,
+					     1, /* max_clients */
+					     &table);
+	if (!NT_STATUS_IS_OK(status)) {
+		return status;
+	}
+
+	if (table->local.num_clients >= table->local.max_clients) {
+		TALLOC_FREE(table);
+		return NT_STATUS_INSUFFICIENT_RESOURCES;
+	}
+
+	client = talloc_zero(mem_ctx, struct smbXsrv_client);
+	if (client == NULL) {
+		TALLOC_FREE(table);
+		return NT_STATUS_NO_MEMORY;
+	}
+	client->ev_ctx = ev_ctx;
+	client->msg_ctx = msg_ctx;
+
+	client->table = talloc_move(client, &table);
+	table = client->table;
+
+	global = talloc_zero(client, struct smbXsrv_client_global0);
+	if (global == NULL) {
+		TALLOC_FREE(client);
+		return NT_STATUS_NO_MEMORY;
+	}
+	talloc_set_destructor(global, smbXsrv_client_global_destructor);
+	client->global = global;
+
+	global->initial_connect_time = now;
+
+	global->server_id = messaging_server_id(client->msg_ctx);
+
+	table->local.num_clients += 1;
+
+	talloc_set_destructor(client, smbXsrv_client_destructor);
+
+	if (DEBUGLVL(DBGLVL_DEBUG)) {
+		struct smbXsrv_clientB client_blob;
+
+		ZERO_STRUCT(client_blob);
+		client_blob.version = SMBXSRV_VERSION_0;
+		client_blob.info.info0 = client;
+
+		DBG_DEBUG("client_guid[%s] stored\n",
+			  GUID_string(talloc_tos(), &global->client_guid));
+		NDR_PRINT_DEBUG(smbXsrv_clientB, &client_blob);
+	}
+
+	subreq = messaging_filtered_read_send(client, client->ev_ctx, client->msg_ctx,
+					      smbXsrv_client_connection_pass_filter,
+					      client);
+	if (subreq == NULL) {
+		TALLOC_FREE(client);
+		return NT_STATUS_NO_MEMORY;
+	}
+	tevent_req_set_callback(subreq, smbXsrv_client_connection_pass_loop, client);
+
+	*_client = client;
+	return NT_STATUS_OK;
+}
+
+static bool smbXsrv_client_connection_pass_filter(struct messaging_rec *rec, void *private_data)
+{
+	if (rec->msg_type != MSG_SMBXSRV_CONNECTION_PASS) {
+		return false;
+	}
+
+	if (rec->num_fds != 1) {
+		return false;
+	}
+
+	if (rec->buf.length < SMB2_HDR_BODY) {
+		return false;
+	}
+
+	/* TODO: verify client_guid...? */
+
+	return true;
+}
+
+static void smbXsrv_client_connection_pass_loop(struct tevent_req *subreq)
+{
+	struct smbXsrv_client *client =
+		tevent_req_callback_data(subreq,
+		struct smbXsrv_client);
+	struct smbXsrv_connection *xconn = NULL;
+	int ret;
+	struct messaging_rec *rec = NULL;
+	struct smbXsrv_connection_passB pass_blob;
+	enum ndr_err_code ndr_err;
+	struct smbXsrv_connection_pass0 *pass_info0 = NULL;
+	NTSTATUS status;
+	int sock_fd = -1;
+	uint64_t seq_low;
+
+	ret = messaging_filtered_read_recv(subreq, talloc_tos(), &rec);
+	TALLOC_FREE(subreq);
+	if (ret != 0) {
+		goto next;
+	}
+
+	ndr_err = ndr_pull_struct_blob(&rec->buf, rec, &pass_blob,
+			(ndr_pull_flags_fn_t)ndr_pull_smbXsrv_connection_passB);
+	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+		status = ndr_map_error2ntstatus(ndr_err);
+		DBG_WARNING("ndr_pull_struct_blob - %s\n", nt_errstr(status));
+		goto next;
+	}
+
+	DBG_DEBUG("MSG_SMBXSRV_CLIENT_CLOSE\n");
+	if (DEBUGLVL(DBGLVL_DEBUG)) {
+		NDR_PRINT_DEBUG(smbXsrv_connection_passB, &pass_blob);
+	}
+
+	if (pass_blob.version != SMBXSRV_VERSION_0) {
+		DBG_ERR("ignore invalid version %u\n", pass_blob.version);
+		NDR_PRINT_DEBUG(smbXsrv_connection_passB, &pass_blob);
+		goto next;
+	}
+
+	pass_info0 = pass_blob.info.info0;
+	if (pass_info0 == NULL) {
+		DBG_ERR("ignore NULL info %u\n", pass_blob.version);
+		NDR_PRINT_DEBUG(smbXsrv_connection_passB, &pass_blob);
+		goto next;
+	}
+
+	if (!GUID_equal(&client->global->client_guid, &pass_info0->client_guid))
+	{
+		DBG_WARNING("client's client_guid [%s] != passed guid [%s]\n",
+			GUID_string(talloc_tos(), &client->global->client_guid),
+			GUID_string(talloc_tos(), &pass_info0->client_guid));
+		if (DEBUGLVL(DBGLVL_WARNING)) {
+			NDR_PRINT_DEBUG(smbXsrv_connection_passB, &pass_blob);
+		}
+		goto next;
+	}
+
+	if (client->global->initial_connect_time !=
+	    pass_info0->initial_connect_time)
+	{
+		DBG_WARNING("client's initial connect time [%s] (%llu) != "
+			"passed initial connect time [%s] (%llu)\n",
+			nt_time_string(talloc_tos(),
+				       client->global->initial_connect_time),
+			(unsigned long long)client->global->initial_connect_time,
+			nt_time_string(talloc_tos(),
+				       pass_info0->initial_connect_time),
+			(unsigned long long)pass_info0->initial_connect_time);
+		if (DEBUGLVL(DBGLVL_WARNING)) {
+			NDR_PRINT_DEBUG(smbXsrv_connection_passB, &pass_blob);
+		}
+		goto next;
+	}
+
+	SMB_ASSERT(rec->num_fds == 1);
+	sock_fd = rec->fds[0];
+
+	DBG_ERR("got connection sockfd[%d]\n", sock_fd);
+	NDR_PRINT_DEBUG(smbXsrv_connection_passB, &pass_blob);
+	status = smbd_add_connection(client, sock_fd, &xconn);
+	if (!NT_STATUS_IS_OK(status)) {
+		close(sock_fd);
+		sock_fd = -1;
+		DBG_ERR("smbd_add_connection => %s\n", nt_errstr(status));
+		NDR_PRINT_DEBUG(smbXsrv_connection_passB, &pass_blob);
+		goto next;
+	}
+
+	/*
+	 * Set seq_low to mid received in negprot
+	 */
+	seq_low = BVAL(pass_info0->negotiate_request.data,
+		       SMB2_HDR_MESSAGE_ID);
+
+	xconn->smb2.client.guid_verified = true;
+	smbd_smb2_process_negprot(xconn, seq_low,
+				  pass_info0->negotiate_request.data,
+				  pass_info0->negotiate_request.length);
+
+next:
+	TALLOC_FREE(rec);
+
+	subreq = messaging_filtered_read_send(client, client->ev_ctx, client->msg_ctx,
+					      smbXsrv_client_connection_pass_filter,
+					      client);
+	if (subreq == NULL) {
+		const char *r;
+		r = "messaging_read_send(MSG_SMBXSRV_CONNECTION_PASS failed";
+		exit_server_cleanly(r);
+		return;
+	}
+	tevent_req_set_callback(subreq, smbXsrv_client_connection_pass_loop, client);
+}
+
+NTSTATUS smbXsrv_client_update(struct smbXsrv_client *client)
+{
+	struct smbXsrv_client_table *table = client->table;
+	NTSTATUS status;
+	uint8_t key_buf[SMBXSRV_CLIENT_GLOBAL_TDB_KEY_SIZE];
+	TDB_DATA key;
+
+	if (client->global->db_rec != NULL) {
+		DBG_ERR("guid [%s]: Called with db_rec != NULL'\n",
+			GUID_string(talloc_tos(),
+			&client->global->client_guid));
+		return NT_STATUS_INTERNAL_ERROR;
+	}
+
+	key = smbXsrv_client_global_id_to_key(&client->global->client_guid,
+					      key_buf);
+
+	client->global->db_rec = dbwrap_fetch_locked(table->global.db_ctx,
+						     client->global, key);
+	if (client->global->db_rec == NULL) {
+		DBG_ERR("guid [%s]: Failed to lock key '%s'\n",
+			GUID_string(talloc_tos(), &client->global->client_guid),
+			hex_encode_talloc(talloc_tos(), key.dptr, key.dsize));
+		return NT_STATUS_INTERNAL_DB_ERROR;
+	}
+
+	status = smbXsrv_client_global_store(client->global);
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_ERR("client_guid[%s] store failed - %s\n",
+			GUID_string(talloc_tos(), &client->global->client_guid),
+			nt_errstr(status));
+		return status;
+	}
+
+	if (DEBUGLVL(DBGLVL_DEBUG)) {
+		struct smbXsrv_clientB client_blob;
+
+		ZERO_STRUCT(client_blob);
+		client_blob.version = SMBXSRV_VERSION_0;
+		client_blob.info.info0 = client;
+
+		DBG_DEBUG("client_guid[%s] stored\n",
+			GUID_string(talloc_tos(), &client->global->client_guid));
+		NDR_PRINT_DEBUG(smbXsrv_clientB, &client_blob);
+	}
+
+	return NT_STATUS_OK;
+}
+
+NTSTATUS smbXsrv_client_remove(struct smbXsrv_client *client)
+{
+	struct smbXsrv_client_table *table = client->table;
+	NTSTATUS status;
+	uint8_t key_buf[SMBXSRV_CLIENT_GLOBAL_TDB_KEY_SIZE];
+	TDB_DATA key;
+
+	if (client->global->db_rec != NULL) {
+		DBG_ERR("client_guid[%s]: Called with db_rec != NULL'\n",
+			GUID_string(talloc_tos(), &client->global->client_guid));
+		return NT_STATUS_INTERNAL_ERROR;
+	}
+
+	if (!client->global->stored) {
+		return NT_STATUS_OK;
+	}
+
+	key = smbXsrv_client_global_id_to_key(&client->global->client_guid,
+					      key_buf);
+
+	client->global->db_rec = dbwrap_fetch_locked(table->global.db_ctx,
+						     client->global, key);
+	if (client->global->db_rec == NULL) {
+		DBG_ERR("client_guid[%s]: Failed to lock key '%s'\n",
+			GUID_string(talloc_tos(), &client->global->client_guid),
+			hex_encode_talloc(talloc_tos(), key.dptr, key.dsize));
+		return NT_STATUS_INTERNAL_DB_ERROR;
+	}
+
+	status = smbXsrv_client_global_remove(client->global);
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_ERR("client_guid[%s] store failed - %s\n",
+			GUID_string(talloc_tos(), &client->global->client_guid),
+			nt_errstr(status));
+		return status;
+	}
+
+	if (DEBUGLVL(DBGLVL_DEBUG)) {
+		struct smbXsrv_clientB client_blob;
+
+		ZERO_STRUCT(client_blob);
+		client_blob.version = SMBXSRV_VERSION_0;
+		client_blob.info.info0 = client;
+
+		DBG_DEBUG("client_guid[%s] stored\n",
+			GUID_string(talloc_tos(), &client->global->client_guid));
+		NDR_PRINT_DEBUG(smbXsrv_clientB, &client_blob);
+	}
+
+	return NT_STATUS_OK;
+}
diff --git a/source3/wscript_build b/source3/wscript_build
index 06b24bf..27e1b5e 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -592,6 +592,7 @@ bld.SAMBA3_LIBRARY('smbd_base',
                    smbd/smb2_setinfo.c
                    smbd/smb2_break.c
                    smbd/smbXsrv_version.c
+                   smbd/smbXsrv_client.c
                    smbd/smbXsrv_session.c
                    smbd/smbXsrv_tcon.c
                    smbd/smbXsrv_open.c
-- 
2.5.0


From f4dfeff0b5940b9144784bfeb8fb15467f0f0121 Mon Sep 17 00:00:00 2001
From: Michael Adam <obnox at samba.org>
Date: Mon, 25 Jan 2016 19:02:04 +0100
Subject: [PATCH 6/9] smbd:process: treat initialized table in
 smbXsrv_connection_init_tables

Pair-Programmed-With: Stefan Metzmacher <metze at samba.org>

Signed-off-by: Michael Adam <obnox at samba.org>
---
 source3/smbd/process.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/source3/smbd/process.c b/source3/smbd/process.c
index e5c52be..13f06c5 100644
--- a/source3/smbd/process.c
+++ b/source3/smbd/process.c
@@ -3563,6 +3563,10 @@ NTSTATUS smbXsrv_connection_init_tables(struct smbXsrv_connection *conn,
 
 	conn->protocol = protocol;
 
+	if (conn->client->session_table != NULL) {
+		return NT_STATUS_OK;
+	}
+
 	if (protocol >= PROTOCOL_SMB2_02) {
 		status = smb2srv_session_table_init(conn);
 		if (!NT_STATUS_IS_OK(status)) {
-- 
2.5.0


From 78859277d98216b8d219062bb59ad3b57bf9e2de Mon Sep 17 00:00:00 2001
From: Michael Adam <obnox at samba.org>
Date: Fri, 22 Jan 2016 13:13:44 +0100
Subject: [PATCH 7/9] smbd:process: use smbXsrv_client_create.

Signed-off-by: Michael Adam <obnox at samba.org>
---
 source3/smbd/process.c | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/source3/smbd/process.c b/source3/smbd/process.c
index 13f06c5..8c94434 100644
--- a/source3/smbd/process.c
+++ b/source3/smbd/process.c
@@ -3902,10 +3902,11 @@ void smbd_process(struct tevent_context *ev_ctx,
 	const char *remaddr = NULL;
 	int ret;
 	NTSTATUS status;
+	NTTIME now = 0; // TODO
 
-	client = talloc_zero(ev_ctx, struct smbXsrv_client);
-	if (client == NULL) {
-		DEBUG(0,("talloc_zero(struct smbXsrv_client)\n"));
+	status = smbXsrv_client_create(ev_ctx, ev_ctx, msg_ctx, now, &client);
+	if (!NT_STATUS_IS_OK(status)) {
+		DBG_ERR("smbXsrv_client_create(): %s\n", nt_errstr(status));
 		exit_server_cleanly("talloc_zero(struct smbXsrv_client).\n");
 	}
 
@@ -3914,9 +3915,6 @@ void smbd_process(struct tevent_context *ev_ctx,
 	 */
 	global_smbXsrv_client = client;
 
-	client->ev_ctx = ev_ctx;
-	client->msg_ctx = msg_ctx;
-
 	sconn = talloc_zero(client, struct smbd_server_connection);
 	if (sconn == NULL) {
 		exit_server("failed to create smbd_server_connection");
-- 
2.5.0


From fc1a73f824500016225a4f44ea99d0bc6fe82f98 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze at samba.org>
Date: Thu, 18 Sep 2014 19:27:42 +0200
Subject: [PATCH 8/9] TODO: smbd:smb2_server: let
 smbd_server_connection_terminate() only call exit_server() for the last
 connection

TODO: we need to cancel pending requests on the connection and defer the talloc_free
---
 source3/smbd/smb2_server.c | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)

diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c
index 9adbb99..58f933b 100644
--- a/source3/smbd/smb2_server.c
+++ b/source3/smbd/smb2_server.c
@@ -1059,8 +1059,21 @@ void smbd_server_connection_terminate_ex(struct smbXsrv_connection *xconn,
 					 const char *reason,
 					 const char *location)
 {
-	DEBUG(10,("smbd_server_connection_terminate_ex: reason[%s] at %s\n",
-		  reason, location));
+	struct smbXsrv_client *client = xconn->client;
+
+	DEBUG(10,("smbd_server_connection_terminate_ex: conn[%s] reason[%s] at %s\n",
+		  smbXsrv_connection_dbg(xconn), reason, location));
+
+	if (client->connections->next != NULL) {
+		/* TODO: cancel pending requests */
+		DLIST_REMOVE(client->connections, xconn);
+		TALLOC_FREE(xconn);
+		return;
+	}
+
+	/*
+	 * The last connection was disconnected
+	 */
 	exit_server_cleanly(reason);
 }
 
-- 
2.5.0


From 5b209e790b62e682ae0c579d428ba5942a16921a Mon Sep 17 00:00:00 2001
From: Michael Adam <obnox at samba.org>
Date: Tue, 26 Jan 2016 10:12:46 +0100
Subject: [PATCH 9/9] smbd:smb2_negprot: implement connection passing based on
 client_guid

Pair-Programmed-With: Stefan Metzmacher <metze at samba.org>

Signed-off-by: Michael Adam <obnox at samba.org>
---
 source3/smbd/smb2_negprot.c | 58 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 58 insertions(+)

diff --git a/source3/smbd/smb2_negprot.c b/source3/smbd/smb2_negprot.c
index 1582072..0bb13bc 100644
--- a/source3/smbd/smb2_negprot.c
+++ b/source3/smbd/smb2_negprot.c
@@ -587,6 +587,8 @@ NTSTATUS smbd_smb2_request_process_negprot(struct smbd_smb2_request *req)
 	req->sconn->using_smb2 = true;
 
 	if (dialect != SMB2_DIALECT_REVISION_2FF) {
+		struct smbXsrv_client_global0 *global0 = NULL;
+
 		status = smbXsrv_connection_init_tables(xconn, protocol);
 		if (!NT_STATUS_IS_OK(status)) {
 			return smbd_smb2_request_error(req, status);
@@ -613,6 +615,62 @@ NTSTATUS smbd_smb2_request_process_negprot(struct smbd_smb2_request *req)
 		xconn->smb2.server.max_trans = max_trans;
 		xconn->smb2.server.max_read  = max_read;
 		xconn->smb2.server.max_write = max_write;
+
+		if (xconn->protocol < PROTOCOL_SMB2_10) {
+			/*
+			 * SMB2_02 doesn't support client guids
+			 */
+			return smbd_smb2_request_done(req, outbody, &outdyn);
+		}
+
+		if (!xconn->client->server_multi_channel_enabled) {
+			/*
+			 * Only deal with the client guid database
+			 * if multi-channel is enabled.
+			 */
+			return smbd_smb2_request_done(req, outbody, &outdyn);
+		}
+
+		if (xconn->smb2.client.guid_verified) {
+			/*
+			 * The connection was passed from another
+			 * smbd process.
+			 */
+			return smbd_smb2_request_done(req, outbody, &outdyn);
+		}
+
+		status = smb2srv_client_lookup_global(xconn->client,
+						xconn->smb2.client.guid,
+						req, &global0);
+		/*
+		 * TODO: check for races...
+		 */
+		if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECTID_NOT_FOUND)) {
+			/*
+			 * This stores the new client information in
+			 * smbXsrv_client_global.tdb
+			 */
+			xconn->client->global->client_guid =
+						xconn->smb2.client.guid;
+			status = smbXsrv_client_update(xconn->client);
+			if (!NT_STATUS_IS_OK(status)) {
+				return status;
+			}
+
+			xconn->smb2.client.guid_verified = true;
+		} else if (NT_STATUS_IS_OK(status)) {
+			status = smb2srv_client_connection_pass(req,
+								global0);
+			if (!NT_STATUS_IS_OK(status)) {
+				return smbd_smb2_request_error(req, status);
+			}
+
+			smbd_server_connection_terminate(xconn,
+							 "passed connection");
+			return NT_STATUS_OBJECTID_EXISTS;
+		} else {
+			return smbd_smb2_request_error(req, status);
+		}
 	}
 
 	return smbd_smb2_request_done(req, outbody, &outdyn);
-- 
2.5.0

-------------- next part --------------
From 7a961dba2db5861ad0d151c7fd12fb3cd8356a2e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=BCnther=20Deschner?= <gd at samba.org>
Date: Wed, 20 Jan 2016 17:44:45 +0100
Subject: [PATCH 1/2] param: add parameter "server multi channel support",
 defaults to off.

Guenther

Pair-Programmed-With: Michael Adam <obnox at samba.org>

Signed-off-by: Guenther Deschner <gd at samba.org>
Signed-off-by: Michael Adam <obnox at samba.org>
---
 .../smbdotconf/protocol/servermultichannelsupport.xml | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)
 create mode 100644 docs-xml/smbdotconf/protocol/servermultichannelsupport.xml

diff --git a/docs-xml/smbdotconf/protocol/servermultichannelsupport.xml b/docs-xml/smbdotconf/protocol/servermultichannelsupport.xml
new file mode 100644
index 0000000..b85bbd3
--- /dev/null
+++ b/docs-xml/smbdotconf/protocol/servermultichannelsupport.xml
@@ -0,0 +1,19 @@
+<samba:parameter name="server multi channel support"
+                 context="G"
+                 type="boolean"
+                 xmlns:samba="http://www.samba.org/samba/DTD/samba-doc">
+<description>
+    <para>This boolean parameter controls whether
+    <citerefentry><refentrytitle>smbd</refentrytitle>
+    <manvolnum>8</manvolnum></citerefentry> will support
+    SMB3 multi-channel.
+    </para>
+    <para>This parameter has been added with version 4.4.</para>
+    <para>
+    Warning: Note that this feature is considered experimental in Samba 4.4.
+    Use it at your own risk: it may result in data corruption.
+    </para>
+</description>
+
+<value type="default">no</value>
+</samba:parameter>
-- 
2.5.0


From 1f63533b92b859e78e924cc21b7c305ecf245cc8 Mon Sep 17 00:00:00 2001
From: Michael Adam <obnox at samba.org>
Date: Tue, 26 Jan 2016 08:16:51 +0100
Subject: [PATCH 2/2] smbd: enable multi-channel if 'server multi channel
 support = yes' in the config

Signed-off-by: Michael Adam <obnox at samba.org>
---
 source3/smbd/smbXsrv_client.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/source3/smbd/smbXsrv_client.c b/source3/smbd/smbXsrv_client.c
index 87cc307..8062593 100644
--- a/source3/smbd/smbXsrv_client.c
+++ b/source3/smbd/smbXsrv_client.c
@@ -490,6 +490,8 @@ NTSTATUS smbXsrv_client_create(TALLOC_CTX *mem_ctx,
 	client->ev_ctx = ev_ctx;
 	client->msg_ctx = msg_ctx;
 
+	client->server_multi_channel_enabled = lp_server_multi_channel_support();
+
 	client->table = talloc_move(client, &table);
 	table = client->table;
 
-- 
2.5.0

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 198 bytes
Desc: not available
URL: <http://lists.samba.org/pipermail/samba-technical/attachments/20160126/0dcee9a7/signature-0001.sig>


More information about the samba-technical mailing list