server side dcerpc over tcp code
Copyright (C) Andrew Tridgell 2003
-
+ Copyright (C) Stefan (metze) Metzmacher 2004
+
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
#include "includes.h"
-struct rpc_server_context {
- struct dcesrv_endpoint *endpoint;
- const struct dcesrv_endpoint_ops *endpoint_ops;
- const struct model_ops *model_ops;
- struct dcesrv_state *dce;
- struct dcesrv_context dcesrv_context;
- int socket_fd;
- struct event_context *events;
+struct dcesrv_socket_context {
+ const struct dcesrv_endpoint *endpoint;
+ struct dcesrv_context *dcesrv_ctx;
};
/*
- a callback from the process model termination routine
+ write_fn callback for dcesrv_output()
*/
-void rpc_server_terminate(void *rr)
+static ssize_t dcerpc_write_fn(void *private, DATA_BLOB *out)
{
- struct rpc_server_context *r = rr;
+ NTSTATUS status;
+ struct socket_context *sock = private;
+ size_t sendlen;
- dcesrv_endpoint_disconnect(r->dce);
- close(r->socket_fd);
- event_remove_fd_all(r->events, r->socket_fd);
- free(r);
+ status = socket_send(sock, sock, out, &sendlen, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ return -1;
+ }
+
+ return sendlen;
}
-/*
- called when a rpc session needs to be shutdown
-*/
-static void terminate_rpc_session(struct rpc_server_context *r, const char *reason)
+void dcesrv_terminate_connection(struct dcesrv_connection *dce_conn, const char *reason)
{
- r->model_ops->terminate_rpc_connection(r, reason);
+ server_terminate_connection(dce_conn->srv_conn, reason);
}
/*
- called when a RPC socket becomes writable
+ add a socket address to the list of events, one event per dcerpc endpoint
*/
-static void dcerpc_write_handler(struct event_context *ev, struct fd_event *fde,
- time_t t, uint16 flags)
+static void add_socket_rpc(struct server_service *service,
+ const struct model_ops *model_ops,
+ struct dcesrv_context *dce_ctx,
+ struct in_addr *ifip)
{
- struct rpc_server_context *r = fde->private;
- DATA_BLOB blob;
- NTSTATUS status;
+ struct dcesrv_endpoint *e;
+ char *ip_str = talloc_strdup(service, inet_ntoa(*ifip));
+
+ for (e=dce_ctx->endpoint_list;e;e=e->next) {
+ if (e->ep_description.transport == NCACN_IP_TCP) {
+ struct server_socket *sock;
+ struct dcesrv_socket_context *dcesrv_sock;
+ uint16_t port = 0;
+
+ if (e->ep_description.options && e->ep_description.options[0])
+ port = atoi(e->ep_description.options[0]);
+
+ sock = service_setup_socket(service,model_ops, "ipv4", ip_str, &port);
+ if (!sock) {
+ DEBUG(0,("service_setup_socket(port=%u) failed\n",port));
+ continue;
+ }
- blob = data_blob(NULL, 0x4000);
- if (!blob.data) {
- terminate_rpc_session(r, "out of memory");
- return;
- }
+ /* And put the settings back into the binding. This will
+ * go away once we store the 'encoded' endpoint instead of a
+ * string describing it */
+ if (e->ep_description.options == NULL) {
+ e->ep_description.options = talloc_array_p(dce_ctx, const char *, 2);
+ e->ep_description.options[0] = talloc_asprintf(dce_ctx, "%d", port);
+ e->ep_description.options[1] = NULL;
+ }
- status = dcesrv_output(r->dce, &blob);
+ dcesrv_sock = talloc_p(sock, struct dcesrv_socket_context);
+ if (!dcesrv_sock) {
+ DEBUG(0,("talloc_p(sock->mem_ctx, struct dcesrv_socket_context) failed\n"));
+ continue;
+ }
- if (NT_STATUS_IS_OK(status)) {
- write_data(fde->fd, blob.data, blob.length);
- }
+ /* remember the endpoint of this socket */
+ dcesrv_sock->endpoint = e;
+ dcesrv_sock->dcesrv_ctx = dce_ctx;
- if (!r->dce->call_list || !r->dce->call_list->replies) {
- fde->flags &= ~EVENT_FD_WRITE;
+ sock->private_data = dcesrv_sock;
+ }
}
- data_blob_free(&blob);
+ talloc_free(ip_str);
}
-/*
- called when a RPC socket becomes readable
-*/
-static void dcerpc_read_handler(struct event_context *ev, struct fd_event *fde,
- time_t t, uint16 flags)
+/****************************************************************************
+ Open the listening sockets for RPC over TCP
+****************************************************************************/
+void dcesrv_tcp_init(struct server_service *service, const struct model_ops *model_ops, struct dcesrv_context *dce_ctx)
{
- struct rpc_server_context *r = fde->private;
- DATA_BLOB blob;
- ssize_t ret;
+ DEBUG(1,("dcesrv_tcp_init\n"));
- blob = data_blob(NULL, 0x4000);
- if (!blob.data) {
- terminate_rpc_session(r, "out of memory");
- return;
- }
-
- ret = read(fde->fd, blob.data, blob.length);
- if (ret == 0 || (ret == -1 && errno != EINTR)) {
- data_blob_free(&blob);
- terminate_rpc_session(r, "eof on socket");
- return;
- }
- if (ret == -1) {
- data_blob_free(&blob);
- return;
+ if (lp_interfaces() && lp_bind_interfaces_only()) {
+ int num_interfaces = iface_count();
+ int i;
+ for(i = 0; i < num_interfaces; i++) {
+ struct in_addr *ifip = iface_n_ip(i);
+ if (ifip == NULL) {
+ continue;
+ }
+ add_socket_rpc(service, model_ops, dce_ctx, ifip);
+ }
+ } else {
+ struct in_addr *ifip;
+ ifip = interpret_addr2(dce_ctx, lp_socket_address());
+ add_socket_rpc(service, model_ops, dce_ctx, ifip);
+ talloc_free(ifip);
}
- blob.length = ret;
-
- dcesrv_input(r->dce, &blob);
-
- data_blob_free(&blob);
-
- if (r->dce->call_list && r->dce->call_list->replies) {
- fde->flags |= EVENT_FD_WRITE;
- }
+ return;
}
+void dcesrv_tcp_accept(struct server_connection *conn)
+{
+ NTSTATUS status;
+ struct dcesrv_socket_context *dcesrv_sock = conn->server_socket->private_data;
+ struct dcesrv_connection *dcesrv_conn = NULL;
+ DEBUG(5,("dcesrv_tcp_accept\n"));
-
-/*
- called when a RPC socket becomes readable
-*/
-static void dcerpc_io_handler(struct event_context *ev, struct fd_event *fde,
- time_t t, uint16 flags)
-{
- if (flags & EVENT_FD_WRITE) {
- dcerpc_write_handler(ev, fde, t, flags);
+ status = dcesrv_endpoint_connect(dcesrv_sock->dcesrv_ctx, dcesrv_sock->endpoint, &dcesrv_conn);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("dcesrv_tcp_accept: dcesrv_endpoint_connect failed: %s\n",
+ nt_errstr(status)));
+ return;
}
- if (flags & EVENT_FD_READ) {
- dcerpc_read_handler(ev, fde, t, flags);
- }
-}
-
-/*
- initialise a server_context from a open socket and register a event handler
- for reading from that socket
-*/
-void init_rpc_session(struct event_context *ev, void *private, int fd)
-{
- struct fd_event fde;
- struct rpc_server_context *r = private;
+ dcesrv_conn->srv_conn = conn;
- r = memdup(r, sizeof(struct rpc_server_context));
+ conn->private_data = dcesrv_conn;
- r->events = ev;
- r->socket_fd = fd;
+ return;
+}
- set_socket_options(fd,"SO_KEEPALIVE");
- set_socket_options(fd, lp_socket_options());
+void dcesrv_tcp_recv(struct server_connection *conn, time_t t, uint16_t flags)
+{
+ NTSTATUS status;
+ struct dcesrv_connection *dce_conn = conn->private_data;
+ DATA_BLOB tmp_blob;
- dcesrv_endpoint_connect_ops(&r->dcesrv_context, r->endpoint, r->endpoint_ops, &r->dce);
+ DEBUG(10,("dcesrv_tcp_recv\n"));
- r->dce->dce = &r->dcesrv_context;
+ status = socket_recv(conn->socket, conn->socket, &tmp_blob, 0x4000, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_IS_ERR(status)) {
+ dcesrv_terminate_connection(dce_conn, "eof on socket");
+ return;
+ }
+ return;
+ }
- set_blocking(fd, False);
+ status = dcesrv_input(dce_conn, &tmp_blob);
+ talloc_free(tmp_blob.data);
+ if (!NT_STATUS_IS_OK(status)) {
+ dcesrv_terminate_connection(dce_conn, "eof on socket");
+ return;
+ }
- /* setup a event handler for this socket. We are initially
- only interested in reading from the socket */
- fde.fd = fd;
- fde.handler = dcerpc_io_handler;
- fde.private = r;
- fde.flags = EVENT_FD_READ;
+ if (dce_conn->call_list && dce_conn->call_list->replies) {
+ conn->event.fde->flags |= EVENT_FD_WRITE;
+ }
- event_add_fd(ev, &fde);
+ return;
}
-
-/*
- setup a single rpc listener
- */
-static void setup_listen_rpc(struct event_context *events,
- struct model_ops *model_ops,
- struct in_addr *ifip, uint32 *port,
- struct rpc_server_context *r,
- const struct dcesrv_endpoint_ops *endpoint_ops)
+void dcesrv_tcp_send(struct server_connection *conn, time_t t, uint16_t flags)
{
- struct fd_event fde;
- int i;
-
- if (*port == 0) {
- fde.fd = -1;
- for (i=DCERPC_TCP_LOW_PORT;i<= DCERPC_TCP_HIGH_PORT;i++) {
- fde.fd = open_socket_in(SOCK_STREAM, i, 0, ifip->s_addr, True);
- if (fde.fd != -1) break;
- }
- if (fde.fd != -1) {
- *port = i;
- }
- } else {
- fde.fd = open_socket_in(SOCK_STREAM, *port, 0, ifip->s_addr, True);
- }
+ struct dcesrv_connection *dce_conn = conn->private_data;
+ NTSTATUS status;
+
+ DEBUG(10,("dcesrv_tcp_send\n"));
- if (fde.fd == -1) {
- DEBUG(0,("Failed to open socket on %s:%u - %s\n",
- inet_ntoa(*ifip), *port, strerror(errno)));
+ status = dcesrv_output(dce_conn, conn->socket, dcerpc_write_fn);
+ if (!NT_STATUS_IS_OK(status)) {
+ dcesrv_terminate_connection(dce_conn, "eof on socket");
return;
}
- /* each listening socket has separate state, so must use a different context */
- r = memdup(r, sizeof(struct rpc_server_context));
- if (!r) {
- smb_panic("out of memory");
+ if (!dce_conn->call_list || !dce_conn->call_list->replies) {
+ conn->event.fde->flags &= ~EVENT_FD_WRITE;
}
- r->endpoint_ops = endpoint_ops;
+ return;
+}
- r->endpoint = malloc(sizeof(struct dcesrv_endpoint));
- if (!r->endpoint) {
- smb_panic("out of memory");
- }
- r->endpoint->type = ENDPOINT_TCP;
- r->endpoint->info.tcp_port = *port;
-
- /* ready to listen */
- set_socket_options(fde.fd, "SO_KEEPALIVE");
- set_socket_options(fde.fd, lp_socket_options());
-
- if (listen(fde.fd, SMBD_LISTEN_BACKLOG) == -1) {
- DEBUG(0,("Failed to listen on %s:%d - %s\n",
- inet_ntoa(*ifip), *port, strerror(errno)));
- close(fde.fd);
- return;
- }
+void dcesrv_tcp_idle(struct server_connection *conn, time_t t)
+{
+ DEBUG(10,("dcesrv_tcp_idle\n"));
+ conn->event.idle->next_event = t + 5;
- /* we are only interested in read events on the listen socket */
- fde.flags = EVENT_FD_READ;
- fde.private = r;
- fde.handler = model_ops->accept_rpc_connection;
-
- event_add_fd(events, &fde);
+ return;
}
-/*
- add a socket address to the list of events, one event per dcerpc endpoint
-*/
-static void add_socket_rpc(struct event_context *events,
- struct model_ops *model_ops,
- struct in_addr *ifip)
+void dcesrv_tcp_close(struct server_connection *conn, const char *reason)
{
- struct dce_endpoint *e;
- struct rpc_server_context *r;
+ struct dcesrv_connection *dce_conn = conn->private_data;
- r = malloc(sizeof(struct rpc_server_context));
- if (!r) {
- smb_panic("out of memory");
- }
+ DEBUG(5,("dcesrv_tcp_close: %s\n",reason));
- r->dcesrv_context.endpoint_list = NULL;
- dcesrv_init(&r->dcesrv_context);
- r->endpoint = NULL;
- r->model_ops = model_ops;
- r->dce = NULL;
- r->socket_fd = -1;
- r->events = NULL;
-
- for (e=r->dcesrv_context.endpoint_list;e;e=e->next) {
- if (e->endpoint.type == ENDPOINT_TCP) {
- setup_listen_rpc(events, model_ops, ifip,
- &e->endpoint.info.tcp_port,
- r, e->endpoint_ops);
- }
- }
+ talloc_free(dce_conn);
- free(r);
+ return;
}
-/****************************************************************************
- Open the listening sockets for RPC over TCP
-****************************************************************************/
-void open_sockets_rpc(struct event_context *events,
- struct model_ops *model_ops)
+void dcesrv_tcp_exit(struct server_service *service, const char *reason)
{
- if (lp_interfaces() && lp_bind_interfaces_only()) {
- int num_interfaces = iface_count();
- int i;
- for(i = 0; i < num_interfaces; i++) {
- struct in_addr *ifip = iface_n_ip(i);
- if (ifip == NULL) {
- continue;
- }
- add_socket_rpc(events, model_ops, ifip);
- }
- } else {
- TALLOC_CTX *mem_ctx = talloc_init("open_sockets_smbd");
- struct in_addr *ifip = interpret_addr2(mem_ctx, lp_socket_address());
- if (!mem_ctx) {
- smb_panic("No memory");
- }
- add_socket_rpc(events, model_ops, ifip);
- talloc_destroy(mem_ctx);
- }
+ DEBUG(1,("dcesrv_tcp_exit: %s\n",reason));
+ return;
}