>From fcba9849de1e6499a2dcf7ccda277698ef8e4552 Mon Sep 17 00:00:00 2001
From: Martin Nagy <mnagy@redhat.com>
Date: Mon, 11 Jan 2010 18:24:16 +0100
Subject: [PATCH] Make periodic checks for DNS timeouts

Since we only call c-ares to process input on FD when there is an
activity on them, c-ares never gets a chance to react to a timed-out
request. This caused SSSD to hang. The current timeout is still pretty
long, about 1 minute. This will be fixed in a subsequent commit.

Fixes: #381
---
 server/resolv/async_resolv.c |   72 +++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 71 insertions(+), 1 deletions(-)

diff --git a/server/resolv/async_resolv.c b/server/resolv/async_resolv.c
index c350d6c..0f77aab 100644
--- a/server/resolv/async_resolv.c
+++ b/server/resolv/async_resolv.c
@@ -68,10 +68,11 @@ struct resolv_ctx {
     struct resolv_ctx *next;
 
     struct tevent_context *ev_ctx;
-
     ares_channel channel;
     /* List of file descriptors that are watched by tevent. */
     struct fd_watch *fds;
+    int pending_requests;
+    int timeout_scheduler_on;
 };
 
 struct resolv_ctx *context_list;
@@ -118,6 +119,66 @@ fd_input_available(struct tevent_context *ev, struct tevent_fd *fde,
     ares_process_fd(watch->ctx->channel, watch->fd, watch->fd);
 }
 
+static void
+check_fd_timeouts(struct tevent_context *ev, struct tevent_timer *te,
+                  struct timeval current_time, void *private_data)
+{
+    struct resolv_ctx *ctx = private_data;
+    struct fd_watch *fd_watch;
+    struct tevent_timer *tev_timer;
+
+    if (ctx->pending_requests == 0) {
+        ctx->timeout_scheduler_on = 0;
+        DEBUG(9, ("Unscheduling DNS timeout watcher\n"));
+        return;
+    }
+
+    DEBUG(9, ("Checking for DNS timeouts\n"));
+    DLIST_FOR_EACH(fd_watch, ctx->fds) {
+        ares_process_fd(ctx->channel, fd_watch->fd, fd_watch->fd);
+    }
+
+    current_time.tv_sec += 5;
+    tev_timer = tevent_add_timer(ev, ctx, current_time, check_fd_timeouts,
+                                 ctx);
+    if (tev_timer == NULL) {
+        DEBUG(1, ("Out of memory\n"));
+    }
+}
+
+static void
+schedule_timeout_watcher(struct tevent_context *ev, struct resolv_ctx *ctx)
+{
+    struct timeval tv;
+
+    ctx->pending_requests++;
+    if (ctx->timeout_scheduler_on) {
+        return;
+    }
+
+    DEBUG(9, ("Scheduling DNS timeout watcher\n"));
+
+    gettimeofday(&tv, NULL);
+    tv.tv_sec += 5;
+
+    if (tevent_add_timer(ev, ctx, tv, check_fd_timeouts, ctx) == NULL) {
+        DEBUG(1, ("Out of memory\n"));
+        return;
+    }
+
+    ctx->timeout_scheduler_on = 1;
+}
+
+static void
+unschedule_timeout_watcher(struct resolv_ctx *ctx)
+{
+    if (ctx->pending_requests <= 0) {
+        DEBUG(1, ("Pending DNS requests mismatch\n"));
+        return;
+    }
+
+    ctx->pending_requests--;
+}
 
 static void fd_event_add(struct resolv_ctx *ctx, int s);
 static void fd_event_write(struct resolv_ctx *ctx, int s);
@@ -414,6 +475,7 @@ resolv_gethostbyname_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
         return NULL;
     }
     tevent_req_set_callback(subreq, ares_gethostbyname_wakeup, req);
+    schedule_timeout_watcher(ev, ctx);
 
     return req;
 }
@@ -431,6 +493,8 @@ resolv_gethostbyname_done(void *arg, int status, int timeouts, struct hostent *h
         return;
     }
 
+    unschedule_timeout_watcher(state->resolv_ctx);
+
     if (hostent != NULL) {
         state->hostent = resolv_copy_hostent(req, hostent);
         if (state->hostent == NULL) {
@@ -612,6 +676,7 @@ resolv_getsrv_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
         return NULL;
     }
     tevent_req_set_callback(subreq, ares_getsrv_wakeup, req);
+    schedule_timeout_watcher(ev, ctx);
 
     return req;
 }
@@ -631,6 +696,8 @@ resolv_getsrv_done(void *arg, int status, int timeouts, unsigned char *abuf, int
         return;
     }
 
+    unschedule_timeout_watcher(state->resolv_ctx);
+
     state->status = status;
     state->timeouts = timeouts;
 
@@ -812,6 +879,7 @@ resolv_gettxt_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
         return NULL;
     }
     tevent_req_set_callback(subreq, ares_gettxt_wakeup, req);
+    schedule_timeout_watcher(ev, ctx);
 
     return req;
 }
@@ -831,6 +899,8 @@ resolv_gettxt_done(void *arg, int status, int timeouts, unsigned char *abuf, int
         return;
     }
 
+    unschedule_timeout_watcher(state->resolv_ctx);
+
     state->status = status;
     state->timeouts = timeouts;
 
-- 
1.6.2.5

