Skip to content

Commit

Permalink
WIP wisp support
Browse files Browse the repository at this point in the history
  • Loading branch information
ProgrammerIn-wonderland committed Jul 28, 2024
1 parent 0dbfa1b commit 2ac0d4a
Show file tree
Hide file tree
Showing 4 changed files with 1,526 additions and 108 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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-filer.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 \
fetch_network.js print_stats.js filestorage.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 \
Expand Down
231 changes: 125 additions & 106 deletions src/browser/fetch_network.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
"use strict";



// https://www.iana.org/assignments/ieee-802-numbers/ieee-802-numbers.xhtml
const ETHERTYPE_IPV4 = 0x0800;
const ETHERTYPE_ARP = 0x0806;
Expand Down Expand Up @@ -69,35 +67,6 @@ FetchNetworkAdapter.prototype.destroy = function()
{
};

// https://stackoverflow.com/questions/4460586/javascript-regular-expression-to-check-for-ip-addresses
function validateIPaddress(ipaddress) {
if (/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(ipaddress)) {
return true;
}
return false;
}

// DNS over HTTPS fetch, recursively fetch the A record until the first result is an IPv4
async function dohdns(q) {
const preffered_fetch = (window.anura?.net?.fetch) || fetch;
const req = await preffered_fetch(`https://dns.google/resolve?name=${q.name.join(".")}&type=${q.type}`);
if (req.status == 200) {
const res = await req.json();
if (res.Answer) {
if (validateIPaddress(res.Answer[0].data)) {
return res;
} else {
return await dohdns({name: res.Answer[0].data.split("."), type: q.type});
}
}
return
} else {
throw new Error("DNS Server returned error code");
}



}

function siptolong(s) {
let parts = s.split(".").map(function(x) { return parseInt(x, 10); });
Expand All @@ -108,13 +77,40 @@ function iptolong(parts) {
return parts[0] << 24 | parts[1] << 16 | parts[2] << 8 | parts[3];
}

FetchNetworkAdapter.prototype.fetch = async function(url, options)
{
if(this.cors_proxy) url = this.cors_proxy + encodeURIComponent(url);

try
{
const resp = await fetch(url, options);
const ab = await resp.arrayBuffer();
return [resp, ab];
}
catch(e)
{
console.warn("Fetch Failed: " + url + "\n" + e);
let headers = new Headers();
headers.set("Content-Type", "text/plain");
return [
{
status: 502,
statusText: "Fetch Error",
headers: headers,
},
new TextEncoder().encode(`Fetch ${url} failed:\n\n${e.stack}`).buffer
];
}
};

/**
* @param {Uint8Array} data
*/
FetchNetworkAdapter.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 };
Expand All @@ -132,28 +128,16 @@ FetchNetworkAdapter.prototype.send = function(data)
].join(":");


if(packet.tcp.syn) {
if(packet.tcp.syn && packet.tcp.dport === 80) {
if(this.tcp_conn[tuple]) {
dbg_log("SYN to already opened port", LOG_FETCH);
}
const conn = new WebSocket(window.origin.replace("https://", "wss://").replace("http://", "ws://") + "/" + packet.ipv4.dest.join(".") + ":" + packet.tcp.dport) // TODO, replace wsproxy with wisp
conn.binaryType = "arraybuffer";

this.tcp_conn[tuple] = new TCPConnection();
this.tcp_conn[tuple].state = TCP_STATE_SYN_RECEIVED;
this.tcp_conn[tuple].net = this;
this.tcp_conn[tuple].on_data = TCPConnection.prototype.on_data_wisp;
this.tcp_conn[tuple].on_data = TCPConnection.prototype.on_data_http;
this.tcp_conn[tuple].tuple = tuple;
this.tcp_conn[tuple].ws = conn;
const deref = this.tcp_conn[tuple];
this.tcp_conn[tuple].ws.onopen = () => {
deref.accept(packet);
};

this.tcp_conn[tuple].ws.onmessage = (event) => {
deref.write(new Uint8Array(event.data));
};

this.tcp_conn[tuple].accept(packet);
return;
}

Expand Down Expand Up @@ -200,62 +184,48 @@ FetchNetworkAdapter.prototype.send = function(data)
tpa: packet.arp.spa
};
this.receive(make_packet(reply));

}

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 };

let answers = [];
let flags = 0x8000; //Response,
flags |= 0x0180; // Recursion
// flags |= 0x0400; Authoritative
for(let i = 0; i < packet.dns.questions.length; ++i) {
let q = packet.dns.questions[i];

// its actually fine if this errors and it isn't handled, its run as an async function so the error shouldn't take down the whole thing. also its a UDP packet so we dont have to maintain "diplomacy" by responding to the VM, we can ghost it like my ex did to me in 8th grade
const res = await dohdns(q);

switch(q.type){
case 1: // A recrod

// for (const ans in res.Answer) { // v86 DNS server crashes and burns with multiple answers, not quite sure why
if (res.Answer) {
const ans = res.Answer[0];
answers.push({
name: ans.name.split("."),
type: ans.type,
class: q.class,
ttl: ans.TTL,
data: ans.data.split(".")
});
}

// }

break;
default:
}
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 };

let answers = [];
let flags = 0x8000; //Response,
flags |= 0x0180; // Recursion
// flags |= 0x0400; Authoritative

for(let i = 0; i < packet.dns.questions.length; ++i) {
let q = packet.dns.questions[i];

switch(q.type){
case 1: // A recrod
answers.push({
name: q.name,
type: q.type,
class: q.class,
ttl: 600,
data: [192, 168, 87, 1]
});
break;
default:
}

reply.dns = {
id: packet.dns.id,
flags: flags,
questions: packet.dns.questions,
answers: answers
};
this.receive(make_packet(reply));
return;
})();

}

reply.dns = {
id: packet.dns.id,
flags: flags,
questions: packet.dns.questions,
answers: answers
};
this.receive(make_packet(reply));
return;
}

if(packet.ntp) {
Expand Down Expand Up @@ -1190,13 +1160,62 @@ TCPConnection.prototype.process = function(packet) {
this.pump();
};

/**
*
* @param {Uint8Array} data
*/
TCPConnection.prototype.on_data_wisp = async function(data) {
if (data.length !== 0)
this.ws.send(data);
TCPConnection.prototype.on_data_http = async function(data) {
this.read = this.read || "";
this.read += new TextDecoder().decode(data);
if(this.read && this.read.indexOf("\r\n\r\n") !== -1) {
let offset = this.read.indexOf("\r\n\r\n");
let headers = this.read.substring(0, offset).split(/\r\n/);
let data = this.read.substring(offset + 4);
this.read = "";

let first_line = headers[0].split(" ");
let target = new URL("http://host" + first_line[1]);
if(/^https?:/.test(first_line[1])) {
target = new URL(first_line[1]);
}
let req_headers = new Headers();
for(let i = 1; i < headers.length; ++i) {
let parts = headers[i].split(": ");
let key = parts[0].toLowerCase();
let value = parts[1];
if( key === "host" ) target.host = value;
else if( key.length > 1 ) req_headers.set(parts[0], value);
}

dbg_log("HTTP Dispatch: " + target.href, LOG_FETCH);
this.name = target.href;
let opts = {
method: first_line[0],
headers: req_headers,
};
if(["put", "post"].indexOf(opts.method.toLowerCase()) !== -1) {
opts.body = data;
}
const [resp, ab] = await this.net.fetch(target.href, opts);
const lines = [
`HTTP/1.1 ${resp.status} ${resp.statusText}`,
"connection: Closed",
"content-length: " + ab.byteLength
];

lines.push("x-was-fetch-redirected: " + String(resp.redirected));
lines.push("x-fetch-resp-url: " + String(resp.url));

resp.headers.forEach(function (value, key) {
if([
"content-encoding", "connection", "content-length", "transfer-encoding"
].indexOf(key.toLowerCase()) === -1 ) {
lines.push(key + ": " + value);
}
});

lines.push("");
lines.push("");

this.write(new TextEncoder().encode(lines.join("\r\n")));
this.write(new Uint8Array(ab));
}
};

/**
Expand Down Expand Up @@ -1240,4 +1259,4 @@ TCPConnection.prototype.pump = function() {
if(typeof module !== "undefined" && typeof module.exports !== "undefined")
{
module.exports["FetchNetworkAdapter"] = FetchNetworkAdapter;
}
}
5 changes: 4 additions & 1 deletion src/browser/starter.js
Original file line number Diff line number Diff line change
Expand Up @@ -296,8 +296,11 @@ V86.prototype.continue_init = async function(emulator, options)
if(options.network_relay_url === "fetch")
{
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);
}
else
else
{
this.network_adapter = new NetworkAdapter(options.network_relay_url, this.bus);
}
Expand Down
Loading

0 comments on commit 2ac0d4a

Please sign in to comment.