Gisle Vanem's improved verbose output and timeout handling when connecting to

a host name that resolves to multiple IP addresses.
This commit is contained in:
Daniel Stenberg 2004-06-10 11:06:21 +00:00
parent 2098871509
commit 9f341f9ce5
6 changed files with 116 additions and 92 deletions

View File

@ -490,7 +490,7 @@ CURLcode Curl_is_connected(struct connectdata *conn,
/* check for connect without timeout as we want to return immediately */ /* check for connect without timeout as we want to return immediately */
rc = waitconnect(sockfd, 0); rc = waitconnect(sockfd, 0);
if(0 == rc) { if(WAITCONN_CONNECTED == rc) {
if (verifyconnect(sockfd, NULL)) { if (verifyconnect(sockfd, NULL)) {
/* we are connected, awesome! */ /* we are connected, awesome! */
*connected = TRUE; *connected = TRUE;
@ -500,7 +500,7 @@ CURLcode Curl_is_connected(struct connectdata *conn,
failf(data, "Connection failed"); failf(data, "Connection failed");
return CURLE_COULDNT_CONNECT; return CURLE_COULDNT_CONNECT;
} }
else if(1 != rc) { else if(WAITCONN_TIMEOUT != rc) {
int error = Curl_ourerrno(); int error = Curl_ourerrno();
failf(data, "Failed connect to %s:%d; %s", failf(data, "Failed connect to %s:%d; %s",
conn->host.name, conn->port, Curl_strerror(conn,error)); conn->host.name, conn->port, Curl_strerror(conn,error));
@ -549,22 +549,22 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */
bool *connected) /* really connected? */ bool *connected) /* really connected? */
{ {
struct SessionHandle *data = conn->data; struct SessionHandle *data = conn->data;
int rc, error;
curl_socket_t sockfd = CURL_SOCKET_BAD; curl_socket_t sockfd = CURL_SOCKET_BAD;
int aliasindex=0; int rc, error;
char *hostname; int aliasindex;
int num_addr;
const char *hostname;
bool conected;
Curl_ipconnect *curr_addr;
struct timeval after; struct timeval after;
struct timeval before = Curl_tvnow(); struct timeval before = Curl_tvnow();
#ifdef ENABLE_IPV6
struct addrinfo *ai;
#endif
/************************************************************* /*************************************************************
* Figure out what maximum time we have left * 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 */ *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; hostname = data->change.proxy?conn->proxy.name:conn->host.name;
infof(data, "About to connect() to %s port %d\n", infof(data, "About to connect() to %s port %d\n",
hostname, port); 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 #ifdef ENABLE_IPV6
/* /*
* Connecting with a getaddrinfo chain * Connecting with a getaddrinfo chain
*/ */
for (ai = remotehost->addr; ai; ai = ai->ai_next, aliasindex++) { for (curr_addr = remotehost->addr, aliasindex=0; curr_addr;
sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); curr_addr = curr_addr->ai_next, aliasindex++) {
if (sockfd == CURL_SOCKET_BAD) 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; continue;
}
else if(data->set.tcp_nodelay)
Curl_setNoDelay(conn, sockfd);
#else #else
/* /*
* Connecting with old style IPv4-only support * Connecting with old style IPv4-only support
*/ */
curr_addr = (Curl_ipconnect*)remotehost->addr->h_addr_list[0];
/* This is the loop that attempts to connect to all IP-addresses we for(aliasindex=0; curr_addr;
know for the given host. One by one. */ curr_addr=(Curl_ipconnect*)remotehost->addr->h_addr_list[++aliasindex]) {
for(rc=-1, aliasindex=0;
rc && (struct in_addr *)remotehost->addr->h_addr_list[aliasindex];
aliasindex++) {
struct sockaddr_in serv_addr; struct sockaddr_in serv_addr;
/* create an IPv4 TCP socket */ /* create an IPv4 TCP socket */
@ -634,18 +641,24 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */
return CURLE_COULDNT_CONNECT; /* big time error */ 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 */ /* nasty address work before connect can be made */
memset((char *) &serv_addr, '\0', sizeof(serv_addr)); memset((char *) &serv_addr, '\0', sizeof(serv_addr));
memcpy((char *)&(serv_addr.sin_addr), memcpy((char *)&(serv_addr.sin_addr), curr_addr,
(struct in_addr *)remotehost->addr->h_addr_list[aliasindex],
sizeof(struct in_addr)); sizeof(struct in_addr));
serv_addr.sin_family = remotehost->addr->h_addrtype; serv_addr.sin_family = remotehost->addr->h_addrtype;
serv_addr.sin_port = htons((unsigned short)port); serv_addr.sin_port = htons((unsigned short)port);
#endif #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) { if(conn->data->set.device) {
/* user selected to bind the outgoing socket to a specified "device" /* user selected to bind the outgoing socket to a specified "device"
before doing connect */ 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 a defined macro on some platforms and some compilers don't like to mix
#ifdefs with macro usage! (AmigaOS is one such platform) */ #ifdefs with macro usage! (AmigaOS is one such platform) */
#ifdef ENABLE_IPV6 #ifdef ENABLE_IPV6
rc = connect(sockfd, ai->ai_addr, ai->ai_addrlen); rc = connect(sockfd, curr_addr->ai_addr, curr_addr->ai_addrlen);
#else #else
rc = connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); rc = connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
#endif #endif
@ -682,9 +695,9 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */
/* asynchronous connect, wait for connect or timeout */ /* asynchronous connect, wait for connect or timeout */
if(data->state.used_interface == Curl_if_multi) if(data->state.used_interface == Curl_if_multi)
/* don't hang when doing 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; break;
default: default:
/* unknown error, fallthrough and try another address! */ /* 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(). /* The 'WAITCONN_TIMEOUT == rc' comes from the waitconnect(), and not from
We can be sure of this since connect() cannot return 1. */ connect(). We can be sure of this since connect() cannot return 1. */
if((1 == rc) && (data->state.used_interface == Curl_if_multi)) { if((WAITCONN_TIMEOUT == rc) &&
(data->state.used_interface == Curl_if_multi)) {
/* Timeout when running the multi interface, we return here with a /* Timeout when running the multi interface, we return here with a
CURLE_OK return code. */ CURLE_OK return code. */
rc = 0; rc = 0;
break; break;
} }
if(0 == rc) { conected = verifyconnect(sockfd, &error);
if (verifyconnect(sockfd,NULL)) {
if(!rc && conected) {
/* we are connected, awesome! */ /* we are connected, awesome! */
*connected = TRUE; /* this is a true connect */ *connected = TRUE; /* this is a true connect */
break; break;
} }
/* nope, not connected for real */ if(WAITCONN_TIMEOUT == rc)
rc = -1; infof(data, "Timeout\n");
}
else else
verifyconnect(sockfd,&error); /* get non-blocking error */ infof(data, "%s\n", Curl_strerror(conn, error));
/* connect failed or timed out */ /* connect failed or timed out */
sclose(sockfd); sclose(sockfd);
@ -727,24 +741,19 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */
return CURLE_OPERATION_TIMEOUTED; return CURLE_OPERATION_TIMEOUTED;
} }
before = after; before = after;
} } /* end of connect-to-each-address loop */
if (sockfd == CURL_SOCKET_BAD) { if (sockfd == CURL_SOCKET_BAD) {
/* no good connect was made */ /* no good connect was made */
*sockconn = -1; *sockconn = CURL_SOCKET_BAD;
failf(data, "Connect failed; %s", Curl_strerror(conn,error));
return CURLE_COULDNT_CONNECT; return CURLE_COULDNT_CONNECT;
} }
/* leave the socket in non-blocking mode */ /* leave the socket in non-blocking mode */
/* store the address we use */ /* store the address we use */
if(addr) { if(addr)
#ifdef ENABLE_IPV6 *addr = curr_addr;
*addr = ai;
#else
*addr = (struct in_addr *)remotehost->addr->h_addr_list[aliasindex];
#endif
}
/* allow NULL-pointers to get passed in */ /* allow NULL-pointers to get passed in */
if(sockconn) if(sockconn)

View File

@ -79,6 +79,7 @@
#include "share.h" #include "share.h"
#include "strerror.h" #include "strerror.h"
#include "url.h" #include "url.h"
#include "inet_ntop.h"
#define _MPRINTF_REPLACE /* use our functions only */ #define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h> #include <curl/mprintf.h>
@ -167,6 +168,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 * Count the number of characters that an integer would use in a string
* (base 10). * (base 10).

View File

@ -102,6 +102,9 @@ curl_hash *Curl_mk_dnscache(void);
/* prune old entries from the DNS cache */ /* prune old entries from the DNS cache */
void Curl_hostcache_prune(struct SessionHandle *data); 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 #ifdef CURLDEBUG
void curl_dofreeaddrinfo(struct addrinfo *freethis, void curl_dofreeaddrinfo(struct addrinfo *freethis,
int line, const char *source); 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); Curl_addrinfo *Curl_addrinfo_copy(Curl_addrinfo *orig);
/* /*
* (IPv6) Curl_printable_address() returns a printable version of the * Curl_printable_address() returns a printable version of the
* ai->ai_addr address given in the 2nd argument. The first should be the * 1st address given in the 2nd argument. The result will be stored
* ai->ai_family and the result will be stored in the buf that is bufsize * in the buf that is bufsize bytes big.
* 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); char *buf, size_t bufsize);
/* /*

View File

@ -79,7 +79,6 @@
#include "share.h" #include "share.h"
#include "strerror.h" #include "strerror.h"
#include "url.h" #include "url.h"
#include "inet_ntop.h"
#define _MPRINTF_REPLACE /* use our functions only */ #define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h> #include <curl/mprintf.h>
@ -106,26 +105,6 @@ void Curl_freeaddrinfo(Curl_addrinfo *p)
freeaddrinfo(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 #ifdef CURLRES_ASYNCH
/* /*
* Curl_addrinfo_copy() is used by the asynch callback to copy a given * Curl_addrinfo_copy() is used by the asynch callback to copy a given

View File

@ -142,7 +142,7 @@ static void dump_addrinfo (struct connectdata *conn, const struct addrinfo *ai)
trace_it(" fam %2d, CNAME %s, ", trace_it(" fam %2d, CNAME %s, ",
ai->ai_family, ai->ai_canonname ? ai->ai_canonname : "<none>"); 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); trace_it("%s\n", buf);
else else
trace_it("failed; %s\n", Curl_strerror(conn,WSAGetLastError())); trace_it("failed; %s\n", Curl_strerror(conn,WSAGetLastError()));

View File

@ -1990,22 +1990,18 @@ static CURLcode ConnectPlease(struct connectdata *conn,
static void verboseconnect(struct connectdata *conn) static void verboseconnect(struct connectdata *conn)
{ {
struct SessionHandle *data = conn->data; struct SessionHandle *data = conn->data;
const char *host=NULL; char addrbuf[256] = "";
char addrbuf[256]; #ifdef ENABLE_IPV6
const Curl_ipconnect *addr = conn->serv_addr;
#else
const Curl_ipconnect *addr = &conn->serv_addr.sin_addr;
#endif
/* Get a printable version of the network address. */ /* Get a printable version of the network address. */
#ifdef ENABLE_IPV6 Curl_printable_address(addr, addrbuf, sizeof(addrbuf));
struct addrinfo *ai = conn->serv_addr;
host = Curl_printable_address(ai->ai_family, ai->ai_addr,
addrbuf, sizeof(addrbuf));
#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));
#endif
infof(data, "Connected to %s (%s) port %d\n", infof(data, "Connected to %s (%s) port %d\n",
conn->bits.httpproxy ? conn->proxy.dispname : conn->host.dispname, conn->bits.httpproxy ? conn->proxy.dispname : conn->host.dispname,
host?host:"", conn->port); addrbuf[0] ? addrbuf : "??", conn->port);
} }
/* /*