Skip to content

Instantly share code, notes, and snippets.

@linusyang
Created March 10, 2018 14:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save linusyang/d72affa997bfb5b9fb1daeb9fc403512 to your computer and use it in GitHub Desktop.
Save linusyang/d72affa997bfb5b9fb1daeb9fc403512 to your computer and use it in GitHub Desktop.
// x86_64-w64-mingw32-gcc tfo.c -o tfo -lws2_32 -static -g -O0
#define TARGET_URL "localhost"
#define TARGET_PORT 8000
#ifndef _WINSOCK_H
#define _WINSOCK_H
#ifdef __MINGW32__
// Target NT6
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0600
#undef _WIN32_WINNT
#endif
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0600
#endif
// Winsock headers
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <mswsock.h>
#include <stdio.h>
#define LOGE(s, args...) fprintf(stderr, s "\n", ##args)
// Override POSIX error number
#ifdef errno
#undef errno
#endif
#define errno WSAGetLastError()
#ifdef EWOULDBLOCK
#undef EWOULDBLOCK
#endif
#define EWOULDBLOCK WSAEWOULDBLOCK
#ifdef CONNECT_IN_PROGRESS
#undef CONNECT_IN_PROGRESS
#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
#ifdef TCP_FASTOPEN
#undef TCP_FASTOPEN
#endif
#define TCP_FASTOPEN 15
// Enable TFO support
#define TCP_FASTOPEN_WINSOCK 1
#endif
// Override close function
#define close(fd) closesocket(fd)
// Override MinGW functions
#define setsockopt(a,b,c,d,e) setsockopt(a,b,c,(const char *)(d),e)
#define inet_ntop(a,b,c,d) inet_ntop(a,(void *)(b),c,d)
// Override Windows built-in functions
#ifdef ERROR
#undef ERROR
#endif
#define ERROR(s) ss_error(s)
void ss_error(const char *s);
// Missing unistd.h functions
#define sleep(x) Sleep((x) * 1000)
// Winsock compatibility functions
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__
#endif // _WINSOCK_H
#define bcopy(s1, s2, n) memmove((s2), (s1), (n))
void
winsock_init(void)
{
int ret;
WSADATA wsa_data;
ret = WSAStartup(MAKEWORD(2, 2), &wsa_data);
if (ret != 0) {
LOGE("Failed to initialize winsock");
}
}
void
winsock_cleanup(void)
{
WSACleanup();
}
void
ss_error(const char *s)
{
char *msg = NULL;
DWORD err = WSAGetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&msg, 0, NULL);
if (msg != NULL) {
// Remove trailing newline character
ssize_t len = strlen(msg) - 1;
if (len >= 0 && msg[len] == '\n') {
msg[len] = '\0';
}
LOGE("%s: [%ld] %s", s, err, msg);
LocalFree(msg);
}
}
#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
int main(void)
{
winsock_init();
struct sockaddr_in addr;
struct hostent *server;
DWORD s = -1;
DWORD err = 0;
OVERLAPPED olap;
SOCKET fd = socket(AF_INET, SOCK_STREAM, 0);
char *data = "Hello!";
int data_len = strlen(data);
server = gethostbyname(TARGET_URL);
addr.sin_family = AF_INET;
memmove((char *)&addr.sin_addr.s_addr,
(char *)server->h_addr, server->h_length);
addr.sin_port = htons(TARGET_PORT);
do {
int optval = 1;
// Set fast open option
if(setsockopt(fd, IPPROTO_TCP, TCP_FASTOPEN,
&optval, sizeof(optval)) != 0) {
ERROR("setsockopt");
err = WSAEOPNOTSUPP;
break;
}
// Load ConnectEx function
LPFN_CONNECTEX ConnectEx = winsock_getconnectex();
if (ConnectEx == NULL) {
LOGE("Cannot load ConnectEx() function");
err = WSAEOPNOTSUPP;
break;
}
// ConnectEx requires a bound socket
if (winsock_dummybind(fd,
(struct sockaddr *)&(addr)) != 0) {
ERROR("bind");
break;
}
// Call ConnectEx to send data
memset(&olap, 0, sizeof(olap));
if (ConnectEx(fd, (const struct sockaddr *)&(addr),
sizeof(addr), data, data_len,
&s, &olap)) {
break;
};
// XXX: ConnectEx pending, check later in remote_send
if (WSAGetLastError() == ERROR_IO_PENDING) {
err = CONNECT_IN_PROGRESS;
DWORD numBytes;
DWORD flags;
// Non-blocking way to fetch ConnectEx result
if (WSAGetOverlappedResult(fd, &olap,
&numBytes, TRUE, &flags)) {
s = numBytes;
err = 0;
break;
} else {
ERROR("WSAGetOverlappedResult");
break;
};
break;
}
ERROR("ConnectEx");
} while(0);
// Set error number
if (err) {
ERROR("EPIC FAIL");
}
LOGE("Sent bytes: %ld", s);
closesocket(fd);
winsock_cleanup();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment