summaryrefslogtreecommitdiff
path: root/source4
diff options
context:
space:
mode:
Diffstat (limited to 'source4')
-rw-r--r--source4/librpc/rpc/dcerpc.c16
-rw-r--r--source4/librpc/rpc/dcerpc.h2
-rw-r--r--source4/librpc/rpc/dcerpc_smb.c148
-rw-r--r--source4/librpc/rpc/dcerpc_tcp.c33
4 files changed, 153 insertions, 46 deletions
diff --git a/source4/librpc/rpc/dcerpc.c b/source4/librpc/rpc/dcerpc.c
index 1a8fe7373a..61e5c1c8b2 100644
--- a/source4/librpc/rpc/dcerpc.c
+++ b/source4/librpc/rpc/dcerpc.c
@@ -352,7 +352,7 @@ static void full_request_recv(struct dcerpc_pipe *p, DATA_BLOB *blob,
}
/*
- perform a synchronous request - used for the bind code
+ perform a single pdu synchronous request - used for the bind code
this cannot be mixed with normal async requests
*/
static NTSTATUS full_request(struct dcerpc_pipe *p,
@@ -373,13 +373,11 @@ static NTSTATUS full_request(struct dcerpc_pipe *p,
p->transport.recv_data = full_request_recv;
p->full_request_private = state;
- status = p->transport.send_request(p, request_blob);
+ status = p->transport.send_request(p, request_blob, True);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
- p->transport.send_read(p);
-
while (NT_STATUS_IS_OK(state->status) && state->reply_blob) {
struct event_context *ctx = p->transport.event_context(p);
event_loop_once(ctx);
@@ -563,7 +561,7 @@ NTSTATUS dcerpc_auth3(struct dcerpc_pipe *p,
}
/* send it on its way */
- status = p->transport.send_request(p, &blob);
+ status = p->transport.send_request(p, &blob, False);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
@@ -757,7 +755,7 @@ struct rpc_request *dcerpc_request_send(struct dcerpc_pipe *p,
return req;
}
- req->status = p->transport.send_request(p, &blob);
+ req->status = p->transport.send_request(p, &blob, False);
if (!NT_STATUS_IS_OK(req->status)) {
req->state = RPC_REQUEST_DONE;
return req;
@@ -783,8 +781,8 @@ struct rpc_request *dcerpc_request_send(struct dcerpc_pipe *p,
return req;
}
- /* send the pdu */
- req->status = p->transport.send_request(p, &blob);
+ /* send the final pdu */
+ req->status = p->transport.send_request(p, &blob, True);
if (!NT_STATUS_IS_OK(req->status)) {
req->state = RPC_REQUEST_DONE;
@@ -792,8 +790,6 @@ struct rpc_request *dcerpc_request_send(struct dcerpc_pipe *p,
DLIST_ADD(p->pending, req);
- p->transport.send_read(p);
-
return req;
}
diff --git a/source4/librpc/rpc/dcerpc.h b/source4/librpc/rpc/dcerpc.h
index 2dce5df92a..91899a9fec 100644
--- a/source4/librpc/rpc/dcerpc.h
+++ b/source4/librpc/rpc/dcerpc.h
@@ -52,7 +52,7 @@ struct dcerpc_pipe {
const char *(*peer_name)(struct dcerpc_pipe *);
/* send a request to the server */
- NTSTATUS (*send_request)(struct dcerpc_pipe *, DATA_BLOB *);
+ NTSTATUS (*send_request)(struct dcerpc_pipe *, DATA_BLOB *, BOOL trigger_read);
/* send a read request to the server */
NTSTATUS (*send_read)(struct dcerpc_pipe *);
diff --git a/source4/librpc/rpc/dcerpc_smb.c b/source4/librpc/rpc/dcerpc_smb.c
index fffc945403..3f936407a6 100644
--- a/source4/librpc/rpc/dcerpc_smb.c
+++ b/source4/librpc/rpc/dcerpc_smb.c
@@ -65,14 +65,8 @@ static void smb_read_callback(struct smbcli_request *req)
smb = state->p->transport.private;
io = state->io;
- if (!NT_STATUS_IS_OK(req->status)) {
- pipe_dead(state->p, req->status);
- talloc_free(state);
- return;
- }
-
status = smb_raw_read_recv(state->req, io);
- if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_IS_ERR(status)) {
pipe_dead(state->p, status);
talloc_free(state);
return;
@@ -89,6 +83,7 @@ static void smb_read_callback(struct smbcli_request *req)
}
frag_length = dcerpc_get_frag_length(&state->data);
+
if (frag_length <= state->received) {
state->data.length = state->received;
state->p->transport.recv_data(state->p, &state->data, NT_STATUS_OK);
@@ -99,25 +94,27 @@ static void smb_read_callback(struct smbcli_request *req)
/* initiate another read request, as we only got part of a fragment */
state->data.data = talloc_realloc(state->data.data, frag_length);
- io->readx.in.mincnt = frag_length - state->received;
+ io->readx.in.mincnt = MIN(state->p->srv_max_xmit_frag,
+ frag_length - state->received);
io->readx.in.maxcnt = io->readx.in.mincnt;
io->readx.out.data = state->data.data + state->received;
- req = smb_raw_read_send(smb->tree, io);
- if (req == NULL) {
+ state->req = smb_raw_read_send(smb->tree, io);
+ if (state->req == NULL) {
pipe_dead(state->p, NT_STATUS_NO_MEMORY);
talloc_free(state);
return;
}
- req->async.fn = smb_read_callback;
- req->async.private = state;
+ state->req->async.fn = smb_read_callback;
+ state->req->async.private = state;
}
/*
- trigger a read request from the server
+ trigger a read request from the server, possibly with some initial
+ data in the read buffer
*/
-static NTSTATUS send_read_request(struct dcerpc_pipe *p)
+static NTSTATUS send_read_request_continue(struct dcerpc_pipe *p, DATA_BLOB *blob)
{
struct smb_private *smb = p->transport.private;
union smb_read *io;
@@ -130,18 +127,31 @@ static NTSTATUS send_read_request(struct dcerpc_pipe *p)
}
state->p = p;
- state->received = 0;
- state->data = data_blob_talloc(state, NULL, 0x2000);
+ if (blob == NULL) {
+ state->received = 0;
+ state->data = data_blob_talloc(state, NULL, 0x2000);
+ } else {
+ uint32_t frag_length = blob->length>=16?
+ dcerpc_get_frag_length(blob):0x2000;
+ state->received = blob->length;
+ state->data = data_blob_talloc(state, NULL, frag_length);
+ if (!state->data.data) {
+ talloc_free(state);
+ return NT_STATUS_NO_MEMORY;
+ }
+ memcpy(state->data.data, blob->data, blob->length);
+ }
+
state->io = talloc_p(state, union smb_read);
io = state->io;
io->generic.level = RAW_READ_READX;
io->readx.in.fnum = smb->fnum;
- io->readx.in.mincnt = state->data.length;
- io->readx.in.maxcnt = state->data.length;
+ io->readx.in.mincnt = state->data.length - state->received;
+ io->readx.in.maxcnt = io->readx.in.mincnt;
io->readx.in.offset = 0;
io->readx.in.remaining = 0;
- io->readx.out.data = state->data.data;
+ io->readx.out.data = state->data.data + state->received;
req = smb_raw_read_send(smb->tree, io);
if (req == NULL) {
return NT_STATUS_NO_MEMORY;
@@ -157,6 +167,96 @@ static NTSTATUS send_read_request(struct dcerpc_pipe *p)
/*
+ trigger a read request from the server
+*/
+static NTSTATUS send_read_request(struct dcerpc_pipe *p)
+{
+ return send_read_request_continue(p, NULL);
+}
+
+/*
+ this holds the state of an in-flight trans call
+*/
+struct smb_trans_state {
+ struct dcerpc_pipe *p;
+ struct smbcli_request *req;
+ struct smb_trans2 *trans;
+};
+
+/*
+ called when a trans request has completed
+*/
+static void smb_trans_callback(struct smbcli_request *req)
+{
+ struct smb_trans_state *state = req->async.private;
+ struct dcerpc_pipe *p = state->p;
+ NTSTATUS status;
+
+ status = smb_raw_trans_recv(req, state, state->trans);
+
+ if (NT_STATUS_IS_ERR(status)) {
+ pipe_dead(p, status);
+ return;
+ }
+
+ if (!NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) {
+ p->transport.recv_data(p, &state->trans->out.data, NT_STATUS_OK);
+ talloc_free(state);
+ return;
+ }
+
+ /* there is more to receive - setup a readx */
+ send_read_request_continue(p, &state->trans->out.data);
+ talloc_free(state);
+}
+
+/*
+ send a SMBtrans style request
+*/
+static NTSTATUS smb_send_trans_request(struct dcerpc_pipe *p, DATA_BLOB *blob)
+{
+ struct smb_private *smb = p->transport.private;
+ struct smb_trans2 *trans;
+ uint16 setup[2];
+ struct smb_trans_state *state;
+
+ state = talloc_p(smb, struct smb_trans_state);
+ if (state == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state->p = p;
+ state->trans = talloc_p(state, struct smb_trans2);
+ trans = state->trans;
+
+ trans->in.data = *blob;
+ trans->in.params = data_blob(NULL, 0);
+
+ setup[0] = TRANSACT_DCERPCCMD;
+ setup[1] = smb->fnum;
+
+ trans->in.max_param = 0;
+ trans->in.max_data = 0x8000;
+ trans->in.max_setup = 0;
+ trans->in.setup_count = 2;
+ trans->in.flags = 0;
+ trans->in.timeout = 0;
+ trans->in.setup = setup;
+ trans->in.trans_name = "\\PIPE\\";
+
+ state->req = smb_raw_trans_send(smb->tree, trans);
+ if (state->req == NULL) {
+ talloc_free(state);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ state->req->async.fn = smb_trans_callback;
+ state->req->async.private = state;
+
+ return NT_STATUS_OK;
+}
+
+/*
called when a write request has completed
*/
static void smb_write_callback(struct smbcli_request *req)
@@ -174,12 +274,16 @@ static void smb_write_callback(struct smbcli_request *req)
/*
send a packet to the server
*/
-static NTSTATUS smb_send_request(struct dcerpc_pipe *p, DATA_BLOB *blob)
+static NTSTATUS smb_send_request(struct dcerpc_pipe *p, DATA_BLOB *blob, BOOL trigger_read)
{
struct smb_private *smb = p->transport.private;
union smb_write io;
struct smbcli_request *req;
+ if (trigger_read) {
+ return smb_send_trans_request(p, blob);
+ }
+
io.generic.level = RAW_WRITE_WRITEX;
io.writex.in.fnum = smb->fnum;
io.writex.in.offset = 0;
@@ -196,6 +300,10 @@ static NTSTATUS smb_send_request(struct dcerpc_pipe *p, DATA_BLOB *blob)
req->async.fn = smb_write_callback;
req->async.private = p;
+ if (trigger_read) {
+ send_read_request(p);
+ }
+
return NT_STATUS_OK;
}
diff --git a/source4/librpc/rpc/dcerpc_tcp.c b/source4/librpc/rpc/dcerpc_tcp.c
index 7cf7cc98bb..35928cc1c4 100644
--- a/source4/librpc/rpc/dcerpc_tcp.c
+++ b/source4/librpc/rpc/dcerpc_tcp.c
@@ -199,10 +199,23 @@ static void tcp_io_handler(struct event_context *ev, struct fd_event *fde,
}
/*
+ initiate a read request
+*/
+static NTSTATUS tcp_send_read(struct dcerpc_pipe *p)
+{
+ struct tcp_private *tcp = p->transport.private;
+
+ tcp->recv.pending_count++;
+ if (tcp->recv.pending_count == 1) {
+ tcp->fde->flags |= EVENT_FD_READ;
+ }
+ return NT_STATUS_OK;
+}
+
+/*
send an initial pdu in a multi-pdu sequence
*/
-static NTSTATUS tcp_send_request(struct dcerpc_pipe *p,
- DATA_BLOB *data)
+static NTSTATUS tcp_send_request(struct dcerpc_pipe *p, DATA_BLOB *data, BOOL trigger_read)
{
struct tcp_private *tcp = p->transport.private;
struct tcp_blob *blob;
@@ -222,20 +235,10 @@ static NTSTATUS tcp_send_request(struct dcerpc_pipe *p,
tcp->fde->flags |= EVENT_FD_WRITE;
- return NT_STATUS_OK;
-}
-
-/*
- initiate a read request
-*/
-static NTSTATUS tcp_send_read(struct dcerpc_pipe *p)
-{
- struct tcp_private *tcp = p->transport.private;
-
- tcp->recv.pending_count++;
- if (tcp->recv.pending_count == 1) {
- tcp->fde->flags |= EVENT_FD_READ;
+ if (trigger_read) {
+ tcp_send_read(p);
}
+
return NT_STATUS_OK;
}