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