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