tcpip 0.2.4 → 0.3.0
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 +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +61 -50
- package/dist/index.d.ts +61 -50
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
- package/tcpip.wasm +0 -0
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 Y=(s,e)=>(e=Symbol[s])?e:Symbol.for("Symbol."+s),J=s=>{throw TypeError(s)};var p=(s,e,t)=>{if(e!=null){typeof e!="object"&&typeof e!="function"&&J("Object expected");var r,n;t&&(r=e[Y("asyncDispose")]),r===void 0&&(r=e[Y("dispose")],t&&(n=r)),typeof r!="function"&&J("Object not disposable"),n&&(r=function(){try{n.call(this)}catch(o){return Promise.reject(o)}}),s.push([t,r,e])}else t&&s.push([t]);return e},m=(s,e,t)=>{var r=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=t?new r(a,e,"An error was suppressed during disposal"):(t=!0,a),o=a=>{for(;a=s.pop();)try{var i=a[1]&&a[1].call(a[2]);if(a[0])return Promise.resolve(i).then(o,c=>(n(c),o()))}catch(c){n(c)}if(t)throw e};return o()};var l="dispose"in Symbol?Symbol.dispose:Symbol.for("Symbol.dispose");var _browser_wasi_shim = require('@bjorn3/browser_wasi_shim');function ee(s){return s.join(".")}function P(s){return new Uint8Array(s.split(".").map(e=>parseInt(e,10)))}function w(s){let[e,t]=s.split("/");if(!e||!t)throw new Error("invalid cidr");let r=parseInt(t,10),n=ne(r);return{ipAddress:P(e),netmask:n}}function ne(s){let e=new Uint8Array(4);for(let t=0;t<s;t++){let r=Math.floor(t/8),n=7-t%8,o=e[r];if(o===void 0)throw new Error("invalid mask size");e[r]=o|1<<n}return e}function O(s){let e=s.split(":");if(e.length!==6)throw new Error("invalid mac address");return new Uint8Array(e.map(t=>{let r=parseInt(t,16);if(Number.isNaN(r))throw new Error("invalid mac address");return r}))}var h=class{#e=new WeakMap;#t=new WeakMap;setOuter(e,t){this.#e.set(e,t)}setInner(e,t){this.#t.set(e,t)}getOuter(e){let t=this.#e.get(e);if(!t)throw new Error(`outer hooks not set for ${e}`);return t}getInner(e){let t=this.#t.get(e);if(!t)throw new Error(`inner hooks not set for ${e}`);return t}},M=class extends Number{constructor(e,t){super(e),this.free=t}[l](){this.free(this.valueOf())}},x=class extends Map{#e=new Map;wait(e){return new Promise(t=>{let r=_nullishCoalesce(this.#e.get(e), () => (new Set));r.add(t),this.#e.set(e,r)})}set(e,t){super.set(e,t);let r=this.#e.get(e);if(r)for(let n of r)n(t),r.delete(n);return this}};function g(s,e){let t=s.getReader();return oe(t,e)}async function*oe(s,e){try{for(;;){let{done:t,value:r}=await s.read();if(t)return r;yield r}}finally{_optionalChain([e, 'optionalAccess', _2 => _2.preventCancel])||await s.cancel(),s.releaseLock()}}var _=class extends ReadableStream{#e;constructor({lock:e,...t},r){super(t,r),this.#e=e}getReader(){let e=super.getReader();return this.locked&&_optionalChain([this, 'access', _3 => _3.#e, 'optionalCall', _4 => _4()]),e}pipeThrough(e,t){let r=super.pipeThrough(e,t);return this.locked&&_optionalChain([this, 'access', _5 => _5.#e, 'optionalCall', _6 => _6()]),r}pipeTo(e,t){let r=super.pipeTo(e,t);return this.locked&&_optionalChain([this, 'access', _7 => _7.#e, 'optionalCall', _8 => _8()]),r}tee(){let[e,t]=super.tee();return this.locked&&_optionalChain([this, 'access', _9 => _9.#e, 'optionalCall', _10 => _10()]),[e,t]}};async function I(){return await new Promise(s=>queueMicrotask(s))}function L(){let s=new Uint8Array(6);return crypto.getRandomValues(s),s[0]=s[0]&252|2,s}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 M(this.exports.malloc(e),this.exports.free)}copyToMemory(e){let t=new Uint8Array(e),r=t.length,n=this.smartMalloc(r);return new Uint8Array(this.exports.memory.buffer,n.valueOf(),r).set(t),n}copyFromMemory(e,t){let r=this.exports.memory.buffer.slice(e,e+t);return new Uint8Array(r)}};var T={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 A=new h,C= (_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 t=new U;A.setOuter(t,{handle:e,sendFrame:r=>{let n=this.copyToMemory(r),o=this.exports.send_tap_interface(e,n,r.length);if(o!==T.ERR_OK)throw new Error(`failed to send frame: ${o}`)}}),this.interfaces.set(e,t)},receive_frame:async(e,t,r)=>{let n=this.copyFromMemory(t,r);await I();let o=this.interfaces.get(e);if(!o){console.error("received frame on unknown tap interface");return}A.getInner(o).receiveFrame(new Uint8Array(n))}}}async create(e){var f=[];try{let t=e.mac?O(e.mac):L();let{ipAddress:r,netmask:n}=e.ip?w(e.ip):{};let o=p(f,this.copyToMemory(t));let a=p(f,r?this.copyToMemory(r):void 0);let i=p(f,n?this.copyToMemory(n):void 0);let c=this.exports.create_tap_interface(o,_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(y){var b=y,v=!0}finally{m(f,b,v)}}async remove(e){for(let[t,r]of this.interfaces.entries())if(r===e){this.exports.remove_tap_interface(t),this.interfaces.delete(t);return}}}, _class),U= exports.TapInterface =class{#e;#t=!1;constructor(){A.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 _({start:e=>{this.#e=e},lock:()=>{this.#t=!0}}),this.writable=new WritableStream({write:e=>{try{A.getOuter(this).sendFrame(e)}catch(t){console.error("tap interface send failed",t)}}})}listen(){if(this.readable.locked)throw new Error("readable stream already locked");return g(this.readable)}[Symbol.asyncIterator](){return this.listen()}};var B= (_class2 =class extends u{constructor(...args2) { super(...args2); _class2.prototype.__init3.call(this);_class2.prototype.__init4.call(this); }__init3() {this.interfaces=new Map}__init4() {this.imports={}}async create(e){var b=[];try{let t=e.mac?O(e.mac):L();let{ipAddress:r,netmask:n}=e.ip?w(e.ip):{};let o=p(b,this.copyToMemory(t));let a=p(b,r?this.copyToMemory(r):void 0);let i=p(b,n?this.copyToMemory(n):void 0);let c=new Uint32Array(e.ports.map(Q=>Number(A.getOuter(Q).handle)));let d=p(b,this.copyToMemory(c.buffer));let f=this.exports.create_bridge_interface(o,_nullishCoalesce(a, () => (0)),_nullishCoalesce(i, () => (0)),d,e.ports.length);let y=new D;this.interfaces.set(f,y);return y}catch(v){var q=v,G=!0}finally{m(b,q,G)}}async remove(e){for(let[t,r]of this.interfaces.entries())if(r===e){this.exports.remove_bridge_interface(t),this.interfaces.delete(t);return}}}, _class2),D= exports.BridgeInterface =class{};var F= (_class3 =class extends u{constructor(...args3) { super(...args3); _class3.prototype.__init5.call(this);_class3.prototype.__init6.call(this); }__init5() {this.interfaces=new Map}__init6() {this.imports={register_loopback_interface:e=>{let t=new E;this.interfaces.set(e,t)}}}async create(e){var c=[];try{let{ipAddress:t,netmask:r}=e.ip?w(e.ip):{};let n=p(c,t?this.copyToMemory(t):void 0);let o=p(c,r?this.copyToMemory(r):void 0);let a=this.exports.create_loopback_interface(_nullishCoalesce(n, () => (0)),_nullishCoalesce(o, () => (0)));let i=this.interfaces.get(a);if(!i)throw new Error("loopback interface failed to register");return i}catch(d){var f=d,y=!0}finally{m(c,f,y)}}async remove(e){for(let[t,r]of this.interfaces.entries())if(r===e){this.exports.remove_loopback_interface(t),this.interfaces.delete(t);return}}}, _class3),E= exports.LoopbackInterface =class{};var Z=new h,k=new h,X=1460,Ze=X*4,Xe=X*4,se=X,W= (_class4 =class extends u{constructor(...args4) { super(...args4); _class4.prototype.__init7.call(this); }#e=new Map;#t=new x;#r=new Map;__init7() {this.imports={accept_tcp_connection:async(e,t)=>{let r=this.#e.get(e);if(!r){console.error("new tcp connection to unknown listener");return}await I();let n=new S;k.setOuter(n,{send:async o=>{let a=Number(this.copyToMemory(o)),i=this.exports.send_tcp_chunk(t,a,o.length);for(;i<o.length;){await new Promise(d=>{this.#r.set(t,d)});let c=o.length-i;i+=this.exports.send_tcp_chunk(t,a+i,c)}},updateReceiveBuffer:o=>{this.exports.update_tcp_receive_buffer(t,o)},close:async()=>{let o=this.exports.close_tcp_connection(t);if(o!==T.ERR_OK)throw new Error(`failed to close tcp connection: ${o}`)}}),this.#t.set(t,n),Z.getInner(r).accept(n)},connected_tcp_connection:async e=>{await I();let t=new S;k.setOuter(t,{send:async r=>{let n=Number(this.copyToMemory(r)),o=this.exports.send_tcp_chunk(e,n,r.length);for(;o<r.length;){await new Promise(i=>{this.#r.set(e,i)});let a=r.length-o;o+=this.exports.send_tcp_chunk(e,n+o,a)}},updateReceiveBuffer:r=>{this.exports.update_tcp_receive_buffer(e,r)},close:async()=>{this.exports.close_tcp_connection(e)}}),this.#t.set(e,t)},closed_tcp_connection:async e=>{let t=this.#t.get(e);if(!t){console.error("received close on unknown tcp connection");return}await k.getInner(t).close()},receive_tcp_chunk:async(e,t,r)=>{let n=this.copyFromMemory(t,r),o=this.#t.get(e);if(!o){console.error("received chunk on unknown tcp connection");return}await I(),k.getInner(o).receive(new Uint8Array(n))},sent_tcp_chunk:(e,t)=>{let r=this.#r.get(e);this.#r.delete(e),_optionalChain([r, 'optionalCall', _12 => _12(t)])}}}async listen(e){var o=[];try{let t=p(o,e.host?this.copyToMemory(P(e.host)):null);let r=this.exports.create_tcp_listener(t,e.port);let n=new N;Z.setOuter(n,{});this.#e.set(r,n);return n}catch(a){var i=a,c=!0}finally{m(o,i,c)}}async connect(e){var o=[];try{let t=p(o,this.copyToMemory(P(e.host)));let r=this.exports.create_tcp_connection(t,e.port);let n=await this.#t.wait(r);if(!n)throw new Error("tcp failed to connect");return n}catch(a){var i=a,c=!0}finally{m(o,i,c)}}}, _class4),N= exports.TcpListener =class{#e=[];#t;constructor(){Z.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=[]}},S= exports.TcpConnection =class{#e=[];#t;#r;constructor(){k.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:se,size:e=>e.byteLength}),this.writable=new WritableStream({start:e=>{this.#r=e},write:async e=>{await k.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 t=this.#e[0].length;if(e+t>this.#t.desiredSize)break;let r=this.#e.shift();this.#t.enqueue(r),e+=r.length}e>0&&k.getOuter(this).updateReceiveBuffer(e)}async close(){await k.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 g(this.readable)}};var z=new h,V= (_class5 =class extends u{constructor(...args5) { super(...args5); _class5.prototype.__init8.call(this);_class5.prototype.__init9.call(this); }__init8() {this.interfaces=new Map}__init9() {this.imports={register_tun_interface:e=>{let t=new R;z.setOuter(t,{sendPacket:r=>{let n=this.copyToMemory(r);this.exports.send_tun_interface(e,n,r.length)}}),this.interfaces.set(e,t)},receive_packet:async(e,t,r)=>{let n=this.copyFromMemory(t,r);await I();let o=this.interfaces.get(e);if(!o){console.error("received packet on unknown tun interface");return}z.getInner(o).receivePacket(new Uint8Array(n))}}}async create(e){var c=[];try{let{ipAddress:t,netmask:r}=e.ip?w(e.ip):{};let n=p(c,t?this.copyToMemory(t):void 0);let o=p(c,r?this.copyToMemory(r):void 0);let a=this.exports.create_tun_interface(_nullishCoalesce(n, () => (0)),_nullishCoalesce(o, () => (0)));let i=this.interfaces.get(a);if(!i)throw new Error("tun interface failed to register");return i}catch(d){var f=d,y=!0}finally{m(c,f,y)}}async remove(e){for(let[t,r]of this.interfaces.entries())if(r===e){this.exports.remove_tun_interface(t),this.interfaces.delete(t);return}}}, _class5),R= exports.TunInterface =class{#e;#t=!1;constructor(){z.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 _({start:e=>{this.#e=e},lock:()=>{this.#t=!0}}),this.writable=new WritableStream({write:e=>{z.getOuter(this).sendPacket(e)}})}listen(){if(this.readable.locked)throw new Error("readable stream already locked");return g(this.readable)}[Symbol.asyncIterator](){return this.listen()}};var H=new h,$= (_class6 =class extends u{constructor(...args6) { super(...args6); _class6.prototype.__init10.call(this); }#e=new x;__init10() {this.imports={receive_udp_datagram:async(e,t,r,n,o)=>{let a=this.copyFromMemory(t,4),i=this.copyFromMemory(n,o),c=this.#e.get(e);if(!c){console.error("received datagram on unknown udp socket");return}await I(),H.getInner(c).receive({host:ee(a),port:r,data:i})}}}async open(e){var o=[];try{let t=p(o,e.host?this.copyToMemory(P(e.host)):null);let r=this.exports.open_udp_socket(t,_nullishCoalesce(e.port, () => (0)));let n=new K;H.setOuter(n,{send:async d=>{var v=[];try{let f=p(v,this.copyToMemory(P(d.host)));let y=p(v,this.copyToMemory(d.data));let b=this.exports.send_udp_datagram(r,f,d.port,y,d.data.length);if(b!==T.ERR_OK)throw new Error(`failed to send udp datagram: ${b}`)}catch(q){var G=q,Q=!0}finally{m(v,G,Q)}},close:async()=>{this.exports.close_udp_socket(r),this.#e.delete(r)}});this.#e.set(r,n);return n}catch(a){var i=a,c=!0}finally{m(o,i,c)}}}, _class6),K= exports.UdpSocket =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 g(this.readable)}};var ae=typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string";async function te(s,e){return ae?ie(s,e):fetch(s)}async function ie(s,e){let t=await Promise.resolve().then(() => _interopRequireWildcard(require("fs"))),{Readable:r}=await Promise.resolve().then(() => _interopRequireWildcard(require("stream"))),n=t.createReadStream(s),o=r.toWeb(n);return new Response(o,{headers:{"Content-Type":e}})}async function le(s){let e=new j(s);return await e.ready,e}var j=class{#e;#t;#r=new F;#n=new V;#o=new C;#a=new B;#s=new W;#i=new $;get interfaces(){return this.#p()}constructor(e={}){this.#e={...e,initializeLoopback:_nullishCoalesce(e.initializeLoopback, () => (!0))},this.ready=this.#c(),this.ready.then(async()=>{this.#e.initializeLoopback&&await this.createLoopbackInterface({ip:"127.0.0.1/8"})})}async#c(){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}`))]),t=te(new URL("../tcpip.wasm",import.meta.url),"application/wasm"),{instance:r}=await WebAssembly.instantiateStreaming(t,{wasi_snapshot_preview1:e.wasiImport,env:{...this.#r.imports,...this.#n.imports,...this.#o.imports,...this.#a.imports,...this.#s.imports,...this.#i.imports}}),n=r;this.#r.register(n.exports),this.#n.register(n.exports),this.#o.register(n.exports),this.#a.register(n.exports),this.#s.register(n.exports),this.#i.register(n.exports);let o=e.start(n);if(o!==0)throw new Error(`wasi start failed with code ${o}`);this.#t=Number(setInterval(()=>{n.exports.process_queued_packets(),n.exports.process_timeouts()},100))}*#p(){yield*this.#r.interfaces.values(),yield*this.#n.interfaces.values(),yield*this.#o.interfaces.values()}async createLoopbackInterface(e){return await this.ready,this.#r.create(e)}async createTunInterface(e){return await this.ready,this.#n.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){if(await this.ready,e instanceof E)return this.#r.remove(e);if(e instanceof R)return this.#n.remove(e);if(e instanceof U)return this.#o.remove(e);throw new Error("unknown interface type")}async listenTcp(e){return await this.ready,this.#s.listen(e)}async connectTcp(e){return await this.ready,this.#s.connect(e)}async openUdp(e={}){return await this.ready,this.#i.open(e)}};exports.BridgeInterface = D; exports.LoopbackInterface = E; exports.NetworkStack = j; exports.TapInterface = U; exports.TcpConnection = S; exports.TcpListener = N; exports.TunInterface = R; exports.UdpSocket = K; exports.createStack = le;
|
|
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;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/grichardson/Documents/dev/tcpip.js/packages/tcpip/dist/index.cjs","../polyfills/disposable.ts","../src/stack.ts","../src/protocols/ipv4.ts","../src/protocols/ethernet.ts","../src/util.ts"],"names":["DisposeSymbol","parseIPv4Address","data","serializeIPv4Address","ip","byte","serializeIPv4Cidr","cidr","ipString","maskSizeString","maskSize","netmask","generateNetmask","mask","i","byteIndex","bitIndex","maskByte","serializeMacAddress","mac","segments","parsed","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 {\n BridgeBindings,\n type BridgeInterfaceOptions,\n} from './bindings/bridge-interface.js';\nimport {\n LoopbackBindings,\n LoopbackInterface,\n type LoopbackInterfaceOptions,\n} from './bindings/loopback-interface.js';\nimport {\n TapBindings,\n TapInterface,\n type TapInterfaceOptions,\n} from './bindings/tap-interface.js';\nimport {\n TcpBindings,\n type TcpConnectionOptions,\n type TcpListenerOptions,\n} from './bindings/tcp.js';\nimport {\n TunBindings,\n TunInterface,\n type TunInterfaceOptions,\n} from './bindings/tun-interface.js';\nimport { UdpBindings, type UdpSocketOptions } from './bindings/udp.js';\nimport { fetchFile } from './fetch-file.js';\nimport type { NetworkInterface, WasmInstance } from './types.js';\n\nexport async function createStack(options?: NetworkStackOptions) {\n const stack = new NetworkStack(options);\n await stack.ready;\n return stack;\n}\n\nexport type NetworkStackOptions = {\n initializeLoopback?: boolean;\n};\n\nexport class NetworkStack {\n #options: NetworkStackOptions;\n #loopIntervalId?: number;\n\n #loopbackBindings = new LoopbackBindings();\n #tunBindings = new TunBindings();\n #tapBindings = new TapBindings();\n #bridgeBindings = new BridgeBindings();\n #tcpBindings = new TcpBindings();\n #udpBindings = new 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 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 }\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(\n netInterface: LoopbackInterface | TunInterface | TapInterface\n ) {\n await this.ready;\n\n if (netInterface instanceof LoopbackInterface) {\n return this.#loopbackBindings.remove(netInterface);\n }\n\n if (netInterface instanceof TunInterface) {\n return this.#tunBindings.remove(netInterface);\n }\n\n if (netInterface instanceof TapInterface) {\n return this.#tapBindings.remove(netInterface);\n }\n\n throw new Error('unknown interface type');\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 createIcmpMessage,\n parseIcmpMessage,\n type IcmpMessage,\n} from './icmp.js';\nimport {\n createUdpDatagram,\n parseUdpDatagram,\n UDP_HEADER_LENGTH,\n type UdpDatagram,\n} from './udp.js';\nimport { calculateChecksum } from './util.js';\n\nexport type IPv4Address = `${number}.${number}.${number}.${number}`;\nexport type IPv4Cidr = `${IPv4Address}/${number}`;\n\nexport type IPv4PacketBase = {\n version: number;\n dscp: number;\n ecn: number;\n identification: number;\n flags: number;\n fragmentOffset: number;\n ttl: number;\n protocol: string;\n sourceIP: IPv4Address;\n destinationIP: IPv4Address;\n};\n\nexport type IcmpIPv4Packet = IPv4PacketBase & {\n protocol: 'icmp';\n payload: IcmpMessage;\n};\n\nexport type TcpIPv4Packet = IPv4PacketBase & {\n protocol: 'tcp';\n payload: Uint8Array;\n};\n\nexport type UdpIPv4Packet = IPv4PacketBase & {\n protocol: 'udp';\n payload: UdpDatagram;\n};\n\nexport type IPv4Packet = IcmpIPv4Packet | TcpIPv4Packet | UdpIPv4Packet;\n\nexport type IPv4Protocol = IPv4Packet['protocol'];\n\nexport type IPv4PseudoHeader = {\n sourceIP: IPv4Address;\n destinationIP: IPv4Address;\n protocol: IPv4Protocol;\n length: number;\n};\n\nexport const IPV4_HEADER_LENGTH = 20;\n\n/**\n * Parses an IPv4 packet into an object.\n */\nexport function parseIPv4Packet(data: Uint8Array): IPv4Packet {\n const dataView = new DataView(data.buffer, data.byteOffset, data.byteLength);\n\n const headerChecksum = dataView.getUint16(10);\n const header = data.subarray(0, IPV4_HEADER_LENGTH);\n\n if (calculateChecksum(header, 10) !== headerChecksum) {\n throw new Error('invalid ipv4 checksum');\n }\n\n const totalLength = dataView.getUint16(2);\n\n if (totalLength !== data.length) {\n throw new Error('invalid ipv4 total length');\n }\n\n const versionAndHeaderLength = dataView.getUint8(0);\n const version = versionAndHeaderLength >> 4;\n const headerLength = (versionAndHeaderLength & 0xf) * 4;\n const dscp = dataView.getUint8(1) >> 2;\n const ecn = dataView.getUint8(1) & 0x3;\n const identification = dataView.getUint16(4);\n const flags = dataView.getUint8(6) >> 5;\n const fragmentOffset =\n ((dataView.getUint8(6) & 0x1f) << 8) | dataView.getUint8(7);\n const ttl = dataView.getUint8(8);\n const protocol = parseIPv4Protocol(dataView.getUint8(9));\n const sourceIP = parseIPv4Address(data.subarray(12, 16));\n const destinationIP = parseIPv4Address(data.subarray(16, 20));\n const payload = data.subarray(headerLength);\n\n switch (protocol) {\n case 'icmp':\n return {\n version,\n dscp,\n ecn,\n identification,\n flags,\n fragmentOffset,\n ttl,\n protocol,\n sourceIP,\n destinationIP,\n payload: parseIcmpMessage(payload),\n };\n case 'tcp':\n return {\n version,\n dscp,\n ecn,\n identification,\n flags,\n fragmentOffset,\n ttl,\n protocol,\n sourceIP,\n destinationIP,\n payload,\n };\n case 'udp':\n return {\n version,\n dscp,\n ecn,\n identification,\n flags,\n fragmentOffset,\n ttl,\n protocol,\n sourceIP,\n destinationIP,\n payload: parseUdpDatagram(\n payload,\n serializeIPv4PseudoHeader({\n sourceIP,\n destinationIP,\n protocol,\n length: payload.length,\n })\n ),\n };\n default:\n throw new Error('unknown ipv4 protocol');\n }\n}\n\n/**\n * Serializes an IPv4 packet from an `IPv4Packet` object.\n */\nexport function createIPv4Packet(packet: IPv4Packet): Uint8Array {\n let payload: Uint8Array;\n\n switch (packet.protocol) {\n case 'icmp':\n payload = createIcmpMessage(packet.payload);\n break;\n case 'tcp':\n payload = packet.payload;\n break;\n case 'udp':\n payload = createUdpDatagram(packet.payload, {\n sourceIP: packet.sourceIP,\n destinationIP: packet.destinationIP,\n protocol: packet.protocol,\n length: UDP_HEADER_LENGTH + packet.payload.payload.length,\n });\n break;\n default:\n throw new Error('unknown ipv4 protocol');\n }\n\n const data = new Uint8Array(IPV4_HEADER_LENGTH + payload.length);\n const dataView = new DataView(data.buffer, data.byteOffset, data.byteLength);\n\n const totalLength = IPV4_HEADER_LENGTH + payload.length;\n\n dataView.setUint8(0, (packet.version << 4) | (IPV4_HEADER_LENGTH / 4));\n dataView.setUint8(1, (packet.dscp << 2) | packet.ecn);\n dataView.setUint16(2, totalLength);\n dataView.setUint16(4, packet.identification);\n dataView.setUint8(6, (packet.flags << 5) | (packet.fragmentOffset >> 8));\n dataView.setUint8(7, packet.fragmentOffset & 0xff);\n dataView.setUint8(8, packet.ttl);\n dataView.setUint8(9, serializeIPv4Protocol(packet.protocol));\n\n data.set(serializeIPv4Address(packet.sourceIP), 12);\n data.set(serializeIPv4Address(packet.destinationIP), 16);\n\n // Checksum applies to just the header\n const header = data.subarray(0, IPV4_HEADER_LENGTH);\n const checksum = calculateChecksum(header, 10);\n dataView.setUint16(10, checksum);\n\n data.set(payload, 20);\n\n return data;\n}\n\n/**\n * Parses an IPv4 address Uint8Array into a string.\n */\nexport function parseIPv4Address(data: Uint8Array) {\n return data.join('.') as IPv4Address;\n}\n\n/**\n * Serialize an IPv4 address string into a Uint8Array.\n */\nexport function serializeIPv4Address(ip: string) {\n return new Uint8Array(ip.split('.').map((byte) => parseInt(byte, 10)));\n}\n\nexport function parseIPv4Protocol(protocol: number) {\n switch (protocol) {\n case 1:\n return 'icmp';\n case 6:\n return 'tcp';\n case 17:\n return 'udp';\n default:\n throw new Error('unknown ipv4 protocol');\n }\n}\n\nexport function serializeIPv4Protocol(protocol: IPv4Protocol) {\n switch (protocol) {\n case 'icmp':\n return 1;\n case 'tcp':\n return 6;\n case 'udp':\n return 17;\n default:\n throw new Error('unknown ipv4 protocol');\n }\n}\n\n/**\n * Serialize a CIDR notation string into an object with a\n * Uint8Array IP address and netmask.\n */\nexport function serializeIPv4Cidr(cidr: IPv4Cidr) {\n const [ipString, maskSizeString] = cidr.split('/');\n\n if (!ipString || !maskSizeString) {\n throw new Error('invalid cidr');\n }\n\n const maskSize = parseInt(maskSizeString, 10);\n const netmask = generateNetmask(maskSize);\n\n return {\n ipAddress: serializeIPv4Address(ipString),\n netmask,\n };\n}\n\n/**\n * Generates a netmask from a mask size.\n */\nexport function generateNetmask(maskSize: number) {\n const mask = new Uint8Array(4);\n\n for (let i = 0; i < maskSize; i++) {\n const byteIndex = Math.floor(i / 8);\n const bitIndex = 7 - (i % 8);\n const maskByte = mask[byteIndex];\n if (maskByte === undefined) {\n throw new Error('invalid mask size');\n }\n mask[byteIndex] = maskByte | (1 << bitIndex);\n }\n\n return mask;\n}\n\n/**\n * Creates a pseudo header for use in calculating transport layer checksums.\n */\nexport function serializeIPv4PseudoHeader(pseudoHeader: IPv4PseudoHeader) {\n const buffer = new Uint8Array(12);\n const dataView = new DataView(\n buffer.buffer,\n buffer.byteOffset,\n buffer.byteLength\n );\n\n const sourceIPBuffer = serializeIPv4Address(pseudoHeader.sourceIP);\n const destinationIPBuffer = serializeIPv4Address(pseudoHeader.destinationIP);\n const protocolNumber = serializeIPv4Protocol(pseudoHeader.protocol);\n\n buffer.set(sourceIPBuffer, 0);\n buffer.set(destinationIPBuffer, 4);\n dataView.setUint8(8, 0);\n dataView.setUint8(9, protocolNumber);\n dataView.setUint16(10, pseudoHeader.length);\n\n return buffer;\n}\n","import { createArpMessage, parseArpMessage, type ArpMessage } from './arp.js';\nimport { createIPv4Packet, parseIPv4Packet, type IPv4Packet } from './ipv4.js';\n\nexport type MacAddress =\n `${string}:${string}:${string}:${string}:${string}:${string}`;\n\nexport type EthernetFrameBase = {\n destinationMac: MacAddress;\n sourceMac: MacAddress;\n};\n\nexport type IPv4EthernetFrame = EthernetFrameBase & {\n type: 'ipv4';\n payload: IPv4Packet;\n};\n\nexport type ARPEthernetFrame = EthernetFrameBase & {\n type: 'arp';\n payload: ArpMessage;\n};\n\n// TODO: IPv6EthernetFrame\nexport type EthernetFrame = IPv4EthernetFrame | ARPEthernetFrame;\n\n/**\n * Parses an Ethernet frame into an object.\n */\nexport function parseEthernetFrame(frame: Uint8Array): EthernetFrame {\n const destinationMacBytes = frame.subarray(0, 6);\n const sourceMacBytes = frame.subarray(6, 12);\n const typeBytes = frame.subarray(12, 14);\n const payload = frame.subarray(14);\n\n const destinationMac = parseMacAddress(destinationMacBytes);\n const sourceMac = parseMacAddress(sourceMacBytes);\n const type = parseEthernetType(typeBytes);\n\n switch (type) {\n case 'ipv4':\n return {\n destinationMac,\n sourceMac,\n type,\n payload: parseIPv4Packet(payload),\n };\n case 'arp':\n return {\n destinationMac,\n sourceMac,\n type,\n payload: parseArpMessage(payload),\n };\n default:\n throw new Error('unknown ethernet type');\n }\n}\n\n/**\n * Serializes an Ethernet frame from a Frame object.\n */\nexport function createEthernetFrame(frame: EthernetFrame): Uint8Array {\n let payload: Uint8Array;\n\n switch (frame.type) {\n case 'ipv4':\n payload = createIPv4Packet(frame.payload);\n break;\n break;\n case 'arp':\n payload = createArpMessage(frame.payload);\n break;\n default:\n throw new Error('unknown ethernet type');\n }\n\n const data = new Uint8Array(14 + payload.length);\n\n data.set(serializeMacAddress(frame.destinationMac), 0);\n data.set(serializeMacAddress(frame.sourceMac), 6);\n data.set(createEthernetType(frame.type), 12);\n data.set(payload, 14);\n\n return data;\n}\n\n/**\n * Parses a MAC address Uint8Array into a string.\n */\nexport function parseMacAddress(mac: Uint8Array) {\n if (mac.length !== 6) {\n throw new Error('invalid mac address');\n }\n\n return Array.from(mac)\n .map((byte) => byte.toString(16).padStart(2, '0'))\n .join(':') as MacAddress;\n}\n\n/**\n * Serializes a MAC address string into a Uint8Array.\n */\nexport function serializeMacAddress(mac: string) {\n const segments = mac.split(':');\n\n if (segments.length !== 6) {\n throw new Error('invalid mac address');\n }\n\n return new Uint8Array(\n segments.map((byte) => {\n const parsed = parseInt(byte, 16);\n if (Number.isNaN(parsed)) {\n throw new Error('invalid mac address');\n }\n return parsed;\n })\n );\n}\n\n/**\n * Parses an Ethernet type into a string.\n */\nexport function parseEthernetType(etherType: Uint8Array) {\n const dataView = new DataView(\n etherType.buffer,\n etherType.byteOffset,\n etherType.byteLength\n );\n\n const type = dataView.getUint16(0);\n\n switch (type) {\n case 0x0800:\n return 'ipv4';\n case 0x86dd:\n return 'ipv6';\n case 0x0806:\n return 'arp';\n default:\n throw new Error('unknown ethernet type');\n }\n}\n\n/**\n * Serializes an Ethernet type from a string.\n */\nexport function createEthernetType(type: 'ipv4' | 'ipv6' | 'arp') {\n const data = new Uint8Array(2);\n const dataView = new DataView(data.buffer, data.byteOffset, data.byteLength);\n\n switch (type) {\n case 'ipv4':\n dataView.setUint16(0, 0x0800);\n break;\n case 'ipv6':\n dataView.setUint16(0, 0x86dd);\n break;\n case 'arp':\n dataView.setUint16(0, 0x0806);\n break;\n default:\n throw new Error('unknown ethernet type');\n }\n\n return data;\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,mCCOpC,ICCMC,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 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"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,63 +1,77 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
type MacAddress = `${string}:${string}:${string}:${string}:${string}:${string}`;
|
|
5
|
-
|
|
6
|
-
type LoopbackInterfaceOptions = {
|
|
7
|
-
ip?: IPv4Cidr;
|
|
8
|
-
};
|
|
9
|
-
declare class LoopbackInterface {
|
|
10
|
-
}
|
|
1
|
+
import { MacAddress, IPv4Cidr, IPv4Address } from '@tcpip/wire';
|
|
2
|
+
import { NameServer } from '@tcpip/dns';
|
|
11
3
|
|
|
12
4
|
type TapInterfaceOptions = {
|
|
13
5
|
mac?: MacAddress;
|
|
14
6
|
ip?: IPv4Cidr;
|
|
15
7
|
};
|
|
16
|
-
|
|
17
|
-
|
|
8
|
+
type TapInterface = {
|
|
9
|
+
readonly type: 'tap';
|
|
10
|
+
readonly mac: MacAddress;
|
|
11
|
+
readonly ip?: IPv4Address;
|
|
12
|
+
readonly netmask?: IPv4Address;
|
|
18
13
|
readable: ReadableStream<Uint8Array>;
|
|
19
14
|
writable: WritableStream<Uint8Array>;
|
|
20
|
-
constructor();
|
|
21
15
|
listen(): AsyncIterableIterator<Uint8Array>;
|
|
22
16
|
[Symbol.asyncIterator](): AsyncIterableIterator<Uint8Array>;
|
|
23
|
-
}
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
type BridgeInterfaceOptions = {
|
|
20
|
+
ports: TapInterface[];
|
|
21
|
+
mac?: MacAddress;
|
|
22
|
+
ip?: IPv4Cidr;
|
|
23
|
+
};
|
|
24
|
+
type BridgeInterface = {
|
|
25
|
+
readonly type: 'bridge';
|
|
26
|
+
readonly mac: MacAddress;
|
|
27
|
+
readonly ip?: IPv4Address;
|
|
28
|
+
readonly netmask?: IPv4Address;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
type LoopbackInterfaceOptions = {
|
|
32
|
+
ip?: IPv4Cidr;
|
|
33
|
+
};
|
|
34
|
+
type LoopbackInterface = {
|
|
35
|
+
readonly type: 'loopback';
|
|
36
|
+
readonly ip?: IPv4Address;
|
|
37
|
+
readonly netmask?: IPv4Address;
|
|
38
|
+
};
|
|
24
39
|
|
|
25
40
|
type TcpListenerOptions = {
|
|
26
|
-
host?:
|
|
41
|
+
host?: string;
|
|
27
42
|
port: number;
|
|
28
43
|
};
|
|
29
|
-
|
|
30
|
-
#private;
|
|
31
|
-
constructor();
|
|
44
|
+
type TcpListener = {
|
|
32
45
|
[Symbol.asyncIterator](): AsyncIterableIterator<TcpConnection>;
|
|
33
|
-
}
|
|
46
|
+
};
|
|
34
47
|
type TcpConnectionOptions = {
|
|
35
|
-
host:
|
|
48
|
+
host: string;
|
|
36
49
|
port: number;
|
|
37
50
|
};
|
|
38
|
-
|
|
39
|
-
#private;
|
|
51
|
+
type TcpConnection = {
|
|
40
52
|
readable: ReadableStream<Uint8Array>;
|
|
41
53
|
writable: WritableStream<Uint8Array>;
|
|
42
|
-
constructor();
|
|
43
54
|
close(): Promise<void>;
|
|
44
55
|
[Symbol.asyncIterator](): AsyncIterator<Uint8Array>;
|
|
45
|
-
}
|
|
56
|
+
};
|
|
46
57
|
|
|
47
58
|
type TunInterfaceOptions = {
|
|
48
59
|
ip?: IPv4Cidr;
|
|
49
60
|
};
|
|
50
|
-
|
|
51
|
-
|
|
61
|
+
type TunInterface = {
|
|
62
|
+
readonly type: 'tun';
|
|
63
|
+
readonly ip?: IPv4Address;
|
|
64
|
+
readonly netmask?: IPv4Address;
|
|
52
65
|
readable: ReadableStream<Uint8Array>;
|
|
53
66
|
writable: WritableStream<Uint8Array>;
|
|
54
|
-
constructor();
|
|
55
67
|
listen(): AsyncIterableIterator<Uint8Array>;
|
|
56
68
|
[Symbol.asyncIterator](): AsyncIterableIterator<Uint8Array>;
|
|
57
|
-
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
type NetworkInterface = LoopbackInterface | TunInterface | TapInterface | BridgeInterface;
|
|
58
72
|
|
|
59
73
|
type UdpDatagram = {
|
|
60
|
-
host:
|
|
74
|
+
host: string;
|
|
61
75
|
port: number;
|
|
62
76
|
data: Uint8Array;
|
|
63
77
|
};
|
|
@@ -67,7 +81,7 @@ type UdpSocketOptions = {
|
|
|
67
81
|
*
|
|
68
82
|
* If not provided, the socket will bind to all available interfaces.
|
|
69
83
|
*/
|
|
70
|
-
host?:
|
|
84
|
+
host?: string;
|
|
71
85
|
/**
|
|
72
86
|
* The local port to bind to.
|
|
73
87
|
*
|
|
@@ -75,34 +89,31 @@ type UdpSocketOptions = {
|
|
|
75
89
|
*/
|
|
76
90
|
port?: number;
|
|
77
91
|
};
|
|
78
|
-
|
|
79
|
-
#private;
|
|
92
|
+
type UdpSocket = {
|
|
80
93
|
readable: ReadableStream<UdpDatagram>;
|
|
81
94
|
writable: WritableStream<UdpDatagram>;
|
|
82
|
-
constructor();
|
|
83
95
|
close(): Promise<void>;
|
|
84
96
|
[Symbol.asyncIterator](): AsyncIterator<UdpDatagram>;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
type NetworkInterface = LoopbackInterface | TunInterface | TapInterface;
|
|
88
|
-
|
|
89
|
-
type BridgeInterfaceOptions = {
|
|
90
|
-
ports: TapInterface[];
|
|
91
|
-
mac?: MacAddress;
|
|
92
|
-
ip?: IPv4Cidr;
|
|
93
97
|
};
|
|
94
|
-
declare class BridgeInterface {
|
|
95
|
-
}
|
|
96
98
|
|
|
97
99
|
declare function createStack(options?: NetworkStackOptions): Promise<NetworkStack>;
|
|
98
100
|
type NetworkStackOptions = {
|
|
101
|
+
/**
|
|
102
|
+
* Whether to initialize a loopback interface on startup.
|
|
103
|
+
*
|
|
104
|
+
* @default true
|
|
105
|
+
*/
|
|
99
106
|
initializeLoopback?: boolean;
|
|
107
|
+
/**
|
|
108
|
+
* Name server used for DNS resolution.
|
|
109
|
+
*
|
|
110
|
+
* @default { ip: '127.0.0.1', port: 53 }
|
|
111
|
+
*/
|
|
112
|
+
nameServer?: NameServer;
|
|
100
113
|
};
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
get interfaces(): Iterable<NetworkInterface>;
|
|
105
|
-
constructor(options?: NetworkStackOptions);
|
|
114
|
+
type NetworkStack = {
|
|
115
|
+
readonly ready: Promise<void>;
|
|
116
|
+
readonly interfaces: Iterable<NetworkInterface>;
|
|
106
117
|
createLoopbackInterface(options: LoopbackInterfaceOptions): Promise<LoopbackInterface>;
|
|
107
118
|
createTunInterface(options: TunInterfaceOptions): Promise<TunInterface>;
|
|
108
119
|
createTapInterface(options?: TapInterfaceOptions): Promise<TapInterface>;
|
|
@@ -123,6 +134,6 @@ declare class NetworkStack {
|
|
|
123
134
|
* If no local port is provided, the socket will bind to a random port.
|
|
124
135
|
*/
|
|
125
136
|
openUdp(options?: UdpSocketOptions): Promise<UdpSocket>;
|
|
126
|
-
}
|
|
137
|
+
};
|
|
127
138
|
|
|
128
|
-
export { BridgeInterface, type BridgeInterfaceOptions, LoopbackInterface, type LoopbackInterfaceOptions, type NetworkInterface, NetworkStack, TapInterface, type TapInterfaceOptions, TcpConnection, type TcpConnectionOptions, TcpListener, type TcpListenerOptions, TunInterface, type TunInterfaceOptions, type UdpDatagram, UdpSocket, type UdpSocketOptions, createStack };
|
|
139
|
+
export { type BridgeInterface, type BridgeInterfaceOptions, type LoopbackInterface, type LoopbackInterfaceOptions, type NetworkInterface, type NetworkStack, type TapInterface, type TapInterfaceOptions, type TcpConnection, type TcpConnectionOptions, type TcpListener, type TcpListenerOptions, type TunInterface, type TunInterfaceOptions, type UdpDatagram, type UdpSocket, type UdpSocketOptions, createStack };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,63 +1,77 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
type MacAddress = `${string}:${string}:${string}:${string}:${string}:${string}`;
|
|
5
|
-
|
|
6
|
-
type LoopbackInterfaceOptions = {
|
|
7
|
-
ip?: IPv4Cidr;
|
|
8
|
-
};
|
|
9
|
-
declare class LoopbackInterface {
|
|
10
|
-
}
|
|
1
|
+
import { MacAddress, IPv4Cidr, IPv4Address } from '@tcpip/wire';
|
|
2
|
+
import { NameServer } from '@tcpip/dns';
|
|
11
3
|
|
|
12
4
|
type TapInterfaceOptions = {
|
|
13
5
|
mac?: MacAddress;
|
|
14
6
|
ip?: IPv4Cidr;
|
|
15
7
|
};
|
|
16
|
-
|
|
17
|
-
|
|
8
|
+
type TapInterface = {
|
|
9
|
+
readonly type: 'tap';
|
|
10
|
+
readonly mac: MacAddress;
|
|
11
|
+
readonly ip?: IPv4Address;
|
|
12
|
+
readonly netmask?: IPv4Address;
|
|
18
13
|
readable: ReadableStream<Uint8Array>;
|
|
19
14
|
writable: WritableStream<Uint8Array>;
|
|
20
|
-
constructor();
|
|
21
15
|
listen(): AsyncIterableIterator<Uint8Array>;
|
|
22
16
|
[Symbol.asyncIterator](): AsyncIterableIterator<Uint8Array>;
|
|
23
|
-
}
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
type BridgeInterfaceOptions = {
|
|
20
|
+
ports: TapInterface[];
|
|
21
|
+
mac?: MacAddress;
|
|
22
|
+
ip?: IPv4Cidr;
|
|
23
|
+
};
|
|
24
|
+
type BridgeInterface = {
|
|
25
|
+
readonly type: 'bridge';
|
|
26
|
+
readonly mac: MacAddress;
|
|
27
|
+
readonly ip?: IPv4Address;
|
|
28
|
+
readonly netmask?: IPv4Address;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
type LoopbackInterfaceOptions = {
|
|
32
|
+
ip?: IPv4Cidr;
|
|
33
|
+
};
|
|
34
|
+
type LoopbackInterface = {
|
|
35
|
+
readonly type: 'loopback';
|
|
36
|
+
readonly ip?: IPv4Address;
|
|
37
|
+
readonly netmask?: IPv4Address;
|
|
38
|
+
};
|
|
24
39
|
|
|
25
40
|
type TcpListenerOptions = {
|
|
26
|
-
host?:
|
|
41
|
+
host?: string;
|
|
27
42
|
port: number;
|
|
28
43
|
};
|
|
29
|
-
|
|
30
|
-
#private;
|
|
31
|
-
constructor();
|
|
44
|
+
type TcpListener = {
|
|
32
45
|
[Symbol.asyncIterator](): AsyncIterableIterator<TcpConnection>;
|
|
33
|
-
}
|
|
46
|
+
};
|
|
34
47
|
type TcpConnectionOptions = {
|
|
35
|
-
host:
|
|
48
|
+
host: string;
|
|
36
49
|
port: number;
|
|
37
50
|
};
|
|
38
|
-
|
|
39
|
-
#private;
|
|
51
|
+
type TcpConnection = {
|
|
40
52
|
readable: ReadableStream<Uint8Array>;
|
|
41
53
|
writable: WritableStream<Uint8Array>;
|
|
42
|
-
constructor();
|
|
43
54
|
close(): Promise<void>;
|
|
44
55
|
[Symbol.asyncIterator](): AsyncIterator<Uint8Array>;
|
|
45
|
-
}
|
|
56
|
+
};
|
|
46
57
|
|
|
47
58
|
type TunInterfaceOptions = {
|
|
48
59
|
ip?: IPv4Cidr;
|
|
49
60
|
};
|
|
50
|
-
|
|
51
|
-
|
|
61
|
+
type TunInterface = {
|
|
62
|
+
readonly type: 'tun';
|
|
63
|
+
readonly ip?: IPv4Address;
|
|
64
|
+
readonly netmask?: IPv4Address;
|
|
52
65
|
readable: ReadableStream<Uint8Array>;
|
|
53
66
|
writable: WritableStream<Uint8Array>;
|
|
54
|
-
constructor();
|
|
55
67
|
listen(): AsyncIterableIterator<Uint8Array>;
|
|
56
68
|
[Symbol.asyncIterator](): AsyncIterableIterator<Uint8Array>;
|
|
57
|
-
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
type NetworkInterface = LoopbackInterface | TunInterface | TapInterface | BridgeInterface;
|
|
58
72
|
|
|
59
73
|
type UdpDatagram = {
|
|
60
|
-
host:
|
|
74
|
+
host: string;
|
|
61
75
|
port: number;
|
|
62
76
|
data: Uint8Array;
|
|
63
77
|
};
|
|
@@ -67,7 +81,7 @@ type UdpSocketOptions = {
|
|
|
67
81
|
*
|
|
68
82
|
* If not provided, the socket will bind to all available interfaces.
|
|
69
83
|
*/
|
|
70
|
-
host?:
|
|
84
|
+
host?: string;
|
|
71
85
|
/**
|
|
72
86
|
* The local port to bind to.
|
|
73
87
|
*
|
|
@@ -75,34 +89,31 @@ type UdpSocketOptions = {
|
|
|
75
89
|
*/
|
|
76
90
|
port?: number;
|
|
77
91
|
};
|
|
78
|
-
|
|
79
|
-
#private;
|
|
92
|
+
type UdpSocket = {
|
|
80
93
|
readable: ReadableStream<UdpDatagram>;
|
|
81
94
|
writable: WritableStream<UdpDatagram>;
|
|
82
|
-
constructor();
|
|
83
95
|
close(): Promise<void>;
|
|
84
96
|
[Symbol.asyncIterator](): AsyncIterator<UdpDatagram>;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
type NetworkInterface = LoopbackInterface | TunInterface | TapInterface;
|
|
88
|
-
|
|
89
|
-
type BridgeInterfaceOptions = {
|
|
90
|
-
ports: TapInterface[];
|
|
91
|
-
mac?: MacAddress;
|
|
92
|
-
ip?: IPv4Cidr;
|
|
93
97
|
};
|
|
94
|
-
declare class BridgeInterface {
|
|
95
|
-
}
|
|
96
98
|
|
|
97
99
|
declare function createStack(options?: NetworkStackOptions): Promise<NetworkStack>;
|
|
98
100
|
type NetworkStackOptions = {
|
|
101
|
+
/**
|
|
102
|
+
* Whether to initialize a loopback interface on startup.
|
|
103
|
+
*
|
|
104
|
+
* @default true
|
|
105
|
+
*/
|
|
99
106
|
initializeLoopback?: boolean;
|
|
107
|
+
/**
|
|
108
|
+
* Name server used for DNS resolution.
|
|
109
|
+
*
|
|
110
|
+
* @default { ip: '127.0.0.1', port: 53 }
|
|
111
|
+
*/
|
|
112
|
+
nameServer?: NameServer;
|
|
100
113
|
};
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
get interfaces(): Iterable<NetworkInterface>;
|
|
105
|
-
constructor(options?: NetworkStackOptions);
|
|
114
|
+
type NetworkStack = {
|
|
115
|
+
readonly ready: Promise<void>;
|
|
116
|
+
readonly interfaces: Iterable<NetworkInterface>;
|
|
106
117
|
createLoopbackInterface(options: LoopbackInterfaceOptions): Promise<LoopbackInterface>;
|
|
107
118
|
createTunInterface(options: TunInterfaceOptions): Promise<TunInterface>;
|
|
108
119
|
createTapInterface(options?: TapInterfaceOptions): Promise<TapInterface>;
|
|
@@ -123,6 +134,6 @@ declare class NetworkStack {
|
|
|
123
134
|
* If no local port is provided, the socket will bind to a random port.
|
|
124
135
|
*/
|
|
125
136
|
openUdp(options?: UdpSocketOptions): Promise<UdpSocket>;
|
|
126
|
-
}
|
|
137
|
+
};
|
|
127
138
|
|
|
128
|
-
export { BridgeInterface, type BridgeInterfaceOptions, LoopbackInterface, type LoopbackInterfaceOptions, type NetworkInterface, NetworkStack, TapInterface, type TapInterfaceOptions, TcpConnection, type TcpConnectionOptions, TcpListener, type TcpListenerOptions, TunInterface, type TunInterfaceOptions, type UdpDatagram, UdpSocket, type UdpSocketOptions, createStack };
|
|
139
|
+
export { type BridgeInterface, type BridgeInterfaceOptions, type LoopbackInterface, type LoopbackInterfaceOptions, type NetworkInterface, type NetworkStack, type TapInterface, type TapInterfaceOptions, type TcpConnection, type TcpConnectionOptions, type TcpListener, type TcpListenerOptions, type TunInterface, type TunInterfaceOptions, type UdpDatagram, type UdpSocket, type UdpSocketOptions, createStack };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var Y=(s,e)=>(e=Symbol[s])?e:Symbol.for("Symbol."+s),J=s=>{throw TypeError(s)};var p=(s,e,t)=>{if(e!=null){typeof e!="object"&&typeof e!="function"&&J("Object expected");var r,n;t&&(r=e[Y("asyncDispose")]),r===void 0&&(r=e[Y("dispose")],t&&(n=r)),typeof r!="function"&&J("Object not disposable"),n&&(r=function(){try{n.call(this)}catch(o){return Promise.reject(o)}}),s.push([t,r,e])}else t&&s.push([t]);return e},m=(s,e,t)=>{var r=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=t?new r(a,e,"An error was suppressed during disposal"):(t=!0,a),o=a=>{for(;a=s.pop();)try{var i=a[1]&&a[1].call(a[2]);if(a[0])return Promise.resolve(i).then(o,c=>(n(c),o()))}catch(c){n(c)}if(t)throw e};return o()};var l="dispose"in Symbol?Symbol.dispose:Symbol.for("Symbol.dispose");import{ConsoleStdout as re,File as ce,OpenFile as pe,WASI as de}from"@bjorn3/browser_wasi_shim";function ee(s){return s.join(".")}function P(s){return new Uint8Array(s.split(".").map(e=>parseInt(e,10)))}function w(s){let[e,t]=s.split("/");if(!e||!t)throw new Error("invalid cidr");let r=parseInt(t,10),n=ne(r);return{ipAddress:P(e),netmask:n}}function ne(s){let e=new Uint8Array(4);for(let t=0;t<s;t++){let r=Math.floor(t/8),n=7-t%8,o=e[r];if(o===void 0)throw new Error("invalid mask size");e[r]=o|1<<n}return e}function O(s){let e=s.split(":");if(e.length!==6)throw new Error("invalid mac address");return new Uint8Array(e.map(t=>{let r=parseInt(t,16);if(Number.isNaN(r))throw new Error("invalid mac address");return r}))}var h=class{#e=new WeakMap;#t=new WeakMap;setOuter(e,t){this.#e.set(e,t)}setInner(e,t){this.#t.set(e,t)}getOuter(e){let t=this.#e.get(e);if(!t)throw new Error(`outer hooks not set for ${e}`);return t}getInner(e){let t=this.#t.get(e);if(!t)throw new Error(`inner hooks not set for ${e}`);return t}},M=class extends Number{free;constructor(e,t){super(e),this.free=t}[l](){this.free(this.valueOf())}},x=class extends Map{#e=new Map;wait(e){return new Promise(t=>{let r=this.#e.get(e)??new Set;r.add(t),this.#e.set(e,r)})}set(e,t){super.set(e,t);let r=this.#e.get(e);if(r)for(let n of r)n(t),r.delete(n);return this}};function g(s,e){let t=s.getReader();return oe(t,e)}async function*oe(s,e){try{for(;;){let{done:t,value:r}=await s.read();if(t)return r;yield r}}finally{e?.preventCancel||await s.cancel(),s.releaseLock()}}var _=class extends ReadableStream{#e;constructor({lock:e,...t},r){super(t,r),this.#e=e}getReader(){let e=super.getReader();return this.locked&&this.#e?.(),e}pipeThrough(e,t){let r=super.pipeThrough(e,t);return this.locked&&this.#e?.(),r}pipeTo(e,t){let r=super.pipeTo(e,t);return this.locked&&this.#e?.(),r}tee(){let[e,t]=super.tee();return this.locked&&this.#e?.(),[e,t]}};async function I(){return await new Promise(s=>queueMicrotask(s))}function L(){let s=new Uint8Array(6);return crypto.getRandomValues(s),s[0]=s[0]&252|2,s}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 M(this.exports.malloc(e),this.exports.free)}copyToMemory(e){let t=new Uint8Array(e),r=t.length,n=this.smartMalloc(r);return new Uint8Array(this.exports.memory.buffer,n.valueOf(),r).set(t),n}copyFromMemory(e,t){let r=this.exports.memory.buffer.slice(e,e+t);return new Uint8Array(r)}};var T={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 A=new h,C=class extends u{interfaces=new Map;imports={register_tap_interface:e=>{let t=new U;A.setOuter(t,{handle:e,sendFrame:r=>{let n=this.copyToMemory(r),o=this.exports.send_tap_interface(e,n,r.length);if(o!==T.ERR_OK)throw new Error(`failed to send frame: ${o}`)}}),this.interfaces.set(e,t)},receive_frame:async(e,t,r)=>{let n=this.copyFromMemory(t,r);await I();let o=this.interfaces.get(e);if(!o){console.error("received frame on unknown tap interface");return}A.getInner(o).receiveFrame(new Uint8Array(n))}};async create(e){var f=[];try{let t=e.mac?O(e.mac):L();let{ipAddress:r,netmask:n}=e.ip?w(e.ip):{};let o=p(f,this.copyToMemory(t));let a=p(f,r?this.copyToMemory(r):void 0);let i=p(f,n?this.copyToMemory(n):void 0);let c=this.exports.create_tap_interface(o,a??0,i??0);let d=this.interfaces.get(c);if(!d)throw new Error("tap interface failed to register");return d}catch(y){var b=y,v=!0}finally{m(f,b,v)}}async remove(e){for(let[t,r]of this.interfaces.entries())if(r===e){this.exports.remove_tap_interface(t),this.interfaces.delete(t);return}}},U=class{#e;#t=!1;readable;writable;constructor(){A.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 _({start:e=>{this.#e=e},lock:()=>{this.#t=!0}}),this.writable=new WritableStream({write:e=>{try{A.getOuter(this).sendFrame(e)}catch(t){console.error("tap interface send failed",t)}}})}listen(){if(this.readable.locked)throw new Error("readable stream already locked");return g(this.readable)}[Symbol.asyncIterator](){return this.listen()}};var B=class extends u{interfaces=new Map;imports={};async create(e){var b=[];try{let t=e.mac?O(e.mac):L();let{ipAddress:r,netmask:n}=e.ip?w(e.ip):{};let o=p(b,this.copyToMemory(t));let a=p(b,r?this.copyToMemory(r):void 0);let i=p(b,n?this.copyToMemory(n):void 0);let c=new Uint32Array(e.ports.map(Q=>Number(A.getOuter(Q).handle)));let d=p(b,this.copyToMemory(c.buffer));let f=this.exports.create_bridge_interface(o,a??0,i??0,d,e.ports.length);let y=new D;this.interfaces.set(f,y);return y}catch(v){var q=v,G=!0}finally{m(b,q,G)}}async remove(e){for(let[t,r]of this.interfaces.entries())if(r===e){this.exports.remove_bridge_interface(t),this.interfaces.delete(t);return}}},D=class{};var F=class extends u{interfaces=new Map;imports={register_loopback_interface:e=>{let t=new E;this.interfaces.set(e,t)}};async create(e){var c=[];try{let{ipAddress:t,netmask:r}=e.ip?w(e.ip):{};let n=p(c,t?this.copyToMemory(t):void 0);let o=p(c,r?this.copyToMemory(r):void 0);let a=this.exports.create_loopback_interface(n??0,o??0);let i=this.interfaces.get(a);if(!i)throw new Error("loopback interface failed to register");return i}catch(d){var f=d,y=!0}finally{m(c,f,y)}}async remove(e){for(let[t,r]of this.interfaces.entries())if(r===e){this.exports.remove_loopback_interface(t),this.interfaces.delete(t);return}}},E=class{};var Z=new h,k=new h,X=1460,Ze=X*4,Xe=X*4,se=X,W=class extends u{#e=new Map;#t=new x;#r=new Map;imports={accept_tcp_connection:async(e,t)=>{let r=this.#e.get(e);if(!r){console.error("new tcp connection to unknown listener");return}await I();let n=new S;k.setOuter(n,{send:async o=>{let a=Number(this.copyToMemory(o)),i=this.exports.send_tcp_chunk(t,a,o.length);for(;i<o.length;){await new Promise(d=>{this.#r.set(t,d)});let c=o.length-i;i+=this.exports.send_tcp_chunk(t,a+i,c)}},updateReceiveBuffer:o=>{this.exports.update_tcp_receive_buffer(t,o)},close:async()=>{let o=this.exports.close_tcp_connection(t);if(o!==T.ERR_OK)throw new Error(`failed to close tcp connection: ${o}`)}}),this.#t.set(t,n),Z.getInner(r).accept(n)},connected_tcp_connection:async e=>{await I();let t=new S;k.setOuter(t,{send:async r=>{let n=Number(this.copyToMemory(r)),o=this.exports.send_tcp_chunk(e,n,r.length);for(;o<r.length;){await new Promise(i=>{this.#r.set(e,i)});let a=r.length-o;o+=this.exports.send_tcp_chunk(e,n+o,a)}},updateReceiveBuffer:r=>{this.exports.update_tcp_receive_buffer(e,r)},close:async()=>{this.exports.close_tcp_connection(e)}}),this.#t.set(e,t)},closed_tcp_connection:async e=>{let t=this.#t.get(e);if(!t){console.error("received close on unknown tcp connection");return}await k.getInner(t).close()},receive_tcp_chunk:async(e,t,r)=>{let n=this.copyFromMemory(t,r),o=this.#t.get(e);if(!o){console.error("received chunk on unknown tcp connection");return}await I(),k.getInner(o).receive(new Uint8Array(n))},sent_tcp_chunk:(e,t)=>{let r=this.#r.get(e);this.#r.delete(e),r?.(t)}};async listen(e){var o=[];try{let t=p(o,e.host?this.copyToMemory(P(e.host)):null);let r=this.exports.create_tcp_listener(t,e.port);let n=new N;Z.setOuter(n,{});this.#e.set(r,n);return n}catch(a){var i=a,c=!0}finally{m(o,i,c)}}async connect(e){var o=[];try{let t=p(o,this.copyToMemory(P(e.host)));let r=this.exports.create_tcp_connection(t,e.port);let n=await this.#t.wait(r);if(!n)throw new Error("tcp failed to connect");return n}catch(a){var i=a,c=!0}finally{m(o,i,c)}}},N=class{#e=[];#t;constructor(){Z.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=[]}},S=class{#e=[];#t;#r;readable;writable;constructor(){k.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:se,size:e=>e.byteLength}),this.writable=new WritableStream({start:e=>{this.#r=e},write:async e=>{await k.getOuter(this).send(e)}},{highWaterMark:0})}#n(){if(!this.#t?.desiredSize)return;let e=0;for(;this.#e.length>0;){let t=this.#e[0].length;if(e+t>this.#t.desiredSize)break;let r=this.#e.shift();this.#t.enqueue(r),e+=r.length}e>0&&k.getOuter(this).updateReceiveBuffer(e)}async close(){await k.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 g(this.readable)}};var z=new h,V=class extends u{interfaces=new Map;imports={register_tun_interface:e=>{let t=new R;z.setOuter(t,{sendPacket:r=>{let n=this.copyToMemory(r);this.exports.send_tun_interface(e,n,r.length)}}),this.interfaces.set(e,t)},receive_packet:async(e,t,r)=>{let n=this.copyFromMemory(t,r);await I();let o=this.interfaces.get(e);if(!o){console.error("received packet on unknown tun interface");return}z.getInner(o).receivePacket(new Uint8Array(n))}};async create(e){var c=[];try{let{ipAddress:t,netmask:r}=e.ip?w(e.ip):{};let n=p(c,t?this.copyToMemory(t):void 0);let o=p(c,r?this.copyToMemory(r):void 0);let a=this.exports.create_tun_interface(n??0,o??0);let i=this.interfaces.get(a);if(!i)throw new Error("tun interface failed to register");return i}catch(d){var f=d,y=!0}finally{m(c,f,y)}}async remove(e){for(let[t,r]of this.interfaces.entries())if(r===e){this.exports.remove_tun_interface(t),this.interfaces.delete(t);return}}},R=class{#e;#t=!1;readable;writable;constructor(){z.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 _({start:e=>{this.#e=e},lock:()=>{this.#t=!0}}),this.writable=new WritableStream({write:e=>{z.getOuter(this).sendPacket(e)}})}listen(){if(this.readable.locked)throw new Error("readable stream already locked");return g(this.readable)}[Symbol.asyncIterator](){return this.listen()}};var H=new h,$=class extends u{#e=new x;imports={receive_udp_datagram:async(e,t,r,n,o)=>{let a=this.copyFromMemory(t,4),i=this.copyFromMemory(n,o),c=this.#e.get(e);if(!c){console.error("received datagram on unknown udp socket");return}await I(),H.getInner(c).receive({host:ee(a),port:r,data:i})}};async open(e){var o=[];try{let t=p(o,e.host?this.copyToMemory(P(e.host)):null);let r=this.exports.open_udp_socket(t,e.port??0);let n=new K;H.setOuter(n,{send:async d=>{var v=[];try{let f=p(v,this.copyToMemory(P(d.host)));let y=p(v,this.copyToMemory(d.data));let b=this.exports.send_udp_datagram(r,f,d.port,y,d.data.length);if(b!==T.ERR_OK)throw new Error(`failed to send udp datagram: ${b}`)}catch(q){var G=q,Q=!0}finally{m(v,G,Q)}},close:async()=>{this.exports.close_udp_socket(r),this.#e.delete(r)}});this.#e.set(r,n);return n}catch(a){var i=a,c=!0}finally{m(o,i,c)}}},K=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 g(this.readable)}};var ae=typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string";async function te(s,e){return ae?ie(s,e):fetch(s)}async function ie(s,e){let t=await import("node:fs"),{Readable:r}=await import("node:stream"),n=t.createReadStream(s),o=r.toWeb(n);return new Response(o,{headers:{"Content-Type":e}})}async function le(s){let e=new j(s);return await e.ready,e}var j=class{#e;#t;#r=new F;#n=new V;#o=new C;#a=new B;#s=new W;#i=new $;ready;get interfaces(){return this.#p()}constructor(e={}){this.#e={...e,initializeLoopback:e.initializeLoopback??!0},this.ready=this.#c(),this.ready.then(async()=>{this.#e.initializeLoopback&&await this.createLoopbackInterface({ip:"127.0.0.1/8"})})}async#c(){let e=new de([],[],[new pe(new ce([])),re.lineBuffered(a=>console.log(`[WASI stdout] ${a}`)),re.lineBuffered(a=>console.warn(`[WASI stderr] ${a}`))]),t=te(new URL("../tcpip.wasm",import.meta.url),"application/wasm"),{instance:r}=await WebAssembly.instantiateStreaming(t,{wasi_snapshot_preview1:e.wasiImport,env:{...this.#r.imports,...this.#n.imports,...this.#o.imports,...this.#a.imports,...this.#s.imports,...this.#i.imports}}),n=r;this.#r.register(n.exports),this.#n.register(n.exports),this.#o.register(n.exports),this.#a.register(n.exports),this.#s.register(n.exports),this.#i.register(n.exports);let o=e.start(n);if(o!==0)throw new Error(`wasi start failed with code ${o}`);this.#t=Number(setInterval(()=>{n.exports.process_queued_packets(),n.exports.process_timeouts()},100))}*#p(){yield*this.#r.interfaces.values(),yield*this.#n.interfaces.values(),yield*this.#o.interfaces.values()}async createLoopbackInterface(e){return await this.ready,this.#r.create(e)}async createTunInterface(e){return await this.ready,this.#n.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){if(await this.ready,e instanceof E)return this.#r.remove(e);if(e instanceof R)return this.#n.remove(e);if(e instanceof U)return this.#o.remove(e);throw new Error("unknown interface type")}async listenTcp(e){return await this.ready,this.#s.listen(e)}async connectTcp(e){return await this.ready,this.#s.connect(e)}async openUdp(e={}){return await this.ready,this.#i.open(e)}};export{D as BridgeInterface,E as LoopbackInterface,j as NetworkStack,U as TapInterface,S as TcpConnection,N as TcpListener,R as TunInterface,K as UdpSocket,le 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 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};
|
|
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/protocols/ipv4.ts","../src/protocols/ethernet.ts","../src/util.ts","../src/bindings/base.ts","../src/lwip/errors.ts","../src/bindings/tap-interface.ts","../src/bindings/bridge-interface.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 {\n BridgeBindings,\n type BridgeInterfaceOptions,\n} from './bindings/bridge-interface.js';\nimport {\n LoopbackBindings,\n LoopbackInterface,\n type LoopbackInterfaceOptions,\n} from './bindings/loopback-interface.js';\nimport {\n TapBindings,\n TapInterface,\n type TapInterfaceOptions,\n} from './bindings/tap-interface.js';\nimport {\n TcpBindings,\n type TcpConnectionOptions,\n type TcpListenerOptions,\n} from './bindings/tcp.js';\nimport {\n TunBindings,\n TunInterface,\n type TunInterfaceOptions,\n} from './bindings/tun-interface.js';\nimport { UdpBindings, type UdpSocketOptions } from './bindings/udp.js';\nimport { fetchFile } from './fetch-file.js';\nimport type { NetworkInterface, WasmInstance } from './types.js';\n\nexport async function createStack(options?: NetworkStackOptions) {\n const stack = new NetworkStack(options);\n await stack.ready;\n return stack;\n}\n\nexport type NetworkStackOptions = {\n initializeLoopback?: boolean;\n};\n\nexport class NetworkStack {\n #options: NetworkStackOptions;\n #loopIntervalId?: number;\n\n #loopbackBindings = new LoopbackBindings();\n #tunBindings = new TunBindings();\n #tapBindings = new TapBindings();\n #bridgeBindings = new BridgeBindings();\n #tcpBindings = new TcpBindings();\n #udpBindings = new 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 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 }\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(\n netInterface: LoopbackInterface | TunInterface | TapInterface\n ) {\n await this.ready;\n\n if (netInterface instanceof LoopbackInterface) {\n return this.#loopbackBindings.remove(netInterface);\n }\n\n if (netInterface instanceof TunInterface) {\n return this.#tunBindings.remove(netInterface);\n }\n\n if (netInterface instanceof TapInterface) {\n return this.#tapBindings.remove(netInterface);\n }\n\n throw new Error('unknown interface type');\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 createIcmpMessage,\n parseIcmpMessage,\n type IcmpMessage,\n} from './icmp.js';\nimport {\n createUdpDatagram,\n parseUdpDatagram,\n UDP_HEADER_LENGTH,\n type UdpDatagram,\n} from './udp.js';\nimport { calculateChecksum } from './util.js';\n\nexport type IPv4Address = `${number}.${number}.${number}.${number}`;\nexport type IPv4Cidr = `${IPv4Address}/${number}`;\n\nexport type IPv4PacketBase = {\n version: number;\n dscp: number;\n ecn: number;\n identification: number;\n flags: number;\n fragmentOffset: number;\n ttl: number;\n protocol: string;\n sourceIP: IPv4Address;\n destinationIP: IPv4Address;\n};\n\nexport type IcmpIPv4Packet = IPv4PacketBase & {\n protocol: 'icmp';\n payload: IcmpMessage;\n};\n\nexport type TcpIPv4Packet = IPv4PacketBase & {\n protocol: 'tcp';\n payload: Uint8Array;\n};\n\nexport type UdpIPv4Packet = IPv4PacketBase & {\n protocol: 'udp';\n payload: UdpDatagram;\n};\n\nexport type IPv4Packet = IcmpIPv4Packet | TcpIPv4Packet | UdpIPv4Packet;\n\nexport type IPv4Protocol = IPv4Packet['protocol'];\n\nexport type IPv4PseudoHeader = {\n sourceIP: IPv4Address;\n destinationIP: IPv4Address;\n protocol: IPv4Protocol;\n length: number;\n};\n\nexport const IPV4_HEADER_LENGTH = 20;\n\n/**\n * Parses an IPv4 packet into an object.\n */\nexport function parseIPv4Packet(data: Uint8Array): IPv4Packet {\n const dataView = new DataView(data.buffer, data.byteOffset, data.byteLength);\n\n const headerChecksum = dataView.getUint16(10);\n const header = data.subarray(0, IPV4_HEADER_LENGTH);\n\n if (calculateChecksum(header, 10) !== headerChecksum) {\n throw new Error('invalid ipv4 checksum');\n }\n\n const totalLength = dataView.getUint16(2);\n\n if (totalLength !== data.length) {\n throw new Error('invalid ipv4 total length');\n }\n\n const versionAndHeaderLength = dataView.getUint8(0);\n const version = versionAndHeaderLength >> 4;\n const headerLength = (versionAndHeaderLength & 0xf) * 4;\n const dscp = dataView.getUint8(1) >> 2;\n const ecn = dataView.getUint8(1) & 0x3;\n const identification = dataView.getUint16(4);\n const flags = dataView.getUint8(6) >> 5;\n const fragmentOffset =\n ((dataView.getUint8(6) & 0x1f) << 8) | dataView.getUint8(7);\n const ttl = dataView.getUint8(8);\n const protocol = parseIPv4Protocol(dataView.getUint8(9));\n const sourceIP = parseIPv4Address(data.subarray(12, 16));\n const destinationIP = parseIPv4Address(data.subarray(16, 20));\n const payload = data.subarray(headerLength);\n\n switch (protocol) {\n case 'icmp':\n return {\n version,\n dscp,\n ecn,\n identification,\n flags,\n fragmentOffset,\n ttl,\n protocol,\n sourceIP,\n destinationIP,\n payload: parseIcmpMessage(payload),\n };\n case 'tcp':\n return {\n version,\n dscp,\n ecn,\n identification,\n flags,\n fragmentOffset,\n ttl,\n protocol,\n sourceIP,\n destinationIP,\n payload,\n };\n case 'udp':\n return {\n version,\n dscp,\n ecn,\n identification,\n flags,\n fragmentOffset,\n ttl,\n protocol,\n sourceIP,\n destinationIP,\n payload: parseUdpDatagram(\n payload,\n serializeIPv4PseudoHeader({\n sourceIP,\n destinationIP,\n protocol,\n length: payload.length,\n })\n ),\n };\n default:\n throw new Error('unknown ipv4 protocol');\n }\n}\n\n/**\n * Serializes an IPv4 packet from an `IPv4Packet` object.\n */\nexport function createIPv4Packet(packet: IPv4Packet): Uint8Array {\n let payload: Uint8Array;\n\n switch (packet.protocol) {\n case 'icmp':\n payload = createIcmpMessage(packet.payload);\n break;\n case 'tcp':\n payload = packet.payload;\n break;\n case 'udp':\n payload = createUdpDatagram(packet.payload, {\n sourceIP: packet.sourceIP,\n destinationIP: packet.destinationIP,\n protocol: packet.protocol,\n length: UDP_HEADER_LENGTH + packet.payload.payload.length,\n });\n break;\n default:\n throw new Error('unknown ipv4 protocol');\n }\n\n const data = new Uint8Array(IPV4_HEADER_LENGTH + payload.length);\n const dataView = new DataView(data.buffer, data.byteOffset, data.byteLength);\n\n const totalLength = IPV4_HEADER_LENGTH + payload.length;\n\n dataView.setUint8(0, (packet.version << 4) | (IPV4_HEADER_LENGTH / 4));\n dataView.setUint8(1, (packet.dscp << 2) | packet.ecn);\n dataView.setUint16(2, totalLength);\n dataView.setUint16(4, packet.identification);\n dataView.setUint8(6, (packet.flags << 5) | (packet.fragmentOffset >> 8));\n dataView.setUint8(7, packet.fragmentOffset & 0xff);\n dataView.setUint8(8, packet.ttl);\n dataView.setUint8(9, serializeIPv4Protocol(packet.protocol));\n\n data.set(serializeIPv4Address(packet.sourceIP), 12);\n data.set(serializeIPv4Address(packet.destinationIP), 16);\n\n // Checksum applies to just the header\n const header = data.subarray(0, IPV4_HEADER_LENGTH);\n const checksum = calculateChecksum(header, 10);\n dataView.setUint16(10, checksum);\n\n data.set(payload, 20);\n\n return data;\n}\n\n/**\n * Parses an IPv4 address Uint8Array into a string.\n */\nexport function parseIPv4Address(data: Uint8Array) {\n return data.join('.') as IPv4Address;\n}\n\n/**\n * Serialize an IPv4 address string into a Uint8Array.\n */\nexport function serializeIPv4Address(ip: string) {\n return new Uint8Array(ip.split('.').map((byte) => parseInt(byte, 10)));\n}\n\nexport function parseIPv4Protocol(protocol: number) {\n switch (protocol) {\n case 1:\n return 'icmp';\n case 6:\n return 'tcp';\n case 17:\n return 'udp';\n default:\n throw new Error('unknown ipv4 protocol');\n }\n}\n\nexport function serializeIPv4Protocol(protocol: IPv4Protocol) {\n switch (protocol) {\n case 'icmp':\n return 1;\n case 'tcp':\n return 6;\n case 'udp':\n return 17;\n default:\n throw new Error('unknown ipv4 protocol');\n }\n}\n\n/**\n * Serialize a CIDR notation string into an object with a\n * Uint8Array IP address and netmask.\n */\nexport function serializeIPv4Cidr(cidr: IPv4Cidr) {\n const [ipString, maskSizeString] = cidr.split('/');\n\n if (!ipString || !maskSizeString) {\n throw new Error('invalid cidr');\n }\n\n const maskSize = parseInt(maskSizeString, 10);\n const netmask = generateNetmask(maskSize);\n\n return {\n ipAddress: serializeIPv4Address(ipString),\n netmask,\n };\n}\n\n/**\n * Generates a netmask from a mask size.\n */\nexport function generateNetmask(maskSize: number) {\n const mask = new Uint8Array(4);\n\n for (let i = 0; i < maskSize; i++) {\n const byteIndex = Math.floor(i / 8);\n const bitIndex = 7 - (i % 8);\n const maskByte = mask[byteIndex];\n if (maskByte === undefined) {\n throw new Error('invalid mask size');\n }\n mask[byteIndex] = maskByte | (1 << bitIndex);\n }\n\n return mask;\n}\n\n/**\n * Creates a pseudo header for use in calculating transport layer checksums.\n */\nexport function serializeIPv4PseudoHeader(pseudoHeader: IPv4PseudoHeader) {\n const buffer = new Uint8Array(12);\n const dataView = new DataView(\n buffer.buffer,\n buffer.byteOffset,\n buffer.byteLength\n );\n\n const sourceIPBuffer = serializeIPv4Address(pseudoHeader.sourceIP);\n const destinationIPBuffer = serializeIPv4Address(pseudoHeader.destinationIP);\n const protocolNumber = serializeIPv4Protocol(pseudoHeader.protocol);\n\n buffer.set(sourceIPBuffer, 0);\n buffer.set(destinationIPBuffer, 4);\n dataView.setUint8(8, 0);\n dataView.setUint8(9, protocolNumber);\n dataView.setUint16(10, pseudoHeader.length);\n\n return buffer;\n}\n","import { createArpMessage, parseArpMessage, type ArpMessage } from './arp.js';\nimport { createIPv4Packet, parseIPv4Packet, type IPv4Packet } from './ipv4.js';\n\nexport type MacAddress =\n `${string}:${string}:${string}:${string}:${string}:${string}`;\n\nexport type EthernetFrameBase = {\n destinationMac: MacAddress;\n sourceMac: MacAddress;\n};\n\nexport type IPv4EthernetFrame = EthernetFrameBase & {\n type: 'ipv4';\n payload: IPv4Packet;\n};\n\nexport type ARPEthernetFrame = EthernetFrameBase & {\n type: 'arp';\n payload: ArpMessage;\n};\n\n// TODO: IPv6EthernetFrame\nexport type EthernetFrame = IPv4EthernetFrame | ARPEthernetFrame;\n\n/**\n * Parses an Ethernet frame into an object.\n */\nexport function parseEthernetFrame(frame: Uint8Array): EthernetFrame {\n const destinationMacBytes = frame.subarray(0, 6);\n const sourceMacBytes = frame.subarray(6, 12);\n const typeBytes = frame.subarray(12, 14);\n const payload = frame.subarray(14);\n\n const destinationMac = parseMacAddress(destinationMacBytes);\n const sourceMac = parseMacAddress(sourceMacBytes);\n const type = parseEthernetType(typeBytes);\n\n switch (type) {\n case 'ipv4':\n return {\n destinationMac,\n sourceMac,\n type,\n payload: parseIPv4Packet(payload),\n };\n case 'arp':\n return {\n destinationMac,\n sourceMac,\n type,\n payload: parseArpMessage(payload),\n };\n default:\n throw new Error('unknown ethernet type');\n }\n}\n\n/**\n * Serializes an Ethernet frame from a Frame object.\n */\nexport function createEthernetFrame(frame: EthernetFrame): Uint8Array {\n let payload: Uint8Array;\n\n switch (frame.type) {\n case 'ipv4':\n payload = createIPv4Packet(frame.payload);\n break;\n break;\n case 'arp':\n payload = createArpMessage(frame.payload);\n break;\n default:\n throw new Error('unknown ethernet type');\n }\n\n const data = new Uint8Array(14 + payload.length);\n\n data.set(serializeMacAddress(frame.destinationMac), 0);\n data.set(serializeMacAddress(frame.sourceMac), 6);\n data.set(createEthernetType(frame.type), 12);\n data.set(payload, 14);\n\n return data;\n}\n\n/**\n * Parses a MAC address Uint8Array into a string.\n */\nexport function parseMacAddress(mac: Uint8Array) {\n if (mac.length !== 6) {\n throw new Error('invalid mac address');\n }\n\n return Array.from(mac)\n .map((byte) => byte.toString(16).padStart(2, '0'))\n .join(':') as MacAddress;\n}\n\n/**\n * Serializes a MAC address string into a Uint8Array.\n */\nexport function serializeMacAddress(mac: string) {\n const segments = mac.split(':');\n\n if (segments.length !== 6) {\n throw new Error('invalid mac address');\n }\n\n return new Uint8Array(\n segments.map((byte) => {\n const parsed = parseInt(byte, 16);\n if (Number.isNaN(parsed)) {\n throw new Error('invalid mac address');\n }\n return parsed;\n })\n );\n}\n\n/**\n * Parses an Ethernet type into a string.\n */\nexport function parseEthernetType(etherType: Uint8Array) {\n const dataView = new DataView(\n etherType.buffer,\n etherType.byteOffset,\n etherType.byteLength\n );\n\n const type = dataView.getUint16(0);\n\n switch (type) {\n case 0x0800:\n return 'ipv4';\n case 0x86dd:\n return 'ipv6';\n case 0x0806:\n return 'arp';\n default:\n throw new Error('unknown ethernet type');\n }\n}\n\n/**\n * Serializes an Ethernet type from a string.\n */\nexport function createEthernetType(type: 'ipv4' | 'ipv6' | 'arp') {\n const data = new Uint8Array(2);\n const dataView = new DataView(data.buffer, data.byteOffset, data.byteLength);\n\n switch (type) {\n case 'ipv4':\n dataView.setUint16(0, 0x0800);\n break;\n case 'ipv6':\n dataView.setUint16(0, 0x86dd);\n break;\n case 'arp':\n dataView.setUint16(0, 0x0806);\n break;\n default:\n throw new Error('unknown ethernet type');\n }\n\n return data;\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 { SysExports, WasiExports } from '../types.js';\nimport { UniquePointer } from '../util.js';\n\nexport abstract class Bindings<Imports, Exports> {\n #exports?: Exports & WasiExports & SysExports;\n\n abstract imports: Imports;\n get exports(): Exports & WasiExports & SysExports {\n if (!this.#exports) {\n throw new Error('exports were not registered');\n }\n return this.#exports;\n }\n\n register(exports: Exports & WasiExports & SysExports) {\n this.#exports = exports;\n }\n\n smartMalloc(size: number) {\n return new UniquePointer(this.exports.malloc(size), this.exports.free);\n }\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 copyFromMemory(ptr: number, length: number): Uint8Array {\n const buffer = this.exports.memory.buffer.slice(ptr, ptr + length);\n return new Uint8Array(buffer);\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 { LwipError } from '../lwip/errors.js';\nimport { serializeMacAddress, type MacAddress } from '../protocols/ethernet.js';\nimport { serializeIPv4Cidr, type IPv4Cidr } from '../protocols/ipv4.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};\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 TapInterface();\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 });\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 class TapInterface {\n #readableController?: ReadableStreamController<Uint8Array>;\n #isListening = false;\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","import { serializeMacAddress, type MacAddress } from '../protocols/ethernet.js';\nimport { serializeIPv4Cidr, type IPv4Cidr } from '../protocols/ipv4.js';\nimport type { Pointer } from '../types.js';\nimport { generateMacAddress } from '../util.js';\nimport { Bindings } from './base.js';\nimport { tapInterfaceHooks, type TapInterface } from './tap-interface.js';\n\ntype BridgeInterfaceHandle = Pointer;\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 BridgeInterface();\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 class BridgeInterface {}\n","import { Bindings } from './base.js';\nimport { serializeIPv4Cidr, type IPv4Cidr } from '../protocols/ipv4.js';\nimport type { Pointer } from '../types.js';\n\ntype LoopbackInterfaceHandle = Pointer;\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 LoopbackInterface();\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};\nexport class LoopbackInterface {}\n","import { Bindings } from './base.js';\nimport { LwipError } from '../lwip/errors.js';\nimport { serializeIPv4Address, type IPv4Address } from '../protocols/ipv4.js';\nimport type { Pointer } from '../types.js';\nimport { EventMap, fromReadable, Hooks, nextMicrotask } from '../util.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\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 TcpConnection();\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 TcpConnection();\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(serializeIPv4Address(options.host))\n : null;\n\n const handle = this.exports.create_tcp_listener(hostPtr, options.port);\n\n const tcpListener = new TcpListener();\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(serializeIPv4Address(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?: IPv4Address;\n port: number;\n};\n\nexport class TcpListener implements AsyncIterable<TcpConnection> {\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: IPv4Address;\n port: number;\n};\n\nexport class TcpConnection implements AsyncIterable<Uint8Array> {\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 { Bindings } from './base.js';\nimport { serializeIPv4Cidr, type IPv4Cidr } from '../protocols/ipv4.js';\nimport type { Pointer } from '../types.js';\nimport {\n ExtendedReadableStream,\n fromReadable,\n Hooks,\n nextMicrotask,\n} from '../util.js';\n\ntype TunInterfaceHandle = Pointer;\n\ntype TunInterfaceOuterHooks = {\n sendPacket(packet: Uint8Array): void;\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 TunInterface();\n\n tunInterfaceHooks.setOuter(tunInterface, {\n sendPacket: (packet) => {\n const packetPtr = this.copyToMemory(packet);\n this.exports.send_tun_interface(handle, packetPtr, packet.length);\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 class TunInterface {\n #readableController?: ReadableStreamController<Uint8Array>;\n #isListening = false;\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 { LwipError } from '../lwip/errors.js';\nimport {\n parseIPv4Address,\n serializeIPv4Address,\n type IPv4Address,\n} from '../protocols/ipv4.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: IPv4Address;\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\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(serializeIPv4Address(options.host))\n : null;\n\n const handle = this.exports.open_udp_socket(hostPtr, options.port ?? 0);\n\n const udpSocket = new UdpSocket();\n\n UdpSocketHooks.setOuter(udpSocket, {\n send: async (datagram: UdpDatagram) => {\n using hostPtr = this.copyToMemory(serializeIPv4Address(datagram.host));\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?: IPv4Address;\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 class UdpSocket implements 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","parseIPv4Address","data","serializeIPv4Address","ip","byte","serializeIPv4Cidr","cidr","ipString","maskSizeString","maskSize","netmask","generateNetmask","serializeIPv4Address","mask","i","byteIndex","bitIndex","maskByte","serializeMacAddress","mac","segments","byte","parsed","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","LwipError","tapInterfaceHooks","Hooks","TapBindings","Bindings","handle","tapInterface","TapInterface","frame","framePtr","result","LwipError","length","nextMicrotask","options","_stack","macAddress","serializeMacAddress","generateMacAddress","ipAddress","netmask","serializeIPv4Cidr","macAddressPtr","__using","ipAddressPtr","netmaskPtr","_","_error","_hasError","__callDispose","loopback","#readableController","#isListening","ExtendedReadableStream","controller","packet","err","fromReadable","BridgeBindings","Bindings","options","_stack","macAddress","serializeMacAddress","generateMacAddress","ipAddress","netmask","serializeIPv4Cidr","macAddressPtr","__using","ipAddressPtr","netmaskPtr","portHandles","port","tapInterfaceHooks","portHandlesPtr","handle","bridgeInterface","BridgeInterface","_","_error","_hasError","__callDispose","loopback","LoopbackBindings","Bindings","handle","loopbackInterface","LoopbackInterface","options","_stack","ipAddress","netmask","serializeIPv4Cidr","ipAddressPtr","__using","netmaskPtr","_","_error","_hasError","__callDispose","loopback","tcpListenerHooks","Hooks","tcpConnectionHooks","MAX_SEGMENT_SIZE","MAX_WINDOW_SIZE","SEND_BUFFER_SIZE","READABLE_HIGH_WATER_MARK","TcpBindings","Bindings","#tcpListeners","#tcpConnections","EventMap","#tcpAcks","listenerHandle","connectionHandle","listener","nextMicrotask","connection","TcpConnection","data","dataPtr","bytesQueued","resolve","bytesRemaining","length","result","LwipError","handle","chunkPtr","chunk","notifyAck","options","_stack","hostPtr","__using","serializeIPv4Address","tcpListener","TcpListener","_","_error","_hasError","__callDispose","tcpConnection","#connections","#notifyConnection","#receiveBuffer","#readableController","#writableController","#enqueueBuffer","controller","bytesEnqueued","chunkLength","fromReadable","tunInterfaceHooks","Hooks","TunBindings","Bindings","handle","tunInterface","TunInterface","packet","packetPtr","length","nextMicrotask","options","_stack","ipAddress","netmask","serializeIPv4Cidr","ipAddressPtr","__using","netmaskPtr","_","_error","_hasError","__callDispose","loopback","#readableController","#isListening","ExtendedReadableStream","controller","fromReadable","UdpSocketHooks","Hooks","UdpBindings","Bindings","#UdpSockets","EventMap","handle","hostPtr","port","datagramPtr","length","host","datagram","socket","nextMicrotask","parseIPv4Address","options","_stack","__using","serializeIPv4Address","udpSocket","UdpSocket","result","LwipError","_","_error","_hasError","__callDispose","#readableController","#writableController","controller","fromReadable","IN_NODE","fetchFile","input","type","fetchFileNode","fs","Readable","nodeStream","stream","createStack","options","stack","NetworkStack","#options","#loopIntervalId","#loopbackBindings","LoopbackBindings","#tunBindings","TunBindings","#tapBindings","TapBindings","#bridgeBindings","BridgeBindings","#tcpBindings","TcpBindings","#udpBindings","UdpBindings","#listInterfaces","#init","wasi","WASI","OpenFile","File","ConsoleStdout","msg","source","fetchFile","instance","wasmInstance","result","netInterface","LoopbackInterface","TunInterface","TapInterface"]}
|
|
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"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tcpip",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Virtual TCP/IP stack that can run anywhere",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -31,7 +31,9 @@
|
|
|
31
31
|
}
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@bjorn3/browser_wasi_shim": "^0.3.0"
|
|
34
|
+
"@bjorn3/browser_wasi_shim": "^0.3.0",
|
|
35
|
+
"@tcpip/dns": "0.2",
|
|
36
|
+
"@tcpip/wire": "0.1"
|
|
35
37
|
},
|
|
36
38
|
"devDependencies": {
|
|
37
39
|
"@total-typescript/tsconfig": "^1.0.4",
|
package/tcpip.wasm
CHANGED
|
Binary file
|