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