X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source3%2Flibsmb%2Fasync_smb.c;h=8db24d202bb4ef1921c85d76b0f853c46ee15813;hb=6e62dcca878a3e2ead6c85b2ccc57c9a19ae818e;hp=52bfc7500926b654355b749485f8019c9bd3fcd8;hpb=fa5f11279bda32dcb9a85c74b2b4a967a8e52104;p=obnox%2Fsamba-ctdb.git diff --git a/source3/libsmb/async_smb.c b/source3/libsmb/async_smb.c index 52bfc75009..8db24d202b 100644 --- a/source3/libsmb/async_smb.c +++ b/source3/libsmb/async_smb.c @@ -22,6 +22,93 @@ static void cli_state_handler(struct event_context *event_ctx, struct fd_event *event, uint16 flags, void *p); +/* + * Read an smb packet asynchronously, discard keepalives + */ + +struct read_smb_state { + struct tevent_context *ev; + int fd; + uint8_t *buf; +}; + +static ssize_t read_smb_more(uint8_t *buf, size_t buflen, void *private_data); +static void read_smb_done(struct tevent_req *subreq); + +static struct tevent_req *read_smb_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + int fd) +{ + struct tevent_req *result, *subreq; + struct read_smb_state *state; + + result = tevent_req_create(mem_ctx, &state, struct read_smb_state); + if (result == NULL) { + return NULL; + } + state->ev = ev; + state->fd = fd; + + subreq = read_packet_send(state, ev, fd, 4, read_smb_more, NULL); + if (subreq == NULL) { + goto fail; + } + tevent_req_set_callback(subreq, read_smb_done, result); + return result; + fail: + TALLOC_FREE(result); + return NULL; +} + +static ssize_t read_smb_more(uint8_t *buf, size_t buflen, void *private_data) +{ + if (buflen > 4) { + return 0; /* We've been here, we're done */ + } + return smb_len_large(buf); +} + +static void read_smb_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct read_smb_state *state = tevent_req_data( + req, struct read_smb_state); + ssize_t len; + int err; + + len = read_packet_recv(subreq, state, &state->buf, &err); + TALLOC_FREE(subreq); + if (len == -1) { + tevent_req_error(req, err); + return; + } + + if (CVAL(state->buf, 0) == SMBkeepalive) { + subreq = read_packet_send(state, state->ev, state->fd, 4, + read_smb_more, NULL); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, read_smb_done, req); + return; + } + tevent_req_done(req); +} + +static ssize_t read_smb_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + uint8_t **pbuf, int *perrno) +{ + struct read_smb_state *state = tevent_req_data( + req, struct read_smb_state); + + if (tevent_req_is_unix_error(req, perrno)) { + return -1; + } + *pbuf = talloc_move(mem_ctx, &state->buf); + return talloc_get_size(*pbuf); +} + /** * Fetch an error out of a NBT packet * @param[in] buf The SMB packet @@ -151,32 +238,6 @@ bool cli_in_chain(struct cli_state *cli) return (cli->chain_accumulator->num_async != 0); } -/** - * Is the SMB command able to hold an AND_X successor - * @param[in] cmd The SMB command in question - * @retval Can we add a chained request after "cmd"? - */ - -static bool is_andx_req(uint8_t cmd) -{ - switch (cmd) { - case SMBtconX: - case SMBlockingX: - case SMBopenX: - case SMBreadX: - case SMBwriteX: - case SMBsesssetupX: - case SMBulogoffX: - case SMBntcreateX: - return true; - break; - default: - break; - } - - return false; -} - /** * @brief Find the smb_cmd offset of the last command pushed * @param[in] buf The buffer we're building up @@ -435,7 +496,7 @@ static struct async_req *cli_request_chain(TALLOC_CTX *mem_ctx, req->async = tmp_reqs; req->num_async += 1; - req->async[req->num_async-1] = async_req_new(mem_ctx, ev); + req->async[req->num_async-1] = async_req_new(mem_ctx); if (req->async[req->num_async-1] == NULL) { DEBUG(0, ("async_req_new failed\n")); req->num_async -= 1; @@ -552,6 +613,7 @@ bool cli_chain_cork(struct cli_state *cli, struct event_context *ev, void cli_chain_uncork(struct cli_state *cli) { struct cli_request *req = cli->chain_accumulator; + size_t smblen; SMB_ASSERT(req != NULL); @@ -561,7 +623,19 @@ void cli_chain_uncork(struct cli_state *cli) cli->chain_accumulator = NULL; SSVAL(req->outbuf, smb_mid, req->mid); - smb_setlen((char *)req->outbuf, talloc_get_size(req->outbuf) - 4); + + smblen = talloc_get_size(req->outbuf) - 4; + + smb_setlen((char *)req->outbuf, smblen); + + if (smblen > 0x1ffff) { + /* + * This is a POSIX 14 word large write. Overwrite just the + * size field, the '0xFFSMB' has been set by smb_setlen which + * _smb_setlen_large does not do. + */ + _smb_setlen_large(((char *)req->outbuf), smblen); + } cli_calculate_sign_mac(cli, (char *)req->outbuf); @@ -613,6 +687,7 @@ struct async_req *cli_request_send(TALLOC_CTX *mem_ctx, { struct async_req *result; bool uncork = false; + struct timeval endtime; if (cli->chain_accumulator == NULL) { if (!cli_chain_cork(cli, ev, @@ -631,6 +706,14 @@ struct async_req *cli_request_send(TALLOC_CTX *mem_ctx, DEBUG(1, ("cli_request_chain failed\n")); } + endtime = timeval_current_ofs(0, cli->timeout * 1000); + + if (!async_req_set_endtime(result, ev, endtime)) { + DEBUG(1, ("async_req_set_endtime failed\n")); + TALLOC_FREE(result); + return NULL; + } + if (uncork) { cli_chain_uncork(cli); } @@ -861,6 +944,8 @@ static NTSTATUS validate_smb_crypto(struct cli_state *cli, char *pdu) if (!cli_check_sign_mac(cli, pdu)) { DEBUG(10, ("cli_check_sign_mac failed\n")); + close(cli->fd); + cli->fd = -1; return NT_STATUS_ACCESS_DENIED; } @@ -874,7 +959,7 @@ static NTSTATUS validate_smb_crypto(struct cli_state *cli, char *pdu) static void handle_incoming_pdu(struct cli_state *cli) { - struct cli_request *req; + struct cli_request *req, *next; uint16_t mid; size_t raw_pdu_len, buf_len, pdu_len, rest_len; char *pdu; @@ -936,6 +1021,12 @@ static void handle_incoming_pdu(struct cli_state *cli) } } + if ((raw_pdu_len == 4) && (CVAL(pdu, 0) == SMBkeepalive)) { + DEBUG(10, ("Got keepalive\n")); + TALLOC_FREE(pdu); + return; + } + status = validate_smb_crypto(cli, pdu); if (!NT_STATUS_IS_OK(status)) { goto invalidate_requests; @@ -991,8 +1082,11 @@ static void handle_incoming_pdu(struct cli_state *cli) DEBUG(10, ("handle_incoming_pdu: Aborting with %s\n", nt_errstr(status))); - for (req = cli->outstanding_requests; req; req = req->next) { - async_req_error(req->async[0], status); + for (req = cli->outstanding_requests; req; req = next) { + next = req->next; + if (req->num_async) { + async_req_nterror(req->async[0], status); + } } return; } @@ -1009,11 +1103,16 @@ static void cli_state_handler(struct event_context *event_ctx, struct fd_event *event, uint16 flags, void *p) { struct cli_state *cli = (struct cli_state *)p; - struct cli_request *req; + struct cli_request *req, *next; NTSTATUS status; DEBUG(11, ("cli_state_handler called with flags %d\n", flags)); + if (cli->fd == -1) { + status = NT_STATUS_CONNECTION_INVALID; + goto sock_error; + } + if (flags & EVENT_FD_WRITE) { size_t to_send; ssize_t sent; @@ -1115,13 +1214,20 @@ static void cli_state_handler(struct event_context *event_ctx, return; sock_error: - for (req = cli->outstanding_requests; req; req = req->next) { - int i; - for (i=0; inum_async; i++) { - async_req_error(req->async[i], status); + + for (req = cli->outstanding_requests; req; req = next) { + int i, num_async; + + next = req->next; + num_async = req->num_async; + + for (i=0; iasync[i], status); } } TALLOC_FREE(cli->fd_event); - close(cli->fd); - cli->fd = -1; + if (cli->fd != -1) { + close(cli->fd); + cli->fd = -1; + } }