ddon-server/Arrowgene.Ddon.Server/Network/PacketFactory.cs
2026-03-17 09:29:37 +08:00

356 lines
13 KiB
C#

using System;
using System.Collections.Generic;
using Arrowgene.Buffers;
using Arrowgene.Ddon.Shared;
using Arrowgene.Ddon.Shared.Crypto;
using Arrowgene.Ddon.Shared.Entity;
using Arrowgene.Ddon.Shared.Network;
using Arrowgene.Logging;
using Buffer = System.Buffer;
namespace Arrowgene.Ddon.Server.Network
{
public class PacketFactory
{
private static readonly ServerLogger Logger = LogProvider.Logger<ServerLogger>(typeof(PacketFactory));
private static readonly Camellia Camellia = new Camellia();
private static readonly byte[] CamelliaKey = new byte[]
{
0x66, 0x32, 0x33, 0x65, 0x39, 0x38, 0x48, 0x61, 0x66, 0x4A, 0x64, 0x53, 0x6F, 0x61, 0x6A, 0x38,
0x30, 0x51, 0x42, 0x6A, 0x68, 0x68, 0x32, 0x33, 0x6F, 0x61, 0x6A, 0x67, 0x6B, 0x6C, 0x53, 0x61,
};
private static readonly byte[] CamelliaIv = new byte[]
{
0x24, 0x63, 0x62, 0x4D, 0x36, 0x57, 0x50, 0x29, 0x61, 0x58, 0x3D, 0x25, 0x4A, 0x5E, 0x7A, 0x41
};
private const int PacketLengthFieldSize = 2;
private const int PacketHeaderSize = 9;
private const int PacketMinimumDataSize = 16;
private const uint CamelliaKeyLength = 32 * 8;
private bool _readHeader;
private ushort _dataSize;
private uint _packetCount;
private int _position;
private IBuffer _buffer;
private byte[] _camelliaKey;
private readonly IPacketIdResolver _packetIdResolver;
private readonly Memory<byte> _t8Encrypt;
private readonly Memory<byte> _t8Decrypt;
private Memory<byte> _camelliaSubKey;
private bool _firstPacket;
public PacketFactory(IPacketIdResolver packetIdResolver)
{
_packetCount = 1;
_packetIdResolver = packetIdResolver;
_t8Encrypt = new Memory<byte>(new byte[8]);
_t8Decrypt = new Memory<byte>(new byte[8]);
SetCamelliaKey(CamelliaKey);
_firstPacket = true;
Reset();
}
public void SetCamelliaKey(byte[] camelliaKey)
{
_camelliaKey = Util.Copy(camelliaKey);
Camellia.KeySchedule(_camelliaKey, out _camelliaSubKey);
}
public byte[] WriteDataWithLengthPrefix(byte[] data)
{
if (data == null)
{
Logger.Error($"data == null, tried to write invalid data");
return null;
}
int mod = data.Length % 16;
if (mod > 0)
{
int paddingLength = 16 - mod;
byte[] paddedData = new byte[data.Length + paddingLength];
Buffer.BlockCopy(data, 0, paddedData, 0, data.Length);
data = paddedData;
}
int totalLength = data.Length + PacketLengthFieldSize;
if (totalLength < 0 || totalLength > ushort.MaxValue)
{
Logger.Error($"dataLength < 0 || dataLength > ushort.MaxValue (dataLength:{totalLength})");
return null;
}
byte[] encryptedPacketData = Encrypt(data);
IBuffer buffer = Util.Buffer.Provide();
buffer.WriteUInt16((ushort)encryptedPacketData.Length /* without length prefix */, Endianness.Big);
buffer.WriteBytes(encryptedPacketData);
return buffer.GetAllBytes();
}
public byte[] ReadDataWithLengthPrefix(byte[] packet)
{
if (packet == null || packet.Length < 2)
{
Logger.Error("Packet is null or too short to contain a length prefix.");
return null;
}
IBuffer buffer = new StreamBuffer(packet);
buffer.SetPositionStart();
ushort encryptedLength = buffer.ReadUInt16(Endianness.Big);
if (packet.Length < encryptedLength + 2)
{
Logger.Error($"Incomplete packet: expected {encryptedLength} bytes, but only have {packet.Length - 2}");
return null;
}
byte[] encryptedData = buffer.ReadBytes(encryptedLength);
return Decrypt(encryptedData);
}
public byte[] Write(IPacket packet)
{
packet.Source = PacketSource.Server;
byte[] data = packet.Data;
if (data == null)
{
Logger.Error($"data == null, tried to write invalid data");
return null;
}
// char id replace
//00 20 B8 F8
//data = Util.ReplaceBytes(data, new byte[] {0x00, 0x20, 0xB8, 0xF8}, new byte[] {0x00, 0x00, 0x00, 0x01});
int totalLength = data.Length + PacketLengthFieldSize + PacketHeaderSize;
if (totalLength < 0 || totalLength > ushort.MaxValue)
{
Logger.Error($"dataLength < 0 || dataLength > ushort.MaxValue (dataLength:{totalLength})");
return null;
}
if (true)
{
// temporary solution to attach original packet name to custom packet names
PacketId knownPacketId =
_packetIdResolver.Get(packet.Id.GroupId, packet.Id.HandlerId, packet.Id.HandlerSubId);
if (knownPacketId != PacketId.UNKNOWN
&& !packet.Id.Name.Contains("->")
&& knownPacketId.Name != packet.Id.Name
)
{
string combinedName = knownPacketId.Name + "->" + packet.Id.Name;
packet.Id = new PacketId(packet.Id.GroupId, packet.Id.HandlerId, packet.Id.HandlerSubId,
combinedName, _packetIdResolver.ServerType, packet.Source);
}
}
if (packet.Id.HandlerSubId == 16)
{
// only increase for _NTC packets
packet.Count = _packetCount;
_packetCount++;
}
else
{
packet.Count = 0;
}
IBuffer packetDataBuffer = Util.Buffer.Provide();
packetDataBuffer.WriteByte(packet.Id.GroupId);
packetDataBuffer.WriteUInt16(packet.Id.HandlerId, Endianness.Big);
packetDataBuffer.WriteByte(packet.Id.HandlerSubId);
packetDataBuffer.WriteByte((byte)packet.Source);
packetDataBuffer.WriteUInt32(packet.Count, Endianness.Big);
packetDataBuffer.WriteBytes(data);
// Align packet data to 16 bytes.
var neededBytes = 16 - (packetDataBuffer.Position % 16);
packetDataBuffer.WriteBytes(new byte[neededBytes]);
byte[] packetData = packetDataBuffer.GetAllBytes();
byte[] encryptedPacketData = Encrypt(packetData);
IBuffer buffer = Util.Buffer.Provide();
buffer.WriteUInt16((ushort)encryptedPacketData.Length /* without length prefix */, Endianness.Big);
buffer.WriteBytes(encryptedPacketData);
return buffer.GetAllBytes();
}
public List<IPacket> Read(byte[] data)
{
List<IPacket> packets = new List<IPacket>();
if (_buffer == null)
{
_buffer = Util.Buffer.Provide(data);
}
else
{
_buffer.SetPositionEnd();
_buffer.WriteBytes(data);
}
_buffer.Position = _position;
bool read = true;
while (read)
{
read = false;
if (!_readHeader && _buffer.Size - _buffer.Position >= PacketLengthFieldSize)
{
_dataSize = _buffer.ReadUInt16(Endianness.Big);
if (_dataSize < PacketMinimumDataSize)
{
Logger.Error($"DataSize:{_dataSize} < PacketMinimumDataSize:{PacketMinimumDataSize}");
Reset();
return packets;
}
_readHeader = true;
}
if (_readHeader && _buffer.Size - _buffer.Position >= _dataSize)
{
byte[] encryptedPacketData = _buffer.ReadBytes(_dataSize);
byte[] packetData = Decrypt(encryptedPacketData);
byte[] payload;
PacketId packetId;
uint packetCount;
PacketSource packetSource = PacketSource.Unknown;
if (_firstPacket)
{
/*
* Public Key exchange from server or RSA encrypted payload from client.
* These are always the first packets from client/server and do not contain a traditional header.
*/
if (_packetIdResolver.ServerType == ServerType.Game)
{
packetId = PacketId.C2S_CERT_CLIENT_CHALLENGE_REQ;
}
else if (_packetIdResolver.ServerType == ServerType.Login)
{
packetId = PacketId.C2L_CLIENT_CHALLENGE_REQ;
}
else
{
Logger.Error($"Invalid Server Type");
Reset();
return packets;
}
payload = packetData;
packetSource = PacketSource.Client;
packetCount = 0;
_firstPacket = false;
}
else
{
IBuffer packetBuffer = new StreamBuffer(packetData);
packetBuffer.SetPositionStart();
byte groupId = packetBuffer.ReadByte();
ushort handlerId = packetBuffer.ReadUInt16(Endianness.Big);
byte handlerSubId = packetBuffer.ReadByte();
byte packetSourceByte = packetBuffer.ReadByte();
packetCount = packetBuffer.ReadUInt32(Endianness.Big);
if (Enum.IsDefined(typeof(PacketSource), packetSourceByte))
{
packetSource = (PacketSource)packetSourceByte;
}
if (packetSource != PacketSource.Server
&& packetSource != PacketSource.Client)
{
Logger.Error($"Invalid Server Type");
Reset();
return packets;
}
payload = packetBuffer.ReadBytes(packetBuffer.Size - packetBuffer.Position);
packetId = _packetIdResolver.Get(groupId, handlerId, handlerSubId);
}
Packet packet = new Packet(packetId, payload, packetSource, packetCount);
IStructurePacket structurePacket = EntitySerializer.CreateStructurePacket(packet);
if (structurePacket != null)
{
packets.Add(structurePacket);
}
else
{
packets.Add(packet);
}
_readHeader = false;
read = _buffer.Position != _buffer.Size;
}
else
{
if (_dataSize > PacketMinimumDataSize)
{
// If datasize was evaluated and is a valid size, assume that the data might be split across multiple TCP packets
// reset position to before header was read and hope data arrives with the next packet
_readHeader = false;
_buffer.Position -= 2;
}
}
}
if (_buffer.Position == _buffer.Size)
{
Reset();
}
else
{
_position = _buffer.Position;
}
return packets;
}
public byte[] Encrypt(byte[] data)
{
Camellia.Encrypt(
data,
out Span<byte> encrypted,
CamelliaKeyLength,
_camelliaSubKey.Span,
Util.Copy(CamelliaIv),
_t8Encrypt.Span
);
return encrypted.ToArray();
}
public byte[] Decrypt(byte[] encrypted)
{
Camellia.Decrypt(
encrypted,
out Span<byte> decrypted,
CamelliaKeyLength,
_camelliaSubKey.Span,
Util.Copy(CamelliaIv),
_t8Decrypt.Span
);
return decrypted.ToArray();
}
private void Reset()
{
_readHeader = false;
_dataSize = 0;
_position = 0;
_buffer = null;
}
}
}