libndr: EXT_NSL doesn't exist anymore, but it wasn't needed anyway
[jelmer/samba4-debian.git] / source / smbd / service_stream.c
index d238fc4128e03f1955317c008240b54addac22ac..9f744efa812e4b7e6fab84e308fff11eccbce25d 100644 (file)
@@ -8,7 +8,7 @@
    
    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
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
@@ -17,8 +17,7 @@
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
@@ -28,6 +27,8 @@
 #include "smbd/service.h"
 #include "smbd/service_stream.h"
 #include "lib/messaging/irpc.h"
+#include "cluster/cluster.h"
+#include "param/param.h"
 
 /* the range of ports to try for dcerpc over tcp endpoints */
 #define SERVER_TCP_LOW_PORT  1024
@@ -42,6 +43,7 @@
 */
 struct stream_socket {
        const struct stream_server_ops *ops;
+       struct loadparm_context *lp_ctx;
        struct event_context *event_ctx;
        const struct model_ops *model_ops;
        struct socket_context *sock;
@@ -57,14 +59,14 @@ void stream_terminate_connection(struct stream_connection *srv_conn, const char
        struct event_context *event_ctx = srv_conn->event.ctx;
        const struct model_ops *model_ops = srv_conn->model_ops;
 
-       if (!reason) reason = "unknwon reason";
+       if (!reason) reason = "unknown reason";
 
        srv_conn->terminate = reason;
 
        if (srv_conn->processing) {
                /* 
                 * if we're currently inside the stream_io_handler(),
-                * deferr the termination to the end of stream_io_hendler()
+                * defer the termination to the end of stream_io_hendler()
                 *
                 * and we don't want to read or write to the connection...
                 */
@@ -78,31 +80,37 @@ void stream_terminate_connection(struct stream_connection *srv_conn, const char
        model_ops->terminate(event_ctx, reason);
 }
 
-/*
+/**
   the select loop has indicated that a stream is ready for IO
 */
-static void stream_io_handler(struct event_context *ev, struct fd_event *fde, 
-                             uint16_t flags, void *private)
+static void stream_io_handler(struct stream_connection *conn, uint16_t flags)
 {
-       struct stream_connection *conn = talloc_get_type(private, 
-                                                        struct stream_connection);
-
-       conn->processing = True;
+       conn->processing = true;
        if (flags & EVENT_FD_WRITE) {
                conn->ops->send_handler(conn, flags);
        } else if (flags & EVENT_FD_READ) {
                conn->ops->recv_handler(conn, flags);
        }
-       conn->processing = False;
+       conn->processing = false;
 
        if (conn->terminate) {
                stream_terminate_connection(conn, conn->terminate);
        }
 }
 
-void stream_io_handler_callback(void *conn, uint16_t flags) 
+static void stream_io_handler_fde(struct event_context *ev, struct fd_event *fde, 
+                                 uint16_t flags, void *private)
 {
-       stream_io_handler(NULL, NULL, flags, conn);
+       struct stream_connection *conn = talloc_get_type(private, 
+                                                        struct stream_connection);
+       stream_io_handler(conn, flags);
+}
+
+void stream_io_handler_callback(void *private, uint16_t flags) 
+{
+       struct stream_connection *conn = talloc_get_type(private, 
+                                                        struct stream_connection);
+       stream_io_handler(conn, flags);
 }
 
 /*
@@ -128,13 +136,13 @@ NTSTATUS stream_new_connection_merge(struct event_context *ev,
        srv_conn->private       = private_data;
        srv_conn->model_ops     = model_ops;
        srv_conn->socket        = sock;
-       srv_conn->server_id     = 0;
+       srv_conn->server_id     = cluster_id(0, 0);
        srv_conn->ops           = stream_ops;
        srv_conn->msg_ctx       = msg_ctx;
        srv_conn->event.ctx     = ev;
        srv_conn->event.fde     = event_add_fd(ev, srv_conn, socket_get_fd(sock),
                                               EVENT_FD_READ, 
-                                              stream_io_handler, srv_conn);
+                                              stream_io_handler_fde, srv_conn);
        *_srv_conn = srv_conn;
        return NT_STATUS_OK;
 }
@@ -144,8 +152,9 @@ NTSTATUS stream_new_connection_merge(struct event_context *ev,
   context of the new process (if appropriate)
 */
 static void stream_new_connection(struct event_context *ev,
+                                 struct loadparm_context *lp_ctx,
                                  struct socket_context *sock, 
-                                 uint32_t server_id, void *private)
+                                 struct server_id server_id, void *private)
 {
        struct stream_socket *stream_socket = talloc_get_type(private, struct stream_socket);
        struct stream_connection *srv_conn;
@@ -165,17 +174,21 @@ static void stream_new_connection(struct event_context *ev,
        srv_conn->server_id     = server_id;
        srv_conn->ops           = stream_socket->ops;
        srv_conn->event.ctx     = ev;
+       srv_conn->lp_ctx        = lp_ctx;
        srv_conn->event.fde     = event_add_fd(ev, srv_conn, socket_get_fd(sock),
-                                              EVENT_FD_READ, 
-                                              stream_io_handler, srv_conn);
+                                              0, stream_io_handler_fde, srv_conn);
 
-       if (!socket_check_access(sock, "smbd", lp_hostsallow(-1), lp_hostsdeny(-1))) {
+       if (!socket_check_access(sock, "smbd", lp_hostsallow(NULL, lp_default_service(lp_ctx)), lp_hostsdeny(NULL, lp_default_service(lp_ctx)))) {
                stream_terminate_connection(srv_conn, "denied by access rules");
                return;
        }
 
        /* setup to receive internal messages on this connection */
-       srv_conn->msg_ctx = messaging_init(srv_conn, srv_conn->server_id, ev);
+       srv_conn->msg_ctx = messaging_init(srv_conn, 
+                                          lp_messaging_path(srv_conn, lp_ctx),
+                                          srv_conn->server_id, 
+                                          lp_iconv_convenience(lp_ctx),
+                                          ev);
        if (!srv_conn->msg_ctx) {
                stream_terminate_connection(srv_conn, "messaging_init() failed");
                return;
@@ -185,10 +198,10 @@ static void stream_new_connection(struct event_context *ev,
        s = socket_get_my_addr(sock, ev);
        if (s && c) {
                const char *title;
-               title = talloc_asprintf(s, "conn[%s] c[%s:%u] s[%s:%u] server_id[%d]",
+               title = talloc_asprintf(s, "conn[%s] c[%s:%u] s[%s:%u] server_id[%s]",
                                        stream_socket->ops->name, 
                                        c->addr, c->port, s->addr, s->port,
-                                       server_id);
+                                       cluster_id_string(s, server_id));
                if (title) {
                        stream_connection_set_title(srv_conn, title);
                }
@@ -196,6 +209,9 @@ static void stream_new_connection(struct event_context *ev,
        talloc_free(c);
        talloc_free(s);
 
+       /* we're now ready to start receiving events on this stream */
+       EVENT_FD_READABLE(srv_conn->event.fde);
+
        /* call the server specific accept code */
        stream_socket->ops->accept_connection(srv_conn);
 }
@@ -212,22 +228,27 @@ static void stream_accept_handler(struct event_context *ev, struct fd_event *fde
        /* ask the process model to create us a process for this new
           connection.  When done, it calls stream_new_connection()
           with the newly created socket */
-       stream_socket->model_ops->accept_connection(ev, stream_socket->sock, 
+       stream_socket->model_ops->accept_connection(ev, stream_socket->lp_ctx, 
+                                                   stream_socket->sock, 
                                                    stream_new_connection, stream_socket);
 }
 
-
-
 /*
   setup a listen stream socket
   if you pass *port == 0, then a port > 1024 is used
+
+  FIXME: This function is TCP/IP specific - uses an int rather than 
+        a string for the port. Should leave allocating a port nr 
+         to the socket implementation - JRV20070903
  */
 NTSTATUS stream_setup_socket(struct event_context *event_context,
+                            struct loadparm_context *lp_ctx,
                             const struct model_ops *model_ops,
                             const struct stream_server_ops *stream_ops,
                             const char *family,
                             const char *sock_addr,
                             uint16_t *port,
+                            const char *socket_options,
                             void *private)
 {
        NTSTATUS status;
@@ -243,16 +264,31 @@ NTSTATUS stream_setup_socket(struct event_context *event_context,
 
        talloc_steal(stream_socket, stream_socket->sock);
 
+       stream_socket->lp_ctx = talloc_reference(stream_socket, lp_ctx);
+
        /* ready to listen */
        status = socket_set_option(stream_socket->sock, "SO_KEEPALIVE", NULL);
        NT_STATUS_NOT_OK_RETURN(status);
 
-       status = socket_set_option(stream_socket->sock, lp_socket_options(), NULL);
-       NT_STATUS_NOT_OK_RETURN(status);
+       if (socket_options != NULL) {
+               status = socket_set_option(stream_socket->sock, socket_options, NULL);
+               NT_STATUS_NOT_OK_RETURN(status);
+       }
+
+       /* TODO: set socket ACL's (host allow etc) here when they're
+        * implemented */
 
-       /* TODO: set socket ACL's here when they're implemented */
+       /* Some sockets don't have a port, or are just described from
+        * the string.  We are indicating this by having port == NULL */
+       if (!port) {
+               socket_address = socket_address_from_strings(stream_socket, 
+                                                            stream_socket->sock->backend_name,
+                                                            sock_addr, 0);
+               NT_STATUS_HAVE_NO_MEMORY(socket_address);
+               status = socket_listen(stream_socket->sock, socket_address, SERVER_LISTEN_BACKLOG, 0);
+               talloc_free(socket_address);
 
-       if (*port == 0) {
+       } else if (*port == 0) {
                for (i=SERVER_TCP_LOW_PORT;i<= SERVER_TCP_HIGH_PORT;i++) {
                        socket_address = socket_address_from_strings(stream_socket, 
                                                                     stream_socket->sock->backend_name,
@@ -282,9 +318,20 @@ NTSTATUS stream_setup_socket(struct event_context *event_context,
                return status;
        }
 
+       /* By specifying EVENT_FD_AUTOCLOSE below, we indicate that we
+        * will close the socket using the events system.  This avoids
+        * nasty interactions with waiting for talloc to close the socket. */
+
+       socket_set_flags(stream_socket->sock, SOCKET_FLAG_NOCLOSE);
+
+       /* Add the FD from the newly created socket into the event
+        * subsystem.  it will call the accept handler whenever we get
+        * new connections */
+
        event_add_fd(event_context, stream_socket->sock, 
                     socket_get_fd(stream_socket->sock), 
-                    EVENT_FD_READ, stream_accept_handler, stream_socket);
+                    EVENT_FD_READ|EVENT_FD_AUTOCLOSE, 
+                    stream_accept_handler, stream_socket);
 
        stream_socket->private          = talloc_reference(stream_socket, private);
        stream_socket->ops              = stream_ops;