r3162: Add client-side support for the ncalrpc: and ncacn_unix_stream: transports.
[gd/samba-autobuild/.git] / source4 / rpc_server / dcerpc_tcp.c
index c9aeb400d012b7f4355011dbbe2b2567d1984903..adfddae14fca6e897ca88862cd6bd2cdc4f86481 100644 (file)
@@ -4,7 +4,8 @@
    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;
 }