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