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