-
-
Save Mattosx/5c9b9a43d52d62c2d0e70e806719bf57 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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