mirror of
https://github.com/FWGS/xash3d-fwgs.git
synced 2026-08-05 03:24:56 +08:00
engine: server: add challenge to A2S_GOLDSRC_RULES and A2S_GOLDSRC_PLAYERS
This commit is contained in:
@@ -138,7 +138,7 @@ These match Source Engine Query messages used in GoldSrc and Source engine and i
|
||||
|
||||
Unlike other connectionless messages these are binary: integers are little-endian, floats are 32-bit IEEE 754 and strings are null-terminated.
|
||||
|
||||
`A2S_GOLDSRC_PLAYERS` and `A2S_GOLDSRC_RULES` requests carry a 32-bit challenge, `A2S_GOLDSRC_INFO` may have one appended after the query string. When no challenge is known yet, `\xff\xff\xff\xff` is sent in it's place. The server may answer with a challenge instead of the data: byte `A` (`0x41`) followed by a 32-bit challenge value, 9 bytes total with the header. The request is then repeated with the received challenge. Don't confuse this message with textual `S2C_GOLDSRC_CHALLENGE` used during connection, they only share the first byte. Xash servers respond to queries immediately and never send query challenges, GoldSrc servers require them since the 2020 update.
|
||||
`A2S_GOLDSRC_PLAYERS` and `A2S_GOLDSRC_RULES` requests carry a 32-bit challenge, `A2S_GOLDSRC_INFO` may have one appended after the query string. When no challenge is known yet, `\xff\xff\xff\xff` is sent in it's place. The server may answer with a challenge instead of the data: byte `A` (`0x41`) followed by a 32-bit challenge value, 9 bytes total with the header. The request is then repeated with the received challenge. Don't confuse this message with textual `S2C_GOLDSRC_CHALLENGE` used during connection, they only share the first byte.
|
||||
|
||||
Responses that don't fit into a single packet (usually rules) come from GoldSrc servers split into fragments. Such packets start with `\xfe\xff\xff\xff` instead of `\xff\xff\xff\xff`, followed by a 32-bit sequence number, same in all fragments of one response, and a byte with total fragment count in the lower 4 bits and this fragment's index in the upper 4 bits. Fragment payloads concatenated in index order form a normal `\xff\xff\xff\xff` message. Xash servers never split query responses and send them as single datagrams.
|
||||
|
||||
|
||||
@@ -542,6 +542,8 @@ void SV_UpdateServerInfo( void );
|
||||
void SV_EndRedirect( host_redirect_t *rd );
|
||||
void SV_RejectConnection( netadr_t from, const char *fmt, ... ) FORMAT_CHECK( 2 );
|
||||
void SV_GetPlayerCount( int *clients, int *bots );
|
||||
int SV_CreateChallenge( netadr_t from, qboolean *error );
|
||||
qboolean SV_ValidateChallenge( netadr_t from, int challenge );
|
||||
|
||||
static inline qboolean SV_HavePassword( void )
|
||||
{
|
||||
@@ -701,6 +703,6 @@ int SV_LightForEntity( edict_t *pEdict );
|
||||
//
|
||||
// sv_query.c
|
||||
//
|
||||
void SV_SourceQuery_HandleConnnectionlessPacket( const char *c, netadr_t from );
|
||||
void SV_SourceQuery_HandleConnnectionlessPacket( const char *c, netadr_t from, sizebuf_t *msg );
|
||||
|
||||
#endif//SERVER_H
|
||||
|
||||
@@ -109,11 +109,43 @@ static int SV_GetChallenge( netadr_t from, uint32_t time_window, qboolean *error
|
||||
return digest[0] | digest[1] << 8 | digest[2] << 16 | digest[3] << 24;
|
||||
}
|
||||
|
||||
static void SV_SendChallenge( netadr_t from, qboolean skip_bandwidth_test )
|
||||
/*
|
||||
=================
|
||||
SV_CreateChallenge
|
||||
=================
|
||||
*/
|
||||
int SV_CreateChallenge( netadr_t from, qboolean *error )
|
||||
{
|
||||
uint32_t time_window = (uint32_t)( host.realtime / CHALLENGE_WINDOW_SECONDS );
|
||||
|
||||
return SV_GetChallenge( from, time_window, error );
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
SV_ValidateChallenge
|
||||
=================
|
||||
*/
|
||||
qboolean SV_ValidateChallenge( netadr_t from, int challenge )
|
||||
{
|
||||
qboolean error = false;
|
||||
uint32_t time_window = (uint32_t)( host.realtime / CHALLENGE_WINDOW_SECONDS );
|
||||
int challenge = SV_GetChallenge( from, time_window, &error );
|
||||
|
||||
// accept the current window and the previous one so challenges issued just
|
||||
// before a window boundary remain valid for the full expected lifetime
|
||||
if( SV_GetChallenge( from, time_window, &error ) == challenge && !error )
|
||||
return true;
|
||||
|
||||
if( SV_GetChallenge( from, time_window - 1, &error ) == challenge && !error )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void SV_SendChallenge( netadr_t from, qboolean skip_bandwidth_test )
|
||||
{
|
||||
qboolean error = false;
|
||||
int challenge = SV_CreateChallenge( from, &error );
|
||||
|
||||
if( error )
|
||||
return;
|
||||
@@ -213,15 +245,7 @@ Make sure connecting client is not spoofing
|
||||
*/
|
||||
static int SV_CheckChallenge( netadr_t from, int challenge )
|
||||
{
|
||||
qboolean error = false;
|
||||
uint32_t time_window = (uint32_t)( host.realtime / CHALLENGE_WINDOW_SECONDS );
|
||||
|
||||
// accept the current window and the previous one so challenges issued just
|
||||
// before a window boundary remain valid for the full expected lifetime
|
||||
if( SV_GetChallenge( from, time_window, &error ) == challenge && !error )
|
||||
return true;
|
||||
|
||||
if( SV_GetChallenge( from, time_window - 1, &error ) == challenge && !error )
|
||||
if( SV_ValidateChallenge( from, challenge ))
|
||||
return true;
|
||||
|
||||
SV_RejectConnection( from, "no challenge for your address\n" );
|
||||
@@ -3138,7 +3162,7 @@ void SV_ConnectionlessPacket( netadr_t from, sizebuf_t *msg )
|
||||
// `pcmd` points only to the first word from the query string.
|
||||
if( !Q_strcmp( args, A2S_GOLDSRC_INFO ) || pcmd[0] == A2S_GOLDSRC_PLAYERS || pcmd[0] == A2S_GOLDSRC_RULES )
|
||||
{
|
||||
SV_SourceQuery_HandleConnnectionlessPacket( args, from );
|
||||
SV_SourceQuery_HandleConnnectionlessPacket( args, from, msg );
|
||||
}
|
||||
else if( !Q_strcmp( pcmd, A2A_INFO ))
|
||||
{
|
||||
|
||||
@@ -165,12 +165,49 @@ static void SV_SourceQuery_Players( netadr_t from )
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SV_SourceQuery_CheckChallenge
|
||||
|
||||
Validates the challenge that follows the query type byte.
|
||||
On missing, mismatched or explicitly requested (-1) challenge,
|
||||
replies with a new challenge for this address.
|
||||
==================
|
||||
*/
|
||||
static qboolean SV_SourceQuery_CheckChallenge( netadr_t from, sizebuf_t *msg )
|
||||
{
|
||||
qboolean error = false;
|
||||
|
||||
MSG_SeekToBit( msg, ( sizeof( uint32_t ) + sizeof( uint8_t )) << 3, SEEK_SET );
|
||||
int challenge = MSG_ReadLong( msg );
|
||||
|
||||
if( !MSG_CheckOverflow( msg ) && challenge != -1 && SV_ValidateChallenge( from, challenge ))
|
||||
return true;
|
||||
|
||||
challenge = SV_CreateChallenge( from, &error );
|
||||
if( error )
|
||||
return false;
|
||||
|
||||
sizebuf_t buf;
|
||||
char answer[16];
|
||||
|
||||
MSG_Init( &buf, "QueryChallenge", answer, sizeof( answer ));
|
||||
|
||||
MSG_WriteDword( &buf, 0xFFFFFFFFU );
|
||||
MSG_WriteByte( &buf, S2C_GOLDSRC_CHALLENGE[0] );
|
||||
MSG_WriteLong( &buf, challenge );
|
||||
|
||||
NET_SendPacket( NS_SERVER, MSG_GetNumBytesWritten( &buf ), MSG_GetData( &buf ), from );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SV_SourceQuery_HandleConnnectionlessPacket
|
||||
==================
|
||||
*/
|
||||
void SV_SourceQuery_HandleConnnectionlessPacket( const char *c, netadr_t from )
|
||||
void SV_SourceQuery_HandleConnnectionlessPacket( const char *c, netadr_t from, sizebuf_t *msg )
|
||||
{
|
||||
if( !Q_strcmp( c, A2S_GOLDSRC_INFO ))
|
||||
{
|
||||
@@ -179,10 +216,12 @@ void SV_SourceQuery_HandleConnnectionlessPacket( const char *c, netadr_t from )
|
||||
else switch( c[0] )
|
||||
{
|
||||
case A2S_GOLDSRC_RULES:
|
||||
SV_SourceQuery_Rules( from );
|
||||
if( SV_SourceQuery_CheckChallenge( from, msg ))
|
||||
SV_SourceQuery_Rules( from );
|
||||
break;
|
||||
case A2S_GOLDSRC_PLAYERS:
|
||||
SV_SourceQuery_Players( from );
|
||||
if( SV_SourceQuery_CheckChallenge( from, msg ))
|
||||
SV_SourceQuery_Players( from );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user