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