Mirror: Best-effort discovery of the machine's local network using just Node.js dgram sockets
1#!/usr/bin/env node 2 3import type { GatewayAssignment } from './types'; 4import { 5 DEFAULT_ASSIGNMENT, 6 interfaceAssignments, 7 matchAssignment, 8} from './network'; 9import { probeDefaultRoute } from './route'; 10import { dhcpDiscover } from './dhcp'; 11import { lanNetwork } from './index'; 12 13type Mode = 'help' | 'dhcp' | 'probe' | 'fallback' | 'default'; 14 15function help() { 16 const output = [ 17 "Discover the machine's default gateway and local network IP (test utility)", 18 '', 19 'Usage', 20 ' $ lan-network', 21 ' $ lan-network --default', 22 '', 23 'Modes', 24 ' --probe Discover gateway via UDP4 socket to publicly routed address', 25 ' --dhcp Discover gateway via DHCPv4 discover broadcast', 26 ' --fallback Return highest-priority IPv4 network interface assignment', 27 ' --default Try the three above modes in order', 28 ' --help Print help output', 29 ].join('\n'); 30 console.log(output); 31} 32 33async function dhcp() { 34 const assignments = interfaceAssignments(); 35 if (!assignments.length) { 36 console.error('No available network interface assignments'); 37 process.exit(1); 38 } 39 const discoveries = await Promise.allSettled( 40 assignments.map(assignment => { 41 // For each assignment, we send a DHCPDISCOVER packet to its network mask 42 return dhcpDiscover(assignment); 43 }) 44 ); 45 console.log(discoveries); 46 let assignment: GatewayAssignment | null = null; 47 for (const discovery of discoveries) { 48 // The first discovered gateway is returned, if it matches an assignment 49 if (discovery.status === 'fulfilled' && discovery.value) { 50 const dhcpRoute = discovery.value; 51 if ((assignment = matchAssignment(assignments, dhcpRoute))) { 52 break; 53 } 54 } 55 } 56 if (assignment && assignment !== DEFAULT_ASSIGNMENT) { 57 console.log(JSON.stringify(assignment, null, 2)); 58 process.exit(0); 59 } else { 60 console.error('No DHCP router was discoverable'); 61 process.exit(1); 62 } 63} 64 65async function probe() { 66 const assignments = interfaceAssignments(); 67 if (!assignments.length) { 68 console.error('No available network interface assignments'); 69 process.exit(1); 70 } 71 try { 72 const defaultRoute = await probeDefaultRoute(); 73 const assignment = matchAssignment(assignments, defaultRoute); 74 if (assignment && assignment !== DEFAULT_ASSIGNMENT) { 75 console.log(JSON.stringify(assignment, null, 2)); 76 process.exit(0); 77 } else { 78 console.error('No default gateway or route'); 79 process.exit(1); 80 } 81 } catch (error) { 82 console.error('No default gateway or route'); 83 console.error(error); 84 process.exit(1); 85 } 86} 87 88async function fallback() { 89 const assignments = interfaceAssignments(); 90 if (!assignments.length) { 91 console.error('No available network interface assignments'); 92 process.exit(1); 93 } 94 const assignment = { ...assignments[0], gateway: null }; 95 console.log(JSON.stringify(assignment, null, 2)); 96 process.exit(0); 97} 98 99async function main() { 100 const assignment = await lanNetwork(); 101 if (assignment !== DEFAULT_ASSIGNMENT) { 102 console.log(JSON.stringify(assignment, null, 2)); 103 process.exit(0); 104 } else { 105 console.error('No default gateway, route, or DHCP router'); 106 process.exit(1); 107 } 108} 109 110function cli() { 111 let mode: Mode = 'default'; 112 parseArgs: for (let i = 1; i < process.argv.length; i++) { 113 const arg = process.argv[i].trim().toLowerCase(); 114 switch (arg) { 115 case '-h': 116 case '--help': 117 mode = 'help'; 118 break parseArgs; 119 case '-d': 120 case '--dhcp': 121 mode = 'dhcp'; 122 break; 123 case '-p': 124 case '--probe': 125 mode = 'probe'; 126 break; 127 case '-f': 128 case '--fallback': 129 mode = 'fallback'; 130 break; 131 default: 132 if (arg.startsWith('-')) throw new TypeError(`Invalid flag: ${arg}`); 133 } 134 } 135 switch (mode) { 136 case 'help': 137 return help(); 138 case 'dhcp': 139 return dhcp(); 140 case 'probe': 141 return probe(); 142 case 'fallback': 143 return fallback(); 144 case 'default': 145 return main(); 146 } 147} 148 149cli();