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 031ab233dd..1053de2942 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 6a6e1a2faa..9b564fc48e 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 e764147432..4d6c32edfa 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 75a5b30e30..d159ffbac3 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},  | 
