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 	if (!password[0]) {
2743 		Con_Print("^4LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2744 		return false;
2745 	}
2746 
2747 	t1 = (long) time(NULL);
2748 	t2 = strtol(s, NULL, 0);
2749 	if(abs(t1 - t2) > rcon_secure_maxdiff.integer)
2750 		return false;
2751 
2752 	if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2753 		return false;
2754 
2755 	return !memcmp(mdfourbuf, hash, 16);
2756 }
2757 
hmac_mdfour_challenge_matching(lhnetaddress_t * peeraddress,const char * password,const char * hash,const char * s,int slen)2758 static qboolean hmac_mdfour_challenge_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2759 {
2760 	char mdfourbuf[16];
2761 	int i;
2762 
2763 	if (!password[0]) {
2764 		Con_Print("^4LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2765 		return false;
2766 	}
2767 
2768 	if(slen < (int)(sizeof(challenges[0].string)) - 1)
2769 		return false;
2770 
2771 	// validate the challenge
2772 	for (i = 0;i < MAX_CHALLENGES;i++)
2773 		if(challenges[i].time > 0)
2774 			if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strncmp(challenges[i].string, s, sizeof(challenges[0].string) - 1))
2775 				break;
2776 	// if the challenge is not recognized, drop the packet
2777 	if (i == MAX_CHALLENGES)
2778 		return false;
2779 
2780 	if(!HMAC_MDFOUR_16BYTES((unsigned char *) mdfourbuf, (unsigned char *) s, slen, (unsigned char *) password, (int)strlen(password)))
2781 		return false;
2782 
2783 	if(memcmp(mdfourbuf, hash, 16))
2784 		return false;
2785 
2786 	// unmark challenge to prevent replay attacks
2787 	challenges[i].time = 0;
2788 
2789 	return true;
2790 }
2791 
plaintext_matching(lhnetaddress_t * peeraddress,const char * password,const char * hash,const char * s,int slen)2792 static qboolean plaintext_matching(lhnetaddress_t *peeraddress, const char *password, const char *hash, const char *s, int slen)
2793 {
2794 	if (!password[0]) {
2795 		Con_Print("^4LOGIC ERROR: RCon_Authenticate should never call the comparator with an empty password. Please report.\n");
2796 		return false;
2797 	}
2798 
2799 	return !strcmp(password, hash);
2800 }
2801 
2802 /// 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)2803 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)
2804 {
2805 	const char *text, *userpass_start, *userpass_end, *userpass_startpass;
2806 	static char buf[MAX_INPUTLINE];
2807 	qboolean hasquotes;
2808 	qboolean restricted = false;
2809 	qboolean have_usernames = false;
2810 	static char vabuf[1024];
2811 
2812 	userpass_start = rcon_password.string;
2813 	while((userpass_end = strchr(userpass_start, ' ')))
2814 	{
2815 		have_usernames = true;
2816 		strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2817 		if(buf[0])  // Ignore empty entries due to leading/duplicate space.
2818 			if(comparator(peeraddress, buf, password, cs, cslen))
2819 				goto allow;
2820 		userpass_start = userpass_end + 1;
2821 	}
2822 	if(userpass_start[0])  // Ignore empty trailing entry due to trailing space or password not set.
2823 	{
2824 		userpass_end = userpass_start + strlen(userpass_start);
2825 		if(comparator(peeraddress, userpass_start, password, cs, cslen))
2826 			goto allow;
2827 	}
2828 
2829 	restricted = true;
2830 	have_usernames = false;
2831 	userpass_start = rcon_restricted_password.string;
2832 	while((userpass_end = strchr(userpass_start, ' ')))
2833 	{
2834 		have_usernames = true;
2835 		strlcpy(buf, userpass_start, ((size_t)(userpass_end-userpass_start) >= sizeof(buf)) ? (int)(sizeof(buf)) : (int)(userpass_end-userpass_start+1));
2836 		if(buf[0])  // Ignore empty entries due to leading/duplicate space.
2837 			if(comparator(peeraddress, buf, password, cs, cslen))
2838 				goto check;
2839 		userpass_start = userpass_end + 1;
2840 	}
2841 	if(userpass_start[0])  // Ignore empty trailing entry due to trailing space or password not set.
2842 	{
2843 		userpass_end = userpass_start + strlen(userpass_start);
2844 		if(comparator(peeraddress, userpass_start, password, cs, cslen))
2845 			goto check;
2846 	}
2847 
2848 	return NULL; // DENIED
2849 
2850 check:
2851 	for(text = s; text != endpos; ++text)
2852 		if((signed char) *text > 0 && ((signed char) *text < (signed char) ' ' || *text == ';'))
2853 			return NULL; // block possible exploits against the parser/alias expansion
2854 
2855 	while(s != endpos)
2856 	{
2857 		size_t l = strlen(s);
2858 		if(l)
2859 		{
2860 			hasquotes = (strchr(s, '"') != NULL);
2861 			// sorry, we can't allow these substrings in wildcard expressions,
2862 			// as they can mess with the argument counts
2863 			text = rcon_restricted_commands.string;
2864 			while(COM_ParseToken_Console(&text))
2865 			{
2866 				// com_token now contains a pattern to check for...
2867 				if(strchr(com_token, '*') || strchr(com_token, '?')) // wildcard expression, * can only match a SINGLE argument
2868 				{
2869 					if(!hasquotes)
2870 						if(matchpattern_with_separator(s, com_token, true, " ", true)) // note how we excluded tab, newline etc. above
2871 							goto match;
2872 				}
2873 				else if(strchr(com_token, ' ')) // multi-arg expression? must match in whole
2874 				{
2875 					if(!strcmp(com_token, s))
2876 						goto match;
2877 				}
2878 				else // single-arg expression? must match the beginning of the command
2879 				{
2880 					if(!strcmp(com_token, s))
2881 						goto match;
2882 					if(!memcmp(va(vabuf, sizeof(vabuf), "%s ", com_token), s, strlen(com_token) + 1))
2883 						goto match;
2884 				}
2885 			}
2886 			// if we got here, nothing matched!
2887 			return NULL;
2888 		}
2889 match:
2890 		s += l + 1;
2891 	}
2892 
2893 allow:
2894 	userpass_startpass = strchr(userpass_start, ':');
2895 	if(have_usernames && userpass_startpass && userpass_startpass < userpass_end)
2896 		return va(vabuf, sizeof(vabuf), "%srcon (username %.*s)", restricted ? "restricted " : "", (int)(userpass_startpass-userpass_start), userpass_start);
2897 
2898 	return va(vabuf, sizeof(vabuf), "%srcon", restricted ? "restricted " : "");
2899 }
2900 
RCon_Execute(lhnetsocket_t * mysocket,lhnetaddress_t * peeraddress,const char * addressstring2,const char * userlevel,const char * s,const char * endpos,qboolean proquakeprotocol)2901 static void RCon_Execute(lhnetsocket_t *mysocket, lhnetaddress_t *peeraddress, const char *addressstring2, const char *userlevel, const char *s, const char *endpos, qboolean proquakeprotocol)
2902 {
2903 	if(userlevel)
2904 	{
2905 		// looks like a legitimate rcon command with the correct password
2906 		const char *s_ptr = s;
2907 		Con_Printf("server received %s command from %s: ", userlevel, host_client ? host_client->name : addressstring2);
2908 		while(s_ptr != endpos)
2909 		{
2910 			size_t l = strlen(s_ptr);
2911 			if(l)
2912 				Con_Printf(" %s;", s_ptr);
2913 			s_ptr += l + 1;
2914 		}
2915 		Con_Printf("\n");
2916 
2917 		if (!host_client || !host_client->netconnection || LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
2918 			Con_Rcon_Redirect_Init(mysocket, peeraddress, proquakeprotocol);
2919 		while(s != endpos)
2920 		{
2921 			size_t l = strlen(s);
2922 			if(l)
2923 			{
2924 				client_t *host_client_save = host_client;
2925 				Cmd_ExecuteString(s, src_command, true);
2926 				host_client = host_client_save;
2927 				// in case it is a command that changes host_client (like restart)
2928 			}
2929 			s += l + 1;
2930 		}
2931 		Con_Rcon_Redirect_End();
2932 	}
2933 	else
2934 	{
2935 		Con_Printf("server denied rcon access to %s\n", host_client ? host_client->name : addressstring2);
2936 	}
2937 }
2938 
NetConn_ServerParsePacket(lhnetsocket_t * mysocket,unsigned char * data,int length,lhnetaddress_t * peeraddress)2939 static int NetConn_ServerParsePacket(lhnetsocket_t *mysocket, unsigned char *data, int length, lhnetaddress_t *peeraddress)
2940 {
2941 	int i, ret, clientnum, best;
2942 	double besttime;
2943 	char *string, response[1400], addressstring2[128];
2944 	static char stringbuf[16384]; // server only
2945 	qboolean islocal = (LHNETADDRESS_GetAddressType(peeraddress) == LHNETADDRESSTYPE_LOOP);
2946 	char senddata[NET_HEADERSIZE+NET_MAXMESSAGE+CRYPTO_HEADERSIZE];
2947 	size_t sendlength, response_len;
2948 	char infostringvalue[MAX_INPUTLINE];
2949 
2950 	if (!sv.active)
2951 		return false;
2952 
2953 	// convert the address to a string incase we need it
2954 	LHNETADDRESS_ToString(peeraddress, addressstring2, sizeof(addressstring2), true);
2955 
2956 	// see if we can identify the sender as a local player
2957 	// (this is necessary for rcon to send a reliable reply if the client is
2958 	//  actually on the server, not sending remotely)
2959 	for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
2960 		if (host_client->netconnection && host_client->netconnection->mysocket == mysocket && !LHNETADDRESS_Compare(&host_client->netconnection->peeraddress, peeraddress))
2961 			break;
2962 	if (i == svs.maxclients)
2963 		host_client = NULL;
2964 
2965 	if (length >= 5 && data[0] == 255 && data[1] == 255 && data[2] == 255 && data[3] == 255)
2966 	{
2967 		// received a command string - strip off the packaging and put it
2968 		// into our string buffer with NULL termination
2969 		data += 4;
2970 		length -= 4;
2971 		length = min(length, (int)sizeof(stringbuf) - 1);
2972 		memcpy(stringbuf, data, length);
2973 		stringbuf[length] = 0;
2974 		string = stringbuf;
2975 
2976 		if (developer_extra.integer)
2977 		{
2978 			Con_Printf("NetConn_ServerParsePacket: %s sent us a command:\n", addressstring2);
2979 			Com_HexDumpToConsole(data, length);
2980 		}
2981 
2982 		sendlength = sizeof(senddata) - 4;
2983 		switch(Crypto_ServerParsePacket(string, length, senddata+4, &sendlength, peeraddress))
2984 		{
2985 			case CRYPTO_NOMATCH:
2986 				// nothing to do
2987 				break;
2988 			case CRYPTO_MATCH:
2989 				if(sendlength)
2990 				{
2991 					memcpy(senddata, "\377\377\377\377", 4);
2992 					NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
2993 				}
2994 				break;
2995 			case CRYPTO_DISCARD:
2996 				if(sendlength)
2997 				{
2998 					memcpy(senddata, "\377\377\377\377", 4);
2999 					NetConn_Write(mysocket, senddata, (int)sendlength+4, peeraddress);
3000 				}
3001 				return true;
3002 				break;
3003 			case CRYPTO_REPLACE:
3004 				string = senddata+4;
3005 				length = (int)sendlength;
3006 				break;
3007 		}
3008 
3009 		if (length >= 12 && !memcmp(string, "getchallenge", 12) && (islocal || sv_public.integer > -3))
3010 		{
3011 			for (i = 0, best = 0, besttime = realtime;i < MAX_CHALLENGES;i++)
3012 			{
3013 				if(challenges[i].time > 0)
3014 					if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address))
3015 						break;
3016 				if (besttime > challenges[i].time)
3017 					besttime = challenges[best = i].time;
3018 			}
3019 			// if we did not find an exact match, choose the oldest and
3020 			// update address and string
3021 			if (i == MAX_CHALLENGES)
3022 			{
3023 				i = best;
3024 				challenges[i].address = *peeraddress;
3025 				NetConn_BuildChallengeString(challenges[i].string, sizeof(challenges[i].string));
3026 			}
3027 			else
3028 			{
3029 				// flood control: drop if requesting challenge too often
3030 				if(challenges[i].time > realtime - net_challengefloodblockingtimeout.value)
3031 					return true;
3032 			}
3033 			challenges[i].time = realtime;
3034 			// send the challenge
3035 			memcpy(response, "\377\377\377\377", 4);
3036 			dpsnprintf(response+4, sizeof(response)-4, "challenge %s", challenges[i].string);
3037 			response_len = strlen(response) + 1;
3038 			Crypto_ServerAppendToChallenge(string, length, response, &response_len, sizeof(response));
3039 			NetConn_Write(mysocket, response, (int)response_len, peeraddress);
3040 			return true;
3041 		}
3042 		if (length > 8 && !memcmp(string, "connect\\", 8))
3043 		{
3044 			char *s;
3045 			client_t *client;
3046 			crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3047 			string += 7;
3048 			length -= 7;
3049 
3050 			if(crypto && crypto->authenticated)
3051 			{
3052 				// no need to check challenge
3053 				if(crypto_developer.integer)
3054 				{
3055 					Con_Printf("%s connection to %s is being established: client is %s@%s%.*s, I am %.*s@%s%.*s\n",
3056 							crypto->use_aes ? "Encrypted" : "Authenticated",
3057 							addressstring2,
3058 							crypto->client_idfp[0] ? crypto->client_idfp : "-",
3059 							(crypto->client_issigned || !crypto->client_keyfp[0]) ? "" : "~",
3060 							crypto_keyfp_recommended_length, crypto->client_keyfp[0] ? crypto->client_keyfp : "-",
3061 							crypto_keyfp_recommended_length, crypto->server_idfp[0] ? crypto->server_idfp : "-",
3062 							(crypto->server_issigned || !crypto->server_keyfp[0]) ? "" : "~",
3063 							crypto_keyfp_recommended_length, crypto->server_keyfp[0] ? crypto->server_keyfp : "-"
3064 						  );
3065 				}
3066 			}
3067 			else
3068 			{
3069 				if ((s = InfoString_GetValue(string, "challenge", infostringvalue, sizeof(infostringvalue))))
3070 				{
3071 					// validate the challenge
3072 					for (i = 0;i < MAX_CHALLENGES;i++)
3073 						if(challenges[i].time > 0)
3074 							if (!LHNETADDRESS_Compare(peeraddress, &challenges[i].address) && !strcmp(challenges[i].string, s))
3075 								break;
3076 					// if the challenge is not recognized, drop the packet
3077 					if (i == MAX_CHALLENGES)
3078 						return true;
3079 				}
3080 			}
3081 
3082 			if((s = InfoString_GetValue(string, "message", infostringvalue, sizeof(infostringvalue))))
3083 				Con_DPrintf("Connecting client %s sent us the message: %s\n", addressstring2, s);
3084 
3085 			if(!(islocal || sv_public.integer > -2))
3086 			{
3087 				if (developer_extra.integer)
3088 					Con_Printf("Datagram_ParseConnectionless: sending \"reject %s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3089 				memcpy(response, "\377\377\377\377", 4);
3090 				dpsnprintf(response+4, sizeof(response)-4, "reject %s", sv_public_rejectreason.string);
3091 				NetConn_WriteString(mysocket, response, peeraddress);
3092 				return true;
3093 			}
3094 
3095 			// check engine protocol
3096 			if(!(s = InfoString_GetValue(string, "protocol", infostringvalue, sizeof(infostringvalue))) || strcmp(s, "darkplaces 3"))
3097 			{
3098 				if (developer_extra.integer)
3099 					Con_Printf("Datagram_ParseConnectionless: sending \"reject Wrong game protocol.\" to %s.\n", addressstring2);
3100 				NetConn_WriteString(mysocket, "\377\377\377\377reject Wrong game protocol.", peeraddress);
3101 				return true;
3102 			}
3103 
3104 			// see if this is a duplicate connection request or a disconnected
3105 			// client who is rejoining to the same client slot
3106 			for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3107 			{
3108 				if (client->netconnection && LHNETADDRESS_Compare(peeraddress, &client->netconnection->peeraddress) == 0)
3109 				{
3110 					// this is a known client...
3111 					if(crypto && crypto->authenticated)
3112 					{
3113 						// reject if changing key!
3114 						if(client->netconnection->crypto.authenticated)
3115 						{
3116 							if(
3117 									strcmp(client->netconnection->crypto.client_idfp, crypto->client_idfp)
3118 									||
3119 									strcmp(client->netconnection->crypto.server_idfp, crypto->server_idfp)
3120 									||
3121 									strcmp(client->netconnection->crypto.client_keyfp, crypto->client_keyfp)
3122 									||
3123 									strcmp(client->netconnection->crypto.server_keyfp, crypto->server_keyfp)
3124 							  )
3125 							{
3126 								if (developer_extra.integer)
3127 									Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to change key of crypto.\" to %s.\n", addressstring2);
3128 								NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to change key of crypto.", peeraddress);
3129 								return true;
3130 							}
3131 						}
3132 					}
3133 					else
3134 					{
3135 						// reject if downgrading!
3136 						if(client->netconnection->crypto.authenticated)
3137 						{
3138 							if (developer_extra.integer)
3139 								Con_Printf("Datagram_ParseConnectionless: sending \"reject Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3140 							NetConn_WriteString(mysocket, "\377\377\377\377reject Attempt to downgrade crypto.", peeraddress);
3141 							return true;
3142 						}
3143 					}
3144 					if (client->begun)
3145 					{
3146 						// client crashed and is coming back,
3147 						// keep their stuff intact
3148 						if (developer_extra.integer)
3149 							Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", addressstring2);
3150 						NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3151 						if(crypto && crypto->authenticated)
3152 							Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3153 						SV_SendServerinfo(client);
3154 					}
3155 					else
3156 					{
3157 						// client is still trying to connect,
3158 						// so we send a duplicate reply
3159 						if (developer_extra.integer)
3160 							Con_Printf("Datagram_ParseConnectionless: sending duplicate accept to %s.\n", addressstring2);
3161 						if(crypto && crypto->authenticated)
3162 							Crypto_FinishInstance(&client->netconnection->crypto, crypto);
3163 						NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3164 					}
3165 					return true;
3166 				}
3167 			}
3168 
3169 			if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3170 				return true;
3171 
3172 			// find an empty client slot for this new client
3173 			for (clientnum = 0, client = svs.clients;clientnum < svs.maxclients;clientnum++, client++)
3174 			{
3175 				netconn_t *conn;
3176 				if (!client->active && (conn = NetConn_Open(mysocket, peeraddress)))
3177 				{
3178 					// allocated connection
3179 					if (developer_extra.integer)
3180 						Con_Printf("Datagram_ParseConnectionless: sending \"accept\" to %s.\n", conn->address);
3181 					NetConn_WriteString(mysocket, "\377\377\377\377accept", peeraddress);
3182 					// now set up the client
3183 					if(crypto && crypto->authenticated)
3184 						Crypto_FinishInstance(&conn->crypto, crypto);
3185 					SV_ConnectClient(clientnum, conn);
3186 					NetConn_Heartbeat(1);
3187 					return true;
3188 				}
3189 			}
3190 
3191 			// no empty slots found - server is full
3192 			if (developer_extra.integer)
3193 				Con_Printf("Datagram_ParseConnectionless: sending \"reject Server is full.\" to %s.\n", addressstring2);
3194 			NetConn_WriteString(mysocket, "\377\377\377\377reject Server is full.", peeraddress);
3195 
3196 			return true;
3197 		}
3198 		if (length >= 7 && !memcmp(string, "getinfo", 7) && (islocal || sv_public.integer > -1))
3199 		{
3200 			const char *challenge = NULL;
3201 
3202 			if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3203 				return true;
3204 
3205 			// If there was a challenge in the getinfo message
3206 			if (length > 8 && string[7] == ' ')
3207 				challenge = string + 8;
3208 
3209 			if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), false))
3210 			{
3211 				if (developer_extra.integer)
3212 					Con_DPrintf("Sending reply to master %s - %s\n", addressstring2, response);
3213 				NetConn_WriteString(mysocket, response, peeraddress);
3214 			}
3215 			return true;
3216 		}
3217 		if (length >= 9 && !memcmp(string, "getstatus", 9) && (islocal || sv_public.integer > -1))
3218 		{
3219 			const char *challenge = NULL;
3220 
3221 			if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3222 				return true;
3223 
3224 			// If there was a challenge in the getinfo message
3225 			if (length > 10 && string[9] == ' ')
3226 				challenge = string + 10;
3227 
3228 			if (NetConn_BuildStatusResponse(challenge, response, sizeof(response), true))
3229 			{
3230 				if (developer_extra.integer)
3231 					Con_DPrintf("Sending reply to client %s - %s\n", addressstring2, response);
3232 				NetConn_WriteString(mysocket, response, peeraddress);
3233 			}
3234 			return true;
3235 		}
3236 		if (length >= 37 && !memcmp(string, "srcon HMAC-MD4 TIME ", 20))
3237 		{
3238 			char *password = string + 20;
3239 			char *timeval = string + 37;
3240 			char *s = strchr(timeval, ' ');
3241 			char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3242 			const char *userlevel;
3243 
3244 			if(rcon_secure.integer > 1)
3245 				return true;
3246 
3247 			if(!s)
3248 				return true; // invalid packet
3249 			++s;
3250 
3251 			userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_time_matching, timeval, endpos - timeval - 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 >= 42 && !memcmp(string, "srcon HMAC-MD4 CHALLENGE ", 25))
3256 		{
3257 			char *password = string + 25;
3258 			char *challenge = string + 42;
3259 			char *s = strchr(challenge, ' ');
3260 			char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3261 			const char *userlevel;
3262 			if(!s)
3263 				return true; // invalid packet
3264 			++s;
3265 
3266 			userlevel = RCon_Authenticate(peeraddress, password, s, endpos, hmac_mdfour_challenge_matching, challenge, endpos - challenge - 1); // not including the appended \0 into the HMAC
3267 			RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3268 			return true;
3269 		}
3270 		if (length >= 5 && !memcmp(string, "rcon ", 5))
3271 		{
3272 			int j;
3273 			char *s = string + 5;
3274 			char *endpos = string + length + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3275 			char password[64];
3276 
3277 			if(rcon_secure.integer > 0)
3278 				return true;
3279 
3280 			for (j = 0;!ISWHITESPACE(*s);s++)
3281 				if (j < (int)sizeof(password) - 1)
3282 					password[j++] = *s;
3283 			if(ISWHITESPACE(*s) && s != endpos) // skip leading ugly space
3284 				++s;
3285 			password[j] = 0;
3286 			if (!ISWHITESPACE(password[0]))
3287 			{
3288 				const char *userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3289 				RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, false);
3290 			}
3291 			return true;
3292 		}
3293 		if (!strncmp(string, "extResponse ", 12))
3294 		{
3295 			++sv_net_extresponse_count;
3296 			if(sv_net_extresponse_count > NET_EXTRESPONSE_MAX)
3297 				sv_net_extresponse_count = NET_EXTRESPONSE_MAX;
3298 			sv_net_extresponse_last = (sv_net_extresponse_last + 1) % NET_EXTRESPONSE_MAX;
3299 			dpsnprintf(sv_net_extresponse[sv_net_extresponse_last], sizeof(sv_net_extresponse[sv_net_extresponse_last]), "'%s' %s", addressstring2, string + 12);
3300 			return true;
3301 		}
3302 		if (!strncmp(string, "ping", 4))
3303 		{
3304 			if (developer_extra.integer)
3305 				Con_DPrintf("Received ping from %s, sending ack\n", addressstring2);
3306 			NetConn_WriteString(mysocket, "\377\377\377\377ack", peeraddress);
3307 			return true;
3308 		}
3309 		if (!strncmp(string, "ack", 3))
3310 			return true;
3311 		// we may not have liked the packet, but it was a command packet, so
3312 		// we're done processing this packet now
3313 		return true;
3314 	}
3315 	// netquake control packets, supported for compatibility only, and only
3316 	// when running game protocols that are normally served via this connection
3317 	// protocol
3318 	// (this protects more modern protocols against being used for
3319 	//  Quake packet flood Denial Of Service attacks)
3320 	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)
3321 	{
3322 		int c;
3323 		int protocolnumber;
3324 		const char *protocolname;
3325 		client_t *knownclient;
3326 		client_t *newclient;
3327 		data += 4;
3328 		length -= 4;
3329 		SZ_Clear(&sv_message);
3330 		SZ_Write(&sv_message, data, length);
3331 		MSG_BeginReading(&sv_message);
3332 		c = MSG_ReadByte(&sv_message);
3333 		switch (c)
3334 		{
3335 		case CCREQ_CONNECT:
3336 			if (developer_extra.integer)
3337 				Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_CONNECT from %s.\n", addressstring2);
3338 			if(!(islocal || sv_public.integer > -2))
3339 			{
3340 				if (developer_extra.integer)
3341 					Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"%s\" to %s.\n", sv_public_rejectreason.string, addressstring2);
3342 				SZ_Clear(&sv_message);
3343 				// save space for the header, filled in later
3344 				MSG_WriteLong(&sv_message, 0);
3345 				MSG_WriteByte(&sv_message, CCREP_REJECT);
3346 				MSG_WriteUnterminatedString(&sv_message, sv_public_rejectreason.string);
3347 				MSG_WriteString(&sv_message, "\n");
3348 				StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3349 				NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3350 				SZ_Clear(&sv_message);
3351 				break;
3352 			}
3353 
3354 			protocolname = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3355 			protocolnumber = MSG_ReadByte(&sv_message);
3356 			if (strcmp(protocolname, "QUAKE") || protocolnumber != NET_PROTOCOL_VERSION)
3357 			{
3358 				if (developer_extra.integer)
3359 					Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Incompatible version.\" to %s.\n", addressstring2);
3360 				SZ_Clear(&sv_message);
3361 				// save space for the header, filled in later
3362 				MSG_WriteLong(&sv_message, 0);
3363 				MSG_WriteByte(&sv_message, CCREP_REJECT);
3364 				MSG_WriteString(&sv_message, "Incompatible version.\n");
3365 				StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3366 				NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3367 				SZ_Clear(&sv_message);
3368 				break;
3369 			}
3370 
3371 			// see if this connect request comes from a known client
3372 			for (clientnum = 0, knownclient = svs.clients;clientnum < svs.maxclients;clientnum++, knownclient++)
3373 			{
3374 				if (knownclient->netconnection && LHNETADDRESS_Compare(peeraddress, &knownclient->netconnection->peeraddress) == 0)
3375 				{
3376 					// this is either a duplicate connection request
3377 					// or coming back from a timeout
3378 					// (if so, keep their stuff intact)
3379 
3380 					crypto_t *crypto = Crypto_ServerGetInstance(peeraddress);
3381 					if((crypto && crypto->authenticated) || knownclient->netconnection->crypto.authenticated)
3382 					{
3383 						if (developer_extra.integer)
3384 							Con_Printf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Attempt to downgrade crypto.\" to %s.\n", addressstring2);
3385 						SZ_Clear(&sv_message);
3386 						// save space for the header, filled in later
3387 						MSG_WriteLong(&sv_message, 0);
3388 						MSG_WriteByte(&sv_message, CCREP_REJECT);
3389 						MSG_WriteString(&sv_message, "Attempt to downgrade crypto.\n");
3390 						StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3391 						NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3392 						SZ_Clear(&sv_message);
3393 						return true;
3394 					}
3395 
3396 					// send a reply
3397 					if (developer_extra.integer)
3398 						Con_DPrintf("Datagram_ParseConnectionless: sending duplicate CCREP_ACCEPT to %s.\n", addressstring2);
3399 					SZ_Clear(&sv_message);
3400 					// save space for the header, filled in later
3401 					MSG_WriteLong(&sv_message, 0);
3402 					MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3403 					MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(knownclient->netconnection->mysocket)));
3404 					StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3405 					NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3406 					SZ_Clear(&sv_message);
3407 
3408 					// if client is already spawned, re-send the
3409 					// serverinfo message as they'll need it to play
3410 					if (knownclient->begun)
3411 						SV_SendServerinfo(knownclient);
3412 					return true;
3413 				}
3414 			}
3415 
3416 			// this is a new client, check for connection flood
3417 			if (NetConn_PreventFlood(peeraddress, sv.connectfloodaddresses, sizeof(sv.connectfloodaddresses) / sizeof(sv.connectfloodaddresses[0]), net_connectfloodblockingtimeout.value, true))
3418 				break;
3419 
3420 			// find a slot for the new client
3421 			for (clientnum = 0, newclient = svs.clients;clientnum < svs.maxclients;clientnum++, newclient++)
3422 			{
3423 				netconn_t *conn;
3424 				if (!newclient->active && (newclient->netconnection = conn = NetConn_Open(mysocket, peeraddress)) != NULL)
3425 				{
3426 					// connect to the client
3427 					// everything is allocated, just fill in the details
3428 					strlcpy (conn->address, addressstring2, sizeof (conn->address));
3429 					if (developer_extra.integer)
3430 						Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_ACCEPT to %s.\n", addressstring2);
3431 					// send back the info about the server connection
3432 					SZ_Clear(&sv_message);
3433 					// save space for the header, filled in later
3434 					MSG_WriteLong(&sv_message, 0);
3435 					MSG_WriteByte(&sv_message, CCREP_ACCEPT);
3436 					MSG_WriteLong(&sv_message, LHNETADDRESS_GetPort(LHNET_AddressFromSocket(conn->mysocket)));
3437 					StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3438 					NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3439 					SZ_Clear(&sv_message);
3440 					// now set up the client struct
3441 					SV_ConnectClient(clientnum, conn);
3442 					NetConn_Heartbeat(1);
3443 					return true;
3444 				}
3445 			}
3446 
3447 			if (developer_extra.integer)
3448 				Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_REJECT \"Server is full.\" to %s.\n", addressstring2);
3449 			// no room; try to let player know
3450 			SZ_Clear(&sv_message);
3451 			// save space for the header, filled in later
3452 			MSG_WriteLong(&sv_message, 0);
3453 			MSG_WriteByte(&sv_message, CCREP_REJECT);
3454 			MSG_WriteString(&sv_message, "Server is full.\n");
3455 			StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3456 			NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3457 			SZ_Clear(&sv_message);
3458 			break;
3459 		case CCREQ_SERVER_INFO:
3460 			if (developer_extra.integer)
3461 				Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_SERVER_INFO from %s.\n", addressstring2);
3462 			if(!(islocal || sv_public.integer > -1))
3463 				break;
3464 
3465 			if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3466 				break;
3467 
3468 			if (sv.active && !strcmp(MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), "QUAKE"))
3469 			{
3470 				int numclients;
3471 				char myaddressstring[128];
3472 				if (developer_extra.integer)
3473 					Con_DPrintf("Datagram_ParseConnectionless: sending CCREP_SERVER_INFO to %s.\n", addressstring2);
3474 				SZ_Clear(&sv_message);
3475 				// save space for the header, filled in later
3476 				MSG_WriteLong(&sv_message, 0);
3477 				MSG_WriteByte(&sv_message, CCREP_SERVER_INFO);
3478 				LHNETADDRESS_ToString(LHNET_AddressFromSocket(mysocket), myaddressstring, sizeof(myaddressstring), true);
3479 				MSG_WriteString(&sv_message, myaddressstring);
3480 				MSG_WriteString(&sv_message, hostname.string);
3481 				MSG_WriteString(&sv_message, sv.name);
3482 				// How many clients are there?
3483 				for (i = 0, numclients = 0;i < svs.maxclients;i++)
3484 					if (svs.clients[i].active)
3485 						numclients++;
3486 				MSG_WriteByte(&sv_message, numclients);
3487 				MSG_WriteByte(&sv_message, svs.maxclients);
3488 				MSG_WriteByte(&sv_message, NET_PROTOCOL_VERSION);
3489 				StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3490 				NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3491 				SZ_Clear(&sv_message);
3492 			}
3493 			break;
3494 		case CCREQ_PLAYER_INFO:
3495 			if (developer_extra.integer)
3496 				Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_PLAYER_INFO from %s.\n", addressstring2);
3497 			if(!(islocal || sv_public.integer > -1))
3498 				break;
3499 
3500 			if (NetConn_PreventFlood(peeraddress, sv.getstatusfloodaddresses, sizeof(sv.getstatusfloodaddresses) / sizeof(sv.getstatusfloodaddresses[0]), net_getstatusfloodblockingtimeout.value, false))
3501 				break;
3502 
3503 			if (sv.active)
3504 			{
3505 				int playerNumber, activeNumber, clientNumber;
3506 				client_t *client;
3507 
3508 				playerNumber = MSG_ReadByte(&sv_message);
3509 				activeNumber = -1;
3510 				for (clientNumber = 0, client = svs.clients; clientNumber < svs.maxclients; clientNumber++, client++)
3511 					if (client->active && ++activeNumber == playerNumber)
3512 						break;
3513 				if (clientNumber != svs.maxclients)
3514 				{
3515 					SZ_Clear(&sv_message);
3516 					// save space for the header, filled in later
3517 					MSG_WriteLong(&sv_message, 0);
3518 					MSG_WriteByte(&sv_message, CCREP_PLAYER_INFO);
3519 					MSG_WriteByte(&sv_message, playerNumber);
3520 					MSG_WriteString(&sv_message, client->name);
3521 					MSG_WriteLong(&sv_message, client->colors);
3522 					MSG_WriteLong(&sv_message, client->frags);
3523 					MSG_WriteLong(&sv_message, (int)(realtime - client->connecttime));
3524 					if(sv_status_privacy.integer)
3525 						MSG_WriteString(&sv_message, client->netconnection ? "hidden" : "botclient");
3526 					else
3527 						MSG_WriteString(&sv_message, client->netconnection ? client->netconnection->address : "botclient");
3528 					StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3529 					NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3530 					SZ_Clear(&sv_message);
3531 				}
3532 			}
3533 			break;
3534 		case CCREQ_RULE_INFO:
3535 			if (developer_extra.integer)
3536 				Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RULE_INFO from %s.\n", addressstring2);
3537 			if(!(islocal || sv_public.integer > -1))
3538 				break;
3539 
3540 			// no flood check here, as it only returns one cvar for one cvar and clients may iterate quickly
3541 
3542 			if (sv.active)
3543 			{
3544 				char *prevCvarName;
3545 				cvar_t *var;
3546 
3547 				// find the search start location
3548 				prevCvarName = MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring));
3549 				var = Cvar_FindVarAfter(prevCvarName, CVAR_NOTIFY);
3550 
3551 				// send the response
3552 				SZ_Clear(&sv_message);
3553 				// save space for the header, filled in later
3554 				MSG_WriteLong(&sv_message, 0);
3555 				MSG_WriteByte(&sv_message, CCREP_RULE_INFO);
3556 				if (var)
3557 				{
3558 					MSG_WriteString(&sv_message, var->name);
3559 					MSG_WriteString(&sv_message, var->string);
3560 				}
3561 				StoreBigLong(sv_message.data, NETFLAG_CTL | (sv_message.cursize & NETFLAG_LENGTH_MASK));
3562 				NetConn_Write(mysocket, sv_message.data, sv_message.cursize, peeraddress);
3563 				SZ_Clear(&sv_message);
3564 			}
3565 			break;
3566 		case CCREQ_RCON:
3567 			if (developer_extra.integer)
3568 				Con_DPrintf("Datagram_ParseConnectionless: received CCREQ_RCON from %s.\n", addressstring2);
3569 			if (sv.active && !rcon_secure.integer)
3570 			{
3571 				char password[2048];
3572 				char cmd[2048];
3573 				char *s;
3574 				char *endpos;
3575 				const char *userlevel;
3576 				strlcpy(password, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(password));
3577 				strlcpy(cmd, MSG_ReadString(&sv_message, sv_readstring, sizeof(sv_readstring)), sizeof(cmd));
3578 				s = cmd;
3579 				endpos = cmd + strlen(cmd) + 1; // one behind the NUL, so adding strlen+1 will eventually reach it
3580 				userlevel = RCon_Authenticate(peeraddress, password, s, endpos, plaintext_matching, NULL, 0);
3581 				RCon_Execute(mysocket, peeraddress, addressstring2, userlevel, s, endpos, true);
3582 				return true;
3583 			}
3584 			break;
3585 		default:
3586 			break;
3587 		}
3588 		SZ_Clear(&sv_message);
3589 		// we may not have liked the packet, but it was a valid control
3590 		// packet, so we're done processing this packet now
3591 		return true;
3592 	}
3593 	if (host_client)
3594 	{
3595 		if ((ret = NetConn_ReceivedMessage(host_client->netconnection, data, length, sv.protocol, host_client->begun ? net_messagetimeout.value : net_connecttimeout.value)) == 2)
3596 		{
3597 			SV_ReadClientMessage();
3598 			return ret;
3599 		}
3600 	}
3601 	return 0;
3602 }
3603 
NetConn_ServerFrame(void)3604 void NetConn_ServerFrame(void)
3605 {
3606 	int i, length;
3607 	lhnetaddress_t peeraddress;
3608 	unsigned char readbuffer[NET_HEADERSIZE+NET_MAXMESSAGE];
3609 	for (i = 0;i < sv_numsockets;i++)
3610 		while (sv_sockets[i] && (length = NetConn_Read(sv_sockets[i], readbuffer, sizeof(readbuffer), &peeraddress)) > 0)
3611 			NetConn_ServerParsePacket(sv_sockets[i], readbuffer, length, &peeraddress);
3612 	for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
3613 	{
3614 		// never timeout loopback connections
3615 		if (host_client->netconnection && realtime > host_client->netconnection->timeout && LHNETADDRESS_GetAddressType(&host_client->netconnection->peeraddress) != LHNETADDRESSTYPE_LOOP)
3616 		{
3617 			Con_Printf("Client \"%s\" connection timed out\n", host_client->name);
3618 			SV_DropClient(false);
3619 		}
3620 	}
3621 }
3622 
NetConn_SleepMicroseconds(int microseconds)3623 void NetConn_SleepMicroseconds(int microseconds)
3624 {
3625 	LHNET_SleepUntilPacket_Microseconds(microseconds);
3626 }
3627 
3628 #ifdef CONFIG_MENU
NetConn_QueryMasters(qboolean querydp,qboolean queryqw)3629 void NetConn_QueryMasters(qboolean querydp, qboolean queryqw)
3630 {
3631 	int i, j;
3632 	int masternum;
3633 	lhnetaddress_t masteraddress;
3634 	lhnetaddress_t broadcastaddress;
3635 	char request[256];
3636 
3637 	if (serverlist_cachecount >= SERVERLIST_TOTALSIZE)
3638 		return;
3639 
3640 	// 26000 is the default quake server port, servers on other ports will not
3641 	// be found
3642 	// note this is IPv4-only, I doubt there are IPv6-only LANs out there
3643 	LHNETADDRESS_FromString(&broadcastaddress, "255.255.255.255", 26000);
3644 
3645 	if (querydp)
3646 	{
3647 		for (i = 0;i < cl_numsockets;i++)
3648 		{
3649 			if (cl_sockets[i])
3650 			{
3651 				const char *cmdname, *extraoptions;
3652 				int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3653 
3654 				if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3655 				{
3656 					// search LAN for Quake servers
3657 					SZ_Clear(&cl_message);
3658 					// save space for the header, filled in later
3659 					MSG_WriteLong(&cl_message, 0);
3660 					MSG_WriteByte(&cl_message, CCREQ_SERVER_INFO);
3661 					MSG_WriteString(&cl_message, "QUAKE");
3662 					MSG_WriteByte(&cl_message, NET_PROTOCOL_VERSION);
3663 					StoreBigLong(cl_message.data, NETFLAG_CTL | (cl_message.cursize & NETFLAG_LENGTH_MASK));
3664 					NetConn_Write(cl_sockets[i], cl_message.data, cl_message.cursize, &broadcastaddress);
3665 					SZ_Clear(&cl_message);
3666 
3667 					// search LAN for DarkPlaces servers
3668 					NetConn_WriteString(cl_sockets[i], "\377\377\377\377getstatus", &broadcastaddress);
3669 				}
3670 
3671 				// build the getservers message to send to the dpmaster master servers
3672 				if (LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])) == LHNETADDRESSTYPE_INET6)
3673 				{
3674 					cmdname = "getserversExt";
3675 					extraoptions = " ipv4 ipv6";  // ask for IPv4 and IPv6 servers
3676 				}
3677 				else
3678 				{
3679 					cmdname = "getservers";
3680 					extraoptions = "";
3681 				}
3682 				memcpy(request, "\377\377\377\377", 4);
3683 				dpsnprintf(request+4, sizeof(request)-4, "%s %s %u empty full%s", cmdname, gamenetworkfiltername, NET_PROTOCOL_VERSION, extraoptions);
3684 
3685 				// search internet
3686 				for (masternum = 0;sv_masters[masternum].name;masternum++)
3687 				{
3688 					if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == af)
3689 					{
3690 						masterquerycount++;
3691 						NetConn_WriteString(cl_sockets[i], request, &masteraddress);
3692 					}
3693 				}
3694 
3695 				// search favorite servers
3696 				for(j = 0; j < nFavorites; ++j)
3697 				{
3698 					if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3699 					{
3700 						if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3701 							NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_DARKPLACES7, request, true );
3702 					}
3703 				}
3704 			}
3705 		}
3706 	}
3707 
3708 	// only query QuakeWorld servers when the user wants to
3709 	if (queryqw)
3710 	{
3711 		for (i = 0;i < cl_numsockets;i++)
3712 		{
3713 			if (cl_sockets[i])
3714 			{
3715 				int af = LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i]));
3716 
3717 				if(LHNETADDRESS_GetAddressType(&broadcastaddress) == af)
3718 				{
3719 					// search LAN for QuakeWorld servers
3720 					NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &broadcastaddress);
3721 
3722 					// build the getservers message to send to the qwmaster master servers
3723 					// note this has no -1 prefix, and the trailing nul byte is sent
3724 					dpsnprintf(request, sizeof(request), "c\n");
3725 				}
3726 
3727 				// search internet
3728 				for (masternum = 0;sv_qwmasters[masternum].name;masternum++)
3729 				{
3730 					if (sv_qwmasters[masternum].string && LHNETADDRESS_FromString(&masteraddress, sv_qwmasters[masternum].string, QWMASTER_PORT) && LHNETADDRESS_GetAddressType(&masteraddress) == LHNETADDRESS_GetAddressType(LHNET_AddressFromSocket(cl_sockets[i])))
3731 					{
3732 						if (m_state != m_slist)
3733 						{
3734 							char lookupstring[128];
3735 							LHNETADDRESS_ToString(&masteraddress, lookupstring, sizeof(lookupstring), true);
3736 							Con_Printf("Querying master %s (resolved from %s)\n", lookupstring, sv_qwmasters[masternum].string);
3737 						}
3738 						masterquerycount++;
3739 						NetConn_Write(cl_sockets[i], request, (int)strlen(request) + 1, &masteraddress);
3740 					}
3741 				}
3742 
3743 				// search favorite servers
3744 				for(j = 0; j < nFavorites; ++j)
3745 				{
3746 					if(LHNETADDRESS_GetAddressType(&favorites[j]) == af)
3747 					{
3748 						if(LHNETADDRESS_ToString(&favorites[j], request, sizeof(request), true))
3749 						{
3750 							NetConn_WriteString(cl_sockets[i], "\377\377\377\377status\n", &favorites[j]);
3751 							NetConn_ClientParsePacket_ServerList_PrepareQuery( PROTOCOL_QUAKEWORLD, request, true );
3752 						}
3753 					}
3754 				}
3755 			}
3756 		}
3757 	}
3758 	if (!masterquerycount)
3759 	{
3760 		Con_Print("Unable to query master servers, no suitable network sockets active.\n");
3761 		M_Update_Return_Reason("No network");
3762 	}
3763 }
3764 #endif
3765 
NetConn_Heartbeat(int priority)3766 void NetConn_Heartbeat(int priority)
3767 {
3768 	lhnetaddress_t masteraddress;
3769 	int masternum;
3770 	lhnetsocket_t *mysocket;
3771 
3772 	// if it's a state change (client connected), limit next heartbeat to no
3773 	// more than 30 sec in the future
3774 	if (priority == 1 && nextheartbeattime > realtime + 30.0)
3775 		nextheartbeattime = realtime + 30.0;
3776 
3777 	// limit heartbeatperiod to 30 to 270 second range,
3778 	// lower limit is to avoid abusing master servers with excess traffic,
3779 	// upper limit is to avoid timing out on the master server (which uses
3780 	// 300 sec timeout)
3781 	if (sv_heartbeatperiod.value < 30)
3782 		Cvar_SetValueQuick(&sv_heartbeatperiod, 30);
3783 	if (sv_heartbeatperiod.value > 270)
3784 		Cvar_SetValueQuick(&sv_heartbeatperiod, 270);
3785 
3786 	// make advertising optional and don't advertise singleplayer games, and
3787 	// only send a heartbeat as often as the admin wants
3788 	if (sv.active && sv_public.integer > 0 && svs.maxclients >= 2 && (priority > 1 || realtime > nextheartbeattime))
3789 	{
3790 		nextheartbeattime = realtime + sv_heartbeatperiod.value;
3791 		for (masternum = 0;sv_masters[masternum].name;masternum++)
3792 			if (sv_masters[masternum].string && sv_masters[masternum].string[0] && LHNETADDRESS_FromString(&masteraddress, sv_masters[masternum].string, DPMASTER_PORT) && (mysocket = NetConn_ChooseServerSocketForAddress(&masteraddress)))
3793 				NetConn_WriteString(mysocket, "\377\377\377\377heartbeat DarkPlaces\x0A", &masteraddress);
3794 	}
3795 }
3796 
Net_Heartbeat_f(void)3797 static void Net_Heartbeat_f(void)
3798 {
3799 	if (sv.active)
3800 		NetConn_Heartbeat(2);
3801 	else
3802 		Con_Print("No server running, can not heartbeat to master server.\n");
3803 }
3804 
PrintStats(netconn_t * conn)3805 static void PrintStats(netconn_t *conn)
3806 {
3807 	if ((cls.state == ca_connected && cls.protocol == PROTOCOL_QUAKEWORLD) || (sv.active && sv.protocol == PROTOCOL_QUAKEWORLD))
3808 		Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->outgoing_unreliable_sequence, conn->qw.incoming_sequence);
3809 	else
3810 		Con_Printf("address=%21s canSend=%u sendSeq=%6u recvSeq=%6u\n", conn->address, !conn->sendMessageLength, conn->nq.sendSequence, conn->nq.receiveSequence);
3811 	Con_Printf("unreliable messages sent   = %i\n", conn->unreliableMessagesSent);
3812 	Con_Printf("unreliable messages recv   = %i\n", conn->unreliableMessagesReceived);
3813 	Con_Printf("reliable messages sent     = %i\n", conn->reliableMessagesSent);
3814 	Con_Printf("reliable messages received = %i\n", conn->reliableMessagesReceived);
3815 	Con_Printf("packetsSent                = %i\n", conn->packetsSent);
3816 	Con_Printf("packetsReSent              = %i\n", conn->packetsReSent);
3817 	Con_Printf("packetsReceived            = %i\n", conn->packetsReceived);
3818 	Con_Printf("receivedDuplicateCount     = %i\n", conn->receivedDuplicateCount);
3819 	Con_Printf("droppedDatagrams           = %i\n", conn->droppedDatagrams);
3820 }
3821 
Net_Stats_f(void)3822 void Net_Stats_f(void)
3823 {
3824 	netconn_t *conn;
3825 	Con_Print("connections                =\n");
3826 	for (conn = netconn_list;conn;conn = conn->next)
3827 		PrintStats(conn);
3828 }
3829 
3830 #ifdef CONFIG_MENU
Net_Refresh_f(void)3831 void Net_Refresh_f(void)
3832 {
3833 	if (m_state != m_slist) {
3834 		Con_Print("Sending new requests to master servers\n");
3835 		ServerList_QueryList(false, true, false, true);
3836 		Con_Print("Listening for replies...\n");
3837 	} else
3838 		ServerList_QueryList(false, true, false, false);
3839 }
3840 
Net_Slist_f(void)3841 void Net_Slist_f(void)
3842 {
3843 	ServerList_ResetMasks();
3844 	serverlist_sortbyfield = SLIF_PING;
3845 	serverlist_sortflags = 0;
3846     if (m_state != m_slist) {
3847 		Con_Print("Sending requests to master servers\n");
3848 		ServerList_QueryList(true, true, false, true);
3849 		Con_Print("Listening for replies...\n");
3850 	} else
3851 		ServerList_QueryList(true, true, false, false);
3852 }
3853 
Net_SlistQW_f(void)3854 void Net_SlistQW_f(void)
3855 {
3856 	ServerList_ResetMasks();
3857 	serverlist_sortbyfield = SLIF_PING;
3858 	serverlist_sortflags = 0;
3859     if (m_state != m_slist) {
3860 		Con_Print("Sending requests to master servers\n");
3861 		ServerList_QueryList(true, false, true, true);
3862 		serverlist_consoleoutput = true;
3863 		Con_Print("Listening for replies...\n");
3864 	} else
3865 		ServerList_QueryList(true, false, true, false);
3866 }
3867 #endif
3868 
NetConn_Init(void)3869 void NetConn_Init(void)
3870 {
3871 	int i;
3872 	lhnetaddress_t tempaddress;
3873 	netconn_mempool = Mem_AllocPool("network connections", 0, NULL);
3874 	Cmd_AddCommand("net_stats", Net_Stats_f, "print network statistics");
3875 #ifdef CONFIG_MENU
3876 	Cmd_AddCommand("net_slist", Net_Slist_f, "query dp master servers and print all server information");
3877 	Cmd_AddCommand("net_slistqw", Net_SlistQW_f, "query qw master servers and print all server information");
3878 	Cmd_AddCommand("net_refresh", Net_Refresh_f, "query dp master servers and refresh all server information");
3879 #endif
3880 	Cmd_AddCommand("heartbeat", Net_Heartbeat_f, "send a heartbeat to the master server (updates your server information)");
3881 	Cvar_RegisterVariable(&net_test);
3882 	Cvar_RegisterVariable(&net_usesizelimit);
3883 	Cvar_RegisterVariable(&net_burstreserve);
3884 	Cvar_RegisterVariable(&rcon_restricted_password);
3885 	Cvar_RegisterVariable(&rcon_restricted_commands);
3886 	Cvar_RegisterVariable(&rcon_secure_maxdiff);
3887 	Cvar_RegisterVariable(&net_slist_queriespersecond);
3888 	Cvar_RegisterVariable(&net_slist_queriesperframe);
3889 	Cvar_RegisterVariable(&net_slist_timeout);
3890 	Cvar_RegisterVariable(&net_slist_maxtries);
3891 	Cvar_RegisterVariable(&net_slist_favorites);
3892 	Cvar_RegisterVariable(&net_slist_pause);
3893 	if(LHNET_DefaultDSCP(-1) >= 0) // register cvar only if supported
3894 		Cvar_RegisterVariable(&net_tos_dscp);
3895 	Cvar_RegisterVariable(&net_messagetimeout);
3896 	Cvar_RegisterVariable(&net_connecttimeout);
3897 	Cvar_RegisterVariable(&net_connectfloodblockingtimeout);
3898 	Cvar_RegisterVariable(&net_challengefloodblockingtimeout);
3899 	Cvar_RegisterVariable(&net_getstatusfloodblockingtimeout);
3900 	Cvar_RegisterVariable(&net_sourceaddresscheck);
3901 	Cvar_RegisterVariable(&cl_netlocalping);
3902 	Cvar_RegisterVariable(&cl_netpacketloss_send);
3903 	Cvar_RegisterVariable(&cl_netpacketloss_receive);
3904 	Cvar_RegisterVariable(&hostname);
3905 	Cvar_RegisterVariable(&developer_networking);
3906 	Cvar_RegisterVariable(&cl_netport);
3907 	Cvar_RegisterVariable(&sv_netport);
3908 	Cvar_RegisterVariable(&net_address);
3909 	Cvar_RegisterVariable(&net_address_ipv6);
3910 	Cvar_RegisterVariable(&sv_public);
3911 	Cvar_RegisterVariable(&sv_public_rejectreason);
3912 	Cvar_RegisterVariable(&sv_heartbeatperiod);
3913 	for (i = 0;sv_masters[i].name;i++)
3914 		Cvar_RegisterVariable(&sv_masters[i]);
3915 	Cvar_RegisterVariable(&gameversion);
3916 	Cvar_RegisterVariable(&gameversion_min);
3917 	Cvar_RegisterVariable(&gameversion_max);
3918 // 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.
3919 	if ((i = COM_CheckParm("-ip")) && i + 1 < com_argc)
3920 	{
3921 		if (LHNETADDRESS_FromString(&tempaddress, com_argv[i + 1], 0) == 1)
3922 		{
3923 			Con_Printf("-ip option used, setting net_address to \"%s\"\n", com_argv[i + 1]);
3924 			Cvar_SetQuick(&net_address, com_argv[i + 1]);
3925 		}
3926 		else
3927 			Con_Printf("-ip option used, but unable to parse the address \"%s\"\n", com_argv[i + 1]);
3928 	}
3929 // 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
3930 	if (((i = COM_CheckParm("-port")) || (i = COM_CheckParm("-ipport")) || (i = COM_CheckParm("-udpport"))) && i + 1 < com_argc)
3931 	{
3932 		i = atoi(com_argv[i + 1]);
3933 		if (i >= 0 && i < 65536)
3934 		{
3935 			Con_Printf("-port option used, setting port cvar to %i\n", i);
3936 			Cvar_SetValueQuick(&sv_netport, i);
3937 		}
3938 		else
3939 			Con_Printf("-port option used, but %i is not a valid port number\n", i);
3940 	}
3941 	cl_numsockets = 0;
3942 	sv_numsockets = 0;
3943 	cl_message.data = cl_message_buf;
3944 	cl_message.maxsize = sizeof(cl_message_buf);
3945 	cl_message.cursize = 0;
3946 	sv_message.data = sv_message_buf;
3947 	sv_message.maxsize = sizeof(sv_message_buf);
3948 	sv_message.cursize = 0;
3949 	LHNET_Init();
3950 	if (Thread_HasThreads())
3951 		netconn_mutex = Thread_CreateMutex();
3952 }
3953 
NetConn_Shutdown(void)3954 void NetConn_Shutdown(void)
3955 {
3956 	NetConn_CloseClientPorts();
3957 	NetConn_CloseServerPorts();
3958 	LHNET_Shutdown();
3959 	if (netconn_mutex)
3960 		Thread_DestroyMutex(netconn_mutex);
3961 	netconn_mutex = NULL;
3962 }
3963 
3964