diff options
-rw-r--r-- | source4/rpc_server/dcerpc_server.c | 135 | ||||
-rw-r--r-- | source4/rpc_server/dcerpc_server.h | 9 |
2 files changed, 103 insertions, 41 deletions
diff --git a/source4/rpc_server/dcerpc_server.c b/source4/rpc_server/dcerpc_server.c index 0ee5fad3fc..45e720b8c1 100644 --- a/source4/rpc_server/dcerpc_server.c +++ b/source4/rpc_server/dcerpc_server.c @@ -100,6 +100,7 @@ NTSTATUS dcesrv_endpoint_connect(struct server_context *smb, (*p)->ops = ops; (*p)->private = NULL; (*p)->call_list = NULL; + (*p)->cli_max_recv_frag = 0; /* make sure the endpoint server likes the connection */ status = ops->connect(*p); @@ -128,6 +129,7 @@ static NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32 fault_code) { struct ndr_push *push; struct dcerpc_packet pkt; + struct dcesrv_call_reply *rep; NTSTATUS status; /* setup a bind_ack */ @@ -157,8 +159,15 @@ static NTSTATUS dcesrv_fault(struct dcesrv_call_state *call, uint32 fault_code) return status; } - call->data = ndr_push_blob(push); - SSVAL(call->data.data, DCERPC_FRAG_LEN_OFFSET, call->data.length); + rep = talloc(call->mem_ctx, sizeof(*rep)); + if (!rep) { + return NT_STATUS_NO_MEMORY; + } + + rep->data = ndr_push_blob(push); + SSVAL(rep->data.data, DCERPC_FRAG_LEN_OFFSET, rep->data.length); + + DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *); return NT_STATUS_OK; } @@ -173,6 +182,7 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call) uint32 if_version, transfer_syntax_version; struct dcerpc_packet pkt; struct ndr_push *push; + struct dcesrv_call_reply *rep; NTSTATUS status; if (call->pkt.u.bind.num_contexts != 1 || @@ -202,6 +212,10 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call) return dcesrv_fault(call, DCERPC_FAULT_TODO); } + if (call->dce->cli_max_recv_frag == 0) { + call->dce->cli_max_recv_frag = call->pkt.u.bind.max_recv_frag; + } + /* setup a bind_ack */ pkt.rpc_vers = 5; pkt.rpc_vers_minor = 0; @@ -241,8 +255,15 @@ static NTSTATUS dcesrv_bind(struct dcesrv_call_state *call) return status; } - call->data = ndr_push_blob(push); - SSVAL(call->data.data, DCERPC_FRAG_LEN_OFFSET, call->data.length); + rep = talloc(call->mem_ctx, sizeof(*rep)); + if (!rep) { + return NT_STATUS_NO_MEMORY; + } + + rep->data = ndr_push_blob(push); + SSVAL(rep->data.data, DCERPC_FRAG_LEN_OFFSET, rep->data.length); + + DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *); return NT_STATUS_OK; } @@ -259,7 +280,6 @@ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call) void *r; NTSTATUS status; DATA_BLOB stub; - struct dcerpc_packet pkt; opnum = call->pkt.u.request.opnum; @@ -302,34 +322,62 @@ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call) stub = ndr_push_blob(push); - /* form the dcerpc response packet */ - pkt.rpc_vers = 5; - pkt.rpc_vers_minor = 0; - pkt.drep[0] = 0x10; /* Little endian */ - pkt.drep[1] = 0; - pkt.drep[2] = 0; - pkt.drep[3] = 0; - pkt.auth_length = 0; - pkt.call_id = call->pkt.call_id; - pkt.ptype = DCERPC_PKT_RESPONSE; - pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST; - pkt.u.response.alloc_hint = stub.length; - pkt.u.response.context_id = call->pkt.u.request.context_id; - pkt.u.response.cancel_count = 0; - pkt.u.response.stub_and_verifier = stub; + do { + uint32 length; + struct dcesrv_call_reply *rep; + struct dcerpc_packet pkt; - push = ndr_push_init_ctx(call->mem_ctx); - if (!push) { - return NT_STATUS_NO_MEMORY; - } + rep = talloc(call->mem_ctx, sizeof(*rep)); + if (!rep) { + return NT_STATUS_NO_MEMORY; + } - status = ndr_push_dcerpc_packet(push, NDR_SCALARS|NDR_BUFFERS, &pkt); - if (!NT_STATUS_IS_OK(status)) { - return status; - } + length = stub.length; + if (length + DCERPC_RESPONSE_LENGTH > call->dce->cli_max_recv_frag) { + length = call->dce->cli_max_recv_frag - DCERPC_RESPONSE_LENGTH; + } - call->data = ndr_push_blob(push); - SSVAL(call->data.data, DCERPC_FRAG_LEN_OFFSET, call->data.length); + /* form the dcerpc response packet */ + pkt.rpc_vers = 5; + pkt.rpc_vers_minor = 0; + pkt.drep[0] = 0x10; /* Little endian */ + pkt.drep[1] = 0; + pkt.drep[2] = 0; + pkt.drep[3] = 0; + pkt.auth_length = 0; + pkt.call_id = call->pkt.call_id; + pkt.ptype = DCERPC_PKT_RESPONSE; + pkt.pfc_flags = 0; + if (!call->replies) { + pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST; + } + if (length == stub.length) { + pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST; + } + pkt.u.response.alloc_hint = stub.length; + pkt.u.response.context_id = call->pkt.u.request.context_id; + pkt.u.response.cancel_count = 0; + pkt.u.response.stub_and_verifier.data = stub.data; + pkt.u.response.stub_and_verifier.length = length; + + push = ndr_push_init_ctx(call->mem_ctx); + if (!push) { + return NT_STATUS_NO_MEMORY; + } + + status = ndr_push_dcerpc_packet(push, NDR_SCALARS|NDR_BUFFERS, &pkt); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + rep->data = ndr_push_blob(push); + SSVAL(rep->data.data, DCERPC_FRAG_LEN_OFFSET, rep->data.length); + + DLIST_ADD_END(call->replies, rep, struct dcesrv_call_reply *); + + stub.data += length; + stub.length -= length; + } while (stub.length != 0); return NT_STATUS_OK; } @@ -357,7 +405,7 @@ NTSTATUS dcesrv_input(struct dcesrv_state *dce, const DATA_BLOB *data) } call->mem_ctx = mem_ctx; call->dce = dce; - call->data = data_blob(NULL, 0); + call->replies = NULL; ndr = ndr_pull_init_blob(data, mem_ctx); if (!ndr) { @@ -454,22 +502,29 @@ NTSTATUS dcesrv_input(struct dcesrv_state *dce, const DATA_BLOB *data) NTSTATUS dcesrv_output(struct dcesrv_state *dce, DATA_BLOB *data) { struct dcesrv_call_state *call; + struct dcesrv_call_reply *rep; call = dce->call_list; - if (!call) { + if (!call || !call->replies) { return NT_STATUS_FOOBAR; } - - if (data->length >= call->data.length) { - data->length = call->data.length; + rep = call->replies; + + if (data->length >= rep->data.length) { + data->length = rep->data.length; } - memcpy(data->data, call->data.data, data->length); - call->data.length -= data->length; - call->data.data += data->length; + memcpy(data->data, rep->data.data, data->length); + rep->data.length -= data->length; + rep->data.data += data->length; + + if (rep->data.length == 0) { + /* we're done with this section of the call */ + DLIST_REMOVE(call->replies, rep); + } - if (call->data.length == 0) { - /* we're done with this call */ + if (call->replies == NULL) { + /* we're done with the whole call */ DLIST_REMOVE(dce->call_list, call); talloc_destroy(call->mem_ctx); } diff --git a/source4/rpc_server/dcerpc_server.h b/source4/rpc_server/dcerpc_server.h index 9895254cad..165eddc5e6 100644 --- a/source4/rpc_server/dcerpc_server.h +++ b/source4/rpc_server/dcerpc_server.h @@ -43,7 +43,11 @@ struct dcesrv_call_state { struct dcesrv_state *dce; TALLOC_CTX *mem_ctx; struct dcerpc_packet pkt; - DATA_BLOB data; + + struct dcesrv_call_reply { + struct dcesrv_call_reply *next, *prev; + DATA_BLOB data; + } *replies; }; /* the state associated with a dcerpc server connection */ @@ -66,6 +70,9 @@ struct dcesrv_state { /* the state of the current calls */ struct dcesrv_call_state *call_list; + /* the maximum size the client wants to receive */ + uint32 cli_max_recv_frag; + /* private data for the endpoint server */ void *private; }; |