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