]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - netconn.c
Just in case, make QW sequence numbers unsigned too.
[xonotic/darkplaces.git] / netconn.c
index 77fb60ca4c61d5efbe5a2a8a325ff65aab0efd72..9db3a473c311baab53a27840c628c8a2712d0d50 100755 (executable)
--- a/netconn.c
+++ b/netconn.c
@@ -21,14 +21,22 @@ 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
+#include "hmac.h"
+#include "mdfour.h"
+#include <time.h>
+
 #define QWMASTER_PORT 27000
 #define DPMASTER_PORT 27950
 
 // note this defaults on for dedicated servers, off for listen servers
-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"};
+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 [] =
 {
@@ -37,11 +45,16 @@ static cvar_t sv_masters [] =
        {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.6", "dpmaster.tchr.no - default master server 3 (admin: tChr)"}, // admin: tChr
+       {0, "sv_masterextra2", "107.161.23.68", "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", "[2a03:4000:2:225::51:334d]:27950", "dpmaster.sudo.rm-f.org - default master server 4 (admin: divVerent)"}, // admin: divVerent
+       {0, "sv_masterextra5", "[2604:180::4ac:98c1]:27950", "dpmaster.deathmask.net - default master server 5 ipv6 address of dpmaster.deathmask.net (admin: Willis)"}, // admin: Willis
+#endif
        {0, NULL, NULL, NULL}
 };
 
+#ifdef CONFIG_MENU
 static cvar_t sv_qwmasters [] =
 {
        {CVAR_SAVE, "sv_qwmaster1", "", "user-chosen qwmaster server 1"},
@@ -52,18 +65,28 @@ static cvar_t sv_qwmasters [] =
        {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_qwmasterextra5", "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 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)"};
 
@@ -75,23 +98,16 @@ static cvar_t net_slist_queriesperframe = {0, "net_slist_queriesperframe", "4",
 static cvar_t net_slist_timeout = {0, "net_slist_timeout", "4", "how long to listen for a server information response before giving up"};
 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, "net_slist_favorites", "", "contains a list of IP addresses and ports to always query explicitly"};
-
-static cvar_t gameversion = {0, "gameversion", "0", "version of game data (mod-specific), when client and server gameversion mismatch in the server browser the server is shown as incompatible"};
-static cvar_t rcon_restricted_password = {CVAR_PRIVATE, "rcon_restricted_password", "", "password to authenticate rcon commands in restricted mode"};
+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"};
+static cvar_t rcon_restricted_password = {CVAR_PRIVATE, "rcon_restricted_password", "", "password to authenticate rcon commands in restricted mode; may be set to a string of the form user1:pass1 user2:pass2 user3:pass3 to allow multiple user accounts - the client then has to specify ONE of these combinations"};
 static cvar_t rcon_restricted_commands = {0, "rcon_restricted_commands", "", "allowed commands for rcon when the restricted mode password was used"};
-
-/* 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;
+static cvar_t rcon_secure_maxdiff = {0, "rcon_secure_maxdiff", "5", "maximum time difference between rcon request and server system clock (to protect against replay attack)"};
+extern cvar_t rcon_secure;
+extern cvar_t rcon_secure_challengetimeout;
 
 double masterquerytime = -1000;
 int masterquerycount = 0;
@@ -99,16 +115,17 @@ int masterreplycount = 0;
 int serverquerycount = 0;
 int serverreplycount = 0;
 
-// this is only false if there are still servers left to query
+challenge_t challenge[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;
-// this is pushed a second or two ahead of realtime whenever a master server
-// reply is received, to avoid issuing queries while master replies are still
-// flooding in (which would make a mess of the ping times)
+/// this is pushed a second or two ahead of realtime whenever a master server
+/// 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];
@@ -117,16 +134,22 @@ 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"};
-cvar_t net_address = {0, "net_address", "0.0.0.0", "network address to open ports on"};
-//cvar_t net_netaddress_ipv6 = {0, "net_address_ipv6", "[0:0:0:0:0:0:0:0]", "network address to open ipv6 ports on"};
+cvar_t net_address = {0, "net_address", "", "network address to open ipv4 ports on (if empty, use default interfaces)"};
+cvar_t net_address_ipv6 = {0, "net_address_ipv6", "", "network address to open ipv6 ports on (if empty, use default interfaces)"};
+
+char cl_net_extresponse[NET_EXTRESPONSE_MAX][1400];
+int cl_net_extresponse_count = 0;
+int cl_net_extresponse_last = 0;
 
-char net_extresponse[NET_EXTRESPONSE_MAX][1400];
-int net_extresponse_count = 0;
-int net_extresponse_last = 0;
+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];
@@ -135,15 +158,44 @@ serverlist_infofield_t serverlist_sortbyfield;
 int serverlist_sortflags;
 
 int serverlist_viewcount = 0;
-serverlist_entry_t *serverlist_viewlist[SERVERLIST_VIEWLISTSIZE];
+unsigned short serverlist_viewlist[SERVERLIST_VIEWLISTSIZE];
 
-int serverlist_cachecount;
-serverlist_entry_t serverlist_cache[SERVERLIST_TOTALSIZE];
+int serverlist_maxcachecount = 0;
+int serverlist_cachecount = 0;
+serverlist_entry_t *serverlist_cache = NULL;
 
 qboolean serverlist_consoleoutput;
 
-// helper function to insert a value into the viewset
-// spare entries will be removed
+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(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;
+               }
+       }
+}
+
+/// helper function to insert a value into the viewset
+/// spare entries will be removed
 static void _ServerList_ViewList_Helper_InsertBefore( int index, serverlist_entry_t *entry )
 {
     int i;
@@ -156,10 +208,10 @@ static void _ServerList_ViewList_Helper_InsertBefore( int index, serverlist_entr
        for( ; i > index ; i-- )
                serverlist_viewlist[ i ] = serverlist_viewlist[ i - 1 ];
 
-       serverlist_viewlist[index] = entry;
+       serverlist_viewlist[index] = (int)(entry - serverlist_cache);
 }
 
-// we suppose serverlist_viewcount to be valid, ie > 0
+/// we suppose serverlist_viewcount to be valid, ie > 0
 static void _ServerList_ViewList_Helper_Remove( int index )
 {
        serverlist_viewcount--;
@@ -167,17 +219,24 @@ static void _ServerList_ViewList_Helper_Remove( int index )
                serverlist_viewlist[index] = serverlist_viewlist[index + 1];
 }
 
-// returns true if A should be inserted before B
+/// \returns true if A should be inserted before B
 static qboolean _ServerList_Entry_Compare( serverlist_entry_t *A, serverlist_entry_t *B )
 {
        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;
@@ -218,6 +277,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;
@@ -345,6 +407,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;
@@ -352,25 +416,51 @@ static qboolean _ServerList_Entry_Mask( serverlist_mask_t *mask, serverlist_info
 
 static void ServerList_ViewList_Insert( serverlist_entry_t *entry )
 {
-       int start, end, mid;
-       const char *text;
+       int start, end, mid, i;
+       lhnetaddress_t addr;
 
        // reject incompatible servers
-       if (entry->info.gameversion != gameversion.integer)
+       if(
+               entry->info.gameversion != gameversion.integer
+               &&
+               !(
+                          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
+                )
+       )
                return;
 
        // refresh the "favorite" status
-       text = net_slist_favorites.string;
        entry->info.isfavorite = false;
-       while(COM_ParseToken_Console(&text))
+       if(LHNETADDRESS_FromString(&addr, entry->info.cname, 26000))
        {
-               if(!strcmp(com_token, entry->info.cname))
+               char idfp[FP64_SIZE+1];
+               for(i = 0; i < nFavorites; ++i)
                {
-                       entry->info.isfavorite = true;
-                       break;
+                       if(LHNETADDRESS_Compare(&addr, &favorites[i]) == 0)
+                       {
+                               entry->info.isfavorite = true;
+                               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++ )
@@ -391,11 +481,11 @@ static void ServerList_ViewList_Insert( serverlist_entry_t *entry )
 
        // two special cases
        // check whether to insert it as new first item
-       if( _ServerList_Entry_Compare( entry, serverlist_viewlist[0] ) ) {
+       if( _ServerList_Entry_Compare( entry, ServerList_GetViewEntry(0) ) ) {
                _ServerList_ViewList_Helper_InsertBefore( 0, entry );
                return;
        } // check whether to insert it as new last item
-       else if( !_ServerList_Entry_Compare( entry, serverlist_viewlist[serverlist_viewcount - 1] ) ) {
+       else if( !_ServerList_Entry_Compare( entry, ServerList_GetViewEntry(serverlist_viewcount - 1) ) ) {
                _ServerList_ViewList_Helper_InsertBefore( serverlist_viewcount, entry );
                return;
        }
@@ -405,7 +495,7 @@ static void ServerList_ViewList_Insert( serverlist_entry_t *entry )
        {
                mid = (start + end) / 2;
                // test the item that lies in the middle between start and end
-               if( _ServerList_Entry_Compare( entry, serverlist_viewlist[mid] ) )
+               if( _ServerList_Entry_Compare( entry, ServerList_GetViewEntry(mid) ) )
                        // the item has to be in the upper half
                        end = mid;
                else
@@ -420,7 +510,7 @@ static void ServerList_ViewList_Remove( serverlist_entry_t *entry )
        int i;
        for( i = 0; i < serverlist_viewcount; i++ )
        {
-               if (serverlist_viewlist[i] == entry)
+               if (ServerList_GetViewEntry(i) == entry)
                {
                        _ServerList_ViewList_Helper_Remove(i);
                        break;
@@ -474,6 +564,11 @@ void ServerList_GetPlayerStatistics(int *numplayerspointer, int *maxplayerspoint
 static void _ServerList_Test(void)
 {
        int i;
+       if (serverlist_maxcachecount <= 1024)
+       {
+               serverlist_maxcachecount = 1024;
+               serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
+       }
        for( i = 0 ; i < 1024 ; i++ ) {
                memset( &serverlist_cache[serverlist_cachecount], 0, sizeof( serverlist_entry_t ) );
                serverlist_cache[serverlist_cachecount].info.ping = 1000 + 1024 - i;
@@ -496,6 +591,8 @@ void ServerList_QueryList(qboolean resetcache, qboolean querydp, qboolean queryq
                serverreplycount = 0;
                serverlist_cachecount = 0;
                serverlist_viewcount = 0;
+               serverlist_maxcachecount = 0;
+               serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
        } else {
                // refresh all entries
                int n;
@@ -511,13 +608,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)
@@ -531,11 +634,11 @@ int NetConn_Read(lhnetsocket_t *mysocket, void *data, int maxlength, lhnetaddres
                if (length > 0)
                {
                        LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
-                       Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i from %s:\n", mysocket, addressstring, data, maxlength, peeraddress, length, addressstring2);
+                       Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i from %s:\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length, addressstring2);
                        Com_HexDumpToConsole((unsigned char *)data, length);
                }
                else
-                       Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i\n", mysocket, addressstring, data, maxlength, peeraddress, length);
+                       Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length);
        }
        return length;
 }
@@ -548,13 +651,17 @@ 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];
                LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
                LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
-               Con_Printf("LHNET_Write(%p (%s), %p, %i, %p (%s)) = %i%s\n", mysocket, addressstring, data, length, peeraddress, addressstring2, length, ret == length ? "" : " (ERROR)");
+               Con_Printf("LHNET_Write(%p (%s), %p, %i, %p (%s)) = %i%s\n", (void *)mysocket, addressstring, (void *)data, length, (void *)peeraddress, addressstring2, length, ret == length ? "" : " (ERROR)");
                Com_HexDumpToConsole((unsigned char *)data, length);
        }
        return ret;
@@ -569,27 +676,71 @@ int NetConn_WriteString(lhnetsocket_t *mysocket, const char *string, const lhnet
 qboolean NetConn_CanSend(netconn_t *conn)
 {
        conn->outgoing_packetcounter = (conn->outgoing_packetcounter + 1) % NETGRAPH_PACKETS;
-       conn->outgoing_unreliablesize[conn->outgoing_packetcounter] = NETGRAPH_NOPACKET;
-       conn->outgoing_reliablesize[conn->outgoing_packetcounter] = NETGRAPH_NOPACKET;
-       conn->outgoing_acksize[conn->outgoing_packetcounter] = NETGRAPH_NOPACKET;
+       conn->outgoing_netgraph[conn->outgoing_packetcounter].time            = realtime;
+       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
        {
-               conn->outgoing_unreliablesize[conn->outgoing_packetcounter] = NETGRAPH_CHOKEDPACKET;
+               conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_CHOKEDPACKET;
                return false;
        }
 }
 
-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
        // (this mostly happens on level changes and disconnects and such)
-       if (conn->outgoing_unreliablesize[conn->outgoing_packetcounter] == NETGRAPH_CHOKEDPACKET)
-               conn->outgoing_unreliablesize[conn->outgoing_packetcounter] = NETGRAPH_NOPACKET;
+       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)
        {
@@ -613,9 +764,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)
-               *((int *)(sendbuffer + 0)) = LittleLong((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)
-               *((int *)(sendbuffer + 4)) = LittleLong((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
@@ -632,12 +783,12 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers
                        return -1;
                }
 
-               conn->outgoing_unreliablesize[conn->outgoing_packetcounter] += packetLen;
+               conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
 
                // add the reliable message if there is one
                if (sendreliable)
                {
-                       conn->outgoing_reliablesize[conn->outgoing_packetcounter] += conn->sendMessageLength;
+                       conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += conn->sendMessageLength + 28;
                        memcpy(sendbuffer + packetLen, conn->sendMessage, conn->sendMessageLength);
                        packetLen += conn->sendMessageLength;
                        conn->qw.last_reliable_sequence = conn->outgoing_unreliable_sequence;
@@ -646,15 +797,15 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers
                // add the unreliable message if possible
                if (packetLen + data->cursize <= 1400)
                {
-                       conn->outgoing_unreliablesize[conn->outgoing_packetcounter] += data->cursize;
+                       conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += data->cursize + 28;
                        memcpy(sendbuffer + packetLen, data->data, data->cursize);
                        packetLen += data->cursize;
                }
 
                NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
 
-               packetsSent++;
-               unreliableMessagesSent++;
+               conn->packetsSent++;
+               conn->unreliableMessagesSent++;
 
                totallen += packetLen + 28;
        }
@@ -663,7 +814,8 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers
                unsigned int packetLen;
                unsigned int dataLen;
                unsigned int eom;
-               unsigned int *header;
+               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)
@@ -681,20 +833,20 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers
 
                        packetLen = NET_HEADERSIZE + dataLen;
 
-                       header = (unsigned int *)sendbuffer;
-                       header[0] = BigLong(packetLen | (NETFLAG_DATA | eom));
-                       header[1] = BigLong(conn->nq.sendSequence - 1);
+                       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_reliablesize[conn->outgoing_packetcounter] += packetLen;
+                       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
@@ -730,22 +882,23 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers
 
                        packetLen = NET_HEADERSIZE + dataLen;
 
-                       header = (unsigned int *)sendbuffer;
-                       header[0] = BigLong(packetLen | (NETFLAG_DATA | eom));
-                       header[1] = BigLong(conn->nq.sendSequence);
+                       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++;
 
-                       conn->outgoing_reliablesize[conn->outgoing_packetcounter] += packetLen;
+                       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
@@ -759,30 +912,26 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers
                                return -1;
                        }
 
-                       header = (unsigned int *)sendbuffer;
-                       header[0] = BigLong(packetLen | NETFLAG_UNRELIABLE);
-                       header[1] = BigLong(conn->outgoing_unreliable_sequence);
+                       StoreBigLong(sendbuffer, packetLen | NETFLAG_UNRELIABLE | NetConn_AddCryptoFlag(&conn->crypto));
+                       StoreBigLong(sendbuffer + 4, conn->outgoing_unreliable_sequence);
                        memcpy(sendbuffer + NET_HEADERSIZE, data->data, data->cursize);
 
                        conn->outgoing_unreliable_sequence++;
 
-                       conn->outgoing_unreliablesize[conn->outgoing_packetcounter] += packetLen;
+                       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;
 }
@@ -804,18 +953,24 @@ void NetConn_CloseClientPorts(void)
                        LHNET_CloseSocket(cl_sockets[cl_numsockets - 1]);
 }
 
-void NetConn_OpenClientPort(const char *addressstring, int defaultport)
+static void NetConn_OpenClientPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport)
 {
        lhnetaddress_t address;
        lhnetsocket_t *s;
+       int success;
        char addressstring2[1024];
-       if (LHNETADDRESS_FromString(&address, addressstring, defaultport))
+       if (addressstring && addressstring[0])
+               success = LHNETADDRESS_FromString(&address, addressstring, defaultport);
+       else
+               success = LHNETADDRESS_FromPort(&address, addresstype, defaultport);
+       if (success)
        {
                if ((s = LHNET_OpenSocket_Connectionless(&address)))
                {
                        cl_sockets[cl_numsockets++] = s;
                        LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
-                       Con_Printf("Client opened a socket on address %s\n", addressstring2);
+                       if (addresstype != LHNETADDRESSTYPE_LOOP)
+                               Con_Printf("Client opened a socket on address %s\n", addressstring2);
                }
                else
                {
@@ -831,6 +986,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);
@@ -838,9 +998,11 @@ void NetConn_OpenClientPorts(void)
                Con_Printf("Client using an automatically assigned port\n");
        else
                Con_Printf("Client using port %i\n", port);
-       NetConn_OpenClientPort("local:2", 0);
-       NetConn_OpenClientPort(net_address.string, port);
-       //NetConn_OpenClientPort(net_address_ipv6.string, port);
+       NetConn_OpenClientPort(NULL, LHNETADDRESSTYPE_LOOP, 2);
+       NetConn_OpenClientPort(net_address.string, LHNETADDRESSTYPE_INET4, port);
+#ifdef SUPPORTIPV6
+       NetConn_OpenClientPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port);
+#endif
 }
 
 void NetConn_CloseServerPorts(void)
@@ -850,23 +1012,29 @@ void NetConn_CloseServerPorts(void)
                        LHNET_CloseSocket(sv_sockets[sv_numsockets - 1]);
 }
 
-void NetConn_OpenServerPort(const char *addressstring, int defaultport)
+static qboolean NetConn_OpenServerPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport, int range)
 {
        lhnetaddress_t address;
        lhnetsocket_t *s;
        int port;
        char addressstring2[1024];
+       int success;
 
-       for (port = defaultport; port <= defaultport + 100; port++)
+       for (port = defaultport; port <= defaultport + range; port++)
        {
-               if (LHNETADDRESS_FromString(&address, addressstring, port))
+               if (addressstring && addressstring[0])
+                       success = LHNETADDRESS_FromString(&address, addressstring, port);
+               else
+                       success = LHNETADDRESS_FromPort(&address, addresstype, port);
+               if (success)
                {
                        if ((s = LHNET_OpenSocket_Connectionless(&address)))
                        {
                                sv_sockets[sv_numsockets++] = s;
                                LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
-                               Con_Printf("Server listening on address %s\n", addressstring2);
-                               break;
+                               if (addresstype != LHNETADDRESSTYPE_LOOP)
+                                       Con_Printf("Server listening on address %s\n", addressstring2);
+                               return true;
                        }
                        else
                        {
@@ -878,15 +1046,21 @@ void NetConn_OpenServerPort(const char *addressstring, int defaultport)
                {
                        Con_Printf("Server unable to parse address %s\n", addressstring);
                        // if it cant parse one address, it wont be able to parse another for sure
-                       break;
+                       return false;
                }
        }
+       return false;
 }
 
 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)
@@ -895,11 +1069,15 @@ void NetConn_OpenServerPorts(int opennetports)
        if (sv_netport.integer != port)
                Cvar_SetValueQuick(&sv_netport, port);
        if (cls.state != ca_dedicated)
-               NetConn_OpenServerPort("local:1", 0);
+               NetConn_OpenServerPort(NULL, LHNETADDRESSTYPE_LOOP, 1, 1);
        if (opennetports)
        {
-               NetConn_OpenServerPort(net_address.string, port);
-               //NetConn_OpenServerPort(net_address_ipv6.string, port);
+#ifdef SUPPORTIPV6
+               qboolean ip4success = NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
+               NetConn_OpenServerPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port, ip4success ? 1 : 100);
+#else
+               NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
+#endif
        }
        if (sv_numsockets == 0)
                Host_Error("NetConn_OpenServerPorts: unable to open any ports!");
@@ -942,14 +1120,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;
@@ -976,6 +1154,11 @@ static int clientport2 = -1;
 static int hostport = -1;
 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)
@@ -999,20 +1182,40 @@ void NetConn_UpdateSockets(void)
                if (sv.active)
                        Con_Print("Changing \"port\" will not take effect until \"map\" command is executed.\n");
        }
+
+       for (j = 0;j < MAX_RCONS;j++)
+       {
+               i = (cls.rcon_ringpos + j + 1) % MAX_RCONS;
+               if(cls.rcon_commands[i][0])
+               {
+                       if(realtime > cls.rcon_timeout[i])
+                       {
+                               char s[128];
+                               LHNETADDRESS_ToString(&cls.rcon_addresses[i], s, sizeof(s), true);
+                               Con_Printf("rcon to %s (for command %s) failed: challenge request timed out\n", s, cls.rcon_commands[i]);
+                               cls.rcon_commands[i][0] = 0;
+                               --cls.rcon_trying;
+                               break;
+                       }
+               }
+       }
 }
 
-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;
+               unsigned int sequence, sequence_ack;
                int reliable_ack, reliable_message;
                int count;
-               int qport;
+               //int qport;
 
                sequence = LittleLong(*((int *)(data + 0)));
                sequence_ack = LittleLong(*((int *)(data + 4)));
@@ -1025,12 +1228,12 @@ static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int len
                        if (length < 2)
                                return 0;
                        // TODO: use qport to identify that this client really is who they say they are?  (and elsewhere in the code to identify the connection without a port match?)
-                       qport = LittleShort(*((int *)(data + 8)));
+                       //qport = LittleShort(*((int *)(data + 8)));
                        data += 2;
                        length -= 2;
                }
 
-               packetsReceived++;
+               conn->packetsReceived++;
                reliable_message = (sequence >> 31) & 1;
                reliable_ack = (sequence_ack >> 31) & 1;
                sequence &= ~(1<<31);
@@ -1043,25 +1246,44 @@ 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_unreliablesize[conn->incoming_packetcounter] = NETGRAPH_LOSTPACKET;
-                               conn->incoming_reliablesize[conn->incoming_packetcounter] = NETGRAPH_NOPACKET;
-                               conn->incoming_acksize[conn->incoming_packetcounter] = NETGRAPH_NOPACKET;
+                               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;
                        }
                }
                conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
-               conn->incoming_unreliablesize[conn->incoming_packetcounter] = originallength;
-               conn->incoming_reliablesize[conn->incoming_packetcounter] = NETGRAPH_NOPACKET;
-               conn->incoming_acksize[conn->incoming_packetcounter] = NETGRAPH_NOPACKET;
+               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)
@@ -1072,10 +1294,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
@@ -1083,16 +1314,25 @@ 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)BigLong(((int *)data)[0]);
+               qlength = (unsigned int)BuffBigLong(data);
                flags = qlength & ~NETFLAG_LENGTH_MASK;
                qlength &= NETFLAG_LENGTH_MASK;
                // control packets were already handled
                if (!(flags & NETFLAG_CTL) && qlength == length)
                {
-                       sequence = BigLong(((int *)data)[1]);
-                       packetsReceived++;
+                       sequence = BuffBigLong(data + 4);
+                       conn->packetsReceived++;
                        data += 8;
                        length -= 8;
                        if (flags & NETFLAG_UNRELIABLE)
@@ -1102,29 +1342,50 @@ 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_unreliablesize[conn->incoming_packetcounter] = NETGRAPH_LOSTPACKET;
-                                                       conn->incoming_reliablesize[conn->incoming_packetcounter] = NETGRAPH_NOPACKET;
-                                                       conn->incoming_acksize[conn->incoming_packetcounter] = NETGRAPH_NOPACKET;
+                                                       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;
                                                }
                                        }
                                        conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
-                                       conn->incoming_unreliablesize[conn->incoming_packetcounter] = originallength;
-                                       conn->incoming_reliablesize[conn->incoming_packetcounter] = NETGRAPH_NOPACKET;
-                                       conn->incoming_acksize[conn->incoming_packetcounter] = NETGRAPH_NOPACKET;
+                                       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;
                                        }
                                }
@@ -1134,7 +1395,9 @@ static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int len
                        }
                        else if (flags & NETFLAG_ACK)
                        {
-                               conn->incoming_acksize[conn->incoming_packetcounter] += originallength;
+                               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)
@@ -1149,7 +1412,6 @@ static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int len
                                                        unsigned int packetLen;
                                                        unsigned int dataLen;
                                                        unsigned int eom;
-                                                       unsigned int *header;
 
                                                        conn->sendMessageLength -= MAX_PACKETFRAGMENT;
                                                        memmove(conn->sendMessage, conn->sendMessage+MAX_PACKETFRAGMENT, conn->sendMessageLength);
@@ -1167,17 +1429,17 @@ static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int len
 
                                                        packetLen = NET_HEADERSIZE + dataLen;
 
-                                                       header = (unsigned int *)sendbuffer;
-                                                       header[0] = BigLong(packetLen | (NETFLAG_DATA | eom));
-                                                       header[1] = BigLong(conn->nq.sendSequence);
+                                                       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
@@ -1192,12 +1454,17 @@ static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int len
                        }
                        else if (flags & NETFLAG_DATA)
                        {
-                               unsigned int temppacket[2];
-                               conn->incoming_reliablesize[conn->incoming_packetcounter] += originallength;
-                               conn->outgoing_acksize[conn->outgoing_packetcounter] += 8;
-                               temppacket[0] = BigLong(8 | NETFLAG_ACK);
-                               temppacket[1] = BigLong(sequence);
-                               NetConn_Write(conn->mysocket, (unsigned char *)temppacket, 8, &conn->peeraddress);
+                               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 | NetConn_AddCryptoFlag(&conn->crypto));
+                               StoreBigLong(temppacket + 4, sequence);
+                               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;
@@ -1205,7 +1472,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 );
@@ -1214,20 +1481,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;
                        }
                }
@@ -1235,20 +1511,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
@@ -1267,7 +1567,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);
        }
 }
 
@@ -1278,6 +1578,7 @@ int NetConn_IsLocalGame(void)
        return false;
 }
 
+#ifdef CONFIG_MENU
 static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *addressstring)
 {
        int n;
@@ -1300,6 +1601,11 @@ static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *address
                if (serverlist_cachecount == SERVERLIST_TOTALSIZE)
                        return -1;
 
+               if (serverlist_maxcachecount <= serverlist_cachecount)
+               {
+                       serverlist_maxcachecount += 64;
+                       serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
+               }
                entry = &serverlist_cache[n];
 
                memset(entry, 0, sizeof(*entry));
@@ -1311,16 +1617,6 @@ static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *address
                if (serverlist_consoleoutput)
                        Con_Printf("querying %s\n", addressstring);
                ++serverlist_cachecount;
-
-#if 0
-               // we should not NEED this part...
-               text = net_slist_favorites.string;
-               while(COM_ParseToken_Console(&text))
-               {
-                       if(!strcmp(com_token, addressstring))
-                               entry->isfavorite = 1;
-               }
-#endif
        }
        // if this is the first reply from this server, count it as having replied
        pingtime = (int)((realtime - entry->querytime) * 1000.0 + 0.5);
@@ -1345,7 +1641,18 @@ static void NetConn_ClientParsePacket_ServerList_UpdateCache(int n)
        serverlist_info_t *info = &entry->info;
        // update description strings for engine menu and console output
        dpsnprintf(entry->line1, sizeof(serverlist_cache[n].line1), "^%c%5d^7 ^%c%3u^7/%3u %-65.65s", info->ping >= 300 ? '1' : (info->ping >= 200 ? '3' : '7'), (int)info->ping, ((info->numhumans > 0 && info->numhumans < info->maxplayers) ? (info->numhumans >= 4 ? '7' : '3') : '1'), info->numplayers, info->maxplayers, info->name);
-       dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game, (info->gameversion != gameversion.integer) ? '1' : '4', info->mod, info->map);
+       dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game,
+                       (
+                        info->gameversion != gameversion.integer
+                        &&
+                        !(
+                                   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
+                         )
+                       ) ? '1' : '4',
+                       info->mod, info->map);
        if (entry->query == SQS_QUERIED)
        {
                if(!serverlist_paused)
@@ -1374,14 +1681,20 @@ static qboolean NetConn_ClientParsePacket_ServerList_PrepareQuery( int protocol,
                if( !strcmp( ipstring, serverlist_cache[ n ].info.cname ) )
                        break;
 
-       entry = &serverlist_cache[n];
-
        if( n < serverlist_cachecount ) {
                // the entry has already been queried once or 
                return true;
        }
 
-       memset(entry, 0, sizeof(entry));
+       if (serverlist_maxcachecount <= n)
+       {
+               serverlist_maxcachecount += 64;
+               serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
+       }
+
+       entry = &serverlist_cache[n];
+
+       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));
@@ -1399,13 +1712,97 @@ static qboolean NetConn_ClientParsePacket_ServerList_PrepareQuery( int protocol,
        return true;
 }
 
+static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *senderaddress, const unsigned char *data, int length, qboolean isextended)
+{
+       masterreplycount++;
+       if (serverlist_consoleoutput)
+               Con_Printf("received DarkPlaces %sserver list...\n", isextended ? "extended " : "");
+       while (length >= 7)
+       {
+               char ipstring [128];
+
+               // IPv4 address
+               if (data[0] == '\\')
+               {
+                       unsigned short port = data[5] * 256 + data[6];
+
+                       if (port != 0 && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF))
+                               dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%hu", data[1], data[2], data[3], data[4], port);
+
+                       // move on to next address in packet
+                       data += 7;
+                       length -= 7;
+               }
+               // IPv6 address
+               else if (data[0] == '/' && isextended && length >= 19)
+               {
+                       unsigned short port = data[17] * 256 + data[18];
+
+                       if (port != 0)
+                       {
+#ifdef WHY_JUST_WHY
+                               const char *ifname;
+                               char ifnamebuf[16];
+
+                               /// \TODO: make some basic checks of the IP address (broadcast, ...)
+
+                               ifname = LHNETADDRESS_GetInterfaceName(senderaddress, ifnamebuf, sizeof(ifnamebuf));
+                               if (ifname != NULL)
+                               {
+                                       dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x%%%s]:%hu",
+                                                               (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
+                                                               (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
+                                                               ifname, port);
+                               }
+                               else
+#endif
+                               {
+                                       dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x]:%hu",
+                                                               (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
+                                                               (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
+                                                               port);
+                               }
+                       }
+
+                       // move on to next address in packet
+                       data += 19;
+                       length -= 19;
+               }
+               else
+               {
+                       Con_Print("Error while parsing the server list\n");
+                       break;
+               }
+
+               if (serverlist_consoleoutput && developer_networking.integer)
+                       Con_Printf("Requesting info from DarkPlaces server %s\n", ipstring);
+               
+               if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, ipstring, false ) ) {
+                       break;
+               }
+
+       }
+
+       // begin or resume serverlist queries
+       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
+       char vabuf[1024];
 
        // quakeworld ingame packet
        fromserver = cls.netcon && mysocket == cls.netcon->mysocket && !LHNETADDRESS_Compare(&cls.netcon->peeraddress, peeraddress);
@@ -1430,37 +1827,122 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        Com_HexDumpToConsole(data, length);
                }
 
-               if (length > 10 && !memcmp(string, "challenge ", 10) && cls.connect_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++)
+                       {
+                               // note: this value from i is used outside the loop too...
+                               i = (cls.rcon_ringpos + j) % MAX_RCONS;
+                               if(cls.rcon_commands[i][0])
+                                       if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i]))
+                                               break;
+                       }
+                       if (j < MAX_RCONS)
+                       {
+                               char buf[1500];
+                               char argbuf[1500];
+                               const char *e;
+                               int n;
+                               dpsnprintf(argbuf, sizeof(argbuf), "%s %s", string + 10, cls.rcon_commands[i]);
+                               memcpy(buf, "\377\377\377\377srcon HMAC-MD4 CHALLENGE ", 29);
+
+                               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, (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 + (int)strlen(buf + 46), peeraddress);
+                                       cls.rcon_commands[i][0] = 0;
+                                       --cls.rcon_trying;
+
+                                       for (k = 0;k < MAX_RCONS;k++)
+                                               if(cls.rcon_commands[k][0])
+                                                       if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[k]))
+                                                               break;
+                                       if(k < MAX_RCONS)
+                                       {
+                                               int l;
+                                               NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", peeraddress);
+                                               // extend the timeout on other requests as we asked for a challenge
+                                               for (l = 0;l < MAX_RCONS;l++)
+                                                       if(cls.rcon_commands[l][0])
+                                                               if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[l]))
+                                                                       cls.rcon_timeout[l] = realtime + rcon_secure_challengetimeout.value;
+                                       }
+
+                                       return true; // we used up the challenge, so we can't use this oen for connecting now anyway
+                               }
+                       }
+               }
+               if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying)
                {
                        // darkplaces or quake3
                        char protocolnames[1400];
                        Protocol_Names(protocolnames, sizeof(protocolnames));
-                       Con_Printf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
+                       Con_DPrintf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
+#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\\challenge\\%s", protocolnames, string + 10), peeraddress);
+                       NetConn_WriteString(mysocket, va(vabuf, sizeof(vabuf), "\377\377\377\377connect\\protocol\\darkplaces 3\\protocols\\%s%s\\challenge\\%s", protocolnames, cls.connect_userinfo, string + 10), peeraddress);
                        return true;
                }
                if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying)
                {
                        // darkplaces or quake3
+#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];
                        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;
@@ -1495,17 +1977,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;
 
@@ -1537,16 +2019,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;
 
@@ -1559,26 +2041,15 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        // Extract the IP addresses
                        data += 18;
                        length -= 18;
-                       masterreplycount++;
-                       if (serverlist_consoleoutput)
-                               Con_Print("received DarkPlaces server list...\n");
-                       while (length >= 7 && data[0] == '\\' && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF) && data[5] * 256 + data[6] != 0)
-                       {
-                               dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%u", data[1], data[2], data[3], data[4], data[5] * 256 + data[6]);
-                               if (serverlist_consoleoutput && developer_networking.integer)
-                                       Con_Printf("Requesting info from DarkPlaces server %s\n", ipstring);
-                               
-                               if( !NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, ipstring, false ) ) {
-                                       break;
-                               }
-
-                               // move on to next address in packet
-                               data += 7;
-                               length -= 7;
-                       }
-                       // begin or resume serverlist queries
-                       serverlist_querysleep = false;
-                       serverlist_querywaittime = realtime + 3;
+                       NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, false);
+                       return true;
+               }
+               if (!strncmp(string, "getserversExtResponse", 21) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
+               {
+                       // Extract the IP addresses
+                       data += 21;
+                       length -= 21;
+                       NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, data, length, true);
                        return true;
                }
                if (!memcmp(string, "d\n", 2) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
@@ -1608,19 +2079,20 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        serverlist_querywaittime = realtime + 3;
                        return true;
                }
+#endif
                if (!strncmp(string, "extResponse ", 12))
                {
-                       ++net_extresponse_count;
-                       if(net_extresponse_count > NET_EXTRESPONSE_MAX)
-                               net_extresponse_count = NET_EXTRESPONSE_MAX;
-                       net_extresponse_last = (net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
-                       dpsnprintf(net_extresponse[net_extresponse_last], sizeof(net_extresponse[net_extresponse_last]), "'%s' %s", addressstring2, string + 12);
+                       ++cl_net_extresponse_count;
+                       if(cl_net_extresponse_count > NET_EXTRESPONSE_MAX)
+                               cl_net_extresponse_count = NET_EXTRESPONSE_MAX;
+                       cl_net_extresponse_last = (cl_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
+                       dpsnprintf(cl_net_extresponse[cl_net_extresponse_last], sizeof(cl_net_extresponse[cl_net_extresponse_last]), "\"%s\" %s", addressstring2, string + 12);
                        return true;
                }
                if (!strncmp(string, "ping", 4))
                {
-                       if (developer.integer >= 10)
-                               Con_Printf("Received ping from %s, sending ack\n", addressstring2);
+                       if (developer_extra.integer)
+                               Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
                        NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
                        return true;
                }
@@ -1631,22 +2103,27 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                {
                        // challenge message
                        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\"\n", 28, cls.qw_qport, atoi(string + 1), cls.userinfo), peeraddress);
+                       NetConn_WriteString(mysocket, va(vabuf, sizeof(vabuf), "\377\377\377\377connect %i %i %i \"%s%s\"\n", 28, cls.qw_qport, atoi(string + 1), cls.userinfo, cls.connect_userinfo), peeraddress);
                        return true;
                }
                if (length >= 1 && string[0] == 'j' && cls.connect_trying)
                {
                        // accept message
+#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;
 
@@ -1662,14 +2139,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)
@@ -1690,7 +2167,7 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        }
 
                        NetConn_ClientParsePacket_ServerList_UpdateCache(n);
-
+#endif
                        return true;
                }
                if (string[0] == 'n')
@@ -1710,45 +2187,67 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                return ret;
        }
        // netquake control packets, supported for compatibility only
-       if (length >= 5 && (control = BigLong(*((int *)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:
-                       if (developer.integer >= 10)
-                               Con_Printf("Datagram_ParseConnectionless: received CCREP_ACCEPT from %s.\n", addressstring2);
+                       if (developer_extra.integer)
+                               Con_DPrintf("Datagram_ParseConnectionless: received CCREP_ACCEPT from %s.\n", addressstring2);
                        if (cls.connect_trying)
                        {
                                lhnetaddress_t clientportaddress;
                                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.integer >= 10)
-                               Con_Printf("Datagram_ParseConnectionless: received CCREP_REJECT from %s.\n", addressstring2);
+                       if (developer_extra.integer)
+                               Con_DPrintf("Datagram_ParseConnectionless: received CCREP_REJECT from %s.\n", addressstring2);
                        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.integer >= 10)
-                               Con_Printf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
+                       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)
@@ -1757,29 +2256,35 @@ 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 (developer_extra.integer)
+                               Con_DPrintf("Datagram_ParseConnectionless: received CCREP_RCON from %s.\n", addressstring2);
 
+                       Con_Printf("%s\n", MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
                        break;
                case CCREP_PLAYER_INFO:
                        // we got a CCREP_PLAYER_INFO??
-                       //if (developer.integer >= 10)
+                       //if (developer_extra.integer)
                                Con_Printf("Datagram_ParseConnectionless: received CCREP_PLAYER_INFO from %s.\n", addressstring2);
                        break;
                case CCREP_RULE_INFO:
                        // we got a CCREP_RULE_INFO??
-                       //if (developer.integer >= 10)
+                       //if (developer_extra.integer)
                                Con_Printf("Datagram_ParseConnectionless: received CCREP_RULE_INFO from %s.\n", addressstring2);
                        break;
                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;
@@ -1790,6 +2295,7 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
        return ret;
 }
 
+#ifdef CONFIG_MENU
 void NetConn_QueryQueueFrame(void)
 {
        int index;
@@ -1800,7 +2306,7 @@ void NetConn_QueryQueueFrame(void)
 
        if(!net_slist_pause.integer && serverlist_paused)
                ServerList_RebuildViewList();
-       serverlist_paused = net_slist_pause.integer;
+       serverlist_paused = net_slist_pause.integer != 0;
 
        if (serverlist_querysleep)
                return;
@@ -1878,58 +2384,74 @@ 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);
-               *((int *)net_message.data) = BigLong(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++)
+       {
                while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
+               {
+//                     R_TimeReport("clientreadnetwork");
                        NetConn_ClientParsePacket(cl_sockets[i], readbuffer, length, &peeraddress);
+//                     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;
@@ -1945,15 +2467,16 @@ static void NetConn_BuildChallengeString(char *buffer, int bufferlength)
        buffer[i] = 0;
 }
 
-// (div0) build the full response only if possible; better a getinfo response than no response at all if getstatus won't fit
+/// (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 *str;
 
        // How many clients are there?
        for (i = 0;i < (unsigned int)svs.maxclients;i++)
@@ -1967,34 +2490,34 @@ static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg
        }
 
        *qcstatus = 0;
-       if(prog->globaloffsets.worldstatus >= 0)
+       str = PRVM_GetString(prog, PRVM_serverglobalstring(worldstatus));
+       if(str && *str)
        {
-               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 = str; *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
+       /// \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,
-                                               nb_clients, nb_bots, sv.name, hostname.string, NET_PROTOCOL_VERSION,
+                                               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
@@ -2020,6 +2543,8 @@ static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg
                                int nameind, cleanind, pingvalue;
                                char curchar;
                                char cleanname [sizeof(cl->name)];
+                               const char *str;
+                               prvm_edict_t *ed;
 
                                // Remove all characters '"' and '\' in the player name
                                nameind = 0;
@@ -2043,22 +2568,20 @@ static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg
                                        pingvalue = 0;
 
                                *qcstatus = 0;
-                               if(prog->fieldoffsets.clientstatus >= 0)
+                               ed = PRVM_EDICT_NUM(i + 1);
+                               str = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
+                               if(str && *str)
                                {
-                                       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 = str; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
+                                               if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
+                                                       *p++ = *q;
+                                       *p = 0;
                                }
 
-                               if ((gamemode == GAME_NEXUIZ) && (teamplay.integer > 0))
+                               if (IS_NEXUIZ_DERIVED(gamemode) && (teamplay.integer > 0))
                                {
                                        if(cl->frags == -666) // spectator
                                                strlcpy(teambuf, " 0", sizeof(teambuf));
@@ -2105,39 +2628,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;
                        }
@@ -2147,44 +2671,133 @@ 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");
                }
        }
 }
 
-// returns a string describing the user level, or NULL for auth failure
-const char *RCon_Authenticate(const char *password, const char *s, const char *endpos)
+typedef qboolean (*rcon_matchfunc_t) (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)
 {
-       const char *text;
+       char mdfourbuf[16];
+       long t1, t2;
+
+       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, (int)strlen(password)))
+               return false;
+
+       return !memcmp(mdfourbuf, hash, 16);
+}
+
+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)
+               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))
+                               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, (int)strlen(password)))
+               return false;
+
+       if(memcmp(mdfourbuf, hash, 16))
+               return false;
+
+       // unmark challenge to prevent replay attacks
+       challenge[i].time = 0;
+
+       return true;
+}
+
+static qboolean plaintext_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
+{
+       return !strcmp(password, hash);
+}
+
+/// returns a string describing the user level, or NULL for auth failure
+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];
 
-       if(!strcmp(rcon_password.string, password))
-               return "rcon";
+       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(comparator(peeraddress, buf, password, cs, cslen))
+                               goto allow;
+               userpass_start = userpass_end + 1;
+       }
+       if(userpass_start[0])
+       {
+               userpass_end = userpass_start + strlen(userpass_start);
+               if(comparator(peeraddress, userpass_start, password, cs, cslen))
+                       goto allow;
+       }
+
+       restricted = true;
+       have_usernames = false;
+       userpass_start = rcon_restricted_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(comparator(peeraddress, buf, password, cs, cslen))
+                               goto check;
+               userpass_start = userpass_end + 1;
+       }
+       if(userpass_start[0])
+       {
+               userpass_end = userpass_start + strlen(userpass_start);
+               if(comparator(peeraddress, userpass_start, password, cs, cslen))
+                       goto check;
+       }
        
-       if(strcmp(rcon_restricted_password.string, password))
-               return NULL;
+       return NULL; // DENIED
 
+check:
        for(text = s; text != endpos; ++text)
                if((signed char) *text > 0 && ((signed char) *text < (signed char) ' ' || *text == ';'))
                        return NULL; // block possible exploits against the parser/alias expansion
@@ -2216,7 +2829,7 @@ const char *RCon_Authenticate(const char *password, const char *s, const char *e
                                {
                                        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;
                                }
                        }
@@ -2227,17 +2840,64 @@ match:
                s += l + 1;
        }
 
-       return "restricted rcon";
+allow:
+       userpass_startpass = strchr(userpass_start, ':');
+       if(have_usernames && userpass_startpass && userpass_startpass < userpass_end)
+               return va(vabuf, sizeof(vabuf), "%srcon (username %.*s)", restricted ? "restricted " : "", (int)(userpass_startpass-userpass_start), userpass_start);
+
+       return va(vabuf, sizeof(vabuf), "%srcon", restricted ? "restricted " : "");
+}
+
+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)
+       {
+               // looks like a legitimate rcon command with the correct password
+               const char *s_ptr = s;
+               Con_Printf("server received %s command from %s: ", userlevel, host_client ? host_client->name : addressstring2);
+               while(s_ptr != endpos)
+               {
+                       size_t l = strlen(s_ptr);
+                       if(l)
+                               Con_Printf(" %s;", s_ptr);
+                       s_ptr += l + 1;
+               }
+               Con_Printf("\n");
+
+               if (!host_client || !host_client->netconnection || LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
+                       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, true);
+                               host_client = host_client_save;
+                               // in case it is a command that changes host_client (like restart)
+                       }
+                       s += l + 1;
+               }
+               Con_Rcon_Redirect_End();
+       }
+       else
+       {
+               Con_Printf("server denied rcon access to %s\n", host_client ? host_client->name : addressstring2);
+       }
 }
 
-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], stringbuf[16384];
+       char *s, *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];
+       char vabuf[1024];
 
        if (!sv.active)
                return false;
@@ -2265,18 +2925,46 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                stringbuf[length] = 0;
                string = stringbuf;
 
-               if (developer.integer >= 10)
+               if (developer_extra.integer)
                {
                        Con_Printf("NetConn_ServerParsePacket: %s sent us a command:\n", addressstring2);
                        Com_HexDumpToConsole(data, length);
                }
 
-               if (length >= 12 && !memcmp(string, "getchallenge", 12) && (islocal || sv_public.integer > -2))
+               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 (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address))
-                                       break;
+                               if(challenge[i].time > 0)
+                                       if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address))
+                                               break;
                                if (besttime > challenge[i].time)
                                        besttime = challenge[best = i].time;
                        }
@@ -2288,30 +2976,73 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                challenge[i].address = *peeraddress;
                                NetConn_BuildChallengeString(challenge[i].string, sizeof(challenge[i].string));
                        }
+                       else
+                       {
+                               // flood control: drop if requesting challenge too often
+                               if(challenge[i].time > realtime - net_challengefloodblockingtimeout.value)
+                                       return true;
+                       }
                        challenge[i].time = realtime;
                        // send the challenge
-                       NetConn_WriteString(mysocket, va("\377\377\377\377challenge %s", challenge[i].string), peeraddress);
+                       dpsnprintf(response, sizeof(response), "\377\377\377\377challenge %s", challenge[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) && (islocal || sv_public.integer > -2))
+               if (length > 8 && !memcmp(string, "connect\\", 8))
                {
+                       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 (!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)
+                       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(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((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(vabuf, sizeof(vabuf), "\377\377\377\377reject %s", sv_public_rejectreason.string), 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.integer >= 10)
+                               if (developer_extra.integer)
                                        Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
                                NetConn_WriteString(mysocket, "\377\377\377\377reject Wrong game protocol.", peeraddress);
                                return true;
@@ -2324,30 +3055,65 @@ 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.integer >= 10)
+                                               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
                                        {
                                                // client is still trying to connect,
                                                // so we send a duplicate reply
-                                               if (developer.integer >= 10)
+                                               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
@@ -2357,20 +3123,20 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                if (!client->active && (conn = NetConn_Open(mysocket, peeraddress)))
                                {
                                        // allocated connection
-                                       if (developer.integer >= 10)
+                                       if (developer_extra.integer)
                                                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;
                                }
                        }
 
                        // no empty slots found - server is full
-                       if (developer.integer >= 10)
+                       if (developer_extra.integer)
                                Con_Printf("Datagram_ParseConnectionless: sending \"reject Server is full.\" to %s.\n", addressstring2);
                        NetConn_WriteString(mysocket, "\377\377\377\377reject Server is full.", peeraddress);
 
@@ -2380,14 +3146,17 @@ 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;
 
                        if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), false))
                        {
-                               if (developer.integer >= 10)
-                                       Con_Printf("Sending reply to master %s - %s\n", addressstring2, response);
+                               if (developer_extra.integer)
+                                       Con_DPrintf("Sending reply to master %s - %s\n", addressstring2, response);
                                NetConn_WriteString(mysocket, response, peeraddress);
                        }
                        return true;
@@ -2396,24 +3165,65 @@ 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;
 
                        if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), true))
                        {
-                               if (developer.integer >= 10)
-                                       Con_Printf("Sending reply to client %s - %s\n", addressstring2, response);
+                               if (developer_extra.integer)
+                                       Con_DPrintf("Sending reply to client %s - %s\n", addressstring2, response);
                                NetConn_WriteString(mysocket, response, peeraddress);
                        }
                        return true;
                }
+               if (length >= 37 && !memcmp(string, "srcon HMAC-MD4 TIME ", 20))
+               {
+                       char *password = string + 20;
+                       char *timeval = string + 37;
+                       char *s = strchr(timeval, ' ');
+                       char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
+                       const char *userlevel;
+
+                       if(rcon_secure.integer > 1)
+                               return true;
+
+                       if(!s)
+                               return true; // invalid packet
+                       ++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, false);
+                       return true;
+               }
+               if (length >= 42 && !memcmp(string, "srcon HMAC-MD4 CHALLENGE ", 25))
+               {
+                       char *password = string + 25;
+                       char *challenge = string + 42;
+                       char *s = strchr(challenge, ' ');
+                       char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
+                       const char *userlevel;
+                       if(!s)
+                               return true; // invalid packet
+                       ++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, false);
+                       return true;
+               }
                if (length >= 5 && !memcmp(string, "rcon ", 5))
                {
                        int i;
                        char *s = string + 5;
                        char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
                        char password[64];
+
+                       if(rcon_secure.integer > 0)
+                               return true;
+
                        for (i = 0;!ISWHITESPACE(*s);s++)
                                if (i < (int)sizeof(password) - 1)
                                        password[i++] = *s;
@@ -2422,48 +3232,24 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        password[i] = 0;
                        if (!ISWHITESPACE(password[0]))
                        {
-                               const char *userlevel = RCon_Authenticate(password, s, endpos);
-                               if(userlevel)
-                               {
-                                       // looks like a legitimate rcon command with the correct password
-                                       char *s_ptr = s;
-                                       Con_Printf("server received %s command from %s: ", userlevel, host_client ? host_client->name : addressstring2);
-                                       while(s_ptr != endpos)
-                                       {
-                                               size_t l = strlen(s_ptr);
-                                               if(l)
-                                                       Con_Printf(" %s;", s_ptr);
-                                               s_ptr += l + 1;
-                                       }
-                                       Con_Printf("\n");
-
-                                       if (!host_client || !host_client->netconnection || LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
-                                               Con_Rcon_Redirect_Init(mysocket, peeraddress);
-                                       while(s != endpos)
-                                       {
-                                               size_t l = strlen(s);
-                                               if(l)
-                                               {
-                                                       client_t *host_client_save = host_client;
-                                                       Cmd_ExecuteString(s, src_command);
-                                                       host_client = host_client_save;
-                                                       // in case it is a command that changes host_client (like restart)
-                                               }
-                                               s += l + 1;
-                                       }
-                                       Con_Rcon_Redirect_End();
-                               }
-                               else
-                               {
-                                       Con_Printf("server denied rcon access to %s\n", host_client ? host_client->name : addressstring2);
-                               }
+                               const char *userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
+                               RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
                        }
                        return true;
                }
+               if (!strncmp(string, "extResponse ", 12))
+               {
+                       ++sv_net_extresponse_count;
+                       if(sv_net_extresponse_count > NET_EXTRESPONSE_MAX)
+                               sv_net_extresponse_count = NET_EXTRESPONSE_MAX;
+                       sv_net_extresponse_last = (sv_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
+                       dpsnprintf(sv_net_extresponse[sv_net_extresponse_last], sizeof(sv_net_extresponse[sv_net_extresponse_last]), "'%s' %s", addressstring2, string + 12);
+                       return true;
+               }
                if (!strncmp(string, "ping", 4))
                {
-                       if (developer.integer >= 10)
-                               Con_Printf("Received ping from %s, sending ack\n", addressstring2);
+                       if (developer_extra.integer)
+                               Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
                        NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
                        return true;
                }
@@ -2478,39 +3264,51 @@ 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 = BigLong(*((int *)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;
                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:
-                       if (developer.integer >= 10)
-                               Con_Printf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
-                       if(!islocal && sv_public.integer <= -2)
+                       if (developer_extra.integer)
+                               Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
+                       if(!(islocal || sv_public.integer > -2))
+                       {
+                               if (developer_extra.integer)
+                                       Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"%s\" to %s.\n", sv_public_rejectreason.string, 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, va(vabuf, sizeof(vabuf), "%s\n", sv_public_rejectreason.string));
+                               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.integer >= 10)
-                                       Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
-                               SZ_Clear(&net_message);
+                               if (developer_extra.integer)
+                                       Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
+                               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");
-                               *((int *)net_message.data) = BigLong(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;
                        }
 
@@ -2523,32 +3321,44 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                        // or coming back from a timeout
                                        // (if so, keep their stuff intact)
 
+                                       crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
+                                       if((crypto && crypto->authenticated) || client->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.integer >= 10)
-                                               Con_Printf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
-                                       SZ_Clear(&net_message);
+                                       if (developer_extra.integer)
+                                               Con_DPrintf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
+                                       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)));
-                                       *((int *)net_message.data) = BigLong(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(client->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();
+                                       if (client->begun)
                                                SV_SendServerinfo(client);
-                                               SV_VM_End();
-                                       }
                                        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
@@ -2560,146 +3370,175 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                        // connect to the client
                                        // everything is allocated, just fill in the details
                                        strlcpy (conn->address, addressstring2, sizeof (conn->address));
-                                       if (developer.integer >= 10)
-                                               Con_Printf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
+                                       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)));
-                                       *((int *)net_message.data) = BigLong(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;
                                }
                        }
 
-                       if (developer.integer >= 10)
-                               Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
+                       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");
-                       *((int *)net_message.data) = BigLong(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.integer >= 10)
-                               Con_Printf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
-                       if(!islocal && sv_public.integer <= -1)
+                       if (developer_extra.integer)
+                               Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_SERVER_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 && !strcmp(MSG_ReadString(), "QUAKE"))
+
+                       if (sv.active && !strcmp(MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), "QUAKE"))
                        {
                                int numclients;
                                char myaddressstring[128];
-                               if (developer.integer >= 10)
-                                       Con_Printf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
-                               SZ_Clear(&net_message);
+                               if (developer_extra.integer)
+                                       Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
+                               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);
-                               *((int *)net_message.data) = BigLong(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:
-                       if (developer.integer >= 10)
-                               Con_Printf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
-                       if(!islocal && sv_public.integer <= -1)
+                       if (developer_extra.integer)
+                               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");
-                                       *((int *)net_message.data) = BigLong(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;
                case CCREQ_RULE_INFO:
-                       if (developer.integer >= 10)
-                               Con_Printf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
-                       if(!islocal && sv_public.integer <= -1)
+                       if (developer_extra.integer)
+                               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);
                                }
-                               *((int *)net_message.data) = BigLong(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;
                }
        }
@@ -2710,6 +3549,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);
@@ -2719,9 +3559,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();
                }
        }
 }
@@ -2731,14 +3569,13 @@ void NetConn_SleepMicroseconds(int microseconds)
        LHNET_SleepUntilPacket_Microseconds(microseconds);
 }
 
+#ifdef CONFIG_MENU
 void NetConn_QueryMasters(qboolean querydp, qboolean queryqw)
 {
-       int i;
+       int i, j;
        int masternum;
        lhnetaddress_t masteraddress;
        lhnetaddress_t broadcastaddress;
-       lhnetaddress_t serveraddress;
-       const char *text;
        char request[256];
 
        if (serverlist_cachecount >= SERVERLIST_TOTALSIZE)
@@ -2755,27 +3592,38 @@ void NetConn_QueryMasters(qboolean querydp, qboolean queryqw)
                {
                        if (cl_sockets[i])
                        {
+                               const char *cmdname, *extraoptions;
                                int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
 
                                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);
-                                       *((int *)net_message.data) = BigLong(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);
                                }
 
                                // build the getservers message to send to the dpmaster master servers
-                               dpsnprintf(request, sizeof(request), "\377\377\377\377getservers %s %u empty full\x0A", gamename, NET_PROTOCOL_VERSION);
+                               if (LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == LHNETADDRESSTYPE_INET6)
+                               {
+                                       cmdname = "getserversExt";
+                                       extraoptions = " ipv4 ipv6";  // ask for IPv4 and IPv6 servers
+                               }
+                               else
+                               {
+                                       cmdname = "getservers";
+                                       extraoptions = "";
+                               }
+                               dpsnprintf(request, sizeof(request), "\377\377\377\377%s %s %u empty full%s", cmdname, gamenetworkfiltername, NET_PROTOCOL_VERSION, extraoptions);
 
                                // search internet
                                for (masternum = 0;sv_masters[masternum].name;masternum++)
@@ -2788,12 +3636,12 @@ void NetConn_QueryMasters(qboolean querydp, qboolean queryqw)
                                }
 
                                // search favorite servers
-                               text = net_slist_favorites.string;
-                               while(COM_ParseToken_Console(&text))
+                               for(j = 0; j < nFavorites; ++j)
                                {
-                                       if(LHNETADDRESS_FromString(&serveraddress, com_token, 26000) && LHNETADDRESS_GetAddressType(&masteraddress) == af)
+                                       if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
                                        {
-                                               NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, com_token, true );
+                                               if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
+                                                       NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, request, true );
                                        }
                                }
                        }
@@ -2836,16 +3684,15 @@ void NetConn_QueryMasters(qboolean querydp, qboolean queryqw)
                                }
 
                                // search favorite servers
-                               text = net_slist_favorites.string;
-                               while(COM_ParseToken_Console(&text))
+                               for(j = 0; j < nFavorites; ++j)
                                {
-                                       if(LHNETADDRESS_FromString(&serveraddress, com_token, 26000) && LHNETADDRESS_GetAddressType(&masteraddress) == af)
+                                       if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
                                        {
-                                               // writing AND querying to catch replies for both
-                                               // protocols (in case DP has been queried above, this
-                                               // would only try the DP protocol otherwise)
-                                               NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &serveraddress);
-                                               NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, com_token, true );
+                                               if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
+                                               {
+                                                       NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &favorites[j]);
+                                                       NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, request, true );
+                                               }
                                        }
                                }
                        }
@@ -2857,6 +3704,7 @@ void NetConn_QueryMasters(qboolean querydp, qboolean queryqw)
                M_Update_Return_Reason("No network");
        }
 }
+#endif
 
 void NetConn_Heartbeat(int priority)
 {
@@ -2897,31 +3745,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) {
@@ -2958,6 +3807,7 @@ void Net_SlistQW_f(void)
        } else
                ServerList_QueryList(true, false, true, false);
 }
+#endif
 
 void NetConn_Init(void)
 {
@@ -2965,21 +3815,31 @@ 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);
        Cvar_RegisterVariable(&net_slist_queriespersecond);
        Cvar_RegisterVariable(&net_slist_queriesperframe);
        Cvar_RegisterVariable(&net_slist_timeout);
        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(&cl_netlocalping);
        Cvar_RegisterVariable(&cl_netpacketloss_send);
        Cvar_RegisterVariable(&cl_netpacketloss_receive);
@@ -2988,12 +3848,15 @@ void NetConn_Init(void)
        Cvar_RegisterVariable(&cl_netport);
        Cvar_RegisterVariable(&sv_netport);
        Cvar_RegisterVariable(&net_address);
-       //Cvar_RegisterVariable(&net_address_ipv6);
+       Cvar_RegisterVariable(&net_address_ipv6);
        Cvar_RegisterVariable(&sv_public);
+       Cvar_RegisterVariable(&sv_public_rejectreason);
        Cvar_RegisterVariable(&sv_heartbeatperiod);
        for (i = 0;sv_masters[i].name;i++)
                Cvar_RegisterVariable(&sv_masters[i]);
        Cvar_RegisterVariable(&gameversion);
+       Cvar_RegisterVariable(&gameversion_min);
+       Cvar_RegisterVariable(&gameversion_max);
 // COMMANDLINEOPTION: Server: -ip <ipaddress> sets the ip address of this machine for purposes of networking (default 0.0.0.0 also known as INADDR_ANY), use only if you have multiple network adapters and need to choose one specifically.
        if ((i = COM_CheckParm("-ip")) && i + 1 < com_argc)
        {
@@ -3019,10 +3882,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)
@@ -3030,5 +3898,8 @@ void NetConn_Shutdown(void)
        NetConn_CloseClientPorts();
        NetConn_CloseServerPorts();
        LHNET_Shutdown();
+       if (netconn_mutex)
+               Thread_DestroyMutex(netconn_mutex);
+       netconn_mutex = NULL;
 }