Newer
Older
over alternative addresses/socket families etc. */
return CURLE_OK;
Daniel Stenberg
committed
/* store remote address and port used in this connection attempt */
if(!getaddressinfo((struct sockaddr*)&addr.sa_addr,
/* malformed address or bug in inet_ntop, try next address */
error = ERRNO;
failf(data, "sa_addr inet_ntop() failed with errno %d: %s",
error, Curl_strerror(conn, error));
return CURLE_OK;
infof(data, " Trying %s...\n", ipaddress);
#ifdef ENABLE_IPV6
is_tcp = (addr.family == AF_INET || addr.family == AF_INET6) &&
addr.socktype == SOCK_STREAM;
#else
is_tcp = (addr.family == AF_INET) && addr.socktype == SOCK_STREAM;
#endif
if(is_tcp && data->set.tcp_nodelay)
Curl_tcpnodelay(conn, sockfd);
Daniel Stenberg
committed
Daniel Stenberg
committed
Daniel Stenberg
committed
Curl_sndbufset(sockfd);
if(is_tcp && data->set.tcp_keepalive)
tcpkeepalive(data, sockfd);
if(data->set.fsockopt) {
/* activate callback for setting socket options */
error = data->set.fsockopt(data->set.sockopt_client,
sockfd,
CURLSOCKTYPE_IPCXN);
if(error == CURL_SOCKOPT_ALREADY_CONNECTED)
isconnected = TRUE;
else if(error) {
Curl_closesocket(conn, sockfd); /* close the socket and bail out */
return CURLE_ABORTED_BY_CALLBACK;
}
}
Daniel Stenberg
committed
/* possibly bind the local end to an IP, interface or port */
if(addr.family == AF_INET
#ifdef ENABLE_IPV6
|| addr.family == AF_INET6
#endif
) {
result = bindlocal(conn, sockfd, addr.family,
Curl_ipv6_scope((struct sockaddr*)&addr.sa_addr));
if(result) {
Curl_closesocket(conn, sockfd); /* close socket and bail out */
if(result == CURLE_UNSUPPORTED_PROTOCOL) {
/* The address family is not supported on this interface.
We can continue trying addresses */
Patrick Monnerat
committed
return CURLE_COULDNT_CONNECT;
Daniel Stenberg
committed
}
/* set socket non-blocking */
(void)curlx_nonblock(sockfd, TRUE);
Daniel Stenberg
committed
conn->connecttime = Curl_tvnow();
if(conn->num_addr > 1)
Curl_expire_latest(data, conn->timeoutms_per_addr);
/* Connect TCP sockets, bind UDP */
if(!isconnected && (conn->socktype == SOCK_STREAM)) {
if(conn->bits.tcp_fastopen) {
#if defined(CONNECT_DATA_IDEMPOTENT) /* OS X */
sa_endpoints_t endpoints;
endpoints.sae_srcif = 0;
endpoints.sae_srcaddr = NULL;
endpoints.sae_srcaddrlen = 0;
endpoints.sae_dstaddr = &addr.sa_addr;
endpoints.sae_dstaddrlen = addr.addrlen;
rc = connectx(sockfd, &endpoints, SAE_ASSOCID_ANY,
CONNECT_RESUME_ON_READ_WRITE | CONNECT_DATA_IDEMPOTENT,
NULL, 0, NULL, NULL);
#elif defined(MSG_FASTOPEN) /* Linux */
if(conn->given->flags & PROTOPT_SSL)
rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
else
rc = 0; /* Do nothing */
#endif
}
else {
rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
}
if(-1 == rc)
error = SOCKERRNO;
else {
*sockp = sockfd;
return CURLE_OK;
}
#ifdef ENABLE_IPV6
conn->bits.ipv6 = (addr.family == AF_INET6)?TRUE:FALSE;
#endif
Daniel Stenberg
committed
Daniel Stenberg
committed
if(-1 == rc) {
switch(error) {
Daniel Stenberg
committed
case EINPROGRESS:
case EWOULDBLOCK:
#if defined(EAGAIN)
#if (EAGAIN) != (EWOULDBLOCK)
Daniel Stenberg
committed
/* On some platforms EAGAIN and EWOULDBLOCK are the
* same value, and on others they are different, hence
* the odd #if
*/
case EAGAIN:
#endif
Daniel Stenberg
committed
#endif
Daniel Stenberg
committed
default:
/* unknown error, fallthrough and try another address! */
infof(data, "Immediate connect fail for %s: %s\n",
ipaddress, Curl_strerror(conn, error));
data->state.os_errno = error;
Daniel Stenberg
committed
Curl_closesocket(conn, sockfd);
result = CURLE_COULDNT_CONNECT;
Daniel Stenberg
committed
}
Daniel Stenberg
committed
*sockp = sockfd;
Daniel Stenberg
committed
}
/*
* 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
* pointer with the connected socket.
*/
CURLcode Curl_connecthost(struct connectdata *conn, /* context */
const struct Curl_dns_entry *remotehost)
struct Curl_easy *data = conn->data;
CURLcode result = CURLE_COULDNT_CONNECT;
Daniel Stenberg
committed
long timeout_ms = Curl_timeleft(data, &before, TRUE);
Daniel Stenberg
committed
if(timeout_ms < 0) {
/* a precaution, no need to continue if time already is up */
failf(data, "Connection time-out");
return CURLE_OPERATION_TIMEDOUT;
conn->num_addr = Curl_num_addresses(remotehost->addr);
conn->tempaddr[0] = remotehost->addr;
conn->tempaddr[1] = NULL;
conn->tempsock[0] = CURL_SOCKET_BAD;
conn->tempsock[1] = CURL_SOCKET_BAD;
Curl_expire(conn->data, HAPPY_EYEBALLS_TIMEOUT);
/* Max time for the next connection attempt */
conn->timeoutms_per_addr =
conn->tempaddr[0]->ai_next == NULL ? timeout_ms : timeout_ms / 2;
/* start connecting to first IP */
while(conn->tempaddr[0]) {
result = singleipconnect(conn, conn->tempaddr[0], &(conn->tempsock[0]));
if(!result)
break;
conn->tempaddr[0] = conn->tempaddr[0]->ai_next;
}
if(conn->tempsock[0] == CURL_SOCKET_BAD) {
if(!result)
result = CURLE_COULDNT_CONNECT;
data->info.numconnects++; /* to track the number of connections made */
Daniel Stenberg
committed
struct connfind {
struct connectdata *tofind;
bool found;
};
static int conn_is_conn(struct connectdata *conn, void *param)
{
struct connfind *f = (struct connfind *)param;
if(conn == f->tofind) {
f->found = TRUE;
return 1;
}
return 0;
}
Daniel Stenberg
committed
/*
* Used to extract socket and connectdata struct for the most recent
* transfer on the given Curl_easy.
Daniel Stenberg
committed
*
* The returned socket will be CURL_SOCKET_BAD in case of failure!
Daniel Stenberg
committed
*/
curl_socket_t Curl_getconnectinfo(struct Curl_easy *data,
struct connectdata **connp)
Daniel Stenberg
committed
{
curl_socket_t sockfd;
DEBUGASSERT(data);
/* this works for an easy handle:
* - that has been used for curl_easy_perform()
* - that is associated with a multi handle, and whose connection
* was detached with CURLOPT_CONNECT_ONLY
*/
if(data->state.lastconnect && (data->multi_easy || data->multi)) {
struct connectdata *c = data->state.lastconnect;
struct connfind find;
find.tofind = data->state.lastconnect;
find.found = FALSE;
Curl_conncache_foreach(data->multi_easy?
&data->multi_easy->conn_cache:
&data->multi->conn_cache, &find, conn_is_conn);
if(!find.found) {
data->state.lastconnect = NULL;
return CURL_SOCKET_BAD;
}
Daniel Stenberg
committed
if(connp)
/* only store this if the caller cares for it */
*connp = c;
sockfd = c->sock[FIRSTSOCKET];
Daniel Stenberg
committed
/* 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))
return CURL_SOCKET_BAD; /* FIN received */
Daniel Stenberg
committed
}
/* Minix 3.1 doesn't support any flags on recv; just assume socket is OK */
#ifdef MSG_PEEK
else if(sockfd != CURL_SOCKET_BAD) {
Daniel Stenberg
committed
/* use the socket */
char buf;
if(recv((RECV_TYPE_ARG1)sockfd, (RECV_TYPE_ARG2)&buf,
Daniel Stenberg
committed
(RECV_TYPE_ARG3)1, (RECV_TYPE_ARG4)MSG_PEEK) == 0) {
return CURL_SOCKET_BAD; /* FIN received */
Daniel Stenberg
committed
}
}
#endif
}
else
return CURL_SOCKET_BAD;
Daniel Stenberg
committed
Daniel Stenberg
committed
}
/*
* Close a socket.
*
* 'conn' can be NULL, beware!
*/
int Curl_closesocket(struct connectdata *conn,
if((sock == conn->sock[SECONDARYSOCKET]) &&
conn->sock_accepted[SECONDARYSOCKET])
/* if this socket matches the second socket, and that was created with
accept, then we MUST NOT call the callback but clear the accepted
status */
conn->sock_accepted[SECONDARYSOCKET] = FALSE;
else {
Curl_multi_closed(conn, sock);
return conn->fclosesocket(conn->closesocket_client, sock);
if(conn)
/* tell the multi-socket code about this */
Curl_multi_closed(conn, sock);
sclose(sock);
/*
* Create a socket based on info from 'conn' and 'ai'.
*
* 'addr' should be a pointer to the correct struct to get data back, or NULL.
* 'sockfd' must be a pointer to a socket descriptor.
*
* If the open socket callback is set, used that!
*
*/
CURLcode Curl_socket(struct connectdata *conn,
const Curl_addrinfo *ai,
struct Curl_sockaddr_ex *addr,
curl_socket_t *sockfd)
{
struct Curl_easy *data = conn->data;
struct Curl_sockaddr_ex dummy;
if(!addr)
/* if the caller doesn't want info back, use a local temp copy */
addr = &dummy;
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
/*
* The Curl_sockaddr_ex structure is basically libcurl's external API
* curl_sockaddr structure with enough space available to directly hold
* any protocol-specific address structures. The variable declared here
* will be used to pass / receive data to/from the fopensocket callback
* if this has been set, before that, it is initialized from parameters.
*/
addr->family = ai->ai_family;
addr->socktype = conn->socktype;
addr->protocol = conn->socktype==SOCK_DGRAM?IPPROTO_UDP:ai->ai_protocol;
addr->addrlen = ai->ai_addrlen;
if(addr->addrlen > sizeof(struct Curl_sockaddr_storage))
addr->addrlen = sizeof(struct Curl_sockaddr_storage);
memcpy(&addr->sa_addr, ai->ai_addr, addr->addrlen);
if(data->set.fopensocket)
/*
* If the opensocket callback is set, all the destination address
* information is passed to the callback. Depending on this information the
* callback may opt to abort the connection, this is indicated returning
* CURL_SOCKET_BAD; otherwise it will return a not-connected socket. When
* the callback returns a valid socket the destination address information
* might have been changed and this 'new' address will actually be used
* here to connect.
*/
*sockfd = data->set.fopensocket(data->set.opensocket_client,
CURLSOCKTYPE_IPCXN,
(struct curl_sockaddr *)addr);
else
/* opensocket callback not set, so simply create the socket now */
*sockfd = socket(addr->family, addr->socktype, addr->protocol);
if(*sockfd == CURL_SOCKET_BAD)
/* no socket, no connection */
return CURLE_COULDNT_CONNECT;
#if defined(ENABLE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
if(conn->scope_id && (addr->family == AF_INET6)) {
struct sockaddr_in6 * const sa6 = (void *)&addr->sa_addr;
sa6->sin6_scope_id = conn->scope_id;
#endif
return CURLE_OK;
}
#ifdef CURLDEBUG
/*
* Curl_conncontrol() is used to set the conn->bits.close bit on or off. It
* MUST be called with the connclose() or connkeep() macros with a stated
* reason. The reason is only shown in debug builds but helps to figure out
* decision paths when connections are or aren't re-used as expected.
*/
void Curl_conncontrol(struct connectdata *conn, bool closeit,
const char *reason)
{
#if defined(CURL_DISABLE_VERBOSE_STRINGS)
(void) reason;
#endif
if(closeit != conn->bits.close) {
infof(conn->data, "Marked for [%s]: %s\n", closeit?"closure":"keep alive",
reason);
conn->bits.close = closeit; /* the only place in the source code that
should assign this bit */
}