[quake3-commits] r2075 - in trunk: . code/client code/q3_ui code/qcommon code/server
DONOTREPLY at icculus.org
DONOTREPLY at icculus.org
Tue Jul 12 07:59:48 EDT 2011
Author: thilo
Date: 2011-07-12 07:59:48 -0400 (Tue, 12 Jul 2011)
New Revision: 2075
Modified:
trunk/README
trunk/code/client/cl_main.c
trunk/code/client/cl_net_chan.c
trunk/code/client/client.h
trunk/code/q3_ui/ui_demo2.c
trunk/code/qcommon/common.c
trunk/code/qcommon/files.c
trunk/code/qcommon/net_chan.c
trunk/code/qcommon/q_shared.h
trunk/code/qcommon/qcommon.h
trunk/code/server/server.h
trunk/code/server/sv_client.c
trunk/code/server/sv_main.c
Log:
- Implement dual protocol support (#4962)
- Fix several UDP spoofing security issues
Modified: trunk/README
===================================================================
--- trunk/README 2011-07-12 11:01:49 UTC (rev 2074)
+++ trunk/README 2011-07-12 11:59:48 UTC (rev 2075)
@@ -185,6 +185,10 @@
standalone mode
com_homepath - Specify name that is to be appended to the
home path
+ com_legacyprotocol - Specify protocol version number for
+ legacy Quake3 1.32c protocol, see
+ "Network protocols" section below
+ (startup only)
com_maxfpsUnfocused - Maximum frames per second when unfocused
com_maxfpsMinimized - Maximum frames per second when minimized
com_busyWait - Will use a busy loop to wait for rendering
@@ -193,6 +197,10 @@
through which other processes can control
the server while it is running.
Nonfunctional on Windows.
+ com_protocol - Specify protocol version number for
+ current ioquake3 protocol, see
+ "Network protocols" section below
+ (startup only)
in_joystickNo - select which joystick to use
in_availableJoysticks - list of available Joysticks
@@ -218,9 +226,6 @@
ipv6 servers on the local network
net_mcastiface - outgoing interface to use for scan
- protocol - Allow changing protocol version
- (startup only)
-
r_allowResize - make window resizable (SDL only)
r_ext_texture_filter_anisotropic - anisotropic texture filtering
r_zProj - distance of observer camera to projection
@@ -281,8 +286,9 @@
which <filename/path> - print out the path on disk to a loaded item
------------------------------------------------------------- Miscellaneous -----
+--------------------------------------------------------- README for Users -----
+
Using shared libraries instead of qvm
To force Q3 to use shared libraries instead of qvms run it with the following
parameters: +set sv_pure 0 +set vm_cgame 0 +set vm_game 0 +set vm_ui 0
@@ -309,6 +315,114 @@
In this case you can always revert back to the old behaviour by setting the
cvar com_busyWait to 1.
+Using HTTP/FTP Download Support (Server)
+ You can enable redirected downloads on your server even if it's not
+ an ioquake3 server. You simply need to use the 'sets' command to put
+ the sv_dlURL cvar into your SERVERINFO string and ensure sv_allowDownloads
+ is set to 1
+
+ sv_dlURL is the base of the URL that contains your custom .pk3 files
+ the client will append both fs_game and the filename to the end of
+ this value. For example, if you have sv_dlURL set to
+ "http://ioquake3.org", fs_game is "baseq3", and the client is
+ missing "test.pk3", it will attempt to download from the URL
+ "http://ioquake3.org/baseq3/test.pk3"
+
+ sv_allowDownload's value is now a bitmask made up of the following
+ flags:
+ 1 - ENABLE
+ 4 - do not use UDP downloads
+ 8 - do not ask the client to disconnect when using HTTP/FTP
+
+ Server operators who are concerned about potential "leeching" from their
+ HTTP servers from other ioquake3 servers can make use of the HTTP_REFERER
+ that ioquake3 sets which is "ioQ3://{SERVER_IP}:{SERVER_PORT}". For,
+ example, Apache's mod_rewrite can restrict access based on HTTP_REFERER.
+
+ On a sidenote, downloading via UDP has been improved and yields higher data
+ rates now. You can configure the maximum bandwidth for UDP downloads via the
+ cvar sv_dlRate. Due to system-specific limits the download rate is capped
+ at about 1 Mbyte/s per client, so curl downloading may still be faster.
+
+Using HTTP/FTP Download Support (Client)
+ Simply setting cl_allowDownload to 1 will enable HTTP/FTP downloads
+ assuming ioquake3 was compiled with USE_CURL=1 (the default).
+ like sv_allowDownload, cl_allowDownload also uses a bitmask value
+ supporting the following flags:
+ 1 - ENABLE
+ 2 - do not use HTTP/FTP downloads
+ 4 - do not use UDP downloads
+
+ When ioquake3 is built with USE_CURL_DLOPEN=1 (default on some platforms),
+ it will use the value of the cvar cl_cURLLib as the filename of the cURL
+ library to dynamically load.
+
+Multiuser Support on Windows systems
+ On Windows, all user specific files such as autogenerated configuration,
+ demos, videos, screenshots, and autodownloaded pk3s are now saved in a
+ directory specific to the user who is running ioquake3.
+
+ On NT-based such as Windows XP, this is usually a directory named:
+ "C:\Documents and Settings\%USERNAME%\Application Data\Quake3\"
+
+ Windows 95, Windows 98, and Windows ME will use a directory like:
+ "C:\Windows\Application Data\Quake3"
+ in single-user mode, or:
+ "C:\Windows\Profiles\%USERNAME%\Application Data\Quake3"
+ if multiple logins have been enabled.
+
+ In order to access this directory more easily, the installer may create a
+ Shortcut which has its target set to:
+ "%APPDATA%\Quake3\"
+ This Shortcut would work for all users on the system regardless of the
+ locale settings. Unfortunately, this environment variable is only
+ present on Windows NT based systems.
+
+ You can revert to the old single-user behaviour by setting the fs_homepath
+ cvar to the directory where ioquake3 is installed. For example:
+ ioquake3.exe +set fs_homepath "c:\ioquake3"
+ Note that this cvar MUST be set as a command line parameter.
+
+SDL Keyboard Differences
+ ioquake3 clients have different keyboard behaviour compared to the original
+ Quake3 clients.
+
+ * "Caps Lock" and "Num Lock" can not be used as normal binds since they
+ do not send a KEYUP event until the key is pressed again.
+
+ * SDL > 1.2.9 does not support disabling dead key recognition. In order to
+ send dead key characters (e.g. ~, ', `, and ^), you must key a Space (or
+ sometimes the same character again) after the character to send it on
+ many international keyboard layouts.
+
+ * The SDL client supports many more keys than the original Quake3 client.
+ For example the keys: "Windows", "SysReq", "ScrollLock", and "Break".
+ For non-US keyboards, all of the so called "World" keys are now supported
+ as well as F13, F14, F15, and the country-specific mode/meta keys.
+
+ On many international layouts the default console toggle keys are also dead
+ keys, meaning that dropping the console potentially results in
+ unintentionally initiating the keying of a dead key. Futhermore SDL 1.2's
+ dead key support is broken by design and Q3 doesn't support non-ASCII text
+ entry, so the chances are you won't get the correct character anyway.
+
+ If you use such a keyboard layout, you can set the cvar cl_consoleKeys. This
+ is a space delimited list of key names that will toggle the console. The key
+ names are the usual Q3 names e.g. "~", "`", "c", "BACKSPACE", "PAUSE",
+ "WINDOWS" etc. It's also possible to use ASCII characters, by hexadecimal
+ number. Some example values for cl_consoleKeys:
+
+ "~ ` 0x7e 0x60" Toggle on ~ or ` (the default)
+ "WINDOWS" Toggle on the Windows key
+ "c" Toggle on the c key
+ "0x43" Toggle on the C character (Shift-c)
+ "PAUSE F1 PGUP" Toggle on the Pause, F1 or Page Up keys
+
+ Note that when you elect a set of console keys or characters, they cannot
+ then be used for binding, nor will they generate characters when entering
+ text. Also, in addition to the nominated console keys, Shift-ESC is hard
+ coded to always toggle the console.
+
QuakeLive mouse acceleration (patch and this text written by TTimo from id)
I've been using an experimental mouse acceleration code for a while, and
decided to make it available to everyone. Don't be too worried if you don't
@@ -354,6 +468,9 @@
If you try the new acceleration code and start using it, I'd be very
interested by your feedback.
+
+---------------------------------------------------- README for Developers -----
+
64bit mods
If you wish to compile external mods as shared libraries on a 64bit platform,
and the mod source is derived from the id Q3 SDK, you will need to modify the
@@ -410,7 +527,7 @@
+set com_homepath <homedirname>
- to the command line. Then you can control which kind of messages to send to
+ to the command line. You can also control which kind of messages to send to
the master server:
+set sv_heartbeat <heartbeat> +set sv_flatline <flatline>
@@ -460,6 +577,35 @@
maps) created by yourself are your property and can be sold like every other
game you find in stores.
+Network protocols
+ There are now two cvars that give you some degree of freedom over the reported
+ protocol versions between clients and servers: "com_protocol" and
+ "com_legacyprotocol".
+ The reason for this is that some standalone games increased the protocol
+ number even though nothing really changed in their protocol and the ioquake3
+ engine is still fully compatible.
+
+ In order to harden the network protocol against UDP spoofing attacks a new
+ network protocol was introduced that defends against such attacks.
+ Unfortunately, this protocol will be incompatible to the original quake3 1.32c
+ which is the latest official release from id.
+ Luckily, ioquake3 has backwards compatibility, on the client as well as on the
+ server. This means ioquake3 players can play on old servers just as ioquake3
+ servers are able to service old clients.
+
+ The cvar "com_protocol" denotes the protocol version for the new hardened
+ protocol, whereas the "com_legacyprotocol" cvar denotes the protocol version
+ for the legacy protocol.
+ If the value for "com_protocol" and "com_legacyprotocol" is identical, then
+ the legacy protocol is always used. If "com_legacyprotocol" is set to 0, then
+ support for the legacy protocol is disabled.
+
+ Mods that use a standalone engine obviously do not require dual protocol
+ support, and it is turned off if the engine is compiled with STANDALONE per
+ default. If you desire backwards compatibility to older versions of your
+ game you can still enable it in q_shared.h by defining
+ LEGACY_PROTOCOL.
+
cl_guid Support
cl_guid is a cvar which is part of the client's USERINFO string. Its value
is a 32 character string made up of [a-f] and [0-9] characters. This
@@ -478,114 +624,6 @@
than just name
2) granting some weak admin rights to players without requiring passwords
-Using HTTP/FTP Download Support (Server)
- You can enable redirected downloads on your server even if it's not
- an ioquake3 server. You simply need to use the 'sets' command to put
- the sv_dlURL cvar into your SERVERINFO string and ensure sv_allowDownloads
- is set to 1
-
- sv_dlURL is the base of the URL that contains your custom .pk3 files
- the client will append both fs_game and the filename to the end of
- this value. For example, if you have sv_dlURL set to
- "http://ioquake3.org", fs_game is "baseq3", and the client is
- missing "test.pk3", it will attempt to download from the URL
- "http://ioquake3.org/baseq3/test.pk3"
-
- sv_allowDownload's value is now a bitmask made up of the following
- flags:
- 1 - ENABLE
- 4 - do not use UDP downloads
- 8 - do not ask the client to disconnect when using HTTP/FTP
-
- Server operators who are concerned about potential "leeching" from their
- HTTP servers from other ioquake3 servers can make use of the HTTP_REFERER
- that ioquake3 sets which is "ioQ3://{SERVER_IP}:{SERVER_PORT}". For,
- example, Apache's mod_rewrite can restrict access based on HTTP_REFERER.
-
- On a sidenote, downloading via UDP has been improved and yields higher data
- rates now. You can configure the maximum bandwidth for UDP downloads via the
- cvar sv_dlRate. Due to system-specific limits the download rate is capped
- at about 1 Mbyte/s per client, so curl downloading may still be faster.
-
-Using HTTP/FTP Download Support (Client)
- Simply setting cl_allowDownload to 1 will enable HTTP/FTP downloads
- assuming ioquake3 was compiled with USE_CURL=1 (the default).
- like sv_allowDownload, cl_allowDownload also uses a bitmask value
- supporting the following flags:
- 1 - ENABLE
- 2 - do not use HTTP/FTP downloads
- 4 - do not use UDP downloads
-
- When ioquake3 is built with USE_CURL_DLOPEN=1 (default on some platforms),
- it will use the value of the cvar cl_cURLLib as the filename of the cURL
- library to dynamically load.
-
-Multiuser Support on Windows systems
- On Windows, all user specific files such as autogenerated configuration,
- demos, videos, screenshots, and autodownloaded pk3s are now saved in a
- directory specific to the user who is running ioquake3.
-
- On NT-based such as Windows XP, this is usually a directory named:
- "C:\Documents and Settings\%USERNAME%\Application Data\Quake3\"
-
- Windows 95, Windows 98, and Windows ME will use a directory like:
- "C:\Windows\Application Data\Quake3"
- in single-user mode, or:
- "C:\Windows\Profiles\%USERNAME%\Application Data\Quake3"
- if multiple logins have been enabled.
-
- In order to access this directory more easily, the installer may create a
- Shortcut which has its target set to:
- "%APPDATA%\Quake3\"
- This Shortcut would work for all users on the system regardless of the
- locale settings. Unfortunately, this environment variable is only
- present on Windows NT based systems.
-
- You can revert to the old single-user behaviour by setting the fs_homepath
- cvar to the directory where ioquake3 is installed. For example:
- ioquake3.exe +set fs_homepath "c:\ioquake3"
- Note that this cvar MUST be set as a command line parameter.
-
-SDL Keyboard Differences
- ioquake3 clients have different keyboard behaviour compared to the original
- Quake3 clients.
-
- * "Caps Lock" and "Num Lock" can not be used as normal binds since they
- do not send a KEYUP event until the key is pressed again.
-
- * SDL > 1.2.9 does not support disabling dead key recognition. In order to
- send dead key characters (e.g. ~, ', `, and ^), you must key a Space (or
- sometimes the same character again) after the character to send it on
- many international keyboard layouts.
-
- * The SDL client supports many more keys than the original Quake3 client.
- For example the keys: "Windows", "SysReq", "ScrollLock", and "Break".
- For non-US keyboards, all of the so called "World" keys are now supported
- as well as F13, F14, F15, and the country-specific mode/meta keys.
-
- On many international layouts the default console toggle keys are also dead
- keys, meaning that dropping the console potentially results in
- unintentionally initiating the keying of a dead key. Futhermore SDL 1.2's
- dead key support is broken by design and Q3 doesn't support non-ASCII text
- entry, so the chances are you won't get the correct character anyway.
-
- If you use such a keyboard layout, you can set the cvar cl_consoleKeys. This
- is a space delimited list of key names that will toggle the console. The key
- names are the usual Q3 names e.g. "~", "`", "c", "BACKSPACE", "PAUSE",
- "WINDOWS" etc. It's also possible to use ASCII characters, by hexadecimal
- number. Some example values for cl_consoleKeys:
-
- "~ ` 0x7e 0x60" Toggle on ~ or ` (the default)
- "WINDOWS" Toggle on the Windows key
- "c" Toggle on the c key
- "0x43" Toggle on the C character (Shift-c)
- "PAUSE F1 PGUP" Toggle on the Pause, F1 or Page Up keys
-
- Note that when you elect a set of console keys or characters, they cannot
- then be used for binding, nor will they generate characters when entering
- text. Also, in addition to the nominated console keys, Shift-ESC is hard
- coded to always toggle the console.
-
PNG support
ioquake3 supports the use of PNG (Portable Network Graphic) images as
textures. It should be noted that the use of such images in a map will
Modified: trunk/code/client/cl_main.c
===================================================================
--- trunk/code/client/cl_main.c 2011-07-12 11:01:49 UTC (rev 2074)
+++ trunk/code/client/cl_main.c 2011-07-12 11:59:48 UTC (rev 2075)
@@ -533,7 +533,6 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
#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 @@
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 @@
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 @@
// 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(ver != com_protocol->integer)
+ {
+#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 || atoi(c) != clc.challenge)
+ if(!*c || challenge != clc.challenge)
+ {
+ Com_DPrintf("Challenge response received from unexpected source. Ignored.\n");
+ return;
+ }
+ }
+ }
+ else
+#endif
+ {
+ if(!*c || challenge != clc.challenge)
{
- Com_DPrintf("Challenge response received from unexpected source. Ignored.\n");
+ Com_Printf("Bad challenge for challengeResponse. Ignored.\n");
return;
}
}
@@ -2494,7 +2598,36 @@
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 @@
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 @@
}
// 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 @@
// 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;
}
Modified: trunk/code/client/cl_net_chan.c
===================================================================
--- trunk/code/client/cl_net_chan.c 2011-07-12 11:01:49 UTC (rev 2074)
+++ trunk/code/client/cl_net_chan.c 2011-07-12 11:59:48 UTC (rev 2075)
@@ -147,9 +147,6 @@
Netchan_Transmit( chan, msg->cursize, msg->data );
}
-extern int oldsize;
-int newsize = 0;
-
/*
=================
CL_Netchan_Process
@@ -161,7 +158,8 @@
ret = Netchan_Process( chan, msg );
if (!ret)
return qfalse;
+
CL_Netchan_Decode( msg );
- newsize += msg->cursize;
+
return qtrue;
}
Modified: trunk/code/client/client.h
===================================================================
--- trunk/code/client/client.h 2011-07-12 11:01:49 UTC (rev 2074)
+++ trunk/code/client/client.h 2011-07-12 11:59:48 UTC (rev 2075)
@@ -263,6 +263,10 @@
float voipPower;
#endif
+#ifdef LEGACY_PROTOCOL
+ qboolean compat;
+#endif
+
// big stuff at end of structure so most offsets are 15 bits or less
netchan_t netchan;
} clientConnection_t;
Modified: trunk/code/q3_ui/ui_demo2.c
===================================================================
--- trunk/code/q3_ui/ui_demo2.c 2011-07-12 11:01:49 UTC (rev 2074)
+++ trunk/code/q3_ui/ui_demo2.c 2011-07-12 11:59:48 UTC (rev 2075)
@@ -42,8 +42,8 @@
#define ART_ARROWLEFT "menu/art/arrows_horz_left"
#define ART_ARROWRIGHT "menu/art/arrows_horz_right"
-#define MAX_DEMOS 128
-#define NAMEBUFSIZE ( MAX_DEMOS * 16 )
+#define MAX_DEMOS 1024
+#define NAMEBUFSIZE (MAX_DEMOS * 32)
#define ID_BACK 10
#define ID_GO 11
@@ -72,6 +72,9 @@
int numDemos;
char names[NAMEBUFSIZE];
+ int numLegacyDemos;
+ char namesLegacy[NAMEBUFSIZE];
+
char *demolist[MAX_DEMOS];
} demos_t;
@@ -133,6 +136,7 @@
int i;
int len;
char *demoname, extension[32];
+ int protocol, protocolLegacy;
memset( &s_demos, 0 ,sizeof(demos_t) );
s_demos.menu.key = UI_DemosMenu_Key;
@@ -223,11 +227,34 @@
s_demos.list.generic.y = 130;
s_demos.list.width = 16;
s_demos.list.height = 14;
- Com_sprintf(extension, sizeof(extension), ".%s%d", DEMOEXT, (int) trap_Cvar_VariableValue("protocol"));
- s_demos.list.numitems = trap_FS_GetFileList( "demos", extension, s_demos.names, NAMEBUFSIZE );
s_demos.list.itemnames = (const char **)s_demos.demolist;
s_demos.list.columns = 3;
+ protocolLegacy = trap_Cvar_VariableValue("com_legacyprotocol");
+ protocol = trap_Cvar_VariableValue("com_protocol");
+
+ if(!protocol)
+ protocol = trap_Cvar_VariableValue("protocol");
+ if(protocolLegacy == protocol)
+ protocolLegacy = 0;
+
+ Com_sprintf(extension, sizeof(extension), ".%s%d", DEMOEXT, protocol);
+ s_demos.numDemos = trap_FS_GetFileList("demos", extension, s_demos.names, NAMEBUFSIZE);
+
+ if(s_demos.numDemos > MAX_DEMOS)
+ s_demos.numDemos = MAX_DEMOS;
+
+ if(protocolLegacy > 0)
+ {
+ Com_sprintf(extension, sizeof(extension), ".%s%d", DEMOEXT, protocolLegacy);
+ s_demos.numLegacyDemos = trap_FS_GetFileList("demos", extension, s_demos.namesLegacy, NAMEBUFSIZE);
+ }
+ else
+ s_demos.numLegacyDemos = 0;
+
+ s_demos.list.numitems = s_demos.numDemos + s_demos.numLegacyDemos;
+
+
if (!s_demos.list.numitems) {
strcpy( s_demos.names, "No Demos Found." );
s_demos.list.numitems = 1;
@@ -239,16 +266,22 @@
s_demos.list.numitems = MAX_DEMOS;
demoname = s_demos.names;
- for ( i = 0; i < s_demos.list.numitems; i++ ) {
+ for(i = 0; i < s_demos.numDemos; i++)
+ {
s_demos.list.itemnames[i] = demoname;
- // strip extension
- len = strlen( demoname );
- if (!Q_stricmp(demoname + len - 4,".dm3"))
- demoname[len-4] = '\0';
+ len = strlen(demoname);
-// Q_strupr(demoname);
+ demoname += len + 1;
+ }
+ demoname = s_demos.namesLegacy;
+ for(; i < s_demos.list.numitems; i++)
+ {
+ s_demos.list.itemnames[i] = demoname;
+
+ len = strlen(demoname);
+
demoname += len + 1;
}
Modified: trunk/code/qcommon/common.c
===================================================================
--- trunk/code/qcommon/common.c 2011-07-12 11:01:49 UTC (rev 2074)
+++ trunk/code/qcommon/common.c 2011-07-12 11:59:48 UTC (rev 2075)
@@ -32,7 +32,7 @@
#endif
int demo_protocols[] =
-{ 66, 67, 68, 0 };
+{ 67, 66, 0 };
#define MAX_NUM_ARGVS 50
@@ -87,6 +87,9 @@
cvar_t *com_abnormalExit;
cvar_t *com_standalone;
cvar_t *com_protocol;
+#ifdef LEGACY_PROTOCOL
+cvar_t *com_legacyprotocol;
+#endif
cvar_t *com_basegame;
cvar_t *com_homepath;
cvar_t *com_busyWait;
@@ -2796,8 +2799,17 @@
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);
+ com_protocol = Cvar_Get("com_protocol", va("%i", PROTOCOL_VERSION), CVAR_SERVERINFO | CVAR_INIT);
+#ifdef LEGACY_PROTOCOL
+ com_legacyprotocol = Cvar_Get("com_legacyprotocol", va("%i", PROTOCOL_LEGACY_VERSION), CVAR_INIT);
+ // Keep for compatibility with old mods / mods that haven't updated yet.
+ if(com_legacyprotocol->integer > 0)
+ Cvar_Get("protocol", com_legacyprotocol->string, CVAR_ROM);
+ else
+#endif
+ Cvar_Get("protocol", com_protocol->string, CVAR_ROM);
+
Sys_Init();
if( Sys_WritePIDFile( ) ) {
Modified: trunk/code/qcommon/files.c
===================================================================
--- trunk/code/qcommon/files.c 2011-07-12 11:01:49 UTC (rev 2074)
+++ trunk/code/qcommon/files.c 2011-07-12 11:59:48 UTC (rev 2075)
@@ -1056,6 +1056,11 @@
if(protocol == com_protocol->integer)
return qtrue;
+#ifdef LEGACY_PROTOCOL
+ if(protocol == com_legacyprotocol->integer)
+ return qtrue;
+#endif
+
for(index = 0; demo_protocols[index]; index++)
{
if(demo_protocols[index] == protocol)
Modified: trunk/code/qcommon/net_chan.c
===================================================================
--- trunk/code/qcommon/net_chan.c 2011-07-12 11:01:49 UTC (rev 2074)
+++ trunk/code/qcommon/net_chan.c 2011-07-12 11:59:48 UTC (rev 2075)
@@ -83,7 +83,8 @@
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, qboolean compat)
+{
Com_Memset (chan, 0, sizeof(*chan));
chan->sock = sock;
@@ -91,6 +92,11 @@
chan->qport = qport;
chan->incomingSequence = 0;
chan->outgoingSequence = 1;
+ chan->challenge = challenge;
+
+#ifdef LEGACY_PROTOCOL
+ chan->compat = compat;
+#endif
}
// TTimo: unused, commenting out to make gcc happy
@@ -190,17 +196,24 @@
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 LEGACY_PROTOCOL
+ 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,13 +281,18 @@
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 LEGACY_PROTOCOL
+ if(!chan->compat)
+#endif
+ MSG_WriteLong(&send, NETCHAN_GENCHECKSUM(chan->challenge, chan->outgoingSequence));
+
+ chan->outgoingSequence++;
+
MSG_WriteData( &send, data, length );
// send the datagram
@@ -327,6 +345,17 @@
qport = MSG_ReadShort( msg );
}
+#ifdef LEGACY_PROTOCOL
+ 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 );
Modified: trunk/code/qcommon/q_shared.h
===================================================================
--- trunk/code/qcommon/q_shared.h 2011-07-12 11:01:49 UTC (rev 2074)
+++ trunk/code/qcommon/q_shared.h 2011-07-12 11:59:48 UTC (rev 2075)
@@ -37,6 +37,7 @@
#define HOMEPATH_NAME_UNIX ".foo"
#define HOMEPATH_NAME_WIN "FooBar"
#define HOMEPATH_NAME_MACOSX HOMEPATH_NAME_WIN
+// #define LEGACY_PROTOCOL // You probably don't need this for your standalone game
#else
#define PRODUCT_NAME "ioq3"
#define BASEGAME "baseq3"
@@ -48,6 +49,7 @@
#define HOMEPATH_NAME_UNIX ".q3a"
#define HOMEPATH_NAME_WIN "Quake3"
#define HOMEPATH_NAME_MACOSX HOMEPATH_NAME_WIN
+ #define LEGACY_PROTOCOL
#endif
#define BASETA "missionpack"
Modified: trunk/code/qcommon/qcommon.h
===================================================================
--- trunk/code/qcommon/qcommon.h 2011-07-12 11:01:49 UTC (rev 2074)
+++ trunk/code/qcommon/qcommon.h 2011-07-12 11:59:48 UTC (rev 2075)
@@ -195,8 +195,9 @@
#define MAX_DOWNLOAD_WINDOW 48 // ACK window of 48 download chunks. Cannot set this higher, or clients
// will overflow the reliable commands buffer
#define MAX_DOWNLOAD_BLKSIZE 1024 // 896 byte block chunks
-
+#define NETCHAN_GENCHECKSUM(challenge, sequence) ((challenge) ^ ((sequence) * (challenge)))
+
/*
Netchan handles packet fragmentation and out of order / duplicate suppression
*/
@@ -224,10 +225,16 @@
int unsentFragmentStart;
int unsentLength;
byte unsentBuffer[MAX_MSGLEN];
+
+ int challenge;
+
+#ifdef LEGACY_PROTOCOL
+ 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, qboolean compat);
void Netchan_Transmit( netchan_t *chan, int length, const byte *data );
void Netchan_TransmitNextFragment( netchan_t *chan );
@@ -243,7 +250,8 @@
==============================================================
*/
-#define PROTOCOL_VERSION 68
+#define PROTOCOL_VERSION 69
+#define PROTOCOL_LEGACY_VERSION 68
// 1.31 - 67
// maintain a list of compatible protocols for demo playing
@@ -863,6 +871,9 @@
extern cvar_t *sv_packetdelay;
extern cvar_t *com_protocol;
+#ifdef LEGACY_PROTOCOL
+extern cvar_t *com_legacyprotocol;
+#endif
// com_speeds times
extern int time_game;
Modified: trunk/code/server/server.h
===================================================================
--- trunk/code/server/server.h 2011-07-12 11:01:49 UTC (rev 2074)
+++ trunk/code/server/server.h 2011-07-12 11:59:48 UTC (rev 2075)
@@ -188,7 +188,11 @@
#endif
int oldServerTime;
- qboolean csUpdated[MAX_CONFIGSTRINGS+1];
+ qboolean csUpdated[MAX_CONFIGSTRINGS+1];
+
+#ifdef LEGACY_PROTOCOL
+ qboolean compat;
+#endif
} client_t;
//=============================================================================
Modified: trunk/code/server/sv_client.c
===================================================================
--- trunk/code/server/sv_client.c 2011-07-12 11:01:49 UTC (rev 2074)
+++ trunk/code/server/sv_client.c 2011-07-12 11:59:48 UTC (rev 2075)
@@ -59,12 +59,25 @@
int clientChallenge;
challenge_t *challenge;
qboolean wasfound = qfalse;
+ char *gameName;
// ignore if we are in single player
if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive")) {
return;
}
+ gameName = Cmd_Argv(2);
+ if(gameName && *gameName)
+ {
+ // reject client if the heartbeat string sent by the client doesn't match ours
+ if(strcmp(gameName, sv_heartbeat->string))
+ {
+ NET_OutOfBandPrint(NS_SERVER, from, "print\nGame mismatch: This is a %s server\n",
+ sv_heartbeat->string);
+ return;
+ }
+ }
+
oldest = 0;
oldestClientTime = oldestTime = 0x7fffffff;
@@ -302,6 +315,9 @@
intptr_t denied;
int count;
char *ip;
+#ifdef LEGACY_PROTOCOL
+ qboolean compat = qfalse;
+#endif
Com_DPrintf ("SVC_DirectConnect ()\n");
@@ -314,11 +330,21 @@
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 LEGACY_PROTOCOL
+ if(version > 0 && com_legacyprotocol->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" ) );
@@ -500,7 +526,12 @@
newcl->challenge = challenge;
// save the address
- Netchan_Setup (NS_SERVER, &newcl->netchan , from, qport);
+#ifdef LEGACY_PROTOCOL
+ newcl->compat = compat;
+ Netchan_Setup(NS_SERVER, &newcl->netchan, from, qport, challenge, compat);
+#else
+ Netchan_Setup(NS_SERVER, &newcl->netchan, from, qport, challenge, qfalse);
+#endif
// init the netchan queue
newcl->netchan_end_queue = &newcl->netchan_start_queue;
@@ -521,7 +552,7 @@
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 );
Modified: trunk/code/server/sv_main.c
===================================================================
--- trunk/code/server/sv_main.c 2011-07-12 11:01:49 UTC (rev 2074)
+++ trunk/code/server/sv_main.c 2011-07-12 11:59:48 UTC (rev 2075)
@@ -643,7 +643,13 @@
// to prevent timed spoofed reply packets that add ghost servers
Info_SetValueForKey( infostring, "challenge", Cmd_Argv(1) );
- Info_SetValueForKey( infostring, "protocol", va("%i", com_protocol->integer) );
+#ifdef LEGACY_PROTOCOL
+ if(com_legacyprotocol->integer > 0)
+ Info_SetValueForKey(infostring, "protocol", va("%i", com_legacyprotocol->integer));
+ else
+#endif
+ Info_SetValueForKey(infostring, "protocol", va("%i", com_protocol->integer));
+
Info_SetValueForKey( infostring, "hostname", sv_hostname->string );
Info_SetValueForKey( infostring, "mapname", sv_mapname->string );
Info_SetValueForKey( infostring, "clients", va("%i", count) );
@@ -868,10 +874,6 @@
}
return;
}
-
- // if we received a sequenced packet from an address we don't recognize,
- // send an out of band disconnect packet to it
- NET_OutOfBandPrint( NS_SERVER, from, "disconnect" );
}
More information about the quake3-commits
mailing list