summaryrefslogtreecommitdiff
path: root/source3/libsmb
diff options
context:
space:
mode:
authorJelmer Vernooij <jelmer@samba.org>2008-09-09 19:27:00 +0200
committerJelmer Vernooij <jelmer@samba.org>2008-09-09 19:27:00 +0200
commitc82f268a1c1c5eff1cbdb410da3c717464bdbdd7 (patch)
tree00f3aaee7050f96229d3a74b052ca1edf7e5e2b9 /source3/libsmb
parent372ecdfde372a8bdd666cdf009262776eddd28d4 (diff)
parent7965249bd613eb41eeca24ba8271189e2f90257c (diff)
downloadsamba-c82f268a1c1c5eff1cbdb410da3c717464bdbdd7.tar.gz
samba-c82f268a1c1c5eff1cbdb410da3c717464bdbdd7.tar.bz2
samba-c82f268a1c1c5eff1cbdb410da3c717464bdbdd7.zip
Merge branch 'v3-devel' of ssh://git.samba.org/data/git/samba into v3-devel
(This used to be commit c3d1b0097590566417bda3fa33a64e994c9c7526)
Diffstat (limited to 'source3/libsmb')
-rw-r--r--source3/libsmb/async_smb.c17
-rw-r--r--source3/libsmb/clifile.c2
-rw-r--r--source3/libsmb/clirap.c40
-rw-r--r--source3/libsmb/clisecdesc.c37
-rw-r--r--source3/libsmb/clitrans.c701
5 files changed, 753 insertions, 44 deletions
diff --git a/source3/libsmb/async_smb.c b/source3/libsmb/async_smb.c
index 435c8c1cb9..eedc7d4481 100644
--- a/source3/libsmb/async_smb.c
+++ b/source3/libsmb/async_smb.c
@@ -137,6 +137,21 @@ static int cli_request_destructor(struct cli_request *req)
}
/**
+ * 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);
+}
+
+/**
* Is the SMB command able to hold an AND_X successor
* @param[in] cmd The SMB command in question
* @retval Can we add a chained request after "cmd"?
@@ -422,7 +437,6 @@ bool cli_chain_cork(struct cli_state *cli, struct event_context *ev,
cli_setup_packet_buf(cli, req->outbuf);
req->mid = cli_new_mid(cli);
- SSVAL(req->outbuf, smb_mid, req->mid);
cli->chain_accumulator = req;
@@ -453,6 +467,7 @@ void cli_chain_uncork(struct cli_state *cli)
cli->chain_accumulator = NULL;
+ SSVAL(req->outbuf, smb_mid, req->mid);
smb_setlen(req->outbuf, talloc_get_size(req->outbuf) - 4);
cli_calculate_sign_mac(cli, req->outbuf);
diff --git a/source3/libsmb/clifile.c b/source3/libsmb/clifile.c
index a8b3440513..d3819af444 100644
--- a/source3/libsmb/clifile.c
+++ b/source3/libsmb/clifile.c
@@ -781,7 +781,7 @@ int cli_nt_create(struct cli_state *cli, const char *fname, uint32 DesiredAccess
FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, 0x0, 0x0);
}
-static uint8_t *smb_bytes_push_str(uint8_t *buf, bool ucs2, const char *str)
+uint8_t *smb_bytes_push_str(uint8_t *buf, bool ucs2, const char *str)
{
size_t buflen = talloc_get_size(buf);
char *converted;
diff --git a/source3/libsmb/clirap.c b/source3/libsmb/clirap.c
index 3b4db2e26d..631bc3f36b 100644
--- a/source3/libsmb/clirap.c
+++ b/source3/libsmb/clirap.c
@@ -998,8 +998,9 @@ bool cli_qfileinfo(struct cli_state *cli, int fnum,
unsigned int data_len = 0;
unsigned int param_len = 0;
uint16 setup = TRANSACT2_QFILEINFO;
- char param[4];
- char *rparam=NULL, *rdata=NULL;
+ uint8_t param[4];
+ uint8_t *rparam=NULL, *rdata=NULL;
+ NTSTATUS status;
/* if its a win95 server then fail this - win95 totally screws it
up */
@@ -1010,20 +1011,17 @@ bool cli_qfileinfo(struct cli_state *cli, int fnum,
SSVAL(param, 0, fnum);
SSVAL(param, 2, SMB_QUERY_FILE_ALL_INFO);
- if (!cli_send_trans(cli, SMBtrans2,
- NULL, /* name */
- -1, 0, /* fid, flags */
- &setup, 1, 0, /* setup, length, max */
- param, param_len, 2, /* param, length, max */
- NULL, data_len, cli->max_xmit /* data, length, max */
- )) {
- return False;
- }
+ status = cli_trans(talloc_tos(), cli, SMBtrans2,
+ NULL, -1, 0, 0, /* name, fid, function, flags */
+ &setup, 1, 0, /* setup, length, max */
+ param, param_len, 2, /* param, length, max */
+ NULL, 0, MIN(cli->max_xmit, 0xffff), /* data, length, max */
+ NULL, NULL, /* rsetup, length */
+ &rparam, &param_len, /* rparam, length */
+ &rdata, &data_len);
- if (!cli_receive_trans(cli, SMBtrans2,
- &rparam, &param_len,
- &rdata, &data_len)) {
- return False;
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
}
if (!rdata || data_len < 68) {
@@ -1031,16 +1029,16 @@ bool cli_qfileinfo(struct cli_state *cli, int fnum,
}
if (create_time) {
- *create_time = interpret_long_date(rdata+0);
+ *create_time = interpret_long_date((char *)rdata+0);
}
if (access_time) {
- *access_time = interpret_long_date(rdata+8);
+ *access_time = interpret_long_date((char *)rdata+8);
}
if (write_time) {
- *write_time = interpret_long_date(rdata+16);
+ *write_time = interpret_long_date((char *)rdata+16);
}
if (change_time) {
- *change_time = interpret_long_date(rdata+24);
+ *change_time = interpret_long_date((char *)rdata+24);
}
if (mode) {
*mode = SVAL(rdata, 32);
@@ -1052,8 +1050,8 @@ bool cli_qfileinfo(struct cli_state *cli, int fnum,
*ino = IVAL(rdata, 64);
}
- SAFE_FREE(rdata);
- SAFE_FREE(rparam);
+ TALLOC_FREE(rdata);
+ TALLOC_FREE(rparam);
return True;
}
diff --git a/source3/libsmb/clisecdesc.c b/source3/libsmb/clisecdesc.c
index adc6fba9af..f0b786c899 100644
--- a/source3/libsmb/clisecdesc.c
+++ b/source3/libsmb/clisecdesc.c
@@ -25,8 +25,8 @@
SEC_DESC *cli_query_secdesc(struct cli_state *cli, int fnum,
TALLOC_CTX *mem_ctx)
{
- char param[8];
- char *rparam=NULL, *rdata=NULL;
+ uint8_t param[8];
+ uint8_t *rparam=NULL, *rdata=NULL;
unsigned int rparam_count=0, rdata_count=0;
SEC_DESC *psd = NULL;
NTSTATUS status;
@@ -34,27 +34,22 @@ SEC_DESC *cli_query_secdesc(struct cli_state *cli, int fnum,
SIVAL(param, 0, fnum);
SIVAL(param, 4, 0x7);
- if (!cli_send_nt_trans(cli,
- NT_TRANSACT_QUERY_SECURITY_DESC,
- 0,
- NULL, 0, 0,
- param, 8, 4,
- NULL, 0, 0x10000)) {
- DEBUG(1,("Failed to send NT_TRANSACT_QUERY_SECURITY_DESC\n"));
- goto cleanup;
- }
-
+ status = cli_trans(talloc_tos(), cli, SMBnttrans,
+ NULL, -1, /* name, fid */
+ NT_TRANSACT_QUERY_SECURITY_DESC, 0, /* function, flags */
+ NULL, 0, 0, /* setup, length, max */
+ param, 8, 4, /* param, length, max */
+ NULL, 0, 0x10000, /* data, length, max */
+ NULL, NULL, /* rsetup, length */
+ &rparam, &rparam_count,
+ &rdata, &rdata_count);
- if (!cli_receive_nt_trans(cli,
- &rparam, &rparam_count,
- &rdata, &rdata_count)) {
- DEBUG(1,("Failed to recv NT_TRANSACT_QUERY_SECURITY_DESC\n"));
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("NT_TRANSACT_QUERY_SECURITY_DESC failed: %s\n",
+ nt_errstr(status)));
goto cleanup;
}
- if (cli_is_error(cli))
- goto cleanup;
-
status = unmarshall_sec_desc(mem_ctx, (uint8 *)rdata, rdata_count,
&psd);
@@ -66,8 +61,8 @@ SEC_DESC *cli_query_secdesc(struct cli_state *cli, int fnum,
cleanup:
- SAFE_FREE(rparam);
- SAFE_FREE(rdata);
+ TALLOC_FREE(rparam);
+ TALLOC_FREE(rdata);
return psd;
}
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;
+}