Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 : smb2 lib
4 : Copyright (C) Volker Lendecke 2017
5 :
6 : This program is free software; you can redistribute it and/or modify
7 : it under the terms of the GNU General Public License as published by
8 : the Free Software Foundation; either version 3 of the License, or
9 : (at your option) any later version.
10 :
11 : This program is distributed in the hope that it will be useful,
12 : but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : GNU General Public License for more details.
15 :
16 : You should have received a copy of the GNU General Public License
17 : along with this program. If not, see <http://www.gnu.org/licenses/>.
18 : */
19 :
20 : #include "includes.h"
21 : #include "system/network.h"
22 : #include "lib/util/tevent_ntstatus.h"
23 : #include "smb_common.h"
24 : #include "smbXcli_base.h"
25 : #include "librpc/gen_ndr/ndr_notify.h"
26 :
27 : struct smb2cli_notify_state {
28 : uint8_t fixed[32];
29 :
30 : struct iovec *recv_iov;
31 : uint8_t *data;
32 : uint32_t data_length;
33 :
34 : struct tevent_req *subreq;
35 : struct tevent_req *timeout_subreq;
36 : };
37 :
38 : static void smb2cli_notify_done(struct tevent_req *subreq);
39 : static void smb2cli_notify_timedout(struct tevent_req *subreq);
40 : static bool smb2cli_notify_cancel(struct tevent_req *req);
41 :
42 42 : struct tevent_req *smb2cli_notify_send(TALLOC_CTX *mem_ctx,
43 : struct tevent_context *ev,
44 : struct smbXcli_conn *conn,
45 : uint32_t timeout_msec,
46 : struct smbXcli_session *session,
47 : struct smbXcli_tcon *tcon,
48 : uint32_t output_buffer_length,
49 : uint64_t fid_persistent,
50 : uint64_t fid_volatile,
51 : uint32_t completion_filter,
52 : bool recursive)
53 : {
54 0 : struct tevent_req *req;
55 0 : struct smb2cli_notify_state *state;
56 0 : uint8_t *fixed;
57 0 : uint16_t watch_tree;
58 :
59 42 : req = tevent_req_create(mem_ctx, &state,
60 : struct smb2cli_notify_state);
61 42 : if (req == NULL) {
62 0 : return NULL;
63 : }
64 :
65 42 : watch_tree = recursive ? SMB2_WATCH_TREE : 0;
66 42 : fixed = state->fixed;
67 42 : SSVAL(fixed, 0, 32);
68 42 : SSVAL(fixed, 2, watch_tree);
69 42 : SIVAL(fixed, 4, output_buffer_length);
70 42 : SBVAL(fixed, 8, fid_persistent);
71 42 : SBVAL(fixed, 16, fid_volatile);
72 42 : SIVAL(fixed, 24, completion_filter);
73 42 : SIVAL(fixed, 28, 0); /* reserved */
74 :
75 84 : state->subreq = smb2cli_req_send(state, ev, conn, SMB2_OP_NOTIFY,
76 : 0, 0, /* flags */
77 : 0, /* timeout_msec */
78 : tcon,
79 : session,
80 42 : state->fixed, sizeof(state->fixed),
81 : NULL, 0, /* dyn* */
82 : 0); /* max_dyn_len */
83 42 : if (tevent_req_nomem(state->subreq, req)) {
84 0 : return tevent_req_post(req, ev);
85 : }
86 42 : tevent_req_set_callback(state->subreq, smb2cli_notify_done, req);
87 :
88 42 : if (timeout_msec != 0) {
89 0 : state->timeout_subreq = tevent_wakeup_send(
90 : state, ev, timeval_current_ofs_msec(timeout_msec));
91 0 : if (tevent_req_nomem(state->timeout_subreq, req)) {
92 0 : return tevent_req_post(req, ev);
93 : }
94 0 : tevent_req_set_callback(
95 : state->timeout_subreq, smb2cli_notify_timedout, req);
96 : }
97 :
98 42 : tevent_req_set_cancel_fn(req, smb2cli_notify_cancel);
99 :
100 42 : return req;
101 : }
102 :
103 0 : static bool smb2cli_notify_cancel(struct tevent_req *req)
104 : {
105 0 : struct smb2cli_notify_state *state = tevent_req_data(
106 : req, struct smb2cli_notify_state);
107 0 : bool ok;
108 :
109 0 : TALLOC_FREE(state->timeout_subreq);
110 :
111 0 : ok = tevent_req_cancel(state->subreq);
112 0 : return ok;
113 : }
114 :
115 0 : static void smb2cli_notify_timedout(struct tevent_req *subreq)
116 : {
117 0 : struct tevent_req *req = tevent_req_callback_data(
118 : subreq, struct tevent_req);
119 0 : struct smb2cli_notify_state *state = tevent_req_data(
120 : req, struct smb2cli_notify_state);
121 0 : bool ok;
122 :
123 0 : ok = tevent_wakeup_recv(subreq);
124 0 : if (!ok) {
125 0 : tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
126 0 : return;
127 : }
128 :
129 0 : ok = tevent_req_cancel(state->subreq);
130 0 : if (!ok) {
131 0 : tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
132 0 : return;
133 : }
134 : }
135 :
136 42 : static void smb2cli_notify_done(struct tevent_req *subreq)
137 : {
138 42 : struct tevent_req *req = tevent_req_callback_data(
139 : subreq, struct tevent_req);
140 42 : struct smb2cli_notify_state *state = tevent_req_data(
141 : req, struct smb2cli_notify_state);
142 0 : NTSTATUS status;
143 0 : struct iovec *iov;
144 0 : uint16_t data_offset;
145 0 : static const struct smb2cli_req_expected_response expected[] = {
146 : {
147 : .status = NT_STATUS_OK,
148 : .body_size = 0x09
149 : }
150 : };
151 :
152 42 : status = smb2cli_req_recv(subreq, state, &iov,
153 : expected, ARRAY_SIZE(expected));
154 42 : TALLOC_FREE(subreq);
155 :
156 42 : if (NT_STATUS_EQUAL(status, NT_STATUS_CANCELLED)) {
157 0 : status = NT_STATUS_IO_TIMEOUT;
158 : }
159 42 : if (tevent_req_nterror(req, status)) {
160 12 : return;
161 : }
162 :
163 30 : data_offset = SVAL(iov[1].iov_base, 2);
164 30 : state->data_length = IVAL(iov[1].iov_base, 4);
165 :
166 30 : if ((data_offset != SMB2_HDR_BODY + 8) ||
167 30 : (state->data_length > iov[2].iov_len)) {
168 0 : tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
169 0 : return;
170 : }
171 :
172 30 : state->recv_iov = iov;
173 30 : state->data = (uint8_t *)iov[2].iov_base;
174 30 : tevent_req_done(req);
175 : }
176 :
177 42 : NTSTATUS smb2cli_notify_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
178 : uint8_t **data, uint32_t *data_length)
179 : {
180 42 : struct smb2cli_notify_state *state = tevent_req_data(
181 : req, struct smb2cli_notify_state);
182 0 : NTSTATUS status;
183 :
184 42 : if (tevent_req_is_nterror(req, &status)) {
185 12 : return status;
186 : }
187 30 : talloc_steal(mem_ctx, state->recv_iov);
188 30 : *data_length = state->data_length;
189 30 : *data = state->data;
190 30 : return NT_STATUS_OK;
191 : }
192 :
193 0 : NTSTATUS smb2cli_notify(struct smbXcli_conn *conn,
194 : uint32_t timeout_msec,
195 : struct smbXcli_session *session,
196 : struct smbXcli_tcon *tcon,
197 : uint32_t output_buffer_length,
198 : uint64_t fid_persistent,
199 : uint64_t fid_volatile,
200 : uint32_t completion_filter,
201 : bool recursive,
202 : TALLOC_CTX *mem_ctx,
203 : uint8_t **data,
204 : uint32_t *data_length)
205 : {
206 0 : TALLOC_CTX *frame = talloc_stackframe();
207 0 : struct tevent_context *ev;
208 0 : struct tevent_req *req;
209 0 : NTSTATUS status = NT_STATUS_NO_MEMORY;
210 :
211 0 : if (smbXcli_conn_has_async_calls(conn)) {
212 : /*
213 : * Can't use sync call while an async call is in flight
214 : */
215 0 : status = NT_STATUS_INVALID_PARAMETER;
216 0 : goto fail;
217 : }
218 0 : ev = samba_tevent_context_init(frame);
219 0 : if (ev == NULL) {
220 0 : goto fail;
221 : }
222 0 : req = smb2cli_notify_send(frame, ev, conn, timeout_msec,
223 : session, tcon, output_buffer_length,
224 : fid_persistent, fid_volatile,
225 : completion_filter, recursive);
226 0 : if (req == NULL) {
227 0 : goto fail;
228 : }
229 0 : if (!tevent_req_poll_ntstatus(req, ev, &status)) {
230 0 : goto fail;
231 : }
232 0 : status = smb2cli_notify_recv(req, mem_ctx, data, data_length);
233 0 : fail:
234 0 : TALLOC_FREE(frame);
235 0 : return status;
236 : }
|