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