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 * Fetch an error out of a NBT packet
26 NTSTATUS
cli_pull_error(char *buf
)
28 uint32_t flags2
= SVAL(buf
, smb_flg2
);
30 if (flags2
& FLAGS2_32_BIT_ERROR_CODES
) {
31 return NT_STATUS(IVAL(buf
, smb_rcls
));
34 return NT_STATUS_DOS(CVAL(buf
, smb_rcls
), SVAL(buf
,smb_err
));
38 * Compatibility helper for the sync APIs: Fake NTSTATUS in cli->inbuf
41 void cli_set_error(struct cli_state
*cli
, NTSTATUS status
)
43 uint32_t flags2
= SVAL(cli
->inbuf
, smb_flg2
);
45 if (NT_STATUS_IS_DOS(status
)) {
46 SSVAL(cli
->inbuf
, smb_flg2
,
47 flags2
& ~FLAGS2_32_BIT_ERROR_CODES
);
48 SCVAL(cli
->inbuf
, smb_rcls
, NT_STATUS_DOS_CLASS(status
));
49 SSVAL(cli
->inbuf
, smb_err
, NT_STATUS_DOS_CODE(status
));
53 SSVAL(cli
->inbuf
, smb_flg2
, flags2
| FLAGS2_32_BIT_ERROR_CODES
);
54 SIVAL(cli
->inbuf
, smb_rcls
, NT_STATUS_V(status
));
62 static uint16_t cli_new_mid(struct cli_state
*cli
)
65 struct cli_request
*req
;
73 for (req
= cli
->outstanding_requests
; req
; req
= req
->next
) {
74 if (result
== req
->mid
) {
85 static char *cli_request_print(TALLOC_CTX
*mem_ctx
, struct async_req
*req
)
87 char *result
= async_req_print(mem_ctx
, req
);
88 struct cli_request
*cli_req
= cli_request_get(req
);
94 return talloc_asprintf_append_buffer(
95 result
, "mid=%d\n", cli_req
->mid
);
98 static int cli_request_destructor(struct cli_request
*req
)
100 if (req
->enc_state
!= NULL
) {
101 common_free_enc_buffer(req
->enc_state
, req
->outbuf
);
103 DLIST_REMOVE(req
->cli
->outstanding_requests
, req
);
108 * Create a fresh async smb request
111 struct async_req
*cli_request_new(TALLOC_CTX
*mem_ctx
,
112 struct event_context
*ev
,
113 struct cli_state
*cli
,
114 uint8_t num_words
, size_t num_bytes
,
115 struct cli_request
**preq
)
117 struct async_req
*result
;
118 struct cli_request
*cli_req
;
119 size_t bufsize
= smb_size
+ num_words
* 2 + num_bytes
;
121 result
= async_req_new(mem_ctx
, ev
);
122 if (result
== NULL
) {
126 cli_req
= (struct cli_request
*)talloc_size(
127 result
, sizeof(*cli_req
) + bufsize
);
128 if (cli_req
== NULL
) {
132 talloc_set_name_const(cli_req
, "struct cli_request");
133 result
->private_data
= cli_req
;
134 result
->print
= cli_request_print
;
136 cli_req
->async
= result
;
138 cli_req
->outbuf
= ((char *)cli_req
+ sizeof(*cli_req
));
140 cli_req
->mid
= cli_new_mid(cli
);
141 cli_req
->inbuf
= NULL
;
142 cli_req
->enc_state
= NULL
;
144 SCVAL(cli_req
->outbuf
, smb_wct
, num_words
);
145 SSVAL(cli_req
->outbuf
, smb_vwv
+ num_words
* 2, num_bytes
);
147 DLIST_ADD_END(cli
->outstanding_requests
, cli_req
,
148 struct cli_request
*);
149 talloc_set_destructor(cli_req
, cli_request_destructor
);
151 DEBUG(10, ("cli_request_new: mid=%d\n", cli_req
->mid
));
158 * Convenience function to get the SMB part out of an async_req
161 struct cli_request
*cli_request_get(struct async_req
*req
)
166 return talloc_get_type_abort(req
->private_data
, struct cli_request
);
170 * A PDU has arrived on cli->evt_inbuf
173 static void handle_incoming_pdu(struct cli_state
*cli
)
175 struct cli_request
*req
;
177 size_t raw_pdu_len
, buf_len
, pdu_len
, rest_len
;
182 * The encrypted PDU len might differ from the unencrypted one
184 raw_pdu_len
= smb_len(cli
->evt_inbuf
) + 4;
185 buf_len
= talloc_get_size(cli
->evt_inbuf
);
186 rest_len
= buf_len
- raw_pdu_len
;
188 if (buf_len
== raw_pdu_len
) {
190 * Optimal case: Exactly one PDU was in the socket buffer
192 pdu
= cli
->evt_inbuf
;
193 cli
->evt_inbuf
= NULL
;
196 DEBUG(11, ("buf_len = %d, raw_pdu_len = %d, splitting "
197 "buffer\n", (int)buf_len
, (int)raw_pdu_len
));
199 if (raw_pdu_len
< rest_len
) {
201 * The PDU is shorter, talloc_memdup that one.
203 pdu
= (char *)talloc_memdup(
204 cli
, cli
->evt_inbuf
, raw_pdu_len
);
206 memmove(cli
->evt_inbuf
, cli
->evt_inbuf
+ raw_pdu_len
,
207 buf_len
- raw_pdu_len
);
209 cli
->evt_inbuf
= TALLOC_REALLOC_ARRAY(
210 NULL
, cli
->evt_inbuf
, char, rest_len
);
213 status
= NT_STATUS_NO_MEMORY
;
214 goto invalidate_requests
;
219 * The PDU is larger than the rest, talloc_memdup the
222 pdu
= cli
->evt_inbuf
;
224 cli
->evt_inbuf
= (char *)talloc_memdup(
225 cli
, pdu
+ raw_pdu_len
, rest_len
);
227 if (cli
->evt_inbuf
== NULL
) {
228 status
= NT_STATUS_NO_MEMORY
;
229 goto invalidate_requests
;
236 * TODO: Handle oplock break requests
239 if (cli_encryption_on(cli
) && CVAL(pdu
, 0) == 0) {
240 uint16_t enc_ctx_num
;
242 status
= get_enc_ctx_num((uint8_t *)pdu
, &enc_ctx_num
);
243 if (!NT_STATUS_IS_OK(status
)) {
244 DEBUG(10, ("get_enc_ctx_num returned %s\n",
246 goto invalidate_requests
;
249 if (enc_ctx_num
!= cli
->trans_enc_state
->enc_ctx_num
) {
250 DEBUG(10, ("wrong enc_ctx %d, expected %d\n",
252 cli
->trans_enc_state
->enc_ctx_num
));
253 status
= NT_STATUS_INVALID_HANDLE
;
254 goto invalidate_requests
;
257 status
= common_decrypt_buffer(cli
->trans_enc_state
,
259 if (!NT_STATUS_IS_OK(status
)) {
260 DEBUG(10, ("common_decrypt_buffer returned %s\n",
262 goto invalidate_requests
;
266 if (!cli_check_sign_mac(cli
, pdu
)) {
267 DEBUG(10, ("cli_check_sign_mac failed\n"));
268 status
= NT_STATUS_ACCESS_DENIED
;
269 goto invalidate_requests
;
272 mid
= SVAL(pdu
, smb_mid
);
274 DEBUG(10, ("handle_incoming_pdu: got mid %d\n", mid
));
276 for (req
= cli
->outstanding_requests
; req
; req
= req
->next
) {
277 if (req
->mid
== mid
) {
282 pdu_len
= smb_len(pdu
) + 4;
285 DEBUG(3, ("Request for mid %d not found, dumping PDU\n", mid
));
291 req
->inbuf
= talloc_move(req
, &pdu
);
293 async_req_done(req
->async
);
298 DEBUG(10, ("handle_incoming_pdu: Aborting with %s\n",
301 for (req
= cli
->outstanding_requests
; req
; req
= req
->next
) {
302 async_req_error(req
->async
, status
);
308 * fd event callback. This is the basic connection to the socket
311 static void cli_state_handler(struct event_context
*event_ctx
,
312 struct fd_event
*event
, uint16 flags
, void *p
)
314 struct cli_state
*cli
= (struct cli_state
*)p
;
315 struct cli_request
*req
;
317 DEBUG(11, ("cli_state_handler called with flags %d\n", flags
));
319 if (flags
& EVENT_FD_READ
) {
321 size_t old_size
, new_size
;
324 res
= ioctl(cli
->fd
, FIONREAD
, &available
);
326 DEBUG(10, ("ioctl(FIONREAD) failed: %s\n",
331 if (available
== 0) {
336 old_size
= talloc_get_size(cli
->evt_inbuf
);
337 new_size
= old_size
+ available
;
339 if (new_size
< old_size
) {
344 tmp
= TALLOC_REALLOC_ARRAY(cli
, cli
->evt_inbuf
, char,
350 cli
->evt_inbuf
= tmp
;
352 res
= recv(cli
->fd
, cli
->evt_inbuf
+ old_size
, available
, 0);
354 DEBUG(10, ("recv failed: %s\n", strerror(errno
)));
358 DEBUG(11, ("cli_state_handler: received %d bytes, "
359 "smb_len(evt_inbuf) = %d\n", (int)res
,
360 smb_len(cli
->evt_inbuf
)));
362 /* recv *might* have returned less than announced */
363 new_size
= old_size
+ res
;
365 /* shrink, so I don't expect errors here */
366 cli
->evt_inbuf
= TALLOC_REALLOC_ARRAY(cli
, cli
->evt_inbuf
,
369 while ((cli
->evt_inbuf
!= NULL
)
370 && ((smb_len(cli
->evt_inbuf
) + 4) <= new_size
)) {
372 * we've got a complete NBT level PDU in evt_inbuf
374 handle_incoming_pdu(cli
);
375 new_size
= talloc_get_size(cli
->evt_inbuf
);
379 if (flags
& EVENT_FD_WRITE
) {
383 for (req
= cli
->outstanding_requests
; req
; req
= req
->next
) {
384 to_send
= smb_len(req
->outbuf
)+4;
385 if (to_send
> req
->sent
) {
391 event_fd_set_not_writeable(event
);
395 sent
= send(cli
->fd
, req
->outbuf
+ req
->sent
,
396 to_send
- req
->sent
, 0);
404 if (req
->sent
== to_send
) {
411 for (req
= cli
->outstanding_requests
; req
; req
= req
->next
) {
412 req
->async
->state
= ASYNC_REQ_ERROR
;
413 req
->async
->status
= map_nt_error_from_unix(errno
);
415 TALLOC_FREE(cli
->fd_event
);
421 * Holder for a talloc_destructor, we need to zero out the pointers in cli
424 struct cli_tmp_event
{
425 struct cli_state
*cli
;
428 static int cli_tmp_event_destructor(struct cli_tmp_event
*e
)
430 TALLOC_FREE(e
->cli
->fd_event
);
431 TALLOC_FREE(e
->cli
->event_ctx
);
436 * Create a temporary event context for use in the sync helper functions
439 struct cli_tmp_event
*cli_tmp_event_ctx(TALLOC_CTX
*mem_ctx
,
440 struct cli_state
*cli
)
442 struct cli_tmp_event
*state
;
444 if (cli
->event_ctx
!= NULL
) {
448 state
= talloc(mem_ctx
, struct cli_tmp_event
);
453 talloc_set_destructor(state
, cli_tmp_event_destructor
);
455 cli
->event_ctx
= event_context_init(state
);
456 if (cli
->event_ctx
== NULL
) {
461 cli
->fd_event
= event_add_fd(cli
->event_ctx
, state
, cli
->fd
,
462 EVENT_FD_READ
, cli_state_handler
, cli
);
463 if (cli
->fd_event
== NULL
) {
471 * Attach an event context permanently to a cli_struct
474 NTSTATUS
cli_add_event_ctx(struct cli_state
*cli
,
475 struct event_context
*event_ctx
)
477 cli
->event_ctx
= event_ctx
;
478 cli
->fd_event
= event_add_fd(event_ctx
, cli
, cli
->fd
, EVENT_FD_READ
,
479 cli_state_handler
, cli
);
480 if (cli
->fd_event
== NULL
) {
481 return NT_STATUS_NO_MEMORY
;