diff --git a/reaction/code/client/cl_main.c b/reaction/code/client/cl_main.c index 0661862e..933ace9c 100644 --- a/reaction/code/client/cl_main.c +++ b/reaction/code/client/cl_main.c @@ -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,25 @@ 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.dm_%d", demoName, PROTOCOL_VERSION ); +#ifdef PROTOCOL_SUPPORT_OLD + if(clc.compat) + Com_sprintf(name, sizeof(name), "demos/%s.%s%d", demoName, DEMOEXT, com_oldprotocol->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.dm_%d", demoName, PROTOCOL_VERSION ); +#ifdef PROTOCOL_SUPPORT_OLD + if(clc.compat) + Com_sprintf(name, sizeof(name), "demos/%s.%s%d", demoName, DEMOEXT, com_oldprotocol->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 +675,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,23 +898,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; + +#ifdef PROTOCOL_SUPPORT_OLD + if(com_oldprotocol->integer > 0) + { + Com_sprintf(name, MAX_OSPATH, "demos/%s.%s%d", arg, DEMOEXT, com_oldprotocol->integer); + FS_FOpenFileRead(name, demofile, qtrue); + + if (*demofile) + { + Com_Printf("Demo file: %s\n", name); + return com_oldprotocol->integer; + } + } + + if(com_protocol->integer != com_oldprotocol->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]) { - Com_sprintf (name, MAX_OSPATH, "demos/%s.dm_%d", arg, demo_protocols[i]); +#ifdef PROTOCOL_SUPPORT_OLD + if(demo_protocols[i] == com_oldprotocol->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; } /* @@ -949,38 +997,49 @@ void CL_PlayDemo_f( void ) { // open the demo file arg = Cmd_Argv(1); - + CL_Disconnect( qtrue ); - // check for an extension .dm_?? (?? is protocol) - ext_test = arg + strlen(arg) - 6; - if ((strlen(arg) > 6) && (ext_test[0] == '.') && - ((ext_test[1] == 'd') || (ext_test[1] == 'D')) && - ((ext_test[2] == 'm') || (ext_test[2] == 'M')) && - (ext_test[3] == '_')) + // check for an extension .DEMOEXT_?? (?? is protocol) + ext_test = Q_strrchr(arg, '.'); + + if(ext_test && !Q_stricmpn(ext_test + 1, DEMOEXT, ARRAY_LEN(DEMOEXT) - 1)) { - protocol = atoi(ext_test+4); - i=0; - while(demo_protocols[i]) + protocol = atoi(ext_test + ARRAY_LEN(DEMOEXT)); + + for(i = 0; demo_protocols[i]; i++) { - if (demo_protocols[i] == protocol) - break; - i++; + if(demo_protocols[i] == protocol) + break; } - if (demo_protocols[i]) + + if(demo_protocols[i] || protocol == com_protocol->integer +#ifdef PROTOCOL_SUPPORT_OLD + || protocol == com_oldprotocol->integer +#endif + ) { - Com_sprintf (name, sizeof(name), "demos/%s", arg); - FS_FOpenFileRead( name, &clc.demofile, qtrue ); - } else { + Com_sprintf(name, sizeof(name), "demos/%s", arg); + FS_FOpenFileRead(name, &clc.demofile, qtrue); + } + else + { + int len; + Com_Printf("Protocol %d not supported for demos\n", protocol); - Q_strncpyz(retry, arg, sizeof(retry)); - retry[strlen(retry)-6] = 0; - CL_WalkDemoExt( retry, name, &clc.demofile ); + len = ext_test - arg; + + if(len >= ARRAY_LEN(retry)) + len = ARRAY_LEN(retry) - 1; + + Q_strncpyz(retry, arg, len + 1); + retry[len] = '\0'; + protocol = CL_WalkDemoExt(retry, name, &clc.demofile); } - } else { - CL_WalkDemoExt( arg, name, &clc.demofile ); } - + else + protocol = CL_WalkDemoExt(arg, name, &clc.demofile); + if (!clc.demofile) { Com_Error( ERR_DROP, "couldn't open %s", name); return; @@ -993,6 +1052,13 @@ void CL_PlayDemo_f( void ) { clc.demoplaying = qtrue; Q_strncpyz( cls.servername, Cmd_Argv(1), sizeof( cls.servername ) ); +#ifdef PROTOCOL_SUPPORT_OLD + if(protocol <= com_oldprotocol->integer) + clc.compat = qtrue; + else + clc.compat = qfalse; +#endif + // read demo messages until connected while ( cls.state >= CA_CONNECTED && cls.state < CA_PRIMED ) { CL_ReadDemoMessage(); @@ -2133,7 +2199,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 PROTOCOL_SUPPORT_OLD + if(com_oldprotocol->integer == com_protocol->integer) + clc.compat = qtrue; + + if(clc.compat) + Info_SetValueForKey(info, "protocol", va("%i", com_oldprotocol->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 ) ); @@ -2374,6 +2449,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 @@ -2395,20 +2471,35 @@ void CL_ConnectionlessPacket( netadr_t from, msg_t *msg ) { return; } - if(!NET_CompareAdr(from, clc.serverAddress)) + c = Cmd_Argv(2); + if(*c) + challenge = atoi(c); + +#ifdef PROTOCOL_SUPPORT_OLD + if(!clc.compat) { - // This challenge response is not coming from the expected address. - // Check whether we have a matching client challenge to prevent - // connection hi-jacking. - - c = Cmd_Argv(2); - - if(!*c || atoi(c) != clc.challenge) + if(!*c || challenge != clc.challenge) { - Com_DPrintf("Challenge response received from unexpected source. Ignored.\n"); + Com_Printf("Bad challenge for challengeResponse. Ignored.\n"); return; } } + else +#endif + { + 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; + } + } + } // start sending challenge response instead of challenge request packets clc.challenge = atoi(Cmd_Argv(1)); @@ -2437,7 +2528,34 @@ 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 PROTOCOL_SUPPORT_OLD + if(!clc.compat) + { + 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; + } + } + + 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); +#endif + cls.state = CA_CONNECTED; clc.lastPacketSentTime = -9999; // send first packet immediately return; @@ -2455,13 +2573,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) ); @@ -2481,10 +2592,33 @@ 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 ); + + #ifdef PROTOCOL_SUPPORT_OLD + // Hack to detect legacy server protocol + if(cls.state == CA_CHALLENGING && com_oldprotocol->integer > 0) + { + char buf[128]; + int len; + + len = Com_sprintf(buf, sizeof(buf), "Server uses protocol version %d", + com_oldprotocol->integer); + + if(len < sizeof(buf) && !Q_strncmp(s, buf, len) && !isdigit(s[len])) + { + // This is an old, but compatible protocol version. + // Go back to connecting state. + clc.compat = qtrue; + cls.state = CA_CONNECTING; + return; + } + } + #endif + Q_strncpyz( clc.serverMessage, s, sizeof( clc.serverMessage ) ); Com_Printf( "%s", s ); + return; } @@ -3423,7 +3557,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 PROTOCOL_SUPPORT_OLD + && prot != com_oldprotocol->integer +#endif + ) + { Com_DPrintf( "Different protocol info packet: %s\n", infoString ); return; } diff --git a/reaction/code/client/cl_net_chan.c b/reaction/code/client/cl_net_chan.c index 1433c434..abea06d1 100644 --- a/reaction/code/client/cl_net_chan.c +++ b/reaction/code/client/cl_net_chan.c @@ -147,9 +147,6 @@ void CL_Netchan_Transmit( netchan_t *chan, msg_t* msg ) { Netchan_Transmit( chan, msg->cursize, msg->data ); } -extern int oldsize; -int newsize = 0; - /* ================= CL_Netchan_Process @@ -161,7 +158,8 @@ qboolean CL_Netchan_Process( netchan_t *chan, msg_t *msg ) { ret = Netchan_Process( chan, msg ); if (!ret) return qfalse; + CL_Netchan_Decode( msg ); - newsize += msg->cursize; + return qtrue; } diff --git a/reaction/code/client/client.h b/reaction/code/client/client.h index 3758e6fa..02994433 100644 --- a/reaction/code/client/client.h +++ b/reaction/code/client/client.h @@ -260,6 +260,10 @@ typedef struct { float voipPower; #endif +#ifdef PROTOCOL_SUPPORT_OLD + qboolean compat; +#endif + // big stuff at end of structure so most offsets are 15 bits or less netchan_t netchan; } clientConnection_t; diff --git a/reaction/code/qcommon/common.c b/reaction/code/qcommon/common.c index 7d0b7e9b..eeae28bb 100644 --- a/reaction/code/qcommon/common.c +++ b/reaction/code/qcommon/common.c @@ -32,7 +32,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #endif int demo_protocols[] = -{ 66, 67, 68, 0 }; +{ 67, 66, 0 }; #define MAX_NUM_ARGVS 50 @@ -86,6 +86,9 @@ cvar_t *com_maxfpsMinimized; cvar_t *com_abnormalExit; cvar_t *com_standalone; cvar_t *com_protocol; +#ifdef PROTOCOL_SUPPORT_OLD +cvar_t *com_oldprotocol; +#endif cvar_t *com_basegame; cvar_t *com_homepath; cvar_t *com_busyWait; @@ -2710,6 +2713,9 @@ void Com_Init( char *commandLine ) { s = va("%s %s %s", Q3_VERSION, PLATFORM_STRING, __DATE__ ); com_version = Cvar_Get ("version", s, CVAR_ROM | CVAR_SERVERINFO ); com_protocol = Cvar_Get ("protocol", va("%i", PROTOCOL_VERSION), CVAR_SERVERINFO | CVAR_INIT); +#ifdef PROTOCOL_SUPPORT_OLD + com_oldprotocol = Cvar_Get ("oldprotocol", va("%i", PROTOCOL_OLD_VERSION), CVAR_INIT); +#endif Sys_Init(); diff --git a/reaction/code/qcommon/files.c b/reaction/code/qcommon/files.c index e3ff9fcf..30086e37 100644 --- a/reaction/code/qcommon/files.c +++ b/reaction/code/qcommon/files.c @@ -987,6 +987,64 @@ qboolean FS_FilenameCompare( const char *s1, const char *s2 ) { return qfalse; // strings are equal } +/* +=========== +FS_IsExt + +Return qtrue if ext matches file extension filename +=========== +*/ + +qboolean FS_IsExt(const char *filename, const char *ext, int namelen) +{ + int extlen; + + extlen = strlen(ext); + + if(extlen > namelen) + return qfalse; + + filename += namelen - extlen; + + return !Q_stricmp(filename, ext); +} + +/* +=========== +FS_IsDemoExt + +Return qtrue if filename has a demo extension +=========== +*/ + +qboolean FS_IsDemoExt(const char *filename, int namelen) +{ + char *ext_test; + int index, protocol; + + ext_test = Q_strrchr(filename, '.'); + if(ext_test && !Q_stricmpn(ext_test + 1, DEMOEXT, ARRAY_LEN(DEMOEXT) - 1)) + { + protocol = atoi(ext_test + ARRAY_LEN(DEMOEXT)); + + if(protocol == com_protocol->integer) + return qtrue; + +#ifdef PROTOCOL_SUPPORT_OLD + if(protocol == PROTOCOL_OLD_VERSION) + return qtrue; +#endif + + for(index = 0; demo_protocols[index]; index++) + { + if(demo_protocols[index] == protocol) + return qtrue; + } + } + + return qfalse; +} + /* =========== FS_FOpenFileRead diff --git a/reaction/code/qcommon/net_chan.c b/reaction/code/qcommon/net_chan.c index 5b359f92..6a976872 100644 --- a/reaction/code/qcommon/net_chan.c +++ b/reaction/code/qcommon/net_chan.c @@ -83,7 +83,12 @@ Netchan_Setup called to open a channel to a remote system ============== */ -void Netchan_Setup( netsrc_t sock, netchan_t *chan, netadr_t adr, int qport ) { +void Netchan_Setup(netsrc_t sock, netchan_t *chan, netadr_t adr, int qport, int challenge +#ifdef PROTOCOL_SUPPORT_OLD + , qboolean compat +#endif + ) +{ Com_Memset (chan, 0, sizeof(*chan)); chan->sock = sock; @@ -91,6 +96,10 @@ void Netchan_Setup( netsrc_t sock, netchan_t *chan, netadr_t adr, int qport ) { chan->qport = qport; chan->incomingSequence = 0; chan->outgoingSequence = 1; +#ifdef PROTOCOL_SUPPORT_OLD + chan->compat = compat; + chan->challenge = challenge; +#endif } // TTimo: unused, commenting out to make gcc happy @@ -190,17 +199,24 @@ void Netchan_TransmitNextFragment( netchan_t *chan ) { msg_t send; byte send_buf[MAX_PACKETLEN]; int fragmentLength; + int outgoingSequence; // write the packet header MSG_InitOOB (&send, send_buf, sizeof(send_buf)); // <-- only do the oob here - MSG_WriteLong( &send, chan->outgoingSequence | FRAGMENT_BIT ); + outgoingSequence = chan->outgoingSequence | FRAGMENT_BIT; + MSG_WriteLong(&send, outgoingSequence); // send the qport if we are a client if ( chan->sock == NS_CLIENT ) { MSG_WriteShort( &send, qport->integer ); } +#ifdef PROTOCOL_SUPPORT_OLD + if(!chan->compat) +#endif + MSG_WriteLong(&send, NETCHAN_GENCHECKSUM(chan->challenge, chan->outgoingSequence)); + // copy the reliable message to the packet first fragmentLength = FRAGMENT_SIZE; if ( chan->unsentFragmentStart + fragmentLength > chan->unsentLength ) { @@ -268,12 +284,17 @@ void Netchan_Transmit( netchan_t *chan, int length, const byte *data ) { MSG_InitOOB (&send, send_buf, sizeof(send_buf)); MSG_WriteLong( &send, chan->outgoingSequence ); - chan->outgoingSequence++; // send the qport if we are a client - if ( chan->sock == NS_CLIENT ) { - MSG_WriteShort( &send, qport->integer ); - } + if(chan->sock == NS_CLIENT) + MSG_WriteShort(&send, qport->integer); + +#ifdef PROTOCOL_SUPPORT_OLD + if(!chan->compat) +#endif + MSG_WriteLong(&send, NETCHAN_GENCHECKSUM(chan->challenge, chan->outgoingSequence)); + + chan->outgoingSequence++; MSG_WriteData( &send, data, length ); @@ -327,6 +348,17 @@ qboolean Netchan_Process( netchan_t *chan, msg_t *msg ) { qport = MSG_ReadShort( msg ); } +#ifdef PROTOCOL_SUPPORT_OLD + if(!chan->compat) +#endif + { + int checksum = MSG_ReadLong(msg); + + // UDP spoofing protection + if(NETCHAN_GENCHECKSUM(chan->challenge, sequence) != checksum) + return qfalse; + } + // read the fragment information if ( fragmented ) { fragmentStart = MSG_ReadShort( msg ); diff --git a/reaction/code/qcommon/q_shared.c b/reaction/code/qcommon/q_shared.c index 36261335..aae00879 100644 --- a/reaction/code/qcommon/q_shared.c +++ b/reaction/code/qcommon/q_shared.c @@ -962,7 +962,7 @@ int Q_CountChar(const char *string, char tocount) return count; } -void QDECL Com_sprintf(char *dest, int size, const char *fmt, ...) +int QDECL Com_sprintf(char *dest, int size, const char *fmt, ...) { int len; va_list argptr; @@ -973,6 +973,8 @@ void QDECL Com_sprintf(char *dest, int size, const char *fmt, ...) if(len >= size) Com_Printf("Com_sprintf: Output length %d too short, require %d bytes.\n", size, len); + + return len; } /* diff --git a/reaction/code/qcommon/q_shared.h b/reaction/code/qcommon/q_shared.h index b582e7cb..2fcfd6ba 100644 --- a/reaction/code/qcommon/q_shared.h +++ b/reaction/code/qcommon/q_shared.h @@ -34,6 +34,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define GAMENAME_FOR_MASTER "Reaction" // must NOT contain whitespaces #define HEARTBEAT_FOR_MASTER GAMENAME_FOR_MASTER #define FLATLINE_FOR_MASTER GAMENAME_FOR_MASTER "dead" +// #define PROTOCOL_SUPPORT_OLD // You probably don't need this for your standalone game #else #define PRODUCT_NAME "Reaction" #define BASEGAME "Boomstick" @@ -42,6 +43,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define GAMENAME_FOR_MASTER "Reaction" #define HEARTBEAT_FOR_MASTER "Reaction-1" #define FLATLINE_FOR_MASTER HEARTBEAT_FOR_MASTER + #define PROTOCOL_SUPPORT_OLD #endif #define BASETA "missionpack" @@ -711,7 +713,7 @@ void Parse2DMatrix (char **buf_p, int y, int x, float *m); void Parse3DMatrix (char **buf_p, int z, int y, int x, float *m); int Com_HexStrToInt( const char *str ); -void QDECL Com_sprintf (char *dest, int size, const char *fmt, ...) __attribute__ ((format (printf, 3, 4))); +int QDECL Com_sprintf (char *dest, int size, const char *fmt, ...) __attribute__ ((format (printf, 3, 4))); char *Com_SkipTokens( char *s, int numTokens, char *sep ); char *Com_SkipCharset( char *s, char *sep ); diff --git a/reaction/code/qcommon/qcommon.h b/reaction/code/qcommon/qcommon.h index e9e89200..f3549bb5 100644 --- a/reaction/code/qcommon/qcommon.h +++ b/reaction/code/qcommon/qcommon.h @@ -194,7 +194,8 @@ void NET_Sleep(int msec); #define MAX_DOWNLOAD_WINDOW 8 // max of eight download frames #define MAX_DOWNLOAD_BLKSIZE 2048 // 2048 byte block chunks - + +#define NETCHAN_GENCHECKSUM(challenge, sequence) ((challenge) ^ ((sequence) * (challenge))) /* Netchan handles packet fragmentation and out of order / duplicate suppression @@ -223,10 +224,20 @@ typedef struct { int unsentFragmentStart; int unsentLength; byte unsentBuffer[MAX_MSGLEN]; + + int challenge; + +#ifdef PROTOCOL_SUPPORT_OLD + qboolean compat; +#endif } netchan_t; void Netchan_Init( int qport ); -void Netchan_Setup( netsrc_t sock, netchan_t *chan, netadr_t adr, int qport ); +void Netchan_Setup(netsrc_t sock, netchan_t *chan, netadr_t adr, int qport, int challenge +#ifdef PROTOCOL_SUPPORT_OLD + , qboolean compat +#endif +); void Netchan_Transmit( netchan_t *chan, int length, const byte *data ); void Netchan_TransmitNextFragment( netchan_t *chan ); @@ -242,7 +253,8 @@ PROTOCOL ============================================================== */ -#define PROTOCOL_VERSION 68 +#define PROTOCOL_VERSION 69 +#define PROTOCOL_OLD_VERSION 68 // 1.31 - 67 // maintain a list of compatible protocols for demo playing @@ -857,6 +869,9 @@ extern cvar_t *cl_packetdelay; extern cvar_t *sv_packetdelay; extern cvar_t *com_protocol; +#ifdef PROTOCOL_SUPPORT_OLD +extern cvar_t *com_oldprotocol; +#endif // com_speeds times extern int time_game; diff --git a/reaction/code/server/server.h b/reaction/code/server/server.h index c8e0a8ca..2497d2e2 100644 --- a/reaction/code/server/server.h +++ b/reaction/code/server/server.h @@ -188,7 +188,11 @@ typedef struct client_s { #endif int oldServerTime; - qboolean csUpdated[MAX_CONFIGSTRINGS+1]; + qboolean csUpdated[MAX_CONFIGSTRINGS+1]; + +#ifdef PROTOCOL_SUPPORT_OLD + qboolean compat; +#endif } client_t; //============================================================================= @@ -197,7 +201,11 @@ typedef struct client_s { // MAX_CHALLENGES is made large to prevent a denial // of service attack that could cycle all of them // out before legitimate users connected -#define MAX_CHALLENGES 1024 +#define MAX_CHALLENGES 2048 +// Allow a certain amount of challenges to have the same IP address +// to make it a bit harder to DOS one single IP address from connecting +// while not allowing a single ip to grab all challenge resources +#define MAX_CHALLENGES_MULTI (MAX_CHALLENGES / 2) #define AUTHORIZE_TIMEOUT 5000 diff --git a/reaction/code/server/sv_client.c b/reaction/code/server/sv_client.c index f4ebe60f..e5f3c8eb 100644 --- a/reaction/code/server/sv_client.c +++ b/reaction/code/server/sv_client.c @@ -55,8 +55,10 @@ void SV_GetChallenge(netadr_t from) int i; int oldest; int oldestTime; - const char *clientChallenge = Cmd_Argv(1); + int oldestClientTime; + int clientChallenge; challenge_t *challenge; + qboolean wasfound = qfalse; // ignore if we are in single player if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive")) { @@ -64,15 +66,30 @@ void SV_GetChallenge(netadr_t from) } oldest = 0; - oldestTime = 0x7fffffff; + oldestClientTime = oldestTime = 0x7fffffff; // see if we already have a challenge for this ip challenge = &svs.challenges[0]; - for (i = 0 ; i < MAX_CHALLENGES ; i++, challenge++) { - if (!challenge->connected && NET_CompareAdr( from, challenge->adr ) ) { + clientChallenge = atoi(Cmd_Argv(1)); + + for(i = 0 ; i < MAX_CHALLENGES ; i++, challenge++) + { + if(!challenge->connected && NET_CompareAdr(from, challenge->adr)) + { + wasfound = qtrue; + + if(challenge->time < oldestClientTime) + oldestClientTime = challenge->time; + } + + if(wasfound && i >= MAX_CHALLENGES_MULTI) + { + i = MAX_CHALLENGES; break; } - if ( challenge->time < oldestTime ) { + + if(challenge->time < oldestTime) + { oldestTime = challenge->time; oldest = i; } @@ -82,17 +99,16 @@ void SV_GetChallenge(netadr_t from) { // this is the first time this client has asked for a challenge challenge = &svs.challenges[oldest]; - challenge->clientChallenge = 0; + challenge->clientChallenge = clientChallenge; challenge->adr = from; challenge->firstTime = svs.time; - challenge->time = svs.time; challenge->connected = qfalse; } // always generate a new challenge number, so the client cannot circumvent sv_maxping challenge->challenge = ( (rand() << 16) ^ rand() ) ^ svs.time; challenge->wasrefused = qfalse; - + challenge->time = svs.time; #ifndef STANDALONE // Drop the authorize stuff if this client is coming in via v6 as the auth server does not support ipv6. @@ -121,7 +137,7 @@ void SV_GetChallenge(netadr_t from) // if they have been challenging for a long time and we // haven't heard anything from the authorize server, go ahead and // let them in, assuming the id server is down - else if(svs.time - challenge->firstTime > AUTHORIZE_TIMEOUT) + else if(svs.time - oldestClientTime > AUTHORIZE_TIMEOUT) Com_DPrintf( "authorize server timed out\n" ); else { @@ -129,10 +145,6 @@ void SV_GetChallenge(netadr_t from) cvar_t *fs; char game[1024]; - // If the client provided us with a client challenge, store it... - if(*clientChallenge) - challenge->clientChallenge = atoi(clientChallenge); - Com_DPrintf( "sending getIpAuthorize for %s\n", NET_AdrToString( from )); strcpy(game, BASEGAME); @@ -153,7 +165,7 @@ void SV_GetChallenge(netadr_t from) #endif challenge->pingTime = svs.time; - NET_OutOfBandPrint( NS_SERVER, challenge->adr, "challengeResponse %i %s", challenge->challenge, clientChallenge); + NET_OutOfBandPrint( NS_SERVER, challenge->adr, "challengeResponse %i %d", challenge->challenge, clientChallenge); } #ifndef STANDALONE @@ -289,6 +301,9 @@ void SV_DirectConnect( netadr_t from ) { intptr_t denied; int count; char *ip; +#ifdef PROTOCOL_SUPPORT_OLD + qboolean compat = qfalse; +#endif Com_DPrintf ("SVC_DirectConnect ()\n"); @@ -301,11 +316,21 @@ void SV_DirectConnect( netadr_t from ) { Q_strncpyz( userinfo, Cmd_Argv(1), sizeof(userinfo) ); - version = atoi( Info_ValueForKey( userinfo, "protocol" ) ); - if ( version != com_protocol->integer ) { - NET_OutOfBandPrint( NS_SERVER, from, "print\nServer uses protocol version %i (yours is %i).\n", com_protocol->integer, version ); - Com_DPrintf (" rejected connect from version %i\n", version); - return; + version = atoi(Info_ValueForKey(userinfo, "protocol")); + +#ifdef PROTOCOL_SUPPORT_OLD + if(version > 0 && com_oldprotocol->integer == version) + compat = qtrue; + else +#endif + { + if(version != com_protocol->integer) + { + NET_OutOfBandPrint(NS_SERVER, from, "print\nServer uses protocol version %i " + "(yours is %i).\n", com_protocol->integer, version); + Com_DPrintf(" rejected connect from version %i\n", version); + return; + } } challenge = atoi( Info_ValueForKey( userinfo, "challenge" ) ); @@ -487,7 +512,12 @@ gotnewcl: newcl->challenge = challenge; // save the address - Netchan_Setup (NS_SERVER, &newcl->netchan , from, qport); +#ifdef PROTOCOL_SUPPORT_OLD + newcl->compat = compat; + Netchan_Setup(NS_SERVER, &newcl->netchan, from, qport, challenge, compat); +#else + Netchan_Setup(NS_SERVER, &newcl->netchan, from, qport, challenge); +#endif // init the netchan queue newcl->netchan_end_queue = &newcl->netchan_start_queue; @@ -508,7 +538,7 @@ gotnewcl: SV_UserinfoChanged( newcl ); // send the connect packet to the client - NET_OutOfBandPrint( NS_SERVER, from, "connectResponse" ); + NET_OutOfBandPrint(NS_SERVER, from, "connectResponse %d", challenge); Com_DPrintf( "Going from CS_FREE to CS_CONNECTED for %s\n", newcl->name );