s3: Add async cli_session_request
[obnox/samba-ctdb.git] / source3 / libsmb / async_smb.c
1 /*
2    Unix SMB/CIFS implementation.
3    Infrastructure for async SMB client requests
4    Copyright (C) Volker Lendecke 2008
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
22 static void cli_state_handler(struct event_context *event_ctx,
23                               struct fd_event *event, uint16 flags, void *p);
24
25 /*
26  * Read an smb packet asynchronously, discard keepalives
27  */
28
29 struct read_smb_state {
30         struct tevent_context *ev;
31         int fd;
32         uint8_t *buf;
33 };
34
35 static ssize_t read_smb_more(uint8_t *buf, size_t buflen, void *private_data);
36 static void read_smb_done(struct tevent_req *subreq);
37
38 static struct tevent_req *read_smb_send(TALLOC_CTX *mem_ctx,
39                                         struct tevent_context *ev,
40                                         int fd)
41 {
42         struct tevent_req *result, *subreq;
43         struct read_smb_state *state;
44
45         result = tevent_req_create(mem_ctx, &state, struct read_smb_state);
46         if (result == NULL) {
47                 return NULL;
48         }
49         state->ev = ev;
50         state->fd = fd;
51
52         subreq = read_packet_send(state, ev, fd, 4, read_smb_more, NULL);
53         if (subreq == NULL) {
54                 goto fail;
55         }
56         tevent_req_set_callback(subreq, read_smb_done, result);
57         return result;
58  fail:
59         TALLOC_FREE(result);
60         return NULL;
61 }
62
63 static ssize_t read_smb_more(uint8_t *buf, size_t buflen, void *private_data)
64 {
65         if (buflen > 4) {
66                 return 0;       /* We've been here, we're done */
67         }
68         return smb_len_large(buf);
69 }
70
71 static void read_smb_done(struct tevent_req *subreq)
72 {
73         struct tevent_req *req = tevent_req_callback_data(
74                 subreq, struct tevent_req);
75         struct read_smb_state *state = tevent_req_data(
76                 req, struct read_smb_state);
77         ssize_t len;
78         int err;
79
80         len = read_packet_recv(subreq, state, &state->buf, &err);
81         TALLOC_FREE(subreq);
82         if (len == -1) {
83                 tevent_req_error(req, err);
84                 return;
85         }
86
87         if (CVAL(state->buf, 0) == SMBkeepalive) {
88                 subreq = read_packet_send(state, state->ev, state->fd, 4,
89                                           read_smb_more, NULL);
90                 if (tevent_req_nomem(subreq, req)) {
91                         return;
92                 }
93                 tevent_req_set_callback(subreq, read_smb_done, req);
94                 return;
95         }
96         tevent_req_done(req);
97 }
98
99 static ssize_t read_smb_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
100                              uint8_t **pbuf, int *perrno)
101 {
102         struct read_smb_state *state = tevent_req_data(
103                 req, struct read_smb_state);
104
105         if (tevent_req_is_unix_error(req, perrno)) {
106                 return -1;
107         }
108         *pbuf = talloc_move(mem_ctx, &state->buf);
109         return talloc_get_size(*pbuf);
110 }
111
112 /**
113  * Fetch an error out of a NBT packet
114  * @param[in] buf       The SMB packet
115  * @retval              The error, converted to NTSTATUS
116  */
117
118 NTSTATUS cli_pull_error(char *buf)
119 {
120         uint32_t flags2 = SVAL(buf, smb_flg2);
121
122         if (flags2 & FLAGS2_32_BIT_ERROR_CODES) {
123                 return NT_STATUS(IVAL(buf, smb_rcls));
124         }
125
126         /* if the client uses dos errors, but there is no error,
127            we should return no error here, otherwise it looks
128            like an unknown bad NT_STATUS. jmcd */
129         if (CVAL(buf, smb_rcls) == 0)
130                 return NT_STATUS_OK;
131
132         return NT_STATUS_DOS(CVAL(buf, smb_rcls), SVAL(buf,smb_err));
133 }
134
135 /**
136  * Compatibility helper for the sync APIs: Fake NTSTATUS in cli->inbuf
137  * @param[in] cli       The client connection that just received an error
138  * @param[in] status    The error to set on "cli"
139  */
140
141 void cli_set_error(struct cli_state *cli, NTSTATUS status)
142 {
143         uint32_t flags2 = SVAL(cli->inbuf, smb_flg2);
144
145         if (NT_STATUS_IS_DOS(status)) {
146                 SSVAL(cli->inbuf, smb_flg2,
147                       flags2 & ~FLAGS2_32_BIT_ERROR_CODES);
148                 SCVAL(cli->inbuf, smb_rcls, NT_STATUS_DOS_CLASS(status));
149                 SSVAL(cli->inbuf, smb_err, NT_STATUS_DOS_CODE(status));
150                 return;
151         }
152
153         SSVAL(cli->inbuf, smb_flg2, flags2 | FLAGS2_32_BIT_ERROR_CODES);
154         SIVAL(cli->inbuf, smb_rcls, NT_STATUS_V(status));
155         return;
156 }
157
158 /**
159  * Allocate a new mid
160  * @param[in] cli       The client connection
161  * @retval              The new, unused mid
162  */
163
164 static uint16_t cli_new_mid(struct cli_state *cli)
165 {
166         uint16_t result;
167         struct cli_request *req;
168
169         while (true) {
170                 result = cli->mid++;
171                 if (result == 0) {
172                         continue;
173                 }
174
175                 for (req = cli->outstanding_requests; req; req = req->next) {
176                         if (result == req->mid) {
177                                 break;
178                         }
179                 }
180
181                 if (req == NULL) {
182                         return result;
183                 }
184         }
185 }
186
187 /**
188  * Print an async req that happens to be a cli_request
189  * @param[in] mem_ctx   The TALLOC_CTX to put the result on
190  * @param[in] req       The request to print
191  * @retval              The string representation of "req"
192  */
193
194 static char *cli_request_print(TALLOC_CTX *mem_ctx, struct async_req *req)
195 {
196         char *result = async_req_print(mem_ctx, req);
197         struct cli_request *cli_req = talloc_get_type_abort(
198                 req->private_data, struct cli_request);
199
200         if (result == NULL) {
201                 return NULL;
202         }
203
204         return talloc_asprintf_append_buffer(
205                 result, "mid=%d\n", cli_req->mid);
206 }
207
208 /**
209  * Destroy a cli_request
210  * @param[in] req       The cli_request to kill
211  * @retval Can't fail
212  */
213
214 static int cli_request_destructor(struct cli_request *req)
215 {
216         if (req->enc_state != NULL) {
217                 common_free_enc_buffer(req->enc_state, (char *)req->outbuf);
218         }
219         DLIST_REMOVE(req->cli->outstanding_requests, req);
220         if (req->cli->outstanding_requests == NULL) {
221                 TALLOC_FREE(req->cli->fd_event);
222         }
223         return 0;
224 }
225
226 /**
227  * Are there already requests waiting in the chain_accumulator?
228  * @param[in] cli       The cli_state we want to check
229  * @retval reply :-)
230  */
231
232 bool cli_in_chain(struct cli_state *cli)
233 {
234         if (cli->chain_accumulator == NULL) {
235                 return false;
236         }
237
238         return (cli->chain_accumulator->num_async != 0);
239 }
240
241 /**
242  * @brief Find the smb_cmd offset of the last command pushed
243  * @param[in] buf       The buffer we're building up
244  * @retval              Where can we put our next andx cmd?
245  *
246  * While chaining requests, the "next" request we're looking at needs to put
247  * its SMB_Command before the data the previous request already built up added
248  * to the chain. Find the offset to the place where we have to put our cmd.
249  */
250
251 static bool find_andx_cmd_ofs(uint8_t *buf, size_t *pofs)
252 {
253         uint8_t cmd;
254         size_t ofs;
255
256         cmd = CVAL(buf, smb_com);
257
258         SMB_ASSERT(is_andx_req(cmd));
259
260         ofs = smb_vwv0;
261
262         while (CVAL(buf, ofs) != 0xff) {
263
264                 if (!is_andx_req(CVAL(buf, ofs))) {
265                         return false;
266                 }
267
268                 /*
269                  * ofs is from start of smb header, so add the 4 length
270                  * bytes. The next cmd is right after the wct field.
271                  */
272                 ofs = SVAL(buf, ofs+2) + 4 + 1;
273
274                 SMB_ASSERT(ofs+4 < talloc_get_size(buf));
275         }
276
277         *pofs = ofs;
278         return true;
279 }
280
281 /**
282  * @brief Do the smb chaining at a buffer level
283  * @param[in] poutbuf           Pointer to the talloc'ed buffer to be modified
284  * @param[in] smb_command       The command that we want to issue
285  * @param[in] wct               How many words?
286  * @param[in] vwv               The words, already in network order
287  * @param[in] bytes_alignment   How shall we align "bytes"?
288  * @param[in] num_bytes         How many bytes?
289  * @param[in] bytes             The data the request ships
290  *
291  * smb_splice_chain() adds the vwv and bytes to the request already present in
292  * *poutbuf.
293  */
294
295 bool smb_splice_chain(uint8_t **poutbuf, uint8_t smb_command,
296                       uint8_t wct, const uint16_t *vwv,
297                       size_t bytes_alignment,
298                       uint32_t num_bytes, const uint8_t *bytes)
299 {
300         uint8_t *outbuf;
301         size_t old_size, new_size;
302         size_t ofs;
303         size_t chain_padding = 0;
304         size_t bytes_padding = 0;
305         bool first_request;
306
307         old_size = talloc_get_size(*poutbuf);
308
309         /*
310          * old_size == smb_wct means we're pushing the first request in for
311          * libsmb/
312          */
313
314         first_request = (old_size == smb_wct);
315
316         if (!first_request && ((old_size % 4) != 0)) {
317                 /*
318                  * Align the wct field of subsequent requests to a 4-byte
319                  * boundary
320                  */
321                 chain_padding = 4 - (old_size % 4);
322         }
323
324         /*
325          * After the old request comes the new wct field (1 byte), the vwv's
326          * and the num_bytes field. After at we might need to align the bytes
327          * given to us to "bytes_alignment", increasing the num_bytes value.
328          */
329
330         new_size = old_size + chain_padding + 1 + wct * sizeof(uint16_t) + 2;
331
332         if ((bytes_alignment != 0) && ((new_size % bytes_alignment) != 0)) {
333                 bytes_padding = bytes_alignment - (new_size % bytes_alignment);
334         }
335
336         new_size += bytes_padding + num_bytes;
337
338         if ((smb_command != SMBwriteX) && (new_size > 0xffff)) {
339                 DEBUG(1, ("splice_chain: %u bytes won't fit\n",
340                           (unsigned)new_size));
341                 return false;
342         }
343
344         outbuf = TALLOC_REALLOC_ARRAY(NULL, *poutbuf, uint8_t, new_size);
345         if (outbuf == NULL) {
346                 DEBUG(0, ("talloc failed\n"));
347                 return false;
348         }
349         *poutbuf = outbuf;
350
351         if (first_request) {
352                 SCVAL(outbuf, smb_com, smb_command);
353         } else {
354                 size_t andx_cmd_ofs;
355
356                 if (!find_andx_cmd_ofs(outbuf, &andx_cmd_ofs)) {
357                         DEBUG(1, ("invalid command chain\n"));
358                         *poutbuf = TALLOC_REALLOC_ARRAY(
359                                 NULL, *poutbuf, uint8_t, old_size);
360                         return false;
361                 }
362
363                 if (chain_padding != 0) {
364                         memset(outbuf + old_size, 0, chain_padding);
365                         old_size += chain_padding;
366                 }
367
368                 SCVAL(outbuf, andx_cmd_ofs, smb_command);
369                 SSVAL(outbuf, andx_cmd_ofs + 2, old_size - 4);
370         }
371
372         ofs = old_size;
373
374         /*
375          * Push the chained request:
376          *
377          * wct field
378          */
379
380         SCVAL(outbuf, ofs, wct);
381         ofs += 1;
382
383         /*
384          * vwv array
385          */
386
387         memcpy(outbuf + ofs, vwv, sizeof(uint16_t) * wct);
388         ofs += sizeof(uint16_t) * wct;
389
390         /*
391          * bcc (byte count)
392          */
393
394         SSVAL(outbuf, ofs, num_bytes + bytes_padding);
395         ofs += sizeof(uint16_t);
396
397         /*
398          * padding
399          */
400
401         if (bytes_padding != 0) {
402                 memset(outbuf + ofs, 0, bytes_padding);
403                 ofs += bytes_padding;
404         }
405
406         /*
407          * The bytes field
408          */
409
410         memcpy(outbuf + ofs, bytes, num_bytes);
411
412         return true;
413 }
414
415 /**
416  * @brief Destroy an async_req that is the visible part of a cli_request
417  * @param[in] req       The request to kill
418  * @retval Return 0 to make talloc happy
419  *
420  * This destructor is a bit tricky: Because a cli_request can host more than
421  * one async_req for chained requests, we need to make sure that the
422  * "cli_request" that we were part of is correctly destroyed at the right
423  * time. This is done by NULLing out ourself from the "async" member of our
424  * "cli_request". If there is none left, then also TALLOC_FREE() the
425  * cli_request, which was a talloc child of the client connection cli_state.
426  */
427
428 static int cli_async_req_destructor(struct async_req *req)
429 {
430         struct cli_request *cli_req = talloc_get_type_abort(
431                 req->private_data, struct cli_request);
432         int i, pending;
433         bool found = false;
434
435         pending = 0;
436
437         for (i=0; i<cli_req->num_async; i++) {
438                 if (cli_req->async[i] == req) {
439                         cli_req->async[i] = NULL;
440                         found = true;
441                 }
442                 if (cli_req->async[i] != NULL) {
443                         pending += 1;
444                 }
445         }
446
447         SMB_ASSERT(found);
448
449         if (pending == 0) {
450                 TALLOC_FREE(cli_req);
451         }
452
453         return 0;
454 }
455
456 /**
457  * @brief Chain up a request
458  * @param[in] mem_ctx           The TALLOC_CTX for the result
459  * @param[in] ev                The event context that will call us back
460  * @param[in] cli               The cli_state we queue the request up for
461  * @param[in] smb_command       The command that we want to issue
462  * @param[in] additional_flags  open_and_x wants to add oplock header flags
463  * @param[in] wct               How many words?
464  * @param[in] vwv               The words, already in network order
465  * @param[in] bytes_alignment   How shall we align "bytes"?
466  * @param[in] num_bytes         How many bytes?
467  * @param[in] bytes             The data the request ships
468  *
469  * cli_request_chain() is the core of the SMB request marshalling routine. It
470  * will create a new async_req structure in the cli->chain_accumulator->async
471  * array and marshall the smb_cmd, the vwv array and the bytes into
472  * cli->chain_accumulator->outbuf.
473  */
474
475 static struct async_req *cli_request_chain(TALLOC_CTX *mem_ctx,
476                                            struct event_context *ev,
477                                            struct cli_state *cli,
478                                            uint8_t smb_command,
479                                            uint8_t additional_flags,
480                                            uint8_t wct, const uint16_t *vwv,
481                                            size_t bytes_alignment,
482                                            uint32_t num_bytes,
483                                            const uint8_t *bytes)
484 {
485         struct async_req **tmp_reqs;
486         struct cli_request *req;
487
488         req = cli->chain_accumulator;
489
490         tmp_reqs = TALLOC_REALLOC_ARRAY(req, req->async, struct async_req *,
491                                         req->num_async + 1);
492         if (tmp_reqs == NULL) {
493                 DEBUG(0, ("talloc failed\n"));
494                 return NULL;
495         }
496         req->async = tmp_reqs;
497         req->num_async += 1;
498
499         req->async[req->num_async-1] = async_req_new(mem_ctx);
500         if (req->async[req->num_async-1] == NULL) {
501                 DEBUG(0, ("async_req_new failed\n"));
502                 req->num_async -= 1;
503                 return NULL;
504         }
505         req->async[req->num_async-1]->private_data = req;
506         req->async[req->num_async-1]->print = cli_request_print;
507         talloc_set_destructor(req->async[req->num_async-1],
508                               cli_async_req_destructor);
509
510         if (!smb_splice_chain(&req->outbuf, smb_command, wct, vwv,
511                               bytes_alignment, num_bytes, bytes)) {
512                 goto fail;
513         }
514
515         return req->async[req->num_async-1];
516
517  fail:
518         TALLOC_FREE(req->async[req->num_async-1]);
519         req->num_async -= 1;
520         return NULL;
521 }
522
523 /**
524  * @brief prepare a cli_state to accept a chain of requests
525  * @param[in] cli       The cli_state we want to queue up in
526  * @param[in] ev        The event_context that will call us back for the socket
527  * @param[in] size_hint How many bytes are expected, just an optimization
528  * @retval Did we have enough memory?
529  *
530  * cli_chain_cork() sets up a new cli_request in cli->chain_accumulator. If
531  * cli is used in an async fashion, i.e. if we have outstanding requests, then
532  * we do not have to create a fd event. If cli is used only with the sync
533  * helpers, we need to create the fd_event here.
534  *
535  * If you want to issue a chained request to the server, do a
536  * cli_chain_cork(), then do you cli_open_send(), cli_read_and_x_send(),
537  * cli_close_send() and so on. The async requests that come out of
538  * cli_xxx_send() are normal async requests with the difference that they
539  * won't be shipped individually. But the event_context will still trigger the
540  * req->async.fn to be called on every single request.
541  *
542  * You have to take care yourself that you only issue chainable requests in
543  * the middle of the chain.
544  */
545
546 bool cli_chain_cork(struct cli_state *cli, struct event_context *ev,
547                     size_t size_hint)
548 {
549         struct cli_request *req = NULL;
550
551         SMB_ASSERT(cli->chain_accumulator == NULL);
552
553         if (cli->fd == -1) {
554                 DEBUG(10, ("cli->fd closed\n"));
555                 return false;
556         }
557
558         if (cli->fd_event == NULL) {
559                 SMB_ASSERT(cli->outstanding_requests == NULL);
560                 cli->fd_event = event_add_fd(ev, cli, cli->fd,
561                                              EVENT_FD_READ,
562                                              cli_state_handler, cli);
563                 if (cli->fd_event == NULL) {
564                         return false;
565                 }
566         }
567
568         req = talloc(cli, struct cli_request);
569         if (req == NULL) {
570                 goto fail;
571         }
572         req->cli = cli;
573
574         if (size_hint == 0) {
575                 size_hint = 100;
576         }
577         req->outbuf = talloc_array(req, uint8_t, smb_wct + size_hint);
578         if (req->outbuf == NULL) {
579                 goto fail;
580         }
581         req->outbuf = TALLOC_REALLOC_ARRAY(NULL, req->outbuf, uint8_t,
582                                            smb_wct);
583
584         req->num_async = 0;
585         req->async = NULL;
586
587         req->enc_state = NULL;
588         req->recv_helper.fn = NULL;
589
590         SSVAL(req->outbuf, smb_tid, cli->cnum);
591         cli_setup_packet_buf(cli, (char *)req->outbuf);
592
593         req->mid = cli_new_mid(cli);
594
595         cli->chain_accumulator = req;
596
597         DEBUG(10, ("cli_chain_cork: mid=%d\n", req->mid));
598
599         return true;
600  fail:
601         TALLOC_FREE(req);
602         if (cli->outstanding_requests == NULL) {
603                 TALLOC_FREE(cli->fd_event);
604         }
605         return false;
606 }
607
608 /**
609  * Ship a request queued up via cli_request_chain()
610  * @param[in] cl        The connection
611  */
612
613 void cli_chain_uncork(struct cli_state *cli)
614 {
615         struct cli_request *req = cli->chain_accumulator;
616         size_t smblen;
617
618         SMB_ASSERT(req != NULL);
619
620         DLIST_ADD_END(cli->outstanding_requests, req, struct cli_request *);
621         talloc_set_destructor(req, cli_request_destructor);
622
623         cli->chain_accumulator = NULL;
624
625         SSVAL(req->outbuf, smb_mid, req->mid);
626
627         smblen = talloc_get_size(req->outbuf) - 4;
628
629         smb_setlen((char *)req->outbuf, smblen);
630
631         if (smblen > 0x1ffff) {
632                 /*
633                  * This is a POSIX 14 word large write. Overwrite just the
634                  * size field, the '0xFFSMB' has been set by smb_setlen which
635                  * _smb_setlen_large does not do.
636                  */
637                 _smb_setlen_large(((char *)req->outbuf), smblen);
638         }
639
640         cli_calculate_sign_mac(cli, (char *)req->outbuf);
641
642         if (cli_encryption_on(cli)) {
643                 NTSTATUS status;
644                 char *enc_buf;
645
646                 status = cli_encrypt_message(cli, (char *)req->outbuf,
647                                              &enc_buf);
648                 if (!NT_STATUS_IS_OK(status)) {
649                         DEBUG(0, ("Error in encrypting client message. "
650                                   "Error %s\n", nt_errstr(status)));
651                         TALLOC_FREE(req);
652                         return;
653                 }
654                 req->outbuf = (uint8_t *)enc_buf;
655                 req->enc_state = cli->trans_enc_state;
656         }
657
658         req->sent = 0;
659
660         event_fd_set_writeable(cli->fd_event);
661 }
662
663 /**
664  * @brief Send a request to the server
665  * @param[in] mem_ctx           The TALLOC_CTX for the result
666  * @param[in] ev                The event context that will call us back
667  * @param[in] cli               The cli_state we queue the request up for
668  * @param[in] smb_command       The command that we want to issue
669  * @param[in] additional_flags  open_and_x wants to add oplock header flags
670  * @param[in] wct               How many words?
671  * @param[in] vwv               The words, already in network order
672  * @param[in] bytes_alignment   How shall we align "bytes"?
673  * @param[in] num_bytes         How many bytes?
674  * @param[in] bytes             The data the request ships
675  *
676  * This is the generic routine to be used by the cli_xxx_send routines.
677  */
678
679 struct async_req *cli_request_send(TALLOC_CTX *mem_ctx,
680                                    struct event_context *ev,
681                                    struct cli_state *cli,
682                                    uint8_t smb_command,
683                                    uint8_t additional_flags,
684                                    uint8_t wct, const uint16_t *vwv,
685                                    size_t bytes_alignment,
686                                    uint32_t num_bytes, const uint8_t *bytes)
687 {
688         struct async_req *result;
689         bool uncork = false;
690         struct timeval endtime;
691
692         if (cli->chain_accumulator == NULL) {
693                 if (!cli_chain_cork(cli, ev,
694                                     wct * sizeof(uint16_t) + num_bytes + 3)) {
695                         DEBUG(1, ("cli_chain_cork failed\n"));
696                         return NULL;
697                 }
698                 uncork = true;
699         }
700
701         result = cli_request_chain(mem_ctx, ev, cli, smb_command,
702                                    additional_flags, wct, vwv, bytes_alignment,
703                                    num_bytes, bytes);
704
705         if (result == NULL) {
706                 DEBUG(1, ("cli_request_chain failed\n"));
707         }
708
709         endtime = timeval_current_ofs(0, cli->timeout * 1000);
710
711         if (!async_req_set_endtime(result, ev, endtime)) {
712                 DEBUG(1, ("async_req_set_endtime failed\n"));
713                 TALLOC_FREE(result);
714                 return NULL;
715         }
716
717         if (uncork) {
718                 cli_chain_uncork(cli);
719         }
720
721         return result;
722 }
723
724 /**
725  * Calculate the current ofs to wct for requests like write&x
726  * @param[in] req       The smb request we're currently building
727  * @retval how many bytes offset have we accumulated?
728  */
729
730 uint16_t cli_wct_ofs(const struct cli_state *cli)
731 {
732         size_t buf_size;
733
734         if (cli->chain_accumulator == NULL) {
735                 return smb_wct - 4;
736         }
737
738         buf_size = talloc_get_size(cli->chain_accumulator->outbuf);
739
740         if (buf_size == smb_wct) {
741                 return smb_wct - 4;
742         }
743
744         /*
745          * Add alignment for subsequent requests
746          */
747
748         if ((buf_size % 4) != 0) {
749                 buf_size += (4 - (buf_size % 4));
750         }
751
752         return buf_size - 4;
753 }
754
755 /**
756  * Figure out if there is an andx command behind the current one
757  * @param[in] buf       The smb buffer to look at
758  * @param[in] ofs       The offset to the wct field that is followed by the cmd
759  * @retval Is there a command following?
760  */
761
762 static bool have_andx_command(const char *buf, uint16_t ofs)
763 {
764         uint8_t wct;
765         size_t buflen = talloc_get_size(buf);
766
767         if ((ofs == buflen-1) || (ofs == buflen)) {
768                 return false;
769         }
770
771         wct = CVAL(buf, ofs);
772         if (wct < 2) {
773                 /*
774                  * Not enough space for the command and a following pointer
775                  */
776                 return false;
777         }
778         return (CVAL(buf, ofs+1) != 0xff);
779 }
780
781 /**
782  * @brief Pull reply data out of a request
783  * @param[in] req               The request that we just received a reply for
784  * @param[out] pwct             How many words did the server send?
785  * @param[out] pvwv             The words themselves
786  * @param[out] pnum_bytes       How many bytes did the server send?
787  * @param[out] pbytes           The bytes themselves
788  * @retval Was the reply formally correct?
789  */
790
791 NTSTATUS cli_pull_reply(struct async_req *req,
792                         uint8_t *pwct, uint16_t **pvwv,
793                         uint16_t *pnum_bytes, uint8_t **pbytes)
794 {
795         struct cli_request *cli_req = talloc_get_type_abort(
796                 req->private_data, struct cli_request);
797         uint8_t wct, cmd;
798         uint16_t num_bytes;
799         size_t wct_ofs, bytes_offset;
800         int i, j;
801         NTSTATUS status;
802
803         for (i = 0; i < cli_req->num_async; i++) {
804                 if (req == cli_req->async[i]) {
805                         break;
806                 }
807         }
808
809         if (i == cli_req->num_async) {
810                 cli_set_error(cli_req->cli, NT_STATUS_INVALID_PARAMETER);
811                 return NT_STATUS_INVALID_PARAMETER;
812         }
813
814         /**
815          * The status we pull here is only relevant for the last reply in the
816          * chain.
817          */
818
819         status = cli_pull_error(cli_req->inbuf);
820
821         if (i == 0) {
822                 if (NT_STATUS_IS_ERR(status)
823                     && !have_andx_command(cli_req->inbuf, smb_wct)) {
824                         cli_set_error(cli_req->cli, status);
825                         return status;
826                 }
827                 wct_ofs = smb_wct;
828                 goto done;
829         }
830
831         cmd = CVAL(cli_req->inbuf, smb_com);
832         wct_ofs = smb_wct;
833
834         for (j = 0; j < i; j++) {
835                 if (j < i-1) {
836                         if (cmd == 0xff) {
837                                 return NT_STATUS_REQUEST_ABORTED;
838                         }
839                         if (!is_andx_req(cmd)) {
840                                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
841                         }
842                 }
843
844                 if (!have_andx_command(cli_req->inbuf, wct_ofs)) {
845                         /*
846                          * This request was not completed because a previous
847                          * request in the chain had received an error.
848                          */
849                         return NT_STATUS_REQUEST_ABORTED;
850                 }
851
852                 wct_ofs = SVAL(cli_req->inbuf, wct_ofs + 3);
853
854                 /*
855                  * Skip the all-present length field. No overflow, we've just
856                  * put a 16-bit value into a size_t.
857                  */
858                 wct_ofs += 4;
859
860                 if (wct_ofs+2 > talloc_get_size(cli_req->inbuf)) {
861                         return NT_STATUS_INVALID_NETWORK_RESPONSE;
862                 }
863
864                 cmd = CVAL(cli_req->inbuf, wct_ofs + 1);
865         }
866
867         if (!have_andx_command(cli_req->inbuf, wct_ofs)
868             && NT_STATUS_IS_ERR(status)) {
869                 /*
870                  * The last command takes the error code. All further commands
871                  * down the requested chain will get a
872                  * NT_STATUS_REQUEST_ABORTED.
873                  */
874                 return status;
875         }
876
877  done:
878         wct = CVAL(cli_req->inbuf, wct_ofs);
879
880         bytes_offset = wct_ofs + 1 + wct * sizeof(uint16_t);
881         num_bytes = SVAL(cli_req->inbuf, bytes_offset);
882
883         /*
884          * wct_ofs is a 16-bit value plus 4, wct is a 8-bit value, num_bytes
885          * is a 16-bit value. So bytes_offset being size_t should be far from
886          * wrapping.
887          */
888
889         if ((bytes_offset + 2 > talloc_get_size(cli_req->inbuf))
890             || (bytes_offset > 0xffff)) {
891                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
892         }
893
894         *pwct = wct;
895         *pvwv = (uint16_t *)(cli_req->inbuf + wct_ofs + 1);
896         *pnum_bytes = num_bytes;
897         *pbytes = (uint8_t *)cli_req->inbuf + bytes_offset + 2;
898
899         return NT_STATUS_OK;
900 }
901
902 /**
903  * Decrypt a PDU, check the signature
904  * @param[in] cli       The cli_state that received something
905  * @param[in] pdu       The incoming bytes
906  * @retval error code
907  */
908
909
910 static NTSTATUS validate_smb_crypto(struct cli_state *cli, char *pdu)
911 {
912         NTSTATUS status;
913
914         if ((IVAL(pdu, 4) != 0x424d53ff) /* 0xFF"SMB" */
915             && (SVAL(pdu, 4) != 0x45ff)) /* 0xFF"E" */ {
916                 DEBUG(10, ("Got non-SMB PDU\n"));
917                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
918         }
919
920         if (cli_encryption_on(cli) && CVAL(pdu, 0) == 0) {
921                 uint16_t enc_ctx_num;
922
923                 status = get_enc_ctx_num((uint8_t *)pdu, &enc_ctx_num);
924                 if (!NT_STATUS_IS_OK(status)) {
925                         DEBUG(10, ("get_enc_ctx_num returned %s\n",
926                                    nt_errstr(status)));
927                         return status;
928                 }
929
930                 if (enc_ctx_num != cli->trans_enc_state->enc_ctx_num) {
931                         DEBUG(10, ("wrong enc_ctx %d, expected %d\n",
932                                    enc_ctx_num,
933                                    cli->trans_enc_state->enc_ctx_num));
934                         return NT_STATUS_INVALID_HANDLE;
935                 }
936
937                 status = common_decrypt_buffer(cli->trans_enc_state, pdu);
938                 if (!NT_STATUS_IS_OK(status)) {
939                         DEBUG(10, ("common_decrypt_buffer returned %s\n",
940                                    nt_errstr(status)));
941                         return status;
942                 }
943         }
944
945         if (!cli_check_sign_mac(cli, pdu)) {
946                 DEBUG(10, ("cli_check_sign_mac failed\n"));
947                 close(cli->fd);
948                 cli->fd = -1;
949                 return NT_STATUS_ACCESS_DENIED;
950         }
951
952         return NT_STATUS_OK;
953 }
954
955 /**
956  * A PDU has arrived on cli->evt_inbuf
957  * @param[in] cli       The cli_state that received something
958  */
959
960 static void handle_incoming_pdu(struct cli_state *cli)
961 {
962         struct cli_request *req, *next;
963         uint16_t mid;
964         size_t raw_pdu_len, buf_len, pdu_len, rest_len;
965         char *pdu;
966         int i;
967         NTSTATUS status;
968
969         int num_async;
970
971         /*
972          * The encrypted PDU len might differ from the unencrypted one
973          */
974         raw_pdu_len = smb_len(cli->evt_inbuf) + 4;
975         buf_len = talloc_get_size(cli->evt_inbuf);
976         rest_len = buf_len - raw_pdu_len;
977
978         if (buf_len == raw_pdu_len) {
979                 /*
980                  * Optimal case: Exactly one PDU was in the socket buffer
981                  */
982                 pdu = cli->evt_inbuf;
983                 cli->evt_inbuf = NULL;
984         }
985         else {
986                 DEBUG(11, ("buf_len = %d, raw_pdu_len = %d, splitting "
987                            "buffer\n", (int)buf_len, (int)raw_pdu_len));
988
989                 if (raw_pdu_len < rest_len) {
990                         /*
991                          * The PDU is shorter, talloc_memdup that one.
992                          */
993                         pdu = (char *)talloc_memdup(
994                                 cli, cli->evt_inbuf, raw_pdu_len);
995
996                         memmove(cli->evt_inbuf, cli->evt_inbuf + raw_pdu_len,
997                                 buf_len - raw_pdu_len);
998
999                         cli->evt_inbuf = TALLOC_REALLOC_ARRAY(
1000                                 NULL, cli->evt_inbuf, char, rest_len);
1001
1002                         if (pdu == NULL) {
1003                                 status = NT_STATUS_NO_MEMORY;
1004                                 goto invalidate_requests;
1005                         }
1006                 }
1007                 else {
1008                         /*
1009                          * The PDU is larger than the rest, talloc_memdup the
1010                          * rest
1011                          */
1012                         pdu = cli->evt_inbuf;
1013
1014                         cli->evt_inbuf = (char *)talloc_memdup(
1015                                 cli, pdu + raw_pdu_len, rest_len);
1016
1017                         if (cli->evt_inbuf == NULL) {
1018                                 status = NT_STATUS_NO_MEMORY;
1019                                 goto invalidate_requests;
1020                         }
1021                 }
1022         }
1023
1024         if ((raw_pdu_len == 4) && (CVAL(pdu, 0) == SMBkeepalive)) {
1025                 DEBUG(10, ("Got keepalive\n"));
1026                 TALLOC_FREE(pdu);
1027                 return;
1028         }
1029
1030         status = validate_smb_crypto(cli, pdu);
1031         if (!NT_STATUS_IS_OK(status)) {
1032                 goto invalidate_requests;
1033         }
1034
1035         mid = SVAL(pdu, smb_mid);
1036
1037         DEBUG(10, ("handle_incoming_pdu: got mid %d\n", mid));
1038
1039         for (req = cli->outstanding_requests; req; req = req->next) {
1040                 if (req->mid == mid) {
1041                         break;
1042                 }
1043         }
1044
1045         pdu_len = smb_len(pdu) + 4;
1046
1047         if (req == NULL) {
1048                 DEBUG(3, ("Request for mid %d not found, dumping PDU\n", mid));
1049
1050                 TALLOC_FREE(pdu);
1051                 return;
1052         }
1053
1054         req->inbuf = talloc_move(req, &pdu);
1055
1056         /*
1057          * Freeing the last async_req will free the req (see
1058          * cli_async_req_destructor). So make a copy of req->num_async, we
1059          * can't reference it in the last round.
1060          */
1061
1062         num_async = req->num_async;
1063
1064         for (i=0; i<num_async; i++) {
1065                 /**
1066                  * A request might have been talloc_free()'ed before we arrive
1067                  * here. It will have removed itself from req->async via its
1068                  * destructor cli_async_req_destructor().
1069                  */
1070                 if (req->async[i] != NULL) {
1071                         if (req->recv_helper.fn != NULL) {
1072                                 req->recv_helper.fn(req->async[i]);
1073                         } else {
1074                                 async_req_done(req->async[i]);
1075                         }
1076                 }
1077         }
1078         return;
1079
1080  invalidate_requests:
1081
1082         DEBUG(10, ("handle_incoming_pdu: Aborting with %s\n",
1083                    nt_errstr(status)));
1084
1085         for (req = cli->outstanding_requests; req; req = next) {
1086                 next = req->next;
1087                 if (req->num_async) {
1088                         async_req_nterror(req->async[0], status);
1089                 }
1090         }
1091         return;
1092 }
1093
1094 /**
1095  * fd event callback. This is the basic connection to the socket
1096  * @param[in] event_ctx The event context that called us
1097  * @param[in] event     The event that fired
1098  * @param[in] flags     EVENT_FD_READ | EVENT_FD_WRITE
1099  * @param[in] p         private_data, in this case the cli_state
1100  */
1101
1102 static void cli_state_handler(struct event_context *event_ctx,
1103                               struct fd_event *event, uint16 flags, void *p)
1104 {
1105         struct cli_state *cli = (struct cli_state *)p;
1106         struct cli_request *req, *next;
1107         NTSTATUS status;
1108
1109         DEBUG(11, ("cli_state_handler called with flags %d\n", flags));
1110
1111         if (cli->fd == -1) {
1112                 status = NT_STATUS_CONNECTION_INVALID;
1113                 goto sock_error;
1114         }
1115
1116         if (flags & EVENT_FD_WRITE) {
1117                 size_t to_send;
1118                 ssize_t sent;
1119
1120                 for (req = cli->outstanding_requests; req; req = req->next) {
1121                         to_send = smb_len(req->outbuf)+4;
1122                         if (to_send > req->sent) {
1123                                 break;
1124                         }
1125                 }
1126
1127                 if (req == NULL) {
1128                         if (cli->fd_event != NULL) {
1129                                 event_fd_set_not_writeable(cli->fd_event);
1130                         }
1131                         return;
1132                 }
1133
1134                 sent = sys_send(cli->fd, req->outbuf + req->sent,
1135                             to_send - req->sent, 0);
1136
1137                 if (sent < 0) {
1138                         status = map_nt_error_from_unix(errno);
1139                         goto sock_error;
1140                 }
1141
1142                 req->sent += sent;
1143
1144                 if (req->sent == to_send) {
1145                         return;
1146                 }
1147         }
1148
1149         if (flags & EVENT_FD_READ) {
1150                 int res, available;
1151                 size_t old_size, new_size;
1152                 char *tmp;
1153
1154                 res = ioctl(cli->fd, FIONREAD, &available);
1155                 if (res == -1) {
1156                         DEBUG(10, ("ioctl(FIONREAD) failed: %s\n",
1157                                    strerror(errno)));
1158                         status = map_nt_error_from_unix(errno);
1159                         goto sock_error;
1160                 }
1161
1162                 if (available == 0) {
1163                         /* EOF */
1164                         status = NT_STATUS_END_OF_FILE;
1165                         goto sock_error;
1166                 }
1167
1168                 old_size = talloc_get_size(cli->evt_inbuf);
1169                 new_size = old_size + available;
1170
1171                 if (new_size < old_size) {
1172                         /* wrap */
1173                         status = NT_STATUS_UNEXPECTED_IO_ERROR;
1174                         goto sock_error;
1175                 }
1176
1177                 tmp = TALLOC_REALLOC_ARRAY(cli, cli->evt_inbuf, char,
1178                                            new_size);
1179                 if (tmp == NULL) {
1180                         /* nomem */
1181                         status = NT_STATUS_NO_MEMORY;
1182                         goto sock_error;
1183                 }
1184                 cli->evt_inbuf = tmp;
1185
1186                 res = sys_recv(cli->fd, cli->evt_inbuf + old_size, available, 0);
1187                 if (res == -1) {
1188                         DEBUG(10, ("recv failed: %s\n", strerror(errno)));
1189                         status = map_nt_error_from_unix(errno);
1190                         goto sock_error;
1191                 }
1192
1193                 DEBUG(11, ("cli_state_handler: received %d bytes, "
1194                            "smb_len(evt_inbuf) = %d\n", (int)res,
1195                            smb_len(cli->evt_inbuf)));
1196
1197                 /* recv *might* have returned less than announced */
1198                 new_size = old_size + res;
1199
1200                 /* shrink, so I don't expect errors here */
1201                 cli->evt_inbuf = TALLOC_REALLOC_ARRAY(cli, cli->evt_inbuf,
1202                                                       char, new_size);
1203
1204                 while ((cli->evt_inbuf != NULL)
1205                        && ((smb_len(cli->evt_inbuf) + 4) <= new_size)) {
1206                         /*
1207                          * we've got a complete NBT level PDU in evt_inbuf
1208                          */
1209                         handle_incoming_pdu(cli);
1210                         new_size = talloc_get_size(cli->evt_inbuf);
1211                 }
1212         }
1213
1214         return;
1215
1216  sock_error:
1217
1218         for (req = cli->outstanding_requests; req; req = next) {
1219                 int i, num_async;
1220
1221                 next = req->next;
1222                 num_async = req->num_async;
1223
1224                 for (i=0; i<num_async; i++) {
1225                         async_req_nterror(req->async[i], status);
1226                 }
1227         }
1228         TALLOC_FREE(cli->fd_event);
1229         if (cli->fd != -1) {
1230                 close(cli->fd);
1231                 cli->fd = -1;
1232         }
1233 }
1234
1235
1236 struct cli_session_request_state {
1237         struct tevent_context *ev;
1238         int sock;
1239         uint32 len_hdr;
1240         struct iovec iov[3];
1241         uint8_t nb_session_response;
1242 };
1243
1244 static void cli_session_request_sent(struct tevent_req *subreq);
1245 static void cli_session_request_recvd(struct tevent_req *subreq);
1246
1247 struct tevent_req *cli_session_request_send(TALLOC_CTX *mem_ctx,
1248                                             struct tevent_context *ev,
1249                                             int sock,
1250                                             const struct nmb_name *called,
1251                                             const struct nmb_name *calling)
1252 {
1253         struct tevent_req *req, *subreq;
1254         struct cli_session_request_state *state;
1255
1256         req = tevent_req_create(mem_ctx, &state,
1257                                 struct cli_session_request_state);
1258         if (req == NULL) {
1259                 return NULL;
1260         }
1261         state->ev = ev;
1262         state->sock = sock;
1263
1264         state->iov[1].iov_base = name_mangle(
1265                 state, called->name, called->name_type);
1266         if (tevent_req_nomem(state->iov[1].iov_base, req)) {
1267                 return tevent_req_post(req, ev);
1268         }
1269         state->iov[1].iov_len = name_len(
1270                 (char *)state->iov[1].iov_base);
1271
1272         state->iov[2].iov_base = name_mangle(
1273                 state, calling->name, calling->name_type);
1274         if (tevent_req_nomem(state->iov[2].iov_base, req)) {
1275                 return tevent_req_post(req, ev);
1276         }
1277         state->iov[2].iov_len = name_len(
1278                 (char *)state->iov[2].iov_base);
1279
1280         _smb_setlen(((char *)&state->len_hdr),
1281                     state->iov[1].iov_len + state->iov[2].iov_len);
1282         SCVAL((char *)&state->len_hdr, 0, 0x81);
1283
1284         state->iov[0].iov_base = &state->len_hdr;
1285         state->iov[0].iov_len = sizeof(state->len_hdr);
1286
1287         subreq = writev_send(state, ev, NULL, sock, state->iov, 3);
1288         if (tevent_req_nomem(subreq, req)) {
1289                 return tevent_req_post(req, ev);
1290         }
1291         tevent_req_set_callback(subreq, cli_session_request_sent, req);
1292         return req;
1293 }
1294
1295 static void cli_session_request_sent(struct tevent_req *subreq)
1296 {
1297         struct tevent_req *req = tevent_req_callback_data(
1298                 subreq, struct tevent_req);
1299         struct cli_session_request_state *state = tevent_req_data(
1300                 req, struct cli_session_request_state);
1301         ssize_t ret;
1302         int err;
1303
1304         ret = writev_recv(subreq, &err);
1305         TALLOC_FREE(subreq);
1306         if (ret == -1) {
1307                 tevent_req_error(req, err);
1308                 return;
1309         }
1310         subreq = read_smb_send(state, state->ev, state->sock);
1311         if (tevent_req_nomem(subreq, req)) {
1312                 return;
1313         }
1314         tevent_req_set_callback(subreq, cli_session_request_recvd, req);
1315 }
1316
1317 static void cli_session_request_recvd(struct tevent_req *subreq)
1318 {
1319         struct tevent_req *req = tevent_req_callback_data(
1320                 subreq, struct tevent_req);
1321         struct cli_session_request_state *state = tevent_req_data(
1322                 req, struct cli_session_request_state);
1323         uint8_t *buf;
1324         ssize_t ret;
1325         int err;
1326
1327         ret = read_smb_recv(subreq, talloc_tos(), &buf, &err);
1328         TALLOC_FREE(subreq);
1329
1330         if (ret < 4) {
1331                 ret = -1;
1332                 err = EIO;
1333         }
1334         if (ret == -1) {
1335                 tevent_req_error(req, err);
1336                 return;
1337         }
1338         /*
1339          * In case of an error there is more information in the data
1340          * portion according to RFC1002. We're not subtle enough to
1341          * respond to the different error conditions, so drop the
1342          * error info here.
1343          */
1344         state->nb_session_response = CVAL(buf, 0);
1345         tevent_req_done(req);
1346 }
1347
1348 bool cli_session_request_recv(struct tevent_req *req, int *err, uint8_t *resp)
1349 {
1350         struct cli_session_request_state *state = tevent_req_data(
1351                 req, struct cli_session_request_state);
1352
1353         if (tevent_req_is_unix_error(req, err)) {
1354                 return false;
1355         }
1356         *resp = state->nb_session_response;
1357         return true;
1358 }