From c58e57b7c763877262882387282dba8de5b91608 Mon Sep 17 00:00:00 2001 From: IonutParau Date: Sun, 24 May 2026 18:40:53 +0200 Subject: [PATCH] internet card progress --- src/main.c | 22 ++++ src/ncomplib.c | 3 +- src/neonucleus.c | 270 +++++++++++++++++++++++++++++++++++++++++++++++ src/neonucleus.h | 61 +++++++++-- 4 files changed, 349 insertions(+), 7 deletions(-) diff --git a/src/main.c b/src/main.c index b31a56f..72767ec 100644 --- a/src/main.c +++ b/src/main.c @@ -556,6 +556,25 @@ void ne_env(nn_EnvironmentRequest *req) { } } +nn_Exit ne_inetBullshit(nn_InternetRequest *req) { + // welcome to hell + nn_Computer *C = req->computer; + + // TODO: make this shi connect to the real internet + // preferrably use libcurl, openssl and a basic blocking socket layer + + if(req->action == NN_INTERNET_CONNECT) { + req->connection->state = ne_inetBullshit; + return NN_OK; + } + + if(req->action == NN_INTERNET_CLOSE) { + return NN_OK; + } + + if(C) nn_setError(C, "inet bullshit: not supported"); + return NN_EBADCALL; +} int main(int argc, char **argv) { const char *player = getenv("USER"); @@ -610,6 +629,7 @@ int main(int argc, char **argv) { nn_Component *dataCard = nn_createDataCard(u, NULL, &nn_defaultDataCards[2], NULL, ne_dataBullshit); nn_Component *modem = nn_createModem(u, NULL, &nn_defaultWirelessModems[1], NULL, ne_modemBullshit); nn_Component *tunnel = nn_createTunnel(u, NULL, &nn_defaultTunnel, NULL, ne_tunnelBullshit); + nn_Component *inet = nn_createInternet(u, NULL, &nn_defaultInternetCard, NULL, ne_inetBullshit); char *eepromCode = (char *)minBIOS; size_t eepromSize = strlen(minBIOS); @@ -755,6 +775,7 @@ int main(int argc, char **argv) { nn_mountComponent(c, modem, 7, false); nn_mountComponent(c, tunnel, 8, false); nn_mountComponent(c, debugfs, 9, false); + nn_mountComponent(c, inet, 10, false); int ltx = 0, lty = 0; double scrollBuf = 0; double tickTime = 0; @@ -994,6 +1015,7 @@ cleanup:; nn_dropComponent(modem); nn_dropComponent(tunnel); nn_dropComponent(debugfs); + nn_dropComponent(inet); // rip the universe nn_destroyUniverse(u); ncl_destroyGlyphCache(gc); diff --git a/src/ncomplib.c b/src/ncomplib.c index bcaabac..e852c29 100644 --- a/src/ncomplib.c +++ b/src/ncomplib.c @@ -1485,7 +1485,8 @@ static nn_Exit ncl_tmpfsHandler(nn_FSRequest *req) { fildes->file->data = data; fildes->file->datalen = capNeeded; } - memcpy(fildes->file->data + fildes->offset, req->write.buf, req->write.len); + // ubsan is acting weird + if(fildes->file->data != NULL) memcpy(fildes->file->data + fildes->offset, req->write.buf, req->write.len); fildes->offset += req->write.len; nn_unlock(ctx, tmpfs->lock); return NN_OK; diff --git a/src/neonucleus.c b/src/neonucleus.c index ea912cc..74e4853 100644 --- a/src/neonucleus.c +++ b/src/neonucleus.c @@ -4499,6 +4499,7 @@ static nn_Exit nn_fsHandler(nn_ComponentRequest *req) { nn_Context *ctx = req->ctx; nn_FSState *state = req->classState; nn_FSRequest freq; + freq.action = NN_FS_DROP; freq.ctx = req->ctx; freq.computer = req->computer; freq.state = req->state; @@ -7429,5 +7430,274 @@ nn_Component *nn_createTunnel(nn_Universe *universe, const char *address, const nn_InternetCard nn_defaultInternetCard = { .protocolsSupported = NN_INET_ALL, + .maxReadSize = 64 * NN_KiB, .transmissionEnergyCost = 0, }; + +typedef enum nn_InternetNum { + NN_INETNUM_MAXREADSIZE, + + // is*Enabled + NN_INETNUM_ISHTTP, + NN_INETNUM_ISTCP, + NN_INETNUM_ISTLS, + NN_INETNUM_ISUDP, + NN_INETNUM_ISWEBSOCK, + + // TCP/TLS connection + NN_INETNUM_CONNECT, + // WebSocket/UDP connection + NN_INETNUM_CHANNEL, + // HTTP/HTTPS request + NN_INETNUM_REQUEST, + + NN_INETNUM_COUNT, +} nn_InternetNum; + +typedef struct nn_InternetState { + nn_Context *ctx; + nn_InternetCard inet; + nn_InternetHandler *handler; +} nn_InternetState; + +static nn_Exit nn_inetCloseConn(nn_InternetRequest req, nn_InternetHandler *handler, nn_InternetConnection *conn) { + if(conn == NULL) return NN_OK; + if(conn->state != NULL) { + req.connection = conn; + req.action = NN_INTERNET_CLOSE; + nn_Exit e = handler(&req); + if(e) return e; + } + conn->state = NULL; + return NN_OK; +} + +// Pushes a userdata that makes the connection, to save on brain power. +static nn_Exit nn_inetMakeConn(nn_InternetRequest req, nn_InternetHandler *handler, nn_InternetProtocol proto) { + if(req.action != NN_INTERNET_CONNECT) return NN_EBADSTATE; + nn_Context *ctx = req.ctx; + + nn_InternetConnection *conn = NULL; + nn_Exit e = NN_OK; + + conn = nn_alloc(ctx, sizeof(*conn)); + if(conn == NULL) { + e = NN_ENOMEM; + goto fail; + } + + conn->protocol = proto; + conn->port = req.connect.port; + conn->state = NULL; + nn_randomUUID(ctx, conn->id); + + req.connection = conn; + e = handler(&req); + if(e) goto fail; + + int idx = nn_allocUserdata(req.computer, conn, req.localAddress); + if(idx < 0) { + e = NN_ELIMIT; + goto fail; + } + + e = nn_pushuserdata(req.computer, idx); + if(e) goto fail; + + return NN_OK; +fail: + nn_inetCloseConn(req, handler, conn); + nn_free(ctx, conn, sizeof(*conn)); + return e; +} + +static nn_Exit nn_inetHandler(nn_ComponentRequest *req) { + nn_Context *ctx = req->ctx; + nn_InternetState *state = req->classState; + nn_Computer *C = req->computer; + nn_Exit e; + + if(req->action == NN_COMP_CHECKMETHOD) return NN_OK; + + nn_InternetRequest ireq; + ireq.ctx = ctx; + ireq.computer = C; + ireq.state = req->state; + ireq.inet = &state->inet; + ireq.localAddress = req->compAddress; + ireq.connection = NULL; + + if(req->action == NN_COMP_DROP) { + ireq.action = NN_INTERNET_DROP; + state->handler(&ireq); + nn_free(ctx, state, sizeof(*state)); + return NN_OK; + } + + if(req->action == NN_COMP_USERDATA) { + nn_UserdataRequest *ureq = req->user; + nn_InternetConnection *conn = ureq->state; + + if(conn->state == NULL) return NN_EBADSTATE; + + if(ureq->action == NN_USER_DROP) { + nn_inetCloseConn(ireq, state->handler, conn); + nn_free(ctx, conn, sizeof(*conn)); + return NN_OK; + } + + if(ureq->action == NN_USER_GETMETHOD) { + size_t idx = ureq->getmethod.idx; + nn_Method *method = ureq->getmethod.method; + + if(idx == 0) { + method->name = "finishConnect"; + method->doc = "function(): boolean - Returns whether the connection finished. Can also return an error if the connection failed, or no error if still pending"; + method->flags = NN_DIRECT; + } else if(idx == 1) { + method->name = "id"; + method->doc = "function(): string - Returns the id of the connection"; + method->flags = NN_DIRECT; + } else if(idx == 2) { + method->name = "close"; + method->doc = "function() - Closes the connection"; + method->flags = NN_DIRECT; + } else if(idx == 3) { + method->name = "read"; + method->doc = "function(n?: number): string? - Reads data from the connection. Returns the data, or nil if EoF"; + method->flags = NN_DIRECT; + } else if(idx == 4) { + method->name = conn->protocol == NN_INET_HTTP ? NULL : "write"; + method->doc = "function(data: string): integer - Writes data to the connection. Returns how much data was successfully written"; + method->flags = NN_DIRECT; + } else { + ureq->getmethod.method = NULL; + } + return NN_OK; + } + + if(ureq->action == NN_USER_INVOKE) { + const char *method = ureq->invoke.method; + + if(nn_strcmp(method, "id") == 0) { + ureq->invoke.returnCount = 1; + return nn_pushlstring(C, conn->id, 36); + } + if(nn_strcmp(method, "close") == 0) { + e = nn_inetCloseConn(ireq, state->handler, conn); + if(e) return e; + ureq->invoke.returnCount = 1; + return nn_pushbool(C, true); + } + } + + if(C) nn_setError(C, "internet: not implemented yet"); + return NN_EBADCALL; + } + + nn_InternetNum method = req->methodIdx; + + if(method == NN_INETNUM_MAXREADSIZE) { + req->returnCount = 1; + return nn_pushinteger(C, state->inet.maxReadSize); + } + if(method == NN_INETNUM_ISHTTP) { + req->returnCount = 1; + return nn_pushbool(C, (state->inet.protocolsSupported & NN_INET_HTTP) != 0); + } + if(method == NN_INETNUM_ISTCP) { + req->returnCount = 1; + return nn_pushbool(C, (state->inet.protocolsSupported & NN_INET_TCP) != 0); + } + if(method == NN_INETNUM_ISTLS) { + req->returnCount = 1; + return nn_pushbool(C, (state->inet.protocolsSupported & NN_INET_TLS) != 0); + } + if(method == NN_INETNUM_ISUDP) { + req->returnCount = 1; + return nn_pushbool(C, (state->inet.protocolsSupported & NN_INET_UDP) != 0); + } + if(method == NN_INETNUM_ISWEBSOCK) { + req->returnCount = 1; + return nn_pushbool(C, (state->inet.protocolsSupported & NN_INET_WEBSOCKET) != 0); + } + + if(method == NN_INETNUM_REQUEST) { + if((state->inet.protocolsSupported & NN_INET_HTTP) == 0) { + nn_setError(C, "protocol disabled"); + return NN_EBADCALL; + } + + if(nn_checkstring(C, 0, "bad argument #1 (string expected")) return NN_EBADCALL; + e = nn_defaultstring(C, 1, ""); + if(e) return e; + if(nn_checkstring(C, 1, "bad argument #2 (string expected")) return NN_EBADCALL; + e = nn_defaulttable(C, 2); + if(e) return e; + if(nn_checktable(C, 2, "bad argument #3 (table expected)")) return NN_EBADCALL; + + ireq.action = NN_INTERNET_CONNECT; + ireq.connect.url = nn_tostring(C, 0); + ireq.connect.http.postdata = nn_tolstring(C, 1, &ireq.connect.http.postdatalen); + size_t headerlen = 0; + e = nn_dumptable(C, 2, &headerlen); + if(e) return e; + ireq.connect.http.headerlen = headerlen; + nn_HTTPHeader *headers = nn_alloc(ctx, sizeof(nn_HTTPHeader) * headerlen); + if(headers == NULL) return NN_ENOMEM; + + for(size_t i = 3; i < 3 + headerlen * 2; i += 2) { + if(!nn_isstring(C, i) || !nn_isstring(C, i+1)) { + nn_free(ctx, headers, sizeof(nn_HTTPHeader) * headerlen); + nn_setError(C, "malformed headers"); + return NN_EBADCALL; + } + headers[i].name = nn_tostring(C, i); + headers[i].value = nn_tostring(C, i+1); + } + + req->returnCount = 1; + e = nn_inetMakeConn(ireq, state->handler, NN_INET_HTTP); + nn_free(ctx, headers, sizeof(nn_HTTPHeader) * headerlen); + return e; + } + + if(C) nn_setError(C, "internet: not implemented yet"); + return NN_EBADCALL; +} + +nn_Component *nn_createInternet(nn_Universe *universe, const char *address, const nn_InternetCard *inet, void *state, nn_InternetHandler *handler) { + nn_Component *c = nn_createComponent( + universe, address, "internet"); + if(c == NULL) return NULL; + + const nn_Method methods[NN_INETNUM_COUNT] = { + [NN_INETNUM_MAXREADSIZE] = {"maxReadSize", "function(): integer - Returns the maximum size of a read", NN_DIRECT}, + [NN_INETNUM_ISHTTP] = {"isHttpEnabled", "function(): boolean - Returns whether HTTP/HTTPS support is enabled", NN_DIRECT}, + [NN_INETNUM_ISTCP] = {"isTcpEnabled", "function(): boolean - Returns whether TCP support is enabled", NN_DIRECT}, + [NN_INETNUM_ISTLS] = {"isTlsEnabled", "function(): boolean - Returns whether TLS support is enabled", NN_DIRECT}, + [NN_INETNUM_ISWEBSOCK] = {"isWebsocketEnabled", "function(): boolean - Returns whether WebSocket / WSS support is enabled", NN_DIRECT}, + [NN_INETNUM_ISUDP] = {"isUdpEnabled", "function(): boolean - Returns whether UDP support is enabled", NN_DIRECT}, + [NN_INETNUM_CONNECT] = {"connect", "function(address: string, port: number = -1, protocol: string = 'tcp'): userdata - Opens a socket connection and returns a handle to it. Valid protocols are tcp and tls", NN_INDIRECT}, + [NN_INETNUM_CHANNEL] = {"channel", "function(address: string, port: number = -1, protocol: string = 'websocket', subprotocols?: string[]): userdata - Opens a packet channel and reutrns a handle to it. Valid protocols are websocket and udp", NN_INDIRECT}, + [NN_INETNUM_REQUEST] = {"request", "function(url: string, portData?: string, headers?: table): userdata - Sends an HTTP/HTTPS requests, returns a handle to it", NN_INDIRECT}, + }; + + nn_Exit e = nn_setComponentMethodsArray( + c, methods, NN_INETNUM_COUNT); + if(e) { nn_dropComponent(c); return NULL; } + + nn_Context *ctx = &universe->ctx; + nn_InternetState *cls = nn_alloc(ctx, sizeof(*cls)); + if(cls == NULL) { + nn_dropComponent(c); + return NULL; + } + cls->ctx = ctx; + cls->inet = *inet; + cls->handler = handler; + nn_setComponentState(c, state); + nn_setComponentClassState(c, cls); + nn_setComponentHandler(c, nn_inetHandler); + return c; +} diff --git a/src/neonucleus.h b/src/neonucleus.h index 013436c..c8aa30a 100644 --- a/src/neonucleus.h +++ b/src/neonucleus.h @@ -2354,6 +2354,7 @@ typedef enum nn_InternetProtocol { typedef struct nn_InternetCard { // bitwise OR multiple of them unsigned char protocolsSupported; + unsigned int maxReadSize; // per-byte cost of a write double transmissionEnergyCost; } nn_InternetCard; @@ -2362,7 +2363,9 @@ extern nn_InternetCard nn_defaultInternetCard; typedef struct nn_InternetConnection { nn_InternetProtocol protocol; + int port; void *state; + nn_uuid id; } nn_InternetConnection; typedef struct nn_HTTPHeader { @@ -2370,13 +2373,38 @@ typedef struct nn_HTTPHeader { const char *value; } nn_HTTPHeader; +typedef enum nn_InternetAction { + NN_INTERNET_DROP, + + // start a connection, remember to check connection->protocol to know which type of connection to establish + NN_INTERNET_CONNECT, + + // Ensure a connection is established, error if not. + // It is assumed this operation will return whether it is currently connected, + // and should be called repeatedly until it works + NN_INTERNET_FINISHCONNECT, + + // close a connection + NN_INTERNET_CLOSE, + + // read from a connection. Set read.buf to NULL to mark EoF. read.len is the capacity, but also set how much was read in it. + NN_INTERNET_READ, + + // write to a connection. HTTP connections do not have this method + NN_INTERNET_WRITE, + + // get an HTTP response. Should push the response code, message (ie. OK for 200) and table + NN_INTERNET_RESPONSE, +} nn_INternetAction; + typedef struct nn_InternetRequest { nn_Context *ctx; nn_Computer *computer; void *state; - const nn_Tunnel *tunnel; + const nn_InternetCard *inet; const char *localAddress; nn_InternetConnection *connection; + nn_INternetAction action; union { // does a socket connection, for any of the supported protocols struct { @@ -2384,15 +2412,36 @@ typedef struct nn_InternetRequest { const char *url; // port. Useless for HTTP connections, as they should use 80 for HTTP and 443 for HTTPS unsigned short port; - // HTTP specific - size_t postdatalen; - const char *postdata; - const nn_HTTPHeader *headers; - size_t headerlen; + union { + struct { + // HTTP specific + size_t postdatalen; + const char *postdata; + const nn_HTTPHeader *headers; + size_t headerlen; + } http; + struct { + const char * const * subprotocols; + size_t subprotocolcount; + } websockets; + }; } connect; + struct { + const char *buf; + size_t len; + } write; + struct { + char *buf; + size_t len; + } read; + bool isConnected; }; } nn_InternetRequest; +typedef nn_Exit (nn_InternetHandler)(nn_InternetRequest *req); + +nn_Component *nn_createInternet(nn_Universe *universe, const char *address, const nn_InternetCard *inet, void *state, nn_InternetHandler *handler); + // Colors and palettes. // Do note that the