smbXcli: cp source3/libsmb/clitrans.c libcli/smb/smb1cli_trans.c
[ira/wip.git] / libcli / smb / smb1cli_trans.c
1 /*
2    Unix SMB/CIFS implementation.
3    client transaction calls
4    Copyright (C) Andrew Tridgell 1994-1998
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 "libsmb/libsmb.h"
22 #include "../lib/util/tevent_ntstatus.h"
23 #include "async_smb.h"
24
25 struct trans_recvblob {
26         uint8_t *data;
27         uint32_t max, total, received;
28 };
29
30 struct cli_trans_state {
31         struct cli_state *cli;
32         struct tevent_context *ev;
33         uint8_t cmd;
34         uint16_t mid;
35         const char *pipe_name;
36         uint8_t *pipe_name_conv;
37         size_t pipe_name_conv_len;
38         uint16_t fid;
39         uint16_t function;
40         int flags;
41         uint16_t *setup;
42         uint8_t num_setup, max_setup;
43         uint8_t *param;
44         uint32_t num_param, param_sent;
45         uint8_t *data;
46         uint32_t num_data, data_sent;
47
48         uint8_t num_rsetup;
49         uint16_t *rsetup;
50         struct trans_recvblob rparam;
51         struct trans_recvblob rdata;
52         uint16_t recv_flags2;
53
54         struct iovec iov[6];
55         uint8_t pad[4];
56         uint8_t zero_pad[4];
57         uint16_t vwv[32];
58
59         struct tevent_req *primary_subreq;
60 };
61
62 static void cli_trans_cleanup_primary(struct cli_trans_state *state)
63 {
64         if (state->primary_subreq) {
65                 cli_smb_req_set_mid(state->primary_subreq, 0);
66                 cli_smb_req_unset_pending(state->primary_subreq);
67                 TALLOC_FREE(state->primary_subreq);
68         }
69 }
70
71 static int cli_trans_state_destructor(struct cli_trans_state *state)
72 {
73         cli_trans_cleanup_primary(state);
74         return 0;
75 }
76
77 static NTSTATUS cli_pull_trans(uint8_t *inbuf,
78                                uint8_t wct, uint16_t *vwv,
79                                uint16_t num_bytes, uint8_t *bytes,
80                                uint8_t smb_cmd, bool expect_first_reply,
81                                uint8_t *pnum_setup, uint16_t **psetup,
82                                uint32_t *ptotal_param, uint32_t *pnum_param,
83                                uint32_t *pparam_disp, uint8_t **pparam,
84                                uint32_t *ptotal_data, uint32_t *pnum_data,
85                                uint32_t *pdata_disp, uint8_t **pdata)
86 {
87         uint32_t param_ofs, data_ofs;
88         uint8_t expected_num_setup;
89
90         if (expect_first_reply) {
91                 if ((wct != 0) || (num_bytes != 0)) {
92                         return NT_STATUS_INVALID_NETWORK_RESPONSE;
93                 }
94                 return NT_STATUS_OK;
95         }
96
97         switch (smb_cmd) {
98         case SMBtrans:
99         case SMBtrans2:
100                 if (wct < 10) {
101                         return NT_STATUS_INVALID_NETWORK_RESPONSE;
102                 }
103                 expected_num_setup = wct - 10;
104                 *ptotal_param   = SVAL(vwv + 0, 0);
105                 *ptotal_data    = SVAL(vwv + 1, 0);
106                 *pnum_param     = SVAL(vwv + 3, 0);
107                 param_ofs       = SVAL(vwv + 4, 0);
108                 *pparam_disp    = SVAL(vwv + 5, 0);
109                 *pnum_data      = SVAL(vwv + 6, 0);
110                 data_ofs        = SVAL(vwv + 7, 0);
111                 *pdata_disp     = SVAL(vwv + 8, 0);
112                 *pnum_setup     = CVAL(vwv + 9, 0);
113                 if (expected_num_setup < (*pnum_setup)) {
114                         return NT_STATUS_INVALID_NETWORK_RESPONSE;
115                 }
116                 *psetup = vwv + 10;
117
118                 break;
119         case SMBnttrans:
120                 if (wct < 18) {
121                         return NT_STATUS_INVALID_NETWORK_RESPONSE;
122                 }
123                 expected_num_setup = wct - 18;
124                 *ptotal_param   = IVAL(vwv, 3);
125                 *ptotal_data    = IVAL(vwv, 7);
126                 *pnum_param     = IVAL(vwv, 11);
127                 param_ofs       = IVAL(vwv, 15);
128                 *pparam_disp    = IVAL(vwv, 19);
129                 *pnum_data      = IVAL(vwv, 23);
130                 data_ofs        = IVAL(vwv, 27);
131                 *pdata_disp     = IVAL(vwv, 31);
132                 *pnum_setup     = CVAL(vwv, 35);
133                 if (expected_num_setup < (*pnum_setup)) {
134                         return NT_STATUS_INVALID_NETWORK_RESPONSE;
135                 }
136                 *psetup         = vwv + 18;
137                 break;
138
139         default:
140                 return NT_STATUS_INTERNAL_ERROR;
141         }
142
143         /*
144          * Check for buffer overflows. data_ofs needs to be checked against
145          * the incoming buffer length, data_disp against the total
146          * length. Likewise for param_ofs/param_disp.
147          */
148
149         if (smb_buffer_oob(smb_len_nbt(inbuf), param_ofs, *pnum_param)
150             || smb_buffer_oob(*ptotal_param, *pparam_disp, *pnum_param)
151             || smb_buffer_oob(smb_len_nbt(inbuf), data_ofs, *pnum_data)
152             || smb_buffer_oob(*ptotal_data, *pdata_disp, *pnum_data)) {
153                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
154         }
155
156         *pparam = (uint8_t *)inbuf + 4 + param_ofs;
157         *pdata = (uint8_t *)inbuf + 4 + data_ofs;
158
159         return NT_STATUS_OK;
160 }
161
162 static NTSTATUS cli_trans_pull_blob(TALLOC_CTX *mem_ctx,
163                                     struct trans_recvblob *blob,
164                                     uint32_t total, uint32_t thistime,
165                                     uint8_t *buf, uint32_t displacement)
166 {
167         if (blob->data == NULL) {
168                 if (total > blob->max) {
169                         return NT_STATUS_INVALID_NETWORK_RESPONSE;
170                 }
171                 blob->total = total;
172                 blob->data = talloc_array(mem_ctx, uint8_t, total);
173                 if (blob->data == NULL) {
174                         return NT_STATUS_NO_MEMORY;
175                 }
176         }
177
178         if (total > blob->total) {
179                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
180         }
181
182         if (thistime) {
183                 memcpy(blob->data + displacement, buf, thistime);
184                 blob->received += thistime;
185         }
186
187         return NT_STATUS_OK;
188 }
189
190 static void cli_trans_format(struct cli_trans_state *state, uint8_t *pwct,
191                              int *piov_count)
192 {
193         struct cli_state *cli = state->cli;
194         uint8_t wct = 0;
195         struct iovec *iov = state->iov;
196         uint8_t *pad = state->pad;
197         uint16_t *vwv = state->vwv;
198         uint32_t param_offset;
199         uint32_t this_param = 0;
200         uint32_t param_pad;
201         uint32_t data_offset;
202         uint32_t this_data = 0;
203         uint32_t data_pad;
204         uint32_t useable_space;
205         uint8_t cmd;
206
207         cmd = state->cmd;
208
209         if ((state->param_sent != 0) || (state->data_sent != 0)) {
210                 /* The secondary commands are one after the primary ones */
211                 cmd += 1;
212         }
213
214         param_offset = MIN_SMB_SIZE;
215
216         switch (cmd) {
217         case SMBtrans:
218                 if (cli_ucs2(state->cli)) {
219                         pad[0] = 0;
220                         iov[0].iov_base = (void *)pad;
221                         iov[0].iov_len = 1;
222                         param_offset += 1;
223                         iov += 1;
224                 }
225                 iov[0].iov_base = (void *)state->pipe_name_conv;
226                 iov[0].iov_len = state->pipe_name_conv_len;
227                 wct = 14 + state->num_setup;
228                 param_offset += iov[0].iov_len;
229                 iov += 1;
230                 break;
231         case SMBtrans2:
232                 pad[0] = 0;
233                 pad[1] = 'D'; /* Copy this from "old" 3.0 behaviour */
234                 pad[2] = ' ';
235                 iov[0].iov_base = (void *)pad;
236                 iov[0].iov_len = 3;
237                 wct = 14 + state->num_setup;
238                 param_offset += 3;
239                 iov += 1;
240                 break;
241         case SMBtranss:
242                 wct = 8;
243                 break;
244         case SMBtranss2:
245                 wct = 9;
246                 break;
247         case SMBnttrans:
248                 wct = 19 + state->num_setup;
249                 break;
250         case SMBnttranss:
251                 wct = 18;
252                 break;
253         }
254
255         param_offset += wct * sizeof(uint16_t);
256         useable_space = cli_state_available_size(cli, param_offset);
257
258         param_pad = param_offset % 4;
259         if (param_pad > 0) {
260                 param_pad = MIN(param_pad, useable_space);
261                 iov[0].iov_base = (void *)state->zero_pad;
262                 iov[0].iov_len = param_pad;
263                 iov += 1;
264                 param_offset += param_pad;
265         }
266         useable_space = cli_state_available_size(cli, param_offset);
267
268         if (state->param_sent < state->num_param) {
269                 this_param = MIN(state->num_param - state->param_sent,
270                                  useable_space);
271                 iov[0].iov_base = (void *)(state->param + state->param_sent);
272                 iov[0].iov_len = this_param;
273                 iov += 1;
274         }
275
276         data_offset = param_offset + this_param;
277         useable_space = cli_state_available_size(cli, data_offset);
278
279         data_pad = data_offset % 4;
280         if (data_pad > 0) {
281                 data_pad = MIN(data_pad, useable_space);
282                 iov[0].iov_base = (void *)state->zero_pad;
283                 iov[0].iov_len = data_pad;
284                 iov += 1;
285                 data_offset += data_pad;
286         }
287         useable_space = cli_state_available_size(cli, data_offset);
288
289         if (state->data_sent < state->num_data) {
290                 this_data = MIN(state->num_data - state->data_sent,
291                                 useable_space);
292                 iov[0].iov_base = (void *)(state->data + state->data_sent);
293                 iov[0].iov_len = this_data;
294                 iov += 1;
295         }
296
297         DEBUG(10, ("num_setup=%u, max_setup=%u, "
298                    "param_total=%u, this_param=%u, max_param=%u, "
299                    "data_total=%u, this_data=%u, max_data=%u, "
300                    "param_offset=%u, param_pad=%u, param_disp=%u, "
301                    "data_offset=%u, data_pad=%u, data_disp=%u\n",
302                    (unsigned)state->num_setup, (unsigned)state->max_setup,
303                    (unsigned)state->num_param, (unsigned)this_param,
304                    (unsigned)state->rparam.max,
305                    (unsigned)state->num_data, (unsigned)this_data,
306                    (unsigned)state->rdata.max,
307                    (unsigned)param_offset, (unsigned)param_pad,
308                    (unsigned)state->param_sent,
309                    (unsigned)data_offset, (unsigned)data_pad,
310                    (unsigned)state->data_sent));
311
312         switch (cmd) {
313         case SMBtrans:
314         case SMBtrans2:
315                 SSVAL(vwv + 0, 0, state->num_param);
316                 SSVAL(vwv + 1, 0, state->num_data);
317                 SSVAL(vwv + 2, 0, state->rparam.max);
318                 SSVAL(vwv + 3, 0, state->rdata.max);
319                 SCVAL(vwv + 4, 0, state->max_setup);
320                 SCVAL(vwv + 4, 1, 0);   /* reserved */
321                 SSVAL(vwv + 5, 0, state->flags);
322                 SIVAL(vwv + 6, 0, 0);   /* timeout */
323                 SSVAL(vwv + 8, 0, 0);   /* reserved */
324                 SSVAL(vwv + 9, 0, this_param);
325                 SSVAL(vwv +10, 0, param_offset);
326                 SSVAL(vwv +11, 0, this_data);
327                 SSVAL(vwv +12, 0, data_offset);
328                 SCVAL(vwv +13, 0, state->num_setup);
329                 SCVAL(vwv +13, 1, 0);   /* reserved */
330                 memcpy(vwv + 14, state->setup,
331                        sizeof(uint16_t) * state->num_setup);
332                 break;
333         case SMBtranss:
334         case SMBtranss2:
335                 SSVAL(vwv + 0, 0, state->num_param);
336                 SSVAL(vwv + 1, 0, state->num_data);
337                 SSVAL(vwv + 2, 0, this_param);
338                 SSVAL(vwv + 3, 0, param_offset);
339                 SSVAL(vwv + 4, 0, state->param_sent);
340                 SSVAL(vwv + 5, 0, this_data);
341                 SSVAL(vwv + 6, 0, data_offset);
342                 SSVAL(vwv + 7, 0, state->data_sent);
343                 if (cmd == SMBtranss2) {
344                         SSVAL(vwv + 8, 0, state->fid);
345                 }
346                 break;
347         case SMBnttrans:
348                 SCVAL(vwv + 0, 0, state->max_setup);
349                 SSVAL(vwv + 0, 1, 0); /* reserved */
350                 SIVAL(vwv + 1, 1, state->num_param);
351                 SIVAL(vwv + 3, 1, state->num_data);
352                 SIVAL(vwv + 5, 1, state->rparam.max);
353                 SIVAL(vwv + 7, 1, state->rdata.max);
354                 SIVAL(vwv + 9, 1, this_param);
355                 SIVAL(vwv +11, 1, param_offset);
356                 SIVAL(vwv +13, 1, this_data);
357                 SIVAL(vwv +15, 1, data_offset);
358                 SCVAL(vwv +17, 1, state->num_setup);
359                 SSVAL(vwv +18, 0, state->function);
360                 memcpy(vwv + 19, state->setup,
361                        sizeof(uint16_t) * state->num_setup);
362                 break;
363         case SMBnttranss:
364                 SSVAL(vwv + 0, 0, 0); /* reserved */
365                 SCVAL(vwv + 1, 0, 0); /* reserved */
366                 SIVAL(vwv + 1, 1, state->num_param);
367                 SIVAL(vwv + 3, 1, state->num_data);
368                 SIVAL(vwv + 5, 1, this_param);
369                 SIVAL(vwv + 7, 1, param_offset);
370                 SIVAL(vwv + 9, 1, state->param_sent);
371                 SIVAL(vwv +11, 1, this_data);
372                 SIVAL(vwv +13, 1, data_offset);
373                 SIVAL(vwv +15, 1, state->data_sent);
374                 SCVAL(vwv +17, 1, 0); /* reserved */
375                 break;
376         }
377
378         state->param_sent += this_param;
379         state->data_sent += this_data;
380
381         *pwct = wct;
382         *piov_count = iov - state->iov;
383 }
384
385 static void cli_trans_done(struct tevent_req *subreq);
386
387 struct tevent_req *cli_trans_send(
388         TALLOC_CTX *mem_ctx, struct tevent_context *ev,
389         struct cli_state *cli, uint8_t cmd,
390         const char *pipe_name, uint16_t fid, uint16_t function, int flags,
391         uint16_t *setup, uint8_t num_setup, uint8_t max_setup,
392         uint8_t *param, uint32_t num_param, uint32_t max_param,
393         uint8_t *data, uint32_t num_data, uint32_t max_data)
394 {
395         struct tevent_req *req, *subreq;
396         struct cli_trans_state *state;
397         int iov_count;
398         uint8_t wct;
399         NTSTATUS status;
400
401         req = tevent_req_create(mem_ctx, &state, struct cli_trans_state);
402         if (req == NULL) {
403                 return NULL;
404         }
405
406         if ((cmd == SMBtrans) || (cmd == SMBtrans2)) {
407                 if ((num_param > 0xffff) || (max_param > 0xffff)
408                     || (num_data > 0xffff) || (max_data > 0xffff)) {
409                         DEBUG(3, ("Attempt to send invalid trans2 request "
410                                   "(setup %u, params %u/%u, data %u/%u)\n",
411                                   (unsigned)num_setup,
412                                   (unsigned)num_param, (unsigned)max_param,
413                                   (unsigned)num_data, (unsigned)max_data));
414                         tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
415                         return tevent_req_post(req, ev);
416                 }
417         }
418
419         /*
420          * The largest wct will be for nttrans (19+num_setup). Make sure we
421          * don't overflow state->vwv in cli_trans_format.
422          */
423
424         if ((num_setup + 19) > ARRAY_SIZE(state->vwv)) {
425                 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
426                 return tevent_req_post(req, ev);
427         }
428
429         state->cli = cli;
430         state->ev = ev;
431         state->cmd = cmd;
432         state->flags = flags;
433         state->num_rsetup = 0;
434         state->rsetup = NULL;
435         ZERO_STRUCT(state->rparam);
436         ZERO_STRUCT(state->rdata);
437
438         if ((pipe_name != NULL)
439             && (!convert_string_talloc(state, CH_UNIX,
440                                        cli_ucs2(cli) ? CH_UTF16LE : CH_DOS,
441                                        pipe_name, strlen(pipe_name) + 1,
442                                        &state->pipe_name_conv,
443                                        &state->pipe_name_conv_len))) {
444                 tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
445                 return tevent_req_post(req, ev);
446         }
447         state->fid = fid;       /* trans2 */
448         state->function = function; /* nttrans */
449
450         state->setup = setup;
451         state->num_setup = num_setup;
452         state->max_setup = max_setup;
453
454         state->param = param;
455         state->num_param = num_param;
456         state->param_sent = 0;
457         state->rparam.max = max_param;
458
459         state->data = data;
460         state->num_data = num_data;
461         state->data_sent = 0;
462         state->rdata.max = max_data;
463
464         cli_trans_format(state, &wct, &iov_count);
465
466         subreq = cli_smb_req_create(state, ev, cli, cmd, 0, wct, state->vwv,
467                                     iov_count, state->iov);
468         if (tevent_req_nomem(subreq, req)) {
469                 return tevent_req_post(req, ev);
470         }
471         status = cli_smb_req_send(subreq);
472         if (!NT_STATUS_IS_OK(status)) {
473                 tevent_req_nterror(req, status);
474                 return tevent_req_post(req, state->ev);
475         }
476         tevent_req_set_callback(subreq, cli_trans_done, req);
477
478         /*
479          * Now get the MID of the primary request
480          * and mark it as persistent. This means
481          * we will able to send and receive multiple
482          * SMB pdus using this MID in both directions
483          * (including correct SMB signing).
484          */
485         state->mid = cli_smb_req_mid(subreq);
486         cli_smb_req_set_mid(subreq, state->mid);
487         state->primary_subreq = subreq;
488         talloc_set_destructor(state, cli_trans_state_destructor);
489
490         return req;
491 }
492
493 static void cli_trans_done2(struct tevent_req *subreq);
494
495 static void cli_trans_done(struct tevent_req *subreq)
496 {
497         struct tevent_req *req = tevent_req_callback_data(
498                 subreq, struct tevent_req);
499         struct cli_trans_state *state = tevent_req_data(
500                 req, struct cli_trans_state);
501         NTSTATUS status;
502         bool sent_all;
503         const uint8_t *inhdr;
504         uint8_t wct;
505         uint16_t *vwv;
506         uint32_t num_bytes;
507         uint8_t *bytes;
508         uint8_t *inbuf;
509         uint8_t num_setup       = 0;
510         uint16_t *setup         = NULL;
511         uint32_t total_param    = 0;
512         uint32_t num_param      = 0;
513         uint32_t param_disp     = 0;
514         uint32_t total_data     = 0;
515         uint32_t num_data       = 0;
516         uint32_t data_disp      = 0;
517         uint8_t *param          = NULL;
518         uint8_t *data           = NULL;
519
520         status = cli_smb_recv(subreq, state, &inbuf, 0, &wct, &vwv,
521                               &num_bytes, &bytes);
522         /*
523          * Do not TALLOC_FREE(subreq) here, we might receive more than
524          * one response for the same mid.
525          */
526
527         /*
528          * We can receive something like STATUS_MORE_ENTRIES, so don't use
529          * !NT_STATUS_IS_OK(status) here.
530          */
531
532         if (NT_STATUS_IS_ERR(status)) {
533                 goto fail;
534         }
535         inhdr = inbuf + NBT_HDR_SIZE;
536
537         sent_all = ((state->param_sent == state->num_param)
538                     && (state->data_sent == state->num_data));
539
540         status = cli_pull_trans(
541                 inbuf, wct, vwv, num_bytes, bytes,
542                 state->cmd, !sent_all, &num_setup, &setup,
543                 &total_param, &num_param, &param_disp, &param,
544                 &total_data, &num_data, &data_disp, &data);
545
546         if (!NT_STATUS_IS_OK(status)) {
547                 goto fail;
548         }
549
550         if (!sent_all) {
551                 int iov_count;
552                 struct tevent_req *subreq2;
553
554                 cli_trans_format(state, &wct, &iov_count);
555
556                 subreq2 = cli_smb_req_create(state, state->ev, state->cli,
557                                              state->cmd + 1, 0, wct, state->vwv,
558                                              iov_count, state->iov);
559                 if (tevent_req_nomem(subreq2, req)) {
560                         return;
561                 }
562                 cli_smb_req_set_mid(subreq2, state->mid);
563
564                 status = cli_smb_req_send(subreq2);
565
566                 if (!NT_STATUS_IS_OK(status)) {
567                         goto fail;
568                 }
569                 tevent_req_set_callback(subreq2, cli_trans_done2, req);
570
571                 return;
572         }
573
574         status = cli_trans_pull_blob(
575                 state, &state->rparam, total_param, num_param, param,
576                 param_disp);
577
578         if (!NT_STATUS_IS_OK(status)) {
579                 DEBUG(10, ("Pulling params failed: %s\n", nt_errstr(status)));
580                 goto fail;
581         }
582
583         status = cli_trans_pull_blob(
584                 state, &state->rdata, total_data, num_data, data,
585                 data_disp);
586
587         if (!NT_STATUS_IS_OK(status)) {
588                 DEBUG(10, ("Pulling data failed: %s\n", nt_errstr(status)));
589                 goto fail;
590         }
591
592         if ((state->rparam.total == state->rparam.received)
593             && (state->rdata.total == state->rdata.received)) {
594                 state->recv_flags2 = SVAL(inhdr, HDR_FLG2);
595                 cli_trans_cleanup_primary(state);
596                 tevent_req_done(req);
597                 return;
598         }
599
600         TALLOC_FREE(inbuf);
601
602         return;
603
604  fail:
605         cli_trans_cleanup_primary(state);
606         tevent_req_nterror(req, status);
607 }
608
609 static void cli_trans_done2(struct tevent_req *subreq2)
610 {
611         struct tevent_req *req = tevent_req_callback_data(
612                 subreq2, struct tevent_req);
613         struct cli_trans_state *state = tevent_req_data(
614                 req, struct cli_trans_state);
615         NTSTATUS status;
616         bool sent_all;
617         uint8_t wct;
618         uint32_t seqnum;
619
620         /*
621          * First backup the seqnum of the secondary request
622          * and attach it to the primary request.
623          */
624         seqnum = cli_smb_req_seqnum(subreq2);
625         cli_smb_req_set_seqnum(state->primary_subreq, seqnum);
626
627         status = cli_smb_recv(subreq2, state, NULL, 0, &wct, NULL,
628                               NULL, NULL);
629         TALLOC_FREE(subreq2);
630
631         if (!NT_STATUS_IS_OK(status)) {
632                 goto fail;
633         }
634
635         if (wct != 0) {
636                 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
637                 goto fail;
638         }
639
640         sent_all = ((state->param_sent == state->num_param)
641                     && (state->data_sent == state->num_data));
642
643         if (!sent_all) {
644                 int iov_count;
645
646                 cli_trans_format(state, &wct, &iov_count);
647
648                 subreq2 = cli_smb_req_create(state, state->ev, state->cli,
649                                              state->cmd + 1, 0, wct, state->vwv,
650                                              iov_count, state->iov);
651                 if (tevent_req_nomem(subreq2, req)) {
652                         return;
653                 }
654                 cli_smb_req_set_mid(subreq2, state->mid);
655
656                 status = cli_smb_req_send(subreq2);
657
658                 if (!NT_STATUS_IS_OK(status)) {
659                         goto fail;
660                 }
661                 tevent_req_set_callback(subreq2, cli_trans_done2, req);
662                 return;
663         }
664
665         return;
666
667  fail:
668         cli_trans_cleanup_primary(state);
669         tevent_req_nterror(req, status);
670 }
671
672 NTSTATUS cli_trans_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
673                         uint16_t *recv_flags2,
674                         uint16_t **setup, uint8_t min_setup,
675                         uint8_t *num_setup,
676                         uint8_t **param, uint32_t min_param,
677                         uint32_t *num_param,
678                         uint8_t **data, uint32_t min_data,
679                         uint32_t *num_data)
680 {
681         struct cli_trans_state *state = tevent_req_data(
682                 req, struct cli_trans_state);
683         NTSTATUS status;
684
685         cli_trans_cleanup_primary(state);
686
687         if (tevent_req_is_nterror(req, &status)) {
688                 return status;
689         }
690
691         if ((state->num_rsetup < min_setup)
692             || (state->rparam.total < min_param)
693             || (state->rdata.total < min_data)) {
694                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
695         }
696
697         if (recv_flags2 != NULL) {
698                 *recv_flags2 = state->recv_flags2;
699         }
700
701         if (setup != NULL) {
702                 *setup = talloc_move(mem_ctx, &state->rsetup);
703                 *num_setup = state->num_rsetup;
704         } else {
705                 TALLOC_FREE(state->rsetup);
706         }
707
708         if (param != NULL) {
709                 *param = talloc_move(mem_ctx, &state->rparam.data);
710                 *num_param = state->rparam.total;
711         } else {
712                 TALLOC_FREE(state->rparam.data);
713         }
714
715         if (data != NULL) {
716                 *data = talloc_move(mem_ctx, &state->rdata.data);
717                 *num_data = state->rdata.total;
718         } else {
719                 TALLOC_FREE(state->rdata.data);
720         }
721
722         return NT_STATUS_OK;
723 }
724
725 NTSTATUS cli_trans(TALLOC_CTX *mem_ctx, struct cli_state *cli,
726                    uint8_t trans_cmd,
727                    const char *pipe_name, uint16_t fid, uint16_t function,
728                    int flags,
729                    uint16_t *setup, uint8_t num_setup, uint8_t max_setup,
730                    uint8_t *param, uint32_t num_param, uint32_t max_param,
731                    uint8_t *data, uint32_t num_data, uint32_t max_data,
732                    uint16_t *recv_flags2,
733                    uint16_t **rsetup, uint8_t min_rsetup, uint8_t *num_rsetup,
734                    uint8_t **rparam, uint32_t min_rparam, uint32_t *num_rparam,
735                    uint8_t **rdata, uint32_t min_rdata, uint32_t *num_rdata)
736 {
737         TALLOC_CTX *frame = talloc_stackframe();
738         struct tevent_context *ev;
739         struct tevent_req *req;
740         NTSTATUS status = NT_STATUS_OK;
741
742         if (cli_has_async_calls(cli)) {
743                 /*
744                  * Can't use sync call while an async call is in flight
745                  */
746                 status = NT_STATUS_INVALID_PARAMETER;
747                 goto fail;
748         }
749
750         ev = tevent_context_init(frame);
751         if (ev == NULL) {
752                 status = NT_STATUS_NO_MEMORY;
753                 goto fail;
754         }
755
756         req = cli_trans_send(frame, ev, cli, trans_cmd,
757                              pipe_name, fid, function, flags,
758                              setup, num_setup, max_setup,
759                              param, num_param, max_param,
760                              data, num_data, max_data);
761         if (req == NULL) {
762                 status = NT_STATUS_NO_MEMORY;
763                 goto fail;
764         }
765
766         if (!tevent_req_poll(req, ev)) {
767                 status = map_nt_error_from_unix_common(errno);
768                 goto fail;
769         }
770
771         status = cli_trans_recv(req, mem_ctx, recv_flags2,
772                                 rsetup, min_rsetup, num_rsetup,
773                                 rparam, min_rparam, num_rparam,
774                                 rdata, min_rdata, num_rdata);
775  fail:
776         TALLOC_FREE(frame);
777         return status;
778 }