Add multichannel tests to smbtorture

Jeremy Allison jra at samba.org
Wed Apr 17 18:23:42 UTC 2019


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.

Cheersm

	Jeremy.
-------------- next part --------------
From f77f362dd1915292c7d2a0cc5a1a9801fc91c425 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

Signed-off-by: Günther Deschner <gd at samba.org>
Reviewed-by: Jeremy Allison <jra 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.21.0.392.gf8f6787159e-goog


From c02fa0ddb112380ea143cee302245adf3b6cfa16 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>
Reviewed-by: Jeremy Allison <jra 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.21.0.392.gf8f6787159e-goog


From bf654e9e735a6bcbffbedf7c108ef1053b3ece59 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.

Signed-off-by: Guenther Deschner <gd at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
 source4/torture/smb2/oplock_break_handler.c | 91 +++++++++++++++++++++
 source4/torture/smb2/oplock_break_handler.h | 50 +++++++++++
 source4/torture/smb2/replay.c               | 78 +-----------------
 source4/torture/smb2/wscript_build          |  1 +
 4 files changed, 143 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..44421a4d3ba
--- /dev/null
+++ b/source4/torture/smb2/oplock_break_handler.h
@@ -0,0 +1,50 @@
+/*
+ * 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 _TORTURE_SMB2_OPLOCK_BREAK_HANDLER_H_
+#define _TORTURE_SMB2_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 /* _TORTURE_SMB2_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.21.0.392.gf8f6787159e-goog


From 8e9c6418da906ccbdc8b6b8940d827ea3ea83976 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.

Signed-off-by: Guenther Deschner <gd at samba.org>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
 source4/torture/smb2/oplock.c               | 54 --------------------
 source4/torture/smb2/oplock_break_handler.c | 53 ++++++++++++++++++++
 source4/torture/smb2/oplock_break_handler.h |  1 +
 source4/torture/smb2/replay.c               | 55 ---------------------
 4 files changed, 54 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..1b207810658 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,55 @@ 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 44421a4d3ba..a106fd54ec1 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.21.0.392.gf8f6787159e-goog


From 67f4ea8a5bb625b7bfaec5c826f1ed9f9adfbcfe 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
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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: Günther Deschner <gd at samba.org>
Reviewed-by: Jeremy Allison <jra 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.21.0.392.gf8f6787159e-goog


From db6c27b3adbe8ee00504471d2460f2e9a6a570e2 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
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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: Günther Deschner <gd at samba.org>
Reviewed-by: Jeremy Allison <jra 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 1b207810658..de46eb32f2a 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 a106fd54ec1..e53e4d672d8 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.21.0.392.gf8f6787159e-goog


From 4329f2673d471259eb05b456340d80cbc7a39dc3 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
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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: Günther Deschner <gd at samba.org>
Reviewed-by: Jeremy Allison <jra 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 de46eb32f2a..d64b0bed6db 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.21.0.392.gf8f6787159e-goog


From 46a2dfd885aef8aa3bb6baff1244b6f918ea7e55 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.

Signed-off-by: Guenther Deschner <gd at samba.org>
Reviewed-by: Jeremy Allison <jra 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..b15a7ed1998 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_response_size = 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.21.0.392.gf8f6787159e-goog


From 72b866be9acf845c3274d843d95e73480bad62dc 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

Signed-off-by: Guenther Deschner <gd at samba.org>
Signed-off-by: Sachin Prabhu <sprabhu at redhat.com>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
 source4/torture/smb2/block.c       | 372 +++++++++++++++++++++++++++++
 source4/torture/smb2/block.h       |  45 ++++
 source4/torture/smb2/wscript_build |   1 +
 3 files changed, 418 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..0cc619c0006
--- /dev/null
+++ b/source4/torture/smb2/block.c
@@ -0,0 +1,372 @@
+/*
+ * 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.21.0.392.gf8f6787159e-goog


From a3f48938ef6519671478a6b9527887ef89731496 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>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
 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 b15a7ed1998..3692ffb695b 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.21.0.392.gf8f6787159e-goog


From 37357870e91abf4a4f1d4694a9606a0457417d71 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>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
 source4/torture/smb2/multichannel.c | 145 +++++++++++++++++++++++++++-
 1 file changed, 144 insertions(+), 1 deletion(-)

diff --git a/source4/torture/smb2/multichannel.c b/source4/torture/smb2/multichannel.c
index 3692ffb695b..ab022249eb7 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,148 @@ 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);
+
+	return tree;
+done:
+	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.21.0.392.gf8f6787159e-goog


From f3bbddd4a53aaf800aeb1d946703aa7a3e163714 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>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
 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 ab022249eb7..d1c1049ce00 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"
 
@@ -299,6 +301,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.21.0.392.gf8f6787159e-goog


From e15bf2f29a467b42f3fbf1d1bd97d2975a37aba2 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>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
 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 d1c1049ce00..dd72e7fb9a8 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"
 
@@ -301,6 +304,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.
@@ -396,12 +438,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.21.0.392.gf8f6787159e-goog


From 487839ce85c8743830742c46ad8649ddd009a002 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>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
 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 dd72e7fb9a8..0a926e5b4c7 100644
--- a/source4/torture/smb2/multichannel.c
+++ b/source4/torture/smb2/multichannel.c
@@ -607,6 +607,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");
@@ -622,6 +876,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.21.0.392.gf8f6787159e-goog


From f4e43638307a5f737d5ec04665cbf64ff5e1ebf2 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>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
 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 0a926e5b4c7..12aecd2e50b 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"
@@ -861,6 +862,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");
@@ -868,9 +1076,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);
@@ -878,6 +1089,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.21.0.392.gf8f6787159e-goog


From a713b38ca11e7c0081553b988dc6f9ad4a8a16ba 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>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
 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 12aecd2e50b..42db38bbc69 100644
--- a/source4/torture/smb2/multichannel.c
+++ b/source4/torture/smb2/multichannel.c
@@ -1069,6 +1069,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");
@@ -1091,6 +1403,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.21.0.392.gf8f6787159e-goog


From ef8bf61299f51791a4778a72a4cbbeb56fdc9b39 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>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
 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 42db38bbc69..f54d8a39e0d 100644
--- a/source4/torture/smb2/multichannel.c
+++ b/source4/torture/smb2/multichannel.c
@@ -439,6 +439,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.
@@ -1381,6 +1391,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");
@@ -1405,6 +1553,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.21.0.392.gf8f6787159e-goog


From cd347cef93da9416abf520989258fb9a2a948b2a 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>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
 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 f54d8a39e0d..cee862b2363 100644
--- a/source4/torture/smb2/multichannel.c
+++ b/source4/torture/smb2/multichannel.c
@@ -1529,6 +1529,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");
@@ -1555,6 +1743,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.21.0.392.gf8f6787159e-goog


From ac8a34d128f1c05218f148d0100d70b24ea86739 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.

Signed-off-by: Guenther Deschner <gd at samba.org>
Reviewed-by: Jeremy Allison <jra 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 cee862b2363..3b861b21ab2 100644
--- a/source4/torture/smb2/multichannel.c
+++ b/source4/torture/smb2/multichannel.c
@@ -1717,6 +1717,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");
@@ -1733,6 +1846,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.21.0.392.gf8f6787159e-goog



More information about the samba-technical mailing list