From dddcda8e8eeb878390cda442ab79825787fb663e Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Mon, 17 Jan 2022 15:10:33 -0600 Subject: [PATCH] added basic networking for Linux --- core/net/net.onyx | 171 ++++++++++++++++++++++++++++ core/std.onyx | 3 +- modules/onyx_runtime/onyx_runtime.c | 157 +++++++++++++++++++++++++ 3 files changed, 330 insertions(+), 1 deletion(-) create mode 100644 core/net/net.onyx diff --git a/core/net/net.onyx b/core/net/net.onyx new file mode 100644 index 00000000..a407f65c --- /dev/null +++ b/core/net/net.onyx @@ -0,0 +1,171 @@ +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; diff --git a/core/std.onyx b/core/std.onyx index 5861d6c9..bfd4775f 100644 --- a/core/std.onyx +++ b/core/std.onyx @@ -50,6 +50,7 @@ package core #load "./runtime/onyx_run" #load "./os/process" #load "./os/onyx_fs" + #load "./net/net" } #if runtime.runtime == .Wasi { #load "./wasi/wasi" @@ -70,4 +71,4 @@ package core #load "./sync/barrier" #load "./threads/thread" -} \ No newline at end of file +} diff --git a/modules/onyx_runtime/onyx_runtime.c b/modules/onyx_runtime/onyx_runtime.c index 4ea91594..5291f8bd 100644 --- a/modules/onyx_runtime/onyx_runtime.c +++ b/modules/onyx_runtime/onyx_runtime.c @@ -13,6 +13,8 @@ #include #include #include + #include + #include #endif #include "types.h" // For POINTER_SIZE @@ -777,6 +779,151 @@ ONYX_DEF(__exit, (WASM_I32), ()) { 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) @@ -807,5 +954,15 @@ ONYX_LIBRARY { 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 }; -- 2.25.1