r25446: Merge some changes I made on the way home from SFO:
[jelmer/samba4-debian.git] / source / smbd / service_stream.c
index 1ed8b4d8af6e381e8747a595e75c83d0047eb2ad..31107df032563e4ef6f4e8aa113fb0fcf1473992 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,
    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"
 #include "process_model.h"
 #include "lib/events/events.h"
 #include "lib/socket/socket.h"
+#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
@@ -55,7 +57,24 @@ 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 = "unknown reason";
+
+       srv_conn->terminate = reason;
+
+       if (srv_conn->processing) {
+               /* 
+                * if we're currently inside the stream_io_handler(),
+                * defer the termination to the end of stream_io_hendler()
+                *
+                * and we don't want to read or write to the connection...
+                */
+               event_set_fd_flags(srv_conn->event.fde, 0);
+               return;
+       }
+
        talloc_free(srv_conn->event.fde);
+       srv_conn->event.fde = NULL;
        talloc_free(srv_conn);
        model_ops->terminate(event_ctx, reason);
 }
@@ -63,21 +82,69 @@ void stream_terminate_connection(struct stream_connection *srv_conn, const char
 /*
   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;
        if (flags & EVENT_FD_WRITE) {
                conn->ops->send_handler(conn, flags);
-               return;
+       } else if (flags & EVENT_FD_READ) {
+               conn->ops->recv_handler(conn, flags);
        }
+       conn->processing = False;
 
-       if (flags & EVENT_FD_READ) {
-               conn->ops->recv_handler(conn, flags);
+       if (conn->terminate) {
+               stream_terminate_connection(conn, conn->terminate);
        }
 }
 
+static void stream_io_handler_fde(struct event_context *ev, struct fd_event *fde, 
+                                 uint16_t flags, void *private)
+{
+       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);
+}
+
+/*
+  this creates a stream_connection from an already existing connection,
+  used for protocols, where a client connection needs to switched into
+  a server connection
+*/
+NTSTATUS stream_new_connection_merge(struct event_context *ev,
+                                    const struct model_ops *model_ops,
+                                    struct socket_context *sock,
+                                    const struct stream_server_ops *stream_ops,
+                                    struct messaging_context *msg_ctx,
+                                    void *private_data,
+                                    struct stream_connection **_srv_conn)
+{
+       struct stream_connection *srv_conn;
+
+       srv_conn = talloc_zero(ev, struct stream_connection);
+       NT_STATUS_HAVE_NO_MEMORY(srv_conn);
+
+       talloc_steal(srv_conn, sock);
+
+       srv_conn->private       = private_data;
+       srv_conn->model_ops     = model_ops;
+       srv_conn->socket        = sock;
+       srv_conn->server_id     = cluster_id(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_fde, srv_conn);
+       *_srv_conn = srv_conn;
+       return NT_STATUS_OK;
+}
 
 /*
   called when a new socket connection has been established. This is called in the process
@@ -85,10 +152,11 @@ static void stream_io_handler(struct event_context *ev, struct fd_event *fde,
 */
 static void stream_new_connection(struct event_context *ev,
                                  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;
+       struct socket_address *c, *s;
 
        srv_conn = talloc_zero(ev, struct stream_connection);
        if (!srv_conn) {
@@ -105,21 +173,40 @@ static void stream_new_connection(struct event_context *ev,
        srv_conn->ops           = stream_socket->ops;
        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);
+                                              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_hostsdeny(NULL))) {
                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, global_loadparm),
+                                          srv_conn->server_id, ev);
        if (!srv_conn->msg_ctx) {
                stream_terminate_connection(srv_conn, "messaging_init() failed");
                return;
        }
 
+       c = socket_get_peer_addr(sock, 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[%s]",
+                                       stream_socket->ops->name, 
+                                       c->addr, c->port, s->addr, s->port,
+                                       cluster_id_string(s, server_id));
+               if (title) {
+                       stream_connection_set_title(srv_conn, title);
+               }
+       }
+       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);
 }
@@ -140,11 +227,13 @@ static void stream_accept_handler(struct event_context *ev, struct fd_event *fde
                                                    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,
                             const struct model_ops *model_ops,
@@ -156,6 +245,7 @@ NTSTATUS stream_setup_socket(struct event_context *event_context,
 {
        NTSTATUS status;
        struct stream_socket *stream_socket;
+       struct socket_address *socket_address;
        int i;
 
        stream_socket = talloc_zero(event_context, struct stream_socket);
@@ -170,22 +260,33 @@ NTSTATUS stream_setup_socket(struct event_context *event_context,
        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);
+       status = socket_set_option(stream_socket->sock, lp_socket_options(global_loadparm), 
+                                  NULL);
        NT_STATUS_NOT_OK_RETURN(status);
 
        /* TODO: set socket ACL's here when they're implemented */
 
        if (*port == 0) {
                for (i=SERVER_TCP_LOW_PORT;i<= SERVER_TCP_HIGH_PORT;i++) {
-                       status = socket_listen(stream_socket->sock, sock_addr, i, 
+                       socket_address = socket_address_from_strings(stream_socket, 
+                                                                    stream_socket->sock->backend_name,
+                                                                    sock_addr, i);
+                       NT_STATUS_HAVE_NO_MEMORY(socket_address);
+                       status = socket_listen(stream_socket->sock, socket_address, 
                                               SERVER_LISTEN_BACKLOG, 0);
+                       talloc_free(socket_address);
                        if (NT_STATUS_IS_OK(status)) {
                                *port = i;
                                break;
                        }
                }
        } else {
-               status = socket_listen(stream_socket->sock, sock_addr, *port, SERVER_LISTEN_BACKLOG, 0);
+               socket_address = socket_address_from_strings(stream_socket, 
+                                                            stream_socket->sock->backend_name,
+                                                            sock_addr, *port);
+               NT_STATUS_HAVE_NO_MEMORY(socket_address);
+               status = socket_listen(stream_socket->sock, socket_address, SERVER_LISTEN_BACKLOG, 0);
+               talloc_free(socket_address);
        }
 
        if (!NT_STATUS_IS_OK(status)) {
@@ -195,9 +296,13 @@ NTSTATUS stream_setup_socket(struct event_context *event_context,
                return status;
        }
 
+       /* we will close the socket using the events system */
+       socket_set_flags(stream_socket->sock, SOCKET_FLAG_NOCLOSE);
+
        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;
@@ -206,3 +311,11 @@ NTSTATUS stream_setup_socket(struct event_context *event_context,
 
        return NT_STATUS_OK;
 }
+
+/*
+  setup a connection title 
+*/
+void stream_connection_set_title(struct stream_connection *conn, const char *title)
+{
+       conn->model_ops->set_title(conn->event.ctx, title);
+}