* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
#ifdef HAVE_SYS_UN_H
#include <sys/un.h> /* for sockaddr_un */
#endif
-#ifdef HAVE_NETINET_TCP_H
-#include <netinet/tcp.h> /* for TCP_NODELAY */
+#ifdef HAVE_LINUX_TCP_H
+#include <linux/tcp.h>
+#elif defined(HAVE_NETINET_TCP_H)
+#include <netinet/tcp.h>
#endif
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
*
* @unittest: 1303
*/
-time_t Curl_timeleft(struct Curl_easy *data,
- struct timeval *nowp,
- bool duringconnect)
+timediff_t Curl_timeleft(struct Curl_easy *data,
+ struct curltime *nowp,
+ bool duringconnect)
{
int timeout_set = 0;
- time_t timeout_ms = duringconnect?DEFAULT_CONNECT_TIMEOUT:0;
- struct timeval now;
+ timediff_t timeout_ms = duringconnect?DEFAULT_CONNECT_TIMEOUT:0;
+ struct curltime now;
/* if a timeout is set, use the most restrictive one */
}
if(!nowp) {
- now = Curl_tvnow();
+ now = Curl_now();
nowp = &now;
}
/* subtract elapsed time */
if(duringconnect)
/* since this most recent connect started */
- timeout_ms -= Curl_tvdiff(*nowp, data->progress.t_startsingle);
+ timeout_ms -= Curl_timediff(*nowp, data->progress.t_startsingle);
else
/* since the entire operation started */
- timeout_ms -= Curl_tvdiff(*nowp, data->progress.t_startop);
+ timeout_ms -= Curl_timediff(*nowp, data->progress.t_startop);
if(!timeout_ms)
/* avoid returning 0 as that means no timeout! */
return -1;
struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&sa;
#endif
- struct Curl_dns_entry *h=NULL;
+ struct Curl_dns_entry *h = NULL;
unsigned short port = data->set.localport; /* use this port number, 0 for
"random" */
/* how many port numbers to try to bind to, increasing one at a time */
/* interface */
if(!is_host) {
+#ifdef SO_BINDTODEVICE
+ /* I am not sure any other OSs than Linux that provide this feature,
+ * and at the least I cannot test. --Ben
+ *
+ * This feature allows one to tightly bind the local socket to a
+ * particular interface. This will force even requests to other
+ * local interfaces to go out the external interface.
+ *
+ *
+ * Only bind to the interface when specified as interface, not just
+ * as a hostname or ip address.
+ *
+ * interface might be a VRF, eg: vrf-blue, which means it cannot be
+ * converted to an IP address and would fail Curl_if2ip. Simply try
+ * to use it straight away.
+ */
+ if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
+ dev, (curl_socklen_t)strlen(dev) + 1) == 0) {
+ /* This is typically "errno 1, error: Operation not permitted" if
+ * you're not running as root or another suitable privileged
+ * user.
+ * If it succeeds it means the parameter was a valid interface and
+ * not an IP address. Return immediately.
+ */
+ return CURLE_OK;
+ }
+#endif
+
switch(Curl_if2ip(af, scope, conn->scope_id, dev,
myhost, sizeof(myhost))) {
case IF2IP_NOT_FOUND:
infof(data, "Local Interface %s is ip %s using address family %i\n",
dev, myhost, af);
done = 1;
-
-#ifdef SO_BINDTODEVICE
- /* I am not sure any other OSs than Linux that provide this feature,
- * and at the least I cannot test. --Ben
- *
- * This feature allows one to tightly bind the local socket to a
- * particular interface. This will force even requests to other
- * local interfaces to go out the external interface.
- *
- *
- * Only bind to the interface when specified as interface, not just
- * as a hostname or ip address.
- */
- if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
- dev, (curl_socklen_t)strlen(dev)+1) != 0) {
- error = SOCKERRNO;
- infof(data, "SO_BINDTODEVICE %s failed with errno %d: %s;"
- " will do regular bind\n",
- dev, error, Curl_strerror(conn, error));
- /* This is typically "errno 1, error: Operation not permitted" if
- you're not running as root or another suitable privileged
- user */
- }
-#endif
break;
}
}
}
if(done < 1) {
+ /* errorbuf is set false so failf will overwrite any message already in
+ the error buffer, so the user receives this error message instead of a
+ generic resolve error. */
+ data->state.errorbuf = FALSE;
failf(data, "Couldn't bind to '%s'", dev);
return CURLE_INTERFACE_FAILED;
}
conn->data->info.conn_local_port = conn->local_port;
}
-/* retrieves ip address and port from a sockaddr structure */
-static bool getaddressinfo(struct sockaddr *sa, char *addr,
- long *port)
+/* retrieves ip address and port from a sockaddr structure.
+ note it calls Curl_inet_ntop which sets errno on fail, not SOCKERRNO. */
+bool Curl_getaddressinfo(struct sockaddr *sa, char *addr,
+ long *port)
{
unsigned short us_port;
struct sockaddr_in *si = NULL;
addr[0] = '\0';
*port = 0;
-
+ errno = EAFNOSUPPORT;
return FALSE;
}
return;
if(!conn->bits.reuse && !conn->bits.tcp_fastopen) {
- int error;
-
len = sizeof(struct Curl_sockaddr_storage);
if(getpeername(sockfd, (struct sockaddr*) &ssrem, &len)) {
- error = SOCKERRNO;
+ int error = SOCKERRNO;
failf(data, "getpeername() failed with errno %d: %s",
error, Curl_strerror(conn, error));
return;
len = sizeof(struct Curl_sockaddr_storage);
memset(&ssloc, 0, sizeof(ssloc));
if(getsockname(sockfd, (struct sockaddr*) &ssloc, &len)) {
- error = SOCKERRNO;
+ int error = SOCKERRNO;
failf(data, "getsockname() failed with errno %d: %s",
error, Curl_strerror(conn, error));
return;
}
- if(!getaddressinfo((struct sockaddr*)&ssrem,
- conn->primary_ip, &conn->primary_port)) {
- error = ERRNO;
+ if(!Curl_getaddressinfo((struct sockaddr*)&ssrem,
+ conn->primary_ip, &conn->primary_port)) {
failf(data, "ssrem inet_ntop() failed with errno %d: %s",
- error, Curl_strerror(conn, error));
+ errno, Curl_strerror(conn, errno));
return;
}
memcpy(conn->ip_addr_str, conn->primary_ip, MAX_IPADR_LEN);
- if(!getaddressinfo((struct sockaddr*)&ssloc,
- conn->local_ip, &conn->local_port)) {
- error = ERRNO;
+ if(!Curl_getaddressinfo((struct sockaddr*)&ssloc,
+ conn->local_ip, &conn->local_port)) {
failf(data, "ssloc inet_ntop() failed with errno %d: %s",
- error, Curl_strerror(conn, error));
+ errno, Curl_strerror(conn, errno));
return;
}
{
struct Curl_easy *data = conn->data;
CURLcode result = CURLE_OK;
- time_t allow;
+ timediff_t allow;
int error = 0;
- struct timeval now;
+ struct curltime now;
int rc;
int i;
return CURLE_OK;
}
- now = Curl_tvnow();
+ now = Curl_now();
/* figure out how long time we have left to connect */
allow = Curl_timeleft(data, &now, TRUE);
return CURLE_OPERATION_TIMEDOUT;
}
- for(i=0; i<2; i++) {
+ for(i = 0; i<2; i++) {
const int other = i ^ 1;
if(conn->tempsock[i] == CURL_SOCKET_BAD)
continue;
if(rc == 0) { /* no connection yet */
error = 0;
- if(curlx_tvdiff(now, conn->connecttime) >= conn->timeoutms_per_addr) {
+ if(Curl_timediff(now, conn->connecttime) >= conn->timeoutms_per_addr) {
infof(data, "After %ldms connect time, move on!\n",
conn->timeoutms_per_addr);
error = ETIMEDOUT;
/* should we try another protocol family? */
if(i == 0 && conn->tempaddr[1] == NULL &&
- curlx_tvdiff(now, conn->connecttime) >= HAPPY_EYEBALLS_TIMEOUT) {
+ (Curl_timediff(now, conn->connecttime) >=
+ data->set.happy_eyeballs_timeout)) {
trynextip(conn, sockindex, 1);
}
}
conn->sock[sockindex] = conn->tempsock[i];
conn->ip_addr = conn->tempaddr[i];
conn->tempsock[i] = CURL_SOCKET_BAD;
+#ifdef ENABLE_IPV6
+ conn->bits.ipv6 = (conn->ip_addr->ai_family == AF_INET6)?TRUE:FALSE;
+#endif
/* close the other socket, if open */
if(conn->tempsock[other] != CURL_SOCKET_BAD) {
return CURLE_OK;
}
- else
- infof(data, "Connection failed\n");
+ infof(data, "Connection failed\n");
}
else if(rc & CURL_CSELECT_ERR)
(void)verifyconnect(conn->tempsock[i], &error);
curl_socklen_t onoff = (curl_socklen_t) 1;
int level = IPPROTO_TCP;
-#if 0
- /* The use of getprotobyname() is disabled since it isn't thread-safe on
- numerous systems. On these getprotobyname_r() should be used instead, but
- that exists in at least one 4 arg version and one 5 arg version, and
- since the proto number rarely changes anyway we now just use the hard
- coded number. The "proper" fix would need a configure check for the
- correct function much in the same style the gethostbyname_r versions are
- detected. */
- struct protoent *pe = getprotobyname("tcp");
- if(pe)
- level = pe->p_proto;
-#endif
-
#if defined(CURL_DISABLE_VERBOSE_STRINGS)
(void) conn;
#endif
static void nosigpipe(struct connectdata *conn,
curl_socket_t sockfd)
{
- struct Curl_easy *data= conn->data;
+ struct Curl_easy *data = conn->data;
int onoff = 1;
if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff,
sizeof(onoff)) < 0)
char ipaddress[MAX_IPADR_LEN];
long port;
bool is_tcp;
+#ifdef TCP_FASTOPEN_CONNECT
+ int optval = 1;
+#endif
*sockp = CURL_SOCKET_BAD;
return CURLE_OK;
/* store remote address and port used in this connection attempt */
- if(!getaddressinfo((struct sockaddr*)&addr.sa_addr,
- ipaddress, &port)) {
+ if(!Curl_getaddressinfo((struct sockaddr*)&addr.sa_addr,
+ ipaddress, &port)) {
/* 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));
+ errno, Curl_strerror(conn, errno));
Curl_closesocket(conn, sockfd);
return CURLE_OK;
}
if(data->set.fsockopt) {
/* activate callback for setting socket options */
+ Curl_set_in_callback(data, true);
error = data->set.fsockopt(data->set.sockopt_client,
sockfd,
CURLSOCKTYPE_IPCXN);
+ Curl_set_in_callback(data, false);
if(error == CURL_SOCKOPT_ALREADY_CONNECTED)
isconnected = TRUE;
/* set socket non-blocking */
(void)curlx_nonblock(sockfd, TRUE);
- conn->connecttime = Curl_tvnow();
+ conn->connecttime = Curl_now();
if(conn->num_addr > 1)
- Curl_expire_latest(data, conn->timeoutms_per_addr);
+ 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 */
- 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 defined(CONNECT_DATA_IDEMPOTENT) /* Darwin */
+# if defined(HAVE_BUILTIN_AVAILABLE)
+ /* while connectx function is available since macOS 10.11 / iOS 9,
+ it did not have the interface declared correctly until
+ Xcode 9 / macOS SDK 10.13 */
+ if(__builtin_available(macOS 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *)) {
+ 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);
+ }
+ else {
+ rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
+ }
+# else
+ rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
+# endif /* HAVE_BUILTIN_AVAILABLE */
+#elif defined(TCP_FASTOPEN_CONNECT) /* Linux >= 4.11 */
+ if(setsockopt(sockfd, IPPROTO_TCP, TCP_FASTOPEN_CONNECT,
+ (void *)&optval, sizeof(optval)) < 0)
+ infof(data, "Failed to enable TCP Fast Open on fd %d\n", sockfd);
+ else
+ infof(data, "TCP_FASTOPEN_CONNECT set\n");
+
+ rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
+#elif defined(MSG_FASTOPEN) /* old Linux */
if(conn->given->flags & PROTOPT_SSL)
rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
else
return CURLE_OK;
}
-#ifdef ENABLE_IPV6
- conn->bits.ipv6 = (addr.family == AF_INET6)?TRUE:FALSE;
-#endif
-
if(-1 == rc) {
switch(error) {
case EINPROGRESS:
const struct Curl_dns_entry *remotehost)
{
struct Curl_easy *data = conn->data;
- struct timeval before = Curl_tvnow();
+ struct curltime before = Curl_now();
CURLcode result = CURLE_COULDNT_CONNECT;
- time_t timeout_ms = Curl_timeleft(data, &before, TRUE);
+ timediff_t timeout_ms = Curl_timeleft(data, &before, TRUE);
if(timeout_ms < 0) {
/* a precaution, no need to continue if time already is up */
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 =
}
data->info.numconnects++; /* to track the number of connections made */
+ Curl_expire(conn->data, data->set.happy_eyeballs_timeout,
+ EXPIRE_HAPPY_EYEBALLS);
return CURLE_OK;
}
find.tofind = data->state.lastconnect;
find.found = FALSE;
- Curl_conncache_foreach(data->multi_easy?
+ Curl_conncache_foreach(data, data->multi_easy?
&data->multi_easy->conn_cache:
&data->multi->conn_cache, &find, conn_is_conn);
status */
conn->sock_accepted[SECONDARYSOCKET] = FALSE;
else {
+ int rc;
Curl_multi_closed(conn, sock);
- return conn->fclosesocket(conn->closesocket_client, sock);
+ Curl_set_in_callback(conn->data, true);
+ rc = conn->fclosesocket(conn->closesocket_client, sock);
+ Curl_set_in_callback(conn->data, false);
+ return rc;
}
}
addr->family = ai->ai_family;
addr->socktype = conn->socktype;
- addr->protocol = conn->socktype==SOCK_DGRAM?IPPROTO_UDP:ai->ai_protocol;
+ 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(data->set.fopensocket) {
/*
* If the opensocket callback is set, all the destination address
* information is passed to the callback. Depending on this information the
* might have been changed and this 'new' address will actually be used
* here to connect.
*/
+ Curl_set_in_callback(data, true);
*sockfd = data->set.fopensocket(data->set.opensocket_client,
CURLSOCKTYPE_IPCXN,
(struct curl_sockaddr *)addr);
+ Curl_set_in_callback(data, false);
+ }
else
/* opensocket callback not set, so simply create the socket now */
*sockfd = socket(addr->family, addr->socktype, addr->protocol);
*/
void Curl_conncontrol(struct connectdata *conn,
int ctrl /* see defines in header */
-#ifdef DEBUGBUILD
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
, const char *reason
#endif
)