@@ -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
@@ -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 @@ class NetworkCaptureLogger
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
@@ -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;
@@ -43,4 +43,5 @@ class NetworkWidget : public QDockWidget
QCheckBox* m_dump_root_ca_checkbox;
QCheckBox* m_dump_peer_cert_checkbox;
QCheckBox* m_verify_certificates_checkbox;
QCheckBox* m_dump_as_pcap_checkbox;
};