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