- Implement dual protocol support (#4962)

- Fix several UDP spoofing security issues
This commit is contained in:
Thilo Schulz 2011-07-12 11:59:48 +00:00
parent 309c322b80
commit e06c117e9e
13 changed files with 550 additions and 247 deletions

View file

@ -533,7 +533,6 @@ void CL_WriteDemoMessage ( msg_t *msg, int headerBytes ) {
len = clc.serverMessageSequence;
swlen = LittleLong( len );
FS_Write (&swlen, 4, clc.demofile);
// skip the packet sequencing information
len = msg->cursize - headerBytes;
swlen = LittleLong(len);
@ -636,14 +635,24 @@ void CL_Record_f( void ) {
if ( Cmd_Argc() == 2 ) {
s = Cmd_Argv(1);
Q_strncpyz( demoName, s, sizeof( demoName ) );
Com_sprintf (name, sizeof(name), "demos/%s.%s%d", demoName, DEMOEXT, com_protocol->integer );
#ifdef LEGACY_PROTOCOL
if(clc.compat)
Com_sprintf(name, sizeof(name), "demos/%s.%s%d", demoName, DEMOEXT, com_legacyprotocol->integer);
else
#endif
Com_sprintf(name, sizeof(name), "demos/%s.%s%d", demoName, DEMOEXT, com_protocol->integer);
} else {
int number;
// scan for a free demo name
for ( number = 0 ; number <= 9999 ; number++ ) {
CL_DemoFilename( number, demoName );
Com_sprintf (name, sizeof(name), "demos/%s.%s%d", demoName, DEMOEXT, com_protocol->integer );
#ifdef LEGACY_PROTOCOL
if(clc.compat)
Com_sprintf(name, sizeof(name), "demos/%s.%s%d", demoName, DEMOEXT, com_legacyprotocol->integer);
else
#endif
Com_sprintf(name, sizeof(name), "demos/%s.%s%d", demoName, DEMOEXT, com_protocol->integer);
if (!FS_FileExists(name))
break; // file doesn't exist
@ -665,7 +674,6 @@ void CL_Record_f( void ) {
clc.spDemoRecording = qfalse;
}
Q_strncpyz( clc.demoName, demoName, sizeof( clc.demoName ) );
// don't start saving messages until a non-delta compressed message is received
@ -889,36 +897,62 @@ void CL_ReadDemoMessage( void ) {
CL_WalkDemoExt
====================
*/
static void CL_WalkDemoExt(char *arg, char *name, int *demofile)
static int CL_WalkDemoExt(char *arg, char *name, int *demofile)
{
int i = 0;
*demofile = 0;
Com_sprintf (name, MAX_OSPATH, "demos/%s.%s%d", arg, DEMOEXT, com_protocol->integer);
FS_FOpenFileRead( name, demofile, qtrue );
if (*demofile)
#ifdef LEGACY_PROTOCOL
if(com_legacyprotocol->integer > 0)
{
Com_Printf("Demo file: %s\n", name);
return;
Com_sprintf(name, MAX_OSPATH, "demos/%s.%s%d", arg, DEMOEXT, com_legacyprotocol->integer);
FS_FOpenFileRead(name, demofile, qtrue);
if (*demofile)
{
Com_Printf("Demo file: %s\n", name);
return com_legacyprotocol->integer;
}
}
if(com_protocol->integer != com_legacyprotocol->integer)
#endif
{
Com_sprintf(name, MAX_OSPATH, "demos/%s.%s%d", arg, DEMOEXT, com_protocol->integer);
FS_FOpenFileRead(name, demofile, qtrue);
if (*demofile)
{
Com_Printf("Demo file: %s\n", name);
return com_protocol->integer;
}
}
Com_Printf("Not found: %s\n", name);
while(demo_protocols[i])
{
#ifdef LEGACY_PROTOCOL
if(demo_protocols[i] == com_legacyprotocol->integer)
continue;
#endif
if(demo_protocols[i] == com_protocol->integer)
continue;
Com_sprintf (name, MAX_OSPATH, "demos/%s.%s%d", arg, DEMOEXT, demo_protocols[i]);
FS_FOpenFileRead( name, demofile, qtrue );
if (*demofile)
{
Com_Printf("Demo file: %s\n", name);
break;
return demo_protocols[i];
}
else
Com_Printf("Not found: %s\n", name);
i++;
}
return -1;
}
/*
@ -978,7 +1012,11 @@ void CL_PlayDemo_f( void ) {
break;
}
if(demo_protocols[i] || protocol == com_protocol->integer)
if(demo_protocols[i] || protocol == com_protocol->integer
#ifdef LEGACY_PROTOCOL
|| protocol == com_legacyprotocol->integer
#endif
)
{
Com_sprintf(name, sizeof(name), "demos/%s", arg);
FS_FOpenFileRead(name, &clc.demofile, qtrue);
@ -995,11 +1033,11 @@ void CL_PlayDemo_f( void ) {
Q_strncpyz(retry, arg, len + 1);
retry[len] = '\0';
CL_WalkDemoExt(retry, name, &clc.demofile);
protocol = CL_WalkDemoExt(retry, name, &clc.demofile);
}
}
else
CL_WalkDemoExt(arg, name, &clc.demofile);
protocol = CL_WalkDemoExt(arg, name, &clc.demofile);
if (!clc.demofile) {
Com_Error( ERR_DROP, "couldn't open %s", name);
@ -1013,6 +1051,13 @@ void CL_PlayDemo_f( void ) {
clc.demoplaying = qtrue;
Q_strncpyz( clc.servername, Cmd_Argv(1), sizeof( clc.servername ) );
#ifdef LEGACY_PROTOCOL
if(protocol <= com_legacyprotocol->integer)
clc.compat = qtrue;
else
clc.compat = qfalse;
#endif
// read demo messages until connected
while ( clc.state >= CA_CONNECTED && clc.state < CA_PRIMED ) {
CL_ReadDemoMessage();
@ -2180,7 +2225,9 @@ void CL_CheckForResend( void ) {
#endif
// The challenge request shall be followed by a client challenge so no malicious server can hijack this connection.
Com_sprintf(data, sizeof(data), "getchallenge %d", clc.challenge);
// Add the heartbeat gamename so the server knows we're running the correct game and can reject the client
// with a meaningful message
Com_sprintf(data, sizeof(data), "getchallenge %d %s", clc.challenge, Cvar_VariableString("sv_heartbeat"));
NET_OutOfBandPrint(NS_CLIENT, clc.serverAddress, "%s", data);
break;
@ -2190,7 +2237,16 @@ void CL_CheckForResend( void ) {
port = Cvar_VariableValue ("net_qport");
Q_strncpyz( info, Cvar_InfoString( CVAR_USERINFO ), sizeof( info ) );
Info_SetValueForKey( info, "protocol", va("%i", com_protocol->integer ) );
#ifdef LEGACY_PROTOCOL
if(com_legacyprotocol->integer == com_protocol->integer)
clc.compat = qtrue;
if(clc.compat)
Info_SetValueForKey(info, "protocol", va("%i", com_legacyprotocol->integer));
else
#endif
Info_SetValueForKey(info, "protocol", va("%i", com_protocol->integer));
Info_SetValueForKey( info, "qport", va("%i", port ) );
Info_SetValueForKey( info, "challenge", va("%i", clc.challenge ) );
@ -2431,6 +2487,7 @@ Responses to broadcasts, etc
void CL_ConnectionlessPacket( netadr_t from, msg_t *msg ) {
char *s;
char *c;
int challenge;
MSG_BeginReadingOOB( msg );
MSG_ReadLong( msg ); // skip the -1
@ -2446,23 +2503,70 @@ void CL_ConnectionlessPacket( netadr_t from, msg_t *msg ) {
// challenge from the server we are connecting to
if (!Q_stricmp(c, "challengeResponse"))
{
char *strver;
int ver;
if (clc.state != CA_CONNECTING)
{
Com_DPrintf("Unwanted challenge response received. Ignored.\n");
return;
}
if(!NET_CompareAdr(from, clc.serverAddress))
c = Cmd_Argv(2);
if(*c)
challenge = atoi(c);
strver = Cmd_Argv(3);
if(*strver)
{
// This challenge response is not coming from the expected address.
// Check whether we have a matching client challenge to prevent
// connection hi-jacking.
ver = atoi(strver);
c = Cmd_Argv(2);
if(!*c || atoi(c) != clc.challenge)
if(ver != com_protocol->integer)
{
Com_DPrintf("Challenge response received from unexpected source. Ignored.\n");
#ifdef LEGACY_PROTOCOL
if(com_legacyprotocol->integer > 0)
{
// Server is ioq3 but has a different protocol than we do.
// Fall back to idq3 protocol.
clc.compat = qtrue;
Com_Printf(S_COLOR_YELLOW "Warning: Server reports protocol version %d, "
"we have %d. Trying legacy protocol %d.\n",
ver, com_protocol->integer, com_legacyprotocol->integer);
}
else
#endif
{
Com_Printf(S_COLOR_YELLOW "Warning: Server reports protocol version %d, we have %d. "
"Trying anyways.\n", ver, com_protocol->integer);
}
}
}
#ifdef LEGACY_PROTOCOL
else
clc.compat = qtrue;
if(clc.compat)
{
if(!NET_CompareAdr(from, clc.serverAddress))
{
// This challenge response is not coming from the expected address.
// Check whether we have a matching client challenge to prevent
// connection hi-jacking.
if(!*c || challenge != clc.challenge)
{
Com_DPrintf("Challenge response received from unexpected source. Ignored.\n");
return;
}
}
}
else
#endif
{
if(!*c || challenge != clc.challenge)
{
Com_Printf("Bad challenge for challengeResponse. Ignored.\n");
return;
}
}
@ -2494,7 +2598,36 @@ void CL_ConnectionlessPacket( netadr_t from, msg_t *msg ) {
Com_Printf( "connectResponse from wrong address. Ignored.\n" );
return;
}
Netchan_Setup (NS_CLIENT, &clc.netchan, from, Cvar_VariableValue( "net_qport" ) );
#ifdef LEGACY_PROTOCOL
if(!clc.compat)
#endif
{
c = Cmd_Argv(1);
if(*c)
challenge = atoi(c);
else
{
Com_Printf("Bad connectResponse received. Ignored.\n");
return;
}
if(challenge != clc.challenge)
{
Com_Printf("ConnectResponse with bad challenge received. Ignored.\n");
return;
}
}
#ifdef LEGACY_PROTOCOL
Netchan_Setup(NS_CLIENT, &clc.netchan, from, Cvar_VariableValue("net_qport"),
clc.challenge, clc.compat);
#else
Netchan_Setup(NS_CLIENT, &clc.netchan, from, Cvar_VariableValue("net_qport"),
clc.challenge, qfalse);
#endif
clc.state = CA_CONNECTED;
clc.lastPacketSentTime = -9999; // send first packet immediately
return;
@ -2512,13 +2645,6 @@ void CL_ConnectionlessPacket( netadr_t from, msg_t *msg ) {
return;
}
// a disconnect message from the server, which will happen if the server
// dropped the connection but it is still getting packets from us
if (!Q_stricmp(c, "disconnect")) {
CL_DisconnectPacket( from );
return;
}
// echo request from server
if ( !Q_stricmp(c, "echo") ) {
NET_OutOfBandPrint( NS_CLIENT, from, "%s", Cmd_Argv(1) );
@ -2538,10 +2664,12 @@ void CL_ConnectionlessPacket( netadr_t from, msg_t *msg ) {
}
// echo request from server
if ( !Q_stricmp(c, "print") ) {
if(!Q_stricmp(c, "print")){
s = MSG_ReadString( msg );
Q_strncpyz( clc.serverMessage, s, sizeof( clc.serverMessage ) );
Com_Printf( "%s", s );
return;
}
@ -3492,7 +3620,13 @@ void CL_ServerInfoPacket( netadr_t from, msg_t *msg ) {
// if this isn't the correct protocol version, ignore it
prot = atoi( Info_ValueForKey( infoString, "protocol" ) );
if ( prot != com_protocol->integer ) {
if(prot != com_protocol->integer
#ifdef LEGACY_PROTOCOL
&& prot != com_legacyprotocol->integer
#endif
)
{
Com_DPrintf( "Different protocol info packet: %s\n", infoString );
return;
}