]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - netconn.c
Fix engine not starting on Windows if linked against SDL > 2.0.5
[xonotic/darkplaces.git] / netconn.c
index eb99dbd6e9517dbd5fcf2cbb409b9e9335956aaa..342d78b253cdb623d36ad5c1c9fee34b025bf970 100755 (executable)
--- a/netconn.c
+++ b/netconn.c
@@ -44,15 +44,13 @@ static cvar_t sv_masters [] =
        {CVAR_SAVE, "sv_master2", "", "user-chosen master server 2"},
        {CVAR_SAVE, "sv_master3", "", "user-chosen master server 3"},
        {CVAR_SAVE, "sv_master4", "", "user-chosen master server 4"},
-       {0, "sv_masterextra1", "69.59.212.88", "ghdigital.com - default master server 1 (admin: LordHavoc)"}, // admin: LordHavoc
-       {0, "sv_masterextra2", "64.22.107.125", "dpmaster.deathmask.net - default master server 2 (admin: Willis)"}, // admin: Willis
-       {0, "sv_masterextra3", "92.62.40.73", "dpmaster.tchr.no - default master server 3 (admin: tChr)"}, // admin: tChr
-#ifdef SUPPORTIPV6
-       {0, "sv_masterextra4", "[2a03:4000:2:225::51:334d]:27950", "dpmaster.sudo.rm-f.org - default master server 4 (admin: divVerent)"}, // admin: divVerent
-#endif
+       {0, "sv_masterextra1", "ghdigital.com", "ghdigital.com - default master server 1 (admin: LordHavoc)"}, // admin: LordHavoc
+       {0, "sv_masterextra2", "dpmaster.deathmask.net", "dpmaster.deathmask.net - default master server 2 (admin: Willis)"}, // admin: Willis
+       {0, "sv_masterextra3", "dpmaster.tchr.no", "dpmaster.tchr.no - default master server 3 (admin: tChr)"}, // admin: tChr
        {0, NULL, NULL, NULL}
 };
 
+#ifdef CONFIG_MENU
 static cvar_t sv_qwmasters [] =
 {
        {CVAR_SAVE, "sv_qwmaster1", "", "user-chosen qwmaster server 1"},
@@ -62,10 +60,10 @@ static cvar_t sv_qwmasters [] =
        {0, "sv_qwmasterextra1", "master.quakeservers.net:27000", "Global master server. (admin: unknown)"},
        {0, "sv_qwmasterextra2", "asgaard.morphos-team.net:27000", "Global master server. (admin: unknown)"},
        {0, "sv_qwmasterextra3", "qwmaster.ocrana.de:27000", "German master server. (admin: unknown)"},
-       {0, "sv_qwmasterextra4", "masterserver.exhale.de:27000", "German master server. (admin: unknown)"},
-       {0, "sv_qwmasterextra5", "qwmaster.fodquake.net:27000", "Global master server. (admin: unknown)"},
+       {0, "sv_qwmasterextra4", "qwmaster.fodquake.net:27000", "Global master server. (admin: unknown)"},
        {0, NULL, NULL, NULL}
 };
+#endif
 
 static double nextheartbeattime = 0;
 
@@ -84,6 +82,7 @@ cvar_t net_connecttimeout = {0, "net_connecttimeout","15", "after requesting a c
 cvar_t net_connectfloodblockingtimeout = {0, "net_connectfloodblockingtimeout", "5", "when a connection packet is received, it will block all future connect packets from that IP address for this many seconds (cuts down on connect floods). Note that this does not include retries from the same IP; these are handled earlier and let in."};
 cvar_t net_challengefloodblockingtimeout = {0, "net_challengefloodblockingtimeout", "0.5", "when a challenge packet is received, it will block all future challenge packets from that IP address for this many seconds (cuts down on challenge floods). DarkPlaces clients retry once per second, so this should be <= 1. Failure here may lead to connect attempts failing."};
 cvar_t net_getstatusfloodblockingtimeout = {0, "net_getstatusfloodblockingtimeout", "1", "when a getstatus packet is received, it will block all future getstatus packets from that IP address for this many seconds (cuts down on getstatus floods). DarkPlaces retries every 4 seconds, and qstat retries once per second, so this should be <= 1. Failure here may lead to server not showing up in the server list."};
+cvar_t net_sourceaddresscheck = {0, "net_sourceaddresscheck", "1", "compare the source IP address for replies (more secure, may break some bad multihoming setups"};
 cvar_t hostname = {CVAR_SAVE, "hostname", "UNNAMED", "server message to show in server browser"};
 cvar_t developer_networking = {0, "developer_networking", "0", "prints all received and sent packets (recommended only for debugging)"};
 
@@ -112,8 +111,9 @@ int masterreplycount = 0;
 int serverquerycount = 0;
 int serverreplycount = 0;
 
-challenge_t challenge[MAX_CHALLENGES];
+challenge_t challenges[MAX_CHALLENGES];
 
+#ifdef CONFIG_MENU
 /// this is only false if there are still servers left to query
 static qboolean serverlist_querysleep = true;
 static qboolean serverlist_paused = false;
@@ -121,6 +121,7 @@ static qboolean serverlist_paused = false;
 /// reply is received, to avoid issuing queries while master replies are still
 /// flooding in (which would make a mess of the ping times)
 static double serverlist_querywaittime = 0;
+#endif
 
 static int cl_numsockets;
 static lhnetsocket_t *cl_sockets[16];
@@ -144,6 +145,7 @@ char sv_net_extresponse[NET_EXTRESPONSE_MAX][1400];
 int sv_net_extresponse_count = 0;
 int sv_net_extresponse_last = 0;
 
+#ifdef CONFIG_MENU
 // ServerList interface
 serverlist_mask_t serverlist_andmasks[SERVERLIST_ANDMASKCOUNT];
 serverlist_mask_t serverlist_ormasks[SERVERLIST_ORMASKCOUNT];
@@ -218,12 +220,19 @@ static qboolean _ServerList_Entry_Compare( serverlist_entry_t *A, serverlist_ent
 {
        int result = 0; // > 0 if for numbers A > B and for text if A < B
 
-       if( serverlist_sortflags & SLSF_FAVORITESFIRST )
+       if( serverlist_sortflags & SLSF_CATEGORIES )
+       {
+               result = A->info.category - B->info.category;
+               if (result != 0)
+                       return result < 0;
+       }
+
+       if( serverlist_sortflags & SLSF_FAVORITES )
        {
                if(A->info.isfavorite != B->info.isfavorite)
                        return A->info.isfavorite;
        }
-
+       
        switch( serverlist_sortbyfield ) {
                case SLIF_PING:
                        result = A->info.ping - B->info.ping;
@@ -264,6 +273,9 @@ static qboolean _ServerList_Entry_Compare( serverlist_entry_t *A, serverlist_ent
                case SLIF_QCSTATUS:
                        result = strcasecmp( B->info.qcstatus, A->info.qcstatus ); // not really THAT useful, though
                        break;
+               case SLIF_CATEGORY:
+                       result = A->info.category - B->info.category;
+                       break;
                case SLIF_ISFAVORITE:
                        result = !!B->info.isfavorite - !!A->info.isfavorite;
                        break;
@@ -391,6 +403,8 @@ static qboolean _ServerList_Entry_Mask( serverlist_mask_t *mask, serverlist_info
        if( *mask->info.players
                && !_ServerList_CompareStr( info->players, mask->tests[SLIF_PLAYERS], mask->info.players ) )
                return false;
+       if( !_ServerList_CompareInt( info->category, mask->tests[SLIF_CATEGORY], mask->info.category ) )
+               return false;
        if( !_ServerList_CompareInt( info->isfavorite, mask->tests[SLIF_ISFAVORITE], mask->info.isfavorite ))
                return false;
        return true;
@@ -427,7 +441,7 @@ static void ServerList_ViewList_Insert( serverlist_entry_t *entry )
                                break;
                        }
                }
-               if(Crypto_RetrieveHostKey(&addr, 0, NULL, 0, idfp, sizeof(idfp), NULL))
+               if(Crypto_RetrieveHostKey(&addr, 0, NULL, 0, idfp, sizeof(idfp), NULL, NULL))
                {
                        for(i = 0; i < nFavorites_idfp; ++i)
                        {
@@ -440,6 +454,9 @@ static void ServerList_ViewList_Insert( serverlist_entry_t *entry )
                }
        }
 
+       // refresh the "category"
+       entry->info.category = MR_GetServerListEntryCategory(entry);
+
        // FIXME: change this to be more readable (...)
        // now check whether it passes through the masks
        for( start = 0 ; start < SERVERLIST_ANDMASKCOUNT && serverlist_andmasks[start].active; start++ )
@@ -587,6 +604,7 @@ void ServerList_QueryList(qboolean resetcache, qboolean querydp, qboolean queryq
 
        NetConn_QueryMasters(querydp, queryqw);
 }
+#endif
 
 // rest
 
@@ -668,7 +686,7 @@ qboolean NetConn_CanSend(netconn_t *conn)
        }
 }
 
-void NetConn_UpdateCleartime(double *cleartime, int rate, int burstsize, int len)
+static void NetConn_UpdateCleartime(double *cleartime, int rate, int burstsize, int len)
 {
        double bursttime = burstsize / (double)rate;
 
@@ -685,6 +703,27 @@ void NetConn_UpdateCleartime(double *cleartime, int rate, int burstsize, int len
        }
 }
 
+static int NetConn_AddCryptoFlag(crypto_t *crypto)
+{
+       // HACK: if an encrypted connection is used, randomly set some unused
+       // flags. When AES encryption is enabled, that will make resends differ
+       // from the original, so that e.g. substring filters in a router/IPS
+       // are unlikely to match a second time. See also "startkeylogger".
+       int flag = 0;
+       if (crypto->authenticated)
+       {
+               // Let's always set at least one of the bits.
+               int r = rand() % 7 + 1;
+               if (r & 1)
+                       flag |= NETFLAG_CRYPTO0;
+               if (r & 2)
+                       flag |= NETFLAG_CRYPTO1;
+               if (r & 4)
+                       flag |= NETFLAG_CRYPTO2;
+       }
+       return flag;
+}
+
 int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolversion_t protocol, int rate, int burstsize, qboolean quakesignon_suppressreliables)
 {
        int totallen = 0;
@@ -721,9 +760,9 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers
                        sendreliable = true;
                }
                // outgoing unreliable packet number, and outgoing reliable packet number (0 or 1)
-               StoreLittleLong(sendbuffer, (unsigned int)conn->outgoing_unreliable_sequence | ((unsigned int)sendreliable<<31));
+               StoreLittleLong(sendbuffer, conn->outgoing_unreliable_sequence | (((unsigned int)sendreliable)<<31));
                // last received unreliable packet number, and last received reliable packet number (0 or 1)
-               StoreLittleLong(sendbuffer + 4, (unsigned int)conn->qw.incoming_sequence | ((unsigned int)conn->qw.incoming_reliable_sequence<<31));
+               StoreLittleLong(sendbuffer + 4, conn->qw.incoming_sequence | (((unsigned int)conn->qw.incoming_reliable_sequence)<<31));
                packetLen = 8;
                conn->outgoing_unreliable_sequence++;
                // client sends qport in every packet
@@ -790,20 +829,20 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers
 
                        packetLen = NET_HEADERSIZE + dataLen;
 
-                       StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom));
+                       StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
                        StoreBigLong(sendbuffer + 4, conn->nq.sendSequence - 1);
                        memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
 
                        conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
 
                        sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
-                       if (sendme && NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress) == (int)sendmelen)
+                       if (sendme && NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress) == (int)sendmelen)
                        {
                                conn->lastSendTime = realtime;
                                conn->packetsReSent++;
                        }
 
-                       totallen += sendmelen + 28;
+                       totallen += (int)sendmelen + 28;
                }
 
                // if we have a new reliable message to send, do so
@@ -839,7 +878,7 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers
 
                        packetLen = NET_HEADERSIZE + dataLen;
 
-                       StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom));
+                       StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
                        StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
                        memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
 
@@ -849,13 +888,13 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers
 
                        sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
                        if(sendme)
-                               NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress);
+                               NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
 
                        conn->lastSendTime = realtime;
                        conn->packetsSent++;
                        conn->reliableMessagesSent++;
 
-                       totallen += sendmelen + 28;
+                       totallen += (int)sendmelen + 28;
                }
 
                // if we have an unreliable message to send, do so
@@ -869,7 +908,7 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers
                                return -1;
                        }
 
-                       StoreBigLong(sendbuffer, packetLen | NETFLAG_UNRELIABLE);
+                       StoreBigLong(sendbuffer, packetLen | NETFLAG_UNRELIABLE | NetConn_AddCryptoFlag(&conn->crypto));
                        StoreBigLong(sendbuffer + 4, conn->outgoing_unreliable_sequence);
                        memcpy(sendbuffer + NET_HEADERSIZE, data->data, data->cursize);
 
@@ -879,16 +918,16 @@ int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolvers
 
                        sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
                        if(sendme)
-                               NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress);
+                               NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
 
                        conn->packetsSent++;
                        conn->unreliableMessagesSent++;
 
-                       totallen += sendmelen + 28;
+                       totallen += (int)sendmelen + 28;
                }
        }
 
-       NetConn_UpdateCleartime(&conn->cleartime, cl_rate.integer, cl_rate_burstsize.integer, totallen);
+       NetConn_UpdateCleartime(&conn->cleartime, rate, burstsize, totallen);
 
        return 0;
 }
@@ -957,7 +996,7 @@ void NetConn_OpenClientPorts(void)
                Con_Printf("Client using port %i\n", port);
        NetConn_OpenClientPort(NULL, LHNETADDRESSTYPE_LOOP, 2);
        NetConn_OpenClientPort(net_address.string, LHNETADDRESSTYPE_INET4, port);
-#ifdef SUPPORTIPV6
+#ifndef NOSUPPORTIPV6
        NetConn_OpenClientPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port);
 #endif
 }
@@ -1029,7 +1068,7 @@ void NetConn_OpenServerPorts(int opennetports)
                NetConn_OpenServerPort(NULL, LHNETADDRESSTYPE_LOOP, 1, 1);
        if (opennetports)
        {
-#ifdef SUPPORTIPV6
+#ifndef NOSUPPORTIPV6
                qboolean ip4success = NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
                NetConn_OpenServerPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port, ip4success ? 1 : 100);
 #else
@@ -1160,7 +1199,7 @@ void NetConn_UpdateSockets(void)
 
 static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, size_t length, protocolversion_t protocol, double newtimeout)
 {
-       int originallength = length;
+       int originallength = (int)length;
        unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
        unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
        unsigned char cryptoreadbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
@@ -1169,8 +1208,8 @@ static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, s
 
        if (protocol == PROTOCOL_QUAKEWORLD)
        {
-               int sequence, sequence_ack;
-               int reliable_ack, reliable_message;
+               unsigned int sequence, sequence_ack;
+               qboolean reliable_ack, reliable_message;
                int count;
                //int qport;
 
@@ -1191,8 +1230,8 @@ static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, s
                }
 
                conn->packetsReceived++;
-               reliable_message = (sequence >> 31) & 1;
-               reliable_ack = (sequence_ack >> 31) & 1;
+               reliable_message = (sequence >> 31) != 0;
+               reliable_ack = (sequence_ack >> 31) != 0;
                sequence &= ~(1<<31);
                sequence_ack &= ~(1<<31);
                if (sequence <= conn->qw.incoming_sequence)
@@ -1205,6 +1244,12 @@ static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, s
                {
                        conn->droppedDatagrams += count;
                        //Con_DPrintf("Dropped %u datagram(s)\n", count);
+                       // If too may packets have been dropped, only write the
+                       // last NETGRAPH_PACKETS ones to the netgraph. Why?
+                       // Because there's no point in writing more than
+                       // these as the netgraph is going to be full anyway.
+                       if (count > NETGRAPH_PACKETS)
+                               count = NETGRAPH_PACKETS;
                        while (count--)
                        {
                                conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
@@ -1249,13 +1294,13 @@ static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, s
                if (conn == cls.netcon)
                {
                        SZ_Clear(&cl_message);
-                       SZ_Write(&cl_message, data, length);
+                       SZ_Write(&cl_message, data, (int)length);
                        MSG_BeginReading(&cl_message);
                }
                else
                {
                        SZ_Clear(&sv_message);
-                       SZ_Write(&sv_message, data, length);
+                       SZ_Write(&sv_message, data, (int)length);
                        MSG_BeginReading(&sv_message);
                }
                return 2;
@@ -1269,7 +1314,7 @@ static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, s
                const void *sendme;
                size_t sendmelen;
 
-               originallength = length;
+               originallength = (int)length;
                data = (const unsigned char *) Crypto_DecryptPacket(&conn->crypto, data, length, cryptoreadbuffer, &length, sizeof(cryptoreadbuffer));
                if(!data)
                        return 0;
@@ -1295,6 +1340,12 @@ static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, s
                                                count = sequence - conn->nq.unreliableReceiveSequence;
                                                conn->droppedDatagrams += count;
                                                //Con_DPrintf("Dropped %u datagram(s)\n", count);
+                                               // If too may packets have been dropped, only write the
+                                               // last NETGRAPH_PACKETS ones to the netgraph. Why?
+                                               // Because there's no point in writing more than
+                                               // these as the netgraph is going to be full anyway.
+                                               if (count > NETGRAPH_PACKETS)
+                                                       count = NETGRAPH_PACKETS;
                                                while (count--)
                                                {
                                                        conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
@@ -1322,13 +1373,13 @@ static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, s
                                                if (conn == cls.netcon)
                                                {
                                                        SZ_Clear(&cl_message);
-                                                       SZ_Write(&cl_message, data, length);
+                                                       SZ_Write(&cl_message, data, (int)length);
                                                        MSG_BeginReading(&cl_message);
                                                }
                                                else
                                                {
                                                        SZ_Clear(&sv_message);
-                                                       SZ_Write(&sv_message, data, length);
+                                                       SZ_Write(&sv_message, data, (int)length);
                                                        MSG_BeginReading(&sv_message);
                                                }
                                                return 2;
@@ -1374,14 +1425,14 @@ static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, s
 
                                                        packetLen = NET_HEADERSIZE + dataLen;
 
-                                                       StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom));
+                                                       StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
                                                        StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
                                                        memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
 
                                                        conn->nq.sendSequence++;
 
                                                        sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
-                                                       if (sendme && NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress) == (int)sendmelen)
+                                                       if (sendme && NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress) == (int)sendmelen)
                                                        {
                                                                conn->lastSendTime = realtime;
                                                                conn->packetsSent++;
@@ -1405,11 +1456,11 @@ static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, s
 
                                conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes        += 8 + 28;
 
-                               StoreBigLong(temppacket, 8 | NETFLAG_ACK);
+                               StoreBigLong(temppacket, 8 | NETFLAG_ACK | NetConn_AddCryptoFlag(&conn->crypto));
                                StoreBigLong(temppacket + 4, sequence);
                                sendme = Crypto_EncryptPacket(&conn->crypto, temppacket, 8, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
                                if(sendme)
-                                       NetConn_Write(conn->mysocket, sendme, sendmelen, &conn->peeraddress);
+                                       NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
                                if (sequence == conn->nq.receiveSequence)
                                {
                                        conn->lastMessageTime = realtime;
@@ -1417,7 +1468,7 @@ static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, s
                                        conn->nq.receiveSequence++;
                                        if( conn->receiveMessageLength + length <= (int)sizeof( conn->receiveMessage ) ) {
                                                memcpy(conn->receiveMessage + conn->receiveMessageLength, data, length);
-                                               conn->receiveMessageLength += length;
+                                               conn->receiveMessageLength += (int)length;
                                        } else {
                                                Con_Printf( "Reliable message (seq: %i) too big for message buffer!\n"
                                                                        "Dropping the message!\n", sequence );
@@ -1434,13 +1485,13 @@ static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, s
                                                        if (conn == cls.netcon)
                                                        {
                                                                SZ_Clear(&cl_message);
-                                                               SZ_Write(&cl_message, conn->receiveMessage, length);
+                                                               SZ_Write(&cl_message, conn->receiveMessage, (int)length);
                                                                MSG_BeginReading(&cl_message);
                                                        }
                                                        else
                                                        {
                                                                SZ_Clear(&sv_message);
-                                                               SZ_Write(&sv_message, conn->receiveMessage, length);
+                                                               SZ_Write(&sv_message, conn->receiveMessage, (int)length);
                                                                MSG_BeginReading(&sv_message);
                                                        }
                                                        return 2;
@@ -1474,16 +1525,18 @@ static void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_
        }
        // allocate a net connection to keep track of things
        cls.netcon = NetConn_Open(mysocket, peeraddress);
-       crypto = &cls.crypto;
-       if(crypto && crypto->authenticated)
+       crypto = &cls.netcon->crypto;
+       if(cls.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_FinishInstance(crypto, &cls.crypto);
+               Con_Printf("%s connection to %s has been established: server is %s@%s%.*s, I am %.*s@%s%.*s\n",
                                crypto->use_aes ? "Encrypted" : "Authenticated",
                                cls.netcon->address,
                                crypto->server_idfp[0] ? crypto->server_idfp : "-",
+                               (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
                                crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-",
                                crypto_keyfp_recommended_length, crypto->client_idfp[0] ? crypto->client_idfp : "-",
+                               (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
                                crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-"
                                );
        }
@@ -1521,6 +1574,7 @@ int NetConn_IsLocalGame(void)
        return false;
 }
 
+#ifdef CONFIG_MENU
 static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *addressstring)
 {
        int n;
@@ -1729,18 +1783,21 @@ static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *sen
        serverlist_querysleep = false;
        serverlist_querywaittime = realtime + 3;
 }
+#endif
 
 static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
 {
        qboolean fromserver;
        int ret, c;
-       const char *s;
-       char *string, addressstring2[128], ipstring[32];
+       char *string, addressstring2[128];
        char stringbuf[16384];
        char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
        size_t sendlength;
+#ifdef CONFIG_MENU
        char infostringvalue[MAX_INPUTLINE];
-       char vabuf[1024];
+       char ipstring[32];
+       const char *s;
+#endif
 
        // quakeworld ingame packet
        fromserver = cls.netcon && mysocket == cls.netcon->mysocket && !LHNETADDRESS_Compare(&cls.netcon->peeraddress, peeraddress);
@@ -1775,20 +1832,20 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                if(sendlength)
                                {
                                        memcpy(senddata, "\377\377\377\377", 4);
-                                       NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
+                                       NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
                                }
                                break;
                        case CRYPTO_DISCARD:
                                if(sendlength)
                                {
                                        memcpy(senddata, "\377\377\377\377", 4);
-                                       NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
+                                       NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
                                }
                                return true;
                                break;
                        case CRYPTO_REPLACE:
                                string = senddata+4;
-                               length = sendlength;
+                               length = (int)sendlength;
                                break;
                }
 
@@ -1815,12 +1872,12 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                e = strchr(rcon_password.string, ' ');
                                n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
 
-                               if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, strlen(argbuf), (unsigned char *) rcon_password.string, n))
+                               if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, (int)strlen(argbuf), (unsigned char *) rcon_password.string, n))
                                {
                                        int k;
                                        buf[45] = ' ';
                                        strlcpy(buf + 46, argbuf, sizeof(buf) - 46);
-                                       NetConn_Write(mysocket, buf, 46 + strlen(buf + 46), peeraddress);
+                                       NetConn_Write(mysocket, buf, 46 + (int)strlen(buf + 46), peeraddress);
                                        cls.rcon_commands[i][0] = 0;
                                        --cls.rcon_trying;
 
@@ -1847,20 +1904,30 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                {
                        // darkplaces or quake3
                        char protocolnames[1400];
-                       Protocol_Names(protocolnames, sizeof(protocolnames));
                        Con_DPrintf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
+                       if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
+                               Con_DPrintf("challenge message from wrong server %s\n", addressstring2);
+                               return true;
+                       }
+                       Protocol_Names(protocolnames, sizeof(protocolnames));
 #ifdef CONFIG_MENU
                        M_Update_Return_Reason("Got challenge response");
 #endif
                        // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
                        InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
                        // TODO: add userinfo stuff here instead of using NQ commands?
-                       NetConn_WriteString(mysocket, va(vabuf, sizeof(vabuf), "\377\377\377\377connect\\protocol\\darkplaces 3\\protocols\\%s%s\\challenge\\%s", protocolnames, cls.connect_userinfo, string + 10), peeraddress);
+                       memcpy(senddata, "\377\377\377\377", 4);
+                       dpsnprintf(senddata+4, sizeof(senddata)-4, "connect\\protocol\\darkplaces 3\\protocols\\%s%s\\challenge\\%s", protocolnames, cls.connect_userinfo, string + 10);
+                       NetConn_WriteString(mysocket, senddata, peeraddress);
                        return true;
                }
                if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying)
                {
                        // darkplaces or quake3
+                       if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
+                               Con_DPrintf("accept message from wrong server %s\n", addressstring2);
+                               return true;
+                       }
 #ifdef CONFIG_MENU
                        M_Update_Return_Reason("Accepted");
 #endif
@@ -1870,6 +1937,10 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                if (length > 7 && !memcmp(string, "reject ", 7) && cls.connect_trying)
                {
                        char rejectreason[128];
+                       if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
+                               Con_DPrintf("reject message from wrong server %s\n", addressstring2);
+                               return true;
+                       }
                        cls.connect_trying = false;
                        string += 7;
                        length = min(length - 7, (int)sizeof(rejectreason) - 1);
@@ -1880,6 +1951,7 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
 #endif
                        return true;
                }
+#ifdef CONFIG_MENU
                if (length >= 15 && !memcmp(string, "statusResponse\x0A", 15))
                {
                        serverlist_info_t *info;
@@ -2016,6 +2088,7 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        serverlist_querywaittime = realtime + 3;
                        return true;
                }
+#endif
                if (!strncmp(string, "extResponse ", 12))
                {
                        ++cl_net_extresponse_count;
@@ -2038,6 +2111,10 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                if (length > 1 && string[0] == 'c' && (string[1] == '-' || (string[1] >= '0' && string[1] <= '9')) && cls.connect_trying)
                {
                        // challenge message
+                       if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
+                               Con_DPrintf("c message from wrong server %s\n", addressstring2);
+                               return true;
+                       }
                        Con_Printf("challenge %s received, sending QuakeWorld connect request back to %s\n", string + 1, addressstring2);
 #ifdef CONFIG_MENU
                        M_Update_Return_Reason("Got QuakeWorld challenge response");
@@ -2045,12 +2122,18 @@ 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(vabuf, sizeof(vabuf), "\377\377\377\377connect %i %i %i \"%s%s\"\n", 28, cls.qw_qport, atoi(string + 1), cls.userinfo, cls.connect_userinfo), peeraddress);
+                       memcpy(senddata, "\377\377\377\377", 4);
+                       dpsnprintf(senddata+4, sizeof(senddata)-4, "connect %i %i %i \"%s%s\"\n", 28, cls.qw_qport, atoi(string + 1), cls.userinfo, cls.connect_userinfo);
+                       NetConn_WriteString(mysocket, senddata, peeraddress);
                        return true;
                }
                if (length >= 1 && string[0] == 'j' && cls.connect_trying)
                {
                        // accept message
+                       if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
+                               Con_DPrintf("j message from wrong server %s\n", addressstring2);
+                               return true;
+                       }
 #ifdef CONFIG_MENU
                        M_Update_Return_Reason("QuakeWorld Accepted");
 #endif
@@ -2059,6 +2142,7 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                }
                if (length > 2 && !memcmp(string, "n\\", 2))
                {
+#ifdef CONFIG_MENU
                        serverlist_info_t *info;
                        int n;
 
@@ -2102,12 +2186,16 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        }
 
                        NetConn_ClientParsePacket_ServerList_UpdateCache(n);
-
+#endif
                        return true;
                }
                if (string[0] == 'n')
                {
-                       // qw print command
+                       // qw print command, used by rcon replies too
+                       if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address) && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) {
+                               Con_DPrintf("n message from wrong server %s\n", addressstring2);
+                               return true;
+                       }
                        Con_Printf("QW print command from server at %s:\n%s\n", addressstring2, string + 1);
                }
                // we may not have liked the packet, but it was a command packet, so
@@ -2124,8 +2212,10 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
        // netquake control packets, supported for compatibility only
        if (length >= 5 && BuffBigLong(data) == ((int)NETFLAG_CTL | length) && !ENCRYPTION_REQUIRED)
        {
+#ifdef CONFIG_MENU
                int n;
                serverlist_info_t *info;
+#endif
 
                data += 4;
                length -= 4;
@@ -2141,6 +2231,10 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        if (cls.connect_trying)
                        {
                                lhnetaddress_t clientportaddress;
+                               if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
+                                       Con_DPrintf("CCREP_ACCEPT message from wrong server %s\n", addressstring2);
+                                       break;
+                               }
                                clientportaddress = *peeraddress;
                                LHNETADDRESS_SetPort(&clientportaddress, MSG_ReadLong(&cl_message));
                                // extra ProQuake stuff
@@ -2167,8 +2261,12 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        }
                        break;
                case CCREP_REJECT:
-                       if (developer_extra.integer)
-                               Con_DPrintf("Datagram_ParseConnectionless: received CCREP_REJECT from %s.\n", addressstring2);
+                       if (developer_extra.integer) {
+                               Con_DPrintf("CCREP_REJECT message from wrong server %s\n", addressstring2);
+                               break;
+                       }
+                       if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address))
+                               break;
                        cls.connect_trying = false;
 #ifdef CONFIG_MENU
                        M_Update_Return_Reason((char *)MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
@@ -2177,6 +2275,7 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                case CCREP_SERVER_INFO:
                        if (developer_extra.integer)
                                Con_DPrintf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
+#ifdef CONFIG_MENU
                        // LordHavoc: because the quake server may report weird addresses
                        // we just ignore it and keep the real address
                        MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
@@ -2195,9 +2294,13 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        info->protocol = MSG_ReadByte(&cl_message);
 
                        NetConn_ClientParsePacket_ServerList_UpdateCache(n);
-
+#endif
                        break;
                case CCREP_RCON: // RocketGuy: ProQuake rcon support
+                       if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) {
+                               Con_DPrintf("CCREP_RCON message from wrong server %s\n", addressstring2);
+                               break;
+                       }
                        if (developer_extra.integer)
                                Con_DPrintf("Datagram_ParseConnectionless: received CCREP_RCON from %s.\n", addressstring2);
 
@@ -2227,6 +2330,7 @@ static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
        return ret;
 }
 
+#ifdef CONFIG_MENU
 void NetConn_QueryQueueFrame(void)
 {
        int index;
@@ -2315,6 +2419,7 @@ void NetConn_QueryQueueFrame(void)
                }
        }
 }
+#endif
 
 void NetConn_ClientFrame(void)
 {
@@ -2369,7 +2474,9 @@ void NetConn_ClientFrame(void)
 //                     R_TimeReport("clientparsepacket");
                }
        }
+#ifdef CONFIG_MENU
        NetConn_QueryQueueFrame();
+#endif
        if (cls.netcon && realtime > cls.netcon->timeout && !sv.active)
        {
                Con_Print("Connection timed out\n");
@@ -2404,7 +2511,7 @@ static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg
        int length;
        char teambuf[3];
        const char *crypto_idstring;
-       const char *str;
+       const char *worldstatusstr;
 
        // How many clients are there?
        for (i = 0;i < (unsigned int)svs.maxclients;i++)
@@ -2418,13 +2525,13 @@ static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg
        }
 
        *qcstatus = 0;
-       str = PRVM_GetString(prog, PRVM_serverglobalstring(worldstatus));
-       if(str && *str)
+       worldstatusstr = PRVM_GetString(prog, PRVM_serverglobalstring(worldstatus));
+       if(worldstatusstr && *worldstatusstr)
        {
                char *p;
                const char *q;
                p = qcstatus;
-               for(q = str; *q && (size_t)(p - qcstatus) < (sizeof(qcstatus) - 1); ++q)
+               for(q = worldstatusstr; *q && (size_t)(p - qcstatus) < (sizeof(qcstatus) - 1); ++q)
                        if(*q != '\\' && *q != '\n')
                                *p++ = *q;
                *p = 0;
@@ -2441,7 +2548,7 @@ static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg
                                                "%s%s"
                                                "%s",
                                                fullstatus ? "statusResponse" : "infoResponse",
-                                               gamename, com_modname, gameversion.integer, svs.maxclients,
+                                               gamenetworkfiltername, com_modname, gameversion.integer, svs.maxclients,
                                                nb_clients, nb_bots, sv.worldbasename, hostname.string, NET_PROTOCOL_VERSION,
                                                *qcstatus ? "\\qcstatus\\" : "", qcstatus,
                                                challenge ? "\\challenge\\" : "", challenge ? challenge : "",
@@ -2465,13 +2572,13 @@ static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg
 
                for (i = 0;i < (unsigned int)svs.maxclients;i++)
                {
-                       client_t *cl = &svs.clients[i];
-                       if (cl->active)
+                       client_t *client = &svs.clients[i];
+                       if (client->active)
                        {
                                int nameind, cleanind, pingvalue;
                                char curchar;
-                               char cleanname [sizeof(cl->name)];
-                               const char *str;
+                               char cleanname [sizeof(client->name)];
+                               const char *statusstr;
                                prvm_edict_t *ed;
 
                                // Remove all characters '"' and '\' in the player name
@@ -2479,7 +2586,7 @@ static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg
                                cleanind = 0;
                                do
                                {
-                                       curchar = cl->name[nameind++];
+                                       curchar = client->name[nameind++];
                                        if (curchar != '"' && curchar != '\\')
                                        {
                                                cleanname[cleanind++] = curchar;
@@ -2489,21 +2596,21 @@ static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg
                                } while (curchar != '\0');
                                cleanname[cleanind] = 0; // cleanind is always a valid index even at this point
 
-                               pingvalue = (int)(cl->ping * 1000.0f);
-                               if(cl->netconnection)
+                               pingvalue = (int)(client->ping * 1000.0f);
+                               if(client->netconnection)
                                        pingvalue = bound(1, pingvalue, 9999);
                                else
                                        pingvalue = 0;
 
                                *qcstatus = 0;
                                ed = PRVM_EDICT_NUM(i + 1);
-                               str = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
-                               if(str && *str)
+                               statusstr = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
+                               if(statusstr && *statusstr)
                                {
                                        char *p;
                                        const char *q;
                                        p = qcstatus;
-                                       for(q = str; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
+                                       for(q = statusstr; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
                                                if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
                                                        *p++ = *q;
                                        *p = 0;
@@ -2511,15 +2618,15 @@ static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg
 
                                if (IS_NEXUIZ_DERIVED(gamemode) && (teamplay.integer > 0))
                                {
-                                       if(cl->frags == -666) // spectator
+                                       if(client->frags == -666) // spectator
                                                strlcpy(teambuf, " 0", sizeof(teambuf));
-                                       else if(cl->colors == 0x44) // red team
+                                       else if(client->colors == 0x44) // red team
                                                strlcpy(teambuf, " 1", sizeof(teambuf));
-                                       else if(cl->colors == 0xDD) // blue team
+                                       else if(client->colors == 0xDD) // blue team
                                                strlcpy(teambuf, " 2", sizeof(teambuf));
-                                       else if(cl->colors == 0xCC) // yellow team
+                                       else if(client->colors == 0xCC) // yellow team
                                                strlcpy(teambuf, " 3", sizeof(teambuf));
-                                       else if(cl->colors == 0x99) // pink team
+                                       else if(client->colors == 0x99) // pink team
                                                strlcpy(teambuf, " 4", sizeof(teambuf));
                                        else
                                                strlcpy(teambuf, " 0", sizeof(teambuf));
@@ -2536,7 +2643,7 @@ static qboolean NetConn_BuildStatusResponse(const char* challenge, char* out_msg
                                                                                cleanname);
                                else
                                        length = dpsnprintf(ptr, left, "%d %d%s \"%s\"\n",
-                                                                               cl->frags,
+                                                                               client->frags,
                                                                                pingvalue,
                                                                                teambuf,
                                                                                cleanname);
@@ -2632,12 +2739,17 @@ static qboolean hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const cha
        char mdfourbuf[16];
        long t1, t2;
 
+       if (!password[0]) {
+               Con_Print("^4LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
+               return false;
+       }
+
        t1 = (long) time(NULL);
        t2 = strtol(s, NULL, 0);
        if(abs(t1 - t2) > rcon_secure_maxdiff.integer)
                return false;
 
-       if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, strlen(password)))
+       if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
                return false;
 
        return !memcmp(mdfourbuf, hash, 16);
@@ -2648,32 +2760,42 @@ static qboolean hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, cons
        char mdfourbuf[16];
        int i;
 
-       if(slen < (int)(sizeof(challenge[0].string)) - 1)
+       if (!password[0]) {
+               Con_Print("^4LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
+               return false;
+       }
+
+       if(slen < (int)(sizeof(challenges[0].string)) - 1)
                return false;
 
        // validate the challenge
        for (i = 0;i < MAX_CHALLENGES;i++)
-               if(challenge[i].time > 0)
-                       if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address) && !strncmp(challenge[i].string, s, sizeof(challenge[0].string) - 1))
+               if(challenges[i].time > 0)
+                       if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strncmp(challenges[i].string, s, sizeof(challenges[0].string) - 1))
                                break;
        // if the challenge is not recognized, drop the packet
        if (i == MAX_CHALLENGES)
                return false;
 
-       if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, strlen(password)))
+       if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
                return false;
 
        if(memcmp(mdfourbuf, hash, 16))
                return false;
 
        // unmark challenge to prevent replay attacks
-       challenge[i].time = 0;
+       challenges[i].time = 0;
 
        return true;
 }
 
 static qboolean plaintext_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
 {
+       if (!password[0]) {
+               Con_Print("^4LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
+               return false;
+       }
+
        return !strcmp(password, hash);
 }
 
@@ -2685,19 +2807,19 @@ static const char *RCon_Authenticate(lhnetaddress_t *peeraddress, const char *pa
        qboolean hasquotes;
        qboolean restricted = false;
        qboolean have_usernames = false;
-       char vabuf[1024];
+       static char vabuf[1024];
 
        userpass_start = rcon_password.string;
        while((userpass_end = strchr(userpass_start, ' ')))
        {
                have_usernames = true;
                strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
-               if(buf[0])
+               if(buf[0])  // Ignore empty entries due to leading/duplicate space.
                        if(comparator(peeraddress, buf, password, cs, cslen))
                                goto allow;
                userpass_start = userpass_end + 1;
        }
-       if(userpass_start[0])
+       if(userpass_start[0])  // Ignore empty trailing entry due to trailing space or password not set.
        {
                userpass_end = userpass_start + strlen(userpass_start);
                if(comparator(peeraddress, userpass_start, password, cs, cslen))
@@ -2711,12 +2833,12 @@ static const char *RCon_Authenticate(lhnetaddress_t *peeraddress, const char *pa
        {
                have_usernames = true;
                strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
-               if(buf[0])
+               if(buf[0])  // Ignore empty entries due to leading/duplicate space.
                        if(comparator(peeraddress, buf, password, cs, cslen))
                                goto check;
                userpass_start = userpass_end + 1;
        }
-       if(userpass_start[0])
+       if(userpass_start[0])  // Ignore empty trailing entry due to trailing space or password not set.
        {
                userpass_end = userpass_start + strlen(userpass_start);
                if(comparator(peeraddress, userpass_start, password, cs, cslen))
@@ -2818,14 +2940,12 @@ 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];
+       char *string, response[1400], addressstring2[128];
        static char stringbuf[16384]; // server only
        qboolean islocal = (LHNETADDRESS_GetAddressType(peeraddress) == LHNETADDRESSTYPE_LOOP);
        char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
        size_t sendlength, response_len;
        char infostringvalue[MAX_INPUTLINE];
-       char vabuf[1024];
 
        if (!sv.active)
                return false;
@@ -2869,20 +2989,20 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                if(sendlength)
                                {
                                        memcpy(senddata, "\377\377\377\377", 4);
-                                       NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
+                                       NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
                                }
                                break;
                        case CRYPTO_DISCARD:
                                if(sendlength)
                                {
                                        memcpy(senddata, "\377\377\377\377", 4);
-                                       NetConn_Write(mysocket, senddata, sendlength+4, peeraddress);
+                                       NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
                                }
                                return true;
                                break;
                        case CRYPTO_REPLACE:
                                string = senddata+4;
-                               length = sendlength;
+                               length = (int)sendlength;
                                break;
                }
 
@@ -2890,36 +3010,39 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                {
                        for (i = 0, best = 0, besttime = realtime;i < MAX_CHALLENGES;i++)
                        {
-                               if(challenge[i].time > 0)
-                                       if (!LHNETADDRESS_Compare(peeraddress, &challenge[i].address))
+                               if(challenges[i].time > 0)
+                                       if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address))
                                                break;
-                               if (besttime > challenge[i].time)
-                                       besttime = challenge[best = i].time;
+                               if (besttime > challenges[i].time)
+                                       besttime = challenges[best = i].time;
                        }
                        // if we did not find an exact match, choose the oldest and
                        // update address and string
                        if (i == MAX_CHALLENGES)
                        {
                                i = best;
-                               challenge[i].address = *peeraddress;
-                               NetConn_BuildChallengeString(challenge[i].string, sizeof(challenge[i].string));
+                               challenges[i].address = *peeraddress;
+                               NetConn_BuildChallengeString(challenges[i].string, sizeof(challenges[i].string));
                        }
                        else
                        {
                                // flood control: drop if requesting challenge too often
-                               if(challenge[i].time > realtime - net_challengefloodblockingtimeout.value)
+                               if(challenges[i].time > realtime - net_challengefloodblockingtimeout.value)
                                        return true;
                        }
-                       challenge[i].time = realtime;
+                       challenges[i].time = realtime;
                        // send the challenge
-                       dpsnprintf(response, sizeof(response), "\377\377\377\377challenge %s", challenge[i].string);
+                       memcpy(response, "\377\377\377\377", 4);
+                       dpsnprintf(response+4, sizeof(response)-4, "challenge %s", challenges[i].string);
                        response_len = strlen(response) + 1;
                        Crypto_ServerAppendToChallenge(string, length, response, &response_len, sizeof(response));
-                       NetConn_Write(mysocket, response, response_len, peeraddress);
+                       NetConn_Write(mysocket, response, (int)response_len, peeraddress);
                        return true;
                }
                if (length > 8 && !memcmp(string, "connect\\", 8))
                {
+                       char *s;
+                       client_t *client;
                        crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
                        string += 7;
                        length -= 7;
@@ -2929,12 +3052,14 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                // 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",
+                                       Con_Printf("%s connection to %s is being established: client is %s@%s%.*s, I am %.*s@%s%.*s\n",
                                                        crypto->use_aes ? "Encrypted" : "Authenticated",
                                                        addressstring2,
                                                        crypto->client_idfp[0] ? crypto->client_idfp : "-",
+                                                       (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
                                                        crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-",
                                                        crypto_keyfp_recommended_length, crypto->server_idfp[0] ? crypto->server_idfp : "-",
+                                                       (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
                                                        crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-"
                                                  );
                                }
@@ -2945,8 +3070,8 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                {
                                        // 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))
+                                               if(challenges[i].time > 0)
+                                                       if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strcmp(challenges[i].string, s))
                                                                break;
                                        // if the challenge is not recognized, drop the packet
                                        if (i == MAX_CHALLENGES)
@@ -2961,7 +3086,9 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        {
                                if (developer_extra.integer)
                                        Con_Printf("Datagram_ParseConnectionless: sending \"reject %s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
-                               NetConn_WriteString(mysocket, va(vabuf, sizeof(vabuf), "\377\377\377\377reject %s", sv_public_rejectreason.string), peeraddress);
+                               memcpy(response, "\377\377\377\377", 4);
+                               dpsnprintf(response+4, sizeof(response)-4, "reject %s", sv_public_rejectreason.string);
+                               NetConn_WriteString(mysocket, response, peeraddress);
                                return true;
                        }
 
@@ -3022,7 +3149,7 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                                        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);
+                                                       Crypto_FinishInstance(&client->netconnection->crypto, crypto);
                                                SV_SendServerinfo(client);
                                        }
                                        else
@@ -3032,7 +3159,7 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                                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);
+                                                       Crypto_FinishInstance(&client->netconnection->crypto, crypto);
                                                NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
                                        }
                                        return true;
@@ -3054,7 +3181,7 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                        NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
                                        // now set up the client
                                        if(crypto && crypto->authenticated)
-                                               Crypto_ServerFinishInstance(&conn->crypto, crypto);
+                                               Crypto_FinishInstance(&conn->crypto, crypto);
                                        SV_ConnectClient(clientnum, conn);
                                        NetConn_Heartbeat(1);
                                        return true;
@@ -3142,7 +3269,7 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                }
                if (length >= 5 && !memcmp(string, "rcon ", 5))
                {
-                       int i;
+                       int j;
                        char *s = string + 5;
                        char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
                        char password[64];
@@ -3150,12 +3277,12 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        if(rcon_secure.integer > 0)
                                return true;
 
-                       for (i = 0;!ISWHITESPACE(*s);s++)
-                               if (i < (int)sizeof(password) - 1)
-                                       password[i++] = *s;
+                       for (j = 0;!ISWHITESPACE(*s);s++)
+                               if (j < (int)sizeof(password) - 1)
+                                       password[j++] = *s;
                        if(ISWHITESPACE(*s) && s != endpos) // skip leading ugly space
                                ++s;
-                       password[i] = 0;
+                       password[j] = 0;
                        if (!ISWHITESPACE(password[0]))
                        {
                                const char *userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
@@ -3195,6 +3322,8 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                int c;
                int protocolnumber;
                const char *protocolname;
+               client_t *knownclient;
+               client_t *newclient;
                data += 4;
                length -= 4;
                SZ_Clear(&sv_message);
@@ -3214,7 +3343,8 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                // save space for the header, filled in later
                                MSG_WriteLong(&sv_message, 0);
                                MSG_WriteByte(&sv_message, CCREP_REJECT);
-                               MSG_WriteString(&sv_message, va(vabuf, sizeof(vabuf), "%s\n", sv_public_rejectreason.string));
+                               MSG_WriteUnterminatedString(&sv_message, sv_public_rejectreason.string);
+                               MSG_WriteString(&sv_message, "\n");
                                StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
                                NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
                                SZ_Clear(&sv_message);
@@ -3239,16 +3369,16 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                        }
 
                        // see if this connect request comes from a known client
-                       for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
+                       for (clientnum = 0, knownclient = svs.clients;clientnum < svs.maxclients;clientnum++, knownclient++)
                        {
-                               if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
+                               if (knownclient->netconnection && LHNETADDRESS_Compare(peeraddress, &knownclient->netconnection->peeraddress) == 0)
                                {
                                        // this is either a duplicate connection request
                                        // or coming back from a timeout
                                        // (if so, keep their stuff intact)
 
                                        crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
-                                       if((crypto && crypto->authenticated) || client->netconnection->crypto.authenticated)
+                                       if((crypto && crypto->authenticated) || knownclient->netconnection->crypto.authenticated)
                                        {
                                                if (developer_extra.integer)
                                                        Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Attempt to downgrade crypto.\" to %s.\n", addressstring2);
@@ -3270,15 +3400,15 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                        // save space for the header, filled in later
                                        MSG_WriteLong(&sv_message, 0);
                                        MSG_WriteByte(&sv_message, CCREP_ACCEPT);
-                                       MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(client->netconnection->mysocket)));
+                                       MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(knownclient->netconnection->mysocket)));
                                        StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
                                        NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
                                        SZ_Clear(&sv_message);
 
                                        // if client is already spawned, re-send the
                                        // serverinfo message as they'll need it to play
-                                       if (client->begun)
-                                               SV_SendServerinfo(client);
+                                       if (knownclient->begun)
+                                               SV_SendServerinfo(knownclient);
                                        return true;
                                }
                        }
@@ -3288,10 +3418,10 @@ static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *dat
                                break;
 
                        // find a slot for the new client
-                       for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
+                       for (clientnum = 0, newclient = svs.clients;clientnum < svs.maxclients;clientnum++, newclient++)
                        {
                                netconn_t *conn;
-                               if (!client->active && (client->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
+                               if (!newclient->active && (newclient->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
                                {
                                        // connect to the client
                                        // everything is allocated, just fill in the details
@@ -3495,6 +3625,7 @@ void NetConn_SleepMicroseconds(int microseconds)
        LHNET_SleepUntilPacket_Microseconds(microseconds);
 }
 
+#ifdef CONFIG_MENU
 void NetConn_QueryMasters(qboolean querydp, qboolean queryqw)
 {
        int i, j;
@@ -3548,7 +3679,8 @@ void NetConn_QueryMasters(qboolean querydp, qboolean queryqw)
                                        cmdname = "getservers";
                                        extraoptions = "";
                                }
-                               dpsnprintf(request, sizeof(request), "\377\377\377\377%s %s %u empty full%s", cmdname, gamename, NET_PROTOCOL_VERSION, extraoptions);
+                               memcpy(request, "\377\377\377\377", 4);
+                               dpsnprintf(request+4, sizeof(request)-4, "%s %s %u empty full%s", cmdname, gamenetworkfiltername, NET_PROTOCOL_VERSION, extraoptions);
 
                                // search internet
                                for (masternum = 0;sv_masters[masternum].name;masternum++)
@@ -3597,16 +3729,12 @@ void NetConn_QueryMasters(qboolean querydp, qboolean queryqw)
                                {
                                        if (sv_qwmasters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_qwmasters[masternum].string, QWMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])))
                                        {
-#ifdef CONFIG_MENU
                                                if (m_state != m_slist)
                                                {
-#endif
                                                        char lookupstring[128];
                                                        LHNETADDRESS_ToString(&masteraddress, lookupstring, sizeof(lookupstring), true);
                                                        Con_Printf("Querying master %s (resolved from %s)\n", lookupstring, sv_qwmasters[masternum].string);
-#ifdef CONFIG_MENU
                                                }
-#endif
                                                masterquerycount++;
                                                NetConn_Write(cl_sockets[i], request, (int)strlen(request) + 1, &masteraddress);
                                        }
@@ -3630,11 +3758,10 @@ void NetConn_QueryMasters(qboolean querydp, qboolean queryqw)
        if (!masterquerycount)
        {
                Con_Print("Unable to query master servers, no suitable network sockets active.\n");
-#ifdef CONFIG_MENU
                M_Update_Return_Reason("No network");
-#endif
        }
 }
+#endif
 
 void NetConn_Heartbeat(int priority)
 {
@@ -3700,18 +3827,15 @@ void Net_Stats_f(void)
                PrintStats(conn);
 }
 
+#ifdef CONFIG_MENU
 void Net_Refresh_f(void)
 {
-#ifdef CONFIG_MENU
        if (m_state != m_slist) {
-#endif
                Con_Print("Sending new requests to master servers\n");
                ServerList_QueryList(false, true, false, true);
                Con_Print("Listening for replies...\n");
-#ifdef CONFIG_MENU
        } else
                ServerList_QueryList(false, true, false, false);
-#endif
 }
 
 void Net_Slist_f(void)
@@ -3719,16 +3843,12 @@ void Net_Slist_f(void)
        ServerList_ResetMasks();
        serverlist_sortbyfield = SLIF_PING;
        serverlist_sortflags = 0;
-#ifdef CONFIG_MENU
     if (m_state != m_slist) {
-#endif
                Con_Print("Sending requests to master servers\n");
                ServerList_QueryList(true, true, false, true);
                Con_Print("Listening for replies...\n");
-#ifdef CONFIG_MENU
        } else
                ServerList_QueryList(true, true, false, false);
-#endif
 }
 
 void Net_SlistQW_f(void)
@@ -3736,18 +3856,15 @@ void Net_SlistQW_f(void)
        ServerList_ResetMasks();
        serverlist_sortbyfield = SLIF_PING;
        serverlist_sortflags = 0;
-#ifdef CONFIG_MENU
     if (m_state != m_slist) {
-#endif
                Con_Print("Sending requests to master servers\n");
                ServerList_QueryList(true, false, true, true);
                serverlist_consoleoutput = true;
                Con_Print("Listening for replies...\n");
-#ifdef CONFIG_MENU
        } else
                ServerList_QueryList(true, false, true, false);
-#endif
 }
+#endif
 
 void NetConn_Init(void)
 {
@@ -3755,9 +3872,11 @@ void NetConn_Init(void)
        lhnetaddress_t tempaddress;
        netconn_mempool = Mem_AllocPool("network connections", 0, NULL);
        Cmd_AddCommand("net_stats", Net_Stats_f, "print network statistics");
+#ifdef CONFIG_MENU
        Cmd_AddCommand("net_slist", Net_Slist_f, "query dp master servers and print all server information");
        Cmd_AddCommand("net_slistqw", Net_SlistQW_f, "query qw master servers and print all server information");
        Cmd_AddCommand("net_refresh", Net_Refresh_f, "query dp master servers and refresh all server information");
+#endif
        Cmd_AddCommand("heartbeat", Net_Heartbeat_f, "send a heartbeat to the master server (updates your server information)");
        Cvar_RegisterVariable(&net_test);
        Cvar_RegisterVariable(&net_usesizelimit);
@@ -3778,6 +3897,7 @@ void NetConn_Init(void)
        Cvar_RegisterVariable(&net_connectfloodblockingtimeout);
        Cvar_RegisterVariable(&net_challengefloodblockingtimeout);
        Cvar_RegisterVariable(&net_getstatusfloodblockingtimeout);
+       Cvar_RegisterVariable(&net_sourceaddresscheck);
        Cvar_RegisterVariable(&cl_netlocalping);
        Cvar_RegisterVariable(&cl_netpacketloss_send);
        Cvar_RegisterVariable(&cl_netpacketloss_receive);