f3a9962f82345ce0fc22fa8821c74b85ed04692b
[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 cli_state *tstream_cli_np_get_cli_state(struct tstream_context *stream)
334 {
335         struct tstream_cli_np *cli_nps = tstream_context_data(stream,
336                                          struct tstream_cli_np);
337
338         return cli_nps->cli;
339 }
340
341 struct tstream_cli_np_writev_state {
342         struct tstream_context *stream;
343         struct tevent_context *ev;
344
345         struct iovec *vector;
346         size_t count;
347
348         int ret;
349
350         struct {
351                 int val;
352                 const char *location;
353         } error;
354 };
355
356 static int tstream_cli_np_writev_state_destructor(struct tstream_cli_np_writev_state *state)
357 {
358         struct tstream_cli_np *cli_nps =
359                 tstream_context_data(state->stream,
360                 struct tstream_cli_np);
361
362         cli_nps->trans.write_req = NULL;
363
364         return 0;
365 }
366
367 static void tstream_cli_np_writev_write_next(struct tevent_req *req);
368
369 static struct tevent_req *tstream_cli_np_writev_send(TALLOC_CTX *mem_ctx,
370                                         struct tevent_context *ev,
371                                         struct tstream_context *stream,
372                                         const struct iovec *vector,
373                                         size_t count)
374 {
375         struct tevent_req *req;
376         struct tstream_cli_np_writev_state *state;
377         struct tstream_cli_np *cli_nps = tstream_context_data(stream,
378                                          struct tstream_cli_np);
379
380         req = tevent_req_create(mem_ctx, &state,
381                                 struct tstream_cli_np_writev_state);
382         if (!req) {
383                 return NULL;
384         }
385         state->stream = stream;
386         state->ev = ev;
387         state->ret = 0;
388
389         talloc_set_destructor(state, tstream_cli_np_writev_state_destructor);
390
391         if (!cli_state_is_connected(cli_nps->cli)) {
392                 tevent_req_error(req, ENOTCONN);
393                 return tevent_req_post(req, ev);
394         }
395
396         /*
397          * we make a copy of the vector so we can change the structure
398          */
399         state->vector = talloc_array(state, struct iovec, count);
400         if (tevent_req_nomem(state->vector, req)) {
401                 return tevent_req_post(req, ev);
402         }
403         memcpy(state->vector, vector, sizeof(struct iovec) * count);
404         state->count = count;
405
406         tstream_cli_np_writev_write_next(req);
407         if (!tevent_req_is_in_progress(req)) {
408                 return tevent_req_post(req, ev);
409         }
410
411         return req;
412 }
413
414 static void tstream_cli_np_readv_trans_start(struct tevent_req *req);
415 static void tstream_cli_np_writev_write_done(struct tevent_req *subreq);
416
417 static void tstream_cli_np_writev_write_next(struct tevent_req *req)
418 {
419         struct tstream_cli_np_writev_state *state =
420                 tevent_req_data(req,
421                 struct tstream_cli_np_writev_state);
422         struct tstream_cli_np *cli_nps =
423                 tstream_context_data(state->stream,
424                 struct tstream_cli_np);
425         struct tevent_req *subreq;
426         size_t i;
427         size_t left = 0;
428
429         for (i=0; i < state->count; i++) {
430                 left += state->vector[i].iov_len;
431         }
432
433         if (left == 0) {
434                 TALLOC_FREE(cli_nps->write.buf);
435                 tevent_req_done(req);
436                 return;
437         }
438
439         cli_nps->write.ofs = 0;
440         cli_nps->write.left = MIN(left, TSTREAM_CLI_NP_MAX_BUF_SIZE);
441         cli_nps->write.buf = talloc_realloc(cli_nps, cli_nps->write.buf,
442                                             uint8_t, cli_nps->write.left);
443         if (tevent_req_nomem(cli_nps->write.buf, req)) {
444                 return;
445         }
446
447         /*
448          * copy the pending buffer first
449          */
450         while (cli_nps->write.left > 0 && state->count > 0) {
451                 uint8_t *base = (uint8_t *)state->vector[0].iov_base;
452                 size_t len = MIN(cli_nps->write.left, state->vector[0].iov_len);
453
454                 memcpy(cli_nps->write.buf + cli_nps->write.ofs, base, len);
455
456                 base += len;
457                 state->vector[0].iov_base = base;
458                 state->vector[0].iov_len -= len;
459
460                 cli_nps->write.ofs += len;
461                 cli_nps->write.left -= len;
462
463                 if (state->vector[0].iov_len == 0) {
464                         state->vector += 1;
465                         state->count -= 1;
466                 }
467
468                 state->ret += len;
469         }
470
471         if (cli_nps->trans.active && state->count == 0) {
472                 cli_nps->trans.active = false;
473                 cli_nps->trans.write_req = req;
474                 return;
475         }
476
477         if (cli_nps->trans.read_req && state->count == 0) {
478                 cli_nps->trans.write_req = req;
479                 tstream_cli_np_readv_trans_start(cli_nps->trans.read_req);
480                 return;
481         }
482
483         if (cli_nps->is_smb1) {
484                 subreq = cli_write_andx_send(state, state->ev, cli_nps->cli,
485                                              cli_nps->fnum,
486                                              8, /* 8 means message mode. */
487                                              cli_nps->write.buf,
488                                              0, /* offset */
489                                              cli_nps->write.ofs); /* size */
490         } else {
491                 subreq = smb2cli_write_send(state, state->ev,
492                                             cli_nps->cli->conn,
493                                             cli_nps->cli->timeout,
494                                             cli_nps->cli->smb2.session,
495                                             cli_nps->cli->smb2.tcon,
496                                             cli_nps->write.ofs, /* length */
497                                             0, /* offset */
498                                             cli_nps->fid_persistent,
499                                             cli_nps->fid_volatile,
500                                             0, /* remaining_bytes */
501                                             0, /* flags */
502                                             cli_nps->write.buf);
503         }
504         if (tevent_req_nomem(subreq, req)) {
505                 return;
506         }
507         tevent_req_set_callback(subreq,
508                                 tstream_cli_np_writev_write_done,
509                                 req);
510 }
511
512 static void tstream_cli_np_writev_disconnect_now(struct tevent_req *req,
513                                                  int error,
514                                                  const char *location);
515
516 static void tstream_cli_np_writev_write_done(struct tevent_req *subreq)
517 {
518         struct tevent_req *req =
519                 tevent_req_callback_data(subreq, struct tevent_req);
520         struct tstream_cli_np_writev_state *state =
521                 tevent_req_data(req, struct tstream_cli_np_writev_state);
522         struct tstream_cli_np *cli_nps =
523                 tstream_context_data(state->stream,
524                 struct tstream_cli_np);
525         size_t written;
526         NTSTATUS status;
527
528         if (cli_nps->is_smb1) {
529                 status = cli_write_andx_recv(subreq, &written);
530         } else {
531                 uint32_t smb2_written;
532                 status = smb2cli_write_recv(subreq, &smb2_written);
533                 if (NT_STATUS_IS_OK(status)) {
534                         written = smb2_written;
535                 }
536         }
537         TALLOC_FREE(subreq);
538         if (!NT_STATUS_IS_OK(status)) {
539                 tstream_cli_np_writev_disconnect_now(req, EIO, __location__);
540                 return;
541         }
542
543         if (written != cli_nps->write.ofs) {
544                 tstream_cli_np_writev_disconnect_now(req, EIO, __location__);
545                 return;
546         }
547
548         tstream_cli_np_writev_write_next(req);
549 }
550
551 static void tstream_cli_np_writev_disconnect_done(struct tevent_req *subreq);
552
553 static void tstream_cli_np_writev_disconnect_now(struct tevent_req *req,
554                                                  int error,
555                                                  const char *location)
556 {
557         struct tstream_cli_np_writev_state *state =
558                 tevent_req_data(req,
559                 struct tstream_cli_np_writev_state);
560         struct tstream_cli_np *cli_nps =
561                 tstream_context_data(state->stream,
562                 struct tstream_cli_np);
563         struct tevent_req *subreq;
564
565         state->error.val = error;
566         state->error.location = location;
567
568         if (!cli_state_is_connected(cli_nps->cli)) {
569                 /* return the original error */
570                 _tevent_req_error(req, state->error.val, state->error.location);
571                 return;
572         }
573
574         if (cli_nps->is_smb1) {
575                 subreq = cli_close_send(state, state->ev, cli_nps->cli,
576                                         cli_nps->fnum);
577         } else {
578                 subreq = smb2cli_close_send(state, state->ev,
579                                             cli_nps->cli->conn,
580                                             cli_nps->cli->timeout,
581                                             cli_nps->cli->smb2.session,
582                                             cli_nps->cli->smb2.tcon,
583                                             0, /* flags */
584                                             cli_nps->fid_persistent,
585                                             cli_nps->fid_volatile);
586         }
587         if (subreq == NULL) {
588                 /* return the original error */
589                 _tevent_req_error(req, state->error.val, state->error.location);
590                 return;
591         }
592         tevent_req_set_callback(subreq,
593                                 tstream_cli_np_writev_disconnect_done,
594                                 req);
595 }
596
597 static void tstream_cli_np_writev_disconnect_done(struct tevent_req *subreq)
598 {
599         struct tevent_req *req =
600                 tevent_req_callback_data(subreq, struct tevent_req);
601         struct tstream_cli_np_writev_state *state =
602                 tevent_req_data(req, struct tstream_cli_np_writev_state);
603         struct tstream_cli_np *cli_nps =
604                 tstream_context_data(state->stream, struct tstream_cli_np);
605
606         if (cli_nps->is_smb1) {
607                 cli_close_recv(subreq);
608         } else {
609                 smb2cli_close_recv(subreq);
610         }
611         TALLOC_FREE(subreq);
612
613         cli_nps->cli = NULL;
614
615         /* return the original error */
616         _tevent_req_error(req, state->error.val, state->error.location);
617 }
618
619 static int tstream_cli_np_writev_recv(struct tevent_req *req,
620                                       int *perrno)
621 {
622         struct tstream_cli_np_writev_state *state =
623                 tevent_req_data(req,
624                 struct tstream_cli_np_writev_state);
625         int ret;
626
627         ret = tsocket_simple_int_recv(req, perrno);
628         if (ret == 0) {
629                 ret = state->ret;
630         }
631
632         tevent_req_received(req);
633         return ret;
634 }
635
636 struct tstream_cli_np_readv_state {
637         struct tstream_context *stream;
638         struct tevent_context *ev;
639
640         struct iovec *vector;
641         size_t count;
642
643         int ret;
644
645         struct {
646                 struct tevent_immediate *im;
647         } trans;
648
649         struct {
650                 int val;
651                 const char *location;
652         } error;
653 };
654
655 static int tstream_cli_np_readv_state_destructor(struct tstream_cli_np_readv_state *state)
656 {
657         struct tstream_cli_np *cli_nps =
658                 tstream_context_data(state->stream,
659                 struct tstream_cli_np);
660
661         cli_nps->trans.read_req = NULL;
662
663         return 0;
664 }
665
666 static void tstream_cli_np_readv_read_next(struct tevent_req *req);
667
668 static struct tevent_req *tstream_cli_np_readv_send(TALLOC_CTX *mem_ctx,
669                                         struct tevent_context *ev,
670                                         struct tstream_context *stream,
671                                         struct iovec *vector,
672                                         size_t count)
673 {
674         struct tevent_req *req;
675         struct tstream_cli_np_readv_state *state;
676         struct tstream_cli_np *cli_nps =
677                 tstream_context_data(stream, struct tstream_cli_np);
678
679         req = tevent_req_create(mem_ctx, &state,
680                                 struct tstream_cli_np_readv_state);
681         if (!req) {
682                 return NULL;
683         }
684         state->stream = stream;
685         state->ev = ev;
686         state->ret = 0;
687
688         talloc_set_destructor(state, tstream_cli_np_readv_state_destructor);
689
690         if (!cli_state_is_connected(cli_nps->cli)) {
691                 tevent_req_error(req, ENOTCONN);
692                 return tevent_req_post(req, ev);
693         }
694
695         /*
696          * we make a copy of the vector so we can change the structure
697          */
698         state->vector = talloc_array(state, struct iovec, count);
699         if (tevent_req_nomem(state->vector, req)) {
700                 return tevent_req_post(req, ev);
701         }
702         memcpy(state->vector, vector, sizeof(struct iovec) * count);
703         state->count = count;
704
705         tstream_cli_np_readv_read_next(req);
706         if (!tevent_req_is_in_progress(req)) {
707                 return tevent_req_post(req, ev);
708         }
709
710         return req;
711 }
712
713 static void tstream_cli_np_readv_read_done(struct tevent_req *subreq);
714
715 static void tstream_cli_np_readv_read_next(struct tevent_req *req)
716 {
717         struct tstream_cli_np_readv_state *state =
718                 tevent_req_data(req,
719                 struct tstream_cli_np_readv_state);
720         struct tstream_cli_np *cli_nps =
721                 tstream_context_data(state->stream,
722                 struct tstream_cli_np);
723         struct tevent_req *subreq;
724
725         /*
726          * copy the pending buffer first
727          */
728         while (cli_nps->read.left > 0 && state->count > 0) {
729                 uint8_t *base = (uint8_t *)state->vector[0].iov_base;
730                 size_t len = MIN(cli_nps->read.left, state->vector[0].iov_len);
731
732                 memcpy(base, cli_nps->read.buf + cli_nps->read.ofs, len);
733
734                 base += len;
735                 state->vector[0].iov_base = base;
736                 state->vector[0].iov_len -= len;
737
738                 cli_nps->read.ofs += len;
739                 cli_nps->read.left -= len;
740
741                 if (state->vector[0].iov_len == 0) {
742                         state->vector += 1;
743                         state->count -= 1;
744                 }
745
746                 state->ret += len;
747         }
748
749         if (cli_nps->read.left == 0) {
750                 TALLOC_FREE(cli_nps->read.buf);
751         }
752
753         if (state->count == 0) {
754                 tevent_req_done(req);
755                 return;
756         }
757
758         if (cli_nps->trans.active) {
759                 cli_nps->trans.active = false;
760                 cli_nps->trans.read_req = req;
761                 return;
762         }
763
764         if (cli_nps->trans.write_req) {
765                 cli_nps->trans.read_req = req;
766                 tstream_cli_np_readv_trans_start(req);
767                 return;
768         }
769
770         if (cli_nps->is_smb1) {
771                 subreq = cli_read_andx_send(state, state->ev, cli_nps->cli,
772                                             cli_nps->fnum,
773                                             0, /* offset */
774                                             TSTREAM_CLI_NP_MAX_BUF_SIZE);
775         } else {
776                 subreq = smb2cli_read_send(state, state->ev,
777                                            cli_nps->cli->conn,
778                                            cli_nps->cli->timeout,
779                                            cli_nps->cli->smb2.session,
780                                            cli_nps->cli->smb2.tcon,
781                                            TSTREAM_CLI_NP_MAX_BUF_SIZE, /* length */
782                                            0, /* offset */
783                                            cli_nps->fid_persistent,
784                                            cli_nps->fid_volatile,
785                                            0, /* minimum_count */
786                                            0); /* remaining_bytes */
787         }
788         if (tevent_req_nomem(subreq, req)) {
789                 return;
790         }
791         tevent_req_set_callback(subreq,
792                                 tstream_cli_np_readv_read_done,
793                                 req);
794 }
795
796 static void tstream_cli_np_readv_trans_done(struct tevent_req *subreq);
797
798 static void tstream_cli_np_readv_trans_start(struct tevent_req *req)
799 {
800         struct tstream_cli_np_readv_state *state =
801                 tevent_req_data(req,
802                 struct tstream_cli_np_readv_state);
803         struct tstream_cli_np *cli_nps =
804                 tstream_context_data(state->stream,
805                 struct tstream_cli_np);
806         struct tevent_req *subreq;
807
808         state->trans.im = tevent_create_immediate(state);
809         if (tevent_req_nomem(state->trans.im, req)) {
810                 return;
811         }
812
813         if (cli_nps->is_smb1) {
814                 subreq = cli_trans_send(state, state->ev,
815                                         cli_nps->cli,
816                                         SMBtrans,
817                                         "\\PIPE\\",
818                                         0, 0, 0,
819                                         cli_nps->trans.setup, 2,
820                                         0,
821                                         NULL, 0, 0,
822                                         cli_nps->write.buf,
823                                         cli_nps->write.ofs,
824                                         TSTREAM_CLI_NP_MAX_BUF_SIZE);
825         } else {
826                 DATA_BLOB in_input_buffer = data_blob_null;
827                 DATA_BLOB in_output_buffer = data_blob_null;
828
829                 in_input_buffer = data_blob_const(cli_nps->write.buf,
830                                                   cli_nps->write.ofs);
831
832                 subreq = smb2cli_ioctl_send(state, state->ev,
833                                             cli_nps->cli->conn,
834                                             cli_nps->cli->timeout,
835                                             cli_nps->cli->smb2.session,
836                                             cli_nps->cli->smb2.tcon,
837                                             cli_nps->fid_persistent,
838                                             cli_nps->fid_volatile,
839                                             FSCTL_NAMED_PIPE_READ_WRITE,
840                                             0, /* in_max_input_length */
841                                             &in_input_buffer,
842                                             /* in_max_output_length */
843                                             TSTREAM_CLI_NP_MAX_BUF_SIZE,
844                                             &in_output_buffer,
845                                             SMB2_IOCTL_FLAG_IS_FSCTL);
846         }
847         if (tevent_req_nomem(subreq, req)) {
848                 return;
849         }
850         tevent_req_set_callback(subreq,
851                                 tstream_cli_np_readv_trans_done,
852                                 req);
853 }
854
855 static void tstream_cli_np_readv_disconnect_now(struct tevent_req *req,
856                                                 int error,
857                                                 const char *location);
858 static void tstream_cli_np_readv_trans_next(struct tevent_context *ctx,
859                                             struct tevent_immediate *im,
860                                             void *private_data);
861
862 static void tstream_cli_np_readv_trans_done(struct tevent_req *subreq)
863 {
864         struct tevent_req *req =
865                 tevent_req_callback_data(subreq, struct tevent_req);
866         struct tstream_cli_np_readv_state *state =
867                 tevent_req_data(req, struct tstream_cli_np_readv_state);
868         struct tstream_cli_np *cli_nps =
869                 tstream_context_data(state->stream, struct tstream_cli_np);
870         uint8_t *rcvbuf;
871         uint32_t received;
872         NTSTATUS status;
873
874         if (cli_nps->is_smb1) {
875                 status = cli_trans_recv(subreq, state, NULL, NULL, 0, NULL,
876                                         NULL, 0, NULL,
877                                         &rcvbuf, 0, &received);
878         } else {
879                 DATA_BLOB out_input_buffer = data_blob_null;
880                 DATA_BLOB out_output_buffer = data_blob_null;
881
882                 status = smb2cli_ioctl_recv(subreq, state,
883                                             &out_input_buffer,
884                                             &out_output_buffer);
885
886                 /* Note that rcvbuf is not a talloc pointer here */
887                 rcvbuf = out_output_buffer.data;
888                 received = out_output_buffer.length;
889         }
890         TALLOC_FREE(subreq);
891         if (NT_STATUS_EQUAL(status, NT_STATUS_BUFFER_TOO_SMALL)) {
892                 status = NT_STATUS_OK;
893         }
894         if (!NT_STATUS_IS_OK(status)) {
895                 tstream_cli_np_readv_disconnect_now(req, EIO, __location__);
896                 return;
897         }
898
899         if (received > TSTREAM_CLI_NP_MAX_BUF_SIZE) {
900                 tstream_cli_np_readv_disconnect_now(req, EIO, __location__);
901                 return;
902         }
903
904         if (received == 0) {
905                 tstream_cli_np_readv_disconnect_now(req, EPIPE, __location__);
906                 return;
907         }
908
909         cli_nps->read.ofs = 0;
910         cli_nps->read.left = received;
911         cli_nps->read.buf = talloc_array(cli_nps, uint8_t, received);
912         if (cli_nps->read.buf == NULL) {
913                 TALLOC_FREE(subreq);
914                 tevent_req_nomem(cli_nps->read.buf, req);
915                 return;
916         }
917         memcpy(cli_nps->read.buf, rcvbuf, received);
918
919         if (cli_nps->trans.write_req == NULL) {
920                 tstream_cli_np_readv_read_next(req);
921                 return;
922         }
923
924         tevent_schedule_immediate(state->trans.im, state->ev,
925                                   tstream_cli_np_readv_trans_next, req);
926
927         tevent_req_done(cli_nps->trans.write_req);
928 }
929
930 static void tstream_cli_np_readv_trans_next(struct tevent_context *ctx,
931                                             struct tevent_immediate *im,
932                                             void *private_data)
933 {
934         struct tevent_req *req =
935                 talloc_get_type_abort(private_data,
936                 struct tevent_req);
937
938         tstream_cli_np_readv_read_next(req);
939 }
940
941 static void tstream_cli_np_readv_read_done(struct tevent_req *subreq)
942 {
943         struct tevent_req *req =
944                 tevent_req_callback_data(subreq, struct tevent_req);
945         struct tstream_cli_np_readv_state *state =
946                 tevent_req_data(req, struct tstream_cli_np_readv_state);
947         struct tstream_cli_np *cli_nps =
948                 tstream_context_data(state->stream, struct tstream_cli_np);
949         uint8_t *rcvbuf;
950         ssize_t received;
951         NTSTATUS status;
952
953         /*
954          * We must free subreq in this function as there is
955          * a timer event attached to it.
956          */
957
958         if (cli_nps->is_smb1) {
959                 status = cli_read_andx_recv(subreq, &received, &rcvbuf);
960         } else {
961                 uint32_t data_length = 0;
962                 status = smb2cli_read_recv(subreq, state, &rcvbuf, &data_length);
963                 received = data_length;
964         }
965         /*
966          * We can't TALLOC_FREE(subreq) as usual here, as rcvbuf still is a
967          * child of that.
968          */
969         if (NT_STATUS_EQUAL(status, NT_STATUS_BUFFER_TOO_SMALL)) {
970                 /*
971                  * NT_STATUS_BUFFER_TOO_SMALL means that there's
972                  * more data to read when the named pipe is used
973                  * in message mode (which is the case here).
974                  *
975                  * But we hide this from the caller.
976                  */
977                 status = NT_STATUS_OK;
978         }
979         if (!NT_STATUS_IS_OK(status)) {
980                 TALLOC_FREE(subreq);
981                 tstream_cli_np_readv_disconnect_now(req, EIO, __location__);
982                 return;
983         }
984
985         if (received > TSTREAM_CLI_NP_MAX_BUF_SIZE) {
986                 TALLOC_FREE(subreq);
987                 tstream_cli_np_readv_disconnect_now(req, EIO, __location__);
988                 return;
989         }
990
991         if (received == 0) {
992                 TALLOC_FREE(subreq);
993                 tstream_cli_np_readv_disconnect_now(req, EPIPE, __location__);
994                 return;
995         }
996
997         cli_nps->read.ofs = 0;
998         cli_nps->read.left = received;
999         cli_nps->read.buf = talloc_array(cli_nps, uint8_t, received);
1000         if (cli_nps->read.buf == NULL) {
1001                 TALLOC_FREE(subreq);
1002                 tevent_req_nomem(cli_nps->read.buf, req);
1003                 return;
1004         }
1005         memcpy(cli_nps->read.buf, rcvbuf, received);
1006         TALLOC_FREE(subreq);
1007
1008         tstream_cli_np_readv_read_next(req);
1009 }
1010
1011 static void tstream_cli_np_readv_disconnect_done(struct tevent_req *subreq);
1012
1013 static void tstream_cli_np_readv_error(struct tevent_req *req);
1014
1015 static void tstream_cli_np_readv_disconnect_now(struct tevent_req *req,
1016                                                 int error,
1017                                                 const char *location)
1018 {
1019         struct tstream_cli_np_readv_state *state =
1020                 tevent_req_data(req,
1021                 struct tstream_cli_np_readv_state);
1022         struct tstream_cli_np *cli_nps =
1023                 tstream_context_data(state->stream,
1024                 struct tstream_cli_np);
1025         struct tevent_req *subreq;
1026
1027         state->error.val = error;
1028         state->error.location = location;
1029
1030         if (!cli_state_is_connected(cli_nps->cli)) {
1031                 /* return the original error */
1032                 tstream_cli_np_readv_error(req);
1033                 return;
1034         }
1035
1036         if (cli_nps->is_smb1) {
1037                 subreq = cli_close_send(state, state->ev, cli_nps->cli,
1038                                         cli_nps->fnum);
1039         } else {
1040                 subreq = smb2cli_close_send(state, state->ev,
1041                                             cli_nps->cli->conn,
1042                                             cli_nps->cli->timeout,
1043                                             cli_nps->cli->smb2.session,
1044                                             cli_nps->cli->smb2.tcon,
1045                                             0, /* flags */
1046                                             cli_nps->fid_persistent,
1047                                             cli_nps->fid_volatile);
1048         }
1049         if (subreq == NULL) {
1050                 /* return the original error */
1051                 tstream_cli_np_readv_error(req);
1052                 return;
1053         }
1054         tevent_req_set_callback(subreq,
1055                                 tstream_cli_np_readv_disconnect_done,
1056                                 req);
1057 }
1058
1059 static void tstream_cli_np_readv_disconnect_done(struct tevent_req *subreq)
1060 {
1061         struct tevent_req *req =
1062                 tevent_req_callback_data(subreq, struct tevent_req);
1063         struct tstream_cli_np_readv_state *state =
1064                 tevent_req_data(req, struct tstream_cli_np_readv_state);
1065         struct tstream_cli_np *cli_nps =
1066                 tstream_context_data(state->stream, struct tstream_cli_np);
1067
1068         if (cli_nps->is_smb1) {
1069                 cli_close_recv(subreq);
1070         } else {
1071                 smb2cli_close_recv(subreq);
1072         }
1073         TALLOC_FREE(subreq);
1074
1075         cli_nps->cli = NULL;
1076
1077         tstream_cli_np_readv_error(req);
1078 }
1079
1080 static void tstream_cli_np_readv_error_trigger(struct tevent_context *ctx,
1081                                                struct tevent_immediate *im,
1082                                                void *private_data);
1083
1084 static void tstream_cli_np_readv_error(struct tevent_req *req)
1085 {
1086         struct tstream_cli_np_readv_state *state =
1087                 tevent_req_data(req,
1088                 struct tstream_cli_np_readv_state);
1089         struct tstream_cli_np *cli_nps =
1090                 tstream_context_data(state->stream,
1091                 struct tstream_cli_np);
1092
1093         if (cli_nps->trans.write_req == NULL) {
1094                 /* return the original error */
1095                 _tevent_req_error(req, state->error.val, state->error.location);
1096                 return;
1097         }
1098
1099         if (state->trans.im == NULL) {
1100                 /* return the original error */
1101                 _tevent_req_error(req, state->error.val, state->error.location);
1102                 return;
1103         }
1104
1105         tevent_schedule_immediate(state->trans.im, state->ev,
1106                                   tstream_cli_np_readv_error_trigger, req);
1107
1108         /* return the original error for writev */
1109         _tevent_req_error(cli_nps->trans.write_req,
1110                           state->error.val, state->error.location);
1111 }
1112
1113 static void tstream_cli_np_readv_error_trigger(struct tevent_context *ctx,
1114                                                struct tevent_immediate *im,
1115                                                void *private_data)
1116 {
1117         struct tevent_req *req =
1118                 talloc_get_type_abort(private_data,
1119                 struct tevent_req);
1120         struct tstream_cli_np_readv_state *state =
1121                 tevent_req_data(req,
1122                 struct tstream_cli_np_readv_state);
1123
1124         /* return the original error */
1125         _tevent_req_error(req, state->error.val, state->error.location);
1126 }
1127
1128 static int tstream_cli_np_readv_recv(struct tevent_req *req,
1129                                    int *perrno)
1130 {
1131         struct tstream_cli_np_readv_state *state =
1132                 tevent_req_data(req, struct tstream_cli_np_readv_state);
1133         int ret;
1134
1135         ret = tsocket_simple_int_recv(req, perrno);
1136         if (ret == 0) {
1137                 ret = state->ret;
1138         }
1139
1140         tevent_req_received(req);
1141         return ret;
1142 }
1143
1144 struct tstream_cli_np_disconnect_state {
1145         struct tstream_context *stream;
1146 };
1147
1148 static void tstream_cli_np_disconnect_done(struct tevent_req *subreq);
1149
1150 static struct tevent_req *tstream_cli_np_disconnect_send(TALLOC_CTX *mem_ctx,
1151                                                 struct tevent_context *ev,
1152                                                 struct tstream_context *stream)
1153 {
1154         struct tstream_cli_np *cli_nps = tstream_context_data(stream,
1155                                          struct tstream_cli_np);
1156         struct tevent_req *req;
1157         struct tstream_cli_np_disconnect_state *state;
1158         struct tevent_req *subreq;
1159
1160         req = tevent_req_create(mem_ctx, &state,
1161                                 struct tstream_cli_np_disconnect_state);
1162         if (req == NULL) {
1163                 return NULL;
1164         }
1165
1166         state->stream = stream;
1167
1168         if (!cli_state_is_connected(cli_nps->cli)) {
1169                 tevent_req_error(req, ENOTCONN);
1170                 return tevent_req_post(req, ev);
1171         }
1172
1173         if (cli_nps->is_smb1) {
1174                 subreq = cli_close_send(state, ev, cli_nps->cli,
1175                                         cli_nps->fnum);
1176         } else {
1177                 subreq = smb2cli_close_send(state, ev, cli_nps->cli->conn,
1178                                             cli_nps->cli->timeout,
1179                                             cli_nps->cli->smb2.session,
1180                                             cli_nps->cli->smb2.tcon,
1181                                             0, /* flags */
1182                                             cli_nps->fid_persistent,
1183                                             cli_nps->fid_volatile);
1184         }
1185         if (tevent_req_nomem(subreq, req)) {
1186                 return tevent_req_post(req, ev);
1187         }
1188         tevent_req_set_callback(subreq, tstream_cli_np_disconnect_done, req);
1189
1190         return req;
1191 }
1192
1193 static void tstream_cli_np_disconnect_done(struct tevent_req *subreq)
1194 {
1195         struct tevent_req *req = tevent_req_callback_data(subreq,
1196                                                           struct tevent_req);
1197         struct tstream_cli_np_disconnect_state *state =
1198                 tevent_req_data(req, struct tstream_cli_np_disconnect_state);
1199         struct tstream_cli_np *cli_nps =
1200                 tstream_context_data(state->stream, struct tstream_cli_np);
1201         NTSTATUS status;
1202
1203         if (cli_nps->is_smb1) {
1204                 status = cli_close_recv(subreq);
1205         } else {
1206                 status = smb2cli_close_recv(subreq);
1207         }
1208         TALLOC_FREE(subreq);
1209         if (!NT_STATUS_IS_OK(status)) {
1210                 tevent_req_error(req, EIO);
1211                 return;
1212         }
1213
1214         cli_nps->cli = NULL;
1215
1216         tevent_req_done(req);
1217 }
1218
1219 static int tstream_cli_np_disconnect_recv(struct tevent_req *req,
1220                                           int *perrno)
1221 {
1222         int ret;
1223
1224         ret = tsocket_simple_int_recv(req, perrno);
1225
1226         tevent_req_received(req);
1227         return ret;
1228 }
1229
1230 static const struct tstream_context_ops tstream_cli_np_ops = {
1231         .name                   = "cli_np",
1232
1233         .pending_bytes          = tstream_cli_np_pending_bytes,
1234
1235         .readv_send             = tstream_cli_np_readv_send,
1236         .readv_recv             = tstream_cli_np_readv_recv,
1237
1238         .writev_send            = tstream_cli_np_writev_send,
1239         .writev_recv            = tstream_cli_np_writev_recv,
1240
1241         .disconnect_send        = tstream_cli_np_disconnect_send,
1242         .disconnect_recv        = tstream_cli_np_disconnect_recv,
1243 };