/** * snoop.cc */ #include #include #include #include #include "server.h" #include "client.h" #include "snoop.h" #include "Channel.h" #include "iClient.h" #include "Network.h" #include "EConfig.h" #include "StringTokenizer.h" #include "misc.h" namespace gnuworld { using std::endl ; using std::string ; using std::stringstream ; extern "C" { xClient* _gnuwinit( const string& args ) { return new snoop( args ) ; } } snoop::snoop( const string& confFileName ) : xClient( confFileName ) { EConfig conf( confFileName ) ; cmdchar = conf.Require( "cmdchar" )->second ; adminChanName = conf.Require( "adminchan" )->second ; relayChanName = conf.Require( "relaychan" )->second ; defaultQuitMessage = conf.Require( "defaultquitmessage" )->second ; maxnicklen = ::atoi( conf.Require( "maxnicklen" )->second.c_str() ) ; EConfig::const_iterator ptr = conf.Find( "permit_user" ) ; while( ptr != conf.end() && ptr->first == "permit_user" ) { allowAccess.push_back( ptr->second ) ; ++ptr ; } ptr = conf.Find( "joinchan" ) ; while( ptr != conf.end() && ptr->first == "joinchan" ) { joinchans.push_back( ptr->second ) ; ++ptr ; } ptr = conf.Find( "address" ) ; while( ptr != conf.end() && ptr->first == "address" ) { addresses.push_back( ptr->second ) ; ++ptr ; } } snoop::~snoop() {} void snoop::BurstChannels() { xClient::BurstChannels() ; // It's ok if admin and relay chans are the same, // xServer::JoinChannel() will not join more than once Join( adminChanName ) ; Join( relayChanName ) ; //MyUplink->RegisterTimer( ::time( 0 ) + 1, this ) ; for (std::vector::const_iterator addr = addresses.begin(); addr < addresses.end(); addr++) { StringTokenizer straddr(*addr); string nick = straddr[0]; string user = straddr[1]; string host = straddr[2]; string real = straddr.assemble(3); string fulladdr = nick + '!' + user + '@' +host; elog << "snoop::handleSpawnClient> " << fulladdr << endl ; if ((validNickname(nick)) && ( Network->findNick( nick ) == 0 )) { char newCharYY[ 6 ] ; newCharYY[ 2 ] = 0 ; inttobase64( newCharYY, MyUplink->getIntYY(), 2 ) ; iClient* newClient = new (std::nothrow) iClient( MyUplink->getIntYY(), // intYY newCharYY, // charYYXXX nick, user, "AAAAAA", // host base 64 host, "realInsecureHost.com", // realInsecureHost "+i", // mode string(), // account 0, // account_ts real, 31337 // connect time ) ; assert( newClient != 0 ) ; if( !getUplink()->AttachClient( newClient, this ) ) { elog << "snoop::handleSpawnClient> Failed to add new client: " << *newClient << endl ; //Notice( srcClient, "Failed to create new fake client" ) ; delete newClient ; newClient = 0 ; continue; } // Each relay (spawn) client must join the relay chan getUplink()->JoinChannel( newClient, relayChanName ); clones.push_back( newClient ) ; } joinAll(); } } void snoop::OnChannelMessage( iClient* srcClient, Channel* theChan, const string& Message ) { if( strcasecmp( theChan->getName(), adminChanName ) ) { // Not the admin chan, ignore return ; } bool userHasAccess = hasAccess( srcClient->getAccount() ) ; if ((!srcClient->isOper()) && (!userHasAccess)) { return ; } StringTokenizer st( Message ) ; if( st.empty() || st[ 0 ] != cmdchar ) { return ; } elog << "snoop::OnChannelMessage> Received chan message: " << st.assemble( 0 ) << endl ; if( st.size() < 2 ) { return ; } string command( st[ 1 ] ) ; string_lower( command ) ; if( command == "spawnclient" ) { handleSpawnClient( srcClient, theChan, st ) ; } else if( command == "join" ) { handleSpawnJoin( srcClient, theChan, st ) ; } else if( command == "joinall" ) { if (st.size() < 3) joinAll(); else joinAll( srcClient, theChan, st ) ; } else if( command == "part" ) { handleSpawnPart( srcClient, theChan, st ) ; } else if( command == "partall" ) { partAll( srcClient, theChan, st ) ; } else if( command == "quit" ) { handleSpawnQuit( srcClient, st ) ; } else if( command == "quitall" ) { quitAll(srcClient, st ) ; } else if( (command == "say") || (command == "do") ) { say(srcClient,st); } else if( (command == "sayall") || (command == "doall") ) { sayAll(srcClient,st); } else if( command == "reload" ) { getUplink()->UnloadClient( this, "Something has changed in the matrix..." ) ; getUplink()->LoadClient( "libsnoop.la", getConfigFileName() ) ; } else if( command == "shutdown" ) { getUplink()->Shutdown() ; } } void snoop::handleSpawnClient( iClient* srcClient, Channel* theChan, const StringTokenizer& st ) { // snoop spawnclient nick!user@host realname if( st.size() < 4 ) { usage( srcClient, "spawnclient" ) ; return ; } const string realname( st.assemble( 3 ) ) ; //elog << "snoop::OnChannelMessage> nickTokens, st[ 2 ]: " // << st[ 2 ] // << endl ; // st[ 2 ] is nick!user@host:realname StringTokenizer nickTokens( st[ 2 ], '!' ) ; if( nickTokens.size() != 2 ) { usage( srcClient, "spawnclient" ) ; return ; } StringTokenizer userTokens( nickTokens[ 1 ], '@' ) ; if( userTokens.size() != 2 ) { usage( srcClient, "spawnclient" ) ; return ; } const string nickname( nickTokens[ 0 ] ) ; const string username( userTokens[ 0 ] ) ; const string hostname( userTokens[ 1 ] ) ; if( nickname.empty() || username.empty() || hostname.empty() ) { Notice( srcClient, "Please specify non-empty nick/user/host names" ) ; return ; } // Verify that the nickname is valid if( !validNickname( nickname ) ) { Notice( srcClient, "Invalid nickname" ) ; return ; } if( Network->findNick( nickname ) != 0 ) { Notice( srcClient, "SPAWNCLIENT: Nickname %s already exists", nickname.c_str() ) ;//elog << "cloner::OnPrivateMessage> " << Message << endl ; return ; } if( string::npos == hostname.find( '.' ) ) { Notice( srcClient, "SPAWNCLIENT: Hostname must appear valid" ) ; return ; } char newCharYY[ 6 ] ; newCharYY[ 2 ] = 0 ; inttobase64( newCharYY, MyUplink->getIntYY(), 2 ) ; iClient* newClient = new (std::nothrow) iClient( MyUplink->getIntYY(), // intYY newCharYY, // charYYXXX nickname, username, "AAAAAA", // host base 64 hostname, "realInsecureHost.com", // realInsecureHost "+i", // mode string(), // account 0, // account_ts realname, 31337 // connect time ) ; assert( newClient != 0 ) ; if( !getUplink()->AttachClient( newClient, this ) ) { elog << "snoop::handleSpawnClient> Failed to add new client: " << *newClient << endl ; Notice( srcClient, "Failed to create new fake client" ) ; delete newClient ; newClient = 0 ; } // Each relay (spawn) client must join the relay chan if( !getUplink()->JoinChannel( newClient, relayChanName ) ) { Notice( srcClient, "Unable to make \'%s\' join channel " "\'%s\', killing off client", st[ 2 ].c_str(), st[ 3 ].c_str() ) ; getUplink()->DetachClient( newClient ) ; delete newClient ; newClient = 0 ; return ; } clones.push_back( newClient ) ; Message( theChan, "Successfully created nick %s and joined " "relay channel %s", nickname.c_str(), relayChanName.c_str() ) ; } void snoop::handleSpawnJoin( iClient* srcClient, Channel* theChan, const StringTokenizer& st ) { // snoopy spawnjoin if( st.size() != 4 ) { usage( srcClient, "join" ) ; return ; } // Find the client iClient* fakeClient = Network->findNick( st[ 2 ] ) ; if( 0 == fakeClient ) { Notice( srcClient, "Nick \'%s\' does not exist", st[ 2 ].c_str() ) ; return ; } // Verify that it is a fake client, and owned by this module xClient* ownerClient = Network->findFakeClientOwner( fakeClient ) ; if( ownerClient != this ) { Notice( srcClient, "I don't own that client!" ) ; return ; } // fakeClient is valid, and owned by this module if( !getUplink()->JoinChannel( fakeClient, st[ 3 ] ) ) { Notice( srcClient, "Unable to make \'%s\' join channel " "%s", st[ 2 ].c_str(), st[ 3 ].c_str() ) ; } else { Message( theChan, "%s successfully joined %s", st[ 2 ].c_str(), st[ 3 ].c_str() ) ; } } void snoop::joinAll() { if (joinchans.empty()) return; for (std::vector::const_iterator jchan = joinchans.begin(); jchan < joinchans.end(); jchan++) for( std::list< iClient* >::const_iterator ptr = clones.begin(), endPtr = clones.end() ; ptr != endPtr ; ++ptr ) { //if( !getUplink()->JoinChannel( *ptr, relayChanName ) ) return; getUplink()->JoinChannel( *ptr, *jchan ); } } void snoop::joinAll(iClient* srcClient, Channel* theChan, const StringTokenizer& st) { // snoopy joinall if( st.size() != 3 ) { usage( srcClient, "joinall" ) ; return ; } string chanName = st[2]; //st.assemble(2); //( st[ 0 ] ) ; if( chanName[ 0 ] != '#' ) { chanName.insert( chanName.begin(), '#' ) ; } for( std::list< iClient* >::const_iterator ptr = clones.begin(), endPtr = clones.end() ; ptr != endPtr ; ++ptr ) { if( !getUplink()->JoinChannel( *ptr, st.assemble(2) ) ) { Message(theChan, "%s unable to join channel %s",(*ptr)->getNickName().c_str(),chanName.c_str()); //Notice( srcClient, "Unable to make \'%s\' join channel " // "\'%s\', killing off client", // chanName.c_str(), // (*ptr)->getNickName().c_str() ) ; //getUplink()->DetachClient( *ptr ) ; //delete (*ptr); (*ptr) = 0; //delete newClient ; newClient = 0 ; //return ; } } } void snoop::handleSpawnPart( iClient* srcClient, Channel* theChan, const StringTokenizer& st ) { // snoopy part [message] if( st.size() < 4 ) { usage( srcClient, "part" ) ; return ; } // Find the client iClient* fakeClient = Network->findNick( st[ 2 ] ) ; if( 0 == fakeClient ) { Notice( srcClient, "Nick \'%s\' does not exist", st[ 2 ].c_str() ) ; return ; } // Verify that it is a fake client, and owned by this module xClient* ownerClient = Network->findFakeClientOwner( fakeClient ) ; if( ownerClient != this ) { Notice( srcClient, "I don't own that client!" ) ; return ; } // fakeClient is valid, and owned by this module getUplink()->PartChannel( fakeClient, st.assemble(3) ) ; //st[3] Message( theChan, "%s successfully parted %s", st[ 2 ].c_str(), st[ 3 ].c_str() ) ; } void snoop::partAll(iClient* srcClient, Channel* theChan, const StringTokenizer& st) { // snoopy partall [message] if( st.size() < 3 ) { usage( srcClient, "partall" ) ; return ; } for( std::list< iClient* >::const_iterator ptr = clones.begin(), endPtr = clones.end() ; ptr != endPtr ; ++ptr ) { // Find the client iClient* fakeClient = Network->findFakeClient(*ptr); //->findNick( st[ 2 ] ) ; if( 0 == fakeClient ) { Notice( srcClient, "Nick \'%s\' does not exist", st[ 2 ].c_str() ) ; return ; } // Verify that it is a fake client, and owned by this module xClient* ownerClient = Network->findFakeClientOwner( fakeClient ) ; if( ownerClient != this ) { Notice( srcClient, "I don't own that client!" ) ; return ; } // fakeClient is valid, and owned by this module getUplink()->PartChannel( fakeClient, st.assemble(2) ) ; //st[2] //Message( theChan, "%s successfully parted %s", st[ 2 ].c_str(), st[ 3 ].c_str() ) ; } Message(theChan, "Successfully parted all channels"); } void snoop::usage( iClient* srcClient, const string& command ) { if( command == "spawnclient" ) { Notice( srcClient, "Usage: spawnclient " ) ; } else if( command == "join" ) { Notice( srcClient, "Usage: join [key]" ) ; } else if( command == "joinall" ) { Notice( srcClient, "Usage: joinall [key]" ) ; } else if( command == "part" ) { Notice( srcClient, "Usage: part [message]" ) ; } else if( command == "partall" ) { Notice( srcClient, "Usage: partall [message]" ) ; } else if( command == "quit" ) { Notice( srcClient, "Usage: quit [quit message]" ) ; } else if( command == "quitall" ) { Notice( srcClient, "Usage: quitall [quit message]" ) ; } else if( command == "say" ) { Notice( srcClient, "Usage: say " ) ; } else if( command == "sayall" ) { Notice( srcClient, "Usage: sayall " ) ; } else if( command == "do" ) { Notice( srcClient, "Usage: do " ) ; } else if( command == "doall" ) { Notice( srcClient, "Usage: doall " ) ; } } /* //From now on, snooping is not our primary task void snoop::OnFakeChannelMessage( iClient* srcClient, iClient* fakeClient, Channel* theChan, const string& Message ) { // A fake client received a channel message // Ignore if it's the admin or relay channel if( !strcasecmp( theChan->getName(), adminChanName ) || !strcasecmp( theChan->getName(), relayChanName ) ) { return ; } // For now simply relay to the relay chan without checking for // state, etc Channel* relayChan = Network->findChannel( relayChanName ) ; if( 0 == relayChan ) { elog << "snoop::OnFakeChannelMessage> Fake client: " << *fakeClient << " unable to find relay chan: " << relayChanName << endl ; return ; } stringstream s ; s << "[RELAY " << theChan->getName() << "] " << srcClient->getNickName() << "!" << srcClient->getUserName() << "@" << srcClient->getInsecureHost() << ": " << Message ; FakeMessage( relayChan, fakeClient, s.str().c_str() ) ; } */ void snoop::handleSpawnQuit( iClient* srcClient, const StringTokenizer& st ) { // snoopy quit [message] if( st.size() < 3 ) { usage( srcClient, "quit" ) ; return ; } string quitMessage( defaultQuitMessage ) ; if( st.size() >= 4 ) { quitMessage = st.assemble( 3 ) ; } // Find the client iClient* fakeClient = Network->findFakeNick( st[ 2 ] ) ; if( 0 == fakeClient ) { Notice( srcClient, "Nick \'%s\' does not exist", st[ 2 ].c_str() ) ; return ; } // Verify that it is a fake client, and owned by this module xClient* ownerClient = Network->findFakeClientOwner( fakeClient ) ; if( ownerClient != this ) { Notice( srcClient, "I don't own that client!" ) ; return ; } getUplink()->DetachClient( fakeClient, quitMessage ) ; Notice( srcClient, "%s detached", st[ 2 ].c_str() ) ; // This module allocated the iClient, and it must therefore deallocate // it. delete fakeClient ; fakeClient = 0 ; } void snoop::quitAll(iClient* srcClient, const StringTokenizer& st) { // snoopy quitall [message] if( st.size() < 3 ) { usage( srcClient, "quitall" ) ; return ; } string quitMessage( defaultQuitMessage ) ; if( st.size() >= 3 ) { quitMessage = st.assemble( 2 ) ; } //std::vector toDie; for( std::list< iClient* >::const_iterator ptr = clones.begin(), endPtr = clones.end() ; ptr != endPtr ; ++ptr ) { // Find the client iClient* fakeClient = Network->findFakeClient(*ptr); //->findFakeNick( st[ 2 ] ) ; if( 0 == fakeClient ) { Notice( srcClient, "Nick \'%s\' does not exist", st[ 2 ].c_str() ) ; return ; } // Verify that it is a fake client, and owned by this module xClient* ownerClient = Network->findFakeClientOwner( fakeClient ) ; if( ownerClient != this ) { Notice( srcClient, "I don't own that client!" ) ; return ; } getUplink()->DetachClient( fakeClient, quitMessage ) ; //Notice( srcClient, "%s detached", st[ 2 ].c_str() ) ; // This module allocated the iClient, and it must therefore deallocate // it. //toDie.push_back(*ptr); delete fakeClient ; fakeClient = 0 ; } clones.clear(); } void snoop::say(iClient* srcClient, const StringTokenizer& st) { // snoopy say [message] if( st.size() < 5 ) { if (st[1] == "say") usage( srcClient, "say" ) ; if (st[1] == "do" ) usage( srcClient, "do" ) ; return ; } string chanOrNickName( st[ 3 ] ) ; string privMsg( st.assemble(4).c_str() ) ; if( chanOrNickName[ 0 ] != '#' ) { // Assume nickname iClient* Target = Network->findNick( st[ 3 ] ) ; if( NULL == Target ) { Notice( srcClient, "Unable to find nick: %s" , st[ 3 ].c_str() ) ; return ; } chanOrNickName = Target->getCharYYXXX(); } iClient* fakeClient = Network->findFakeNick( st[ 2 ] ) ; if( 0 == fakeClient ) { Notice( srcClient, "Nick \'%s\' does not exist", st[ 2 ].c_str() ) ; return ; } // Verify that it is a fake client, and owned by this module xClient* ownerClient = Network->findFakeClientOwner( fakeClient ) ; if( ownerClient != this ) { Notice( srcClient, "I don't own that client!" ) ; return ; } stringstream s ; s << fakeClient->getCharYYXXX() << " P " << chanOrNickName << " :" << privMsg ; MyUplink->Write( s ) ; } void snoop::sayAll(iClient* srcClient, const StringTokenizer& st) { // snoopy say [message] if( st.size() < 4 ) { if (st[1] == "sayall") usage( srcClient, "sayall" ) ; if (st[1] == "doall" ) usage( srcClient, "doall" ) ; return ; } string chanOrNickName( st[ 2 ] ) ; string privMsg( st.assemble(3).c_str() ) ; if( chanOrNickName[ 0 ] != '#' ) { // Assume nickname iClient* Target = Network->findNick( st[ 2 ] ) ; if( NULL == Target ) { Notice( srcClient, "Unable to find nick: %s" , st[ 2 ].c_str() ) ; return ; } chanOrNickName = Target->getCharYYXXX(); } for( std::list< iClient* >::const_iterator ptr = clones.begin(), endPtr = clones.end() ; ptr != endPtr ; ++ptr ) { stringstream s ; s << (*ptr)->getCharYYXXX() << " P " << chanOrNickName << " :" << privMsg ; MyUplink->Write( s ) ; } } bool snoop::validNickname( const string& nickname ) const { if( nickname.empty() || nickname.size() > maxnicklen ) { return false ; } /* * From ircu: * Nickname characters are in range 'A'..'}', '_', '-', '0'..'9' * anything outside the above set will terminate nickname. * In addition, the first character cannot be '-' or a Digit. */ if( isdigit( nickname[ 0 ] ) ) { return false ; } for( string::const_iterator sItr = nickname.begin() ; sItr != nickname.end() ; ++sItr ) { if( *sItr >= 'A' && *sItr <= '}' ) { // ok continue ; } if( '_' == *sItr || '-' == *sItr ) { // ok continue ; } if( *sItr >= '0' && *sItr <= '9' ) { // ok continue ; } // bad char return false ; } return true ; } bool snoop::hasAccess( const string& accountName ) const { for( std::list< string >::const_iterator itr = allowAccess.begin() ; itr != allowAccess.end() ; ++itr ) { if( !strcasecmp( accountName, *itr ) ) { return true ; } } return false ; } } // namespace gnuworld