r8593: register the rpc server with irpc
[sfrench/samba-autobuild/.git] / source4 / rpc_server / dcerpc_sock.c
index bffa5cffd8889f2bd5fadfd3ca0ab2d1894c48e1..cf3ce3cf7e3d704a6f44a96506b6c01840436a81 100644 (file)
@@ -4,7 +4,7 @@
    server side dcerpc using various kinds of sockets (tcp, unix domain)
 
    Copyright (C) Andrew Tridgell 2003
-   Copyright (C) Stefan (metze) Metzmacher 2004   
+   Copyright (C) Stefan (metze) Metzmacher 2004-2005  
    Copyright (C) Jelmer Vernooij 2004
 
    This program is free software; you can redistribute it and/or modify
 */
 
 #include "includes.h"
+#include "lib/socket/socket.h"
+#include "system/dir.h"
+#include "system/filesys.h"
+#include "lib/events/events.h"
+#include "rpc_server/dcerpc_server.h"
+#include "smbd/service_stream.h"
+#include "lib/messaging/irpc.h"
 
 struct dcesrv_socket_context {
        const struct dcesrv_endpoint *endpoint;
@@ -32,189 +39,268 @@ struct dcesrv_socket_context {
 /*
   write_fn callback for dcesrv_output()
 */
-static ssize_t dcerpc_write_fn(void *private, DATA_BLOB *out)
+static NTSTATUS dcerpc_write_fn(void *private_data, DATA_BLOB *out, size_t *nwritten)
 {
        NTSTATUS status;
-       struct socket_context *sock = private;
+       struct socket_context *sock = talloc_get_type(private_data, struct socket_context);
        size_t sendlen;
 
-       status = socket_send(sock, sock, out, &sendlen, 0);
+       status = socket_send(sock, out, &sendlen, 0);
+       NT_STATUS_IS_ERR_RETURN(status);
+
+       *nwritten = sendlen;
+       return status;
+}
+
+static void dcesrv_terminate_connection(struct dcesrv_connection *dce_conn, const char *reason)
+{
+       stream_terminate_connection(dce_conn->srv_conn, reason);
+}
+
+
+static void dcesrv_sock_accept(struct stream_connection *srv_conn)
+{
+       NTSTATUS status;
+       struct dcesrv_socket_context *dcesrv_sock = 
+               talloc_get_type(srv_conn->private, struct dcesrv_socket_context);
+       struct dcesrv_connection *dcesrv_conn = NULL;
+
+       status = dcesrv_endpoint_connect(dcesrv_sock->dcesrv_ctx,
+                                        srv_conn,
+                                        dcesrv_sock->endpoint,
+                                        srv_conn,
+                                        &dcesrv_conn);
        if (!NT_STATUS_IS_OK(status)) {
-               return -1;
+               DEBUG(0,("dcesrv_sock_accept: dcesrv_endpoint_connect failed: %s\n", 
+                       nt_errstr(status)));
+               return;
        }
 
-       return sendlen;
+       srv_conn->private = dcesrv_conn;
+
+       irpc_add_name(srv_conn->msg_ctx, "rpc_server");
+
+       return; 
 }
 
-void dcesrv_terminate_connection(struct dcesrv_connection *dce_conn, const char *reason)
+static void dcesrv_sock_recv(struct stream_connection *conn, uint16_t flags)
 {
-       server_terminate_connection(dce_conn->srv_conn, reason);
+       NTSTATUS status;
+       struct dcesrv_connection *dce_conn = talloc_get_type(conn->private, struct dcesrv_connection);
+       DATA_BLOB tmp_blob;
+       size_t nread;
+
+       if (dce_conn->processing) {
+               EVENT_FD_NOT_READABLE(conn->event.fde);
+               return;
+       }
+
+       tmp_blob = data_blob_talloc(conn->socket, NULL, 0x1000);
+       if (tmp_blob.data == NULL) {
+               dcesrv_terminate_connection(dce_conn, "out of memory");
+               return;
+       }
+
+       status = socket_recv(conn->socket, tmp_blob.data, tmp_blob.length, &nread, 0);
+       if (NT_STATUS_IS_ERR(status)) {
+               dcesrv_terminate_connection(dce_conn, nt_errstr(status));
+               return;
+       }
+       if (nread == 0) {
+               talloc_free(tmp_blob.data);
+               return;
+       }
+
+       tmp_blob.length = nread;
+
+       dce_conn->processing = True;
+       status = dcesrv_input(dce_conn, &tmp_blob);
+       dce_conn->processing = False;
+       talloc_free(tmp_blob.data);
+
+       EVENT_FD_READABLE(conn->event.fde);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               dcesrv_terminate_connection(dce_conn, nt_errstr(status));
+               return;
+       }
+
+       if (dce_conn->call_list && dce_conn->call_list->replies) {
+               EVENT_FD_WRITEABLE(conn->event.fde);
+       }
 }
 
-static void add_socket_rpc_unix(struct server_service *service,
-                               const struct model_ops *model_ops,
-                               struct dcesrv_context *dce_ctx,
-                               struct dcesrv_endpoint *e)
+static void dcesrv_sock_send(struct stream_connection *conn, uint16_t flags)
 {
-       struct server_socket *sock;
-       struct dcesrv_socket_context *dcesrv_sock;
-       uint16_t port = 1;
+       struct dcesrv_connection *dce_conn = talloc_get_type(conn->private, struct dcesrv_connection);
+       NTSTATUS status;
 
-       sock = service_setup_socket(service,model_ops, "unix", e->ep_description.endpoint, &port);
-       if (!sock) {
-               DEBUG(0,("service_setup_socket(path=%s) failed\n",e->ep_description.endpoint));
+       status = dcesrv_output(dce_conn, conn->socket, dcerpc_write_fn);
+       if (NT_STATUS_IS_ERR(status)) {
+               dcesrv_terminate_connection(dce_conn, "eof on socket");
                return;
        }
 
-       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"));
-               return;
+       if (!dce_conn->call_list || !dce_conn->call_list->replies) {
+               EVENT_FD_NOT_WRITEABLE(conn->event.fde);
        }
+}
+
+
+static const struct stream_server_ops dcesrv_stream_ops = {
+       .name                   = "rpc",
+       .accept_connection      = dcesrv_sock_accept,
+       .recv_handler           = dcesrv_sock_recv,
+       .send_handler           = dcesrv_sock_send,
+};
+
+
+
+static NTSTATUS add_socket_rpc_unix(struct dcesrv_context *dce_ctx, struct dcesrv_endpoint *e,
+                                   struct event_context *event_ctx, const struct model_ops *model_ops)
+{
+       struct dcesrv_socket_context *dcesrv_sock;
+       uint16_t port = 1;
+       NTSTATUS status;
+
+       dcesrv_sock = talloc(event_ctx, struct dcesrv_socket_context);
+       NT_STATUS_HAVE_NO_MEMORY(dcesrv_sock);
 
        /* remember the endpoint of this socket */
        dcesrv_sock->endpoint           = e;
-       dcesrv_sock->dcesrv_ctx         = dce_ctx;
+       dcesrv_sock->dcesrv_ctx         = talloc_reference(dcesrv_sock, dce_ctx);
+
+       status = stream_setup_socket(event_ctx, model_ops, &dcesrv_stream_ops, 
+                                    "unix", e->ep_description->endpoint, &port, 
+                                    dcesrv_sock);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0,("service_setup_stream_socket(path=%s) failed - %s\n",
+                        e->ep_description->endpoint, nt_errstr(status)));
+       }
 
-       sock->private_data = dcesrv_sock;
+       return status;
 }
 
-static void add_socket_rpc_ncalrpc(struct server_service *service,
-                                                       const struct model_ops *model_ops,
-                                                       struct dcesrv_context *dce_ctx, 
-                                                       struct dcesrv_endpoint *e)
+static NTSTATUS add_socket_rpc_ncalrpc(struct dcesrv_context *dce_ctx, struct dcesrv_endpoint *e,
+                                      struct event_context *event_ctx, const struct model_ops *model_ops)
 {
-       struct server_socket *sock;
        struct dcesrv_socket_context *dcesrv_sock;
        uint16_t port = 1;
        char *full_path;
+       NTSTATUS status;
 
-       if (!e->ep_description.endpoint) {
+       if (!e->ep_description->endpoint) {
                /* No identifier specified: use DEFAULT. 
                 * DO NOT hardcode this value anywhere else. Rather, specify 
                 * no endpoint and let the epmapper worry about it. */
-               e->ep_description.endpoint = talloc_strdup(dce_ctx, "DEFAULT");
+               e->ep_description->endpoint = talloc_strdup(dce_ctx, "DEFAULT");
        }
 
-       full_path = talloc_asprintf(dce_ctx, "%s/%s", lp_ncalrpc_dir(), e->ep_description.endpoint);
+       full_path = talloc_asprintf(dce_ctx, "%s/%s", lp_ncalrpc_dir(), e->ep_description->endpoint);
 
-       sock = service_setup_socket(service,model_ops, "unix", full_path, &port);
-       if (!sock) {
-               DEBUG(0,("service_setup_socket(identifier=%s,path=%s) failed\n",e->ep_description.endpoint, full_path));
-               return;
-       }
-
-       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"));
-               return;
-       }
+       dcesrv_sock = talloc(event_ctx, struct dcesrv_socket_context);
+       NT_STATUS_HAVE_NO_MEMORY(dcesrv_sock);
 
        /* remember the endpoint of this socket */
        dcesrv_sock->endpoint           = e;
-       dcesrv_sock->dcesrv_ctx         = dce_ctx;
-
-       sock->private_data = dcesrv_sock;
+       dcesrv_sock->dcesrv_ctx         = talloc_reference(dcesrv_sock, dce_ctx);
 
-       return;
+       status = stream_setup_socket(event_ctx, model_ops, &dcesrv_stream_ops, 
+                                    "unix", full_path, &port, dcesrv_sock);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0,("service_setup_stream_socket(identifier=%s,path=%s) failed - %s\n",
+                        e->ep_description->endpoint, full_path, nt_errstr(status)));
+       }
+       return status;
 }
 
 /*
   add a socket address to the list of events, one event per dcerpc endpoint
 */
-static void add_socket_rpc_tcp_iface(struct server_service *service, 
-                      const struct model_ops *model_ops,
-                      struct dcesrv_context *dce_ctx, 
-                          struct dcesrv_endpoint *e,
-                      struct in_addr *ifip)
+static NTSTATUS add_socket_rpc_tcp_iface(struct dcesrv_context *dce_ctx, struct dcesrv_endpoint *e,
+                                        struct event_context *event_ctx, const struct model_ops *model_ops,
+                                        const char *address)
 {
-       struct server_socket *sock;
        struct dcesrv_socket_context *dcesrv_sock;
        uint16_t port = 0;
-       const char *ip_str = talloc_strdup(service, inet_ntoa(*ifip));
+       NTSTATUS status;
                        
-       if (e->ep_description.endpoint) 
-               port = atoi(e->ep_description.endpoint);
-
-       sock = service_setup_socket(service,model_ops, "ipv4", ip_str, &port);
-       if (!sock) {
-               DEBUG(0,("service_setup_socket(port=%u) failed\n",port));
-               return;
+       if (e->ep_description->endpoint) {
+               port = atoi(e->ep_description->endpoint);
        }
 
-       if (e->ep_description.endpoint == NULL) {
-               e->ep_description.endpoint = talloc_asprintf(dce_ctx, "%d", port);
-       }
-
-       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"));
-               return;
-       }
+       dcesrv_sock = talloc(event_ctx, struct dcesrv_socket_context);
+       NT_STATUS_HAVE_NO_MEMORY(dcesrv_sock);
 
        /* remember the endpoint of this socket */
        dcesrv_sock->endpoint           = e;
-       dcesrv_sock->dcesrv_ctx         = dce_ctx;
+       dcesrv_sock->dcesrv_ctx         = talloc_reference(dcesrv_sock, dce_ctx);
 
-       sock->private_data = dcesrv_sock;
+       status = stream_setup_socket(event_ctx, model_ops, &dcesrv_stream_ops, 
+                                    "ipv4", address, &port, dcesrv_sock);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0,("service_setup_stream_socket(address=%s,port=%u) failed - %s\n", 
+                        address, port, nt_errstr(status)));
+       }
 
-       talloc_free(ip_str);
+       if (e->ep_description->endpoint == NULL) {
+               e->ep_description->endpoint = talloc_asprintf(dce_ctx, "%d", port);
+       }
 
-       return;
+       return status;
 }
 
-static void add_socket_rpc_tcp(struct server_service *service,
-                                                       const struct model_ops *model_ops,
-                                                       struct dcesrv_context *dce_ctx, 
-                                                       struct dcesrv_endpoint *e)
+static NTSTATUS add_socket_rpc_tcp(struct dcesrv_context *dce_ctx, struct dcesrv_endpoint *e,
+                                  struct event_context *event_ctx, const struct model_ops *model_ops)
 {
+       NTSTATUS status;
+
        /* Add TCP/IP sockets */
        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_tcp_iface(service, model_ops, dce_ctx, e, ifip);
+                       const char *address = iface_n_ip(i);
+                       status = add_socket_rpc_tcp_iface(dce_ctx, e, event_ctx, model_ops, address);
+                       NT_STATUS_NOT_OK_RETURN(status);
                }
        } else {
-               struct in_addr *ifip;
-               ifip = interpret_addr2(dce_ctx, lp_socket_address());
-               add_socket_rpc_tcp_iface(service, model_ops, dce_ctx, e, ifip);
-               talloc_free(ifip);
+               status = add_socket_rpc_tcp_iface(dce_ctx, e, event_ctx, model_ops, lp_socket_address());
+               NT_STATUS_NOT_OK_RETURN(status);
        }
 
-       return;
+       return NT_STATUS_OK;
 }
 
 /****************************************************************************
  Open the listening sockets for RPC over NCACN_IP_TCP/NCALRPC/NCACN_UNIX_STREAM
 ****************************************************************************/
-void dcesrv_sock_init(struct server_service *service, const struct model_ops *model_ops, struct dcesrv_context *dce_ctx)
+NTSTATUS dcesrv_sock_init(struct dcesrv_context *dce_ctx, 
+                         struct event_context *event_ctx, const struct model_ops *model_ops)
 {
        struct dcesrv_endpoint *e;
-
-       DEBUG(1,("dcesrv_sock_init\n"));
+       NTSTATUS status;
 
        /* Make sure the directory for NCALRPC exists */
-       if (!directory_exist(lp_ncalrpc_dir(), NULL)) {
-               mkdir(lp_ncalrpc_dir(), S_IWUSR | S_IRUSR | S_IXUSR);
+       if (!directory_exist(lp_ncalrpc_dir())) {
+               mkdir(lp_ncalrpc_dir(), 0755);
        }
 
        for (e=dce_ctx->endpoint_list;e;e=e->next) {
-               switch (e->ep_description.transport) {
+               switch (e->ep_description->transport) {
                case NCACN_UNIX_STREAM:
-                       add_socket_rpc_unix(service, model_ops, dce_ctx, e);
+                       status = add_socket_rpc_unix(dce_ctx, e, event_ctx, model_ops);
+                       NT_STATUS_NOT_OK_RETURN(status);
                        break;
                
                case NCALRPC:
-                       add_socket_rpc_ncalrpc(service, model_ops, dce_ctx, e);
+                       status = add_socket_rpc_ncalrpc(dce_ctx, e, event_ctx, model_ops);
+                       NT_STATUS_NOT_OK_RETURN(status);
                        break;
 
                case NCACN_IP_TCP:
-                       add_socket_rpc_tcp(service, model_ops, dce_ctx, e);
+                       status = add_socket_rpc_tcp(dce_ctx, e, event_ctx, model_ops);
+                       NT_STATUS_NOT_OK_RETURN(status);
                        break;
 
                default:
@@ -222,103 +308,6 @@ void dcesrv_sock_init(struct server_service *service, const struct model_ops *mo
                }
        }
 
-       return; 
+       return NT_STATUS_OK;    
 }
 
-void dcesrv_sock_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_sock_accept\n"));
-
-       status = dcesrv_endpoint_connect(dcesrv_sock->dcesrv_ctx, dcesrv_sock->endpoint, &dcesrv_conn);
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(0,("dcesrv_sock_accept: dcesrv_endpoint_connect failed: %s\n", 
-                       nt_errstr(status)));
-               return;
-       }
-
-       dcesrv_conn->srv_conn = conn;
-
-       conn->private_data = dcesrv_conn;
-
-       return; 
-}
-
-void dcesrv_sock_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;
-
-       DEBUG(10,("dcesrv_sock_recv\n"));
-
-       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;
-       }
-
-       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;
-       }
-
-       if (dce_conn->call_list && dce_conn->call_list->replies) {
-               conn->event.fde->flags |= EVENT_FD_WRITE;
-       }
-
-       return; 
-}
-
-void dcesrv_sock_send(struct server_connection *conn, time_t t, uint16_t flags)
-{
-       struct dcesrv_connection *dce_conn = conn->private_data;
-       NTSTATUS status;
-
-       DEBUG(10,("dcesrv_sock_send\n"));
-
-       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;
-       }
-
-       if (!dce_conn->call_list || !dce_conn->call_list->replies) {
-               conn->event.fde->flags &= ~EVENT_FD_WRITE;
-       }
-
-       return;
-}
-
-void dcesrv_sock_idle(struct server_connection *conn, time_t t)
-{
-       DEBUG(10,("dcesrv_sock_idle\n"));
-       conn->event.idle->next_event = t + 5;
-
-       return; 
-}
-
-void dcesrv_sock_close(struct server_connection *conn, const char *reason)
-{
-       struct dcesrv_connection *dce_conn = conn->private_data;
-
-       DEBUG(5,("dcesrv_sock_close: %s\n",reason));
-
-       talloc_free(dce_conn);
-
-       return;
-}
-
-void dcesrv_sock_exit(struct server_service *service, const char *reason)
-{
-       DEBUG(1,("dcesrv_sock_exit: %s\n",reason));
-       return;
-}