From aae356d49894ab1460fa85d42df3f3170ba50efa Mon Sep 17 00:00:00 2001 From: imaginos Date: Wed, 18 May 2005 06:12:41 +0000 Subject: [PATCH] implement sockets, thus far untested git-svn-id: https://znc.svn.sourceforge.net/svnroot/znc/trunk@333 726aef4b-f618-498e-8847-2d620e286838 --- modules/modperl.cpp | 278 ++++++++++++++++++++++++++++++++++++++++++-- modules/modperl.pm | 117 ++++++++++++++++--- 2 files changed, 365 insertions(+), 30 deletions(-) diff --git a/modules/modperl.cpp b/modules/modperl.cpp index aa65bae5..c0ab55a6 100644 --- a/modules/modperl.cpp +++ b/modules/modperl.cpp @@ -17,6 +17,8 @@ #define ZNCEvalCB "ZNC::COREEval" #define ZNCCallFuncCB "ZNC::CORECallFunc" #define ZNCCallTimerCB "ZNC::CORECallTimer" +#define ZNCCallSockCB "ZNC::CORECallSock" +#define ZNCSOCK ":::ZncSock:::" class PString : public CString { @@ -65,7 +67,7 @@ public: break; case STRING: default: - pSV = newSVpv( c_str(), length() ); + pSV = newSVpv( data(), length() ); break; } @@ -110,36 +112,78 @@ public: CPerlSock() : Csock() { m_pModule = NULL; + m_iParentFD = -1; + SetSockName( ZNCSOCK ); } CPerlSock( const CS_STRING & sHost, int iPort, int iTimeout = 60 ) : Csock( sHost, iPort, iTimeout ) { m_pModule = NULL; + m_iParentFD = -1; + SetSockName( ZNCSOCK ); } + +// # OnSockDestroy( $sockhandle ) + virtual ~CPerlSock(); + virtual Csock *GetSockObj( const CS_STRING & sHostname, int iPort ) { CPerlSock *p = new CPerlSock( sHostname, iPort ); p->SetModule( m_pModule ); + p->SetParentFD( GetRSock() ); + p->SetUsername( m_sUsername ); + p->SetModuleName( m_sModuleName ); + p->SetSockName( ZNCSOCK ); return( p ); } void SetModule( CModPerl *pModule ) { m_pModule = pModule; } + void SetParentFD( int iFD ) { m_iParentFD = iFD; } + void SetUsername( const CString & sUsername ) { m_sUsername = sUsername; } + void SetModuleName( const CString & sModuleName ) { m_sModuleName = sModuleName; } + + const CString & GetUsername() { return( m_sUsername ); } + const CString & GetModuleName() { return( m_sModuleName ); } -// // # OnConnect( $sockhandle, $parentsockhandle ) + virtual void Connected(); // # OnConnectionFrom( $sockhandle, $remotehost, $remoteport ) + virtual bool ConnectionFrom( const CS_STRING & sHost, int iPort ); // # OnError( $sockhandle, $errno ) + virtual void SockError( int iErrno ); // # OnConnectionRefused( $sockhandle ) + virtual void ConnectionRefused(); // # OnTimeout( $sockhandle ) + virtual void Timeout(); // # OnDisconnect( $sockhandle ) + virtual void Disconnected(); // # OnData( $sockhandle, $bytes, $length ) -// # OnReadLine( $sockhandle, $bytes, $length ) + virtual void ReadData( const char *data, int len ); +// # OnReadLine( $sockhandle, $line ) + virtual void ReadLine( const CS_STRING & sLine ); + private: CModPerl *m_pModule; CString m_sModuleName; CString m_sUsername; // NEED these so we can send the signal to the right guy + int m_iParentFD; + VPString m_vArgs; + + void SetupArgs() + { + m_vArgs.clear(); + m_vArgs.push_back( m_sModuleName ); + m_vArgs.push_back( GetRSock() ); + } + + void AddArg( const PString & sArg ) + { + m_vArgs.push_back( sArg ); + } + + int CallBack( const PString & sFuncName ); }; class CPerlTimer : public CTimer @@ -176,6 +220,7 @@ public: virtual ~CModPerl() { + DestroyAllSocks(); if ( m_pPerl ) { CBNone( "Shutdown" ); @@ -220,6 +265,9 @@ public: PutModule( sTmp ); } + TSocketManager * GetSockManager() { return( m_pManager ); } + void DestroyAllSocks(); + CUser * GetUser() { return( m_pUser ); } virtual bool OnLoad( const CString & sArgs ); @@ -341,12 +389,14 @@ public: enum ECBTYPES { - CB_LOCAL = 1, - CB_ONHOOK = 2, - CB_TIMER = 3 + CB_LOCAL = 1, + CB_ONHOOK = 2, + CB_TIMER = 3, + CB_SOCK = 4 }; - EModRet CallBack( const PString & sHookName, const VPString & vsArgs, ECBTYPES eCBType = CB_ONHOOK ); + EModRet CallBack( const PString & sHookName, const VPString & vsArgs, + ECBTYPES eCBType = CB_ONHOOK, const PString & sUsername = "" ); EModRet CBNone( const PString & sHookName ) { @@ -636,6 +686,100 @@ XS(XS_ZNC_GetString) } } +////////////// Perl SOCKET guts ///////////// +#define MANAGER g_ModPerl->GetSockManager() +XS(XS_ZNC_WriteSock) +{ + dXSARGS; + if ( items <= 1 ) + Perl_croak( aTHX_ "Usage: ZNC::WriteSock( sockhandle, bytes, len )" ); + + SP -= items; + ax = (SP - PL_stack_base) + 1 ; + { + if ( g_ModPerl ) + { + PString sReturn = false; + int iSockFD = SvIV(ST(0)); + u_int iLen = SvUV(ST(2)); + if ( iLen > 0 ) + { + PString sData; + sData.append( SvPV( ST(1), iLen ), iLen ); + CPerlSock *pSock = (CPerlSock *)MANAGER->FindSockByFD( iSockFD ); + if ( ( pSock ) && ( pSock->GetSockName() == ZNCSOCK ) ) + sReturn = pSock->Write( sData.data(), sData.length() ); + } + XPUSHs( sReturn.GetSV() ); + } + PUTBACK; + } +} + +XS(XS_ZNC_COREConnectSock) +{ + dXSARGS; + if ( items != 5 ) + Perl_croak( aTHX_ "Usage: ZNC::COREConnectSock( $host, $port, $timeout, $bEnableReadline, $bUseSSL )" ); + + SP -= items; + ax = (SP - PL_stack_base) + 1 ; + { + if ( g_ModPerl ) + { + PString sReturn = -1; + PString sHostname = (char *)SvPV(ST(0),PL_na); + int iPort = SvIV(ST(1)); + u_int iTimeout = SvUV(ST(2)); + u_int iEnableReadline = SvUV(ST(3)); + u_int iUseSSL = SvUV(ST(4)); + CPerlSock *pSock = new CPerlSock( sHostname, iPort, iTimeout ); + pSock->SetSockName( ZNCSOCK ); + if ( iEnableReadline ) + pSock->EnableReadLine(); + + if ( MANAGER->Connect( sHostname, iPort, ZNCSOCK, iTimeout, ( iUseSSL != 0 ), "", pSock ) ) + sReturn = pSock->GetRSock(); + + XPUSHs( sReturn.GetSV() ); + } + PUTBACK; + } +} + +XS(XS_ZNC_COREListenSock) +{ + dXSARGS; + if ( items != 4 ) + Perl_croak( aTHX_ "Usage: ZNC::COREListenSock( $port, $bindhost, $bEnableReadline, $bUseSSL )" ); + + SP -= items; + ax = (SP - PL_stack_base) + 1 ; + { + if ( g_ModPerl ) + { + PString sReturn = -1; + int iPort = SvIV(ST(0)); + PString sHostname = (char *)SvPV(ST(1),PL_na); + u_int iEnableReadline = SvUV(ST(2)); + u_int iUseSSL = SvUV(ST(3)); + + CPerlSock *pSock = new CPerlSock(); + pSock->SetSockName( ZNCSOCK ); + + if ( iEnableReadline ) + pSock->EnableReadLine(); + + if ( MANAGER->ListenHost( iPort, ZNCSOCK, sHostname, ( iUseSSL != 0 ), SOMAXCONN, pSock ) ) + sReturn = pSock->GetRSock(); + + XPUSHs( sReturn.GetSV() ); + + } + PUTBACK; + } +} + /////////// supporting functions from within module bool CModPerl::Eval( const CString & sScript, const CString & sFuncName ) @@ -666,7 +810,8 @@ bool CModPerl::Eval( const CString & sScript, const CString & sFuncName ) return( bReturn ); } -CModPerl::EModRet CModPerl::CallBack( const PString & sHookName, const VPString & vsArgs, ECBTYPES eCBType ) +CModPerl::EModRet CModPerl::CallBack( const PString & sHookName, const VPString & vsArgs, + ECBTYPES eCBType, const PString & sUsername ) { if ( !m_pPerl ) return( CONTINUE ); @@ -682,13 +827,17 @@ CModPerl::EModRet CModPerl::CallBack( const PString & sHookName, const VPString sFuncToCall = sHookName; else { - PString sUsername = m_pUser->GetUserName(); - XPUSHs( sUsername.GetSV() ); + if ( sUsername.empty() ) + XPUSHs( PString( m_pUser->GetUserName() ).GetSV() ); + else + XPUSHs( sUsername.GetSV() ); XPUSHs( sHookName.GetSV() ); if ( eCBType == CB_ONHOOK ) sFuncToCall = ZNCCallFuncCB; - else + else if ( eCBType == CB_TIMER ) sFuncToCall = ZNCCallTimerCB; + else + sFuncToCall = ZNCCallSockCB; } for( VPString::size_type a = 0; a < vsArgs.size(); a++ ) @@ -751,12 +900,15 @@ bool CModPerl::OnLoad( const CString & sArgs ) newXS( "ZNC::COREAddTimer", XS_ZNC_COREAddTimer, (char *)file ); newXS( "ZNC::CORERemTimer", XS_ZNC_CORERemTimer, (char *)file ); newXS( "ZNC::COREPuts", XS_ZNC_COREPuts, (char *)file ); + newXS( "ZNC::COREConnectSock", XS_ZNC_COREConnectSock, (char *)file ); + newXS( "ZNC::COREListentSock", XS_ZNC_COREListenSock, (char *)file ); /* user functions */ newXS( "ZNC::GetNicks", XS_ZNC_GetNicks, (char *)file ); newXS( "ZNC::GetString", XS_ZNC_GetString, (char *)file ); newXS( "ZNC::LoadMod", XS_ZNC_LoadMod, (char *)file ); newXS( "ZNC::UnLoadMod", XS_ZNC_UnLoadMod, (char *)file ); + newXS( "ZNC::WriteSock", XS_ZNC_WriteSock, (char *)file ); // this sets up the eval CB that we call from here on out. this way we can grab the error produced SetupZNCScript(); @@ -795,8 +947,17 @@ void CModPerl::LoadPerlMod( const CString & sModule ) } } +void CModPerl::DestroyAllSocks() +{ + for( u_int a = 0; a < m_pManager->size(); a++ ) + { + if ( (*m_pManager)[a]->GetSockName() == ZNCSOCK ) + m_pManager->DelSock( a-- ); + } +} void CModPerl::UnLoadPerlMod( const CString & sModule ) { + DestroyAllSocks(); Eval( "ZNC::COREUnLoadMod( '" + m_pUser->GetUserName() + "', '" + sModule + "');" ); } @@ -820,4 +981,99 @@ void CPerlTimer::RunJob() if ( ((CModPerl *)m_pModule)->CallBack( m_sFuncName, vArgs, CModPerl::CB_TIMER ) != CModPerl::CONTINUE ) Stop(); } + +/////////////////////////// CPerlSock stuff //////////////////// +#define SOCKCB( a ) if ( CallBack( a ) != CModPerl::CONTINUE ) { Close(); } +int CPerlSock::CallBack( const PString & sFuncName ) +{ + return( m_pModule->CallBack( sFuncName, m_vArgs, CModPerl::CB_SOCK, m_sUsername ) ); +} + +// # OnConnect( $sockhandle, $parentsockhandle ) +void CPerlSock::Connected() +{ + SetupArgs(); + if ( m_iParentFD >= 0 ) + AddArg( m_iParentFD ); + + SOCKCB( "OnConnect" ) +} + +// # OnConnectionFrom( $sockhandle, $remotehost, $remoteport ) +bool CPerlSock::ConnectionFrom( const CS_STRING & sHost, int iPort ) +{ + SetupArgs(); + AddArg( sHost ); + AddArg( iPort ); + + // special case here + if ( CallBack( "OnConnectionFrom" ) != CModPerl::CONTINUE ) + return( false ); + + return( true ); +} + + +// # OnError( $sockhandle, $errno ) +void CPerlSock::SockError( int iErrno ) +{ + SetupArgs(); + AddArg( iErrno ); + SOCKCB( "OnError" ) +} + +// # OnConnectionRefused( $sockhandle ) +void CPerlSock::ConnectionRefused() +{ + SetupArgs(); + SOCKCB( "OnConnectionRefused" ) +} + +// # OnTimeout( $sockhandle ) +void CPerlSock::Timeout() +{ + SetupArgs(); + SOCKCB( "OnTimeout" ) +} + +// # OnDisconnect( $sockhandle ) +void CPerlSock::Disconnected() +{ + SetupArgs(); + SOCKCB( "OnDisconnect" ); +} +// # OnData( $sockhandle, $bytes, $length ) +void CPerlSock::ReadData( const char *data, int len ) +{ + SetupArgs(); + PString sData; + sData.append( data, len ); + AddArg( sData ); + AddArg( len ); + SOCKCB( "OnData" ) +} +// # OnReadLine( $sockhandle, $line ) +void CPerlSock::ReadLine( const CS_STRING & sLine ) +{ + SetupArgs(); + AddArg( sLine ); + SOCKCB( "OnReadLine" ); +} + +// # OnSockDestroy( $sockhandle ) +CPerlSock::~CPerlSock() +{ + SetupArgs(); + CallBack( "OnSockDestroy" ); +} + + #endif /* HAVE_PERL */ + + + + + + + + diff --git a/modules/modperl.pm b/modules/modperl.pm index 776ab176..745a1345 100644 --- a/modules/modperl.pm +++ b/modules/modperl.pm @@ -1,22 +1,3 @@ -# -# TODO need to add socket support -# OnConnect( $sockhandle, $parentsockhandle ) -# in the event this is comming in from a listener, the $sockhandle will be different from the others, so need to -# register it in the socket class like the others -# socket will register as $self = { "$sockhandle" => 1 } -# -# For Listeners -# OnConnectionFrom( $sockhandle, $remotehost, $remoteport ) -# -# OnError( $sockhandle, $errno ) -# OnConnectionRefused( $sockhandle ) -# OnTimeout( $sockhandle ) -# OnDisconnect( $sockhandle ) -# OnData( $sockhandle, $bytes, $length ) -# OnReadLine( $sockhandle, $bytes, $length ) -# ZNC::WriteSock( $sockhandle, $bytes, $length ) -# my $sockhandle = ZNC::ConnectSock( $host, $port, $timeout, $enablereadline ) -# my $sockhandle = ZNC::ConnectSockSSL( $host, $port, $timeout, $enablereadline ) # my $sockhandle = ZNC::ListenSock( $port, $bindhostname, $enablereadline ) # my $sockhandle = ZNC::ListenSockSSL( $port, $bindhostname, $enablereadline ) # @@ -34,6 +15,10 @@ sub COREEval eval $arg; } +sub COREShutdown() +{ + +} sub CORECallFunc { my ( $Username, $Func, @args ) = @_; @@ -190,6 +175,28 @@ sub CORECallTimer return( HALTMODS() ); } +sub CORECallSock +{ + my ( $Username, $Func, $ModName ) = @_; + + for( my $i = 0; $i < @MODS; $i++ ) + { + if ( ( $MODS[$i]->{ZNC_Username} eq $Username ) && ( $MODS[$i]->{ZNC_Name} eq $ModName ) ) + { + if ( $MODS[$i]->can( $Func ) ) + { + my $ret = $MODS[$i]->$Func(); + if ( $Func eq "OnConnectionFrom" ) + { # special case this for now, requires a return value + return( $ret ); + } + } + return( CONTINUE() ); + } + } + return( HALTMODS() ); +} + ###################### # Ease Of use USER functions sub AddTimer @@ -261,6 +268,74 @@ sub PutTarget PutIRC( "PRIVMSG $target :$line" ); } + + +sub ConnectSock +{ + my ( $host, $port, $timeout, $bEnableReadline, $bUseSSL ) = @_; + if ( ( !$host ) || ( $port ) ) + { + return( -1 ); + } + + if ( !$timeout ) + { + $timeout = 60; + } + + if ( !$bEnableReadline ) + { + $bEnableReadline = 0; + } + + if ( !$bUseSSL ) + { + $bUseSSL = 0; + } + + return( COREConnectSock( $host, $port, $bEnableReadline, $bUseSSL ) ); +} + +sub ConnectSockSSL +{ + my ( $host, $port, $timeout, $bEnableReadline, $bUseSSL ) = @_; + + return( ConnectSock( $host, $port, $timeout, $bEnableReadline, $bUseSSL, 1 ) ); +} + +sub ListenSock +{ + my ( $port, $bindhost, $bEnableReadline, $bUseSSL ) = @_; + + if ( !$port ) + { + return( -1 ); + } + + if ( !$bindhost ) + { + $bindhost = ""; + } + + if ( !$bEnableReadline ) + { + $bEnableReadline = 0; + } + + if ( !$bUseSSL ) + { + $bUseSSL = 0; + } + + return( COREListenSock( $port, $bindhost, $bEnableReadline, $bUseSSL ) ); +} + +sub ListenSockSSL +{ + my ( $port, $bindhost, $bEnableReadline ) = @_; + return( ListenSock( $port, $bindhost, $bEnableReadline, 1 ) ); +} + 1; @@ -270,3 +345,7 @@ sub PutTarget + + + +