62 lines
2.0 KiB
TypeScript
62 lines
2.0 KiB
TypeScript
import { exec } from 'node:child_process';
|
|
import { promisify } from 'node:util';
|
|
import { env } from '@/config/env';
|
|
import type { DiscoveryResult, DiscoveryTarget, Scanner } from '../types';
|
|
|
|
const execAsync = promisify(exec);
|
|
|
|
/**
|
|
* Lit la table ARP locale via `ip neigh` (Linux) ou `arp -a` (fallback).
|
|
* Ne scanne pas activement : retourne ce que le kernel a déjà vu.
|
|
* Pour forcer la résolution, il faut ping-sweeper d'abord (voir discovery.service).
|
|
*/
|
|
export const arpScanner: Scanner = {
|
|
kind: 'arp',
|
|
label: 'Table ARP locale',
|
|
enabled: env.DISCOVERY_ENABLE_ARP,
|
|
|
|
async run(_target: DiscoveryTarget): Promise<DiscoveryResult[]> {
|
|
const { stdout } = await execAsync('ip neigh show').catch(() => ({ stdout: '' }));
|
|
if (stdout) return parseIpNeigh(stdout);
|
|
|
|
const { stdout: arpOut } = await execAsync('arp -a').catch(() => ({ stdout: '' }));
|
|
return parseArpA(arpOut);
|
|
},
|
|
};
|
|
|
|
function parseIpNeigh(output: string): DiscoveryResult[] {
|
|
// Exemple : 192.168.1.10 dev eth0 lladdr aa:bb:cc:dd:ee:ff REACHABLE
|
|
return output
|
|
.split('\n')
|
|
.map((line) => line.trim())
|
|
.filter(Boolean)
|
|
.map((line) => {
|
|
const ipMatch = line.match(/^(\d{1,3}(?:\.\d{1,3}){3})/);
|
|
const macMatch = line.match(/lladdr\s+([0-9a-fA-F:]{17})/);
|
|
if (!ipMatch) return null;
|
|
return {
|
|
ipAddress: ipMatch[1],
|
|
macAddress: macMatch?.[1],
|
|
} satisfies DiscoveryResult;
|
|
})
|
|
.filter((r): r is DiscoveryResult => r !== null);
|
|
}
|
|
|
|
function parseArpA(output: string): DiscoveryResult[] {
|
|
// Exemple : ? (192.168.1.10) at aa:bb:cc:dd:ee:ff [ether] on eth0
|
|
return output
|
|
.split('\n')
|
|
.map((line) => line.trim())
|
|
.filter(Boolean)
|
|
.map((line) => {
|
|
const ipMatch = line.match(/\((\d{1,3}(?:\.\d{1,3}){3})\)/);
|
|
const macMatch = line.match(/([0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5})/);
|
|
if (!ipMatch) return null;
|
|
return {
|
|
ipAddress: ipMatch[1],
|
|
macAddress: macMatch?.[1],
|
|
} satisfies DiscoveryResult;
|
|
})
|
|
.filter((r): r is DiscoveryResult => r !== null);
|
|
}
|