s3:libsmb: make use of map_nt_error_from_unix_common() 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 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
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 = MIN_SMB_SIZE;
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 tevent_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         const uint8_t *inhdr;
494         uint8_t wct;
495         uint16_t *vwv;
496         uint32_t num_bytes;
497         uint8_t *bytes;
498         uint8_t *inbuf;
499         uint8_t num_setup       = 0;
500         uint16_t *setup         = NULL;
501         uint32_t total_param    = 0;
502         uint32_t num_param      = 0;
503         uint32_t param_disp     = 0;
504         uint32_t total_data     = 0;
505         uint32_t num_data       = 0;
506         uint32_t data_disp      = 0;
507         uint8_t *param          = NULL;
508         uint8_t *data           = NULL;
509
510         status = cli_smb_recv(subreq, state, &inbuf, 0, &wct, &vwv,
511                               &num_bytes, &bytes);
512         /*
513          * Do not TALLOC_FREE(subreq) here, we might receive more than
514          * one response for the same mid.
515          */
516
517         /*
518          * We can receive something like STATUS_MORE_ENTRIES, so don't use
519          * !NT_STATUS_IS_OK(status) here.
520          */
521
522         if (NT_STATUS_IS_ERR(status)) {
523                 goto fail;
524         }
525         inhdr = inbuf + NBT_HDR_SIZE;
526
527         sent_all = ((state->param_sent == state->num_param)
528                     && (state->data_sent == state->num_data));
529
530         status = cli_pull_trans(
531                 inbuf, wct, vwv, num_bytes, bytes,
532                 state->cmd, !sent_all, &num_setup, &setup,
533                 &total_param, &num_param, &param_disp, &param,
534                 &total_data, &num_data, &data_disp, &data);
535
536         if (!NT_STATUS_IS_OK(status)) {
537                 goto fail;
538         }
539
540         if (!sent_all) {
541                 int iov_count;
542                 struct tevent_req *subreq2;
543
544                 cli_trans_format(state, &wct, &iov_count);
545
546                 subreq2 = cli_smb_req_create(state, state->ev, state->cli,
547                                              state->cmd + 1, 0, wct, state->vwv,
548                                              iov_count, state->iov);
549                 if (tevent_req_nomem(subreq2, req)) {
550                         return;
551                 }
552                 cli_smb_req_set_mid(subreq2, state->mid);
553
554                 status = cli_smb_req_send(subreq2);
555
556                 if (!NT_STATUS_IS_OK(status)) {
557                         goto fail;
558                 }
559                 tevent_req_set_callback(subreq2, cli_trans_done2, req);
560
561                 return;
562         }
563
564         status = cli_trans_pull_blob(
565                 state, &state->rparam, total_param, num_param, param,
566                 param_disp);
567
568         if (!NT_STATUS_IS_OK(status)) {
569                 DEBUG(10, ("Pulling params failed: %s\n", nt_errstr(status)));
570                 goto fail;
571         }
572
573         status = cli_trans_pull_blob(
574                 state, &state->rdata, total_data, num_data, data,
575                 data_disp);
576
577         if (!NT_STATUS_IS_OK(status)) {
578                 DEBUG(10, ("Pulling data failed: %s\n", nt_errstr(status)));
579                 goto fail;
580         }
581
582         if ((state->rparam.total == state->rparam.received)
583             && (state->rdata.total == state->rdata.received)) {
584                 state->recv_flags2 = SVAL(inhdr, HDR_FLG2);
585                 cli_trans_cleanup_primary(state);
586                 tevent_req_done(req);
587                 return;
588         }
589
590         TALLOC_FREE(inbuf);
591
592         return;
593
594  fail:
595         cli_trans_cleanup_primary(state);
596         tevent_req_nterror(req, status);
597 }
598
599 static void cli_trans_done2(struct tevent_req *subreq2)
600 {
601         struct tevent_req *req = tevent_req_callback_data(
602                 subreq2, struct tevent_req);
603         struct cli_trans_state *state = tevent_req_data(
604                 req, struct cli_trans_state);
605         NTSTATUS status;
606         bool sent_all;
607         uint8_t wct;
608         uint32_t seqnum;
609
610         /*
611          * First backup the seqnum of the secondary request
612          * and attach it to the primary request.
613          */
614         seqnum = cli_smb_req_seqnum(subreq2);
615         cli_smb_req_set_seqnum(state->primary_subreq, seqnum);
616
617         status = cli_smb_recv(subreq2, state, NULL, 0, &wct, NULL,
618                               NULL, NULL);
619         TALLOC_FREE(subreq2);
620
621         if (!NT_STATUS_IS_OK(status)) {
622                 goto fail;
623         }
624
625         if (wct != 0) {
626                 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
627                 goto fail;
628         }
629
630         sent_all = ((state->param_sent == state->num_param)
631                     && (state->data_sent == state->num_data));
632
633         if (!sent_all) {
634                 int iov_count;
635
636                 cli_trans_format(state, &wct, &iov_count);
637
638                 subreq2 = cli_smb_req_create(state, state->ev, state->cli,
639                                              state->cmd + 1, 0, wct, state->vwv,
640                                              iov_count, state->iov);
641                 if (tevent_req_nomem(subreq2, req)) {
642                         return;
643                 }
644                 cli_smb_req_set_mid(subreq2, state->mid);
645
646                 status = cli_smb_req_send(subreq2);
647
648                 if (!NT_STATUS_IS_OK(status)) {
649                         goto fail;
650                 }
651                 tevent_req_set_callback(subreq2, cli_trans_done2, req);
652                 return;
653         }
654
655         return;
656
657  fail:
658         cli_trans_cleanup_primary(state);
659         tevent_req_nterror(req, status);
660 }
661
662 NTSTATUS cli_trans_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
663                         uint16_t *recv_flags2,
664                         uint16_t **setup, uint8_t min_setup,
665                         uint8_t *num_setup,
666                         uint8_t **param, uint32_t min_param,
667                         uint32_t *num_param,
668                         uint8_t **data, uint32_t min_data,
669                         uint32_t *num_data)
670 {
671         struct cli_trans_state *state = tevent_req_data(
672                 req, struct cli_trans_state);
673         NTSTATUS status;
674
675         cli_trans_cleanup_primary(state);
676
677         if (tevent_req_is_nterror(req, &status)) {
678                 return status;
679         }
680
681         if ((state->num_rsetup < min_setup)
682             || (state->rparam.total < min_param)
683             || (state->rdata.total < min_data)) {
684                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
685         }
686
687         if (recv_flags2 != NULL) {
688                 *recv_flags2 = state->recv_flags2;
689         }
690
691         if (setup != NULL) {
692                 *setup = talloc_move(mem_ctx, &state->rsetup);
693                 *num_setup = state->num_rsetup;
694         } else {
695                 TALLOC_FREE(state->rsetup);
696         }
697
698         if (param != NULL) {
699                 *param = talloc_move(mem_ctx, &state->rparam.data);
700                 *num_param = state->rparam.total;
701         } else {
702                 TALLOC_FREE(state->rparam.data);
703         }
704
705         if (data != NULL) {
706                 *data = talloc_move(mem_ctx, &state->rdata.data);
707                 *num_data = state->rdata.total;
708         } else {
709                 TALLOC_FREE(state->rdata.data);
710         }
711
712         return NT_STATUS_OK;
713 }
714
715 NTSTATUS cli_trans(TALLOC_CTX *mem_ctx, struct cli_state *cli,
716                    uint8_t trans_cmd,
717                    const char *pipe_name, uint16_t fid, uint16_t function,
718                    int flags,
719                    uint16_t *setup, uint8_t num_setup, uint8_t max_setup,
720                    uint8_t *param, uint32_t num_param, uint32_t max_param,
721                    uint8_t *data, uint32_t num_data, uint32_t max_data,
722                    uint16_t *recv_flags2,
723                    uint16_t **rsetup, uint8_t min_rsetup, uint8_t *num_rsetup,
724                    uint8_t **rparam, uint32_t min_rparam, uint32_t *num_rparam,
725                    uint8_t **rdata, uint32_t min_rdata, uint32_t *num_rdata)
726 {
727         TALLOC_CTX *frame = talloc_stackframe();
728         struct tevent_context *ev;
729         struct tevent_req *req;
730         NTSTATUS status = NT_STATUS_OK;
731
732         if (cli_has_async_calls(cli)) {
733                 /*
734                  * Can't use sync call while an async call is in flight
735                  */
736                 status = NT_STATUS_INVALID_PARAMETER;
737                 goto fail;
738         }
739
740         ev = tevent_context_init(frame);
741         if (ev == NULL) {
742                 status = NT_STATUS_NO_MEMORY;
743                 goto fail;
744         }
745
746         req = cli_trans_send(frame, ev, cli, trans_cmd,
747                              pipe_name, fid, function, flags,
748                              setup, num_setup, max_setup,
749                              param, num_param, max_param,
750                              data, num_data, max_data);
751         if (req == NULL) {
752                 status = NT_STATUS_NO_MEMORY;
753                 goto fail;
754         }
755
756         if (!tevent_req_poll(req, ev)) {
757                 status = map_nt_error_from_unix_common(errno);
758                 goto fail;
759         }
760
761         status = cli_trans_recv(req, mem_ctx, recv_flags2,
762                                 rsetup, min_rsetup, num_rsetup,
763                                 rparam, min_rparam, num_rparam,
764                                 rdata, min_rdata, num_rdata);
765  fail:
766         TALLOC_FREE(frame);
767         return status;
768 }