aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Stenberg <daniel@haxx.se>2004-06-29 11:20:07 +0000
committerDaniel Stenberg <daniel@haxx.se>2004-06-29 11:20:07 +0000
commit6ed5feda2b3fd15c720e04c040e470ecc3cd075c (patch)
treef890bc513e3b3eca3d7c78057f249842464364ff
parent964066c0deec87e97771530a63c36d1aa3313eb8 (diff)
First attempt at making the multi interface work when connecting to a host
that resolves to multiple IP addresses.
-rw-r--r--lib/connect.c287
-rw-r--r--lib/multi.c11
2 files changed, 176 insertions, 122 deletions
diff --git a/lib/connect.c b/lib/connect.c
index 58506751b..a30614334 100644
--- a/lib/connect.c
+++ b/lib/connect.c
@@ -101,8 +101,16 @@
/* The last #include file should be: */
#include "memdebug.h"
+#define DEFAULT_CONNECT_TIMEOUT 300000 /* milliseconds == five minutes */
+
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
* hide platform specific for the function that calls this.
@@ -422,43 +430,74 @@ static bool verifyconnect(curl_socket_t sockfd, int *error)
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
* firstsocket has connected.
*/
CURLcode Curl_is_connected(struct connectdata *conn,
- curl_socket_t sockfd,
+ int sockindex,
bool *connected)
{
int rc;
struct SessionHandle *data = conn->data;
+ CURLcode code = CURLE_OK;
+ curl_socket_t sockfd = conn->sock[sockindex];
+ long allow = DEFAULT_CONNECT_TIMEOUT;
+ long has_passed;
- *connected = FALSE; /* a very negative world view is best */
+ curlassert(sockindex >= FIRSTSOCKET && sockindex <= SECONDARYSOCKET);
- if(data->set.timeout || data->set.connecttimeout) {
- /* there is a timeout set */
+ *connected = FALSE; /* a very negative world view is best */
- /* Evaluate in milliseconds how much time that has passed */
- long has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.start);
+ /* Evaluate in milliseconds how much time that has passed */
+ has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.start);
- /* subtract the most strict timeout of the ones */
- 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;
+ /* subtract the most strict timeout of the ones */
+ if(data->set.timeout && data->set.connecttimeout) {
+ if (data->set.timeout < data->set.connecttimeout)
+ allow = data->set.timeout*1000;
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 ) {
- /* time-out, bail out, go home */
- failf(data, "Connection time-out");
- return CURLE_OPERATION_TIMEOUTED;
- }
+ if(has_passed > allow ) {
+ /* time-out, bail out, go home */
+ failf(data, "Connection time-out after %ld ms", has_passed);
+ return CURLE_OPERATION_TIMEOUTED;
}
if(conn->bits.tcpconnect) {
/* we are connected already! */
@@ -476,21 +515,27 @@ CURLcode Curl_is_connected(struct connectdata *conn,
return CURLE_OK;
}
/* nope, not connected for real */
- failf(data, "Connection failed");
- return CURLE_COULDNT_CONNECT;
+ infof(data, "Connection failed\n");
+ if(trynextip(conn, sockindex, allow-has_passed, connected)) {
+ code = CURLE_COULDNT_CONNECT;
+ }
}
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));
- return CURLE_COULDNT_CONNECT;
+ /* nope, not connected */
+ infof(data, "Connection failed\n");
+ if(trynextip(conn, sockindex, allow-has_passed, connected)) {
+ 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
* address" for the given host.
*/
- return CURLE_OK;
+ return code;
}
static void tcpnodelay(struct connectdata *conn,
@@ -511,6 +556,95 @@ static void tcpnodelay(struct connectdata *conn,
#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.
* 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;
curl_socket_t sockfd = CURL_SOCKET_BAD;
- int rc, error;
int aliasindex;
int num_addr;
- bool conected;
- char addr_buf[256];
Curl_addrinfo *ai;
Curl_addrinfo *curr_addr;
@@ -539,7 +670,7 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */
/*************************************************************
* 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;
*connected = FALSE; /* default to not connected */
@@ -583,98 +714,24 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */
ai = remotehost->addr;
/* 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;
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;
- }
-
- Curl_printable_address(curr_addr, 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);
+ /* start connecting to the IP curr_addr points to */
+ sockfd = singleipconnect(conn, curr_addr, timeout_per_addr, connected);
- /* 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;
- }
-
- conected = verifyconnect(sockfd, &error);
-
- if(!rc && conected) {
- /* we are connected, awesome! */
- *connected = TRUE; /* this is a true connect */
+ if(sockfd != CURL_SOCKET_BAD)
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 */
after = Curl_tvnow();
diff --git a/lib/multi.c b/lib/multi.c
index 8d1547d17..12b987ff0 100644
--- a/lib/multi.c
+++ b/lib/multi.c
@@ -320,7 +320,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
easy=multi->easy.next;
while(easy) {
-#ifdef CURLDEBUG
+#if 0
fprintf(stderr, "HANDLE %p: State: %x\n",
(char *)easy, easy->state);
#endif
@@ -416,8 +416,7 @@ CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
case CURLM_STATE_WAITCONNECT:
/* awaiting a completion of an asynch connect */
- easy->result = Curl_is_connected(easy->easy_conn,
- easy->easy_conn->sock[FIRSTSOCKET],
+ easy->result = Curl_is_connected(easy->easy_conn, FIRSTSOCKET,
&connected);
if(connected)
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.
*/
- easy->result =
- Curl_is_connected(easy->easy_conn,
- easy->easy_conn->sock[SECONDARYSOCKET],
- &connected);
+ easy->result = Curl_is_connected(easy->easy_conn, SECONDARYSOCKET,
+ &connected);
if(connected) {
/*
* When we are connected, DO MORE and then go PERFORM