s3:smb2cli_base: make use of tevent_req_defer_callback()
[Samba/gebeck_regimport.git] / source3 / libsmb / smb2cli_base.c
blobac7d423b572ac9db1af7eee2c42b9ef9fa495001
1 /*
2 Unix SMB/CIFS implementation.
3 smb2 lib
4 Copyright (C) Volker Lendecke 2011
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.
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.
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/>.
20 #include "includes.h"
21 #include "client.h"
22 #include "read_smb.h"
23 #include "smb2cli_base.h"
24 #include "lib/async_req/async_sock.h"
25 #include "lib/util/tevent_ntstatus.h"
27 struct smb2cli_req_state {
28 struct tevent_context *ev;
29 struct cli_state *cli;
31 const uint8_t *fixed;
32 uint16_t fixed_len;
33 const uint8_t *dyn;
34 uint16_t dyn_len;
36 uint8_t nbt[4];
37 uint8_t hdr[64];
38 uint8_t pad[7]; /* padding space for compounding */
40 /* always an array of 3 talloc elements */
41 struct iovec *recv_iov;
44 static void smb2cli_req_unset_pending(struct tevent_req *req)
46 struct smb2cli_req_state *state =
47 tevent_req_data(req,
48 struct smb2cli_req_state);
49 struct cli_state *cli = state->cli;
50 int num_pending = talloc_array_length(cli->pending);
51 int i;
53 talloc_set_destructor(req, NULL);
55 if (num_pending == 1) {
57 * The pending read_smb tevent_req is a child of
58 * cli->pending. So if nothing is pending anymore, we need to
59 * delete the socket read fde.
61 TALLOC_FREE(cli->pending);
62 return;
65 for (i=0; i<num_pending; i++) {
66 if (req == cli->pending[i]) {
67 break;
70 if (i == num_pending) {
72 * Something's seriously broken. Just returning here is the
73 * right thing nevertheless, the point of this routine is to
74 * remove ourselves from cli->pending.
76 return;
80 * Remove ourselves from the cli->pending array
82 for (; i < (num_pending - 1); i++) {
83 cli->pending[i] = cli->pending[i+1];
87 * No NULL check here, we're shrinking by sizeof(void *), and
88 * talloc_realloc just adjusts the size for this.
90 cli->pending = talloc_realloc(NULL, cli->pending, struct tevent_req *,
91 num_pending - 1);
92 return;
95 static int smb2cli_req_destructor(struct tevent_req *req)
97 smb2cli_req_unset_pending(req);
98 return 0;
101 static void smb2cli_inbuf_received(struct tevent_req *subreq);
103 static bool smb2cli_req_set_pending(struct tevent_req *req)
105 struct smb2cli_req_state *state =
106 tevent_req_data(req,
107 struct smb2cli_req_state);
108 struct cli_state *cli;
109 struct tevent_req **pending;
110 int num_pending;
111 struct tevent_req *subreq;
113 cli = state->cli;
114 num_pending = talloc_array_length(cli->pending);
116 pending = talloc_realloc(cli, cli->pending, struct tevent_req *,
117 num_pending+1);
118 if (pending == NULL) {
119 return false;
121 pending[num_pending] = req;
122 cli->pending = pending;
123 talloc_set_destructor(req, smb2cli_req_destructor);
125 if (num_pending > 0) {
126 return true;
130 * We're the first ones, add the read_smb request that waits for the
131 * answer from the server
133 subreq = read_smb_send(cli->pending, state->ev, cli->fd);
134 if (subreq == NULL) {
135 smb2cli_req_unset_pending(req);
136 return false;
138 tevent_req_set_callback(subreq, smb2cli_inbuf_received, cli);
139 return true;
142 static void smb2cli_notify_pending(struct cli_state *cli, NTSTATUS status)
144 if (cli->fd != -1) {
145 close(cli->fd);
146 cli->fd = -1;
150 * Cancel all pending requests. We don't do a for-loop walking
151 * cli->pending because that array changes in
152 * cli_smb_req_destructor().
154 while (talloc_array_length(cli->pending) > 0) {
155 struct tevent_req *req;
156 struct smb2cli_req_state *state;
158 req = cli->pending[0];
159 state = tevent_req_data(req, struct smb2cli_req_state);
161 smb2cli_req_unset_pending(req);
164 * we need to defer the callback, because we may notify more
165 * then one caller.
167 tevent_req_defer_callback(req, state->ev);
168 tevent_req_nterror(req, status);
172 struct tevent_req *smb2cli_req_create(TALLOC_CTX *mem_ctx,
173 struct tevent_context *ev,
174 struct cli_state *cli,
175 uint16_t cmd,
176 uint32_t flags,
177 const uint8_t *fixed,
178 uint16_t fixed_len,
179 const uint8_t *dyn,
180 uint16_t dyn_len)
182 struct tevent_req *req;
183 struct smb2cli_req_state *state;
185 req = tevent_req_create(mem_ctx, &state,
186 struct smb2cli_req_state);
187 if (req == NULL) {
188 return NULL;
190 state->ev = ev;
191 state->cli = cli;
193 state->recv_iov = talloc_zero_array(state, struct iovec, 3);
194 if (state->recv_iov == NULL) {
195 TALLOC_FREE(req);
196 return NULL;
199 state->fixed = fixed;
200 state->fixed_len = fixed_len;
201 state->dyn = dyn;
202 state->dyn_len = dyn_len;
204 SIVAL(state->hdr, SMB2_HDR_PROTOCOL_ID, SMB2_MAGIC);
205 SSVAL(state->hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY);
206 SSVAL(state->hdr, SMB2_HDR_EPOCH, 1);
207 SIVAL(state->hdr, SMB2_HDR_STATUS, NT_STATUS_V(NT_STATUS_OK));
208 SSVAL(state->hdr, SMB2_HDR_OPCODE, cmd);
209 SSVAL(state->hdr, SMB2_HDR_CREDIT, 31);
210 SIVAL(state->hdr, SMB2_HDR_FLAGS, flags);
211 SIVAL(state->hdr, SMB2_HDR_PID, cli->smb2.pid);
212 SIVAL(state->hdr, SMB2_HDR_TID, cli->smb2.tid);
213 SBVAL(state->hdr, SMB2_HDR_SESSION_ID, cli->smb2.uid);
215 return req;
218 static void smb2cli_writev_done(struct tevent_req *subreq);
220 NTSTATUS smb2cli_req_compound_submit(struct tevent_req **reqs,
221 int num_reqs)
223 struct smb2cli_req_state *state;
224 struct tevent_req *subreq;
225 struct iovec *iov;
226 int i, num_iov, nbt_len;
229 * 1 for the nbt length
230 * per request: HDR, fixed, dyn, padding
231 * -1 because the last one does not need padding
234 iov = talloc_array(reqs[0], struct iovec, 1 + 4*num_reqs - 1);
235 if (iov == NULL) {
236 return NT_STATUS_NO_MEMORY;
239 num_iov = 1;
240 nbt_len = 0;
242 for (i=0; i<num_reqs; i++) {
243 size_t reqlen;
244 bool ret;
246 state = tevent_req_data(reqs[i], struct smb2cli_req_state);
248 SBVAL(state->hdr, SMB2_HDR_MESSAGE_ID, state->cli->smb2.mid++);
250 iov[num_iov].iov_base = state->hdr;
251 iov[num_iov].iov_len = sizeof(state->hdr);
252 num_iov += 1;
254 iov[num_iov].iov_base = discard_const(state->fixed);
255 iov[num_iov].iov_len = state->fixed_len;
256 num_iov += 1;
258 if (state->dyn != NULL) {
259 iov[num_iov].iov_base = discard_const(state->dyn);
260 iov[num_iov].iov_len = state->dyn_len;
261 num_iov += 1;
264 reqlen = sizeof(state->hdr) + state->fixed_len +
265 state->dyn_len;
267 if (i < num_reqs-1) {
268 if ((reqlen % 8) > 0) {
269 uint8_t pad = 8 - (reqlen % 8);
270 iov[num_iov].iov_base = state->pad;
271 iov[num_iov].iov_len = pad;
272 num_iov += 1;
273 reqlen += pad;
275 SIVAL(state->hdr, SMB2_HDR_NEXT_COMMAND, reqlen);
277 nbt_len += reqlen;
279 ret = smb2cli_req_set_pending(reqs[i]);
280 if (!ret) {
281 return NT_STATUS_NO_MEMORY;
286 * TODO: Do signing here
289 state = tevent_req_data(reqs[0], struct smb2cli_req_state);
290 _smb_setlen_large(state->nbt, nbt_len);
291 iov[0].iov_base = state->nbt;
292 iov[0].iov_len = sizeof(state->nbt);
294 subreq = writev_send(state, state->ev, state->cli->outgoing,
295 state->cli->fd, false, iov, num_iov);
296 if (subreq == NULL) {
297 return NT_STATUS_NO_MEMORY;
299 tevent_req_set_callback(subreq, smb2cli_writev_done, reqs[0]);
300 return NT_STATUS_OK;
303 struct tevent_req *smb2cli_req_send(TALLOC_CTX *mem_ctx,
304 struct tevent_context *ev,
305 struct cli_state *cli,
306 uint16_t cmd,
307 uint32_t flags,
308 const uint8_t *fixed,
309 uint16_t fixed_len,
310 const uint8_t *dyn,
311 uint16_t dyn_len)
313 struct tevent_req *req;
314 NTSTATUS status;
316 req = smb2cli_req_create(mem_ctx, ev, cli, cmd, flags,
317 fixed, fixed_len, dyn, dyn_len);
318 if (req == NULL) {
319 return NULL;
321 if (!tevent_req_is_in_progress(req)) {
322 return req;
324 status = smb2cli_req_compound_submit(&req, 1);
325 if (tevent_req_nterror(req, status)) {
326 return tevent_req_post(req, ev);
328 return req;
331 static void smb2cli_writev_done(struct tevent_req *subreq)
333 struct tevent_req *req =
334 tevent_req_callback_data(subreq,
335 struct tevent_req);
336 struct smb2cli_req_state *state =
337 tevent_req_data(req,
338 struct smb2cli_req_state);
339 ssize_t nwritten;
340 int err;
342 nwritten = writev_recv(subreq, &err);
343 TALLOC_FREE(subreq);
344 if (nwritten == -1) {
345 /* here, we need to notify all pending requests */
346 smb2cli_notify_pending(state->cli, map_nt_error_from_unix(err));
347 return;
351 static NTSTATUS smb2cli_inbuf_parse_compound(uint8_t *buf, TALLOC_CTX *mem_ctx,
352 struct iovec **piov, int *pnum_iov)
354 struct iovec *iov;
355 int num_iov;
356 size_t buflen;
357 size_t taken;
359 num_iov = 1;
361 iov = talloc_array(mem_ctx, struct iovec, num_iov);
362 if (iov == NULL) {
363 return NT_STATUS_NO_MEMORY;
365 iov[0].iov_base = buf;
366 iov[0].iov_len = 4;
368 buflen = smb_len_large(buf) + 4;
369 taken = 4;
371 while (taken < buflen) {
372 size_t len = buflen - taken;
373 uint8_t *hdr = buf + taken;
374 struct iovec *cur;
375 size_t full_size;
376 size_t next_command_ofs;
377 uint16_t body_size;
378 struct iovec *iov_tmp;
381 * We need the header plus the body length field
384 if (len < SMB2_HDR_BODY + 2) {
385 DEBUG(10, ("%d bytes left, expected at least %d\n",
386 (int)len, SMB2_HDR_BODY));
387 goto inval;
389 if (IVAL(hdr, 0) != SMB2_MAGIC) {
390 DEBUG(10, ("Got non-SMB2 PDU: %x\n",
391 IVAL(hdr, 0)));
392 goto inval;
394 if (SVAL(hdr, 4) != SMB2_HDR_BODY) {
395 DEBUG(10, ("Got HDR len %d, expected %d\n",
396 SVAL(hdr, 4), SMB2_HDR_BODY));
397 goto inval;
400 full_size = len;
401 next_command_ofs = IVAL(hdr, SMB2_HDR_NEXT_COMMAND);
402 body_size = SVAL(hdr, SMB2_HDR_BODY);
404 if (next_command_ofs != 0) {
405 if (next_command_ofs < (SMB2_HDR_BODY + 2)) {
406 goto inval;
408 if (next_command_ofs > full_size) {
409 goto inval;
411 full_size = next_command_ofs;
413 if (body_size < 2) {
414 goto inval;
416 body_size &= 0xfffe;
418 if (body_size > (full_size - SMB2_HDR_BODY)) {
419 goto inval;
422 iov_tmp = talloc_realloc(mem_ctx, iov, struct iovec,
423 num_iov + 3);
424 if (iov_tmp == NULL) {
425 TALLOC_FREE(iov);
426 return NT_STATUS_NO_MEMORY;
428 iov = iov_tmp;
429 cur = &iov[num_iov];
430 num_iov += 3;
432 cur[0].iov_base = hdr;
433 cur[0].iov_len = SMB2_HDR_BODY;
434 cur[1].iov_base = hdr + SMB2_HDR_BODY;
435 cur[1].iov_len = body_size;
436 cur[2].iov_base = hdr + SMB2_HDR_BODY + body_size;
437 cur[2].iov_len = full_size - (SMB2_HDR_BODY + body_size);
439 taken += full_size;
442 *piov = iov;
443 *pnum_iov = num_iov;
444 return NT_STATUS_OK;
446 inval:
447 TALLOC_FREE(iov);
448 return NT_STATUS_INVALID_NETWORK_RESPONSE;
451 static struct tevent_req *cli_smb2_find_pending(struct cli_state *cli,
452 uint64_t mid)
454 int num_pending = talloc_array_length(cli->pending);
455 int i;
457 for (i=0; i<num_pending; i++) {
458 struct tevent_req *req = cli->pending[i];
459 struct smb2cli_req_state *state =
460 tevent_req_data(req,
461 struct smb2cli_req_state);
463 if (mid == BVAL(state->hdr, SMB2_HDR_MESSAGE_ID)) {
464 return req;
467 return NULL;
470 static void smb2cli_inbuf_received(struct tevent_req *subreq)
472 struct cli_state *cli =
473 tevent_req_callback_data(subreq,
474 struct cli_state);
475 TALLOC_CTX *frame = talloc_stackframe();
476 struct tevent_req *req;
477 struct iovec *iov;
478 int i, num_iov;
479 NTSTATUS status;
480 uint8_t *inbuf;
481 ssize_t received;
482 int err;
484 received = read_smb_recv(subreq, frame, &inbuf, &err);
485 TALLOC_FREE(subreq);
486 if (received == -1) {
488 * We need to close the connection and notify
489 * all pending requests.
491 smb2cli_notify_pending(cli, map_nt_error_from_unix(err));
492 TALLOC_FREE(frame);
493 return;
496 status = smb2cli_inbuf_parse_compound(inbuf, frame,
497 &iov, &num_iov);
498 if (!NT_STATUS_IS_OK(status)) {
500 * if we cannot parse the incoming pdu,
501 * the connection becomes unusable.
503 * We need to close the connection and notify
504 * all pending requests.
506 smb2cli_notify_pending(cli, status);
507 TALLOC_FREE(frame);
508 return;
511 for (i=1; i<num_iov; i+=3) {
512 uint8_t *inbuf_ref = NULL;
513 struct iovec *cur = &iov[i];
514 uint8_t *inhdr = (uint8_t *)cur[0].iov_base;
515 struct smb2cli_req_state *state;
517 req = cli_smb2_find_pending(
518 cli, BVAL(inhdr, SMB2_HDR_MESSAGE_ID));
519 if (req == NULL) {
521 * TODO: handle oplock breaks and async responses
525 * We need to close the connection and notify
526 * all pending requests.
528 smb2cli_notify_pending(cli, status);
529 TALLOC_FREE(frame);
530 return;
532 smb2cli_req_unset_pending(req);
533 state = tevent_req_data(req, struct smb2cli_req_state);
536 * There might be more than one response
537 * we need to defer the notifications
539 tevent_req_defer_callback(req, state->ev);
542 * Note: here we use talloc_reference() in a way
543 * that does not expose it to the caller.
545 inbuf_ref = talloc_reference(state->recv_iov, inbuf);
546 if (tevent_req_nomem(inbuf_ref, req)) {
547 continue;
550 /* copy the related buffers */
551 state->recv_iov[0] = cur[0];
552 state->recv_iov[1] = cur[1];
553 state->recv_iov[2] = cur[2];
555 tevent_req_done(req);
558 TALLOC_FREE(frame);
561 NTSTATUS smb2cli_req_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
562 struct iovec **piov, int body_size)
564 struct smb2cli_req_state *state =
565 tevent_req_data(req,
566 struct smb2cli_req_state);
567 NTSTATUS status;
569 if (tevent_req_is_nterror(req, &status)) {
570 return status;
573 status = NT_STATUS(IVAL(state->recv_iov[0].iov_base, SMB2_HDR_STATUS));
575 if (body_size != 0) {
576 if (body_size != SVAL(state->recv_iov[1].iov_base, 0)) {
577 return NT_STATUS_INVALID_NETWORK_RESPONSE;
580 if (piov != NULL) {
581 *piov = talloc_move(mem_ctx, &state->recv_iov);
584 return status;