2 Unix SMB/CIFS implementation.
3 Infrastructure for async SMB client requests
4 Copyright (C) Volker Lendecke 2008
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.
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.
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/>.
23 * Read an smb packet asynchronously, discard keepalives
26 struct read_smb_state
{
27 struct tevent_context
*ev
;
32 static ssize_t
read_smb_more(uint8_t *buf
, size_t buflen
, void *private_data
);
33 static void read_smb_done(struct tevent_req
*subreq
);
35 static struct tevent_req
*read_smb_send(TALLOC_CTX
*mem_ctx
,
36 struct tevent_context
*ev
,
39 struct tevent_req
*result
, *subreq
;
40 struct read_smb_state
*state
;
42 result
= tevent_req_create(mem_ctx
, &state
, struct read_smb_state
);
49 subreq
= read_packet_send(state
, ev
, fd
, 4, read_smb_more
, NULL
);
53 tevent_req_set_callback(subreq
, read_smb_done
, result
);
60 static ssize_t
read_smb_more(uint8_t *buf
, size_t buflen
, void *private_data
)
63 return 0; /* We've been here, we're done */
65 return smb_len_large(buf
);
68 static void read_smb_done(struct tevent_req
*subreq
)
70 struct tevent_req
*req
= tevent_req_callback_data(
71 subreq
, struct tevent_req
);
72 struct read_smb_state
*state
= tevent_req_data(
73 req
, struct read_smb_state
);
77 len
= read_packet_recv(subreq
, state
, &state
->buf
, &err
);
80 tevent_req_error(req
, err
);
84 if (CVAL(state
->buf
, 0) == SMBkeepalive
) {
85 subreq
= read_packet_send(state
, state
->ev
, state
->fd
, 4,
87 if (tevent_req_nomem(subreq
, req
)) {
90 tevent_req_set_callback(subreq
, read_smb_done
, req
);
96 static ssize_t
read_smb_recv(struct tevent_req
*req
, TALLOC_CTX
*mem_ctx
,
97 uint8_t **pbuf
, int *perrno
)
99 struct read_smb_state
*state
= tevent_req_data(
100 req
, struct read_smb_state
);
102 if (tevent_req_is_unix_error(req
, perrno
)) {
105 *pbuf
= talloc_move(mem_ctx
, &state
->buf
);
106 return talloc_get_size(*pbuf
);
110 * Fetch an error out of a NBT packet
111 * @param[in] buf The SMB packet
112 * @retval The error, converted to NTSTATUS
115 NTSTATUS
cli_pull_error(char *buf
)
117 uint32_t flags2
= SVAL(buf
, smb_flg2
);
119 if (flags2
& FLAGS2_32_BIT_ERROR_CODES
) {
120 return NT_STATUS(IVAL(buf
, smb_rcls
));
123 /* if the client uses dos errors, but there is no error,
124 we should return no error here, otherwise it looks
125 like an unknown bad NT_STATUS. jmcd */
126 if (CVAL(buf
, smb_rcls
) == 0)
129 return NT_STATUS_DOS(CVAL(buf
, smb_rcls
), SVAL(buf
,smb_err
));
133 * Compatibility helper for the sync APIs: Fake NTSTATUS in cli->inbuf
134 * @param[in] cli The client connection that just received an error
135 * @param[in] status The error to set on "cli"
138 void cli_set_error(struct cli_state
*cli
, NTSTATUS status
)
140 uint32_t flags2
= SVAL(cli
->inbuf
, smb_flg2
);
142 if (NT_STATUS_IS_DOS(status
)) {
143 SSVAL(cli
->inbuf
, smb_flg2
,
144 flags2
& ~FLAGS2_32_BIT_ERROR_CODES
);
145 SCVAL(cli
->inbuf
, smb_rcls
, NT_STATUS_DOS_CLASS(status
));
146 SSVAL(cli
->inbuf
, smb_err
, NT_STATUS_DOS_CODE(status
));
150 SSVAL(cli
->inbuf
, smb_flg2
, flags2
| FLAGS2_32_BIT_ERROR_CODES
);
151 SIVAL(cli
->inbuf
, smb_rcls
, NT_STATUS_V(status
));
156 * @brief Find the smb_cmd offset of the last command pushed
157 * @param[in] buf The buffer we're building up
158 * @retval Where can we put our next andx cmd?
160 * While chaining requests, the "next" request we're looking at needs to put
161 * its SMB_Command before the data the previous request already built up added
162 * to the chain. Find the offset to the place where we have to put our cmd.
165 static bool find_andx_cmd_ofs(uint8_t *buf
, size_t *pofs
)
170 cmd
= CVAL(buf
, smb_com
);
172 SMB_ASSERT(is_andx_req(cmd
));
176 while (CVAL(buf
, ofs
) != 0xff) {
178 if (!is_andx_req(CVAL(buf
, ofs
))) {
183 * ofs is from start of smb header, so add the 4 length
184 * bytes. The next cmd is right after the wct field.
186 ofs
= SVAL(buf
, ofs
+2) + 4 + 1;
188 SMB_ASSERT(ofs
+4 < talloc_get_size(buf
));
196 * @brief Do the smb chaining at a buffer level
197 * @param[in] poutbuf Pointer to the talloc'ed buffer to be modified
198 * @param[in] smb_command The command that we want to issue
199 * @param[in] wct How many words?
200 * @param[in] vwv The words, already in network order
201 * @param[in] bytes_alignment How shall we align "bytes"?
202 * @param[in] num_bytes How many bytes?
203 * @param[in] bytes The data the request ships
205 * smb_splice_chain() adds the vwv and bytes to the request already present in
209 bool smb_splice_chain(uint8_t **poutbuf
, uint8_t smb_command
,
210 uint8_t wct
, const uint16_t *vwv
,
211 size_t bytes_alignment
,
212 uint32_t num_bytes
, const uint8_t *bytes
)
215 size_t old_size
, new_size
;
217 size_t chain_padding
= 0;
218 size_t bytes_padding
= 0;
221 old_size
= talloc_get_size(*poutbuf
);
224 * old_size == smb_wct means we're pushing the first request in for
228 first_request
= (old_size
== smb_wct
);
230 if (!first_request
&& ((old_size
% 4) != 0)) {
232 * Align the wct field of subsequent requests to a 4-byte
235 chain_padding
= 4 - (old_size
% 4);
239 * After the old request comes the new wct field (1 byte), the vwv's
240 * and the num_bytes field. After at we might need to align the bytes
241 * given to us to "bytes_alignment", increasing the num_bytes value.
244 new_size
= old_size
+ chain_padding
+ 1 + wct
* sizeof(uint16_t) + 2;
246 if ((bytes_alignment
!= 0) && ((new_size
% bytes_alignment
) != 0)) {
247 bytes_padding
= bytes_alignment
- (new_size
% bytes_alignment
);
250 new_size
+= bytes_padding
+ num_bytes
;
252 if ((smb_command
!= SMBwriteX
) && (new_size
> 0xffff)) {
253 DEBUG(1, ("splice_chain: %u bytes won't fit\n",
254 (unsigned)new_size
));
258 outbuf
= TALLOC_REALLOC_ARRAY(NULL
, *poutbuf
, uint8_t, new_size
);
259 if (outbuf
== NULL
) {
260 DEBUG(0, ("talloc failed\n"));
266 SCVAL(outbuf
, smb_com
, smb_command
);
270 if (!find_andx_cmd_ofs(outbuf
, &andx_cmd_ofs
)) {
271 DEBUG(1, ("invalid command chain\n"));
272 *poutbuf
= TALLOC_REALLOC_ARRAY(
273 NULL
, *poutbuf
, uint8_t, old_size
);
277 if (chain_padding
!= 0) {
278 memset(outbuf
+ old_size
, 0, chain_padding
);
279 old_size
+= chain_padding
;
282 SCVAL(outbuf
, andx_cmd_ofs
, smb_command
);
283 SSVAL(outbuf
, andx_cmd_ofs
+ 2, old_size
- 4);
289 * Push the chained request:
294 SCVAL(outbuf
, ofs
, wct
);
301 memcpy(outbuf
+ ofs
, vwv
, sizeof(uint16_t) * wct
);
302 ofs
+= sizeof(uint16_t) * wct
;
308 SSVAL(outbuf
, ofs
, num_bytes
+ bytes_padding
);
309 ofs
+= sizeof(uint16_t);
315 if (bytes_padding
!= 0) {
316 memset(outbuf
+ ofs
, 0, bytes_padding
);
317 ofs
+= bytes_padding
;
324 memcpy(outbuf
+ ofs
, bytes
, num_bytes
);
330 * Figure out if there is an andx command behind the current one
331 * @param[in] buf The smb buffer to look at
332 * @param[in] ofs The offset to the wct field that is followed by the cmd
333 * @retval Is there a command following?
336 static bool have_andx_command(const char *buf
, uint16_t ofs
)
339 size_t buflen
= talloc_get_size(buf
);
341 if ((ofs
== buflen
-1) || (ofs
== buflen
)) {
345 wct
= CVAL(buf
, ofs
);
348 * Not enough space for the command and a following pointer
352 return (CVAL(buf
, ofs
+1) != 0xff);
355 #define MAX_SMB_IOV 5
357 struct cli_smb_state
{
358 struct tevent_context
*ev
;
359 struct cli_state
*cli
;
360 uint8_t header
[smb_wct
+1]; /* Space for the header including the wct */
363 * For normal requests, cli_smb_req_send chooses a mid. Secondary
364 * trans requests need to use the mid of the primary request, so we
365 * need a place to store it. Assume it's set if != 0.
370 uint8_t bytecount_buf
[2];
372 struct iovec iov
[MAX_SMB_IOV
+3];
378 struct tevent_req
**chained_requests
;
381 static uint16_t cli_alloc_mid(struct cli_state
*cli
)
383 int num_pending
= talloc_array_length(cli
->pending
);
390 if ((result
== 0) || (result
== 0xffff)) {
394 for (i
=0; i
<num_pending
; i
++) {
395 if (result
== cli_smb_req_mid(cli
->pending
[i
])) {
400 if (i
== num_pending
) {
406 void cli_smb_req_unset_pending(struct tevent_req
*req
)
408 struct cli_smb_state
*state
= tevent_req_data(
409 req
, struct cli_smb_state
);
410 struct cli_state
*cli
= state
->cli
;
411 int num_pending
= talloc_array_length(cli
->pending
);
414 if (num_pending
== 1) {
416 * The pending read_smb tevent_req is a child of
417 * cli->pending. So if nothing is pending anymore, we need to
418 * delete the socket read fde.
420 TALLOC_FREE(cli
->pending
);
424 for (i
=0; i
<num_pending
; i
++) {
425 if (req
== cli
->pending
[i
]) {
429 if (i
== num_pending
) {
431 * Something's seriously broken. Just returning here is the
432 * right thing nevertheless, the point of this routine is to
433 * remove ourselves from cli->pending.
439 * Remove ourselves from the cli->pending array
441 if (num_pending
> 1) {
442 cli
->pending
[i
] = cli
->pending
[num_pending
-1];
446 * No NULL check here, we're shrinking by sizeof(void *), and
447 * talloc_realloc just adjusts the size for this.
449 cli
->pending
= talloc_realloc(NULL
, cli
->pending
, struct tevent_req
*,
454 static int cli_smb_req_destructor(struct tevent_req
*req
)
456 cli_smb_req_unset_pending(req
);
460 static void cli_smb_received(struct tevent_req
*subreq
);
462 bool cli_smb_req_set_pending(struct tevent_req
*req
)
464 struct cli_smb_state
*state
= tevent_req_data(
465 req
, struct cli_smb_state
);
466 struct cli_state
*cli
;
467 struct tevent_req
**pending
;
469 struct tevent_req
*subreq
;
472 num_pending
= talloc_array_length(cli
->pending
);
474 pending
= talloc_realloc(cli
, cli
->pending
, struct tevent_req
*,
476 if (pending
== NULL
) {
479 pending
[num_pending
] = req
;
480 cli
->pending
= pending
;
481 talloc_set_destructor(req
, cli_smb_req_destructor
);
483 if (num_pending
> 0) {
488 * We're the first ones, add the read_smb request that waits for the
489 * answer from the server
491 subreq
= read_smb_send(cli
->pending
, state
->ev
, cli
->fd
);
492 if (subreq
== NULL
) {
493 cli_smb_req_unset_pending(req
);
496 tevent_req_set_callback(subreq
, cli_smb_received
, cli
);
501 * Fetch a smb request's mid. Only valid after the request has been sent by
502 * cli_smb_req_send().
504 uint16_t cli_smb_req_mid(struct tevent_req
*req
)
506 struct cli_smb_state
*state
= tevent_req_data(
507 req
, struct cli_smb_state
);
508 return SVAL(state
->header
, smb_mid
);
511 void cli_smb_req_set_mid(struct tevent_req
*req
, uint16_t mid
)
513 struct cli_smb_state
*state
= tevent_req_data(
514 req
, struct cli_smb_state
);
518 static size_t iov_len(const struct iovec
*iov
, int count
)
522 for (i
=0; i
<count
; i
++) {
523 result
+= iov
[i
].iov_len
;
528 static uint8_t *iov_concat(TALLOC_CTX
*mem_ctx
, const struct iovec
*iov
,
531 size_t len
= iov_len(iov
, count
);
536 buf
= talloc_array(mem_ctx
, uint8_t, len
);
541 for (i
=0; i
<count
; i
++) {
542 memcpy(buf
+copied
, iov
[i
].iov_base
, iov
[i
].iov_len
);
543 copied
+= iov
[i
].iov_len
;
548 struct tevent_req
*cli_smb_req_create(TALLOC_CTX
*mem_ctx
,
549 struct event_context
*ev
,
550 struct cli_state
*cli
,
552 uint8_t additional_flags
,
553 uint8_t wct
, uint16_t *vwv
,
555 struct iovec
*bytes_iov
)
557 struct tevent_req
*result
;
558 struct cli_smb_state
*state
;
560 if (iov_count
> MAX_SMB_IOV
) {
562 * Should not happen :-)
567 result
= tevent_req_create(mem_ctx
, &state
, struct cli_smb_state
);
568 if (result
== NULL
) {
573 state
->mid
= 0; /* Set to auto-choose in cli_smb_req_send */
574 state
->chain_num
= 0;
575 state
->chained_requests
= NULL
;
577 cli_setup_packet_buf(cli
, (char *)state
->header
);
578 SCVAL(state
->header
, smb_com
, smb_command
);
579 SSVAL(state
->header
, smb_tid
, cli
->cnum
);
580 SCVAL(state
->header
, smb_wct
, wct
);
584 SSVAL(state
->bytecount_buf
, 0, iov_len(bytes_iov
, iov_count
));
586 state
->iov
[0].iov_base
= (void *)state
->header
;
587 state
->iov
[0].iov_len
= sizeof(state
->header
);
588 state
->iov
[1].iov_base
= (void *)state
->vwv
;
589 state
->iov
[1].iov_len
= wct
* sizeof(uint16_t);
590 state
->iov
[2].iov_base
= (void *)state
->bytecount_buf
;
591 state
->iov
[2].iov_len
= sizeof(uint16_t);
593 if (iov_count
!= 0) {
594 memcpy(&state
->iov
[3], bytes_iov
,
595 iov_count
* sizeof(*bytes_iov
));
597 state
->iov_count
= iov_count
+ 3;
602 static NTSTATUS
cli_signv(struct cli_state
*cli
, struct iovec
*iov
, int count
,
608 * Obvious optimization: Make cli_calculate_sign_mac work with struct
609 * iovec directly. MD5Update would do that just fine.
612 if ((count
<= 0) || (iov
[0].iov_len
< smb_wct
)) {
613 return NT_STATUS_INVALID_PARAMETER
;
616 buf
= iov_concat(talloc_tos(), iov
, count
);
618 return NT_STATUS_NO_MEMORY
;
621 cli_calculate_sign_mac(cli
, (char *)buf
, seqnum
);
622 memcpy(iov
[0].iov_base
, buf
, iov
[0].iov_len
);
628 static void cli_smb_sent(struct tevent_req
*subreq
);
630 static NTSTATUS
cli_smb_req_iov_send(struct tevent_req
*req
,
631 struct cli_smb_state
*state
,
632 struct iovec
*iov
, int iov_count
)
634 struct tevent_req
*subreq
;
637 if (state
->cli
->fd
== -1) {
638 return NT_STATUS_CONNECTION_INVALID
;
641 if (iov
[0].iov_len
< smb_wct
) {
642 return NT_STATUS_INVALID_PARAMETER
;
645 if (state
->mid
!= 0) {
646 SSVAL(iov
[0].iov_base
, smb_mid
, state
->mid
);
648 SSVAL(iov
[0].iov_base
, smb_mid
, cli_alloc_mid(state
->cli
));
651 smb_setlen((char *)iov
[0].iov_base
, iov_len(iov
, iov_count
) - 4);
653 status
= cli_signv(state
->cli
, iov
, iov_count
, &state
->seqnum
);
655 if (!NT_STATUS_IS_OK(status
)) {
659 if (cli_encryption_on(state
->cli
)) {
662 buf
= (char *)iov_concat(talloc_tos(), iov
, iov_count
);
664 return NT_STATUS_NO_MEMORY
;
666 status
= cli_encrypt_message(state
->cli
, (char *)buf
,
669 if (!NT_STATUS_IS_OK(status
)) {
670 DEBUG(0, ("Error in encrypting client message: %s\n",
674 buf
= (char *)talloc_memdup(state
, enc_buf
,
678 return NT_STATUS_NO_MEMORY
;
680 iov
[0].iov_base
= (void *)buf
;
681 iov
[0].iov_len
= talloc_get_size(buf
);
682 subreq
= writev_send(state
, state
->ev
, state
->cli
->outgoing
,
683 state
->cli
->fd
, false, iov
, 1);
685 subreq
= writev_send(state
, state
->ev
, state
->cli
->outgoing
,
686 state
->cli
->fd
, false, iov
, iov_count
);
688 if (subreq
== NULL
) {
689 return NT_STATUS_NO_MEMORY
;
691 tevent_req_set_callback(subreq
, cli_smb_sent
, req
);
695 NTSTATUS
cli_smb_req_send(struct tevent_req
*req
)
697 struct cli_smb_state
*state
= tevent_req_data(
698 req
, struct cli_smb_state
);
700 return cli_smb_req_iov_send(req
, state
, state
->iov
, state
->iov_count
);
703 struct tevent_req
*cli_smb_send(TALLOC_CTX
*mem_ctx
,
704 struct event_context
*ev
,
705 struct cli_state
*cli
,
707 uint8_t additional_flags
,
708 uint8_t wct
, uint16_t *vwv
,
710 const uint8_t *bytes
)
712 struct tevent_req
*req
;
716 iov
.iov_base
= CONST_DISCARD(void *, bytes
);
717 iov
.iov_len
= num_bytes
;
719 req
= cli_smb_req_create(mem_ctx
, ev
, cli
, smb_command
,
720 additional_flags
, wct
, vwv
, 1, &iov
);
725 status
= cli_smb_req_send(req
);
726 if (!NT_STATUS_IS_OK(status
)) {
727 tevent_req_nterror(req
, status
);
728 return tevent_req_post(req
, ev
);
733 static void cli_smb_sent(struct tevent_req
*subreq
)
735 struct tevent_req
*req
= tevent_req_callback_data(
736 subreq
, struct tevent_req
);
737 struct cli_smb_state
*state
= tevent_req_data(
738 req
, struct cli_smb_state
);
742 nwritten
= writev_recv(subreq
, &err
);
744 if (nwritten
== -1) {
745 if (state
->cli
->fd
!= -1) {
746 close(state
->cli
->fd
);
749 tevent_req_nterror(req
, map_nt_error_from_unix(err
));
753 switch (CVAL(state
->header
, smb_com
)) {
759 tevent_req_done(req
);
762 if ((CVAL(state
->header
, smb_wct
) == 8) &&
763 (CVAL(state
->vwv
+3, 0) == LOCKING_ANDX_OPLOCK_RELEASE
)) {
765 tevent_req_done(req
);
770 if (!cli_smb_req_set_pending(req
)) {
771 tevent_req_nterror(req
, NT_STATUS_NO_MEMORY
);
776 static void cli_smb_received(struct tevent_req
*subreq
)
778 struct cli_state
*cli
= tevent_req_callback_data(
779 subreq
, struct cli_state
);
780 struct tevent_req
*req
;
781 struct cli_smb_state
*state
;
782 struct tevent_context
*ev
;
791 received
= read_smb_recv(subreq
, talloc_tos(), &inbuf
, &err
);
793 if (received
== -1) {
798 status
= map_nt_error_from_unix(err
);
802 if ((IVAL(inbuf
, 4) != 0x424d53ff) /* 0xFF"SMB" */
803 && (SVAL(inbuf
, 4) != 0x45ff)) /* 0xFF"E" */ {
804 DEBUG(10, ("Got non-SMB PDU\n"));
805 status
= NT_STATUS_INVALID_NETWORK_RESPONSE
;
809 if (cli_encryption_on(cli
) && (CVAL(inbuf
, 0) == 0)) {
810 uint16_t enc_ctx_num
;
812 status
= get_enc_ctx_num(inbuf
, &enc_ctx_num
);
813 if (!NT_STATUS_IS_OK(status
)) {
814 DEBUG(10, ("get_enc_ctx_num returned %s\n",
819 if (enc_ctx_num
!= cli
->trans_enc_state
->enc_ctx_num
) {
820 DEBUG(10, ("wrong enc_ctx %d, expected %d\n",
822 cli
->trans_enc_state
->enc_ctx_num
));
823 status
= NT_STATUS_INVALID_HANDLE
;
827 status
= common_decrypt_buffer(cli
->trans_enc_state
,
829 if (!NT_STATUS_IS_OK(status
)) {
830 DEBUG(10, ("common_decrypt_buffer returned %s\n",
836 mid
= SVAL(inbuf
, smb_mid
);
837 num_pending
= talloc_array_length(cli
->pending
);
839 for (i
=0; i
<num_pending
; i
++) {
840 if (mid
== cli_smb_req_mid(cli
->pending
[i
])) {
844 if (i
== num_pending
) {
845 /* Dump unexpected reply */
850 oplock_break
= false;
854 * Paranoia checks that this is really an oplock break request.
856 oplock_break
= (smb_len(inbuf
) == 51); /* hdr + 8 words */
857 oplock_break
&= ((CVAL(inbuf
, smb_flg
) & FLAG_REPLY
) == 0);
858 oplock_break
&= (CVAL(inbuf
, smb_com
) == SMBlockingX
);
859 oplock_break
&= (SVAL(inbuf
, smb_vwv6
) == 0);
860 oplock_break
&= (SVAL(inbuf
, smb_vwv7
) == 0);
863 /* Dump unexpected reply */
869 req
= cli
->pending
[i
];
870 state
= tevent_req_data(req
, struct cli_smb_state
);
873 if (!oplock_break
/* oplock breaks are not signed */
874 && !cli_check_sign_mac(cli
, (char *)inbuf
, state
->seqnum
+1)) {
875 DEBUG(10, ("cli_check_sign_mac failed\n"));
877 status
= NT_STATUS_ACCESS_DENIED
;
881 if (state
->chained_requests
== NULL
) {
882 state
->inbuf
= talloc_move(state
, &inbuf
);
883 talloc_set_destructor(req
, NULL
);
884 cli_smb_req_destructor(req
);
885 tevent_req_done(req
);
887 struct tevent_req
**chain
= talloc_move(
888 talloc_tos(), &state
->chained_requests
);
889 int num_chained
= talloc_array_length(chain
);
891 for (i
=0; i
<num_chained
; i
++) {
892 state
= tevent_req_data(chain
[i
], struct
894 state
->inbuf
= inbuf
;
895 state
->chain_num
= i
;
896 tevent_req_done(chain
[i
]);
902 if (talloc_array_length(cli
->pending
) > 0) {
904 * Set up another read request for the other pending cli_smb
907 state
= tevent_req_data(cli
->pending
[0], struct cli_smb_state
);
908 subreq
= read_smb_send(cli
->pending
, state
->ev
, cli
->fd
);
909 if (subreq
== NULL
) {
910 status
= NT_STATUS_NO_MEMORY
;
913 tevent_req_set_callback(subreq
, cli_smb_received
, cli
);
918 * Cancel all pending requests. We don't do a for-loop walking
919 * cli->pending because that array changes in
920 * cli_smb_req_destructor().
922 while (talloc_array_length(cli
->pending
) > 0) {
923 req
= cli
->pending
[0];
924 talloc_set_destructor(req
, NULL
);
925 cli_smb_req_destructor(req
);
926 tevent_req_nterror(req
, status
);
930 NTSTATUS
cli_smb_recv(struct tevent_req
*req
, uint8_t min_wct
,
931 uint8_t *pwct
, uint16_t **pvwv
,
932 uint32_t *pnum_bytes
, uint8_t **pbytes
)
934 struct cli_smb_state
*state
= tevent_req_data(
935 req
, struct cli_smb_state
);
936 NTSTATUS status
= NT_STATUS_OK
;
939 size_t wct_ofs
, bytes_offset
;
942 if (tevent_req_is_nterror(req
, &status
)) {
946 if (state
->inbuf
== NULL
) {
947 /* This was a request without a reply */
952 cmd
= CVAL(state
->inbuf
, smb_com
);
954 for (i
=0; i
<state
->chain_num
; i
++) {
955 if (i
< state
->chain_num
-1) {
957 return NT_STATUS_REQUEST_ABORTED
;
959 if (!is_andx_req(cmd
)) {
960 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
964 if (!have_andx_command((char *)state
->inbuf
, wct_ofs
)) {
966 * This request was not completed because a previous
967 * request in the chain had received an error.
969 return NT_STATUS_REQUEST_ABORTED
;
972 wct_ofs
= SVAL(state
->inbuf
, wct_ofs
+ 3);
975 * Skip the all-present length field. No overflow, we've just
976 * put a 16-bit value into a size_t.
980 if (wct_ofs
+2 > talloc_get_size(state
->inbuf
)) {
981 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
984 cmd
= CVAL(state
->inbuf
, wct_ofs
+ 1);
987 status
= cli_pull_error((char *)state
->inbuf
);
989 if (!have_andx_command((char *)state
->inbuf
, wct_ofs
)
990 && NT_STATUS_IS_ERR(status
)) {
992 * The last command takes the error code. All further commands
993 * down the requested chain will get a
994 * NT_STATUS_REQUEST_ABORTED.
999 wct
= CVAL(state
->inbuf
, wct_ofs
);
1000 bytes_offset
= wct_ofs
+ 1 + wct
* sizeof(uint16_t);
1001 num_bytes
= SVAL(state
->inbuf
, bytes_offset
);
1003 if (wct
< min_wct
) {
1004 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
1008 * wct_ofs is a 16-bit value plus 4, wct is a 8-bit value, num_bytes
1009 * is a 16-bit value. So bytes_offset being size_t should be far from
1012 if ((bytes_offset
+ 2 > talloc_get_size(state
->inbuf
))
1013 || (bytes_offset
> 0xffff)) {
1014 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
1021 *pvwv
= (uint16_t *)(state
->inbuf
+ wct_ofs
+ 1);
1023 if (pnum_bytes
!= NULL
) {
1024 *pnum_bytes
= num_bytes
;
1026 if (pbytes
!= NULL
) {
1027 *pbytes
= (uint8_t *)state
->inbuf
+ bytes_offset
+ 2;
1030 return NT_STATUS_OK
;
1033 size_t cli_smb_wct_ofs(struct tevent_req
**reqs
, int num_reqs
)
1038 wct_ofs
= smb_wct
- 4;
1040 for (i
=0; i
<num_reqs
; i
++) {
1041 struct cli_smb_state
*state
;
1042 state
= tevent_req_data(reqs
[i
], struct cli_smb_state
);
1043 wct_ofs
+= iov_len(state
->iov
+1, state
->iov_count
-1);
1044 wct_ofs
= (wct_ofs
+ 3) & ~3;
1049 NTSTATUS
cli_smb_chain_send(struct tevent_req
**reqs
, int num_reqs
)
1051 struct cli_smb_state
*first_state
= tevent_req_data(
1052 reqs
[0], struct cli_smb_state
);
1053 struct cli_smb_state
*last_state
= tevent_req_data(
1054 reqs
[num_reqs
-1], struct cli_smb_state
);
1055 struct cli_smb_state
*state
;
1057 size_t chain_padding
= 0;
1059 struct iovec
*iov
= NULL
;
1060 struct iovec
*this_iov
;
1064 for (i
=0; i
<num_reqs
; i
++) {
1065 state
= tevent_req_data(reqs
[i
], struct cli_smb_state
);
1066 iovlen
+= state
->iov_count
;
1069 iov
= talloc_array(last_state
, struct iovec
, iovlen
);
1071 return NT_STATUS_NO_MEMORY
;
1074 first_state
->chained_requests
= (struct tevent_req
**)talloc_memdup(
1075 last_state
, reqs
, sizeof(*reqs
) * num_reqs
);
1076 if (first_state
->chained_requests
== NULL
) {
1078 return NT_STATUS_NO_MEMORY
;
1081 wct_offset
= smb_wct
- 4;
1084 for (i
=0; i
<num_reqs
; i
++) {
1085 size_t next_padding
= 0;
1088 state
= tevent_req_data(reqs
[i
], struct cli_smb_state
);
1090 if (i
< num_reqs
-1) {
1091 if (!is_andx_req(CVAL(state
->header
, smb_com
))
1092 || CVAL(state
->header
, smb_wct
) < 2) {
1094 TALLOC_FREE(first_state
->chained_requests
);
1095 return NT_STATUS_INVALID_PARAMETER
;
1099 wct_offset
+= iov_len(state
->iov
+1, state
->iov_count
-1) + 1;
1100 if ((wct_offset
% 4) != 0) {
1101 next_padding
= 4 - (wct_offset
% 4);
1103 wct_offset
+= next_padding
;
1106 if (i
< num_reqs
-1) {
1107 struct cli_smb_state
*next_state
= tevent_req_data(
1108 reqs
[i
+1], struct cli_smb_state
);
1109 SCVAL(vwv
+0, 0, CVAL(next_state
->header
, smb_com
));
1111 SSVAL(vwv
+1, 0, wct_offset
);
1112 } else if (is_andx_req(CVAL(state
->header
, smb_com
))) {
1113 /* properly end the chain */
1114 SCVAL(vwv
+0, 0, 0xff);
1115 SCVAL(vwv
+0, 1, 0xff);
1120 this_iov
[0] = state
->iov
[0];
1123 * This one is a bit subtle. We have to add
1124 * chain_padding bytes between the requests, and we
1125 * have to also include the wct field of the
1126 * subsequent requests. We use the subsequent header
1127 * for the padding, it contains the wct field in its
1130 this_iov
[0].iov_len
= chain_padding
+1;
1131 this_iov
[0].iov_base
= (void *)&state
->header
[
1132 sizeof(state
->header
) - this_iov
[0].iov_len
];
1133 memset(this_iov
[0].iov_base
, 0, this_iov
[0].iov_len
-1);
1135 memcpy(this_iov
+1, state
->iov
+1,
1136 sizeof(struct iovec
) * (state
->iov_count
-1));
1137 this_iov
+= state
->iov_count
;
1138 chain_padding
= next_padding
;
1141 status
= cli_smb_req_iov_send(reqs
[0], last_state
, iov
, iovlen
);
1142 if (!NT_STATUS_IS_OK(status
)) {
1144 TALLOC_FREE(first_state
->chained_requests
);
1148 return NT_STATUS_OK
;
1151 uint8_t *cli_smb_inbuf(struct tevent_req
*req
)
1153 struct cli_smb_state
*state
= tevent_req_data(
1154 req
, struct cli_smb_state
);
1155 return state
->inbuf
;
1158 bool cli_has_async_calls(struct cli_state
*cli
)
1160 return ((tevent_queue_length(cli
->outgoing
) != 0)
1161 || (talloc_array_length(cli
->pending
) != 0));
1164 struct cli_smb_oplock_break_waiter_state
{
1169 static void cli_smb_oplock_break_waiter_done(struct tevent_req
*subreq
);
1171 struct tevent_req
*cli_smb_oplock_break_waiter_send(TALLOC_CTX
*mem_ctx
,
1172 struct event_context
*ev
,
1173 struct cli_state
*cli
)
1175 struct tevent_req
*req
, *subreq
;
1176 struct cli_smb_oplock_break_waiter_state
*state
;
1177 struct cli_smb_state
*smb_state
;
1179 req
= tevent_req_create(mem_ctx
, &state
,
1180 struct cli_smb_oplock_break_waiter_state
);
1186 * Create a fake SMB request that we will never send out. This is only
1187 * used to be set into the pending queue with the right mid.
1189 subreq
= cli_smb_req_create(mem_ctx
, ev
, cli
, 0, 0, 0, NULL
, 0, NULL
);
1190 if (tevent_req_nomem(subreq
, req
)) {
1191 return tevent_req_post(req
, ev
);
1193 smb_state
= tevent_req_data(subreq
, struct cli_smb_state
);
1194 SSVAL(smb_state
->header
, smb_mid
, 0xffff);
1196 if (!cli_smb_req_set_pending(subreq
)) {
1197 tevent_req_nterror(req
, NT_STATUS_NO_MEMORY
);
1198 return tevent_req_post(req
, ev
);
1200 tevent_req_set_callback(subreq
, cli_smb_oplock_break_waiter_done
, req
);
1204 static void cli_smb_oplock_break_waiter_done(struct tevent_req
*subreq
)
1206 struct tevent_req
*req
= tevent_req_callback_data(
1207 subreq
, struct tevent_req
);
1208 struct cli_smb_oplock_break_waiter_state
*state
= tevent_req_data(
1209 req
, struct cli_smb_oplock_break_waiter_state
);
1216 status
= cli_smb_recv(subreq
, 8, &wct
, &vwv
, &num_bytes
, &bytes
);
1217 if (!NT_STATUS_IS_OK(status
)) {
1218 TALLOC_FREE(subreq
);
1219 tevent_req_nterror(req
, status
);
1222 state
->fnum
= SVAL(vwv
+2, 0);
1223 state
->level
= CVAL(vwv
+3, 1);
1224 tevent_req_done(req
);
1227 NTSTATUS
cli_smb_oplock_break_waiter_recv(struct tevent_req
*req
,
1231 struct cli_smb_oplock_break_waiter_state
*state
= tevent_req_data(
1232 req
, struct cli_smb_oplock_break_waiter_state
);
1235 if (tevent_req_is_nterror(req
, &status
)) {
1238 *pfnum
= state
->fnum
;
1239 *plevel
= state
->level
;
1240 return NT_STATUS_OK
;