/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see .
*/
#include "PacketLog.h"
#include "Config.h"
#include "GameTime.h"
#include "IpAddress.h"
#include "RealmList.h"
#include "Timer.h"
#include "WorldPacket.h"
#pragma pack(push, 1)
// Packet logging structures in PKT 3.1 format
struct LogHeader
{
char Signature[3];
uint16 FormatVersion;
uint8 SnifferId;
uint32 Build;
char Locale[4];
uint8 SessionKey[40];
uint32 SniffStartUnixtime;
uint32 SniffStartTicks;
uint32 OptionalDataSize;
};
struct PacketHeader
{
// used to uniquely identify a connection
struct OptionalData
{
uint8 SocketIPBytes[16];
uint32 SocketPort;
};
uint32 Direction;
uint32 ConnectionId;
uint32 ArrivalTicks;
uint32 OptionalDataSize;
uint32 Length;
OptionalData OptionalData;
uint32 Opcode;
};
#pragma pack(pop)
PacketLog::PacketLog() : _file(nullptr)
{
std::call_once(_initializeFlag, &PacketLog::Initialize, this);
}
PacketLog::~PacketLog()
{
if (_file)
fclose(_file);
_file = nullptr;
}
PacketLog* PacketLog::instance()
{
static PacketLog instance;
return &instance;
}
void PacketLog::Initialize()
{
std::string logsDir = sConfigMgr->GetStringDefault("LogsDir", "");
if (!logsDir.empty())
if ((logsDir.at(logsDir.length() - 1) != '/') && (logsDir.at(logsDir.length() - 1) != '\\'))
logsDir.push_back('/');
std::string logname = sConfigMgr->GetStringDefault("PacketLogFile", "");
if (!logname.empty())
{
_file = fopen((logsDir + logname).c_str(), "wb");
if (CanLogPacket())
{
LogHeader header;
header.Signature[0] = 'P'; header.Signature[1] = 'K'; header.Signature[2] = 'T';
header.FormatVersion = 0x0301;
header.SnifferId = 'T';
if (std::shared_ptr currentRealm = sRealmList->GetCurrentRealm())
header.Build = currentRealm->Build;
else
header.Build = 0;
header.Locale[0] = 'e'; header.Locale[1] = 'n'; header.Locale[2] = 'U'; header.Locale[3] = 'S';
std::memset(header.SessionKey, 0, sizeof(header.SessionKey));
header.SniffStartUnixtime = GameTime::GetGameTime();
header.SniffStartTicks = getMSTime();
header.OptionalDataSize = 0;
fwrite(&header, sizeof(header), 1, _file);
}
}
}
void PacketLog::LogPacket(WorldPacket const& packet, Direction direction, boost::asio::ip::address const& addr, uint16 port, ConnectionType connectionType)
{
std::lock_guard lock(_logPacketLock);
PacketHeader header;
header.Direction = direction == CLIENT_TO_SERVER ? 0x47534d43 : 0x47534d53;
header.ConnectionId = connectionType;
header.ArrivalTicks = getMSTime();
header.OptionalDataSize = sizeof(header.OptionalData);
memset(header.OptionalData.SocketIPBytes, 0, sizeof(header.OptionalData.SocketIPBytes));
if (addr.is_v4())
{
auto bytes = addr.to_v4().to_bytes();
memcpy(header.OptionalData.SocketIPBytes, bytes.data(), bytes.size());
}
else if (addr.is_v6())
{
auto bytes = addr.to_v6().to_bytes();
memcpy(header.OptionalData.SocketIPBytes, bytes.data(), bytes.size());
}
header.OptionalData.SocketPort = port;
std::size_t size = packet.size();
if (direction == CLIENT_TO_SERVER)
size -= 4;
header.Length = size + sizeof(header.Opcode);
header.Opcode = packet.GetOpcode();
fwrite(&header, sizeof(header), 1, _file);
if (size)
{
uint8 const* data = packet.data();
if (direction == CLIENT_TO_SERVER)
data += 4;
fwrite(data, 1, size, _file);
}
fflush(_file);
}