1/* 2 Unix SMB/CIFS implementation. 3 4 SMB2 notify test suite 5 6 Copyright (C) Stefan Metzmacher 2006 7 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. 12 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. 17 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/>. 20*/ 21 22#include "includes.h" 23#include "libcli/smb2/smb2.h" 24#include "libcli/smb2/smb2_calls.h" 25 26#include "torture/torture.h" 27#include "torture/smb2/proto.h" 28 29#include "libcli/raw/libcliraw.h" 30#include "lib/events/events.h" 31 32#define CHECK_STATUS(status, correct) do { \ 33 if (!NT_STATUS_EQUAL(status, correct)) { \ 34 printf("(%s) Incorrect status %s - should be %s\n", \ 35 __location__, nt_errstr(status), nt_errstr(correct)); \ 36 ret = false; \ 37 goto done; \ 38 }} while (0) 39 40#define CHECK_VALUE(v, correct) do { \ 41 if ((v) != (correct)) { \ 42 printf("(%s) Incorrect value %s=%d - should be %d\n", \ 43 __location__, #v, v, correct); \ 44 ret = false; \ 45 goto done; \ 46 }} while (0) 47 48#define CHECK_WIRE_STR(field, value) do { \ 49 if (!field.s || strcmp(field.s, value)) { \ 50 printf("(%s) %s [%s] != %s\n", \ 51 __location__, #field, field.s, value); \ 52 ret = false; \ 53 goto done; \ 54 }} while (0) 55 56#define FNAME "smb2-notify01.dat" 57 58#define TARGET_IS_WIN7(_tctx) (torture_setting_bool(_tctx, "win7", false)) 59 60static bool test_valid_request(struct torture_context *tctx, struct smb2_tree *tree) 61{ 62 bool ret = true; 63 NTSTATUS status; 64 struct smb2_handle dh; 65 struct smb2_notify n; 66 struct smb2_request *req; 67 uint32_t max_buffer_size = 0x00080000; 68 69 if (TARGET_IS_WIN7(tctx)) { 70 max_buffer_size = 0x00010000; 71 } 72 73 smb2_util_unlink(tree, FNAME); 74 75 status = smb2_util_roothandle(tree, &dh); 76 CHECK_STATUS(status, NT_STATUS_OK); 77 78 n.in.recursive = 0x0000; 79 n.in.buffer_size = max_buffer_size; 80 n.in.file.handle = dh; 81 n.in.completion_filter = 0x00000FFF; 82 n.in.unknown = 0x00000000; 83 req = smb2_notify_send(tree, &n); 84 85 while (!req->cancel.can_cancel && req->state <= SMB2_REQUEST_RECV) { 86 if (event_loop_once(req->transport->socket->event.ctx) != 0) { 87 break; 88 } 89 } 90 91 status = torture_setup_complex_file(tree, FNAME); 92 CHECK_STATUS(status, NT_STATUS_OK); 93 94 status = smb2_notify_recv(req, tctx, &n); 95 CHECK_STATUS(status, NT_STATUS_OK); 96 CHECK_VALUE(n.out.num_changes, 1); 97 CHECK_VALUE(n.out.changes[0].action, NOTIFY_ACTION_ADDED); 98 CHECK_WIRE_STR(n.out.changes[0].name, FNAME); 99 100 /* 101 * if the change response doesn't fit in the buffer 102 * NOTIFY_ENUM_DIR is returned. 103 */ 104 n.in.buffer_size = 0x00000000; 105 req = smb2_notify_send(tree, &n); 106 107 while (!req->cancel.can_cancel && req->state <= SMB2_REQUEST_RECV) { 108 if (event_loop_once(req->transport->socket->event.ctx) != 0) { 109 break; 110 } 111 } 112 113 status = torture_setup_complex_file(tree, FNAME); 114 CHECK_STATUS(status, NT_STATUS_OK); 115 116 status = smb2_notify_recv(req, tctx, &n); 117 CHECK_STATUS(status, STATUS_NOTIFY_ENUM_DIR); 118 119 /* 120 * if the change response fits in the buffer we get 121 * NT_STATUS_OK again 122 */ 123 n.in.buffer_size = max_buffer_size; 124 req = smb2_notify_send(tree, &n); 125 126 while (!req->cancel.can_cancel && req->state <= SMB2_REQUEST_RECV) { 127 if (event_loop_once(req->transport->socket->event.ctx) != 0) { 128 break; 129 } 130 } 131 132 status = torture_setup_complex_file(tree, FNAME); 133 CHECK_STATUS(status, NT_STATUS_OK); 134 135 status = smb2_notify_recv(req, tctx, &n); 136 CHECK_STATUS(status, NT_STATUS_OK); 137 CHECK_VALUE(n.out.num_changes, 3); 138 CHECK_VALUE(n.out.changes[0].action, NOTIFY_ACTION_REMOVED); 139 CHECK_WIRE_STR(n.out.changes[0].name, FNAME); 140 CHECK_VALUE(n.out.changes[1].action, NOTIFY_ACTION_ADDED); 141 CHECK_WIRE_STR(n.out.changes[1].name, FNAME); 142 CHECK_VALUE(n.out.changes[2].action, NOTIFY_ACTION_MODIFIED); 143 CHECK_WIRE_STR(n.out.changes[2].name, FNAME); 144 145 /* if the first notify returns NOTIFY_ENUM_DIR, all do */ 146 status = smb2_util_close(tree, dh); 147 CHECK_STATUS(status, NT_STATUS_OK); 148 status = smb2_util_roothandle(tree, &dh); 149 CHECK_STATUS(status, NT_STATUS_OK); 150 151 n.in.recursive = 0x0000; 152 n.in.buffer_size = 0x00000001; 153 n.in.file.handle = dh; 154 n.in.completion_filter = 0x00000FFF; 155 n.in.unknown = 0x00000000; 156 req = smb2_notify_send(tree, &n); 157 158 while (!req->cancel.can_cancel && req->state <= SMB2_REQUEST_RECV) { 159 if (event_loop_once(req->transport->socket->event.ctx) != 0) { 160 break; 161 } 162 } 163 164 status = torture_setup_complex_file(tree, FNAME); 165 CHECK_STATUS(status, NT_STATUS_OK); 166 167 status = smb2_notify_recv(req, tctx, &n); 168 CHECK_STATUS(status, STATUS_NOTIFY_ENUM_DIR); 169 170 n.in.buffer_size = max_buffer_size; 171 req = smb2_notify_send(tree, &n); 172 while (!req->cancel.can_cancel && req->state <= SMB2_REQUEST_RECV) { 173 if (event_loop_once(req->transport->socket->event.ctx) != 0) { 174 break; 175 } 176 } 177 178 status = torture_setup_complex_file(tree, FNAME); 179 CHECK_STATUS(status, NT_STATUS_OK); 180 181 status = smb2_notify_recv(req, tctx, &n); 182 CHECK_STATUS(status, STATUS_NOTIFY_ENUM_DIR); 183 184 /* if the buffer size is too large, we get invalid parameter */ 185 n.in.recursive = 0x0000; 186 n.in.buffer_size = max_buffer_size + 1; 187 n.in.file.handle = dh; 188 n.in.completion_filter = 0x00000FFF; 189 n.in.unknown = 0x00000000; 190 req = smb2_notify_send(tree, &n); 191 status = smb2_notify_recv(req, tctx, &n); 192 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); 193 194done: 195 return ret; 196} 197 198/* basic testing of SMB2 notify 199*/ 200bool torture_smb2_notify(struct torture_context *torture) 201{ 202 struct smb2_tree *tree; 203 bool ret = true; 204 205 if (!torture_smb2_connection(torture, &tree)) { 206 return false; 207 } 208 209 ret &= test_valid_request(torture, tree); 210 211 return ret; 212} 213