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