diff --git a/src/library.js b/src/library.js index 94be77488da6a..807f3ed0d91b9 100644 --- a/src/library.js +++ b/src/library.js @@ -3295,6 +3295,7 @@ LibraryManager.library = { // arpa/inet.h // ========================================================================== +#if PROXY_POSIX_SOCKETS == 0 // old ipv4 only functions inet_addr__deps: ['_inet_pton4_raw'], inet_addr: function(ptr) { @@ -4037,6 +4038,8 @@ LibraryManager.library = { 0x0b00000a, 0x0c00000a, 0x0d00000a, 0x0e00000a] /* 0x0100000a is reserved */ }, +#endif // PROXY_POSIX_SOCKETS == 0 + // pwd.h getpwnam: function() { throw 'getpwnam: TODO' }, diff --git a/src/settings.js b/src/settings.js index 1013484e23e58..593bccb45f935 100644 --- a/src/settings.js +++ b/src/settings.js @@ -363,6 +363,9 @@ var SOCKET_WEBRTC = 0; // where addr and port are derived from the socket connect/bind/accept calls. var WEBSOCKET_URL = 'ws://'; +// If 1, the POSIX sockets API uses a proxy bridge to proxy sockets calls +var PROXY_POSIX_SOCKETS = 0; + // A string containing a comma separated list of WebSocket subprotocols // as would be present in the Sec-WebSocket-Protocol header. var WEBSOCKET_SUBPROTOCOL = 'binary'; diff --git a/system/include/emscripten/posix_socket.h b/system/include/emscripten/posix_socket.h new file mode 100644 index 0000000000000..c07c7999213ab --- /dev/null +++ b/system/include/emscripten/posix_socket.h @@ -0,0 +1,13 @@ +#pragma once + +#include "websocket.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern EMSCRIPTEN_RESULT emscripten_init_websocket_to_posix_socket_bridge(const char *bridgeUrl); + +#ifdef __cplusplus +} +#endif diff --git a/system/lib/websocket/websocket_to_posix_socket.cpp b/system/lib/websocket/websocket_to_posix_socket.cpp new file mode 100644 index 0000000000000..a186c0f172824 --- /dev/null +++ b/system/lib/websocket/websocket_to_posix_socket.cpp @@ -0,0 +1,962 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +// Uncomment to enable debug printing +// #define POSIX_SOCKET_DEBUG + +// Uncomment to enable more verbose debug printing (in addition to uncommenting POSIX_SOCKET_DEBUG) +// #define POSIX_SOCKET_DEEP_DEBUG + +#define MIN(a,b) (((a)<(b))?(a):(b)) + +extern "C" +{ + +static void *memdup(const void *ptr, size_t sz) +{ + if (!ptr) return 0; + void *dup = malloc(sz); + if (dup) memcpy(dup, ptr, sz); + return dup; +} + +// Each proxied socket call has at least the following data. +struct SocketCallHeader +{ + int callId; + int function; +}; + +// Each socket call returns at least the following data. +struct SocketCallResultHeader +{ + int callId; + int ret; + int errno_; + // Buffer can contain more data here, conceptually: + // uint8_t extraData[]; +}; + +struct PosixSocketCallResult +{ + PosixSocketCallResult *next; + int callId; + int operationCompleted; + + // Before the call has finished, this field represents the minimum expected number of bytes that server will need to report back. + // After the call has finished, this field reports back the number of bytes pointed to by data, >= the expected value. + int bytes; + + // Result data: + SocketCallResultHeader *data; +}; + +// Shield multithreaded accesses to POSIX sockets functions in the program, namely the two variables 'bridgeSocket' and 'callResultHead' below. +static pthread_mutex_t bridgeLock = PTHREAD_MUTEX_INITIALIZER; + +// Socket handle for the connection from browser WebSocket to the sockets bridge proxy server. +static EMSCRIPTEN_WEBSOCKET_T bridgeSocket = (EMSCRIPTEN_WEBSOCKET_T)0; + +// Stores a linked list of all currently pending sockets operations (ones that are waiting for a reply back from the sockets proxy server) +static PosixSocketCallResult *callResultHead = 0; + +static PosixSocketCallResult *allocate_call_result(int expectedBytes) +{ + pthread_mutex_lock(&bridgeLock); // Guard multithreaded access to 'callResultHead' and 'nextId' below + PosixSocketCallResult *b = (PosixSocketCallResult*)(malloc(sizeof(PosixSocketCallResult))); + if (!b) + { +#ifdef POSIX_SOCKET_DEBUG + emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_ERROR | EM_LOG_JS_STACK, "allocate_call_result: Failed to allocate call result struct of size %d bytes!\n", (int)sizeof(PosixSocketCallResult)); +#endif + pthread_mutex_unlock(&bridgeLock); + return 0; + } + static int nextId = 1; + b->callId = nextId++; +#ifdef POSIX_SOCKET_DEEP_DEBUG + emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_ERROR | EM_LOG_JS_STACK, "allocate_call_result: allocated call ID %d\n", b->callId); +#endif + b->bytes = expectedBytes; + b->data = 0; + b->operationCompleted = 0; + b->next = 0; + + if (!callResultHead) + callResultHead = b; + else + { + PosixSocketCallResult *t = callResultHead; + while(t->next) t = t->next; + t->next = b; + } + pthread_mutex_unlock(&bridgeLock); + return b; +} + +static void free_call_result(PosixSocketCallResult *buffer) +{ +#ifdef POSIX_SOCKET_DEEP_DEBUG + if (buffer) + emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_ERROR | EM_LOG_JS_STACK, "free_call_result: freed call ID %d\n", buffer->callId); +#endif + + if (buffer->data) free(buffer->data); + free(buffer); +} + +PosixSocketCallResult *pop_call_result(int callId) +{ + pthread_mutex_lock(&bridgeLock); // Guard multithreaded access to 'callResultHead' + PosixSocketCallResult *prev = 0; + PosixSocketCallResult *b = callResultHead; + while(b) + { + if (b->callId == callId) + { + if (prev) prev->next = b->next; + else callResultHead = b->next; + b->next = 0; +#ifdef POSIX_SOCKET_DEEP_DEBUG + emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_ERROR | EM_LOG_JS_STACK, "pop_call_result: Removed call ID %d from pending sockets call queue\n", callId); +#endif + pthread_mutex_unlock(&bridgeLock); + return b; + } + prev = b; + b = b->next; + } + pthread_mutex_unlock(&bridgeLock); +#ifdef POSIX_SOCKET_DEBUG + emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_ERROR | EM_LOG_JS_STACK, "pop_call_result: No such call ID %d in pending sockets call queue!\n", callId); +#endif + return 0; +} + +void wait_for_call_result(PosixSocketCallResult *b) +{ +#ifdef POSIX_SOCKET_DEEP_DEBUG + emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_ERROR | EM_LOG_JS_STACK, "wait_for_call_result: Waiting for call ID %d\n", b->callId); +#endif + while(!emscripten_atomic_load_u32(&b->operationCompleted)) + emscripten_futex_wait(&b->operationCompleted, 0, 1e9); +#ifdef POSIX_SOCKET_DEEP_DEBUG + emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_ERROR | EM_LOG_JS_STACK, "wait_for_call_result: Waiting for call ID %d done\n", b->callId); +#endif +} + +static EM_BOOL bridge_socket_on_message(int eventType, const EmscriptenWebSocketMessageEvent *websocketEvent, void *userData) +{ + if (websocketEvent->numBytes < sizeof(SocketCallResultHeader)) + { + emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_ERROR | EM_LOG_JS_STACK, "Received corrupt WebSocket result message with size %d, not enough space for header, at least %d bytes!\n", (int)websocketEvent->numBytes, (int)sizeof(SocketCallResultHeader)); + return EM_TRUE; + } + + SocketCallResultHeader *header = (SocketCallResultHeader *)websocketEvent->data; + +#ifdef POSIX_SOCKET_DEEP_DEBUG + emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_ERROR | EM_LOG_JS_STACK, "POSIX sockets bridge received message on thread %p, size: %d bytes, for call ID %d\n", (void*)pthread_self(), websocketEvent->numBytes, header->callId); +#endif + + PosixSocketCallResult *b = pop_call_result(header->callId); + if (!b) + { + emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_ERROR | EM_LOG_JS_STACK, "Received WebSocket result message to unknown call ID %d!\n", (int)header->callId); + // TODO: Craft a socket result that signifies a failure, and wake the listening thread + return EM_TRUE; + } + + if (websocketEvent->numBytes < b->bytes) + { + emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_ERROR | EM_LOG_JS_STACK, "Received corrupt WebSocket result message with size %d, expected at least %d bytes!\n", (int)websocketEvent->numBytes, b->bytes); + // TODO: Craft a socket result that signifies a failure, and wake the listening thread + return EM_TRUE; + } + + b->bytes = websocketEvent->numBytes; + b->data = (SocketCallResultHeader*)memdup(websocketEvent->data, websocketEvent->numBytes); + + if (!b->data) + { + emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_ERROR | EM_LOG_JS_STACK, "Out of memory, tried to allocate %d bytes!\n", websocketEvent->numBytes); + return EM_TRUE; + } + + if (b->operationCompleted != 0) + { + emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_ERROR | EM_LOG_JS_STACK, "Memory corruption(?): the received result for completed operation at address %p was expected to be in state 0, but it was at state %d!\n", &b->operationCompleted, (int)b->operationCompleted); + } + + emscripten_atomic_store_u32(&b->operationCompleted, 1); + emscripten_futex_wake(&b->operationCompleted, 0x7FFFFFFF); + + return EM_TRUE; +} + +EMSCRIPTEN_WEBSOCKET_T emscripten_init_websocket_to_posix_socket_bridge(const char *bridgeUrl) +{ +#ifdef POSIX_SOCKET_DEBUG + emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_JS_STACK, "emscripten_init_websocket_to_posix_socket_bridge(bridgeUrl=\"%s\")\n", bridgeUrl); +#endif + pthread_mutex_lock(&bridgeLock); // Guard multithreaded access to 'bridgeSocket' + if (bridgeSocket) + { +#ifdef POSIX_SOCKET_DEBUG + emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_WARN | EM_LOG_JS_STACK, "emscripten_init_websocket_to_posix_socket_bridge(bridgeUrl=\"%s\"): A previous bridge socket connection handle existed! Forcibly tearing old connection down.\n", bridgeUrl); +#endif + emscripten_websocket_close(bridgeSocket, 0, 0); + emscripten_websocket_delete(bridgeSocket); + bridgeSocket = 0; + } + EmscriptenWebSocketCreateAttributes attr; + emscripten_websocket_init_create_attributes(&attr); + attr.url = bridgeUrl; + bridgeSocket = emscripten_websocket_new(&attr); + emscripten_websocket_set_onmessage_callback_on_thread(bridgeSocket, 0, bridge_socket_on_message, EM_CALLBACK_THREAD_CONTEXT_MAIN_BROWSER_THREAD); + + pthread_mutex_unlock(&bridgeLock); + return bridgeSocket; +} + +#define POSIX_SOCKET_MSG_SOCKET 1 +#define POSIX_SOCKET_MSG_SOCKETPAIR 2 +#define POSIX_SOCKET_MSG_SHUTDOWN 3 +#define POSIX_SOCKET_MSG_BIND 4 +#define POSIX_SOCKET_MSG_CONNECT 5 +#define POSIX_SOCKET_MSG_LISTEN 6 +#define POSIX_SOCKET_MSG_ACCEPT 7 +#define POSIX_SOCKET_MSG_GETSOCKNAME 8 +#define POSIX_SOCKET_MSG_GETPEERNAME 9 +#define POSIX_SOCKET_MSG_SEND 10 +#define POSIX_SOCKET_MSG_RECV 11 +#define POSIX_SOCKET_MSG_SENDTO 12 +#define POSIX_SOCKET_MSG_RECVFROM 13 +#define POSIX_SOCKET_MSG_SENDMSG 14 +#define POSIX_SOCKET_MSG_RECVMSG 15 +#define POSIX_SOCKET_MSG_GETSOCKOPT 16 +#define POSIX_SOCKET_MSG_SETSOCKOPT 17 +#define POSIX_SOCKET_MSG_GETADDRINFO 18 +#define POSIX_SOCKET_MSG_GETNAMEINFO 19 + +#define MAX_SOCKADDR_SIZE 256 +#define MAX_OPTIONVALUE_SIZE 16 + +int socket(int domain, int type, int protocol) +{ +#ifdef POSIX_SOCKET_DEBUG + emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_ERROR | EM_LOG_JS_STACK, "socket(domain=%d,type=%d,protocol=%d) on thread %p\n", domain, type, protocol, (void*)pthread_self()); +#endif + + struct { + SocketCallHeader header; + int domain; + int type; + int protocol; + } d; + + PosixSocketCallResult *b = allocate_call_result(sizeof(SocketCallResultHeader)); + d.header.callId = b->callId; + d.header.function = POSIX_SOCKET_MSG_SOCKET; + d.domain = domain; + d.type = type; + d.protocol = protocol; + emscripten_websocket_send_binary(bridgeSocket, &d, sizeof(d)); + + wait_for_call_result(b); + int ret = b->data->ret; + if (ret < 0) errno = b->data->errno_; + free_call_result(b); + return ret; +} + +int socketpair(int domain, int type, int protocol, int socket_vector[2]) +{ +#ifdef POSIX_SOCKET_DEBUG + emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_ERROR | EM_LOG_JS_STACK, "socketpair(domain=%d,type=%d,protocol=%d, socket_vector=[%d,%d])\n", domain, type, protocol, socket_vector[0], socket_vector[1]); +#endif + + struct { + SocketCallHeader header; + int domain; + int type; + int protocol; + } d; + + struct Result { + SocketCallResultHeader header; + int sv[2]; + }; + + PosixSocketCallResult *b = allocate_call_result(sizeof(Result)); + d.header.callId = b->callId; + d.header.function = POSIX_SOCKET_MSG_SOCKETPAIR; + d.domain = domain; + d.type = type; + d.protocol = protocol; + emscripten_websocket_send_binary(bridgeSocket, &d, sizeof(d)); + + wait_for_call_result(b); + int ret = b->data->ret; + if (ret == 0) + { + Result *r = (Result*)b->data; + socket_vector[0] = r->sv[0]; + socket_vector[1] = r->sv[1]; + } + else + { + errno = b->data->errno_; + } + free_call_result(b); + return ret; +} + +int shutdown(int socket, int how) +{ +#ifdef POSIX_SOCKET_DEBUG + emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_ERROR | EM_LOG_JS_STACK, "shutdown(socket=%d,how=%d)\n", socket, how); +#endif + + struct { + SocketCallHeader header; + int socket; + int how; + } d; + + PosixSocketCallResult *b = allocate_call_result(sizeof(SocketCallResultHeader)); + d.header.callId = b->callId; + d.header.function = POSIX_SOCKET_MSG_SHUTDOWN; + d.socket = socket; + d.how = how; + emscripten_websocket_send_binary(bridgeSocket, &d, sizeof(d)); + + wait_for_call_result(b); + int ret = b->data->ret; + if (ret != 0) errno = b->data->errno_; + free_call_result(b); + return ret; +} + +int bind(int socket, const struct sockaddr *address, socklen_t address_len) +{ +#ifdef POSIX_SOCKET_DEBUG + emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_ERROR | EM_LOG_JS_STACK, "bind(socket=%d,address=%p,address_len=%d)\n", socket, address, address_len); +#endif + + struct Data { + SocketCallHeader header; + int socket; + uint32_t/*socklen_t*/ address_len; + uint8_t address[]; + }; + int numBytes = sizeof(Data) + address_len; + Data *d = (Data*)malloc(numBytes); + + PosixSocketCallResult *b = allocate_call_result(sizeof(SocketCallResultHeader)); + d->header.callId = b->callId; + d->header.function = POSIX_SOCKET_MSG_BIND; + d->socket = socket; + d->address_len = address_len; + if (address) memcpy(d->address, address, address_len); + else memset(d->address, 0, address_len); + emscripten_websocket_send_binary(bridgeSocket, d, numBytes); + + wait_for_call_result(b); + int ret = b->data->ret; + if (ret != 0) errno = b->data->errno_; + free_call_result(b); + + free(d); + return ret; +} + +int connect(int socket, const struct sockaddr *address, socklen_t address_len) +{ +#ifdef POSIX_SOCKET_DEBUG + emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_ERROR | EM_LOG_JS_STACK, "connect(socket=%d,address=%p,address_len=%d)\n", socket, address, address_len); +#endif + + struct Data { + SocketCallHeader header; + int socket; + uint32_t/*socklen_t*/ address_len; + uint8_t address[]; + }; + int numBytes = sizeof(Data) + address_len; + Data *d = (Data*)malloc(numBytes); + + PosixSocketCallResult *b = allocate_call_result(sizeof(SocketCallResultHeader)); + d->header.callId = b->callId; + d->header.function = POSIX_SOCKET_MSG_CONNECT; + d->socket = socket; + d->address_len = address_len; + if (address) memcpy(d->address, address, address_len); + else memset(d->address, 0, address_len); + emscripten_websocket_send_binary(bridgeSocket, d, numBytes); + + wait_for_call_result(b); + int ret = b->data->ret; + if (ret != 0) errno = b->data->errno_; + free_call_result(b); + + free(d); + return ret; +} + +int listen(int socket, int backlog) +{ +#ifdef POSIX_SOCKET_DEBUG + emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_ERROR | EM_LOG_JS_STACK, "listen(socket=%d,backlog=%d)\n", socket, backlog); +#endif + + struct { + SocketCallHeader header; + int socket; + int backlog; + } d; + + PosixSocketCallResult *b = allocate_call_result(sizeof(SocketCallResultHeader)); + d.header.callId = b->callId; + d.header.function = POSIX_SOCKET_MSG_LISTEN; + d.socket = socket; + d.backlog = backlog; + emscripten_websocket_send_binary(bridgeSocket, &d, sizeof(d)); + + wait_for_call_result(b); + int ret = b->data->ret; + if (ret != 0) errno = b->data->errno_; + free_call_result(b); + return ret; +} + +int accept(int socket, struct sockaddr *address, socklen_t *address_len) +{ +#ifdef POSIX_SOCKET_DEBUG + emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_ERROR | EM_LOG_JS_STACK, "accept(socket=%d,address=%p,address_len=%p)\n", socket, address, address_len); +#endif + + struct { + SocketCallHeader header; + int socket; + uint32_t/*socklen_t*/ address_len; + } d; + + PosixSocketCallResult *b = allocate_call_result(sizeof(SocketCallResultHeader)); + d.header.callId = b->callId; + d.header.function = POSIX_SOCKET_MSG_ACCEPT; + d.socket = socket; + d.address_len = address_len ? *address_len : 0; + emscripten_websocket_send_binary(bridgeSocket, &d, sizeof(d)); + + struct Result { + SocketCallResultHeader header; + int address_len; + uint8_t address[]; + }; + + wait_for_call_result(b); + int ret = b->data->ret; + if (ret == 0) + { + Result *r = (Result*)b->data; + int realAddressLen = MIN(b->bytes - sizeof(Result), r->address_len); + int copiedAddressLen = MIN(*address_len, realAddressLen); + if (address) memcpy(address, r->address, copiedAddressLen); + if (address_len) *address_len = realAddressLen; + } + else + { + errno = b->data->errno_; + } + free_call_result(b); + return ret; +} + +int getsockname(int socket, struct sockaddr *address, socklen_t *address_len) +{ +#ifdef POSIX_SOCKET_DEBUG + emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_ERROR | EM_LOG_JS_STACK, "getsockname(socket=%d,address=%p,address_len=%p)\n", socket, address, address_len); +#endif + + struct { + SocketCallHeader header; + int socket; + uint32_t/*socklen_t*/ address_len; + } d; + + struct Result { + SocketCallResultHeader header; + int address_len; + uint8_t address[]; + }; + + PosixSocketCallResult *b = allocate_call_result(sizeof(Result)); + d.header.callId = b->callId; + d.header.function = POSIX_SOCKET_MSG_GETSOCKNAME; + d.socket = socket; + d.address_len = *address_len; + emscripten_websocket_send_binary(bridgeSocket, &d, sizeof(d) + *address_len - MAX_SOCKADDR_SIZE); + + wait_for_call_result(b); + int ret = b->data->ret; + if (ret == 0) + { + Result *r = (Result*)b->data; + int realAddressLen = MIN(b->bytes - sizeof(Result), r->address_len); + int copiedAddressLen = MIN(*address_len, realAddressLen); + if (address) memcpy(address, r->address, copiedAddressLen); + if (address_len) *address_len = realAddressLen; + } + else + { + errno = b->data->errno_; + } + free_call_result(b); + return ret; +} + +int getpeername(int socket, struct sockaddr *address, socklen_t *address_len) +{ +#ifdef POSIX_SOCKET_DEBUG + emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_ERROR | EM_LOG_JS_STACK, "getpeername(socket=%d,address=%p,address_len=%p)\n", socket, address, address_len); +#endif + + struct { + SocketCallHeader header; + int socket; + uint32_t/*socklen_t*/ address_len; + } d; + + struct Result { + SocketCallResultHeader header; + int address_len; + uint8_t address[]; + }; + + PosixSocketCallResult *b = allocate_call_result(sizeof(Result)); + d.header.callId = b->callId; + d.header.function = POSIX_SOCKET_MSG_GETPEERNAME; + d.socket = socket; + d.address_len = *address_len; + emscripten_websocket_send_binary(bridgeSocket, &d, sizeof(d) + *address_len - MAX_SOCKADDR_SIZE); + + wait_for_call_result(b); + int ret = b->data->ret; + if (ret == 0) + { + Result *r = (Result*)b->data; + int realAddressLen = MIN(b->bytes - sizeof(Result), r->address_len); + int copiedAddressLen = MIN(*address_len, realAddressLen); + if (address) memcpy(address, r->address, copiedAddressLen); + if (address_len) *address_len = realAddressLen; + } + else + { + errno = b->data->errno_; + } + free_call_result(b); + return ret; +} + +ssize_t send(int socket, const void *message, size_t length, int flags) +{ +#ifdef POSIX_SOCKET_DEBUG + emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_ERROR | EM_LOG_JS_STACK, "send(socket=%d,message=%p,length=%zd,flags=%d)\n", socket, message, length, flags); +#endif + + struct MSG { + SocketCallHeader header; + int socket; + uint32_t/*size_t*/ length; + int flags; + uint8_t message[]; + }; + size_t sz = sizeof(MSG)+length; + MSG *d = (MSG*)malloc(sz); + + PosixSocketCallResult *b = allocate_call_result(sizeof(SocketCallResultHeader)); + d->header.callId = b->callId; + d->header.function = POSIX_SOCKET_MSG_SEND; + d->socket = socket; + d->length = length; + d->flags = flags; + if (message) memcpy(d->message, message, length); + else memset(d->message, 0, length); + emscripten_websocket_send_binary(bridgeSocket, d, sz); + + wait_for_call_result(b); + int ret = b->data->ret; + if (ret < 0) errno = b->data->errno_; + free_call_result(b); + + free(d); + return ret; +} + +ssize_t recv(int socket, void *buffer, size_t length, int flags) +{ +#ifdef POSIX_SOCKET_DEBUG + emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_ERROR | EM_LOG_JS_STACK, "recv(socket=%d,buffer=%p,length=%zd,flags=%d)\n", socket, buffer, length, flags); +#endif + + struct { + SocketCallHeader header; + int socket; + uint32_t/*size_t*/ length; + int flags; + } d; + + PosixSocketCallResult *b = allocate_call_result(sizeof(SocketCallResultHeader)); + d.header.callId = b->callId; + d.header.function = POSIX_SOCKET_MSG_RECV; + d.socket = socket; + d.length = length; + d.flags = flags; + emscripten_websocket_send_binary(bridgeSocket, &d, sizeof(d)); + + wait_for_call_result(b); + int ret = b->data->ret; + if (ret >= 0) + { + struct Result { + SocketCallResultHeader header; + uint8_t data[]; + }; + Result *r = (Result*)b->data; + if (buffer) memcpy(buffer, r->data, MIN(ret, length)); + } + else + { + errno = b->data->errno_; + } + free_call_result(b); + + return ret; +} + +ssize_t sendto(int socket, const void *message, size_t length, int flags, const struct sockaddr *dest_addr, socklen_t dest_len) +{ +#ifdef POSIX_SOCKET_DEBUG + emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_ERROR | EM_LOG_JS_STACK, "sendto(socket=%d,message=%p,length=%zd,flags=%d,dest_addr=%p,dest_len=%d)\n", socket, message, length, flags, dest_addr, dest_len); +#endif + + struct MSG { + SocketCallHeader header; + int socket; + uint32_t/*size_t*/ length; + int flags; + uint32_t/*socklen_t*/ dest_len; + uint8_t dest_addr[MAX_SOCKADDR_SIZE]; + uint8_t message[]; + }; + size_t sz = sizeof(MSG)+length; + MSG *d = (MSG*)malloc(sz); + + PosixSocketCallResult *b = allocate_call_result(sizeof(SocketCallResultHeader)); + d->header.callId = b->callId; + d->header.function = POSIX_SOCKET_MSG_SENDTO; + d->socket = socket; + d->length = length; + d->flags = flags; + d->dest_len = dest_len; + memset(d->dest_addr, 0, sizeof(d->dest_addr)); + if (dest_addr) memcpy(d->dest_addr, dest_addr, dest_len); + if (message) memcpy(d->message, message, length); + else memset(d->message, 0, length); + emscripten_websocket_send_binary(bridgeSocket, d, sz); + + wait_for_call_result(b); + int ret = b->data->ret; + if (ret < 0) errno = b->data->errno_; + free_call_result(b); + + free(d); + return ret; +} + +ssize_t recvfrom(int socket, void *buffer, size_t length, int flags, struct sockaddr *address, socklen_t *address_len) +{ +#ifdef POSIX_SOCKET_DEBUG + emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_ERROR | EM_LOG_JS_STACK, "recvfrom(socket=%d,buffer=%p,length=%zd,flags=%d,address=%p,address_len=%p)\n", socket, buffer, length, flags, address, address_len); +#endif + + struct { + SocketCallHeader header; + int socket; + uint32_t/*size_t*/ length; + int flags; + uint32_t/*socklen_t*/ address_len; + } d; + + PosixSocketCallResult *b = allocate_call_result(sizeof(SocketCallResultHeader)); + d.header.callId = b->callId; + d.header.function = POSIX_SOCKET_MSG_RECVFROM; + d.socket = socket; + d.length = length; + d.flags = flags; + d.address_len = *address_len; + emscripten_websocket_send_binary(bridgeSocket, &d, sizeof(d)); + + wait_for_call_result(b); + int ret = b->data->ret; + if (ret >= 0) + { + struct Result { + SocketCallResultHeader header; + int data_len; + int address_len; // N.B. this is the reported address length of the sender, that may be larger than what is actually serialized to this message. + uint8_t data_and_address[]; + }; + Result *r = (Result*)b->data; + if (buffer) memcpy(buffer, r->data_and_address, MIN(r->data_len, length)); + int copiedAddressLen = MIN((address_len ? *address_len : 0), r->address_len); + if (address) memcpy(address, r->data_and_address + r->data_len, copiedAddressLen); + if (address_len) *address_len = r->address_len; + } + else + { + errno = b->data->errno_; + } + free_call_result(b); + + return ret; +} + +ssize_t sendmsg(int socket, const struct msghdr *message, int flags) +{ +#ifdef POSIX_SOCKET_DEBUG + emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_ERROR | EM_LOG_JS_STACK, "sendmsg(socket=%d,message=%p,flags=%d)\n", socket, message, flags); +#endif + + exit(1); // TODO + return 0; +} + +ssize_t recvmsg(int socket, struct msghdr *message, int flags) +{ +#ifdef POSIX_SOCKET_DEBUG + emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_ERROR | EM_LOG_JS_STACK, "recvmsg(socket=%d,message=%p,flags=%d)\n", socket, message, flags); +#endif + + exit(1); // TODO + return 0; +} + +int getsockopt(int socket, int level, int option_name, void *option_value, socklen_t *option_len) +{ +#ifdef POSIX_SOCKET_DEBUG + emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_ERROR | EM_LOG_JS_STACK, "getsockopt(socket=%d,level=%d,option_name=%d,option_value=%p,option_len=%p)\n", socket, level, option_name, option_value, option_len); +#endif + + struct { + SocketCallHeader header; + int socket; + int level; + int option_name; + uint32_t/*socklen_t*/ option_len; + } d; + + struct Result { + SocketCallResultHeader header; + uint8_t option_value[]; + }; + + PosixSocketCallResult *b = allocate_call_result(sizeof(Result)); + d.header.callId = b->callId; + d.header.function = POSIX_SOCKET_MSG_GETSOCKOPT; + d.socket = socket; + d.level = level; + d.option_name = option_name; + d.option_len = *option_len; + emscripten_websocket_send_binary(bridgeSocket, &d, sizeof(d)); + + wait_for_call_result(b); + int ret = b->data->ret; + if (ret == 0) + { + Result *r = (Result*)b->data; + int optLen = b->bytes - sizeof(Result); + if (option_value) memcpy(option_value, r->option_value, MIN(*option_len, optLen)); + if (option_len) *option_len = optLen; + } + else + { + errno = b->data->errno_; + } + free_call_result(b); + return ret; +} + +int setsockopt(int socket, int level, int option_name, const void *option_value, socklen_t option_len) +{ +#ifdef POSIX_SOCKET_DEBUG + emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_ERROR | EM_LOG_JS_STACK, "setsockopt(socket=%d,level=%d,option_name=%d,option_value=%p,option_len=%d)\n", socket, level, option_name, option_value, option_len); +#endif + + struct MSG { + SocketCallHeader header; + int socket; + int level; + int option_name; + int option_len; + uint8_t option_value[]; + }; + int messageSize = sizeof(MSG) + option_len; + MSG *d = (MSG*)malloc(messageSize); + + PosixSocketCallResult *b = allocate_call_result(sizeof(SocketCallResultHeader)); + d->header.callId = b->callId; + d->header.function = POSIX_SOCKET_MSG_SETSOCKOPT; + d->socket = socket; + d->level = level; + d->option_name = option_name; + if (option_value) memcpy(d->option_value, option_value, option_len); + else memset(d->option_value, 0, option_len); + d->option_len = option_len; + emscripten_websocket_send_binary(bridgeSocket, d, messageSize); + + wait_for_call_result(b); + int ret = b->data->ret; + if (ret != 0) errno = b->data->errno_; + free_call_result(b); + + free(d); + return ret; +} + +// Host name resolution: + +int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res) +{ +#define MAX_NODE_LEN 2048 +#define MAX_SERVICE_LEN 128 + + struct { + SocketCallHeader header; + char node[MAX_NODE_LEN]; // Arbitrary max length limit + char service[MAX_SERVICE_LEN]; // Arbitrary max length limit + int hasHints; + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + } d; + + struct ResAddrinfo + { + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + int/*socklen_t*/ ai_addrlen; + uint8_t /*sockaddr **/ ai_addr[]; + }; + + struct Result { + SocketCallResultHeader header; + char ai_canonname[MAX_NODE_LEN]; + int addrCount; + uint8_t /*ResAddrinfo[]*/ addr[]; + }; + + memset(&d, 0, sizeof(d)); + PosixSocketCallResult *b = allocate_call_result(sizeof(Result)); + d.header.callId = b->callId; + d.header.function = POSIX_SOCKET_MSG_GETADDRINFO; + if (node) + { + assert(strlen(node) <= MAX_NODE_LEN-1); + strncpy(d.node, node, MAX_NODE_LEN-1); + } + if (service) + { + assert(strlen(service) <= MAX_SERVICE_LEN-1); + strncpy(d.service, service, MAX_SERVICE_LEN-1); + } + d.hasHints = !!hints; + if (hints) + { + d.ai_flags = hints->ai_flags; + d.ai_family = hints->ai_family; + d.ai_socktype = hints->ai_socktype; + d.ai_protocol = hints->ai_protocol; + } + +#ifdef POSIX_SOCKET_DEBUG + emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_ERROR | EM_LOG_JS_STACK, "getaddrinfo(node=%s,service=%s,hasHints=%d,ai_flags=%d,ai_family=%d,ai_socktype=%d,ai_protocol=%d,hintsPtr=%p,resPtr=%p)\n", node, service, d.hasHints, d.ai_flags, d.ai_family, d.ai_socktype, d.ai_protocol, hints, res); +#endif + + emscripten_websocket_send_binary(bridgeSocket, &d, sizeof(d)); + + wait_for_call_result(b); + int ret = b->data->ret; + emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_ERROR | EM_LOG_JS_STACK, "getaddrinfo finished, ret=%d\n", ret); + if (ret == 0) + { + if (res) + { + Result *r = (Result*)b->data; + uint8_t *raiAddr = (uint8_t*)&r->addr[0]; + addrinfo *results = (addrinfo*)malloc(sizeof(addrinfo)*r->addrCount); + emscripten_log(EM_LOG_NO_PATHS | EM_LOG_CONSOLE | EM_LOG_ERROR | EM_LOG_JS_STACK, "%d results\n", r->addrCount); + for(size_t i = 0; i < r->addrCount; ++i) + { + ResAddrinfo *rai = (ResAddrinfo*)raiAddr; + results[i].ai_flags = rai->ai_flags; + results[i].ai_family = rai->ai_family; + results[i].ai_socktype = rai->ai_socktype; + results[i].ai_protocol = rai->ai_protocol; + results[i].ai_addrlen = rai->ai_addrlen; + results[i].ai_addr = (sockaddr *)malloc(results[i].ai_addrlen); + memcpy(results[i].ai_addr, rai->ai_addr, results[i].ai_addrlen); + results[i].ai_canonname = (i == 0) ? strdup(r->ai_canonname) : 0; + results[i].ai_next = i+1 < r->addrCount ? &results[i+1] : 0; + fprintf(stderr, "%d: ai_flags=%d, ai_family=%d, ai_socktype=%d, ai_protocol=%d, ai_addrlen=%d, ai_addr=", (int)i, results[i].ai_flags, results[i].ai_family, results[i].ai_socktype, results[i].ai_protocol, results[i].ai_addrlen); + for(size_t j = 0; j < results[i].ai_addrlen; ++j) + fprintf(stderr, " %02X", ((uint8_t*)results[i].ai_addr)[j]); + fprintf(stderr, ",ai_canonname=%s, ai_next=%p\n", results[i].ai_canonname, results[i].ai_next); + raiAddr += sizeof(ResAddrinfo) + rai->ai_addrlen; + } + *res = results; + } + } + else + { + errno = b->data->errno_; + if (res) *res = 0; + } + free_call_result(b); + + return ret; +} + +void freeaddrinfo(struct addrinfo *res) +{ + for(addrinfo *r = res; r; r = r->ai_next) + { + free(r->ai_canonname); + free(r->ai_addr); + } + free(res); +} + +int getnameinfo(const struct sockaddr *addr, socklen_t addrlen, char *host, socklen_t hostlen, char *serv, socklen_t servlen, int flags) +{ + // POSIX_SOCKET_MSG_GETNAMEINFO +} + +// const char *gai_strerror(int); + + + +} // ~extern "C" diff --git a/tests/websocket/tcp_client.cpp b/tests/websocket/tcp_client.cpp new file mode 100644 index 0000000000000..96e59ae60f2d5 --- /dev/null +++ b/tests/websocket/tcp_client.cpp @@ -0,0 +1,120 @@ +// TCP client that sends a few messages to a server and prints out the replies +#include +#include +#include +#include +#include +#include +#include + +#ifdef __EMSCRIPTEN__ +#include +#include +#include + +EMSCRIPTEN_WEBSOCKET_T bridgeSocket = 0; + +extern "C" { +EMSCRIPTEN_WEBSOCKET_T emscripten_init_websocket_to_posix_socket_bridge(const char *bridgeUrl); +} +#endif + +int lookup_host(const char *host) +{ + struct addrinfo hints, *res; + int errcode; + char addrstr[100]; + void *ptr; + + memset(&hints, 0, sizeof (hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags |= AI_CANONNAME; + + errcode = getaddrinfo(host, NULL, &hints, &res); + if (errcode != 0) + { + printf("getaddrinfo failed!\n"); + return -1; + } + + printf("Host: %s\n", host); + while (res) + { + inet_ntop(res->ai_family, res->ai_addr->sa_data, addrstr, 100); + + switch (res->ai_family) + { + case AF_INET: + ptr = &((struct sockaddr_in *)res->ai_addr)->sin_addr; + break; + case AF_INET6: + ptr = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; + break; + } + inet_ntop(res->ai_family, ptr, addrstr, 100); + printf("IPv%d address: %s (%s)\n", res->ai_family == PF_INET6 ? 6 : 4, addrstr, res->ai_canonname); + res = res->ai_next; + } + + return 0; +} + +int main(int argc , char *argv[]) +{ +#ifdef __EMSCRIPTEN__ + bridgeSocket = emscripten_init_websocket_to_posix_socket_bridge("ws://localhost:8080"); + // Synchronously wait until connection has been established. + uint16_t readyState = 0; + do { + emscripten_websocket_get_ready_state(bridgeSocket, &readyState); + emscripten_thread_sleep(100); + } while(readyState == 0); +#endif + + lookup_host("google.com"); + + // Create socket + int sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock == -1) + { + printf("Could not create socket"); + exit(1); + } + printf("Socket created: %d\n", sock); + + struct sockaddr_in server; + server.sin_addr.s_addr = inet_addr("127.0.0.1"); + server.sin_family = AF_INET; + server.sin_port = htons(8888); + + if (connect(sock, (struct sockaddr *)&server, sizeof(server)) < 0) + { + perror("connect failed. Error"); + return 1; + } + + puts("Connected\n"); + for(int i = 0; i < 10; ++i) + { + const char message[] = "hell"; + if (send(sock, message, strlen(message), 0) < 0) + { + puts("Send failed"); + return 1; + } + + char server_reply[256]; + if (recv(sock, server_reply, 256, 0) < 0) + { + puts("recv failed"); + break; + } + + puts("Server reply: "); + puts(server_reply); + } + + close(sock); + return 0; +} diff --git a/tests/websocket/tcp_in_two_threads.cpp b/tests/websocket/tcp_in_two_threads.cpp new file mode 100644 index 0000000000000..b74378259d9a8 --- /dev/null +++ b/tests/websocket/tcp_in_two_threads.cpp @@ -0,0 +1,162 @@ +// TCP client that operates TCP connection in two threads: one thread that does blocking recv(), +// and another that does send()s +#include +#include +#include +#include +#include +#include +#include + +#ifdef __EMSCRIPTEN__ +#include +#include +#include + +EMSCRIPTEN_WEBSOCKET_T bridgeSocket = 0; + +extern "C" { +EMSCRIPTEN_WEBSOCKET_T emscripten_init_websocket_to_posix_socket_bridge(const char *bridgeUrl); +} +#endif + +int lookup_host(const char *host) +{ + struct addrinfo hints, *res; + int errcode; + char addrstr[100]; + void *ptr; + + memset(&hints, 0, sizeof (hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags |= AI_CANONNAME; + + errcode = getaddrinfo(host, NULL, &hints, &res); + if (errcode != 0) + { + printf("getaddrinfo failed!\n"); + return -1; + } + + printf("Host: %s\n", host); + while (res) + { + inet_ntop(res->ai_family, res->ai_addr->sa_data, addrstr, 100); + + switch (res->ai_family) + { + case AF_INET: + ptr = &((struct sockaddr_in *)res->ai_addr)->sin_addr; + break; + case AF_INET6: + ptr = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; + break; + } + inet_ntop(res->ai_family, ptr, addrstr, 100); + printf("IPv%d address: %s (%s)\n", res->ai_family == PF_INET6 ? 6 : 4, addrstr, res->ai_canonname); + res = res->ai_next; + } + + return 0; +} + +int pendingMessages = 0; + +void *send_thread(void *arg) +{ + int sock = (int)arg; + + for(int i = 0; i < 10; ++i) + { + char message[] = "hella"; + message[4] += i; + + while(emscripten_atomic_load_u32(&pendingMessages) != 0) + emscripten_thread_sleep(10); + + printf("Send()ing\n"); + if (send(sock, message, strlen(message), 0) < 0) + { + puts("Send failed"); + pthread_exit((void*)1); + } + printf("Send() done\n"); + emscripten_atomic_add_u32(&pendingMessages, 1); + } + pthread_exit(0); +} + +void *recv_thread(void *arg) +{ + int sock = (int)arg; + + for(int i = 0; i < 10; ++i) + { + char server_reply[256] = {}; + printf("Recv()ing\n"); + if (recv(sock, server_reply, 256, 0) < 0) + { + puts("recv failed"); + pthread_exit((void*)1); + } + printf("Recv() done\n"); + + puts("Server reply: "); + puts(server_reply); + emscripten_atomic_sub_u32(&pendingMessages, 1); + } + pthread_exit(0); +} + +int main(int argc , char *argv[]) +{ +#ifdef __EMSCRIPTEN__ + bridgeSocket = emscripten_init_websocket_to_posix_socket_bridge("ws://localhost:8080"); + // Synchronously wait until connection has been established. + uint16_t readyState = 0; + do { + emscripten_websocket_get_ready_state(bridgeSocket, &readyState); + emscripten_thread_sleep(100); + } while(readyState == 0); +#endif + + lookup_host("google.com"); + + // Create socket + int sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock == -1) + { + printf("Could not create socket"); + exit(1); + } + printf("Socket created: %d\n", sock); + + struct sockaddr_in server; + server.sin_addr.s_addr = inet_addr("127.0.0.1"); + server.sin_family = AF_INET; + server.sin_port = htons(8888); + + if (connect(sock, (struct sockaddr *)&server, sizeof(server)) < 0) + { + perror("connect failed. Error"); + return 1; + } + + pthread_t sendThread; + pthread_t recvThread; + pthread_create(&recvThread, 0, &recv_thread, (void*)sock); + emscripten_thread_sleep(1000); // To get a better guarantee that the first recv() precedes the first send(), sleep for a moment between recv+send thread starts + pthread_create(&sendThread, 0, &send_thread, (void*)sock); + void *sendRet; + void *recvRet; + printf("Waiting for send thread to be finished\n"); + pthread_join(sendThread, &sendRet); + printf("Send thread finished, waiting for recv thread to be finished\n"); + pthread_join(recvThread, &recvRet); + printf("Send thread and recv thread finished\n"); + close(sock); + if ((int)sendRet != 0) fprintf(stderr, "pthread send failed!\n"); + if ((int)recvRet != 0) fprintf(stderr, "pthread recv failed!\n"); + return 0; +} diff --git a/tools/system_libs.py b/tools/system_libs.py index 9f21abf547aeb..8ae9e1092a162 100755 --- a/tools/system_libs.py +++ b/tools/system_libs.py @@ -180,12 +180,17 @@ def create_libc(libname): # individual files blacklist += [ 'memcpy.c', 'memset.c', 'memmove.c', 'getaddrinfo.c', 'getnameinfo.c', - 'inet_addr.c', 'res_query.c', 'res_querydomain.c', 'gai_strerror.c', + 'freeaddrinfo.c', 'res_query.c', 'res_querydomain.c', 'gai_strerror.c', 'proto.c', 'gethostbyaddr.c', 'gethostbyaddr_r.c', 'gethostbyname.c', 'gethostbyname2_r.c', 'gethostbyname_r.c', 'gethostbyname2.c', 'usleep.c', 'alarm.c', 'syscall.c', '_exit.c', 'popen.c', 'getgrouplist.c', 'initgroups.c', 'wordexp.c', 'timer_create.c', 'faccessat.c', + 'socket.c', 'socketpair.c', 'shutdown.c', 'bind.c', 'connect.c', + 'listen.c', 'accept.c', 'getsockname.c', 'getpeername.c', 'send.c', + 'recv.c', 'sendto.c', 'recvfrom.c', 'sendmsg.c', 'recvmsg.c', + 'getsockopt.c', 'setsockopt.c' + ] # individual math files @@ -385,6 +390,9 @@ def create_html5(libname): files += [os.path.join(src_dir, f) for f in filenames] return build_libc(libname, files, ['-Oz']) + def create_posix_proxy(libname): + return build_libc(libname, [shared.path_from_root('system', 'lib', 'websocket', 'websocket_to_posix_socket.cpp')], ['-Oz']) + def create_compiler_rt(libname): files = files_in_path( path_components=['system', 'lib', 'compiler-rt', 'lib', 'builtins'], @@ -623,6 +631,9 @@ class Dummy(object): system_libs += [Library('libgl', ext, create_gl, gl_symbols, [libc_name], False)] # noqa system_libs.append(Library(libc_name, ext, create_libc, libc_symbols, libc_deps, False)) + if shared.Settings.PROXY_POSIX_SOCKETS: + system_libs.append(Library('posix_proxy', ext, create_posix_proxy, [], [], False)) + force.add('posix_proxy') # if building to wasm, we need more math code, since we have less builtins if shared.Settings.WASM: diff --git a/tools/websocket_to_posix_proxy/CMakeLists.txt b/tools/websocket_to_posix_proxy/CMakeLists.txt new file mode 100644 index 0000000000000..ce06da6c2145f --- /dev/null +++ b/tools/websocket_to_posix_proxy/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 2.8) + +project(websocket_to_posix_proxy) + +file(GLOB sourceFiles src/*.cpp src/*.h) + +add_executable(websocket_to_posix_proxy ${sourceFiles}) + +if (WIN32) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) + add_definitions(/wd4200) # "nonstandard extension used: zero-sized array in struct/union" + target_link_libraries(websocket_to_posix_proxy Ws2_32.lib) +endif() diff --git a/tools/websocket_to_posix_proxy/src/main.cpp b/tools/websocket_to_posix_proxy/src/main.cpp new file mode 100644 index 0000000000000..96b84ffde50cc --- /dev/null +++ b/tools/websocket_to_posix_proxy/src/main.cpp @@ -0,0 +1,382 @@ +#include +#include +#include +#include +#include "posix_sockets.h" +#include "threads.h" +#include +#include + +#include "sha1.h" +#include "websocket_to_posix_proxy.h" +#include "socket_registry.h" + +// #define PROXY_DEBUG + +// #define PROXY_DEEP_DEBUG + +static const unsigned char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static void base64_encode(void *dst, const void *src, size_t len) // thread-safe, re-entrant +{ + assert(dst != src); + unsigned int *d = (unsigned int *)dst; + const unsigned char *s = (const unsigned char*)src; + const unsigned char *end = s + len; + while(s < end) + { + uint32_t e = *s++ << 16; + if (s < end) e |= *s++ << 8; + if (s < end) e |= *s++; + *d++ = b64[e >> 18] | (b64[(e >> 12) & 0x3F] << 8) | (b64[(e >> 6) & 0x3F] << 16) | (b64[e & 0x3F] << 24); + } + for (size_t i = 0; i < (3 - (len % 3)) % 3; i++) ((char *)d)[-1-i] = '='; +} + +#define BUFFER_SIZE 1024 +#define on_error(...) { fprintf(stderr, __VA_ARGS__); fflush(stderr); exit(1); } + +// Given a multiline string of HTTP headers, returns a pointer to the beginning of the value of given header inside the string that was passed in. +static int GetHttpHeader(const char *headers, const char *header, char *out) // thread-safe, re-entrant +{ + const char *pos = strstr(headers, header); + if (!pos) return 0; + pos += strlen(header); + const char *end = pos; + while(*end != '\r') ++end; + memcpy(out, pos, end-pos); + out[end-pos] = '\0'; + return end-pos; +} + +// Sends WebSocket handshake back to the given WebSocket connection. +void SendHandshake(int fd, const char *request) +{ + char key[128]; + GetHttpHeader(request, "Sec-WebSocket-Key: ", key); + const char webSocketGlobalGuid[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + strcat(key, webSocketGlobalGuid); + + char sha1[21]; + printf("hashing key: \"%s\"\n", key); + SHA1(sha1, key, strlen(key)); + + char handshakeMsg[] = + "HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Accept: 0000000000000000000000000000\r\n" + "\r\n"; + + base64_encode(strstr(handshakeMsg, "Sec-WebSocket-Accept: ") + strlen("Sec-WebSocket-Accept: "), sha1, 20); + + int err = send(fd, handshakeMsg, strlen(handshakeMsg), 0); + if (err < 0) on_error("Client write failed\n"); + printf("Sent handshake:\n%s\n", handshakeMsg); +} + +// Validates if the given, possibly partially received WebSocket message has enough bytes to contain a full WebSocket header. +static bool WebSocketHasFullHeader(uint8_t *data, uint64_t obtainedNumBytes) +{ + if (obtainedNumBytes < 2) return false; + uint64_t expectedNumBytes = 2; + WebSocketMessageHeader *header = (WebSocketMessageHeader *)data; + if (header->mask) expectedNumBytes += 4; + switch(header->payloadLength) + { + case 127: return expectedNumBytes += 8; break; + case 126: return expectedNumBytes += 2; break; + default: break; + } + return obtainedNumBytes >= expectedNumBytes; +} + +// Computes the total number of bytes that the given WebSocket message will take up. +uint64_t WebSocketFullMessageSize(uint8_t *data, uint64_t obtainedNumBytes) +{ + assert(WebSocketHasFullHeader(data, obtainedNumBytes)); + + uint64_t expectedNumBytes = 2; + WebSocketMessageHeader *header = (WebSocketMessageHeader *)data; + if (header->mask) expectedNumBytes += 4; + switch(header->payloadLength) + { + case 127: return expectedNumBytes += 8 + ntoh64(*(uint64_t*)(data+2)); break; + case 126: return expectedNumBytes += 2 + ntohs(*(uint16_t*)(data+2)); break; + default: expectedNumBytes += header->payloadLength; break; + } + return expectedNumBytes; +} + +// Tests the structure integrity of the websocket message length. +bool WebSocketValidateMessageSize(uint8_t *data, uint64_t obtainedNumBytes) +{ + uint64_t expectedNumBytes = WebSocketFullMessageSize(data, obtainedNumBytes); + + if (expectedNumBytes != obtainedNumBytes) + { + printf("Corrupt WebSocket message size! (got %llu bytes, expected %llu bytes)\n", obtainedNumBytes, expectedNumBytes); + printf("Received data:"); + for(size_t i = 0; i < obtainedNumBytes; ++i) + printf(" %02X", data[i]); + printf("\n"); + } + return expectedNumBytes == obtainedNumBytes; +} + +uint64_t WebSocketMessagePayloadLength(uint8_t *data, uint64_t numBytes) +{ + WebSocketMessageHeader *header = (WebSocketMessageHeader *)data; + switch(header->payloadLength) + { + case 127: return ntoh64(*(uint64_t*)(data+2)); + case 126: return ntohs(*(uint16_t*)(data+2)); + default: return header->payloadLength; + } +} + +uint32_t WebSocketMessageMaskingKey(uint8_t *data, uint64_t numBytes) +{ + WebSocketMessageHeader *header = (WebSocketMessageHeader *)data; + if (!header->mask) return 0; + switch(header->payloadLength) + { + case 127: return *(uint32_t*)(data+10); + case 126: return *(uint32_t*)(data+4); + default: return *(uint32_t*)(data+2); + } +} + +uint8_t *WebSocketMessageData(uint8_t *data, uint64_t numBytes) +{ + WebSocketMessageHeader *header = (WebSocketMessageHeader *)data; + data += 2; // Two bytes of fixed size header + if (header->mask) data += 4; // If there is a masking key present in the header, that takes up 4 bytes + switch(header->payloadLength) + { + case 127: return data + 8; // 64-bit length + case 126: return data + 2; // 16-bit length + default: return data; // 7-bit length that was embedded in fixed size header. + } +} + +void CloseWebSocket(int client_fd) +{ + printf("Closing WebSocket connection %d\n", client_fd); + CloseAllSocketsByConnection(client_fd); + shutdown(client_fd, SHUTDOWN_BIDIRECTIONAL); + CLOSE_SOCKET(client_fd); +} + +const char *WebSocketOpcodeToString(int opcode) +{ + static const char *opcodes[] = { "continuation frame (0x0)", "text frame (0x1)", "binary frame (0x2)", "reserved(0x3)", "reserved(0x4)", "reserved(0x5)", + "reserved(0x6)", "reserved(0x7)", "connection close (0x8)", "ping (0x9)", "pong (0xA)", "reserved(0xB)", "reserved(0xC)", "reserved(0xD)", "reserved(0xE)", "reserved(0xF)" }; + return opcodes[opcode]; +} + +void DumpWebSocketMessage(uint8_t *data, uint64_t numBytes) +{ + bool goodMessageSize = WebSocketValidateMessageSize(data, numBytes); + if (!goodMessageSize) + return; + + WebSocketMessageHeader *header = (WebSocketMessageHeader *)data; + uint64_t payloadLength = WebSocketMessagePayloadLength(data, numBytes); + uint8_t *payload = WebSocketMessageData(data, numBytes); + + printf("Received: FIN: %d, opcode: %s, mask: 0x%08X, payload length: %llu bytes, unmasked payload:", header->fin, WebSocketOpcodeToString(header->opcode), + WebSocketMessageMaskingKey(data, numBytes), payloadLength); + for(uint64_t i = 0; i < payloadLength; ++i) + { + if (i%16 == 0) printf("\n"); + if (i%8==0) printf(" "); + printf(" %02X", payload[i]); + if (i >= 63 && payloadLength > 64) + { + printf("\n ... (%llu more bytes)", payloadLength-i); + break; + } + } + printf("\n"); +} + +// connection thread manages a single active proxy connection. +THREAD_RETURN_T connection_thread(void *arg) +{ + int client_fd = (int)(uintptr_t)arg; + printf("Established new proxy connection handler thread for incoming connection, at fd=%d\n", client_fd); // TODO: print out getpeername()+getsockname() for more info + + // Waiting for connection upgrade handshake + char buf[BUFFER_SIZE]; + int read = recv(client_fd, buf, BUFFER_SIZE, 0); + + if (!read) + { + CloseWebSocket(client_fd); + EXIT_THREAD(0); + } + + if (read < 0) + { + fprintf(stderr, "Client read failed\n"); + CloseWebSocket(client_fd); + EXIT_THREAD(0); + } + +#ifdef PROXY_DEEP_DEBUG + printf("Received:"); + for(int i = 0; i < read; ++i) + { + printf(" %02X", buf[i]); + } + printf("\n"); +// printf("In text:\n%s\n", buf); +#endif + SendHandshake(client_fd, buf); + +#ifdef PROXY_DEEP_DEBUG + printf("Handshake received, entering message loop:\n"); +#endif + + std::vector fragmentData; + + bool connectionAlive = true; + while (connectionAlive) + { + int read = recv(client_fd, buf, BUFFER_SIZE, 0); + + if (!read) break; // done reading + if (read < 0) + { + fprintf(stderr, "Client read failed\n"); + EXIT_THREAD(0); + } + +#ifdef PROXY_DEEP_DEBUG + printf("Received:"); + for(int i = 0; i < read; ++i) + { + printf(" %02X", ((unsigned char*)buf)[i]); + } + printf("\n"); +// printf("In text:\n%s\n", buf); +#endif + +#ifdef PROXY_DEEP_DEBUG + printf("Have %d+%d==%d bytes now in queue\n", (int)fragmentData.size(), (int)read, (int)(fragmentData.size()+read)); +#endif + fragmentData.insert(fragmentData.end(), buf, buf+read); + + // Process received fragments until there is not enough data for a full message + while(!fragmentData.empty()) + { + bool hasFullHeader = WebSocketHasFullHeader(&fragmentData[0], fragmentData.size()); + if (!hasFullHeader) + { +#ifdef PROXY_DEEP_DEBUG + printf("(not enough for a full WebSocket header)\n"); +#endif + break; + } + uint64_t neededBytes = WebSocketFullMessageSize(&fragmentData[0], fragmentData.size()); + if (fragmentData.size() < neededBytes) + { +#ifdef PROXY_DEEP_DEBUG + printf("(not enough for a full WebSocket message, needed %d bytes)\n", (int)neededBytes); +#endif + break; + } + + WebSocketMessageHeader *header = (WebSocketMessageHeader *)&fragmentData[0]; + uint64_t payloadLength = WebSocketMessagePayloadLength(&fragmentData[0], neededBytes); + uint8_t *payload = WebSocketMessageData(&fragmentData[0], neededBytes); + + // Unmask payload + if (header->mask) + WebSocketMessageUnmaskPayload(payload, payloadLength, WebSocketMessageMaskingKey(&fragmentData[0], neededBytes)); + +#ifdef PROXY_DEEP_DEBUG + DumpWebSocketMessage(&fragmentData[0], neededBytes); +#endif + + switch(header->opcode) + { + case 0x02: /*binary message*/ ProcessWebSocketMessage(client_fd, payload, payloadLength); break; + case 0x08: connectionAlive = false; break; + default: + fprintf(stderr, "Unknown WebSocket opcode received %x!\n", header->opcode); + connectionAlive = false; // Kill connection + break; + } + + fragmentData.erase(fragmentData.begin(), fragmentData.begin() + (ptrdiff_t)neededBytes); +#ifdef PROXY_DEEP_DEBUG + printf("Cleared used bytes, got %d left in fragment queue.\n", (int)fragmentData.size()); +#endif + } + } + printf("Proxy connection closed\n"); + CloseWebSocket(client_fd); + EXIT_THREAD(0); +} + +int main(int argc, char *argv[]) +{ + if (argc < 2) on_error("websocket_to_posix_proxy creates a bridge that allows WebSocket connections on a web page to proxy out to perform TCP/UDP connections.\nUsage: %s [port]\n", argv[0]); + +#ifdef _WIN32 + WSADATA wsaData; + int failed = WSAStartup(MAKEWORD(2,2), &wsaData); + if (failed) + { + printf("WSAStartup failed: %d\n", failed); + return 1; + } +#else + signal(SIGPIPE, SIG_IGN); +#endif + + const int port = atoi(argv[1]); + int server_fd = socket(AF_INET, SOCK_STREAM, 0); + if (server_fd < 0) on_error("Could not create socket\n"); + + struct sockaddr_in server; + server.sin_family = AF_INET; + server.sin_port = htons(port); + server.sin_addr.s_addr = htonl(INADDR_ANY); + + int opt_val = 1; + setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, (SETSOCKOPT_PTR_TYPE)&opt_val, sizeof opt_val); + + int err = bind(server_fd, (struct sockaddr *) &server, sizeof(server)); + if (err < 0) on_error("Could not bind socket\n"); + + err = listen(server_fd, 128); + if (err < 0) on_error("Could not listen on socket\n"); + + printf("websocket_to_posix_proxy server is now listening for WebSocket connections to ws://localhost:%d/\n", port); + + while (1) + { + int client_fd = accept(server_fd, 0, 0); + if (client_fd < 0) + { + fprintf(stderr, "Could not establish new incoming proxy connection\n"); + continue; // Do not quit here, but keep serving any existing proxy connections. + } + + THREAD_T connection; + CREATE_THREAD_RETURN_T ret = CREATE_THREAD(connection, connection_thread, (void*)(uintptr_t)client_fd); + if (!CREATE_THREAD_SUCCEEDED(ret)) + { + fprintf(stderr, "Failed to create a connection handler thread for incoming proxy connection!\n"); + continue; // Do not quit here, but keep program alive to manage other existing proxy connections. + } + } + +#ifdef _WIN32 + WSACleanup(); +#endif + + return 0; +} diff --git a/tools/websocket_to_posix_proxy/src/posix_sockets.h b/tools/websocket_to_posix_proxy/src/posix_sockets.h new file mode 100644 index 0000000000000..78b433d9f5197 --- /dev/null +++ b/tools/websocket_to_posix_proxy/src/posix_sockets.h @@ -0,0 +1,52 @@ +#pragma once + +#include + +#if defined(__APPLE__) || defined(____linux__) + +#include +#include +#include +#include +#include + +#define SHUTDOWN_READ SHUT_RD +#define SHUTDOWN_WRITE SHUT_WR +#define SHUTDOWN_BIDIRECTIONAL SHUT_RDWR +#define SETSOCKOPT_PTR_TYPE const int* +#define SEND_RET_TYPE ssize_t +#define SEND_FORMATTING_SPECIFIER "%ld" +#define CLOSE_SOCKET(x) close(x) + +#define GET_SOCKET_ERROR() (errno) + +#define PRINT_SOCKET_ERROR(errorCode) do { \ + printf("Call failed! errno: %s(%d)\n", strerror(errorCode), errorCode); \ + } while(0) + +#elif defined(_MSC_VER) + +#include +#include + +#define SHUTDOWN_READ SD_RECEIVE +#define SHUTDOWN_WRITE SD_SEND +#define SHUTDOWN_BIDIRECTIONAL SD_BOTH +#define SETSOCKOPT_PTR_TYPE const char* +#define SEND_RET_TYPE int +#define SEND_FORMATTING_SPECIFIER "%d" +#define CLOSE_SOCKET(x) closesocket(x) + +#define GET_SOCKET_ERROR() (WSAGetLastError()) + +static inline void PRINT_SOCKET_ERROR(int errorCode) +{ + void *lpMsgBuf = 0; + HRESULT hresult = HRESULT_FROM_WIN32(errorCode); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + 0, hresult, 0 /*Default language*/, (LPTSTR)&lpMsgBuf, 0, 0); + printf("Call failed! WSAGetLastError: %s(%d)\n", (char*)lpMsgBuf, errorCode); + LocalFree(lpMsgBuf); +} + +#endif diff --git a/tools/websocket_to_posix_proxy/src/sha1.cpp b/tools/websocket_to_posix_proxy/src/sha1.cpp new file mode 100644 index 0000000000000..c2c529f529d75 --- /dev/null +++ b/tools/websocket_to_posix_proxy/src/sha1.cpp @@ -0,0 +1,295 @@ +/* +SHA-1 in C +By Steve Reid +100% Public Domain + +Test Vectors (from FIPS PUB 180-1) +"abc" + A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D +"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 +A million repetitions of "a" + 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F +*/ + +/* #define LITTLE_ENDIAN * This should be #define'd already, if true. */ +/* #define SHA1HANDSOFF * Copies data before messing with it. */ + +#define SHA1HANDSOFF + +#include +#include + +/* for uint32_t */ +#include + +#include "sha1.h" + + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +/* blk0() and blk() perform the initial expand. */ +/* I got the idea of expanding during the round function from SSLeay */ +#if BYTE_ORDER == LITTLE_ENDIAN +#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ + |(rol(block->l[i],8)&0x00FF00FF)) +#elif BYTE_ORDER == BIG_ENDIAN +#define blk0(i) block->l[i] +#else +#error "Endianness not defined!" +#endif +#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ + ^block->l[(i+2)&15]^block->l[i&15],1)) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); + + +/* Hash a single 512-bit block. This is the core of the algorithm. */ + +void SHA1Transform( + uint32_t state[5], + const unsigned char buffer[64] +) +{ + uint32_t a, b, c, d, e; + + typedef union + { + unsigned char c[64]; + uint32_t l[16]; + } CHAR64LONG16; + +#ifdef SHA1HANDSOFF + CHAR64LONG16 block[1]; /* use array to appear as a pointer */ + + memcpy(block, buffer, 64); +#else + /* The following had better never be used because it causes the + * pointer-to-const buffer to be cast into a pointer to non-const. + * And the result is written through. I threw a "const" in, hoping + * this will cause a diagnostic. + */ + CHAR64LONG16 *block = (const CHAR64LONG16 *) buffer; +#endif + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a, b, c, d, e, 0); + R0(e, a, b, c, d, 1); + R0(d, e, a, b, c, 2); + R0(c, d, e, a, b, 3); + R0(b, c, d, e, a, 4); + R0(a, b, c, d, e, 5); + R0(e, a, b, c, d, 6); + R0(d, e, a, b, c, 7); + R0(c, d, e, a, b, 8); + R0(b, c, d, e, a, 9); + R0(a, b, c, d, e, 10); + R0(e, a, b, c, d, 11); + R0(d, e, a, b, c, 12); + R0(c, d, e, a, b, 13); + R0(b, c, d, e, a, 14); + R0(a, b, c, d, e, 15); + R1(e, a, b, c, d, 16); + R1(d, e, a, b, c, 17); + R1(c, d, e, a, b, 18); + R1(b, c, d, e, a, 19); + R2(a, b, c, d, e, 20); + R2(e, a, b, c, d, 21); + R2(d, e, a, b, c, 22); + R2(c, d, e, a, b, 23); + R2(b, c, d, e, a, 24); + R2(a, b, c, d, e, 25); + R2(e, a, b, c, d, 26); + R2(d, e, a, b, c, 27); + R2(c, d, e, a, b, 28); + R2(b, c, d, e, a, 29); + R2(a, b, c, d, e, 30); + R2(e, a, b, c, d, 31); + R2(d, e, a, b, c, 32); + R2(c, d, e, a, b, 33); + R2(b, c, d, e, a, 34); + R2(a, b, c, d, e, 35); + R2(e, a, b, c, d, 36); + R2(d, e, a, b, c, 37); + R2(c, d, e, a, b, 38); + R2(b, c, d, e, a, 39); + R3(a, b, c, d, e, 40); + R3(e, a, b, c, d, 41); + R3(d, e, a, b, c, 42); + R3(c, d, e, a, b, 43); + R3(b, c, d, e, a, 44); + R3(a, b, c, d, e, 45); + R3(e, a, b, c, d, 46); + R3(d, e, a, b, c, 47); + R3(c, d, e, a, b, 48); + R3(b, c, d, e, a, 49); + R3(a, b, c, d, e, 50); + R3(e, a, b, c, d, 51); + R3(d, e, a, b, c, 52); + R3(c, d, e, a, b, 53); + R3(b, c, d, e, a, 54); + R3(a, b, c, d, e, 55); + R3(e, a, b, c, d, 56); + R3(d, e, a, b, c, 57); + R3(c, d, e, a, b, 58); + R3(b, c, d, e, a, 59); + R4(a, b, c, d, e, 60); + R4(e, a, b, c, d, 61); + R4(d, e, a, b, c, 62); + R4(c, d, e, a, b, 63); + R4(b, c, d, e, a, 64); + R4(a, b, c, d, e, 65); + R4(e, a, b, c, d, 66); + R4(d, e, a, b, c, 67); + R4(c, d, e, a, b, 68); + R4(b, c, d, e, a, 69); + R4(a, b, c, d, e, 70); + R4(e, a, b, c, d, 71); + R4(d, e, a, b, c, 72); + R4(c, d, e, a, b, 73); + R4(b, c, d, e, a, 74); + R4(a, b, c, d, e, 75); + R4(e, a, b, c, d, 76); + R4(d, e, a, b, c, 77); + R4(c, d, e, a, b, 78); + R4(b, c, d, e, a, 79); + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + /* Wipe variables */ + a = b = c = d = e = 0; +#ifdef SHA1HANDSOFF + memset(block, '\0', sizeof(block)); +#endif +} + + +/* SHA1Init - Initialize new context */ + +void SHA1Init( + SHA1_CTX * context +) +{ + /* SHA1 initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + + +/* Run your data through this. */ + +void SHA1Update( + SHA1_CTX * context, + const unsigned char *data, + uint32_t len +) +{ + uint32_t i; + + uint32_t j; + + j = context->count[0]; + if ((context->count[0] += len << 3) < j) + context->count[1]++; + context->count[1] += (len >> 29); + j = (j >> 3) & 63; + if ((j + len) > 63) + { + memcpy(&context->buffer[j], data, (i = 64 - j)); + SHA1Transform(context->state, context->buffer); + for (; i + 63 < len; i += 64) + { + SHA1Transform(context->state, &data[i]); + } + j = 0; + } + else + i = 0; + memcpy(&context->buffer[j], &data[i], len - i); +} + + +/* Add padding and return the message digest. */ + +void SHA1Final( + unsigned char digest[20], + SHA1_CTX * context +) +{ + unsigned i; + + unsigned char finalcount[8]; + + unsigned char c; + +#if 0 /* untested "improvement" by DHR */ + /* Convert context->count to a sequence of bytes + * in finalcount. Second element first, but + * big-endian order within element. + * But we do it all backwards. + */ + unsigned char *fcp = &finalcount[8]; + + for (i = 0; i < 2; i++) + { + uint32_t t = context->count[i]; + + int j; + + for (j = 0; j < 4; t >>= 8, j++) + *--fcp = (unsigned char) t} +#else + for (i = 0; i < 8; i++) + { + finalcount[i] = (unsigned char) ((context->count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8)) & 255); /* Endian independent */ + } +#endif + c = 0200; + SHA1Update(context, &c, 1); + while ((context->count[0] & 504) != 448) + { + c = 0000; + SHA1Update(context, &c, 1); + } + SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ + for (i = 0; i < 20; i++) + { + digest[i] = (unsigned char) + ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255); + } + /* Wipe variables */ + memset(context, '\0', sizeof(*context)); + memset(&finalcount, '\0', sizeof(finalcount)); +} + +void SHA1( + char *hash_out, + const char *str, + int len) +{ + SHA1_CTX ctx; + int ii; + + SHA1Init(&ctx); + for (ii=0; ii + 100% Public Domain + */ + +#include "stdint.h" + +typedef struct +{ + uint32_t state[5]; + uint32_t count[2]; + unsigned char buffer[64]; +} SHA1_CTX; + +void SHA1Transform( + uint32_t state[5], + const unsigned char buffer[64] + ); + +void SHA1Init( + SHA1_CTX * context + ); + +void SHA1Update( + SHA1_CTX * context, + const unsigned char *data, + uint32_t len + ); + +void SHA1Final( + unsigned char digest[20], + SHA1_CTX * context + ); + +void SHA1( + char *hash_out, + const char *str, + int len); + +#endif /* SHA1_H */ diff --git a/tools/websocket_to_posix_proxy/src/socket_registry.cpp b/tools/websocket_to_posix_proxy/src/socket_registry.cpp new file mode 100644 index 0000000000000..f9d564d2be7ec --- /dev/null +++ b/tools/websocket_to_posix_proxy/src/socket_registry.cpp @@ -0,0 +1,50 @@ +#include "socket_registry.h" + +#include +#include +#include + +namespace +{ + std::map > socketsPerProxyConnection; +} + +void TrackSocketUsedByConnection(int proxyConnection, int usedSocket) +{ + if (usedSocket == 0) return; + if (IsSocketPartOfConnection(proxyConnection, usedSocket)) + return; + socketsPerProxyConnection[proxyConnection].push_back(usedSocket); +} + +void CloseSocketByConnection(int proxyConnection, int usedSocket) +{ + if (!IsSocketPartOfConnection(proxyConnection, usedSocket)) + return; + printf("Closing socket fd %d used by proxy connection %d\n", usedSocket, proxyConnection); + CLOSE_SOCKET(usedSocket); + std::vector &sockets = socketsPerProxyConnection[proxyConnection]; + sockets.erase(std::remove(sockets.begin(), sockets.end(), usedSocket), sockets.end()); +} + +void CloseAllSocketsByConnection(int proxyConnection) +{ + std::vector &sockets = socketsPerProxyConnection[proxyConnection]; + for(size_t i = 0; i < sockets.size(); ++i) + { + printf("Closing socket fd %d used by proxy connection %d.\n", sockets[i], proxyConnection); + shutdown(sockets[i], SHUTDOWN_BIDIRECTIONAL); + CLOSE_SOCKET(sockets[i]); + } + socketsPerProxyConnection.erase(proxyConnection); +} + +bool IsSocketPartOfConnection(int proxyConnection, int usedSocket) +{ + if (usedSocket == 0) return true; // Allow all proxy connections to access "socket 0" when/if they need to refer to socket that does not exist. + if (socketsPerProxyConnection.find(proxyConnection) == socketsPerProxyConnection.end()) + return false; + + std::vector &sockets = socketsPerProxyConnection[proxyConnection]; + return std::find(sockets.begin(), sockets.end(), usedSocket) != sockets.end(); +} diff --git a/tools/websocket_to_posix_proxy/src/socket_registry.h b/tools/websocket_to_posix_proxy/src/socket_registry.h new file mode 100644 index 0000000000000..ae29104d085a9 --- /dev/null +++ b/tools/websocket_to_posix_proxy/src/socket_registry.h @@ -0,0 +1,21 @@ +#pragma once + +#include "posix_sockets.h" + +// Socket Registry remembers all the sockets created by incoming proxy connections, so that those sockets can be properly +// shut down when an incoming proxy connection disconnects. + +// Tracks that the given socket is part of the specified proxy connection. When proxyConnection disconnects, all sockets +// used by it are shut down. +void TrackSocketUsedByConnection(int proxyConnection, int usedSocket); + +// Untracks the given socket - the proxy connection has shut it down. +void CloseSocketByConnection(int proxyConnection, int usedSocket); + +// Given proxy connection has disconnected - shut down all the sockets it had created. +void CloseAllSocketsByConnection(int proxyConnection); + +// Returns if the given socket is known to be owned by the specified proxy connection. +// This is used to gate socket connections so that two proxy connections can not access +// each others' sockets. +bool IsSocketPartOfConnection(int proxyConnection, int usedSocket); diff --git a/tools/websocket_to_posix_proxy/src/threads.h b/tools/websocket_to_posix_proxy/src/threads.h new file mode 100644 index 0000000000000..52bba0e8f2bc2 --- /dev/null +++ b/tools/websocket_to_posix_proxy/src/threads.h @@ -0,0 +1,39 @@ +#pragma once + +#if defined(__unix__) || defined(__APPLE__) || defined(__linux__) +#include +#define THREAD_T pthread_t +#define CREATE_THREAD(threadPtr, threadFunc, threadArg) pthread_create(&threadPtr, 0, threadFunc, threadArg) +#define CREATE_THREAD_RETURN_T int +#define CREATE_THREAD_SUCCEEDED(x) (x == 0) +#define EXIT_THREAD(code) pthread_exit((void*)(uintptr_t)code) +#define THREAD_RETURN_T void* +#define MUTEX_T pthread_mutex_t +inline MUTEX_T CREATE_MUTEX() +{ + pthread_mutex_t m; + pthread_mutex_init(&m, 0); + return m; +} +#define LOCK_MUTEX(m) pthread_mutex_lock(m) +#define UNLOCK_MUTEX(m) pthread_mutex_unlock(m) +#endif + +#if defined(_WIN32) +#include +#define THREAD_T HANDLE +#define CREATE_THREAD(threadPtr, threadFunc, threadArg) threadPtr = CreateThread(0, 0, threadFunc, threadArg, 0, 0) +#define CREATE_THREAD_RETURN_T HANDLE +#define CREATE_THREAD_SUCCEEDED(x) (x != 0) +#define EXIT_THREAD(code) ExitThread((DWORD)code) +#define THREAD_RETURN_T DWORD WINAPI +#define MUTEX_T CRITICAL_SECTION +inline MUTEX_T CREATE_MUTEX() +{ + CRITICAL_SECTION cs; + InitializeCriticalSectionAndSpinCount(&cs, 0x00000400); + return cs; +} +#define LOCK_MUTEX(m) EnterCriticalSection(m) +#define UNLOCK_MUTEX(m) LeaveCriticalSection(m) +#endif diff --git a/tools/websocket_to_posix_proxy/src/websocket_to_posix_proxy.cpp b/tools/websocket_to_posix_proxy/src/websocket_to_posix_proxy.cpp new file mode 100644 index 0000000000000..c436734d62a9b --- /dev/null +++ b/tools/websocket_to_posix_proxy/src/websocket_to_posix_proxy.cpp @@ -0,0 +1,1672 @@ +#include +#include +#include +#include "posix_sockets.h" +#include "threads.h" +#include +#include +#include + +#include "websocket_to_posix_proxy.h" +#include "socket_registry.h" + +// Uncomment to enable debug printing +// #define POSIX_SOCKET_DEBUG + +// Uncomment to enable more verbose debug printing (in addition to uncommenting POSIX_SOCKET_DEBUG) +// #define POSIX_SOCKET_DEEP_DEBUG + +#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MAX(a,b) (((a)>(b))?(a):(b)) + +uint64_t ntoh64(uint64_t x) // thread-safe, re-entrant +{ + return ntohl(x>>32) | ((uint64_t)ntohl(x&0xFFFFFFFFu) << 32); +} + +#define POSIX_SOCKET_MSG_SOCKET 1 +#define POSIX_SOCKET_MSG_SOCKETPAIR 2 +#define POSIX_SOCKET_MSG_SHUTDOWN 3 +#define POSIX_SOCKET_MSG_BIND 4 +#define POSIX_SOCKET_MSG_CONNECT 5 +#define POSIX_SOCKET_MSG_LISTEN 6 +#define POSIX_SOCKET_MSG_ACCEPT 7 +#define POSIX_SOCKET_MSG_GETSOCKNAME 8 +#define POSIX_SOCKET_MSG_GETPEERNAME 9 +#define POSIX_SOCKET_MSG_SEND 10 +#define POSIX_SOCKET_MSG_RECV 11 +#define POSIX_SOCKET_MSG_SENDTO 12 +#define POSIX_SOCKET_MSG_RECVFROM 13 +#define POSIX_SOCKET_MSG_SENDMSG 14 +#define POSIX_SOCKET_MSG_RECVMSG 15 +#define POSIX_SOCKET_MSG_GETSOCKOPT 16 +#define POSIX_SOCKET_MSG_SETSOCKOPT 17 +#define POSIX_SOCKET_MSG_GETADDRINFO 18 +#define POSIX_SOCKET_MSG_GETNAMEINFO 19 + +#define MAX_SOCKADDR_SIZE 256 +#define MAX_OPTIONVALUE_SIZE 16 + +struct SocketCallHeader +{ + int callId; + int function; +}; + +static char buf_temp_str[2048] = {}; + +static char *BufferToString(const void *buf, size_t len) // not thread-safe, but only used for debug prints, so expected not to cause trouble +{ + uint8_t *b = (uint8_t *)buf; + if (!b) + { + sprintf(buf_temp_str, "(null ptr) (%d bytes)", (int)len); + return buf_temp_str; + } + + for(size_t i = 0; i < len && i*3 + 64 < sizeof(buf_temp_str); ++i) + { + sprintf(buf_temp_str + i*3, "%02X ", b[i]); + } + sprintf(buf_temp_str + len*3, " (%d bytes)", (int)len); + return buf_temp_str; +} + +void WebSocketMessageUnmaskPayload(uint8_t *payload, uint64_t payloadLength, uint32_t maskingKey) // thread-safe, re-entrant +{ + uint8_t maskingKey8[4]; + memcpy(maskingKey8, &maskingKey, 4); + uint32_t *data_u32 = (uint32_t *)payload; + uint32_t *end_u32 = (uint32_t *)((uintptr_t)(payload + (payloadLength & ~3u))); + + while(data_u32 < end_u32) + *data_u32++ ^= maskingKey; + + uint8_t *end = payload + payloadLength; + uint8_t *data = (uint8_t *)data_u32; + while(data < end) + { + *data ^= maskingKey8[(data-payload) % 4]; + ++data; + } +} + +// Technically only would need one lock per connection, but this is now one lock per all connections, which would be +// slightly inefficient if we were handling multiple proxied connections at the same time. (currently that is a rare +// use case, expected to only be proxying one connection at a time - if this proxy bridge is expected to be used +// for hundreds of connections simultaneously, this mutex should be refactored to be per-connection) +static MUTEX_T webSocketLock = CREATE_MUTEX(); + +void SendWebSocketMessage(int client_fd, void *buf, uint64_t numBytes) +{ + LOCK_MUTEX(&webSocketLock); + uint8_t headerData[sizeof(WebSocketMessageHeader) + 8/*possible extended length*/] = {}; + WebSocketMessageHeader *header = (WebSocketMessageHeader *)headerData; + header->opcode = 0x02; + header->fin = 1; + int headerBytes = 2; + + if (numBytes < 126) + header->payloadLength = numBytes; + else if (numBytes <= 65535) + { + header->payloadLength = 126; + *(uint16_t*)(headerData+headerBytes) = htons((unsigned short)numBytes); + headerBytes += 2; + } + else + { + header->payloadLength = 127; + *(uint64_t*)(headerData+headerBytes) = hton64(numBytes); + headerBytes += 8; + } + +#ifdef POSIX_SOCKET_DEEP_DEBUG + printf("Sending %llu bytes message (%llu bytes of payload) to WebSocket\n", headerBytes + numBytes, numBytes); + + printf("Header:"); + for(int i = 0; i < headerBytes; ++i) + printf(" %02X", headerData[i]); + + printf("\nPayload:"); + for(int i = 0; i < numBytes; ++i) + printf(" %02X", ((unsigned char*)buf)[i]); + printf("\n"); +#endif + + send(client_fd, (const char*)headerData, headerBytes, 0); // header + send(client_fd, (const char*)buf, (int)numBytes, 0); // payload + UNLOCK_MUTEX(&webSocketLock); +} + +#define MUSL_PF_UNSPEC 0 +#define MUSL_PF_LOCAL 1 +#define MUSL_PF_UNIX PF_LOCAL +#define MUSL_PF_FILE PF_LOCAL +#define MUSL_PF_INET 2 +#define MUSL_PF_AX25 3 +#define MUSL_PF_IPX 4 +#define MUSL_PF_APPLETALK 5 +#define MUSL_PF_NETROM 6 +#define MUSL_PF_BRIDGE 7 +#define MUSL_PF_ATMPVC 8 +#define MUSL_PF_X25 9 +#define MUSL_PF_INET6 10 +#define MUSL_PF_ROSE 11 +#define MUSL_PF_DECnet 12 +#define MUSL_PF_NETBEUI 13 +#define MUSL_PF_SECURITY 14 +#define MUSL_PF_KEY 15 +#define MUSL_PF_NETLINK 16 +#define MUSL_PF_ROUTE PF_NETLINK +#define MUSL_PF_PACKET 17 +#define MUSL_PF_ASH 18 +#define MUSL_PF_ECONET 19 +#define MUSL_PF_ATMSVC 20 +#define MUSL_PF_RDS 21 +#define MUSL_PF_SNA 22 +#define MUSL_PF_IRDA 23 +#define MUSL_PF_PPPOX 24 +#define MUSL_PF_WANPIPE 25 +#define MUSL_PF_LLC 26 +#define MUSL_PF_IB 27 +#define MUSL_PF_MPLS 28 +#define MUSL_PF_CAN 29 +#define MUSL_PF_TIPC 30 +#define MUSL_PF_BLUETOOTH 31 +#define MUSL_PF_IUCV 32 +#define MUSL_PF_RXRPC 33 +#define MUSL_PF_ISDN 34 +#define MUSL_PF_PHONET 35 +#define MUSL_PF_IEEE802154 36 +#define MUSL_PF_CAIF 37 +#define MUSL_PF_ALG 38 +#define MUSL_PF_NFC 39 +#define MUSL_PF_VSOCK 40 +#define MUSL_PF_KCM 41 +#define MUSL_PF_MAX 42 + +#define MUSL_AF_UNSPEC MUSL_PF_UNSPEC +#define MUSL_AF_LOCAL MUSL_PF_LOCAL +#define MUSL_AF_UNIX MUSL_AF_LOCAL +#define MUSL_AF_FILE MUSL_AF_LOCAL +#define MUSL_AF_INET MUSL_PF_INET +#define MUSL_AF_AX25 MUSL_PF_AX25 +#define MUSL_AF_IPX MUSL_PF_IPX +#define MUSL_AF_APPLETALK MUSL_PF_APPLETALK +#define MUSL_AF_NETROM MUSL_PF_NETROM +#define MUSL_AF_BRIDGE MUSL_PF_BRIDGE +#define MUSL_AF_ATMPVC MUSL_PF_ATMPVC +#define MUSL_AF_X25 MUSL_PF_X25 +#define MUSL_AF_INET6 MUSL_PF_INET6 +#define MUSL_AF_ROSE MUSL_PF_ROSE +#define MUSL_AF_DECnet MUSL_PF_DECnet +#define MUSL_AF_NETBEUI MUSL_PF_NETBEUI +#define MUSL_AF_SECURITY MUSL_PF_SECURITY +#define MUSL_AF_KEY MUSL_PF_KEY +#define MUSL_AF_NETLINK MUSL_PF_NETLINK +#define MUSL_AF_ROUTE MUSL_PF_ROUTE +#define MUSL_AF_PACKET MUSL_PF_PACKET +#define MUSL_AF_ASH MUSL_PF_ASH +#define MUSL_AF_ECONET MUSL_PF_ECONET +#define MUSL_AF_ATMSVC MUSL_PF_ATMSVC +#define MUSL_AF_RDS MUSL_PF_RDS +#define MUSL_AF_SNA MUSL_PF_SNA +#define MUSL_AF_IRDA MUSL_PF_IRDA +#define MUSL_AF_PPPOX MUSL_PF_PPPOX +#define MUSL_AF_WANPIPE MUSL_PF_WANPIPE +#define MUSL_AF_LLC MUSL_PF_LLC +#define MUSL_AF_IB MUSL_PF_IB +#define MUSL_AF_MPLS MUSL_PF_MPLS +#define MUSL_AF_CAN MUSL_PF_CAN +#define MUSL_AF_TIPC MUSL_PF_TIPC +#define MUSL_AF_BLUETOOTH MUSL_PF_BLUETOOTH +#define MUSL_AF_IUCV MUSL_PF_IUCV +#define MUSL_AF_RXRPC MUSL_PF_RXRPC +#define MUSL_AF_ISDN MUSL_PF_ISDN +#define MUSL_AF_PHONET MUSL_PF_PHONET +#define MUSL_AF_IEEE802154 MUSL_PF_IEEE802154 +#define MUSL_AF_CAIF MUSL_PF_CAIF +#define MUSL_AF_ALG MUSL_PF_ALG +#define MUSL_AF_NFC MUSL_PF_NFC +#define MUSL_AF_VSOCK MUSL_PF_VSOCK +#define MUSL_AF_KCM MUSL_PF_KCM +#define MUSL_AF_MAX MUSL_PF_MAX + +static int Translate_Socket_Domain(int domain) +{ + switch(domain) + { +// case MUSL_PF_UNSPEC: return PF_UNSPEC; +// case MUSL_PF_LOCAL: return PF_LOCAL; +// case MUSL_PF_UNIX: return PF_UNIX; +// case MUSL_PF_FILE: return PF_FILE; +// case MUSL_PF_INET: return PF_INET; +// case MUSL_PF_AX25: return PF_AX25; +// case MUSL_PF_IPX: return PF_IPX; +// case MUSL_PF_APPLETALK: return PF_APPLETALK; +// case MUSL_PF_NETROM: return PF_NETROM; +// case MUSL_PF_BRIDGE: return PF_BRIDGE; +// case MUSL_PF_ATMPVC: return PF_ATMPVC; +// case MUSL_PF_X25: return PF_X25; +// case MUSL_PF_INET6: return PF_INET6; +// case MUSL_PF_ROSE: return PF_ROSE; +// case MUSL_PF_DECnet: return PF_DECnet; +// case MUSL_PF_NETBEUI: return PF_NETBEUI; +// case MUSL_PF_SECURITY: return PF_SECURITY; +// case MUSL_PF_KEY: return PF_KEY; +// case MUSL_PF_NETLINK: return PF_NETLINK; +// case MUSL_PF_ROUTE: return PF_ROUTE; +// case MUSL_PF_PACKET: return PF_PACKET; +// case MUSL_PF_ASH: return PF_ASH; +// case MUSL_PF_ECONET: return PF_ECONET; +// case MUSL_PF_ATMSVC: return PF_ATMSVC; +// case MUSL_PF_RDS: return PF_RDS; +// case MUSL_PF_SNA: return PF_SNA; +// case MUSL_PF_IRDA: return PF_IRDA; +// case MUSL_PF_PPPOX: return PF_PPPOX; +// case MUSL_PF_WANPIPE: return PF_WANPIPE; +// case MUSL_PF_LLC: return PF_LLC; +// case MUSL_PF_IB: return PF_IB; +// case MUSL_PF_MPLS: return PF_MPLS; +// case MUSL_PF_CAN: return PF_CAN; +// case MUSL_PF_TIPC: return PF_TIPC; +// case MUSL_PF_BLUETOOTH: return PF_BLUETOOTH; +// case MUSL_PF_IUCV: return PF_IUCV; +// case MUSL_PF_RXRPC: return PF_RXRPC; +// case MUSL_PF_ISDN: return PF_ISDN; +// case MUSL_PF_PHONET: return PF_PHONET; +// case MUSL_PF_IEEE802154: return PF_IEEE802154; +// case MUSL_PF_CAIF: return PF_CAIF; +// case MUSL_PF_ALG: return PF_ALG; +// case MUSL_PF_NFC: return PF_NFC; +// case MUSL_PF_VSOCK: return PF_VSOCK; +// case MUSL_PF_KCM: return PF_KCM; +// case MUSL_PF_MAX: return PF_MAX; + + case MUSL_AF_UNSPEC: return AF_UNSPEC; +#ifdef AF_LOCAL + case MUSL_AF_LOCAL: return AF_LOCAL; +#endif +// case MUSL_AF_UNIX: return AF_UNIX; +// case MUSL_AF_FILE: return AF_FILE; + case MUSL_AF_INET: return AF_INET; +// case MUSL_AF_AX25: return AF_AX25; + case MUSL_AF_IPX: return AF_IPX; + case MUSL_AF_APPLETALK: return AF_APPLETALK; +// case MUSL_AF_NETROM: return AF_NETROM; +// case MUSL_AF_BRIDGE: return AF_BRIDGE; +// case MUSL_AF_ATMPVC: return AF_ATMPVC; +// case MUSL_AF_X25: return AF_X25; + case MUSL_AF_INET6: return AF_INET6; +// case MUSL_AF_ROSE: return AF_ROSE; + case MUSL_AF_DECnet: return AF_DECnet; +// case MUSL_AF_NETBEUI: return AF_NETBEUI; +// case MUSL_AF_SECURITY: return AF_SECURITY; +// case MUSL_AF_KEY: return AF_KEY; +// case MUSL_AF_NETLINK: return AF_NETLINK; +// case MUSL_AF_ROUTE: return AF_ROUTE; +// case MUSL_AF_PACKET: return AF_PACKET; +// case MUSL_AF_ASH: return AF_ASH; +// case MUSL_AF_ECONET: return AF_ECONET; +// case MUSL_AF_ATMSVC: return AF_ATMSVC; +// case MUSL_AF_RDS: return AF_RDS; + case MUSL_AF_SNA: return AF_SNA; +// case MUSL_AF_IRDA: return AF_IRDA; +// case MUSL_AF_PPPOX: return AF_PPPOX; +// case MUSL_AF_WANPIPE: return AF_WANPIPE; +// case MUSL_AF_LLC: return AF_LLC; +// case MUSL_AF_IB: return AF_IB; +// case MUSL_AF_MPLS: return AF_MPLS; +// case MUSL_AF_CAN: return AF_CAN; +// case MUSL_AF_TIPC: return AF_TIPC; +// case MUSL_AF_BLUETOOTH: return AF_BLUETOOTH; +// case MUSL_AF_IUCV: return AF_IUCV; +// case MUSL_AF_RXRPC: return AF_RXRPC; +#ifdef AF_ISDN + case MUSL_AF_ISDN: return AF_ISDN; +#endif +// case MUSL_AF_PHONET: return AF_PHONET; +// case MUSL_AF_IEEE802154: return AF_IEEE802154; +// case MUSL_AF_CAIF: return AF_CAIF; +// case MUSL_AF_ALG: return AF_ALG; +// case MUSL_AF_NFC: return AF_NFC; +// case MUSL_AF_VSOCK: return AF_VSOCK; +// case MUSL_AF_KCM: return AF_KCM; + case MUSL_AF_MAX: return AF_MAX; + default: + fprintf(stderr, "Uncrecognized Socket Domain %d!\n", domain); + return domain; + } +} + +#define MUSL_SOCK_STREAM 1 +#define MUSL_SOCK_DGRAM 2 +#define MUSL_SOCK_RAW 3 +#define MUSL_SOCK_RDM 4 +#define MUSL_SOCK_SEQPACKET 5 +#define MUSL_SOCK_DCCP 6 +#define MUSL_SOCK_PACKET 10 +#define MUSL_SOCK_CLOEXEC 02000000 +#define MUSL_SOCK_NONBLOCK 04000 + +static int Translate_Socket_Type(int type) +{ + if ((type & MUSL_SOCK_CLOEXEC) != 0) + { + fprintf(stderr, "Unsupported MUSL SOCK_CLOEXEC passed!\n"); + type &= ~MUSL_SOCK_CLOEXEC; + } + if ((type & MUSL_SOCK_NONBLOCK) != 0) + { + fprintf(stderr, "Unsupported MUSL SOCK_NONBLOCK passed!\n"); + type &= ~MUSL_SOCK_NONBLOCK; + } + + switch(type) + { + case MUSL_SOCK_STREAM: return SOCK_STREAM; + case MUSL_SOCK_DGRAM: return SOCK_DGRAM; + case MUSL_SOCK_RAW: return SOCK_RAW; + case MUSL_SOCK_RDM: return SOCK_RDM; + case MUSL_SOCK_SEQPACKET: return SOCK_SEQPACKET; +// case MUSL_SOCK_DCCP: return SOCK_DCCP; +// case MUSL_SOCK_PACKET: return SOCK_PACKET; + default: + fprintf(stderr, "Uncrecognized socket type %d!\n", type); + return type; + } +} + +#define MUSL_IPPROTO_IP 0 +#define MUSL_IPPROTO_HOPOPTS 0 +#define MUSL_IPPROTO_ICMP 1 +#define MUSL_IPPROTO_IGMP 2 +#define MUSL_IPPROTO_IPIP 4 +#define MUSL_IPPROTO_TCP 6 +#define MUSL_IPPROTO_EGP 8 +#define MUSL_IPPROTO_PUP 12 +#define MUSL_IPPROTO_UDP 17 +#define MUSL_IPPROTO_IDP 22 +#define MUSL_IPPROTO_TP 29 +#define MUSL_IPPROTO_DCCP 33 +#define MUSL_IPPROTO_IPV6 41 +#define MUSL_IPPROTO_ROUTING 43 +#define MUSL_IPPROTO_FRAGMENT 44 +#define MUSL_IPPROTO_RSVP 46 +#define MUSL_IPPROTO_GRE 47 +#define MUSL_IPPROTO_ESP 50 +#define MUSL_IPPROTO_AH 51 +#define MUSL_IPPROTO_ICMPV6 58 +#define MUSL_IPPROTO_NONE 59 +#define MUSL_IPPROTO_DSTOPTS 60 +#define MUSL_IPPROTO_MTP 92 +#define MUSL_IPPROTO_BEETPH 94 +#define MUSL_IPPROTO_ENCAP 98 +#define MUSL_IPPROTO_PIM 103 +#define MUSL_IPPROTO_COMP 108 +#define MUSL_IPPROTO_SCTP 132 +#define MUSL_IPPROTO_MH 135 +#define MUSL_IPPROTO_UDPLITE 136 +#define MUSL_IPPROTO_MPLS 137 +#define MUSL_IPPROTO_RAW 255 + +static int Translate_Socket_Protocol(int protocol) +{ + switch(protocol) + { + case MUSL_IPPROTO_IP: return IPPROTO_IP; +// case MUSL_IPPROTO_HOPOPTS: return IPPROTO_HOPOPTS; + case MUSL_IPPROTO_ICMP: return IPPROTO_ICMP; + case MUSL_IPPROTO_IGMP: return IPPROTO_IGMP; +#ifdef IPPROTO_IPIP + case MUSL_IPPROTO_IPIP: return IPPROTO_IPIP; +#endif + case MUSL_IPPROTO_TCP: return IPPROTO_TCP; + case MUSL_IPPROTO_EGP: return IPPROTO_EGP; + case MUSL_IPPROTO_PUP: return IPPROTO_PUP; + case MUSL_IPPROTO_UDP: return IPPROTO_UDP; + case MUSL_IPPROTO_IDP: return IPPROTO_IDP; +#ifdef IPPROTO_TP + case MUSL_IPPROTO_TP: return IPPROTO_TP; +#endif +// case MUSL_IPPROTO_DCCP: return IPPROTO_DCCP; + case MUSL_IPPROTO_IPV6: return IPPROTO_IPV6; + case MUSL_IPPROTO_ROUTING: return IPPROTO_ROUTING; + case MUSL_IPPROTO_FRAGMENT: return IPPROTO_FRAGMENT; +#ifdef IPPROTO_RSVP + case MUSL_IPPROTO_RSVP: return IPPROTO_RSVP; +#endif +#ifdef IPPROTO_GRE + case MUSL_IPPROTO_GRE: return IPPROTO_GRE; +#endif + case MUSL_IPPROTO_ESP: return IPPROTO_ESP; + case MUSL_IPPROTO_AH: return IPPROTO_AH; + case MUSL_IPPROTO_ICMPV6: return IPPROTO_ICMPV6; + case MUSL_IPPROTO_NONE: return IPPROTO_NONE; + case MUSL_IPPROTO_DSTOPTS: return IPPROTO_DSTOPTS; +#ifdef IPPROTO_MTP + case MUSL_IPPROTO_MTP: return IPPROTO_MTP; +#endif +// case MUSL_IPPROTO_BEETPH: return IPPROTO_BEETPH; +#ifdef IPPROTO_ENCAP + case MUSL_IPPROTO_ENCAP: return IPPROTO_ENCAP; +#endif + case MUSL_IPPROTO_PIM: return IPPROTO_PIM; +// case MUSL_IPPROTO_COMP: return IPPROTO_COMP; + case MUSL_IPPROTO_SCTP: return IPPROTO_SCTP; +// case MUSL_IPPROTO_MH: return IPPROTO_MH; +// case MUSL_IPPROTO_UDPLITE: return IPPROTO_UDPLITE; +// case MUSL_IPPROTO_MPLS: return IPPROTO_MPLS; + case MUSL_IPPROTO_RAW: return IPPROTO_RAW; + default: + fprintf(stderr, "Unrecognized socket protocol %d!\n", protocol); + return protocol; + } +} + +#define MUSL_SOL_SOCKET 1 +#define MUSL_SOL_IP 0 +#define MUSL_SOL_IPV6 41 +#define MUSL_SOL_ICMPV6 58 +#define MUSL_SOL_RAW 255 +#define MUSL_SOL_DECNET 261 +#define MUSL_SOL_X25 262 +#define MUSL_SOL_PACKET 263 +#define MUSL_SOL_ATM 264 +#define MUSL_SOL_AAL 265 +#define MUSL_SOL_IRDA 266 +#define MUSL_SOL_NETBEUI 267 +#define MUSL_SOL_LLC 268 +#define MUSL_SOL_DCCP 269 +#define MUSL_SOL_NETLINK 270 +#define MUSL_SOL_TIPC 271 +#define MUSL_SOL_RXRPC 272 +#define MUSL_SOL_PPPOL2TP 273 +#define MUSL_SOL_BLUETOOTH 274 +#define MUSL_SOL_PNPIPE 275 +#define MUSL_SOL_RDS 276 +#define MUSL_SOL_IUCV 277 +#define MUSL_SOL_CAIF 278 +#define MUSL_SOL_ALG 279 +#define MUSL_SOL_NFC 280 +#define MUSL_SOL_KCM 281 + +static int Translate_Socket_Level(int level) +{ + switch(level) + { + case MUSL_SOL_SOCKET: return SOL_SOCKET; + case MUSL_IPPROTO_TCP: return IPPROTO_TCP; +// case MUSL_SOL_IP: return SOL_IP; +// case MUSL_SOL_IPV6: return SOL_IPV6; +// case MUSL_SOL_ICMPV6: return SOL_ICMPV6; +// case MUSL_SOL_RAW: return SOL_RAW; +// case MUSL_SOL_DECNET: return SOL_DECNET; +// case MUSL_SOL_X25: return SOL_X25; +// case MUSL_SOL_PACKET: return SOL_PACKET; +// case MUSL_SOL_ATM: return SOL_ATM; +// case MUSL_SOL_AAL: return SOL_AAL; +// case MUSL_SOL_IRDA: return SOL_IRDA; +// case MUSL_SOL_NETBEUI: return SOL_NETBEUI; +// case MUSL_SOL_LLC: return SOL_LLC; +// case MUSL_SOL_DCCP: return SOL_DCCP; +// case MUSL_SOL_NETLINK: return SOL_NETLINK; +// case MUSL_SOL_TIPC: return SOL_TIPC; +// case MUSL_SOL_RXRPC: return SOL_RXRPC; +// case MUSL_SOL_PPPOL2TP: return SOL_PPPOL2TP; +// case MUSL_SOL_BLUETOOTH: return SOL_BLUETOOTH; +// case MUSL_SOL_PNPIPE: return SOL_PNPIPE; +// case MUSL_SOL_RDS: return SOL_RDS; +// case MUSL_SOL_IUCV: return SOL_IUCV; +// case MUSL_SOL_CAIF: return SOL_CAIF; +// case MUSL_SOL_ALG: return SOL_ALG; +// case MUSL_SOL_NFC: return SOL_NFC; +// case MUSL_SOL_KCM: return SOL_KCM; + default: + fprintf(stderr, "Uncrecognized socket level %d!\n", level); + return level; + } +} + +#define MUSL_SO_DEBUG 1 +#define MUSL_SO_REUSEADDR 2 +#define MUSL_SO_TYPE 3 +#define MUSL_SO_ERROR 4 +#define MUSL_SO_DONTROUTE 5 +#define MUSL_SO_BROADCAST 6 +#define MUSL_SO_SNDBUF 7 +#define MUSL_SO_RCVBUF 8 +#define MUSL_SO_KEEPALIVE 9 +#define MUSL_SO_OOBINLINE 10 +#define MUSL_SO_NO_CHECK 11 +#define MUSL_SO_PRIORITY 12 +#define MUSL_SO_LINGER 13 +#define MUSL_SO_BSDCOMPAT 14 +#define MUSL_SO_REUSEPORT 15 +#define MUSL_SO_PASSCRED 16 +#define MUSL_SO_PEERCRED 17 +#define MUSL_SO_RCVLOWAT 18 +#define MUSL_SO_SNDLOWAT 19 +#define MUSL_SO_RCVTIMEO 20 +#define MUSL_SO_SNDTIMEO 21 +#define MUSL_SO_ACCEPTCONN 30 +#define MUSL_SO_SNDBUFFORCE 32 +#define MUSL_SO_RCVBUFFORCE 33 +#define MUSL_SO_PROTOCOL 38 +#define MUSL_SO_DOMAIN 39 +#define MUSL_SO_SECURITY_AUTHENTICATION 22 +#define MUSL_SO_SECURITY_ENCRYPTION_TRANSPORT 23 +#define MUSL_SO_SECURITY_ENCRYPTION_NETWORK 24 +#define MUSL_SO_BINDTODEVICE 25 +#define MUSL_SO_ATTACH_FILTER 26 +#define MUSL_SO_DETACH_FILTER 27 +#define MUSL_SO_PEERNAME 28 +#define MUSL_SO_TIMESTAMP 29 +#define MUSL_SO_PEERSEC 31 +#define MUSL_SO_PASSSEC 34 +#define MUSL_SO_TIMESTAMPNS 35 +#define MUSL_SO_MARK 36 +#define MUSL_SO_TIMESTAMPING 37 +#define MUSL_SO_RXQ_OVFL 40 +#define MUSL_SO_WIFI_STATUS 41 +#define MUSL_SO_PEEK_OFF 42 +#define MUSL_SO_NOFCS 43 +#define MUSL_SO_LOCK_FILTER 44 +#define MUSL_SO_SELECT_ERR_QUEUE 45 +#define MUSL_SO_BUSY_POLL 46 +#define MUSL_SO_MAX_PACING_RATE 47 +#define MUSL_SO_BPF_EXTENSIONS 48 +#define MUSL_SO_INCOMING_CPU 49 +#define MUSL_SO_ATTACH_BPF 50 +#define MUSL_SO_ATTACH_REUSEPORT_CBPF 51 +#define MUSL_SO_ATTACH_REUSEPORT_EBPF 52 +#define MUSL_SO_CNX_ADVICE 53 + +static int Translate_SOL_SOCKET_option(int sockopt) +{ + switch(sockopt) + { + case MUSL_SO_DEBUG: return SO_DEBUG; + case MUSL_SO_REUSEADDR: return SO_REUSEADDR; + case MUSL_SO_TYPE: return SO_TYPE; + case MUSL_SO_ERROR: return SO_ERROR; + case MUSL_SO_DONTROUTE: return SO_DONTROUTE; + case MUSL_SO_BROADCAST: return SO_BROADCAST; + case MUSL_SO_SNDBUF: return SO_SNDBUF; + case MUSL_SO_RCVBUF: return SO_RCVBUF; + case MUSL_SO_KEEPALIVE: return SO_KEEPALIVE; + case MUSL_SO_OOBINLINE: return SO_OOBINLINE; +// case MUSL_SO_NO_CHECK: return SO_NO_CHECK; +// case MUSL_SO_PRIORITY: return SO_PRIORITY; + case MUSL_SO_LINGER: return SO_LINGER; +// case MUSL_SO_BSDCOMPAT: return SO_BSDCOMPAT; +#ifdef SO_REUSEPORT + case MUSL_SO_REUSEPORT: return SO_REUSEPORT; +#endif +// case MUSL_SO_PASSCRED: return SO_PASSCRED; +// case MUSL_SO_PEERCRED: return SO_PEERCRED; + case MUSL_SO_RCVLOWAT: return SO_RCVLOWAT; + case MUSL_SO_SNDLOWAT: return SO_SNDLOWAT; + case MUSL_SO_RCVTIMEO: return SO_RCVTIMEO; + case MUSL_SO_SNDTIMEO: return SO_SNDTIMEO; + case MUSL_SO_ACCEPTCONN: return SO_ACCEPTCONN; +// case MUSL_SO_SNDBUFFORCE: return SO_SNDBUFFORCE; +// case MUSL_SO_RCVBUFFORCE: return SO_RCVBUFFORCE; +// case MUSL_SO_PROTOCOL: return SO_PROTOCOL; +// case MUSL_SO_DOMAIN: return SO_DOMAIN; +// case MUSL_SO_SECURITY_AUTHENTICATION: return SO_SECURITY_AUTHENTICATION; +// case MUSL_SO_SECURITY_ENCRYPTION_TRANSPORT: return SO_SECURITY_ENCRYPTION_TRANSPORT; +// case MUSL_SO_SECURITY_ENCRYPTION_NETWORK: return SO_SECURITY_ENCRYPTION_NETWORK; +// case MUSL_SO_BINDTODEVICE: return SO_BINDTODEVICE; +// case MUSL_SO_ATTACH_FILTER: return SO_ATTACH_FILTER; +// case MUSL_SO_DETACH_FILTER: return SO_DETACH_FILTER; +// case MUSL_SO_PEERNAME: return SO_PEERNAME; +#ifdef SO_TIMESTAMP + case MUSL_SO_TIMESTAMP: return SO_TIMESTAMP; +#endif +// case MUSL_SO_PEERSEC: return SO_PEERSEC; +// case MUSL_SO_PASSSEC: return SO_PASSSEC; +// case MUSL_SO_TIMESTAMPNS: return SO_TIMESTAMPNS; +// case MUSL_SO_MARK: return SO_MARK; +// case MUSL_SO_TIMESTAMPING: return SO_TIMESTAMPING; +// case MUSL_SO_RXQ_OVFL: return SO_RXQ_OVFL; +// case MUSL_SO_WIFI_STATUS: return SO_WIFI_STATUS; +// case MUSL_SO_PEEK_OFF: return SO_PEEK_OFF; +// case MUSL_SO_NOFCS: return SO_NOFCS; +// case MUSL_SO_LOCK_FILTER: return SO_LOCK_FILTER; +// case MUSL_SO_SELECT_ERR_QUEUE: return SO_SELECT_ERR_QUEUE; +// case MUSL_SO_BUSY_POLL: return SO_BUSY_POLL; +// case MUSL_SO_MAX_PACING_RATE: return SO_MAX_PACING_RATE; +// case MUSL_SO_BPF_EXTENSIONS: return SO_BPF_EXTENSIONS; +// case MUSL_SO_INCOMING_CPU: return SO_INCOMING_CPU; +// case MUSL_SO_ATTACH_BPF: return SO_ATTACH_BPF; +// case MUSL_SO_ATTACH_REUSEPORT_CBPF: return SO_ATTACH_REUSEPORT_CBPF; +// case MUSL_SO_ATTACH_REUSEPORT_EBPF: return SO_ATTACH_REUSEPORT_EBPF; +// case MUSL_SO_CNX_ADVICE: return SO_CNX_ADVICE; + default: + fprintf(stderr, "Unrecognized SOL_SOCKET option %d!\n", sockopt); + return sockopt; + } +} + +#define MUSL_TCP_NODELAY 1 +#define MUSL_TCP_MAXSEG 2 +#define MUSL_TCP_CORK 3 +#define MUSL_TCP_KEEPIDLE 4 +#define MUSL_TCP_KEEPINTVL 5 +#define MUSL_TCP_KEEPCNT 6 +#define MUSL_TCP_SYNCNT 7 +#define MUSL_TCP_LINGER2 8 +#define MUSL_TCP_DEFER_ACCEPT 9 +#define MUSL_TCP_WINDOW_CLAMP 10 +#define MUSL_TCP_INFO 11 +#define MUSL_TCP_QUICKACK 12 +#define MUSL_TCP_CONGESTION 13 +#define MUSL_TCP_MD5SIG 14 +#define MUSL_TCP_THIN_LINEAR_TIMEOUTS 16 +#define MUSL_TCP_THIN_DUPACK 17 +#define MUSL_TCP_USER_TIMEOUT 18 +#define MUSL_TCP_REPAIR 19 +#define MUSL_TCP_REPAIR_QUEUE 20 +#define MUSL_TCP_QUEUE_SEQ 21 +#define MUSL_TCP_REPAIR_OPTIONS 22 +#define MUSL_TCP_FASTOPEN 23 +#define MUSL_TCP_TIMESTAMP 24 +#define MUSL_TCP_NOTSENT_LOWAT 25 +#define MUSL_TCP_CC_INFO 26 +#define MUSL_TCP_SAVE_SYN 27 +#define MUSL_TCP_SAVED_SYN 28 + +static int Translate_IPPROTO_TCP_option(int sockopt) +{ + switch(sockopt) + { + case MUSL_TCP_NODELAY: return TCP_NODELAY; + case MUSL_TCP_MAXSEG: return TCP_MAXSEG; +// case MUSL_TCP_CORK: return TCP_CORK; +// case MUSL_TCP_KEEPIDLE: return TCP_KEEPIDLE; + case MUSL_TCP_KEEPINTVL: return TCP_KEEPINTVL; + case MUSL_TCP_KEEPCNT: return TCP_KEEPCNT; +// case MUSL_TCP_SYNCNT: return TCP_SYNCNT; +// case MUSL_TCP_LINGER2: return TCP_LINGER2; +// case MUSL_TCP_DEFER_ACCEPT: return TCP_DEFER_ACCEPT; +// case MUSL_TCP_WINDOW_CLAMP: return TCP_WINDOW_CLAMP; +// case MUSL_TCP_INFO: return TCP_INFO; +// case MUSL_TCP_QUICKACK: return TCP_QUICKACK; +// case MUSL_TCP_CONGESTION: return TCP_CONGESTION; +// case MUSL_TCP_MD5SIG: return TCP_MD5SIG; +// case MUSL_TCP_THIN_LINEAR_TIMEOUTS: return TCP_THIN_LINEAR_TIMEOUTS; +// case MUSL_TCP_THIN_DUPACK: return TCP_THIN_DUPACK; +// case MUSL_TCP_USER_TIMEOUT: return TCP_USER_TIMEOUT; +// case MUSL_TCP_REPAIR: return TCP_REPAIR; +// case MUSL_TCP_REPAIR_QUEUE: return TCP_REPAIR_QUEUE; +// case MUSL_TCP_QUEUE_SEQ: return TCP_QUEUE_SEQ; +// case MUSL_TCP_REPAIR_OPTIONS: return TCP_REPAIR_OPTIONS; + case MUSL_TCP_FASTOPEN: return TCP_FASTOPEN; +// case MUSL_TCP_TIMESTAMP: return TCP_TIMESTAMP; +#ifdef TCP_NOTSENT_LOWAT + case MUSL_TCP_NOTSENT_LOWAT: return TCP_NOTSENT_LOWAT; +#endif +// case MUSL_TCP_CC_INFO: return TCP_CC_INFO; +// case MUSL_TCP_SAVE_SYN: return TCP_SAVE_SYN; +// case MUSL_TCP_SAVED_SYN: return TCP_SAVED_SYN; + default: + fprintf(stderr, "Unrecognized IPPROTO_TCP option %d!\n", sockopt); + return sockopt; + } +} + +void Socket(int client_fd, uint8_t *data, uint64_t numBytes) // int socket(int domain, int type, int protocol); +{ + struct MSG { + SocketCallHeader header; + int domain; + int type; + int protocol; + }; + MSG *d = (MSG*)data; + + d->domain = Translate_Socket_Domain(d->domain); + d->type = Translate_Socket_Type(d->type); + d->protocol = Translate_Socket_Protocol(d->protocol); + int ret = socket(d->domain, d->type, d->protocol); + int errorCode = (ret < 0) ? GET_SOCKET_ERROR() : 0; + +#ifdef POSIX_SOCKET_DEBUG + printf("socket(domain=%d,type=%d,protocol=%d)->%d\n", d->domain, d->type, d->protocol, ret); + if (errorCode) PRINT_SOCKET_ERROR(errorCode); +#endif + + if (errorCode == 0) + { + // The proxy client connection created a new socket - track its lifetime and mark the new socket to be part of + // this particular proxy connection so that it will be properly freed when the proxy connection disconnects, + // and that no other proxy connections will be able to access this socket. + TrackSocketUsedByConnection(client_fd, ret); + } + + struct { + int callId; + int ret; + int errno_; + } r; + r.callId = d->header.callId; + r.ret = ret; + r.errno_ = errorCode; + SendWebSocketMessage(client_fd, &r, sizeof(r)); +} + +void Socketpair(int client_fd, uint8_t *data, uint64_t numBytes) // int socketpair(int domain, int type, int protocol, int socket_vector[2]); +{ + struct MSG { + SocketCallHeader header; + int domain; + int type; + int protocol; + }; + MSG *d = (MSG*)data; + + int socket_vector[2]; + +#ifdef _MSC_VER + printf("TODO implement socketpair() on Windows\n"); + int ret = -1; + int errorCode = -1; +#else + d->domain = Translate_Socket_Domain(d->domain); + d->type = Translate_Socket_Type(d->type); + d->protocol = Translate_Socket_Protocol(d->protocol); + int ret = socketpair(d->domain, d->type, d->protocol, socket_vector); + int errorCode = (ret != 0) ? GET_SOCKET_ERROR() : 0; +#endif + +#ifdef POSIX_SOCKET_DEBUG + printf("socketpair(domain=%d,type=%d,protocol=%d, socket_vector=[%d,%d])->%d\n", d->domain, d->type, d->protocol, socket_vector[0], socket_vector[1], ret); + if (errorCode) PRINT_SOCKET_ERROR(errorCode); +#endif + + if (errorCode == 0) + { + // The proxy client connection created two new sockets - track their lifetime and mark the new sockets to be part of + // this particular proxy connection so that they will be properly freed when the proxy connection disconnects, + // and that no other proxy connections will be able to access these sockets. + TrackSocketUsedByConnection(client_fd, socket_vector[0]); + TrackSocketUsedByConnection(client_fd, socket_vector[1]); + } + + struct { + int callId; + int ret; + int errno_; + int sv[2]; + } r; + r.callId = d->header.callId; + r.ret = ret; + r.errno_ = errorCode; + r.sv[0] = socket_vector[0]; + r.sv[1] = socket_vector[1]; + SendWebSocketMessage(client_fd, &r, sizeof(r)); +} + +#define MUSL_SHUT_RD 0 +#define MUSL_SHUT_WR 1 +#define MUSL_SHUT_RDWR 2 + +static int Translate_Shutdown_How(int how) +{ + switch(how) + { + case MUSL_SHUT_RD: return SHUTDOWN_READ; + case MUSL_SHUT_WR: return SHUTDOWN_WRITE; + case MUSL_SHUT_RDWR: return SHUTDOWN_BIDIRECTIONAL; + default: + fprintf(stderr, "Unrecognized shutdown() how option %d!\n", how); + return how; + } +} + +void Shutdown(int client_fd, uint8_t *data, uint64_t numBytes) // int shutdown(int socket, int how); +{ + struct MSG { + SocketCallHeader header; + int socket; + int how; + }; + MSG *d = (MSG*)data; + + d->how = Translate_Shutdown_How(d->how); + + int ret, errorCode; + + if (IsSocketPartOfConnection(client_fd, d->socket)) + { + ret = shutdown(d->socket, d->how); + errorCode = (ret != 0) ? GET_SOCKET_ERROR() : 0; +#ifdef POSIX_SOCKET_DEBUG + printf("shutdown(socket=%d,how=%d)->%d\n", d->socket, d->how, ret); + if (errorCode) PRINT_SOCKET_ERROR(errorCode); +#endif + if (errorCode == 0 && d->how == SHUTDOWN_BIDIRECTIONAL) + { + // Proxy client performed bidirectional close, mark this socket as being disconnected, and disallow it + // from accessing this socket again - this close()s the socket. + CloseSocketByConnection(client_fd, d->socket); + } + } + else + { + fprintf(stderr, "shutdown(socket=%d,how=%d): Proxy client connection client_fd=%d attempted to call shutdown() on a socket fd=%d that it did not create (or has already shut down)\n", d->socket, d->how, client_fd, d->socket); + ret = errorCode = -1; + } + + struct { + int callId; + int ret; + int errno_; + } r; + r.callId = d->header.callId; + r.ret = ret; + r.errno_ = (ret != 0) ? errno : 0; + SendWebSocketMessage(client_fd, &r, sizeof(r)); +} + +void Bind(int client_fd, uint8_t *data, uint64_t numBytes) // int bind(int socket, const struct sockaddr *address, socklen_t address_len); +{ + struct MSG { + SocketCallHeader header; + int socket; + uint32_t/*socklen_t*/ address_len; + uint8_t address[]; + }; + MSG *d = (MSG*)data; + + int ret, errorCode; + + if (IsSocketPartOfConnection(client_fd, d->socket)) + { + ret = bind(d->socket, (sockaddr*)d->address, d->address_len); + errorCode = (ret != 0) ? GET_SOCKET_ERROR() : 0; +#ifdef POSIX_SOCKET_DEBUG + printf("bind(socket=%d,address=%p,address_len=%d, address=\"%s\")->%d\n", d->socket, d->address, d->address_len, BufferToString(d->address, d->address_len), ret); + if (errorCode) PRINT_SOCKET_ERROR(errorCode); +#endif + } + else + { + fprintf(stderr, "bind(): Proxy client connection client_fd=%d attempted to call bind() on a socket fd=%d that it did not create (or has already shut down)\n", client_fd, d->socket); + ret = errorCode = -1; + } + + struct { + int callId; + int ret; + int errno_; + } r; + r.callId = d->header.callId; + r.ret = ret; + r.errno_ = (ret != 0) ? errno : 0; + SendWebSocketMessage(client_fd, &r, sizeof(r)); +} + +void Connect(int client_fd, uint8_t *data, uint64_t numBytes) // int connect(int socket, const struct sockaddr *address, socklen_t address_len); +{ + struct MSG { + SocketCallHeader header; + int socket; + uint32_t/*socklen_t*/ address_len; + uint8_t address[]; + }; + MSG *d = (MSG*)data; + + int actualAddressLen = MIN(d->address_len, (uint32_t)numBytes - sizeof(MSG)); + + int ret, errorCode; + + if (IsSocketPartOfConnection(client_fd, d->socket)) + { + ret = connect(d->socket, (sockaddr*)d->address, actualAddressLen); + errorCode = (ret != 0) ? GET_SOCKET_ERROR() : 0; +#ifdef POSIX_SOCKET_DEBUG + printf("connect(socket=%d,address=%p,address_len=%d, address=\"%s\")->%d\n", d->socket, d->address, d->address_len, BufferToString(d->address, actualAddressLen), ret); + if (errorCode) PRINT_SOCKET_ERROR(errorCode); +#endif + } + else + { + fprintf(stderr, "connect(): Proxy client connection client_fd=%d attempted to call connect() on a socket fd=%d that it did not create (or has already shut down)\n", client_fd, d->socket); + ret = errorCode = -1; + } + + struct { + int callId; + int ret; + int errno_; + } r; + r.callId = d->header.callId; + r.ret = ret; + r.errno_ = (ret != 0) ? errno : 0; + SendWebSocketMessage(client_fd, &r, sizeof(r)); +} + +void Listen(int client_fd, uint8_t *data, uint64_t numBytes) // int listen(int socket, int backlog); +{ + struct MSG { + SocketCallHeader header; + int socket; + int backlog; + }; + MSG *d = (MSG*)data; + + int ret, errorCode; + + if (IsSocketPartOfConnection(client_fd, d->socket)) + { + ret = listen(d->socket, d->backlog); + errorCode = (ret != 0) ? GET_SOCKET_ERROR() : 0; +#ifdef POSIX_SOCKET_DEBUG + printf("listen(socket=%d,backlog=%d)->%d\n", d->socket, d->backlog, ret); + if (errorCode) PRINT_SOCKET_ERROR(errorCode); +#endif + } + else + { + fprintf(stderr, "bind(): Proxy client connection client_fd=%d attempted to call bind() on a socket fd=%d that it did not create (or has already shut down)\n", client_fd, d->socket); + ret = errorCode = -1; + } + + struct { + int callId; + int ret; + int errno_; + } r; + r.callId = d->header.callId; + r.ret = ret; + r.errno_ = (ret != 0) ? errno : 0; + SendWebSocketMessage(client_fd, &r, sizeof(r)); +} + +void Accept(int client_fd, uint8_t *data, uint64_t numBytes) // int accept(int socket, struct sockaddr *address, socklen_t *address_len); +{ + struct MSG { + SocketCallHeader header; + int socket; + uint32_t/*socklen_t*/ address_len; + }; + MSG *d = (MSG*)data; + + uint8_t address[MAX_SOCKADDR_SIZE] = {}; + socklen_t addressLen = (socklen_t)MAX(0, MIN(d->address_len, MAX_SOCKADDR_SIZE)); + + int ret, errorCode; + + if (IsSocketPartOfConnection(client_fd, d->socket)) + { +#ifdef POSIX_SOCKET_DEBUG + printf("accept(socket=%d,address=%p,address_len=%u, address=\"%s\")\n", d->socket, address, d->address_len, BufferToString(address, addressLen)); +#endif + ret = accept(d->socket, d->address_len ? (sockaddr*)address : 0, d->address_len ? &addressLen : 0); + errorCode = (ret < 0) ? GET_SOCKET_ERROR() : 0; + +#ifdef POSIX_SOCKET_DEBUG + printf("accept returned %d (address=\"%s\")\n", ret, BufferToString(address, addressLen)); + if (errorCode) PRINT_SOCKET_ERROR(errorCode); +#endif + + if (ret > 0) + { + // New connection socket created by the proxy bridge, mark it as part of this WebSocket proxy connection. + TrackSocketUsedByConnection(client_fd, ret); + } + } + else + { + fprintf(stderr, "accept(): Proxy client connection client_fd=%d attempted to call accept() on a socket fd=%d that it did not create (or has already shut down)\n", client_fd, d->socket); + ret = errorCode = -1; + addressLen = 0; + } + + struct Result { + int callId; + int ret; + int errno_; + int address_len; + uint8_t address[]; + }; + + int actualAddressLen = MIN(addressLen, (socklen_t)d->address_len); + int resultSize = sizeof(Result) + actualAddressLen; + Result *r = (Result*)malloc(resultSize); + r->callId = d->header.callId; + r->ret = ret; + r->errno_ = errorCode; + r->address_len = addressLen; + memcpy(r->address, address, actualAddressLen); + SendWebSocketMessage(client_fd, r, resultSize); + free(r); +} + +void Getsockname(int client_fd, uint8_t *data, uint64_t numBytes) // int getsockname(int socket, struct sockaddr *address, socklen_t *address_len); +{ + struct MSG { + SocketCallHeader header; + int socket; + uint32_t/*socklen_t*/ address_len; + }; + MSG *d = (MSG*)data; + + uint8_t address[MAX_SOCKADDR_SIZE]; + + socklen_t addressLen = (socklen_t)d->address_len; + + int ret, errorCode; + + if (IsSocketPartOfConnection(client_fd, d->socket)) + { + ret = getsockname(d->socket, (sockaddr*)address, &addressLen); + errorCode = (ret != 0) ? GET_SOCKET_ERROR() : 0; + +#ifdef POSIX_SOCKET_DEBUG + printf("getsockname(socket=%d,address=%p,address_len=%u)->%d (ret address: \"%s\")\n", d->socket, address, d->address_len, ret, BufferToString(address, addressLen)); + if (errorCode) PRINT_SOCKET_ERROR(errorCode); +#endif + } + else + { + fprintf(stderr, "getsockname(): Proxy client connection client_fd=%d attempted to call getsockname() on a socket fd=%d that it did not create (or has already shut down)\n", client_fd, d->socket); + ret = errorCode = -1; + addressLen = 0; + } + + struct Result { + int callId; + int ret; + int errno_; + int address_len; + uint8_t address[]; + }; + int actualAddressLen = MIN(addressLen, (socklen_t)d->address_len); + int resultSize = sizeof(Result) + actualAddressLen; + Result *r = (Result*)malloc(resultSize); + r->callId = d->header.callId; + r->ret = ret; + r->errno_ = errorCode; + r->address_len = addressLen; + memcpy(r->address, address, actualAddressLen); + SendWebSocketMessage(client_fd, r, resultSize); + free(r); +} + +void Getpeername(int client_fd, uint8_t *data, uint64_t numBytes) // int getpeername(int socket, struct sockaddr *address, socklen_t *address_len); +{ + struct MSG { + SocketCallHeader header; + int socket; + uint32_t/*socklen_t*/ address_len; + }; + MSG *d = (MSG*)data; + + uint8_t address[MAX_SOCKADDR_SIZE]; + socklen_t addressLen = (socklen_t)d->address_len; + + int ret, errorCode; + + if (IsSocketPartOfConnection(client_fd, d->socket)) + { + ret = getpeername(d->socket, (sockaddr*)address, &addressLen); + errorCode = (ret != 0) ? GET_SOCKET_ERROR() : 0; + +#ifdef POSIX_SOCKET_DEBUG + printf("getpeername(socket=%d,address=%p,address_len=%u, address=\"%s\")->%d\n", d->socket, address, d->address_len, BufferToString(address, addressLen), ret); + if (errorCode) PRINT_SOCKET_ERROR(errorCode); +#endif + } + else + { + fprintf(stderr, "getpeername(): Proxy client connection client_fd=%d attempted to call getpeername() on a socket fd=%d that it did not create (or has already shut down)\n", client_fd, d->socket); + ret = errorCode = -1; + addressLen = 0; + } + + struct Result { + int callId; + int ret; + int errno_; + int address_len; + uint8_t address[]; + }; + int actualAddressLen = MIN(addressLen, (socklen_t)d->address_len); + int resultSize = sizeof(Result) + actualAddressLen; + Result *r = (Result*)malloc(resultSize); + r->callId = d->header.callId; + r->ret = ret; + r->errno_ = errorCode; + r->address_len = addressLen; + memcpy(r->address, address, actualAddressLen); + SendWebSocketMessage(client_fd, r, resultSize); + free(r); +} + +void Send(int client_fd, uint8_t *data, uint64_t numBytes) // ssize_t/int send(int socket, const void *message, size_t length, int flags); +{ + struct MSG { + SocketCallHeader header; + int socket; + uint32_t/*size_t*/ length; + int flags; + uint8_t message[]; + }; + MSG *d = (MSG*)data; + + int actualBytes = MIN((int)numBytes - sizeof(MSG), d->length); + SEND_RET_TYPE ret; + int errorCode; + + if (IsSocketPartOfConnection(client_fd, d->socket)) + { + ret = send(d->socket, (const char *)d->message, actualBytes, d->flags); + errorCode = (ret != 0) ? GET_SOCKET_ERROR() : 0; + +#ifdef POSIX_SOCKET_DEBUG + printf("send(socket=%d,message=%p,length=%zd,flags=%d, data=\"%s\")->" SEND_FORMATTING_SPECIFIER "\n", d->socket, d->message, d->length, d->flags, BufferToString(d->message, d->length), ret); + if (errorCode) PRINT_SOCKET_ERROR(errorCode); +#endif + } + else + { + fprintf(stderr, "send(): Proxy client connection client_fd=%d attempted to call send() on a socket fd=%d that it did not create (or has already shut down)\n", client_fd, d->socket); + ret = errorCode = -1; + } + + struct { + int callId; + int/*ssize_t/int*/ ret; + int errno_; + } r; + r.callId = d->header.callId; + r.ret = (int)ret; + r.errno_ = (ret != 0) ? errno : 0; + SendWebSocketMessage(client_fd, &r, sizeof(r)); +} + +void Recv(int client_fd, uint8_t *data, uint64_t numBytes) // ssize_t/int recv(int socket, void *buffer, size_t length, int flags); +{ + struct MSG { + SocketCallHeader header; + int socket; + uint32_t/*size_t*/ length; + int flags; + }; + MSG *d = (MSG*)data; + + uint8_t *buffer = (uint8_t*)malloc(d->length); + SEND_RET_TYPE ret; + int errorCode; + int receivedBytes; + + if (IsSocketPartOfConnection(client_fd, d->socket)) + { + ret = recv(d->socket, (char *)buffer, d->length, d->flags); + errorCode = (ret != 0) ? GET_SOCKET_ERROR() : 0; + receivedBytes = MAX(ret, 0); + +#ifdef POSIX_SOCKET_DEBUG + printf("recv(socket=%d,buffer=%p,length=%zd,flags=%d)->" SEND_FORMATTING_SPECIFIER " received \"%s\"\n", d->socket, buffer, d->length, d->flags, ret, BufferToString(buffer, receivedBytes)); + if (errorCode) PRINT_SOCKET_ERROR(errorCode); +#endif + } + else + { + fprintf(stderr, "recv(): Proxy client connection client_fd=%d attempted to call recv() on a socket fd=%d that it did not create (or has already shut down)\n", client_fd, d->socket); + ret = errorCode = -1; + receivedBytes = 0; + } + + struct Result { + int callId; + int/*ssize_t/int*/ ret; + int errno_; + uint8_t data[]; + }; + int resultSize = sizeof(Result) + receivedBytes; + Result *r = (Result *)malloc(resultSize); + r->callId = d->header.callId; + r->ret = (int)ret; + r->errno_ = errorCode; + memcpy(r->data, buffer, receivedBytes); + free(buffer); + SendWebSocketMessage(client_fd, r, resultSize); + free(r); +} + +void Sendto(int client_fd, uint8_t *data, uint64_t numBytes) // ssize_t/int sendto(int socket, const void *message, size_t length, int flags, const struct sockaddr *dest_addr, socklen_t dest_len); +{ + struct MSG { + SocketCallHeader header; + int socket; + uint32_t/*size_t*/ length; + int flags; + uint32_t/*socklen_t*/ dest_len; + uint8_t dest_addr[MAX_SOCKADDR_SIZE]; + uint8_t message[]; + }; + MSG *d = (MSG*)data; + + SEND_RET_TYPE ret; + int errorCode; + + if (IsSocketPartOfConnection(client_fd, d->socket)) + { + ret = sendto(d->socket, (const char *)d->message, d->length, d->flags, (sockaddr*)d->dest_addr, d->dest_len); + errorCode = (ret != 0) ? GET_SOCKET_ERROR() : 0; + +#ifdef POSIX_SOCKET_DEBUG + printf("sendto(socket=%d,message=%p,length=%zd,flags=%d,dest_addr=%p,dest_len=%d)->" SEND_FORMATTING_SPECIFIER "\n", d->socket, d->message, d->length, d->flags, d->dest_addr, d->dest_len, ret); + if (errorCode) PRINT_SOCKET_ERROR(errorCode); +#endif + } + else + { + fprintf(stderr, "sendto(): Proxy client connection client_fd=%d attempted to call sendto() on a socket fd=%d that it did not create (or has already shut down)\n", client_fd, d->socket); + ret = errorCode = -1; + } + + struct { + int callId; + int/*ssize_t/int*/ ret; + int errno_; + } r; + r.callId = d->header.callId; + r.ret = (int)ret; + r.errno_ = (ret != 0) ? errno : 0; + SendWebSocketMessage(client_fd, &r, sizeof(r)); +} + +void Recvfrom(int client_fd, uint8_t *data, uint64_t numBytes) // ssize_t/int recvfrom(int socket, void *buffer, size_t length, int flags, struct sockaddr *address, socklen_t *address_len); +{ + struct MSG { + SocketCallHeader header; + int socket; + uint32_t/*size_t*/ length; + int flags; + uint32_t/*socklen_t*/ address_len; + }; + MSG *d = (MSG*)data; + + uint8_t address[MAX_SOCKADDR_SIZE]; + uint8_t *buffer = (uint8_t *)malloc(d->length); + + socklen_t address_len = (socklen_t)d->address_len; + + int ret, errorCode, receivedBytes; + + if (IsSocketPartOfConnection(client_fd, d->socket)) + { + ret = recvfrom(d->socket, (char *)buffer, d->length, d->flags, (sockaddr*)address, &address_len); + errorCode = (ret != 0) ? GET_SOCKET_ERROR() : 0; +#ifdef POSIX_SOCKET_DEBUG + printf("recvfrom(socket=%d,buffer=%p,length=%zd,flags=%d,address=%p,address_len=%u, address=\"%s\")->%d\n", d->socket, buffer, d->length, d->flags, address, d->address_len, BufferToString(address, address_len), ret); + if (errorCode) PRINT_SOCKET_ERROR(errorCode); +#endif + receivedBytes = MAX(ret, 0); + } + else + { + fprintf(stderr, "recvfrom(): Proxy client connection client_fd=%d attempted to call recvfrom() on a socket fd=%d that it did not create (or has already shut down)\n", client_fd, d->socket); + ret = errorCode = -1; + receivedBytes = 0; + address_len = 0; + } + + int actualAddressLen = MIN(address_len, (socklen_t)d->address_len); + + struct Result { + int callId; + int/*ssize_t/int*/ ret; + int errno_; + int data_len; + int address_len; // N.B. this is the reported address length of the sender, that may be larger than what is actually serialized to this message. + uint8_t data_and_address[]; + }; + int resultSize = sizeof(Result) + receivedBytes + actualAddressLen; + Result *r = (Result *)malloc(resultSize); + r->callId = d->header.callId; + r->ret = (int)ret; + r->errno_ = errorCode; + r->data_len = receivedBytes; + r->address_len = d->address_len; // How many bytes would have been needed to fit the whole sender address, not the actual size provided + memcpy(r->data_and_address, buffer, receivedBytes); + memcpy(r->data_and_address + receivedBytes, address, actualAddressLen); + SendWebSocketMessage(client_fd, r, resultSize); + free(r); +} + +void Sendmsg(int client_fd, uint8_t *data, uint64_t numBytes) // ssize_t/int sendmsg(int socket, const struct msghdr *message, int flags); +{ + printf("TODO implement sendmsg()\n"); +#ifdef POSIX_SOCKET_DEBUG +// printf("sendmsg(socket=%d,message=%p,flags=%d)\n", d->socket, d->message, d->flags); +#endif + + // TODO +} + +void Recvmsg(int client_fd, uint8_t *data, uint64_t numBytes) // ssize_t/int recvmsg(int socket, struct msghdr *message, int flags); +{ + printf("TODO implement recvmsg()\n"); +#ifdef POSIX_SOCKET_DEBUG +// printf("recvmsg(socket=%d,message=%p,flags=%d)\n", d->socket, d->message, d->flags); +#endif +} + +void Getsockopt(int client_fd, uint8_t *data, uint64_t numBytes) // int getsockopt(int socket, int level, int option_name, void *option_value, socklen_t *option_len); +{ + struct MSG { + SocketCallHeader header; + int socket; + int level; + int option_name; + uint32_t/*socklen_t*/ option_len; + }; + MSG *d = (MSG*)data; + + uint8_t option_value[MAX_OPTIONVALUE_SIZE]; + + d->level = Translate_Socket_Level(d->level); + d->option_name = Translate_SOL_SOCKET_option(d->option_name); + + socklen_t option_len = (socklen_t)d->option_len; + + int ret, errorCode; + + if (IsSocketPartOfConnection(client_fd, d->socket)) + { + ret = getsockopt(d->socket, d->level, d->option_name, (char*)option_value, &option_len); + errorCode = (ret != 0) ? GET_SOCKET_ERROR() : 0; + +#ifdef POSIX_SOCKET_DEBUG + printf("getsockopt(socket=%d,level=%d,option_name=%d,option_value=%p,option_len=%u, optionData=\"%s\")->%d\n", d->socket, d->level, d->option_name, option_value, d->option_len, BufferToString(option_value, option_len), ret); + if (errorCode) PRINT_SOCKET_ERROR(errorCode); +#endif + } + else + { + fprintf(stderr, "getsockopt(): Proxy client connection client_fd=%d attempted to call getsockopt() on a socket fd=%d that it did not create (or has already shut down)\n", client_fd, d->socket); + ret = errorCode = -1; + option_len = 0; + } + + struct Result { + int callId; + int ret; + int errno_; + int option_len; + uint8_t option_value[]; + }; + + int actualOptionLen = MIN(option_len, (socklen_t)d->option_len); + int resultSize = sizeof(Result) + actualOptionLen; + Result *r = (Result*)malloc(resultSize); + r->callId = d->header.callId; + r->ret = ret; + r->errno_ = errorCode; + r->option_len = option_len; + memcpy(r->option_value, option_value, actualOptionLen); + SendWebSocketMessage(client_fd, r, resultSize); + free(r); +} + +void Setsockopt(int client_fd, uint8_t *data, uint64_t numBytes) // int setsockopt(int socket, int level, int option_name, const void *option_value, socklen_t option_len); +{ + struct MSG { + SocketCallHeader header; + int socket; + int level; + int option_name; + int option_len; + uint8_t option_value[]; + }; + MSG *d = (MSG*)data; + int actualOptionLen = MIN(d->option_len, (int)(numBytes - sizeof(MSG))); + + d->level = Translate_Socket_Level(d->level); + switch(d->level) + { + case SOL_SOCKET: d->option_name = Translate_SOL_SOCKET_option(d->option_name); break; + case IPPROTO_TCP: d->option_name = Translate_IPPROTO_TCP_option(d->option_name); break; + default: + fprintf(stderr, "Unknown socket level %d, unable to translate socket option\n", d->level); + break; + } + + int ret, errorCode; + + if (IsSocketPartOfConnection(client_fd, d->socket)) + { + ret = setsockopt(d->socket, d->level, d->option_name, (const char *)d->option_value, actualOptionLen); + errorCode = (ret != 0) ? GET_SOCKET_ERROR() : 0; + +#ifdef POSIX_SOCKET_DEBUG + printf("setsockopt(socket=%d,level=%d,option_name=%d,option_value=%p,option_len=%d, optionData=\"%s\")->%d\n", d->socket, d->level, d->option_name, d->option_value, d->option_len, BufferToString(d->option_value, actualOptionLen), ret); + if (errorCode) PRINT_SOCKET_ERROR(errorCode); +#endif + } + else + { + fprintf(stderr, "setsockopt(): Proxy client connection client_fd=%d attempted to call setsockopt() on a socket fd=%d that it did not create (or has already shut down)\n", client_fd, d->socket); + ret = errorCode = -1; + } + + struct { + int callId; + int ret; + int errno_; + } r; + r.callId = d->header.callId; + r.ret = ret; + r.errno_ = (ret != 0) ? errno : 0; + SendWebSocketMessage(client_fd, &r, sizeof(r)); +} + +void Getaddrinfo(int client_fd, uint8_t *data, uint64_t numBytes) // int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res); +{ +#define MAX_NODE_LEN 2048 +#define MAX_SERVICE_LEN 128 + + struct MSG { + SocketCallHeader header; + char node[MAX_NODE_LEN]; // Arbitrary max length limit + char service[MAX_SERVICE_LEN]; // Arbitrary max length limit + int hasHints; + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + }; + MSG *d = (MSG*)data; + + addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = d->ai_flags; + hints.ai_family = d->ai_family; + hints.ai_socktype = d->ai_socktype; + hints.ai_protocol = d->ai_protocol; + + addrinfo *res = 0; + int ret = getaddrinfo(d->node, d->service, d->hasHints ? &hints : 0, &res); + int errorCode = (ret != 0) ? GET_SOCKET_ERROR() : 0; + +#ifdef POSIX_SOCKET_DEBUG + printf("getaddrinfo(node=%s,service=%s,hasHints=%d,ai_flags=%d,ai_family=%d,ai_socktype=%d,ai_protocol=%d)->%d\n", d->node, d->service, d->hasHints, d->ai_flags, d->ai_family, d->ai_socktype, d->ai_protocol, ret); + if (errorCode) PRINT_SOCKET_ERROR(errorCode); +#endif + + char ai_canonname[MAX_NODE_LEN] = {}; + int ai_addrTotalLen = 0; + int addrCount = 0; + + if (ret == 0) + { + if (res && res->ai_canonname) + { + if (strlen(res->ai_canonname) >= MAX_NODE_LEN) printf("Warning: Truncated res->ai_canonname to %d bytes (was %s)\n", MAX_NODE_LEN, res->ai_canonname); + strncpy(ai_canonname, res->ai_canonname, MAX_NODE_LEN-1); + } + + addrinfo *ai = res; + while(ai) + { + ai_addrTotalLen += ai->ai_addrlen; + ++addrCount; + ai = ai->ai_next; + } + } + + struct ResAddrinfo + { + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + int/*socklen_t*/ ai_addrlen; + uint8_t /*sockaddr **/ ai_addr[]; + }; + + struct Result { + int callId; + int ret; + int errno_; + char ai_canonname[MAX_NODE_LEN]; + int addrCount; + uint8_t /*ResAddrinfo[]*/ addr[]; + }; + + int resultSize = sizeof(Result) + sizeof(ResAddrinfo)*addrCount + ai_addrTotalLen; + Result *r = (Result*)malloc(resultSize); + + memset(r, 0, resultSize); + r->callId = d->header.callId; + r->ret = ret; + r->errno_ = errorCode; + strncpy(r->ai_canonname, ai_canonname, MAX_NODE_LEN-1); + r->addrCount = addrCount; + + addrinfo *ai = res; + int offset = 0; + while(ai) + { + ResAddrinfo *o = (ResAddrinfo*)(r->addr + offset); + o->ai_flags = ai->ai_flags; + o->ai_family = ai->ai_family; + o->ai_socktype = ai->ai_socktype; + o->ai_protocol = ai->ai_protocol; + o->ai_addrlen = ai->ai_addrlen; + memcpy(o->ai_addr, ai->ai_addr, ai->ai_addrlen); + offset += sizeof(ResAddrinfo) + ai->ai_addrlen; + ai = ai->ai_next; + } + if (res) freeaddrinfo(res); + + SendWebSocketMessage(client_fd, r, resultSize); + + free(r); +} + +void Getnameinfo(int client_fd, uint8_t *data, uint64_t numBytes) // int getnameinfo(const struct sockaddr *addr, socklen_t addrlen, char *host, socklen_t hostlen, char *serv, socklen_t servlen, int flags); +{ + fprintf(stderr, "TODO getnameinfo() unimplemented!\n"); +} + +static void *memdup(const void *ptr, size_t sz) +{ + if (!ptr) return 0; + void *dup = malloc(sz); + if (dup) memcpy(dup, ptr, sz); + return dup; +} + +struct MessageArg +{ + int client_fd; + uint8_t *payload; + uint64_t numBytes; +}; + +void ProcessWebSocketMessageSynchronouslyInCurrentThread(int client_fd, uint8_t *payload, uint64_t numBytes); + +THREAD_RETURN_T message_processing_thread(void *arg) +{ + MessageArg *msg = (MessageArg*)arg; + assert(msg); + assert(msg->client_fd); + ProcessWebSocketMessageSynchronouslyInCurrentThread(msg->client_fd, msg->payload, msg->numBytes); + free(msg->payload); + free(msg); + EXIT_THREAD(0); +} + +// Offloads the processing of the given message to a background thread. +void ProcessWebSocketMessageAsynchronouslyInBackgroundThread(int client_fd, uint8_t *payload, uint64_t numBytes) +{ + MessageArg *arg = (MessageArg*)malloc(sizeof(MessageArg)); + arg->client_fd = client_fd; + arg->payload = (uint8_t*)memdup(payload, (size_t)numBytes); + arg->numBytes = numBytes; + THREAD_T thread; + // TODO: Instead of unconditionally always creating a thread here, create a thread pool and push messages to it. + // (leaving this as a future optimization because not sure if it matters here much at all for performance) + CREATE_THREAD(thread, message_processing_thread, arg); +} + +void ProcessWebSocketMessageSynchronouslyInCurrentThread(int client_fd, uint8_t *payload, uint64_t numBytes) +{ + assert(numBytes >= sizeof(SocketCallHeader)); // Already validated in ProcessWebSocketMessage() before coming here, so we should be good. + SocketCallHeader *header = (SocketCallHeader*)payload; + switch(header->function) + { + case POSIX_SOCKET_MSG_SOCKET: Socket(client_fd, payload, numBytes); break; + case POSIX_SOCKET_MSG_SOCKETPAIR: Socketpair(client_fd, payload, numBytes); break; + case POSIX_SOCKET_MSG_SHUTDOWN: Shutdown(client_fd, payload, numBytes); break; + case POSIX_SOCKET_MSG_BIND: Bind(client_fd, payload, numBytes); break; + case POSIX_SOCKET_MSG_CONNECT: Connect(client_fd, payload, numBytes); break; + case POSIX_SOCKET_MSG_LISTEN: Listen(client_fd, payload, numBytes); break; + case POSIX_SOCKET_MSG_ACCEPT: Accept(client_fd, payload, numBytes); break; + case POSIX_SOCKET_MSG_GETSOCKNAME: Getsockname(client_fd, payload, numBytes); break; + case POSIX_SOCKET_MSG_GETPEERNAME: Getpeername(client_fd, payload, numBytes); break; + case POSIX_SOCKET_MSG_SEND: Send(client_fd, payload, numBytes); break; + case POSIX_SOCKET_MSG_RECV: Recv(client_fd, payload, numBytes); break; + case POSIX_SOCKET_MSG_SENDTO: Sendto(client_fd, payload, numBytes); break; + case POSIX_SOCKET_MSG_RECVFROM: Recvfrom(client_fd, payload, numBytes); break; + case POSIX_SOCKET_MSG_SENDMSG: Sendmsg(client_fd, payload, numBytes); break; + case POSIX_SOCKET_MSG_RECVMSG: Recvmsg(client_fd, payload, numBytes); break; + case POSIX_SOCKET_MSG_GETSOCKOPT: Getsockopt(client_fd, payload, numBytes); break; + case POSIX_SOCKET_MSG_SETSOCKOPT: Setsockopt(client_fd, payload, numBytes); break; + case POSIX_SOCKET_MSG_GETADDRINFO: Getaddrinfo(client_fd, payload, numBytes); break; + case POSIX_SOCKET_MSG_GETNAMEINFO: Getnameinfo(client_fd, payload, numBytes); break; + default: + printf("Unknown POSIX_SOCKET_MSG %u received!\n", header->function); + break; + } +} + +void ProcessWebSocketMessage(int client_fd, uint8_t *payload, uint64_t numBytes) +{ + if (numBytes < sizeof(SocketCallHeader)) + { + printf("Received too small sockets call message! size: %d bytes, expected at least %d bytes\n", (int)numBytes, (int)sizeof(SocketCallHeader)); + return; + } + SocketCallHeader *header = (SocketCallHeader*)payload; + if (header->function == POSIX_SOCKET_MSG_RECV || header->function == POSIX_SOCKET_MSG_RECVFROM || header->function == POSIX_SOCKET_MSG_RECVMSG || header->function == POSIX_SOCKET_MSG_CONNECT || header->function == POSIX_SOCKET_MSG_LISTEN) + { + // Synchonous/blocking recv()s can halt indefinitely until a message is actually received. An application might + // be send()ing messages in one thread while using another thread to wait for recv(). Therefore run these potentially + // blocking recv()s in a separate thread. The nonblocking operations can run synchronously in calling thread (they could + // also run in a background thread, but for performance, do not offload them since it is not necessary) + ProcessWebSocketMessageAsynchronouslyInBackgroundThread(client_fd, payload, numBytes); + } + else + { + ProcessWebSocketMessageSynchronouslyInCurrentThread(client_fd, payload, numBytes); + } +} diff --git a/tools/websocket_to_posix_proxy/src/websocket_to_posix_proxy.h b/tools/websocket_to_posix_proxy/src/websocket_to_posix_proxy.h new file mode 100644 index 0000000000000..85b3bc34ff2bf --- /dev/null +++ b/tools/websocket_to_posix_proxy/src/websocket_to_posix_proxy.h @@ -0,0 +1,31 @@ +#pragma once + +#include + +uint64_t ntoh64(uint64_t x); +#define hton64 ntoh64 + +void WebSocketMessageUnmaskPayload(uint8_t *payload, uint64_t payloadLength, uint32_t maskingKey); +void ProcessWebSocketMessage(int client_fd, uint8_t *payload, uint64_t numBytes); + +#ifdef _MSC_VER +#pragma pack(push,1) +#endif + +struct +#if defined(__GNUC__) +__attribute__ ((packed, aligned(1))) +#endif + +WebSocketMessageHeader +{ + unsigned opcode : 4; + unsigned rsv : 3; + unsigned fin : 1; + unsigned payloadLength : 7; + unsigned mask : 1; +}; + +#ifdef _MSC_VER +__pragma(pack(pop)) +#endif