Peter Sylvester found a flaw in the connect code for ipv6-enabled hosts.

I guess it seldomly happens on linux and that's why it wasn't found before.
He used Solaris to notice it.

I took the opportunity to rewrite the Curl_connecthost() slightly to feature
less duplicate code in the two different versions (ipv4/ipv6).
This commit is contained in:
Daniel Stenberg 2003-11-13 13:28:40 +00:00
parent b9d3c71178
commit 43bb20461f
2 changed files with 66 additions and 136 deletions

16
CHANGES
View File

@ -7,6 +7,22 @@
Changelog
Daniel (13 November)
- Default Content-Type for parts in multipart formposts has changed to
"application/octet-stream". This seems more appropriate, and I believe
mozilla and the likes do this. In the same area: .html files now get
text/html as Content-Type. (Pointed out in bug report #839806)
- Gisle Vanem corrected the --progress-bar output by doing a flush of the
output, which apparently makes it look better on at least windows, but
possibly other platforms too.
- Peter Sylvester identified a problem in the connect code, which made the
multi interface on a ipv6-enabled solaris box do bad. Test case 504 to be
specific. I've spent some time to clean-up the Curl_connecthost() function
now to use less duplicated code for the two different sections: ipv6 and
ipv4.
Daniel (11 November)
- Added CURLOPT_NETRC_FILE. Use this to tell libcurl which file to use instead
of trying to find a .netrc in the current user's home directory. The

View File

@ -473,6 +473,10 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */
struct timeval after;
struct timeval before = Curl_tvnow();
#ifdef ENABLE_IPV6
struct addrinfo *ai;
#endif
/*************************************************************
* Figure out what maximum time we have left
*************************************************************/
@ -513,118 +517,21 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */
}
hostname = data->change.proxy?conn->proxyhost:conn->hostname;
infof(data, "About to connect() to %s%s%s:%d\n",
conn->bits.ipv6_ip?"[":"",
hostname,
conn->bits.ipv6_ip?"]":"",
port);
infof(data, "About to connect() to %s port %d\n",
hostname, port);
#ifdef ENABLE_IPV6
/*
* Connecting with IPv6 support is so much easier and cleanly done
* Connecting with a getaddrinfo chain
*/
{
struct addrinfo *ai;
port =0; /* prevent compiler warning */
for (ai = remotehost->addr; ai; ai = ai->ai_next, aliasindex++) {
sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (sockfd < 0)
continue;
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) {
int 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
case EINTR:
/* asynchronous connect, wait for connect or timeout */
if(data->state.used_interface == Curl_if_multi)
/* don't hang when doing multi */
timeout_ms = 0;
rc = waitconnect(sockfd, timeout_ms);
break;
case ECONNREFUSED: /* no one listening */
default:
/* unknown error, fallthrough and try another address! */
failf(data, "Failed connect to %s: %d", hostname, error);
break;
}
}
if(0 == rc) {
/* we might be connected, if the socket says it is OK! Ask it! */
if(verifyconnect(sockfd)) {
/* we are connected, awesome! */
*connected = TRUE; /* this is truly a connect */
break;
}
failf(data, "socket error");
/* we are _not_ connected, it was a false alert, continue please */
}
else if(2 == rc)
/* waitconnect() returned error */
;
else if(data->state.used_interface == Curl_if_multi) {
/* When running the multi interface, we bail out here */
rc = 0;
break;
}
/* connect failed or timed out */
sclose(sockfd);
sockfd = -1;
/* get a new timeout for next attempt */
after = Curl_tvnow();
timeout_ms -= Curl_tvdiff(after, before);
if(timeout_ms < 0) {
failf(data, "connect() timed out!");
return CURLE_OPERATION_TIMEOUTED;
}
before = after;
continue;
}
for (ai = remotehost->addr; ai; ai = ai->ai_next, aliasindex++) {
sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (sockfd < 0)
return CURLE_COULDNT_CONNECT;
/* leave the socket in non-blocking mode */
if(addr)
*addr = ai; /* the address we ended up connected to */
}
continue;
#else
/*
* Connecting with IPv4-only support
* Connecting with old style IPv4-only support
*/
if(!remotehost->addr->h_addr_list[0]) {
/* If there is no addresses in the address list, then we return
error right away */
failf(data, "no address available");
return CURLE_COULDNT_CONNECT;
}
/* This is the loop that attempts to connect to all IP-addresses we
know for the given host. One by one. */
@ -639,7 +546,16 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */
failf(data, "couldn't create socket");
return CURLE_COULDNT_CONNECT; /* big time error */
}
/* 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],
sizeof(struct in_addr));
serv_addr.sin_family = remotehost->addr->h_addrtype;
serv_addr.sin_port = htons((unsigned short)port);
#endif
if(conn->data->set.device) {
/* user selected to bind the outgoing socket to a specified "device"
before doing connect */
@ -648,19 +564,16 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */
return res;
}
/* Convert socket to non-blocking type */
/* set socket non-blocking */
Curl_nonblock(sockfd, TRUE);
/* do this nasty work to do the connect */
memset((char *) &serv_addr, '\0', sizeof(serv_addr));
memcpy((char *)&(serv_addr.sin_addr),
(struct in_addr *)remotehost->addr->h_addr_list[aliasindex],
sizeof(struct in_addr));
serv_addr.sin_family = remotehost->addr->h_addrtype;
serv_addr.sin_port = htons((unsigned short)port);
rc = connect(sockfd, (struct sockaddr *)&serv_addr,
sizeof(serv_addr));
rc = connect(sockfd,
#ifdef ENABLE_IPV6
ai->ai_addr, ai->ai_addrlen
#else
(struct sockaddr *)&serv_addr, sizeof(serv_addr)
#endif
);
if(-1 == rc) {
int error=Curl_ourerrno();
@ -679,7 +592,7 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */
if(data->state.used_interface == Curl_if_multi)
/* don't hang when doing multi */
timeout_ms = 0;
rc = waitconnect(sockfd, timeout_ms);
break;
default:
@ -698,7 +611,7 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */
rc = 0;
break;
}
if(0 == rc) {
if (verifyconnect(sockfd)) {
/* we are connected, awesome! */
@ -709,22 +622,20 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */
rc = -1;
}
if(0 != rc) {
/* get a new timeout for next attempt */
sclose(sockfd);
after = Curl_tvnow();
timeout_ms -= Curl_tvdiff(after, before);
if(timeout_ms < 0) {
failf(data, "Connect timeout on IP number %d", aliasindex+1);
break;
}
before = after;
continue; /* try next address */
}
break;
}
/* connect failed or timed out */
sclose(sockfd);
sockfd = -1;
if(0 != rc) {
/* get a new timeout for next attempt */
after = Curl_tvnow();
timeout_ms -= Curl_tvdiff(after, before);
if(timeout_ms < 0) {
failf(data, "connect() timed out!");
return CURLE_OPERATION_TIMEOUTED;
}
before = after;
}
if (sockfd < 0) {
/* no good connect was made */
*sockconn = -1;
failf(data, "Connect failed");
@ -733,10 +644,14 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */
/* leave the socket in non-blocking mode */
if(addr)
/* this is the address we've connected to */
/* store the address we use */
if(addr) {
#ifdef ENABLE_IPV6
*addr = ai;
#else
*addr = (struct in_addr *)remotehost->addr->h_addr_list[aliasindex];
#endif
}
/* allow NULL-pointers to get passed in */
if(sockconn)
@ -744,4 +659,3 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */
return CURLE_OK;
}