multi: handle timeouts on DNS servers by checking for new sockets
authorJason Glasgow <jglasgow@chromium.org>
Wed, 30 Nov 2011 20:23:44 +0000 (15:23 -0500)
committerDaniel Stenberg <daniel@haxx.se>
Fri, 2 Dec 2011 09:18:52 +0000 (10:18 +0100)
If the first name server is not available, the multi interface does
not invoke the socket_cb when the DNS request to the first name server
timesout.  Ensure that the list of sockets are always updated after
calling Curl_resolver_is_resolved.

This bug can be reproduced if Curl is complied with --enable_ares and
your code uses the multi socket interfaces and the
CURLMOPT_SOCKETFUNCTION option.  To test try:
  iptables -I INPUT \
           -s $(sed -n -e '/name/{s/.* //p;q}' /etc/resolv.conf)/32 \
           -j REJECT
and then run a program which uses the multi-interface.

lib/asyn-ares.c
lib/multi.c

index 7c2c372..0b45484 100644 (file)
@@ -227,18 +227,19 @@ int Curl_resolver_getsock(struct connectdata *conn,
   struct timeval maxtime;
   struct timeval timebuf;
   struct timeval *timeout;
+  long milli;
   int max = ares_getsock((ares_channel)conn->data->state.resolver,
                          (ares_socket_t *)socks, numsocks);
 
-
   maxtime.tv_sec = CURL_TIMEOUT_RESOLVE;
   maxtime.tv_usec = 0;
 
   timeout = ares_timeout((ares_channel)conn->data->state.resolver, &maxtime,
                          &timebuf);
-
-  Curl_expire(conn->data,
-              (timeout->tv_sec * 1000) + (timeout->tv_usec/1000));
+  milli = (timeout->tv_sec * 1000) + (timeout->tv_usec/1000);
+  if(milli == 0)
+    milli += 10;
+  Curl_expire(conn->data, milli);
 
   return max;
 }
index ae70851..3059e49 100644 (file)
@@ -1085,12 +1085,15 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
       /* check if we have the name resolved by now */
       easy->result = Curl_resolver_is_resolved(easy->easy_conn, &dns);
 
-      if(dns) {
-        /* Update sockets here. Mainly because the socket(s) may have been
-           closed and the application thus needs to be told, even if it is
-           likely that the same socket(s) will again be used further down. */
-        singlesocket(multi, easy);
+      /* Update sockets here, because the socket(s) may have been
+         closed and the application thus needs to be told, even if it
+         is likely that the same socket(s) will again be used further
+         down.  If the name has not yet been resolved, it is likely
+         that new sockets have been opened in an attempt to contact
+         another resolver. */
+      singlesocket(multi, easy);
 
+      if(dns) {
         /* Perform the next step in the connection phase, and then move on
            to the WAITCONNECT state */
         easy->result = Curl_async_resolved(easy->easy_conn,