Browse Source

Merge pull request #1965 from linusyang92/mingw-tfo

Support TCP fast open on Windows
pull/1975/head
Max Lv 6 years ago
committed by GitHub
parent
commit
bc0eaacc33
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 263 additions and 6 deletions
  1. 2
      configure.ac
  2. 78
      src/local.c
  3. 8
      src/local.h
  4. 3
      src/resolv.c
  5. 79
      src/server.c
  6. 8
      src/server.h
  7. 57
      src/winsock.c
  8. 34
      src/winsock.h

2
configure.ac

@ -167,7 +167,7 @@ AC_CHECK_HEADERS([net/if.h], [], [],
case $host in
*-mingw*)
AC_DEFINE([CONNECT_IN_PROGRESS], [WSAEWOULDBLOCK], [errno for incomplete non-blocking connect(2)])
AC_CHECK_HEADERS([winsock2.h ws2tcpip.h], [], [AC_MSG_ERROR([Missing MinGW headers])], [])
AC_CHECK_HEADERS([winsock2.h ws2tcpip.h mswsock.h], [], [AC_MSG_ERROR([Missing MinGW headers])], [])
;;
*-linux*)
AC_DEFINE([CONNECT_IN_PROGRESS], [EINPROGRESS], [errno for incomplete non-blocking connect(2)])

78
src/local.c

@ -371,11 +371,56 @@ server_recv_cb(EV_P_ ev_io *w, int revents)
ev_io_start(EV_A_ & remote->send_ctx->io);
ev_timer_start(EV_A_ & remote->send_ctx->watcher);
} else {
int s = -1;
#if defined(MSG_FASTOPEN) && !defined(TCP_FASTOPEN_CONNECT)
int s = -1;
s = sendto(remote->fd, remote->buf->data, remote->buf->len, MSG_FASTOPEN,
(struct sockaddr *)&(remote->addr), remote->addr_len);
#elif defined(TCP_FASTOPEN_WINSOCK)
DWORD s = -1;
DWORD err = 0;
do {
int optval = 1;
// Set fast open option
if (setsockopt(remote->fd, IPPROTO_TCP, TCP_FASTOPEN,
&optval, sizeof(optval)) != 0) {
ERROR("setsockopt");
break;
}
// Load ConnectEx function
LPFN_CONNECTEX ConnectEx = winsock_getconnectex();
if (ConnectEx == NULL) {
LOGE("Cannot load ConnectEx() function");
err = WSAENOPROTOOPT;
break;
}
// ConnectEx requires a bound socket
if (winsock_dummybind(remote->fd,
(struct sockaddr *)&(remote->addr)) != 0) {
ERROR("bind");
break;
}
// Call ConnectEx to send data
memset(&remote->olap, 0, sizeof(remote->olap));
remote->connect_ex_done = 0;
if (ConnectEx(remote->fd, (const struct sockaddr *)&(remote->addr),
remote->addr_len, remote->buf->data, remote->buf->len,
&s, &remote->olap)) {
remote->connect_ex_done = 1;
break;
};
// XXX: ConnectEx pending, check later in remote_send
if (WSAGetLastError() == ERROR_IO_PENDING) {
err = CONNECT_IN_PROGRESS;
break;
}
ERROR("ConnectEx");
} while(0);
// Set error number
if (err) {
SetLastError(err);
}
#else
int s = -1;
#if defined(CONNECT_DATA_IDEMPOTENT)
((struct sockaddr_in *)&(remote->addr))->sin_len = sizeof(struct sockaddr_in);
sa_endpoints_t endpoints;
@ -958,6 +1003,37 @@ remote_send_cb(EV_P_ ev_io *w, int revents)
server_t *server = remote->server;
if (!remote_send_ctx->connected) {
#ifdef TCP_FASTOPEN_WINSOCK
if (fast_open) {
// Check if ConnectEx is done
if (!remote->connect_ex_done) {
DWORD numBytes;
DWORD flags;
// Non-blocking way to fetch ConnectEx result
if (WSAGetOverlappedResult(remote->fd, &remote->olap,
&numBytes, FALSE, &flags)) {
remote->buf->len -= numBytes;
remote->buf->idx = numBytes;
remote->connect_ex_done = 1;
} else if (WSAGetLastError() == WSA_IO_INCOMPLETE) {
// XXX: ConnectEx still not connected, wait for next time
return;
} else {
ERROR("WSAGetOverlappedResult");
// not connected
close_and_free_remote(EV_A_ remote);
close_and_free_server(EV_A_ server);
return;
};
}
// Make getpeername work
if (setsockopt(remote->fd, SOL_SOCKET,
SO_UPDATE_CONNECT_CONTEXT, NULL, 0) != 0) {
ERROR("setsockopt");
}
}
#endif
struct sockaddr_storage addr;
socklen_t len = sizeof addr;
int r = getpeername(remote->fd, (struct sockaddr *)&addr, &len);

8
src/local.h

@ -31,6 +31,10 @@
#include <ev.h>
#endif
#ifdef __MINGW32__
#include "winsock.h"
#endif
#include "crypto.h"
#include "jconf.h"
#include "protocol.h"
@ -85,6 +89,10 @@ typedef struct remote {
int direct;
int addr_len;
uint32_t counter;
#ifdef TCP_FASTOPEN_WINSOCK
OVERLAPPED olap;
int connect_ex_done;
#endif
buffer_t *buf;

3
src/resolv.c

@ -38,6 +38,8 @@
#include <netinet/in.h>
#include <errno.h>
#include <unistd.h>
#else
#include "winsock.h" // Should be before <ares.h>
#endif
#include <ares.h>
@ -52,7 +54,6 @@
#include "resolv.h"
#include "utils.h"
#include "netutils.h"
#include "winsock.h"
#ifdef __MINGW32__
#define CONV_STATE_CB (ares_sock_state_cb)

79
src/server.c

@ -307,7 +307,7 @@ setfastopen(int fd)
int s = 0;
#ifdef TCP_FASTOPEN
if (fast_open) {
#ifdef __APPLE__
#if defined(__APPLE__) || defined(__MINGW32__)
int opt = 1;
#else
int opt = 5;
@ -517,11 +517,55 @@ connect_to_remote(EV_P_ struct addrinfo *res,
remote_t *remote = new_remote(sockfd);
if (fast_open) {
int s = -1;
#if defined(MSG_FASTOPEN) && !defined(TCP_FASTOPEN_CONNECT)
int s = -1;
s = sendto(sockfd, server->buf->data, server->buf->len,
MSG_FASTOPEN, res->ai_addr, res->ai_addrlen);
#elif defined(TCP_FASTOPEN_WINSOCK)
DWORD s = -1;
DWORD err = 0;
do {
int optval = 1;
// Set fast open option
if (setsockopt(sockfd, IPPROTO_TCP, TCP_FASTOPEN,
&optval, sizeof(optval)) != 0) {
ERROR("setsockopt");
break;
}
// Load ConnectEx function
LPFN_CONNECTEX ConnectEx = winsock_getconnectex();
if (ConnectEx == NULL) {
LOGE("Cannot load ConnectEx() function");
err = WSAENOPROTOOPT;
break;
}
// ConnectEx requires a bound socket
if (winsock_dummybind(sockfd, res->ai_addr) != 0) {
ERROR("bind");
break;
}
// Call ConnectEx to send data
memset(&remote->olap, 0, sizeof(remote->olap));
remote->connect_ex_done = 0;
if (ConnectEx(sockfd, res->ai_addr, res->ai_addrlen,
server->buf->data, server->buf->len,
&s, &remote->olap)) {
remote->connect_ex_done = 1;
break;
};
// XXX: ConnectEx pending, check later in remote_send
if (WSAGetLastError() == ERROR_IO_PENDING) {
err = CONNECT_IN_PROGRESS;
break;
}
ERROR("ConnectEx");
} while(0);
// Set error number
if (err) {
SetLastError(err);
}
#else
int s = -1;
#if defined(TCP_FASTOPEN_CONNECT)
int optval = 1;
if(setsockopt(sockfd, IPPROTO_TCP, TCP_FASTOPEN_CONNECT,
@ -1175,6 +1219,37 @@ remote_send_cb(EV_P_ ev_io *w, int revents)
}
if (!remote_send_ctx->connected) {
#ifdef TCP_FASTOPEN_WINSOCK
if (fast_open) {
// Check if ConnectEx is done
if (!remote->connect_ex_done) {
DWORD numBytes;
DWORD flags;
// Non-blocking way to fetch ConnectEx result
if (WSAGetOverlappedResult(remote->fd, &remote->olap,
&numBytes, FALSE, &flags)) {
remote->buf->len -= numBytes;
remote->buf->idx = numBytes;
remote->connect_ex_done = 1;
} else if (WSAGetLastError() == WSA_IO_INCOMPLETE) {
// XXX: ConnectEx still not connected, wait for next time
return;
} else {
ERROR("WSAGetOverlappedResult");
// not connected
close_and_free_remote(EV_A_ remote);
close_and_free_server(EV_A_ server);
return;
};
}
// Make getpeername work
if (setsockopt(remote->fd, SOL_SOCKET,
SO_UPDATE_CONNECT_CONTEXT, NULL, 0) != 0) {
ERROR("setsockopt");
}
}
#endif
struct sockaddr_storage addr;
socklen_t len = sizeof(struct sockaddr_storage);
memset(&addr, 0, len);

8
src/server.h

@ -32,6 +32,10 @@
#include <ev.h>
#endif
#ifdef __MINGW32__
#include "winsock.h"
#endif
#include "crypto.h"
#include "jconf.h"
#include "resolv.h"
@ -104,6 +108,10 @@ typedef struct remote_ctx {
typedef struct remote {
int fd;
#ifdef TCP_FASTOPEN_WINSOCK
OVERLAPPED olap;
int connect_ex_done;
#endif
buffer_t *buf;
struct remote_ctx *recv_ctx;
struct remote_ctx *send_ctx;

57
src/winsock.c

@ -72,4 +72,61 @@ ss_error(const char *s)
}
}
#ifdef TCP_FASTOPEN_WINSOCK
LPFN_CONNECTEX
winsock_getconnectex(void)
{
static LPFN_CONNECTEX pConnectEx = NULL;
if (pConnectEx != NULL) {
return pConnectEx;
}
// Dummy socket for WSAIoctl
SOCKET s = socket(AF_INET, SOCK_STREAM, 0);
if (s == INVALID_SOCKET) {
ERROR("socket");
return NULL;
}
// Load ConnectEx function
GUID guid = WSAID_CONNECTEX;
DWORD numBytes;
int ret = -1;
ret = WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER,
(void *)&guid, sizeof(guid),
(void *)&pConnectEx, sizeof(pConnectEx),
&numBytes, NULL, NULL);
if (ret != 0) {
ERROR("WSAIoctl");
closesocket(s);
return NULL;
}
closesocket(s);
return pConnectEx;
}
int
winsock_dummybind(SOCKET fd, struct sockaddr *sa)
{
struct sockaddr_storage ss;
memset(&ss, 0, sizeof(ss));
if (sa->sa_family == AF_INET) {
struct sockaddr_in *sin = (struct sockaddr_in *)&ss;
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = INADDR_ANY;
} else if (sa->sa_family == AF_INET6) {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ss;
sin6->sin6_family = AF_INET6;
sin6->sin6_addr = in6addr_any;
} else {
return -1;
}
if (bind(fd, (struct sockaddr *)&ss, sizeof(ss)) < 0 &&
WSAGetLastError() != WSAEINVAL) {
return -1;
}
return 0;
}
#endif
#endif // __MINGW32__

34
src/winsock.h

@ -25,6 +25,7 @@
#ifdef __MINGW32__
// Target NT6
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
@ -37,11 +38,13 @@
#define _WIN32_WINNT 0x0600
#endif
// Winsock headers
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <mswsock.h>
// Override error number
// Override POSIX error number
#ifdef errno
#undef errno
#endif
@ -57,6 +60,31 @@
#endif
#define CONNECT_IN_PROGRESS WSAEWOULDBLOCK
#ifdef EOPNOTSUPP
#undef EOPNOTSUPP
#endif
#define EOPNOTSUPP WSAEOPNOTSUPP
#ifdef EPROTONOSUPPORT
#undef EPROTONOSUPPORT
#endif
#define EPROTONOSUPPORT WSAEPROTONOSUPPORT
#ifdef ENOPROTOOPT
#undef ENOPROTOOPT
#endif
#define ENOPROTOOPT WSAENOPROTOOPT
// Check if ConnectEx supported in header
#ifdef WSAID_CONNECTEX
// Hardcode TCP fast open option
#ifndef TCP_FASTOPEN
#define TCP_FASTOPEN 15
#endif
// Enable TFO support
#define TCP_FASTOPEN_WINSOCK 1
#endif
// Override close function
#define close(fd) closesocket(fd)
@ -80,6 +108,10 @@ void ss_error(const char *s);
int setnonblocking(SOCKET socket);
void winsock_init(void);
void winsock_cleanup(void);
#ifdef TCP_FASTOPEN_WINSOCK
LPFN_CONNECTEX winsock_getconnectex(void);
int winsock_dummybind(SOCKET fd, struct sockaddr *sa);
#endif
#endif // __MINGW32__

Loading…
Cancel
Save