示例#1
0
//*****************************************************************************
//
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 );
}
示例#2
0
//*****************************************************************************
//
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 );
}
示例#3
0
//*****************************************************************************
//
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 );
}
示例#4
0
//*****************************************************************************
//
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 );
}
示例#5
0
//*****************************************************************************
//
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 );
}
示例#6
0
//*****************************************************************************
//
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 ));
}
示例#7
0
//*****************************************************************************
//
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 );
}
示例#8
0
//*****************************************************************************
//
void NETWORK_WriteFloat( BYTESTREAM_s *pByteStream, float Float )
{
	union
	{
		float	f;
		int		l;
	} dat;

	dat.f = Float;

	NETWORK_WriteLong( pByteStream, dat.l );
}
示例#9
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 );
}
示例#10
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 );
}