using System; using System.Linq; using System.Net; using System.Threading; using System.Threading.Tasks; using SharpPcap; using PacketDotNet; namespace LanDiscovery.Services { public class PassiveDiscoveryResult { public required IPAddress Ip { get; set; } public required string MacAddress { get; set; } } public class ArpPassiveSniffer { private ICaptureDevice? _device; private Action? _onDiscovery; private bool _isCapturing; public bool IsSupported { get; private set; } = true; public string Status { get; private set; } = "Ready"; public void Start(string nicId, Action onDiscovery) { try { _onDiscovery = onDiscovery; var devices = CaptureDeviceList.Instance; if (devices.Count == 0) { Status = "No Capture Devices found (Npcap missing?)"; IsSupported = false; return; } _device = null; foreach(var dev in devices) { if (dev.Name.Contains(nicId) || dev.Description.Contains(nicId)) { _device = dev; break; } } if (_device == null) return; _device.OnPacketArrival += OnPacketArrival; _device.Open(DeviceModes.Promiscuous, 1000); _device.Filter = "arp"; _device.StartCapture(); _isCapturing = true; Status = "Running"; } catch (Exception ex) { IsSupported = false; Status = $"Failed: {ex.Message}"; } } public void StartByIp(string ipAddress, Action onDiscovery) { try { _onDiscovery = onDiscovery; var devices = CaptureDeviceList.Instance; foreach(var dev in devices) { // Check addresses by casting to LibPcapLiveDevice if possible if (dev is SharpPcap.LibPcap.LibPcapLiveDevice pcapDev) { foreach(var addr in pcapDev.Addresses) { if (addr.Addr != null && addr.Addr.ipAddress != null && addr.Addr.ipAddress.ToString() == ipAddress) { _device = dev; goto Found; } } } } Status = $"No Pcap device found for IP {ipAddress}"; return; Found: if (_device != null) { _device.OnPacketArrival += OnPacketArrival; _device.Open(DeviceModes.Promiscuous | DeviceModes.MaxResponsiveness); _device.Filter = "arp"; _device.StartCapture(); _isCapturing = true; Status = "Running"; } } catch (Exception ex) { IsSupported = false; Status = $"Capture Init Failed: {ex.Message}"; } } private void OnPacketArrival(object sender, PacketCapture e) { try { var packet = Packet.ParsePacket(e.GetPacket().LinkLayerType, e.GetPacket().Data); var arpPacket = packet.Extract(); if (arpPacket != null) { var senderIp = arpPacket.SenderProtocolAddress; var senderMac = arpPacket.SenderHardwareAddress; if (senderIp != null && senderMac != null) { _onDiscovery?.Invoke(new PassiveDiscoveryResult { Ip = senderIp, MacAddress = senderMac.ToString() }); } } } catch {} } public void Stop() { if (_isCapturing && _device != null) { try { _device.StopCapture(); _device.Close(); } catch {} _isCapturing = false; Status = "Stopped"; } } } }