Index: code/client/client.h
===================================================================
--- code/client/client.h	2011-04-27 16:17:19.904299000 -0500
+++ code/client/client.h	2011-04-27 16:41:05.087963000 -0500
@@ -260,6 +260,10 @@
 	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;
Index: code/client/cl_main.c
===================================================================
--- code/client/cl_main.c	2011-04-27 16:27:58.121838000 -0500
+++ code/client/cl_main.c	2011-04-27 16:41:05.099668000 -0500
@@ -635,6 +635,11 @@
 	if ( Cmd_Argc() == 2 ) {
 		s = Cmd_Argv(1);
 		Q_strncpyz( demoName, s, sizeof( demoName ) );
+#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;
@@ -642,6 +647,11 @@
 		// scan for a free demo name
 		for ( number = 0 ; number <= 9999 ; number++ ) {
 			CL_DemoFilename( number, demoName );
+#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))
@@ -892,6 +902,22 @@
 	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);
 
@@ -900,11 +926,16 @@
 		Com_Printf("Demo file: %s\n", name);
 		return com_protocol->integer;
 	}
+	}
 
 	Com_Printf("Not found: %s\n", name);
 
 	while(demo_protocols[i])
 	{
+#ifdef PROTOCOL_SUPPORT_OLD
+		if(demo_protocols[i] == com_oldprotocol->integer)
+			continue;
+#endif
 		if(demo_protocols[i] == com_protocol->integer)
 			continue;
 
@@ -981,7 +1012,11 @@
 				break;
 		}
 
-		if(demo_protocols[i] || protocol == com_protocol->integer)
+		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);
@@ -998,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);
@@ -1016,6 +1051,13 @@
 	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();
@@ -2156,6 +2198,15 @@
 		port = Cvar_VariableValue ("net_qport");
 
 		Q_strncpyz( info, Cvar_InfoString( CVAR_USERINFO ), sizeof( info ) );
+		
+#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 ) );
@@ -2423,6 +2474,18 @@
 		if(*c)
 			challenge = atoi(c);
 		
+#ifdef PROTOCOL_SUPPORT_OLD
+		if(!clc.compat)
+		{
+			if(!*c || challenge != clc.challenge)
+			{
+				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.
@@ -2435,6 +2498,7 @@
 				return;
 			}
 		}
+		}
 
 		// start sending challenge response instead of challenge request packets
 		clc.challenge = atoi(Cmd_Argv(1));
@@ -2464,7 +2528,32 @@
 			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
@@ -2505,6 +2594,27 @@
 	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 );
 
@@ -3449,7 +3559,11 @@
 	// 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;
Index: code/qcommon/common.c
===================================================================
--- code/qcommon/common.c	2011-04-27 16:17:38.101853000 -0500
+++ code/qcommon/common.c	2011-04-27 16:41:05.084544000 -0500
@@ -32,7 +32,7 @@
 #endif
 
 int demo_protocols[] =
-{ 66, 67, 68, 0 };
+{ 67, 66, 0 };
 
 #define MAX_NUM_ARGVS	50
 
@@ -86,6 +86,9 @@
 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 @@
 	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();
 
Index: code/qcommon/files.c
===================================================================
--- code/qcommon/files.c	2011-04-27 16:17:46.951955000 -0500
+++ code/qcommon/files.c	2011-04-27 16:41:05.071416000 -0500
@@ -1030,6 +1030,11 @@
 		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)
Index: code/qcommon/net_chan.c
===================================================================
--- code/qcommon/net_chan.c	2011-04-27 16:10:45.727408000 -0500
+++ code/qcommon/net_chan.c	2011-04-27 16:41:05.051095000 -0500
@@ -83,7 +83,11 @@
 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));
 	
@@ -92,6 +96,10 @@
 	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
@@ -204,6 +212,11 @@
 		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 ) {
@@ -276,6 +289,11 @@
 	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 );
@@ -330,6 +348,17 @@
 		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 );
Index: code/qcommon/qcommon.h
===================================================================
--- code/qcommon/qcommon.h	2011-04-27 16:19:13.089009000 -0500
+++ code/qcommon/qcommon.h	2011-04-27 16:41:05.076983000 -0500
@@ -195,6 +195,8 @@
 #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
 */
@@ -222,10 +224,20 @@
 	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 );
@@ -241,7 +253,8 @@
 ==============================================================
 */
 
-#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
@@ -856,6 +869,9 @@
 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;
Index: code/qcommon/q_shared.h
===================================================================
--- code/qcommon/q_shared.h	2011-04-27 16:18:22.441883000 -0500
+++ code/qcommon/q_shared.h	2011-04-27 16:41:05.059512000 -0500
@@ -34,6 +34,7 @@
   #define GAMENAME_FOR_MASTER		"iofoo3"		// 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			"ioq3"
   #define BASEGAME			"baseq3"
@@ -42,6 +43,7 @@
   #define GAMENAME_FOR_MASTER		"Quake3Arena"
   #define HEARTBEAT_FOR_MASTER		"QuakeArena-1"
   #define FLATLINE_FOR_MASTER		HEARTBEAT_FOR_MASTER
+  #define PROTOCOL_SUPPORT_OLD
 #endif
 
 #define BASETA				"missionpack"
Index: code/server/server.h
===================================================================
--- code/server/server.h	2011-04-27 16:19:25.983433000 -0500
+++ code/server/server.h	2011-04-27 16:41:05.048389000 -0500
@@ -189,6 +189,10 @@
 
 	int				oldServerTime;
 	qboolean		csUpdated[MAX_CONFIGSTRINGS+1];	
+	
+#ifdef PROTOCOL_SUPPORT_OLD
+	qboolean		compat;
+#endif
 } client_t;
 
 //=============================================================================
Index: code/server/sv_client.c
===================================================================
--- code/server/sv_client.c	2011-04-27 16:36:36.852664000 -0500
+++ code/server/sv_client.c	2011-04-27 16:41:05.045123000 -0500
@@ -301,6 +301,9 @@
 	intptr_t		denied;
 	int			count;
 	char		*ip;
+#ifdef PROTOCOL_SUPPORT_OLD
+	qboolean	compat = qfalse;
+#endif
 
 	Com_DPrintf ("SVC_DirectConnect ()\n");
 	
@@ -315,6 +318,12 @@
 
 	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 "
@@ -322,6 +331,7 @@
 		Com_DPrintf("    rejected connect from version %i\n", version);
 		return;
 	}
+	}
 
 	challenge = atoi( Info_ValueForKey( userinfo, "challenge" ) );
 	qport = atoi( Info_ValueForKey( userinfo, "qport" ) );
@@ -502,7 +512,12 @@
 	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;
 
@@ -523,7 +538,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 );
 
Index: README
===================================================================
--- README	2011-04-27 16:09:52.377222000 -0500
+++ README	2011-04-27 16:41:05.039575000 -0500
@@ -214,9 +214,15 @@
                                       ipv6 servers on the local network
   net_mcastiface                    - outgoing interface to use for scan
 
-  protocol                          - Allow changing protocol version
+  oldprotocol                       - when encountering a server/client that
+                                      only supports the version configured in
+                                      this cvar, ioquake3 will use the old and
+                                      less secure protocol from quake3 1.32c.
                                       (startup only)
 
+  protocol                          - Allow changing protocol version that is
+                                      sent to the server (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
@@ -563,6 +569,36 @@
   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: "protocol" and "oldprotocol".
+  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 fix a vulnerability in the network protocol as outlined in
+  
+    http://aluigi.altervista.org/papers/q3noclient.txt
+    
+  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 "protocol" denotes the protocol version for the new hardened
+  protocol, whereas the "oldprotocol" cvar denotes the protocol version for the
+  legacy protocol.
+  If the value for "oldprotocol" and "protocol" is identical, then the legacy
+  protocol is always used. If oldprotocol 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. You can enable it in q_shared.h if desired by defining
+  PROTOCOL_SUPPORT_OLD.
+
 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