summaryrefslogtreecommitdiff
path: root/Source/Core
diff options
context:
space:
mode:
authorLéo Lam <leo@leolam.fr>2021-02-11 00:15:37 +0100
committerGitHub <noreply@github.com>2021-02-11 00:15:37 +0100
commit1fc6fbc2c0c77d5ccde9440b54aba1b007b07136 (patch)
treeaf30d218c6c12f2fe5c31b8001c4251b7c24e424 /Source/Core
parentddacbf83f65b9642f3c9cee9e075b7ec3d473321 (diff)
parent82bb5d99152b1fac5e2ed1542b38b172beddd1da (diff)
Merge pull request #6075 from sepalani/pcap-log
PCAP logging with fake TCP/UDP packet
Diffstat (limited to 'Source/Core')
-rw-r--r--Source/Core/Common/Network.cpp115
-rw-r--r--Source/Core/Common/Network.h75
-rw-r--r--Source/Core/Common/PcapFile.cpp7
-rw-r--r--Source/Core/Common/PcapFile.h13
-rw-r--r--Source/Core/Core/Config/MainSettings.cpp1
-rw-r--r--Source/Core/Core/Config/MainSettings.h1
-rw-r--r--Source/Core/Core/Debugger/PPCDebugInterface.cpp13
-rw-r--r--Source/Core/Core/IOS/Network/Socket.cpp25
-rw-r--r--Source/Core/Core/NetworkCaptureLogger.cpp176
-rw-r--r--Source/Core/Core/NetworkCaptureLogger.h80
-rw-r--r--Source/Core/DolphinQt/Debugger/NetworkWidget.cpp7
-rw-r--r--Source/Core/DolphinQt/Debugger/NetworkWidget.h1
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 <algorithm>
#include <cctype>
+#include <string_view>
+
+#ifndef _WIN32
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#else
+#include <WinSock2.h>
+#endif
#include <fmt/format.h>
@@ -72,4 +81,110 @@ std::optional<MACAddress> 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<u16>(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<u16>(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<u16>(tcp_checksum));
+}
+
+u16 TCPHeader::Size() const
+{
+ return static_cast<u16>(SIZE);
+}
+
+u8 TCPHeader::IPProto() const
+{
+ return static_cast<u8>(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<u16>(SIZE);
+}
+
+u8 UDPHeader::IPProto() const
+{
+ return static_cast<u8>(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<const char*>(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<u16>(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<u8, MAC_ADDRESS_SIZE>;
+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<MACAddress> 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<u32>(link_type));
+ }
template <typename T>
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<File::IOFile> 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<bool> MAIN_NETWORK_SSL_VERIFY_CERTIFICATES{
const Info<bool> MAIN_NETWORK_SSL_DUMP_ROOT_CA{{System::Main, "Network", "SSLDumpRootCA"}, false};
const Info<bool> MAIN_NETWORK_SSL_DUMP_PEER_CERT{{System::Main, "Network", "SSLDumpPeerCert"},
false};
+const Info<bool> 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<bool> MAIN_NETWORK_SSL_DUMP_WRITE;
extern const Info<bool> MAIN_NETWORK_SSL_VERIFY_CERTIFICATES;
extern const Info<bool> MAIN_NETWORK_SSL_DUMP_ROOT_CA;
extern const Info<bool> MAIN_NETWORK_SSL_DUMP_PEER_CERT;
+extern const Info<bool> 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<Core::NetworkCaptureLogger> 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<Core::PCAPSSLCaptureLogger>();
+ break;
case Core::NetworkCaptureType::Raw:
m_network_logger = std::make_shared<Core::BinarySSLCaptureLogger>();
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<mbedtls_net_context*>(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<mbedtls_net_context*>(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<sockaddr*>(&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<sockaddr*>(&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 <array>
+#include <cerrno>
+#include <cstring>
+#include <iterator>
+
+#include <fmt/chrono.h>
+#include <fmt/format.h>
+
#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::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)
+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<Common::PCAP>(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<sockaddr*>(&sock), &sock_len) != 0)
+ {
+ RestoreState(state);
+ return;
+ }
+
+ if (other == nullptr && getpeername(socket, reinterpret_cast<sockaddr*>(&peer), &peer_len) != 0)
+ {
+ RestoreState(state);
+ return;
+ }
+
+ if (log_type == LogType::Read)
+ {
+ from = other ? reinterpret_cast<sockaddr_in*>(other) : &peer;
+ to = &sock;
+ }
+ else
+ {
+ from = &sock;
+ to = other ? reinterpret_cast<sockaddr_in*>(other) : &peer;
+ }
+
+ LogIPv4(log_type, reinterpret_cast<const u8*>(data), static_cast<u16>(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<char*>(&socket_type),
+ &option_length) != 0 ||
+ (socket_type != SOCK_STREAM && socket_type != SOCK_DGRAM))
+ {
+ return;
+ }
+
+ std::vector<u8> packet;
+ auto insert = [&](const auto* data, std::size_t size) {
+ const u8* begin = reinterpret_cast<const u8*>(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(&ethernet_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<u32>(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 <array>
#include <cstddef>
+#include <memory>
+
+#ifdef _WIN32
+#include <WinSock2.h>
+using socklen_t = int;
+#else
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#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 DummyNetworkCaptureLogger
+{
+public:
+ 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 BinarySSLCaptureLogger final : public NetworkCaptureLogger
+class PCAPSSLCaptureLogger final : public NetworkCaptureLogger
{
public:
- void LogRead(const void* data, std::size_t length) override;
- void LogWrite(const void* data, std::size_t length) override;
+ 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<Common::PCAP> 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;
};