first commit
This commit is contained in:
61
src/modules/discovery/scanners/arp.scanner.ts
Normal file
61
src/modules/discovery/scanners/arp.scanner.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
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);
|
||||
}
|
||||
Reference in New Issue
Block a user