diff options
| -rw-r--r-- | source3/include/proto.h | 21 | ||||
| -rw-r--r-- | source3/libsmb/clitrans.c | 701 | 
2 files changed, 722 insertions, 0 deletions
diff --git a/source3/include/proto.h b/source3/include/proto.h index d51be6781d..2901911c70 100644 --- a/source3/include/proto.h +++ b/source3/include/proto.h @@ -4732,6 +4732,27 @@ bool cli_send_nt_trans(struct cli_state *cli,  bool cli_receive_nt_trans(struct cli_state *cli,  			  char **param, unsigned int *param_len,  			  char **data, unsigned int *data_len); +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); +NTSTATUS cli_trans_recv(struct async_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); +NTSTATUS cli_trans(TALLOC_CTX *mem_ctx, 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, +		   uint16_t **rsetup, uint8_t *num_rsetup, +		   uint8_t **rparam, uint32_t *num_rparam, +		   uint8_t **rdata, uint32_t *num_rdata);  /* The following definitions come from libsmb/conncache.c  */ diff --git a/source3/libsmb/clitrans.c b/source3/libsmb/clitrans.c index 4bb70f1a08..5b84e68fc4 100644 --- a/source3/libsmb/clitrans.c +++ b/source3/libsmb/clitrans.c @@ -701,3 +701,704 @@ bool cli_receive_nt_trans(struct cli_state *cli,  	client_set_trans_sign_state_off(cli, SVAL(cli->inbuf,smb_mid));  	return ret;  } + +struct trans_recvblob { +	uint8_t *data; +	uint32_t max, total, received; +}; + +struct cli_trans_state { +	struct cli_state *cli; +	struct event_context *ev; +	uint8_t cmd; +	uint16_t mid; +	const char *pipe_name; +	uint16_t fid; +	uint16_t function; +	int flags; +	uint16_t *setup; +	uint8_t num_setup, max_setup; +	uint8_t *param; +	uint32_t num_param, param_sent; +	uint8_t *data; +	uint32_t num_data, data_sent; + +	uint8_t num_rsetup; +	uint16_t *rsetup; +	struct trans_recvblob rparam; +	struct trans_recvblob rdata; + +	TALLOC_CTX *secondary_request_ctx; +}; + +static void cli_trans_recv_helper(struct async_req *req); + +static struct async_req *cli_ship_trans(TALLOC_CTX *mem_ctx, +					struct cli_trans_state *state) +{ +	TALLOC_CTX *frame; +	struct async_req *result = NULL; +	struct cli_request *cli_req; +	uint8_t wct; +	uint16_t *vwv; +	uint8_t *bytes = NULL; +	uint16_t param_offset; +	uint16_t this_param = 0; +	uint16_t this_data = 0; +	uint32_t useable_space; +	uint8_t cmd; + +	frame = talloc_stackframe(); + +	cmd = state->cmd; + +	if ((state->param_sent != 0) || (state->data_sent != 0)) { +		/* The secondary commands are one after the primary ones */ +		cmd += 1; +	} + +	param_offset = smb_size - 4; + +	switch (cmd) { +	case SMBtrans: +		bytes = TALLOC_ZERO_P(talloc_tos(), uint8_t); /* padding */ +		if (bytes == NULL) { +			goto fail; +		} +		bytes = smb_bytes_push_str( +			bytes, (state->cli->capabilities & CAP_UNICODE) != 0, +			state->pipe_name); +		if (bytes == NULL) { +			goto fail; +		} +		wct = 14 + state->num_setup; +		param_offset += talloc_get_size(bytes); +		break; +	case SMBtrans2: +		bytes = TALLOC_ARRAY(talloc_tos(), uint8_t, 3); /* padding */ +		if (bytes == NULL) { +			goto fail; +		} +		bytes[0] = 0; +		bytes[1] = 'D';	/* Copy this from "old" 3.0 behaviour */ +		bytes[2] = ' '; +		wct = 14 + state->num_setup; +		param_offset += talloc_get_size(bytes); +		break; +	case SMBtranss: +		wct = 8; +		break; +	case SMBtranss2: +		wct = 9; +		break; +	case SMBnttrans: +		wct = 19 + state->num_setup; +		break; +	case SMBnttranss: +		wct = 18; +		break; +	default: +		goto fail; +	} + +	useable_space = state->cli->max_xmit - smb_size - sizeof(uint16_t)*wct; + +	if (state->param_sent < state->num_param) { +		this_param = MIN(state->num_param - state->param_sent, +				 useable_space); +	} + +	if (state->data_sent < state->num_data) { +		this_data = MIN(state->num_data - state->data_sent, +				useable_space - this_param); +	} + +	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, " +		   "param_total=%u, this_param=%u, max_param=%u, " +		   "data_total=%u, this_data=%u, max_data=%u, " +		   "param_offset=%u, param_disp=%u, data_disp=%u\n", +		   (unsigned)state->num_setup, (unsigned)state->max_setup, +		   (unsigned)state->num_param, (unsigned)this_param, +		   (unsigned)state->rparam.max, +		   (unsigned)state->num_data, (unsigned)this_data, +		   (unsigned)state->rdata.max, +		   (unsigned)param_offset, +		   (unsigned)state->param_sent, (unsigned)state->data_sent)); + +	switch (cmd) { +	case SMBtrans: +	case SMBtrans2: +		SSVAL(vwv + 0, 0, state->num_param); +		SSVAL(vwv + 1, 0, state->num_data); +		SSVAL(vwv + 2, 0, state->rparam.max); +		SSVAL(vwv + 3, 0, state->rdata.max); +		SCVAL(vwv + 4, 0, state->max_setup); +		SCVAL(vwv + 4, 1, 0);	/* reserved */ +		SSVAL(vwv + 5, 0, state->flags); +		SIVAL(vwv + 6, 0, 0);	/* timeout */ +		SSVAL(vwv + 8, 0, 0);	/* reserved */ +		SSVAL(vwv + 9, 0, this_param); +		SSVAL(vwv +10, 0, param_offset); +		SSVAL(vwv +11, 0, this_data); +		SSVAL(vwv +12, 0, param_offset + this_param); +		SCVAL(vwv +13, 0, state->num_setup); +		SCVAL(vwv +13, 1, 0);	/* reserved */ +		memcpy(vwv + 14, state->setup, +		       sizeof(uint16_t) * state->num_setup); +		break; +	case SMBtranss: +	case SMBtranss2: +		SSVAL(vwv + 0, 0, state->num_param); +		SSVAL(vwv + 1, 0, state->num_data); +		SSVAL(vwv + 2, 0, this_param); +		SSVAL(vwv + 3, 0, param_offset); +		SSVAL(vwv + 4, 0, state->param_sent); +		SSVAL(vwv + 5, 0, this_data); +		SSVAL(vwv + 6, 0, param_offset + this_param); +		SSVAL(vwv + 7, 0, state->data_sent); +		if (cmd == SMBtranss2) { +			SSVAL(vwv + 8, 0, state->fid); +		} +		break; +	case SMBnttrans: +		SCVAL(vwv,  0, state->max_setup); +		SSVAL(vwv,  1, 0); /* reserved */ +		SIVAL(vwv,  3, state->num_param); +		SIVAL(vwv,  7, state->num_data); +		SIVAL(vwv, 11, state->rparam.max); +		SIVAL(vwv, 15, state->rdata.max); +		SIVAL(vwv, 19, this_param); +		SIVAL(vwv, 23, param_offset); +		SIVAL(vwv, 27, this_data); +		SIVAL(vwv, 31, param_offset + this_param); +		SCVAL(vwv, 35, state->num_setup); +		SSVAL(vwv, 36, state->function); +		memcpy(vwv + 19, state->setup, +		       sizeof(uint16_t) * state->num_setup); +		break; +	case SMBnttranss: +		SSVAL(vwv,  0, 0); /* reserved */ +		SCVAL(vwv,  2, 0); /* reserved */ +		SIVAL(vwv,  3, state->num_param); +		SIVAL(vwv,  7, state->num_data); +		SIVAL(vwv, 11, this_param); +		SIVAL(vwv, 15, param_offset); +		SIVAL(vwv, 19, state->param_sent); +		SIVAL(vwv, 23, this_data); +		SIVAL(vwv, 27, param_offset + this_param); +		SIVAL(vwv, 31, state->data_sent); +		SCVAL(vwv, 35, 0); /* reserved */ +		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, +					  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; +	} 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, 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; +		client_set_trans_sign_state_off(state->cli, state->mid); +		cli_chain_uncork(state->cli); +	} + +	client_set_trans_sign_state_on(state->cli, state->mid); + + fail: +	TALLOC_FREE(frame); +	return result; +} + +static void cli_trans_ship_rest(struct async_req *req, +				struct cli_trans_state *state) +{ +	state->secondary_request_ctx = talloc_new(state); +	if (state->secondary_request_ctx == NULL) { +		async_req_error(req, NT_STATUS_NO_MEMORY); +		return; +	} + +	while ((state->param_sent < state->num_param) +	       || (state->data_sent < state->num_data)) { +		struct async_req *cli_req; + +		cli_req = cli_ship_trans(state->secondary_request_ctx, state); +		if (cli_req == NULL) { +			async_req_error(req, NT_STATUS_NO_MEMORY); +			return; +		} +	} +} + +static bool cli_trans_oob(uint32_t bufsize, uint32_t offset, uint32_t length) +{ +	if ((offset + length < offset) || (offset + length < length)) { +		/* wrap */ +		return true; +	} +	if ((offset > bufsize) || (offset + length > bufsize)) { +		/* overflow */ +		return true; +	} +	return false; +} + +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. +	 */ + +	if (NT_STATUS_IS_ERR(status)) { +		return status; +	} + +	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; + +	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 (cli_trans_oob(smb_len(cli_req->inbuf), param_ofs, *pnum_param) +	    || cli_trans_oob(*ptotal_param, *pparam_disp, *pnum_param) +	    || cli_trans_oob(smb_len(cli_req->inbuf), data_ofs, *pnum_data) +	    || cli_trans_oob(*ptotal_data, *pdata_disp, *pnum_data)) { +		return NT_STATUS_INVALID_NETWORK_RESPONSE; +	} + +	*pparam = (uint8_t *)cli_req->inbuf + 4 + param_ofs; +	*pdata = (uint8_t *)cli_req->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) +{ +	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_recv_helper(struct async_req *req) +{ +	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); +	uint8_t num_setup; +	uint16_t *setup; +	uint32_t total_param, num_param, param_disp; +	uint32_t total_data, num_data, data_disp; +	uint8_t *param, *data; +	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); + +	/* +	 * We can receive something like STATUS_MORE_ENTRIES, so don't use +	 * !NT_STATUS_IS_OK(status) here. +	 */ + +	if (NT_STATUS_IS_ERR(status)) { +		async_req_error(req, status); +		return; +	} + +	if (!sent_all) { +		cli_trans_ship_rest(req, state); +		return; +	} + +	/* +	 * 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 (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_error(req, NT_STATUS_NO_MEMORY); +			return; +		} +	} + +	status = cli_trans_pull_blob( +		state, &state->rparam, total_param, num_param, param, +		param_disp); + +	if (!NT_STATUS_IS_OK(status)) { +		DEBUG(10, ("Pulling params failed: %s\n", nt_errstr(status))); +		async_req_error(req, status); +		return; +	} + +	status = cli_trans_pull_blob( +		state, &state->rdata, total_data, num_data, data, +		data_disp); + +	if (!NT_STATUS_IS_OK(status)) { +		DEBUG(10, ("Pulling data failed: %s\n", nt_errstr(status))); +		async_req_error(req, status); +		return; +	} + +	if ((state->rparam.total == state->rparam.received) +	    && (state->rdata.total == state->rdata.received)) { +		client_set_trans_sign_state_off(state->cli, state->mid); +		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; +	} + +	state->data_sent = 0; +	state->rdata.max = max_data; + +	req = cli_ship_trans(state, state); +	if (req == NULL) { +		goto nomem; +	} + +	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; +} + +NTSTATUS cli_trans_recv(struct async_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); + +	SMB_ASSERT(req->state >= ASYNC_REQ_DONE); +	if (req->state == ASYNC_REQ_ERROR) { +		return req->status; +	} + +	if (setup != NULL) { +		*setup = talloc_move(mem_ctx, &state->rsetup); +		*num_setup = state->num_rsetup; +	} else { +		TALLOC_FREE(state->rsetup); +	} + +	if (param != NULL) { +		*param = talloc_move(mem_ctx, &state->rparam.data); +		*num_param = state->rparam.total; +	} else { +		TALLOC_FREE(state->rparam.data); +	} + +	if (data != NULL) { +		*data = talloc_move(mem_ctx, &state->rdata.data); +		*num_data = state->rdata.total; +	} else { +		TALLOC_FREE(state->rdata.data); +	} + +	return NT_STATUS_OK; +} + +NTSTATUS cli_trans(TALLOC_CTX *mem_ctx, 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, +		   uint16_t **rsetup, uint8_t *num_rsetup, +		   uint8_t **rparam, uint32_t *num_rparam, +		   uint8_t **rdata, uint32_t *num_rdata) +{ +	TALLOC_CTX *frame = talloc_stackframe(); +	struct event_context *ev; +	struct async_req *req; +	NTSTATUS status = NT_STATUS_NO_MEMORY; + +	if (cli->fd_event != NULL) { +		/* +		 * Can't use sync call while an async call is in flight +		 */ +		cli_set_error(cli, NT_STATUS_INVALID_PARAMETER); +		goto fail; +	} + +	ev = event_context_init(frame); +	if (ev == NULL) { +		goto fail; +	} + +	req = cli_trans_send(frame, ev, cli, trans_cmd, +			     pipe_name, fid, function, flags, +			     setup, num_setup, max_setup, +			     param, num_param, max_param, +			     data, num_data, max_data); +	if (req == NULL) { +		goto fail; +	} + +	while (req->state < ASYNC_REQ_DONE) { +		event_loop_once(ev); +	} + +	status = cli_trans_recv(req, mem_ctx, rsetup, num_rsetup, +				rparam, num_rparam, rdata, num_rdata); + fail: +	TALLOC_FREE(frame); +	return status; +}  | 
