onelobby 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,7 +9,7 @@ You need a OneLobby lobby server (the WebSocket URL, like `wss://lobby.example.c
9
9
  ## Use it from a `<script>` tag (no build tools)
10
10
 
11
11
  ```html
12
- <script src="https://cdn.jsdelivr.net/npm/onelobby@0.2.1/dist/onelobby.global.min.js"></script>
12
+ <script src="https://cdn.jsdelivr.net/npm/onelobby@0.2.2/dist/onelobby.global.min.js"></script>
13
13
  <script>
14
14
  const kit = new OneLobby.OneLobby({
15
15
  gameId: "com.you.yourgame", // any unique name for your game
@@ -51,7 +51,7 @@ const lobby = await kit.host({ name: "My game", maxPlayers: 4 });
51
51
  console.log("Tell your friend this code:", formatCode(lobby.code));
52
52
  ```
53
53
 
54
- Without a bundler: `import { OneLobby } from "https://cdn.jsdelivr.net/npm/onelobby@0.2.1/dist/onelobby.bundle.mjs"`.
54
+ Without a bundler: `import { OneLobby } from "https://cdn.jsdelivr.net/npm/onelobby@0.2.2/dist/onelobby.bundle.mjs"`.
55
55
 
56
56
  ## Made for players
57
57
 
@@ -1,5 +1,6 @@
1
1
  /// <reference types="node" />
2
2
  import { createHash } from "node:crypto";
3
+ import { createSocket } from "node:dgram";
3
4
  import { createServer } from "node:net";
4
5
  import { SignalingHub, directHubOptions } from "./hub.js";
5
6
  import { PortMapper } from "./portmap.js";
@@ -7,6 +8,26 @@ const WS_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
7
8
  const MAX_HEADER_BYTES = 8192;
8
9
  const HANDSHAKE_TIMEOUT_MS = 5000;
9
10
  const STATUS_TEXT = { 200: "OK", 301: "Moved Permanently", 302: "Found", 404: "Not Found", 500: "Internal Server Error" };
11
+ /**
12
+ * Rejects (EADDRINUSE) when another program holds IPv4 TCP or UDP `port`. Windows lets the dual-stack listener bind
13
+ * over a program on 0.0.0.0 (every IPv4 joiner would then reach that program), and the peer links need UDP `port`
14
+ * too, so both are tried and released before the real listen.
15
+ */
16
+ async function probeIPv4(port) {
17
+ await new Promise((resolve, reject) => {
18
+ const s = createServer();
19
+ s.once("error", reject);
20
+ s.listen({ port, host: "0.0.0.0", exclusive: true }, () => s.close(() => resolve()));
21
+ });
22
+ await new Promise((resolve, reject) => {
23
+ const u = createSocket({ type: "udp4" });
24
+ u.once("error", (e) => {
25
+ u.close();
26
+ reject(e);
27
+ });
28
+ u.bind({ port, address: "0.0.0.0", exclusive: true }, () => u.close(() => resolve()));
29
+ });
30
+ }
10
31
  export class PortInUseError extends Error {
11
32
  code = "port_in_use";
12
33
  constructor(port, detail) {
@@ -56,6 +77,8 @@ export class DirectHubHost {
56
77
  if (deps?.host)
57
78
  await tryListen(deps.host);
58
79
  else {
80
+ if (port !== 0)
81
+ await probeIPv4(port);
59
82
  try {
60
83
  await tryListen("::", false);
61
84
  }
@@ -1 +1 @@
1
- {"version":3,"file":"hub-host.js","sourceRoot":"","sources":["../../src/direct/hub-host.ts"],"names":[],"mappings":"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","sourcesContent":["/// <reference types=\"node\" />\nimport { createHash } from \"node:crypto\";\nimport { createServer, type Server, type Socket } from \"node:net\";\nimport type { SocketLike } from \"../transport.js\";\nimport { SignalingHub, directHubOptions, type HubDeps, type HubOptions, type Session } from \"./hub.js\";\nimport { PortMapper, type PortMapperOptions, type PortMapResult } from \"./portmap.js\";\n\nconst WS_GUID = \"258EAFA5-E914-47DA-95CA-C5AB0DC85B11\";\nconst MAX_HEADER_BYTES = 8192;\nconst HANDSHAKE_TIMEOUT_MS = 5000;\nconst STATUS_TEXT: Record<number, string> = { 200: \"OK\", 301: \"Moved Permanently\", 302: \"Found\", 404: \"Not Found\", 500: \"Internal Server Error\" };\n\nexport class PortInUseError extends Error {\n readonly code = \"port_in_use\";\n constructor(port: number, detail: string) {\n super(`port_in_use: cannot listen on port ${port} (${detail}): is another copy of the game hosting?`);\n }\n}\n\n/**\n * Runs a {@link SignalingHub} inside a Node process and listens for joiners' signaling WebSockets on TCP `port`: the\n * Node twin of C#'s `DirectHubHost` (docs/direct-ip.md s.2, s.5 step 3). A plain `net` server with a minimal HTTP/1.1\n * upgrade and RFC 6455 framing (text, close, ping/pong, fragments; client frames must be masked), so the package keeps\n * zero runtime dependencies. Any path upgrades; `GET /healthz` answers `ok`. The host's own client talks to the hub in\n * memory through {@link connectLocal}. `hub.tick()` runs once a second on an unref'd timer.\n */\n/** One plain-HTTP answer from the host's port, e.g. a browser join page. */\nexport interface HttpReply {\n /** Default 200. */\n status?: number;\n /** Content-Type, e.g. \"text/html; charset=utf-8\". */\n type: string;\n body: string | Uint8Array;\n}\n\n/** Serves plain `GET` requests on the direct port (path without the query); null answers 404. */\nexport type HttpHandler = (path: string) => HttpReply | null;\n\nexport class DirectHubHost {\n readonly hub: SignalingHub;\n /**\n * Plain `GET`s that are not WebSocket upgrades go here, so the host can serve its own join page: a browser opening\n * `http://<host address>:<port>/` gets a page that joins with `kit.joinDirect(location.host)`. Null (default): 426.\n */\n http: HttpHandler | null = null;\n /** The bound TCP port (the requested one, or the one the OS picked for 0). */\n readonly port: number;\n portMapper: PortMapper | null = null;\n private readonly server: Server;\n private readonly sockets = new Set<Socket>();\n private readonly ticker: ReturnType<typeof setInterval>;\n private readonly maxMessageBytes: number;\n private closed = false;\n\n private constructor(server: Server, port: number, hub: SignalingHub, maxMessageBytes: number) {\n this.server = server;\n this.port = port;\n this.hub = hub;\n this.maxMessageBytes = maxMessageBytes;\n this.ticker = setInterval(() => this.hub.tick(), 1000);\n this.ticker.unref?.();\n server.on(\"connection\", (sock) => this.accept(sock));\n }\n\n /**\n * Binds TCP `port` on every interface (IPv6 dual-stack where available). Hub options default to\n * {@link directHubOptions} (one lobby, no codes, no ICE servers). Rejects with {@link PortInUseError}.\n */\n static async listen(port: number, options?: Partial<HubOptions>, deps?: HubDeps & { host?: string }): Promise<DirectHubHost> {\n const opts = { ...directHubOptions(8), ...options };\n const server = createServer({ noDelay: true });\n const tryListen = (host: string, ipv6Only?: boolean) =>\n new Promise<void>((resolve, reject) => {\n const onError = (e: NodeJS.ErrnoException) => reject(e);\n server.once(\"error\", onError);\n server.listen({ port, host, ipv6Only }, () => {\n server.off(\"error\", onError);\n resolve();\n });\n });\n try {\n if (deps?.host) await tryListen(deps.host);\n else {\n try {\n await tryListen(\"::\", false);\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === \"EADDRINUSE\") throw e;\n await tryListen(\"0.0.0.0\"); // no IPv6 on this machine\n }\n }\n } catch (e) {\n throw new PortInUseError(port, (e as NodeJS.ErrnoException).code ?? String(e));\n }\n const addr = server.address();\n const bound = typeof addr === \"object\" && addr ? addr.port : port;\n return new DirectHubHost(server, bound, new SignalingHub(opts, deps), opts.maxMessageBytes);\n }\n\n /** Joiner sockets currently open. */\n get connectionCount(): number {\n return this.sockets.size;\n }\n\n /**\n * The host's own signaling socket: a WebSocket look-alike wired straight into the hub (no network). Pass it to the\n * kit as `advanced.socketFactory: () => host.connectLocal()`.\n */\n connectLocal(): SocketLike {\n return new LocalHubSocket(this.hub);\n }\n\n /** Asks the home router to forward TCP and UDP `port` here (UPnP, then NAT-PMP; docs/direct-ip.md s.2a). */\n openPort(options?: PortMapperOptions): Promise<PortMapResult> {\n if (this.portMapper) throw new Error(\"the port is already being opened\");\n this.portMapper = new PortMapper(this.port, options);\n return this.portMapper.open();\n }\n\n /** Stops listening, closes every joiner socket and removes the router mapping. */\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n clearInterval(this.ticker);\n const unmap = this.portMapper?.close();\n for (const s of this.sockets) s.end(closeFrame(1001, \"host left\"));\n await new Promise<void>((resolve) => this.server.close(() => resolve()));\n setTimeout(() => { for (const s of this.sockets) s.destroy(); }, 300).unref?.();\n await unmap;\n }\n\n // ---------------------------------------------------------------- one TCP connection\n\n private accept(sock: Socket): void {\n if (this.closed) {\n sock.destroy();\n return;\n }\n this.sockets.add(sock);\n sock.on(\"close\", () => this.sockets.delete(sock));\n sock.on(\"error\", () => {});\n const ip = (sock.remoteAddress ?? \"\").replace(/^::ffff:/, \"\");\n let head = Buffer.alloc(0);\n const timer = setTimeout(() => sock.destroy(), HANDSHAKE_TIMEOUT_MS);\n const onData = (chunk: Buffer) => {\n head = Buffer.concat([head, chunk]);\n const end = head.indexOf(\"\\r\\n\\r\\n\");\n if (end < 0) {\n if (head.length > MAX_HEADER_BYTES) sock.destroy();\n return;\n }\n clearTimeout(timer);\n sock.off(\"data\", onData);\n const rest = head.subarray(end + 4);\n if (!this.handshake(sock, head.subarray(0, end).toString(\"latin1\"))) return;\n this.run(sock, ip, rest);\n };\n sock.on(\"data\", onData);\n }\n\n /** RFC 6455 s.4.2: answers 101 with Sec-WebSocket-Accept, or 200 for /healthz, or 400/426. */\n private handshake(sock: Socket, head: string): boolean {\n const [requestLine, ...lines] = head.split(\"\\r\\n\");\n const headers = new Map<string, string>();\n for (const l of lines) {\n const i = l.indexOf(\":\");\n if (i > 0) headers.set(l.slice(0, i).trim().toLowerCase(), l.slice(i + 1).trim());\n }\n const [method, path] = requestLine.split(\" \");\n const reply = (status: string, body: string) =>\n sock.end(`HTTP/1.1 ${status}\\r\\nContent-Type: text/plain\\r\\nContent-Length: ${Buffer.byteLength(body)}\\r\\nConnection: close\\r\\n\\r\\n${body}`);\n if (method === \"GET\" && path === \"/healthz\") {\n reply(\"200 OK\", \"ok\");\n return false;\n }\n const key = headers.get(\"sec-websocket-key\");\n const upgrade = /websocket/i.test(headers.get(\"upgrade\") ?? \"\");\n if (method === \"GET\" && !upgrade && this.http && path) {\n let page: HttpReply | null = null;\n try {\n page = this.http(path.split(\"?\")[0]);\n } catch {\n page = { status: 500, type: \"text/plain\", body: \"error\" };\n }\n const p = page ?? { status: 404, type: \"text/plain\", body: \"not found\" };\n const body = typeof p.body === \"string\" ? Buffer.from(p.body, \"utf8\") : Buffer.from(p.body);\n const status = p.status ?? 200;\n sock.write(`HTTP/1.1 ${status} ${STATUS_TEXT[status] ?? \"OK\"}\\r\\nContent-Type: ${p.type}\\r\\n` +\n `Content-Length: ${body.length}\\r\\nCache-Control: no-store\\r\\nX-Content-Type-Options: nosniff\\r\\nConnection: close\\r\\n\\r\\n`);\n sock.end(body);\n return false;\n }\n if (method !== \"GET\" || !upgrade || !key) {\n reply(\"426 Upgrade Required\", \"this port speaks WebSocket (OneLobby direct host)\");\n return false;\n }\n const accept = createHash(\"sha1\").update(key + WS_GUID).digest(\"base64\");\n sock.write(`HTTP/1.1 101 Switching Protocols\\r\\nUpgrade: websocket\\r\\nConnection: Upgrade\\r\\nSec-WebSocket-Accept: ${accept}\\r\\n\\r\\n`);\n return true;\n }\n\n private run(sock: Socket, ip: string, initial: Buffer): void {\n let open = true;\n const sendText = (text: string) => {\n if (open && !sock.destroyed) sock.write(frame(0x1, Buffer.from(text, \"utf8\")));\n };\n const shut = (code: number, reason: string) => {\n if (!open) return;\n open = false;\n if (!sock.destroyed) sock.end(closeFrame(code, reason));\n };\n let session: Session | null = null;\n session = this.hub.connect(ip, sendText, () => shut(1000, \"closed by the hub\"));\n if (!session) return;\n const s = session;\n const reader = new FrameReader(this.maxMessageBytes + 1024, {\n text: (t) => this.hub.receive(s, t),\n ping: (p) => !sock.destroyed && sock.write(frame(0xa, p)),\n close: () => {\n shut(1000, \"\");\n this.hub.disconnect(s);\n },\n fail: (code, why) => {\n shut(code, why);\n this.hub.disconnect(s);\n },\n });\n sock.on(\"data\", (b: Buffer) => reader.push(b));\n sock.on(\"close\", () => {\n open = false;\n this.hub.disconnect(s);\n });\n if (initial.length) reader.push(initial);\n }\n}\n\n// ------------------------------------------------------------------------------------------------ RFC 6455 framing\n\n/** One unmasked server frame (FIN set). */\nfunction frame(opcode: number, payload: Buffer): Buffer {\n const n = payload.length;\n const head = n < 126 ? Buffer.from([0x80 | opcode, n]) : n < 65536 ? Buffer.alloc(4) : Buffer.alloc(10);\n if (n >= 126) {\n head[0] = 0x80 | opcode;\n if (n < 65536) {\n head[1] = 126;\n head.writeUInt16BE(n, 2);\n } else {\n head[1] = 127;\n head.writeBigUInt64BE(BigInt(n), 2);\n }\n }\n return Buffer.concat([head, payload]);\n}\n\nfunction closeFrame(code: number, reason: string): Buffer {\n const r = Buffer.from(reason.slice(0, 100), \"utf8\");\n const p = Buffer.alloc(2 + r.length);\n p.writeUInt16BE(code, 0);\n r.copy(p, 2);\n return frame(0x8, p);\n}\n\ninterface FrameSink {\n text(t: string): void;\n ping(payload: Buffer): void;\n close(): void;\n fail(code: number, why: string): void;\n}\n\n/** Incremental client-frame parser: masked frames only, text messages (fragmented or not), control frames between. */\nclass FrameReader {\n private buf: Buffer = Buffer.alloc(0);\n private parts: Buffer[] = [];\n private partBytes = 0;\n private inMessage = false;\n private binary = false;\n private done = false;\n private readonly utf8 = new TextDecoder(\"utf-8\", { fatal: true });\n\n constructor(private readonly maxMessage: number, private readonly sink: FrameSink) {}\n\n push(chunk: Buffer): void {\n if (this.done) return;\n this.buf = this.buf.length ? Buffer.concat([this.buf, chunk]) : chunk;\n while (!this.done) {\n if (this.buf.length < 2) return;\n const b0 = this.buf[0];\n const b1 = this.buf[1];\n const fin = (b0 & 0x80) !== 0;\n const opcode = b0 & 0x0f;\n if (b0 & 0x70) return this.fail(1002, \"reserved bits set\");\n if (!(b1 & 0x80)) return this.fail(1002, \"client frames must be masked\");\n let len = b1 & 0x7f;\n let off = 2;\n if (len === 126) {\n if (this.buf.length < 4) return;\n len = this.buf.readUInt16BE(2);\n off = 4;\n } else if (len === 127) {\n if (this.buf.length < 10) return;\n const big = this.buf.readBigUInt64BE(2);\n if (big > BigInt(this.maxMessage)) return this.fail(1009, \"message too big\");\n len = Number(big);\n off = 10;\n }\n if (len > this.maxMessage) return this.fail(1009, \"message too big\");\n if (this.buf.length < off + 4 + len) return;\n const mask = this.buf.subarray(off, off + 4);\n const payload = Buffer.from(this.buf.subarray(off + 4, off + 4 + len));\n for (let i = 0; i < payload.length; i++) payload[i] ^= mask[i & 3];\n this.buf = this.buf.subarray(off + 4 + len);\n if (opcode >= 0x8) {\n if (!fin || len > 125) return this.fail(1002, \"bad control frame\");\n if (opcode === 0x8) {\n this.done = true;\n this.sink.close();\n return;\n }\n if (opcode === 0x9) this.sink.ping(payload);\n continue; // pong or unknown control: ignore\n }\n if (opcode === 0x1 || opcode === 0x2) {\n if (this.inMessage) return this.fail(1002, \"new message inside a fragmented one\");\n this.inMessage = true;\n this.binary = opcode === 0x2; // spec/signaling.md: binary frames are ignored\n } else if (opcode !== 0x0 || !this.inMessage) return this.fail(1002, \"unexpected continuation\");\n this.partBytes += payload.length;\n if (this.partBytes > this.maxMessage) return this.fail(1009, \"message too big\");\n if (!this.binary) this.parts.push(payload);\n if (fin) {\n const all = this.parts.length === 1 ? this.parts[0] : Buffer.concat(this.parts);\n const skip = this.binary;\n this.parts = [];\n this.partBytes = 0;\n this.inMessage = false;\n this.binary = false;\n if (skip) continue;\n let text: string;\n try {\n text = this.utf8.decode(all);\n } catch {\n return this.fail(1007, \"invalid UTF-8\");\n }\n this.sink.text(text);\n }\n }\n }\n\n private fail(code: number, why: string): void {\n this.done = true;\n this.sink.fail(code, why);\n }\n}\n\n// ------------------------------------------------------------------------------------------------ in-memory socket\n\n/**\n * The direct-IP host's own signaling socket (C#'s `HubSignalingSocket`): sends go straight into the hub; the hub's\n * replies are delivered on a microtask, so callbacks never run inside the hub.\n */\nexport class LocalHubSocket implements SocketLike {\n /** Address the hub sees for the host itself (rate limits and kicks are per address). */\n static readonly ADDRESS = \"local\";\n readyState = 0;\n onopen: SocketLike[\"onopen\"] = null;\n onmessage: SocketLike[\"onmessage\"] = null;\n onclose: SocketLike[\"onclose\"] = null;\n onerror: SocketLike[\"onerror\"] = null;\n private session: Session | null;\n\n constructor(private readonly hub: SignalingHub) {\n this.session = hub.connect(LocalHubSocket.ADDRESS, (text) => queueMicrotask(() => this.readyState === 1 && this.onmessage?.({ data: text })), () =>\n this.finish(),\n );\n queueMicrotask(() => {\n if (!this.session) {\n this.finish();\n return;\n }\n this.readyState = 1;\n this.onopen?.();\n });\n }\n\n send(data: string): void {\n if (this.readyState === 1 && this.session) this.hub.receive(this.session, data);\n }\n\n close(): void {\n const s = this.session;\n const wasOpen = this.readyState === 1;\n this.finish();\n if (wasOpen && s) this.hub.disconnect(s);\n }\n\n private finish(): void {\n if (this.readyState === 3) return;\n this.readyState = 3;\n queueMicrotask(() => this.onclose?.());\n }\n}\n"]}
1
+ {"version":3,"file":"hub-host.js","sourceRoot":"","sources":["../../src/direct/hub-host.ts"],"names":[],"mappings":"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","sourcesContent":["/// <reference types=\"node\" />\nimport { createHash } from \"node:crypto\";\nimport { createSocket } from \"node:dgram\";\nimport { createServer, type Server, type Socket } from \"node:net\";\nimport type { SocketLike } from \"../transport.js\";\nimport { SignalingHub, directHubOptions, type HubDeps, type HubOptions, type Session } from \"./hub.js\";\nimport { PortMapper, type PortMapperOptions, type PortMapResult } from \"./portmap.js\";\n\nconst WS_GUID = \"258EAFA5-E914-47DA-95CA-C5AB0DC85B11\";\nconst MAX_HEADER_BYTES = 8192;\nconst HANDSHAKE_TIMEOUT_MS = 5000;\nconst STATUS_TEXT: Record<number, string> = { 200: \"OK\", 301: \"Moved Permanently\", 302: \"Found\", 404: \"Not Found\", 500: \"Internal Server Error\" };\n\n/**\n * Rejects (EADDRINUSE) when another program holds IPv4 TCP or UDP `port`. Windows lets the dual-stack listener bind\n * over a program on 0.0.0.0 (every IPv4 joiner would then reach that program), and the peer links need UDP `port`\n * too, so both are tried and released before the real listen.\n */\nasync function probeIPv4(port: number): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n const s = createServer();\n s.once(\"error\", reject);\n s.listen({ port, host: \"0.0.0.0\", exclusive: true }, () => s.close(() => resolve()));\n });\n await new Promise<void>((resolve, reject) => {\n const u = createSocket({ type: \"udp4\" });\n u.once(\"error\", (e) => {\n u.close();\n reject(e);\n });\n u.bind({ port, address: \"0.0.0.0\", exclusive: true }, () => u.close(() => resolve()));\n });\n}\n\nexport class PortInUseError extends Error {\n readonly code = \"port_in_use\";\n constructor(port: number, detail: string) {\n super(`port_in_use: cannot listen on port ${port} (${detail}): is another copy of the game hosting?`);\n }\n}\n\n/**\n * Runs a {@link SignalingHub} inside a Node process and listens for joiners' signaling WebSockets on TCP `port`: the\n * Node twin of C#'s `DirectHubHost` (docs/direct-ip.md s.2, s.5 step 3). A plain `net` server with a minimal HTTP/1.1\n * upgrade and RFC 6455 framing (text, close, ping/pong, fragments; client frames must be masked), so the package keeps\n * zero runtime dependencies. Any path upgrades; `GET /healthz` answers `ok`. The host's own client talks to the hub in\n * memory through {@link connectLocal}. `hub.tick()` runs once a second on an unref'd timer.\n */\n/** One plain-HTTP answer from the host's port, e.g. a browser join page. */\nexport interface HttpReply {\n /** Default 200. */\n status?: number;\n /** Content-Type, e.g. \"text/html; charset=utf-8\". */\n type: string;\n body: string | Uint8Array;\n}\n\n/** Serves plain `GET` requests on the direct port (path without the query); null answers 404. */\nexport type HttpHandler = (path: string) => HttpReply | null;\n\nexport class DirectHubHost {\n readonly hub: SignalingHub;\n /**\n * Plain `GET`s that are not WebSocket upgrades go here, so the host can serve its own join page: a browser opening\n * `http://<host address>:<port>/` gets a page that joins with `kit.joinDirect(location.host)`. Null (default): 426.\n */\n http: HttpHandler | null = null;\n /** The bound TCP port (the requested one, or the one the OS picked for 0). */\n readonly port: number;\n portMapper: PortMapper | null = null;\n private readonly server: Server;\n private readonly sockets = new Set<Socket>();\n private readonly ticker: ReturnType<typeof setInterval>;\n private readonly maxMessageBytes: number;\n private closed = false;\n\n private constructor(server: Server, port: number, hub: SignalingHub, maxMessageBytes: number) {\n this.server = server;\n this.port = port;\n this.hub = hub;\n this.maxMessageBytes = maxMessageBytes;\n this.ticker = setInterval(() => this.hub.tick(), 1000);\n this.ticker.unref?.();\n server.on(\"connection\", (sock) => this.accept(sock));\n }\n\n /**\n * Binds TCP `port` on every interface (IPv6 dual-stack where available). Hub options default to\n * {@link directHubOptions} (one lobby, no codes, no ICE servers). Rejects with {@link PortInUseError}.\n */\n static async listen(port: number, options?: Partial<HubOptions>, deps?: HubDeps & { host?: string }): Promise<DirectHubHost> {\n const opts = { ...directHubOptions(8), ...options };\n const server = createServer({ noDelay: true });\n const tryListen = (host: string, ipv6Only?: boolean) =>\n new Promise<void>((resolve, reject) => {\n const onError = (e: NodeJS.ErrnoException) => reject(e);\n server.once(\"error\", onError);\n server.listen({ port, host, ipv6Only }, () => {\n server.off(\"error\", onError);\n resolve();\n });\n });\n try {\n if (deps?.host) await tryListen(deps.host);\n else {\n if (port !== 0) await probeIPv4(port);\n try {\n await tryListen(\"::\", false);\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === \"EADDRINUSE\") throw e;\n await tryListen(\"0.0.0.0\"); // no IPv6 on this machine\n }\n }\n } catch (e) {\n throw new PortInUseError(port, (e as NodeJS.ErrnoException).code ?? String(e));\n }\n const addr = server.address();\n const bound = typeof addr === \"object\" && addr ? addr.port : port;\n return new DirectHubHost(server, bound, new SignalingHub(opts, deps), opts.maxMessageBytes);\n }\n\n /** Joiner sockets currently open. */\n get connectionCount(): number {\n return this.sockets.size;\n }\n\n /**\n * The host's own signaling socket: a WebSocket look-alike wired straight into the hub (no network). Pass it to the\n * kit as `advanced.socketFactory: () => host.connectLocal()`.\n */\n connectLocal(): SocketLike {\n return new LocalHubSocket(this.hub);\n }\n\n /** Asks the home router to forward TCP and UDP `port` here (UPnP, then NAT-PMP; docs/direct-ip.md s.2a). */\n openPort(options?: PortMapperOptions): Promise<PortMapResult> {\n if (this.portMapper) throw new Error(\"the port is already being opened\");\n this.portMapper = new PortMapper(this.port, options);\n return this.portMapper.open();\n }\n\n /** Stops listening, closes every joiner socket and removes the router mapping. */\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n clearInterval(this.ticker);\n const unmap = this.portMapper?.close();\n for (const s of this.sockets) s.end(closeFrame(1001, \"host left\"));\n await new Promise<void>((resolve) => this.server.close(() => resolve()));\n setTimeout(() => { for (const s of this.sockets) s.destroy(); }, 300).unref?.();\n await unmap;\n }\n\n // ---------------------------------------------------------------- one TCP connection\n\n private accept(sock: Socket): void {\n if (this.closed) {\n sock.destroy();\n return;\n }\n this.sockets.add(sock);\n sock.on(\"close\", () => this.sockets.delete(sock));\n sock.on(\"error\", () => {});\n const ip = (sock.remoteAddress ?? \"\").replace(/^::ffff:/, \"\");\n let head = Buffer.alloc(0);\n const timer = setTimeout(() => sock.destroy(), HANDSHAKE_TIMEOUT_MS);\n const onData = (chunk: Buffer) => {\n head = Buffer.concat([head, chunk]);\n const end = head.indexOf(\"\\r\\n\\r\\n\");\n if (end < 0) {\n if (head.length > MAX_HEADER_BYTES) sock.destroy();\n return;\n }\n clearTimeout(timer);\n sock.off(\"data\", onData);\n const rest = head.subarray(end + 4);\n if (!this.handshake(sock, head.subarray(0, end).toString(\"latin1\"))) return;\n this.run(sock, ip, rest);\n };\n sock.on(\"data\", onData);\n }\n\n /** RFC 6455 s.4.2: answers 101 with Sec-WebSocket-Accept, or 200 for /healthz, or 400/426. */\n private handshake(sock: Socket, head: string): boolean {\n const [requestLine, ...lines] = head.split(\"\\r\\n\");\n const headers = new Map<string, string>();\n for (const l of lines) {\n const i = l.indexOf(\":\");\n if (i > 0) headers.set(l.slice(0, i).trim().toLowerCase(), l.slice(i + 1).trim());\n }\n const [method, path] = requestLine.split(\" \");\n const reply = (status: string, body: string) =>\n sock.end(`HTTP/1.1 ${status}\\r\\nContent-Type: text/plain\\r\\nContent-Length: ${Buffer.byteLength(body)}\\r\\nConnection: close\\r\\n\\r\\n${body}`);\n if (method === \"GET\" && path === \"/healthz\") {\n reply(\"200 OK\", \"ok\");\n return false;\n }\n const key = headers.get(\"sec-websocket-key\");\n const upgrade = /websocket/i.test(headers.get(\"upgrade\") ?? \"\");\n if (method === \"GET\" && !upgrade && this.http && path) {\n let page: HttpReply | null = null;\n try {\n page = this.http(path.split(\"?\")[0]);\n } catch {\n page = { status: 500, type: \"text/plain\", body: \"error\" };\n }\n const p = page ?? { status: 404, type: \"text/plain\", body: \"not found\" };\n const body = typeof p.body === \"string\" ? Buffer.from(p.body, \"utf8\") : Buffer.from(p.body);\n const status = p.status ?? 200;\n sock.write(`HTTP/1.1 ${status} ${STATUS_TEXT[status] ?? \"OK\"}\\r\\nContent-Type: ${p.type}\\r\\n` +\n `Content-Length: ${body.length}\\r\\nCache-Control: no-store\\r\\nX-Content-Type-Options: nosniff\\r\\nConnection: close\\r\\n\\r\\n`);\n sock.end(body);\n return false;\n }\n if (method !== \"GET\" || !upgrade || !key) {\n reply(\"426 Upgrade Required\", \"this port speaks WebSocket (OneLobby direct host)\");\n return false;\n }\n const accept = createHash(\"sha1\").update(key + WS_GUID).digest(\"base64\");\n sock.write(`HTTP/1.1 101 Switching Protocols\\r\\nUpgrade: websocket\\r\\nConnection: Upgrade\\r\\nSec-WebSocket-Accept: ${accept}\\r\\n\\r\\n`);\n return true;\n }\n\n private run(sock: Socket, ip: string, initial: Buffer): void {\n let open = true;\n const sendText = (text: string) => {\n if (open && !sock.destroyed) sock.write(frame(0x1, Buffer.from(text, \"utf8\")));\n };\n const shut = (code: number, reason: string) => {\n if (!open) return;\n open = false;\n if (!sock.destroyed) sock.end(closeFrame(code, reason));\n };\n let session: Session | null = null;\n session = this.hub.connect(ip, sendText, () => shut(1000, \"closed by the hub\"));\n if (!session) return;\n const s = session;\n const reader = new FrameReader(this.maxMessageBytes + 1024, {\n text: (t) => this.hub.receive(s, t),\n ping: (p) => !sock.destroyed && sock.write(frame(0xa, p)),\n close: () => {\n shut(1000, \"\");\n this.hub.disconnect(s);\n },\n fail: (code, why) => {\n shut(code, why);\n this.hub.disconnect(s);\n },\n });\n sock.on(\"data\", (b: Buffer) => reader.push(b));\n sock.on(\"close\", () => {\n open = false;\n this.hub.disconnect(s);\n });\n if (initial.length) reader.push(initial);\n }\n}\n\n// ------------------------------------------------------------------------------------------------ RFC 6455 framing\n\n/** One unmasked server frame (FIN set). */\nfunction frame(opcode: number, payload: Buffer): Buffer {\n const n = payload.length;\n const head = n < 126 ? Buffer.from([0x80 | opcode, n]) : n < 65536 ? Buffer.alloc(4) : Buffer.alloc(10);\n if (n >= 126) {\n head[0] = 0x80 | opcode;\n if (n < 65536) {\n head[1] = 126;\n head.writeUInt16BE(n, 2);\n } else {\n head[1] = 127;\n head.writeBigUInt64BE(BigInt(n), 2);\n }\n }\n return Buffer.concat([head, payload]);\n}\n\nfunction closeFrame(code: number, reason: string): Buffer {\n const r = Buffer.from(reason.slice(0, 100), \"utf8\");\n const p = Buffer.alloc(2 + r.length);\n p.writeUInt16BE(code, 0);\n r.copy(p, 2);\n return frame(0x8, p);\n}\n\ninterface FrameSink {\n text(t: string): void;\n ping(payload: Buffer): void;\n close(): void;\n fail(code: number, why: string): void;\n}\n\n/** Incremental client-frame parser: masked frames only, text messages (fragmented or not), control frames between. */\nclass FrameReader {\n private buf: Buffer = Buffer.alloc(0);\n private parts: Buffer[] = [];\n private partBytes = 0;\n private inMessage = false;\n private binary = false;\n private done = false;\n private readonly utf8 = new TextDecoder(\"utf-8\", { fatal: true });\n\n constructor(private readonly maxMessage: number, private readonly sink: FrameSink) {}\n\n push(chunk: Buffer): void {\n if (this.done) return;\n this.buf = this.buf.length ? Buffer.concat([this.buf, chunk]) : chunk;\n while (!this.done) {\n if (this.buf.length < 2) return;\n const b0 = this.buf[0];\n const b1 = this.buf[1];\n const fin = (b0 & 0x80) !== 0;\n const opcode = b0 & 0x0f;\n if (b0 & 0x70) return this.fail(1002, \"reserved bits set\");\n if (!(b1 & 0x80)) return this.fail(1002, \"client frames must be masked\");\n let len = b1 & 0x7f;\n let off = 2;\n if (len === 126) {\n if (this.buf.length < 4) return;\n len = this.buf.readUInt16BE(2);\n off = 4;\n } else if (len === 127) {\n if (this.buf.length < 10) return;\n const big = this.buf.readBigUInt64BE(2);\n if (big > BigInt(this.maxMessage)) return this.fail(1009, \"message too big\");\n len = Number(big);\n off = 10;\n }\n if (len > this.maxMessage) return this.fail(1009, \"message too big\");\n if (this.buf.length < off + 4 + len) return;\n const mask = this.buf.subarray(off, off + 4);\n const payload = Buffer.from(this.buf.subarray(off + 4, off + 4 + len));\n for (let i = 0; i < payload.length; i++) payload[i] ^= mask[i & 3];\n this.buf = this.buf.subarray(off + 4 + len);\n if (opcode >= 0x8) {\n if (!fin || len > 125) return this.fail(1002, \"bad control frame\");\n if (opcode === 0x8) {\n this.done = true;\n this.sink.close();\n return;\n }\n if (opcode === 0x9) this.sink.ping(payload);\n continue; // pong or unknown control: ignore\n }\n if (opcode === 0x1 || opcode === 0x2) {\n if (this.inMessage) return this.fail(1002, \"new message inside a fragmented one\");\n this.inMessage = true;\n this.binary = opcode === 0x2; // spec/signaling.md: binary frames are ignored\n } else if (opcode !== 0x0 || !this.inMessage) return this.fail(1002, \"unexpected continuation\");\n this.partBytes += payload.length;\n if (this.partBytes > this.maxMessage) return this.fail(1009, \"message too big\");\n if (!this.binary) this.parts.push(payload);\n if (fin) {\n const all = this.parts.length === 1 ? this.parts[0] : Buffer.concat(this.parts);\n const skip = this.binary;\n this.parts = [];\n this.partBytes = 0;\n this.inMessage = false;\n this.binary = false;\n if (skip) continue;\n let text: string;\n try {\n text = this.utf8.decode(all);\n } catch {\n return this.fail(1007, \"invalid UTF-8\");\n }\n this.sink.text(text);\n }\n }\n }\n\n private fail(code: number, why: string): void {\n this.done = true;\n this.sink.fail(code, why);\n }\n}\n\n// ------------------------------------------------------------------------------------------------ in-memory socket\n\n/**\n * The direct-IP host's own signaling socket (C#'s `HubSignalingSocket`): sends go straight into the hub; the hub's\n * replies are delivered on a microtask, so callbacks never run inside the hub.\n */\nexport class LocalHubSocket implements SocketLike {\n /** Address the hub sees for the host itself (rate limits and kicks are per address). */\n static readonly ADDRESS = \"local\";\n readyState = 0;\n onopen: SocketLike[\"onopen\"] = null;\n onmessage: SocketLike[\"onmessage\"] = null;\n onclose: SocketLike[\"onclose\"] = null;\n onerror: SocketLike[\"onerror\"] = null;\n private session: Session | null;\n\n constructor(private readonly hub: SignalingHub) {\n this.session = hub.connect(LocalHubSocket.ADDRESS, (text) => queueMicrotask(() => this.readyState === 1 && this.onmessage?.({ data: text })), () =>\n this.finish(),\n );\n queueMicrotask(() => {\n if (!this.session) {\n this.finish();\n return;\n }\n this.readyState = 1;\n this.onopen?.();\n });\n }\n\n send(data: string): void {\n if (this.readyState === 1 && this.session) this.hub.receive(this.session, data);\n }\n\n close(): void {\n const s = this.session;\n const wasOpen = this.readyState === 1;\n this.finish();\n if (wasOpen && s) this.hub.disconnect(s);\n }\n\n private finish(): void {\n if (this.readyState === 3) return;\n this.readyState = 3;\n queueMicrotask(() => this.onclose?.());\n }\n}\n"]}
@@ -46,9 +46,16 @@ export declare class PortMapper {
46
46
  private natPmpAt;
47
47
  private renew;
48
48
  private stopped;
49
+ private opening;
50
+ /**
51
+ * Every mapping a request was sent for and the router did not refuse ("upnp TCP", "pmp 1", ...): what close()
52
+ * deletes. Added before the request goes out, so a host that quits mid-request still undoes it.
53
+ */
54
+ private readonly maybeMapped;
49
55
  constructor(port: number, options?: PortMapperOptions);
50
56
  /** Finds the router and maps TCP + UDP `port`. Never rejects for network trouble: see `detail`. */
51
57
  open(): Promise<PortMapResult>;
58
+ private openBoth;
52
59
  private done;
53
60
  private tryUpnp;
54
61
  private addUpnpBoth;
@@ -69,8 +76,12 @@ export declare class PortMapper {
69
76
  private mapRequest;
70
77
  /** RFC 6886 s.3.1 retransmission: 250 ms, doubling, 3 tries (under 2 s). Null when nobody answers. */
71
78
  private natPmpRequest;
72
- /** Deletes the mappings (best effort, bounded to about 1.5 s) and stops renewing. */
79
+ /**
80
+ * Stops renewing and deletes every mapping it may have made, even while {@link open} is still talking to the router
81
+ * (best effort, bounded to about two seconds; a request still out when it returns undoes itself when it lands).
82
+ */
73
83
  close(): Promise<void>;
84
+ private undo;
74
85
  private natPmpOnce;
75
86
  }
76
87
  /**
@@ -36,6 +36,12 @@ export class PortMapper {
36
36
  natPmpAt = null;
37
37
  renew = null;
38
38
  stopped = false;
39
+ opening = null;
40
+ /**
41
+ * Every mapping a request was sent for and the router did not refuse ("upnp TCP", "pmp 1", ...): what close()
42
+ * deletes. Added before the request goes out, so a host that quits mid-request still undoes it.
43
+ */
44
+ maybeMapped = new Set();
39
45
  constructor(port, options = {}) {
40
46
  this.port = port;
41
47
  if (!Number.isInteger(port) || port < 1 || port > 65535)
@@ -52,7 +58,10 @@ export class PortMapper {
52
58
  };
53
59
  }
54
60
  /** Finds the router and maps TCP + UDP `port`. Never rejects for network trouble: see `detail`. */
55
- async open() {
61
+ open() {
62
+ return (this.opening = this.openBoth());
63
+ }
64
+ async openBoth() {
56
65
  const notes = [];
57
66
  if (this.o.tryUpnp) {
58
67
  try {
@@ -141,11 +150,27 @@ export class PortMapper {
141
150
  return { mapped: true, method: "upnp", externalIPv4: external, localIPv4: local, detail: "UPnP" };
142
151
  }
143
152
  async addUpnpBoth(local, lease) {
144
- for (const proto of ["TCP", "UDP"])
145
- await this.soap("AddPortMapping", [
146
- ["NewRemoteHost", ""], ["NewExternalPort", String(this.port)], ["NewProtocol", proto], ["NewInternalPort", String(this.port)],
147
- ["NewInternalClient", local], ["NewEnabled", "1"], ["NewPortMappingDescription", this.o.description], ["NewLeaseDuration", String(lease)],
148
- ]);
153
+ for (const proto of ["TCP", "UDP"]) {
154
+ if (this.stopped)
155
+ throw new Error("closed before the router answered");
156
+ const key = `upnp ${proto}`;
157
+ this.maybeMapped.add(key);
158
+ try {
159
+ await this.soap("AddPortMapping", [
160
+ ["NewRemoteHost", ""], ["NewExternalPort", String(this.port)], ["NewProtocol", proto], ["NewInternalPort", String(this.port)],
161
+ ["NewInternalClient", local], ["NewEnabled", "1"], ["NewPortMappingDescription", this.o.description], ["NewLeaseDuration", String(lease)],
162
+ ]);
163
+ }
164
+ catch (e) {
165
+ // refused (e.g. 718: another device holds the port): not ours, so close() must not delete it
166
+ if (e instanceof UpnpError)
167
+ this.maybeMapped.delete(key);
168
+ throw e;
169
+ }
170
+ // close() ran while this request was out: its delete may have reached the router first, so undo it now
171
+ if (this.stopped)
172
+ await this.undo([key]);
173
+ }
149
174
  }
150
175
  /**
151
176
  * SSDP search: multicast once per interface that has a gateway (so a Hyper-V, WSL or Docker adapter cannot swallow
@@ -257,15 +282,24 @@ export class PortMapper {
257
282
  /** Maps UDP then TCP; null on success, else why. A different external port counts as failure (and is undone). */
258
283
  async natPmpMapBoth(lifetime) {
259
284
  for (const op of [1, 2]) {
285
+ if (this.stopped)
286
+ return "closed before the router answered";
287
+ const key = `pmp ${op}`;
288
+ this.maybeMapped.add(key);
260
289
  const reply = await this.natPmpRequest(this.natPmpAt, this.mapRequest(op, lifetime), 16);
290
+ if (this.stopped)
291
+ await this.undo([key]);
261
292
  if (!reply)
262
293
  return "no answer to the mapping request";
263
294
  const rc = reply.readUInt16BE(2);
264
- if (rc !== 0)
295
+ if (rc !== 0) {
296
+ this.maybeMapped.delete(key);
265
297
  return `result code ${rc}`;
298
+ }
266
299
  const mapped = reply.readUInt16BE(10);
267
300
  if (lifetime > 0 && mapped !== this.port) {
268
301
  await this.natPmpRequest(this.natPmpAt, this.mapRequest(op, 0), 16);
302
+ this.maybeMapped.delete(key);
269
303
  return `router offered port ${mapped} instead of ${this.port}`;
270
304
  }
271
305
  }
@@ -307,20 +341,30 @@ export class PortMapper {
307
341
  s.bind(0, attempt);
308
342
  });
309
343
  }
310
- /** Deletes the mappings (best effort, bounded to about 1.5 s) and stops renewing. */
344
+ /**
345
+ * Stops renewing and deletes every mapping it may have made, even while {@link open} is still talking to the router
346
+ * (best effort, bounded to about two seconds; a request still out when it returns undoes itself when it lands).
347
+ */
311
348
  async close() {
312
349
  if (this.stopped)
313
350
  return;
314
351
  this.stopped = true;
315
352
  if (this.renew)
316
353
  clearInterval(this.renew);
317
- const r = this.result;
318
- if (!r?.mapped)
354
+ if (this.opening)
355
+ await Promise.race([this.opening.catch(() => { }), delay(500)]);
356
+ await this.undo([...this.maybeMapped]);
357
+ }
358
+ async undo(keys) {
359
+ if (keys.length === 0)
319
360
  return;
320
- const work = r.method === "upnp"
321
- ? Promise.all(["TCP", "UDP"].map((p) => this.soap("DeletePortMapping", [["NewRemoteHost", ""], ["NewExternalPort", String(this.port)], ["NewProtocol", p]])))
322
- : Promise.all([1, 2].map((op) => this.natPmpOnce(this.mapRequest(op, 0))));
323
- await Promise.race([work.catch(() => { }), new Promise((r2) => setTimeout(r2, 1500).unref?.())]);
361
+ const work = Promise.all(keys.map((k) => {
362
+ this.maybeMapped.delete(k);
363
+ return k.startsWith("upnp ")
364
+ ? this.soap("DeletePortMapping", [["NewRemoteHost", ""], ["NewExternalPort", String(this.port)], ["NewProtocol", k.slice(5)]])
365
+ : this.natPmpOnce(this.mapRequest(Number(k.slice(4)), 0));
366
+ }));
367
+ await Promise.race([work.catch(() => { }), delay(1500)]);
324
368
  }
325
369
  natPmpOnce(req) {
326
370
  return new Promise((resolve) => {
@@ -462,4 +506,7 @@ export function localAddresses(port) {
462
506
  }
463
507
  return [...v4, ...v6];
464
508
  }
509
+ function delay(ms) {
510
+ return new Promise((r) => setTimeout(r, ms).unref?.());
511
+ }
465
512
  //# sourceMappingURL=portmap.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"portmap.js","sourceRoot":"","sources":["../../src/direct/portmap.ts"],"names":[],"mappings":"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","sourcesContent":["/// <reference types=\"node\" />\nimport { createSocket, type Socket as UdpSocket } from \"node:dgram\";\nimport { request as httpRequest } from \"node:http\";\nimport { networkInterfaces } from \"node:os\";\nimport { execFile } from \"node:child_process\";\n\nexport type PortMapMethod = \"none\" | \"upnp\" | \"natpmp\";\n\n/** What {@link PortMapper.open} found. `mapped` means TCP and UDP `port` both forward here. */\nexport interface PortMapResult {\n mapped: boolean;\n method: PortMapMethod;\n /** The router's WAN address as it reports it (may itself be private or carrier-grade NAT). */\n externalIPv4: string | null;\n /** This PC's address on the router's network, when the router was found. */\n localIPv4: string | null;\n /** Why nothing was mapped, for logs. */\n detail: string;\n}\n\n/** Seams for tests (a fake gateway on loopback) and the few knobs a game might want. Same as C#'s PortMapperOptions. */\nexport interface PortMapperOptions {\n /** Where the SSDP M-SEARCH goes; default the UPnP multicast group 239.255.255.250:1900. */\n ssdp?: { address: string; port: number };\n /** NAT-PMP server; default this PC's IPv4 default gateway, port 5351. */\n natPmp?: { address: string; port: number };\n discoveryTimeoutMs?: number;\n requestTimeoutMs?: number;\n /** Lease asked for; renewed at half of it while hosting. Routers that only allow permanent leases get 0. */\n leaseSeconds?: number;\n description?: string;\n tryUpnp?: boolean;\n tryNatPmp?: boolean;\n}\n\nconst SEARCH_TARGETS = [\n \"urn:schemas-upnp-org:device:InternetGatewayDevice:1\",\n \"urn:schemas-upnp-org:device:InternetGatewayDevice:2\",\n \"urn:schemas-upnp-org:service:WANIPConnection:1\",\n];\nconst SERVICE_TYPES = [\n \"urn:schemas-upnp-org:service:WANIPConnection:2\",\n \"urn:schemas-upnp-org:service:WANIPConnection:1\",\n \"urn:schemas-upnp-org:service:WANPPPConnection:1\",\n];\n\nclass UpnpError extends Error {\n constructor(readonly code: number, message: string) {\n super(message);\n }\n}\n\n/**\n * Opens TCP and UDP `port` on the home router for a direct-IP host (docs/direct-ip.md s.2a): UPnP IGD (SSDP, then\n * GetExternalIPAddress + AddPortMapping), then NAT-PMP (RFC 6886). Node twin of C#'s `PortMapper`, same order and rules:\n * same external and internal port or nothing (a join code carries one port), leases renew at half-life, {@link close}\n * deletes the mappings. Windows Firewall drops SSDP replies on a network marked Public; the result is then unmapped.\n */\nexport class PortMapper {\n result: PortMapResult | null = null;\n private readonly o: Required<Omit<PortMapperOptions, \"natPmp\">> & { natPmp?: { address: string; port: number } };\n private control: URL | null = null;\n private service = \"\";\n private lease = 0;\n private natPmpAt: { address: string; port: number } | null = null;\n private renew: ReturnType<typeof setInterval> | null = null;\n private stopped = false;\n\n constructor(readonly port: number, options: PortMapperOptions = {}) {\n if (!Number.isInteger(port) || port < 1 || port > 65535) throw new RangeError(`bad port ${port}`);\n this.o = {\n ssdp: { address: \"239.255.255.250\", port: 1900 },\n discoveryTimeoutMs: 2000,\n requestTimeoutMs: 3000,\n leaseSeconds: 7200,\n description: \"OneLobby\",\n tryUpnp: true,\n tryNatPmp: true,\n ...options,\n };\n }\n\n /** Finds the router and maps TCP + UDP `port`. Never rejects for network trouble: see `detail`. */\n async open(): Promise<PortMapResult> {\n const notes: string[] = [];\n if (this.o.tryUpnp) {\n try {\n const r = await this.tryUpnp();\n if (r) return this.done(r);\n notes.push(\"no UPnP gateway answered\");\n } catch (e) {\n notes.push(`UPnP: ${(e as Error).message}`);\n }\n }\n if (this.o.tryNatPmp) {\n try {\n const r = await this.tryNatPmp();\n if (r) return this.done(r);\n notes.push(\"no NAT-PMP answer\");\n } catch (e) {\n notes.push(`NAT-PMP: ${(e as Error).message}`);\n }\n }\n return (this.result = { mapped: false, method: \"none\", externalIPv4: null, localIPv4: null, detail: notes.join(\"; \") });\n }\n\n private done(r: PortMapResult): PortMapResult {\n this.result = r;\n if (r.mapped && !this.stopped && (this.lease > 0 || r.method === \"natpmp\")) {\n const everyMs = Math.max(30, (this.lease > 0 ? this.lease : this.o.leaseSeconds) / 2) * 1000;\n this.renew = setInterval(() => {\n const p = r.method === \"upnp\" ? this.addUpnpBoth(r.localIPv4!, this.lease) : this.natPmpMapBoth(this.o.leaseSeconds);\n p.catch(() => {}); // the next round tries again; the old lease still has half its life\n }, everyMs);\n this.renew.unref?.();\n }\n return r;\n }\n\n // ------------------------------------------------------------------------------------------------ UPnP IGD\n\n private async tryUpnp(): Promise<PortMapResult | null> {\n const location = await this.discover();\n if (!location) return null;\n const desc = await this.http(\"GET\", location, null, {});\n const baseText = tag(desc.body, \"URLBase\");\n const base = baseText && /^https?:\\/\\//i.test(baseText) ? new URL(baseText) : location;\n for (const type of SERVICE_TYPES) {\n for (const svc of desc.body.match(/<service>[\\s\\S]*?<\\/service>/g) ?? []) {\n if ((tag(svc, \"serviceType\") ?? \"\").trim() !== type) continue;\n const ctl = tag(svc, \"controlURL\")?.trim();\n if (!ctl) continue;\n this.control = new URL(ctl, base);\n this.service = type;\n break;\n }\n if (this.control) break;\n }\n if (!this.control) return { mapped: false, method: \"upnp\", externalIPv4: null, localIPv4: null, detail: \"UPnP gateway has no WANIPConnection service\" };\n\n const local = await localAddressToward(this.control.hostname, Number(this.control.port) || 80);\n let external: string | null = null;\n try {\n external = tag((await this.soap(\"GetExternalIPAddress\", [])).body, \"NewExternalIPAddress\")?.trim() ?? null;\n } catch (e) {\n if (!(e instanceof UpnpError)) throw e; // some routers refuse this but still map\n }\n this.lease = this.o.leaseSeconds;\n try {\n await this.addUpnpBoth(local, this.lease);\n } catch (e) {\n if (!(e instanceof UpnpError)) throw e;\n if (e.code === 725) {\n // OnlyPermanentLeasesSupported\n this.lease = 0;\n await this.addUpnpBoth(local, 0);\n } else return { mapped: false, method: \"upnp\", externalIPv4: external, localIPv4: local, detail: `UPnP ${e.message}` };\n }\n return { mapped: true, method: \"upnp\", externalIPv4: external, localIPv4: local, detail: \"UPnP\" };\n }\n\n private async addUpnpBoth(local: string, lease: number): Promise<void> {\n for (const proto of [\"TCP\", \"UDP\"])\n await this.soap(\"AddPortMapping\", [\n [\"NewRemoteHost\", \"\"], [\"NewExternalPort\", String(this.port)], [\"NewProtocol\", proto], [\"NewInternalPort\", String(this.port)],\n [\"NewInternalClient\", local], [\"NewEnabled\", \"1\"], [\"NewPortMappingDescription\", this.o.description], [\"NewLeaseDuration\", String(lease)],\n ]);\n }\n\n /**\n * SSDP search: multicast once per interface that has a gateway (so a Hyper-V, WSL or Docker adapter cannot swallow\n * it), plus a unicast copy to the gateway.\n */\n private discover(): Promise<URL | null> {\n const multicast = Number(this.o.ssdp.address.split(\".\")[0]) >= 224;\n const locals = multicast ? gatewayInterfaces() : [];\n if (locals.length === 0) locals.push({ local: \"0.0.0.0\", gateway: null });\n return new Promise((resolve) => {\n const sockets: UdpSocket[] = [];\n let finished = false;\n const finish = (u: URL | null) => {\n if (finished) return;\n finished = true;\n clearTimeout(timer);\n for (const s of sockets) s.close();\n resolve(u);\n };\n const timer = setTimeout(() => finish(null), this.o.discoveryTimeoutMs);\n for (const { local, gateway } of locals) {\n const s = createSocket({ type: \"udp4\", reuseAddr: false });\n sockets.push(s);\n s.on(\"error\", () => {});\n s.on(\"message\", (m) => {\n const u = parseSsdp(m.toString(\"latin1\"));\n if (u) finish(u);\n });\n s.bind(0, local === \"0.0.0.0\" ? undefined : local, () => {\n try {\n s.setMulticastTTL(2);\n if (local !== \"0.0.0.0\") s.setMulticastInterface(local);\n } catch {\n /* not fatal: the OS default interface is used */\n }\n for (const st of SEARCH_TARGETS) {\n const b = Buffer.from(`M-SEARCH * HTTP/1.1\\r\\nHOST: 239.255.255.250:1900\\r\\nMAN: \"ssdp:discover\"\\r\\nMX: 1\\r\\nST: ${st}\\r\\n\\r\\n`);\n s.send(b, this.o.ssdp.port, this.o.ssdp.address, () => {});\n if (gateway) s.send(b, 1900, gateway, () => {});\n }\n });\n }\n });\n }\n\n private async soap(action: string, args: [string, string][]): Promise<{ status: number; body: string }> {\n const inner = args.map(([k, v]) => `<${k}>${xmlEscape(v)}</${k}>`).join(\"\");\n const body =\n `<?xml version=\"1.0\"?><s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">` +\n `<s:Body><u:${action} xmlns:u=\"${this.service}\">${inner}</u:${action}></s:Body></s:Envelope>`;\n const res = await this.http(\"POST\", this.control!, body, { \"Content-Type\": 'text/xml; charset=\"utf-8\"', SOAPAction: `\"${this.service}#${action}\"` });\n if (res.status < 200 || res.status >= 300) {\n const code = tag(res.body, \"errorCode\");\n const desc = tag(res.body, \"errorDescription\") ?? \"\";\n throw new UpnpError(Number(code) || 0, `${action} error ${code ?? res.status} ${desc}`.trim());\n }\n return res;\n }\n\n /**\n * One connection per request: some routers close after each reply without saying so, and a reused\n * keep-alive socket then fails every second request with \"socket hang up\".\n */\n private http(method: string, url: URL, body: string | null, headers: Record<string, string>): Promise<{ status: number; body: string }> {\n return new Promise((resolve, reject) => {\n const h = { ...headers, Connection: \"close\" };\n const req = httpRequest(\n { method, hostname: url.hostname, port: url.port || 80, path: url.pathname + url.search, timeout: this.o.requestTimeoutMs, agent: false,\n headers: body === null ? h : { ...h, \"Content-Length\": Buffer.byteLength(body) } },\n (res) => {\n let text = \"\";\n res.setEncoding(\"utf8\");\n res.on(\"data\", (d) => (text += d));\n res.on(\"end\", () => resolve({ status: res.statusCode ?? 0, body: text }));\n },\n );\n req.on(\"timeout\", () => req.destroy(new Error(`${method} ${url.pathname} timed out`)));\n req.on(\"error\", reject);\n if (body !== null) req.write(body);\n req.end();\n });\n }\n\n // ------------------------------------------------------------------------------------------------ NAT-PMP\n\n private async tryNatPmp(): Promise<PortMapResult | null> {\n // One gateway only: each silent one costs the full retransmission budget.\n let gw0 = this.o.natPmp ?? null;\n if (!gw0) {\n const routed = (await defaultGatewayFallback()) ?? gatewayInterfaces().find((g) => g.gateway)?.gateway ?? null;\n if (routed) gw0 = { address: routed, port: 5351 };\n }\n const candidates = gw0 ? [gw0] : [];\n for (const gw of candidates) {\n const ext = await this.natPmpRequest(gw, Buffer.from([0, 0]), 12);\n if (!ext) continue;\n this.natPmpAt = gw;\n const local = await localAddressToward(gw.address, gw.port);\n const external = ext.readUInt16BE(2) === 0 ? `${ext[8]}.${ext[9]}.${ext[10]}.${ext[11]}` : null;\n const err = await this.natPmpMapBoth(this.o.leaseSeconds);\n if (err) return { mapped: false, method: \"natpmp\", externalIPv4: external, localIPv4: local, detail: `NAT-PMP ${err}` };\n return { mapped: true, method: \"natpmp\", externalIPv4: external, localIPv4: local, detail: \"NAT-PMP\" };\n }\n return null;\n }\n\n /** Maps UDP then TCP; null on success, else why. A different external port counts as failure (and is undone). */\n private async natPmpMapBoth(lifetime: number): Promise<string | null> {\n for (const op of [1, 2]) {\n const reply = await this.natPmpRequest(this.natPmpAt!, this.mapRequest(op, lifetime), 16);\n if (!reply) return \"no answer to the mapping request\";\n const rc = reply.readUInt16BE(2);\n if (rc !== 0) return `result code ${rc}`;\n const mapped = reply.readUInt16BE(10);\n if (lifetime > 0 && mapped !== this.port) {\n await this.natPmpRequest(this.natPmpAt!, this.mapRequest(op, 0), 16);\n return `router offered port ${mapped} instead of ${this.port}`;\n }\n }\n return null;\n }\n\n private mapRequest(op: number, lifetime: number): Buffer {\n const b = Buffer.alloc(12);\n b[1] = op;\n b.writeUInt16BE(this.port, 4);\n b.writeUInt16BE(lifetime > 0 ? this.port : 0, 6);\n b.writeUInt32BE(lifetime, 8);\n return b;\n }\n\n /** RFC 6886 s.3.1 retransmission: 250 ms, doubling, 3 tries (under 2 s). Null when nobody answers. */\n private natPmpRequest(to: { address: string; port: number }, req: Buffer, replyLength: number): Promise<Buffer | null> {\n return new Promise((resolve) => {\n const s = createSocket(\"udp4\");\n let wait = 250;\n let tries = 0;\n let timer: ReturnType<typeof setTimeout> | null = null;\n const finish = (b: Buffer | null) => {\n if (timer) clearTimeout(timer);\n s.close();\n resolve(b);\n };\n s.on(\"error\", () => {}); // ICMP port unreachable on Windows: keep waiting for the timer\n s.on(\"message\", (b) => {\n if (b.length >= replyLength && b[0] === 0 && b[1] === req[1] + 128) finish(b);\n });\n const attempt = () => {\n if (tries++ >= 3) return finish(null);\n s.send(req, to.port, to.address, () => {});\n timer = setTimeout(attempt, wait);\n wait *= 2;\n };\n s.bind(0, attempt);\n });\n }\n\n /** Deletes the mappings (best effort, bounded to about 1.5 s) and stops renewing. */\n async close(): Promise<void> {\n if (this.stopped) return;\n this.stopped = true;\n if (this.renew) clearInterval(this.renew);\n const r = this.result;\n if (!r?.mapped) return;\n const work =\n r.method === \"upnp\"\n ? Promise.all([\"TCP\", \"UDP\"].map((p) => this.soap(\"DeletePortMapping\", [[\"NewRemoteHost\", \"\"], [\"NewExternalPort\", String(this.port)], [\"NewProtocol\", p]])))\n : Promise.all([1, 2].map((op) => this.natPmpOnce(this.mapRequest(op, 0))));\n await Promise.race([work.catch(() => {}), new Promise((r2) => setTimeout(r2, 1500).unref?.())]);\n }\n\n private natPmpOnce(req: Buffer): Promise<void> {\n return new Promise((resolve) => {\n const s = createSocket(\"udp4\");\n s.on(\"error\", () => {});\n s.send(req, this.natPmpAt!.port, this.natPmpAt!.address, () => {\n s.close();\n resolve();\n });\n });\n }\n}\n\n// ------------------------------------------------------------------------------------------------ helpers\n\n/**\n * True for an address the internet can route to: not private (RFC 1918), carrier-grade NAT (100.64/10), loopback,\n * link-local, 0/8, or multicast/reserved (224+). A router that reports a non-public WAN address is itself behind NAT.\n */\nexport function isPublicIPv4(address: string | null | undefined): boolean {\n if (!address) return false;\n const m = /^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$/.exec(address);\n if (!m) return false;\n const [a, b] = [Number(m[1]), Number(m[2])];\n if (m.slice(1).some((x) => Number(x) > 255)) return false;\n if (a === 0 || a === 10 || a === 127 || a >= 224) return false;\n if (a === 100 && b >= 64 && b < 128) return false;\n if (a === 169 && b === 254) return false;\n if (a === 172 && b >= 16 && b < 32) return false;\n if (a === 192 && b === 168) return false;\n return true;\n}\n\nfunction tag(xml: string, name: string): string | null {\n const m = new RegExp(`<(?:[A-Za-z0-9_]+:)?${name}(?:\\\\s[^>]*)?>([\\\\s\\\\S]*?)</(?:[A-Za-z0-9_]+:)?${name}>`).exec(xml);\n return m ? m[1] : null;\n}\n\nfunction xmlEscape(s: string): string {\n return s.replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\").replace(/\"/g, \"&quot;\").replace(/'/g, \"&apos;\");\n}\n\nfunction parseSsdp(text: string): URL | null {\n let location: string | null = null;\n let gateway = false;\n for (const line of text.split(/\\r?\\n/)) {\n const i = line.indexOf(\":\");\n if (i < 0) continue;\n const name = line.slice(0, i).trim().toLowerCase();\n const value = line.slice(i + 1).trim();\n if (name === \"location\") location = value;\n if ((name === \"st\" || name === \"usn\") && /InternetGatewayDevice|WANIPConnection/.test(value)) gateway = true;\n }\n if (!gateway || !location) return null;\n try {\n return new URL(location);\n } catch {\n return null;\n }\n}\n\n/**\n * Each IPv4 interface address, with its gateway when one can be guessed. Node's `os` does not report gateways, so the\n * gateway is the interface's .1 (true for nearly every home router); the SSDP multicast does not depend on it.\n */\nfunction gatewayInterfaces(): { local: string; gateway: string | null }[] {\n const out: { local: string; gateway: string | null }[] = [];\n for (const addrs of Object.values(networkInterfaces())) {\n for (const a of addrs ?? []) {\n if (a.family !== \"IPv4\" || a.internal || a.address.startsWith(\"169.254.\")) continue;\n const ip = a.address.split(\".\").map(Number);\n const mask = a.netmask.split(\".\").map(Number);\n const net = ip.map((x, i) => x & mask[i]);\n out.push({ local: a.address, gateway: mask[3] === 255 ? null : `${net[0]}.${net[1]}.${net[2]}.${net[3] + 1}` });\n }\n }\n return out;\n}\n\n/** The IPv4 default gateway from the OS route table (Windows `route print`, else `ip route`); null if it will not say. */\nfunction defaultGatewayFallback(): Promise<string | null> {\n return new Promise((resolve) => {\n const win = process.platform === \"win32\";\n execFile(win ? \"route\" : \"ip\", win ? [\"print\", \"0.0.0.0\"] : [\"route\", \"show\", \"default\"], { timeout: 2000 }, (err, stdout) => {\n if (err) return resolve(null);\n const m = win ? /0\\.0\\.0\\.0\\s+0\\.0\\.0\\.0\\s+(\\d+\\.\\d+\\.\\d+\\.\\d+)/.exec(stdout) : /default via (\\d+\\.\\d+\\.\\d+\\.\\d+)/.exec(stdout);\n resolve(m ? m[1] : null);\n });\n });\n}\n\n/** The local IPv4 the OS would use to reach `host` (a connected UDP socket sends nothing). */\nfunction localAddressToward(host: string, port: number): Promise<string> {\n return new Promise((resolve, reject) => {\n const s = createSocket(\"udp4\");\n s.on(\"error\", (e) => {\n s.close();\n reject(e);\n });\n s.connect(port, host, () => {\n const a = s.address().address;\n s.close();\n resolve(a);\n });\n });\n}\n\n/** The IPv4 a same-network join code carries when the router was not found (see C#'s DirectAddress.LanIPv4). */\nexport async function lanIPv4(): Promise<string | null> {\n try {\n const a = await localAddressToward(\"192.0.2.1\", 9); // TEST-NET-1: routed like the internet, never contacted\n if (a && a !== \"0.0.0.0\" && !a.startsWith(\"127.\")) return a;\n } catch {\n /* no default route: offline LAN party, look at the interfaces instead */\n }\n return gatewayInterfaces()[0]?.local ?? null;\n}\n\n/** This machine's addresses with the port, IPv4 first, for an \"Advanced\" fold-out on the host screen. */\nexport function localAddresses(port: number): string[] {\n const v4: string[] = [];\n const v6: string[] = [];\n for (const addrs of Object.values(networkInterfaces())) {\n for (const a of addrs ?? []) {\n if (a.internal) continue;\n if (a.family === \"IPv4\" && !a.address.startsWith(\"169.254.\")) v4.push(`${a.address}:${port}`);\n else if (a.family === \"IPv6\" && !/^fe80:/i.test(a.address) && !/^ff/i.test(a.address)) v6.push(`[${a.address}]:${port}`);\n }\n }\n return [...v4, ...v6];\n}\n"]}
1
+ {"version":3,"file":"portmap.js","sourceRoot":"","sources":["../../src/direct/portmap.ts"],"names":[],"mappings":"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","sourcesContent":["/// <reference types=\"node\" />\nimport { createSocket, type Socket as UdpSocket } from \"node:dgram\";\nimport { request as httpRequest } from \"node:http\";\nimport { networkInterfaces } from \"node:os\";\nimport { execFile } from \"node:child_process\";\n\nexport type PortMapMethod = \"none\" | \"upnp\" | \"natpmp\";\n\n/** What {@link PortMapper.open} found. `mapped` means TCP and UDP `port` both forward here. */\nexport interface PortMapResult {\n mapped: boolean;\n method: PortMapMethod;\n /** The router's WAN address as it reports it (may itself be private or carrier-grade NAT). */\n externalIPv4: string | null;\n /** This PC's address on the router's network, when the router was found. */\n localIPv4: string | null;\n /** Why nothing was mapped, for logs. */\n detail: string;\n}\n\n/** Seams for tests (a fake gateway on loopback) and the few knobs a game might want. Same as C#'s PortMapperOptions. */\nexport interface PortMapperOptions {\n /** Where the SSDP M-SEARCH goes; default the UPnP multicast group 239.255.255.250:1900. */\n ssdp?: { address: string; port: number };\n /** NAT-PMP server; default this PC's IPv4 default gateway, port 5351. */\n natPmp?: { address: string; port: number };\n discoveryTimeoutMs?: number;\n requestTimeoutMs?: number;\n /** Lease asked for; renewed at half of it while hosting. Routers that only allow permanent leases get 0. */\n leaseSeconds?: number;\n description?: string;\n tryUpnp?: boolean;\n tryNatPmp?: boolean;\n}\n\nconst SEARCH_TARGETS = [\n \"urn:schemas-upnp-org:device:InternetGatewayDevice:1\",\n \"urn:schemas-upnp-org:device:InternetGatewayDevice:2\",\n \"urn:schemas-upnp-org:service:WANIPConnection:1\",\n];\nconst SERVICE_TYPES = [\n \"urn:schemas-upnp-org:service:WANIPConnection:2\",\n \"urn:schemas-upnp-org:service:WANIPConnection:1\",\n \"urn:schemas-upnp-org:service:WANPPPConnection:1\",\n];\n\nclass UpnpError extends Error {\n constructor(readonly code: number, message: string) {\n super(message);\n }\n}\n\n/**\n * Opens TCP and UDP `port` on the home router for a direct-IP host (docs/direct-ip.md s.2a): UPnP IGD (SSDP, then\n * GetExternalIPAddress + AddPortMapping), then NAT-PMP (RFC 6886). Node twin of C#'s `PortMapper`, same order and rules:\n * same external and internal port or nothing (a join code carries one port), leases renew at half-life, {@link close}\n * deletes the mappings. Windows Firewall drops SSDP replies on a network marked Public; the result is then unmapped.\n */\nexport class PortMapper {\n result: PortMapResult | null = null;\n private readonly o: Required<Omit<PortMapperOptions, \"natPmp\">> & { natPmp?: { address: string; port: number } };\n private control: URL | null = null;\n private service = \"\";\n private lease = 0;\n private natPmpAt: { address: string; port: number } | null = null;\n private renew: ReturnType<typeof setInterval> | null = null;\n private stopped = false;\n private opening: Promise<PortMapResult> | null = null;\n /**\n * Every mapping a request was sent for and the router did not refuse (\"upnp TCP\", \"pmp 1\", ...): what close()\n * deletes. Added before the request goes out, so a host that quits mid-request still undoes it.\n */\n private readonly maybeMapped = new Set<string>();\n\n constructor(readonly port: number, options: PortMapperOptions = {}) {\n if (!Number.isInteger(port) || port < 1 || port > 65535) throw new RangeError(`bad port ${port}`);\n this.o = {\n ssdp: { address: \"239.255.255.250\", port: 1900 },\n discoveryTimeoutMs: 2000,\n requestTimeoutMs: 3000,\n leaseSeconds: 7200,\n description: \"OneLobby\",\n tryUpnp: true,\n tryNatPmp: true,\n ...options,\n };\n }\n\n /** Finds the router and maps TCP + UDP `port`. Never rejects for network trouble: see `detail`. */\n open(): Promise<PortMapResult> {\n return (this.opening = this.openBoth());\n }\n\n private async openBoth(): Promise<PortMapResult> {\n const notes: string[] = [];\n if (this.o.tryUpnp) {\n try {\n const r = await this.tryUpnp();\n if (r) return this.done(r);\n notes.push(\"no UPnP gateway answered\");\n } catch (e) {\n notes.push(`UPnP: ${(e as Error).message}`);\n }\n }\n if (this.o.tryNatPmp) {\n try {\n const r = await this.tryNatPmp();\n if (r) return this.done(r);\n notes.push(\"no NAT-PMP answer\");\n } catch (e) {\n notes.push(`NAT-PMP: ${(e as Error).message}`);\n }\n }\n return (this.result = { mapped: false, method: \"none\", externalIPv4: null, localIPv4: null, detail: notes.join(\"; \") });\n }\n\n private done(r: PortMapResult): PortMapResult {\n this.result = r;\n if (r.mapped && !this.stopped && (this.lease > 0 || r.method === \"natpmp\")) {\n const everyMs = Math.max(30, (this.lease > 0 ? this.lease : this.o.leaseSeconds) / 2) * 1000;\n this.renew = setInterval(() => {\n const p = r.method === \"upnp\" ? this.addUpnpBoth(r.localIPv4!, this.lease) : this.natPmpMapBoth(this.o.leaseSeconds);\n p.catch(() => {}); // the next round tries again; the old lease still has half its life\n }, everyMs);\n this.renew.unref?.();\n }\n return r;\n }\n\n // ------------------------------------------------------------------------------------------------ UPnP IGD\n\n private async tryUpnp(): Promise<PortMapResult | null> {\n const location = await this.discover();\n if (!location) return null;\n const desc = await this.http(\"GET\", location, null, {});\n const baseText = tag(desc.body, \"URLBase\");\n const base = baseText && /^https?:\\/\\//i.test(baseText) ? new URL(baseText) : location;\n for (const type of SERVICE_TYPES) {\n for (const svc of desc.body.match(/<service>[\\s\\S]*?<\\/service>/g) ?? []) {\n if ((tag(svc, \"serviceType\") ?? \"\").trim() !== type) continue;\n const ctl = tag(svc, \"controlURL\")?.trim();\n if (!ctl) continue;\n this.control = new URL(ctl, base);\n this.service = type;\n break;\n }\n if (this.control) break;\n }\n if (!this.control) return { mapped: false, method: \"upnp\", externalIPv4: null, localIPv4: null, detail: \"UPnP gateway has no WANIPConnection service\" };\n\n const local = await localAddressToward(this.control.hostname, Number(this.control.port) || 80);\n let external: string | null = null;\n try {\n external = tag((await this.soap(\"GetExternalIPAddress\", [])).body, \"NewExternalIPAddress\")?.trim() ?? null;\n } catch (e) {\n if (!(e instanceof UpnpError)) throw e; // some routers refuse this but still map\n }\n this.lease = this.o.leaseSeconds;\n try {\n await this.addUpnpBoth(local, this.lease);\n } catch (e) {\n if (!(e instanceof UpnpError)) throw e;\n if (e.code === 725) {\n // OnlyPermanentLeasesSupported\n this.lease = 0;\n await this.addUpnpBoth(local, 0);\n } else return { mapped: false, method: \"upnp\", externalIPv4: external, localIPv4: local, detail: `UPnP ${e.message}` };\n }\n return { mapped: true, method: \"upnp\", externalIPv4: external, localIPv4: local, detail: \"UPnP\" };\n }\n\n private async addUpnpBoth(local: string, lease: number): Promise<void> {\n for (const proto of [\"TCP\", \"UDP\"]) {\n if (this.stopped) throw new Error(\"closed before the router answered\");\n const key = `upnp ${proto}`;\n this.maybeMapped.add(key);\n try {\n await this.soap(\"AddPortMapping\", [\n [\"NewRemoteHost\", \"\"], [\"NewExternalPort\", String(this.port)], [\"NewProtocol\", proto], [\"NewInternalPort\", String(this.port)],\n [\"NewInternalClient\", local], [\"NewEnabled\", \"1\"], [\"NewPortMappingDescription\", this.o.description], [\"NewLeaseDuration\", String(lease)],\n ]);\n } catch (e) {\n // refused (e.g. 718: another device holds the port): not ours, so close() must not delete it\n if (e instanceof UpnpError) this.maybeMapped.delete(key);\n throw e;\n }\n // close() ran while this request was out: its delete may have reached the router first, so undo it now\n if (this.stopped) await this.undo([key]);\n }\n }\n\n /**\n * SSDP search: multicast once per interface that has a gateway (so a Hyper-V, WSL or Docker adapter cannot swallow\n * it), plus a unicast copy to the gateway.\n */\n private discover(): Promise<URL | null> {\n const multicast = Number(this.o.ssdp.address.split(\".\")[0]) >= 224;\n const locals = multicast ? gatewayInterfaces() : [];\n if (locals.length === 0) locals.push({ local: \"0.0.0.0\", gateway: null });\n return new Promise((resolve) => {\n const sockets: UdpSocket[] = [];\n let finished = false;\n const finish = (u: URL | null) => {\n if (finished) return;\n finished = true;\n clearTimeout(timer);\n for (const s of sockets) s.close();\n resolve(u);\n };\n const timer = setTimeout(() => finish(null), this.o.discoveryTimeoutMs);\n for (const { local, gateway } of locals) {\n const s = createSocket({ type: \"udp4\", reuseAddr: false });\n sockets.push(s);\n s.on(\"error\", () => {});\n s.on(\"message\", (m) => {\n const u = parseSsdp(m.toString(\"latin1\"));\n if (u) finish(u);\n });\n s.bind(0, local === \"0.0.0.0\" ? undefined : local, () => {\n try {\n s.setMulticastTTL(2);\n if (local !== \"0.0.0.0\") s.setMulticastInterface(local);\n } catch {\n /* not fatal: the OS default interface is used */\n }\n for (const st of SEARCH_TARGETS) {\n const b = Buffer.from(`M-SEARCH * HTTP/1.1\\r\\nHOST: 239.255.255.250:1900\\r\\nMAN: \"ssdp:discover\"\\r\\nMX: 1\\r\\nST: ${st}\\r\\n\\r\\n`);\n s.send(b, this.o.ssdp.port, this.o.ssdp.address, () => {});\n if (gateway) s.send(b, 1900, gateway, () => {});\n }\n });\n }\n });\n }\n\n private async soap(action: string, args: [string, string][]): Promise<{ status: number; body: string }> {\n const inner = args.map(([k, v]) => `<${k}>${xmlEscape(v)}</${k}>`).join(\"\");\n const body =\n `<?xml version=\"1.0\"?><s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">` +\n `<s:Body><u:${action} xmlns:u=\"${this.service}\">${inner}</u:${action}></s:Body></s:Envelope>`;\n const res = await this.http(\"POST\", this.control!, body, { \"Content-Type\": 'text/xml; charset=\"utf-8\"', SOAPAction: `\"${this.service}#${action}\"` });\n if (res.status < 200 || res.status >= 300) {\n const code = tag(res.body, \"errorCode\");\n const desc = tag(res.body, \"errorDescription\") ?? \"\";\n throw new UpnpError(Number(code) || 0, `${action} error ${code ?? res.status} ${desc}`.trim());\n }\n return res;\n }\n\n /**\n * One connection per request: some routers close after each reply without saying so, and a reused\n * keep-alive socket then fails every second request with \"socket hang up\".\n */\n private http(method: string, url: URL, body: string | null, headers: Record<string, string>): Promise<{ status: number; body: string }> {\n return new Promise((resolve, reject) => {\n const h = { ...headers, Connection: \"close\" };\n const req = httpRequest(\n { method, hostname: url.hostname, port: url.port || 80, path: url.pathname + url.search, timeout: this.o.requestTimeoutMs, agent: false,\n headers: body === null ? h : { ...h, \"Content-Length\": Buffer.byteLength(body) } },\n (res) => {\n let text = \"\";\n res.setEncoding(\"utf8\");\n res.on(\"data\", (d) => (text += d));\n res.on(\"end\", () => resolve({ status: res.statusCode ?? 0, body: text }));\n },\n );\n req.on(\"timeout\", () => req.destroy(new Error(`${method} ${url.pathname} timed out`)));\n req.on(\"error\", reject);\n if (body !== null) req.write(body);\n req.end();\n });\n }\n\n // ------------------------------------------------------------------------------------------------ NAT-PMP\n\n private async tryNatPmp(): Promise<PortMapResult | null> {\n // One gateway only: each silent one costs the full retransmission budget.\n let gw0 = this.o.natPmp ?? null;\n if (!gw0) {\n const routed = (await defaultGatewayFallback()) ?? gatewayInterfaces().find((g) => g.gateway)?.gateway ?? null;\n if (routed) gw0 = { address: routed, port: 5351 };\n }\n const candidates = gw0 ? [gw0] : [];\n for (const gw of candidates) {\n const ext = await this.natPmpRequest(gw, Buffer.from([0, 0]), 12);\n if (!ext) continue;\n this.natPmpAt = gw;\n const local = await localAddressToward(gw.address, gw.port);\n const external = ext.readUInt16BE(2) === 0 ? `${ext[8]}.${ext[9]}.${ext[10]}.${ext[11]}` : null;\n const err = await this.natPmpMapBoth(this.o.leaseSeconds);\n if (err) return { mapped: false, method: \"natpmp\", externalIPv4: external, localIPv4: local, detail: `NAT-PMP ${err}` };\n return { mapped: true, method: \"natpmp\", externalIPv4: external, localIPv4: local, detail: \"NAT-PMP\" };\n }\n return null;\n }\n\n /** Maps UDP then TCP; null on success, else why. A different external port counts as failure (and is undone). */\n private async natPmpMapBoth(lifetime: number): Promise<string | null> {\n for (const op of [1, 2]) {\n if (this.stopped) return \"closed before the router answered\";\n const key = `pmp ${op}`;\n this.maybeMapped.add(key);\n const reply = await this.natPmpRequest(this.natPmpAt!, this.mapRequest(op, lifetime), 16);\n if (this.stopped) await this.undo([key]);\n if (!reply) return \"no answer to the mapping request\";\n const rc = reply.readUInt16BE(2);\n if (rc !== 0) {\n this.maybeMapped.delete(key);\n return `result code ${rc}`;\n }\n const mapped = reply.readUInt16BE(10);\n if (lifetime > 0 && mapped !== this.port) {\n await this.natPmpRequest(this.natPmpAt!, this.mapRequest(op, 0), 16);\n this.maybeMapped.delete(key);\n return `router offered port ${mapped} instead of ${this.port}`;\n }\n }\n return null;\n }\n\n private mapRequest(op: number, lifetime: number): Buffer {\n const b = Buffer.alloc(12);\n b[1] = op;\n b.writeUInt16BE(this.port, 4);\n b.writeUInt16BE(lifetime > 0 ? this.port : 0, 6);\n b.writeUInt32BE(lifetime, 8);\n return b;\n }\n\n /** RFC 6886 s.3.1 retransmission: 250 ms, doubling, 3 tries (under 2 s). Null when nobody answers. */\n private natPmpRequest(to: { address: string; port: number }, req: Buffer, replyLength: number): Promise<Buffer | null> {\n return new Promise((resolve) => {\n const s = createSocket(\"udp4\");\n let wait = 250;\n let tries = 0;\n let timer: ReturnType<typeof setTimeout> | null = null;\n const finish = (b: Buffer | null) => {\n if (timer) clearTimeout(timer);\n s.close();\n resolve(b);\n };\n s.on(\"error\", () => {}); // ICMP port unreachable on Windows: keep waiting for the timer\n s.on(\"message\", (b) => {\n if (b.length >= replyLength && b[0] === 0 && b[1] === req[1] + 128) finish(b);\n });\n const attempt = () => {\n if (tries++ >= 3) return finish(null);\n s.send(req, to.port, to.address, () => {});\n timer = setTimeout(attempt, wait);\n wait *= 2;\n };\n s.bind(0, attempt);\n });\n }\n\n /**\n * Stops renewing and deletes every mapping it may have made, even while {@link open} is still talking to the router\n * (best effort, bounded to about two seconds; a request still out when it returns undoes itself when it lands).\n */\n async close(): Promise<void> {\n if (this.stopped) return;\n this.stopped = true;\n if (this.renew) clearInterval(this.renew);\n if (this.opening) await Promise.race([this.opening.catch(() => {}), delay(500)]);\n await this.undo([...this.maybeMapped]);\n }\n\n private async undo(keys: string[]): Promise<void> {\n if (keys.length === 0) return;\n const work = Promise.all(\n keys.map((k) => {\n this.maybeMapped.delete(k);\n return k.startsWith(\"upnp \")\n ? this.soap(\"DeletePortMapping\", [[\"NewRemoteHost\", \"\"], [\"NewExternalPort\", String(this.port)], [\"NewProtocol\", k.slice(5)]])\n : this.natPmpOnce(this.mapRequest(Number(k.slice(4)), 0));\n }),\n );\n await Promise.race([work.catch(() => {}), delay(1500)]);\n }\n\n private natPmpOnce(req: Buffer): Promise<void> {\n return new Promise((resolve) => {\n const s = createSocket(\"udp4\");\n s.on(\"error\", () => {});\n s.send(req, this.natPmpAt!.port, this.natPmpAt!.address, () => {\n s.close();\n resolve();\n });\n });\n }\n}\n\n// ------------------------------------------------------------------------------------------------ helpers\n\n/**\n * True for an address the internet can route to: not private (RFC 1918), carrier-grade NAT (100.64/10), loopback,\n * link-local, 0/8, or multicast/reserved (224+). A router that reports a non-public WAN address is itself behind NAT.\n */\nexport function isPublicIPv4(address: string | null | undefined): boolean {\n if (!address) return false;\n const m = /^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$/.exec(address);\n if (!m) return false;\n const [a, b] = [Number(m[1]), Number(m[2])];\n if (m.slice(1).some((x) => Number(x) > 255)) return false;\n if (a === 0 || a === 10 || a === 127 || a >= 224) return false;\n if (a === 100 && b >= 64 && b < 128) return false;\n if (a === 169 && b === 254) return false;\n if (a === 172 && b >= 16 && b < 32) return false;\n if (a === 192 && b === 168) return false;\n return true;\n}\n\nfunction tag(xml: string, name: string): string | null {\n const m = new RegExp(`<(?:[A-Za-z0-9_]+:)?${name}(?:\\\\s[^>]*)?>([\\\\s\\\\S]*?)</(?:[A-Za-z0-9_]+:)?${name}>`).exec(xml);\n return m ? m[1] : null;\n}\n\nfunction xmlEscape(s: string): string {\n return s.replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\").replace(/\"/g, \"&quot;\").replace(/'/g, \"&apos;\");\n}\n\nfunction parseSsdp(text: string): URL | null {\n let location: string | null = null;\n let gateway = false;\n for (const line of text.split(/\\r?\\n/)) {\n const i = line.indexOf(\":\");\n if (i < 0) continue;\n const name = line.slice(0, i).trim().toLowerCase();\n const value = line.slice(i + 1).trim();\n if (name === \"location\") location = value;\n if ((name === \"st\" || name === \"usn\") && /InternetGatewayDevice|WANIPConnection/.test(value)) gateway = true;\n }\n if (!gateway || !location) return null;\n try {\n return new URL(location);\n } catch {\n return null;\n }\n}\n\n/**\n * Each IPv4 interface address, with its gateway when one can be guessed. Node's `os` does not report gateways, so the\n * gateway is the interface's .1 (true for nearly every home router); the SSDP multicast does not depend on it.\n */\nfunction gatewayInterfaces(): { local: string; gateway: string | null }[] {\n const out: { local: string; gateway: string | null }[] = [];\n for (const addrs of Object.values(networkInterfaces())) {\n for (const a of addrs ?? []) {\n if (a.family !== \"IPv4\" || a.internal || a.address.startsWith(\"169.254.\")) continue;\n const ip = a.address.split(\".\").map(Number);\n const mask = a.netmask.split(\".\").map(Number);\n const net = ip.map((x, i) => x & mask[i]);\n out.push({ local: a.address, gateway: mask[3] === 255 ? null : `${net[0]}.${net[1]}.${net[2]}.${net[3] + 1}` });\n }\n }\n return out;\n}\n\n/** The IPv4 default gateway from the OS route table (Windows `route print`, else `ip route`); null if it will not say. */\nfunction defaultGatewayFallback(): Promise<string | null> {\n return new Promise((resolve) => {\n const win = process.platform === \"win32\";\n execFile(win ? \"route\" : \"ip\", win ? [\"print\", \"0.0.0.0\"] : [\"route\", \"show\", \"default\"], { timeout: 2000 }, (err, stdout) => {\n if (err) return resolve(null);\n const m = win ? /0\\.0\\.0\\.0\\s+0\\.0\\.0\\.0\\s+(\\d+\\.\\d+\\.\\d+\\.\\d+)/.exec(stdout) : /default via (\\d+\\.\\d+\\.\\d+\\.\\d+)/.exec(stdout);\n resolve(m ? m[1] : null);\n });\n });\n}\n\n/** The local IPv4 the OS would use to reach `host` (a connected UDP socket sends nothing). */\nfunction localAddressToward(host: string, port: number): Promise<string> {\n return new Promise((resolve, reject) => {\n const s = createSocket(\"udp4\");\n s.on(\"error\", (e) => {\n s.close();\n reject(e);\n });\n s.connect(port, host, () => {\n const a = s.address().address;\n s.close();\n resolve(a);\n });\n });\n}\n\n/** The IPv4 a same-network join code carries when the router was not found (see C#'s DirectAddress.LanIPv4). */\nexport async function lanIPv4(): Promise<string | null> {\n try {\n const a = await localAddressToward(\"192.0.2.1\", 9); // TEST-NET-1: routed like the internet, never contacted\n if (a && a !== \"0.0.0.0\" && !a.startsWith(\"127.\")) return a;\n } catch {\n /* no default route: offline LAN party, look at the interfaces instead */\n }\n return gatewayInterfaces()[0]?.local ?? null;\n}\n\n/** This machine's addresses with the port, IPv4 first, for an \"Advanced\" fold-out on the host screen. */\nexport function localAddresses(port: number): string[] {\n const v4: string[] = [];\n const v6: string[] = [];\n for (const addrs of Object.values(networkInterfaces())) {\n for (const a of addrs ?? []) {\n if (a.internal) continue;\n if (a.family === \"IPv4\" && !a.address.startsWith(\"169.254.\")) v4.push(`${a.address}:${port}`);\n else if (a.family === \"IPv6\" && !/^fe80:/i.test(a.address) && !/^ff/i.test(a.address)) v6.push(`[${a.address}]:${port}`);\n }\n }\n return [...v4, ...v6];\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((r) => setTimeout(r, ms).unref?.());\n}\n"]}
package/dist/lobby.d.ts CHANGED
@@ -114,6 +114,8 @@ export declare class Lobby extends Emitter<LobbyEvents> {
114
114
  private cfg;
115
115
  private roster;
116
116
  private links;
117
+ /** ICE from a peer that has no link yet: its candidates can overtake its offer (node-datachannel). */
118
+ private earlyIce;
117
119
  private connected;
118
120
  private hiSentTo;
119
121
  private epoch;