s3/libsmb: Generalise cli_state in smb2 write calls
[Samba/wip.git] / source3 / libsmb / cli_np_tstream.c
blob53e0c41d8cbc5f4d49ae5539f5634bbdc8e1064d
1 /*
2 Unix SMB/CIFS implementation.
4 Copyright (C) Stefan Metzmacher 2010
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 "system/network.h"
22 #include "libsmb/libsmb.h"
23 #include "libsmb/smb2cli.h"
24 #include "../lib/util/tevent_ntstatus.h"
25 #include "../lib/tsocket/tsocket.h"
26 #include "../lib/tsocket/tsocket_internal.h"
27 #include "cli_np_tstream.h"
29 static const struct tstream_context_ops tstream_cli_np_ops;
32 * Windows uses 4280 (the max xmit/recv size negotiated on DCERPC).
33 * This is fits into the max_xmit negotiated at the SMB layer.
35 * On the sending side they may use SMBtranss if the request does not
36 * fit into a single SMBtrans call.
38 * Windows uses 1024 as max data size of a SMBtrans request and then
39 * possibly reads the rest of the DCERPC fragment (up to 3256 bytes)
40 * via a SMBreadX.
42 * For now we just ask for the full 4280 bytes (max data size) in the SMBtrans
43 * request to get the whole fragment at once (like samba 3.5.x and below did.
45 * It is important that we use do SMBwriteX with the size of a full fragment,
46 * otherwise we may get NT_STATUS_PIPE_BUSY on the SMBtrans request
47 * from NT4 servers. (See bug #8195)
49 #define TSTREAM_CLI_NP_MAX_BUF_SIZE 4280
51 struct tstream_cli_np {
52 struct cli_state *cli;
53 const char *npipe;
54 bool is_smb1;
55 uint16_t fnum;
56 uint64_t fid_persistent;
57 uint64_t fid_volatile;
58 unsigned int default_timeout;
60 struct {
61 bool active;
62 struct tevent_req *read_req;
63 struct tevent_req *write_req;
64 uint16_t setup[2];
65 } trans;
67 struct {
68 off_t ofs;
69 size_t left;
70 uint8_t *buf;
71 } read, write;
74 static int tstream_cli_np_destructor(struct tstream_cli_np *cli_nps)
76 NTSTATUS status;
78 if (!cli_state_is_connected(cli_nps->cli)) {
79 return 0;
83 * TODO: do not use a sync call with a destructor!!!
85 * This only happens, if a caller does talloc_free(),
86 * while the everything was still ok.
88 * If we get an unexpected failure within a normal
89 * operation, we already do an async cli_close_send()/_recv().
91 * Once we've fixed all callers to call
92 * tstream_disconnect_send()/_recv(), this will
93 * never be called.
95 if (cli_nps->is_smb1) {
96 status = cli_close(cli_nps->cli, cli_nps->fnum);
97 } else {
98 status = smb2cli_close(cli_nps->cli->conn,
99 cli_nps->cli->timeout,
100 cli_nps->cli->smb2.session,
101 cli_nps->cli->smb2.tid, 0,
102 cli_nps->fid_persistent,
103 cli_nps->fid_volatile);
105 if (!NT_STATUS_IS_OK(status)) {
106 DEBUG(1, ("tstream_cli_np_destructor: cli_close "
107 "failed on pipe %s. Error was %s\n",
108 cli_nps->npipe, nt_errstr(status)));
111 * We can't do much on failure
113 return 0;
116 struct tstream_cli_np_open_state {
117 struct cli_state *cli;
118 bool is_smb1;
119 uint16_t fnum;
120 uint64_t fid_persistent;
121 uint64_t fid_volatile;
122 const char *npipe;
125 static void tstream_cli_np_open_done(struct tevent_req *subreq);
127 struct tevent_req *tstream_cli_np_open_send(TALLOC_CTX *mem_ctx,
128 struct tevent_context *ev,
129 struct cli_state *cli,
130 const char *npipe)
132 struct tevent_req *req;
133 struct tstream_cli_np_open_state *state;
134 struct tevent_req *subreq;
136 req = tevent_req_create(mem_ctx, &state,
137 struct tstream_cli_np_open_state);
138 if (!req) {
139 return NULL;
141 state->cli = cli;
143 state->npipe = talloc_strdup(state, npipe);
144 if (tevent_req_nomem(state->npipe, req)) {
145 return tevent_req_post(req, ev);
148 if (cli_state_protocol(cli) < PROTOCOL_SMB2_02) {
149 state->is_smb1 = true;
152 if (state->is_smb1) {
153 subreq = cli_ntcreate_send(state, ev, cli,
154 npipe,
156 DESIRED_ACCESS_PIPE,
158 FILE_SHARE_READ|FILE_SHARE_WRITE,
159 FILE_OPEN,
162 } else {
163 subreq = smb2cli_create_send(state, ev, cli->conn,
164 cli->timeout, cli->smb2.session,
165 cli->smb2.tid,
166 npipe,
167 SMB2_OPLOCK_LEVEL_NONE,
168 SMB2_IMPERSONATION_IMPERSONATION,
169 DESIRED_ACCESS_PIPE,
170 0, /* file_attributes */
171 FILE_SHARE_READ|FILE_SHARE_WRITE,
172 FILE_OPEN,
173 0, /* create_options */
174 NULL); /* blobs */
176 if (tevent_req_nomem(subreq, req)) {
177 return tevent_req_post(req, ev);
179 tevent_req_set_callback(subreq, tstream_cli_np_open_done, req);
181 return req;
184 static void tstream_cli_np_open_done(struct tevent_req *subreq)
186 struct tevent_req *req =
187 tevent_req_callback_data(subreq, struct tevent_req);
188 struct tstream_cli_np_open_state *state =
189 tevent_req_data(req, struct tstream_cli_np_open_state);
190 NTSTATUS status;
192 if (state->is_smb1) {
193 status = cli_ntcreate_recv(subreq, &state->fnum);
194 } else {
195 status = smb2cli_create_recv(subreq,
196 &state->fid_persistent,
197 &state->fid_volatile);
199 TALLOC_FREE(subreq);
200 if (!NT_STATUS_IS_OK(status)) {
201 tevent_req_nterror(req, status);
202 return;
205 tevent_req_done(req);
208 NTSTATUS _tstream_cli_np_open_recv(struct tevent_req *req,
209 TALLOC_CTX *mem_ctx,
210 struct tstream_context **_stream,
211 const char *location)
213 struct tstream_cli_np_open_state *state =
214 tevent_req_data(req, struct tstream_cli_np_open_state);
215 struct tstream_context *stream;
216 struct tstream_cli_np *cli_nps;
217 NTSTATUS status;
219 if (tevent_req_is_nterror(req, &status)) {
220 tevent_req_received(req);
221 return status;
224 stream = tstream_context_create(mem_ctx,
225 &tstream_cli_np_ops,
226 &cli_nps,
227 struct tstream_cli_np,
228 location);
229 if (!stream) {
230 tevent_req_received(req);
231 return NT_STATUS_NO_MEMORY;
233 ZERO_STRUCTP(cli_nps);
235 cli_nps->cli = state->cli;
236 cli_nps->npipe = talloc_move(cli_nps, &state->npipe);
237 cli_nps->is_smb1 = state->is_smb1;
238 cli_nps->fnum = state->fnum;
239 cli_nps->fid_persistent = state->fid_persistent;
240 cli_nps->fid_volatile = state->fid_volatile;
241 cli_nps->default_timeout = cli_set_timeout(state->cli, 0);
242 cli_set_timeout(state->cli, cli_nps->default_timeout);
244 talloc_set_destructor(cli_nps, tstream_cli_np_destructor);
246 cli_nps->trans.active = false;
247 cli_nps->trans.read_req = NULL;
248 cli_nps->trans.write_req = NULL;
249 SSVAL(cli_nps->trans.setup+0, 0, TRANSACT_DCERPCCMD);
250 SSVAL(cli_nps->trans.setup+1, 0, cli_nps->fnum);
252 *_stream = stream;
253 tevent_req_received(req);
254 return NT_STATUS_OK;
257 static ssize_t tstream_cli_np_pending_bytes(struct tstream_context *stream)
259 struct tstream_cli_np *cli_nps = tstream_context_data(stream,
260 struct tstream_cli_np);
262 if (!cli_state_is_connected(cli_nps->cli)) {
263 errno = ENOTCONN;
264 return -1;
267 return cli_nps->read.left;
270 bool tstream_is_cli_np(struct tstream_context *stream)
272 struct tstream_cli_np *cli_nps =
273 talloc_get_type(_tstream_context_data(stream),
274 struct tstream_cli_np);
276 if (!cli_nps) {
277 return false;
280 return true;
283 NTSTATUS tstream_cli_np_use_trans(struct tstream_context *stream)
285 struct tstream_cli_np *cli_nps = tstream_context_data(stream,
286 struct tstream_cli_np);
288 if (cli_nps->trans.read_req) {
289 return NT_STATUS_PIPE_BUSY;
292 if (cli_nps->trans.write_req) {
293 return NT_STATUS_PIPE_BUSY;
296 if (cli_nps->trans.active) {
297 return NT_STATUS_PIPE_BUSY;
300 cli_nps->trans.active = true;
302 return NT_STATUS_OK;
305 unsigned int tstream_cli_np_set_timeout(struct tstream_context *stream,
306 unsigned int timeout)
308 struct tstream_cli_np *cli_nps = tstream_context_data(stream,
309 struct tstream_cli_np);
311 if (!cli_state_is_connected(cli_nps->cli)) {
312 return cli_nps->default_timeout;
315 return cli_set_timeout(cli_nps->cli, timeout);
318 struct cli_state *tstream_cli_np_get_cli_state(struct tstream_context *stream)
320 struct tstream_cli_np *cli_nps = tstream_context_data(stream,
321 struct tstream_cli_np);
323 return cli_nps->cli;
326 struct tstream_cli_np_writev_state {
327 struct tstream_context *stream;
328 struct tevent_context *ev;
330 struct iovec *vector;
331 size_t count;
333 int ret;
335 struct {
336 int val;
337 const char *location;
338 } error;
341 static int tstream_cli_np_writev_state_destructor(struct tstream_cli_np_writev_state *state)
343 struct tstream_cli_np *cli_nps =
344 tstream_context_data(state->stream,
345 struct tstream_cli_np);
347 cli_nps->trans.write_req = NULL;
349 return 0;
352 static void tstream_cli_np_writev_write_next(struct tevent_req *req);
354 static struct tevent_req *tstream_cli_np_writev_send(TALLOC_CTX *mem_ctx,
355 struct tevent_context *ev,
356 struct tstream_context *stream,
357 const struct iovec *vector,
358 size_t count)
360 struct tevent_req *req;
361 struct tstream_cli_np_writev_state *state;
362 struct tstream_cli_np *cli_nps = tstream_context_data(stream,
363 struct tstream_cli_np);
365 req = tevent_req_create(mem_ctx, &state,
366 struct tstream_cli_np_writev_state);
367 if (!req) {
368 return NULL;
370 state->stream = stream;
371 state->ev = ev;
372 state->ret = 0;
374 talloc_set_destructor(state, tstream_cli_np_writev_state_destructor);
376 if (!cli_state_is_connected(cli_nps->cli)) {
377 tevent_req_error(req, ENOTCONN);
378 return tevent_req_post(req, ev);
382 * we make a copy of the vector so we can change the structure
384 state->vector = talloc_array(state, struct iovec, count);
385 if (tevent_req_nomem(state->vector, req)) {
386 return tevent_req_post(req, ev);
388 memcpy(state->vector, vector, sizeof(struct iovec) * count);
389 state->count = count;
391 tstream_cli_np_writev_write_next(req);
392 if (!tevent_req_is_in_progress(req)) {
393 return tevent_req_post(req, ev);
396 return req;
399 static void tstream_cli_np_readv_trans_start(struct tevent_req *req);
400 static void tstream_cli_np_writev_write_done(struct tevent_req *subreq);
402 static void tstream_cli_np_writev_write_next(struct tevent_req *req)
404 struct tstream_cli_np_writev_state *state =
405 tevent_req_data(req,
406 struct tstream_cli_np_writev_state);
407 struct tstream_cli_np *cli_nps =
408 tstream_context_data(state->stream,
409 struct tstream_cli_np);
410 struct tevent_req *subreq;
411 size_t i;
412 size_t left = 0;
414 for (i=0; i < state->count; i++) {
415 left += state->vector[i].iov_len;
418 if (left == 0) {
419 TALLOC_FREE(cli_nps->write.buf);
420 tevent_req_done(req);
421 return;
424 cli_nps->write.ofs = 0;
425 cli_nps->write.left = MIN(left, TSTREAM_CLI_NP_MAX_BUF_SIZE);
426 cli_nps->write.buf = talloc_realloc(cli_nps, cli_nps->write.buf,
427 uint8_t, cli_nps->write.left);
428 if (tevent_req_nomem(cli_nps->write.buf, req)) {
429 return;
433 * copy the pending buffer first
435 while (cli_nps->write.left > 0 && state->count > 0) {
436 uint8_t *base = (uint8_t *)state->vector[0].iov_base;
437 size_t len = MIN(cli_nps->write.left, state->vector[0].iov_len);
439 memcpy(cli_nps->write.buf + cli_nps->write.ofs, base, len);
441 base += len;
442 state->vector[0].iov_base = base;
443 state->vector[0].iov_len -= len;
445 cli_nps->write.ofs += len;
446 cli_nps->write.left -= len;
448 if (state->vector[0].iov_len == 0) {
449 state->vector += 1;
450 state->count -= 1;
453 state->ret += len;
456 if (cli_nps->trans.active && state->count == 0) {
457 cli_nps->trans.active = false;
458 cli_nps->trans.write_req = req;
459 return;
462 if (cli_nps->trans.read_req && state->count == 0) {
463 cli_nps->trans.write_req = req;
464 tstream_cli_np_readv_trans_start(cli_nps->trans.read_req);
465 return;
468 if (cli_nps->is_smb1) {
469 subreq = cli_write_andx_send(state, state->ev, cli_nps->cli,
470 cli_nps->fnum,
471 8, /* 8 means message mode. */
472 cli_nps->write.buf,
473 0, /* offset */
474 cli_nps->write.ofs); /* size */
475 } else {
476 subreq = smb2cli_write_send(state, state->ev,
477 cli_nps->cli->conn,
478 cli_nps->cli->timeout,
479 cli_nps->cli->smb2.session,
480 cli_nps->cli->smb2.tid,
481 cli_nps->write.ofs, /* length */
482 0, /* offset */
483 cli_nps->fid_persistent,
484 cli_nps->fid_volatile,
485 0, /* remaining_bytes */
486 0, /* flags */
487 cli_nps->write.buf);
489 if (tevent_req_nomem(subreq, req)) {
490 return;
492 tevent_req_set_callback(subreq,
493 tstream_cli_np_writev_write_done,
494 req);
497 static void tstream_cli_np_writev_disconnect_now(struct tevent_req *req,
498 int error,
499 const char *location);
501 static void tstream_cli_np_writev_write_done(struct tevent_req *subreq)
503 struct tevent_req *req =
504 tevent_req_callback_data(subreq, struct tevent_req);
505 struct tstream_cli_np_writev_state *state =
506 tevent_req_data(req, struct tstream_cli_np_writev_state);
507 struct tstream_cli_np *cli_nps =
508 tstream_context_data(state->stream,
509 struct tstream_cli_np);
510 size_t written;
511 NTSTATUS status;
513 if (cli_nps->is_smb1) {
514 status = cli_write_andx_recv(subreq, &written);
515 } else {
516 status = smb2cli_write_recv(subreq);
517 written = cli_nps->write.ofs; // TODO: get the value from the server
519 TALLOC_FREE(subreq);
520 if (!NT_STATUS_IS_OK(status)) {
521 tstream_cli_np_writev_disconnect_now(req, EIO, __location__);
522 return;
525 if (written != cli_nps->write.ofs) {
526 tstream_cli_np_writev_disconnect_now(req, EIO, __location__);
527 return;
530 tstream_cli_np_writev_write_next(req);
533 static void tstream_cli_np_writev_disconnect_done(struct tevent_req *subreq);
535 static void tstream_cli_np_writev_disconnect_now(struct tevent_req *req,
536 int error,
537 const char *location)
539 struct tstream_cli_np_writev_state *state =
540 tevent_req_data(req,
541 struct tstream_cli_np_writev_state);
542 struct tstream_cli_np *cli_nps =
543 tstream_context_data(state->stream,
544 struct tstream_cli_np);
545 struct tevent_req *subreq;
547 state->error.val = error;
548 state->error.location = location;
550 if (!cli_state_is_connected(cli_nps->cli)) {
551 /* return the original error */
552 _tevent_req_error(req, state->error.val, state->error.location);
553 return;
556 if (cli_nps->is_smb1) {
557 subreq = cli_close_send(state, state->ev, cli_nps->cli,
558 cli_nps->fnum);
559 } else {
560 subreq = smb2cli_close_send(state, state->ev,
561 cli_nps->cli->conn,
562 cli_nps->cli->timeout,
563 cli_nps->cli->smb2.session,
564 cli_nps->cli->smb2.tid,
565 0, /* flags */
566 cli_nps->fid_persistent,
567 cli_nps->fid_volatile);
569 if (subreq == NULL) {
570 /* return the original error */
571 _tevent_req_error(req, state->error.val, state->error.location);
572 return;
574 tevent_req_set_callback(subreq,
575 tstream_cli_np_writev_disconnect_done,
576 req);
579 static void tstream_cli_np_writev_disconnect_done(struct tevent_req *subreq)
581 struct tevent_req *req =
582 tevent_req_callback_data(subreq, struct tevent_req);
583 struct tstream_cli_np_writev_state *state =
584 tevent_req_data(req, struct tstream_cli_np_writev_state);
585 struct tstream_cli_np *cli_nps =
586 tstream_context_data(state->stream, struct tstream_cli_np);
588 if (cli_nps->is_smb1) {
589 cli_close_recv(subreq);
590 } else {
591 smb2cli_close_recv(subreq);
593 TALLOC_FREE(subreq);
595 cli_nps->cli = NULL;
597 /* return the original error */
598 _tevent_req_error(req, state->error.val, state->error.location);
601 static int tstream_cli_np_writev_recv(struct tevent_req *req,
602 int *perrno)
604 struct tstream_cli_np_writev_state *state =
605 tevent_req_data(req,
606 struct tstream_cli_np_writev_state);
607 int ret;
609 ret = tsocket_simple_int_recv(req, perrno);
610 if (ret == 0) {
611 ret = state->ret;
614 tevent_req_received(req);
615 return ret;
618 struct tstream_cli_np_readv_state {
619 struct tstream_context *stream;
620 struct tevent_context *ev;
622 struct iovec *vector;
623 size_t count;
625 int ret;
627 struct {
628 struct tevent_immediate *im;
629 } trans;
631 struct {
632 int val;
633 const char *location;
634 } error;
637 static int tstream_cli_np_readv_state_destructor(struct tstream_cli_np_readv_state *state)
639 struct tstream_cli_np *cli_nps =
640 tstream_context_data(state->stream,
641 struct tstream_cli_np);
643 cli_nps->trans.read_req = NULL;
645 return 0;
648 static void tstream_cli_np_readv_read_next(struct tevent_req *req);
650 static struct tevent_req *tstream_cli_np_readv_send(TALLOC_CTX *mem_ctx,
651 struct tevent_context *ev,
652 struct tstream_context *stream,
653 struct iovec *vector,
654 size_t count)
656 struct tevent_req *req;
657 struct tstream_cli_np_readv_state *state;
658 struct tstream_cli_np *cli_nps =
659 tstream_context_data(stream, struct tstream_cli_np);
661 req = tevent_req_create(mem_ctx, &state,
662 struct tstream_cli_np_readv_state);
663 if (!req) {
664 return NULL;
666 state->stream = stream;
667 state->ev = ev;
668 state->ret = 0;
670 talloc_set_destructor(state, tstream_cli_np_readv_state_destructor);
672 if (!cli_state_is_connected(cli_nps->cli)) {
673 tevent_req_error(req, ENOTCONN);
674 return tevent_req_post(req, ev);
678 * we make a copy of the vector so we can change the structure
680 state->vector = talloc_array(state, struct iovec, count);
681 if (tevent_req_nomem(state->vector, req)) {
682 return tevent_req_post(req, ev);
684 memcpy(state->vector, vector, sizeof(struct iovec) * count);
685 state->count = count;
687 tstream_cli_np_readv_read_next(req);
688 if (!tevent_req_is_in_progress(req)) {
689 return tevent_req_post(req, ev);
692 return req;
695 static void tstream_cli_np_readv_read_done(struct tevent_req *subreq);
697 static void tstream_cli_np_readv_read_next(struct tevent_req *req)
699 struct tstream_cli_np_readv_state *state =
700 tevent_req_data(req,
701 struct tstream_cli_np_readv_state);
702 struct tstream_cli_np *cli_nps =
703 tstream_context_data(state->stream,
704 struct tstream_cli_np);
705 struct tevent_req *subreq;
708 * copy the pending buffer first
710 while (cli_nps->read.left > 0 && state->count > 0) {
711 uint8_t *base = (uint8_t *)state->vector[0].iov_base;
712 size_t len = MIN(cli_nps->read.left, state->vector[0].iov_len);
714 memcpy(base, cli_nps->read.buf + cli_nps->read.ofs, len);
716 base += len;
717 state->vector[0].iov_base = base;
718 state->vector[0].iov_len -= len;
720 cli_nps->read.ofs += len;
721 cli_nps->read.left -= len;
723 if (state->vector[0].iov_len == 0) {
724 state->vector += 1;
725 state->count -= 1;
728 state->ret += len;
731 if (cli_nps->read.left == 0) {
732 TALLOC_FREE(cli_nps->read.buf);
735 if (state->count == 0) {
736 tevent_req_done(req);
737 return;
740 if (cli_nps->trans.active) {
741 cli_nps->trans.active = false;
742 cli_nps->trans.read_req = req;
743 return;
746 if (cli_nps->trans.write_req) {
747 cli_nps->trans.read_req = req;
748 tstream_cli_np_readv_trans_start(req);
749 return;
752 if (cli_nps->is_smb1) {
753 subreq = cli_read_andx_send(state, state->ev, cli_nps->cli,
754 cli_nps->fnum,
755 0, /* offset */
756 TSTREAM_CLI_NP_MAX_BUF_SIZE);
757 } else {
758 subreq = smb2cli_read_send(state, state->ev,
759 cli_nps->cli->conn,
760 cli_nps->cli->timeout,
761 cli_nps->cli->smb2.session,
762 cli_nps->cli->smb2.tid,
763 TSTREAM_CLI_NP_MAX_BUF_SIZE, /* length */
764 0, /* offset */
765 cli_nps->fid_persistent,
766 cli_nps->fid_volatile,
767 0, /* minimum_count */
768 0); /* remaining_bytes */
770 if (tevent_req_nomem(subreq, req)) {
771 return;
773 tevent_req_set_callback(subreq,
774 tstream_cli_np_readv_read_done,
775 req);
778 static void tstream_cli_np_readv_trans_done(struct tevent_req *subreq);
780 static void tstream_cli_np_readv_trans_start(struct tevent_req *req)
782 struct tstream_cli_np_readv_state *state =
783 tevent_req_data(req,
784 struct tstream_cli_np_readv_state);
785 struct tstream_cli_np *cli_nps =
786 tstream_context_data(state->stream,
787 struct tstream_cli_np);
788 struct tevent_req *subreq;
790 state->trans.im = tevent_create_immediate(state);
791 if (tevent_req_nomem(state->trans.im, req)) {
792 return;
795 if (cli_nps->is_smb1) {
796 subreq = cli_trans_send(state, state->ev,
797 cli_nps->cli,
798 SMBtrans,
799 "\\PIPE\\",
800 0, 0, 0,
801 cli_nps->trans.setup, 2,
803 NULL, 0, 0,
804 cli_nps->write.buf,
805 cli_nps->write.ofs,
806 TSTREAM_CLI_NP_MAX_BUF_SIZE);
807 } else {
808 DATA_BLOB in_input_buffer = data_blob_null;
809 DATA_BLOB in_output_buffer = data_blob_null;
811 in_input_buffer = data_blob_const(cli_nps->write.buf,
812 cli_nps->write.ofs);
814 subreq = smb2cli_ioctl_send(state, state->ev,
815 cli_nps->cli->conn,
816 cli_nps->cli->timeout,
817 cli_nps->cli->smb2.session,
818 cli_nps->cli->smb2.tid,
819 cli_nps->fid_persistent,
820 cli_nps->fid_volatile,
821 FSCTL_NAMED_PIPE_READ_WRITE,
822 0, /* in_max_input_length */
823 &in_input_buffer,
824 /* in_max_output_length */
825 TSTREAM_CLI_NP_MAX_BUF_SIZE,
826 &in_output_buffer,
827 SMB2_IOCTL_FLAG_IS_FSCTL);
829 if (tevent_req_nomem(subreq, req)) {
830 return;
832 tevent_req_set_callback(subreq,
833 tstream_cli_np_readv_trans_done,
834 req);
837 static void tstream_cli_np_readv_disconnect_now(struct tevent_req *req,
838 int error,
839 const char *location);
840 static void tstream_cli_np_readv_trans_next(struct tevent_context *ctx,
841 struct tevent_immediate *im,
842 void *private_data);
844 static void tstream_cli_np_readv_trans_done(struct tevent_req *subreq)
846 struct tevent_req *req =
847 tevent_req_callback_data(subreq, struct tevent_req);
848 struct tstream_cli_np_readv_state *state =
849 tevent_req_data(req, struct tstream_cli_np_readv_state);
850 struct tstream_cli_np *cli_nps =
851 tstream_context_data(state->stream, struct tstream_cli_np);
852 uint8_t *rcvbuf;
853 uint32_t received;
854 NTSTATUS status;
856 if (cli_nps->is_smb1) {
857 status = cli_trans_recv(subreq, state, NULL, NULL, 0, NULL,
858 NULL, 0, NULL,
859 &rcvbuf, 0, &received);
860 } else {
861 DATA_BLOB out_input_buffer = data_blob_null;
862 DATA_BLOB out_output_buffer = data_blob_null;
864 status = smb2cli_ioctl_recv(subreq, state,
865 &out_input_buffer,
866 &out_output_buffer);
868 /* Note that rcvbuf is not a talloc pointer here */
869 rcvbuf = out_output_buffer.data;
870 received = out_output_buffer.length;
872 TALLOC_FREE(subreq);
873 if (NT_STATUS_EQUAL(status, NT_STATUS_BUFFER_TOO_SMALL)) {
874 status = NT_STATUS_OK;
876 if (!NT_STATUS_IS_OK(status)) {
877 tstream_cli_np_readv_disconnect_now(req, EIO, __location__);
878 return;
881 if (received > TSTREAM_CLI_NP_MAX_BUF_SIZE) {
882 tstream_cli_np_readv_disconnect_now(req, EIO, __location__);
883 return;
886 if (received == 0) {
887 tstream_cli_np_readv_disconnect_now(req, EPIPE, __location__);
888 return;
891 cli_nps->read.ofs = 0;
892 cli_nps->read.left = received;
893 cli_nps->read.buf = talloc_array(cli_nps, uint8_t, received);
894 if (cli_nps->read.buf == NULL) {
895 TALLOC_FREE(subreq);
896 tevent_req_nomem(cli_nps->read.buf, req);
897 return;
899 memcpy(cli_nps->read.buf, rcvbuf, received);
901 if (cli_nps->trans.write_req == NULL) {
902 tstream_cli_np_readv_read_next(req);
903 return;
906 tevent_schedule_immediate(state->trans.im, state->ev,
907 tstream_cli_np_readv_trans_next, req);
909 tevent_req_done(cli_nps->trans.write_req);
912 static void tstream_cli_np_readv_trans_next(struct tevent_context *ctx,
913 struct tevent_immediate *im,
914 void *private_data)
916 struct tevent_req *req =
917 talloc_get_type_abort(private_data,
918 struct tevent_req);
920 tstream_cli_np_readv_read_next(req);
923 static void tstream_cli_np_readv_read_done(struct tevent_req *subreq)
925 struct tevent_req *req =
926 tevent_req_callback_data(subreq, struct tevent_req);
927 struct tstream_cli_np_readv_state *state =
928 tevent_req_data(req, struct tstream_cli_np_readv_state);
929 struct tstream_cli_np *cli_nps =
930 tstream_context_data(state->stream, struct tstream_cli_np);
931 uint8_t *rcvbuf;
932 ssize_t received;
933 NTSTATUS status;
936 * We must free subreq in this function as there is
937 * a timer event attached to it.
940 if (cli_nps->is_smb1) {
941 status = cli_read_andx_recv(subreq, &received, &rcvbuf);
942 } else {
943 uint32_t data_length = 0;
944 status = smb2cli_read_recv(subreq, state, &rcvbuf, &data_length);
945 received = data_length;
948 * We can't TALLOC_FREE(subreq) as usual here, as rcvbuf still is a
949 * child of that.
951 if (NT_STATUS_EQUAL(status, NT_STATUS_BUFFER_TOO_SMALL)) {
953 * NT_STATUS_BUFFER_TOO_SMALL means that there's
954 * more data to read when the named pipe is used
955 * in message mode (which is the case here).
957 * But we hide this from the caller.
959 status = NT_STATUS_OK;
961 if (!NT_STATUS_IS_OK(status)) {
962 TALLOC_FREE(subreq);
963 tstream_cli_np_readv_disconnect_now(req, EIO, __location__);
964 return;
967 if (received > TSTREAM_CLI_NP_MAX_BUF_SIZE) {
968 TALLOC_FREE(subreq);
969 tstream_cli_np_readv_disconnect_now(req, EIO, __location__);
970 return;
973 if (received == 0) {
974 TALLOC_FREE(subreq);
975 tstream_cli_np_readv_disconnect_now(req, EPIPE, __location__);
976 return;
979 cli_nps->read.ofs = 0;
980 cli_nps->read.left = received;
981 cli_nps->read.buf = talloc_array(cli_nps, uint8_t, received);
982 if (cli_nps->read.buf == NULL) {
983 TALLOC_FREE(subreq);
984 tevent_req_nomem(cli_nps->read.buf, req);
985 return;
987 memcpy(cli_nps->read.buf, rcvbuf, received);
988 TALLOC_FREE(subreq);
990 tstream_cli_np_readv_read_next(req);
993 static void tstream_cli_np_readv_disconnect_done(struct tevent_req *subreq);
995 static void tstream_cli_np_readv_error(struct tevent_req *req);
997 static void tstream_cli_np_readv_disconnect_now(struct tevent_req *req,
998 int error,
999 const char *location)
1001 struct tstream_cli_np_readv_state *state =
1002 tevent_req_data(req,
1003 struct tstream_cli_np_readv_state);
1004 struct tstream_cli_np *cli_nps =
1005 tstream_context_data(state->stream,
1006 struct tstream_cli_np);
1007 struct tevent_req *subreq;
1009 state->error.val = error;
1010 state->error.location = location;
1012 if (!cli_state_is_connected(cli_nps->cli)) {
1013 /* return the original error */
1014 tstream_cli_np_readv_error(req);
1015 return;
1018 if (cli_nps->is_smb1) {
1019 subreq = cli_close_send(state, state->ev, cli_nps->cli,
1020 cli_nps->fnum);
1021 } else {
1022 subreq = smb2cli_close_send(state, state->ev,
1023 cli_nps->cli->conn,
1024 cli_nps->cli->timeout,
1025 cli_nps->cli->smb2.session,
1026 cli_nps->cli->smb2.tid,
1027 0, /* flags */
1028 cli_nps->fid_persistent,
1029 cli_nps->fid_volatile);
1031 if (subreq == NULL) {
1032 /* return the original error */
1033 tstream_cli_np_readv_error(req);
1034 return;
1036 tevent_req_set_callback(subreq,
1037 tstream_cli_np_readv_disconnect_done,
1038 req);
1041 static void tstream_cli_np_readv_disconnect_done(struct tevent_req *subreq)
1043 struct tevent_req *req =
1044 tevent_req_callback_data(subreq, struct tevent_req);
1045 struct tstream_cli_np_readv_state *state =
1046 tevent_req_data(req, struct tstream_cli_np_readv_state);
1047 struct tstream_cli_np *cli_nps =
1048 tstream_context_data(state->stream, struct tstream_cli_np);
1050 if (cli_nps->is_smb1) {
1051 cli_close_recv(subreq);
1052 } else {
1053 smb2cli_close_recv(subreq);
1055 TALLOC_FREE(subreq);
1057 cli_nps->cli = NULL;
1059 tstream_cli_np_readv_error(req);
1062 static void tstream_cli_np_readv_error_trigger(struct tevent_context *ctx,
1063 struct tevent_immediate *im,
1064 void *private_data);
1066 static void tstream_cli_np_readv_error(struct tevent_req *req)
1068 struct tstream_cli_np_readv_state *state =
1069 tevent_req_data(req,
1070 struct tstream_cli_np_readv_state);
1071 struct tstream_cli_np *cli_nps =
1072 tstream_context_data(state->stream,
1073 struct tstream_cli_np);
1075 if (cli_nps->trans.write_req == NULL) {
1076 /* return the original error */
1077 _tevent_req_error(req, state->error.val, state->error.location);
1078 return;
1081 if (state->trans.im == NULL) {
1082 /* return the original error */
1083 _tevent_req_error(req, state->error.val, state->error.location);
1084 return;
1087 tevent_schedule_immediate(state->trans.im, state->ev,
1088 tstream_cli_np_readv_error_trigger, req);
1090 /* return the original error for writev */
1091 _tevent_req_error(cli_nps->trans.write_req,
1092 state->error.val, state->error.location);
1095 static void tstream_cli_np_readv_error_trigger(struct tevent_context *ctx,
1096 struct tevent_immediate *im,
1097 void *private_data)
1099 struct tevent_req *req =
1100 talloc_get_type_abort(private_data,
1101 struct tevent_req);
1102 struct tstream_cli_np_readv_state *state =
1103 tevent_req_data(req,
1104 struct tstream_cli_np_readv_state);
1106 /* return the original error */
1107 _tevent_req_error(req, state->error.val, state->error.location);
1110 static int tstream_cli_np_readv_recv(struct tevent_req *req,
1111 int *perrno)
1113 struct tstream_cli_np_readv_state *state =
1114 tevent_req_data(req, struct tstream_cli_np_readv_state);
1115 int ret;
1117 ret = tsocket_simple_int_recv(req, perrno);
1118 if (ret == 0) {
1119 ret = state->ret;
1122 tevent_req_received(req);
1123 return ret;
1126 struct tstream_cli_np_disconnect_state {
1127 struct tstream_context *stream;
1130 static void tstream_cli_np_disconnect_done(struct tevent_req *subreq);
1132 static struct tevent_req *tstream_cli_np_disconnect_send(TALLOC_CTX *mem_ctx,
1133 struct tevent_context *ev,
1134 struct tstream_context *stream)
1136 struct tstream_cli_np *cli_nps = tstream_context_data(stream,
1137 struct tstream_cli_np);
1138 struct tevent_req *req;
1139 struct tstream_cli_np_disconnect_state *state;
1140 struct tevent_req *subreq;
1142 req = tevent_req_create(mem_ctx, &state,
1143 struct tstream_cli_np_disconnect_state);
1144 if (req == NULL) {
1145 return NULL;
1148 state->stream = stream;
1150 if (!cli_state_is_connected(cli_nps->cli)) {
1151 tevent_req_error(req, ENOTCONN);
1152 return tevent_req_post(req, ev);
1155 if (cli_nps->is_smb1) {
1156 subreq = cli_close_send(state, ev, cli_nps->cli,
1157 cli_nps->fnum);
1158 } else {
1159 subreq = smb2cli_close_send(state, ev, cli_nps->cli->conn,
1160 cli_nps->cli->timeout,
1161 cli_nps->cli->smb2.session,
1162 cli_nps->cli->smb2.tid,
1163 0, /* flags */
1164 cli_nps->fid_persistent,
1165 cli_nps->fid_volatile);
1167 if (tevent_req_nomem(subreq, req)) {
1168 return tevent_req_post(req, ev);
1170 tevent_req_set_callback(subreq, tstream_cli_np_disconnect_done, req);
1172 return req;
1175 static void tstream_cli_np_disconnect_done(struct tevent_req *subreq)
1177 struct tevent_req *req = tevent_req_callback_data(subreq,
1178 struct tevent_req);
1179 struct tstream_cli_np_disconnect_state *state =
1180 tevent_req_data(req, struct tstream_cli_np_disconnect_state);
1181 struct tstream_cli_np *cli_nps =
1182 tstream_context_data(state->stream, struct tstream_cli_np);
1183 NTSTATUS status;
1185 if (cli_nps->is_smb1) {
1186 status = cli_close_recv(subreq);
1187 } else {
1188 status = smb2cli_close_recv(subreq);
1190 TALLOC_FREE(subreq);
1191 if (!NT_STATUS_IS_OK(status)) {
1192 tevent_req_error(req, EIO);
1193 return;
1196 cli_nps->cli = NULL;
1198 tevent_req_done(req);
1201 static int tstream_cli_np_disconnect_recv(struct tevent_req *req,
1202 int *perrno)
1204 int ret;
1206 ret = tsocket_simple_int_recv(req, perrno);
1208 tevent_req_received(req);
1209 return ret;
1212 static const struct tstream_context_ops tstream_cli_np_ops = {
1213 .name = "cli_np",
1215 .pending_bytes = tstream_cli_np_pending_bytes,
1217 .readv_send = tstream_cli_np_readv_send,
1218 .readv_recv = tstream_cli_np_readv_recv,
1220 .writev_send = tstream_cli_np_writev_send,
1221 .writev_recv = tstream_cli_np_writev_recv,
1223 .disconnect_send = tstream_cli_np_disconnect_send,
1224 .disconnect_recv = tstream_cli_np_disconnect_recv,