summaryrefslogtreecommitdiff
path: root/source4/librpc/rpc/dcerpc_tcp.c
diff options
context:
space:
mode:
Diffstat (limited to 'source4/librpc/rpc/dcerpc_tcp.c')
-rw-r--r--source4/librpc/rpc/dcerpc_tcp.c267
1 files changed, 194 insertions, 73 deletions
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;