diff options
-rw-r--r-- | source4/build/pidl/client.pm | 19 | ||||
-rw-r--r-- | source4/lib/events.c | 2 | ||||
-rw-r--r-- | source4/librpc/ndr/ndr.c | 8 | ||||
-rw-r--r-- | source4/librpc/rpc/dcerpc.c | 492 | ||||
-rw-r--r-- | source4/librpc/rpc/dcerpc.h | 55 | ||||
-rw-r--r-- | source4/librpc/rpc/dcerpc_auth.c | 4 | ||||
-rw-r--r-- | source4/librpc/rpc/dcerpc_error.c | 8 | ||||
-rw-r--r-- | source4/librpc/rpc/dcerpc_schannel.c | 8 | ||||
-rw-r--r-- | source4/librpc/rpc/dcerpc_smb.c | 314 | ||||
-rw-r--r-- | source4/librpc/rpc/dcerpc_tcp.c | 267 | ||||
-rw-r--r-- | source4/librpc/rpc/dcerpc_util.c | 22 | ||||
-rw-r--r-- | source4/torture/rpc/drsuapi.c | 2 | ||||
-rw-r--r-- | source4/torture/rpc/epmapper.c | 12 | ||||
-rw-r--r-- | source4/torture/rpc/testjoin.c | 2 |
14 files changed, 772 insertions, 443 deletions
diff --git a/source4/build/pidl/client.pm b/source4/build/pidl/client.pm index 89d367f037..13939c3372 100644 --- a/source4/build/pidl/client.pm +++ b/source4/build/pidl/client.pm @@ -19,18 +19,29 @@ sub ParseFunction($) $res .= " -NTSTATUS dcerpc_$name(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, struct $name *r) +struct rpc_request *dcerpc_$name\_send(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, struct $name *r) { - NTSTATUS status; - if (p->flags & DCERPC_DEBUG_PRINT_IN) { NDR_PRINT_IN_DEBUG($name, r); } - status = dcerpc_ndr_request(p, DCERPC_$uname, mem_ctx, + return dcerpc_ndr_request_send(p, DCERPC_$uname, mem_ctx, (ndr_push_flags_fn_t) ndr_push_$name, (ndr_pull_flags_fn_t) ndr_pull_$name, r, sizeof(*r)); +} + +"; + + $res .= +" +NTSTATUS dcerpc_$name(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, struct $name *r) +{ + struct rpc_request *req = dcerpc_$name\_send(p, mem_ctx, r); + NTSTATUS status; + if (req == NULL) return NT_STATUS_NO_MEMORY; + + status = dcerpc_ndr_request_recv(req); if (NT_STATUS_IS_OK(status) && (p->flags & DCERPC_DEBUG_PRINT_OUT)) { NDR_PRINT_OUT_DEBUG($name, r); diff --git a/source4/lib/events.c b/source4/lib/events.c index a6099db5c5..9affaf1246 100644 --- a/source4/lib/events.c +++ b/source4/lib/events.c @@ -403,7 +403,7 @@ void event_loop_once(struct event_context *ev) made readable and that should have removed the event, so this must be a bug. This is a fatal error. */ - DEBUG(0,("EBADF on event_loop_wait - exiting\n")); + DEBUG(0,("EBADF on event_loop_once - exiting\n")); return; } diff --git a/source4/librpc/ndr/ndr.c b/source4/librpc/ndr/ndr.c index 13daac78c2..5efeb77289 100644 --- a/source4/librpc/ndr/ndr.c +++ b/source4/librpc/ndr/ndr.c @@ -147,13 +147,7 @@ struct ndr_push *ndr_push_init_ctx(TALLOC_CTX *mem_ctx) struct ndr_push *ndr_push_init(void) { struct ndr_push *ndr; - TALLOC_CTX *mem_ctx = talloc_init("ndr_push_init"); - if (!mem_ctx) return NULL; - ndr = ndr_push_init_ctx(mem_ctx); - if (!ndr) { - talloc_destroy(mem_ctx); - } - return ndr; + return ndr_push_init_ctx(NULL); } /* free a ndr_push structure */ diff --git a/source4/librpc/rpc/dcerpc.c b/source4/librpc/rpc/dcerpc.c index b605b4d110..1a8fe7373a 100644 --- a/source4/librpc/rpc/dcerpc.c +++ b/source4/librpc/rpc/dcerpc.c @@ -28,18 +28,12 @@ struct dcerpc_pipe *dcerpc_pipe_init(void) { struct dcerpc_pipe *p; - TALLOC_CTX *mem_ctx = talloc_init("dcerpc_tree"); - if (mem_ctx == NULL) - return NULL; - - p = talloc(mem_ctx, sizeof(*p)); + p = talloc_p(NULL, struct dcerpc_pipe); if (!p) { - talloc_destroy(mem_ctx); return NULL; } p->reference_count = 0; - p->mem_ctx = mem_ctx; p->call_id = 1; p->security_state.auth_info = NULL; p->security_state.generic_state = NULL; @@ -48,10 +42,23 @@ struct dcerpc_pipe *dcerpc_pipe_init(void) p->srv_max_xmit_frag = 0; p->srv_max_recv_frag = 0; p->last_fault_code = 0; + p->pending = NULL; return p; } +/* + choose the next call id to use +*/ +static uint32_t next_call_id(struct dcerpc_pipe *p) +{ + p->call_id++; + if (p->call_id == 0) { + p->call_id++; + } + return p->call_id; +} + /* close down a dcerpc over SMB pipe */ void dcerpc_pipe_close(struct dcerpc_pipe *p) { @@ -62,7 +69,7 @@ void dcerpc_pipe_close(struct dcerpc_pipe *p) gensec_end(&p->security_state.generic_state); } p->transport.shutdown_pipe(p); - talloc_destroy(p->mem_ctx); + talloc_free(p); } } @@ -320,6 +327,67 @@ static void init_dcerpc_hdr(struct dcerpc_pipe *p, struct dcerpc_packet *pkt) pkt->drep[3] = 0; } +/* + hold the state of pending full requests +*/ +struct full_request_state { + DATA_BLOB *reply_blob; + NTSTATUS status; +}; + +/* + receive a reply to a full request + */ +static void full_request_recv(struct dcerpc_pipe *p, DATA_BLOB *blob, + NTSTATUS status) +{ + struct full_request_state *state = p->full_request_private; + + if (!NT_STATUS_IS_OK(status)) { + state->status = status; + return; + } + state->reply_blob[0] = data_blob_talloc(state, blob->data, blob->length); + state->reply_blob = NULL; +} + +/* + perform a synchronous request - used for the bind code + this cannot be mixed with normal async requests +*/ +static NTSTATUS full_request(struct dcerpc_pipe *p, + TALLOC_CTX *mem_ctx, + DATA_BLOB *request_blob, + DATA_BLOB *reply_blob) +{ + struct full_request_state *state = talloc_p(mem_ctx, struct full_request_state); + NTSTATUS status; + + if (state == NULL) { + return NT_STATUS_NO_MEMORY; + } + + state->reply_blob = reply_blob; + state->status = NT_STATUS_OK; + + p->transport.recv_data = full_request_recv; + p->full_request_private = state; + + status = p->transport.send_request(p, request_blob); + 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); + } + + return state->status; +} + /* perform a bind using the given syntax @@ -367,7 +435,7 @@ NTSTATUS dcerpc_bind(struct dcerpc_pipe *p, } /* send it on its way */ - status = p->transport.full_request(p, mem_ctx, &blob, &blob); + status = full_request(p, mem_ctx, &blob, &blob); if (!NT_STATUS_IS_OK(status)) { return status; } @@ -441,7 +509,7 @@ NTSTATUS dcerpc_alter(struct dcerpc_pipe *p, } /* send it on its way */ - status = p->transport.full_request(p, mem_ctx, &blob, &blob); + status = full_request(p, mem_ctx, &blob, &blob); if (!NT_STATUS_IS_OK(status)) { return status; } @@ -483,7 +551,7 @@ NTSTATUS dcerpc_auth3(struct dcerpc_pipe *p, pkt.ptype = DCERPC_PKT_AUTH3; pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST; - pkt.call_id = p->call_id++; + pkt.call_id = next_call_id(p); pkt.auth_length = 0; pkt.u.auth._pad = 0; pkt.u.auth.auth_info = data_blob(NULL, 0); @@ -495,7 +563,7 @@ NTSTATUS dcerpc_auth3(struct dcerpc_pipe *p, } /* send it on its way */ - status = p->transport.initial_request(p, mem_ctx, &blob); + status = p->transport.send_request(p, &blob); if (!NT_STATUS_IS_OK(status)) { return status; } @@ -530,33 +598,141 @@ NTSTATUS dcerpc_bind_byuuid(struct dcerpc_pipe *p, } /* - perform a full request/response pair on a dcerpc pipe + process a fragment received from the transport layer during a + request */ -NTSTATUS dcerpc_request(struct dcerpc_pipe *p, - uint16_t opnum, - TALLOC_CTX *mem_ctx, - DATA_BLOB *stub_data_in, - DATA_BLOB *stub_data_out) +static void dcerpc_request_recv_data(struct dcerpc_pipe *p, + DATA_BLOB *data, + NTSTATUS status) { + struct dcerpc_packet pkt; + struct rpc_request *req; + uint_t length; + if (!NT_STATUS_IS_OK(status)) { + /* all pending requests get the error */ + while (p->pending) { + req = p->pending; + req->state = RPC_REQUEST_DONE; + req->status = status; + DLIST_REMOVE(p->pending, req); + } + return; + } + + pkt.call_id = 0; + + status = dcerpc_pull_request_sign(p, data, (TALLOC_CTX *)data->data, &pkt); + + /* find the matching request. Notice we match before we check + the status. this is ok as a pending call_id can never be + zero */ + for (req=p->pending;req;req=req->next) { + if (pkt.call_id == req->call_id) break; + } + + if (req == NULL) { + DEBUG(2,("dcerpc_request: unmatched call_id in response packet\n")); + return; + } + + if (!NT_STATUS_IS_OK(status)) { + req->status = status; + req->state = RPC_REQUEST_DONE; + DLIST_REMOVE(p->pending, req); + return; + } + + if (pkt.ptype == DCERPC_PKT_FAULT) { + DEBUG(5,("rpc fault 0x%x\n", pkt.u.fault.status)); + req->fault_code = pkt.u.fault.status; + req->status = NT_STATUS_NET_WRITE_FAULT; + req->state = RPC_REQUEST_DONE; + DLIST_REMOVE(p->pending, req); + return; + } + + if (pkt.ptype != DCERPC_PKT_RESPONSE) { + DEBUG(2,("Unexpected packet type %d in dcerpc response\n", + (int)pkt.ptype)); + req->fault_code = DCERPC_FAULT_OTHER; + req->status = NT_STATUS_NET_WRITE_FAULT; + req->state = RPC_REQUEST_DONE; + DLIST_REMOVE(p->pending, req); + return; + } + + length = pkt.u.response.stub_and_verifier.length; + + if (length > 0) { + req->payload.data = talloc_realloc(req->payload.data, + req->payload.length + length); + if (!req->payload.data) { + req->status = NT_STATUS_NO_MEMORY; + req->state = RPC_REQUEST_DONE; + DLIST_REMOVE(p->pending, req); + return; + } + memcpy(req->payload.data+req->payload.length, + pkt.u.response.stub_and_verifier.data, length); + req->payload.length += length; + } + + if (!(pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) { + p->transport.send_read(p); + return; + } + + /* we've got the full payload */ + req->state = RPC_REQUEST_DONE; + DLIST_REMOVE(p->pending, req); + + if (!(pkt.drep[0] & DCERPC_DREP_LE)) { + req->flags |= DCERPC_PULL_BIGENDIAN; + } else { + req->flags &= ~DCERPC_PULL_BIGENDIAN; + } +} + + +/* + perform a full request/response pair on a dcerpc pipe +*/ +struct rpc_request *dcerpc_request_send(struct dcerpc_pipe *p, + uint16_t opnum, + TALLOC_CTX *mem_ctx, + DATA_BLOB *stub_data) +{ + struct rpc_request *req; struct dcerpc_packet pkt; - NTSTATUS status; - DATA_BLOB blob, payload; + DATA_BLOB blob; uint32_t remaining, chunk_size; - /* allow the application to tell when a fault has happened */ - p->last_fault_code = 0; + p->transport.recv_data = dcerpc_request_recv_data; + + req = talloc_p(mem_ctx, struct rpc_request); + if (req == NULL) { + return NULL; + } + + req->p = p; + req->call_id = next_call_id(p); + req->status = NT_STATUS_OK; + req->state = RPC_REQUEST_PENDING; + req->payload = data_blob(NULL, 0); + req->flags = 0; + req->fault_code = 0; init_dcerpc_hdr(p, &pkt); - remaining = stub_data_in->length; + remaining = stub_data->length; /* we can write a full max_recv_frag size, minus the dcerpc request header size */ chunk_size = p->srv_max_recv_frag - (DCERPC_MAX_SIGN_SIZE+DCERPC_REQUEST_LENGTH); pkt.ptype = DCERPC_PKT_REQUEST; - pkt.call_id = p->call_id++; + pkt.call_id = req->call_id; pkt.auth_length = 0; pkt.u.request.alloc_hint = remaining; pkt.u.request.context_id = 0; @@ -565,24 +741,26 @@ NTSTATUS dcerpc_request(struct dcerpc_pipe *p, /* we send a series of pdus without waiting for a reply until the last pdu */ while (remaining > chunk_size) { - if (remaining == stub_data_in->length) { + if (remaining == stub_data->length) { pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST; } else { pkt.pfc_flags = 0; } - pkt.u.request.stub_and_verifier.data = stub_data_in->data + - (stub_data_in->length - remaining); + pkt.u.request.stub_and_verifier.data = stub_data->data + + (stub_data->length - remaining); pkt.u.request.stub_and_verifier.length = chunk_size; - status = dcerpc_push_request_sign(p, &blob, mem_ctx, &pkt); - if (!NT_STATUS_IS_OK(status)) { - return status; + req->status = dcerpc_push_request_sign(p, &blob, mem_ctx, &pkt); + if (!NT_STATUS_IS_OK(req->status)) { + req->state = RPC_REQUEST_DONE; + return req; } - status = p->transport.initial_request(p, mem_ctx, &blob); - if (!NT_STATUS_IS_OK(status)) { - return status; + req->status = p->transport.send_request(p, &blob); + if (!NT_STATUS_IS_OK(req->status)) { + req->state = RPC_REQUEST_DONE; + return req; } remaining -= chunk_size; @@ -590,102 +768,88 @@ NTSTATUS dcerpc_request(struct dcerpc_pipe *p, /* now we send a pdu with LAST_FRAG sent and get the first part of the reply */ - if (remaining == stub_data_in->length) { + if (remaining == stub_data->length) { pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST; } else { pkt.pfc_flags = DCERPC_PFC_FLAG_LAST; } - pkt.u.request.stub_and_verifier.data = stub_data_in->data + - (stub_data_in->length - remaining); + pkt.u.request.stub_and_verifier.data = stub_data->data + + (stub_data->length - remaining); pkt.u.request.stub_and_verifier.length = remaining; - status = dcerpc_push_request_sign(p, &blob, mem_ctx, &pkt); - if (!NT_STATUS_IS_OK(status)) { - return status; + req->status = dcerpc_push_request_sign(p, &blob, mem_ctx, &pkt); + if (!NT_STATUS_IS_OK(req->status)) { + req->state = RPC_REQUEST_DONE; + return req; } - /* send the pdu and get the initial response pdu */ - status = p->transport.full_request(p, mem_ctx, &blob, &blob); - if (!NT_STATUS_IS_OK(status)) { - return status; - } + /* send the pdu */ + req->status = p->transport.send_request(p, &blob); - status = dcerpc_pull_request_sign(p, &blob, mem_ctx, &pkt); - if (!NT_STATUS_IS_OK(status)) { - return status; + if (!NT_STATUS_IS_OK(req->status)) { + req->state = RPC_REQUEST_DONE; } - if (pkt.ptype == DCERPC_PKT_FAULT) { - DEBUG(5,("rpc fault 0x%x\n", pkt.u.fault.status)); - p->last_fault_code = pkt.u.fault.status; - return NT_STATUS_NET_WRITE_FAULT; - } + DLIST_ADD(p->pending, req); - if (pkt.ptype != DCERPC_PKT_RESPONSE) { - return NT_STATUS_UNSUCCESSFUL; - } + p->transport.send_read(p); - if (!(pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST)) { - /* something is badly wrong! */ - return NT_STATUS_UNSUCCESSFUL; - } - - payload = pkt.u.response.stub_and_verifier; - - /* continue receiving fragments */ - while (!(pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) { - uint32_t length; - - status = p->transport.secondary_request(p, mem_ctx, &blob); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - status = dcerpc_pull_request_sign(p, &blob, mem_ctx, &pkt); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - if (pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST) { - /* start of another packet!? */ - return NT_STATUS_UNSUCCESSFUL; - } - - if (pkt.ptype == DCERPC_PKT_FAULT) { - p->last_fault_code = pkt.u.fault.status; - return NT_STATUS_NET_WRITE_FAULT; - } + return req; +} - if (pkt.ptype != DCERPC_PKT_RESPONSE) { - return NT_STATUS_UNSUCCESSFUL; - } +/* + return the event context for a dcerpc pipe + used by callers who wish to operate asynchronously +*/ +struct event_context *dcerpc_event_context(struct dcerpc_pipe *p) +{ + return p->transport.event_context(p); +} - length = pkt.u.response.stub_and_verifier.length; - payload.data = talloc_realloc(payload.data, - payload.length + length); - if (!payload.data) { - return NT_STATUS_NO_MEMORY; - } - memcpy(payload.data + payload.length, - pkt.u.response.stub_and_verifier.data, - length); +/* + perform a full request/response pair on a dcerpc pipe +*/ +NTSTATUS dcerpc_request_recv(struct rpc_request *req, + TALLOC_CTX *mem_ctx, + DATA_BLOB *stub_data) +{ + NTSTATUS status; - payload.length += length; + while (req->state == RPC_REQUEST_PENDING) { + struct event_context *ctx = dcerpc_event_context(req->p); + event_loop_once(ctx); } - - if (stub_data_out) { - *stub_data_out = payload; + *stub_data = req->payload; + status = req->status; + if (stub_data->data) { + stub_data->data = talloc_steal(mem_ctx, stub_data->data); + } + if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { + req->p->last_fault_code = req->fault_code; } + talloc_free(req); + return status; +} - if (!(pkt.drep[0] & DCERPC_DREP_LE)) { - p->flags |= DCERPC_PULL_BIGENDIAN; - } else { - p->flags &= ~DCERPC_PULL_BIGENDIAN; +/* + perform a full request/response pair on a dcerpc pipe +*/ +NTSTATUS dcerpc_request(struct dcerpc_pipe *p, + uint16_t opnum, + TALLOC_CTX *mem_ctx, + DATA_BLOB *stub_data_in, + DATA_BLOB *stub_data_out) +{ + struct rpc_request *req; + + req = dcerpc_request_send(p, opnum, mem_ctx, stub_data_in); + if (req == NULL) { + return NT_STATUS_NO_MEMORY; } - return status; + return dcerpc_request_recv(req, mem_ctx, stub_data_out); } @@ -830,30 +994,29 @@ static NTSTATUS dcerpc_ndr_validate_out(TALLOC_CTX *mem_ctx, return NT_STATUS_OK; } + /* - a useful helper function for synchronous rpc requests + send a rpc request with a given set of ndr helper functions - this can be used when you have ndr push/pull functions in the - standard format + call dcerpc_ndr_request_recv() to receive the answer */ -NTSTATUS dcerpc_ndr_request(struct dcerpc_pipe *p, - uint32_t opnum, - TALLOC_CTX *mem_ctx, - NTSTATUS (*ndr_push)(struct ndr_push *, int, void *), - NTSTATUS (*ndr_pull)(struct ndr_pull *, int, void *), - void *struct_ptr, - size_t struct_size) +struct rpc_request *dcerpc_ndr_request_send(struct dcerpc_pipe *p, + uint32_t opnum, + TALLOC_CTX *mem_ctx, + NTSTATUS (*ndr_push)(struct ndr_push *, int, void *), + NTSTATUS (*ndr_pull)(struct ndr_pull *, int, void *), + void *struct_ptr, + size_t struct_size) { struct ndr_push *push; - struct ndr_pull *pull; NTSTATUS status; - DATA_BLOB request, response; + DATA_BLOB request; + struct rpc_request *req; /* setup for a ndr_push_* call */ push = ndr_push_init(); if (!push) { - talloc_destroy(mem_ctx); - return NT_STATUS_NO_MEMORY; + return NULL; } if (p->flags & DCERPC_PUSH_BIGENDIAN) { @@ -863,7 +1026,10 @@ NTSTATUS dcerpc_ndr_request(struct dcerpc_pipe *p, /* push the structure into a blob */ status = ndr_push(push, NDR_IN, struct_ptr); if (!NT_STATUS_IS_OK(status)) { - goto failed; + DEBUG(2,("Unable to ndr_push structure in dcerpc_ndr_request_send - %s\n", + nt_errstr(status))); + ndr_push_free(push); + return NULL; } /* retrieve the blob */ @@ -873,7 +1039,10 @@ NTSTATUS dcerpc_ndr_request(struct dcerpc_pipe *p, status = dcerpc_ndr_validate_in(mem_ctx, request, struct_size, ndr_push, ndr_pull); if (!NT_STATUS_IS_OK(status)) { - goto failed; + DEBUG(2,("Validation failed in dcerpc_ndr_request_send - %s\n", + nt_errstr(status))); + ndr_push_free(push); + return NULL; } } @@ -881,18 +1050,45 @@ NTSTATUS dcerpc_ndr_request(struct dcerpc_pipe *p, dump_data(10, request.data, request.length); /* make the actual dcerpc request */ - status = dcerpc_request(p, opnum, mem_ctx, &request, &response); + req = dcerpc_request_send(p, opnum, mem_ctx, &request); + + if (req != NULL) { + req->ndr.ndr_push = ndr_push; + req->ndr.ndr_pull = ndr_pull; + req->ndr.struct_ptr = struct_ptr; + req->ndr.struct_size = struct_size; + req->ndr.mem_ctx = mem_ctx; + } + + ndr_push_free(push); + + return req; +} + +/* + receive the answer from a dcerpc_ndr_request_send() +*/ +NTSTATUS dcerpc_ndr_request_recv(struct rpc_request *req) +{ + struct dcerpc_pipe *p = req->p; + NTSTATUS status; + DATA_BLOB response; + struct ndr_pull *pull; + struct rpc_request_ndr ndr = req->ndr; + uint_t flags = req->flags; + + status = dcerpc_request_recv(req, ndr.mem_ctx, &response); if (!NT_STATUS_IS_OK(status)) { - goto failed; + return status; } /* prepare for ndr_pull_* */ - pull = ndr_pull_init_blob(&response, mem_ctx); + pull = ndr_pull_init_blob(&response, ndr.mem_ctx); if (!pull) { - goto failed; + return NT_STATUS_NO_MEMORY; } - if (p->flags & DCERPC_PULL_BIGENDIAN) { + if (flags & DCERPC_PULL_BIGENDIAN) { pull->flags |= LIBNDR_FLAG_BIGENDIAN; } @@ -900,19 +1096,16 @@ NTSTATUS dcerpc_ndr_request(struct dcerpc_pipe *p, dump_data(10, pull->data, pull->data_size); /* pull the structure from the blob */ - status = ndr_pull(pull, NDR_OUT, struct_ptr); + status = ndr.ndr_pull(pull, NDR_OUT, ndr.struct_ptr); if (!NT_STATUS_IS_OK(status)) { - goto failed; + return status; } - /* possibly check the packet signature */ - - if (p->flags & DCERPC_DEBUG_VALIDATE_OUT) { - status = dcerpc_ndr_validate_out(mem_ctx, struct_ptr, struct_size, - ndr_push, ndr_pull); + status = dcerpc_ndr_validate_out(ndr.mem_ctx, ndr.struct_ptr, ndr.struct_size, + ndr.ndr_push, ndr.ndr_pull); if (!NT_STATUS_IS_OK(status)) { - goto failed; + return status; } } @@ -926,9 +1119,32 @@ NTSTATUS dcerpc_ndr_request(struct dcerpc_pipe *p, those versions then we need to ignore this error */ } -failed: - ndr_push_free(push); - return status; + return NT_STATUS_OK; +} + + +/* + a useful helper function for synchronous rpc requests + + this can be used when you have ndr push/pull functions in the + standard format +*/ +NTSTATUS dcerpc_ndr_request(struct dcerpc_pipe *p, + uint32_t opnum, + TALLOC_CTX *mem_ctx, + NTSTATUS (*ndr_push)(struct ndr_push *, int, void *), + NTSTATUS (*ndr_pull)(struct ndr_pull *, int, void *), + void *struct_ptr, + size_t struct_size) +{ + struct rpc_request *req; + + req = dcerpc_ndr_request_send(p, opnum, mem_ctx, ndr_push, ndr_pull, struct_ptr, struct_size); + if (req == NULL) { + return NT_STATUS_NO_MEMORY; + } + + return dcerpc_ndr_request_recv(req); } diff --git a/source4/librpc/rpc/dcerpc.h b/source4/librpc/rpc/dcerpc.h index 2c36241020..2dce5df92a 100644 --- a/source4/librpc/rpc/dcerpc.h +++ b/source4/librpc/rpc/dcerpc.h @@ -32,7 +32,6 @@ struct dcerpc_security { }; struct dcerpc_pipe { - TALLOC_CTX *mem_ctx; int reference_count; uint32_t call_id; uint32_t srv_max_xmit_frag; @@ -47,16 +46,33 @@ struct dcerpc_pipe { struct dcerpc_transport { enum dcerpc_transport_t transport; void *private; - NTSTATUS (*full_request)(struct dcerpc_pipe *, - TALLOC_CTX *, DATA_BLOB *, DATA_BLOB *); - NTSTATUS (*secondary_request)(struct dcerpc_pipe *, TALLOC_CTX *, DATA_BLOB *); - NTSTATUS (*initial_request)(struct dcerpc_pipe *, TALLOC_CTX *, DATA_BLOB *); + NTSTATUS (*shutdown_pipe)(struct dcerpc_pipe *); + const char *(*peer_name)(struct dcerpc_pipe *); + + /* send a request to the server */ + NTSTATUS (*send_request)(struct dcerpc_pipe *, DATA_BLOB *); + + /* send a read request to the server */ + NTSTATUS (*send_read)(struct dcerpc_pipe *); + + /* get an event context for the connection */ + struct event_context *(*event_context)(struct dcerpc_pipe *); + + /* a callback to the dcerpc code when a full fragment + has been received */ + void (*recv_data)(struct dcerpc_pipe *, DATA_BLOB *, NTSTATUS status); } transport; /* the last fault code from a DCERPC fault */ uint32_t last_fault_code; + + /* pending requests */ + struct rpc_request *pending; + + /* private pointer for pending full requests */ + void *full_request_private; }; /* dcerpc pipe flags */ @@ -119,3 +135,32 @@ struct dcerpc_binding { const char **options; uint32_t flags; }; + + +enum rpc_request_state { + RPC_REQUEST_PENDING, + RPC_REQUEST_DONE +}; + +/* + handle for an async dcerpc request +*/ +struct rpc_request { + struct rpc_request *next, *prev; + struct dcerpc_pipe *p; + NTSTATUS status; + uint32_t call_id; + enum rpc_request_state state; + DATA_BLOB payload; + uint_t flags; + uint32_t fault_code; + + /* use by the ndr level async recv call */ + struct rpc_request_ndr { + NTSTATUS (*ndr_push)(struct ndr_push *, int, void *); + NTSTATUS (*ndr_pull)(struct ndr_pull *, int, void *); + void *struct_ptr; + size_t struct_size; + TALLOC_CTX *mem_ctx; + } ndr; +}; diff --git a/source4/librpc/rpc/dcerpc_auth.c b/source4/librpc/rpc/dcerpc_auth.c index 6ae6a80596..9587fb9390 100644 --- a/source4/librpc/rpc/dcerpc_auth.c +++ b/source4/librpc/rpc/dcerpc_auth.c @@ -70,7 +70,7 @@ NTSTATUS dcerpc_bind_auth3(struct dcerpc_pipe *p, uint8_t auth_type, uint8_t aut } } - p->security_state.auth_info = talloc(p->mem_ctx, sizeof(*p->security_state.auth_info)); + p->security_state.auth_info = talloc(p, sizeof(*p->security_state.auth_info)); if (!p->security_state.auth_info) { status = NT_STATUS_NO_MEMORY; goto done; @@ -146,7 +146,7 @@ NTSTATUS dcerpc_bind_alter(struct dcerpc_pipe *p, uint8_t auth_type, uint8_t aut } } - p->security_state.auth_info = talloc(p->mem_ctx, sizeof(*p->security_state.auth_info)); + p->security_state.auth_info = talloc(p, sizeof(*p->security_state.auth_info)); if (!p->security_state.auth_info) { status = NT_STATUS_NO_MEMORY; goto done; diff --git a/source4/librpc/rpc/dcerpc_error.c b/source4/librpc/rpc/dcerpc_error.c index d1456cad72..c9434060a1 100644 --- a/source4/librpc/rpc/dcerpc_error.c +++ b/source4/librpc/rpc/dcerpc_error.c @@ -40,10 +40,8 @@ static const struct dcerpc_fault_table dcerpc_faults[] = { NULL, 0} }; -const char *dcerpc_errstr(uint32_t fault_code) +const char *dcerpc_errstr(TALLOC_CTX *mem_ctx, uint32_t fault_code) { - /* TODO: remove static pstring! */ - static pstring msg; int idx = 0; while (dcerpc_faults[idx].errstr != NULL) { @@ -53,7 +51,5 @@ const char *dcerpc_errstr(uint32_t fault_code) idx++; } - slprintf(msg, sizeof(msg), "DCERPC fault 0x%08x", fault_code); - - return msg; + return talloc_asprintf(mem_ctx, "DCERPC fault 0x%08x", fault_code); } diff --git a/source4/librpc/rpc/dcerpc_schannel.c b/source4/librpc/rpc/dcerpc_schannel.c index e7b1d08988..efe609f9a3 100644 --- a/source4/librpc/rpc/dcerpc_schannel.c +++ b/source4/librpc/rpc/dcerpc_schannel.c @@ -369,14 +369,14 @@ static NTSTATUS dcerpc_schannel_key(struct dcerpc_pipe *p, /* step 2 - request a netlogon challenge */ - r.in.server_name = talloc_asprintf(p->mem_ctx, "\\\\%s", dcerpc_server_name(p)); + r.in.server_name = talloc_asprintf(p, "\\\\%s", dcerpc_server_name(p)); r.in.computer_name = workstation; r.in.credentials = &credentials1; r.out.credentials = &credentials2; generate_random_buffer(credentials1.data, sizeof(credentials1.data)); - status = dcerpc_netr_ServerReqChallenge(p2, p->mem_ctx, &r); + status = dcerpc_netr_ServerReqChallenge(p2, p, &r); if (!NT_STATUS_IS_OK(status)) { return status; } @@ -389,7 +389,7 @@ static NTSTATUS dcerpc_schannel_key(struct dcerpc_pipe *p, negotiate_flags); a.in.server_name = r.in.server_name; - a.in.account_name = talloc_asprintf(p->mem_ctx, "%s$", workstation); + a.in.account_name = talloc_asprintf(p, "%s$", workstation); a.in.secure_channel_type = chan_type; a.in.computer_name = workstation; a.in.negotiate_flags = &negotiate_flags; @@ -397,7 +397,7 @@ static NTSTATUS dcerpc_schannel_key(struct dcerpc_pipe *p, a.in.credentials = &credentials3; a.out.credentials = &credentials3; - status = dcerpc_netr_ServerAuthenticate2(p2, p->mem_ctx, &a); + status = dcerpc_netr_ServerAuthenticate2(p2, p, &a); if (!NT_STATUS_IS_OK(status)) { return status; } diff --git a/source4/librpc/rpc/dcerpc_smb.c b/source4/librpc/rpc/dcerpc_smb.c index d3a0a42d10..fffc945403 100644 --- a/source4/librpc/rpc/dcerpc_smb.c +++ b/source4/librpc/rpc/dcerpc_smb.c @@ -29,221 +29,156 @@ struct smb_private { struct smbcli_tree *tree; }; -static struct smbcli_request *dcerpc_raw_send(struct dcerpc_pipe *p, DATA_BLOB *blob) -{ - struct smb_private *smb = p->transport.private; - struct smb_trans2 trans; - uint16_t setup[2]; - struct smbcli_request *req; - TALLOC_CTX *mem_ctx; - - mem_ctx = talloc_init("dcerpc_raw_send"); - if (!mem_ctx) return NULL; - - 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\\"; - req = smb_raw_trans_send(smb->tree, &trans); - - talloc_destroy(mem_ctx); - - return req; +/* + tell the dcerpc layer that the transport is dead +*/ +static void pipe_dead(struct dcerpc_pipe *p, NTSTATUS status) +{ + p->transport.recv_data(p, NULL, status); } -static NTSTATUS dcerpc_raw_recv(struct dcerpc_pipe *p, - struct smbcli_request *req, - TALLOC_CTX *mem_ctx, - DATA_BLOB *blob) +/* + this holds the state of an in-flight call +*/ +struct smb_read_state { + struct dcerpc_pipe *p; + struct smbcli_request *req; + size_t received; + DATA_BLOB data; + union smb_read *io; +}; + +/* + called when a read request has completed +*/ +static void smb_read_callback(struct smbcli_request *req) { - struct smb_private *smb = p->transport.private; - struct smb_trans2 trans; - NTSTATUS status; + struct smb_private *smb; + struct smb_read_state *state; + union smb_read *io; uint16_t frag_length; - DATA_BLOB payload; + NTSTATUS status; - status = smb_raw_trans_recv(req, mem_ctx, &trans); + state = req->async.private; + smb = state->p->transport.private; + io = state->io; - /* STATUS_BUFFER_OVERFLOW means that there is more data - available via SMBreadX */ - if (!NT_STATUS_IS_OK(status) && - !NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) { - return status; + if (!NT_STATUS_IS_OK(req->status)) { + pipe_dead(state->p, req->status); + talloc_free(state); + return; } - payload = trans.out.data; - - if (trans.out.data.length < 16 || - !NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) { - goto done; + status = smb_raw_read_recv(state->req, io); + if (!NT_STATUS_IS_OK(status)) { + pipe_dead(state->p, status); + talloc_free(state); + return; } - /* we might have recieved a partial fragment, in which case we - need to pull the rest of it */ - frag_length = dcerpc_get_frag_length(&payload); - if (frag_length <= payload.length) { - goto done; - } + state->received += io->readx.out.nread; - /* make sure the payload can hold the whole fragment */ - payload.data = talloc_realloc(payload.data, frag_length); - if (!payload.data) { - return NT_STATUS_NO_MEMORY; + if (state->received < 16) { + DEBUG(0,("dcerpc_smb: short packet (length %d) in read callback!\n", + state->received)); + pipe_dead(state->p, NT_STATUS_INFO_LENGTH_MISMATCH); + talloc_free(state); + return; } - /* the rest of the data is available via SMBreadX */ - while (frag_length > payload.length) { - uint32_t n; - union smb_read io; - - n = frag_length - payload.length; - if (n > 0xFF00) { - n = 0xFF00; - } - - io.generic.level = RAW_READ_READX; - io.readx.in.fnum = smb->fnum; - io.readx.in.mincnt = n; - io.readx.in.maxcnt = n; - io.readx.in.offset = 0; - io.readx.in.remaining = 0; - io.readx.out.data = payload.data + payload.length; - status = smb_raw_read(smb->tree, &io); - if (!NT_STATUS_IS_OK(status) && - !NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) { - break; - } - - n = io.readx.out.nread; - if (n == 0) { - status = NT_STATUS_UNSUCCESSFUL; - break; - } - - payload.length += n; - - /* if the SMBreadX returns NT_STATUS_OK then there - isn't any more data to be read */ - if (NT_STATUS_IS_OK(status)) { - break; - } + 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); + talloc_free(state); + return; } -done: - if (blob) { - *blob = payload; - } + /* initiate another read request, as we only got part of a fragment */ + state->data.data = talloc_realloc(state->data.data, frag_length); - return status; -} + io->readx.in.mincnt = frag_length - state->received; + io->readx.in.maxcnt = io->readx.in.mincnt; + io->readx.out.data = state->data.data + state->received; -static NTSTATUS smb_full_request(struct dcerpc_pipe *p, - TALLOC_CTX *mem_ctx, - DATA_BLOB *request_blob, - DATA_BLOB *reply_blob) -{ - struct smbcli_request *req; - req = dcerpc_raw_send(p, request_blob); - return dcerpc_raw_recv(p, req, mem_ctx, reply_blob); + req = smb_raw_read_send(smb->tree, io); + if (req == NULL) { + pipe_dead(state->p, NT_STATUS_NO_MEMORY); + talloc_free(state); + return; + } + + req->async.fn = smb_read_callback; + req->async.private = state; } - -/* - retrieve a secondary pdu from a pipe +/* + trigger a read request from the server */ -static NTSTATUS smb_secondary_request(struct dcerpc_pipe *p, - TALLOC_CTX *mem_ctx, - DATA_BLOB *blob) +static NTSTATUS send_read_request(struct dcerpc_pipe *p) { struct smb_private *smb = p->transport.private; - union smb_read io; - uint32_t n = 0x2000; - uint32_t frag_length; - NTSTATUS status; + union smb_read *io; + struct smb_read_state *state; + struct smbcli_request *req; - *blob = data_blob_talloc(mem_ctx, NULL, n); - if (!blob->data) { + state = talloc_p(smb, struct smb_read_state); + if (state == NULL) { return NT_STATUS_NO_MEMORY; } - io.generic.level = RAW_READ_READX; - io.readx.in.fnum = smb->fnum; - io.readx.in.mincnt = n; - io.readx.in.maxcnt = n; - io.readx.in.offset = 0; - io.readx.in.remaining = 0; - io.readx.out.data = blob->data; - - status = smb_raw_read(smb->tree, &io); - if (!NT_STATUS_IS_OK(status) && - !NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) { - return status; + state->p = p; + state->received = 0; + state->data = data_blob_talloc(state, NULL, 0x2000); + 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.offset = 0; + io->readx.in.remaining = 0; + io->readx.out.data = state->data.data; + req = smb_raw_read_send(smb->tree, io); + if (req == NULL) { + return NT_STATUS_NO_MEMORY; } - blob->length = io.readx.out.nread; + req->async.fn = smb_read_callback; + req->async.private = state; - if (blob->length < 16) { - return status; - } + state->req = req; - frag_length = dcerpc_get_frag_length(blob); - if (frag_length <= blob->length) { - return status; - } + return NT_STATUS_OK; +} - blob->data = talloc_realloc(blob->data, frag_length); - if (!blob->data) { - return NT_STATUS_NO_MEMORY; - } - while (frag_length > blob->length && - NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) { - - n = frag_length - blob->length; - if (n > 0xFF00) { - n = 0xFF00; - } - - io.readx.in.mincnt = n; - io.readx.in.maxcnt = n; - io.readx.out.data = blob->data + blob->length; - status = smb_raw_read(smb->tree, &io); - - if (!NT_STATUS_IS_OK(status) && - !NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) { - return status; - } - - n = io.readx.out.nread; - blob->length += n; +/* + called when a write request has completed +*/ +static void smb_write_callback(struct smbcli_request *req) +{ + struct dcerpc_pipe *p = req->async.private; + + if (!NT_STATUS_IS_OK(req->status)) { + DEBUG(0,("dcerpc_smb: write callback error\n")); + pipe_dead(p, req->status); } - return status; + smbcli_request_destroy(req); } - /* - send an initial pdu in a multi-pdu sequence + send a packet to the server */ -static NTSTATUS smb_initial_request(struct dcerpc_pipe *p, - TALLOC_CTX *mem_ctx, - DATA_BLOB *blob) +static NTSTATUS smb_send_request(struct dcerpc_pipe *p, DATA_BLOB *blob) { struct smb_private *smb = p->transport.private; union smb_write io; - NTSTATUS status; + struct smbcli_request *req; io.generic.level = RAW_WRITE_WRITEX; io.writex.in.fnum = smb->fnum; @@ -253,17 +188,26 @@ static NTSTATUS smb_initial_request(struct dcerpc_pipe *p, io.writex.in.count = blob->length; io.writex.in.data = blob->data; - status = smb_raw_write(smb->tree, &io); - if (NT_STATUS_IS_OK(status)) { - return status; + req = smb_raw_write_send(smb->tree, &io); + if (req == NULL) { + return NT_STATUS_NO_MEMORY; } - /* make sure it accepted it all */ - if (io.writex.out.nwritten != blob->length) { - return NT_STATUS_UNSUCCESSFUL; - } + req->async.fn = smb_write_callback; + req->async.private = p; + + return NT_STATUS_OK; +} + +/* + return the event context for the pipe, so the caller can wait + for events asynchronously +*/ +static struct event_context *smb_event_context(struct dcerpc_pipe *p) +{ + struct smb_private *smb = p->transport.private; - return status; + return smb->tree->session->transport->event.ctx; } @@ -356,13 +300,15 @@ NTSTATUS dcerpc_pipe_open_smb(struct dcerpc_pipe **p, */ (*p)->transport.transport = NCACN_NP; (*p)->transport.private = NULL; - (*p)->transport.full_request = smb_full_request; - (*p)->transport.secondary_request = smb_secondary_request; - (*p)->transport.initial_request = smb_initial_request; (*p)->transport.shutdown_pipe = smb_shutdown_pipe; (*p)->transport.peer_name = smb_peer_name; + + (*p)->transport.send_request = smb_send_request; + (*p)->transport.send_read = send_read_request; + (*p)->transport.event_context = smb_event_context; + (*p)->transport.recv_data = NULL; - smb = talloc((*p)->mem_ctx, sizeof(*smb)); + smb = talloc((*p), sizeof(*smb)); if (!smb) { dcerpc_pipe_close(*p); return NT_STATUS_NO_MEMORY; diff --git a/source4/librpc/rpc/dcerpc_tcp.c b/source4/librpc/rpc/dcerpc_tcp.c index 05c700ea89..7cf7cc98bb 100644 --- a/source4/librpc/rpc/dcerpc_tcp.c +++ b/source4/librpc/rpc/dcerpc_tcp.c @@ -22,136 +22,239 @@ #include "includes.h" +#define MIN_HDR_SIZE 16 + +struct tcp_blob { + struct tcp_blob *next, *prev; + DATA_BLOB data; +}; + /* transport private information used by TCP pipe transport */ struct tcp_private { + struct event_context *event_ctx; + struct fd_event *fde; int fd; char *server_name; uint32_t port; + + struct tcp_blob *pending_send; + + struct { + size_t received; + DATA_BLOB data; + uint_t pending_count; + } recv; }; /* mark the socket dead */ -static void tcp_sock_dead(struct tcp_private *tcp) +static void tcp_sock_dead(struct dcerpc_pipe *p, NTSTATUS status) { + struct tcp_private *tcp = p->transport.private; + if (tcp && tcp->fd != -1) { close(tcp->fd); tcp->fd = -1; } + + /* wipe any pending sends */ + while (tcp->pending_send) { + struct tcp_blob *blob = tcp->pending_send; + DLIST_REMOVE(tcp->pending_send, blob); + talloc_free(blob); + } + + if (!NT_STATUS_IS_OK(status)) { + p->transport.recv_data(p, NULL, status); + } } -static NTSTATUS tcp_raw_recv(struct dcerpc_pipe *p, - TALLOC_CTX *mem_ctx, - DATA_BLOB *blob) +/* + process send requests +*/ +static void tcp_process_send(struct dcerpc_pipe *p) { struct tcp_private *tcp = p->transport.private; - ssize_t ret; - uint32_t frag_length; - DATA_BLOB blob1; - blob1 = data_blob_talloc(mem_ctx, NULL, 16); - if (!blob1.data) { - return NT_STATUS_NO_MEMORY; + while (tcp->pending_send) { + struct tcp_blob *blob = tcp->pending_send; + ssize_t ret = write(tcp->fd, blob->data.data, blob->data.length); + if (ret == -1) { + if (errno != EAGAIN && errno != EINTR) { + tcp_sock_dead(p, NT_STATUS_NET_WRITE_FAULT); + } + break; + } + + blob->data.data += ret; + blob->data.length -= ret; + + if (blob->data.length != 0) { + break; + } + + DLIST_REMOVE(tcp->pending_send, blob); + talloc_free(blob); } - ret = read_data(tcp->fd, blob1.data, blob1.length); - if (ret != blob1.length) { - tcp_sock_dead(tcp); - return NT_STATUS_UNEXPECTED_NETWORK_ERROR; + if (tcp->pending_send == NULL) { + tcp->fde->flags &= ~EVENT_FD_WRITE; } +} + + +/* + process recv requests +*/ +static void tcp_process_recv(struct dcerpc_pipe *p) +{ + struct tcp_private *tcp = p->transport.private; + ssize_t ret; - /* this could be a ncacn_http endpoint - this doesn't work - yet, but it goes close */ - if (strncmp(blob1.data, "ncacn_http/1.0", 14) == 0) { - memmove(blob1.data, blob1.data+14, 2); - ret = read_data(tcp->fd, blob1.data+2, 14); - if (ret != 14) { - tcp_sock_dead(tcp); - return NT_STATUS_UNEXPECTED_NETWORK_ERROR; + /* read in the base header to get the fragment length */ + if (tcp->recv.received < MIN_HDR_SIZE) { + uint32_t frag_length; + + ret = read(tcp->fd, tcp->recv.data.data, + MIN_HDR_SIZE - tcp->recv.received); + if (ret == -1) { + if (errno != EAGAIN && errno != EINTR) { + tcp_sock_dead(p, NT_STATUS_NET_WRITE_FAULT); + } + return; } - } + if (ret == 0) { + tcp_sock_dead(p, NT_STATUS_NET_WRITE_FAULT); + return; + } + + tcp->recv.received += ret; + + if (tcp->recv.received != MIN_HDR_SIZE) { + return; + } + frag_length = dcerpc_get_frag_length(&tcp->recv.data); - /* we might have recieved a partial fragment, in which case we - need to pull the rest of it */ - frag_length = dcerpc_get_frag_length(&blob1); - if (frag_length == blob1.length) { - *blob = blob1; - return NT_STATUS_OK; + tcp->recv.data.data = talloc_realloc(tcp->recv.data.data, + frag_length); + if (tcp->recv.data.data == NULL) { + tcp_sock_dead(p, NT_STATUS_NO_MEMORY); + return; + } + tcp->recv.data.length = frag_length; } - *blob = data_blob_talloc(mem_ctx, NULL, frag_length); - if (!blob->data) { - return NT_STATUS_NO_MEMORY; + /* read in the rest of the packet */ + ret = read(tcp->fd, tcp->recv.data.data + tcp->recv.received, + tcp->recv.data.length - tcp->recv.received); + if (ret == -1) { + if (errno != EAGAIN && errno != EINTR) { + tcp_sock_dead(p, NT_STATUS_NET_WRITE_FAULT); + } + return; + } + if (ret == 0) { + tcp_sock_dead(p, NT_STATUS_NET_WRITE_FAULT); + return; } - memcpy(blob->data, blob1.data, blob1.length); - ret = read_data(tcp->fd, blob->data + blob1.length, frag_length - blob1.length); - if (ret != frag_length - blob1.length) { - tcp_sock_dead(tcp); - return NT_STATUS_UNEXPECTED_NETWORK_ERROR; + tcp->recv.received += ret; + + if (tcp->recv.received != tcp->recv.data.length) { + return; } - return NT_STATUS_OK; + /* we have a full packet */ + p->transport.recv_data(p, &tcp->recv.data, NT_STATUS_OK); + + tcp->recv.received = 0; + tcp->recv.pending_count--; + if (tcp->recv.pending_count == 0) { + tcp->fde->flags &= ~EVENT_FD_READ; + } } -static NTSTATUS tcp_full_request(struct dcerpc_pipe *p, - TALLOC_CTX *mem_ctx, - DATA_BLOB *request_blob, - DATA_BLOB *reply_blob) +/* + called when a IO is triggered by the events system +*/ +static void tcp_io_handler(struct event_context *ev, struct fd_event *fde, + time_t t, uint16_t flags) { + struct dcerpc_pipe *p = fde->private; struct tcp_private *tcp = p->transport.private; - ssize_t ret; - ret = write_data(tcp->fd, request_blob->data, request_blob->length); - if (ret != request_blob->length) { - tcp_sock_dead(tcp); - return NT_STATUS_UNEXPECTED_NETWORK_ERROR; + if (flags & EVENT_FD_WRITE) { + tcp_process_send(p); } - return tcp_raw_recv(p, mem_ctx, reply_blob); + if (tcp->fd == -1) { + return; + } + + if (flags & EVENT_FD_READ) { + tcp_process_recv(p); + } } - /* - retrieve a secondary pdu from a pipe + send an initial pdu in a multi-pdu sequence */ -static NTSTATUS tcp_secondary_request(struct dcerpc_pipe *p, - TALLOC_CTX *mem_ctx, - DATA_BLOB *blob) +static NTSTATUS tcp_send_request(struct dcerpc_pipe *p, + DATA_BLOB *data) { - return tcp_raw_recv(p, mem_ctx, blob); -} + struct tcp_private *tcp = p->transport.private; + struct tcp_blob *blob; + + blob = talloc_p(tcp, struct tcp_blob); + if (blob == NULL) { + return NT_STATUS_NO_MEMORY; + } + + blob->data = data_blob_talloc(blob, data->data, data->length); + if (blob->data.data == NULL) { + talloc_free(blob); + return NT_STATUS_NO_MEMORY; + } + DLIST_ADD_END(tcp->pending_send, blob, struct tcp_blob *); + + tcp->fde->flags |= EVENT_FD_WRITE; + + return NT_STATUS_OK; +} /* - send an initial pdu in a multi-pdu sequence + initiate a read request */ -static NTSTATUS tcp_initial_request(struct dcerpc_pipe *p, - TALLOC_CTX *mem_ctx, - DATA_BLOB *blob) +static NTSTATUS tcp_send_read(struct dcerpc_pipe *p) { struct tcp_private *tcp = p->transport.private; - ssize_t ret; - ret = write_data(tcp->fd, blob->data, blob->length); - if (ret != blob->length) { - tcp_sock_dead(tcp); - return NT_STATUS_UNEXPECTED_NETWORK_ERROR; + tcp->recv.pending_count++; + if (tcp->recv.pending_count == 1) { + tcp->fde->flags |= EVENT_FD_READ; } - return NT_STATUS_OK; } +/* + return the event context so the caller can process asynchronously +*/ +static struct event_context *tcp_event_context(struct dcerpc_pipe *p) +{ + struct tcp_private *tcp = p->transport.private; + + return tcp->event_ctx; +} /* shutdown TCP pipe connection */ static NTSTATUS tcp_shutdown_pipe(struct dcerpc_pipe *p) { - struct tcp_private *tcp = p->transport.private; - - tcp_sock_dead(tcp); + tcp_sock_dead(p, NT_STATUS_OK); return NT_STATUS_OK; } @@ -176,6 +279,7 @@ NTSTATUS dcerpc_pipe_open_tcp(struct dcerpc_pipe **p, struct tcp_private *tcp; int fd; struct in_addr addr; + struct fd_event fde; if (port == 0) { port = EPMAPPER_PORT; @@ -191,6 +295,8 @@ NTSTATUS dcerpc_pipe_open_tcp(struct dcerpc_pipe **p, return NT_STATUS_PORT_CONNECTION_REFUSED; } + set_blocking(fd, False); + if (!(*p = dcerpc_pipe_init())) { return NT_STATUS_NO_MEMORY; } @@ -200,20 +306,35 @@ NTSTATUS dcerpc_pipe_open_tcp(struct dcerpc_pipe **p, */ (*p)->transport.transport = NCACN_IP_TCP; (*p)->transport.private = NULL; - (*p)->transport.full_request = tcp_full_request; - (*p)->transport.secondary_request = tcp_secondary_request; - (*p)->transport.initial_request = tcp_initial_request; + + (*p)->transport.send_request = tcp_send_request; + (*p)->transport.send_read = tcp_send_read; + (*p)->transport.event_context = tcp_event_context; + (*p)->transport.recv_data = NULL; + (*p)->transport.shutdown_pipe = tcp_shutdown_pipe; (*p)->transport.peer_name = tcp_peer_name; - tcp = talloc((*p)->mem_ctx, sizeof(*tcp)); + tcp = talloc((*p), sizeof(*tcp)); if (!tcp) { dcerpc_pipe_close(*p); return NT_STATUS_NO_MEMORY; } tcp->fd = fd; - tcp->server_name = talloc_strdup((*p)->mem_ctx, server); + tcp->server_name = talloc_strdup((*p), server); + tcp->event_ctx = event_context_init(); + tcp->pending_send = NULL; + tcp->recv.received = 0; + tcp->recv.data = data_blob_talloc(tcp, NULL, MIN_HDR_SIZE); + tcp->recv.pending_count = 0; + + fde.fd = fd; + fde.flags = 0; + fde.handler = tcp_io_handler; + fde.private = *p; + + tcp->fde = event_add_fd(tcp->event_ctx, &fde); (*p)->transport.private = tcp; diff --git a/source4/librpc/rpc/dcerpc_util.c b/source4/librpc/rpc/dcerpc_util.c index 21325659f7..5a3b875c01 100644 --- a/source4/librpc/rpc/dcerpc_util.c +++ b/source4/librpc/rpc/dcerpc_util.c @@ -76,7 +76,7 @@ NTSTATUS dcerpc_epm_map_tcp_port(const char *server, /* we can use the pipes memory context here as we will have a short lived connection */ - status = dcerpc_bind_byuuid(p, p->mem_ctx, + status = dcerpc_bind_byuuid(p, p, DCERPC_EPMAPPER_UUID, DCERPC_EPMAPPER_VERSION); if (!NT_STATUS_IS_OK(status)) { @@ -88,7 +88,7 @@ NTSTATUS dcerpc_epm_map_tcp_port(const char *server, ZERO_STRUCT(guid); twr.towers.num_floors = 5; - twr.towers.floors = talloc(p->mem_ctx, sizeof(twr.towers.floors[0]) * 5); + twr.towers.floors = talloc(p, sizeof(twr.towers.floors[0]) * 5); /* what I'd like for christmas ... */ @@ -96,28 +96,28 @@ NTSTATUS dcerpc_epm_map_tcp_port(const char *server, twr.towers.floors[0].lhs.protocol = EPM_PROTOCOL_UUID; GUID_from_string(uuid, &twr.towers.floors[0].lhs.info.uuid.uuid); twr.towers.floors[0].lhs.info.uuid.version = version; - twr.towers.floors[0].rhs.rhs_data = data_blob_talloc_zero(p->mem_ctx, 2); + twr.towers.floors[0].rhs.rhs_data = data_blob_talloc_zero(p, 2); /* encoded with NDR ... */ twr.towers.floors[1].lhs.protocol = EPM_PROTOCOL_UUID; GUID_from_string(NDR_GUID, &twr.towers.floors[1].lhs.info.uuid.uuid); twr.towers.floors[1].lhs.info.uuid.version = NDR_GUID_VERSION; - twr.towers.floors[1].rhs.rhs_data = data_blob_talloc_zero(p->mem_ctx, 2); + twr.towers.floors[1].rhs.rhs_data = data_blob_talloc_zero(p, 2); /* on an RPC connection ... */ twr.towers.floors[2].lhs.protocol = EPM_PROTOCOL_NCACN_RPC_C; twr.towers.floors[2].lhs.info.lhs_data = data_blob(NULL, 0); - twr.towers.floors[2].rhs.rhs_data = data_blob_talloc_zero(p->mem_ctx, 2); + twr.towers.floors[2].rhs.rhs_data = data_blob_talloc_zero(p, 2); /* on a TCP port ... */ twr.towers.floors[3].lhs.protocol = EPM_PROTOCOL_NCACN_TCP; twr.towers.floors[3].lhs.info.lhs_data = data_blob(NULL, 0); - twr.towers.floors[3].rhs.rhs_data = data_blob_talloc_zero(p->mem_ctx, 2); + twr.towers.floors[3].rhs.rhs_data = data_blob_talloc_zero(p, 2); /* on an IP link ... */ twr.towers.floors[4].lhs.protocol = EPM_PROTOCOL_NCACN_IP; twr.towers.floors[4].lhs.info.lhs_data = data_blob(NULL, 0); - twr.towers.floors[4].rhs.rhs_data = data_blob_talloc_zero(p->mem_ctx, 4); + twr.towers.floors[4].rhs.rhs_data = data_blob_talloc_zero(p, 4); /* with some nice pretty paper around it of course */ r.in.object = &guid; @@ -126,7 +126,7 @@ NTSTATUS dcerpc_epm_map_tcp_port(const char *server, r.in.max_towers = 1; r.out.entry_handle = &handle; - status = dcerpc_epm_Map(p, p->mem_ctx, &r); + status = dcerpc_epm_Map(p, p, &r); if (!NT_STATUS_IS_OK(status)) { dcerpc_pipe_close(p); return status; @@ -490,7 +490,7 @@ static NTSTATUS dcerpc_pipe_connect_ncacn_np(struct dcerpc_pipe **p, (*p)->flags = binding->flags; /* remember the binding string for possible secondary connections */ - (*p)->binding_string = dcerpc_binding_string((*p)->mem_ctx, binding); + (*p)->binding_string = dcerpc_binding_string((*p), binding); if (username && username[0] && (binding->flags & DCERPC_SCHANNEL_ANY)) { status = dcerpc_bind_auth_schannel(*p, pipe_uuid, pipe_version, @@ -556,7 +556,7 @@ static NTSTATUS dcerpc_pipe_connect_ncacn_ip_tcp(struct dcerpc_pipe **p, (*p)->flags = binding->flags; /* remember the binding string for possible secondary connections */ - (*p)->binding_string = dcerpc_binding_string((*p)->mem_ctx, binding); + (*p)->binding_string = dcerpc_binding_string((*p), binding); if (username && username[0] && (binding->flags & DCERPC_SCHANNEL_ANY)) { status = dcerpc_bind_auth_schannel(*p, pipe_uuid, pipe_version, @@ -665,7 +665,7 @@ NTSTATUS dcerpc_secondary_connection(struct dcerpc_pipe *p, struct dcerpc_pipe * break; case NCACN_IP_TCP: - status = dcerpc_parse_binding(p->mem_ctx, p->binding_string, &b); + status = dcerpc_parse_binding(p, p->binding_string, &b); if (!NT_STATUS_IS_OK(status)) { return status; } diff --git a/source4/torture/rpc/drsuapi.c b/source4/torture/rpc/drsuapi.c index 5a12ef37f4..d056ab92da 100644 --- a/source4/torture/rpc/drsuapi.c +++ b/source4/torture/rpc/drsuapi.c @@ -197,7 +197,7 @@ static BOOL test_DRSBind(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx) if (!NT_STATUS_IS_OK(status)) { const char *errstr = nt_errstr(status); if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { - errstr = dcerpc_errstr(p->last_fault_code); + errstr = dcerpc_errstr(mem_ctx, p->last_fault_code); } printf("DRSUAPI_BIND level failed - %s\n", errstr); ret = False; diff --git a/source4/torture/rpc/epmapper.c b/source4/torture/rpc/epmapper.c index 9f5c337f15..6a50d18276 100644 --- a/source4/torture/rpc/epmapper.c +++ b/source4/torture/rpc/epmapper.c @@ -151,15 +151,15 @@ static BOOL test_Map(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, twr->towers.floors[2].lhs.protocol = EPM_PROTOCOL_NCACN_RPC_C; twr->towers.floors[2].lhs.info.lhs_data = data_blob(NULL, 0); - twr->towers.floors[2].rhs.rhs_data = data_blob_talloc_zero(p->mem_ctx, 2); + twr->towers.floors[2].rhs.rhs_data = data_blob_talloc_zero(p, 2); twr->towers.floors[3].lhs.protocol = EPM_PROTOCOL_NCACN_TCP; twr->towers.floors[3].lhs.info.lhs_data = data_blob(NULL, 0); - twr->towers.floors[3].rhs.rhs_data = data_blob_talloc_zero(p->mem_ctx, 2); + twr->towers.floors[3].rhs.rhs_data = data_blob_talloc_zero(p, 2); twr->towers.floors[4].lhs.protocol = EPM_PROTOCOL_NCACN_IP; twr->towers.floors[4].lhs.info.lhs_data = data_blob(NULL, 0); - twr->towers.floors[4].rhs.rhs_data = data_blob_talloc_zero(p->mem_ctx, 4); + twr->towers.floors[4].rhs.rhs_data = data_blob_talloc_zero(p, 4); status = dcerpc_epm_Map(p, mem_ctx, &r); if (NT_STATUS_IS_OK(status) && r.out.result == 0) { @@ -172,7 +172,7 @@ static BOOL test_Map(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, twr->towers.floors[3].lhs.protocol = EPM_PROTOCOL_NCACN_HTTP; twr->towers.floors[3].lhs.info.lhs_data = data_blob(NULL, 0); - twr->towers.floors[3].rhs.rhs_data = data_blob_talloc_zero(p->mem_ctx, 2); + twr->towers.floors[3].rhs.rhs_data = data_blob_talloc_zero(p, 2); status = dcerpc_epm_Map(p, mem_ctx, &r); if (NT_STATUS_IS_OK(status) && r.out.result == 0) { @@ -185,11 +185,11 @@ static BOOL test_Map(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, twr->towers.floors[3].lhs.protocol = EPM_PROTOCOL_NCACN_SMB; twr->towers.floors[3].lhs.info.lhs_data = data_blob(NULL, 0); - twr->towers.floors[3].rhs.rhs_data = data_blob_talloc_zero(p->mem_ctx, 2); + twr->towers.floors[3].rhs.rhs_data = data_blob_talloc_zero(p, 2); twr->towers.floors[4].lhs.protocol = EPM_PROTOCOL_NCACN_NETBIOS; twr->towers.floors[4].lhs.info.lhs_data = data_blob(NULL, 0); - twr->towers.floors[4].rhs.rhs_data = data_blob_talloc_zero(p->mem_ctx, 2); + twr->towers.floors[4].rhs.rhs_data = data_blob_talloc_zero(p, 2); status = dcerpc_epm_Map(p, mem_ctx, &r); if (NT_STATUS_IS_OK(status) && r.out.result == 0) { diff --git a/source4/torture/rpc/testjoin.c b/source4/torture/rpc/testjoin.c index 9c9fd749fb..32ea6c5ce4 100644 --- a/source4/torture/rpc/testjoin.c +++ b/source4/torture/rpc/testjoin.c @@ -141,7 +141,7 @@ void *torture_join_domain(const char *machine_name, if (!NT_STATUS_IS_OK(status)) { const char *errstr = nt_errstr(status); if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) { - errstr = dcerpc_errstr(join->p->last_fault_code); + errstr = dcerpc_errstr(mem_ctx, join->p->last_fault_code); } printf("samr_Connect failed - %s\n", errstr); goto failed; |