cli_np_tstream: remove unused tstream_cli_np_get_cli_state()
[mat/samba.git] / source3 / libsmb / cli_np_tstream.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Copyright (C) Stefan Metzmacher 2010
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 "libsmb/libsmb.h"
23 #include "libsmb/smb2cli.h"
24 #include "../libcli/smb/smbXcli_base.h"
25 #include "../lib/util/tevent_ntstatus.h"
26 #include "../lib/tsocket/tsocket.h"
27 #include "../lib/tsocket/tsocket_internal.h"
28 #include "cli_np_tstream.h"
29
30 static const struct tstream_context_ops tstream_cli_np_ops;
31
32 /*
33  * Windows uses 4280 (the max xmit/recv size negotiated on DCERPC).
34  * This is fits into the max_xmit negotiated at the SMB layer.
35  *
36  * On the sending side they may use SMBtranss if the request does not
37  * fit into a single SMBtrans call.
38  *
39  * Windows uses 1024 as max data size of a SMBtrans request and then
40  * possibly reads the rest of the DCERPC fragment (up to 3256 bytes)
41  * via a SMBreadX.
42  *
43  * For now we just ask for the full 4280 bytes (max data size) in the SMBtrans
44  * request to get the whole fragment at once (like samba 3.5.x and below did.
45  *
46  * It is important that we use do SMBwriteX with the size of a full fragment,
47  * otherwise we may get NT_STATUS_PIPE_BUSY on the SMBtrans request
48  * from NT4 servers. (See bug #8195)
49  */
50 #define TSTREAM_CLI_NP_MAX_BUF_SIZE 4280
51
52 struct tstream_cli_np {
53         struct cli_state *cli;
54         const char *npipe;
55         bool is_smb1;
56         uint16_t fnum;
57         uint64_t fid_persistent;
58         uint64_t fid_volatile;
59         unsigned int default_timeout;
60
61         struct {
62                 bool active;
63                 struct tevent_req *read_req;
64                 struct tevent_req *write_req;
65                 uint16_t setup[2];
66         } trans;
67
68         struct {
69                 off_t ofs;
70                 size_t left;
71                 uint8_t *buf;
72         } read, write;
73 };
74
75 static int tstream_cli_np_destructor(struct tstream_cli_np *cli_nps)
76 {
77         NTSTATUS status;
78
79         if (!cli_state_is_connected(cli_nps->cli)) {
80                 return 0;
81         }
82
83         /*
84          * TODO: do not use a sync call with a destructor!!!
85          *
86          * This only happens, if a caller does talloc_free(),
87          * while the everything was still ok.
88          *
89          * If we get an unexpected failure within a normal
90          * operation, we already do an async cli_close_send()/_recv().
91          *
92          * Once we've fixed all callers to call
93          * tstream_disconnect_send()/_recv(), this will
94          * never be called.
95          */
96         if (cli_nps->is_smb1) {
97                 status = cli_close(cli_nps->cli, cli_nps->fnum);
98         } else {
99                 status = smb2cli_close(cli_nps->cli->conn,
100                                        cli_nps->cli->timeout,
101                                        cli_nps->cli->smb2.session,
102                                        cli_nps->cli->smb2.tcon,
103                                        0, /* flags */
104                                        cli_nps->fid_persistent,
105                                        cli_nps->fid_volatile);
106         }
107         if (!NT_STATUS_IS_OK(status)) {
108                 DEBUG(1, ("tstream_cli_np_destructor: cli_close "
109                           "failed on pipe %s. Error was %s\n",
110                           cli_nps->npipe, nt_errstr(status)));
111         }
112         /*
113          * We can't do much on failure
114          */
115         return 0;
116 }
117
118 struct tstream_cli_np_open_state {
119         struct cli_state *cli;
120         bool is_smb1;
121         uint16_t fnum;
122         uint64_t fid_persistent;
123         uint64_t fid_volatile;
124         const char *npipe;
125 };
126
127 static void tstream_cli_np_open_done(struct tevent_req *subreq);
128
129 struct tevent_req *tstream_cli_np_open_send(TALLOC_CTX *mem_ctx,
130                                             struct tevent_context *ev,
131                                             struct cli_state *cli,
132                                             const char *npipe)
133 {
134         struct tevent_req *req;
135         struct tstream_cli_np_open_state *state;
136         struct tevent_req *subreq;
137
138         req = tevent_req_create(mem_ctx, &state,
139                                 struct tstream_cli_np_open_state);
140         if (!req) {
141                 return NULL;
142         }
143         state->cli = cli;
144
145         state->npipe = talloc_strdup(state, npipe);
146         if (tevent_req_nomem(state->npipe, req)) {
147                 return tevent_req_post(req, ev);
148         }
149
150         if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
151                 state->is_smb1 = true;
152         }
153
154         if (state->is_smb1) {
155                 const char *smb1_npipe;
156
157                 /*
158                  * Windows and newer Samba versions allow
159                  * the pipe name without leading backslash,
160                  * but we should better behave like windows clients
161                  */
162                 smb1_npipe = talloc_asprintf(state, "\\%s", state->npipe);
163                 if (tevent_req_nomem(smb1_npipe, req)) {
164                         return tevent_req_post(req, ev);
165                 }
166
167                 subreq = cli_ntcreate_send(state, ev, cli,
168                                            smb1_npipe,
169                                            0,
170                                            DESIRED_ACCESS_PIPE,
171                                            0,
172                                            FILE_SHARE_READ|FILE_SHARE_WRITE,
173                                            FILE_OPEN,
174                                            0,
175                                            0);
176         } else {
177                 subreq = smb2cli_create_send(state, ev, cli->conn,
178                                              cli->timeout, cli->smb2.session,
179                                              cli->smb2.tcon,
180                                              npipe,
181                                              SMB2_OPLOCK_LEVEL_NONE,
182                                              SMB2_IMPERSONATION_IMPERSONATION,
183                                              DESIRED_ACCESS_PIPE,
184                                              0, /* file_attributes */
185                                              FILE_SHARE_READ|FILE_SHARE_WRITE,
186                                              FILE_OPEN,
187                                              0, /* create_options */
188                                              NULL); /* blobs */
189         }
190         if (tevent_req_nomem(subreq, req)) {
191                 return tevent_req_post(req, ev);
192         }
193         tevent_req_set_callback(subreq, tstream_cli_np_open_done, req);
194
195         return req;
196 }
197
198 static void tstream_cli_np_open_done(struct tevent_req *subreq)
199 {
200         struct tevent_req *req =
201                 tevent_req_callback_data(subreq, struct tevent_req);
202         struct tstream_cli_np_open_state *state =
203                 tevent_req_data(req, struct tstream_cli_np_open_state);
204         NTSTATUS status;
205
206         if (state->is_smb1) {
207                 status = cli_ntcreate_recv(subreq, &state->fnum);
208         } else {
209                 status = smb2cli_create_recv(subreq,
210                                              &state->fid_persistent,
211                                              &state->fid_volatile,
212                                              NULL);
213         }
214         TALLOC_FREE(subreq);
215         if (!NT_STATUS_IS_OK(status)) {
216                 tevent_req_nterror(req, status);
217                 return;
218         }
219
220         tevent_req_done(req);
221 }
222
223 NTSTATUS _tstream_cli_np_open_recv(struct tevent_req *req,
224                                    TALLOC_CTX *mem_ctx,
225                                    struct tstream_context **_stream,
226                                    const char *location)
227 {
228         struct tstream_cli_np_open_state *state =
229                 tevent_req_data(req, struct tstream_cli_np_open_state);
230         struct tstream_context *stream;
231         struct tstream_cli_np *cli_nps;
232         NTSTATUS status;
233
234         if (tevent_req_is_nterror(req, &status)) {
235                 tevent_req_received(req);
236                 return status;
237         }
238
239         stream = tstream_context_create(mem_ctx,
240                                         &tstream_cli_np_ops,
241                                         &cli_nps,
242                                         struct tstream_cli_np,
243                                         location);
244         if (!stream) {
245                 tevent_req_received(req);
246                 return NT_STATUS_NO_MEMORY;
247         }
248         ZERO_STRUCTP(cli_nps);
249
250         cli_nps->cli = state->cli;
251         cli_nps->npipe = talloc_move(cli_nps, &state->npipe);
252         cli_nps->is_smb1 = state->is_smb1;
253         cli_nps->fnum = state->fnum;
254         cli_nps->fid_persistent = state->fid_persistent;
255         cli_nps->fid_volatile = state->fid_volatile;
256         cli_nps->default_timeout = cli_set_timeout(state->cli, 0);
257         cli_set_timeout(state->cli, cli_nps->default_timeout);
258
259         talloc_set_destructor(cli_nps, tstream_cli_np_destructor);
260
261         cli_nps->trans.active = false;
262         cli_nps->trans.read_req = NULL;
263         cli_nps->trans.write_req = NULL;
264         SSVAL(cli_nps->trans.setup+0, 0, TRANSACT_DCERPCCMD);
265         SSVAL(cli_nps->trans.setup+1, 0, cli_nps->fnum);
266
267         *_stream = stream;
268         tevent_req_received(req);
269         return NT_STATUS_OK;
270 }
271
272 static ssize_t tstream_cli_np_pending_bytes(struct tstream_context *stream)
273 {
274         struct tstream_cli_np *cli_nps = tstream_context_data(stream,
275                                          struct tstream_cli_np);
276
277         if (!cli_state_is_connected(cli_nps->cli)) {
278                 errno = ENOTCONN;
279                 return -1;
280         }
281
282         return cli_nps->read.left;
283 }
284
285 bool tstream_is_cli_np(struct tstream_context *stream)
286 {
287         struct tstream_cli_np *cli_nps =
288                 talloc_get_type(_tstream_context_data(stream),
289                 struct tstream_cli_np);
290
291         if (!cli_nps) {
292                 return false;
293         }
294
295         return true;
296 }
297
298 NTSTATUS tstream_cli_np_use_trans(struct tstream_context *stream)
299 {
300         struct tstream_cli_np *cli_nps = tstream_context_data(stream,
301                                          struct tstream_cli_np);
302
303         if (cli_nps->trans.read_req) {
304                 return NT_STATUS_PIPE_BUSY;
305         }
306
307         if (cli_nps->trans.write_req) {
308                 return NT_STATUS_PIPE_BUSY;
309         }
310
311         if (cli_nps->trans.active) {
312                 return NT_STATUS_PIPE_BUSY;
313         }
314
315         cli_nps->trans.active = true;
316
317         return NT_STATUS_OK;
318 }
319
320 unsigned int tstream_cli_np_set_timeout(struct tstream_context *stream,
321                                         unsigned int timeout)
322 {
323         struct tstream_cli_np *cli_nps = tstream_context_data(stream,
324                                          struct tstream_cli_np);
325
326         if (!cli_state_is_connected(cli_nps->cli)) {
327                 return cli_nps->default_timeout;
328         }
329
330         return cli_set_timeout(cli_nps->cli, timeout);
331 }
332
333 struct tstream_cli_np_writev_state {
334         struct tstream_context *stream;
335         struct tevent_context *ev;
336
337         struct iovec *vector;
338         size_t count;
339
340         int ret;
341
342         struct {
343                 int val;
344                 const char *location;
345         } error;
346 };
347
348 static int tstream_cli_np_writev_state_destructor(struct tstream_cli_np_writev_state *state)
349 {
350         struct tstream_cli_np *cli_nps =
351                 tstream_context_data(state->stream,
352                 struct tstream_cli_np);
353
354         cli_nps->trans.write_req = NULL;
355
356         return 0;
357 }
358
359 static void tstream_cli_np_writev_write_next(struct tevent_req *req);
360
361 static struct tevent_req *tstream_cli_np_writev_send(TALLOC_CTX *mem_ctx,
362                                         struct tevent_context *ev,
363                                         struct tstream_context *stream,
364                                         const struct iovec *vector,
365                                         size_t count)
366 {
367         struct tevent_req *req;
368         struct tstream_cli_np_writev_state *state;
369         struct tstream_cli_np *cli_nps = tstream_context_data(stream,
370                                          struct tstream_cli_np);
371
372         req = tevent_req_create(mem_ctx, &state,
373                                 struct tstream_cli_np_writev_state);
374         if (!req) {
375                 return NULL;
376         }
377         state->stream = stream;
378         state->ev = ev;
379         state->ret = 0;
380
381         talloc_set_destructor(state, tstream_cli_np_writev_state_destructor);
382
383         if (!cli_state_is_connected(cli_nps->cli)) {
384                 tevent_req_error(req, ENOTCONN);
385                 return tevent_req_post(req, ev);
386         }
387
388         /*
389          * we make a copy of the vector so we can change the structure
390          */
391         state->vector = talloc_array(state, struct iovec, count);
392         if (tevent_req_nomem(state->vector, req)) {
393                 return tevent_req_post(req, ev);
394         }
395         memcpy(state->vector, vector, sizeof(struct iovec) * count);
396         state->count = count;
397
398         tstream_cli_np_writev_write_next(req);
399         if (!tevent_req_is_in_progress(req)) {
400                 return tevent_req_post(req, ev);
401         }
402
403         return req;
404 }
405
406 static void tstream_cli_np_readv_trans_start(struct tevent_req *req);
407 static void tstream_cli_np_writev_write_done(struct tevent_req *subreq);
408
409 static void tstream_cli_np_writev_write_next(struct tevent_req *req)
410 {
411         struct tstream_cli_np_writev_state *state =
412                 tevent_req_data(req,
413                 struct tstream_cli_np_writev_state);
414         struct tstream_cli_np *cli_nps =
415                 tstream_context_data(state->stream,
416                 struct tstream_cli_np);
417         struct tevent_req *subreq;
418         size_t i;
419         size_t left = 0;
420
421         for (i=0; i < state->count; i++) {
422                 left += state->vector[i].iov_len;
423         }
424
425         if (left == 0) {
426                 TALLOC_FREE(cli_nps->write.buf);
427                 tevent_req_done(req);
428                 return;
429         }
430
431         cli_nps->write.ofs = 0;
432         cli_nps->write.left = MIN(left, TSTREAM_CLI_NP_MAX_BUF_SIZE);
433         cli_nps->write.buf = talloc_realloc(cli_nps, cli_nps->write.buf,
434                                             uint8_t, cli_nps->write.left);
435         if (tevent_req_nomem(cli_nps->write.buf, req)) {
436                 return;
437         }
438
439         /*
440          * copy the pending buffer first
441          */
442         while (cli_nps->write.left > 0 && state->count > 0) {
443                 uint8_t *base = (uint8_t *)state->vector[0].iov_base;
444                 size_t len = MIN(cli_nps->write.left, state->vector[0].iov_len);
445
446                 memcpy(cli_nps->write.buf + cli_nps->write.ofs, base, len);
447
448                 base += len;
449                 state->vector[0].iov_base = base;
450                 state->vector[0].iov_len -= len;
451
452                 cli_nps->write.ofs += len;
453                 cli_nps->write.left -= len;
454
455                 if (state->vector[0].iov_len == 0) {
456                         state->vector += 1;
457                         state->count -= 1;
458                 }
459
460                 state->ret += len;
461         }
462
463         if (cli_nps->trans.active && state->count == 0) {
464                 cli_nps->trans.active = false;
465                 cli_nps->trans.write_req = req;
466                 return;
467         }
468
469         if (cli_nps->trans.read_req && state->count == 0) {
470                 cli_nps->trans.write_req = req;
471                 tstream_cli_np_readv_trans_start(cli_nps->trans.read_req);
472                 return;
473         }
474
475         if (cli_nps->is_smb1) {
476                 subreq = cli_write_andx_send(state, state->ev, cli_nps->cli,
477                                              cli_nps->fnum,
478                                              8, /* 8 means message mode. */
479                                              cli_nps->write.buf,
480                                              0, /* offset */
481                                              cli_nps->write.ofs); /* size */
482         } else {
483                 subreq = smb2cli_write_send(state, state->ev,
484                                             cli_nps->cli->conn,
485                                             cli_nps->cli->timeout,
486                                             cli_nps->cli->smb2.session,
487                                             cli_nps->cli->smb2.tcon,
488                                             cli_nps->write.ofs, /* length */
489                                             0, /* offset */
490                                             cli_nps->fid_persistent,
491                                             cli_nps->fid_volatile,
492                                             0, /* remaining_bytes */
493                                             0, /* flags */
494                                             cli_nps->write.buf);
495         }
496         if (tevent_req_nomem(subreq, req)) {
497                 return;
498         }
499         tevent_req_set_callback(subreq,
500                                 tstream_cli_np_writev_write_done,
501                                 req);
502 }
503
504 static void tstream_cli_np_writev_disconnect_now(struct tevent_req *req,
505                                                  int error,
506                                                  const char *location);
507
508 static void tstream_cli_np_writev_write_done(struct tevent_req *subreq)
509 {
510         struct tevent_req *req =
511                 tevent_req_callback_data(subreq, struct tevent_req);
512         struct tstream_cli_np_writev_state *state =
513                 tevent_req_data(req, struct tstream_cli_np_writev_state);
514         struct tstream_cli_np *cli_nps =
515                 tstream_context_data(state->stream,
516                 struct tstream_cli_np);
517         size_t written;
518         NTSTATUS status;
519
520         if (cli_nps->is_smb1) {
521                 status = cli_write_andx_recv(subreq, &written);
522         } else {
523                 uint32_t smb2_written;
524                 status = smb2cli_write_recv(subreq, &smb2_written);
525                 if (NT_STATUS_IS_OK(status)) {
526                         written = smb2_written;
527                 }
528         }
529         TALLOC_FREE(subreq);
530         if (!NT_STATUS_IS_OK(status)) {
531                 tstream_cli_np_writev_disconnect_now(req, EIO, __location__);
532                 return;
533         }
534
535         if (written != cli_nps->write.ofs) {
536                 tstream_cli_np_writev_disconnect_now(req, EIO, __location__);
537                 return;
538         }
539
540         tstream_cli_np_writev_write_next(req);
541 }
542
543 static void tstream_cli_np_writev_disconnect_done(struct tevent_req *subreq);
544
545 static void tstream_cli_np_writev_disconnect_now(struct tevent_req *req,
546                                                  int error,
547                                                  const char *location)
548 {
549         struct tstream_cli_np_writev_state *state =
550                 tevent_req_data(req,
551                 struct tstream_cli_np_writev_state);
552         struct tstream_cli_np *cli_nps =
553                 tstream_context_data(state->stream,
554                 struct tstream_cli_np);
555         struct tevent_req *subreq;
556
557         state->error.val = error;
558         state->error.location = location;
559
560         if (!cli_state_is_connected(cli_nps->cli)) {
561                 /* return the original error */
562                 _tevent_req_error(req, state->error.val, state->error.location);
563                 return;
564         }
565
566         if (cli_nps->is_smb1) {
567                 subreq = cli_close_send(state, state->ev, cli_nps->cli,
568                                         cli_nps->fnum);
569         } else {
570                 subreq = smb2cli_close_send(state, state->ev,
571                                             cli_nps->cli->conn,
572                                             cli_nps->cli->timeout,
573                                             cli_nps->cli->smb2.session,
574                                             cli_nps->cli->smb2.tcon,
575                                             0, /* flags */
576                                             cli_nps->fid_persistent,
577                                             cli_nps->fid_volatile);
578         }
579         if (subreq == NULL) {
580                 /* return the original error */
581                 _tevent_req_error(req, state->error.val, state->error.location);
582                 return;
583         }
584         tevent_req_set_callback(subreq,
585                                 tstream_cli_np_writev_disconnect_done,
586                                 req);
587 }
588
589 static void tstream_cli_np_writev_disconnect_done(struct tevent_req *subreq)
590 {
591         struct tevent_req *req =
592                 tevent_req_callback_data(subreq, struct tevent_req);
593         struct tstream_cli_np_writev_state *state =
594                 tevent_req_data(req, struct tstream_cli_np_writev_state);
595         struct tstream_cli_np *cli_nps =
596                 tstream_context_data(state->stream, struct tstream_cli_np);
597
598         if (cli_nps->is_smb1) {
599                 cli_close_recv(subreq);
600         } else {
601                 smb2cli_close_recv(subreq);
602         }
603         TALLOC_FREE(subreq);
604
605         cli_nps->cli = NULL;
606
607         /* return the original error */
608         _tevent_req_error(req, state->error.val, state->error.location);
609 }
610
611 static int tstream_cli_np_writev_recv(struct tevent_req *req,
612                                       int *perrno)
613 {
614         struct tstream_cli_np_writev_state *state =
615                 tevent_req_data(req,
616                 struct tstream_cli_np_writev_state);
617         int ret;
618
619         ret = tsocket_simple_int_recv(req, perrno);
620         if (ret == 0) {
621                 ret = state->ret;
622         }
623
624         tevent_req_received(req);
625         return ret;
626 }
627
628 struct tstream_cli_np_readv_state {
629         struct tstream_context *stream;
630         struct tevent_context *ev;
631
632         struct iovec *vector;
633         size_t count;
634
635         int ret;
636
637         struct {
638                 struct tevent_immediate *im;
639         } trans;
640
641         struct {
642                 int val;
643                 const char *location;
644         } error;
645 };
646
647 static int tstream_cli_np_readv_state_destructor(struct tstream_cli_np_readv_state *state)
648 {
649         struct tstream_cli_np *cli_nps =
650                 tstream_context_data(state->stream,
651                 struct tstream_cli_np);
652
653         cli_nps->trans.read_req = NULL;
654
655         return 0;
656 }
657
658 static void tstream_cli_np_readv_read_next(struct tevent_req *req);
659
660 static struct tevent_req *tstream_cli_np_readv_send(TALLOC_CTX *mem_ctx,
661                                         struct tevent_context *ev,
662                                         struct tstream_context *stream,
663                                         struct iovec *vector,
664                                         size_t count)
665 {
666         struct tevent_req *req;
667         struct tstream_cli_np_readv_state *state;
668         struct tstream_cli_np *cli_nps =
669                 tstream_context_data(stream, struct tstream_cli_np);
670
671         req = tevent_req_create(mem_ctx, &state,
672                                 struct tstream_cli_np_readv_state);
673         if (!req) {
674                 return NULL;
675         }
676         state->stream = stream;
677         state->ev = ev;
678         state->ret = 0;
679
680         talloc_set_destructor(state, tstream_cli_np_readv_state_destructor);
681
682         if (!cli_state_is_connected(cli_nps->cli)) {
683                 tevent_req_error(req, ENOTCONN);
684                 return tevent_req_post(req, ev);
685         }
686
687         /*
688          * we make a copy of the vector so we can change the structure
689          */
690         state->vector = talloc_array(state, struct iovec, count);
691         if (tevent_req_nomem(state->vector, req)) {
692                 return tevent_req_post(req, ev);
693         }
694         memcpy(state->vector, vector, sizeof(struct iovec) * count);
695         state->count = count;
696
697         tstream_cli_np_readv_read_next(req);
698         if (!tevent_req_is_in_progress(req)) {
699                 return tevent_req_post(req, ev);
700         }
701
702         return req;
703 }
704
705 static void tstream_cli_np_readv_read_done(struct tevent_req *subreq);
706
707 static void tstream_cli_np_readv_read_next(struct tevent_req *req)
708 {
709         struct tstream_cli_np_readv_state *state =
710                 tevent_req_data(req,
711                 struct tstream_cli_np_readv_state);
712         struct tstream_cli_np *cli_nps =
713                 tstream_context_data(state->stream,
714                 struct tstream_cli_np);
715         struct tevent_req *subreq;
716
717         /*
718          * copy the pending buffer first
719          */
720         while (cli_nps->read.left > 0 && state->count > 0) {
721                 uint8_t *base = (uint8_t *)state->vector[0].iov_base;
722                 size_t len = MIN(cli_nps->read.left, state->vector[0].iov_len);
723
724                 memcpy(base, cli_nps->read.buf + cli_nps->read.ofs, len);
725
726                 base += len;
727                 state->vector[0].iov_base = base;
728                 state->vector[0].iov_len -= len;
729
730                 cli_nps->read.ofs += len;
731                 cli_nps->read.left -= len;
732
733                 if (state->vector[0].iov_len == 0) {
734                         state->vector += 1;
735                         state->count -= 1;
736                 }
737
738                 state->ret += len;
739         }
740
741         if (cli_nps->read.left == 0) {
742                 TALLOC_FREE(cli_nps->read.buf);
743         }
744
745         if (state->count == 0) {
746                 tevent_req_done(req);
747                 return;
748         }
749
750         if (cli_nps->trans.active) {
751                 cli_nps->trans.active = false;
752                 cli_nps->trans.read_req = req;
753                 return;
754         }
755
756         if (cli_nps->trans.write_req) {
757                 cli_nps->trans.read_req = req;
758                 tstream_cli_np_readv_trans_start(req);
759                 return;
760         }
761
762         if (cli_nps->is_smb1) {
763                 subreq = cli_read_andx_send(state, state->ev, cli_nps->cli,
764                                             cli_nps->fnum,
765                                             0, /* offset */
766                                             TSTREAM_CLI_NP_MAX_BUF_SIZE);
767         } else {
768                 subreq = smb2cli_read_send(state, state->ev,
769                                            cli_nps->cli->conn,
770                                            cli_nps->cli->timeout,
771                                            cli_nps->cli->smb2.session,
772                                            cli_nps->cli->smb2.tcon,
773                                            TSTREAM_CLI_NP_MAX_BUF_SIZE, /* length */
774                                            0, /* offset */
775                                            cli_nps->fid_persistent,
776                                            cli_nps->fid_volatile,
777                                            0, /* minimum_count */
778                                            0); /* remaining_bytes */
779         }
780         if (tevent_req_nomem(subreq, req)) {
781                 return;
782         }
783         tevent_req_set_callback(subreq,
784                                 tstream_cli_np_readv_read_done,
785                                 req);
786 }
787
788 static void tstream_cli_np_readv_trans_done(struct tevent_req *subreq);
789
790 static void tstream_cli_np_readv_trans_start(struct tevent_req *req)
791 {
792         struct tstream_cli_np_readv_state *state =
793                 tevent_req_data(req,
794                 struct tstream_cli_np_readv_state);
795         struct tstream_cli_np *cli_nps =
796                 tstream_context_data(state->stream,
797                 struct tstream_cli_np);
798         struct tevent_req *subreq;
799
800         state->trans.im = tevent_create_immediate(state);
801         if (tevent_req_nomem(state->trans.im, req)) {
802                 return;
803         }
804
805         if (cli_nps->is_smb1) {
806                 subreq = cli_trans_send(state, state->ev,
807                                         cli_nps->cli,
808                                         SMBtrans,
809                                         "\\PIPE\\",
810                                         0, 0, 0,
811                                         cli_nps->trans.setup, 2,
812                                         0,
813                                         NULL, 0, 0,
814                                         cli_nps->write.buf,
815                                         cli_nps->write.ofs,
816                                         TSTREAM_CLI_NP_MAX_BUF_SIZE);
817         } else {
818                 DATA_BLOB in_input_buffer = data_blob_null;
819                 DATA_BLOB in_output_buffer = data_blob_null;
820
821                 in_input_buffer = data_blob_const(cli_nps->write.buf,
822                                                   cli_nps->write.ofs);
823
824                 subreq = smb2cli_ioctl_send(state, state->ev,
825                                             cli_nps->cli->conn,
826                                             cli_nps->cli->timeout,
827                                             cli_nps->cli->smb2.session,
828                                             cli_nps->cli->smb2.tcon,
829                                             cli_nps->fid_persistent,
830                                             cli_nps->fid_volatile,
831                                             FSCTL_NAMED_PIPE_READ_WRITE,
832                                             0, /* in_max_input_length */
833                                             &in_input_buffer,
834                                             /* in_max_output_length */
835                                             TSTREAM_CLI_NP_MAX_BUF_SIZE,
836                                             &in_output_buffer,
837                                             SMB2_IOCTL_FLAG_IS_FSCTL);
838         }
839         if (tevent_req_nomem(subreq, req)) {
840                 return;
841         }
842         tevent_req_set_callback(subreq,
843                                 tstream_cli_np_readv_trans_done,
844                                 req);
845 }
846
847 static void tstream_cli_np_readv_disconnect_now(struct tevent_req *req,
848                                                 int error,
849                                                 const char *location);
850 static void tstream_cli_np_readv_trans_next(struct tevent_context *ctx,
851                                             struct tevent_immediate *im,
852                                             void *private_data);
853
854 static void tstream_cli_np_readv_trans_done(struct tevent_req *subreq)
855 {
856         struct tevent_req *req =
857                 tevent_req_callback_data(subreq, struct tevent_req);
858         struct tstream_cli_np_readv_state *state =
859                 tevent_req_data(req, struct tstream_cli_np_readv_state);
860         struct tstream_cli_np *cli_nps =
861                 tstream_context_data(state->stream, struct tstream_cli_np);
862         uint8_t *rcvbuf;
863         uint32_t received;
864         NTSTATUS status;
865
866         if (cli_nps->is_smb1) {
867                 status = cli_trans_recv(subreq, state, NULL, NULL, 0, NULL,
868                                         NULL, 0, NULL,
869                                         &rcvbuf, 0, &received);
870         } else {
871                 DATA_BLOB out_input_buffer = data_blob_null;
872                 DATA_BLOB out_output_buffer = data_blob_null;
873
874                 status = smb2cli_ioctl_recv(subreq, state,
875                                             &out_input_buffer,
876                                             &out_output_buffer);
877
878                 /* Note that rcvbuf is not a talloc pointer here */
879                 rcvbuf = out_output_buffer.data;
880                 received = out_output_buffer.length;
881         }
882         TALLOC_FREE(subreq);
883         if (NT_STATUS_EQUAL(status, NT_STATUS_BUFFER_TOO_SMALL)) {
884                 status = NT_STATUS_OK;
885         }
886         if (!NT_STATUS_IS_OK(status)) {
887                 tstream_cli_np_readv_disconnect_now(req, EIO, __location__);
888                 return;
889         }
890
891         if (received > TSTREAM_CLI_NP_MAX_BUF_SIZE) {
892                 tstream_cli_np_readv_disconnect_now(req, EIO, __location__);
893                 return;
894         }
895
896         if (received == 0) {
897                 tstream_cli_np_readv_disconnect_now(req, EPIPE, __location__);
898                 return;
899         }
900
901         cli_nps->read.ofs = 0;
902         cli_nps->read.left = received;
903         cli_nps->read.buf = talloc_array(cli_nps, uint8_t, received);
904         if (cli_nps->read.buf == NULL) {
905                 TALLOC_FREE(subreq);
906                 tevent_req_nomem(cli_nps->read.buf, req);
907                 return;
908         }
909         memcpy(cli_nps->read.buf, rcvbuf, received);
910
911         if (cli_nps->trans.write_req == NULL) {
912                 tstream_cli_np_readv_read_next(req);
913                 return;
914         }
915
916         tevent_schedule_immediate(state->trans.im, state->ev,
917                                   tstream_cli_np_readv_trans_next, req);
918
919         tevent_req_done(cli_nps->trans.write_req);
920 }
921
922 static void tstream_cli_np_readv_trans_next(struct tevent_context *ctx,
923                                             struct tevent_immediate *im,
924                                             void *private_data)
925 {
926         struct tevent_req *req =
927                 talloc_get_type_abort(private_data,
928                 struct tevent_req);
929
930         tstream_cli_np_readv_read_next(req);
931 }
932
933 static void tstream_cli_np_readv_read_done(struct tevent_req *subreq)
934 {
935         struct tevent_req *req =
936                 tevent_req_callback_data(subreq, struct tevent_req);
937         struct tstream_cli_np_readv_state *state =
938                 tevent_req_data(req, struct tstream_cli_np_readv_state);
939         struct tstream_cli_np *cli_nps =
940                 tstream_context_data(state->stream, struct tstream_cli_np);
941         uint8_t *rcvbuf;
942         ssize_t received;
943         NTSTATUS status;
944
945         /*
946          * We must free subreq in this function as there is
947          * a timer event attached to it.
948          */
949
950         if (cli_nps->is_smb1) {
951                 status = cli_read_andx_recv(subreq, &received, &rcvbuf);
952         } else {
953                 uint32_t data_length = 0;
954                 status = smb2cli_read_recv(subreq, state, &rcvbuf, &data_length);
955                 received = data_length;
956         }
957         /*
958          * We can't TALLOC_FREE(subreq) as usual here, as rcvbuf still is a
959          * child of that.
960          */
961         if (NT_STATUS_EQUAL(status, NT_STATUS_BUFFER_TOO_SMALL)) {
962                 /*
963                  * NT_STATUS_BUFFER_TOO_SMALL means that there's
964                  * more data to read when the named pipe is used
965                  * in message mode (which is the case here).
966                  *
967                  * But we hide this from the caller.
968                  */
969                 status = NT_STATUS_OK;
970         }
971         if (!NT_STATUS_IS_OK(status)) {
972                 TALLOC_FREE(subreq);
973                 tstream_cli_np_readv_disconnect_now(req, EIO, __location__);
974                 return;
975         }
976
977         if (received > TSTREAM_CLI_NP_MAX_BUF_SIZE) {
978                 TALLOC_FREE(subreq);
979                 tstream_cli_np_readv_disconnect_now(req, EIO, __location__);
980                 return;
981         }
982
983         if (received == 0) {
984                 TALLOC_FREE(subreq);
985                 tstream_cli_np_readv_disconnect_now(req, EPIPE, __location__);
986                 return;
987         }
988
989         cli_nps->read.ofs = 0;
990         cli_nps->read.left = received;
991         cli_nps->read.buf = talloc_array(cli_nps, uint8_t, received);
992         if (cli_nps->read.buf == NULL) {
993                 TALLOC_FREE(subreq);
994                 tevent_req_nomem(cli_nps->read.buf, req);
995                 return;
996         }
997         memcpy(cli_nps->read.buf, rcvbuf, received);
998         TALLOC_FREE(subreq);
999
1000         tstream_cli_np_readv_read_next(req);
1001 }
1002
1003 static void tstream_cli_np_readv_disconnect_done(struct tevent_req *subreq);
1004
1005 static void tstream_cli_np_readv_error(struct tevent_req *req);
1006
1007 static void tstream_cli_np_readv_disconnect_now(struct tevent_req *req,
1008                                                 int error,
1009                                                 const char *location)
1010 {
1011         struct tstream_cli_np_readv_state *state =
1012                 tevent_req_data(req,
1013                 struct tstream_cli_np_readv_state);
1014         struct tstream_cli_np *cli_nps =
1015                 tstream_context_data(state->stream,
1016                 struct tstream_cli_np);
1017         struct tevent_req *subreq;
1018
1019         state->error.val = error;
1020         state->error.location = location;
1021
1022         if (!cli_state_is_connected(cli_nps->cli)) {
1023                 /* return the original error */
1024                 tstream_cli_np_readv_error(req);
1025                 return;
1026         }
1027
1028         if (cli_nps->is_smb1) {
1029                 subreq = cli_close_send(state, state->ev, cli_nps->cli,
1030                                         cli_nps->fnum);
1031         } else {
1032                 subreq = smb2cli_close_send(state, state->ev,
1033                                             cli_nps->cli->conn,
1034                                             cli_nps->cli->timeout,
1035                                             cli_nps->cli->smb2.session,
1036                                             cli_nps->cli->smb2.tcon,
1037                                             0, /* flags */
1038                                             cli_nps->fid_persistent,
1039                                             cli_nps->fid_volatile);
1040         }
1041         if (subreq == NULL) {
1042                 /* return the original error */
1043                 tstream_cli_np_readv_error(req);
1044                 return;
1045         }
1046         tevent_req_set_callback(subreq,
1047                                 tstream_cli_np_readv_disconnect_done,
1048                                 req);
1049 }
1050
1051 static void tstream_cli_np_readv_disconnect_done(struct tevent_req *subreq)
1052 {
1053         struct tevent_req *req =
1054                 tevent_req_callback_data(subreq, struct tevent_req);
1055         struct tstream_cli_np_readv_state *state =
1056                 tevent_req_data(req, struct tstream_cli_np_readv_state);
1057         struct tstream_cli_np *cli_nps =
1058                 tstream_context_data(state->stream, struct tstream_cli_np);
1059
1060         if (cli_nps->is_smb1) {
1061                 cli_close_recv(subreq);
1062         } else {
1063                 smb2cli_close_recv(subreq);
1064         }
1065         TALLOC_FREE(subreq);
1066
1067         cli_nps->cli = NULL;
1068
1069         tstream_cli_np_readv_error(req);
1070 }
1071
1072 static void tstream_cli_np_readv_error_trigger(struct tevent_context *ctx,
1073                                                struct tevent_immediate *im,
1074                                                void *private_data);
1075
1076 static void tstream_cli_np_readv_error(struct tevent_req *req)
1077 {
1078         struct tstream_cli_np_readv_state *state =
1079                 tevent_req_data(req,
1080                 struct tstream_cli_np_readv_state);
1081         struct tstream_cli_np *cli_nps =
1082                 tstream_context_data(state->stream,
1083                 struct tstream_cli_np);
1084
1085         if (cli_nps->trans.write_req == NULL) {
1086                 /* return the original error */
1087                 _tevent_req_error(req, state->error.val, state->error.location);
1088                 return;
1089         }
1090
1091         if (state->trans.im == NULL) {
1092                 /* return the original error */
1093                 _tevent_req_error(req, state->error.val, state->error.location);
1094                 return;
1095         }
1096
1097         tevent_schedule_immediate(state->trans.im, state->ev,
1098                                   tstream_cli_np_readv_error_trigger, req);
1099
1100         /* return the original error for writev */
1101         _tevent_req_error(cli_nps->trans.write_req,
1102                           state->error.val, state->error.location);
1103 }
1104
1105 static void tstream_cli_np_readv_error_trigger(struct tevent_context *ctx,
1106                                                struct tevent_immediate *im,
1107                                                void *private_data)
1108 {
1109         struct tevent_req *req =
1110                 talloc_get_type_abort(private_data,
1111                 struct tevent_req);
1112         struct tstream_cli_np_readv_state *state =
1113                 tevent_req_data(req,
1114                 struct tstream_cli_np_readv_state);
1115
1116         /* return the original error */
1117         _tevent_req_error(req, state->error.val, state->error.location);
1118 }
1119
1120 static int tstream_cli_np_readv_recv(struct tevent_req *req,
1121                                    int *perrno)
1122 {
1123         struct tstream_cli_np_readv_state *state =
1124                 tevent_req_data(req, struct tstream_cli_np_readv_state);
1125         int ret;
1126
1127         ret = tsocket_simple_int_recv(req, perrno);
1128         if (ret == 0) {
1129                 ret = state->ret;
1130         }
1131
1132         tevent_req_received(req);
1133         return ret;
1134 }
1135
1136 struct tstream_cli_np_disconnect_state {
1137         struct tstream_context *stream;
1138 };
1139
1140 static void tstream_cli_np_disconnect_done(struct tevent_req *subreq);
1141
1142 static struct tevent_req *tstream_cli_np_disconnect_send(TALLOC_CTX *mem_ctx,
1143                                                 struct tevent_context *ev,
1144                                                 struct tstream_context *stream)
1145 {
1146         struct tstream_cli_np *cli_nps = tstream_context_data(stream,
1147                                          struct tstream_cli_np);
1148         struct tevent_req *req;
1149         struct tstream_cli_np_disconnect_state *state;
1150         struct tevent_req *subreq;
1151
1152         req = tevent_req_create(mem_ctx, &state,
1153                                 struct tstream_cli_np_disconnect_state);
1154         if (req == NULL) {
1155                 return NULL;
1156         }
1157
1158         state->stream = stream;
1159
1160         if (!cli_state_is_connected(cli_nps->cli)) {
1161                 tevent_req_error(req, ENOTCONN);
1162                 return tevent_req_post(req, ev);
1163         }
1164
1165         if (cli_nps->is_smb1) {
1166                 subreq = cli_close_send(state, ev, cli_nps->cli,
1167                                         cli_nps->fnum);
1168         } else {
1169                 subreq = smb2cli_close_send(state, ev, cli_nps->cli->conn,
1170                                             cli_nps->cli->timeout,
1171                                             cli_nps->cli->smb2.session,
1172                                             cli_nps->cli->smb2.tcon,
1173                                             0, /* flags */
1174                                             cli_nps->fid_persistent,
1175                                             cli_nps->fid_volatile);
1176         }
1177         if (tevent_req_nomem(subreq, req)) {
1178                 return tevent_req_post(req, ev);
1179         }
1180         tevent_req_set_callback(subreq, tstream_cli_np_disconnect_done, req);
1181
1182         return req;
1183 }
1184
1185 static void tstream_cli_np_disconnect_done(struct tevent_req *subreq)
1186 {
1187         struct tevent_req *req = tevent_req_callback_data(subreq,
1188                                                           struct tevent_req);
1189         struct tstream_cli_np_disconnect_state *state =
1190                 tevent_req_data(req, struct tstream_cli_np_disconnect_state);
1191         struct tstream_cli_np *cli_nps =
1192                 tstream_context_data(state->stream, struct tstream_cli_np);
1193         NTSTATUS status;
1194
1195         if (cli_nps->is_smb1) {
1196                 status = cli_close_recv(subreq);
1197         } else {
1198                 status = smb2cli_close_recv(subreq);
1199         }
1200         TALLOC_FREE(subreq);
1201         if (!NT_STATUS_IS_OK(status)) {
1202                 tevent_req_error(req, EIO);
1203                 return;
1204         }
1205
1206         cli_nps->cli = NULL;
1207
1208         tevent_req_done(req);
1209 }
1210
1211 static int tstream_cli_np_disconnect_recv(struct tevent_req *req,
1212                                           int *perrno)
1213 {
1214         int ret;
1215
1216         ret = tsocket_simple_int_recv(req, perrno);
1217
1218         tevent_req_received(req);
1219         return ret;
1220 }
1221
1222 static const struct tstream_context_ops tstream_cli_np_ops = {
1223         .name                   = "cli_np",
1224
1225         .pending_bytes          = tstream_cli_np_pending_bytes,
1226
1227         .readv_send             = tstream_cli_np_readv_send,
1228         .readv_recv             = tstream_cli_np_readv_recv,
1229
1230         .writev_send            = tstream_cli_np_writev_send,
1231         .writev_recv            = tstream_cli_np_writev_recv,
1232
1233         .disconnect_send        = tstream_cli_np_disconnect_send,
1234         .disconnect_recv        = tstream_cli_np_disconnect_recv,
1235 };