s4-winbindd: Do not terminate a connection that is still pending (bug #9820)
authorAndrew Bartlett <abartlet@samba.org>
Thu, 27 Jun 2013 01:28:03 +0000 (11:28 +1000)
committerStefan Metzmacher <metze@samba.org>
Wed, 10 Jul 2013 04:57:06 +0000 (06:57 +0200)
Instead, wait until the call attempts to reply, and let it terminate then

(often this happens in the attempt to then write to the broken pipe).

Andrew Bartlett

Pair-Programmed-With: Stefan Metzmacher <metze@samba.org>

Signed-off-by: Andrew Bartlett <abartlet@samba.org>
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Stefan Metzmacher <metze@samba.org>
source4/winbind/wb_samba3_protocol.c
source4/winbind/wb_server.c
source4/winbind/wb_server.h

index 2846e9ce90d8542f3c641bdaf69285b520f8a187..1b78c99c1f942b1cb405490a4b639f0805d00e45 100644 (file)
@@ -297,6 +297,8 @@ NTSTATUS wbsrv_samba3_send_reply(struct wbsrv_samba3_call *call)
        struct tevent_req *subreq;
        NTSTATUS status;
 
+       call->wbconn->pending_calls--;
+
        status = wbsrv_samba3_push_reply(call);
        NT_STATUS_NOT_OK_RETURN(status);
 
@@ -355,9 +357,12 @@ NTSTATUS wbsrv_samba3_process(struct wbsrv_samba3_call *call)
                return status;
        }
 
+       call->wbconn->pending_calls++;
+
        status = wbsrv_samba3_handle_call(call);
 
        if (!NT_STATUS_IS_OK(status)) {
+               call->wbconn->pending_calls--;
                talloc_free(call);
                return status;
        }
index 983f9f56a0d1ed662a23b7df58a7ad4da2219060..29ed5a67161e70351cae555690629c3b74c09d42 100644 (file)
 #include "libcli/util/tstream.h"
 #include "param/param.h"
 #include "param/secrets.h"
+#include "lib/util/dlinklist.h"
 
 void wbsrv_terminate_connection(struct wbsrv_connection *wbconn, const char *reason)
 {
-       stream_terminate_connection(wbconn->conn, reason);
+       struct wbsrv_service *service = wbconn->listen_socket->service;
+
+       if (wbconn->pending_calls == 0) {
+               char *full_reason = talloc_asprintf(wbconn, "wbsrv: %s", reason);
+
+               DLIST_REMOVE(service->broken_connections, wbconn);
+               stream_terminate_connection(wbconn->conn, full_reason ? full_reason : reason);
+               return;
+       }
+
+       if (wbconn->terminate != NULL) {
+               return;
+       }
+
+       DEBUG(3,("wbsrv: terminating connection due to '%s' defered due to %d pending calls\n",
+                reason, wbconn->pending_calls));
+       wbconn->terminate = talloc_strdup(wbconn, reason);
+       if (wbconn->terminate == NULL) {
+               wbconn->terminate = "wbsrv: defered terminating connection - no memory";
+       }
+       DLIST_ADD_END(service->broken_connections, wbconn, NULL);
+}
+
+static void wbsrv_cleanup_broken_connections(struct wbsrv_service *s)
+{
+       struct wbsrv_connection *cur, *next;
+
+       next = s->broken_connections;
+       while (next != NULL) {
+               cur = next;
+               next = cur->next;
+
+               wbsrv_terminate_connection(cur, cur->terminate);
+       }
 }
 
 static void wbsrv_call_loop(struct tevent_req *subreq)
 {
        struct wbsrv_connection *wbsrv_conn = tevent_req_callback_data(subreq,
                                      struct wbsrv_connection);
+       struct wbsrv_service *service = wbsrv_conn->listen_socket->service;
        struct wbsrv_samba3_call *call;
        NTSTATUS status;
 
+       if (wbsrv_conn->terminate) {
+               /*
+                * if the current connection is broken
+                * we need to clean it up before any other connection
+                */
+               wbsrv_terminate_connection(wbsrv_conn, wbsrv_conn->terminate);
+               wbsrv_cleanup_broken_connections(service);
+               return;
+       }
+
+       wbsrv_cleanup_broken_connections(service);
+
        call = talloc_zero(wbsrv_conn, struct wbsrv_samba3_call);
        if (call == NULL) {
                wbsrv_terminate_connection(wbsrv_conn, "wbsrv_call_loop: "
@@ -112,6 +159,8 @@ static void wbsrv_accept(struct stream_connection *conn)
        struct tevent_req *subreq;
        int rc;
 
+       wbsrv_cleanup_broken_connections(wbsrv_socket->service);
+
        wbsrv_conn = talloc_zero(conn, struct wbsrv_connection);
        if (wbsrv_conn == NULL) {
                stream_terminate_connection(conn, "wbsrv_accept: out of memory");
index 9b03004b28558953fbbd1c55edb72a5995632d5f..26c404d12bdb2f684186820c7ff62c93fb4eb638 100644 (file)
@@ -34,6 +34,8 @@ struct wbsrv_service {
        struct idmap_context *idmap_ctx;
        const char *priv_pipe_dir;
        const char *pipe_dir;
+
+       struct wbsrv_connection *broken_connections;
 };
 
 struct wbsrv_samconn {
@@ -85,6 +87,9 @@ struct wbsrv_listen_socket {
   state of an open winbind connection
 */
 struct wbsrv_connection {
+       /* for the broken_connections DLIST */
+       struct wbsrv_connection *prev, *next;
+
        /* stream connection we belong to */
        struct stream_connection *conn;
 
@@ -94,9 +99,12 @@ struct wbsrv_connection {
        /* storage for protocol specific data */
        void *protocol_private_data;
 
-       /* how many calls are pending */
+       /* how many calls are pending (do not terminate the connection with calls pending a reply) */
        uint32_t pending_calls;
 
+       /* is this connection pending termination?  If so, why? */
+       const char *terminate;
+
        struct tstream_context *tstream;
 
        struct tevent_queue *send_queue;