Newer
Older
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
Curl_expire(data, conn->timeoutms_per_addr, EXPIRE_DNS_PER_NAME);
/* Connect TCP sockets, bind UDP */
if(!isconnected && (conn->socktype == SOCK_STREAM)) {
if(conn->bits.tcp_fastopen) {
#if defined(CONNECT_DATA_IDEMPOTENT) /* OS X */
#ifdef HAVE_BUILTIN_AVAILABLE
if(__builtin_available(macOS 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *)) {
#endif /* HAVE_BUILTIN_AVAILABLE */
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);
#ifdef HAVE_BUILTIN_AVAILABLE
}
else {
rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
}
#endif /* HAVE_BUILTIN_AVAILABLE */
#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;
}
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;
struct curltime before = Curl_now();
CURLcode result = CURLE_COULDNT_CONNECT;
Daniel Stenberg
committed
timediff_t 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;
/* 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 */
Curl_expire(conn->data, HAPPY_EYEBALLS_TIMEOUT, EXPIRE_HAPPY_EYEBALLS);
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, 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
}
else
return CURL_SOCKET_BAD;
Daniel Stenberg
committed
Daniel Stenberg
committed
}
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
/*
* Check if a connection seems to be alive.
*/
bool Curl_connalive(struct connectdata *conn)
{
/* First determine if ssl */
if(conn->ssl[FIRSTSOCKET].use) {
/* use the SSL context */
if(!Curl_ssl_check_cxn(conn))
return false; /* FIN received */
}
/* Minix 3.1 doesn't support any flags on recv; just assume socket is OK */
#ifdef MSG_PEEK
else if(conn->sock[FIRSTSOCKET] == CURL_SOCKET_BAD)
return false;
else {
/* use the socket */
char buf;
if(recv((RECV_TYPE_ARG1)conn->sock[FIRSTSOCKET], (RECV_TYPE_ARG2)&buf,
(RECV_TYPE_ARG3)1, (RECV_TYPE_ARG4)MSG_PEEK) == 0) {
return false; /* FIN received */
}
}
#endif
return true;
}
/*
* 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;
/*
* 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;
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
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;
}
* Curl_conncontrol() marks streams or connection for closure.
void Curl_conncontrol(struct connectdata *conn,
int ctrl /* see defines in header */
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
, const char *reason
#endif
)
{
/* close if a connection, or a stream that isn't multiplexed */
bool closeit = (ctrl == CONNCTRL_CONNECTION) ||
((ctrl == CONNCTRL_STREAM) && !(conn->handler->flags & PROTOPT_STREAM));
if((ctrl == CONNCTRL_STREAM) &&
(conn->handler->flags & PROTOPT_STREAM))
DEBUGF(infof(conn->data, "Kill stream: %s\n", reason));
else if(closeit != conn->bits.close) {
DEBUGF(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 */
}
/* Data received can be cached at various levels, so check them all here. */
bool Curl_conn_data_pending(struct connectdata *conn, int sockindex)
{
int readable;
if(Curl_ssl_data_pending(conn, sockindex) ||
Curl_recv_has_postponed_data(conn, sockindex))
return true;
readable = SOCKET_READABLE(conn->sock[sockindex], 0);
return (readable > 0 && (readable & CURL_CSELECT_IN));
}