diff options
author | Dan Fandrich <dan@coneharvesters.com> | 2008-09-29 21:02:22 +0000 |
---|---|---|
committer | Dan Fandrich <dan@coneharvesters.com> | 2008-09-29 21:02:22 +0000 |
commit | 4adcf7e9ed32ed055470a17f715ee222285c1308 (patch) | |
tree | 6a8d9c28552a054836261626d2290ff185695c61 /lib/hostip.c | |
parent | 9f8c40ed78a8673ed14427dee8bd3c1858843745 (diff) | |
download | curl-4adcf7e9ed32ed055470a17f715ee222285c1308.tar.gz |
Moved all signal-based name resolution timeout handling into a single new
Curl_resolv_timeout function to reduce coupling.
Diffstat (limited to 'lib/hostip.c')
-rw-r--r-- | lib/hostip.c | 190 |
1 files changed, 178 insertions, 12 deletions
diff --git a/lib/hostip.c b/lib/hostip.c index 722c4b525..69f3c997f 100644 --- a/lib/hostip.c +++ b/lib/hostip.c @@ -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> @@ -76,6 +79,12 @@ /* 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 |