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