mirror of
https://github.com/shadowsocks/shadowsocks-libev.git
synced 2026-10-21 06:23:52 +00:00
Merge pull request #1965 from linusyang92/mingw-tfo
Support TCP fast open on Windows
This commit is contained in:
+1
-1
@@ -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)])
|
||||
|
||||
+77
-1
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
+2
-1
@@ -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)
|
||||
|
||||
+77
-2
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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__
|
||||
|
||||
+33
-1
@@ -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__
|
||||
|
||||
|
||||
Reference in New Issue
Block a user