diff options
author | Lucas Eckels <eckels@google.com> | 2012-08-06 15:07:02 -0700 |
---|---|---|
committer | Lucas Eckels <eckels@google.com> | 2012-08-08 09:28:48 -0700 |
commit | 9bd90e6e25f1e55f50201c87a1b5837de7e5b64a (patch) | |
tree | d2061a00d7d0ee884170bc955fceeed2d0edf284 /lib/connect.c | |
parent | e6f2b03027b5feb92b30f5d47801ec3fabe9fd95 (diff) | |
download | android_external_curl-9bd90e6e25f1e55f50201c87a1b5837de7e5b64a.tar.gz android_external_curl-9bd90e6e25f1e55f50201c87a1b5837de7e5b64a.tar.bz2 android_external_curl-9bd90e6e25f1e55f50201c87a1b5837de7e5b64a.zip |
Add Music Manager's curl 7.21.2 source.
Change-Id: I259a43fa52d581524a5ce8ae1711467acb1d9d50
Diffstat (limited to 'lib/connect.c')
-rw-r--r-- | lib/connect.c | 245 |
1 files changed, 176 insertions, 69 deletions
diff --git a/lib/connect.c b/lib/connect.c index eb6df71..e178633 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -112,10 +112,11 @@ struct Curl_sockaddr_ex { static bool verifyconnect(curl_socket_t sockfd, int *error); -static curl_socket_t +static CURLcode singleipconnect(struct connectdata *conn, const Curl_addrinfo *ai, /* start connecting to this */ long timeout_ms, + curl_socket_t *sock, bool *connected); /* @@ -173,6 +174,9 @@ long Curl_timeleft(struct connectdata *conn, /* substract elapsed time */ timeout_ms -= Curl_tvdiff(*nowp, data->progress.t_startsingle); + if(!timeout_ms) + /* avoid returning 0 as that means no timeout! */ + return -1; return timeout_ms; } @@ -180,16 +184,13 @@ long Curl_timeleft(struct connectdata *conn, /* * waitconnect() waits for a TCP connect on the given socket for the specified * number if milliseconds. It returns: - * 0 fine connect - * -1 select() error - * 1 select() timeout - * 2 select() returned with an error condition fd_set */ #define WAITCONN_CONNECTED 0 #define WAITCONN_SELECT_ERROR -1 #define WAITCONN_TIMEOUT 1 #define WAITCONN_FDSET_ERROR 2 +#define WAITCONN_ABORTED 3 static int waitconnect(struct connectdata *conn, @@ -209,9 +210,8 @@ int waitconnect(struct connectdata *conn, /* now select() until we get connect or timeout */ rc = Curl_socket_ready(CURL_SOCKET_BAD, sockfd, (int)(timeout_msec>1000? 1000:timeout_msec)); - if(Curl_pgrsUpdate(conn)) - return CURLE_ABORTED_BY_CALLBACK; + return WAITCONN_ABORTED; if(-1 == rc) /* error, no connect here, try next */ @@ -492,37 +492,130 @@ static bool verifyconnect(curl_socket_t sockfd, int *error) /* Used within the multi interface. Try next IP address, return TRUE if no more address exists or error */ -static bool trynextip(struct connectdata *conn, - int sockindex, - bool *connected) +static CURLcode trynextip(struct connectdata *conn, + int sockindex, + bool *connected) { curl_socket_t sockfd; Curl_addrinfo *ai; - /* first close the failed socket */ - sclose(conn->sock[sockindex]); + /* First clean up after the failed socket. + Don't close it yet to ensure that the next IP's socket gets a different + file descriptor, which can prevent bugs when the curl_multi_socket_action + interface is used with certain select() replacements such as kqueue. */ + curl_socket_t fd_to_close = conn->sock[sockindex]; conn->sock[sockindex] = CURL_SOCKET_BAD; *connected = FALSE; - if(sockindex != FIRSTSOCKET) - return TRUE; /* no next */ + if(sockindex != FIRSTSOCKET) { + sclose(fd_to_close); + return CURLE_COULDNT_CONNECT; /* no next */ + } /* try the next address */ ai = conn->ip_addr->ai_next; while(ai) { - sockfd = singleipconnect(conn, ai, 0L, connected); + CURLcode res = singleipconnect(conn, ai, 0L, &sockfd, connected); + if(res) + return res; if(sockfd != CURL_SOCKET_BAD) { /* store the new socket descriptor */ conn->sock[sockindex] = sockfd; conn->ip_addr = ai; - return FALSE; + sclose(fd_to_close); + return CURLE_OK; } ai = ai->ai_next; } + sclose(fd_to_close); + return CURLE_COULDNT_CONNECT; +} + +/* retrieves ip address and port from a sockaddr structure */ +static bool getaddressinfo(struct sockaddr* sa, char* addr, + long* port) +{ + unsigned short us_port; + struct sockaddr_in* si = NULL; +#ifdef ENABLE_IPV6 + struct sockaddr_in6* si6 = NULL; +#endif + + switch (sa->sa_family) { + case AF_INET: + si = (struct sockaddr_in*) sa; + if(Curl_inet_ntop(sa->sa_family, &si->sin_addr, + addr, MAX_IPADR_LEN) == NULL) + return FALSE; + us_port = ntohs(si->sin_port); + *port = us_port; + break; +#ifdef ENABLE_IPV6 + case AF_INET6: + si6 = (struct sockaddr_in6*)sa; + if(Curl_inet_ntop(sa->sa_family, &si6->sin6_addr, + addr, MAX_IPADR_LEN) == NULL) + return FALSE; + us_port = ntohs(si6->sin6_port); + *port = us_port; + break; +#endif + default: + addr[0] = '\0'; + *port = 0; + } return TRUE; } +/* retrieves the start/end point information of a socket of an established + connection */ +void Curl_updateconninfo(struct connectdata *conn, curl_socket_t sockfd) +{ + int error; + curl_socklen_t len; + struct Curl_sockaddr_storage ssrem; + struct Curl_sockaddr_storage ssloc; + struct SessionHandle *data = conn->data; + struct PureInfo *info = &conn->data->info; + + if(conn->bits.reuse) + /* reusing same connection */ + return; + + len = sizeof(struct Curl_sockaddr_storage); + if(getpeername(sockfd, (struct sockaddr*) &ssrem, &len)) { + error = SOCKERRNO; + failf(data, "getpeername() failed with errno %d: %s", + error, Curl_strerror(conn, error)); + return; + } + + len = sizeof(struct Curl_sockaddr_storage); + if(getsockname(sockfd, (struct sockaddr*) &ssloc, &len)) { + error = SOCKERRNO; + failf(data, "getsockname() failed with errno %d: %s", + error, Curl_strerror(conn, error)); + return; + } + + if(!getaddressinfo((struct sockaddr*)&ssrem, + info->ip, &info->port)) { + error = ERRNO; + failf(data, "ssrem inet_ntop() failed with errno %d: %s", + error, Curl_strerror(conn, error)); + return; + } + + if(!getaddressinfo((struct sockaddr*)&ssloc, + info->localip, &info->localport)) { + error = ERRNO; + failf(data, "ssloc inet_ntop() failed with errno %d: %s", + error, Curl_strerror(conn, error)); + return; + } +} + /* * Curl_is_connected() is used from the multi interface to check if the * firstsocket has connected. @@ -537,6 +630,7 @@ CURLcode Curl_is_connected(struct connectdata *conn, CURLcode code = CURLE_OK; curl_socket_t sockfd = conn->sock[sockindex]; long allow = DEFAULT_CONNECT_TIMEOUT; + int error = 0; DEBUGASSERT(sockindex >= FIRSTSOCKET && sockindex <= SECONDARYSOCKET); @@ -544,13 +638,6 @@ CURLcode Curl_is_connected(struct connectdata *conn, if(conn->bits.tcpconnect) { /* we are connected already! */ - long allow_total = 0; - - /* subtract the most strict timeout of the ones */ - if(data->set.timeout) - allow_total = data->set.timeout; - - Curl_expire(data, allow_total); *connected = TRUE; return CURLE_OK; } @@ -564,52 +651,52 @@ CURLcode Curl_is_connected(struct connectdata *conn, return CURLE_OPERATION_TIMEDOUT; } - Curl_expire(data, allow); - /* check for connect without timeout as we want to return immediately */ rc = waitconnect(conn, sockfd, 0); + if(WAITCONN_TIMEOUT == rc) + /* not an error, but also no connection yet */ + return code; if(WAITCONN_CONNECTED == rc) { - int error; if(verifyconnect(sockfd, &error)) { /* we are connected, awesome! */ conn->bits.tcpconnect = TRUE; *connected = TRUE; + Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */ + Curl_verboseconnect(conn); + Curl_updateconninfo(conn, sockfd); + return CURLE_OK; } /* nope, not connected for real */ - data->state.os_errno = error; - infof(data, "Connection failed\n"); - if(trynextip(conn, sockindex, connected)) { - failf(data, "Failed connect to %s:%ld; %s", - conn->host.name, conn->port, Curl_strerror(conn, error)); - code = CURLE_COULDNT_CONNECT; - } } - else if(WAITCONN_TIMEOUT != rc) { - int error = 0; - + else { /* nope, not connected */ if(WAITCONN_FDSET_ERROR == rc) { (void)verifyconnect(sockfd, &error); - data->state.os_errno = error; - infof(data, "%s\n",Curl_strerror(conn,error)); + infof(data, "%s\n",Curl_strerror(conn, error)); } else infof(data, "Connection failed\n"); - - if(trynextip(conn, sockindex, connected)) { - error = SOCKERRNO; - data->state.os_errno = error; - failf(data, "Failed connect to %s:%ld; %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. + * The connection failed here, we should attempt to connect to the "next + * address" for the given host. But first remember the latest error. */ + if(error) { + data->state.os_errno = error; + SET_SOCKERRNO(error); + } + + code = trynextip(conn, sockindex, connected); + + if(code) { + error = SOCKERRNO; + data->state.os_errno = error; + failf(data, "Failed connect to %s:%ld; %s", + conn->host.name, conn->port, Curl_strerror(conn, error)); + } return code; } @@ -691,12 +778,20 @@ void Curl_sndbufset(curl_socket_t sockfd) #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() + * + * Note that even on connect fail it returns CURLE_OK, but with 'sock' set to + * CURL_SOCKET_BAD. Other errors will however return proper errors. + * + * singleipconnect() connects to the given IP only, and it may return without + * having connected if used from the multi interface. + */ +static CURLcode singleipconnect(struct connectdata *conn, const Curl_addrinfo *ai, long timeout_ms, + curl_socket_t *sockp, bool *connected) { struct Curl_sockaddr_ex addr; @@ -706,13 +801,15 @@ singleipconnect(struct connectdata *conn, bool isconnected; struct SessionHandle *data = conn->data; curl_socket_t sockfd; - CURLcode res; + CURLcode res = CURLE_OK; const void *iptoprint; struct sockaddr_in * const sa4 = (void *)&addr.sa_addr; #ifdef ENABLE_IPV6 struct sockaddr_in6 * const sa6 = (void *)&addr.sa_addr; #endif + *sockp = CURL_SOCKET_BAD; + /* * The Curl_sockaddr_ex structure is basically libcurl's external API * curl_sockaddr structure with enough space available to directly hold @@ -751,7 +848,7 @@ singleipconnect(struct connectdata *conn, if(sockfd == CURL_SOCKET_BAD) /* no socket, no connection */ - return CURL_SOCKET_BAD; + return CURLE_OK; #if defined(ENABLE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) if (conn->scope && (addr.family == AF_INET6)) @@ -804,7 +901,7 @@ singleipconnect(struct connectdata *conn, CURLSOCKTYPE_IPCXN); if(error) { sclose(sockfd); /* close the socket and bail out */ - return CURL_SOCKET_BAD; + return res; } } @@ -812,7 +909,7 @@ singleipconnect(struct connectdata *conn, res = bindlocal(conn, sockfd, addr.family); if(res) { sclose(sockfd); /* close socket and bail out */ - return CURL_SOCKET_BAD; + return res; } /* set socket non-blocking */ @@ -840,6 +937,10 @@ singleipconnect(struct connectdata *conn, #endif #endif rc = waitconnect(conn, sockfd, timeout_ms); + if(WAITCONN_ABORTED == rc) { + sclose(sockfd); + return CURLE_ABORTED_BY_CALLBACK; + } break; default: /* unknown error, fallthrough and try another address! */ @@ -855,7 +956,8 @@ singleipconnect(struct connectdata *conn, if((WAITCONN_TIMEOUT == rc) && (data->state.used_interface == Curl_if_multi)) { /* Timeout when running the multi interface */ - return sockfd; + *sockp = sockfd; + return CURLE_OK; } isconnected = verifyconnect(sockfd, &error); @@ -864,7 +966,9 @@ singleipconnect(struct connectdata *conn, /* we are connected, awesome! */ *connected = TRUE; /* this is a true connect */ infof(data, "connected\n"); - return sockfd; + Curl_updateconninfo(conn, sockfd); + *sockp = sockfd; + return CURLE_OK; } else if(WAITCONN_TIMEOUT == rc) infof(data, "Timeout\n"); @@ -876,7 +980,7 @@ singleipconnect(struct connectdata *conn, /* connect failed or timed out */ sclose(sockfd); - return CURL_SOCKET_BAD; + return CURLE_OK; } /* @@ -918,7 +1022,6 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ failf(data, "Connection time-out"); return CURLE_OPERATION_TIMEDOUT; } - Curl_expire(data, timeout_ms); /* Max time for each address */ num_addr = Curl_num_addresses(remotehost->addr); @@ -941,7 +1044,11 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ curr_addr = curr_addr->ai_next, aliasindex++) { /* start connecting to the IP curr_addr points to */ - sockfd = singleipconnect(conn, curr_addr, timeout_per_addr, connected); + CURLcode res = + singleipconnect(conn, curr_addr, timeout_per_addr, &sockfd, connected); + + if(res) + return res; if(sockfd != CURL_SOCKET_BAD) break; @@ -979,12 +1086,12 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */ * Used to extract socket and connectdata struct for the most recent * transfer on the given SessionHandle. * - * The socket 'long' will be -1 in case of failure! + * The returned socket will be CURL_SOCKET_BAD in case of failure! */ -CURLcode Curl_getconnectinfo(struct SessionHandle *data, - long *param_longp, - struct connectdata **connp) +curl_socket_t Curl_getconnectinfo(struct SessionHandle *data, + struct connectdata **connp) { + curl_socket_t sockfd; if((data->state.lastconnect != -1) && (data->state.connc->connects[data->state.lastconnect] != NULL)) { struct connectdata *c = @@ -992,13 +1099,13 @@ CURLcode Curl_getconnectinfo(struct SessionHandle *data, if(connp) /* only store this if the caller cares for it */ *connp = c; - *param_longp = c->sock[FIRSTSOCKET]; + sockfd = c->sock[FIRSTSOCKET]; /* we have a socket connected, let's determine if the server shut down */ /* determine if ssl */ if(c->ssl[FIRSTSOCKET].use) { /* use the SSL context */ if(!Curl_ssl_check_cxn(c)) - *param_longp = -1; /* FIN received */ + return CURL_SOCKET_BAD; /* FIN received */ } /* Minix 3.1 doesn't support any flags on recv; just assume socket is OK */ #ifdef MSG_PEEK @@ -1007,13 +1114,13 @@ CURLcode Curl_getconnectinfo(struct SessionHandle *data, char buf; if(recv((RECV_TYPE_ARG1)c->sock[FIRSTSOCKET], (RECV_TYPE_ARG2)&buf, (RECV_TYPE_ARG3)1, (RECV_TYPE_ARG4)MSG_PEEK) == 0) { - *param_longp = -1; /* FIN received */ + return CURL_SOCKET_BAD; /* FIN received */ } } #endif } else - *param_longp = -1; + return CURL_SOCKET_BAD; - return CURLE_OK; + return sockfd; } |