diff --git a/lib/utils/discover.ts b/lib/utils/discover.ts index 76945582..813a5cf4 100644 --- a/lib/utils/discover.ts +++ b/lib/utils/discover.ts @@ -1,5 +1,6 @@ import Bonjour from 'bonjour-service'; import type { Service } from 'bonjour-service'; +import * as os from 'os'; interface LocalBalenaOsDevice { address: string; @@ -19,20 +20,25 @@ const avahiBalenaSshSubtype = 'resin-device'; export async function discoverLocalBalenaOsDevices( timeout = 4000, ): Promise { - const services = await new Promise((resolve) => { - const bonjour = new Bonjour({}, async (err: string | Error) => { - await (await import('../errors')).handleError(err); - }); - const resinSshServices: Service[] = []; - const browser = bonjour.find(avahiBalenaSshConfig, (service) => - resinSshServices.push(service), - ); - setTimeout(() => { - browser.stop(); - bonjour.destroy(); - resolve(resinSshServices); - }, timeout); - }); + // search over all network interfaces + const networks = os.networkInterfaces(); + const validNics: os.NetworkInterfaceInfo[] = []; + for (const networkName of Object.keys(networks)) { + for (const iface of networks[networkName]!) { + if (isIPv4(iface.family) && !iface.internal) { + validNics.push(iface); + } + } + } + + const allServices = await Promise.all( + validNics.map((iface) => searchBalenaDevicesOnInterface(iface, timeout)), + ); + + // dedupe services in case the same device is found on multiple interfaces + const services = Array.from( + new Map(allServices.flat().map((item) => [item.fqdn, item])).values(), + ); return services .filter( @@ -46,3 +52,36 @@ export async function discoverLocalBalenaOsDevices( port, })); } + +async function searchBalenaDevicesOnInterface( + iface: os.NetworkInterfaceInfo, + timeout: number, +): Promise { + return await new Promise((resolve) => { + const bonjour = new Bonjour( + { + // @ts-expect-error bonjour-service types are incorrect https://github.com/onlxltd/bonjour-service/issues/10 + interface: iface.address, + // binds to receive from any incoming interface + // see: https://github.com/mafintosh/multicast-dns/issues/53#issuecomment-638365104 + bind: '0.0.0.0', + }, + async (err: string | Error) => { + await (await import('../errors')).handleError(err); + }, + ); + const resinSshServices: Service[] = []; + const browser = bonjour.find(avahiBalenaSshConfig, (service) => + resinSshServices.push(service), + ); + setTimeout(() => { + browser.stop(); + bonjour.destroy(); + resolve(resinSshServices); + }, timeout); + }); +} + +function isIPv4(family: string | number) { + return family === 4 || family === 'IPv4'; +} diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 58646faa..fee88c9d 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -4257,9 +4257,9 @@ } }, "node_modules/@types/node": { - "version": "20.14.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz", - "integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==", + "version": "20.14.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.11.tgz", + "integrity": "sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==", "dependencies": { "undici-types": "~5.26.4" } @@ -5780,10 +5780,9 @@ } }, "node_modules/balena-sdk/node_modules/@types/node": { - "version": "18.19.39", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.39.tgz", - "integrity": "sha512-nPwTRDKUctxw3di5b4TfT3I0sWDiWoPQCZjXhvdkINntwr8lcoVCKsTgnXeRubKIlfnV+eN/HYk6Jb40tbcEAQ==", - "license": "MIT", + "version": "18.19.40", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.40.tgz", + "integrity": "sha512-MIxieZHrm4Ee8XArBIc+Or9HINt2StOmCbgRcXGSJl8q14svRvkZPe7LJq9HKtTI1SK3wU8b91TjntUm7T69Pg==", "dependencies": { "undici-types": "~5.26.4" }