]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - netconn.c
Remove the CONFIG_CD macro, and enable faketracks unconditionally.
[xonotic/darkplaces.git] / netconn.c
index b27a4d8b8b60c6fc100e6a899297eb4ee1b490e6..342d78b253cdb623d36ad5c1c9fee34b025bf970 100755 (executable)
--- a/netconn.c
+++ b/netconn.c
@@ -21,6 +21,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
 
 #include "quakedef.h"
+#include "thread.h"
 #include "lhnet.h"
 
 // for secure rcon authentication
@@ -35,6 +36,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 cvar_t sv_public = {0, "sv_public", "0", "1: advertises this server on the master server (so that players can find it in the server browser); 0: allow direct queries only; -1: do not respond to direct queries; -2: do not allow anyone to connect; -3: already block at getchallenge level"};
 cvar_t sv_public_rejectreason = {0, "sv_public_rejectreason", "The server is closing.", "Rejection reason for connects when sv_public is -2"};
 static cvar_t sv_heartbeatperiod = {CVAR_SAVE, "sv_heartbeatperiod", "120", "how often to send heartbeat in seconds (only used if sv_public is 1)"};
+extern cvar_t sv_status_privacy;
 
 static cvar_t sv_masters [] =
 {
@@ -42,15 +44,13 @@ static cvar_t sv_masters [] =
        {CVAR_SAVE, "sv_master2", "", "user-chosen master server 2"},
        {CVAR_SAVE, "sv_master3", "", "user-chosen master server 3"},
        {CVAR_SAVE, "sv_master4", "", "user-chosen master server 4"},
-       {0, "sv_masterextra1", "69.59.212.88", "ghdigital.com - default master server 1 (admin: LordHavoc)"}, // admin: LordHavoc
-       {0, "sv_masterextra2", "64.22.107.125", "dpmaster.deathmask.net - default master server 2 (admin: Willis)"}, // admin: Willis
-       {0, "sv_masterextra3", "92.62.40.73", "dpmaster.tchr.no - default master server 3 (admin: tChr)"}, // admin: tChr
-#ifdef SUPPORTIPV6
-       {0, "sv_masterextra4", "[2001:41d0:2:1628::4450]:27950", "dpmaster.div0.qc.to - default master server 4 (admin: divVerent)"}, // admin: divVerent
-#endif
+       {0, "sv_masterextra1", "ghdigital.com", "ghdigital.com - default master server 1 (admin: LordHavoc)"}, // admin: LordHavoc
+       {0, "sv_masterextra2", "dpmaster.deathmask.net", "dpmaster.deathmask.net - default master server 2 (admin: Willis)"}, // admin: Willis
+       {0, "sv_masterextra3", "dpmaster.tchr.no", "dpmaster.tchr.no - default master server 3 (admin: tChr)"}, // admin: tChr
        {0, NULL, NULL, NULL}
 };
 
+#ifdef CONFIG_MENU
 static cvar_t sv_qwmasters [] =
 {
        {CVAR_SAVE, "sv_qwmaster1", "", "user-chosen qwmaster server 1"},
@@ -60,19 +60,29 @@ static cvar_t sv_qwmasters [] =
        {0, "sv_qwmasterextra1", "master.quakeservers.net:27000", "Global master server. (admin: unknown)"},
        {0, "sv_qwmasterextra2", "asgaard.morphos-team.net:27000", "Global master server. (admin: unknown)"},
        {0, "sv_qwmasterextra3", "qwmaster.ocrana.de:27000", "German master server. (admin: unknown)"},
-       {0, "sv_qwmasterextra4", "masterserver.exhale.de:27000", "German master server. (admin: unknown)"},
-       {0, "sv_qwmasterextra5", "kubus.rulez.pl:27000", "Poland master server. (admin: unknown)"},
+       {0, "sv_qwmasterextra4", "qwmaster.fodquake.net:27000", "Global master server. (admin: unknown)"},
        {0, NULL, NULL, NULL}
 };
+#endif
 
 static double nextheartbeattime = 0;
 
-sizebuf_t net_message;
-static unsigned char net_message_buf[NET_MAXMESSAGE];
+sizebuf_t cl_message;
+sizebuf_t sv_message;
+static unsigned char cl_message_buf[NET_MAXMESSAGE];
+static unsigned char sv_message_buf[NET_MAXMESSAGE];
+char cl_readstring[MAX_INPUTLINE];
+char sv_readstring[MAX_INPUTLINE];
 
+cvar_t net_test = {0, "net_test", "0", "internal development use only, leave it alone (usually does nothing anyway)"};
+cvar_t net_usesizelimit = {0, "net_usesizelimit", "2", "use packet size limiting (0: never, 1: in non-CSQC mode, 2: always)"};
+cvar_t net_burstreserve = {0, "net_burstreserve", "0.3", "how much of the burst time to reserve for packet size spikes"};
 cvar_t net_messagetimeout = {0, "net_messagetimeout","300", "drops players who have not sent any packets for this many seconds"};
 cvar_t net_connecttimeout = {0, "net_connecttimeout","15", "after requesting a connection, the client must reply within this many seconds or be dropped (cuts down on connect floods). Must be above 10 seconds."};
-cvar_t net_connectfloodblockingtimeout = {0, "net_connectfloodblockingtimeout", "5", "when a connection packet is received, it will block all future connect packets from that IP address for this many seconds (cuts down on connect floods)"};
+cvar_t net_connectfloodblockingtimeout = {0, "net_connectfloodblockingtimeout", "5", "when a connection packet is received, it will block all future connect packets from that IP address for this many seconds (cuts down on connect floods). Note that this does not include retries from the same IP; these are handled earlier and let in."};
+cvar_t net_challengefloodblockingtimeout = {0, "net_challengefloodblockingtimeout", "0.5", "when a challenge packet is received, it will block all future challenge packets from that IP address for this many seconds (cuts down on challenge floods). DarkPlaces clients retry once per second, so this should be <= 1. Failure here may lead to connect attempts failing."};
+cvar_t net_getstatusfloodblockingtimeout = {0, "net_getstatusfloodblockingtimeout", "1", "when a getstatus packet is received, it will block all future getstatus packets from that IP address for this many seconds (cuts down on getstatus floods). DarkPlaces retries every 4 seconds, and qstat retries once per second, so this should be <= 1. Failure here may lead to server not showing up in the server list."};
+cvar_t net_sourceaddresscheck = {0, "net_sourceaddresscheck", "1", "compare the source IP address for replies (more secure, may break some bad multihoming setups"};
 cvar_t hostname = {CVAR_SAVE, "hostname", "UNNAMED", "server message to show in server browser"};
 cvar_t developer_networking = {0, "developer_networking", "0", "prints all received and sent packets (recommended only for debugging)"};
 
@@ -85,6 +95,7 @@ static cvar_t net_slist_timeout = {0, "net_slist_timeout", "4", "how long to lis
 static cvar_t net_slist_pause = {0, "net_slist_pause", "0", "when set to 1, the server list won't update until it is set back to 0"};
 static cvar_t net_slist_maxtries = {0, "net_slist_maxtries", "3", "how many times to ask the same server for information (more times gives better ping reports but takes longer)"};
 static cvar_t net_slist_favorites = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "net_slist_favorites", "", "contains a list of IP addresses and ports to always query explicitly"};
+static cvar_t net_tos_dscp = {CVAR_SAVE, "net_tos_dscp", "32", "DiffServ Codepoint for network sockets (may need game restart to apply)"};
 static cvar_t gameversion = {0, "gameversion", "0", "version of game data (mod-specific) to be sent to querying clients"};
 static cvar_t gameversion_min = {0, "gameversion_min", "-1", "minimum version of game data (mod-specific), when client and server gameversion mismatch in the server browser the server is shown as incompatible; if -1, gameversion is used alone"};
 static cvar_t gameversion_max = {0, "gameversion_max", "-1", "maximum version of game data (mod-specific), when client and server gameversion mismatch in the server browser the server is shown as incompatible; if -1, gameversion is used alone"};
@@ -94,24 +105,15 @@ static cvar_t rcon_secure_maxdiff = {0, "rcon_secure_maxdiff", "5", "maximum tim
 extern cvar_t rcon_secure;
 extern cvar_t rcon_secure_challengetimeout;
 
-/* statistic counters */
-static int packetsSent = 0;
-static int packetsReSent = 0;
-static int packetsReceived = 0;
-static int receivedDuplicateCount = 0;
-static int droppedDatagrams = 0;
-
-static int unreliableMessagesSent = 0;
-static int unreliableMessagesReceived = 0;
-static int reliableMessagesSent = 0;
-static int reliableMessagesReceived = 0;
-
 double masterquerytime = -1000;
 int masterquerycount = 0;
 int masterreplycount = 0;
 int serverquerycount = 0;
 int serverreplycount = 0;
 
+challenge_t challenges[MAX_CHALLENGES];
+
+#ifdef CONFIG_MENU
 /// this is only false if there are still servers left to query
 static qboolean serverlist_querysleep = true;
 static qboolean serverlist_paused = false;
@@ -119,9 +121,7 @@ static qboolean serverlist_paused = false;
 /// reply is received, to avoid issuing queries while master replies are still
 /// flooding in (which would make a mess of the ping times)
 static double serverlist_querywaittime = 0;
-
-static unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
-static unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
+#endif
 
 static int cl_numsockets;
 static lhnetsocket_t *cl_sockets[16];
@@ -130,6 +130,7 @@ static lhnetsocket_t *sv_sockets[16];
 
 netconn_t *netconn_list = NULL;
 mempool_t *netconn_mempool = NULL;
+void *netconn_mutex = NULL;
 
 cvar_t cl_netport = {0, "cl_port", "0", "forces client to use chosen port number if not 0"};
 cvar_t sv_netport = {0, "port", "26000", "server port for players to connect to"};
@@ -144,6 +145,7 @@ char sv_net_extresponse[NET_EXTRESPONSE_MAX][1400];
 int sv_net_extresponse_count = 0;
 int sv_net_extresponse_last = 0;
 
+#ifdef CONFIG_MENU
 // ServerList interface
 serverlist_mask_t serverlist_andmasks[SERVERLIST_ANDMASKCOUNT];
 serverlist_mask_t serverlist_ormasks[SERVERLIST_ORMASKCOUNT];
@@ -162,16 +164,29 @@ qboolean serverlist_consoleoutput;
 
 static int nFavorites = 0;
 static lhnetaddress_t favorites[MAX_FAVORITESERVERS];
+static int nFavorites_idfp = 0;
+static char favorites_idfp[MAX_FAVORITESERVERS][FP64_SIZE+1];
 
 void NetConn_UpdateFavorites(void)
 {
        const char *p;
        nFavorites = 0;
+       nFavorites_idfp = 0;
        p = net_slist_favorites.string;
        while((size_t) nFavorites < sizeof(favorites) / sizeof(*favorites) && COM_ParseToken_Console(&p))
        {
-               if(LHNETADDRESS_FromString(&favorites[nFavorites], com_token, 26000))
-                       ++nFavorites;
+               if(com_token[0] != '[' && strlen(com_token) == FP64_SIZE && !strchr(com_token, '.'))
+               // currently 44 bytes, longest possible IPv6 address: 39 bytes, so this works
+               // (if v6 address contains port, it must start with '[')
+               {
+                       strlcpy(favorites_idfp[nFavorites_idfp], com_token, sizeof(favorites_idfp[nFavorites_idfp]));
+                       ++nFavorites_idfp;
+               }
+               else 
+               {
+                       if(LHNETADDRESS_FromString(&favorites[nFavorites], com_token, 26000))
+                               ++nFavorites;
+               }
        }
 }
 
@@ -205,12 +220,19 @@ static qboolean _ServerList_Entry_Compare( serverlist_entry_t *A, serverlist_ent
 {
        int result = 0; // > 0 if for numbers A > B and for text if A < B
 
-       if( serverlist_sortflags & SLSF_FAVORITESFIRST )
+       if( serverlist_sortflags & SLSF_CATEGORIES )
+       {
+               result = A->info.category - B->info.category;
+               if (result != 0)
+                       return result < 0;
+       }
+
+       if( serverlist_sortflags & SLSF_FAVORITES )
        {
                if(A->info.isfavorite != B->info.isfavorite)
                        return A->info.isfavorite;
        }
-
+       
        switch( serverlist_sortbyfield ) {
                case SLIF_PING:
                        result = A->info.ping - B->info.ping;
@@ -251,6 +273,9 @@ static qboolean _ServerList_Entry_Compare( serverlist_entry_t *A, serverlist_ent
                case SLIF_QCSTATUS:
                        result = strcasecmp( B->info.qcstatus, A->info.qcstatus ); // not really THAT useful, though
                        break;
+               case SLIF_CATEGORY:
+                       result = A->info.category - B->info.category;
+                       break;
                case SLIF_ISFAVORITE:
                        result = !!B->info.isfavorite - !!A->info.isfavorite;
                        break;
@@ -378,6 +403,8 @@ static qboolean _ServerList_Entry_Mask( serverlist_mask_t *mask, serverlist_info
        if( *mask->info.players
                && !_ServerList_CompareStr( info->players, mask->tests[SLIF_PLAYERS], mask->info.players ) )
                return false;
+       if( !_ServerList_CompareInt( info->category, mask->tests[SLIF_CATEGORY], mask->info.category ) )
+               return false;
        if( !_ServerList_CompareInt( info->isfavorite, mask->tests[SLIF_ISFAVORITE], mask->info.isfavorite ))
                return false;
        return true;
@@ -395,8 +422,8 @@ static void ServerList_ViewList_Insert( serverlist_entry_t *entry )
                !(
                           gameversion_min.integer >= 0 // min/max range set by user/mod?
                        && gameversion_max.integer >= 0
-                       && gameversion_min.integer >= entry->info.gameversion // version of server in min/max range?
-                       && gameversion_max.integer <= entry->info.gameversion
+                       && gameversion_min.integer <= entry->info.gameversion // version of server in min/max range?
+                       && gameversion_max.integer >= entry->info.gameversion
                 )
        )
                return;
@@ -405,6 +432,7 @@ static void ServerList_ViewList_Insert( serverlist_entry_t *entry )
        entry->info.isfavorite = false;
        if(LHNETADDRESS_FromString(&addr, entry->info.cname, 26000))
        {
+               char idfp[FP64_SIZE+1];
                for(i = 0; i < nFavorites; ++i)
                {
                        if(LHNETADDRESS_Compare(&addr, &favorites[i]) == 0)
@@ -413,8 +441,22 @@ static void ServerList_ViewList_Insert( serverlist_entry_t *entry )
                                break;
                        }
                }
+               if(Crypto_RetrieveHostKey(&addr, 0, NULL, 0, idfp, sizeof(idfp), NULL, NULL))
+               {
+                       for(i = 0; i < nFavorites_idfp; ++i)
+                       {
+                               if(!strcmp(idfp, favorites_idfp[i]))
+                               {
+                                       entry->info.isfavorite = true;
+                                       break;
+                               }
+                       }
+               }
        }
 
+       // refresh the "category"
+       entry->info.category = MR_GetServerListEntryCategory(entry);
+
        // FIXME: change this to be more readable (...)
        // now check whether it passes through the masks
        for( start = 0 ; start < SERVERLIST_ANDMASKCOUNT && serverlist_andmasks[start].active; start++ )
@@ -562,13 +604,19 @@ void ServerList_QueryList(qboolean resetcache, qboolean querydp, qboolean queryq
 
        NetConn_QueryMasters(querydp, queryqw);
 }
+#endif
 
 // rest
 
 int NetConn_Read(lhnetsocket_t *mysocket, void *data, int maxlength, lhnetaddress_t *peeraddress)
 {
-       int length = LHNET_Read(mysocket, data, maxlength, peeraddress);
+       int length;
        int i;
+       if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
+               Thread_LockMutex(netconn_mutex);
+       length = LHNET_Read(mysocket, data, maxlength, peeraddress);
+       if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
+               Thread_UnlockMutex(netconn_mutex);
        if (length == 0)
                return 0;
        if (cl_netpacketloss_receive.integer)
@@ -599,7 +647,11 @@ int NetConn_Write(lhnetsocket_t *mysocket, const void *data, int length, const l
                for (i = 0;i < cl_numsockets;i++)
                        if (cl_sockets[i] == mysocket && (rand() % 100) < cl_netpacketloss_send.integer)
                                return length;
+       if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
+               Thread_LockMutex(netconn_mutex);
        ret = LHNET_Write(mysocket, data, length, peeraddress);
+       if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
+               Thread_UnlockMutex(netconn_mutex);
        if (developer_networking.integer)
        {
                char addressstring[128], addressstring2[128];
@@ -624,6 +676,7 @@ qboolean NetConn_CanSend(netconn_t *conn)
        conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
        conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes   = NETGRAPH_NOPACKET;
        conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes        = NETGRAPH_NOPACKET;
+       conn->outgoing_netgraph[conn->outgoing_packetcounter].cleartime       = conn->cleartime;
        if (realtime > conn->cleartime)
                return true;
        else
@@ -633,9 +686,49 @@ qboolean NetConn_CanSend(netconn_t *conn)
        }
 }
 
-int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolversion_t protocol, int rate, qboolean quakesignon_suppressreliables)
+static void NetConn_UpdateCleartime(double *cleartime, int rate, int burstsize, int len)
+{
+       double bursttime = burstsize / (double)rate;
+
+       // delay later packets to obey rate limit
+       if (*cleartime < realtime - bursttime)
+               *cleartime = realtime - bursttime;
+       *cleartime = *cleartime + len / (double)rate;
+
+       // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
+       if (net_test.integer)
+       {
+               if (*cleartime < realtime)
+                       *cleartime = realtime;
+       }
+}
+
+static int NetConn_AddCryptoFlag(crypto_t *crypto)
+{
+       // HACK: if an encrypted connection is used, randomly set some unused
+       // flags. When AES encryption is enabled, that will make resends differ
+       // from the original, so that e.g. substring filters in a router/IPS
+       // are unlikely to match a second time. See also "startkeylogger".
+       int flag = 0;
+       if (crypto->authenticated)
+       {
+               // Let's always set at least one of the bits.
+               int r = rand() % 7 + 1;
+               if (r & 1)
+                       flag |= NETFLAG_CRYPTO0;
+               if (r & 2)
+                       flag |= NETFLAG_CRYPTO1;
+               if (r & 4)
+                       flag |= NETFLAG_CRYPTO2;
+       }
+       return flag;
+}
+
+int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolversion_t protocol, int rate, int burstsize, qboolean quakesignon_suppressreliables)
 {
        int totallen = 0;
+       unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
+       unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
 
        // if this packet was supposedly choked, but we find ourselves sending one
        // anyway, make sure the size counting starts at zero
@@ -643,6 +736,8 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers
        if (conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes == NETGRAPH_CHOKEDPACKET)
                conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
 
+       conn->outgoing_netgraph[conn->outgoing_packetcounter].cleartime = conn->cleartime;
+
        if (protocol == PROTOCOL_QUAKEWORLD)
        {
                int packetLen;
@@ -665,9 +760,9 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers
                        sendreliable = true;
                }
                // outgoing unreliable packet number, and outgoing reliable packet number (0 or 1)
-               StoreLittleLong(sendbuffer, (unsigned int)conn->outgoing_unreliable_sequence | ((unsigned int)sendreliable<<31));
+               StoreLittleLong(sendbuffer, conn->outgoing_unreliable_sequence | (((unsigned int)sendreliable)<<31));
                // last received unreliable packet number, and last received reliable packet number (0 or 1)
-               StoreLittleLong(sendbuffer + 4, (unsigned int)conn->qw.incoming_sequence | ((unsigned int)conn->qw.incoming_reliable_sequence<<31));
+               StoreLittleLong(sendbuffer + 4, conn->qw.incoming_sequence | (((unsigned int)conn->qw.incoming_reliable_sequence)<<31));
                packetLen = 8;
                conn->outgoing_unreliable_sequence++;
                // client sends qport in every packet
@@ -705,8 +800,8 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers
 
                NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
 
-               packetsSent++;
-               unreliableMessagesSent++;
+               conn->packetsSent++;
+               conn->unreliableMessagesSent++;
 
                totallen += packetLen + 28;
        }
@@ -715,6 +810,8 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers
                unsigned int packetLen;
                unsigned int dataLen;
                unsigned int eom;
+               const void *sendme;
+               size_t sendmelen;
 
                // if a reliable message fragment has been lost, send it again
                if (conn->sendMessageLength && (realtime - conn->lastSendTime) > 1.0)
@@ -732,19 +829,20 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers
 
                        packetLen = NET_HEADERSIZE + dataLen;
 
-                       StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom));
+                       StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
                        StoreBigLong(sendbuffer + 4, conn->nq.sendSequence - 1);
                        memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
 
                        conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
 
-                       if (NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress) == (int)packetLen)
+                       sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
+                       if (sendme && NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress) == (int)sendmelen)
                        {
                                conn->lastSendTime = realtime;
-                               packetsReSent++;
+                               conn->packetsReSent++;
                        }
 
-                       totallen += packetLen + 28;
+                       totallen += (int)sendmelen + 28;
                }
 
                // if we have a new reliable message to send, do so
@@ -780,7 +878,7 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers
 
                        packetLen = NET_HEADERSIZE + dataLen;
 
-                       StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom));
+                       StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
                        StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
                        memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
 
@@ -788,13 +886,15 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers
 
                        conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
 
-                       NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
+                       sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
+                       if(sendme)
+                               NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
 
                        conn->lastSendTime = realtime;
-                       packetsSent++;
-                       reliableMessagesSent++;
+                       conn->packetsSent++;
+                       conn->reliableMessagesSent++;
 
-                       totallen += packetLen + 28;
+                       totallen += (int)sendmelen + 28;
                }
 
                // if we have an unreliable message to send, do so
@@ -808,7 +908,7 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers
                                return -1;
                        }
 
-                       StoreBigLong(sendbuffer, packetLen | NETFLAG_UNRELIABLE);
+                       StoreBigLong(sendbuffer, packetLen | NETFLAG_UNRELIABLE | NetConn_AddCryptoFlag(&conn->crypto));
                        StoreBigLong(sendbuffer + 4, conn->outgoing_unreliable_sequence);
                        memcpy(sendbuffer + NET_HEADERSIZE, data->data, data->cursize);
 
@@ -816,21 +916,18 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers
 
                        conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
 
-                       NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
+                       sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
+                       if(sendme)
+                               NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
 
-                       packetsSent++;
-                       unreliableMessagesSent++;
+                       conn->packetsSent++;
+                       conn->unreliableMessagesSent++;
 
-                       totallen += packetLen + 28;
+                       totallen += (int)sendmelen + 28;
                }
        }
 
-       // delay later packets to obey rate limit
-       if (conn->cleartime < realtime - 0.1)
-               conn->cleartime = realtime - 0.1;
-       conn->cleartime = conn->cleartime + (double)totallen / (double)rate;
-       if (conn->cleartime < realtime)
-               conn->cleartime = realtime;
+       NetConn_UpdateCleartime(&conn->cleartime, rate, burstsize, totallen);
 
        return 0;
 }
@@ -852,7 +949,7 @@ void NetConn_CloseClientPorts(void)
                        LHNET_CloseSocket(cl_sockets[cl_numsockets - 1]);
 }
 
-void NetConn_OpenClientPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport)
+static void NetConn_OpenClientPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport)
 {
        lhnetaddress_t address;
        lhnetsocket_t *s;
@@ -885,6 +982,11 @@ void NetConn_OpenClientPorts(void)
 {
        int port;
        NetConn_CloseClientPorts();
+
+       SV_LockThreadMutex(); // FIXME recursive?
+       Crypto_LoadKeys(); // client sockets
+       SV_UnlockThreadMutex();
+
        port = bound(0, cl_netport.integer, 65535);
        if (cl_netport.integer != port)
                Cvar_SetValueQuick(&cl_netport, port);
@@ -894,7 +996,7 @@ void NetConn_OpenClientPorts(void)
                Con_Printf("Client using port %i\n", port);
        NetConn_OpenClientPort(NULL, LHNETADDRESSTYPE_LOOP, 2);
        NetConn_OpenClientPort(net_address.string, LHNETADDRESSTYPE_INET4, port);
-#ifdef SUPPORTIPV6
+#ifndef NOSUPPORTIPV6
        NetConn_OpenClientPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port);
 #endif
 }
@@ -906,7 +1008,7 @@ void NetConn_CloseServerPorts(void)
                        LHNET_CloseSocket(sv_sockets[sv_numsockets - 1]);
 }
 
-qboolean NetConn_OpenServerPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport, int range)
+static qboolean NetConn_OpenServerPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport, int range)
 {
        lhnetaddress_t address;
        lhnetsocket_t *s;
@@ -950,6 +1052,11 @@ void NetConn_OpenServerPorts(int opennetports)
 {
        int port;
        NetConn_CloseServerPorts();
+
+       SV_LockThreadMutex(); // FIXME recursive?
+       Crypto_LoadKeys(); // server sockets
+       SV_UnlockThreadMutex();
+
        NetConn_UpdateSockets();
        port = bound(0, sv_netport.integer, 65535);
        if (port == 0)
@@ -961,7 +1068,7 @@ void NetConn_OpenServerPorts(int opennetports)
                NetConn_OpenServerPort(NULL, LHNETADDRESSTYPE_LOOP, 1, 1);
        if (opennetports)
        {
-#ifdef SUPPORTIPV6
+#ifndef NOSUPPORTIPV6
                qboolean ip4success = NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
                NetConn_OpenServerPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port, ip4success ? 1 : 100);
 #else
@@ -1009,14 +1116,14 @@ netconn_t *NetConn_Open(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress)
        return conn;
 }
 
-void NetConn_ClearConnectFlood(lhnetaddress_t *peeraddress);
+void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength);
 void NetConn_Close(netconn_t *conn)
 {
        netconn_t *c;
        // remove connection from list
 
        // allow the client to reconnect immediately
-       NetConn_ClearConnectFlood(&(conn->peeraddress));
+       NetConn_ClearFlood(&(conn->peeraddress), sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]));
 
        if (conn == netconn_list)
                netconn_list = conn->next;
@@ -1045,6 +1152,9 @@ void NetConn_UpdateSockets(void)
 {
        int i, j;
 
+       // TODO add logic to automatically close sockets if needed
+       LHNET_DefaultDSCP(net_tos_dscp.integer);
+
        if (cls.state != ca_dedicated)
        {
                if (clientport2 != cl_netport.integer)
@@ -1087,16 +1197,19 @@ void NetConn_UpdateSockets(void)
        }
 }
 
-static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int length, protocolversion_t protocol, double newtimeout)
+static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, size_t length, protocolversion_t protocol, double newtimeout)
 {
-       int originallength = length;
+       int originallength = (int)length;
+       unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
+       unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
+       unsigned char cryptoreadbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
        if (length < 8)
                return 0;
 
        if (protocol == PROTOCOL_QUAKEWORLD)
        {
-               int sequence, sequence_ack;
-               int reliable_ack, reliable_message;
+               unsigned int sequence, sequence_ack;
+               qboolean reliable_ack, reliable_message;
                int count;
                //int qport;
 
@@ -1116,9 +1229,9 @@ static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int len
                        length -= 2;
                }
 
-               packetsReceived++;
-               reliable_message = (sequence >> 31) & 1;
-               reliable_ack = (sequence_ack >> 31) & 1;
+               conn->packetsReceived++;
+               reliable_message = (sequence >> 31) != 0;
+               reliable_ack = (sequence_ack >> 31) != 0;
                sequence &= ~(1<<31);
                sequence_ack &= ~(1<<31);
                if (sequence <= conn->qw.incoming_sequence)
@@ -1129,12 +1242,19 @@ static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int len
                count = sequence - (conn->qw.incoming_sequence + 1);
                if (count > 0)
                {
-                       droppedDatagrams += count;
+                       conn->droppedDatagrams += count;
                        //Con_DPrintf("Dropped %u datagram(s)\n", count);
+                       // If too may packets have been dropped, only write the
+                       // last NETGRAPH_PACKETS ones to the netgraph. Why?
+                       // Because there's no point in writing more than
+                       // these as the netgraph is going to be full anyway.
+                       if (count > NETGRAPH_PACKETS)
+                               count = NETGRAPH_PACKETS;
                        while (count--)
                        {
                                conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
                                conn->incoming_netgraph[conn->incoming_packetcounter].time            = realtime;
+                               conn->incoming_netgraph[conn->incoming_packetcounter].cleartime       = conn->incoming_cleartime;
                                conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
                                conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes   = NETGRAPH_NOPACKET;
                                conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes        = NETGRAPH_NOPACKET;
@@ -1142,14 +1262,24 @@ static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int len
                }
                conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
                conn->incoming_netgraph[conn->incoming_packetcounter].time            = realtime;
+               conn->incoming_netgraph[conn->incoming_packetcounter].cleartime       = conn->incoming_cleartime;
                conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
                conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes   = NETGRAPH_NOPACKET;
                conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes        = NETGRAPH_NOPACKET;
+               NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
+
+               // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
+               if (net_test.integer)
+               {
+                       if (conn->cleartime < realtime)
+                               conn->cleartime = realtime;
+               }
+
                if (reliable_ack == conn->qw.reliable_sequence)
                {
                        // received, now we will be able to send another reliable message
                        conn->sendMessageLength = 0;
-                       reliableMessagesReceived++;
+                       conn->reliableMessagesReceived++;
                }
                conn->qw.incoming_sequence = sequence;
                if (conn == cls.netcon)
@@ -1160,10 +1290,19 @@ static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int len
                        conn->qw.incoming_reliable_sequence ^= 1;
                conn->lastMessageTime = realtime;
                conn->timeout = realtime + newtimeout;
-               unreliableMessagesReceived++;
-               SZ_Clear(&net_message);
-               SZ_Write(&net_message, data, length);
-               MSG_BeginReading();
+               conn->unreliableMessagesReceived++;
+               if (conn == cls.netcon)
+               {
+                       SZ_Clear(&cl_message);
+                       SZ_Write(&cl_message, data, (int)length);
+                       MSG_BeginReading(&cl_message);
+               }
+               else
+               {
+                       SZ_Clear(&sv_message);
+                       SZ_Write(&sv_message, data, (int)length);
+                       MSG_BeginReading(&sv_message);
+               }
                return 2;
        }
        else
@@ -1171,7 +1310,16 @@ static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int len
                unsigned int count;
                unsigned int flags;
                unsigned int sequence;
-               int qlength;
+               size_t qlength;
+               const void *sendme;
+               size_t sendmelen;
+
+               originallength = (int)length;
+               data = (const unsigned char *) Crypto_DecryptPacket(&conn->crypto, data, length, cryptoreadbuffer, &length, sizeof(cryptoreadbuffer));
+               if(!data)
+                       return 0;
+               if(length < 8)
+                       return 0;
 
                qlength = (unsigned int)BuffBigLong(data);
                flags = qlength & ~NETFLAG_LENGTH_MASK;
@@ -1180,7 +1328,7 @@ static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int len
                if (!(flags & NETFLAG_CTL) && qlength == length)
                {
                        sequence = BuffBigLong(data + 4);
-                       packetsReceived++;
+                       conn->packetsReceived++;
                        data += 8;
                        length -= 8;
                        if (flags & NETFLAG_UNRELIABLE)
@@ -1190,12 +1338,19 @@ static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int len
                                        if (sequence > conn->nq.unreliableReceiveSequence)
                                        {
                                                count = sequence - conn->nq.unreliableReceiveSequence;
-                                               droppedDatagrams += count;
+                                               conn->droppedDatagrams += count;
                                                //Con_DPrintf("Dropped %u datagram(s)\n", count);
+                                               // If too may packets have been dropped, only write the
+                                               // last NETGRAPH_PACKETS ones to the netgraph. Why?
+                                               // Because there's no point in writing more than
+                                               // these as the netgraph is going to be full anyway.
+                                               if (count > NETGRAPH_PACKETS)
+                                                       count = NETGRAPH_PACKETS;
                                                while (count--)
                                                {
                                                        conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
                                                        conn->incoming_netgraph[conn->incoming_packetcounter].time            = realtime;
+                                                       conn->incoming_netgraph[conn->incoming_packetcounter].cleartime       = conn->incoming_cleartime;
                                                        conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
                                                        conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes   = NETGRAPH_NOPACKET;
                                                        conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes        = NETGRAPH_NOPACKET;
@@ -1203,18 +1358,30 @@ static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int len
                                        }
                                        conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
                                        conn->incoming_netgraph[conn->incoming_packetcounter].time            = realtime;
+                                       conn->incoming_netgraph[conn->incoming_packetcounter].cleartime       = conn->incoming_cleartime;
                                        conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
                                        conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes   = NETGRAPH_NOPACKET;
                                        conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes        = NETGRAPH_NOPACKET;
+                                       NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
+
                                        conn->nq.unreliableReceiveSequence = sequence + 1;
                                        conn->lastMessageTime = realtime;
                                        conn->timeout = realtime + newtimeout;
-                                       unreliableMessagesReceived++;
+                                       conn->unreliableMessagesReceived++;
                                        if (length > 0)
                                        {
-                                               SZ_Clear(&net_message);
-                                               SZ_Write(&net_message, data, length);
-                                               MSG_BeginReading();
+                                               if (conn == cls.netcon)
+                                               {
+                                                       SZ_Clear(&cl_message);
+                                                       SZ_Write(&cl_message, data, (int)length);
+                                                       MSG_BeginReading(&cl_message);
+                                               }
+                                               else
+                                               {
+                                                       SZ_Clear(&sv_message);
+                                                       SZ_Write(&sv_message, data, (int)length);
+                                                       MSG_BeginReading(&sv_message);
+                                               }
                                                return 2;
                                        }
                                }
@@ -1225,6 +1392,8 @@ static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int len
                        else if (flags & NETFLAG_ACK)
                        {
                                conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes += originallength + 28;
+                               NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
+
                                if (sequence == (conn->nq.sendSequence - 1))
                                {
                                        if (sequence == conn->nq.ackSequence)
@@ -1256,16 +1425,17 @@ static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int len
 
                                                        packetLen = NET_HEADERSIZE + dataLen;
 
-                                                       StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom));
+                                                       StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
                                                        StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
                                                        memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
 
                                                        conn->nq.sendSequence++;
 
-                                                       if (NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress) == (int)packetLen)
+                                                       sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
+                                                       if (sendme && NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress) == (int)sendmelen)
                                                        {
                                                                conn->lastSendTime = realtime;
-                                                               packetsSent++;
+                                                               conn->packetsSent++;
                                                        }
                                                }
                                                else
@@ -1282,10 +1452,15 @@ static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int len
                        {
                                unsigned char temppacket[8];
                                conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes   += originallength + 28;
+                               NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
+
                                conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes        += 8 + 28;
-                               StoreBigLong(temppacket, 8 | NETFLAG_ACK);
+
+                               StoreBigLong(temppacket, 8 | NETFLAG_ACK | NetConn_AddCryptoFlag(&conn->crypto));
                                StoreBigLong(temppacket + 4, sequence);
-                               NetConn_Write(conn->mysocket, (unsigned char *)temppacket, 8, &conn->peeraddress);
+                               sendme = Crypto_EncryptPacket(&conn->crypto, temppacket, 8, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
+                               if(sendme)
+                                       NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
                                if (sequence == conn->nq.receiveSequence)
                                {
                                        conn->lastMessageTime = realtime;
@@ -1293,7 +1468,7 @@ static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int len
                                        conn->nq.receiveSequence++;
                                        if( conn->receiveMessageLength + length <= (int)sizeof( conn->receiveMessage ) ) {
                                                memcpy(conn->receiveMessage + conn->receiveMessageLength, data, length);
-                                               conn->receiveMessageLength += length;
+                                               conn->receiveMessageLength += (int)length;
                                        } else {
                                                Con_Printf( "Reliable message (seq: %i) too big for message buffer!\n"
                                                                        "Dropping the message!\n", sequence );
@@ -1302,20 +1477,29 @@ static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int len
                                        }
                                        if (flags & NETFLAG_EOM)
                                        {
-                                               reliableMessagesReceived++;
+                                               conn->reliableMessagesReceived++;
                                                length = conn->receiveMessageLength;
                                                conn->receiveMessageLength = 0;
                                                if (length > 0)
                                                {
-                                                       SZ_Clear(&net_message);
-                                                       SZ_Write(&net_message, conn->receiveMessage, length);
-                                                       MSG_BeginReading();
+                                                       if (conn == cls.netcon)
+                                                       {
+                                                               SZ_Clear(&cl_message);
+                                                               SZ_Write(&cl_message, conn->receiveMessage, (int)length);
+                                                               MSG_BeginReading(&cl_message);
+                                                       }
+                                                       else
+                                                       {
+                                                               SZ_Clear(&sv_message);
+                                                               SZ_Write(&sv_message, conn->receiveMessage, (int)length);
+                                                               MSG_BeginReading(&sv_message);
+                                                       }
                                                        return 2;
                                                }
                                        }
                                }
                                else
-                                       receivedDuplicateCount++;
+                                       conn->receivedDuplicateCount++;
                                return 1;
                        }
                }
@@ -1323,20 +1507,44 @@ static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int len
        return 0;
 }
 
-void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, protocolversion_t initialprotocol)
+static void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, protocolversion_t initialprotocol)
 {
+       crypto_t *crypto;
        cls.connect_trying = false;
+#ifdef CONFIG_MENU
        M_Update_Return_Reason("");
+#endif
        // the connection request succeeded, stop current connection and set up a new connection
        CL_Disconnect();
        // if we're connecting to a remote server, shut down any local server
        if (LHNETADDRESS_GetAddressType(peeraddress) != LHNETADDRESSTYPE_LOOP && sv.active)
+       {
+               SV_LockThreadMutex();
                Host_ShutdownServer ();
+               SV_UnlockThreadMutex();
+       }
        // allocate a net connection to keep track of things
        cls.netcon = NetConn_Open(mysocket, peeraddress);
+       crypto = &cls.netcon->crypto;
+       if(cls.crypto.authenticated)
+       {
+               Crypto_FinishInstance(crypto, &cls.crypto);
+               Con_Printf("%s connection to %s has been established: server is %s@%s%.*s, I am %.*s@%s%.*s\n",
+                               crypto->use_aes ? "Encrypted" : "Authenticated",
+                               cls.netcon->address,
+                               crypto->server_idfp[0] ? crypto->server_idfp : "-",
+                               (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
+                               crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-",
+                               crypto_keyfp_recommended_length, crypto->client_idfp[0] ? crypto->client_idfp : "-",
+                               (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
+                               crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-"
+                               );
+       }
        Con_Printf("Connection accepted to %s\n", cls.netcon->address);
        key_dest = key_game;
+#ifdef CONFIG_MENU
        m_state = m_none;
+#endif
        cls.demonum = -1;                       // not in the demo loop now
        cls.state = ca_connected;
        cls.signon = 0;                         // need all the signon messages before playing
@@ -1355,7 +1563,7 @@ void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peer
                msg.data = buf;
                msg.maxsize = sizeof(buf);
                MSG_WriteChar(&msg, clc_nop);
-               NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, false);
+               NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, 0, false);
        }
 }
 
@@ -1366,6 +1574,7 @@ int NetConn_IsLocalGame(void)
        return false;
 }
 
+#ifdef CONFIG_MENU
 static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *addressstring)
 {
        int n;
@@ -1435,8 +1644,8 @@ static void NetConn_ClientParsePacket_ServerList_UpdateCache(int n)
                         !(
                                    gameversion_min.integer >= 0 // min/max range set by user/mod?
                                 && gameversion_max.integer >= 0
-                                && gameversion_min.integer >= info->gameversion // version of server in min/max range?
-                                && gameversion_max.integer <= info->gameversion
+                                && gameversion_min.integer <= info->gameversion // version of server in min/max range?
+                                && gameversion_max.integer >= info->gameversion
                          )
                        ) ? '1' : '4',
                        info->mod, info->map);
@@ -1481,7 +1690,7 @@ static qboolean NetConn_ClientParsePacket_ServerList_PrepareQuery( int protocol,
 
        entry = &serverlist_cache[n];
 
-       memset(entry, 0, sizeof(entry));
+       memset(entry, 0, sizeof(*entry));
        entry->protocol =       protocol;
        //      store   the data        the engine cares about (address and     ping)
        strlcpy (entry->info.cname, ipstring, sizeof(entry->info.cname));
@@ -1527,12 +1736,13 @@ static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *sen
 
                        if (port != 0)
                        {
+#ifdef WHY_JUST_WHY
                                const char *ifname;
+                               char ifnamebuf[16];
 
                                /// \TODO: make some basic checks of the IP address (broadcast, ...)
 
-#ifdef WHY_JUST_WHY
-                               ifname = LHNETADDRESS_GetInterfaceName(senderaddress);
+                               ifname = LHNETADDRESS_GetInterfaceName(senderaddress, ifnamebuf, sizeof(ifnamebuf));
                                if (ifname != NULL)
                                {
                                        dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x%%%s]:%hu",
@@ -1573,14 +1783,21 @@ static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *sen
        serverlist_querysleep = false;
        serverlist_querywaittime = realtime + 3;
 }
+#endif
 
 static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
 {
        qboolean fromserver;
-       int ret, c, control;
-       const char *s;
-       char *string, addressstring2[128], ipstring[32];
+       int ret, c;
+       char *string, addressstring2[128];
        char stringbuf[16384];
+       char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
+       size_t sendlength;
+#ifdef CONFIG_MENU
+       char infostringvalue[MAX_INPUTLINE];
+       char ipstring[32];
+       const char *s;
+#endif
 
        // quakeworld ingame packet
        fromserver = cls.netcon && mysocket == cls.netcon->mysocket && !LHNETADDRESS_Compare(&cls.netcon->peeraddress, peeraddress);
@@ -1605,7 +1822,34 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        Com_HexDumpToConsole(data, length);
                }
 
-               if (length > 10 && !memcmp(string, "challenge ", 10) && cls.rcon_trying)
+               sendlength = sizeof(senddata) - 4;
+               switch(Crypto_ClientParsePacket(string, length, senddata+4, &sendlength, peeraddress))
+               {
+                       case CRYPTO_NOMATCH:
+                               // nothing to do
+                               break;
+                       case CRYPTO_MATCH:
+                               if(sendlength)
+                               {
+                                       memcpy(senddata, "\377\377\377\377", 4);
+                                       NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
+                               }
+                               break;
+                       case CRYPTO_DISCARD:
+                               if(sendlength)
+                               {
+                                       memcpy(senddata, "\377\377\377\377", 4);
+                                       NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
+                               }
+                               return true;
+                               break;
+                       case CRYPTO_REPLACE:
+                               string = senddata+4;
+                               length = (int)sendlength;
+                               break;
+               }
+
+               if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.rcon_trying)
                {
                        int i = 0, j;
                        for (j = 0;j < MAX_RCONS;j++)
@@ -1628,12 +1872,12 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                e = strchr(rcon_password.string, ' ');
                                n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
 
-                               if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, strlen(argbuf), (unsigned char *) rcon_password.string, n))
+                               if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, (int)strlen(argbuf), (unsigned char *) rcon_password.string, n))
                                {
                                        int k;
                                        buf[45] = ' ';
                                        strlcpy(buf + 46, argbuf, sizeof(buf) - 46);
-                                       NetConn_Write(mysocket, buf, 46 + strlen(buf + 46), peeraddress);
+                                       NetConn_Write(mysocket, buf, 46 + (int)strlen(buf + 46), peeraddress);
                                        cls.rcon_commands[i][0] = 0;
                                        --cls.rcon_trying;
 
@@ -1656,37 +1900,58 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                }
                        }
                }
-               if (length > 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying)
+               if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying)
                {
                        // darkplaces or quake3
                        char protocolnames[1400];
-                       Protocol_Names(protocolnames, sizeof(protocolnames));
                        Con_DPrintf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
+                       if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
+                               Con_DPrintf("challenge message from wrong server %s\n", addressstring2);
+                               return true;
+                       }
+                       Protocol_Names(protocolnames, sizeof(protocolnames));
+#ifdef CONFIG_MENU
                        M_Update_Return_Reason("Got challenge response");
+#endif
                        // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
                        InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
                        // TODO: add userinfo stuff here instead of using NQ commands?
-                       NetConn_WriteString(mysocket, va("\377\377\377\377connect\\protocol\\darkplaces 3\\protocols\\%s%s\\challenge\\%s", protocolnames, cls.connect_userinfo, string + 10), peeraddress);
+                       memcpy(senddata, "\377\377\377\377", 4);
+                       dpsnprintf(senddata+4, sizeof(senddata)-4, "connect\\protocol\\darkplaces 3\\protocols\\%s%s\\challenge\\%s", protocolnames, cls.connect_userinfo, string + 10);
+                       NetConn_WriteString(mysocket, senddata, peeraddress);
                        return true;
                }
                if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying)
                {
                        // darkplaces or quake3
+                       if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
+                               Con_DPrintf("accept message from wrong server %s\n", addressstring2);
+                               return true;
+                       }
+#ifdef CONFIG_MENU
                        M_Update_Return_Reason("Accepted");
+#endif
                        NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_DARKPLACES3);
                        return true;
                }
                if (length > 7 && !memcmp(string, "reject ", 7) && cls.connect_trying)
                {
-                       char rejectreason[32];
+                       char rejectreason[128];
+                       if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
+                               Con_DPrintf("reject message from wrong server %s\n", addressstring2);
+                               return true;
+                       }
                        cls.connect_trying = false;
                        string += 7;
                        length = min(length - 7, (int)sizeof(rejectreason) - 1);
                        memcpy(rejectreason, string, length);
                        rejectreason[length] = 0;
+#ifdef CONFIG_MENU
                        M_Update_Return_Reason(rejectreason);
+#endif
                        return true;
                }
+#ifdef CONFIG_MENU
                if (length >= 15 && !memcmp(string, "statusResponse\x0A", 15))
                {
                        serverlist_info_t *info;
@@ -1721,17 +1986,17 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        else
                                Con_Printf("statusResponse without players block?\n");
 
-                       if ((s = SearchInfostring(string, "gamename"     )) != NULL) strlcpy(info->game, s, sizeof (info->game));
-                       if ((s = SearchInfostring(string, "modname"      )) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
-                       if ((s = SearchInfostring(string, "mapname"      )) != NULL) strlcpy(info->map , s, sizeof (info->map ));
-                       if ((s = SearchInfostring(string, "hostname"     )) != NULL) strlcpy(info->name, s, sizeof (info->name));
-                       if ((s = SearchInfostring(string, "protocol"     )) != NULL) info->protocol = atoi(s);
-                       if ((s = SearchInfostring(string, "clients"      )) != NULL) info->numplayers = atoi(s);
-                       if ((s = SearchInfostring(string, "bots"         )) != NULL) info->numbots = atoi(s);
-                       if ((s = SearchInfostring(string, "sv_maxclients")) != NULL) info->maxplayers = atoi(s);
-                       if ((s = SearchInfostring(string, "gameversion"  )) != NULL) info->gameversion = atoi(s);
-                       if ((s = SearchInfostring(string, "qcstatus"     )) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
-                       if (p                                               != NULL) strlcpy(info->players, p, sizeof(info->players));
+                       if ((s = InfoString_GetValue(string, "gamename"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
+                       if ((s = InfoString_GetValue(string, "modname"      , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
+                       if ((s = InfoString_GetValue(string, "mapname"      , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
+                       if ((s = InfoString_GetValue(string, "hostname"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
+                       if ((s = InfoString_GetValue(string, "protocol"     , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
+                       if ((s = InfoString_GetValue(string, "clients"      , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
+                       if ((s = InfoString_GetValue(string, "bots"         , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
+                       if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
+                       if ((s = InfoString_GetValue(string, "gameversion"  , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
+                       if ((s = InfoString_GetValue(string, "qcstatus"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
+                       if (p                                                                                         != NULL) strlcpy(info->players, p, sizeof(info->players));
                        info->numhumans = info->numplayers - max(0, info->numbots);
                        info->freeslots = info->maxplayers - info->numplayers;
 
@@ -1763,16 +2028,16 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        info->maxplayers  = 0;
                        info->gameversion = 0;
 
-                       if ((s = SearchInfostring(string, "gamename"     )) != NULL) strlcpy(info->game, s, sizeof (info->game));
-                       if ((s = SearchInfostring(string, "modname"      )) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
-                       if ((s = SearchInfostring(string, "mapname"      )) != NULL) strlcpy(info->map , s, sizeof (info->map ));
-                       if ((s = SearchInfostring(string, "hostname"     )) != NULL) strlcpy(info->name, s, sizeof (info->name));
-                       if ((s = SearchInfostring(string, "protocol"     )) != NULL) info->protocol = atoi(s);
-                       if ((s = SearchInfostring(string, "clients"      )) != NULL) info->numplayers = atoi(s);
-                       if ((s = SearchInfostring(string, "bots"         )) != NULL) info->numbots = atoi(s);
-                       if ((s = SearchInfostring(string, "sv_maxclients")) != NULL) info->maxplayers = atoi(s);
-                       if ((s = SearchInfostring(string, "gameversion"  )) != NULL) info->gameversion = atoi(s);
-                       if ((s = SearchInfostring(string, "qcstatus"     )) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
+                       if ((s = InfoString_GetValue(string, "gamename"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
+                       if ((s = InfoString_GetValue(string, "modname"      , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
+                       if ((s = InfoString_GetValue(string, "mapname"      , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
+                       if ((s = InfoString_GetValue(string, "hostname"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
+                       if ((s = InfoString_GetValue(string, "protocol"     , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
+                       if ((s = InfoString_GetValue(string, "clients"      , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
+                       if ((s = InfoString_GetValue(string, "bots"         , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
+                       if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
+                       if ((s = InfoString_GetValue(string, "gameversion"  , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
+                       if ((s = InfoString_GetValue(string, "qcstatus"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
                        info->numhumans = info->numplayers - max(0, info->numbots);
                        info->freeslots = info->maxplayers - info->numplayers;
 
@@ -1823,6 +2088,7 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        serverlist_querywaittime = realtime + 3;
                        return true;
                }
+#endif
                if (!strncmp(string, "extResponse ", 12))
                {
                        ++cl_net_extresponse_count;
@@ -1845,23 +2111,38 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                if (length > 1 && string[0] == 'c' && (string[1] == '-' || (string[1] >= '0' && string[1] <= '9')) && cls.connect_trying)
                {
                        // challenge message
+                       if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
+                               Con_DPrintf("c message from wrong server %s\n", addressstring2);
+                               return true;
+                       }
                        Con_Printf("challenge %s received, sending QuakeWorld connect request back to %s\n", string + 1, addressstring2);
+#ifdef CONFIG_MENU
                        M_Update_Return_Reason("Got QuakeWorld challenge response");
+#endif
                        cls.qw_qport = qport.integer;
                        // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
                        InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
-                       NetConn_WriteString(mysocket, va("\377\377\377\377connect %i %i %i \"%s%s\"\n", 28, cls.qw_qport, atoi(string + 1), cls.userinfo, cls.connect_userinfo), peeraddress);
+                       memcpy(senddata, "\377\377\377\377", 4);
+                       dpsnprintf(senddata+4, sizeof(senddata)-4, "connect %i %i %i \"%s%s\"\n", 28, cls.qw_qport, atoi(string + 1), cls.userinfo, cls.connect_userinfo);
+                       NetConn_WriteString(mysocket, senddata, peeraddress);
                        return true;
                }
                if (length >= 1 && string[0] == 'j' && cls.connect_trying)
                {
                        // accept message
+                       if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
+                               Con_DPrintf("j message from wrong server %s\n", addressstring2);
+                               return true;
+                       }
+#ifdef CONFIG_MENU
                        M_Update_Return_Reason("QuakeWorld Accepted");
+#endif
                        NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_QUAKEWORLD);
                        return true;
                }
                if (length > 2 && !memcmp(string, "n\\", 2))
                {
+#ifdef CONFIG_MENU
                        serverlist_info_t *info;
                        int n;
 
@@ -1877,14 +2158,14 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
 
                        info = &serverlist_cache[n].info;
                        strlcpy(info->game, "QuakeWorld", sizeof(info->game));
-                       if ((s = SearchInfostring(string, "*gamedir"     )) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));else info->mod[0]  = 0;
-                       if ((s = SearchInfostring(string, "map"          )) != NULL) strlcpy(info->map , s, sizeof (info->map ));else info->map[0]  = 0;
-                       if ((s = SearchInfostring(string, "hostname"     )) != NULL) strlcpy(info->name, s, sizeof (info->name));else info->name[0] = 0;
+                       if ((s = InfoString_GetValue(string, "*gamedir"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));else info->mod[0]  = 0;
+                       if ((s = InfoString_GetValue(string, "map"          , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));else info->map[0]  = 0;
+                       if ((s = InfoString_GetValue(string, "hostname"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));else info->name[0] = 0;
                        info->protocol = 0;
                        info->numplayers = 0; // updated below
                        info->numhumans = 0; // updated below
-                       if ((s = SearchInfostring(string, "maxclients"   )) != NULL) info->maxplayers = atoi(s);else info->maxplayers  = 0;
-                       if ((s = SearchInfostring(string, "gameversion"  )) != NULL) info->gameversion = atoi(s);else info->gameversion = 0;
+                       if ((s = InfoString_GetValue(string, "maxclients"   , infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);else info->maxplayers  = 0;
+                       if ((s = InfoString_GetValue(string, "gameversion"  , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);else info->gameversion = 0;
 
                        // count active players on server
                        // (we could gather more info, but we're just after the number)
@@ -1905,12 +2186,16 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        }
 
                        NetConn_ClientParsePacket_ServerList_UpdateCache(n);
-
+#endif
                        return true;
                }
                if (string[0] == 'n')
                {
-                       // qw print command
+                       // qw print command, used by rcon replies too
+                       if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address) && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) {
+                               Con_DPrintf("n message from wrong server %s\n", addressstring2);
+                               return true;
+                       }
                        Con_Printf("QW print command from server at %s:\n%s\n", addressstring2, string + 1);
                }
                // we may not have liked the packet, but it was a command packet, so
@@ -1925,17 +2210,19 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                return ret;
        }
        // netquake control packets, supported for compatibility only
-       if (length >= 5 && (control = BuffBigLong(data)) && (control & (~NETFLAG_LENGTH_MASK)) == (int)NETFLAG_CTL && (control & NETFLAG_LENGTH_MASK) == length)
+       if (length >= 5 && BuffBigLong(data) == ((int)NETFLAG_CTL | length) && !ENCRYPTION_REQUIRED)
        {
+#ifdef CONFIG_MENU
                int n;
                serverlist_info_t *info;
+#endif
 
                data += 4;
                length -= 4;
-               SZ_Clear(&net_message);
-               SZ_Write(&net_message, data, length);
-               MSG_BeginReading();
-               c = MSG_ReadByte();
+               SZ_Clear(&cl_message);
+               SZ_Write(&cl_message, data, length);
+               MSG_BeginReading(&cl_message);
+               c = MSG_ReadByte(&cl_message);
                switch (c)
                {
                case CCREP_ACCEPT:
@@ -1944,26 +2231,54 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        if (cls.connect_trying)
                        {
                                lhnetaddress_t clientportaddress;
+                               if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
+                                       Con_DPrintf("CCREP_ACCEPT message from wrong server %s\n", addressstring2);
+                                       break;
+                               }
                                clientportaddress = *peeraddress;
-                               LHNETADDRESS_SetPort(&clientportaddress, MSG_ReadLong());
+                               LHNETADDRESS_SetPort(&clientportaddress, MSG_ReadLong(&cl_message));
+                               // extra ProQuake stuff
+                               if (length >= 6)
+                                       cls.proquake_servermod = MSG_ReadByte(&cl_message); // MOD_PROQUAKE
+                               else
+                                       cls.proquake_servermod = 0;
+                               if (length >= 7)
+                                       cls.proquake_serverversion = MSG_ReadByte(&cl_message); // version * 10
+                               else
+                                       cls.proquake_serverversion = 0;
+                               if (length >= 8)
+                                       cls.proquake_serverflags = MSG_ReadByte(&cl_message); // flags (mainly PQF_CHEATFREE)
+                               else
+                                       cls.proquake_serverflags = 0;
+                               if (cls.proquake_servermod == 1)
+                                       Con_Printf("Connected to ProQuake %.1f server, enabling precise aim\n", cls.proquake_serverversion / 10.0f);
                                // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
                                InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
+#ifdef CONFIG_MENU
                                M_Update_Return_Reason("Accepted");
+#endif
                                NetConn_ConnectionEstablished(mysocket, &clientportaddress, PROTOCOL_QUAKE);
                        }
                        break;
                case CCREP_REJECT:
-                       if (developer_extra.integer)
-                               Con_DPrintf("Datagram_ParseConnectionless: received CCREP_REJECT from %s.\n", addressstring2);
+                       if (developer_extra.integer) {
+                               Con_DPrintf("CCREP_REJECT message from wrong server %s\n", addressstring2);
+                               break;
+                       }
+                       if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address))
+                               break;
                        cls.connect_trying = false;
-                       M_Update_Return_Reason((char *)MSG_ReadString());
+#ifdef CONFIG_MENU
+                       M_Update_Return_Reason((char *)MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
+#endif
                        break;
                case CCREP_SERVER_INFO:
                        if (developer_extra.integer)
                                Con_DPrintf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
+#ifdef CONFIG_MENU
                        // LordHavoc: because the quake server may report weird addresses
                        // we just ignore it and keep the real address
-                       MSG_ReadString();
+                       MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
                        // search the cache for this server and update it
                        n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2);
                        if (n < 0)
@@ -1972,20 +2287,24 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        info = &serverlist_cache[n].info;
                        strlcpy(info->game, "Quake", sizeof(info->game));
                        strlcpy(info->mod , "", sizeof(info->mod)); // mod name is not specified
-                       strlcpy(info->name, MSG_ReadString(), sizeof(info->name));
-                       strlcpy(info->map , MSG_ReadString(), sizeof(info->map));
-                       info->numplayers = MSG_ReadByte();
-                       info->maxplayers = MSG_ReadByte();
-                       info->protocol = MSG_ReadByte();
+                       strlcpy(info->name, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->name));
+                       strlcpy(info->map , MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->map));
+                       info->numplayers = MSG_ReadByte(&cl_message);
+                       info->maxplayers = MSG_ReadByte(&cl_message);
+                       info->protocol = MSG_ReadByte(&cl_message);
 
                        NetConn_ClientParsePacket_ServerList_UpdateCache(n);
-
+#endif
                        break;
                case CCREP_RCON: // RocketGuy: ProQuake rcon support
+                       if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) {
+                               Con_DPrintf("CCREP_RCON message from wrong server %s\n", addressstring2);
+                               break;
+                       }
                        if (developer_extra.integer)
                                Con_DPrintf("Datagram_ParseConnectionless: received CCREP_RCON from %s.\n", addressstring2);
 
-                       Con_Printf("%s\n", MSG_ReadString());
+                       Con_Printf("%s\n", MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
                        break;
                case CCREP_PLAYER_INFO:
                        // we got a CCREP_PLAYER_INFO??
@@ -2000,7 +2319,7 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                default:
                        break;
                }
-               SZ_Clear(&net_message);
+               SZ_Clear(&cl_message);
                // we may not have liked the packet, but it was a valid control
                // packet, so we're done processing this packet now
                return true;
@@ -2011,6 +2330,7 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
        return ret;
 }
 
+#ifdef CONFIG_MENU
 void NetConn_QueryQueueFrame(void)
 {
        int index;
@@ -2099,36 +2419,51 @@ void NetConn_QueryQueueFrame(void)
                }
        }
 }
+#endif
 
 void NetConn_ClientFrame(void)
 {
        int i, length;
        lhnetaddress_t peeraddress;
+       unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
        NetConn_UpdateSockets();
        if (cls.connect_trying && cls.connect_nextsendtime < realtime)
        {
+#ifdef CONFIG_MENU
                if (cls.connect_remainingtries == 0)
                        M_Update_Return_Reason("Connect: Waiting 10 seconds for reply");
+#endif
                cls.connect_nextsendtime = realtime + 1;
                cls.connect_remainingtries--;
                if (cls.connect_remainingtries <= -10)
                {
                        cls.connect_trying = false;
+#ifdef CONFIG_MENU
                        M_Update_Return_Reason("Connect: Failed");
+#endif
                        return;
                }
                // try challenge first (newer DP server or QW)
                NetConn_WriteString(cls.connect_mysocket, "\377\377\377\377getchallenge", &cls.connect_address);
                // then try netquake as a fallback (old server, or netquake)
-               SZ_Clear(&net_message);
+               SZ_Clear(&cl_message);
                // save space for the header, filled in later
-               MSG_WriteLong(&net_message, 0);
-               MSG_WriteByte(&net_message, CCREQ_CONNECT);
-               MSG_WriteString(&net_message, "QUAKE");
-               MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
-               StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
-               NetConn_Write(cls.connect_mysocket, net_message.data, net_message.cursize, &cls.connect_address);
-               SZ_Clear(&net_message);
+               MSG_WriteLong(&cl_message, 0);
+               MSG_WriteByte(&cl_message, CCREQ_CONNECT);
+               MSG_WriteString(&cl_message, "QUAKE");
+               MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
+               // extended proquake stuff
+               MSG_WriteByte(&cl_message, 1); // mod = MOD_PROQUAKE
+               // this version matches ProQuake 3.40, the first version to support
+               // the NAT fix, and it only supports the NAT fix for ProQuake 3.40 or
+               // higher clients, so we pretend we are that version...
+               MSG_WriteByte(&cl_message, 34); // version * 10
+               MSG_WriteByte(&cl_message, 0); // flags
+               MSG_WriteLong(&cl_message, 0); // password
+               // write the packetsize now...
+               StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
+               NetConn_Write(cls.connect_mysocket, cl_message.data, cl_message.cursize, &cls.connect_address);
+               SZ_Clear(&cl_message);
        }
        for (i = 0;i < cl_numsockets;i++)
        {
@@ -2139,24 +2474,19 @@ void NetConn_ClientFrame(void)
 //                     R_TimeReport("clientparsepacket");
                }
        }
+#ifdef CONFIG_MENU
        NetConn_QueryQueueFrame();
+#endif
        if (cls.netcon && realtime > cls.netcon->timeout && !sv.active)
        {
                Con_Print("Connection timed out\n");
                CL_Disconnect();
+               SV_LockThreadMutex();
                Host_ShutdownServer ();
+               SV_UnlockThreadMutex();
        }
 }
 
-#define MAX_CHALLENGES 128
-struct challenge_s
-{
-       lhnetaddress_t address;
-       double time;
-       char string[12];
-}
-challenge[MAX_CHALLENGES];
-
 static void NetConn_BuildChallengeString(char *buffer, int bufferlength)
 {
        int i;
@@ -2175,12 +2505,13 @@ static void NetConn_BuildChallengeString(char *buffer, int bufferlength)
 /// (div0) build the full response only if possible; better a getinfo response than no response at all if getstatus won't fit
 static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg, size_t out_size, qboolean fullstatus)
 {
+       prvm_prog_t *prog = SVVM_prog;
        char qcstatus[256];
        unsigned int nb_clients = 0, nb_bots = 0, i;
        int length;
        char teambuf[3];
-
-       SV_VM_Begin();
+       const char *crypto_idstring;
+       const char *worldstatusstr;
 
        // How many clients are there?
        for (i = 0;i < (unsigned int)svs.maxclients;i++)
@@ -2194,34 +2525,34 @@ static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg
        }
 
        *qcstatus = 0;
-       if(prog->globaloffsets.worldstatus >= 0)
+       worldstatusstr = PRVM_GetString(prog, PRVM_serverglobalstring(worldstatus));
+       if(worldstatusstr && *worldstatusstr)
        {
-               const char *str = PRVM_G_STRING(prog->globaloffsets.worldstatus);
-               if(str && *str)
-               {
-                       char *p;
-                       const char *q;
-                       p = qcstatus;
-                       for(q = str; *q; ++q)
-                               if(*q != '\\' && *q != '\n')
-                                       *p++ = *q;
-                       *p = 0;
-               }
+               char *p;
+               const char *q;
+               p = qcstatus;
+               for(q = worldstatusstr; *q && (size_t)(p - qcstatus) < (sizeof(qcstatus) - 1); ++q)
+                       if(*q != '\\' && *q != '\n')
+                               *p++ = *q;
+               *p = 0;
        }
 
        /// \TODO: we should add more information for the full status string
+       crypto_idstring = Crypto_GetInfoResponseDataString();
        length = dpsnprintf(out_msg, out_size,
                                                "\377\377\377\377%s\x0A"
                                                "\\gamename\\%s\\modname\\%s\\gameversion\\%d\\sv_maxclients\\%d"
                                                "\\clients\\%d\\bots\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d"
                                                "%s%s"
                                                "%s%s"
+                                               "%s%s"
                                                "%s",
                                                fullstatus ? "statusResponse" : "infoResponse",
-                                               gamename, com_modname, gameversion.integer, svs.maxclients,
+                                               gamenetworkfiltername, com_modname, gameversion.integer, svs.maxclients,
                                                nb_clients, nb_bots, sv.worldbasename, hostname.string, NET_PROTOCOL_VERSION,
                                                *qcstatus ? "\\qcstatus\\" : "", qcstatus,
                                                challenge ? "\\challenge\\" : "", challenge ? challenge : "",
+                                               crypto_idstring ? "\\d0_blind_id\\" : "", crypto_idstring ? crypto_idstring : "",
                                                fullstatus ? "\n" : "");
 
        // Make sure it fits in the buffer
@@ -2241,19 +2572,21 @@ static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg
 
                for (i = 0;i < (unsigned int)svs.maxclients;i++)
                {
-                       client_t *cl = &svs.clients[i];
-                       if (cl->active)
+                       client_t *client = &svs.clients[i];
+                       if (client->active)
                        {
                                int nameind, cleanind, pingvalue;
                                char curchar;
-                               char cleanname [sizeof(cl->name)];
+                               char cleanname [sizeof(client->name)];
+                               const char *statusstr;
+                               prvm_edict_t *ed;
 
                                // Remove all characters '"' and '\' in the player name
                                nameind = 0;
                                cleanind = 0;
                                do
                                {
-                                       curchar = cl->name[nameind++];
+                                       curchar = client->name[nameind++];
                                        if (curchar != '"' && curchar != '\\')
                                        {
                                                cleanname[cleanind++] = curchar;
@@ -2263,39 +2596,37 @@ static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg
                                } while (curchar != '\0');
                                cleanname[cleanind] = 0; // cleanind is always a valid index even at this point
 
-                               pingvalue = (int)(cl->ping * 1000.0f);
-                               if(cl->netconnection)
+                               pingvalue = (int)(client->ping * 1000.0f);
+                               if(client->netconnection)
                                        pingvalue = bound(1, pingvalue, 9999);
                                else
                                        pingvalue = 0;
 
                                *qcstatus = 0;
-                               if(prog->fieldoffsets.clientstatus >= 0)
+                               ed = PRVM_EDICT_NUM(i + 1);
+                               statusstr = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
+                               if(statusstr && *statusstr)
                                {
-                                       const char *str = PRVM_E_STRING(PRVM_EDICT_NUM(i + 1), prog->fieldoffsets.clientstatus);
-                                       if(str && *str)
-                                       {
-                                               char *p;
-                                               const char *q;
-                                               p = qcstatus;
-                                               for(q = str; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
-                                                       if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
-                                                               *p++ = *q;
-                                               *p = 0;
-                                       }
+                                       char *p;
+                                       const char *q;
+                                       p = qcstatus;
+                                       for(q = statusstr; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
+                                               if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
+                                                       *p++ = *q;
+                                       *p = 0;
                                }
 
-                               if ((gamemode == GAME_NEXUIZ || gamemode == GAME_XONOTIC) && (teamplay.integer > 0))
+                               if (IS_NEXUIZ_DERIVED(gamemode) && (teamplay.integer > 0))
                                {
-                                       if(cl->frags == -666) // spectator
+                                       if(client->frags == -666) // spectator
                                                strlcpy(teambuf, " 0", sizeof(teambuf));
-                                       else if(cl->colors == 0x44) // red team
+                                       else if(client->colors == 0x44) // red team
                                                strlcpy(teambuf, " 1", sizeof(teambuf));
-                                       else if(cl->colors == 0xDD) // blue team
+                                       else if(client->colors == 0xDD) // blue team
                                                strlcpy(teambuf, " 2", sizeof(teambuf));
-                                       else if(cl->colors == 0xCC) // yellow team
+                                       else if(client->colors == 0xCC) // yellow team
                                                strlcpy(teambuf, " 3", sizeof(teambuf));
-                                       else if(cl->colors == 0x99) // pink team
+                                       else if(client->colors == 0x99) // pink team
                                                strlcpy(teambuf, " 4", sizeof(teambuf));
                                        else
                                                strlcpy(teambuf, " 0", sizeof(teambuf));
@@ -2312,7 +2643,7 @@ static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg
                                                                                cleanname);
                                else
                                        length = dpsnprintf(ptr, left, "%d %d%s \"%s\"\n",
-                                                                               cl->frags,
+                                                                               client->frags,
                                                                                pingvalue,
                                                                                teambuf,
                                                                                cleanname);
@@ -2332,39 +2663,40 @@ static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg
                }
        }
 
-       SV_VM_End();
        return true;
 
 bad:
-       SV_VM_End();
        return false;
 }
 
-static qboolean NetConn_PreventConnectFlood(lhnetaddress_t *peeraddress)
+static qboolean NetConn_PreventFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength, double floodtime, qboolean renew)
 {
-       int floodslotnum, bestfloodslotnum;
+       size_t floodslotnum, bestfloodslotnum;
        double bestfloodtime;
        lhnetaddress_t noportpeeraddress;
        // see if this is a connect flood
        noportpeeraddress = *peeraddress;
        LHNETADDRESS_SetPort(&noportpeeraddress, 0);
        bestfloodslotnum = 0;
-       bestfloodtime = sv.connectfloodaddresses[bestfloodslotnum].lasttime;
-       for (floodslotnum = 0;floodslotnum < MAX_CONNECTFLOODADDRESSES;floodslotnum++)
+       bestfloodtime = floodlist[bestfloodslotnum].lasttime;
+       for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
        {
-               if (bestfloodtime >= sv.connectfloodaddresses[floodslotnum].lasttime)
+               if (bestfloodtime >= floodlist[floodslotnum].lasttime)
                {
-                       bestfloodtime = sv.connectfloodaddresses[floodslotnum].lasttime;
+                       bestfloodtime = floodlist[floodslotnum].lasttime;
                        bestfloodslotnum = floodslotnum;
                }
-               if (sv.connectfloodaddresses[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &sv.connectfloodaddresses[floodslotnum].address) == 0)
+               if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
                {
                        // this address matches an ongoing flood address
-                       if (realtime < sv.connectfloodaddresses[floodslotnum].lasttime + net_connectfloodblockingtimeout.value)
+                       if (realtime < floodlist[floodslotnum].lasttime + floodtime)
                        {
-                               // renew the ban on this address so it does not expire
-                               // until the flood has subsided
-                               sv.connectfloodaddresses[floodslotnum].lasttime = realtime;
+                               if(renew)
+                               {
+                                       // renew the ban on this address so it does not expire
+                                       // until the flood has subsided
+                                       floodlist[floodslotnum].lasttime = realtime;
+                               }
                                //Con_Printf("Flood detected!\n");
                                return true;
                        }
@@ -2374,27 +2706,27 @@ static qboolean NetConn_PreventConnectFlood(lhnetaddress_t *peeraddress)
                }
        }
        // begin a new timeout on this address
-       sv.connectfloodaddresses[bestfloodslotnum].address = noportpeeraddress;
-       sv.connectfloodaddresses[bestfloodslotnum].lasttime = realtime;
+       floodlist[bestfloodslotnum].address = noportpeeraddress;
+       floodlist[bestfloodslotnum].lasttime = realtime;
        //Con_Printf("Flood detection initiated!\n");
        return false;
 }
 
-void NetConn_ClearConnectFlood(lhnetaddress_t *peeraddress)
+void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength)
 {
-       int floodslotnum;
+       size_t floodslotnum;
        lhnetaddress_t noportpeeraddress;
        // see if this is a connect flood
        noportpeeraddress = *peeraddress;
        LHNETADDRESS_SetPort(&noportpeeraddress, 0);
-       for (floodslotnum = 0;floodslotnum < MAX_CONNECTFLOODADDRESSES;floodslotnum++)
+       for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
        {
-               if (sv.connectfloodaddresses[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &sv.connectfloodaddresses[floodslotnum].address) == 0)
+               if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
                {
                        // this address matches an ongoing flood address
                        // remove the ban
-                       sv.connectfloodaddresses[floodslotnum].address.addresstype = LHNETADDRESSTYPE_NONE;
-                       sv.connectfloodaddresses[floodslotnum].lasttime = 0;
+                       floodlist[floodslotnum].address.addresstype = LHNETADDRESSTYPE_NONE;
+                       floodlist[floodslotnum].lasttime = 0;
                        //Con_Printf("Flood cleared!\n");
                }
        }
@@ -2402,76 +2734,92 @@ void NetConn_ClearConnectFlood(lhnetaddress_t *peeraddress)
 
 typedef qboolean (*rcon_matchfunc_t) (lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen);
 
-qboolean hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
+static qboolean hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
 {
        char mdfourbuf[16];
        long t1, t2;
 
+       if (!password[0]) {
+               Con_Print("^4LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
+               return false;
+       }
+
        t1 = (long) time(NULL);
        t2 = strtol(s, NULL, 0);
        if(abs(t1 - t2) > rcon_secure_maxdiff.integer)
                return false;
 
-       if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, strlen(password)))
+       if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
                return false;
 
        return !memcmp(mdfourbuf, hash, 16);
 }
 
-qboolean hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
+static qboolean hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
 {
        char mdfourbuf[16];
        int i;
 
-       if(slen < (int)(sizeof(challenge[0].string)) - 1)
+       if (!password[0]) {
+               Con_Print("^4LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
+               return false;
+       }
+
+       if(slen < (int)(sizeof(challenges[0].string)) - 1)
                return false;
 
        // validate the challenge
        for (i = 0;i < MAX_CHALLENGES;i++)
-               if(challenge[i].time > 0)
-                       if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strncmp(challenge[i].string, s, sizeof(challenge[0].string) - 1))
+               if(challenges[i].time > 0)
+                       if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strncmp(challenges[i].string, s, sizeof(challenges[0].string) - 1))
                                break;
        // if the challenge is not recognized, drop the packet
        if (i == MAX_CHALLENGES)
                return false;
 
-       if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, strlen(password)))
+       if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
                return false;
 
        if(memcmp(mdfourbuf, hash, 16))
                return false;
 
        // unmark challenge to prevent replay attacks
-       challenge[i].time = 0;
+       challenges[i].time = 0;
 
        return true;
 }
 
-qboolean plaintext_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
+static qboolean plaintext_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
 {
+       if (!password[0]) {
+               Con_Print("^4LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
+               return false;
+       }
+
        return !strcmp(password, hash);
 }
 
 /// returns a string describing the user level, or NULL for auth failure
-const char *RCon_Authenticate(lhnetaddress_t *peeraddress, const char *password, const char *s, const char *endpos, rcon_matchfunc_t comparator, const char *cs, int cslen)
+static const char *RCon_Authenticate(lhnetaddress_t *peeraddress, const char *password, const char *s, const char *endpos, rcon_matchfunc_t comparator, const char *cs, int cslen)
 {
        const char *text, *userpass_start, *userpass_end, *userpass_startpass;
        static char buf[MAX_INPUTLINE];
        qboolean hasquotes;
        qboolean restricted = false;
        qboolean have_usernames = false;
+       static char vabuf[1024];
 
        userpass_start = rcon_password.string;
        while((userpass_end = strchr(userpass_start, ' ')))
        {
                have_usernames = true;
                strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
-               if(buf[0])
+               if(buf[0])  // Ignore empty entries due to leading/duplicate space.
                        if(comparator(peeraddress, buf, password, cs, cslen))
                                goto allow;
                userpass_start = userpass_end + 1;
        }
-       if(userpass_start[0])
+       if(userpass_start[0])  // Ignore empty trailing entry due to trailing space or password not set.
        {
                userpass_end = userpass_start + strlen(userpass_start);
                if(comparator(peeraddress, userpass_start, password, cs, cslen))
@@ -2485,12 +2833,12 @@ const char *RCon_Authenticate(lhnetaddress_t *peeraddress, const char *password,
        {
                have_usernames = true;
                strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
-               if(buf[0])
+               if(buf[0])  // Ignore empty entries due to leading/duplicate space.
                        if(comparator(peeraddress, buf, password, cs, cslen))
                                goto check;
                userpass_start = userpass_end + 1;
        }
-       if(userpass_start[0])
+       if(userpass_start[0])  // Ignore empty trailing entry due to trailing space or password not set.
        {
                userpass_end = userpass_start + strlen(userpass_start);
                if(comparator(peeraddress, userpass_start, password, cs, cslen))
@@ -2531,7 +2879,7 @@ check:
                                {
                                        if(!strcmp(com_token, s))
                                                goto match;
-                                       if(!memcmp(va("%s ", com_token), s, strlen(com_token) + 1))
+                                       if(!memcmp(va(vabuf, sizeof(vabuf), "%s ", com_token), s, strlen(com_token) + 1))
                                                goto match;
                                }
                        }
@@ -2545,12 +2893,12 @@ match:
 allow:
        userpass_startpass = strchr(userpass_start, ':');
        if(have_usernames && userpass_startpass && userpass_startpass < userpass_end)
-               return va("%srcon (username %.*s)", restricted ? "restricted " : "", (int)(userpass_startpass-userpass_start), userpass_start);
+               return va(vabuf, sizeof(vabuf), "%srcon (username %.*s)", restricted ? "restricted " : "", (int)(userpass_startpass-userpass_start), userpass_start);
 
-       return va("%srcon", restricted ? "restricted " : "");
+       return va(vabuf, sizeof(vabuf), "%srcon", restricted ? "restricted " : "");
 }
 
-void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const char *addressstring2, const char *userlevel, const char *s, const char *endpos)
+static void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const char *addressstring2, const char *userlevel, const char *s, const char *endpos, qboolean proquakeprotocol)
 {
        if(userlevel)
        {
@@ -2567,14 +2915,14 @@ void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const ch
                Con_Printf("\n");
 
                if (!host_client || !host_client->netconnection || LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
-                       Con_Rcon_Redirect_Init(mysocket, peeraddress);
+                       Con_Rcon_Redirect_Init(mysocket, peeraddress, proquakeprotocol);
                while(s != endpos)
                {
                        size_t l = strlen(s);
                        if(l)
                        {
                                client_t *host_client_save = host_client;
-                               Cmd_ExecuteString(s, src_command);
+                               Cmd_ExecuteString(s, src_command, true);
                                host_client = host_client_save;
                                // in case it is a command that changes host_client (like restart)
                        }
@@ -2588,15 +2936,16 @@ void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const ch
        }
 }
 
-extern void SV_SendServerinfo (client_t *client);
 static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
 {
        int i, ret, clientnum, best;
        double besttime;
-       client_t *client;
-       char *s, *string, response[1400], addressstring2[128];
-       static char stringbuf[16384];
+       char *string, response[1400], addressstring2[128];
+       static char stringbuf[16384]; // server only
        qboolean islocal = (LHNETADDRESS_GetAddressType(peeraddress) == LHNETADDRESSTYPE_LOOP);
+       char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
+       size_t sendlength, response_len;
+       char infostringvalue[MAX_INPUTLINE];
 
        if (!sv.active)
                return false;
@@ -2630,55 +2979,121 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        Com_HexDumpToConsole(data, length);
                }
 
+               sendlength = sizeof(senddata) - 4;
+               switch(Crypto_ServerParsePacket(string, length, senddata+4, &sendlength, peeraddress))
+               {
+                       case CRYPTO_NOMATCH:
+                               // nothing to do
+                               break;
+                       case CRYPTO_MATCH:
+                               if(sendlength)
+                               {
+                                       memcpy(senddata, "\377\377\377\377", 4);
+                                       NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
+                               }
+                               break;
+                       case CRYPTO_DISCARD:
+                               if(sendlength)
+                               {
+                                       memcpy(senddata, "\377\377\377\377", 4);
+                                       NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
+                               }
+                               return true;
+                               break;
+                       case CRYPTO_REPLACE:
+                               string = senddata+4;
+                               length = (int)sendlength;
+                               break;
+               }
+
                if (length >= 12 && !memcmp(string, "getchallenge", 12) && (islocal || sv_public.integer > -3))
                {
                        for (i = 0, best = 0, besttime = realtime;i < MAX_CHALLENGES;i++)
                        {
-                               if(challenge[i].time > 0)
-                                       if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address))
+                               if(challenges[i].time > 0)
+                                       if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address))
                                                break;
-                               if (besttime > challenge[i].time)
-                                       besttime = challenge[best = i].time;
+                               if (besttime > challenges[i].time)
+                                       besttime = challenges[best = i].time;
                        }
                        // if we did not find an exact match, choose the oldest and
                        // update address and string
                        if (i == MAX_CHALLENGES)
                        {
                                i = best;
-                               challenge[i].address = *peeraddress;
-                               NetConn_BuildChallengeString(challenge[i].string, sizeof(challenge[i].string));
+                               challenges[i].address = *peeraddress;
+                               NetConn_BuildChallengeString(challenges[i].string, sizeof(challenges[i].string));
                        }
-                       challenge[i].time = realtime;
+                       else
+                       {
+                               // flood control: drop if requesting challenge too often
+                               if(challenges[i].time > realtime - net_challengefloodblockingtimeout.value)
+                                       return true;
+                       }
+                       challenges[i].time = realtime;
                        // send the challenge
-                       NetConn_WriteString(mysocket, va("\377\377\377\377challenge %s", challenge[i].string), peeraddress);
+                       memcpy(response, "\377\377\377\377", 4);
+                       dpsnprintf(response+4, sizeof(response)-4, "challenge %s", challenges[i].string);
+                       response_len = strlen(response) + 1;
+                       Crypto_ServerAppendToChallenge(string, length, response, &response_len, sizeof(response));
+                       NetConn_Write(mysocket, response, (int)response_len, peeraddress);
                        return true;
                }
                if (length > 8 && !memcmp(string, "connect\\", 8))
                {
+                       char *s;
+                       client_t *client;
+                       crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
                        string += 7;
                        length -= 7;
 
-                       if (!(s = SearchInfostring(string, "challenge")))
-                               return true;
-                       // validate the challenge
-                       for (i = 0;i < MAX_CHALLENGES;i++)
-                               if(challenge[i].time > 0)
-                                       if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strcmp(challenge[i].string, s))
-                                               break;
-                       // if the challenge is not recognized, drop the packet
-                       if (i == MAX_CHALLENGES)
-                               return true;
+                       if(crypto && crypto->authenticated)
+                       {
+                               // no need to check challenge
+                               if(crypto_developer.integer)
+                               {
+                                       Con_Printf("%s connection to %s is being established: client is %s@%s%.*s, I am %.*s@%s%.*s\n",
+                                                       crypto->use_aes ? "Encrypted" : "Authenticated",
+                                                       addressstring2,
+                                                       crypto->client_idfp[0] ? crypto->client_idfp : "-",
+                                                       (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
+                                                       crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-",
+                                                       crypto_keyfp_recommended_length, crypto->server_idfp[0] ? crypto->server_idfp : "-",
+                                                       (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
+                                                       crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-"
+                                                 );
+                               }
+                       }
+                       else
+                       {
+                               if ((s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue))))
+                               {
+                                       // validate the challenge
+                                       for (i = 0;i < MAX_CHALLENGES;i++)
+                                               if(challenges[i].time > 0)
+                                                       if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strcmp(challenges[i].string, s))
+                                                               break;
+                                       // if the challenge is not recognized, drop the packet
+                                       if (i == MAX_CHALLENGES)
+                                               return true;
+                               }
+                       }
+
+                       if((s = InfoString_GetValue(string, "message", infostringvalue, sizeof(infostringvalue))))
+                               Con_DPrintf("Connecting client %s sent us the message: %s\n", addressstring2, s);
 
                        if(!(islocal || sv_public.integer > -2))
                        {
                                if (developer_extra.integer)
                                        Con_Printf("Datagram_ParseConnectionless: sending \"reject %s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
-                               NetConn_WriteString(mysocket, va("\377\377\377\377reject %s", sv_public_rejectreason.string), peeraddress);
+                               memcpy(response, "\377\377\377\377", 4);
+                               dpsnprintf(response+4, sizeof(response)-4, "reject %s", sv_public_rejectreason.string);
+                               NetConn_WriteString(mysocket, response, peeraddress);
                                return true;
                        }
 
                        // check engine protocol
-                       if(!(s = SearchInfostring(string, "protocol")) || strcmp(s, "darkplaces 3"))
+                       if(!(s = InfoString_GetValue(string, "protocol", infostringvalue, sizeof(infostringvalue))) || strcmp(s, "darkplaces 3"))
                        {
                                if (developer_extra.integer)
                                        Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
@@ -2693,16 +3108,49 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
                                {
                                        // this is a known client...
-                                       if (client->spawned)
+                                       if(crypto && crypto->authenticated)
+                                       {
+                                               // reject if changing key!
+                                               if(client->netconnection->crypto.authenticated)
+                                               {
+                                                       if(
+                                                                       strcmp(client->netconnection->crypto.client_idfp, crypto->client_idfp)
+                                                                       ||
+                                                                       strcmp(client->netconnection->crypto.server_idfp, crypto->server_idfp)
+                                                                       ||
+                                                                       strcmp(client->netconnection->crypto.client_keyfp, crypto->client_keyfp)
+                                                                       ||
+                                                                       strcmp(client->netconnection->crypto.server_keyfp, crypto->server_keyfp)
+                                                         )
+                                                       {
+                                                               if (developer_extra.integer)
+                                                                       Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to change key of crypto.\" to %s.\n", addressstring2);
+                                                               NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to change key of crypto.", peeraddress);
+                                                               return true;
+                                                       }
+                                               }
+                                       }
+                                       else
+                                       {
+                                               // reject if downgrading!
+                                               if(client->netconnection->crypto.authenticated)
+                                               {
+                                                       if (developer_extra.integer)
+                                                               Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to downgrade crypto.\" to %s.\n", addressstring2);
+                                                       NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to downgrade crypto.", peeraddress);
+                                                       return true;
+                                               }
+                                       }
+                                       if (client->begun)
                                        {
                                                // client crashed and is coming back,
                                                // keep their stuff intact
                                                if (developer_extra.integer)
                                                        Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", addressstring2);
                                                NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
-                                               SV_VM_Begin();
+                                               if(crypto && crypto->authenticated)
+                                                       Crypto_FinishInstance(&client->netconnection->crypto, crypto);
                                                SV_SendServerinfo(client);
-                                               SV_VM_End();
                                        }
                                        else
                                        {
@@ -2710,13 +3158,15 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                                // so we send a duplicate reply
                                                if (developer_extra.integer)
                                                        Con_Printf("Datagram_ParseConnectionless: sending duplicate accept to %s.\n", addressstring2);
+                                               if(crypto && crypto->authenticated)
+                                                       Crypto_FinishInstance(&client->netconnection->crypto, crypto);
                                                NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
                                        }
                                        return true;
                                }
                        }
 
-                       if (NetConn_PreventConnectFlood(peeraddress))
+                       if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
                                return true;
 
                        // find an empty client slot for this new client
@@ -2730,9 +3180,9 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                                Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
                                        NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
                                        // now set up the client
-                                       SV_VM_Begin();
+                                       if(crypto && crypto->authenticated)
+                                               Crypto_FinishInstance(&conn->crypto, crypto);
                                        SV_ConnectClient(clientnum, conn);
-                                       SV_VM_End();
                                        NetConn_Heartbeat(1);
                                        return true;
                                }
@@ -2749,6 +3199,9 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                {
                        const char *challenge = NULL;
 
+                       if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
+                               return true;
+
                        // If there was a challenge in the getinfo message
                        if (length > 8 && string[7] == ' ')
                                challenge = string + 8;
@@ -2765,6 +3218,9 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                {
                        const char *challenge = NULL;
 
+                       if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
+                               return true;
+
                        // If there was a challenge in the getinfo message
                        if (length > 10 && string[9] == ' ')
                                challenge = string + 10;
@@ -2793,7 +3249,7 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        ++s;
 
                        userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_time_matching, timeval, endpos - timeval - 1); // not including the appended \0 into the HMAC
-                       RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos);
+                       RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
                        return true;
                }
                if (length >= 42 && !memcmp(string, "srcon HMAC-MD4 CHALLENGE ", 25))
@@ -2808,12 +3264,12 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        ++s;
 
                        userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_challenge_matching, challenge, endpos - challenge - 1); // not including the appended \0 into the HMAC
-                       RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos);
+                       RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
                        return true;
                }
                if (length >= 5 && !memcmp(string, "rcon ", 5))
                {
-                       int i;
+                       int j;
                        char *s = string + 5;
                        char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
                        char password[64];
@@ -2821,16 +3277,16 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        if(rcon_secure.integer > 0)
                                return true;
 
-                       for (i = 0;!ISWHITESPACE(*s);s++)
-                               if (i < (int)sizeof(password) - 1)
-                                       password[i++] = *s;
+                       for (j = 0;!ISWHITESPACE(*s);s++)
+                               if (j < (int)sizeof(password) - 1)
+                                       password[j++] = *s;
                        if(ISWHITESPACE(*s) && s != endpos) // skip leading ugly space
                                ++s;
-                       password[i] = 0;
+                       password[j] = 0;
                        if (!ISWHITESPACE(password[0]))
                        {
                                const char *userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
-                               RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos);
+                               RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
                        }
                        return true;
                }
@@ -2861,17 +3317,19 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
        // protocol
        // (this protects more modern protocols against being used for
        //  Quake packet flood Denial Of Service attacks)
-       if (length >= 5 && (i = BuffBigLong(data)) && (i & (~NETFLAG_LENGTH_MASK)) == (int)NETFLAG_CTL && (i & NETFLAG_LENGTH_MASK) == length && (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3 || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3))
+       if (length >= 5 && (i = BuffBigLong(data)) && (i & (~NETFLAG_LENGTH_MASK)) == (int)NETFLAG_CTL && (i & NETFLAG_LENGTH_MASK) == length && (sv.protocol == PROTOCOL_QUAKE || sv.protocol == PROTOCOL_QUAKEDP || sv.protocol == PROTOCOL_NEHAHRAMOVIE || sv.protocol == PROTOCOL_NEHAHRABJP || sv.protocol == PROTOCOL_NEHAHRABJP2 || sv.protocol == PROTOCOL_NEHAHRABJP3 || sv.protocol == PROTOCOL_DARKPLACES1 || sv.protocol == PROTOCOL_DARKPLACES2 || sv.protocol == PROTOCOL_DARKPLACES3) && !ENCRYPTION_REQUIRED)
        {
                int c;
                int protocolnumber;
                const char *protocolname;
+               client_t *knownclient;
+               client_t *newclient;
                data += 4;
                length -= 4;
-               SZ_Clear(&net_message);
-               SZ_Write(&net_message, data, length);
-               MSG_BeginReading();
-               c = MSG_ReadByte();
+               SZ_Clear(&sv_message);
+               SZ_Write(&sv_message, data, length);
+               MSG_BeginReading(&sv_message);
+               c = MSG_ReadByte(&sv_message);
                switch (c)
                {
                case CCREQ_CONNECT:
@@ -2881,76 +3339,89 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        {
                                if (developer_extra.integer)
                                        Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"%s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
-                               SZ_Clear(&net_message);
+                               SZ_Clear(&sv_message);
                                // save space for the header, filled in later
-                               MSG_WriteLong(&net_message, 0);
-                               MSG_WriteByte(&net_message, CCREP_REJECT);
-                               MSG_WriteString(&net_message, va("%s\n", sv_public_rejectreason.string));
-                               StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
-                               NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
-                               SZ_Clear(&net_message);
+                               MSG_WriteLong(&sv_message, 0);
+                               MSG_WriteByte(&sv_message, CCREP_REJECT);
+                               MSG_WriteUnterminatedString(&sv_message, sv_public_rejectreason.string);
+                               MSG_WriteString(&sv_message, "\n");
+                               StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
+                               NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
+                               SZ_Clear(&sv_message);
                                break;
                        }
 
-                       protocolname = MSG_ReadString();
-                       protocolnumber = MSG_ReadByte();
+                       protocolname = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
+                       protocolnumber = MSG_ReadByte(&sv_message);
                        if (strcmp(protocolname, "QUAKE") || protocolnumber != NET_PROTOCOL_VERSION)
                        {
                                if (developer_extra.integer)
                                        Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
-                               SZ_Clear(&net_message);
+                               SZ_Clear(&sv_message);
                                // save space for the header, filled in later
-                               MSG_WriteLong(&net_message, 0);
-                               MSG_WriteByte(&net_message, CCREP_REJECT);
-                               MSG_WriteString(&net_message, "Incompatible version.\n");
-                               StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
-                               NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
-                               SZ_Clear(&net_message);
+                               MSG_WriteLong(&sv_message, 0);
+                               MSG_WriteByte(&sv_message, CCREP_REJECT);
+                               MSG_WriteString(&sv_message, "Incompatible version.\n");
+                               StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
+                               NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
+                               SZ_Clear(&sv_message);
                                break;
                        }
 
                        // see if this connect request comes from a known client
-                       for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
+                       for (clientnum = 0, knownclient = svs.clients;clientnum < svs.maxclients;clientnum++, knownclient++)
                        {
-                               if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
+                               if (knownclient->netconnection && LHNETADDRESS_Compare(peeraddress, &knownclient->netconnection->peeraddress) == 0)
                                {
                                        // this is either a duplicate connection request
                                        // or coming back from a timeout
                                        // (if so, keep their stuff intact)
 
+                                       crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
+                                       if((crypto && crypto->authenticated) || knownclient->netconnection->crypto.authenticated)
+                                       {
+                                               if (developer_extra.integer)
+                                                       Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Attempt to downgrade crypto.\" to %s.\n", addressstring2);
+                                               SZ_Clear(&sv_message);
+                                               // save space for the header, filled in later
+                                               MSG_WriteLong(&sv_message, 0);
+                                               MSG_WriteByte(&sv_message, CCREP_REJECT);
+                                               MSG_WriteString(&sv_message, "Attempt to downgrade crypto.\n");
+                                               StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
+                                               NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
+                                               SZ_Clear(&sv_message);
+                                               return true;
+                                       }
+
                                        // send a reply
                                        if (developer_extra.integer)
                                                Con_DPrintf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
-                                       SZ_Clear(&net_message);
+                                       SZ_Clear(&sv_message);
                                        // save space for the header, filled in later
-                                       MSG_WriteLong(&net_message, 0);
-                                       MSG_WriteByte(&net_message, CCREP_ACCEPT);
-                                       MSG_WriteLong(&net_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(client->netconnection->mysocket)));
-                                       StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
-                                       NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
-                                       SZ_Clear(&net_message);
+                                       MSG_WriteLong(&sv_message, 0);
+                                       MSG_WriteByte(&sv_message, CCREP_ACCEPT);
+                                       MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(knownclient->netconnection->mysocket)));
+                                       StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
+                                       NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
+                                       SZ_Clear(&sv_message);
 
                                        // if client is already spawned, re-send the
                                        // serverinfo message as they'll need it to play
-                                       if (client->spawned)
-                                       {
-                                               SV_VM_Begin();
-                                               SV_SendServerinfo(client);
-                                               SV_VM_End();
-                                       }
+                                       if (knownclient->begun)
+                                               SV_SendServerinfo(knownclient);
                                        return true;
                                }
                        }
 
                        // this is a new client, check for connection flood
-                       if (NetConn_PreventConnectFlood(peeraddress))
+                       if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
                                break;
 
                        // find a slot for the new client
-                       for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
+                       for (clientnum = 0, newclient = svs.clients;clientnum < svs.maxclients;clientnum++, newclient++)
                        {
                                netconn_t *conn;
-                               if (!client->active && (client->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
+                               if (!newclient->active && (newclient->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
                                {
                                        // connect to the client
                                        // everything is allocated, just fill in the details
@@ -2958,18 +3429,16 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                        if (developer_extra.integer)
                                                Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
                                        // send back the info about the server connection
-                                       SZ_Clear(&net_message);
+                                       SZ_Clear(&sv_message);
                                        // save space for the header, filled in later
-                                       MSG_WriteLong(&net_message, 0);
-                                       MSG_WriteByte(&net_message, CCREP_ACCEPT);
-                                       MSG_WriteLong(&net_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
-                                       StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
-                                       NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
-                                       SZ_Clear(&net_message);
+                                       MSG_WriteLong(&sv_message, 0);
+                                       MSG_WriteByte(&sv_message, CCREP_ACCEPT);
+                                       MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
+                                       StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
+                                       NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
+                                       SZ_Clear(&sv_message);
                                        // now set up the client struct
-                                       SV_VM_Begin();
                                        SV_ConnectClient(clientnum, conn);
-                                       SV_VM_End();
                                        NetConn_Heartbeat(1);
                                        return true;
                                }
@@ -2978,44 +3447,48 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        if (developer_extra.integer)
                                Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
                        // no room; try to let player know
-                       SZ_Clear(&net_message);
+                       SZ_Clear(&sv_message);
                        // save space for the header, filled in later
-                       MSG_WriteLong(&net_message, 0);
-                       MSG_WriteByte(&net_message, CCREP_REJECT);
-                       MSG_WriteString(&net_message, "Server is full.\n");
-                       StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
-                       NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
-                       SZ_Clear(&net_message);
+                       MSG_WriteLong(&sv_message, 0);
+                       MSG_WriteByte(&sv_message, CCREP_REJECT);
+                       MSG_WriteString(&sv_message, "Server is full.\n");
+                       StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
+                       NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
+                       SZ_Clear(&sv_message);
                        break;
                case CCREQ_SERVER_INFO:
                        if (developer_extra.integer)
                                Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
                        if(!(islocal || sv_public.integer > -1))
                                break;
-                       if (sv.active && !strcmp(MSG_ReadString(), "QUAKE"))
+
+                       if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
+                               break;
+
+                       if (sv.active && !strcmp(MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), "QUAKE"))
                        {
                                int numclients;
                                char myaddressstring[128];
                                if (developer_extra.integer)
                                        Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
-                               SZ_Clear(&net_message);
+                               SZ_Clear(&sv_message);
                                // save space for the header, filled in later
-                               MSG_WriteLong(&net_message, 0);
-                               MSG_WriteByte(&net_message, CCREP_SERVER_INFO);
+                               MSG_WriteLong(&sv_message, 0);
+                               MSG_WriteByte(&sv_message, CCREP_SERVER_INFO);
                                LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), myaddressstring, sizeof(myaddressstring), true);
-                               MSG_WriteString(&net_message, myaddressstring);
-                               MSG_WriteString(&net_message, hostname.string);
-                               MSG_WriteString(&net_message, sv.name);
+                               MSG_WriteString(&sv_message, myaddressstring);
+                               MSG_WriteString(&sv_message, hostname.string);
+                               MSG_WriteString(&sv_message, sv.name);
                                // How many clients are there?
                                for (i = 0, numclients = 0;i < svs.maxclients;i++)
                                        if (svs.clients[i].active)
                                                numclients++;
-                               MSG_WriteByte(&net_message, numclients);
-                               MSG_WriteByte(&net_message, svs.maxclients);
-                               MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
-                               StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
-                               NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
-                               SZ_Clear(&net_message);
+                               MSG_WriteByte(&sv_message, numclients);
+                               MSG_WriteByte(&sv_message, svs.maxclients);
+                               MSG_WriteByte(&sv_message, NET_PROTOCOL_VERSION);
+                               StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
+                               NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
+                               SZ_Clear(&sv_message);
                        }
                        break;
                case CCREQ_PLAYER_INFO:
@@ -3023,31 +3496,38 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
                        if(!(islocal || sv_public.integer > -1))
                                break;
+
+                       if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
+                               break;
+
                        if (sv.active)
                        {
                                int playerNumber, activeNumber, clientNumber;
                                client_t *client;
 
-                               playerNumber = MSG_ReadByte();
+                               playerNumber = MSG_ReadByte(&sv_message);
                                activeNumber = -1;
                                for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
                                        if (client->active && ++activeNumber == playerNumber)
                                                break;
                                if (clientNumber != svs.maxclients)
                                {
-                                       SZ_Clear(&net_message);
+                                       SZ_Clear(&sv_message);
                                        // save space for the header, filled in later
-                                       MSG_WriteLong(&net_message, 0);
-                                       MSG_WriteByte(&net_message, CCREP_PLAYER_INFO);
-                                       MSG_WriteByte(&net_message, playerNumber);
-                                       MSG_WriteString(&net_message, client->name);
-                                       MSG_WriteLong(&net_message, client->colors);
-                                       MSG_WriteLong(&net_message, client->frags);
-                                       MSG_WriteLong(&net_message, (int)(realtime - client->connecttime));
-                                       MSG_WriteString(&net_message, client->netconnection ? client->netconnection->address : "botclient");
-                                       StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
-                                       NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
-                                       SZ_Clear(&net_message);
+                                       MSG_WriteLong(&sv_message, 0);
+                                       MSG_WriteByte(&sv_message, CCREP_PLAYER_INFO);
+                                       MSG_WriteByte(&sv_message, playerNumber);
+                                       MSG_WriteString(&sv_message, client->name);
+                                       MSG_WriteLong(&sv_message, client->colors);
+                                       MSG_WriteLong(&sv_message, client->frags);
+                                       MSG_WriteLong(&sv_message, (int)(realtime - client->connecttime));
+                                       if(sv_status_privacy.integer)
+                                               MSG_WriteString(&sv_message, client->netconnection ? "hidden" : "botclient");
+                                       else
+                                               MSG_WriteString(&sv_message, client->netconnection ? client->netconnection->address : "botclient");
+                                       StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
+                                       NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
+                                       SZ_Clear(&sv_message);
                                }
                        }
                        break;
@@ -3056,45 +3536,65 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
                        if(!(islocal || sv_public.integer > -1))
                                break;
+
+                       // no flood check here, as it only returns one cvar for one cvar and clients may iterate quickly
+
                        if (sv.active)
                        {
                                char *prevCvarName;
                                cvar_t *var;
 
                                // find the search start location
-                               prevCvarName = MSG_ReadString();
+                               prevCvarName = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
                                var = Cvar_FindVarAfter(prevCvarName, CVAR_NOTIFY);
 
                                // send the response
-                               SZ_Clear(&net_message);
+                               SZ_Clear(&sv_message);
                                // save space for the header, filled in later
-                               MSG_WriteLong(&net_message, 0);
-                               MSG_WriteByte(&net_message, CCREP_RULE_INFO);
+                               MSG_WriteLong(&sv_message, 0);
+                               MSG_WriteByte(&sv_message, CCREP_RULE_INFO);
                                if (var)
                                {
-                                       MSG_WriteString(&net_message, var->name);
-                                       MSG_WriteString(&net_message, var->string);
+                                       MSG_WriteString(&sv_message, var->name);
+                                       MSG_WriteString(&sv_message, var->string);
                                }
-                               StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
-                               NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
-                               SZ_Clear(&net_message);
+                               StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
+                               NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
+                               SZ_Clear(&sv_message);
+                       }
+                       break;
+               case CCREQ_RCON:
+                       if (developer_extra.integer)
+                               Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RCON from %s.\n", addressstring2);
+                       if (sv.active && !rcon_secure.integer)
+                       {
+                               char password[2048];
+                               char cmd[2048];
+                               char *s;
+                               char *endpos;
+                               const char *userlevel;
+                               strlcpy(password, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(password));
+                               strlcpy(cmd, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(cmd));
+                               s = cmd;
+                               endpos = cmd + strlen(cmd) + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
+                               userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
+                               RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, true);
+                               return true;
                        }
                        break;
                default:
                        break;
                }
-               SZ_Clear(&net_message);
+               SZ_Clear(&sv_message);
                // we may not have liked the packet, but it was a valid control
                // packet, so we're done processing this packet now
                return true;
        }
        if (host_client)
        {
-               if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length, sv.protocol, host_client->spawned ? net_messagetimeout.value : net_connecttimeout.value)) == 2)
+               if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length, sv.protocol, host_client->begun ? net_messagetimeout.value : net_connecttimeout.value)) == 2)
                {
-                       SV_VM_Begin();
                        SV_ReadClientMessage();
-                       SV_VM_End();
                        return ret;
                }
        }
@@ -3105,6 +3605,7 @@ void NetConn_ServerFrame(void)
 {
        int i, length;
        lhnetaddress_t peeraddress;
+       unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
        for (i = 0;i < sv_numsockets;i++)
                while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
                        NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
@@ -3114,9 +3615,7 @@ void NetConn_ServerFrame(void)
                if (host_client->netconnection && realtime > host_client->netconnection->timeout && LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
                {
                        Con_Printf("Client \"%s\" connection timed out\n", host_client->name);
-                       SV_VM_Begin();
                        SV_DropClient(false);
-                       SV_VM_End();
                }
        }
 }
@@ -3126,6 +3625,7 @@ void NetConn_SleepMicroseconds(int microseconds)
        LHNET_SleepUntilPacket_Microseconds(microseconds);
 }
 
+#ifdef CONFIG_MENU
 void NetConn_QueryMasters(qboolean querydp, qboolean queryqw)
 {
        int i, j;
@@ -3154,15 +3654,15 @@ void NetConn_QueryMasters(qboolean querydp, qboolean queryqw)
                                if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
                                {
                                        // search LAN for Quake servers
-                                       SZ_Clear(&net_message);
+                                       SZ_Clear(&cl_message);
                                        // save space for the header, filled in later
-                                       MSG_WriteLong(&net_message, 0);
-                                       MSG_WriteByte(&net_message, CCREQ_SERVER_INFO);
-                                       MSG_WriteString(&net_message, "QUAKE");
-                                       MSG_WriteByte(&net_message, NET_PROTOCOL_VERSION);
-                                       StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
-                                       NetConn_Write(cl_sockets[i], net_message.data, net_message.cursize, &broadcastaddress);
-                                       SZ_Clear(&net_message);
+                                       MSG_WriteLong(&cl_message, 0);
+                                       MSG_WriteByte(&cl_message, CCREQ_SERVER_INFO);
+                                       MSG_WriteString(&cl_message, "QUAKE");
+                                       MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
+                                       StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
+                                       NetConn_Write(cl_sockets[i], cl_message.data, cl_message.cursize, &broadcastaddress);
+                                       SZ_Clear(&cl_message);
 
                                        // search LAN for DarkPlaces servers
                                        NetConn_WriteString(cl_sockets[i], "\377\377\377\377getstatus", &broadcastaddress);
@@ -3179,7 +3679,8 @@ void NetConn_QueryMasters(qboolean querydp, qboolean queryqw)
                                        cmdname = "getservers";
                                        extraoptions = "";
                                }
-                               dpsnprintf(request, sizeof(request), "\377\377\377\377%s %s %u empty full%s", cmdname, gamename, NET_PROTOCOL_VERSION, extraoptions);
+                               memcpy(request, "\377\377\377\377", 4);
+                               dpsnprintf(request+4, sizeof(request)-4, "%s %s %u empty full%s", cmdname, gamenetworkfiltername, NET_PROTOCOL_VERSION, extraoptions);
 
                                // search internet
                                for (masternum = 0;sv_masters[masternum].name;masternum++)
@@ -3260,6 +3761,7 @@ void NetConn_QueryMasters(qboolean querydp, qboolean queryqw)
                M_Update_Return_Reason("No network");
        }
 }
+#endif
 
 void NetConn_Heartbeat(int priority)
 {
@@ -3300,31 +3802,32 @@ static void Net_Heartbeat_f(void)
                Con_Print("No server running, can not heartbeat to master server.\n");
 }
 
-void PrintStats(netconn_t *conn)
+static void PrintStats(netconn_t *conn)
 {
        if ((cls.state == ca_connected && cls.protocol == PROTOCOL_QUAKEWORLD) || (sv.active && sv.protocol == PROTOCOL_QUAKEWORLD))
                Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->outgoing_unreliable_sequence, conn->qw.incoming_sequence);
        else
                Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->nq.sendSequence, conn->nq.receiveSequence);
+       Con_Printf("unreliable messages sent   = %i\n", conn->unreliableMessagesSent);
+       Con_Printf("unreliable messages recv   = %i\n", conn->unreliableMessagesReceived);
+       Con_Printf("reliable messages sent     = %i\n", conn->reliableMessagesSent);
+       Con_Printf("reliable messages received = %i\n", conn->reliableMessagesReceived);
+       Con_Printf("packetsSent                = %i\n", conn->packetsSent);
+       Con_Printf("packetsReSent              = %i\n", conn->packetsReSent);
+       Con_Printf("packetsReceived            = %i\n", conn->packetsReceived);
+       Con_Printf("receivedDuplicateCount     = %i\n", conn->receivedDuplicateCount);
+       Con_Printf("droppedDatagrams           = %i\n", conn->droppedDatagrams);
 }
 
 void Net_Stats_f(void)
 {
        netconn_t *conn;
-       Con_Printf("unreliable messages sent   = %i\n", unreliableMessagesSent);
-       Con_Printf("unreliable messages recv   = %i\n", unreliableMessagesReceived);
-       Con_Printf("reliable messages sent     = %i\n", reliableMessagesSent);
-       Con_Printf("reliable messages received = %i\n", reliableMessagesReceived);
-       Con_Printf("packetsSent                = %i\n", packetsSent);
-       Con_Printf("packetsReSent              = %i\n", packetsReSent);
-       Con_Printf("packetsReceived            = %i\n", packetsReceived);
-       Con_Printf("receivedDuplicateCount     = %i\n", receivedDuplicateCount);
-       Con_Printf("droppedDatagrams           = %i\n", droppedDatagrams);
        Con_Print("connections                =\n");
        for (conn = netconn_list;conn;conn = conn->next)
                PrintStats(conn);
 }
 
+#ifdef CONFIG_MENU
 void Net_Refresh_f(void)
 {
        if (m_state != m_slist) {
@@ -3361,6 +3864,7 @@ void Net_SlistQW_f(void)
        } else
                ServerList_QueryList(true, false, true, false);
 }
+#endif
 
 void NetConn_Init(void)
 {
@@ -3368,10 +3872,15 @@ void NetConn_Init(void)
        lhnetaddress_t tempaddress;
        netconn_mempool = Mem_AllocPool("network connections", 0, NULL);
        Cmd_AddCommand("net_stats", Net_Stats_f, "print network statistics");
+#ifdef CONFIG_MENU
        Cmd_AddCommand("net_slist", Net_Slist_f, "query dp master servers and print all server information");
        Cmd_AddCommand("net_slistqw", Net_SlistQW_f, "query qw master servers and print all server information");
        Cmd_AddCommand("net_refresh", Net_Refresh_f, "query dp master servers and refresh all server information");
+#endif
        Cmd_AddCommand("heartbeat", Net_Heartbeat_f, "send a heartbeat to the master server (updates your server information)");
+       Cvar_RegisterVariable(&net_test);
+       Cvar_RegisterVariable(&net_usesizelimit);
+       Cvar_RegisterVariable(&net_burstreserve);
        Cvar_RegisterVariable(&rcon_restricted_password);
        Cvar_RegisterVariable(&rcon_restricted_commands);
        Cvar_RegisterVariable(&rcon_secure_maxdiff);
@@ -3381,9 +3890,14 @@ void NetConn_Init(void)
        Cvar_RegisterVariable(&net_slist_maxtries);
        Cvar_RegisterVariable(&net_slist_favorites);
        Cvar_RegisterVariable(&net_slist_pause);
+       if(LHNET_DefaultDSCP(-1) >= 0) // register cvar only if supported
+               Cvar_RegisterVariable(&net_tos_dscp);
        Cvar_RegisterVariable(&net_messagetimeout);
        Cvar_RegisterVariable(&net_connecttimeout);
        Cvar_RegisterVariable(&net_connectfloodblockingtimeout);
+       Cvar_RegisterVariable(&net_challengefloodblockingtimeout);
+       Cvar_RegisterVariable(&net_getstatusfloodblockingtimeout);
+       Cvar_RegisterVariable(&net_sourceaddresscheck);
        Cvar_RegisterVariable(&cl_netlocalping);
        Cvar_RegisterVariable(&cl_netpacketloss_send);
        Cvar_RegisterVariable(&cl_netpacketloss_receive);
@@ -3426,10 +3940,15 @@ void NetConn_Init(void)
        }
        cl_numsockets = 0;
        sv_numsockets = 0;
-       net_message.data = net_message_buf;
-       net_message.maxsize = sizeof(net_message_buf);
-       net_message.cursize = 0;
+       cl_message.data = cl_message_buf;
+       cl_message.maxsize = sizeof(cl_message_buf);
+       cl_message.cursize = 0;
+       sv_message.data = sv_message_buf;
+       sv_message.maxsize = sizeof(sv_message_buf);
+       sv_message.cursize = 0;
        LHNET_Init();
+       if (Thread_HasThreads())
+               netconn_mutex = Thread_CreateMutex();
 }
 
 void NetConn_Shutdown(void)
@@ -3437,5 +3956,8 @@ void NetConn_Shutdown(void)
        NetConn_CloseClientPorts();
        NetConn_CloseServerPorts();
        LHNET_Shutdown();
+       if (netconn_mutex)
+               Thread_DestroyMutex(netconn_mutex);
+       netconn_mutex = NULL;
 }