ctdb-scripts: Support storing statd-callout state in cluster filesystem
[samba4-gss.git] / source4 / torture / smb2 / multichannel.c
blob6b6e00e7c1dec7db81dc790131774de68a0d3056
1 /*
2 * Unix SMB/CIFS implementation.
4 * test SMB2 multichannel operations
6 * Copyright (C) Guenther Deschner, 2016
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "includes.h"
23 #include "libcli/smb2/smb2.h"
24 #include "libcli/smb2/smb2_calls.h"
25 #include "torture/torture.h"
26 #include "torture/smb2/proto.h"
27 #include "libcli/security/security.h"
28 #include "librpc/gen_ndr/ndr_security.h"
29 #include "librpc/gen_ndr/ndr_ioctl.h"
30 #include "../libcli/smb/smbXcli_base.h"
31 #include "lib/cmdline/cmdline.h"
32 #include "libcli/security/security.h"
33 #include "libcli/resolve/resolve.h"
34 #include "lib/socket/socket.h"
35 #include "lib/param/param.h"
36 #include "lib/events/events.h"
37 #include "oplock_break_handler.h"
38 #include "lease_break_handler.h"
39 #include "torture/smb2/block.h"
41 #define BASEDIR "multichanneltestdir"
43 #define CHECK_STATUS(status, correct) \
44 torture_assert_ntstatus_equal_goto(tctx, status, correct,\
45 ret, done, "")
47 #define CHECK_VAL(v, correct) do { \
48 if ((v) != (correct)) { \
49 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s" \
50 " got 0x%x - should be 0x%x\n", \
51 __location__, #v, (int)v, (int)correct); \
52 ret = false; \
53 goto done; \
54 } } while (0)
56 #define CHECK_VAL_GREATER_THAN(v, gt_val) do { \
57 if ((v) <= (gt_val)) { \
58 torture_result(tctx, TORTURE_FAIL, \
59 "(%s): wrong value for %s got 0x%x - " \
60 "should be greater than 0x%x\n", \
61 __location__, #v, (int)v, (int)gt_val); \
62 ret = false; \
63 goto done; \
64 } } while (0)
66 #define CHECK_CREATED(__io, __created, __attribute) \
67 do { \
68 CHECK_VAL((__io)->out.create_action, \
69 NTCREATEX_ACTION_ ## __created); \
70 CHECK_VAL((__io)->out.size, 0); \
71 CHECK_VAL((__io)->out.file_attr, (__attribute)); \
72 CHECK_VAL((__io)->out.reserved2, 0); \
73 } while (0)
75 #define CHECK_PTR(ptr, correct) do { \
76 if ((ptr) != (correct)) { \
77 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s " \
78 "got 0x%p - should be 0x%p\n", \
79 __location__, #ptr, ptr, correct); \
80 ret = false; \
81 goto done; \
82 } } while (0)
84 #define CHECK_LEASE(__io, __state, __oplevel, __key, __flags) \
85 do { \
86 CHECK_VAL((__io)->out.lease_response.lease_version, 1); \
87 if (__oplevel) { \
88 CHECK_VAL((__io)->out.oplock_level, \
89 SMB2_OPLOCK_LEVEL_LEASE); \
90 CHECK_VAL((__io)->out.lease_response.lease_key.data[0],\
91 (__key)); \
92 CHECK_VAL((__io)->out.lease_response.lease_key.data[1],\
93 ~(__key)); \
94 CHECK_VAL((__io)->out.lease_response.lease_state,\
95 smb2_util_lease_state(__state)); \
96 } else { \
97 CHECK_VAL((__io)->out.oplock_level,\
98 SMB2_OPLOCK_LEVEL_NONE); \
99 CHECK_VAL((__io)->out.lease_response.lease_key.data[0],\
100 0); \
101 CHECK_VAL((__io)->out.lease_response.lease_key.data[1],\
102 0); \
103 CHECK_VAL((__io)->out.lease_response.lease_state, 0); \
106 CHECK_VAL((__io)->out.lease_response.lease_flags, (__flags)); \
107 CHECK_VAL((__io)->out.lease_response.lease_duration, 0); \
108 CHECK_VAL((__io)->out.lease_response.lease_epoch, 0); \
109 } while (0)
111 #define CHECK_LEASE_V2(__io, __state, __oplevel, __key, __flags, __parent, __epoch) \
112 do { \
113 CHECK_VAL((__io)->out.lease_response_v2.lease_version, 2); \
114 if (__oplevel) { \
115 CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); \
116 CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[0], (__key)); \
117 CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[1], ~(__key)); \
118 CHECK_VAL((__io)->out.lease_response_v2.lease_state, smb2_util_lease_state(__state)); \
119 } else { \
120 CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); \
121 CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[0], 0); \
122 CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[1], 0); \
123 CHECK_VAL((__io)->out.lease_response_v2.lease_state, 0); \
126 CHECK_VAL((__io)->out.lease_response_v2.lease_flags, __flags); \
127 if (__flags & SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET) { \
128 CHECK_VAL((__io)->out.lease_response_v2.parent_lease_key.data[0], (__parent)); \
129 CHECK_VAL((__io)->out.lease_response_v2.parent_lease_key.data[1], ~(__parent)); \
131 CHECK_VAL((__io)->out.lease_response_v2.lease_duration, 0); \
132 CHECK_VAL((__io)->out.lease_response_v2.lease_epoch, (__epoch)); \
133 } while(0)
135 #define CHECK_LEASE_BREAK_V2(__lb, __key, __from, __to, __break_flags, __new_epoch) \
136 do { \
137 CHECK_VAL((__lb).current_lease.lease_key.data[0], (__key)); \
138 CHECK_VAL((__lb).current_lease.lease_key.data[1], ~(__key)); \
139 CHECK_VAL((__lb).current_lease.lease_state, smb2_util_lease_state(__from)); \
140 CHECK_VAL((__lb).new_epoch, (__new_epoch)); \
141 CHECK_VAL((__lb).break_flags, (__break_flags)); \
142 CHECK_VAL((__lb).new_lease_state, smb2_util_lease_state(__to)); \
143 } while(0)
145 static bool test_ioctl_network_interface_info(struct torture_context *tctx,
146 struct smb2_tree *tree,
147 struct fsctl_net_iface_info *info)
149 union smb_ioctl ioctl;
150 struct smb2_handle fh;
151 uint32_t caps;
153 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
154 if (!(caps & SMB2_CAP_MULTI_CHANNEL)) {
155 torture_skip(tctx,
156 "server doesn't support SMB2_CAP_MULTI_CHANNEL\n");
159 ZERO_STRUCT(ioctl);
161 ioctl.smb2.level = RAW_IOCTL_SMB2;
163 fh.data[0] = UINT64_MAX;
164 fh.data[1] = UINT64_MAX;
166 ioctl.smb2.in.file.handle = fh;
167 ioctl.smb2.in.function = FSCTL_QUERY_NETWORK_INTERFACE_INFO;
168 /* Windows client sets this to 64KiB */
169 ioctl.smb2.in.max_output_response = 0x10000;
170 ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
172 torture_assert_ntstatus_ok(tctx,
173 smb2_ioctl(tree, tctx, &ioctl.smb2),
174 "FSCTL_QUERY_NETWORK_INTERFACE_INFO failed");
176 torture_assert(tctx,
177 (ioctl.smb2.out.out.length != 0),
178 "no interface info returned???");
180 torture_assert_ndr_success(tctx,
181 ndr_pull_struct_blob(&ioctl.smb2.out.out, tctx, info,
182 (ndr_pull_flags_fn_t)ndr_pull_fsctl_net_iface_info),
183 "failed to ndr pull");
185 if (DEBUGLVL(1)) {
186 NDR_PRINT_DEBUG(fsctl_net_iface_info, info);
189 return true;
192 static bool test_multichannel_interface_info(struct torture_context *tctx,
193 struct smb2_tree *tree)
195 struct fsctl_net_iface_info info;
197 return test_ioctl_network_interface_info(tctx, tree, &info);
200 static struct smb2_tree *test_multichannel_create_channel(
201 struct torture_context *tctx,
202 const char *host,
203 const char *share,
204 struct cli_credentials *credentials,
205 const struct smbcli_options *_transport_options,
206 struct smb2_tree *parent_tree
209 struct smbcli_options transport_options = *_transport_options;
210 NTSTATUS status;
211 struct smb2_transport *transport;
212 struct smb2_session *session;
213 bool ret = true;
214 struct smb2_tree *tree;
216 if (parent_tree) {
217 transport_options.only_negprot = true;
220 status = smb2_connect(tctx,
221 host,
222 lpcfg_smb_ports(tctx->lp_ctx),
223 share,
224 lpcfg_resolve_context(tctx->lp_ctx),
225 credentials,
226 &tree,
227 tctx->ev,
228 &transport_options,
229 lpcfg_socket_options(tctx->lp_ctx),
230 lpcfg_gensec_settings(tctx, tctx->lp_ctx)
232 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
233 "smb2_connect failed");
234 transport = tree->session->transport;
235 transport->oplock.handler = torture_oplock_ack_handler;
236 transport->oplock.private_data = tree;
237 transport->lease.handler = torture_lease_handler;
238 transport->lease.private_data = tree;
239 torture_comment(tctx, "established transport [%p]\n", transport);
242 * If parent tree is set, bind the session to the parent transport
244 if (parent_tree) {
245 session = smb2_session_channel(transport,
246 lpcfg_gensec_settings(tctx, tctx->lp_ctx),
247 parent_tree, parent_tree->session);
248 torture_assert_goto(tctx, session != NULL, ret, done,
249 "smb2_session_channel failed");
251 tree->smbXcli = parent_tree->smbXcli;
252 tree->session = session;
253 status = smb2_session_setup_spnego(session,
254 credentials,
255 0 /* previous_session_id */);
256 CHECK_STATUS(status, NT_STATUS_OK);
257 torture_comment(tctx, "bound new session to parent\n");
260 * We absolutely need to make sure to send something over this
261 * connection to register the oplock break handler with the smb client
262 * connection. If we do not send something (at least a keepalive), we
263 * will *NEVER* receive anything over this transport.
265 smb2_keepalive(transport);
267 done:
268 if (ret) {
269 return tree;
270 } else {
271 return NULL;
275 bool test_multichannel_create_channel_array(
276 struct torture_context *tctx,
277 const char *host,
278 const char *share,
279 struct cli_credentials *credentials,
280 struct smbcli_options *transport_options,
281 uint8_t num_trees,
282 struct smb2_tree **trees)
284 uint8_t i;
286 transport_options->client_guid = GUID_random();
288 for (i = 0; i < num_trees; i++) {
289 struct smb2_tree *parent_tree = NULL;
290 struct smb2_tree *tree = NULL;
291 struct smb2_transport *transport = NULL;
292 uint16_t local_port = 0;
294 if (i > 0) {
295 parent_tree = trees[0];
298 torture_comment(tctx, "Setting up connection %d\n", i);
299 tree = test_multichannel_create_channel(tctx, host, share,
300 credentials, transport_options,
301 parent_tree);
302 torture_assert(tctx, tree, "failed to created new channel");
304 trees[i] = tree;
305 transport = tree->session->transport;
306 local_port = torture_get_local_port_from_transport(transport);
307 torture_comment(tctx, "transport[%d] uses tcp port: %d\n",
308 i, local_port);
311 return true;
314 bool test_multichannel_create_channels(
315 struct torture_context *tctx,
316 const char *host,
317 const char *share,
318 struct cli_credentials *credentials,
319 struct smbcli_options *transport_options,
320 struct smb2_tree **tree2A,
321 struct smb2_tree **tree2B,
322 struct smb2_tree **tree2C
325 struct smb2_tree **trees = NULL;
326 size_t num_trees = 0;
327 bool ret;
329 torture_assert(tctx, tree2A, "tree2A required!");
330 num_trees += 1;
331 torture_assert(tctx, tree2B, "tree2B required!");
332 num_trees += 1;
333 if (tree2C != NULL) {
334 num_trees += 1;
336 trees = talloc_zero_array(tctx, struct smb2_tree *, num_trees);
337 torture_assert(tctx, trees, "out of memory");
339 ret = test_multichannel_create_channel_array(tctx, host, share, credentials,
340 transport_options,
341 num_trees, trees);
342 if (!ret) {
343 return false;
346 *tree2A = trees[0];
347 *tree2B = trees[1];
348 if (tree2C != NULL) {
349 *tree2C = trees[2];
352 return true;
355 static void test_multichannel_free_channels(struct smb2_tree *tree2A,
356 struct smb2_tree *tree2B,
357 struct smb2_tree *tree2C)
359 TALLOC_FREE(tree2A);
360 TALLOC_FREE(tree2B);
361 TALLOC_FREE(tree2C);
364 static bool test_multichannel_initial_checks(struct torture_context *tctx,
365 struct smb2_tree *tree1)
367 struct smb2_transport *transport1 = tree1->session->transport;
368 uint32_t server_capabilities;
369 struct fsctl_net_iface_info info;
371 if (smbXcli_conn_protocol(transport1->conn) < PROTOCOL_SMB3_00) {
372 torture_skip_goto(tctx, fail,
373 "SMB 3.X Dialect family required for "
374 "Multichannel tests\n");
377 server_capabilities = smb2cli_conn_server_capabilities(
378 tree1->session->transport->conn);
379 if (!(server_capabilities & SMB2_CAP_MULTI_CHANNEL)) {
380 torture_skip_goto(tctx, fail,
381 "Server does not support multichannel.");
384 torture_assert(tctx,
385 test_ioctl_network_interface_info(tctx, tree1, &info),
386 "failed to retrieve network interface info");
388 return true;
389 fail:
390 return false;
393 static void test_multichannel_init_smb_create(struct smb2_create *io)
395 io->in.durable_open = false;
396 io->in.durable_open_v2 = true;
397 io->in.persistent_open = false;
398 io->in.create_guid = GUID_random();
399 io->in.timeout = 0x493E0; /* 300000 */
400 /* windows 2016 returns 300000 0x493E0 */
403 /* Timer handler function notifies the registering function that time is up */
404 static void timeout_cb(struct tevent_context *ev,
405 struct tevent_timer *te,
406 struct timeval current_time,
407 void *private_data)
409 bool *timesup = (bool *)private_data;
410 *timesup = true;
414 * Oplock break - Test 1
415 * Test to confirm that server sends oplock breaks as expected.
416 * open file1 in session 2A
417 * open file2 in session 2B
418 * open file1 in session 1
419 * oplock break received
420 * open file1 in session 1
421 * oplock break received
422 * Cleanup
424 static bool test_multichannel_oplock_break_test1(struct torture_context *tctx,
425 struct smb2_tree *tree1)
427 const char *host = torture_setting_string(tctx, "host", NULL);
428 const char *share = torture_setting_string(tctx, "share", NULL);
429 struct cli_credentials *credentials = samba_cmdline_get_creds();
430 NTSTATUS status;
431 TALLOC_CTX *mem_ctx = talloc_new(tctx);
432 struct smb2_handle _h;
433 struct smb2_handle h_client1_file1 = {{0}};
434 struct smb2_handle h_client1_file2 = {{0}};
435 struct smb2_handle h_client1_file3 = {{0}};
436 struct smb2_handle h_client2_file1 = {{0}};
437 struct smb2_handle h_client2_file2 = {{0}};
438 struct smb2_handle h_client2_file3 = {{0}};
439 struct smb2_create io1, io2, io3;
440 bool ret = true;
441 const char *fname1 = BASEDIR "\\oplock_break_test1.dat";
442 const char *fname2 = BASEDIR "\\oplock_break_test2.dat";
443 const char *fname3 = BASEDIR "\\oplock_break_test3.dat";
444 struct smb2_tree *tree2A = NULL;
445 struct smb2_tree *tree2B = NULL;
446 struct smb2_tree *tree2C = NULL;
447 struct smb2_transport *transport1 = tree1->session->transport;
448 struct smbcli_options transport2_options;
449 struct smb2_session *session1 = tree1->session;
450 uint16_t local_port = 0;
452 if (!test_multichannel_initial_checks(tctx, tree1)) {
453 return true;
456 torture_comment(tctx, "Oplock break retry: Test1\n");
458 torture_reset_break_info(tctx, &break_info);
460 transport1->oplock.handler = torture_oplock_ack_handler;
461 transport1->oplock.private_data = tree1;
462 torture_comment(tctx, "transport1 [%p]\n", transport1);
463 local_port = torture_get_local_port_from_transport(transport1);
464 torture_comment(tctx, "transport1 uses tcp port: %d\n", local_port);
466 status = torture_smb2_testdir(tree1, BASEDIR, &_h);
467 CHECK_STATUS(status, NT_STATUS_OK);
468 smb2_util_close(tree1, _h);
469 smb2_util_unlink(tree1, fname1);
470 smb2_util_unlink(tree1, fname2);
471 smb2_util_unlink(tree1, fname3);
472 CHECK_VAL(break_info.count, 0);
474 smb2_oplock_create_share(&io1, fname1,
475 smb2_util_share_access("RWD"),
476 smb2_util_oplock_level("b"));
477 test_multichannel_init_smb_create(&io1);
479 smb2_oplock_create_share(&io2, fname2,
480 smb2_util_share_access("RWD"),
481 smb2_util_oplock_level("b"));
482 test_multichannel_init_smb_create(&io2);
484 smb2_oplock_create_share(&io3, fname3,
485 smb2_util_share_access("RWD"),
486 smb2_util_oplock_level("b"));
487 test_multichannel_init_smb_create(&io3);
489 transport2_options = transport1->options;
491 ret = test_multichannel_create_channels(tctx, host, share,
492 credentials,
493 &transport2_options,
494 &tree2A, &tree2B, NULL);
495 torture_assert(tctx, ret, "Could not create channels.\n");
497 /* 2a opens file1 */
498 torture_comment(tctx, "client2 opens fname1 via session 2A\n");
499 status = smb2_create(tree2A, mem_ctx, &io1);
500 CHECK_STATUS(status, NT_STATUS_OK);
501 h_client2_file1 = io1.out.file.handle;
502 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
503 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
504 torture_wait_for_oplock_break(tctx);
505 CHECK_VAL(break_info.count, 0);
507 /* 2b opens file2 */
508 torture_comment(tctx, "client2 opens fname2 via session 2B\n");
509 status = smb2_create(tree2B, mem_ctx, &io2);
510 CHECK_STATUS(status, NT_STATUS_OK);
511 h_client2_file2 = io2.out.file.handle;
512 CHECK_CREATED(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE);
513 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
514 torture_wait_for_oplock_break(tctx);
515 CHECK_VAL(break_info.count, 0);
518 /* 1 opens file1 - batchoplock break? */
519 torture_comment(tctx, "client1 opens fname1 via session 1\n");
520 io1.in.oplock_level = smb2_util_oplock_level("b");
521 status = smb2_create(tree1, mem_ctx, &io1);
522 CHECK_STATUS(status, NT_STATUS_OK);
523 h_client1_file1 = io1.out.file.handle;
524 CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
525 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("s"));
526 torture_wait_for_oplock_break(tctx);
527 CHECK_VAL(break_info.count, 1);
529 torture_reset_break_info(tctx, &break_info);
531 /* 1 opens file2 - batchoplock break? */
532 torture_comment(tctx, "client1 opens fname2 via session 1\n");
533 io2.in.oplock_level = smb2_util_oplock_level("b");
534 status = smb2_create(tree1, mem_ctx, &io2);
535 CHECK_STATUS(status, NT_STATUS_OK);
536 h_client1_file2 = io2.out.file.handle;
537 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
538 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("s"));
539 torture_wait_for_oplock_break(tctx);
540 CHECK_VAL(break_info.count, 1);
542 /* cleanup everything */
543 torture_reset_break_info(tctx, &break_info);
545 smb2_util_close(tree1, h_client1_file1);
546 smb2_util_close(tree1, h_client1_file2);
547 smb2_util_close(tree1, h_client1_file3);
548 smb2_util_close(tree2A, h_client2_file1);
549 smb2_util_close(tree2A, h_client2_file2);
550 smb2_util_close(tree2A, h_client2_file3);
552 smb2_util_unlink(tree1, fname1);
553 smb2_util_unlink(tree1, fname2);
554 smb2_util_unlink(tree1, fname3);
555 CHECK_VAL(break_info.count, 0);
556 test_multichannel_free_channels(tree2A, tree2B, tree2C);
557 tree2A = tree2B = tree2C = NULL;
558 done:
559 tree1->session = session1;
561 smb2_util_close(tree1, h_client1_file1);
562 smb2_util_close(tree1, h_client1_file2);
563 smb2_util_close(tree1, h_client1_file3);
564 if (tree2A != NULL) {
565 smb2_util_close(tree2A, h_client2_file1);
566 smb2_util_close(tree2A, h_client2_file2);
567 smb2_util_close(tree2A, h_client2_file3);
570 smb2_util_unlink(tree1, fname1);
571 smb2_util_unlink(tree1, fname2);
572 smb2_util_unlink(tree1, fname3);
573 smb2_deltree(tree1, BASEDIR);
575 test_multichannel_free_channels(tree2A, tree2B, tree2C);
576 talloc_free(tree1);
577 talloc_free(mem_ctx);
579 return ret;
583 * Oplock Break Test 2
584 * Test to see if oplock break retries are sent by the server.
585 * Also checks to see if new channels can be created and used
586 * after an oplock break retry.
587 * open file1 in 2A
588 * open file2 in 2B
589 * open file1 in session 1
590 * oplock break received
591 * block channel on which oplock break received
592 * open file2 in session 1
593 * oplock break not received. Retry received.
594 * file opened
595 * write to file2 on 2B
596 * Break sent to session 1(which has file2 open)
597 * Break sent to session 2A(which has read oplock)
598 * close file1 in session 1
599 * open file1 with session 1
600 * unblock blocked channel
601 * disconnect blocked channel
602 * connect channel 2D
603 * open file3 in 2D
604 * open file3 in session 1
605 * receive break
607 static bool test_multichannel_oplock_break_test2(struct torture_context *tctx,
608 struct smb2_tree *tree1)
610 const char *host = torture_setting_string(tctx, "host", NULL);
611 const char *share = torture_setting_string(tctx, "share", NULL);
612 struct cli_credentials *credentials = samba_cmdline_get_creds();
613 NTSTATUS status;
614 TALLOC_CTX *mem_ctx = talloc_new(tctx);
615 struct smb2_handle _h;
616 struct smb2_handle h_client1_file1 = {{0}};
617 struct smb2_handle h_client1_file2 = {{0}};
618 struct smb2_handle h_client1_file3 = {{0}};
619 struct smb2_handle h_client2_file1 = {{0}};
620 struct smb2_handle h_client2_file2 = {{0}};
621 struct smb2_handle h_client2_file3 = {{0}};
622 struct smb2_create io1, io2, io3;
623 bool ret = true;
624 const char *fname1 = BASEDIR "\\oplock_break_test1.dat";
625 const char *fname2 = BASEDIR "\\oplock_break_test2.dat";
626 const char *fname3 = BASEDIR "\\oplock_break_test3.dat";
627 struct smb2_tree *tree2A = NULL;
628 struct smb2_tree *tree2B = NULL;
629 struct smb2_tree *tree2C = NULL;
630 struct smb2_tree *tree2D = NULL;
631 struct smb2_transport *transport1 = tree1->session->transport;
632 struct smb2_transport *transport2 = NULL;
633 struct smbcli_options transport2_options;
634 struct smb2_session *session1 = tree1->session;
635 uint16_t local_port = 0;
636 DATA_BLOB blob;
637 bool block_setup = false;
638 bool block_ok = false;
639 bool unblock_ok = false;
641 if (!test_multichannel_initial_checks(tctx, tree1)) {
642 return true;
645 torture_comment(tctx, "Oplock break retry: Test2\n");
647 torture_reset_break_info(tctx, &break_info);
649 transport1->oplock.handler = torture_oplock_ack_handler;
650 transport1->oplock.private_data = tree1;
651 torture_comment(tctx, "transport1 [%p]\n", transport1);
652 local_port = torture_get_local_port_from_transport(transport1);
653 torture_comment(tctx, "transport1 uses tcp port: %d\n", local_port);
655 status = torture_smb2_testdir(tree1, BASEDIR, &_h);
656 CHECK_STATUS(status, NT_STATUS_OK);
657 smb2_util_close(tree1, _h);
658 smb2_util_unlink(tree1, fname1);
659 smb2_util_unlink(tree1, fname2);
660 smb2_util_unlink(tree1, fname3);
661 CHECK_VAL(break_info.count, 0);
663 smb2_oplock_create_share(&io1, fname1,
664 smb2_util_share_access("RWD"),
665 smb2_util_oplock_level("b"));
666 test_multichannel_init_smb_create(&io1);
668 smb2_oplock_create_share(&io2, fname2,
669 smb2_util_share_access("RWD"),
670 smb2_util_oplock_level("b"));
671 test_multichannel_init_smb_create(&io2);
673 smb2_oplock_create_share(&io3, fname3,
674 smb2_util_share_access("RWD"),
675 smb2_util_oplock_level("b"));
676 test_multichannel_init_smb_create(&io3);
678 transport2_options = transport1->options;
680 ret = test_multichannel_create_channels(tctx, host, share,
681 credentials,
682 &transport2_options,
683 &tree2A, &tree2B, &tree2C);
684 torture_assert(tctx, ret, "Could not create channels.\n");
686 torture_comment(tctx, "client2 opens fname1 via session 2A\n");
687 io1.in.oplock_level = smb2_util_oplock_level("b");
688 status = smb2_create(tree2A, mem_ctx, &io1);
689 CHECK_STATUS(status, NT_STATUS_OK);
690 h_client2_file1 = io1.out.file.handle;
691 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
692 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
693 torture_wait_for_oplock_break(tctx);
694 CHECK_VAL(break_info.count, 0);
697 torture_comment(tctx, "client2 opens fname2 via session 2B\n");
698 io2.in.oplock_level = smb2_util_oplock_level("b");
699 status = smb2_create(tree2B, mem_ctx, &io2);
700 CHECK_STATUS(status, NT_STATUS_OK);
701 h_client2_file2 = io2.out.file.handle;
702 CHECK_CREATED(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE);
703 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
704 torture_wait_for_oplock_break(tctx);
705 CHECK_VAL(break_info.count, 0);
708 torture_comment(tctx, "client1 opens fname1 via session 1\n");
709 io1.in.oplock_level = smb2_util_oplock_level("b");
710 status = smb2_create(tree1, mem_ctx, &io1);
711 CHECK_STATUS(status, NT_STATUS_OK);
712 h_client1_file1 = io1.out.file.handle;
713 CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
714 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("s"));
715 torture_wait_for_oplock_break(tctx);
716 CHECK_VAL(break_info.count, 1);
718 /* We use the transport over which this oplock break was received */
719 transport2 = break_info.received_transport;
720 torture_reset_break_info(tctx, &break_info);
722 block_setup = test_setup_blocked_transports(tctx);
723 torture_assert(tctx, block_setup, "test_setup_blocked_transports");
725 /* block channel */
726 block_ok = test_block_smb2_transport(tctx, transport2);
728 torture_comment(tctx, "client1 opens fname2 via session 1\n");
729 io2.in.oplock_level = smb2_util_oplock_level("b");
730 status = smb2_create(tree1, mem_ctx, &io2);
731 CHECK_STATUS(status, NT_STATUS_OK);
732 h_client1_file2 = io2.out.file.handle;
733 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
734 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("s"));
737 * Samba downgrades oplock to a level 2 oplock.
738 * Windows 2016 revokes oplock
740 torture_wait_for_oplock_break(tctx);
741 CHECK_VAL(break_info.count, 1);
742 torture_reset_break_info(tctx, &break_info);
744 torture_comment(tctx, "Trying write to file2 on tree2B\n");
746 blob = data_blob_string_const("Here I am");
747 status = smb2_util_write(tree2B,
748 h_client2_file2,
749 blob.data,
751 blob.length);
752 torture_assert_ntstatus_ok(tctx, status,
753 "failed to write file2 via channel 2B");
756 * Samba: Write triggers 2 oplock breaks
757 * for session 1 which has file2 open
758 * for session 2 which has type 2 oplock
759 * Windows 2016: Only one oplock break for session 1
761 torture_wait_for_oplock_break(tctx);
762 CHECK_VAL_GREATER_THAN(break_info.count, 0);
763 torture_reset_break_info(tctx, &break_info);
765 torture_comment(tctx, "client1 closes fname2 via session 1\n");
766 smb2_util_close(tree1, h_client1_file2);
768 torture_comment(tctx, "client1 opens fname2 via session 1 again\n");
769 io2.in.oplock_level = smb2_util_oplock_level("b");
770 status = smb2_create(tree1, mem_ctx, &io2);
771 CHECK_STATUS(status, NT_STATUS_OK);
772 h_client1_file2 = io2.out.file.handle;
773 io2.out.alloc_size = 0;
774 io2.out.size = 0;
775 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
776 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("s"));
779 * now add a fourth channel and repeat the test, we need to reestablish
780 * transport2 because the remote end has invalidated our connection
782 torture_comment(tctx, "Connecting session 2D\n");
783 tree2D = test_multichannel_create_channel(tctx, host, share,
784 credentials, &transport2_options, tree2B);
785 if (!tree2D) {
786 goto done;
789 torture_reset_break_info(tctx, &break_info);
790 torture_comment(tctx, "client 2 opening fname3 over transport2D\n");
791 status = smb2_create(tree2D, mem_ctx, &io3);
792 CHECK_STATUS(status, NT_STATUS_OK);
793 h_client2_file3 = io3.out.file.handle;
794 CHECK_CREATED(&io3, CREATED, FILE_ATTRIBUTE_ARCHIVE);
795 CHECK_VAL(io3.out.oplock_level, smb2_util_oplock_level("b"));
796 torture_wait_for_oplock_break(tctx);
797 CHECK_VAL(break_info.count, 0);
799 torture_comment(tctx, "client1 opens fname3 via session 1\n");
800 status = smb2_create(tree1, mem_ctx, &io3);
801 CHECK_STATUS(status, NT_STATUS_OK);
802 h_client1_file3 = io3.out.file.handle;
803 CHECK_CREATED(&io3, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
804 CHECK_VAL(io3.out.oplock_level, smb2_util_oplock_level("s"));
805 torture_wait_for_oplock_break(tctx);
806 CHECK_VAL(break_info.count, 1);
808 done:
809 if (block_ok && !unblock_ok) {
810 test_unblock_smb2_transport(tctx, transport2);
812 test_cleanup_blocked_transports(tctx);
814 tree1->session = session1;
816 smb2_util_close(tree1, h_client1_file1);
817 smb2_util_close(tree1, h_client1_file2);
818 smb2_util_close(tree1, h_client1_file3);
819 if (tree2B != NULL) {
820 smb2_util_close(tree2B, h_client2_file1);
821 smb2_util_close(tree2B, h_client2_file2);
822 smb2_util_close(tree2B, h_client2_file3);
825 smb2_util_unlink(tree1, fname1);
826 smb2_util_unlink(tree1, fname2);
827 smb2_util_unlink(tree1, fname3);
828 smb2_deltree(tree1, BASEDIR);
830 test_multichannel_free_channels(tree2A, tree2B, tree2C);
831 if (tree2D != NULL) {
832 TALLOC_FREE(tree2D);
834 talloc_free(tree1);
835 talloc_free(mem_ctx);
837 return ret;
840 struct test_multichannel_oplock_break_state;
842 struct test_multichannel_oplock_break_channel {
843 struct test_multichannel_oplock_break_state *state;
844 size_t idx;
845 char name[64];
846 struct smb2_tree *tree;
847 bool blocked;
848 struct timeval break_time;
849 double full_duration;
850 double relative_duration;
851 uint8_t level;
852 size_t break_num;
855 struct test_multichannel_oplock_break_state {
856 struct torture_context *tctx;
857 struct timeval open_req_time;
858 struct timeval open_rep_time;
859 size_t num_breaks;
860 struct timeval last_break_time;
861 struct test_multichannel_oplock_break_channel channels[32];
864 static bool test_multichannel_oplock_break_handler(struct smb2_transport *transport,
865 const struct smb2_handle *handle,
866 uint8_t level,
867 void *private_data)
869 struct test_multichannel_oplock_break_channel *c =
870 (struct test_multichannel_oplock_break_channel *)private_data;
871 struct test_multichannel_oplock_break_state *state = c->state;
873 c->break_time = timeval_current();
874 c->full_duration = timeval_elapsed2(&state->open_req_time,
875 &c->break_time);
876 c->relative_duration = timeval_elapsed2(&state->last_break_time,
877 &c->break_time);
878 state->last_break_time = c->break_time;
879 c->level = level;
880 c->break_num = ++state->num_breaks;
882 torture_comment(state->tctx, "Got OPLOCK break %zu on %s after %f ( %f)\n",
883 c->break_num, c->name,
884 c->relative_duration,
885 c->full_duration);
887 return torture_oplock_ack_handler(transport, handle, level, c->tree);
890 static bool test_multichannel_oplock_break_test3_windows(struct torture_context *tctx,
891 struct smb2_tree *tree1)
893 const char *host = torture_setting_string(tctx, "host", NULL);
894 const char *share = torture_setting_string(tctx, "share", NULL);
895 struct cli_credentials *credentials = samba_cmdline_get_creds();
896 NTSTATUS status;
897 TALLOC_CTX *mem_ctx = talloc_new(tctx);
898 struct test_multichannel_oplock_break_state state = {
899 .tctx = tctx,
901 struct test_multichannel_oplock_break_channel *open2_channel = NULL;
902 struct smb2_handle _h;
903 struct smb2_handle *h = NULL;
904 struct smb2_handle h_client1_file1 = {{0}};
905 struct smb2_handle h_client2_file1 = {{0}};
906 struct smb2_create io1;
907 struct smb2_create io2;
908 bool ret = true;
909 const char *fname1 = BASEDIR "\\oplock_break_test3w.dat";
910 struct smb2_tree *trees2[32] = { NULL, };
911 size_t i;
912 struct smb2_transport *transport1 = tree1->session->transport;
913 struct smbcli_options transport2_options;
914 struct smb2_session *session1 = tree1->session;
915 uint16_t local_port = 0;
916 bool block_setup = false;
917 bool block_ok = false;
918 double open_duration;
920 if (!test_multichannel_initial_checks(tctx, tree1)) {
921 return true;
924 torture_comment(tctx, "Oplock break retry: Test3 (Windows behavior)\n");
926 torture_reset_break_info(tctx, &break_info);
927 break_info.oplock_skip_ack = true;
929 transport1->oplock.handler = torture_oplock_ack_handler;
930 transport1->oplock.private_data = tree1;
931 torture_comment(tctx, "transport1 [%p]\n", transport1);
932 local_port = torture_get_local_port_from_transport(transport1);
933 torture_comment(tctx, "transport1 uses tcp port: %d\n", local_port);
935 status = torture_smb2_testdir(tree1, BASEDIR, &_h);
936 CHECK_STATUS(status, NT_STATUS_OK);
937 smb2_util_close(tree1, _h);
938 smb2_util_unlink(tree1, fname1);
939 CHECK_VAL(break_info.count, 0);
941 smb2_oplock_create_share(&io2, fname1,
942 smb2_util_share_access("RWD"),
943 smb2_util_oplock_level("b"));
945 transport2_options = transport1->options;
947 ret = test_multichannel_create_channel_array(tctx, host, share, credentials,
948 &transport2_options,
949 ARRAY_SIZE(trees2), trees2);
950 torture_assert(tctx, ret, "Could not create channels.\n");
952 for (i = 0; i < ARRAY_SIZE(trees2); i++) {
953 struct test_multichannel_oplock_break_channel *c = &state.channels[i];
954 struct smb2_transport *t = trees2[i]->session->transport;
956 c->state = &state;
957 c->idx = i+1;
958 c->tree = trees2[i];
959 snprintf(c->name, sizeof(c->name), "trees2_%zu", c->idx);
961 t->oplock.handler = test_multichannel_oplock_break_handler;
962 t->oplock.private_data = c;
965 open2_channel = &state.channels[0];
967 /* 2a opens file1 */
968 torture_comment(tctx, "client2 opens fname1 via %s\n",
969 open2_channel->name);
970 status = smb2_create(open2_channel->tree, mem_ctx, &io2);
971 CHECK_STATUS(status, NT_STATUS_OK);
972 h_client2_file1 = io2.out.file.handle;
973 CHECK_CREATED(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE);
974 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
975 CHECK_VAL(io2.out.durable_open_v2, false);
976 CHECK_VAL(io2.out.timeout, io2.in.timeout);
977 CHECK_VAL(io2.out.durable_open, false);
978 CHECK_VAL(break_info.count, 0);
980 block_setup = test_setup_blocked_transports(tctx);
981 torture_assert(tctx, block_setup, "test_setup_blocked_transports");
983 for (i = 0; i < ARRAY_SIZE(state.channels); i++) {
984 struct test_multichannel_oplock_break_channel *c = &state.channels[i];
985 struct smb2_transport *t = c->tree->session->transport;
987 torture_comment(tctx, "Blocking %s\n", c->name);
988 block_ok = _test_block_smb2_transport(tctx, t, c->name);
989 torture_assert_goto(tctx, block_ok, ret, done, "we could not block tcp transport");
990 c->blocked = true;
993 /* 1 opens file2 */
994 torture_comment(tctx,
995 "Client opens fname1 with session 1 with all %zu blocked\n",
996 ARRAY_SIZE(trees2));
997 smb2_oplock_create_share(&io1, fname1,
998 smb2_util_share_access("RWD"),
999 smb2_util_oplock_level("b"));
1000 CHECK_VAL(lease_break_info.count, 0);
1001 state.open_req_time = timeval_current();
1002 state.last_break_time = state.open_req_time;
1003 status = smb2_create(tree1, mem_ctx, &io1);
1004 state.open_rep_time = timeval_current();
1005 CHECK_STATUS(status, NT_STATUS_OK);
1006 h_client1_file1 = io1.out.file.handle;
1007 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("s"));
1009 CHECK_VAL(break_info.count, 1);
1011 open_duration = timeval_elapsed2(&state.open_req_time,
1012 &state.open_rep_time);
1013 torture_comment(tctx, "open_duration: %f\n", open_duration);
1014 CHECK_VAL_GREATER_THAN(open_duration, 35);
1016 if (break_info.count == 0) {
1017 torture_comment(tctx,
1018 "Did not receive expected oplock break!!\n");
1019 } else {
1020 torture_comment(tctx, "Received %d oplock break(s)!!\n",
1021 break_info.count);
1024 for (i = 0; i < ARRAY_SIZE(state.channels); i++) {
1025 struct test_multichannel_oplock_break_channel *c = &state.channels[i];
1026 size_t expected_break_num = 0;
1029 * Only the latest channel gets a break notification
1031 if (i == (ARRAY_SIZE(state.channels) - 1)) {
1032 expected_break_num = 1;
1035 torture_comment(tctx, "Verify %s\n", c->name);
1036 torture_assert_int_equal(tctx, c->break_num, expected_break_num,
1037 "Got oplock break on wrong channel");
1038 if (expected_break_num != 0) {
1039 CHECK_VAL(c->level, smb2_util_oplock_level("s"));
1043 done:
1044 for (i = 0; i < ARRAY_SIZE(state.channels); i++) {
1045 struct test_multichannel_oplock_break_channel *c = &state.channels[i];
1046 struct smb2_transport *t = NULL;
1048 if (!c->blocked) {
1049 continue;
1052 t = c->tree->session->transport;
1054 torture_comment(tctx, "Unblocking %s\n", c->name);
1055 _test_unblock_smb2_transport(tctx, t, c->name);
1056 c->blocked = false;
1058 if (block_setup) {
1059 test_cleanup_blocked_transports(tctx);
1062 tree1->session = session1;
1064 smb2_util_close(tree1, h_client1_file1);
1065 if (trees2[0] != NULL) {
1066 smb2_util_close(trees2[0], h_client2_file1);
1069 if (h != NULL) {
1070 smb2_util_close(tree1, *h);
1073 smb2_util_unlink(tree1, fname1);
1074 smb2_deltree(tree1, BASEDIR);
1076 for (i = 0; i < ARRAY_SIZE(trees2); i++) {
1077 if (trees2[i] == NULL) {
1078 continue;
1080 TALLOC_FREE(trees2[i]);
1082 talloc_free(tree1);
1083 talloc_free(mem_ctx);
1085 return ret;
1088 static bool test_multichannel_oplock_break_test3_specification(struct torture_context *tctx,
1089 struct smb2_tree *tree1)
1091 const char *host = torture_setting_string(tctx, "host", NULL);
1092 const char *share = torture_setting_string(tctx, "share", NULL);
1093 struct cli_credentials *credentials = samba_cmdline_get_creds();
1094 NTSTATUS status;
1095 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1096 struct test_multichannel_oplock_break_state state = {
1097 .tctx = tctx,
1099 struct test_multichannel_oplock_break_channel *open2_channel = NULL;
1100 struct smb2_handle _h;
1101 struct smb2_handle *h = NULL;
1102 struct smb2_handle h_client1_file1 = {{0}};
1103 struct smb2_handle h_client2_file1 = {{0}};
1104 struct smb2_create io1;
1105 struct smb2_create io2;
1106 bool ret = true;
1107 const char *fname1 = BASEDIR "\\oplock_break_test3s.dat";
1108 struct smb2_tree *trees2[32] = { NULL, };
1109 size_t i;
1110 struct smb2_transport *transport1 = tree1->session->transport;
1111 struct smbcli_options transport2_options;
1112 struct smb2_session *session1 = tree1->session;
1113 uint16_t local_port = 0;
1114 bool block_setup = false;
1115 bool block_ok = false;
1116 double open_duration;
1118 if (!test_multichannel_initial_checks(tctx, tree1)) {
1119 return true;
1122 torture_comment(tctx, "Oplock break retry: Test3 (Specification behavior)\n");
1124 torture_reset_break_info(tctx, &break_info);
1125 break_info.oplock_skip_ack = true;
1127 transport1->oplock.handler = torture_oplock_ack_handler;
1128 transport1->oplock.private_data = tree1;
1129 torture_comment(tctx, "transport1 [%p]\n", transport1);
1130 local_port = torture_get_local_port_from_transport(transport1);
1131 torture_comment(tctx, "transport1 uses tcp port: %d\n", local_port);
1133 status = torture_smb2_testdir(tree1, BASEDIR, &_h);
1134 CHECK_STATUS(status, NT_STATUS_OK);
1135 smb2_util_close(tree1, _h);
1136 smb2_util_unlink(tree1, fname1);
1137 CHECK_VAL(break_info.count, 0);
1139 smb2_oplock_create_share(&io2, fname1,
1140 smb2_util_share_access("RWD"),
1141 smb2_util_oplock_level("b"));
1143 transport2_options = transport1->options;
1145 ret = test_multichannel_create_channel_array(tctx, host, share, credentials,
1146 &transport2_options,
1147 ARRAY_SIZE(trees2), trees2);
1148 torture_assert(tctx, ret, "Could not create channels.\n");
1150 for (i = 0; i < ARRAY_SIZE(trees2); i++) {
1151 struct test_multichannel_oplock_break_channel *c = &state.channels[i];
1152 struct smb2_transport *t = trees2[i]->session->transport;
1154 c->state = &state;
1155 c->idx = i+1;
1156 c->tree = trees2[i];
1157 snprintf(c->name, sizeof(c->name), "trees2_%zu", c->idx);
1159 t->oplock.handler = test_multichannel_oplock_break_handler;
1160 t->oplock.private_data = c;
1163 open2_channel = &state.channels[0];
1165 /* 2a opens file1 */
1166 torture_comment(tctx, "client2 opens fname1 via %s\n",
1167 open2_channel->name);
1168 status = smb2_create(open2_channel->tree, mem_ctx, &io2);
1169 CHECK_STATUS(status, NT_STATUS_OK);
1170 h_client2_file1 = io2.out.file.handle;
1171 CHECK_CREATED(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1172 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1173 CHECK_VAL(io2.out.durable_open_v2, false);
1174 CHECK_VAL(io2.out.timeout, io2.in.timeout);
1175 CHECK_VAL(io2.out.durable_open, false);
1176 CHECK_VAL(break_info.count, 0);
1178 block_setup = test_setup_blocked_transports(tctx);
1179 torture_assert(tctx, block_setup, "test_setup_blocked_transports");
1181 for (i = 0; i < ARRAY_SIZE(state.channels); i++) {
1182 struct test_multichannel_oplock_break_channel *c = &state.channels[i];
1183 struct smb2_transport *t = c->tree->session->transport;
1185 torture_comment(tctx, "Blocking %s\n", c->name);
1186 block_ok = _test_block_smb2_transport(tctx, t, c->name);
1187 torture_assert_goto(tctx, block_ok, ret, done, "we could not block tcp transport");
1188 c->blocked = true;
1191 /* 1 opens file2 */
1192 torture_comment(tctx,
1193 "Client opens fname1 with session 1 with all %zu blocked\n",
1194 ARRAY_SIZE(trees2));
1195 smb2_oplock_create_share(&io1, fname1,
1196 smb2_util_share_access("RWD"),
1197 smb2_util_oplock_level("b"));
1198 CHECK_VAL(lease_break_info.count, 0);
1199 state.open_req_time = timeval_current();
1200 state.last_break_time = state.open_req_time;
1201 status = smb2_create(tree1, mem_ctx, &io1);
1202 state.open_rep_time = timeval_current();
1203 CHECK_STATUS(status, NT_STATUS_OK);
1204 h_client1_file1 = io1.out.file.handle;
1206 CHECK_VAL_GREATER_THAN(break_info.count, 1);
1208 open_duration = timeval_elapsed2(&state.open_req_time,
1209 &state.open_rep_time);
1210 torture_comment(tctx, "open_duration: %f\n", open_duration);
1211 if (break_info.count < ARRAY_SIZE(state.channels)) {
1212 CHECK_VAL_GREATER_THAN(open_duration, 35);
1213 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("s"));
1214 } else {
1215 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1218 for (i = 0; i < ARRAY_SIZE(state.channels); i++) {
1219 if (break_info.count >= ARRAY_SIZE(state.channels)) {
1220 break;
1222 torture_comment(tctx, "Received %d oplock break(s) wait for more!!\n",
1223 break_info.count);
1224 torture_wait_for_oplock_break(tctx);
1227 if (break_info.count == 0) {
1228 torture_comment(tctx,
1229 "Did not receive expected oplock break!!\n");
1230 } else {
1231 torture_comment(tctx, "Received %d oplock break(s)!!\n",
1232 break_info.count);
1235 if (break_info.count < ARRAY_SIZE(state.channels)) {
1236 CHECK_VAL_GREATER_THAN(break_info.count, 3);
1237 } else {
1238 CHECK_VAL(break_info.count, ARRAY_SIZE(state.channels));
1241 for (i = 0; i < break_info.count; i++) {
1242 struct test_multichannel_oplock_break_channel *c = &state.channels[i];
1244 torture_comment(tctx, "Verify %s\n", c->name);
1245 torture_assert_int_equal(tctx, c->break_num, c->idx,
1246 "Got oplock break on wrong channel");
1247 CHECK_VAL(c->level, smb2_util_oplock_level("s"));
1250 done:
1251 for (i = 0; i < ARRAY_SIZE(state.channels); i++) {
1252 struct test_multichannel_oplock_break_channel *c = &state.channels[i];
1253 struct smb2_transport *t = NULL;
1255 if (!c->blocked) {
1256 continue;
1259 t = c->tree->session->transport;
1261 torture_comment(tctx, "Unblocking %s\n", c->name);
1262 _test_unblock_smb2_transport(tctx, t, c->name);
1263 c->blocked = false;
1265 if (block_setup) {
1266 test_cleanup_blocked_transports(tctx);
1269 tree1->session = session1;
1271 smb2_util_close(tree1, h_client1_file1);
1272 if (trees2[0] != NULL) {
1273 smb2_util_close(trees2[0], h_client2_file1);
1276 if (h != NULL) {
1277 smb2_util_close(tree1, *h);
1280 smb2_util_unlink(tree1, fname1);
1281 smb2_deltree(tree1, BASEDIR);
1283 for (i = 0; i < ARRAY_SIZE(trees2); i++) {
1284 if (trees2[i] == NULL) {
1285 continue;
1287 TALLOC_FREE(trees2[i]);
1289 talloc_free(tree1);
1290 talloc_free(mem_ctx);
1292 return ret;
1295 static const uint64_t LEASE1F1 = 0xBADC0FFEE0DDF00Dull;
1296 static const uint64_t LEASE1F2 = 0xBADC0FFEE0DDD00Dull;
1297 static const uint64_t LEASE1F3 = 0xDADC0FFEE0DDD00Dull;
1298 static const uint64_t LEASE2F1 = 0xDEADBEEFFEEDBEADull;
1299 static const uint64_t LEASE2F2 = 0xDAD0FFEDD00DF00Dull;
1300 static const uint64_t LEASE2F3 = 0xBAD0FFEDD00DF00Dull;
1303 * Lease Break Test 1:
1304 * Test to check if lease breaks are sent by the server as expected.
1305 * open file1 in session 2A
1306 * open file2 in session 2B
1307 * open file3 in session 2C
1308 * open file1 in session 1
1309 * lease break sent
1310 * open file2 in session 1
1311 * lease break sent
1312 * open file3 in session 1
1313 * lease break sent
1315 static bool test_multichannel_lease_break_test1(struct torture_context *tctx,
1316 struct smb2_tree *tree1)
1318 const char *host = torture_setting_string(tctx, "host", NULL);
1319 const char *share = torture_setting_string(tctx, "share", NULL);
1320 struct cli_credentials *credentials = samba_cmdline_get_creds();
1321 NTSTATUS status;
1322 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1323 struct smb2_handle _h;
1324 struct smb2_handle *h = NULL;
1325 struct smb2_handle h_client1_file1 = {{0}};
1326 struct smb2_handle h_client1_file2 = {{0}};
1327 struct smb2_handle h_client1_file3 = {{0}};
1328 struct smb2_handle h_client2_file1 = {{0}};
1329 struct smb2_handle h_client2_file2 = {{0}};
1330 struct smb2_handle h_client2_file3 = {{0}};
1331 struct smb2_create io1, io2, io3;
1332 bool ret = true;
1333 const char *fname1 = BASEDIR "\\lease_break_test1.dat";
1334 const char *fname2 = BASEDIR "\\lease_break_test2.dat";
1335 const char *fname3 = BASEDIR "\\lease_break_test3.dat";
1336 struct smb2_tree *tree2A = NULL;
1337 struct smb2_tree *tree2B = NULL;
1338 struct smb2_tree *tree2C = NULL;
1339 struct smb2_transport *transport1 = tree1->session->transport;
1340 struct smbcli_options transport2_options;
1341 struct smb2_session *session1 = tree1->session;
1342 uint16_t local_port = 0;
1343 struct smb2_lease ls1;
1344 struct smb2_lease ls2;
1345 struct smb2_lease ls3;
1347 if (!test_multichannel_initial_checks(tctx, tree1)) {
1348 return true;
1351 torture_comment(tctx, "Lease break retry: Test1\n");
1353 torture_reset_lease_break_info(tctx, &lease_break_info);
1355 transport1->lease.handler = torture_lease_handler;
1356 transport1->lease.private_data = tree1;
1357 torture_comment(tctx, "transport1 [%p]\n", transport1);
1358 local_port = torture_get_local_port_from_transport(transport1);
1359 torture_comment(tctx, "transport1 uses tcp port: %d\n", local_port);
1361 status = torture_smb2_testdir(tree1, BASEDIR, &_h);
1362 CHECK_STATUS(status, NT_STATUS_OK);
1363 smb2_util_close(tree1, _h);
1364 smb2_util_unlink(tree1, fname1);
1365 smb2_util_unlink(tree1, fname2);
1366 smb2_util_unlink(tree1, fname3);
1367 CHECK_VAL(lease_break_info.count, 0);
1369 smb2_lease_create(&io1, &ls1, false, fname1, LEASE2F1,
1370 smb2_util_lease_state("RHW"));
1371 test_multichannel_init_smb_create(&io1);
1373 smb2_lease_create(&io2, &ls2, false, fname2, LEASE2F2,
1374 smb2_util_lease_state("RHW"));
1375 test_multichannel_init_smb_create(&io2);
1377 smb2_lease_create(&io3, &ls3, false, fname3, LEASE2F3,
1378 smb2_util_lease_state("RHW"));
1379 test_multichannel_init_smb_create(&io3);
1381 transport2_options = transport1->options;
1383 ret = test_multichannel_create_channels(tctx, host, share,
1384 credentials,
1385 &transport2_options,
1386 &tree2A, &tree2B, &tree2C);
1387 torture_assert(tctx, ret, "Could not create channels.\n");
1389 /* 2a opens file1 */
1390 torture_comment(tctx, "client2 opens fname1 via session 2A\n");
1391 status = smb2_create(tree2A, mem_ctx, &io1);
1392 CHECK_STATUS(status, NT_STATUS_OK);
1393 h_client2_file1 = io1.out.file.handle;
1394 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1395 CHECK_LEASE(&io1, "RHW", true, LEASE2F1, 0);
1396 CHECK_VAL(lease_break_info.count, 0);
1398 /* 2b opens file2 */
1399 torture_comment(tctx, "client2 opens fname2 via session 2B\n");
1400 status = smb2_create(tree2B, mem_ctx, &io2);
1401 CHECK_STATUS(status, NT_STATUS_OK);
1402 h_client2_file2 = io2.out.file.handle;
1403 CHECK_CREATED(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1404 CHECK_LEASE(&io2, "RHW", true, LEASE2F2, 0);
1405 CHECK_VAL(lease_break_info.count, 0);
1407 /* 2c opens file3 */
1408 torture_comment(tctx, "client2 opens fname3 via session 2C\n");
1409 smb2_lease_create(&io3, &ls3, false, fname3, LEASE2F3,
1410 smb2_util_lease_state("RHW"));
1411 status = smb2_create(tree2C, mem_ctx, &io3);
1412 CHECK_STATUS(status, NT_STATUS_OK);
1413 h_client2_file3 = io3.out.file.handle;
1414 CHECK_CREATED(&io3, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1415 CHECK_LEASE(&io3, "RHW", true, LEASE2F3, 0);
1416 CHECK_VAL(lease_break_info.count, 0);
1418 /* 1 opens file1 - lease break? */
1419 torture_comment(tctx, "client1 opens fname1 via session 1\n");
1420 smb2_lease_create(&io1, &ls1, false, fname1, LEASE1F1,
1421 smb2_util_lease_state("RHW"));
1422 status = smb2_create(tree1, mem_ctx, &io1);
1423 CHECK_STATUS(status, NT_STATUS_OK);
1424 h_client1_file1 = io1.out.file.handle;
1425 CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1426 CHECK_LEASE(&io1, "RH", true, LEASE1F1, 0);
1427 CHECK_BREAK_INFO("RHW", "RH", LEASE2F1);
1428 CHECK_VAL(lease_break_info.count, 1);
1430 torture_reset_lease_break_info(tctx, &lease_break_info);
1432 /* 1 opens file2 - lease break? */
1433 torture_comment(tctx, "client1 opens fname2 via session 1\n");
1434 smb2_lease_create(&io2, &ls2, false, fname2, LEASE1F2,
1435 smb2_util_lease_state("RHW"));
1436 status = smb2_create(tree1, mem_ctx, &io2);
1437 CHECK_STATUS(status, NT_STATUS_OK);
1438 h_client1_file2 = io2.out.file.handle;
1439 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1440 CHECK_LEASE(&io2, "RH", true, LEASE1F2, 0);
1441 CHECK_BREAK_INFO("RHW", "RH", LEASE2F2);
1442 CHECK_VAL(lease_break_info.count, 1);
1444 torture_reset_lease_break_info(tctx, &lease_break_info);
1446 /* 1 opens file3 - lease break? */
1447 torture_comment(tctx, "client1 opens fname3 via session 1\n");
1448 smb2_lease_create(&io3, &ls3, false, fname3, LEASE1F3,
1449 smb2_util_lease_state("RHW"));
1450 status = smb2_create(tree1, mem_ctx, &io3);
1451 CHECK_STATUS(status, NT_STATUS_OK);
1452 h_client1_file3 = io3.out.file.handle;
1453 CHECK_CREATED(&io3, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1454 CHECK_LEASE(&io3, "RH", true, LEASE1F3, 0);
1455 CHECK_BREAK_INFO("RHW", "RH", LEASE2F3);
1456 CHECK_VAL(lease_break_info.count, 1);
1458 /* cleanup everything */
1459 torture_reset_lease_break_info(tctx, &lease_break_info);
1461 smb2_util_close(tree1, h_client1_file1);
1462 smb2_util_close(tree1, h_client1_file2);
1463 smb2_util_close(tree1, h_client1_file3);
1464 smb2_util_close(tree2A, h_client2_file1);
1465 smb2_util_close(tree2A, h_client2_file2);
1466 smb2_util_close(tree2A, h_client2_file3);
1468 smb2_util_unlink(tree1, fname1);
1469 smb2_util_unlink(tree1, fname2);
1470 smb2_util_unlink(tree1, fname3);
1471 CHECK_VAL(lease_break_info.count, 0);
1472 test_multichannel_free_channels(tree2A, tree2B, tree2C);
1473 tree2A = tree2B = tree2C = NULL;
1474 done:
1475 tree1->session = session1;
1477 smb2_util_close(tree1, h_client1_file1);
1478 smb2_util_close(tree1, h_client1_file2);
1479 smb2_util_close(tree1, h_client1_file3);
1480 if (tree2A != NULL) {
1481 smb2_util_close(tree2A, h_client2_file1);
1482 smb2_util_close(tree2A, h_client2_file2);
1483 smb2_util_close(tree2A, h_client2_file3);
1486 if (h != NULL) {
1487 smb2_util_close(tree1, *h);
1490 smb2_util_unlink(tree1, fname1);
1491 smb2_util_unlink(tree1, fname2);
1492 smb2_util_unlink(tree1, fname3);
1493 smb2_deltree(tree1, BASEDIR);
1495 test_multichannel_free_channels(tree2A, tree2B, tree2C);
1496 talloc_free(tree1);
1497 talloc_free(mem_ctx);
1499 return ret;
1503 * Lease Break Test 2:
1504 * Test for lease break retries being sent by the server.
1505 * Connect 2A, 2B
1506 * open file1 in session 2A
1507 * open file2 in session 2B
1508 * block 2A
1509 * open file2 in session 1
1510 * lease break retry reaches the client?
1511 * Connect 2C
1512 * open file3 in session 2C
1513 * unblock 2A
1514 * open file1 in session 1
1515 * lease break reaches the client?
1516 * open file3 in session 1
1517 * lease break reached the client?
1518 * Cleanup
1519 * On deletion by 1, lease breaks sent for file1, file2 and file3
1520 * on 2B
1521 * This changes RH lease to R for Session 2.
1522 * (This has been disabled while we add support for sending lease
1523 * break for handle leases.)
1525 static bool test_multichannel_lease_break_test2(struct torture_context *tctx,
1526 struct smb2_tree *tree1)
1528 const char *host = torture_setting_string(tctx, "host", NULL);
1529 const char *share = torture_setting_string(tctx, "share", NULL);
1530 struct cli_credentials *credentials = samba_cmdline_get_creds();
1531 NTSTATUS status;
1532 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1533 struct smb2_handle _h;
1534 struct smb2_handle *h = NULL;
1535 struct smb2_handle h_client1_file1 = {{0}};
1536 struct smb2_handle h_client1_file2 = {{0}};
1537 struct smb2_handle h_client1_file3 = {{0}};
1538 struct smb2_handle h_client2_file1 = {{0}};
1539 struct smb2_handle h_client2_file2 = {{0}};
1540 struct smb2_handle h_client2_file3 = {{0}};
1541 struct smb2_create io1, io2, io3;
1542 bool ret = true;
1543 const char *fname1 = BASEDIR "\\lease_break_test1.dat";
1544 const char *fname2 = BASEDIR "\\lease_break_test2.dat";
1545 const char *fname3 = BASEDIR "\\lease_break_test3.dat";
1546 struct smb2_tree *tree2A = NULL;
1547 struct smb2_tree *tree2B = NULL;
1548 struct smb2_tree *tree2C = NULL;
1549 struct smb2_transport *transport1 = tree1->session->transport;
1550 struct smb2_transport *transport2A = NULL;
1551 struct smbcli_options transport2_options;
1552 struct smb2_session *session1 = tree1->session;
1553 uint16_t local_port = 0;
1554 struct smb2_lease ls1;
1555 struct smb2_lease ls2;
1556 struct smb2_lease ls3;
1557 bool block_setup = false;
1558 bool block_ok = false;
1559 bool unblock_ok = false;
1562 if (!test_multichannel_initial_checks(tctx, tree1)) {
1563 return true;
1566 torture_comment(tctx, "Lease break retry: Test2\n");
1568 torture_reset_lease_break_info(tctx, &lease_break_info);
1570 transport1->lease.handler = torture_lease_handler;
1571 transport1->lease.private_data = tree1;
1572 torture_comment(tctx, "transport1 [%p]\n", transport1);
1573 local_port = torture_get_local_port_from_transport(transport1);
1574 torture_comment(tctx, "transport1 uses tcp port: %d\n", local_port);
1576 status = torture_smb2_testdir(tree1, BASEDIR, &_h);
1577 CHECK_STATUS(status, NT_STATUS_OK);
1578 smb2_util_close(tree1, _h);
1579 smb2_util_unlink(tree1, fname1);
1580 smb2_util_unlink(tree1, fname2);
1581 smb2_util_unlink(tree1, fname3);
1582 CHECK_VAL(lease_break_info.count, 0);
1584 smb2_lease_create(&io1, &ls1, false, fname1, LEASE2F1,
1585 smb2_util_lease_state("RHW"));
1586 test_multichannel_init_smb_create(&io1);
1588 smb2_lease_create(&io2, &ls2, false, fname2, LEASE2F2,
1589 smb2_util_lease_state("RHW"));
1590 test_multichannel_init_smb_create(&io2);
1592 smb2_lease_create(&io3, &ls3, false, fname3, LEASE2F3,
1593 smb2_util_lease_state("RHW"));
1594 test_multichannel_init_smb_create(&io3);
1596 transport2_options = transport1->options;
1598 ret = test_multichannel_create_channels(tctx, host, share,
1599 credentials,
1600 &transport2_options,
1601 &tree2A, &tree2B, NULL);
1602 torture_assert(tctx, ret, "Could not create channels.\n");
1603 transport2A = tree2A->session->transport;
1605 /* 2a opens file1 */
1606 torture_comment(tctx, "client2 opens fname1 via session 2A\n");
1607 smb2_lease_create(&io1, &ls1, false, fname1, LEASE2F1,
1608 smb2_util_lease_state("RHW"));
1609 status = smb2_create(tree2A, mem_ctx, &io1);
1610 CHECK_STATUS(status, NT_STATUS_OK);
1611 h_client2_file1 = io1.out.file.handle;
1612 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1613 CHECK_LEASE(&io1, "RHW", true, LEASE2F1, 0);
1614 CHECK_VAL(io1.out.durable_open_v2, false); //true);
1615 CHECK_VAL(io1.out.timeout, io1.in.timeout);
1616 CHECK_VAL(io1.out.durable_open, false);
1617 CHECK_VAL(lease_break_info.count, 0);
1619 /* 2b opens file2 */
1620 torture_comment(tctx, "client2 opens fname2 via session 2B\n");
1621 smb2_lease_create(&io2, &ls2, false, fname2, LEASE2F2,
1622 smb2_util_lease_state("RHW"));
1623 status = smb2_create(tree2B, mem_ctx, &io2);
1624 CHECK_STATUS(status, NT_STATUS_OK);
1625 h_client2_file2 = io2.out.file.handle;
1626 CHECK_CREATED(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1627 CHECK_LEASE(&io2, "RHW", true, LEASE2F2, 0);
1628 CHECK_VAL(io2.out.durable_open_v2, false); //true);
1629 CHECK_VAL(io2.out.timeout, io2.in.timeout);
1630 CHECK_VAL(io2.out.durable_open, false);
1631 CHECK_VAL(lease_break_info.count, 0);
1633 block_setup = test_setup_blocked_transports(tctx);
1634 torture_assert(tctx, block_setup, "test_setup_blocked_transports");
1636 torture_comment(tctx, "Blocking 2A\n");
1637 /* Block 2A */
1638 block_ok = test_block_smb2_transport(tctx, transport2A);
1639 torture_assert(tctx, block_ok, "we could not block tcp transport");
1641 torture_wait_for_lease_break(tctx);
1642 CHECK_VAL(lease_break_info.count, 0);
1644 /* 1 opens file2 */
1645 torture_comment(tctx,
1646 "Client opens fname2 with session1 with 2A blocked\n");
1647 smb2_lease_create(&io2, &ls2, false, fname2, LEASE1F2,
1648 smb2_util_lease_state("RHW"));
1649 status = smb2_create(tree1, mem_ctx, &io2);
1650 CHECK_STATUS(status, NT_STATUS_OK);
1651 h_client1_file2 = io2.out.file.handle;
1652 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1653 CHECK_LEASE(&io2, "RH", true, LEASE1F2, 0);
1654 CHECK_VAL(io2.out.durable_open_v2, false);
1655 CHECK_VAL(io2.out.timeout, 0);
1656 CHECK_VAL(io2.out.durable_open, false);
1658 if (lease_break_info.count == 0) {
1659 torture_comment(tctx,
1660 "Did not receive expected lease break!!\n");
1661 } else {
1662 torture_comment(tctx, "Received %d lease break(s)!!\n",
1663 lease_break_info.count);
1667 * We got breaks on both channels
1668 * (one failed on the blocked connection)
1670 CHECK_VAL(lease_break_info.count, 2);
1671 lease_break_info.count -= 1;
1672 CHECK_VAL(lease_break_info.failures, 1);
1673 lease_break_info.failures -= 1;
1674 CHECK_BREAK_INFO("RHW", "RH", LEASE2F2);
1675 torture_reset_lease_break_info(tctx, &lease_break_info);
1677 /* Connect 2C */
1678 torture_comment(tctx, "Connecting session 2C\n");
1679 talloc_free(tree2C);
1680 tree2C = test_multichannel_create_channel(tctx, host, share,
1681 credentials, &transport2_options, tree2A);
1682 if (!tree2C) {
1683 goto done;
1686 /* 2c opens file3 */
1687 torture_comment(tctx, "client2 opens fname3 via session 2C\n");
1688 smb2_lease_create(&io3, &ls3, false, fname3, LEASE2F3,
1689 smb2_util_lease_state("RHW"));
1690 status = smb2_create(tree2C, mem_ctx, &io3);
1691 CHECK_STATUS(status, NT_STATUS_OK);
1692 h_client2_file3 = io3.out.file.handle;
1693 CHECK_CREATED(&io3, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1694 CHECK_LEASE(&io3, "RHW", true, LEASE2F3, 0);
1695 CHECK_VAL(io3.out.durable_open_v2, false);
1696 CHECK_VAL(io3.out.timeout, io2.in.timeout);
1697 CHECK_VAL(io3.out.durable_open, false);
1698 CHECK_VAL(lease_break_info.count, 0);
1700 /* Unblock 2A */
1701 torture_comment(tctx, "Unblocking 2A\n");
1702 unblock_ok = test_unblock_smb2_transport(tctx, transport2A);
1703 torture_assert(tctx, unblock_ok, "we could not unblock tcp transport");
1705 /* 1 opens file1 */
1706 torture_comment(tctx, "Client opens fname1 with session 1\n");
1707 smb2_lease_create(&io1, &ls1, false, fname1, LEASE1F1,
1708 smb2_util_lease_state("RHW"));
1709 status = smb2_create(tree1, mem_ctx, &io1);
1710 CHECK_STATUS(status, NT_STATUS_OK);
1711 h_client1_file1 = io1.out.file.handle;
1712 CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1713 CHECK_LEASE(&io1, "RH", true, LEASE1F1, 0);
1715 if (lease_break_info.count == 0) {
1716 torture_comment(tctx,
1717 "Did not receive expected lease break!!\n");
1718 } else {
1719 torture_comment(tctx,
1720 "Received %d lease break(s)!!\n",
1721 lease_break_info.count);
1723 CHECK_VAL(lease_break_info.count, 1);
1724 CHECK_BREAK_INFO("RHW", "RH", LEASE2F1);
1725 torture_reset_lease_break_info(tctx, &lease_break_info);
1727 /*1 opens file3 */
1728 torture_comment(tctx, "client opens fname3 via session 1\n");
1730 smb2_lease_create(&io3, &ls3, false, fname3, LEASE1F3,
1731 smb2_util_lease_state("RHW"));
1732 status = smb2_create(tree1, mem_ctx, &io3);
1733 CHECK_STATUS(status, NT_STATUS_OK);
1734 h_client1_file3 = io3.out.file.handle;
1735 CHECK_CREATED(&io3, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1736 CHECK_LEASE(&io3, "RH", true, LEASE1F3, 0);
1738 if (lease_break_info.count == 0) {
1739 torture_comment(tctx,
1740 "Did not receive expected lease break!!\n");
1741 } else {
1742 torture_comment(tctx,
1743 "Received %d lease break(s)!!\n",
1744 lease_break_info.count);
1746 CHECK_VAL(lease_break_info.count, 1);
1747 CHECK_BREAK_INFO("RHW", "RH", LEASE2F3);
1748 torture_reset_lease_break_info(tctx, &lease_break_info);
1750 smb2_util_close(tree1, h_client1_file1);
1751 smb2_util_close(tree1, h_client1_file2);
1752 smb2_util_close(tree1, h_client1_file3);
1755 * Session 2 still has RW lease on file 1. Deletion of this file by 1
1756 * leads to a lease break call to session 2 file1
1758 smb2_util_unlink(tree1, fname1);
1760 * Bug - Samba does not revoke Handle lease on unlink
1761 * CHECK_BREAK_INFO("RH", "R", LEASE2F1);
1763 torture_reset_lease_break_info(tctx, &lease_break_info);
1766 * Session 2 still has RW lease on file 2. Deletion of this file by 1
1767 * leads to a lease break call to session 2 file2
1769 smb2_util_unlink(tree1, fname2);
1771 * Bug - Samba does not revoke Handle lease on unlink
1772 * CHECK_BREAK_INFO("RH", "R", LEASE2F2);
1774 torture_reset_lease_break_info(tctx, &lease_break_info);
1777 * Session 2 still has RW lease on file 3. Deletion of this file by 1
1778 * leads to a lease break call to session 2 file3
1780 smb2_util_unlink(tree1, fname3);
1782 * Bug - Samba does not revoke Handle lease on unlink
1783 * CHECK_BREAK_INFO("RH", "R", LEASE2F3);
1785 torture_reset_lease_break_info(tctx, &lease_break_info);
1787 smb2_util_close(tree2C, h_client2_file1);
1788 smb2_util_close(tree2C, h_client2_file2);
1789 smb2_util_close(tree2C, h_client2_file3);
1791 test_multichannel_free_channels(tree2A, tree2B, tree2C);
1792 tree2A = tree2B = tree2C = NULL;
1794 done:
1795 if (block_ok && !unblock_ok) {
1796 test_unblock_smb2_transport(tctx, transport2A);
1798 if (block_setup) {
1799 test_cleanup_blocked_transports(tctx);
1802 tree1->session = session1;
1804 smb2_util_close(tree1, h_client1_file1);
1805 smb2_util_close(tree1, h_client1_file2);
1806 smb2_util_close(tree1, h_client1_file3);
1807 if (tree2A != NULL) {
1808 smb2_util_close(tree2A, h_client2_file1);
1809 smb2_util_close(tree2A, h_client2_file2);
1810 smb2_util_close(tree2A, h_client2_file3);
1813 if (h != NULL) {
1814 smb2_util_close(tree1, *h);
1817 smb2_util_unlink(tree1, fname1);
1818 smb2_util_unlink(tree1, fname2);
1819 smb2_util_unlink(tree1, fname3);
1820 smb2_deltree(tree1, BASEDIR);
1822 test_multichannel_free_channels(tree2A, tree2B, tree2C);
1823 talloc_free(tree1);
1824 talloc_free(mem_ctx);
1826 return ret;
1830 * Test 3: Check to see how the server behaves if lease break
1831 * response is sent over a different channel to one over which
1832 * the break is received.
1833 * Connect 2A, 2B
1834 * open file1 in session 2A
1835 * open file1 in session 1
1836 * Lease break sent to 2A
1837 * 2B sends back lease break reply.
1838 * session 1 allowed to open file
1840 static bool test_multichannel_lease_break_test3(struct torture_context *tctx,
1841 struct smb2_tree *tree1)
1843 const char *host = torture_setting_string(tctx, "host", NULL);
1844 const char *share = torture_setting_string(tctx, "share", NULL);
1845 struct cli_credentials *credentials = samba_cmdline_get_creds();
1846 NTSTATUS status;
1847 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1848 struct smb2_handle _h;
1849 struct smb2_handle *h = NULL;
1850 struct smb2_handle h_client1_file1 = {{0}};
1851 struct smb2_handle h_client2_file1 = {{0}};
1852 struct smb2_create io1;
1853 bool ret = true;
1854 const char *fname1 = BASEDIR "\\lease_break_test1.dat";
1855 struct smb2_tree *tree2A = NULL;
1856 struct smb2_tree *tree2B = NULL;
1857 struct smb2_transport *transport1 = tree1->session->transport;
1858 struct smb2_transport *transport2A = NULL;
1859 struct smbcli_options transport2_options;
1860 uint16_t local_port = 0;
1861 struct smb2_lease ls1;
1862 struct tevent_timer *te = NULL;
1863 struct timeval ne;
1864 bool timesup = false;
1866 if (!test_multichannel_initial_checks(tctx, tree1)) {
1867 return true;
1870 torture_comment(tctx, "Lease break retry: Test3\n");
1872 torture_reset_lease_break_info(tctx, &lease_break_info);
1874 transport1->lease.handler = torture_lease_handler;
1875 transport1->lease.private_data = tree1;
1876 torture_comment(tctx, "transport1 [%p]\n", transport1);
1877 local_port = torture_get_local_port_from_transport(transport1);
1878 torture_comment(tctx, "transport1 uses tcp port: %d\n", local_port);
1880 status = torture_smb2_testdir(tree1, BASEDIR, &_h);
1881 CHECK_STATUS(status, NT_STATUS_OK);
1882 smb2_util_close(tree1, _h);
1883 smb2_util_unlink(tree1, fname1);
1884 CHECK_VAL(lease_break_info.count, 0);
1886 smb2_lease_create(&io1, &ls1, false, fname1, LEASE2F1,
1887 smb2_util_lease_state("RHW"));
1888 test_multichannel_init_smb_create(&io1);
1890 transport2_options = transport1->options;
1892 ret = test_multichannel_create_channels(tctx, host, share,
1893 credentials,
1894 &transport2_options,
1895 &tree2A, &tree2B, NULL);
1896 torture_assert(tctx, ret, "Could not create channels.\n");
1897 transport2A = tree2A->session->transport;
1898 transport2A->lease.private_data = tree2B;
1900 /* 2a opens file1 */
1901 torture_comment(tctx, "client2 opens fname1 via session 2A\n");
1902 smb2_lease_create(&io1, &ls1, false, fname1, LEASE2F1,
1903 smb2_util_lease_state("RHW"));
1904 status = smb2_create(tree2A, mem_ctx, &io1);
1905 CHECK_STATUS(status, NT_STATUS_OK);
1906 h_client2_file1 = io1.out.file.handle;
1907 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1908 CHECK_LEASE(&io1, "RHW", true, LEASE2F1, 0);
1909 CHECK_VAL(io1.out.durable_open_v2, false); //true);
1910 CHECK_VAL(io1.out.timeout, io1.in.timeout);
1911 CHECK_VAL(io1.out.durable_open, false);
1912 CHECK_VAL(lease_break_info.count, 0);
1914 /* Set a timeout for 5 seconds for session 1 to open file1 */
1915 ne = tevent_timeval_current_ofs(0, 5000000);
1916 te = tevent_add_timer(tctx->ev, mem_ctx, ne, timeout_cb, &timesup);
1917 if (te == NULL) {
1918 torture_comment(tctx, "Failed to add timer.");
1919 goto done;
1922 /* 1 opens file2 */
1923 torture_comment(tctx, "Client opens fname1 with session 1\n");
1924 smb2_lease_create(&io1, &ls1, false, fname1, LEASE1F1,
1925 smb2_util_lease_state("RHW"));
1926 status = smb2_create(tree1, mem_ctx, &io1);
1927 CHECK_STATUS(status, NT_STATUS_OK);
1928 h_client1_file1 = io1.out.file.handle;
1929 CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1930 CHECK_LEASE(&io1, "RH", true, LEASE1F1, 0);
1931 CHECK_VAL(io1.out.durable_open_v2, false);
1932 CHECK_VAL(io1.out.timeout, 0);
1933 CHECK_VAL(io1.out.durable_open, false);
1935 CHECK_VAL(lease_break_info.count, 1);
1936 CHECK_BREAK_INFO("RHW", "RH", LEASE2F1);
1939 * Check if timeout handler was fired. This would indicate
1940 * that the server didn't receive a reply for the oplock break
1941 * from the client and the server let session 1 open the file
1942 * only after the oplock break timeout.
1944 CHECK_VAL(timesup, false);
1946 done:
1947 smb2_util_close(tree1, h_client1_file1);
1948 if (tree2A != NULL) {
1949 smb2_util_close(tree2A, h_client2_file1);
1952 if (h != NULL) {
1953 smb2_util_close(tree1, *h);
1956 smb2_util_unlink(tree1, fname1);
1957 smb2_deltree(tree1, BASEDIR);
1959 test_multichannel_free_channels(tree2A, tree2B, NULL);
1960 talloc_free(tree1);
1961 talloc_free(mem_ctx);
1963 return ret;
1967 * Test limits of channels
1969 static bool test_multichannel_num_channels(struct torture_context *tctx,
1970 struct smb2_tree *tree1)
1972 const char *host = torture_setting_string(tctx, "host", NULL);
1973 const char *share = torture_setting_string(tctx, "share", NULL);
1974 struct cli_credentials *credentials = samba_cmdline_get_creds();
1975 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1976 bool ret = true;
1977 struct smb2_tree **tree2 = NULL;
1978 struct smb2_transport *transport1 = tree1->session->transport;
1979 struct smb2_transport **transport2 = NULL;
1980 struct smbcli_options transport2_options;
1981 struct smb2_session **session2 = NULL;
1982 uint32_t server_capabilities;
1983 int i;
1984 int max_channels = 33; /* 32 is the W2K12R2 and W2K16 limit */
1986 if (smbXcli_conn_protocol(transport1->conn) < PROTOCOL_SMB3_00) {
1987 torture_fail(tctx,
1988 "SMB 3.X Dialect family required for Multichannel"
1989 " tests\n");
1992 server_capabilities = smb2cli_conn_server_capabilities(
1993 tree1->session->transport->conn);
1994 if (!(server_capabilities & SMB2_CAP_MULTI_CHANNEL)) {
1995 torture_fail(tctx,
1996 "Server does not support multichannel.");
1999 torture_comment(tctx, "Testing max. number of channels\n");
2001 transport2_options = transport1->options;
2002 transport2_options.client_guid = GUID_random();
2004 tree2 = talloc_zero_array(mem_ctx, struct smb2_tree *,
2005 max_channels);
2006 transport2 = talloc_zero_array(mem_ctx, struct smb2_transport *,
2007 max_channels);
2008 session2 = talloc_zero_array(mem_ctx, struct smb2_session *,
2009 max_channels);
2010 if (tree2 == NULL || transport2 == NULL || session2 == NULL) {
2011 torture_fail(tctx, "out of memory");
2014 for (i = 0; i < max_channels; i++) {
2016 NTSTATUS expected_status;
2018 torture_assert_ntstatus_ok_goto(tctx,
2019 smb2_connect(tctx,
2020 host,
2021 lpcfg_smb_ports(tctx->lp_ctx),
2022 share,
2023 lpcfg_resolve_context(tctx->lp_ctx),
2024 credentials,
2025 &tree2[i],
2026 tctx->ev,
2027 &transport2_options,
2028 lpcfg_socket_options(tctx->lp_ctx),
2029 lpcfg_gensec_settings(tctx, tctx->lp_ctx)
2031 ret, done, "smb2_connect failed");
2033 transport2[i] = tree2[i]->session->transport;
2035 if (i == 0) {
2037 * done for the 1st channel
2039 * For all remaining channels we do the
2040 * session setup on our own.
2042 transport2_options.only_negprot = true;
2043 continue;
2047 * Now bind the session2[i] to the transport2
2049 session2[i] = smb2_session_channel(transport2[i],
2050 lpcfg_gensec_settings(tctx,
2051 tctx->lp_ctx),
2052 tree2[0],
2053 tree2[0]->session);
2055 torture_assert(tctx, session2[i] != NULL,
2056 "smb2_session_channel failed");
2058 torture_comment(tctx, "established transport2 [#%d]\n", i);
2060 if (i >= 32) {
2061 expected_status = NT_STATUS_INSUFFICIENT_RESOURCES;
2062 } else {
2063 expected_status = NT_STATUS_OK;
2066 torture_assert_ntstatus_equal_goto(tctx,
2067 smb2_session_setup_spnego(session2[i],
2068 samba_cmdline_get_creds(),
2069 0 /* previous_session_id */),
2070 expected_status,
2071 ret, done,
2072 talloc_asprintf(tctx, "failed to establish session "
2073 "setup for channel #%d", i));
2075 torture_comment(tctx, "bound session2 [#%d] to session2 [0]\n",
2079 done:
2080 talloc_free(mem_ctx);
2082 return ret;
2085 struct test_multichannel_lease_break_state;
2087 struct test_multichannel_lease_break_channel {
2088 struct test_multichannel_lease_break_state *state;
2089 size_t idx;
2090 char name[64];
2091 struct smb2_tree *tree;
2092 bool blocked;
2093 struct timeval break_time;
2094 double full_duration;
2095 double relative_duration;
2096 struct smb2_lease_break lb;
2097 size_t break_num;
2100 struct test_multichannel_lease_break_state {
2101 struct torture_context *tctx;
2102 struct timeval open_req_time;
2103 struct timeval open_rep_time;
2104 size_t num_breaks;
2105 struct timeval last_break_time;
2106 struct test_multichannel_lease_break_channel channels[32];
2109 static bool test_multichannel_lease_break_handler(struct smb2_transport *transport,
2110 const struct smb2_lease_break *lb,
2111 void *private_data)
2113 struct test_multichannel_lease_break_channel *c =
2114 (struct test_multichannel_lease_break_channel *)private_data;
2115 struct test_multichannel_lease_break_state *state = c->state;
2117 c->break_time = timeval_current();
2118 c->full_duration = timeval_elapsed2(&state->open_req_time,
2119 &c->break_time);
2120 c->relative_duration = timeval_elapsed2(&state->last_break_time,
2121 &c->break_time);
2122 state->last_break_time = c->break_time;
2123 c->lb = *lb;
2124 c->break_num = ++state->num_breaks;
2126 torture_comment(state->tctx, "Got LEASE break epoch[0x%x] %zu on %s after %f ( %f)\n",
2127 c->lb.new_epoch, c->break_num, c->name,
2128 c->relative_duration,
2129 c->full_duration);
2131 return torture_lease_handler(transport, lb, c->tree);
2134 static bool test_multichannel_lease_break_test4(struct torture_context *tctx,
2135 struct smb2_tree *tree1)
2137 const char *host = torture_setting_string(tctx, "host", NULL);
2138 const char *share = torture_setting_string(tctx, "share", NULL);
2139 struct cli_credentials *credentials = samba_cmdline_get_creds();
2140 NTSTATUS status;
2141 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2142 struct test_multichannel_lease_break_state state = {
2143 .tctx = tctx,
2145 struct test_multichannel_lease_break_channel *open2_channel = NULL;
2146 struct smb2_handle _h;
2147 struct smb2_handle *h = NULL;
2148 struct smb2_handle h_client1_file1 = {{0}};
2149 struct smb2_handle h_client2_file1 = {{0}};
2150 struct smb2_create io1;
2151 struct smb2_create io2;
2152 bool ret = true;
2153 const char *fname1 = BASEDIR "\\lease_break_test4.dat";
2154 struct smb2_tree *trees2[32] = { NULL, };
2155 size_t i;
2156 struct smb2_transport *transport1 = tree1->session->transport;
2157 struct smbcli_options transport2_options;
2158 struct smb2_session *session1 = tree1->session;
2159 uint16_t local_port = 0;
2160 struct smb2_lease ls1;
2161 struct smb2_lease ls2;
2162 bool block_setup = false;
2163 bool block_ok = false;
2164 double open_duration;
2166 if (!test_multichannel_initial_checks(tctx, tree1)) {
2167 return true;
2170 torture_comment(tctx, "Lease break retry: Test4\n");
2172 torture_reset_lease_break_info(tctx, &lease_break_info);
2173 lease_break_info.lease_skip_ack = true;
2175 transport1->lease.handler = torture_lease_handler;
2176 transport1->lease.private_data = tree1;
2177 torture_comment(tctx, "transport1 [%p]\n", transport1);
2178 local_port = torture_get_local_port_from_transport(transport1);
2179 torture_comment(tctx, "transport1 uses tcp port: %d\n", local_port);
2181 status = torture_smb2_testdir(tree1, BASEDIR, &_h);
2182 CHECK_STATUS(status, NT_STATUS_OK);
2183 smb2_util_close(tree1, _h);
2184 smb2_util_unlink(tree1, fname1);
2185 CHECK_VAL(lease_break_info.count, 0);
2187 smb2_lease_v2_create(&io2, &ls2, false, fname1,
2188 LEASE2F1, NULL,
2189 smb2_util_lease_state("RHW"),
2190 0x20);
2192 transport2_options = transport1->options;
2194 ret = test_multichannel_create_channel_array(tctx, host, share, credentials,
2195 &transport2_options,
2196 ARRAY_SIZE(trees2), trees2);
2197 torture_assert(tctx, ret, "Could not create channels.\n");
2199 for (i = 0; i < ARRAY_SIZE(trees2); i++) {
2200 struct test_multichannel_lease_break_channel *c = &state.channels[i];
2201 struct smb2_transport *t = trees2[i]->session->transport;
2203 c->state = &state;
2204 c->idx = i+1;
2205 c->tree = trees2[i];
2206 snprintf(c->name, sizeof(c->name), "trees2_%zu", c->idx);
2208 t->lease.handler = test_multichannel_lease_break_handler;
2209 t->lease.private_data = c;
2212 open2_channel = &state.channels[0];
2214 /* 2a opens file1 */
2215 torture_comment(tctx, "client2 opens fname1 via %s\n",
2216 open2_channel->name);
2217 status = smb2_create(open2_channel->tree, mem_ctx, &io2);
2218 CHECK_STATUS(status, NT_STATUS_OK);
2219 h_client2_file1 = io2.out.file.handle;
2220 CHECK_CREATED(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2221 CHECK_LEASE_V2(&io2, "RHW", true, LEASE2F1, 0, 0, 0x21);
2222 CHECK_VAL(io2.out.durable_open_v2, false);
2223 CHECK_VAL(io2.out.timeout, io2.in.timeout);
2224 CHECK_VAL(io2.out.durable_open, false);
2225 CHECK_VAL(lease_break_info.count, 0);
2227 block_setup = test_setup_blocked_transports(tctx);
2228 torture_assert(tctx, block_setup, "test_setup_blocked_transports");
2230 for (i = 0; i < ARRAY_SIZE(state.channels); i++) {
2231 struct test_multichannel_lease_break_channel *c = &state.channels[i];
2232 struct smb2_transport *t = c->tree->session->transport;
2234 torture_comment(tctx, "Blocking %s\n", c->name);
2235 block_ok = _test_block_smb2_transport(tctx, t, c->name);
2236 torture_assert_goto(tctx, block_ok, ret, done, "we could not block tcp transport");
2237 c->blocked = true;
2240 /* 1 opens file2 */
2241 torture_comment(tctx,
2242 "Client opens fname1 with session 1 with all %zu blocked\n",
2243 ARRAY_SIZE(trees2));
2244 smb2_lease_v2_create(&io1, &ls1, false, fname1,
2245 LEASE1F1, NULL,
2246 smb2_util_lease_state("RHW"),
2247 0x10);
2248 CHECK_VAL(lease_break_info.count, 0);
2249 state.open_req_time = timeval_current();
2250 state.last_break_time = state.open_req_time;
2251 status = smb2_create(tree1, mem_ctx, &io1);
2252 state.open_rep_time = timeval_current();
2253 CHECK_STATUS(status, NT_STATUS_OK);
2254 h_client1_file1 = io1.out.file.handle;
2256 CHECK_VAL_GREATER_THAN(lease_break_info.count, 1);
2258 open_duration = timeval_elapsed2(&state.open_req_time,
2259 &state.open_rep_time);
2260 torture_comment(tctx, "open_duration: %f\n", open_duration);
2261 if (lease_break_info.count < ARRAY_SIZE(state.channels)) {
2262 CHECK_VAL_GREATER_THAN(open_duration, 35);
2263 CHECK_LEASE_V2(&io1, "RH", true, LEASE1F1, 0, 0, 0x11);
2264 } else {
2265 CHECK_LEASE_V2(&io1, "RWH", true, LEASE1F1, 0, 0, 0x11);
2268 for (i = 0; i < ARRAY_SIZE(state.channels); i++) {
2269 if (lease_break_info.count >= ARRAY_SIZE(state.channels)) {
2270 break;
2272 torture_comment(tctx, "Received %d lease break(s) wait for more!!\n",
2273 lease_break_info.count);
2274 torture_wait_for_lease_break(tctx);
2277 if (lease_break_info.count == 0) {
2278 torture_comment(tctx,
2279 "Did not receive expected lease break!!\n");
2280 } else {
2281 torture_comment(tctx, "Received %d lease break(s)!!\n",
2282 lease_break_info.count);
2285 if (lease_break_info.count < ARRAY_SIZE(state.channels)) {
2286 CHECK_VAL_GREATER_THAN(lease_break_info.count, 3);
2287 } else {
2288 CHECK_VAL(lease_break_info.count, ARRAY_SIZE(state.channels));
2291 for (i = 0; i < lease_break_info.count; i++) {
2292 struct test_multichannel_lease_break_channel *c = &state.channels[i];
2294 torture_comment(tctx, "Verify %s\n", c->name);
2295 torture_assert_int_equal(tctx, c->break_num, c->idx,
2296 "Got lease break in wrong order");
2297 CHECK_LEASE_BREAK_V2(c->lb, LEASE2F1, "RWH", "RH",
2298 SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED,
2299 0x22);
2302 done:
2303 for (i = 0; i < ARRAY_SIZE(state.channels); i++) {
2304 struct test_multichannel_lease_break_channel *c = &state.channels[i];
2305 struct smb2_transport *t = NULL;
2307 if (!c->blocked) {
2308 continue;
2311 t = c->tree->session->transport;
2313 torture_comment(tctx, "Unblocking %s\n", c->name);
2314 _test_unblock_smb2_transport(tctx, t, c->name);
2315 c->blocked = false;
2317 if (block_setup) {
2318 test_cleanup_blocked_transports(tctx);
2321 tree1->session = session1;
2323 smb2_util_close(tree1, h_client1_file1);
2324 if (trees2[0] != NULL) {
2325 smb2_util_close(trees2[0], h_client2_file1);
2328 if (h != NULL) {
2329 smb2_util_close(tree1, *h);
2332 smb2_util_unlink(tree1, fname1);
2333 smb2_deltree(tree1, BASEDIR);
2335 for (i = 0; i < ARRAY_SIZE(trees2); i++) {
2336 if (trees2[i] == NULL) {
2337 continue;
2339 TALLOC_FREE(trees2[i]);
2341 talloc_free(tree1);
2342 talloc_free(mem_ctx);
2344 return ret;
2348 * Test channel merging race
2349 * This is a regression test for
2350 * https://bugzilla.samba.org/show_bug.cgi?id=15346
2352 struct test_multichannel_bug_15346_conn;
2354 struct test_multichannel_bug_15346_state {
2355 struct torture_context *tctx;
2356 struct test_multichannel_bug_15346_conn *conns;
2357 size_t num_conns;
2358 size_t num_ready;
2359 bool asserted;
2360 bool looping;
2363 struct test_multichannel_bug_15346_conn {
2364 struct test_multichannel_bug_15346_state *state;
2365 size_t idx;
2366 struct smbXcli_conn *smbXcli;
2367 struct tevent_req *nreq;
2368 struct tevent_req *ereq;
2371 static void test_multichannel_bug_15346_ndone(struct tevent_req *subreq);
2372 static void test_multichannel_bug_15346_edone(struct tevent_req *subreq);
2374 static void test_multichannel_bug_15346_ndone(struct tevent_req *subreq)
2376 struct test_multichannel_bug_15346_conn *conn =
2377 (struct test_multichannel_bug_15346_conn *)
2378 tevent_req_callback_data_void(subreq);
2379 struct test_multichannel_bug_15346_state *state = conn->state;
2380 struct torture_context *tctx = state->tctx;
2381 struct timeval current_time;
2382 struct tm tm_buf;
2383 struct tm *current_tm = NULL;
2384 char time_str[sizeof "10000-01-01T00:00:00"];
2385 size_t time_str_len;
2386 NTSTATUS status;
2387 bool ok = false;
2389 SMB_ASSERT(conn->nreq == subreq);
2390 conn->nreq = NULL;
2392 status = smbXcli_negprot_recv(subreq, NULL, NULL);
2393 TALLOC_FREE(subreq);
2394 torture_assert_ntstatus_ok_goto(tctx, status, ok, asserted,
2395 "smbXcli_negprot_recv failed");
2397 current_time = tevent_timeval_current();
2398 current_tm = gmtime_r(&current_time.tv_sec, &tm_buf);
2399 torture_assert_not_null_goto(tctx, current_tm, ok, asserted,
2400 "gmtime_r failed");
2402 time_str_len = strftime(time_str, sizeof time_str, "%FT%T", current_tm);
2403 torture_assert_size_not_equal_goto(tctx, time_str_len, 0, ok, asserted,
2404 "strftime failed");
2406 torture_comment(tctx,
2407 "%s.%ldZ: conn[%zu]: negprot done\n",
2408 time_str,
2409 (long)current_time.tv_usec,
2410 conn->idx);
2412 conn->ereq = smb2cli_echo_send(conn->smbXcli,
2413 tctx->ev,
2414 conn->smbXcli,
2415 state->num_conns * 2 * 1000);
2416 torture_assert_goto(tctx, conn->ereq != NULL, ok, asserted,
2417 "smb2cli_echo_send");
2418 tevent_req_set_callback(conn->ereq,
2419 test_multichannel_bug_15346_edone,
2420 conn);
2422 return;
2424 asserted:
2425 SMB_ASSERT(!ok);
2426 state->asserted = true;
2427 state->looping = false;
2428 return;
2431 static void test_multichannel_bug_15346_edone(struct tevent_req *subreq)
2433 struct test_multichannel_bug_15346_conn *conn =
2434 (struct test_multichannel_bug_15346_conn *)
2435 tevent_req_callback_data_void(subreq);
2436 struct test_multichannel_bug_15346_state *state = conn->state;
2437 struct torture_context *tctx = state->tctx;
2438 struct timeval current_time;
2439 struct tm tm_buf;
2440 struct tm *current_tm = NULL;
2441 char time_str[sizeof "10000-01-01T00:00:00"];
2442 size_t time_str_len;
2443 const char *outcome = NULL;
2444 NTSTATUS status;
2445 bool ok = false;
2447 SMB_ASSERT(conn->ereq == subreq);
2448 conn->ereq = NULL;
2450 current_time = tevent_timeval_current();
2451 current_tm = gmtime_r(&current_time.tv_sec, &tm_buf);
2452 torture_assert_not_null_goto(tctx, current_tm, ok, asserted,
2453 "gmtime_r failed");
2455 time_str_len = strftime(time_str, sizeof time_str, "%FT%T", current_tm);
2456 torture_assert_size_not_equal_goto(tctx, time_str_len, 0, ok, asserted,
2457 "strftime failed");
2459 status = smb2cli_echo_recv(subreq);
2460 TALLOC_FREE(subreq);
2461 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
2462 outcome = "timed out";
2463 } else if (!NT_STATUS_IS_OK(status)) {
2464 outcome = "failed";
2465 } else {
2466 outcome = "done";
2468 torture_comment(tctx,
2469 "%s.%ldZ: conn[%zu]: echo %s\n",
2470 time_str,
2471 (long)current_time.tv_usec,
2472 conn->idx,
2473 outcome);
2474 torture_assert_ntstatus_ok_goto(tctx, status, ok, asserted,
2475 "smb2cli_echo_recv failed");
2477 state->num_ready += 1;
2478 if (state->num_ready < state->num_conns) {
2479 return;
2482 state->looping = false;
2483 return;
2485 asserted:
2486 SMB_ASSERT(!ok);
2487 state->asserted = true;
2488 state->looping = false;
2489 return;
2492 static bool test_multichannel_bug_15346(struct torture_context *tctx,
2493 struct smb2_tree *tree1)
2495 const char *host = torture_setting_string(tctx, "host", NULL);
2496 const char *share = torture_setting_string(tctx, "share", NULL);
2497 struct resolve_context *resolve_ctx = lpcfg_resolve_context(tctx->lp_ctx);
2498 const char *socket_options = lpcfg_socket_options(tctx->lp_ctx);
2499 struct gensec_settings *gsettings = NULL;
2500 bool ret = true;
2501 NTSTATUS status;
2502 struct smb2_transport *transport1 = tree1->session->transport;
2503 struct test_multichannel_bug_15346_state *state = NULL;
2504 uint32_t server_capabilities;
2505 struct smb2_handle root_handle = {{0}};
2506 size_t i;
2508 if (smbXcli_conn_protocol(transport1->conn) < PROTOCOL_SMB3_00) {
2509 torture_fail(tctx,
2510 "SMB 3.X Dialect family required for Multichannel"
2511 " tests\n");
2514 server_capabilities = smb2cli_conn_server_capabilities(
2515 tree1->session->transport->conn);
2516 if (!(server_capabilities & SMB2_CAP_MULTI_CHANNEL)) {
2517 torture_fail(tctx,
2518 "Server does not support multichannel.");
2521 torture_comment(tctx, "Testing for BUG 15346\n");
2523 state = talloc_zero(tctx, struct test_multichannel_bug_15346_state);
2524 torture_assert_goto(tctx, state != NULL, ret, done,
2525 "talloc_zero");
2526 state->tctx = tctx;
2528 gsettings = lpcfg_gensec_settings(state, tctx->lp_ctx);
2529 torture_assert_goto(tctx, gsettings != NULL, ret, done,
2530 "lpcfg_gensec_settings");
2533 * 32 is the W2K12R2 and W2K16 limit
2534 * add 31 additional connections
2536 state->num_conns = 31;
2537 state->conns = talloc_zero_array(state,
2538 struct test_multichannel_bug_15346_conn,
2539 state->num_conns);
2540 torture_assert_goto(tctx, state->conns != NULL, ret, done,
2541 "talloc_zero_array");
2544 * First we open the additional tcp connections
2547 for (i = 0; i < state->num_conns; i++) {
2548 struct test_multichannel_bug_15346_conn *conn = &state->conns[i];
2549 struct socket_context *sock = NULL;
2550 uint16_t port = 445;
2551 struct smbcli_options options = transport1->options;
2553 conn->state = state;
2554 conn->idx = i;
2556 status = socket_connect_multi(state->conns,
2557 host,
2558 1, &port,
2559 resolve_ctx,
2560 tctx->ev,
2561 &sock,
2562 &port);
2563 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2564 "socket_connect_multi failed");
2566 conn->smbXcli = smbXcli_conn_create(state->conns,
2567 sock->fd,
2568 host,
2569 SMB_SIGNING_OFF,
2571 &options.client_guid,
2572 options.smb2_capabilities,
2573 &options.smb3_capabilities);
2574 torture_assert_goto(tctx, conn->smbXcli != NULL, ret, done,
2575 "smbXcli_conn_create failed");
2576 sock->fd = -1;
2577 TALLOC_FREE(sock);
2581 * Now prepare the async SMB2 Negotiate requests
2583 for (i = 0; i < state->num_conns; i++) {
2584 struct test_multichannel_bug_15346_conn *conn = &state->conns[i];
2586 conn->nreq = smbXcli_negprot_send(conn->smbXcli,
2587 tctx->ev,
2588 conn->smbXcli,
2589 state->num_conns * 2 * 1000,
2590 smbXcli_conn_protocol(transport1->conn),
2591 smbXcli_conn_protocol(transport1->conn),
2592 33, /* max_credits */
2593 NULL);
2594 torture_assert_goto(tctx, conn->nreq != NULL, ret, done, "smbXcli_negprot_send");
2595 tevent_req_set_callback(conn->nreq,
2596 test_multichannel_bug_15346_ndone,
2597 conn);
2601 * now we loop until all negprot and the first round
2602 * of echos are done.
2604 state->looping = true;
2605 while (state->looping) {
2606 torture_assert_goto(tctx, tevent_loop_once(tctx->ev) == 0,
2607 ret, done, "tevent_loop_once");
2610 if (state->asserted) {
2611 ret = false;
2612 goto done;
2616 * Now we check that the connections are still usable
2618 for (i = 0; i < state->num_conns; i++) {
2619 struct test_multichannel_bug_15346_conn *conn = &state->conns[i];
2621 torture_comment(tctx, "conn[%zu]: checking echo again1\n", conn->idx);
2623 status = smb2cli_echo(conn->smbXcli, 1000);
2624 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2625 "smb2cli_echo failed");
2628 status = smb2_util_roothandle(tree1, &root_handle);
2629 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2630 "smb2_util_roothandle failed");
2633 * Now we check that the connections are still usable
2635 for (i = 0; i < state->num_conns; i++) {
2636 struct test_multichannel_bug_15346_conn *conn = &state->conns[i];
2637 struct smbcli_options options = transport1->options;
2638 struct smb2_session *session = NULL;
2639 struct smb2_tree *tree = NULL;
2640 union smb_fileinfo io;
2642 torture_comment(tctx, "conn[%zu]: checking session bind\n", conn->idx);
2645 * Prepare smb2_{tree,session,transport} structures
2646 * for the existing connection.
2648 options.only_negprot = true;
2649 status = smb2_connect_ext(state->conns,
2650 host,
2651 NULL, /* ports */
2652 share,
2653 resolve_ctx,
2654 samba_cmdline_get_creds(),
2655 &conn->smbXcli,
2656 0, /* previous_session_id */
2657 &tree,
2658 tctx->ev,
2659 &options,
2660 socket_options,
2661 gsettings);
2662 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2663 "smb2_connect_ext failed");
2664 conn->smbXcli = tree->session->transport->conn;
2666 session = smb2_session_channel(tree->session->transport,
2667 lpcfg_gensec_settings(tree, tctx->lp_ctx),
2668 tree,
2669 tree1->session);
2670 torture_assert_goto(tctx, session != NULL, ret, done,
2671 "smb2_session_channel failed");
2673 status = smb2_session_setup_spnego(session,
2674 samba_cmdline_get_creds(),
2675 0 /* previous_session_id */);
2676 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2677 "smb2_session_setup_spnego failed");
2680 * Fix up the bound smb2_tree
2682 tree->session = session;
2683 tree->smbXcli = tree1->smbXcli;
2685 ZERO_STRUCT(io);
2686 io.generic.level = RAW_FILEINFO_SMB2_ALL_INFORMATION;
2687 io.generic.in.file.handle = root_handle;
2689 status = smb2_getinfo_file(tree, tree, &io);
2690 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
2691 "smb2_getinfo_file failed");
2694 done:
2695 talloc_free(state);
2697 return ret;
2700 struct torture_suite *torture_smb2_multichannel_init(TALLOC_CTX *ctx)
2702 struct torture_suite *suite = torture_suite_create(ctx, "multichannel");
2703 struct torture_suite *suite_generic = torture_suite_create(ctx,
2704 "generic");
2705 struct torture_suite *suite_oplocks = torture_suite_create(ctx,
2706 "oplocks");
2707 struct torture_suite *suite_leases = torture_suite_create(ctx,
2708 "leases");
2709 struct torture_suite *suite_bugs = torture_suite_create(ctx,
2710 "bugs");
2712 torture_suite_add_suite(suite, suite_generic);
2713 torture_suite_add_suite(suite, suite_oplocks);
2714 torture_suite_add_suite(suite, suite_leases);
2715 torture_suite_add_suite(suite, suite_bugs);
2717 torture_suite_add_1smb2_test(suite_generic, "interface_info",
2718 test_multichannel_interface_info);
2719 torture_suite_add_1smb2_test(suite_generic, "num_channels",
2720 test_multichannel_num_channels);
2721 torture_suite_add_1smb2_test(suite_oplocks, "test1",
2722 test_multichannel_oplock_break_test1);
2723 torture_suite_add_1smb2_test(suite_oplocks, "test2",
2724 test_multichannel_oplock_break_test2);
2725 torture_suite_add_1smb2_test(suite_oplocks, "test3_windows",
2726 test_multichannel_oplock_break_test3_windows);
2727 torture_suite_add_1smb2_test(suite_oplocks, "test3_specification",
2728 test_multichannel_oplock_break_test3_specification);
2729 torture_suite_add_1smb2_test(suite_leases, "test1",
2730 test_multichannel_lease_break_test1);
2731 torture_suite_add_1smb2_test(suite_leases, "test2",
2732 test_multichannel_lease_break_test2);
2733 torture_suite_add_1smb2_test(suite_leases, "test3",
2734 test_multichannel_lease_break_test3);
2735 torture_suite_add_1smb2_test(suite_leases, "test4",
2736 test_multichannel_lease_break_test4);
2737 torture_suite_add_1smb2_test(suite_bugs, "bug_15346",
2738 test_multichannel_bug_15346);
2740 suite->description = talloc_strdup(suite, "SMB2 Multichannel tests");
2742 return suite;