| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #include <assert.h> |
| #include <errno.h> |
| #include <limits.h> |
| #include <netdb.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <pthread.h> |
| #include <sys/socket.h> |
|
|
| #include <emscripten/console.h> |
| #include <emscripten/threading.h> |
| #include <emscripten/websocket.h> |
|
|
| |
| |
|
|
| |
| |
|
|
| #ifdef POSIX_SOCKET_DEBUG |
| #define DBG_OUT(format, ...) emscripten_errf("[proxy] " format, ##__VA_ARGS__) |
| #else |
| #define DBG_OUT(...) do {} while(0) |
| #endif |
|
|
| #ifdef POSIX_SOCKET_DEEP_DEBUG |
| #define DEEP_DBG_OUT(format, ...) emscripten_errf("[proxy] " format, ##__VA_ARGS__) |
| #else |
| #define DEEP_DBG_OUT(...) do {} while(0) |
| #endif |
|
|
| #define MIN(a,b) (((a)<(b))?(a):(b)) |
|
|
| static void *memdup(const void *ptr, size_t sz) { |
| assert(ptr); |
| void *dup = malloc(sz); |
| assert(dup); |
| memcpy(dup, ptr, sz); |
| return dup; |
| } |
|
|
| |
| typedef struct SocketCallHeader { |
| int callId; |
| int function; |
| } SocketCallHeader; |
|
|
| |
| typedef struct SocketCallResultHeader { |
| int callId; |
| int ret; |
| int errno_; |
| |
| |
| } SocketCallResultHeader; |
|
|
| typedef struct PosixSocketCallResult { |
| struct PosixSocketCallResult *next; |
| int callId; |
| _Atomic uint32_t operationCompleted; |
|
|
| |
| |
| |
| |
| size_t bytes; |
|
|
| |
| SocketCallResultHeader *data; |
| } PosixSocketCallResult; |
|
|
| |
| |
| static pthread_mutex_t bridgeLock = PTHREAD_MUTEX_INITIALIZER; |
|
|
| |
| |
| static EMSCRIPTEN_WEBSOCKET_T bridgeSocket = (EMSCRIPTEN_WEBSOCKET_T)0; |
|
|
| |
| |
| static PosixSocketCallResult *callResultHead = NULL; |
|
|
| static PosixSocketCallResult *allocate_call_result(size_t expectedBytes) { |
| PosixSocketCallResult *b = (PosixSocketCallResult*)malloc(sizeof(PosixSocketCallResult)); |
| assert(b); |
| pthread_mutex_lock(&bridgeLock); |
| static int nextId = 1; |
| b->callId = nextId++; |
| DEEP_DBG_OUT("allocate_call_result: allocated call ID %d", b->callId); |
| b->bytes = expectedBytes; |
| b->data = NULL; |
| b->operationCompleted = 0; |
| b->next = NULL; |
|
|
| 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) { |
| if (buffer) { |
| DEEP_DBG_OUT("free_call_result: freed call ID %d", buffer->callId); |
| } |
|
|
| if (buffer->data) { |
| free(buffer->data); |
| } |
| free(buffer); |
| } |
|
|
| static PosixSocketCallResult *pop_call_result(int callId) { |
| pthread_mutex_lock(&bridgeLock); |
| PosixSocketCallResult *prev = NULL; |
| PosixSocketCallResult *b = callResultHead; |
| while (b) { |
| if (b->callId == callId) { |
| if (prev) { |
| prev->next = b->next; |
| } else { |
| callResultHead = b->next; |
| } |
| b->next = NULL; |
| DEEP_DBG_OUT("pop_call_result: Removed call ID %d from pending sockets call queue", callId); |
| pthread_mutex_unlock(&bridgeLock); |
| return b; |
| } |
| prev = b; |
| b = b->next; |
| } |
| pthread_mutex_unlock(&bridgeLock); |
| DBG_OUT("pop_call_result: No such call ID %d in pending sockets call queue!", callId); |
| return 0; |
| } |
|
|
| static void wait_for_call_result(PosixSocketCallResult *b) { |
| DEEP_DBG_OUT("wait_for_call_result: Waiting for call ID %d", b->callId); |
| while (!b->operationCompleted) { |
| emscripten_futex_wait(&b->operationCompleted, 0, 1e9); |
| } |
| DEEP_DBG_OUT("wait_for_call_result: Waiting for call ID %d done", b->callId); |
| } |
|
|
| static bool |
| bridge_socket_on_message(int eventType, |
| const EmscriptenWebSocketMessageEvent* websocketEvent, |
| void* userData) { |
| if (websocketEvent->numBytes < sizeof(SocketCallResultHeader)) { |
| emscripten_errf("Received corrupt WebSocket result message with size %d, not enough space for header, at least %zu bytes!", websocketEvent->numBytes, sizeof(SocketCallResultHeader)); |
| return true; |
| } |
|
|
| SocketCallResultHeader *header = (SocketCallResultHeader *)websocketEvent->data; |
|
|
| DEEP_DBG_OUT("POSIX sockets bridge received message on thread %p, size: %d bytes, for call ID %d", (void*)pthread_self(), websocketEvent->numBytes, header->callId); |
|
|
| PosixSocketCallResult *b = pop_call_result(header->callId); |
| if (!b) { |
| emscripten_errf("Received WebSocket result message to unknown call ID %d!", header->callId); |
| |
| return true; |
| } |
|
|
| if (websocketEvent->numBytes < b->bytes) { |
| emscripten_errf("Received corrupt WebSocket result message with size %d, expected at least %zu bytes!", websocketEvent->numBytes, b->bytes); |
| |
| return true; |
| } |
|
|
| b->bytes = websocketEvent->numBytes; |
| b->data = (SocketCallResultHeader*)memdup(websocketEvent->data, websocketEvent->numBytes); |
|
|
| if (b->operationCompleted != 0) { |
| emscripten_errf("Memory corruption(?): the received result for completed operation at address %p was expected to be in state 0, but it was at state %d!", &b->operationCompleted, b->operationCompleted); |
| } |
|
|
| b->operationCompleted = 1; |
| emscripten_futex_wake(&b->operationCompleted, INT_MAX); |
|
|
| return true; |
| } |
|
|
| EMSCRIPTEN_WEBSOCKET_T emscripten_init_websocket_to_posix_socket_bridge(const char *bridgeUrl) { |
| DBG_OUT("emscripten_init_websocket_to_posix_socket_bridge(bridgeUrl='%s')", bridgeUrl); |
| pthread_mutex_lock(&bridgeLock); |
| if (bridgeSocket) { |
| DBG_OUT("emscripten_init_websocket_to_posix_socket_bridge(bridgeUrl='%s'): A previous bridge socket connection handle existed! Forcibly tearing old connection down", bridgeUrl); |
| 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_RUNTIME_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) { |
| DBG_OUT("socket(domain=%d,type=%d,protocol=%d) on thread %p", domain, type, protocol, (void*)pthread_self()); |
|
|
| 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]) { |
| DBG_OUT("socketpair(domain=%d,type=%d,protocol=%d, socket_vector=[%d,%d])", domain, type, protocol, socket_vector[0], socket_vector[1]); |
|
|
| struct { |
| SocketCallHeader header; |
| int domain; |
| int type; |
| int protocol; |
| } d; |
|
|
| typedef struct Result { |
| SocketCallResultHeader header; |
| int sv[2]; |
| } Result; |
|
|
| 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) { |
| DBG_OUT("shutdown(socket=%d,how=%d)", socket, how); |
|
|
| 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) { |
| DBG_OUT("bind(socket=%d,address=%p,address_len=%d)", socket, address, address_len); |
|
|
| typedef struct Data { |
| SocketCallHeader header; |
| int socket; |
| uint32_t address_len; |
| uint8_t address[]; |
| } Data; |
| 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) { |
| DBG_OUT("connect(socket=%d,address=%p,address_len=%d)", socket, address, address_len); |
|
|
| typedef struct Data { |
| SocketCallHeader header; |
| int socket; |
| uint32_t address_len; |
| uint8_t address[]; |
| } Data; |
| 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) { |
| DBG_OUT("listen(socket=%d,backlog=%d)", socket, backlog); |
|
|
| 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 accept4(int socket, struct sockaddr *address, socklen_t *address_len, int flags) { |
| if (flags) { |
| abort(); |
| } |
| return accept(socket, address, address_len); |
| } |
|
|
| int accept(int socket, struct sockaddr *address, socklen_t *address_len) { |
| DBG_OUT("accept(socket=%d,address=%p,address_len=%p)", socket, address, address_len); |
|
|
| struct { |
| SocketCallHeader header; |
| int socket; |
| uint32_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)); |
|
|
| typedef struct Result { |
| SocketCallResultHeader header; |
| int address_len; |
| uint8_t address[]; |
| } Result; |
|
|
| 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) { |
| DBG_OUT("getsockname(socket=%d,address=%p,address_len=%p)", socket, address, address_len); |
|
|
| struct { |
| SocketCallHeader header; |
| int socket; |
| uint32_t address_len; |
| } d; |
|
|
| typedef struct Result { |
| SocketCallResultHeader header; |
| int address_len; |
| uint8_t address[]; |
| } Result; |
|
|
| 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) { |
| DBG_OUT("getpeername(socket=%d,address=%p,address_len=%p)", socket, address, address_len); |
|
|
| struct { |
| SocketCallHeader header; |
| int socket; |
| uint32_t address_len; |
| } d; |
|
|
| typedef struct Result { |
| SocketCallResultHeader header; |
| int address_len; |
| uint8_t address[]; |
| } Result; |
|
|
| 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) { |
| DBG_OUT("send(socket=%d,message=%p,length=%zd,flags=%d)", socket, message, length, flags); |
|
|
| typedef struct MSG { |
| SocketCallHeader header; |
| int socket; |
| uint32_t length; |
| int flags; |
| uint8_t message[]; |
| } MSG; |
| 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) { |
| DBG_OUT("recv(socket=%d,buffer=%p,length=%zd,flags=%d)", socket, buffer, length, flags); |
|
|
| struct { |
| SocketCallHeader header; |
| int socket; |
| uint32_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) { |
| typedef struct Result { |
| SocketCallResultHeader header; |
| uint8_t data[]; |
| } Result; |
| 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) { |
| DBG_OUT("sendto(socket=%d,message=%p,length=%zd,flags=%d,dest_addr=%p,dest_len=%d)", socket, message, length, flags, dest_addr, dest_len); |
|
|
| typedef struct MSG { |
| SocketCallHeader header; |
| int socket; |
| uint32_t length; |
| int flags; |
| uint32_t dest_len; |
| uint8_t dest_addr[MAX_SOCKADDR_SIZE]; |
| uint8_t message[]; |
| } MSG; |
| 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) { |
| DBG_OUT("recvfrom(socket=%d,buffer=%p,length=%zd,flags=%d,address=%p,address_len=%p)", socket, buffer, length, flags, address, address_len); |
|
|
| struct { |
| SocketCallHeader header; |
| int socket; |
| uint32_t length; |
| int flags; |
| uint32_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) { |
| typedef struct Result { |
| SocketCallResultHeader header; |
| int data_len; |
| int address_len; |
| uint8_t data_and_address[]; |
| } Result; |
| 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) { |
| DBG_OUT("sendmsg(socket=%d,message=%p,flags=%d)", socket, message, flags); |
|
|
| abort(); |
| return 0; |
| } |
|
|
| ssize_t recvmsg(int socket, struct msghdr *message, int flags) { |
| DBG_OUT("recvmsg(socket=%d,message=%p,flags=%d)", socket, message, flags); |
|
|
| abort(); |
| return 0; |
| } |
|
|
| int getsockopt(int socket, |
| int level, |
| int option_name, |
| void* option_value, |
| socklen_t* option_len) { |
| DBG_OUT("getsockopt(socket=%d,level=%d,option_name=%d,option_value=%p,option_len=%p)", socket, level, option_name, option_value, option_len); |
|
|
| struct { |
| SocketCallHeader header; |
| int socket; |
| int level; |
| int option_name; |
| uint32_t option_len; |
| } d; |
|
|
| typedef struct Result { |
| SocketCallResultHeader header; |
| uint8_t option_value[]; |
| } Result; |
|
|
| 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) { |
| DBG_OUT("setsockopt(socket=%d,level=%d,option_name=%d,option_value=%p,option_len=%d)", socket, level, option_name, option_value, option_len); |
|
|
| typedef struct MSG { |
| SocketCallHeader header; |
| int socket; |
| int level; |
| int option_name; |
| int option_len; |
| uint8_t option_value[]; |
| } MSG; |
| 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; |
| } |
|
|
| |
|
|
| 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]; |
| char service[MAX_SERVICE_LEN]; |
| int hasHints; |
| int ai_flags; |
| int ai_family; |
| int ai_socktype; |
| int ai_protocol; |
| } d; |
|
|
| typedef struct ResAddrinfo { |
| int ai_flags; |
| int ai_family; |
| int ai_socktype; |
| int ai_protocol; |
| int ai_addrlen; |
| uint8_t ai_addr[]; |
| } ResAddrinfo; |
|
|
| typedef struct Result { |
| SocketCallResultHeader header; |
| char ai_canonname[MAX_NODE_LEN]; |
| int addrCount; |
| uint8_t addr[]; |
| } Result; |
|
|
| 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; |
| } |
|
|
| DBG_OUT("getaddrinfo(node=%s,service=%s,hasHints=%d,ai_flags=%d,ai_family=%d,ai_socktype=%d,ai_protocol=%d,hintsPtr=%p,resPtr=%p)", node, service, d.hasHints, d.ai_flags, d.ai_family, d.ai_socktype, d.ai_protocol, hints, res); |
|
|
| emscripten_websocket_send_binary(bridgeSocket, &d, sizeof(d)); |
|
|
| wait_for_call_result(b); |
| int ret = b->data->ret; |
| DBG_OUT("getaddrinfo finished, ret=%d", ret); |
| if (ret == 0) { |
| if (res) { |
| Result *r = (Result*)b->data; |
| uint8_t *raiAddr = (uint8_t*)&r->addr[0]; |
| struct addrinfo *results = (struct addrinfo*)malloc(sizeof(struct addrinfo)*r->addrCount); |
| DBG_OUT("%d results", 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 = (struct 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 = NULL; |
| } |
| } |
| free_call_result(b); |
|
|
| return ret; |
| } |
|
|
| void freeaddrinfo(struct addrinfo *res) { |
| for (struct 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) { |
| |
| |
| return -1; |
| } |
|
|
| |
| |
|
|