diff options
Diffstat (limited to 'source4')
-rw-r--r-- | source4/librpc/rpc/dcerpc.c | 16 | ||||
-rw-r--r-- | source4/librpc/rpc/dcerpc.h | 2 | ||||
-rw-r--r-- | source4/librpc/rpc/dcerpc_smb.c | 148 | ||||
-rw-r--r-- | source4/librpc/rpc/dcerpc_tcp.c | 33 |
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; } |