1 /**
2  * @file
3  * @brief Main server code?
4  */
5 
6 /*
7 All original material Copyright (C) 2002-2013 UFO: Alien Invasion.
8 
9 Original file from Quake 2 v3.21: quake2-2.31/server/sv_main.c
10 Copyright (C) 1997-2001 Id Software, Inc.
11 
12 This program is free software; you can redistribute it and/or
13 modify it under the terms of the GNU General Public License
14 as published by the Free Software Foundation; either version 2
15 of the License, or (at your option) any later version.
16 
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20 
21 See the GNU General Public License for more details.
22 
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
26 
27 */
28 
29 #include "server.h"
30 #include "sv_log.h"
31 #include "../ports/system.h"
32 #include "../shared/scopedmutex.h"
33 #include <SDL.h>
34 
35 /** password for remote server commands */
36 static cvar_t* rcon_password;
37 static cvar_t* sv_http_downloadserver;
38 static cvar_t* sv_enablemorale;
39 static cvar_t* sv_maxsoldiersperteam;
40 static cvar_t* sv_maxsoldiersperplayer;
41 static cvar_t* sv_hostname;
42 /** minimum seconds between connect messages */
43 static cvar_t* sv_reconnect_limit;
44 static cvar_t* sv_timeout;			/* seconds without any message */
45 
46 cvar_t* sv_maxclients = nullptr;
47 cvar_t* sv_dumpmapassembly;
48 cvar_t* sv_threads;
49 cvar_t* sv_rma;
50 cvar_t* sv_rmadisplaythemap;
51 /** should heartbeats be sent */
52 cvar_t* sv_public;
53 cvar_t* sv_mapname;
54 
55 memPool_t* sv_genericPool;
56 
57 typedef struct leakyBucket_s {
58 	char node[64];
59 
60 	int lastTime;
61 	signed char burst;
62 
63 	long hash;
64 
65 	struct leakyBucket_s* prev;
66 	struct leakyBucket_s* next;
67 } leakyBucket_t;
68 
69 /* This is deliberately quite large to make it more of an effort to DoS */
70 #define MAX_BUCKETS			16384
71 #define MAX_HASHES			1024
72 
73 static leakyBucket_t buckets[MAX_BUCKETS];
74 static leakyBucket_t* bucketHashes[MAX_HASHES];
75 static leakyBucket_t outboundLeakyBucket;
76 
SV_GetConfigString(int index)77 char* SV_GetConfigString (int index)
78 {
79 	if (!Com_CheckConfigStringIndex(index))
80 		Com_Error(ERR_FATAL, "Invalid config string index given: %i", index);
81 
82 	return sv->configstrings[index];
83 }
84 
SV_GetConfigStringInteger(int index)85 int SV_GetConfigStringInteger (int index)
86 {
87 	return atoi(SV_GetConfigString(index));
88 }
89 
SV_SetConfigString(int index,...)90 char* SV_SetConfigString (int index, ...)
91 {
92 	va_list ap;
93 	const char* value;
94 
95 	if (!Com_CheckConfigStringIndex(index))
96 		Com_Error(ERR_FATAL, "Invalid config string index given: %i", index);
97 
98 	va_start(ap, index);
99 
100 	switch (index) {
101 	case CS_LIGHTMAP:
102 	case CS_MAPCHECKSUM:
103 	case CS_UFOCHECKSUM:
104 	case CS_OBJECTAMOUNT:
105 		value = va("%i", va_arg(ap, int));
106 		break;
107 	default:
108 		value = va_arg(ap, char*);
109 		break;
110 	}
111 
112 	/* change the string in sv
113 	 * there may be overflows in i==CS_TILES - but thats ok
114 	 * see definition of configstrings and MAX_TILESTRINGS */
115 	if (index == CS_TILES || index == CS_POSITIONS)
116 		Q_strncpyz(sv->configstrings[index], value, MAX_TOKEN_CHARS * MAX_TILESTRINGS);
117 	else
118 		Q_strncpyz(sv->configstrings[index], value, sizeof(sv->configstrings[index]));
119 
120 	va_end(ap);
121 
122 	return sv->configstrings[index];
123 }
124 
125 /**
126  * @brief Iterates through clients
127  * @param[in] lastClient Pointer of the client to iterate from. call with nullptr to get the first one.
128  */
SV_GetNextClient(client_t * lastClient)129 client_t* SV_GetNextClient (client_t* lastClient)
130 {
131 	client_t* endOfClients = &svs.clients[sv_maxclients->integer];
132 	client_t* client;
133 
134 	if (!sv_maxclients->integer)
135 		return nullptr;
136 
137 	if (!lastClient)
138 		return svs.clients;
139 	assert(lastClient >= svs.clients);
140 	assert(lastClient < endOfClients);
141 
142 	client = lastClient;
143 
144 	client++;
145 	if (client >= endOfClients)
146 		return nullptr;
147 	else
148 		return client;
149 }
150 
SV_GetClient(int index)151 client_t* SV_GetClient (int index)
152 {
153 	return &svs.clients[index];
154 }
155 
156 /**
157  * @brief Called when the player is totally leaving the server, either willingly
158  * or unwillingly. This is NOT called if the entire server is quitting
159  * or crashing.
160  */
SV_DropClient(client_t * drop,const char * message)161 void SV_DropClient (client_t* drop, const char* message)
162 {
163 	/* add the disconnect */
164 	dbuffer msg(2 + strlen(message));
165 	NET_WriteByte(&msg, svc_disconnect);
166 	NET_WriteString(&msg, message);
167 	NET_WriteMsg(drop->stream, msg);
168 	SV_BroadcastPrintf(PRINT_CHAT, "%s was dropped from the server - reason: %s\n", drop->name, message);
169 
170 	if (drop->state == cs_spawned || drop->state == cs_spawning) {
171 		/* call the prog function for removing a client */
172 		/* this will remove the body, among other things */
173 		const ScopedMutex scopedMutex(svs.serverMutex);
174 		svs.ge->ClientDisconnect(*drop->player);
175 	}
176 
177 	NET_StreamFinished(drop->stream);
178 	drop->stream = nullptr;
179 
180 	drop->player->setInUse(false);
181 	SV_SetClientState(drop, cs_free);
182 	drop->name[0] = 0;
183 
184 	if (svs.abandon) {
185 		int count = 0;
186 		client_t* cl = nullptr;
187 		while ((cl = SV_GetNextClient(cl)) != nullptr)
188 			if (cl->state >= cs_connected)
189 				count++;
190 		if (count == 0)
191 			svs.killserver = true;
192 	}
193 }
194 
195 /*
196 ==============================================================================
197 CONNECTIONLESS COMMANDS
198 ==============================================================================
199 */
200 
201 /**
202  * @brief Find or allocate a bucket for an address
203  */
SVC_BucketForAddress(struct net_stream & address,int burst,int period)204 static leakyBucket_t* SVC_BucketForAddress (struct net_stream &address, int burst, int period)
205 {
206 	char node[64];
207 	NET_StreamPeerToName(&address, node, sizeof(node), false);
208 
209 	const long hash = Com_HashKey(node, MAX_HASHES);
210 	const int now = Sys_Milliseconds();
211 
212 	for (leakyBucket_t* bucket = bucketHashes[hash]; bucket; bucket = bucket->next) {
213 		if (!Q_streq(bucket->node, node))
214 			continue;
215 
216 		return bucket;
217 	}
218 
219 	for (int i = 0; i < MAX_BUCKETS; i++) {
220 		leakyBucket_t* bucket = &buckets[i];
221 		const int interval = now - bucket->lastTime;
222 
223 		/* Reclaim expired buckets */
224 		if (bucket->lastTime > 0 && (interval > (burst * period) || interval < 0)) {
225 			if (bucket->prev != nullptr) {
226 				bucket->prev->next = bucket->next;
227 			} else {
228 				bucketHashes[bucket->hash] = bucket->next;
229 			}
230 
231 			if (bucket->next != nullptr) {
232 				bucket->next->prev = bucket->prev;
233 			}
234 
235 			OBJZERO(*bucket);
236 		}
237 
238 		if (Q_strnull(bucket->node)) {
239 			Q_strncpyz(bucket->node, node, sizeof(bucket->node));
240 			bucket->lastTime = now;
241 			bucket->burst = 0;
242 			bucket->hash = hash;
243 
244 			/* Add to the head of the relevant hash chain */
245 			bucket->next = bucketHashes[hash];
246 			if (bucketHashes[hash] != nullptr) {
247 				bucketHashes[hash]->prev = bucket;
248 			}
249 
250 			bucket->prev = nullptr;
251 			bucketHashes[hash] = bucket;
252 
253 			return bucket;
254 		}
255 	}
256 
257 	/* Couldn't allocate a bucket for this address */
258 	return nullptr;
259 }
260 
SVC_RateLimit(leakyBucket_t * bucket,int burst=10,int period=100)261 static bool SVC_RateLimit (leakyBucket_t* bucket, int burst = 10, int period = 100)
262 {
263 	if (bucket == nullptr)
264 		return true;
265 
266 	const int now = Sys_Milliseconds();
267 	const int interval = now - bucket->lastTime;
268 	const int expired = interval / period;
269 	const int expiredRemainder = interval % period;
270 
271 	if (expired > bucket->burst) {
272 		bucket->burst = 0;
273 		bucket->lastTime = now;
274 	} else {
275 		bucket->burst -= expired;
276 		bucket->lastTime = now - expiredRemainder;
277 	}
278 
279 	if (bucket->burst < burst) {
280 		bucket->burst++;
281 		return false;
282 	}
283 
284 	return true;
285 }
286 
287 /**
288  * @brief Rate limit for a particular address
289  */
SVC_RateLimitAddress(struct net_stream & from,int burst=10,int period=1000)290 static bool SVC_RateLimitAddress (struct net_stream &from, int burst = 10, int period = 1000)
291 {
292 	leakyBucket_t* bucket = SVC_BucketForAddress(from, burst, period);
293 	return SVC_RateLimit(bucket, burst, period);
294 }
295 
296 /**
297  * @brief Responds with teaminfo such as free team num
298  * @sa CL_ParseTeamInfoMessage
299  */
SVC_TeamInfo(struct net_stream * s)300 static void SVC_TeamInfo (struct net_stream *s)
301 {
302 	if (SVC_RateLimitAddress(*s)) {
303 		Com_DPrintf(DEBUG_SERVER, "SVC_TeamInfo: rate limit from %s exceeded, dropping request\n", NET_StreamToString(s));
304 		return;
305 	}
306 
307 	/* Allow getinfo to be DoSed relatively easily, but prevent excess outbound bandwidth usage when being flooded inbound */
308 	if (SVC_RateLimit(&outboundLeakyBucket)) {
309 		Com_DPrintf(DEBUG_SERVER, "SVC_TeamInfo: rate limit exceeded, dropping request\n");
310 		return;
311 	}
312 
313 	char infoGlobal[MAX_INFO_STRING] = "";
314 	Info_SetValueForKey(infoGlobal, sizeof(infoGlobal), "sv_teamplay", Cvar_GetString("sv_teamplay"));
315 	Info_SetValueForKey(infoGlobal, sizeof(infoGlobal), "sv_maxteams", Cvar_GetString("sv_maxteams"));
316 	Info_SetValueForKey(infoGlobal, sizeof(infoGlobal), "sv_maxplayersperteam", Cvar_GetString("sv_maxplayersperteam"));
317 
318 	dbuffer msg;
319 	NET_WriteByte(&msg, svc_oob);
320 	NET_WriteRawString(&msg, "teaminfo\n");
321 	NET_WriteString(&msg, infoGlobal);
322 
323 	client_t* cl = nullptr;
324 	while ((cl = SV_GetNextClient(cl)) != nullptr) {
325 		if (cl->state < cs_connected)
326 			continue;
327 		char infoPlayer[MAX_INFO_STRING] = "";
328 		/* show players that already have a team with their teamnum */
329 		int teamId = svs.ge->ClientGetTeamNum(*cl->player);
330 		if (!teamId)
331 			teamId = TEAM_NO_ACTIVE;
332 		Info_SetValueForKeyAsInteger(infoPlayer, sizeof(infoPlayer), "cl_team", teamId);
333 		Info_SetValueForKeyAsInteger(infoPlayer, sizeof(infoPlayer), "cl_ready", svs.ge->ClientIsReady(cl->player));
334 		Info_SetValueForKey(infoPlayer, sizeof(infoPlayer), "cl_name", cl->name);
335 		NET_WriteString(&msg, infoPlayer);
336 	}
337 
338 	NET_WriteByte(&msg, 0);
339 
340 	NET_WriteMsg(s, msg);
341 }
342 
343 /**
344  * @brief Responds with all the info that the server browser can see
345  * @sa SV_StatusString
346  */
SVC_Status(struct net_stream * s)347 static void SVC_Status (struct net_stream *s)
348 {
349 	if (SVC_RateLimitAddress(*s)) {
350 		Com_DPrintf(DEBUG_SERVER, "SVC_Status: rate limit from %s exceeded, dropping request\n", NET_StreamToString(s));
351 		return;
352 	}
353 
354 	/* Allow getstatus to be DoSed relatively easily, but prevent excess outbound bandwidth usage when being flooded inbound */
355 	if (SVC_RateLimit(&outboundLeakyBucket, 10, 100)) {
356 		Com_DPrintf(DEBUG_SERVER, "SVC_Status: rate limit exceeded, dropping request\n");
357 		return;
358 	}
359 
360 	dbuffer msg;
361 	NET_WriteByte(&msg, svc_oob);
362 	NET_WriteRawString(&msg, SV_CMD_PRINT "\n");
363 	char info[MAX_INFO_STRING];
364 	NET_WriteRawString(&msg, Cvar_Serverinfo(info, sizeof(info)));
365 	NET_WriteRawString(&msg, "\n");
366 
367 	client_t* cl = nullptr;
368 	while ((cl = SV_GetNextClient(cl)) != nullptr) {
369 		if (cl->state <= cs_free)
370 			continue;
371 
372 		char player[1024];
373 		Com_sprintf(player, sizeof(player), "%i \"%s\"\n", svs.ge->ClientGetTeamNum(*cl->player), cl->name);
374 		NET_WriteRawString(&msg, player);
375 	}
376 
377 	NET_WriteMsg(s, msg);
378 }
379 
380 /**
381  * @brief Responds with short info for broadcast scans
382  * @note The second parameter should be the current protocol version number.
383  * @note Only a short server description - the user can determine whether he is
384  * interested in a full status
385  * @sa CL_ParseStatusMessage
386  * @sa CL_ProcessPingReply
387  */
SVC_Info(struct net_stream * s)388 static void SVC_Info (struct net_stream *s)
389 {
390 	if (SVC_RateLimitAddress(*s)) {
391 		Com_DPrintf(DEBUG_SERVER, "SVC_Info: rate limit from %s exceeded, dropping request\n", NET_StreamToString(s));
392 		return;
393 	}
394 
395 	/* Allow getinfo to be DoSed relatively easily, but prevent excess outbound bandwidth usage when being flooded inbound */
396 	if (SVC_RateLimit(&outboundLeakyBucket)) {
397 		Com_DPrintf(DEBUG_SERVER, "SVC_Info: rate limit exceeded, dropping request\n");
398 		return;
399 	}
400 
401 	if (sv_maxclients->integer == 1) {
402 		Com_DPrintf(DEBUG_SERVER, "Ignore info string in singleplayer mode\n");
403 		return;	/* ignore in single player */
404 	}
405 
406 	const int version = atoi(Cmd_Argv(1));
407 	if (version != PROTOCOL_VERSION) {
408 		char string[MAX_VAR];
409 		Com_sprintf(string, sizeof(string), "%s: wrong version (client: %i, host: %i)\n", sv_hostname->string, version, PROTOCOL_VERSION);
410 		NET_OOB_Printf(s, SV_CMD_PRINT "\n%s", string);
411 		return;
412 	}
413 
414 	int count = 0;
415 
416 	client_t* cl = nullptr;
417 	while ((cl = SV_GetNextClient(cl)) != nullptr)
418 		if (cl->state >= cs_spawning)
419 			count++;
420 
421 	char infostring[MAX_INFO_STRING];
422 	infostring[0] = '\0';
423 	Info_SetValueForKey(infostring, sizeof(infostring), "sv_protocol", DOUBLEQUOTE(PROTOCOL_VERSION));
424 	Info_SetValueForKey(infostring, sizeof(infostring), "sv_hostname", sv_hostname->string);
425 	Info_SetValueForKey(infostring, sizeof(infostring), "sv_dedicated", sv_dedicated->string);
426 	Info_SetValueForKey(infostring, sizeof(infostring), "sv_gametype", sv_gametype->string);
427 	Info_SetValueForKey(infostring, sizeof(infostring), "sv_mapname", sv->name);
428 	Info_SetValueForKeyAsInteger(infostring, sizeof(infostring), "clients", count);
429 	Info_SetValueForKey(infostring, sizeof(infostring), "sv_maxclients", sv_maxclients->string);
430 	Info_SetValueForKey(infostring, sizeof(infostring), "sv_version", UFO_VERSION);
431 	NET_OOB_Printf(s, SV_CMD_INFO "\n%s", infostring);
432 }
433 
434 
435 /**
436  * @brief A connection request that did not come from the master
437  * @sa CL_ConnectionlessPacket
438  */
SVC_DirectConnect(struct net_stream * stream)439 static void SVC_DirectConnect (struct net_stream *stream)
440 {
441 	Com_DPrintf(DEBUG_SERVER, "SVC_DirectConnect()\n");
442 
443 	if (sv->started || sv->spawned) {
444 		Com_Printf("rejected connect because match is already running\n");
445 		NET_OOB_Printf(stream, SV_CMD_PRINT "\n" REJ_GAME_ALREADY_STARTED "\n");
446 		return;
447 	}
448 
449 	char buf[256];
450 	const char* peername = NET_StreamPeerToName(stream, buf, sizeof(buf), false);
451 
452 	const int version = atoi(Cmd_Argv(1));
453 	if (version != PROTOCOL_VERSION) {
454 		Com_Printf("rejected connect from version %i - %s\n", version, peername);
455 		NET_OOB_Printf(stream, SV_CMD_PRINT "\n" REJ_SERVER_VERSION_MISMATCH "\n");
456 		return;
457 	}
458 
459 	char userinfo[MAX_INFO_STRING];
460 	Q_strncpyz(userinfo, Cmd_Argv(2), sizeof(userinfo));
461 
462 	if (Q_strnull(userinfo)) {  /* catch empty userinfo */
463 		Com_Printf("Empty userinfo from %s\n", peername);
464 		NET_OOB_Printf(stream, SV_CMD_PRINT "\n" REJ_CONNECTION_REFUSED "\n");
465 		return;
466 	}
467 
468 	if (strchr(userinfo, '\xFF')) {  /* catch end of message in string exploit */
469 		Com_Printf("Illegal userinfo contained xFF from %s\n", peername);
470 		NET_OOB_Printf(stream, SV_CMD_PRINT "\n" REJ_CONNECTION_REFUSED "\n");
471 		return;
472 	}
473 
474 	if (strlen(Info_ValueForKey(userinfo, "ip"))) {  /* catch spoofed ips  */
475 		Com_Printf("Illegal userinfo contained ip from %s\n", peername);
476 		NET_OOB_Printf(stream, SV_CMD_PRINT "\n" REJ_CONNECTION_REFUSED "\n");
477 		return;
478 	}
479 
480 	/* force the IP key/value pair so the game can filter based on ip */
481 	Info_SetValueForKey(userinfo, sizeof(userinfo), "ip", peername);
482 
483 	/* find a client slot */
484 	client_t* cl = nullptr;
485 	while ((cl = SV_GetNextClient(cl)) != nullptr)
486 		if (cl->state == cs_free)
487 			break;
488 	if (cl == nullptr) {
489 		NET_OOB_Printf(stream, SV_CMD_PRINT "\n" REJ_SERVER_FULL "\n");
490 		Com_Printf("Rejected a connection - server is full.\n");
491 		return;
492 	}
493 
494 	/* build a new connection - accept the new client
495 	 * this is the only place a client_t is ever initialized */
496 	OBJZERO(*cl);
497 	const int playernum = cl - SV_GetClient(0);
498 	SrvPlayer *player = PLAYER_NUM(playernum);
499 	cl->player = player;
500 	cl->player->setNum(playernum);
501 
502 	bool connected;
503 	{
504 		const ScopedMutex scopedMutex(svs.serverMutex);
505 		connected = svs.ge->ClientConnect(player, userinfo, sizeof(userinfo));
506 	}
507 
508 	/* get the game a chance to reject this connection or modify the userinfo */
509 	if (!connected) {
510 		const char* rejmsg = Info_ValueForKey(userinfo, "rejmsg");
511 		if (rejmsg[0] != '\0') {
512 			NET_OOB_Printf(stream, SV_CMD_PRINT "\n%s\n" REJ_CONNECTION_REFUSED "\n", rejmsg);
513 			Com_Printf("Game rejected a connection from %s. Reason: %s\n", peername, rejmsg);
514 		} else {
515 			NET_OOB_Printf(stream, SV_CMD_PRINT "\n" REJ_CONNECTION_REFUSED "\n");
516 			Com_Printf("Game rejected a connection from %s.\n", peername);
517 		}
518 		return;
519 	}
520 
521 	/* new player */
522 	cl->player->setInUse(true);
523 	cl->lastmessage = svs.realtime;
524 
525 	/* parse some info from the info strings */
526 	Q_strncpyz(cl->userinfo, userinfo, sizeof(cl->userinfo));
527 	SV_UserinfoChanged(cl);
528 
529 	/* send the connect packet to the client */
530 	if (sv_http_downloadserver->string[0])
531 		NET_OOB_Printf(stream, CL_CMD_CLIENT_CONNECT " dlserver=%s", sv_http_downloadserver->string);
532 	else
533 		NET_OOB_Printf(stream, CL_CMD_CLIENT_CONNECT);
534 
535 	SV_SetClientState(cl, cs_connected);
536 
537 	Q_strncpyz(cl->peername, peername, sizeof(cl->peername));
538 	cl->stream = stream;
539 	NET_StreamSetData(stream, cl);
540 }
541 
542 /**
543  * @brief Checks whether the remote connection is allowed (rcon_password must be
544  * set on the server) - and verify the user given password with the cvar value.
545  */
Rcon_Validate(const char * password)546 static inline bool Rcon_Validate (const char* password)
547 {
548 	/* no rcon access */
549 	if (Q_strnull(rcon_password->string))
550 		return false;
551 
552 	/* password not valid */
553 	if (!Q_streq(password, rcon_password->string))
554 		return false;
555 
556 	return true;
557 }
558 
559 /**
560  * @brief A client issued an rcon command. Shift down the remaining args. Redirect all printfs
561  */
SVC_RemoteCommand(struct net_stream * stream)562 static void SVC_RemoteCommand (struct net_stream *stream)
563 {
564 	char buf[64];
565 	const char* peername = NET_StreamPeerToName(stream, buf, sizeof(buf), false);
566 
567 	/* Prevent using rcon as an amplifier and make dictionary attacks impractical */
568 	if (SVC_RateLimitAddress(*stream)) {
569 		Com_DPrintf(DEBUG_SERVER, "SVC_RemoteCommand: rate limit from %s exceeded, dropping request\n", peername);
570 		return;
571 	}
572 
573 	const bool valid = Rcon_Validate(Cmd_Argv(1));
574 	if (!valid) {
575 		static leakyBucket_t bucket;
576 		/* Make DoS via rcon impractical */
577 		if (SVC_RateLimit(&bucket, 10, 1000)) {
578 			Com_DPrintf(DEBUG_SERVER, "SVC_RemoteCommand: rate limit exceeded, dropping request\n");
579 			return;
580 		}
581 
582 		Com_Printf("Bad rcon from %s with password: '%s'\n", peername, Cmd_Argv(1));
583 	} else {
584 		Com_Printf("Rcon from %s\n", peername);
585 	}
586 
587 	static char sv_outputbuf[1024];
588 	Com_BeginRedirect(stream, sv_outputbuf, sizeof(sv_outputbuf));
589 
590 	if (!valid) {
591 		/* inform the client */
592 		Com_Printf(BAD_RCON_PASSWORD);
593 	} else {
594 		char remaining[1024] = "";
595 		int i;
596 
597 		/* execute the rcon commands */
598 		for (i = 2; i < Cmd_Argc(); i++) {
599 			Q_strcat(remaining, sizeof(remaining), "%s ", Cmd_Argv(i));
600 		}
601 
602 		/* execute the string */
603 		Cmd_ExecuteString("%s", remaining);
604 	}
605 
606 	Com_EndRedirect();
607 }
608 
609 /**
610  * @brief Handles a connectionless message from a client
611  * @sa NET_OOB_Printf
612  * @param[out] stream The stream to write to
613  * @param msg The message buffer to read the connectionless data from
614  */
SV_ConnectionlessPacket(struct net_stream * stream,dbuffer * msg)615 static void SV_ConnectionlessPacket (struct net_stream *stream, dbuffer* msg)
616 {
617 	char s[512];
618 
619 	NET_ReadStringLine(msg, s, sizeof(s));
620 	Cmd_TokenizeString(s, false, false);
621 
622 	const char* c = Cmd_Argv(0);
623 	Com_DPrintf(DEBUG_SERVER, "Packet : %s\n", c);
624 
625 	if (Q_streq(c, SV_CMD_TEAMINFO)) {
626 		SVC_TeamInfo(stream);
627 	} else if (Q_streq(c, SV_CMD_INFO)) {
628 		SVC_Info(stream);
629 	} else if (Q_streq(c, SV_CMD_STATUS)) {
630 		SVC_Status(stream);
631 	} else if (Q_streq(c, SV_CMD_CONNECT)) {
632 		SVC_DirectConnect(stream);
633 	} else if (Q_streq(c, SV_CMD_RCON)) {
634 		SVC_RemoteCommand(stream);
635 	} else {
636 		char buf[256];
637 		Com_Printf("Bad connectionless packet from %s:\n%s\n", NET_StreamPeerToName(stream, buf, sizeof(buf), true), s);
638 	}
639 }
640 
641 /**
642  * @sa CL_ReadPacket
643  * @sa NET_ReadMsg
644  * @sa SV_Start
645  */
SV_ReadPacket(struct net_stream * s)646 void SV_ReadPacket (struct net_stream *s)
647 {
648 	client_t* cl = static_cast<client_t* >(NET_StreamGetData(s));
649 	dbuffer* msg;
650 
651 	while ((msg = NET_ReadMsg(s))) {
652 		const int cmd = NET_ReadByte(msg);
653 
654 		if (cmd == clc_oob)
655 			SV_ConnectionlessPacket(s, msg);
656 		else if (cl)
657 			SV_ExecuteClientMessage(cl, cmd, msg);
658 		else {
659 			NET_StreamFree(s);
660 			s = nullptr;
661 		}
662 
663 		delete msg;
664 	}
665 }
666 
667 #define	HEARTBEAT_SECONDS	30
668 
669 static SDL_Thread *masterServerHeartBeatThread;
670 
671 /**
672  * @brief Send a message to the master every few minutes to
673  * let it know we are alive, and log information
674  */
Master_HeartbeatThread(void * data)675 static int Master_HeartbeatThread (void*  data)
676 {
677 	char url[512];
678 	Com_sprintf(url, sizeof(url), "%s/ufo/masterserver.php?heartbeat&port=%s", masterserver_url->string, port->string);
679 
680 	/* send to master */
681 	Com_Printf("sending heartbeat\n");
682 	HTTP_GetURL(url, nullptr);
683 
684 	masterServerHeartBeatThread = nullptr;
685 	return 0;
686 }
687 
688 /**
689  * @sa CL_PingServers_f
690  */
Master_Heartbeat(void)691 static void Master_Heartbeat (void)
692 {
693 	if (!sv_dedicated || !sv_dedicated->integer)
694 		return;		/* only dedicated servers send heartbeats */
695 
696 	if (!sv_public || !sv_public->integer)
697 		return;		/* a private dedicated game */
698 
699 	/* check for time wraparound */
700 	if (svs.lastHeartbeat > svs.realtime)
701 		svs.lastHeartbeat = svs.realtime;
702 
703 	if (svs.realtime - svs.lastHeartbeat < HEARTBEAT_SECONDS * 1000)
704 		return;					/* not time to send yet */
705 
706 	svs.lastHeartbeat = svs.realtime;
707 
708 	if (masterServerHeartBeatThread != nullptr)
709 		SDL_WaitThread(masterServerHeartBeatThread, nullptr);
710 
711 #if SDL_VERSION_ATLEAST(2,0,0)
712 	masterServerHeartBeatThread = SDL_CreateThread(Master_HeartbeatThread, "HeartbeatThread", nullptr);
713 #else
714 	masterServerHeartBeatThread = SDL_CreateThread(Master_HeartbeatThread, nullptr);
715 #endif
716 }
717 
718 /**
719  * @brief If all connected clients have set their ready flag the server will spawn the clients
720  * and that change the client state.
721  * @sa SV_Spawn_f
722  */
SV_CheckSpawnSoldiers(void)723 static void SV_CheckSpawnSoldiers (void)
724 {
725 	/* already started? */
726 	if (sv->spawned)
727 		return;
728 
729 	client_t* cl = nullptr;
730 	while ((cl = SV_GetNextClient(cl)) != nullptr) {
731 		/* all players must be connected and all of them must have set
732 		 * the ready flag */
733 		if (cl->state != cs_began || !cl->player->isReady())
734 			return;
735 	}
736 
737 	sv->spawned = true;
738 
739 	cl = nullptr;
740 	while ((cl = SV_GetNextClient(cl)) != nullptr)
741 		if (cl->state != cs_free)
742 			SV_ClientCommand(cl, CL_SPAWNSOLDIERS "\n");
743 }
744 
SV_CheckStartMatch(void)745 static void SV_CheckStartMatch (void)
746 {
747 	client_t* cl;
748 
749 	if (!sv->spawned || sv->started)
750 		return;
751 
752 	if (sv_maxclients->integer > 1) {
753 		cl = nullptr;
754 		while ((cl = SV_GetNextClient(cl)) != nullptr) {
755 			/* all players must have their actors spawned */
756 			if (cl->state != cs_spawned)
757 				return;
758 		}
759 	} else if (SV_GetClient(0)->state != cs_spawned) {
760 		/* in single player mode we must have received the 'spawnsoldiers' */
761 		return;
762 	}
763 
764 	sv->started = true;
765 
766 	cl = nullptr;
767 	while ((cl = SV_GetNextClient(cl)) != nullptr)
768 		if (cl->state != cs_free)
769 			SV_ClientCommand(cl, CL_STARTMATCH "\n");
770 }
771 
772 #define	PING_SECONDS	5
773 
SV_PingPlayers(void)774 static void SV_PingPlayers (void)
775 {
776 	/* check for time wraparound */
777 	if (svs.lastPing > svs.realtime)
778 		svs.lastPing = svs.realtime;
779 
780 	if (svs.realtime - svs.lastPing < PING_SECONDS * 1000)
781 		return;					/* not time to send yet */
782 
783 	svs.lastPing = svs.realtime;
784 	client_t* cl = nullptr;
785 	while ((cl = SV_GetNextClient(cl)) != nullptr) {
786 		if (cl->state == cs_free)
787 			continue;
788 		dbuffer msg(1);
789 		NET_WriteByte(&msg, svc_ping);
790 		NET_WriteMsg(cl->stream, msg);
791 	}
792 }
793 
SV_CheckTimeouts(void)794 static void SV_CheckTimeouts (void)
795 {
796 	const int droppoint = svs.realtime - 1000 * sv_timeout->integer;
797 
798 	if (sv_maxclients->integer == 1)
799 		return;
800 
801 	client_t* cl = nullptr;
802 	while ((cl = SV_GetNextClient(cl)) != nullptr) {
803 		if (cl->state == cs_free)
804 			continue;
805 
806 		/* might be invalid across a mapchange */
807 		if (cl->lastmessage > svs.realtime)
808 			cl->lastmessage = svs.realtime;
809 
810 		if (cl->lastmessage > 0 && cl->lastmessage < droppoint)
811 			SV_DropClient(cl, "timed out");
812 	}
813 }
814 
815 /**
816  * @sa Qcommon_Frame
817  */
SV_Frame(int now,void * data)818 void SV_Frame (int now, void* data)
819 {
820 	Com_ReadFromPipe();
821 
822 	/* change the gametype even if no server is running (e.g. the first time) */
823 	if (sv_dedicated->integer && sv_gametype->modified) {
824 		Com_SetGameType();
825 		sv_gametype->modified = false;
826 	}
827 
828 	if (sv_dedicated->integer) {
829 		const char* s;
830 		do {
831 			s = Sys_ConsoleInput();
832 			if (s)
833 				Cbuf_AddText("%s\n", s);
834 		} while (s);
835 	}
836 
837 	/* if server is not active, do nothing */
838 	if (!svs.initialized) {
839 #ifdef DEDICATED_ONLY
840 		Com_Printf("Starting next map from the mapcycle\n");
841 		SV_NextMapcycle();
842 #endif
843 		return;
844 	}
845 
846 	svs.realtime = now;
847 
848 	/* if time is about to hit the 32nd bit, kick all clients
849 	 * and clear sv.time, rather
850 	 * than checking for negative time wraparound everywhere.
851 	 * 2giga-milliseconds = 23 days, so it won't be too often */
852 	if (svs.realtime > 0x70000000) {
853 		SV_Map(true, sv->name, sv->assembly);
854 		return;
855 	}
856 
857 	/* keep the random time dependent */
858 	rand();
859 
860 	SV_CheckSpawnSoldiers();
861 	SV_CheckStartMatch();
862 	SV_CheckTimeouts();
863 
864 	if (!sv_threads->integer)
865 		SV_RunGameFrame();
866 	else
867 		/* signal the game frame thread to wake up */
868 		SDL_CondSignal(svs.gameFrameCond);
869 	SV_LogHandleOutput();
870 
871 	/* next map in the cycle */
872 	if (sv->endgame && sv_maxclients->integer > 1)
873 		SV_NextMapcycle();
874 
875 	/* send a heartbeat to the master if needed */
876 	Master_Heartbeat();
877 	SV_PingPlayers();
878 
879 	/* server is empty - so shutdown */
880 	if (svs.abandon && svs.killserver)
881 		SV_Shutdown("Server disconnected.", false);
882 }
883 
884 /**
885  * @brief Informs all masters that this server is going down
886  */
Master_Shutdown(void)887 static void Master_Shutdown (void)
888 {
889 	if (!sv_dedicated || !sv_dedicated->integer)
890 		return;					/* only dedicated servers send heartbeats */
891 
892 	if (!sv_public || !sv_public->integer)
893 		return;					/* a private dedicated game */
894 
895 	/* send to master */
896 	HTTP_GetURL(va("%s/ufo/masterserver.php?shutdown&port=%s", masterserver_url->string, port->string), nullptr);
897 }
898 
899 /**
900  * @brief Pull specific info from a newly changed userinfo string into a more C friendly form.
901  */
SV_UserinfoChanged(client_t * cl)902 void SV_UserinfoChanged (client_t* cl)
903 {
904 	unsigned int i;
905 
906 	/* call prog code to allow overrides */
907 	{
908 		const ScopedMutex scopedMutex(svs.serverMutex);
909 		svs.ge->ClientUserinfoChanged(*cl->player, cl->userinfo);
910 	}
911 
912 	/* name of the player */
913 	Q_strncpyz(cl->name, Info_ValueForKey(cl->userinfo, "cl_name"), sizeof(cl->name));
914 	/* mask off high bit */
915 	for (i = 0; i < sizeof(cl->name); i++)
916 		cl->name[i] &= 127;
917 
918 	/* msg command */
919 	cl->messagelevel = Info_IntegerForKey(cl->userinfo, "cl_msg");
920 
921 	Com_DPrintf(DEBUG_SERVER, "SV_UserinfoChanged: Changed userinfo for player %s\n", cl->name);
922 }
923 
SV_CheckMaxSoldiersPerPlayer(cvar_t * cvar)924 static bool SV_CheckMaxSoldiersPerPlayer (cvar_t* cvar)
925 {
926 	const int max = MAX_ACTIVETEAM;
927 	return Cvar_AssertValue(cvar, 1, max, true);
928 }
929 
SV_GetMapData(void)930 mapData_t* SV_GetMapData (void)
931 {
932 	return &sv->mapData;
933 }
934 
SV_GetMapTiles(void)935 mapTiles_t* SV_GetMapTiles (void)
936 {
937 	return &sv->mapTiles;
938 }
939 
940 /**
941  * @brief Only called once at startup, not for each game
942  */
SV_Init(void)943 void SV_Init (void)
944 {
945 	Com_Printf("\n------ server initialization -------\n");
946 
947 	sv_genericPool = Mem_CreatePool("Server: Generic");
948 
949 	OBJZERO(svs);
950 
951 	sv = Mem_PoolAllocType(serverInstanceGame_t, sv_genericPool);
952 
953 	SV_InitOperatorCommands();
954 
955 	rcon_password = Cvar_Get("rcon_password", "", CVAR_ARCHIVE);
956 	Cvar_Get("sv_cheats", "0", CVAR_SERVERINFO | CVAR_LATCH);
957 	Cvar_Get("sv_protocol", DOUBLEQUOTE(PROTOCOL_VERSION), CVAR_SERVERINFO | CVAR_NOSET);
958 	/* this cvar will become a latched cvar when you start the server */
959 	sv_maxclients = Cvar_Get("sv_maxclients", "1", CVAR_SERVERINFO, "Max. connected clients");
960 	sv_hostname = Cvar_Get("sv_hostname", "noname", CVAR_SERVERINFO | CVAR_ARCHIVE, "The name of the server that is displayed in the serverlist");
961 	sv_http_downloadserver = Cvar_Get("sv_http_downloadserver", "", CVAR_ARCHIVE, "URL to a location where clients can download game content over HTTP");
962 	sv_enablemorale = Cvar_Get("sv_enablemorale", "1", CVAR_ARCHIVE | CVAR_SERVERINFO | CVAR_LATCH, "Enable morale changes in multiplayer");
963 	sv_maxsoldiersperteam = Cvar_Get("sv_maxsoldiersperteam", "4", CVAR_ARCHIVE | CVAR_SERVERINFO, "Max. amount of soldiers per team (see sv_maxsoldiersperplayer and sv_teamplay)");
964 	sv_maxsoldiersperplayer = Cvar_Get("sv_maxsoldiersperplayer", DOUBLEQUOTE(MAX_ACTIVETEAM), CVAR_ARCHIVE | CVAR_SERVERINFO, "Max. amount of soldiers each player can control (see maxsoldiers and sv_teamplay)");
965 	Cvar_SetCheckFunction("sv_maxsoldiersperplayer", SV_CheckMaxSoldiersPerPlayer);
966 
967 	sv_dumpmapassembly = Cvar_Get("sv_dumpmapassembly", "0", CVAR_ARCHIVE, "Dump map assembly information to game console");
968 
969 	sv_threads = Cvar_Get("sv_threads", "1", CVAR_LATCH | CVAR_ARCHIVE, "Run the server threaded");
970 	sv_rma = Cvar_Get("sv_rma", "2", 0, "1 = old algorithm, 2 = new algorithm");
971 	sv_rmadisplaythemap = Cvar_Get("sv_rmadisplaythemap", "0", 0, "Activate rma problem output");
972 	sv_public = Cvar_Get("sv_public", "1", 0, "Should heartbeats be send to the masterserver");
973 	sv_reconnect_limit = Cvar_Get("sv_reconnect_limit", "3", CVAR_ARCHIVE, "Minimum seconds between connect messages");
974 	sv_timeout = Cvar_Get("sv_timeout", "200", CVAR_ARCHIVE, "Seconds until a client times out");
975 
976 	SV_MapcycleInit();
977 	SV_LogInit();
978 }
979 
980 /**
981  * @brief Used by SV_Shutdown to send a final message to all
982  * connected clients before the server goes down.
983  * @sa SV_Shutdown
984  */
SV_FinalMessage(const char * message,bool reconnect)985 static void SV_FinalMessage (const char* message, bool reconnect)
986 {
987 	client_t* cl;
988 	dbuffer msg(2 + strlen(message));
989 
990 	if (reconnect)
991 		NET_WriteByte(&msg, svc_reconnect);
992 	else
993 		NET_WriteByte(&msg, svc_disconnect);
994 	NET_WriteString(&msg, message);
995 
996 	cl = nullptr;
997 	while ((cl = SV_GetNextClient(cl)) != nullptr)
998 		if (cl->state >= cs_connected) {
999 			NET_WriteConstMsg(cl->stream, msg);
1000 			NET_StreamFinished(cl->stream);
1001 			cl->stream = nullptr;
1002 		}
1003 
1004 	/* make sure, that this is send */
1005 	NET_Wait(0);
1006 }
1007 
1008 /**
1009  * @brief Cleanup when the whole game process is shutting down
1010  * @sa SV_Shutdown
1011  * @sa Com_Quit
1012  */
SV_Clear(void)1013 void SV_Clear (void)
1014 {
1015 	SV_MapcycleClear();
1016 	SV_LogShutdown();
1017 }
1018 
1019 /**
1020  * @brief Called when each game quits, before Sys_Quit or Sys_Error
1021  * @param[in] finalmsg The message all clients get as server shutdown message
1022  * @param[in] reconnect True if this is only a restart (new map or map restart),
1023  * false if the server shutdown completely and you also want to disconnect all clients
1024  */
SV_Shutdown(const char * finalmsg,bool reconnect)1025 void SV_Shutdown (const char* finalmsg, bool reconnect)
1026 {
1027 	unsigned int i;
1028 
1029 	if (!svs.initialized)
1030 		return;
1031 
1032 	if (svs.clients)
1033 		SV_FinalMessage(finalmsg, reconnect);
1034 
1035 	Com_Printf("Shutdown server: %s\n", finalmsg);
1036 
1037 	Master_Shutdown();
1038 	SV_ShutdownGameProgs();
1039 
1040 	NET_DatagramSocketClose(svs.netDatagramSocket);
1041 	SV_Stop();
1042 
1043 	for (i = 0; i < sv->numSVModels; i++) {
1044 		sv_model_t* model = &sv->svModels[i];
1045 		Mem_Free(model->name);
1046 	}
1047 
1048 	/* free current level */
1049 	OBJZERO(*sv);
1050 
1051 	/* free server static data */
1052 	Mem_Free(svs.clients);
1053 
1054 	SDL_DestroyMutex(svs.serverMutex);
1055 
1056 	OBJZERO(svs);
1057 
1058 	/* maybe we shut down before we init - e.g. in case of an error */
1059 	if (sv_maxclients)
1060 		sv_maxclients->flags &= ~CVAR_LATCH;
1061 
1062 	if (sv_mapname)
1063 		sv_mapname->flags &= ~CVAR_NOSET;
1064 }
1065 
1066 /**
1067  * @brief Will eventually shutdown the server once all clients have disconnected
1068  * @sa SV_CountPlayers
1069  */
SV_ShutdownWhenEmpty(void)1070 void SV_ShutdownWhenEmpty (void)
1071 {
1072 	svs.abandon = true;
1073 	/* pretend server is already dead, otherwise clients may try and reconnect */
1074 	Com_SetServerState(ss_dead);
1075 }
1076 
1077 /**
1078  * @brief Returns the number of spawned players
1079  * @sa SV_ShutdownWhenEmpty
1080  */
SV_CountPlayers(void)1081 int SV_CountPlayers (void)
1082 {
1083 	int count = 0;
1084 	client_t* cl;
1085 
1086 	if (!svs.initialized)
1087 		return 0;
1088 
1089 	cl = nullptr;
1090 	while ((cl = SV_GetNextClient(cl)) != nullptr) {
1091 		if (cl->state != cs_spawned)
1092 			continue;
1093 
1094 		count++;
1095 	}
1096 
1097 	return count;
1098 }
1099