Newer
Older
Daniel Stenberg
committed
/* set socket non-blocking */
Daniel Stenberg
committed
curlx_nonblock(sockfd, TRUE);
Daniel Stenberg
committed
conn->connecttime = Curl_tvnow();
if(conn->num_addr > 1)
Curl_expire(data, conn->timeoutms_per_addr);
/* Connect TCP sockets, bind UDP */
if(!isconnected && (conn->socktype == SOCK_STREAM)) {
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) {
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
*sockp = sockfd;
return CURLE_OK;
Daniel Stenberg
committed
default:
/* unknown error, fallthrough and try another address! */
failf(data, "Failed to connect to %s: %s",
conn->ip_addr_str, Curl_strerror(conn,error));
data->state.os_errno = error;
Daniel Stenberg
committed
/* connect failed */
Curl_closesocket(conn, sockfd);
Daniel Stenberg
committed
}
else
*sockp = sockfd;
Daniel Stenberg
committed
return CURLE_OK;
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,
bool *connected) /* really connected? */
{
struct SessionHandle *data = conn->data;
struct timeval after;
struct timeval before = Curl_tvnow();
/*************************************************************
* Figure out what maximum time we have left
*************************************************************/
Daniel Stenberg
committed
*connected = FALSE; /* default to not connected */
Daniel Stenberg
committed
/* get the timeout left */
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] = remotehost->addr;
Daniel Stenberg
committed
Daniel Stenberg
committed
/* Below is the loop that attempts to connect to all IP-addresses we
* know for the given host.
* One by one, for each protocol, until one IP succeeds.
Daniel Stenberg
committed
*/
Daniel Stenberg
committed
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
for(i=0; i<2; i++) {
curl_socket_t sockfd = CURL_SOCKET_BAD;
Curl_addrinfo *ai = conn->tempaddr[i];
int family = i ? AF_INET6 : AF_INET;
/* find first address for this address family, if any */
while(ai && ai->ai_family != family)
ai = ai->ai_next;
/*
* Connecting with a Curl_addrinfo chain
*/
while(ai) {
CURLcode res;
/* Max time for the next connection attempt */
conn->timeoutms_per_addr = ai->ai_next == NULL ?
timeout_ms : timeout_ms / 2;
/* start connecting to the IP curr_addr points to */
res = singleipconnect(conn, ai, &sockfd, connected);
if(res)
return res;
if(sockfd != CURL_SOCKET_BAD)
break;
/* 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_TIMEDOUT;
}
before = after;
/* next addresses */
do {
ai = ai->ai_next;
} while(ai && ai->ai_family != family);
} /* end of connect-to-each-address loop */
Daniel Stenberg
committed
conn->tempsock[i] = sockfd;
conn->tempaddr[i] = ai;
}
if((conn->tempsock[0] == CURL_SOCKET_BAD) &&
(conn->tempsock[1] == CURL_SOCKET_BAD)) {
failf(data, "couldn't connect to %s at %s:%ld",
conn->bits.proxy?"proxy":"host",
conn->bits.proxy?conn->proxy.name:conn->host.name, conn->port);
return CURLE_COULDNT_CONNECT;
}
/* leave the socket in non-blocking mode */
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 SessionHandle.
*
* The returned socket will be CURL_SOCKET_BAD in case of failure!
Daniel Stenberg
committed
*/
curl_socket_t Curl_getconnectinfo(struct SessionHandle *data,
struct connectdata **connp)
Daniel Stenberg
committed
{
curl_socket_t sockfd;
DEBUGASSERT(data);
/* this only works for an easy handle that has been used for
curl_easy_perform()! */
if(data->state.lastconnect && data->multi_easy) {
struct connectdata *c = data->state.lastconnect;
struct connfind find;
find.tofind = data->state.lastconnect;
find.found = FALSE;
Curl_conncache_foreach(data->multi_easy->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 {
/* use the socket */
char buf;
if(recv((RECV_TYPE_ARG1)c->sock[FIRSTSOCKET], (RECV_TYPE_ARG2)&buf,
(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
return conn->fclosesocket(conn->closesocket_client, sock);
}
sclose(sock);
if(conn)
/* tell the multi-socket code about this */
Curl_multi_closed(conn, sock);
return 0;
/*
* 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 SessionHandle *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;
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
/*
* 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 && (addr->family == AF_INET6)) {
struct sockaddr_in6 * const sa6 = (void *)&addr->sa_addr;
sa6->sin6_scope_id = conn->scope;
#endif
return CURLE_OK;
}