summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source3/include/proto.h21
-rw-r--r--source3/libsmb/clitrans.c701
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, &param_disp, &param,
+ &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;
+}