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