s3-utils/net_rpc_printer.c: print more info on write error
[Samba/gebeck_regimport.git] / source3 / libsmb / smb2cli_base.c
blob88b890438ab328e69c6defd17bbcc7725f36c1a7
1 /*
2 Unix SMB/CIFS implementation.
3 smb2 lib
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/>.
21 #include "includes.h"
22 #include "client.h"
23 #include "read_smb.h"
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;
32 const uint8_t *fixed;
33 uint16_t fixed_len;
34 const uint8_t *dyn;
35 uint16_t dyn_len;
37 uint8_t nbt[4];
38 uint8_t hdr[64];
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 =
48 tevent_req_data(req,
49 struct smb2cli_req_state);
50 struct cli_state *cli = state->cli;
51 int num_pending = talloc_array_length(cli->conn.pending);
52 int i;
54 talloc_set_destructor(req, NULL);
56 if (num_pending == 1) {
58 * The pending read_smb tevent_req is a child of
59 * cli->conn.pending. So if nothing is pending anymore,
60 * we need to delete the socket read fde.
62 TALLOC_FREE(cli->conn.pending);
63 return;
66 for (i=0; i<num_pending; i++) {
67 if (req == cli->conn.pending[i]) {
68 break;
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->conn.pending.
77 return;
81 * Remove ourselves from the cli->pending array
83 for (; i < (num_pending - 1); i++) {
84 cli->conn.pending[i] = cli->conn.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->conn.pending = talloc_realloc(NULL, cli->conn.pending,
92 struct tevent_req *,
93 num_pending - 1);
94 return;
97 static int smb2cli_req_destructor(struct tevent_req *req)
99 smb2cli_req_unset_pending(req);
100 return 0;
103 static void smb2cli_inbuf_received(struct tevent_req *subreq);
105 static bool smb2cli_req_set_pending(struct tevent_req *req)
107 struct smb2cli_req_state *state =
108 tevent_req_data(req,
109 struct smb2cli_req_state);
110 struct cli_state *cli;
111 struct tevent_req **pending;
112 int num_pending;
113 struct tevent_req *subreq;
115 cli = state->cli;
116 num_pending = talloc_array_length(cli->conn.pending);
118 pending = talloc_realloc(cli, cli->conn.pending, struct tevent_req *,
119 num_pending+1);
120 if (pending == NULL) {
121 return false;
123 pending[num_pending] = req;
124 cli->conn.pending = pending;
125 talloc_set_destructor(req, smb2cli_req_destructor);
127 if (num_pending > 0) {
128 return true;
132 * We're the first ones, add the read_smb request that waits for the
133 * answer from the server
135 subreq = read_smb_send(cli->conn.pending, state->ev, cli->conn.fd);
136 if (subreq == NULL) {
137 smb2cli_req_unset_pending(req);
138 return false;
140 tevent_req_set_callback(subreq, smb2cli_inbuf_received, cli);
141 return true;
144 static void smb2cli_notify_pending(struct cli_state *cli, NTSTATUS status)
146 if (cli->conn.fd != -1) {
147 close(cli->conn.fd);
149 cli->conn.fd = -1;
152 * Cancel all pending requests. We don't do a for-loop walking
153 * cli->conn.pending because that array changes in
154 * cli_smb_req_destructor().
156 while (talloc_array_length(cli->conn.pending) > 0) {
157 struct tevent_req *req;
158 struct smb2cli_req_state *state;
160 req = cli->conn.pending[0];
161 state = tevent_req_data(req, struct smb2cli_req_state);
163 smb2cli_req_unset_pending(req);
166 * we need to defer the callback, because we may notify more
167 * then one caller.
169 tevent_req_defer_callback(req, state->ev);
170 tevent_req_nterror(req, status);
174 struct tevent_req *smb2cli_req_create(TALLOC_CTX *mem_ctx,
175 struct tevent_context *ev,
176 struct cli_state *cli,
177 uint16_t cmd,
178 uint32_t flags,
179 const uint8_t *fixed,
180 uint16_t fixed_len,
181 const uint8_t *dyn,
182 uint16_t dyn_len)
184 struct tevent_req *req;
185 struct smb2cli_req_state *state;
187 req = tevent_req_create(mem_ctx, &state,
188 struct smb2cli_req_state);
189 if (req == NULL) {
190 return NULL;
192 state->ev = ev;
193 state->cli = cli;
195 state->recv_iov = talloc_zero_array(state, struct iovec, 3);
196 if (state->recv_iov == NULL) {
197 TALLOC_FREE(req);
198 return NULL;
201 state->fixed = fixed;
202 state->fixed_len = fixed_len;
203 state->dyn = dyn;
204 state->dyn_len = dyn_len;
206 SIVAL(state->hdr, SMB2_HDR_PROTOCOL_ID, SMB2_MAGIC);
207 SSVAL(state->hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY);
208 SSVAL(state->hdr, SMB2_HDR_EPOCH, 1);
209 SIVAL(state->hdr, SMB2_HDR_STATUS, NT_STATUS_V(NT_STATUS_OK));
210 SSVAL(state->hdr, SMB2_HDR_OPCODE, cmd);
211 SSVAL(state->hdr, SMB2_HDR_CREDIT, 31);
212 SIVAL(state->hdr, SMB2_HDR_FLAGS, flags);
213 SIVAL(state->hdr, SMB2_HDR_PID, cli->smb2.pid);
214 SIVAL(state->hdr, SMB2_HDR_TID, cli->smb2.tid);
215 SBVAL(state->hdr, SMB2_HDR_SESSION_ID, cli->smb2.uid);
217 return req;
220 static void smb2cli_writev_done(struct tevent_req *subreq);
222 NTSTATUS smb2cli_req_compound_submit(struct tevent_req **reqs,
223 int num_reqs)
225 struct smb2cli_req_state *state;
226 struct tevent_req *subreq;
227 struct iovec *iov;
228 int i, num_iov, nbt_len;
231 * 1 for the nbt length
232 * per request: HDR, fixed, dyn, padding
233 * -1 because the last one does not need padding
236 iov = talloc_array(reqs[0], struct iovec, 1 + 4*num_reqs - 1);
237 if (iov == NULL) {
238 return NT_STATUS_NO_MEMORY;
241 num_iov = 1;
242 nbt_len = 0;
244 for (i=0; i<num_reqs; i++) {
245 size_t reqlen;
246 bool ret;
248 state = tevent_req_data(reqs[i], struct smb2cli_req_state);
250 SBVAL(state->hdr, SMB2_HDR_MESSAGE_ID, state->cli->smb2.mid++);
252 iov[num_iov].iov_base = state->hdr;
253 iov[num_iov].iov_len = sizeof(state->hdr);
254 num_iov += 1;
256 iov[num_iov].iov_base = discard_const(state->fixed);
257 iov[num_iov].iov_len = state->fixed_len;
258 num_iov += 1;
260 if (state->dyn != NULL) {
261 iov[num_iov].iov_base = discard_const(state->dyn);
262 iov[num_iov].iov_len = state->dyn_len;
263 num_iov += 1;
266 reqlen = sizeof(state->hdr) + state->fixed_len +
267 state->dyn_len;
269 if (i < num_reqs-1) {
270 if ((reqlen % 8) > 0) {
271 uint8_t pad = 8 - (reqlen % 8);
272 iov[num_iov].iov_base = state->pad;
273 iov[num_iov].iov_len = pad;
274 num_iov += 1;
275 reqlen += pad;
277 SIVAL(state->hdr, SMB2_HDR_NEXT_COMMAND, reqlen);
279 nbt_len += reqlen;
281 ret = smb2cli_req_set_pending(reqs[i]);
282 if (!ret) {
283 return NT_STATUS_NO_MEMORY;
288 * TODO: Do signing here
291 state = tevent_req_data(reqs[0], struct smb2cli_req_state);
292 _smb_setlen_large(state->nbt, nbt_len);
293 iov[0].iov_base = state->nbt;
294 iov[0].iov_len = sizeof(state->nbt);
296 subreq = writev_send(state, state->ev, state->cli->conn.outgoing,
297 state->cli->conn.fd, false, iov, num_iov);
298 if (subreq == NULL) {
299 return NT_STATUS_NO_MEMORY;
301 tevent_req_set_callback(subreq, smb2cli_writev_done, reqs[0]);
302 return NT_STATUS_OK;
305 struct tevent_req *smb2cli_req_send(TALLOC_CTX *mem_ctx,
306 struct tevent_context *ev,
307 struct cli_state *cli,
308 uint16_t cmd,
309 uint32_t flags,
310 const uint8_t *fixed,
311 uint16_t fixed_len,
312 const uint8_t *dyn,
313 uint16_t dyn_len)
315 struct tevent_req *req;
316 NTSTATUS status;
318 req = smb2cli_req_create(mem_ctx, ev, cli, cmd, flags,
319 fixed, fixed_len, dyn, dyn_len);
320 if (req == NULL) {
321 return NULL;
323 if (!tevent_req_is_in_progress(req)) {
324 return req;
326 status = smb2cli_req_compound_submit(&req, 1);
327 if (tevent_req_nterror(req, status)) {
328 return tevent_req_post(req, ev);
330 return req;
333 static void smb2cli_writev_done(struct tevent_req *subreq)
335 struct tevent_req *req =
336 tevent_req_callback_data(subreq,
337 struct tevent_req);
338 struct smb2cli_req_state *state =
339 tevent_req_data(req,
340 struct smb2cli_req_state);
341 ssize_t nwritten;
342 int err;
344 nwritten = writev_recv(subreq, &err);
345 TALLOC_FREE(subreq);
346 if (nwritten == -1) {
347 /* here, we need to notify all pending requests */
348 smb2cli_notify_pending(state->cli, map_nt_error_from_unix(err));
349 return;
353 static NTSTATUS smb2cli_inbuf_parse_compound(uint8_t *buf, TALLOC_CTX *mem_ctx,
354 struct iovec **piov, int *pnum_iov)
356 struct iovec *iov;
357 int num_iov;
358 size_t buflen;
359 size_t taken;
361 num_iov = 1;
363 iov = talloc_array(mem_ctx, struct iovec, num_iov);
364 if (iov == NULL) {
365 return NT_STATUS_NO_MEMORY;
367 iov[0].iov_base = buf;
368 iov[0].iov_len = 4;
370 buflen = smb_len_large(buf) + 4;
371 taken = 4;
373 while (taken < buflen) {
374 size_t len = buflen - taken;
375 uint8_t *hdr = buf + taken;
376 struct iovec *cur;
377 size_t full_size;
378 size_t next_command_ofs;
379 uint16_t body_size;
380 struct iovec *iov_tmp;
383 * We need the header plus the body length field
386 if (len < SMB2_HDR_BODY + 2) {
387 DEBUG(10, ("%d bytes left, expected at least %d\n",
388 (int)len, SMB2_HDR_BODY));
389 goto inval;
391 if (IVAL(hdr, 0) != SMB2_MAGIC) {
392 DEBUG(10, ("Got non-SMB2 PDU: %x\n",
393 IVAL(hdr, 0)));
394 goto inval;
396 if (SVAL(hdr, 4) != SMB2_HDR_BODY) {
397 DEBUG(10, ("Got HDR len %d, expected %d\n",
398 SVAL(hdr, 4), SMB2_HDR_BODY));
399 goto inval;
402 full_size = len;
403 next_command_ofs = IVAL(hdr, SMB2_HDR_NEXT_COMMAND);
404 body_size = SVAL(hdr, SMB2_HDR_BODY);
406 if (next_command_ofs != 0) {
407 if (next_command_ofs < (SMB2_HDR_BODY + 2)) {
408 goto inval;
410 if (next_command_ofs > full_size) {
411 goto inval;
413 full_size = next_command_ofs;
415 if (body_size < 2) {
416 goto inval;
418 body_size &= 0xfffe;
420 if (body_size > (full_size - SMB2_HDR_BODY)) {
421 goto inval;
424 iov_tmp = talloc_realloc(mem_ctx, iov, struct iovec,
425 num_iov + 3);
426 if (iov_tmp == NULL) {
427 TALLOC_FREE(iov);
428 return NT_STATUS_NO_MEMORY;
430 iov = iov_tmp;
431 cur = &iov[num_iov];
432 num_iov += 3;
434 cur[0].iov_base = hdr;
435 cur[0].iov_len = SMB2_HDR_BODY;
436 cur[1].iov_base = hdr + SMB2_HDR_BODY;
437 cur[1].iov_len = body_size;
438 cur[2].iov_base = hdr + SMB2_HDR_BODY + body_size;
439 cur[2].iov_len = full_size - (SMB2_HDR_BODY + body_size);
441 taken += full_size;
444 *piov = iov;
445 *pnum_iov = num_iov;
446 return NT_STATUS_OK;
448 inval:
449 TALLOC_FREE(iov);
450 return NT_STATUS_INVALID_NETWORK_RESPONSE;
453 static struct tevent_req *cli_smb2_find_pending(struct cli_state *cli,
454 uint64_t mid)
456 int num_pending = talloc_array_length(cli->conn.pending);
457 int i;
459 for (i=0; i<num_pending; i++) {
460 struct tevent_req *req = cli->conn.pending[i];
461 struct smb2cli_req_state *state =
462 tevent_req_data(req,
463 struct smb2cli_req_state);
465 if (mid == BVAL(state->hdr, SMB2_HDR_MESSAGE_ID)) {
466 return req;
469 return NULL;
472 static void smb2cli_inbuf_received(struct tevent_req *subreq)
474 struct cli_state *cli =
475 tevent_req_callback_data(subreq,
476 struct cli_state);
477 TALLOC_CTX *frame = talloc_stackframe();
478 struct tevent_req *req;
479 struct smb2cli_req_state *state;
480 struct iovec *iov;
481 int i, num_iov;
482 NTSTATUS status;
483 uint8_t *inbuf;
484 ssize_t received;
485 int err;
486 size_t num_pending;
488 received = read_smb_recv(subreq, frame, &inbuf, &err);
489 TALLOC_FREE(subreq);
490 if (received == -1) {
492 * We need to close the connection and notify
493 * all pending requests.
495 smb2cli_notify_pending(cli, map_nt_error_from_unix(err));
496 TALLOC_FREE(frame);
497 return;
500 status = smb2cli_inbuf_parse_compound(inbuf, frame,
501 &iov, &num_iov);
502 if (!NT_STATUS_IS_OK(status)) {
504 * if we cannot parse the incoming pdu,
505 * the connection becomes unusable.
507 * We need to close the connection and notify
508 * all pending requests.
510 smb2cli_notify_pending(cli, status);
511 TALLOC_FREE(frame);
512 return;
515 for (i=1; i<num_iov; i+=3) {
516 uint8_t *inbuf_ref = NULL;
517 struct iovec *cur = &iov[i];
518 uint8_t *inhdr = (uint8_t *)cur[0].iov_base;
520 req = cli_smb2_find_pending(
521 cli, BVAL(inhdr, SMB2_HDR_MESSAGE_ID));
522 if (req == NULL) {
524 * TODO: handle oplock breaks and async responses
528 * We need to close the connection and notify
529 * all pending requests.
531 smb2cli_notify_pending(cli, status);
532 TALLOC_FREE(frame);
533 return;
535 smb2cli_req_unset_pending(req);
536 state = tevent_req_data(req, struct smb2cli_req_state);
539 * There might be more than one response
540 * we need to defer the notifications
542 tevent_req_defer_callback(req, state->ev);
545 * Note: here we use talloc_reference() in a way
546 * that does not expose it to the caller.
548 inbuf_ref = talloc_reference(state->recv_iov, inbuf);
549 if (tevent_req_nomem(inbuf_ref, req)) {
550 continue;
553 /* copy the related buffers */
554 state->recv_iov[0] = cur[0];
555 state->recv_iov[1] = cur[1];
556 state->recv_iov[2] = cur[2];
558 tevent_req_done(req);
561 TALLOC_FREE(frame);
563 num_pending = talloc_array_length(cli->conn.pending);
564 if (num_pending == 0) {
565 /* no more pending requests, so we are done for now */
566 return;
568 req = cli->conn.pending[0];
569 state = tevent_req_data(req, struct smb2cli_req_state);
572 * add the read_smb request that waits for the
573 * next answer from the server
575 subreq = read_smb_send(cli->conn.pending, state->ev, cli->conn.fd);
576 if (subreq == NULL) {
577 smb2cli_notify_pending(cli, NT_STATUS_NO_MEMORY);
578 return;
580 tevent_req_set_callback(subreq, smb2cli_inbuf_received, cli);
583 NTSTATUS smb2cli_req_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
584 struct iovec **piov, int body_size)
586 struct smb2cli_req_state *state =
587 tevent_req_data(req,
588 struct smb2cli_req_state);
589 NTSTATUS status;
591 if (tevent_req_is_nterror(req, &status)) {
592 return status;
595 status = NT_STATUS(IVAL(state->recv_iov[0].iov_base, SMB2_HDR_STATUS));
597 if (body_size != 0) {
598 if (body_size != SVAL(state->recv_iov[1].iov_base, 0)) {
599 return NT_STATUS_INVALID_NETWORK_RESPONSE;
602 if (piov != NULL) {
603 *piov = talloc_move(mem_ctx, &state->recv_iov);
606 return status;