polserver/pol-core/pol/network/auxclient.cpp
turleypol 43b776b94d
New format lib version (#592)
* renamed include guard to be able to use both format lib versions in
parallel

* build Fmt lib and link it into clib

* use std::string instead of format Writer for logging
added variant "2" for logging with new fmt lib

* use new logging variant for two logs

* always add \n to flush log, lets see if the assumption holds

* use new lib for tostring

* replaced all INFO_PRINTS in clib

* formatter for Token, BObject and BObjectImp

* started with bsccript

* use function instead of actual object for logging

* remaining bscript, started plib

* logvariant without newline added, more template magic

* format support for base types, added test for format padding
new version for fdump

* chanhed ecompile/runecl fixed compilation with ESCRIPT_PROFILE

* rewrite INFO_PRINT in plib,poltool/uoconvert/uotool

* replaced remaining INFO_PRINTS, renamed macros INFO_PRINT for logging
without newline, INFO_PRINTLN for newline addition.
Replaced also INFO_PRINT_TRACE and renamed it to INFO_PRINTLN_TRACE

* gitignore for fmt lib

* cleanup vim leftovers
2024-01-10 18:29:10 +01:00

356 lines
9.5 KiB
C++

/** @file
*
* @par History
* - 2005/11/26 Shinigami: changed "strcmp" into "stricmp" to suppress Script Errors
*/
#include "pol_global_config.h"
#include "auxclient.h"
#include <chrono>
#include <iosfwd>
#include "../../bscript/berror.h"
#include "../../bscript/bobject.h"
#include "../../bscript/bstruct.h"
#include "../../bscript/executor.h"
#include "../../bscript/impstr.h"
#include "../../clib/cfgelem.h"
#include "../../clib/clib.h"
#include "../../clib/esignal.h"
#include "../../clib/logfacility.h"
#include "../../clib/network/sckutil.h"
#include "../../clib/network/socketsvc.h"
#include "../../clib/network/wnsckt.h"
#include "../../clib/threadhelp.h"
#include "../../plib/pkg.h"
#include "../globals/network.h"
#include "../module/uomod.h"
#include "../polsem.h"
#include "../scrdef.h"
#include "../scrsched.h"
#include "../uoexec.h"
namespace Pol
{
namespace Network
{
Bscript::BObjectImp* AuxConnection::copy() const
{
return const_cast<AuxConnection*>( this );
}
std::string AuxConnection::getStringRep() const
{
return "<AuxConnection>";
}
size_t AuxConnection::sizeEstimate() const
{
return sizeof( AuxConnection ) + _ip.capacity();
}
bool AuxConnection::isTrue() const
{
return ( _auxclientthread != nullptr );
}
Bscript::BObjectRef AuxConnection::get_member( const char* membername )
{
if ( stricmp( membername, "ip" ) == 0 )
{
return Bscript::BObjectRef( new Bscript::String( _ip ) );
}
return Bscript::BObjectRef( Bscript::UninitObject::create() );
}
Bscript::BObjectImp* AuxConnection::call_polmethod( const char* methodname, Core::UOExecutor& ex )
{
if ( stricmp( methodname, "transmit" ) == 0 )
{
if ( ex.numParams() == 1 )
{
if ( _auxclientthread != nullptr )
{
Bscript::BObjectImp* value = ex.getParamImp( 0 );
_auxclientthread->transmit( value );
}
else
{
return new Bscript::BError( "Client has disconnected" );
}
}
else
{
return new Bscript::BError( "1 parameter expected" );
}
}
return nullptr;
}
void AuxConnection::disconnect()
{
_auxclientthread = nullptr;
}
AuxClientThread::AuxClientThread( AuxService* auxsvc, Clib::Socket&& sock )
: SocketClientThread( std::move( sock ) ),
_auxservice( auxsvc ),
_auxconnection(),
_uoexec( nullptr ),
_scriptdef(),
_params( nullptr ),
_assume_string( false ),
_transmit_counter( 0 ),
_keep_alive( false ),
_ignore_line_breaks( false ),
_transmit_mutex()
{
}
AuxClientThread::AuxClientThread( Core::ScriptDef scriptdef, Clib::Socket&& sock,
Bscript::BObjectImp* params, bool assume_string, bool keep_alive,
bool ignore_line_breaks )
: SocketClientThread( std::move( sock ) ),
_auxservice( nullptr ),
_auxconnection(),
_uoexec( nullptr ),
_scriptdef( scriptdef ),
_params( params ),
_assume_string( assume_string ),
_transmit_counter( 0 ),
_keep_alive( keep_alive ),
_ignore_line_breaks( ignore_line_breaks ),
_transmit_mutex()
{
}
bool AuxClientThread::init()
{
Core::PolLock lock;
struct sockaddr ConnectingIP = _sck.peer_address();
if ( ipAllowed( ConnectingIP ) )
{
_auxconnection.set( new AuxConnection( this, _sck.getpeername() ) );
Module::UOExecutorModule* uoemod;
if ( _auxservice )
uoemod = Core::start_script( _auxservice->scriptdef(), _auxconnection.get() );
else
uoemod = Core::start_script( _scriptdef, _auxconnection.get(), _params );
if ( uoemod == nullptr )
return false;
auto& uoex = uoemod->uoexec();
_uoexec = uoex.weakptr;
if ( _assume_string )
{
uoex.auxsvc_assume_string = _assume_string;
}
return true;
}
else
{
return false;
}
}
bool AuxClientThread::ipAllowed( sockaddr MyPeer )
{
if ( !_auxservice || _auxservice->_aux_ip_match.empty() )
{
return true;
}
for ( unsigned j = 0; j < _auxservice->_aux_ip_match.size(); ++j )
{
unsigned int addr1part, addr2part;
struct sockaddr_in* sockin = reinterpret_cast<struct sockaddr_in*>( &MyPeer );
addr1part = _auxservice->_aux_ip_match[j] & _auxservice->_aux_ip_match_mask[j];
#ifdef _WIN32
addr2part = sockin->sin_addr.S_un.S_addr & _auxservice->_aux_ip_match_mask[j];
#else
addr2part = sockin->sin_addr.s_addr & _auxservice->_aux_ip_match_mask[j];
#endif
if ( addr1part == addr2part )
return true;
}
return false;
}
void AuxClientThread::run()
{
if ( !init() )
{
if ( _sck.connected() )
{
writeline( _sck, "Connection closed" );
_sck.close();
}
_auxconnection.clear();
return;
}
std::string tmp;
bool result, timeout_exit;
std::unique_ptr<Clib::SocketReader> reader;
if ( _ignore_line_breaks )
{
reader = std::make_unique<Clib::SocketByteReader>( _sck, 5, !_keep_alive );
}
else
{
reader = std::make_unique<Clib::SocketLineReader>( _sck, 5, 0, !_keep_alive );
}
for ( ;; )
{
result = reader->read( tmp, &timeout_exit );
if ( Clib::exit_signalled || ( !result && !timeout_exit && !_keep_alive ) )
break;
Core::PolLock lock;
if ( _uoexec.exists() )
{
if ( result )
{
std::istringstream is( tmp );
std::unique_ptr<Bscript::BObjectImp> value( _uoexec->auxsvc_assume_string
? new Bscript::String( tmp )
: Bscript::BObjectImp::unpack( is ) );
std::unique_ptr<Bscript::BStruct> event( new Bscript::BStruct );
event->addMember( "type", new Bscript::String( "recv" ) );
event->addMember( "value", value.release() );
_uoexec->signal_event( event.release() );
}
}
else
{ // the controlling script dropped its last reference to the connection,
// by exiting or otherwise.
break;
}
}
// wait for all transmits to finish
while ( !Clib::exit_signalled && _transmit_counter > 0 )
std::this_thread::sleep_for( std::chrono::seconds( 1 ) );
Core::PolLock lock;
// hold also the transmit mutex, the counter syncs but not in a way that the threadsanitizer
// recognizes is.
std::unique_lock<std::mutex> transmitlock( _transmit_mutex );
_auxconnection->disconnect();
// the auxconnection is probably referenced by another ref_ptr,
// so its deletion must be protected by the lock.
// Clear our reference:
_auxconnection.clear();
}
void AuxClientThread::transmit( const Bscript::BObjectImp* value )
{
// defer transmit to not block server
std::string tmp = _uoexec->auxsvc_assume_string ? value->getStringRep() : value->pack();
++_transmit_counter;
Core::networkManager.auxthreadpool->push( [tmp, this]() { transmit( tmp ); } );
}
void AuxClientThread::transmit( const std::string& msg )
{
// wait for all other transmits to finish
// sending in parallel is nothing what we want
std::unique_lock<std::mutex> lock( _transmit_mutex );
if ( _sck.connected() )
{
if ( _ignore_line_breaks )
_sck.write( msg );
else
writeline( _sck, msg );
}
--_transmit_counter;
}
AuxService::AuxService( const Plib::Package* pkg, Clib::ConfigElem& elem )
: _pkg( pkg ),
_scriptdef( elem.remove_string( "SCRIPT" ), _pkg ),
_port( elem.remove_ushort( "PORT" ) )
{
std::string iptext;
while ( elem.remove_prop( "IPMATCH", &iptext ) )
{
auto delim = iptext.find_first_of( '/' );
if ( delim != std::string::npos )
{
std::string ipaddr_str = iptext.substr( 0, delim );
std::string ipmask_str = iptext.substr( delim + 1 );
unsigned int ipaddr = inet_addr( ipaddr_str.c_str() );
unsigned int ipmask = inet_addr( ipmask_str.c_str() );
_aux_ip_match.push_back( ipaddr );
_aux_ip_match_mask.push_back( ipmask );
}
else
{
unsigned int ipaddr = inet_addr( iptext.c_str() );
_aux_ip_match.push_back( ipaddr );
_aux_ip_match_mask.push_back( 0xFFffFFffLu );
}
}
}
void AuxService::run()
{
INFO_PRINTLN( "Starting Aux Listener ({}, port {})", _scriptdef.relativename(), _port );
Clib::SocketListener listener( _port );
while ( !Clib::exit_signalled )
{
Clib::Socket sock;
if ( listener.GetConnection( &sock, 5 ) && sock.connected() )
{
Core::PolLock lock;
AuxClientThread* client( new AuxClientThread( this, std::move( sock ) ) );
Core::networkManager.auxthreadpool->push(
[client]()
{
std::unique_ptr<AuxClientThread> _clientptr( client );
_clientptr->run();
} );
}
}
}
size_t AuxService::estimateSize() const
{
size_t size =
sizeof( Plib::Package* ) + _scriptdef.estimatedSize() + sizeof( unsigned short ) /*_port*/
+ 3 * sizeof( unsigned int* ) + _aux_ip_match.capacity() * sizeof( unsigned int ) +
3 * sizeof( unsigned int* ) + _aux_ip_match_mask.capacity() * sizeof( unsigned int );
return size;
}
void aux_service_thread_stub( void* arg )
{
AuxService* as = static_cast<AuxService*>( arg );
as->run();
}
void start_aux_services()
{
for ( unsigned i = 0; i < Core::networkManager.auxservices.size(); ++i )
{
threadhelp::start_thread( aux_service_thread_stub, "AuxService",
Core::networkManager.auxservices[i] );
}
}
void load_auxservice_entry( const Plib::Package* pkg, Clib::ConfigElem& elem )
{
Core::networkManager.auxservices.push_back( new AuxService( pkg, elem ) );
}
void load_aux_services()
{
load_packaged_cfgs( "auxsvc.cfg", "AuxService", load_auxservice_entry );
}
} // namespace Network
} // namespace Pol