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