tcpip 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4; var _class5; var _class6; var _class7; var _class8; var _class9; var _class10;var Y=(o,e)=>(e=Symbol[o])?e:Symbol.for("Symbol."+o),J=o=>{throw TypeError(o)};var p=(o,e,r)=>{if(e!=null){typeof e!="object"&&typeof e!="function"&&J("Object expected");var t,n;r&&(t=e[Y("asyncDispose")]),t===void 0&&(t=e[Y("dispose")],r&&(n=t)),typeof t!="function"&&J("Object not disposable"),n&&(t=function(){try{n.call(this)}catch(s){return Promise.reject(s)}}),o.push([r,t,e])}else r&&o.push([r]);return e},f=(o,e,r)=>{var t=typeof SuppressedError=="function"?SuppressedError:function(a,i,c,d){return d=Error(c),d.name="SuppressedError",d.error=a,d.suppressed=i,d},n=a=>e=r?new t(a,e,"An error was suppressed during disposal"):(r=!0,a),s=a=>{for(;a=o.pop();)try{var i=a[1]&&a[1].call(a[2]);if(a[0])return Promise.resolve(i).then(s,c=>(n(c),s()))}catch(c){n(c)}if(r)throw e};return s()};var y="dispose"in Symbol?Symbol.dispose:Symbol.for("Symbol.dispose");var _browser_wasi_shim = require('@bjorn3/browser_wasi_shim');var _dns = require('@tcpip/dns');var _wire = require('@tcpip/wire');var l=class{#e=new WeakMap;#t=new WeakMap;setOuter(e,r){this.#e.set(e,r)}setInner(e,r){this.#t.set(e,r)}getOuter(e){let r=this.#e.get(e);if(!r)throw new Error(`outer hooks not set for ${e}`);return r}getInner(e){let r=this.#t.get(e);if(!r)throw new Error(`inner hooks not set for ${e}`);return r}},H=class extends Number{constructor(e,r){super(e),this.free=r}[y](){this.free(this.valueOf())}},P=class extends Map{#e=new Map;wait(e){return new Promise(r=>{let t=_nullishCoalesce(this.#e.get(e), () => (new Set));t.add(r),this.#e.set(e,t)})}set(e,r){super.set(e,r);let t=this.#e.get(e);if(t)for(let n of t)n(r),t.delete(n);return this}};function w(o,e){let r=o.getReader();return ce(r,e)}async function*ce(o,e){try{for(;;){let{done:r,value:t}=await o.read();if(r)return t;yield t}}finally{_optionalChain([e, 'optionalAccess', _2 => _2.preventCancel])||await o.cancel(),o.releaseLock()}}var T=class extends ReadableStream{#e;constructor({lock:e,...r},t){super(r,t),this.#e=e}getReader(){let e=super.getReader();return this.locked&&_optionalChain([this, 'access', _3 => _3.#e, 'optionalCall', _4 => _4()]),e}pipeThrough(e,r){let t=super.pipeThrough(e,r);return this.locked&&_optionalChain([this, 'access', _5 => _5.#e, 'optionalCall', _6 => _6()]),t}pipeTo(e,r){let t=super.pipeTo(e,r);return this.locked&&_optionalChain([this, 'access', _7 => _7.#e, 'optionalCall', _8 => _8()]),t}tee(){let[e,r]=super.tee();return this.locked&&_optionalChain([this, 'access', _9 => _9.#e, 'optionalCall', _10 => _10()]),[e,r]}};async function g(){return await new Promise(o=>queueMicrotask(o))}function O(){let o=new Uint8Array(6);return crypto.getRandomValues(o),o[0]=o[0]&252|2,o}var u=class{#e;get exports(){if(!this.#e)throw new Error("exports were not registered");return this.#e}register(e){this.#e=e}smartMalloc(e){return new H(this.exports.malloc(e),this.exports.free)}copyToMemory(e){let r=new Uint8Array(e),t=r.length,n=this.smartMalloc(t);return new Uint8Array(this.exports.memory.buffer,n.valueOf(),t).set(r),n}copyFromMemory(e,r){let t=this.exports.memory.buffer.slice(Number(e),Number(e)+r);return new Uint8Array(t)}viewFromMemory(e,r){return new Uint8Array(this.exports.memory.buffer,Number(e),r)}};var A={ERR_OK:0,ERR_MEM:-1,ERR_BUF:-2,ERR_TIMEOUT:-3,ERR_RTE:-4,ERR_INPROGRESS:-5,ERR_VAL:-6,ERR_WOULDBLOCK:-7,ERR_USE:-8,ERR_ALREADY:-9,ERR_ISCONN:-10,ERR_CONN:-11,ERR_IF:-12,ERR_ABRT:-13,ERR_RST:-14,ERR_CLSD:-15,ERR_ARG:-16};var k=new l,U= (_class =class extends u{constructor(...args) { super(...args); _class.prototype.__init.call(this);_class.prototype.__init2.call(this); }__init() {this.interfaces=new Map}__init2() {this.imports={register_tap_interface:e=>{let r=new j;k.setOuter(r,{handle:e,sendFrame:t=>{let n=this.copyToMemory(t),s=this.exports.send_tap_interface(e,n,t.length);if(s!==A.ERR_OK)throw new Error(`failed to send frame: ${s}`)},getMacAddress:()=>{let t=this.exports.get_interface_mac_address(e),n=this.viewFromMemory(t,6);return _wire.parseMacAddress.call(void 0, n)},getIPv4Address:()=>{let t=this.exports.get_interface_ip4_address(e);if(t===0)return;let n=this.viewFromMemory(t,4);return _wire.parseIPv4Address.call(void 0, n)},getIPv4Netmask:()=>{let t=this.exports.get_interface_ip4_netmask(e);if(t===0)return;let n=this.viewFromMemory(t,4);return _wire.parseIPv4Address.call(void 0, n)}}),this.interfaces.set(e,r)},receive_frame:async(e,r,t)=>{let n=this.copyFromMemory(r,t);await g();let s=this.interfaces.get(e);if(!s){console.error("received frame on unknown tap interface");return}k.getInner(s).receiveFrame(new Uint8Array(n))}}}async create(e){var m=[];try{let r=e.mac?_wire.serializeMacAddress.call(void 0, e.mac):O();let{ipAddress:t,netmask:n}=e.ip?_wire.serializeIPv4Cidr.call(void 0, e.ip):{};let s=p(m,this.copyToMemory(r));let a=p(m,t?this.copyToMemory(t):void 0);let i=p(m,n?this.copyToMemory(n):void 0);let c=this.exports.create_tap_interface(s,_nullishCoalesce(a, () => (0)),_nullishCoalesce(i, () => (0)));let d=this.interfaces.get(c);if(!d)throw new Error("tap interface failed to register");return d}catch(h){var b=h,_=!0}finally{f(m,b,_)}}async remove(e){for(let[r,t]of this.interfaces.entries())if(t===e){this.exports.remove_tap_interface(r),this.interfaces.delete(r);return}}}, _class),j= (_class2 =class{#e;#t=!1;__init3() {this.type="tap"}get mac(){return k.getOuter(this).getMacAddress()}get ip(){return k.getOuter(this).getIPv4Address()}get netmask(){return k.getOuter(this).getIPv4Netmask()}constructor(){;_class2.prototype.__init3.call(this);k.setInner(this,{receiveFrame:async e=>{if(this.#t){if(!this.#e)throw new Error("readable stream not initialized");this.#e.enqueue(e)}}}),this.readable=new T({start:e=>{this.#e=e},lock:()=>{this.#t=!0}}),this.writable=new WritableStream({write:e=>{try{k.getOuter(this).sendFrame(e)}catch(r){console.error("tap interface send failed",r)}}})}listen(){if(this.readable.locked)throw new Error("readable stream already locked");return w(this.readable)}[Symbol.asyncIterator](){return this.listen()}}, _class2);var E=new l,C= (_class3 =class extends u{constructor(...args2) { super(...args2); _class3.prototype.__init4.call(this);_class3.prototype.__init5.call(this); }__init4() {this.interfaces=new Map}__init5() {this.imports={}}async create(e){var b=[];try{let r=e.mac?_wire.serializeMacAddress.call(void 0, e.mac):O();let{ipAddress:t,netmask:n}=e.ip?_wire.serializeIPv4Cidr.call(void 0, e.ip):{};let s=p(b,this.copyToMemory(r));let a=p(b,t?this.copyToMemory(t):void 0);let i=p(b,n?this.copyToMemory(n):void 0);let c=new Uint32Array(e.ports.map(I=>Number(k.getOuter(I).handle)));let d=p(b,this.copyToMemory(c.buffer));let m=this.exports.create_bridge_interface(s,_nullishCoalesce(a, () => (0)),_nullishCoalesce(i, () => (0)),d,e.ports.length);let h=new K;E.setOuter(h,{handle:m,getMacAddress:()=>{let I=this.exports.get_interface_mac_address(m),S=this.viewFromMemory(I,6);return _wire.parseMacAddress.call(void 0, S)},getIPv4Address:()=>{let I=this.exports.get_interface_ip4_address(m);if(I===0)return;let S=this.viewFromMemory(I,4);return _wire.parseIPv4Address.call(void 0, S)},getIPv4Netmask:()=>{let I=this.exports.get_interface_ip4_netmask(m);if(I===0)return;let S=this.viewFromMemory(I,4);return _wire.parseIPv4Address.call(void 0, S)}});this.interfaces.set(m,h);return h}catch(_){var D=_,F=!0}finally{f(b,D,F)}}async remove(e){for(let[r,t]of this.interfaces.entries())if(t===e){this.exports.remove_bridge_interface(r),this.interfaces.delete(r);return}}}, _class3),K= (_class4 =class{constructor() { _class4.prototype.__init6.call(this); }__init6() {this.type="bridge"}get mac(){return E.getOuter(this).getMacAddress()}get ip(){return E.getOuter(this).getIPv4Address()}get netmask(){return E.getOuter(this).getIPv4Netmask()}}, _class4);var z=new l,M= (_class5 =class extends u{constructor(...args3) { super(...args3); _class5.prototype.__init7.call(this);_class5.prototype.__init8.call(this); }__init7() {this.interfaces=new Map}__init8() {this.imports={register_loopback_interface:e=>{let r=new q;z.setOuter(r,{handle:e,getIPv4Address:()=>{let t=this.exports.get_interface_ip4_address(e);if(t===0)return;let n=this.viewFromMemory(t,4);return _wire.parseIPv4Address.call(void 0, n)},getIPv4Netmask:()=>{let t=this.exports.get_interface_ip4_netmask(e);if(t===0)return;let n=this.viewFromMemory(t,4);return _wire.parseIPv4Address.call(void 0, n)}}),this.interfaces.set(e,r)}}}async create(e){var c=[];try{let{ipAddress:r,netmask:t}=e.ip?_wire.serializeIPv4Cidr.call(void 0, e.ip):{};let n=p(c,r?this.copyToMemory(r):void 0);let s=p(c,t?this.copyToMemory(t):void 0);let a=this.exports.create_loopback_interface(_nullishCoalesce(n, () => (0)),_nullishCoalesce(s, () => (0)));let i=this.interfaces.get(a);if(!i)throw new Error("loopback interface failed to register");return i}catch(d){var m=d,h=!0}finally{f(c,m,h)}}async remove(e){for(let[r,t]of this.interfaces.entries())if(t===e){this.exports.remove_loopback_interface(r),this.interfaces.delete(r);return}}}, _class5),q= (_class6 =class{constructor() { _class6.prototype.__init9.call(this); }__init9() {this.type="loopback"}get ip(){return z.getOuter(this).getIPv4Address()}get netmask(){return z.getOuter(this).getIPv4Netmask()}}, _class6);var $=new l,v=new l,Q=1460,ot=Q*4,at=Q*4,he=Q,L= (_class7 =class extends u{#e=new Map;#t=new P;#r=new Map;#n;async#s(e){try{return _wire.serializeIPv4Address.call(void 0, e)}catch (e2){let t=await this.#n.lookup(e);return _wire.serializeIPv4Address.call(void 0, t)}}constructor(e){super();_class7.prototype.__init10.call(this);,this.#n=e}__init10() {this.imports={accept_tcp_connection:async(e,r)=>{let t=this.#e.get(e);if(!t){console.error("new tcp connection to unknown listener");return}await g();let n=new B;v.setOuter(n,{send:async s=>{let a=Number(this.copyToMemory(s)),i=this.exports.send_tcp_chunk(r,a,s.length);for(;i<s.length;){await new Promise(d=>{this.#r.set(r,d)});let c=s.length-i;i+=this.exports.send_tcp_chunk(r,a+i,c)}},updateReceiveBuffer:s=>{this.exports.update_tcp_receive_buffer(r,s)},close:async()=>{let s=this.exports.close_tcp_connection(r);if(s!==A.ERR_OK)throw new Error(`failed to close tcp connection: ${s}`)}}),this.#t.set(r,n),$.getInner(t).accept(n)},connected_tcp_connection:async e=>{await g();let r=new B;v.setOuter(r,{send:async t=>{let n=Number(this.copyToMemory(t)),s=this.exports.send_tcp_chunk(e,n,t.length);for(;s<t.length;){await new Promise(i=>{this.#r.set(e,i)});let a=t.length-s;s+=this.exports.send_tcp_chunk(e,n+s,a)}},updateReceiveBuffer:t=>{this.exports.update_tcp_receive_buffer(e,t)},close:async()=>{this.exports.close_tcp_connection(e)}}),this.#t.set(e,r)},closed_tcp_connection:async e=>{let r=this.#t.get(e);if(!r){console.error("received close on unknown tcp connection");return}await v.getInner(r).close()},receive_tcp_chunk:async(e,r,t)=>{let n=this.copyFromMemory(r,t),s=this.#t.get(e);if(!s){console.error("received chunk on unknown tcp connection");return}await g(),v.getInner(s).receive(new Uint8Array(n))},sent_tcp_chunk:(e,r)=>{let t=this.#r.get(e);this.#r.delete(e),_optionalChain([t, 'optionalCall', _12 => _12(r)])}}}async listen(e){var s=[];try{let r=p(s,e.host?this.copyToMemory(await this.#s(e.host)):null);let t=this.exports.create_tcp_listener(r,e.port);let n=new G;$.setOuter(n,{});this.#e.set(t,n);return n}catch(a){var i=a,c=!0}finally{f(s,i,c)}}async connect(e){var s=[];try{let r=p(s,this.copyToMemory(await this.#s(e.host)));let t=this.exports.create_tcp_connection(r,e.port);let n=await this.#t.wait(t);if(!n)throw new Error("tcp failed to connect");return n}catch(a){var i=a,c=!0}finally{f(s,i,c)}}}, _class7),G=class{#e=[];#t;constructor(){$.setInner(this,{accept:async e=>{this.#e.push(e),_optionalChain([this, 'access', _13 => _13.#t, 'optionalCall', _14 => _14()])}})}async*[Symbol.asyncIterator](){for(;;)await new Promise(e=>{this.#t=e}),yield*this.#e,this.#e=[]}},B=class{#e=[];#t;#r;constructor(){v.setInner(this,{receive:async e=>{this.#e.push(e),this.#n()},close:async()=>{this.close()}}),this.readable=new ReadableStream({start:e=>{this.#t=e},pull:()=>{this.#n()}},{highWaterMark:he,size:e=>e.byteLength}),this.writable=new WritableStream({start:e=>{this.#r=e},write:async e=>{await v.getOuter(this).send(e)}},{highWaterMark:0})}#n(){if(!_optionalChain([this, 'access', _15 => _15.#t, 'optionalAccess', _16 => _16.desiredSize]))return;let e=0;for(;this.#e.length>0;){let r=this.#e[0].length;if(e+r>this.#t.desiredSize)break;let t=this.#e.shift();this.#t.enqueue(t),e+=t.length}e>0&&v.getOuter(this).updateReceiveBuffer(e)}async close(){await v.getOuter(this).close(),_optionalChain([this, 'access', _17 => _17.#t, 'optionalAccess', _18 => _18.error, 'call', _19 => _19(new Error("tcp connection closed"))]),_optionalChain([this, 'access', _20 => _20.#r, 'optionalAccess', _21 => _21.error, 'call', _22 => _22(new Error("tcp connection closed"))])}[Symbol.asyncIterator](){if(this.readable.locked)throw new Error("readable stream already locked");return w(this.readable)}};var x=new l,N= (_class8 =class extends u{constructor(...args4) { super(...args4); _class8.prototype.__init11.call(this);_class8.prototype.__init12.call(this); }__init11() {this.interfaces=new Map}__init12() {this.imports={register_tun_interface:e=>{let r=new Z;x.setOuter(r,{handle:e,sendPacket:t=>{let n=this.copyToMemory(t);this.exports.send_tun_interface(e,n,t.length)},getIPv4Address:()=>{let t=this.exports.get_interface_ip4_address(e);if(t===0)return;let n=this.viewFromMemory(t,4);return _wire.parseIPv4Address.call(void 0, n)},getIPv4Netmask:()=>{let t=this.exports.get_interface_ip4_netmask(e);if(t===0)return;let n=this.viewFromMemory(t,4);return _wire.parseIPv4Address.call(void 0, n)}}),this.interfaces.set(e,r)},receive_packet:async(e,r,t)=>{let n=this.copyFromMemory(r,t);await g();let s=this.interfaces.get(e);if(!s){console.error("received packet on unknown tun interface");return}x.getInner(s).receivePacket(new Uint8Array(n))}}}async create(e){var c=[];try{let{ipAddress:r,netmask:t}=e.ip?_wire.serializeIPv4Cidr.call(void 0, e.ip):{};let n=p(c,r?this.copyToMemory(r):void 0);let s=p(c,t?this.copyToMemory(t):void 0);let a=this.exports.create_tun_interface(_nullishCoalesce(n, () => (0)),_nullishCoalesce(s, () => (0)));let i=this.interfaces.get(a);if(!i)throw new Error("tun interface failed to register");return i}catch(d){var m=d,h=!0}finally{f(c,m,h)}}async remove(e){for(let[r,t]of this.interfaces.entries())if(t===e){this.exports.remove_tun_interface(r),this.interfaces.delete(r);return}}}, _class8),Z= (_class9 =class{#e;#t=!1;__init13() {this.type="tun"}get ip(){return x.getOuter(this).getIPv4Address()}get netmask(){return x.getOuter(this).getIPv4Netmask()}constructor(){;_class9.prototype.__init13.call(this);x.setInner(this,{receivePacket:async e=>{if(this.#t){if(!this.#e)throw new Error("readable stream not initialized");_optionalChain([this, 'access', _23 => _23.#e, 'optionalAccess', _24 => _24.enqueue, 'call', _25 => _25(e)])}}}),this.readable=new T({start:e=>{this.#e=e},lock:()=>{this.#t=!0}}),this.writable=new WritableStream({write:e=>{x.getOuter(this).sendPacket(e)}})}listen(){if(this.readable.locked)throw new Error("readable stream already locked");return w(this.readable)}[Symbol.asyncIterator](){return this.listen()}}, _class9);var R=new l,W= (_class10 =class extends u{#e=new P;#t;async#r(e){try{return _wire.serializeIPv4Address.call(void 0, e)}catch (e3){let t=await this.#t.lookup(e);return _wire.serializeIPv4Address.call(void 0, t)}}constructor(e){super();_class10.prototype.__init14.call(this);,this.#t=e}__init14() {this.imports={receive_udp_datagram:async(e,r,t,n,s)=>{let a=this.copyFromMemory(r,4),i=this.copyFromMemory(n,s),c=this.#e.get(e);if(!c){console.error("received datagram on unknown udp socket");return}await g(),R.getInner(c).receive({host:_wire.parseIPv4Address.call(void 0, a),port:t,data:i})}}}async open(e){var s=[];try{let r=p(s,e.host?this.copyToMemory(await this.#r(e.host)):null);let t=this.exports.open_udp_socket(r,_nullishCoalesce(e.port, () => (0)));if(Number(t)===0)throw new Error("failed to open udp socket");let n=new X;R.setOuter(n,{send:async d=>{var _=[];try{let m=p(_,this.copyToMemory(await this.#r(d.host)));let h=p(_,this.copyToMemory(d.data));let b=this.exports.send_udp_datagram(t,m,d.port,h,d.data.length);if(b!==A.ERR_OK)throw new Error(`failed to send udp datagram: ${b}`)}catch(D){var F=D,I=!0}finally{f(_,F,I)}},close:async()=>{this.exports.close_udp_socket(t),this.#e.delete(t)}});this.#e.set(t,n);return n}catch(a){var i=a,c=!0}finally{f(s,i,c)}}}, _class10),X=class{#e;#t;constructor(){R.setInner(this,{receive:async e=>{if(!this.#e)throw new Error("readable controller not initialized");this.#e.enqueue(e)}}),this.readable=new ReadableStream({start:e=>{this.#e=e}}),this.writable=new WritableStream({start:e=>{this.#t=e},write:async e=>{await R.getOuter(this).send(e)}})}async close(){await R.getOuter(this).close(),_optionalChain([this, 'access', _26 => _26.#e, 'optionalAccess', _27 => _27.error, 'call', _28 => _28(new Error("udp socket closed"))]),_optionalChain([this, 'access', _29 => _29.#t, 'optionalAccess', _30 => _30.error, 'call', _31 => _31(new Error("udp socket closed"))])}[Symbol.asyncIterator](){if(this.readable.locked)throw new Error("readable stream already locked");return w(this.readable)}};var ge=typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string";async function ae(o,e){return ge?ke(o,e):fetch(o)}async function ke(o,e){let r=await Promise.resolve().then(() => _interopRequireWildcard(require("fs"))),{Readable:t}=await Promise.resolve().then(() => _interopRequireWildcard(require("stream"))),n=r.createReadStream(o),s=t.toWeb(n);return new Response(s,{headers:{"Content-Type":e}})}async function Te(o){let e=new V(o);return await e.ready,e}var V=class{#e;#t;#r;#n;#s;#o;#a;#i;#c;get interfaces(){return this.#d()}constructor(e={}){this.#e={...e,initializeLoopback:_nullishCoalesce(e.initializeLoopback, () => (!0))},this.#r=new (0, _dns.DnsClient)(this,{nameServer:_nullishCoalesce(e.nameServer, () => ({ip:"127.0.0.1",port:53}))}),this.#n=new M,this.#s=new N,this.#o=new U,this.#a=new C,this.#i=new L(this.#r),this.#c=new W(this.#r),this.ready=this.#p(),this.ready.then(async()=>{this.#e.initializeLoopback&&await this.createLoopbackInterface({ip:"127.0.0.1/8"})})}async#p(){let e=new (0, _browser_wasi_shim.WASI)([],[],[new (0, _browser_wasi_shim.OpenFile)(new (0, _browser_wasi_shim.File)([])),_browser_wasi_shim.ConsoleStdout.lineBuffered(a=>console.log(`[WASI stdout] ${a}`)),_browser_wasi_shim.ConsoleStdout.lineBuffered(a=>console.warn(`[WASI stderr] ${a}`))]),r=ae(new URL("../tcpip.wasm",import.meta.url),"application/wasm"),{instance:t}=await WebAssembly.instantiateStreaming(r,{wasi_snapshot_preview1:e.wasiImport,env:{...this.#n.imports,...this.#s.imports,...this.#o.imports,...this.#a.imports,...this.#i.imports,...this.#c.imports}}),n=t;this.#n.register(n.exports),this.#s.register(n.exports),this.#o.register(n.exports),this.#a.register(n.exports),this.#i.register(n.exports),this.#c.register(n.exports);let s=e.start(n);if(s!==0)throw new Error(`wasi start failed with code ${s}`);this.#t=Number(setInterval(()=>{n.exports.process_queued_packets(),n.exports.process_timeouts()},100))}*#d(){yield*this.#n.interfaces.values(),yield*this.#s.interfaces.values(),yield*this.#o.interfaces.values(),yield*this.#a.interfaces.values()}async createLoopbackInterface(e){return await this.ready,this.#n.create(e)}async createTunInterface(e){return await this.ready,this.#s.create(e)}async createTapInterface(e={}){return await this.ready,this.#o.create(e)}async createBridgeInterface(e){return await this.ready,this.#a.create(e)}async removeInterface(e){switch(await this.ready,e.type){case"loopback":return this.#n.remove(e);case"tun":return this.#s.remove(e);case"tap":return this.#o.remove(e);case"bridge":return this.#a.remove(e);default:throw new Error("unknown interface type")}}async listenTcp(e){return await this.ready,this.#i.listen(e)}async connectTcp(e){return await this.ready,this.#i.connect(e)}async openUdp(e={}){return await this.ready,this.#c.open(e)}};exports.createStack = Te;
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4; var _class5; var _class6; var _class7; var _class8; var _class9; var _class10;var Y=(o,e)=>(e=Symbol[o])?e:Symbol.for("Symbol."+o),J=o=>{throw TypeError(o)};var p=(o,e,r)=>{if(e!=null){typeof e!="object"&&typeof e!="function"&&J("Object expected");var t,n;r&&(t=e[Y("asyncDispose")]),t===void 0&&(t=e[Y("dispose")],r&&(n=t)),typeof t!="function"&&J("Object not disposable"),n&&(t=function(){try{n.call(this)}catch(s){return Promise.reject(s)}}),o.push([r,t,e])}else r&&o.push([r]);return e},f=(o,e,r)=>{var t=typeof SuppressedError=="function"?SuppressedError:function(a,i,c,d){return d=Error(c),d.name="SuppressedError",d.error=a,d.suppressed=i,d},n=a=>e=r?new t(a,e,"An error was suppressed during disposal"):(r=!0,a),s=a=>{for(;a=o.pop();)try{var i=a[1]&&a[1].call(a[2]);if(a[0])return Promise.resolve(i).then(s,c=>(n(c),s()))}catch(c){n(c)}if(r)throw e};return s()};var y="dispose"in Symbol?Symbol.dispose:Symbol.for("Symbol.dispose");var _browser_wasi_shim = require('@bjorn3/browser_wasi_shim');var _dns = require('@tcpip/dns');var _wire = require('@tcpip/wire');var l=class{#e=new WeakMap;#t=new WeakMap;setOuter(e,r){this.#e.set(e,r)}setInner(e,r){this.#t.set(e,r)}getOuter(e){let r=this.#e.get(e);if(!r)throw new Error(`outer hooks not set for ${e}`);return r}getInner(e){let r=this.#t.get(e);if(!r)throw new Error(`inner hooks not set for ${e}`);return r}},R=class extends Number{constructor(e,r){super(e),this.free=r}[y](){this.free(this.valueOf())}},P=class extends Map{#e=new Map;wait(e){return new Promise(r=>{let t=_nullishCoalesce(this.#e.get(e), () => (new Set));t.add(r),this.#e.set(e,t)})}set(e,r){super.set(e,r);let t=this.#e.get(e);if(t)for(let n of t)n(r),t.delete(n);return this}};function w(o,e){let r=o.getReader();return ie(r,e)}async function*ie(o,e){try{for(;;){let{done:r,value:t}=await o.read();if(r)return t;yield t}}finally{_optionalChain([e, 'optionalAccess', _2 => _2.preventCancel])||await o.cancel(),o.releaseLock()}}var T=class extends ReadableStream{#e;constructor({lock:e,...r},t){super(r,t),this.#e=e}getReader(){let e=super.getReader();return this.locked&&_optionalChain([this, 'access', _3 => _3.#e, 'optionalCall', _4 => _4()]),e}pipeThrough(e,r){let t=super.pipeThrough(e,r);return this.locked&&_optionalChain([this, 'access', _5 => _5.#e, 'optionalCall', _6 => _6()]),t}pipeTo(e,r){let t=super.pipeTo(e,r);return this.locked&&_optionalChain([this, 'access', _7 => _7.#e, 'optionalCall', _8 => _8()]),t}tee(){let[e,r]=super.tee();return this.locked&&_optionalChain([this, 'access', _9 => _9.#e, 'optionalCall', _10 => _10()]),[e,r]}};async function g(){return await new Promise(o=>queueMicrotask(o))}var u=class{#e;get exports(){if(!this.#e)throw new Error("exports were not registered");return this.#e}register(e){this.#e=e}smartMalloc(e){return new R(this.exports.malloc(e),this.exports.free)}copyToMemory(e){let r=new Uint8Array(e),t=r.length,n=this.smartMalloc(t);return new Uint8Array(this.exports.memory.buffer,n.valueOf(),t).set(r),n}copyFromMemory(e,r){let t=this.exports.memory.buffer.slice(Number(e),Number(e)+r);return new Uint8Array(t)}viewFromMemory(e,r){return new Uint8Array(this.exports.memory.buffer,Number(e),r)}};var A={ERR_OK:0,ERR_MEM:-1,ERR_BUF:-2,ERR_TIMEOUT:-3,ERR_RTE:-4,ERR_INPROGRESS:-5,ERR_VAL:-6,ERR_WOULDBLOCK:-7,ERR_USE:-8,ERR_ALREADY:-9,ERR_ISCONN:-10,ERR_CONN:-11,ERR_IF:-12,ERR_ABRT:-13,ERR_RST:-14,ERR_CLSD:-15,ERR_ARG:-16};var k=new l,O= (_class =class extends u{constructor(...args) { super(...args); _class.prototype.__init.call(this);_class.prototype.__init2.call(this); }__init() {this.interfaces=new Map}__init2() {this.imports={register_tap_interface:e=>{let r=new F;k.setOuter(r,{handle:e,sendFrame:t=>{let n=this.copyToMemory(t),s=this.exports.send_tap_interface(e,n,t.length);if(s!==A.ERR_OK)throw new Error(`failed to send frame: ${s}`)},getMacAddress:()=>{let t=this.exports.get_interface_mac_address(e),n=this.viewFromMemory(t,6);return _wire.parseMacAddress.call(void 0, n)},getIPv4Address:()=>{let t=this.exports.get_interface_ip4_address(e);if(t===0)return;let n=this.viewFromMemory(t,4);return _wire.parseIPv4Address.call(void 0, n)},getIPv4Netmask:()=>{let t=this.exports.get_interface_ip4_netmask(e);if(t===0)return;let n=this.viewFromMemory(t,4);return _wire.parseIPv4Address.call(void 0, n)}}),this.interfaces.set(e,r)},receive_frame:async(e,r,t)=>{let n=this.copyFromMemory(r,t);await g();let s=this.interfaces.get(e);if(!s){console.error("received frame on unknown tap interface");return}k.getInner(s).receiveFrame(new Uint8Array(n))}}}async create(e){var m=[];try{let r=e.mac?_wire.serializeMacAddress.call(void 0, e.mac):_wire.generateMacAddress.call(void 0, );let{ipAddress:t,netmask:n}=e.ip?_wire.serializeIPv4Cidr.call(void 0, e.ip):{};let s=p(m,this.copyToMemory(r));let a=p(m,t?this.copyToMemory(t):void 0);let i=p(m,n?this.copyToMemory(n):void 0);let c=this.exports.create_tap_interface(s,_nullishCoalesce(a, () => (0)),_nullishCoalesce(i, () => (0)));let d=this.interfaces.get(c);if(!d)throw new Error("tap interface failed to register");return d}catch(h){var b=h,_=!0}finally{f(m,b,_)}}async remove(e){for(let[r,t]of this.interfaces.entries())if(t===e){this.exports.remove_tap_interface(r),this.interfaces.delete(r);return}}}, _class),F= (_class2 =class{#e;#t=!1;__init3() {this.type="tap"}get mac(){return k.getOuter(this).getMacAddress()}get ip(){return k.getOuter(this).getIPv4Address()}get netmask(){return k.getOuter(this).getIPv4Netmask()}constructor(){;_class2.prototype.__init3.call(this);k.setInner(this,{receiveFrame:async e=>{if(this.#t){if(!this.#e)throw new Error("readable stream not initialized");this.#e.enqueue(e)}}}),this.readable=new T({start:e=>{this.#e=e},lock:()=>{this.#t=!0}}),this.writable=new WritableStream({write:e=>{try{k.getOuter(this).sendFrame(e)}catch(r){console.error("tap interface send failed",r)}}})}listen(){if(this.readable.locked)throw new Error("readable stream already locked");return w(this.readable)}[Symbol.asyncIterator](){return this.listen()}}, _class2);var U=new l,E= (_class3 =class extends u{constructor(...args2) { super(...args2); _class3.prototype.__init4.call(this);_class3.prototype.__init5.call(this); }__init4() {this.interfaces=new Map}__init5() {this.imports={}}async create(e){var b=[];try{let r=e.mac?_wire.serializeMacAddress.call(void 0, e.mac):_wire.generateMacAddress.call(void 0, );let{ipAddress:t,netmask:n}=e.ip?_wire.serializeIPv4Cidr.call(void 0, e.ip):{};let s=p(b,this.copyToMemory(r));let a=p(b,t?this.copyToMemory(t):void 0);let i=p(b,n?this.copyToMemory(n):void 0);let c=new Uint32Array(e.ports.map(I=>Number(k.getOuter(I).handle)));let d=p(b,this.copyToMemory(c.buffer));let m=this.exports.create_bridge_interface(s,_nullishCoalesce(a, () => (0)),_nullishCoalesce(i, () => (0)),d,e.ports.length);let h=new j;U.setOuter(h,{handle:m,getMacAddress:()=>{let I=this.exports.get_interface_mac_address(m),S=this.viewFromMemory(I,6);return _wire.parseMacAddress.call(void 0, S)},getIPv4Address:()=>{let I=this.exports.get_interface_ip4_address(m);if(I===0)return;let S=this.viewFromMemory(I,4);return _wire.parseIPv4Address.call(void 0, S)},getIPv4Netmask:()=>{let I=this.exports.get_interface_ip4_netmask(m);if(I===0)return;let S=this.viewFromMemory(I,4);return _wire.parseIPv4Address.call(void 0, S)}});this.interfaces.set(m,h);return h}catch(_){var W=_,D=!0}finally{f(b,W,D)}}async remove(e){for(let[r,t]of this.interfaces.entries())if(t===e){this.exports.remove_bridge_interface(r),this.interfaces.delete(r);return}}}, _class3),j= (_class4 =class{constructor() { _class4.prototype.__init6.call(this); }__init6() {this.type="bridge"}get mac(){return U.getOuter(this).getMacAddress()}get ip(){return U.getOuter(this).getIPv4Address()}get netmask(){return U.getOuter(this).getIPv4Netmask()}}, _class4);var z=new l,C= (_class5 =class extends u{constructor(...args3) { super(...args3); _class5.prototype.__init7.call(this);_class5.prototype.__init8.call(this); }__init7() {this.interfaces=new Map}__init8() {this.imports={register_loopback_interface:e=>{let r=new K;z.setOuter(r,{handle:e,getIPv4Address:()=>{let t=this.exports.get_interface_ip4_address(e);if(t===0)return;let n=this.viewFromMemory(t,4);return _wire.parseIPv4Address.call(void 0, n)},getIPv4Netmask:()=>{let t=this.exports.get_interface_ip4_netmask(e);if(t===0)return;let n=this.viewFromMemory(t,4);return _wire.parseIPv4Address.call(void 0, n)}}),this.interfaces.set(e,r)}}}async create(e){var c=[];try{let{ipAddress:r,netmask:t}=e.ip?_wire.serializeIPv4Cidr.call(void 0, e.ip):{};let n=p(c,r?this.copyToMemory(r):void 0);let s=p(c,t?this.copyToMemory(t):void 0);let a=this.exports.create_loopback_interface(_nullishCoalesce(n, () => (0)),_nullishCoalesce(s, () => (0)));let i=this.interfaces.get(a);if(!i)throw new Error("loopback interface failed to register");return i}catch(d){var m=d,h=!0}finally{f(c,m,h)}}async remove(e){for(let[r,t]of this.interfaces.entries())if(t===e){this.exports.remove_loopback_interface(r),this.interfaces.delete(r);return}}}, _class5),K= (_class6 =class{constructor() { _class6.prototype.__init9.call(this); }__init9() {this.type="loopback"}get ip(){return z.getOuter(this).getIPv4Address()}get netmask(){return z.getOuter(this).getIPv4Netmask()}}, _class6);var q=new l,v=new l,G=1460,at=G*4,it=G*4,Ie=G,M= (_class7 =class extends u{#e=new Map;#t=new P;#r=new Map;#n;async#s(e){try{return _wire.serializeIPv4Address.call(void 0, e)}catch (e2){let t=await this.#n.lookup(e);return _wire.serializeIPv4Address.call(void 0, t)}}constructor(e){super();_class7.prototype.__init10.call(this);,this.#n=e}__init10() {this.imports={accept_tcp_connection:async(e,r)=>{let t=this.#e.get(e);if(!t){console.error("new tcp connection to unknown listener");return}await g();let n=new L;v.setOuter(n,{send:async s=>{let a=Number(this.copyToMemory(s)),i=this.exports.send_tcp_chunk(r,a,s.length);for(;i<s.length;){await new Promise(d=>{this.#r.set(r,d)});let c=s.length-i;i+=this.exports.send_tcp_chunk(r,a+i,c)}},updateReceiveBuffer:s=>{this.exports.update_tcp_receive_buffer(r,s)},close:async()=>{let s=this.exports.close_tcp_connection(r);if(s!==A.ERR_OK)throw new Error(`failed to close tcp connection: ${s}`)}}),this.#t.set(r,n),q.getInner(t).accept(n)},connected_tcp_connection:async e=>{await g();let r=new L;v.setOuter(r,{send:async t=>{let n=Number(this.copyToMemory(t)),s=this.exports.send_tcp_chunk(e,n,t.length);for(;s<t.length;){await new Promise(i=>{this.#r.set(e,i)});let a=t.length-s;s+=this.exports.send_tcp_chunk(e,n+s,a)}},updateReceiveBuffer:t=>{this.exports.update_tcp_receive_buffer(e,t)},close:async()=>{this.exports.close_tcp_connection(e)}}),this.#t.set(e,r)},closed_tcp_connection:async e=>{let r=this.#t.get(e);if(!r){console.error("received close on unknown tcp connection");return}await v.getInner(r).close()},receive_tcp_chunk:async(e,r,t)=>{let n=this.copyFromMemory(r,t),s=this.#t.get(e);if(!s){console.error("received chunk on unknown tcp connection");return}await g(),v.getInner(s).receive(new Uint8Array(n))},sent_tcp_chunk:(e,r)=>{let t=this.#r.get(e);this.#r.delete(e),_optionalChain([t, 'optionalCall', _12 => _12(r)])}}}async listen(e){var s=[];try{let r=p(s,e.host?this.copyToMemory(await this.#s(e.host)):null);let t=this.exports.create_tcp_listener(r,e.port);let n=new $;q.setOuter(n,{});this.#e.set(t,n);return n}catch(a){var i=a,c=!0}finally{f(s,i,c)}}async connect(e){var s=[];try{let r=p(s,this.copyToMemory(await this.#s(e.host)));let t=this.exports.create_tcp_connection(r,e.port);let n=await this.#t.wait(t);if(!n)throw new Error("tcp failed to connect");return n}catch(a){var i=a,c=!0}finally{f(s,i,c)}}}, _class7),$=class{#e=[];#t;constructor(){q.setInner(this,{accept:async e=>{this.#e.push(e),_optionalChain([this, 'access', _13 => _13.#t, 'optionalCall', _14 => _14()])}})}async*[Symbol.asyncIterator](){for(;;)await new Promise(e=>{this.#t=e}),yield*this.#e,this.#e=[]}},L=class{#e=[];#t;#r;constructor(){v.setInner(this,{receive:async e=>{this.#e.push(e),this.#n()},close:async()=>{this.close()}}),this.readable=new ReadableStream({start:e=>{this.#t=e},pull:()=>{this.#n()}},{highWaterMark:Ie,size:e=>e.byteLength}),this.writable=new WritableStream({start:e=>{this.#r=e},write:async e=>{await v.getOuter(this).send(e)}},{highWaterMark:0})}#n(){if(!_optionalChain([this, 'access', _15 => _15.#t, 'optionalAccess', _16 => _16.desiredSize]))return;let e=0;for(;this.#e.length>0;){let r=this.#e[0].length;if(e+r>this.#t.desiredSize)break;let t=this.#e.shift();this.#t.enqueue(t),e+=t.length}e>0&&v.getOuter(this).updateReceiveBuffer(e)}async close(){await v.getOuter(this).close(),_optionalChain([this, 'access', _17 => _17.#t, 'optionalAccess', _18 => _18.error, 'call', _19 => _19(new Error("tcp connection closed"))]),_optionalChain([this, 'access', _20 => _20.#r, 'optionalAccess', _21 => _21.error, 'call', _22 => _22(new Error("tcp connection closed"))])}[Symbol.asyncIterator](){if(this.readable.locked)throw new Error("readable stream already locked");return w(this.readable)}};var x=new l,B= (_class8 =class extends u{constructor(...args4) { super(...args4); _class8.prototype.__init11.call(this);_class8.prototype.__init12.call(this); }__init11() {this.interfaces=new Map}__init12() {this.imports={register_tun_interface:e=>{let r=new Q;x.setOuter(r,{handle:e,sendPacket:t=>{let n=this.copyToMemory(t);this.exports.send_tun_interface(e,n,t.length)},getIPv4Address:()=>{let t=this.exports.get_interface_ip4_address(e);if(t===0)return;let n=this.viewFromMemory(t,4);return _wire.parseIPv4Address.call(void 0, n)},getIPv4Netmask:()=>{let t=this.exports.get_interface_ip4_netmask(e);if(t===0)return;let n=this.viewFromMemory(t,4);return _wire.parseIPv4Address.call(void 0, n)}}),this.interfaces.set(e,r)},receive_packet:async(e,r,t)=>{let n=this.copyFromMemory(r,t);await g();let s=this.interfaces.get(e);if(!s){console.error("received packet on unknown tun interface");return}x.getInner(s).receivePacket(new Uint8Array(n))}}}async create(e){var c=[];try{let{ipAddress:r,netmask:t}=e.ip?_wire.serializeIPv4Cidr.call(void 0, e.ip):{};let n=p(c,r?this.copyToMemory(r):void 0);let s=p(c,t?this.copyToMemory(t):void 0);let a=this.exports.create_tun_interface(_nullishCoalesce(n, () => (0)),_nullishCoalesce(s, () => (0)));let i=this.interfaces.get(a);if(!i)throw new Error("tun interface failed to register");return i}catch(d){var m=d,h=!0}finally{f(c,m,h)}}async remove(e){for(let[r,t]of this.interfaces.entries())if(t===e){this.exports.remove_tun_interface(r),this.interfaces.delete(r);return}}}, _class8),Q= (_class9 =class{#e;#t=!1;__init13() {this.type="tun"}get ip(){return x.getOuter(this).getIPv4Address()}get netmask(){return x.getOuter(this).getIPv4Netmask()}constructor(){;_class9.prototype.__init13.call(this);x.setInner(this,{receivePacket:async e=>{if(this.#t){if(!this.#e)throw new Error("readable stream not initialized");_optionalChain([this, 'access', _23 => _23.#e, 'optionalAccess', _24 => _24.enqueue, 'call', _25 => _25(e)])}}}),this.readable=new T({start:e=>{this.#e=e},lock:()=>{this.#t=!0}}),this.writable=new WritableStream({write:e=>{x.getOuter(this).sendPacket(e)}})}listen(){if(this.readable.locked)throw new Error("readable stream already locked");return w(this.readable)}[Symbol.asyncIterator](){return this.listen()}}, _class9);var H=new l,N= (_class10 =class extends u{#e=new P;#t;async#r(e){try{return _wire.serializeIPv4Address.call(void 0, e)}catch (e3){let t=await this.#t.lookup(e);return _wire.serializeIPv4Address.call(void 0, t)}}constructor(e){super();_class10.prototype.__init14.call(this);,this.#t=e}__init14() {this.imports={receive_udp_datagram:async(e,r,t,n,s)=>{let a=this.copyFromMemory(r,4),i=this.copyFromMemory(n,s),c=this.#e.get(e);if(!c){console.error("received datagram on unknown udp socket");return}await g(),H.getInner(c).receive({host:_wire.parseIPv4Address.call(void 0, a),port:t,data:i})}}}async open(e){var s=[];try{let r=p(s,e.host?this.copyToMemory(await this.#r(e.host)):null);let t=this.exports.open_udp_socket(r,_nullishCoalesce(e.port, () => (0)));if(Number(t)===0)throw new Error("failed to open udp socket");let n=new Z;H.setOuter(n,{send:async d=>{var _=[];try{let m=p(_,this.copyToMemory(await this.#r(d.host)));let h=p(_,this.copyToMemory(d.data));let b=this.exports.send_udp_datagram(t,m,d.port,h,d.data.length);if(b!==A.ERR_OK)throw new Error(`failed to send udp datagram: ${b}`)}catch(W){var D=W,I=!0}finally{f(_,D,I)}},close:async()=>{this.exports.close_udp_socket(t),this.#e.delete(t)}});this.#e.set(t,n);return n}catch(a){var i=a,c=!0}finally{f(s,i,c)}}}, _class10),Z=class{#e;#t;constructor(){H.setInner(this,{receive:async e=>{if(!this.#e)throw new Error("readable controller not initialized");this.#e.enqueue(e)}}),this.readable=new ReadableStream({start:e=>{this.#e=e}}),this.writable=new WritableStream({start:e=>{this.#t=e},write:async e=>{await H.getOuter(this).send(e)}})}async close(){await H.getOuter(this).close(),_optionalChain([this, 'access', _26 => _26.#e, 'optionalAccess', _27 => _27.error, 'call', _28 => _28(new Error("udp socket closed"))]),_optionalChain([this, 'access', _29 => _29.#t, 'optionalAccess', _30 => _30.error, 'call', _31 => _31(new Error("udp socket closed"))])}[Symbol.asyncIterator](){if(this.readable.locked)throw new Error("readable stream already locked");return w(this.readable)}};var ke=typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string";async function oe(o,e){return ke?we(o,e):fetch(o)}async function we(o,e){let r=await Promise.resolve().then(() => _interopRequireWildcard(require("fs"))),{Readable:t}=await Promise.resolve().then(() => _interopRequireWildcard(require("stream"))),n=r.createReadStream(o),s=t.toWeb(n);return new Response(s,{headers:{"Content-Type":e}})}async function Ae(o){let e=new X(o);return await e.ready,e}var X=class{#e;#t;#r;#n;#s;#o;#a;#i;#c;get interfaces(){return this.#d()}constructor(e={}){this.#e={...e,initializeLoopback:_nullishCoalesce(e.initializeLoopback, () => (!0))},this.#r=new (0, _dns.DnsClient)(this,{nameServer:_nullishCoalesce(e.nameServer, () => ({ip:"127.0.0.1",port:53}))}),this.#n=new C,this.#s=new B,this.#o=new O,this.#a=new E,this.#i=new M(this.#r),this.#c=new N(this.#r),this.ready=this.#p(),this.ready.then(async()=>{this.#e.initializeLoopback&&await this.createLoopbackInterface({ip:"127.0.0.1/8"})})}async#p(){let e=new (0, _browser_wasi_shim.WASI)([],[],[new (0, _browser_wasi_shim.OpenFile)(new (0, _browser_wasi_shim.File)([])),_browser_wasi_shim.ConsoleStdout.lineBuffered(s=>console.log(`[WASI stdout] ${s}`)),_browser_wasi_shim.ConsoleStdout.lineBuffered(s=>console.warn(`[WASI stderr] ${s}`))]),r=oe(new URL("../tcpip.wasm",import.meta.url),"application/wasm"),{instance:t}=await WebAssembly.instantiateStreaming(r,{wasi_snapshot_preview1:e.wasiImport,env:{...this.#n.imports,...this.#s.imports,...this.#o.imports,...this.#a.imports,...this.#i.imports,...this.#c.imports}}),n=t;this.#n.register(n.exports),this.#s.register(n.exports),this.#o.register(n.exports),this.#a.register(n.exports),this.#i.register(n.exports),this.#c.register(n.exports),e.initialize(n),this.#t=Number(setInterval(()=>{n.exports.process_queued_packets(),n.exports.process_timeouts()},100))}*#d(){yield*this.#n.interfaces.values(),yield*this.#s.interfaces.values(),yield*this.#o.interfaces.values(),yield*this.#a.interfaces.values()}async createLoopbackInterface(e){return await this.ready,this.#n.create(e)}async createTunInterface(e){return await this.ready,this.#s.create(e)}async createTapInterface(e={}){return await this.ready,this.#o.create(e)}async createBridgeInterface(e){return await this.ready,this.#a.create(e)}async removeInterface(e){switch(await this.ready,e.type){case"loopback":return this.#n.remove(e);case"tun":return this.#s.remove(e);case"tap":return this.#o.remove(e);case"bridge":return this.#a.remove(e);default:throw new Error("unknown interface type")}}async listenTcp(e){return await this.ready,this.#i.listen(e)}async connectTcp(e){return await this.ready,this.#i.connect(e)}async openUdp(e={}){return await this.ready,this.#c.open(e)}};exports.createStack = Ae;
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/grichardson/Documents/dev/tcpip.js/packages/tcpip/dist/index.cjs","../polyfills/disposable.ts","../src/stack.ts","../src/bindings/bridge-interface.ts","../src/util.ts"],"names":["DisposeSymbol","Hooks","#outerHooks","#innerHooks","key","hooks"],"mappings":"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","file":"/Users/grichardson/Documents/dev/tcpip.js/packages/tcpip/dist/index.cjs","sourcesContent":[null,"/**\n * Scoped polyfill for `Symbol.dispose` without polluting the global scope.\n * Required for the `using` keyword which we use internally.\n * \n * We export this symbol as 'Symbol.dispose' which tells ESBuild to inject\n * it into the output bundle.\n * \n * The below works because Typescript's `using` implementation falls back to\n * `Symbol.for(\"Symbol.dispose\")` if the built-in `Symbol.dispose` is not available.\n */\n\nconst DisposeSymbol = 'dispose' in (Symbol as object) ? Symbol.dispose : Symbol.for('Symbol.dispose');\n\nexport {\n DisposeSymbol as 'Symbol.dispose'\n};\n","import { ConsoleStdout, File, OpenFile, WASI } from '@bjorn3/browser_wasi_shim';\nimport { DnsClient, type NameServer } from '@tcpip/dns';\nimport {\n BridgeBindings,\n type BridgeInterface,\n type BridgeInterfaceOptions,\n} from './bindings/bridge-interface.js';\nimport {\n LoopbackBindings,\n type LoopbackInterface,\n type LoopbackInterfaceOptions,\n} from './bindings/loopback-interface.js';\nimport {\n TapBindings,\n type TapInterface,\n type TapInterfaceOptions,\n} from './bindings/tap-interface.js';\nimport {\n TcpBindings,\n type TcpConnection,\n type TcpConnectionOptions,\n type TcpListener,\n type TcpListenerOptions,\n} from './bindings/tcp.js';\nimport {\n TunBindings,\n type TunInterface,\n type TunInterfaceOptions,\n} from './bindings/tun-interface.js';\nimport {\n UdpBindings,\n type UdpSocket,\n type UdpSocketOptions,\n} from './bindings/udp.js';\nimport { fetchFile } from './fetch-file.js';\nimport type { NetworkInterface, WasmInstance } from './types.js';\n\nexport async function createStack(\n options?: NetworkStackOptions\n): Promise<NetworkStack> {\n const stack = new VirtualNetworkStack(options);\n await stack.ready;\n return stack;\n}\n\nexport type NetworkStackOptions = {\n /**\n * Whether to initialize a loopback interface on startup.\n *\n * @default true\n */\n initializeLoopback?: boolean;\n\n /**\n * Name server used for DNS resolution.\n *\n * @default { ip: '127.0.0.1', port: 53 }\n */\n nameServer?: NameServer;\n};\n\nexport type NetworkStack = {\n readonly ready: Promise<void>;\n readonly interfaces: Iterable<NetworkInterface>;\n\n createLoopbackInterface(\n options: LoopbackInterfaceOptions\n ): Promise<LoopbackInterface>;\n createTunInterface(options: TunInterfaceOptions): Promise<TunInterface>;\n createTapInterface(options?: TapInterfaceOptions): Promise<TapInterface>;\n createBridgeInterface(\n options: BridgeInterfaceOptions\n ): Promise<BridgeInterface>;\n removeInterface(\n netInterface: LoopbackInterface | TunInterface | TapInterface\n ): Promise<void>;\n /**\n * Listens for incoming TCP connections on the specified host/port.\n */\n listenTcp(options: TcpListenerOptions): Promise<TcpListener>;\n /**\n * Establishes an outbound TCP connection to a remote host/port.\n */\n connectTcp(options: TcpConnectionOptions): Promise<TcpConnection>;\n /**\n * Opens a UDP socket for sending and receiving datagrams.\n *\n * If no local host is provided, the socket will bind to all available interfaces.\n * If no local port is provided, the socket will bind to a random port.\n */\n openUdp(options?: UdpSocketOptions): Promise<UdpSocket>;\n};\n\nexport class VirtualNetworkStack implements NetworkStack {\n #options: NetworkStackOptions;\n #loopIntervalId?: number;\n #dnsClient: DnsClient;\n\n #loopbackBindings: LoopbackBindings;\n #tunBindings: TunBindings;\n #tapBindings: TapBindings;\n #bridgeBindings: BridgeBindings;\n #tcpBindings: TcpBindings;\n #udpBindings: UdpBindings;\n\n ready: Promise<void>;\n get interfaces() {\n return this.#listInterfaces();\n }\n\n constructor(options: NetworkStackOptions = {}) {\n this.#options = {\n ...options,\n initializeLoopback: options.initializeLoopback ?? true,\n };\n\n this.#dnsClient = new DnsClient(this, {\n nameServer: options.nameServer ?? { ip: '127.0.0.1', port: 53 },\n });\n\n // Initialize bindings\n this.#loopbackBindings = new LoopbackBindings();\n this.#tunBindings = new TunBindings();\n this.#tapBindings = new TapBindings();\n this.#bridgeBindings = new BridgeBindings();\n this.#tcpBindings = new TcpBindings(this.#dnsClient);\n this.#udpBindings = new UdpBindings(this.#dnsClient);\n\n // Initialize the stack\n this.ready = this.#init();\n\n // Post-init setup\n this.ready.then(async () => {\n if (this.#options.initializeLoopback) {\n await this.createLoopbackInterface({\n ip: '127.0.0.1/8',\n });\n }\n });\n }\n\n async #init() {\n const wasi = new WASI(\n [],\n [],\n [\n new OpenFile(new File([])), // stdin\n ConsoleStdout.lineBuffered((msg) =>\n console.log(`[WASI stdout] ${msg}`)\n ),\n ConsoleStdout.lineBuffered((msg) =>\n console.warn(`[WASI stderr] ${msg}`)\n ),\n ]\n );\n\n const source = fetchFile(\n new URL('../tcpip.wasm', import.meta.url),\n 'application/wasm'\n );\n\n // Instantiate with both WASI and custom imports\n const { instance } = await WebAssembly.instantiateStreaming(source, {\n wasi_snapshot_preview1: wasi.wasiImport,\n env: {\n ...this.#loopbackBindings.imports,\n ...this.#tunBindings.imports,\n ...this.#tapBindings.imports,\n ...this.#bridgeBindings.imports,\n ...this.#tcpBindings.imports,\n ...this.#udpBindings.imports,\n },\n });\n\n const wasmInstance = instance as WasmInstance;\n\n this.#loopbackBindings.register(wasmInstance.exports);\n this.#tunBindings.register(wasmInstance.exports);\n this.#tapBindings.register(wasmInstance.exports);\n this.#bridgeBindings.register(wasmInstance.exports);\n this.#tcpBindings.register(wasmInstance.exports);\n this.#udpBindings.register(wasmInstance.exports);\n\n const result = wasi.start(wasmInstance);\n\n if (result !== 0) {\n throw new Error(`wasi start failed with code ${result}`);\n }\n\n // Call lwIP's main loop regularly (required in NO_SYS mode)\n // Used to process queued packets (eg. loopback) and expired timeouts\n this.#loopIntervalId = Number(\n setInterval(() => {\n wasmInstance.exports.process_queued_packets();\n wasmInstance.exports.process_timeouts();\n }, 100)\n );\n }\n\n *#listInterfaces(): Iterable<NetworkInterface> {\n yield* this.#loopbackBindings.interfaces.values();\n yield* this.#tunBindings.interfaces.values();\n yield* this.#tapBindings.interfaces.values();\n yield* this.#bridgeBindings.interfaces.values();\n }\n\n async createLoopbackInterface(\n options: LoopbackInterfaceOptions\n ): Promise<LoopbackInterface> {\n await this.ready;\n return this.#loopbackBindings.create(options);\n }\n\n async createTunInterface(\n options: TunInterfaceOptions\n ): Promise<TunInterface> {\n await this.ready;\n return this.#tunBindings.create(options);\n }\n\n async createTapInterface(\n options: TapInterfaceOptions = {}\n ): Promise<TapInterface> {\n await this.ready;\n return this.#tapBindings.create(options);\n }\n\n async createBridgeInterface(options: BridgeInterfaceOptions) {\n await this.ready;\n return this.#bridgeBindings.create(options);\n }\n\n async removeInterface(netInterface: NetworkInterface) {\n await this.ready;\n\n switch (netInterface.type) {\n case 'loopback':\n return this.#loopbackBindings.remove(netInterface);\n case 'tun':\n return this.#tunBindings.remove(netInterface);\n case 'tap':\n return this.#tapBindings.remove(netInterface);\n case 'bridge':\n return this.#bridgeBindings.remove(netInterface);\n default:\n throw new Error('unknown interface type');\n }\n }\n\n /**\n * Listens for incoming TCP connections on the specified host/port.\n */\n async listenTcp(options: TcpListenerOptions) {\n await this.ready;\n return this.#tcpBindings.listen(options);\n }\n\n /**\n * Establishes an outbound TCP connection to a remote host/port.\n */\n async connectTcp(options: TcpConnectionOptions) {\n await this.ready;\n return this.#tcpBindings.connect(options);\n }\n\n /**\n * Opens a UDP socket for sending and receiving datagrams.\n *\n * If no local host is provided, the socket will bind to all available interfaces.\n * If no local port is provided, the socket will bind to a random port.\n */\n async openUdp(options: UdpSocketOptions = {}) {\n await this.ready;\n return this.#udpBindings.open(options);\n }\n}\n","import {\n parseIPv4Address,\n parseMacAddress,\n serializeIPv4Cidr,\n serializeMacAddress,\n type IPv4Address,\n type IPv4Cidr,\n type MacAddress,\n} from '@tcpip/wire';\nimport type { Pointer } from '../types.js';\nimport { generateMacAddress, Hooks } from '../util.js';\nimport { Bindings } from './base.js';\nimport { tapInterfaceHooks, type TapInterface } from './tap-interface.js';\n\ntype BridgeInterfaceHandle = Pointer;\n\ntype BridgeInterfaceOuterHooks = {\n handle: BridgeInterfaceHandle;\n getMacAddress(): MacAddress;\n getIPv4Address(): IPv4Address | undefined;\n getIPv4Netmask(): IPv4Address | undefined;\n};\n\ntype BridgeInterfaceInnerHooks = {};\n\nexport const bridgeInterfaceHooks = new Hooks<\n BridgeInterface,\n BridgeInterfaceOuterHooks,\n BridgeInterfaceInnerHooks\n>();\n\nexport type BridgeImports = {};\n\nexport type BridgeExports = {\n create_bridge_interface(\n macAddress: Pointer,\n ipAddress: Pointer,\n netmask: Pointer,\n ports: Pointer,\n ports_length: number\n ): BridgeInterfaceHandle;\n remove_bridge_interface(handle: BridgeInterfaceHandle): void;\n};\n\nexport class BridgeBindings extends Bindings<BridgeImports, BridgeExports> {\n interfaces = new Map<BridgeInterfaceHandle, BridgeInterface>();\n\n imports = {};\n\n async create(options: BridgeInterfaceOptions) {\n const macAddress = options.mac\n ? serializeMacAddress(options.mac)\n : generateMacAddress();\n\n const { ipAddress, netmask } = options.ip\n ? serializeIPv4Cidr(options.ip)\n : {};\n\n using macAddressPtr = this.copyToMemory(macAddress);\n using ipAddressPtr = ipAddress ? this.copyToMemory(ipAddress) : undefined;\n using netmaskPtr = netmask ? this.copyToMemory(netmask) : undefined;\n const portHandles = new Uint32Array(\n options.ports.map((port) =>\n Number(tapInterfaceHooks.getOuter(port).handle)\n )\n );\n\n using portHandlesPtr = this.copyToMemory(portHandles.buffer);\n\n const handle = this.exports.create_bridge_interface(\n macAddressPtr,\n ipAddressPtr ?? 0,\n netmaskPtr ?? 0,\n portHandlesPtr,\n options.ports.length\n );\n\n const bridgeInterface = new VirtualBridgeInterface();\n\n bridgeInterfaceHooks.setOuter(bridgeInterface, {\n handle,\n getMacAddress: () => {\n const macPtr = this.exports.get_interface_mac_address(handle);\n\n const macBytes = this.viewFromMemory(macPtr, 6);\n return parseMacAddress(macBytes);\n },\n getIPv4Address: () => {\n const ipPtr = this.exports.get_interface_ip4_address(handle);\n\n if (ipPtr === 0) {\n return;\n }\n\n const ipBytes = this.viewFromMemory(ipPtr, 4);\n return parseIPv4Address(ipBytes);\n },\n getIPv4Netmask: () => {\n const netmaskPtr = this.exports.get_interface_ip4_netmask(handle);\n\n if (netmaskPtr === 0) {\n return;\n }\n\n const netmaskBytes = this.viewFromMemory(netmaskPtr, 4);\n return parseIPv4Address(netmaskBytes);\n },\n });\n\n this.interfaces.set(handle, bridgeInterface);\n\n return bridgeInterface;\n }\n\n async remove(bridgeInterface: BridgeInterface) {\n for (const [handle, loopback] of this.interfaces.entries()) {\n if (loopback === bridgeInterface) {\n this.exports.remove_bridge_interface(handle);\n this.interfaces.delete(handle);\n return;\n }\n }\n }\n}\n\nexport type BridgeInterfaceOptions = {\n ports: TapInterface[];\n mac?: MacAddress;\n ip?: IPv4Cidr;\n};\n\nexport type BridgeInterface = {\n readonly type: 'bridge';\n readonly mac: MacAddress;\n readonly ip?: IPv4Address;\n readonly netmask?: IPv4Address;\n};\n\nexport class VirtualBridgeInterface implements BridgeInterface {\n readonly type = 'bridge';\n get mac(): MacAddress {\n return bridgeInterfaceHooks.getOuter(this).getMacAddress();\n }\n get ip(): IPv4Address | undefined {\n return bridgeInterfaceHooks.getOuter(this).getIPv4Address();\n }\n get netmask(): IPv4Address | undefined {\n return bridgeInterfaceHooks.getOuter(this).getIPv4Netmask();\n }\n}\n","/**\n * Utility class to facilitate internal communication\n * between bindings and JS instances.\n * Hooks are created for both the outer (bindings) and\n * inner (JS instance) sides of the communication.\n *\n * Uses `WeakMap` to map each JS instance to a set of\n * hooks while avoiding memory leaks.\n */\nexport class Hooks<K extends WeakKey, O, I> {\n #outerHooks = new WeakMap<K, O>();\n #innerHooks = new WeakMap<K, I>();\n\n setOuter(key: K, hooks: O) {\n this.#outerHooks.set(key, hooks);\n }\n\n setInner(key: K, hooks: I) {\n this.#innerHooks.set(key, hooks);\n }\n\n getOuter(key: K) {\n const hooks = this.#outerHooks.get(key);\n\n if (!hooks) {\n throw new Error(`outer hooks not set for ${key}`);\n }\n\n return hooks;\n }\n\n getInner(key: K) {\n const hooks = this.#innerHooks.get(key);\n\n if (!hooks) {\n throw new Error(`inner hooks not set for ${key}`);\n }\n\n return hooks;\n }\n}\n\nexport class UniquePointer extends Number {\n free: (ptr: number) => void;\n\n /**\n * A unique pointer that will automatically free virtual memory when\n * it is disposed. Named after the C++ concept of a unique pointer.\n *\n * Should be used with the `using` keyword to ensure that the pointer is\n * freed (via dispose function) when it is no longer in scope.\n *\n * Useful with WASM modules that require allocating and freeing memory.\n *\n * @example\n * ```ts\n * using ptr = new UniquePointer(wasmBridge.malloc(10), wasmBridge.free);\n * ```\n *\n * @param address The address of the pointer\n * @param free The function to call to free the pointer\n */\n constructor(address: number, free: (ptr: number) => void) {\n super(address);\n this.free = free;\n }\n\n [Symbol.dispose]() {\n this.free(this.valueOf());\n }\n}\n\n/**\n * Map that allows waiting for changes to values.\n */\nexport class EventMap<K, V> extends Map<K, V> {\n #listeners = new Map<K, Set<(value: V) => void>>();\n\n /**\n * Waits for the next `set()` call on the given key.\n */\n wait(key: K): Promise<V> {\n return new Promise((resolve) => {\n const listeners = this.#listeners.get(key) ?? new Set();\n listeners.add(resolve);\n this.#listeners.set(key, listeners);\n });\n }\n\n override set(key: K, value: V) {\n super.set(key, value);\n\n const listeners = this.#listeners.get(key);\n\n if (listeners) {\n for (const listener of listeners) {\n listener(value);\n listeners.delete(listener);\n }\n }\n\n return this;\n }\n}\n\n/**\n * Converts a `ReadableStream` into an `AsyncIterableIterator`.\n *\n * Allows you to use ReadableStreams in a `for await ... of` loop.\n */\nexport function fromReadable<R>(\n readable: ReadableStream<R>,\n options?: { preventCancel?: boolean }\n): AsyncIterableIterator<R> {\n const reader = readable.getReader();\n return fromReader(reader, options);\n}\n\n/**\n * Converts a `ReadableStreamDefaultReader` into an `AsyncIterableIterator`.\n *\n * Allows you to use Readers in a `for await ... of` loop.\n */\nexport async function* fromReader<R>(\n reader: ReadableStreamDefaultReader<R>,\n options?: { preventCancel?: boolean }\n): AsyncIterableIterator<R> {\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n return value;\n }\n yield value;\n }\n } finally {\n if (!options?.preventCancel) {\n await reader.cancel();\n }\n reader.releaseLock();\n }\n}\n\nexport type UnderlyingSourceLockCallback = () => void;\n\n/**\n * `ReadableStream` with an optional lock callback.\n */\nexport class ExtendedReadableStream<R> extends ReadableStream<R> {\n #notifyLock?: () => void;\n\n constructor(\n {\n lock,\n ...underlyingSource\n }: UnderlyingSource & { lock?: UnderlyingSourceLockCallback },\n strategy?: QueuingStrategy<R>\n ) {\n super(underlyingSource, strategy);\n this.#notifyLock = lock;\n }\n\n override getReader() {\n const reader = super.getReader() as any;\n if (this.locked) {\n this.#notifyLock?.();\n }\n return reader;\n }\n\n override pipeThrough<T>(\n transform: ReadableWritablePair<T, R>,\n options?: StreamPipeOptions\n ): ReadableStream<T> {\n const stream = super.pipeThrough(transform, options);\n if (this.locked) {\n this.#notifyLock?.();\n }\n return stream;\n }\n\n override pipeTo(\n dest: WritableStream<R>,\n options?: StreamPipeOptions\n ): Promise<void> {\n const promise = super.pipeTo(dest, options);\n if (this.locked) {\n this.#notifyLock?.();\n }\n return promise;\n }\n\n override tee(): [ReadableStream<R>, ReadableStream<R>] {\n const [a, b] = super.tee();\n if (this.locked) {\n this.#notifyLock?.();\n }\n return [a, b];\n }\n}\n\n/**\n * Queues a microtask and returns a promise that resolves when\n * the microtask is executed.\n *\n * Microtasks are executed after the current task has completed,\n * but before the next task begins (tasks are the main unit of\n * work in the event loop).\n *\n * Useful when you want synchronous code from the current task to\n * complete before executing asynchronous code.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API/Microtask_guide\n */\nexport async function nextMicrotask() {\n return await new Promise<void>((resolve) => queueMicrotask(resolve));\n}\n\n/**\n * Generates a random MAC address.\n *\n * The generated address is locally administered (so won't conflict\n * with real devices) and unicast (so it can be used as a source address).\n */\nexport function generateMacAddress() {\n const mac = new Uint8Array(6);\n crypto.getRandomValues(mac);\n\n // Control bits only apply to the first byte\n mac[0] =\n // Clear the 2 least significant bits\n (mac[0]! & 0b11111100) |\n // Set locally administered bit (bit 1) to 1 and unicast bit (bit 0) to 0\n 0b00000010;\n\n return mac;\n}\n"]}
1
+ {"version":3,"sources":["/Users/grichardson/Documents/dev/tcpip.js/packages/tcpip/dist/index.cjs","../polyfills/disposable.ts","../src/stack.ts","../src/bindings/bridge-interface.ts","../src/util.ts"],"names":["DisposeSymbol","Hooks","#outerHooks","#innerHooks","key","hooks"],"mappings":"AAAA,ykCAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,OAAO,eAAe,EAAE,UAAU,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,yCAAyC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CCWvxB,IAAMA,CAAAA,CAAgB,SAAA,GAAc,MAAA,CAAoB,MAAA,CAAO,OAAA,CAAU,MAAA,CAAO,GAAA,CAAI,gBAAgB,CAAA,CCXpG,8DAAoD,iCACT,mCCQpC,ICAMC,CAAAA,CAAN,KAAqC,CAC1CC,CAAAA,CAAAA,CAAc,IAAI,OAAA,CAClBC,CAAAA,CAAAA,CAAc,IAAI,OAAA,CAElB,QAAA,CAASC,CAAAA,CAAQC,CAAAA,CAAU,CACzB,IAAA,CAAKH,CAAAA,CAAAA,CAAY,GAAA,CAAIE,CAAAA,CAAKC,CAAK,CACjC,CAEA,QAAA,CAASD,CAAAA,CAAQC,CAAAA,CAAU,CACzB,IAAA,CAAKF,CAAAA,CAAAA,CAAY,GAAA,CAAIC,CAAAA,CAAKC,CAAK,CACjC,CAEA,QAAA,CAASD,CAAAA,CAAQ,CACf,IAAMC,CAAAA,CAAQ,IAAA,CAAKH,CAAAA,CAAAA,CAAY,GAAA,CAAIE,CAAG,CAAA,CAEtC,EAAA,CAAI,CAACC,CAAAA,CACH,MAAM,IAAI,KAAA,CAAM,CAAA,wBAAA,EAA2BD,CAAG,CAAA,CAAA","file":"/Users/grichardson/Documents/dev/tcpip.js/packages/tcpip/dist/index.cjs","sourcesContent":[null,"/**\n * Scoped polyfill for `Symbol.dispose` without polluting the global scope.\n * Required for the `using` keyword which we use internally.\n * \n * We export this symbol as 'Symbol.dispose' which tells ESBuild to inject\n * it into the output bundle.\n * \n * The below works because Typescript's `using` implementation falls back to\n * `Symbol.for(\"Symbol.dispose\")` if the built-in `Symbol.dispose` is not available.\n */\n\nconst DisposeSymbol = 'dispose' in (Symbol as object) ? Symbol.dispose : Symbol.for('Symbol.dispose');\n\nexport {\n DisposeSymbol as 'Symbol.dispose'\n};\n","import { ConsoleStdout, File, OpenFile, WASI } from '@bjorn3/browser_wasi_shim';\nimport { DnsClient, type NameServer } from '@tcpip/dns';\nimport {\n BridgeBindings,\n type BridgeInterface,\n type BridgeInterfaceOptions,\n} from './bindings/bridge-interface.js';\nimport {\n LoopbackBindings,\n type LoopbackInterface,\n type LoopbackInterfaceOptions,\n} from './bindings/loopback-interface.js';\nimport {\n TapBindings,\n type TapInterface,\n type TapInterfaceOptions,\n} from './bindings/tap-interface.js';\nimport {\n TcpBindings,\n type TcpConnection,\n type TcpConnectionOptions,\n type TcpListener,\n type TcpListenerOptions,\n} from './bindings/tcp.js';\nimport {\n TunBindings,\n type TunInterface,\n type TunInterfaceOptions,\n} from './bindings/tun-interface.js';\nimport {\n UdpBindings,\n type UdpSocket,\n type UdpSocketOptions,\n} from './bindings/udp.js';\nimport { fetchFile } from './fetch-file.js';\nimport type { NetworkInterface, WasmInstance } from './types.js';\n\nexport async function createStack(\n options?: NetworkStackOptions\n): Promise<NetworkStack> {\n const stack = new VirtualNetworkStack(options);\n await stack.ready;\n return stack;\n}\n\nexport type NetworkStackOptions = {\n /**\n * Whether to initialize a loopback interface on startup.\n *\n * @default true\n */\n initializeLoopback?: boolean;\n\n /**\n * Name server used for DNS resolution.\n *\n * @default { ip: '127.0.0.1', port: 53 }\n */\n nameServer?: NameServer;\n};\n\nexport type NetworkStack = {\n readonly ready: Promise<void>;\n readonly interfaces: Iterable<NetworkInterface>;\n\n createLoopbackInterface(\n options: LoopbackInterfaceOptions\n ): Promise<LoopbackInterface>;\n createTunInterface(options: TunInterfaceOptions): Promise<TunInterface>;\n createTapInterface(options?: TapInterfaceOptions): Promise<TapInterface>;\n createBridgeInterface(\n options: BridgeInterfaceOptions\n ): Promise<BridgeInterface>;\n removeInterface(\n netInterface: LoopbackInterface | TunInterface | TapInterface\n ): Promise<void>;\n /**\n * Listens for incoming TCP connections on the specified host/port.\n */\n listenTcp(options: TcpListenerOptions): Promise<TcpListener>;\n /**\n * Establishes an outbound TCP connection to a remote host/port.\n */\n connectTcp(options: TcpConnectionOptions): Promise<TcpConnection>;\n /**\n * Opens a UDP socket for sending and receiving datagrams.\n *\n * If no local host is provided, the socket will bind to all available interfaces.\n * If no local port is provided, the socket will bind to a random port.\n */\n openUdp(options?: UdpSocketOptions): Promise<UdpSocket>;\n};\n\nexport class VirtualNetworkStack implements NetworkStack {\n #options: NetworkStackOptions;\n #loopIntervalId?: number;\n #dnsClient: DnsClient;\n\n #loopbackBindings: LoopbackBindings;\n #tunBindings: TunBindings;\n #tapBindings: TapBindings;\n #bridgeBindings: BridgeBindings;\n #tcpBindings: TcpBindings;\n #udpBindings: UdpBindings;\n\n ready: Promise<void>;\n get interfaces() {\n return this.#listInterfaces();\n }\n\n constructor(options: NetworkStackOptions = {}) {\n this.#options = {\n ...options,\n initializeLoopback: options.initializeLoopback ?? true,\n };\n\n this.#dnsClient = new DnsClient(this, {\n nameServer: options.nameServer ?? { ip: '127.0.0.1', port: 53 },\n });\n\n // Initialize bindings\n this.#loopbackBindings = new LoopbackBindings();\n this.#tunBindings = new TunBindings();\n this.#tapBindings = new TapBindings();\n this.#bridgeBindings = new BridgeBindings();\n this.#tcpBindings = new TcpBindings(this.#dnsClient);\n this.#udpBindings = new UdpBindings(this.#dnsClient);\n\n // Initialize the stack\n this.ready = this.#init();\n\n // Post-init setup\n this.ready.then(async () => {\n if (this.#options.initializeLoopback) {\n await this.createLoopbackInterface({\n ip: '127.0.0.1/8',\n });\n }\n });\n }\n\n async #init() {\n const wasi = new WASI(\n [],\n [],\n [\n new OpenFile(new File([])), // stdin\n ConsoleStdout.lineBuffered((msg) =>\n console.log(`[WASI stdout] ${msg}`)\n ),\n ConsoleStdout.lineBuffered((msg) =>\n console.warn(`[WASI stderr] ${msg}`)\n ),\n ]\n );\n\n const source = fetchFile(\n new URL('../tcpip.wasm', import.meta.url),\n 'application/wasm'\n );\n\n // Instantiate with both WASI and custom imports\n const { instance } = await WebAssembly.instantiateStreaming(source, {\n wasi_snapshot_preview1: wasi.wasiImport,\n env: {\n ...this.#loopbackBindings.imports,\n ...this.#tunBindings.imports,\n ...this.#tapBindings.imports,\n ...this.#bridgeBindings.imports,\n ...this.#tcpBindings.imports,\n ...this.#udpBindings.imports,\n },\n });\n\n const wasmInstance = instance as WasmInstance;\n\n this.#loopbackBindings.register(wasmInstance.exports);\n this.#tunBindings.register(wasmInstance.exports);\n this.#tapBindings.register(wasmInstance.exports);\n this.#bridgeBindings.register(wasmInstance.exports);\n this.#tcpBindings.register(wasmInstance.exports);\n this.#udpBindings.register(wasmInstance.exports);\n\n // Our WASM binary is a WASI reactor module (ie. a lib),\n // so we call `initialize()` instead of `start()`.\n wasi.initialize(wasmInstance);\n\n // Call lwIP's main loop regularly (required in NO_SYS mode)\n // Used to process queued packets (eg. loopback) and expired timeouts\n this.#loopIntervalId = Number(\n setInterval(() => {\n wasmInstance.exports.process_queued_packets();\n wasmInstance.exports.process_timeouts();\n }, 100)\n );\n }\n\n *#listInterfaces(): Iterable<NetworkInterface> {\n yield* this.#loopbackBindings.interfaces.values();\n yield* this.#tunBindings.interfaces.values();\n yield* this.#tapBindings.interfaces.values();\n yield* this.#bridgeBindings.interfaces.values();\n }\n\n async createLoopbackInterface(\n options: LoopbackInterfaceOptions\n ): Promise<LoopbackInterface> {\n await this.ready;\n return this.#loopbackBindings.create(options);\n }\n\n async createTunInterface(\n options: TunInterfaceOptions\n ): Promise<TunInterface> {\n await this.ready;\n return this.#tunBindings.create(options);\n }\n\n async createTapInterface(\n options: TapInterfaceOptions = {}\n ): Promise<TapInterface> {\n await this.ready;\n return this.#tapBindings.create(options);\n }\n\n async createBridgeInterface(options: BridgeInterfaceOptions) {\n await this.ready;\n return this.#bridgeBindings.create(options);\n }\n\n async removeInterface(netInterface: NetworkInterface) {\n await this.ready;\n\n switch (netInterface.type) {\n case 'loopback':\n return this.#loopbackBindings.remove(netInterface);\n case 'tun':\n return this.#tunBindings.remove(netInterface);\n case 'tap':\n return this.#tapBindings.remove(netInterface);\n case 'bridge':\n return this.#bridgeBindings.remove(netInterface);\n default:\n throw new Error('unknown interface type');\n }\n }\n\n /**\n * Listens for incoming TCP connections on the specified host/port.\n */\n async listenTcp(options: TcpListenerOptions) {\n await this.ready;\n return this.#tcpBindings.listen(options);\n }\n\n /**\n * Establishes an outbound TCP connection to a remote host/port.\n */\n async connectTcp(options: TcpConnectionOptions) {\n await this.ready;\n return this.#tcpBindings.connect(options);\n }\n\n /**\n * Opens a UDP socket for sending and receiving datagrams.\n *\n * If no local host is provided, the socket will bind to all available interfaces.\n * If no local port is provided, the socket will bind to a random port.\n */\n async openUdp(options: UdpSocketOptions = {}) {\n await this.ready;\n return this.#udpBindings.open(options);\n }\n}\n","import {\n generateMacAddress,\n parseIPv4Address,\n parseMacAddress,\n serializeIPv4Cidr,\n serializeMacAddress,\n type IPv4Address,\n type IPv4Cidr,\n type MacAddress,\n} from '@tcpip/wire';\nimport type { Pointer } from '../types.js';\nimport { Hooks } from '../util.js';\nimport { Bindings } from './base.js';\nimport { tapInterfaceHooks, type TapInterface } from './tap-interface.js';\n\ntype BridgeInterfaceHandle = Pointer;\n\ntype BridgeInterfaceOuterHooks = {\n handle: BridgeInterfaceHandle;\n getMacAddress(): MacAddress;\n getIPv4Address(): IPv4Address | undefined;\n getIPv4Netmask(): IPv4Address | undefined;\n};\n\ntype BridgeInterfaceInnerHooks = {};\n\nexport const bridgeInterfaceHooks = new Hooks<\n BridgeInterface,\n BridgeInterfaceOuterHooks,\n BridgeInterfaceInnerHooks\n>();\n\nexport type BridgeImports = {};\n\nexport type BridgeExports = {\n create_bridge_interface(\n macAddress: Pointer,\n ipAddress: Pointer,\n netmask: Pointer,\n ports: Pointer,\n ports_length: number\n ): BridgeInterfaceHandle;\n remove_bridge_interface(handle: BridgeInterfaceHandle): void;\n};\n\nexport class BridgeBindings extends Bindings<BridgeImports, BridgeExports> {\n interfaces = new Map<BridgeInterfaceHandle, BridgeInterface>();\n\n imports = {};\n\n async create(options: BridgeInterfaceOptions) {\n const macAddress = options.mac\n ? serializeMacAddress(options.mac)\n : generateMacAddress();\n\n const { ipAddress, netmask } = options.ip\n ? serializeIPv4Cidr(options.ip)\n : {};\n\n using macAddressPtr = this.copyToMemory(macAddress);\n using ipAddressPtr = ipAddress ? this.copyToMemory(ipAddress) : undefined;\n using netmaskPtr = netmask ? this.copyToMemory(netmask) : undefined;\n const portHandles = new Uint32Array(\n options.ports.map((port) =>\n Number(tapInterfaceHooks.getOuter(port).handle)\n )\n );\n\n using portHandlesPtr = this.copyToMemory(portHandles.buffer);\n\n const handle = this.exports.create_bridge_interface(\n macAddressPtr,\n ipAddressPtr ?? 0,\n netmaskPtr ?? 0,\n portHandlesPtr,\n options.ports.length\n );\n\n const bridgeInterface = new VirtualBridgeInterface();\n\n bridgeInterfaceHooks.setOuter(bridgeInterface, {\n handle,\n getMacAddress: () => {\n const macPtr = this.exports.get_interface_mac_address(handle);\n\n const macBytes = this.viewFromMemory(macPtr, 6);\n return parseMacAddress(macBytes);\n },\n getIPv4Address: () => {\n const ipPtr = this.exports.get_interface_ip4_address(handle);\n\n if (ipPtr === 0) {\n return;\n }\n\n const ipBytes = this.viewFromMemory(ipPtr, 4);\n return parseIPv4Address(ipBytes);\n },\n getIPv4Netmask: () => {\n const netmaskPtr = this.exports.get_interface_ip4_netmask(handle);\n\n if (netmaskPtr === 0) {\n return;\n }\n\n const netmaskBytes = this.viewFromMemory(netmaskPtr, 4);\n return parseIPv4Address(netmaskBytes);\n },\n });\n\n this.interfaces.set(handle, bridgeInterface);\n\n return bridgeInterface;\n }\n\n async remove(bridgeInterface: BridgeInterface) {\n for (const [handle, loopback] of this.interfaces.entries()) {\n if (loopback === bridgeInterface) {\n this.exports.remove_bridge_interface(handle);\n this.interfaces.delete(handle);\n return;\n }\n }\n }\n}\n\nexport type BridgeInterfaceOptions = {\n ports: TapInterface[];\n mac?: MacAddress;\n ip?: IPv4Cidr;\n};\n\nexport type BridgeInterface = {\n readonly type: 'bridge';\n readonly mac: MacAddress;\n readonly ip?: IPv4Address;\n readonly netmask?: IPv4Address;\n};\n\nexport class VirtualBridgeInterface implements BridgeInterface {\n readonly type = 'bridge';\n get mac(): MacAddress {\n return bridgeInterfaceHooks.getOuter(this).getMacAddress();\n }\n get ip(): IPv4Address | undefined {\n return bridgeInterfaceHooks.getOuter(this).getIPv4Address();\n }\n get netmask(): IPv4Address | undefined {\n return bridgeInterfaceHooks.getOuter(this).getIPv4Netmask();\n }\n}\n","/**\n * Utility class to facilitate internal communication\n * between bindings and JS instances.\n * Hooks are created for both the outer (bindings) and\n * inner (JS instance) sides of the communication.\n *\n * Uses `WeakMap` to map each JS instance to a set of\n * hooks while avoiding memory leaks.\n */\nexport class Hooks<K extends WeakKey, O, I> {\n #outerHooks = new WeakMap<K, O>();\n #innerHooks = new WeakMap<K, I>();\n\n setOuter(key: K, hooks: O) {\n this.#outerHooks.set(key, hooks);\n }\n\n setInner(key: K, hooks: I) {\n this.#innerHooks.set(key, hooks);\n }\n\n getOuter(key: K) {\n const hooks = this.#outerHooks.get(key);\n\n if (!hooks) {\n throw new Error(`outer hooks not set for ${key}`);\n }\n\n return hooks;\n }\n\n getInner(key: K) {\n const hooks = this.#innerHooks.get(key);\n\n if (!hooks) {\n throw new Error(`inner hooks not set for ${key}`);\n }\n\n return hooks;\n }\n}\n\nexport class UniquePointer extends Number {\n free: (ptr: number) => void;\n\n /**\n * A unique pointer that will automatically free virtual memory when\n * it is disposed. Named after the C++ concept of a unique pointer.\n *\n * Should be used with the `using` keyword to ensure that the pointer is\n * freed (via dispose function) when it is no longer in scope.\n *\n * Useful with WASM modules that require allocating and freeing memory.\n *\n * @example\n * ```ts\n * using ptr = new UniquePointer(wasmBridge.malloc(10), wasmBridge.free);\n * ```\n *\n * @param address The address of the pointer\n * @param free The function to call to free the pointer\n */\n constructor(address: number, free: (ptr: number) => void) {\n super(address);\n this.free = free;\n }\n\n [Symbol.dispose]() {\n this.free(this.valueOf());\n }\n}\n\n/**\n * Map that allows waiting for changes to values.\n */\nexport class EventMap<K, V> extends Map<K, V> {\n #listeners = new Map<K, Set<(value: V) => void>>();\n\n /**\n * Waits for the next `set()` call on the given key.\n */\n wait(key: K): Promise<V> {\n return new Promise((resolve) => {\n const listeners = this.#listeners.get(key) ?? new Set();\n listeners.add(resolve);\n this.#listeners.set(key, listeners);\n });\n }\n\n override set(key: K, value: V) {\n super.set(key, value);\n\n const listeners = this.#listeners.get(key);\n\n if (listeners) {\n for (const listener of listeners) {\n listener(value);\n listeners.delete(listener);\n }\n }\n\n return this;\n }\n}\n\n/**\n * Converts a `ReadableStream` into an `AsyncIterableIterator`.\n *\n * Allows you to use ReadableStreams in a `for await ... of` loop.\n */\nexport function fromReadable<R>(\n readable: ReadableStream<R>,\n options?: { preventCancel?: boolean }\n): AsyncIterableIterator<R> {\n const reader = readable.getReader();\n return fromReader(reader, options);\n}\n\n/**\n * Converts a `ReadableStreamDefaultReader` into an `AsyncIterableIterator`.\n *\n * Allows you to use Readers in a `for await ... of` loop.\n */\nexport async function* fromReader<R>(\n reader: ReadableStreamDefaultReader<R>,\n options?: { preventCancel?: boolean }\n): AsyncIterableIterator<R> {\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n return value;\n }\n yield value;\n }\n } finally {\n if (!options?.preventCancel) {\n await reader.cancel();\n }\n reader.releaseLock();\n }\n}\n\nexport type UnderlyingSourceLockCallback = () => void;\n\n/**\n * `ReadableStream` with an optional lock callback.\n */\nexport class ExtendedReadableStream<R> extends ReadableStream<R> {\n #notifyLock?: () => void;\n\n constructor(\n {\n lock,\n ...underlyingSource\n }: UnderlyingSource & { lock?: UnderlyingSourceLockCallback },\n strategy?: QueuingStrategy<R>\n ) {\n super(underlyingSource, strategy);\n this.#notifyLock = lock;\n }\n\n override getReader() {\n const reader = super.getReader() as any;\n if (this.locked) {\n this.#notifyLock?.();\n }\n return reader;\n }\n\n override pipeThrough<T>(\n transform: ReadableWritablePair<T, R>,\n options?: StreamPipeOptions\n ): ReadableStream<T> {\n const stream = super.pipeThrough(transform, options);\n if (this.locked) {\n this.#notifyLock?.();\n }\n return stream;\n }\n\n override pipeTo(\n dest: WritableStream<R>,\n options?: StreamPipeOptions\n ): Promise<void> {\n const promise = super.pipeTo(dest, options);\n if (this.locked) {\n this.#notifyLock?.();\n }\n return promise;\n }\n\n override tee(): [ReadableStream<R>, ReadableStream<R>] {\n const [a, b] = super.tee();\n if (this.locked) {\n this.#notifyLock?.();\n }\n return [a, b];\n }\n}\n\n/**\n * Queues a microtask and returns a promise that resolves when\n * the microtask is executed.\n *\n * Microtasks are executed after the current task has completed,\n * but before the next task begins (tasks are the main unit of\n * work in the event loop).\n *\n * Useful when you want synchronous code from the current task to\n * complete before executing asynchronous code.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API/Microtask_guide\n */\nexport async function nextMicrotask() {\n return await new Promise<void>((resolve) => queueMicrotask(resolve));\n}\n"]}
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- var Y=(o,e)=>(e=Symbol[o])?e:Symbol.for("Symbol."+o),J=o=>{throw TypeError(o)};var p=(o,e,r)=>{if(e!=null){typeof e!="object"&&typeof e!="function"&&J("Object expected");var t,n;r&&(t=e[Y("asyncDispose")]),t===void 0&&(t=e[Y("dispose")],r&&(n=t)),typeof t!="function"&&J("Object not disposable"),n&&(t=function(){try{n.call(this)}catch(s){return Promise.reject(s)}}),o.push([r,t,e])}else r&&o.push([r]);return e},f=(o,e,r)=>{var t=typeof SuppressedError=="function"?SuppressedError:function(a,i,c,d){return d=Error(c),d.name="SuppressedError",d.error=a,d.suppressed=i,d},n=a=>e=r?new t(a,e,"An error was suppressed during disposal"):(r=!0,a),s=a=>{for(;a=o.pop();)try{var i=a[1]&&a[1].call(a[2]);if(a[0])return Promise.resolve(i).then(s,c=>(n(c),s()))}catch(c){n(c)}if(r)throw e};return s()};var y="dispose"in Symbol?Symbol.dispose:Symbol.for("Symbol.dispose");import{ConsoleStdout as ie,File as we,OpenFile as ve,WASI as _e}from"@bjorn3/browser_wasi_shim";import{DnsClient as Pe}from"@tcpip/dns";import{parseIPv4Address as te,parseMacAddress as le,serializeIPv4Cidr as fe,serializeMacAddress as ue}from"@tcpip/wire";var l=class{#e=new WeakMap;#t=new WeakMap;setOuter(e,r){this.#e.set(e,r)}setInner(e,r){this.#t.set(e,r)}getOuter(e){let r=this.#e.get(e);if(!r)throw new Error(`outer hooks not set for ${e}`);return r}getInner(e){let r=this.#t.get(e);if(!r)throw new Error(`inner hooks not set for ${e}`);return r}},H=class extends Number{free;constructor(e,r){super(e),this.free=r}[y](){this.free(this.valueOf())}},P=class extends Map{#e=new Map;wait(e){return new Promise(r=>{let t=this.#e.get(e)??new Set;t.add(r),this.#e.set(e,t)})}set(e,r){super.set(e,r);let t=this.#e.get(e);if(t)for(let n of t)n(r),t.delete(n);return this}};function w(o,e){let r=o.getReader();return ce(r,e)}async function*ce(o,e){try{for(;;){let{done:r,value:t}=await o.read();if(r)return t;yield t}}finally{e?.preventCancel||await o.cancel(),o.releaseLock()}}var T=class extends ReadableStream{#e;constructor({lock:e,...r},t){super(r,t),this.#e=e}getReader(){let e=super.getReader();return this.locked&&this.#e?.(),e}pipeThrough(e,r){let t=super.pipeThrough(e,r);return this.locked&&this.#e?.(),t}pipeTo(e,r){let t=super.pipeTo(e,r);return this.locked&&this.#e?.(),t}tee(){let[e,r]=super.tee();return this.locked&&this.#e?.(),[e,r]}};async function g(){return await new Promise(o=>queueMicrotask(o))}function O(){let o=new Uint8Array(6);return crypto.getRandomValues(o),o[0]=o[0]&252|2,o}var u=class{#e;get exports(){if(!this.#e)throw new Error("exports were not registered");return this.#e}register(e){this.#e=e}smartMalloc(e){return new H(this.exports.malloc(e),this.exports.free)}copyToMemory(e){let r=new Uint8Array(e),t=r.length,n=this.smartMalloc(t);return new Uint8Array(this.exports.memory.buffer,n.valueOf(),t).set(r),n}copyFromMemory(e,r){let t=this.exports.memory.buffer.slice(Number(e),Number(e)+r);return new Uint8Array(t)}viewFromMemory(e,r){return new Uint8Array(this.exports.memory.buffer,Number(e),r)}};import{parseIPv4Address as ee,parseMacAddress as pe,serializeIPv4Cidr as de,serializeMacAddress as me}from"@tcpip/wire";var A={ERR_OK:0,ERR_MEM:-1,ERR_BUF:-2,ERR_TIMEOUT:-3,ERR_RTE:-4,ERR_INPROGRESS:-5,ERR_VAL:-6,ERR_WOULDBLOCK:-7,ERR_USE:-8,ERR_ALREADY:-9,ERR_ISCONN:-10,ERR_CONN:-11,ERR_IF:-12,ERR_ABRT:-13,ERR_RST:-14,ERR_CLSD:-15,ERR_ARG:-16};var k=new l,U=class extends u{interfaces=new Map;imports={register_tap_interface:e=>{let r=new j;k.setOuter(r,{handle:e,sendFrame:t=>{let n=this.copyToMemory(t),s=this.exports.send_tap_interface(e,n,t.length);if(s!==A.ERR_OK)throw new Error(`failed to send frame: ${s}`)},getMacAddress:()=>{let t=this.exports.get_interface_mac_address(e),n=this.viewFromMemory(t,6);return pe(n)},getIPv4Address:()=>{let t=this.exports.get_interface_ip4_address(e);if(t===0)return;let n=this.viewFromMemory(t,4);return ee(n)},getIPv4Netmask:()=>{let t=this.exports.get_interface_ip4_netmask(e);if(t===0)return;let n=this.viewFromMemory(t,4);return ee(n)}}),this.interfaces.set(e,r)},receive_frame:async(e,r,t)=>{let n=this.copyFromMemory(r,t);await g();let s=this.interfaces.get(e);if(!s){console.error("received frame on unknown tap interface");return}k.getInner(s).receiveFrame(new Uint8Array(n))}};async create(e){var m=[];try{let r=e.mac?me(e.mac):O();let{ipAddress:t,netmask:n}=e.ip?de(e.ip):{};let s=p(m,this.copyToMemory(r));let a=p(m,t?this.copyToMemory(t):void 0);let i=p(m,n?this.copyToMemory(n):void 0);let c=this.exports.create_tap_interface(s,a??0,i??0);let d=this.interfaces.get(c);if(!d)throw new Error("tap interface failed to register");return d}catch(h){var b=h,_=!0}finally{f(m,b,_)}}async remove(e){for(let[r,t]of this.interfaces.entries())if(t===e){this.exports.remove_tap_interface(r),this.interfaces.delete(r);return}}},j=class{#e;#t=!1;type="tap";get mac(){return k.getOuter(this).getMacAddress()}get ip(){return k.getOuter(this).getIPv4Address()}get netmask(){return k.getOuter(this).getIPv4Netmask()}readable;writable;constructor(){k.setInner(this,{receiveFrame:async e=>{if(this.#t){if(!this.#e)throw new Error("readable stream not initialized");this.#e.enqueue(e)}}}),this.readable=new T({start:e=>{this.#e=e},lock:()=>{this.#t=!0}}),this.writable=new WritableStream({write:e=>{try{k.getOuter(this).sendFrame(e)}catch(r){console.error("tap interface send failed",r)}}})}listen(){if(this.readable.locked)throw new Error("readable stream already locked");return w(this.readable)}[Symbol.asyncIterator](){return this.listen()}};var E=new l,C=class extends u{interfaces=new Map;imports={};async create(e){var b=[];try{let r=e.mac?ue(e.mac):O();let{ipAddress:t,netmask:n}=e.ip?fe(e.ip):{};let s=p(b,this.copyToMemory(r));let a=p(b,t?this.copyToMemory(t):void 0);let i=p(b,n?this.copyToMemory(n):void 0);let c=new Uint32Array(e.ports.map(I=>Number(k.getOuter(I).handle)));let d=p(b,this.copyToMemory(c.buffer));let m=this.exports.create_bridge_interface(s,a??0,i??0,d,e.ports.length);let h=new K;E.setOuter(h,{handle:m,getMacAddress:()=>{let I=this.exports.get_interface_mac_address(m),S=this.viewFromMemory(I,6);return le(S)},getIPv4Address:()=>{let I=this.exports.get_interface_ip4_address(m);if(I===0)return;let S=this.viewFromMemory(I,4);return te(S)},getIPv4Netmask:()=>{let I=this.exports.get_interface_ip4_netmask(m);if(I===0)return;let S=this.viewFromMemory(I,4);return te(S)}});this.interfaces.set(m,h);return h}catch(_){var D=_,F=!0}finally{f(b,D,F)}}async remove(e){for(let[r,t]of this.interfaces.entries())if(t===e){this.exports.remove_bridge_interface(r),this.interfaces.delete(r);return}}},K=class{type="bridge";get mac(){return E.getOuter(this).getMacAddress()}get ip(){return E.getOuter(this).getIPv4Address()}get netmask(){return E.getOuter(this).getIPv4Netmask()}};import{parseIPv4Address as re,serializeIPv4Cidr as ye}from"@tcpip/wire";var z=new l,M=class extends u{interfaces=new Map;imports={register_loopback_interface:e=>{let r=new q;z.setOuter(r,{handle:e,getIPv4Address:()=>{let t=this.exports.get_interface_ip4_address(e);if(t===0)return;let n=this.viewFromMemory(t,4);return re(n)},getIPv4Netmask:()=>{let t=this.exports.get_interface_ip4_netmask(e);if(t===0)return;let n=this.viewFromMemory(t,4);return re(n)}}),this.interfaces.set(e,r)}};async create(e){var c=[];try{let{ipAddress:r,netmask:t}=e.ip?ye(e.ip):{};let n=p(c,r?this.copyToMemory(r):void 0);let s=p(c,t?this.copyToMemory(t):void 0);let a=this.exports.create_loopback_interface(n??0,s??0);let i=this.interfaces.get(a);if(!i)throw new Error("loopback interface failed to register");return i}catch(d){var m=d,h=!0}finally{f(c,m,h)}}async remove(e){for(let[r,t]of this.interfaces.entries())if(t===e){this.exports.remove_loopback_interface(r),this.interfaces.delete(r);return}}},q=class{type="loopback";get ip(){return z.getOuter(this).getIPv4Address()}get netmask(){return z.getOuter(this).getIPv4Netmask()}};import{serializeIPv4Address as ne}from"@tcpip/wire";var $=new l,v=new l,Q=1460,ot=Q*4,at=Q*4,he=Q,L=class extends u{#e=new Map;#t=new P;#r=new Map;#n;async#s(e){try{return ne(e)}catch{let t=await this.#n.lookup(e);return ne(t)}}constructor(e){super(),this.#n=e}imports={accept_tcp_connection:async(e,r)=>{let t=this.#e.get(e);if(!t){console.error("new tcp connection to unknown listener");return}await g();let n=new B;v.setOuter(n,{send:async s=>{let a=Number(this.copyToMemory(s)),i=this.exports.send_tcp_chunk(r,a,s.length);for(;i<s.length;){await new Promise(d=>{this.#r.set(r,d)});let c=s.length-i;i+=this.exports.send_tcp_chunk(r,a+i,c)}},updateReceiveBuffer:s=>{this.exports.update_tcp_receive_buffer(r,s)},close:async()=>{let s=this.exports.close_tcp_connection(r);if(s!==A.ERR_OK)throw new Error(`failed to close tcp connection: ${s}`)}}),this.#t.set(r,n),$.getInner(t).accept(n)},connected_tcp_connection:async e=>{await g();let r=new B;v.setOuter(r,{send:async t=>{let n=Number(this.copyToMemory(t)),s=this.exports.send_tcp_chunk(e,n,t.length);for(;s<t.length;){await new Promise(i=>{this.#r.set(e,i)});let a=t.length-s;s+=this.exports.send_tcp_chunk(e,n+s,a)}},updateReceiveBuffer:t=>{this.exports.update_tcp_receive_buffer(e,t)},close:async()=>{this.exports.close_tcp_connection(e)}}),this.#t.set(e,r)},closed_tcp_connection:async e=>{let r=this.#t.get(e);if(!r){console.error("received close on unknown tcp connection");return}await v.getInner(r).close()},receive_tcp_chunk:async(e,r,t)=>{let n=this.copyFromMemory(r,t),s=this.#t.get(e);if(!s){console.error("received chunk on unknown tcp connection");return}await g(),v.getInner(s).receive(new Uint8Array(n))},sent_tcp_chunk:(e,r)=>{let t=this.#r.get(e);this.#r.delete(e),t?.(r)}};async listen(e){var s=[];try{let r=p(s,e.host?this.copyToMemory(await this.#s(e.host)):null);let t=this.exports.create_tcp_listener(r,e.port);let n=new G;$.setOuter(n,{});this.#e.set(t,n);return n}catch(a){var i=a,c=!0}finally{f(s,i,c)}}async connect(e){var s=[];try{let r=p(s,this.copyToMemory(await this.#s(e.host)));let t=this.exports.create_tcp_connection(r,e.port);let n=await this.#t.wait(t);if(!n)throw new Error("tcp failed to connect");return n}catch(a){var i=a,c=!0}finally{f(s,i,c)}}},G=class{#e=[];#t;constructor(){$.setInner(this,{accept:async e=>{this.#e.push(e),this.#t?.()}})}async*[Symbol.asyncIterator](){for(;;)await new Promise(e=>{this.#t=e}),yield*this.#e,this.#e=[]}},B=class{#e=[];#t;#r;readable;writable;constructor(){v.setInner(this,{receive:async e=>{this.#e.push(e),this.#n()},close:async()=>{this.close()}}),this.readable=new ReadableStream({start:e=>{this.#t=e},pull:()=>{this.#n()}},{highWaterMark:he,size:e=>e.byteLength}),this.writable=new WritableStream({start:e=>{this.#r=e},write:async e=>{await v.getOuter(this).send(e)}},{highWaterMark:0})}#n(){if(!this.#t?.desiredSize)return;let e=0;for(;this.#e.length>0;){let r=this.#e[0].length;if(e+r>this.#t.desiredSize)break;let t=this.#e.shift();this.#t.enqueue(t),e+=t.length}e>0&&v.getOuter(this).updateReceiveBuffer(e)}async close(){await v.getOuter(this).close(),this.#t?.error(new Error("tcp connection closed")),this.#r?.error(new Error("tcp connection closed"))}[Symbol.asyncIterator](){if(this.readable.locked)throw new Error("readable stream already locked");return w(this.readable)}};import{parseIPv4Address as se,serializeIPv4Cidr as Ie}from"@tcpip/wire";var x=new l,N=class extends u{interfaces=new Map;imports={register_tun_interface:e=>{let r=new Z;x.setOuter(r,{handle:e,sendPacket:t=>{let n=this.copyToMemory(t);this.exports.send_tun_interface(e,n,t.length)},getIPv4Address:()=>{let t=this.exports.get_interface_ip4_address(e);if(t===0)return;let n=this.viewFromMemory(t,4);return se(n)},getIPv4Netmask:()=>{let t=this.exports.get_interface_ip4_netmask(e);if(t===0)return;let n=this.viewFromMemory(t,4);return se(n)}}),this.interfaces.set(e,r)},receive_packet:async(e,r,t)=>{let n=this.copyFromMemory(r,t);await g();let s=this.interfaces.get(e);if(!s){console.error("received packet on unknown tun interface");return}x.getInner(s).receivePacket(new Uint8Array(n))}};async create(e){var c=[];try{let{ipAddress:r,netmask:t}=e.ip?Ie(e.ip):{};let n=p(c,r?this.copyToMemory(r):void 0);let s=p(c,t?this.copyToMemory(t):void 0);let a=this.exports.create_tun_interface(n??0,s??0);let i=this.interfaces.get(a);if(!i)throw new Error("tun interface failed to register");return i}catch(d){var m=d,h=!0}finally{f(c,m,h)}}async remove(e){for(let[r,t]of this.interfaces.entries())if(t===e){this.exports.remove_tun_interface(r),this.interfaces.delete(r);return}}},Z=class{#e;#t=!1;type="tun";get ip(){return x.getOuter(this).getIPv4Address()}get netmask(){return x.getOuter(this).getIPv4Netmask()}readable;writable;constructor(){x.setInner(this,{receivePacket:async e=>{if(this.#t){if(!this.#e)throw new Error("readable stream not initialized");this.#e?.enqueue(e)}}}),this.readable=new T({start:e=>{this.#e=e},lock:()=>{this.#t=!0}}),this.writable=new WritableStream({write:e=>{x.getOuter(this).sendPacket(e)}})}listen(){if(this.readable.locked)throw new Error("readable stream already locked");return w(this.readable)}[Symbol.asyncIterator](){return this.listen()}};import{parseIPv4Address as be,serializeIPv4Address as oe}from"@tcpip/wire";var R=new l,W=class extends u{#e=new P;#t;async#r(e){try{return oe(e)}catch{let t=await this.#t.lookup(e);return oe(t)}}constructor(e){super(),this.#t=e}imports={receive_udp_datagram:async(e,r,t,n,s)=>{let a=this.copyFromMemory(r,4),i=this.copyFromMemory(n,s),c=this.#e.get(e);if(!c){console.error("received datagram on unknown udp socket");return}await g(),R.getInner(c).receive({host:be(a),port:t,data:i})}};async open(e){var s=[];try{let r=p(s,e.host?this.copyToMemory(await this.#r(e.host)):null);let t=this.exports.open_udp_socket(r,e.port??0);if(Number(t)===0)throw new Error("failed to open udp socket");let n=new X;R.setOuter(n,{send:async d=>{var _=[];try{let m=p(_,this.copyToMemory(await this.#r(d.host)));let h=p(_,this.copyToMemory(d.data));let b=this.exports.send_udp_datagram(t,m,d.port,h,d.data.length);if(b!==A.ERR_OK)throw new Error(`failed to send udp datagram: ${b}`)}catch(D){var F=D,I=!0}finally{f(_,F,I)}},close:async()=>{this.exports.close_udp_socket(t),this.#e.delete(t)}});this.#e.set(t,n);return n}catch(a){var i=a,c=!0}finally{f(s,i,c)}}},X=class{#e;#t;readable;writable;constructor(){R.setInner(this,{receive:async e=>{if(!this.#e)throw new Error("readable controller not initialized");this.#e.enqueue(e)}}),this.readable=new ReadableStream({start:e=>{this.#e=e}}),this.writable=new WritableStream({start:e=>{this.#t=e},write:async e=>{await R.getOuter(this).send(e)}})}async close(){await R.getOuter(this).close(),this.#e?.error(new Error("udp socket closed")),this.#t?.error(new Error("udp socket closed"))}[Symbol.asyncIterator](){if(this.readable.locked)throw new Error("readable stream already locked");return w(this.readable)}};var ge=typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string";async function ae(o,e){return ge?ke(o,e):fetch(o)}async function ke(o,e){let r=await import("node:fs"),{Readable:t}=await import("node:stream"),n=r.createReadStream(o),s=t.toWeb(n);return new Response(s,{headers:{"Content-Type":e}})}async function Te(o){let e=new V(o);return await e.ready,e}var V=class{#e;#t;#r;#n;#s;#o;#a;#i;#c;ready;get interfaces(){return this.#d()}constructor(e={}){this.#e={...e,initializeLoopback:e.initializeLoopback??!0},this.#r=new Pe(this,{nameServer:e.nameServer??{ip:"127.0.0.1",port:53}}),this.#n=new M,this.#s=new N,this.#o=new U,this.#a=new C,this.#i=new L(this.#r),this.#c=new W(this.#r),this.ready=this.#p(),this.ready.then(async()=>{this.#e.initializeLoopback&&await this.createLoopbackInterface({ip:"127.0.0.1/8"})})}async#p(){let e=new _e([],[],[new ve(new we([])),ie.lineBuffered(a=>console.log(`[WASI stdout] ${a}`)),ie.lineBuffered(a=>console.warn(`[WASI stderr] ${a}`))]),r=ae(new URL("../tcpip.wasm",import.meta.url),"application/wasm"),{instance:t}=await WebAssembly.instantiateStreaming(r,{wasi_snapshot_preview1:e.wasiImport,env:{...this.#n.imports,...this.#s.imports,...this.#o.imports,...this.#a.imports,...this.#i.imports,...this.#c.imports}}),n=t;this.#n.register(n.exports),this.#s.register(n.exports),this.#o.register(n.exports),this.#a.register(n.exports),this.#i.register(n.exports),this.#c.register(n.exports);let s=e.start(n);if(s!==0)throw new Error(`wasi start failed with code ${s}`);this.#t=Number(setInterval(()=>{n.exports.process_queued_packets(),n.exports.process_timeouts()},100))}*#d(){yield*this.#n.interfaces.values(),yield*this.#s.interfaces.values(),yield*this.#o.interfaces.values(),yield*this.#a.interfaces.values()}async createLoopbackInterface(e){return await this.ready,this.#n.create(e)}async createTunInterface(e){return await this.ready,this.#s.create(e)}async createTapInterface(e={}){return await this.ready,this.#o.create(e)}async createBridgeInterface(e){return await this.ready,this.#a.create(e)}async removeInterface(e){switch(await this.ready,e.type){case"loopback":return this.#n.remove(e);case"tun":return this.#s.remove(e);case"tap":return this.#o.remove(e);case"bridge":return this.#a.remove(e);default:throw new Error("unknown interface type")}}async listenTcp(e){return await this.ready,this.#i.listen(e)}async connectTcp(e){return await this.ready,this.#i.connect(e)}async openUdp(e={}){return await this.ready,this.#c.open(e)}};export{Te as createStack};
1
+ var Y=(o,e)=>(e=Symbol[o])?e:Symbol.for("Symbol."+o),J=o=>{throw TypeError(o)};var p=(o,e,r)=>{if(e!=null){typeof e!="object"&&typeof e!="function"&&J("Object expected");var t,n;r&&(t=e[Y("asyncDispose")]),t===void 0&&(t=e[Y("dispose")],r&&(n=t)),typeof t!="function"&&J("Object not disposable"),n&&(t=function(){try{n.call(this)}catch(s){return Promise.reject(s)}}),o.push([r,t,e])}else r&&o.push([r]);return e},f=(o,e,r)=>{var t=typeof SuppressedError=="function"?SuppressedError:function(a,i,c,d){return d=Error(c),d.name="SuppressedError",d.error=a,d.suppressed=i,d},n=a=>e=r?new t(a,e,"An error was suppressed during disposal"):(r=!0,a),s=a=>{for(;a=o.pop();)try{var i=a[1]&&a[1].call(a[2]);if(a[0])return Promise.resolve(i).then(s,c=>(n(c),s()))}catch(c){n(c)}if(r)throw e};return s()};var y="dispose"in Symbol?Symbol.dispose:Symbol.for("Symbol.dispose");import{ConsoleStdout as ae,File as ve,OpenFile as _e,WASI as Pe}from"@bjorn3/browser_wasi_shim";import{DnsClient as Te}from"@tcpip/dns";import{generateMacAddress as le,parseIPv4Address as ee,parseMacAddress as fe,serializeIPv4Cidr as ue,serializeMacAddress as ye}from"@tcpip/wire";var l=class{#e=new WeakMap;#t=new WeakMap;setOuter(e,r){this.#e.set(e,r)}setInner(e,r){this.#t.set(e,r)}getOuter(e){let r=this.#e.get(e);if(!r)throw new Error(`outer hooks not set for ${e}`);return r}getInner(e){let r=this.#t.get(e);if(!r)throw new Error(`inner hooks not set for ${e}`);return r}},R=class extends Number{free;constructor(e,r){super(e),this.free=r}[y](){this.free(this.valueOf())}},P=class extends Map{#e=new Map;wait(e){return new Promise(r=>{let t=this.#e.get(e)??new Set;t.add(r),this.#e.set(e,t)})}set(e,r){super.set(e,r);let t=this.#e.get(e);if(t)for(let n of t)n(r),t.delete(n);return this}};function w(o,e){let r=o.getReader();return ie(r,e)}async function*ie(o,e){try{for(;;){let{done:r,value:t}=await o.read();if(r)return t;yield t}}finally{e?.preventCancel||await o.cancel(),o.releaseLock()}}var T=class extends ReadableStream{#e;constructor({lock:e,...r},t){super(r,t),this.#e=e}getReader(){let e=super.getReader();return this.locked&&this.#e?.(),e}pipeThrough(e,r){let t=super.pipeThrough(e,r);return this.locked&&this.#e?.(),t}pipeTo(e,r){let t=super.pipeTo(e,r);return this.locked&&this.#e?.(),t}tee(){let[e,r]=super.tee();return this.locked&&this.#e?.(),[e,r]}};async function g(){return await new Promise(o=>queueMicrotask(o))}var u=class{#e;get exports(){if(!this.#e)throw new Error("exports were not registered");return this.#e}register(e){this.#e=e}smartMalloc(e){return new R(this.exports.malloc(e),this.exports.free)}copyToMemory(e){let r=new Uint8Array(e),t=r.length,n=this.smartMalloc(t);return new Uint8Array(this.exports.memory.buffer,n.valueOf(),t).set(r),n}copyFromMemory(e,r){let t=this.exports.memory.buffer.slice(Number(e),Number(e)+r);return new Uint8Array(t)}viewFromMemory(e,r){return new Uint8Array(this.exports.memory.buffer,Number(e),r)}};import{generateMacAddress as ce,parseIPv4Address as V,parseMacAddress as pe,serializeIPv4Cidr as de,serializeMacAddress as me}from"@tcpip/wire";var A={ERR_OK:0,ERR_MEM:-1,ERR_BUF:-2,ERR_TIMEOUT:-3,ERR_RTE:-4,ERR_INPROGRESS:-5,ERR_VAL:-6,ERR_WOULDBLOCK:-7,ERR_USE:-8,ERR_ALREADY:-9,ERR_ISCONN:-10,ERR_CONN:-11,ERR_IF:-12,ERR_ABRT:-13,ERR_RST:-14,ERR_CLSD:-15,ERR_ARG:-16};var k=new l,O=class extends u{interfaces=new Map;imports={register_tap_interface:e=>{let r=new F;k.setOuter(r,{handle:e,sendFrame:t=>{let n=this.copyToMemory(t),s=this.exports.send_tap_interface(e,n,t.length);if(s!==A.ERR_OK)throw new Error(`failed to send frame: ${s}`)},getMacAddress:()=>{let t=this.exports.get_interface_mac_address(e),n=this.viewFromMemory(t,6);return pe(n)},getIPv4Address:()=>{let t=this.exports.get_interface_ip4_address(e);if(t===0)return;let n=this.viewFromMemory(t,4);return V(n)},getIPv4Netmask:()=>{let t=this.exports.get_interface_ip4_netmask(e);if(t===0)return;let n=this.viewFromMemory(t,4);return V(n)}}),this.interfaces.set(e,r)},receive_frame:async(e,r,t)=>{let n=this.copyFromMemory(r,t);await g();let s=this.interfaces.get(e);if(!s){console.error("received frame on unknown tap interface");return}k.getInner(s).receiveFrame(new Uint8Array(n))}};async create(e){var m=[];try{let r=e.mac?me(e.mac):ce();let{ipAddress:t,netmask:n}=e.ip?de(e.ip):{};let s=p(m,this.copyToMemory(r));let a=p(m,t?this.copyToMemory(t):void 0);let i=p(m,n?this.copyToMemory(n):void 0);let c=this.exports.create_tap_interface(s,a??0,i??0);let d=this.interfaces.get(c);if(!d)throw new Error("tap interface failed to register");return d}catch(h){var b=h,_=!0}finally{f(m,b,_)}}async remove(e){for(let[r,t]of this.interfaces.entries())if(t===e){this.exports.remove_tap_interface(r),this.interfaces.delete(r);return}}},F=class{#e;#t=!1;type="tap";get mac(){return k.getOuter(this).getMacAddress()}get ip(){return k.getOuter(this).getIPv4Address()}get netmask(){return k.getOuter(this).getIPv4Netmask()}readable;writable;constructor(){k.setInner(this,{receiveFrame:async e=>{if(this.#t){if(!this.#e)throw new Error("readable stream not initialized");this.#e.enqueue(e)}}}),this.readable=new T({start:e=>{this.#e=e},lock:()=>{this.#t=!0}}),this.writable=new WritableStream({write:e=>{try{k.getOuter(this).sendFrame(e)}catch(r){console.error("tap interface send failed",r)}}})}listen(){if(this.readable.locked)throw new Error("readable stream already locked");return w(this.readable)}[Symbol.asyncIterator](){return this.listen()}};var U=new l,E=class extends u{interfaces=new Map;imports={};async create(e){var b=[];try{let r=e.mac?ye(e.mac):le();let{ipAddress:t,netmask:n}=e.ip?ue(e.ip):{};let s=p(b,this.copyToMemory(r));let a=p(b,t?this.copyToMemory(t):void 0);let i=p(b,n?this.copyToMemory(n):void 0);let c=new Uint32Array(e.ports.map(I=>Number(k.getOuter(I).handle)));let d=p(b,this.copyToMemory(c.buffer));let m=this.exports.create_bridge_interface(s,a??0,i??0,d,e.ports.length);let h=new j;U.setOuter(h,{handle:m,getMacAddress:()=>{let I=this.exports.get_interface_mac_address(m),S=this.viewFromMemory(I,6);return fe(S)},getIPv4Address:()=>{let I=this.exports.get_interface_ip4_address(m);if(I===0)return;let S=this.viewFromMemory(I,4);return ee(S)},getIPv4Netmask:()=>{let I=this.exports.get_interface_ip4_netmask(m);if(I===0)return;let S=this.viewFromMemory(I,4);return ee(S)}});this.interfaces.set(m,h);return h}catch(_){var W=_,D=!0}finally{f(b,W,D)}}async remove(e){for(let[r,t]of this.interfaces.entries())if(t===e){this.exports.remove_bridge_interface(r),this.interfaces.delete(r);return}}},j=class{type="bridge";get mac(){return U.getOuter(this).getMacAddress()}get ip(){return U.getOuter(this).getIPv4Address()}get netmask(){return U.getOuter(this).getIPv4Netmask()}};import{parseIPv4Address as te,serializeIPv4Cidr as he}from"@tcpip/wire";var z=new l,C=class extends u{interfaces=new Map;imports={register_loopback_interface:e=>{let r=new K;z.setOuter(r,{handle:e,getIPv4Address:()=>{let t=this.exports.get_interface_ip4_address(e);if(t===0)return;let n=this.viewFromMemory(t,4);return te(n)},getIPv4Netmask:()=>{let t=this.exports.get_interface_ip4_netmask(e);if(t===0)return;let n=this.viewFromMemory(t,4);return te(n)}}),this.interfaces.set(e,r)}};async create(e){var c=[];try{let{ipAddress:r,netmask:t}=e.ip?he(e.ip):{};let n=p(c,r?this.copyToMemory(r):void 0);let s=p(c,t?this.copyToMemory(t):void 0);let a=this.exports.create_loopback_interface(n??0,s??0);let i=this.interfaces.get(a);if(!i)throw new Error("loopback interface failed to register");return i}catch(d){var m=d,h=!0}finally{f(c,m,h)}}async remove(e){for(let[r,t]of this.interfaces.entries())if(t===e){this.exports.remove_loopback_interface(r),this.interfaces.delete(r);return}}},K=class{type="loopback";get ip(){return z.getOuter(this).getIPv4Address()}get netmask(){return z.getOuter(this).getIPv4Netmask()}};import{serializeIPv4Address as re}from"@tcpip/wire";var q=new l,v=new l,G=1460,at=G*4,it=G*4,Ie=G,M=class extends u{#e=new Map;#t=new P;#r=new Map;#n;async#s(e){try{return re(e)}catch{let t=await this.#n.lookup(e);return re(t)}}constructor(e){super(),this.#n=e}imports={accept_tcp_connection:async(e,r)=>{let t=this.#e.get(e);if(!t){console.error("new tcp connection to unknown listener");return}await g();let n=new L;v.setOuter(n,{send:async s=>{let a=Number(this.copyToMemory(s)),i=this.exports.send_tcp_chunk(r,a,s.length);for(;i<s.length;){await new Promise(d=>{this.#r.set(r,d)});let c=s.length-i;i+=this.exports.send_tcp_chunk(r,a+i,c)}},updateReceiveBuffer:s=>{this.exports.update_tcp_receive_buffer(r,s)},close:async()=>{let s=this.exports.close_tcp_connection(r);if(s!==A.ERR_OK)throw new Error(`failed to close tcp connection: ${s}`)}}),this.#t.set(r,n),q.getInner(t).accept(n)},connected_tcp_connection:async e=>{await g();let r=new L;v.setOuter(r,{send:async t=>{let n=Number(this.copyToMemory(t)),s=this.exports.send_tcp_chunk(e,n,t.length);for(;s<t.length;){await new Promise(i=>{this.#r.set(e,i)});let a=t.length-s;s+=this.exports.send_tcp_chunk(e,n+s,a)}},updateReceiveBuffer:t=>{this.exports.update_tcp_receive_buffer(e,t)},close:async()=>{this.exports.close_tcp_connection(e)}}),this.#t.set(e,r)},closed_tcp_connection:async e=>{let r=this.#t.get(e);if(!r){console.error("received close on unknown tcp connection");return}await v.getInner(r).close()},receive_tcp_chunk:async(e,r,t)=>{let n=this.copyFromMemory(r,t),s=this.#t.get(e);if(!s){console.error("received chunk on unknown tcp connection");return}await g(),v.getInner(s).receive(new Uint8Array(n))},sent_tcp_chunk:(e,r)=>{let t=this.#r.get(e);this.#r.delete(e),t?.(r)}};async listen(e){var s=[];try{let r=p(s,e.host?this.copyToMemory(await this.#s(e.host)):null);let t=this.exports.create_tcp_listener(r,e.port);let n=new $;q.setOuter(n,{});this.#e.set(t,n);return n}catch(a){var i=a,c=!0}finally{f(s,i,c)}}async connect(e){var s=[];try{let r=p(s,this.copyToMemory(await this.#s(e.host)));let t=this.exports.create_tcp_connection(r,e.port);let n=await this.#t.wait(t);if(!n)throw new Error("tcp failed to connect");return n}catch(a){var i=a,c=!0}finally{f(s,i,c)}}},$=class{#e=[];#t;constructor(){q.setInner(this,{accept:async e=>{this.#e.push(e),this.#t?.()}})}async*[Symbol.asyncIterator](){for(;;)await new Promise(e=>{this.#t=e}),yield*this.#e,this.#e=[]}},L=class{#e=[];#t;#r;readable;writable;constructor(){v.setInner(this,{receive:async e=>{this.#e.push(e),this.#n()},close:async()=>{this.close()}}),this.readable=new ReadableStream({start:e=>{this.#t=e},pull:()=>{this.#n()}},{highWaterMark:Ie,size:e=>e.byteLength}),this.writable=new WritableStream({start:e=>{this.#r=e},write:async e=>{await v.getOuter(this).send(e)}},{highWaterMark:0})}#n(){if(!this.#t?.desiredSize)return;let e=0;for(;this.#e.length>0;){let r=this.#e[0].length;if(e+r>this.#t.desiredSize)break;let t=this.#e.shift();this.#t.enqueue(t),e+=t.length}e>0&&v.getOuter(this).updateReceiveBuffer(e)}async close(){await v.getOuter(this).close(),this.#t?.error(new Error("tcp connection closed")),this.#r?.error(new Error("tcp connection closed"))}[Symbol.asyncIterator](){if(this.readable.locked)throw new Error("readable stream already locked");return w(this.readable)}};import{parseIPv4Address as ne,serializeIPv4Cidr as be}from"@tcpip/wire";var x=new l,B=class extends u{interfaces=new Map;imports={register_tun_interface:e=>{let r=new Q;x.setOuter(r,{handle:e,sendPacket:t=>{let n=this.copyToMemory(t);this.exports.send_tun_interface(e,n,t.length)},getIPv4Address:()=>{let t=this.exports.get_interface_ip4_address(e);if(t===0)return;let n=this.viewFromMemory(t,4);return ne(n)},getIPv4Netmask:()=>{let t=this.exports.get_interface_ip4_netmask(e);if(t===0)return;let n=this.viewFromMemory(t,4);return ne(n)}}),this.interfaces.set(e,r)},receive_packet:async(e,r,t)=>{let n=this.copyFromMemory(r,t);await g();let s=this.interfaces.get(e);if(!s){console.error("received packet on unknown tun interface");return}x.getInner(s).receivePacket(new Uint8Array(n))}};async create(e){var c=[];try{let{ipAddress:r,netmask:t}=e.ip?be(e.ip):{};let n=p(c,r?this.copyToMemory(r):void 0);let s=p(c,t?this.copyToMemory(t):void 0);let a=this.exports.create_tun_interface(n??0,s??0);let i=this.interfaces.get(a);if(!i)throw new Error("tun interface failed to register");return i}catch(d){var m=d,h=!0}finally{f(c,m,h)}}async remove(e){for(let[r,t]of this.interfaces.entries())if(t===e){this.exports.remove_tun_interface(r),this.interfaces.delete(r);return}}},Q=class{#e;#t=!1;type="tun";get ip(){return x.getOuter(this).getIPv4Address()}get netmask(){return x.getOuter(this).getIPv4Netmask()}readable;writable;constructor(){x.setInner(this,{receivePacket:async e=>{if(this.#t){if(!this.#e)throw new Error("readable stream not initialized");this.#e?.enqueue(e)}}}),this.readable=new T({start:e=>{this.#e=e},lock:()=>{this.#t=!0}}),this.writable=new WritableStream({write:e=>{x.getOuter(this).sendPacket(e)}})}listen(){if(this.readable.locked)throw new Error("readable stream already locked");return w(this.readable)}[Symbol.asyncIterator](){return this.listen()}};import{parseIPv4Address as ge,serializeIPv4Address as se}from"@tcpip/wire";var H=new l,N=class extends u{#e=new P;#t;async#r(e){try{return se(e)}catch{let t=await this.#t.lookup(e);return se(t)}}constructor(e){super(),this.#t=e}imports={receive_udp_datagram:async(e,r,t,n,s)=>{let a=this.copyFromMemory(r,4),i=this.copyFromMemory(n,s),c=this.#e.get(e);if(!c){console.error("received datagram on unknown udp socket");return}await g(),H.getInner(c).receive({host:ge(a),port:t,data:i})}};async open(e){var s=[];try{let r=p(s,e.host?this.copyToMemory(await this.#r(e.host)):null);let t=this.exports.open_udp_socket(r,e.port??0);if(Number(t)===0)throw new Error("failed to open udp socket");let n=new Z;H.setOuter(n,{send:async d=>{var _=[];try{let m=p(_,this.copyToMemory(await this.#r(d.host)));let h=p(_,this.copyToMemory(d.data));let b=this.exports.send_udp_datagram(t,m,d.port,h,d.data.length);if(b!==A.ERR_OK)throw new Error(`failed to send udp datagram: ${b}`)}catch(W){var D=W,I=!0}finally{f(_,D,I)}},close:async()=>{this.exports.close_udp_socket(t),this.#e.delete(t)}});this.#e.set(t,n);return n}catch(a){var i=a,c=!0}finally{f(s,i,c)}}},Z=class{#e;#t;readable;writable;constructor(){H.setInner(this,{receive:async e=>{if(!this.#e)throw new Error("readable controller not initialized");this.#e.enqueue(e)}}),this.readable=new ReadableStream({start:e=>{this.#e=e}}),this.writable=new WritableStream({start:e=>{this.#t=e},write:async e=>{await H.getOuter(this).send(e)}})}async close(){await H.getOuter(this).close(),this.#e?.error(new Error("udp socket closed")),this.#t?.error(new Error("udp socket closed"))}[Symbol.asyncIterator](){if(this.readable.locked)throw new Error("readable stream already locked");return w(this.readable)}};var ke=typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string";async function oe(o,e){return ke?we(o,e):fetch(o)}async function we(o,e){let r=await import("node:fs"),{Readable:t}=await import("node:stream"),n=r.createReadStream(o),s=t.toWeb(n);return new Response(s,{headers:{"Content-Type":e}})}async function Ae(o){let e=new X(o);return await e.ready,e}var X=class{#e;#t;#r;#n;#s;#o;#a;#i;#c;ready;get interfaces(){return this.#d()}constructor(e={}){this.#e={...e,initializeLoopback:e.initializeLoopback??!0},this.#r=new Te(this,{nameServer:e.nameServer??{ip:"127.0.0.1",port:53}}),this.#n=new C,this.#s=new B,this.#o=new O,this.#a=new E,this.#i=new M(this.#r),this.#c=new N(this.#r),this.ready=this.#p(),this.ready.then(async()=>{this.#e.initializeLoopback&&await this.createLoopbackInterface({ip:"127.0.0.1/8"})})}async#p(){let e=new Pe([],[],[new _e(new ve([])),ae.lineBuffered(s=>console.log(`[WASI stdout] ${s}`)),ae.lineBuffered(s=>console.warn(`[WASI stderr] ${s}`))]),r=oe(new URL("../tcpip.wasm",import.meta.url),"application/wasm"),{instance:t}=await WebAssembly.instantiateStreaming(r,{wasi_snapshot_preview1:e.wasiImport,env:{...this.#n.imports,...this.#s.imports,...this.#o.imports,...this.#a.imports,...this.#i.imports,...this.#c.imports}}),n=t;this.#n.register(n.exports),this.#s.register(n.exports),this.#o.register(n.exports),this.#a.register(n.exports),this.#i.register(n.exports),this.#c.register(n.exports),e.initialize(n),this.#t=Number(setInterval(()=>{n.exports.process_queued_packets(),n.exports.process_timeouts()},100))}*#d(){yield*this.#n.interfaces.values(),yield*this.#s.interfaces.values(),yield*this.#o.interfaces.values(),yield*this.#a.interfaces.values()}async createLoopbackInterface(e){return await this.ready,this.#n.create(e)}async createTunInterface(e){return await this.ready,this.#s.create(e)}async createTapInterface(e={}){return await this.ready,this.#o.create(e)}async createBridgeInterface(e){return await this.ready,this.#a.create(e)}async removeInterface(e){switch(await this.ready,e.type){case"loopback":return this.#n.remove(e);case"tun":return this.#s.remove(e);case"tap":return this.#o.remove(e);case"bridge":return this.#a.remove(e);default:throw new Error("unknown interface type")}}async listenTcp(e){return await this.ready,this.#i.listen(e)}async connectTcp(e){return await this.ready,this.#i.connect(e)}async openUdp(e={}){return await this.ready,this.#c.open(e)}};export{Ae as createStack};
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../polyfills/disposable.ts","../src/stack.ts","../src/bindings/bridge-interface.ts","../src/util.ts","../src/bindings/base.ts","../src/bindings/tap-interface.ts","../src/lwip/errors.ts","../src/bindings/loopback-interface.ts","../src/bindings/tcp.ts","../src/bindings/tun-interface.ts","../src/bindings/udp.ts","../src/fetch-file.ts"],"sourcesContent":["/**\n * Scoped polyfill for `Symbol.dispose` without polluting the global scope.\n * Required for the `using` keyword which we use internally.\n * \n * We export this symbol as 'Symbol.dispose' which tells ESBuild to inject\n * it into the output bundle.\n * \n * The below works because Typescript's `using` implementation falls back to\n * `Symbol.for(\"Symbol.dispose\")` if the built-in `Symbol.dispose` is not available.\n */\n\nconst DisposeSymbol = 'dispose' in (Symbol as object) ? Symbol.dispose : Symbol.for('Symbol.dispose');\n\nexport {\n DisposeSymbol as 'Symbol.dispose'\n};\n","import { ConsoleStdout, File, OpenFile, WASI } from '@bjorn3/browser_wasi_shim';\nimport { DnsClient, type NameServer } from '@tcpip/dns';\nimport {\n BridgeBindings,\n type BridgeInterface,\n type BridgeInterfaceOptions,\n} from './bindings/bridge-interface.js';\nimport {\n LoopbackBindings,\n type LoopbackInterface,\n type LoopbackInterfaceOptions,\n} from './bindings/loopback-interface.js';\nimport {\n TapBindings,\n type TapInterface,\n type TapInterfaceOptions,\n} from './bindings/tap-interface.js';\nimport {\n TcpBindings,\n type TcpConnection,\n type TcpConnectionOptions,\n type TcpListener,\n type TcpListenerOptions,\n} from './bindings/tcp.js';\nimport {\n TunBindings,\n type TunInterface,\n type TunInterfaceOptions,\n} from './bindings/tun-interface.js';\nimport {\n UdpBindings,\n type UdpSocket,\n type UdpSocketOptions,\n} from './bindings/udp.js';\nimport { fetchFile } from './fetch-file.js';\nimport type { NetworkInterface, WasmInstance } from './types.js';\n\nexport async function createStack(\n options?: NetworkStackOptions\n): Promise<NetworkStack> {\n const stack = new VirtualNetworkStack(options);\n await stack.ready;\n return stack;\n}\n\nexport type NetworkStackOptions = {\n /**\n * Whether to initialize a loopback interface on startup.\n *\n * @default true\n */\n initializeLoopback?: boolean;\n\n /**\n * Name server used for DNS resolution.\n *\n * @default { ip: '127.0.0.1', port: 53 }\n */\n nameServer?: NameServer;\n};\n\nexport type NetworkStack = {\n readonly ready: Promise<void>;\n readonly interfaces: Iterable<NetworkInterface>;\n\n createLoopbackInterface(\n options: LoopbackInterfaceOptions\n ): Promise<LoopbackInterface>;\n createTunInterface(options: TunInterfaceOptions): Promise<TunInterface>;\n createTapInterface(options?: TapInterfaceOptions): Promise<TapInterface>;\n createBridgeInterface(\n options: BridgeInterfaceOptions\n ): Promise<BridgeInterface>;\n removeInterface(\n netInterface: LoopbackInterface | TunInterface | TapInterface\n ): Promise<void>;\n /**\n * Listens for incoming TCP connections on the specified host/port.\n */\n listenTcp(options: TcpListenerOptions): Promise<TcpListener>;\n /**\n * Establishes an outbound TCP connection to a remote host/port.\n */\n connectTcp(options: TcpConnectionOptions): Promise<TcpConnection>;\n /**\n * Opens a UDP socket for sending and receiving datagrams.\n *\n * If no local host is provided, the socket will bind to all available interfaces.\n * If no local port is provided, the socket will bind to a random port.\n */\n openUdp(options?: UdpSocketOptions): Promise<UdpSocket>;\n};\n\nexport class VirtualNetworkStack implements NetworkStack {\n #options: NetworkStackOptions;\n #loopIntervalId?: number;\n #dnsClient: DnsClient;\n\n #loopbackBindings: LoopbackBindings;\n #tunBindings: TunBindings;\n #tapBindings: TapBindings;\n #bridgeBindings: BridgeBindings;\n #tcpBindings: TcpBindings;\n #udpBindings: UdpBindings;\n\n ready: Promise<void>;\n get interfaces() {\n return this.#listInterfaces();\n }\n\n constructor(options: NetworkStackOptions = {}) {\n this.#options = {\n ...options,\n initializeLoopback: options.initializeLoopback ?? true,\n };\n\n this.#dnsClient = new DnsClient(this, {\n nameServer: options.nameServer ?? { ip: '127.0.0.1', port: 53 },\n });\n\n // Initialize bindings\n this.#loopbackBindings = new LoopbackBindings();\n this.#tunBindings = new TunBindings();\n this.#tapBindings = new TapBindings();\n this.#bridgeBindings = new BridgeBindings();\n this.#tcpBindings = new TcpBindings(this.#dnsClient);\n this.#udpBindings = new UdpBindings(this.#dnsClient);\n\n // Initialize the stack\n this.ready = this.#init();\n\n // Post-init setup\n this.ready.then(async () => {\n if (this.#options.initializeLoopback) {\n await this.createLoopbackInterface({\n ip: '127.0.0.1/8',\n });\n }\n });\n }\n\n async #init() {\n const wasi = new WASI(\n [],\n [],\n [\n new OpenFile(new File([])), // stdin\n ConsoleStdout.lineBuffered((msg) =>\n console.log(`[WASI stdout] ${msg}`)\n ),\n ConsoleStdout.lineBuffered((msg) =>\n console.warn(`[WASI stderr] ${msg}`)\n ),\n ]\n );\n\n const source = fetchFile(\n new URL('../tcpip.wasm', import.meta.url),\n 'application/wasm'\n );\n\n // Instantiate with both WASI and custom imports\n const { instance } = await WebAssembly.instantiateStreaming(source, {\n wasi_snapshot_preview1: wasi.wasiImport,\n env: {\n ...this.#loopbackBindings.imports,\n ...this.#tunBindings.imports,\n ...this.#tapBindings.imports,\n ...this.#bridgeBindings.imports,\n ...this.#tcpBindings.imports,\n ...this.#udpBindings.imports,\n },\n });\n\n const wasmInstance = instance as WasmInstance;\n\n this.#loopbackBindings.register(wasmInstance.exports);\n this.#tunBindings.register(wasmInstance.exports);\n this.#tapBindings.register(wasmInstance.exports);\n this.#bridgeBindings.register(wasmInstance.exports);\n this.#tcpBindings.register(wasmInstance.exports);\n this.#udpBindings.register(wasmInstance.exports);\n\n const result = wasi.start(wasmInstance);\n\n if (result !== 0) {\n throw new Error(`wasi start failed with code ${result}`);\n }\n\n // Call lwIP's main loop regularly (required in NO_SYS mode)\n // Used to process queued packets (eg. loopback) and expired timeouts\n this.#loopIntervalId = Number(\n setInterval(() => {\n wasmInstance.exports.process_queued_packets();\n wasmInstance.exports.process_timeouts();\n }, 100)\n );\n }\n\n *#listInterfaces(): Iterable<NetworkInterface> {\n yield* this.#loopbackBindings.interfaces.values();\n yield* this.#tunBindings.interfaces.values();\n yield* this.#tapBindings.interfaces.values();\n yield* this.#bridgeBindings.interfaces.values();\n }\n\n async createLoopbackInterface(\n options: LoopbackInterfaceOptions\n ): Promise<LoopbackInterface> {\n await this.ready;\n return this.#loopbackBindings.create(options);\n }\n\n async createTunInterface(\n options: TunInterfaceOptions\n ): Promise<TunInterface> {\n await this.ready;\n return this.#tunBindings.create(options);\n }\n\n async createTapInterface(\n options: TapInterfaceOptions = {}\n ): Promise<TapInterface> {\n await this.ready;\n return this.#tapBindings.create(options);\n }\n\n async createBridgeInterface(options: BridgeInterfaceOptions) {\n await this.ready;\n return this.#bridgeBindings.create(options);\n }\n\n async removeInterface(netInterface: NetworkInterface) {\n await this.ready;\n\n switch (netInterface.type) {\n case 'loopback':\n return this.#loopbackBindings.remove(netInterface);\n case 'tun':\n return this.#tunBindings.remove(netInterface);\n case 'tap':\n return this.#tapBindings.remove(netInterface);\n case 'bridge':\n return this.#bridgeBindings.remove(netInterface);\n default:\n throw new Error('unknown interface type');\n }\n }\n\n /**\n * Listens for incoming TCP connections on the specified host/port.\n */\n async listenTcp(options: TcpListenerOptions) {\n await this.ready;\n return this.#tcpBindings.listen(options);\n }\n\n /**\n * Establishes an outbound TCP connection to a remote host/port.\n */\n async connectTcp(options: TcpConnectionOptions) {\n await this.ready;\n return this.#tcpBindings.connect(options);\n }\n\n /**\n * Opens a UDP socket for sending and receiving datagrams.\n *\n * If no local host is provided, the socket will bind to all available interfaces.\n * If no local port is provided, the socket will bind to a random port.\n */\n async openUdp(options: UdpSocketOptions = {}) {\n await this.ready;\n return this.#udpBindings.open(options);\n }\n}\n","import {\n parseIPv4Address,\n parseMacAddress,\n serializeIPv4Cidr,\n serializeMacAddress,\n type IPv4Address,\n type IPv4Cidr,\n type MacAddress,\n} from '@tcpip/wire';\nimport type { Pointer } from '../types.js';\nimport { generateMacAddress, Hooks } from '../util.js';\nimport { Bindings } from './base.js';\nimport { tapInterfaceHooks, type TapInterface } from './tap-interface.js';\n\ntype BridgeInterfaceHandle = Pointer;\n\ntype BridgeInterfaceOuterHooks = {\n handle: BridgeInterfaceHandle;\n getMacAddress(): MacAddress;\n getIPv4Address(): IPv4Address | undefined;\n getIPv4Netmask(): IPv4Address | undefined;\n};\n\ntype BridgeInterfaceInnerHooks = {};\n\nexport const bridgeInterfaceHooks = new Hooks<\n BridgeInterface,\n BridgeInterfaceOuterHooks,\n BridgeInterfaceInnerHooks\n>();\n\nexport type BridgeImports = {};\n\nexport type BridgeExports = {\n create_bridge_interface(\n macAddress: Pointer,\n ipAddress: Pointer,\n netmask: Pointer,\n ports: Pointer,\n ports_length: number\n ): BridgeInterfaceHandle;\n remove_bridge_interface(handle: BridgeInterfaceHandle): void;\n};\n\nexport class BridgeBindings extends Bindings<BridgeImports, BridgeExports> {\n interfaces = new Map<BridgeInterfaceHandle, BridgeInterface>();\n\n imports = {};\n\n async create(options: BridgeInterfaceOptions) {\n const macAddress = options.mac\n ? serializeMacAddress(options.mac)\n : generateMacAddress();\n\n const { ipAddress, netmask } = options.ip\n ? serializeIPv4Cidr(options.ip)\n : {};\n\n using macAddressPtr = this.copyToMemory(macAddress);\n using ipAddressPtr = ipAddress ? this.copyToMemory(ipAddress) : undefined;\n using netmaskPtr = netmask ? this.copyToMemory(netmask) : undefined;\n const portHandles = new Uint32Array(\n options.ports.map((port) =>\n Number(tapInterfaceHooks.getOuter(port).handle)\n )\n );\n\n using portHandlesPtr = this.copyToMemory(portHandles.buffer);\n\n const handle = this.exports.create_bridge_interface(\n macAddressPtr,\n ipAddressPtr ?? 0,\n netmaskPtr ?? 0,\n portHandlesPtr,\n options.ports.length\n );\n\n const bridgeInterface = new VirtualBridgeInterface();\n\n bridgeInterfaceHooks.setOuter(bridgeInterface, {\n handle,\n getMacAddress: () => {\n const macPtr = this.exports.get_interface_mac_address(handle);\n\n const macBytes = this.viewFromMemory(macPtr, 6);\n return parseMacAddress(macBytes);\n },\n getIPv4Address: () => {\n const ipPtr = this.exports.get_interface_ip4_address(handle);\n\n if (ipPtr === 0) {\n return;\n }\n\n const ipBytes = this.viewFromMemory(ipPtr, 4);\n return parseIPv4Address(ipBytes);\n },\n getIPv4Netmask: () => {\n const netmaskPtr = this.exports.get_interface_ip4_netmask(handle);\n\n if (netmaskPtr === 0) {\n return;\n }\n\n const netmaskBytes = this.viewFromMemory(netmaskPtr, 4);\n return parseIPv4Address(netmaskBytes);\n },\n });\n\n this.interfaces.set(handle, bridgeInterface);\n\n return bridgeInterface;\n }\n\n async remove(bridgeInterface: BridgeInterface) {\n for (const [handle, loopback] of this.interfaces.entries()) {\n if (loopback === bridgeInterface) {\n this.exports.remove_bridge_interface(handle);\n this.interfaces.delete(handle);\n return;\n }\n }\n }\n}\n\nexport type BridgeInterfaceOptions = {\n ports: TapInterface[];\n mac?: MacAddress;\n ip?: IPv4Cidr;\n};\n\nexport type BridgeInterface = {\n readonly type: 'bridge';\n readonly mac: MacAddress;\n readonly ip?: IPv4Address;\n readonly netmask?: IPv4Address;\n};\n\nexport class VirtualBridgeInterface implements BridgeInterface {\n readonly type = 'bridge';\n get mac(): MacAddress {\n return bridgeInterfaceHooks.getOuter(this).getMacAddress();\n }\n get ip(): IPv4Address | undefined {\n return bridgeInterfaceHooks.getOuter(this).getIPv4Address();\n }\n get netmask(): IPv4Address | undefined {\n return bridgeInterfaceHooks.getOuter(this).getIPv4Netmask();\n }\n}\n","/**\n * Utility class to facilitate internal communication\n * between bindings and JS instances.\n * Hooks are created for both the outer (bindings) and\n * inner (JS instance) sides of the communication.\n *\n * Uses `WeakMap` to map each JS instance to a set of\n * hooks while avoiding memory leaks.\n */\nexport class Hooks<K extends WeakKey, O, I> {\n #outerHooks = new WeakMap<K, O>();\n #innerHooks = new WeakMap<K, I>();\n\n setOuter(key: K, hooks: O) {\n this.#outerHooks.set(key, hooks);\n }\n\n setInner(key: K, hooks: I) {\n this.#innerHooks.set(key, hooks);\n }\n\n getOuter(key: K) {\n const hooks = this.#outerHooks.get(key);\n\n if (!hooks) {\n throw new Error(`outer hooks not set for ${key}`);\n }\n\n return hooks;\n }\n\n getInner(key: K) {\n const hooks = this.#innerHooks.get(key);\n\n if (!hooks) {\n throw new Error(`inner hooks not set for ${key}`);\n }\n\n return hooks;\n }\n}\n\nexport class UniquePointer extends Number {\n free: (ptr: number) => void;\n\n /**\n * A unique pointer that will automatically free virtual memory when\n * it is disposed. Named after the C++ concept of a unique pointer.\n *\n * Should be used with the `using` keyword to ensure that the pointer is\n * freed (via dispose function) when it is no longer in scope.\n *\n * Useful with WASM modules that require allocating and freeing memory.\n *\n * @example\n * ```ts\n * using ptr = new UniquePointer(wasmBridge.malloc(10), wasmBridge.free);\n * ```\n *\n * @param address The address of the pointer\n * @param free The function to call to free the pointer\n */\n constructor(address: number, free: (ptr: number) => void) {\n super(address);\n this.free = free;\n }\n\n [Symbol.dispose]() {\n this.free(this.valueOf());\n }\n}\n\n/**\n * Map that allows waiting for changes to values.\n */\nexport class EventMap<K, V> extends Map<K, V> {\n #listeners = new Map<K, Set<(value: V) => void>>();\n\n /**\n * Waits for the next `set()` call on the given key.\n */\n wait(key: K): Promise<V> {\n return new Promise((resolve) => {\n const listeners = this.#listeners.get(key) ?? new Set();\n listeners.add(resolve);\n this.#listeners.set(key, listeners);\n });\n }\n\n override set(key: K, value: V) {\n super.set(key, value);\n\n const listeners = this.#listeners.get(key);\n\n if (listeners) {\n for (const listener of listeners) {\n listener(value);\n listeners.delete(listener);\n }\n }\n\n return this;\n }\n}\n\n/**\n * Converts a `ReadableStream` into an `AsyncIterableIterator`.\n *\n * Allows you to use ReadableStreams in a `for await ... of` loop.\n */\nexport function fromReadable<R>(\n readable: ReadableStream<R>,\n options?: { preventCancel?: boolean }\n): AsyncIterableIterator<R> {\n const reader = readable.getReader();\n return fromReader(reader, options);\n}\n\n/**\n * Converts a `ReadableStreamDefaultReader` into an `AsyncIterableIterator`.\n *\n * Allows you to use Readers in a `for await ... of` loop.\n */\nexport async function* fromReader<R>(\n reader: ReadableStreamDefaultReader<R>,\n options?: { preventCancel?: boolean }\n): AsyncIterableIterator<R> {\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n return value;\n }\n yield value;\n }\n } finally {\n if (!options?.preventCancel) {\n await reader.cancel();\n }\n reader.releaseLock();\n }\n}\n\nexport type UnderlyingSourceLockCallback = () => void;\n\n/**\n * `ReadableStream` with an optional lock callback.\n */\nexport class ExtendedReadableStream<R> extends ReadableStream<R> {\n #notifyLock?: () => void;\n\n constructor(\n {\n lock,\n ...underlyingSource\n }: UnderlyingSource & { lock?: UnderlyingSourceLockCallback },\n strategy?: QueuingStrategy<R>\n ) {\n super(underlyingSource, strategy);\n this.#notifyLock = lock;\n }\n\n override getReader() {\n const reader = super.getReader() as any;\n if (this.locked) {\n this.#notifyLock?.();\n }\n return reader;\n }\n\n override pipeThrough<T>(\n transform: ReadableWritablePair<T, R>,\n options?: StreamPipeOptions\n ): ReadableStream<T> {\n const stream = super.pipeThrough(transform, options);\n if (this.locked) {\n this.#notifyLock?.();\n }\n return stream;\n }\n\n override pipeTo(\n dest: WritableStream<R>,\n options?: StreamPipeOptions\n ): Promise<void> {\n const promise = super.pipeTo(dest, options);\n if (this.locked) {\n this.#notifyLock?.();\n }\n return promise;\n }\n\n override tee(): [ReadableStream<R>, ReadableStream<R>] {\n const [a, b] = super.tee();\n if (this.locked) {\n this.#notifyLock?.();\n }\n return [a, b];\n }\n}\n\n/**\n * Queues a microtask and returns a promise that resolves when\n * the microtask is executed.\n *\n * Microtasks are executed after the current task has completed,\n * but before the next task begins (tasks are the main unit of\n * work in the event loop).\n *\n * Useful when you want synchronous code from the current task to\n * complete before executing asynchronous code.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API/Microtask_guide\n */\nexport async function nextMicrotask() {\n return await new Promise<void>((resolve) => queueMicrotask(resolve));\n}\n\n/**\n * Generates a random MAC address.\n *\n * The generated address is locally administered (so won't conflict\n * with real devices) and unicast (so it can be used as a source address).\n */\nexport function generateMacAddress() {\n const mac = new Uint8Array(6);\n crypto.getRandomValues(mac);\n\n // Control bits only apply to the first byte\n mac[0] =\n // Clear the 2 least significant bits\n (mac[0]! & 0b11111100) |\n // Set locally administered bit (bit 1) to 1 and unicast bit (bit 0) to 0\n 0b00000010;\n\n return mac;\n}\n","import type { Pointer, SysExports, WasiExports } from '../types.js';\nimport { UniquePointer } from '../util.js';\n\nexport type CommonExports = {\n get_interface_mac_address(handle: Pointer): Pointer;\n get_interface_ip4_address(handle: Pointer): Pointer;\n get_interface_ip4_netmask(handle: Pointer): Pointer;\n};\n\nexport abstract class Bindings<Imports, Exports> {\n #exports?: Exports & CommonExports & WasiExports & SysExports;\n\n abstract imports: Imports;\n\n get exports(): Exports & CommonExports & WasiExports & SysExports {\n if (!this.#exports) {\n throw new Error('exports were not registered');\n }\n return this.#exports;\n }\n\n /**\n * Register the exports object from the wasm module.\n */\n register(exports: Exports & CommonExports & WasiExports & SysExports) {\n this.#exports = exports;\n }\n\n /**\n * Allocates a region of wasm memory and returns a `UniquePointer` to the start.\n *\n * `UniquePointer` will automatically free the memory when it is disposed.\n * It is intended to be used with the `using` statement which will automatically\n * dispose of the pointer when the current scope ends.\n */\n smartMalloc(size: number) {\n return new UniquePointer(this.exports.malloc(size), this.exports.free);\n }\n\n /**\n * Copies a Uint8Array to a newly allocated region of wasm memory.\n *\n * @returns A pointer to the start of the copied data.\n */\n copyToMemory(data: ArrayBuffer) {\n const bytes = new Uint8Array(data);\n const length = bytes.length;\n const pointer = this.smartMalloc(length);\n\n const memoryView = new Uint8Array(\n this.exports.memory.buffer,\n pointer.valueOf(),\n length\n );\n\n memoryView.set(bytes);\n\n return pointer;\n }\n\n /**\n * Copies a region of wasm memory to a new Uint8Array.\n *\n * @returns A new Uint8Array containing the copied data.\n */\n copyFromMemory(ptr: Pointer | number, length: number): Uint8Array {\n const buffer = this.exports.memory.buffer.slice(\n Number(ptr),\n Number(ptr) + length\n );\n return new Uint8Array(buffer);\n }\n\n /**\n * Creates a Uint8Array view over a region of wasm memory.\n */\n viewFromMemory(ptr: Pointer | number, length: number): Uint8Array {\n return new Uint8Array(this.exports.memory.buffer, Number(ptr), length);\n }\n}\n","import {\n parseIPv4Address,\n parseMacAddress,\n serializeIPv4Cidr,\n serializeMacAddress,\n type IPv4Address,\n type IPv4Cidr,\n type MacAddress,\n} from '@tcpip/wire';\nimport { LwipError } from '../lwip/errors.js';\nimport type { Pointer } from '../types.js';\nimport {\n ExtendedReadableStream,\n fromReadable,\n generateMacAddress,\n Hooks,\n nextMicrotask,\n} from '../util.js';\nimport { Bindings } from './base.js';\n\ntype TapInterfaceHandle = Pointer;\n\ntype TapInterfaceOuterHooks = {\n handle: TapInterfaceHandle;\n sendFrame(frame: Uint8Array): void;\n getMacAddress(): MacAddress;\n getIPv4Address(): IPv4Address | undefined;\n getIPv4Netmask(): IPv4Address | undefined;\n};\n\ntype TapInterfaceInnerHooks = {\n receiveFrame(frame: Uint8Array): void;\n};\n\nexport const tapInterfaceHooks = new Hooks<\n TapInterface,\n TapInterfaceOuterHooks,\n TapInterfaceInnerHooks\n>();\n\nexport type TapImports = {\n register_tap_interface(handle: TapInterfaceHandle): void;\n receive_frame(\n handle: TapInterfaceHandle,\n framePtr: number,\n length: number\n ): Promise<void>;\n};\n\nexport type TapExports = {\n create_tap_interface(\n macAddress: Pointer,\n ipAddress: Pointer,\n netmask: Pointer\n ): TapInterfaceHandle;\n remove_tap_interface(handle: TapInterfaceHandle): void;\n send_tap_interface(\n handle: TapInterfaceHandle,\n frame: Pointer,\n length: number\n ): number;\n enable_tap_interface(handle: TapInterfaceHandle): void;\n disable_tap_interface(handle: TapInterfaceHandle): void;\n};\n\nexport class TapBindings extends Bindings<TapImports, TapExports> {\n interfaces = new Map<TapInterfaceHandle, TapInterface>();\n\n imports = {\n register_tap_interface: (handle: TapInterfaceHandle) => {\n const tapInterface = new VirtualTapInterface();\n\n tapInterfaceHooks.setOuter(tapInterface, {\n handle,\n sendFrame: (frame) => {\n const framePtr = this.copyToMemory(frame);\n const result = this.exports.send_tap_interface(\n handle,\n framePtr,\n frame.length\n );\n\n if (result !== LwipError.ERR_OK) {\n throw new Error(`failed to send frame: ${result}`);\n }\n },\n getMacAddress: () => {\n const macPtr = this.exports.get_interface_mac_address(handle);\n\n const macBytes = this.viewFromMemory(macPtr, 6);\n return parseMacAddress(macBytes);\n },\n getIPv4Address: () => {\n const ipPtr = this.exports.get_interface_ip4_address(handle);\n\n if (ipPtr === 0) {\n return;\n }\n\n const ipBytes = this.viewFromMemory(ipPtr, 4);\n return parseIPv4Address(ipBytes);\n },\n getIPv4Netmask: () => {\n const netmaskPtr = this.exports.get_interface_ip4_netmask(handle);\n\n if (netmaskPtr === 0) {\n return;\n }\n\n const netmaskBytes = this.viewFromMemory(netmaskPtr, 4);\n return parseIPv4Address(netmaskBytes);\n },\n });\n\n this.interfaces.set(handle, tapInterface);\n },\n receive_frame: async (\n handle: TapInterfaceHandle,\n framePtr: number,\n length: number\n ) => {\n const frame = this.copyFromMemory(framePtr, length);\n\n // Wait for synchronous lwIP operations to complete to prevent reentrancy issues\n // This also gives the consumer a chance to start listening before we enqueue the first frame\n await nextMicrotask();\n\n const tapInterface = this.interfaces.get(handle);\n\n if (!tapInterface) {\n console.error('received frame on unknown tap interface');\n return;\n }\n\n tapInterfaceHooks\n .getInner(tapInterface)\n .receiveFrame(new Uint8Array(frame));\n },\n };\n\n async create(options: TapInterfaceOptions) {\n const macAddress = options.mac\n ? serializeMacAddress(options.mac)\n : generateMacAddress();\n\n const { ipAddress, netmask } = options.ip\n ? serializeIPv4Cidr(options.ip)\n : {};\n\n using macAddressPtr = this.copyToMemory(macAddress);\n using ipAddressPtr = ipAddress ? this.copyToMemory(ipAddress) : undefined;\n using netmaskPtr = netmask ? this.copyToMemory(netmask) : undefined;\n\n const handle = this.exports.create_tap_interface(\n macAddressPtr,\n ipAddressPtr ?? 0,\n netmaskPtr ?? 0\n );\n\n const tapInterface = this.interfaces.get(handle);\n\n if (!tapInterface) {\n throw new Error('tap interface failed to register');\n }\n\n return tapInterface;\n }\n\n async remove(tapInterface: TapInterface) {\n for (const [handle, loopback] of this.interfaces.entries()) {\n if (loopback === tapInterface) {\n this.exports.remove_tap_interface(handle);\n this.interfaces.delete(handle);\n return;\n }\n }\n }\n}\n\nexport type TapInterfaceOptions = {\n mac?: MacAddress;\n ip?: IPv4Cidr;\n};\n\nexport type TapInterface = {\n readonly type: 'tap';\n readonly mac: MacAddress;\n readonly ip?: IPv4Address;\n readonly netmask?: IPv4Address;\n readable: ReadableStream<Uint8Array>;\n writable: WritableStream<Uint8Array>;\n listen(): AsyncIterableIterator<Uint8Array>;\n [Symbol.asyncIterator](): AsyncIterableIterator<Uint8Array>;\n};\n\nexport class VirtualTapInterface implements TapInterface {\n #readableController?: ReadableStreamController<Uint8Array>;\n #isListening = false;\n\n readonly type = 'tap' as const;\n get mac(): MacAddress {\n return tapInterfaceHooks.getOuter(this).getMacAddress();\n }\n get ip(): IPv4Address | undefined {\n return tapInterfaceHooks.getOuter(this).getIPv4Address();\n }\n get netmask(): IPv4Address | undefined {\n return tapInterfaceHooks.getOuter(this).getIPv4Netmask();\n }\n readable: ReadableStream<Uint8Array>;\n writable: WritableStream<Uint8Array>;\n\n constructor() {\n tapInterfaceHooks.setInner(this, {\n receiveFrame: async (frame: Uint8Array) => {\n // Do not buffer frames until the consumer signals intent\n // to listen - otherwise memory will grow indefinitely\n if (!this.#isListening) {\n return;\n }\n\n if (!this.#readableController) {\n throw new Error('readable stream not initialized');\n }\n\n this.#readableController.enqueue(frame);\n },\n });\n\n this.readable = new ExtendedReadableStream<Uint8Array>({\n start: (controller) => {\n this.#readableController = controller;\n },\n lock: () => {\n // We interpret anything that locks the stream (getReader, pipeThrough, pipeTo, tee)\n // as intent to start listening\n this.#isListening = true;\n },\n });\n\n this.writable = new WritableStream({\n write: (packet) => {\n try {\n tapInterfaceHooks.getOuter(this).sendFrame(packet);\n } catch (err) {\n console.error('tap interface send failed', err);\n }\n },\n });\n }\n\n listen() {\n if (this.readable.locked) {\n throw new Error('readable stream already locked');\n }\n return fromReadable(this.readable);\n }\n\n [Symbol.asyncIterator](): AsyncIterableIterator<Uint8Array> {\n return this.listen();\n }\n}\n","// Intentionally not using enum to avoid need for a transpiler\nexport const LwipError = {\n ERR_OK: 0,\n ERR_MEM: -1,\n ERR_BUF: -2,\n ERR_TIMEOUT: -3,\n ERR_RTE: -4,\n ERR_INPROGRESS: -5,\n ERR_VAL: -6,\n ERR_WOULDBLOCK: -7,\n ERR_USE: -8,\n ERR_ALREADY: -9,\n ERR_ISCONN: -10,\n ERR_CONN: -11,\n ERR_IF: -12,\n ERR_ABRT: -13,\n ERR_RST: -14,\n ERR_CLSD: -15,\n ERR_ARG: -16,\n} as const;\n\nexport type LwipError = (typeof LwipError)[keyof typeof LwipError];\n","import {\n parseIPv4Address,\n serializeIPv4Cidr,\n type IPv4Address,\n type IPv4Cidr,\n} from '@tcpip/wire';\nimport type { Pointer } from '../types.js';\nimport { Hooks } from '../util.js';\nimport { Bindings } from './base.js';\n\ntype LoopbackInterfaceHandle = Pointer;\n\ntype LoopbackInterfaceOuterHooks = {\n handle: LoopbackInterfaceHandle;\n getIPv4Address(): IPv4Address | undefined;\n getIPv4Netmask(): IPv4Address | undefined;\n};\n\ntype LoopbackInterfaceInnerHooks = {};\n\nexport const loopbackInterfaceHooks = new Hooks<\n LoopbackInterface,\n LoopbackInterfaceOuterHooks,\n LoopbackInterfaceInnerHooks\n>();\n\nexport type LoopbackImports = {\n register_loopback_interface(handle: LoopbackInterfaceHandle): void;\n};\n\nexport type LoopbackExports = {\n create_loopback_interface(\n ipAddress: Pointer,\n netmask: Pointer\n ): LoopbackInterfaceHandle;\n remove_loopback_interface(handle: LoopbackInterfaceHandle): void;\n};\n\nexport class LoopbackBindings extends Bindings<\n LoopbackImports,\n LoopbackExports\n> {\n interfaces = new Map<LoopbackInterfaceHandle, LoopbackInterface>();\n\n imports = {\n register_loopback_interface: (handle: LoopbackInterfaceHandle) => {\n const loopbackInterface = new VirtualLoopbackInterface();\n\n loopbackInterfaceHooks.setOuter(loopbackInterface, {\n handle,\n getIPv4Address: () => {\n const ipPtr = this.exports.get_interface_ip4_address(handle);\n\n if (ipPtr === 0) {\n return;\n }\n\n const ipBytes = this.viewFromMemory(ipPtr, 4);\n return parseIPv4Address(ipBytes);\n },\n getIPv4Netmask: () => {\n const netmaskPtr = this.exports.get_interface_ip4_netmask(handle);\n\n if (netmaskPtr === 0) {\n return;\n }\n\n const netmaskBytes = this.viewFromMemory(netmaskPtr, 4);\n return parseIPv4Address(netmaskBytes);\n },\n });\n\n this.interfaces.set(handle, loopbackInterface);\n },\n };\n\n async create(options: LoopbackInterfaceOptions) {\n const { ipAddress, netmask } = options.ip\n ? serializeIPv4Cidr(options.ip)\n : {};\n\n using ipAddressPtr = ipAddress ? this.copyToMemory(ipAddress) : undefined;\n using netmaskPtr = netmask ? this.copyToMemory(netmask) : undefined;\n\n const handle = this.exports.create_loopback_interface(\n ipAddressPtr ?? 0,\n netmaskPtr ?? 0\n );\n\n const loopbackInterface = this.interfaces.get(handle);\n\n if (!loopbackInterface) {\n throw new Error('loopback interface failed to register');\n }\n\n return loopbackInterface;\n }\n\n async remove(loopbackInterface: LoopbackInterface) {\n for (const [handle, loopback] of this.interfaces.entries()) {\n if (loopback === loopbackInterface) {\n this.exports.remove_loopback_interface(handle);\n this.interfaces.delete(handle);\n return;\n }\n }\n }\n}\n\nexport type LoopbackInterfaceOptions = {\n ip?: IPv4Cidr;\n};\n\nexport type LoopbackInterface = {\n readonly type: 'loopback';\n readonly ip?: IPv4Address;\n readonly netmask?: IPv4Address;\n};\n\nexport class VirtualLoopbackInterface implements LoopbackInterface {\n readonly type = 'loopback';\n get ip(): IPv4Address | undefined {\n return loopbackInterfaceHooks.getOuter(this).getIPv4Address();\n }\n get netmask(): IPv4Address | undefined {\n return loopbackInterfaceHooks.getOuter(this).getIPv4Netmask();\n }\n}\n","import type { DnsClient } from '@tcpip/dns';\nimport { serializeIPv4Address, type IPv4Address } from '@tcpip/wire';\nimport { LwipError } from '../lwip/errors.js';\nimport type { Pointer } from '../types.js';\nimport { EventMap, fromReadable, Hooks, nextMicrotask } from '../util.js';\nimport { Bindings } from './base.js';\n\ntype TcpListenerHandle = Pointer;\ntype TcpConnectionHandle = Pointer;\n\ntype TcpListenerOuterHooks = {};\n\ntype TcpListenerInnerHooks = {\n accept(connection: TcpConnection): void;\n};\n\ntype TcpConnectionOuterHooks = {\n send(data: Uint8Array): Promise<void>;\n updateReceiveBuffer(length: number): void;\n close(): Promise<void>;\n};\n\ntype TcpConnectionInnerHooks = {\n receive(data: Uint8Array): Promise<void>;\n close(): Promise<void>;\n};\n\nconst tcpListenerHooks = new Hooks<\n TcpListener,\n TcpListenerOuterHooks,\n TcpListenerInnerHooks\n>();\n\nconst tcpConnectionHooks = new Hooks<\n TcpConnection,\n TcpConnectionOuterHooks,\n TcpConnectionInnerHooks\n>();\n\nexport const MAX_SEGMENT_SIZE = 1460; // This must match TCP_MSS in lwipopts.h\nexport const MAX_WINDOW_SIZE = MAX_SEGMENT_SIZE * 4; // This must match TCP_WND in lwipopts.h\nexport const SEND_BUFFER_SIZE = MAX_SEGMENT_SIZE * 4; // This must match TCP_SND_BUF in lwipopts.h\nexport const READABLE_HIGH_WATER_MARK = MAX_SEGMENT_SIZE;\n\nexport type TcpImports = {\n accept_tcp_connection(\n listenerHandle: TcpListenerHandle,\n connectionHandle: TcpConnectionHandle\n ): Promise<void>;\n connected_tcp_connection(handle: TcpConnectionHandle): Promise<void>;\n closed_tcp_connection(handle: TcpConnectionHandle): Promise<void>;\n receive_tcp_chunk(\n handle: TcpConnectionHandle,\n chunkPtr: number,\n length: number\n ): Promise<void>;\n sent_tcp_chunk(handle: TcpConnectionHandle, length: number): void;\n};\n\nexport type TcpExports = {\n create_tcp_listener(host: Pointer | null, port: number): TcpListenerHandle;\n create_tcp_connection(host: Pointer, port: number): TcpConnectionHandle;\n close_tcp_connection(handle: TcpConnectionHandle): number;\n send_tcp_chunk(\n handle: TcpConnectionHandle,\n chunk: number,\n length: number\n ): number;\n update_tcp_receive_buffer(handle: TcpConnectionHandle, length: number): void;\n};\n\nexport class TcpBindings extends Bindings<TcpImports, TcpExports> {\n #tcpListeners = new Map<TcpListenerHandle, TcpListener>();\n #tcpConnections = new EventMap<TcpConnectionHandle, TcpConnection>();\n #tcpAcks = new Map<TcpConnectionHandle, (length: number) => void>();\n #dnsClient: DnsClient;\n\n async #resolveHost(host: string) {\n try {\n return serializeIPv4Address(host);\n } catch (e) {\n const ip = await this.#dnsClient.lookup(host);\n return serializeIPv4Address(ip);\n }\n }\n\n constructor(dnsClient: DnsClient) {\n super();\n this.#dnsClient = dnsClient;\n }\n\n imports = {\n accept_tcp_connection: async (\n listenerHandle: TcpListenerHandle,\n connectionHandle: TcpConnectionHandle\n ) => {\n const listener = this.#tcpListeners.get(listenerHandle);\n\n if (!listener) {\n console.error('new tcp connection to unknown listener');\n return;\n }\n\n // Wait for synchronous lwIP operations to complete to prevent reentrancy issues\n await nextMicrotask();\n\n const connection = new VirtualTcpConnection();\n\n tcpConnectionHooks.setOuter(connection, {\n send: async (data) => {\n const dataPtr = Number(this.copyToMemory(data));\n\n let bytesQueued = this.exports.send_tcp_chunk(\n connectionHandle,\n dataPtr,\n data.length\n );\n\n // If the entire data was not queued, send the remaining\n // chunks as space becomes available\n while (bytesQueued < data.length) {\n await new Promise<number>((resolve) => {\n this.#tcpAcks.set(connectionHandle, resolve);\n });\n const bytesRemaining = data.length - bytesQueued;\n\n bytesQueued += this.exports.send_tcp_chunk(\n connectionHandle,\n dataPtr + bytesQueued,\n bytesRemaining\n );\n }\n },\n updateReceiveBuffer: (length: number) => {\n this.exports.update_tcp_receive_buffer(connectionHandle, length);\n },\n close: async () => {\n const result = this.exports.close_tcp_connection(connectionHandle);\n\n if (result !== LwipError.ERR_OK) {\n throw new Error(`failed to close tcp connection: ${result}`);\n }\n },\n });\n\n this.#tcpConnections.set(connectionHandle, connection);\n\n tcpListenerHooks.getInner(listener).accept(connection);\n },\n connected_tcp_connection: async (handle: TcpConnectionHandle) => {\n // Wait for synchronous lwIP operations to complete to prevent reentrancy issues\n await nextMicrotask();\n\n const connection = new VirtualTcpConnection();\n\n tcpConnectionHooks.setOuter(connection, {\n send: async (data) => {\n const dataPtr = Number(this.copyToMemory(data));\n\n let bytesQueued = this.exports.send_tcp_chunk(\n handle,\n dataPtr,\n data.length\n );\n\n // If the entire data was not queued, send the remaining\n // chunks as space becomes available\n while (bytesQueued < data.length) {\n await new Promise<number>((resolve) => {\n this.#tcpAcks.set(handle, resolve);\n });\n const bytesRemaining = data.length - bytesQueued;\n\n bytesQueued += this.exports.send_tcp_chunk(\n handle,\n dataPtr + bytesQueued,\n bytesRemaining\n );\n }\n },\n updateReceiveBuffer: (length: number) => {\n this.exports.update_tcp_receive_buffer(handle, length);\n },\n close: async () => {\n this.exports.close_tcp_connection(handle);\n },\n });\n\n this.#tcpConnections.set(handle, connection);\n },\n closed_tcp_connection: async (handle: TcpConnectionHandle) => {\n const connection = this.#tcpConnections.get(handle);\n\n if (!connection) {\n console.error('received close on unknown tcp connection');\n return;\n }\n\n await tcpConnectionHooks.getInner(connection).close();\n },\n receive_tcp_chunk: async (\n handle: TcpConnectionHandle,\n chunkPtr: number,\n length: number\n ) => {\n const chunk = this.copyFromMemory(chunkPtr, length);\n const connection = this.#tcpConnections.get(handle);\n\n if (!connection) {\n console.error('received chunk on unknown tcp connection');\n return;\n }\n\n // Wait for synchronous lwIP operations to complete to prevent reentrancy issues\n await nextMicrotask();\n\n tcpConnectionHooks.getInner(connection).receive(new Uint8Array(chunk));\n },\n sent_tcp_chunk: (handle: TcpConnectionHandle, length: number) => {\n const notifyAck = this.#tcpAcks.get(handle);\n this.#tcpAcks.delete(handle);\n notifyAck?.(length);\n },\n };\n\n async listen(options: TcpListenerOptions) {\n using hostPtr = options.host\n ? this.copyToMemory(await this.#resolveHost(options.host))\n : null;\n\n const handle = this.exports.create_tcp_listener(hostPtr, options.port);\n\n const tcpListener = new VirtualTcpListener();\n\n tcpListenerHooks.setOuter(tcpListener, {});\n\n this.#tcpListeners.set(handle, tcpListener);\n\n return tcpListener;\n }\n\n async connect(options: TcpConnectionOptions) {\n using hostPtr = this.copyToMemory(await this.#resolveHost(options.host));\n\n const handle = this.exports.create_tcp_connection(hostPtr, options.port);\n\n const tcpConnection = await this.#tcpConnections.wait(handle);\n\n if (!tcpConnection) {\n throw new Error('tcp failed to connect');\n }\n\n return tcpConnection;\n }\n}\n\nexport type TcpListenerOptions = {\n host?: string;\n port: number;\n};\n\nexport type TcpListener = {\n [Symbol.asyncIterator](): AsyncIterableIterator<TcpConnection>;\n};\n\nexport class VirtualTcpListener\n implements TcpListener, AsyncIterable<TcpConnection>\n{\n #connections: TcpConnection[] = [];\n #notifyConnection?: () => void;\n\n constructor() {\n tcpListenerHooks.setInner(this, {\n accept: async (connection: TcpConnection) => {\n this.#connections.push(connection);\n this.#notifyConnection?.();\n },\n });\n }\n\n async *[Symbol.asyncIterator](): AsyncIterableIterator<TcpConnection> {\n while (true) {\n await new Promise<void>((resolve) => {\n this.#notifyConnection = resolve;\n });\n\n yield* this.#connections;\n this.#connections = [];\n }\n }\n}\n\nexport type TcpConnectionOptions = {\n host: string;\n port: number;\n};\n\nexport type TcpConnection = {\n readable: ReadableStream<Uint8Array>;\n writable: WritableStream<Uint8Array>;\n close(): Promise<void>;\n [Symbol.asyncIterator](): AsyncIterator<Uint8Array>;\n};\n\nexport class VirtualTcpConnection\n implements TcpConnection, AsyncIterable<Uint8Array>\n{\n #receiveBuffer: Uint8Array[] = [];\n #readableController?: ReadableStreamDefaultController<Uint8Array>;\n #writableController?: WritableStreamDefaultController;\n\n readable: ReadableStream<Uint8Array>;\n writable: WritableStream<Uint8Array>;\n\n constructor() {\n tcpConnectionHooks.setInner(this, {\n receive: async (data: Uint8Array) => {\n // We maintain our own receive buffer prior to enqueueing to the readable\n // stream so that we can send window updates as data is consumed\n this.#receiveBuffer.push(data);\n this.#enqueueBuffer();\n },\n close: async () => {\n this.close();\n },\n });\n\n this.readable = new ReadableStream(\n {\n start: (controller) => {\n this.#readableController = controller;\n },\n pull: () => {\n this.#enqueueBuffer();\n },\n },\n {\n highWaterMark: READABLE_HIGH_WATER_MARK,\n size: (chunk) => chunk.byteLength,\n }\n );\n\n this.writable = new WritableStream(\n {\n start: (controller) => {\n this.#writableController = controller;\n },\n write: async (chunk) => {\n await tcpConnectionHooks.getOuter(this).send(chunk);\n },\n },\n {\n // Send buffer is managed by the TCP stack\n highWaterMark: 0,\n }\n );\n }\n\n #enqueueBuffer() {\n if (!this.#readableController?.desiredSize) {\n return;\n }\n\n let bytesEnqueued = 0;\n\n // Enqueue chunks until the desired size is reached\n while (this.#receiveBuffer.length > 0) {\n const chunkLength = this.#receiveBuffer[0]!.length;\n\n if (bytesEnqueued + chunkLength > this.#readableController.desiredSize) {\n break;\n }\n\n const chunk = this.#receiveBuffer.shift()!;\n this.#readableController.enqueue(chunk);\n bytesEnqueued += chunk.length;\n }\n\n // Notify the TCP stack that we've read the data\n if (bytesEnqueued > 0) {\n tcpConnectionHooks.getOuter(this).updateReceiveBuffer(bytesEnqueued);\n }\n }\n\n async close() {\n await tcpConnectionHooks.getOuter(this).close();\n this.#readableController?.error(new Error('tcp connection closed'));\n this.#writableController?.error(new Error('tcp connection closed'));\n }\n\n [Symbol.asyncIterator](): AsyncIterator<Uint8Array> {\n if (this.readable.locked) {\n throw new Error('readable stream already locked');\n }\n return fromReadable(this.readable);\n }\n}\n","import {\n parseIPv4Address,\n serializeIPv4Cidr,\n type IPv4Address,\n type IPv4Cidr,\n} from '@tcpip/wire';\nimport type { Pointer } from '../types.js';\nimport {\n ExtendedReadableStream,\n fromReadable,\n Hooks,\n nextMicrotask,\n} from '../util.js';\nimport { Bindings } from './base.js';\n\ntype TunInterfaceHandle = Pointer;\n\ntype TunInterfaceOuterHooks = {\n handle: TunInterfaceHandle;\n sendPacket(packet: Uint8Array): void;\n getIPv4Address(): IPv4Address | undefined;\n getIPv4Netmask(): IPv4Address | undefined;\n};\n\ntype TunInterfaceInnerHooks = {\n receivePacket(packet: Uint8Array): void;\n};\n\nconst tunInterfaceHooks = new Hooks<\n TunInterface,\n TunInterfaceOuterHooks,\n TunInterfaceInnerHooks\n>();\n\nexport type TunImports = {\n register_tun_interface(handle: TunInterfaceHandle): void;\n receive_packet(\n handle: TunInterfaceHandle,\n packetPtr: number,\n length: number\n ): Promise<void>;\n};\n\nexport type TunExports = {\n create_tun_interface(\n ipAddress: Pointer,\n netmask: Pointer\n ): TunInterfaceHandle;\n remove_tun_interface(handle: TunInterfaceHandle): void;\n send_tun_interface(\n handle: TunInterfaceHandle,\n packet: Pointer,\n length: number\n ): void;\n};\n\nexport class TunBindings extends Bindings<TunImports, TunExports> {\n interfaces = new Map<TunInterfaceHandle, TunInterface>();\n\n imports = {\n register_tun_interface: (handle: TunInterfaceHandle) => {\n const tunInterface = new VirtualTunInterface();\n\n tunInterfaceHooks.setOuter(tunInterface, {\n handle,\n sendPacket: (packet) => {\n const packetPtr = this.copyToMemory(packet);\n this.exports.send_tun_interface(handle, packetPtr, packet.length);\n },\n getIPv4Address: () => {\n const ipPtr = this.exports.get_interface_ip4_address(handle);\n\n if (ipPtr === 0) {\n return;\n }\n\n const ipBytes = this.viewFromMemory(ipPtr, 4);\n return parseIPv4Address(ipBytes);\n },\n getIPv4Netmask: () => {\n const netmaskPtr = this.exports.get_interface_ip4_netmask(handle);\n\n if (netmaskPtr === 0) {\n return;\n }\n\n const netmaskBytes = this.viewFromMemory(netmaskPtr, 4);\n return parseIPv4Address(netmaskBytes);\n },\n });\n\n this.interfaces.set(handle, tunInterface);\n },\n receive_packet: async (\n handle: TunInterfaceHandle,\n packetPtr: number,\n length: number\n ) => {\n const packet = this.copyFromMemory(packetPtr, length);\n\n // Wait for synchronous lwIP operations to complete to prevent reentrancy issues\n // This also gives the consumer a chance to start listening before we enqueue the first packet\n await nextMicrotask();\n\n const tunInterface = this.interfaces.get(handle);\n\n if (!tunInterface) {\n console.error('received packet on unknown tun interface');\n return;\n }\n\n tunInterfaceHooks\n .getInner(tunInterface)\n .receivePacket(new Uint8Array(packet));\n },\n };\n\n async create(options: TunInterfaceOptions) {\n const { ipAddress, netmask } = options.ip\n ? serializeIPv4Cidr(options.ip)\n : {};\n\n using ipAddressPtr = ipAddress ? this.copyToMemory(ipAddress) : undefined;\n using netmaskPtr = netmask ? this.copyToMemory(netmask) : undefined;\n\n const handle = this.exports.create_tun_interface(\n ipAddressPtr ?? 0,\n netmaskPtr ?? 0\n );\n\n const tunInterface = this.interfaces.get(handle);\n\n if (!tunInterface) {\n throw new Error('tun interface failed to register');\n }\n\n return tunInterface;\n }\n\n async remove(tunInterface: TunInterface) {\n for (const [handle, loopback] of this.interfaces.entries()) {\n if (loopback === tunInterface) {\n this.exports.remove_tun_interface(handle);\n this.interfaces.delete(handle);\n return;\n }\n }\n }\n}\n\nexport type TunInterfaceOptions = {\n ip?: IPv4Cidr;\n};\n\nexport type TunInterface = {\n readonly type: 'tun';\n readonly ip?: IPv4Address;\n readonly netmask?: IPv4Address;\n readable: ReadableStream<Uint8Array>;\n writable: WritableStream<Uint8Array>;\n listen(): AsyncIterableIterator<Uint8Array>;\n [Symbol.asyncIterator](): AsyncIterableIterator<Uint8Array>;\n};\n\nexport class VirtualTunInterface implements TunInterface {\n #readableController?: ReadableStreamController<Uint8Array>;\n #isListening = false;\n\n readonly type = 'tun' as const;\n get ip(): IPv4Address | undefined {\n return tunInterfaceHooks.getOuter(this).getIPv4Address();\n }\n get netmask(): IPv4Address | undefined {\n return tunInterfaceHooks.getOuter(this).getIPv4Netmask();\n }\n readable: ReadableStream<Uint8Array>;\n writable: WritableStream<Uint8Array>;\n\n constructor() {\n tunInterfaceHooks.setInner(this, {\n receivePacket: async (packet: Uint8Array) => {\n // Do not buffer packets until the consumer signals intent\n // to listen - otherwise memory will grow indefinitely\n if (!this.#isListening) {\n return;\n }\n\n if (!this.#readableController) {\n throw new Error('readable stream not initialized');\n }\n\n this.#readableController?.enqueue(packet);\n },\n });\n\n this.readable = new ExtendedReadableStream<Uint8Array>({\n start: (controller) => {\n this.#readableController = controller;\n },\n lock: () => {\n // We interpret anything that locks the stream (getReader, pipeThrough, pipeTo, tee)\n // as intent to start listening\n this.#isListening = true;\n },\n });\n\n this.writable = new WritableStream({\n write: (packet) => {\n tunInterfaceHooks.getOuter(this).sendPacket(packet);\n },\n });\n }\n\n listen() {\n if (this.readable.locked) {\n throw new Error('readable stream already locked');\n }\n return fromReadable(this.readable);\n }\n\n [Symbol.asyncIterator](): AsyncIterableIterator<Uint8Array> {\n return this.listen();\n }\n}\n","import type { DnsClient } from '@tcpip/dns';\nimport {\n type IPv4Address,\n parseIPv4Address,\n serializeIPv4Address,\n} from '@tcpip/wire';\nimport { LwipError } from '../lwip/errors.js';\nimport type { Pointer } from '../types.js';\nimport { EventMap, fromReadable, Hooks, nextMicrotask } from '../util.js';\nimport { Bindings } from './base.js';\n\nexport type UdpDatagram = {\n host: string;\n port: number;\n data: Uint8Array;\n};\n\ntype UdpSocketHandle = Pointer;\n\ntype UdpSocketOuterHooks = {\n send(datagram: UdpDatagram): Promise<void>;\n close(): Promise<void>;\n};\n\ntype UdpSocketInnerHooks = {\n receive(datagram: UdpDatagram): Promise<void>;\n};\n\nconst udpSocketHooks = new Hooks<\n UdpSocket,\n UdpSocketOuterHooks,\n UdpSocketInnerHooks\n>();\n\nexport type UdpImports = {\n receive_udp_datagram(\n handle: UdpSocketHandle,\n ip: number,\n port: number,\n datagramPtr: number,\n length: number\n ): Promise<void>;\n};\n\nexport type UdpExports = {\n open_udp_socket(host: Pointer | null, port: number): UdpSocketHandle;\n close_udp_socket(handle: UdpSocketHandle): void;\n send_udp_datagram(\n handle: UdpSocketHandle,\n ip: Pointer | null,\n port: number,\n datagram: Pointer,\n length: number\n ): number;\n};\n\nexport class UdpBindings extends Bindings<UdpImports, UdpExports> {\n #udpSockets = new EventMap<UdpSocketHandle, UdpSocket>();\n #dnsClient: DnsClient;\n\n async #resolveHost(host: string) {\n try {\n return serializeIPv4Address(host);\n } catch (e) {\n const ip = await this.#dnsClient.lookup(host);\n return serializeIPv4Address(ip);\n }\n }\n\n constructor(dnsClient: DnsClient) {\n super();\n this.#dnsClient = dnsClient;\n }\n\n imports = {\n receive_udp_datagram: async (\n handle: UdpSocketHandle,\n hostPtr: number,\n port: number,\n datagramPtr: number,\n length: number\n ) => {\n const host = this.copyFromMemory(hostPtr, 4);\n const datagram = this.copyFromMemory(datagramPtr, length);\n const socket = this.#udpSockets.get(handle);\n\n if (!socket) {\n console.error('received datagram on unknown udp socket');\n return;\n }\n\n // Wait for synchronous lwIP operations to complete to prevent reentrancy issues\n await nextMicrotask();\n\n udpSocketHooks.getInner(socket).receive({\n host: parseIPv4Address(host),\n port,\n data: datagram,\n });\n },\n };\n\n async open(options: UdpSocketOptions) {\n using hostPtr = options.host\n ? this.copyToMemory(await this.#resolveHost(options.host))\n : null;\n\n const handle = this.exports.open_udp_socket(hostPtr, options.port ?? 0);\n\n if (Number(handle) === 0) {\n throw new Error('failed to open udp socket');\n }\n\n const udpSocket = new VirtualUdpSocket();\n\n udpSocketHooks.setOuter(udpSocket, {\n send: async (datagram: UdpDatagram) => {\n using hostPtr = this.copyToMemory(\n await this.#resolveHost(datagram.host)\n );\n using datagramPtr = this.copyToMemory(datagram.data);\n\n const result = this.exports.send_udp_datagram(\n handle,\n hostPtr,\n datagram.port,\n datagramPtr,\n datagram.data.length\n );\n\n if (result !== LwipError.ERR_OK) {\n throw new Error(`failed to send udp datagram: ${result}`);\n }\n },\n close: async () => {\n this.exports.close_udp_socket(handle);\n this.#udpSockets.delete(handle);\n },\n });\n\n this.#udpSockets.set(handle, udpSocket);\n\n return udpSocket;\n }\n}\n\nexport type UdpSocketOptions = {\n /**\n * The local host to bind to.\n *\n * If not provided, the socket will bind to all available interfaces.\n */\n host?: string;\n\n /**\n * The local port to bind to.\n *\n * If not provided, the socket will bind to a random port.\n */\n port?: number;\n};\n\nexport type UdpSocket = {\n readable: ReadableStream<UdpDatagram>;\n writable: WritableStream<UdpDatagram>;\n close(): Promise<void>;\n [Symbol.asyncIterator](): AsyncIterator<UdpDatagram>;\n};\n\nexport class VirtualUdpSocket implements UdpSocket, AsyncIterable<UdpDatagram> {\n #readableController?: ReadableStreamDefaultController<UdpDatagram>;\n #writableController?: WritableStreamDefaultController;\n\n readable: ReadableStream<UdpDatagram>;\n writable: WritableStream<UdpDatagram>;\n\n constructor() {\n udpSocketHooks.setInner(this, {\n receive: async (datagram: UdpDatagram) => {\n if (!this.#readableController) {\n throw new Error('readable controller not initialized');\n }\n this.#readableController.enqueue(datagram);\n },\n });\n\n this.readable = new ReadableStream({\n start: (controller) => {\n this.#readableController = controller;\n },\n });\n\n this.writable = new WritableStream({\n start: (controller) => {\n this.#writableController = controller;\n },\n write: async (datagram) => {\n await udpSocketHooks.getOuter(this).send(datagram);\n },\n });\n }\n\n async close() {\n await udpSocketHooks.getOuter(this).close();\n this.#readableController?.error(new Error('udp socket closed'));\n this.#writableController?.error(new Error('udp socket closed'));\n }\n\n [Symbol.asyncIterator](): AsyncIterator<UdpDatagram> {\n if (this.readable.locked) {\n throw new Error('readable stream already locked');\n }\n return fromReadable(this.readable);\n }\n}\n","const IN_NODE =\n typeof process === 'object' &&\n typeof process.versions === 'object' &&\n typeof process.versions.node === 'string';\n\n/**\n * Fetches a file from the network or filesystem\n * depending on the environment.\n */\nexport async function fetchFile(input: string | URL, type: string) {\n if (IN_NODE) {\n return fetchFileNode(input, type);\n }\n return fetch(input);\n}\n\nasync function fetchFileNode(input: string | URL, type: string) {\n const fs = await import('node:fs');\n const { Readable } = await import('node:stream');\n const nodeStream = fs.createReadStream(input);\n const stream = Readable.toWeb(nodeStream) as ReadableStream<Uint8Array>;\n return new Response(stream, { headers: { 'Content-Type': type } });\n}\n"],"mappings":"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","names":["DisposeSymbol","ConsoleStdout","File","OpenFile","WASI","DnsClient","parseIPv4Address","parseMacAddress","serializeIPv4Cidr","serializeMacAddress","Hooks","#outerHooks","#innerHooks","key","hooks","UniquePointer","address","free","DisposeSymbol","EventMap","#listeners","resolve","listeners","value","listener","fromReadable","readable","options","reader","fromReader","done","ExtendedReadableStream","#notifyLock","lock","underlyingSource","strategy","transform","stream","dest","promise","a","b","nextMicrotask","generateMacAddress","mac","Bindings","#exports","exports","size","UniquePointer","data","bytes","length","pointer","ptr","buffer","parseIPv4Address","parseMacAddress","serializeIPv4Cidr","serializeMacAddress","LwipError","tapInterfaceHooks","Hooks","TapBindings","Bindings","handle","tapInterface","VirtualTapInterface","frame","framePtr","result","LwipError","macPtr","macBytes","parseMacAddress","ipPtr","ipBytes","parseIPv4Address","netmaskPtr","netmaskBytes","length","nextMicrotask","options","_stack","macAddress","serializeMacAddress","generateMacAddress","ipAddress","netmask","serializeIPv4Cidr","macAddressPtr","__using","ipAddressPtr","_","_error","_hasError","__callDispose","loopback","#readableController","#isListening","ExtendedReadableStream","controller","packet","err","fromReadable","bridgeInterfaceHooks","Hooks","BridgeBindings","Bindings","options","_stack","macAddress","serializeMacAddress","generateMacAddress","ipAddress","netmask","serializeIPv4Cidr","macAddressPtr","__using","ipAddressPtr","netmaskPtr","portHandles","port","tapInterfaceHooks","portHandlesPtr","handle","bridgeInterface","VirtualBridgeInterface","macPtr","macBytes","parseMacAddress","ipPtr","ipBytes","parseIPv4Address","netmaskBytes","_error","_hasError","__callDispose","loopback","parseIPv4Address","serializeIPv4Cidr","loopbackInterfaceHooks","Hooks","LoopbackBindings","Bindings","handle","loopbackInterface","VirtualLoopbackInterface","ipPtr","ipBytes","parseIPv4Address","netmaskPtr","netmaskBytes","options","_stack","ipAddress","netmask","serializeIPv4Cidr","ipAddressPtr","__using","_","_error","_hasError","__callDispose","loopback","serializeIPv4Address","tcpListenerHooks","Hooks","tcpConnectionHooks","MAX_SEGMENT_SIZE","MAX_WINDOW_SIZE","SEND_BUFFER_SIZE","READABLE_HIGH_WATER_MARK","TcpBindings","Bindings","#tcpListeners","#tcpConnections","EventMap","#tcpAcks","#dnsClient","#resolveHost","host","serializeIPv4Address","ip","dnsClient","listenerHandle","connectionHandle","listener","nextMicrotask","connection","VirtualTcpConnection","data","dataPtr","bytesQueued","resolve","bytesRemaining","length","result","LwipError","handle","chunkPtr","chunk","notifyAck","options","_stack","hostPtr","__using","tcpListener","VirtualTcpListener","_","_error","_hasError","__callDispose","tcpConnection","#connections","#notifyConnection","#receiveBuffer","#readableController","#writableController","#enqueueBuffer","controller","bytesEnqueued","chunkLength","fromReadable","parseIPv4Address","serializeIPv4Cidr","tunInterfaceHooks","Hooks","TunBindings","Bindings","handle","tunInterface","VirtualTunInterface","packet","packetPtr","ipPtr","ipBytes","parseIPv4Address","netmaskPtr","netmaskBytes","length","nextMicrotask","options","_stack","ipAddress","netmask","serializeIPv4Cidr","ipAddressPtr","__using","_","_error","_hasError","__callDispose","loopback","#readableController","#isListening","ExtendedReadableStream","controller","fromReadable","parseIPv4Address","serializeIPv4Address","udpSocketHooks","Hooks","UdpBindings","Bindings","#udpSockets","EventMap","#dnsClient","#resolveHost","host","serializeIPv4Address","ip","dnsClient","handle","hostPtr","port","datagramPtr","length","datagram","socket","nextMicrotask","parseIPv4Address","options","_stack","__using","udpSocket","VirtualUdpSocket","result","LwipError","_","_error","_hasError","__callDispose","#readableController","#writableController","controller","fromReadable","IN_NODE","fetchFile","input","type","fetchFileNode","fs","Readable","nodeStream","stream","createStack","options","stack","VirtualNetworkStack","#options","#loopIntervalId","#dnsClient","#loopbackBindings","#tunBindings","#tapBindings","#bridgeBindings","#tcpBindings","#udpBindings","#listInterfaces","DnsClient","LoopbackBindings","TunBindings","TapBindings","BridgeBindings","TcpBindings","UdpBindings","#init","wasi","WASI","OpenFile","File","ConsoleStdout","msg","source","fetchFile","instance","wasmInstance","result","netInterface"]}
1
+ {"version":3,"sources":["../polyfills/disposable.ts","../src/stack.ts","../src/bindings/bridge-interface.ts","../src/util.ts","../src/bindings/base.ts","../src/bindings/tap-interface.ts","../src/lwip/errors.ts","../src/bindings/loopback-interface.ts","../src/bindings/tcp.ts","../src/bindings/tun-interface.ts","../src/bindings/udp.ts","../src/fetch-file.ts"],"sourcesContent":["/**\n * Scoped polyfill for `Symbol.dispose` without polluting the global scope.\n * Required for the `using` keyword which we use internally.\n * \n * We export this symbol as 'Symbol.dispose' which tells ESBuild to inject\n * it into the output bundle.\n * \n * The below works because Typescript's `using` implementation falls back to\n * `Symbol.for(\"Symbol.dispose\")` if the built-in `Symbol.dispose` is not available.\n */\n\nconst DisposeSymbol = 'dispose' in (Symbol as object) ? Symbol.dispose : Symbol.for('Symbol.dispose');\n\nexport {\n DisposeSymbol as 'Symbol.dispose'\n};\n","import { ConsoleStdout, File, OpenFile, WASI } from '@bjorn3/browser_wasi_shim';\nimport { DnsClient, type NameServer } from '@tcpip/dns';\nimport {\n BridgeBindings,\n type BridgeInterface,\n type BridgeInterfaceOptions,\n} from './bindings/bridge-interface.js';\nimport {\n LoopbackBindings,\n type LoopbackInterface,\n type LoopbackInterfaceOptions,\n} from './bindings/loopback-interface.js';\nimport {\n TapBindings,\n type TapInterface,\n type TapInterfaceOptions,\n} from './bindings/tap-interface.js';\nimport {\n TcpBindings,\n type TcpConnection,\n type TcpConnectionOptions,\n type TcpListener,\n type TcpListenerOptions,\n} from './bindings/tcp.js';\nimport {\n TunBindings,\n type TunInterface,\n type TunInterfaceOptions,\n} from './bindings/tun-interface.js';\nimport {\n UdpBindings,\n type UdpSocket,\n type UdpSocketOptions,\n} from './bindings/udp.js';\nimport { fetchFile } from './fetch-file.js';\nimport type { NetworkInterface, WasmInstance } from './types.js';\n\nexport async function createStack(\n options?: NetworkStackOptions\n): Promise<NetworkStack> {\n const stack = new VirtualNetworkStack(options);\n await stack.ready;\n return stack;\n}\n\nexport type NetworkStackOptions = {\n /**\n * Whether to initialize a loopback interface on startup.\n *\n * @default true\n */\n initializeLoopback?: boolean;\n\n /**\n * Name server used for DNS resolution.\n *\n * @default { ip: '127.0.0.1', port: 53 }\n */\n nameServer?: NameServer;\n};\n\nexport type NetworkStack = {\n readonly ready: Promise<void>;\n readonly interfaces: Iterable<NetworkInterface>;\n\n createLoopbackInterface(\n options: LoopbackInterfaceOptions\n ): Promise<LoopbackInterface>;\n createTunInterface(options: TunInterfaceOptions): Promise<TunInterface>;\n createTapInterface(options?: TapInterfaceOptions): Promise<TapInterface>;\n createBridgeInterface(\n options: BridgeInterfaceOptions\n ): Promise<BridgeInterface>;\n removeInterface(\n netInterface: LoopbackInterface | TunInterface | TapInterface\n ): Promise<void>;\n /**\n * Listens for incoming TCP connections on the specified host/port.\n */\n listenTcp(options: TcpListenerOptions): Promise<TcpListener>;\n /**\n * Establishes an outbound TCP connection to a remote host/port.\n */\n connectTcp(options: TcpConnectionOptions): Promise<TcpConnection>;\n /**\n * Opens a UDP socket for sending and receiving datagrams.\n *\n * If no local host is provided, the socket will bind to all available interfaces.\n * If no local port is provided, the socket will bind to a random port.\n */\n openUdp(options?: UdpSocketOptions): Promise<UdpSocket>;\n};\n\nexport class VirtualNetworkStack implements NetworkStack {\n #options: NetworkStackOptions;\n #loopIntervalId?: number;\n #dnsClient: DnsClient;\n\n #loopbackBindings: LoopbackBindings;\n #tunBindings: TunBindings;\n #tapBindings: TapBindings;\n #bridgeBindings: BridgeBindings;\n #tcpBindings: TcpBindings;\n #udpBindings: UdpBindings;\n\n ready: Promise<void>;\n get interfaces() {\n return this.#listInterfaces();\n }\n\n constructor(options: NetworkStackOptions = {}) {\n this.#options = {\n ...options,\n initializeLoopback: options.initializeLoopback ?? true,\n };\n\n this.#dnsClient = new DnsClient(this, {\n nameServer: options.nameServer ?? { ip: '127.0.0.1', port: 53 },\n });\n\n // Initialize bindings\n this.#loopbackBindings = new LoopbackBindings();\n this.#tunBindings = new TunBindings();\n this.#tapBindings = new TapBindings();\n this.#bridgeBindings = new BridgeBindings();\n this.#tcpBindings = new TcpBindings(this.#dnsClient);\n this.#udpBindings = new UdpBindings(this.#dnsClient);\n\n // Initialize the stack\n this.ready = this.#init();\n\n // Post-init setup\n this.ready.then(async () => {\n if (this.#options.initializeLoopback) {\n await this.createLoopbackInterface({\n ip: '127.0.0.1/8',\n });\n }\n });\n }\n\n async #init() {\n const wasi = new WASI(\n [],\n [],\n [\n new OpenFile(new File([])), // stdin\n ConsoleStdout.lineBuffered((msg) =>\n console.log(`[WASI stdout] ${msg}`)\n ),\n ConsoleStdout.lineBuffered((msg) =>\n console.warn(`[WASI stderr] ${msg}`)\n ),\n ]\n );\n\n const source = fetchFile(\n new URL('../tcpip.wasm', import.meta.url),\n 'application/wasm'\n );\n\n // Instantiate with both WASI and custom imports\n const { instance } = await WebAssembly.instantiateStreaming(source, {\n wasi_snapshot_preview1: wasi.wasiImport,\n env: {\n ...this.#loopbackBindings.imports,\n ...this.#tunBindings.imports,\n ...this.#tapBindings.imports,\n ...this.#bridgeBindings.imports,\n ...this.#tcpBindings.imports,\n ...this.#udpBindings.imports,\n },\n });\n\n const wasmInstance = instance as WasmInstance;\n\n this.#loopbackBindings.register(wasmInstance.exports);\n this.#tunBindings.register(wasmInstance.exports);\n this.#tapBindings.register(wasmInstance.exports);\n this.#bridgeBindings.register(wasmInstance.exports);\n this.#tcpBindings.register(wasmInstance.exports);\n this.#udpBindings.register(wasmInstance.exports);\n\n // Our WASM binary is a WASI reactor module (ie. a lib),\n // so we call `initialize()` instead of `start()`.\n wasi.initialize(wasmInstance);\n\n // Call lwIP's main loop regularly (required in NO_SYS mode)\n // Used to process queued packets (eg. loopback) and expired timeouts\n this.#loopIntervalId = Number(\n setInterval(() => {\n wasmInstance.exports.process_queued_packets();\n wasmInstance.exports.process_timeouts();\n }, 100)\n );\n }\n\n *#listInterfaces(): Iterable<NetworkInterface> {\n yield* this.#loopbackBindings.interfaces.values();\n yield* this.#tunBindings.interfaces.values();\n yield* this.#tapBindings.interfaces.values();\n yield* this.#bridgeBindings.interfaces.values();\n }\n\n async createLoopbackInterface(\n options: LoopbackInterfaceOptions\n ): Promise<LoopbackInterface> {\n await this.ready;\n return this.#loopbackBindings.create(options);\n }\n\n async createTunInterface(\n options: TunInterfaceOptions\n ): Promise<TunInterface> {\n await this.ready;\n return this.#tunBindings.create(options);\n }\n\n async createTapInterface(\n options: TapInterfaceOptions = {}\n ): Promise<TapInterface> {\n await this.ready;\n return this.#tapBindings.create(options);\n }\n\n async createBridgeInterface(options: BridgeInterfaceOptions) {\n await this.ready;\n return this.#bridgeBindings.create(options);\n }\n\n async removeInterface(netInterface: NetworkInterface) {\n await this.ready;\n\n switch (netInterface.type) {\n case 'loopback':\n return this.#loopbackBindings.remove(netInterface);\n case 'tun':\n return this.#tunBindings.remove(netInterface);\n case 'tap':\n return this.#tapBindings.remove(netInterface);\n case 'bridge':\n return this.#bridgeBindings.remove(netInterface);\n default:\n throw new Error('unknown interface type');\n }\n }\n\n /**\n * Listens for incoming TCP connections on the specified host/port.\n */\n async listenTcp(options: TcpListenerOptions) {\n await this.ready;\n return this.#tcpBindings.listen(options);\n }\n\n /**\n * Establishes an outbound TCP connection to a remote host/port.\n */\n async connectTcp(options: TcpConnectionOptions) {\n await this.ready;\n return this.#tcpBindings.connect(options);\n }\n\n /**\n * Opens a UDP socket for sending and receiving datagrams.\n *\n * If no local host is provided, the socket will bind to all available interfaces.\n * If no local port is provided, the socket will bind to a random port.\n */\n async openUdp(options: UdpSocketOptions = {}) {\n await this.ready;\n return this.#udpBindings.open(options);\n }\n}\n","import {\n generateMacAddress,\n parseIPv4Address,\n parseMacAddress,\n serializeIPv4Cidr,\n serializeMacAddress,\n type IPv4Address,\n type IPv4Cidr,\n type MacAddress,\n} from '@tcpip/wire';\nimport type { Pointer } from '../types.js';\nimport { Hooks } from '../util.js';\nimport { Bindings } from './base.js';\nimport { tapInterfaceHooks, type TapInterface } from './tap-interface.js';\n\ntype BridgeInterfaceHandle = Pointer;\n\ntype BridgeInterfaceOuterHooks = {\n handle: BridgeInterfaceHandle;\n getMacAddress(): MacAddress;\n getIPv4Address(): IPv4Address | undefined;\n getIPv4Netmask(): IPv4Address | undefined;\n};\n\ntype BridgeInterfaceInnerHooks = {};\n\nexport const bridgeInterfaceHooks = new Hooks<\n BridgeInterface,\n BridgeInterfaceOuterHooks,\n BridgeInterfaceInnerHooks\n>();\n\nexport type BridgeImports = {};\n\nexport type BridgeExports = {\n create_bridge_interface(\n macAddress: Pointer,\n ipAddress: Pointer,\n netmask: Pointer,\n ports: Pointer,\n ports_length: number\n ): BridgeInterfaceHandle;\n remove_bridge_interface(handle: BridgeInterfaceHandle): void;\n};\n\nexport class BridgeBindings extends Bindings<BridgeImports, BridgeExports> {\n interfaces = new Map<BridgeInterfaceHandle, BridgeInterface>();\n\n imports = {};\n\n async create(options: BridgeInterfaceOptions) {\n const macAddress = options.mac\n ? serializeMacAddress(options.mac)\n : generateMacAddress();\n\n const { ipAddress, netmask } = options.ip\n ? serializeIPv4Cidr(options.ip)\n : {};\n\n using macAddressPtr = this.copyToMemory(macAddress);\n using ipAddressPtr = ipAddress ? this.copyToMemory(ipAddress) : undefined;\n using netmaskPtr = netmask ? this.copyToMemory(netmask) : undefined;\n const portHandles = new Uint32Array(\n options.ports.map((port) =>\n Number(tapInterfaceHooks.getOuter(port).handle)\n )\n );\n\n using portHandlesPtr = this.copyToMemory(portHandles.buffer);\n\n const handle = this.exports.create_bridge_interface(\n macAddressPtr,\n ipAddressPtr ?? 0,\n netmaskPtr ?? 0,\n portHandlesPtr,\n options.ports.length\n );\n\n const bridgeInterface = new VirtualBridgeInterface();\n\n bridgeInterfaceHooks.setOuter(bridgeInterface, {\n handle,\n getMacAddress: () => {\n const macPtr = this.exports.get_interface_mac_address(handle);\n\n const macBytes = this.viewFromMemory(macPtr, 6);\n return parseMacAddress(macBytes);\n },\n getIPv4Address: () => {\n const ipPtr = this.exports.get_interface_ip4_address(handle);\n\n if (ipPtr === 0) {\n return;\n }\n\n const ipBytes = this.viewFromMemory(ipPtr, 4);\n return parseIPv4Address(ipBytes);\n },\n getIPv4Netmask: () => {\n const netmaskPtr = this.exports.get_interface_ip4_netmask(handle);\n\n if (netmaskPtr === 0) {\n return;\n }\n\n const netmaskBytes = this.viewFromMemory(netmaskPtr, 4);\n return parseIPv4Address(netmaskBytes);\n },\n });\n\n this.interfaces.set(handle, bridgeInterface);\n\n return bridgeInterface;\n }\n\n async remove(bridgeInterface: BridgeInterface) {\n for (const [handle, loopback] of this.interfaces.entries()) {\n if (loopback === bridgeInterface) {\n this.exports.remove_bridge_interface(handle);\n this.interfaces.delete(handle);\n return;\n }\n }\n }\n}\n\nexport type BridgeInterfaceOptions = {\n ports: TapInterface[];\n mac?: MacAddress;\n ip?: IPv4Cidr;\n};\n\nexport type BridgeInterface = {\n readonly type: 'bridge';\n readonly mac: MacAddress;\n readonly ip?: IPv4Address;\n readonly netmask?: IPv4Address;\n};\n\nexport class VirtualBridgeInterface implements BridgeInterface {\n readonly type = 'bridge';\n get mac(): MacAddress {\n return bridgeInterfaceHooks.getOuter(this).getMacAddress();\n }\n get ip(): IPv4Address | undefined {\n return bridgeInterfaceHooks.getOuter(this).getIPv4Address();\n }\n get netmask(): IPv4Address | undefined {\n return bridgeInterfaceHooks.getOuter(this).getIPv4Netmask();\n }\n}\n","/**\n * Utility class to facilitate internal communication\n * between bindings and JS instances.\n * Hooks are created for both the outer (bindings) and\n * inner (JS instance) sides of the communication.\n *\n * Uses `WeakMap` to map each JS instance to a set of\n * hooks while avoiding memory leaks.\n */\nexport class Hooks<K extends WeakKey, O, I> {\n #outerHooks = new WeakMap<K, O>();\n #innerHooks = new WeakMap<K, I>();\n\n setOuter(key: K, hooks: O) {\n this.#outerHooks.set(key, hooks);\n }\n\n setInner(key: K, hooks: I) {\n this.#innerHooks.set(key, hooks);\n }\n\n getOuter(key: K) {\n const hooks = this.#outerHooks.get(key);\n\n if (!hooks) {\n throw new Error(`outer hooks not set for ${key}`);\n }\n\n return hooks;\n }\n\n getInner(key: K) {\n const hooks = this.#innerHooks.get(key);\n\n if (!hooks) {\n throw new Error(`inner hooks not set for ${key}`);\n }\n\n return hooks;\n }\n}\n\nexport class UniquePointer extends Number {\n free: (ptr: number) => void;\n\n /**\n * A unique pointer that will automatically free virtual memory when\n * it is disposed. Named after the C++ concept of a unique pointer.\n *\n * Should be used with the `using` keyword to ensure that the pointer is\n * freed (via dispose function) when it is no longer in scope.\n *\n * Useful with WASM modules that require allocating and freeing memory.\n *\n * @example\n * ```ts\n * using ptr = new UniquePointer(wasmBridge.malloc(10), wasmBridge.free);\n * ```\n *\n * @param address The address of the pointer\n * @param free The function to call to free the pointer\n */\n constructor(address: number, free: (ptr: number) => void) {\n super(address);\n this.free = free;\n }\n\n [Symbol.dispose]() {\n this.free(this.valueOf());\n }\n}\n\n/**\n * Map that allows waiting for changes to values.\n */\nexport class EventMap<K, V> extends Map<K, V> {\n #listeners = new Map<K, Set<(value: V) => void>>();\n\n /**\n * Waits for the next `set()` call on the given key.\n */\n wait(key: K): Promise<V> {\n return new Promise((resolve) => {\n const listeners = this.#listeners.get(key) ?? new Set();\n listeners.add(resolve);\n this.#listeners.set(key, listeners);\n });\n }\n\n override set(key: K, value: V) {\n super.set(key, value);\n\n const listeners = this.#listeners.get(key);\n\n if (listeners) {\n for (const listener of listeners) {\n listener(value);\n listeners.delete(listener);\n }\n }\n\n return this;\n }\n}\n\n/**\n * Converts a `ReadableStream` into an `AsyncIterableIterator`.\n *\n * Allows you to use ReadableStreams in a `for await ... of` loop.\n */\nexport function fromReadable<R>(\n readable: ReadableStream<R>,\n options?: { preventCancel?: boolean }\n): AsyncIterableIterator<R> {\n const reader = readable.getReader();\n return fromReader(reader, options);\n}\n\n/**\n * Converts a `ReadableStreamDefaultReader` into an `AsyncIterableIterator`.\n *\n * Allows you to use Readers in a `for await ... of` loop.\n */\nexport async function* fromReader<R>(\n reader: ReadableStreamDefaultReader<R>,\n options?: { preventCancel?: boolean }\n): AsyncIterableIterator<R> {\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n return value;\n }\n yield value;\n }\n } finally {\n if (!options?.preventCancel) {\n await reader.cancel();\n }\n reader.releaseLock();\n }\n}\n\nexport type UnderlyingSourceLockCallback = () => void;\n\n/**\n * `ReadableStream` with an optional lock callback.\n */\nexport class ExtendedReadableStream<R> extends ReadableStream<R> {\n #notifyLock?: () => void;\n\n constructor(\n {\n lock,\n ...underlyingSource\n }: UnderlyingSource & { lock?: UnderlyingSourceLockCallback },\n strategy?: QueuingStrategy<R>\n ) {\n super(underlyingSource, strategy);\n this.#notifyLock = lock;\n }\n\n override getReader() {\n const reader = super.getReader() as any;\n if (this.locked) {\n this.#notifyLock?.();\n }\n return reader;\n }\n\n override pipeThrough<T>(\n transform: ReadableWritablePair<T, R>,\n options?: StreamPipeOptions\n ): ReadableStream<T> {\n const stream = super.pipeThrough(transform, options);\n if (this.locked) {\n this.#notifyLock?.();\n }\n return stream;\n }\n\n override pipeTo(\n dest: WritableStream<R>,\n options?: StreamPipeOptions\n ): Promise<void> {\n const promise = super.pipeTo(dest, options);\n if (this.locked) {\n this.#notifyLock?.();\n }\n return promise;\n }\n\n override tee(): [ReadableStream<R>, ReadableStream<R>] {\n const [a, b] = super.tee();\n if (this.locked) {\n this.#notifyLock?.();\n }\n return [a, b];\n }\n}\n\n/**\n * Queues a microtask and returns a promise that resolves when\n * the microtask is executed.\n *\n * Microtasks are executed after the current task has completed,\n * but before the next task begins (tasks are the main unit of\n * work in the event loop).\n *\n * Useful when you want synchronous code from the current task to\n * complete before executing asynchronous code.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API/Microtask_guide\n */\nexport async function nextMicrotask() {\n return await new Promise<void>((resolve) => queueMicrotask(resolve));\n}\n","import type { Pointer, SysExports, WasiExports } from '../types.js';\nimport { UniquePointer } from '../util.js';\n\nexport type CommonExports = {\n get_interface_mac_address(handle: Pointer): Pointer;\n get_interface_ip4_address(handle: Pointer): Pointer;\n get_interface_ip4_netmask(handle: Pointer): Pointer;\n};\n\nexport abstract class Bindings<Imports, Exports> {\n #exports?: Exports & CommonExports & WasiExports & SysExports;\n\n abstract imports: Imports;\n\n get exports(): Exports & CommonExports & WasiExports & SysExports {\n if (!this.#exports) {\n throw new Error('exports were not registered');\n }\n return this.#exports;\n }\n\n /**\n * Register the exports object from the wasm module.\n */\n register(exports: Exports & CommonExports & WasiExports & SysExports) {\n this.#exports = exports;\n }\n\n /**\n * Allocates a region of wasm memory and returns a `UniquePointer` to the start.\n *\n * `UniquePointer` will automatically free the memory when it is disposed.\n * It is intended to be used with the `using` statement which will automatically\n * dispose of the pointer when the current scope ends.\n */\n smartMalloc(size: number) {\n return new UniquePointer(this.exports.malloc(size), this.exports.free);\n }\n\n /**\n * Copies a Uint8Array to a newly allocated region of wasm memory.\n *\n * @returns A pointer to the start of the copied data.\n */\n copyToMemory(data: ArrayBuffer) {\n const bytes = new Uint8Array(data);\n const length = bytes.length;\n const pointer = this.smartMalloc(length);\n\n const memoryView = new Uint8Array(\n this.exports.memory.buffer,\n pointer.valueOf(),\n length\n );\n\n memoryView.set(bytes);\n\n return pointer;\n }\n\n /**\n * Copies a region of wasm memory to a new Uint8Array.\n *\n * @returns A new Uint8Array containing the copied data.\n */\n copyFromMemory(ptr: Pointer | number, length: number): Uint8Array {\n const buffer = this.exports.memory.buffer.slice(\n Number(ptr),\n Number(ptr) + length\n );\n return new Uint8Array(buffer);\n }\n\n /**\n * Creates a Uint8Array view over a region of wasm memory.\n */\n viewFromMemory(ptr: Pointer | number, length: number): Uint8Array {\n return new Uint8Array(this.exports.memory.buffer, Number(ptr), length);\n }\n}\n","import {\n generateMacAddress,\n parseIPv4Address,\n parseMacAddress,\n serializeIPv4Cidr,\n serializeMacAddress,\n type IPv4Address,\n type IPv4Cidr,\n type MacAddress,\n} from '@tcpip/wire';\nimport { LwipError } from '../lwip/errors.js';\nimport type { Pointer } from '../types.js';\nimport {\n ExtendedReadableStream,\n fromReadable,\n Hooks,\n nextMicrotask,\n} from '../util.js';\nimport { Bindings } from './base.js';\n\ntype TapInterfaceHandle = Pointer;\n\ntype TapInterfaceOuterHooks = {\n handle: TapInterfaceHandle;\n sendFrame(frame: Uint8Array): void;\n getMacAddress(): MacAddress;\n getIPv4Address(): IPv4Address | undefined;\n getIPv4Netmask(): IPv4Address | undefined;\n};\n\ntype TapInterfaceInnerHooks = {\n receiveFrame(frame: Uint8Array): void;\n};\n\nexport const tapInterfaceHooks = new Hooks<\n TapInterface,\n TapInterfaceOuterHooks,\n TapInterfaceInnerHooks\n>();\n\nexport type TapImports = {\n register_tap_interface(handle: TapInterfaceHandle): void;\n receive_frame(\n handle: TapInterfaceHandle,\n framePtr: number,\n length: number\n ): Promise<void>;\n};\n\nexport type TapExports = {\n create_tap_interface(\n macAddress: Pointer,\n ipAddress: Pointer,\n netmask: Pointer\n ): TapInterfaceHandle;\n remove_tap_interface(handle: TapInterfaceHandle): void;\n send_tap_interface(\n handle: TapInterfaceHandle,\n frame: Pointer,\n length: number\n ): number;\n enable_tap_interface(handle: TapInterfaceHandle): void;\n disable_tap_interface(handle: TapInterfaceHandle): void;\n};\n\nexport class TapBindings extends Bindings<TapImports, TapExports> {\n interfaces = new Map<TapInterfaceHandle, TapInterface>();\n\n imports = {\n register_tap_interface: (handle: TapInterfaceHandle) => {\n const tapInterface = new VirtualTapInterface();\n\n tapInterfaceHooks.setOuter(tapInterface, {\n handle,\n sendFrame: (frame) => {\n const framePtr = this.copyToMemory(frame);\n const result = this.exports.send_tap_interface(\n handle,\n framePtr,\n frame.length\n );\n\n if (result !== LwipError.ERR_OK) {\n throw new Error(`failed to send frame: ${result}`);\n }\n },\n getMacAddress: () => {\n const macPtr = this.exports.get_interface_mac_address(handle);\n\n const macBytes = this.viewFromMemory(macPtr, 6);\n return parseMacAddress(macBytes);\n },\n getIPv4Address: () => {\n const ipPtr = this.exports.get_interface_ip4_address(handle);\n\n if (ipPtr === 0) {\n return;\n }\n\n const ipBytes = this.viewFromMemory(ipPtr, 4);\n return parseIPv4Address(ipBytes);\n },\n getIPv4Netmask: () => {\n const netmaskPtr = this.exports.get_interface_ip4_netmask(handle);\n\n if (netmaskPtr === 0) {\n return;\n }\n\n const netmaskBytes = this.viewFromMemory(netmaskPtr, 4);\n return parseIPv4Address(netmaskBytes);\n },\n });\n\n this.interfaces.set(handle, tapInterface);\n },\n receive_frame: async (\n handle: TapInterfaceHandle,\n framePtr: number,\n length: number\n ) => {\n const frame = this.copyFromMemory(framePtr, length);\n\n // Wait for synchronous lwIP operations to complete to prevent reentrancy issues\n // This also gives the consumer a chance to start listening before we enqueue the first frame\n await nextMicrotask();\n\n const tapInterface = this.interfaces.get(handle);\n\n if (!tapInterface) {\n console.error('received frame on unknown tap interface');\n return;\n }\n\n tapInterfaceHooks\n .getInner(tapInterface)\n .receiveFrame(new Uint8Array(frame));\n },\n };\n\n async create(options: TapInterfaceOptions) {\n const macAddress = options.mac\n ? serializeMacAddress(options.mac)\n : generateMacAddress();\n\n const { ipAddress, netmask } = options.ip\n ? serializeIPv4Cidr(options.ip)\n : {};\n\n using macAddressPtr = this.copyToMemory(macAddress);\n using ipAddressPtr = ipAddress ? this.copyToMemory(ipAddress) : undefined;\n using netmaskPtr = netmask ? this.copyToMemory(netmask) : undefined;\n\n const handle = this.exports.create_tap_interface(\n macAddressPtr,\n ipAddressPtr ?? 0,\n netmaskPtr ?? 0\n );\n\n const tapInterface = this.interfaces.get(handle);\n\n if (!tapInterface) {\n throw new Error('tap interface failed to register');\n }\n\n return tapInterface;\n }\n\n async remove(tapInterface: TapInterface) {\n for (const [handle, loopback] of this.interfaces.entries()) {\n if (loopback === tapInterface) {\n this.exports.remove_tap_interface(handle);\n this.interfaces.delete(handle);\n return;\n }\n }\n }\n}\n\nexport type TapInterfaceOptions = {\n mac?: MacAddress;\n ip?: IPv4Cidr;\n};\n\nexport type TapInterface = {\n readonly type: 'tap';\n readonly mac: MacAddress;\n readonly ip?: IPv4Address;\n readonly netmask?: IPv4Address;\n readable: ReadableStream<Uint8Array>;\n writable: WritableStream<Uint8Array>;\n listen(): AsyncIterableIterator<Uint8Array>;\n [Symbol.asyncIterator](): AsyncIterableIterator<Uint8Array>;\n};\n\nexport class VirtualTapInterface implements TapInterface {\n #readableController?: ReadableStreamController<Uint8Array>;\n #isListening = false;\n\n readonly type = 'tap' as const;\n get mac(): MacAddress {\n return tapInterfaceHooks.getOuter(this).getMacAddress();\n }\n get ip(): IPv4Address | undefined {\n return tapInterfaceHooks.getOuter(this).getIPv4Address();\n }\n get netmask(): IPv4Address | undefined {\n return tapInterfaceHooks.getOuter(this).getIPv4Netmask();\n }\n readable: ReadableStream<Uint8Array>;\n writable: WritableStream<Uint8Array>;\n\n constructor() {\n tapInterfaceHooks.setInner(this, {\n receiveFrame: async (frame: Uint8Array) => {\n // Do not buffer frames until the consumer signals intent\n // to listen - otherwise memory will grow indefinitely\n if (!this.#isListening) {\n return;\n }\n\n if (!this.#readableController) {\n throw new Error('readable stream not initialized');\n }\n\n this.#readableController.enqueue(frame);\n },\n });\n\n this.readable = new ExtendedReadableStream<Uint8Array>({\n start: (controller) => {\n this.#readableController = controller;\n },\n lock: () => {\n // We interpret anything that locks the stream (getReader, pipeThrough, pipeTo, tee)\n // as intent to start listening\n this.#isListening = true;\n },\n });\n\n this.writable = new WritableStream({\n write: (packet) => {\n try {\n tapInterfaceHooks.getOuter(this).sendFrame(packet);\n } catch (err) {\n console.error('tap interface send failed', err);\n }\n },\n });\n }\n\n listen() {\n if (this.readable.locked) {\n throw new Error('readable stream already locked');\n }\n return fromReadable(this.readable);\n }\n\n [Symbol.asyncIterator](): AsyncIterableIterator<Uint8Array> {\n return this.listen();\n }\n}\n","// Intentionally not using enum to avoid need for a transpiler\nexport const LwipError = {\n ERR_OK: 0,\n ERR_MEM: -1,\n ERR_BUF: -2,\n ERR_TIMEOUT: -3,\n ERR_RTE: -4,\n ERR_INPROGRESS: -5,\n ERR_VAL: -6,\n ERR_WOULDBLOCK: -7,\n ERR_USE: -8,\n ERR_ALREADY: -9,\n ERR_ISCONN: -10,\n ERR_CONN: -11,\n ERR_IF: -12,\n ERR_ABRT: -13,\n ERR_RST: -14,\n ERR_CLSD: -15,\n ERR_ARG: -16,\n} as const;\n\nexport type LwipError = (typeof LwipError)[keyof typeof LwipError];\n","import {\n parseIPv4Address,\n serializeIPv4Cidr,\n type IPv4Address,\n type IPv4Cidr,\n} from '@tcpip/wire';\nimport type { Pointer } from '../types.js';\nimport { Hooks } from '../util.js';\nimport { Bindings } from './base.js';\n\ntype LoopbackInterfaceHandle = Pointer;\n\ntype LoopbackInterfaceOuterHooks = {\n handle: LoopbackInterfaceHandle;\n getIPv4Address(): IPv4Address | undefined;\n getIPv4Netmask(): IPv4Address | undefined;\n};\n\ntype LoopbackInterfaceInnerHooks = {};\n\nexport const loopbackInterfaceHooks = new Hooks<\n LoopbackInterface,\n LoopbackInterfaceOuterHooks,\n LoopbackInterfaceInnerHooks\n>();\n\nexport type LoopbackImports = {\n register_loopback_interface(handle: LoopbackInterfaceHandle): void;\n};\n\nexport type LoopbackExports = {\n create_loopback_interface(\n ipAddress: Pointer,\n netmask: Pointer\n ): LoopbackInterfaceHandle;\n remove_loopback_interface(handle: LoopbackInterfaceHandle): void;\n};\n\nexport class LoopbackBindings extends Bindings<\n LoopbackImports,\n LoopbackExports\n> {\n interfaces = new Map<LoopbackInterfaceHandle, LoopbackInterface>();\n\n imports = {\n register_loopback_interface: (handle: LoopbackInterfaceHandle) => {\n const loopbackInterface = new VirtualLoopbackInterface();\n\n loopbackInterfaceHooks.setOuter(loopbackInterface, {\n handle,\n getIPv4Address: () => {\n const ipPtr = this.exports.get_interface_ip4_address(handle);\n\n if (ipPtr === 0) {\n return;\n }\n\n const ipBytes = this.viewFromMemory(ipPtr, 4);\n return parseIPv4Address(ipBytes);\n },\n getIPv4Netmask: () => {\n const netmaskPtr = this.exports.get_interface_ip4_netmask(handle);\n\n if (netmaskPtr === 0) {\n return;\n }\n\n const netmaskBytes = this.viewFromMemory(netmaskPtr, 4);\n return parseIPv4Address(netmaskBytes);\n },\n });\n\n this.interfaces.set(handle, loopbackInterface);\n },\n };\n\n async create(options: LoopbackInterfaceOptions) {\n const { ipAddress, netmask } = options.ip\n ? serializeIPv4Cidr(options.ip)\n : {};\n\n using ipAddressPtr = ipAddress ? this.copyToMemory(ipAddress) : undefined;\n using netmaskPtr = netmask ? this.copyToMemory(netmask) : undefined;\n\n const handle = this.exports.create_loopback_interface(\n ipAddressPtr ?? 0,\n netmaskPtr ?? 0\n );\n\n const loopbackInterface = this.interfaces.get(handle);\n\n if (!loopbackInterface) {\n throw new Error('loopback interface failed to register');\n }\n\n return loopbackInterface;\n }\n\n async remove(loopbackInterface: LoopbackInterface) {\n for (const [handle, loopback] of this.interfaces.entries()) {\n if (loopback === loopbackInterface) {\n this.exports.remove_loopback_interface(handle);\n this.interfaces.delete(handle);\n return;\n }\n }\n }\n}\n\nexport type LoopbackInterfaceOptions = {\n ip?: IPv4Cidr;\n};\n\nexport type LoopbackInterface = {\n readonly type: 'loopback';\n readonly ip?: IPv4Address;\n readonly netmask?: IPv4Address;\n};\n\nexport class VirtualLoopbackInterface implements LoopbackInterface {\n readonly type = 'loopback';\n get ip(): IPv4Address | undefined {\n return loopbackInterfaceHooks.getOuter(this).getIPv4Address();\n }\n get netmask(): IPv4Address | undefined {\n return loopbackInterfaceHooks.getOuter(this).getIPv4Netmask();\n }\n}\n","import type { DnsClient } from '@tcpip/dns';\nimport { serializeIPv4Address, type IPv4Address } from '@tcpip/wire';\nimport { LwipError } from '../lwip/errors.js';\nimport type { Pointer } from '../types.js';\nimport { EventMap, fromReadable, Hooks, nextMicrotask } from '../util.js';\nimport { Bindings } from './base.js';\n\ntype TcpListenerHandle = Pointer;\ntype TcpConnectionHandle = Pointer;\n\ntype TcpListenerOuterHooks = {};\n\ntype TcpListenerInnerHooks = {\n accept(connection: TcpConnection): void;\n};\n\ntype TcpConnectionOuterHooks = {\n send(data: Uint8Array): Promise<void>;\n updateReceiveBuffer(length: number): void;\n close(): Promise<void>;\n};\n\ntype TcpConnectionInnerHooks = {\n receive(data: Uint8Array): Promise<void>;\n close(): Promise<void>;\n};\n\nconst tcpListenerHooks = new Hooks<\n TcpListener,\n TcpListenerOuterHooks,\n TcpListenerInnerHooks\n>();\n\nconst tcpConnectionHooks = new Hooks<\n TcpConnection,\n TcpConnectionOuterHooks,\n TcpConnectionInnerHooks\n>();\n\nexport const MAX_SEGMENT_SIZE = 1460; // This must match TCP_MSS in lwipopts.h\nexport const MAX_WINDOW_SIZE = MAX_SEGMENT_SIZE * 4; // This must match TCP_WND in lwipopts.h\nexport const SEND_BUFFER_SIZE = MAX_SEGMENT_SIZE * 4; // This must match TCP_SND_BUF in lwipopts.h\nexport const READABLE_HIGH_WATER_MARK = MAX_SEGMENT_SIZE;\n\nexport type TcpImports = {\n accept_tcp_connection(\n listenerHandle: TcpListenerHandle,\n connectionHandle: TcpConnectionHandle\n ): Promise<void>;\n connected_tcp_connection(handle: TcpConnectionHandle): Promise<void>;\n closed_tcp_connection(handle: TcpConnectionHandle): Promise<void>;\n receive_tcp_chunk(\n handle: TcpConnectionHandle,\n chunkPtr: number,\n length: number\n ): Promise<void>;\n sent_tcp_chunk(handle: TcpConnectionHandle, length: number): void;\n};\n\nexport type TcpExports = {\n create_tcp_listener(host: Pointer | null, port: number): TcpListenerHandle;\n create_tcp_connection(host: Pointer, port: number): TcpConnectionHandle;\n close_tcp_connection(handle: TcpConnectionHandle): number;\n send_tcp_chunk(\n handle: TcpConnectionHandle,\n chunk: number,\n length: number\n ): number;\n update_tcp_receive_buffer(handle: TcpConnectionHandle, length: number): void;\n};\n\nexport class TcpBindings extends Bindings<TcpImports, TcpExports> {\n #tcpListeners = new Map<TcpListenerHandle, TcpListener>();\n #tcpConnections = new EventMap<TcpConnectionHandle, TcpConnection>();\n #tcpAcks = new Map<TcpConnectionHandle, (length: number) => void>();\n #dnsClient: DnsClient;\n\n async #resolveHost(host: string) {\n try {\n return serializeIPv4Address(host);\n } catch (e) {\n const ip = await this.#dnsClient.lookup(host);\n return serializeIPv4Address(ip);\n }\n }\n\n constructor(dnsClient: DnsClient) {\n super();\n this.#dnsClient = dnsClient;\n }\n\n imports = {\n accept_tcp_connection: async (\n listenerHandle: TcpListenerHandle,\n connectionHandle: TcpConnectionHandle\n ) => {\n const listener = this.#tcpListeners.get(listenerHandle);\n\n if (!listener) {\n console.error('new tcp connection to unknown listener');\n return;\n }\n\n // Wait for synchronous lwIP operations to complete to prevent reentrancy issues\n await nextMicrotask();\n\n const connection = new VirtualTcpConnection();\n\n tcpConnectionHooks.setOuter(connection, {\n send: async (data) => {\n const dataPtr = Number(this.copyToMemory(data));\n\n let bytesQueued = this.exports.send_tcp_chunk(\n connectionHandle,\n dataPtr,\n data.length\n );\n\n // If the entire data was not queued, send the remaining\n // chunks as space becomes available\n while (bytesQueued < data.length) {\n await new Promise<number>((resolve) => {\n this.#tcpAcks.set(connectionHandle, resolve);\n });\n const bytesRemaining = data.length - bytesQueued;\n\n bytesQueued += this.exports.send_tcp_chunk(\n connectionHandle,\n dataPtr + bytesQueued,\n bytesRemaining\n );\n }\n },\n updateReceiveBuffer: (length: number) => {\n this.exports.update_tcp_receive_buffer(connectionHandle, length);\n },\n close: async () => {\n const result = this.exports.close_tcp_connection(connectionHandle);\n\n if (result !== LwipError.ERR_OK) {\n throw new Error(`failed to close tcp connection: ${result}`);\n }\n },\n });\n\n this.#tcpConnections.set(connectionHandle, connection);\n\n tcpListenerHooks.getInner(listener).accept(connection);\n },\n connected_tcp_connection: async (handle: TcpConnectionHandle) => {\n // Wait for synchronous lwIP operations to complete to prevent reentrancy issues\n await nextMicrotask();\n\n const connection = new VirtualTcpConnection();\n\n tcpConnectionHooks.setOuter(connection, {\n send: async (data) => {\n const dataPtr = Number(this.copyToMemory(data));\n\n let bytesQueued = this.exports.send_tcp_chunk(\n handle,\n dataPtr,\n data.length\n );\n\n // If the entire data was not queued, send the remaining\n // chunks as space becomes available\n while (bytesQueued < data.length) {\n await new Promise<number>((resolve) => {\n this.#tcpAcks.set(handle, resolve);\n });\n const bytesRemaining = data.length - bytesQueued;\n\n bytesQueued += this.exports.send_tcp_chunk(\n handle,\n dataPtr + bytesQueued,\n bytesRemaining\n );\n }\n },\n updateReceiveBuffer: (length: number) => {\n this.exports.update_tcp_receive_buffer(handle, length);\n },\n close: async () => {\n this.exports.close_tcp_connection(handle);\n },\n });\n\n this.#tcpConnections.set(handle, connection);\n },\n closed_tcp_connection: async (handle: TcpConnectionHandle) => {\n const connection = this.#tcpConnections.get(handle);\n\n if (!connection) {\n console.error('received close on unknown tcp connection');\n return;\n }\n\n await tcpConnectionHooks.getInner(connection).close();\n },\n receive_tcp_chunk: async (\n handle: TcpConnectionHandle,\n chunkPtr: number,\n length: number\n ) => {\n const chunk = this.copyFromMemory(chunkPtr, length);\n const connection = this.#tcpConnections.get(handle);\n\n if (!connection) {\n console.error('received chunk on unknown tcp connection');\n return;\n }\n\n // Wait for synchronous lwIP operations to complete to prevent reentrancy issues\n await nextMicrotask();\n\n tcpConnectionHooks.getInner(connection).receive(new Uint8Array(chunk));\n },\n sent_tcp_chunk: (handle: TcpConnectionHandle, length: number) => {\n const notifyAck = this.#tcpAcks.get(handle);\n this.#tcpAcks.delete(handle);\n notifyAck?.(length);\n },\n };\n\n async listen(options: TcpListenerOptions) {\n using hostPtr = options.host\n ? this.copyToMemory(await this.#resolveHost(options.host))\n : null;\n\n const handle = this.exports.create_tcp_listener(hostPtr, options.port);\n\n const tcpListener = new VirtualTcpListener();\n\n tcpListenerHooks.setOuter(tcpListener, {});\n\n this.#tcpListeners.set(handle, tcpListener);\n\n return tcpListener;\n }\n\n async connect(options: TcpConnectionOptions) {\n using hostPtr = this.copyToMemory(await this.#resolveHost(options.host));\n\n const handle = this.exports.create_tcp_connection(hostPtr, options.port);\n\n const tcpConnection = await this.#tcpConnections.wait(handle);\n\n if (!tcpConnection) {\n throw new Error('tcp failed to connect');\n }\n\n return tcpConnection;\n }\n}\n\nexport type TcpListenerOptions = {\n host?: string;\n port: number;\n};\n\nexport type TcpListener = {\n [Symbol.asyncIterator](): AsyncIterableIterator<TcpConnection>;\n};\n\nexport class VirtualTcpListener\n implements TcpListener, AsyncIterable<TcpConnection>\n{\n #connections: TcpConnection[] = [];\n #notifyConnection?: () => void;\n\n constructor() {\n tcpListenerHooks.setInner(this, {\n accept: async (connection: TcpConnection) => {\n this.#connections.push(connection);\n this.#notifyConnection?.();\n },\n });\n }\n\n async *[Symbol.asyncIterator](): AsyncIterableIterator<TcpConnection> {\n while (true) {\n await new Promise<void>((resolve) => {\n this.#notifyConnection = resolve;\n });\n\n yield* this.#connections;\n this.#connections = [];\n }\n }\n}\n\nexport type TcpConnectionOptions = {\n host: string;\n port: number;\n};\n\nexport type TcpConnection = {\n readable: ReadableStream<Uint8Array>;\n writable: WritableStream<Uint8Array>;\n close(): Promise<void>;\n [Symbol.asyncIterator](): AsyncIterator<Uint8Array>;\n};\n\nexport class VirtualTcpConnection\n implements TcpConnection, AsyncIterable<Uint8Array>\n{\n #receiveBuffer: Uint8Array[] = [];\n #readableController?: ReadableStreamDefaultController<Uint8Array>;\n #writableController?: WritableStreamDefaultController;\n\n readable: ReadableStream<Uint8Array>;\n writable: WritableStream<Uint8Array>;\n\n constructor() {\n tcpConnectionHooks.setInner(this, {\n receive: async (data: Uint8Array) => {\n // We maintain our own receive buffer prior to enqueueing to the readable\n // stream so that we can send window updates as data is consumed\n this.#receiveBuffer.push(data);\n this.#enqueueBuffer();\n },\n close: async () => {\n this.close();\n },\n });\n\n this.readable = new ReadableStream(\n {\n start: (controller) => {\n this.#readableController = controller;\n },\n pull: () => {\n this.#enqueueBuffer();\n },\n },\n {\n highWaterMark: READABLE_HIGH_WATER_MARK,\n size: (chunk) => chunk.byteLength,\n }\n );\n\n this.writable = new WritableStream(\n {\n start: (controller) => {\n this.#writableController = controller;\n },\n write: async (chunk) => {\n await tcpConnectionHooks.getOuter(this).send(chunk);\n },\n },\n {\n // Send buffer is managed by the TCP stack\n highWaterMark: 0,\n }\n );\n }\n\n #enqueueBuffer() {\n if (!this.#readableController?.desiredSize) {\n return;\n }\n\n let bytesEnqueued = 0;\n\n // Enqueue chunks until the desired size is reached\n while (this.#receiveBuffer.length > 0) {\n const chunkLength = this.#receiveBuffer[0]!.length;\n\n if (bytesEnqueued + chunkLength > this.#readableController.desiredSize) {\n break;\n }\n\n const chunk = this.#receiveBuffer.shift()!;\n this.#readableController.enqueue(chunk);\n bytesEnqueued += chunk.length;\n }\n\n // Notify the TCP stack that we've read the data\n if (bytesEnqueued > 0) {\n tcpConnectionHooks.getOuter(this).updateReceiveBuffer(bytesEnqueued);\n }\n }\n\n async close() {\n await tcpConnectionHooks.getOuter(this).close();\n this.#readableController?.error(new Error('tcp connection closed'));\n this.#writableController?.error(new Error('tcp connection closed'));\n }\n\n [Symbol.asyncIterator](): AsyncIterator<Uint8Array> {\n if (this.readable.locked) {\n throw new Error('readable stream already locked');\n }\n return fromReadable(this.readable);\n }\n}\n","import {\n parseIPv4Address,\n serializeIPv4Cidr,\n type IPv4Address,\n type IPv4Cidr,\n} from '@tcpip/wire';\nimport type { Pointer } from '../types.js';\nimport {\n ExtendedReadableStream,\n fromReadable,\n Hooks,\n nextMicrotask,\n} from '../util.js';\nimport { Bindings } from './base.js';\n\ntype TunInterfaceHandle = Pointer;\n\ntype TunInterfaceOuterHooks = {\n handle: TunInterfaceHandle;\n sendPacket(packet: Uint8Array): void;\n getIPv4Address(): IPv4Address | undefined;\n getIPv4Netmask(): IPv4Address | undefined;\n};\n\ntype TunInterfaceInnerHooks = {\n receivePacket(packet: Uint8Array): void;\n};\n\nconst tunInterfaceHooks = new Hooks<\n TunInterface,\n TunInterfaceOuterHooks,\n TunInterfaceInnerHooks\n>();\n\nexport type TunImports = {\n register_tun_interface(handle: TunInterfaceHandle): void;\n receive_packet(\n handle: TunInterfaceHandle,\n packetPtr: number,\n length: number\n ): Promise<void>;\n};\n\nexport type TunExports = {\n create_tun_interface(\n ipAddress: Pointer,\n netmask: Pointer\n ): TunInterfaceHandle;\n remove_tun_interface(handle: TunInterfaceHandle): void;\n send_tun_interface(\n handle: TunInterfaceHandle,\n packet: Pointer,\n length: number\n ): void;\n};\n\nexport class TunBindings extends Bindings<TunImports, TunExports> {\n interfaces = new Map<TunInterfaceHandle, TunInterface>();\n\n imports = {\n register_tun_interface: (handle: TunInterfaceHandle) => {\n const tunInterface = new VirtualTunInterface();\n\n tunInterfaceHooks.setOuter(tunInterface, {\n handle,\n sendPacket: (packet) => {\n const packetPtr = this.copyToMemory(packet);\n this.exports.send_tun_interface(handle, packetPtr, packet.length);\n },\n getIPv4Address: () => {\n const ipPtr = this.exports.get_interface_ip4_address(handle);\n\n if (ipPtr === 0) {\n return;\n }\n\n const ipBytes = this.viewFromMemory(ipPtr, 4);\n return parseIPv4Address(ipBytes);\n },\n getIPv4Netmask: () => {\n const netmaskPtr = this.exports.get_interface_ip4_netmask(handle);\n\n if (netmaskPtr === 0) {\n return;\n }\n\n const netmaskBytes = this.viewFromMemory(netmaskPtr, 4);\n return parseIPv4Address(netmaskBytes);\n },\n });\n\n this.interfaces.set(handle, tunInterface);\n },\n receive_packet: async (\n handle: TunInterfaceHandle,\n packetPtr: number,\n length: number\n ) => {\n const packet = this.copyFromMemory(packetPtr, length);\n\n // Wait for synchronous lwIP operations to complete to prevent reentrancy issues\n // This also gives the consumer a chance to start listening before we enqueue the first packet\n await nextMicrotask();\n\n const tunInterface = this.interfaces.get(handle);\n\n if (!tunInterface) {\n console.error('received packet on unknown tun interface');\n return;\n }\n\n tunInterfaceHooks\n .getInner(tunInterface)\n .receivePacket(new Uint8Array(packet));\n },\n };\n\n async create(options: TunInterfaceOptions) {\n const { ipAddress, netmask } = options.ip\n ? serializeIPv4Cidr(options.ip)\n : {};\n\n using ipAddressPtr = ipAddress ? this.copyToMemory(ipAddress) : undefined;\n using netmaskPtr = netmask ? this.copyToMemory(netmask) : undefined;\n\n const handle = this.exports.create_tun_interface(\n ipAddressPtr ?? 0,\n netmaskPtr ?? 0\n );\n\n const tunInterface = this.interfaces.get(handle);\n\n if (!tunInterface) {\n throw new Error('tun interface failed to register');\n }\n\n return tunInterface;\n }\n\n async remove(tunInterface: TunInterface) {\n for (const [handle, loopback] of this.interfaces.entries()) {\n if (loopback === tunInterface) {\n this.exports.remove_tun_interface(handle);\n this.interfaces.delete(handle);\n return;\n }\n }\n }\n}\n\nexport type TunInterfaceOptions = {\n ip?: IPv4Cidr;\n};\n\nexport type TunInterface = {\n readonly type: 'tun';\n readonly ip?: IPv4Address;\n readonly netmask?: IPv4Address;\n readable: ReadableStream<Uint8Array>;\n writable: WritableStream<Uint8Array>;\n listen(): AsyncIterableIterator<Uint8Array>;\n [Symbol.asyncIterator](): AsyncIterableIterator<Uint8Array>;\n};\n\nexport class VirtualTunInterface implements TunInterface {\n #readableController?: ReadableStreamController<Uint8Array>;\n #isListening = false;\n\n readonly type = 'tun' as const;\n get ip(): IPv4Address | undefined {\n return tunInterfaceHooks.getOuter(this).getIPv4Address();\n }\n get netmask(): IPv4Address | undefined {\n return tunInterfaceHooks.getOuter(this).getIPv4Netmask();\n }\n readable: ReadableStream<Uint8Array>;\n writable: WritableStream<Uint8Array>;\n\n constructor() {\n tunInterfaceHooks.setInner(this, {\n receivePacket: async (packet: Uint8Array) => {\n // Do not buffer packets until the consumer signals intent\n // to listen - otherwise memory will grow indefinitely\n if (!this.#isListening) {\n return;\n }\n\n if (!this.#readableController) {\n throw new Error('readable stream not initialized');\n }\n\n this.#readableController?.enqueue(packet);\n },\n });\n\n this.readable = new ExtendedReadableStream<Uint8Array>({\n start: (controller) => {\n this.#readableController = controller;\n },\n lock: () => {\n // We interpret anything that locks the stream (getReader, pipeThrough, pipeTo, tee)\n // as intent to start listening\n this.#isListening = true;\n },\n });\n\n this.writable = new WritableStream({\n write: (packet) => {\n tunInterfaceHooks.getOuter(this).sendPacket(packet);\n },\n });\n }\n\n listen() {\n if (this.readable.locked) {\n throw new Error('readable stream already locked');\n }\n return fromReadable(this.readable);\n }\n\n [Symbol.asyncIterator](): AsyncIterableIterator<Uint8Array> {\n return this.listen();\n }\n}\n","import type { DnsClient } from '@tcpip/dns';\nimport {\n type IPv4Address,\n parseIPv4Address,\n serializeIPv4Address,\n} from '@tcpip/wire';\nimport { LwipError } from '../lwip/errors.js';\nimport type { Pointer } from '../types.js';\nimport { EventMap, fromReadable, Hooks, nextMicrotask } from '../util.js';\nimport { Bindings } from './base.js';\n\nexport type UdpDatagram = {\n host: string;\n port: number;\n data: Uint8Array;\n};\n\ntype UdpSocketHandle = Pointer;\n\ntype UdpSocketOuterHooks = {\n send(datagram: UdpDatagram): Promise<void>;\n close(): Promise<void>;\n};\n\ntype UdpSocketInnerHooks = {\n receive(datagram: UdpDatagram): Promise<void>;\n};\n\nconst udpSocketHooks = new Hooks<\n UdpSocket,\n UdpSocketOuterHooks,\n UdpSocketInnerHooks\n>();\n\nexport type UdpImports = {\n receive_udp_datagram(\n handle: UdpSocketHandle,\n ip: number,\n port: number,\n datagramPtr: number,\n length: number\n ): Promise<void>;\n};\n\nexport type UdpExports = {\n open_udp_socket(host: Pointer | null, port: number): UdpSocketHandle;\n close_udp_socket(handle: UdpSocketHandle): void;\n send_udp_datagram(\n handle: UdpSocketHandle,\n ip: Pointer | null,\n port: number,\n datagram: Pointer,\n length: number\n ): number;\n};\n\nexport class UdpBindings extends Bindings<UdpImports, UdpExports> {\n #udpSockets = new EventMap<UdpSocketHandle, UdpSocket>();\n #dnsClient: DnsClient;\n\n async #resolveHost(host: string) {\n try {\n return serializeIPv4Address(host);\n } catch (e) {\n const ip = await this.#dnsClient.lookup(host);\n return serializeIPv4Address(ip);\n }\n }\n\n constructor(dnsClient: DnsClient) {\n super();\n this.#dnsClient = dnsClient;\n }\n\n imports = {\n receive_udp_datagram: async (\n handle: UdpSocketHandle,\n hostPtr: number,\n port: number,\n datagramPtr: number,\n length: number\n ) => {\n const host = this.copyFromMemory(hostPtr, 4);\n const datagram = this.copyFromMemory(datagramPtr, length);\n const socket = this.#udpSockets.get(handle);\n\n if (!socket) {\n console.error('received datagram on unknown udp socket');\n return;\n }\n\n // Wait for synchronous lwIP operations to complete to prevent reentrancy issues\n await nextMicrotask();\n\n udpSocketHooks.getInner(socket).receive({\n host: parseIPv4Address(host),\n port,\n data: datagram,\n });\n },\n };\n\n async open(options: UdpSocketOptions) {\n using hostPtr = options.host\n ? this.copyToMemory(await this.#resolveHost(options.host))\n : null;\n\n const handle = this.exports.open_udp_socket(hostPtr, options.port ?? 0);\n\n if (Number(handle) === 0) {\n throw new Error('failed to open udp socket');\n }\n\n const udpSocket = new VirtualUdpSocket();\n\n udpSocketHooks.setOuter(udpSocket, {\n send: async (datagram: UdpDatagram) => {\n using hostPtr = this.copyToMemory(\n await this.#resolveHost(datagram.host)\n );\n using datagramPtr = this.copyToMemory(datagram.data);\n\n const result = this.exports.send_udp_datagram(\n handle,\n hostPtr,\n datagram.port,\n datagramPtr,\n datagram.data.length\n );\n\n if (result !== LwipError.ERR_OK) {\n throw new Error(`failed to send udp datagram: ${result}`);\n }\n },\n close: async () => {\n this.exports.close_udp_socket(handle);\n this.#udpSockets.delete(handle);\n },\n });\n\n this.#udpSockets.set(handle, udpSocket);\n\n return udpSocket;\n }\n}\n\nexport type UdpSocketOptions = {\n /**\n * The local host to bind to.\n *\n * If not provided, the socket will bind to all available interfaces.\n */\n host?: string;\n\n /**\n * The local port to bind to.\n *\n * If not provided, the socket will bind to a random port.\n */\n port?: number;\n};\n\nexport type UdpSocket = {\n readable: ReadableStream<UdpDatagram>;\n writable: WritableStream<UdpDatagram>;\n close(): Promise<void>;\n [Symbol.asyncIterator](): AsyncIterator<UdpDatagram>;\n};\n\nexport class VirtualUdpSocket implements UdpSocket, AsyncIterable<UdpDatagram> {\n #readableController?: ReadableStreamDefaultController<UdpDatagram>;\n #writableController?: WritableStreamDefaultController;\n\n readable: ReadableStream<UdpDatagram>;\n writable: WritableStream<UdpDatagram>;\n\n constructor() {\n udpSocketHooks.setInner(this, {\n receive: async (datagram: UdpDatagram) => {\n if (!this.#readableController) {\n throw new Error('readable controller not initialized');\n }\n this.#readableController.enqueue(datagram);\n },\n });\n\n this.readable = new ReadableStream({\n start: (controller) => {\n this.#readableController = controller;\n },\n });\n\n this.writable = new WritableStream({\n start: (controller) => {\n this.#writableController = controller;\n },\n write: async (datagram) => {\n await udpSocketHooks.getOuter(this).send(datagram);\n },\n });\n }\n\n async close() {\n await udpSocketHooks.getOuter(this).close();\n this.#readableController?.error(new Error('udp socket closed'));\n this.#writableController?.error(new Error('udp socket closed'));\n }\n\n [Symbol.asyncIterator](): AsyncIterator<UdpDatagram> {\n if (this.readable.locked) {\n throw new Error('readable stream already locked');\n }\n return fromReadable(this.readable);\n }\n}\n","const IN_NODE =\n typeof process === 'object' &&\n typeof process.versions === 'object' &&\n typeof process.versions.node === 'string';\n\n/**\n * Fetches a file from the network or filesystem\n * depending on the environment.\n */\nexport async function fetchFile(input: string | URL, type: string) {\n if (IN_NODE) {\n return fetchFileNode(input, type);\n }\n return fetch(input);\n}\n\nasync function fetchFileNode(input: string | URL, type: string) {\n const fs = await import('node:fs');\n const { Readable } = await import('node:stream');\n const nodeStream = fs.createReadStream(input);\n const stream = Readable.toWeb(nodeStream) as ReadableStream<Uint8Array>;\n return new Response(stream, { headers: { 'Content-Type': type } });\n}\n"],"mappings":"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","names":["DisposeSymbol","ConsoleStdout","File","OpenFile","WASI","DnsClient","generateMacAddress","parseIPv4Address","parseMacAddress","serializeIPv4Cidr","serializeMacAddress","Hooks","#outerHooks","#innerHooks","key","hooks","UniquePointer","address","free","DisposeSymbol","EventMap","#listeners","resolve","listeners","value","listener","fromReadable","readable","options","reader","fromReader","done","ExtendedReadableStream","#notifyLock","lock","underlyingSource","strategy","transform","stream","dest","promise","a","b","nextMicrotask","Bindings","#exports","exports","size","UniquePointer","data","bytes","length","pointer","ptr","buffer","generateMacAddress","parseIPv4Address","parseMacAddress","serializeIPv4Cidr","serializeMacAddress","LwipError","tapInterfaceHooks","Hooks","TapBindings","Bindings","handle","tapInterface","VirtualTapInterface","frame","framePtr","result","LwipError","macPtr","macBytes","parseMacAddress","ipPtr","ipBytes","parseIPv4Address","netmaskPtr","netmaskBytes","length","nextMicrotask","options","_stack","macAddress","serializeMacAddress","generateMacAddress","ipAddress","netmask","serializeIPv4Cidr","macAddressPtr","__using","ipAddressPtr","_","_error","_hasError","__callDispose","loopback","#readableController","#isListening","ExtendedReadableStream","controller","packet","err","fromReadable","bridgeInterfaceHooks","Hooks","BridgeBindings","Bindings","options","_stack","macAddress","serializeMacAddress","generateMacAddress","ipAddress","netmask","serializeIPv4Cidr","macAddressPtr","__using","ipAddressPtr","netmaskPtr","portHandles","port","tapInterfaceHooks","portHandlesPtr","handle","bridgeInterface","VirtualBridgeInterface","macPtr","macBytes","parseMacAddress","ipPtr","ipBytes","parseIPv4Address","netmaskBytes","_error","_hasError","__callDispose","loopback","parseIPv4Address","serializeIPv4Cidr","loopbackInterfaceHooks","Hooks","LoopbackBindings","Bindings","handle","loopbackInterface","VirtualLoopbackInterface","ipPtr","ipBytes","parseIPv4Address","netmaskPtr","netmaskBytes","options","_stack","ipAddress","netmask","serializeIPv4Cidr","ipAddressPtr","__using","_","_error","_hasError","__callDispose","loopback","serializeIPv4Address","tcpListenerHooks","Hooks","tcpConnectionHooks","MAX_SEGMENT_SIZE","MAX_WINDOW_SIZE","SEND_BUFFER_SIZE","READABLE_HIGH_WATER_MARK","TcpBindings","Bindings","#tcpListeners","#tcpConnections","EventMap","#tcpAcks","#dnsClient","#resolveHost","host","serializeIPv4Address","ip","dnsClient","listenerHandle","connectionHandle","listener","nextMicrotask","connection","VirtualTcpConnection","data","dataPtr","bytesQueued","resolve","bytesRemaining","length","result","LwipError","handle","chunkPtr","chunk","notifyAck","options","_stack","hostPtr","__using","tcpListener","VirtualTcpListener","_","_error","_hasError","__callDispose","tcpConnection","#connections","#notifyConnection","#receiveBuffer","#readableController","#writableController","#enqueueBuffer","controller","bytesEnqueued","chunkLength","fromReadable","parseIPv4Address","serializeIPv4Cidr","tunInterfaceHooks","Hooks","TunBindings","Bindings","handle","tunInterface","VirtualTunInterface","packet","packetPtr","ipPtr","ipBytes","parseIPv4Address","netmaskPtr","netmaskBytes","length","nextMicrotask","options","_stack","ipAddress","netmask","serializeIPv4Cidr","ipAddressPtr","__using","_","_error","_hasError","__callDispose","loopback","#readableController","#isListening","ExtendedReadableStream","controller","fromReadable","parseIPv4Address","serializeIPv4Address","udpSocketHooks","Hooks","UdpBindings","Bindings","#udpSockets","EventMap","#dnsClient","#resolveHost","host","serializeIPv4Address","ip","dnsClient","handle","hostPtr","port","datagramPtr","length","datagram","socket","nextMicrotask","parseIPv4Address","options","_stack","__using","udpSocket","VirtualUdpSocket","result","LwipError","_","_error","_hasError","__callDispose","#readableController","#writableController","controller","fromReadable","IN_NODE","fetchFile","input","type","fetchFileNode","fs","Readable","nodeStream","stream","createStack","options","stack","VirtualNetworkStack","#options","#loopIntervalId","#dnsClient","#loopbackBindings","#tunBindings","#tapBindings","#bridgeBindings","#tcpBindings","#udpBindings","#listInterfaces","DnsClient","LoopbackBindings","TunBindings","TapBindings","BridgeBindings","TcpBindings","UdpBindings","#init","wasi","WASI","OpenFile","File","ConsoleStdout","msg","source","fetchFile","instance","wasmInstance","netInterface"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tcpip",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Virtual TCP/IP stack that can run anywhere",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/tcpip.wasm CHANGED
Binary file