Line data Source code
1 : /* 2 : * Unix SMB/CIFS implementation. 3 : * 4 : * test suite for SMB2 replay 5 : * 6 : * Copyright (C) Anubhav Rakshit 2014 7 : * Copyright (C) Stefan Metzmacher 2014 8 : * 9 : * This program is free software; you can redistribute it and/or modify 10 : * it under the terms of the GNU General Public License as published by 11 : * the Free Software Foundation; either version 3 of the License, or 12 : * (at your option) any later version. 13 : * 14 : * This program is distributed in the hope that it will be useful, 15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 : * GNU General Public License for more details. 18 : * 19 : * You should have received a copy of the GNU General Public License 20 : * along with this program. If not, see <http://www.gnu.org/licenses/>. 21 : */ 22 : 23 : #include "includes.h" 24 : #include "libcli/smb2/smb2.h" 25 : #include "libcli/smb2/smb2_calls.h" 26 : #include "torture/torture.h" 27 : #include "torture/smb2/proto.h" 28 : #include "../libcli/smb/smbXcli_base.h" 29 : #include "oplock_break_handler.h" 30 : #include "lib/events/events.h" 31 : 32 : struct break_info break_info; 33 : 34 30 : static void torture_oplock_ack_callback(struct smb2_request *req) 35 : { 36 0 : NTSTATUS status; 37 : 38 30 : status = smb2_break_recv(req, &break_info.br); 39 30 : if (!NT_STATUS_IS_OK(status)) { 40 2 : break_info.failures++; 41 2 : break_info.failure_status = status; 42 : } 43 30 : } 44 : 45 : /** 46 : * A general oplock break notification handler. This should be used when a 47 : * test expects to break from batch or exclusive to a lower level. 48 : */ 49 : 50 222 : bool torture_oplock_ack_handler(struct smb2_transport *transport, 51 : const struct smb2_handle *handle, 52 : uint8_t level, 53 : void *private_data) 54 : { 55 222 : struct smb2_tree *tree = private_data; 56 0 : const char *name; 57 0 : struct smb2_request *req; 58 : 59 222 : ZERO_STRUCT(break_info.br); 60 : 61 222 : break_info.handle = *handle; 62 222 : break_info.level = level; 63 222 : break_info.count++; 64 : 65 222 : switch (level) { 66 220 : case SMB2_OPLOCK_LEVEL_II: 67 220 : name = "level II"; 68 220 : break; 69 2 : case SMB2_OPLOCK_LEVEL_NONE: 70 2 : name = "none"; 71 2 : break; 72 0 : default: 73 0 : name = "unknown"; 74 0 : break_info.failures++; 75 : } 76 : 77 222 : break_info.br.in.file.handle = *handle; 78 222 : break_info.br.in.oplock_level = level; 79 222 : break_info.br.in.reserved = 0; 80 222 : break_info.br.in.reserved2 = 0; 81 222 : break_info.received_transport = tree->session->transport; 82 222 : SMB_ASSERT(tree->session->transport == transport); 83 : 84 222 : if (break_info.oplock_skip_ack) { 85 192 : torture_comment(break_info.tctx, 86 : "transport[%p] skip acking to %s [0x%02X] in oplock handler\n", 87 : transport, name, level); 88 192 : return true; 89 : } 90 : 91 30 : torture_comment(break_info.tctx, 92 : "transport[%p] Acking to %s [0x%02X] in oplock handler\n", 93 : transport, name, level); 94 : 95 30 : req = smb2_break_send(tree, &break_info.br); 96 30 : req->async.fn = torture_oplock_ack_callback; 97 30 : req->async.private_data = NULL; 98 : 99 30 : return true; 100 : } 101 : 102 : /** 103 : * A oplock break handler designed to ignore incoming break requests. 104 : * This is used when incoming oplock break requests need to be ignored 105 : */ 106 0 : bool torture_oplock_ignore_handler(struct smb2_transport *transport, 107 : const struct smb2_handle *handle, 108 : uint8_t level, void *private_data) 109 : { 110 0 : return true; 111 : } 112 : 113 : /* 114 : * Timer handler function notifies the registering function that time is up 115 : */ 116 480 : static void timeout_cb(struct tevent_context *ev, 117 : struct tevent_timer *te, 118 : struct timeval current_time, 119 : void *private_data) 120 : { 121 480 : bool *timesup = (bool *)private_data; 122 480 : *timesup = true; 123 480 : } 124 : 125 : /* 126 : * Wait a short period of time to receive a single oplock break request 127 : */ 128 544 : void torture_wait_for_oplock_break(struct torture_context *tctx) 129 : { 130 544 : TALLOC_CTX *tmp_ctx = talloc_new(NULL); 131 544 : struct tevent_timer *te = NULL; 132 0 : struct timeval ne; 133 544 : bool timesup = false; 134 544 : int old_count = break_info.count; 135 : 136 : /* Wait 1 second for an oplock break */ 137 544 : ne = tevent_timeval_current_ofs(0, 1000000); 138 : 139 544 : te = tevent_add_timer(tctx->ev, tmp_ctx, ne, timeout_cb, ×up); 140 544 : if (te == NULL) { 141 0 : torture_comment(tctx, "Failed to wait for an oplock break. " 142 : "test results may not be accurate.\n"); 143 0 : goto done; 144 : } 145 : 146 544 : torture_comment(tctx, "Waiting for a potential oplock break...\n"); 147 1368 : while (!timesup && break_info.count < old_count + 1) { 148 824 : if (tevent_loop_once(tctx->ev) != 0) { 149 0 : torture_comment(tctx, "Failed to wait for an oplock " 150 : "break. test results may not be " 151 : "accurate\n."); 152 0 : goto done; 153 : } 154 : } 155 544 : if (timesup) { 156 480 : torture_comment(tctx, "... waiting for an oplock break timed out\n"); 157 : } else { 158 64 : torture_comment(tctx, "Got %u oplock breaks\n", 159 64 : break_info.count - old_count); 160 : } 161 : 162 544 : done: 163 : /* We don't know if the timed event fired and was freed, we received 164 : * our oplock break, or some other event triggered the loop. Thus, 165 : * we create a tmp_ctx to be able to safely free/remove the timed 166 : * event in all 3 cases. 167 : */ 168 544 : talloc_free(tmp_ctx); 169 544 : }