flst / upstream /emscripten /system /lib /websocket /websocket_to_posix_socket.c
arudradey's picture
Upload folder using huggingface_hub
00df61d verified
Raw
History Blame Contribute Delete
28.1 kB
/*
* Copyright 2019 The Emscripten Authors. All rights reserved.
* Emscripten is available under two separate licenses, the MIT license and the
* University of Illinois/NCSA Open Source License. Both these licenses can be
* found in the LICENSE file.
*
* Alternative socket system implementation that gets compiled to
* libsockets_proxy.a and included when the `-sPROXY_POSIX_SOCKETS`
* is used.
*/
#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>
// 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
#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;
}
// Each proxied socket call has at least the following data.
typedef struct SocketCallHeader {
int callId;
int function;
} SocketCallHeader;
// Each socket call returns at least the following data.
typedef struct SocketCallResultHeader {
int callId;
int ret;
int errno_;
// Buffer can contain more data here, conceptually:
// uint8_t extraData[];
} SocketCallResultHeader;
typedef struct PosixSocketCallResult {
struct PosixSocketCallResult *next;
int callId;
_Atomic uint32_t 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.
size_t bytes;
// Result data:
SocketCallResultHeader *data;
} PosixSocketCallResult;
// 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 = NULL;
static PosixSocketCallResult *allocate_call_result(size_t expectedBytes) {
PosixSocketCallResult *b = (PosixSocketCallResult*)malloc(sizeof(PosixSocketCallResult));
assert(b);
pthread_mutex_lock(&bridgeLock); // Guard multithreaded access to 'callResultHead' and 'nextId' below
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); // Guard multithreaded access to 'callResultHead'
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);
// TODO: Craft a socket result that signifies a failure, and wake the listening thread
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);
// TODO: Craft a socket result that signifies a failure, and wake the listening thread
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); // Guard multithreaded access to 'bridgeSocket'
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/*socklen_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/*socklen_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(); // TODO
}
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/*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));
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/*socklen_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/*socklen_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/*size_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/*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) {
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/*size_t*/ length;
int flags;
uint32_t/*socklen_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/*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) {
typedef 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;
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(); // TODO
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(); // TODO
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/*socklen_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;
}
// Host name resolution: <netdb.h>
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;
typedef struct ResAddrinfo {
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
int/*socklen_t*/ ai_addrlen;
uint8_t /*sockaddr **/ ai_addr[];
} ResAddrinfo;
typedef struct Result {
SocketCallResultHeader header;
char ai_canonname[MAX_NODE_LEN];
int addrCount;
uint8_t /*ResAddrinfo[]*/ 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) {
// TODO
// POSIX_SOCKET_MSG_GETNAMEINFO
return -1;
}
// TODO:
// const char *gai_strerror(int);