Merge pull request #6075 from sepalani/pcap-log
PCAP logging with fake TCP/UDP packet
This commit is contained in:
commit
1fc6fbc2c0
|
@ -6,6 +6,15 @@
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cctype>
|
#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>
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
@ -72,4 +81,110 @@ std::optional<MACAddress> StringToMacAddress(std::string_view mac_string)
|
||||||
|
|
||||||
return std::make_optional(mac);
|
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
|
} // namespace Common
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
|
|
||||||
|
struct sockaddr_in;
|
||||||
|
|
||||||
namespace Common
|
namespace Common
|
||||||
{
|
{
|
||||||
enum class MACConsumer
|
enum class MACConsumer
|
||||||
|
@ -25,8 +27,81 @@ enum
|
||||||
};
|
};
|
||||||
|
|
||||||
using MACAddress = std::array<u8, MAC_ADDRESS_SIZE>;
|
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);
|
MACAddress GenerateMacAddress(MACConsumer type);
|
||||||
std::string MacAddressToString(const MACAddress& mac);
|
std::string MacAddressToString(const MACAddress& mac);
|
||||||
std::optional<MACAddress> StringToMacAddress(std::string_view mac_string);
|
std::optional<MACAddress> StringToMacAddress(std::string_view mac_string);
|
||||||
|
u16 ComputeNetworkChecksum(const void* data, u16 length, u32 initial_value = 0);
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|
|
@ -17,9 +17,6 @@ const u16 PCAP_VERSION_MAJOR = 2;
|
||||||
const u16 PCAP_VERSION_MINOR = 4;
|
const u16 PCAP_VERSION_MINOR = 4;
|
||||||
const u32 PCAP_CAPTURE_LENGTH = 65535;
|
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
|
// Designed to be directly written into the PCAP file. The PCAP format is
|
||||||
// endian independent, so this works just fine.
|
// endian independent, so this works just fine.
|
||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
|
@ -45,10 +42,10 @@ struct PCAPRecordHeader
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void PCAP::AddHeader()
|
void PCAP::AddHeader(u32 link_type)
|
||||||
{
|
{
|
||||||
PCAPHeader hdr = {PCAP_MAGIC, PCAP_VERSION_MAJOR, PCAP_VERSION_MINOR, 0,
|
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));
|
m_fp->WriteBytes(&hdr, sizeof(hdr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,9 +24,18 @@ namespace Common
|
||||||
class PCAP final
|
class PCAP final
|
||||||
{
|
{
|
||||||
public:
|
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
|
// Takes ownership of the file object. Assumes the file object is already
|
||||||
// opened in write mode.
|
// 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>
|
template <typename T>
|
||||||
void AddPacket(const T& obj)
|
void AddPacket(const T& obj)
|
||||||
{
|
{
|
||||||
|
@ -36,7 +45,7 @@ public:
|
||||||
void AddPacket(const u8* bytes, size_t size);
|
void AddPacket(const u8* bytes, size_t size);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void AddHeader();
|
void AddHeader(u32 link_type);
|
||||||
|
|
||||||
std::unique_ptr<File::IOFile> m_fp;
|
std::unique_ptr<File::IOFile> m_fp;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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_ROOT_CA{{System::Main, "Network", "SSLDumpRootCA"}, false};
|
||||||
const Info<bool> MAIN_NETWORK_SSL_DUMP_PEER_CERT{{System::Main, "Network", "SSLDumpPeerCert"},
|
const Info<bool> MAIN_NETWORK_SSL_DUMP_PEER_CERT{{System::Main, "Network", "SSLDumpPeerCert"},
|
||||||
false};
|
false};
|
||||||
|
const Info<bool> MAIN_NETWORK_DUMP_AS_PCAP{{System::Main, "Network", "DumpAsPCAP"}, false};
|
||||||
|
|
||||||
// Main.Interface
|
// Main.Interface
|
||||||
|
|
||||||
|
|
|
@ -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_VERIFY_CERTIFICATES;
|
||||||
extern const Info<bool> MAIN_NETWORK_SSL_DUMP_ROOT_CA;
|
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_SSL_DUMP_PEER_CERT;
|
||||||
|
extern const Info<bool> MAIN_NETWORK_DUMP_AS_PCAP;
|
||||||
|
|
||||||
// Main.Interface
|
// Main.Interface
|
||||||
|
|
||||||
|
|
|
@ -388,14 +388,23 @@ std::shared_ptr<Core::NetworkCaptureLogger> PPCDebugInterface::NetworkLogger()
|
||||||
{
|
{
|
||||||
const bool has_ssl = Config::Get(Config::MAIN_NETWORK_SSL_DUMP_READ) ||
|
const bool has_ssl = Config::Get(Config::MAIN_NETWORK_SSL_DUMP_READ) ||
|
||||||
Config::Get(Config::MAIN_NETWORK_SSL_DUMP_WRITE);
|
Config::Get(Config::MAIN_NETWORK_SSL_DUMP_WRITE);
|
||||||
const auto current_capture_type =
|
const bool is_pcap = Config::Get(Config::MAIN_NETWORK_DUMP_AS_PCAP);
|
||||||
has_ssl ? Core::NetworkCaptureType::Raw : Core::NetworkCaptureType::None;
|
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)
|
if (m_network_logger && m_network_logger->GetCaptureType() == current_capture_type)
|
||||||
return m_network_logger;
|
return m_network_logger;
|
||||||
|
|
||||||
switch (current_capture_type)
|
switch (current_capture_type)
|
||||||
{
|
{
|
||||||
|
case Core::NetworkCaptureType::PCAP:
|
||||||
|
m_network_logger = std::make_shared<Core::PCAPSSLCaptureLogger>();
|
||||||
|
break;
|
||||||
case Core::NetworkCaptureType::Raw:
|
case Core::NetworkCaptureType::Raw:
|
||||||
m_network_logger = std::make_shared<Core::BinarySSLCaptureLogger>();
|
m_network_logger = std::make_shared<Core::BinarySSLCaptureLogger>();
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -462,8 +462,9 @@ void WiiSocket::Update(bool read, bool write, bool except)
|
||||||
|
|
||||||
if (ret >= 0)
|
if (ret >= 0)
|
||||||
{
|
{
|
||||||
PowerPC::debug_interface.NetworkLogger()->LogWrite(Memory::GetPointer(BufferOut2),
|
PowerPC::debug_interface.NetworkLogger()->LogSSLWrite(
|
||||||
ret);
|
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
|
// Return bytes written or SSL_ERR_ZERO if none
|
||||||
WriteReturnValue((ret == 0) ? SSL_ERR_ZERO : ret, BufferIn);
|
WriteReturnValue((ret == 0) ? SSL_ERR_ZERO : ret, BufferIn);
|
||||||
}
|
}
|
||||||
|
@ -495,7 +496,9 @@ void WiiSocket::Update(bool read, bool write, bool except)
|
||||||
|
|
||||||
if (ret >= 0)
|
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
|
// Return bytes read or SSL_ERR_ZERO if none
|
||||||
WriteReturnValue((ret == 0) ? SSL_ERR_ZERO : ret, BufferIn);
|
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);
|
WiiSockMan::Convert(*wii_name, local_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
const int ret = sendto(fd, data, BufferInSize, flags,
|
auto* to = has_destaddr ? reinterpret_cast<sockaddr*>(&local_name) : nullptr;
|
||||||
has_destaddr ? (struct sockaddr*)&local_name : nullptr,
|
socklen_t tolen = has_destaddr ? sizeof(sockaddr) : 0;
|
||||||
has_destaddr ? sizeof(sockaddr) : 0);
|
const int ret = sendto(fd, data, BufferInSize, flags, to, tolen);
|
||||||
ReturnValue = WiiSockMan::GetNetErrorCode(ret, "SO_SENDTO", true);
|
ReturnValue = WiiSockMan::GetNetErrorCode(ret, "SO_SENDTO", true);
|
||||||
|
if (ret > 0)
|
||||||
|
PowerPC::debug_interface.NetworkLogger()->LogWrite(data, ret, fd, to);
|
||||||
|
|
||||||
INFO_LOG_FMT(IOS_NET,
|
INFO_LOG_FMT(IOS_NET,
|
||||||
"{} = {} Socket: {:08x}, BufferIn: ({:08x}, {}), BufferIn2: ({:08x}, {}), "
|
"{} = {} Socket: {:08x}, BufferIn: ({:08x}, {}), BufferIn2: ({:08x}, {}), "
|
||||||
|
@ -599,11 +604,13 @@ void WiiSocket::Update(bool read, bool write, bool except)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
socklen_t addrlen = sizeof(sockaddr_in);
|
socklen_t addrlen = sizeof(sockaddr_in);
|
||||||
const int ret = recvfrom(fd, data, data_len, flags,
|
auto* from = BufferOutSize2 ? reinterpret_cast<sockaddr*>(&local_name) : nullptr;
|
||||||
BufferOutSize2 ? (struct sockaddr*)&local_name : nullptr,
|
socklen_t* fromlen = BufferOutSize2 ? &addrlen : nullptr;
|
||||||
BufferOutSize2 ? &addrlen : nullptr);
|
const int ret = recvfrom(fd, data, data_len, flags, from, fromlen);
|
||||||
ReturnValue =
|
ReturnValue =
|
||||||
WiiSockMan::GetNetErrorCode(ret, BufferOutSize2 ? "SO_RECVFROM" : "SO_RECV", true);
|
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,
|
INFO_LOG_FMT(IOS_NET,
|
||||||
"{}({}, {}) Socket: {:08X}, Flags: {:08X}, "
|
"{}({}, {}) Socket: {:08X}, Flags: {:08X}, "
|
||||||
|
|
|
@ -4,8 +4,18 @@
|
||||||
|
|
||||||
#include "Core/NetworkCaptureLogger.h"
|
#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/FileUtil.h"
|
||||||
#include "Common/IOFile.h"
|
#include "Common/IOFile.h"
|
||||||
|
#include "Common/Network.h"
|
||||||
|
#include "Common/PcapFile.h"
|
||||||
#include "Core/Config/MainSettings.h"
|
#include "Core/Config/MainSettings.h"
|
||||||
#include "Core/ConfigManager.h"
|
#include "Core/ConfigManager.h"
|
||||||
|
|
||||||
|
@ -14,11 +24,21 @@ namespace Core
|
||||||
NetworkCaptureLogger::NetworkCaptureLogger() = default;
|
NetworkCaptureLogger::NetworkCaptureLogger() = default;
|
||||||
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;
|
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))
|
if (!Config::Get(Config::MAIN_NETWORK_SSL_DUMP_READ))
|
||||||
return;
|
return;
|
||||||
|
@ -36,7 +56,7 @@ void BinarySSLCaptureLogger::LogRead(const void* data, std::size_t length)
|
||||||
File::IOFile(filename, "ab").WriteBytes(data, 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))
|
if (!Config::Get(Config::MAIN_NETWORK_SSL_DUMP_WRITE))
|
||||||
return;
|
return;
|
||||||
|
@ -49,4 +69,152 @@ NetworkCaptureType BinarySSLCaptureLogger::GetCaptureType() const
|
||||||
{
|
{
|
||||||
return NetworkCaptureType::Raw;
|
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(ð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<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
|
} // namespace Core
|
||||||
|
|
|
@ -4,7 +4,25 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <cstddef>
|
#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
|
namespace Core
|
||||||
{
|
{
|
||||||
|
@ -25,24 +43,72 @@ public:
|
||||||
NetworkCaptureLogger& operator=(NetworkCaptureLogger&&) = delete;
|
NetworkCaptureLogger& operator=(NetworkCaptureLogger&&) = delete;
|
||||||
virtual ~NetworkCaptureLogger();
|
virtual ~NetworkCaptureLogger();
|
||||||
|
|
||||||
virtual void LogRead(const void* data, std::size_t length) = 0;
|
virtual void LogSSLRead(const void* data, std::size_t length, s32 socket) = 0;
|
||||||
virtual void LogWrite(const void* data, std::size_t length) = 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;
|
virtual NetworkCaptureType GetCaptureType() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DummyNetworkCaptureLogger : public NetworkCaptureLogger
|
class DummyNetworkCaptureLogger : public NetworkCaptureLogger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void LogRead(const void* data, std::size_t length) override;
|
void LogSSLRead(const void* data, std::size_t length, s32 socket) override;
|
||||||
void LogWrite(const void* data, std::size_t length) 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;
|
NetworkCaptureType GetCaptureType() const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class BinarySSLCaptureLogger final : public NetworkCaptureLogger
|
class BinarySSLCaptureLogger final : public DummyNetworkCaptureLogger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void LogRead(const void* data, std::size_t length) override;
|
void LogSSLRead(const void* data, std::size_t length, s32 socket) override;
|
||||||
void LogWrite(const void* data, std::size_t length) override;
|
void LogSSLWrite(const void* data, std::size_t length, s32 socket) override;
|
||||||
|
|
||||||
NetworkCaptureType GetCaptureType() const 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<Common::PCAP> m_file;
|
||||||
|
u32 m_read_sequence_number = 0;
|
||||||
|
u32 m_write_sequence_number = 0;
|
||||||
|
};
|
||||||
} // namespace Core
|
} // namespace Core
|
||||||
|
|
|
@ -215,6 +215,9 @@ void NetworkWidget::ConnectWidgets()
|
||||||
connect(m_verify_certificates_checkbox, &QCheckBox::stateChanged, [](int state) {
|
connect(m_verify_certificates_checkbox, &QCheckBox::stateChanged, [](int state) {
|
||||||
Config::SetBaseOrCurrent(Config::MAIN_NETWORK_SSL_VERIFY_CERTIFICATES, state == Qt::Checked);
|
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()
|
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_dump_peer_cert_checkbox->setChecked(Config::Get(Config::MAIN_NETWORK_SSL_DUMP_PEER_CERT));
|
||||||
m_verify_certificates_checkbox->setChecked(
|
m_verify_certificates_checkbox->setChecked(
|
||||||
Config::Get(Config::MAIN_NETWORK_SSL_VERIFY_CERTIFICATES));
|
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()
|
QGroupBox* NetworkWidget::CreateSocketTableGroup()
|
||||||
|
@ -317,12 +321,15 @@ QGroupBox* NetworkWidget::CreateSSLOptionsGroup()
|
||||||
m_dump_root_ca_checkbox = new QCheckBox(tr("Dump root CA"));
|
m_dump_root_ca_checkbox = new QCheckBox(tr("Dump root CA"));
|
||||||
m_dump_peer_cert_checkbox = new QCheckBox(tr("Dump peer certificates"));
|
m_dump_peer_cert_checkbox = new QCheckBox(tr("Dump peer certificates"));
|
||||||
m_verify_certificates_checkbox = new QCheckBox(tr("Verify 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_read_checkbox, 0, 0);
|
||||||
ssl_options_layout->addWidget(m_dump_ssl_write_checkbox, 1, 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_verify_certificates_checkbox, 2, 0);
|
||||||
ssl_options_layout->addWidget(m_dump_root_ca_checkbox, 0, 1);
|
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_peer_cert_checkbox, 1, 1);
|
||||||
|
ssl_options_layout->addWidget(m_dump_as_pcap_checkbox, 2, 1);
|
||||||
|
|
||||||
ssl_options_layout->setSpacing(1);
|
ssl_options_layout->setSpacing(1);
|
||||||
return ssl_options_group;
|
return ssl_options_group;
|
||||||
|
|
|
@ -43,4 +43,5 @@ private:
|
||||||
QCheckBox* m_dump_root_ca_checkbox;
|
QCheckBox* m_dump_root_ca_checkbox;
|
||||||
QCheckBox* m_dump_peer_cert_checkbox;
|
QCheckBox* m_dump_peer_cert_checkbox;
|
||||||
QCheckBox* m_verify_certificates_checkbox;
|
QCheckBox* m_verify_certificates_checkbox;
|
||||||
|
QCheckBox* m_dump_as_pcap_checkbox;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue