* Test whether a port is free on a specific host. * * Attempts an ephemeral bind-and-release with `net.createServer()`. Only * `EADDRINUSE` means "genuinely occupied" — other errnos (EADDRNOTAVAIL when * IPv6 is disabled, EACCES for privileged ports, EAFNOSUPPORT for missing * address families)
(port: number, host: string)
| 40 | * the port as free for this host rather than poisoning the whole scan. |
| 41 | */ |
| 42 | async function isPortAvailableOnHost(port: number, host: string): Promise<boolean> { |
| 43 | const probe = net.createServer(); |
| 44 | probe.unref(); |
| 45 | |
| 46 | const bindError = await new Promise<NodeJS.ErrnoException | null>((settle) => { |
| 47 | const handleError = (err: NodeJS.ErrnoException): void => settle(err); |
| 48 | probe.once("error", handleError); |
| 49 | probe.listen({ port, host }, () => { |
| 50 | probe.removeListener("error", handleError); |
| 51 | settle(null); |
| 52 | }); |
| 53 | }); |
| 54 | |
| 55 | if (bindError !== null) { |
| 56 | return bindError.code !== "EADDRINUSE"; |
| 57 | } |
| 58 | |
| 59 | await new Promise<void>((done) => probe.close(() => done())); |
| 60 | return true; |
| 61 | } |
| 62 | |
| 63 | export const PORT_PROBE_HOSTS = ["127.0.0.1", "0.0.0.0", "::1", "::"] as const; |
| 64 |