// // PcapNostaleManager.cs // // Copyright (c) František Boháček. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Collections.Concurrent; using System.Diagnostics; using System.Net; using SharpPcap; using SharpPcap.LibPcap; namespace NosSmooth.Pcap; /// /// Captures packets, distributes them to Pcap clients. /// public class PcapNostaleManager { private readonly ConcurrentDictionary _connections; private readonly ConcurrentDictionary _clients; private int _clientsCount; private bool _started; /// /// Initializes a new instance of the class. /// public PcapNostaleManager() { _connections = new ConcurrentDictionary(); _clients = new ConcurrentDictionary(); } /// /// Add a pcap client. /// internal void AddClient() { var count = Interlocked.Increment(ref _clientsCount); if (count == 1) { StartCapturing(); } } /// /// Remove a pcap client. /// /// /// When no clients are left, packet capture will be stopped. /// internal void RemoveClient() { var count = Interlocked.Decrement(ref _clientsCount); if (count == 0) { Stop(); } } /// /// Associate the given connection with the given client. /// /// The connection to associate. /// The client to associate the connection with. internal void RegisterConnection(TcpConnection connection, PcapNostaleClient client) { _clients.AddOrUpdate(connection, (c) => client, (c1, c2) => client); if (_connections.TryGetValue(connection, out var data)) { foreach (var sniffedPacket in data.SniffedData) { client.OnPacketArrival(connection, sniffedPacket); } } } /// /// Disassociate the given connection. /// /// The connection to disassociate. internal void UnregisterConnection(TcpConnection connection) { _clients.TryRemove(connection, out _); } private void Stop() { if (!_started) { return; } _started = false; foreach (var device in LibPcapLiveDeviceList.Instance) { device.StopCapture(); } } /// /// Start capturing packets from all devices. /// public void StartCapturing() { if (_started) { return; } _started = true; foreach (var device in LibPcapLiveDeviceList.Instance) { if (!device.Opened) { device.Open(); } device.Filter = "ip and tcp"; device.OnPacketArrival += DeviceOnOnPacketArrival; device.StartCapture(); } } private void DeviceOnOnPacketArrival(object sender, PacketCapture e) { var rawPacket = e.GetPacket(); var packet = PacketDotNet.Packet.ParsePacket(rawPacket.LinkLayerType, rawPacket.Data); var tcpPacket = packet.Extract(); if (tcpPacket is null) { return; } if (!tcpPacket.HasPayloadData || tcpPacket.PayloadData.Length == 0 || tcpPacket.PayloadData.Length > 500) { return; } var ipPacket = (PacketDotNet.IPPacket)tcpPacket.ParentPacket; System.Net.IPAddress srcIp = ipPacket.SourceAddress; System.Net.IPAddress dstIp = ipPacket.DestinationAddress; int srcPort = tcpPacket.SourcePort; int dstPort = tcpPacket.DestinationPort; var tcpConnection = new TcpConnection(srcIp.Address, srcPort, dstIp.Address, dstPort); if (!_connections.ContainsKey(tcpConnection)) { _connections.TryAdd ( tcpConnection, new ConnectionData ( srcIp, srcPort, dstIp, dstPort, new List(), DateTimeOffset.Now ) ); } var data = _connections[tcpConnection]; if (data.SniffedData.Count < 5) { data.SniffedData.Add(tcpPacket.PayloadData); } // TODO: clean up the sniffed data in case they are not needed. if (_clients.TryGetValue(tcpConnection, out var client)) { client.OnPacketArrival(tcpConnection, tcpPacket.PayloadData); } } }