mirror of
https://github.com/moparisthebest/curl
synced 2025-03-11 07:39:50 -04:00
First attempt at making the multi interface work when connecting to a host
that resolves to multiple IP addresses.
This commit is contained in:
parent
964066c0de
commit
6ed5feda2b
287
lib/connect.c
287
lib/connect.c
@ -101,8 +101,16 @@
|
|||||||
/* The last #include file should be: */
|
/* The last #include file should be: */
|
||||||
#include "memdebug.h"
|
#include "memdebug.h"
|
||||||
|
|
||||||
|
#define DEFAULT_CONNECT_TIMEOUT 300000 /* milliseconds == five minutes */
|
||||||
|
|
||||||
static bool verifyconnect(curl_socket_t sockfd, int *error);
|
static bool verifyconnect(curl_socket_t sockfd, int *error);
|
||||||
|
|
||||||
|
static curl_socket_t
|
||||||
|
singleipconnect(struct connectdata *conn,
|
||||||
|
Curl_addrinfo *ai, /* start connecting to this */
|
||||||
|
long timeout_ms,
|
||||||
|
bool *connected);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Curl_ourerrno() returns the errno (or equivalent) on this platform to
|
* Curl_ourerrno() returns the errno (or equivalent) on this platform to
|
||||||
* hide platform specific for the function that calls this.
|
* hide platform specific for the function that calls this.
|
||||||
@ -422,43 +430,74 @@ static bool verifyconnect(curl_socket_t sockfd, int *error)
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Used within the multi interface. Try next IP address, return TRUE if no
|
||||||
|
more address exists */
|
||||||
|
static bool trynextip(struct connectdata *conn,
|
||||||
|
int sockindex,
|
||||||
|
long timeout,
|
||||||
|
bool *connected)
|
||||||
|
{
|
||||||
|
curl_socket_t sockfd;
|
||||||
|
Curl_addrinfo *ai;
|
||||||
|
|
||||||
|
if(sockindex != FIRSTSOCKET)
|
||||||
|
return TRUE; /* no next */
|
||||||
|
|
||||||
|
ai = conn->ip_addr->ai_next;
|
||||||
|
|
||||||
|
while (ai) {
|
||||||
|
sockfd = singleipconnect(conn, ai, timeout, connected);
|
||||||
|
if(sockfd != CURL_SOCKET_BAD) {
|
||||||
|
/* store the new socket descriptor */
|
||||||
|
conn->sock[sockindex] = sockfd;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
ai = ai->ai_next;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Curl_is_connected() is used from the multi interface to check if the
|
* Curl_is_connected() is used from the multi interface to check if the
|
||||||
* firstsocket has connected.
|
* firstsocket has connected.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
CURLcode Curl_is_connected(struct connectdata *conn,
|
CURLcode Curl_is_connected(struct connectdata *conn,
|
||||||
curl_socket_t sockfd,
|
int sockindex,
|
||||||
bool *connected)
|
bool *connected)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
struct SessionHandle *data = conn->data;
|
struct SessionHandle *data = conn->data;
|
||||||
|
CURLcode code = CURLE_OK;
|
||||||
|
curl_socket_t sockfd = conn->sock[sockindex];
|
||||||
|
long allow = DEFAULT_CONNECT_TIMEOUT;
|
||||||
|
long has_passed;
|
||||||
|
|
||||||
|
curlassert(sockindex >= FIRSTSOCKET && sockindex <= SECONDARYSOCKET);
|
||||||
|
|
||||||
*connected = FALSE; /* a very negative world view is best */
|
*connected = FALSE; /* a very negative world view is best */
|
||||||
|
|
||||||
if(data->set.timeout || data->set.connecttimeout) {
|
/* Evaluate in milliseconds how much time that has passed */
|
||||||
/* there is a timeout set */
|
has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.start);
|
||||||
|
|
||||||
/* Evaluate in milliseconds how much time that has passed */
|
/* subtract the most strict timeout of the ones */
|
||||||
long has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.start);
|
if(data->set.timeout && data->set.connecttimeout) {
|
||||||
|
if (data->set.timeout < data->set.connecttimeout)
|
||||||
/* subtract the most strict timeout of the ones */
|
allow = data->set.timeout*1000;
|
||||||
if(data->set.timeout && data->set.connecttimeout) {
|
|
||||||
if (data->set.timeout < data->set.connecttimeout)
|
|
||||||
has_passed -= data->set.timeout*1000;
|
|
||||||
else
|
|
||||||
has_passed -= data->set.connecttimeout*1000;
|
|
||||||
}
|
|
||||||
else if(data->set.timeout)
|
|
||||||
has_passed -= data->set.timeout*1000;
|
|
||||||
else
|
else
|
||||||
has_passed -= data->set.connecttimeout*1000;
|
allow = data->set.connecttimeout*1000;
|
||||||
|
}
|
||||||
|
else if(data->set.timeout) {
|
||||||
|
allow = data->set.timeout*1000;
|
||||||
|
}
|
||||||
|
else if(data->set.connecttimeout) {
|
||||||
|
allow = data->set.connecttimeout*1000;
|
||||||
|
}
|
||||||
|
|
||||||
if(has_passed > 0 ) {
|
if(has_passed > allow ) {
|
||||||
/* time-out, bail out, go home */
|
/* time-out, bail out, go home */
|
||||||
failf(data, "Connection time-out");
|
failf(data, "Connection time-out after %ld ms", has_passed);
|
||||||
return CURLE_OPERATION_TIMEOUTED;
|
return CURLE_OPERATION_TIMEOUTED;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if(conn->bits.tcpconnect) {
|
if(conn->bits.tcpconnect) {
|
||||||
/* we are connected already! */
|
/* we are connected already! */
|
||||||
@ -476,21 +515,27 @@ CURLcode Curl_is_connected(struct connectdata *conn,
|
|||||||
return CURLE_OK;
|
return CURLE_OK;
|
||||||
}
|
}
|
||||||
/* nope, not connected for real */
|
/* nope, not connected for real */
|
||||||
failf(data, "Connection failed");
|
infof(data, "Connection failed\n");
|
||||||
return CURLE_COULDNT_CONNECT;
|
if(trynextip(conn, sockindex, allow-has_passed, connected)) {
|
||||||
|
code = CURLE_COULDNT_CONNECT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if(WAITCONN_TIMEOUT != rc) {
|
else if(WAITCONN_TIMEOUT != rc) {
|
||||||
int error = Curl_ourerrno();
|
/* nope, not connected */
|
||||||
failf(data, "Failed connect to %s:%d; %s",
|
infof(data, "Connection failed\n");
|
||||||
conn->host.name, conn->port, Curl_strerror(conn,error));
|
if(trynextip(conn, sockindex, allow-has_passed, connected)) {
|
||||||
return CURLE_COULDNT_CONNECT;
|
int error = Curl_ourerrno();
|
||||||
|
failf(data, "Failed connect to %s:%d; %s",
|
||||||
|
conn->host.name, conn->port, Curl_strerror(conn,error));
|
||||||
|
code = CURLE_COULDNT_CONNECT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* If the connection failed here, we should attempt to connect to the "next
|
* If the connection failed here, we should attempt to connect to the "next
|
||||||
* address" for the given host.
|
* address" for the given host.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
return CURLE_OK;
|
return code;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tcpnodelay(struct connectdata *conn,
|
static void tcpnodelay(struct connectdata *conn,
|
||||||
@ -511,6 +556,95 @@ static void tcpnodelay(struct connectdata *conn,
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* singleipconnect() connects to the given IP only, and it may return without
|
||||||
|
having connected if used from the multi interface. */
|
||||||
|
static curl_socket_t
|
||||||
|
singleipconnect(struct connectdata *conn,
|
||||||
|
Curl_addrinfo *ai,
|
||||||
|
long timeout_ms,
|
||||||
|
bool *connected)
|
||||||
|
{
|
||||||
|
char addr_buf[128];
|
||||||
|
int rc;
|
||||||
|
int error;
|
||||||
|
bool conected;
|
||||||
|
struct SessionHandle *data = conn->data;
|
||||||
|
curl_socket_t sockfd = socket(ai->ai_family, ai->ai_socktype,
|
||||||
|
ai->ai_protocol);
|
||||||
|
if (sockfd == CURL_SOCKET_BAD)
|
||||||
|
return CURL_SOCKET_BAD;
|
||||||
|
|
||||||
|
*connected = FALSE; /* default is not connected */
|
||||||
|
|
||||||
|
Curl_printable_address(ai, addr_buf, sizeof(addr_buf));
|
||||||
|
infof(data, " Trying %s... ", addr_buf);
|
||||||
|
|
||||||
|
if(data->set.tcp_nodelay)
|
||||||
|
tcpnodelay(conn, sockfd);
|
||||||
|
|
||||||
|
if(conn->data->set.device) {
|
||||||
|
/* user selected to bind the outgoing socket to a specified "device"
|
||||||
|
before doing connect */
|
||||||
|
CURLcode res = bindlocal(conn, sockfd);
|
||||||
|
if(res)
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set socket non-blocking */
|
||||||
|
Curl_nonblock(sockfd, TRUE);
|
||||||
|
|
||||||
|
rc = connect(sockfd, ai->ai_addr, ai->ai_addrlen);
|
||||||
|
|
||||||
|
if(-1 == rc) {
|
||||||
|
error = Curl_ourerrno();
|
||||||
|
|
||||||
|
switch (error) {
|
||||||
|
case EINPROGRESS:
|
||||||
|
case EWOULDBLOCK:
|
||||||
|
#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
|
||||||
|
/* On some platforms EAGAIN and EWOULDBLOCK are the
|
||||||
|
* same value, and on others they are different, hence
|
||||||
|
* the odd #if
|
||||||
|
*/
|
||||||
|
case EAGAIN:
|
||||||
|
#endif
|
||||||
|
rc = waitconnect(sockfd, timeout_ms);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* unknown error, fallthrough and try another address! */
|
||||||
|
failf(data, "Failed to connect to %s: %s",
|
||||||
|
addr_buf, Curl_strerror(conn,error));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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 */
|
||||||
|
return sockfd;
|
||||||
|
}
|
||||||
|
|
||||||
|
conected = verifyconnect(sockfd, &error);
|
||||||
|
|
||||||
|
if(!rc && conected) {
|
||||||
|
/* we are connected, awesome! */
|
||||||
|
*connected = TRUE; /* this is a true connect */
|
||||||
|
infof(data, "connected\n");
|
||||||
|
return sockfd;
|
||||||
|
}
|
||||||
|
else if(WAITCONN_TIMEOUT == rc)
|
||||||
|
infof(data, "Timeout\n");
|
||||||
|
else
|
||||||
|
infof(data, "%s\n", Curl_strerror(conn, error));
|
||||||
|
|
||||||
|
/* connect failed or timed out */
|
||||||
|
sclose(sockfd);
|
||||||
|
|
||||||
|
return CURL_SOCKET_BAD;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TCP connect to the given host with timeout, proxy or remote doesn't matter.
|
* TCP connect to the given host with timeout, proxy or remote doesn't matter.
|
||||||
* There might be more than one IP address to try out. Fill in the passed
|
* There might be more than one IP address to try out. Fill in the passed
|
||||||
@ -525,11 +659,8 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */
|
|||||||
{
|
{
|
||||||
struct SessionHandle *data = conn->data;
|
struct SessionHandle *data = conn->data;
|
||||||
curl_socket_t sockfd = CURL_SOCKET_BAD;
|
curl_socket_t sockfd = CURL_SOCKET_BAD;
|
||||||
int rc, error;
|
|
||||||
int aliasindex;
|
int aliasindex;
|
||||||
int num_addr;
|
int num_addr;
|
||||||
bool conected;
|
|
||||||
char addr_buf[256];
|
|
||||||
Curl_addrinfo *ai;
|
Curl_addrinfo *ai;
|
||||||
Curl_addrinfo *curr_addr;
|
Curl_addrinfo *curr_addr;
|
||||||
|
|
||||||
@ -539,7 +670,7 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */
|
|||||||
/*************************************************************
|
/*************************************************************
|
||||||
* 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 total */
|
long timeout_ms= DEFAULT_CONNECT_TIMEOUT;
|
||||||
long timeout_per_addr;
|
long timeout_per_addr;
|
||||||
|
|
||||||
*connected = FALSE; /* default to not connected */
|
*connected = FALSE; /* default to not connected */
|
||||||
@ -583,98 +714,24 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */
|
|||||||
ai = remotehost->addr;
|
ai = remotehost->addr;
|
||||||
|
|
||||||
/* Below is the loop that attempts to connect to all IP-addresses we
|
/* 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.
|
* know for the given host. One by one until one IP succeeds.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
if(data->state.used_interface == Curl_if_multi)
|
||||||
|
/* don't hang when doing multi */
|
||||||
|
timeout_per_addr = timeout_ms = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Connecting with a getaddrinfo chain
|
* Connecting with a Curl_addrinfo chain
|
||||||
*/
|
*/
|
||||||
for (curr_addr = ai, aliasindex=0; curr_addr;
|
for (curr_addr = ai, aliasindex=0; curr_addr;
|
||||||
curr_addr = curr_addr->ai_next, aliasindex++) {
|
curr_addr = curr_addr->ai_next, aliasindex++) {
|
||||||
|
|
||||||
sockfd = socket(curr_addr->ai_family, curr_addr->ai_socktype,
|
/* start connecting to the IP curr_addr points to */
|
||||||
curr_addr->ai_protocol);
|
sockfd = singleipconnect(conn, curr_addr, timeout_per_addr, connected);
|
||||||
if (sockfd == CURL_SOCKET_BAD) {
|
|
||||||
timeout_per_addr += timeout_per_addr / (num_addr - aliasindex);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Curl_printable_address(curr_addr, addr_buf, sizeof(addr_buf));
|
if(sockfd != CURL_SOCKET_BAD)
|
||||||
infof(data, " Trying %s... ", addr_buf);
|
|
||||||
|
|
||||||
if(data->set.tcp_nodelay)
|
|
||||||
tcpnodelay(conn, sockfd);
|
|
||||||
|
|
||||||
if(conn->data->set.device) {
|
|
||||||
/* user selected to bind the outgoing socket to a specified "device"
|
|
||||||
before doing connect */
|
|
||||||
CURLcode res = bindlocal(conn, sockfd);
|
|
||||||
if(res)
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* set socket non-blocking */
|
|
||||||
Curl_nonblock(sockfd, TRUE);
|
|
||||||
|
|
||||||
/* do not use #ifdef within the function arguments below, as connect() is
|
|
||||||
a defined macro on some platforms and some compilers don't like to mix
|
|
||||||
#ifdefs with macro usage! (AmigaOS is one such platform) */
|
|
||||||
|
|
||||||
rc = connect(sockfd, curr_addr->ai_addr, curr_addr->ai_addrlen);
|
|
||||||
|
|
||||||
if(-1 == rc) {
|
|
||||||
error = Curl_ourerrno();
|
|
||||||
|
|
||||||
switch (error) {
|
|
||||||
case EINPROGRESS:
|
|
||||||
case EWOULDBLOCK:
|
|
||||||
#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
|
|
||||||
/* On some platforms EAGAIN and EWOULDBLOCK are the
|
|
||||||
* same value, and on others they are different, hence
|
|
||||||
* the odd #if
|
|
||||||
*/
|
|
||||||
case EAGAIN:
|
|
||||||
#endif
|
|
||||||
/* asynchronous connect, wait for connect or timeout */
|
|
||||||
if(data->state.used_interface == Curl_if_multi)
|
|
||||||
/* don't hang when doing multi */
|
|
||||||
timeout_per_addr = timeout_ms = 0;
|
|
||||||
|
|
||||||
rc = waitconnect(sockfd, timeout_per_addr);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/* unknown error, fallthrough and try another address! */
|
|
||||||
failf(data, "Failed to connect to %s (IP number %d): %s",
|
|
||||||
addr_buf, aliasindex+1, Curl_strerror(conn,error));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
infof(data, "%s\n", Curl_strerror(conn, error));
|
|
||||||
|
|
||||||
/* connect failed or timed out */
|
|
||||||
sclose(sockfd);
|
|
||||||
sockfd = CURL_SOCKET_BAD;
|
|
||||||
|
|
||||||
/* get a new timeout for next attempt */
|
/* get a new timeout for next attempt */
|
||||||
after = Curl_tvnow();
|
after = Curl_tvnow();
|
||||||
|
11
lib/multi.c
11
lib/multi.c
@ -320,7 +320,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
|
|||||||
|
|
||||||
easy=multi->easy.next;
|
easy=multi->easy.next;
|
||||||
while(easy) {
|
while(easy) {
|
||||||
#ifdef CURLDEBUG
|
#if 0
|
||||||
fprintf(stderr, "HANDLE %p: State: %x\n",
|
fprintf(stderr, "HANDLE %p: State: %x\n",
|
||||||
(char *)easy, easy->state);
|
(char *)easy, easy->state);
|
||||||
#endif
|
#endif
|
||||||
@ -416,8 +416,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
|
|||||||
|
|
||||||
case CURLM_STATE_WAITCONNECT:
|
case CURLM_STATE_WAITCONNECT:
|
||||||
/* awaiting a completion of an asynch connect */
|
/* awaiting a completion of an asynch connect */
|
||||||
easy->result = Curl_is_connected(easy->easy_conn,
|
easy->result = Curl_is_connected(easy->easy_conn, FIRSTSOCKET,
|
||||||
easy->easy_conn->sock[FIRSTSOCKET],
|
|
||||||
&connected);
|
&connected);
|
||||||
if(connected)
|
if(connected)
|
||||||
easy->result = Curl_protocol_connect(easy->easy_conn);
|
easy->result = Curl_protocol_connect(easy->easy_conn);
|
||||||
@ -463,10 +462,8 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
|
|||||||
/*
|
/*
|
||||||
* First, check if we really are ready to do more.
|
* First, check if we really are ready to do more.
|
||||||
*/
|
*/
|
||||||
easy->result =
|
easy->result = Curl_is_connected(easy->easy_conn, SECONDARYSOCKET,
|
||||||
Curl_is_connected(easy->easy_conn,
|
&connected);
|
||||||
easy->easy_conn->sock[SECONDARYSOCKET],
|
|
||||||
&connected);
|
|
||||||
if(connected) {
|
if(connected) {
|
||||||
/*
|
/*
|
||||||
* When we are connected, DO MORE and then go PERFORM
|
* When we are connected, DO MORE and then go PERFORM
|
||||||
|
Loading…
x
Reference in New Issue
Block a user