diff --git a/src/core/sniffer.cpp b/src/core/sniffer.cpp new file mode 100644 index 0000000..ba4f3fe --- /dev/null +++ b/src/core/sniffer.cpp @@ -0,0 +1,135 @@ +#include "sniffer.hpp" +#include "logger.hpp" +#include + +Sniffer::Sniffer(std::string localIp, bool maskIp) + : ip_(std::move(localIp)) + , mask_(maskIp) +{ +} + +int Sniffer::run() +{ + const auto ifaceName = resolveIfaceNameByIp(ip_); + + Tins::SnifferConfiguration cfg; + cfg.set_promisc_mode(true); + cfg.set_immediate_mode(true); + + Tins::Sniffer sniffer(ifaceName, cfg); + + auto running = [this] { return !stop_.load(std::memory_order_relaxed); }; + + auto handler = [&](Tins::PDU &pdu) -> bool { + if (!running()) + return false; + + std::string src, dst; + const char *type = "OTHER"; + uint16_t sport = 0, dport = 0; + + if (auto *arp = pdu.find_pdu()) { + src = arp->sender_ip_addr().to_string(); + dst = arp->target_ip_addr().to_string(); + type = "ARP"; + } else if (auto *ip4 = pdu.find_pdu()) { + src = ip4->src_addr().to_string(); + dst = ip4->dst_addr().to_string(); + + if (auto *tcp = pdu.find_pdu()) { + type = "TCP"; + sport = tcp->sport(); + dport = tcp->dport(); + } else if (auto *udp = pdu.find_pdu()) { + type = "UDP"; + sport = udp->sport(); + dport = udp->dport(); + } else if (pdu.find_pdu()) { + type = "ICMP"; + } else { + type = "IP"; + } + } else if (auto *ip6 = pdu.find_pdu()) { + src = ip6->src_addr().to_string(); + dst = ip6->dst_addr().to_string(); + + if (auto *tcp = pdu.find_pdu()) { + type = "TCP"; + sport = tcp->sport(); + dport = tcp->dport(); + } else if (auto *udp = pdu.find_pdu()) { + type = "UDP"; + sport = udp->sport(); + dport = udp->dport(); + } else if (pdu.find_pdu()) { + type = "ICMPv6"; + } else { + type = "IPv6"; + } + } else { + Logger::info("(unknown src) -> (unknown dst) [OTHER]"); + return running(); + } + + if (mask_) { + src = hideHalf(src); + dst = hideHalf(dst); + } + + printLine(src, dst, type, sport, dport); + return running(); + }; + + sniffer.sniff_loop(handler); + return 0; +} + +void Sniffer::stop() +{ + stop_.store(true, std::memory_order_relaxed); +} + +std::string Sniffer::hideHalf(const std::string &s) const +{ + if (s.empty()) { + return s; + } + + const size_t n = s.size(); + const size_t hide = n / 2; + const size_t keep = n - hide; + std::string out = s.substr(0, keep); + out.append(hide, '*'); + + return out; +} + +std::string Sniffer::resolveIfaceNameByIp(const std::string &ip) +{ + const Tins::IPv4Address want(ip); + for (const auto &iface : Tins::NetworkInterface::all()) { + const auto info = iface.addresses(); // .ip_addr/.netmask/... + if (info.ip_addr == want) { + return iface.name(); + } + } + throw std::runtime_error("Interface with IP " + ip + " not found"); +} + +void Sniffer::printLine( + const std::string &src, + const std::string &dst, + const char *type, + uint16_t sport, + uint16_t dport +) +{ + if (sport || dport) { + Logger::info( + std::string(src) + ":" + std::to_string(sport) + " -> " + dst + ":" + + std::to_string(dport) + " [" + type + "]" + ); + } else { + Logger::info(std::string(src) + " -> " + dst + " [" + type + "]"); + } +} diff --git a/src/core/sniffer.hpp b/src/core/sniffer.hpp new file mode 100644 index 0000000..a03ebf2 --- /dev/null +++ b/src/core/sniffer.hpp @@ -0,0 +1,32 @@ +#pragma once +#include +#include +#include + +class Sniffer +{ + public: + explicit Sniffer(std::string localIp, bool maskIp = false); + + Sniffer(const Sniffer &) = delete; + Sniffer &operator=(const Sniffer &) = delete; + + std::string hideHalf(const std::string &s) const; + + int run(); + void stop(); + + private: + static std::string resolveIfaceNameByIp(const std::string &ip); + static void printLine( + const std::string &src, + const std::string &dst, + const char *type, + uint16_t sport = 0, + uint16_t dport = 0 + ); + + std::string ip_; + std::atomic stop_{false}; + bool mask_{false}; +};