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 0 : static void torture_oplock_ack_callback(struct smb2_request *req)
35 : {
36 : NTSTATUS status;
37 :
38 0 : status = smb2_break_recv(req, &break_info.br);
39 0 : if (!NT_STATUS_IS_OK(status)) {
40 0 : break_info.failures++;
41 0 : break_info.failure_status = status;
42 : }
43 0 : }
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 0 : 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 0 : struct smb2_tree *tree = private_data;
56 : const char *name;
57 : struct smb2_request *req;
58 :
59 0 : ZERO_STRUCT(break_info.br);
60 :
61 0 : break_info.handle = *handle;
62 0 : break_info.level = level;
63 0 : break_info.count++;
64 :
65 0 : switch (level) {
66 0 : case SMB2_OPLOCK_LEVEL_II:
67 0 : name = "level II";
68 0 : break;
69 0 : case SMB2_OPLOCK_LEVEL_NONE:
70 0 : name = "none";
71 0 : break;
72 0 : default:
73 0 : name = "unknown";
74 0 : break_info.failures++;
75 : }
76 :
77 0 : break_info.br.in.file.handle = *handle;
78 0 : break_info.br.in.oplock_level = level;
79 0 : break_info.br.in.reserved = 0;
80 0 : break_info.br.in.reserved2 = 0;
81 0 : break_info.received_transport = tree->session->transport;
82 0 : SMB_ASSERT(tree->session->transport == transport);
83 :
84 0 : if (break_info.oplock_skip_ack) {
85 0 : torture_comment(break_info.tctx,
86 : "transport[%p] skip acking to %s [0x%02X] in oplock handler\n",
87 : transport, name, level);
88 0 : return true;
89 : }
90 :
91 0 : torture_comment(break_info.tctx,
92 : "transport[%p] Acking to %s [0x%02X] in oplock handler\n",
93 : transport, name, level);
94 :
95 0 : req = smb2_break_send(tree, &break_info.br);
96 0 : req->async.fn = torture_oplock_ack_callback;
97 0 : req->async.private_data = NULL;
98 :
99 0 : 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 61 : static void timeout_cb(struct tevent_context *ev,
117 : struct tevent_timer *te,
118 : struct timeval current_time,
119 : void *private_data)
120 : {
121 61 : bool *timesup = (bool *)private_data;
122 61 : *timesup = true;
123 61 : }
124 :
125 : /*
126 : * Wait a short period of time to receive a single oplock break request
127 : */
128 61 : void torture_wait_for_oplock_break(struct torture_context *tctx)
129 : {
130 61 : TALLOC_CTX *tmp_ctx = talloc_new(NULL);
131 61 : struct tevent_timer *te = NULL;
132 : struct timeval ne;
133 61 : bool timesup = false;
134 61 : int old_count = break_info.count;
135 :
136 : /* Wait 1 second for an oplock break */
137 61 : ne = tevent_timeval_current_ofs(0, 1000000);
138 :
139 61 : te = tevent_add_timer(tctx->ev, tmp_ctx, ne, timeout_cb, ×up);
140 61 : 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 61 : torture_comment(tctx, "Waiting for a potential oplock break...\n");
147 61 : while (!timesup && break_info.count < old_count + 1) {
148 105 : 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 61 : if (timesup) {
156 61 : torture_comment(tctx, "... waiting for an oplock break timed out\n");
157 : } else {
158 0 : torture_comment(tctx, "Got %u oplock breaks\n",
159 0 : break_info.count - old_count);
160 : }
161 :
162 61 : 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 61 : talloc_free(tmp_ctx);
169 61 : }
|