Pol  Revision:4b29d2b
packethooks.cpp
Go to the documentation of this file.
1 
20 #include "packethooks.h"
21 
22 #include <ctype.h>
23 #include <stdlib.h>
24 #include <string.h>
25 
26 #include "../../clib/cfgelem.h"
27 #include "../../clib/clib.h"
28 #include "../../clib/clib_endian.h"
29 #include "../../clib/logfacility.h"
30 #include "../../clib/rawtypes.h"
31 #include "../../clib/refptr.h"
32 #include "../../clib/strutil.h"
33 #include "../../plib/pkg.h"
34 #include "../globals/network.h"
35 #include "../mobile/charactr.h"
36 #include "../packetscrobj.h"
37 #include "../syshook.h"
38 #include "client.h"
39 
40 namespace Pol
41 {
42 namespace Network
43 {
44 u32 GetSubCmd( const unsigned char* message, PacketHookData* phd )
45 {
46  if ( phd->sub_command_length == 1 )
47  return *( reinterpret_cast<const u8*>( &message[phd->sub_command_offset] ) );
48  else if ( phd->sub_command_length == 2 )
49  return cfBEu16( *( reinterpret_cast<const u16*>( &message[phd->sub_command_offset] ) ) );
50  // else if(phd->sub_command_length == 4)
51  // return cfBEu32(*(reinterpret_cast<const u32*>(&message[phd->sub_command_offset])));
52  else
53  return cfBEu32( *( reinterpret_cast<const u32*>( &message[phd->sub_command_offset] ) ) );
54 }
55 
56 // Variable length is defined in MSGLEN_2BYTELEN_DATA
57 static bool is_fixed_length( const PacketHookData* phd )
58 {
59  return phd->length > 0;
60 }
61 
62 // Gets the packet hook for a specific packet version
64 {
65  if ( version == PacketVersion::V2 )
66  return Core::networkManager.packet_hook_data_v2.at( msgid ).get();
67 
68  return Core::networkManager.packet_hook_data.at( msgid ).get();
69 }
70 
71 // Gets the packet hook according to the client version
72 PacketHookData* get_packethook( u8 msgid, const Client* client )
73 {
75  if ( phd->version == PacketVersion::V2 &&
77  return phd;
78 
79  return get_packethook( msgid, PacketVersion::Default );
80 }
81 
82 // MSG_HANDLER function used for each hooked packet type.
83 void ExportedPacketHookHandler( Client* client, void* data )
84 {
85  // find the script handler data
86  unsigned char* message = static_cast<unsigned char*>( data );
87 
88  u8 msgid = message[0];
89  PacketHookData* phd = get_packethook( msgid, client );
90 
91  if ( phd->function == nullptr && phd->SubCommands.empty() )
92  {
93  if ( phd->default_handler == nullptr )
94  POLLOG.Format( "Expected packet hook function for msg 0x{:X} but was null!\n" )
95  << (int)*message;
96  else // only SendFunction is definied but default_handler is definied
97  phd->default_handler( client, data );
98  return;
99  }
100 
101  if ( !phd->SubCommands.empty() )
102  {
103  u32 subcmd = GetSubCmd( message, phd );
104  std::map<u32, PacketHookData*>::iterator itr;
105  itr = phd->SubCommands.find( subcmd );
106  if ( itr != phd->SubCommands.end() )
107  {
108  if ( itr->second->function != nullptr )
109  phd = itr->second;
110  }
111  }
112  if ( phd->function ==
113  nullptr ) // this will happen if the main packet entry does not define a receive function,
114  // but has subcommands, and we've received an unhooked subcmd.
115  {
116  if ( phd->default_handler != nullptr )
117  phd->default_handler( client, data );
118  return;
119  }
120 
121  // Sends a client reference if still connecting
122  Bscript::BObjectImp* calling_ref;
123  if ( client->chr )
124  calling_ref = client->chr->make_ref();
125  else
126  calling_ref = client->make_ref();
127 
128  // This packet has fixed length
129  if ( is_fixed_length( phd ) )
130  {
132  new Core::BPacket( message, static_cast<unsigned short>( phd->length ), false ) );
133  // if function returns 0, we need to call the default handler
134 
135 
136  if ( phd->function->call( calling_ref, pkt.get() ) == 0 )
137  {
138  if ( phd->default_handler != nullptr )
139  phd->default_handler( client, static_cast<void*>( &pkt->buffer[0] ) );
140  }
141  }
142  else // packet is variable length
143  {
144  // discover packet length, and create new packet
145  unsigned short len = cfBEu16( *( reinterpret_cast<unsigned short*>( &message[1] ) ) );
146  ref_ptr<Core::BPacket> pkt( new Core::BPacket( message, len, true ) );
147  // if function returns 0, we need to call the default handler
148 
149  if ( phd->function->call( calling_ref, pkt.get() ) == 0 )
150  {
151  if ( phd->default_handler != nullptr )
152  {
153  // the buffer size may have changed in the script, make sure the packet gets the right size
154  // u16* sizeptr = (u16*)(&pkt->buffer[1]);
155  //*sizeptr = ctBEu16(pkt->buffer.size());
156  phd->default_handler( client, static_cast<void*>( &pkt->buffer[0] ) );
157  }
158  }
159  }
160 }
161 
162 
163 void CallOutgoingPacketExportedFunction( Client* client, const void*& data, int& inlength,
164  ref_ptr<Core::BPacket>& outpacket, PacketHookData* phd,
165  bool& handled )
166 {
167  const unsigned char* message = static_cast<const unsigned char*>( data );
168 
169  // Sends a client reference if still connecting
170  Bscript::BObjectImp* calling_ref;
171  if ( client->chr )
172  calling_ref = client->chr->make_ref();
173  else
174  calling_ref = client->make_ref();
175 
176  // This packet has fixed length
177  if ( is_fixed_length( phd ) )
178  {
179  outpacket.set(
180  new Core::BPacket( message, static_cast<unsigned short>( phd->length ), false ) );
181  // if function returns 0, we need to call the default handler
182 
183  if ( phd->outgoing_function->call( calling_ref, outpacket.get() ) == 0 )
184  {
185  data = static_cast<void*>( &outpacket->buffer[0] );
186  // a fixed-length packet
187  inlength = phd->length;
188  handled = false;
189  }
190  else
191  handled = true;
192  }
193  else // packet is variable length
194  {
195  // discover packet length, and create new packet
196  unsigned short len = cfBEu16( *( reinterpret_cast<const unsigned short*>( &message[1] ) ) );
197  outpacket.set( new Core::BPacket( message, len, true ) );
198  // if function returns 0, we need to call the default handler
199 
200  if ( phd->outgoing_function->call( calling_ref, outpacket.get() ) == 0 )
201  {
202  // the buffer size may have changed in the script, make sure the packet gets the right size
203 
204  u16* sizeptr = reinterpret_cast<u16*>(
205  &outpacket->buffer[1] ); // var-length packets always have length at 2nd and 3rd byte
206  //*sizeptr = ctBEu16(outpacket->buffer.size());
207 
208  data = static_cast<void*>( &outpacket->buffer[0] );
209  // pass the new size back to client::transmit
210  inlength = cfBEu16( *sizeptr );
211  handled = false;
212  }
213  else
214  handled = true;
215  }
216 }
217 
218 bool GetAndCheckPacketHooked( Client* client, const void*& data, PacketHookData*& phd )
219 {
220  // find the script handler data
221  bool subcmd_handler_exists = false;
222  const unsigned char* message = static_cast<const unsigned char*>( data );
223 
224  u8 msgid = message[0];
225  phd = get_packethook( msgid, client );
226 
227  if ( !phd->SubCommands.empty() )
228  {
229  u32 subcmd = GetSubCmd( message, phd );
230  auto itr = phd->SubCommands.find( subcmd );
231  if ( itr != phd->SubCommands.end() )
232  {
233  if ( itr->second->outgoing_function != nullptr )
234  {
235  phd = itr->second;
236  subcmd_handler_exists = true;
237  }
238  }
239  }
240  if ( phd->outgoing_function == nullptr && !subcmd_handler_exists )
241  {
242  return false;
243  }
244  return true;
245 }
246 
248 {
249  unsigned short pktversion;
250 
251  if ( !elem.remove_prop( "Version", &pktversion ) )
252  pktversion = 1;
253 
254  switch ( pktversion )
255  {
256  case 1:
257  return PacketVersion::V1;
258  case 2:
259  return PacketVersion::V2;
260  default:
261  elem.throw_error( "Only versions 1 and 2 are currently implemented." );
262  }
263 }
264 
266 {
267  std::string lengthstr;
268  int length = 0;
269 
270  if ( !elem.remove_prop( "Length", &lengthstr ) )
271  elem.throw_error( "Length property missing." );
272 
273  if ( lengthstr == "variable" )
274  length = MSGLEN_2BYTELEN_DATA; // sets length to indicate variable length
275  else
276  {
277  unsigned short temp;
278  char* endptr = nullptr;
279  temp = (unsigned short)strtoul( lengthstr.c_str(), &endptr, 0 );
280  if ( temp == 0 || ( ( endptr != nullptr ) && ( *endptr != '\0' ) && !isspace( *endptr ) ) )
281  {
282  elem.throw_error( "Length must be a positive integer or 'variable'" );
283  }
284  else
285  length = temp;
286  }
287 
288  return length;
289 }
290 
292 {
293  PacketHookData* hook_data = get_packethook( msgid, pktversion );
294 
295  auto existing_in_func = hook_data->function;
296  auto existing_out_func = hook_data->outgoing_function;
297 
298  if ( existing_in_func != nullptr )
299  POLLOG.Format( "Packet hook receive function multiply defined for packet 0x{:X}!\n" )
300  << (int)msgid;
301  if ( existing_out_func != nullptr )
302  POLLOG.Format( "Packet hook send function multiply defined for packet 0x{:X}!\n" )
303  << (int)msgid;
304 }
305 
307 {
308  if ( stricmp( elem.type(), "Packet" ) != 0 )
309  return;
310 
311  int length = 0;
312 
313  PacketVersion pktversion;
314  std::string client_string;
315  VersionDetailStruct client_struct;
316 
318  Core::ExportedFunction* exoutfunc = (Core::ExportedFunction*)nullptr;
319  if ( elem.has_prop( "ReceiveFunction" ) )
320  exfunc =
321  Core::FindExportedFunction( elem, pkg, elem.remove_string( "ReceiveFunction" ), 2, true );
322  if ( elem.has_prop( "SendFunction" ) )
323  exoutfunc =
324  Core::FindExportedFunction( elem, pkg, elem.remove_string( "SendFunction" ), 2, true );
325 
326  char* endptr = nullptr;
327  unsigned int idlong = strtoul( elem.rest(), &endptr, 0 );
328  if ( ( endptr != nullptr ) && ( *endptr != '\0' ) && !isspace( *endptr ) )
329  {
330  elem.throw_error( "Packet ID not defined or poorly formed" );
331  }
332  if ( idlong > 0xFF )
333  elem.throw_error( "Packet ID must be between 0x0 and 0xFF" );
334 
335  // Reads the packet version ("Version") and throws an error if not 1 or 2
336  pktversion = load_packethook_version( elem );
337 
338  client_string = elem.remove_string( "Client", "1.25.25.0" );
339  SetVersionDetailStruct( client_string, client_struct );
340 
341  unsigned char id = static_cast<unsigned char>( idlong );
342 
343  unsigned short subcmdoff;
344  if ( !elem.remove_prop( "SubCommandOffset", &subcmdoff ) )
345  subcmdoff = 0;
346  unsigned short subcmdlen;
347  if ( !elem.remove_prop( "SubCommandLength", &subcmdlen ) )
348  subcmdlen = 0;
349 
350  // Loads the length ("Length"), which is either 'variable' or a positive integer
351  // if 'variable', length will be MSGLEN_2BYTELEN_DATA
352  length = load_packethook_length( elem );
353 
354  // Checks if packethook has been previously defined and prints a warning
355  packethook_warn_if_previously_defined( id, pktversion );
356 
357  PacketHookData* pkt_data = get_packethook( id, pktversion );
358  pkt_data->function = exfunc;
359  pkt_data->outgoing_function = exoutfunc;
360  pkt_data->length = length;
361  pkt_data->sub_command_offset = subcmdoff;
362  pkt_data->sub_command_length = subcmdlen;
363  pkt_data->version = pktversion;
364  pkt_data->client_ver = client_struct;
365  pkt_data->default_handler = PacketRegistry::get_callback( id, pktversion );
366 
367  PacketRegistry::set_handler( id, length, ExportedPacketHookHandler, pktversion );
368 }
369 
370 static void packethook_assert_valid_parent( u8 id, const PacketHookData* parent,
371  const Clib::ConfigElem& elem )
372 {
373  // validate that the parent packet has a definition and a SubCommandOffset
374  if ( !parent->sub_command_offset )
375  elem.throw_error( std::string( "Parent packet " + Clib::hexint( id ) +
376  " does not define SubCommandOffset!" ) );
377  if ( !parent->sub_command_length )
378  elem.throw_error( std::string( "Parent packet " + Clib::hexint( id ) +
379  " does not define SubCommandLength" ) );
380 }
381 
383 {
384  if ( stricmp( elem.type(), "SubPacket" ) != 0 )
385  return;
387  Core::ExportedFunction* exoutfunc = (Core::ExportedFunction*)nullptr;
388 
389  PacketVersion pktversion;
390  std::string client_string;
391  VersionDetailStruct client_struct;
392 
393  if ( elem.has_prop( "ReceiveFunction" ) )
394  exfunc =
395  Core::FindExportedFunction( elem, pkg, elem.remove_string( "ReceiveFunction" ), 2, true );
396  if ( elem.has_prop( "SendFunction" ) )
397  exoutfunc =
398  Core::FindExportedFunction( elem, pkg, elem.remove_string( "SendFunction" ), 2, true );
399 
400  char* endptr = nullptr;
401  unsigned int idlong = strtoul( elem.rest(), &endptr, 0 );
402  if ( ( endptr != nullptr ) && ( *endptr != '\0' ) && !isspace( *endptr ) )
403  {
404  elem.throw_error( "Packet ID not defined or poorly formed" );
405  }
406  if ( idlong > 0xFF )
407  elem.throw_error( "Packet ID must be between 0x0 and 0xFF" );
408 
409  unsigned char id = static_cast<unsigned char>( idlong );
410 
411  unsigned short subid = elem.remove_ushort( "SubCommandID" );
412 
413  // Reads the packet version ("Version") and throws an error if not 1 or 2
414  pktversion = load_packethook_version( elem );
415 
416  client_string = elem.remove_string( "Client", "1.25.25.0" );
417  SetVersionDetailStruct( client_string, client_struct );
418 
419  PacketHookData* parent = get_packethook( id, pktversion );
420  packethook_assert_valid_parent( id, parent, elem );
421 
422  if ( parent->SubCommands.find( subid ) != parent->SubCommands.end() )
423  elem.throw_error( std::string( "SubCommand " + Clib::hexint( subid ) + " for packet " +
424  Clib::hexint( id ) + " multiply defined!" ) );
425 
426  PacketHookData* SubData = new PacketHookData();
427  SubData->function = exfunc;
428  SubData->outgoing_function = exoutfunc;
429  SubData->length = parent->length;
430  SubData->default_handler = parent->default_handler;
431  SubData->version = pktversion;
432  SubData->client_ver = client_struct;
433 
434  parent->SubCommands.insert( std::make_pair( subid, SubData ) );
435 }
436 
437 // loads "uopacket.cfg" entries from packages
439 {
440  Plib::load_packaged_cfgs( "uopacket.cfg", "packet subpacket", load_packet_entries );
441  Plib::load_packaged_cfgs( "uopacket.cfg", "packet subpacket", load_subpacket_entries );
442 }
443 
445  : length( 0 ),
446  function( nullptr ),
447  outgoing_function( nullptr ),
448  default_handler( nullptr ),
449  sub_command_offset( 0 ),
450  sub_command_length( 0 ),
451  version( PacketVersion::Default )
452 {
453  memset( &client_ver, 0, sizeof( client_ver ) );
454 };
455 
457 {
458  std::map<u32, PacketHookData*>::iterator itr = SubCommands.begin(), end = SubCommands.end();
459  for ( ; itr != end; ++itr )
460  {
461  delete itr->second;
462  }
463  if ( function != nullptr )
464  delete function;
465  if ( outgoing_function != nullptr )
466  delete outgoing_function;
467 }
468 
469 void PacketHookData::initializeGameData( std::vector<std::unique_ptr<PacketHookData>>* data )
470 {
471  data->clear();
472  data->reserve( 256 );
473  for ( int i = 0; i < 256; ++i )
474  {
475  data->emplace_back( new PacketHookData() );
476  }
477 }
478 
480 {
481  size_t size = sizeof( PacketHookData ) + 2 * sizeof( Core::ExportedFunction );
482  for ( const auto& subs : SubCommands )
483  {
484  size += ( sizeof( u32 ) + sizeof( PacketHookData* ) + ( sizeof( void* ) * 3 + 1 ) / 2 );
485  if ( subs.second != nullptr )
486  size += subs.second->estimateSize();
487  }
488  return size;
489 }
490 
491 
493 {
496 }
497 
498 void SetVersionDetailStruct( const std::string& ver, VersionDetailStruct& detail )
499 {
500  try
501  {
502  size_t dot1 = ver.find_first_of( '.', 0 );
503  size_t dot2 = ver.find_first_of( '.', dot1 + 1 );
504  size_t dot3 = ver.find_first_of( '.', dot2 + 1 );
505  if ( dot3 == std::string::npos ) // since 5.0.7 patch is digit
506  {
507  dot3 = dot2 + 1;
508  while ( ( dot3 < ver.length() ) && ( isdigit( ver[dot3] ) ) )
509  {
510  dot3++;
511  }
512  }
513 
514  detail.major = atoi( ver.substr( 0, dot1 ).c_str() );
515  detail.minor = atoi( ver.substr( dot1 + 1, dot2 - dot1 - 1 ).c_str() );
516  detail.rev = atoi( ver.substr( dot2 + 1, dot3 - dot2 - 1 ).c_str() );
517  detail.patch = 0;
518  if ( dot3 < ver.length() )
519  {
520  if ( ( detail.major <= 5 ) && ( detail.minor <= 0 ) && ( detail.rev <= 6 ) )
521  {
522  if ( ver[dot3] != ' ' )
523  detail.patch = ( ver[dot3] - 'a' ) + 1; // char to int
524  }
525  else
526  detail.patch = atoi( ver.substr( dot3 + 1, ver.length() - dot3 - 1 ).c_str() );
527  }
528  }
529  catch ( ... )
530  {
531  detail.major = 0;
532  detail.minor = 0;
533  detail.rev = 0;
534  detail.patch = 0;
535  POLLOG_ERROR.Format( "Malformed client version string in Packethook: {}\n" ) << ver;
536  }
537 }
538 
540 {
541  if ( ver1.major > ver2.major )
542  return true;
543  else if ( ver1.major < ver2.major )
544  return false;
545  else if ( ver1.minor > ver2.minor )
546  return true;
547  else if ( ver1.minor < ver2.minor )
548  return false;
549  else if ( ver1.rev > ver2.rev )
550  return true;
551  else if ( ver1.rev < ver2.rev )
552  return false;
553  else if ( ver1.patch > ver2.patch )
554  return true;
555  else if ( ver1.patch < ver2.patch )
556  return false;
557  else
558  return true;
559 }
560 }
561 }
unsigned char u8
Definition: rawtypes.h:25
VersionDetailStruct getversiondetail() const
Definition: client.h:164
PktHandlerFunc default_handler
Definition: packethooks.h:46
void CallOutgoingPacketExportedFunction(Client *client, const void *&data, int &inlength, ref_ptr< Core::BPacket > &outpacket, PacketHookData *phd, bool &handled)
static void packethook_warn_if_previously_defined(u8 msgid, PacketVersion pktversion)
#define MSGLEN_2BYTELEN_DATA
Definition: msghandl.h:98
std::string remove_string(const char *propname)
Definition: cfgfile.cpp:381
void load_packaged_cfgs(const char *cfgname, const char *taglist, void(*loadentry)(const Package *, Clib::ConfigElem &))
Definition: pkg.cpp:454
Core::ExportedFunction * function
Definition: packethooks.h:43
bool call(Bscript::BObjectImp *p0)
Definition: syshook.cpp:44
#define cfBEu32(x)
Definition: clib_endian.h:43
T * get() const
Definition: refptr.h:176
VersionDetailStruct client_ver
Definition: packethooks.h:51
#define POLLOG_ERROR
Definition: logfacility.h:207
u32 GetSubCmd(const unsigned char *message, PacketHookData *phd)
Definition: packethooks.cpp:44
PacketHookData * get_packethook(u8 msgid, PacketVersion version=PacketVersion::Default)
Definition: packethooks.cpp:63
Mobile::Character * chr
Definition: client.h:182
void SetVersionDetailStruct(const std::string &ver, VersionDetailStruct &detail)
std::string hexint(unsigned short v)
Definition: strutil.cpp:23
unsigned short u16
Definition: rawtypes.h:26
const char * rest() const
Definition: cfgfile.cpp:71
unsigned int u32
Definition: rawtypes.h:27
std::vector< std::unique_ptr< Network::PacketHookData > > packet_hook_data_v2
Definition: network.h:79
POL_NORETURN void throw_error(const std::string &errmsg) const
Definition: cfgfile.cpp:285
static PktHandlerFunc get_callback(unsigned char msgid, PacketVersion version=PacketVersion::V1)
Definition: msghandl.cpp:34
bool GetAndCheckPacketHooked(Client *client, const void *&data, PacketHookData *&phd)
Definition: refptr.h:65
static void set_handler(unsigned char msgid, int len, PktHandlerFunc func, PacketVersion version=PacketVersion::V1)
Definition: msghandl.cpp:53
NetworkManager networkManager
Definition: network.cpp:28
std::vector< std::unique_ptr< Network::PacketHookData > > packet_hook_data
Definition: network.h:78
static void packethook_assert_valid_parent(u8 id, const PacketHookData *parent, const Clib::ConfigElem &elem)
void load_subpacket_entries(const Plib::Package *pkg, Clib::ConfigElem &elem)
void ExportedPacketHookHandler(Client *client, void *data)
Definition: packethooks.cpp:83
static int load_packethook_length(Clib::ConfigElem &elem)
#define cfBEu16(x)
Definition: clib_endian.h:44
virtual Bscript::BObjectImp * make_ref() POL_OVERRIDE
Definition: uoscrobj.cpp:1650
#define POLLOG
Definition: logfacility.h:219
std::unordered_map< u64, ScriptDiffData > data
Definition: osmod.cpp:966
bool remove_prop(const char *propname, std::string *value)
Definition: cfgfile.cpp:128
static bool is_fixed_length(const PacketHookData *phd)
Definition: packethooks.cpp:57
unsigned short sub_command_offset
Definition: packethooks.h:48
bool has_prop(const char *propname) const
Definition: cfgfile.cpp:112
Core::ExportedFunction * outgoing_function
Definition: packethooks.h:44
unsigned short sub_command_length
Definition: packethooks.h:49
std::map< u32, PacketHookData * > SubCommands
Definition: packethooks.h:52
unsigned short remove_ushort(const char *propname)
Definition: cfgfile.cpp:318
ExportedFunction * FindExportedFunction(Clib::ConfigElem &elem, const Plib::Package *pkg, const std::string &descriptor, unsigned nargs, bool complain_if_missing)
Definition: syshook.cpp:336
void set(T *ptr)
Definition: refptr.h:276
static PacketVersion load_packethook_version(Clib::ConfigElem &elem)
Bscript::BObjectImp * make_ref()
Definition: client.cpp:708
const char * type() const
Definition: cfgfile.cpp:66
void load_packet_hooks()
bool CompareVersionDetail(VersionDetailStruct ver1, VersionDetailStruct ver2)
Definition: berror.cpp:12
void load_packet_entries(const Plib::Package *pkg, Clib::ConfigElem &elem)
void clean_packethooks()
static void initializeGameData(std::vector< std::unique_ptr< PacketHookData >> *data)