diff options
-rw-r--r-- | source3/include/async_smb.h | 21 | ||||
-rw-r--r-- | source3/include/client.h | 8 | ||||
-rw-r--r-- | source3/libsmb/async_smb.c | 203 | ||||
-rw-r--r-- | source3/torture/torture.c | 80 |
4 files changed, 133 insertions, 179 deletions
diff --git a/source3/include/async_smb.h b/source3/include/async_smb.h index 031ab233dd4..1053de29422 100644 --- a/source3/include/async_smb.h +++ b/source3/include/async_smb.h @@ -22,6 +22,13 @@ #include "includes.h" +/** + * struct cli_request is the state holder for an async client request we sent + * to the server. It can consist of more than one struct async_req that we + * have to server if the application did a cli_chain_cork() and + * cli_chain_uncork() + */ + struct cli_request { /** * "prev" and "next" form the doubly linked list in @@ -30,9 +37,15 @@ struct cli_request { struct cli_request *prev, *next; /** - * "our" struct async_req; + * num_async: How many chained requests do we serve? + */ + int num_async; + + /** + * async: This is the list of chained requests that were queued up by + * cli_request_chain before we sent out this request */ - struct async_req *async; + struct async_req **async; /** * The client connection for this request @@ -92,6 +105,10 @@ struct async_req *cli_request_send(TALLOC_CTX *mem_ctx, uint8_t wct, const uint16_t *vwv, uint16_t num_bytes, const uint8_t *bytes); +bool cli_chain_cork(struct cli_state *cli, struct event_context *ev, + size_t size_hint); +void cli_chain_uncork(struct cli_state *cli); + /* * Convenience function to get the SMB part out of an async_req */ diff --git a/source3/include/client.h b/source3/include/client.h index 6a6e1a2faa0..9b564fc48e7 100644 --- a/source3/include/client.h +++ b/source3/include/client.h @@ -208,7 +208,8 @@ struct cli_state { * fd_event is around while we have async requests outstanding or are * building a chained request. * - * (fd_event!=NULL) && (outstanding_request!=NULL) + * (fd_event!=NULL) && + * ((outstanding_request!=NULL)||(chain_accumulator!=NULL)) * * should always be true, as well as the reverse: If both cli_request * pointers are NULL, no fd_event is around. @@ -220,6 +221,11 @@ struct cli_state { * A linked list of requests that are waiting for a reply */ struct cli_request *outstanding_requests; + + /** + * The place to build up the list of chained requests. + */ + struct cli_request *chain_accumulator; }; typedef struct file_info { diff --git a/source3/libsmb/async_smb.c b/source3/libsmb/async_smb.c index e7641474328..4d6c32edfa2 100644 --- a/source3/libsmb/async_smb.c +++ b/source3/libsmb/async_smb.c @@ -135,8 +135,6 @@ static int cli_request_destructor(struct cli_request *req) return 0; } -#if 0 - /** * Is the SMB command able to hold an AND_X successor * @param[in] cmd The SMB command in question @@ -667,176 +665,6 @@ NTSTATUS cli_pull_reply(struct async_req *req, return NT_STATUS_OK; } -#endif - -/* - * Create a fresh async smb request - */ - -static struct async_req *cli_request_new(TALLOC_CTX *mem_ctx, - struct event_context *ev, - struct cli_state *cli, - uint8_t num_words, size_t num_bytes, - struct cli_request **preq) -{ - struct async_req *result; - struct cli_request *cli_req; - size_t bufsize = smb_size + num_words * 2 + num_bytes; - - result = async_req_new(mem_ctx, ev); - if (result == NULL) { - return NULL; - } - - cli_req = (struct cli_request *)talloc_size( - result, sizeof(*cli_req) + bufsize); - if (cli_req == NULL) { - TALLOC_FREE(result); - return NULL; - } - talloc_set_name_const(cli_req, "struct cli_request"); - result->private_data = cli_req; - result->print = cli_request_print; - - cli_req->async = result; - cli_req->cli = cli; - cli_req->outbuf = ((char *)cli_req + sizeof(*cli_req)); - cli_req->sent = 0; - cli_req->mid = cli_new_mid(cli); - cli_req->inbuf = NULL; - cli_req->enc_state = NULL; - - SCVAL(cli_req->outbuf, smb_wct, num_words); - SSVAL(cli_req->outbuf, smb_vwv + num_words * 2, num_bytes); - - DLIST_ADD_END(cli->outstanding_requests, cli_req, - struct cli_request *); - talloc_set_destructor(cli_req, cli_request_destructor); - - DEBUG(10, ("cli_request_new: mid=%d\n", cli_req->mid)); - - *preq = cli_req; - return result; -} - -/* - * Ship a new smb request to the server - */ -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, - uint16_t num_bytes, const uint8_t *bytes) -{ - struct async_req *result; - struct cli_request *req; - - 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 NULL; - } - } - - result = cli_request_new(mem_ctx, ev, cli, wct, num_bytes, &req); - if (result == NULL) { - DEBUG(0, ("cli_request_new failed\n")); - TALLOC_FREE(cli->fd_event); - return NULL; - } - - cli_set_message(req->outbuf, wct, num_bytes, false); - SCVAL(req->outbuf, smb_com, smb_command); - SSVAL(req->outbuf, smb_tid, cli->cnum); - cli_setup_packet_buf(cli, req->outbuf); - - memcpy(req->outbuf + smb_vwv0, vwv, sizeof(uint16_t) * wct); - memcpy(smb_buf(req->outbuf), bytes, num_bytes); - SSVAL(req->outbuf, smb_mid, req->mid); - SCVAL(cli->outbuf, smb_flg, - CVAL(cli->outbuf,smb_flg) | additional_flags); - - cli_calculate_sign_mac(cli, req->outbuf); - - if (cli_encryption_on(cli)) { - NTSTATUS status; - char *enc_buf; - - status = cli_encrypt_message(cli, req->outbuf, &enc_buf); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(0, ("Error in encrypting client message. " - "Error %s\n", nt_errstr(status))); - TALLOC_FREE(req); - return NULL; - } - req->outbuf = enc_buf; - req->enc_state = cli->trans_enc_state; - } - - event_fd_set_writeable(cli->fd_event); - - return result; -} - -/** - * @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) -{ - struct cli_request *cli_req = cli_request_get(req); - uint8_t wct, cmd; - uint16_t num_bytes; - size_t wct_ofs, bytes_offset; - NTSTATUS status; - - status = cli_pull_error(cli_req->inbuf); - - if (NT_STATUS_IS_ERR(status)) { - cli_set_error(cli_req->cli, status); - return status; - } - - cmd = CVAL(cli_req->inbuf, smb_com); - wct_ofs = smb_wct; - - wct = CVAL(cli_req->inbuf, wct_ofs); - - bytes_offset = wct_ofs + 1 + wct * sizeof(uint16_t); - num_bytes = SVAL(cli_req->inbuf, bytes_offset); - - /* - * 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)) - || (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; -} - /** * Convenience function to get the SMB part out of an async_req * @param[in] req The request to look at @@ -862,8 +690,11 @@ static void handle_incoming_pdu(struct cli_state *cli) uint16_t mid; size_t raw_pdu_len, buf_len, pdu_len, rest_len; char *pdu; + int i; NTSTATUS status; + int num_async; + /* * The encrypted PDU len might differ from the unencrypted one */ @@ -976,7 +807,24 @@ static void handle_incoming_pdu(struct cli_state *cli) req->inbuf = talloc_move(req, &pdu); - async_req_done(req->async); + /* + * 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. + */ + + num_async = req->num_async; + + 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) { + async_req_done(req->async[i]); + } + } return; invalidate_requests: @@ -985,7 +833,7 @@ static void handle_incoming_pdu(struct cli_state *cli) nt_errstr(status))); for (req = cli->outstanding_requests; req; req = req->next) { - async_req_error(req->async, status); + async_req_error(req->async[0], status); } return; } @@ -1101,8 +949,11 @@ static void cli_state_handler(struct event_context *event_ctx, sock_error: for (req = cli->outstanding_requests; req; req = req->next) { - req->async->state = ASYNC_REQ_ERROR; - req->async->status = map_nt_error_from_unix(errno); + int i; + for (i=0; i<req->num_async; i++) { + req->async[i]->state = ASYNC_REQ_ERROR; + req->async[i]->status = map_nt_error_from_unix(errno); + } } TALLOC_FREE(cli->fd_event); close(cli->fd); diff --git a/source3/torture/torture.c b/source3/torture/torture.c index 75a5b30e304..d159ffbac35 100644 --- a/source3/torture/torture.c +++ b/source3/torture/torture.c @@ -4897,6 +4897,85 @@ static bool subst_test(const char *str, const char *user, const char *domain, return result; } +static void chain1_open_completion(struct async_req *req) +{ + int fnum; + NTSTATUS status; + + status = cli_open_recv(req, &fnum); + TALLOC_FREE(req); + + d_printf("cli_open_recv returned %s: %d\n", + nt_errstr(status), + NT_STATUS_IS_OK(status) ? fnum : -1); +} + +static void chain1_read_completion(struct async_req *req) +{ + NTSTATUS status; + ssize_t received; + uint8_t *rcvbuf; + + status = cli_read_andx_recv(req, &received, &rcvbuf); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(req); + d_printf("cli_read_andx_recv returned %s\n", + nt_errstr(status)); + return; + } + + d_printf("got %d bytes: %.*s\n", (int)received, (int)received, + (char *)rcvbuf); + TALLOC_FREE(req); +} + +static void chain1_close_completion(struct async_req *req) +{ + NTSTATUS status; + + status = cli_close_recv(req); + *((bool *)(req->async.priv)) = true; + + TALLOC_FREE(req); + + d_printf("cli_close returned %s\n", nt_errstr(status)); +} + +static bool run_chain1(int dummy) +{ + struct cli_state *cli1; + struct event_context *evt = event_context_init(NULL); + struct async_req *reqs[4]; + bool done = false; + + printf("starting chain1 test\n"); + if (!torture_open_connection(&cli1, 0)) { + return False; + } + + cli_sockopt(cli1, sockops); + + cli_chain_cork(cli1, evt, 0); + reqs[0] = cli_open_send(talloc_tos(), evt, cli1, "\\test", + O_CREAT|O_RDWR, 0); + reqs[0]->async.fn = chain1_open_completion; + reqs[1] = cli_read_andx_send(talloc_tos(), evt, cli1, 0, 0, 10); + reqs[1]->async.fn = chain1_read_completion; + reqs[2] = cli_read_andx_send(talloc_tos(), evt, cli1, 0, 1, 10); + reqs[2]->async.fn = chain1_read_completion; + reqs[3] = cli_close_send(talloc_tos(), evt, cli1, 0); + reqs[3]->async.fn = chain1_close_completion; + reqs[3]->async.priv = (void *)&done; + cli_chain_uncork(cli1); + + while (!done) { + event_loop_once(evt); + } + + torture_close_connection(cli1); + return True; +} + static bool run_local_substitute(int dummy) { bool ok = true; @@ -5394,6 +5473,7 @@ static struct { {"FDSESS", run_fdsesstest, 0}, { "EATEST", run_eatest, 0}, { "SESSSETUP_BENCH", run_sesssetup_bench, 0}, + { "CHAIN1", run_chain1, 0}, { "LOCAL-SUBSTITUTE", run_local_substitute, 0}, { "LOCAL-GENCACHE", run_local_gencache, 0}, { "LOCAL-RBTREE", run_local_rbtree, 0}, |