onelobby 0.2.0 → 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.
@@ -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
@@ -8,6 +8,20 @@ import type { ChannelName, IceServer, LinkFactory, PeerLink } from "./transport.
8
8
  import { Voice, type VoiceDeps } from "./voice.js";
9
9
  import { type LobbySocket, type LobbySocketOptions } from "./socket.js";
10
10
  export type Phase = "lobby" | "in_game";
11
+ /** The close code and reason a host chose with `kick()` / `disconnect()`, passed to the removed player's sockets. */
12
+ export interface CloseInfo {
13
+ code?: number;
14
+ reason?: string;
15
+ }
16
+ /** Options for `lobby.kick()`. */
17
+ export interface KickOptions {
18
+ /** Default true: the player's address cannot rejoin this lobby while it lasts. false = `disconnect()`. */
19
+ ban?: boolean;
20
+ /** Close code for the player's `asWebSocket()` onclose: 1000 or 3000-4999. */
21
+ code?: number;
22
+ /** Close reason for that onclose, at most 120 characters. */
23
+ reason?: string;
24
+ }
11
25
  export type Peer = PeerInfo;
12
26
  export type LobbyEvents = {
13
27
  peerJoined: [Peer];
@@ -15,12 +29,14 @@ export type LobbyEvents = {
15
29
  peerReady: [Peer];
16
30
  /** Joiner: the link to the host is open (the host itself is ready from the start). Also available as `lobby.ready`. */
17
31
  ready: [];
18
- peerLeft: [Peer, reason: string];
32
+ /** reason "left", "kicked", "disconnected" (kick with ban false), "timeout", ...; `close` when the host gave one. */
33
+ peerLeft: [Peer, reason: string, close?: CloseInfo];
19
34
  joinRequested: [Peer];
20
35
  hostChanged: [hostId: number];
21
36
  phase: [Phase];
22
37
  message: [from: number, bytes: Uint8Array, channel: ChannelName];
23
- sessionEnded: [reason: string];
38
+ /** `close` is the code and reason the host gave when it removed this player (`kick()` / `disconnect()`). */
39
+ sessionEnded: [reason: string, close?: CloseInfo];
24
40
  /** The signaling server went away. The game continues on the data channels (s.9). */
25
41
  serverLost: [];
26
42
  metaChanged: [Record<string, string>];
@@ -64,6 +80,8 @@ export interface RateLimit {
64
80
  }
65
81
  /** The smallest `peerTimeoutMs` honoured: three missed pings. */
66
82
  export declare const MIN_PEER_TIMEOUT_MS = 6000;
83
+ /** Host: how long a kicked / disconnected peer's link stays open for the hub's notice to reach it first. */
84
+ export declare const KICK_LINGER_MS = 3000;
67
85
  /** The name shown for a peer that chose none: "Player N", N = its seat (peer id, join order). */
68
86
  export declare function seatName(p: PeerInfo): PeerInfo;
69
87
  /** One joined lobby: roster, star of peer links, and the lockstep/chat/voice engines riding on them. */
@@ -96,6 +114,8 @@ export declare class Lobby extends Emitter<LobbyEvents> {
96
114
  private cfg;
97
115
  private roster;
98
116
  private links;
117
+ /** ICE from a peer that has no link yet: its candidates can overtake its offer (node-datachannel). */
118
+ private earlyIce;
99
119
  private connected;
100
120
  private hiSentTo;
101
121
  private epoch;
@@ -115,12 +135,14 @@ export declare class Lobby extends Emitter<LobbyEvents> {
115
135
  private relayRetry;
116
136
  /** clock.now() of the last frame from each peer (peerTimeoutMs). */
117
137
  private lastHeard;
138
+ /** Host: links of kicked / disconnected peers, kept open briefly (removePeer `linger`). */
139
+ private readonly lingering;
118
140
  /** Host: per-peer fixed window for rateLimit. */
119
141
  private rate;
120
142
  /**
121
143
  * Resolves when this peer can exchange data: immediately for the host, once the link to the host is open for a
122
144
  * joiner. Rejects with OneLobbyError("ice_failed") when the joiner cannot reach the host in `readyTimeoutMs`
123
- * (or with "closed"/"kicked" when the lobby ends first).
145
+ * (or with "closed"/"kicked" when the lobby ends first; "closed" also when the host disconnected it).
124
146
  */
125
147
  readonly ready: Promise<void>;
126
148
  constructor(cfg: LobbyConfig);
@@ -147,7 +169,18 @@ export declare class Lobby extends Emitter<LobbyEvents> {
147
169
  _begin(): void;
148
170
  start(): Promise<void>;
149
171
  setMeta(meta: Record<string, string>): Promise<void>;
150
- kick(peerId: number): Promise<void>;
172
+ /**
173
+ * Host: removes a player. By default it is a ban: the hub refuses that player's address for the rest of this lobby
174
+ * (a new tab or a reload included). `{ ban: false }` is `disconnect()`. `code` / `reason` reach the player's
175
+ * `asWebSocket()` onclose (and `sessionEnded`'s second argument); without them its socket closes with 4000 "kicked".
176
+ */
177
+ kick(peerId: number, opts?: KickOptions): Promise<void>;
178
+ /**
179
+ * Host: ends one player's connection without a ban, like a server closing one WebSocket. The player's
180
+ * `asWebSocket()` gets onclose with `code` (default 1000) and `reason` (default "disconnected"), its lobby ends with
181
+ * `sessionEnded("disconnected")`, and it may join again (a new tab, a reload). Same as `kick(peerId, { ban: false, code, reason })`.
182
+ */
183
+ disconnect(peerId: number, code?: number, reason?: string): Promise<void>;
151
184
  approve(peerId: number, ok: boolean): Promise<void>;
152
185
  leave(): Promise<void>;
153
186
  /** raw mode (and any extra game data): to one peer. */
@@ -188,7 +221,13 @@ export declare class Lobby extends Emitter<LobbyEvents> {
188
221
  private onHostClaim;
189
222
  private onSignaling;
190
223
  private onSignal;
224
+ /**
225
+ * `linger` (host kick / disconnect): stop using the peer's link at once but leave it open until that peer closes it
226
+ * or KICK_LINGER_MS pass. The hub tells the peer over its WebSocket; closing the data link first would make it look
227
+ * like the host vanished (a migration and a 1001 instead of the host's close code).
228
+ */
191
229
  private removePeer;
230
+ /** Lockstep drops (stalled, desynced): removed without a ban, so the player can come back (a rejoin resyncs). */
192
231
  private dropPeer;
193
232
  private setPhase;
194
233
  private onServerClosed;