EQ2EMu/EQ2/source/common/EQPacket.cpp
image e9cd846d27 Code to investigate issue #163
Code to help identify issue #163
2020-08-11 21:49:56 -04:00

648 lines
16 KiB
C++

/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator 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 3 of the License, or
(at your option) any later version.
EQ2Emulator 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 EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
*/
#include "debug.h"
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <iomanip>
#include "EQPacket.h"
#include "misc.h"
#include "op_codes.h"
#include "CRC16.h"
#include "opcodemgr.h"
#include "packet_dump.h"
#include <map>
#include "Log.h"
#include <time.h>
using namespace std;
extern map<int16,OpcodeManager*>EQOpcodeManager;
uint8 EQApplicationPacket::default_opcode_size=2;
EQPacket::EQPacket(const uint16 op, const unsigned char *buf, uint32 len)
{
this->opcode=op;
this->pBuffer=NULL;
this->size=0;
version = 0;
setTimeInfo(0,0);
if (len>0) {
this->size=len;
pBuffer= new unsigned char[this->size];
if (buf) {
memcpy(this->pBuffer,buf,this->size);
} else {
memset(this->pBuffer,0,this->size);
}
}
}
const char* EQ2Packet::GetOpcodeName() {
int16 OpcodeVersion = GetOpcodeVersion(version);
if (EQOpcodeManager.count(OpcodeVersion) > 0)
return EQOpcodeManager[OpcodeVersion]->EmuToName(login_op);
else
return NULL;
}
int8 EQ2Packet::PreparePacket(int16 MaxLen) {
int16 OpcodeVersion = GetOpcodeVersion(version);
// stops a crash for incorrect version
if (EQOpcodeManager.count(OpcodeVersion) == 0)
{
LogWrite(PACKET__ERROR, 0, "Packet", "Version %i is not listed in the opcodes table.", version);
return -1;
}
packet_prepared = true;
int16 login_opcode = EQOpcodeManager[OpcodeVersion]->EmuToEQ(login_op);
if (login_opcode == 0xcdcd)
{
LogWrite(PACKET__ERROR, 0, "Packet", "Version %i is not listed in the opcodes table for opcode %s", version, EQOpcodeManager[OpcodeVersion]->EmuToName(login_op));
return -1;
}
int8 offset = 0;
//one of the int16s is for the seq, other is for the EQ2 opcode and compressed flag (OP_Packet is the header, not the opcode)
int32 new_size = size + sizeof(int16) + sizeof(int8);
bool oversized = false;
if (login_opcode != 2) {
new_size += sizeof(int8); //for opcode
if (login_opcode >= 255) {
new_size += sizeof(int16);
oversized = true;
}
else
login_opcode = ntohs(login_opcode);
}
uchar* new_buffer = new uchar[new_size];
memset(new_buffer, 0, new_size);
uchar* ptr = new_buffer + sizeof(int16); // sequence is first
if (login_opcode != 2) {
if (oversized) {
ptr += sizeof(int8); //compressed flag
int8 addon = 0xff;
memcpy(ptr, &addon, sizeof(int8));
ptr += sizeof(int8);
}
memcpy(ptr, &login_opcode, sizeof(int16));
ptr += sizeof(int16);
}
else {
memcpy(ptr, &login_opcode, sizeof(int8));
ptr += sizeof(int8);
}
memcpy(ptr, pBuffer, size);
safe_delete_array(pBuffer);
pBuffer = new_buffer;
offset = new_size - size - 1;
size = new_size;
return offset;
}
uint32 EQProtocolPacket::serialize(unsigned char *dest, int8 offset) const
{
if (opcode>0xff) {
*(uint16 *)dest=opcode;
} else {
*(dest)=0;
*(dest+1)=opcode;
}
memcpy(dest+2,pBuffer+offset,size-offset);
return size+2;
}
uint32 EQApplicationPacket::serialize(unsigned char *dest) const
{
uint8 OpCodeBytes = app_opcode_size;
if (app_opcode_size==1)
*(unsigned char *)dest=opcode;
else
{
// Application opcodes with a low order byte of 0x00 require an extra 0x00 byte inserting prior to the opcode.
if ((opcode & 0x00ff) == 0)
{
*(uint8*)dest = 0;
*(uint16*)(dest + 1) = opcode;
++OpCodeBytes;
}
else
*(uint16*)dest = opcode;
}
memcpy(dest+app_opcode_size,pBuffer,size);
return size+ OpCodeBytes;
}
EQPacket::~EQPacket()
{
safe_delete_array(pBuffer);
pBuffer=NULL;
}
void EQPacket::DumpRawHeader(uint16 seq, FILE* to) const
{
/*if (timestamp.tv_sec) {
char temp[20];
tm t;
const time_t sec = timestamp.tv_sec;
localtime_s(&t, &sec);
strftime(temp, 20, "%F %T", &t);
fprintf(to, "%s.%06lu ", temp, timestamp.tv_usec);
}*/
DumpRawHeaderNoTime(seq, to);
}
const char* EQPacket::GetOpcodeName(){
int16 OpcodeVersion = GetOpcodeVersion(version);
if(EQOpcodeManager.count(OpcodeVersion) > 0)
return EQOpcodeManager[OpcodeVersion]->EQToName(opcode);
else
return NULL;
}
void EQPacket::DumpRawHeaderNoTime(uint16 seq, FILE *to) const
{
if (src_ip) {
string sIP,dIP;;
sIP=long2ip(src_ip);
dIP=long2ip(dst_ip);
fprintf(to, "[%s:%d->%s:%d] ",sIP.c_str(),src_port,dIP.c_str(),dst_port);
}
if (seq != 0xffff)
fprintf(to, "[Seq=%u] ",seq);
string name;
int16 OpcodeVersion = GetOpcodeVersion(version);
if(EQOpcodeManager.count(OpcodeVersion) > 0)
name = EQOpcodeManager[OpcodeVersion]->EQToName(opcode);
fprintf(to, "[OpCode 0x%04x (%s) Size=%u]\n",opcode,name.c_str(),size);
}
void EQPacket::DumpRaw(FILE *to) const
{
DumpRawHeader();
if (pBuffer && size)
dump_message_column(pBuffer, size, " ", to);
fprintf(to, "\n");
}
EQProtocolPacket::EQProtocolPacket(const unsigned char *buf, uint32 len, int in_opcode)
{
uint32 offset = 0;
if(in_opcode>=0)
opcode = in_opcode;
else{
offset=2;
opcode=ntohs(*(const uint16 *)buf);
}
if (len-offset) {
pBuffer= new unsigned char[len-offset];
size=len-offset;
if(buf)
memcpy(pBuffer,buf+offset,len-offset);
else
memset(pBuffer,0,size);
} else {
pBuffer=NULL;
size=0;
}
version = 0;
eq2_compressed = false;
packet_prepared = false;
packet_encrypted = false;
sent_time = 0;
attempt_count = 0;
sequence = 0;
}
bool EQ2Packet::AppCombine(EQ2Packet* rhs){
bool result = false;
uchar* tmpbuffer = 0;
bool over_sized_packet = false;
int32 new_size = 0;
//bool whee = false;
// DumpPacket(this);
// DumpPacket(rhs);
/*if(rhs->size >= 255){
DumpPacket(this);
DumpPacket(rhs);
whee = true;
}*/
if (opcode==OP_AppCombined && ((size + rhs->size + 3) < 255)){
int16 tmp_size = rhs->size - 2;
if(tmp_size >= 255){
new_size = size+tmp_size+3;
over_sized_packet = true;
}
else
new_size = size+tmp_size+1;
tmpbuffer = new uchar[new_size];
uchar* ptr = tmpbuffer;
memcpy(ptr, pBuffer, size);
ptr += size;
if(over_sized_packet){
memset(ptr, 255, sizeof(int8));
ptr += sizeof(int8);
tmp_size = htons(tmp_size);
memcpy(ptr, &tmp_size, sizeof(int16));
ptr += sizeof(int16);
}
else{
memcpy(ptr, &tmp_size, sizeof(int8));
ptr += sizeof(int8);
}
memcpy(ptr, rhs->pBuffer+2, rhs->size-2);
delete[] pBuffer;
size = new_size;
pBuffer=tmpbuffer;
safe_delete(rhs);
result=true;
}
else if (rhs->size > 2 && size > 2 && (size + rhs->size + 6) < 255) {
int32 tmp_size = size - 2;
int32 tmp_size2 = rhs->size - 2;
opcode=OP_AppCombined;
bool over_sized_packet2 = false;
new_size = size;
if(tmp_size >= 255){
new_size += 5;
over_sized_packet = true;
}
else
new_size += 3;
if(tmp_size2 >= 255){
new_size += tmp_size2+3;
over_sized_packet2 = true;
}
else
new_size += tmp_size2+1;
tmpbuffer = new uchar[new_size];
tmpbuffer[2]=0;
tmpbuffer[3]=0x19;
uchar* ptr = tmpbuffer+4;
if(over_sized_packet){
memset(ptr, 255, sizeof(int8));
ptr += sizeof(int8);
tmp_size = htons(tmp_size);
memcpy(ptr, &tmp_size, sizeof(int16));
ptr += sizeof(int16);
}
else{
memcpy(ptr, &tmp_size, sizeof(int8));
ptr += sizeof(int8);
}
memcpy(ptr, pBuffer+2, size-2);
ptr += (size-2);
if(over_sized_packet2){
memset(ptr, 255, sizeof(int8));
ptr += sizeof(int8);
tmp_size2 = htons(tmp_size2);
memcpy(ptr, &tmp_size2, sizeof(int16));
ptr += sizeof(int16);
}
else{
memcpy(ptr, &tmp_size2, sizeof(int8));
ptr += sizeof(int8);
}
memcpy(ptr, rhs->pBuffer+2, rhs->size-2);
size = new_size;
delete[] pBuffer;
pBuffer=tmpbuffer;
safe_delete(rhs);
result=true;
}
/*if(whee){
DumpPacket(this);
cout << "fsdfsdf";
}*/
//DumpPacket(this);
return result;
}
bool EQProtocolPacket::combine(const EQProtocolPacket *rhs)
{
bool result=false;
//if(dont_combine)
// return false;
//if (opcode==OP_Combined && size+rhs->size+5<256) {
if (opcode == OP_Combined && size + rhs->size + 5 < 256) {
auto tmpbuffer = new unsigned char[size + rhs->size + 3];
memcpy(tmpbuffer, pBuffer, size);
uint32 offset = size;
tmpbuffer[offset++] = rhs->Size();
offset += rhs->serialize(tmpbuffer + offset);
size = offset;
delete[] pBuffer;
pBuffer = tmpbuffer;
result = true;
}
else if (size + rhs->size + 7 < 256) {
auto tmpbuffer = new unsigned char[size + rhs->size + 6];
uint32 offset = 0;
tmpbuffer[offset++] = Size();
offset += serialize(tmpbuffer + offset);
tmpbuffer[offset++] = rhs->Size();
offset += rhs->serialize(tmpbuffer + offset);
size = offset;
delete[] pBuffer;
pBuffer = tmpbuffer;
opcode = OP_Combined;
result = true;
}
return result;
}
EQApplicationPacket::EQApplicationPacket(const unsigned char *buf, uint32 len, uint8 opcode_size)
{
uint32 offset=0;
app_opcode_size=(opcode_size==0) ? EQApplicationPacket::default_opcode_size : opcode_size;
if (app_opcode_size==1) {
opcode=*(const unsigned char *)buf;
offset++;
} else {
opcode=*(const uint16 *)buf;
offset+=2;
}
if ((len-offset)>0) {
pBuffer=new unsigned char[len-offset];
memcpy(pBuffer,buf+offset,len-offset);
size=len-offset;
} else {
pBuffer=NULL;
size=0;
}
emu_opcode = OP_Unknown;
}
bool EQApplicationPacket::combine(const EQApplicationPacket *rhs)
{
cout << "CALLED AP COMBINE!!!!\n";
return false;
}
void EQApplicationPacket::SetOpcode(EmuOpcode emu_op) {
if(emu_op == OP_Unknown) {
opcode = 0;
emu_opcode = OP_Unknown;
return;
}
opcode = EQOpcodeManager[GetOpcodeVersion(version)]->EmuToEQ(emu_op);
if(opcode == OP_Unknown) {
LogWrite(PACKET__DEBUG, 0, "Packet", "Unable to convert Emu opcode %s (%d) into an EQ opcode.", OpcodeNames[emu_op], emu_op);
}
//save the emu opcode we just set.
emu_opcode = emu_op;
}
const EmuOpcode EQApplicationPacket::GetOpcodeConst() const {
if(emu_opcode != OP_Unknown) {
return(emu_opcode);
}
if(opcode == 10000) {
return(OP_Unknown);
}
EmuOpcode emu_op;
emu_op = EQOpcodeManager[GetOpcodeVersion(version)]->EQToEmu(opcode);
if(emu_op == OP_Unknown) {
LogWrite(PACKET__DEBUG, 1, "Packet", "Unable to convert EQ opcode 0x%.4X (%i) to an emu opcode (%s)", opcode, opcode, __FUNCTION__);
}
return(emu_op);
}
EQApplicationPacket *EQProtocolPacket::MakeApplicationPacket(uint8 opcode_size) const {
EQApplicationPacket *res = new EQApplicationPacket;
res->app_opcode_size=(opcode_size==0) ? EQApplicationPacket::default_opcode_size : opcode_size;
if (res->app_opcode_size==1) {
res->pBuffer= new unsigned char[size+1];
memcpy(res->pBuffer+1,pBuffer,size);
*(res->pBuffer)=htons(opcode)&0xff;
res->opcode=opcode&0xff;
res->size=size+1;
} else {
res->pBuffer= new unsigned char[size];
memcpy(res->pBuffer,pBuffer,size);
res->opcode=opcode;
res->size=size;
}
res->copyInfo(this);
return(res);
}
bool EQProtocolPacket::ValidateCRC(const unsigned char *buffer, int length, uint32 Key)
{
bool valid=false;
// OP_SessionRequest, OP_SessionResponse, OP_OutOfSession are not CRC'd
if (buffer[0]==0x00 && (buffer[1]==OP_SessionRequest || buffer[1]==OP_SessionResponse || buffer[1]==OP_OutOfSession)) {
valid=true;
} else if(buffer[2] == 0x00 && buffer[3] == 0x19){
valid = true;
}
else {
uint16 comp_crc=CRC16(buffer,length-2,Key);
uint16 packet_crc=ntohs(*(const uint16 *)(buffer+length-2));
#ifdef EQN_DEBUG
if (packet_crc && comp_crc != packet_crc) {
cout << "CRC mismatch: comp=" << hex << comp_crc << ", packet=" << packet_crc << dec << endl;
}
#endif
valid = (!packet_crc || comp_crc == packet_crc);
}
return valid;
}
uint32 EQProtocolPacket::Decompress(const unsigned char *buffer, const uint32 length, unsigned char *newbuf, uint32 newbufsize)
{
uint32 newlen=0;
uint32 flag_offset=0;
newbuf[0]=buffer[0];
if (buffer[0]==0x00) {
flag_offset=2;
newbuf[1]=buffer[1];
} else
flag_offset=1;
if (length>2 && buffer[flag_offset]==0x5a) {
LogWrite(PACKET__DEBUG, 0, "Packet", "In Decompress 1");
newlen=Inflate(const_cast<unsigned char *>(buffer+flag_offset+1),length-(flag_offset+1)-2,newbuf+flag_offset,newbufsize-flag_offset)+2;
// something went bad with zlib
if (newlen == -1)
{
LogWrite(PACKET__ERROR, 0, "Packet", "Debug Bad Inflate!");
DumpPacket(buffer, length);
memcpy(newbuf, buffer, length);
return length;
}
newbuf[newlen++]=buffer[length-2];
newbuf[newlen++]=buffer[length-1];
} else if (length>2 && buffer[flag_offset]==0xa5) {
LogWrite(PACKET__DEBUG, 0, "Packet", "In Decompress 2");
memcpy(newbuf+flag_offset,buffer+flag_offset+1,length-(flag_offset+1));
newlen=length-1;
} else {
memcpy(newbuf,buffer,length);
newlen=length;
}
return newlen;
}
uint32 EQProtocolPacket::Compress(const unsigned char *buffer, const uint32 length, unsigned char *newbuf, uint32 newbufsize) {
uint32 flag_offset=1,newlength;
//dump_message_column(buffer,length,"Before: ");
newbuf[0]=buffer[0];
if (buffer[0]==0) {
flag_offset=2;
newbuf[1]=buffer[1];
}
if (length>30) {
newlength=Deflate(const_cast<unsigned char *>(buffer+flag_offset),length-flag_offset,newbuf+flag_offset+1,newbufsize);
*(newbuf+flag_offset)=0x5a;
newlength+=flag_offset+1;
} else {
memmove(newbuf+flag_offset+1,buffer+flag_offset,length-flag_offset);
*(newbuf+flag_offset)=0xa5;
newlength=length+1;
}
//dump_message_column(newbuf,length,"After: ");
return newlength;
}
void EQProtocolPacket::ChatDecode(unsigned char *buffer, int size, int DecodeKey)
{
if (buffer[1]!=0x01 && buffer[0]!=0x02 && buffer[0]!=0x1d) {
int Key=DecodeKey;
unsigned char *test=(unsigned char *)malloc(size);
buffer+=2;
size-=2;
int i;
for (i = 0 ; i+4 <= size ; i+=4)
{
int pt = (*(int*)&buffer[i])^(Key);
Key = (*(int*)&buffer[i]);
*(int*)&test[i]=pt;
}
unsigned char KC=Key&0xFF;
for ( ; i < size ; i++)
{
test[i]=buffer[i]^KC;
}
memcpy(buffer,test,size);
free(test);
}
}
void EQProtocolPacket::ChatEncode(unsigned char *buffer, int size, int EncodeKey)
{
if (buffer[1]!=0x01 && buffer[0]!=0x02 && buffer[0]!=0x1d) {
int Key=EncodeKey;
char *test=(char*)malloc(size);
int i;
buffer+=2;
size-=2;
for ( i = 0 ; i+4 <= size ; i+=4)
{
int pt = (*(int*)&buffer[i])^(Key);
Key = pt;
*(int*)&test[i]=pt;
}
unsigned char KC=Key&0xFF;
for ( ; i < size ; i++)
{
test[i]=buffer[i]^KC;
}
memcpy(buffer,test,size);
free(test);
}
}
bool EQProtocolPacket::IsProtocolPacket(const unsigned char* in_buff, uint32_t len, bool bTrimCRC) {
bool ret = false;
uint16_t opcode = ntohs(*(uint16_t*)in_buff);
uint32_t offset = 2;
switch (opcode) {
case OP_SessionRequest:
case OP_SessionDisconnect:
case OP_KeepAlive:
case OP_SessionStatResponse:
case OP_Packet:
case OP_Combined:
case OP_Fragment:
case OP_Ack:
case OP_OutOfOrderAck:
case OP_OutOfSession:
{
ret = true;
break;
}
}
return ret;
}
void DumpPacketHex(const EQApplicationPacket* app)
{
DumpPacketHex(app->pBuffer, app->size);
}
void DumpPacketAscii(const EQApplicationPacket* app)
{
DumpPacketAscii(app->pBuffer, app->size);
}
void DumpPacket(const EQProtocolPacket* app) {
DumpPacketHex(app->pBuffer, app->size);
}
void DumpPacket(const EQApplicationPacket* app, bool iShowInfo) {
if (iShowInfo) {
cout << "Dumping Applayer: 0x" << hex << setfill('0') << setw(4) << app->GetOpcode() << dec;
cout << " size:" << app->size << endl;
}
DumpPacketHex(app->pBuffer, app->size);
// DumpPacketAscii(app->pBuffer, app->size);
}
void DumpPacketBin(const EQApplicationPacket* app) {
DumpPacketBin(app->pBuffer, app->size);
}