]> git.xonotic.org Git - xonotic/darkplaces.git/blob - netconn.c
client: improve server connection status messages
[xonotic/darkplaces.git] / netconn.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3 Copyright (C) 2002 Mathieu Olivier
4 Copyright (C) 2003 Ashley Rose Hale (LadyHavoc)
5
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
15 See the GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20
21 */
22
23 #include "quakedef.h"
24 #include "thread.h"
25 #include "lhnet.h"
26
27 // for secure rcon authentication
28 #include "hmac.h"
29 #include "mdfour.h"
30 #include <time.h>
31
32 #define QWMASTER_PORT 27000
33 #define DPMASTER_PORT 27950
34
35 // note this defaults on for dedicated servers, off for listen servers
36 cvar_t sv_public = {CF_SERVER, "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"};
37 cvar_t sv_public_rejectreason = {CF_SERVER, "sv_public_rejectreason", "The server is closing.", "Rejection reason for connects when sv_public is -2"};
38 static cvar_t sv_heartbeatperiod = {CF_SERVER | CF_ARCHIVE, "sv_heartbeatperiod", "120", "how often to send heartbeat in seconds (only used if sv_public is 1)"};
39 extern cvar_t sv_status_privacy;
40
41 static cvar_t sv_masters [] =
42 {
43         {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_master1", "", "user-chosen master server 1"},
44         {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_master2", "", "user-chosen master server 2"},
45         {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_master3", "", "user-chosen master server 3"},
46         {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_master4", "", "user-chosen master server 4"},
47         {CF_CLIENT | CF_SERVER, "sv_masterextra1", "dpmaster.deathmask.net", "dpmaster.deathmask.net - default master server 1 (admin: Willis)"},
48         {CF_CLIENT | CF_SERVER, "sv_masterextra2", "dpmaster.tchr.no", "dpmaster.tchr.no - default master server 2 (admin: tChr)"},
49         {CF_CLIENT | CF_SERVER, "sv_masterextra3", "dpm.dpmaster.org:27777", "dpm.dpmaster.org - default master server 3 (admin: gazby/soylent_cow)"},
50 };
51
52 #ifdef CONFIG_MENU
53 static cvar_t sv_qwmasters [] =
54 {
55         {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_qwmaster1", "", "user-chosen qwmaster server 1"},
56         {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_qwmaster2", "", "user-chosen qwmaster server 2"},
57         {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_qwmaster3", "", "user-chosen qwmaster server 3"},
58         {CF_CLIENT | CF_SERVER | CF_ARCHIVE, "sv_qwmaster4", "", "user-chosen qwmaster server 4"},
59         {CF_CLIENT | CF_SERVER, "sv_qwmasterextra1", "master.quakeservers.net:27000", "Global master server. (admin: unknown)"},
60         {CF_CLIENT | CF_SERVER, "sv_qwmasterextra2", "asgaard.morphos-team.net:27000", "Global master server. (admin: unknown)"},
61         {CF_CLIENT | CF_SERVER, "sv_qwmasterextra3", "qwmaster.ocrana.de:27000", "German master server. (admin: unknown)"},
62         {CF_CLIENT | CF_SERVER, "sv_qwmasterextra4", "qwmaster.fodquake.net:27000", "Global master server. (admin: unknown)"},
63 };
64 #endif
65
66 static double nextheartbeattime = 0;
67
68 sizebuf_t cl_message;
69 sizebuf_t sv_message;
70 static unsigned char cl_message_buf[NET_MAXMESSAGE];
71 static unsigned char sv_message_buf[NET_MAXMESSAGE];
72 char cl_readstring[MAX_INPUTLINE];
73 char sv_readstring[MAX_INPUTLINE];
74
75 cvar_t net_test = {CF_CLIENT | CF_SERVER, "net_test", "0", "internal development use only, leave it alone (usually does nothing anyway)"};
76 cvar_t net_usesizelimit = {CF_SERVER, "net_usesizelimit", "2", "use packet size limiting (0: never, 1: in non-CSQC mode, 2: always)"};
77 cvar_t net_burstreserve = {CF_SERVER, "net_burstreserve", "0.3", "how much of the burst time to reserve for packet size spikes"};
78 cvar_t net_messagetimeout = {CF_CLIENT | CF_SERVER, "net_messagetimeout","300", "drops players who have not sent any packets for this many seconds"};
79 cvar_t net_connecttimeout = {CF_CLIENT | CF_SERVER, "net_connecttimeout","15", "after requesting a connection, the client must reply within this many seconds or be dropped (cuts down on connect floods). Must be above 10 seconds."};
80 cvar_t net_connectfloodblockingtimeout = {CF_SERVER, "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."};
81 cvar_t net_challengefloodblockingtimeout = {CF_SERVER, "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."};
82 cvar_t net_getstatusfloodblockingtimeout = {CF_SERVER, "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 net_slist_timeout 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."};
83 cvar_t net_sourceaddresscheck = {CF_CLIENT, "net_sourceaddresscheck", "1", "compare the source IP address for replies (more secure, may break some bad multihoming setups"};
84 cvar_t hostname = {CF_SERVER | CF_ARCHIVE, "hostname", "UNNAMED", "server message to show in server browser"};
85 cvar_t developer_networking = {CF_CLIENT | CF_SERVER, "developer_networking", "0", "prints all received and sent packets (recommended only for debugging)"};
86
87 cvar_t net_fakelag = {CF_CLIENT, "net_fakelag","0", "lags local loopback connection by this much ping time (useful to play more fairly on your own server with people with higher pings)"};
88 static cvar_t net_fakeloss_send = {CF_CLIENT, "net_fakeloss_send","0", "drops this percentage of outgoing packets, useful for testing network protocol robustness (jerky movement, prediction errors, etc)"};
89 static cvar_t net_fakeloss_receive = {CF_CLIENT, "net_fakeloss_receive","0", "drops this percentage of incoming packets, useful for testing network protocol robustness (jerky movement, effects failing to start, sounds failing to play, etc)"};
90
91 #ifdef CONFIG_MENU
92 static cvar_t net_slist_debug = {CF_CLIENT, "net_slist_debug", "0", "enables verbose messages for master server queries"};
93 static cvar_t net_slist_favorites = {CF_CLIENT | CF_ARCHIVE, "net_slist_favorites", "", "contains a list of IP addresses and ports to always query explicitly"};
94 static cvar_t net_slist_interval = {CF_CLIENT, "net_slist_interval", "1.125", "minimum number of seconds to wait between getstatus queries to the same DP server, must be >= server's net_getstatusfloodblockingtimeout"};
95 static cvar_t net_slist_maxping = {CF_CLIENT | CF_ARCHIVE, "net_slist_maxping", "420", "server query responses are ignored if their ping in milliseconds is higher than this"};
96 static cvar_t net_slist_maxtries = {CF_CLIENT, "net_slist_maxtries", "3", "how many times to ask the same server for information (more times gives better ping reports but takes longer)"};
97 static cvar_t net_slist_pause = {CF_CLIENT, "net_slist_pause", "0", "when set to 1, the server list sorting in the menu won't update until it is set back to 0"};
98 static cvar_t net_slist_queriespersecond = {CF_CLIENT, "net_slist_queriespersecond", "128", "how many server information requests to send per second"};
99 static cvar_t net_slist_queriesperframe = {CF_CLIENT, "net_slist_queriesperframe", "2", "maximum number of server information requests to send each rendered frame (guards against low framerates causing problems)"};
100 static cvar_t net_slist_timeout = {CF_CLIENT, "net_slist_timeout", "4", "minimum number of seconds to wait between status queries to the same QW server, determines which response belongs to which query so low values will cause impossible pings; also a warning is printed if a dpmaster query fails to complete within this time"};
101 #endif
102
103 static cvar_t net_tos_dscp = {CF_CLIENT | CF_ARCHIVE, "net_tos_dscp", "32", "DiffServ Codepoint for network sockets (may need game restart to apply)"};
104 static cvar_t gameversion = {CF_SERVER, "gameversion", "0", "version of game data (mod-specific) to be sent to querying clients"};
105 static cvar_t gameversion_min = {CF_CLIENT | CF_SERVER, "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"};
106 static cvar_t gameversion_max = {CF_CLIENT | CF_SERVER, "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"};
107 static cvar_t rcon_restricted_password = {CF_SERVER | CF_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"};
108 static cvar_t rcon_restricted_commands = {CF_SERVER, "rcon_restricted_commands", "", "allowed commands for rcon when the restricted mode password was used"};
109 static cvar_t rcon_secure_maxdiff = {CF_SERVER, "rcon_secure_maxdiff", "5", "maximum time difference between rcon request and server system clock (to protect against replay attack)"};
110 extern cvar_t rcon_secure;
111 extern cvar_t rcon_secure_challengetimeout;
112
113 double masterquerytime = -1000;
114 unsigned masterquerycount = 0;
115 unsigned masterreplycount = 0;
116 unsigned serverquerycount = 0;
117 unsigned serverreplycount = 0;
118
119 challenge_t challenges[MAX_CHALLENGES];
120
121 #define DPMASTER_COUNT sizeof(sv_masters) / sizeof(cvar_t)
122 #define QWMASTER_COUNT sizeof(sv_qwmasters) / sizeof(cvar_t)
123
124 /// bitfield because in theory we could be doing QW & DP simultaneously
125 uint8_t serverlist_querystage = 0;
126
127 #ifdef CONFIG_MENU
128 #define SLIST_QUERYSTAGE_DPMASTERS 1
129 #define SLIST_QUERYSTAGE_QWMASTERS 2
130 #define SLIST_QUERYSTAGE_SERVERS 4
131
132 static uint8_t dpmasterstatus[DPMASTER_COUNT] = {0};
133 static uint8_t qwmasterstatus[QWMASTER_COUNT] = {0};
134 #define MASTER_TX_QUERY 1    // we sent the query
135 #define MASTER_RX_RESPONSE 2 // we got at least 1 packet of the response
136 #define MASTER_RX_COMPLETE 3 // we saw the EOT marker (assumes dpmaster >= 2.0, see dpmaster/doc/techinfo.txt)
137
138 /// the hash password for timestamp verification
139 char serverlist_dpserverquerykey[12]; // challenge_t uses [12]
140 #endif
141
142 static unsigned cl_numsockets;
143 static lhnetsocket_t *cl_sockets[16];
144 static unsigned sv_numsockets;
145 static lhnetsocket_t *sv_sockets[16];
146
147 netconn_t *netconn_list = NULL;
148 mempool_t *netconn_mempool = NULL;
149 void *netconn_mutex = NULL;
150
151 cvar_t cl_netport = {CF_CLIENT, "cl_port", "0", "forces client to use chosen port number if not 0"};
152 cvar_t sv_netport = {CF_SERVER, "port", "26000", "server port for players to connect to"};
153 cvar_t net_address = {CF_CLIENT | CF_SERVER, "net_address", "", "network address to open ipv4 ports on (if empty, use default interfaces)"};
154 cvar_t net_address_ipv6 = {CF_CLIENT | CF_SERVER, "net_address_ipv6", "", "network address to open ipv6 ports on (if empty, use default interfaces)"};
155
156 char cl_net_extresponse[NET_EXTRESPONSE_MAX][1400];
157 unsigned cl_net_extresponse_count = 0;
158 unsigned cl_net_extresponse_last = 0;
159
160 char sv_net_extresponse[NET_EXTRESPONSE_MAX][1400];
161 unsigned sv_net_extresponse_count = 0;
162 unsigned sv_net_extresponse_last = 0;
163
164 #ifdef CONFIG_MENU
165 // ServerList interface
166 serverlist_mask_t serverlist_andmasks[SERVERLIST_ANDMASKCOUNT];
167 serverlist_mask_t serverlist_ormasks[SERVERLIST_ORMASKCOUNT];
168
169 serverlist_infofield_t serverlist_sortbyfield;
170 unsigned serverlist_sortflags;
171
172 unsigned serverlist_viewcount = 0;
173 uint16_t serverlist_viewlist[SERVERLIST_VIEWLISTSIZE];
174
175 unsigned serverlist_maxcachecount = 0;
176 unsigned serverlist_cachecount = 0;
177 serverlist_entry_t *serverlist_cache = NULL;
178
179 static qbool serverlist_consoleoutput;
180
181 static unsigned nFavorites = 0;
182 static lhnetaddress_t favorites[MAX_FAVORITESERVERS];
183 static unsigned nFavorites_idfp = 0;
184 static char favorites_idfp[MAX_FAVORITESERVERS][FP64_SIZE+1];
185
186 void NetConn_UpdateFavorites_c(cvar_t *var)
187 {
188         const char *p;
189         nFavorites = 0;
190         nFavorites_idfp = 0;
191         p = var->string;
192         while((size_t) nFavorites < sizeof(favorites) / sizeof(*favorites) && COM_ParseToken_Console(&p))
193         {
194                 if(com_token[0] != '[' && strlen(com_token) == FP64_SIZE && !strchr(com_token, '.'))
195                 // currently 44 bytes, longest possible IPv6 address: 39 bytes, so this works
196                 // (if v6 address contains port, it must start with '[')
197                 {
198                         strlcpy(favorites_idfp[nFavorites_idfp], com_token, sizeof(favorites_idfp[nFavorites_idfp]));
199                         ++nFavorites_idfp;
200                 }
201                 else 
202                 {
203                         if(LHNETADDRESS_FromString(&favorites[nFavorites], com_token, 26000))
204                                 ++nFavorites;
205                 }
206         }
207 }
208
209 /// helper function to insert a value into the viewset
210 /// spare entries will be removed
211 static void _ServerList_ViewList_Helper_InsertBefore(unsigned index, serverlist_entry_t *entry)
212 {
213         unsigned i;
214
215         if( serverlist_viewcount < SERVERLIST_VIEWLISTSIZE ) {
216                 i = serverlist_viewcount++;
217         } else {
218                 i = SERVERLIST_VIEWLISTSIZE - 1;
219         }
220
221         for( ; i > index ; i-- )
222                 serverlist_viewlist[ i ] = serverlist_viewlist[ i - 1 ];
223
224         serverlist_viewlist[index] = (int)(entry - serverlist_cache);
225 }
226
227 /// we suppose serverlist_viewcount to be valid, ie > 0
228 static inline void _ServerList_ViewList_Helper_Remove(unsigned index)
229 {
230         serverlist_viewcount--;
231         for( ; index < serverlist_viewcount ; index++ )
232                 serverlist_viewlist[index] = serverlist_viewlist[index + 1];
233 }
234
235 /// \returns true if A should be inserted before B
236 static qbool _ServerList_Entry_Compare( serverlist_entry_t *A, serverlist_entry_t *B )
237 {
238         int result = 0; // > 0 if for numbers A > B and for text if A < B
239
240         if( serverlist_sortflags & SLSF_CATEGORIES )
241         {
242                 result = A->info.category - B->info.category;
243                 if (result != 0)
244                         return result < 0;
245         }
246
247         if( serverlist_sortflags & SLSF_FAVORITES )
248         {
249                 if(A->info.isfavorite != B->info.isfavorite)
250                         return A->info.isfavorite;
251         }
252         
253         switch( serverlist_sortbyfield ) {
254                 case SLIF_PING:
255                         result = A->info.ping - B->info.ping;
256                         break;
257                 case SLIF_MAXPLAYERS:
258                         result = A->info.maxplayers - B->info.maxplayers;
259                         break;
260                 case SLIF_NUMPLAYERS:
261                         result = A->info.numplayers - B->info.numplayers;
262                         break;
263                 case SLIF_NUMBOTS:
264                         result = A->info.numbots - B->info.numbots;
265                         break;
266                 case SLIF_NUMHUMANS:
267                         result = A->info.numhumans - B->info.numhumans;
268                         break;
269                 case SLIF_FREESLOTS:
270                         result = A->info.freeslots - B->info.freeslots;
271                         break;
272                 case SLIF_PROTOCOL:
273                         result = A->info.protocol - B->info.protocol;
274                         break;
275                 case SLIF_CNAME:
276                         result = strcmp( B->info.cname, A->info.cname );
277                         break;
278                 case SLIF_GAME:
279                         result = strcasecmp( B->info.game, A->info.game );
280                         break;
281                 case SLIF_MAP:
282                         result = strcasecmp( B->info.map, A->info.map );
283                         break;
284                 case SLIF_MOD:
285                         result = strcasecmp( B->info.mod, A->info.mod );
286                         break;
287                 case SLIF_NAME:
288                         result = strcasecmp( B->info.name, A->info.name );
289                         break;
290                 case SLIF_QCSTATUS:
291                         result = strcasecmp( B->info.qcstatus, A->info.qcstatus ); // not really THAT useful, though
292                         break;
293                 case SLIF_CATEGORY:
294                         result = A->info.category - B->info.category;
295                         break;
296                 case SLIF_ISFAVORITE:
297                         result = !!B->info.isfavorite - !!A->info.isfavorite;
298                         break;
299                 default:
300                         Con_DPrint( "_ServerList_Entry_Compare: Bad serverlist_sortbyfield!\n" );
301                         break;
302         }
303
304         if (result != 0)
305         {
306                 if( serverlist_sortflags & SLSF_DESCENDING )
307                         return result > 0;
308                 else
309                         return result < 0;
310         }
311
312         // if the chosen sort key is identical, sort by index
313         // (makes this a stable sort, so that later replies from servers won't
314         //  shuffle the servers around when they have the same ping)
315         return A < B;
316 }
317
318 static qbool _ServerList_CompareInt( int A, serverlist_maskop_t op, int B )
319 {
320         // This should actually be done with some intermediate and end-of-function return
321         switch( op ) {
322                 case SLMO_LESS:
323                         return A < B;
324                 case SLMO_LESSEQUAL:
325                         return A <= B;
326                 case SLMO_EQUAL:
327                         return A == B;
328                 case SLMO_GREATER:
329                         return A > B;
330                 case SLMO_NOTEQUAL:
331                         return A != B;
332                 case SLMO_GREATEREQUAL:
333                 case SLMO_CONTAINS:
334                 case SLMO_NOTCONTAIN:
335                 case SLMO_STARTSWITH:
336                 case SLMO_NOTSTARTSWITH:
337                         return A >= B;
338                 default:
339                         Con_DPrint( "_ServerList_CompareInt: Bad op!\n" );
340                         return false;
341         }
342 }
343
344 static qbool _ServerList_CompareStr( const char *A, serverlist_maskop_t op, const char *B )
345 {
346         int i;
347         char bufferA[ 1400 ], bufferB[ 1400 ]; // should be more than enough
348         COM_StringDecolorize(A, 0, bufferA, sizeof(bufferA), false);
349         for (i = 0;i < (int)sizeof(bufferA)-1 && bufferA[i];i++)
350                 bufferA[i] = (bufferA[i] >= 'A' && bufferA[i] <= 'Z') ? (bufferA[i] + 'a' - 'A') : bufferA[i];
351         bufferA[i] = 0;
352         for (i = 0;i < (int)sizeof(bufferB)-1 && B[i];i++)
353                 bufferB[i] = (B[i] >= 'A' && B[i] <= 'Z') ? (B[i] + 'a' - 'A') : B[i];
354         bufferB[i] = 0;
355
356         // Same here, also using an intermediate & final return would be more appropriate
357         // A info B mask
358         switch( op ) {
359                 case SLMO_CONTAINS:
360                         return *bufferB && !!strstr( bufferA, bufferB ); // we want a real bool
361                 case SLMO_NOTCONTAIN:
362                         return !*bufferB || !strstr( bufferA, bufferB );
363                 case SLMO_STARTSWITH:
364                         //Con_Printf("startsWith: %s %s\n", bufferA, bufferB);
365                         return *bufferB && !memcmp(bufferA, bufferB, strlen(bufferB));
366                 case SLMO_NOTSTARTSWITH:
367                         return !*bufferB || memcmp(bufferA, bufferB, strlen(bufferB));
368                 case SLMO_LESS:
369                         return strcmp( bufferA, bufferB ) < 0;
370                 case SLMO_LESSEQUAL:
371                         return strcmp( bufferA, bufferB ) <= 0;
372                 case SLMO_EQUAL:
373                         return strcmp( bufferA, bufferB ) == 0;
374                 case SLMO_GREATER:
375                         return strcmp( bufferA, bufferB ) > 0;
376                 case SLMO_NOTEQUAL:
377                         return strcmp( bufferA, bufferB ) != 0;
378                 case SLMO_GREATEREQUAL:
379                         return strcmp( bufferA, bufferB ) >= 0;
380                 default:
381                         Con_DPrint( "_ServerList_CompareStr: Bad op!\n" );
382                         return false;
383         }
384 }
385
386 static qbool _ServerList_Entry_Mask( serverlist_mask_t *mask, serverlist_info_t *info )
387 {
388         if( !_ServerList_CompareInt( info->ping, mask->tests[SLIF_PING], mask->info.ping ) )
389                 return false;
390         if( !_ServerList_CompareInt( info->maxplayers, mask->tests[SLIF_MAXPLAYERS], mask->info.maxplayers ) )
391                 return false;
392         if( !_ServerList_CompareInt( info->numplayers, mask->tests[SLIF_NUMPLAYERS], mask->info.numplayers ) )
393                 return false;
394         if( !_ServerList_CompareInt( info->numbots, mask->tests[SLIF_NUMBOTS], mask->info.numbots ) )
395                 return false;
396         if( !_ServerList_CompareInt( info->numhumans, mask->tests[SLIF_NUMHUMANS], mask->info.numhumans ) )
397                 return false;
398         if( !_ServerList_CompareInt( info->freeslots, mask->tests[SLIF_FREESLOTS], mask->info.freeslots ) )
399                 return false;
400         if( !_ServerList_CompareInt( info->protocol, mask->tests[SLIF_PROTOCOL], mask->info.protocol ))
401                 return false;
402         if( *mask->info.cname
403                 && !_ServerList_CompareStr( info->cname, mask->tests[SLIF_CNAME], mask->info.cname ) )
404                 return false;
405         if( *mask->info.game
406                 && !_ServerList_CompareStr( info->game, mask->tests[SLIF_GAME], mask->info.game ) )
407                 return false;
408         if( *mask->info.mod
409                 && !_ServerList_CompareStr( info->mod, mask->tests[SLIF_MOD], mask->info.mod ) )
410                 return false;
411         if( *mask->info.map
412                 && !_ServerList_CompareStr( info->map, mask->tests[SLIF_MAP], mask->info.map ) )
413                 return false;
414         if( *mask->info.name
415                 && !_ServerList_CompareStr( info->name, mask->tests[SLIF_NAME], mask->info.name ) )
416                 return false;
417         if( *mask->info.qcstatus
418                 && !_ServerList_CompareStr( info->qcstatus, mask->tests[SLIF_QCSTATUS], mask->info.qcstatus ) )
419                 return false;
420         if( *mask->info.players
421                 && !_ServerList_CompareStr( info->players, mask->tests[SLIF_PLAYERS], mask->info.players ) )
422                 return false;
423         if( !_ServerList_CompareInt( info->category, mask->tests[SLIF_CATEGORY], mask->info.category ) )
424                 return false;
425         if( !_ServerList_CompareInt( info->isfavorite, mask->tests[SLIF_ISFAVORITE], mask->info.isfavorite ))
426                 return false;
427         return true;
428 }
429
430 static void ServerList_ViewList_Insert( serverlist_entry_t *entry )
431 {
432         unsigned start, end, mid, i;
433         lhnetaddress_t addr;
434
435         // reject incompatible servers
436         if(
437                 entry->info.gameversion != gameversion.integer
438                 &&
439                 !(
440                            gameversion_min.integer >= 0 // min/max range set by user/mod?
441                         && gameversion_max.integer >= 0
442                         && gameversion_min.integer <= entry->info.gameversion // version of server in min/max range?
443                         && gameversion_max.integer >= entry->info.gameversion
444                  )
445         )
446                 return;
447
448         // also display entries that are currently being refreshed [11/8/2007 Black]
449         // bones_was_here: if their previous ping was acceptable (unset if timeout occurs)
450         if (!entry->info.ping)
451                 return;
452
453         // refresh the "favorite" status
454         entry->info.isfavorite = false;
455         if(LHNETADDRESS_FromString(&addr, entry->info.cname, 26000))
456         {
457                 char idfp[FP64_SIZE+1];
458                 for(i = 0; i < nFavorites; ++i)
459                 {
460                         if(LHNETADDRESS_Compare(&addr, &favorites[i]) == 0)
461                         {
462                                 entry->info.isfavorite = true;
463                                 break;
464                         }
465                 }
466                 if(Crypto_RetrieveHostKey(&addr, 0, NULL, 0, idfp, sizeof(idfp), NULL, NULL))
467                 {
468                         for(i = 0; i < nFavorites_idfp; ++i)
469                         {
470                                 if(!strcmp(idfp, favorites_idfp[i]))
471                                 {
472                                         entry->info.isfavorite = true;
473                                         break;
474                                 }
475                         }
476                 }
477         }
478
479         // refresh the "category"
480         entry->info.category = MR_GetServerListEntryCategory(entry);
481
482         // FIXME: change this to be more readable (...)
483         // now check whether it passes through the masks
484         for( start = 0 ; start < SERVERLIST_ANDMASKCOUNT && serverlist_andmasks[start].active; start++ )
485                 if( !_ServerList_Entry_Mask( &serverlist_andmasks[start], &entry->info ) )
486                         return;
487
488         for( start = 0 ; start < SERVERLIST_ORMASKCOUNT && serverlist_ormasks[start].active ; start++ )
489                 if( _ServerList_Entry_Mask( &serverlist_ormasks[start], &entry->info ) )
490                         break;
491         if( start == SERVERLIST_ORMASKCOUNT || (start > 0 && !serverlist_ormasks[start].active) )
492                 return;
493
494         if( !serverlist_viewcount ) {
495                 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
496                 return;
497         }
498         // ok, insert it, we just need to find out where exactly:
499
500         // two special cases
501         // check whether to insert it as new first item
502         if( _ServerList_Entry_Compare( entry, ServerList_GetViewEntry(0) ) ) {
503                 _ServerList_ViewList_Helper_InsertBefore( 0, entry );
504                 return;
505         } // check whether to insert it as new last item
506         else if( !_ServerList_Entry_Compare( entry, ServerList_GetViewEntry(serverlist_viewcount - 1) ) ) {
507                 _ServerList_ViewList_Helper_InsertBefore( serverlist_viewcount, entry );
508                 return;
509         }
510         start = 0;
511         end = serverlist_viewcount - 1;
512         while( end > start + 1 )
513         {
514                 mid = (start + end) / 2;
515                 // test the item that lies in the middle between start and end
516                 if( _ServerList_Entry_Compare( entry, ServerList_GetViewEntry(mid) ) )
517                         // the item has to be in the upper half
518                         end = mid;
519                 else
520                         // the item has to be in the lower half
521                         start = mid;
522         }
523         _ServerList_ViewList_Helper_InsertBefore( start + 1, entry );
524 }
525
526 static void ServerList_ViewList_Remove( serverlist_entry_t *entry )
527 {
528         unsigned i;
529
530         for( i = 0; i < serverlist_viewcount; i++ )
531         {
532                 if (ServerList_GetViewEntry(i) == entry)
533                 {
534                         _ServerList_ViewList_Helper_Remove(i);
535                         break;
536                 }
537         }
538 }
539
540 void ServerList_RebuildViewList(cvar_t *var)
541 {
542         unsigned i;
543
544         if (net_slist_pause.integer)
545                 return;
546
547         serverlist_viewcount = 0;
548         for (i = 0; i < serverlist_cachecount; ++i)
549                 ServerList_ViewList_Insert(&serverlist_cache[i]);
550 }
551
552 void ServerList_ResetMasks(void)
553 {
554         int i;
555
556         memset( &serverlist_andmasks, 0, sizeof( serverlist_andmasks ) );
557         memset( &serverlist_ormasks, 0, sizeof( serverlist_ormasks ) );
558         // numbots needs to be compared to -1 to always succeed
559         for(i = 0; i < SERVERLIST_ANDMASKCOUNT; ++i)
560                 serverlist_andmasks[i].info.numbots = -1;
561         for(i = 0; i < SERVERLIST_ORMASKCOUNT; ++i)
562                 serverlist_ormasks[i].info.numbots = -1;
563 }
564
565 void ServerList_GetPlayerStatistics(unsigned *numplayerspointer, unsigned *maxplayerspointer)
566 {
567         unsigned i;
568         unsigned numplayers = 0, maxplayers = 0;
569
570         for (i = 0;i < serverlist_cachecount;i++)
571         {
572                 if (serverlist_cache[i].info.ping)
573                 {
574                         numplayers += serverlist_cache[i].info.numhumans;
575                         maxplayers += serverlist_cache[i].info.maxplayers;
576                 }
577         }
578         *numplayerspointer = numplayers;
579         *maxplayerspointer = maxplayers;
580 }
581
582 #if 0
583 static void _ServerList_Test(void)
584 {
585         int i;
586         if (serverlist_maxcachecount <= 1024)
587         {
588                 serverlist_maxcachecount = 1024;
589                 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
590         }
591         for( i = 0 ; i < 1024 ; i++ ) {
592                 memset( &serverlist_cache[serverlist_cachecount], 0, sizeof( serverlist_entry_t ) );
593                 serverlist_cache[serverlist_cachecount].info.ping = 1000 + 1024 - i;
594                 dpsnprintf( serverlist_cache[serverlist_cachecount].info.name, sizeof(serverlist_cache[serverlist_cachecount].info.name), "Black's ServerList Test %i", i );
595                 serverlist_cache[serverlist_cachecount].finished = true;
596                 dpsnprintf( serverlist_cache[serverlist_cachecount].line1, sizeof(serverlist_cache[serverlist_cachecount].info.line1), "%i %s", serverlist_cache[serverlist_cachecount].info.ping, serverlist_cache[serverlist_cachecount].info.name );
597                 ServerList_ViewList_Insert( &serverlist_cache[serverlist_cachecount] );
598                 serverlist_cachecount++;
599         }
600 }
601 #endif
602
603 /*
604 ====================
605 ServerList_BuildDPServerQuery
606
607 Generates the string for pinging DP servers with a hash-verified timestamp
608 to provide reliable pings while preventing ping cheating,
609 and discard spurious getstatus/getinfo packets.
610
611 14 bytes of header including the mandatory space,
612 22 bytes of base64-encoded hash,
613 up to 16 bytes of unsigned hexadecimal milliseconds (typically 4-5 bytes sent),
614 null terminator.
615
616 The maximum challenge length (after the space) for existing DP7 servers is 49.
617 ====================
618 */
619 static inline void ServerList_BuildDPServerQuery(char *buffer, size_t buffersize, double querytime)
620 {
621         unsigned char hash[24]; // 4*(16/3) rounded up to 4 byte multiple
622         uint64_t timestamp = querytime * 1000.0; // no rounding up as that could make small pings go <= 0
623
624         HMAC_MDFOUR_16BYTES(hash,
625                 (unsigned char *)&timestamp, sizeof(timestamp),
626                 (unsigned char *)serverlist_dpserverquerykey, sizeof(serverlist_dpserverquerykey));
627         base64_encode(hash, 16, sizeof(hash));
628         dpsnprintf(buffer, buffersize, "\377\377\377\377getstatus %.22s%" PRIx64, hash, timestamp);
629 }
630
631 static void NetConn_BuildChallengeString(char *buffer, int bufferlength);
632 void ServerList_QueryList(qbool resetcache, qbool querydp, qbool queryqw, qbool consoleoutput)
633 {
634         unsigned i;
635         lhnetaddress_t broadcastaddress;
636         char dpquery[53]; // theoretical max: 14+22+16+1
637
638         if (net_slist_debug.integer)
639                 Con_Printf("^2Querying master, favourite and LAN servers, reset=%u\n", resetcache);
640         serverlist_querystage = (querydp ? SLIST_QUERYSTAGE_DPMASTERS : 0) | (queryqw ? SLIST_QUERYSTAGE_QWMASTERS : 0);
641         masterquerycount = 0;
642         masterreplycount = 0;
643         if (resetcache)
644         {
645                 serverquerycount = 0;
646                 serverreplycount = 0;
647                 serverlist_cachecount = 0;
648                 serverlist_viewcount = 0;
649                 serverlist_maxcachecount = 0;
650                 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
651         }
652         else
653         {
654                 // refresh all entries
655                 for (i = 0; i < serverlist_cachecount; ++i)
656                 {
657                         serverlist_entry_t *entry = &serverlist_cache[i];
658                         entry->responded = false;
659                         entry->querycounter = 0;
660                 }
661         }
662         serverlist_consoleoutput = consoleoutput;
663
664         //_ServerList_Test();
665
666         NetConn_QueryMasters(querydp, queryqw);
667
668         // Generate new DP server query key string
669         // Used to prevent ping cheating and discard spurious getstatus/getinfo packets
670         if (!serverlist_querystage) // don't change key while updating
671                 NetConn_BuildChallengeString(serverlist_dpserverquerykey, sizeof(serverlist_dpserverquerykey));
672
673         // LAN search
674
675         // Master and and/or favourite queries were likely delayed by DNS lag,
676         // for correct pings we need to know what host.realtime would be if it were updated now.
677         masterquerytime = host.realtime + (Sys_DirtyTime() - host.dirtytime);
678         ServerList_BuildDPServerQuery(dpquery, sizeof(dpquery), masterquerytime);
679
680         // 26000 is the default quake server port, servers on other ports will not be found
681         // note this is IPv4-only, I doubt there are IPv6-only LANs out there
682         LHNETADDRESS_FromString(&broadcastaddress, "255.255.255.255", 26000);
683
684         for (i = 0; i < cl_numsockets; ++i)
685         {
686                 if (!cl_sockets[i])
687                         continue;
688                 if (LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) != LHNETADDRESS_GetAddressType(&broadcastaddress))
689                         continue;
690
691                 if (querydp)
692                 {
693                         // search LAN for Quake servers
694                         SZ_Clear(&cl_message);
695                         // save space for the header, filled in later
696                         MSG_WriteLong(&cl_message, 0);
697                         MSG_WriteByte(&cl_message, CCREQ_SERVER_INFO);
698                         MSG_WriteString(&cl_message, "QUAKE");
699                         MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
700                         StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
701                         NetConn_Write(cl_sockets[i], cl_message.data, cl_message.cursize, &broadcastaddress);
702                         SZ_Clear(&cl_message);
703
704                         // search LAN for DarkPlaces servers
705                         NetConn_WriteString(cl_sockets[i], dpquery, &broadcastaddress);
706                 }
707
708                 if (queryqw)
709                         // search LAN for QuakeWorld servers
710                         NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &broadcastaddress);
711         }
712 }
713 #endif
714
715 // rest
716
717 int NetConn_Read(lhnetsocket_t *mysocket, void *data, int maxlength, lhnetaddress_t *peeraddress)
718 {
719         int length;
720         unsigned i;
721
722         if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
723                 Thread_LockMutex(netconn_mutex);
724         length = LHNET_Read(mysocket, data, maxlength, peeraddress);
725         if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
726                 Thread_UnlockMutex(netconn_mutex);
727         if (length == 0)
728                 return 0;
729         if (net_fakeloss_receive.integer)
730                 for (i = 0;i < cl_numsockets;i++)
731                         if (cl_sockets[i] == mysocket && (rand() % 100) < net_fakeloss_receive.integer)
732                                 return 0;
733         if (developer_networking.integer)
734         {
735                 char addressstring[128], addressstring2[128];
736                 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
737                 if (length > 0)
738                 {
739                         LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
740                         Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i from %s:\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length, addressstring2);
741                         Com_HexDumpToConsole((unsigned char *)data, length);
742                 }
743                 else
744                         Con_Printf("LHNET_Read(%p (%s), %p, %i, %p) = %i\n", (void *)mysocket, addressstring, (void *)data, maxlength, (void *)peeraddress, length);
745         }
746         return length;
747 }
748
749 int NetConn_Write(lhnetsocket_t *mysocket, const void *data, int length, const lhnetaddress_t *peeraddress)
750 {
751         int ret;
752         unsigned i;
753
754         if (net_fakeloss_send.integer)
755                 for (i = 0;i < cl_numsockets;i++)
756                         if (cl_sockets[i] == mysocket && (rand() % 100) < net_fakeloss_send.integer)
757                                 return length;
758         if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
759                 Thread_LockMutex(netconn_mutex);
760         ret = LHNET_Write(mysocket, data, length, peeraddress);
761         if (mysocket->address.addresstype == LHNETADDRESSTYPE_LOOP && netconn_mutex)
762                 Thread_UnlockMutex(netconn_mutex);
763         if (developer_networking.integer)
764         {
765                 char addressstring[128], addressstring2[128];
766                 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), addressstring, sizeof(addressstring), true);
767                 LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
768                 Con_Printf("LHNET_Write(%p (%s), %p, %i, %p (%s)) = %i%s\n", (void *)mysocket, addressstring, (void *)data, length, (void *)peeraddress, addressstring2, length, ret == length ? "" : " (ERROR)");
769                 Com_HexDumpToConsole((unsigned char *)data, length);
770         }
771         return ret;
772 }
773
774 int NetConn_WriteString(lhnetsocket_t *mysocket, const char *string, const lhnetaddress_t *peeraddress)
775 {
776         // note this does not include the trailing NULL because we add that in the parser
777         return NetConn_Write(mysocket, string, (int)strlen(string), peeraddress);
778 }
779
780 qbool NetConn_CanSend(netconn_t *conn)
781 {
782         conn->outgoing_packetcounter = (conn->outgoing_packetcounter + 1) % NETGRAPH_PACKETS;
783         conn->outgoing_netgraph[conn->outgoing_packetcounter].time            = host.realtime;
784         conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
785         conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes   = NETGRAPH_NOPACKET;
786         conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes        = NETGRAPH_NOPACKET;
787         conn->outgoing_netgraph[conn->outgoing_packetcounter].cleartime       = conn->cleartime;
788         if (host.realtime > conn->cleartime)
789                 return true;
790         else
791         {
792                 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_CHOKEDPACKET;
793                 return false;
794         }
795 }
796
797 static void NetConn_UpdateCleartime(double *cleartime, int rate, int burstsize, int len)
798 {
799         double bursttime = burstsize / (double)rate;
800
801         // delay later packets to obey rate limit
802         if (*cleartime < host.realtime - bursttime)
803                 *cleartime = host.realtime - bursttime;
804         *cleartime = *cleartime + len / (double)rate;
805
806         // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
807         if (net_test.integer)
808         {
809                 if (*cleartime < host.realtime)
810                         *cleartime = host.realtime;
811         }
812 }
813
814 static int NetConn_AddCryptoFlag(crypto_t *crypto)
815 {
816         // HACK: if an encrypted connection is used, randomly set some unused
817         // flags. When AES encryption is enabled, that will make resends differ
818         // from the original, so that e.g. substring filters in a router/IPS
819         // are unlikely to match a second time. See also "startkeylogger".
820         int flag = 0;
821         if (crypto->authenticated)
822         {
823                 // Let's always set at least one of the bits.
824                 int r = rand() % 7 + 1;
825                 if (r & 1)
826                         flag |= NETFLAG_CRYPTO0;
827                 if (r & 2)
828                         flag |= NETFLAG_CRYPTO1;
829                 if (r & 4)
830                         flag |= NETFLAG_CRYPTO2;
831         }
832         return flag;
833 }
834
835 int NetConn_SendUnreliableMessage(netconn_t *conn, sizebuf_t *data, protocolversion_t protocol, int rate, int burstsize, qbool quakesignon_suppressreliables)
836 {
837         int totallen = 0;
838         unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
839         unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
840
841         // if this packet was supposedly choked, but we find ourselves sending one
842         // anyway, make sure the size counting starts at zero
843         // (this mostly happens on level changes and disconnects and such)
844         if (conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes == NETGRAPH_CHOKEDPACKET)
845                 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes = NETGRAPH_NOPACKET;
846
847         conn->outgoing_netgraph[conn->outgoing_packetcounter].cleartime = conn->cleartime;
848
849         if (protocol == PROTOCOL_QUAKEWORLD)
850         {
851                 int packetLen;
852                 qbool sendreliable;
853
854                 // note that it is ok to send empty messages to the qw server,
855                 // otherwise it won't respond to us at all
856
857                 sendreliable = false;
858                 // if the remote side dropped the last reliable message, resend it
859                 if (conn->qw.incoming_acknowledged > conn->qw.last_reliable_sequence && conn->qw.incoming_reliable_acknowledged != conn->qw.reliable_sequence)
860                         sendreliable = true;
861                 // if the reliable transmit buffer is empty, copy the current message out
862                 if (!conn->sendMessageLength && conn->message.cursize)
863                 {
864                         memcpy (conn->sendMessage, conn->message.data, conn->message.cursize);
865                         conn->sendMessageLength = conn->message.cursize;
866                         SZ_Clear(&conn->message); // clear the message buffer
867                         conn->qw.reliable_sequence ^= 1;
868                         sendreliable = true;
869                 }
870                 // outgoing unreliable packet number, and outgoing reliable packet number (0 or 1)
871                 StoreLittleLong(sendbuffer, conn->outgoing_unreliable_sequence | (((unsigned int)sendreliable)<<31));
872                 // last received unreliable packet number, and last received reliable packet number (0 or 1)
873                 StoreLittleLong(sendbuffer + 4, conn->qw.incoming_sequence | (((unsigned int)conn->qw.incoming_reliable_sequence)<<31));
874                 packetLen = 8;
875                 conn->outgoing_unreliable_sequence++;
876                 // client sends qport in every packet
877                 if (conn == cls.netcon)
878                 {
879                         *((short *)(sendbuffer + 8)) = LittleShort(cls.qw_qport);
880                         packetLen += 2;
881                         // also update cls.qw_outgoing_sequence
882                         cls.qw_outgoing_sequence = conn->outgoing_unreliable_sequence;
883                 }
884                 if (packetLen + (sendreliable ? conn->sendMessageLength : 0) > 1400)
885                 {
886                         Con_Printf ("NetConn_SendUnreliableMessage: reliable message too big %u\n", data->cursize);
887                         return -1;
888                 }
889
890                 conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
891
892                 // add the reliable message if there is one
893                 if (sendreliable)
894                 {
895                         conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += conn->sendMessageLength + 28;
896                         memcpy(sendbuffer + packetLen, conn->sendMessage, conn->sendMessageLength);
897                         packetLen += conn->sendMessageLength;
898                         conn->qw.last_reliable_sequence = conn->outgoing_unreliable_sequence;
899                 }
900
901                 // add the unreliable message if possible
902                 if (packetLen + data->cursize <= 1400)
903                 {
904                         conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += data->cursize + 28;
905                         memcpy(sendbuffer + packetLen, data->data, data->cursize);
906                         packetLen += data->cursize;
907                 }
908
909                 NetConn_Write(conn->mysocket, (void *)&sendbuffer, packetLen, &conn->peeraddress);
910
911                 conn->packetsSent++;
912                 conn->unreliableMessagesSent++;
913
914                 totallen += packetLen + 28;
915         }
916         else
917         {
918                 unsigned int packetLen;
919                 unsigned int dataLen;
920                 unsigned int eom;
921                 const void *sendme;
922                 size_t sendmelen;
923
924                 // if a reliable message fragment has been lost, send it again
925                 if (conn->sendMessageLength && (host.realtime - conn->lastSendTime) > 1.0)
926                 {
927                         if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
928                         {
929                                 dataLen = conn->sendMessageLength;
930                                 eom = NETFLAG_EOM;
931                         }
932                         else
933                         {
934                                 dataLen = MAX_PACKETFRAGMENT;
935                                 eom = 0;
936                         }
937
938                         packetLen = NET_HEADERSIZE + dataLen;
939
940                         StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
941                         StoreBigLong(sendbuffer + 4, conn->nq.sendSequence - 1);
942                         memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
943
944                         conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
945
946                         sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
947                         if (sendme && NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress) == (int)sendmelen)
948                         {
949                                 conn->lastSendTime = host.realtime;
950                                 conn->packetsReSent++;
951                         }
952
953                         totallen += (int)sendmelen + 28;
954                 }
955
956                 // if we have a new reliable message to send, do so
957                 if (!conn->sendMessageLength && conn->message.cursize && !quakesignon_suppressreliables)
958                 {
959                         if (conn->message.cursize > (int)sizeof(conn->sendMessage))
960                         {
961                                 Con_Printf("NetConn_SendUnreliableMessage: reliable message too big (%u > %u)\n", conn->message.cursize, (int)sizeof(conn->sendMessage));
962                                 conn->message.overflowed = true;
963                                 return -1;
964                         }
965
966                         if (developer_networking.integer && conn == cls.netcon)
967                         {
968                                 Con_Print("client sending reliable message to server:\n");
969                                 SZ_HexDumpToConsole(&conn->message);
970                         }
971
972                         memcpy(conn->sendMessage, conn->message.data, conn->message.cursize);
973                         conn->sendMessageLength = conn->message.cursize;
974                         SZ_Clear(&conn->message);
975
976                         if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
977                         {
978                                 dataLen = conn->sendMessageLength;
979                                 eom = NETFLAG_EOM;
980                         }
981                         else
982                         {
983                                 dataLen = MAX_PACKETFRAGMENT;
984                                 eom = 0;
985                         }
986
987                         packetLen = NET_HEADERSIZE + dataLen;
988
989                         StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
990                         StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
991                         memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
992
993                         conn->nq.sendSequence++;
994
995                         conn->outgoing_netgraph[conn->outgoing_packetcounter].reliablebytes += packetLen + 28;
996
997                         sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
998                         if(sendme)
999                                 NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
1000
1001                         conn->lastSendTime = host.realtime;
1002                         conn->packetsSent++;
1003                         conn->reliableMessagesSent++;
1004
1005                         totallen += (int)sendmelen + 28;
1006                 }
1007
1008                 // if we have an unreliable message to send, do so
1009                 if (data->cursize)
1010                 {
1011                         packetLen = NET_HEADERSIZE + data->cursize;
1012
1013                         if (packetLen > (int)sizeof(sendbuffer))
1014                         {
1015                                 Con_Printf("NetConn_SendUnreliableMessage: message too big %u\n", data->cursize);
1016                                 return -1;
1017                         }
1018
1019                         StoreBigLong(sendbuffer, packetLen | NETFLAG_UNRELIABLE | NetConn_AddCryptoFlag(&conn->crypto));
1020                         StoreBigLong(sendbuffer + 4, conn->outgoing_unreliable_sequence);
1021                         memcpy(sendbuffer + NET_HEADERSIZE, data->data, data->cursize);
1022
1023                         conn->outgoing_unreliable_sequence++;
1024
1025                         conn->outgoing_netgraph[conn->outgoing_packetcounter].unreliablebytes += packetLen + 28;
1026
1027                         sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1028                         if(sendme)
1029                                 NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
1030
1031                         conn->packetsSent++;
1032                         conn->unreliableMessagesSent++;
1033
1034                         totallen += (int)sendmelen + 28;
1035                 }
1036         }
1037
1038         NetConn_UpdateCleartime(&conn->cleartime, rate, burstsize, totallen);
1039
1040         return 0;
1041 }
1042
1043 qbool NetConn_HaveClientPorts(void)
1044 {
1045         return !!cl_numsockets;
1046 }
1047
1048 qbool NetConn_HaveServerPorts(void)
1049 {
1050         return !!sv_numsockets;
1051 }
1052
1053 void NetConn_CloseClientPorts(void)
1054 {
1055         for (;cl_numsockets > 0;cl_numsockets--)
1056                 if (cl_sockets[cl_numsockets - 1])
1057                         LHNET_CloseSocket(cl_sockets[cl_numsockets - 1]);
1058 }
1059
1060 static void NetConn_OpenClientPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport)
1061 {
1062         lhnetaddress_t address;
1063         lhnetsocket_t *s;
1064         int success;
1065         char addressstring2[1024];
1066         if (addressstring && addressstring[0])
1067                 success = LHNETADDRESS_FromString(&address, addressstring, defaultport);
1068         else
1069                 success = LHNETADDRESS_FromPort(&address, addresstype, defaultport);
1070         if (success)
1071         {
1072                 if ((s = LHNET_OpenSocket_Connectionless(&address)))
1073                 {
1074                         cl_sockets[cl_numsockets++] = s;
1075                         LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
1076                         if (addresstype != LHNETADDRESSTYPE_LOOP)
1077                                 Con_Printf("Client opened a socket on address %s\n", addressstring2);
1078                 }
1079                 else
1080                 {
1081                         LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
1082                         Con_Printf(CON_ERROR "Client failed to open a socket on address %s\n", addressstring2);
1083                 }
1084         }
1085         else
1086                 Con_Printf(CON_ERROR "Client unable to parse address %s\n", addressstring);
1087 }
1088
1089 void NetConn_OpenClientPorts(void)
1090 {
1091         int port;
1092         NetConn_CloseClientPorts();
1093
1094         SV_LockThreadMutex(); // FIXME recursive?
1095         Crypto_LoadKeys(); // client sockets
1096         SV_UnlockThreadMutex();
1097
1098         port = bound(0, cl_netport.integer, 65535);
1099         if (cl_netport.integer != port)
1100                 Cvar_SetValueQuick(&cl_netport, port);
1101         if(port == 0)
1102                 Con_Printf("Client using an automatically assigned port\n");
1103         else
1104                 Con_Printf("Client using port %i\n", port);
1105         NetConn_OpenClientPort(NULL, LHNETADDRESSTYPE_LOOP, 2);
1106         NetConn_OpenClientPort(net_address.string, LHNETADDRESSTYPE_INET4, port);
1107 #ifndef NOSUPPORTIPV6
1108         NetConn_OpenClientPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port);
1109 #endif
1110 }
1111
1112 void NetConn_CloseServerPorts(void)
1113 {
1114         for (;sv_numsockets > 0;sv_numsockets--)
1115                 if (sv_sockets[sv_numsockets - 1])
1116                         LHNET_CloseSocket(sv_sockets[sv_numsockets - 1]);
1117 }
1118
1119 static qbool NetConn_OpenServerPort(const char *addressstring, lhnetaddresstype_t addresstype, int defaultport, int range)
1120 {
1121         lhnetaddress_t address;
1122         lhnetsocket_t *s;
1123         int port;
1124         char addressstring2[1024];
1125         int success;
1126
1127         for (port = defaultport; port <= defaultport + range; port++)
1128         {
1129                 if (addressstring && addressstring[0])
1130                         success = LHNETADDRESS_FromString(&address, addressstring, port);
1131                 else
1132                         success = LHNETADDRESS_FromPort(&address, addresstype, port);
1133                 if (success)
1134                 {
1135                         if ((s = LHNET_OpenSocket_Connectionless(&address)))
1136                         {
1137                                 sv_sockets[sv_numsockets++] = s;
1138                                 LHNETADDRESS_ToString(LHNET_AddressFromSocket(s), addressstring2, sizeof(addressstring2), true);
1139                                 if (addresstype != LHNETADDRESSTYPE_LOOP)
1140                                         Con_Printf("Server listening on address %s\n", addressstring2);
1141                                 return true;
1142                         }
1143                         else
1144                         {
1145                                 LHNETADDRESS_ToString(&address, addressstring2, sizeof(addressstring2), true);
1146                                 Con_Printf(CON_ERROR "Server failed to open socket on address %s\n", addressstring2);
1147                         }
1148                 }
1149                 else
1150                 {
1151                         Con_Printf(CON_ERROR "Server unable to parse address %s\n", addressstring);
1152                         // if it cant parse one address, it wont be able to parse another for sure
1153                         return false;
1154                 }
1155         }
1156         return false;
1157 }
1158
1159 void NetConn_OpenServerPorts(int opennetports)
1160 {
1161         int port;
1162         NetConn_CloseServerPorts();
1163
1164         SV_LockThreadMutex(); // FIXME recursive?
1165         Crypto_LoadKeys(); // server sockets
1166         SV_UnlockThreadMutex();
1167
1168         NetConn_UpdateSockets();
1169         port = bound(0, sv_netport.integer, 65535);
1170         if (port == 0)
1171                 port = 26000;
1172         if (sv_netport.integer != port)
1173                 Cvar_SetValueQuick(&sv_netport, port);
1174         if (cls.state != ca_dedicated)
1175                 NetConn_OpenServerPort(NULL, LHNETADDRESSTYPE_LOOP, 1, 1);
1176         if (opennetports)
1177         {
1178 #ifndef NOSUPPORTIPV6
1179                 qbool ip4success = NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1180                 NetConn_OpenServerPort(net_address_ipv6.string, LHNETADDRESSTYPE_INET6, port, ip4success ? 1 : 100);
1181 #else
1182                 NetConn_OpenServerPort(net_address.string, LHNETADDRESSTYPE_INET4, port, 100);
1183 #endif
1184         }
1185         if (sv_numsockets == 0)
1186                 Host_Error("NetConn_OpenServerPorts: unable to open any ports!");
1187 }
1188
1189 lhnetsocket_t *NetConn_ChooseClientSocketForAddress(lhnetaddress_t *address)
1190 {
1191         unsigned i;
1192         lhnetaddresstype_t a = LHNETADDRESS_GetAddressType(address);
1193
1194         for (i = 0;i < cl_numsockets;i++)
1195                 if (cl_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == a)
1196                         return cl_sockets[i];
1197         return NULL;
1198 }
1199
1200 lhnetsocket_t *NetConn_ChooseServerSocketForAddress(lhnetaddress_t *address)
1201 {
1202         unsigned i;
1203         lhnetaddresstype_t a = LHNETADDRESS_GetAddressType(address);
1204
1205         for (i = 0;i < sv_numsockets;i++)
1206                 if (sv_sockets[i] && LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(sv_sockets[i])) == a)
1207                         return sv_sockets[i];
1208         return NULL;
1209 }
1210
1211 netconn_t *NetConn_Open(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress)
1212 {
1213         netconn_t *conn;
1214         conn = (netconn_t *)Mem_Alloc(netconn_mempool, sizeof(*conn));
1215         conn->mysocket = mysocket;
1216         conn->peeraddress = *peeraddress;
1217         conn->lastMessageTime = host.realtime;
1218         conn->message.data = conn->messagedata;
1219         conn->message.maxsize = sizeof(conn->messagedata);
1220         conn->message.cursize = 0;
1221         // LadyHavoc: (inspired by ProQuake) use a short connect timeout to
1222         // reduce effectiveness of connection request floods
1223         conn->timeout = host.realtime + net_connecttimeout.value;
1224         LHNETADDRESS_ToString(&conn->peeraddress, conn->address, sizeof(conn->address), true);
1225         conn->next = netconn_list;
1226         netconn_list = conn;
1227         return conn;
1228 }
1229
1230 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength);
1231 void NetConn_Close(netconn_t *conn)
1232 {
1233         netconn_t *c;
1234         // remove connection from list
1235
1236         // allow the client to reconnect immediately
1237         NetConn_ClearFlood(&(conn->peeraddress), sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]));
1238
1239         if (conn == netconn_list)
1240                 netconn_list = conn->next;
1241         else
1242         {
1243                 for (c = netconn_list;c;c = c->next)
1244                 {
1245                         if (c->next == conn)
1246                         {
1247                                 c->next = conn->next;
1248                                 break;
1249                         }
1250                 }
1251                 // not found in list, we'll avoid crashing here...
1252                 if (!c)
1253                         return;
1254         }
1255         // free connection
1256         Mem_Free(conn);
1257 }
1258
1259 static int clientport = -1;
1260 static int clientport2 = -1;
1261
1262 // Call on disconnect, during startup, or if cl_port/cl_netport is changed
1263 static void NetConn_CL_UpdateSockets_Callback(cvar_t *var)
1264 {
1265         if(cls.state != ca_dedicated)
1266         {
1267                 if (clientport2 != var->integer)
1268                 {
1269                         clientport2 = var->integer;
1270                         if (cls.state == ca_connected)
1271                                 Con_Print("Changing \"cl_port\" will not take effect until you reconnect.\n");
1272                 }
1273
1274                 if (cls.state == ca_disconnected && clientport != clientport2)
1275                 {
1276                         clientport = clientport2;
1277                         NetConn_CloseClientPorts();
1278                 }
1279                 if (cl_numsockets == 0)
1280                         NetConn_OpenClientPorts();
1281         }
1282 }
1283
1284 static int hostport = -1;
1285
1286 // Call when port/sv_netport is changed
1287 static void NetConn_sv_netport_Callback(cvar_t *var)
1288 {
1289         if (hostport != var->integer)
1290         {
1291                 hostport = var->integer;
1292                 if (sv.active)
1293                         Con_Print("Changing \"port\" will not take effect until \"map\" command is executed.\n");
1294         }
1295 }
1296
1297 void NetConn_UpdateSockets(void)
1298 {
1299         int i, j;
1300
1301         // TODO add logic to automatically close sockets if needed
1302         LHNET_DefaultDSCP(net_tos_dscp.integer);
1303
1304         for (j = 0;j < MAX_RCONS;j++)
1305         {
1306                 i = (cls.rcon_ringpos + j + 1) % MAX_RCONS;
1307                 if(cls.rcon_commands[i][0])
1308                 {
1309                         if(host.realtime > cls.rcon_timeout[i])
1310                         {
1311                                 char s[128];
1312                                 LHNETADDRESS_ToString(&cls.rcon_addresses[i], s, sizeof(s), true);
1313                                 Con_Printf("rcon to %s (for command %s) failed: challenge request timed out\n", s, cls.rcon_commands[i]);
1314                                 cls.rcon_commands[i][0] = 0;
1315                                 --cls.rcon_trying;
1316                                 break;
1317                         }
1318                 }
1319         }
1320 }
1321
1322 static int NetConn_ReceivedMessage(netconn_t *conn, const unsigned char *data, size_t length, protocolversion_t protocol, double newtimeout)
1323 {
1324         int originallength = (int)length;
1325         unsigned char sendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
1326         unsigned char cryptosendbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1327         unsigned char cryptoreadbuffer[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
1328         if (length < 8)
1329                 return 0;
1330
1331         if (protocol == PROTOCOL_QUAKEWORLD)
1332         {
1333                 unsigned int sequence, sequence_ack;
1334                 qbool reliable_ack, reliable_message;
1335                 int count;
1336                 //int qport;
1337
1338                 sequence = LittleLong(*((int *)(data + 0)));
1339                 sequence_ack = LittleLong(*((int *)(data + 4)));
1340                 data += 8;
1341                 length -= 8;
1342
1343                 if (conn != cls.netcon)
1344                 {
1345                         // server only
1346                         if (length < 2)
1347                                 return 0;
1348                         // 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?)
1349                         //qport = LittleShort(*((int *)(data + 8)));
1350                         data += 2;
1351                         length -= 2;
1352                 }
1353
1354                 conn->packetsReceived++;
1355                 reliable_message = (sequence >> 31) != 0;
1356                 reliable_ack = (sequence_ack >> 31) != 0;
1357                 sequence &= ~(1<<31);
1358                 sequence_ack &= ~(1<<31);
1359                 if (sequence <= conn->qw.incoming_sequence)
1360                 {
1361                         //Con_DPrint("Got a stale datagram\n");
1362                         return 0;
1363                 }
1364                 count = sequence - (conn->qw.incoming_sequence + 1);
1365                 if (count > 0)
1366                 {
1367                         conn->droppedDatagrams += count;
1368                         //Con_DPrintf("Dropped %u datagram(s)\n", count);
1369                         // If too may packets have been dropped, only write the
1370                         // last NETGRAPH_PACKETS ones to the netgraph. Why?
1371                         // Because there's no point in writing more than
1372                         // these as the netgraph is going to be full anyway.
1373                         if (count > NETGRAPH_PACKETS)
1374                                 count = NETGRAPH_PACKETS;
1375                         while (count--)
1376                         {
1377                                 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1378                                 conn->incoming_netgraph[conn->incoming_packetcounter].time            = host.realtime;
1379                                 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime       = conn->incoming_cleartime;
1380                                 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1381                                 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes   = NETGRAPH_NOPACKET;
1382                                 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes        = NETGRAPH_NOPACKET;
1383                         }
1384                 }
1385                 conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1386                 conn->incoming_netgraph[conn->incoming_packetcounter].time            = host.realtime;
1387                 conn->incoming_netgraph[conn->incoming_packetcounter].cleartime       = conn->incoming_cleartime;
1388                 conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1389                 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes   = NETGRAPH_NOPACKET;
1390                 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes        = NETGRAPH_NOPACKET;
1391                 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1392
1393                 // limit bursts to one packet in size ("dialup mode" emulating old behaviour)
1394                 if (net_test.integer)
1395                 {
1396                         if (conn->cleartime < host.realtime)
1397                                 conn->cleartime = host.realtime;
1398                 }
1399
1400                 if (reliable_ack == conn->qw.reliable_sequence)
1401                 {
1402                         // received, now we will be able to send another reliable message
1403                         conn->sendMessageLength = 0;
1404                         conn->reliableMessagesReceived++;
1405                 }
1406                 conn->qw.incoming_sequence = sequence;
1407                 if (conn == cls.netcon)
1408                         cls.qw_incoming_sequence = conn->qw.incoming_sequence;
1409                 conn->qw.incoming_acknowledged = sequence_ack;
1410                 conn->qw.incoming_reliable_acknowledged = reliable_ack;
1411                 if (reliable_message)
1412                         conn->qw.incoming_reliable_sequence ^= 1;
1413                 conn->lastMessageTime = host.realtime;
1414                 conn->timeout = host.realtime + newtimeout;
1415                 conn->unreliableMessagesReceived++;
1416                 if (conn == cls.netcon)
1417                 {
1418                         SZ_Clear(&cl_message);
1419                         SZ_Write(&cl_message, data, (int)length);
1420                         MSG_BeginReading(&cl_message);
1421                 }
1422                 else
1423                 {
1424                         SZ_Clear(&sv_message);
1425                         SZ_Write(&sv_message, data, (int)length);
1426                         MSG_BeginReading(&sv_message);
1427                 }
1428                 return 2;
1429         }
1430         else
1431         {
1432                 unsigned int count;
1433                 unsigned int flags;
1434                 unsigned int sequence;
1435                 size_t qlength;
1436                 const void *sendme;
1437                 size_t sendmelen;
1438
1439                 originallength = (int)length;
1440                 data = (const unsigned char *) Crypto_DecryptPacket(&conn->crypto, data, length, cryptoreadbuffer, &length, sizeof(cryptoreadbuffer));
1441                 if(!data)
1442                         return 0;
1443                 if(length < 8)
1444                         return 0;
1445
1446                 qlength = (unsigned int)BuffBigLong(data);
1447                 flags = qlength & ~NETFLAG_LENGTH_MASK;
1448                 qlength &= NETFLAG_LENGTH_MASK;
1449                 // control packets were already handled
1450                 if (!(flags & NETFLAG_CTL) && qlength == length)
1451                 {
1452                         sequence = BuffBigLong(data + 4);
1453                         conn->packetsReceived++;
1454                         data += 8;
1455                         length -= 8;
1456                         if (flags & NETFLAG_UNRELIABLE)
1457                         {
1458                                 if (sequence >= conn->nq.unreliableReceiveSequence)
1459                                 {
1460                                         if (sequence > conn->nq.unreliableReceiveSequence)
1461                                         {
1462                                                 count = sequence - conn->nq.unreliableReceiveSequence;
1463                                                 conn->droppedDatagrams += count;
1464                                                 //Con_DPrintf("Dropped %u datagram(s)\n", count);
1465                                                 // If too may packets have been dropped, only write the
1466                                                 // last NETGRAPH_PACKETS ones to the netgraph. Why?
1467                                                 // Because there's no point in writing more than
1468                                                 // these as the netgraph is going to be full anyway.
1469                                                 if (count > NETGRAPH_PACKETS)
1470                                                         count = NETGRAPH_PACKETS;
1471                                                 while (count--)
1472                                                 {
1473                                                         conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1474                                                         conn->incoming_netgraph[conn->incoming_packetcounter].time            = host.realtime;
1475                                                         conn->incoming_netgraph[conn->incoming_packetcounter].cleartime       = conn->incoming_cleartime;
1476                                                         conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = NETGRAPH_LOSTPACKET;
1477                                                         conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes   = NETGRAPH_NOPACKET;
1478                                                         conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes        = NETGRAPH_NOPACKET;
1479                                                 }
1480                                         }
1481                                         conn->incoming_packetcounter = (conn->incoming_packetcounter + 1) % NETGRAPH_PACKETS;
1482                                         conn->incoming_netgraph[conn->incoming_packetcounter].time            = host.realtime;
1483                                         conn->incoming_netgraph[conn->incoming_packetcounter].cleartime       = conn->incoming_cleartime;
1484                                         conn->incoming_netgraph[conn->incoming_packetcounter].unreliablebytes = originallength + 28;
1485                                         conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes   = NETGRAPH_NOPACKET;
1486                                         conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes        = NETGRAPH_NOPACKET;
1487                                         NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1488
1489                                         conn->nq.unreliableReceiveSequence = sequence + 1;
1490                                         conn->lastMessageTime = host.realtime;
1491                                         conn->timeout = host.realtime + newtimeout;
1492                                         conn->unreliableMessagesReceived++;
1493                                         if (length > 0)
1494                                         {
1495                                                 if (conn == cls.netcon)
1496                                                 {
1497                                                         SZ_Clear(&cl_message);
1498                                                         SZ_Write(&cl_message, data, (int)length);
1499                                                         MSG_BeginReading(&cl_message);
1500                                                 }
1501                                                 else
1502                                                 {
1503                                                         SZ_Clear(&sv_message);
1504                                                         SZ_Write(&sv_message, data, (int)length);
1505                                                         MSG_BeginReading(&sv_message);
1506                                                 }
1507                                                 return 2;
1508                                         }
1509                                 }
1510                                 //else
1511                                 //      Con_DPrint("Got a stale datagram\n");
1512                                 return 1;
1513                         }
1514                         else if (flags & NETFLAG_ACK)
1515                         {
1516                                 conn->incoming_netgraph[conn->incoming_packetcounter].ackbytes += originallength + 28;
1517                                 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1518
1519                                 if (sequence == (conn->nq.sendSequence - 1))
1520                                 {
1521                                         if (sequence == conn->nq.ackSequence)
1522                                         {
1523                                                 conn->nq.ackSequence++;
1524                                                 if (conn->nq.ackSequence != conn->nq.sendSequence)
1525                                                         Con_DPrint("ack sequencing error\n");
1526                                                 conn->lastMessageTime = host.realtime;
1527                                                 conn->timeout = host.realtime + newtimeout;
1528                                                 if (conn->sendMessageLength > MAX_PACKETFRAGMENT)
1529                                                 {
1530                                                         unsigned int packetLen;
1531                                                         unsigned int dataLen;
1532                                                         unsigned int eom;
1533
1534                                                         conn->sendMessageLength -= MAX_PACKETFRAGMENT;
1535                                                         memmove(conn->sendMessage, conn->sendMessage+MAX_PACKETFRAGMENT, conn->sendMessageLength);
1536
1537                                                         if (conn->sendMessageLength <= MAX_PACKETFRAGMENT)
1538                                                         {
1539                                                                 dataLen = conn->sendMessageLength;
1540                                                                 eom = NETFLAG_EOM;
1541                                                         }
1542                                                         else
1543                                                         {
1544                                                                 dataLen = MAX_PACKETFRAGMENT;
1545                                                                 eom = 0;
1546                                                         }
1547
1548                                                         packetLen = NET_HEADERSIZE + dataLen;
1549
1550                                                         StoreBigLong(sendbuffer, packetLen | (NETFLAG_DATA | eom | NetConn_AddCryptoFlag(&conn->crypto)));
1551                                                         StoreBigLong(sendbuffer + 4, conn->nq.sendSequence);
1552                                                         memcpy(sendbuffer + NET_HEADERSIZE, conn->sendMessage, dataLen);
1553
1554                                                         conn->nq.sendSequence++;
1555
1556                                                         sendme = Crypto_EncryptPacket(&conn->crypto, &sendbuffer, packetLen, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1557                                                         if (sendme && NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress) == (int)sendmelen)
1558                                                         {
1559                                                                 conn->lastSendTime = host.realtime;
1560                                                                 conn->packetsSent++;
1561                                                         }
1562                                                 }
1563                                                 else
1564                                                         conn->sendMessageLength = 0;
1565                                         }
1566                                         //else
1567                                         //      Con_DPrint("Duplicate ACK received\n");
1568                                 }
1569                                 //else
1570                                 //      Con_DPrint("Stale ACK received\n");
1571                                 return 1;
1572                         }
1573                         else if (flags & NETFLAG_DATA)
1574                         {
1575                                 unsigned char temppacket[8];
1576                                 conn->incoming_netgraph[conn->incoming_packetcounter].reliablebytes   += originallength + 28;
1577                                 NetConn_UpdateCleartime(&conn->incoming_cleartime, cl_rate.integer, cl_rate_burstsize.integer, originallength + 28);
1578
1579                                 conn->outgoing_netgraph[conn->outgoing_packetcounter].ackbytes        += 8 + 28;
1580
1581                                 StoreBigLong(temppacket, 8 | NETFLAG_ACK | NetConn_AddCryptoFlag(&conn->crypto));
1582                                 StoreBigLong(temppacket + 4, sequence);
1583                                 sendme = Crypto_EncryptPacket(&conn->crypto, temppacket, 8, &cryptosendbuffer, &sendmelen, sizeof(cryptosendbuffer));
1584                                 if(sendme)
1585                                         NetConn_Write(conn->mysocket, sendme, (int)sendmelen, &conn->peeraddress);
1586                                 if (sequence == conn->nq.receiveSequence)
1587                                 {
1588                                         conn->lastMessageTime = host.realtime;
1589                                         conn->timeout = host.realtime + newtimeout;
1590                                         conn->nq.receiveSequence++;
1591                                         if( conn->receiveMessageLength + length <= (int)sizeof( conn->receiveMessage ) ) {
1592                                                 memcpy(conn->receiveMessage + conn->receiveMessageLength, data, length);
1593                                                 conn->receiveMessageLength += (int)length;
1594                                         } else {
1595                                                 Con_Printf( "Reliable message (seq: %i) too big for message buffer!\n"
1596                                                                         "Dropping the message!\n", sequence );
1597                                                 conn->receiveMessageLength = 0;
1598                                                 return 1;
1599                                         }
1600                                         if (flags & NETFLAG_EOM)
1601                                         {
1602                                                 conn->reliableMessagesReceived++;
1603                                                 length = conn->receiveMessageLength;
1604                                                 conn->receiveMessageLength = 0;
1605                                                 if (length > 0)
1606                                                 {
1607                                                         if (conn == cls.netcon)
1608                                                         {
1609                                                                 SZ_Clear(&cl_message);
1610                                                                 SZ_Write(&cl_message, conn->receiveMessage, (int)length);
1611                                                                 MSG_BeginReading(&cl_message);
1612                                                         }
1613                                                         else
1614                                                         {
1615                                                                 SZ_Clear(&sv_message);
1616                                                                 SZ_Write(&sv_message, conn->receiveMessage, (int)length);
1617                                                                 MSG_BeginReading(&sv_message);
1618                                                         }
1619                                                         return 2;
1620                                                 }
1621                                         }
1622                                 }
1623                                 else
1624                                         conn->receivedDuplicateCount++;
1625                                 return 1;
1626                         }
1627                 }
1628         }
1629         return 0;
1630 }
1631
1632 static void NetConn_ConnectionEstablished(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, protocolversion_t initialprotocol)
1633 {
1634         crypto_t *crypto;
1635
1636         cls.connect_trying = false;
1637         // Disconnect from the current server or stop demo playback
1638         if(cls.state == ca_connected || cls.demoplayback)
1639                 CL_Disconnect();
1640         // allocate a net connection to keep track of things
1641         cls.netcon = NetConn_Open(mysocket, peeraddress);
1642         strlcpy(cl_connect_status, "Connection established", sizeof(cl_connect_status));
1643         crypto = &cls.netcon->crypto;
1644         if(cls.crypto.authenticated)
1645         {
1646                 Crypto_FinishInstance(crypto, &cls.crypto);
1647                 Con_Printf("%s connection to %s has been established: server is %s@%s%.*s, I am %.*s@%s%.*s\n",
1648                                 crypto->use_aes ? "Encrypted" : "Authenticated",
1649                                 cls.netcon->address,
1650                                 crypto->server_idfp[0] ? crypto->server_idfp : "-",
1651                                 (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
1652                                 crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-",
1653                                 crypto_keyfp_recommended_length, crypto->client_idfp[0] ? crypto->client_idfp : "-",
1654                                 (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
1655                                 crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-"
1656                                 );
1657         }
1658         else
1659                 Con_Printf("%s to %s\n", cl_connect_status, cls.netcon->address);
1660
1661         key_dest = key_game;
1662 #ifdef CONFIG_MENU
1663         m_state = m_none;
1664 #endif
1665         cls.demonum = -1;                       // not in the demo loop now
1666         cls.state = ca_connected;
1667         cls.signon = 0;                         // need all the signon messages before playing
1668         cls.protocol = initialprotocol;
1669         // reset move sequence numbering on this new connection
1670         cls.servermovesequence = 0;
1671         if (cls.protocol == PROTOCOL_QUAKEWORLD)
1672                 CL_ForwardToServer("new");
1673         if (cls.protocol == PROTOCOL_QUAKE)
1674         {
1675                 // write a keepalive (clc_nop) as it seems to greatly improve the
1676                 // chances of connecting to a netquake server
1677                 sizebuf_t msg;
1678                 unsigned char buf[4];
1679                 memset(&msg, 0, sizeof(msg));
1680                 msg.data = buf;
1681                 msg.maxsize = sizeof(buf);
1682                 MSG_WriteChar(&msg, clc_nop);
1683                 NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, 0, false);
1684         }
1685 }
1686
1687 int NetConn_IsLocalGame(void)
1688 {
1689         if (cls.state == ca_connected && sv.active && cl.maxclients == 1)
1690                 return true;
1691         return false;
1692 }
1693
1694 #ifdef CONFIG_MENU
1695 static qbool hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen);
1696 static int NetConn_ClientParsePacket_ServerList_ProcessReply(const char *addressstring, const char *challenge)
1697 {
1698         unsigned n;
1699         float ping;
1700         double currentrealtime;
1701         serverlist_entry_t *entry;
1702
1703         // search the cache for this server and update it
1704         for (n = 0; n < serverlist_cachecount; ++n)
1705         {
1706                 entry = &serverlist_cache[n];
1707                 if (!strcmp(addressstring, entry->info.cname))
1708                         break;
1709         }
1710
1711         if (n == serverlist_cachecount)
1712         {
1713                 if (net_slist_debug.integer)
1714                         Con_Printf("^6Received LAN broadcast response from %s\n", addressstring);
1715
1716                 // find a slot
1717                 if (serverlist_cachecount == SERVERLIST_TOTALSIZE)
1718                         return -1;
1719
1720                 if (serverlist_maxcachecount <= serverlist_cachecount)
1721                 {
1722                         serverlist_maxcachecount += 64;
1723                         serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1724                 }
1725                 ++serverlist_cachecount;
1726                 entry = &serverlist_cache[n];
1727
1728                 memset(entry, 0, sizeof(*entry));
1729                 strlcpy(entry->info.cname, addressstring, sizeof(entry->info.cname));
1730
1731                 // consider the broadcast to be the first query
1732                 // NetConn_QueryQueueFrame() will perform more until net_slist_maxtries is reached
1733                 entry->querycounter = 1;
1734                 entry->querytime = masterquerytime;
1735                 // protocol is one of these at all
1736                 // NetConn_ClientParsePacket_ServerList_PrepareQuery() callsites
1737                 entry->protocol = challenge ? PROTOCOL_DARKPLACES7 : PROTOCOL_QUAKEWORLD;
1738
1739                 if (serverlist_consoleoutput)
1740                         Con_Printf("querying %s\n", addressstring);
1741         }
1742
1743         // If the client stalls partway through a frame (test command: `alias a a;a`)
1744         // for correct pings we need to know what host.realtime would be if it were updated now.
1745         currentrealtime = host.realtime + (Sys_DirtyTime() - host.dirtytime);
1746
1747         if (challenge)
1748         {
1749                 unsigned char hash[24]; // 4*(16/3) rounded up to 4 byte multiple
1750                 uint64_t timestamp = strtoull(&challenge[22], NULL, 16);
1751
1752                 HMAC_MDFOUR_16BYTES(hash,
1753                         (unsigned char *)&timestamp, sizeof(timestamp),
1754                         (unsigned char *)serverlist_dpserverquerykey, sizeof(serverlist_dpserverquerykey));
1755                 base64_encode(hash, 16, sizeof(hash));
1756                 if (memcmp(hash, challenge, 22) != 0)
1757                         return -1;
1758
1759                 ping = currentrealtime * 1000.0 - timestamp;
1760         }
1761         else
1762                 ping = 1000 * (currentrealtime - entry->querytime);
1763
1764         if (ping <= 0 || ping > net_slist_maxping.value
1765         || (entry->info.ping && ping > entry->info.ping + 100)) // server loading map, client stall, etc
1766                 return -1;
1767
1768         // never round down to 0, 0 latency is impossible, 0 means no data available
1769         if (ping < 1)
1770                 ping = 1;
1771
1772         if (entry->info.ping)
1773                 entry->info.ping = (entry->info.ping + ping) * 0.5 + 0.5; // "average" biased toward most recent results
1774         else
1775         {
1776                 entry->info.ping = ping + 0.5;
1777                 serverreplycount++;
1778         }
1779         entry->responded = true;
1780
1781         // other server info is updated by the caller
1782         return n;
1783 }
1784
1785 static void NetConn_ClientParsePacket_ServerList_UpdateCache(int n)
1786 {
1787         serverlist_entry_t *entry = &serverlist_cache[n];
1788         serverlist_info_t *info = &entry->info;
1789
1790         // update description strings for engine menu and console output
1791         dpsnprintf(entry->line1, sizeof(serverlist_cache[n].line1), "^%c%5.0f^7 ^%c%3u^7/%3u %-65.65s",
1792                    info->ping >= 300 ? '1' : (info->ping >= 200 ? '3' : '7'),
1793                    info->ping ?: INFINITY, // display inf when a listed server times out and net_slist_pause blocks its removal
1794                    ((info->numhumans > 0 && info->numhumans < info->maxplayers) ? (info->numhumans >= 4 ? '7' : '3') : '1'),
1795                    info->numplayers,
1796                    info->maxplayers,
1797                    info->name);
1798         dpsnprintf(entry->line2, sizeof(serverlist_cache[n].line2), "^4%-21.21s %-19.19s ^%c%-17.17s^4 %-20.20s", info->cname, info->game,
1799                         (
1800                          info->gameversion != gameversion.integer
1801                          &&
1802                          !(
1803                                     gameversion_min.integer >= 0 // min/max range set by user/mod?
1804                                  && gameversion_max.integer >= 0
1805                                  && gameversion_min.integer <= info->gameversion // version of server in min/max range?
1806                                  && gameversion_max.integer >= info->gameversion
1807                           )
1808                         ) ? '1' : '4',
1809                         info->mod, info->map);
1810
1811         if(!net_slist_pause.integer)
1812         {
1813                 ServerList_ViewList_Remove(entry);
1814                 ServerList_ViewList_Insert(entry);
1815         }
1816
1817         if (serverlist_consoleoutput)
1818                 Con_Printf("%s\n%s\n", entry->line1, entry->line2);
1819 }
1820
1821 // returns true, if it's sensible to continue the processing
1822 static qbool NetConn_ClientParsePacket_ServerList_PrepareQuery(int protocol, const char *ipstring, qbool isfavorite)
1823 {
1824         unsigned n;
1825         serverlist_entry_t *entry;
1826
1827         // ignore the rest of the message if the serverlist is full
1828         if (serverlist_cachecount == SERVERLIST_TOTALSIZE)
1829                 return false;
1830
1831         for (n = 0; n < serverlist_cachecount; ++n)
1832                 if (!strcmp(ipstring, serverlist_cache[n].info.cname))
1833                         break;
1834
1835         // also ignore it if we have already queried it (other master server response)
1836         if (n < serverlist_cachecount)
1837                 return true;
1838
1839         if (serverlist_maxcachecount <= n)
1840         {
1841                 serverlist_maxcachecount += 64;
1842                 serverlist_cache = (serverlist_entry_t *)Mem_Realloc(netconn_mempool, (void *)serverlist_cache, sizeof(serverlist_entry_t) * serverlist_maxcachecount);
1843         }
1844
1845         entry = &serverlist_cache[n];
1846         memset(entry, 0, sizeof(*entry));
1847         entry->protocol = protocol;
1848         strlcpy(entry->info.cname, ipstring, sizeof(entry->info.cname));
1849         entry->info.isfavorite = isfavorite;
1850
1851         serverlist_cachecount++;
1852         serverquerycount++;
1853
1854         return true;
1855 }
1856
1857 static void NetConn_ClientParsePacket_ServerList_ParseDPList(lhnetaddress_t *masteraddress, const char *masteraddressstring, const unsigned char *data, int length, qbool isextended)
1858 {
1859         unsigned masternum;
1860         lhnetaddress_t testaddress;
1861
1862         for (masternum = 0; masternum < DPMASTER_COUNT; ++masternum)
1863                 if (sv_masters[masternum].string[0]
1864                 && LHNETADDRESS_FromString(&testaddress, sv_masters[masternum].string, DPMASTER_PORT)
1865                 && LHNETADDRESS_Compare(&testaddress, masteraddress) == 0)
1866                         break;
1867         if (net_sourceaddresscheck.integer && masternum >= DPMASTER_COUNT)
1868         {
1869                 Con_Printf(CON_WARN "ignoring DarkPlaces %sserver list from unrecognised master %s\n", isextended ? "extended " : "", masteraddressstring);
1870                 return;
1871         }
1872
1873         masterreplycount++;
1874         dpmasterstatus[masternum] = MASTER_RX_RESPONSE;
1875         if (serverlist_consoleoutput || net_slist_debug.integer)
1876                 Con_Printf("^5Received DarkPlaces server list %sfrom %s\n", isextended ? "(extended) " : "", sv_masters[masternum].string);
1877
1878         while (length >= 7)
1879         {
1880                 char ipstring [128];
1881
1882                 // IPv4 address
1883                 if (data[0] == '\\')
1884                 {
1885                         unsigned short port = data[5] * 256 + data[6];
1886
1887                         if (port != 0 && (data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF))
1888                                 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%hu", data[1], data[2], data[3], data[4], port);
1889                         else if (port == 0 && data[1] == 'E' && data[2] == 'O' && data[3] == 'T' && data[4] == '\0')
1890                         {
1891                                 dpmasterstatus[masternum] = MASTER_RX_COMPLETE;
1892                                 if (net_slist_debug.integer)
1893                                         Con_Printf("^4End Of Transmission %sfrom %s\n", isextended ? "(extended) " : "", sv_masters[masternum].string);
1894                                 break;
1895                         }
1896
1897                         // move on to next address in packet
1898                         data += 7;
1899                         length -= 7;
1900                 }
1901                 // IPv6 address
1902                 else if (data[0] == '/' && isextended && length >= 19)
1903                 {
1904                         unsigned short port = data[17] * 256 + data[18];
1905
1906                         if (port != 0)
1907                         {
1908 #ifdef WHY_JUST_WHY
1909                                 const char *ifname;
1910                                 char ifnamebuf[16];
1911
1912                                 /// \TODO: make some basic checks of the IP address (broadcast, ...)
1913
1914                                 ifname = LHNETADDRESS_GetInterfaceName(senderaddress, ifnamebuf, sizeof(ifnamebuf));
1915                                 if (ifname != NULL)
1916                                 {
1917                                         dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x%%%s]:%hu",
1918                                                                 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1919                                                                 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1920                                                                 ifname, port);
1921                                 }
1922                                 else
1923 #endif
1924                                 {
1925                                         dpsnprintf (ipstring, sizeof (ipstring), "[%x:%x:%x:%x:%x:%x:%x:%x]:%hu",
1926                                                                 (data[1] << 8) | data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6], (data[7] << 8) | data[8],
1927                                                                 (data[9] << 8) | data[10], (data[11] << 8) | data[12], (data[13] << 8) | data[14], (data[15] << 8) | data[16],
1928                                                                 port);
1929                                 }
1930                         }
1931
1932                         // move on to next address in packet
1933                         data += 19;
1934                         length -= 19;
1935                 }
1936                 else
1937                 {
1938                         Con_Print(CON_WARN "Error while parsing the server list\n");
1939                         break;
1940                 }
1941
1942                 if (serverlist_consoleoutput && developer_networking.integer)
1943                         Con_Printf("Requesting info from DarkPlaces server %s\n", ipstring);
1944
1945                 if (!NetConn_ClientParsePacket_ServerList_PrepareQuery(PROTOCOL_DARKPLACES7, ipstring, false))
1946                         break;
1947         }
1948
1949         if (serverlist_querystage & SLIST_QUERYSTAGE_QWMASTERS)
1950                 return; // we must wait if we're also querying QW as it has no EOT marker
1951         // begin or resume serverlist queries
1952         for (masternum = 0; masternum < DPMASTER_COUNT; ++masternum)
1953                 if (dpmasterstatus[masternum] && dpmasterstatus[masternum] < MASTER_RX_COMPLETE)
1954                         break; // was queried but no EOT marker received yet
1955         if (masternum >= DPMASTER_COUNT)
1956         {
1957                 serverlist_querystage = SLIST_QUERYSTAGE_SERVERS;
1958                 if (net_slist_debug.integer)
1959                         Con_Print("^2Starting to query servers early (got EOT from all masters)\n");
1960         }
1961 }
1962
1963 static void NetConn_ClientParsePacket_ServerList_ParseQWList(lhnetaddress_t *masteraddress, const char *masteraddressstring, const unsigned char *data, int length)
1964 {
1965         uint8_t masternum;
1966         lhnetaddress_t testaddress;
1967
1968         for (masternum = 0; masternum < QWMASTER_COUNT; ++masternum)
1969                 if (sv_qwmasters[masternum].string[0]
1970                 && LHNETADDRESS_FromString(&testaddress, sv_qwmasters[masternum].string, QWMASTER_PORT)
1971                 && LHNETADDRESS_Compare(&testaddress, masteraddress) == 0)
1972                         break;
1973         if (net_sourceaddresscheck.integer && masternum >= QWMASTER_COUNT)
1974         {
1975                 Con_Printf(CON_WARN "ignoring QuakeWorld server list from unrecognised master %s\n", masteraddressstring);
1976                 return;
1977         }
1978
1979         masterreplycount++;
1980         qwmasterstatus[masternum] = MASTER_RX_RESPONSE;
1981         if (serverlist_consoleoutput || net_slist_debug.integer)
1982                 Con_Printf("^5Received QuakeWorld server list from %s\n", sv_qwmasters[masternum].string);
1983
1984         while (length >= 6 && (data[0] != 0xFF || data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF) && data[4] * 256 + data[5] != 0)
1985         {
1986                 char ipstring[32];
1987
1988                 dpsnprintf (ipstring, sizeof (ipstring), "%u.%u.%u.%u:%u", data[0], data[1], data[2], data[3], data[4] * 256 + data[5]);
1989                 if (serverlist_consoleoutput && developer_networking.integer)
1990                         Con_Printf("Requesting info from QuakeWorld server %s\n", ipstring);
1991
1992                 if (!NetConn_ClientParsePacket_ServerList_PrepareQuery(PROTOCOL_QUAKEWORLD, ipstring, false))
1993                         break;
1994
1995                 // move on to next address in packet
1996                 data += 6;
1997                 length -= 6;
1998         }
1999
2000         // Unlike in NetConn_ClientParsePacket_ServerList_ParseDPList()
2001         // we can't start to query servers early here because QW has no EOT marker.
2002 }
2003 #endif
2004
2005 static int NetConn_ClientParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
2006 {
2007         qbool fromserver;
2008         int ret, c;
2009         char *string, addressstring2[128];
2010         char stringbuf[16384];
2011         char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
2012         size_t sendlength;
2013 #ifdef CONFIG_MENU
2014         char infostringvalue[MAX_INPUTLINE];
2015         const char *s;
2016 #endif
2017
2018         // quakeworld ingame packet
2019         fromserver = cls.netcon && mysocket == cls.netcon->mysocket && !LHNETADDRESS_Compare(&cls.netcon->peeraddress, peeraddress);
2020
2021         // convert the address to a string incase we need it
2022         LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
2023
2024         if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
2025         {
2026                 // received a command string - strip off the packaging and put it
2027                 // into our string buffer with NULL termination
2028                 data += 4;
2029                 length -= 4;
2030                 length = min(length, (int)sizeof(stringbuf) - 1);
2031                 memcpy(stringbuf, data, length);
2032                 stringbuf[length] = 0;
2033                 string = stringbuf;
2034
2035                 if (developer_networking.integer)
2036                 {
2037                         Con_Printf("NetConn_ClientParsePacket: %s sent us a command:\n", addressstring2);
2038                         Com_HexDumpToConsole(data, length);
2039                 }
2040
2041                 sendlength = sizeof(senddata) - 4;
2042                 switch(Crypto_ClientParsePacket(string, length, senddata+4, &sendlength, peeraddress, addressstring2))
2043                 {
2044                         case CRYPTO_NOMATCH:
2045                                 // nothing to do
2046                                 break;
2047                         case CRYPTO_MATCH:
2048                                 if(sendlength)
2049                                 {
2050                                         memcpy(senddata, "\377\377\377\377", 4);
2051                                         NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
2052                                 }
2053                                 break;
2054                         case CRYPTO_DISCARD:
2055                                 if(sendlength)
2056                                 {
2057                                         memcpy(senddata, "\377\377\377\377", 4);
2058                                         NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
2059                                 }
2060                                 return true;
2061                                 break;
2062                         case CRYPTO_REPLACE:
2063                                 string = senddata+4;
2064                                 length = (int)sendlength;
2065                                 break;
2066                 }
2067
2068                 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.rcon_trying)
2069                 {
2070                         int i = 0, j;
2071                         for (j = 0;j < MAX_RCONS;j++)
2072                         {
2073                                 // note: this value from i is used outside the loop too...
2074                                 i = (cls.rcon_ringpos + j) % MAX_RCONS;
2075                                 if(cls.rcon_commands[i][0])
2076                                         if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[i]))
2077                                                 break;
2078                         }
2079                         if (j < MAX_RCONS)
2080                         {
2081                                 char buf[1500];
2082                                 char argbuf[1500];
2083                                 const char *e;
2084                                 int n;
2085                                 dpsnprintf(argbuf, sizeof(argbuf), "%s %s", string + 10, cls.rcon_commands[i]);
2086                                 memcpy(buf, "\377\377\377\377srcon HMAC-MD4 CHALLENGE ", 29);
2087
2088                                 e = strchr(rcon_password.string, ' ');
2089                                 n = e ? e-rcon_password.string : (int)strlen(rcon_password.string);
2090
2091                                 if(HMAC_MDFOUR_16BYTES((unsigned char *) (buf + 29), (unsigned char *) argbuf, (int)strlen(argbuf), (unsigned char *) rcon_password.string, n))
2092                                 {
2093                                         int k;
2094                                         buf[45] = ' ';
2095                                         strlcpy(buf + 46, argbuf, sizeof(buf) - 46);
2096                                         NetConn_Write(mysocket, buf, 46 + (int)strlen(buf + 46), peeraddress);
2097                                         cls.rcon_commands[i][0] = 0;
2098                                         --cls.rcon_trying;
2099
2100                                         for (k = 0;k < MAX_RCONS;k++)
2101                                                 if(cls.rcon_commands[k][0])
2102                                                         if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[k]))
2103                                                                 break;
2104                                         if(k < MAX_RCONS)
2105                                         {
2106                                                 int l;
2107                                                 NetConn_WriteString(mysocket, "\377\377\377\377getchallenge", peeraddress);
2108                                                 // extend the timeout on other requests as we asked for a challenge
2109                                                 for (l = 0;l < MAX_RCONS;l++)
2110                                                         if(cls.rcon_commands[l][0])
2111                                                                 if (!LHNETADDRESS_Compare(peeraddress, &cls.rcon_addresses[l]))
2112                                                                         cls.rcon_timeout[l] = host.realtime + rcon_secure_challengetimeout.value;
2113                                         }
2114
2115                                         return true; // we used up the challenge, so we can't use this oen for connecting now anyway
2116                                 }
2117                         }
2118                 }
2119                 if (length >= 10 && !memcmp(string, "challenge ", 10) && cls.connect_trying)
2120                 {
2121                         // darkplaces or quake3
2122                         char protocolnames[1400];
2123
2124                         if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2125                                 Con_Printf(CON_WARN "ignoring challenge message from wrong server %s\n", addressstring2);
2126                                 return true;
2127                         }
2128                         Con_DPrintf("\"%s\" received, sending connect request back to %s\n", string, addressstring2);
2129                         strlcpy(cl_connect_status, "Connect: replying to challenge...", sizeof(cl_connect_status));
2130
2131                         Protocol_Names(protocolnames, sizeof(protocolnames));
2132                         // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2133                         InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2134                         // TODO: add userinfo stuff here instead of using NQ commands?
2135                         memcpy(senddata, "\377\377\377\377", 4);
2136                         dpsnprintf(senddata+4, sizeof(senddata)-4, "connect\\protocol\\darkplaces 3\\protocols\\%s%s\\challenge\\%s", protocolnames, cls.connect_userinfo, string + 10);
2137                         NetConn_WriteString(mysocket, senddata, peeraddress);
2138                         return true;
2139                 }
2140                 if (length == 6 && !memcmp(string, "accept", 6) && cls.connect_trying)
2141                 {
2142                         // darkplaces or quake3
2143                         if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2144                                 Con_Printf(CON_WARN "ignoring accept message from wrong server %s\n", addressstring2);
2145                                 return true;
2146                         }
2147                         NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_DARKPLACES3);
2148                         return true;
2149                 }
2150                 if (length > 7 && !memcmp(string, "reject ", 7) && cls.connect_trying)
2151                 {
2152                         if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2153                                 Con_Printf(CON_WARN "ignoring reject message from wrong server %s\n", addressstring2);
2154                                 return true;
2155                         }
2156                         cls.connect_trying = false;
2157                         string += 7;
2158                         length = min(length - 7, (int)sizeof(cl_connect_status) - 1);
2159                         dpsnprintf(cl_connect_status, sizeof(cl_connect_status), "Connect: rejected, %.*s", length, string);
2160                         Con_Printf(CON_ERROR "Connect: rejected by %s\n" CON_ERROR "%.*s\n", addressstring2, length, string);
2161                         return true;
2162                 }
2163 #ifdef CONFIG_MENU
2164                 if(key_dest != key_game)
2165                 {
2166                         if (length >= 15 && !memcmp(string, "statusResponse\x0A", 15))
2167                         {
2168                                 serverlist_info_t *info;
2169                                 char *p;
2170                                 int n;
2171
2172                                 string += 15;
2173                                 // search the cache for this server and update it
2174                                 // the challenge is (ab)used to return the query time
2175                                 s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue));
2176                                 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2, s);
2177                                 if (n < 0)
2178                                         return true;
2179
2180                                 info = &serverlist_cache[n].info;
2181                                 info->game[0] = 0;
2182                                 info->mod[0]  = 0;
2183                                 info->map[0]  = 0;
2184                                 info->name[0] = 0;
2185                                 info->qcstatus[0] = 0;
2186                                 info->players[0] = 0;
2187                                 info->protocol = -1;
2188                                 info->numplayers = 0;
2189                                 info->numbots = -1;
2190                                 info->maxplayers  = 0;
2191                                 info->gameversion = 0;
2192
2193                                 p = strchr(string, '\n');
2194                                 if(p)
2195                                 {
2196                                         *p = 0; // cut off the string there
2197                                         ++p;
2198                                 }
2199                                 else
2200                                         Con_Printf("statusResponse without players block?\n");
2201
2202                                 if ((s = InfoString_GetValue(string, "gamename"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
2203                                 if ((s = InfoString_GetValue(string, "modname"      , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
2204                                 if ((s = InfoString_GetValue(string, "mapname"      , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
2205                                 if ((s = InfoString_GetValue(string, "hostname"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
2206                                 if ((s = InfoString_GetValue(string, "protocol"     , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
2207                                 if ((s = InfoString_GetValue(string, "clients"      , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
2208                                 if ((s = InfoString_GetValue(string, "bots"         , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
2209                                 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
2210                                 if ((s = InfoString_GetValue(string, "gameversion"  , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
2211                                 if ((s = InfoString_GetValue(string, "qcstatus"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
2212                                 if (p                                                                                         != NULL) strlcpy(info->players, p, sizeof(info->players));
2213                                 info->numhumans = info->numplayers - max(0, info->numbots);
2214                                 info->freeslots = info->maxplayers - info->numplayers;
2215
2216                                 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2217
2218                                 return true;
2219                         }
2220                         if (length >= 13 && !memcmp(string, "infoResponse\x0A", 13))
2221                         {
2222                                 serverlist_info_t *info;
2223                                 int n;
2224
2225                                 string += 13;
2226                                 // search the cache for this server and update it
2227                                 // the challenge is (ab)used to return the query time
2228                                 s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue));
2229                                 n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2, s);
2230                                 if (n < 0)
2231                                         return true;
2232
2233                                 info = &serverlist_cache[n].info;
2234                                 info->game[0] = 0;
2235                                 info->mod[0]  = 0;
2236                                 info->map[0]  = 0;
2237                                 info->name[0] = 0;
2238                                 info->qcstatus[0] = 0;
2239                                 info->players[0] = 0;
2240                                 info->protocol = -1;
2241                                 info->numplayers = 0;
2242                                 info->numbots = -1;
2243                                 info->maxplayers  = 0;
2244                                 info->gameversion = 0;
2245
2246                                 if ((s = InfoString_GetValue(string, "gamename"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->game, s, sizeof (info->game));
2247                                 if ((s = InfoString_GetValue(string, "modname"      , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));
2248                                 if ((s = InfoString_GetValue(string, "mapname"      , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));
2249                                 if ((s = InfoString_GetValue(string, "hostname"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));
2250                                 if ((s = InfoString_GetValue(string, "protocol"     , infostringvalue, sizeof(infostringvalue))) != NULL) info->protocol = atoi(s);
2251                                 if ((s = InfoString_GetValue(string, "clients"      , infostringvalue, sizeof(infostringvalue))) != NULL) info->numplayers = atoi(s);
2252                                 if ((s = InfoString_GetValue(string, "bots"         , infostringvalue, sizeof(infostringvalue))) != NULL) info->numbots = atoi(s);
2253                                 if ((s = InfoString_GetValue(string, "sv_maxclients", infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);
2254                                 if ((s = InfoString_GetValue(string, "gameversion"  , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);
2255                                 if ((s = InfoString_GetValue(string, "qcstatus"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->qcstatus, s, sizeof(info->qcstatus));
2256                                 info->numhumans = info->numplayers - max(0, info->numbots);
2257                                 info->freeslots = info->maxplayers - info->numplayers;
2258
2259                                 NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2260
2261                                 return true;
2262                         }
2263                         if (!strncmp(string, "getserversResponse\\", 19) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2264                         {
2265                                 // Extract the IP addresses
2266                                 data += 18;
2267                                 length -= 18;
2268                                 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, addressstring2, data, length, false);
2269                                 return true;
2270                         }
2271                         if (!strncmp(string, "getserversExtResponse", 21) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2272                         {
2273                                 // Extract the IP addresses
2274                                 data += 21;
2275                                 length -= 21;
2276                                 NetConn_ClientParsePacket_ServerList_ParseDPList(peeraddress, addressstring2, data, length, true);
2277                                 return true;
2278                         }
2279                         if (!memcmp(string, "d\n", 2) && serverlist_cachecount < SERVERLIST_TOTALSIZE)
2280                         {
2281                                 // Extract the IP addresses
2282                                 data += 2;
2283                                 length -= 2;
2284                                 NetConn_ClientParsePacket_ServerList_ParseQWList(peeraddress, addressstring2, data, length);
2285                                 return true;
2286                         }
2287                 }
2288 #endif
2289                 if (!strncmp(string, "extResponse ", 12))
2290                 {
2291                         ++cl_net_extresponse_count;
2292                         if(cl_net_extresponse_count > NET_EXTRESPONSE_MAX)
2293                                 cl_net_extresponse_count = NET_EXTRESPONSE_MAX;
2294                         cl_net_extresponse_last = (cl_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
2295                         dpsnprintf(cl_net_extresponse[cl_net_extresponse_last], sizeof(cl_net_extresponse[cl_net_extresponse_last]), "\"%s\" %s", addressstring2, string + 12);
2296                         return true;
2297                 }
2298                 if (!strncmp(string, "ping", 4))
2299                 {
2300                         if (developer_extra.integer)
2301                                 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
2302                         NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
2303                         return true;
2304                 }
2305                 if (!strncmp(string, "ack", 3))
2306                         return true;
2307                 // QuakeWorld compatibility
2308                 if (length > 1 && string[0] == 'c' && (string[1] == '-' || (string[1] >= '0' && string[1] <= '9')) && cls.connect_trying)
2309                 {
2310                         // challenge message
2311                         if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2312                                 Con_Printf(CON_WARN "ignoring c message from wrong server %s\n", addressstring2);
2313                                 return true;
2314                         }
2315                         Con_DPrintf("challenge %s received, sending QuakeWorld connect request back to %s\n", string + 1, addressstring2);
2316                         strlcpy(cl_connect_status, "Connect: replying to challenge...", sizeof(cl_connect_status));
2317
2318                         cls.qw_qport = qport.integer;
2319                         // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2320                         InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2321                         memcpy(senddata, "\377\377\377\377", 4);
2322                         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);
2323                         NetConn_WriteString(mysocket, senddata, peeraddress);
2324                         return true;
2325                 }
2326                 if (length >= 1 && string[0] == 'j' && cls.connect_trying)
2327                 {
2328                         // accept message
2329                         if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2330                                 Con_Printf(CON_WARN "ignoring j message from wrong server %s\n", addressstring2);
2331                                 return true;
2332                         }
2333                         NetConn_ConnectionEstablished(mysocket, peeraddress, PROTOCOL_QUAKEWORLD);
2334                         return true;
2335                 }
2336                 if (length > 2 && !memcmp(string, "n\\", 2))
2337                 {
2338 #ifdef CONFIG_MENU
2339                         serverlist_info_t *info;
2340                         int n;
2341
2342                         // qw server status
2343                         if (serverlist_consoleoutput && developer_networking.integer >= 2)
2344                                 Con_Printf("QW server status from server at %s:\n%s\n", addressstring2, string + 1);
2345
2346                         string += 1;
2347                         // search the cache for this server and update it
2348                         n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2, NULL);
2349                         if (n < 0)
2350                                 return true;
2351
2352                         info = &serverlist_cache[n].info;
2353                         strlcpy(info->game, "QuakeWorld", sizeof(info->game));
2354                         if ((s = InfoString_GetValue(string, "*gamedir"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->mod , s, sizeof (info->mod ));else info->mod[0]  = 0;
2355                         if ((s = InfoString_GetValue(string, "map"          , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->map , s, sizeof (info->map ));else info->map[0]  = 0;
2356                         if ((s = InfoString_GetValue(string, "hostname"     , infostringvalue, sizeof(infostringvalue))) != NULL) strlcpy(info->name, s, sizeof (info->name));else info->name[0] = 0;
2357                         info->protocol = 0;
2358                         info->numplayers = 0; // updated below
2359                         info->numhumans = 0; // updated below
2360                         if ((s = InfoString_GetValue(string, "maxclients"   , infostringvalue, sizeof(infostringvalue))) != NULL) info->maxplayers = atoi(s);else info->maxplayers  = 0;
2361                         if ((s = InfoString_GetValue(string, "gameversion"  , infostringvalue, sizeof(infostringvalue))) != NULL) info->gameversion = atoi(s);else info->gameversion = 0;
2362
2363                         // count active players on server
2364                         // (we could gather more info, but we're just after the number)
2365                         s = strchr(string, '\n');
2366                         if (s)
2367                         {
2368                                 s++;
2369                                 while (s < string + length)
2370                                 {
2371                                         for (;s < string + length && *s != '\n';s++)
2372                                                 ;
2373                                         if (s >= string + length)
2374                                                 break;
2375                                         info->numplayers++;
2376                                         info->numhumans++;
2377                                         s++;
2378                                 }
2379                         }
2380
2381                         NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2382 #endif
2383                         return true;
2384                 }
2385                 if (string[0] == 'n')
2386                 {
2387                         // qw print command, used by rcon replies too
2388                         if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address) && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) {
2389                                 Con_Printf(CON_WARN "ignoring n message from wrong server %s\n", addressstring2);
2390                                 return true;
2391                         }
2392                         Con_Printf("QW print command from server at %s:\n%s\n", addressstring2, string + 1);
2393                 }
2394                 // we may not have liked the packet, but it was a command packet, so
2395                 // we're done processing this packet now
2396                 return true;
2397         }
2398         // quakeworld ingame packet
2399         if (fromserver && cls.protocol == PROTOCOL_QUAKEWORLD && length >= 8 && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2400         {
2401                 ret = 0;
2402                 CL_ParseServerMessage();
2403                 return ret;
2404         }
2405         // netquake control packets, supported for compatibility only
2406         if (length >= 5 && BuffBigLong(data) == ((int)NETFLAG_CTL | length) && !ENCRYPTION_REQUIRED)
2407         {
2408 #ifdef CONFIG_MENU
2409                 int n;
2410                 serverlist_info_t *info;
2411 #endif
2412
2413                 data += 4;
2414                 length -= 4;
2415                 SZ_Clear(&cl_message);
2416                 SZ_Write(&cl_message, data, length);
2417                 MSG_BeginReading(&cl_message);
2418                 c = MSG_ReadByte(&cl_message);
2419                 switch (c)
2420                 {
2421                 case CCREP_ACCEPT:
2422                         if (developer_extra.integer)
2423                                 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_ACCEPT from %s.\n", addressstring2);
2424                         if (cls.connect_trying)
2425                         {
2426                                 lhnetaddress_t clientportaddress;
2427                                 if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address)) {
2428                                         Con_Printf(CON_WARN "ignoring CCREP_ACCEPT message from wrong server %s\n", addressstring2);
2429                                         break;
2430                                 }
2431                                 clientportaddress = *peeraddress;
2432                                 LHNETADDRESS_SetPort(&clientportaddress, MSG_ReadLong(&cl_message));
2433                                 // extra ProQuake stuff
2434                                 if (length >= 6)
2435                                         cls.proquake_servermod = MSG_ReadByte(&cl_message); // MOD_PROQUAKE
2436                                 else
2437                                         cls.proquake_servermod = 0;
2438                                 if (length >= 7)
2439                                         cls.proquake_serverversion = MSG_ReadByte(&cl_message); // version * 10
2440                                 else
2441                                         cls.proquake_serverversion = 0;
2442                                 if (length >= 8)
2443                                         cls.proquake_serverflags = MSG_ReadByte(&cl_message); // flags (mainly PQF_CHEATFREE)
2444                                 else
2445                                         cls.proquake_serverflags = 0;
2446                                 if (cls.proquake_servermod == 1)
2447                                         Con_Printf("Connected to ProQuake %.1f server, enabling precise aim\n", cls.proquake_serverversion / 10.0f);
2448                                 // update the server IP in the userinfo (QW servers expect this, and it is used by the reconnect command)
2449                                 InfoString_SetValue(cls.userinfo, sizeof(cls.userinfo), "*ip", addressstring2);
2450                                 NetConn_ConnectionEstablished(mysocket, &clientportaddress, PROTOCOL_QUAKE);
2451                         }
2452                         break;
2453                 case CCREP_REJECT:
2454                         if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.connect_address))
2455                         {
2456                                 Con_Printf(CON_WARN "ignoring CCREP_REJECT message from wrong server %s\n", addressstring2);
2457                                 break;
2458                         }
2459                         cls.connect_trying = false;
2460                         dpsnprintf(cl_connect_status, sizeof(cl_connect_status), "Connect: rejected, %s", MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2461                         Con_Printf(CON_ERROR "Connect: rejected by %s\n%s\n", addressstring2, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2462                         break;
2463                 case CCREP_SERVER_INFO:
2464                         if (developer_extra.integer)
2465                                 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_SERVER_INFO from %s.\n", addressstring2);
2466 #ifdef CONFIG_MENU
2467                         // LadyHavoc: because the quake server may report weird addresses
2468                         // we just ignore it and keep the real address
2469                         MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
2470                         // search the cache for this server and update it
2471                         n = NetConn_ClientParsePacket_ServerList_ProcessReply(addressstring2, NULL);
2472                         if (n < 0)
2473                                 break;
2474
2475                         info = &serverlist_cache[n].info;
2476                         strlcpy(info->game, "Quake", sizeof(info->game));
2477                         strlcpy(info->mod , "", sizeof(info->mod)); // mod name is not specified
2478                         strlcpy(info->name, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->name));
2479                         strlcpy(info->map , MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(info->map));
2480                         info->numplayers = MSG_ReadByte(&cl_message);
2481                         info->maxplayers = MSG_ReadByte(&cl_message);
2482                         info->protocol = MSG_ReadByte(&cl_message);
2483
2484                         NetConn_ClientParsePacket_ServerList_UpdateCache(n);
2485 #endif
2486                         break;
2487                 case CCREP_RCON: // RocketGuy: ProQuake rcon support
2488                         if (net_sourceaddresscheck.integer && LHNETADDRESS_Compare(peeraddress, &cls.rcon_address)) {
2489                                 Con_Printf(CON_WARN "ignoring CCREP_RCON message from wrong server %s\n", addressstring2);
2490                                 break;
2491                         }
2492                         if (developer_extra.integer)
2493                                 Con_DPrintf("Datagram_ParseConnectionless: received CCREP_RCON from %s.\n", addressstring2);
2494
2495                         Con_Printf("%s\n", MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
2496                         break;
2497                 case CCREP_PLAYER_INFO:
2498                         // we got a CCREP_PLAYER_INFO??
2499                         //if (developer_extra.integer)
2500                                 Con_Printf("Datagram_ParseConnectionless: received CCREP_PLAYER_INFO from %s.\n", addressstring2);
2501                         break;
2502                 case CCREP_RULE_INFO:
2503                         // we got a CCREP_RULE_INFO??
2504                         //if (developer_extra.integer)
2505                                 Con_Printf("Datagram_ParseConnectionless: received CCREP_RULE_INFO from %s.\n", addressstring2);
2506                         break;
2507                 default:
2508                         break;
2509                 }
2510                 SZ_Clear(&cl_message);
2511                 // we may not have liked the packet, but it was a valid control
2512                 // packet, so we're done processing this packet now
2513                 return true;
2514         }
2515         ret = 0;
2516         if (fromserver && length >= (int)NET_HEADERSIZE && (ret = NetConn_ReceivedMessage(cls.netcon, data, length, cls.protocol, net_messagetimeout.value)) == 2)
2517                 CL_ParseServerMessage();
2518         return ret;
2519 }
2520
2521 #ifdef CONFIG_MENU
2522 void NetConn_QueryQueueFrame(void)
2523 {
2524         unsigned index;
2525         unsigned queries, maxqueries;
2526         char dpquery[53]; // theoretical max: 14+22+16+1
2527         double currentrealtime;
2528         static double querycounter = 0;
2529         qbool pending = false;
2530
2531         if (!serverlist_querystage)
2532                 return;
2533
2534         // If the client stalls partway through a frame (test command: `alias a a;a`)
2535         // for correct pings we need to know what host.realtime would be if it were updated now.
2536         currentrealtime = host.realtime + (Sys_DirtyTime() - host.dirtytime);
2537
2538         // apply a cool down time after master server replies,
2539         // to avoid messing up the ping times on the servers
2540         if (serverlist_querystage < SLIST_QUERYSTAGE_SERVERS)
2541         {
2542                 if (currentrealtime < masterquerytime + net_slist_timeout.value)
2543                         return;
2544
2545                 // Report the masters that timed out or whose response was incomplete.
2546                 for (index = 0; index < DPMASTER_COUNT; ++index)
2547                         if (dpmasterstatus[index] && dpmasterstatus[index] < MASTER_RX_COMPLETE)
2548                                 Con_Printf(CON_WARN "WARNING: dpmaster %s %s\n", sv_masters[index].string, dpmasterstatus[index] == MASTER_TX_QUERY ? "timed out" : "response incomplete");
2549                 for (index = 0; index < QWMASTER_COUNT; ++index)
2550                         if (qwmasterstatus[index] && qwmasterstatus[index] < MASTER_RX_RESPONSE) // no EOT marker in QW lists
2551                                 Con_Printf(CON_WARN "WARNING: qwmaster %s timed out\n", sv_qwmasters[index].string);
2552
2553                 serverlist_querystage = SLIST_QUERYSTAGE_SERVERS;
2554         }
2555
2556         // each time querycounter reaches 1.0 issue a query
2557         querycounter += cl.realframetime * net_slist_queriespersecond.value;
2558         maxqueries = bound(0, (int)querycounter, net_slist_queriesperframe.integer);
2559         querycounter -= maxqueries;
2560         if (maxqueries == 0)
2561                 return;
2562
2563         // QW depends on waiting "long enough" between queries that responses "definitely" refer to the most recent querytime
2564         // DP servers can echo back a timestamp for reliable (and more frequent, see net_slist_interval) pings
2565         ServerList_BuildDPServerQuery(dpquery, sizeof(dpquery), currentrealtime);
2566
2567         for (index = 0, queries = 0; index < serverlist_cachecount && queries < maxqueries; ++index)
2568         {
2569                 serverlist_entry_t *entry = &serverlist_cache[index];
2570
2571                 if (entry->querycounter < min(8, (unsigned)net_slist_maxtries.integer))
2572                 {
2573                         lhnetaddress_t address;
2574                         unsigned socket;
2575
2576                         // only check this when there are tries remaining so we finish querying sooner
2577                         if (currentrealtime < entry->querytime + (entry->protocol == PROTOCOL_QUAKEWORLD ? net_slist_timeout : net_slist_interval).value)
2578                         {
2579                                 pending = true;
2580                                 continue;
2581                         }
2582
2583                         LHNETADDRESS_FromString(&address, entry->info.cname, 0);
2584                         if (entry->protocol == PROTOCOL_QUAKEWORLD)
2585                         {
2586                                 for (socket = 0; socket < cl_numsockets; ++socket)
2587                                         if (cl_sockets[socket])
2588                                                 NetConn_WriteString(cl_sockets[socket], "\377\377\377\377status\n", &address);
2589                         }
2590                         else
2591                         {
2592                                 for (socket = 0; socket < cl_numsockets; ++socket)
2593                                         if (cl_sockets[socket])
2594                                                 NetConn_WriteString(cl_sockets[socket], dpquery, &address);
2595                         }
2596
2597                         entry->querytime = currentrealtime;
2598                         entry->querycounter++;
2599                         queries++;
2600
2601                         if (serverlist_consoleoutput)
2602                                 Con_Printf("querying %25s (%i. try)\n", entry->info.cname, entry->querycounter);
2603                 }
2604                 else // reached net_slist_maxtries
2605                 if (!entry->responded // no acceptable response during this refresh cycle
2606                 && (entry->info.ping)) // visible in the list (has old ping from previous refresh cycle)
2607                 {
2608                         if (currentrealtime >= entry->querytime + net_slist_maxping.integer/1000)
2609                         {
2610                                 // you have no chance to survive make your timeout
2611                                 serverreplycount--;
2612                                 if(!net_slist_pause.integer)
2613                                         ServerList_ViewList_Remove(entry);
2614                                 entry->info.ping = 0; // removed later if net_slist_pause
2615                         }
2616                         else // still has time
2617                                 pending = true;
2618                 }
2619         }
2620
2621         // If we got to the end of the list (didn't hit maxqueries)
2622         // and no servers remain to be queried or checked for timeout,
2623         // there's nothing else to do here until the next refresh cycle.
2624         if (index >= serverlist_cachecount && !pending)
2625         {
2626                 if (net_slist_debug.integer)
2627                         Con_Printf("^2Finished querying masters and servers in %f\n", currentrealtime - masterquerytime);
2628                 serverlist_querystage = 0;
2629         }
2630 }
2631 #endif
2632
2633 void NetConn_ClientFrame(void)
2634 {
2635         unsigned i;
2636         int length;
2637         lhnetaddress_t peeraddress;
2638         unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
2639
2640         NetConn_UpdateSockets();
2641
2642         if (cls.connect_trying && cls.connect_nextsendtime < host.realtime)
2643         {
2644                 if (cls.connect_remainingtries > 0)
2645                 {
2646                         cls.connect_remainingtries--;
2647                         dpsnprintf(cl_connect_status, sizeof(cl_connect_status), "Connect: sending initial request, %i %s left...", cls.connect_remainingtries, cls.connect_remainingtries == 1 ? "retry" : "retries");
2648                 }
2649                 else
2650                 {
2651                         char address[128];
2652
2653                         cls.connect_trying = false;
2654                         LHNETADDRESS_ToString(&cls.connect_address, address, sizeof(address), true);
2655                         strlcpy(cl_connect_status, "Connect: failed, no reply", sizeof(cl_connect_status));
2656                         Con_Printf(CON_ERROR "%s from %s\n", cl_connect_status, address);
2657                         return;
2658                 }
2659                 cls.connect_nextsendtime = host.realtime + 1;
2660
2661                 // try challenge first (newer DP server or QW)
2662                 NetConn_WriteString(cls.connect_mysocket, "\377\377\377\377getchallenge", &cls.connect_address);
2663                 // then try netquake as a fallback (old server, or netquake)
2664                 SZ_Clear(&cl_message);
2665                 // save space for the header, filled in later
2666                 MSG_WriteLong(&cl_message, 0);
2667                 MSG_WriteByte(&cl_message, CCREQ_CONNECT);
2668                 MSG_WriteString(&cl_message, "QUAKE");
2669                 MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
2670                 // extended proquake stuff
2671                 MSG_WriteByte(&cl_message, 1); // mod = MOD_PROQUAKE
2672                 // this version matches ProQuake 3.40, the first version to support
2673                 // the NAT fix, and it only supports the NAT fix for ProQuake 3.40 or
2674                 // higher clients, so we pretend we are that version...
2675                 MSG_WriteByte(&cl_message, 34); // version * 10
2676                 MSG_WriteByte(&cl_message, 0); // flags
2677                 MSG_WriteLong(&cl_message, 0); // password
2678                 // write the packetsize now...
2679                 StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
2680                 NetConn_Write(cls.connect_mysocket, cl_message.data, cl_message.cursize, &cls.connect_address);
2681                 SZ_Clear(&cl_message);
2682         }
2683
2684         for (i = 0;i < cl_numsockets;i++)
2685         {
2686                 while (cl_sockets[i] && (length = NetConn_Read(cl_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
2687                 {
2688 //                      R_TimeReport("clientreadnetwork");
2689                         NetConn_ClientParsePacket(cl_sockets[i], readbuffer, length, &peeraddress);
2690 //                      R_TimeReport("clientparsepacket");
2691                 }
2692         }
2693 #ifdef CONFIG_MENU
2694         NetConn_QueryQueueFrame();
2695 #endif
2696         if (cls.netcon && host.realtime > cls.netcon->timeout && !sv.active)
2697                 CL_DisconnectEx(true, "Connection timed out");
2698 }
2699
2700 static void NetConn_BuildChallengeString(char *buffer, int bufferlength)
2701 {
2702         int i;
2703         char c;
2704         for (i = 0;i < bufferlength - 1;i++)
2705         {
2706                 do
2707                 {
2708                         c = rand () % (127 - 33) + 33;
2709                 } while (c == '\\' || c == ';' || c == '"' || c == '%' || c == '/');
2710                 buffer[i] = c;
2711         }
2712         buffer[i] = 0;
2713 }
2714
2715 /// (div0) build the full response only if possible; better a getinfo response than no response at all if getstatus won't fit
2716 static qbool NetConn_BuildStatusResponse(const char* challenge, char* out_msg, size_t out_size, qbool fullstatus)
2717 {
2718         prvm_prog_t *prog = SVVM_prog;
2719         char qcstatus[256];
2720         unsigned int nb_clients = 0, nb_bots = 0, i;
2721         int length;
2722         char teambuf[3];
2723         const char *crypto_idstring;
2724         const char *worldstatusstr;
2725
2726         // How many clients are there?
2727         for (i = 0;i < (unsigned int)svs.maxclients;i++)
2728         {
2729                 if (svs.clients[i].active)
2730                 {
2731                         nb_clients++;
2732                         if (!svs.clients[i].netconnection)
2733                                 nb_bots++;
2734                 }
2735         }
2736
2737         *qcstatus = 0;
2738         worldstatusstr = PRVM_GetString(prog, PRVM_serverglobalstring(worldstatus));
2739         if(worldstatusstr && *worldstatusstr)
2740         {
2741                 char *p;
2742                 const char *q;
2743                 p = qcstatus;
2744                 for(q = worldstatusstr; *q && (size_t)(p - qcstatus) < (sizeof(qcstatus) - 1); ++q)
2745                         if(*q != '\\' && *q != '\n')
2746                                 *p++ = *q;
2747                 *p = 0;
2748         }
2749
2750         /// \TODO: we should add more information for the full status string
2751         crypto_idstring = Crypto_GetInfoResponseDataString();
2752         length = dpsnprintf(out_msg, out_size,
2753                                                 "\377\377\377\377%s\x0A"
2754                                                 "\\gamename\\%s\\modname\\%s\\gameversion\\%d\\sv_maxclients\\%d"
2755                                                 "\\clients\\%d\\bots\\%d\\mapname\\%s\\hostname\\%s\\protocol\\%d"
2756                                                 "%s%s"
2757                                                 "%s%s"
2758                                                 "%s%s"
2759                                                 "%s",
2760                                                 fullstatus ? "statusResponse" : "infoResponse",
2761                                                 gamenetworkfiltername, com_modname, gameversion.integer, svs.maxclients,
2762                                                 nb_clients, nb_bots, sv.worldbasename, hostname.string, NET_PROTOCOL_VERSION,
2763                                                 *qcstatus ? "\\qcstatus\\" : "", qcstatus,
2764                                                 challenge ? "\\challenge\\" : "", challenge ? challenge : "",
2765                                                 crypto_idstring ? "\\d0_blind_id\\" : "", crypto_idstring ? crypto_idstring : "",
2766                                                 fullstatus ? "\n" : "");
2767
2768         // Make sure it fits in the buffer
2769         if (length < 0)
2770                 goto bad;
2771
2772         if (fullstatus)
2773         {
2774                 char *ptr;
2775                 int left;
2776                 int savelength;
2777
2778                 savelength = length;
2779
2780                 ptr = out_msg + length;
2781                 left = (int)out_size - length;
2782
2783                 for (i = 0;i < (unsigned int)svs.maxclients;i++)
2784                 {
2785                         client_t *client = &svs.clients[i];
2786                         if (client->active)
2787                         {
2788                                 int nameind, cleanind, pingvalue;
2789                                 char curchar;
2790                                 char cleanname [sizeof(client->name)];
2791                                 const char *statusstr;
2792                                 prvm_edict_t *ed;
2793
2794                                 // Remove all characters '"' and '\' in the player name
2795                                 nameind = 0;
2796                                 cleanind = 0;
2797                                 do
2798                                 {
2799                                         curchar = client->name[nameind++];
2800                                         if (curchar != '"' && curchar != '\\')
2801                                         {
2802                                                 cleanname[cleanind++] = curchar;
2803                                                 if (cleanind == sizeof(cleanname) - 1)
2804                                                         break;
2805                                         }
2806                                 } while (curchar != '\0');
2807                                 cleanname[cleanind] = 0; // cleanind is always a valid index even at this point
2808
2809                                 pingvalue = (int)(client->ping * 1000.0f);
2810                                 if(client->netconnection)
2811                                         pingvalue = bound(1, pingvalue, 9999);
2812                                 else
2813                                         pingvalue = 0;
2814
2815                                 *qcstatus = 0;
2816                                 ed = PRVM_EDICT_NUM(i + 1);
2817                                 statusstr = PRVM_GetString(prog, PRVM_serveredictstring(ed, clientstatus));
2818                                 if(statusstr && *statusstr)
2819                                 {
2820                                         char *p;
2821                                         const char *q;
2822                                         p = qcstatus;
2823                                         for(q = statusstr; *q && p != qcstatus + sizeof(qcstatus) - 1; ++q)
2824                                                 if(*q != '\\' && *q != '"' && !ISWHITESPACE(*q))
2825                                                         *p++ = *q;
2826                                         *p = 0;
2827                                 }
2828
2829                                 if (IS_NEXUIZ_DERIVED(gamemode) && (teamplay.integer > 0))
2830                                 {
2831                                         if(client->frags == -666) // spectator
2832                                                 strlcpy(teambuf, " 0", sizeof(teambuf));
2833                                         else if(client->colors == 0x44) // red team
2834                                                 strlcpy(teambuf, " 1", sizeof(teambuf));
2835                                         else if(client->colors == 0xDD) // blue team
2836                                                 strlcpy(teambuf, " 2", sizeof(teambuf));
2837                                         else if(client->colors == 0xCC) // yellow team
2838                                                 strlcpy(teambuf, " 3", sizeof(teambuf));
2839                                         else if(client->colors == 0x99) // pink team
2840                                                 strlcpy(teambuf, " 4", sizeof(teambuf));
2841                                         else
2842                                                 strlcpy(teambuf, " 0", sizeof(teambuf));
2843                                 }
2844                                 else
2845                                         *teambuf = 0;
2846
2847                                 // note: team number is inserted according to SoF2 protocol
2848                                 if(*qcstatus)
2849                                         length = dpsnprintf(ptr, left, "%s %d%s \"%s\"\n",
2850                                                                                 qcstatus,
2851                                                                                 pingvalue,
2852                                                                                 teambuf,
2853                                                                                 cleanname);
2854                                 else
2855                                         length = dpsnprintf(ptr, left, "%d %d%s \"%s\"\n",
2856                                                                                 client->frags,
2857                                                                                 pingvalue,
2858                                                                                 teambuf,
2859                                                                                 cleanname);
2860
2861                                 if(length < 0)
2862                                 {
2863                                         // out of space?
2864                                         // turn it into an infoResponse!
2865                                         out_msg[savelength] = 0;
2866                                         memcpy(out_msg + 4, "infoResponse\x0A", 13);
2867                                         memmove(out_msg + 17, out_msg + 19, savelength - 19);
2868                                         break;
2869                                 }
2870                                 left -= length;
2871                                 ptr += length;
2872                         }
2873                 }
2874         }
2875
2876         return true;
2877
2878 bad:
2879         return false;
2880 }
2881
2882 static qbool NetConn_PreventFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength, double floodtime, qbool renew)
2883 {
2884         size_t floodslotnum, bestfloodslotnum;
2885         double bestfloodtime;
2886         lhnetaddress_t noportpeeraddress;
2887         // see if this is a connect flood
2888         noportpeeraddress = *peeraddress;
2889         LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2890         bestfloodslotnum = 0;
2891         bestfloodtime = floodlist[bestfloodslotnum].lasttime;
2892         for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2893         {
2894                 if (bestfloodtime >= floodlist[floodslotnum].lasttime)
2895                 {
2896                         bestfloodtime = floodlist[floodslotnum].lasttime;
2897                         bestfloodslotnum = floodslotnum;
2898                 }
2899                 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2900                 {
2901                         // this address matches an ongoing flood address
2902                         if (host.realtime < floodlist[floodslotnum].lasttime + floodtime)
2903                         {
2904                                 if(renew)
2905                                 {
2906                                         // renew the ban on this address so it does not expire
2907                                         // until the flood has subsided
2908                                         floodlist[floodslotnum].lasttime = host.realtime;
2909                                 }
2910                                 //Con_Printf("Flood detected!\n");
2911                                 return true;
2912                         }
2913                         // the flood appears to have subsided, so allow this
2914                         bestfloodslotnum = floodslotnum; // reuse the same slot
2915                         break;
2916                 }
2917         }
2918         // begin a new timeout on this address
2919         floodlist[bestfloodslotnum].address = noportpeeraddress;
2920         floodlist[bestfloodslotnum].lasttime = host.realtime;
2921         //Con_Printf("Flood detection initiated!\n");
2922         return false;
2923 }
2924
2925 void NetConn_ClearFlood(lhnetaddress_t *peeraddress, server_floodaddress_t *floodlist, size_t floodlength)
2926 {
2927         size_t floodslotnum;
2928         lhnetaddress_t noportpeeraddress;
2929         // see if this is a connect flood
2930         noportpeeraddress = *peeraddress;
2931         LHNETADDRESS_SetPort(&noportpeeraddress, 0);
2932         for (floodslotnum = 0;floodslotnum < floodlength;floodslotnum++)
2933         {
2934                 if (floodlist[floodslotnum].lasttime && LHNETADDRESS_Compare(&noportpeeraddress, &floodlist[floodslotnum].address) == 0)
2935                 {
2936                         // this address matches an ongoing flood address
2937                         // remove the ban
2938                         floodlist[floodslotnum].address.addresstype = LHNETADDRESSTYPE_NONE;
2939                         floodlist[floodslotnum].lasttime = 0;
2940                         //Con_Printf("Flood cleared!\n");
2941                 }
2942         }
2943 }
2944
2945 typedef qbool (*rcon_matchfunc_t) (lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen);
2946
2947 static qbool hmac_mdfour_time_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2948 {
2949         char mdfourbuf[16];
2950         long t1, t2;
2951
2952         if (!password[0]) {
2953                 Con_Print(CON_ERROR "LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2954                 return false;
2955         }
2956
2957         t1 = (long) time(NULL);
2958         t2 = strtol(s, NULL, 0);
2959         if(labs(t1 - t2) > rcon_secure_maxdiff.integer)
2960                 return false;
2961
2962         if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2963                 return false;
2964
2965         return !memcmp(mdfourbuf, hash, 16);
2966 }
2967
2968 static qbool hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2969 {
2970         char mdfourbuf[16];
2971         int i;
2972
2973         if (!password[0]) {
2974                 Con_Print(CON_ERROR "LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2975                 return false;
2976         }
2977
2978         if(slen < (int)(sizeof(challenges[0].string)) - 1)
2979                 return false;
2980
2981         // validate the challenge
2982         for (i = 0;i < MAX_CHALLENGES;i++)
2983                 if(challenges[i].time > 0)
2984                         if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strncmp(challenges[i].string, s, sizeof(challenges[0].string) - 1))
2985                                 break;
2986         // if the challenge is not recognized, drop the packet
2987         if (i == MAX_CHALLENGES)
2988                 return false;
2989
2990         if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2991                 return false;
2992
2993         if(memcmp(mdfourbuf, hash, 16))
2994                 return false;
2995
2996         // unmark challenge to prevent replay attacks
2997         challenges[i].time = 0;
2998
2999         return true;
3000 }
3001
3002 static qbool plaintext_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
3003 {
3004         if (!password[0]) {
3005                 Con_Print(CON_ERROR "LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
3006                 return false;
3007         }
3008
3009         return !strcmp(password, hash);
3010 }
3011
3012 /// returns a string describing the user level, or NULL for auth failure
3013 static const char *RCon_Authenticate(lhnetaddress_t *peeraddress, const char *password, const char *s, const char *endpos, rcon_matchfunc_t comparator, const char *cs, int cslen)
3014 {
3015         const char *text, *userpass_start, *userpass_end, *userpass_startpass;
3016         static char buf[MAX_INPUTLINE];
3017         qbool hasquotes;
3018         qbool restricted = false;
3019         qbool have_usernames = false;
3020         static char vabuf[1024];
3021
3022         userpass_start = rcon_password.string;
3023         while((userpass_end = strchr(userpass_start, ' ')))
3024         {
3025                 have_usernames = true;
3026                 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
3027                 if(buf[0])  // Ignore empty entries due to leading/duplicate space.
3028                         if(comparator(peeraddress, buf, password, cs, cslen))
3029                                 goto allow;
3030                 userpass_start = userpass_end + 1;
3031         }
3032         if(userpass_start[0])  // Ignore empty trailing entry due to trailing space or password not set.
3033         {
3034                 userpass_end = userpass_start + strlen(userpass_start);
3035                 if(comparator(peeraddress, userpass_start, password, cs, cslen))
3036                         goto allow;
3037         }
3038
3039         restricted = true;
3040         have_usernames = false;
3041         userpass_start = rcon_restricted_password.string;
3042         while((userpass_end = strchr(userpass_start, ' ')))
3043         {
3044                 have_usernames = true;
3045                 strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
3046                 if(buf[0])  // Ignore empty entries due to leading/duplicate space.
3047                         if(comparator(peeraddress, buf, password, cs, cslen))
3048                                 goto check;
3049                 userpass_start = userpass_end + 1;
3050         }
3051         if(userpass_start[0])  // Ignore empty trailing entry due to trailing space or password not set.
3052         {
3053                 userpass_end = userpass_start + strlen(userpass_start);
3054                 if(comparator(peeraddress, userpass_start, password, cs, cslen))
3055                         goto check;
3056         }
3057         
3058         return NULL; // DENIED
3059
3060 check:
3061         for(text = s; text != endpos; ++text)
3062                 if((signed char) *text > 0 && ((signed char) *text < (signed char) ' ' || *text == ';'))
3063                         return NULL; // block possible exploits against the parser/alias expansion
3064
3065         while(s != endpos)
3066         {
3067                 size_t l = strlen(s);
3068                 if(l)
3069                 {
3070                         hasquotes = (strchr(s, '"') != NULL);
3071                         // sorry, we can't allow these substrings in wildcard expressions,
3072                         // as they can mess with the argument counts
3073                         text = rcon_restricted_commands.string;
3074                         while(COM_ParseToken_Console(&text))
3075                         {
3076                                 // com_token now contains a pattern to check for...
3077                                 if(strchr(com_token, '*') || strchr(com_token, '?')) // wildcard expression, * can only match a SINGLE argument
3078                                 {
3079                                         if(!hasquotes)
3080                                                 if(matchpattern_with_separator(s, com_token, true, " ", true)) // note how we excluded tab, newline etc. above
3081                                                         goto match;
3082                                 }
3083                                 else if(strchr(com_token, ' ')) // multi-arg expression? must match in whole
3084                                 {
3085                                         if(!strcmp(com_token, s))
3086                                                 goto match;
3087                                 }
3088                                 else // single-arg expression? must match the beginning of the command
3089                                 {
3090                                         if(!strcmp(com_token, s))
3091                                                 goto match;
3092                                         if(!memcmp(va(vabuf, sizeof(vabuf), "%s ", com_token), s, strlen(com_token) + 1))
3093                                                 goto match;
3094                                 }
3095                         }
3096                         // if we got here, nothing matched!
3097                         return NULL;
3098                 }
3099 match:
3100                 s += l + 1;
3101         }
3102
3103 allow:
3104         userpass_startpass = strchr(userpass_start, ':');
3105         if(have_usernames && userpass_startpass && userpass_startpass < userpass_end)
3106                 return va(vabuf, sizeof(vabuf), "%srcon (username %.*s)", restricted ? "restricted " : "", (int)(userpass_startpass-userpass_start), userpass_start);
3107
3108         return va(vabuf, sizeof(vabuf), "%srcon", restricted ? "restricted " : "");
3109 }
3110
3111 static void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const char *addressstring2, const char *userlevel, const char *s, const char *endpos, qbool proquakeprotocol)
3112 {
3113         if(userlevel)
3114         {
3115                 // looks like a legitimate rcon command with the correct password
3116                 const char *s_ptr = s;
3117                 Con_Printf("server received %s command from %s: ", userlevel, host_client ? host_client->name : addressstring2);
3118                 while(s_ptr != endpos)
3119                 {
3120                         size_t l = strlen(s_ptr);
3121                         if(l)
3122                                 Con_Printf(" %s;", s_ptr);
3123                         s_ptr += l + 1;
3124                 }
3125                 Con_Printf("\n");
3126
3127                 if (!host_client || !host_client->netconnection || LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
3128                         Con_Rcon_Redirect_Init(mysocket, peeraddress, proquakeprotocol);
3129                 while(s != endpos)
3130                 {
3131                         size_t l = strlen(s);
3132                         if(l)
3133                         {
3134                                 client_t *host_client_save = host_client;
3135                                 Cmd_ExecuteString(cmd_local, s, src_local, true);
3136                                 host_client = host_client_save;
3137                                 // in case it is a command that changes host_client (like restart)
3138                         }
3139                         s += l + 1;
3140                 }
3141                 Con_Rcon_Redirect_End();
3142         }
3143         else
3144         {
3145                 Con_Printf("server denied rcon access to %s\n", host_client ? host_client->name : addressstring2);
3146         }
3147 }
3148
3149 static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
3150 {
3151         int i, ret, clientnum, best;
3152         double besttime;
3153         char *string, response[2800], addressstring2[128];
3154         static char stringbuf[16384]; // server only
3155         qbool islocal = (LHNETADDRESS_GetAddressType(peeraddress) == LHNETADDRESSTYPE_LOOP);
3156         char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
3157         size_t sendlength, response_len;
3158         char infostringvalue[MAX_INPUTLINE];
3159
3160         if (!sv.active)
3161                 return false;
3162
3163         // convert the address to a string incase we need it
3164         LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
3165
3166         // see if we can identify the sender as a local player
3167         // (this is necessary for rcon to send a reliable reply if the client is
3168         //  actually on the server, not sending remotely)
3169         for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
3170                 if (host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
3171                         break;
3172         if (i == svs.maxclients)
3173                 host_client = NULL;
3174
3175         if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
3176         {
3177                 // received a command string - strip off the packaging and put it
3178                 // into our string buffer with NULL termination
3179                 data += 4;
3180                 length -= 4;
3181                 length = min(length, (int)sizeof(stringbuf) - 1);
3182                 memcpy(stringbuf, data, length);
3183                 stringbuf[length] = 0;
3184                 string = stringbuf;
3185
3186                 if (developer_extra.integer)
3187                 {
3188                         Con_Printf("NetConn_ServerParsePacket: %s sent us a command:\n", addressstring2);
3189                         Com_HexDumpToConsole(data, length);
3190                 }
3191
3192                 sendlength = sizeof(senddata) - 4;
3193                 switch(Crypto_ServerParsePacket(string, length, senddata+4, &sendlength, peeraddress))
3194                 {
3195                         case CRYPTO_NOMATCH:
3196                                 // nothing to do
3197                                 break;
3198                         case CRYPTO_MATCH:
3199                                 if(sendlength)
3200                                 {
3201                                         memcpy(senddata, "\377\377\377\377", 4);
3202                                         NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
3203                                 }
3204                                 break;
3205                         case CRYPTO_DISCARD:
3206                                 if(sendlength)
3207                                 {
3208                                         memcpy(senddata, "\377\377\377\377", 4);
3209                                         NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
3210                                 }
3211                                 return true;
3212                                 break;
3213                         case CRYPTO_REPLACE:
3214                                 string = senddata+4;
3215                                 length = (int)sendlength;
3216                                 break;
3217                 }
3218
3219                 if (length >= 12 && !memcmp(string, "getchallenge", 12) && (islocal || sv_public.integer > -3))
3220                 {
3221                         for (i = 0, best = 0, besttime = host.realtime;i < MAX_CHALLENGES;i++)
3222                         {
3223                                 if(challenges[i].time > 0)
3224                                         if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address))
3225                                                 break;
3226                                 if (besttime > challenges[i].time)
3227                                         besttime = challenges[best = i].time;
3228                         }
3229                         // if we did not find an exact match, choose the oldest and
3230                         // update address and string
3231                         if (i == MAX_CHALLENGES)
3232                         {
3233                                 i = best;
3234                                 challenges[i].address = *peeraddress;
3235                                 NetConn_BuildChallengeString(challenges[i].string, sizeof(challenges[i].string));
3236                         }
3237                         else
3238                         {
3239                                 // flood control: drop if requesting challenge too often
3240                                 if(challenges[i].time > host.realtime - net_challengefloodblockingtimeout.value)
3241                                         return true;
3242                         }
3243                         challenges[i].time = host.realtime;
3244                         // send the challenge
3245                         memcpy(response, "\377\377\377\377", 4);
3246                         dpsnprintf(response+4, sizeof(response)-4, "challenge %s", challenges[i].string);
3247                         response_len = strlen(response) + 1;
3248                         Crypto_ServerAppendToChallenge(string, length, response, &response_len, sizeof(response));
3249                         NetConn_Write(mysocket, response, (int)response_len, peeraddress);
3250                         return true;
3251                 }
3252                 if (length > 8 && !memcmp(string, "connect\\", 8))
3253                 {
3254                         char *s;
3255                         client_t *client;
3256                         crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3257                         string += 7;
3258                         length -= 7;
3259
3260                         if(crypto && crypto->authenticated)
3261                         {
3262                                 // no need to check challenge
3263                                 if(crypto_developer.integer)
3264                                 {
3265                                         Con_Printf("%s connection to %s is being established: client is %s@%s%.*s, I am %.*s@%s%.*s\n",
3266                                                         crypto->use_aes ? "Encrypted" : "Authenticated",
3267                                                         addressstring2,
3268                                                         crypto->client_idfp[0] ? crypto->client_idfp : "-",
3269                                                         (crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
3270                                                         crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-",
3271                                                         crypto_keyfp_recommended_length, crypto->server_idfp[0] ? crypto->server_idfp : "-",
3272                                                         (crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
3273                                                         crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-"
3274                                                   );
3275                                 }
3276                         }
3277                         else
3278                         {
3279                                 if ((s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue))))
3280                                 {
3281                                         // validate the challenge
3282                                         for (i = 0;i < MAX_CHALLENGES;i++)
3283                                                 if(challenges[i].time > 0)
3284                                                         if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strcmp(challenges[i].string, s))
3285                                                                 break;
3286                                         // if the challenge is not recognized, drop the packet
3287                                         if (i == MAX_CHALLENGES)
3288                                                 return true;
3289                                 }
3290                         }
3291
3292                         if((s = InfoString_GetValue(string, "message", infostringvalue, sizeof(infostringvalue))))
3293                                 Con_DPrintf("Connecting client %s sent us the message: %s\n", addressstring2, s);
3294
3295                         if(!(islocal || sv_public.integer > -2))
3296                         {
3297                                 if (developer_extra.integer)
3298                                         Con_Printf("Datagram_ParseConnectionless: sending \"reject %s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3299                                 memcpy(response, "\377\377\377\377", 4);
3300                                 dpsnprintf(response+4, sizeof(response)-4, "reject %s", sv_public_rejectreason.string);
3301                                 NetConn_WriteString(mysocket, response, peeraddress);
3302                                 return true;
3303                         }
3304
3305                         // check engine protocol
3306                         if(!(s = InfoString_GetValue(string, "protocol", infostringvalue, sizeof(infostringvalue))) || strcmp(s, "darkplaces 3"))
3307                         {
3308                                 if (developer_extra.integer)
3309                                         Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
3310                                 NetConn_WriteString(mysocket, "\377\377\377\377reject Wrong game protocol.", peeraddress);
3311                                 return true;
3312                         }
3313
3314                         // see if this is a duplicate connection request or a disconnected
3315                         // client who is rejoining to the same client slot
3316                         for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3317                         {
3318                                 if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
3319                                 {
3320                                         // this is a known client...
3321                                         if(crypto && crypto->authenticated)
3322                                         {
3323                                                 // reject if changing key!
3324                                                 if(client->netconnection->crypto.authenticated)
3325                                                 {
3326                                                         if(
3327                                                                         strcmp(client->netconnection->crypto.client_idfp, crypto->client_idfp)
3328                                                                         ||
3329                                                                         strcmp(client->netconnection->crypto.server_idfp, crypto->server_idfp)
3330                                                                         ||
3331                                                                         strcmp(client->netconnection->crypto.client_keyfp, crypto->client_keyfp)
3332                                                                         ||
3333                                                                         strcmp(client->netconnection->crypto.server_keyfp, crypto->server_keyfp)
3334                                                           )
3335                                                         {
3336                                                                 if (developer_extra.integer)
3337                                                                         Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to change key of crypto.\" to %s.\n", addressstring2);
3338                                                                 NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to change key of crypto.", peeraddress);
3339                                                                 return true;
3340                                                         }
3341                                                 }
3342                                         }
3343                                         else
3344                                         {
3345                                                 // reject if downgrading!
3346                                                 if(client->netconnection->crypto.authenticated)
3347                                                 {
3348                                                         if (developer_extra.integer)
3349                                                                 Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3350                                                         NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to downgrade crypto.", peeraddress);
3351                                                         return true;
3352                                                 }
3353                                         }
3354                                         if (client->begun)
3355                                         {
3356                                                 // client crashed and is coming back,
3357                                                 // keep their stuff intact
3358                                                 if (developer_extra.integer)
3359                                                         Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", addressstring2);
3360                                                 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3361                                                 if(crypto && crypto->authenticated)
3362                                                         Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3363                                                 SV_SendServerinfo(client);
3364                                         }
3365                                         else
3366                                         {
3367                                                 // client is still trying to connect,
3368                                                 // so we send a duplicate reply
3369                                                 if (developer_extra.integer)
3370                                                         Con_Printf("Datagram_ParseConnectionless: sending duplicate accept to %s.\n", addressstring2);
3371                                                 if(crypto && crypto->authenticated)
3372                                                         Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3373                                                 NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3374                                         }
3375                                         return true;
3376                                 }
3377                         }
3378
3379                         if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3380                                 return true;
3381
3382                         // find an empty client slot for this new client
3383                         for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3384                         {
3385                                 netconn_t *conn;
3386                                 if (!client->active && (conn = NetConn_Open(mysocket, peeraddress)))
3387                                 {
3388                                         // allocated connection
3389                                         if (developer_extra.integer)
3390                                                 Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
3391                                         NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3392                                         // now set up the client
3393                                         if(crypto && crypto->authenticated)
3394                                                 Crypto_FinishInstance(&conn->crypto, crypto);
3395                                         SV_ConnectClient(clientnum, conn);
3396                                         NetConn_Heartbeat(1);
3397                                         return true;
3398                                 }
3399                         }
3400
3401                         // no empty slots found - server is full
3402                         if (developer_extra.integer)
3403                                 Con_Printf("Datagram_ParseConnectionless: sending \"reject Server is full.\" to %s.\n", addressstring2);
3404                         NetConn_WriteString(mysocket, "\377\377\377\377reject Server is full.", peeraddress);
3405
3406                         return true;
3407                 }
3408                 if (length >= 7 && !memcmp(string, "getinfo", 7) && (islocal || sv_public.integer > -1))
3409                 {
3410                         const char *challenge = NULL;
3411
3412                         if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3413                                 return true;
3414
3415                         // If there was a challenge in the getinfo message
3416                         if (length > 8 && string[7] == ' ')
3417                                 challenge = string + 8;
3418
3419                         if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), false))
3420                         {
3421                                 if (developer_extra.integer)
3422                                         Con_DPrintf("Sending reply to master %s - %s\n", addressstring2, response);
3423                                 NetConn_WriteString(mysocket, response, peeraddress);
3424                         }
3425                         return true;
3426                 }
3427                 if (length >= 9 && !memcmp(string, "getstatus", 9) && (islocal || sv_public.integer > -1))
3428                 {
3429                         const char *challenge = NULL;
3430
3431                         if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3432                                 return true;
3433
3434                         // If there was a challenge in the getinfo message
3435                         if (length > 10 && string[9] == ' ')
3436                                 challenge = string + 10;
3437
3438                         if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), true))
3439                         {
3440                                 if (developer_extra.integer)
3441                                         Con_DPrintf("Sending reply to client %s - %s\n", addressstring2, response);
3442                                 NetConn_WriteString(mysocket, response, peeraddress);
3443                         }
3444                         return true;
3445                 }
3446                 if (length >= 37 && !memcmp(string, "srcon HMAC-MD4 TIME ", 20))
3447                 {
3448                         char *password = string + 20;
3449                         char *timeval = string + 37;
3450                         char *s = strchr(timeval, ' ');
3451                         char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3452                         const char *userlevel;
3453
3454                         if(rcon_secure.integer > 1)
3455                                 return true;
3456
3457                         if(!s)
3458                                 return true; // invalid packet
3459                         ++s;
3460
3461                         userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_time_matching, timeval, endpos - timeval - 1); // not including the appended \0 into the HMAC
3462                         RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3463                         return true;
3464                 }
3465                 if (length >= 42 && !memcmp(string, "srcon HMAC-MD4 CHALLENGE ", 25))
3466                 {
3467                         char *password = string + 25;
3468                         char *challenge = string + 42;
3469                         char *s = strchr(challenge, ' ');
3470                         char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3471                         const char *userlevel;
3472                         if(!s)
3473                                 return true; // invalid packet
3474                         ++s;
3475
3476                         userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_challenge_matching, challenge, endpos - challenge - 1); // not including the appended \0 into the HMAC
3477                         RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3478                         return true;
3479                 }
3480                 if (length >= 5 && !memcmp(string, "rcon ", 5))
3481                 {
3482                         int j;
3483                         char *s = string + 5;
3484                         char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3485                         char password[64];
3486
3487                         if(rcon_secure.integer > 0)
3488                                 return true;
3489
3490                         for (j = 0;!ISWHITESPACE(*s);s++)
3491                                 if (j < (int)sizeof(password) - 1)
3492                                         password[j++] = *s;
3493                         if(ISWHITESPACE(*s) && s != endpos) // skip leading ugly space
3494                                 ++s;
3495                         password[j] = 0;
3496                         if (!ISWHITESPACE(password[0]))
3497                         {
3498                                 const char *userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3499                                 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3500                         }
3501                         return true;
3502                 }
3503                 if (!strncmp(string, "extResponse ", 12))
3504                 {
3505                         ++sv_net_extresponse_count;
3506                         if(sv_net_extresponse_count > NET_EXTRESPONSE_MAX)
3507                                 sv_net_extresponse_count = NET_EXTRESPONSE_MAX;
3508                         sv_net_extresponse_last = (sv_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
3509                         dpsnprintf(sv_net_extresponse[sv_net_extresponse_last], sizeof(sv_net_extresponse[sv_net_extresponse_last]), "'%s' %s", addressstring2, string + 12);
3510                         return true;
3511                 }
3512                 if (!strncmp(string, "ping", 4))
3513                 {
3514                         if (developer_extra.integer)
3515                                 Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
3516                         NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
3517                         return true;
3518                 }
3519                 if (!strncmp(string, "ack", 3))
3520                         return true;
3521                 // we may not have liked the packet, but it was a command packet, so
3522                 // we're done processing this packet now
3523                 return true;
3524         }
3525         // netquake control packets, supported for compatibility only, and only
3526         // when running game protocols that are normally served via this connection
3527         // protocol
3528         // (this protects more modern protocols against being used for
3529         //  Quake packet flood Denial Of Service attacks)
3530         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)
3531         {
3532                 int c;
3533                 int protocolnumber;
3534                 const char *protocolname;
3535                 client_t *knownclient;
3536                 client_t *newclient;
3537                 data += 4;
3538                 length -= 4;
3539                 SZ_Clear(&sv_message);
3540                 SZ_Write(&sv_message, data, length);
3541                 MSG_BeginReading(&sv_message);
3542                 c = MSG_ReadByte(&sv_message);
3543                 switch (c)
3544                 {
3545                 case CCREQ_CONNECT:
3546                         if (developer_extra.integer)
3547                                 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
3548                         if(!(islocal || sv_public.integer > -2))
3549                         {
3550                                 if (developer_extra.integer)
3551                                         Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"%s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3552                                 SZ_Clear(&sv_message);
3553                                 // save space for the header, filled in later
3554                                 MSG_WriteLong(&sv_message, 0);
3555                                 MSG_WriteByte(&sv_message, CCREP_REJECT);
3556                                 MSG_WriteUnterminatedString(&sv_message, sv_public_rejectreason.string);
3557                                 MSG_WriteString(&sv_message, "\n");
3558                                 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3559                                 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3560                                 SZ_Clear(&sv_message);
3561                                 break;
3562                         }
3563
3564                         protocolname = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3565                         protocolnumber = MSG_ReadByte(&sv_message);
3566                         if (strcmp(protocolname, "QUAKE") || protocolnumber != NET_PROTOCOL_VERSION)
3567                         {
3568                                 if (developer_extra.integer)
3569                                         Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
3570                                 SZ_Clear(&sv_message);
3571                                 // save space for the header, filled in later
3572                                 MSG_WriteLong(&sv_message, 0);
3573                                 MSG_WriteByte(&sv_message, CCREP_REJECT);
3574                                 MSG_WriteString(&sv_message, "Incompatible version.\n");
3575                                 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3576                                 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3577                                 SZ_Clear(&sv_message);
3578                                 break;
3579                         }
3580
3581                         // see if this connect request comes from a known client
3582                         for (clientnum = 0, knownclient = svs.clients;clientnum < svs.maxclients;clientnum++, knownclient++)
3583                         {
3584                                 if (knownclient->netconnection && LHNETADDRESS_Compare(peeraddress, &knownclient->netconnection->peeraddress) == 0)
3585                                 {
3586                                         // this is either a duplicate connection request
3587                                         // or coming back from a timeout
3588                                         // (if so, keep their stuff intact)
3589
3590                                         crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3591                                         if((crypto && crypto->authenticated) || knownclient->netconnection->crypto.authenticated)
3592                                         {
3593                                                 if (developer_extra.integer)
3594                                                         Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3595                                                 SZ_Clear(&sv_message);
3596                                                 // save space for the header, filled in later
3597                                                 MSG_WriteLong(&sv_message, 0);
3598                                                 MSG_WriteByte(&sv_message, CCREP_REJECT);
3599                                                 MSG_WriteString(&sv_message, "Attempt to downgrade crypto.\n");
3600                                                 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3601                                                 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3602                                                 SZ_Clear(&sv_message);
3603                                                 return true;
3604                                         }
3605
3606                                         // send a reply
3607                                         if (developer_extra.integer)
3608                                                 Con_DPrintf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
3609                                         SZ_Clear(&sv_message);
3610                                         // save space for the header, filled in later
3611                                         MSG_WriteLong(&sv_message, 0);
3612                                         MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3613                                         MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(knownclient->netconnection->mysocket)));
3614                                         StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3615                                         NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3616                                         SZ_Clear(&sv_message);
3617
3618                                         // if client is already spawned, re-send the
3619                                         // serverinfo message as they'll need it to play
3620                                         if (knownclient->begun)
3621                                                 SV_SendServerinfo(knownclient);
3622                                         return true;
3623                                 }
3624                         }
3625
3626                         // this is a new client, check for connection flood
3627                         if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3628                                 break;
3629
3630                         // find a slot for the new client
3631                         for (clientnum = 0, newclient = svs.clients;clientnum < svs.maxclients;clientnum++, newclient++)
3632                         {
3633                                 netconn_t *conn;
3634                                 if (!newclient->active && (newclient->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
3635                                 {
3636                                         // connect to the client
3637                                         // everything is allocated, just fill in the details
3638                                         strlcpy (conn->address, addressstring2, sizeof (conn->address));
3639                                         if (developer_extra.integer)
3640                                                 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
3641                                         // send back the info about the server connection
3642                                         SZ_Clear(&sv_message);
3643                                         // save space for the header, filled in later
3644                                         MSG_WriteLong(&sv_message, 0);
3645                                         MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3646                                         MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
3647                                         StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3648                                         NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3649                                         SZ_Clear(&sv_message);
3650                                         // now set up the client struct
3651                                         SV_ConnectClient(clientnum, conn);
3652                                         NetConn_Heartbeat(1);
3653                                         return true;
3654                                 }
3655                         }
3656
3657                         if (developer_extra.integer)
3658                                 Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
3659                         // no room; try to let player know
3660                         SZ_Clear(&sv_message);
3661                         // save space for the header, filled in later
3662                         MSG_WriteLong(&sv_message, 0);
3663                         MSG_WriteByte(&sv_message, CCREP_REJECT);
3664                         MSG_WriteString(&sv_message, "Server is full.\n");
3665                         StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3666                         NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3667                         SZ_Clear(&sv_message);
3668                         break;
3669                 case CCREQ_SERVER_INFO:
3670                         if (developer_extra.integer)
3671                                 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
3672                         if(!(islocal || sv_public.integer > -1))
3673                                 break;
3674
3675                         if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3676                                 break;
3677
3678                         if (sv.active && !strcmp(MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), "QUAKE"))
3679                         {
3680                                 int numclients;
3681                                 char myaddressstring[128];
3682                                 if (developer_extra.integer)
3683                                         Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
3684                                 SZ_Clear(&sv_message);
3685                                 // save space for the header, filled in later
3686                                 MSG_WriteLong(&sv_message, 0);
3687                                 MSG_WriteByte(&sv_message, CCREP_SERVER_INFO);
3688                                 LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), myaddressstring, sizeof(myaddressstring), true);
3689                                 MSG_WriteString(&sv_message, myaddressstring);
3690                                 MSG_WriteString(&sv_message, hostname.string);
3691                                 MSG_WriteString(&sv_message, sv.name);
3692                                 // How many clients are there?
3693                                 for (i = 0, numclients = 0;i < svs.maxclients;i++)
3694                                         if (svs.clients[i].active)
3695                                                 numclients++;
3696                                 MSG_WriteByte(&sv_message, numclients);
3697                                 MSG_WriteByte(&sv_message, svs.maxclients);
3698                                 MSG_WriteByte(&sv_message, NET_PROTOCOL_VERSION);
3699                                 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3700                                 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3701                                 SZ_Clear(&sv_message);
3702                         }
3703                         break;
3704                 case CCREQ_PLAYER_INFO:
3705                         if (developer_extra.integer)
3706                                 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
3707                         if(!(islocal || sv_public.integer > -1))
3708                                 break;
3709
3710                         if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3711                                 break;
3712
3713                         if (sv.active)
3714                         {
3715                                 int playerNumber, activeNumber, clientNumber;
3716                                 client_t *client;
3717
3718                                 playerNumber = MSG_ReadByte(&sv_message);
3719                                 activeNumber = -1;
3720                                 for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
3721                                         if (client->active && ++activeNumber == playerNumber)
3722                                                 break;
3723                                 if (clientNumber != svs.maxclients)
3724                                 {
3725                                         SZ_Clear(&sv_message);
3726                                         // save space for the header, filled in later
3727                                         MSG_WriteLong(&sv_message, 0);
3728                                         MSG_WriteByte(&sv_message, CCREP_PLAYER_INFO);
3729                                         MSG_WriteByte(&sv_message, playerNumber);
3730                                         MSG_WriteString(&sv_message, client->name);
3731                                         MSG_WriteLong(&sv_message, client->colors);
3732                                         MSG_WriteLong(&sv_message, client->frags);
3733                                         MSG_WriteLong(&sv_message, (int)(host.realtime - client->connecttime));
3734                                         if(sv_status_privacy.integer)
3735                                                 MSG_WriteString(&sv_message, client->netconnection ? "hidden" : "botclient");
3736                                         else
3737                                                 MSG_WriteString(&sv_message, client->netconnection ? client->netconnection->address : "botclient");
3738                                         StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3739                                         NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3740                                         SZ_Clear(&sv_message);
3741                                 }
3742                         }
3743                         break;
3744                 case CCREQ_RULE_INFO:
3745                         if (developer_extra.integer)
3746                                 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
3747                         if(!(islocal || sv_public.integer > -1))
3748                                 break;
3749
3750                         // no flood check here, as it only returns one cvar for one cvar and clients may iterate quickly
3751
3752                         if (sv.active)
3753                         {
3754                                 char *prevCvarName;
3755                                 cvar_t *var;
3756
3757                                 // find the search start location
3758                                 prevCvarName = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3759                                 var = Cvar_FindVarAfter(&cvars_all, prevCvarName, CF_NOTIFY);
3760
3761                                 // send the response
3762                                 SZ_Clear(&sv_message);
3763                                 // save space for the header, filled in later
3764                                 MSG_WriteLong(&sv_message, 0);
3765                                 MSG_WriteByte(&sv_message, CCREP_RULE_INFO);
3766                                 if (var)
3767                                 {
3768                                         MSG_WriteString(&sv_message, var->name);
3769                                         MSG_WriteString(&sv_message, var->string);
3770                                 }
3771                                 StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3772                                 NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3773                                 SZ_Clear(&sv_message);
3774                         }
3775                         break;
3776                 case CCREQ_RCON:
3777                         if (developer_extra.integer)
3778                                 Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RCON from %s.\n", addressstring2);
3779                         if (sv.active && !rcon_secure.integer)
3780                         {
3781                                 char password[2048];
3782                                 char cmd[2048];
3783                                 char *s;
3784                                 char *endpos;
3785                                 const char *userlevel;
3786                                 strlcpy(password, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(password));
3787                                 strlcpy(cmd, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(cmd));
3788                                 s = cmd;
3789                                 endpos = cmd + strlen(cmd) + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3790                                 userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3791                                 RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, true);
3792                                 return true;
3793                         }
3794                         break;
3795                 default:
3796                         break;
3797                 }
3798                 SZ_Clear(&sv_message);
3799                 // we may not have liked the packet, but it was a valid control
3800                 // packet, so we're done processing this packet now
3801                 return true;
3802         }
3803         if (host_client)
3804         {
3805                 if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length, sv.protocol, host_client->begun ? net_messagetimeout.value : net_connecttimeout.value)) == 2)
3806                 {
3807                         SV_ReadClientMessage();
3808                         return ret;
3809                 }
3810         }
3811         return 0;
3812 }
3813
3814 void NetConn_ServerFrame(void)
3815 {
3816         unsigned i;
3817         int length;
3818         lhnetaddress_t peeraddress;
3819         unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
3820
3821         for (i = 0;i < sv_numsockets;i++)
3822                 while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
3823                         NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
3824 }
3825
3826 #ifdef CONFIG_MENU
3827 void NetConn_QueryMasters(qbool querydp, qbool queryqw)
3828 {
3829         unsigned i, j;
3830         unsigned masternum;
3831         lhnetaddress_t masteraddress;
3832         char request[256];
3833         char lookupstring[128];
3834
3835         if (serverlist_cachecount >= SERVERLIST_TOTALSIZE)
3836                 return;
3837
3838         memset(dpmasterstatus, 0, sizeof(*dpmasterstatus));
3839         memset(qwmasterstatus, 0, sizeof(*qwmasterstatus));
3840
3841         if (querydp)
3842         {
3843                 for (i = 0;i < cl_numsockets;i++)
3844                 {
3845                         if (cl_sockets[i])
3846                         {
3847                                 const char *cmdname, *extraoptions;
3848                                 lhnetaddresstype_t af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3849
3850                                 // build the getservers message to send to the dpmaster master servers
3851                                 if (LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == LHNETADDRESSTYPE_INET6)
3852                                 {
3853                                         cmdname = "getserversExt";
3854                                         extraoptions = " ipv4 ipv6";  // ask for IPv4 and IPv6 servers
3855                                 }
3856                                 else
3857                                 {
3858                                         cmdname = "getservers";
3859                                         extraoptions = "";
3860                                 }
3861                                 memcpy(request, "\377\377\377\377", 4);
3862                                 dpsnprintf(request+4, sizeof(request)-4, "%s %s %u empty full%s", cmdname, gamenetworkfiltername, NET_PROTOCOL_VERSION, extraoptions);
3863
3864                                 // search internet
3865                                 for (masternum = 0; masternum < DPMASTER_COUNT; ++masternum)
3866                                 {
3867                                         if(sv_masters[masternum].string[0]
3868                                         && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT)
3869                                         && LHNETADDRESS_GetAddressType(&masteraddress) == af)
3870                                         {
3871                                                 if (serverlist_consoleoutput || net_slist_debug.integer)
3872                                                 {
3873                                                         LHNETADDRESS_ToString(&masteraddress, lookupstring, sizeof(lookupstring), true);
3874                                                         Con_Printf("Querying DP master %s (resolved from %s)\n", lookupstring, sv_masters[masternum].string);
3875                                                 }
3876                                                 masterquerycount++;
3877                                                 NetConn_WriteString(cl_sockets[i], request, &masteraddress);
3878                                                 dpmasterstatus[masternum] = MASTER_TX_QUERY;
3879                                         }
3880                                 }
3881
3882                                 // search favorite servers
3883                                 for(j = 0; j < nFavorites; ++j)
3884                                         if(LHNETADDRESS_GetAddressType(&favorites[j]) == af
3885                                         && LHNETADDRESS_ToString(&favorites[j], lookupstring, sizeof(lookupstring), true))
3886                                                 NetConn_ClientParsePacket_ServerList_PrepareQuery(PROTOCOL_DARKPLACES7, lookupstring, true);
3887                         }
3888                 }
3889         }
3890
3891         // only query QuakeWorld servers when the user wants to
3892         if (queryqw)
3893         {
3894                 dpsnprintf(request, sizeof(request), "c\n");
3895
3896                 for (i = 0;i < cl_numsockets;i++)
3897                 {
3898                         if (cl_sockets[i])
3899                         {
3900                                 lhnetaddresstype_t af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3901
3902                                 // search internet
3903                                 for (masternum = 0; masternum < QWMASTER_COUNT; ++masternum)
3904                                 {
3905                                         if(sv_qwmasters[masternum].string[0]
3906                                         && LHNETADDRESS_FromString(&masteraddress, sv_qwmasters[masternum].string, QWMASTER_PORT)
3907                                         && LHNETADDRESS_GetAddressType(&masteraddress) == af)
3908                                         {
3909                                                 if (serverlist_consoleoutput || net_slist_debug.integer)
3910                                                 {
3911                                                         LHNETADDRESS_ToString(&masteraddress, lookupstring, sizeof(lookupstring), true);
3912                                                         Con_Printf("Querying QW master %s (resolved from %s)\n", lookupstring, sv_qwmasters[masternum].string);
3913                                                 }
3914                                                 masterquerycount++;
3915                                                 NetConn_Write(cl_sockets[i], request, (int)strlen(request) + 1, &masteraddress);
3916                                                 qwmasterstatus[masternum] = MASTER_TX_QUERY;
3917                                         }
3918                                 }
3919
3920                                 // search favorite servers
3921                                 for(j = 0; j < nFavorites; ++j)
3922                                         if(LHNETADDRESS_GetAddressType(&favorites[j]) == af
3923                                         && LHNETADDRESS_ToString(&favorites[j], lookupstring, sizeof(lookupstring), true))
3924                                                 NetConn_ClientParsePacket_ServerList_PrepareQuery(PROTOCOL_QUAKEWORLD, lookupstring, true);
3925                         }
3926                 }
3927         }
3928
3929         if (!masterquerycount)
3930         {
3931                 Con_Print(CON_ERROR "Unable to query master servers, no suitable network sockets active.\n");
3932                 strlcpy(cl_connect_status, "No network", sizeof(cl_connect_status));
3933         }
3934 }
3935 #endif
3936
3937 void NetConn_Heartbeat(int priority)
3938 {
3939         lhnetaddress_t masteraddress;
3940         uint8_t masternum;
3941         lhnetsocket_t *mysocket;
3942
3943         // if it's a state change (client connected), limit next heartbeat to no
3944         // more than 30 sec in the future
3945         if (priority == 1 && nextheartbeattime > host.realtime + 30.0)
3946                 nextheartbeattime = host.realtime + 30.0;
3947
3948         // limit heartbeatperiod to 30 to 270 second range,
3949         // lower limit is to avoid abusing master servers with excess traffic,
3950         // upper limit is to avoid timing out on the master server (which uses
3951         // 300 sec timeout)
3952         if (sv_heartbeatperiod.value < 30)
3953                 Cvar_SetValueQuick(&sv_heartbeatperiod, 30);
3954         if (sv_heartbeatperiod.value > 270)
3955                 Cvar_SetValueQuick(&sv_heartbeatperiod, 270);
3956
3957         // make advertising optional and don't advertise singleplayer games, and
3958         // only send a heartbeat as often as the admin wants
3959         if (sv.active && sv_public.integer > 0 && svs.maxclients >= 2 && (priority > 1 || host.realtime > nextheartbeattime))
3960         {
3961                 nextheartbeattime = host.realtime + sv_heartbeatperiod.value;
3962                 for (masternum = 0; masternum < DPMASTER_COUNT; ++masternum)
3963                         if (sv_masters[masternum].string[0]
3964                         && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT)
3965                         && (mysocket = NetConn_ChooseServerSocketForAddress(&masteraddress)))
3966                                 NetConn_WriteString(mysocket, "\377\377\377\377heartbeat DarkPlaces\x0A", &masteraddress);
3967         }
3968 }
3969
3970 static void Net_Heartbeat_f(cmd_state_t *cmd)
3971 {
3972         if (sv.active)
3973                 NetConn_Heartbeat(2);
3974         else
3975                 Con_Print("No server running, can not heartbeat to master server.\n");
3976 }
3977
3978 static void PrintStats(netconn_t *conn)
3979 {
3980         if ((cls.state == ca_connected && cls.protocol == PROTOCOL_QUAKEWORLD) || (sv.active && sv.protocol == PROTOCOL_QUAKEWORLD))
3981                 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->outgoing_unreliable_sequence, conn->qw.incoming_sequence);
3982         else
3983                 Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->nq.sendSequence, conn->nq.receiveSequence);
3984         Con_Printf("unreliable messages sent   = %i\n", conn->unreliableMessagesSent);
3985         Con_Printf("unreliable messages recv   = %i\n", conn->unreliableMessagesReceived);
3986         Con_Printf("reliable messages sent     = %i\n", conn->reliableMessagesSent);
3987         Con_Printf("reliable messages received = %i\n", conn->reliableMessagesReceived);
3988         Con_Printf("packetsSent                = %i\n", conn->packetsSent);
3989         Con_Printf("packetsReSent              = %i\n", conn->packetsReSent);
3990         Con_Printf("packetsReceived            = %i\n", conn->packetsReceived);
3991         Con_Printf("receivedDuplicateCount     = %i\n", conn->receivedDuplicateCount);
3992         Con_Printf("droppedDatagrams           = %i\n", conn->droppedDatagrams);
3993 }
3994
3995 void Net_Stats_f(cmd_state_t *cmd)
3996 {
3997         netconn_t *conn;
3998         Con_Print("connections                =\n");
3999         for (conn = netconn_list;conn;conn = conn->next)
4000                 PrintStats(conn);
4001 }
4002
4003 #ifdef CONFIG_MENU
4004 void Net_Refresh_f(cmd_state_t *cmd)
4005 {
4006         if (m_state != m_slist)
4007         {
4008                 Con_Print("Sending new requests to DP master servers\n");
4009                 ServerList_QueryList(false, true, false, true);
4010                 Con_Print("Listening for replies...\n");
4011         }
4012         else
4013                 ServerList_QueryList(false, true, false, false);
4014 }
4015
4016 void Net_Slist_f(cmd_state_t *cmd)
4017 {
4018         ServerList_ResetMasks();
4019         serverlist_sortbyfield = SLIF_PING;
4020         serverlist_sortflags = 0;
4021         if (m_state != m_slist)
4022         {
4023                 Con_Print("Sending requests to DP master servers\n");
4024                 ServerList_QueryList(true, true, false, true);
4025                 Con_Print("Listening for replies...\n");
4026         }
4027         else
4028                 ServerList_QueryList(true, true, false, false);
4029 }
4030
4031 void Net_SlistQW_f(cmd_state_t *cmd)
4032 {
4033         ServerList_ResetMasks();
4034         serverlist_sortbyfield = SLIF_PING;
4035         serverlist_sortflags = 0;
4036         if (m_state != m_slist)
4037         {
4038                 Con_Print("Sending requests to QW master servers\n");
4039                 ServerList_QueryList(true, false, true, true);
4040                 Con_Print("Listening for replies...\n");
4041         }
4042         else
4043                 ServerList_QueryList(true, false, true, false);
4044 }
4045 #endif
4046
4047 void NetConn_Init(void)
4048 {
4049         int i;
4050         lhnetaddress_t tempaddress;
4051         netconn_mempool = Mem_AllocPool("network connections", 0, NULL);
4052         Cmd_AddCommand(CF_SHARED, "net_stats", Net_Stats_f, "print network statistics");
4053 #ifdef CONFIG_MENU
4054         Cmd_AddCommand(CF_CLIENT, "net_slist", Net_Slist_f, "query dp master servers and print all server information");
4055         Cmd_AddCommand(CF_CLIENT, "net_slistqw", Net_SlistQW_f, "query qw master servers and print all server information");
4056         Cmd_AddCommand(CF_CLIENT, "net_refresh", Net_Refresh_f, "query dp master servers and refresh all server information");
4057 #endif
4058         Cmd_AddCommand(CF_SERVER, "heartbeat", Net_Heartbeat_f, "send a heartbeat to the master server (updates your server information)");
4059         Cvar_RegisterVariable(&net_test);
4060         Cvar_RegisterVariable(&net_usesizelimit);
4061         Cvar_RegisterVariable(&net_burstreserve);
4062         Cvar_RegisterVariable(&rcon_restricted_password);
4063         Cvar_RegisterVariable(&rcon_restricted_commands);
4064         Cvar_RegisterVariable(&rcon_secure_maxdiff);
4065
4066 #ifdef CONFIG_MENU
4067         Cvar_RegisterVariable(&net_slist_debug);
4068         Cvar_RegisterVariable(&net_slist_favorites);
4069         Cvar_RegisterCallback(&net_slist_favorites, NetConn_UpdateFavorites_c);
4070         Cvar_RegisterVariable(&net_slist_interval);
4071         Cvar_RegisterVariable(&net_slist_maxping);
4072         Cvar_RegisterVariable(&net_slist_maxtries);
4073         Cvar_RegisterVariable(&net_slist_pause);
4074         Cvar_RegisterCallback(&net_slist_pause, ServerList_RebuildViewList);
4075         Cvar_RegisterVariable(&net_slist_queriespersecond);
4076         Cvar_RegisterVariable(&net_slist_queriesperframe);
4077         Cvar_RegisterVariable(&net_slist_timeout);
4078 #endif
4079
4080 #ifdef IP_TOS // register cvar only if supported
4081         Cvar_RegisterVariable(&net_tos_dscp);
4082 #endif
4083         Cvar_RegisterVariable(&net_messagetimeout);
4084         Cvar_RegisterVariable(&net_connecttimeout);
4085         Cvar_RegisterVariable(&net_connectfloodblockingtimeout);
4086         Cvar_RegisterVariable(&net_challengefloodblockingtimeout);
4087         Cvar_RegisterVariable(&net_getstatusfloodblockingtimeout);
4088         Cvar_RegisterVariable(&net_sourceaddresscheck);
4089         Cvar_RegisterVariable(&net_fakelag);
4090         Cvar_RegisterVariable(&net_fakeloss_send);
4091         Cvar_RegisterVariable(&net_fakeloss_receive);
4092         Cvar_RegisterVirtual(&net_fakelag, "cl_netlocalping");
4093         Cvar_RegisterVirtual(&net_fakeloss_send, "cl_netpacketloss_send");
4094         Cvar_RegisterVirtual(&net_fakeloss_receive, "cl_netpacketloss_receive");
4095         Cvar_RegisterVariable(&hostname);
4096         Cvar_RegisterVariable(&developer_networking);
4097         Cvar_RegisterVariable(&cl_netport);
4098         Cvar_RegisterCallback(&cl_netport, NetConn_CL_UpdateSockets_Callback);
4099         Cvar_RegisterVariable(&sv_netport);
4100         Cvar_RegisterCallback(&sv_netport, NetConn_sv_netport_Callback);
4101         Cvar_RegisterVariable(&net_address);
4102         Cvar_RegisterVariable(&net_address_ipv6);
4103         Cvar_RegisterVariable(&sv_public);
4104         Cvar_RegisterVariable(&sv_public_rejectreason);
4105         Cvar_RegisterVariable(&sv_heartbeatperiod);
4106         for (uint8_t masternum = 0; masternum < DPMASTER_COUNT; ++masternum)
4107                 Cvar_RegisterVariable(&sv_masters[masternum]);
4108         Cvar_RegisterVariable(&gameversion);
4109         Cvar_RegisterVariable(&gameversion_min);
4110         Cvar_RegisterVariable(&gameversion_max);
4111 // COMMANDLINEOPTION: Server: -ip <ipaddress> sets the ip address of this machine for purposes of networking (default 0.0.0.0 also known as INADDR_ANY), use only if you have multiple network adapters and need to choose one specifically.
4112         if ((i = Sys_CheckParm("-ip")) && i + 1 < sys.argc)
4113         {
4114                 if (LHNETADDRESS_FromString(&tempaddress, sys.argv[i + 1], 0) == 1)
4115                 {
4116                         Con_Printf("-ip option used, setting net_address to \"%s\"\n", sys.argv[i + 1]);
4117                         Cvar_SetQuick(&net_address, sys.argv[i + 1]);
4118                 }
4119                 else
4120                         Con_Printf(CON_ERROR "-ip option used, but unable to parse the address \"%s\"\n", sys.argv[i + 1]);
4121         }
4122 // COMMANDLINEOPTION: Server: -port <portnumber> sets the port to use for a server (default 26000, the same port as QUAKE itself), useful if you host multiple servers on your machine
4123         if (((i = Sys_CheckParm("-port")) || (i = Sys_CheckParm("-ipport")) || (i = Sys_CheckParm("-udpport"))) && i + 1 < sys.argc)
4124         {
4125                 i = atoi(sys.argv[i + 1]);
4126                 if (i >= 0 && i < 65536)
4127                 {
4128                         Con_Printf("-port option used, setting port cvar to %i\n", i);
4129                         Cvar_SetValueQuick(&sv_netport, i);
4130                 }
4131                 else
4132                         Con_Printf(CON_ERROR "-port option used, but %i is not a valid port number\n", i);
4133         }
4134         cl_numsockets = 0;
4135         sv_numsockets = 0;
4136         cl_message.data = cl_message_buf;
4137         cl_message.maxsize = sizeof(cl_message_buf);
4138         cl_message.cursize = 0;
4139         sv_message.data = sv_message_buf;
4140         sv_message.maxsize = sizeof(sv_message_buf);
4141         sv_message.cursize = 0;
4142         LHNET_Init();
4143         if (Thread_HasThreads())
4144                 netconn_mutex = Thread_CreateMutex();
4145 }
4146
4147 void NetConn_Shutdown(void)
4148 {
4149         NetConn_CloseClientPorts();
4150         NetConn_CloseServerPorts();
4151         LHNET_Shutdown();
4152         if (netconn_mutex)
4153                 Thread_DestroyMutex(netconn_mutex);
4154         netconn_mutex = NULL;
4155 }
4156