]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - netconn.c
reworked PRVM_EDICTFIELD* and PRVM_GLOBALFIELD* usage to have more
[xonotic/darkplaces.git] / netconn.c
index 1dfb0cc008baa2fab45f1d520fbd7cf991ac0ec5..65b64830067da60339105a1a36e91d4cacd5cd98 100755 (executable)
--- a/netconn.c
+++ b/netconn.c
@@ -32,8 +32,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #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 [] =
 {
@@ -44,6 +46,9 @@ static cvar_t sv_masters [] =
        {0, "sv_masterextra1", "69.59.212.88", "ghdigital.com - default master server 1 (admin: LordHavoc)"}, // admin: LordHavoc
        {0, "sv_masterextra2", "64.22.107.125", "dpmaster.deathmask.net - default master server 2 (admin: Willis)"}, // admin: Willis
        {0, "sv_masterextra3", "92.62.40.73", "dpmaster.tchr.no - default master server 3 (admin: tChr)"}, // admin: tChr
+#ifdef SUPPORTIPV6
+       {0, "sv_masterextra4", "[2001:41d0:2:1628::4450]:27950", "dpmaster.div0.qc.to - default master server 4 (admin: divVerent)"}, // admin: divVerent
+#endif
        {0, NULL, NULL, NULL}
 };
 
@@ -84,10 +89,11 @@ static cvar_t net_slist_favorites = {CVAR_SAVE | CVAR_NQUSERINFOHACK, "net_slist
 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"};
+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"};
 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;
 
 /* statistic counters */
 static int packetsSent = 0;
@@ -107,6 +113,8 @@ int masterreplycount = 0;
 int serverquerycount = 0;
 int serverreplycount = 0;
 
+challenge_t challenge[MAX_CHALLENGES];
+
 /// this is only false if there are still servers left to query
 static qboolean serverlist_querysleep = true;
 static qboolean serverlist_paused = false;
@@ -117,6 +125,8 @@ static double serverlist_querywaittime = 0;
 
 static unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
 static unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
+static unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
+static unsigned char cryptoreadbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
 
 static int cl_numsockets;
 static lhnetsocket_t *cl_sockets[16];
@@ -131,9 +141,13 @@ cvar_t sv_netport = {0, "port", "26000", "server port for players to connect to"
 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 net_extresponse[NET_EXTRESPONSE_MAX][1400];
-int net_extresponse_count = 0;
-int net_extresponse_last = 0;
+char cl_net_extresponse[NET_EXTRESPONSE_MAX][1400];
+int cl_net_extresponse_count = 0;
+int cl_net_extresponse_last = 0;
+
+char sv_net_extresponse[NET_EXTRESPONSE_MAX][1400];
+int sv_net_extresponse_count = 0;
+int sv_net_extresponse_last = 0;
 
 // ServerList interface
 serverlist_mask_t serverlist_andmasks[SERVERLIST_ANDMASKCOUNT];
@@ -143,25 +157,39 @@ 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;
 
 static int nFavorites = 0;
-static lhnetaddress_t favorites[256];
+static lhnetaddress_t favorites[MAX_FAVORITESERVERS];
+static int nFavorites_idfp = 0;
+static char favorites_idfp[MAX_FAVORITESERVERS][FP64_SIZE+1];
 
 void NetConn_UpdateFavorites(void)
 {
        const char *p;
        nFavorites = 0;
+       nFavorites_idfp = 0;
        p = net_slist_favorites.string;
        while((size_t) nFavorites < sizeof(favorites) / sizeof(*favorites) && COM_ParseToken_Console(&p))
        {
-               if(LHNETADDRESS_FromString(&favorites[nFavorites], com_token, 26000))
-                       ++nFavorites;
+               if(com_token[0] != '[' && strlen(com_token) == FP64_SIZE && !strchr(com_token, '.'))
+               // currently 44 bytes, longest possible IPv6 address: 39 bytes, so this works
+               // (if v6 address contains port, it must start with '[')
+               {
+                       strlcpy(favorites_idfp[nFavorites_idfp], com_token, sizeof(favorites_idfp[nFavorites_idfp]));
+                       ++nFavorites_idfp;
+               }
+               else 
+               {
+                       if(LHNETADDRESS_FromString(&favorites[nFavorites], com_token, 26000))
+                               ++nFavorites;
+               }
        }
 }
 
@@ -179,7 +207,7 @@ 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
@@ -395,6 +423,7 @@ static void ServerList_ViewList_Insert( serverlist_entry_t *entry )
        entry->info.isfavorite = false;
        if(LHNETADDRESS_FromString(&addr, entry->info.cname, 26000))
        {
+               char idfp[FP64_SIZE+1];
                for(i = 0; i < nFavorites; ++i)
                {
                        if(LHNETADDRESS_Compare(&addr, &favorites[i]) == 0)
@@ -403,6 +432,17 @@ static void ServerList_ViewList_Insert( serverlist_entry_t *entry )
                                break;
                        }
                }
+               if(Crypto_RetrieveHostKey(&addr, 0, NULL, 0, idfp, sizeof(idfp), NULL))
+               {
+                       for(i = 0; i < nFavorites_idfp; ++i)
+                       {
+                               if(!strcmp(idfp, favorites_idfp[i]))
+                               {
+                                       entry->info.isfavorite = true;
+                                       break;
+                               }
+                       }
+               }
        }
 
        // FIXME: change this to be more readable (...)
@@ -425,11 +465,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;
        }
@@ -439,7 +479,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
@@ -454,7 +494,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;
@@ -508,6 +548,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;
@@ -530,6 +575,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;
@@ -648,9 +695,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, (unsigned int)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, (unsigned int)conn->qw.incoming_sequence | ((unsigned int)conn->qw.incoming_reliable_sequence<<31));
                packetLen = 8;
                conn->outgoing_unreliable_sequence++;
                // client sends qport in every packet
@@ -698,7 +745,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)
@@ -716,20 +764,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));
+                       StoreBigLong(sendbuffer + 4, conn->nq.sendSequence - 1);
                        memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
 
                        conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
 
-                       if (NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress) == (int)packetLen)
+                       sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
+                       if (sendme && NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress) == (int)sendmelen)
                        {
                                conn->lastSendTime = realtime;
                                packetsReSent++;
                        }
 
-                       totallen += packetLen + 28;
+                       totallen += sendmelen + 28;
                }
 
                // if we have a new reliable message to send, do so
@@ -765,22 +813,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));
+                       StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
                        memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
 
                        conn->nq.sendSequence++;
 
                        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, sendmelen, &conn->peeraddress);
 
                        conn->lastSendTime = realtime;
                        packetsSent++;
                        reliableMessagesSent++;
 
-                       totallen += packetLen + 28;
+                       totallen += sendmelen + 28;
                }
 
                // if we have an unreliable message to send, do so
@@ -794,21 +843,22 @@ 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);
+                       StoreBigLong(sendbuffer + 4, conn->outgoing_unreliable_sequence);
                        memcpy(sendbuffer + NET_HEADERSIZE, data->data, data->cursize);
 
                        conn->outgoing_unreliable_sequence++;
 
                        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, sendmelen, &conn->peeraddress);
 
                        packetsSent++;
                        unreliableMessagesSent++;
 
-                       totallen += packetLen + 28;
+                       totallen += sendmelen + 28;
                }
        }
 
@@ -855,7 +905,8 @@ void NetConn_OpenClientPort(const char *addressstring, lhnetaddresstype_t addres
                {
                        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
                {
@@ -880,7 +931,9 @@ void NetConn_OpenClientPorts(void)
                Con_Printf("Client using port %i\n", port);
        NetConn_OpenClientPort(NULL, LHNETADDRESSTYPE_LOOP, 2);
        NetConn_OpenClientPort(net_address.string, LHNETADDRESSTYPE_INET4, port);
+#ifdef SUPPORTIPV6
        NetConn_OpenClientPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port);
+#endif
 }
 
 void NetConn_CloseServerPorts(void)
@@ -910,7 +963,8 @@ qboolean NetConn_OpenServerPort(const char *addressstring, lhnetaddresstype_t ad
                        {
                                sv_sockets[sv_numsockets++] = s;
                                LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
-                               Con_Printf("Server listening on address %s\n", addressstring2);
+                               if (addresstype != LHNETADDRESSTYPE_LOOP)
+                                       Con_Printf("Server listening on address %s\n", addressstring2);
                                return true;
                        }
                        else
@@ -944,8 +998,12 @@ void NetConn_OpenServerPorts(int opennetports)
                NetConn_OpenServerPort(NULL, LHNETADDRESSTYPE_LOOP, 1, 1);
        if (opennetports)
        {
+#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!");
@@ -1022,6 +1080,8 @@ static int clientport2 = -1;
 static int hostport = -1;
 void NetConn_UpdateSockets(void)
 {
+       int i, j;
+
        if (cls.state != ca_dedicated)
        {
                if (clientport2 != cl_netport.integer)
@@ -1045,9 +1105,26 @@ 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;
        if (length < 8)
@@ -1058,7 +1135,7 @@ static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int len
                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)));
@@ -1071,7 +1148,7 @@ 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;
                }
@@ -1131,15 +1208,24 @@ 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;
 
-               qlength = (unsigned int)BigLong(((int *)data)[0]);
+               originallength = length;
+               data = (const unsigned char *) Crypto_DecryptPacket(&conn->crypto, data, length, cryptoreadbuffer, &length, sizeof(cryptoreadbuffer));
+               if(!data)
+                       return 0;
+               if(length < 8)
+                       return 0;
+
+               qlength = (unsigned int)BuffBigLong(data);
                flags = qlength & ~NETFLAG_LENGTH_MASK;
                qlength &= NETFLAG_LENGTH_MASK;
                // control packets were already handled
                if (!(flags & NETFLAG_CTL) && qlength == length)
                {
-                       sequence = BigLong(((int *)data)[1]);
+                       sequence = BuffBigLong(data + 4);
                        packetsReceived++;
                        data += 8;
                        length -= 8;
@@ -1199,7 +1285,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);
@@ -1217,14 +1302,14 @@ 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));
+                                                       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, sendmelen, &conn->peeraddress) == (int)sendmelen)
                                                        {
                                                                conn->lastSendTime = realtime;
                                                                packetsSent++;
@@ -1242,12 +1327,14 @@ static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int len
                        }
                        else if (flags & NETFLAG_DATA)
                        {
-                               unsigned int temppacket[2];
+                               unsigned char temppacket[8];
                                conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes   += originallength + 28;
                                conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes        += 8 + 28;
-                               temppacket[0] = BigLong(8 | NETFLAG_ACK);
-                               temppacket[1] = BigLong(sequence);
-                               NetConn_Write(conn->mysocket, (unsigned char *)temppacket, 8, &conn->peeraddress);
+                               StoreBigLong(temppacket, 8 | NETFLAG_ACK);
+                               StoreBigLong(temppacket + 4, sequence);
+                               sendme = Crypto_EncryptPacket(&conn->crypto, temppacket, 8, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
+                               if(sendme)
+                                       NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress);
                                if (sequence == conn->nq.receiveSequence)
                                {
                                        conn->lastMessageTime = realtime;
@@ -1287,6 +1374,7 @@ static int NetConn_ReceivedMessage(netconn_t *conn, unsigned char *data, int len
 
 void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, protocolversion_t initialprotocol)
 {
+       crypto_t *crypto;
        cls.connect_trying = false;
        M_Update_Return_Reason("");
        // the connection request succeeded, stop current connection and set up a new connection
@@ -1296,6 +1384,19 @@ void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peer
                Host_ShutdownServer ();
        // allocate a net connection to keep track of things
        cls.netcon = NetConn_Open(mysocket, peeraddress);
+       crypto = &cls.crypto;
+       if(crypto && crypto->authenticated)
+       {
+               Crypto_ServerFinishInstance(&cls.netcon->crypto, crypto);
+               Con_Printf("%s connection to %s has been established: server is %s@%.*s, I am %.*s@%.*s\n",
+                               crypto->use_aes ? "Encrypted" : "Authenticated",
+                               cls.netcon->address,
+                               crypto->server_idfp[0] ? crypto->server_idfp : "-",
+                               crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-",
+                               crypto_keyfp_recommended_length, crypto->client_idfp[0] ? crypto->client_idfp : "-",
+                               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;
        m_state = m_none;
@@ -1350,6 +1451,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));
@@ -1425,13 +1531,19 @@ 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;
        }
 
+       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)
@@ -1478,6 +1590,7 @@ static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *sen
 
                        if (port != 0)
                        {
+#ifdef WHY_JUST_WHY
                                const char *ifname;
 
                                /// \TODO: make some basic checks of the IP address (broadcast, ...)
@@ -1485,16 +1598,17 @@ static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *sen
                                ifname = LHNETADDRESS_GetInterfaceName(senderaddress);
                                if (ifname != NULL)
                                {
-                                       dpsnprintf (ipstring, sizeof (ipstring), "[%x%02x:%x%02x:%x%02x:%x%02x:%x%02x:%x%02x:%x%02x:%x%02x%%%s]:%hu",
-                                                               data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8],
-                                                               data[9], data[10], data[11], data[12], data[13], data[14], data[15], data[16],
+                                       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%02x:%x%02x:%x%02x:%x%02x:%x%02x:%x%02x:%x%02x:%x%02x]:%hu",
-                                                               data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8],
-                                                               data[9], data[10], data[11], data[12], data[13], data[14], data[15], data[16],
+                                       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);
                                }
                        }
@@ -1530,6 +1644,8 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
        const char *s;
        char *string, addressstring2[128], ipstring[32];
        char stringbuf[16384];
+       char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
+       size_t sendlength;
 
        // quakeworld ingame packet
        fromserver = cls.netcon && mysocket == cls.netcon->mysocket && !LHNETADDRESS_Compare(&cls.netcon->peeraddress, peeraddress);
@@ -1554,41 +1670,95 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        Com_HexDumpToConsole(data, length);
                }
 
-               if (length > 10 && !memcmp(string, "challenge ", 10) && cls.rcon_trying)
+               sendlength = sizeof(senddata) - 4;
+               switch(Crypto_ClientParsePacket(string, length, senddata+4, &sendlength, peeraddress))
                {
-                       int i;
-                       for (i = 0;i < MAX_RCONS;i++)
+                       case CRYPTO_NOMATCH:
+                               // nothing to do
+                               break;
+                       case CRYPTO_MATCH:
+                               if(sendlength)
+                               {
+                                       memcpy(senddata, "\377\377\377\377", 4);
+                                       NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
+                               }
+                               break;
+                       case CRYPTO_DISCARD:
+                               if(sendlength)
+                               {
+                                       memcpy(senddata, "\377\377\377\377", 4);
+                                       NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
+                               }
+                               return true;
+                               break;
+                       case CRYPTO_REPLACE:
+                               string = senddata+4;
+                               length = 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 (i < MAX_RCONS)
+                       }
+                       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);
-                               if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, strlen(argbuf), (unsigned char *) rcon_password.string, strlen(rcon_password.string)))
+
+                               e = strchr(rcon_password.string, ' ');
+                               n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
+
+                               if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, strlen(argbuf), (unsigned char *) rcon_password.string, n))
                                {
+                                       int k;
                                        buf[45] = ' ';
                                        strlcpy(buf + 46, argbuf, sizeof(buf) - 46);
                                        NetConn_Write(mysocket, buf, 46 + 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)
+               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);
                        M_Update_Return_Reason("Got challenge response");
                        // 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("\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)
@@ -1600,7 +1770,7 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                }
                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);
@@ -1747,17 +1917,17 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                }
                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;
                }
@@ -1772,7 +1942,7 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        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("\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)
@@ -1847,7 +2017,7 @@ 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 && (control = BuffBigLong(data)) && (control & (~NETFLAG_LENGTH_MASK)) == (int)NETFLAG_CTL && (control & NETFLAG_LENGTH_MASK) == length && !ENCRYPTION_REQUIRED)
        {
                int n;
                serverlist_info_t *info;
@@ -1861,13 +2031,28 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                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());
+                               // extra ProQuake stuff
+                               if (length >= 6)
+                                       cls.proquake_servermod = MSG_ReadByte(); // MOD_PROQUAKE
+                               else
+                                       cls.proquake_servermod = 0;
+                               if (length >= 7)
+                                       cls.proquake_serverversion = MSG_ReadByte(); // version * 10
+                               else
+                                       cls.proquake_serverversion = 0;
+                               if (length >= 8)
+                                       cls.proquake_serverflags = MSG_ReadByte(); // 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);
                                M_Update_Return_Reason("Accepted");
@@ -1875,14 +2060,14 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        }
                        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());
                        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);
                        // LordHavoc: because the quake server may report weird addresses
                        // we just ignore it and keep the real address
                        MSG_ReadString();
@@ -1904,19 +2089,19 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
 
                        break;
                case CCREP_RCON: // RocketGuy: ProQuake rcon support
-                       if (developer.integer >= 10)
-                               Con_Printf("Datagram_ParseConnectionless: received CCREP_RCON from %s.\n", addressstring2);
+                       if (developer_extra.integer)
+                               Con_DPrintf("Datagram_ParseConnectionless: received CCREP_RCON from %s.\n", addressstring2);
 
                        Con_Printf("%s\n", MSG_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:
@@ -2048,13 +2233,28 @@ void NetConn_ClientFrame(void)
                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));
+               // extended proquake stuff
+               MSG_WriteByte(&net_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(&net_message, 34); // version * 10
+               MSG_WriteByte(&net_message, 0); // flags
+               MSG_WriteLong(&net_message, 0); // password
+               // write the packetsize now...
+               StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
                NetConn_Write(cls.connect_mysocket, net_message.data, net_message.cursize, &cls.connect_address);
                SZ_Clear(&net_message);
        }
        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");
+               }
+       }
        NetConn_QueryQueueFrame();
        if (cls.netcon && realtime > cls.netcon->timeout && !sv.active)
        {
@@ -2064,15 +2264,6 @@ void NetConn_ClientFrame(void)
        }
 }
 
-#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;
@@ -2095,6 +2286,8 @@ static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg
        unsigned int nb_clients = 0, nb_bots = 0, i;
        int length;
        char teambuf[3];
+       const char *crypto_idstring;
+       const char *str;
 
        SV_VM_Begin();
 
@@ -2110,34 +2303,34 @@ static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg
        }
 
        *qcstatus = 0;
-       if(prog->globaloffsets.worldstatus >= 0)
+       str = PRVM_GetString(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 && p - qcstatus < (ptrdiff_t)(sizeof(qcstatus)) - 1; ++q)
+                       if(*q != '\\' && *q != '\n')
+                               *p++ = *q;
+               *p = 0;
        }
 
        /// \TODO: we should add more information for the full status string
+       crypto_idstring = Crypto_GetInfoResponseDataString();
        length = dpsnprintf(out_msg, out_size,
                                                "\377\377\377\377%s\x0A"
                                                "\\gamename\\%s\\modname\\%s\\gameversion\\%d\\sv_maxclients\\%d"
                                                "\\clients\\%d\\bots\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d"
                                                "%s%s"
                                                "%s%s"
+                                               "%s%s"
                                                "%s",
                                                fullstatus ? "statusResponse" : "infoResponse",
                                                gamename, com_modname, gameversion.integer, svs.maxclients,
-                                               nb_clients, nb_bots, sv.name, hostname.string, NET_PROTOCOL_VERSION,
+                                               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
@@ -2163,6 +2356,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;
@@ -2186,22 +2381,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(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 ((gamemode == GAME_NEXUIZ || gamemode == GAME_XONOTIC) && (teamplay.integer > 0))
                                {
                                        if(cl->frags == -666) // spectator
                                                strlcpy(teambuf, " 0", sizeof(teambuf));
@@ -2344,8 +2537,9 @@ qboolean hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char
 
        // validate the challenge
        for (i = 0;i < MAX_CHALLENGES;i++)
-               if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strncmp(challenge[i].string, s, sizeof(challenge[0].string) - 1))
-                       break;
+               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;
@@ -2357,13 +2551,7 @@ qboolean hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char
                return false;
 
        // unmark challenge to prevent replay attacks
-       // FIXME as there is currently no unmark facility, let's invalidate it
-       // as much as possible
-       challenge[i].string[0] = '\\'; // not allowed in infostrings, so connects cannot match
-       NetConn_BuildChallengeString(challenge[i].string + 1, sizeof(challenge[i].string) - 1);
        challenge[i].time = 0;
-       LHNETADDRESS_FromString(&challenge[i].address, "local:42", 42); // no rcon will come from there for sure
-       challenge[i].address = *peeraddress;
 
        return true;
 }
@@ -2376,15 +2564,51 @@ qboolean plaintext_matching(lhnetaddress_t *peeraddress, const char *password, c
 /// returns a string describing the user level, or NULL for auth failure
 const char *RCon_Authenticate(lhnetaddress_t *peeraddress, const char *password, const char *s, const char *endpos, rcon_matchfunc_t comparator, const char *cs, int cslen)
 {
-       const char *text;
+       const char *text, *userpass_start, *userpass_end, *userpass_startpass;
+       static char buf[MAX_INPUTLINE];
        qboolean hasquotes;
+       qboolean restricted = false;
+       qboolean have_usernames = false;
 
-       if(comparator(peeraddress, rcon_password.string, password, cs, cslen))
-               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(!comparator(peeraddress, rcon_restricted_password.string, password, cs, cslen))
-               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
@@ -2427,10 +2651,15 @@ match:
                s += l + 1;
        }
 
-       return "restricted rcon";
+allow:
+       userpass_startpass = strchr(userpass_start, ':');
+       if(have_usernames && userpass_startpass && userpass_startpass < userpass_end)
+               return va("%srcon (username %.*s)", restricted ? "restricted " : "", (int)(userpass_startpass-userpass_start), userpass_start);
+
+       return va("%srcon", restricted ? "restricted " : "");
 }
 
-void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const char *addressstring2, const char *userlevel, const char *s, const char *endpos)
+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)
        {
@@ -2447,7 +2676,7 @@ void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const ch
                Con_Printf("\n");
 
                if (!host_client || !host_client->netconnection || LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
-                       Con_Rcon_Redirect_Init(mysocket, peeraddress);
+                       Con_Rcon_Redirect_Init(mysocket, peeraddress, proquakeprotocol);
                while(s != endpos)
                {
                        size_t l = strlen(s);
@@ -2474,8 +2703,11 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
        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];
        qboolean islocal = (LHNETADDRESS_GetAddressType(peeraddress) == LHNETADDRESSTYPE_LOOP);
+       char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
+       size_t sendlength, response_len;
 
        if (!sv.active)
                return false;
@@ -2503,18 +2735,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, sendlength+4, peeraddress);
+                               }
+                               break;
+                       case CRYPTO_DISCARD:
+                               if(sendlength)
+                               {
+                                       memcpy(senddata, "\377\377\377\377", 4);
+                                       NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
+                               }
+                               return true;
+                               break;
+                       case CRYPTO_REPLACE:
+                               string = senddata+4;
+                               length = 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;
                        }
@@ -2528,28 +2788,63 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        }
                        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, 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, I am %.*s@%.*s\n",
+                                                       crypto->use_aes ? "Encrypted" : "Authenticated",
+                                                       addressstring2,
+                                                       crypto->client_idfp[0] ? crypto->client_idfp : "-",
+                                                       crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-",
+                                                       crypto_keyfp_recommended_length, crypto->server_idfp[0] ? crypto->server_idfp : "-",
+                                                       crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-"
+                                                 );
+                               }
+                       }
+                       else
+                       {
+                               if ((s = SearchInfostring(string, "challenge")))
+                               {
+                                       // 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 = SearchInfostring(string, "message")))
+                               Con_DPrintf("Connecting client %s sent us the message: %s\n", addressstring2, s);
+
+                       if(!(islocal || sv_public.integer > -2))
+                       {
+                               if (developer_extra.integer)
+                                       Con_Printf("Datagram_ParseConnectionless: sending \"reject %s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
+                               NetConn_WriteString(mysocket, va("\377\377\377\377reject %s", sv_public_rejectreason.string), peeraddress);
                                return true;
+                       }
 
                        // check engine protocol
                        if(!(s = SearchInfostring(string, "protocol")) || 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;
@@ -2562,13 +2857,48 @@ 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(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->spawned)
                                        {
                                                // 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);
+                                               if(crypto && crypto->authenticated)
+                                                       Crypto_ServerFinishInstance(&client->netconnection->crypto, crypto);
                                                SV_VM_Begin();
                                                SV_SendServerinfo(client);
                                                SV_VM_End();
@@ -2577,8 +2907,10 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                        {
                                                // 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_ServerFinishInstance(&client->netconnection->crypto, crypto);
                                                NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
                                        }
                                        return true;
@@ -2595,10 +2927,12 @@ 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
+                                       if(crypto && crypto->authenticated)
+                                               Crypto_ServerFinishInstance(&conn->crypto, crypto);
                                        SV_VM_Begin();
                                        SV_ConnectClient(clientnum, conn);
                                        SV_VM_End();
@@ -2608,7 +2942,7 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        }
 
                        // 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);
 
@@ -2624,8 +2958,8 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
 
                        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;
@@ -2640,8 +2974,8 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
 
                        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;
@@ -2662,7 +2996,7 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        ++s;
 
                        userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_time_matching, timeval, endpos - timeval - 1); // not including the appended \0 into the HMAC
-                       RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos);
+                       RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
                        return true;
                }
                if (length >= 42 && !memcmp(string, "srcon HMAC-MD4 CHALLENGE ", 25))
@@ -2677,7 +3011,7 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        ++s;
 
                        userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_challenge_matching, challenge, endpos - challenge - 1); // not including the appended \0 into the HMAC
-                       RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos);
+                       RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
                        return true;
                }
                if (length >= 5 && !memcmp(string, "rcon ", 5))
@@ -2687,7 +3021,7 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
                        char password[64];
 
-                       if(rcon_secure.integer)
+                       if(rcon_secure.integer > 0)
                                return true;
 
                        for (i = 0;!ISWHITESPACE(*s);s++)
@@ -2699,14 +3033,23 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        if (!ISWHITESPACE(password[0]))
                        {
                                const char *userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
-                               RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos);
+                               RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
                        }
                        return true;
                }
+               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;
                }
@@ -2721,7 +3064,7 @@ 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;
@@ -2735,23 +3078,35 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                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(&net_message);
+                               // save space for the header, filled in later
+                               MSG_WriteLong(&net_message, 0);
+                               MSG_WriteByte(&net_message, CCREP_REJECT);
+                               MSG_WriteString(&net_message, va("%s\n", sv_public_rejectreason.string));
+                               StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+                               NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
+                               SZ_Clear(&net_message);
                                break;
+                       }
 
                        protocolname = MSG_ReadString();
                        protocolnumber = MSG_ReadByte();
                        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);
+                               if (developer_extra.integer)
+                                       Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
                                SZ_Clear(&net_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));
+                               StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
                                NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
                                SZ_Clear(&net_message);
                                break;
@@ -2767,14 +3122,14 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                        // (if so, keep their stuff intact)
 
                                        // send a reply
-                                       if (developer.integer >= 10)
-                                               Con_Printf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
+                                       if (developer_extra.integer)
+                                               Con_DPrintf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
                                        SZ_Clear(&net_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));
+                                       StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
                                        NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
                                        SZ_Clear(&net_message);
 
@@ -2803,15 +3158,15 @@ 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);
                                        // 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));
+                                       StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
                                        NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
                                        SZ_Clear(&net_message);
                                        // now set up the client struct
@@ -2823,29 +3178,29 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                }
                        }
 
-                       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);
                        // 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));
+                       StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
                        NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
                        SZ_Clear(&net_message);
                        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 (sv.active && !strcmp(MSG_ReadString(), "QUAKE"))
                        {
                                int numclients;
                                char myaddressstring[128];
-                               if (developer.integer >= 10)
-                                       Con_Printf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
+                               if (developer_extra.integer)
+                                       Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
                                SZ_Clear(&net_message);
                                // save space for the header, filled in later
                                MSG_WriteLong(&net_message, 0);
@@ -2861,15 +3216,15 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                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));
+                               StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
                                NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
                                SZ_Clear(&net_message);
                        }
                        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 (sv.active)
                        {
@@ -2892,17 +3247,20 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                        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));
+                                       if(sv_status_privacy.integer)
+                                               MSG_WriteString(&net_message, client->netconnection ? "hidden" : "botclient");
+                                       else
+                                               MSG_WriteString(&net_message, client->netconnection ? client->netconnection->address : "botclient");
+                                       StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
                                        NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
                                        SZ_Clear(&net_message);
                                }
                        }
                        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;
                        if (sv.active)
                        {
@@ -2923,11 +3281,30 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                        MSG_WriteString(&net_message, var->name);
                                        MSG_WriteString(&net_message, var->string);
                                }
-                               *((int *)net_message.data) = BigLong(NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
+                               StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
                                NetConn_Write(mysocket, net_message.data, net_message.cursize, peeraddress);
                                SZ_Clear(&net_message);
                        }
                        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(), sizeof(password));
+                               strlcpy(cmd, MSG_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;
                }
@@ -3008,7 +3385,7 @@ void NetConn_QueryMasters(qboolean querydp, qboolean queryqw)
                                        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));
+                                       StoreBigLong(net_message.data, NETFLAG_CTL | (net_message.cursize & NETFLAG_LENGTH_MASK));
                                        NetConn_Write(cl_sockets[i], net_message.data, net_message.cursize, &broadcastaddress);
                                        SZ_Clear(&net_message);
 
@@ -3242,6 +3619,7 @@ void NetConn_Init(void)
        Cvar_RegisterVariable(&net_address);
        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]);