added basic networking for Linux
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 17 Jan 2022 21:10:33 +0000 (15:10 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 17 Jan 2022 21:10:33 +0000 (15:10 -0600)
core/net/net.onyx [new file with mode: 0644]
core/std.onyx
modules/onyx_runtime/onyx_runtime.c

diff --git a/core/net/net.onyx b/core/net/net.onyx
new file mode 100644 (file)
index 0000000..a407f65
--- /dev/null
@@ -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;
index 5861d6c9595afbee5b1fc456290d05d3fde1e396..bfd4775f6bf443627780b29ecb4b9e1d9863cb94 100644 (file)
@@ -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
+}
index 4ea915949dd8b5d6f9dffd54c0bf24e886ab432e..5291f8bd93fcc333e898f0361188b5e80635f673 100644 (file)
@@ -13,6 +13,8 @@
     #include <sys/types.h>
     #include <dlfcn.h>
     #include <dirent.h>
+    #include <netdb.h>
+    #include <netinet/in.h>
 #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
 };