--- /dev/null
+package core.net
+
+use package core
+
+/*
+ Desired API for working with networking.
+
+ // Client
+ s, err := net.socket_create(.Inet, .Stream);
+ if err != .None {
+ // Error handling
+ }
+ s->connect("10.0.0.1", 9001);
+
+ net.socket_send(s, "message");
+ msg := net.socket_recv(s, allocator=context.allocator); // Allocates the result
+
+ // with aliases for "methods"
+
+ s->send("message");
+ msg := s->recv(1024);
+
+
+
+ // Server (single client)
+ s, err := net.socket_create(.Inet, .Stream);
+ if err != .None { / ... / }
+
+ s->bind(9001);
+ if !s->listen() { // error }
+
+ client, addr := s->accept();
+ while true {
+ msg := s->recv(1024);
+ if msg.count > 0 {
+ s->send(msg);
+ }
+ }
+*/
+
+Socket :: struct {
+ Handle :: #distinct i32
+
+ use stream : io.Stream;
+ handle: Handle;
+
+ close :: socket_close
+ bind :: socket_bind
+ listen :: socket_listen
+ accept :: socket_accept
+ connect :: socket_connect
+ send :: socket_send
+ sendall :: socket_sendall
+ recv :: socket_recv
+}
+
+#foreign "onyx_runtime" {
+ __net_create_socket :: (out_handle: ^Socket.Handle, domain: SocketDomain, type: SocketType) -> SocketError ---
+ __net_close_socket :: (handle: Socket.Handle) -> void ---
+ __net_bind :: (handle: Socket.Handle, port: u16) -> bool ---
+ __net_listen :: (handle: Socket.Handle, backlog: i32) -> void ---
+ __net_accept :: (handle: Socket.Handle) -> Socket.Handle --- // This should also return the address, but in what format?
+ __net_connect :: (handle: Socket.Handle, host: str, port: u16) -> SocketError ---
+ __net_send :: (handle: Socket.Handle, data: [] u8) -> u32 ---
+ __net_recv :: (handle: Socket.Handle, data: [] u8) -> u32 ---
+}
+
+#local {
+ SocketError :: enum {
+ None :: 0x00;
+ BadSettings :: 0x01;
+ NoHost :: 0x02;
+ ConnectFailed :: 0x03;
+ }
+
+ SocketDomain :: enum {
+ Unix :: 0x00;
+ Inet :: 0x01;
+ Inet6 :: 0x02;
+ }
+
+ SocketType :: enum {
+ Stream :: 0x00;
+ Dgram :: 0x01;
+ }
+}
+
+socket_create :: (domain: SocketDomain, type: SocketType) -> (Socket, SocketError) {
+ s: Socket;
+
+ err := __net_create_socket(^s.handle, domain, type);
+ if err == .None {
+ s.vtable = ^__net_socket_vtable;
+ }
+
+ return s, err;
+}
+
+socket_close :: (s: ^Socket) {
+ __net_close_socket(s.handle);
+ s.vtable = null;
+}
+
+socket_connect :: (s: ^Socket, host: str, port: u16) -> SocketError {
+ return __net_connect(s.handle, host, port);
+}
+
+socket_bind :: (s: ^Socket, port: u16) -> bool {
+ return __net_bind(s.handle, port);
+}
+
+socket_listen :: (s: ^Socket, backlog := 32) {
+ __net_listen(s.handle, backlog);
+}
+
+socket_accept :: (s: ^Socket) -> Socket {
+ new_socket: Socket;
+ new_socket.handle = __net_accept(s.handle);
+ if new_socket.handle >= 0 {
+ new_socket.vtable = ^__net_socket_vtable;
+ }
+
+ return new_socket;
+}
+
+socket_send :: (s: ^Socket, data: [] u8) -> u32 {
+ sent := __net_send(s.handle, data);
+ return sent;
+}
+
+socket_sendall :: (s: ^Socket, data: [] u8) {
+}
+
+socket_recv :: (s: ^Socket, maxlen := 1024, allocator := context.allocator) -> [] u8 {
+ buffer := alloc.from_stack(maxlen);
+ received := __net_recv(s.handle, .{ buffer, maxlen });
+
+ result := memory.make_slice(u8, received, allocator=allocator);
+ memory.copy(result.data, buffer, received);
+
+ return result;
+}
+
+socket_recv_into :: (s: ^Socket, buffer: [] u8) -> u32 {
+ return __net_recv(s.handle, buffer);
+}
+
+#local __net_socket_vtable := io.Stream_Vtable.{
+ read = (use s: ^Socket, buffer: [] u8) -> (io.Error, u32) {
+ if handle == 0 do return .BadFile, 0;
+
+ bytes_read := __net_recv(handle, buffer);
+ return .None, bytes_read;
+ },
+
+ write = (use p: ^Socket, buffer: [] u8) -> (io.Error, u32) {
+ if handle == 0 do return .BadFile, 0;
+
+ bytes_written := __net_send(handle, buffer);
+ return .None, bytes_written;
+ },
+
+ close = (use p: ^Socket) -> io.Error {
+ __net_close_socket(handle);
+ return .None;
+ }
+};
+
+
+#operator >= macro (a, b: Socket.Handle) => cast(u32) a >= cast(u32) b;
+#operator == macro (a, b: Socket.Handle) => cast(u32) a == cast(u32) b;
#include <sys/types.h>
#include <dlfcn.h>
#include <dirent.h>
+ #include <netdb.h>
+ #include <netinet/in.h>
#endif
#include "types.h" // For POINTER_SIZE
return NULL;
}
+
+
+//
+// Networking
+//
+ONYX_DEF(__net_create_socket, (WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
+
+ #ifdef _BH_LINUX
+ int domain = 0;
+ switch (params->data[1].of.i32) { // :EnumDependent
+ case 0: domain = AF_UNIX; break;
+ case 1: domain = AF_INET; break;
+ case 2: domain = AF_INET6; break;
+ default: goto bad_settings;
+ }
+
+ int type = 0;
+ switch (params->data[2].of.i32) { // :EnumDependent
+ case 0: type = SOCK_STREAM; break;
+ case 1: type = SOCK_DGRAM; break;
+ default: goto bad_settings;
+ }
+
+ *((int *) ONYX_PTR(params->data[0].of.i32)) = socket(domain, type, 0);
+
+ results->data[0] = WASM_I32_VAL(0);
+ return NULL;
+ #endif
+
+ #ifdef _BH_WINDOWS
+ #endif
+
+bad_settings:
+ results->data[0] = WASM_I32_VAL(1); // :EnumDependent
+ return NULL;
+}
+
+ONYX_DEF(__net_close_socket, (WASM_I32), ()) {
+ #ifdef _BH_LINUX
+ close(params->data[0].of.i32);
+ #endif
+
+ #ifdef _BH_WINDOWS
+ #endif
+
+ return NULL;
+}
+
+ONYX_DEF(__net_bind, (WASM_I32, WASM_I32), (WASM_I32)) {
+
+ #ifdef _BH_LINUX
+ struct sockaddr_in bind_addr;
+ memset(&bind_addr, 0, sizeof(bind_addr));
+
+ bind_addr.sin_family = AF_INET; // Should this be configurable? Or is binding only ever done for INET? INET6?
+ bind_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ bind_addr.sin_port = htons(params->data[1].of.i32);
+
+ int res = bind(params->data[0].of.i32, &bind_addr, sizeof(bind_addr));
+ results->data[0] = WASM_I32_VAL(res >= 0);
+ #endif
+
+ #ifdef _BH_WINDOWS
+ #endif
+
+ return NULL;
+}
+
+ONYX_DEF(__net_listen, (WASM_I32, WASM_I32), ()) {
+ #ifdef _BH_LINUX
+ listen(params->data[0].of.i32, params->data[1].of.i32);
+ #endif
+
+ #ifdef _BH_WINDOWS
+ #endif
+
+ return NULL;
+}
+
+ONYX_DEF(__net_accept, (WASM_I32), (WASM_I32)) {
+ #ifdef _BH_LINUX
+ struct sockaddr_in client_addr;
+ int client_len = sizeof(client_addr);
+
+ int client_socket = accept(params->data[0].of.i32, &client_addr, &client_len);
+
+ results->data[0] = WASM_I32_VAL(client_socket);
+ #endif
+
+ return NULL;
+}
+
+ONYX_DEF(__net_connect, (WASM_I32, WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
+ #ifdef _BH_LINUX
+ int hostlen = params->data[2].of.i32;
+ char *hostname = alloca(hostlen + 1);
+ memcpy(hostname, ONYX_PTR(params->data[1].of.i32), hostlen);
+ hostname[hostlen] = '\0';
+
+ struct hostent *host;
+ host = gethostbyname(hostname);
+ if (host == NULL) {
+ results->data[0] = WASM_I32_VAL(2); // :EnumDependent
+ return NULL;
+ }
+
+ struct sockaddr_in server_addr;
+ memset(&server_addr, 0, sizeof(server_addr));
+
+ server_addr.sin_family = AF_INET; // See comment above
+ memcpy((char *)&server_addr.sin_addr.s_addr, (char *)host->h_addr, host->h_length);
+ server_addr.sin_port = htons(params->data[3].of.i32);
+
+ int result = connect(params->data[0].of.i32, &server_addr, sizeof(server_addr));
+ if (result == 0) results->data[0] = WASM_I32_VAL(0);
+ else results->data[0] = WASM_I32_VAL(3); // :EnumDependent
+
+ return NULL;
+ #endif
+
+ #ifdef _BH_WINDOWS
+ #endif
+}
+
+ONYX_DEF(__net_send, (WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
+ #ifdef _BH_LINUX
+ // TODO: The flags at the end should be controllable.
+ int sent = send(params->data[0].of.i32, ONYX_PTR(params->data[1].of.i32), params->data[2].of.i32, 0);
+ results->data[0] = WASM_I32_VAL(sent);
+ #endif
+
+ return NULL;
+}
+
+ONYX_DEF(__net_recv, (WASM_I32, WASM_I32, WASM_I32), (WASM_I32)) {
+ #ifdef _BH_LINUX
+ // TODO: The flags at the end should be controllable.
+ int received = recv(params->data[0].of.i32, ONYX_PTR(params->data[1].of.i32), params->data[2].of.i32, 0);
+ results->data[0] = WASM_I32_VAL(received);
+ #endif
+
+ return NULL;
+}
+
+
ONYX_LIBRARY {
ONYX_FUNC(__file_open_impl)
ONYX_FUNC(__file_close)
ONYX_FUNC(__args_sizes_get)
ONYX_FUNC(__exit)
+
+ ONYX_FUNC(__net_create_socket)
+ ONYX_FUNC(__net_close_socket)
+ ONYX_FUNC(__net_bind)
+ ONYX_FUNC(__net_listen)
+ ONYX_FUNC(__net_accept)
+ ONYX_FUNC(__net_connect)
+ ONYX_FUNC(__net_send)
+ ONYX_FUNC(__net_recv)
+
NULL
};