diff options
Diffstat (limited to 'source3/libsmb')
-rw-r--r-- | source3/libsmb/async_smb.c | 1388 | ||||
-rw-r--r-- | source3/libsmb/cliconnect.c | 339 | ||||
-rw-r--r-- | source3/libsmb/clientgen.c | 157 | ||||
-rw-r--r-- | source3/libsmb/clifile.c | 342 | ||||
-rw-r--r-- | source3/libsmb/clikrb5.c | 23 | ||||
-rw-r--r-- | source3/libsmb/clioplock.c | 123 | ||||
-rw-r--r-- | source3/libsmb/clireadwrite.c | 611 | ||||
-rw-r--r-- | source3/libsmb/clitrans.c | 668 |
8 files changed, 1956 insertions, 1695 deletions
diff --git a/source3/libsmb/async_smb.c b/source3/libsmb/async_smb.c index 86fd5c8bef..6faf96c207 100644 --- a/source3/libsmb/async_smb.c +++ b/source3/libsmb/async_smb.c @@ -19,9 +19,6 @@ #include "includes.h" -static void cli_state_handler(struct event_context *event_ctx, - struct fd_event *event, uint16 flags, void *p); - /** * Fetch an error out of a NBT packet * @param[in] buf The SMB packet @@ -69,89 +66,6 @@ void cli_set_error(struct cli_state *cli, NTSTATUS status) } /** - * Allocate a new mid - * @param[in] cli The client connection - * @retval The new, unused mid - */ - -static uint16_t cli_new_mid(struct cli_state *cli) -{ - uint16_t result; - struct cli_request *req; - - while (true) { - result = cli->mid++; - if (result == 0) { - continue; - } - - for (req = cli->outstanding_requests; req; req = req->next) { - if (result == req->mid) { - break; - } - } - - if (req == NULL) { - return result; - } - } -} - -/** - * Print an async req that happens to be a cli_request - * @param[in] mem_ctx The TALLOC_CTX to put the result on - * @param[in] req The request to print - * @retval The string representation of "req" - */ - -static char *cli_request_print(TALLOC_CTX *mem_ctx, struct async_req *req) -{ - char *result = async_req_print(mem_ctx, req); - struct cli_request *cli_req = talloc_get_type_abort( - req->private_data, struct cli_request); - - if (result == NULL) { - return NULL; - } - - return talloc_asprintf_append_buffer( - result, "mid=%d\n", cli_req->mid); -} - -/** - * Destroy a cli_request - * @param[in] req The cli_request to kill - * @retval Can't fail - */ - -static int cli_request_destructor(struct cli_request *req) -{ - if (req->enc_state != NULL) { - common_free_enc_buffer(req->enc_state, (char *)req->outbuf); - } - DLIST_REMOVE(req->cli->outstanding_requests, req); - if (req->cli->outstanding_requests == NULL) { - TALLOC_FREE(req->cli->fd_event); - } - return 0; -} - -/** - * Are there already requests waiting in the chain_accumulator? - * @param[in] cli The cli_state we want to check - * @retval reply :-) - */ - -bool cli_in_chain(struct cli_state *cli) -{ - if (cli->chain_accumulator == NULL) { - return false; - } - - return (cli->chain_accumulator->num_async != 0); -} - -/** * @brief Find the smb_cmd offset of the last command pushed * @param[in] buf The buffer we're building up * @retval Where can we put our next andx cmd? @@ -326,417 +240,614 @@ bool smb_splice_chain(uint8_t **poutbuf, uint8_t smb_command, } /** - * @brief Destroy an async_req that is the visible part of a cli_request - * @param[in] req The request to kill - * @retval Return 0 to make talloc happy - * - * This destructor is a bit tricky: Because a cli_request can host more than - * one async_req for chained requests, we need to make sure that the - * "cli_request" that we were part of is correctly destroyed at the right - * time. This is done by NULLing out ourself from the "async" member of our - * "cli_request". If there is none left, then also TALLOC_FREE() the - * cli_request, which was a talloc child of the client connection cli_state. + * Figure out if there is an andx command behind the current one + * @param[in] buf The smb buffer to look at + * @param[in] ofs The offset to the wct field that is followed by the cmd + * @retval Is there a command following? */ -static int cli_async_req_destructor(struct async_req *req) +static bool have_andx_command(const char *buf, uint16_t ofs) { - struct cli_request *cli_req = talloc_get_type_abort( - req->private_data, struct cli_request); - int i, pending; - bool found = false; + uint8_t wct; + size_t buflen = talloc_get_size(buf); - pending = 0; + if ((ofs == buflen-1) || (ofs == buflen)) { + return false; + } - for (i=0; i<cli_req->num_async; i++) { - if (cli_req->async[i] == req) { - cli_req->async[i] = NULL; - found = true; - } - if (cli_req->async[i] != NULL) { - pending += 1; - } + wct = CVAL(buf, ofs); + if (wct < 2) { + /* + * Not enough space for the command and a following pointer + */ + return false; } + return (CVAL(buf, ofs+1) != 0xff); +} - SMB_ASSERT(found); +#define MAX_SMB_IOV 5 - if (pending == 0) { - TALLOC_FREE(cli_req); - } +struct cli_smb_state { + struct tevent_context *ev; + struct cli_state *cli; + uint8_t header[smb_wct+1]; /* Space for the header including the wct */ - return 0; -} + /* + * For normal requests, cli_smb_req_send chooses a mid. Secondary + * trans requests need to use the mid of the primary request, so we + * need a place to store it. Assume it's set if != 0. + */ + uint16_t mid; -/** - * @brief Chain up a request - * @param[in] mem_ctx The TALLOC_CTX for the result - * @param[in] ev The event context that will call us back - * @param[in] cli The cli_state we queue the request up for - * @param[in] smb_command The command that we want to issue - * @param[in] additional_flags open_and_x wants to add oplock header flags - * @param[in] wct How many words? - * @param[in] vwv The words, already in network order - * @param[in] bytes_alignment How shall we align "bytes"? - * @param[in] num_bytes How many bytes? - * @param[in] bytes The data the request ships - * - * cli_request_chain() is the core of the SMB request marshalling routine. It - * will create a new async_req structure in the cli->chain_accumulator->async - * array and marshall the smb_cmd, the vwv array and the bytes into - * cli->chain_accumulator->outbuf. - */ + uint16_t *vwv; + uint8_t bytecount_buf[2]; + + struct iovec iov[MAX_SMB_IOV+3]; + int iov_count; -static struct async_req *cli_request_chain(TALLOC_CTX *mem_ctx, - struct event_context *ev, - struct cli_state *cli, - uint8_t smb_command, - uint8_t additional_flags, - uint8_t wct, const uint16_t *vwv, - size_t bytes_alignment, - uint32_t num_bytes, - const uint8_t *bytes) + uint8_t *inbuf; + uint32_t seqnum; + int chain_num; + struct tevent_req **chained_requests; +}; + +static uint16_t cli_alloc_mid(struct cli_state *cli) { - struct async_req **tmp_reqs; - struct cli_request *req; + int num_pending = talloc_array_length(cli->pending); + uint16_t result; - req = cli->chain_accumulator; + while (true) { + int i; - tmp_reqs = TALLOC_REALLOC_ARRAY(req, req->async, struct async_req *, - req->num_async + 1); - if (tmp_reqs == NULL) { - DEBUG(0, ("talloc failed\n")); - return NULL; + result = cli->mid++; + if ((result == 0) || (result == 0xffff)) { + continue; + } + + for (i=0; i<num_pending; i++) { + if (result == cli_smb_req_mid(cli->pending[i])) { + break; + } + } + + if (i == num_pending) { + return result; + } } - req->async = tmp_reqs; - req->num_async += 1; +} - 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; - return NULL; +void cli_smb_req_unset_pending(struct tevent_req *req) +{ + struct cli_smb_state *state = tevent_req_data( + req, struct cli_smb_state); + struct cli_state *cli = state->cli; + int num_pending = talloc_array_length(cli->pending); + int i; + + if (num_pending == 1) { + /* + * The pending read_smb tevent_req is a child of + * cli->pending. So if nothing is pending anymore, we need to + * delete the socket read fde. + */ + TALLOC_FREE(cli->pending); + return; } - req->async[req->num_async-1]->private_data = req; - req->async[req->num_async-1]->print = cli_request_print; - talloc_set_destructor(req->async[req->num_async-1], - cli_async_req_destructor); - if (!smb_splice_chain(&req->outbuf, smb_command, wct, vwv, - bytes_alignment, num_bytes, bytes)) { - goto fail; + for (i=0; i<num_pending; i++) { + if (req == cli->pending[i]) { + break; + } + } + if (i == num_pending) { + /* + * Something's seriously broken. Just returning here is the + * right thing nevertheless, the point of this routine is to + * remove ourselves from cli->pending. + */ + return; + } + + /* + * Remove ourselves from the cli->pending array + */ + if (num_pending > 1) { + cli->pending[i] = cli->pending[num_pending-1]; } - return req->async[req->num_async-1]; + /* + * No NULL check here, we're shrinking by sizeof(void *), and + * talloc_realloc just adjusts the size for this. + */ + cli->pending = talloc_realloc(NULL, cli->pending, struct tevent_req *, + num_pending - 1); + return; +} - fail: - TALLOC_FREE(req->async[req->num_async-1]); - req->num_async -= 1; - return NULL; +static int cli_smb_req_destructor(struct tevent_req *req) +{ + cli_smb_req_unset_pending(req); + return 0; } -/** - * @brief prepare a cli_state to accept a chain of requests - * @param[in] cli The cli_state we want to queue up in - * @param[in] ev The event_context that will call us back for the socket - * @param[in] size_hint How many bytes are expected, just an optimization - * @retval Did we have enough memory? - * - * cli_chain_cork() sets up a new cli_request in cli->chain_accumulator. If - * cli is used in an async fashion, i.e. if we have outstanding requests, then - * we do not have to create a fd event. If cli is used only with the sync - * helpers, we need to create the fd_event here. - * - * If you want to issue a chained request to the server, do a - * cli_chain_cork(), then do you cli_open_send(), cli_read_and_x_send(), - * cli_close_send() and so on. The async requests that come out of - * cli_xxx_send() are normal async requests with the difference that they - * won't be shipped individually. But the event_context will still trigger the - * req->async.fn to be called on every single request. - * - * You have to take care yourself that you only issue chainable requests in - * the middle of the chain. - */ +static void cli_smb_received(struct tevent_req *subreq); -bool cli_chain_cork(struct cli_state *cli, struct event_context *ev, - size_t size_hint) +bool cli_smb_req_set_pending(struct tevent_req *req) { - struct cli_request *req = NULL; + struct cli_smb_state *state = tevent_req_data( + req, struct cli_smb_state); + struct cli_state *cli; + struct tevent_req **pending; + int num_pending; + struct tevent_req *subreq; + + cli = state->cli; + num_pending = talloc_array_length(cli->pending); + + pending = talloc_realloc(cli, cli->pending, struct tevent_req *, + num_pending+1); + if (pending == NULL) { + return false; + } + pending[num_pending] = req; + cli->pending = pending; + talloc_set_destructor(req, cli_smb_req_destructor); - SMB_ASSERT(cli->chain_accumulator == NULL); + if (num_pending > 0) { + return true; + } - if (cli->fd == -1) { - DEBUG(10, ("cli->fd closed\n")); + /* + * We're the first ones, add the read_smb request that waits for the + * answer from the server + */ + subreq = read_smb_send(cli->pending, state->ev, cli->fd); + if (subreq == NULL) { + cli_smb_req_unset_pending(req); return false; } + tevent_req_set_callback(subreq, cli_smb_received, cli); + return true; +} - if (cli->fd_event == NULL) { - SMB_ASSERT(cli->outstanding_requests == NULL); - cli->fd_event = event_add_fd(ev, cli, cli->fd, - EVENT_FD_READ, - cli_state_handler, cli); - if (cli->fd_event == NULL) { - return false; - } - } +/* + * Fetch a smb request's mid. Only valid after the request has been sent by + * cli_smb_req_send(). + */ +uint16_t cli_smb_req_mid(struct tevent_req *req) +{ + struct cli_smb_state *state = tevent_req_data( + req, struct cli_smb_state); + return SVAL(state->header, smb_mid); +} - req = talloc(cli, struct cli_request); - if (req == NULL) { - goto fail; +void cli_smb_req_set_mid(struct tevent_req *req, uint16_t mid) +{ + struct cli_smb_state *state = tevent_req_data( + req, struct cli_smb_state); + state->mid = mid; +} + +static size_t iov_len(const struct iovec *iov, int count) +{ + size_t result = 0; + int i; + for (i=0; i<count; i++) { + result += iov[i].iov_len; } - req->cli = cli; + return result; +} - if (size_hint == 0) { - size_hint = 100; +static uint8_t *iov_concat(TALLOC_CTX *mem_ctx, const struct iovec *iov, + int count) +{ + size_t len = iov_len(iov, count); + size_t copied; + uint8_t *buf; + int i; + + buf = talloc_array(mem_ctx, uint8_t, len); + if (buf == NULL) { + return NULL; } - req->outbuf = talloc_array(req, uint8_t, smb_wct + size_hint); - if (req->outbuf == NULL) { - goto fail; + copied = 0; + for (i=0; i<count; i++) { + memcpy(buf+copied, iov[i].iov_base, iov[i].iov_len); + copied += iov[i].iov_len; } - req->outbuf = TALLOC_REALLOC_ARRAY(NULL, req->outbuf, uint8_t, - smb_wct); + return buf; +} - req->num_async = 0; - req->async = NULL; +struct tevent_req *cli_smb_req_create(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct cli_state *cli, + uint8_t smb_command, + uint8_t additional_flags, + uint8_t wct, uint16_t *vwv, + int iov_count, + struct iovec *bytes_iov) +{ + struct tevent_req *result; + struct cli_smb_state *state; - req->enc_state = NULL; - req->recv_helper.fn = NULL; + if (iov_count > MAX_SMB_IOV) { + /* + * Should not happen :-) + */ + return NULL; + } - SSVAL(req->outbuf, smb_tid, cli->cnum); - cli_setup_packet_buf(cli, (char *)req->outbuf); + result = tevent_req_create(mem_ctx, &state, struct cli_smb_state); + if (result == NULL) { + return NULL; + } + state->ev = ev; + state->cli = cli; + state->mid = 0; /* Set to auto-choose in cli_smb_req_send */ + state->chain_num = 0; + state->chained_requests = NULL; + + cli_setup_packet_buf(cli, (char *)state->header); + SCVAL(state->header, smb_com, smb_command); + SSVAL(state->header, smb_tid, cli->cnum); + SCVAL(state->header, smb_wct, wct); + + state->vwv = vwv; + + SSVAL(state->bytecount_buf, 0, iov_len(bytes_iov, iov_count)); + + state->iov[0].iov_base = state->header; + state->iov[0].iov_len = sizeof(state->header); + state->iov[1].iov_base = state->vwv; + state->iov[1].iov_len = wct * sizeof(uint16_t); + state->iov[2].iov_base = state->bytecount_buf; + state->iov[2].iov_len = sizeof(uint16_t); + + if (iov_count != 0) { + memcpy(&state->iov[3], bytes_iov, + iov_count * sizeof(*bytes_iov)); + } + state->iov_count = iov_count + 3; - req->mid = cli_new_mid(cli); + return result; +} - cli->chain_accumulator = req; +static bool cli_signv(struct cli_state *cli, struct iovec *iov, int count, + uint32_t *seqnum) +{ + uint8_t *buf; - DEBUG(10, ("cli_chain_cork: mid=%d\n", req->mid)); + /* + * Obvious optimization: Make cli_calculate_sign_mac work with struct + * iovec directly. MD5Update would do that just fine. + */ - return true; - fail: - TALLOC_FREE(req); - if (cli->outstanding_requests == NULL) { - TALLOC_FREE(cli->fd_event); + if ((count <= 0) || (iov[0].iov_len < smb_wct)) { + return false; } - return false; -} -/** - * Ship a request queued up via cli_request_chain() - * @param[in] cl The connection - */ + buf = iov_concat(talloc_tos(), iov, count); + if (buf == NULL) { + return false; + } -void cli_chain_uncork(struct cli_state *cli) -{ - struct cli_request *req = cli->chain_accumulator; - size_t smblen; + cli_calculate_sign_mac(cli, (char *)buf, seqnum); + memcpy(iov[0].iov_base, buf, iov[0].iov_len); - SMB_ASSERT(req != NULL); + TALLOC_FREE(buf); + return true; +} - DLIST_ADD_END(cli->outstanding_requests, req, struct cli_request *); - talloc_set_destructor(req, cli_request_destructor); +static void cli_smb_sent(struct tevent_req *subreq); - cli->chain_accumulator = NULL; +static bool cli_smb_req_iov_send(struct tevent_req *req, + struct cli_smb_state *state, + struct iovec *iov, int iov_count) +{ + struct tevent_req *subreq; - SSVAL(req->outbuf, smb_mid, req->mid); + if (iov[0].iov_len < smb_wct) { + return false; + } - smblen = talloc_get_size(req->outbuf) - 4; + if (state->mid != 0) { + SSVAL(iov[0].iov_base, smb_mid, state->mid); + } else { + SSVAL(iov[0].iov_base, smb_mid, cli_alloc_mid(state->cli)); + } - smb_setlen((char *)req->outbuf, smblen); + smb_setlen((char *)iov[0].iov_base, iov_len(iov, iov_count) - 4); - 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); + if (!cli_signv(state->cli, iov, iov_count, &state->seqnum)) { + return false; } - cli_calculate_sign_mac(cli, (char *)req->outbuf, &req->seqnum); - - if (cli_encryption_on(cli)) { + if (cli_encryption_on(state->cli)) { NTSTATUS status; - char *enc_buf; + char *buf, *enc_buf; - status = cli_encrypt_message(cli, (char *)req->outbuf, + buf = (char *)iov_concat(talloc_tos(), iov, iov_count); + if (buf == NULL) { + return false; + } + status = cli_encrypt_message(state->cli, (char *)buf, &enc_buf); + TALLOC_FREE(buf); if (!NT_STATUS_IS_OK(status)) { - DEBUG(0, ("Error in encrypting client message. " - "Error %s\n", nt_errstr(status))); - TALLOC_FREE(req); - return; + DEBUG(0, ("Error in encrypting client message: %s\n", + nt_errstr(status))); + return false; } - req->outbuf = (uint8_t *)enc_buf; - req->enc_state = cli->trans_enc_state; + buf = (char *)talloc_memdup(state, enc_buf, + smb_len(enc_buf)+4); + SAFE_FREE(enc_buf); + if (buf == NULL) { + return false; + } + iov[0].iov_base = buf; + iov[0].iov_len = talloc_get_size(buf); + subreq = writev_send(state, state->ev, state->cli->outgoing, + state->cli->fd, iov, 1); + } else { + subreq = writev_send(state, state->ev, state->cli->outgoing, + state->cli->fd, iov, iov_count); + } + if (subreq == NULL) { + return false; } + tevent_req_set_callback(subreq, cli_smb_sent, req); + return true; +} - req->sent = 0; +bool cli_smb_req_send(struct tevent_req *req) +{ + struct cli_smb_state *state = tevent_req_data( + req, struct cli_smb_state); - event_fd_set_writeable(cli->fd_event); + return cli_smb_req_iov_send(req, state, state->iov, state->iov_count); } -/** - * @brief Send a request to the server - * @param[in] mem_ctx The TALLOC_CTX for the result - * @param[in] ev The event context that will call us back - * @param[in] cli The cli_state we queue the request up for - * @param[in] smb_command The command that we want to issue - * @param[in] additional_flags open_and_x wants to add oplock header flags - * @param[in] wct How many words? - * @param[in] vwv The words, already in network order - * @param[in] bytes_alignment How shall we align "bytes"? - * @param[in] num_bytes How many bytes? - * @param[in] bytes The data the request ships - * - * This is the generic routine to be used by the cli_xxx_send routines. - */ - -struct async_req *cli_request_send(TALLOC_CTX *mem_ctx, - struct event_context *ev, - struct cli_state *cli, - uint8_t smb_command, - uint8_t additional_flags, - uint8_t wct, const uint16_t *vwv, - size_t bytes_alignment, - uint32_t num_bytes, const uint8_t *bytes) +struct tevent_req *cli_smb_send(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct cli_state *cli, + uint8_t smb_command, + uint8_t additional_flags, + uint8_t wct, uint16_t *vwv, + uint32_t num_bytes, + const uint8_t *bytes) { - struct async_req *result; - bool uncork = false; - - if (cli->chain_accumulator == NULL) { - if (!cli_chain_cork(cli, ev, - wct * sizeof(uint16_t) + num_bytes + 3)) { - DEBUG(1, ("cli_chain_cork failed\n")); - return NULL; - } - uncork = true; - } + struct tevent_req *req; + struct iovec iov; - result = cli_request_chain(mem_ctx, ev, cli, smb_command, - additional_flags, wct, vwv, bytes_alignment, - num_bytes, bytes); + iov.iov_base = CONST_DISCARD(char *, bytes); + iov.iov_len = num_bytes; - if (result == NULL) { - DEBUG(1, ("cli_request_chain failed\n")); + req = cli_smb_req_create(mem_ctx, ev, cli, smb_command, + additional_flags, wct, vwv, 1, &iov); + if (req == NULL) { + return NULL; } + if (!cli_smb_req_send(req)) { + TALLOC_FREE(req); + } + return req; +} - if (uncork) { - cli_chain_uncork(cli); +static void cli_smb_sent(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_smb_state *state = tevent_req_data( + req, struct cli_smb_state); + ssize_t nwritten; + int err; + + nwritten = writev_recv(subreq, &err); + TALLOC_FREE(subreq); + if (nwritten == -1) { + tevent_req_nterror(req, map_nt_error_from_unix(err)); + return; } - return result; -} + switch (CVAL(state->header, smb_com)) { + case SMBtranss: + case SMBtranss2: + case SMBnttranss: + case SMBntcancel: + state->inbuf = NULL; + tevent_req_done(req); + return; + case SMBlockingX: + if ((CVAL(state->header, smb_wct) == 8) && + (CVAL(state->vwv+3, 0) == LOCKING_ANDX_OPLOCK_RELEASE)) { + state->inbuf = NULL; + tevent_req_done(req); + return; + } + } -/** - * Calculate the current ofs to wct for requests like write&x - * @param[in] req The smb request we're currently building - * @retval how many bytes offset have we accumulated? - */ + if (!cli_smb_req_set_pending(req)) { + tevent_req_nterror(req, NT_STATUS_NO_MEMORY); + return; + } +} -uint16_t cli_wct_ofs(const struct cli_state *cli) +static void cli_smb_received(struct tevent_req *subreq) { - size_t buf_size; + struct cli_state *cli = tevent_req_callback_data( + subreq, struct cli_state); + struct tevent_req *req; + struct cli_smb_state *state; + struct tevent_context *ev; + NTSTATUS status; + uint8_t *inbuf; + ssize_t received; + int num_pending; + int i, err; + uint16_t mid; + bool oplock_break; + + received = read_smb_recv(subreq, talloc_tos(), &inbuf, &err); + TALLOC_FREE(subreq); + if (received == -1) { + status = map_nt_error_from_unix(err); + goto fail; + } - if (cli->chain_accumulator == NULL) { - return smb_wct - 4; + if ((IVAL(inbuf, 4) != 0x424d53ff) /* 0xFF"SMB" */ + && (SVAL(inbuf, 4) != 0x45ff)) /* 0xFF"E" */ { + DEBUG(10, ("Got non-SMB PDU\n")); + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto fail; } - buf_size = talloc_get_size(cli->chain_accumulator->outbuf); + if (cli_encryption_on(cli) && (CVAL(inbuf, 0) == 0)) { + uint16_t enc_ctx_num; + + status = get_enc_ctx_num(inbuf, &enc_ctx_num); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("get_enc_ctx_num returned %s\n", + nt_errstr(status))); + goto fail; + } + + if (enc_ctx_num != cli->trans_enc_state->enc_ctx_num) { + DEBUG(10, ("wrong enc_ctx %d, expected %d\n", + enc_ctx_num, + cli->trans_enc_state->enc_ctx_num)); + status = NT_STATUS_INVALID_HANDLE; + goto fail; + } - if (buf_size == smb_wct) { - return smb_wct - 4; + status = common_decrypt_buffer(cli->trans_enc_state, + (char *)inbuf); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("common_decrypt_buffer returned %s\n", + nt_errstr(status))); + goto fail; + } } - /* - * Add alignment for subsequent requests - */ + mid = SVAL(inbuf, smb_mid); + num_pending = talloc_array_length(cli->pending); - if ((buf_size % 4) != 0) { - buf_size += (4 - (buf_size % 4)); + for (i=0; i<num_pending; i++) { + if (mid == cli_smb_req_mid(cli->pending[i])) { + break; + } + } + if (i == num_pending) { + /* Dump unexpected reply */ + TALLOC_FREE(inbuf); + goto done; } - return buf_size - 4; -} + oplock_break = false; -/** - * Figure out if there is an andx command behind the current one - * @param[in] buf The smb buffer to look at - * @param[in] ofs The offset to the wct field that is followed by the cmd - * @retval Is there a command following? - */ + if (mid == 0xffff) { + /* + * Paranoia checks that this is really an oplock break request. + */ + oplock_break = (smb_len(inbuf) == 51); /* hdr + 8 words */ + oplock_break &= ((CVAL(inbuf, smb_flg) & FLAG_REPLY) == 0); + oplock_break &= (CVAL(inbuf, smb_com) == SMBlockingX); + oplock_break &= (SVAL(inbuf, smb_vwv6) == 0); + oplock_break &= (SVAL(inbuf, smb_vwv7) == 0); + + if (!oplock_break) { + /* Dump unexpected reply */ + TALLOC_FREE(inbuf); + goto done; + } + } -static bool have_andx_command(const char *buf, uint16_t ofs) -{ - uint8_t wct; - size_t buflen = talloc_get_size(buf); + req = cli->pending[i]; + state = tevent_req_data(req, struct cli_smb_state); + ev = state->ev; - if ((ofs == buflen-1) || (ofs == buflen)) { - return false; + if (!oplock_break /* oplock breaks are not signed */ + && !cli_check_sign_mac(cli, (char *)inbuf, state->seqnum+1)) { + DEBUG(10, ("cli_check_sign_mac failed\n")); + TALLOC_FREE(inbuf); + status = NT_STATUS_ACCESS_DENIED; + goto fail; } - wct = CVAL(buf, ofs); - if (wct < 2) { + if (state->chained_requests == NULL) { + state->inbuf = talloc_move(state, &inbuf); + talloc_set_destructor(req, NULL); + cli_smb_req_destructor(req); + tevent_req_done(req); + } else { + struct tevent_req **chain = talloc_move( + talloc_tos(), &state->chained_requests); + int num_chained = talloc_array_length(chain); + + for (i=0; i<num_chained; i++) { + state = tevent_req_data(chain[i], struct + cli_smb_state); + state->inbuf = inbuf; + state->chain_num = i; + tevent_req_done(chain[i]); + } + TALLOC_FREE(inbuf); + TALLOC_FREE(chain); + } + done: + if (talloc_array_length(cli->pending) > 0) { /* - * Not enough space for the command and a following pointer + * Set up another read request for the other pending cli_smb + * requests */ - return false; + state = tevent_req_data(cli->pending[0], struct cli_smb_state); + subreq = read_smb_send(cli->pending, state->ev, cli->fd); + if (subreq == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + tevent_req_set_callback(subreq, cli_smb_received, cli); + } + return; + fail: + /* + * Cancel all pending requests. We don't do a for-loop walking + * cli->pending because that array changes in + * cli_smb_req_destructor(). + */ + while (talloc_array_length(cli->pending) > 0) { + req = cli->pending[0]; + talloc_set_destructor(req, NULL); + cli_smb_req_destructor(req); + tevent_req_nterror(req, status); } - return (CVAL(buf, ofs+1) != 0xff); } -/** - * @brief Pull reply data out of a request - * @param[in] req The request that we just received a reply for - * @param[out] pwct How many words did the server send? - * @param[out] pvwv The words themselves - * @param[out] pnum_bytes How many bytes did the server send? - * @param[out] pbytes The bytes themselves - * @retval Was the reply formally correct? - */ - -NTSTATUS cli_pull_reply(struct async_req *req, - uint8_t *pwct, uint16_t **pvwv, - uint16_t *pnum_bytes, uint8_t **pbytes) +NTSTATUS cli_smb_recv(struct tevent_req *req, uint8_t min_wct, + uint8_t *pwct, uint16_t **pvwv, + uint32_t *pnum_bytes, uint8_t **pbytes) { - struct cli_request *cli_req = talloc_get_type_abort( - req->private_data, struct cli_request); - uint8_t wct, cmd; + struct cli_smb_state *state = tevent_req_data( + req, struct cli_smb_state); + NTSTATUS status = NT_STATUS_OK; + uint8_t cmd, wct; uint16_t num_bytes; size_t wct_ofs, bytes_offset; - int i, j; - NTSTATUS status; - - for (i = 0; i < cli_req->num_async; i++) { - if (req == cli_req->async[i]) { - break; - } - } + int i; - if (i == cli_req->num_async) { - cli_set_error(cli_req->cli, NT_STATUS_INVALID_PARAMETER); - return NT_STATUS_INVALID_PARAMETER; + if (tevent_req_is_nterror(req, &status)) { + return status; } - /** - * The status we pull here is only relevant for the last reply in the - * chain. - */ - - status = cli_pull_error(cli_req->inbuf); - - if (i == 0) { - if (NT_STATUS_IS_ERR(status) - && !have_andx_command(cli_req->inbuf, smb_wct)) { - cli_set_error(cli_req->cli, status); - return status; - } - wct_ofs = smb_wct; - goto done; + if (state->inbuf == NULL) { + /* This was a request without a reply */ + return NT_STATUS_OK; } - cmd = CVAL(cli_req->inbuf, smb_com); wct_ofs = smb_wct; + cmd = CVAL(state->inbuf, smb_com); - for (j = 0; j < i; j++) { - if (j < i-1) { + for (i=0; i<state->chain_num; i++) { + if (i < state->chain_num-1) { if (cmd == 0xff) { return NT_STATUS_REQUEST_ABORTED; } @@ -745,7 +856,7 @@ NTSTATUS cli_pull_reply(struct async_req *req, } } - if (!have_andx_command(cli_req->inbuf, wct_ofs)) { + if (!have_andx_command((char *)state->inbuf, wct_ofs)) { /* * This request was not completed because a previous * request in the chain had received an error. @@ -753,7 +864,7 @@ NTSTATUS cli_pull_reply(struct async_req *req, return NT_STATUS_REQUEST_ABORTED; } - wct_ofs = SVAL(cli_req->inbuf, wct_ofs + 3); + wct_ofs = SVAL(state->inbuf, wct_ofs + 3); /* * Skip the all-present length field. No overflow, we've just @@ -761,14 +872,16 @@ NTSTATUS cli_pull_reply(struct async_req *req, */ wct_ofs += 4; - if (wct_ofs+2 > talloc_get_size(cli_req->inbuf)) { + if (wct_ofs+2 > talloc_get_size(state->inbuf)) { return NT_STATUS_INVALID_NETWORK_RESPONSE; } - cmd = CVAL(cli_req->inbuf, wct_ofs + 1); + cmd = CVAL(state->inbuf, wct_ofs + 1); } - if (!have_andx_command(cli_req->inbuf, wct_ofs) + status = cli_pull_error((char *)state->inbuf); + + if (!have_andx_command((char *)state->inbuf, wct_ofs) && NT_STATUS_IS_ERR(status)) { /* * The last command takes the error code. All further commands @@ -778,358 +891,241 @@ NTSTATUS cli_pull_reply(struct async_req *req, return status; } - done: - wct = CVAL(cli_req->inbuf, wct_ofs); - + wct = CVAL(state->inbuf, wct_ofs); bytes_offset = wct_ofs + 1 + wct * sizeof(uint16_t); - num_bytes = SVAL(cli_req->inbuf, bytes_offset); + num_bytes = SVAL(state->inbuf, bytes_offset); + + if (wct < min_wct) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } /* * wct_ofs is a 16-bit value plus 4, wct is a 8-bit value, num_bytes * is a 16-bit value. So bytes_offset being size_t should be far from * wrapping. */ - - if ((bytes_offset + 2 > talloc_get_size(cli_req->inbuf)) + if ((bytes_offset + 2 > talloc_get_size(state->inbuf)) || (bytes_offset > 0xffff)) { return NT_STATUS_INVALID_NETWORK_RESPONSE; } - *pwct = wct; - *pvwv = (uint16_t *)(cli_req->inbuf + wct_ofs + 1); - *pnum_bytes = num_bytes; - *pbytes = (uint8_t *)cli_req->inbuf + bytes_offset + 2; - - return NT_STATUS_OK; -} - -/** - * Decrypt a PDU, check the signature - * @param[in] cli The cli_state that received something - * @param[in] pdu The incoming bytes - * @retval error code - */ - - -static NTSTATUS validate_smb_crypto(struct cli_state *cli, char *pdu, - struct cli_request **_req, - uint16_t *_mid) -{ - NTSTATUS status; - struct cli_request *req = NULL; - uint16_t mid; - - *_req = NULL; - *_mid = 0; - - if ((IVAL(pdu, 4) != 0x424d53ff) /* 0xFF"SMB" */ - && (SVAL(pdu, 4) != 0x45ff)) /* 0xFF"E" */ { - DEBUG(10, ("Got non-SMB PDU\n")); - return NT_STATUS_INVALID_NETWORK_RESPONSE; - } - - if (cli_encryption_on(cli) && CVAL(pdu, 0) == 0) { - uint16_t enc_ctx_num; - - status = get_enc_ctx_num((uint8_t *)pdu, &enc_ctx_num); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(10, ("get_enc_ctx_num returned %s\n", - nt_errstr(status))); - return status; - } - - if (enc_ctx_num != cli->trans_enc_state->enc_ctx_num) { - DEBUG(10, ("wrong enc_ctx %d, expected %d\n", - enc_ctx_num, - cli->trans_enc_state->enc_ctx_num)); - return NT_STATUS_INVALID_HANDLE; - } - - status = common_decrypt_buffer(cli->trans_enc_state, pdu); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(10, ("common_decrypt_buffer returned %s\n", - nt_errstr(status))); - return status; - } + if (pwct != NULL) { + *pwct = wct; } - - mid = SVAL(pdu, smb_mid); - - for (req = cli->outstanding_requests; req; req = req->next) { - if (req->mid == mid) { - break; - } + if (pvwv != NULL) { + *pvwv = (uint16_t *)(state->inbuf + wct_ofs + 1); } - - if (!req) { - /* oplock breaks are not signed */ - goto done; + if (pnum_bytes != NULL) { + *pnum_bytes = num_bytes; } - - if (!cli_check_sign_mac(cli, pdu, req->seqnum+1)) { - DEBUG(10, ("cli_check_sign_mac failed\n")); - return NT_STATUS_ACCESS_DENIED; + if (pbytes != NULL) { + *pbytes = (uint8_t *)state->inbuf + bytes_offset + 2; } -done: - *_req = req; - *_mid = mid; return NT_STATUS_OK; } -/** - * A PDU has arrived on cli->evt_inbuf - * @param[in] cli The cli_state that received something - */ - -static void handle_incoming_pdu(struct cli_state *cli) +size_t cli_smb_wct_ofs(struct tevent_req **reqs, int num_reqs) { - struct cli_request *req, *next; - uint16_t mid; - size_t raw_pdu_len, buf_len, rest_len; - char *pdu; + size_t wct_ofs; int i; - NTSTATUS status; - - int num_async; - /* - * The encrypted PDU len might differ from the unencrypted one - */ - raw_pdu_len = smb_len(cli->evt_inbuf) + 4; - buf_len = talloc_get_size(cli->evt_inbuf); - rest_len = buf_len - raw_pdu_len; + wct_ofs = smb_wct - 4; - if (buf_len == raw_pdu_len) { - /* - * Optimal case: Exactly one PDU was in the socket buffer - */ - pdu = cli->evt_inbuf; - cli->evt_inbuf = NULL; + for (i=0; i<num_reqs; i++) { + struct cli_smb_state *state; + state = tevent_req_data(reqs[i], struct cli_smb_state); + wct_ofs += iov_len(state->iov+1, state->iov_count-1); + wct_ofs = (wct_ofs + 3) & ~3; } - else { - DEBUG(11, ("buf_len = %d, raw_pdu_len = %d, splitting " - "buffer\n", (int)buf_len, (int)raw_pdu_len)); - - if (raw_pdu_len < rest_len) { - /* - * The PDU is shorter, talloc_memdup that one. - */ - pdu = (char *)talloc_memdup( - cli, cli->evt_inbuf, raw_pdu_len); - - memmove(cli->evt_inbuf, cli->evt_inbuf + raw_pdu_len, - buf_len - raw_pdu_len); - - cli->evt_inbuf = TALLOC_REALLOC_ARRAY( - NULL, cli->evt_inbuf, char, rest_len); - - if (pdu == NULL) { - status = NT_STATUS_NO_MEMORY; - goto invalidate_requests; - } - } - else { - /* - * The PDU is larger than the rest, talloc_memdup the - * rest - */ - pdu = cli->evt_inbuf; - - cli->evt_inbuf = (char *)talloc_memdup( - cli, pdu + raw_pdu_len, rest_len); + return wct_ofs; +} - if (cli->evt_inbuf == NULL) { - status = NT_STATUS_NO_MEMORY; - goto invalidate_requests; - } - } +bool cli_smb_chain_send(struct tevent_req **reqs, int num_reqs) +{ + struct cli_smb_state *first_state = tevent_req_data( + reqs[0], struct cli_smb_state); + struct cli_smb_state *last_state = tevent_req_data( + reqs[num_reqs-1], struct cli_smb_state); + struct cli_smb_state *state; + size_t wct_offset; + size_t chain_padding = 0; + int i, iovlen; + struct iovec *iov = NULL; + struct iovec *this_iov; + + iovlen = 0; + for (i=0; i<num_reqs; i++) { + state = tevent_req_data(reqs[i], struct cli_smb_state); + iovlen += state->iov_count; } - status = validate_smb_crypto(cli, pdu, &req, &mid); - if (!NT_STATUS_IS_OK(status)) { - goto invalidate_requests; + iov = talloc_array(last_state, struct iovec, iovlen); + if (iov == NULL) { + goto fail; } - DEBUG(10, ("handle_incoming_pdu: got mid %d\n", mid)); - - if (req == NULL) { - DEBUG(3, ("Request for mid %d not found, dumping PDU\n", mid)); - - TALLOC_FREE(pdu); - return; + first_state->chained_requests = (struct tevent_req **)talloc_memdup( + last_state, reqs, sizeof(*reqs) * num_reqs); + if (first_state->chained_requests == NULL) { + goto fail; } - req->inbuf = talloc_move(req, &pdu); + wct_offset = smb_wct - 4; + this_iov = iov; - /* - * Freeing the last async_req will free the req (see - * cli_async_req_destructor). So make a copy of req->num_async, we - * can't reference it in the last round. - */ + for (i=0; i<num_reqs; i++) { + size_t next_padding = 0; + uint16_t *vwv; - num_async = req->num_async; + state = tevent_req_data(reqs[i], struct cli_smb_state); - for (i=0; i<num_async; i++) { - /** - * A request might have been talloc_free()'ed before we arrive - * here. It will have removed itself from req->async via its - * destructor cli_async_req_destructor(). - */ - if (req->async[i] != NULL) { - if (req->recv_helper.fn != NULL) { - req->recv_helper.fn(req->async[i]); - } else { - async_req_done(req->async[i]); + if (i < num_reqs-1) { + if (!is_andx_req(CVAL(state->header, smb_com)) + || CVAL(state->header, smb_wct) < 2) { + goto fail; } } - } - return; - invalidate_requests: - - DEBUG(10, ("handle_incoming_pdu: Aborting with %s\n", - nt_errstr(status))); - - for (req = cli->outstanding_requests; req; req = next) { - next = req->next; - if (req->num_async) { - async_req_nterror(req->async[0], status); + wct_offset += iov_len(state->iov+1, state->iov_count-1) + 1; + if ((wct_offset % 4) != 0) { + next_padding = 4 - (wct_offset % 4); } - } - return; -} - -/** - * fd event callback. This is the basic connection to the socket - * @param[in] event_ctx The event context that called us - * @param[in] event The event that fired - * @param[in] flags EVENT_FD_READ | EVENT_FD_WRITE - * @param[in] p private_data, in this case the cli_state - */ - -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, *next; - NTSTATUS status; - - DEBUG(11, ("cli_state_handler called with flags %d\n", flags)); - - if (flags & EVENT_FD_WRITE) { - size_t to_send; - ssize_t sent; - - for (req = cli->outstanding_requests; req; req = req->next) { - to_send = smb_len(req->outbuf)+4; - if (to_send > req->sent) { - break; - } + wct_offset += next_padding; + vwv = state->vwv; + + if (i < num_reqs-1) { + struct cli_smb_state *next_state = tevent_req_data( + reqs[i+1], struct cli_smb_state); + SCVAL(vwv+0, 0, CVAL(next_state->header, smb_com)); + SCVAL(vwv+0, 1, 0); + SSVAL(vwv+1, 0, wct_offset); + } else if (is_andx_req(CVAL(state->header, smb_com))) { + /* properly end the chain */ + SCVAL(vwv+0, 0, 0xff); + SCVAL(vwv+0, 1, 0xff); + SSVAL(vwv+1, 0, 0); } - if (req == NULL) { - if (cli->fd_event != NULL) { - event_fd_set_not_writeable(cli->fd_event); - } - return; - } - - sent = sys_send(cli->fd, req->outbuf + req->sent, - to_send - req->sent, 0); - - if (sent < 0) { - status = map_nt_error_from_unix(errno); - goto sock_error; - } - - req->sent += sent; - - if (req->sent == to_send) { - return; + if (i == 0) { + this_iov[0] = state->iov[0]; + } else { + /* + * This one is a bit subtle. We have to add + * chain_padding bytes between the requests, and we + * have to also include the wct field of the + * subsequent requests. We use the subsequent header + * for the padding, it contains the wct field in its + * last byte. + */ + this_iov[0].iov_len = chain_padding+1; + this_iov[0].iov_base = &state->header[ + sizeof(state->header) - this_iov[0].iov_len]; + memset(this_iov[0].iov_base, 0, this_iov[0].iov_len-1); } + memcpy(this_iov+1, state->iov+1, + sizeof(struct iovec) * (state->iov_count-1)); + this_iov += state->iov_count; + chain_padding = next_padding; } - if (flags & EVENT_FD_READ) { - int res, available; - size_t old_size, new_size; - char *tmp; - - res = ioctl(cli->fd, FIONREAD, &available); - if (res == -1) { - DEBUG(10, ("ioctl(FIONREAD) failed: %s\n", - strerror(errno))); - status = map_nt_error_from_unix(errno); - goto sock_error; - } - - if (available == 0) { - /* EOF */ - status = NT_STATUS_END_OF_FILE; - goto sock_error; - } - - old_size = talloc_get_size(cli->evt_inbuf); - new_size = old_size + available; + if (!cli_smb_req_iov_send(reqs[0], last_state, iov, iovlen)) { + goto fail; + } + return true; + fail: + TALLOC_FREE(iov); + return false; +} - if (new_size < old_size) { - /* wrap */ - status = NT_STATUS_UNEXPECTED_IO_ERROR; - goto sock_error; - } +uint8_t *cli_smb_inbuf(struct tevent_req *req) +{ + struct cli_smb_state *state = tevent_req_data( + req, struct cli_smb_state); + return state->inbuf; +} - tmp = TALLOC_REALLOC_ARRAY(cli, cli->evt_inbuf, char, - new_size); - if (tmp == NULL) { - /* nomem */ - status = NT_STATUS_NO_MEMORY; - goto sock_error; - } - cli->evt_inbuf = tmp; +bool cli_has_async_calls(struct cli_state *cli) +{ + return ((tevent_queue_length(cli->outgoing) != 0) + || (talloc_array_length(cli->pending) != 0)); +} - res = sys_recv(cli->fd, cli->evt_inbuf + old_size, available, 0); - if (res == -1) { - DEBUG(10, ("recv failed: %s\n", strerror(errno))); - status = map_nt_error_from_unix(errno); - goto sock_error; - } +struct cli_smb_oplock_break_waiter_state { + uint16_t fnum; + uint8_t level; +}; - DEBUG(11, ("cli_state_handler: received %d bytes, " - "smb_len(evt_inbuf) = %d\n", (int)res, - smb_len(cli->evt_inbuf))); +static void cli_smb_oplock_break_waiter_done(struct tevent_req *subreq); - /* recv *might* have returned less than announced */ - new_size = old_size + res; +struct tevent_req *cli_smb_oplock_break_waiter_send(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct cli_state *cli) +{ + struct tevent_req *req, *subreq; + struct cli_smb_oplock_break_waiter_state *state; + struct cli_smb_state *smb_state; - /* shrink, so I don't expect errors here */ - cli->evt_inbuf = TALLOC_REALLOC_ARRAY(cli, cli->evt_inbuf, - char, new_size); + req = tevent_req_create(mem_ctx, &state, + struct cli_smb_oplock_break_waiter_state); + if (req == NULL) { + return NULL; + } - while ((cli->evt_inbuf != NULL) - && ((smb_len(cli->evt_inbuf) + 4) <= new_size)) { - /* - * we've got a complete NBT level PDU in evt_inbuf - */ - handle_incoming_pdu(cli); - new_size = talloc_get_size(cli->evt_inbuf); - } + /* + * Create a fake SMB request that we will never send out. This is only + * used to be set into the pending queue with the right mid. + */ + subreq = cli_smb_req_create(mem_ctx, ev, cli, 0, 0, 0, NULL, 0, NULL); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); } + smb_state = tevent_req_data(subreq, struct cli_smb_state); + SSVAL(smb_state->header, smb_mid, 0xffff); - return; + if (!cli_smb_req_set_pending(subreq)) { + tevent_req_nterror(req, NT_STATUS_NO_MEMORY); + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_smb_oplock_break_waiter_done, req); + return req; +} - sock_error: +static void cli_smb_oplock_break_waiter_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_smb_oplock_break_waiter_state *state = tevent_req_data( + req, struct cli_smb_oplock_break_waiter_state); + uint8_t wct; + uint16_t *vwv; + uint32_t num_bytes; + uint8_t *bytes; + NTSTATUS status; - for (req = cli->outstanding_requests; req; req = next) { - int i, num_async; + status = cli_smb_recv(subreq, 8, &wct, &vwv, &num_bytes, &bytes); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(subreq); + tevent_req_nterror(req, status); + return; + } + state->fnum = SVAL(vwv+2, 0); + state->level = CVAL(vwv+3, 1); + tevent_req_done(req); +} - next = req->next; - num_async = req->num_async; +NTSTATUS cli_smb_oplock_break_waiter_recv(struct tevent_req *req, + uint16_t *pfnum, + uint8_t *plevel) +{ + struct cli_smb_oplock_break_waiter_state *state = tevent_req_data( + req, struct cli_smb_oplock_break_waiter_state); + NTSTATUS status; - for (i=0; i<num_async; i++) { - async_req_nterror(req->async[i], status); - } + if (tevent_req_is_nterror(req, &status)) { + return status; } - TALLOC_FREE(cli->fd_event); - close(cli->fd); - cli->fd = -1; + *pfnum = state->fnum; + *plevel = state->level; + return NT_STATUS_OK; } diff --git a/source3/libsmb/cliconnect.c b/source3/libsmb/cliconnect.c index aa1ca595a7..53a812d222 100644 --- a/source3/libsmb/cliconnect.c +++ b/source3/libsmb/cliconnect.c @@ -165,14 +165,30 @@ static uint32 cli_session_setup_capabilities(struct cli_state *cli) Do a NT1 guest session setup. ****************************************************************************/ -struct async_req *cli_session_setup_guest_send(TALLOC_CTX *mem_ctx, - struct event_context *ev, - struct cli_state *cli) +struct cli_session_setup_guest_state { + struct cli_state *cli; + uint16_t vwv[16]; +}; + +static void cli_session_setup_guest_done(struct tevent_req *subreq); + +struct tevent_req *cli_session_setup_guest_send(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct cli_state *cli) { - struct async_req *result; - uint16_t vwv[13]; + struct tevent_req *req, *subreq; + struct cli_session_setup_guest_state *state; + uint16_t *vwv; uint8_t *bytes; + req = tevent_req_create(mem_ctx, &state, + struct cli_session_setup_guest_state); + if (req == NULL) { + return NULL; + } + state->cli = cli; + vwv = state->vwv; + SCVAL(vwv+0, 0, 0xFF); SCVAL(vwv+0, 1, 0); SSVAL(vwv+1, 0, 0); @@ -186,7 +202,7 @@ struct async_req *cli_session_setup_guest_send(TALLOC_CTX *mem_ctx, SSVAL(vwv+10, 0, 0); SIVAL(vwv+11, 0, cli_session_setup_capabilities(cli)); - bytes = talloc_array(talloc_tos(), uint8_t, 0); + bytes = talloc_array(state, uint8_t, 0); bytes = smb_bytes_push_str(bytes, cli_ucs2(cli), "", 1, /* username */ NULL); @@ -195,68 +211,78 @@ struct async_req *cli_session_setup_guest_send(TALLOC_CTX *mem_ctx, bytes = smb_bytes_push_str(bytes, cli_ucs2(cli), "Unix", 5, NULL); bytes = smb_bytes_push_str(bytes, cli_ucs2(cli), "Samba", 6, NULL); - if (bytes == NULL) { - return NULL; + if (tevent_req_nomem(bytes, req)) { + return tevent_req_post(req, ev); } - result = cli_request_send(mem_ctx, ev, cli, SMBsesssetupX, 0, - 13, vwv, 0, talloc_get_size(bytes), bytes); - TALLOC_FREE(bytes); - return result; + subreq = cli_smb_send(state, ev, cli, SMBsesssetupX, 0, 13, vwv, + talloc_get_size(bytes), bytes); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_session_setup_guest_done, req); + return req; } -NTSTATUS cli_session_setup_guest_recv(struct async_req *req) +static void cli_session_setup_guest_done(struct tevent_req *subreq) { - struct cli_request *cli_req = talloc_get_type_abort( - req->private_data, struct cli_request); - struct cli_state *cli = cli_req->cli; - uint8_t wct; - uint16_t *vwv; - uint16_t num_bytes; + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_session_setup_guest_state *state = tevent_req_data( + req, struct cli_session_setup_guest_state); + struct cli_state *cli = state->cli; + uint32_t num_bytes; + char *inbuf; uint8_t *bytes; uint8_t *p; NTSTATUS status; - if (async_req_is_nterror(req, &status)) { - return status; - } - - status = cli_pull_reply(req, &wct, &vwv, &num_bytes, &bytes); + status = cli_smb_recv(subreq, 0, NULL, NULL, &num_bytes, &bytes); if (!NT_STATUS_IS_OK(status)) { - return status; + TALLOC_FREE(subreq); + tevent_req_nterror(req, status); + return; } + inbuf = (char *)cli_smb_inbuf(subreq); p = bytes; - cli->vuid = SVAL(cli_req->inbuf, smb_uid); + cli->vuid = SVAL(inbuf, smb_uid); - p += clistr_pull(cli_req->inbuf, cli->server_os, (char *)p, - sizeof(fstring), bytes+num_bytes-p, STR_TERMINATE); - p += clistr_pull(cli_req->inbuf, cli->server_type, (char *)p, - sizeof(fstring), bytes+num_bytes-p, STR_TERMINATE); - p += clistr_pull(cli_req->inbuf, cli->server_domain, (char *)p, - sizeof(fstring), bytes+num_bytes-p, STR_TERMINATE); + p += clistr_pull(inbuf, cli->server_os, (char *)p, sizeof(fstring), + bytes+num_bytes-p, STR_TERMINATE); + p += clistr_pull(inbuf, cli->server_type, (char *)p, sizeof(fstring), + bytes+num_bytes-p, STR_TERMINATE); + p += clistr_pull(inbuf, cli->server_domain, (char *)p, sizeof(fstring), + bytes+num_bytes-p, STR_TERMINATE); if (strstr(cli->server_type, "Samba")) { cli->is_samba = True; } + TALLOC_FREE(subreq); + status = cli_set_username(cli, ""); if (!NT_STATUS_IS_OK(status)) { - return status; + tevent_req_nterror(req, status); + return; } + tevent_req_done(req); +} - return NT_STATUS_OK; +NTSTATUS cli_session_setup_guest_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); } static NTSTATUS cli_session_setup_guest(struct cli_state *cli) { TALLOC_CTX *frame = talloc_stackframe(); struct event_context *ev; - struct async_req *req; - NTSTATUS status; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_OK; - if (cli->fd_event != NULL) { + if (cli_has_async_calls(cli)) { /* * Can't use sync call while an async call is in flight */ @@ -276,13 +302,17 @@ static NTSTATUS cli_session_setup_guest(struct cli_state *cli) goto fail; } - while (req->state < ASYNC_REQ_DONE) { - event_loop_once(ev); + if (!tevent_req_poll(req, ev)) { + status = map_nt_error_from_unix(errno); + goto fail; } status = cli_session_setup_guest_recv(req); fail: TALLOC_FREE(frame); + if (!NT_STATUS_IS_OK(status)) { + cli_set_error(cli, status); + } return status; } @@ -1189,18 +1219,33 @@ bool cli_ulogoff(struct cli_state *cli) Send a tconX. ****************************************************************************/ -struct async_req *cli_tcon_andx_send(TALLOC_CTX *mem_ctx, - struct event_context *ev, - struct cli_state *cli, - const char *share, const char *dev, - const char *pass, int passlen) +struct cli_tcon_andx_state { + struct cli_state *cli; + uint16_t vwv[4]; +}; + +static void cli_tcon_andx_done(struct tevent_req *subreq); + +struct tevent_req *cli_tcon_andx_send(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct cli_state *cli, + const char *share, const char *dev, + const char *pass, int passlen) { + struct tevent_req *req, *subreq; + struct cli_tcon_andx_state *state; fstring pword; + uint16_t *vwv; char *tmp = NULL; - struct async_req *result; - uint16_t vwv[4]; uint8_t *bytes; + req = tevent_req_create(mem_ctx, &state, struct cli_tcon_andx_state); + if (req == NULL) { + return NULL; + } + state->cli = cli; + vwv = state->vwv; + fstrcpy(cli->share, share); /* in user level security don't send a password now */ @@ -1263,9 +1308,9 @@ struct async_req *cli_tcon_andx_send(TALLOC_CTX *mem_ctx, SSVAL(vwv+3, 0, passlen); if (passlen) { - bytes = (uint8_t *)talloc_memdup(talloc_tos(), pword, passlen); + bytes = (uint8_t *)talloc_memdup(state, pword, passlen); } else { - bytes = talloc_array(talloc_tos(), uint8_t, 0); + bytes = talloc_array(state, uint8_t, 0); } /* @@ -1273,9 +1318,8 @@ struct async_req *cli_tcon_andx_send(TALLOC_CTX *mem_ctx, */ tmp = talloc_asprintf_strupper_m(talloc_tos(), "\\\\%s\\%s", cli->desthost, share); - if (tmp == NULL) { - TALLOC_FREE(bytes); - return NULL; + if (tevent_req_nomem(tmp, req)) { + return tevent_req_post(req, ev); } bytes = smb_bytes_push_str(bytes, cli_ucs2(cli), tmp, strlen(tmp)+1, NULL); @@ -1285,60 +1329,52 @@ struct async_req *cli_tcon_andx_send(TALLOC_CTX *mem_ctx, * Add the devicetype */ tmp = talloc_strdup_upper(talloc_tos(), dev); - if (tmp == NULL) { - TALLOC_FREE(bytes); - return NULL; + if (tevent_req_nomem(tmp, req)) { + return tevent_req_post(req, ev); } bytes = smb_bytes_push_str(bytes, false, tmp, strlen(tmp)+1, NULL); TALLOC_FREE(tmp); - if (bytes == NULL) { - return NULL; + if (tevent_req_nomem(bytes, req)) { + return tevent_req_post(req, ev); } - result = cli_request_send(mem_ctx, ev, cli, SMBtconX, 0, - 4, vwv, 0, talloc_get_size(bytes), bytes); - TALLOC_FREE(bytes); - return result; + subreq = cli_smb_send(state, ev, cli, SMBtconX, 0, 4, vwv, + talloc_get_size(bytes), bytes); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_tcon_andx_done, req); + return req; access_denied: - { - struct cli_request *state; - if (!async_req_setup(mem_ctx, &result, &state, - struct cli_request)) { - goto fail; - } - if (async_post_ntstatus(result, ev, NT_STATUS_ACCESS_DENIED)) { - return result; - } - } - fail: - TALLOC_FREE(result); - return NULL; + tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED); + return tevent_req_post(req, ev); } -NTSTATUS cli_tcon_andx_recv(struct async_req *req) +static void cli_tcon_andx_done(struct tevent_req *subreq) { - struct cli_request *cli_req = talloc_get_type_abort( - req->private_data, struct cli_request); - struct cli_state *cli = cli_req->cli; + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_tcon_andx_state *state = tevent_req_data( + req, struct cli_tcon_andx_state); + struct cli_state *cli = state->cli; + char *inbuf = (char *)cli_smb_inbuf(subreq); uint8_t wct; uint16_t *vwv; - uint16_t num_bytes; + uint32_t num_bytes; uint8_t *bytes; NTSTATUS status; - if (async_req_is_nterror(req, &status)) { - return status; - } - - status = cli_pull_reply(req, &wct, &vwv, &num_bytes, &bytes); + status = cli_smb_recv(subreq, 0, &wct, &vwv, &num_bytes, &bytes); if (!NT_STATUS_IS_OK(status)) { - return status; + TALLOC_FREE(subreq); + tevent_req_nterror(req, status); + return; } - clistr_pull(cli_req->inbuf, cli->dev, bytes, sizeof(fstring), - num_bytes, STR_TERMINATE|STR_ASCII); + clistr_pull(inbuf, cli->dev, bytes, sizeof(fstring), num_bytes, + STR_TERMINATE|STR_ASCII); if ((cli->protocol >= PROTOCOL_NT1) && (num_bytes == 3)) { /* almost certainly win95 - enable bug fixes */ @@ -1356,8 +1392,13 @@ NTSTATUS cli_tcon_andx_recv(struct async_req *req) cli->dfsroot = ((SVAL(vwv+2, 0) & SMB_SHARE_IN_DFS) != 0); } - cli->cnum = SVAL(cli_req->inbuf,smb_tid); - return NT_STATUS_OK; + cli->cnum = SVAL(inbuf,smb_tid); + tevent_req_done(req); +} + +NTSTATUS cli_tcon_andx_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); } NTSTATUS cli_tcon_andx(struct cli_state *cli, const char *share, @@ -1365,10 +1406,10 @@ NTSTATUS cli_tcon_andx(struct cli_state *cli, const char *share, { TALLOC_CTX *frame = talloc_stackframe(); struct event_context *ev; - struct async_req *req; - NTSTATUS status; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_OK; - if (cli->fd_event != NULL) { + if (cli_has_async_calls(cli)) { /* * Can't use sync call while an async call is in flight */ @@ -1388,13 +1429,17 @@ NTSTATUS cli_tcon_andx(struct cli_state *cli, const char *share, goto fail; } - while (req->state < ASYNC_REQ_DONE) { - event_loop_once(ev); + if (!tevent_req_poll(req, ev)) { + status = map_nt_error_from_unix(errno); + goto fail; } status = cli_tcon_andx_recv(req); fail: TALLOC_FREE(frame); + if (!NT_STATUS_IS_OK(status)) { + cli_set_error(cli, status); + } return status; } @@ -1461,14 +1506,27 @@ void cli_negprot_sendsync(struct cli_state *cli) Send a negprot command. ****************************************************************************/ -struct async_req *cli_negprot_send(TALLOC_CTX *mem_ctx, - struct event_context *ev, - struct cli_state *cli) +struct cli_negprot_state { + struct cli_state *cli; +}; + +static void cli_negprot_done(struct tevent_req *subreq); + +struct tevent_req *cli_negprot_send(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct cli_state *cli) { - struct async_req *result; + struct tevent_req *req, *subreq; + struct cli_negprot_state *state; uint8_t *bytes = NULL; int numprots; + req = tevent_req_create(mem_ctx, &state, struct cli_negprot_state); + if (req == NULL) { + return NULL; + } + state->cli = cli; + if (cli->protocol < PROTOCOL_NT1) cli->use_spnego = False; @@ -1479,51 +1537,54 @@ struct async_req *cli_negprot_send(TALLOC_CTX *mem_ctx, break; } bytes = (uint8_t *)talloc_append_blob( - talloc_tos(), bytes, data_blob_const(&c, sizeof(c))); - if (bytes == NULL) { - return NULL; + state, bytes, data_blob_const(&c, sizeof(c))); + if (tevent_req_nomem(bytes, req)) { + return tevent_req_post(req, ev); } bytes = smb_bytes_push_str(bytes, false, prots[numprots].name, strlen(prots[numprots].name)+1, NULL); - if (bytes == NULL) { - return NULL; + if (tevent_req_nomem(bytes, req)) { + return tevent_req_post(req, ev); } } - result = cli_request_send(mem_ctx, ev, cli, SMBnegprot, 0, 0, NULL, 0, - talloc_get_size(bytes), bytes); - TALLOC_FREE(bytes); - return result; + subreq = cli_smb_send(state, ev, cli, SMBnegprot, 0, 0, NULL, + talloc_get_size(bytes), bytes); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_negprot_done, req); + return req; } -NTSTATUS cli_negprot_recv(struct async_req *req) +static void cli_negprot_done(struct tevent_req *subreq) { - struct cli_request *cli_req = talloc_get_type_abort( - req->private_data, struct cli_request); - struct cli_state *cli = cli_req->cli; + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_negprot_state *state = tevent_req_data( + req, struct cli_negprot_state); + struct cli_state *cli = state->cli; uint8_t wct; uint16_t *vwv; - uint16_t num_bytes; + uint32_t num_bytes; uint8_t *bytes; NTSTATUS status; uint16_t protnum; - if (async_req_is_nterror(req, &status)) { - return status; - } - - status = cli_pull_reply(req, &wct, &vwv, &num_bytes, &bytes); + status = cli_smb_recv(subreq, 1, &wct, &vwv, &num_bytes, &bytes); if (!NT_STATUS_IS_OK(status)) { - return status; + TALLOC_FREE(subreq); + return; } protnum = SVAL(vwv, 0); if ((protnum >= ARRAY_SIZE(prots)) - || (prots[protnum].prot > cli_req->cli->protocol)) { - return NT_STATUS_INVALID_NETWORK_RESPONSE; + || (prots[protnum].prot > cli->protocol)) { + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; } cli->protocol = prots[protnum].prot; @@ -1531,7 +1592,8 @@ NTSTATUS cli_negprot_recv(struct async_req *req) if ((cli->protocol < PROTOCOL_NT1) && client_is_signing_mandatory(cli)) { DEBUG(0,("cli_negprot: SMB signing is mandatory and the selected protocol level doesn't support it.\n")); - return NT_STATUS_ACCESS_DENIED; + tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED); + return; } if (cli->protocol >= PROTOCOL_NT1) { @@ -1572,14 +1634,18 @@ NTSTATUS cli_negprot_recv(struct async_req *req) /* Fail if server says signing is mandatory and we don't want to support it. */ if (!client_is_signing_allowed(cli)) { DEBUG(0,("cli_negprot: SMB signing is mandatory and we have disabled it.\n")); - return NT_STATUS_ACCESS_DENIED; + tevent_req_nterror(req, + NT_STATUS_ACCESS_DENIED); + return; } negotiated_smb_signing = true; } else if (client_is_signing_mandatory(cli) && client_is_signing_allowed(cli)) { /* Fail if client says signing is mandatory and the server doesn't support it. */ if (!(cli->sec_mode & NEGOTIATE_SECURITY_SIGNATURES_ENABLED)) { DEBUG(1,("cli_negprot: SMB signing is mandatory and the server doesn't support it.\n")); - return NT_STATUS_ACCESS_DENIED; + tevent_req_nterror(req, + NT_STATUS_ACCESS_DENIED); + return; } negotiated_smb_signing = true; } else if (cli->sec_mode & NEGOTIATE_SECURITY_SIGNATURES_ENABLED) { @@ -1625,41 +1691,52 @@ NTSTATUS cli_negprot_recv(struct async_req *req) if (getenv("CLI_FORCE_ASCII")) cli->capabilities &= ~CAP_UNICODE; - return NT_STATUS_OK; + tevent_req_done(req); +} + +NTSTATUS cli_negprot_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); } NTSTATUS cli_negprot(struct cli_state *cli) { TALLOC_CTX *frame = talloc_stackframe(); struct event_context *ev; - struct async_req *req; - NTSTATUS status = NT_STATUS_NO_MEMORY; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_OK; - if (cli->fd_event != NULL) { + if (cli_has_async_calls(cli)) { /* * Can't use sync call while an async call is in flight */ - cli_set_error(cli, NT_STATUS_INVALID_PARAMETER); + status = NT_STATUS_INVALID_PARAMETER; goto fail; } ev = event_context_init(frame); if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; goto fail; } req = cli_negprot_send(frame, ev, cli); if (req == NULL) { + status = NT_STATUS_NO_MEMORY; goto fail; } - while (req->state < ASYNC_REQ_DONE) { - event_loop_once(ev); + if (!tevent_req_poll(req, ev)) { + status = map_nt_error_from_unix(errno); + goto fail; } status = cli_negprot_recv(req); fail: TALLOC_FREE(frame); + if (!NT_STATUS_IS_OK(status)) { + cli_set_error(cli, status); + } return status; } @@ -2092,6 +2169,10 @@ NTSTATUS cli_full_connection(struct cli_state **output_cli, return nt_status; } + cli->use_oplocks = ((flags & CLI_FULL_CONNECTION_OPLOCKS) != 0); + cli->use_level_II_oplocks = + ((flags & CLI_FULL_CONNECTION_LEVEL_II_OPLOCKS) != 0); + nt_status = cli_session_setup(cli, user, password, pw_len, password, pw_len, domain); if (!NT_STATUS_IS_OK(nt_status)) { diff --git a/source3/libsmb/clientgen.c b/source3/libsmb/clientgen.c index 6186387076..45addc2163 100644 --- a/source3/libsmb/clientgen.c +++ b/source3/libsmb/clientgen.c @@ -650,6 +650,12 @@ struct cli_state *cli_initialise_ex(int signing_state) goto error; } + cli->outgoing = tevent_queue_create(cli, "cli_outgoing"); + if (cli->outgoing == NULL) { + goto error; + } + cli->pending = NULL; + cli->initialised = 1; return cli; @@ -740,6 +746,12 @@ void cli_shutdown(struct cli_state *cli) cli->fd = -1; cli->smb_rw_error = SMB_READ_OK; + /* + * Need to free pending first, they remove themselves + */ + while (cli->pending) { + talloc_free(cli->pending[0]); + } TALLOC_FREE(cli); } @@ -793,90 +805,72 @@ bool cli_send_keepalive(struct cli_state *cli) return true; } -/** - * @brief: Collect a echo reply - * @param[in] req The corresponding async request - * - * There might be more than one echo reply. This helper pulls the reply out of - * the data stream. If all expected replies have arrived, declare the - * async_req done. - */ - -static void cli_echo_recv_helper(struct async_req *req) -{ - struct cli_request *cli_req; - uint8_t wct; - uint16_t *vwv; - uint16_t num_bytes; - uint8_t *bytes; - NTSTATUS status; +struct cli_echo_state { + uint16_t vwv[1]; + DATA_BLOB data; + int num_echos; +}; - status = cli_pull_reply(req, &wct, &vwv, &num_bytes, &bytes); - if (!NT_STATUS_IS_OK(status)) { - async_req_nterror(req, status); - return; - } +static void cli_echo_done(struct tevent_req *subreq); - cli_req = talloc_get_type_abort(req->private_data, struct cli_request); +struct tevent_req *cli_echo_send(TALLOC_CTX *mem_ctx, struct event_context *ev, + struct cli_state *cli, uint16_t num_echos, + DATA_BLOB data) +{ + struct tevent_req *req, *subreq; + struct cli_echo_state *state; - if ((num_bytes != cli_req->data.echo.data.length) - || (memcmp(cli_req->data.echo.data.data, bytes, - num_bytes) != 0)) { - async_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); - return; + req = tevent_req_create(mem_ctx, &state, struct cli_echo_state); + if (req == NULL) { + return NULL; } + SSVAL(state->vwv, 0, num_echos); + state->data = data; + state->num_echos = num_echos; - cli_req->data.echo.num_echos -= 1; - - if (cli_req->data.echo.num_echos == 0) { - async_req_done(req); - return; + subreq = cli_smb_send(state, ev, cli, SMBecho, 0, 1, state->vwv, + data.length, data.data); + if (subreq == NULL) { + goto fail; } - - return; + tevent_req_set_callback(subreq, cli_echo_done, req); + return req; + fail: + TALLOC_FREE(req); + return NULL; } -/** - * @brief Send SMBEcho requests - * @param[in] mem_ctx The memory context to put the async_req on - * @param[in] ev The event context that will call us back - * @param[in] cli The connection to send the echo to - * @param[in] num_echos How many times do we want to get the reply? - * @param[in] data The data we want to get back - * @retval The async request - */ - -struct async_req *cli_echo_send(TALLOC_CTX *mem_ctx, struct event_context *ev, - struct cli_state *cli, uint16_t num_echos, - DATA_BLOB data) +static void cli_echo_done(struct tevent_req *subreq) { - uint16_t vwv[1]; - uint8_t *data_copy; - struct async_req *result; - struct cli_request *req; - - SSVAL(vwv, 0, num_echos); + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_echo_state *state = tevent_req_data( + req, struct cli_echo_state); + NTSTATUS status; + uint32_t num_bytes; + uint8_t *bytes; - data_copy = (uint8_t *)talloc_memdup(mem_ctx, data.data, data.length); - if (data_copy == NULL) { - return NULL; + status = cli_smb_recv(subreq, 0, NULL, NULL, &num_bytes, &bytes); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; } - - result = cli_request_send(mem_ctx, ev, cli, SMBecho, 0, 1, vwv, 0, - data.length, data.data); - if (result == NULL) { - TALLOC_FREE(data_copy); - return NULL; + if ((num_bytes != state->data.length) + || (memcmp(bytes, state->data.data, num_bytes) != 0)) { + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; } - req = talloc_get_type_abort(result->private_data, struct cli_request); - req->data.echo.num_echos = num_echos; - req->data.echo.data.data = talloc_move(req, &data_copy); - req->data.echo.data.length = data.length; - - req->recv_helper.fn = cli_echo_recv_helper; + state->num_echos -=1; + if (state->num_echos == 0) { + tevent_req_done(req); + return; + } - return result; + if (!cli_smb_req_set_pending(subreq)) { + tevent_req_nterror(req, NT_STATUS_NO_MEMORY); + return; + } } /** @@ -885,9 +879,9 @@ struct async_req *cli_echo_send(TALLOC_CTX *mem_ctx, struct event_context *ev, * @retval Did the server reply correctly? */ -NTSTATUS cli_echo_recv(struct async_req *req) +NTSTATUS cli_echo_recv(struct tevent_req *req) { - return async_req_simple_recv_ntstatus(req); + return tevent_req_simple_recv_ntstatus(req); } /** @@ -904,35 +898,40 @@ NTSTATUS cli_echo(struct cli_state *cli, uint16_t num_echos, DATA_BLOB data) { TALLOC_CTX *frame = talloc_stackframe(); struct event_context *ev; - struct async_req *req; - NTSTATUS status = NT_STATUS_NO_MEMORY; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_OK; - if (cli->fd_event != NULL) { + if (cli_has_async_calls(cli)) { /* * Can't use sync call while an async call is in flight */ - cli_set_error(cli, NT_STATUS_INVALID_PARAMETER); + status = NT_STATUS_INVALID_PARAMETER; goto fail; } ev = event_context_init(frame); if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; goto fail; } req = cli_echo_send(frame, ev, cli, num_echos, data); if (req == NULL) { + status = NT_STATUS_NO_MEMORY; goto fail; } - while (req->state < ASYNC_REQ_DONE) { - event_loop_once(ev); + if (!tevent_req_poll(req, ev)) { + status = map_nt_error_from_unix(errno); + goto fail; } status = cli_echo_recv(req); - fail: TALLOC_FREE(frame); + if (!NT_STATUS_IS_OK(status)) { + cli_set_error(cli, status); + } return status; } diff --git a/source3/libsmb/clifile.c b/source3/libsmb/clifile.c index 0703f04c5f..4293828214 100644 --- a/source3/libsmb/clifile.c +++ b/source3/libsmb/clifile.c @@ -771,22 +771,36 @@ int cli_nt_create_full(struct cli_state *cli, const char *fname, return SVAL(cli->inbuf,smb_vwv2 + 1); } -struct async_req *cli_ntcreate_send(TALLOC_CTX *mem_ctx, - struct event_context *ev, - struct cli_state *cli, - const char *fname, - uint32_t CreatFlags, - uint32_t DesiredAccess, - uint32_t FileAttributes, - uint32_t ShareAccess, - uint32_t CreateDisposition, - uint32_t CreateOptions, - uint8_t SecurityFlags) -{ - struct async_req *result; +struct cli_ntcreate_state { + uint16_t vwv[24]; + uint16_t fnum; +}; + +static void cli_ntcreate_done(struct tevent_req *subreq); + +struct tevent_req *cli_ntcreate_send(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct cli_state *cli, + const char *fname, + uint32_t CreatFlags, + uint32_t DesiredAccess, + uint32_t FileAttributes, + uint32_t ShareAccess, + uint32_t CreateDisposition, + uint32_t CreateOptions, + uint8_t SecurityFlags) +{ + struct tevent_req *req, *subreq; + struct cli_ntcreate_state *state; + uint16_t *vwv; uint8_t *bytes; size_t converted_len; - uint16_t vwv[24]; + + req = tevent_req_create(mem_ctx, &state, struct cli_ntcreate_state); + if (req == NULL) { + return NULL; + } + vwv = state->vwv; SCVAL(vwv+0, 0, 0xFF); SCVAL(vwv+0, 1, 0); @@ -808,7 +822,7 @@ struct async_req *cli_ntcreate_send(TALLOC_CTX *mem_ctx, SIVAL(vwv+21, 1, 0x02); /* ImpersonationLevel */ SCVAL(vwv+23, 1, SecurityFlags); - bytes = talloc_array(talloc_tos(), uint8_t, 0); + bytes = talloc_array(state, uint8_t, 0); bytes = smb_bytes_push_str(bytes, cli_ucs2(cli), fname, strlen(fname)+1, &converted_len); @@ -816,41 +830,53 @@ struct async_req *cli_ntcreate_send(TALLOC_CTX *mem_ctx, /* sigh. this copes with broken netapp filer behaviour */ bytes = smb_bytes_push_str(bytes, cli_ucs2(cli), "", 1, NULL); - if (bytes == NULL) { - return NULL; + if (tevent_req_nomem(bytes, req)) { + return tevent_req_post(req, ev); } SIVAL(vwv+2, 1, converted_len); - result = cli_request_send(mem_ctx, ev, cli, SMBntcreateX, 0, - 24, vwv, 0, talloc_get_size(bytes), bytes); - TALLOC_FREE(bytes); - return result; + subreq = cli_smb_send(state, ev, cli, SMBntcreateX, 0, 24, vwv, + talloc_get_size(bytes), bytes); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_ntcreate_done, req); + return req; } -NTSTATUS cli_ntcreate_recv(struct async_req *req, uint16_t *pfnum) +static void cli_ntcreate_done(struct tevent_req *subreq) { + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_ntcreate_state *state = tevent_req_data( + req, struct cli_ntcreate_state); uint8_t wct; uint16_t *vwv; - uint16_t num_bytes; + uint32_t num_bytes; uint8_t *bytes; NTSTATUS status; - if (async_req_is_nterror(req, &status)) { - return status; - } - - status = cli_pull_reply(req, &wct, &vwv, &num_bytes, &bytes); + status = cli_smb_recv(subreq, 3, &wct, &vwv, &num_bytes, &bytes); if (!NT_STATUS_IS_OK(status)) { - return status; - } - - if (wct < 3) { - return NT_STATUS_INVALID_NETWORK_RESPONSE; + TALLOC_FREE(subreq); + tevent_req_nterror(req, status); + return; } + state->fnum = SVAL(vwv+2, 1); + tevent_req_done(req); +} - *pfnum = SVAL(vwv+2, 1); +NTSTATUS cli_ntcreate_recv(struct tevent_req *req, uint16_t *pfnum) +{ + struct cli_ntcreate_state *state = tevent_req_data( + req, struct cli_ntcreate_state); + NTSTATUS status; + if (tevent_req_is_nterror(req, &status)) { + return status; + } + *pfnum = state->fnum; return NT_STATUS_OK; } @@ -867,10 +893,10 @@ NTSTATUS cli_ntcreate(struct cli_state *cli, { TALLOC_CTX *frame = talloc_stackframe(); struct event_context *ev; - struct async_req *req; - NTSTATUS status; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_OK; - if (cli->fd_event != NULL) { + if (cli_has_async_calls(cli)) { /* * Can't use sync call while an async call is in flight */ @@ -893,13 +919,17 @@ NTSTATUS cli_ntcreate(struct cli_state *cli, goto fail; } - while (req->state < ASYNC_REQ_DONE) { - event_loop_once(ev); + if (!tevent_req_poll(req, ev)) { + status = map_nt_error_from_unix(errno); + goto fail; } status = cli_ntcreate_recv(req, pfid); fail: TALLOC_FREE(frame); + if (!NT_STATUS_IS_OK(status)) { + cli_set_error(cli, status); + } return status; } @@ -968,17 +998,33 @@ uint8_t *smb_bytes_push_str(uint8_t *buf, bool ucs2, WARNING: if you open with O_WRONLY then getattrE won't work! ****************************************************************************/ -struct async_req *cli_open_send(TALLOC_CTX *mem_ctx, struct event_context *ev, - struct cli_state *cli, - const char *fname, int flags, int share_mode) +struct cli_open_state { + uint16_t vwv[15]; + int fnum; + struct iovec bytes; +}; + +static void cli_open_done(struct tevent_req *subreq); + +struct tevent_req *cli_open_create(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct cli_state *cli, const char *fname, + int flags, int share_mode, + struct tevent_req **psmbreq) { - unsigned openfn = 0; - unsigned accessmode = 0; - uint8_t additional_flags = 0; + struct tevent_req *req, *subreq; + struct cli_open_state *state; + unsigned openfn; + unsigned accessmode; + uint8_t additional_flags; uint8_t *bytes; - uint16_t vwv[15]; - struct async_req *result; + req = tevent_req_create(mem_ctx, &state, struct cli_open_state); + if (req == NULL) { + return NULL; + } + + openfn = 0; if (flags & O_CREAT) { openfn |= (1<<4); } @@ -1007,63 +1053,96 @@ struct async_req *cli_open_send(TALLOC_CTX *mem_ctx, struct event_context *ev, accessmode = 0xFF; } - SCVAL(vwv + 0, 0, 0xFF); - SCVAL(vwv + 0, 1, 0); - SSVAL(vwv + 1, 0, 0); - SSVAL(vwv + 2, 0, 0); /* no additional info */ - SSVAL(vwv + 3, 0, accessmode); - SSVAL(vwv + 4, 0, aSYSTEM | aHIDDEN); - SSVAL(vwv + 5, 0, 0); - SIVAL(vwv + 6, 0, 0); - SSVAL(vwv + 8, 0, openfn); - SIVAL(vwv + 9, 0, 0); - SIVAL(vwv + 11, 0, 0); - SIVAL(vwv + 13, 0, 0); + SCVAL(state->vwv + 0, 0, 0xFF); + SCVAL(state->vwv + 0, 1, 0); + SSVAL(state->vwv + 1, 0, 0); + SSVAL(state->vwv + 2, 0, 0); /* no additional info */ + SSVAL(state->vwv + 3, 0, accessmode); + SSVAL(state->vwv + 4, 0, aSYSTEM | aHIDDEN); + SSVAL(state->vwv + 5, 0, 0); + SIVAL(state->vwv + 6, 0, 0); + SSVAL(state->vwv + 8, 0, openfn); + SIVAL(state->vwv + 9, 0, 0); + SIVAL(state->vwv + 11, 0, 0); + SIVAL(state->vwv + 13, 0, 0); + + additional_flags = 0; if (cli->use_oplocks) { /* if using oplocks then ask for a batch oplock via core and extended methods */ additional_flags = FLAG_REQUEST_OPLOCK|FLAG_REQUEST_BATCH_OPLOCK; - SSVAL(vwv+2, 0, SVAL(vwv+2, 0) | 6); + SSVAL(state->vwv+2, 0, SVAL(state->vwv+2, 0) | 6); } - bytes = talloc_array(talloc_tos(), uint8_t, 0); + bytes = talloc_array(state, uint8_t, 0); bytes = smb_bytes_push_str(bytes, cli_ucs2(cli), fname, strlen(fname)+1, NULL); - if (bytes == NULL) { + + if (tevent_req_nomem(bytes, req)) { + return tevent_req_post(req, ev); + } + + state->bytes.iov_base = bytes; + state->bytes.iov_len = talloc_get_size(bytes); + + subreq = cli_smb_req_create(state, ev, cli, SMBopenX, additional_flags, + 15, state->vwv, 1, &state->bytes); + if (subreq == NULL) { + TALLOC_FREE(req); return NULL; } + tevent_req_set_callback(subreq, cli_open_done, req); + *psmbreq = subreq; + return req; +} - result = cli_request_send(mem_ctx, ev, cli, SMBopenX, additional_flags, - 15, vwv, 0, talloc_get_size(bytes), bytes); - TALLOC_FREE(bytes); - return result; +struct tevent_req *cli_open_send(TALLOC_CTX *mem_ctx, struct event_context *ev, + struct cli_state *cli, const char *fname, + int flags, int share_mode) +{ + struct tevent_req *req, *subreq; + + req = cli_open_create(mem_ctx, ev, cli, fname, flags, share_mode, + &subreq); + if ((req == NULL) || !cli_smb_req_send(subreq)) { + TALLOC_FREE(req); + return NULL; + } + return req; } -NTSTATUS cli_open_recv(struct async_req *req, int *fnum) +static void cli_open_done(struct tevent_req *subreq) { + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_open_state *state = tevent_req_data( + req, struct cli_open_state); uint8_t wct; uint16_t *vwv; - uint16_t num_bytes; - uint8_t *bytes; NTSTATUS status; - if (async_req_is_nterror(req, &status)) { - return status; - } - - status = cli_pull_reply(req, &wct, &vwv, &num_bytes, &bytes); + status = cli_smb_recv(subreq, 3, &wct, &vwv, NULL, NULL); if (!NT_STATUS_IS_OK(status)) { - return status; - } - - if (wct < 3) { - return NT_STATUS_INVALID_NETWORK_RESPONSE; + TALLOC_FREE(subreq); + tevent_req_nterror(req, status); + return; } + state->fnum = SVAL(vwv+2, 0); + tevent_req_done(req); +} - *fnum = SVAL(vwv+2, 0); +NTSTATUS cli_open_recv(struct tevent_req *req, int *fnum) +{ + struct cli_open_state *state = tevent_req_data( + req, struct cli_open_state); + NTSTATUS status; + if (tevent_req_is_nterror(req, &status)) { + return status; + } + *fnum = state->fnum; return NT_STATUS_OK; } @@ -1072,34 +1151,41 @@ int cli_open(struct cli_state *cli, const char *fname, int flags, { TALLOC_CTX *frame = talloc_stackframe(); struct event_context *ev; - struct async_req *req; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_OK; int result = -1; - if (cli->fd_event != NULL) { + if (cli_has_async_calls(cli)) { /* * Can't use sync call while an async call is in flight */ - cli_set_error(cli, NT_STATUS_INVALID_PARAMETER); + status = NT_STATUS_INVALID_PARAMETER; goto fail; } ev = event_context_init(frame); if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; goto fail; } req = cli_open_send(frame, ev, cli, fname, flags, share_mode); if (req == NULL) { + status = NT_STATUS_NO_MEMORY; goto fail; } - while (req->state < ASYNC_REQ_DONE) { - event_loop_once(ev); + if (!tevent_req_poll(req, ev)) { + status = map_nt_error_from_unix(errno); + goto fail; } cli_open_recv(req, &result); fail: TALLOC_FREE(frame); + if (!NT_STATUS_IS_OK(status)) { + cli_set_error(cli, status); + } return result; } @@ -1107,65 +1193,111 @@ int cli_open(struct cli_state *cli, const char *fname, int flags, Close a file. ****************************************************************************/ -struct async_req *cli_close_send(TALLOC_CTX *mem_ctx, struct event_context *ev, - struct cli_state *cli, int fnum) -{ +struct cli_close_state { uint16_t vwv[3]; +}; + +static void cli_close_done(struct tevent_req *subreq); + +struct tevent_req *cli_close_create(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct cli_state *cli, int fnum, + struct tevent_req **psubreq) +{ + struct tevent_req *req, *subreq; + struct cli_close_state *state; + + req = tevent_req_create(mem_ctx, &state, struct cli_close_state); + if (req == NULL) { + return NULL; + } + SSVAL(state->vwv+0, 0, fnum); + SIVALS(state->vwv+1, 0, -1); + + subreq = cli_smb_req_create(state, ev, cli, SMBclose, 0, 3, state->vwv, + 0, NULL); + if (subreq == NULL) { + TALLOC_FREE(req); + return NULL; + } + tevent_req_set_callback(subreq, cli_close_done, req); + *psubreq = subreq; + return req; +} - SSVAL(vwv+0, 0, fnum); - SIVALS(vwv+1, 0, -1); +struct tevent_req *cli_close_send(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct cli_state *cli, int fnum) +{ + struct tevent_req *req, *subreq; - return cli_request_send(mem_ctx, ev, cli, SMBclose, 0, 3, vwv, 0, - 0, NULL); + req = cli_close_create(mem_ctx, ev, cli, fnum, &subreq); + if ((req == NULL) || !cli_smb_req_send(subreq)) { + TALLOC_FREE(req); + return NULL; + } + return req; } -NTSTATUS cli_close_recv(struct async_req *req) +static void cli_close_done(struct tevent_req *subreq) { - uint8_t wct; - uint16_t *vwv; - uint16_t num_bytes; - uint8_t *bytes; + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); NTSTATUS status; - if (async_req_is_nterror(req, &status)) { - return status; + status = cli_smb_recv(subreq, 0, NULL, NULL, NULL, NULL); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; } + tevent_req_done(req); +} - return cli_pull_reply(req, &wct, &vwv, &num_bytes, &bytes); +NTSTATUS cli_close_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); } bool cli_close(struct cli_state *cli, int fnum) { TALLOC_CTX *frame = talloc_stackframe(); struct event_context *ev; - struct async_req *req; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_OK; bool result = false; - if (cli->fd_event != NULL) { + if (cli_has_async_calls(cli)) { /* * Can't use sync call while an async call is in flight */ - cli_set_error(cli, NT_STATUS_INVALID_PARAMETER); + status = NT_STATUS_INVALID_PARAMETER; goto fail; } ev = event_context_init(frame); if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; goto fail; } req = cli_close_send(frame, ev, cli, fnum); if (req == NULL) { + status = NT_STATUS_NO_MEMORY; goto fail; } - while (req->state < ASYNC_REQ_DONE) { - event_loop_once(ev); + if (!tevent_req_poll(req, ev)) { + status = map_nt_error_from_unix(errno); + goto fail; } result = NT_STATUS_IS_OK(cli_close_recv(req)); fail: TALLOC_FREE(frame); + if (!NT_STATUS_IS_OK(status)) { + cli_set_error(cli, status); + } return result; } diff --git a/source3/libsmb/clikrb5.c b/source3/libsmb/clikrb5.c index 4ab31374e2..aa98d7668f 100644 --- a/source3/libsmb/clikrb5.c +++ b/source3/libsmb/clikrb5.c @@ -56,12 +56,12 @@ static krb5_error_code ads_krb5_get_fwd_ticket( krb5_context context, char *utf8_name; size_t converted_size; - if (!push_utf8_allocate(&utf8_name, name, &converted_size)) { + if (!push_utf8_talloc(talloc_tos(), &utf8_name, name, &converted_size)) { return ENOMEM; } ret = krb5_parse_name(context, utf8_name, principal); - SAFE_FREE(utf8_name); + TALLOC_FREE(utf8_name); return ret; } @@ -79,24 +79,25 @@ static krb5_error_code smb_krb5_parse_name_norealm_conv(krb5_context context, size_t converted_size; *principal = NULL; - if (!push_utf8_allocate(&utf8_name, name, &converted_size)) { + if (!push_utf8_talloc(talloc_tos(), &utf8_name, name, &converted_size)) { return ENOMEM; } ret = krb5_parse_name_norealm(context, utf8_name, principal); - SAFE_FREE(utf8_name); + TALLOC_FREE(utf8_name); return ret; } #endif /************************************************************** krb5_parse_name that returns a UNIX charset name. Must - be freed with normal free() call. + be freed with talloc_free() call. **************************************************************/ - krb5_error_code smb_krb5_unparse_name(krb5_context context, - krb5_const_principal principal, - char **unix_name) +krb5_error_code smb_krb5_unparse_name(TALLOC_CTX *mem_ctx, + krb5_context context, + krb5_const_principal principal, + char **unix_name) { krb5_error_code ret; char *utf8_name; @@ -108,7 +109,7 @@ static krb5_error_code smb_krb5_parse_name_norealm_conv(krb5_context context, return ret; } - if (!pull_utf8_allocate(unix_name, utf8_name, &converted_size)) { + if (!pull_utf8_talloc(mem_ctx, unix_name, utf8_name, &converted_size)) { krb5_free_unparsed_name(context, utf8_name); return ENOMEM; } @@ -1081,10 +1082,10 @@ get_key_from_keytab(krb5_context context, } if ( DEBUGLEVEL >= 10 ) { - if (smb_krb5_unparse_name(context, server, &name) == 0) { + if (smb_krb5_unparse_name(talloc_tos(), context, server, &name) == 0) { DEBUG(10,("get_key_from_keytab: will look for kvno %d, enctype %d and name: %s\n", kvno, enctype, name)); - SAFE_FREE(name); + TALLOC_FREE(name); } } diff --git a/source3/libsmb/clioplock.c b/source3/libsmb/clioplock.c index ef8b396461..e3fb66aba0 100644 --- a/source3/libsmb/clioplock.c +++ b/source3/libsmb/clioplock.c @@ -23,38 +23,103 @@ send an ack for an oplock break request ****************************************************************************/ -bool cli_oplock_ack(struct cli_state *cli, int fnum, unsigned char level) +struct cli_oplock_ack_state { + uint16_t vwv[8]; +}; + +static void cli_oplock_ack_done(struct tevent_req *subreq); + +struct tevent_req *cli_oplock_ack_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, uint8_t level) +{ + struct tevent_req *req, *subreq; + struct cli_oplock_ack_state *state; + + req = tevent_req_create(mem_ctx, &state, struct cli_oplock_ack_state);; + if (req == NULL) { + return NULL; + } + SCVAL(state->vwv+0, 0, 0xff); + SCVAL(state->vwv+0, 1, 0); + SSVAL(state->vwv+1, 0, 0); + SSVAL(state->vwv+2, 0, fnum); + SCVAL(state->vwv+3, 0, LOCKING_ANDX_OPLOCK_RELEASE); + SCVAL(state->vwv+3, 1, level); + SIVAL(state->vwv+4, 0, 0); /* timeout */ + SSVAL(state->vwv+6, 0, 0); /* unlockcount */ + SSVAL(state->vwv+7, 0, 0); /* lockcount */ + + subreq = cli_smb_send(state, ev, cli, SMBlockingX, 0, 8, state->vwv, + 0, NULL); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_oplock_ack_done, req); + return req; +} + +static void cli_oplock_ack_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + NTSTATUS status; + + status = cli_smb_recv(subreq, 0, NULL, NULL, NULL, NULL); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; + } + tevent_req_done(req); +} + +NTSTATUS cli_oplock_ack_recv(struct tevent_req *req) { - char *oldbuf = cli->outbuf; - char buf[smb_size+16]; - bool ret; - - cli->outbuf = buf; - - memset(buf,'\0',smb_size); - cli_set_message(buf,8,0,True); - - SCVAL(buf,smb_com,SMBlockingX); - SSVAL(buf,smb_tid, cli->cnum); - cli_setup_packet(cli); - SSVAL(buf,smb_vwv0,0xFF); - SSVAL(buf,smb_vwv1,0); - SSVAL(buf,smb_vwv2,fnum); - if (level == 1) - SSVAL(buf,smb_vwv3,0x102); /* levelII oplock break ack */ - else - SSVAL(buf,smb_vwv3,2); /* exclusive oplock break ack */ - SIVAL(buf,smb_vwv4,0); /* timoeut */ - SSVAL(buf,smb_vwv6,0); /* unlockcount */ - SSVAL(buf,smb_vwv7,0); /* lockcount */ - - ret = cli_send_smb(cli); - - cli->outbuf = oldbuf; - - return ret; + return tevent_req_simple_recv_ntstatus(req); } +bool cli_oplock_ack(struct cli_state *cli, int fnum, unsigned char level) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct event_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_OK; + + if (cli_has_async_calls(cli)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = event_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + req = cli_oplock_ack_send(frame, ev, cli, fnum, level); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + if (!tevent_req_poll(req, ev)) { + status = map_nt_error_from_unix(errno); + goto fail; + } + + status = cli_oplock_ack_recv(req); + fail: + TALLOC_FREE(frame); + if (!NT_STATUS_IS_OK(status)) { + cli_set_error(cli, status); + } + return NT_STATUS_IS_OK(status); +} /**************************************************************************** set the oplock handler for a connection diff --git a/source3/libsmb/clireadwrite.c b/source3/libsmb/clireadwrite.c index f2f447b4c9..1d2f5f79ec 100644 --- a/source3/libsmb/clireadwrite.c +++ b/source3/libsmb/clireadwrite.c @@ -70,21 +70,25 @@ static size_t cli_write_max_bufsize(struct cli_state *cli, uint16_t write_mode) return CLI_WINDOWS_MAX_LARGE_WRITEX_SIZE; } +struct cli_read_andx_state { + size_t size; + uint16_t vwv[12]; + NTSTATUS status; + size_t received; + uint8_t *buf; +}; -/* - * Send a read&x request - */ +static void cli_read_andx_done(struct tevent_req *subreq); -struct async_req *cli_read_andx_send(TALLOC_CTX *mem_ctx, - struct event_context *ev, - struct cli_state *cli, int fnum, - off_t offset, size_t size) +struct tevent_req *cli_read_andx_create(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct cli_state *cli, int fnum, + off_t offset, size_t size, + struct tevent_req **psmbreq) { - struct async_req *result; - struct cli_request *req; + struct tevent_req *req, *subreq; + struct cli_read_andx_state *state; bool bigoffset = False; - - uint16_t vwv[12]; uint8_t wct = 10; if (size > cli_read_max_bufsize(cli)) { @@ -94,82 +98,85 @@ struct async_req *cli_read_andx_send(TALLOC_CTX *mem_ctx, return NULL; } - SCVAL(vwv + 0, 0, 0xFF); - SCVAL(vwv + 0, 1, 0); - SSVAL(vwv + 1, 0, 0); - SSVAL(vwv + 2, 0, fnum); - SIVAL(vwv + 3, 0, offset); - SSVAL(vwv + 5, 0, size); - SSVAL(vwv + 6, 0, size); - SSVAL(vwv + 7, 0, (size >> 16)); - SSVAL(vwv + 8, 0, 0); - SSVAL(vwv + 9, 0, 0); + req = tevent_req_create(mem_ctx, &state, struct cli_read_andx_state); + if (req == NULL) { + return NULL; + } + state->size = size; + + SCVAL(state->vwv + 0, 0, 0xFF); + SCVAL(state->vwv + 0, 1, 0); + SSVAL(state->vwv + 1, 0, 0); + SSVAL(state->vwv + 2, 0, fnum); + SIVAL(state->vwv + 3, 0, offset); + SSVAL(state->vwv + 5, 0, size); + SSVAL(state->vwv + 6, 0, size); + SSVAL(state->vwv + 7, 0, (size >> 16)); + SSVAL(state->vwv + 8, 0, 0); + SSVAL(state->vwv + 9, 0, 0); if ((uint64_t)offset >> 32) { - bigoffset = True; - SIVAL(vwv + 10, 0, + bigoffset = true; + SIVAL(state->vwv + 10, 0, (((uint64_t)offset)>>32) & 0xffffffff); wct += 2; } - result = cli_request_send(mem_ctx, ev, cli, SMBreadX, 0, wct, vwv, 0, - 0, NULL); - if (result == NULL) { + subreq = cli_smb_req_create(state, ev, cli, SMBreadX, 0, wct, + state->vwv, 0, NULL); + if (subreq == NULL) { + TALLOC_FREE(req); return NULL; } + tevent_req_set_callback(subreq, cli_read_andx_done, req); + *psmbreq = subreq; + return req; +} - req = talloc_get_type_abort(result->private_data, struct cli_request); - - req->data.read.ofs = offset; - req->data.read.size = size; - req->data.read.received = 0; - req->data.read.rcvbuf = NULL; +struct tevent_req *cli_read_andx_send(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct cli_state *cli, int fnum, + off_t offset, size_t size) +{ + struct tevent_req *req, *subreq; - return result; + req = cli_read_andx_create(mem_ctx, ev, cli, fnum, offset, size, + &subreq); + if ((req == NULL) || !cli_smb_req_send(subreq)) { + TALLOC_FREE(req); + return NULL; + } + return req; } -/* - * Pull the data out of a finished async read_and_x request. rcvbuf is - * talloced from the request, so better make sure that you copy it away before - * you talloc_free(req). "rcvbuf" is NOT a talloc_ctx of its own, so do not - * talloc_move it! - */ - -NTSTATUS cli_read_andx_recv(struct async_req *req, ssize_t *received, - uint8_t **rcvbuf) +static void cli_read_andx_done(struct tevent_req *subreq) { - struct cli_request *cli_req = talloc_get_type_abort( - req->private_data, struct cli_request); + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_read_andx_state *state = tevent_req_data( + req, struct cli_read_andx_state); + uint8_t *inbuf; uint8_t wct; uint16_t *vwv; - uint16_t num_bytes; + uint32_t num_bytes; uint8_t *bytes; - uint8_t *buf; - NTSTATUS status; - size_t size; - - if (async_req_is_nterror(req, &status)) { - return status; - } - - status = cli_pull_reply(req, &wct, &vwv, &num_bytes, &bytes); - - if (NT_STATUS_IS_ERR(status)) { - return status; - } - if (wct < 12) { - return NT_STATUS_INVALID_NETWORK_RESPONSE; + state->status = cli_smb_recv(subreq, 12, &wct, &vwv, &num_bytes, + &bytes); + if (NT_STATUS_IS_ERR(state->status)) { + tevent_req_nterror(req, state->status); + return; } /* size is the number of bytes the server returned. * Might be zero. */ - size = SVAL(vwv + 5, 0); - size |= (((unsigned int)SVAL(vwv + 7, 0)) << 16); + state->received = SVAL(vwv + 5, 0); + state->received |= (((unsigned int)SVAL(vwv + 7, 0)) << 16); - if (size > cli_req->data.read.size) { + if (state->received > state->size) { DEBUG(5,("server returned more than we wanted!\n")); - return NT_STATUS_UNEXPECTED_IO_ERROR; + tevent_req_nterror(req, NT_STATUS_UNEXPECTED_IO_ERROR); + return; } /* @@ -177,24 +184,52 @@ NTSTATUS cli_read_andx_recv(struct async_req *req, ssize_t *received, * bcc field can't be correct. */ - if ((size < 0xffff) && (size > num_bytes)) { + if ((state->received < 0xffff) && (state->received > num_bytes)) { DEBUG(5, ("server announced more bytes than sent\n")); - return NT_STATUS_INVALID_NETWORK_RESPONSE; + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; } - buf = (uint8_t *)smb_base(cli_req->inbuf) + SVAL(vwv+6, 0); + inbuf = cli_smb_inbuf(subreq); + state->buf = (uint8_t *)smb_base(inbuf) + SVAL(vwv+6, 0); - if (trans_oob(smb_len(cli_req->inbuf), SVAL(vwv+6, 0), size) - || (buf < bytes)) { + if (trans_oob(smb_len(inbuf), SVAL(vwv+6, 0), state->received) + || (state->buf < bytes)) { DEBUG(5, ("server returned invalid read&x data offset\n")); - return NT_STATUS_INVALID_NETWORK_RESPONSE; + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; } + tevent_req_done(req); +} + +/* + * Pull the data out of a finished async read_and_x request. rcvbuf is + * talloced from the request, so better make sure that you copy it away before + * you talloc_free(req). "rcvbuf" is NOT a talloc_ctx of its own, so do not + * talloc_move it! + */ + +NTSTATUS cli_read_andx_recv(struct tevent_req *req, ssize_t *received, + uint8_t **rcvbuf) +{ + struct cli_read_andx_state *state = tevent_req_data( + req, struct cli_read_andx_state); + NTSTATUS status; - *rcvbuf = (uint8_t *)(smb_base(cli_req->inbuf) + SVAL(vwv + 6, 0)); - *received = size; + if (tevent_req_is_nterror(req, &status)) { + return status; + } + *received = state->received; + *rcvbuf = state->buf; return NT_STATUS_OK; } +struct cli_pull_subreq { + struct tevent_req *req; + ssize_t received; + uint8_t *buf; +}; + /* * Parallel read support. * @@ -204,7 +239,7 @@ NTSTATUS cli_read_andx_recv(struct async_req *req, ssize_t *received, */ struct cli_pull_state { - struct async_req *req; + struct tevent_req *req; struct event_context *ev; struct cli_state *cli; @@ -221,7 +256,7 @@ struct cli_pull_state { * Outstanding requests */ int num_reqs; - struct async_req **reqs; + struct cli_pull_subreq *reqs; /* * For how many bytes did we send requests already? @@ -243,13 +278,13 @@ struct cli_pull_state { SMB_OFF_T pushed; }; -static char *cli_pull_print(TALLOC_CTX *mem_ctx, struct async_req *req) +static char *cli_pull_print(struct tevent_req *req, TALLOC_CTX *mem_ctx) { - struct cli_pull_state *state = talloc_get_type_abort( - req->private_data, struct cli_pull_state); + struct cli_pull_state *state = tevent_req_data( + req, struct cli_pull_state); char *result; - result = async_req_print(mem_ctx, req); + result = tevent_req_print(mem_ctx, req); if (result == NULL) { return NULL; } @@ -259,31 +294,31 @@ static char *cli_pull_print(TALLOC_CTX *mem_ctx, struct async_req *req) state->num_reqs, state->top_req); } -static void cli_pull_read_done(struct async_req *read_req); +static void cli_pull_read_done(struct tevent_req *read_req); /* * Prepare an async pull request */ -struct async_req *cli_pull_send(TALLOC_CTX *mem_ctx, - struct event_context *ev, - struct cli_state *cli, - uint16_t fnum, off_t start_offset, - SMB_OFF_T size, size_t window_size, - NTSTATUS (*sink)(char *buf, size_t n, - void *priv), - void *priv) +struct tevent_req *cli_pull_send(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct cli_state *cli, + uint16_t fnum, off_t start_offset, + SMB_OFF_T size, size_t window_size, + NTSTATUS (*sink)(char *buf, size_t n, + void *priv), + void *priv) { - struct async_req *result; + struct tevent_req *req; struct cli_pull_state *state; int i; - if (!async_req_setup(mem_ctx, &result, &state, - struct cli_pull_state)) { + req = tevent_req_create(mem_ctx, &state, struct cli_pull_state); + if (req == NULL) { return NULL; } - result->print = cli_pull_print; - state->req = result; + tevent_req_set_print_fn(req, cli_pull_print); + state->req = req; state->cli = cli; state->ev = ev; @@ -297,10 +332,8 @@ struct async_req *cli_pull_send(TALLOC_CTX *mem_ctx, state->top_req = 0; if (size == 0) { - if (!async_post_ntstatus(result, ev, NT_STATUS_OK)) { - goto failed; - } - return result; + tevent_req_done(req); + return tevent_req_post(req, ev); } state->chunk_size = cli_read_max_bufsize(cli); @@ -308,7 +341,7 @@ struct async_req *cli_pull_send(TALLOC_CTX *mem_ctx, state->num_reqs = MAX(window_size/state->chunk_size, 1); state->num_reqs = MIN(state->num_reqs, cli->max_mux); - state->reqs = TALLOC_ZERO_ARRAY(state, struct async_req *, + state->reqs = TALLOC_ZERO_ARRAY(state, struct cli_pull_subreq, state->num_reqs); if (state->reqs == NULL) { goto failed; @@ -317,6 +350,7 @@ struct async_req *cli_pull_send(TALLOC_CTX *mem_ctx, state->requested = 0; for (i=0; i<state->num_reqs; i++) { + struct cli_pull_subreq *subreq = &state->reqs[i]; SMB_OFF_T size_left; size_t request_thistime; @@ -328,24 +362,21 @@ struct async_req *cli_pull_send(TALLOC_CTX *mem_ctx, size_left = size - state->requested; request_thistime = MIN(size_left, state->chunk_size); - state->reqs[i] = cli_read_andx_send( + subreq->req = cli_read_andx_send( state->reqs, ev, cli, fnum, state->start_offset + state->requested, request_thistime); - if (state->reqs[i] == NULL) { + if (subreq->req == NULL) { goto failed; } - - state->reqs[i]->async.fn = cli_pull_read_done; - state->reqs[i]->async.priv = result; - + tevent_req_set_callback(subreq->req, cli_pull_read_done, req); state->requested += request_thistime; } - return result; + return req; failed: - TALLOC_FREE(result); + TALLOC_FREE(req); return NULL; } @@ -354,20 +385,32 @@ failed: * requests if necessary. */ -static void cli_pull_read_done(struct async_req *read_req) +static void cli_pull_read_done(struct tevent_req *subreq) { - struct async_req *pull_req = talloc_get_type_abort( - read_req->async.priv, struct async_req); - struct cli_pull_state *state = talloc_get_type_abort( - pull_req->private_data, struct cli_pull_state); - struct cli_request *read_state = talloc_get_type_abort( - read_req->private_data, struct cli_request); + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_pull_state *state = tevent_req_data( + req, struct cli_pull_state); + struct cli_pull_subreq *pull_subreq = NULL; NTSTATUS status; + int i; - status = cli_read_andx_recv(read_req, &read_state->data.read.received, - &read_state->data.read.rcvbuf); + for (i = 0; i < state->num_reqs; i++) { + pull_subreq = &state->reqs[i]; + if (subreq == pull_subreq->req) { + break; + } + } + if (i == state->num_reqs) { + /* Huh -- received something we did not send?? */ + tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR); + return; + } + + status = cli_read_andx_recv(subreq, &pull_subreq->received, + &pull_subreq->buf); if (!NT_STATUS_IS_OK(status)) { - async_req_nterror(state->req, status); + tevent_req_nterror(state->req, status); return; } @@ -380,39 +423,36 @@ static void cli_pull_read_done(struct async_req *read_req) * requests. */ - while (state->reqs[state->top_req] != NULL) { - struct cli_request *top_read; + while (state->reqs[state->top_req].req != NULL) { + struct cli_pull_subreq *top_subreq; DEBUG(11, ("cli_pull_read_done: top_req = %d\n", state->top_req)); - if (state->reqs[state->top_req]->state < ASYNC_REQ_DONE) { + top_subreq = &state->reqs[state->top_req]; + + if (tevent_req_is_in_progress(top_subreq->req)) { DEBUG(11, ("cli_pull_read_done: top request not yet " "done\n")); return; } - top_read = talloc_get_type_abort( - state->reqs[state->top_req]->private_data, - struct cli_request); - DEBUG(10, ("cli_pull_read_done: Pushing %d bytes, %d already " - "pushed\n", (int)top_read->data.read.received, + "pushed\n", (int)top_subreq->received, (int)state->pushed)); - status = state->sink((char *)top_read->data.read.rcvbuf, - top_read->data.read.received, - state->priv); + status = state->sink((char *)top_subreq->buf, + top_subreq->received, state->priv); if (!NT_STATUS_IS_OK(status)) { - async_req_nterror(state->req, status); + tevent_req_nterror(state->req, status); return; } - state->pushed += top_read->data.read.received; + state->pushed += top_subreq->received; - TALLOC_FREE(state->reqs[state->top_req]); + TALLOC_FREE(state->reqs[state->top_req].req); if (state->requested < state->size) { - struct async_req *new_req; + struct tevent_req *new_req; SMB_OFF_T size_left; size_t request_thistime; @@ -432,30 +472,29 @@ static void cli_pull_read_done(struct async_req *read_req) state->start_offset + state->requested, request_thistime); - if (async_req_nomem(new_req, state->req)) { + if (tevent_req_nomem(new_req, state->req)) { return; } + tevent_req_set_callback(new_req, cli_pull_read_done, + req); - new_req->async.fn = cli_pull_read_done; - new_req->async.priv = pull_req; - - state->reqs[state->top_req] = new_req; + state->reqs[state->top_req].req = new_req; state->requested += request_thistime; } state->top_req = (state->top_req+1) % state->num_reqs; } - async_req_done(pull_req); + tevent_req_done(req); } -NTSTATUS cli_pull_recv(struct async_req *req, SMB_OFF_T *received) +NTSTATUS cli_pull_recv(struct tevent_req *req, SMB_OFF_T *received) { - struct cli_pull_state *state = talloc_get_type_abort( - req->private_data, struct cli_pull_state); + struct cli_pull_state *state = tevent_req_data( + req, struct cli_pull_state); NTSTATUS status; - if (async_req_is_nterror(req, &status)) { + if (tevent_req_is_nterror(req, &status)) { return status; } *received = state->pushed; @@ -469,35 +508,42 @@ NTSTATUS cli_pull(struct cli_state *cli, uint16_t fnum, { TALLOC_CTX *frame = talloc_stackframe(); struct event_context *ev; - struct async_req *req; - NTSTATUS result = NT_STATUS_NO_MEMORY; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_OK; - if (cli->fd_event != NULL) { + if (cli_has_async_calls(cli)) { /* * Can't use sync call while an async call is in flight */ - return NT_STATUS_INVALID_PARAMETER; + status = NT_STATUS_INVALID_PARAMETER; + goto fail; } ev = event_context_init(frame); if (ev == NULL) { - goto nomem; + status = NT_STATUS_NO_MEMORY; + goto fail; } req = cli_pull_send(frame, ev, cli, fnum, start_offset, size, window_size, sink, priv); if (req == NULL) { - goto nomem; + status = NT_STATUS_NO_MEMORY; + goto fail; } - while (req->state < ASYNC_REQ_DONE) { - event_loop_once(ev); + if (!tevent_req_poll(req, ev)) { + status = map_nt_error_from_unix(errno); + goto fail; } - result = cli_pull_recv(req, received); - nomem: + status = cli_pull_recv(req, received); + fail: TALLOC_FREE(frame); - return result; + if (!NT_STATUS_IS_OK(status)) { + cli_set_error(cli, status); + } + return status; } static NTSTATUS cli_read_sink(char *buf, size_t n, void *priv) @@ -744,19 +790,41 @@ ssize_t cli_smbwrite(struct cli_state *cli, * Send a write&x request */ -struct async_req *cli_write_andx_send(TALLOC_CTX *mem_ctx, - struct event_context *ev, - struct cli_state *cli, uint16_t fnum, - uint16_t mode, const uint8_t *buf, - off_t offset, size_t size) +struct cli_write_andx_state { + size_t size; + uint16_t vwv[14]; + size_t written; + uint8_t pad; + struct iovec iov[2]; +}; + +static void cli_write_andx_done(struct tevent_req *subreq); + +struct tevent_req *cli_write_andx_create(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct cli_state *cli, uint16_t fnum, + uint16_t mode, const uint8_t *buf, + off_t offset, size_t size, + struct tevent_req **reqs_before, + int num_reqs_before, + struct tevent_req **psmbreq) { + struct tevent_req *req, *subreq; + struct cli_write_andx_state *state; bool bigoffset = ((cli->capabilities & CAP_LARGE_FILES) != 0); uint8_t wct = bigoffset ? 14 : 12; size_t max_write = cli_write_max_bufsize(cli, mode); - uint16_t vwv[14]; + uint16_t *vwv; + + req = tevent_req_create(mem_ctx, &state, struct cli_write_andx_state); + if (req == NULL) { + return NULL; + } size = MIN(size, max_write); + vwv = state->vwv; + SCVAL(vwv+0, 0, 0xFF); SCVAL(vwv+0, 1, 0); SSVAL(vwv+1, 0, 0); @@ -769,7 +837,7 @@ struct async_req *cli_write_andx_send(TALLOC_CTX *mem_ctx, SSVAL(vwv+10, 0, size); SSVAL(vwv+11, 0, - cli_wct_ofs(cli) + cli_smb_wct_ofs(reqs_before, num_reqs_before) + 1 /* the wct field */ + wct * 2 /* vwv */ + 2 /* num_bytes field */ @@ -779,37 +847,70 @@ struct async_req *cli_write_andx_send(TALLOC_CTX *mem_ctx, SIVAL(vwv+12, 0, (((uint64_t)offset)>>32) & 0xffffffff); } - return cli_request_send(mem_ctx, ev, cli, SMBwriteX, 0, wct, vwv, - 2, size, buf); + state->pad = 0; + state->iov[0].iov_base = &state->pad; + state->iov[0].iov_len = 1; + state->iov[1].iov_base = CONST_DISCARD(uint8_t *, buf); + state->iov[1].iov_len = size; + + subreq = cli_smb_req_create(state, ev, cli, SMBwriteX, 0, wct, vwv, + 2, state->iov); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_write_andx_done, req); + *psmbreq = subreq; + return req; } -NTSTATUS cli_write_andx_recv(struct async_req *req, size_t *pwritten) +struct tevent_req *cli_write_andx_send(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct cli_state *cli, uint16_t fnum, + uint16_t mode, const uint8_t *buf, + off_t offset, size_t size) { + struct tevent_req *req, *subreq; + + req = cli_write_andx_create(mem_ctx, ev, cli, fnum, mode, buf, offset, + size, NULL, 0, &subreq); + if ((req == NULL) || !cli_smb_req_send(subreq)) { + TALLOC_FREE(req); + return NULL; + } + return req; +} + +static void cli_write_andx_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_write_andx_state *state = tevent_req_data( + req, struct cli_write_andx_state); uint8_t wct; uint16_t *vwv; - uint16_t num_bytes; - uint8_t *bytes; NTSTATUS status; - size_t written; - if (async_req_is_nterror(req, &status)) { - return status; + status = cli_smb_recv(subreq, 6, &wct, &vwv, NULL, NULL); + if (NT_STATUS_IS_ERR(status)) { + TALLOC_FREE(subreq); + tevent_req_nterror(req, status); + return; } + state->written = SVAL(vwv+2, 0); + state->written |= SVAL(vwv+4, 0)<<16; + tevent_req_done(req); +} - status = cli_pull_reply(req, &wct, &vwv, &num_bytes, &bytes); +NTSTATUS cli_write_andx_recv(struct tevent_req *req, size_t *pwritten) +{ + struct cli_write_andx_state *state = tevent_req_data( + req, struct cli_write_andx_state); + NTSTATUS status; - if (NT_STATUS_IS_ERR(status)) { + if (tevent_req_is_nterror(req, &status)) { return status; } - - if (wct < 6) { - return NT_STATUS_INVALID_NETWORK_RESPONSE; - } - - written = SVAL(vwv+2, 0); - written |= SVAL(vwv+4, 0)<<16; - *pwritten = written; - + *pwritten = state->written; return NT_STATUS_OK; } @@ -824,22 +925,21 @@ struct cli_writeall_state { size_t written; }; -static void cli_writeall_written(struct async_req *req); +static void cli_writeall_written(struct tevent_req *req); -static struct async_req *cli_writeall_send(TALLOC_CTX *mem_ctx, - struct event_context *ev, - struct cli_state *cli, - uint16_t fnum, - uint16_t mode, - const uint8_t *buf, - off_t offset, size_t size) +static struct tevent_req *cli_writeall_send(TALLOC_CTX *mem_ctx, + struct event_context *ev, + struct cli_state *cli, + uint16_t fnum, + uint16_t mode, + const uint8_t *buf, + off_t offset, size_t size) { - struct async_req *result; - struct async_req *subreq; + struct tevent_req *req, *subreq; struct cli_writeall_state *state; - if (!async_req_setup(mem_ctx, &result, &state, - struct cli_writeall_state)) { + req = tevent_req_create(mem_ctx, &state, struct cli_writeall_state); + if (req == NULL) { return NULL; } state->ev = ev; @@ -854,46 +954,40 @@ static struct async_req *cli_writeall_send(TALLOC_CTX *mem_ctx, subreq = cli_write_andx_send(state, state->ev, state->cli, state->fnum, state->mode, state->buf, state->offset, state->size); - if (subreq == NULL) { - goto fail; + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); } - - subreq->async.fn = cli_writeall_written; - subreq->async.priv = result; - return result; - - fail: - TALLOC_FREE(result); - return NULL; + tevent_req_set_callback(subreq, cli_writeall_written, req); + return req; } -static void cli_writeall_written(struct async_req *subreq) +static void cli_writeall_written(struct tevent_req *subreq) { - struct async_req *req = talloc_get_type_abort( - subreq->async.priv, struct async_req); - struct cli_writeall_state *state = talloc_get_type_abort( - req->private_data, struct cli_writeall_state); + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_writeall_state *state = tevent_req_data( + req, struct cli_writeall_state); NTSTATUS status; size_t written, to_write; status = cli_write_andx_recv(subreq, &written); TALLOC_FREE(subreq); if (!NT_STATUS_IS_OK(status)) { - async_req_nterror(req, status); + tevent_req_nterror(req, status); return; } state->written += written; if (state->written > state->size) { - async_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); return; } to_write = state->size - state->written; if (to_write == 0) { - async_req_done(req); + tevent_req_done(req); return; } @@ -901,22 +995,19 @@ static void cli_writeall_written(struct async_req *subreq) state->mode, state->buf + state->written, state->offset + state->written, to_write); - if (subreq == NULL) { - async_req_nterror(req, NT_STATUS_NO_MEMORY); + if (tevent_req_nomem(subreq, req)) { return; } - - subreq->async.fn = cli_writeall_written; - subreq->async.priv = req; + tevent_req_set_callback(subreq, cli_writeall_written, req); } -static NTSTATUS cli_writeall_recv(struct async_req *req) +static NTSTATUS cli_writeall_recv(struct tevent_req *req) { - return async_req_simple_recv_ntstatus(req); + return tevent_req_simple_recv_ntstatus(req); } struct cli_push_write_state { - struct async_req *req;/* This is the main request! Not the subreq */ + struct tevent_req *req;/* This is the main request! Not the subreq */ uint32_t idx; off_t ofs; uint8_t *buf; @@ -947,14 +1038,14 @@ struct cli_push_state { struct cli_push_write_state **reqs; }; -static void cli_push_written(struct async_req *req); +static void cli_push_written(struct tevent_req *req); -static bool cli_push_write_setup(struct async_req *req, +static bool cli_push_write_setup(struct tevent_req *req, struct cli_push_state *state, uint32_t idx) { struct cli_push_write_state *substate; - struct async_req *subreq; + struct tevent_req *subreq; substate = talloc(state->reqs, struct cli_push_write_state); if (!substate) { @@ -988,8 +1079,7 @@ static bool cli_push_write_setup(struct async_req *req, talloc_free(substate); return false; } - subreq->async.fn = cli_push_written; - subreq->async.priv = substate; + tevent_req_set_callback(subreq, cli_push_written, substate); state->reqs[idx] = substate; state->pending += 1; @@ -998,20 +1088,20 @@ static bool cli_push_write_setup(struct async_req *req, return true; } -struct async_req *cli_push_send(TALLOC_CTX *mem_ctx, struct event_context *ev, - struct cli_state *cli, - uint16_t fnum, uint16_t mode, - off_t start_offset, size_t window_size, - size_t (*source)(uint8_t *buf, size_t n, - void *priv), - void *priv) +struct tevent_req *cli_push_send(TALLOC_CTX *mem_ctx, struct event_context *ev, + struct cli_state *cli, + uint16_t fnum, uint16_t mode, + off_t start_offset, size_t window_size, + size_t (*source)(uint8_t *buf, size_t n, + void *priv), + void *priv) { - struct async_req *req; + struct tevent_req *req; struct cli_push_state *state; uint32_t i; - if (!async_req_setup(mem_ctx, &req, &state, - struct cli_push_state)) { + req = tevent_req_create(mem_ctx, &state, struct cli_push_state); + if (req == NULL) { return NULL; } state->cli = cli; @@ -1054,26 +1144,24 @@ struct async_req *cli_push_send(TALLOC_CTX *mem_ctx, struct event_context *ev, } if (state->pending == 0) { - if (!async_post_ntstatus(req, ev, NT_STATUS_OK)) { - goto failed; - } - return req; + tevent_req_done(req); + return tevent_req_post(req, ev); } return req; failed: - TALLOC_FREE(req); - return NULL; + tevent_req_nterror(req, NT_STATUS_NO_MEMORY); + return tevent_req_post(req, ev); } -static void cli_push_written(struct async_req *subreq) +static void cli_push_written(struct tevent_req *subreq) { - struct cli_push_write_state *substate = talloc_get_type_abort( - subreq->async.priv, struct cli_push_write_state); - struct async_req *req = substate->req; - struct cli_push_state *state = talloc_get_type_abort( - req->private_data, struct cli_push_state); + struct cli_push_write_state *substate = tevent_req_callback_data( + subreq, struct cli_push_write_state); + struct tevent_req *req = substate->req; + struct cli_push_state *state = tevent_req_data( + req, struct cli_push_state); NTSTATUS status; uint32_t idx = substate->idx; @@ -1084,26 +1172,26 @@ static void cli_push_written(struct async_req *subreq) TALLOC_FREE(subreq); TALLOC_FREE(substate); if (!NT_STATUS_IS_OK(status)) { - async_req_nterror(req, status); + tevent_req_nterror(req, status); return; } if (!state->eof) { if (!cli_push_write_setup(req, state, idx)) { - async_req_nomem(NULL, req); + tevent_req_nterror(req, NT_STATUS_NO_MEMORY); return; } } if (state->pending == 0) { - async_req_done(req); + tevent_req_done(req); return; } } -NTSTATUS cli_push_recv(struct async_req *req) +NTSTATUS cli_push_recv(struct tevent_req *req) { - return async_req_simple_recv_ntstatus(req); + return tevent_req_simple_recv_ntstatus(req); } NTSTATUS cli_push(struct cli_state *cli, uint16_t fnum, uint16_t mode, @@ -1113,33 +1201,40 @@ NTSTATUS cli_push(struct cli_state *cli, uint16_t fnum, uint16_t mode, { TALLOC_CTX *frame = talloc_stackframe(); struct event_context *ev; - struct async_req *req; - NTSTATUS result = NT_STATUS_NO_MEMORY; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_OK; - if (cli->fd_event != NULL) { + if (cli_has_async_calls(cli)) { /* * Can't use sync call while an async call is in flight */ - return NT_STATUS_INVALID_PARAMETER; + status = NT_STATUS_INVALID_PARAMETER; + goto fail; } ev = event_context_init(frame); if (ev == NULL) { - goto nomem; + status = NT_STATUS_NO_MEMORY; + goto fail; } req = cli_push_send(frame, ev, cli, fnum, mode, start_offset, window_size, source, priv); if (req == NULL) { - goto nomem; + status = NT_STATUS_NO_MEMORY; + goto fail; } - while (req->state < ASYNC_REQ_DONE) { - event_loop_once(ev); + if (!tevent_req_poll(req, ev)) { + status = map_nt_error_from_unix(errno); + goto fail; } - result = cli_push_recv(req); - nomem: + status = cli_push_recv(req); + fail: TALLOC_FREE(frame); - return result; + if (!NT_STATUS_IS_OK(status)) { + cli_set_error(cli, status); + } + return status; } diff --git a/source3/libsmb/clitrans.c b/source3/libsmb/clitrans.c index 91fb6e6296..3a6aa9e72c 100644 --- a/source3/libsmb/clitrans.c +++ b/source3/libsmb/clitrans.c @@ -704,6 +704,8 @@ struct cli_trans_state { uint16_t mid; uint32_t seqnum; const char *pipe_name; + uint8_t *pipe_name_conv; + size_t pipe_name_conv_len; uint16_t fid; uint16_t function; int flags; @@ -720,27 +722,131 @@ struct cli_trans_state { struct trans_recvblob rdata; TALLOC_CTX *secondary_request_ctx; + + struct iovec iov[4]; + uint8_t pad[4]; + uint16_t vwv[32]; }; -static void cli_trans_recv_helper(struct async_req *req); +static NTSTATUS cli_pull_trans(uint8_t *inbuf, + uint8_t wct, uint16_t *vwv, + uint16_t num_bytes, uint8_t *bytes, + uint8_t smb_cmd, bool expect_first_reply, + uint8_t *pnum_setup, uint16_t **psetup, + uint32_t *ptotal_param, uint32_t *pnum_param, + uint32_t *pparam_disp, uint8_t **pparam, + uint32_t *ptotal_data, uint32_t *pnum_data, + uint32_t *pdata_disp, uint8_t **pdata) +{ + uint32_t param_ofs, data_ofs; + + if (expect_first_reply) { + if ((wct != 0) || (num_bytes != 0)) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + return NT_STATUS_OK; + } + + switch (smb_cmd) { + case SMBtrans: + case SMBtrans2: + if (wct < 10) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + *ptotal_param = SVAL(vwv + 0, 0); + *ptotal_data = SVAL(vwv + 1, 0); + *pnum_param = SVAL(vwv + 3, 0); + param_ofs = SVAL(vwv + 4, 0); + *pparam_disp = SVAL(vwv + 5, 0); + *pnum_data = SVAL(vwv + 6, 0); + data_ofs = SVAL(vwv + 7, 0); + *pdata_disp = SVAL(vwv + 8, 0); + *pnum_setup = CVAL(vwv + 9, 0); + if (wct < 10 + (*pnum_setup)) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + *psetup = vwv + 10; -static struct async_req *cli_ship_trans(TALLOC_CTX *mem_ctx, - struct cli_trans_state *state) + break; + case SMBnttrans: + if (wct < 18) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + *ptotal_param = IVAL(vwv, 3); + *ptotal_data = IVAL(vwv, 7); + *pnum_param = IVAL(vwv, 11); + param_ofs = IVAL(vwv, 15); + *pparam_disp = IVAL(vwv, 19); + *pnum_data = IVAL(vwv, 23); + data_ofs = IVAL(vwv, 27); + *pdata_disp = IVAL(vwv, 31); + *pnum_setup = CVAL(vwv, 35); + *psetup = vwv + 18; + break; + + default: + return NT_STATUS_INTERNAL_ERROR; + } + + /* + * Check for buffer overflows. data_ofs needs to be checked against + * the incoming buffer length, data_disp against the total + * length. Likewise for param_ofs/param_disp. + */ + + if (trans_oob(smb_len(inbuf), param_ofs, *pnum_param) + || trans_oob(*ptotal_param, *pparam_disp, *pnum_param) + || trans_oob(smb_len(inbuf), data_ofs, *pnum_data) + || trans_oob(*ptotal_data, *pdata_disp, *pnum_data)) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + *pparam = (uint8_t *)inbuf + 4 + param_ofs; + *pdata = (uint8_t *)inbuf + 4 + data_ofs; + + return NT_STATUS_OK; +} + +static NTSTATUS cli_trans_pull_blob(TALLOC_CTX *mem_ctx, + struct trans_recvblob *blob, + uint32_t total, uint32_t thistime, + uint8_t *buf, uint32_t displacement) { - TALLOC_CTX *frame; - struct async_req *result = NULL; - struct cli_request *cli_req; - uint8_t wct; - uint16_t *vwv; - uint8_t *bytes = NULL; + if (blob->data == NULL) { + if (total > blob->max) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + blob->total = total; + blob->data = TALLOC_ARRAY(mem_ctx, uint8_t, total); + if (blob->data == NULL) { + return NT_STATUS_NO_MEMORY; + } + } + + if (total > blob->total) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + if (thistime) { + memcpy(blob->data + displacement, buf, thistime); + blob->received += thistime; + } + + return NT_STATUS_OK; +} + +static void cli_trans_format(struct cli_trans_state *state, uint8_t *pwct, + int *piov_count) +{ + uint8_t wct = 0; + struct iovec *iov = state->iov; + uint8_t *pad = state->pad; + uint16_t *vwv = state->vwv; uint16_t param_offset; uint16_t this_param = 0; uint16_t this_data = 0; uint32_t useable_space; uint8_t cmd; - uint8_t pad[3]; - - frame = talloc_stackframe(); cmd = state->cmd; @@ -751,39 +857,26 @@ static struct async_req *cli_ship_trans(TALLOC_CTX *mem_ctx, param_offset = smb_size - 4; - bytes = TALLOC_ARRAY(talloc_tos(), uint8_t, 0); /* padding */ - if (bytes == NULL) { - goto fail; - } - switch (cmd) { case SMBtrans: pad[0] = 0; - bytes = (uint8_t *)talloc_append_blob(talloc_tos(), bytes, - data_blob_const(pad, 1)); - if (bytes == NULL) { - goto fail; - } - bytes = smb_bytes_push_str(bytes, cli_ucs2(state->cli), - state->pipe_name, - strlen(state->pipe_name)+1, NULL); - if (bytes == NULL) { - goto fail; - } + iov[0].iov_base = pad; + iov[0].iov_len = 1; + iov[1].iov_base = state->pipe_name_conv; + iov[1].iov_len = state->pipe_name_conv_len; wct = 14 + state->num_setup; - param_offset += talloc_get_size(bytes); + param_offset += iov[0].iov_len + iov[1].iov_len; + iov += 2; break; case SMBtrans2: pad[0] = 0; pad[1] = 'D'; /* Copy this from "old" 3.0 behaviour */ pad[2] = ' '; - bytes = (uint8_t *)talloc_append_blob(talloc_tos(), bytes, - data_blob_const(pad, 3)); - if (bytes == NULL) { - goto fail; - } + iov[0].iov_base = pad; + iov[0].iov_len = 3; wct = 14 + state->num_setup; - param_offset += talloc_get_size(bytes); + param_offset += 3; + iov += 1; break; case SMBtranss: wct = 8; @@ -797,8 +890,6 @@ static struct async_req *cli_ship_trans(TALLOC_CTX *mem_ctx, case SMBnttranss: wct = 18; break; - default: - goto fail; } useable_space = state->cli->max_xmit - smb_size - sizeof(uint16_t)*wct; @@ -806,17 +897,19 @@ static struct async_req *cli_ship_trans(TALLOC_CTX *mem_ctx, if (state->param_sent < state->num_param) { this_param = MIN(state->num_param - state->param_sent, useable_space); + iov[0].iov_base = state->param + state->param_sent; + iov[0].iov_len = this_param; + iov += 1; } if (state->data_sent < state->num_data) { this_data = MIN(state->num_data - state->data_sent, useable_space - this_param); + iov[0].iov_base = state->data + state->data_sent; + iov[0].iov_len = this_data; + iov += 1; } - vwv = TALLOC_ARRAY(talloc_tos(), uint16_t, wct); - if (vwv == NULL) { - goto fail; - } param_offset += wct * sizeof(uint16_t); DEBUG(10, ("num_setup=%u, max_setup=%u, " @@ -897,221 +990,120 @@ static struct async_req *cli_ship_trans(TALLOC_CTX *mem_ctx, break; } - bytes = (uint8_t *)talloc_append_blob( - talloc_tos(), bytes, - data_blob_const(state->param + state->param_sent, this_param)); - if (bytes == NULL) { - goto fail; - } state->param_sent += this_param; - - bytes = (uint8_t *)talloc_append_blob( - talloc_tos(), bytes, - data_blob_const(state->data + state->data_sent, this_data)); - if (bytes == NULL) { - goto fail; - } state->data_sent += this_data; - if ((cmd == SMBtrans) || (cmd == SMBtrans2) || (cmd == SMBnttrans)) { - /* - * Primary request, retrieve our mid - */ - result = cli_request_send(mem_ctx, state->ev, state->cli, - cmd, 0, wct, vwv, 0, - talloc_get_size(bytes), bytes); - if (result == NULL) { - goto fail; - } - cli_req = talloc_get_type_abort(result->private_data, - struct cli_request); - state->mid = cli_req->mid; - state->seqnum = cli_req->seqnum; - } else { - uint16_t num_bytes = talloc_get_size(bytes); - /* - * Secondary request, we have to fix up the mid. Thus we do - * the chain_cork/chain/uncork ourselves. - */ - if (!cli_chain_cork(state->cli, state->ev, - wct * sizeof(uint16_t) + num_bytes + 3)) { - goto fail; - } - result = cli_request_send(mem_ctx, state->ev, state->cli, cmd, - 0, wct, vwv, 0, num_bytes, bytes); - if (result == NULL) { - goto fail; - } - cli_req = talloc_get_type_abort(result->private_data, - struct cli_request); - cli_req->recv_helper.fn = cli_trans_recv_helper; - cli_req->recv_helper.priv = state; - cli_req->mid = state->mid; - cli_chain_uncork(state->cli); - state->seqnum = cli_req->seqnum; - } - - fail: - TALLOC_FREE(frame); - return result; + *pwct = wct; + *piov_count = iov - state->iov; } -static void cli_trans_ship_rest(struct async_req *req, - struct cli_trans_state *state) +static void cli_trans_done(struct tevent_req *subreq); + +struct tevent_req *cli_trans_send( + TALLOC_CTX *mem_ctx, struct event_context *ev, + struct cli_state *cli, uint8_t cmd, + const char *pipe_name, uint16_t fid, uint16_t function, int flags, + uint16_t *setup, uint8_t num_setup, uint8_t max_setup, + uint8_t *param, uint32_t num_param, uint32_t max_param, + uint8_t *data, uint32_t num_data, uint32_t max_data) { - struct cli_request *cli_req; + struct tevent_req *req, *subreq; + struct cli_trans_state *state; + int iov_count; + uint8_t wct; - state->secondary_request_ctx = talloc_new(state); - if (state->secondary_request_ctx == NULL) { - async_req_nterror(req, NT_STATUS_NO_MEMORY); - return; + req = tevent_req_create(mem_ctx, &state, struct cli_trans_state); + if (req == NULL) { + return NULL; } - while ((state->param_sent < state->num_param) - || (state->data_sent < state->num_data)) { - struct async_req *subreq; - - subreq = cli_ship_trans(state->secondary_request_ctx, state); - if (subreq == NULL) { - async_req_nterror(req, NT_STATUS_NO_MEMORY); - return; + if ((cmd == SMBtrans) || (cmd == SMBtrans2)) { + if ((num_param > 0xffff) || (max_param > 0xffff) + || (num_data > 0xffff) || (max_data > 0xffff)) { + DEBUG(3, ("Attempt to send invalid trans2 request " + "(setup %u, params %u/%u, data %u/%u)\n", + (unsigned)num_setup, + (unsigned)num_param, (unsigned)max_param, + (unsigned)num_data, (unsigned)max_data)); + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); } } - cli_req = talloc_get_type_abort(req->private_data, - struct cli_request); - - cli_req->seqnum = state->seqnum; -} - -static NTSTATUS cli_pull_trans(struct async_req *req, - struct cli_request *cli_req, - uint8_t smb_cmd, bool expect_first_reply, - uint8_t *pnum_setup, uint16_t **psetup, - uint32_t *ptotal_param, uint32_t *pnum_param, - uint32_t *pparam_disp, uint8_t **pparam, - uint32_t *ptotal_data, uint32_t *pnum_data, - uint32_t *pdata_disp, uint8_t **pdata) -{ - uint32_t param_ofs, data_ofs; - uint8_t wct; - uint16_t *vwv; - uint16_t num_bytes; - uint8_t *bytes; - NTSTATUS status; - - status = cli_pull_reply(req, &wct, &vwv, &num_bytes, &bytes); - /* - * We can receive something like STATUS_MORE_ENTRIES, so don't use - * !NT_STATUS_IS_OK(status) here. + * The largest wct will be for nttrans (19+num_setup). Make sure we + * don't overflow state->vwv in cli_trans_format. */ - if (NT_STATUS_IS_ERR(status)) { - return status; + if ((num_setup + 19) > ARRAY_SIZE(state->vwv)) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); } - if (expect_first_reply) { - if ((wct != 0) || (num_bytes != 0)) { - return NT_STATUS_INVALID_NETWORK_RESPONSE; - } - return NT_STATUS_OK; - } - - switch (smb_cmd) { - case SMBtrans: - case SMBtrans2: - if (wct < 10) { - return NT_STATUS_INVALID_NETWORK_RESPONSE; - } - *ptotal_param = SVAL(vwv + 0, 0); - *ptotal_data = SVAL(vwv + 1, 0); - *pnum_param = SVAL(vwv + 3, 0); - param_ofs = SVAL(vwv + 4, 0); - *pparam_disp = SVAL(vwv + 5, 0); - *pnum_data = SVAL(vwv + 6, 0); - data_ofs = SVAL(vwv + 7, 0); - *pdata_disp = SVAL(vwv + 8, 0); - *pnum_setup = CVAL(vwv + 9, 0); - if (wct < 10 + (*pnum_setup)) { - return NT_STATUS_INVALID_NETWORK_RESPONSE; - } - *psetup = vwv + 10; - - break; - case SMBnttrans: - if (wct < 18) { - return NT_STATUS_INVALID_NETWORK_RESPONSE; - } - *ptotal_param = IVAL(vwv, 3); - *ptotal_data = IVAL(vwv, 7); - *pnum_param = IVAL(vwv, 11); - param_ofs = IVAL(vwv, 15); - *pparam_disp = IVAL(vwv, 19); - *pnum_data = IVAL(vwv, 23); - data_ofs = IVAL(vwv, 27); - *pdata_disp = IVAL(vwv, 31); - *pnum_setup = CVAL(vwv, 35); - *psetup = vwv + 18; - break; + state->cli = cli; + state->ev = ev; + state->cmd = cmd; + state->flags = flags; + state->num_rsetup = 0; + state->rsetup = NULL; + ZERO_STRUCT(state->rparam); + ZERO_STRUCT(state->rdata); - default: - return NT_STATUS_INTERNAL_ERROR; + if ((pipe_name != NULL) + && (!convert_string_allocate(state, CH_UNIX, + cli_ucs2(cli) ? CH_UTF16LE : CH_DOS, + pipe_name, strlen(pipe_name) + 1, + &state->pipe_name_conv, + &state->pipe_name_conv_len, true))) { + tevent_req_nterror(req, NT_STATUS_NO_MEMORY); + return tevent_req_post(req, ev); } + state->fid = fid; /* trans2 */ + state->function = function; /* nttrans */ - /* - * Check for buffer overflows. data_ofs needs to be checked against - * the incoming buffer length, data_disp against the total - * length. Likewise for param_ofs/param_disp. - */ - - if (trans_oob(smb_len(cli_req->inbuf), param_ofs, *pnum_param) - || trans_oob(*ptotal_param, *pparam_disp, *pnum_param) - || trans_oob(smb_len(cli_req->inbuf), data_ofs, *pnum_data) - || trans_oob(*ptotal_data, *pdata_disp, *pnum_data)) { - return NT_STATUS_INVALID_NETWORK_RESPONSE; - } + state->setup = setup; + state->num_setup = num_setup; + state->max_setup = max_setup; - *pparam = (uint8_t *)cli_req->inbuf + 4 + param_ofs; - *pdata = (uint8_t *)cli_req->inbuf + 4 + data_ofs; + state->param = param; + state->num_param = num_param; + state->param_sent = 0; + state->rparam.max = max_param; - return NT_STATUS_OK; -} + state->data = data; + state->num_data = num_data; + state->data_sent = 0; + state->rdata.max = max_data; -static NTSTATUS cli_trans_pull_blob(TALLOC_CTX *mem_ctx, - struct trans_recvblob *blob, - uint32_t total, uint32_t thistime, - uint8_t *buf, uint32_t displacement) -{ - if (blob->data == NULL) { - if (total > blob->max) { - return NT_STATUS_INVALID_NETWORK_RESPONSE; - } - blob->total = total; - blob->data = TALLOC_ARRAY(mem_ctx, uint8_t, total); - if (blob->data == NULL) { - return NT_STATUS_NO_MEMORY; - } - } + cli_trans_format(state, &wct, &iov_count); - if (total > blob->total) { - return NT_STATUS_INVALID_NETWORK_RESPONSE; + subreq = cli_smb_req_create(state, ev, cli, cmd, 0, wct, state->vwv, + iov_count, state->iov); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); } - - if (thistime) { - memcpy(blob->data + displacement, buf, thistime); - blob->received += thistime; + state->mid = cli_smb_req_mid(subreq); + if (!cli_smb_req_send(subreq)) { + tevent_req_nterror(req, NT_STATUS_NO_MEMORY); + return tevent_req_post(req, state->ev); } - - return NT_STATUS_OK; + cli_state_seqnum_persistent(cli, state->mid); + tevent_req_set_callback(subreq, cli_trans_done, req); + return req; } -static void cli_trans_recv_helper(struct async_req *req) +static void cli_trans_done(struct tevent_req *subreq) { - struct cli_request *cli_req = talloc_get_type_abort( - req->private_data, struct cli_request); - struct cli_trans_state *state = talloc_get_type_abort( - cli_req->recv_helper.priv, struct cli_trans_state); + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_trans_state *state = tevent_req_data( + req, struct cli_trans_state); + NTSTATUS status; + bool sent_all; + uint8_t wct; + uint16_t *vwv; + uint32_t num_bytes; + uint8_t *bytes; uint8_t num_setup = 0; uint16_t *setup = NULL; uint32_t total_param = 0; @@ -1122,16 +1114,8 @@ static void cli_trans_recv_helper(struct async_req *req) uint32_t data_disp = 0; uint8_t *param = NULL; uint8_t *data = NULL; - bool sent_all; - NTSTATUS status; - sent_all = (state->param_sent == state->num_param) - && (state->data_sent == state->num_data); - - status = cli_pull_trans( - req, cli_req, state->cmd, !sent_all, &num_setup, &setup, - &total_param, &num_param, ¶m_disp, ¶m, - &total_data, &num_data, &data_disp, &data); + status = cli_smb_recv(subreq, 0, &wct, &vwv, &num_bytes, &bytes); /* * We can receive something like STATUS_MORE_ENTRIES, so don't use @@ -1139,30 +1123,43 @@ static void cli_trans_recv_helper(struct async_req *req) */ if (NT_STATUS_IS_ERR(status)) { - async_req_nterror(req, status); - return; + goto fail; } - if (!sent_all) { - cli_trans_ship_rest(req, state); - return; + sent_all = ((state->param_sent == state->num_param) + && (state->data_sent == state->num_data)); + + status = cli_pull_trans( + cli_smb_inbuf(subreq), wct, vwv, num_bytes, bytes, + state->cmd, !sent_all, &num_setup, &setup, + &total_param, &num_param, ¶m_disp, ¶m, + &total_data, &num_data, &data_disp, &data); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; } - /* - * We've just received a real response. This means that we don't need - * the secondary cli_request structures anymore, they have all been - * shipped to the server. - */ - TALLOC_FREE(state->secondary_request_ctx); + if (!sent_all) { + int iov_count; - if (num_setup != 0) { - TALLOC_FREE(state->rsetup); - state->rsetup = (uint16_t *)TALLOC_MEMDUP( - state, setup, sizeof(uint16_t) * num_setup); - if (state->rsetup == NULL) { - async_req_nterror(req, NT_STATUS_NO_MEMORY); + TALLOC_FREE(subreq); + + cli_trans_format(state, &wct, &iov_count); + + subreq = cli_smb_req_create(state, state->ev, state->cli, + state->cmd + 1, 0, wct, state->vwv, + iov_count, state->iov); + if (tevent_req_nomem(subreq, req)) { return; } + cli_smb_req_set_mid(subreq, state->mid); + + if (!cli_smb_req_send(subreq)) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + tevent_req_set_callback(subreq, cli_trans_done, req); + return; } status = cli_trans_pull_blob( @@ -1171,8 +1168,7 @@ static void cli_trans_recv_helper(struct async_req *req) if (!NT_STATUS_IS_OK(status)) { DEBUG(10, ("Pulling params failed: %s\n", nt_errstr(status))); - async_req_nterror(req, status); - return; + goto fail; } status = cli_trans_pull_blob( @@ -1181,149 +1177,39 @@ static void cli_trans_recv_helper(struct async_req *req) if (!NT_STATUS_IS_OK(status)) { DEBUG(10, ("Pulling data failed: %s\n", nt_errstr(status))); - async_req_nterror(req, status); - return; + goto fail; } if ((state->rparam.total == state->rparam.received) && (state->rdata.total == state->rdata.received)) { - async_req_done(req); - } -} - -struct async_req *cli_trans_send( - TALLOC_CTX *mem_ctx, struct event_context *ev, - struct cli_state *cli, uint8_t trans_cmd, - const char *pipe_name, uint16_t fid, uint16_t function, int flags, - uint16_t *setup, uint8_t num_setup, uint8_t max_setup, - uint8_t *param, uint32_t num_param, uint32_t max_param, - uint8_t *data, uint32_t num_data, uint32_t max_data) -{ - struct async_req *req; - struct cli_request *cli_req; - struct cli_trans_state *state; - - /* - * We can't use it in a chained request chain, we'd get the offset - * calculations wrong. - */ - - if (cli_in_chain(cli)) { - return NULL; - } - - if ((trans_cmd == SMBtrans) || (trans_cmd == SMBtrans2)) { - if ((num_param > 0xffff) || (max_param > 0xffff) - || (num_data > 0xffff) || (max_data > 0xffff)) { - DEBUG(3, ("Attempt to send invalid trans2 request " - "(setup %u, params %u/%u, data %u/%u)\n", - (unsigned)num_setup, - (unsigned)num_param, (unsigned)max_param, - (unsigned)num_data, (unsigned)max_data)); - return NULL; - } - } - - state = talloc(mem_ctx, struct cli_trans_state); - if (state == NULL) { - goto nomem; - } - - state->cli = cli; - state->ev = ev; - state->cmd = trans_cmd; - state->num_rsetup = 0; - state->rsetup = NULL; - ZERO_STRUCT(state->rparam); - ZERO_STRUCT(state->rdata); - state->secondary_request_ctx = NULL; - - if (trans_cmd == SMBtrans) { - state->pipe_name = talloc_strdup(state, pipe_name); - if (state->pipe_name == NULL) { - goto nomem; - } - } - if (trans_cmd == SMBtrans2) { - state->fid = fid; - } - if (trans_cmd == SMBnttrans) { - state->function = function; - } - - state->flags = flags; - - if (setup != NULL) { - state->setup = (uint16_t *)TALLOC_MEMDUP( - state, setup, sizeof(*setup) * num_setup); - if (state->setup == NULL) { - goto nomem; - } - state->num_setup = num_setup; - } else { - state->setup = NULL; - state->num_setup = 0; - } - - state->max_setup = max_setup; - - if (param != NULL) { - state->param = (uint8_t *)TALLOC_MEMDUP(state, param, - num_param); - if (state->param == NULL) { - goto nomem; - } - state->num_param = num_param; - } else { - state->param = NULL; - state->num_param = 0; - } - - state->param_sent = 0; - state->rparam.max = max_param; - - if (data != NULL) { - state->data = (uint8_t *)TALLOC_MEMDUP(state, data, num_data); - if (state->data == NULL) { - goto nomem; - } - state->num_data = num_data; - } else { - state->data = NULL; - state->num_data = 0; + TALLOC_FREE(subreq); + cli_state_seqnum_remove(state->cli, state->mid); + tevent_req_done(req); + return; } - state->data_sent = 0; - state->rdata.max = max_data; - - req = cli_ship_trans(state, state); - if (req == NULL) { - goto nomem; + if (!cli_smb_req_set_pending(subreq)) { + status = NT_STATUS_NO_MEMORY; + goto fail; } + return; - cli_req = talloc_get_type_abort(req->private_data, struct cli_request); - cli_req->recv_helper.fn = cli_trans_recv_helper; - cli_req->recv_helper.priv = state; - - return req; - - nomem: - TALLOC_FREE(state); - return NULL; + fail: + cli_state_seqnum_remove(state->cli, state->mid); + TALLOC_FREE(subreq); + tevent_req_nterror(req, status); } -NTSTATUS cli_trans_recv(struct async_req *req, TALLOC_CTX *mem_ctx, +NTSTATUS cli_trans_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, uint16_t **setup, uint8_t *num_setup, uint8_t **param, uint32_t *num_param, uint8_t **data, uint32_t *num_data) { - struct cli_request *cli_req = talloc_get_type_abort( - req->private_data, struct cli_request); - struct cli_trans_state *state = talloc_get_type_abort( - cli_req->recv_helper.priv, struct cli_trans_state); + struct cli_trans_state *state = tevent_req_data( + req, struct cli_trans_state); NTSTATUS status; - if (async_req_is_nterror(req, &status)) { + if (tevent_req_is_nterror(req, &status)) { return status; } @@ -1364,19 +1250,20 @@ NTSTATUS cli_trans(TALLOC_CTX *mem_ctx, struct cli_state *cli, { TALLOC_CTX *frame = talloc_stackframe(); struct event_context *ev; - struct async_req *req; - NTSTATUS status = NT_STATUS_NO_MEMORY; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_OK; - if (cli->fd_event != NULL) { + if (cli_has_async_calls(cli)) { /* * Can't use sync call while an async call is in flight */ - cli_set_error(cli, NT_STATUS_INVALID_PARAMETER); + status = NT_STATUS_INVALID_PARAMETER; goto fail; } ev = event_context_init(frame); if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; goto fail; } @@ -1386,16 +1273,21 @@ NTSTATUS cli_trans(TALLOC_CTX *mem_ctx, struct cli_state *cli, param, num_param, max_param, data, num_data, max_data); if (req == NULL) { + status = NT_STATUS_NO_MEMORY; goto fail; } - while (req->state < ASYNC_REQ_DONE) { - event_loop_once(ev); + if (!tevent_req_poll(req, ev)) { + status = map_nt_error_from_unix(errno); + goto fail; } status = cli_trans_recv(req, mem_ctx, rsetup, num_rsetup, rparam, num_rparam, rdata, num_rdata); fail: TALLOC_FREE(frame); + if (!NT_STATUS_IS_OK(status)) { + cli_set_error(cli, status); + } return status; } |