static void main_Disconnect( ) { NETWORK_ClearBuffer( &g_MessageBuffer ); NETWORK_WriteByte( &g_MessageBuffer.ByteStream, CLRC_DISCONNECT ); NETWORK_LaunchPacket( &g_MessageBuffer, g_ServerAddress ); main_SetState( STATE_WAITING ); }
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 SERVER_RCON_Print( const char *pszString ) { for ( unsigned int i = 0; i < g_AuthedClients.Size( ); i++ ) { NETWORK_ClearBuffer( &g_MessageBuffer ); NETWORK_WriteByte( &g_MessageBuffer.ByteStream, SVRC_MESSAGE ); NETWORK_WriteString( &g_MessageBuffer.ByteStream, pszString ); NETWORK_LaunchPacket( &g_MessageBuffer, g_AuthedClients[i].Address ); } //========================================== // Add this to the cache of recent messages. //========================================== if ( g_RecentConsoleLines.size() >= 32 ) g_RecentConsoleLines.pop_front(); FString fsLogged = pszString; time_t tNow = time(0); struct tm *pTimeInfo = localtime( &tNow ); if ( pTimeInfo->tm_hour < 12 ) fsLogged.Format( "[%02d:%02d:%02d am] %s ", ( pTimeInfo->tm_hour == 0 ) ? 12 : pTimeInfo->tm_hour, pTimeInfo->tm_min, pTimeInfo->tm_sec, pszString ); else fsLogged.Format( "[%02d:%02d:%02d pm] %s", ( pTimeInfo->tm_hour == 12 ) ? 12 : pTimeInfo->tm_hour % 12, pTimeInfo->tm_min, pTimeInfo->tm_sec, pszString ); g_RecentConsoleLines.push_back( fsLogged ); }
//***************************************************************************** // void SERVER_RCON_Construct( ) { NETWORK_InitBuffer( &g_MessageBuffer, MAX_UDP_PACKET, BUFFERTYPE_WRITE ); NETWORK_ClearBuffer( &g_MessageBuffer ); // Call SERVER_RCON_Destruct() when Skulltag closes. atterm( SERVER_RCON_Destruct ); }
void startPacket ( ) { NETWORK_ClearBuffer( &_netBuffer ); NETWORK_WriteByte( &_netBuffer.ByteStream, MASTER_SERVER_BANLISTPART ); NETWORK_WriteString( &_netBuffer.ByteStream, _destServer.MasterBanlistVerificationString.c_str() ); NETWORK_WriteByte( &_netBuffer.ByteStream, _ulPacketNum ); _ulSizeOfPacket = 2 + _destServer.MasterBanlistVerificationString.length(); ++_ulPacketNum; }
//***************************************************************************** // void MASTERSERVER_RequestServerVerification( const SERVER_s &Server ) { NETWORK_ClearBuffer( &g_MessageBuffer ); NETWORK_WriteByte( &g_MessageBuffer.ByteStream, MASTER_SERVER_VERIFICATION ); NETWORK_WriteString( &g_MessageBuffer.ByteStream, Server.MasterBanlistVerificationString.c_str() ); NETWORK_WriteLong( &g_MessageBuffer.ByteStream, Server.ServerVerificationInt ); NETWORK_LaunchPacket( &g_MessageBuffer, Server.Address ); }
//***************************************************************************** // void SERVER_AUTH_Negotiate ( const char *Username, const unsigned int ClientSessionID ) { NETWORK_ClearBuffer( &g_AuthServerBuffer ); NETWORK_WriteLong( &g_AuthServerBuffer.ByteStream, SERVER_AUTH_NEGOTIATE ); NETWORK_WriteByte( &g_AuthServerBuffer.ByteStream, AUTH_PROTOCOL_VERSION ); NETWORK_WriteLong( &g_AuthServerBuffer.ByteStream, ClientSessionID); NETWORK_WriteString( &g_AuthServerBuffer.ByteStream, Username ); NETWORK_LaunchPacket( &g_AuthServerBuffer, g_AuthServerAddress ); }
BanlistPacketSender ( const SERVER_s &DestServer ) : _ulMaxPacketSize ( 1024 ), _ulPacketNum ( 0 ), _ulSizeOfPacket ( 0 ), _destServer ( DestServer ) { NETWORK_InitBuffer( &_netBuffer, MAX_UDP_PACKET, BUFFERTYPE_WRITE ); NETWORK_ClearBuffer( &_netBuffer ); }
void NETWORK_AUTH_Construct( void ) { NETWORK_InitBuffer( &g_AuthServerBuffer, MAX_UDP_PACKET, BUFFERTYPE_WRITE ); NETWORK_ClearBuffer( &g_AuthServerBuffer ); g_AuthServerAddress = NETWORK_AUTH_GetServerAddress(); atterm( NETWORK_AUTH_Destruct ); }
//***************************************************************************** // void SERVER_AUTH_SRPMessage ( const int MagicNumber, const int SessionID, const TArray<unsigned char> &Bytes ) { NETWORK_ClearBuffer( &g_AuthServerBuffer ); NETWORK_WriteLong( &g_AuthServerBuffer.ByteStream, MagicNumber ); NETWORK_WriteLong( &g_AuthServerBuffer.ByteStream, SessionID ); NETWORK_WriteShort( &g_AuthServerBuffer.ByteStream, Bytes.Size() ); for ( unsigned int i = 0; i < Bytes.Size(); ++i ) NETWORK_WriteByte( &g_AuthServerBuffer.ByteStream, Bytes[i] ); NETWORK_LaunchPacket( &g_AuthServerBuffer, g_AuthServerAddress ); }
//***************************************************************************** // void ZANDRONUM_QueryMasterServer( void ) { // Clear out the buffer, and write out launcher challenge. NETWORK_ClearBuffer( &g_MessageBuffer ); NETWORK_WriteLong( &g_MessageBuffer.ByteStream, LAUNCHER_MASTER_CHALLENGE ); NETWORK_WriteShort( &g_MessageBuffer.ByteStream, MASTER_SERVER_VERSION ); // Send the master server our packet. NETWORK_LaunchPacket( &g_MessageBuffer, g_AddressMasterServer, true ); }
//***************************************************************************** // void ZANDRONUM_QueryServer( SERVER_s *pServer ) { // Clear out the buffer, and write out launcher challenge. NETWORK_ClearBuffer( &g_MessageBuffer ); NETWORK_WriteLong( &g_MessageBuffer.ByteStream, LAUNCHER_SERVER_CHALLENGE ); NETWORK_WriteLong( &g_MessageBuffer.ByteStream, SQF_NUMPLAYERS|SQF_PLAYERDATA|SQF_GAMETYPE ); NETWORK_WriteLong( &g_MessageBuffer.ByteStream, 0 ); // Send the server our packet. NETWORK_LaunchPacket( &g_MessageBuffer, pServer->Address, true ); }
void ZANDRONUM_Construct( void ) { // Setup our message buffer. NETWORK_InitBuffer( &g_MessageBuffer, 8192, BUFFERTYPE_WRITE ); NETWORK_ClearBuffer( &g_MessageBuffer ); NETWORK_StringToAddress( "master.zandronum.com", &g_AddressMasterServer ); g_AddressMasterServer.usPort = htons( DEFAULT_MASTER_PORT ); // Call ZANDRONUM_Destruct when the program terminates. atexit( ZANDRONUM_Destruct ); }
void SERVER_RCON_UpdateInfo( int iUpdateType ) { int iNumPlayers = (int) SERVER_CountPlayers( true ); for ( unsigned int i = 0; i < g_AuthedClients.Size( ); i++ ) { NETWORK_ClearBuffer( &g_MessageBuffer ); NETWORK_WriteByte( &g_MessageBuffer.ByteStream, SVRC_UPDATE ); server_WriteUpdateInfo( &g_MessageBuffer.ByteStream, iUpdateType ); NETWORK_LaunchPacket( &g_MessageBuffer, g_AuthedClients[i].Address ); } }
static void main_SendPassword( const char *pszSalt ) { char szString[512]; main_UpdateStatusbar( "Authenticating..." ); FString fsString, fsHash; fsString.Format( "%s%s", pszSalt, g_szPassword ); CMD5Checksum::GetMD5( reinterpret_cast<const BYTE *>(fsString.GetChars()), fsString.Len(), fsHash ); NETWORK_ClearBuffer( &g_MessageBuffer ); NETWORK_WriteByte( &g_MessageBuffer.ByteStream, CLRC_PASSWORD ); NETWORK_WriteString( &g_MessageBuffer.ByteStream, fsHash.GetChars() ); NETWORK_LaunchPacket( &g_MessageBuffer, g_ServerAddress ); time( &g_tLastSentCommand ); }
//***************************************************************************** // void SERVER_MASTER_Tick( void ) { UCVarValue Val; while (( g_lStoredQueryIPHead != g_lStoredQueryIPTail ) && ( gametic >= g_StoredQueryIPs[g_lStoredQueryIPHead].lNextAllowedGametic )) { g_lStoredQueryIPHead++; g_lStoredQueryIPHead = g_lStoredQueryIPHead % MAX_STORED_QUERY_IPS; } // Send an update to the master server every 30 seconds. if ( gametic % ( TICRATE * 30 )) return; // User doesn't wish to update the master server. if ( sv_updatemaster == false ) return; NETWORK_ClearBuffer( &g_MasterServerBuffer ); Val = sv_masterip.GetGenericRep( CVAR_String ); NETWORK_StringToAddress( Val.String, &g_AddressMasterServer ); I_SetPort( g_AddressMasterServer, g_lMasterPort ); // Write to our packet a challenge to the master server. Val = sv_masteroverrideip.GetGenericRep( CVAR_String ); if ( Val.String[0] == '\0' ) NETWORK_WriteLong( &g_MasterServerBuffer, SERVER_MASTER_CHALLENGE ); else { netadr_t OverrideIP; NETWORK_WriteLong( &g_MasterServerBuffer, SERVER_MASTER_CHALLENGE_OVERRIDE ); NETWORK_StringToAddress( Val.String, &OverrideIP ); NETWORK_WriteByte( &g_MasterServerBuffer, OverrideIP.ip[0] ); NETWORK_WriteByte( &g_MasterServerBuffer, OverrideIP.ip[1] ); NETWORK_WriteByte( &g_MasterServerBuffer, OverrideIP.ip[2] ); NETWORK_WriteByte( &g_MasterServerBuffer, OverrideIP.ip[3] ); NETWORK_WriteShort( &g_MasterServerBuffer, NETWORK_GetLocalPort( )); } // Send the master server our packet. // NETWORK_LaunchPacket( &g_MasterServerBuffer, g_AddressMasterServer, true ); NETWORK_LaunchPacket( &g_MasterServerBuffer, g_AddressMasterServer ); }
//***************************************************************************** // 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 )); }
static void server_rcon_HandleNewConnection( NETADDRESS_s Address, int iProtocolVersion ) { // Banned client? Notify him, ignore him, and get out of here. if ( SERVERBAN_IsIPBanned( Address )) { NETWORK_WriteByte( &g_MessageBuffer.ByteStream, SVRC_BANNED ); NETWORK_LaunchPacket( &g_MessageBuffer, Address ); g_BadRequestFloodQueue.addAddress( Address, gametic / 1000 ); return; } // Old protocol version? Notify, ignore, and quit. if ( iProtocolVersion < MIN_PROTOCOL_VERSION ) { NETWORK_WriteByte( &g_MessageBuffer.ByteStream, SVRC_OLDPROTOCOL ); NETWORK_WriteByte( &g_MessageBuffer.ByteStream, PROTOCOL_VERSION ); NETWORK_WriteString( &g_MessageBuffer.ByteStream, DOTVERSIONSTR ); NETWORK_LaunchPacket( &g_MessageBuffer, Address ); g_BadRequestFloodQueue.addAddress( Address, gametic / 1000 ); return; } // Check if there's already a user at this address. Remove him if so (must be reconnecting). // This ensures that each address never has more than one entry (since this is the only function that gives out new candidate slots). int iIndex = server_rcon_FindCandidate( Address ); if ( iIndex != -1 ) g_Candidates.Delete( iIndex ); iIndex = server_rcon_FindClient( Address ); if ( iIndex != -1 ) g_AuthedClients.Delete( iIndex ); // Create a slot for him, and request his password. RCONCANDIDATE_s Candidate; Candidate.iLastMessageTic = gametic; Candidate.Address = Address; server_rcon_CreateSalt( Candidate.szSalt ); g_Candidates.Push( Candidate ); NETWORK_ClearBuffer( &g_MessageBuffer ); NETWORK_WriteByte( &g_MessageBuffer.ByteStream, SVRC_SALT ); NETWORK_WriteString( &g_MessageBuffer.ByteStream, Candidate.szSalt ); NETWORK_LaunchPacket( &g_MessageBuffer, Address ); }
void SERVER_MASTER_Construct( void ) { char *pszPort; // Setup our message buffer. NETWORK_InitBuffer( &g_MasterServerBuffer, MAX_UDP_PACKET ); NETWORK_ClearBuffer( &g_MasterServerBuffer ); // Allow the user to specify which port the master server is on. pszPort = Args.CheckValue( "-masterport" ); if ( pszPort ) { g_lMasterPort = atoi( pszPort ); Printf( PRINT_HIGH, "Alternate master server port: %d.\n", g_lMasterPort ); } else g_lMasterPort = DEFAULT_MASTER_PORT; g_lStoredQueryIPHead = 0; g_lStoredQueryIPTail = 0; }
//***************************************************************************** // void SERVER_MASTER_SendServerInfo( netadr_t Address, ULONG ulFlags, ULONG ulTime ) { UCVarValue Val; char szAddress[4][4]; ULONG ulIdx; ULONG ulBits; ULONG ulNumPWADs; // Let's just use the master server buffer! It gets cleared again when we need it anyway! NETWORK_ClearBuffer( &g_MasterServerBuffer ); // First, check to see if we've been queried by this address recently. if ( g_lStoredQueryIPHead != g_lStoredQueryIPTail ) { ulIdx = g_lStoredQueryIPHead; while ( ulIdx != (ULONG)g_lStoredQueryIPTail ) { // Check to see if this IP exists in our stored query IP list. If it does, then // ignore it, since it queried us less than 10 seconds ago. if ( NETWORK_CompareAddress( Address, g_StoredQueryIPs[ulIdx].Address, true )) { // Write our header. NETWORK_WriteLong( &g_MasterServerBuffer, SERVER_LAUNCHER_IGNORING ); // Send the time the launcher sent to us. NETWORK_WriteLong( &g_MasterServerBuffer, ulTime ); // Send the packet. // NETWORK_LaunchPacket( &g_MasterServerBuffer, Address, true ); NETWORK_LaunchPacket( &g_MasterServerBuffer, Address ); if ( sv_showlauncherqueries ) Printf( "Ignored IP launcher challenge.\n" ); // Nothing more to do here. return; } ulIdx++; ulIdx = ulIdx % MAX_STORED_QUERY_IPS; } } // Now, check to see if this IP has been banend from this server. itoa( Address.ip[0], szAddress[0], 10 ); itoa( Address.ip[1], szAddress[1], 10 ); itoa( Address.ip[2], szAddress[2], 10 ); itoa( Address.ip[3], szAddress[3], 10 ); if (( sv_enforcebans ) && ( SERVERBAN_IsIPBanned( szAddress[0], szAddress[1], szAddress[2], szAddress[3] ))) { // Write our header. NETWORK_WriteLong( &g_MasterServerBuffer, SERVER_LAUNCHER_BANNED ); // Send the time the launcher sent to us. NETWORK_WriteLong( &g_MasterServerBuffer, ulTime ); // Send the packet. NETWORK_LaunchPacket( &g_MasterServerBuffer, Address ); if ( sv_showlauncherqueries ) Printf( "Denied BANNED IP launcher challenge.\n" ); // Nothing more to do here. return; } // This IP didn't exist in the list. and it wasn't banned. // So, add it, and keep it there for 10 seconds. g_StoredQueryIPs[g_lStoredQueryIPTail].Address = Address; g_StoredQueryIPs[g_lStoredQueryIPTail].lNextAllowedGametic = gametic + ( TICRATE * ( sv_queryignoretime )); g_lStoredQueryIPTail++; g_lStoredQueryIPTail = g_lStoredQueryIPTail % MAX_STORED_QUERY_IPS; if ( g_lStoredQueryIPTail == g_lStoredQueryIPHead ) Printf( "SERVER_MASTER_SendServerInfo: WARNING! g_lStoredQueryIPTail == g_lStoredQueryIPHead\n" ); // Write our header. NETWORK_WriteLong( &g_MasterServerBuffer, SERVER_LAUNCHER_CHALLENGE ); // Send the time the launcher sent to us. NETWORK_WriteLong( &g_MasterServerBuffer, ulTime ); // Send our version. NETWORK_WriteString( &g_MasterServerBuffer, DOTVERSIONSTR ); // Send the information about the data that will be sent. ulBits = ulFlags; // If the launcher desires to know the team damage, but we're not in a game mode where // team damage applies, then don't send back team damage information. if (( teamplay || teamgame || teamlms || teampossession || teamcoop || (( deathmatch == false ) && ( teamgame == false ))) == false ) { if ( ulBits & SQF_TEAMDAMAGE ) ulBits &= ~SQF_TEAMDAMAGE; } // If the launcher desires to know the team score, but we're not in a game mode where // teams have scores, then don't send back team score information. if (( teamplay || teamgame || teamlms || teampossession || teamcoop ) == false ) { if ( ulBits & SQF_TEAMSCORES ) ulBits &= ~SQF_TEAMSCORES; } // If the launcher wants to know player data, then we have to tell them how many players // are in the server. if ( ulBits & SQF_PLAYERDATA ) ulBits |= SQF_NUMPLAYERS; NETWORK_WriteLong( &g_MasterServerBuffer, ulBits ); // Send the server name. if ( ulBits & SQF_NAME ) { Val = sv_hostname.GetGenericRep( CVAR_String ); NETWORK_WriteString( &g_MasterServerBuffer, Val.String ); } // Send the website URL. if ( ulBits & SQF_URL ) { Val = sv_website.GetGenericRep( CVAR_String ); NETWORK_WriteString( &g_MasterServerBuffer, Val.String ); } // Send the host's e-mail address. if ( ulBits & SQF_EMAIL ) { Val = sv_hostemail.GetGenericRep( CVAR_String ); NETWORK_WriteString( &g_MasterServerBuffer, Val.String ); } if ( ulBits & SQF_MAPNAME ) NETWORK_WriteString( &g_MasterServerBuffer, level.mapname ); if ( ulBits & SQF_MAXCLIENTS ) NETWORK_WriteByte( &g_MasterServerBuffer, sv_maxclients ); if ( ulBits & SQF_MAXPLAYERS ) NETWORK_WriteByte( &g_MasterServerBuffer, sv_maxplayers ); // Send out the PWAD information. if ( ulBits & SQF_PWADS ) { ulNumPWADs = 0; for ( ulIdx = 0; Wads.GetWadName( ulIdx ) != NULL; ulIdx++ ) { // Skip the IWAD file index, skulltag.wad/pk3, and files that were automatically // loaded from subdirectories (such as skin files). if (( ulIdx == FWadCollection::IWAD_FILENUM ) || ( stricmp( Wads.GetWadName( ulIdx ), "scoredoomst.wad" ) == 0 ) || ( stricmp( Wads.GetWadName( ulIdx ), "scoredoomst.pk3" ) == 0 ) || ( Wads.GetLoadedAutomatically( ulIdx ))) { continue; } ulNumPWADs++; } NETWORK_WriteByte( &g_MasterServerBuffer, ulNumPWADs ); for ( ulIdx = 0; Wads.GetWadName( ulIdx ) != NULL; ulIdx++ ) { // Skip the IWAD file index, skulltag.wad/pk3, and files that were automatically // loaded from subdirectories (such as skin files). if (( ulIdx == FWadCollection::IWAD_FILENUM ) || ( stricmp( Wads.GetWadName( ulIdx ), "scoredoomst.wad" ) == 0 ) || ( stricmp( Wads.GetWadName( ulIdx ), "scoredoomst.pk3" ) == 0 ) || ( Wads.GetLoadedAutomatically( ulIdx ))) { continue; } NETWORK_WriteString( &g_MasterServerBuffer, (char *)Wads.GetWadName( ulIdx )); } } if ( ulBits & SQF_GAMETYPE ) { NETWORK_WriteByte( &g_MasterServerBuffer, GAME_GetGameType( )); NETWORK_WriteByte( &g_MasterServerBuffer, instagib ); NETWORK_WriteByte( &g_MasterServerBuffer, buckshot ); } if ( ulBits & SQF_GAMENAME ) NETWORK_WriteString( &g_MasterServerBuffer, SERVER_MASTER_GetGameName( )); if ( ulBits & SQF_IWAD ) NETWORK_WriteString( &g_MasterServerBuffer, (char *)Wads.GetWadName( FWadCollection::IWAD_FILENUM )); if ( ulBits & SQF_FORCEPASSWORD ) NETWORK_WriteByte( &g_MasterServerBuffer, sv_forcepassword ); if ( ulBits & SQF_FORCEJOINPASSWORD ) NETWORK_WriteByte( &g_MasterServerBuffer, sv_forcejoinpassword ); if ( ulBits & SQF_GAMESKILL ) NETWORK_WriteByte( &g_MasterServerBuffer, gameskill ); if ( ulBits & SQF_BOTSKILL ) NETWORK_WriteByte( &g_MasterServerBuffer, botskill ); if ( ulBits & SQF_DMFLAGS ) { NETWORK_WriteLong( &g_MasterServerBuffer, dmflags ); NETWORK_WriteLong( &g_MasterServerBuffer, dmflags2 ); NETWORK_WriteLong( &g_MasterServerBuffer, compatflags ); } if ( ulBits & SQF_LIMITS ) { NETWORK_WriteShort( &g_MasterServerBuffer, fraglimit ); NETWORK_WriteShort( &g_MasterServerBuffer, (SHORT)timelimit ); if ( timelimit ) { LONG lTimeLeft; lTimeLeft = (LONG)( timelimit - ( level.totaltime / ( TICRATE * 60 ))); //ghk if ( lTimeLeft < 0 ) lTimeLeft = 0; NETWORK_WriteShort( &g_MasterServerBuffer, lTimeLeft ); } NETWORK_WriteShort( &g_MasterServerBuffer, duellimit ); NETWORK_WriteLong( &g_MasterServerBuffer, pointlimit ); //ghk writelong NETWORK_WriteShort( &g_MasterServerBuffer, winlimit ); } // Send the team damage scale. if ( teamplay || teamgame || teamlms || teampossession || teamcoop || (( deathmatch == false ) && ( teamgame == false ))) { if ( ulBits & SQF_TEAMDAMAGE ) NETWORK_WriteFloat( &g_MasterServerBuffer, teamdamage ); } // Send the team scores. if ( teamplay || teamgame || teamlms || teampossession || teamcoop ) { if ( ulBits & SQF_TEAMSCORES ) { for ( ulIdx = 0; ulIdx < NUM_TEAMS; ulIdx++ ) { if ( teamplay ) NETWORK_WriteShort( &g_MasterServerBuffer, TEAM_GetFragCount( ulIdx )); else if ( teamlms ) NETWORK_WriteShort( &g_MasterServerBuffer, TEAM_GetWinCount( ulIdx )); else NETWORK_WriteShort( &g_MasterServerBuffer, TEAM_GetScore( ulIdx )); //ghk writelong? } } } if ( ulBits & SQF_NUMPLAYERS ) NETWORK_WriteByte( &g_MasterServerBuffer, SERVER_CalcNumPlayers( )); if ( ulBits & SQF_PLAYERDATA ) { for ( ulIdx = 0; ulIdx < MAXPLAYERS; ulIdx++ ) { if ( playeringame[ulIdx] == false ) continue; NETWORK_WriteString( &g_MasterServerBuffer, players[ulIdx].userinfo.netname ); if ( teamgame || possession || teampossession || cooperative || teamcoop ) NETWORK_WriteLong( &g_MasterServerBuffer, players[ulIdx].lPointCount );//ghk writelong else if ( deathmatch ) NETWORK_WriteLong( &g_MasterServerBuffer, players[ulIdx].fragcount ); //ghk writelong else NETWORK_WriteLong( &g_MasterServerBuffer, players[ulIdx].killcount ); //ghk writelong NETWORK_WriteShort( &g_MasterServerBuffer, players[ulIdx].ulPing ); NETWORK_WriteByte( &g_MasterServerBuffer, PLAYER_IsTrueSpectator( &players[ulIdx] )); NETWORK_WriteByte( &g_MasterServerBuffer, players[ulIdx].bIsBot ); if ( teamplay || teamgame || teamlms || teampossession || teamcoop ) { if ( players[ulIdx].bOnTeam == false ) NETWORK_WriteByte( &g_MasterServerBuffer, 255 ); else NETWORK_WriteByte( &g_MasterServerBuffer, players[ulIdx].ulTeam ); } NETWORK_WriteByte( &g_MasterServerBuffer, players[ulIdx].ulTime / ( TICRATE * 60 )); } } // NETWORK_LaunchPacket( &g_MasterServerBuffer, Address, true ); NETWORK_LaunchPacket( &g_MasterServerBuffer, Address ); }
//***************************************************************************** // 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_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 ); }
BOOL CALLBACK main_RCONDialogCallback( HWND hDlg, UINT Message, WPARAM wParam, LPARAM lParam ) { char szBuffer[128]; switch ( Message ) { case WM_CTLCOLORSTATIC: switch ( GetDlgCtrlID( (HWND) lParam )) { // Paint these two labels (and the disconnct button's background) white. case IDCANCEL: case IDC_STATUS: case IDC_SERVERSUBINFO: return (LRESULT) g_hWhiteBrush; // Ignore everything else. default: return NULL; } break; case WM_PAINT: { // Paint the top of the form white. PAINTSTRUCT Ps; RECT r; r.left = 0; r.top = 0; r.bottom = 48; r.right = 800; main_PaintRectangle( BeginPaint(hDlg, &Ps), &r, RGB(255, 255, 255)); } break; case WM_INITDIALOG: // Hide the old dialog, and take its place. ShowWindow( g_hDlg, SW_HIDE ); g_hDlg = hDlg; SendDlgItemMessage( hDlg, IDC_CONSOLEBOX, EM_SETLIMITTEXT, 4096, 0 ); SendDlgItemMessage( hDlg, IDC_INPUTBOX, EM_SETLIMITTEXT, 256, 0 ); SetWindowText( hDlg, g_szHostname ); main_SetState( STATE_CONNECTED ); Printf( "\nMap: %s\n", g_szMapname ); // Fill the console with the received history. sprintf( szBuffer, "Connected to \"%s\".", g_szHostname ); SetDlgItemText( hDlg, IDC_CONSOLEBOX, szBuffer ); SetDlgItemText( hDlg, IDC_STATUS, szBuffer ); main_UpdateTrayTooltip( szBuffer ); Printf_NoTimestamp( "\n" ); for( std::list<FString>::iterator i = g_RecentConsoleHistory.begin(); i != g_RecentConsoleHistory.end(); ++i ) Printf_NoTimestamp( "%s", *i ); g_RecentConsoleHistory.clear(); // Set up the top, white section. SendMessage( GetDlgItem( g_hDlg, IDC_STATUS ), WM_SETFONT, (WPARAM) CreateFont( 13, 0, 0, 0, 600, 0, 0, 0, 0, 0, 0, 0, 0, "Tahoma" ), (LPARAM) 1 ); LOGBRUSH LogBrush; LogBrush.lbStyle = BS_SOLID; LogBrush.lbColor = RGB( 255, 255, 255 ); g_hWhiteBrush = CreateBrushIndirect( &LogBrush ); main_UpdateServerStatus( ); // Set up the player list LVCOLUMN ColumnData; ColumnData.mask = LVCF_FMT|LVCF_TEXT|LVCF_WIDTH; ColumnData.fmt = LVCFMT_LEFT; ColumnData.cx = 192; ColumnData.pszText = "Name"; ColumnData.cchTextMax = 64; ColumnData.iSubItem = 0; SendDlgItemMessage( hDlg, IDC_PLAYERLIST, LVM_INSERTCOLUMN, COLUMN_NAME, (LPARAM)&ColumnData ); // Add the cached list of players. LVITEM Item; Item.mask = LVIF_TEXT; Item.iSubItem = COLUMN_NAME; Item.iItem = MAXPLAYERS; while ( g_InitialPlayers.size( ) ) { Item.pszText = (LPSTR) g_InitialPlayers.front( ).GetChars( ); g_InitialPlayers.pop_front( ); SendDlgItemMessage( g_hDlg, IDC_PLAYERLIST, LVM_INSERTITEM, 0, (LPARAM)&Item ) ; } // Load the icon. SendMessage( hDlg, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM) (HICON) LoadImage( g_hInst, MAKEINTRESOURCE( AAA_MAIN_ICON ), IMAGE_ICON, 16, 16, LR_SHARED )); SendMessage( hDlg, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)LoadIcon( g_hInst, MAKEINTRESOURCE( AAA_MAIN_ICON ))); // Set up the status bar. g_hDlgStatusBar = CreateStatusWindow(WS_CHILD | WS_VISIBLE, (LPCTSTR)NULL, hDlg, IDC_STATIC); g_bRCONDialogVisible = true; break; case WM_COMMAND: switch ( LOWORD( wParam )) { // This also occurs when esc is pressed. case IDCANCEL: main_Quit( ); break; // "Send" button. case IDC_SEND: char szCommand[256]; GetDlgItemText( hDlg, IDC_INPUTBOX, szCommand, sizeof( szCommand )); NETWORK_ClearBuffer( &g_MessageBuffer ); NETWORK_WriteByte( &g_MessageBuffer.ByteStream, CLRC_COMMAND ); if ( szCommand[0] == ':' ) // If the text in the send buffer begins with a :, the admin is just talking. { char szBuffer2[256 + 4]; sprintf( szBuffer2, "say %s", szCommand + 1 ); NETWORK_WriteString( &g_MessageBuffer.ByteStream, szBuffer2 ); break; } else if ( szCommand[0] == '/' ) // If the text in the send buffer begins with a slash, error out -- Skulltag used to require you to do this to send commands. { Printf( "You longer have to prefix commands with a / to send them.\n" ); SetDlgItemText( hDlg, IDC_INPUTBOX, szCommand + 1 ); SendMessage( GetDlgItem( hDlg, IDC_INPUTBOX ), EM_SETSEL, strlen( szCommand ) - 1, strlen( szCommand ) - 1 ); break; } else NETWORK_WriteString( &g_MessageBuffer.ByteStream, szCommand ); NETWORK_LaunchPacket( &g_MessageBuffer, g_ServerAddress ); time( &g_tLastSentCommand ); SetDlgItemText( hDlg, IDC_INPUTBOX, "" ); break; } break; case WM_SYSCOMMAND: // Hide the window when minimized. if ( wParam == SC_MINIMIZE ) ShowWindow( hDlg, SW_HIDE ); else DefWindowProc( hDlg, Message, wParam, lParam ); break; case WM_CLOSE: main_Quit( ); break; case WM_DESTROY: Shell_NotifyIcon( NIM_DELETE, &g_NotifyIconData ); PostQuitMessage( 0 ); break; case UWM_TRAY_TRAYID: return main_TrayIconClicked( hDlg, lParam ); default: return FALSE; } return TRUE; // If this is false, minimizing the window won't hide it. }
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 ); } }
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 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" ); }
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; } }