mirror of
https://github.com/moparisthebest/curl
synced 2024-08-13 17:03:50 -04:00
connect: happy eyeballs cleanup
Make sure each separate index in connn->tempaddr[] is used for a fixed family (and only that family) during the connection process. If family one takes a long time and family two fails immediately, the previous logic could misbehave and retry the same family two address repeatedly. Reported-by: Paul Vixie Reported-by: Jay Satiro Fixes #5083 Fixes #4954 Closes #5089
This commit is contained in:
parent
dc595210ae
commit
dbd16c3e25
101
lib/connect.c
101
lib/connect.c
@ -167,7 +167,7 @@ tcpkeepalive(struct Curl_easy *data,
|
|||||||
static CURLcode
|
static CURLcode
|
||||||
singleipconnect(struct connectdata *conn,
|
singleipconnect(struct connectdata *conn,
|
||||||
const Curl_addrinfo *ai, /* start connecting to this */
|
const Curl_addrinfo *ai, /* start connecting to this */
|
||||||
int sockindex); /* 0 or 1 among the temp ones */
|
int tempindex); /* 0 or 1 among the temp ones */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Curl_timeleft() returns the amount of milliseconds left allowed for the
|
* Curl_timeleft() returns the amount of milliseconds left allowed for the
|
||||||
@ -555,13 +555,27 @@ static bool verifyconnect(curl_socket_t sockfd, int *error)
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* update tempaddr[tempindex] (to the next entry), makes sure to stick
|
||||||
|
to the correct family */
|
||||||
|
static Curl_addrinfo *ainext(struct connectdata *conn,
|
||||||
|
int tempindex,
|
||||||
|
bool next) /* use current or next entry */
|
||||||
|
{
|
||||||
|
Curl_addrinfo *ai = conn->tempaddr[tempindex];
|
||||||
|
if(ai && next)
|
||||||
|
ai = ai->ai_next;
|
||||||
|
while(ai && (ai->ai_family != conn->tempfamily[tempindex]))
|
||||||
|
ai = ai->ai_next;
|
||||||
|
conn->tempaddr[tempindex] = ai;
|
||||||
|
return ai;
|
||||||
|
}
|
||||||
|
|
||||||
/* Used within the multi interface. Try next IP address, return TRUE if no
|
/* Used within the multi interface. Try next IP address, return TRUE if no
|
||||||
more address exists or error */
|
more address exists or error */
|
||||||
static CURLcode trynextip(struct connectdata *conn,
|
static CURLcode trynextip(struct connectdata *conn,
|
||||||
int sockindex,
|
int sockindex,
|
||||||
int tempindex)
|
int tempindex)
|
||||||
{
|
{
|
||||||
const int other = tempindex ^ 1;
|
|
||||||
CURLcode result = CURLE_COULDNT_CONNECT;
|
CURLcode result = CURLE_COULDNT_CONNECT;
|
||||||
|
|
||||||
/* First clean up after the failed socket.
|
/* First clean up after the failed socket.
|
||||||
@ -572,38 +586,15 @@ static CURLcode trynextip(struct connectdata *conn,
|
|||||||
conn->tempsock[tempindex] = CURL_SOCKET_BAD;
|
conn->tempsock[tempindex] = CURL_SOCKET_BAD;
|
||||||
|
|
||||||
if(sockindex == FIRSTSOCKET) {
|
if(sockindex == FIRSTSOCKET) {
|
||||||
Curl_addrinfo *ai = NULL;
|
Curl_addrinfo *ai = conn->tempaddr[tempindex];
|
||||||
int family = AF_UNSPEC;
|
|
||||||
|
|
||||||
if(conn->tempaddr[tempindex]) {
|
|
||||||
/* find next address in the same protocol family */
|
|
||||||
family = conn->tempaddr[tempindex]->ai_family;
|
|
||||||
ai = conn->tempaddr[tempindex]->ai_next;
|
|
||||||
}
|
|
||||||
#ifdef ENABLE_IPV6
|
|
||||||
else if(conn->tempaddr[0]) {
|
|
||||||
/* happy eyeballs - try the other protocol family */
|
|
||||||
int firstfamily = conn->tempaddr[0]->ai_family;
|
|
||||||
family = (firstfamily == AF_INET) ? AF_INET6 : AF_INET;
|
|
||||||
ai = conn->tempaddr[0]->ai_next;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
while(ai) {
|
while(ai) {
|
||||||
if(conn->tempaddr[other]) {
|
|
||||||
/* we can safely skip addresses of the other protocol family */
|
|
||||||
while(ai && ai->ai_family != family)
|
|
||||||
ai = ai->ai_next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(ai) {
|
if(ai) {
|
||||||
result = singleipconnect(conn, ai, tempindex);
|
result = singleipconnect(conn, ai, tempindex);
|
||||||
if(result == CURLE_COULDNT_CONNECT) {
|
if(result == CURLE_COULDNT_CONNECT) {
|
||||||
ai = ai->ai_next;
|
ai = ainext(conn, tempindex, TRUE);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
conn->tempaddr[tempindex] = ai;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -905,9 +896,10 @@ CURLcode Curl_is_connected(struct connectdata *conn,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* should we try another protocol family? */
|
/* should we try another protocol family? */
|
||||||
if(i == 0 && conn->tempaddr[1] == NULL &&
|
if(i == 0 && !conn->parallel_connect &&
|
||||||
(Curl_timediff(now, conn->connecttime) >=
|
(Curl_timediff(now, conn->connecttime) >=
|
||||||
data->set.happy_eyeballs_timeout)) {
|
data->set.happy_eyeballs_timeout)) {
|
||||||
|
conn->parallel_connect = TRUE; /* starting now */
|
||||||
trynextip(conn, sockindex, 1);
|
trynextip(conn, sockindex, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -967,7 +959,7 @@ CURLcode Curl_is_connected(struct connectdata *conn,
|
|||||||
|
|
||||||
conn->timeoutms_per_addr = conn->tempaddr[i]->ai_next == NULL ?
|
conn->timeoutms_per_addr = conn->tempaddr[i]->ai_next == NULL ?
|
||||||
allow : allow / 2;
|
allow : allow / 2;
|
||||||
|
ainext(conn, i, TRUE);
|
||||||
status = trynextip(conn, sockindex, i);
|
status = trynextip(conn, sockindex, i);
|
||||||
if((status != CURLE_COULDNT_CONNECT) ||
|
if((status != CURLE_COULDNT_CONNECT) ||
|
||||||
conn->tempsock[other] == CURL_SOCKET_BAD)
|
conn->tempsock[other] == CURL_SOCKET_BAD)
|
||||||
@ -984,7 +976,7 @@ CURLcode Curl_is_connected(struct connectdata *conn,
|
|||||||
|
|
||||||
/* if the first address family runs out of addresses to try before
|
/* if the first address family runs out of addresses to try before
|
||||||
the happy eyeball timeout, go ahead and try the next family now */
|
the happy eyeball timeout, go ahead and try the next family now */
|
||||||
if(conn->tempaddr[1] == NULL) {
|
{
|
||||||
result = trynextip(conn, sockindex, 1);
|
result = trynextip(conn, sockindex, 1);
|
||||||
if(!result)
|
if(!result)
|
||||||
return result;
|
return result;
|
||||||
@ -1113,7 +1105,7 @@ void Curl_sndbufset(curl_socket_t sockfd)
|
|||||||
*/
|
*/
|
||||||
static CURLcode singleipconnect(struct connectdata *conn,
|
static CURLcode singleipconnect(struct connectdata *conn,
|
||||||
const Curl_addrinfo *ai,
|
const Curl_addrinfo *ai,
|
||||||
int sockindex)
|
int tempindex)
|
||||||
{
|
{
|
||||||
struct Curl_sockaddr_ex addr;
|
struct Curl_sockaddr_ex addr;
|
||||||
int rc = -1;
|
int rc = -1;
|
||||||
@ -1129,15 +1121,12 @@ static CURLcode singleipconnect(struct connectdata *conn,
|
|||||||
int optval = 1;
|
int optval = 1;
|
||||||
#endif
|
#endif
|
||||||
char buffer[STRERROR_LEN];
|
char buffer[STRERROR_LEN];
|
||||||
curl_socket_t *sockp = &conn->tempsock[sockindex];
|
curl_socket_t *sockp = &conn->tempsock[tempindex];
|
||||||
*sockp = CURL_SOCKET_BAD;
|
*sockp = CURL_SOCKET_BAD;
|
||||||
|
|
||||||
result = Curl_socket(conn, ai, &addr, &sockfd);
|
result = Curl_socket(conn, ai, &addr, &sockfd);
|
||||||
if(result)
|
if(result)
|
||||||
/* Failed to create the socket, but still return OK since we signal the
|
return result;
|
||||||
lack of socket as well. This allows the parent function to keep looping
|
|
||||||
over alternative addresses/socket families etc. */
|
|
||||||
return CURLE_OK;
|
|
||||||
|
|
||||||
/* store remote address and port used in this connection attempt */
|
/* store remote address and port used in this connection attempt */
|
||||||
if(!Curl_addr2string((struct sockaddr*)&addr.sa_addr, addr.addrlen,
|
if(!Curl_addr2string((struct sockaddr*)&addr.sa_addr, addr.addrlen,
|
||||||
@ -1257,7 +1246,7 @@ static CURLcode singleipconnect(struct connectdata *conn,
|
|||||||
else if(conn->transport == TRNSPRT_QUIC) {
|
else if(conn->transport == TRNSPRT_QUIC) {
|
||||||
/* pass in 'sockfd' separately since it hasn't been put into the
|
/* pass in 'sockfd' separately since it hasn't been put into the
|
||||||
tempsock array at this point */
|
tempsock array at this point */
|
||||||
result = Curl_quic_connect(conn, sockfd, sockindex,
|
result = Curl_quic_connect(conn, sockfd, tempindex,
|
||||||
&addr.sa_addr, addr.addrlen);
|
&addr.sa_addr, addr.addrlen);
|
||||||
if(result)
|
if(result)
|
||||||
error = SOCKERRNO;
|
error = SOCKERRNO;
|
||||||
@ -1315,7 +1304,7 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */
|
|||||||
struct Curl_easy *data = conn->data;
|
struct Curl_easy *data = conn->data;
|
||||||
struct curltime before = Curl_now();
|
struct curltime before = Curl_now();
|
||||||
CURLcode result = CURLE_COULDNT_CONNECT;
|
CURLcode result = CURLE_COULDNT_CONNECT;
|
||||||
|
int i;
|
||||||
timediff_t timeout_ms = Curl_timeleft(data, &before, TRUE);
|
timediff_t timeout_ms = Curl_timeleft(data, &before, TRUE);
|
||||||
|
|
||||||
if(timeout_ms < 0) {
|
if(timeout_ms < 0) {
|
||||||
@ -1325,28 +1314,34 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */
|
|||||||
}
|
}
|
||||||
|
|
||||||
conn->num_addr = Curl_num_addresses(remotehost->addr);
|
conn->num_addr = Curl_num_addresses(remotehost->addr);
|
||||||
conn->tempaddr[0] = remotehost->addr;
|
conn->tempaddr[0] = conn->tempaddr[1] = remotehost->addr;
|
||||||
conn->tempaddr[1] = NULL;
|
conn->tempsock[0] = conn->tempsock[1] = CURL_SOCKET_BAD;
|
||||||
conn->tempsock[0] = CURL_SOCKET_BAD;
|
|
||||||
conn->tempsock[1] = CURL_SOCKET_BAD;
|
|
||||||
|
|
||||||
/* Max time for the next connection attempt */
|
/* Max time for the next connection attempt */
|
||||||
conn->timeoutms_per_addr =
|
conn->timeoutms_per_addr =
|
||||||
conn->tempaddr[0]->ai_next == NULL ? timeout_ms : timeout_ms / 2;
|
conn->tempaddr[0]->ai_next == NULL ? timeout_ms : timeout_ms / 2;
|
||||||
|
|
||||||
/* start connecting to first IP */
|
conn->tempfamily[0] = conn->tempaddr[0]?
|
||||||
while(conn->tempaddr[0]) {
|
conn->tempaddr[0]->ai_family:0;
|
||||||
result = singleipconnect(conn, conn->tempaddr[0], 0);
|
conn->tempfamily[1] = conn->tempfamily[0] == AF_INET6 ?
|
||||||
if(!result)
|
AF_INET : AF_INET6;
|
||||||
break;
|
ainext(conn, 1, FALSE); /* assigns conn->tempaddr[1] accordingly */
|
||||||
conn->tempaddr[0] = conn->tempaddr[0]->ai_next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(conn->tempsock[0] == CURL_SOCKET_BAD) {
|
DEBUGF(infof(data, "family0 == %s, family1 == %s\n",
|
||||||
if(!result)
|
conn->tempfamily[0] == AF_INET ? "v4" : "v6",
|
||||||
result = CURLE_COULDNT_CONNECT;
|
conn->tempfamily[1] == AF_INET ? "v4" : "v6"));
|
||||||
return result;
|
|
||||||
|
/* get through the list in family order in case of quick failures */
|
||||||
|
for(i = 0; (i < 2) && result; i++) {
|
||||||
|
while(conn->tempaddr[i]) {
|
||||||
|
result = singleipconnect(conn, conn->tempaddr[i], i);
|
||||||
|
if(!result)
|
||||||
|
break;
|
||||||
|
ainext(conn, i, TRUE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if(result)
|
||||||
|
return result;
|
||||||
|
|
||||||
data->info.numconnects++; /* to track the number of connections made */
|
data->info.numconnects++; /* to track the number of connections made */
|
||||||
Curl_expire(conn->data, data->set.happy_eyeballs_timeout,
|
Curl_expire(conn->data, data->set.happy_eyeballs_timeout,
|
||||||
|
@ -957,6 +957,7 @@ struct connectdata {
|
|||||||
curl_socket_t sock[2]; /* two sockets, the second is used for the data
|
curl_socket_t sock[2]; /* two sockets, the second is used for the data
|
||||||
transfer when doing FTP */
|
transfer when doing FTP */
|
||||||
curl_socket_t tempsock[2]; /* temporary sockets for happy eyeballs */
|
curl_socket_t tempsock[2]; /* temporary sockets for happy eyeballs */
|
||||||
|
int tempfamily[2]; /* family used for the temp sockets */
|
||||||
Curl_recv *recv[2];
|
Curl_recv *recv[2];
|
||||||
Curl_send *send[2];
|
Curl_send *send[2];
|
||||||
|
|
||||||
@ -1113,6 +1114,8 @@ struct connectdata {
|
|||||||
handle */
|
handle */
|
||||||
BIT(sock_accepted); /* TRUE if the SECONDARYSOCKET was created with
|
BIT(sock_accepted); /* TRUE if the SECONDARYSOCKET was created with
|
||||||
accept() */
|
accept() */
|
||||||
|
BIT(parallel_connect); /* set TRUE when a parallel connect attempt has
|
||||||
|
started (happy eyeballs) */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* The end of connectdata. */
|
/* The end of connectdata. */
|
||||||
|
Loading…
Reference in New Issue
Block a user