From 82bb5d99152b1fac5e2ed1542b38b172beddd1da Mon Sep 17 00:00:00 2001 From: Sepalani Date: Tue, 29 Aug 2017 22:14:01 +0100 Subject: [PATCH] NetworkCaptureLogger: PCAP support added Log TCP/UDP read/write with fake packet. --- Source/Core/Common/Network.cpp | 115 ++++++++++++ Source/Core/Common/Network.h | 75 ++++++++ Source/Core/Common/PcapFile.cpp | 7 +- Source/Core/Common/PcapFile.h | 13 +- Source/Core/Core/Config/MainSettings.cpp | 1 + Source/Core/Core/Config/MainSettings.h | 1 + .../Core/Core/Debugger/PPCDebugInterface.cpp | 13 +- Source/Core/Core/IOS/Network/Socket.cpp | 25 ++- Source/Core/Core/NetworkCaptureLogger.cpp | 176 +++++++++++++++++- Source/Core/Core/NetworkCaptureLogger.h | 80 +++++++- .../Core/DolphinQt/Debugger/NetworkWidget.cpp | 7 + .../Core/DolphinQt/Debugger/NetworkWidget.h | 1 + 12 files changed, 485 insertions(+), 29 deletions(-) diff --git a/Source/Core/Common/Network.cpp b/Source/Core/Common/Network.cpp index a928e8e831..c84cf5fd88 100644 --- a/Source/Core/Common/Network.cpp +++ b/Source/Core/Common/Network.cpp @@ -6,6 +6,15 @@ #include #include +#include + +#ifndef _WIN32 +#include +#include +#include +#else +#include +#endif #include @@ -72,4 +81,110 @@ std::optional StringToMacAddress(std::string_view mac_string) return std::make_optional(mac); } + +EthernetHeader::EthernetHeader() = default; + +EthernetHeader::EthernetHeader(u16 ether_type) +{ + ethertype = htons(ether_type); +} + +u16 EthernetHeader::Size() const +{ + return static_cast(SIZE); +} + +IPv4Header::IPv4Header() = default; + +IPv4Header::IPv4Header(u16 data_size, u8 ip_proto, const sockaddr_in& from, const sockaddr_in& to) +{ + version_ihl = 0x45; + total_len = htons(Size() + data_size); + flags_fragment_offset = htons(0x4000); + ttl = 0x40; + protocol = ip_proto; + std::memcpy(&source_addr, &from.sin_addr, IPV4_ADDR_LEN); + std::memcpy(&destination_addr, &to.sin_addr, IPV4_ADDR_LEN); + + header_checksum = htons(ComputeNetworkChecksum(this, Size())); +} + +u16 IPv4Header::Size() const +{ + return static_cast(SIZE); +} + +TCPHeader::TCPHeader() = default; + +TCPHeader::TCPHeader(const sockaddr_in& from, const sockaddr_in& to, u32 seq, const u8* data, + u16 length) +{ + std::memcpy(&source_port, &from.sin_port, 2); + std::memcpy(&destination_port, &to.sin_port, 2); + sequence_number = htonl(seq); + + // TODO: Write flags + // Write data offset + std::memset(&properties, 0x50, 1); + + window_size = 0xFFFF; + + // Compute the TCP checksum with its pseudo header + const u32 source_addr = ntohl(from.sin_addr.s_addr); + const u32 destination_addr = ntohl(to.sin_addr.s_addr); + const u32 initial_value = (source_addr >> 16) + (source_addr & 0xFFFF) + + (destination_addr >> 16) + (destination_addr & 0xFFFF) + IPProto() + + Size() + length; + u32 tcp_checksum = ComputeNetworkChecksum(this, Size(), initial_value); + tcp_checksum += ComputeNetworkChecksum(data, length); + while (tcp_checksum > 0xFFFF) + tcp_checksum = (tcp_checksum >> 16) + (tcp_checksum & 0xFFFF); + checksum = htons(static_cast(tcp_checksum)); +} + +u16 TCPHeader::Size() const +{ + return static_cast(SIZE); +} + +u8 TCPHeader::IPProto() const +{ + return static_cast(IPPROTO_TCP); +} + +UDPHeader::UDPHeader() = default; + +UDPHeader::UDPHeader(const sockaddr_in& from, const sockaddr_in& to, u16 data_length) +{ + std::memcpy(&source_port, &from.sin_port, 2); + std::memcpy(&destination_port, &to.sin_port, 2); + length = htons(Size() + data_length); +} + +u16 UDPHeader::Size() const +{ + return static_cast(SIZE); +} + +u8 UDPHeader::IPProto() const +{ + return static_cast(IPPROTO_UDP); +} + +// Compute the network checksum with a 32-bit accumulator using the +// "Normal" order, see RFC 1071 for more details. +u16 ComputeNetworkChecksum(const void* data, u16 length, u32 initial_value) +{ + u32 checksum = initial_value; + std::size_t index = 0; + const std::string_view data_view{reinterpret_cast(data), length}; + for (u8 b : data_view) + { + const bool is_hi = index++ % 2 == 0; + checksum += is_hi ? b << 8 : b; + } + while (checksum > 0xFFFF) + checksum = (checksum >> 16) + (checksum & 0xFFFF); + return ~static_cast(checksum); +} } // namespace Common diff --git a/Source/Core/Common/Network.h b/Source/Core/Common/Network.h index 18a2f887e9..2f1666b152 100644 --- a/Source/Core/Common/Network.h +++ b/Source/Core/Common/Network.h @@ -11,6 +11,8 @@ #include "Common/CommonTypes.h" +struct sockaddr_in; + namespace Common { enum class MACConsumer @@ -25,8 +27,81 @@ enum }; using MACAddress = std::array; +constexpr std::size_t IPV4_ADDR_LEN = 4; + +struct EthernetHeader +{ + EthernetHeader(); + explicit EthernetHeader(u16 ether_type); + u16 Size() const; + + static constexpr std::size_t SIZE = 14; + + MACAddress destination = {}; + MACAddress source = {}; + u16 ethertype = 0; +}; +static_assert(sizeof(EthernetHeader) == EthernetHeader::SIZE); + +struct IPv4Header +{ + IPv4Header(); + IPv4Header(u16 data_size, u8 ip_proto, const sockaddr_in& from, const sockaddr_in& to); + u16 Size() const; + + static constexpr std::size_t SIZE = 20; + + u8 version_ihl = 0; + u8 dscp_esn = 0; + u16 total_len = 0; + u16 identification = 0; + u16 flags_fragment_offset = 0; + u8 ttl = 0; + u8 protocol = 0; + u16 header_checksum = 0; + u8 source_addr[IPV4_ADDR_LEN]{}; + u8 destination_addr[IPV4_ADDR_LEN]{}; +}; +static_assert(sizeof(IPv4Header) == IPv4Header::SIZE); + +struct TCPHeader +{ + TCPHeader(); + TCPHeader(const sockaddr_in& from, const sockaddr_in& to, u32 seq, const u8* data, u16 length); + u16 Size() const; + u8 IPProto() const; + + static constexpr std::size_t SIZE = 20; + + u16 source_port = 0; + u16 destination_port = 0; + u32 sequence_number = 0; + u32 acknowledgement_number = 0; + u16 properties = 0; + u16 window_size = 0; + u16 checksum = 0; + u16 urgent_pointer = 0; +}; +static_assert(sizeof(TCPHeader) == TCPHeader::SIZE); + +struct UDPHeader +{ + UDPHeader(); + UDPHeader(const sockaddr_in& from, const sockaddr_in& to, u16 data_length); + u16 Size() const; + u8 IPProto() const; + + static constexpr std::size_t SIZE = 8; + + u16 source_port = 0; + u16 destination_port = 0; + u16 length = 0; + u16 checksum = 0; +}; +static_assert(sizeof(UDPHeader) == UDPHeader::SIZE); MACAddress GenerateMacAddress(MACConsumer type); std::string MacAddressToString(const MACAddress& mac); std::optional StringToMacAddress(std::string_view mac_string); +u16 ComputeNetworkChecksum(const void* data, u16 length, u32 initial_value = 0); } // namespace Common diff --git a/Source/Core/Common/PcapFile.cpp b/Source/Core/Common/PcapFile.cpp index 540f641d54..b7feb4f4eb 100644 --- a/Source/Core/Common/PcapFile.cpp +++ b/Source/Core/Common/PcapFile.cpp @@ -17,9 +17,6 @@ const u16 PCAP_VERSION_MAJOR = 2; const u16 PCAP_VERSION_MINOR = 4; const u32 PCAP_CAPTURE_LENGTH = 65535; -// TODO(delroth): Make this configurable at PCAP creation time? -const u32 PCAP_DATA_LINK_TYPE = 147; // Reserved for internal use. - // Designed to be directly written into the PCAP file. The PCAP format is // endian independent, so this works just fine. #pragma pack(push, 1) @@ -45,10 +42,10 @@ struct PCAPRecordHeader } // namespace -void PCAP::AddHeader() +void PCAP::AddHeader(u32 link_type) { PCAPHeader hdr = {PCAP_MAGIC, PCAP_VERSION_MAJOR, PCAP_VERSION_MINOR, 0, - 0, PCAP_CAPTURE_LENGTH, PCAP_DATA_LINK_TYPE}; + 0, PCAP_CAPTURE_LENGTH, link_type}; m_fp->WriteBytes(&hdr, sizeof(hdr)); } diff --git a/Source/Core/Common/PcapFile.h b/Source/Core/Common/PcapFile.h index dae0c1b2bb..957c5ca6e4 100644 --- a/Source/Core/Common/PcapFile.h +++ b/Source/Core/Common/PcapFile.h @@ -24,9 +24,18 @@ namespace Common class PCAP final { public: + enum class LinkType : u32 + { + Ethernet = 1, // IEEE 802.3 Ethernet + User = 147, // Reserved for internal use + }; + // Takes ownership of the file object. Assumes the file object is already // opened in write mode. - explicit PCAP(File::IOFile* fp) : m_fp(fp) { AddHeader(); } + explicit PCAP(File::IOFile* fp, LinkType link_type = LinkType::User) : m_fp(fp) + { + AddHeader(static_cast(link_type)); + } template void AddPacket(const T& obj) { @@ -36,7 +45,7 @@ public: void AddPacket(const u8* bytes, size_t size); private: - void AddHeader(); + void AddHeader(u32 link_type); std::unique_ptr m_fp; }; diff --git a/Source/Core/Core/Config/MainSettings.cpp b/Source/Core/Core/Config/MainSettings.cpp index 291aaa79f5..bcd8887def 100644 --- a/Source/Core/Core/Config/MainSettings.cpp +++ b/Source/Core/Core/Config/MainSettings.cpp @@ -156,6 +156,7 @@ const Info MAIN_NETWORK_SSL_VERIFY_CERTIFICATES{ const Info MAIN_NETWORK_SSL_DUMP_ROOT_CA{{System::Main, "Network", "SSLDumpRootCA"}, false}; const Info MAIN_NETWORK_SSL_DUMP_PEER_CERT{{System::Main, "Network", "SSLDumpPeerCert"}, false}; +const Info MAIN_NETWORK_DUMP_AS_PCAP{{System::Main, "Network", "DumpAsPCAP"}, false}; // Main.Interface diff --git a/Source/Core/Core/Config/MainSettings.h b/Source/Core/Core/Config/MainSettings.h index 5338aa7a76..1d9a687153 100644 --- a/Source/Core/Core/Config/MainSettings.h +++ b/Source/Core/Core/Config/MainSettings.h @@ -129,6 +129,7 @@ extern const Info MAIN_NETWORK_SSL_DUMP_WRITE; extern const Info MAIN_NETWORK_SSL_VERIFY_CERTIFICATES; extern const Info MAIN_NETWORK_SSL_DUMP_ROOT_CA; extern const Info MAIN_NETWORK_SSL_DUMP_PEER_CERT; +extern const Info MAIN_NETWORK_DUMP_AS_PCAP; // Main.Interface diff --git a/Source/Core/Core/Debugger/PPCDebugInterface.cpp b/Source/Core/Core/Debugger/PPCDebugInterface.cpp index 44bdf0e3c7..fb6840a327 100644 --- a/Source/Core/Core/Debugger/PPCDebugInterface.cpp +++ b/Source/Core/Core/Debugger/PPCDebugInterface.cpp @@ -388,14 +388,23 @@ std::shared_ptr PPCDebugInterface::NetworkLogger() { const bool has_ssl = Config::Get(Config::MAIN_NETWORK_SSL_DUMP_READ) || Config::Get(Config::MAIN_NETWORK_SSL_DUMP_WRITE); - const auto current_capture_type = - has_ssl ? Core::NetworkCaptureType::Raw : Core::NetworkCaptureType::None; + const bool is_pcap = Config::Get(Config::MAIN_NETWORK_DUMP_AS_PCAP); + const auto current_capture_type = [&] { + if (is_pcap) + return Core::NetworkCaptureType::PCAP; + if (has_ssl) + return Core::NetworkCaptureType::Raw; + return Core::NetworkCaptureType::None; + }(); if (m_network_logger && m_network_logger->GetCaptureType() == current_capture_type) return m_network_logger; switch (current_capture_type) { + case Core::NetworkCaptureType::PCAP: + m_network_logger = std::make_shared(); + break; case Core::NetworkCaptureType::Raw: m_network_logger = std::make_shared(); break; diff --git a/Source/Core/Core/IOS/Network/Socket.cpp b/Source/Core/Core/IOS/Network/Socket.cpp index d168da3709..321e8c1efe 100644 --- a/Source/Core/Core/IOS/Network/Socket.cpp +++ b/Source/Core/Core/IOS/Network/Socket.cpp @@ -462,8 +462,9 @@ void WiiSocket::Update(bool read, bool write, bool except) if (ret >= 0) { - PowerPC::debug_interface.NetworkLogger()->LogWrite(Memory::GetPointer(BufferOut2), - ret); + PowerPC::debug_interface.NetworkLogger()->LogSSLWrite( + Memory::GetPointer(BufferOut2), ret, + static_cast(Device::NetSSL::_SSL[sslID].ctx.p_bio)->fd); // Return bytes written or SSL_ERR_ZERO if none WriteReturnValue((ret == 0) ? SSL_ERR_ZERO : ret, BufferIn); } @@ -495,7 +496,9 @@ void WiiSocket::Update(bool read, bool write, bool except) if (ret >= 0) { - PowerPC::debug_interface.NetworkLogger()->LogRead(Memory::GetPointer(BufferIn2), ret); + PowerPC::debug_interface.NetworkLogger()->LogSSLRead( + Memory::GetPointer(BufferIn2), ret, + static_cast(Device::NetSSL::_SSL[sslID].ctx.p_bio)->fd); // Return bytes read or SSL_ERR_ZERO if none WriteReturnValue((ret == 0) ? SSL_ERR_ZERO : ret, BufferIn); } @@ -553,10 +556,12 @@ void WiiSocket::Update(bool read, bool write, bool except) WiiSockMan::Convert(*wii_name, local_name); } - const int ret = sendto(fd, data, BufferInSize, flags, - has_destaddr ? (struct sockaddr*)&local_name : nullptr, - has_destaddr ? sizeof(sockaddr) : 0); + auto* to = has_destaddr ? reinterpret_cast(&local_name) : nullptr; + socklen_t tolen = has_destaddr ? sizeof(sockaddr) : 0; + const int ret = sendto(fd, data, BufferInSize, flags, to, tolen); ReturnValue = WiiSockMan::GetNetErrorCode(ret, "SO_SENDTO", true); + if (ret > 0) + PowerPC::debug_interface.NetworkLogger()->LogWrite(data, ret, fd, to); INFO_LOG_FMT(IOS_NET, "{} = {} Socket: {:08x}, BufferIn: ({:08x}, {}), BufferIn2: ({:08x}, {}), " @@ -599,11 +604,13 @@ void WiiSocket::Update(bool read, bool write, bool except) } #endif socklen_t addrlen = sizeof(sockaddr_in); - const int ret = recvfrom(fd, data, data_len, flags, - BufferOutSize2 ? (struct sockaddr*)&local_name : nullptr, - BufferOutSize2 ? &addrlen : nullptr); + auto* from = BufferOutSize2 ? reinterpret_cast(&local_name) : nullptr; + socklen_t* fromlen = BufferOutSize2 ? &addrlen : nullptr; + const int ret = recvfrom(fd, data, data_len, flags, from, fromlen); ReturnValue = WiiSockMan::GetNetErrorCode(ret, BufferOutSize2 ? "SO_RECVFROM" : "SO_RECV", true); + if (ret > 0) + PowerPC::debug_interface.NetworkLogger()->LogRead(data, ret, fd, from); INFO_LOG_FMT(IOS_NET, "{}({}, {}) Socket: {:08X}, Flags: {:08X}, " diff --git a/Source/Core/Core/NetworkCaptureLogger.cpp b/Source/Core/Core/NetworkCaptureLogger.cpp index b3fb6c69cc..048adb4e08 100644 --- a/Source/Core/Core/NetworkCaptureLogger.cpp +++ b/Source/Core/Core/NetworkCaptureLogger.cpp @@ -4,8 +4,18 @@ #include "Core/NetworkCaptureLogger.h" +#include +#include +#include +#include + +#include +#include + #include "Common/FileUtil.h" #include "Common/IOFile.h" +#include "Common/Network.h" +#include "Common/PcapFile.h" #include "Core/Config/MainSettings.h" #include "Core/ConfigManager.h" @@ -14,11 +24,21 @@ namespace Core NetworkCaptureLogger::NetworkCaptureLogger() = default; NetworkCaptureLogger::~NetworkCaptureLogger() = default; -void DummyNetworkCaptureLogger::LogRead(const void* data, std::size_t length) +void DummyNetworkCaptureLogger::LogSSLRead(const void* data, std::size_t length, s32 socket) { } -void DummyNetworkCaptureLogger::LogWrite(const void* data, std::size_t length) +void DummyNetworkCaptureLogger::LogSSLWrite(const void* data, std::size_t length, s32 socket) +{ +} + +void DummyNetworkCaptureLogger::LogRead(const void* data, std::size_t length, s32 socket, + sockaddr* from) +{ +} + +void DummyNetworkCaptureLogger::LogWrite(const void* data, std::size_t length, s32 socket, + sockaddr* to) { } @@ -27,7 +47,7 @@ NetworkCaptureType DummyNetworkCaptureLogger::GetCaptureType() const return NetworkCaptureType::None; } -void BinarySSLCaptureLogger::LogRead(const void* data, std::size_t length) +void BinarySSLCaptureLogger::LogSSLRead(const void* data, std::size_t length, s32 socket) { if (!Config::Get(Config::MAIN_NETWORK_SSL_DUMP_READ)) return; @@ -36,7 +56,7 @@ void BinarySSLCaptureLogger::LogRead(const void* data, std::size_t length) File::IOFile(filename, "ab").WriteBytes(data, length); } -void BinarySSLCaptureLogger::LogWrite(const void* data, std::size_t length) +void BinarySSLCaptureLogger::LogSSLWrite(const void* data, std::size_t length, s32 socket) { if (!Config::Get(Config::MAIN_NETWORK_SSL_DUMP_WRITE)) return; @@ -49,4 +69,152 @@ NetworkCaptureType BinarySSLCaptureLogger::GetCaptureType() const { return NetworkCaptureType::Raw; } + +PCAPSSLCaptureLogger::PCAPSSLCaptureLogger() +{ + const std::string filepath = + fmt::format("{}{} {:%Y-%m-%d %Hh%Mm%Ss}.pcap", File::GetUserPath(D_DUMPSSL_IDX), + SConfig::GetInstance().GetGameID(), fmt::localtime(std::time(nullptr))); + m_file = std::make_unique(new File::IOFile(filepath, "wb"), + Common::PCAP::LinkType::Ethernet); +} + +PCAPSSLCaptureLogger::~PCAPSSLCaptureLogger() = default; + +PCAPSSLCaptureLogger::ErrorState PCAPSSLCaptureLogger::SaveState() const +{ + return { + errno, +#ifdef _WIN32 + WSAGetLastError(), +#endif + }; +} + +void PCAPSSLCaptureLogger::RestoreState(const PCAPSSLCaptureLogger::ErrorState& state) const +{ + errno = state.error; +#ifdef _WIN32 + WSASetLastError(state.wsa_error); +#endif +} + +void PCAPSSLCaptureLogger::LogSSLRead(const void* data, std::size_t length, s32 socket) +{ + if (!Config::Get(Config::MAIN_NETWORK_SSL_DUMP_READ)) + return; + Log(LogType::Read, data, length, socket, nullptr); +} + +void PCAPSSLCaptureLogger::LogSSLWrite(const void* data, std::size_t length, s32 socket) +{ + if (!Config::Get(Config::MAIN_NETWORK_SSL_DUMP_WRITE)) + return; + Log(LogType::Write, data, length, socket, nullptr); +} + +void PCAPSSLCaptureLogger::LogRead(const void* data, std::size_t length, s32 socket, sockaddr* from) +{ + Log(LogType::Read, data, length, socket, from); +} + +void PCAPSSLCaptureLogger::LogWrite(const void* data, std::size_t length, s32 socket, sockaddr* to) +{ + Log(LogType::Write, data, length, socket, to); +} + +void PCAPSSLCaptureLogger::Log(LogType log_type, const void* data, std::size_t length, s32 socket, + sockaddr* other) +{ + const auto state = SaveState(); + sockaddr_in sock; + sockaddr_in peer; + sockaddr_in* from; + sockaddr_in* to; + socklen_t sock_len = sizeof(sock); + socklen_t peer_len = sizeof(sock); + + if (getsockname(socket, reinterpret_cast(&sock), &sock_len) != 0) + { + RestoreState(state); + return; + } + + if (other == nullptr && getpeername(socket, reinterpret_cast(&peer), &peer_len) != 0) + { + RestoreState(state); + return; + } + + if (log_type == LogType::Read) + { + from = other ? reinterpret_cast(other) : &peer; + to = &sock; + } + else + { + from = &sock; + to = other ? reinterpret_cast(other) : &peer; + } + + LogIPv4(log_type, reinterpret_cast(data), static_cast(length), socket, *from, + *to); + RestoreState(state); +} + +void PCAPSSLCaptureLogger::LogIPv4(LogType log_type, const u8* data, u16 length, s32 socket, + const sockaddr_in& from, const sockaddr_in& to) +{ + int socket_type; + socklen_t option_length = sizeof(int); + + if (getsockopt(socket, SOL_SOCKET, SO_TYPE, reinterpret_cast(&socket_type), + &option_length) != 0 || + (socket_type != SOCK_STREAM && socket_type != SOCK_DGRAM)) + { + return; + } + + std::vector packet; + auto insert = [&](const auto* data, std::size_t size) { + const u8* begin = reinterpret_cast(data); + packet.insert(packet.end(), begin, begin + size); + }; + + Common::EthernetHeader ethernet_header(0x800); + auto mac = Common::StringToMacAddress(SConfig::GetInstance().m_WirelessMac); + if (mac) + { + auto& mac_address = + (log_type == LogType::Write) ? ethernet_header.source : ethernet_header.destination; + mac_address = *mac; + } + insert(ðernet_header, ethernet_header.Size()); + + if (socket_type == SOCK_STREAM) + { + u32& sequence_number = + (log_type == LogType::Read) ? m_read_sequence_number : m_write_sequence_number; + Common::TCPHeader tcp_header(from, to, sequence_number, data, length); + sequence_number += static_cast(length); + Common::IPv4Header ip_header(tcp_header.Size() + length, tcp_header.IPProto(), from, to); + insert(&ip_header, ip_header.Size()); + insert(&tcp_header, tcp_header.Size()); + } + else if (socket_type == SOCK_DGRAM) + { + Common::UDPHeader udp_header(from, to, length); + Common::IPv4Header ip_header(udp_header.Size() + length, udp_header.IPProto(), from, to); + insert(&ip_header, ip_header.Size()); + insert(&udp_header, udp_header.Size()); + } + + packet.insert(packet.end(), data, data + length); + m_file->AddPacket(packet.data(), packet.size()); +} + +NetworkCaptureType PCAPSSLCaptureLogger::GetCaptureType() const +{ + return NetworkCaptureType::PCAP; +} } // namespace Core diff --git a/Source/Core/Core/NetworkCaptureLogger.h b/Source/Core/Core/NetworkCaptureLogger.h index 24c129e772..814c630855 100644 --- a/Source/Core/Core/NetworkCaptureLogger.h +++ b/Source/Core/Core/NetworkCaptureLogger.h @@ -4,7 +4,25 @@ #pragma once +#include #include +#include + +#ifdef _WIN32 +#include +using socklen_t = int; +#else +#include +#include +#include +#endif + +#include "Common/CommonTypes.h" + +namespace Common +{ +class PCAP; +} namespace Core { @@ -25,24 +43,72 @@ public: NetworkCaptureLogger& operator=(NetworkCaptureLogger&&) = delete; virtual ~NetworkCaptureLogger(); - virtual void LogRead(const void* data, std::size_t length) = 0; - virtual void LogWrite(const void* data, std::size_t length) = 0; + virtual void LogSSLRead(const void* data, std::size_t length, s32 socket) = 0; + virtual void LogSSLWrite(const void* data, std::size_t length, s32 socket) = 0; + + virtual void LogRead(const void* data, std::size_t length, s32 socket, sockaddr* from) = 0; + virtual void LogWrite(const void* data, std::size_t length, s32 socket, sockaddr* to) = 0; + virtual NetworkCaptureType GetCaptureType() const = 0; }; class DummyNetworkCaptureLogger : public NetworkCaptureLogger { public: - void LogRead(const void* data, std::size_t length) override; - void LogWrite(const void* data, std::size_t length) override; + void LogSSLRead(const void* data, std::size_t length, s32 socket) override; + void LogSSLWrite(const void* data, std::size_t length, s32 socket) override; + + void LogRead(const void* data, std::size_t length, s32 socket, sockaddr* from) override; + void LogWrite(const void* data, std::size_t length, s32 socket, sockaddr* to) override; + NetworkCaptureType GetCaptureType() const override; }; -class BinarySSLCaptureLogger final : public NetworkCaptureLogger +class BinarySSLCaptureLogger final : public DummyNetworkCaptureLogger { public: - void LogRead(const void* data, std::size_t length) override; - void LogWrite(const void* data, std::size_t length) override; + void LogSSLRead(const void* data, std::size_t length, s32 socket) override; + void LogSSLWrite(const void* data, std::size_t length, s32 socket) override; + NetworkCaptureType GetCaptureType() const override; }; + +class PCAPSSLCaptureLogger final : public NetworkCaptureLogger +{ +public: + PCAPSSLCaptureLogger(); + ~PCAPSSLCaptureLogger(); + + void LogSSLRead(const void* data, std::size_t length, s32 socket) override; + void LogSSLWrite(const void* data, std::size_t length, s32 socket) override; + + void LogRead(const void* data, std::size_t length, s32 socket, sockaddr* from) override; + void LogWrite(const void* data, std::size_t length, s32 socket, sockaddr* to) override; + + NetworkCaptureType GetCaptureType() const override; + +private: + enum class LogType + { + Read, + Write, + }; + struct ErrorState + { + int error; +#ifdef _WIN32 + int wsa_error; +#endif + }; + ErrorState SaveState() const; + void RestoreState(const ErrorState& state) const; + + void Log(LogType log_type, const void* data, std::size_t length, s32 socket, sockaddr* other); + void LogIPv4(LogType log_type, const u8* data, u16 length, s32 socket, const sockaddr_in& from, + const sockaddr_in& to); + + std::unique_ptr m_file; + u32 m_read_sequence_number = 0; + u32 m_write_sequence_number = 0; +}; } // namespace Core diff --git a/Source/Core/DolphinQt/Debugger/NetworkWidget.cpp b/Source/Core/DolphinQt/Debugger/NetworkWidget.cpp index 6bf992335b..f93b529d54 100644 --- a/Source/Core/DolphinQt/Debugger/NetworkWidget.cpp +++ b/Source/Core/DolphinQt/Debugger/NetworkWidget.cpp @@ -215,6 +215,9 @@ void NetworkWidget::ConnectWidgets() connect(m_verify_certificates_checkbox, &QCheckBox::stateChanged, [](int state) { Config::SetBaseOrCurrent(Config::MAIN_NETWORK_SSL_VERIFY_CERTIFICATES, state == Qt::Checked); }); + connect(m_dump_as_pcap_checkbox, &QCheckBox::stateChanged, [](int state) { + Config::SetBaseOrCurrent(Config::MAIN_NETWORK_DUMP_AS_PCAP, state == Qt::Checked); + }); } void NetworkWidget::Update() @@ -258,6 +261,7 @@ void NetworkWidget::Update() m_dump_peer_cert_checkbox->setChecked(Config::Get(Config::MAIN_NETWORK_SSL_DUMP_PEER_CERT)); m_verify_certificates_checkbox->setChecked( Config::Get(Config::MAIN_NETWORK_SSL_VERIFY_CERTIFICATES)); + m_dump_as_pcap_checkbox->setChecked(Config::Get(Config::MAIN_NETWORK_DUMP_AS_PCAP)); } QGroupBox* NetworkWidget::CreateSocketTableGroup() @@ -317,12 +321,15 @@ QGroupBox* NetworkWidget::CreateSSLOptionsGroup() m_dump_root_ca_checkbox = new QCheckBox(tr("Dump root CA")); m_dump_peer_cert_checkbox = new QCheckBox(tr("Dump peer certificates")); m_verify_certificates_checkbox = new QCheckBox(tr("Verify certificates")); + // i18n: PCAP is a file format + m_dump_as_pcap_checkbox = new QCheckBox(tr("Dump as PCAP")); ssl_options_layout->addWidget(m_dump_ssl_read_checkbox, 0, 0); ssl_options_layout->addWidget(m_dump_ssl_write_checkbox, 1, 0); ssl_options_layout->addWidget(m_verify_certificates_checkbox, 2, 0); ssl_options_layout->addWidget(m_dump_root_ca_checkbox, 0, 1); ssl_options_layout->addWidget(m_dump_peer_cert_checkbox, 1, 1); + ssl_options_layout->addWidget(m_dump_as_pcap_checkbox, 2, 1); ssl_options_layout->setSpacing(1); return ssl_options_group; diff --git a/Source/Core/DolphinQt/Debugger/NetworkWidget.h b/Source/Core/DolphinQt/Debugger/NetworkWidget.h index 735c398dc9..f3c93d11a6 100644 --- a/Source/Core/DolphinQt/Debugger/NetworkWidget.h +++ b/Source/Core/DolphinQt/Debugger/NetworkWidget.h @@ -43,4 +43,5 @@ private: QCheckBox* m_dump_root_ca_checkbox; QCheckBox* m_dump_peer_cert_checkbox; QCheckBox* m_verify_certificates_checkbox; + QCheckBox* m_dump_as_pcap_checkbox; };