1
0

upload old login

This commit is contained in:
Sky Johnson 2025-09-01 12:31:14 -05:00
parent f8236efffd
commit 76fa77d590
106 changed files with 11732 additions and 3696 deletions

88
crc16.go Normal file
View File

@ -0,0 +1,88 @@
package eq2net
// CRC16 calculates the CRC-16 checksum for EQ2 packets
// This matches the exact implementation from the original EQ2Emu
func CRC16(buf []byte, size int, key uint32) uint32 {
// CRC32 table used for EQ2's CRC16 calculation
intArray := [256]uint32{
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D,
}
ecx := key
eax := ecx
eax = ^eax
eax &= 0xFF
eax = intArray[eax]
eax ^= 0x00FFFFFF
edx := ecx
edx = edx >> 8
edx = edx ^ eax
eax = eax >> 8
edx &= 0xFF
eax &= 0x00FFFFFF
eax ^= intArray[edx]
edx = ecx
edx = edx >> 0x10
edx ^= eax
eax = eax >> 8
edx &= 0xFF
esi := intArray[edx]
eax &= 0x00FFFFFF
eax ^= esi
ecx = ecx >> 0x18
ecx ^= eax
ecx &= 0xFF
esi = intArray[ecx]
eax = eax >> 8
eax &= 0x00FFFFFF
eax ^= esi
for x := range size {
edx := uint32(0)
edx = uint32(buf[x]) & 0x00FF
edx ^= eax
eax = eax >> 8
edx &= 0xFF
edi := intArray[edx]
eax &= 0x00FFFFFF
eax ^= edi
}
return ^eax
}
// CalculateCRC16 is a convenience wrapper
func CalculateCRC16(data []byte, key uint32) uint16 {
return uint16(CRC16(data, len(data), key))
}

View File

@ -1,661 +0,0 @@
/*
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;
}
int16 orig_opcode = login_opcode;
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 {
// Ensure there are at least 2 bytes for the opcode
if (len < 2 || buf == nullptr) {
// Not enough data to read opcode; set defaults or handle error appropriately
opcode = 0; // or set to a designated invalid opcode
offset = len; // no payload available
} else {
offset = 2;
opcode = ntohs(*(const uint16 *)buf);
}
}
// Check that there is payload data after the header
if (len > offset) {
size = len - offset;
pBuffer = new unsigned char[size];
if(buf)
memcpy(pBuffer, buf + offset, size);
else
memset(pBuffer, 0, size);
} else {
pBuffer = nullptr;
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);
}

View File

@ -1,209 +0,0 @@
/*
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/>.
*/
#ifndef _EQPACKET_H
#define _EQPACKET_H
#include "types.h"
#include <stdio.h>
#include <string.h>
#ifdef WIN32
#include <time.h>
#include <WinSock2.h>
#else
#include <sys/time.h>
#include <netinet/in.h>
#endif
#include "emu_opcodes.h"
#include "op_codes.h"
#include "packet_dump.h"
class OpcodeManager;
class EQStream;
class EQPacket {
friend class EQStream;
public:
unsigned char *pBuffer;
uint32 size;
uint32 src_ip,dst_ip;
uint16 src_port,dst_port;
uint32 priority;
timeval timestamp;
int16 version;
~EQPacket();
void DumpRawHeader(uint16 seq=0xffff, FILE *to = stdout) const;
void DumpRawHeaderNoTime(uint16 seq=0xffff, FILE *to = stdout) const;
void DumpRaw(FILE *to = stdout) const;
const char* GetOpcodeName();
void setVersion(int16 new_version){ version = new_version; }
void setSrcInfo(uint32 sip, uint16 sport) { src_ip=sip; src_port=sport; }
void setDstInfo(uint32 dip, uint16 dport) { dst_ip=dip; dst_port=dport; }
void setTimeInfo(uint32 ts_sec, uint32 ts_usec) { timestamp.tv_sec=ts_sec; timestamp.tv_usec=ts_usec; }
void copyInfo(const EQPacket *p) { src_ip=p->src_ip; src_port=p->src_port; dst_ip=p->dst_ip; dst_port=p->dst_port; timestamp.tv_sec=p->timestamp.tv_sec; timestamp.tv_usec=p->timestamp.tv_usec; }
uint32 Size() const { return size+2; }
//no reason to have this method in zone or world
uint16 GetRawOpcode() const { return(opcode); }
inline bool operator<(const EQPacket &rhs) {
return (timestamp.tv_sec < rhs.timestamp.tv_sec || (timestamp.tv_sec==rhs.timestamp.tv_sec && timestamp.tv_usec < rhs.timestamp.tv_usec));
}
void SetProtocolOpcode(int16 new_opcode){
opcode = new_opcode;
}
protected:
uint16 opcode;
EQPacket(const uint16 op, const unsigned char *buf, const uint32 len);
EQPacket(const EQPacket &p) { version = 0; }
EQPacket() { opcode=0; pBuffer=NULL; size=0; version = 0; setTimeInfo(0, 0); }
};
class EQApplicationPacket;
class EQProtocolPacket : public EQPacket {
public:
EQProtocolPacket(uint16 op, const unsigned char *buf, uint32 len) : EQPacket(op,buf,len) {
eq2_compressed = false;
packet_prepared = false;
packet_encrypted = false;
sequence = 0;
sent_time = 0;
attempt_count = 0;
acked = false;
}
EQProtocolPacket(const unsigned char *buf, uint32 len, int in_opcode = -1);
bool combine(const EQProtocolPacket *rhs);
uint32 serialize (unsigned char *dest, int8 offset = 0) const;
static bool ValidateCRC(const unsigned char *buffer, int length, uint32 Key);
static uint32 Decompress(const unsigned char *buffer, const uint32 length, unsigned char *newbuf, uint32 newbufsize);
static uint32 Compress(const unsigned char *buffer, const uint32 length, unsigned char *newbuf, uint32 newbufsize);
static void ChatDecode(unsigned char *buffer, int size, int DecodeKey);
static void ChatEncode(unsigned char *buffer, int size, int EncodeKey);
static bool IsProtocolPacket(const unsigned char* in_buff, uint32_t len, bool bTrimCRC);
EQProtocolPacket *Copy() {
EQProtocolPacket* new_packet = new EQProtocolPacket(opcode,pBuffer,size);
new_packet->eq2_compressed = this->eq2_compressed;
new_packet->packet_prepared = this->packet_prepared;
new_packet->packet_encrypted = this->packet_encrypted;
return new_packet;
}
EQApplicationPacket *MakeApplicationPacket(uint8 opcode_size=0) const;
bool eq2_compressed;
bool packet_prepared;
bool packet_encrypted;
bool acked;
int32 sent_time;
int8 attempt_count;
int32 sequence;
private:
EQProtocolPacket(const EQProtocolPacket &p) { }
//bool dont_combine;
};
class EQ2Packet : public EQProtocolPacket {
public:
EQ2Packet(const EmuOpcode in_login_op, const unsigned char *buf, uint32 len) : EQProtocolPacket(OP_Packet,buf,len){
login_op = in_login_op;
eq2_compressed = false;
packet_prepared = false;
packet_encrypted = false;
}
bool AppCombine(EQ2Packet* rhs);
EQ2Packet* Copy() {
EQ2Packet* new_packet = new EQ2Packet(login_op,pBuffer,size);
new_packet->eq2_compressed = this->eq2_compressed;
new_packet->packet_prepared = this->packet_prepared;
new_packet->packet_encrypted = this->packet_encrypted;
return new_packet;
}
int8 PreparePacket(int16 MaxLen);
const char* GetOpcodeName();
EmuOpcode login_op;
};
class EQApplicationPacket : public EQPacket {
friend class EQProtocolPacket;
friend class EQStream;
public:
EQApplicationPacket() : EQPacket(0,NULL,0) { emu_opcode = OP_Unknown; app_opcode_size=default_opcode_size; }
EQApplicationPacket(const EmuOpcode op) : EQPacket(0,NULL,0) { SetOpcode(op); app_opcode_size=default_opcode_size; }
EQApplicationPacket(const EmuOpcode op, const uint32 len) : EQPacket(0,NULL,len) { SetOpcode(op); app_opcode_size=default_opcode_size; }
EQApplicationPacket(const EmuOpcode op, const unsigned char *buf, const uint32 len) : EQPacket(0,buf,len) { SetOpcode(op); app_opcode_size=default_opcode_size; }
bool combine(const EQApplicationPacket *rhs);
uint32 serialize (unsigned char *dest) const;
uint32 Size() const { return size+app_opcode_size; }
EQApplicationPacket *Copy() const {
EQApplicationPacket *it = new EQApplicationPacket;
try {
it->pBuffer= new unsigned char[size];
memcpy(it->pBuffer,pBuffer,size);
it->size=size;
it->opcode = opcode;
it->emu_opcode = emu_opcode;
it->version = version;
return(it);
}
catch( bad_alloc &ba )
{
cout << ba.what() << endl;
if( NULL != it )
delete it;
}
return NULL;
}
void SetOpcodeSize(uint8 s) { app_opcode_size=s; }
void SetOpcode(EmuOpcode op);
const EmuOpcode GetOpcodeConst() const;
inline const EmuOpcode GetOpcode() const { return(GetOpcodeConst()); }
//caching version of get
inline const EmuOpcode GetOpcode() { EmuOpcode r = GetOpcodeConst(); emu_opcode = r; return(r); }
static uint8 default_opcode_size;
protected:
//this is just a cache so we dont look it up several times on Get()
EmuOpcode emu_opcode;
private:
//this constructor should only be used by EQProtocolPacket, as it
//assumes the first two bytes of buf are the opcode.
EQApplicationPacket(const unsigned char *buf, uint32 len, uint8 opcode_size=0);
EQApplicationPacket(const EQApplicationPacket &p) { emu_opcode = OP_Unknown; app_opcode_size=default_opcode_size; }
uint8 app_opcode_size;
};
void DumpPacketHex(const EQApplicationPacket* app);
void DumpPacket(const EQProtocolPacket* app);
void DumpPacketAscii(const EQApplicationPacket* app);
void DumpPacket(const EQApplicationPacket* app, bool iShowInfo = false);
void DumpPacketBin(const EQApplicationPacket* app);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,375 +0,0 @@
/*
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/>.
*/
#ifndef _EQPROTOCOL_H
#define _EQPROTOCOL_H
#include <string>
#include <vector>
#include <deque>
#include <queue>
#include <map>
#include <set>
#ifndef WIN32
#include <netinet/in.h>
#endif
#include "EQPacket.h"
#include "Mutex.h"
#include "opcodemgr.h"
#include "misc.h"
#include "Condition.h"
#include "Crypto.h"
#include "zlib.h"
#include "timer.h"
#ifdef WRITE_PACKETS
#include <stdarg.h>
#endif
using namespace std;
typedef enum {
ESTABLISHED,
WAIT_CLOSE,
CLOSING,
DISCONNECTING,
CLOSED
} EQStreamState;
#define FLAG_COMPRESSED 0x01
#define FLAG_ENCODED 0x04
#define RATEBASE 1048576 // 1 MB
#define DECAYBASE 78642 // RATEBASE/10
#ifndef RETRANSMIT_TIMEOUT_MULT
#define RETRANSMIT_TIMEOUT_MULT 3.0
#endif
#ifndef RETRANSMIT_TIMEOUT_MAX
#define RETRANSMIT_TIMEOUT_MAX 5000
#endif
#ifndef AVERAGE_DELTA_MAX
#define AVERAGE_DELTA_MAX 2500
#endif
#pragma pack(1)
struct SessionRequest {
uint32 UnknownA;
uint32 Session;
uint32 MaxLength;
};
struct SessionResponse {
uint32 Session;
uint32 Key;
uint8 UnknownA;
uint8 Format;
uint8 UnknownB;
uint32 MaxLength;
uint32 UnknownD;
};
//Deltas are in ms, representing round trip times
struct ClientSessionStats {
/*000*/ uint16 RequestID;
/*002*/ uint32 last_local_delta;
/*006*/ uint32 average_delta;
/*010*/ uint32 low_delta;
/*014*/ uint32 high_delta;
/*018*/ uint32 last_remote_delta;
/*022*/ uint64 packets_sent;
/*030*/ uint64 packets_recieved;
/*038*/
};
struct ServerSessionStats {
uint16 RequestID;
uint32 current_time;
uint32 unknown1;
uint32 received_packets;
uint32 unknown2;
uint32 sent_packets;
uint32 unknown3;
uint32 sent_packets2;
uint32 unknown4;
uint32 received_packets2;
};
#pragma pack()
class OpcodeManager;
extern OpcodeManager *EQNetworkOpcodeManager;
class EQStreamFactory;
typedef enum {
UnknownStream=0,
LoginStream,
WorldStream,
ZoneStream,
ChatOrMailStream,
ChatStream,
MailStream,
EQ2Stream,
} EQStreamType;
class EQStream {
protected:
typedef enum {
SeqPast,
SeqInOrder,
SeqFuture
} SeqOrder;
uint32 received_packets;
uint32 sent_packets;
uint32 remote_ip;
uint16 remote_port;
uint8 buffer[8192];
unsigned char *oversize_buffer;
uint32 oversize_offset,oversize_length;
unsigned char *rogue_buffer;
uint32 roguebuf_offset,roguebuf_size;
uint8 app_opcode_size;
EQStreamType StreamType;
bool compressed,encoded;
unsigned char write_buffer[2048];
uint32 retransmittimer;
uint32 retransmittimeout;
//uint32 buffer_len;
uint16 sessionAttempts;
uint16 reconnectAttempt;
bool streamactive;
uint32 Session, Key;
uint16 NextInSeq;
uint16 NextOutSeq;
uint16 SequencedBase; //the sequence number of SequencedQueue[0]
uint32 MaxLen;
uint16 MaxSends;
int8 timeout_delays;
uint8 active_users; //how many things are actively using this
Mutex MInUse;
#ifdef WRITE_PACKETS
FILE* write_packets = NULL;
char GetChar(uchar in);
void WriteToFile(char* pFormat, ...);
void WritePackets(const char* opcodeName, uchar* data, int32 size, bool outgoing);
void WritePackets(EQ2Packet* app, bool outgoing);
Mutex MWritePackets;
#endif
EQStreamState State;
Mutex MState;
uint32 LastPacket;
Mutex MVarlock;
EQApplicationPacket* CombinedAppPacket;
Mutex MCombinedAppPacket;
long LastSeqSent;
Mutex MLastSeqSent;
void SetLastSeqSent(uint32);
// Ack sequence tracking.
long MaxAckReceived,NextAckToSend,LastAckSent;
long GetMaxAckReceived();
long GetNextAckToSend();
long GetLastAckSent();
void SetMaxAckReceived(uint32 seq);
void SetNextAckToSend(uint32);
void SetLastAckSent(uint32);
Mutex MAcks;
// Packets waiting to be sent
queue<EQProtocolPacket*> NonSequencedQueue;
deque<EQProtocolPacket*> SequencedQueue;
map<uint16, EQProtocolPacket *> OutOfOrderpackets;
Mutex MOutboundQueue;
// Packes waiting to be processed
deque<EQApplicationPacket *> InboundQueue;
Mutex MInboundQueue;
static uint16 MaxWindowSize;
sint32 BytesWritten;
Mutex MRate;
sint32 RateThreshold;
sint32 DecayRate;
uint32 AverageDelta;
EQStreamFactory *Factory;
public:
Mutex MCombineQueueLock;
bool CheckCombineQueue();
deque<EQ2Packet*> combine_queue;
Timer* combine_timer;
Crypto* crypto;
int8 EQ2_Compress(EQ2Packet* app, int8 offset = 3);
z_stream stream;
uchar* stream_buffer;
int32 stream_buffer_size;
bool eq2_compressed;
int8 compressed_offset;
int16 client_version;
int16 GetClientVersion(){ return client_version; }
void SetClientVersion(int16 version){ client_version = version; }
void ResetSessionAttempts() { reconnectAttempt = 0; }
bool HasSessionAttempts() { return reconnectAttempt>0; }
EQStream() { init(); remote_ip = 0; remote_port = 0; State = CLOSED; StreamType = UnknownStream; compressed = true;
encoded = false; app_opcode_size = 2;}
EQStream(sockaddr_in addr);
virtual ~EQStream() {
MOutboundQueue.lock();
SetState(CLOSED);
MOutboundQueue.unlock();
RemoveData();
safe_delete(crypto);
safe_delete(combine_timer);
safe_delete(resend_que_timer);
safe_delete_array(oversize_buffer);
safe_delete_array(rogue_buffer);
deque<EQ2Packet*>::iterator cmb;
MCombineQueueLock.lock();
for (cmb = combine_queue.begin(); cmb != combine_queue.end(); cmb++){
safe_delete(*cmb);
}
MCombineQueueLock.unlock();
deflateEnd(&stream);
map<int16, EQProtocolPacket*>::iterator oop;
for (oop = OutOfOrderpackets.begin(); oop != OutOfOrderpackets.end(); oop++){
safe_delete(oop->second);
}
#ifdef WRITE_PACKETS
if (write_packets)
fclose(write_packets);
#endif
}
inline void SetFactory(EQStreamFactory *f) { Factory=f; }
void init(bool resetSession = true);
void SetMaxLen(uint32 length) { MaxLen=length; }
int8 getTimeoutDelays(){ return timeout_delays; }
void addTimeoutDelay(){ timeout_delays++; }
void EQ2QueuePacket(EQ2Packet* app, bool attempted_combine = false);
void PreparePacket(EQ2Packet* app, int8 offset = 0);
void UnPreparePacket(EQ2Packet* app);
void EncryptPacket(EQ2Packet* app, int8 compress_offset, int8 offset);
void FlushCombinedPacket();
void SendPacket(EQApplicationPacket *p);
void QueuePacket(EQProtocolPacket *p);
void SendPacket(EQProtocolPacket *p);
vector<EQProtocolPacket *> convert(EQApplicationPacket *p);
void NonSequencedPush(EQProtocolPacket *p);
void SequencedPush(EQProtocolPacket *p);
Mutex MResendQue;
Mutex MCompressData;
deque<EQProtocolPacket*>resend_que;
void CheckResend(int eq_fd);
void AckPackets(uint16 seq);
void Write(int eq_fd);
void SetActive(bool val) { streamactive = val; }
void WritePacket(int fd,EQProtocolPacket *p);
void EncryptPacket(uchar* data, int16 size);
uint32 GetKey() { return Key; }
void SetKey(uint32 k) { Key=k; }
void SetSession(uint32 s) { Session=s; }
void SetLastPacketTime(uint32 t) {LastPacket=t;}
void Process(const unsigned char *data, const uint32 length);
void ProcessPacket(EQProtocolPacket *p, EQProtocolPacket* lastp=NULL);
bool ProcessEmbeddedPacket(uchar* pBuffer, uint16 length, int8 opcode = OP_Packet);
bool HandleEmbeddedPacket(EQProtocolPacket *p, int16 offset = 2, int16 length = 0);
EQProtocolPacket * ProcessEncryptedPacket(EQProtocolPacket *p);
EQProtocolPacket * ProcessEncryptedData(uchar* data, int32 size, int16 opcode);
virtual void DispatchPacket(EQApplicationPacket *p) { p->DumpRaw(); }
void SendSessionResponse();
void SendSessionRequest();
void SendDisconnect(bool setstate = true);
void SendAck(uint16 seq);
void SendOutOfOrderAck(uint16 seq);
bool CheckTimeout(uint32 now, uint32 timeout=30) { return (LastPacket && (now-LastPacket) > timeout); }
bool Stale(uint32 now, uint32 timeout=30) { return (LastPacket && (now-LastPacket) > timeout); }
void InboundQueuePush(EQApplicationPacket *p);
EQApplicationPacket *PopPacket(); // InboundQueuePop
void InboundQueueClear();
void OutboundQueueClear();
bool HasOutgoingData();
void SendKeyRequest();
int16 processRSAKey(EQProtocolPacket *p, uint16 subpacket_length = 0);
void RemoveData() { InboundQueueClear(); OutboundQueueClear(); if (CombinedAppPacket) delete CombinedAppPacket; }
//
inline bool IsInUse() { bool flag; MInUse.lock(); flag=(active_users>0); MInUse.unlock(); return flag; }
inline void PutInUse() { MInUse.lock(); active_users++; MInUse.unlock(); }
inline void ReleaseFromUse() { MInUse.lock(); if(active_users > 0) active_users--; MInUse.unlock(); }
static SeqOrder CompareSequence(uint16 expected_seq, uint16 seq);
inline EQStreamState GetState() { return State; }
inline void SetState(EQStreamState state) { MState.lock(); State = state; MState.unlock(); }
inline uint32 GetRemoteIP() { return remote_ip; }
inline uint32 GetrIP() { return remote_ip; }
inline uint16 GetRemotePort() { return remote_port; }
inline uint16 GetrPort() { return remote_port; }
static EQProtocolPacket *Read(int eq_fd, sockaddr_in *from);
void Close() { SendDisconnect(); }
bool CheckActive() { return (GetState()==ESTABLISHED); }
bool CheckClosed() { return GetState()==CLOSED; }
void SetOpcodeSize(uint8 s) { app_opcode_size = s; }
void SetStreamType(EQStreamType t);
inline const EQStreamType GetStreamType() const { return StreamType; }
void ProcessQueue();
EQProtocolPacket* RemoveQueue(uint16 seq);
void Decay();
void AdjustRates(uint32 average_delta);
Timer* resend_que_timer;
};
#endif

View File

@ -1,444 +0,0 @@
/*
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 "EQStreamFactory.h"
#include "Log.h"
#ifdef WIN32
#include <WinSock2.h>
#include <windows.h>
#include <process.h>
#include <io.h>
#include <stdio.h>
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <pthread.h>
#endif
#include <fcntl.h>
#include <fstream>
#include <iostream>
#include "op_codes.h"
#include "EQStream.h"
#include "packet_dump.h"
#ifdef WORLD
#include "../WorldServer/client.h"
#endif
using namespace std;
#ifdef WORLD
extern ClientList client_list;
#endif
ThreadReturnType EQStreamFactoryReaderLoop(void *eqfs)
{
if(eqfs){
EQStreamFactory *fs=(EQStreamFactory *)eqfs;
fs->ReaderLoop();
}
THREAD_RETURN(NULL);
}
ThreadReturnType EQStreamFactoryWriterLoop(void *eqfs)
{
if(eqfs){
EQStreamFactory *fs=(EQStreamFactory *)eqfs;
fs->WriterLoop();
}
THREAD_RETURN(NULL);
}
ThreadReturnType EQStreamFactoryCombinePacketLoop(void *eqfs)
{
if(eqfs){
EQStreamFactory *fs=(EQStreamFactory *)eqfs;
fs->CombinePacketLoop();
}
THREAD_RETURN(NULL);
}
EQStreamFactory::EQStreamFactory(EQStreamType type, int port)
{
StreamType=type;
Port=port;
listen_ip_address = 0;
}
void EQStreamFactory::Close()
{
CheckTimeout(true);
Stop();
if (sock != -1) {
#ifdef WIN32
closesocket(sock);
#else
close(sock);
#endif
sock = -1;
}
}
bool EQStreamFactory::Open()
{
struct sockaddr_in address;
#ifndef WIN32
pthread_t t1, t2, t3;
#endif
/* Setup internet address information.
This is used with the bind() call */
memset((char *) &address, 0, sizeof(address));
address.sin_family = AF_INET;
address.sin_port = htons(Port);
#if defined(LOGIN) || defined(MINILOGIN)
if(listen_ip_address)
address.sin_addr.s_addr = inet_addr(listen_ip_address);
else
address.sin_addr.s_addr = htonl(INADDR_ANY);
#else
address.sin_addr.s_addr = htonl(INADDR_ANY);
#endif
/* Setting up UDP port for new clients */
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
return false;
}
if (::bind(sock, (struct sockaddr *) &address, sizeof(address)) < 0) {
//close(sock);
sock=-1;
return false;
}
#ifdef WIN32
unsigned long nonblock = 1;
ioctlsocket(sock, FIONBIO, &nonblock);
#else
fcntl(sock, F_SETFL, O_NONBLOCK);
#endif
//moved these because on windows the output was delayed and causing the console window to look bad
#ifdef LOGIN
LogWrite(LOGIN__DEBUG, 0, "Login", "Starting factory Reader");
LogWrite(LOGIN__DEBUG, 0, "Login", "Starting factory Writer");
#elif WORLD
LogWrite(WORLD__DEBUG, 0, "World", "Starting factory Reader");
LogWrite(WORLD__DEBUG, 0, "World", "Starting factory Writer");
#endif
#ifdef WIN32
_beginthread(EQStreamFactoryReaderLoop,0, this);
_beginthread(EQStreamFactoryWriterLoop,0, this);
_beginthread(EQStreamFactoryCombinePacketLoop,0, this);
#else
pthread_create(&t1,NULL,EQStreamFactoryReaderLoop,this);
pthread_create(&t2,NULL,EQStreamFactoryWriterLoop,this);
pthread_create(&t3,NULL,EQStreamFactoryCombinePacketLoop,this);
pthread_detach(t1);
pthread_detach(t2);
pthread_detach(t3);
#endif
return true;
}
EQStream *EQStreamFactory::Pop()
{
if (!NewStreams.size())
return NULL;
EQStream *s=NULL;
//cout << "Pop():Locking MNewStreams" << endl;
MNewStreams.lock();
if (NewStreams.size()) {
s=NewStreams.front();
NewStreams.pop();
s->PutInUse();
}
MNewStreams.unlock();
//cout << "Pop(): Unlocking MNewStreams" << endl;
return s;
}
void EQStreamFactory::Push(EQStream *s)
{
//cout << "Push():Locking MNewStreams" << endl;
MNewStreams.lock();
NewStreams.push(s);
MNewStreams.unlock();
//cout << "Push(): Unlocking MNewStreams" << endl;
}
void EQStreamFactory::ReaderLoop()
{
fd_set readset;
map<string,EQStream *>::iterator stream_itr;
int num;
int length;
unsigned char buffer[2048];
sockaddr_in from;
int socklen=sizeof(sockaddr_in);
timeval sleep_time;
ReaderRunning=true;
while(sock!=-1) {
MReaderRunning.lock();
if (!ReaderRunning)
break;
MReaderRunning.unlock();
FD_ZERO(&readset);
FD_SET(sock,&readset);
sleep_time.tv_sec=30;
sleep_time.tv_usec=0;
if ((num=select(sock+1,&readset,NULL,NULL,&sleep_time))<0) {
// What do we wanna do?
} else if (num==0)
continue;
if (FD_ISSET(sock,&readset)) {
#ifdef WIN32
if ((length=recvfrom(sock,(char*)buffer,sizeof(buffer),0,(struct sockaddr*)&from,(int *)&socklen))<2)
#else
if ((length=recvfrom(sock,buffer,2048,0,(struct sockaddr *)&from,(socklen_t *)&socklen))<2)
#endif
{
// What do we wanna do?
} else {
char temp[25];
sprintf(temp,"%u.%d",ntohl(from.sin_addr.s_addr),ntohs(from.sin_port));
MStreams.lock();
if ((stream_itr=Streams.find(temp))==Streams.end() || buffer[1]==OP_SessionRequest) {
MStreams.unlock();
if (buffer[1]==OP_SessionRequest) {
if(stream_itr != Streams.end() && stream_itr->second)
stream_itr->second->SetState(CLOSED);
EQStream *s=new EQStream(from);
s->SetFactory(this);
s->SetStreamType(StreamType);
Streams[temp]=s;
WriterWork.Signal();
Push(s);
s->Process(buffer,length);
s->SetLastPacketTime(Timer::GetCurrentTime2());
}
} else {
EQStream *curstream = stream_itr->second;
//dont bother processing incoming packets for closed connections
if(curstream->CheckClosed())
curstream = NULL;
else
curstream->PutInUse();
MStreams.unlock();
if(curstream) {
curstream->Process(buffer,length);
curstream->SetLastPacketTime(Timer::GetCurrentTime2());
curstream->ReleaseFromUse();
}
}
}
}
}
}
void EQStreamFactory::CheckTimeout(bool remove_all)
{
//lock streams the entire time were checking timeouts, it should be fast.
MStreams.lock();
unsigned long now=Timer::GetCurrentTime2();
map<string,EQStream *>::iterator stream_itr;
for(stream_itr=Streams.begin();stream_itr!=Streams.end();) {
EQStream *s = stream_itr->second;
EQStreamState state = s->GetState();
if (state==CLOSING && !s->HasOutgoingData()) {
stream_itr->second->SetState(CLOSED);
state = CLOSED;
} else if (s->CheckTimeout(now, STREAM_TIMEOUT)) {
const char* stateString;
switch (state){
case ESTABLISHED:
stateString = "Established";
break;
case CLOSING:
stateString = "Closing";
break;
case CLOSED:
stateString = "Closed";
break;
case WAIT_CLOSE:
stateString = "Wait-Close";
break;
default:
stateString = "Unknown";
break;
}
LogWrite(WORLD__DEBUG, 0, "World", "Timeout up!, state=%s (%u)", stateString, state);
if (state==ESTABLISHED) {
s->Close();
}
else if (state == WAIT_CLOSE) {
s->SetState(CLOSING);
state = CLOSING;
}
else if (state == CLOSING) {
//if we time out in the closing state, just give up
s->SetState(CLOSED);
state = CLOSED;
}
}
//not part of the else so we check it right away on state change
if (remove_all || state==CLOSED) {
if (!remove_all && s->getTimeoutDelays()<2) {
s->addTimeoutDelay();
//give it a little time for everybody to finish with it
} else {
//everybody is done, we can delete it now
#ifdef LOGIN
LogWrite(LOGIN__DEBUG, 0, "Login", "Removing connection...");
#else
LogWrite(WORLD__DEBUG, 0, "World", "Removing connection...");
#endif
map<string,EQStream *>::iterator temp=stream_itr;
stream_itr++;
//let whoever has the stream outside delete it
#ifdef WORLD
client_list.RemoveConnection(temp->second);
#endif
EQStream* stream = temp->second;
Streams.erase(temp);
delete stream;
continue;
}
}
stream_itr++;
}
MStreams.unlock();
}
void EQStreamFactory::CombinePacketLoop(){
deque<EQStream*> combine_que;
CombinePacketRunning = true;
bool packets_waiting = false;
while(sock!=-1) {
if (!CombinePacketRunning)
break;
MStreams.lock();
map<string,EQStream *>::iterator stream_itr;
for(stream_itr=Streams.begin();stream_itr!=Streams.end();stream_itr++) {
if(!stream_itr->second){
continue;
}
if(stream_itr->second->combine_timer && stream_itr->second->combine_timer->Check())
combine_que.push_back(stream_itr->second);
}
EQStream* stream = 0;
packets_waiting = false;
while(combine_que.size()){
stream = combine_que.front();
if(stream->CheckActive()){
if(!stream->CheckCombineQueue())
packets_waiting = true;
}
combine_que.pop_front();
}
MStreams.unlock();
if(!packets_waiting)
Sleep(25);
Sleep(1);
}
}
void EQStreamFactory::WriterLoop()
{
map<string,EQStream *>::iterator stream_itr;
vector<EQStream *> wants_write;
vector<EQStream *>::iterator cur,end;
deque<EQStream*> resend_que;
bool decay=false;
uint32 stream_count;
Timer DecayTimer(20);
WriterRunning=true;
DecayTimer.Enable();
while(sock!=-1) {
Timer::SetCurrentTime();
//if (!havework) {
//WriterWork.Wait();
//}
MWriterRunning.lock();
if (!WriterRunning)
break;
MWriterRunning.unlock();
wants_write.clear();
decay=DecayTimer.Check();
//copy streams into a seperate list so we dont have to keep
//MStreams locked while we are writting
MStreams.lock();
for(stream_itr=Streams.begin();stream_itr!=Streams.end();stream_itr++) {
// If it's time to decay the bytes sent, then let's do it before we try to write
if(!stream_itr->second){
Streams.erase(stream_itr);
break;
}
if (decay)
stream_itr->second->Decay();
if (stream_itr->second->HasOutgoingData()) {
stream_itr->second->PutInUse();
wants_write.push_back(stream_itr->second);
}
if(stream_itr->second->resend_que_timer->Check())
resend_que.push_back(stream_itr->second);
}
MStreams.unlock();
//do the actual writes
cur = wants_write.begin();
end = wants_write.end();
for(; cur != end; cur++) {
(*cur)->Write(sock);
(*cur)->ReleaseFromUse();
}
while(resend_que.size()){
resend_que.front()->CheckResend(sock);
resend_que.pop_front();
}
Sleep(10);
MStreams.lock();
stream_count=Streams.size();
MStreams.unlock();
if (!stream_count) {
//cout << "No streams, waiting on condition" << endl;
WriterWork.Wait();
//cout << "Awake from condition, must have a stream now" << endl;
}
}
}

View File

@ -1,86 +0,0 @@
/*
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/>.
*/
#ifndef _EQSTREAMFACTORY_H
#define _EQSTREAMFACTORY_H
#include <queue>
#include <map>
#include "../common/EQStream.h"
#include "../common/Condition.h"
#include "../common/opcodemgr.h"
#include "../common/timer.h"
#define STREAM_TIMEOUT 45000 //in ms
class EQStreamFactory {
private:
int sock;
int Port;
bool ReaderRunning;
Mutex MReaderRunning;
bool WriterRunning;
Mutex MWriterRunning;
bool CombinePacketRunning;
Mutex MCombinePacketRunning;
Condition WriterWork;
EQStreamType StreamType;
queue<EQStream *> NewStreams;
Mutex MNewStreams;
map<string,EQStream *> Streams;
Mutex MStreams;
Timer *DecayTimer;
public:
char* listen_ip_address;
void CheckTimeout(bool remove_all = false);
EQStreamFactory(EQStreamType type) { ReaderRunning=false; WriterRunning=false; StreamType=type; }
EQStreamFactory(EQStreamType type, int port);
~EQStreamFactory(){
safe_delete_array(listen_ip_address);
}
EQStream *Pop();
void Push(EQStream *s);
bool loadPublicKey();
bool Open();
bool Open(unsigned long port) { Port=port; return Open(); }
void Close();
void ReaderLoop();
void WriterLoop();
void CombinePacketLoop();
void Stop() { StopReader(); StopWriter(); StopCombinePacket(); }
void StopReader() { MReaderRunning.lock(); ReaderRunning=false; MReaderRunning.unlock(); }
void StopWriter() { MWriterRunning.lock(); WriterRunning=false; MWriterRunning.unlock(); WriterWork.Signal(); }
void StopCombinePacket() { MCombinePacketRunning.lock(); CombinePacketRunning=false; MCombinePacketRunning.unlock(); }
void SignalWriter() { WriterWork.Signal(); }
};
#endif

969
old/common/EQPacket.cpp Normal file
View File

@ -0,0 +1,969 @@
// EQ2Emulator: Everquest II Server Emulator
// Copyright (C) 2007 EQ2EMulator Development Team
// Licensed under GPL v3 - see <http://www.gnu.org/licenses/>
#include "debug.h"
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <iomanip>
#include <map>
#include <memory>
#include <time.h>
#include "EQPacket.h"
#include "misc.h"
#include "op_codes.h"
#include "CRC16.h"
#include "opcodemgr.h"
#include "packet_dump.h"
#include "Log.h"
using namespace std;
// Global opcode manager map
extern map<int16, OpcodeManager*> EQOpcodeManager;
// Default opcode size for application packets
uint8 EQApplicationPacket::default_opcode_size = 2;
/**
* EQPacket constructor - creates a packet with specified opcode and data.
*
* @param op - The packet opcode
* @param buf - Source buffer to copy data from (can be nullptr)
* @param len - Length of data to allocate/copy
*/
EQPacket::EQPacket(const uint16 op, const unsigned char* buf, uint32 len)
{
this->opcode = op;
this->pBuffer = nullptr;
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);
}
}
}
/**
* Get the human-readable name of this EQ2 packet's opcode.
*
* @return Opcode name string, or nullptr if not found
*/
const char* EQ2Packet::GetOpcodeName()
{
int16 OpcodeVersion = GetOpcodeVersion(version);
if (EQOpcodeManager.count(OpcodeVersion) > 0) {
return EQOpcodeManager[OpcodeVersion]->EmuToName(login_op);
}
return nullptr;
}
/**
* Prepare an EQ2 packet for transmission by adding protocol headers.
* Converts the emulator opcode to network opcode and adds necessary headers.
*
* @param MaxLen - Maximum allowed packet length
* @return Offset value for the prepared packet, or -1 on error
*/
int8 EQ2Packet::PreparePacket(int16 MaxLen)
{
int16 OpcodeVersion = GetOpcodeVersion(version);
// Validate that we have an opcode manager for this 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;
// Convert emulator opcode to network opcode
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;
// Calculate new size: sequence (int16) + compressed flag (int8) + opcode + data
int32 new_size = size + sizeof(int16) + sizeof(int8);
bool oversized = false;
// Handle different opcode sizes and formats
if (login_opcode != 2) {
new_size += sizeof(int8); // for opcode type
if (login_opcode >= 255) {
new_size += sizeof(int16); // oversized opcode needs extra bytes
oversized = true;
} else {
login_opcode = ntohs(login_opcode);
}
}
// Allocate new buffer and build the packet
uchar* new_buffer = new uchar[new_size];
memset(new_buffer, 0, new_size);
uchar* ptr = new_buffer + sizeof(int16); // skip sequence field
if (login_opcode != 2) {
if (oversized) {
ptr += sizeof(int8); // compressed flag position
int8 addon = 0xff; // oversized marker
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);
}
// Copy original packet data
memcpy(ptr, pBuffer, size);
// Replace old buffer with new prepared buffer
safe_delete_array(pBuffer);
pBuffer = new_buffer;
offset = new_size - size - 1;
size = new_size;
return offset;
}
/**
* Serialize this protocol packet into a destination buffer.
*
* @param dest - Destination buffer to write to
* @param offset - Offset into source buffer to start copying from
* @return Total bytes written to destination
*/
uint32 EQProtocolPacket::serialize(unsigned char* dest, int8 offset) const
{
// Write opcode (2 bytes, handling both 8-bit and 16-bit opcodes)
if (opcode > 0xff) {
*(uint16*)dest = opcode;
} else {
*(dest) = 0;
*(dest + 1) = opcode;
}
// Copy packet data after the opcode
memcpy(dest + 2, pBuffer + offset, size - offset);
return size + 2;
}
/**
* Serialize this application packet into a destination buffer.
* Handles special opcode encoding rules for application-level packets.
*
* @param dest - Destination buffer to write to
* @return Total bytes written to destination
*/
uint32 EQApplicationPacket::serialize(unsigned char* dest) const
{
uint8 OpCodeBytes = app_opcode_size;
if (app_opcode_size == 1) {
// Single-byte opcode
*(unsigned char*)dest = opcode;
} else {
// Two-byte opcode with special encoding rules
// Application opcodes with low byte = 0x00 need extra 0x00 prefix
if ((opcode & 0x00ff) == 0) {
*(uint8*)dest = 0;
*(uint16*)(dest + 1) = opcode;
++OpCodeBytes;
} else {
*(uint16*)dest = opcode;
}
}
// Copy packet data after opcode
memcpy(dest + app_opcode_size, pBuffer, size);
return size + OpCodeBytes;
}
/**
* EQPacket destructor - cleans up allocated buffer memory.
*/
EQPacket::~EQPacket()
{
safe_delete_array(pBuffer);
pBuffer = nullptr;
}
/**
* Dump packet header with timestamp information to file.
*
* @param seq - Sequence number to display
* @param to - File pointer to write to (default: stdout)
*/
void EQPacket::DumpRawHeader(uint16 seq, FILE* to) const
{
// Note: Timestamp formatting code is commented out but preserved
// for potential future use in debugging
/*
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);
}
/**
* Get the human-readable name of this packet's opcode.
*
* @return Opcode name string, or nullptr if not found
*/
const char* EQPacket::GetOpcodeName()
{
int16 OpcodeVersion = GetOpcodeVersion(version);
if (EQOpcodeManager.count(OpcodeVersion) > 0) {
return EQOpcodeManager[OpcodeVersion]->EQToName(opcode);
}
return nullptr;
}
/**
* Dump packet header without timestamp to file.
* Shows network addresses, sequence number, opcode, and size.
*
* @param seq - Sequence number to display (0xffff = don't show)
* @param to - File pointer to write to
*/
void EQPacket::DumpRawHeaderNoTime(uint16 seq, FILE* to) const
{
// Show network addressing if available
if (src_ip) {
string sIP = long2ip(src_ip);
string dIP = long2ip(dst_ip);
fprintf(to, "[%s:%d->%s:%d] ", sIP.c_str(), src_port, dIP.c_str(), dst_port);
}
// Show sequence number if valid
if (seq != 0xffff) {
fprintf(to, "[Seq=%u] ", seq);
}
// Get opcode name for display
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);
}
/**
* Dump complete packet (header + data) to file in formatted columns.
*
* @param to - File pointer to write to (default: stdout)
*/
void EQPacket::DumpRaw(FILE* to) const {
DumpRawHeader();
if (pBuffer && size) {
dump_message_column(pBuffer, size, " ", to);
}
fprintf(to, "\n");
}
/**
* EQProtocolPacket constructor from raw buffer.
* Parses opcode from buffer or uses provided opcode override.
*
* @param buf - Raw packet buffer
* @param len - Length of buffer
* @param in_opcode - Optional opcode override (-1 = parse from buffer)
*/
EQProtocolPacket::EQProtocolPacket(const unsigned char* buf, uint32 len, int in_opcode)
{
uint32 offset = 0;
if (in_opcode >= 0) {
// Use provided opcode override
opcode = in_opcode;
} else {
// Parse opcode from buffer (first 2 bytes)
if (len < 2 || buf == nullptr) {
// Insufficient data - set safe defaults
opcode = 0;
offset = len; // consume entire buffer
} else {
offset = 2;
opcode = ntohs(*(const uint16*)buf);
}
}
// Allocate and copy payload data after opcode
if (len > offset) {
size = len - offset;
pBuffer = new unsigned char[size];
if (buf) {
memcpy(pBuffer, buf + offset, size);
} else {
memset(pBuffer, 0, size);
}
} else {
pBuffer = nullptr;
size = 0;
}
// Initialize protocol packet state
version = 0;
eq2_compressed = false;
packet_prepared = false;
packet_encrypted = false;
sent_time = 0;
attempt_count = 0;
sequence = 0;
}
/**
* Combine this EQ2 packet with another EQ2 packet for efficient transmission.
* Implements EQ2's application-level packet combining protocol.
*
* @param rhs - Right-hand side packet to combine with this one
* @return true if packets were successfully combined, false otherwise
*/
bool EQ2Packet::AppCombine(EQ2Packet* rhs)
{
bool result = false;
uchar* tmpbuffer = nullptr;
bool over_sized_packet = false;
int32 new_size = 0;
// Case 1: This packet is already a combined packet
if (opcode == OP_AppCombined && ((size + rhs->size + 3) < 255)) {
int16 tmp_size = rhs->size - 2; // Subtract opcode bytes
// Check if we need oversized packet encoding
if (tmp_size >= 255) {
new_size = size + tmp_size + 3; // Extra bytes for oversized encoding
over_sized_packet = true;
} else {
new_size = size + tmp_size + 1; // One byte for size prefix
}
tmpbuffer = new uchar[new_size];
uchar* ptr = tmpbuffer;
// Copy existing combined packet data
memcpy(ptr, pBuffer, size);
ptr += size;
// Add size information for the new packet
if (over_sized_packet) {
*ptr++ = 255; // Oversized marker
tmp_size = htons(tmp_size);
memcpy(ptr, &tmp_size, sizeof(int16));
ptr += sizeof(int16);
} else {
*ptr++ = static_cast<uint8>(tmp_size);
}
// Copy packet data (skip opcode)
memcpy(ptr, rhs->pBuffer + 2, rhs->size - 2);
// Replace buffer and clean up
delete[] pBuffer;
size = new_size;
pBuffer = tmpbuffer;
safe_delete(rhs);
result = true;
}
// Case 2: Neither packet is combined - create new combined packet
else if (rhs->size > 2 && size > 2 && (size + rhs->size + 6) < 255) {
int32 tmp_size = size - 2;
int32 tmp_size2 = rhs->size - 2;
bool over_sized_packet2 = false;
// Calculate new size with headers
new_size = 4; // Base combined packet header
// First packet size encoding
if (tmp_size >= 255) {
new_size += tmp_size + 3; // Oversized encoding
over_sized_packet = true;
} else {
new_size += tmp_size + 1; // Normal encoding
}
// Second packet size encoding
if (tmp_size2 >= 255) {
new_size += tmp_size2 + 3; // Oversized encoding
over_sized_packet2 = true;
} else {
new_size += tmp_size2 + 1; // Normal encoding
}
tmpbuffer = new uchar[new_size];
// Set combined packet header
tmpbuffer[2] = 0;
tmpbuffer[3] = 0x19; // Combined packet marker
uchar* ptr = tmpbuffer + 4;
// Add first packet
if (over_sized_packet) {
*ptr++ = 255; // Oversized marker
tmp_size = htons(tmp_size);
memcpy(ptr, &tmp_size, sizeof(int16));
ptr += sizeof(int16);
} else {
*ptr++ = static_cast<uint8>(tmp_size);
}
memcpy(ptr, pBuffer + 2, size - 2);
ptr += (size - 2);
// Add second packet
if (over_sized_packet2) {
*ptr++ = 255; // Oversized marker
tmp_size2 = htons(tmp_size2);
memcpy(ptr, &tmp_size2, sizeof(int16));
ptr += sizeof(int16);
} else {
*ptr++ = static_cast<uint8>(tmp_size2);
}
memcpy(ptr, rhs->pBuffer + 2, rhs->size - 2);
// Replace buffer and update opcode
delete[] pBuffer;
size = new_size;
pBuffer = tmpbuffer;
opcode = OP_AppCombined;
safe_delete(rhs);
result = true;
}
return result;
}
/**
* Combine this protocol packet with another protocol packet.
* Used for efficient transmission of multiple small packets together.
*
* @param rhs - Right-hand side packet to combine with this one
* @return true if packets were successfully combined, false otherwise
*/
bool EQProtocolPacket::combine(const EQProtocolPacket* rhs)
{
bool result = false;
// Case 1: This packet is already combined - append to it
if (opcode == OP_Combined && size + rhs->size + 5 < 256) {
auto tmpbuffer = new unsigned char[size + rhs->size + 3];
// Copy existing combined data
memcpy(tmpbuffer, pBuffer, size);
uint32 offset = size;
// Add size prefix for new packet
tmpbuffer[offset++] = rhs->Size();
// Serialize and append new packet
offset += rhs->serialize(tmpbuffer + offset);
// Update buffer
size = offset;
delete[] pBuffer;
pBuffer = tmpbuffer;
result = true;
}
// Case 2: Neither packet is combined - create new combined packet
else if (size + rhs->size + 7 < 256) {
auto tmpbuffer = new unsigned char[size + rhs->size + 6];
uint32 offset = 0;
// Add first packet with size prefix
tmpbuffer[offset++] = Size();
offset += serialize(tmpbuffer + offset);
// Add second packet with size prefix
tmpbuffer[offset++] = rhs->Size();
offset += rhs->serialize(tmpbuffer + offset);
// Update buffer and mark as combined
size = offset;
delete[] pBuffer;
pBuffer = tmpbuffer;
opcode = OP_Combined;
result = true;
}
return result;
}
/**
* EQApplicationPacket constructor from raw buffer.
* Used by EQProtocolPacket to create application packets from network data.
*
* @param buf - Raw packet buffer (starts with opcode)
* @param len - Length of buffer
* @param opcode_size - Size of opcode in bytes (0 = use default)
*/
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;
// Extract opcode based on size
if (app_opcode_size == 1) {
opcode = *(const unsigned char*)buf;
offset++;
} else {
opcode = *(const uint16*)buf;
offset += 2;
}
// Copy remaining data as payload
if ((len - offset) > 0) {
pBuffer = new unsigned char[len - offset];
memcpy(pBuffer, buf + offset, len - offset);
size = len - offset;
} else {
pBuffer = nullptr;
size = 0;
}
emu_opcode = OP_Unknown;
}
/**
* Combine this application packet with another application packet.
* Currently not implemented for application-level packets.
*
* @param rhs - Right-hand side packet to combine
* @return false (combining not supported at application level)
*/
bool EQApplicationPacket::combine(const EQApplicationPacket* rhs)
{
// Application packet combining is not implemented
// Use protocol-level combining instead
return false;
}
/**
* Set the opcode for this application packet.
* Converts emulator opcode to network protocol opcode.
*
* @param emu_op - Emulator opcode to set
*/
void EQApplicationPacket::SetOpcode(EmuOpcode emu_op)
{
if (emu_op == OP_Unknown) {
opcode = 0;
emu_opcode = OP_Unknown;
return;
}
// Convert emulator opcode to network opcode
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);
}
// Cache the emulator opcode for future lookups
emu_opcode = emu_op;
}
/**
* Get the emulator opcode for this application packet.
* Converts network opcode to emulator opcode if not cached.
*
* @return Emulator opcode constant
*/
const EmuOpcode EQApplicationPacket::GetOpcodeConst() const
{
// Return cached opcode if available
if (emu_opcode != OP_Unknown) {
return emu_opcode;
}
// Handle special invalid opcode case
if (opcode == 10000) {
return OP_Unknown;
}
// Convert network opcode to emulator opcode
EmuOpcode 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;
}
/**
* Convert this protocol packet to an application packet.
* Handles opcode format conversion between protocol and application layers.
*
* @param opcode_size - Size of application opcode (0 = use default)
* @return New application packet, or nullptr on failure
*/
EQApplicationPacket* EQProtocolPacket::MakeApplicationPacket(uint8 opcode_size) const
{
auto res = new EQApplicationPacket;
res->app_opcode_size = (opcode_size == 0) ? EQApplicationPacket::default_opcode_size : opcode_size;
if (res->app_opcode_size == 1) {
// Single-byte opcode format
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 {
// Two-byte opcode format
res->pBuffer = new unsigned char[size];
memcpy(res->pBuffer, pBuffer, size);
res->opcode = opcode;
res->size = size;
}
// Copy network and timing information
res->copyInfo(this);
return res;
}
/**
* Validate the CRC checksum of a network packet.
* Some packet types (session packets) are exempt from CRC validation.
*
* @param buffer - Packet buffer to validate
* @param length - Length of packet buffer
* @param Key - CRC key for validation
* @return true if CRC is valid or packet is exempt, false otherwise
*/
bool EQProtocolPacket::ValidateCRC(const unsigned char* buffer, int length, uint32 Key)
{
bool valid = false;
// Session packets are not CRC protected
if (buffer[0] == 0x00 &&
(buffer[1] == OP_SessionRequest ||
buffer[1] == OP_SessionResponse ||
buffer[1] == OP_OutOfSession)) {
valid = true;
}
// Combined application packets are also exempt
else if (buffer[2] == 0x00 && buffer[3] == 0x19) {
valid = true;
}
// All other packets must have valid CRC
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 if no CRC present (packet_crc == 0) or CRCs match
valid = (!packet_crc || comp_crc == packet_crc);
}
return valid;
}
/**
* Decompress a network packet using zlib or simple encoding.
* Supports both zlib compression (0x5a) and simple encoding (0xa5).
*
* @param buffer - Compressed packet buffer
* @param length - Length of compressed buffer
* @param newbuf - Destination buffer for decompressed data
* @param newbufsize - Size of destination buffer
* @return Length of decompressed data
*/
uint32 EQProtocolPacket::Decompress(const unsigned char* buffer, uint32 length, unsigned char* newbuf, uint32 newbufsize)
{
uint32 newlen = 0;
uint32 flag_offset = 0;
// Copy opcode header
newbuf[0] = buffer[0];
if (buffer[0] == 0x00) {
flag_offset = 2; // Two-byte opcode
newbuf[1] = buffer[1];
} else {
flag_offset = 1; // One-byte opcode
}
// Check compression type
if (length > 2 && buffer[flag_offset] == 0x5a) {
// Zlib compression
LogWrite(PACKET__DEBUG, 0, "Packet", "Decompressing zlib packet");
newlen = Inflate(const_cast<unsigned char*>(buffer + flag_offset + 1),
length - (flag_offset + 1) - 2, // Subtract CRC bytes
newbuf + flag_offset,
newbufsize - flag_offset) + flag_offset;
// Handle decompression failure
if (newlen == static_cast<uint32>(-1)) {
LogWrite(PACKET__ERROR, 0, "Packet", "Zlib decompression failed!");
DumpPacket(buffer, length);
// Fallback: copy original buffer
memcpy(newbuf, buffer, length);
return length;
}
// Copy CRC bytes to end
newbuf[newlen++] = buffer[length - 2];
newbuf[newlen++] = buffer[length - 1];
} else if (length > 2 && buffer[flag_offset] == 0xa5) {
// Simple encoding - just remove the encoding flag
LogWrite(PACKET__DEBUG, 0, "Packet", "Decompressing simple encoded packet");
memcpy(newbuf + flag_offset, buffer + flag_offset + 1, length - (flag_offset + 1));
newlen = length - 1;
} else {
// No compression - direct copy
memcpy(newbuf, buffer, length);
newlen = length;
}
return newlen;
}
/**
* Compress a network packet using zlib or simple encoding.
* Uses zlib for packets > 30 bytes, simple encoding for smaller packets.
*
* @param buffer - Source packet buffer
* @param length - Length of source buffer
* @param newbuf - Destination buffer for compressed data
* @param newbufsize - Size of destination buffer
* @return Length of compressed data
*/
uint32 EQProtocolPacket::Compress(const unsigned char* buffer, uint32 length, unsigned char* newbuf, uint32 newbufsize)
{
uint32 flag_offset = 1;
uint32 newlength;
// Copy opcode header
newbuf[0] = buffer[0];
if (buffer[0] == 0) {
flag_offset = 2; // Two-byte opcode
newbuf[1] = buffer[1];
}
// Choose compression method based on packet size
if (length > 30) {
// Use zlib compression for larger packets
newlength = Deflate(const_cast<unsigned char*>(buffer + flag_offset),
length - flag_offset,
newbuf + flag_offset + 1,
newbufsize);
*(newbuf + flag_offset) = 0x5a; // Zlib compression flag
newlength += flag_offset + 1;
} else {
// Use simple encoding for smaller packets
memmove(newbuf + flag_offset + 1, buffer + flag_offset, length - flag_offset);
*(newbuf + flag_offset) = 0xa5; // Simple encoding flag
newlength = length + 1;
}
return newlength;
}
/**
* Decode chat packet data using XOR encryption.
* Uses a rolling XOR key that updates with each 4-byte block.
*
* @param buffer - Buffer containing chat data to decode
* @param size - Size of buffer
* @param DecodeKey - Initial decoding key
*/
void EQProtocolPacket::ChatDecode(unsigned char* buffer, int size, int DecodeKey)
{
// Skip decoding for certain packet types
if (buffer[1] != 0x01 && buffer[0] != 0x02 && buffer[0] != 0x1d) {
int Key = DecodeKey;
auto test = static_cast<unsigned char*>(malloc(size));
// Skip the first 2 bytes (opcode)
buffer += 2;
size -= 2;
// Decode 4-byte blocks with rolling key
int i;
for (i = 0; i + 4 <= size; i += 4) {
int pt = (*(int*)&buffer[i]) ^ Key;
Key = (*(int*)&buffer[i]); // Update key with encrypted data
*(int*)&test[i] = pt;
}
// Decode remaining bytes with last key byte
unsigned char KC = Key & 0xFF;
for (; i < size; i++) {
test[i] = buffer[i] ^ KC;
}
// Copy decoded data back
memcpy(buffer, test, size);
free(test);
}
}
/**
* Encode chat packet data using XOR encryption.
* Uses a rolling XOR key that updates with each encrypted 4-byte block.
*
* @param buffer - Buffer containing chat data to encode
* @param size - Size of buffer
* @param EncodeKey - Initial encoding key
*/
void EQProtocolPacket::ChatEncode(unsigned char* buffer, int size, int EncodeKey)
{
// Skip encoding for certain packet types
if (buffer[1] != 0x01 && buffer[0] != 0x02 && buffer[0] != 0x1d) {
int Key = EncodeKey;
auto test = static_cast<char*>(malloc(size));
// Skip the first 2 bytes (opcode)
buffer += 2;
size -= 2;
// Encode 4-byte blocks with rolling key
int i;
for (i = 0; i + 4 <= size; i += 4) {
int pt = (*(int*)&buffer[i]) ^ Key;
Key = pt; // Update key with encrypted data
*(int*)&test[i] = pt;
}
// Encode remaining bytes with last key byte
unsigned char KC = Key & 0xFF;
for (; i < size; i++) {
test[i] = buffer[i] ^ KC;
}
// Copy encoded data back
memcpy(buffer, test, size);
free(test);
}
}
/**
* Check if a buffer contains a valid EverQuest protocol packet.
* Validates the opcode against known protocol opcodes.
*
* @param in_buff - Input buffer to check
* @param len - Length of input buffer
* @param bTrimCRC - Whether CRC should be trimmed (unused)
* @return true if buffer contains a valid protocol packet
*/
bool EQProtocolPacket::IsProtocolPacket(const unsigned char* in_buff, uint32_t len, bool bTrimCRC)
{
bool ret = false;
uint16_t opcode = ntohs(*(uint16_t*)in_buff);
// Check against known protocol opcodes
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;
default:
// Unknown opcode - not a protocol packet
ret = false;
break;
}
return ret;
}
/**
* Dump application packet data in hexadecimal format.
*
* @param app - Application packet to dump
*/
void DumpPacketHex(const EQApplicationPacket* app)
{
DumpPacketHex(app->pBuffer, app->size);
}
/**
* Dump application packet data in ASCII format.
*
* @param app - Application packet to dump
*/
void DumpPacketAscii(const EQApplicationPacket* app)
{
DumpPacketAscii(app->pBuffer, app->size);
}
/**
* Dump protocol packet data in hexadecimal format.
*
* @param app - Protocol packet to dump
*/
void DumpPacket(const EQProtocolPacket* app)
{
DumpPacketHex(app->pBuffer, app->size);
}
/**
* Dump application packet with optional header information.
*
* @param app - Application packet to dump
* @param iShowInfo - Whether to show packet information header
*/
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);
// ASCII dump is commented out but available:
// DumpPacketAscii(app->pBuffer, app->size);
}
/**
* Dump application packet data in binary format.
*
* @param app - Application packet to dump
*/
void DumpPacketBin(const EQApplicationPacket* app)
{
DumpPacketBin(app->pBuffer, app->size);
}

310
old/common/EQPacket.h Normal file
View File

@ -0,0 +1,310 @@
// EQ2Emulator: Everquest II Server Emulator
// Copyright (C) 2007 EQ2EMulator Development Team
// Licensed under GPL v3 - see <http://www.gnu.org/licenses/>
#ifndef _EQPACKET_H
#define _EQPACKET_H
#include "types.h"
#include <stdio.h>
#include <string.h>
#include <memory>
#ifdef WIN32
#include <time.h>
#include <WinSock2.h>
#else
#include <sys/time.h>
#include <netinet/in.h>
#endif
#include "emu_opcodes.h"
#include "op_codes.h"
#include "packet_dump.h"
// Forward declarations
class OpcodeManager;
class EQStream;
/**
* Base class for all EverQuest packet types.
* Provides common functionality for packet management, opcode handling,
* and network information tracking.
*/
class EQPacket {
friend class EQStream;
public:
// Packet data
unsigned char* pBuffer; // Raw packet data buffer
uint32 size; // Size of packet data
// Network information
uint32 src_ip, dst_ip; // Source and destination IP addresses
uint16 src_port, dst_port; // Source and destination ports
uint32 priority; // Packet priority for processing
timeval timestamp; // Packet timestamp
int16 version; // Protocol version
~EQPacket();
// Debug and display methods
void DumpRawHeader(uint16 seq = 0xffff, FILE* to = stdout) const;
void DumpRawHeaderNoTime(uint16 seq = 0xffff, FILE* to = stdout) const;
void DumpRaw(FILE* to = stdout) const;
const char* GetOpcodeName();
// Packet information setters
void setVersion(int16 new_version) { version = new_version; }
void setSrcInfo(uint32 sip, uint16 sport) { src_ip = sip; src_port = sport; }
void setDstInfo(uint32 dip, uint16 dport) { dst_ip = dip; dst_port = dport; }
void setTimeInfo(uint32 ts_sec, uint32 ts_usec) {
timestamp.tv_sec = ts_sec;
timestamp.tv_usec = ts_usec;
}
// Copy network and timing info from another packet
void copyInfo(const EQPacket* p) {
src_ip = p->src_ip;
src_port = p->src_port;
dst_ip = p->dst_ip;
dst_port = p->dst_port;
timestamp.tv_sec = p->timestamp.tv_sec;
timestamp.tv_usec = p->timestamp.tv_usec;
}
// Get total packet size including opcode
uint32 Size() const { return size + 2; }
// Get raw opcode value
uint16 GetRawOpcode() const { return opcode; }
// Comparison operator for timestamp-based sorting
bool operator<(const EQPacket& rhs) const {
return (timestamp.tv_sec < rhs.timestamp.tv_sec ||
(timestamp.tv_sec == rhs.timestamp.tv_sec &&
timestamp.tv_usec < rhs.timestamp.tv_usec));
}
// Set the protocol opcode
void SetProtocolOpcode(int16 new_opcode) {
opcode = new_opcode;
}
protected:
uint16 opcode; // Packet opcode
// Constructors (protected to enforce proper inheritance)
EQPacket(uint16 op, const unsigned char* buf, uint32 len);
EQPacket(const EQPacket& p) { version = 0; }
EQPacket() {
opcode = 0;
pBuffer = nullptr;
size = 0;
version = 0;
setTimeInfo(0, 0);
}
};
// Forward declaration
class EQApplicationPacket;
/**
* Protocol-level packet class for EverQuest network communication.
* Handles low-level protocol features like compression, encryption,
* sequencing, and packet combining.
*/
class EQProtocolPacket : public EQPacket {
public:
// Constructor with opcode and buffer
EQProtocolPacket(uint16 op, const unsigned char* buf, uint32 len)
: EQPacket(op, buf, len) {
eq2_compressed = false;
packet_prepared = false;
packet_encrypted = false;
sequence = 0;
sent_time = 0;
attempt_count = 0;
acked = false;
}
// Constructor from raw buffer with optional opcode override
EQProtocolPacket(const unsigned char* buf, uint32 len, int in_opcode = -1);
// Packet manipulation methods
bool combine(const EQProtocolPacket* rhs);
uint32 serialize(unsigned char* dest, int8 offset = 0) const;
// Static utility methods for packet processing
static bool ValidateCRC(const unsigned char* buffer, int length, uint32 Key);
static uint32 Decompress(const unsigned char* buffer, uint32 length,
unsigned char* newbuf, uint32 newbufsize);
static uint32 Compress(const unsigned char* buffer, uint32 length,
unsigned char* newbuf, uint32 newbufsize);
static void ChatDecode(unsigned char* buffer, int size, int DecodeKey);
static void ChatEncode(unsigned char* buffer, int size, int EncodeKey);
static bool IsProtocolPacket(const unsigned char* in_buff, uint32_t len, bool bTrimCRC);
// Create a copy of this packet
EQProtocolPacket* Copy() {
auto new_packet = new EQProtocolPacket(opcode, pBuffer, size);
new_packet->eq2_compressed = this->eq2_compressed;
new_packet->packet_prepared = this->packet_prepared;
new_packet->packet_encrypted = this->packet_encrypted;
return new_packet;
}
// Convert to application-level packet
EQApplicationPacket* MakeApplicationPacket(uint8 opcode_size = 0) const;
// Protocol packet state flags
bool eq2_compressed; // Packet is compressed
bool packet_prepared; // Packet has been prepared for sending
bool packet_encrypted; // Packet is encrypted
bool acked; // Packet has been acknowledged
// Reliability and sequencing
int32 sent_time; // Timestamp when packet was sent
int8 attempt_count; // Number of send attempts
int32 sequence; // Sequence number for ordering
private:
// Prevent copy construction
EQProtocolPacket(const EQProtocolPacket& p) = delete;
EQProtocolPacket& operator=(const EQProtocolPacket& p) = delete;
};
/**
* EverQuest 2 specific packet class.
* Handles EQ2-specific packet operations like application combining
* and login opcode management.
*/
class EQ2Packet : public EQProtocolPacket {
public:
// Constructor with EQ2 login opcode
EQ2Packet(EmuOpcode in_login_op, const unsigned char* buf, uint32 len)
: EQProtocolPacket(OP_Packet, buf, len) {
login_op = in_login_op;
eq2_compressed = false;
packet_prepared = false;
packet_encrypted = false;
}
// EQ2-specific packet operations
bool AppCombine(EQ2Packet* rhs);
int8 PreparePacket(int16 MaxLen);
const char* GetOpcodeName();
// Create a copy of this EQ2 packet
EQ2Packet* Copy() {
auto new_packet = new EQ2Packet(login_op, pBuffer, size);
new_packet->eq2_compressed = this->eq2_compressed;
new_packet->packet_prepared = this->packet_prepared;
new_packet->packet_encrypted = this->packet_encrypted;
return new_packet;
}
EmuOpcode login_op; // EQ2 login/application opcode
};
/**
* Application-level packet class for EverQuest.
* Handles high-level game opcodes and application data.
* This is the main packet type used by game logic.
*/
class EQApplicationPacket : public EQPacket {
friend class EQProtocolPacket;
friend class EQStream;
public:
// Default constructor
EQApplicationPacket() : EQPacket(0, nullptr, 0) {
emu_opcode = OP_Unknown;
app_opcode_size = default_opcode_size;
}
// Constructor with opcode only
EQApplicationPacket(EmuOpcode op) : EQPacket(0, nullptr, 0) {
SetOpcode(op);
app_opcode_size = default_opcode_size;
}
// Constructor with opcode and size
EQApplicationPacket(EmuOpcode op, uint32 len) : EQPacket(0, nullptr, len) {
SetOpcode(op);
app_opcode_size = default_opcode_size;
}
// Constructor with opcode, buffer, and size
EQApplicationPacket(EmuOpcode op, const unsigned char* buf, uint32 len)
: EQPacket(0, buf, len) {
SetOpcode(op);
app_opcode_size = default_opcode_size;
}
// Packet operations
bool combine(const EQApplicationPacket* rhs);
uint32 serialize(unsigned char* dest) const;
uint32 Size() const { return size + app_opcode_size; }
// Create a deep copy of this packet
EQApplicationPacket* Copy() const {
auto it = std::make_unique<EQApplicationPacket>();
try {
if (size > 0) {
it->pBuffer = new unsigned char[size];
memcpy(it->pBuffer, pBuffer, size);
}
it->size = size;
it->opcode = opcode;
it->emu_opcode = emu_opcode;
it->version = version;
return it.release();
}
catch (const std::bad_alloc& ba) {
// Memory allocation failed - return nullptr
return nullptr;
}
}
// Opcode management
void SetOpcodeSize(uint8 s) { app_opcode_size = s; }
void SetOpcode(EmuOpcode op);
const EmuOpcode GetOpcodeConst() const;
// Get opcode (const version)
const EmuOpcode GetOpcode() const { return GetOpcodeConst(); }
// Get opcode (caching version for performance)
EmuOpcode GetOpcode() {
EmuOpcode r = GetOpcodeConst();
emu_opcode = r;
return r;
}
// Default opcode size for all application packets
static uint8 default_opcode_size;
protected:
// Cached emulator opcode to avoid repeated lookups
EmuOpcode emu_opcode;
private:
// Constructor used by EQProtocolPacket - assumes first bytes are opcode
EQApplicationPacket(const unsigned char* buf, uint32 len, uint8 opcode_size = 0);
// Prevent copy construction
EQApplicationPacket(const EQApplicationPacket& p) = delete;
EQApplicationPacket& operator=(const EQApplicationPacket& p) = delete;
// Size of the opcode in bytes (1 or 2)
uint8 app_opcode_size;
};
// Packet debugging and display functions
void DumpPacketHex(const EQApplicationPacket* app);
void DumpPacket(const EQProtocolPacket* app);
void DumpPacketAscii(const EQApplicationPacket* app);
void DumpPacket(const EQApplicationPacket* app, bool iShowInfo = false);
void DumpPacketBin(const EQApplicationPacket* app);
#endif

2680
old/common/EQStream.cpp Normal file

File diff suppressed because it is too large Load Diff

523
old/common/EQStream.h Normal file
View File

@ -0,0 +1,523 @@
// EQ2Emulator: Everquest II Server Emulator
// Copyright (C) 2007 EQ2EMulator Development Team
// Licensed under GPL v3 - see <http://www.gnu.org/licenses/>
#ifndef _EQPROTOCOL_H
#define _EQPROTOCOL_H
#include <string>
#include <vector>
#include <deque>
#include <queue>
#include <map>
#include <set>
#include <memory>
#include <cstdint>
// Unix networking headers
#include <netinet/in.h>
// Project headers
#include "EQPacket.h"
#include "Mutex.h"
#include "opcodemgr.h"
#include "misc.h"
#include "Condition.h"
#include "Crypto.h"
#include "zlib.h"
#include "timer.h"
#ifdef WRITE_PACKETS
#include <stdarg.h>
#endif
using namespace std;
// Forward declarations
class OpcodeManager;
class EQStreamFactory;
/**
* EverQuest stream connection states.
* Represents the current state of a network stream connection.
*/
typedef enum {
ESTABLISHED, // Active connection ready for data
WAIT_CLOSE, // Waiting for graceful close
CLOSING, // In process of closing
DISCONNECTING, // Actively disconnecting
CLOSED // Connection fully closed
} EQStreamState;
// Packet flags
#define FLAG_COMPRESSED 0x01 // Packet is compressed
#define FLAG_ENCODED 0x04 // Packet is encoded/encrypted
// Rate limiting and bandwidth constants
#define RATEBASE 1048576 // Base rate: 1 MB
#define DECAYBASE 78642 // Decay rate: RATEBASE/10
// Retransmission timing constants
#ifndef RETRANSMIT_TIMEOUT_MULT
#define RETRANSMIT_TIMEOUT_MULT 3.0 // Timeout multiplier
#endif
#ifndef RETRANSMIT_TIMEOUT_MAX
#define RETRANSMIT_TIMEOUT_MAX 5000 // Maximum retransmit timeout (ms)
#endif
#ifndef AVERAGE_DELTA_MAX
#define AVERAGE_DELTA_MAX 2500 // Maximum average delta (ms)
#endif
#pragma pack(1)
/**
* Session request packet structure.
* Sent by client to initiate a new session.
*/
struct SessionRequest {
uint32 UnknownA; // Unknown field A
uint32 Session; // Requested session ID
uint32 MaxLength; // Maximum packet length
};
/**
* Session response packet structure.
* Sent by server in response to session request.
*/
struct SessionResponse {
uint32 Session; // Assigned session ID
uint32 Key; // Encryption/authentication key
uint8 UnknownA; // Unknown field A
uint8 Format; // Packet format version
uint8 UnknownB; // Unknown field B
uint32 MaxLength; // Maximum packet length
uint32 UnknownD; // Unknown field D
};
/**
* Client-side session statistics.
* Deltas are in milliseconds, representing round trip times.
*/
struct ClientSessionStats {
/*000*/ uint16 RequestID; // Statistics request ID
/*002*/ uint32 last_local_delta; // Last local round trip time
/*006*/ uint32 average_delta; // Average round trip time
/*010*/ uint32 low_delta; // Lowest recorded round trip time
/*014*/ uint32 high_delta; // Highest recorded round trip time
/*018*/ uint32 last_remote_delta; // Last remote round trip time
/*022*/ uint64 packets_sent; // Total packets sent
/*030*/ uint64 packets_recieved; // Total packets received
/*038*/
};
/**
* Server-side session statistics.
* Provides server perspective on connection quality.
*/
struct ServerSessionStats {
uint16 RequestID; // Statistics request ID
uint32 current_time; // Current server time
uint32 unknown1; // Unknown field 1
uint32 received_packets; // Packets received by server
uint32 unknown2; // Unknown field 2
uint32 sent_packets; // Packets sent by server
uint32 unknown3; // Unknown field 3
uint32 sent_packets2; // Duplicate sent packets count
uint32 unknown4; // Unknown field 4
uint32 received_packets2; // Duplicate received packets count
};
#pragma pack()
// External opcode manager
extern OpcodeManager* EQNetworkOpcodeManager;
/**
* Types of EverQuest network streams.
* Each type handles different aspects of the game protocol.
*/
typedef enum {
UnknownStream = 0, // Unidentified stream type
LoginStream, // Login server authentication
WorldStream, // World server communication
ZoneStream, // Zone server gameplay
ChatOrMailStream, // Combined chat/mail (legacy)
ChatStream, // Chat system only
MailStream, // Mail system only
EQ2Stream, // EverQuest 2 specific stream
} EQStreamType;
/**
* EverQuest network stream class.
* Handles reliable UDP communication with sequence numbers, acknowledgments,
* compression, encryption, and packet combining for EverQuest protocols.
*/
class EQStream {
protected:
/**
* Sequence number ordering enumeration.
* Used to determine packet sequence relationships.
*/
typedef enum {
SeqPast, // Sequence is from the past (duplicate/old)
SeqInOrder, // Sequence is the expected next sequence
SeqFuture // Sequence is from the future (out of order)
} SeqOrder;
// Packet statistics
uint32 received_packets; // Total packets received
uint32 sent_packets; // Total packets sent
// Remote endpoint information
uint32 remote_ip; // Remote IP address
uint16 remote_port; // Remote port number
// Packet buffers
uint8 buffer[8192]; // Main packet buffer
unsigned char* oversize_buffer; // Buffer for oversized packets
uint32 oversize_offset; // Offset in oversize buffer
uint32 oversize_length; // Length of oversize buffer data
unsigned char* rogue_buffer; // Buffer for rogue/malformed packets
uint32 roguebuf_offset; // Offset in rogue buffer
uint32 roguebuf_size; // Size of rogue buffer
// Protocol configuration
uint8 app_opcode_size; // Size of application opcodes
EQStreamType StreamType; // Type of this stream
bool compressed; // Stream supports compression
bool encoded; // Stream supports encoding/encryption
// Write buffer for outgoing packets
unsigned char write_buffer[2048];
// Retransmission timing
uint32 retransmittimer; // Current retransmit timer
uint32 retransmittimeout; // Retransmit timeout value
// Session management
uint16 sessionAttempts; // Number of session attempts
uint16 reconnectAttempt; // Number of reconnect attempts
bool streamactive; // Stream is actively connected
// Session state
uint32 Session; // Session ID
uint32 Key; // Session encryption key
uint16 NextInSeq; // Next expected incoming sequence
uint16 NextOutSeq; // Next outgoing sequence number
uint16 SequencedBase; // Base sequence for SequencedQueue[0]
uint32 MaxLen; // Maximum packet length
uint16 MaxSends; // Maximum send attempts
int8 timeout_delays; // Number of timeout delays accumulated
// Thread safety for stream usage
uint8 active_users; // Number of active users of this stream
Mutex MInUse; // Mutex for usage tracking
#ifdef WRITE_PACKETS
// Packet logging for debugging
FILE* write_packets = nullptr; // File handle for packet dumps
char GetChar(uchar in); // Convert byte to printable character
void WriteToFile(char* pFormat, ...); // Write formatted data to file
void WritePackets(const char* opcodeName, uchar* data, int32 size, bool outgoing);
void WritePackets(EQ2Packet* app, bool outgoing);
Mutex MWritePackets; // Mutex for packet writing
#endif
// Stream connection state
EQStreamState State; // Current connection state
Mutex MState; // Mutex for state access
// Timing and general variables
uint32 LastPacket; // Timestamp of last packet activity
Mutex MVarlock; // General variable lock
// Combined application packet handling
EQApplicationPacket* CombinedAppPacket; // Current combined packet
Mutex MCombinedAppPacket; // Mutex for combined packet access
// Sequence number tracking
long LastSeqSent; // Last sequence number sent
Mutex MLastSeqSent; // Mutex for last sequence sent
void SetLastSeqSent(uint32 seq); // Set last sent sequence number
// Acknowledgment sequence tracking
long MaxAckReceived; // Highest ack sequence received
long NextAckToSend; // Next ack sequence to send
long LastAckSent; // Last ack sequence sent
// Acknowledgment accessor methods
long GetMaxAckReceived();
long GetNextAckToSend();
long GetLastAckSent();
void SetMaxAckReceived(uint32 seq);
void SetNextAckToSend(uint32 seq);
void SetLastAckSent(uint32 seq);
Mutex MAcks; // Mutex for acknowledgment data
// Outbound packet queues
queue<EQProtocolPacket*> NonSequencedQueue; // Non-sequenced packets
deque<EQProtocolPacket*> SequencedQueue; // Sequenced packets
map<uint16, EQProtocolPacket*> OutOfOrderpackets; // Out-of-order packets
Mutex MOutboundQueue; // Mutex for outbound queues
// Inbound packet queue
deque<EQApplicationPacket*> InboundQueue; // Packets waiting processing
Mutex MInboundQueue; // Mutex for inbound queue
// Static configuration
static uint16 MaxWindowSize; // Maximum window size for flow control
// Rate limiting and flow control
sint32 BytesWritten; // Bytes written this period
Mutex MRate; // Mutex for rate limiting
sint32 RateThreshold; // Rate limiting threshold
sint32 DecayRate; // Rate decay per time period
uint32 AverageDelta; // Average round-trip time
// Factory reference
EQStreamFactory* Factory; // Factory that created this stream
public:
// EQ2-specific packet combining
Mutex MCombineQueueLock; // Mutex for combine queue operations
bool CheckCombineQueue(); // Check and process combine queue
deque<EQ2Packet*> combine_queue; // Queue of packets to combine
Timer* combine_timer; // Timer for combine operations
// Encryption and compression
Crypto* crypto; // Cryptographic handler
int8 EQ2_Compress(EQ2Packet* app, int8 offset = 3); // Compress EQ2 packet
z_stream stream; // zlib compression stream
uchar* stream_buffer; // Compression buffer
int32 stream_buffer_size; // Size of compression buffer
bool eq2_compressed; // Stream uses EQ2 compression
int8 compressed_offset; // Offset for compressed data
// Client version management
int16 client_version; // Client protocol version
int16 GetClientVersion() { return client_version; }
void SetClientVersion(int16 version) { client_version = version; }
// Session attempt management
void ResetSessionAttempts() { reconnectAttempt = 0; }
bool HasSessionAttempts() { return reconnectAttempt > 0; }
// Constructors
EQStream() {
init();
remote_ip = 0;
remote_port = 0;
State = CLOSED;
StreamType = UnknownStream;
compressed = true;
encoded = false;
app_opcode_size = 2;
}
EQStream(sockaddr_in addr);
// Destructor
virtual ~EQStream() {
// Close stream safely
MOutboundQueue.lock();
SetState(CLOSED);
MOutboundQueue.unlock();
// Clean up data structures
RemoveData();
// Clean up allocated resources
safe_delete(crypto);
safe_delete(combine_timer);
safe_delete(resend_que_timer);
safe_delete_array(oversize_buffer);
safe_delete_array(rogue_buffer);
// Clean up combine queue
MCombineQueueLock.lock();
for (auto cmb = combine_queue.begin(); cmb != combine_queue.end(); ++cmb) {
safe_delete(*cmb);
}
MCombineQueueLock.unlock();
// Clean up compression stream
deflateEnd(&stream);
// Clean up out-of-order packets
for (auto oop = OutOfOrderpackets.begin(); oop != OutOfOrderpackets.end(); ++oop) {
safe_delete(oop->second);
}
#ifdef WRITE_PACKETS
if (write_packets) {
fclose(write_packets);
}
#endif
}
// Factory and initialization
void SetFactory(EQStreamFactory* f) { Factory = f; }
void init(bool resetSession = true);
// Configuration
void SetMaxLen(uint32 length) { MaxLen = length; }
int8 getTimeoutDelays() { return timeout_delays; }
void addTimeoutDelay() { timeout_delays++; }
// EQ2-specific packet handling
void EQ2QueuePacket(EQ2Packet* app, bool attempted_combine = false);
void PreparePacket(EQ2Packet* app, int8 offset = 0);
void UnPreparePacket(EQ2Packet* app);
void EncryptPacket(EQ2Packet* app, int8 compress_offset, int8 offset);
void FlushCombinedPacket();
// General packet transmission
void SendPacket(EQApplicationPacket* p);
void QueuePacket(EQProtocolPacket* p);
void SendPacket(EQProtocolPacket* p);
vector<EQProtocolPacket*> convert(EQApplicationPacket* p);
void NonSequencedPush(EQProtocolPacket* p);
void SequencedPush(EQProtocolPacket* p);
// Resend queue management
Mutex MResendQue; // Mutex for resend queue
Mutex MCompressData; // Mutex for compression operations
deque<EQProtocolPacket*> resend_que; // Queue of packets needing resend
void CheckResend(int eq_fd); // Check and handle packet resends
// Acknowledgment and writing
void AckPackets(uint16 seq); // Acknowledge received packets
void Write(int eq_fd); // Write packets to socket
// Stream state management
void SetActive(bool val) { streamactive = val; }
// Low-level packet operations
void WritePacket(int fd, EQProtocolPacket* p);
void EncryptPacket(uchar* data, int16 size);
// Session management
uint32 GetKey() { return Key; }
void SetKey(uint32 k) { Key = k; }
void SetSession(uint32 s) { Session = s; }
void SetLastPacketTime(uint32 t) { LastPacket = t; }
// Packet processing
void Process(const unsigned char* data, uint32 length);
void ProcessPacket(EQProtocolPacket* p, EQProtocolPacket* lastp = nullptr);
// Embedded packet handling
bool ProcessEmbeddedPacket(uchar* pBuffer, uint16 length, int8 opcode = OP_Packet);
bool HandleEmbeddedPacket(EQProtocolPacket* p, int16 offset = 2, int16 length = 0);
// Encryption handling
EQProtocolPacket* ProcessEncryptedPacket(EQProtocolPacket* p);
EQProtocolPacket* ProcessEncryptedData(uchar* data, int32 size, int16 opcode);
// Virtual packet dispatch (override in derived classes)
virtual void DispatchPacket(EQApplicationPacket* p) { p->DumpRaw(); }
// Session protocol messages
void SendSessionResponse();
void SendSessionRequest();
void SendDisconnect(bool setstate = true);
void SendAck(uint16 seq);
void SendOutOfOrderAck(uint16 seq);
// Connection health checks
bool CheckTimeout(uint32 now, uint32 timeout = 30) {
return (LastPacket && (now - LastPacket) > timeout);
}
bool Stale(uint32 now, uint32 timeout = 30) {
return (LastPacket && (now - LastPacket) > timeout);
}
// Inbound queue management
void InboundQueuePush(EQApplicationPacket* p);
EQApplicationPacket* PopPacket(); // Pop packet from inbound queue
void InboundQueueClear();
// Outbound queue management
void OutboundQueueClear();
bool HasOutgoingData();
// Key exchange and RSA
void SendKeyRequest();
int16 processRSAKey(EQProtocolPacket* p, uint16 subpacket_length = 0);
// Data cleanup
void RemoveData() {
InboundQueueClear();
OutboundQueueClear();
if (CombinedAppPacket) {
delete CombinedAppPacket;
CombinedAppPacket = nullptr;
}
}
// Usage tracking (thread-safe reference counting)
bool IsInUse() {
bool flag;
MInUse.lock();
flag = (active_users > 0);
MInUse.unlock();
return flag;
}
void PutInUse() {
MInUse.lock();
active_users++;
MInUse.unlock();
}
void ReleaseFromUse() {
MInUse.lock();
if (active_users > 0) {
active_users--;
}
MInUse.unlock();
}
// Sequence number utilities
static SeqOrder CompareSequence(uint16 expected_seq, uint16 seq);
// State management
EQStreamState GetState() { return State; }
void SetState(EQStreamState state) {
MState.lock();
State = state;
MState.unlock();
}
// Remote endpoint access
uint32 GetRemoteIP() { return remote_ip; }
uint32 GetrIP() { return remote_ip; } // Legacy alias
uint16 GetRemotePort() { return remote_port; }
uint16 GetrPort() { return remote_port; } // Legacy alias
// Static packet reading
static EQProtocolPacket* Read(int eq_fd, sockaddr_in* from);
// Connection management
void Close() { SendDisconnect(); }
bool CheckActive() { return (GetState() == ESTABLISHED); }
bool CheckClosed() { return GetState() == CLOSED; }
// Stream configuration
void SetOpcodeSize(uint8 s) { app_opcode_size = s; }
void SetStreamType(EQStreamType t);
EQStreamType GetStreamType() const { return StreamType; }
// Queue processing
void ProcessQueue();
EQProtocolPacket* RemoveQueue(uint16 seq);
// Rate limiting and flow control
void Decay();
void AdjustRates(uint32 average_delta);
// Resend timer
Timer* resend_que_timer; // Timer for checking resend queue
};
#endif

View File

@ -0,0 +1,546 @@
// EQ2Emulator: Everquest II Server Emulator
// Copyright (C) 2007 EQ2EMulator Development Team
// Licensed under GPL v3 - see <http://www.gnu.org/licenses/>
#include "EQStreamFactory.h"
#include "Log.h"
// Unix/Linux networking headers
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <pthread.h>
#include <fcntl.h>
#include <unistd.h>
// Standard library headers
#include <fstream>
#include <iostream>
#include <cstring>
#include <deque>
#include <vector>
#include <algorithm>
// Project headers
#include "op_codes.h"
#include "EQStream.h"
#include "packet_dump.h"
#ifdef WORLD
#include "../WorldServer/client.h"
#endif
using namespace std;
#ifdef WORLD
extern ClientList client_list;
#endif
/**
* Thread entry point for the packet reader loop.
*
* @param eqfs - Pointer to EQStreamFactory instance
* @return Thread return value
*/
ThreadReturnType EQStreamFactoryReaderLoop(void* eqfs)
{
if (eqfs) {
auto fs = static_cast<EQStreamFactory*>(eqfs);
fs->ReaderLoop();
}
THREAD_RETURN(nullptr);
}
/**
* Thread entry point for the packet writer loop.
*
* @param eqfs - Pointer to EQStreamFactory instance
* @return Thread return value
*/
ThreadReturnType EQStreamFactoryWriterLoop(void* eqfs)
{
if (eqfs) {
auto fs = static_cast<EQStreamFactory*>(eqfs);
fs->WriterLoop();
}
THREAD_RETURN(nullptr);
}
/**
* Thread entry point for the packet combining loop.
*
* @param eqfs - Pointer to EQStreamFactory instance
* @return Thread return value
*/
ThreadReturnType EQStreamFactoryCombinePacketLoop(void* eqfs)
{
if (eqfs) {
auto fs = static_cast<EQStreamFactory*>(eqfs);
fs->CombinePacketLoop();
}
THREAD_RETURN(nullptr);
}
/**
* EQStreamFactory constructor with stream type and port.
*
* @param type - Type of streams to create (login/world)
* @param port - Port number to listen on
*/
EQStreamFactory::EQStreamFactory(EQStreamType type, int port)
{
StreamType = type;
Port = port;
listen_ip_address = nullptr;
sock = -1;
ReaderRunning = false;
WriterRunning = false;
CombinePacketRunning = false;
DecayTimer = nullptr;
}
/**
* Close the stream factory and clean up all resources.
* Stops all threads, closes socket, and removes all streams.
*/
void EQStreamFactory::Close()
{
CheckTimeout(true);
Stop();
if (sock != -1) {
close(sock);
sock = -1;
}
}
/**
* Open the UDP socket and start worker threads.
* Creates reader, writer, and packet combiner threads.
*
* @return true if successful, false on error
*/
bool EQStreamFactory::Open()
{
struct sockaddr_in address;
pthread_t t1, t2, t3;
// Setup socket address structure
memset(reinterpret_cast<char*>(&address), 0, sizeof(address));
address.sin_family = AF_INET;
address.sin_port = htons(Port);
// Set bind address based on configuration
#if defined(LOGIN) || defined(MINILOGIN)
if (listen_ip_address) {
address.sin_addr.s_addr = inet_addr(listen_ip_address);
} else {
address.sin_addr.s_addr = htonl(INADDR_ANY);
}
#else
address.sin_addr.s_addr = htonl(INADDR_ANY);
#endif
// Create UDP socket
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
return false;
}
// Bind socket to address
if (::bind(sock, reinterpret_cast<struct sockaddr*>(&address), sizeof(address)) < 0) {
close(sock);
sock = -1;
return false;
}
// Set socket to non-blocking mode
fcntl(sock, F_SETFL, O_NONBLOCK);
// Log thread startup
#ifdef LOGIN
LogWrite(LOGIN__DEBUG, 0, "Login", "Starting factory Reader");
LogWrite(LOGIN__DEBUG, 0, "Login", "Starting factory Writer");
#elif WORLD
LogWrite(WORLD__DEBUG, 0, "World", "Starting factory Reader");
LogWrite(WORLD__DEBUG, 0, "World", "Starting factory Writer");
#endif
// Create and detach worker threads
pthread_create(&t1, nullptr, EQStreamFactoryReaderLoop, this);
pthread_create(&t2, nullptr, EQStreamFactoryWriterLoop, this);
pthread_create(&t3, nullptr, EQStreamFactoryCombinePacketLoop, this);
pthread_detach(t1);
pthread_detach(t2);
pthread_detach(t3);
return true;
}
/**
* Get the next new stream from the queue.
* Thread-safe method to retrieve newly created streams.
*
* @return Next EQStream from queue, or nullptr if none available
*/
EQStream* EQStreamFactory::Pop()
{
if (!NewStreams.size()) {
return nullptr;
}
EQStream* s = nullptr;
MNewStreams.lock();
if (NewStreams.size()) {
s = NewStreams.front();
NewStreams.pop();
s->PutInUse();
}
MNewStreams.unlock();
return s;
}
/**
* Add a new stream to the queue for processing.
* Thread-safe method to queue newly created streams.
*
* @param s - EQStream to add to queue
*/
void EQStreamFactory::Push(EQStream* s)
{
MNewStreams.lock();
NewStreams.push(s);
MNewStreams.unlock();
}
/**
* Main packet reading loop - runs in separate thread.
* Receives UDP packets and routes them to appropriate streams.
*/
void EQStreamFactory::ReaderLoop()
{
fd_set readset;
map<string, EQStream*>::iterator stream_itr;
int num;
int length;
unsigned char buffer[2048];
sockaddr_in from;
socklen_t socklen = sizeof(sockaddr_in);
timeval sleep_time;
ReaderRunning = true;
while (sock != -1) {
MReaderRunning.lock();
if (!ReaderRunning) {
MReaderRunning.unlock();
break;
}
MReaderRunning.unlock();
// Setup select() for socket monitoring
FD_ZERO(&readset);
FD_SET(sock, &readset);
sleep_time.tv_sec = 30;
sleep_time.tv_usec = 0;
// Wait for incoming data or timeout
num = select(sock + 1, &readset, nullptr, nullptr, &sleep_time);
if (num < 0) {
// Select error - could log this
continue;
} else if (num == 0) {
// Timeout - continue loop
continue;
}
// Check if our socket has data
if (FD_ISSET(sock, &readset)) {
length = recvfrom(sock, buffer, 2048, 0,
reinterpret_cast<struct sockaddr*>(&from),
&socklen);
if (length < 2) {
// Packet too small - ignore
continue;
}
// Create address:port string for stream identification
char temp[25];
snprintf(temp, sizeof(temp), "%u.%d",
ntohl(from.sin_addr.s_addr), ntohs(from.sin_port));
MStreams.lock();
stream_itr = Streams.find(temp);
// Handle new connections or session requests
if (stream_itr == Streams.end() || buffer[1] == OP_SessionRequest) {
MStreams.unlock();
if (buffer[1] == OP_SessionRequest) {
// Close existing stream if present
if (stream_itr != Streams.end() && stream_itr->second) {
stream_itr->second->SetState(CLOSED);
}
// Create new stream
auto s = new EQStream(from);
s->SetFactory(this);
s->SetStreamType(StreamType);
Streams[temp] = s;
WriterWork.Signal();
Push(s);
s->Process(buffer, length);
s->SetLastPacketTime(Timer::GetCurrentTime2());
}
} else {
// Route packet to existing stream
EQStream* curstream = stream_itr->second;
// Skip closed connections
if (curstream->CheckClosed()) {
curstream = nullptr;
} else {
curstream->PutInUse();
}
MStreams.unlock();
if (curstream) {
curstream->Process(buffer, length);
curstream->SetLastPacketTime(Timer::GetCurrentTime2());
curstream->ReleaseFromUse();
}
}
}
}
}
/**
* Check for timed out streams and clean up closed connections.
*
* @param remove_all - If true, remove all streams regardless of state
*/
void EQStreamFactory::CheckTimeout(bool remove_all)
{
// Lock streams for the entire timeout check - should be fast
MStreams.lock();
unsigned long now = Timer::GetCurrentTime2();
map<string, EQStream*>::iterator stream_itr;
for (stream_itr = Streams.begin(); stream_itr != Streams.end();) {
EQStream* s = stream_itr->second;
EQStreamState state = s->GetState();
// Transition CLOSING streams to CLOSED when no outgoing data
if (state == CLOSING && !s->HasOutgoingData()) {
stream_itr->second->SetState(CLOSED);
state = CLOSED;
} else if (s->CheckTimeout(now, STREAM_TIMEOUT)) {
// Handle timeout based on current state
const char* stateString;
switch (state) {
case ESTABLISHED:
stateString = "Established";
break;
case CLOSING:
stateString = "Closing";
break;
case CLOSED:
stateString = "Closed";
break;
case WAIT_CLOSE:
stateString = "Wait-Close";
break;
default:
stateString = "Unknown";
break;
}
LogWrite(WORLD__DEBUG, 0, "World", "Timeout up!, state=%s (%u)",
stateString, state);
if (state == ESTABLISHED) {
s->Close();
} else if (state == WAIT_CLOSE) {
s->SetState(CLOSING);
state = CLOSING;
} else if (state == CLOSING) {
// If we timeout in closing state, force close
s->SetState(CLOSED);
state = CLOSED;
}
}
// Remove closed streams (check immediately after state changes)
if (remove_all || state == CLOSED) {
if (!remove_all && s->getTimeoutDelays() < 2) {
s->addTimeoutDelay();
// Give other threads time to finish with this stream
++stream_itr;
} else {
// Safe to delete now
#ifdef LOGIN
LogWrite(LOGIN__DEBUG, 0, "Login", "Removing connection...");
#else
LogWrite(WORLD__DEBUG, 0, "World", "Removing connection...");
#endif
auto temp = stream_itr;
++stream_itr;
// Let client list handle cleanup if in world server
#ifdef WORLD
client_list.RemoveConnection(temp->second);
#endif
EQStream* stream = temp->second;
Streams.erase(temp);
delete stream;
continue;
}
} else {
++stream_itr;
}
}
MStreams.unlock();
}
/**
* Packet combining optimization loop - runs in separate thread.
* Combines multiple small packets for efficient transmission.
*/
void EQStreamFactory::CombinePacketLoop()
{
deque<EQStream*> combine_que;
CombinePacketRunning = true;
bool packets_waiting = false;
while (sock != -1) {
if (!CombinePacketRunning) {
break;
}
MStreams.lock();
// Check all streams for combine timer expiration
for (auto& stream_pair : Streams) {
if (!stream_pair.second) {
continue;
}
if (stream_pair.second->combine_timer &&
stream_pair.second->combine_timer->Check()) {
combine_que.push_back(stream_pair.second);
}
}
// Process streams that need packet combining
packets_waiting = false;
while (!combine_que.empty()) {
EQStream* stream = combine_que.front();
if (stream->CheckActive()) {
if (!stream->CheckCombineQueue()) {
packets_waiting = true;
}
}
combine_que.pop_front();
}
MStreams.unlock();
// Sleep longer if no packets are waiting
if (!packets_waiting) {
usleep(25000); // 25ms
}
usleep(1000); // 1ms
}
}
/**
* Main packet writing loop - runs in separate thread.
* Handles outgoing packet transmission and resends.
*/
void EQStreamFactory::WriterLoop()
{
map<string, EQStream*>::iterator stream_itr;
vector<EQStream*> wants_write;
vector<EQStream*>::iterator cur, end;
deque<EQStream*> resend_que;
bool decay = false;
uint32 stream_count;
Timer DecayTimer(20);
WriterRunning = true;
DecayTimer.Enable();
while (sock != -1) {
Timer::SetCurrentTime();
MWriterRunning.lock();
if (!WriterRunning) {
MWriterRunning.unlock();
break;
}
MWriterRunning.unlock();
wants_write.clear();
resend_que.clear();
decay = DecayTimer.Check();
// Copy streams into separate list to minimize lock time
MStreams.lock();
for (stream_itr = Streams.begin(); stream_itr != Streams.end(); ++stream_itr) {
if (!stream_itr->second) {
// This shouldn't happen, but handle gracefully
continue;
}
// Apply bandwidth decay if it's time
if (decay) {
stream_itr->second->Decay();
}
// Queue streams with outgoing data
if (stream_itr->second->HasOutgoingData()) {
stream_itr->second->PutInUse();
wants_write.push_back(stream_itr->second);
}
// Queue streams that need resend processing
if (stream_itr->second->resend_que_timer->Check()) {
resend_que.push_back(stream_itr->second);
}
}
MStreams.unlock();
// Perform actual packet writes
for (cur = wants_write.begin(), end = wants_write.end();
cur != end; ++cur) {
(*cur)->Write(sock);
(*cur)->ReleaseFromUse();
}
// Handle packet resends
while (!resend_que.empty()) {
resend_que.front()->CheckResend(sock);
resend_que.pop_front();
}
usleep(10000); // 10ms sleep
// Check if we have any streams - wait if not
MStreams.lock();
stream_count = Streams.size();
MStreams.unlock();
if (!stream_count) {
WriterWork.Wait();
}
}
}

View File

@ -0,0 +1,125 @@
// EQ2Emulator: Everquest II Server Emulator
// Copyright (C) 2007 EQ2EMulator Development Team
// Licensed under GPL v3 - see <http://www.gnu.org/licenses/>
#ifndef _EQSTREAMFACTORY_H
#define _EQSTREAMFACTORY_H
#include <queue>
#include <map>
#include <string>
#include <memory>
#include "../common/EQStream.h"
#include "../common/Condition.h"
#include "../common/opcodemgr.h"
#include "../common/timer.h"
#define STREAM_TIMEOUT 45000 // Stream timeout in milliseconds
/**
* Factory class for creating and managing EverQuest network streams.
* Handles UDP socket communication, stream lifecycle, and packet processing
* for both login and world server connections.
*/
class EQStreamFactory {
private:
// Network socket and configuration
int sock; // UDP socket file descriptor
int Port; // Port number to listen on
// Thread management flags and mutexes
bool ReaderRunning; // Reader thread active flag
Mutex MReaderRunning; // Mutex for reader thread flag
bool WriterRunning; // Writer thread active flag
Mutex MWriterRunning; // Mutex for writer thread flag
bool CombinePacketRunning; // Packet combiner thread active flag
Mutex MCombinePacketRunning; // Mutex for combiner thread flag
// Thread synchronization
Condition WriterWork; // Condition variable for writer thread
// Stream management
EQStreamType StreamType; // Type of streams this factory creates
std::queue<EQStream*> NewStreams; // Queue of new streams waiting for processing
Mutex MNewStreams; // Mutex for new streams queue
std::map<std::string, EQStream*> Streams; // Active streams mapped by address:port
Mutex MStreams; // Mutex for streams map
// Cleanup timer
Timer* DecayTimer; // Timer for periodic cleanup operations
public:
// Network configuration
char* listen_ip_address; // IP address to bind to (nullptr = any)
// Stream lifecycle management
void CheckTimeout(bool remove_all = false);
// Constructors and destructor
EQStreamFactory(EQStreamType type) {
ReaderRunning = false;
WriterRunning = false;
CombinePacketRunning = false;
StreamType = type;
sock = -1;
Port = 0;
listen_ip_address = nullptr;
DecayTimer = nullptr;
}
EQStreamFactory(EQStreamType type, int port);
~EQStreamFactory() {
safe_delete_array(listen_ip_address);
}
// Stream queue management
EQStream* Pop(); // Get next new stream from queue
void Push(EQStream* s); // Add new stream to queue
// Network operations
bool loadPublicKey(); // Load encryption keys (if needed)
bool Open(); // Open socket and start threads
bool Open(unsigned long port) {
Port = port;
return Open();
}
void Close(); // Close socket and stop threads
// Main thread loops
void ReaderLoop(); // Main packet reading loop
void WriterLoop(); // Main packet writing loop
void CombinePacketLoop(); // Packet combining optimization loop
// Thread control
void Stop() {
StopReader();
StopWriter();
StopCombinePacket();
}
void StopReader() {
MReaderRunning.lock();
ReaderRunning = false;
MReaderRunning.unlock();
}
void StopWriter() {
MWriterRunning.lock();
WriterRunning = false;
MWriterRunning.unlock();
WriterWork.Signal();
}
void StopCombinePacket() {
MCombinePacketRunning.lock();
CombinePacketRunning = false;
MCombinePacketRunning.unlock();
}
void SignalWriter() {
WriterWork.Signal();
}
};
#endif

20
old/login/Character.cpp Normal file
View File

@ -0,0 +1,20 @@
/*
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 "Character.h"

25
old/login/Character.h Normal file
View File

@ -0,0 +1,25 @@
/*
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/>.
*/
#ifndef _EQ2_CHARACTER_
#define _EQ2_CHARACTER_
class Character{
};
#endif

1561
old/login/LWorld.cpp Normal file

File diff suppressed because it is too large Load Diff

253
old/login/LWorld.h Normal file
View File

@ -0,0 +1,253 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
*/
#ifndef LWORLD_H
#define LWORLD_H
#include "../common/Mutex.h"
#define ERROR_BADPASSWORD "Bad password"
#define INVALID_ACCOUNT "Invalid Server Account."
#define ERROR_BADVERSION "Incorrect version"
#define ERROR_UNNAMED "Unnamed servers not allowed to connect to full login servers"
#define ERROR_NOTMASTER "Not a master server"
#define ERROR_NOTMESH "Not a mesh server"
#define ERROR_GHOST "Ghost kick"
#define ERROR_UnknownServerType "Unknown Server Type"
#define ERROR_BADNAME_SERVER "Bad server name, name may not contain the word \"Server\" (case sensitive)"
#define ERROR_BADNAME "Bad server name. Unknown reason."
#define WORLD_NAME_SUFFIX " Server"
#include <boost/algorithm/string.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/beast/http.hpp>
#include <sstream>
#include <string>
#include <iostream>
namespace beast = boost::beast; // from <boost/beast.hpp>
namespace http = beast::http; // from <boost/beast/http.hpp>
#include "../common/linked_list.h"
#include "../WorldServer/MutexList.h"
#include "../WorldServer/MutexMap.h"
#include "../common/timer.h"
#include "../common/types.h"
#include "../common/queue.h"
#include "../common/servertalk.h"
#include "../common/TCPConnection.h"
#include "client.h"
#define MAX_UPDATE_COUNT 20
#define MAX_LOGIN_APPEARANCE_COUNT 100
#ifdef WIN32
void ServerUpdateLoop(void* tmp);
#else
void* ServerUpdateLoop(void* tmp);
#endif
enum ConType { UnknownW, World, Chat, Login };
class LWorld
{
public:
LWorld(TCPConnection* in_con, bool OutgoingLoginUplink = false, int32 iIP = 0, int16 iPort = 0, bool iNeverKick = false);
LWorld(int32 in_accountid, char* in_accountname, char* in_worldname, int32 in_admin_id);
LWorld(TCPConnection* in_RemoteLink, int32 in_ip, int32 in_RemoteID, int32 in_accountid, char* in_accountname, char* in_worldname, char* in_address, sint32 in_status, int32 in_adminid, bool in_showdown, int8 in_authlevel, bool in_placeholder, int32 iLinkWorldID);
~LWorld();
static bool CheckServerName(const char* name);
bool Process();
void SendPacket(ServerPacket* pack);
void Message(const char* to, const char* message, ...);
bool SetupWorld(char* in_worldname, char* in_worldaddress, char* in_account, char* in_password, char* in_version);
void UpdateStatus(sint32 in_status, sint32 in_players, sint32 in_zones, int8 in_level) {
// we don't want the server list to update unless something has changed
if(status != in_status || num_players != in_players || num_zones != in_zones || world_max_level != in_level)
{
status = in_status;
num_players = in_players;
num_zones = in_zones;
world_max_level = in_level;
UpdateWorldList();
}
}
void UpdateWorldList(LWorld* to = 0);
void SetRemoteInfo(int32 in_ip, int32 in_accountid, char* in_account, char* in_name, char* in_address, int32 in_status, int32 in_adminid, sint32 in_players, sint32 in_zones);
inline bool IsPlaceholder() { return pPlaceholder; }
inline int32 GetAccountID() { return accountid; }
inline char* GetAccount() { return account; }
inline char* GetAddress() { return address; }
inline int16 GetClientPort() { return pClientPort; }
inline bool IsAddressIP() { return isaddressip; }
inline char* GetName() { return worldname; }
inline sint32 GetStatus() { return status; }
bool IsLocked() { return status==-2; }
inline int32 GetIP() { return ip; }
inline int16 GetPort() { return port; }
inline int32 GetID() { return ID; }
inline int32 GetAdmin() { return admin_id; }
inline bool ShowDown() { return pshowdown; }
inline bool ShowDownActive(){ return show_down_active; }
void ShowDownActive(bool show){ show_down_active = show; }
void ShowDown(bool show){ pshowdown = show; }
inline bool Connected() { return pConnected; }
int8 GetWorldStatus();
void ChangeToPlaceholder();
void Kick(const char* message = ERROR_GHOST, bool iSetKickedFlag = true);
inline bool IsKicked() { return kicked; }
inline bool IsNeverKick() { return pNeverKick; }
inline ConType GetType() { return ptype; }
inline bool IsOutgoingUplink() { return OutgoingUplink; }
inline TCPConnection* GetLink() { return Link; }
inline int32 GetRemoteID() { return RemoteID; }
inline int32 GetLinkWorldID() { return LinkWorldID; }
inline sint32 GetZoneNum() { return num_zones; }
inline sint32 GetPlayerNum() { return num_players; }
void SetID(int32 new_id) { ID = new_id; }
void SendDeleteCharacter( int32 char_id, int32 account_id );
bool IsDevelServer(){ return devel_server; }
inline int8 GetMaxWorldLevel() { return world_max_level; }
bool IsInit;
protected:
friend class LWorldList;
TCPConnection* Link;
Timer* pReconnectTimer;
Timer* pStatsTimer;
private:
int32 ID;
int32 ip;
char IPAddr[64];
int16 port;
bool kicked;
bool pNeverKick;
bool pPlaceholder;
bool devel_server;
int32 accountid;
char account[30];
char address[250];
bool isAuthenticated;
int16 pClientPort;
bool isaddressip;
char worldname[200];
sint32 status;
int32 admin_id;
bool pshowdown;
bool show_down_active;
ConType ptype;
bool OutgoingUplink;
bool pConnected;
sint32 num_players;
sint32 num_zones;
int32 RemoteID;
int32 LinkWorldID;
int8 world_max_level;
};
class LWorldList
{
public:
LWorldList();
~LWorldList();
LWorld* FindByID(int32 WorldID);
LWorld* FindByIP(int32 ip);
LWorld* FindByAddress(char* address);
LWorld* FindByLink(TCPConnection* in_link, int32 in_id);
LWorld* FindByAccount(int32 in_accountid, ConType in_type = World);
void Add(LWorld* worldserver);
void AddInitiateWorld ( LWorld* world );
void Process();
void ReceiveData();
void SendPacket(ServerPacket* pack, LWorld* butnotme = 0);
void SendPacketLocal(ServerPacket* pack, LWorld* butnotme = 0);
void SendPacketLogin(ServerPacket* pack, LWorld* butnotme = 0);
void SendWorldChanged(int32 server_id, bool sendtoallclients=false, Client* sendto = 0);
vector<PacketStruct*>* GetServerListUpdate(int16 version);
EQ2Packet* MakeServerListPacket(int8 lsadmin, int16 version);
void UpdateWorldList(LWorld* to = 0);
void UpdateWorldStats();
void KickGhost(ConType in_type, int32 in_accountid = 0, LWorld* ButNotMe = 0);
void KickGhostIP(int32 ip, LWorld* NotMe = 0, int16 iClientPort = 0);
void RemoveByLink(TCPConnection* in_link, int32 in_id = 0, LWorld* ButNotMe = 0);
void RemoveByID(int32 in_id);
void SendWorldStatus(LWorld* chat, char* adminname);
void ConnectUplink();
bool Init();
void InitWorlds();
void Shutdown();
bool WriteXML();
int32 GetCount(ConType type);
void PopulateWorldList(http::response<http::string_body>& res);
void ListWorldsToConsole();
//devn00b temp
void AddServerEquipmentUpdates(LWorld* world, map<int32, LoginEquipmentUpdate> updates);
void ProcessLSEquipUpdates();
void RequestServerEquipUpdates(LWorld* world);
void SetUpdateServerList ( bool var ) { UpdateServerList = var; }
bool ContinueServerUpdates(){ return server_update_thread; }
void ResetServerUpdates(){server_update_thread = true;}
void ProcessServerUpdates();
void RequestServerUpdates(LWorld* world);
void AddServerZoneUpdates(LWorld* world, map<int32, LoginZoneUpdate> updates);
protected:
friend class LWorld;
int32 GetNextID() { return NextID++; }
private:
Mutex MWorldMap;
map<int32, map<int32, bool> > zone_updates_already_used; //used to determine if someone is trying to DOS us
MutexMap<int32, int32> zone_update_timeouts;
MutexMap<int32, int32> awaiting_zone_update;
MutexMap<LWorld*, int32> last_updated;
MutexMap<int32, map<int32, LoginZoneUpdate> > server_zone_updates;
bool server_update_thread;
int32 NextID;
LinkedList<LWorld*> list;
map<int32,LWorld*> worldmap;
TCPServer* tcplistener;
TCPConnection* OutLink;
//devn00b temp
// JohnAdams: login appearances, copied from above
map<int32, map<int32, bool> > equip_updates_already_used;
MutexMap<int32, int32> equip_update_timeouts;
MutexMap<int32, int32> awaiting_equip_update;
MutexMap<LWorld*, int32> last_equip_updated;
MutexMap<int32, map<int32, LoginEquipmentUpdate> > server_equip_updates;
//
///
// holds the world server list so we don't have to create it for every character
// logging in
map<int32,EQ2Packet*> ServerListData;
bool UpdateServerList;
};
#endif

View File

@ -0,0 +1,58 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
*/
#include "LoginAccount.h"
LoginAccount::LoginAccount(){
}
LoginAccount::~LoginAccount(){
vector<CharSelectProfile*>::iterator iter;
for(iter = charlist.begin(); iter != charlist.end(); iter++){
safe_delete(*iter);
}
}
void LoginAccount::flushCharacters ( )
{
vector<CharSelectProfile*>::iterator iter;
for(iter = charlist.begin(); iter != charlist.end(); iter++){
safe_delete(*iter);
}
charlist.clear ( );
}
CharSelectProfile* LoginAccount::getCharacter(char* name){
vector<CharSelectProfile*>::iterator char_iterator;
CharSelectProfile* profile = 0;
EQ2_16BitString temp;
for(char_iterator = charlist.begin(); char_iterator != charlist.end(); char_iterator++){
profile = *char_iterator;
temp = profile->packet->getType_EQ2_16BitString_ByName("name");
if(strcmp(temp.data.c_str(), name)==0)
return profile;
}
return 0;
}
void LoginAccount::removeCharacter(char* name, int16 version){
vector<CharSelectProfile*>::iterator iter;
CharSelectProfile* profile = 0;
EQ2_16BitString temp;
for(iter = charlist.begin(); iter != charlist.end(); iter++){
profile = *iter;
temp = profile->packet->getType_EQ2_16BitString_ByName("name");
if(strcmp(temp.data.c_str(), name)==0){
if(version <= 561) {
profile->deleted = true; // workaround for char select crash on old clients
}
else {
safe_delete(*iter);
charlist.erase(iter);
}
return;
}
}
}

54
old/login/LoginAccount.h Normal file
View File

@ -0,0 +1,54 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
*/
#ifndef _LOGINACCOUNT_
#define _LOGINACCOUNT_
#include <iostream>
#include <string>
#include <vector>
#include "../common/linked_list.h"
#include "PacketHeaders.h"
#include "../common/PacketStruct.h"
using namespace std;
class LoginAccount {
public:
LoginAccount();
LoginAccount(int32 id, const char* in_name, const char* in_pass){
account_id = id;
strcpy(name, in_name);
strcpy(password, in_pass);
}
~LoginAccount();
bool SaveAccount(LoginAccount* acct);
vector<CharSelectProfile*> charlist;
void setName(const char* in_name) { strcpy(name, in_name); }
void setPassword(const char* in_pass) { strcpy(password, in_pass); }
void setAuthenticated(bool in_auth) { authenticated=in_auth; }
void setAccountID(int32 id){ account_id = id; }
void addCharacter(CharSelectProfile* profile){
charlist.push_back(profile);
}
void removeCharacter(PacketStruct* profile);
void removeCharacter(char* name, int16 version);
void serializeCharacter(uchar* buffer, CharSelectProfile* profile);
void flushCharacters ( );
CharSelectProfile* getCharacter(char* name);
int32 getLoginAccountID(){ return account_id; }
char* getLoginName() { return name; }
char* getLoginPassword() { return password; }
bool getLoginAuthenticated() { return authenticated; }
private:
int32 account_id;
char name[32];
char password[32];
bool authenticated;
};
#endif

1083
old/login/LoginDatabase.cpp Normal file

File diff suppressed because it is too large Load Diff

96
old/login/LoginDatabase.h Normal file
View File

@ -0,0 +1,96 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
*/
#ifndef EQ2LOGIN_EMU_DATABASE_H
#define EQ2LOGIN_EMU_DATABASE_H
#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include <winsock.h>
#include <windows.h>
#endif
#include <mysql.h>
#include <string>
#include <vector>
#include "../common/database.h"
#include "../common/DatabaseNew.h"
#include "../common/types.h"
#include "../common/MiscFunctions.h"
#include "../common/servertalk.h"
#include "../common/Mutex.h"
#include "PacketHeaders.h"
#include "LoginAccount.h"
#include "LWorld.h"
#include "../common/PacketStruct.h"
using namespace std;
#pragma pack()
class LoginDatabase : public Database
{
public:
void FixBugReport();
void UpdateAccountIPAddress(int32 account_id, int32 address);
void UpdateWorldIPAddress(int32 world_id, int32 address);
void SaveBugReport(int32 world_id, char* category, char* subcategory, char* causes_crash, char* reproducible, char* summary, char* description, char* version, char* player, int32 account_id, char* spawn_name, int32 spawn_id, int32 zone_id);
LoginAccount* LoadAccount(const char* name, const char* password, bool attemptAccountCreation=true);
int32 GetAccountIDByName(const char* name);
int32 CheckServerAccount(char* name, char* passwd);
bool IsServerAccountDisabled(char* name);
bool IsIPBanned(char* ipaddr);
void GetServerAccounts(vector<LWorld*>* server_list);
char* GetServerAccountName(int32 id);
bool VerifyDelete(int32 account_id, int32 character_id, const char* name);
void SetServerZoneDescriptions(int32 server_id, map<int32, LoginZoneUpdate> zone_descriptions);
int32 GetServer(int32 accountID, int32 charID, string name);
void LoadCharacters(LoginAccount* acct, int16 version);
void CheckCharacterTimeStamps(LoginAccount* acct);
string GetCharacterName(int32 char_id , int32 server_id, int32 account_id);
void SaveCharacterColors(int32 char_id, char* type, EQ2_Color color);
void SaveCharacterFloats(int32 char_id, char* type, float float1, float float2, float float3, float multiplier=100.0f);
int16 GetAppearanceID(string name);
void DeactivateCharID(int32 server_id, int32 char_id, int32 exception_id);
int32 SaveCharacter(PacketStruct* create, LoginAccount* acct, int32 world_charid, int32 client_version);
void LoadAppearanceData(int32 char_id, PacketStruct* char_select_packet);
bool UpdateCharacterTimeStamp(int32 account_id, int32 character_id, int32 timestamp_update, int32 server_id);
bool UpdateCharacterLevel(int32 account_id, int32 character_id, int8 in_level, int32 server_id);
bool UpdateCharacterRace(int32 account_id, int32 character_id, int16 in_racetype, int8 in_race, int32 server_id);
bool UpdateCharacterClass(int32 account_id, int32 character_id, int8 in_class, int32 server_id);
bool UpdateCharacterName(int32 account_id, int32 character_id, char* newName, int32 server_id);
bool UpdateCharacterZone(int32 account_id, int32 character_id, int32 zone_id, int32 server_id);
bool UpdateCharacterGender(int32 account_id, int32 character_id, int8 in_gender, int32 server_id);
int32 GetRaceID(char* name);
void UpdateRaceID(char* name);
bool DeleteCharacter(int32 account_id, int32 character_id, int32 server_id);
void SaveClientLog(const char* type, const char* message, const char* player_name, int16 version);
bool CheckVersion(char* version);
void GetLatestTableVersions(LatestTableVersions* table_versions);
TableQuery* GetLatestTableQuery(int32 server_ip, char* name, int16 version);
bool VerifyDataTable(char* name);
sint16 GetDataVersion(char* name);
void SetZoneInformation(int32 server_id, int32 zone_id, int32 version, PacketStruct* packet);
string GetZoneDescription(char* name);
string GetColumnNames(char* name);
TableDataQuery* GetTableDataQuery(int32 server_ip, char* name, int16 version);
void UpdateWorldServerStats( LWorld* world, sint32 status);
bool ResetWorldServerStatsConnectedTime( LWorld* world );
void RemoveOldWorldServerStats();
void ResetWorldStats();
//devn00b temp
bool ConnectNewDatabase();
void SetServerEquipmentAppearances(int32 server_id, map<int32, LoginEquipmentUpdate> equip_updates); // JohnAdams: login appearances
int32 GetLoginCharacterIDFromWorldCharID(int32 server_id, int32 char_id); // JohnAdams: login appearances
void RemoveDeletedCharacterData();
int8 GetMaxCharsSetting();
int16 GetAccountBonus(int32 acct_id);
void UpdateWorldVersion(int32 world_id, char* version);
void UpdateAccountClientDataVersion(int32 account_id, int16 version);
void SaveCharacterPicture(int32 account_id, int32 character_id, int32 server_id, int16 picture_size, uchar* picture);
DatabaseNew dbLogin;
};
#endif

View File

@ -0,0 +1,88 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
*/
#include "PacketHeaders.h"
#include "../common/MiscFunctions.h"
#include "LoginDatabase.h"
#include "LWorld.h"
extern LWorldList world_list;
extern LoginDatabase database;
void LS_DeleteCharacterRequest::loadData(EQApplicationPacket* packet){
InitializeLoadData(packet->pBuffer, packet->size);
LoadData(character_number);
LoadData(server_id);
LoadData(spacer);
LoadDataString(name);
}
EQ2Packet* LS_CharSelectList::serialize(int16 version){
Clear();
AddData(num_characters);
AddData(char_data);
if (version <= 561) {
LS_CharListAccountInfoEarlyClient account_info;
account_info.account_id = account_id;
account_info.unknown1 = 0xFFFFFFFF;
account_info.unknown2 = 0;
account_info.maxchars = 7; //live has a max of 7 on gold accounts base.
account_info.unknown4 = 0;
AddData(account_info);
}
else {
LS_CharListAccountInfo account_info;
account_info.account_id = account_id;
account_info.unknown1 = 0xFFFFFFFF;
account_info.unknown2 = 0;
account_info.maxchars = database.GetMaxCharsSetting();
account_info.vet_adv_bonus = database.GetAccountBonus(account_id);
account_info.vet_trade_bonus = 0;
account_info.unknown4 = 0;
for (int i = 0; i < 3; i++)
account_info.unknown5[i] = 0xFFFFFFFF;
account_info.unknown5[3] = 0;
AddData(account_info);
}
return new EQ2Packet(OP_AllCharactersDescReplyMsg, getData(), getDataSize());
}
void LS_CharSelectList::addChar(uchar* data, int16 size){
char_data.append((char*)data, size);
}
void LS_CharSelectList::loadData(int32 account, vector<CharSelectProfile*> charlist, int16 version){
vector<CharSelectProfile*>::iterator itr;
account_id = account;
num_characters = 0;
char_data = "";
CharSelectProfile* character = 0;
for(itr = charlist.begin();itr != charlist.end();itr++){
character = *itr;
int32 serverID = character->packet->getType_int32_ByName("server_id");
if(character->deleted) { // workaround for old clients <= 561 that crash if you delete a char (Doesn't refresh the char panel correctly)
character->packet->setDataByName("name", "(deleted)");
character->packet->setDataByName("charid", 0xFFFFFFFF);
character->packet->setDataByName("name", 0xFFFFFFFF);
character->packet->setDataByName("server_id", 0xFFFFFFFF);
character->packet->setDataByName("created_date", 0xFFFFFFFF);
character->packet->setDataByName("unknown1", 0xFFFFFFFF);
character->packet->setDataByName("unknown2", 0xFFFFFFFF);
character->packet->setDataByName("flags", 0xFF);
}
else if(serverID == 0 || !world_list.FindByID(serverID))
continue;
num_characters++;
character->SaveData(version);
addChar(character->getData(), character->getDataSize());
}
}
void CharSelectProfile::SaveData(int16 in_version){
Clear();
AddData(*packet->serializeString());
}

61
old/login/PacketHeaders.h Normal file
View File

@ -0,0 +1,61 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
*/
#ifndef __PACKET_HEADERS__
#define __PACKET_HEADERS__
#include "../common/types.h"
#include "../common/EQPacket.h"
#include "../common/EQ2_Common_Structs.h"
#include "login_structs.h"
#include "../common/DataBuffer.h"
#include "../common/GlobalHeaders.h"
#include "../common/ConfigReader.h"
#include <vector>
extern ConfigReader configReader;
class CharSelectProfile : public DataBuffer{
public:
CharSelectProfile(int16 version){
deleted = false;
packet = configReader.getStruct("CharSelectProfile",version);
for(int8 i=0;i<24;i++){
packet->setEquipmentByName("equip",0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,i);
}
}
~CharSelectProfile(){
safe_delete(packet);
}
PacketStruct* packet;
void SaveData(int16 in_version);
void Data();
int16 size;
bool deleted;
};
class LS_CharSelectList : public DataBuffer {
public:
int8 num_characters;
int32 account_id;
EQ2Packet* serialize(int16 version);
void addChar(uchar* data, int16 size);
string char_data;
void loadData(int32 account, vector<CharSelectProfile*> charlist, int16 version);
};
class LS_DeleteCharacterRequest : public DataBuffer{
public:
int32 character_number;
int32 server_id;
int32 spacer;
EQ2_16BitString name;
void loadData(EQApplicationPacket* packet);
};
#endif

View File

@ -0,0 +1,67 @@
#include "../net.h"
#include "../LWorld.h"
#include <boost/algorithm/string.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
extern ClientList client_list;
extern LWorldList world_list;
extern NetConnection net;
void NetConnection::Web_loginhandle_status(const http::request<http::string_body>& req, http::response<http::string_body>& res) {
res.set(http::field::content_type, "application/json");
boost::property_tree::ptree pt;
pt.put("web_status", "online");
pt.put("login_status", net.login_running ? "online" : "offline");
pt.put("login_uptime", (getCurrentTimestamp() - net.login_uptime));
auto [days, hours, minutes, seconds] = convertTimestampDuration((getCurrentTimestamp() - net.login_uptime));
std::string uptime_str("Days: " + std::to_string(days) + ", " + "Hours: " + std::to_string(hours) + ", " + "Minutes: " + std::to_string(minutes) + ", " + "Seconds: " + std::to_string(seconds));
pt.put("login_uptime_string", uptime_str);
pt.put("world_count", world_list.GetCount(ConType::World));
pt.put("client_count", net.numclients);
std::ostringstream oss;
boost::property_tree::write_json(oss, pt);
std::string json = oss.str();
res.body() = json;
res.prepare_payload();
}
void NetConnection::Web_loginhandle_worlds(const http::request<http::string_body>& req, http::response<http::string_body>& res) {
world_list.PopulateWorldList(res);
}
void LWorldList::PopulateWorldList(http::response<http::string_body>& res) {
struct in_addr in;
res.set(http::field::content_type, "application/json");
boost::property_tree::ptree maintree;
std::ostringstream oss;
map<int32,LWorld*>::iterator map_list;
for( map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) {
LWorld* world = map_list->second;
in.s_addr = world->GetIP();
if (world->GetType() == World) {
boost::property_tree::ptree pt;
pt.put("id", world->GetID());
pt.put("world_name", world->GetName());
pt.put("status", (world->GetStatus() == 1) ? "online" : "offline");
pt.put("num_players", world->GetPlayerNum());
pt.put("ip_addr", inet_ntoa(in));
maintree.push_back(std::make_pair("", pt));
}
}
boost::property_tree::ptree result;
result.add_child("WorldServers", maintree);
boost::property_tree::write_json(oss, result);
std::string json = oss.str();
res.body() = json;
res.prepare_payload();
}

813
old/login/client.cpp Normal file
View File

@ -0,0 +1,813 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
*/
#include "../common/debug.h"
#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock.h>
#include <process.h>
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#endif
#include <string.h>
#include <iomanip>
#include <stdlib.h>
#include <assert.h>
#include "net.h"
#include "client.h"
#include "../common/EQStream.h"
#include "../common/packet_dump.h"
#include "../common/packet_functions.h"
#include "../common/emu_opcodes.h"
#include "../common/MiscFunctions.h"
#include "LWorld.h"
#include "LoginDatabase.h"
#include "../common/ConfigReader.h"
#include "../common/Log.h"
extern NetConnection net;
extern LWorldList world_list;
extern ClientList client_list;
extern LoginDatabase database;
extern map<int16,OpcodeManager*>EQOpcodeManager;
extern ConfigReader configReader;
using namespace std;
Client::Client(EQStream* ieqnc) {
eqnc = ieqnc;
ip = eqnc->GetrIP();
port = ntohs(eqnc->GetrPort());
account_id = 0;
lsadmin = 0;
worldadmin = 0;
lsstatus = 0;
version = 0;
kicked = false;
verified = false;
memset(bannedreason, 0, sizeof(bannedreason));
//worldresponse_timer = new Timer(10000);
//worldresponse_timer->Disable();
memset(key,0,10);
LoginMode = None;
num_updates = 0;
updatetimer = new Timer(500);
updatelisttimer = new Timer(10000);
//keepalive = new Timer(5000);
//logintimer = new Timer(500); // Give time for the servers to send updates
//keepalive->Start();
//updatetimer->Start();
//logintimer->Disable();
disconnectTimer = 0;
memset(ClientSession,0,25);
request_num = 0;
login_account = 0;
createRequest = 0;
playWaitTimer = NULL;
start = false;
update_position = 0;
update_packets = 0;
needs_world_list = true;
sent_character_list = false;
}
Client::~Client() {
//safe_delete(worldresponse_timer);
//safe_delete(logintimer);
safe_delete(login_account);
eqnc->Close();
safe_delete(playWaitTimer);
safe_delete(createRequest);
safe_delete(disconnectTimer);
safe_delete(updatetimer);
}
bool Client::Process() {
if(!start && !eqnc->CheckActive()){
if(!playWaitTimer)
playWaitTimer = new Timer(5000);
else if(playWaitTimer->Check()){
safe_delete(playWaitTimer);
return false;
}
return true;
}
else if(!start){
safe_delete(playWaitTimer);
start = true;
}
if (disconnectTimer && disconnectTimer->Check())
{
safe_delete(disconnectTimer);
getConnection()->SendDisconnect();
}
if (!kicked) {
/************ Get all packets from packet manager out queue and process them ************/
EQApplicationPacket *app = 0;
/*if(logintimer && logintimer->Check())
{
database.LoadCharacters(GetLoginAccount());
SendLoginAccepted();
logintimer->Disable();
}*/
/*if(worldresponse_timer && worldresponse_timer->Check())
{
FatalError(WorldDownErrorMessage);
worldresponse_timer->Disable();
}*/
if(playWaitTimer != NULL && playWaitTimer->Check ( ) )
{
SendPlayFailed(PLAY_ERROR_SERVER_TIMEOUT);
safe_delete(playWaitTimer);
}
if(!needs_world_list && updatetimer && updatetimer->Check()){
if(updatelisttimer && updatelisttimer->Check()){
if(num_updates >= 180){ //30 minutes
getConnection()->SendDisconnect();
}
else{
vector<PacketStruct*>::iterator itr;
if(update_packets){
for(itr = update_packets->begin(); itr != update_packets->end(); itr++){
safe_delete(*itr);
}
}
safe_delete(update_packets);
update_packets = world_list.GetServerListUpdate(version);
}
num_updates++;
}
else{
if(!update_packets){
update_packets = world_list.GetServerListUpdate(version);
}
else{
if(update_position < update_packets->size()){
QueuePacket(update_packets->at(update_position)->serialize());
update_position++;
}
else
update_position = 0;
}
}
}
while(app = eqnc->PopPacket())
{
switch(app->GetOpcode())
{
case OP_LoginRequestMsg:{
DumpPacket(app);
PacketStruct* packet = configReader.getStruct("LS_LoginRequest", 1);
if(packet && packet->LoadPacketData(app->pBuffer,app->size)){
version = packet->getType_int16_ByName("version");
LogWrite(LOGIN__DEBUG, 0, "Login", "Classic Client Version Provided: %i", version);
if (version == 0 || EQOpcodeManager.count(GetOpcodeVersion(version)) == 0)
{
safe_delete(packet);
packet = configReader.getStruct("LS_LoginRequest", 1208);
if (packet && packet->LoadPacketData(app->pBuffer, app->size)) {
version = packet->getType_int16_ByName("version");
}
else
break;
}
//[7:19 PM] Kirmmin: Well, I very quickly learned that unknown3 in LS_LoginRequest packet is the same value as cl_eqversion in the eq2_defaults.ini file.
LogWrite(LOGIN__DEBUG, 0, "Login", "New Client Version Provided: %i", version);
if (EQOpcodeManager.count(GetOpcodeVersion(version)) == 0) {
LogWrite(LOGIN__ERROR, 0, "Login", "Incompatible client version provided: %i", version);
SendLoginDenied();
return false;
}
if(EQOpcodeManager.count(GetOpcodeVersion(version)) > 0 && getConnection()){
getConnection()->SetClientVersion(GetVersion());
EQ2_16BitString username = packet->getType_EQ2_16BitString_ByName("username");
EQ2_16BitString password = packet->getType_EQ2_16BitString_ByName("password");
LoginAccount* acct = database.LoadAccount(username.data.c_str(),password.data.c_str(), net.IsAllowingAccountCreation());
if(acct){
Client* otherclient = client_list.FindByLSID(acct->getLoginAccountID());
if(otherclient)
otherclient->getConnection()->SendDisconnect(); // This person is already logged in, we don't want them logged in twice, kick the previous client as it might be a ghost
}
if(acct){
SetAccountName(username.data.c_str());
database.UpdateAccountIPAddress(acct->getLoginAccountID(), getConnection()->GetrIP());
database.UpdateAccountClientDataVersion(acct->getLoginAccountID(), version);
LogWrite(LOGIN__INFO, 0, "Login", "%s successfully logged in.", (char*)username.data.c_str());
}
else
{
if (username.size > 0)
LogWrite(LOGIN__ERROR, 0, "Login", "%s login failed!", (char*)username.data.c_str());
else
LogWrite(LOGIN__ERROR, 0, "Login", "[UNKNOWN USER] login failed!");
}
if(!acct)
SendLoginDenied();
else{
needs_world_list = true;
SetLoginAccount(acct);
SendLoginAccepted();
}
}
else{
cout << "Error bad version: " << version << endl;
SendLoginDeniedBadVersion();
}
}
else{
cout << "Error loading LS_LoginRequest packet: \n";
//DumpPacket(app);
}
safe_delete(packet);
break;
}
case OP_KeymapLoadMsg:{
// cout << "Received OP_KeymapNoneMsg\n";
//dunno what this is for
break;
}
case OP_AllWSDescRequestMsg:{
SendWorldList();
needs_world_list = false;
if(!sent_character_list) {
database.LoadCharacters(GetLoginAccount(), GetVersion());
sent_character_list = true;
}
SendCharList();
break;
}
case OP_LsClientCrashlogReplyMsg:{
// DumpPacket(app);
SaveErrorsToDB(app, "Crash Log", GetVersion());
break;
}
case OP_LsClientVerifylogReplyMsg:{
// DumpPacket(app);
SaveErrorsToDB(app, "Verify Log", GetVersion());
break;
}
case OP_LsClientAlertlogReplyMsg:{
// DumpPacket(app);
SaveErrorsToDB(app, "Alert Log", GetVersion());
break;
}
case OP_LsClientBaselogReplyMsg:{
// DumpPacket(app);
SaveErrorsToDB(app, "Base Log", GetVersion());
break;
}
case OP_AllCharactersDescRequestMsg:{
break;
}
case OP_CreateCharacterRequestMsg:{
PacketStruct* packet = configReader.getStruct("CreateCharacter", GetVersion());
DumpPacket(app);
playWaitTimer = new Timer ( 15000 );
playWaitTimer->Start ( );
LogWrite(WORLD__INFO, 1, "World", "Character creation request from account %s", GetAccountName());
if(packet->LoadPacketData(app->pBuffer,app->size, GetVersion() <= 561 ? false : true)){
DumpPacket(app->pBuffer, app->size);
packet->setDataByName("account_id",GetAccountID());
LWorld* world_server = world_list.FindByID(packet->getType_int32_ByName("server_id"));
if(!world_server)
{
DumpPacket(app->pBuffer, app->size);
cout << GetAccountName() << " attempted creation of character with an invalid server id of: " << packet->getType_int32_ByName("server_id") << "\n";
break;
}
else
{
createRequest = packet;
ServerPacket* outpack = new ServerPacket(ServerOP_CharacterCreate, app->size+sizeof(int16));
int16 out_version = GetVersion();
memcpy(outpack->pBuffer, &out_version, sizeof(int16));
memcpy(outpack->pBuffer + sizeof(int16), app->pBuffer, app->size);
uchar* tmp = outpack->pBuffer;
if(out_version<=283)
tmp+=2;
else if(out_version == 373) {
tmp += 6;
}
else
tmp += 7;
int32 account_id = GetAccountID();
memcpy(tmp, &account_id, sizeof(int32));
world_server->SendPacket(outpack);
safe_delete(outpack);
}
}
else{
LogWrite(WORLD__ERROR, 1, "World", "Error in character creation request from account %s!", GetAccountName());
safe_delete(packet);
}
// world_list.SendWorldChanged(create.profile.server_id, false, this);
break;
}
case OP_PlayCharacterRequestMsg:{
int32 char_id = 0;
int32 server_id = 0;
PacketStruct* request = configReader.getStruct("LS_PlayRequest",GetVersion());
if(request && request->LoadPacketData(app->pBuffer,app->size)){
char_id = request->getType_int32_ByName("char_id");
if (GetVersion() <= 283) {
server_id = database.GetServer(GetAccountID(), char_id, request->getType_EQ2_16BitString_ByName("name").data);
}
else {
server_id = request->getType_int32_ByName("server_id");
}
LWorld* world = world_list.FindByID(server_id);
string name = database.GetCharacterName(char_id,server_id,GetAccountID());
if(world && name.length() > 0){
pending_play_char_id = char_id;
ServerPacket* outpack = new ServerPacket(ServerOP_UsertoWorldReq, sizeof(UsertoWorldRequest_Struct));
UsertoWorldRequest_Struct* req = (UsertoWorldRequest_Struct*)outpack->pBuffer;
req->char_id = char_id;
req->lsaccountid = GetAccountID();
req->worldid = server_id;
struct in_addr in;
in.s_addr = GetIP();
strcpy(req->ip_address, inet_ntoa(in));
world->SendPacket(outpack);
delete outpack;
safe_delete(playWaitTimer);
playWaitTimer = new Timer ( 5000 );
playWaitTimer->Start ( );
}
else{
cout << GetAccountName() << " sent invalid Play Request: \n";
SendPlayFailed(PLAY_ERROR_PROBLEM);
DumpPacket(app);
}
}
safe_delete(request);
break;
}
case OP_DeleteCharacterRequestMsg:{
PacketStruct* request = configReader.getStruct("LS_DeleteCharacterRequest", GetVersion());
PacketStruct* response = configReader.getStruct("LS_DeleteCharacterResponse", GetVersion());
if(request && response && request->LoadPacketData(app->pBuffer,app->size)){
EQ2_16BitString name = request->getType_EQ2_16BitString_ByName("name");
int32 acct_id = GetAccountID();
int32 char_id = request->getType_int32_ByName("char_id");
int32 server_id = request->getType_int32_ByName("server_id");
if(database.VerifyDelete(acct_id, char_id, name.data.c_str())){
response->setDataByName("response", 1);
GetLoginAccount()->removeCharacter((char*)name.data.c_str(), GetVersion());
LWorld* world_server = world_list.FindByID(server_id);
if(world_server != NULL)
world_server->SendDeleteCharacter ( char_id , acct_id );
}
else
response->setDataByName("response", 0);
response->setDataByName("server_id", server_id);
response->setDataByName("char_id", char_id);
response->setDataByName("account_id", account_id);
response->setMediumStringByName("name", (char*)name.data.c_str());
response->setDataByName("max_characters", 10);
EQ2Packet* outapp = response->serialize();
QueuePacket(outapp);
this->SendCharList();
}
safe_delete(request);
safe_delete(response);
break;
}
default: {
const char* name = app->GetOpcodeName();
if (name)
LogWrite(OPCODE__DEBUG, 1, "Opcode", "%s Received %04X (%i)", name, app->GetRawOpcode(), app->GetRawOpcode());
else
LogWrite(OPCODE__DEBUG, 1, "Opcode", "Received %04X (%i)", app->GetRawOpcode(), app->GetRawOpcode());
}
}
delete app;
}
}
if (!eqnc->CheckActive()) {
return false;
}
return true;
}
void Client::SaveErrorsToDB(EQApplicationPacket* app, char* type, int32 version){
int32 size = 0;
z_stream zstream;
if (version >= 546) {
memcpy(&size, app->pBuffer + sizeof(int32), sizeof(int32));
zstream.next_in = app->pBuffer + 8;
zstream.avail_in = app->size - 8;
}
else { //box set
size = 0xFFFF;
zstream.next_in = app->pBuffer + 2;
zstream.avail_in = app->size - 2;
}
size++;
char* message = new char[size];
memset(message, 0, size);
int zerror = 0;
zstream.next_out = (BYTE*)message;
zstream.avail_out = size;
zstream.zalloc = Z_NULL;
zstream.zfree = Z_NULL;
zstream.opaque = Z_NULL;
zerror = inflateInit( &zstream);
if(zerror != Z_OK) {
safe_delete_array(message);
return;
}
zerror = inflate( &zstream, 0 );
if(message && strlen(message) > 0)
database.SaveClientLog(type, message, GetLoginAccount()->getLoginName(), GetVersion());
safe_delete_array(message);
}
void Client::CharacterApproved(int32 server_id,int32 char_id)
{
if(createRequest && server_id == createRequest->getType_int32_ByName("server_id")){
LWorld* world_server = world_list.FindByID(server_id);
if(!world_server)
return;
PacketStruct* packet = configReader.getStruct("LS_CreateCharacterReply", GetVersion());
if(packet){
packet->setDataByName("account_id", GetAccountID());
packet->setDataByName("unknown", 0xFFFFFFFF);
packet->setDataByName("response", CREATESUCCESS_REPLY);
packet->setMediumStringByName("name", (char*)createRequest->getType_EQ2_16BitString_ByName("name").data.c_str());
EQ2Packet* outapp = packet->serialize();
QueuePacket(outapp);
safe_delete(packet);
database.SaveCharacter(createRequest, GetLoginAccount(),char_id, GetVersion());
// refresh characters for this account
database.LoadCharacters(GetLoginAccount(), GetVersion());
SendCharList();
if (GetVersion() <= 561)
{
pending_play_char_id = char_id;
ServerPacket* outpack = new ServerPacket(ServerOP_UsertoWorldReq, sizeof(UsertoWorldRequest_Struct));
UsertoWorldRequest_Struct* req = (UsertoWorldRequest_Struct*)outpack->pBuffer;
req->char_id = char_id;
req->lsaccountid = GetAccountID();
req->worldid = server_id;
struct in_addr in;
in.s_addr = GetIP();
strcpy(req->ip_address, inet_ntoa(in));
world_server->SendPacket(outpack);
delete outpack;
}
}
}
else{
cout << GetAccountName() << " received invalid CharacterApproval from server: " << server_id << endl;
}
safe_delete(createRequest);
}
void Client::CharacterRejected(int8 reason_number)
{
PacketStruct* packet = configReader.getStruct("LS_CreateCharacterReply", GetVersion());
if(createRequest && packet){
packet->setDataByName("account_id", GetAccountID());
int8 clientReasonNum = reason_number;
// reason numbers change and instead of updating the world server
// the login server will hold the up to date #'s
/*
switch(reason_number)
{
// these error codes seem to be removed now, they shutdown the client rather immediately
// for now we are just going to play a joke on them and say they can't create a new character.
case INVALIDRACE_REPLY:
case INVALIDGENDER_REPLY:
clientReasonNum = 8;
break;
case BADNAMELENGTH_REPLY:
clientReasonNum = 9;
break;
case NAMEINVALID_REPLY:
clientReasonNum = 10;
break;
case NAMEFILTER_REPLY:
clientReasonNum = 11;
break;
case NAMETAKEN_REPLY:
clientReasonNum = 12;
break;
case OVERLOADEDSERVER_REPLY:
clientReasonNum = 13;
break;
}
*/
packet->setDataByName("response", clientReasonNum);
packet->setMediumStringByName("name", "");
EQ2Packet* outapp = packet->serialize();
QueuePacket(outapp);
safe_delete(packet);
}
/*LS_CreateCharacterReply reply(GetAccountID(), reason_number, create.profile.name.data);
EQ2Packet* outapp = reply.serialize();
QueuePacket(outapp);
create.Clear();*/
}
void Client::SendCharList(){
/*PacketStruct* packet = configReader.getStruct("LS_CreateCharacterReply");
packet->setDataByName("account_id", GetAccountID());
packet->setDataByName("response", reason_number);
packet->setDataByName("name", &create.profile.name);
EQ2Packet* outapp = packet->serialize();
QueuePacket(outapp);
safe_delete(packet);*/
LogWrite(LOGIN__INFO, 0, "Login", "[%s] sending character list.", GetAccountName());
LS_CharSelectList list;
list.loadData(GetAccountID(), GetLoginAccount()->charlist, GetVersion());
EQ2Packet* outapp = list.serialize(GetVersion());
DumpPacket(outapp->pBuffer, outapp->size);
QueuePacket(outapp);
}
void Client::SendLoginDeniedBadVersion(){
EQ2Packet* app = new EQ2Packet(OP_LoginReplyMsg, 0, sizeof(LS_LoginResponse));
LS_LoginResponse* ls_response = (LS_LoginResponse*)app->pBuffer;
ls_response->reply_code = 6;
ls_response->unknown03 = 0xFFFFFFFF;
ls_response->unknown04 = 0xFFFFFFFF;
QueuePacket(app);
StartDisconnectTimer();
}
void Client::SendLoginDenied(){
EQ2Packet* app = new EQ2Packet(OP_LoginReplyMsg, 0, sizeof(LS_LoginResponse));
LS_LoginResponse* ls_response = (LS_LoginResponse*)app->pBuffer;
ls_response->reply_code = 1;
// reply_codes for AoM:
/* 1 = Login rejected: Invalid username or password. Please try again.
2 = Login rejected: Server thinks your account is currently playing; you may have to wait "
"a few minutes for it to clear, then try again
6 = Login rejected: The client's version does not match the server's. Please re-run the patcher.
7 = Login rejected: You have no scheduled playtimes.
8 = Your account does not have the features required to play on this server.
11 = The client's build does not match the server's. Please re-run the patcher.
12 = You must update your password in order to log in. Pressing OK will op"
"en your web browser to the SOE password management page
Other Value > 1 = Login rejected for an unknown reason.
*/
ls_response->unknown03 = 0xFFFFFFFF;
ls_response->unknown04 = 0xFFFFFFFF;
QueuePacket(app);
StartDisconnectTimer();
}
void Client::SendLoginAccepted(int32 account_id, int8 login_response) {
PacketStruct* packet = configReader.getStruct("LS_LoginReplyMsg", GetVersion());
int i = 0;
if (packet)
{
packet->setDataByName("account_id", account_id);
packet->setDataByName("login_response", login_response);
packet->setDataByName("do_not_force_soga", 1);
// sub_level 0xFFFFFFFF = blacks out all portraits for class alignments, considered non membership
// sub_level > 0 = class alignments still required, but portraits are viewable and race selectable
// sub_level = 2 membership, you can 'create characters on time locked servers' vs standard
// sub_level = 0 forces popup on close to web browser
packet->setDataByName("sub_level", net.GetDefaultSubscriptionLevel());
packet->setDataByName("race_flag", 0x1FFFFF);
packet->setDataByName("class_flag", 0x7FFFFFE);
packet->setMediumStringByName("username", GetAccountName());
packet->setMediumStringByName("password", GetAccountName());
// unknown5
// full support = 0x7CFF
// 1 << 12 (-4096) = missing echoes of faydwer, disables Fae and Arasai (black portraits) and kelethin as starting city
// 1 << 13 (-8192) = disables sarnak (black portraits) and gorowyn as starting city
packet->setDataByName("unknown5", net.GetExpansionFlag());
packet->setDataByName("unknown6", 0xFF);
packet->setDataByName("unknown6", 0xFF, 1);
packet->setDataByName("unknown6", 0xFF, 2);
// controls class access / playable characters
packet->setDataByName("unknown10", 0xFF);
// packet->setDataByName("unknown7a", 0x0101);
// packet->setDataByName("race_unknown", 0x01);
packet->setDataByName("unknown7", net.GetEnabledRaces()); // 0x01-0xFF disable extra races FAE(16) ARASAI (17) SARNAK (18) -- with 4096/8192 flags, no visibility of portraits
packet->setDataByName("unknown7a", 0xEE);
packet->setDataByName("unknown8", net.GetCitiesFlag(), 1); // dword_1ECBA18 operand for race flag packs (sublevel 0,1,2?) -- (sublevel -1) controls starting zones omission 0xEE vs 0xCF (CF misses halas)
/*
1 = city of qeynos
2 = city of freeport
4 = city of kelethin
8 = city of neriak
16 = gorowyn
32 = new halas
64 = queens colony
128 = outpost overlord
*/
EQ2Packet* outapp = packet->serialize();
QueuePacket(outapp);
safe_delete(packet);
}
}
void Client::SendWorldList(){
EQ2Packet* pack = world_list.MakeServerListPacket(lsadmin, version);
EQ2Packet* dupe = pack->Copy();
DumpPacket(dupe->pBuffer,dupe->size);
QueuePacket(dupe);
SendLoginAccepted(0, 10); // triggers a different code path in the client to set certain flags
return;
}
void Client::QueuePacket(EQ2Packet* app){
eqnc->EQ2QueuePacket(app);
}
void Client::WorldResponse(int32 worldid, int8 response, char* ip_address, int32 port, int32 access_key)
{
LWorld* world = world_list.FindByID(worldid);
if(world == 0) {
FatalError(0);
return;
}
if(response != 1){
if(response == PLAY_ERROR_CHAR_NOT_LOADED){
string pending_play_char_name = database.GetCharacterName(pending_play_char_id, worldid, GetAccountID());
if(database.VerifyDelete(GetAccountID(), pending_play_char_id, pending_play_char_name.c_str())){
GetLoginAccount()->removeCharacter((char*)pending_play_char_name.c_str(), GetVersion());
}
}
FatalError(response);
return;
}
PacketStruct* response_packet = configReader.getStruct("LS_PlayResponse", GetVersion());
if(response_packet){
safe_delete(playWaitTimer);
response_packet->setDataByName("response", 1);
response_packet->setSmallStringByName("server", ip_address);
response_packet->setDataByName("port", port);
response_packet->setDataByName("account_id", GetAccountID());
response_packet->setDataByName("access_code", access_key);
EQ2Packet* outapp = response_packet->serialize();
QueuePacket(outapp);
safe_delete(response_packet);
}
return;
}
void Client::FatalError(int8 response) {
safe_delete(playWaitTimer);
SendPlayFailed(response);
}
void Client::SendPlayFailed(int8 response){
PacketStruct* response_packet = configReader.getStruct("LS_PlayResponse", GetVersion());
if(response_packet){
response_packet->setDataByName("response", response);
response_packet->setSmallStringByName("server", "");
response_packet->setDataByName("port", 0);
response_packet->setDataByName("account_id", GetAccountID());
response_packet->setDataByName("access_code", 0);
EQ2Packet* outapp = response_packet->serialize();
QueuePacket(outapp);
safe_delete(response_packet);
}
}
void ClientList::Add(Client* client) {
MClientList.writelock();
client_list[client] = true;
MClientList.releasewritelock();
}
Client* ClientList::Get(int32 ip, int16 port) {
Client* ret = 0;
map<Client*, bool>::iterator itr;
MClientList.readlock();
for(itr = client_list.begin(); itr != client_list.end(); itr++){
if(itr->first->GetIP() == ip && itr->first->GetPort() == port){
ret = itr->first;
break;
}
}
MClientList.releasereadlock();
return ret;
}
void ClientList::FindByCreateRequest(){
Client* client = 0;
map<Client*, bool>::iterator itr;
MClientList.readlock();
for(itr = client_list.begin(); itr != client_list.end(); itr++){
if(itr->first->AwaitingCharCreationRequest()){
if(!client)
client = itr->first;
else{
client = 0;//more than 1 character waiting, dont want to send rejection to wrong one
break;
}
}
}
MClientList.releasereadlock();
if(client)
client->CharacterRejected(UNKNOWNERROR_REPLY);
}
Client* ClientList::FindByLSID(int32 lsaccountid) {
Client* client = 0;
map<Client*, bool>::iterator itr;
MClientList.readlock();
for(itr = client_list.begin(); itr != client_list.end(); itr++){
if(itr->first->GetAccountID() == lsaccountid){
client = itr->first;
break;
}
}
MClientList.releasereadlock();
return client;
}
void ClientList::SendPacketToAllClients(EQ2Packet* app){
Client* client = 0;
map<Client*, bool>::iterator itr;
MClientList.readlock();
if(client_list.size() > 0){
for(itr = client_list.begin(); itr != client_list.end(); itr++){
itr->first->QueuePacket(app->Copy());
}
}
safe_delete(app);
MClientList.releasereadlock();
}
void ClientList::Process() {
Client* client = 0;
vector<Client*> erase_list;
map<Client*, bool>::iterator itr;
MClientList.readlock();
for(itr = client_list.begin(); itr != client_list.end(); itr++){
client = itr->first;
if(!client->Process())
erase_list.push_back(client);
}
MClientList.releasereadlock();
if(erase_list.size() > 0){
vector<Client*>::iterator erase_itr;
MClientList.writelock();
for(erase_itr = erase_list.begin(); erase_itr != erase_list.end(); erase_itr++){
client = *erase_itr;
struct in_addr in;
in.s_addr = client->getConnection()->GetRemoteIP();
net.numclients--;
LogWrite(LOGIN__INFO, 0, "Login", "Removing client from ip: %s on port %i, Account Name: %s", inet_ntoa(in), ntohs(client->getConnection()->GetRemotePort()), client->GetAccountName());
client->getConnection()->Close();
net.UpdateWindowTitle();
client_list.erase(client);
}
MClientList.releasewritelock();
}
}
void Client::StartDisconnectTimer() {
if (!disconnectTimer)
{
disconnectTimer = new Timer(1000);
disconnectTimer->Start();
}
}

131
old/login/client.h Normal file
View File

@ -0,0 +1,131 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
*/
#ifndef CLIENT_H
#define CLIENT_H
#include "../common/linked_list.h"
#include "../common/timer.h"
#include "../common/TCPConnection.h"
#include "login_structs.h"
#include "LoginAccount.h"
#include "../common/PacketStruct.h"
#include <string>
#include <vector>
enum eLoginMode { None, Normal, Registration };
class DelayQue;
class Client
{
public:
Client(EQStream* ieqnc);
~Client();
void SendLoginDenied();
void SendLoginDeniedBadVersion();
void SendLoginAccepted(int32 account_id = 1, int8 login_response = 0);
void SendWorldList();
void SendCharList();
int16 AddWorldToList2(uchar* buffer, char* name, int32 id, int16* flags);
void GenerateChecksum(EQApplicationPacket* outapp);
int8 LoginKey[10];
int8 ClientSession[25];
bool Process();
void SaveErrorsToDB(EQApplicationPacket* app, char* type, int32 version);
void CharacterApproved(int32 server_id,int32 char_id);
void CharacterRejected(int8 reason_number);
EQStream* getConnection() { return eqnc; }
LoginAccount* GetLoginAccount() { return login_account; }
void SetLoginAccount(LoginAccount* in_account) {
login_account = in_account;
if(in_account)
account_id = in_account->getLoginAccountID();
}
int16 GetVersion(){ return version; }
char* GetKey() { return key; }
void SetKey(char* in_key) { strcpy(key,in_key); }
int32 GetIP() { return ip; }
int16 GetPort() { return port; }
int32 GetAccountID() { return account_id; }
const char* GetAccountName(){ return (char*)account_name.c_str(); }
void SetAccountName(const char* name){ account_name = string(name); }
void ProcessLogin(char* name, char* pass,int seq=0);
void QueuePacket(EQ2Packet* app);
void FatalError(int8 response);
void WorldResponse(int32 worldid, int8 response, char* ip_address, int32 port, int32 access_key);
bool AwaitingCharCreationRequest(){
if(createRequest)
return true;
else
return false;
}
Timer* updatetimer;
Timer* updatelisttimer;
Timer* disconnectTimer;
//Timer* keepalive;
//Timer* logintimer;
int16 packettotal;
int32 requested_server_id;
int32 request_num;
LinkedList<DelayQue*> delay_que;
void SendPlayFailed(int8 response);
void StartDisconnectTimer();
private:
string pending_play_char_name;
int32 pending_play_char_id;
int8 update_position;
int16 num_updates;
vector<PacketStruct*>* update_packets;
LoginAccount* login_account;
EQStream* eqnc;
int32 ip;
int16 port;
int32 account_id;
string account_name;
char key[10];
int8 lsadmin;
sint16 worldadmin;
int lsstatus;
bool kicked;
bool verified;
bool start;
bool needs_world_list;
int16 version;
char bannedreason[30];
bool sent_character_list;
eLoginMode LoginMode;
PacketStruct* createRequest;
Timer* playWaitTimer;
};
class ClientList
{
public:
ClientList() {}
~ClientList() {}
void Add(Client* client);
Client* Get(int32 ip, int16 port);
Client* FindByLSID(int32 lsaccountid);
void FindByCreateRequest();
void SendPacketToAllClients(EQ2Packet* app);
void Process();
private:
Mutex MClientList;
map<Client*, bool> client_list;
};
class DelayQue {
public:
DelayQue(Timer* in_timer, EQApplicationPacket* in_packet){
timer = in_timer;
packet = in_packet;
};
Timer* timer;
EQApplicationPacket* packet;
};
#endif

52
old/login/login_opcodes.h Normal file
View File

@ -0,0 +1,52 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
*/
#ifndef LOGIN_OPCODES_H
#define LOGIN_OPCODES_H
#define OP_Login2 0x0200
#define OP_GetLoginInfo 0x0300
#define OP_SendServersFragment 0x0D00
#define OP_LoginInfo 0x0100
#define OP_SessionId 0x0900
#define OP_Disconnect 0x0500
//#define OP_Reg_SendPricing 0x0400
#define OP_AllFinish 0x0500
#define OP_Ack5 0x1500
#define OP_Chat_ChannelList 0x0600
#define OP_Chat_JoinChannel 0x0700
#define OP_Chat_PartChannel 0x0800
#define OP_Chat_ChannelMessage 0x0930
#define OP_Chat_Tell 0x0a00
#define OP_Chat_SysMsg 0x0b00
#define OP_Chat_CreateChannel 0x0c00
#define OP_Chat_ChangeChannel 0x0d00
#define OP_Chat_DeleteChannel 0x0e00
#define OP_Chat_UserList 0x1000
#define OP_Reg_GetPricing 0x1a00 // for new account signup
#define OP_Reg_SendPricing 0x1b00
#define OP_RegisterAccount 0x2300
#define OP_Chat_ChannelWelcome 0x2400
#define OP_Chat_PopupMakeWindow 0x3000
#define OP_BillingInfoAccepted 0x3300 // i THINK =p
#define OP_CheckGameCardValid 0x3400
#define OP_GameCardTimeLeft 0x3600
#define OP_AccountExpired 0x4200
#define OP_Reg_GetPricing2 0x4400 // for re-registering
#define OP_ChangePassword 0x4500
#define OP_ServerList 0x4600
#define OP_SessionKey 0x4700
#define OP_RequestServerStatus 0x4800
#define OP_SendServerStatus 0x4A00
#define OP_Reg_ChangeAcctLogin 0x5100
#define OP_LoginBanner 0x5200
#define OP_Chat_GuildsList 0x5500
#define OP_Chat_GuildEdit 0x5700
#define OP_Version 0x5900
#define OP_RenewAccountBillingInfo 0x7a00
#endif /* LOGIN_OPCODES_H */

61
old/login/login_structs.h Normal file
View File

@ -0,0 +1,61 @@
/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
*/
#ifndef LOGIN_STRUCTS_H
#define LOGIN_STRUCTS_H
#include "../common/types.h"
#include "PacketHeaders.h"
#pragma pack(1)
struct LS_LoginRequest{
EQ2_16BitString AccessCode;
EQ2_16BitString unknown1;
EQ2_16BitString username;
EQ2_16BitString password;
EQ2_16BitString unknown2[4];
int16 unknown3;
int32 unknown4[2];
};
struct LS_WorldStatusChanged{
int32 server_id;
int8 up;
int8 locked;
int8 hidden;
};
struct LS_PlayCharacterRequest{
int32 character_id;
int32 server_id;
int16 unknown1;
};
struct LS_OLDPlayCharacterRequest{
int32 character_id;
EQ2_16BitString name;
};
struct LS_CharListAccountInfoEarlyClient {
int32 account_id;
int32 unknown1;
int16 unknown2;
int32 maxchars;
int8 unknown4; // 15 bytes total
// int8 unknown7; // adds 'free' option..
};
struct LS_CharListAccountInfo{
int32 account_id;
int32 unknown1;
int16 unknown2;
int32 maxchars;
// DoF does not have the following data
int8 unknown4;
int32 unknown5[4];
int8 vet_adv_bonus; // sets Veteran Bonus under 'Select Character' yellow (vs greyed out), adventure/tradeskill bonus 200%
int8 vet_trade_bonus; // when 1 (count?) provides free upgrade option for character to lvl 90 (heroic character) -- its a green 'Free' up arrow next to the character that is selected in char select
}; // 33 bytes
#pragma pack()
#endif

Some files were not shown because too many files have changed in this diff Show More