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/>.
22 static void cli_state_handler(struct event_context
*event_ctx
,
23 struct fd_event
*event
, uint16 flags
, void *p
);
26 * Fetch an error out of a NBT packet
27 * @param[in] buf The SMB packet
28 * @retval The error, converted to NTSTATUS
31 NTSTATUS
cli_pull_error(char *buf
)
33 uint32_t flags2
= SVAL(buf
, smb_flg2
);
35 if (flags2
& FLAGS2_32_BIT_ERROR_CODES
) {
36 return NT_STATUS(IVAL(buf
, smb_rcls
));
39 /* if the client uses dos errors, but there is no error,
40 we should return no error here, otherwise it looks
41 like an unknown bad NT_STATUS. jmcd */
42 if (CVAL(buf
, smb_rcls
) == 0)
45 return NT_STATUS_DOS(CVAL(buf
, smb_rcls
), SVAL(buf
,smb_err
));
49 * Compatibility helper for the sync APIs: Fake NTSTATUS in cli->inbuf
50 * @param[in] cli The client connection that just received an error
51 * @param[in] status The error to set on "cli"
54 void cli_set_error(struct cli_state
*cli
, NTSTATUS status
)
56 uint32_t flags2
= SVAL(cli
->inbuf
, smb_flg2
);
58 if (NT_STATUS_IS_DOS(status
)) {
59 SSVAL(cli
->inbuf
, smb_flg2
,
60 flags2
& ~FLAGS2_32_BIT_ERROR_CODES
);
61 SCVAL(cli
->inbuf
, smb_rcls
, NT_STATUS_DOS_CLASS(status
));
62 SSVAL(cli
->inbuf
, smb_err
, NT_STATUS_DOS_CODE(status
));
66 SSVAL(cli
->inbuf
, smb_flg2
, flags2
| FLAGS2_32_BIT_ERROR_CODES
);
67 SIVAL(cli
->inbuf
, smb_rcls
, NT_STATUS_V(status
));
73 * @param[in] cli The client connection
74 * @retval The new, unused mid
77 static uint16_t cli_new_mid(struct cli_state
*cli
)
80 struct cli_request
*req
;
88 for (req
= cli
->outstanding_requests
; req
; req
= req
->next
) {
89 if (result
== req
->mid
) {
101 * Print an async req that happens to be a cli_request
102 * @param[in] mem_ctx The TALLOC_CTX to put the result on
103 * @param[in] req The request to print
104 * @retval The string representation of "req"
107 static char *cli_request_print(TALLOC_CTX
*mem_ctx
, struct async_req
*req
)
109 char *result
= async_req_print(mem_ctx
, req
);
110 struct cli_request
*cli_req
= talloc_get_type_abort(
111 req
->private_data
, struct cli_request
);
113 if (result
== NULL
) {
117 return talloc_asprintf_append_buffer(
118 result
, "mid=%d\n", cli_req
->mid
);
122 * Destroy a cli_request
123 * @param[in] req The cli_request to kill
127 static int cli_request_destructor(struct cli_request
*req
)
129 if (req
->enc_state
!= NULL
) {
130 common_free_enc_buffer(req
->enc_state
, req
->outbuf
);
132 DLIST_REMOVE(req
->cli
->outstanding_requests
, req
);
133 if (req
->cli
->outstanding_requests
== NULL
) {
134 TALLOC_FREE(req
->cli
->fd_event
);
140 * Are there already requests waiting in the chain_accumulator?
141 * @param[in] cli The cli_state we want to check
145 bool cli_in_chain(struct cli_state
*cli
)
147 if (cli
->chain_accumulator
== NULL
) {
151 return (cli
->chain_accumulator
->num_async
!= 0);
155 * Is the SMB command able to hold an AND_X successor
156 * @param[in] cmd The SMB command in question
157 * @retval Can we add a chained request after "cmd"?
160 static bool is_andx_req(uint8_t cmd
)
181 * @brief Find the smb_cmd offset of the last command pushed
182 * @param[in] buf The buffer we're building up
183 * @retval Where can we put our next andx cmd?
185 * While chaining requests, the "next" request we're looking at needs to put
186 * its SMB_Command before the data the previous request already built up added
187 * to the chain. Find the offset to the place where we have to put our cmd.
190 static bool find_andx_cmd_ofs(char *buf
, size_t *pofs
)
195 cmd
= CVAL(buf
, smb_com
);
197 SMB_ASSERT(is_andx_req(cmd
));
201 while (CVAL(buf
, ofs
) != 0xff) {
203 if (!is_andx_req(CVAL(buf
, ofs
))) {
208 * ofs is from start of smb header, so add the 4 length
209 * bytes. The next cmd is right after the wct field.
211 ofs
= SVAL(buf
, ofs
+2) + 4 + 1;
213 SMB_ASSERT(ofs
+4 < talloc_get_size(buf
));
221 * @brief Destroy an async_req that is the visible part of a cli_request
222 * @param[in] req The request to kill
223 * @retval Return 0 to make talloc happy
225 * This destructor is a bit tricky: Because a cli_request can host more than
226 * one async_req for chained requests, we need to make sure that the
227 * "cli_request" that we were part of is correctly destroyed at the right
228 * time. This is done by NULLing out ourself from the "async" member of our
229 * "cli_request". If there is none left, then also TALLOC_FREE() the
230 * cli_request, which was a talloc child of the client connection cli_state.
233 static int cli_async_req_destructor(struct async_req
*req
)
235 struct cli_request
*cli_req
= talloc_get_type_abort(
236 req
->private_data
, struct cli_request
);
242 for (i
=0; i
<cli_req
->num_async
; i
++) {
243 if (cli_req
->async
[i
] == req
) {
244 cli_req
->async
[i
] = NULL
;
247 if (cli_req
->async
[i
] != NULL
) {
255 TALLOC_FREE(cli_req
);
262 * @brief Chain up a request
263 * @param[in] mem_ctx The TALLOC_CTX for the result
264 * @param[in] ev The event context that will call us back
265 * @param[in] cli The cli_state we queue the request up for
266 * @param[in] smb_command The command that we want to issue
267 * @param[in] additional_flags open_and_x wants to add oplock header flags
268 * @param[in] wct How many words?
269 * @param[in] vwv The words, already in network order
270 * @param[in] num_bytes How many bytes?
271 * @param[in] bytes The data the request ships
273 * cli_request_chain() is the core of the SMB request marshalling routine. It
274 * will create a new async_req structure in the cli->chain_accumulator->async
275 * array and marshall the smb_cmd, the vwv array and the bytes into
276 * cli->chain_accumulator->outbuf.
279 static struct async_req
*cli_request_chain(TALLOC_CTX
*mem_ctx
,
280 struct event_context
*ev
,
281 struct cli_state
*cli
,
283 uint8_t additional_flags
,
284 uint8_t wct
, const uint16_t *vwv
,
286 const uint8_t *bytes
)
288 struct async_req
**tmp_reqs
;
290 struct cli_request
*req
;
291 size_t old_size
, new_size
;
294 req
= cli
->chain_accumulator
;
296 tmp_reqs
= TALLOC_REALLOC_ARRAY(req
, req
->async
, struct async_req
*,
298 if (tmp_reqs
== NULL
) {
299 DEBUG(0, ("talloc failed\n"));
302 req
->async
= tmp_reqs
;
305 req
->async
[req
->num_async
-1] = async_req_new(mem_ctx
, ev
);
306 if (req
->async
[req
->num_async
-1] == NULL
) {
307 DEBUG(0, ("async_req_new failed\n"));
311 req
->async
[req
->num_async
-1]->private_data
= req
;
312 req
->async
[req
->num_async
-1]->print
= cli_request_print
;
313 talloc_set_destructor(req
->async
[req
->num_async
-1],
314 cli_async_req_destructor
);
316 old_size
= talloc_get_size(req
->outbuf
);
319 * We need space for the wct field, the words, the byte count field
320 * and the bytes themselves.
322 new_size
= old_size
+ 1 + wct
* sizeof(uint16_t) + 2 + num_bytes
;
324 if (new_size
> 0xffff) {
325 DEBUG(1, ("cli_request_chain: %u bytes won't fit\n",
326 (unsigned)new_size
));
330 tmp_buf
= TALLOC_REALLOC_ARRAY(NULL
, req
->outbuf
, char, new_size
);
331 if (tmp_buf
== NULL
) {
332 DEBUG(0, ("talloc failed\n"));
335 req
->outbuf
= tmp_buf
;
337 if (old_size
== smb_wct
) {
338 SCVAL(req
->outbuf
, smb_com
, smb_command
);
341 if (!find_andx_cmd_ofs(req
->outbuf
, &andx_cmd_ofs
)) {
342 DEBUG(1, ("invalid command chain\n"));
345 SCVAL(req
->outbuf
, andx_cmd_ofs
, smb_command
);
346 SSVAL(req
->outbuf
, andx_cmd_ofs
+ 2, old_size
- 4);
351 SCVAL(req
->outbuf
, ofs
, wct
);
354 memcpy(req
->outbuf
+ ofs
, vwv
, sizeof(uint16_t) * wct
);
355 ofs
+= sizeof(uint16_t) * wct
;
357 SSVAL(req
->outbuf
, ofs
, num_bytes
);
358 ofs
+= sizeof(uint16_t);
360 memcpy(req
->outbuf
+ ofs
, bytes
, num_bytes
);
362 return req
->async
[req
->num_async
-1];
365 TALLOC_FREE(req
->async
[req
->num_async
-1]);
371 * @brief prepare a cli_state to accept a chain of requests
372 * @param[in] cli The cli_state we want to queue up in
373 * @param[in] ev The event_context that will call us back for the socket
374 * @param[in] size_hint How many bytes are expected, just an optimization
375 * @retval Did we have enough memory?
377 * cli_chain_cork() sets up a new cli_request in cli->chain_accumulator. If
378 * cli is used in an async fashion, i.e. if we have outstanding requests, then
379 * we do not have to create a fd event. If cli is used only with the sync
380 * helpers, we need to create the fd_event here.
382 * If you want to issue a chained request to the server, do a
383 * cli_chain_cork(), then do you cli_open_send(), cli_read_and_x_send(),
384 * cli_close_send() and so on. The async requests that come out of
385 * cli_xxx_send() are normal async requests with the difference that they
386 * won't be shipped individually. But the event_context will still trigger the
387 * req->async.fn to be called on every single request.
389 * You have to take care yourself that you only issue chainable requests in
390 * the middle of the chain.
393 bool cli_chain_cork(struct cli_state
*cli
, struct event_context
*ev
,
396 struct cli_request
*req
= NULL
;
398 SMB_ASSERT(cli
->chain_accumulator
== NULL
);
401 DEBUG(10, ("cli->fd closed\n"));
405 if (cli
->fd_event
== NULL
) {
406 SMB_ASSERT(cli
->outstanding_requests
== NULL
);
407 cli
->fd_event
= event_add_fd(ev
, cli
, cli
->fd
,
409 cli_state_handler
, cli
);
410 if (cli
->fd_event
== NULL
) {
415 req
= talloc(cli
, struct cli_request
);
421 if (size_hint
== 0) {
424 req
->outbuf
= talloc_array(req
, char, smb_wct
+ size_hint
);
425 if (req
->outbuf
== NULL
) {
428 req
->outbuf
= TALLOC_REALLOC_ARRAY(NULL
, req
->outbuf
, char, smb_wct
);
433 req
->enc_state
= NULL
;
434 req
->recv_helper
.fn
= NULL
;
436 SSVAL(req
->outbuf
, smb_tid
, cli
->cnum
);
437 cli_setup_packet_buf(cli
, req
->outbuf
);
439 req
->mid
= cli_new_mid(cli
);
440 SSVAL(req
->outbuf
, smb_mid
, req
->mid
);
442 cli
->chain_accumulator
= req
;
444 DEBUG(10, ("cli_chain_cork: mid=%d\n", req
->mid
));
449 if (cli
->outstanding_requests
== NULL
) {
450 TALLOC_FREE(cli
->fd_event
);
456 * Ship a request queued up via cli_request_chain()
457 * @param[in] cl The connection
460 void cli_chain_uncork(struct cli_state
*cli
)
462 struct cli_request
*req
= cli
->chain_accumulator
;
464 SMB_ASSERT(req
!= NULL
);
466 DLIST_ADD_END(cli
->outstanding_requests
, req
, struct cli_request
*);
467 talloc_set_destructor(req
, cli_request_destructor
);
469 cli
->chain_accumulator
= NULL
;
471 smb_setlen(req
->outbuf
, talloc_get_size(req
->outbuf
) - 4);
473 cli_calculate_sign_mac(cli
, req
->outbuf
);
475 if (cli_encryption_on(cli
)) {
479 status
= cli_encrypt_message(cli
, req
->outbuf
, &enc_buf
);
480 if (!NT_STATUS_IS_OK(status
)) {
481 DEBUG(0, ("Error in encrypting client message. "
482 "Error %s\n", nt_errstr(status
)));
486 req
->outbuf
= enc_buf
;
487 req
->enc_state
= cli
->trans_enc_state
;
492 event_fd_set_writeable(cli
->fd_event
);
496 * @brief Send a request to the server
497 * @param[in] mem_ctx The TALLOC_CTX for the result
498 * @param[in] ev The event context that will call us back
499 * @param[in] cli The cli_state we queue the request up for
500 * @param[in] smb_command The command that we want to issue
501 * @param[in] additional_flags open_and_x wants to add oplock header flags
502 * @param[in] wct How many words?
503 * @param[in] vwv The words, already in network order
504 * @param[in] num_bytes How many bytes?
505 * @param[in] bytes The data the request ships
507 * This is the generic routine to be used by the cli_xxx_send routines.
510 struct async_req
*cli_request_send(TALLOC_CTX
*mem_ctx
,
511 struct event_context
*ev
,
512 struct cli_state
*cli
,
514 uint8_t additional_flags
,
515 uint8_t wct
, const uint16_t *vwv
,
516 uint16_t num_bytes
, const uint8_t *bytes
)
518 struct async_req
*result
;
521 if (cli
->chain_accumulator
== NULL
) {
522 if (!cli_chain_cork(cli
, ev
,
523 wct
* sizeof(uint16_t) + num_bytes
+ 3)) {
524 DEBUG(1, ("cli_chain_cork failed\n"));
530 result
= cli_request_chain(mem_ctx
, ev
, cli
, smb_command
,
531 additional_flags
, wct
, vwv
,
534 if (result
== NULL
) {
535 DEBUG(1, ("cli_request_chain failed\n"));
539 cli_chain_uncork(cli
);
546 * Figure out if there is an andx command behind the current one
547 * @param[in] buf The smb buffer to look at
548 * @param[in] ofs The offset to the wct field that is followed by the cmd
549 * @retval Is there a command following?
552 static bool have_andx_command(const char *buf
, uint16_t ofs
)
555 size_t buflen
= talloc_get_size(buf
);
557 if ((ofs
== buflen
-1) || (ofs
== buflen
)) {
561 wct
= CVAL(buf
, ofs
);
564 * Not enough space for the command and a following pointer
568 return (CVAL(buf
, ofs
+1) != 0xff);
572 * @brief Pull reply data out of a request
573 * @param[in] req The request that we just received a reply for
574 * @param[out] pwct How many words did the server send?
575 * @param[out] pvwv The words themselves
576 * @param[out] pnum_bytes How many bytes did the server send?
577 * @param[out] pbytes The bytes themselves
578 * @retval Was the reply formally correct?
581 NTSTATUS
cli_pull_reply(struct async_req
*req
,
582 uint8_t *pwct
, uint16_t **pvwv
,
583 uint16_t *pnum_bytes
, uint8_t **pbytes
)
585 struct cli_request
*cli_req
= talloc_get_type_abort(
586 req
->private_data
, struct cli_request
);
589 size_t wct_ofs
, bytes_offset
;
593 for (i
= 0; i
< cli_req
->num_async
; i
++) {
594 if (req
== cli_req
->async
[i
]) {
599 if (i
== cli_req
->num_async
) {
600 cli_set_error(cli_req
->cli
, NT_STATUS_INVALID_PARAMETER
);
601 return NT_STATUS_INVALID_PARAMETER
;
605 * The status we pull here is only relevant for the last reply in the
609 status
= cli_pull_error(cli_req
->inbuf
);
612 if (NT_STATUS_IS_ERR(status
)
613 && !have_andx_command(cli_req
->inbuf
, smb_wct
)) {
614 cli_set_error(cli_req
->cli
, status
);
621 cmd
= CVAL(cli_req
->inbuf
, smb_com
);
624 for (j
= 0; j
< i
; j
++) {
627 return NT_STATUS_REQUEST_ABORTED
;
629 if (!is_andx_req(cmd
)) {
630 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
634 if (!have_andx_command(cli_req
->inbuf
, wct_ofs
)) {
636 * This request was not completed because a previous
637 * request in the chain had received an error.
639 return NT_STATUS_REQUEST_ABORTED
;
642 wct_ofs
= SVAL(cli_req
->inbuf
, wct_ofs
+ 3);
645 * Skip the all-present length field. No overflow, we've just
646 * put a 16-bit value into a size_t.
650 if (wct_ofs
+2 > talloc_get_size(cli_req
->inbuf
)) {
651 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
654 cmd
= CVAL(cli_req
->inbuf
, wct_ofs
+ 1);
657 if (!have_andx_command(cli_req
->inbuf
, wct_ofs
)
658 && NT_STATUS_IS_ERR(status
)) {
660 * The last command takes the error code. All further commands
661 * down the requested chain will get a
662 * NT_STATUS_REQUEST_ABORTED.
668 wct
= CVAL(cli_req
->inbuf
, wct_ofs
);
670 bytes_offset
= wct_ofs
+ 1 + wct
* sizeof(uint16_t);
671 num_bytes
= SVAL(cli_req
->inbuf
, bytes_offset
);
674 * wct_ofs is a 16-bit value plus 4, wct is a 8-bit value, num_bytes
675 * is a 16-bit value. So bytes_offset being size_t should be far from
679 if ((bytes_offset
+ 2 > talloc_get_size(cli_req
->inbuf
))
680 || (bytes_offset
> 0xffff)) {
681 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
685 *pvwv
= (uint16_t *)(cli_req
->inbuf
+ wct_ofs
+ 1);
686 *pnum_bytes
= num_bytes
;
687 *pbytes
= (uint8_t *)cli_req
->inbuf
+ bytes_offset
+ 2;
693 * A PDU has arrived on cli->evt_inbuf
694 * @param[in] cli The cli_state that received something
697 static void handle_incoming_pdu(struct cli_state
*cli
)
699 struct cli_request
*req
;
701 size_t raw_pdu_len
, buf_len
, pdu_len
, rest_len
;
709 * The encrypted PDU len might differ from the unencrypted one
711 raw_pdu_len
= smb_len(cli
->evt_inbuf
) + 4;
712 buf_len
= talloc_get_size(cli
->evt_inbuf
);
713 rest_len
= buf_len
- raw_pdu_len
;
715 if (buf_len
== raw_pdu_len
) {
717 * Optimal case: Exactly one PDU was in the socket buffer
719 pdu
= cli
->evt_inbuf
;
720 cli
->evt_inbuf
= NULL
;
723 DEBUG(11, ("buf_len = %d, raw_pdu_len = %d, splitting "
724 "buffer\n", (int)buf_len
, (int)raw_pdu_len
));
726 if (raw_pdu_len
< rest_len
) {
728 * The PDU is shorter, talloc_memdup that one.
730 pdu
= (char *)talloc_memdup(
731 cli
, cli
->evt_inbuf
, raw_pdu_len
);
733 memmove(cli
->evt_inbuf
, cli
->evt_inbuf
+ raw_pdu_len
,
734 buf_len
- raw_pdu_len
);
736 cli
->evt_inbuf
= TALLOC_REALLOC_ARRAY(
737 NULL
, cli
->evt_inbuf
, char, rest_len
);
740 status
= NT_STATUS_NO_MEMORY
;
741 goto invalidate_requests
;
746 * The PDU is larger than the rest, talloc_memdup the
749 pdu
= cli
->evt_inbuf
;
751 cli
->evt_inbuf
= (char *)talloc_memdup(
752 cli
, pdu
+ raw_pdu_len
, rest_len
);
754 if (cli
->evt_inbuf
== NULL
) {
755 status
= NT_STATUS_NO_MEMORY
;
756 goto invalidate_requests
;
763 * TODO: Handle oplock break requests
766 if (cli_encryption_on(cli
) && CVAL(pdu
, 0) == 0) {
767 uint16_t enc_ctx_num
;
769 status
= get_enc_ctx_num((uint8_t *)pdu
, &enc_ctx_num
);
770 if (!NT_STATUS_IS_OK(status
)) {
771 DEBUG(10, ("get_enc_ctx_num returned %s\n",
773 goto invalidate_requests
;
776 if (enc_ctx_num
!= cli
->trans_enc_state
->enc_ctx_num
) {
777 DEBUG(10, ("wrong enc_ctx %d, expected %d\n",
779 cli
->trans_enc_state
->enc_ctx_num
));
780 status
= NT_STATUS_INVALID_HANDLE
;
781 goto invalidate_requests
;
784 status
= common_decrypt_buffer(cli
->trans_enc_state
,
786 if (!NT_STATUS_IS_OK(status
)) {
787 DEBUG(10, ("common_decrypt_buffer returned %s\n",
789 goto invalidate_requests
;
793 if (!cli_check_sign_mac(cli
, pdu
)) {
794 DEBUG(10, ("cli_check_sign_mac failed\n"));
795 status
= NT_STATUS_ACCESS_DENIED
;
796 goto invalidate_requests
;
799 mid
= SVAL(pdu
, smb_mid
);
801 DEBUG(10, ("handle_incoming_pdu: got mid %d\n", mid
));
803 for (req
= cli
->outstanding_requests
; req
; req
= req
->next
) {
804 if (req
->mid
== mid
) {
809 pdu_len
= smb_len(pdu
) + 4;
812 DEBUG(3, ("Request for mid %d not found, dumping PDU\n", mid
));
818 req
->inbuf
= talloc_move(req
, &pdu
);
821 * Freeing the last async_req will free the req (see
822 * cli_async_req_destructor). So make a copy of req->num_async, we
823 * can't reference it in the last round.
826 num_async
= req
->num_async
;
828 for (i
=0; i
<num_async
; i
++) {
830 * A request might have been talloc_free()'ed before we arrive
831 * here. It will have removed itself from req->async via its
832 * destructor cli_async_req_destructor().
834 if (req
->async
[i
] != NULL
) {
835 if (req
->recv_helper
.fn
!= NULL
) {
836 req
->recv_helper
.fn(req
->async
[i
]);
838 async_req_done(req
->async
[i
]);
846 DEBUG(10, ("handle_incoming_pdu: Aborting with %s\n",
849 for (req
= cli
->outstanding_requests
; req
; req
= req
->next
) {
850 async_req_error(req
->async
[0], status
);
856 * fd event callback. This is the basic connection to the socket
857 * @param[in] event_ctx The event context that called us
858 * @param[in] event The event that fired
859 * @param[in] flags EVENT_FD_READ | EVENT_FD_WRITE
860 * @param[in] p private_data, in this case the cli_state
863 static void cli_state_handler(struct event_context
*event_ctx
,
864 struct fd_event
*event
, uint16 flags
, void *p
)
866 struct cli_state
*cli
= (struct cli_state
*)p
;
867 struct cli_request
*req
;
870 DEBUG(11, ("cli_state_handler called with flags %d\n", flags
));
872 if (flags
& EVENT_FD_READ
) {
874 size_t old_size
, new_size
;
877 res
= ioctl(cli
->fd
, FIONREAD
, &available
);
879 DEBUG(10, ("ioctl(FIONREAD) failed: %s\n",
881 status
= map_nt_error_from_unix(errno
);
885 if (available
== 0) {
887 status
= NT_STATUS_END_OF_FILE
;
891 old_size
= talloc_get_size(cli
->evt_inbuf
);
892 new_size
= old_size
+ available
;
894 if (new_size
< old_size
) {
896 status
= NT_STATUS_UNEXPECTED_IO_ERROR
;
900 tmp
= TALLOC_REALLOC_ARRAY(cli
, cli
->evt_inbuf
, char,
904 status
= NT_STATUS_NO_MEMORY
;
907 cli
->evt_inbuf
= tmp
;
909 res
= recv(cli
->fd
, cli
->evt_inbuf
+ old_size
, available
, 0);
911 DEBUG(10, ("recv failed: %s\n", strerror(errno
)));
912 status
= map_nt_error_from_unix(errno
);
916 DEBUG(11, ("cli_state_handler: received %d bytes, "
917 "smb_len(evt_inbuf) = %d\n", (int)res
,
918 smb_len(cli
->evt_inbuf
)));
920 /* recv *might* have returned less than announced */
921 new_size
= old_size
+ res
;
923 /* shrink, so I don't expect errors here */
924 cli
->evt_inbuf
= TALLOC_REALLOC_ARRAY(cli
, cli
->evt_inbuf
,
927 while ((cli
->evt_inbuf
!= NULL
)
928 && ((smb_len(cli
->evt_inbuf
) + 4) <= new_size
)) {
930 * we've got a complete NBT level PDU in evt_inbuf
932 handle_incoming_pdu(cli
);
933 new_size
= talloc_get_size(cli
->evt_inbuf
);
937 if (flags
& EVENT_FD_WRITE
) {
941 for (req
= cli
->outstanding_requests
; req
; req
= req
->next
) {
942 to_send
= smb_len(req
->outbuf
)+4;
943 if (to_send
> req
->sent
) {
949 if (cli
->fd_event
!= NULL
) {
950 event_fd_set_not_writeable(cli
->fd_event
);
955 sent
= send(cli
->fd
, req
->outbuf
+ req
->sent
,
956 to_send
- req
->sent
, 0);
959 status
= map_nt_error_from_unix(errno
);
965 if (req
->sent
== to_send
) {
972 for (req
= cli
->outstanding_requests
; req
; req
= req
->next
) {
974 for (i
=0; i
<req
->num_async
; i
++) {
975 async_req_error(req
->async
[i
], status
);
978 TALLOC_FREE(cli
->fd_event
);