summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrad G <43330364+bakedpotat0@users.noreply.github.com>2018-11-29 12:17:21 -0600
committerDaniel Stenberg <daniel@haxx.se>2018-12-16 18:46:01 +0100
commit306a47cea67f29a1c7a19a42de85e66acb03f1d7 (patch)
tree61ffad27ec4f7a2d4e155a64d2fa3ad37b9725b7
parent9e6518481cd4e2603b4f9e04d6cfd1a214a72726 (diff)
downloadcurl-306a47cea67f29a1c7a19a42de85e66acb03f1d7.tar.gz
connect: Add TCP Fast Open support for WindowsTFO-windows
This is the former PR #3327, saved by Johannes Schindelin, rebased, squashed and pushed again. Requires Windows 10 ver 1607 or newer
-rw-r--r--lib/config-win32.h6
-rw-r--r--lib/connect.c57
-rw-r--r--lib/curl_setup.h9
-rw-r--r--lib/easy.c36
-rw-r--r--lib/sendf.c31
-rw-r--r--lib/setopt.c7
-rw-r--r--lib/urldata.h5
7 files changed, 147 insertions, 4 deletions
diff --git a/lib/config-win32.h b/lib/config-win32.h
index 76b00b9bb..aac553f29 100644
--- a/lib/config-win32.h
+++ b/lib/config-win32.h
@@ -609,6 +609,12 @@ Vista
# endif
#endif
+/* Define if you have the ConnectEx WinSock 2 extension */
+/* This requires a build target of WinXP or newer */
+#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0501)
+#define HAVE_CONNECTEX 1
+#endif
+
/* ---------------------------------------------------------------- */
/* STRUCT RELATED */
/* ---------------------------------------------------------------- */
diff --git a/lib/connect.c b/lib/connect.c
index ec3cd3a79..fb6ea4f7a 100644
--- a/lib/connect.c
+++ b/lib/connect.c
@@ -770,8 +770,16 @@ CURLcode Curl_is_connected(struct connectdata *conn,
(void)verifyconnect(conn->tempsock[i], NULL);
#endif
- /* check socket for connect */
- rc = SOCKET_WRITABLE(conn->tempsock[i], 0);
+#ifdef HAVE_CONNECTEX
+ /* socket isn't actually connected until the first attempted write,
+ so it doesn't appear writable here yet; spoof rc to fall into the
+ good branch (socket errors will be detected after the second pass) */
+ if(conn->bits.tcp_fastopen && !conn->fastopen_connected)
+ rc = 1;
+ else
+#endif
+ /* check socket for connect */
+ rc = SOCKET_WRITABLE(conn->tempsock[i], 0);
if(rc == 0) { /* no connection yet */
error = 0;
@@ -994,6 +1002,13 @@ static CURLcode singleipconnect(struct connectdata *conn,
bool is_tcp;
#ifdef TCP_FASTOPEN_CONNECT
int optval = 1;
+#elif defined(HAVE_CONNECTEX)
+ int optval = 1;
+ struct sockaddr_storage ss;
+ struct sockaddr_in *si4 = (struct sockaddr_in *)&ss;
+#ifdef ENABLE_IPV6
+ struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&ss;
+#endif
#endif
*sockp = CURL_SOCKET_BAD;
@@ -1113,7 +1128,45 @@ static CURLcode singleipconnect(struct connectdata *conn,
rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
else
rc = 0; /* Do nothing */
+#elif defined(HAVE_CONNECTEX) /* Windows 10 (ver 1607+) */
+ /* Windows uses ConnectEx() which must be called when the first data */
+ /* is ready to be sent. For now, just set up the socket as needed but */
+ /* don't actually connect */
+ if(setsockopt(sockfd, IPPROTO_TCP, TCP_FASTOPEN,
+ (void *)&optval, sizeof(optval)) == SOCKET_ERROR) {
+ infof(data, "Failed to enable TCP Fast Open on fd %d, errno: %d\n",
+ sockfd, SOCKERRNO);
+ return CURLE_FAILED_INIT;
+ }
+ else
+ infof(data, "TCP_FASTOPEN set\n");
+
+ /* ConnectEx() requires a bound socket */
+ if(!conn->bits.bound) {
+ memset(&ss, 0, sizeof(ss));
+
+ if(addr.family == AF_INET) {
+ si4->sin_family = AF_INET;
+ si4->sin_addr.s_addr = INADDR_ANY;
+ }
+#if defined(USE_IPV6)
+ else if(addr.family == AF_INET6) {
+ si6->sin6_family = AF_INET6;
+ si6->sin6_addr = in6addr_any;
+ }
#endif
+ else {
+ infof(data, "Unknown protocol used with TCP Fast Open: %d\n",
+ addr.family);
+ return CURLE_UNSUPPORTED_PROTOCOL;
+ }
+
+ if(bind(sockfd, (struct sockaddr *)&ss, sizeof(ss)) == 0)
+ rc = 0;
+ }
+ else
+ rc = 0;
+#endif /* defined(HAVE_CONNECTEX) */
}
else {
rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
diff --git a/lib/curl_setup.h b/lib/curl_setup.h
index f83e1ea4f..80e300f1a 100644
--- a/lib/curl_setup.h
+++ b/lib/curl_setup.h
@@ -249,6 +249,15 @@
# include <windows.h>
# ifdef HAVE_WINSOCK2_H
# include <winsock2.h>
+# ifdef HAVE_CONNECTEX
+# include <MSWSock.h>
+# ifdef WSAID_CONNECTEX
+ typedef LPFN_CONNECTEX curl_ConnectEx_callback;
+ extern curl_ConnectEx_callback Curl_ConnectEx;
+# else
+# undef HAVE_CONNECTEX
+# endif
+# endif
# ifdef HAVE_WS2TCPIP_H
# include <ws2tcpip.h>
# endif
diff --git a/lib/easy.c b/lib/easy.c
index e592d7a71..a3d04e062 100644
--- a/lib/easy.c
+++ b/lib/easy.c
@@ -104,6 +104,12 @@ static CURLcode win32_init(void)
WSADATA wsaData;
int res;
+#ifdef HAVE_CONNECTEX
+ curl_socket_t sockfd;
+ GUID guid = WSAID_CONNECTEX;
+ DWORD dummy_val;
+#endif
+
#if defined(ENABLE_IPV6) && (USE_WINSOCK < 2)
Error IPV6_requires_winsock2
#endif
@@ -132,9 +138,31 @@ static CURLcode win32_init(void)
return CURLE_FAILED_INIT;
}
/* The Windows Sockets DLL is acceptable. Proceed. */
+
+ /* Init pointer to ConnectEx(), a Winsock 2 function needed */
+ /* for TCP Fast Open support */
+#ifdef HAVE_CONNECTEX
+ Curl_ConnectEx = NULL;
+ sockfd = socket(AF_INET, SOCK_STREAM, 0);
+
+ if(sockfd != CURL_SOCKET_BAD) {
+ if(WSAIoctl(sockfd, SIO_GET_EXTENSION_FUNCTION_POINTER,
+ &guid, sizeof(guid),
+ &Curl_ConnectEx, sizeof(Curl_ConnectEx),
+ &dummy_val, NULL, NULL)) {
+ DEBUGF(fprintf(stderr,
+ "WSAIoctl() failed to return ConnectEx() ptr, err #%d\n",
+ WSAGetLastError()));
+ Curl_ConnectEx = NULL;
+ }
+
+ sclose(sockfd);
+ }
+
+#endif /* HAVE_CONNECTEX */
#elif defined(USE_LWIPSOCK)
lwip_init();
-#endif
+#endif /* USE_WINSOCK */
#ifdef USE_WINDOWS_SSPI
{
@@ -181,6 +209,12 @@ curl_calloc_callback Curl_ccalloc = (curl_calloc_callback)calloc;
#if defined(WIN32) && defined(UNICODE)
curl_wcsdup_callback Curl_cwcsdup = (curl_wcsdup_callback)_wcsdup;
#endif
+#ifdef HAVE_CONNECTEX
+/*
+ * ConnectEx pointer is initialized by win32_init() after Winsock init
+ */
+curl_ConnectEx_callback Curl_ConnectEx;
+#endif
#else
/*
* Symbian OS doesn't support initialization to code in writable static data.
diff --git a/lib/sendf.c b/lib/sendf.c
index e8598e617..2a4db3971 100644
--- a/lib/sendf.c
+++ b/lib/sendf.c
@@ -387,6 +387,37 @@ ssize_t Curl_send_plain(struct connectdata *conn, int num,
conn->bits.tcp_fastopen = FALSE;
}
else
+#elif defined(HAVE_CONNECTEX)
+ if(conn->bits.tcp_fastopen && !conn->fastopen_connected) {
+ conn->fastopen_connected = TRUE;
+ bytes_written = 0;
+
+ if(!Curl_ConnectEx(sockfd,
+ conn->ip_addr->ai_addr, conn->ip_addr->ai_addrlen,
+ (void *)mem, (DWORD)len,
+ (LPDWORD)&bytes_written,
+ &conn->fastopen_state)) {
+ if(SOCKERRNO != WSA_IO_PENDING)
+ bytes_written = -1;
+ else
+ if(!GetOverlappedResult((HANDLE)sockfd, &conn->fastopen_state,
+ (LPDWORD)&bytes_written, TRUE)) {
+ int err = GetLastError();
+ failf(conn->data, "Send failure: %s",
+ Curl_strerror(conn, err));
+ conn->data->state.os_errno = err;
+ *code = CURLE_SEND_ERROR;
+ return 0;
+ }
+ }
+
+ /* socket in default state; enable previously-set socket params */
+ if(setsockopt(sockfd, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL, 0))
+ infof(conn->data,
+ "setsockopt() failed after TCP Fast Open for fd %d, errno: %d",
+ sockfd, SOCKERRNO);
+ }
+ else
#endif
bytes_written = swrite(sockfd, mem, len);
diff --git a/lib/setopt.c b/lib/setopt.c
index 27046768c..127d3385e 100644
--- a/lib/setopt.c
+++ b/lib/setopt.c
@@ -2547,7 +2547,12 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
case CURLOPT_TCP_FASTOPEN:
#if defined(CONNECT_DATA_IDEMPOTENT) || defined(MSG_FASTOPEN) || \
defined(TCP_FASTOPEN_CONNECT)
- data->set.tcp_fastopen = (0 != va_arg(param, long))?TRUE:FALSE;
+ data->set.tcp_fastopen = (0 != va_arg(param, long)) ? TRUE : FALSE;
+#elif defined(HAVE_CONNECTEX)
+ if(Curl_ConnectEx != NULL)
+ data->set.tcp_fastopen = (0 != va_arg(param, long)) ? TRUE : FALSE;
+ else
+ result = CURLE_FAILED_INIT;
#else
result = CURLE_NOT_BUILT_IN;
#endif
diff --git a/lib/urldata.h b/lib/urldata.h
index 8fb2d2894..6079a75b7 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -1035,6 +1035,11 @@ struct connectdata {
char *unix_domain_socket;
bool abstract_unix_socket;
#endif
+
+#ifdef HAVE_CONNECTEX
+ bool fastopen_connected; /* is socket actually connected (via ConnectEx)? */
+ OVERLAPPED fastopen_state; /* state used by a Windows overlapped-I/O func */
+#endif
};
/* The end of connectdata. */