Mirror: Best-effort discovery of the machine's local network using just Node.js dgram sockets

Add `noProbe` and `noDhcp` configuration options (#18)

authored by kitten.sh and committed by

GitHub 8504d8bb 8fc50d7a

+49 -26
+5
.changeset/long-berries-clean.md
··· 1 + --- 2 + 'lan-network': minor 3 + --- 4 + 5 + Add `noProbe` and `noDhcp` options
+40 -25
src/index.ts
··· 9 9 } from './network'; 10 10 import type { GatewayAssignment } from './types'; 11 11 12 - export async function lanNetwork(): Promise<GatewayAssignment> { 12 + export interface NetworkOptions { 13 + noProbe?: boolean; 14 + noDhcp?: boolean; 15 + } 16 + 17 + export async function lanNetwork( 18 + opts?: NetworkOptions 19 + ): Promise<GatewayAssignment> { 13 20 // Get IPv4 network assignments, sorted by: 14 21 // - external first 15 22 // - LAN-reserved IP range priority ··· 24 31 25 32 // First, we attempt to probe the default route to a publicly routed IP 26 33 // This will generally fail if there's no route, e.g. if the network is offline 27 - try { 28 - const defaultRoute = await probeDefaultRoute(); 29 - // If this route matches a known assignment, return it without a gateway 30 - if ( 31 - (assignment = matchAssignment(assignments, defaultRoute)) && 32 - !isInternal(assignment) 33 - ) { 34 - return assignment; 34 + if (!opts?.noProbe) { 35 + try { 36 + const defaultRoute = await probeDefaultRoute(); 37 + // If this route matches a known assignment, return it without a gateway 38 + if ( 39 + (assignment = matchAssignment(assignments, defaultRoute)) && 40 + !isInternal(assignment) 41 + ) { 42 + return assignment; 43 + } 44 + } catch { 45 + // Ignore errors, since we have a fallback method 35 46 } 36 - } catch { 37 - // Ignore errors, since we have a fallback method 38 47 } 39 48 40 49 // Second, attempt to discover a gateway's DHCP network 41 50 // Because without a gateway we won't get a reply, we do this in parallel 42 - const discoveries = await Promise.allSettled( 43 - assignments.map(assignment => { 44 - // For each assignment, we send a DHCPDISCOVER packet to its network mask 45 - return dhcpDiscover(assignment); 46 - }) 47 - ); 48 - for (const discovery of discoveries) { 49 - // The first discovered gateway is returned, if it matches an assignment 50 - if (discovery.status === 'fulfilled' && discovery.value) { 51 - const dhcpRoute = discovery.value; 52 - if ((assignment = matchAssignment(assignments, dhcpRoute))) { 53 - return assignment; 51 + if (!opts?.noDhcp) { 52 + const discoveries = await Promise.allSettled( 53 + assignments.map(assignment => { 54 + // For each assignment, we send a DHCPDISCOVER packet to its network mask 55 + return dhcpDiscover(assignment); 56 + }) 57 + ); 58 + for (const discovery of discoveries) { 59 + // The first discovered gateway is returned, if it matches an assignment 60 + if (discovery.status === 'fulfilled' && discovery.value) { 61 + const dhcpRoute = discovery.value; 62 + if ((assignment = matchAssignment(assignments, dhcpRoute))) { 63 + return assignment; 64 + } 54 65 } 55 66 } 56 67 } ··· 60 71 return { ...assignments[0], gateway: null }; 61 72 } 62 73 63 - export function lanNetworkSync(): GatewayAssignment { 74 + export function lanNetworkSync(opts?: NetworkOptions): GatewayAssignment { 64 75 const subprocessPath = require.resolve('lan-network/subprocess'); 65 76 const { error, status, stdout } = spawnSync( 66 77 process.execPath, 67 - [subprocessPath], 78 + [ 79 + subprocessPath, 80 + opts?.noProbe ? '--no-probe' : null, 81 + opts?.noDhcp ? '--no-dhcp' : null, 82 + ].filter((x): x is string => !!x), 68 83 { 69 84 shell: false, 70 85 timeout: 500,
+4 -1
src/subprocess.ts
··· 1 1 import { lanNetwork } from './index'; 2 2 3 3 async function output() { 4 - const assignment = await lanNetwork(); 4 + const assignment = await lanNetwork({ 5 + noProbe: process.argv.includes('--no-probe'), 6 + noDhcp: process.argv.includes('--no-dhcp'), 7 + }); 5 8 process.stdout.write(JSON.stringify(assignment)); 6 9 process.exit(0); 7 10 }