From c08041ac72eadc44ef4d7ca49d42610ad440bda5 Mon Sep 17 00:00:00 2001 From: Rob Blanckaert Date: Mon, 3 Feb 2025 13:10:10 -0800 Subject: [PATCH] Fake Networking TCP Improvements - No need for handle now tha we have TCPConnection - Add a function to probe if a port is open on the VM without finishing the three way TCP handshake. --- src/browser/fake_network.js | 63 ++++++++++++++++++++-------------- src/browser/fetch_network.js | 7 +++- src/browser/wisp_network.js | 4 +-- tests/devices/fetch_network.js | 5 +++ 4 files changed, 51 insertions(+), 28 deletions(-) diff --git a/src/browser/fake_network.js b/src/browser/fake_network.js index 6b92a1fca9..eee37e6c26 100644 --- a/src/browser/fake_network.js +++ b/src/browser/fake_network.js @@ -26,6 +26,7 @@ const V86_ASCII = [118, 56, 54]; const TCP_STATE_CLOSED = "closed"; const TCP_STATE_SYN_RECEIVED = "syn-received"; const TCP_STATE_SYN_SENT = "syn-sent"; +const TCP_STATE_SYN_PROBE = "syn-probe"; //const TCP_STATE_LISTEN = "listen"; const TCP_STATE_ESTABLISHED = "established"; const TCP_STATE_FIN_WAIT_1 = "fin-wait-1"; @@ -986,16 +987,8 @@ function fake_tcp_connect(dport, adapter) if(adapter.tcp_conn[tuple]) { throw new Error("pool of dynamic TCP port numbers exhausted, connection aborted"); } - let on_data; - let on_connect; - let on_close; - let on_shutdown; + let conn = new TCPConnection(); - conn.net = adapter; - conn.on_data = function(data) { if(on_data) on_data.call(handle, data); }; - conn.on_connect = function() { if(on_connect) on_connect.call(handle); }; - conn.on_close = function() { if(on_close) on_close.call(handle); }; - conn.on_shutdown = function() { if(on_shutdown) on_shutdown.call(handle); }; conn.tuple = tuple; conn.hsrc = adapter.router_mac; @@ -1004,20 +997,18 @@ function fake_tcp_connect(dport, adapter) conn.hdest = adapter.vm_mac; conn.dport = dport; conn.pdest = adapter.vm_ip; + conn.net = adapter; adapter.tcp_conn[tuple] = conn; conn.connect(); - // TODO: Real event source - let handle = { - write: function(data) { conn.write(data); }, - on: function(event, cb) { - if(event === "data") on_data = cb; - if(event === "connect") on_connect = cb; - if(event === "close") on_close = cb; - if(event === "shutdown") on_shutdown = cb; - }, - close: function() { conn.close(); } - }; - return handle; + return conn; +} + +function fake_tcp_probe(dport, adapter) { + return new Promise((res, rej) => { + let handle = fake_tcp_connect(dport, adapter); + handle.state = TCP_STATE_SYN_PROBE; + handle.on("probe", res); + }); } /** @@ -1032,8 +1023,19 @@ function TCPConnection() this.in_active_close = false; this.delayed_send_fin = false; this.delayed_state = undefined; + this.events_handlers = {}; } +TCPConnection.prototype.on = function(event, handler) { + this.events_handlers[event] = handler; +}; + +TCPConnection.prototype.emit = function(event, ...args) { + if(!this.events_handlers[event]) return; + this.events_handlers[event].apply(this, args); +}; + + TCPConnection.prototype.ipv4_reply = function() { let reply = {}; reply.eth = { ethertype: ETHERTYPE_IPV4, src: this.hsrc, dest: this.hdest }; @@ -1129,6 +1131,11 @@ TCPConnection.prototype.process = function(packet) { return; } else if(packet.tcp.rst) { + if(this.state === TCP_STATE_SYN_PROBE) { + this.emit("probe", false); + this.release(); + return; + } // dbg_log(`TCP[${this.tuple}]: received RST in state "${this.state}"`, LOG_FETCH); this.on_close(); this.release(); @@ -1144,9 +1151,13 @@ TCPConnection.prototype.process = function(packet) { this.net.receive(make_packet(this.net.eth_encoder_buf, reply)); // dbg_log(`TCP[${this.tuple}]: received SYN+ACK in state "${this.state}", next "${TCP_STATE_ESTABLISHED}"`, LOG_FETCH); this.state = TCP_STATE_ESTABLISHED; - if(this.on_connect) { - this.on_connect.call(this); - } + this.emit("connect"); + } + else if(this.state === TCP_STATE_SYN_PROBE && packet.tcp.ack) { + this.emit("probe", true); + const reply = this.packet_reply(packet, {rst: true}); + this.net.receive(make_packet(this.net.eth_encoder_buf, reply)); + this.release(); } else { dbg_log(`TCP[${this.tuple}]: WARNING: unexpected SYN packet dropped`, LOG_FETCH); @@ -1262,7 +1273,7 @@ TCPConnection.prototype.process = function(packet) { this.ack += packet.tcp_data.length; const reply = this.ipv4_reply(); this.net.receive(make_packet(this.net.eth_encoder_buf, reply)); - this.on_data(packet.tcp_data); + this.emit("data", packet.tcp_data); } this.pump(); @@ -1325,10 +1336,12 @@ TCPConnection.prototype.close = function() { }; TCPConnection.prototype.on_shutdown = function() { + this.emit("shutdown"); // forward FIN event from guest device to network adapter }; TCPConnection.prototype.on_close = function() { + this.emit("close"); // forward RST event from guest device to network adapter }; diff --git a/src/browser/fetch_network.js b/src/browser/fetch_network.js index 93cdbba2b5..65d74bfd6a 100644 --- a/src/browser/fetch_network.js +++ b/src/browser/fetch_network.js @@ -47,7 +47,7 @@ FetchNetworkAdapter.prototype.on_tcp_connection = function(packet, tuple) let conn = new TCPConnection(); conn.state = TCP_STATE_SYN_RECEIVED; conn.net = this; - conn.on_data = on_data_http; + conn.on("data", on_data_http); conn.tuple = tuple; conn.accept(packet); this.tcp_conn[tuple] = conn; @@ -61,6 +61,11 @@ FetchNetworkAdapter.prototype.connect = function(port) return fake_tcp_connect(port, this); }; +FetchNetworkAdapter.prototype.tcp_probe = function(port) +{ + return fake_tcp_probe(port, this); +}; + /** * @this {TCPConnection} * @param {!ArrayBuffer} data diff --git a/src/browser/wisp_network.js b/src/browser/wisp_network.js index 4bb6386449..2d07783f39 100644 --- a/src/browser/wisp_network.js +++ b/src/browser/wisp_network.js @@ -186,7 +186,7 @@ WispNetworkAdapter.prototype.on_tcp_connection = function(packet, tuple) conn.stream_id = this.last_stream++; this.tcp_conn[tuple] = conn; - conn.on_data = (data) => { + conn.on("data", data => { if(data.length !== 0) { this.send_wisp_frame({ type: "DATA", @@ -194,7 +194,7 @@ WispNetworkAdapter.prototype.on_tcp_connection = function(packet, tuple) data: data }); } - }; + }); conn.on_close = () => { this.send_wisp_frame({ diff --git a/tests/devices/fetch_network.js b/tests/devices/fetch_network.js index f0ddf7531f..e5ebbb2551 100755 --- a/tests/devices/fetch_network.js +++ b/tests/devices/fetch_network.js @@ -107,8 +107,13 @@ const tests = allow_failure: true, start: async () => { + let open = await emulator.network_adapter.tcp_probe(80); + assert(!open, "Probe shows port not open"); emulator.serial0_send("echo -n hello | socat TCP4-LISTEN:80 - && echo -e done\\\\tlisten\n"); await wait(1000); + open = await emulator.network_adapter.tcp_probe(80); + assert(open, "Probe shows port open, but does not show as a connection"); + await wait(1000); let h = emulator.network_adapter.connect(80); h.on("connect", () => { h.write(new TextEncoder().encode("From VM: "));