UNPKG

@achingbrain/nat-port-mapper

Version:
95 lines 3.37 kB
import { networkInterfaces } from 'node:os'; import ssdp from '@achingbrain/ssdp'; import { isIPv6 } from '@chainsafe/is-ip'; import { logger, enabled } from '@libp2p/logger'; import { DEVICE_INTERNET_GATEWAY_SERVICE_2 } from './constants.js'; const log = logger('nat-port-mapper:discovery'); function weAreSender(sender) { const addresses = []; // only calculate if trace logging is enabled if (enabled('nat-port-mapper:discovery:trace')) { const interfaces = networkInterfaces(); Object.entries(interfaces).forEach(([name, addrs]) => { addrs?.forEach(addr => { addresses.push(addr.address); }); }); } return addresses.includes(sender); } function getSockets() { const sockets = []; for (const interfaces of Object.values(networkInterfaces())) { if (interfaces == null) { continue; } interfaces.forEach(iface => { if (iface.internal) { // skip loopback addresses return; } if (iface.address.startsWith('169.254.') || iface.address.startsWith('fe80')) { // skip link local-local addresses // https://en.wikipedia.org/wiki/Link-local_address return; } sockets.push({ type: iface.family === 'IPv4' ? 'udp4' : 'udp6', bind: { address: iface.address, port: 1900 } }); }); } return sockets; } export async function* discoverGateways(options) { let discovery; try { discovery = await ssdp({ cache: false, sockets: getSockets() }); discovery.on('transport:outgoing-message', (socket, message, remote) => { log.trace('-> Outgoing to %s:%s via %s', isIPv6(remote.address) ? `[${remote.address}]` : remote.address, remote.port, socket.type); log.trace('%s', message); }); discovery.on('transport:incoming-message', (message, remote) => { if (weAreSender(remote.address)) { return; } log.trace('<- Incoming from %s:%s', isIPv6(remote.address) ? `[${remote.address}]` : remote.address, remote.port); log.trace('%s', message); }); discovery.on('error', (err) => { log.error('SSDP error - %e', err); }); log('searching for gateways'); const services = new Set(); for await (const service of discovery.discover({ ...options, serviceType: DEVICE_INTERNET_GATEWAY_SERVICE_2 })) { if (service.serviceType !== DEVICE_INTERNET_GATEWAY_SERVICE_2) { continue; } const location = service.location.toString(); if (services.has(location)) { continue; } services.add(location); log('discovered UPnP2 gateway %s %s', service.location, service.uniqueServiceName); yield service; } } catch (err) { if (options?.signal?.aborted !== true) { log.error('error during service discovery - %e', err); } } finally { await discovery?.stop(); } } //# sourceMappingURL=discovery.js.map