Moved all signal-based name resolution timeout handling into a single new
authorDan Fandrich <dan@coneharvesters.com>
Mon, 29 Sep 2008 21:02:22 +0000 (21:02 +0000)
committerDan Fandrich <dan@coneharvesters.com>
Mon, 29 Sep 2008 21:02:22 +0000 (21:02 +0000)
Curl_resolv_timeout function to reduce coupling.

CHANGES
lib/hostip.c
lib/hostip.h
lib/url.c

diff --git a/CHANGES b/CHANGES
index 8b9f552..8145348 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -6,6 +6,10 @@
 
                                   Changelog
 
+Daniel Fandrich (29 Sep 2008)
+- Moved all signal-based name resolution timeout handling into a single new
+  Curl_resolv_timeout function to reduce coupling.
+
 Daniel Stenberg (29 Sep 2008)
 - Ian Lynagh provided a patch that now makes CURLOPT_RANGE work fine for SFTP
   downloads!
index 722c4b5..69f3c99 100644 (file)
@@ -55,6 +55,9 @@
 #ifdef HAVE_SETJMP_H
 #include <setjmp.h>
 #endif
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
 
 #ifdef HAVE_PROCESS_H
 #include <process.h>
 /* The last #include file should be: */
 #include "memdebug.h"
 
+#if defined(HAVE_ALARM) && defined(SIGALRM) && defined(HAVE_SIGSETJMP) \
+    && !defined(USE_ARES)
+/* alarm-based timeouts can only be used with all the dependencies satisfied */
+#define USE_ALARM_TIMEOUT
+#endif
+
 /*
  * hostip.c explained
  * ==================
@@ -388,19 +397,8 @@ int Curl_resolv(struct connectdata *conn,
   struct SessionHandle *data = conn->data;
   CURLcode result;
   int rc = CURLRESOLV_ERROR; /* default to failure */
-  *entry = NULL;
 
-#ifdef HAVE_SIGSETJMP
-  /* this allows us to time-out from the name resolver, as the timeout
-     will generate a signal and we will siglongjmp() from that here */
-  if(!data->set.no_signal) {
-    if(sigsetjmp(curl_jmpenv, 1)) {
-      /* this is coming from a siglongjmp() */
-      failf(data, "name lookup timed out");
-      return rc;
-    }
-  }
-#endif
+  *entry = NULL;
 
   /* Create an entry id, based upon the hostname and port */
   entry_id = create_hostcache_id(hostname, port);
@@ -484,6 +482,174 @@ int Curl_resolv(struct connectdata *conn,
   return rc;
 }
 
+#ifdef USE_ALARM_TIMEOUT 
+/*
+ * This signal handler jumps back into the main libcurl code and continues
+ * execution.  This effectively causes the remainder of the application to run
+ * within a signal handler which is nonportable and could lead to problems.
+ */
+static
+RETSIGTYPE alarmfunc(int sig)
+{
+  /* this is for "-ansi -Wall -pedantic" to stop complaining!   (rabe) */
+  (void)sig;
+  siglongjmp(curl_jmpenv, 1);
+  return;
+}
+#endif /* USE_ALARM_TIMEOUT */
+
+/*
+ * Curl_resolv_timeout() is the same as Curl_resolv() but specifies a
+ * timeout.  This function might return immediately if we're using asynch
+ * resolves. See the return codes.
+ *
+ * The cache entry we return will get its 'inuse' counter increased when this
+ * function is used. You MUST call Curl_resolv_unlock() later (when you're
+ * done using this struct) to decrease the counter again.
+ *
+ * If built with a synchronous resolver and use of signals is not
+ * disabled by the application, then a nonzero timeout will cause a
+ * timeout after the specified number of milliseconds. Otherwise, timeout
+ * is ignored.
+ *
+ * Return codes:
+ *
+ * CURLRESOLV_TIMEDOUT(-2) = warning, time too short or previous alarm expired
+ * CURLRESOLV_ERROR   (-1) = error, no pointer
+ * CURLRESOLV_RESOLVED (0) = OK, pointer provided
+ * CURLRESOLV_PENDING  (1) = waiting for response, no pointer
+ */
+
+int Curl_resolv_timeout(struct connectdata *conn,
+                        const char *hostname,
+                        int port,
+                        struct Curl_dns_entry **entry,
+                        long timeout)
+{
+#ifdef USE_ALARM_TIMEOUT 
+#ifdef HAVE_SIGACTION
+  struct sigaction keep_sigact;   /* store the old struct here */
+  bool keep_copysig=FALSE;        /* did copy it? */
+  struct sigaction sigact;
+#else
+#ifdef HAVE_SIGNAL
+  void (*keep_sigact)(int);       /* store the old handler here */
+#endif /* HAVE_SIGNAL */
+#endif /* HAVE_SIGACTION */
+
+  unsigned int prev_alarm=0;
+#endif /* USE_ALARM_TIMEOUT */
+
+  struct SessionHandle *data = conn->data;
+  int rc = CURLRESOLV_ERROR; /* error by default */
+
+  *entry = NULL;
+
+#ifdef USE_ALARM_TIMEOUT 
+  if (data->set.no_signal)
+    /* Ignore the timeout when signals are disabled */
+    timeout = 0;
+
+  if(timeout && timeout < 1000)
+    /* The alarm() function only provides integer second resolution, so if
+       we want to wait less than one second we must bail out already now. */
+    return CURLRESOLV_TIMEDOUT;
+
+  if (timeout > 0) {
+    /* This allows us to time-out from the name resolver, as the timeout
+       will generate a signal and we will siglongjmp() from that here.
+       This technique has problems (see alarmfunc). */
+      if(sigsetjmp(curl_jmpenv, 1)) {
+        /* this is coming from a siglongjmp() after an alarm signal */
+        failf(data, "name lookup timed out");
+        return rc;
+      }
+
+    /*************************************************************
+     * Set signal handler to catch SIGALRM
+     * Store the old value to be able to set it back later!
+     *************************************************************/
+#ifdef HAVE_SIGACTION
+    sigaction(SIGALRM, NULL, &sigact);
+    keep_sigact = sigact;
+    keep_copysig = TRUE; /* yes, we have a copy */
+    sigact.sa_handler = alarmfunc;
+#ifdef SA_RESTART
+    /* HPUX doesn't have SA_RESTART but defaults to that behaviour! */
+    sigact.sa_flags &= ~SA_RESTART;
+#endif
+    /* now set the new struct */
+    sigaction(SIGALRM, &sigact, NULL);
+#else /* HAVE_SIGACTION */
+    /* no sigaction(), revert to the much lamer signal() */
+#ifdef HAVE_SIGNAL
+    keep_sigact = signal(SIGALRM, alarmfunc);
+#endif
+#endif /* HAVE_SIGACTION */
+
+    /* alarm() makes a signal get sent when the timeout fires off, and that
+       will abort system calls */
+    prev_alarm = alarm((unsigned int) (timeout ? timeout/1000L : timeout));
+  }
+
+#else
+#ifndef CURLRES_ASYNCH
+  if(timeout)
+    infof(data, "timeout on name lookup is not supported\n");
+#endif
+#endif /* USE_ALARM_TIMEOUT */
+
+  /* Perform the actual name resolution. This might be interrupted by an
+   * alarm if it takes too long.
+   */
+  rc = Curl_resolv(conn, hostname, port, entry);
+
+#ifdef USE_ALARM_TIMEOUT 
+  if (timeout > 0) {
+
+#ifdef HAVE_SIGACTION
+    if(keep_copysig) {
+      /* we got a struct as it looked before, now put that one back nice
+         and clean */
+      sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */
+    }
+#else
+#ifdef HAVE_SIGNAL
+    /* restore the previous SIGALRM handler */
+    signal(SIGALRM, keep_sigact);
+#endif
+#endif /* HAVE_SIGACTION */
+
+    /* switch back the alarm() to either zero or to what it was before minus
+       the time we spent until now! */
+    if(prev_alarm) {
+      /* there was an alarm() set before us, now put it back */
+      unsigned long elapsed_ms = Curl_tvdiff(Curl_tvnow(), conn->created);
+
+      /* the alarm period is counted in even number of seconds */
+      unsigned long alarm_set = prev_alarm - elapsed_ms/1000;
+
+      if(!alarm_set ||
+         ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) {
+        /* if the alarm time-left reached zero or turned "negative" (counted
+           with unsigned values), we should fire off a SIGALRM here, but we
+           won't, and zero would be to switch it off so we never set it to
+           less than 1! */
+        alarm(1);
+        rc = CURLRESOLV_TIMEDOUT;
+        failf(data, "Previous alarm fired off!");
+      }
+      else
+        alarm((unsigned int)alarm_set);
+    }
+    else
+      alarm(0); /* just shut it off */
+  }
+#endif /* USE_ALARM_TIMEOUT */
+
+  return rc;
+}
+
 /*
  * Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been
  * made, the struct may be destroyed due to pruning. It is important that only
index c6172be..3864dd9 100644 (file)
@@ -158,11 +158,15 @@ struct Curl_dns_entry {
  * use, or we'll leak memory!
  */
 /* return codes */
+#define CURLRESOLV_TIMEDOUT -2
 #define CURLRESOLV_ERROR    -1
 #define CURLRESOLV_RESOLVED  0
 #define CURLRESOLV_PENDING   1
 int Curl_resolv(struct connectdata *conn, const char *hostname,
                 int port, struct Curl_dns_entry **dnsentry);
+int Curl_resolv_timeout(struct connectdata *conn, const char *hostname,
+                        int port, struct Curl_dns_entry **dnsentry,
+                        long timeout);
 
 /*
  * Curl_ipvalid() checks what CURL_IPRESOLVE_* requirements that might've
index 53e2bf7..df3e3ca 100644 (file)
--- a/lib/url.c
+++ b/lib/url.c
@@ -60,9 +60,6 @@
 #ifdef HAVE_SYS_IOCTL_H
 #include <sys/ioctl.h>
 #endif
-#ifdef HAVE_SIGNAL_H
-#include <signal.h>
-#endif
 
 #ifdef HAVE_SYS_PARAM_H
 #include <sys/param.h>
 #include <inet.h>
 #endif
 
-#ifdef HAVE_SETJMP_H
-#include <setjmp.h>
-#endif
-
 #ifndef HAVE_SOCKET
 #error "We can't compile without socket() support!"
 #endif
@@ -169,27 +162,6 @@ static void flush_cookies(struct SessionHandle *data, int cleanup);
 #define verboseconnect(x)  do { } while (0)
 #endif
 
-#ifndef WIN32
-/* not for WIN32 builds */
-
-#if defined(HAVE_ALARM) && defined(SIGALRM) && !defined(USE_ARES)
-/*
- * This signal handler jumps back into the main libcurl code and continues
- * execution.  This effectively causes the remainder of the application to run
- * within a signal handler which is nonportable and could lead to problems.
- */
-static
-RETSIGTYPE alarmfunc(int sig)
-{
-  /* this is for "-ansi -Wall -pedantic" to stop complaining!   (rabe) */
-  (void)sig;
-#ifdef HAVE_SIGSETJMP
-  siglongjmp(curl_jmpenv, 1);
-#endif
-  return;
-}
-#endif /* HAVE_ALARM && SIGALRM && !USE_ARES */
-#endif /* WIN32 */
 
 /*
  * Protocol table.
@@ -3788,34 +3760,19 @@ static CURLcode resolve_server(struct SessionHandle *data,
                                bool *async)
 {
   CURLcode result=CURLE_OK;
-
-#if defined(HAVE_ALARM) && defined(SIGALRM) && !defined(USE_ARES)
-#ifdef HAVE_SIGACTION
-  struct sigaction keep_sigact;   /* store the old struct here */
-  bool keep_copysig=FALSE;        /* did copy it? */
-#else
-#ifdef HAVE_SIGNAL
-  void (*keep_sigact)(int);       /* store the old handler here */
-#endif /* HAVE_SIGNAL */
-#endif /* HAVE_SIGACTION */
-
-  unsigned int prev_alarm=0;
+  long shortest = 0; /* default to no timeout */
 
   /*************************************************************
-   * Set timeout if that is being used, and we're not using an asynchronous
-   * name resolve.
+   * Set timeout if that is being used
    *************************************************************/
-  if((data->set.timeout || data->set.connecttimeout) && !data->set.no_signal) {
-#ifdef HAVE_SIGACTION
-    struct sigaction sigact;
-#endif /* HAVE_SIGACTION */
+  if(data->set.timeout || data->set.connecttimeout) {
 
     /* We set the timeout on the name resolving phase first, separately from
      * the download/upload part to allow a maximum time on everything. This is
      * a signal-based timeout, why it won't work and shouldn't be used in
      * multi-threaded environments. */
 
-    long shortest = data->set.timeout; /* default to this timeout value */
+    shortest = data->set.timeout; /* default to this timeout value */
     if(shortest && data->set.connecttimeout &&
        (data->set.connecttimeout < shortest))
       /* if both are set, pick the shortest */
@@ -3823,42 +3780,10 @@ static CURLcode resolve_server(struct SessionHandle *data,
     else if(!shortest)
       /* if timeout is not set, use the connect timeout */
       shortest = data->set.connecttimeout;
-
-    if(shortest < 1000)
-      /* the alarm() function only provide integer second resolution, so if
-         we want to wait less than one second we must bail out already now. */
-      return CURLE_OPERATION_TIMEDOUT;
-
-    /*************************************************************
-     * Set signal handler to catch SIGALRM
-     * Store the old value to be able to set it back later!
-     *************************************************************/
-#ifdef HAVE_SIGACTION
-    sigaction(SIGALRM, NULL, &sigact);
-    keep_sigact = sigact;
-    keep_copysig = TRUE; /* yes, we have a copy */
-    sigact.sa_handler = alarmfunc;
-#ifdef SA_RESTART
-    /* HPUX doesn't have SA_RESTART but defaults to that behaviour! */
-    sigact.sa_flags &= ~SA_RESTART;
-#endif
-    /* now set the new struct */
-    sigaction(SIGALRM, &sigact, NULL);
-#else /* HAVE_SIGACTION */
-    /* no sigaction(), revert to the much lamer signal() */
-#ifdef HAVE_SIGNAL
-    keep_sigact = signal(SIGALRM, alarmfunc);
-#endif
-#endif /* HAVE_SIGACTION */
-
-    /* alarm() makes a signal get sent when the timeout fires off, and that
-       will abort system calls */
-    prev_alarm = alarm((unsigned int) (shortest ? shortest/1000L : shortest));
-    /* We can expect the conn->created time to be "now", as that was just
-       recently set in the beginning of this function and nothing slow
-       has been done since then until now. */
+  /* We can expect the conn->created time to be "now", as that was just
+     recently set in the beginning of this function and nothing slow
+     has been done since then until now. */
   }
-#endif /* HAVE_ALARM && SIGALRM && !USE_ARES */
 
   /*************************************************************
    * Resolve the name of the server or proxy
@@ -3885,10 +3810,14 @@ static CURLcode resolve_server(struct SessionHandle *data,
       conn->port =  conn->remote_port; /* it is the same port */
 
       /* Resolve target host right on */
-      rc = Curl_resolv(conn, conn->host.name, (int)conn->port, &hostaddr);
+      rc = Curl_resolv_timeout(conn, conn->host.name, (int)conn->port,
+                               &hostaddr, shortest);
       if(rc == CURLRESOLV_PENDING)
         *async = TRUE;
 
+      else if (rc == CURLRESOLV_TIMEDOUT)
+        result = CURLE_OPERATION_TIMEDOUT;
+
       else if(!hostaddr) {
         failf(data, "Couldn't resolve host '%s'", conn->host.dispname);
         result =  CURLE_COULDNT_RESOLVE_HOST;
@@ -3902,11 +3831,15 @@ static CURLcode resolve_server(struct SessionHandle *data,
       fix_hostname(data, conn, &conn->proxy);
 
       /* resolve proxy */
-      rc = Curl_resolv(conn, conn->proxy.name, (int)conn->port, &hostaddr);
+      rc = Curl_resolv_timeout(conn, conn->proxy.name, (int)conn->port,
+                               &hostaddr, shortest);
 
       if(rc == CURLRESOLV_PENDING)
         *async = TRUE;
 
+      else if (rc == CURLRESOLV_TIMEDOUT)
+        result = CURLE_OPERATION_TIMEDOUT;
+
       else if(!hostaddr) {
         failf(data, "Couldn't resolve proxy '%s'", conn->proxy.dispname);
         result = CURLE_COULDNT_RESOLVE_PROXY;
@@ -3916,48 +3849,6 @@ static CURLcode resolve_server(struct SessionHandle *data,
     *addr = hostaddr;
   }
 
-#if defined(HAVE_ALARM) && defined(SIGALRM) && !defined(USE_ARES)
-  if((data->set.timeout || data->set.connecttimeout) && !data->set.no_signal) {
-#ifdef HAVE_SIGACTION
-    if(keep_copysig) {
-      /* we got a struct as it looked before, now put that one back nice
-         and clean */
-      sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */
-    }
-#else
-#ifdef HAVE_SIGNAL
-    /* restore the previous SIGALRM handler */
-    signal(SIGALRM, keep_sigact);
-#endif
-#endif /* HAVE_SIGACTION */
-
-    /* switch back the alarm() to either zero or to what it was before minus
-       the time we spent until now! */
-    if(prev_alarm) {
-      /* there was an alarm() set before us, now put it back */
-      unsigned long elapsed_ms = Curl_tvdiff(Curl_tvnow(), conn->created);
-      unsigned long alarm_set;
-
-      /* the alarm period is counted in even number of seconds */
-      alarm_set = prev_alarm - elapsed_ms/1000;
-
-      if(!alarm_set ||
-         ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) {
-        /* if the alarm time-left reached zero or turned "negative" (counted
-           with unsigned values), we should fire off a SIGALRM here, but we
-           won't, and zero would be to switch it off so we never set it to
-           less than 1! */
-        alarm(1);
-        result = CURLE_OPERATION_TIMEDOUT;
-        failf(data, "Previous alarm fired off!");
-      }
-      else
-        alarm((unsigned int)alarm_set);
-    }
-    else
-      alarm(0); /* just shut it off */
-  }
-#endif /* HAVE_ALARM && SIGALRM && !USE_ARES */
   return result;
 }