summaryrefslogtreecommitdiff
path: root/source3/libsmb/clitrans.c
diff options
context:
space:
mode:
Diffstat (limited to 'source3/libsmb/clitrans.c')
-rw-r--r--source3/libsmb/clitrans.c234
1 files changed, 174 insertions, 60 deletions
diff --git a/source3/libsmb/clitrans.c b/source3/libsmb/clitrans.c
index dab8c10c84..dd8063bf1e 100644
--- a/source3/libsmb/clitrans.c
+++ b/source3/libsmb/clitrans.c
@@ -18,6 +18,8 @@
*/
#include "includes.h"
+#include "libsmb/libsmb.h"
+#include "../lib/util/tevent_ntstatus.h"
#include "async_smb.h"
struct trans_recvblob {
@@ -30,7 +32,6 @@ struct cli_trans_state {
struct event_context *ev;
uint8_t cmd;
uint16_t mid;
- uint32_t seqnum;
const char *pipe_name;
uint8_t *pipe_name_conv;
size_t pipe_name_conv_len;
@@ -50,13 +51,29 @@ struct cli_trans_state {
struct trans_recvblob rdata;
uint16_t recv_flags2;
- TALLOC_CTX *secondary_request_ctx;
-
- struct iovec iov[4];
+ struct iovec iov[6];
uint8_t pad[4];
+ uint8_t zero_pad[4];
uint16_t vwv[32];
+
+ struct tevent_req *primary_subreq;
};
+static void cli_trans_cleanup_primary(struct cli_trans_state *state)
+{
+ if (state->primary_subreq) {
+ cli_smb_req_set_mid(state->primary_subreq, 0);
+ cli_smb_req_unset_pending(state->primary_subreq);
+ TALLOC_FREE(state->primary_subreq);
+ }
+}
+
+static int cli_trans_state_destructor(struct cli_trans_state *state)
+{
+ cli_trans_cleanup_primary(state);
+ return 0;
+}
+
static NTSTATUS cli_pull_trans(uint8_t *inbuf,
uint8_t wct, uint16_t *vwv,
uint16_t num_bytes, uint8_t *bytes,
@@ -146,7 +163,7 @@ static NTSTATUS cli_trans_pull_blob(TALLOC_CTX *mem_ctx,
return NT_STATUS_INVALID_NETWORK_RESPONSE;
}
blob->total = total;
- blob->data = TALLOC_ARRAY(mem_ctx, uint8_t, total);
+ blob->data = talloc_array(mem_ctx, uint8_t, total);
if (blob->data == NULL) {
return NT_STATUS_NO_MEMORY;
}
@@ -171,9 +188,12 @@ static void cli_trans_format(struct cli_trans_state *state, uint8_t *pwct,
struct iovec *iov = state->iov;
uint8_t *pad = state->pad;
uint16_t *vwv = state->vwv;
- uint16_t param_offset;
- uint16_t this_param = 0;
- uint16_t this_data = 0;
+ uint32_t param_offset;
+ uint32_t this_param = 0;
+ uint32_t param_pad;
+ uint32_t data_offset;
+ uint32_t this_data = 0;
+ uint32_t data_pad;
uint32_t useable_space;
uint8_t cmd;
@@ -221,7 +241,18 @@ static void cli_trans_format(struct cli_trans_state *state, uint8_t *pwct,
break;
}
- useable_space = state->cli->max_xmit - smb_size - sizeof(uint16_t)*wct;
+ param_offset += wct * sizeof(uint16_t);
+ useable_space = state->cli->max_xmit - param_offset;
+
+ param_pad = param_offset % 4;
+ if (param_pad > 0) {
+ param_pad = MIN(param_pad, useable_space);
+ iov[0].iov_base = (void *)state->zero_pad;
+ iov[0].iov_len = param_pad;
+ iov += 1;
+ param_offset += param_pad;
+ }
+ useable_space = state->cli->max_xmit - param_offset;
if (state->param_sent < state->num_param) {
this_param = MIN(state->num_param - state->param_sent,
@@ -231,27 +262,41 @@ static void cli_trans_format(struct cli_trans_state *state, uint8_t *pwct,
iov += 1;
}
+ data_offset = param_offset + this_param;
+ useable_space = state->cli->max_xmit - data_offset;
+
+ data_pad = data_offset % 4;
+ if (data_pad > 0) {
+ data_pad = MIN(data_pad, useable_space);
+ iov[0].iov_base = (void *)state->zero_pad;
+ iov[0].iov_len = data_pad;
+ iov += 1;
+ data_offset += data_pad;
+ }
+ useable_space = state->cli->max_xmit - data_offset;
+
if (state->data_sent < state->num_data) {
this_data = MIN(state->num_data - state->data_sent,
- useable_space - this_param);
+ useable_space);
iov[0].iov_base = (void *)(state->data + state->data_sent);
iov[0].iov_len = this_data;
iov += 1;
}
- 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",
+ "param_offset=%u, param_pad=%u, param_disp=%u, "
+ "data_offset=%u, data_pad=%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));
+ (unsigned)param_offset, (unsigned)param_pad,
+ (unsigned)state->param_sent,
+ (unsigned)data_offset, (unsigned)data_pad,
+ (unsigned)state->data_sent));
switch (cmd) {
case SMBtrans:
@@ -268,7 +313,7 @@ static void cli_trans_format(struct cli_trans_state *state, uint8_t *pwct,
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);
+ SSVAL(vwv +12, 0, data_offset);
SCVAL(vwv +13, 0, state->num_setup);
SCVAL(vwv +13, 1, 0); /* reserved */
memcpy(vwv + 14, state->setup,
@@ -282,40 +327,40 @@ static void cli_trans_format(struct cli_trans_state *state, uint8_t *pwct,
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 + 6, 0, data_offset);
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);
+ SCVAL(vwv + 0, 0, state->max_setup);
+ SSVAL(vwv + 0, 1, 0); /* reserved */
+ SIVAL(vwv + 1, 1, state->num_param);
+ SIVAL(vwv + 3, 1, state->num_data);
+ SIVAL(vwv + 5, 1, state->rparam.max);
+ SIVAL(vwv + 7, 1, state->rdata.max);
+ SIVAL(vwv + 9, 1, this_param);
+ SIVAL(vwv +11, 1, param_offset);
+ SIVAL(vwv +13, 1, this_data);
+ SIVAL(vwv +15, 1, data_offset);
+ SCVAL(vwv +17, 1, state->num_setup);
+ SSVAL(vwv +18, 0, 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 */
+ SSVAL(vwv + 0, 0, 0); /* reserved */
+ SCVAL(vwv + 1, 0, 0); /* reserved */
+ SIVAL(vwv + 1, 1, state->num_param);
+ SIVAL(vwv + 3, 1, state->num_data);
+ SIVAL(vwv + 5, 1, this_param);
+ SIVAL(vwv + 7, 1, param_offset);
+ SIVAL(vwv + 9, 1, state->param_sent);
+ SIVAL(vwv +11, 1, this_data);
+ SIVAL(vwv +13, 1, data_offset);
+ SIVAL(vwv +15, 1, state->data_sent);
+ SCVAL(vwv +17, 1, 0); /* reserved */
break;
}
@@ -412,17 +457,30 @@ struct tevent_req *cli_trans_send(
if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev);
}
- state->mid = cli_smb_req_mid(subreq);
status = cli_smb_req_send(subreq);
if (!NT_STATUS_IS_OK(status)) {
tevent_req_nterror(req, status);
return tevent_req_post(req, state->ev);
}
- cli_state_seqnum_persistent(cli, state->mid);
tevent_req_set_callback(subreq, cli_trans_done, req);
+
+ /*
+ * Now get the MID of the primary request
+ * and mark it as persistent. This means
+ * we will able to send and receive multiple
+ * SMB pdus using this MID in both directions
+ * (including correct SMB signing).
+ */
+ state->mid = cli_smb_req_mid(subreq);
+ cli_smb_req_set_mid(subreq, state->mid);
+ state->primary_subreq = subreq;
+ talloc_set_destructor(state, cli_trans_state_destructor);
+
return req;
}
+static void cli_trans_done2(struct tevent_req *subreq);
+
static void cli_trans_done(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(
@@ -478,25 +536,25 @@ static void cli_trans_done(struct tevent_req *subreq)
if (!sent_all) {
int iov_count;
-
- TALLOC_FREE(subreq);
+ struct tevent_req *subreq2;
cli_trans_format(state, &wct, &iov_count);
- subreq = cli_smb_req_create(state, state->ev, state->cli,
- state->cmd + 1, 0, wct, state->vwv,
- iov_count, state->iov);
- if (tevent_req_nomem(subreq, req)) {
+ subreq2 = cli_smb_req_create(state, state->ev, state->cli,
+ state->cmd + 1, 0, wct, state->vwv,
+ iov_count, state->iov);
+ if (tevent_req_nomem(subreq2, req)) {
return;
}
- cli_smb_req_set_mid(subreq, state->mid);
+ cli_smb_req_set_mid(subreq2, state->mid);
- status = cli_smb_req_send(subreq);
+ status = cli_smb_req_send(subreq2);
if (!NT_STATUS_IS_OK(status)) {
goto fail;
}
- tevent_req_set_callback(subreq, cli_trans_done, req);
+ tevent_req_set_callback(subreq2, cli_trans_done2, req);
+
return;
}
@@ -521,23 +579,80 @@ static void cli_trans_done(struct tevent_req *subreq)
if ((state->rparam.total == state->rparam.received)
&& (state->rdata.total == state->rdata.received)) {
state->recv_flags2 = SVAL(inbuf, smb_flg2);
- TALLOC_FREE(subreq);
- cli_state_seqnum_remove(state->cli, state->mid);
+ cli_trans_cleanup_primary(state);
tevent_req_done(req);
return;
}
TALLOC_FREE(inbuf);
- if (!cli_smb_req_set_pending(subreq)) {
- status = NT_STATUS_NO_MEMORY;
+ return;
+
+ fail:
+ cli_trans_cleanup_primary(state);
+ tevent_req_nterror(req, status);
+}
+
+static void cli_trans_done2(struct tevent_req *subreq2)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq2, struct tevent_req);
+ struct cli_trans_state *state = tevent_req_data(
+ req, struct cli_trans_state);
+ NTSTATUS status;
+ bool sent_all;
+ uint8_t wct;
+ uint32_t seqnum;
+
+ /*
+ * First backup the seqnum of the secondary request
+ * and attach it to the primary request.
+ */
+ seqnum = cli_smb_req_seqnum(subreq2);
+ cli_smb_req_set_seqnum(state->primary_subreq, seqnum);
+
+ status = cli_smb_recv(subreq2, state, NULL, 0, &wct, NULL,
+ NULL, NULL);
+ TALLOC_FREE(subreq2);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ if (wct != 0) {
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
goto fail;
}
+
+ sent_all = ((state->param_sent == state->num_param)
+ && (state->data_sent == state->num_data));
+
+ if (!sent_all) {
+ int iov_count;
+
+ cli_trans_format(state, &wct, &iov_count);
+
+ subreq2 = cli_smb_req_create(state, state->ev, state->cli,
+ state->cmd + 1, 0, wct, state->vwv,
+ iov_count, state->iov);
+ if (tevent_req_nomem(subreq2, req)) {
+ return;
+ }
+ cli_smb_req_set_mid(subreq2, state->mid);
+
+ status = cli_smb_req_send(subreq2);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+ tevent_req_set_callback(subreq2, cli_trans_done2, req);
+ return;
+ }
+
return;
fail:
- cli_state_seqnum_remove(state->cli, state->mid);
- TALLOC_FREE(subreq);
+ cli_trans_cleanup_primary(state);
tevent_req_nterror(req, status);
}
@@ -554,6 +669,8 @@ NTSTATUS cli_trans_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
req, struct cli_trans_state);
NTSTATUS status;
+ cli_trans_cleanup_primary(state);
+
if (tevent_req_is_nterror(req, &status)) {
return status;
}
@@ -644,8 +761,5 @@ NTSTATUS cli_trans(TALLOC_CTX *mem_ctx, struct cli_state *cli,
rdata, min_rdata, num_rdata);
fail:
TALLOC_FREE(frame);
- if (!NT_STATUS_IS_OK(status)) {
- cli_set_error(cli, status);
- }
return status;
}