From fbedc55d5aa861c381701c9f913b34ee7ab57ec4 Mon Sep 17 00:00:00 2001 From: srdusr <99972264+srdusr@users.noreply.github.com> Date: Tue, 21 May 2024 22:24:00 +0200 Subject: Add GUI parity for -c/-a, mDNS/SSH dissectors, and two new fuzz harnesses GUI parity: checksum_status()/reassembled_http_status() moved out of main.cpp into a shared wireframe/packet_diagnostics.hpp so the GUI can show the same -c/-a diagnostics for the selected packet without duplicating the Ethernet/IPv4/TCP walk. Visually verified under Xvfb with the same split-segment scenario used to verify -a on the CLI. Two new L7 dissectors: mDNS (reuses parse_dns outright - RFC 6762 keeps DNS's wire format, just a different port) and SSH's cleartext identification banner. Live-verified against this machine's real sshd and a real DNS-wire-format packet sent to port 5353. Two new fuzz harnesses (fuzz_checksum, fuzz_tcp_reassembly) covering code added here that the original nine harnesses never touched. All 12 run clean across ~90M executions with no crashes. NAMES.md and PLAN.md updated with this round's decisions and naming candidates. --- include/wireframe/packet_diagnostics.hpp | 92 ++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 include/wireframe/packet_diagnostics.hpp (limited to 'include/wireframe/packet_diagnostics.hpp') diff --git a/include/wireframe/packet_diagnostics.hpp b/include/wireframe/packet_diagnostics.hpp new file mode 100644 index 0000000..4b9b0c6 --- /dev/null +++ b/include/wireframe/packet_diagnostics.hpp @@ -0,0 +1,92 @@ +#pragma once + +#include +#include +#include +#include + +#include "wireframe/l7/http.hpp" +#include "wireframe/net/checksum.hpp" +#include "wireframe/net/ethernet.hpp" +#include "wireframe/net/ipv4.hpp" +#include "wireframe/net/tcp.hpp" +#include "wireframe/net/tcp_reassembly.hpp" + +// Checksum validation and TCP stream reassembly are both deliberately +// kept out of summarize_packet()'s shared per-packet output - see +// wireframe/net/checksum.hpp and wireframe/net/tcp_reassembly.hpp for +// why each is opt-in (checksum offload false positives; reassembly's +// per-flow state and extra per-packet work). Shared between the CLI +// (-c/-a) and GUI frontends so they don't hand-roll two separate +// Ethernet/IPv4/TCP walks down to the same byte spans - the same +// reasoning wireframe::CaptureSession exists for at the setup layer. +namespace wireframe { + +inline std::string checksum_status(std::span bytes, int datalink) { + std::span ip_bytes; + if (datalink == DLT_RAW) { + ip_bytes = bytes; + } else { + auto eth = net::parse_ethernet(bytes); + if (!eth || eth->header.ethertype != net::kEthertypeIPv4) return ""; + ip_bytes = eth->payload; + } + if (ip_bytes.empty() || (ip_bytes[0] >> 4) != 4) return ""; // only IPv4 checksums, for now + + auto ip = net::parse_ipv4(ip_bytes); + if (!ip) return ""; + + std::size_t header_len = static_cast(ip->header.ihl) * 4; + std::string out = "checksums: IP="; + out += net::verify_ipv4_checksum(ip_bytes.first(header_len)) ? "ok" : "BAD"; + + using net::ChecksumResult; + if (ip->header.protocol == net::kProtoTcp) { + auto result = net::verify_tcp_checksum_ipv4(ip->header.src, ip->header.dst, ip->payload); + out += result == ChecksumResult::kValid ? " TCP=ok" : " TCP=BAD"; + } else if (ip->header.protocol == net::kProtoUdp) { + auto result = net::verify_udp_checksum_ipv4(ip->header.src, ip->header.dst, ip->payload); + out += result == ChecksumResult::kValid ? " UDP=ok" + : result == ChecksumResult::kNotPresent ? " UDP=none" + : " UDP=BAD"; + } + return out; +} + +inline std::optional reassembled_http_status(std::span bytes, + int datalink, + net::TcpReassembler& reassembler) { + std::span ip_bytes; + if (datalink == DLT_RAW) { + ip_bytes = bytes; + } else { + auto eth = net::parse_ethernet(bytes); + if (!eth || eth->header.ethertype != net::kEthertypeIPv4) return std::nullopt; + ip_bytes = eth->payload; + } + if (ip_bytes.empty() || (ip_bytes[0] >> 4) != 4) return std::nullopt; // IPv4 only, for now + + auto ip = net::parse_ipv4(ip_bytes); + if (!ip || ip->header.protocol != net::kProtoTcp) return std::nullopt; + + auto tcp = net::parse_tcp(ip->payload); + if (!tcp) return std::nullopt; + + auto reassembled = reassembler.process_segment(ip->header.src, tcp->header.src_port, + ip->header.dst, tcp->header.dst_port, + tcp->header.seq, tcp->header.flags, + tcp->payload); + if (!reassembled) return std::nullopt; + + auto http = net::parse_http(*reassembled); + if (!http) return std::nullopt; + + std::string out = "reassembled "; + out += http->is_request ? "request: " : "response: "; + out += http->method_or_version + " " + http->target_or_status; + if (http->host) out += " Host: " + *http->host; + out += " (" + std::to_string(reassembled->size()) + " bytes so far)"; + return out; +} + +} // namespace wireframe -- cgit v1.2.3