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/>.
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,\
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); \
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); \
66 #define CHECK_CREATED(__io, __created, __attribute) \
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); \
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); \
84 #define CHECK_LEASE(__io, __state, __oplevel, __key, __flags) \
86 CHECK_VAL((__io)->out.lease_response.lease_version, 1); \
88 CHECK_VAL((__io)->out.oplock_level, \
89 SMB2_OPLOCK_LEVEL_LEASE); \
90 CHECK_VAL((__io)->out.lease_response.lease_key.data[0],\
92 CHECK_VAL((__io)->out.lease_response.lease_key.data[1],\
94 CHECK_VAL((__io)->out.lease_response.lease_state,\
95 smb2_util_lease_state(__state)); \
97 CHECK_VAL((__io)->out.oplock_level,\
98 SMB2_OPLOCK_LEVEL_NONE); \
99 CHECK_VAL((__io)->out.lease_response.lease_key.data[0],\
101 CHECK_VAL((__io)->out.lease_response.lease_key.data[1],\
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); \
111 #define CHECK_LEASE_V2(__io, __state, __oplevel, __key, __flags, __parent, __epoch) \
113 CHECK_VAL((__io)->out.lease_response_v2.lease_version, 2); \
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)); \
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)); \
135 #define CHECK_LEASE_BREAK_V2(__lb, __key, __from, __to, __break_flags, __new_epoch) \
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)); \
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
;
153 caps
= smb2cli_conn_server_capabilities(tree
->session
->transport
->conn
);
154 if (!(caps
& SMB2_CAP_MULTI_CHANNEL
)) {
156 "server doesn't support SMB2_CAP_MULTI_CHANNEL\n");
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");
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");
186 NDR_PRINT_DEBUG(fsctl_net_iface_info
, info
);
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
,
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
;
211 struct smb2_transport
*transport
;
212 struct smb2_session
*session
;
214 struct smb2_tree
*tree
;
217 transport_options
.only_negprot
= true;
220 status
= smb2_connect(tctx
,
222 lpcfg_smb_ports(tctx
->lp_ctx
),
224 lpcfg_resolve_context(tctx
->lp_ctx
),
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
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
,
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
);
275 bool test_multichannel_create_channel_array(
276 struct torture_context
*tctx
,
279 struct cli_credentials
*credentials
,
280 struct smbcli_options
*transport_options
,
282 struct smb2_tree
**trees
)
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;
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
,
302 torture_assert(tctx
, tree
, "failed to created new channel");
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",
314 bool test_multichannel_create_channels(
315 struct torture_context
*tctx
,
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;
329 torture_assert(tctx
, tree2A
, "tree2A required!");
331 torture_assert(tctx
, tree2B
, "tree2B required!");
333 if (tree2C
!= NULL
) {
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
,
348 if (tree2C
!= NULL
) {
355 static void test_multichannel_free_channels(struct smb2_tree
*tree2A
,
356 struct smb2_tree
*tree2B
,
357 struct smb2_tree
*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.");
385 test_ioctl_network_interface_info(tctx
, tree1
, &info
),
386 "failed to retrieve network interface info");
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
,
409 bool *timesup
= (bool *)private_data
;
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
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();
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
;
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
)) {
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
,
494 &tree2A
, &tree2B
, NULL
);
495 torture_assert(tctx
, ret
, "Could not create channels.\n");
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);
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
;
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
);
577 talloc_free(mem_ctx
);
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.
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.
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
604 * open file3 in session 1
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();
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
;
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;
637 bool block_setup
= false;
638 bool block_ok
= false;
639 bool unblock_ok
= false;
641 if (!test_multichannel_initial_checks(tctx
, tree1
)) {
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
,
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");
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
,
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;
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
);
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);
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
) {
835 talloc_free(mem_ctx
);
840 struct test_multichannel_oplock_break_state
;
842 struct test_multichannel_oplock_break_channel
{
843 struct test_multichannel_oplock_break_state
*state
;
846 struct smb2_tree
*tree
;
848 struct timeval break_time
;
849 double full_duration
;
850 double relative_duration
;
855 struct test_multichannel_oplock_break_state
{
856 struct torture_context
*tctx
;
857 struct timeval open_req_time
;
858 struct timeval open_rep_time
;
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
,
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
,
876 c
->relative_duration
= timeval_elapsed2(&state
->last_break_time
,
878 state
->last_break_time
= c
->break_time
;
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
,
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();
897 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
898 struct test_multichannel_oplock_break_state state
= {
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
;
909 const char *fname1
= BASEDIR
"\\oplock_break_test3w.dat";
910 struct smb2_tree
*trees2
[32] = { NULL
, };
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
)) {
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
,
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
;
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];
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");
994 torture_comment(tctx
,
995 "Client opens fname1 with session 1 with all %zu blocked\n",
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");
1020 torture_comment(tctx
, "Received %d oplock break(s)!!\n",
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"));
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
;
1052 t
= c
->tree
->session
->transport
;
1054 torture_comment(tctx
, "Unblocking %s\n", c
->name
);
1055 _test_unblock_smb2_transport(tctx
, t
, c
->name
);
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
);
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
) {
1080 TALLOC_FREE(trees2
[i
]);
1083 talloc_free(mem_ctx
);
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();
1095 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1096 struct test_multichannel_oplock_break_state state
= {
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
;
1107 const char *fname1
= BASEDIR
"\\oplock_break_test3s.dat";
1108 struct smb2_tree
*trees2
[32] = { NULL
, };
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
)) {
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
;
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");
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"));
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
)) {
1222 torture_comment(tctx
, "Received %d oplock break(s) wait for more!!\n",
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");
1231 torture_comment(tctx
, "Received %d oplock break(s)!!\n",
1235 if (break_info
.count
< ARRAY_SIZE(state
.channels
)) {
1236 CHECK_VAL_GREATER_THAN(break_info
.count
, 3);
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"));
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
;
1259 t
= c
->tree
->session
->transport
;
1261 torture_comment(tctx
, "Unblocking %s\n", c
->name
);
1262 _test_unblock_smb2_transport(tctx
, t
, c
->name
);
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
);
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
) {
1287 TALLOC_FREE(trees2
[i
]);
1290 talloc_free(mem_ctx
);
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
1310 * open file2 in session 1
1312 * open file3 in session 1
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();
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
;
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
)) {
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
,
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
;
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
);
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
);
1497 talloc_free(mem_ctx
);
1503 * Lease Break Test 2:
1504 * Test for lease break retries being sent by the server.
1506 * open file1 in session 2A
1507 * open file2 in session 2B
1509 * open file2 in session 1
1510 * lease break retry reaches the client?
1512 * open file3 in session 2C
1514 * open file1 in session 1
1515 * lease break reaches the client?
1516 * open file3 in session 1
1517 * lease break reached the client?
1519 * On deletion by 1, lease breaks sent for file1, file2 and file3
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();
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
;
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
)) {
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
,
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");
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);
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");
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
);
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
);
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);
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");
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");
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
);
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");
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
;
1795 if (block_ok
&& !unblock_ok
) {
1796 test_unblock_smb2_transport(tctx
, transport2A
);
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
);
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
);
1824 talloc_free(mem_ctx
);
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.
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();
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
;
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
;
1864 bool timesup
= false;
1866 if (!test_multichannel_initial_checks(tctx
, tree1
)) {
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
,
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
, ×up
);
1918 torture_comment(tctx
, "Failed to add timer.");
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);
1947 smb2_util_close(tree1
, h_client1_file1
);
1948 if (tree2A
!= NULL
) {
1949 smb2_util_close(tree2A
, h_client2_file1
);
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
);
1961 talloc_free(mem_ctx
);
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
);
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
;
1984 int max_channels
= 33; /* 32 is the W2K12R2 and W2K16 limit */
1986 if (smbXcli_conn_protocol(transport1
->conn
) < PROTOCOL_SMB3_00
) {
1988 "SMB 3.X Dialect family required for Multichannel"
1992 server_capabilities
= smb2cli_conn_server_capabilities(
1993 tree1
->session
->transport
->conn
);
1994 if (!(server_capabilities
& SMB2_CAP_MULTI_CHANNEL
)) {
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
*,
2006 transport2
= talloc_zero_array(mem_ctx
, struct smb2_transport
*,
2008 session2
= talloc_zero_array(mem_ctx
, struct smb2_session
*,
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
,
2021 lpcfg_smb_ports(tctx
->lp_ctx
),
2023 lpcfg_resolve_context(tctx
->lp_ctx
),
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
;
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;
2047 * Now bind the session2[i] to the transport2
2049 session2
[i
] = smb2_session_channel(transport2
[i
],
2050 lpcfg_gensec_settings(tctx
,
2055 torture_assert(tctx
, session2
[i
] != NULL
,
2056 "smb2_session_channel failed");
2058 torture_comment(tctx
, "established transport2 [#%d]\n", i
);
2061 expected_status
= NT_STATUS_INSUFFICIENT_RESOURCES
;
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 */),
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",
2080 talloc_free(mem_ctx
);
2085 struct test_multichannel_lease_break_state
;
2087 struct test_multichannel_lease_break_channel
{
2088 struct test_multichannel_lease_break_state
*state
;
2091 struct smb2_tree
*tree
;
2093 struct timeval break_time
;
2094 double full_duration
;
2095 double relative_duration
;
2096 struct smb2_lease_break lb
;
2100 struct test_multichannel_lease_break_state
{
2101 struct torture_context
*tctx
;
2102 struct timeval open_req_time
;
2103 struct timeval open_rep_time
;
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
,
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
,
2120 c
->relative_duration
= timeval_elapsed2(&state
->last_break_time
,
2122 state
->last_break_time
= c
->break_time
;
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
,
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();
2141 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
2142 struct test_multichannel_lease_break_state state
= {
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
;
2153 const char *fname1
= BASEDIR
"\\lease_break_test4.dat";
2154 struct smb2_tree
*trees2
[32] = { NULL
, };
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
)) {
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
,
2189 smb2_util_lease_state("RHW"),
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
;
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");
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
,
2246 smb2_util_lease_state("RHW"),
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);
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
)) {
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");
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);
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
,
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
;
2311 t
= c
->tree
->session
->transport
;
2313 torture_comment(tctx
, "Unblocking %s\n", c
->name
);
2314 _test_unblock_smb2_transport(tctx
, t
, c
->name
);
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
);
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
) {
2339 TALLOC_FREE(trees2
[i
]);
2342 talloc_free(mem_ctx
);
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
;
2363 struct test_multichannel_bug_15346_conn
{
2364 struct test_multichannel_bug_15346_state
*state
;
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
;
2383 struct tm
*current_tm
= NULL
;
2384 char time_str
[sizeof "10000-01-01T00:00:00"];
2385 size_t time_str_len
;
2389 SMB_ASSERT(conn
->nreq
== subreq
);
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(¤t_time
.tv_sec
, &tm_buf
);
2399 torture_assert_not_null_goto(tctx
, current_tm
, ok
, asserted
,
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
,
2406 torture_comment(tctx
,
2407 "%s.%ldZ: conn[%zu]: negprot done\n",
2409 (long)current_time
.tv_usec
,
2412 conn
->ereq
= smb2cli_echo_send(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
,
2426 state
->asserted
= true;
2427 state
->looping
= false;
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
;
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
;
2447 SMB_ASSERT(conn
->ereq
== subreq
);
2450 current_time
= tevent_timeval_current();
2451 current_tm
= gmtime_r(¤t_time
.tv_sec
, &tm_buf
);
2452 torture_assert_not_null_goto(tctx
, current_tm
, ok
, asserted
,
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
,
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
)) {
2468 torture_comment(tctx
,
2469 "%s.%ldZ: conn[%zu]: echo %s\n",
2471 (long)current_time
.tv_usec
,
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
) {
2482 state
->looping
= false;
2487 state
->asserted
= true;
2488 state
->looping
= false;
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
;
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}};
2508 if (smbXcli_conn_protocol(transport1
->conn
) < PROTOCOL_SMB3_00
) {
2510 "SMB 3.X Dialect family required for Multichannel"
2514 server_capabilities
= smb2cli_conn_server_capabilities(
2515 tree1
->session
->transport
->conn
);
2516 if (!(server_capabilities
& SMB2_CAP_MULTI_CHANNEL
)) {
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
,
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
,
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
;
2556 status
= socket_connect_multi(state
->conns
,
2563 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
,
2564 "socket_connect_multi failed");
2566 conn
->smbXcli
= smbXcli_conn_create(state
->conns
,
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");
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
,
2589 state
->num_conns
* 2 * 1000,
2590 smbXcli_conn_protocol(transport1
->conn
),
2591 smbXcli_conn_protocol(transport1
->conn
),
2592 33, /* max_credits */
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
,
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
) {
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
,
2654 samba_cmdline_get_creds(),
2656 0, /* previous_session_id */
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
),
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
;
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");
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
,
2705 struct torture_suite
*suite_oplocks
= torture_suite_create(ctx
,
2707 struct torture_suite
*suite_leases
= torture_suite_create(ctx
,
2709 struct torture_suite
*suite_bugs
= torture_suite_create(ctx
,
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");