bool operator()(SERVER_s s1, SERVER_s s2) const { // [BB] NETWORK_AddressToString uses a static char array to generate the string, so we // need to cache the string conversion of the first string when comparing to two. // Note: Because we need a "<" comparison and not a "!=" comparison we can't use // NETWORK_CompareAddress. std::string address1 = NETWORK_AddressToString ( s1.Address ); return ( stricmp ( address1.c_str(), NETWORK_AddressToString ( s2.Address ) ) < 0 ); }
//***************************************************************************** // void SERVER_RCON_Tick( ) { // Remove timed-out candidates. for ( unsigned int i = 0; i < g_Candidates.Size( ); ) { if (( gametic - g_Candidates[i].iLastMessageTic ) >= ( RCON_CANDIDATE_TIMEOUT_TIME * TICRATE )) g_Candidates.Delete( i ); else i++; } // Remove timed-out clients. for ( unsigned int i = 0; i < g_AuthedClients.Size( ); ) { if (( gametic - g_AuthedClients[i].iLastMessageTic ) >= ( RCON_CLIENT_TIMEOUT_TIME * TICRATE )) { Printf( "RCON client at %s timed out.\n", NETWORK_AddressToString( g_AuthedClients[i].Address )); g_AuthedClients.Delete( i ); SERVER_RCON_UpdateInfo( SVRCU_ADMINCOUNT ); } else i++; } g_BadRequestFloodQueue.adjustHead( gametic / 1000 ); }
static void main_AttemptConnection( ) { char szBuffer[128]; // Update the GUI. time( &g_tLastSentCommand ); main_SetState( STATE_CONNECTING ); sprintf( szBuffer, "Connecting to %s...", NETWORK_AddressToString( g_ServerAddress )); main_UpdateStatusbar( szBuffer ); main_UpdateTrayTooltip( szBuffer ); GetDlgItemText( g_hDlg, IDC_SERVERIP, szBuffer, 128 ); // Save this server to our config file. g_Config.SetSection( "Settings", true ); g_Config.SetValueForKey( "LastServer", szBuffer ); g_Config.WriteConfigFile( ); // Start listening for packets. if ( g_hThread == NULL ) { g_hThread = CreateThread( NULL, 0, main_Loop, 0, 0, 0 ); NETWORK_InitBuffer( &g_MessageBuffer, 8192, BUFFERTYPE_WRITE ); } NETWORK_ClearBuffer( &g_MessageBuffer ); NETWORK_WriteByte( &g_MessageBuffer.ByteStream, CLRC_BEGINCONNECTION ); NETWORK_WriteByte( &g_MessageBuffer.ByteStream, PROTOCOL_VERSION ); NETWORK_LaunchPacket( &g_MessageBuffer, g_ServerAddress ); }
//***************************************************************************** // void MASTERSERVER_AddServer( const SERVER_s &Server, std::set<SERVER_s, SERVERCompFunc> &ServerSet ) { std::set<SERVER_s, SERVERCompFunc>::iterator addedServer = ServerSet.insert ( Server ).first; if ( addedServer == ServerSet.end() ) printf( "ERROR: Adding new entry to the set failed. This should not happen!\n" ); else { addedServer->lLastReceived = g_lCurrentTime; if ( &ServerSet == &g_Servers ) { printf( "+ Adding %s (revision %d) to the server list.\n", NETWORK_AddressToString( addedServer->Address ), addedServer->iServerRevision ); MASTERSERVER_SendBanlistToServer( *addedServer ); } else printf( "+ Adding %s (revision %d) to the verification list.\n", NETWORK_AddressToString( addedServer->Address ), addedServer->iServerRevision ); } }
//***************************************************************************** // void MASTERSERVER_SendBanlistToServer( const SERVER_s &Server ) { // [BB] If the server supports it, potentially split the ban list over multiple packets. if ( Server.iServerRevision >= 2907 ) { BanlistPacketSender sender ( Server ); sender.start(); // Write all the bans. for ( unsigned int i = 0; i < g_BannedIPs.size( ); ++i ) sender.writeBanEntry ( g_BannedIPs.getEntryAsString( i, false, false, false ).c_str( ), MSB_BAN ); // Write all the exceptions. for ( unsigned int i = 0; i < g_BannedIPExemptions.size( ); ++i ) sender.writeBanEntry ( g_BannedIPExemptions.getEntryAsString( i, false, false, false ).c_str( ), MSB_BANEXEMPTION ); sender.end(); } else { NETWORK_ClearBuffer( &g_MessageBuffer ); NETWORK_WriteByte( &g_MessageBuffer.ByteStream, MASTER_SERVER_BANLIST ); // [BB] If the server sent us a verification string, send it along with the ban list. // This allows the server to verify that the list actually was sent from our master // (and is not just a packet with forged source IP). if ( Server.MasterBanlistVerificationString.size() ) NETWORK_WriteString( &g_MessageBuffer.ByteStream, Server.MasterBanlistVerificationString.c_str() ); // Write all the bans. NETWORK_WriteLong( &g_MessageBuffer.ByteStream, g_BannedIPs.size( )); for ( ULONG i = 0; i < g_BannedIPs.size( ); i++ ) NETWORK_WriteString( &g_MessageBuffer.ByteStream, g_BannedIPs.getEntryAsString( i, false, false, false ).c_str( )); // Write all the exceptions. NETWORK_WriteLong( &g_MessageBuffer.ByteStream, g_BannedIPExemptions.size( )); for ( ULONG i = 0; i < g_BannedIPExemptions.size( ); i++ ) NETWORK_WriteString( &g_MessageBuffer.ByteStream, g_BannedIPExemptions.getEntryAsString( i, false, false, false ).c_str( )); NETWORK_LaunchPacket( &g_MessageBuffer, Server.Address ); } Server.bHasLatestBanList = true; Server.bVerifiedLatestBanList = false; printf( "-> Banlist sent to %s.\n", NETWORK_AddressToString( Server.Address )); }
//***************************************************************************** // void CALLVOTE_Tick( void ) { ULONG ulNumYes; ULONG ulNumNo; switch ( g_VoteState ) { case VOTESTATE_NOVOTE: break; case VOTESTATE_INVOTE: // [RC] Hide the voteing screen shortly after voting. if ( g_ulShowVoteScreenTicks ) g_ulShowVoteScreenTicks--; if ( g_ulVoteCountdownTicks ) { g_ulVoteCountdownTicks--; if (( NETWORK_GetState( ) != NETSTATE_CLIENT ) && ( CLIENTDEMO_IsPlaying( ) == false ) && ( g_ulVoteCountdownTicks == 0 )) { ulNumYes = callvote_CountPlayersWhoVotedYes( ); ulNumNo = callvote_CountPlayersWhoVotedNo( ); if (( ulNumYes > 0 ) && ( ulNumYes > ulNumNo )) g_bVotePassed = true; else g_bVotePassed = false; callvote_EndVote( ); } } break; case VOTESTATE_VOTECOMPLETED: if ( g_ulVoteCompletedTicks ) { if ( --g_ulVoteCompletedTicks == 0 ) { g_PreviousVotes.back( ).bPassed = g_bVotePassed; // If the vote passed, execute the command string. if (( g_bVotePassed ) && ( !g_bVoteCancelled ) && ( NETWORK_GetState( ) != NETSTATE_CLIENT ) && ( CLIENTDEMO_IsPlaying( ) == false )) { // [BB, RC] If the vote is a kick vote, we have to rewrite g_VoteCommand to both use the stored IP, and temporarily ban it. // [Dusk] Write the kick reason into the ban reason, [BB] but only if it's not empty. // [BB] "forcespec" votes need a similar handling. if ( ( strncmp( g_VoteCommand, "kick", 4 ) == 0 ) || ( strncmp( g_VoteCommand, "forcespec", 9 ) == 0 ) ) { if ( strncmp( g_VoteCommand, "kick", 4 ) == 0 ) g_VoteCommand.Format( "addban %s 10min \"Vote kick", NETWORK_AddressToString( g_KickVoteVictimAddress ) ); else g_VoteCommand.Format( "kickfromgame_idx %d \"Vote forcespec", static_cast<int>(SERVER_FindClientByAddress ( g_KickVoteVictimAddress )) ); g_VoteCommand.AppendFormat( ", %d to %d", static_cast<int>(callvote_CountPlayersWhoVotedYes( )), static_cast<int>(callvote_CountPlayersWhoVotedNo( )) ); if ( g_VoteReason.IsNotEmpty() ) g_VoteCommand.AppendFormat ( " (%s)", g_VoteReason.GetChars( ) ); g_VoteCommand += ".\""; } AddCommandString( (char *)g_VoteCommand.GetChars( )); } // Reset the module. CALLVOTE_ClearVote( ); } } break; } }
//***************************************************************************** // int main( int argc, char **argv ) { BYTESTREAM_s *pByteStream; std::cerr << "=== Zandronum Master ===\n"; std::cerr << "Revision: "SVN_REVISION_STRING"\n"; std::cerr << "Port: " << DEFAULT_MASTER_PORT << std::endl << std::endl; // Initialize the network system. NETWORK_Construct( DEFAULT_MASTER_PORT, ( ( argc >= 4 ) && ( stricmp ( argv[2], "-useip" ) == 0 ) ) ? argv[3] : NULL ); // Initialize the message buffer we send messages to the launcher in. NETWORK_InitBuffer( &g_MessageBuffer, MAX_UDP_PACKET, BUFFERTYPE_WRITE ); NETWORK_ClearBuffer( &g_MessageBuffer ); // Initialize the bans subsystem. std::cerr << "Initializing ban list...\n"; MASTERSERVER_InitializeBans( ); int lastParsingTime = I_GetTime( ); int lastBanlistVerificationTimeout = lastParsingTime; // [BB] Do we want to hide servers that ignore our ban list? if ( ( argc >= 2 ) && ( stricmp ( argv[1], "-DontHideBanIgnoringServers" ) == 0 ) ) { std::cerr << "Note: Servers that do not enforce our ban list are shown." << std::endl; g_bHideBanIgnoringServers = false; } else { std::cerr << "Note: Servers that do not enforce our ban list are hidden." << std::endl; g_bHideBanIgnoringServers = true; } // Done setting up! std::cerr << "\n=== Master server started! ===\n"; while ( 1 ) { g_lCurrentTime = I_GetTime( ); I_DoSelect( ); while ( NETWORK_GetPackets( )) { // Set up our byte stream. pByteStream = &NETWORK_GetNetworkMessageBuffer( )->ByteStream; pByteStream->pbStream = NETWORK_GetNetworkMessageBuffer( )->pbData; pByteStream->pbStreamEnd = pByteStream->pbStream + NETWORK_GetNetworkMessageBuffer( )->ulCurrentSize; // Now parse the packet. MASTERSERVER_ParseCommands( pByteStream ); } // Update the ignore queues. g_queryIPQueue.adjustHead ( g_lCurrentTime ); g_floodProtectionIPQueue.adjustHead ( g_lCurrentTime ); g_ShortFloodQueue.adjustHead ( g_lCurrentTime ); // See if any servers have timed out. MASTERSERVER_CheckTimeouts( ); if ( g_lCurrentTime > lastBanlistVerificationTimeout + 10 ) { for( std::set<SERVER_s, SERVERCompFunc>::iterator it = g_Servers.begin(); it != g_Servers.end(); ++it ) { if ( ( it->bVerifiedLatestBanList == false ) && ( it->bNewFormatServer == true ) ) { it->bHasLatestBanList = false; std::cerr << "No receipt received from " << NETWORK_AddressToString ( it->Address ) << ". Resending banlist.\n"; } } lastBanlistVerificationTimeout = g_lCurrentTime; } // [BB] Reparse the ban list every 15 minutes. if ( g_lCurrentTime > lastParsingTime + 15*60 ) { std::cerr << "~ Reparsing the ban lists...\n"; MASTERSERVER_InitializeBans( ); lastParsingTime = g_lCurrentTime; } } return ( 0 ); }
//***************************************************************************** // void MASTERSERVER_CheckTimeouts( std::set<SERVER_s, SERVERCompFunc> &ServerSet ) { // [BB] Because we are erasing entries from the set, the iterator has to be incremented inside // the loop, depending on whether and element was erased or not. for( std::set<SERVER_s, SERVERCompFunc>::iterator it = ServerSet.begin(); it != ServerSet.end(); ) { // If the server has timed out, make it an open slot! if (( g_lCurrentTime - it->lLastReceived ) >= 60 ) { printf( "- %server at %s timed out.\n", ( &ServerSet == &g_UnverifiedServers ) ? "Unverified s" : "S", NETWORK_AddressToString( it->Address )); // [BB] The standard does not require set::erase to return the incremented operator, // that's why we must use the post increment operator here. ServerSet.erase ( it++ ); continue; } else { // [BB] If the server doesn't have the latest ban list, send it now. // This construction has the drawback that all servers are updated at once. // Possibly it will be necessary to do this differently. if ( it->bHasLatestBanList == false ) MASTERSERVER_SendBanlistToServer( *it ); ++it; } } }
//***************************************************************************** // void MASTERSERVER_ParseCommands( BYTESTREAM_s *pByteStream ) { long lCommand; NETADDRESS_s AddressFrom; AddressFrom = NETWORK_GetFromAddress( ); // [RC] If this IP is in our flood queue, ignore it completely. if ( g_floodProtectionIPQueue.addressInQueue( AddressFrom ) || g_ShortFloodQueue.addressInQueue( AddressFrom )) { while ( NETWORK_ReadByte( pByteStream ) != -1 ) // [RC] Is this really necessary? ; return; } // Is this IP banned? Send the user an explanation, and ignore the IP for 30 seconds. if ( !g_BannedIPExemptions.isIPInList( AddressFrom ) && g_BannedIPs.isIPInList( AddressFrom )) { NETWORK_ClearBuffer( &g_MessageBuffer ); NETWORK_WriteLong( &g_MessageBuffer.ByteStream, MSC_IPISBANNED ); NETWORK_LaunchPacket( &g_MessageBuffer, AddressFrom ); printf( "* Received challenge from banned IP (%s). Ignoring for 10 seconds.\n", NETWORK_AddressToString( AddressFrom )); g_queryIPQueue.addAddress( AddressFrom, g_lCurrentTime, &std::cerr ); return; } lCommand = NETWORK_ReadLong( pByteStream ); switch ( lCommand ) { // Server is telling master server of its existence. case SERVER_MASTER_CHALLENGE: { // Certain IPs can be blocked from just hosting. if ( !g_BannedIPExemptions.isIPInList( AddressFrom ) && g_BlockedIPs.isIPInList( AddressFrom )) { NETWORK_ClearBuffer( &g_MessageBuffer ); NETWORK_WriteLong( &g_MessageBuffer.ByteStream, MSC_IPISBANNED ); NETWORK_LaunchPacket( &g_MessageBuffer, AddressFrom ); printf( "* Received server challenge from blocked IP (%s). Ignoring for 10 seconds.\n", NETWORK_AddressToString( AddressFrom )); g_queryIPQueue.addAddress( AddressFrom, g_lCurrentTime, &std::cerr ); return; } SERVER_s newServer; newServer.Address = AddressFrom; // [BB] If no verification string was send, NETWORK_ReadString just returns an empty string. // Thus, this is still compatible with older servers that don't send the string. newServer.MasterBanlistVerificationString = NETWORK_ReadString( pByteStream ); // [BB] If no value was send, NETWORK_ReadByte just returns -1. // Thus, this is still compatible with older servers that don't tell us whether they enforce our bans // and gives them the benefit of the doubt, i.e. it assumes that they enforce our bans. const int temp = NETWORK_ReadByte( pByteStream ); newServer.bEnforcesBanList = ( temp != 0 ); newServer.bNewFormatServer = ( temp != -1 ); newServer.iServerRevision = ( ( pByteStream->pbStreamEnd - pByteStream->pbStream ) >= 4 ) ? NETWORK_ReadLong( pByteStream ) : NETWORK_ReadShort( pByteStream ); std::set<SERVER_s, SERVERCompFunc>::iterator currentServer = g_Servers.find ( newServer ); // This is a new server; add it to the list. if ( currentServer == g_Servers.end() ) { unsigned int iNumOtherServers = 0; // First count the number of servers from this IP. for( std::set<SERVER_s, SERVERCompFunc>::const_iterator it = g_Servers.begin(); it != g_Servers.end(); ++it ) { if ( NETWORK_CompareAddress( it->Address, AddressFrom, true )) iNumOtherServers++; } if ( iNumOtherServers >= 10 && !g_MultiServerExceptions.isIPInList( AddressFrom )) printf( "* More than 10 servers received from %s. Ignoring request...\n", NETWORK_AddressToString( AddressFrom )); else { // [BB] 3021 is 98d, don't put those servers on the list. if ( ( newServer.bNewFormatServer ) && ( newServer.iServerRevision != 3021 ) ) { std::set<SERVER_s, SERVERCompFunc>::iterator currentUnverifiedServer = g_UnverifiedServers.find ( newServer ); // [BB] This is a new server, but we still need to verify it. if ( currentUnverifiedServer == g_UnverifiedServers.end() ) { srand ( time(NULL) ); newServer.ServerVerificationInt = rand() + rand() * rand() + rand() * rand() * rand(); // [BB] We don't send the ban list to unverified servers, so just pretent the server already has the list. newServer.bHasLatestBanList = true; MASTERSERVER_RequestServerVerification ( newServer ); MASTERSERVER_AddServer( newServer, g_UnverifiedServers ); } } else { printf( "* Received server challenge from old server (%s). Ignoring IP for 10 seconds.\n", NETWORK_AddressToString( newServer.Address )); g_queryIPQueue.addAddress( newServer.Address, g_lCurrentTime, &std::cerr ); } } } // Command is from a server already on the list. It's just sending us a heartbeat. else { // [BB] Only if the verification string matches. if ( stricmp ( currentServer->MasterBanlistVerificationString.c_str(), newServer.MasterBanlistVerificationString.c_str() ) == 0 ) { currentServer->lLastReceived = g_lCurrentTime; // [BB] The server possibly changed the ban setting, so update it. currentServer->bEnforcesBanList = newServer.bEnforcesBanList; } } // Ignore IP for 10 seconds. // if ( !g_MultiServerExceptions.isIPInList( Address ) ) // g_floodProtectionIPQueue.addAddress( AddressFrom, g_lCurrentTime, &std::cerr ); return; } case SERVER_MASTER_VERIFICATION: { SERVER_s newServer; newServer.Address = AddressFrom; newServer.MasterBanlistVerificationString = NETWORK_ReadString( pByteStream ); newServer.ServerVerificationInt = NETWORK_ReadLong( pByteStream ); std::set<SERVER_s, SERVERCompFunc>::iterator currentServer = g_UnverifiedServers.find ( newServer ); // [BB] Apparently, we didn't request any verification from this server, so ignore it. if ( currentServer == g_UnverifiedServers.end() ) return; if ( ( stricmp ( newServer.MasterBanlistVerificationString.c_str(), currentServer->MasterBanlistVerificationString.c_str() ) == 0 ) && ( newServer.ServerVerificationInt == currentServer->ServerVerificationInt ) ) { MASTERSERVER_AddServer( *currentServer, g_Servers ); g_UnverifiedServers.erase ( currentServer ); } return; } case SERVER_MASTER_BANLIST_RECEIPT: { SERVER_s server; server.Address = AddressFrom; server.MasterBanlistVerificationString = NETWORK_ReadString( pByteStream ); std::set<SERVER_s, SERVERCompFunc>::iterator currentServer = g_Servers.find ( server ); // [BB] We don't know the server. Just ignore it. if ( currentServer == g_Servers.end() ) return; if ( stricmp ( server.MasterBanlistVerificationString.c_str(), currentServer->MasterBanlistVerificationString.c_str() ) == 0 ) { currentServer->bVerifiedLatestBanList = true; std::cerr << NETWORK_AddressToString ( AddressFrom ) << " acknowledged receipt of the banlist.\n"; } } return; // Launcher is asking master server for server list. case LAUNCHER_SERVER_CHALLENGE: case LAUNCHER_MASTER_CHALLENGE: { NETWORK_ClearBuffer( &g_MessageBuffer ); // Did this IP query us recently? If so, send it an explanation, and ignore it completely for 3 seconds. if ( g_queryIPQueue.addressInQueue( AddressFrom )) { NETWORK_WriteLong( &g_MessageBuffer.ByteStream, MSC_REQUESTIGNORED ); NETWORK_LaunchPacket( &g_MessageBuffer, AddressFrom ); printf( "* Extra launcher challenge from %s. Ignoring for 3 seconds.\n", NETWORK_AddressToString( AddressFrom )); g_ShortFloodQueue.addAddress( AddressFrom, g_lCurrentTime, &std::cerr ); return; } // [BB] The launcher only sends the protocol version with LAUNCHER_MASTER_CHALLENGE. if ( lCommand == LAUNCHER_MASTER_CHALLENGE ) { // [BB] Check if the requested version of the protocol matches ours. const unsigned short usVersion = NETWORK_ReadShort( pByteStream ); if ( usVersion != MASTER_SERVER_VERSION ) { NETWORK_WriteLong( &g_MessageBuffer.ByteStream, MSC_WRONGVERSION ); NETWORK_LaunchPacket( &g_MessageBuffer, AddressFrom ); return; } } printf( "-> Sending server list to %s.\n", NETWORK_AddressToString( AddressFrom )); // Wait 10 seconds before sending this IP the server list again. g_queryIPQueue.addAddress( AddressFrom, g_lCurrentTime, &std::cerr ); switch ( lCommand ) { case LAUNCHER_SERVER_CHALLENGE: // Send the list of servers. NETWORK_WriteLong( &g_MessageBuffer.ByteStream, MSC_BEGINSERVERLIST ); for( std::set<SERVER_s, SERVERCompFunc>::const_iterator it = g_Servers.begin(); it != g_Servers.end(); ++it ) { // [BB] Possibly omit servers that don't enforce our ban list. if ( ( it->bEnforcesBanList == true ) || ( g_bHideBanIgnoringServers == false ) ) MASTERSERVER_SendServerIPToLauncher ( it->Address, &g_MessageBuffer.ByteStream ); } // Tell the launcher that we're done sending servers. NETWORK_WriteByte( &g_MessageBuffer.ByteStream, MSC_ENDSERVERLIST ); // Send the launcher our packet. NETWORK_LaunchPacket( &g_MessageBuffer, AddressFrom ); return; case LAUNCHER_MASTER_CHALLENGE: const unsigned long ulMaxPacketSize = 1024; unsigned long ulPacketNum = 0; std::set<SERVER_s, SERVERCompFunc>::const_iterator it = g_Servers.begin(); NETWORK_WriteLong( &g_MessageBuffer.ByteStream, MSC_BEGINSERVERLISTPART ); NETWORK_WriteByte( &g_MessageBuffer.ByteStream, ulPacketNum ); NETWORK_WriteByte( &g_MessageBuffer.ByteStream, MSC_SERVERBLOCK ); unsigned long ulSizeOfPacket = 6; // 4 (MSC_BEGINSERVERLISTPART) + 1 (0) + 1 (MSC_SERVERBLOCK) while ( it != g_Servers.end() ) { NETADDRESS_s serverAddress = it->Address; std::vector<USHORT> serverPortList; do { // [BB] Possibly omit servers that don't enforce our ban list. if ( ( it->bEnforcesBanList == true ) || ( g_bHideBanIgnoringServers == false ) ) serverPortList.push_back ( it->Address.usPort ); ++it; } while ( ( it != g_Servers.end() ) && NETWORK_CompareAddress( it->Address, serverAddress, true ) ); // [BB] All servers on this IP ignore the list, nothing to send. if ( serverPortList.size() == 0 ) continue; const unsigned long ulServerBlockNetSize = MASTERSERVER_CalcServerIPBlockNetSize( serverAddress, serverPortList ); // [BB] If sending this block would cause the current packet to exceed ulMaxPacketSize ... if ( ulSizeOfPacket + ulServerBlockNetSize > ulMaxPacketSize - 1 ) { // [BB] ... close the current packet and start a new one. NETWORK_WriteByte( &g_MessageBuffer.ByteStream, 0 ); // [BB] Terminate MSC_SERVERBLOCK by sending 0 ports. NETWORK_WriteByte( &g_MessageBuffer.ByteStream, MSC_ENDSERVERLISTPART ); NETWORK_LaunchPacket( &g_MessageBuffer, AddressFrom ); NETWORK_ClearBuffer( &g_MessageBuffer ); ++ulPacketNum; ulSizeOfPacket = 5; NETWORK_WriteLong( &g_MessageBuffer.ByteStream, MSC_BEGINSERVERLISTPART ); NETWORK_WriteByte( &g_MessageBuffer.ByteStream, ulPacketNum ); NETWORK_WriteByte( &g_MessageBuffer.ByteStream, MSC_SERVERBLOCK ); } ulSizeOfPacket += ulServerBlockNetSize; MASTERSERVER_SendServerIPBlockToLauncher ( serverAddress, serverPortList, &g_MessageBuffer.ByteStream ); } NETWORK_WriteByte( &g_MessageBuffer.ByteStream, 0 ); // [BB] Terminate MSC_SERVERBLOCK by sending 0 ports. NETWORK_WriteByte( &g_MessageBuffer.ByteStream, MSC_ENDSERVERLIST ); NETWORK_LaunchPacket( &g_MessageBuffer, AddressFrom ); return; } } } printf( "* Received unknown challenge (%ld) from %s. Ignoring for 10 seconds...\n", lCommand, NETWORK_AddressToString( AddressFrom )); g_floodProtectionIPQueue.addAddress( AddressFrom, g_lCurrentTime, &std::cerr ); }
static void server_rcon_HandleLogin( int iCandidateIndex, const char *pszHash ) { // If there's no slot, the candidate must have timed out, or is hacking. Bye! if ( iCandidateIndex == -1 ) return; // Combine the salt and password, and hash it. FString fsString, fsCorrectHash; fsString.Format( "%s%s", g_Candidates[iCandidateIndex].szSalt, sv_rconpassword.GetGenericRep(CVAR_String).String ); CMD5Checksum::GetMD5( reinterpret_cast<const BYTE *>(fsString.GetChars()), fsString.Len(), fsCorrectHash ); // Compare that to what he sent us. // Printf("Mine: %s\nTheirs: %s\n", fsCorrectHash, pszHash ); NETWORK_ClearBuffer( &g_MessageBuffer ); // [BB] Do not allow the server to let anybody use RCON in case sv_rconpassword is empty. if ( fsCorrectHash.Compare( pszHash ) || ( strlen( sv_rconpassword.GetGenericRep(CVAR_String).String ) == 0 ) ) { // Wrong password. NETWORK_WriteByte( &g_MessageBuffer.ByteStream, SVRC_INVALIDPASSWORD ); NETWORK_LaunchPacket( &g_MessageBuffer, g_Candidates[iCandidateIndex].Address ); // [RC] Note: Be sure to finish any packets before calling Printf(). Otherwise SERVER_RCON_Print will clear your buffer. // To prevent mass password flooding, ignore the IP for a few seconds. g_BadRequestFloodQueue.addAddress( g_Candidates[iCandidateIndex].Address, gametic / 1000 ); Printf( "Failed RCON login from %s. Ignoring IP for 10 seconds...\n", NETWORK_AddressToString( g_Candidates[iCandidateIndex].Address )); } else { // [BB] Since we log when RCON clients disconnect, we should also log when they connect. // Do this before we do anything else so that this message is sent to the new RCON client // with the console history. Printf( "RCON client at %s connected.\n", NETWORK_AddressToString( g_Candidates[iCandidateIndex].Address )); // Correct password. Promote him to an authed client. RCONCLIENT_s Client; Client.Address = g_Candidates[iCandidateIndex].Address; Client.iLastMessageTic = gametic; g_AuthedClients.Push( Client ); NETWORK_ClearBuffer( &g_MessageBuffer ); NETWORK_WriteByte( &g_MessageBuffer.ByteStream, SVRC_LOGGEDIN ); // Tell him some info about the server. NETWORK_WriteByte( &g_MessageBuffer.ByteStream, PROTOCOL_VERSION ); NETWORK_WriteString( &g_MessageBuffer.ByteStream, sv_hostname.GetGenericRep( CVAR_String ).String ); // Send updates. NETWORK_WriteByte( &g_MessageBuffer.ByteStream, NUM_RCON_UPDATES ); for ( int i = 0; i < NUM_RCON_UPDATES; i++ ) server_WriteUpdateInfo( &g_MessageBuffer.ByteStream, i ); // Send the console history. NETWORK_WriteByte( &g_MessageBuffer.ByteStream, g_RecentConsoleLines.size() ); for( std::list<FString>::iterator i = g_RecentConsoleLines.begin(); i != g_RecentConsoleLines.end(); ++i ) NETWORK_WriteString( &g_MessageBuffer.ByteStream, *i ); NETWORK_LaunchPacket( &g_MessageBuffer, g_Candidates[iCandidateIndex].Address ); SERVER_RCON_UpdateInfo( SVRCU_ADMINCOUNT ); } // Remove his temporary slot. g_Candidates.Delete( iCandidateIndex ); }
void SERVER_RCON_ParseMessage( NETADDRESS_s Address, LONG lMessage, BYTESTREAM_s *pByteStream ) { int iIndex = -1; switch ( lMessage ) { case CLRC_BEGINCONNECTION: server_rcon_HandleNewConnection( Address, NETWORK_ReadByte( pByteStream )); break; case CLRC_PASSWORD: server_rcon_HandleLogin( server_rcon_FindCandidate( Address ), NETWORK_ReadString( pByteStream )); break; case CLRC_PONG: iIndex = server_rcon_FindClient( Address ); if ( iIndex != -1 ) g_AuthedClients[iIndex].iLastMessageTic = gametic; break; case CLRC_COMMAND: // Execute the command (if this came from an admin). iIndex = server_rcon_FindClient( Address ); if ( iIndex != -1 ) { const char *szCommand = NETWORK_ReadString( pByteStream ); // [BB] Log the command before adding it. If we don't have a server GUI, the command // is executed immediately and may cause Skulltag to exit before the command is logged. Printf( "-> %s (RCON by %s)\n", szCommand, NETWORK_AddressToString( Address ) ); SERVER_AddCommand( szCommand ); g_AuthedClients[iIndex].iLastMessageTic = gametic; } break; case CLRC_DISCONNECT: iIndex = server_rcon_FindClient( Address ); if ( iIndex != -1 ) { g_AuthedClients.Delete( iIndex ); SERVER_RCON_UpdateInfo( SVRCU_ADMINCOUNT ); Printf( "RCON client at %s disconnected.\n", NETWORK_AddressToString( Address )); } break; case CLRC_TABCOMPLETE: // [TP] RCON client wishes to tab-complete iIndex = server_rcon_FindClient( Address ); if ( iIndex != -1 ) { const char* part = NETWORK_ReadString( pByteStream ); TArray<FString> list = C_GetTabCompletes( part ); NETWORK_ClearBuffer( &g_MessageBuffer ); // [TP] Let's not send too many of these though if ( list.Size() < 50 ) { NETWORK_WriteByte( &g_MessageBuffer.ByteStream, SVRC_TABCOMPLETE ); NETWORK_WriteByte( &g_MessageBuffer.ByteStream, list.Size() ); for ( unsigned i = 0; i < list.Size(); ++i ) NETWORK_WriteString( &g_MessageBuffer.ByteStream, list[i] ); } else { NETWORK_WriteByte( &g_MessageBuffer.ByteStream, SVRC_TOOMANYTABCOMPLETES ); NETWORK_WriteShort( &g_MessageBuffer.ByteStream, list.Size() ); } NETWORK_LaunchPacket( &g_MessageBuffer, g_AuthedClients[iIndex].Address ); } break; } }
//***************************************************************************** // void CALLVOTE_BeginVote( FString Command, FString Parameters, FString Reason, ULONG ulPlayer ) { // Don't allow a vote in the middle of another vote. if ( g_VoteState != VOTESTATE_NOVOTE ) { if ( NETWORK_GetState( ) == NETSTATE_SERVER ) SERVER_PrintfPlayer( PRINT_HIGH, SERVER_GetCurrentClient( ), "Another vote is already underway.\n" ); return; } // Check and make sure all the parameters are valid. if ( callvote_CheckValidity( Command, Parameters ) == false ) return; // Prevent excessive re-voting. if (( NETWORK_GetState( ) == NETSTATE_SERVER ) && callvote_CheckForFlooding( Command, Parameters, ulPlayer ) == false ) return; // Play the announcer sound for this. ANNOUNCER_PlayEntry( cl_announcer, "VoteNow" ); // Create the vote console command. g_VoteCommand = Command; g_VoteCommand += " "; g_VoteCommand += Parameters; g_ulVoteCaller = ulPlayer; g_VoteReason = Reason.Left(25); // Create the record of the vote for flood prevention. { VOTE_s VoteRecord; VoteRecord.fsParameter = Parameters; time_t tNow; time( &tNow ); VoteRecord.tTimeCalled = tNow; VoteRecord.Address = SERVER_GetClient( g_ulVoteCaller )->Address; VoteRecord.ulVoteType = callvote_GetVoteType( Command ); if ( callvote_IsKickVote ( VoteRecord.ulVoteType ) ) VoteRecord.KickAddress = g_KickVoteVictimAddress; g_PreviousVotes.push_back( VoteRecord ); } // Display the message in the console. { FString ReasonBlurb = ( g_VoteReason.Len( )) ? ( ", reason: \"" + g_VoteReason + "\"" ) : ""; if ( NETWORK_GetState( ) == NETSTATE_SERVER ) Printf( "%s\\c- (%s) has called a vote (\"%s\"%s).\n", players[ulPlayer].userinfo.netname, NETWORK_AddressToString( SERVER_GetClient( ulPlayer )->Address ), g_VoteCommand.GetChars(), ReasonBlurb.GetChars() ); else Printf( "%s\\c- has called a vote (\"%s\"%s).\n", players[ulPlayer].userinfo.netname, g_VoteCommand.GetChars(), ReasonBlurb.GetChars() ); } g_VoteState = VOTESTATE_INVOTE; g_ulVoteCountdownTicks = VOTE_COUNTDOWN_TIME * TICRATE; g_ulShowVoteScreenTicks = g_ulVoteCountdownTicks; g_bVoteCancelled = false; // Inform clients about the vote being called. if ( NETWORK_GetState( ) == NETSTATE_SERVER ) SERVERCOMMANDS_CallVote( ulPlayer, Command, Parameters, Reason ); }
//***************************************************************************** // void NETWORK_LaunchPacket( NETBUFFER_s *pBuffer, NETADDRESS_s Address ) { LONG lNumBytes; INT iNumBytesOut; struct sockaddr_in SocketAddress; pBuffer->ulCurrentSize = NETWORK_CalcBufferSize( pBuffer ); // Nothing to do. if ( pBuffer->ulCurrentSize == 0 ) return; // Convert the IP address to a socket address. NETWORK_NetAddressToSocketAddress( Address, SocketAddress ); HUFFMAN_Encode( (unsigned char *)pBuffer->pbData, g_ucHuffmanBuffer, pBuffer->ulCurrentSize, &iNumBytesOut ); lNumBytes = sendto( g_NetworkSocket, (const char*)g_ucHuffmanBuffer, iNumBytesOut, 0, (struct sockaddr *)&SocketAddress, sizeof( SocketAddress )); // If sendto returns -1, there was an error. if ( lNumBytes == -1 ) { #ifdef __WIN32__ INT iError = WSAGetLastError( ); // Wouldblock is silent. if ( iError == WSAEWOULDBLOCK ) return; switch ( iError ) { case WSAEACCES: Printf( "NETWORK_LaunchPacket: Error #%d, WSAEACCES: Permission denied for address: %s\n", iError, NETWORK_AddressToString( Address )); return; case WSAEADDRNOTAVAIL: Printf( "NETWORK_LaunchPacket: Error #%d, WSAEADDRENOTAVAIL: Address %s not available\n", iError, NETWORK_AddressToString( Address )); return; case WSAEHOSTUNREACH: Printf( "NETWORK_LaunchPacket: Error #%d, WSAEHOSTUNREACH: Address %s unreachable\n", iError, NETWORK_AddressToString( Address )); return; default: Printf( "NETWORK_LaunchPacket: Error #%d\n", iError ); return; } #else if ( errno == EWOULDBLOCK ) return; if ( errno == ECONNREFUSED ) return; Printf( "NETWORK_LaunchPacket: %s\n", strerror( errno )); Printf( "NETWORK_LaunchPacket: Address %s\n", NETWORK_AddressToString( Address )); #endif } // Record this for our statistics window. if ( NETWORK_GetState( ) == NETSTATE_SERVER ) SERVER_STATISTIC_AddToOutboundDataTransfer( lNumBytes ); }
//***************************************************************************** // int NETWORK_GetLANPackets( void ) { // [BB] If we know that there is a problem with the socket don't try to use it. if ( g_bLANSocketInvalid ) return 0; LONG lNumBytes; INT iDecodedNumBytes; struct sockaddr_in SocketFrom; INT iSocketFromLength; iSocketFromLength = sizeof( SocketFrom ); #ifdef WIN32 lNumBytes = recvfrom( g_LANSocket, (char *)g_ucHuffmanBuffer, sizeof( g_ucHuffmanBuffer ), 0, (struct sockaddr *)&SocketFrom, &iSocketFromLength ); #else lNumBytes = recvfrom( g_LANSocket, (char *)g_ucHuffmanBuffer, sizeof( g_ucHuffmanBuffer ), 0, (struct sockaddr *)&SocketFrom, (socklen_t *)&iSocketFromLength ); #endif // If the number of bytes returned is -1, an error has occured. if ( lNumBytes == -1 ) { #ifdef __WIN32__ errno = WSAGetLastError( ); if ( errno == WSAEWOULDBLOCK ) return ( false ); // Connection reset by peer. Doesn't mean anything to the server. if ( errno == WSAECONNRESET ) return ( false ); if ( errno == WSAEMSGSIZE ) { Printf( "NETWORK_GetPackets: WARNING! Oversize packet from %s\n", NETWORK_AddressToString( g_AddressFrom )); return ( false ); } Printf( "NETWORK_GetPackets: WARNING!: Error #%d: %s\n", errno, strerror( errno )); return ( false ); #else if ( errno == EWOULDBLOCK ) return ( false ); if ( errno == ECONNREFUSED ) return ( false ); Printf( "NETWORK_GetPackets: WARNING!: Error #%d: %s\n", errno, strerror( errno )); return ( false ); #endif } // No packets or an error, dont process anything. if ( lNumBytes <= 0 ) return ( 0 ); // Record this for our statistics window. if ( NETWORK_GetState( ) == NETSTATE_SERVER ) SERVER_STATISTIC_AddToInboundDataTransfer( lNumBytes ); // If the number of bytes we're receiving exceeds our buffer size, ignore the packet. if ( lNumBytes >= static_cast<LONG>(g_NetworkMessage.ulMaxSize) ) return ( 0 ); // Decode the huffman-encoded message we received. HUFFMAN_Decode( g_ucHuffmanBuffer, (unsigned char *)g_NetworkMessage.pbData, lNumBytes, &iDecodedNumBytes ); g_NetworkMessage.ulCurrentSize = iDecodedNumBytes; g_NetworkMessage.ByteStream.pbStream = g_NetworkMessage.pbData; g_NetworkMessage.ByteStream.pbStreamEnd = g_NetworkMessage.ByteStream.pbStream + g_NetworkMessage.ulCurrentSize; // Store the IP address of the sender. NETWORK_SocketAddressToNetAddress( &SocketFrom, &g_AddressFrom ); return ( g_NetworkMessage.ulCurrentSize ); }
void NETWORK_Construct( USHORT usPort, bool bAllocateLANSocket ) { char szString[128]; ULONG ulArg; USHORT usNewPort; bool bSuccess; // Initialize the Huffman buffer. HUFFMAN_Construct( ); #ifdef __WIN32__ // [BB] Linux doesn't know WSADATA, so this may not be moved outside the ifdef. WSADATA WSAData; if ( WSAStartup( 0x0101, &WSAData )) network_Error( "Winsock initialization failed!\n" ); Printf( "Winsock initialization succeeded!\n" ); #endif ULONG ulInAddr = INADDR_ANY; const char* pszIPAddress = Args->CheckValue( "-useip" ); // [BB] An IP was specfied. Check if it's valid and if it is, try to bind our socket to it. if ( pszIPAddress ) { ULONG requestedIP = inet_addr( pszIPAddress ); if ( requestedIP == INADDR_NONE ) { sprintf( szString, "NETWORK_Construct: %s is not a valid IP address\n", pszIPAddress ); network_Error( szString ); } else ulInAddr = requestedIP; } g_usLocalPort = usPort; // Allocate a socket, and attempt to bind it to the given port. g_NetworkSocket = network_AllocateSocket( ); // [BB] If we can't allocate a socket, sending / receiving net packets won't work. if ( g_NetworkSocket == INVALID_SOCKET ) network_Error( "NETWORK_Construct: Couldn't allocate socket. You will not be able to host or join servers.\n" ); else if ( network_BindSocketToPort( g_NetworkSocket, ulInAddr, g_usLocalPort, false ) == false ) { bSuccess = true; bool bSuccessIP = true; usNewPort = g_usLocalPort; while ( network_BindSocketToPort( g_NetworkSocket, ulInAddr, ++usNewPort, false ) == false ) { // Didn't find an available port. Oh well... if ( usNewPort == g_usLocalPort ) { // [BB] We couldn't use the specified IP, so just try any. if ( ulInAddr != INADDR_ANY ) { ulInAddr = INADDR_ANY; bSuccessIP = false; continue; } bSuccess = false; break; } } if ( bSuccess == false ) { sprintf( szString, "NETWORK_Construct: Couldn't bind socket to port: %d\n", g_usLocalPort ); network_Error( szString ); } else if ( bSuccessIP == false ) { sprintf( szString, "NETWORK_Construct: Couldn't bind socket to IP %s, using the default IP instead:\n", pszIPAddress ); network_Error( szString ); } else { Printf( "NETWORK_Construct: Couldn't bind to %d. Binding to %d instead...\n", g_usLocalPort, usNewPort ); g_usLocalPort = usNewPort; } } ulArg = true; if ( ioctlsocket( g_NetworkSocket, FIONBIO, &ulArg ) == -1 ) printf( "network_AllocateSocket: ioctl FIONBIO: %s", strerror( errno )); // If we're not starting a server, setup a socket to listen for LAN servers. if ( bAllocateLANSocket ) { g_LANSocket = network_AllocateSocket( ); if ( network_BindSocketToPort( g_LANSocket, ulInAddr, DEFAULT_BROADCAST_PORT, true ) == false ) { sprintf( szString, "network_BindSocketToPort: Couldn't bind LAN socket to port: %d. You will not be able to see LAN servers in the browser.", DEFAULT_BROADCAST_PORT ); network_Error( szString ); // [BB] The socket won't work in this case, make sure not to use it. g_bLANSocketInvalid = true; } if ( ioctlsocket( g_LANSocket, FIONBIO, &ulArg ) == -1 ) printf( "network_AllocateSocket: ioctl FIONBIO: %s", strerror( errno )); } // Init our read buffer. // [BB] Vortex Cortex pointed us to the fact that the smallest huffman code is only 3 bits // and it turns into 8 bits when it's decompressed. Thus we need to allocate a buffer that // can hold the biggest possible size we may get after decompressing (aka Huffman decoding) // the incoming UDP packet. NETWORK_InitBuffer( &g_NetworkMessage, ((MAX_UDP_PACKET * 8) / 3 + 1), BUFFERTYPE_READ ); NETWORK_ClearBuffer( &g_NetworkMessage ); // [BB] Get and save our local IP. if ( ( ulInAddr == INADDR_ANY ) || ( pszIPAddress == NULL ) ) g_LocalAddress = NETWORK_GetLocalAddress( ); // [BB] We are using a specified IP, so we don't need to figure out what IP we have, but just use the specified one. else { NETWORK_StringToAddress ( pszIPAddress, &g_LocalAddress ); g_LocalAddress.usPort = htons ( NETWORK_GetLocalPort() ); } // Print out our local IP address. Printf( "IP address %s\n", NETWORK_AddressToString( g_LocalAddress )); // If hosting, update the server GUI. if( NETWORK_GetState() == NETSTATE_SERVER ) SERVERCONSOLE_UpdateIP( g_LocalAddress ); // [BB] Initialize the checksum of the non-map lumps that need to be authenticated when connecting a new player. std::vector<std::string> lumpsToAuthenticate; std::vector<LumpAuthenticationMode> lumpsToAuthenticateMode; lumpsToAuthenticate.push_back( "COLORMAP" ); lumpsToAuthenticateMode.push_back( LAST_LUMP ); lumpsToAuthenticate.push_back( "PLAYPAL" ); lumpsToAuthenticateMode.push_back( LAST_LUMP ); lumpsToAuthenticate.push_back( "HTICDEFS" ); lumpsToAuthenticateMode.push_back( ALL_LUMPS ); lumpsToAuthenticate.push_back( "HEXNDEFS" ); lumpsToAuthenticateMode.push_back( ALL_LUMPS ); lumpsToAuthenticate.push_back( "STRFDEFS" ); lumpsToAuthenticateMode.push_back( ALL_LUMPS ); lumpsToAuthenticate.push_back( "DOOMDEFS" ); lumpsToAuthenticateMode.push_back( ALL_LUMPS ); lumpsToAuthenticate.push_back( "GLDEFS" ); lumpsToAuthenticateMode.push_back( ALL_LUMPS ); lumpsToAuthenticate.push_back( "DECORATE" ); lumpsToAuthenticateMode.push_back( ALL_LUMPS ); lumpsToAuthenticate.push_back( "LOADACS" ); lumpsToAuthenticateMode.push_back( ALL_LUMPS ); lumpsToAuthenticate.push_back( "DEHACKED" ); lumpsToAuthenticateMode.push_back( ALL_LUMPS ); lumpsToAuthenticate.push_back( "GAMEMODE" ); lumpsToAuthenticateMode.push_back( ALL_LUMPS ); FString checksum, longChecksum; bool noProtectedLumpsAutoloaded = true; // [BB] All precompiled ACS libraries need to be authenticated. The only way to find all of them // at this point is to parse all LOADACS lumps. { int lump, lastlump = 0; while ((lump = Wads.FindLump ("LOADACS", &lastlump)) != -1) { FScanner sc(lump); while (sc.GetString()) { NETWORK_AddLumpForAuthentication ( Wads.CheckNumForName (sc.String, ns_acslibrary) ); } } } // [BB] First check the lumps that were marked for authentication while initializing. This // includes for example those lumps included by DECORATE lumps. It's much easier to mark those // lumps while the engine parses the DECORATE code than trying to find all included lumps from // the DECORATE lumps directly. for ( unsigned int i = 0; i < g_LumpNumsToAuthenticate.Size(); ++i ) { if ( !network_GenerateLumpMD5HashAndWarnIfNeeded( g_LumpNumsToAuthenticate[i], Wads.GetLumpFullName (g_LumpNumsToAuthenticate[i]), checksum ) ) noProtectedLumpsAutoloaded = false; longChecksum += checksum; } for ( unsigned int i = 0; i < lumpsToAuthenticate.size(); i++ ) { switch ( lumpsToAuthenticateMode[i] ){ case LAST_LUMP: int lump; lump = Wads.CheckNumForName(lumpsToAuthenticate[i].c_str()); // [BB] Possibly we find the COLORMAP lump only in the colormaps name space. if ( ( lump == -1 ) && ( lumpsToAuthenticate[i].compare ( "COLORMAP" ) == 0 ) ) lump = Wads.CheckNumForName("COLORMAP", ns_colormaps); if ( lump == -1 ) { Printf ( PRINT_BOLD, "Warning: Can't find lump %s for authentication!\n", lumpsToAuthenticate[i].c_str() ); continue; } if ( !network_GenerateLumpMD5HashAndWarnIfNeeded( lump, lumpsToAuthenticate[i].c_str(), checksum ) ) noProtectedLumpsAutoloaded = false; // [BB] To make Doom and Freedoom network compatible, substitue the Freedoom PLAYPAL/COLORMAP hash // by the corresponding Doom hash. // 4804c7f34b5285c334a7913dd98fae16 Doom PLAYPAL hash // 061a4c0f80aa8029f2c1bc12dc2e261e Doom COLORMAP hash // 2e01ae6258f2a0fdad32125537efe1af Freedoom PLAYPAL hash // bb535e66cae508e3833a5d2de974267b Freedoom COLORMAP hash if ( ( stricmp ( lumpsToAuthenticate[i].c_str(), "PLAYPAL" ) == 0 ) && ( stricmp ( checksum.GetChars(), "2e01ae6258f2a0fdad32125537efe1af" ) == 0 ) ) checksum = "4804c7f34b5285c334a7913dd98fae16"; else if ( ( stricmp ( lumpsToAuthenticate[i].c_str(), "COLORMAP" ) == 0 ) && ( stricmp ( checksum.GetChars(), "bb535e66cae508e3833a5d2de974267b" ) == 0 ) ) checksum = "061a4c0f80aa8029f2c1bc12dc2e261e"; longChecksum += checksum; break; case ALL_LUMPS: int workingLump, lastLump; lastLump = 0; while ((workingLump = Wads.FindLump(lumpsToAuthenticate[i].c_str(), &lastLump)) != -1) { if ( !network_GenerateLumpMD5HashAndWarnIfNeeded( workingLump, lumpsToAuthenticate[i].c_str(), checksum ) ) noProtectedLumpsAutoloaded = false; longChecksum += checksum; } break; } } CMD5Checksum::GetMD5( reinterpret_cast<const BYTE *>(longChecksum.GetChars()), longChecksum.Len(), g_lumpsAuthenticationChecksum ); // [BB] Warn the user about problematic auto-loaded files. if ( noProtectedLumpsAutoloaded == false ) { Printf ( PRINT_BOLD, "Warning: Above auto-loaded files contain protected lumps.\n" ); if ( Args->CheckParm( "-host" ) ) Printf ( PRINT_BOLD, "Clients without these files can't connect to this server.\n" ); else Printf ( PRINT_BOLD, "You can't connect to servers without these files.\n" ); } // [BB] Initialize the actor network class indices. for ( unsigned int i = 0; i < PClass::m_Types.Size(); i++ ) { PClass* cls = PClass::m_Types[i]; if ( (cls->IsDescendantOf(RUNTIME_CLASS(AActor))) // [BB] The server only binaries don't know DynamicLight and derived classes. && !(cls->IsDescendantOf(PClass::FindClass("DynamicLight"))) ) cls->ActorNetworkIndex = 1 + g_ActorNetworkIndexClassPointerMap.Push ( cls ); else cls->ActorNetworkIndex = 0; } // [RC/BB] Init the list of PWADs. network_InitPWADList( ); // Call NETWORK_Destruct() when Skulltag closes. atterm( NETWORK_Destruct ); Printf( "UDP Initialized.\n" ); }
DWORD WINAPI main_Loop( LPVOID ) { char szBuffer[128]; while ( 1 ) { while ( NETWORK_GetPackets( )) { // Set up our byte stream. BYTESTREAM_s *pByteStream = &NETWORK_GetNetworkMessageBuffer( )->ByteStream; pByteStream->pbStream = NETWORK_GetNetworkMessageBuffer( )->pbData; pByteStream->pbStreamEnd = pByteStream->pbStream + NETWORK_GetNetworkMessageBuffer( )->ulCurrentSize; // Parse the packet, but only if it came from the server we're trying to reach. Ignore everything else. if ( g_State > STATE_WAITING && NETWORK_CompareAddress( NETWORK_GetFromAddress( ), g_ServerAddress, false )) main_ParseCommands( pByteStream ); } time_t tNow = time( 0 ); // Refresh the "connect" button. if ( g_tLastIncorrectLogin > 0 && ( timeGetTime( ) - g_lLastCountdownTime > 250 )) { g_lLastCountdownTime = timeGetTime( ); if ( tNow - g_tLastIncorrectLogin < BAD_QUERY_IGNORE_TIME ) sprintf( szBuffer, "Connect [%d]", BAD_QUERY_IGNORE_TIME - ( tNow - g_tLastIncorrectLogin )); else strcpy( szBuffer, "Connect" ); SetDlgItemText( g_hDlg, IDOK, szBuffer ); EnableWindow( GetDlgItem( g_hDlg, IDOK ), ( tNow - g_tLastIncorrectLogin >= BAD_QUERY_IGNORE_TIME ) && ( g_State == STATE_WAITING )); } // If we've been waiting for a while without response, try again, then error out. if ( g_State == STATE_CONNECTING && ( tNow - g_tLastSentCommand ) > 2 ) { if ( g_iRetries < 3 ) { main_AttemptConnection(); g_iRetries++; sprintf( szBuffer, "Retrying (%d) to reach %s...", g_iRetries, NETWORK_AddressToString( g_ServerAddress )); main_UpdateStatusbar( szBuffer ); main_UpdateTrayTooltip( szBuffer ); } else { main_ShowMessage( "That address doesn't seem valid; there was no response.", MB_ICONEXCLAMATION ); main_SetState( STATE_WAITING ); main_EnableConnectionButtons( TRUE ); g_iRetries = 0; } } // If we haven't sent a message recently, let the server know we're still here. if ( g_State == STATE_CONNECTED && ( tNow - g_tLastSentCommand ) > RCON_CLIENT_TIMEOUT_TIME / 4 ) { NETWORK_ClearBuffer( &g_MessageBuffer ); NETWORK_WriteByte( &g_MessageBuffer.ByteStream, CLRC_PONG ); NETWORK_LaunchPacket( &g_MessageBuffer, g_ServerAddress ); time( &g_tLastSentCommand ); } Sleep( 200 ); } }
//***************************************************************************** // bool CALLVOTE_VoteNo( ULONG ulPlayer ) { ULONG ulIdx; ULONG ulNumYes; ULONG ulNumNo; // Don't allow the vote unless we're in the middle of a vote. if ( g_VoteState != VOTESTATE_INVOTE ) return ( false ); // [RC] If this is our vote, hide the vote screen soon. if (( NETWORK_GetState( ) == NETSTATE_CLIENT ) && ( static_cast<LONG>(ulPlayer) == consoleplayer ) ) g_ulShowVoteScreenTicks = 1 * TICRATE; // [RC] Vote callers can cancel their votes by voting "no". if ( ulPlayer == g_ulVoteCaller && ( NETWORK_GetState( ) == NETSTATE_SERVER )) { // [BB] If a player canceled his own vote, don't prevent others from making this type of vote again. g_PreviousVotes.back( ).ulVoteType = NUM_VOTECMDS; SERVER_Printf( PRINT_HIGH, "Vote caller cancelled the vote.\n" ); g_bVoteCancelled = true; g_bVotePassed = false; callvote_EndVote( ); return ( true ); } // Also, don't allow spectator votes if the server has them disabled. if (( NETWORK_GetState( ) == NETSTATE_SERVER ) && ( sv_nocallvote == 2 && players[ulPlayer].bSpectating )) { SERVER_PrintfPlayer( PRINT_HIGH, ulPlayer, "This server requires spectators to join the game to vote.\n" ); return false; } // If this player has already voted, ignore his vote. for ( ulIdx = 0; ulIdx < ( MAXPLAYERS / 2 ) + 1; ulIdx++ ) { if ( g_ulPlayersWhoVotedYes[ulIdx] == ulPlayer ) return ( false ); if ( g_ulPlayersWhoVotedNo[ulIdx] == ulPlayer ) return ( false ); // If this person matches the IP of a person who already voted, don't let him vote. if ( NETWORK_GetState( ) == NETSTATE_SERVER ) { if ( g_ulPlayersWhoVotedYes[ulIdx] < MAXPLAYERS ) { if ( NETWORK_CompareAddress( SERVER_GetClient( g_ulPlayersWhoVotedYes[ulIdx] )->Address, SERVER_GetClient( ulPlayer )->Address, true )) return ( false ); } if ( g_ulPlayersWhoVotedNo[ulIdx] < MAXPLAYERS ) { if ( NETWORK_CompareAddress( SERVER_GetClient( g_ulPlayersWhoVotedNo[ulIdx] )->Address, SERVER_GetClient( ulPlayer )->Address, true )) return ( false ); } } } // Add this player's vote. for ( ulIdx = 0; ulIdx < ( MAXPLAYERS / 2 ) + 1; ulIdx++ ) { if ( g_ulPlayersWhoVotedNo[ulIdx] == MAXPLAYERS ) { g_ulPlayersWhoVotedNo[ulIdx] = ulPlayer; break; } } // Display the message in the console. if ( NETWORK_GetState( ) == NETSTATE_SERVER ) Printf( "%s\\c- (%s) votes \"no\".\n", players[ulPlayer].userinfo.netname, NETWORK_AddressToString( SERVER_GetClient( ulPlayer )->Address )); else Printf( "%s\\c- votes \"no\".\n", players[ulPlayer].userinfo.netname ); // Nothing more to do here for clients. if (( NETWORK_GetState( ) == NETSTATE_CLIENT ) || ( CLIENTDEMO_IsPlaying( ))) { return ( true ); } SERVERCOMMANDS_PlayerVote( ulPlayer, false ); ulNumYes = callvote_CountPlayersWhoVotedYes( ); ulNumNo = callvote_CountPlayersWhoVotedNo( ); // If more than half of the total eligible voters have voted, we must have a majority! if ( MAX( ulNumYes, ulNumNo ) > ( CALLVOTE_CountNumEligibleVoters( ) / 2 )) { g_bVotePassed = ( ulNumYes > ulNumNo ); callvote_EndVote( ); } return ( true ); }