mirror of
https://github.com/polserver/polserver
synced 2026-08-13 08:23:08 -04:00
572 lines
No EOL
19 KiB
C++
572 lines
No EOL
19 KiB
C++
/*
|
|
History
|
|
=======
|
|
|
|
2006/05/23 Shinigami: added missing Check to ExportedPacketHookHandler() for
|
|
missing default_handler in Packets with SubCommands
|
|
2007/08/19 Shinigami: fixed Memory Leak in PacketHook functions
|
|
2009/08/03 MuadDib: Renaming of MSG_HANDLER_6017 and related, to MSG_HANDLER_V2 for better description
|
|
Renamed secondary handler class to *_V2 for naming convention
|
|
2009/08/25 Shinigami: STLport-5.2.1 fix: params in call of Log2()
|
|
2009/09/03 MuadDib: Relocation of account related cpp/h
|
|
|
|
Notes
|
|
=======
|
|
Version member: Positive Integer. This is used to translate the "version" of the packet structure
|
|
to the correct internal core Message Handler (Default 1, which translates to use handler[]). Each
|
|
new Handler added to the core needs a new Version number here. As of 8/3/09 there is only 2.
|
|
|
|
*/
|
|
|
|
#include "clib/stl_inc.h"
|
|
|
|
#include "clib/cfgelem.h"
|
|
#include "clib/cfgfile.h"
|
|
#include "clib/endian.h"
|
|
#include "clib/logfile.h"
|
|
#include "clib/fileutil.h"
|
|
#include "clib/strutil.h"
|
|
|
|
#include "bscript/bobject.h"
|
|
#include "bscript/impstr.h"
|
|
|
|
#include "plib/pkg.h"
|
|
|
|
#include "accounts/account.h"
|
|
#include "charactr.h"
|
|
#include "client.h"
|
|
#include "msghandl.h"
|
|
#include "packethooks.h"
|
|
#include "packetscrobj.h"
|
|
|
|
//stores information about each packet and its script & default handler
|
|
std::vector<PacketHookData> packet_hook_data(256);
|
|
std::vector<PacketHookData> packet_hook_data_v2(256);
|
|
std::vector<PacketHookData> packet_hook_data_v3(256);
|
|
|
|
//MSG_HANDLER and handler array defined in pol.cpp
|
|
typedef struct {
|
|
int msglen; // if 0, no message handler defined.
|
|
void (*func)(Client *client, void *msg);
|
|
} MSG_HANDLER;
|
|
extern MSG_HANDLER handler[ 256 ];
|
|
|
|
typedef struct {
|
|
int msglen; // if 0, no message handler defined.
|
|
void (*func)(Client *client, void *msg);
|
|
} MSG_HANDLER_V2;
|
|
extern MSG_HANDLER_V2 handler_v2[ 256 ];
|
|
|
|
u32 GetSubCmd(const unsigned char* message, PacketHookData* phd)
|
|
{
|
|
if(phd->sub_command_length == 1)
|
|
return *(reinterpret_cast<const u8*>(&message[phd->sub_command_offset]));
|
|
else if(phd->sub_command_length == 2)
|
|
return cfBEu16(*(reinterpret_cast<const u16*>(&message[phd->sub_command_offset])));
|
|
else if(phd->sub_command_length == 4)
|
|
return cfBEu32(*(reinterpret_cast<const u32*>(&message[phd->sub_command_offset])));
|
|
else
|
|
return cfBEu32(*(reinterpret_cast<const u32*>(&message[phd->sub_command_offset])));
|
|
|
|
}
|
|
|
|
//MSG_HANDLER function used for each hooked packet type.
|
|
void ExportedPacketHookHandler(Client* client, void* data)
|
|
{
|
|
//find the script handler data
|
|
unsigned char* message = static_cast<unsigned char*>(data);
|
|
PacketHookData* phd = &(packet_hook_data_v2.at(message[0]));
|
|
if ( phd->version == 2 )
|
|
{
|
|
if ( !CompareVersionDetail(client->getversiondetail(), phd->client_ver) )
|
|
{
|
|
phd = &(packet_hook_data.at(message[0]));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
phd = &(packet_hook_data.at(message[0]));
|
|
}
|
|
|
|
if(phd->function == NULL && phd->SubCommands.empty())
|
|
{
|
|
if (phd->default_handler == NULL)
|
|
Log("Expected packet hook function for msg %x but was null!\n",*message);
|
|
else // only SendFunction is definied but default_handler is definied
|
|
phd->default_handler(client, data);
|
|
return;
|
|
}
|
|
|
|
if(!phd->SubCommands.empty())
|
|
{
|
|
u32 subcmd = GetSubCmd(message, phd);//cfBEu16(*(reinterpret_cast<u16*>(&message[phd->sub_command_offset])));
|
|
map<u32,PacketHookData*>::iterator itr;
|
|
itr = phd->SubCommands.find(subcmd);
|
|
if(itr != phd->SubCommands.end())
|
|
{
|
|
if(itr->second->function != NULL)
|
|
phd = itr->second;
|
|
}
|
|
}
|
|
if(phd->function == NULL) //this will happen if the main packet entry does not define a receive function,
|
|
//but has subcommands, and we've received an unhooked subcmd.
|
|
{
|
|
if(phd->default_handler != NULL)
|
|
phd->default_handler(client, data);
|
|
return;
|
|
}
|
|
|
|
//This packet has fixed length
|
|
if(phd->length != 0)
|
|
{
|
|
ref_ptr<BPacket> pkt( new BPacket(message, static_cast<unsigned short>(phd->length), false) );
|
|
//if function returns 0, we need to call the default handler
|
|
|
|
BObjectImp* calling_ref;
|
|
if (client->chr)
|
|
{
|
|
calling_ref = client->chr->make_ref();
|
|
}
|
|
else
|
|
{
|
|
// Create the struct only if really needed...
|
|
BStruct* client_ret = new BStruct;
|
|
client_ret->addMember( "ip", new String( client->ipaddrAsString().c_str() ) );
|
|
client_ret->addMember( "account", new String( (client->acct != NULL)? client->acct->name():"No Account Selected" ) );
|
|
|
|
calling_ref = client_ret;
|
|
}
|
|
|
|
if( phd->function->call(calling_ref , pkt.get()) == 0 )
|
|
{
|
|
if(phd->default_handler != NULL)
|
|
phd->default_handler(client, static_cast<void*>(&pkt->buffer[0]));
|
|
}
|
|
}
|
|
else //packet is variable length
|
|
{
|
|
//discover packet length, and create new packet
|
|
unsigned short len = cfBEu16(*( reinterpret_cast<unsigned short*>(&message[1]) ));
|
|
ref_ptr<BPacket> pkt( new BPacket(message, len, true) );
|
|
//if function returns 0, we need to call the default handler
|
|
|
|
BObjectImp* calling_ref;
|
|
if (client->chr)
|
|
{
|
|
calling_ref = client->chr->make_ref();
|
|
}
|
|
else
|
|
{
|
|
// Create the struct only if really needed...
|
|
BStruct* client_ret = new BStruct;
|
|
client_ret->addMember( "ip", new String( client->ipaddrAsString().c_str() ) );
|
|
client_ret->addMember( "account", new String( (client->acct != NULL)? client->acct->name():"No Account Selected" ) );
|
|
|
|
calling_ref = client_ret;
|
|
}
|
|
|
|
if( phd->function->call(calling_ref ,pkt.get()) == 0 )
|
|
{
|
|
if(phd->default_handler != NULL)
|
|
{
|
|
//the buffer size may have changed in the script, make sure the packet gets the right size
|
|
//u16* sizeptr = (u16*)(&pkt->buffer[1]);
|
|
//*sizeptr = ctBEu16(pkt->buffer.size());
|
|
phd->default_handler(client, static_cast<void*>(&pkt->buffer[0]));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CallOutgoingPacketExportedFunction(Client* client, const void*& data, int& inlength, ref_ptr<BPacket>& outpacket, bool& handled)
|
|
{
|
|
//find the script handler data
|
|
bool subcmd_handler_exists = false;
|
|
const unsigned char* message = static_cast<const unsigned char*>(data);
|
|
PacketHookData* phd = &(packet_hook_data_v2.at(message[0]));
|
|
if ( phd->version == 2 )
|
|
{
|
|
if ( !CompareVersionDetail(client->getversiondetail(), phd->client_ver) )
|
|
{
|
|
phd = &(packet_hook_data.at(message[0]));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
phd = &(packet_hook_data.at(message[0]));
|
|
}
|
|
|
|
if(!phd->SubCommands.empty())
|
|
{
|
|
u32 subcmd = GetSubCmd(message, phd);//cfBEu16(*(reinterpret_cast<const u16*>(&message[phd->sub_command_offset])));
|
|
map<u32,PacketHookData*>::iterator itr;
|
|
itr = phd->SubCommands.find(subcmd);
|
|
if(itr != phd->SubCommands.end())
|
|
{
|
|
if(itr->second->outgoing_function != NULL)
|
|
{
|
|
phd = itr->second;
|
|
subcmd_handler_exists = true;
|
|
}
|
|
}
|
|
}
|
|
if(phd->outgoing_function == NULL && !subcmd_handler_exists)
|
|
{
|
|
handled = false;
|
|
return;
|
|
}
|
|
|
|
//This packet has fixed length
|
|
if(phd->length != 0)
|
|
{
|
|
outpacket.set(new BPacket(message, static_cast<unsigned short>(phd->length), false) );
|
|
//if function returns 0, we need to call the default handler
|
|
|
|
BObjectImp* calling_ref;
|
|
if (client->chr)
|
|
{
|
|
calling_ref = client->chr->make_ref();
|
|
}
|
|
else
|
|
{
|
|
// Create the struct only if really needed...
|
|
BStruct* client_ret = new BStruct;
|
|
client_ret->addMember( "ip", new String( client->ipaddrAsString().c_str() ) );
|
|
client_ret->addMember( "account", new String( (client->acct != NULL)? client->acct->name():"No Account Selected" ) );
|
|
|
|
calling_ref = client_ret;
|
|
}
|
|
|
|
if( phd->outgoing_function->call(calling_ref , outpacket.get()) == 0 )
|
|
{
|
|
data = static_cast<void*>(&outpacket->buffer[0]);
|
|
//a fixed-length packet
|
|
inlength = phd->length;
|
|
handled = false;
|
|
}
|
|
else
|
|
handled = true;
|
|
}
|
|
else //packet is variable length
|
|
{
|
|
//discover packet length, and create new packet
|
|
unsigned short len = cfBEu16(*( reinterpret_cast<const unsigned short*>(&message[1]) ));
|
|
outpacket.set(new BPacket(message, len, true) );
|
|
//if function returns 0, we need to call the default handler
|
|
|
|
BObjectImp* calling_ref;
|
|
if (client->chr)
|
|
{
|
|
calling_ref = client->chr->make_ref();
|
|
}
|
|
else
|
|
{
|
|
// Create the struct only if really needed...
|
|
BStruct* client_ret = new BStruct;
|
|
client_ret->addMember( "ip", new String( client->ipaddrAsString().c_str() ) );
|
|
client_ret->addMember( "account", new String( (client->acct != NULL)? client->acct->name():"No Account Selected" ) );
|
|
|
|
calling_ref = client_ret;
|
|
}
|
|
|
|
if( phd->outgoing_function->call(calling_ref ,outpacket.get()) == 0 )
|
|
{
|
|
//the buffer size may have changed in the script, make sure the packet gets the right size
|
|
|
|
u16* sizeptr = reinterpret_cast<u16*>(&outpacket->buffer[1]); //var-length packets always have length at 2nd and 3rd byte
|
|
//*sizeptr = ctBEu16(outpacket->buffer.size());
|
|
|
|
data = static_cast<void*>(&outpacket->buffer[0]);
|
|
//pass the new size back to client::transmit
|
|
inlength = cfBEu16(*sizeptr);
|
|
handled = false;
|
|
}
|
|
else
|
|
handled = true;
|
|
}
|
|
}
|
|
|
|
void load_packet_entries( const Package* pkg, ConfigElem& elem )
|
|
{
|
|
if( stricmp(elem.type(),"Packet") != 0)
|
|
return;
|
|
string lengthstr;
|
|
long length = 0;
|
|
|
|
unsigned short pktversion = 1;
|
|
string client_string = "1.25.25.0";
|
|
VersionDetailStruct client_struct;
|
|
|
|
ExportedFunction* exfunc = (ExportedFunction*) NULL;
|
|
ExportedFunction* exoutfunc = (ExportedFunction*) NULL;
|
|
if(elem.has_prop("ReceiveFunction"))
|
|
exfunc = FindExportedFunction( elem, pkg, elem.remove_string("ReceiveFunction"), 2, true );
|
|
if(elem.has_prop("SendFunction"))
|
|
exoutfunc = FindExportedFunction( elem, pkg, elem.remove_string("SendFunction"), 2, true );
|
|
|
|
char *endptr = NULL;
|
|
unsigned long idlong = strtoul( elem.rest(), &endptr, 0 );
|
|
if ((endptr != NULL) &&
|
|
(*endptr != '\0') &&
|
|
!isspace(*endptr))
|
|
{
|
|
elem.throw_error("Packet ID not defined or poorly formed");
|
|
}
|
|
if(idlong > 0xFF)
|
|
elem.throw_error("Packet ID must be between 0x0 and 0xFF");
|
|
|
|
if( !elem.remove_prop( "Version", &pktversion) )
|
|
pktversion = 1;
|
|
client_string = elem.remove_string( "Client", "1.25.25.0" );
|
|
SetVersionDetailStruct(client_string, client_struct);
|
|
|
|
unsigned char id = static_cast<unsigned char>(idlong);
|
|
|
|
unsigned short subcmdoff;
|
|
if( !elem.remove_prop( "SubCommandOffset", &subcmdoff) )
|
|
subcmdoff = 0;
|
|
unsigned short subcmdlen;
|
|
if( !elem.remove_prop( "SubCommandLength", &subcmdlen) )
|
|
subcmdlen = 0;
|
|
|
|
if( elem.remove_prop( "Length", &lengthstr ) )
|
|
{
|
|
if( lengthstr == "variable" )
|
|
length = 0;
|
|
else
|
|
{
|
|
unsigned short temp;
|
|
endptr = NULL;
|
|
temp = (unsigned short) strtoul( lengthstr.c_str(), &endptr, 0 );
|
|
if ((endptr != NULL) &&
|
|
(*endptr != '\0') &&
|
|
!isspace(*endptr))
|
|
{
|
|
elem.throw_error("Length must be an integer or 'variable'");
|
|
}
|
|
else
|
|
length = temp;
|
|
}
|
|
|
|
}
|
|
else
|
|
elem.throw_error("Length property missing.");
|
|
|
|
ExportedFunction* existing_in_func = NULL;
|
|
ExportedFunction* existing_out_func = NULL;
|
|
|
|
switch (pktversion)
|
|
{
|
|
case 1: existing_in_func = packet_hook_data.at(id).function;
|
|
existing_out_func = packet_hook_data.at(id).outgoing_function;
|
|
break;
|
|
case 2: existing_in_func = packet_hook_data_v2.at(id).function;
|
|
existing_out_func = packet_hook_data_v2.at(id).outgoing_function;
|
|
break;
|
|
case 3: cout << "Packethook Packet Version 3 not implemented" << endl; return; break;
|
|
default: cout << "Invalid Packethook Packet Version." << endl; return; break;
|
|
}
|
|
|
|
if(existing_in_func != NULL)
|
|
Log("Packet hook receive function multiply defined for packet %x!\n",id);
|
|
if(existing_out_func != NULL)
|
|
Log("Packet hook send function multiply defined for packet %x!\n",id);
|
|
|
|
switch (pktversion)
|
|
{
|
|
case 1: packet_hook_data.at(id).function = exfunc;
|
|
packet_hook_data.at(id).outgoing_function = exoutfunc;
|
|
packet_hook_data.at(id).length = length;
|
|
packet_hook_data.at(id).sub_command_offset = subcmdoff;
|
|
packet_hook_data.at(id).sub_command_length = subcmdlen;
|
|
packet_hook_data.at(id).version = pktversion;
|
|
packet_hook_data.at(id).client_ver = client_struct;
|
|
if (handler[id].msglen)
|
|
{
|
|
packet_hook_data.at(id).default_handler = handler[id].func;
|
|
}
|
|
if(length == 0)
|
|
{
|
|
MessageHandler( id, MSGLEN_2BYTELEN_DATA, ExportedPacketHookHandler );
|
|
}
|
|
else
|
|
{
|
|
MessageHandler( id, length, ExportedPacketHookHandler );
|
|
}
|
|
break;
|
|
case 2: packet_hook_data_v2.at(id).function = exfunc;
|
|
packet_hook_data_v2.at(id).outgoing_function = exoutfunc;
|
|
packet_hook_data_v2.at(id).length = length;
|
|
packet_hook_data_v2.at(id).sub_command_offset = subcmdoff;
|
|
packet_hook_data_v2.at(id).sub_command_length = subcmdlen;
|
|
packet_hook_data_v2.at(id).version = pktversion;
|
|
packet_hook_data_v2.at(id).client_ver = client_struct;
|
|
if (handler_v2[id].msglen)
|
|
{
|
|
packet_hook_data_v2.at(id).default_handler = handler_v2[id].func;
|
|
}
|
|
if(length == 0)
|
|
{
|
|
MessageHandler_V2( id, MSGLEN_2BYTELEN_DATA, ExportedPacketHookHandler );
|
|
}
|
|
else
|
|
{
|
|
MessageHandler_V2( id, length, ExportedPacketHookHandler );
|
|
}
|
|
break;
|
|
case 3: cout << "Packethook Packet Version 3 not implemented" << endl; return; break;
|
|
default: cout << "Invalid Packethook Packet Version." << endl; return; break;
|
|
}
|
|
}
|
|
|
|
void load_subpacket_entries( const Package* pkg, ConfigElem& elem )
|
|
{
|
|
if( stricmp(elem.type(),"SubPacket") != 0)
|
|
return;
|
|
ExportedFunction* exfunc = (ExportedFunction*) NULL;
|
|
ExportedFunction* exoutfunc = (ExportedFunction*) NULL;
|
|
|
|
unsigned short pktversion = 1;
|
|
string client_string = "1.25.25.0";
|
|
VersionDetailStruct client_struct;
|
|
|
|
if(elem.has_prop("ReceiveFunction"))
|
|
exfunc = FindExportedFunction( elem, pkg, elem.remove_string("ReceiveFunction"), 2, true );
|
|
if(elem.has_prop("SendFunction"))
|
|
exoutfunc = FindExportedFunction( elem, pkg, elem.remove_string("SendFunction"), 2, true );
|
|
|
|
char *endptr = NULL;
|
|
unsigned long idlong = strtoul( elem.rest(), &endptr, 0 );
|
|
if ((endptr != NULL) &&
|
|
(*endptr != '\0') &&
|
|
!isspace(*endptr))
|
|
{
|
|
elem.throw_error("Packet ID not defined or poorly formed");
|
|
}
|
|
if(idlong > 0xFF)
|
|
elem.throw_error("Packet ID must be between 0x0 and 0xFF");
|
|
|
|
unsigned char id = static_cast<unsigned char>(idlong);
|
|
|
|
unsigned short subid = elem.remove_ushort( "SubCommandID" );
|
|
|
|
if( !elem.remove_prop( "Version", &pktversion) )
|
|
pktversion = 1;
|
|
|
|
client_string = elem.remove_string( "Client", "1.25.25.0" );
|
|
SetVersionDetailStruct(client_string, client_struct);
|
|
|
|
PacketHookData* parent = NULL;
|
|
switch (pktversion)
|
|
{
|
|
case 1: parent = &packet_hook_data.at(id); break;
|
|
case 2: parent = &packet_hook_data_v2.at(id); break;
|
|
case 3: cout << "Packethook Packet Version 3 not implemented" << endl; return; break;
|
|
default: cout << "Invalid Packethook Packet Version." << endl; return; break;
|
|
}
|
|
|
|
//validate that the parent packet has a definition and a SubCommandOffset
|
|
if(!parent->sub_command_offset)
|
|
elem.throw_error( string("Parent packet " + hexint(id) + " does not define SubCommandOffset!") );
|
|
if(!parent->sub_command_length)
|
|
elem.throw_error( string("Parent packet " + hexint(id) + " does not define SubCommandLength") );
|
|
if(parent->SubCommands.find(subid) != parent->SubCommands.end())
|
|
elem.throw_error( string("SubCommand " + hexint(subid) + " for packet " + hexint(id) + " multiply defined!") );
|
|
|
|
PacketHookData* SubData = new PacketHookData();
|
|
SubData->function = exfunc;
|
|
SubData->outgoing_function = exoutfunc;
|
|
SubData->length = parent->length;
|
|
SubData->default_handler = parent->default_handler;
|
|
SubData->version = pktversion;
|
|
SubData->client_ver = client_struct;
|
|
|
|
parent->SubCommands.insert( make_pair(subid,SubData) );
|
|
}
|
|
|
|
//loads "uopacket.cfg" entries from packages
|
|
void load_packet_hooks()
|
|
{
|
|
load_packaged_cfgs( "uopacket.cfg", "packet subpacket", load_packet_entries );
|
|
load_packaged_cfgs( "uopacket.cfg", "packet subpacket", load_subpacket_entries );
|
|
}
|
|
|
|
PacketHookData::~PacketHookData() {
|
|
map<u32,PacketHookData*>::iterator itr = SubCommands.begin(), end = SubCommands.end();
|
|
for ( ; itr != end; itr++) {
|
|
delete itr->second;
|
|
}
|
|
if (function != NULL)
|
|
delete function;
|
|
if (outgoing_function != NULL)
|
|
delete outgoing_function;
|
|
}
|
|
|
|
void clean_packethooks()
|
|
{
|
|
packet_hook_data.clear();
|
|
packet_hook_data_v2.clear();
|
|
}
|
|
|
|
void SetVersionDetailStruct(const std::string& ver, VersionDetailStruct& detail)
|
|
{
|
|
try
|
|
{
|
|
int dot1 = ver.find_first_of('.',0);
|
|
int dot2 = ver.find_first_of('.',dot1 + 1);
|
|
int dot3 = ver.find_first_of('.',dot2 + 1);
|
|
if (dot3 == -1) // since 5.0.7 patch is digit
|
|
{
|
|
dot3 = dot2 + 1;
|
|
while ( (dot3 < (int)ver.length()) && (isdigit(ver[dot3])) )
|
|
{
|
|
dot3++;
|
|
}
|
|
}
|
|
|
|
detail.major = atoi(ver.substr(0,dot1).c_str());
|
|
detail.minor = atoi(ver.substr(dot1+1,dot2 - dot1 - 1).c_str());
|
|
detail.rev = atoi(ver.substr(dot2+1,dot3 - dot2 - 1).c_str());
|
|
detail.patch = 0;
|
|
if (dot3<(int)ver.length())
|
|
{
|
|
if ( (detail.major<=5) && (detail.minor<=0) && (detail.rev<=6))
|
|
{
|
|
if (ver[dot3]!=' ')
|
|
detail.patch = (ver[dot3] - 'a') + 1; // char to int
|
|
}
|
|
else
|
|
detail.patch = atoi(ver.substr(dot3+1,ver.length() - dot3 - 1).c_str());
|
|
}
|
|
}
|
|
catch(...)
|
|
{
|
|
detail.major = 0;
|
|
detail.minor = 0;
|
|
detail.rev = 0;
|
|
detail.patch = 0;
|
|
Log2("Malformed client version string in Packethook: %s\n",ver.c_str());
|
|
}
|
|
}
|
|
|
|
bool CompareVersionDetail(VersionDetailStruct ver1, VersionDetailStruct ver2)
|
|
{
|
|
if ( ver1.major > ver2.major )
|
|
return true;
|
|
else if ( ver1.major < ver2.major )
|
|
return false;
|
|
else if ( ver1.minor > ver2.minor )
|
|
return true;
|
|
else if ( ver1.minor < ver2.minor )
|
|
return false;
|
|
else if ( ver1.rev > ver2.rev )
|
|
return true;
|
|
else if ( ver1.rev < ver2.rev )
|
|
return false;
|
|
else if ( ver1.patch > ver2.patch )
|
|
return true;
|
|
else if ( ver1.patch < ver2.patch )
|
|
return false;
|
|
else
|
|
return true;
|
|
} |