diff options
author | Brad G <43330364+bakedpotat0@users.noreply.github.com> | 2018-11-29 12:17:21 -0600 |
---|---|---|
committer | Daniel Stenberg <daniel@haxx.se> | 2018-12-16 18:46:01 +0100 |
commit | 306a47cea67f29a1c7a19a42de85e66acb03f1d7 (patch) | |
tree | 61ffad27ec4f7a2d4e155a64d2fa3ad37b9725b7 | |
parent | 9e6518481cd4e2603b4f9e04d6cfd1a214a72726 (diff) | |
download | curl-TFO-windows.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.h | 6 | ||||
-rw-r--r-- | lib/connect.c | 57 | ||||
-rw-r--r-- | lib/curl_setup.h | 9 | ||||
-rw-r--r-- | lib/easy.c | 36 | ||||
-rw-r--r-- | lib/sendf.c | 31 | ||||
-rw-r--r-- | lib/setopt.c | 7 | ||||
-rw-r--r-- | lib/urldata.h | 5 |
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. */ |