Gisle Vanem's improved verbose output and timeout handling when connecting to
authorDaniel Stenberg <daniel@haxx.se>
Thu, 10 Jun 2004 11:06:21 +0000 (11:06 +0000)
committerDaniel Stenberg <daniel@haxx.se>
Thu, 10 Jun 2004 11:06:21 +0000 (11:06 +0000)
a host name that resolves to multiple IP addresses.

lib/connect.c
lib/hostip.c
lib/hostip.h
lib/hostip6.c
lib/hostthre.c
lib/url.c

index 26bf283..a2c5ad6 100644 (file)
@@ -490,8 +490,8 @@ CURLcode Curl_is_connected(struct connectdata *conn,
   /* check for connect without timeout as we want to return immediately */
   rc = waitconnect(sockfd, 0);
 
-  if(0 == rc) {
-    if (verifyconnect(sockfd,NULL)) {
+  if(WAITCONN_CONNECTED == rc) {
+    if (verifyconnect(sockfd, NULL)) {
       /* we are connected, awesome! */
       *connected = TRUE;
       return CURLE_OK;
@@ -500,7 +500,7 @@ CURLcode Curl_is_connected(struct connectdata *conn,
     failf(data, "Connection failed");
     return CURLE_COULDNT_CONNECT;
   }
-  else if(1 != rc) {
+  else if(WAITCONN_TIMEOUT != rc) {
     int error = Curl_ourerrno();
     failf(data, "Failed connect to %s:%d; %s",
           conn->host.name, conn->port, Curl_strerror(conn,error));
@@ -549,22 +549,22 @@ CURLcode Curl_connecthost(struct connectdata *conn,  /* context */
                           bool *connected)           /* really connected? */
 {
   struct SessionHandle *data = conn->data;
+  curl_socket_t sockfd = CURL_SOCKET_BAD;
   int rc, error;
-  curl_socket_t sockfd= CURL_SOCKET_BAD;
-  int aliasindex=0;
-  char *hostname;
+  int aliasindex;
+  int num_addr;
+  const char *hostname;
+  bool conected;
 
+  Curl_ipconnect *curr_addr;
   struct timeval after;
   struct timeval before = Curl_tvnow();
 
-#ifdef ENABLE_IPV6
-  struct addrinfo *ai;
-#endif
-
   /*************************************************************
    * Figure out what maximum time we have left
    *************************************************************/
-  long timeout_ms=300000; /* milliseconds, default to five minutes */
+  long timeout_ms=300000; /* milliseconds, default to five minutes total */
+  long timeout_per_addr;
 
   *connected = FALSE; /* default to not connected */
 
@@ -600,31 +600,38 @@ CURLcode Curl_connecthost(struct connectdata *conn,  /* context */
     }
   }
 
+  /* Max time for each address */
+  num_addr = Curl_num_addresses(remotehost->addr);
+  timeout_per_addr = timeout_ms / num_addr;
+
   hostname = data->change.proxy?conn->proxy.name:conn->host.name;
+
   infof(data, "About to connect() to %s port %d\n",
         hostname, port);
 
+  /* Below is the loop that attempts to connect to all IP-addresses we
+   * know for the given host. One by one until one IP succeedes.
+   */
 #ifdef ENABLE_IPV6
   /*
    * Connecting with a getaddrinfo chain
    */
-  for (ai = remotehost->addr; ai; ai = ai->ai_next, aliasindex++) {
-    sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
-    if (sockfd == CURL_SOCKET_BAD)
+  for (curr_addr = remotehost->addr, aliasindex=0; curr_addr;
+       curr_addr = curr_addr->ai_next, aliasindex++) {
+    sockfd = socket(curr_addr->ai_family, curr_addr->ai_socktype,
+                    curr_addr->ai_protocol);
+    if (sockfd == CURL_SOCKET_BAD) {
+      timeout_per_addr += timeout_per_addr / (num_addr - aliasindex);
       continue;
+    }
 
-    else if(data->set.tcp_nodelay)
-      Curl_setNoDelay(conn, sockfd);
 #else
   /*
    * Connecting with old style IPv4-only support
    */
-
-  /* This is the loop that attempts to connect to all IP-addresses we
-     know for the given host. One by one. */
-  for(rc=-1, aliasindex=0;
-      rc && (struct in_addr *)remotehost->addr->h_addr_list[aliasindex];
-      aliasindex++) {
+  curr_addr = (Curl_ipconnect*)remotehost->addr->h_addr_list[0];
+  for(aliasindex=0; curr_addr;
+      curr_addr=(Curl_ipconnect*)remotehost->addr->h_addr_list[++aliasindex]) {
     struct sockaddr_in serv_addr;
 
     /* create an IPv4 TCP socket */
@@ -634,18 +641,24 @@ CURLcode Curl_connecthost(struct connectdata *conn,  /* context */
       return CURLE_COULDNT_CONNECT; /* big time error */
     }
 
-    else if(data->set.tcp_nodelay)
-      Curl_setNoDelay(conn, sockfd);
-
     /* nasty address work before connect can be made */
     memset((char *) &serv_addr, '\0', sizeof(serv_addr));
-    memcpy((char *)&(serv_addr.sin_addr),
-           (struct in_addr *)remotehost->addr->h_addr_list[aliasindex],
+    memcpy((char *)&(serv_addr.sin_addr), curr_addr,
            sizeof(struct in_addr));
     serv_addr.sin_family = remotehost->addr->h_addrtype;
     serv_addr.sin_port = htons((unsigned short)port);
 #endif
 
+    {
+      char addr_buf[256] = "";
+
+      Curl_printable_address(curr_addr, addr_buf, sizeof(addr_buf));
+      infof(data, "  Trying %s... ", addr_buf);
+    }
+
+    if(data->set.tcp_nodelay)
+      Curl_setNoDelay(conn, sockfd);
+
     if(conn->data->set.device) {
       /* user selected to bind the outgoing socket to a specified "device"
          before doing connect */
@@ -661,7 +674,7 @@ CURLcode Curl_connecthost(struct connectdata *conn,  /* context */
        a defined macro on some platforms and some compilers don't like to mix
        #ifdefs with macro usage! (AmigaOS is one such platform) */
 #ifdef ENABLE_IPV6
-    rc = connect(sockfd, ai->ai_addr, ai->ai_addrlen);
+    rc = connect(sockfd, curr_addr->ai_addr, curr_addr->ai_addrlen);
 #else
     rc = connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
 #endif
@@ -682,9 +695,9 @@ CURLcode Curl_connecthost(struct connectdata *conn,  /* context */
         /* asynchronous connect, wait for connect or timeout */
         if(data->state.used_interface == Curl_if_multi)
           /* don't hang when doing multi */
-          timeout_ms = 0;
+          timeout_per_addr = timeout_ms = 0;
 
-        rc = waitconnect(sockfd, timeout_ms);
+        rc = waitconnect(sockfd, timeout_per_addr);
         break;
       default:
         /* unknown error, fallthrough and try another address! */
@@ -694,26 +707,27 @@ CURLcode Curl_connecthost(struct connectdata *conn,  /* context */
       }
     }
 
-    /* The '1 == rc' comes from the waitconnect(), and not from connect().
-       We can be sure of this since connect() cannot return 1. */
-    if((1 == rc) && (data->state.used_interface == Curl_if_multi)) {
+    /* The 'WAITCONN_TIMEOUT == rc' comes from the waitconnect(), and not from
+       connect(). We can be sure of this since connect() cannot return 1. */
+    if((WAITCONN_TIMEOUT == rc) &&
+       (data->state.used_interface == Curl_if_multi)) {
       /* Timeout when running the multi interface, we return here with a
          CURLE_OK return code. */
       rc = 0;
       break;
     }
 
-    if(0 == rc) {
-      if (verifyconnect(sockfd,NULL)) {
-        /* we are connected, awesome! */
-        *connected = TRUE; /* this is a true connect */
-        break;
-      }
-      /* nope, not connected for real */
-      rc = -1;
+    conected = verifyconnect(sockfd, &error);
+
+    if(!rc && conected) {
+      /* we are connected, awesome! */
+      *connected = TRUE; /* this is a true connect */
+      break;
     }
+    if(WAITCONN_TIMEOUT == rc)
+      infof(data, "Timeout\n");
     else
-      verifyconnect(sockfd,&error);    /* get non-blocking error */
+      infof(data, "%s\n", Curl_strerror(conn, error));
 
     /* connect failed or timed out */
     sclose(sockfd);
@@ -727,24 +741,19 @@ CURLcode Curl_connecthost(struct connectdata *conn,  /* context */
       return CURLE_OPERATION_TIMEOUTED;
     }
     before = after;
-  }
+  }  /* end of connect-to-each-address loop */
+
   if (sockfd == CURL_SOCKET_BAD) {
     /* no good connect was made */
-    *sockconn = -1;
-    failf(data, "Connect failed; %s", Curl_strerror(conn,error));
+    *sockconn = CURL_SOCKET_BAD;
     return CURLE_COULDNT_CONNECT;
   }
 
   /* leave the socket in non-blocking mode */
 
   /* store the address we use */
-  if(addr) {
-#ifdef ENABLE_IPV6
-    *addr = ai;
-#else
-    *addr = (struct in_addr *)remotehost->addr->h_addr_list[aliasindex];
-#endif
-  }
+  if(addr)
+    *addr = curr_addr;
 
   /* allow NULL-pointers to get passed in */
   if(sockconn)
index ffc8198..2a03677 100644 (file)
@@ -79,6 +79,7 @@
 #include "share.h"
 #include "strerror.h"
 #include "url.h"
+#include "inet_ntop.h"
 
 #define _MPRINTF_REPLACE /* use our functions only */
 #include <curl/mprintf.h>
@@ -168,6 +169,43 @@ void Curl_global_host_cache_dtor(void)
 }
 
 /*
+ * Return # of adresses in a Curl_addrinfo struct
+ */
+int Curl_num_addresses(const Curl_addrinfo *addr)
+{
+  int i;
+
+#ifdef ENABLE_IPV6
+  for (i = 0; addr; addr = addr->ai_next, i++)
+#else
+  for (i = 0; addr->h_addr_list[i]; i++)
+#endif
+      ;
+  return (i);
+}
+
+/*
+ * Curl_printable_address() returns a printable version of the 1st
+ * address given in the 2nd argument. The result will be stored in
+ * the buf that is bufsize bytes big.
+ *
+ * If the conversion fails, it returns NULL.
+ */
+const char *Curl_printable_address(const Curl_ipconnect *ip,
+                                   char *buf, size_t bufsize)
+{
+#ifdef CURLRES_IPV6
+  const void *ip4 = &((const struct sockaddr_in*)ip->ai_addr)->sin_addr;
+  const void *ip6 = &((const struct sockaddr_in6*)ip->ai_addr)->sin6_addr;
+  int af = ip->ai_family;
+
+  return Curl_inet_ntop(af, af == AF_INET6 ? ip6 : ip4, buf, bufsize);
+#else
+  return Curl_inet_ntop(AF_INET, ip, buf, bufsize);
+#endif
+}
+
+/*
  * Count the number of characters that an integer would use in a string
  * (base 10).
  */
index 75245e6..82aa4d5 100644 (file)
@@ -102,6 +102,9 @@ curl_hash *Curl_mk_dnscache(void);
 /* prune old entries from the DNS cache */
 void Curl_hostcache_prune(struct SessionHandle *data);
 
+/* Return # of adresses in a Curl_addrinfo struct */
+int Curl_num_addresses (const Curl_addrinfo *addr);
+
 #ifdef CURLDEBUG
 void curl_dofreeaddrinfo(struct addrinfo *freethis,
                          int line, const char *source);
@@ -133,12 +136,11 @@ void Curl_hostent_relocate(struct hostent *h, long offset);
 Curl_addrinfo *Curl_addrinfo_copy(Curl_addrinfo *orig);
 
 /*
- * (IPv6) Curl_printable_address() returns a printable version of the
- * ai->ai_addr address given in the 2nd argument. The first should be the
- * ai->ai_family and the result will be stored in the buf that is bufsize
- * bytes big.
+ * Curl_printable_address() returns a printable version of the
+ * 1st address given in the 2nd argument. The result will be stored
+ * in the buf that is bufsize bytes big.
  */
-const char *Curl_printable_address(int af, void *addr,
+const char *Curl_printable_address(const Curl_ipconnect *ip,
                                    char *buf, size_t bufsize);
 
 /*
index 9698ef3..55e2736 100644 (file)
@@ -79,7 +79,6 @@
 #include "share.h"
 #include "strerror.h"
 #include "url.h"
-#include "inet_ntop.h"
 
 #define _MPRINTF_REPLACE /* use our functions only */
 #include <curl/mprintf.h>
@@ -106,26 +105,6 @@ void Curl_freeaddrinfo(Curl_addrinfo *p)
   freeaddrinfo(p);
 }
 
-/*
- * Curl_printable_address() returns a printable version of the ai->ai_addr
- * address given in the 2nd argument. The first should be the ai->ai_family
- * and the result will be stored in the buf that is bufsize bytes big.
- *
- * If the conversion fails, it returns NULL.
- */
-const char *Curl_printable_address(int af, void *addr,
-                                   char *buf, size_t bufsize)
-{
-  const struct in_addr *addr4 =
-    &((const struct sockaddr_in*)addr)->sin_addr;
-  const struct in6_addr *addr6 =
-    &((const struct sockaddr_in6*)addr)->sin6_addr;
-  return Curl_inet_ntop(af, af == AF_INET6 ?
-                        (const void *)addr6 :
-                        (const void *)addr4, buf, bufsize);
-}
-
-
 #ifdef CURLRES_ASYNCH
 /*
  * Curl_addrinfo_copy() is used by the asynch callback to copy a given
index 360cad1..9014b02 100644 (file)
@@ -142,7 +142,7 @@ static void dump_addrinfo (struct connectdata *conn, const struct addrinfo *ai)
 
     trace_it("    fam %2d, CNAME %s, ",
              ai->ai_family, ai->ai_canonname ? ai->ai_canonname : "<none>");
-    if (Curl_printable_address(ai->ai_family, ai->ai_addr, buf, sizeof(buf)))
+    if (Curl_printable_address(ai, buf, sizeof(buf)))
       trace_it("%s\n", buf);
     else
       trace_it("failed; %s\n", Curl_strerror(conn,WSAGetLastError()));
index 2bc9e53..f83e494 100644 (file)
--- a/lib/url.c
+++ b/lib/url.c
@@ -1990,22 +1990,18 @@ static CURLcode ConnectPlease(struct connectdata *conn,
 static void verboseconnect(struct connectdata *conn)
 {
   struct SessionHandle *data = conn->data;
-  const char *host=NULL;
-  char addrbuf[256];
-
-  /* Get a printable version of the network address. */
+  char addrbuf[256] = "";
 #ifdef ENABLE_IPV6
-  struct addrinfo *ai = conn->serv_addr;
-  host = Curl_printable_address(ai->ai_family, ai->ai_addr,
-                                addrbuf, sizeof(addrbuf));
+  const Curl_ipconnect *addr = conn->serv_addr;
 #else
-  struct in_addr in;
-  (void) memcpy(&in.s_addr, &conn->serv_addr.sin_addr, sizeof (in.s_addr));
-  host = Curl_inet_ntop(AF_INET, &in, addrbuf, sizeof(addrbuf));
+  const Curl_ipconnect *addr = &conn->serv_addr.sin_addr;
 #endif
+
+  /* Get a printable version of the network address. */
+  Curl_printable_address(addr, addrbuf, sizeof(addrbuf));
   infof(data, "Connected to %s (%s) port %d\n",
-        conn->bits.httpproxy?conn->proxy.dispname:conn->host.dispname,
-        host?host:"", conn->port);
+        conn->bits.httpproxy ? conn->proxy.dispname : conn->host.dispname,
+        addrbuf[0] ? addrbuf : "??", conn->port);
 }
 
 /*