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