From 88135162d7e20c1855b14cce6aa89a72a7902099 Mon Sep 17 00:00:00 2001 From: ProgrammerIn-wonderland <3838shah@gmail.com> Date: Sat, 24 Aug 2024 18:48:31 -0400 Subject: [PATCH 1/4] rebase ontop of fake networking --- Makefile | 3 +- copy.sh/v86/index.html | 283 +++++++++++++++++++++++++++++ src/browser/fake_network.js | 123 +++++++------ src/browser/starter.js | 3 + src/browser/wisp_network.js | 316 ++++++++++++++++++++++++++++++++ tests/devices/wisp_network.js | 326 ++++++++++++++++++++++++++++++++++ 6 files changed, 998 insertions(+), 56 deletions(-) create mode 100644 copy.sh/v86/index.html create mode 100644 src/browser/wisp_network.js create mode 100755 tests/devices/wisp_network.js diff --git a/Makefile b/Makefile index 3cb82fb5c7..fe0ccb9441 100644 --- a/Makefile +++ b/Makefile @@ -87,7 +87,7 @@ CORE_FILES=const.js config.js io.js main.js lib.js buffer.js ide.js pci.js flopp LIB_FILES=9p.js filesystem.js jor1k.js marshall.js utf8.js BROWSER_FILES=screen.js keyboard.js mouse.js speaker.js serial.js \ network.js starter.js worker_bus.js dummy_screen.js \ - fake_network.js fetch_network.js print_stats.js filestorage.js + fake_network.js wisp_network.js fetch_network.js print_stats.js filestorage.js RUST_FILES=$(shell find src/rust/ -name '*.rs') \ src/rust/gen/interpreter.rs src/rust/gen/interpreter0f.rs \ @@ -306,6 +306,7 @@ devices-test: all-debug ./tests/devices/virtio_9p.js ./tests/devices/virtio_console.js ./tests/devices/fetch_network.js + ./tests/devices/wisp_network.js rust-test: $(RUST_FILES) env RUSTFLAGS="-D warnings" RUST_BACKTRACE=full RUST_TEST_THREADS=1 cargo test -- --nocapture diff --git a/copy.sh/v86/index.html b/copy.sh/v86/index.html new file mode 100644 index 0000000000..27cd2ab1d1 --- /dev/null +++ b/copy.sh/v86/index.html @@ -0,0 +1,283 @@ + + + +Virtual x86 + + + + + + +
+
+

Select profile

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Arch Linux 12 MB >_ + Complete Arch Linux with various compilers, networking and Xorg. Restored from snapshot.
Damn Small Linux 50 MB 💻 + Graphical Linux with 2.4 kernel, Firefox 2.0 and more. Takes 1 minute to boot.
Buildroot Linux 5.0 MB >_ + Minimal Linux with busybox, Lua, tests, internet access, ping, telnet and curl. Exchange files through /mnt/.
ReactOS 18 MB 💻 + Windows-compatible OS with QtWeb and Breakout. Restored from snapshot.
Windows 2000 22 MB 💻 + Including Pinball and Internet Explorer with internet access. Additional sectors are loaded as needed.
Windows 98 9.7 MB 💻 + Including Minesweeper and Internet Explorer with internet access. Additional sectors are loaded as needed.
Windows 95 4.6 MB 💻 + Restored from snapshot
Windows 3.1 15 MB 💻 + Takes 15 seconds to boot
Windows 1.01 0.6 MB 💻 + The first version of Microsoft Windows
MS-DOS 6.22 4.4 MB >_ + With Enhanced Tools, QBasic, vim, games and demos.
FreeDOS 0.5 MB >_ + With nasm, vim, debug.com, Rogue, some games and demos.
FreeBSD 17 MB >_ + FreeBSD 12.0 base install. Restored from snapshot.
OpenBSD 12 MB >_ + OpenBSD 6.6 base install. Restored from snapshot.
9front 4.4 MB 💻 + A Plan 9 fork.
Haiku 38 MB 💻 + An open-source operating system inspired by BeOS. Restored from snapshot. Includes network support.
SerenityOS 17 MB 💻 + A graphical Unix-like operating system. Restored from snapshot.
HelenOS 7.9 MB 💻 + A graphical operating system based on a multiserver microkernel design
FiwixOS 15 MB >_ + A Unix-like OS written from scratch. Includes Doom.
Android-x86 42 MB 💻 + An x86 port of the Android 1.6. Quite slow. Takes about 3 minutes to boot.
Oberon 1.2 MB 💻 + Native Oberon 2.3.6
KolibriOS 1.4 MB 💻 + Fast graphical OS written in Assembly
QNX 1.3 MB 💻 + QNX 4.05 Demo disk (no networking)
Snowdrop 0.3 MB >_ + A homebrew operating system from scratch, written in assembly language
Solar OS 0.3 MB 💻 + Simple graphical OS
Bootchess 512 B >_ + A tiny chess program written in the boot sector
SectorLISP 512 B >_ + A LISP interpreter that fits into the boot sector
+ +
+

Setup

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ +




Disk images are not uploaded to the server
+ MB
+
+ MB
+
+ +

+
+
+
+

+ +
+ +
+ +
+ + + +
+ + + + + + + + +

+ +
+ +
+Version: 98e7110c2 (Feb 16, 2021 12:02) + +
+Enable debug +— +Readme +— +Project on Github +— +Compatibility diff --git a/src/browser/fake_network.js b/src/browser/fake_network.js index 33d99505e0..39ea9ba393 100644 --- a/src/browser/fake_network.js +++ b/src/browser/fake_network.js @@ -225,33 +225,7 @@ function handle_fake_networking(data, adapter) { } if(packet.arp && packet.arp.oper === 1 && packet.arp.ptype === ETHERTYPE_IPV4) { - let packet_subnet = iptolong(packet.arp.tpa) & 0xFFFFFF00; - let router_subnet = iptolong(adapter.router_ip) & 0xFFFFFF00; - - if(!adapter.masquerade) { - if(packet_subnet !== router_subnet) { - return; - } - } - - if(packet_subnet === router_subnet) { - // Ignore the DHCP client area - if(packet.arp.tpa[3] > 99) return; - } - - // Reply to ARP Whohas - let reply = {}; - reply.eth = { ethertype: ETHERTYPE_ARP, src: adapter.router_mac, dest: packet.eth.src }; - reply.arp = { - htype: 1, - ptype: ETHERTYPE_IPV4, - oper: 2, - sha: adapter.router_mac, - spa: packet.arp.tpa, - tha: packet.eth.src, - tpa: packet.arp.spa - }; - adapter.receive(make_packet(reply)); + arp_whohas(packet, adapter); } if(packet.dns) { @@ -264,20 +238,7 @@ function handle_fake_networking(data, adapter) { // ICMP Ping if(packet.icmp && packet.icmp.type === 8) { - let reply = {}; - reply.eth = { ethertype: ETHERTYPE_IPV4, src: adapter.router_mac, dest: packet.eth.src }; - reply.ipv4 = { - proto: IPV4_PROTO_ICMP, - src: adapter.router_ip, - dest: packet.ipv4.src, - }; - reply.icmp = { - type: 0, - code: packet.icmp.code, - data: packet.icmp.data - }; - adapter.receive(make_packet(reply)); - return; + handle_fake_ping(packet, adapter); } if(packet.dhcp) { @@ -285,20 +246,7 @@ function handle_fake_networking(data, adapter) { } if(packet.udp && packet.udp.dport === 8) { - // UDP Echo Server - let reply = {}; - reply.eth = { ethertype: ETHERTYPE_IPV4, src: adapter.router_mac, dest: packet.eth.src }; - reply.ipv4 = { - proto: IPV4_PROTO_UDP, - src: packet.ipv4.dest, - dest: packet.ipv4.src, - }; - reply.udp = { - sport: packet.udp.dport, - dport: packet.udp.sport, - data: new TextEncoder().encode(packet.udp.data_s) - }; - adapter.receive(make_packet(reply)); + handle_udp_echo(packet, adapter); } } @@ -528,6 +476,7 @@ function parse_udp(data, o) { dport: view.getUint16(2), len: view.getUint16(4), checksum: view.getUint16(6), + data: data.subarray(8), data_s: new TextDecoder().decode(data.subarray(8)) }; @@ -1133,3 +1082,67 @@ TCPConnection.prototype.pump = function() { this.net.receive(make_packet(reply)); } }; + + +function arp_whohas(packet, adapter) { + let packet_subnet = iptolong(packet.arp.tpa) & 0xFFFFFF00; + let router_subnet = iptolong(adapter.router_ip) & 0xFFFFFF00; + + if(!adapter.masquerade) { + if(packet_subnet !== router_subnet) { + return; + } + } + + if(packet_subnet === router_subnet) { + // Ignore the DHCP client area + if(packet.arp.tpa[3] > 99) return; + } + + // Reply to ARP Whohas + let reply = {}; + reply.eth = { ethertype: ETHERTYPE_ARP, src: adapter.router_mac, dest: packet.eth.src }; + reply.arp = { + htype: 1, + ptype: ETHERTYPE_IPV4, + oper: 2, + sha: adapter.router_mac, + spa: packet.arp.tpa, + tha: packet.eth.src, + tpa: packet.arp.spa + }; + adapter.receive(make_packet(reply)); +} + +function handle_fake_ping(packet, adapter) { + let reply = {}; + reply.eth = { ethertype: ETHERTYPE_IPV4, src: adapter.router_mac, dest: packet.eth.src }; + reply.ipv4 = { + proto: IPV4_PROTO_ICMP, + src: adapter.router_ip, + dest: packet.ipv4.src, + }; + reply.icmp = { + type: 0, + code: packet.icmp.code, + data: packet.icmp.data + }; + adapter.receive(make_packet(reply)); +} + +function handle_udp_echo(packet, adapter) { + // UDP Echo Server + let reply = {}; + reply.eth = { ethertype: ETHERTYPE_IPV4, src: adapter.router_mac, dest: packet.eth.src }; + reply.ipv4 = { + proto: IPV4_PROTO_UDP, + src: packet.ipv4.dest, + dest: packet.ipv4.src, + }; + reply.udp = { + sport: packet.udp.dport, + dport: packet.udp.sport, + data: new TextEncoder().encode(packet.udp.data_s) + }; + adapter.receive(make_packet(reply)); +} diff --git a/src/browser/starter.js b/src/browser/starter.js index ae1c5425ba..f0ec027d6b 100644 --- a/src/browser/starter.js +++ b/src/browser/starter.js @@ -297,6 +297,9 @@ V86.prototype.continue_init = async function(emulator, options) { this.network_adapter = new FetchNetworkAdapter(this.bus); } + else if(options.network_relay_url.startsWith("wisp://") || options.network_relay_url.startsWith("wisps://")) { + this.network_adapter = new WispNetworkAdapter(options.network_relay_url, this.bus, options); + } else { this.network_adapter = new NetworkAdapter(options.network_relay_url, this.bus); diff --git a/src/browser/wisp_network.js b/src/browser/wisp_network.js new file mode 100644 index 0000000000..234c72d983 --- /dev/null +++ b/src/browser/wisp_network.js @@ -0,0 +1,316 @@ +"use strict"; +const DEFAULT_DOH_SERVER = "cloudflare-dns.com"; + +/** + * @constructor + * + * @param {BusConnector} bus + * @param {*=} config + */ +function WispNetworkAdapter(wisp_url, bus, config) +{ + + this.register_ws(wisp_url); + config = config || {}; + this.bus = bus; + this.id = config.id || 0; + this.router_mac = new Uint8Array((config.router_mac || "52:54:0:1:2:3").split(":").map(function(x) { return parseInt(x, 16); })); + this.router_ip = new Uint8Array((config.router_ip || "192.168.86.1").split(".").map(function(x) { return parseInt(x, 10); })); + this.vm_ip = new Uint8Array((config.vm_ip || "192.168.86.100").split(".").map(function(x) { return parseInt(x, 10); })); + this.masquerade = config.masquerade === undefined || !!config.masquerade; + this.vm_mac = new Uint8Array(6); + this.doh_server = config.doh_server || DEFAULT_DOH_SERVER; + this.tcp_conn = {}; + + this.bus.register("net" + this.id + "-mac", function(mac) { + this.vm_mac = new Uint8Array(mac.split(":").map(function(x) { return parseInt(x, 16); })); + }, this); + this.bus.register("net" + this.id + "-send", function(data) + { + this.send(data); + }, this); +} + +WispNetworkAdapter.prototype.register_ws = function (wisp_url) { + this.wispws = new WebSocket(wisp_url.replace("wisp://", "ws://").replace("wisps://", "wss://")); + this.wispws.binaryType = "arraybuffer"; + this.wispws.onmessage = (event) => { + this.process_incoming_wisp_frame(new Uint8Array(event.data)); + }; + this.wispws.onclose = () => { + setTimeout(() => { + this.register_ws(wisp_url); + }, 10000); // wait 10s before reconnecting + }; +}; +WispNetworkAdapter.prototype.last_stream = 1; + +WispNetworkAdapter.prototype.connections = {0: {congestion: 0}}; + +WispNetworkAdapter.prototype.congested_buffer = []; +WispNetworkAdapter.prototype.send_packet = function (data, type, stream_id) { + if(this.connections[stream_id].congestion > 0) { + if(type === "DATA") { + this.connections[stream_id].congestion--; + } + this.wispws.send(data); + } else { + this.connections[stream_id].congested = true; + this.congested_buffer.push({data: data, type: type}); + } +}; + +WispNetworkAdapter.prototype.process_incoming_wisp_frame = function (frame) { + const view = new DataView(frame.buffer); + const stream_id = view.getUint32(1, true); + switch(frame[0]) { + case 1: // CONNECT + // The server should never send this actually + dbg_log("Server sent client-only packet CONNECT", LOG_NET); + break; + case 2: // DATA + if(this.connections[stream_id]) + this.connections[stream_id].data_callback(frame.slice(5)); + else + throw new Error("Got a DATA packet but stream not registered. ID: " + stream_id); + + + break; + case 3: // CONTINUE + if(this.connections[stream_id]) { + this.connections[stream_id].congestion = view.getUint32(5, true); + } + + if(this.connections[stream_id].congested) { + for(const packet of this.congested_buffer) { + this.send_packet(packet.data, packet.type, stream_id); + } + this.connections[stream_id].congested = false; + } + + break; + case 4: // CLOSE + if(this.connections[stream_id]) + this.connections[stream_id].close_callback(view.getUint8(5)); + delete this.connections[stream_id]; + break; + case 5: // PROTOEXT + dbg_log("got a wisp V2 upgrade request, ignoring", LOG_NET); + // Not responding, this is wisp v1 client not wisp v2; + break; + default: + dbg_log("Wisp server returned unknown packet: " + frame[0], LOG_NET); + } +}; + + +// FrameObj will be the following +// FrameObj.stream_id (number) +// +// FrameObj.type -- CONNECT +// FrameObj.hostname (string) +// FrameObj.port (number) +// FrameObj.data_callback (function (Uint8Array)) +// FrameObj.close_callback (function (number)) OPTIONAL +// +// +// FrameObj.type -- DATA +// FrameObj.data (Uint8Array) +// +// FrameObj.type -- CLOSE +// FrameObj.reason (number) +// +// + +WispNetworkAdapter.prototype.send_wisp_frame = function (frame_obj) { + let full_packet; + let view; + switch(frame_obj.type) { + case "CONNECT": + const hostname_buffer = new TextEncoder().encode(frame_obj.hostname); + full_packet = new Uint8Array(5 + 1 + 2 + hostname_buffer.length); + view = new DataView(full_packet.buffer); + view.setUint8(0, 0x01); // TYPE + view.setUint32(1, frame_obj.stream_id, true); // Stream ID + view.setUint8(5, 0x01); // TCP + view.setUint16(6, frame_obj.port, true); // PORT + full_packet.set(hostname_buffer, 8); // hostname + + // Setting callbacks + this.connections[frame_obj.stream_id] = { + data_callback: frame_obj.data_callback, + close_callback: frame_obj.close_callback, + congestion: this.connections[0].congestion + }; + + + break; + case "DATA": + + full_packet = new Uint8Array(5 + frame_obj.data.length); + view = new DataView(full_packet.buffer); + view.setUint8(0, 0x02); // TYPE + view.setUint32(1, frame_obj.stream_id, true); // Stream ID + full_packet.set(frame_obj.data, 5); // Actual data + + break; + case "CLOSE": + full_packet = new Uint8Array(5 + 1); + view = new DataView(full_packet.buffer); + view.setUint8(0, 0x04); // TYPE + view.setUint32(1, frame_obj.stream_id, true); // Stream ID + view.setUint8(5, frame_obj.reason); // Packet size + + break; + default: + dbg_log("Client tried to send unknown packet: " + frame_obj.type, LOG_NET); + + } + this.send_packet(full_packet, frame_obj.type, frame_obj.stream_id); +}; + +WispNetworkAdapter.prototype.destroy = function() +{ + if(this.wispws) { + this.wispws.onmessage = null; + this.wispws.onclose = null; + this.wispws.close(); + } +}; + +/** + * @param {Uint8Array} data + */ +WispNetworkAdapter.prototype.send = function(data) +{ + let packet = {}; + parse_eth(data, packet); + + if(packet.tcp) { + let reply = {}; + reply.eth = { ethertype: ETHERTYPE_IPV4, src: this.router_mac, dest: packet.eth.src }; + reply.ipv4 = { + proto: IPV4_PROTO_TCP, + src: packet.ipv4.dest, + dest: packet.ipv4.src + }; + + let tuple = [ + packet.ipv4.src.join("."), + packet.tcp.sport, + packet.ipv4.dest.join("."), + packet.tcp.dport + ].join(":"); + + + if(packet.tcp.syn) { + if(this.tcp_conn[tuple]) { + dbg_log("SYN to already opened port", LOG_FETCH); + } + const tcp_conn = new TCPConnection(); + + tcp_conn.state = TCP_STATE_SYN_RECEIVED; + tcp_conn.net = this; + tcp_conn.send_wisp_frame = this.send_wisp_frame; + tcp_conn.tuple = tuple; + tcp_conn.stream_id = this.last_stream++; + this.tcp_conn[tuple] = tcp_conn; + + tcp_conn.on_data = (data) => { + if(data.length !== 0) { + this.send_wisp_frame({ + type: "DATA", + stream_id: tcp_conn.stream_id, + data: data + }); + } + }; + + this.send_wisp_frame({ + type: "CONNECT", + stream_id: tcp_conn.stream_id, + hostname: packet.ipv4.dest.join("."), + port: packet.tcp.dport, + data_callback: (data) => { + tcp_conn.write(data); + }, + close_callback: (data) => { + tcp_conn.close(); + } + }); + + tcp_conn.accept(packet); + + return; + } + + if(!this.tcp_conn[tuple]) { + dbg_log(`I dont know about ${tuple}, so restting`, LOG_FETCH); + let bop = packet.tcp.ackn; + if(packet.tcp.fin || packet.tcp.syn) bop += 1; + reply.tcp = { + sport: packet.tcp.dport, + dport: packet.tcp.sport, + seq: bop, + ackn: packet.tcp.seq + (packet.tcp.syn ? 1: 0), + winsize: packet.tcp.winsize, + rst: true, + ack: packet.tcp.syn + }; + this.receive(make_packet(reply)); + return; + } + + this.tcp_conn[tuple].process(packet); + } + + if(packet.arp && packet.arp.oper === 1 && packet.arp.ptype === ETHERTYPE_IPV4) { + arp_whohas(packet, this); + } + + if(packet.dns) { + (async () => { + let reply = {}; + reply.eth = { ethertype: ETHERTYPE_IPV4, src: this.router_mac, dest: packet.eth.src }; + reply.ipv4 = { + proto: IPV4_PROTO_UDP, + src: this.router_ip, + dest: packet.ipv4.src, + }; + reply.udp = { sport: 53, dport: packet.udp.sport }; + const result = await ((await fetch(`https://${this.doh_server}/dns-query`, {method: "POST", headers: [["content-type", "application/dns-message"]], body: packet.udp.data})).arrayBuffer()); + reply.udp.data = new Uint8Array(result); + this.receive(make_packet(reply)); + + })(); + + } + + if(packet.ntp) { + handle_fake_ntp(packet, this); + return; + } + + // ICMP Ping + if(packet.icmp && packet.icmp.type === 8) { + handle_fake_ping(packet, this); + return; + } + + if(packet.dhcp) { + handle_fake_dhcp(packet, this); + return; + } + + if(packet.udp && packet.udp.dport === 8) { + handle_udp_echo(packet, this); + } +}; + +/** + * @param {Uint8Array} data + */ +WispNetworkAdapter.prototype.receive = function(data) +{ + this.bus.send("net" + this.id + "-receive", new Uint8Array(data)); +}; diff --git a/tests/devices/wisp_network.js b/tests/devices/wisp_network.js new file mode 100755 index 0000000000..6d2bfbdb93 --- /dev/null +++ b/tests/devices/wisp_network.js @@ -0,0 +1,326 @@ +#!/usr/bin/env -S node --experimental-websocket +"use strict"; + +process.on("unhandledRejection", exn => { throw exn; }); + +const TEST_RELEASE_BUILD = +process.env.TEST_RELEASE_BUILD; + +const V86 = require(`../../build/${TEST_RELEASE_BUILD ? "libv86" : "libv86-debug"}.js`).V86; + +const assert = require("assert").strict; +const SHOW_LOGS = false; +const STOP_ON_FIRST_FAILURE = false; + +function log_pass(msg, ...args) +{ + console.log(`\x1b[92m[+] ${msg}\x1b[0m`, ...args); +} + +function log_warn(msg, ...args) +{ + console.error(`\x1b[93m[!] ${msg}\x1b[0m`, ...args); +} + +function log_fail(msg, ...args) +{ + console.error(`\x1b[91m[-] ${msg}\x1b[0m`, ...args); +} + +const tests = +[ + { + name: "DHCP", + timeout: 60, + start: () => + { + emulator.serial0_send("udhcpc\n"); + emulator.serial0_send("echo -e done\\\\tudhcpc\n"); + }, + end_trigger: "done\tudhcpc", + end: (capture) => + { + assert(/lease of 192.168.86.100 obtained/.test(capture), "lease of 192.168.86.100 obtained"); + }, + }, + { + name: "ifconfig", + timeout: 60, + start: () => + { + emulator.serial0_send("ifconfig\n"); + emulator.serial0_send("echo -e done\\\\tifconfig\n"); + }, + end_trigger: "done\tifconfig", + end: (capture) => + { + assert(/192.168.86.100/.test(capture), "192.168.86.100"); + }, + }, + { + name: "route", + timeout: 60, + start: () => + { + emulator.serial0_send("ip route\n"); + emulator.serial0_send("echo -e done\\\\troute\n"); + }, + end_trigger: "done\troute", + end: (capture) => + { + assert(/192.168.86.1/.test(capture), "192.168.86.100"); + }, + }, + { + name: "ping 1.2.3.4", + timeout: 60, + start: () => + { + emulator.serial0_send("ping -c 2 1.2.3.4\n"); + emulator.serial0_send("echo -e done\\\\tping\n"); + }, + end_trigger: "done\tping", + end: (capture) => + { + assert(/2 packets transmitted, 2 packets received, 0% packet loss/.test(capture), "2 packets transmitted, 2 packets received, 0% packet loss"); + }, + }, + { + name: "arp -a", + timeout: 60, + start: () => + { + emulator.serial0_send("arp -a\n"); + emulator.serial0_send("echo -e done\\\\tarp\n"); + }, + end_trigger: "done\tarp", + end: (capture) => + { + assert(/.192.168.86.1. at 52:54:00:01:02:03 \[ether\] {2}on eth0/.test(capture), "(192.168.86.1) at 52:54:00:01:02:03 [ether] on eth0"); + }, + }, + { + name: "Curl example.org", + timeout: 60, + allow_failure: true, + start: () => + { + emulator.serial0_send("wget -T 10 -O - example.org\n"); + emulator.serial0_send("echo -e done\\\\texample.org\n"); + }, + end_trigger: "done\texample.org", + end: (capture) => + { + assert(/This domain is for use in illustrative examples in documents/.test(capture), "got example.org text"); + }, + }, + +]; + +let test_num = 0; +let test_timeout = 0; +const failed_tests = []; + +const emulator = new V86({ + bios: { url: __dirname + "/../../bios/seabios.bin" }, + vga_bios: { url: __dirname + "/../../bios/vgabios.bin" }, + cdrom: { url: __dirname + "/../../images/linux4.iso" }, + autostart: true, + memory_size: 64 * 1024 * 1024, + disable_jit: +process.env.DISABLE_JIT, + network_relay_url: "wisps://wisp.mercurywork.shop/", + log_level: SHOW_LOGS ? 0x400000 : 0, +}); + +emulator.add_listener("emulator-ready", function () { + +}); + +let ran_command = false; +let line = ""; +let capturing = false; +let capture = ""; +let next_trigger; +let next_trigger_handler; + +function start_timeout() +{ + if(tests[test_num].timeout) + { + test_timeout = setTimeout(() => + { + log_fail("Test #%d (%s) took longer than %s sec. Timing out and terminating.", test_num, tests[test_num].name, tests[test_num].timeout); + process.exit(1); + }, tests[test_num].timeout * 1000); + } +} + +function begin() +{ + start_timeout(); + + console.log("\nPreparing test #%d: %s", test_num, tests[test_num].name); + start_test(); +} + +function start_test() +{ + console.log("Starting test #%d: %s", test_num, tests[test_num].name); + + capture = ""; + + tests[test_num].start(); + + if(tests[test_num].capture_trigger) + { + next_trigger = tests[test_num].capture_trigger; + next_trigger_handler = start_capture; + } + else + { + next_trigger = tests[test_num].end_trigger; + next_trigger_handler = end_test; + } + start_capture(); +} + +function start_capture() +{ + console.log("Capturing..."); + capture = ""; + capturing = true; + + next_trigger = tests[test_num].end_trigger; + next_trigger_handler = end_test; +} + +function end_test() +{ + capturing = false; + + if(tests[test_num].timeout) + { + clearTimeout(test_timeout); + } + + let test_has_failed = false; + + try { + tests[test_num].end(capture); + } catch(e) { + console.log(e); + test_has_failed = true; + } + + if(!test_has_failed) + { + log_pass("Test #%d passed: %s", test_num, tests[test_num].name); + } + else + { + if(tests[test_num].allow_failure) + { + log_warn("Test #%d failed: %s (failure allowed)", test_num, tests[test_num].name); + } + else + { + log_fail("Test #%d failed: %s", test_num, tests[test_num].name); + + if(STOP_ON_FIRST_FAILURE) + { + finish_tests(); + } + } + test_has_failed = false; + } + + test_num++; + + if(test_num < tests.length) + { + begin(); + } + else + { + finish_tests(); + } +} + +function finish_tests() +{ + emulator.stop(); + emulator.destroy(); + + console.log("\nTests finished."); + if(failed_tests.length === 0) + { + console.log("All tests passed"); + } + else + { + let unallowed_failure = false; + + console.error("Failed %d out of %d tests:", failed_tests.length, tests.length); + for(const num of failed_tests) + { + if(tests[num].allow_failure) + { + log_warn("#%d %s (failure allowed)", num, tests[num].name); + } + else + { + unallowed_failure = true; + log_fail("#%d %s", num, tests[num].name); + } + } + if(unallowed_failure) + { + process.exit(1); + } + } +} + +emulator.bus.register("emulator-started", function() +{ + console.error("Booting now, please stand by"); +}); + +emulator.add_listener("serial0-output-byte", function(byte) +{ + const chr = String.fromCharCode(byte); + if(chr < " " && chr !== "\n" && chr !== "\t" || chr > "~") + { + return; + } + + let new_line = ""; + let is_new_line = false; + if(chr === "\n") + { + is_new_line = true; + new_line = line; + line = ""; + } + else + { + line += chr; + } + + if(!ran_command && line.endsWith("~% ")) + { + ran_command = true; + begin(); + } + else if(new_line === next_trigger) + { + next_trigger_handler(); + } + else if(is_new_line && capturing) + { + capture += new_line + "\n"; + console.log(" Captured: %s", new_line); + } + else if(is_new_line) + { + console.log(" Serial: %s", new_line); + } +}); From d56e403f5fc0822870a3f47f55be99e268c9b62c Mon Sep 17 00:00:00 2001 From: ProgrammerIn-wonderland <3838shah@gmail.com> Date: Sat, 24 Aug 2024 18:51:36 -0400 Subject: [PATCH 2/4] move last_stream, connections and congested buffer to constructor --- src/browser/wisp_network.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/browser/wisp_network.js b/src/browser/wisp_network.js index 234c72d983..8212d50841 100644 --- a/src/browser/wisp_network.js +++ b/src/browser/wisp_network.js @@ -11,6 +11,10 @@ function WispNetworkAdapter(wisp_url, bus, config) { this.register_ws(wisp_url); + this.last_stream = 1; + this.connections = {0: {congestion: 0}}; + this.congested_buffer = []; + config = config || {}; this.bus = bus; this.id = config.id || 0; @@ -43,11 +47,7 @@ WispNetworkAdapter.prototype.register_ws = function (wisp_url) { }, 10000); // wait 10s before reconnecting }; }; -WispNetworkAdapter.prototype.last_stream = 1; - -WispNetworkAdapter.prototype.connections = {0: {congestion: 0}}; -WispNetworkAdapter.prototype.congested_buffer = []; WispNetworkAdapter.prototype.send_packet = function (data, type, stream_id) { if(this.connections[stream_id].congestion > 0) { if(type === "DATA") { From 6d5c8acdf289e80430c95b102f007c616fc63694 Mon Sep 17 00:00:00 2001 From: ProgrammerIn-wonderland <3838shah@gmail.com> Date: Sat, 24 Aug 2024 22:49:05 -0400 Subject: [PATCH 3/4] delete accidental commit file --- copy.sh/v86/index.html | 283 ----------------------------------------- 1 file changed, 283 deletions(-) delete mode 100644 copy.sh/v86/index.html diff --git a/copy.sh/v86/index.html b/copy.sh/v86/index.html deleted file mode 100644 index 27cd2ab1d1..0000000000 --- a/copy.sh/v86/index.html +++ /dev/null @@ -1,283 +0,0 @@ - - - -Virtual x86 - - - - - - -
-
-

Select profile

- - - - - - - - - - - - - - - - - - - - - - - - - - - -
Arch Linux 12 MB >_ - Complete Arch Linux with various compilers, networking and Xorg. Restored from snapshot.
Damn Small Linux 50 MB 💻 - Graphical Linux with 2.4 kernel, Firefox 2.0 and more. Takes 1 minute to boot.
Buildroot Linux 5.0 MB >_ - Minimal Linux with busybox, Lua, tests, internet access, ping, telnet and curl. Exchange files through /mnt/.
ReactOS 18 MB 💻 - Windows-compatible OS with QtWeb and Breakout. Restored from snapshot.
Windows 2000 22 MB 💻 - Including Pinball and Internet Explorer with internet access. Additional sectors are loaded as needed.
Windows 98 9.7 MB 💻 - Including Minesweeper and Internet Explorer with internet access. Additional sectors are loaded as needed.
Windows 95 4.6 MB 💻 - Restored from snapshot
Windows 3.1 15 MB 💻 - Takes 15 seconds to boot
Windows 1.01 0.6 MB 💻 - The first version of Microsoft Windows
MS-DOS 6.22 4.4 MB >_ - With Enhanced Tools, QBasic, vim, games and demos.
FreeDOS 0.5 MB >_ - With nasm, vim, debug.com, Rogue, some games and demos.
FreeBSD 17 MB >_ - FreeBSD 12.0 base install. Restored from snapshot.
OpenBSD 12 MB >_ - OpenBSD 6.6 base install. Restored from snapshot.
9front 4.4 MB 💻 - A Plan 9 fork.
Haiku 38 MB 💻 - An open-source operating system inspired by BeOS. Restored from snapshot. Includes network support.
SerenityOS 17 MB 💻 - A graphical Unix-like operating system. Restored from snapshot.
HelenOS 7.9 MB 💻 - A graphical operating system based on a multiserver microkernel design
FiwixOS 15 MB >_ - A Unix-like OS written from scratch. Includes Doom.
Android-x86 42 MB 💻 - An x86 port of the Android 1.6. Quite slow. Takes about 3 minutes to boot.
Oberon 1.2 MB 💻 - Native Oberon 2.3.6
KolibriOS 1.4 MB 💻 - Fast graphical OS written in Assembly
QNX 1.3 MB 💻 - QNX 4.05 Demo disk (no networking)
Snowdrop 0.3 MB >_ - A homebrew operating system from scratch, written in assembly language
Solar OS 0.3 MB 💻 - Simple graphical OS
Bootchess 512 B >_ - A tiny chess program written in the boot sector
SectorLISP 512 B >_ - A LISP interpreter that fits into the boot sector
- -
-

Setup

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- -




Disk images are not uploaded to the server
- MB
-
- MB
-
- -

-
-
-
-

- -
- -
- -
- - - -
- - - - - - - - -

- -
- -
-Version: 98e7110c2 (Feb 16, 2021 12:02) - -
-Enable debug -— -Readme -— -Project on Github -— -Compatibility From 376aabb197407a9d9d4343f58429fbcde15532cf Mon Sep 17 00:00:00 2001 From: Fabian Date: Tue, 27 Aug 2024 21:16:46 +0200 Subject: [PATCH 4/4] cleanup --- src/browser/wisp_network.js | 32 ++++++++------------------ tests/devices/wisp_network.js | 42 ++++++++++++----------------------- 2 files changed, 23 insertions(+), 51 deletions(-) diff --git a/src/browser/wisp_network.js b/src/browser/wisp_network.js index 8212d50841..faf0f5fc8c 100644 --- a/src/browser/wisp_network.js +++ b/src/browser/wisp_network.js @@ -1,4 +1,5 @@ "use strict"; + const DEFAULT_DOH_SERVER = "cloudflare-dns.com"; /** @@ -35,7 +36,7 @@ function WispNetworkAdapter(wisp_url, bus, config) }, this); } -WispNetworkAdapter.prototype.register_ws = function (wisp_url) { +WispNetworkAdapter.prototype.register_ws = function(wisp_url) { this.wispws = new WebSocket(wisp_url.replace("wisp://", "ws://").replace("wisps://", "wss://")); this.wispws.binaryType = "arraybuffer"; this.wispws.onmessage = (event) => { @@ -48,7 +49,7 @@ WispNetworkAdapter.prototype.register_ws = function (wisp_url) { }; }; -WispNetworkAdapter.prototype.send_packet = function (data, type, stream_id) { +WispNetworkAdapter.prototype.send_packet = function(data, type, stream_id) { if(this.connections[stream_id].congestion > 0) { if(type === "DATA") { this.connections[stream_id].congestion--; @@ -60,7 +61,7 @@ WispNetworkAdapter.prototype.send_packet = function (data, type, stream_id) { } }; -WispNetworkAdapter.prototype.process_incoming_wisp_frame = function (frame) { +WispNetworkAdapter.prototype.process_incoming_wisp_frame = function(frame) { const view = new DataView(frame.buffer); const stream_id = view.getUint32(1, true); switch(frame[0]) { @@ -73,8 +74,6 @@ WispNetworkAdapter.prototype.process_incoming_wisp_frame = function (frame) { this.connections[stream_id].data_callback(frame.slice(5)); else throw new Error("Got a DATA packet but stream not registered. ID: " + stream_id); - - break; case 3: // CONTINUE if(this.connections[stream_id]) { @@ -87,7 +86,6 @@ WispNetworkAdapter.prototype.process_incoming_wisp_frame = function (frame) { } this.connections[stream_id].congested = false; } - break; case 4: // CLOSE if(this.connections[stream_id]) @@ -122,7 +120,7 @@ WispNetworkAdapter.prototype.process_incoming_wisp_frame = function (frame) { // // -WispNetworkAdapter.prototype.send_wisp_frame = function (frame_obj) { +WispNetworkAdapter.prototype.send_wisp_frame = function(frame_obj) { let full_packet; let view; switch(frame_obj.type) { @@ -142,17 +140,13 @@ WispNetworkAdapter.prototype.send_wisp_frame = function (frame_obj) { close_callback: frame_obj.close_callback, congestion: this.connections[0].congestion }; - - break; case "DATA": - full_packet = new Uint8Array(5 + frame_obj.data.length); view = new DataView(full_packet.buffer); view.setUint8(0, 0x02); // TYPE view.setUint32(1, frame_obj.stream_id, true); // Stream ID full_packet.set(frame_obj.data, 5); // Actual data - break; case "CLOSE": full_packet = new Uint8Array(5 + 1); @@ -160,7 +154,6 @@ WispNetworkAdapter.prototype.send_wisp_frame = function (frame_obj) { view.setUint8(0, 0x04); // TYPE view.setUint32(1, frame_obj.stream_id, true); // Stream ID view.setUint8(5, frame_obj.reason); // Packet size - break; default: dbg_log("Client tried to send unknown packet: " + frame_obj.type, LOG_NET); @@ -175,6 +168,7 @@ WispNetworkAdapter.prototype.destroy = function() this.wispws.onmessage = null; this.wispws.onclose = null; this.wispws.close(); + this.wispws = null; } }; @@ -202,7 +196,6 @@ WispNetworkAdapter.prototype.send = function(data) packet.tcp.dport ].join(":"); - if(packet.tcp.syn) { if(this.tcp_conn[tuple]) { dbg_log("SYN to already opened port", LOG_FETCH); @@ -211,7 +204,6 @@ WispNetworkAdapter.prototype.send = function(data) tcp_conn.state = TCP_STATE_SYN_RECEIVED; tcp_conn.net = this; - tcp_conn.send_wisp_frame = this.send_wisp_frame; tcp_conn.tuple = tuple; tcp_conn.stream_id = this.last_stream++; this.tcp_conn[tuple] = tcp_conn; @@ -240,7 +232,6 @@ WispNetworkAdapter.prototype.send = function(data) }); tcp_conn.accept(packet); - return; } @@ -269,6 +260,7 @@ WispNetworkAdapter.prototype.send = function(data) } if(packet.dns) { + // TODO: remove when this wisp client supports udp (async () => { let reply = {}; reply.eth = { ethertype: ETHERTYPE_IPV4, src: this.router_mac, dest: packet.eth.src }; @@ -281,28 +273,22 @@ WispNetworkAdapter.prototype.send = function(data) const result = await ((await fetch(`https://${this.doh_server}/dns-query`, {method: "POST", headers: [["content-type", "application/dns-message"]], body: packet.udp.data})).arrayBuffer()); reply.udp.data = new Uint8Array(result); this.receive(make_packet(reply)); - })(); - } if(packet.ntp) { + // TODO: remove when this wisp client supports udp handle_fake_ntp(packet, this); return; } - // ICMP Ping - if(packet.icmp && packet.icmp.type === 8) { - handle_fake_ping(packet, this); - return; - } - if(packet.dhcp) { handle_fake_dhcp(packet, this); return; } if(packet.udp && packet.udp.dport === 8) { + // TODO: remove when this wisp client supports udp handle_udp_echo(packet, this); } }; diff --git a/tests/devices/wisp_network.js b/tests/devices/wisp_network.js index 6d2bfbdb93..e0f4d90a23 100755 --- a/tests/devices/wisp_network.js +++ b/tests/devices/wisp_network.js @@ -70,34 +70,20 @@ const tests = assert(/192.168.86.1/.test(capture), "192.168.86.100"); }, }, - { - name: "ping 1.2.3.4", - timeout: 60, - start: () => - { - emulator.serial0_send("ping -c 2 1.2.3.4\n"); - emulator.serial0_send("echo -e done\\\\tping\n"); - }, - end_trigger: "done\tping", - end: (capture) => - { - assert(/2 packets transmitted, 2 packets received, 0% packet loss/.test(capture), "2 packets transmitted, 2 packets received, 0% packet loss"); - }, - }, - { - name: "arp -a", - timeout: 60, - start: () => - { - emulator.serial0_send("arp -a\n"); - emulator.serial0_send("echo -e done\\\\tarp\n"); - }, - end_trigger: "done\tarp", - end: (capture) => - { - assert(/.192.168.86.1. at 52:54:00:01:02:03 \[ether\] {2}on eth0/.test(capture), "(192.168.86.1) at 52:54:00:01:02:03 [ether] on eth0"); - }, - }, + //{ + // name: "arp -a", + // timeout: 60, + // start: () => + // { + // emulator.serial0_send("arp -a\n"); + // emulator.serial0_send("echo -e done\\\\tarp\n"); + // }, + // end_trigger: "done\tarp", + // end: (capture) => + // { + // assert(/.192.168.86.1. at 52:54:00:01:02:03 \[ether\] {2}on eth0/.test(capture), "(192.168.86.1) at 52:54:00:01:02:03 [ether] on eth0"); + // }, + //}, { name: "Curl example.org", timeout: 60,