2 Unix SMB/CIFS implementation.
4 Copyright (C) Volker Lendecke 2011
5 Copyright (C) Stefan Metzmacher 2011
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "smb2cli_base.h"
25 #include "lib/async_req/async_sock.h"
26 #include "lib/util/tevent_ntstatus.h"
28 struct smb2cli_req_state {
29 struct tevent_context *ev;
30 struct cli_state *cli;
39 uint8_t pad[7]; /* padding space for compounding */
41 /* always an array of 3 talloc elements */
42 struct iovec *recv_iov;
45 static void smb2cli_req_unset_pending(struct tevent_req *req)
47 struct smb2cli_req_state *state =
49 struct smb2cli_req_state);
50 struct cli_state *cli = state->cli;
51 int num_pending = talloc_array_length(cli->pending);
54 talloc_set_destructor(req, NULL);
56 if (num_pending == 1) {
58 * The pending read_smb tevent_req is a child of
59 * cli->pending. So if nothing is pending anymore, we need to
60 * delete the socket read fde.
62 TALLOC_FREE(cli->pending);
66 for (i=0; i<num_pending; i++) {
67 if (req == cli->pending[i]) {
71 if (i == num_pending) {
73 * Something's seriously broken. Just returning here is the
74 * right thing nevertheless, the point of this routine is to
75 * remove ourselves from cli->pending.
81 * Remove ourselves from the cli->pending array
83 for (; i < (num_pending - 1); i++) {
84 cli->pending[i] = cli->pending[i+1];
88 * No NULL check here, we're shrinking by sizeof(void *), and
89 * talloc_realloc just adjusts the size for this.
91 cli->pending = talloc_realloc(NULL, cli->pending, struct tevent_req *,
96 static int smb2cli_req_destructor(struct tevent_req *req)
98 smb2cli_req_unset_pending(req);
102 static void smb2cli_inbuf_received(struct tevent_req *subreq);
104 static bool smb2cli_req_set_pending(struct tevent_req *req)
106 struct smb2cli_req_state *state =
108 struct smb2cli_req_state);
109 struct cli_state *cli;
110 struct tevent_req **pending;
112 struct tevent_req *subreq;
115 num_pending = talloc_array_length(cli->pending);
117 pending = talloc_realloc(cli, cli->pending, struct tevent_req *,
119 if (pending == NULL) {
122 pending[num_pending] = req;
123 cli->pending = pending;
124 talloc_set_destructor(req, smb2cli_req_destructor);
126 if (num_pending > 0) {
131 * We're the first ones, add the read_smb request that waits for the
132 * answer from the server
134 subreq = read_smb_send(cli->pending, state->ev, cli->fd);
135 if (subreq == NULL) {
136 smb2cli_req_unset_pending(req);
139 tevent_req_set_callback(subreq, smb2cli_inbuf_received, cli);
143 static void smb2cli_notify_pending(struct cli_state *cli, NTSTATUS status)
151 * Cancel all pending requests. We don't do a for-loop walking
152 * cli->pending because that array changes in
153 * cli_smb_req_destructor().
155 while (talloc_array_length(cli->pending) > 0) {
156 struct tevent_req *req;
157 struct smb2cli_req_state *state;
159 req = cli->pending[0];
160 state = tevent_req_data(req, struct smb2cli_req_state);
162 smb2cli_req_unset_pending(req);
165 * we need to defer the callback, because we may notify more
168 tevent_req_defer_callback(req, state->ev);
169 tevent_req_nterror(req, status);
173 struct tevent_req *smb2cli_req_create(TALLOC_CTX *mem_ctx,
174 struct tevent_context *ev,
175 struct cli_state *cli,
178 const uint8_t *fixed,
183 struct tevent_req *req;
184 struct smb2cli_req_state *state;
186 req = tevent_req_create(mem_ctx, &state,
187 struct smb2cli_req_state);
194 state->recv_iov = talloc_zero_array(state, struct iovec, 3);
195 if (state->recv_iov == NULL) {
200 state->fixed = fixed;
201 state->fixed_len = fixed_len;
203 state->dyn_len = dyn_len;
205 SIVAL(state->hdr, SMB2_HDR_PROTOCOL_ID, SMB2_MAGIC);
206 SSVAL(state->hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY);
207 SSVAL(state->hdr, SMB2_HDR_EPOCH, 1);
208 SIVAL(state->hdr, SMB2_HDR_STATUS, NT_STATUS_V(NT_STATUS_OK));
209 SSVAL(state->hdr, SMB2_HDR_OPCODE, cmd);
210 SSVAL(state->hdr, SMB2_HDR_CREDIT, 31);
211 SIVAL(state->hdr, SMB2_HDR_FLAGS, flags);
212 SIVAL(state->hdr, SMB2_HDR_PID, cli->smb2.pid);
213 SIVAL(state->hdr, SMB2_HDR_TID, cli->smb2.tid);
214 SBVAL(state->hdr, SMB2_HDR_SESSION_ID, cli->smb2.uid);
219 static void smb2cli_writev_done(struct tevent_req *subreq);
221 NTSTATUS smb2cli_req_compound_submit(struct tevent_req **reqs,
224 struct smb2cli_req_state *state;
225 struct tevent_req *subreq;
227 int i, num_iov, nbt_len;
230 * 1 for the nbt length
231 * per request: HDR, fixed, dyn, padding
232 * -1 because the last one does not need padding
235 iov = talloc_array(reqs[0], struct iovec, 1 + 4*num_reqs - 1);
237 return NT_STATUS_NO_MEMORY;
243 for (i=0; i<num_reqs; i++) {
247 state = tevent_req_data(reqs[i], struct smb2cli_req_state);
249 SBVAL(state->hdr, SMB2_HDR_MESSAGE_ID, state->cli->smb2.mid++);
251 iov[num_iov].iov_base = state->hdr;
252 iov[num_iov].iov_len = sizeof(state->hdr);
255 iov[num_iov].iov_base = discard_const(state->fixed);
256 iov[num_iov].iov_len = state->fixed_len;
259 if (state->dyn != NULL) {
260 iov[num_iov].iov_base = discard_const(state->dyn);
261 iov[num_iov].iov_len = state->dyn_len;
265 reqlen = sizeof(state->hdr) + state->fixed_len +
268 if (i < num_reqs-1) {
269 if ((reqlen % 8) > 0) {
270 uint8_t pad = 8 - (reqlen % 8);
271 iov[num_iov].iov_base = state->pad;
272 iov[num_iov].iov_len = pad;
276 SIVAL(state->hdr, SMB2_HDR_NEXT_COMMAND, reqlen);
280 ret = smb2cli_req_set_pending(reqs[i]);
282 return NT_STATUS_NO_MEMORY;
287 * TODO: Do signing here
290 state = tevent_req_data(reqs[0], struct smb2cli_req_state);
291 _smb_setlen_large(state->nbt, nbt_len);
292 iov[0].iov_base = state->nbt;
293 iov[0].iov_len = sizeof(state->nbt);
295 subreq = writev_send(state, state->ev, state->cli->outgoing,
296 state->cli->fd, false, iov, num_iov);
297 if (subreq == NULL) {
298 return NT_STATUS_NO_MEMORY;
300 tevent_req_set_callback(subreq, smb2cli_writev_done, reqs[0]);
304 struct tevent_req *smb2cli_req_send(TALLOC_CTX *mem_ctx,
305 struct tevent_context *ev,
306 struct cli_state *cli,
309 const uint8_t *fixed,
314 struct tevent_req *req;
317 req = smb2cli_req_create(mem_ctx, ev, cli, cmd, flags,
318 fixed, fixed_len, dyn, dyn_len);
322 if (!tevent_req_is_in_progress(req)) {
325 status = smb2cli_req_compound_submit(&req, 1);
326 if (tevent_req_nterror(req, status)) {
327 return tevent_req_post(req, ev);
332 static void smb2cli_writev_done(struct tevent_req *subreq)
334 struct tevent_req *req =
335 tevent_req_callback_data(subreq,
337 struct smb2cli_req_state *state =
339 struct smb2cli_req_state);
343 nwritten = writev_recv(subreq, &err);
345 if (nwritten == -1) {
346 /* here, we need to notify all pending requests */
347 smb2cli_notify_pending(state->cli, map_nt_error_from_unix(err));
352 static NTSTATUS smb2cli_inbuf_parse_compound(uint8_t *buf, TALLOC_CTX *mem_ctx,
353 struct iovec **piov, int *pnum_iov)
362 iov = talloc_array(mem_ctx, struct iovec, num_iov);
364 return NT_STATUS_NO_MEMORY;
366 iov[0].iov_base = buf;
369 buflen = smb_len_large(buf) + 4;
372 while (taken < buflen) {
373 size_t len = buflen - taken;
374 uint8_t *hdr = buf + taken;
377 size_t next_command_ofs;
379 struct iovec *iov_tmp;
382 * We need the header plus the body length field
385 if (len < SMB2_HDR_BODY + 2) {
386 DEBUG(10, ("%d bytes left, expected at least %d\n",
387 (int)len, SMB2_HDR_BODY));
390 if (IVAL(hdr, 0) != SMB2_MAGIC) {
391 DEBUG(10, ("Got non-SMB2 PDU: %x\n",
395 if (SVAL(hdr, 4) != SMB2_HDR_BODY) {
396 DEBUG(10, ("Got HDR len %d, expected %d\n",
397 SVAL(hdr, 4), SMB2_HDR_BODY));
402 next_command_ofs = IVAL(hdr, SMB2_HDR_NEXT_COMMAND);
403 body_size = SVAL(hdr, SMB2_HDR_BODY);
405 if (next_command_ofs != 0) {
406 if (next_command_ofs < (SMB2_HDR_BODY + 2)) {
409 if (next_command_ofs > full_size) {
412 full_size = next_command_ofs;
419 if (body_size > (full_size - SMB2_HDR_BODY)) {
423 iov_tmp = talloc_realloc(mem_ctx, iov, struct iovec,
425 if (iov_tmp == NULL) {
427 return NT_STATUS_NO_MEMORY;
433 cur[0].iov_base = hdr;
434 cur[0].iov_len = SMB2_HDR_BODY;
435 cur[1].iov_base = hdr + SMB2_HDR_BODY;
436 cur[1].iov_len = body_size;
437 cur[2].iov_base = hdr + SMB2_HDR_BODY + body_size;
438 cur[2].iov_len = full_size - (SMB2_HDR_BODY + body_size);
449 return NT_STATUS_INVALID_NETWORK_RESPONSE;
452 static struct tevent_req *cli_smb2_find_pending(struct cli_state *cli,
455 int num_pending = talloc_array_length(cli->pending);
458 for (i=0; i<num_pending; i++) {
459 struct tevent_req *req = cli->pending[i];
460 struct smb2cli_req_state *state =
462 struct smb2cli_req_state);
464 if (mid == BVAL(state->hdr, SMB2_HDR_MESSAGE_ID)) {
471 static void smb2cli_inbuf_received(struct tevent_req *subreq)
473 struct cli_state *cli =
474 tevent_req_callback_data(subreq,
476 TALLOC_CTX *frame = talloc_stackframe();
477 struct tevent_req *req;
478 struct smb2cli_req_state *state;
487 received = read_smb_recv(subreq, frame, &inbuf, &err);
489 if (received == -1) {
491 * We need to close the connection and notify
492 * all pending requests.
494 smb2cli_notify_pending(cli, map_nt_error_from_unix(err));
499 status = smb2cli_inbuf_parse_compound(inbuf, frame,
501 if (!NT_STATUS_IS_OK(status)) {
503 * if we cannot parse the incoming pdu,
504 * the connection becomes unusable.
506 * We need to close the connection and notify
507 * all pending requests.
509 smb2cli_notify_pending(cli, status);
514 for (i=1; i<num_iov; i+=3) {
515 uint8_t *inbuf_ref = NULL;
516 struct iovec *cur = &iov[i];
517 uint8_t *inhdr = (uint8_t *)cur[0].iov_base;
519 req = cli_smb2_find_pending(
520 cli, BVAL(inhdr, SMB2_HDR_MESSAGE_ID));
523 * TODO: handle oplock breaks and async responses
527 * We need to close the connection and notify
528 * all pending requests.
530 smb2cli_notify_pending(cli, status);
534 smb2cli_req_unset_pending(req);
535 state = tevent_req_data(req, struct smb2cli_req_state);
538 * There might be more than one response
539 * we need to defer the notifications
541 tevent_req_defer_callback(req, state->ev);
544 * Note: here we use talloc_reference() in a way
545 * that does not expose it to the caller.
547 inbuf_ref = talloc_reference(state->recv_iov, inbuf);
548 if (tevent_req_nomem(inbuf_ref, req)) {
552 /* copy the related buffers */
553 state->recv_iov[0] = cur[0];
554 state->recv_iov[1] = cur[1];
555 state->recv_iov[2] = cur[2];
557 tevent_req_done(req);
562 num_pending = talloc_array_length(cli->pending);
563 if (num_pending == 0) {
564 /* no more pending requests, so we are done for now */
567 req = cli->pending[0];
568 state = tevent_req_data(req, struct smb2cli_req_state);
571 * add the read_smb request that waits for the
572 * next answer from the server
574 subreq = read_smb_send(cli->pending, state->ev, cli->fd);
575 if (subreq == NULL) {
576 smb2cli_notify_pending(cli, NT_STATUS_NO_MEMORY);
579 tevent_req_set_callback(subreq, smb2cli_inbuf_received, cli);
582 NTSTATUS smb2cli_req_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
583 struct iovec **piov, int body_size)
585 struct smb2cli_req_state *state =
587 struct smb2cli_req_state);
590 if (tevent_req_is_nterror(req, &status)) {
594 status = NT_STATUS(IVAL(state->recv_iov[0].iov_base, SMB2_HDR_STATUS));
596 if (body_size != 0) {
597 if (body_size != SVAL(state->recv_iov[1].iov_base, 0)) {
598 return NT_STATUS_INVALID_NETWORK_RESPONSE;
602 *piov = talloc_move(mem_ctx, &state->recv_iov);