s4:rpc_server: make sure we don't terminate a connection with pending requests (bug...
authorStefan Metzmacher <metze@samba.org>
Tue, 9 Jul 2013 14:38:59 +0000 (16:38 +0200)
committerStefan Metzmacher <metze@samba.org>
Wed, 10 Jul 2013 06:47:38 +0000 (08:47 +0200)
Sadly we may have nested event loops, which won't work correctly with
broken connections, that's why we have to do this...

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
Autobuild-User(master): Stefan Metzmacher <metze@samba.org>
Autobuild-Date(master): Wed Jul 10 08:47:38 CEST 2013 on sn-devel-104

source4/rpc_server/dcerpc_server.c
source4/rpc_server/dcerpc_server.h

index 389cbe36660b5afd76e5fcdb21d0535c30acaaa4..10e711b37d37b8b26cebe9f2d96376d40f8f3d38 100644 (file)
@@ -386,6 +386,8 @@ _PUBLIC_ NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce_ctx,
                return NT_STATUS_NO_MEMORY;
        }
 
+       p->prev = NULL;
+       p->next = NULL;
        p->dce_ctx = dce_ctx;
        p->endpoint = ep;
        p->contexts = NULL;
@@ -402,7 +404,7 @@ _PUBLIC_ NTSTATUS dcesrv_endpoint_connect(struct dcesrv_context *dce_ctx,
        p->event_ctx = event_ctx;
        p->msg_ctx = msg_ctx;
        p->server_id = server_id;
-       p->processing = false;
+       p->terminate = NULL;
        p->state_flags = state_flags;
        ZERO_STRUCT(p->transport);
 
@@ -1143,6 +1145,7 @@ _PUBLIC_ NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx,
        dce_ctx->lp_ctx = lp_ctx;
        dce_ctx->assoc_groups_idr = idr_init(dce_ctx);
        NT_STATUS_HAVE_NO_MEMORY(dce_ctx->assoc_groups_idr);
+       dce_ctx->broken_connections = NULL;
 
        for (i=0;endpoint_servers[i];i++) {
                const struct dcesrv_endpoint_server *ep_server;
@@ -1269,12 +1272,45 @@ const struct dcesrv_critical_sizes *dcerpc_module_version(void)
 
 static void dcesrv_terminate_connection(struct dcesrv_connection *dce_conn, const char *reason)
 {
+       struct dcesrv_context *dce_ctx = dce_conn->dce_ctx;
        struct stream_connection *srv_conn;
        srv_conn = talloc_get_type(dce_conn->transport.private_data,
                                   struct stream_connection);
 
-       stream_terminate_connection(srv_conn, reason);
+       if (dce_conn->pending_call_list == NULL) {
+               char *full_reason = talloc_asprintf(dce_conn, "dcesrv: %s", reason);
+
+               DLIST_REMOVE(dce_ctx->broken_connections, dce_conn);
+               stream_terminate_connection(srv_conn, full_reason ? full_reason : reason);
+               return;
+       }
+
+       if (dce_conn->terminate != NULL) {
+               return;
+       }
+
+       DEBUG(3,("dcesrv: terminating connection due to '%s' defered due to pending calls\n",
+                reason));
+       dce_conn->terminate = talloc_strdup(dce_conn, reason);
+       if (dce_conn->terminate == NULL) {
+               dce_conn->terminate = "dcesrv: defered terminating connection - no memory";
+       }
+       DLIST_ADD_END(dce_ctx->broken_connections, dce_conn, NULL);
 }
+
+static void dcesrv_cleanup_broken_connections(struct dcesrv_context *dce_ctx)
+{
+       struct dcesrv_connection *cur, *next;
+
+       next = dce_ctx->broken_connections;
+       while (next != NULL) {
+               cur = next;
+               next = cur->next;
+
+               dcesrv_terminate_connection(cur, cur->terminate);
+       }
+}
+
 /* We need this include to be able to compile on some plateforms
  * (ie. freebsd 7.2) as it seems that <sys/uio.h> is not included
  * correctly.
@@ -1386,6 +1422,8 @@ static void dcesrv_sock_accept(struct stream_connection *srv_conn)
        struct tevent_req *subreq;
        struct loadparm_context *lp_ctx = dcesrv_sock->dcesrv_ctx->lp_ctx;
 
+       dcesrv_cleanup_broken_connections(dcesrv_sock->dcesrv_ctx);
+
        if (!srv_conn->session_info) {
                status = auth_anonymous_session_info(srv_conn,
                                                     lp_ctx,
@@ -1473,10 +1511,23 @@ static void dcesrv_read_fragment_done(struct tevent_req *subreq)
 {
        struct dcesrv_connection *dce_conn = tevent_req_callback_data(subreq,
                                             struct dcesrv_connection);
+       struct dcesrv_context *dce_ctx = dce_conn->dce_ctx;
        struct ncacn_packet *pkt;
        DATA_BLOB buffer;
        NTSTATUS status;
 
+       if (dce_conn->terminate) {
+               /*
+                * if the current connection is broken
+                * we need to clean it up before any other connection
+                */
+               dcesrv_terminate_connection(dce_conn, dce_conn->terminate);
+               dcesrv_cleanup_broken_connections(dce_ctx);
+               return;
+       }
+
+       dcesrv_cleanup_broken_connections(dce_ctx);
+
        status = dcerpc_read_ncacn_packet_recv(subreq, dce_conn,
                                               &pkt, &buffer);
        TALLOC_FREE(subreq);
index 4fcb5c50a13e11892008efb671e6bf8d077701fc..66fe51e17fcf2f1a1f237e632e87c693e2f7e33b 100644 (file)
@@ -170,6 +170,9 @@ struct dcesrv_connection_context {
 
 /* the state associated with a dcerpc server connection */
 struct dcesrv_connection {
+       /* for the broken_connections DLIST */
+       struct dcesrv_connection *prev, *next;
+
        /* the top level context for this server */
        struct dcesrv_context *dce_ctx;
 
@@ -208,7 +211,8 @@ struct dcesrv_connection {
        /* the transport level session key */
        DATA_BLOB transport_session_key;
 
-       bool processing;
+       /* is this connection pending termination?  If so, why? */
+       const char *terminate;
 
        const char *packet_log_dir;
 
@@ -288,6 +292,8 @@ struct dcesrv_context {
        struct loadparm_context *lp_ctx;
 
        struct idr_context *assoc_groups_idr;
+
+       struct dcesrv_connection *broken_connections;
 };
 
 /* this structure is used by modules to determine the size of some critical types */