Mirror: Best-effort discovery of the machine's local network using just Node.js dgram sockets
at v0.1.1 2.4 kB view raw
1import { networkInterfaces } from 'node:os'; 2import type { GatewayAssignment, NetworkAssignment } from './types'; 3 4export const parseMacStr = (macStr: string): number[] => 5 macStr 6 .split(':') 7 .slice(0, 16) 8 .map(seq => parseInt(seq, 16)); 9 10export const parseIpStr = (ipStr: string): number => { 11 const addr = ipStr 12 .split('.') 13 .slice(0, 4) 14 .map(seq => parseInt(seq, 10)); 15 return addr[3] | (addr[2] << 8) | (addr[1] << 16) | (addr[0] << 24); 16}; 17 18export const toIpStr = (addr: number): string => { 19 const MASK = (1 << 8) - 1; 20 let ipStr = ''; 21 ipStr += `${((addr >>> 24) & MASK).toString(10)}.`; 22 ipStr += `${((addr >>> 16) & MASK).toString(10)}.`; 23 ipStr += `${((addr >>> 8) & MASK).toString(10)}.`; 24 ipStr += (addr & MASK).toString(10); 25 return ipStr; 26}; 27 28const getSubnetPriority = (addr: string): number => { 29 if (addr.startsWith('192.')) return 5; 30 else if (addr.startsWith('172.')) return 4; 31 else if (addr.startsWith('10.')) return 3; 32 else if (addr.startsWith('100.')) return 2; 33 else if (addr.startsWith('127.')) return 1; 34 else return 0; 35}; 36 37export const interfaceAssignments = (): NetworkAssignment[] => { 38 const candidates: NetworkAssignment[] = []; 39 const interfaces = networkInterfaces(); 40 for (const iname in interfaces) { 41 const assignments = interfaces[iname]; 42 if (!assignments) continue; 43 for (const assignment of assignments) { 44 if (assignment.family !== 'IPv4') continue; 45 candidates.push({ ...assignment, iname }); 46 } 47 } 48 return candidates.sort((a, b) => { 49 const priorityA = getSubnetPriority(a.address); 50 const priorityB = getSubnetPriority(b.address); 51 // Prioritise external interfaces, then sort by priority, 52 // when priority is equal, sort by raw IP values 53 const sortBy = 54 +a.internal - +b.internal || 55 priorityB - priorityA || 56 parseIpStr(b.address) - parseIpStr(a.address); 57 return sortBy; 58 }); 59}; 60 61export const matchAssignment = ( 62 candidates: NetworkAssignment[], 63 addr: string 64): GatewayAssignment | null => { 65 const rawAddr = parseIpStr(addr); 66 for (const candidate of candidates) { 67 const candidateAddr = parseIpStr(candidate.address); 68 if (rawAddr === candidateAddr) return { ...candidate, gateway: null }; 69 const mask = parseIpStr(candidate.netmask); 70 if ((rawAddr & mask) === (candidateAddr & mask)) 71 return { ...candidate, gateway: addr }; 72 } 73 return null; 74};