[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