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);
Daniel Stenberg
committed
if(data->set.tcp_nodelay)
tcpnodelay(conn, sockfd);
Daniel Stenberg
committed
Daniel Stenberg
committed
Curl_sndbufset(sockfd);
if(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 */
res = bindlocal(conn, sockfd, addr.family);
Daniel Stenberg
committed
if(res) {
Curl_closesocket(conn, sockfd); /* close socket and bail out */
if(res == CURLE_UNSUPPORTED_PROTOCOL) {
/* The address family is not supported on this interface.
We can continue trying addresses */
return CURLE_OK;
}
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) {
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
res = CURLE_OK;
break;
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);
res = CURLE_COULDNT_CONNECT;
Daniel Stenberg
committed
}
Daniel Stenberg
committed
if(!res)
*sockp = sockfd;
return res;
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 SessionHandle *data = conn->data;
CURLcode res = 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]) {
res = singleipconnect(conn, conn->tempaddr[0], &(conn->tempsock[0]));
if(res == CURLE_OK)
break;
conn->tempaddr[0] = conn->tempaddr[0]->ai_next;
}
if(conn->tempsock[0] == CURL_SOCKET_BAD)
return res;
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;
1275
1276
1277
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
/*
* 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;
}