Add multichannel tests to smbtorture

Sachin Prabhu sprabhu at redhat.com
Fri Apr 19 13:05:06 UTC 2019


Hello Jeremy,

Sorry about the delay. Getting the test environment up and running
took longer than expected. The tests were run successfully against a
Samba server with test multichannel patches and with a Microsoft
Windows 2018 file server.
I have attached the patch with the necessary changes suggested in the
earlier email.

Thanks
Sachin Prabhu

On Fri, Apr 19, 2019 at 12:34 AM Sachin Prabhu <sprabhu at redhat.com> wrote:
>
> On Wed, Apr 17, 2019 at 7:25 PM Jeremy Allison via samba-technical
> <samba-technical at lists.samba.org> wrote:
> >
> > On Wed, Apr 17, 2019 at 10:33:10AM -0700, Jeremy Allison via samba-technical wrote:
> > > On Tue, Apr 16, 2019 at 04:54:50PM +0200, Günther Deschner wrote:
> > > > On 27/03/2019 19:31, Jeremy Allison via samba-technical wrote:
> > > > > Guenther, are you able to be second Team reviewer here ?
> > > >
> > > > Yes, of course :) Sorry, forgot to mention that all of these patches
> > > > were reviewed by me earlier, so Sachin, please upload the same series
> > > > with my RB+ added :)
> > >
> > > Only two comments:
> > >
> > > [PATCH 03/19] s4-torture: move oplock break handler out of the replay testsuite.
> > >
> > > Add include guards around new source4/torture/smb2/oplock_break_handler.h file.
> > >
> > > PATCH 09/19] s4-torture: add torture_block/torture_unblock smb2 transport functions
> > >
> > > Change: samba_chain_name(..., bool) -> samba_chain_name(..., const char *prefix)
> > >
> > > where prefix is passed as "SAMBA_INPUT" or "SAMBA_OUTPUT" instead
> > > of 'true'/'false'. bool args make it impossible to see what this
> > > is doing without looking inside the function, using the strings
> > > directly make it obvious.
> > >
> > > Unfortunately when I compile to test I'm getting errors using:
> > >
> > > --enable-developer --picky-developer
> > >
> > > Errors are below. Can you fix these up and resubmit please ?
> > >
> > > Most of them are just things like adding "const" to the last
> > > argument of _test_block_channel() etc. and removing unused
> > > variables.
> >
> > Just to prove I can be helpful :-), here is a fixed up
> > patchset that removes all errors I found (and includes
> > the changes I requested). Still fails with:
> >
> > [3208/4213] Compiling source4/torture/smb2/multichannel.c
> > ../../source4/torture/smb2/multichannel.c: In function ‘test_ioctl_network_interface_info’:
> > ../../source4/torture/smb2/multichannel.c:135:15: error: ‘struct <anonymous>’ has no member named ‘max_response_size’
> >   ioctl.smb2.in.max_response_size = 0x10000;
> >                ^
> > ../../source4/torture/smb2/multichannel.c: In function ‘test_multichannel_create_channel’:
> > ../../source4/torture/smb2/multichannel.c:178:7: error: variable ‘ret’ set but not used [-Werror=unused-but-set-variable]
> >   bool ret = true;
> >        ^~~
> > cc1: all warnings being treated as errors
> >
> > Waf: Leaving directory `/space/jra/src/samba/git/reviews/bin/default'
> > Build failed
> >
> > but should be a much cleaner base to work with to
> > fix up these last fiddly errors.
>
> Hello Jeremy,
>
> Thanks for pointing out the errors. I had removed the
> --picky-developer option from my workflow because of some problems I
> was facing with the rawhide development environment.
>
> Unfortunately I saw this email after I made the necessary changes but
> thanks again for posting the patches.
>
> I believe that the error
> > ../../source4/torture/smb2/multichannel.c:135:15: error: ‘struct <anonymous>’ has no member named ‘max_response_size’
> >   ioctl.smb2.in.max_response_size = 0x10000;
>
> was because of changes introduced by the patch
> 97b4e6e220d s4:libcli/smb2: align struct smb_ioctl.smb2 to [MS-SMB2] names
> I was using an older base and didn't have these new changes.
>
> I am testing the patches at this moment and will post as soon as I
> have completed testing them.
>
> Sachin Prabhu
>
> >
> > Cheersm
> >
> >         Jeremy.
-------------- next part --------------
From 398c9dc90d12492641d939458f3557fda0e68920 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=BCnther=20Deschner?= <gd at samba.org>
Date: Wed, 20 Sep 2017 19:19:37 +0200
Subject: [PATCH 01/19] s4-torture: include torture/util.h in lease break
 handler
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Guenther

Signed-off-by: Günther Deschner <gd at samba.org>
---
 source4/torture/smb2/lease_break_handler.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/source4/torture/smb2/lease_break_handler.h b/source4/torture/smb2/lease_break_handler.h
index 54e615c3082..dc8841bb8a1 100644
--- a/source4/torture/smb2/lease_break_handler.h
+++ b/source4/torture/smb2/lease_break_handler.h
@@ -19,6 +19,8 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
+#include "torture/util.h"
+
 struct lease_break_info {
 	struct torture_context *tctx;
 
-- 
2.20.1


From 02efb1af00dbef9f39d965fd1fb2123d20f10877 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=BCnther=20Deschner?= <gd at samba.org>
Date: Tue, 19 Jan 2016 14:55:03 +0100
Subject: [PATCH 02/19] s4-torture: add new smb2 multichannel suite skeleton.

Also Skip MC tests for s4 ntvfs fileserver, it's not supported at all.
Use knownfail for s3 fileserver for the time being (until socketwrapper
supports fd-passing).

Guenther

Signed-off-by: Guenther Deschner <gd at samba.org>
---
 selftest/knownfail                  |  1 +
 selftest/skip                       |  1 +
 source4/torture/smb2/multichannel.c | 56 +++++++++++++++++++++++++++++
 source4/torture/smb2/smb2.c         |  1 +
 source4/torture/smb2/wscript_build  |  1 +
 5 files changed, 60 insertions(+)
 create mode 100644 source4/torture/smb2/multichannel.c

diff --git a/selftest/knownfail b/selftest/knownfail
index c588f2f5c6b..5bc18a69168 100644
--- a/selftest/knownfail
+++ b/selftest/knownfail
@@ -200,6 +200,7 @@
 ^samba3.smb2.lock.*replay
 ^samba3.smb2.lease.statopen3
 ^samba3.smb2.lease.unlink # we currently do not downgrade RH lease to R after unlink
+^samba3.smb2.multichannel
 ^samba4.smb2.ioctl.compress_notsup.*\(ad_dc_ntvfs\)
 ^samba3.raw.session.*reauth2 # maybe fix this?
 ^samba3.rpc.lsa.secrets.seal # This gives NT_STATUS_LOCAL_USER_SESSION_KEY
diff --git a/selftest/skip b/selftest/skip
index 3e9a25fc0a3..e628d7ccc85 100644
--- a/selftest/skip
+++ b/selftest/skip
@@ -94,6 +94,7 @@
 ^samba4.smb2.dir
 ^samba4.smb2.session
 ^samba4.smb2.compound
+^samba4.smb2.multichannel
 ^samba4.smb2.oplock.levelii501		# No test yet
 # SMB2 in s4 does not seem to support rename correctly
 ^samba4.smb2.rename.*\(ad_dc_ntvfs\)$
diff --git a/source4/torture/smb2/multichannel.c b/source4/torture/smb2/multichannel.c
new file mode 100644
index 00000000000..2c72bd779d5
--- /dev/null
+++ b/source4/torture/smb2/multichannel.c
@@ -0,0 +1,56 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * test SMB2 multichannel operations
+ *
+ * Copyright (C) Guenther Deschner, 2016
+ *
+ * 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 "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "torture/torture.h"
+#include "torture/util.h"
+#include "torture/smb2/proto.h"
+#include "libcli/security/security.h"
+#include "librpc/gen_ndr/ndr_security.h"
+
+#define CHECK_STATUS(status, correct) do { \
+	if (!NT_STATUS_EQUAL(status, correct)) { \
+		torture_result(tctx, TORTURE_FAIL, \
+			"(%s) Incorrect status %s - should be %s\n", \
+			 __location__, nt_errstr(status), nt_errstr(correct)); \
+		return false; \
+	} } while (0)
+
+static bool test_session_bind(struct torture_context *tctx,
+			      struct smb2_tree *tree)
+{
+	/* TODO */
+
+	return true;
+}
+
+struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
+{
+	struct torture_suite *suite = torture_suite_create(ctx, "multichannel");
+
+	torture_suite_add_1smb2_test(suite, "session-bind", test_session_bind);
+
+	suite->description = talloc_strdup(suite, "SMB2 Multichannel tests");
+
+	return suite;
+}
diff --git a/source4/torture/smb2/smb2.c b/source4/torture/smb2/smb2.c
index a835dc7c050..2a465a55ba2 100644
--- a/source4/torture/smb2/smb2.c
+++ b/source4/torture/smb2/smb2.c
@@ -190,6 +190,7 @@ NTSTATUS torture_smb2_init(TALLOC_CTX *ctx)
 	torture_suite_add_suite(suite, torture_smb2_crediting_init(suite));
 
 	torture_suite_add_suite(suite, torture_smb2_doc_init(suite));
+	torture_suite_add_suite(suite, torture_smb2_multichannel_init(suite));
 
 	suite->description = talloc_strdup(suite, "SMB2-specific tests");
 
diff --git a/source4/torture/smb2/wscript_build b/source4/torture/smb2/wscript_build
index 8b0060a2831..e10d3d90ba5 100644
--- a/source4/torture/smb2/wscript_build
+++ b/source4/torture/smb2/wscript_build
@@ -19,6 +19,7 @@ bld.SAMBA_MODULE('TORTURE_SMB2',
         lock.c
         maxfid.c
         maxwrite.c
+        multichannel.c
         notify.c
         notify_disabled.c
         oplock.c
-- 
2.20.1


From ede3f94809e8725e7004de8845dfc0b5a2f3107a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=BCnther=20Deschner?= <gd at samba.org>
Date: Wed, 28 Sep 2016 21:23:20 +0200
Subject: [PATCH 03/19] s4-torture: move oplock break handler out of the replay
 testsuite.

Guenther

Signed-off-by: Guenther Deschner <gd at samba.org>
---
 source4/torture/smb2/oplock_break_handler.c | 91 +++++++++++++++++++++
 source4/torture/smb2/oplock_break_handler.h | 51 ++++++++++++
 source4/torture/smb2/replay.c               | 78 +-----------------
 source4/torture/smb2/wscript_build          |  1 +
 4 files changed, 144 insertions(+), 77 deletions(-)
 create mode 100644 source4/torture/smb2/oplock_break_handler.c
 create mode 100644 source4/torture/smb2/oplock_break_handler.h

diff --git a/source4/torture/smb2/oplock_break_handler.c b/source4/torture/smb2/oplock_break_handler.c
new file mode 100644
index 00000000000..30863a9167e
--- /dev/null
+++ b/source4/torture/smb2/oplock_break_handler.c
@@ -0,0 +1,91 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * test suite for SMB2 replay
+ *
+ * Copyright (C) Anubhav Rakshit 2014
+ * 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 "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "torture/torture.h"
+#include "torture/smb2/proto.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "oplock_break_handler.h"
+
+struct break_info break_info;
+
+static void torture_oplock_ack_callback(struct smb2_request *req)
+{
+	NTSTATUS status;
+
+	status = smb2_break_recv(req, &break_info.br);
+	if (!NT_STATUS_IS_OK(status)) {
+		break_info.failures++;
+		break_info.failure_status = status;
+	}
+}
+
+/**
+ * A general oplock break notification handler.  This should be used when a
+ * test expects to break from batch or exclusive to a lower level.
+ */
+
+bool torture_oplock_ack_handler(struct smb2_transport *transport,
+				const struct smb2_handle *handle,
+				uint8_t level,
+				void *private_data)
+{
+	struct smb2_tree *tree = private_data;
+	const char *name;
+	struct smb2_request *req;
+
+	ZERO_STRUCT(break_info.br);
+
+	break_info.handle	= *handle;
+	break_info.level	= level;
+	break_info.count++;
+
+	switch (level) {
+	case SMB2_OPLOCK_LEVEL_II:
+		name = "level II";
+		break;
+	case SMB2_OPLOCK_LEVEL_NONE:
+		name = "none";
+		break;
+	default:
+		name = "unknown";
+		break_info.failures++;
+	}
+	torture_comment(break_info.tctx,
+			"transport[%p] Acking to %s [0x%02X] in oplock handler\n",
+			transport, name, level);
+
+	break_info.br.in.file.handle	= *handle;
+	break_info.br.in.oplock_level	= level;
+	break_info.br.in.reserved	= 0;
+	break_info.br.in.reserved2	= 0;
+	break_info.received_transport = tree->session->transport;
+	SMB_ASSERT(tree->session->transport == transport);
+
+	req = smb2_break_send(tree, &break_info.br);
+	req->async.fn = torture_oplock_ack_callback;
+	req->async.private_data = NULL;
+
+	return true;
+}
diff --git a/source4/torture/smb2/oplock_break_handler.h b/source4/torture/smb2/oplock_break_handler.h
new file mode 100644
index 00000000000..78bba103fb2
--- /dev/null
+++ b/source4/torture/smb2/oplock_break_handler.h
@@ -0,0 +1,51 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * test suite for SMB2 replay
+ *
+ * Copyright (C) Anubhav Rakshit 2014
+ * 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/>.
+ */
+
+#ifndef __OPLOCK_BREAK_HANDLER_H__
+#define __OPLOCK_BREAK_HANDLER_H__
+
+struct break_info {
+	struct torture_context *tctx;
+	struct smb2_handle handle;
+	uint8_t level;
+	struct smb2_break br;
+	int count;
+	int failures;
+	NTSTATUS failure_status;
+	struct smb2_transport *received_transport;
+};
+
+extern struct break_info break_info;
+
+bool torture_oplock_ack_handler(struct smb2_transport *transport,
+				const struct smb2_handle *handle,
+				uint8_t level,
+				void *private_data);
+
+static inline void torture_reset_break_info(struct torture_context *tctx,
+			      struct break_info *r)
+{
+	ZERO_STRUCTP(r);
+	r->tctx = tctx;
+}
+
+#endif /* __OPLOCK_BREAK_HANDLER_H__ */
diff --git a/source4/torture/smb2/replay.c b/source4/torture/smb2/replay.c
index 57c7bef2d66..903adc6798f 100644
--- a/source4/torture/smb2/replay.c
+++ b/source4/torture/smb2/replay.c
@@ -32,6 +32,7 @@
 #include "libcli/resolve/resolve.h"
 #include "lib/param/param.h"
 #include "lib/events/events.h"
+#include "oplock_break_handler.h"
 
 #define CHECK_VAL(v, correct) do { \
 	if ((v) != (correct)) { \
@@ -93,83 +94,6 @@
 
 #define BASEDIR "replaytestdir"
 
-struct break_info {
-	struct torture_context *tctx;
-	struct smb2_handle handle;
-	uint8_t level;
-	struct smb2_break br;
-	int count;
-	int failures;
-	NTSTATUS failure_status;
-};
-
-static struct break_info break_info;
-
-static void torture_reset_break_info(struct torture_context *tctx,
-				     struct break_info *r)
-{
-	ZERO_STRUCTP(r);
-	r->tctx = tctx;
-}
-
-static void torture_oplock_ack_callback(struct smb2_request *req)
-{
-	NTSTATUS status;
-
-	status = smb2_break_recv(req, &break_info.br);
-	if (!NT_STATUS_IS_OK(status)) {
-		break_info.failures++;
-		break_info.failure_status = status;
-	}
-
-	return;
-}
-
-/**
- * A general oplock break notification handler.  This should be used when a
- * test expects to break from batch or exclusive to a lower level.
- */
-static bool torture_oplock_ack_handler(struct smb2_transport *transport,
-				       const struct smb2_handle *handle,
-				       uint8_t level,
-				       void *private_data)
-{
-	struct smb2_tree *tree = private_data;
-	const char *name;
-	struct smb2_request *req;
-
-	ZERO_STRUCT(break_info.br);
-
-	break_info.handle	= *handle;
-	break_info.level	= level;
-	break_info.count++;
-
-	switch (level) {
-	case SMB2_OPLOCK_LEVEL_II:
-		name = "level II";
-		break;
-	case SMB2_OPLOCK_LEVEL_NONE:
-		name = "none";
-		break;
-	default:
-		name = "unknown";
-		break_info.failures++;
-	}
-	torture_comment(break_info.tctx,
-			"Acking to %s [0x%02X] in oplock handler\n",
-			name, level);
-
-	break_info.br.in.file.handle	= *handle;
-	break_info.br.in.oplock_level	= level;
-	break_info.br.in.reserved	= 0;
-	break_info.br.in.reserved2	= 0;
-
-	req = smb2_break_send(tree, &break_info.br);
-	req->async.fn = torture_oplock_ack_callback;
-	req->async.private_data = NULL;
-	return true;
-}
-
 /**
  * Timer handler function notifies the registering function that time is up
  */
diff --git a/source4/torture/smb2/wscript_build b/source4/torture/smb2/wscript_build
index e10d3d90ba5..1183cb93772 100644
--- a/source4/torture/smb2/wscript_build
+++ b/source4/torture/smb2/wscript_build
@@ -20,6 +20,7 @@ bld.SAMBA_MODULE('TORTURE_SMB2',
         maxfid.c
         maxwrite.c
         multichannel.c
+        oplock_break_handler.c
         notify.c
         notify_disabled.c
         oplock.c
-- 
2.20.1


From 22abc68ac44fdc7b6845d35eb6fb0b934f5d4fd6 Mon Sep 17 00:00:00 2001
From: Sachin Prabhu <sprabhu at redhat.com>
Date: Thu, 14 Mar 2019 18:15:27 +0000
Subject: [PATCH 04/19] s4-torture: move torture_wait_for_oplock_break() to
 central oplock handler.

Guenther

Signed-off-by: Guenther Deschner <gd at samba.org>
---
 source4/torture/smb2/oplock.c               | 54 --------------------
 source4/torture/smb2/oplock_break_handler.c | 54 ++++++++++++++++++++
 source4/torture/smb2/oplock_break_handler.h |  1 +
 source4/torture/smb2/replay.c               | 55 ---------------------
 4 files changed, 55 insertions(+), 109 deletions(-)

diff --git a/source4/torture/smb2/oplock.c b/source4/torture/smb2/oplock.c
index d6156eb4d8c..885bf1a9e3a 100644
--- a/source4/torture/smb2/oplock.c
+++ b/source4/torture/smb2/oplock.c
@@ -304,60 +304,6 @@ static bool open_smb2_connection_no_level2_oplocks(struct torture_context *tctx,
 	return true;
 }
 
-/*
-   Timer handler function notifies the registering function that time is up
-*/
-static void timeout_cb(struct tevent_context *ev,
-		       struct tevent_timer *te,
-		       struct timeval current_time,
-		       void *private_data)
-{
-	bool *timesup = (bool *)private_data;
-	*timesup = true;
-	return;
-}
-
-/*
-   Wait a short period of time to receive a single oplock break request
-*/
-static void torture_wait_for_oplock_break(struct torture_context *tctx)
-{
-	TALLOC_CTX *tmp_ctx = talloc_new(NULL);
-	struct tevent_timer *te = NULL;
-	struct timeval ne;
-	bool timesup = false;
-	int old_count = break_info.count;
-
-	/* Wait .1 seconds for an oplock break */
-	ne = tevent_timeval_current_ofs(0, 100000);
-
-	if ((te = tevent_add_timer(tctx->ev, tmp_ctx, ne, timeout_cb, &timesup))
-	    == NULL)
-	{
-		torture_comment(tctx, "Failed to wait for an oplock break. "
-				      "test results may not be accurate.");
-		goto done;
-	}
-
-	while (!timesup && break_info.count < old_count + 1) {
-		if (tevent_loop_once(tctx->ev) != 0) {
-			torture_comment(tctx, "Failed to wait for an oplock "
-					      "break. test results may not be "
-					      "accurate.");
-			goto done;
-		}
-	}
-
-done:
-	/* We don't know if the timed event fired and was freed, we received
-	 * our oplock break, or some other event triggered the loop.  Thus,
-	 * we create a tmp_ctx to be able to safely free/remove the timed
-	 * event in all 3 cases. */
-	talloc_free(tmp_ctx);
-
-	return;
-}
-
 static bool test_smb2_oplock_exclusive1(struct torture_context *tctx,
 					struct smb2_tree *tree1,
 					struct smb2_tree *tree2)
diff --git a/source4/torture/smb2/oplock_break_handler.c b/source4/torture/smb2/oplock_break_handler.c
index 30863a9167e..0226d46decb 100644
--- a/source4/torture/smb2/oplock_break_handler.c
+++ b/source4/torture/smb2/oplock_break_handler.c
@@ -27,6 +27,7 @@
 #include "torture/smb2/proto.h"
 #include "../libcli/smb/smbXcli_base.h"
 #include "oplock_break_handler.h"
+#include "lib/events/events.h"
 
 struct break_info break_info;
 
@@ -89,3 +90,56 @@ bool torture_oplock_ack_handler(struct smb2_transport *transport,
 
 	return true;
 }
+
+
+/*
+ * Timer handler function notifies the registering function that time is up
+ */
+static void timeout_cb(struct tevent_context *ev,
+		       struct tevent_timer *te,
+		       struct timeval current_time,
+		       void *private_data)
+{
+	bool *timesup = (bool *)private_data;
+	*timesup = true;
+}
+
+/*
+ * Wait a short period of time to receive a single oplock break request
+ */
+void torture_wait_for_oplock_break(struct torture_context *tctx)
+{
+	TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+	struct tevent_timer *te = NULL;
+	struct timeval ne;
+	bool timesup = false;
+	int old_count = break_info.count;
+
+	/* Wait .1 seconds for an oplock break */
+	ne = tevent_timeval_current_ofs(0, 100000);
+
+	te = tevent_add_timer(tctx->ev, tmp_ctx, ne, timeout_cb, &timesup);
+	if (te == NULL) {
+		torture_comment(tctx, "Failed to wait for an oplock break. "
+				      "test results may not be accurate.");
+		goto done;
+	}
+
+	while (!timesup && break_info.count < old_count + 1) {
+		if (tevent_loop_once(tctx->ev) != 0) {
+			torture_comment(tctx, "Failed to wait for an oplock "
+					      "break. test results may not be "
+					      "accurate.");
+			goto done;
+		}
+	}
+
+done:
+	/* We don't know if the timed event fired and was freed, we received
+	 * our oplock break, or some other event triggered the loop.  Thus,
+	 * we create a tmp_ctx to be able to safely free/remove the timed
+	 * event in all 3 cases.
+	 */
+	talloc_free(tmp_ctx);
+}
+
diff --git a/source4/torture/smb2/oplock_break_handler.h b/source4/torture/smb2/oplock_break_handler.h
index 78bba103fb2..6b1eeb14b63 100644
--- a/source4/torture/smb2/oplock_break_handler.h
+++ b/source4/torture/smb2/oplock_break_handler.h
@@ -40,6 +40,7 @@ bool torture_oplock_ack_handler(struct smb2_transport *transport,
 				const struct smb2_handle *handle,
 				uint8_t level,
 				void *private_data);
+void torture_wait_for_oplock_break(struct torture_context *tctx);
 
 static inline void torture_reset_break_info(struct torture_context *tctx,
 			      struct break_info *r)
diff --git a/source4/torture/smb2/replay.c b/source4/torture/smb2/replay.c
index 903adc6798f..abb026419c7 100644
--- a/source4/torture/smb2/replay.c
+++ b/source4/torture/smb2/replay.c
@@ -94,61 +94,6 @@
 
 #define BASEDIR "replaytestdir"
 
-/**
- * Timer handler function notifies the registering function that time is up
- */
-static void timeout_cb(struct tevent_context *ev,
-		       struct tevent_timer *te,
-		       struct timeval current_time,
-		       void *private_data)
-{
-	bool *timesup = (bool *)private_data;
-	*timesup = true;
-	return;
-}
-
-/**
- *  Wait a short period of time to receive a single oplock break request
- */
-static void torture_wait_for_oplock_break(struct torture_context *tctx)
-{
-	TALLOC_CTX *tmp_ctx = talloc_new(NULL);
-	struct tevent_timer *te = NULL;
-	struct timeval ne;
-	bool timesup = false;
-	int old_count = break_info.count;
-
-	/* Wait .1 seconds for an oplock break */
-	ne = tevent_timeval_current_ofs(0, 100000);
-
-	te = tevent_add_timer(tctx->ev, tmp_ctx, ne, timeout_cb, &timesup);
-	if (te == NULL) {
-		torture_comment(tctx, "Failed to wait for an oplock break. "
-				      "test results may not be accurate.");
-		goto done;
-	}
-
-	while (!timesup && break_info.count < old_count + 1) {
-		if (tevent_loop_once(tctx->ev) != 0) {
-			torture_comment(tctx, "Failed to wait for an oplock "
-					      "break. test results may not be "
-					      "accurate.");
-			goto done;
-		}
-	}
-
-done:
-	/*
-	 * We don't know if the timed event fired and was freed, we received
-	 * our oplock break, or some other event triggered the loop.  Thus,
-	 * we create a tmp_ctx to be able to safely free/remove the timed
-	 * event in all 3 cases.
-	 */
-	talloc_free(tmp_ctx);
-
-	return;
-}
-
 /**
  * Test what happens when SMB2_FLAGS_REPLAY_OPERATION is enabled for various
  * commands. We want to verify if the server returns an error code or not.
-- 
2.20.1


From 16c12ebac7df06dd9246d18ff490049ac41d75ca Mon Sep 17 00:00:00 2001
From: Sachin Prabhu <sprabhu at redhat.com>
Date: Sat, 16 Mar 2019 12:11:04 +0000
Subject: [PATCH 05/19] s4-torture: Add function declarations to
 lease_break_handler.h

Do not completely depend on proto.h.

Also move torture_reset_break_info() to lease_break_handler.h so that
the layout is similar to that of oplock_break_handler.*

Signed-off-by: Sachin Prabhu <sprabhu at redhat.com>
Reviewed-by: Guenther Deschner <gd at samba.org>
---
 source4/torture/smb2/lease_break_handler.c |  8 --------
 source4/torture/smb2/lease_break_handler.h | 13 +++++++++++--
 2 files changed, 11 insertions(+), 10 deletions(-)

diff --git a/source4/torture/smb2/lease_break_handler.c b/source4/torture/smb2/lease_break_handler.c
index b70234420bd..5f8e325d63f 100644
--- a/source4/torture/smb2/lease_break_handler.c
+++ b/source4/torture/smb2/lease_break_handler.c
@@ -124,11 +124,3 @@ done:
 
 	return;
 }
-
- void torture_reset_lease_break_info(struct torture_context *tctx,
-				     struct lease_break_info *r)
-{
-	ZERO_STRUCTP(r);
-	r->tctx = tctx;
-}
-
diff --git a/source4/torture/smb2/lease_break_handler.h b/source4/torture/smb2/lease_break_handler.h
index dc8841bb8a1..1e915e27951 100644
--- a/source4/torture/smb2/lease_break_handler.h
+++ b/source4/torture/smb2/lease_break_handler.h
@@ -118,5 +118,14 @@ struct lease_break_info {
 
 extern struct lease_break_info lease_break_info;
 
-void torture_reset_lease_break_info(struct torture_context *tctx,
-				    struct lease_break_info *r);
+bool torture_lease_handler(struct smb2_transport *transport,
+			   const struct smb2_lease_break *lb,
+			   void *private_data);
+void torture_wait_for_lease_break(struct torture_context *tctx);
+
+static inline void torture_reset_lease_break_info(struct torture_context *tctx,
+						  struct lease_break_info *r)
+{
+	ZERO_STRUCTP(r);
+	r->tctx = tctx;
+}
-- 
2.20.1


From feff46b788ef0784a8ac760b0cc383bdf2ecc6a1 Mon Sep 17 00:00:00 2001
From: Sachin Prabhu <sprabhu at redhat.com>
Date: Sat, 16 Mar 2019 12:25:07 +0000
Subject: [PATCH 06/19] s4-torture: Add handlers to ignore incoming
 oplock/lease break requests

For use in multichannel oplock break tests. These handers ignore
incoming oplock and lease break requests so that we can test the
oplock/lease break retries on the server.

This is meant for use with samba servers which rely on receiving a reply
from the client before timeout.
Windows servers rely on underlying tcp commands to decide if the oplock
break command was delivered successfully to the client and therefore
cannot be tested with this method.

Signed-off-by: Sachin Prabhu <sprabhu at redhat.com>
Reviewed-by: Guenther Deschner <gd at samba.org>
---
 source4/torture/smb2/lease_break_handler.c  | 12 ++++++++++++
 source4/torture/smb2/lease_break_handler.h  |  3 +++
 source4/torture/smb2/oplock_break_handler.c | 10 ++++++++++
 source4/torture/smb2/oplock_break_handler.h |  4 ++++
 4 files changed, 29 insertions(+)

diff --git a/source4/torture/smb2/lease_break_handler.c b/source4/torture/smb2/lease_break_handler.c
index 5f8e325d63f..8e576391eb1 100644
--- a/source4/torture/smb2/lease_break_handler.c
+++ b/source4/torture/smb2/lease_break_handler.c
@@ -72,6 +72,18 @@ bool torture_lease_handler(struct smb2_transport *transport,
 	return true;
 }
 
+/*
+ * A lease break handler which ignores incoming lease break requests
+ * To be used in cases where the client is expected to ignore incoming
+ * lease break requests
+ */
+bool torture_lease_ignore_handler(struct smb2_transport *transport,
+			   const struct smb2_lease_break *lb,
+			   void *private_data)
+{
+	return true;
+}
+
 /*
    Timer handler function notifies the registering function that time is up
 */
diff --git a/source4/torture/smb2/lease_break_handler.h b/source4/torture/smb2/lease_break_handler.h
index 1e915e27951..90fde1a9217 100644
--- a/source4/torture/smb2/lease_break_handler.h
+++ b/source4/torture/smb2/lease_break_handler.h
@@ -121,6 +121,9 @@ extern struct lease_break_info lease_break_info;
 bool torture_lease_handler(struct smb2_transport *transport,
 			   const struct smb2_lease_break *lb,
 			   void *private_data);
+bool torture_lease_ignore_handler(struct smb2_transport *transport,
+				  const struct smb2_lease_break *lb,
+				  void *private_data);
 void torture_wait_for_lease_break(struct torture_context *tctx);
 
 static inline void torture_reset_lease_break_info(struct torture_context *tctx,
diff --git a/source4/torture/smb2/oplock_break_handler.c b/source4/torture/smb2/oplock_break_handler.c
index 0226d46decb..1c2cac83fc0 100644
--- a/source4/torture/smb2/oplock_break_handler.c
+++ b/source4/torture/smb2/oplock_break_handler.c
@@ -91,6 +91,16 @@ bool torture_oplock_ack_handler(struct smb2_transport *transport,
 	return true;
 }
 
+/**
+ * A oplock break handler designed to ignore incoming break requests.
+ * This is used when incoming oplock break requests need to be ignored
+ */
+bool torture_oplock_ignore_handler(struct smb2_transport *transport,
+				   const struct smb2_handle *handle,
+				   uint8_t level, void *private_data)
+{
+	return true;
+}
 
 /*
  * Timer handler function notifies the registering function that time is up
diff --git a/source4/torture/smb2/oplock_break_handler.h b/source4/torture/smb2/oplock_break_handler.h
index 6b1eeb14b63..3286ddbe443 100644
--- a/source4/torture/smb2/oplock_break_handler.h
+++ b/source4/torture/smb2/oplock_break_handler.h
@@ -40,6 +40,10 @@ bool torture_oplock_ack_handler(struct smb2_transport *transport,
 				const struct smb2_handle *handle,
 				uint8_t level,
 				void *private_data);
+bool torture_oplock_ignore_handler(struct smb2_transport *transport,
+				const struct smb2_handle *handle,
+				uint8_t level,
+				void *private_data);
 void torture_wait_for_oplock_break(struct torture_context *tctx);
 
 static inline void torture_reset_break_info(struct torture_context *tctx,
-- 
2.20.1


From df9a8991bf6022822399e509ff423449917fff60 Mon Sep 17 00:00:00 2001
From: Sachin Prabhu <sprabhu at redhat.com>
Date: Mon, 28 May 2018 17:24:54 +0530
Subject: [PATCH 07/19] s4-torture: Increase timeout for lease/oplock break
 handlers

0.1 seconds is not enough when running tests against a server over the
network and are causing timing related bugs. We increase this to 1
second.

Signed-off-by: Sachin Prabhu <sprabhu at redhat.com>
Reviewed-by: Guenther Deschner <gd at samba.org>
---
 source4/torture/smb2/lease_break_handler.c  | 4 ++--
 source4/torture/smb2/oplock_break_handler.c | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/source4/torture/smb2/lease_break_handler.c b/source4/torture/smb2/lease_break_handler.c
index 8e576391eb1..d741127f3d5 100644
--- a/source4/torture/smb2/lease_break_handler.c
+++ b/source4/torture/smb2/lease_break_handler.c
@@ -108,8 +108,8 @@ void torture_wait_for_lease_break(struct torture_context *tctx)
 	bool timesup = false;
 	int old_count = lease_break_info.count;
 
-	/* Wait .1 seconds for an lease break */
-	ne = tevent_timeval_current_ofs(0, 100000);
+	/* Wait 1 second for an lease break */
+	ne = tevent_timeval_current_ofs(0, 1000000);
 
 	te = tevent_add_timer(tctx->ev, tmp_ctx, ne, timeout_cb, &timesup);
 	if (te == NULL) {
diff --git a/source4/torture/smb2/oplock_break_handler.c b/source4/torture/smb2/oplock_break_handler.c
index 1c2cac83fc0..7bf8bb58963 100644
--- a/source4/torture/smb2/oplock_break_handler.c
+++ b/source4/torture/smb2/oplock_break_handler.c
@@ -125,8 +125,8 @@ void torture_wait_for_oplock_break(struct torture_context *tctx)
 	bool timesup = false;
 	int old_count = break_info.count;
 
-	/* Wait .1 seconds for an oplock break */
-	ne = tevent_timeval_current_ofs(0, 100000);
+	/* Wait 1 second for an oplock break */
+	ne = tevent_timeval_current_ofs(0, 1000000);
 
 	te = tevent_add_timer(tctx->ev, tmp_ctx, ne, timeout_cb, &timesup);
 	if (te == NULL) {
-- 
2.20.1


From 77048be89c2d24ddcd82636afa3ccf1d4aa140a2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=BCnther=20Deschner?= <gd at samba.org>
Date: Tue, 19 Jan 2016 15:39:34 +0100
Subject: [PATCH 08/19] s4-torture: add test for interface information
 retrieval for multichannel.

Guenther

Signed-off-by: Guenther Deschner <gd at samba.org>
---
 source4/torture/smb2/multichannel.c | 58 +++++++++++++++++++++++++++--
 1 file changed, 54 insertions(+), 4 deletions(-)

diff --git a/source4/torture/smb2/multichannel.c b/source4/torture/smb2/multichannel.c
index 2c72bd779d5..0a4203aa5fb 100644
--- a/source4/torture/smb2/multichannel.c
+++ b/source4/torture/smb2/multichannel.c
@@ -27,6 +27,8 @@
 #include "torture/smb2/proto.h"
 #include "libcli/security/security.h"
 #include "librpc/gen_ndr/ndr_security.h"
+#include "librpc/gen_ndr/ndr_ioctl.h"
+#include "../libcli/smb/smbXcli_base.h"
 
 #define CHECK_STATUS(status, correct) do { \
 	if (!NT_STATUS_EQUAL(status, correct)) { \
@@ -36,19 +38,67 @@
 		return false; \
 	} } while (0)
 
-static bool test_session_bind(struct torture_context *tctx,
-			      struct smb2_tree *tree)
+static bool test_ioctl_network_interface_info(struct torture_context *tctx,
+					      struct smb2_tree *tree,
+					      struct fsctl_net_iface_info *info)
 {
-	/* TODO */
+	union smb_ioctl ioctl;
+	struct smb2_handle fh;
+	uint32_t caps;
+
+	caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
+	if (!(caps & SMB2_CAP_MULTI_CHANNEL)) {
+		torture_skip(tctx,
+			    "server doesn't support SMB2_CAP_MULTI_CHANNEL\n");
+	}
+
+	ZERO_STRUCT(ioctl);
+
+	ioctl.smb2.level = RAW_IOCTL_SMB2;
+
+	fh.data[0] = UINT64_MAX;
+	fh.data[1] = UINT64_MAX;
+
+	ioctl.smb2.in.file.handle = fh;
+	ioctl.smb2.in.function = FSCTL_QUERY_NETWORK_INTERFACE_INFO;
+	/* Windows client sets this to 64KiB */
+	ioctl.smb2.in.max_output_response = 0x10000;
+	ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
+
+	torture_assert_ntstatus_ok(tctx,
+		smb2_ioctl(tree, tctx, &ioctl.smb2),
+		"FSCTL_QUERY_NETWORK_INTERFACE_INFO failed");
+
+	torture_assert(tctx,
+		(ioctl.smb2.out.out.length != 0),
+		"no interface info returned???");
+
+	torture_assert_ndr_success(tctx,
+		ndr_pull_struct_blob(&ioctl.smb2.out.out, tctx, info,
+			(ndr_pull_flags_fn_t)ndr_pull_fsctl_net_iface_info),
+		"failed to ndr pull");
+
+	if (DEBUGLVL(1)) {
+		NDR_PRINT_DEBUG(fsctl_net_iface_info, info);
+	}
 
 	return true;
 }
 
+static bool test_multichannel_interface_info(struct torture_context *tctx,
+					     struct smb2_tree *tree)
+{
+	struct fsctl_net_iface_info info;
+
+	return test_ioctl_network_interface_info(tctx, tree, &info);
+}
+
 struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
 {
 	struct torture_suite *suite = torture_suite_create(ctx, "multichannel");
 
-	torture_suite_add_1smb2_test(suite, "session-bind", test_session_bind);
+	torture_suite_add_1smb2_test(suite, "interface_info",
+				     test_multichannel_interface_info);
 
 	suite->description = talloc_strdup(suite, "SMB2 Multichannel tests");
 
-- 
2.20.1


From 8fbe3f9ebfdce718d3720997e9fc437a2b13f72c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=BCnther=20Deschner?= <gd at samba.org>
Date: Thu, 29 Sep 2016 06:49:50 +0200
Subject: [PATCH 09/19] s4-torture: add torture_block/torture_unblock smb2
 transport functions

Guenther

Signed-off-by: Guenther Deschner <gd at samba.org>
Signed-off-by: Sachin Prabhu <sprabhu at redhat.com>
---
 source4/torture/smb2/block.c       | 370 +++++++++++++++++++++++++++++
 source4/torture/smb2/block.h       |  45 ++++
 source4/torture/smb2/wscript_build |   1 +
 3 files changed, 416 insertions(+)
 create mode 100644 source4/torture/smb2/block.c
 create mode 100644 source4/torture/smb2/block.h

diff --git a/source4/torture/smb2/block.c b/source4/torture/smb2/block.c
new file mode 100644
index 00000000000..d1e1856bb05
--- /dev/null
+++ b/source4/torture/smb2/block.c
@@ -0,0 +1,370 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * block SMB2 transports using iptables
+ *
+ * Copyright (C) Guenther Deschner, 2017
+ *
+ * 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 "libcli/smb2/smb2.h"
+#include "torture/torture.h"
+#include "torture/smb2/proto.h"
+#include "system/network.h"
+#include "lib/util/util_net.h"
+#include "torture/smb2/block.h"
+#include "libcli/smb/smbXcli_base.h"
+
+/*
+ * INPUT
+ *  |
+ *  -----> SAMBA_INPUT
+ *             |
+ *             -----> SAMBA_INPUT_transportname1
+ *             -----> SAMBA_INPUT_transportname2
+ */
+
+
+static bool run_cmd(const char *cmd)
+{
+	int ret;
+
+	DEBUG(10, ("%s will call '%s'\n", __location__, cmd));
+
+	ret = system(cmd);
+	if (ret) {
+		DEBUG(1, ("%s failed to execute system call: %s: %d\n",
+			__location__, cmd, ret));
+		return false;
+	}
+
+	return true;
+}
+
+int smbrun(const char *cmd, int *outfd, char * const *env);
+
+static bool run_cmd_return_buf(TALLOC_CTX *mem_ctx,
+			       const char *cmd,
+			       int *num_lines, char ***buf)
+{
+	int ret;
+	int fd = -1;
+
+	DEBUG(10, ("%s will call '%s'\n", __location__, cmd));
+
+	ret = smbrun(cmd, &fd, NULL);
+	if (ret) {
+		DEBUG(1, ("%s failed to execute system call: %s: %d\n",
+			__location__, cmd, ret));
+		if (fd != -1) {
+			close(fd);
+		}
+		return false;
+	}
+
+	*buf = fd_lines_load(fd, num_lines, 0, mem_ctx);
+	if (fd != -1) {
+		close(fd);
+	}
+	if (*buf == NULL) {
+		return false;
+	}
+
+	return true;
+}
+
+static const char *iptables_command(struct torture_context *tctx)
+{
+	return torture_setting_string(tctx, "iptables_command",
+				      "/usr/sbin/iptables");
+}
+
+char *escape_shell_string(const char *src);
+
+/*
+ * iptables v1.6.1: chain name `SAMBA_INPUT_tree1->session->transport'
+ * too long (must be under 29 chars)
+ *
+ * maybe truncate chainname ?
+ */
+static const char *samba_chain_name(struct torture_context *tctx,
+				    const char *name,
+				    const char *prefix)
+{
+	const char *s;
+	char *sm;
+
+	s = talloc_asprintf(tctx, "%s_%s", prefix, name);
+	if (s == NULL) {
+		return NULL;
+	}
+
+	sm = escape_shell_string(s);
+	if (sm == NULL) {
+		return NULL;
+	}
+
+	s = talloc_strdup(tctx, sm);
+	free(sm);
+
+	return s;
+}
+
+static bool filter_tcp_setup(struct torture_context *tctx,
+			     bool unblock)
+{
+	const char *cmd_in, *cmd_out;
+	const char *ipt = iptables_command(tctx);
+
+	if (unblock) {
+		cmd_in = talloc_asprintf(tctx,
+				"%s -L SAMBA_INPUT > /dev/null 2>&1 && "
+				"("
+				"%s -F SAMBA_INPUT; "
+				"%s -D INPUT -j SAMBA_INPUT; "
+				"%s -X SAMBA_INPUT;"
+				")",
+				ipt, ipt, ipt, ipt);
+		cmd_out = talloc_asprintf(tctx,
+				"%s -L SAMBA_OUTPUT > /dev/null 2>&1 && "
+				"("
+				"%s -F SAMBA_OUTPUT;"
+				"%s -D OUTPUT -j SAMBA_OUTPUT;"
+				"%s -X SAMBA_OUTPUT;"
+				")",
+				ipt, ipt, ipt, ipt);
+	} else {
+		cmd_in = talloc_asprintf(tctx,
+				"%s -L SAMBA_INPUT > /dev/null 2>&1 || "
+				"("
+				"%s -N SAMBA_INPUT && "
+				"%s -I INPUT -j SAMBA_INPUT "
+				")",
+				ipt, ipt, ipt);
+		cmd_out = talloc_asprintf(tctx,
+				"%s -L SAMBA_OUTPUT > /dev/null 2>&1 || "
+				"("
+				"%s -N SAMBA_OUTPUT && "
+				"%s -I OUTPUT -j SAMBA_OUTPUT;"
+				")",
+				ipt, ipt, ipt);
+	}
+
+	if (cmd_in == NULL || cmd_out == NULL) {
+		return false;
+	}
+
+	if (!run_cmd(cmd_in)) {
+		return false;
+	}
+	/* if (!run_cmd(cmd_out)) { return false; } */
+
+	return true;
+}
+
+static bool filter_tcp_setup_name(struct torture_context *tctx,
+				  const char *name, bool unblock)
+{
+	const char *cmd_in, *cmd_out;
+	const char *chain_in, *chain_out;
+	const char *ipt = iptables_command(tctx);
+
+	chain_in = samba_chain_name(tctx, name, "SAMBA_INPUT");
+	chain_out = samba_chain_name(tctx, name, "SAMBA_OUTPUT");
+	if (chain_in == NULL || chain_out == NULL) {
+		return false;
+	}
+
+	if (unblock) {
+		cmd_in  = talloc_asprintf(tctx, "%s -F %s; "
+						"%s -D SAMBA_INPUT -j %s; "
+						"%s -X %s",
+						ipt, chain_in,
+						ipt, chain_in,
+						ipt, chain_in);
+		cmd_out = talloc_asprintf(tctx, "%s -F %s; "
+						"%s -D SAMBA_OUTPUT -j %s; "
+						"%s -X %s",
+						ipt, chain_out,
+						ipt, chain_out,
+						ipt, chain_out);
+	} else {
+		cmd_in  = talloc_asprintf(tctx, "%s -L %s > /dev/null 2>&1 || "
+						"%s -N %s && "
+						"%s -I SAMBA_INPUT -j %s",
+						ipt, chain_in,
+						ipt, chain_in,
+						ipt, chain_in);
+		cmd_out = talloc_asprintf(tctx, "%s -L %s > /dev/null 2>&1 || "
+						"%s -N %s && "
+						"%s -I SAMBA_OUTPUT -j %s",
+						ipt, chain_out,
+						ipt, chain_out,
+						ipt, chain_out);
+	}
+
+	if (cmd_in == NULL || cmd_out == NULL) {
+		return false;
+	}
+
+	if (!run_cmd(cmd_in)) {
+		return false;
+	}
+	/* if (!run_cmd(cmd_out)) return false; */
+
+	return true;
+}
+
+/* '11   452 DROP tcp -- * *  0.0.0.0/0  0.0.0.0/0  tcp dpt:43062' */
+static bool get_packet_count(const char *s, uint32_t *count)
+{
+	int i = 0;
+	char *p;
+
+	if (s == NULL) {
+		return false;
+	}
+
+	while (s[i] == ' ') {
+		s++;
+	}
+
+	p = strchr(s, ' ');
+	if (p == NULL) {
+		return false;
+	}
+	*p = '\0';
+
+	*count = atoi(s);
+
+	return true;
+}
+
+bool torture_list_tcp_transport_name(struct torture_context *tctx,
+				    const char *name,
+				    uint32_t *_packets)
+{
+	const char *chain_in, *cmd;
+	int num_lines;
+	char **buf;
+	uint32_t packets = 0;
+	const char *ipt = iptables_command(tctx);
+
+	chain_in = samba_chain_name(tctx, name, "SAMBA_INPUT");
+	if (chain_in == NULL) {
+		return false;
+	}
+
+	cmd = talloc_asprintf(tctx, "%s -L %s -v -n", ipt, chain_in);
+	if (cmd == NULL) {
+		return false;
+	}
+
+	if (!run_cmd_return_buf(tctx, cmd, &num_lines, &buf)) {
+		return false;
+	}
+	SMB_ASSERT(num_lines >= 3);
+
+	if (!get_packet_count(buf[2], &packets)) {
+		return false;
+	}
+
+	torture_comment(tctx, "chain: '%s', packets: %d\n", name, (int)packets);
+
+	if (_packets != NULL) {
+		*_packets = packets;
+	}
+
+	return true;
+}
+
+uint16_t torture_get_local_port_from_transport(struct smb2_transport *t)
+{
+	const struct sockaddr_storage *local_ss;
+
+	local_ss = smbXcli_conn_local_sockaddr(t->conn);
+
+	return get_sockaddr_port(local_ss);
+}
+
+static bool torture_block_tcp_transport_name_internal(
+						struct torture_context *tctx,
+						struct smb2_transport *t,
+						const char *name,
+						bool unblock)
+{
+	char *cmd_in;
+	char *cmd_out;
+	const char *chain_in, *chain_out;
+	uint16_t port = torture_get_local_port_from_transport(t);
+	const char *ipt = iptables_command(tctx);
+
+	chain_in = samba_chain_name(tctx, name, "SAMBA_INPUT");
+	chain_out = samba_chain_name(tctx, name, "SAMBA_OUTPUT");
+	if (chain_in == NULL || chain_out == NULL) {
+		return false;
+	}
+
+	if (!unblock) {
+		filter_tcp_setup(tctx, false);
+		filter_tcp_setup_name(tctx, name, false);
+	}
+
+	torture_comment(tctx, "%sblocking %s dport %d\n",
+			unblock ? "un" : "", name, port);
+
+	cmd_in = talloc_asprintf(tctx,
+				 "%s %s %s -p tcp --dport %d -j DROP",
+				 ipt, unblock ? "-D" : "-I", chain_in, port);
+	cmd_out = talloc_asprintf(tctx,
+				  "%s %s %s -p tcp --sport %d -j DROP",
+				  ipt, unblock ? "-D" : "-I", chain_out, port);
+	if (cmd_in == NULL || cmd_out == NULL) {
+		return false;
+	}
+
+	if (!run_cmd(cmd_in)) {
+		return false;
+	}
+	/* if (!run_cmd(cmd_out)) return false; */
+
+	if (unblock) {
+		filter_tcp_setup_name(tctx, name, true);
+		/* better dont cleanup here */
+		/* filter_tcp_setup(tctx, true); */
+	}
+
+	return true;
+}
+
+bool torture_block_tcp_transport_name(struct torture_context *tctx,
+				      struct smb2_transport *t,
+				      const char *name)
+{
+	return torture_block_tcp_transport_name_internal(tctx, t, name, false);
+}
+
+bool torture_unblock_tcp_transport_name(struct torture_context *tctx,
+					struct smb2_transport *t,
+					const char *name)
+{
+	return torture_block_tcp_transport_name_internal(tctx, t, name, true);
+}
+
+void torture_unblock_cleanup(struct torture_context *tctx)
+{
+	filter_tcp_setup(tctx, true);
+}
diff --git a/source4/torture/smb2/block.h b/source4/torture/smb2/block.h
new file mode 100644
index 00000000000..9278caaa2b7
--- /dev/null
+++ b/source4/torture/smb2/block.h
@@ -0,0 +1,45 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * block SMB2 transports using iptables
+ *
+ * Copyright (C) Guenther Deschner, 2017
+ *
+ * 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/>.
+ */
+
+bool torture_list_tcp_transport_name(struct torture_context *tctx,
+				    const char *name,
+				    uint32_t *packets);
+
+bool torture_block_tcp_transport_name(struct torture_context *tctx,
+				      struct smb2_transport *t,
+				      const char *name);
+
+bool torture_unblock_tcp_transport_name(struct torture_context *tctx,
+					struct smb2_transport *t,
+					const char *name);
+
+void torture_unblock_cleanup(struct torture_context *tctx);
+
+uint16_t torture_get_local_port_from_transport(struct smb2_transport *t);
+
+#define torture_block_tcp_transport(_tctx, _t) \
+	torture_block_tcp_transport_name(_tctx, _t, #_t)
+
+#define torture_unblock_tcp_transport(_tctx, _t) \
+	torture_unblock_tcp_transport_name(_tctx, _t, #_t)
+
+#define torture_list_tcp_transport(_tctx, _t, _packets) \
+	torture_list_tcp_transport_name(_tctx, #_t, _packets)
diff --git a/source4/torture/smb2/wscript_build b/source4/torture/smb2/wscript_build
index 1183cb93772..e605a4589ac 100644
--- a/source4/torture/smb2/wscript_build
+++ b/source4/torture/smb2/wscript_build
@@ -3,6 +3,7 @@
 bld.SAMBA_MODULE('TORTURE_SMB2',
 	source='''
         acls.c
+        block.c
         compound.c
         connect.c
         create.c
-- 
2.20.1


From 170817a0e2c6c6f69f23ff5ae5a7ed1ed2b12526 Mon Sep 17 00:00:00 2001
From: Sachin Prabhu <sprabhu at redhat.com>
Date: Mon, 11 Mar 2019 17:14:44 +0000
Subject: [PATCH 10/19] s4-torture: Add #defines required by the new tests

New macros used by our tests.

Signed-off-by: Guenther Deschner <gd at samba.org>
Signed-off-by: Sachin Prabhu <sprabhu at redhat.com>
---
 source4/torture/smb2/multichannel.c | 73 +++++++++++++++++++++++++++--
 1 file changed, 68 insertions(+), 5 deletions(-)

diff --git a/source4/torture/smb2/multichannel.c b/source4/torture/smb2/multichannel.c
index 0a4203aa5fb..9aba784a980 100644
--- a/source4/torture/smb2/multichannel.c
+++ b/source4/torture/smb2/multichannel.c
@@ -30,14 +30,77 @@
 #include "librpc/gen_ndr/ndr_ioctl.h"
 #include "../libcli/smb/smbXcli_base.h"
 
-#define CHECK_STATUS(status, correct) do { \
-	if (!NT_STATUS_EQUAL(status, correct)) { \
+#define BASEDIR "multichanneltestdir"
+
+#define CHECK_STATUS(status, correct) \
+	torture_assert_ntstatus_equal_goto(tctx, status, correct,\
+					   ret, done, "")
+
+#define CHECK_VAL(v, correct) do { \
+	if ((v) != (correct)) { \
+		torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s" \
+				" got 0x%x - should be 0x%x\n", \
+				__location__, #v, (int)v, (int)correct); \
+		ret = false; \
+		goto done; \
+	} } while (0)
+
+#define CHECK_VAL_GREATER_THAN(v, gt_val) do { \
+	if ((v) <= (gt_val)) { \
 		torture_result(tctx, TORTURE_FAIL, \
-			"(%s) Incorrect status %s - should be %s\n", \
-			 __location__, nt_errstr(status), nt_errstr(correct)); \
-		return false; \
+				"(%s): wrong value for %s got 0x%x - " \
+				"should be greater than 0x%x\n", \
+				__location__, #v, (int)v, (int)gt_val); \
+		ret = false; \
+		goto done; \
 	} } while (0)
 
+#define CHECK_CREATED(__io, __created, __attribute)			\
+	do {								\
+		CHECK_VAL((__io)->out.create_action,			\
+				NTCREATEX_ACTION_ ## __created);	\
+		CHECK_VAL((__io)->out.alloc_size, 0);			\
+		CHECK_VAL((__io)->out.size, 0);				\
+		CHECK_VAL((__io)->out.file_attr, (__attribute));	\
+		CHECK_VAL((__io)->out.reserved2, 0);			\
+	} while (0)
+
+#define CHECK_PTR(ptr, correct) do { \
+	if ((ptr) != (correct)) { \
+		torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s " \
+				"got 0x%p - should be 0x%p\n", \
+				__location__, #ptr, ptr, correct); \
+		ret = false; \
+		goto done; \
+	} } while (0)
+
+#define CHECK_LEASE(__io, __state, __oplevel, __key, __flags)		\
+	do {								\
+		CHECK_VAL((__io)->out.lease_response.lease_version, 1); \
+		if (__oplevel) {					\
+			CHECK_VAL((__io)->out.oplock_level, \
+					SMB2_OPLOCK_LEVEL_LEASE); \
+			CHECK_VAL((__io)->out.lease_response.lease_key.data[0],\
+				  (__key)); \
+			CHECK_VAL((__io)->out.lease_response.lease_key.data[1],\
+				  ~(__key)); \
+			CHECK_VAL((__io)->out.lease_response.lease_state,\
+				  smb2_util_lease_state(__state)); \
+		} else {						\
+			CHECK_VAL((__io)->out.oplock_level,\
+				  SMB2_OPLOCK_LEVEL_NONE); \
+			CHECK_VAL((__io)->out.lease_response.lease_key.data[0],\
+				  0); \
+			CHECK_VAL((__io)->out.lease_response.lease_key.data[1],\
+				  0); \
+			CHECK_VAL((__io)->out.lease_response.lease_state, 0); \
+		}							\
+									\
+		CHECK_VAL((__io)->out.lease_response.lease_flags, (__flags)); \
+		CHECK_VAL((__io)->out.lease_response.lease_duration, 0); \
+		CHECK_VAL((__io)->out.lease_response.lease_epoch, 0); \
+	} while (0)
+
 static bool test_ioctl_network_interface_info(struct torture_context *tctx,
 					      struct smb2_tree *tree,
 					      struct fsctl_net_iface_info *info)
-- 
2.20.1


From a7c260a7b858d40b7112d197ddce8b61d758f539 Mon Sep 17 00:00:00 2001
From: Sachin Prabhu <sprabhu at redhat.com>
Date: Thu, 28 Feb 2019 12:09:08 +0000
Subject: [PATCH 11/19] s4-torture: Add helper functions to create channels.

Helper functions used by both oplock and lease break tests.

Signed-off-by: Guenther Deschner <gd at samba.org>
Signed-off-by: Sachin Prabhu <sprabhu at redhat.com>
---
 source4/torture/smb2/multichannel.c | 148 +++++++++++++++++++++++++++-
 1 file changed, 147 insertions(+), 1 deletion(-)

diff --git a/source4/torture/smb2/multichannel.c b/source4/torture/smb2/multichannel.c
index 9aba784a980..5207fa453da 100644
--- a/source4/torture/smb2/multichannel.c
+++ b/source4/torture/smb2/multichannel.c
@@ -23,12 +23,13 @@
 #include "libcli/smb2/smb2.h"
 #include "libcli/smb2/smb2_calls.h"
 #include "torture/torture.h"
-#include "torture/util.h"
 #include "torture/smb2/proto.h"
 #include "libcli/security/security.h"
 #include "librpc/gen_ndr/ndr_security.h"
 #include "librpc/gen_ndr/ndr_ioctl.h"
 #include "../libcli/smb/smbXcli_base.h"
+#include "libcli/resolve/resolve.h"
+#include "lib/param/param.h"
 
 #define BASEDIR "multichanneltestdir"
 
@@ -156,6 +157,151 @@ static bool test_multichannel_interface_info(struct torture_context *tctx,
 	return test_ioctl_network_interface_info(tctx, tree, &info);
 }
 
+static struct smb2_tree *test_multichannel_create_channel(
+				struct torture_context *tctx,
+				const char *host,
+				const char *share,
+				struct cli_credentials *credentials,
+				struct smbcli_options *transport_options,
+				struct smb2_tree *parent_tree
+				)
+{
+	NTSTATUS status;
+	struct smb2_transport *transport;
+	struct smb2_session *session;
+	bool ret = true;
+	struct smb2_tree *tree;
+
+	status = smb2_connect(tctx,
+			host,
+			lpcfg_smb_ports(tctx->lp_ctx),
+			share,
+			lpcfg_resolve_context(tctx->lp_ctx),
+			credentials,
+			&tree,
+			tctx->ev,
+			transport_options,
+			lpcfg_socket_options(tctx->lp_ctx),
+			lpcfg_gensec_settings(tctx, tctx->lp_ctx)
+			);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+			"smb2_connect failed");
+	transport = tree->session->transport;
+	transport->oplock.handler = torture_oplock_ack_handler;
+	transport->oplock.private_data = tree;
+	transport->lease.handler = torture_lease_handler;
+	transport->lease.private_data = tree;
+	torture_comment(tctx, "established transport [%p]\n", transport);
+
+	/*
+	 * If parent tree is set, bind the session to the parent transport
+	 */
+	if (parent_tree) {
+		session = smb2_session_channel(transport,
+				lpcfg_gensec_settings(tctx, tctx->lp_ctx),
+				parent_tree, parent_tree->session);
+		torture_assert_goto(tctx, session != NULL, ret, done,
+				"smb2_session_channel failed");
+
+		tree->smbXcli = parent_tree->smbXcli;
+		tree->session = session;
+		status = smb2_session_setup_spnego(session,
+						credentials,
+						0 /* previous_session_id */);
+		CHECK_STATUS(status, NT_STATUS_OK);
+		torture_comment(tctx, "bound new session to parent\n");
+	}
+	/*
+	 * We absolutely need to make sure to send something over this
+	 * connection to register the oplock break handler with the smb client
+	 * connection. If we do not send something (at least a keepalive), we
+	 * will *NEVER* receive anything over this transport.
+	 */
+	smb2_keepalive(transport);
+
+done:
+	if (ret) {
+		return tree;
+	} else {
+		return NULL;
+	}
+}
+
+bool test_multichannel_create_channels(
+				struct torture_context *tctx,
+				const char *host,
+				const char *share,
+				struct cli_credentials *credentials,
+				struct smbcli_options *transport_options,
+				struct smb2_tree **tree2A,
+				struct smb2_tree **tree2B,
+				struct smb2_tree **tree2C
+				)
+{
+	struct smb2_tree *tree;
+	struct smb2_transport *transport2A;
+	struct smb2_transport *transport2B;
+	struct smb2_transport *transport2C;
+	uint16_t local_port = 0;
+
+	transport_options->client_guid = GUID_random();
+
+	/* Session 2A */
+	torture_comment(tctx, "Setting up connection 2A\n");
+	tree = test_multichannel_create_channel(tctx, host, share,
+				credentials, transport_options, NULL);
+	if (!tree) {
+		goto done;
+	}
+	*tree2A = tree;
+	transport2A = tree->session->transport;
+	local_port = torture_get_local_port_from_transport(transport2A);
+	torture_comment(tctx, "transport2A uses tcp port: %d\n", local_port);
+
+	/* Session 2B */
+	if (tree2B) {
+		torture_comment(tctx, "Setting up connection 2B\n");
+		tree = test_multichannel_create_channel(tctx, host, share,
+				credentials, transport_options, *tree2A);
+		if (!tree) {
+			goto done;
+		}
+		*tree2B = tree;
+		transport2B = tree->session->transport;
+		local_port = torture_get_local_port_from_transport(transport2B);
+		torture_comment(tctx, "transport2B uses tcp port: %d\n",
+								local_port);
+	}
+
+	/* Session 2C */
+	if (tree2C) {
+		torture_comment(tctx, "Setting up connection 2C\n");
+		tree = test_multichannel_create_channel(tctx, host, share,
+				credentials, transport_options, *tree2A);
+		if (!tree) {
+			goto done;
+		}
+		*tree2C = tree;
+		transport2C = tree->session->transport;
+		local_port = torture_get_local_port_from_transport(transport2C);
+		torture_comment(tctx, "transport2C uses tcp port: %d\n",
+								local_port);
+	}
+
+	return true;
+done:
+	return false;
+}
+
+static void test_multichannel_free_channels(struct smb2_tree *tree2A,
+					     struct smb2_tree *tree2B,
+					     struct smb2_tree *tree2C)
+{
+	TALLOC_FREE(tree2A);
+	TALLOC_FREE(tree2B);
+	TALLOC_FREE(tree2C);
+}
+
 struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
 {
 	struct torture_suite *suite = torture_suite_create(ctx, "multichannel");
-- 
2.20.1


From 8d827b0373606cd893f4aac920abe1f6cdced8e4 Mon Sep 17 00:00:00 2001
From: Sachin Prabhu <sprabhu at redhat.com>
Date: Thu, 28 Feb 2019 12:51:02 +0000
Subject: [PATCH 12/19] s4-torture: Add handlers to block channels for testing

We use two methods to block channels

1) Simply ignore incoming oplock break requests and do not respond to
them.
This method doesn't work against Microsoft Windows based servers which
rely on the tcp stack for confirmation that the oplock break command was
sent to the client machine. This is meant to be used with samba servers
and is the default method.

2) Use iptables to block the channel.
The method requires the use of a privileged account and can only be used
on Linux systems with iptables installed. To use this blocking method,
pass the option
--option=torture:use_iptables=true

Signed-off-by: Guenther Deschner <gd at samba.org>
Signed-off-by: Sachin Prabhu <sprabhu at redhat.com>
---
 source4/torture/smb2/multichannel.c | 97 +++++++++++++++++++++++++++++
 1 file changed, 97 insertions(+)

diff --git a/source4/torture/smb2/multichannel.c b/source4/torture/smb2/multichannel.c
index 5207fa453da..4a490f04c97 100644
--- a/source4/torture/smb2/multichannel.c
+++ b/source4/torture/smb2/multichannel.c
@@ -30,6 +30,8 @@
 #include "../libcli/smb/smbXcli_base.h"
 #include "libcli/resolve/resolve.h"
 #include "lib/param/param.h"
+#include "oplock_break_handler.h"
+#include "torture/smb2/block.h"
 
 #define BASEDIR "multichanneltestdir"
 
@@ -302,6 +304,101 @@ static void test_multichannel_free_channels(struct smb2_tree *tree2A,
 	TALLOC_FREE(tree2C);
 }
 
+/*
+ * We simulate blocking incoming oplock break requests by simply ignoring
+ * the incoming break requests.
+ */
+static bool test_set_ignore_break_handler(struct torture_context *tctx,
+					  struct smb2_transport *transport)
+{
+	transport->oplock.handler = torture_oplock_ignore_handler;
+	transport->lease.handler = torture_lease_ignore_handler;
+
+	return true;
+}
+
+static bool test_reset_break_handler(struct torture_context *tctx,
+				     struct smb2_transport *transport)
+{
+	transport->oplock.handler = torture_oplock_ack_handler;
+	transport->lease.handler = torture_lease_handler;
+
+	return true;
+}
+
+/*
+ * Use iptables to block channels
+ */
+static bool test_iptables_block_channel(struct torture_context *tctx,
+					struct smb2_transport *transport,
+					const char *name)
+{
+	uint16_t local_port;
+	bool ret;
+
+	local_port = torture_get_local_port_from_transport(transport);
+	torture_comment(tctx, "transport uses tcp port: %d\n", local_port);
+	ret = torture_block_tcp_transport_name(tctx, transport, name);
+	torture_assert(tctx, ret, "we could not block tcp transport");
+
+	return ret;
+}
+
+static bool test_iptables_unblock_channel(struct torture_context *tctx,
+					  struct smb2_transport *transport,
+					  const char *name)
+{
+	uint16_t local_port;
+	bool ret;
+
+	local_port = torture_get_local_port_from_transport(transport);
+	torture_comment(tctx, "transport uses tcp port: %d\n", local_port);
+	ret = torture_unblock_tcp_transport_name(tctx, transport, name);
+	torture_assert(tctx, ret, "we could not block tcp transport");
+
+	return ret;
+}
+
+#define test_block_channel(_tctx, _t) _test_block_channel(_tctx, _t, #_t)
+static bool _test_block_channel(struct torture_context *tctx,
+					  struct smb2_transport *transport,
+					  const char *name)
+{
+	bool use_iptables = torture_setting_bool(tctx,
+					"use_iptables", false);
+
+	if (use_iptables) {
+		return test_iptables_block_channel(tctx, transport, name);
+	} else {
+		return test_set_ignore_break_handler(tctx, transport);
+	}
+}
+
+#define test_unblock_channel(_tctx, _t) _test_unblock_channel(_tctx, _t, #_t)
+static bool _test_unblock_channel(struct torture_context *tctx,
+					  struct smb2_transport *transport,
+					  const char *name)
+{
+	bool use_iptables = torture_setting_bool(tctx,
+					"use_iptables", false);
+
+	if (use_iptables) {
+		return test_iptables_unblock_channel(tctx, transport, name);
+	} else {
+		return test_reset_break_handler(tctx, transport);
+	}
+}
+
+static void test_cleanup_blocked_channels(struct torture_context *tctx)
+{
+	bool use_iptables = torture_setting_bool(tctx,
+					"use_iptables", false);
+
+	if (use_iptables) {
+		torture_unblock_cleanup(tctx);
+	}
+}
+
 struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
 {
 	struct torture_suite *suite = torture_suite_create(ctx, "multichannel");
-- 
2.20.1


From d9cced2437e3a6f18dbcde30af5b57b48ef3c20e Mon Sep 17 00:00:00 2001
From: Sachin Prabhu <sprabhu at redhat.com>
Date: Mon, 11 Mar 2019 19:42:18 +0000
Subject: [PATCH 13/19] s4-torture: Add oplock break retry tests - test1

Test to confirm that server sends oplock breaks as expected.

Signed-off-by: Guenther Deschner <gd at samba.org>
Signed-off-by: Sachin Prabhu <sprabhu at redhat.com>
---
 source4/torture/smb2/multichannel.c | 220 ++++++++++++++++++++++++++++
 1 file changed, 220 insertions(+)

diff --git a/source4/torture/smb2/multichannel.c b/source4/torture/smb2/multichannel.c
index 4a490f04c97..34283f34e14 100644
--- a/source4/torture/smb2/multichannel.c
+++ b/source4/torture/smb2/multichannel.c
@@ -28,8 +28,11 @@
 #include "librpc/gen_ndr/ndr_security.h"
 #include "librpc/gen_ndr/ndr_ioctl.h"
 #include "../libcli/smb/smbXcli_base.h"
+#include "lib/cmdline/popt_common.h"
+#include "libcli/security/security.h"
 #include "libcli/resolve/resolve.h"
 #include "lib/param/param.h"
+#include "lib/events/events.h"
 #include "oplock_break_handler.h"
 #include "torture/smb2/block.h"
 
@@ -304,6 +307,45 @@ static void test_multichannel_free_channels(struct smb2_tree *tree2A,
 	TALLOC_FREE(tree2C);
 }
 
+static bool test_multichannel_initial_checks(struct torture_context *tctx,
+					     struct smb2_tree *tree1)
+{
+	struct smb2_transport *transport1 = tree1->session->transport;
+	uint32_t server_capabilities;
+	struct fsctl_net_iface_info info;
+
+	if (smbXcli_conn_protocol(transport1->conn) < PROTOCOL_SMB3_00) {
+		torture_skip_goto(tctx, fail,
+				  "SMB 3.X Dialect family required for "
+				  "Multichannel tests\n");
+	}
+
+	server_capabilities = smb2cli_conn_server_capabilities(
+					tree1->session->transport->conn);
+	if (!(server_capabilities & SMB2_CAP_MULTI_CHANNEL)) {
+		torture_skip_goto(tctx, fail,
+			     "Server does not support multichannel.");
+	}
+
+	torture_assert(tctx,
+		test_ioctl_network_interface_info(tctx, tree1, &info),
+		"failed to retrieve network interface info");
+
+	return true;
+fail:
+	return false;
+}
+
+static void test_multichannel_init_smb_create(struct smb2_create *io)
+{
+	io->in.durable_open = false;
+	io->in.durable_open_v2 = true;
+	io->in.persistent_open = false;
+	io->in.create_guid = GUID_random();
+	io->in.timeout = 0x493E0; /* 300000 */
+	/* windows 2016 returns 300000 0x493E0 */
+}
+
 /*
  * We simulate blocking incoming oplock break requests by simply ignoring
  * the incoming break requests.
@@ -399,12 +441,190 @@ static void test_cleanup_blocked_channels(struct torture_context *tctx)
 	}
 }
 
+/*
+ * Oplock break - Test 1
+ * Test to confirm that server sends oplock breaks as expected.
+ * open file1 in session 2A
+ * open file2 in session 2B
+ * open file1 in session 1
+ *      oplock break received
+ * open file1 in session 1
+ *      oplock break received
+ * Cleanup
+ */
+static bool test_multichannel_oplock_break_test1(struct torture_context *tctx,
+					   struct smb2_tree *tree1)
+{
+	const char *host = torture_setting_string(tctx, "host", NULL);
+	const char *share = torture_setting_string(tctx, "share", NULL);
+	struct cli_credentials *credentials = popt_get_cmdline_credentials();
+	NTSTATUS status;
+	TALLOC_CTX *mem_ctx = talloc_new(tctx);
+	struct smb2_handle _h;
+	struct smb2_handle h_client1_file1 = { 0 };
+	struct smb2_handle h_client1_file2 = { 0 };
+	struct smb2_handle h_client1_file3 = { 0 };
+	struct smb2_handle h_client2_file1 = { 0 };
+	struct smb2_handle h_client2_file2 = { 0 };
+	struct smb2_handle h_client2_file3 = { 0 };
+	struct smb2_create io1, io2, io3;
+	bool ret = true;
+	const char *fname1 = BASEDIR "\\oplock_break_test1.dat";
+	const char *fname2 = BASEDIR "\\oplock_break_test2.dat";
+	const char *fname3 = BASEDIR "\\oplock_break_test3.dat";
+	struct smb2_tree *tree2A = NULL;
+	struct smb2_tree *tree2B = NULL;
+	struct smb2_tree *tree2C = NULL;
+	struct smb2_transport *transport1 = tree1->session->transport;
+	struct smbcli_options transport2_options;
+	struct smb2_session *session1 = tree1->session;
+	uint16_t local_port = 0;
+
+	if (!test_multichannel_initial_checks(tctx, tree1)) {
+		return true;
+	}
+
+	torture_comment(tctx, "Oplock break retry: Test1\n");
+
+	torture_reset_break_info(tctx, &break_info);
+
+	transport1->oplock.handler = torture_oplock_ack_handler;
+	transport1->oplock.private_data = tree1;
+	torture_comment(tctx, "transport1  [%p]\n", transport1);
+	local_port = torture_get_local_port_from_transport(transport1);
+	torture_comment(tctx, "transport1 uses tcp port: %d\n", local_port);
+
+	status = torture_smb2_testdir(tree1, BASEDIR, &_h);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	smb2_util_close(tree1, _h);
+	smb2_util_unlink(tree1, fname1);
+	smb2_util_unlink(tree1, fname2);
+	smb2_util_unlink(tree1, fname3);
+	CHECK_VAL(break_info.count, 0);
+
+	smb2_oplock_create_share(&io1, fname1,
+			smb2_util_share_access("RWD"),
+			smb2_util_oplock_level("b"));
+	test_multichannel_init_smb_create(&io1);
+
+	smb2_oplock_create_share(&io2, fname2,
+			smb2_util_share_access("RWD"),
+			smb2_util_oplock_level("b"));
+	test_multichannel_init_smb_create(&io2);
+
+	smb2_oplock_create_share(&io3, fname3,
+			smb2_util_share_access("RWD"),
+			smb2_util_oplock_level("b"));
+	test_multichannel_init_smb_create(&io3);
+
+	transport2_options = transport1->options;
+
+	ret = test_multichannel_create_channels(tctx, host, share,
+						  credentials,
+						  &transport2_options,
+						  &tree2A, &tree2B, NULL);
+	torture_assert(tctx, ret, "Could not create channels.\n");
+
+	/* 2a opens file1 */
+	torture_comment(tctx, "client2 opens fname1 via session 2A\n");
+	status = smb2_create(tree2A, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client2_file1 = io1.out.file.handle;
+	CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
+	torture_wait_for_oplock_break(tctx);
+	CHECK_VAL(break_info.count, 0);
+
+	/* 2b opens file2 */
+	torture_comment(tctx, "client2 opens fname2 via session 2B\n");
+	status = smb2_create(tree2B, mem_ctx, &io2);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client2_file2 = io2.out.file.handle;
+	CHECK_CREATED(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
+	torture_wait_for_oplock_break(tctx);
+	CHECK_VAL(break_info.count, 0);
+
+
+	/* 1 opens file1 - batchoplock break? */
+	torture_comment(tctx, "client1 opens fname1 via session 1\n");
+	io1.in.oplock_level = smb2_util_oplock_level("b");
+	status = smb2_create(tree1, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client1_file1 = io1.out.file.handle;
+	CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("s"));
+	torture_wait_for_oplock_break(tctx);
+	CHECK_VAL(break_info.count, 1);
+
+	torture_reset_break_info(tctx, &break_info);
+
+	/* 1 opens file2 - batchoplock break? */
+	torture_comment(tctx, "client1 opens fname2 via session 1\n");
+	io2.in.oplock_level = smb2_util_oplock_level("b");
+	status = smb2_create(tree1, mem_ctx, &io2);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client1_file2 = io2.out.file.handle;
+	CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("s"));
+	torture_wait_for_oplock_break(tctx);
+	CHECK_VAL(break_info.count, 1);
+
+	/* cleanup everything */
+	torture_reset_break_info(tctx, &break_info);
+
+	smb2_util_close(tree1, h_client1_file1);
+	smb2_util_close(tree1, h_client1_file2);
+	smb2_util_close(tree1, h_client1_file3);
+	smb2_util_close(tree2A, h_client2_file1);
+	smb2_util_close(tree2A, h_client2_file2);
+	smb2_util_close(tree2A, h_client2_file3);
+
+	smb2_util_unlink(tree1, fname1);
+	smb2_util_unlink(tree1, fname2);
+	smb2_util_unlink(tree1, fname3);
+	CHECK_VAL(break_info.count, 0);
+	test_multichannel_free_channels(tree2A, tree2B, tree2C);
+	tree2A = tree2B = tree2C = NULL;
+done:
+	tree1->session = session1;
+
+	smb2_util_close(tree1, h_client1_file1);
+	smb2_util_close(tree1, h_client1_file2);
+	smb2_util_close(tree1, h_client1_file3);
+	if (tree2A != NULL) {
+		smb2_util_close(tree2A, h_client2_file1);
+		smb2_util_close(tree2A, h_client2_file2);
+		smb2_util_close(tree2A, h_client2_file3);
+	}
+
+	smb2_util_unlink(tree1, fname1);
+	smb2_util_unlink(tree1, fname2);
+	smb2_util_unlink(tree1, fname3);
+	smb2_deltree(tree1, BASEDIR);
+
+	test_multichannel_free_channels(tree2A, tree2B, tree2C);
+	talloc_free(tree1);
+	talloc_free(mem_ctx);
+
+	return ret;
+}
+
 struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
 {
 	struct torture_suite *suite = torture_suite_create(ctx, "multichannel");
+	struct torture_suite *suite_generic = torture_suite_create(ctx,
+								   "generic");
+	struct torture_suite *suite_oplocks = torture_suite_create(ctx,
+								   "oplocks");
+
+	torture_suite_add_suite(suite, suite_generic);
+	torture_suite_add_suite(suite, suite_oplocks);
 
 	torture_suite_add_1smb2_test(suite, "interface_info",
 				     test_multichannel_interface_info);
+	torture_suite_add_1smb2_test(suite_oplocks, "test1",
+				     test_multichannel_oplock_break_test1);
 
 	suite->description = talloc_strdup(suite, "SMB2 Multichannel tests");
 
-- 
2.20.1


From 27be88475a2583ef642c24accbfe33fdc5a8a541 Mon Sep 17 00:00:00 2001
From: Sachin Prabhu <sprabhu at redhat.com>
Date: Mon, 11 Mar 2019 20:02:33 +0000
Subject: [PATCH 14/19] s4-torture: Add oplock break retry tests - test2

Test to see if oplock break retries are sent by the server.
Also checks to see if new channels can be created and used
after an oplock break retry.

The test by default blocks channels by ignoring incoming lease break
requests on that channel. This does not work when testing against a
windows server.
Use --option=torture:use_iptables=true to use iptables to block ports
instead when testing against windows servers.

Signed-off-by: Guenther Deschner <gd at samba.org>
Signed-off-by: Sachin Prabhu <sprabhu at redhat.com>
---
 source4/torture/smb2/multichannel.c | 256 ++++++++++++++++++++++++++++
 1 file changed, 256 insertions(+)

diff --git a/source4/torture/smb2/multichannel.c b/source4/torture/smb2/multichannel.c
index 34283f34e14..f9f6e024626 100644
--- a/source4/torture/smb2/multichannel.c
+++ b/source4/torture/smb2/multichannel.c
@@ -610,6 +610,260 @@ done:
 	return ret;
 }
 
+/*
+ * Oplock Break Test 2
+ * Test to see if oplock break retries are sent by the server.
+ * Also checks to see if new channels can be created and used
+ * after an oplock break retry.
+ * open file1 in 2A
+ * open file2 in 2B
+ * open file1 in session 1
+ *      oplock break received
+ * block channel on which oplock break received
+ * open file2 in session 1
+ *      oplock break not received. Retry received.
+ *      file opened
+ * write to file2 on 2B
+ *      Break sent to session 1(which has file2 open)
+ *      Break sent to session 2A(which has read oplock)
+ * close file1 in session 1
+ * open file1 with session 1
+ * unblock blocked channel
+ * disconnect blocked channel
+ * connect channel 2D
+ * open file3 in 2D
+ * open file3 in session 1
+ *      receive break
+ */
+static bool test_multichannel_oplock_break_test2(struct torture_context *tctx,
+					   struct smb2_tree *tree1)
+{
+	const char *host = torture_setting_string(tctx, "host", NULL);
+	const char *share = torture_setting_string(tctx, "share", NULL);
+	struct cli_credentials *credentials = popt_get_cmdline_credentials();
+	NTSTATUS status;
+	TALLOC_CTX *mem_ctx = talloc_new(tctx);
+	struct smb2_handle _h;
+	struct smb2_handle h_client1_file1 = { 0 };
+	struct smb2_handle h_client1_file2 = { 0 };
+	struct smb2_handle h_client1_file3 = { 0 };
+	struct smb2_handle h_client2_file1 = { 0 };
+	struct smb2_handle h_client2_file2 = { 0 };
+	struct smb2_handle h_client2_file3 = { 0 };
+	struct smb2_create io1, io2, io3;
+	bool ret = true;
+	const char *fname1 = BASEDIR "\\oplock_break_test1.dat";
+	const char *fname2 = BASEDIR "\\oplock_break_test2.dat";
+	const char *fname3 = BASEDIR "\\oplock_break_test3.dat";
+	struct smb2_tree *tree2A = NULL;
+	struct smb2_tree *tree2B = NULL;
+	struct smb2_tree *tree2C = NULL;
+	struct smb2_tree *tree2D = NULL;
+	struct smb2_transport *transport1 = tree1->session->transport;
+	struct smb2_transport *transport2 = NULL;
+	struct smbcli_options transport2_options;
+	struct smb2_session *session1 = tree1->session;
+	uint16_t local_port = 0;
+	DATA_BLOB blob;
+	bool block_ok = false;
+	bool unblock_ok = false;
+
+	if (!test_multichannel_initial_checks(tctx, tree1)) {
+		return true;
+	}
+
+	torture_comment(tctx, "Oplock break retry: Test2\n");
+
+	torture_reset_break_info(tctx, &break_info);
+
+	transport1->oplock.handler = torture_oplock_ack_handler;
+	transport1->oplock.private_data = tree1;
+	torture_comment(tctx, "transport1  [%p]\n", transport1);
+	local_port = torture_get_local_port_from_transport(transport1);
+	torture_comment(tctx, "transport1 uses tcp port: %d\n", local_port);
+
+	status = torture_smb2_testdir(tree1, BASEDIR, &_h);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	smb2_util_close(tree1, _h);
+	smb2_util_unlink(tree1, fname1);
+	smb2_util_unlink(tree1, fname2);
+	smb2_util_unlink(tree1, fname3);
+	CHECK_VAL(break_info.count, 0);
+
+	smb2_oplock_create_share(&io1, fname1,
+			smb2_util_share_access("RWD"),
+			smb2_util_oplock_level("b"));
+	test_multichannel_init_smb_create(&io1);
+
+	smb2_oplock_create_share(&io2, fname2,
+			smb2_util_share_access("RWD"),
+			smb2_util_oplock_level("b"));
+	test_multichannel_init_smb_create(&io2);
+
+	smb2_oplock_create_share(&io3, fname3,
+			smb2_util_share_access("RWD"),
+			smb2_util_oplock_level("b"));
+	test_multichannel_init_smb_create(&io3);
+
+	transport2_options = transport1->options;
+
+	ret = test_multichannel_create_channels(tctx, host, share,
+						  credentials,
+						  &transport2_options,
+						  &tree2A, &tree2B, &tree2C);
+	torture_assert(tctx, ret, "Could not create channels.\n")
+
+	torture_comment(tctx, "client2 opens fname1 via session 2A\n");
+	io1.in.oplock_level = smb2_util_oplock_level("b");
+	status = smb2_create(tree2A, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client2_file1 = io1.out.file.handle;
+	CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
+	torture_wait_for_oplock_break(tctx);
+	CHECK_VAL(break_info.count, 0);
+
+
+	torture_comment(tctx, "client2 opens fname2 via session 2B\n");
+	io2.in.oplock_level = smb2_util_oplock_level("b");
+	status = smb2_create(tree2B, mem_ctx, &io2);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client2_file2 = io2.out.file.handle;
+	CHECK_CREATED(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
+	torture_wait_for_oplock_break(tctx);
+	CHECK_VAL(break_info.count, 0);
+
+
+	torture_comment(tctx, "client1 opens fname1 via session 1\n");
+	io1.in.oplock_level = smb2_util_oplock_level("b");
+	status = smb2_create(tree1, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client1_file1 = io1.out.file.handle;
+	CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("s"));
+	torture_wait_for_oplock_break(tctx);
+	CHECK_VAL(break_info.count, 1);
+
+	/* We use the transport over which this oplock break was received */
+	transport2 = break_info.received_transport;
+	torture_reset_break_info(tctx, &break_info);
+
+	/* block channel */
+	block_ok = test_block_channel(tctx, transport2);
+
+	torture_comment(tctx, "client1 opens fname2 via session 1\n");
+	io2.in.oplock_level = smb2_util_oplock_level("b");
+	status = smb2_create(tree1, mem_ctx, &io2);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client1_file2 = io2.out.file.handle;
+	CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("s"));
+
+	/*
+	 * Samba downgrades oplock to a level 2 oplock.
+	 * Windows 2016 revokes oplock
+	 */
+	torture_wait_for_oplock_break(tctx);
+	CHECK_VAL(break_info.count, 1);
+	torture_reset_break_info(tctx, &break_info);
+
+	torture_comment(tctx, "Trying write to file2 on tree2B\n");
+
+	blob = data_blob_string_const("Here I am");
+	status = smb2_util_write(tree2B,
+				 h_client2_file2,
+				 blob.data,
+				 0,
+				 blob.length);
+	torture_assert_ntstatus_ok(tctx, status,
+		"failed to write file2 via channel 2B");
+
+	/*
+	 * Samba: Write triggers 2 oplock breaks
+	 *  for session 1 which has file2 open
+	 *  for session 2 which has type 2 oplock
+	 * Windows 2016: Only one oplock break for session 1
+	 */
+	torture_wait_for_oplock_break(tctx);
+	CHECK_VAL_GREATER_THAN(break_info.count, 0);
+	torture_reset_break_info(tctx, &break_info);
+
+	torture_comment(tctx, "client1 closes fname2 via session 1\n");
+	smb2_util_close(tree1, h_client1_file2);
+
+	torture_comment(tctx, "client1 opens fname2 via session 1 again\n");
+	io2.in.oplock_level = smb2_util_oplock_level("b");
+	status = smb2_create(tree1, mem_ctx, &io2);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client1_file2 = io2.out.file.handle;
+	io2.out.alloc_size = 0;
+	io2.out.size = 0;
+	CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("s"));
+
+	/*
+	 * now add a fourth channel and repeat the test, we need to reestablish
+	 * transport2 because the remote end has invalidated our connection
+	 */
+	torture_comment(tctx, "Connecting session 2D\n");
+	tree2D = test_multichannel_create_channel(tctx, host, share,
+				     credentials, &transport2_options, tree2B);
+	if (!tree2D) {
+		goto done;
+	}
+
+	torture_reset_break_info(tctx, &break_info);
+	torture_comment(tctx, "client 2 opening fname3 over transport2D\n");
+	status = smb2_create(tree2D, mem_ctx, &io3);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client2_file3 = io3.out.file.handle;
+	CHECK_CREATED(&io3, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io3.out.oplock_level, smb2_util_oplock_level("b"));
+	torture_wait_for_oplock_break(tctx);
+	CHECK_VAL(break_info.count, 0);
+
+	torture_comment(tctx, "client1 opens fname3 via session 1\n");
+	status = smb2_create(tree1, mem_ctx, &io3);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client1_file3 = io3.out.file.handle;
+	CHECK_CREATED(&io3, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_VAL(io3.out.oplock_level, smb2_util_oplock_level("s"));
+	torture_wait_for_oplock_break(tctx);
+	CHECK_VAL(break_info.count, 1);
+
+done:
+	if (block_ok && !unblock_ok) {
+		test_unblock_channel(tctx, transport2);
+	}
+	test_cleanup_blocked_channels(tctx);
+
+	tree1->session = session1;
+
+	smb2_util_close(tree1, h_client1_file1);
+	smb2_util_close(tree1, h_client1_file2);
+	smb2_util_close(tree1, h_client1_file3);
+	if (tree2B != NULL) {
+		smb2_util_close(tree2B, h_client2_file1);
+		smb2_util_close(tree2B, h_client2_file2);
+		smb2_util_close(tree2B, h_client2_file3);
+	}
+
+	smb2_util_unlink(tree1, fname1);
+	smb2_util_unlink(tree1, fname2);
+	smb2_util_unlink(tree1, fname3);
+	smb2_deltree(tree1, BASEDIR);
+
+	test_multichannel_free_channels(tree2A, tree2B, tree2C);
+	if (tree2D != NULL) {
+		TALLOC_FREE(tree2D);
+	}
+	talloc_free(tree1);
+	talloc_free(mem_ctx);
+
+	return ret;
+}
+
 struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
 {
 	struct torture_suite *suite = torture_suite_create(ctx, "multichannel");
@@ -625,6 +879,8 @@ struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
 				     test_multichannel_interface_info);
 	torture_suite_add_1smb2_test(suite_oplocks, "test1",
 				     test_multichannel_oplock_break_test1);
+	torture_suite_add_1smb2_test(suite_oplocks, "test2",
+				     test_multichannel_oplock_break_test2);
 
 	suite->description = talloc_strdup(suite, "SMB2 Multichannel tests");
 
-- 
2.20.1


From 9742917f9d1c798217f6ea5749430dc0c3398eca Mon Sep 17 00:00:00 2001
From: Sachin Prabhu <sprabhu at redhat.com>
Date: Mon, 11 Mar 2019 20:05:02 +0000
Subject: [PATCH 15/19] s4-torture: Add lease break retry tests - test1

Test to check if lease breaks are sent by the server as expected.

Signed-off-by: Guenther Deschner <gd at samba.org>
Signed-off-by: Sachin Prabhu <sprabhu at redhat.com>
---
 source4/torture/smb2/multichannel.c | 213 ++++++++++++++++++++++++++++
 1 file changed, 213 insertions(+)

diff --git a/source4/torture/smb2/multichannel.c b/source4/torture/smb2/multichannel.c
index f9f6e024626..e2af94756b3 100644
--- a/source4/torture/smb2/multichannel.c
+++ b/source4/torture/smb2/multichannel.c
@@ -34,6 +34,7 @@
 #include "lib/param/param.h"
 #include "lib/events/events.h"
 #include "oplock_break_handler.h"
+#include "lease_break_handler.h"
 #include "torture/smb2/block.h"
 
 #define BASEDIR "multichanneltestdir"
@@ -864,6 +865,213 @@ done:
 	return ret;
 }
 
+static const uint64_t LEASE1F1 = 0xBADC0FFEE0DDF00Dull;
+static const uint64_t LEASE1F2 = 0xBADC0FFEE0DDD00Dull;
+static const uint64_t LEASE1F3 = 0xDADC0FFEE0DDD00Dull;
+static const uint64_t LEASE2F1 = 0xDEADBEEFFEEDBEADull;
+static const uint64_t LEASE2F2 = 0xDAD0FFEDD00DF00Dull;
+static const uint64_t LEASE2F3 = 0xBAD0FFEDD00DF00Dull;
+
+/*
+ * Lease Break Test 1:
+ * Test to check if lease breaks are sent by the server as expected.
+ *      open file1 in session 2A
+ *      open file2 in session 2B
+ *      open file3 in session 2C
+ *      open file1 in session 1
+ *           lease break sent
+ *      open file2 in session 1
+ *           lease break sent
+ *      open file3 in session 1
+ *           lease break sent
+ */
+static bool test_multichannel_lease_break_test1(struct torture_context *tctx,
+						struct smb2_tree *tree1)
+{
+	const char *host = torture_setting_string(tctx, "host", NULL);
+	const char *share = torture_setting_string(tctx, "share", NULL);
+	struct cli_credentials *credentials = popt_get_cmdline_credentials();
+	NTSTATUS status;
+	TALLOC_CTX *mem_ctx = talloc_new(tctx);
+	struct smb2_handle _h;
+	struct smb2_handle *h = NULL;
+	struct smb2_handle h_client1_file1 = { 0 };
+	struct smb2_handle h_client1_file2 = { 0 };
+	struct smb2_handle h_client1_file3 = { 0 };
+	struct smb2_handle h_client2_file1 = { 0 };
+	struct smb2_handle h_client2_file2 = { 0 };
+	struct smb2_handle h_client2_file3 = { 0 };
+	struct smb2_create io1, io2, io3;
+	bool ret = true;
+	const char *fname1 = BASEDIR "\\lease_break_test1.dat";
+	const char *fname2 = BASEDIR "\\lease_break_test2.dat";
+	const char *fname3 = BASEDIR "\\lease_break_test3.dat";
+	struct smb2_tree *tree2A = NULL;
+	struct smb2_tree *tree2B = NULL;
+	struct smb2_tree *tree2C = NULL;
+	struct smb2_transport *transport1 = tree1->session->transport;
+	struct smbcli_options transport2_options;
+	struct smb2_session *session1 = tree1->session;
+	uint16_t local_port = 0;
+	struct smb2_lease ls1;
+	struct smb2_lease ls2;
+	struct smb2_lease ls3;
+
+	if (!test_multichannel_initial_checks(tctx, tree1)) {
+		return true;
+	}
+
+	torture_comment(tctx, "Lease break retry: Test1\n");
+
+	torture_reset_lease_break_info(tctx, &lease_break_info);
+
+	transport1->lease.handler = torture_lease_handler;
+	transport1->lease.private_data = tree1;
+	torture_comment(tctx, "transport1  [%p]\n", transport1);
+	local_port = torture_get_local_port_from_transport(transport1);
+	torture_comment(tctx, "transport1 uses tcp port: %d\n", local_port);
+
+	status = torture_smb2_testdir(tree1, BASEDIR, &_h);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	smb2_util_close(tree1, _h);
+	smb2_util_unlink(tree1, fname1);
+	smb2_util_unlink(tree1, fname2);
+	smb2_util_unlink(tree1, fname3);
+	CHECK_VAL(lease_break_info.count, 0);
+
+	smb2_lease_create(&io1, &ls1, false, fname1, LEASE2F1,
+			  smb2_util_lease_state("RHW"));
+	test_multichannel_init_smb_create(&io1);
+
+	smb2_lease_create(&io2, &ls2, false, fname2, LEASE2F2,
+			  smb2_util_lease_state("RHW"));
+	test_multichannel_init_smb_create(&io2);
+
+	smb2_lease_create(&io3, &ls3, false, fname3, LEASE2F3,
+			  smb2_util_lease_state("RHW"));
+	test_multichannel_init_smb_create(&io3);
+
+	transport2_options = transport1->options;
+
+	ret = test_multichannel_create_channels(tctx, host, share,
+						  credentials,
+						  &transport2_options,
+						  &tree2A, &tree2B, &tree2C);
+	torture_assert(tctx, ret, "Could not create channels.\n");
+
+	/* 2a opens file1 */
+	torture_comment(tctx, "client2 opens fname1 via session 2A\n");
+	status = smb2_create(tree2A, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client2_file1 = io1.out.file.handle;
+	CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io1, "RHW", true, LEASE2F1, 0);
+	CHECK_VAL(lease_break_info.count, 0);
+
+	/* 2b opens file2 */
+	torture_comment(tctx, "client2 opens fname2 via session 2B\n");
+	status = smb2_create(tree2B, mem_ctx, &io2);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client2_file2 = io2.out.file.handle;
+	CHECK_CREATED(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io2, "RHW", true, LEASE2F2, 0);
+	CHECK_VAL(lease_break_info.count, 0);
+
+	/* 2c opens file3 */
+	torture_comment(tctx, "client2 opens fname3 via session 2C\n");
+	smb2_lease_create(&io3, &ls3, false, fname3, LEASE2F3,
+			  smb2_util_lease_state("RHW"));
+	status = smb2_create(tree2C, mem_ctx, &io3);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client2_file3 = io3.out.file.handle;
+	CHECK_CREATED(&io3, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io3, "RHW", true, LEASE2F3, 0);
+	CHECK_VAL(lease_break_info.count, 0);
+
+	/* 1 opens file1 - lease break? */
+	torture_comment(tctx, "client1 opens fname1 via session 1\n");
+	smb2_lease_create(&io1, &ls1, false, fname1, LEASE1F1,
+			  smb2_util_lease_state("RHW"));
+	status = smb2_create(tree1, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client1_file1 = io1.out.file.handle;
+	CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io1, "RH", true, LEASE1F1, 0);
+	CHECK_BREAK_INFO("RHW", "RH", LEASE2F1);
+	CHECK_VAL(lease_break_info.count, 1);
+
+	torture_reset_lease_break_info(tctx, &lease_break_info);
+
+	/* 1 opens file2 - lease break? */
+	torture_comment(tctx, "client1 opens fname2 via session 1\n");
+	smb2_lease_create(&io2, &ls2, false, fname2, LEASE1F2,
+			  smb2_util_lease_state("RHW"));
+	status = smb2_create(tree1, mem_ctx, &io2);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client1_file2 = io2.out.file.handle;
+	CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io2, "RH", true, LEASE1F2, 0);
+	CHECK_BREAK_INFO("RHW", "RH", LEASE2F2);
+	CHECK_VAL(lease_break_info.count, 1);
+
+	torture_reset_lease_break_info(tctx, &lease_break_info);
+
+	/* 1 opens file3 - lease break? */
+	torture_comment(tctx, "client1 opens fname3 via session 1\n");
+	smb2_lease_create(&io3, &ls3, false, fname3, LEASE1F3,
+			  smb2_util_lease_state("RHW"));
+	status = smb2_create(tree1, mem_ctx, &io3);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client1_file3 = io3.out.file.handle;
+	CHECK_CREATED(&io3, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io3, "RH", true, LEASE1F3, 0);
+	CHECK_BREAK_INFO("RHW", "RH", LEASE2F3);
+	CHECK_VAL(lease_break_info.count, 1);
+
+	/* cleanup everything */
+	torture_reset_lease_break_info(tctx, &lease_break_info);
+
+	smb2_util_close(tree1, h_client1_file1);
+	smb2_util_close(tree1, h_client1_file2);
+	smb2_util_close(tree1, h_client1_file3);
+	smb2_util_close(tree2A, h_client2_file1);
+	smb2_util_close(tree2A, h_client2_file2);
+	smb2_util_close(tree2A, h_client2_file3);
+
+	smb2_util_unlink(tree1, fname1);
+	smb2_util_unlink(tree1, fname2);
+	smb2_util_unlink(tree1, fname3);
+	CHECK_VAL(lease_break_info.count, 0);
+	test_multichannel_free_channels(tree2A, tree2B, tree2C);
+	tree2A = tree2B = tree2C = NULL;
+done:
+	tree1->session = session1;
+
+	smb2_util_close(tree1, h_client1_file1);
+	smb2_util_close(tree1, h_client1_file2);
+	smb2_util_close(tree1, h_client1_file3);
+	if (tree2A != NULL) {
+		smb2_util_close(tree2A, h_client2_file1);
+		smb2_util_close(tree2A, h_client2_file2);
+		smb2_util_close(tree2A, h_client2_file3);
+	}
+
+	if (h != NULL) {
+		smb2_util_close(tree1, *h);
+	}
+
+	smb2_util_unlink(tree1, fname1);
+	smb2_util_unlink(tree1, fname2);
+	smb2_util_unlink(tree1, fname3);
+	smb2_deltree(tree1, BASEDIR);
+
+	test_multichannel_free_channels(tree2A, tree2B, tree2C);
+	talloc_free(tree1);
+	talloc_free(mem_ctx);
+
+	return ret;
+}
+
 struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
 {
 	struct torture_suite *suite = torture_suite_create(ctx, "multichannel");
@@ -871,9 +1079,12 @@ struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
 								   "generic");
 	struct torture_suite *suite_oplocks = torture_suite_create(ctx,
 								   "oplocks");
+	struct torture_suite *suite_leases = torture_suite_create(ctx,
+								  "leases");
 
 	torture_suite_add_suite(suite, suite_generic);
 	torture_suite_add_suite(suite, suite_oplocks);
+	torture_suite_add_suite(suite, suite_leases);
 
 	torture_suite_add_1smb2_test(suite, "interface_info",
 				     test_multichannel_interface_info);
@@ -881,6 +1092,8 @@ struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
 				     test_multichannel_oplock_break_test1);
 	torture_suite_add_1smb2_test(suite_oplocks, "test2",
 				     test_multichannel_oplock_break_test2);
+	torture_suite_add_1smb2_test(suite_leases, "test1",
+				     test_multichannel_lease_break_test1);
 
 	suite->description = talloc_strdup(suite, "SMB2 Multichannel tests");
 
-- 
2.20.1


From 01dbb223a442134181349fdbef781e272c0f3dfa Mon Sep 17 00:00:00 2001
From: Sachin Prabhu <sprabhu at redhat.com>
Date: Mon, 11 Mar 2019 14:34:29 +0000
Subject: [PATCH 16/19] s4-torture: Add lease break retry tests - test2

Test to check if lease breaks are sent by the server as expected.

The test by default blocks channels by ignoring incoming lease break
requests on that channel. This does not work when testing against a
windows server.
Use --option=torture:use_iptables=true to use iptables to block ports
instead when testing against windows servers.

Signed-off-by: Guenther Deschner <gd at samba.org>
Signed-off-by: Sachin Prabhu <sprabhu at redhat.com>
---
 source4/torture/smb2/multichannel.c | 314 ++++++++++++++++++++++++++++
 1 file changed, 314 insertions(+)

diff --git a/source4/torture/smb2/multichannel.c b/source4/torture/smb2/multichannel.c
index e2af94756b3..90b5d5b357c 100644
--- a/source4/torture/smb2/multichannel.c
+++ b/source4/torture/smb2/multichannel.c
@@ -1072,6 +1072,318 @@ done:
 	return ret;
 }
 
+/*
+ * Lease Break Test 2:
+ * Test for lease break retries being sent by the server.
+ *      Connect 2A, 2B
+ *      open file1 in session 2A
+ *      open file2 in session 2B
+ *      block 2A
+ *      open file2 in session 1
+ *           lease break retry reaches the client?
+ *      Connect 2C
+ *      open file3 in session 2C
+ *      unblock 2A
+ *      open file1 in session 1
+ *           lease break reaches the client?
+ *      open file3 in session 1
+ *           lease break reached the client?
+ *      Cleanup
+ *           On deletion by 1, lease breaks sent for file1, file2 and file3
+ *           on 2B
+ *           This changes RH lease to R for Session 2.
+ *           (This has been disabled while we add support for sending lease
+ *            break for handle leases.)
+ */
+static bool test_multichannel_lease_break_test2(struct torture_context *tctx,
+						struct smb2_tree *tree1)
+{
+	const char *host = torture_setting_string(tctx, "host", NULL);
+	const char *share = torture_setting_string(tctx, "share", NULL);
+	struct cli_credentials *credentials = popt_get_cmdline_credentials();
+	NTSTATUS status;
+	TALLOC_CTX *mem_ctx = talloc_new(tctx);
+	struct smb2_handle _h;
+	struct smb2_handle *h = NULL;
+	struct smb2_handle h_client1_file1 = { 0 };
+	struct smb2_handle h_client1_file2 = { 0 };
+	struct smb2_handle h_client1_file3 = { 0 };
+	struct smb2_handle h_client2_file1 = { 0 };
+	struct smb2_handle h_client2_file2 = { 0 };
+	struct smb2_handle h_client2_file3 = { 0 };
+	struct smb2_create io1, io2, io3;
+	bool ret = true;
+	const char *fname1 = BASEDIR "\\lease_break_test1.dat";
+	const char *fname2 = BASEDIR "\\lease_break_test2.dat";
+	const char *fname3 = BASEDIR "\\lease_break_test3.dat";
+	struct smb2_tree *tree2A = NULL;
+	struct smb2_tree *tree2B = NULL;
+	struct smb2_tree *tree2C = NULL;
+	struct smb2_transport *transport1 = tree1->session->transport;
+	struct smb2_transport *transport2A = NULL;
+	struct smbcli_options transport2_options;
+	struct smb2_session *session1 = tree1->session;
+	uint16_t local_port = 0;
+	struct smb2_lease ls1;
+	struct smb2_lease ls2;
+	struct smb2_lease ls3;
+	bool block_ok = false;
+	bool unblock_ok = false;
+
+
+	if (!test_multichannel_initial_checks(tctx, tree1)) {
+		return true;
+	}
+
+	torture_comment(tctx, "Lease break retry: Test2\n");
+
+	torture_reset_lease_break_info(tctx, &lease_break_info);
+
+	transport1->lease.handler = torture_lease_handler;
+	transport1->lease.private_data = tree1;
+	torture_comment(tctx, "transport1  [%p]\n", transport1);
+	local_port = torture_get_local_port_from_transport(transport1);
+	torture_comment(tctx, "transport1 uses tcp port: %d\n", local_port);
+
+	status = torture_smb2_testdir(tree1, BASEDIR, &_h);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	smb2_util_close(tree1, _h);
+	smb2_util_unlink(tree1, fname1);
+	smb2_util_unlink(tree1, fname2);
+	smb2_util_unlink(tree1, fname3);
+	CHECK_VAL(lease_break_info.count, 0);
+
+	smb2_lease_create(&io1, &ls1, false, fname1, LEASE2F1,
+			  smb2_util_lease_state("RHW"));
+	test_multichannel_init_smb_create(&io1);
+
+	smb2_lease_create(&io2, &ls2, false, fname2, LEASE2F2,
+			  smb2_util_lease_state("RHW"));
+	test_multichannel_init_smb_create(&io2);
+
+	smb2_lease_create(&io3, &ls3, false, fname3, LEASE2F3,
+			  smb2_util_lease_state("RHW"));
+	test_multichannel_init_smb_create(&io3);
+
+	transport2_options = transport1->options;
+
+	ret = test_multichannel_create_channels(tctx, host, share,
+						  credentials,
+						  &transport2_options,
+						  &tree2A, &tree2B, NULL);
+	torture_assert(tctx, ret, "Could not create channels.\n");
+	transport2A = tree2A->session->transport;
+
+	/* 2a opens file1 */
+	torture_comment(tctx, "client2 opens fname1 via session 2A\n");
+	smb2_lease_create(&io1, &ls1, false, fname1, LEASE2F1,
+			  smb2_util_lease_state("RHW"));
+	status = smb2_create(tree2A, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client2_file1 = io1.out.file.handle;
+	CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io1, "RHW", true, LEASE2F1, 0);
+	CHECK_VAL(io1.out.durable_open_v2, false); //true);
+	CHECK_VAL(io1.out.timeout, io1.in.timeout);
+	CHECK_VAL(io1.out.durable_open, false);
+	CHECK_VAL(lease_break_info.count, 0);
+
+	/* 2b opens file2 */
+	torture_comment(tctx, "client2 opens fname2 via session 2B\n");
+	smb2_lease_create(&io2, &ls2, false, fname2, LEASE2F2,
+			  smb2_util_lease_state("RHW"));
+	status = smb2_create(tree2B, mem_ctx, &io2);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client2_file2 = io2.out.file.handle;
+	CHECK_CREATED(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io2, "RHW", true, LEASE2F2, 0);
+	CHECK_VAL(io2.out.durable_open_v2, false); //true);
+	CHECK_VAL(io2.out.timeout, io2.in.timeout);
+	CHECK_VAL(io2.out.durable_open, false);
+	CHECK_VAL(lease_break_info.count, 0);
+
+
+	torture_comment(tctx, "Blocking 2A\n");
+	/* Block 2A */
+	block_ok = test_block_channel(tctx, transport2A);
+	torture_assert(tctx, block_ok, "we could not block tcp transport");
+
+	/* 1 opens file2 */
+	torture_comment(tctx,
+			"Client opens fname2 with session1 with 2A blocked\n");
+	smb2_lease_create(&io2, &ls2, false, fname2, LEASE1F2,
+			  smb2_util_lease_state("RHW"));
+	status = smb2_create(tree1, mem_ctx, &io2);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client1_file2 = io2.out.file.handle;
+	CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io2, "RH", true, LEASE1F2, 0);
+	CHECK_VAL(io2.out.durable_open_v2, false);
+	CHECK_VAL(io2.out.timeout, 0);
+	CHECK_VAL(io2.out.durable_open, false);
+
+	if (lease_break_info.count == 0) {
+		torture_comment(tctx,
+				"Did not receive expected lease break!!\n");
+	} else {
+		torture_comment(tctx, "Received %d lease break(s)!!\n",
+				lease_break_info.count);
+	}
+
+	CHECK_VAL(lease_break_info.count, 1);
+	CHECK_BREAK_INFO("RHW", "RH", LEASE2F2);
+	torture_reset_lease_break_info(tctx, &lease_break_info);
+
+	/* Connect 2C */
+	torture_comment(tctx, "Connecting session 2C\n");
+	talloc_free(tree2C);
+	tree2C = test_multichannel_create_channel(tctx, host, share,
+				credentials, &transport2_options, tree2A);
+	if (!tree2C) {
+		goto done;
+	}
+
+	/* 2c opens file3 */
+	torture_comment(tctx, "client2 opens fname3 via session 2C\n");
+	smb2_lease_create(&io3, &ls3, false, fname3, LEASE2F3,
+			  smb2_util_lease_state("RHW"));
+	status = smb2_create(tree2C, mem_ctx, &io3);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client2_file3 = io3.out.file.handle;
+	CHECK_CREATED(&io3, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io3, "RHW", true, LEASE2F3, 0);
+	CHECK_VAL(io3.out.durable_open_v2, false);
+	CHECK_VAL(io3.out.timeout, io2.in.timeout);
+	CHECK_VAL(io3.out.durable_open, false);
+	CHECK_VAL(lease_break_info.count, 0);
+
+	/* Unblock 2A */
+	torture_comment(tctx, "Unblocking 2A\n");
+	unblock_ok = test_unblock_channel(tctx, transport2A);
+	torture_assert(tctx, unblock_ok, "we could not unblock tcp transport");
+
+	/* 1 opens file1 */
+	torture_comment(tctx, "Client opens fname1 with session 1\n");
+	smb2_lease_create(&io1, &ls1, false, fname1, LEASE1F1,
+			  smb2_util_lease_state("RHW"));
+	status = smb2_create(tree1, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client1_file1 = io1.out.file.handle;
+	CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io1, "RH", true, LEASE1F1, 0);
+
+	if (lease_break_info.count == 0) {
+		torture_comment(tctx,
+				"Did not receive expected lease break!!\n");
+	} else {
+		torture_comment(tctx,
+				"Received %d lease break(s)!!\n",
+				lease_break_info.count);
+	}
+	CHECK_VAL(lease_break_info.count, 1);
+	CHECK_BREAK_INFO("RHW", "RH", LEASE2F1);
+	torture_reset_lease_break_info(tctx, &lease_break_info);
+
+	/*1 opens file3 */
+	torture_comment(tctx, "client opens fname3 via session 1\n");
+
+	smb2_lease_create(&io3, &ls3, false, fname3, LEASE1F3,
+			  smb2_util_lease_state("RHW"));
+	status = smb2_create(tree1, mem_ctx, &io3);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client1_file3 = io3.out.file.handle;
+	CHECK_CREATED(&io3, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io3, "RH", true, LEASE1F3, 0);
+
+	if (lease_break_info.count == 0) {
+		torture_comment(tctx,
+				"Did not receive expected lease break!!\n");
+	} else {
+		torture_comment(tctx,
+				"Received %d lease break(s)!!\n",
+				lease_break_info.count);
+	}
+	CHECK_VAL(lease_break_info.count, 1);
+	CHECK_BREAK_INFO("RHW", "RH", LEASE2F3);
+	torture_reset_lease_break_info(tctx, &lease_break_info);
+
+	smb2_util_close(tree1, h_client1_file1);
+	smb2_util_close(tree1, h_client1_file2);
+	smb2_util_close(tree1, h_client1_file3);
+
+	/*
+	 * Session 2 still has RW lease on file 1. Deletion of this file by 1
+	 *  leads to a lease break call to session 2 file1
+	 */
+	smb2_util_unlink(tree1, fname1);
+	/*
+	 * Bug - Samba does not revoke Handle lease on unlink
+	 * CHECK_BREAK_INFO("RH", "R", LEASE2F1);
+	 */
+	torture_reset_lease_break_info(tctx, &lease_break_info);
+
+	/*
+	 * Session 2 still has RW lease on file 2. Deletion of this file by 1
+	 *  leads to a lease break call to session 2 file2
+	 */
+	smb2_util_unlink(tree1, fname2);
+	/*
+	 * Bug - Samba does not revoke Handle lease on unlink
+	 * CHECK_BREAK_INFO("RH", "R", LEASE2F2);
+	 */
+	torture_reset_lease_break_info(tctx, &lease_break_info);
+
+	/*
+	 * Session 2 still has RW lease on file 3. Deletion of this file by 1
+	 *  leads to a lease break call to session 2 file3
+	 */
+	smb2_util_unlink(tree1, fname3);
+	/*
+	 * Bug - Samba does not revoke Handle lease on unlink
+	 * CHECK_BREAK_INFO("RH", "R", LEASE2F3);
+	 */
+	torture_reset_lease_break_info(tctx, &lease_break_info);
+
+	smb2_util_close(tree2C, h_client2_file1);
+	smb2_util_close(tree2C, h_client2_file2);
+	smb2_util_close(tree2C, h_client2_file3);
+
+	test_multichannel_free_channels(tree2A, tree2B, tree2C);
+	tree2A = tree2B = tree2C = NULL;
+
+done:
+	if (block_ok && !unblock_ok) {
+		test_unblock_channel(tctx, transport2A);
+	}
+	test_cleanup_blocked_channels(tctx);
+
+	tree1->session = session1;
+
+	smb2_util_close(tree1, h_client1_file1);
+	smb2_util_close(tree1, h_client1_file2);
+	smb2_util_close(tree1, h_client1_file3);
+	if (tree2A != NULL) {
+		smb2_util_close(tree2A, h_client2_file1);
+		smb2_util_close(tree2A, h_client2_file2);
+		smb2_util_close(tree2A, h_client2_file3);
+	}
+
+	if (h != NULL) {
+		smb2_util_close(tree1, *h);
+	}
+
+	smb2_util_unlink(tree1, fname1);
+	smb2_util_unlink(tree1, fname2);
+	smb2_util_unlink(tree1, fname3);
+	smb2_deltree(tree1, BASEDIR);
+
+	test_multichannel_free_channels(tree2A, tree2B, tree2C);
+	talloc_free(tree1);
+	talloc_free(mem_ctx);
+
+	return ret;
+}
+
 struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
 {
 	struct torture_suite *suite = torture_suite_create(ctx, "multichannel");
@@ -1094,6 +1406,8 @@ struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
 				     test_multichannel_oplock_break_test2);
 	torture_suite_add_1smb2_test(suite_leases, "test1",
 				     test_multichannel_lease_break_test1);
+	torture_suite_add_1smb2_test(suite_leases, "test2",
+				     test_multichannel_lease_break_test2);
 
 	suite->description = talloc_strdup(suite, "SMB2 Multichannel tests");
 
-- 
2.20.1


From aa0145dc8c9976abfe9fd48654e2d87e8ad12b37 Mon Sep 17 00:00:00 2001
From: Sachin Prabhu <sprabhu at redhat.com>
Date: Mon, 11 Mar 2019 14:38:13 +0000
Subject: [PATCH 17/19] s4-torture: Add lease break retry tests - test3

Check to see how the server behaves if lease break response is sent
over a different channel to one over which the break is received.

The test by default blocks channels by ignoring incoming lease break
requests on that channel. This does not work when testing against a
windows server.
Use --option=torture:use_iptables=true to use iptables to block ports
instead when testing against windows servers.

Signed-off-by: Guenther Deschner <gd at samba.org>
Signed-off-by: Sachin Prabhu <sprabhu at redhat.com>
---
 source4/torture/smb2/multichannel.c | 150 ++++++++++++++++++++++++++++
 1 file changed, 150 insertions(+)

diff --git a/source4/torture/smb2/multichannel.c b/source4/torture/smb2/multichannel.c
index 90b5d5b357c..2a007664934 100644
--- a/source4/torture/smb2/multichannel.c
+++ b/source4/torture/smb2/multichannel.c
@@ -442,6 +442,16 @@ static void test_cleanup_blocked_channels(struct torture_context *tctx)
 	}
 }
 
+/* Timer handler function notifies the registering function that time is up */
+static void timeout_cb(struct tevent_context *ev,
+		       struct tevent_timer *te,
+		       struct timeval current_time,
+		       void *private_data)
+{
+	bool *timesup = (bool *)private_data;
+	*timesup = true;
+}
+
 /*
  * Oplock break - Test 1
  * Test to confirm that server sends oplock breaks as expected.
@@ -1384,6 +1394,144 @@ done:
 	return ret;
 }
 
+/*
+ * Test 3: Check to see how the server behaves if lease break
+ *      response is sent over a different channel to one over which
+ *      the break is received.
+ *      Connect 2A, 2B
+ *      open file1 in session 2A
+ *      open file1 in session 1
+ *           Lease break sent to 2A
+ *           2B sends back lease break reply.
+ *      session 1 allowed to open file
+ */
+static bool test_multichannel_lease_break_test3(struct torture_context *tctx,
+						struct smb2_tree *tree1)
+{
+	const char *host = torture_setting_string(tctx, "host", NULL);
+	const char *share = torture_setting_string(tctx, "share", NULL);
+	struct cli_credentials *credentials = popt_get_cmdline_credentials();
+	NTSTATUS status;
+	TALLOC_CTX *mem_ctx = talloc_new(tctx);
+	struct smb2_handle _h;
+	struct smb2_handle *h = NULL;
+	struct smb2_handle h_client1_file1 = { 0 };
+	struct smb2_handle h_client2_file1 = { 0 };
+	struct smb2_create io1;
+	bool ret = true;
+	const char *fname1 = BASEDIR "\\lease_break_test1.dat";
+	struct smb2_tree *tree2A = NULL;
+	struct smb2_tree *tree2B = NULL;
+	struct smb2_transport *transport1 = tree1->session->transport;
+	struct smb2_transport *transport2A = NULL;
+	struct smbcli_options transport2_options;
+	uint16_t local_port = 0;
+	struct smb2_lease ls1;
+	struct tevent_timer *te = NULL;
+	struct timeval ne;
+	bool timesup = false;
+	TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+
+	if (!test_multichannel_initial_checks(tctx, tree1)) {
+		return true;
+	}
+
+	torture_comment(tctx, "Lease break retry: Test3\n");
+
+	torture_reset_lease_break_info(tctx, &lease_break_info);
+
+	transport1->lease.handler = torture_lease_handler;
+	transport1->lease.private_data = tree1;
+	torture_comment(tctx, "transport1  [%p]\n", transport1);
+	local_port = torture_get_local_port_from_transport(transport1);
+	torture_comment(tctx, "transport1 uses tcp port: %d\n", local_port);
+
+	status = torture_smb2_testdir(tree1, BASEDIR, &_h);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	smb2_util_close(tree1, _h);
+	smb2_util_unlink(tree1, fname1);
+	CHECK_VAL(lease_break_info.count, 0);
+
+	smb2_lease_create(&io1, &ls1, false, fname1, LEASE2F1,
+			  smb2_util_lease_state("RHW"));
+	test_multichannel_init_smb_create(&io1);
+
+	transport2_options = transport1->options;
+
+	ret = test_multichannel_create_channels(tctx, host, share,
+						credentials,
+						&transport2_options,
+						&tree2A, &tree2B, NULL);
+	torture_assert(tctx, ret, "Could not create channels.\n");
+	transport2A = tree2A->session->transport;
+	transport2A->lease.private_data = tree2B;
+
+	/* 2a opens file1 */
+	torture_comment(tctx, "client2 opens fname1 via session 2A\n");
+	smb2_lease_create(&io1, &ls1, false, fname1, LEASE2F1,
+			  smb2_util_lease_state("RHW"));
+	status = smb2_create(tree2A, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client2_file1 = io1.out.file.handle;
+	CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io1, "RHW", true, LEASE2F1, 0);
+	CHECK_VAL(io1.out.durable_open_v2, false); //true);
+	CHECK_VAL(io1.out.timeout, io1.in.timeout);
+	CHECK_VAL(io1.out.durable_open, false);
+	CHECK_VAL(lease_break_info.count, 0);
+
+	/* Set a timeout for 5 seconds for session 1 to open file1 */
+	ne = tevent_timeval_current_ofs(0, 5000000);
+	te = tevent_add_timer(tctx->ev, tmp_ctx, ne, timeout_cb, &timesup);
+	if (te == NULL) {
+		torture_comment(tctx, "Failed to add timer.");
+		goto done;
+	}
+
+	/* 1 opens file2 */
+	torture_comment(tctx, "Client opens fname1 with session 1\n");
+	smb2_lease_create(&io1, &ls1, false, fname1, LEASE1F1,
+			  smb2_util_lease_state("RHW"));
+	status = smb2_create(tree1, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client1_file1 = io1.out.file.handle;
+	CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io1, "RH", true, LEASE1F1, 0);
+	CHECK_VAL(io1.out.durable_open_v2, false);
+	CHECK_VAL(io1.out.timeout, 0);
+	CHECK_VAL(io1.out.durable_open, false);
+
+	CHECK_VAL(lease_break_info.count, 1);
+	CHECK_BREAK_INFO("RHW", "RH", LEASE2F1);
+
+	/*
+	 * Check if timeout handler was fired. This would indicate
+	 * that the server didn't receive a reply for the oplock break
+	 * from the client and the server let session 1 open the file
+	 * only after the oplock break timeout.
+	 */
+	CHECK_VAL(timesup, false);
+
+done:
+	smb2_util_close(tree1, h_client1_file1);
+	if (tree2A != NULL) {
+		smb2_util_close(tree2A, h_client2_file1);
+	}
+
+	if (h != NULL) {
+		smb2_util_close(tree1, *h);
+	}
+
+	smb2_util_unlink(tree1, fname1);
+	smb2_deltree(tree1, BASEDIR);
+
+	test_multichannel_free_channels(tree2A, tree2B, NULL);
+	talloc_free(tree1);
+	talloc_free(mem_ctx);
+
+	return ret;
+}
+
 struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
 {
 	struct torture_suite *suite = torture_suite_create(ctx, "multichannel");
@@ -1408,6 +1556,8 @@ struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
 				     test_multichannel_lease_break_test1);
 	torture_suite_add_1smb2_test(suite_leases, "test2",
 				     test_multichannel_lease_break_test2);
+	torture_suite_add_1smb2_test(suite_leases, "test3",
+				     test_multichannel_lease_break_test3);
 
 	suite->description = talloc_strdup(suite, "SMB2 Multichannel tests");
 
-- 
2.20.1


From 32ad658fd574af3522df9312287073357f1fef23 Mon Sep 17 00:00:00 2001
From: Sachin Prabhu <sprabhu at redhat.com>
Date: Mon, 11 Mar 2019 14:47:58 +0000
Subject: [PATCH 18/19] s4-torture: Add lease break retry tests - test4

Test to see how the server behaves when the client flushes data back to
the server but doesn't send the lease break response over the channel.
Does it then retry the lease break?

This test is specifically expected to run against Samba and will not
work against a MS Windows servers because it uses the ignore method to
ignore oplock breaks sent by the server.

Signed-off-by: Guenther Deschner <gd at samba.org>
Signed-off-by: Sachin Prabhu <sprabhu at redhat.com>
---
 source4/torture/smb2/multichannel.c | 190 ++++++++++++++++++++++++++++
 1 file changed, 190 insertions(+)

diff --git a/source4/torture/smb2/multichannel.c b/source4/torture/smb2/multichannel.c
index 2a007664934..7f25f9d3a20 100644
--- a/source4/torture/smb2/multichannel.c
+++ b/source4/torture/smb2/multichannel.c
@@ -1532,6 +1532,194 @@ done:
 	return ret;
 }
 
+/* lease handler for test4 */
+static bool test4_lease_break_handler(struct smb2_transport *transport,
+			   const struct smb2_lease_break *lb,
+			   void *private_data)
+{
+	NTSTATUS status;
+	bool ret = true;
+	struct smb2_tree *test4_tree2 = private_data;
+	struct torture_context *tctx = lease_break_info.tctx;
+	struct smb2_handle test4_h_file = lease_break_info.oplock_handle;
+	DATA_BLOB blob;
+
+	lease_break_info.lease_transport = transport;
+	lease_break_info.lease_break = *lb;
+	lease_break_info.count++;
+
+	torture_comment(tctx, "Test 6 Lease break handler called.\n");
+	torture_comment(tctx, "Trying write to file using given session.\n");
+
+	blob = data_blob_string_const("Here I am");
+	status = smb2_util_write(test4_tree2,
+				 test4_h_file,
+				 blob.data,
+				 0,
+				 blob.length);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+		"failed to write file");
+
+done:
+	return ret;
+}
+
+/*
+ * Lease Break Test 4:
+ * Test to see how the server behaves when the client flushes data back to the
+ * server but doesn't send the lease break response over the channel. Does it
+ * then retry the lease break?
+ *      Connect 2A, 2B
+ *      open file1 in session 2A
+ *      set lease handler for 2A to ignore break requests
+ *      open file1 in session 1
+ *           Lease break sent to 2A
+ *           Write to file in 2A
+ *           Do not send ack to lease break
+ *      Check to see if second lease break sent
+ *      Cleanup
+ */
+static bool test_multichannel_lease_break_test4(struct torture_context *tctx,
+						struct smb2_tree *tree1)
+{
+	const char *host = torture_setting_string(tctx, "host", NULL);
+	const char *share = torture_setting_string(tctx, "share", NULL);
+	struct cli_credentials *credentials = popt_get_cmdline_credentials();
+	NTSTATUS status;
+	TALLOC_CTX *mem_ctx = talloc_new(tctx);
+	struct smb2_handle _h;
+	struct smb2_handle *h = NULL;
+	struct smb2_handle h_client1_file1 = { 0 };
+	struct smb2_handle h_client2_file1 = { 0 };
+	struct smb2_create io1, io2, io3;
+	bool ret = true;
+	const char *fname1 = BASEDIR "\\lease_break_test1.dat";
+	const char *fname2 = BASEDIR "\\lease_break_test2.dat";
+	const char *fname3 = BASEDIR "\\lease_break_test3.dat";
+	struct smb2_tree *tree2A = NULL;
+	struct smb2_tree *tree2B = NULL;
+	struct smb2_tree *tree2C = NULL;
+	struct smb2_transport *transport1 = tree1->session->transport;
+	struct smb2_transport *transport2A = NULL;
+	struct smbcli_options transport2_options;
+	struct smb2_session *session1 = tree1->session;
+	uint16_t local_port = 0;
+	struct smb2_lease ls1;
+	struct smb2_lease ls2;
+	struct smb2_lease ls3;
+
+	if (!test_multichannel_initial_checks(tctx, tree1)) {
+		return true;
+	}
+
+	torture_comment(tctx, "Lease break retry: Test4\n");
+	torture_comment(tctx, "This test is specifically expected to run "
+			"against samba and will not work against windows "
+			"servers. The windows server assumes that if the "
+			"send() command returns successfully, the lease break "
+			"has been delivered. In this test, we rely on the "
+			"Samba behaviour of waiting for a reply for the lease "
+			"break from the server instead.\n");
+
+	torture_reset_lease_break_info(tctx, &lease_break_info);
+
+	transport1->lease.handler = torture_lease_handler;
+	transport1->lease.private_data = tree1;
+	torture_comment(tctx, "transport1  [%p]\n", transport1);
+	local_port = torture_get_local_port_from_transport(transport1);
+	torture_comment(tctx, "transport1 uses tcp port: %d\n", local_port);
+
+	status = torture_smb2_testdir(tree1, BASEDIR, &_h);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	smb2_util_close(tree1, _h);
+	smb2_util_unlink(tree1, fname1);
+	smb2_util_unlink(tree1, fname2);
+	smb2_util_unlink(tree1, fname3);
+	CHECK_VAL(lease_break_info.count, 0);
+
+	smb2_lease_create(&io1, &ls1, false, fname1, LEASE2F1,
+			  smb2_util_lease_state("RHW"));
+	test_multichannel_init_smb_create(&io1);
+
+	smb2_lease_create(&io2, &ls2, false, fname2, LEASE2F2,
+			  smb2_util_lease_state("RHW"));
+	test_multichannel_init_smb_create(&io2);
+
+	smb2_lease_create(&io3, &ls3, false, fname3, LEASE2F3,
+			  smb2_util_lease_state("RHW"));
+	test_multichannel_init_smb_create(&io3);
+
+	transport2_options = transport1->options;
+
+	ret = test_multichannel_create_channels(tctx, host, share,
+						  credentials,
+						  &transport2_options,
+						  &tree2A, &tree2B, NULL);
+	torture_assert(tctx, ret, "Could not create channels.\n");
+	transport2A = tree2A->session->transport;
+
+	torture_comment(tctx, "client2 opens fname1 via session 2A\n");
+	smb2_lease_create(&io1, &ls1, false, fname1, LEASE2F1,
+			  smb2_util_lease_state("RHW"));
+	status = smb2_create(tree2A, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client2_file1 = io1.out.file.handle;
+	CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
+	CHECK_LEASE(&io1, "RHW", true, LEASE2F1, 0);
+	CHECK_VAL(io1.out.durable_open_v2, false); //true);
+	CHECK_VAL(io1.out.timeout, io1.in.timeout);
+	CHECK_VAL(io1.out.durable_open, false);
+	CHECK_VAL(lease_break_info.count, 0);
+
+	torture_comment(tctx, "Blocking 2A\n");
+	/* Set our lease handler for 2A */
+	lease_break_info.oplock_handle = h_client2_file1;
+	transport2A->lease.handler = test4_lease_break_handler;
+
+	torture_comment(tctx,
+			"Client opens fname1 with session 1 with 2A blocked\n");
+	smb2_lease_create(&io1, &ls1, false, fname1, LEASE1F1,
+			  smb2_util_lease_state("RHW"));
+	status = smb2_create(tree1, mem_ctx, &io1);
+	CHECK_STATUS(status, NT_STATUS_OK);
+	h_client1_file1 = io1.out.file.handle;
+	CHECK_LEASE(&io1, "RH", true, LEASE1F1, 0);
+
+	if (lease_break_info.count == 0) {
+		torture_comment(tctx,
+				"Did not receive expected lease break!!\n");
+	} else {
+		torture_comment(tctx,
+				"Received %d lease break(s)!!\n",
+				lease_break_info.count);
+	}
+
+	/* Should receive 2 lease breaks */
+	CHECK_VAL(lease_break_info.count, 2);
+	torture_reset_lease_break_info(tctx, &lease_break_info);
+
+done:
+	tree1->session = session1;
+
+	smb2_util_close(tree1, h_client1_file1);
+	if (tree2A != NULL) {
+		smb2_util_close(tree2A, h_client2_file1);
+	}
+
+	if (h != NULL) {
+		smb2_util_close(tree1, *h);
+	}
+
+	smb2_util_unlink(tree1, fname1);
+	smb2_deltree(tree1, BASEDIR);
+
+	test_multichannel_free_channels(tree2A, tree2B, tree2C);
+	talloc_free(tree1);
+	talloc_free(mem_ctx);
+
+	return ret;
+}
+
 struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
 {
 	struct torture_suite *suite = torture_suite_create(ctx, "multichannel");
@@ -1558,6 +1746,8 @@ struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
 				     test_multichannel_lease_break_test2);
 	torture_suite_add_1smb2_test(suite_leases, "test3",
 				     test_multichannel_lease_break_test3);
+	torture_suite_add_1smb2_test(suite_leases, "test4",
+				     test_multichannel_lease_break_test4);
 
 	suite->description = talloc_strdup(suite, "SMB2 Multichannel tests");
 
-- 
2.20.1


From cb8f7c7a9c3eda32e2f1ada0d2affeae86ecd13a Mon Sep 17 00:00:00 2001
From: Sachin Prabhu <sprabhu at redhat.com>
Date: Mon, 11 Mar 2019 20:07:09 +0000
Subject: [PATCH 19/19] s4-torture: add test to check for max. number of
 channels per session.

Guenther

Signed-off-by: Guenther Deschner <gd at samba.org>
---
 source4/torture/smb2/multichannel.c | 115 ++++++++++++++++++++++++++++
 1 file changed, 115 insertions(+)

diff --git a/source4/torture/smb2/multichannel.c b/source4/torture/smb2/multichannel.c
index 7f25f9d3a20..4536e1adcbc 100644
--- a/source4/torture/smb2/multichannel.c
+++ b/source4/torture/smb2/multichannel.c
@@ -1720,6 +1720,119 @@ done:
 	return ret;
 }
 
+/*
+ * Test limits of channels
+ */
+static bool test_multichannel_num_channels(struct torture_context *tctx,
+					   struct smb2_tree *tree1)
+{
+	const char *host = torture_setting_string(tctx, "host", NULL);
+	const char *share = torture_setting_string(tctx, "share", NULL);
+	struct cli_credentials *credentials = popt_get_cmdline_credentials();
+	TALLOC_CTX *mem_ctx = talloc_new(tctx);
+	bool ret = true;
+	struct smb2_tree **tree2 = NULL;
+	struct smb2_transport *transport1 = tree1->session->transport;
+	struct smb2_transport **transport2 = NULL;
+	struct smbcli_options transport2_options;
+	struct smb2_session **session2 = NULL;
+	uint32_t server_capabilities;
+	int i;
+	int max_channels = 33; /* 32 is the W2K12R2 and W2K16 limit */
+
+	if (smbXcli_conn_protocol(transport1->conn) < PROTOCOL_SMB3_00) {
+		torture_fail(tctx,
+			     "SMB 3.X Dialect family required for Multichannel"
+			     " tests\n");
+	}
+
+	server_capabilities = smb2cli_conn_server_capabilities(
+					tree1->session->transport->conn);
+	if (!(server_capabilities & SMB2_CAP_MULTI_CHANNEL)) {
+		torture_fail(tctx,
+			     "Server does not support multichannel.");
+	}
+
+	torture_comment(tctx, "Testing max. number of channels\n");
+
+	transport2_options = transport1->options;
+	transport2_options.client_guid = GUID_random();
+
+	tree2		= talloc_zero_array(mem_ctx, struct smb2_tree *,
+					    max_channels);
+	transport2	= talloc_zero_array(mem_ctx, struct smb2_transport *,
+					    max_channels);
+	session2	= talloc_zero_array(mem_ctx, struct smb2_session *,
+					    max_channels);
+	if (tree2 == NULL || transport2 == NULL || session2 == NULL) {
+		torture_fail(tctx, "out of memory");
+	}
+
+	for (i = 0; i < max_channels; i++) {
+
+		NTSTATUS expected_status;
+
+		torture_assert_ntstatus_ok_goto(tctx,
+			smb2_connect(tctx,
+				host,
+				lpcfg_smb_ports(tctx->lp_ctx),
+				share,
+				lpcfg_resolve_context(tctx->lp_ctx),
+				credentials,
+				&tree2[i],
+				tctx->ev,
+				&transport2_options,
+				lpcfg_socket_options(tctx->lp_ctx),
+				lpcfg_gensec_settings(tctx, tctx->lp_ctx)
+				),
+			ret, done, "smb2_connect failed");
+
+		transport2[i] = tree2[i]->session->transport;
+
+		if (i == 0) {
+			/* done for the 1st channel */
+			continue;
+		}
+
+		/*
+		 * Now bind the session2[i] to the transport2
+		 */
+		session2[i] = smb2_session_channel(transport2[i],
+						   lpcfg_gensec_settings(tctx,
+								 tctx->lp_ctx),
+						   tree2[0],
+						   tree2[0]->session);
+
+		torture_assert(tctx, session2[i] != NULL,
+			       "smb2_session_channel failed");
+
+		torture_comment(tctx, "established transport2 [#%d]\n", i);
+
+		if (i >= 32) {
+			expected_status = NT_STATUS_INSUFFICIENT_RESOURCES;
+		} else {
+			expected_status = NT_STATUS_OK;
+		}
+
+		torture_assert_ntstatus_equal_goto(tctx,
+			smb2_session_setup_spnego(session2[i],
+				popt_get_cmdline_credentials(),
+				0 /* previous_session_id */),
+			expected_status,
+			ret, done,
+			talloc_asprintf(tctx, "failed to establish session "
+					      "setup for channel #%d", i));
+
+		torture_comment(tctx, "bound session2 [#%d] to session2 [0]\n",
+				i);
+	}
+
+ done:
+	talloc_free(mem_ctx);
+
+	return ret;
+}
+
 struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
 {
 	struct torture_suite *suite = torture_suite_create(ctx, "multichannel");
@@ -1736,6 +1849,8 @@ struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
 
 	torture_suite_add_1smb2_test(suite, "interface_info",
 				     test_multichannel_interface_info);
+	torture_suite_add_1smb2_test(suite_generic, "num_channels",
+				     test_multichannel_num_channels);
 	torture_suite_add_1smb2_test(suite_oplocks, "test1",
 				     test_multichannel_oplock_break_test1);
 	torture_suite_add_1smb2_test(suite_oplocks, "test2",
-- 
2.20.1



More information about the samba-technical mailing list