1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4 
5 This file is part of Quake III Arena source code.
6 
7 Quake III Arena source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
11 
12 Quake III Arena source code is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Quake III Arena source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 ===========================================================================
21 */
22 
23 #include "server.h"
24 
25 /*
26 ===============================================================================
27 
28 OPERATOR CONSOLE ONLY COMMANDS
29 
30 These commands can only be entered from stdin or by a remote operator datagram
31 ===============================================================================
32 */
33 
34 
35 /*
36 ==================
37 SV_GetPlayerByHandle
38 
39 Returns the player with player id or name from Cmd_Argv(1)
40 ==================
41 */
SV_GetPlayerByHandle(void)42 static client_t *SV_GetPlayerByHandle( void ) {
43 	client_t	*cl;
44 	int			i;
45 	char		*s;
46 	char		cleanName[64];
47 
48 	// make sure server is running
49 	if ( !com_sv_running->integer ) {
50 		return NULL;
51 	}
52 
53 	if ( Cmd_Argc() < 2 ) {
54 		Com_Printf( "No player specified.\n" );
55 		return NULL;
56 	}
57 
58 	s = Cmd_Argv(1);
59 
60 	// Check whether this is a numeric player handle
61 	for(i = 0; s[i] >= '0' && s[i] <= '9'; i++);
62 
63 	if(!s[i])
64 	{
65 		int plid = atoi(s);
66 
67 		// Check for numeric playerid match
68 		if(plid >= 0 && plid < sv_maxclients->integer)
69 		{
70 			cl = &svs.clients[plid];
71 
72 			if(cl->state)
73 				return cl;
74 		}
75 	}
76 
77 	// check for a name match
78 	for ( i=0, cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++ ) {
79 		if ( !cl->state ) {
80 			continue;
81 		}
82 		if ( !Q_stricmp( cl->name, s ) ) {
83 			return cl;
84 		}
85 
86 		Q_strncpyz( cleanName, cl->name, sizeof(cleanName) );
87 		Q_CleanStr( cleanName );
88 		if ( !Q_stricmp( cleanName, s ) ) {
89 			return cl;
90 		}
91 	}
92 
93 	Com_Printf( "Player %s is not on the server\n", s );
94 
95 	return NULL;
96 }
97 
98 /*
99 ==================
100 SV_GetPlayerByNum
101 
102 Returns the player with idnum from Cmd_Argv(1)
103 ==================
104 */
SV_GetPlayerByNum(void)105 static client_t *SV_GetPlayerByNum( void ) {
106 	client_t	*cl;
107 	int			i;
108 	int			idnum;
109 	char		*s;
110 
111 	// make sure server is running
112 	if ( !com_sv_running->integer ) {
113 		return NULL;
114 	}
115 
116 	if ( Cmd_Argc() < 2 ) {
117 		Com_Printf( "No player specified.\n" );
118 		return NULL;
119 	}
120 
121 	s = Cmd_Argv(1);
122 
123 	for (i = 0; s[i]; i++) {
124 		if (s[i] < '0' || s[i] > '9') {
125 			Com_Printf( "Bad slot number: %s\n", s);
126 			return NULL;
127 		}
128 	}
129 	idnum = atoi( s );
130 	if ( idnum < 0 || idnum >= sv_maxclients->integer ) {
131 		Com_Printf( "Bad client slot: %i\n", idnum );
132 		return NULL;
133 	}
134 
135 	cl = &svs.clients[idnum];
136 	if ( !cl->state ) {
137 		Com_Printf( "Client %i is not active\n", idnum );
138 		return NULL;
139 	}
140 	return cl;
141 }
142 
143 //=========================================================
144 
145 
146 /*
147 ==================
148 SV_Map_f
149 
150 Restart the server on a different map
151 ==================
152 */
SV_Map_f(void)153 static void SV_Map_f( void ) {
154 	char		*cmd;
155 	char		*map;
156 	qboolean	killBots, cheat;
157 	char		expanded[MAX_QPATH];
158 	char		mapname[MAX_QPATH];
159 
160 	map = Cmd_Argv(1);
161 	if ( !map ) {
162 		return;
163 	}
164 
165 	// make sure the level exists before trying to change, so that
166 	// a typo at the server console won't end the game
167 	Com_sprintf (expanded, sizeof(expanded), "maps/%s.bsp", map);
168 	if ( FS_ReadFile (expanded, NULL) == -1 ) {
169 		Com_Printf ("Can't find map %s\n", expanded);
170 		return;
171 	}
172 
173 	// force latched values to get set
174 	Cvar_Get ("g_gametype", "0", CVAR_SERVERINFO | CVAR_USERINFO | CVAR_LATCH );
175 
176 	//Notice that we have done a restart
177 	sv_dorestart->integer = 0;
178 
179 	cmd = Cmd_Argv(0);
180 	if( Q_stricmpn( cmd, "sp", 2 ) == 0 ) {
181 		Cvar_SetValue( "g_gametype", GT_SINGLE_PLAYER );
182 		Cvar_SetValue( "g_doWarmup", 0 );
183 		// may not set sv_maxclients directly, always set latched
184 		Cvar_SetLatched( "sv_maxclients", "8" );
185 		cmd += 2;
186 		if (!Q_stricmp( cmd, "devmap" ) ) {
187 			cheat = qtrue;
188 		} else {
189 			cheat = qfalse;
190 		}
191 		killBots = qtrue;
192 	}
193 	else {
194 		if ( !Q_stricmp( cmd, "devmap" ) ) {
195 			cheat = qtrue;
196 			killBots = qtrue;
197 		} else {
198 			cheat = qfalse;
199 			killBots = qfalse;
200 		}
201 		if( sv_gametype->integer == GT_SINGLE_PLAYER ) {
202 			Cvar_SetValue( "g_gametype", GT_FFA );
203 		}
204 	}
205 
206 	// save the map name here cause on a map restart we reload the q3config.cfg
207 	// and thus nuke the arguments of the map command
208 	Q_strncpyz(mapname, map, sizeof(mapname));
209 
210 	// start up the map
211 	SV_SpawnServer( mapname, killBots );
212 
213 	// set the cheat value
214 	// if the level was started with "map <levelname>", then
215 	// cheats will not be allowed.  If started with "devmap <levelname>"
216 	// then cheats will be allowed
217 	if ( cheat ) {
218 		Cvar_Set( "sv_cheats", "1" );
219 	} else {
220 		Cvar_Set( "sv_cheats", "0" );
221 	}
222 }
223 
224 /*
225 ================
226 SV_MapRestart_f
227 
228 Completely restarts a level, but doesn't send a new gamestate to the clients.
229 This allows fair starts with variable load times.
230 ================
231 */
SV_MapRestart_f(void)232 static void SV_MapRestart_f( void ) {
233 	int			i;
234 	client_t	*client;
235 	char		*denied;
236 	qboolean	isBot;
237 	int			delay;
238 
239 	// make sure we aren't restarting twice in the same frame
240 	if ( com_frameTime == sv.serverId ) {
241 		return;
242 	}
243 
244 	// make sure server is running
245 	if ( !com_sv_running->integer ) {
246 		Com_Printf( "Server is not running.\n" );
247 		return;
248 	}
249 
250 	if ( sv.restartTime ) {
251 		return;
252 	}
253 
254 	if (Cmd_Argc() > 1 ) {
255 		delay = atoi( Cmd_Argv(1) );
256 	}
257 	else {
258 		delay = 5;
259 	}
260 	if( delay && !Cvar_VariableValue("g_doWarmup") ) {
261 		sv.restartTime = sv.time + delay * 1000;
262 		SV_SetConfigstring( CS_WARMUP, va("%i", sv.restartTime) );
263 		return;
264 	}
265 
266 	// check for changes in variables that can't just be restarted
267 	// check for maxclients change
268 	if ( sv_maxclients->modified || sv_gametype->modified || sv_dorestart->integer ) {
269 		char	mapname[MAX_QPATH];
270 
271 		sv_dorestart->integer = 0;
272 		Com_Printf( "variable change -- restarting.\n" );
273 		// restart the map the slow way
274 		Q_strncpyz( mapname, Cvar_VariableString( "mapname" ), sizeof( mapname ) );
275 
276 		SV_SpawnServer( mapname, qfalse );
277 		return;
278 	}
279 
280 	// toggle the server bit so clients can detect that a
281 	// map_restart has happened
282 	svs.snapFlagServerBit ^= SNAPFLAG_SERVERCOUNT;
283 
284 	// generate a new serverid
285 	// TTimo - don't update restartedserverId there, otherwise we won't deal correctly with multiple map_restart
286 	sv.serverId = com_frameTime;
287 	Cvar_Set( "sv_serverid", va("%i", sv.serverId ) );
288 
289 	// if a map_restart occurs while a client is changing maps, we need
290 	// to give them the correct time so that when they finish loading
291 	// they don't violate the backwards time check in cl_cgame.c
292 	for (i=0 ; i<sv_maxclients->integer ; i++) {
293 		if (svs.clients[i].state == CS_PRIMED) {
294 			svs.clients[i].oldServerTime = sv.restartTime;
295 		}
296 	}
297 
298 	// reset all the vm data in place without changing memory allocation
299 	// note that we do NOT set sv.state = SS_LOADING, so configstrings that
300 	// had been changed from their default values will generate broadcast updates
301 	sv.state = SS_LOADING;
302 	sv.restarting = qtrue;
303 
304 	SV_RestartGameProgs();
305 
306 	// run a few frames to allow everything to settle
307 	for (i = 0; i < 3; i++)
308 	{
309 		VM_Call (gvm, GAME_RUN_FRAME, sv.time);
310 		sv.time += 100;
311 		svs.time += 100;
312 	}
313 
314 	sv.state = SS_GAME;
315 	sv.restarting = qfalse;
316 
317 	// connect and begin all the clients
318 	for (i=0 ; i<sv_maxclients->integer ; i++) {
319 		client = &svs.clients[i];
320 
321 		// send the new gamestate to all connected clients
322 		if ( client->state < CS_CONNECTED) {
323 			continue;
324 		}
325 
326 		if ( client->netchan.remoteAddress.type == NA_BOT ) {
327 			isBot = qtrue;
328 		} else {
329 			isBot = qfalse;
330 		}
331 
332 		// add the map_restart command
333 		SV_AddServerCommand( client, "map_restart\n" );
334 
335 		// connect the client again, without the firstTime flag
336 		denied = VM_ExplicitArgPtr( gvm, VM_Call( gvm, GAME_CLIENT_CONNECT, i, qfalse, isBot ) );
337 		if ( denied ) {
338 			// this generally shouldn't happen, because the client
339 			// was connected before the level change
340 			SV_DropClient( client, denied );
341 			Com_Printf( "SV_MapRestart_f(%d): dropped client %i - denied!\n", delay, i );
342 			continue;
343 		}
344 
345 		if(client->state == CS_ACTIVE)
346 			SV_ClientEnterWorld(client, &client->lastUsercmd);
347 		else
348 		{
349 			// If we don't reset client->lastUsercmd and are restarting during map load,
350 			// the client will hang because we'll use the last Usercmd from the previous map,
351 			// which is wrong obviously.
352 			SV_ClientEnterWorld(client, NULL);
353 		}
354 	}
355 
356 	// run another frame to allow things to look at all the players
357 	VM_Call (gvm, GAME_RUN_FRAME, sv.time);
358 	sv.time += 100;
359 	svs.time += 100;
360 }
361 
362 //===============================================================
363 
364 /*
365 ==================
366 SV_Kick_f
367 
368 Kick a user off of the server  FIXME: move to game
369 ==================
370 */
SV_Kick_f(void)371 static void SV_Kick_f( void ) {
372 	client_t	*cl;
373 	int			i;
374 
375 	// make sure server is running
376 	if ( !com_sv_running->integer ) {
377 		Com_Printf( "Server is not running.\n" );
378 		return;
379 	}
380 
381 	if ( Cmd_Argc() != 2 ) {
382 		Com_Printf ("Usage: kick <player name>\nkick all = kick everyone\nkick allbots = kick all bots\n");
383 		return;
384 	}
385 
386 	cl = SV_GetPlayerByHandle();
387 	if ( !cl ) {
388 		if ( !Q_stricmp(Cmd_Argv(1), "all") ) {
389 			for ( i=0, cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++ ) {
390 				if ( !cl->state ) {
391 					continue;
392 				}
393 				if( cl->netchan.remoteAddress.type == NA_LOOPBACK ) {
394 					continue;
395 				}
396 				SV_DropClient( cl, "was kicked" );
397 				cl->lastPacketTime = svs.time;	// in case there is a funny zombie
398 			}
399 		}
400 		else if ( !Q_stricmp(Cmd_Argv(1), "allbots") ) {
401 			for ( i=0, cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++ ) {
402 				if ( !cl->state ) {
403 					continue;
404 				}
405 				if( cl->netchan.remoteAddress.type != NA_BOT ) {
406 					continue;
407 				}
408 				SV_DropClient( cl, "was kicked" );
409 				cl->lastPacketTime = svs.time;	// in case there is a funny zombie
410 			}
411 		}
412 		return;
413 	}
414 	if( cl->netchan.remoteAddress.type == NA_LOOPBACK ) {
415 		SV_SendServerCommand(NULL, "print \"%s\"", "Cannot kick host player\n");
416 		return;
417 	}
418 
419 	SV_DropClient( cl, "was kicked" );
420 	cl->lastPacketTime = svs.time;	// in case there is a funny zombie
421 }
422 
423 #ifndef STANDALONE
424 // these functions require the auth server which of course is not available anymore for stand-alone games.
425 
426 /*
427 ==================
428 SV_Ban_f
429 
430 Ban a user from being able to play on this server through the auth
431 server
432 ==================
433 */
SV_Ban_f(void)434 static void SV_Ban_f( void ) {
435 	client_t	*cl;
436 
437 	// make sure server is running
438 	if ( !com_sv_running->integer ) {
439 		Com_Printf( "Server is not running.\n" );
440 		return;
441 	}
442 
443 	if ( Cmd_Argc() != 2 ) {
444 		Com_Printf ("Usage: banUser <player name>\n");
445 		return;
446 	}
447 
448 	cl = SV_GetPlayerByHandle();
449 
450 	if (!cl) {
451 		return;
452 	}
453 
454 	if( cl->netchan.remoteAddress.type == NA_LOOPBACK ) {
455 		SV_SendServerCommand(NULL, "print \"%s\"", "Cannot kick host player\n");
456 		return;
457 	}
458 
459 	// look up the authorize server's IP
460 	if ( !svs.authorizeAddress.ip[0] && svs.authorizeAddress.type != NA_BAD ) {
461 		Com_Printf( "Resolving %s\n", AUTHORIZE_SERVER_NAME );
462 		if ( !NET_StringToAdr( AUTHORIZE_SERVER_NAME, &svs.authorizeAddress, NA_IP ) ) {
463 			Com_Printf( "Couldn't resolve address\n" );
464 			return;
465 		}
466 		svs.authorizeAddress.port = BigShort( PORT_AUTHORIZE );
467 		Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", AUTHORIZE_SERVER_NAME,
468 			svs.authorizeAddress.ip[0], svs.authorizeAddress.ip[1],
469 			svs.authorizeAddress.ip[2], svs.authorizeAddress.ip[3],
470 			BigShort( svs.authorizeAddress.port ) );
471 	}
472 
473 	// otherwise send their ip to the authorize server
474 	if ( svs.authorizeAddress.type != NA_BAD ) {
475 		NET_OutOfBandPrint( NS_SERVER, svs.authorizeAddress,
476 			"banUser %i.%i.%i.%i", cl->netchan.remoteAddress.ip[0], cl->netchan.remoteAddress.ip[1],
477 								   cl->netchan.remoteAddress.ip[2], cl->netchan.remoteAddress.ip[3] );
478 		Com_Printf("%s was banned from coming back\n", cl->name);
479 	}
480 }
481 
482 /*
483 ==================
484 SV_BanNum_f
485 
486 Ban a user from being able to play on this server through the auth
487 server
488 ==================
489 */
SV_BanNum_f(void)490 static void SV_BanNum_f( void ) {
491 	client_t	*cl;
492 
493 	// make sure server is running
494 	if ( !com_sv_running->integer ) {
495 		Com_Printf( "Server is not running.\n" );
496 		return;
497 	}
498 
499 	if ( Cmd_Argc() != 2 ) {
500 		Com_Printf ("Usage: banClient <client number>\n");
501 		return;
502 	}
503 
504 	cl = SV_GetPlayerByNum();
505 	if ( !cl ) {
506 		return;
507 	}
508 	if( cl->netchan.remoteAddress.type == NA_LOOPBACK ) {
509 		SV_SendServerCommand(NULL, "print \"%s\"", "Cannot kick host player\n");
510 		return;
511 	}
512 
513 	// look up the authorize server's IP
514 	if ( !svs.authorizeAddress.ip[0] && svs.authorizeAddress.type != NA_BAD ) {
515 		Com_Printf( "Resolving %s\n", AUTHORIZE_SERVER_NAME );
516 		if ( !NET_StringToAdr( AUTHORIZE_SERVER_NAME, &svs.authorizeAddress, NA_IP ) ) {
517 			Com_Printf( "Couldn't resolve address\n" );
518 			return;
519 		}
520 		svs.authorizeAddress.port = BigShort( PORT_AUTHORIZE );
521 		Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", AUTHORIZE_SERVER_NAME,
522 			svs.authorizeAddress.ip[0], svs.authorizeAddress.ip[1],
523 			svs.authorizeAddress.ip[2], svs.authorizeAddress.ip[3],
524 			BigShort( svs.authorizeAddress.port ) );
525 	}
526 
527 	// otherwise send their ip to the authorize server
528 	if ( svs.authorizeAddress.type != NA_BAD ) {
529 		NET_OutOfBandPrint( NS_SERVER, svs.authorizeAddress,
530 			"banUser %i.%i.%i.%i", cl->netchan.remoteAddress.ip[0], cl->netchan.remoteAddress.ip[1],
531 								   cl->netchan.remoteAddress.ip[2], cl->netchan.remoteAddress.ip[3] );
532 		Com_Printf("%s was banned from coming back\n", cl->name);
533 	}
534 }
535 #endif
536 
537 /*
538 ==================
539 SV_RehashBans_f
540 
541 Load saved bans from file.
542 ==================
543 */
SV_RehashBans_f(void)544 static void SV_RehashBans_f(void)
545 {
546 	int index, filelen;
547 	fileHandle_t readfrom;
548 	char *textbuf, *curpos, *maskpos, *newlinepos, *endpos;
549 	char filepath[MAX_QPATH];
550 
551 	serverBansCount = 0;
552 
553 	if(!sv_banFile->string || !*sv_banFile->string)
554 		return;
555 
556 	Com_sprintf(filepath, sizeof(filepath), "%s/%s", FS_GetCurrentGameDir(), sv_banFile->string);
557 
558 	if((filelen = FS_SV_FOpenFileRead(filepath, &readfrom)) >= 0)
559 	{
560 		if(filelen < 2)
561 		{
562 			// Don't bother if file is too short.
563 			FS_FCloseFile(readfrom);
564 			return;
565 		}
566 
567 		curpos = textbuf = Z_Malloc(filelen);
568 
569 		filelen = FS_Read(textbuf, filelen, readfrom);
570 		FS_FCloseFile(readfrom);
571 
572 		endpos = textbuf + filelen;
573 
574 		for(index = 0; index < SERVER_MAXBANS && curpos + 2 < endpos; index++)
575 		{
576 			// find the end of the address string
577 			for(maskpos = curpos + 2; maskpos < endpos && *maskpos != ' '; maskpos++);
578 
579 			if(maskpos + 1 >= endpos)
580 				break;
581 
582 			*maskpos = '\0';
583 			maskpos++;
584 
585 			// find the end of the subnet specifier
586 			for(newlinepos = maskpos; newlinepos < endpos && *newlinepos != '\n'; newlinepos++);
587 
588 			if(newlinepos >= endpos)
589 				break;
590 
591 			*newlinepos = '\0';
592 
593 			if(NET_StringToAdr(curpos + 2, &serverBans[index].ip, NA_UNSPEC))
594 			{
595 				serverBans[index].isexception = (curpos[0] != '0');
596 				serverBans[index].subnet = atoi(maskpos);
597 
598 				if(serverBans[index].ip.type == NA_IP &&
599 				   (serverBans[index].subnet < 1 || serverBans[index].subnet > 32))
600 				{
601 					serverBans[index].subnet = 32;
602 				}
603 				else if(serverBans[index].ip.type == NA_IP6 &&
604 					(serverBans[index].subnet < 1 || serverBans[index].subnet > 128))
605 				{
606 					serverBans[index].subnet = 128;
607 				}
608 			}
609 
610 			curpos = newlinepos + 1;
611 		}
612 
613 		serverBansCount = index;
614 
615 		Z_Free(textbuf);
616 	}
617 }
618 
619 /*
620 ==================
621 SV_WriteBans_f
622 
623 Save bans to file.
624 ==================
625 */
SV_WriteBans(void)626 static void SV_WriteBans(void)
627 {
628 	int index;
629 	fileHandle_t writeto;
630 	char filepath[MAX_QPATH];
631 
632 	if(!sv_banFile->string || !*sv_banFile->string)
633 		return;
634 
635 	Com_sprintf(filepath, sizeof(filepath), "%s/%s", FS_GetCurrentGameDir(), sv_banFile->string);
636 
637 	if((writeto = FS_SV_FOpenFileWrite(filepath)))
638 	{
639 		char writebuf[128];
640 		serverBan_t *curban;
641 
642 		for(index = 0; index < serverBansCount; index++)
643 		{
644 			curban = &serverBans[index];
645 
646 			Com_sprintf(writebuf, sizeof(writebuf), "%d %s %d\n",
647 				    curban->isexception, NET_AdrToString(curban->ip), curban->subnet);
648 			FS_Write(writebuf, strlen(writebuf), writeto);
649 		}
650 
651 		FS_FCloseFile(writeto);
652 	}
653 }
654 
655 /*
656 ==================
657 SV_DelBanEntryFromList
658 
659 Remove a ban or an exception from the list.
660 ==================
661 */
662 
SV_DelBanEntryFromList(int index)663 static qboolean SV_DelBanEntryFromList(int index)
664 {
665 	if(index == serverBansCount - 1)
666 		serverBansCount--;
667 	else if(index < ARRAY_LEN(serverBans) - 1)
668 	{
669 		memmove(serverBans + index, serverBans + index + 1, (serverBansCount - index - 1) * sizeof(*serverBans));
670 		serverBansCount--;
671 	}
672 	else
673 		return qtrue;
674 
675 	return qfalse;
676 }
677 
678 /*
679 ==================
680 SV_ParseCIDRNotation
681 
682 Parse a CIDR notation type string and return a netadr_t and suffix by reference
683 ==================
684 */
685 
SV_ParseCIDRNotation(netadr_t * dest,int * mask,char * adrstr)686 static qboolean SV_ParseCIDRNotation(netadr_t *dest, int *mask, char *adrstr)
687 {
688 	char *suffix;
689 
690 	suffix = strchr(adrstr, '/');
691 	if(suffix)
692 	{
693 		*suffix = '\0';
694 		suffix++;
695 	}
696 
697 	if(!NET_StringToAdr(adrstr, dest, NA_UNSPEC))
698 		return qtrue;
699 
700 	if(suffix)
701 	{
702 		*mask = atoi(suffix);
703 
704 		if(dest->type == NA_IP)
705 		{
706 			if(*mask < 1 || *mask > 32)
707 				*mask = 32;
708 		}
709 		else
710 		{
711 			if(*mask < 1 || *mask > 128)
712 				*mask = 128;
713 		}
714 	}
715 	else if(dest->type == NA_IP)
716 		*mask = 32;
717 	else
718 		*mask = 128;
719 
720 	return qfalse;
721 }
722 
723 /*
724 ==================
725 SV_AddBanToList
726 
727 Ban a user from being able to play on this server based on his ip address.
728 ==================
729 */
730 
SV_AddBanToList(qboolean isexception)731 static void SV_AddBanToList(qboolean isexception)
732 {
733 	char *banstring;
734 	char addy2[NET_ADDRSTRMAXLEN];
735 	netadr_t ip;
736 	int index, argc, mask;
737 	serverBan_t *curban;
738 
739 	argc = Cmd_Argc();
740 
741 	if(argc < 2 || argc > 3)
742 	{
743 		Com_Printf ("Usage: %s (ip[/subnet] | clientnum [subnet])\n", Cmd_Argv(0));
744 		return;
745 	}
746 
747 	if(serverBansCount > ARRAY_LEN(serverBans))
748 	{
749 		Com_Printf ("Error: Maximum number of bans/exceptions exceeded.\n");
750 		return;
751 	}
752 
753 	banstring = Cmd_Argv(1);
754 
755 	if(strchr(banstring, '.') || strchr(banstring, ':'))
756 	{
757 		// This is an ip address, not a client num.
758 
759 		if(SV_ParseCIDRNotation(&ip, &mask, banstring))
760 		{
761 			Com_Printf("Error: Invalid address %s\n", banstring);
762 			return;
763 		}
764 	}
765 	else
766 	{
767 		client_t *cl;
768 
769 		// client num.
770 		if(!com_sv_running->integer)
771 		{
772 			Com_Printf("Server is not running.\n");
773 			return;
774 		}
775 
776 		cl = SV_GetPlayerByNum();
777 
778 		if(!cl)
779 		{
780 			Com_Printf("Error: Playernum %s does not exist.\n", Cmd_Argv(1));
781 			return;
782 		}
783 
784 		ip = cl->netchan.remoteAddress;
785 
786 		if(argc == 3)
787 		{
788 			mask = atoi(Cmd_Argv(2));
789 
790 			if(ip.type == NA_IP)
791 			{
792 				if(mask < 1 || mask > 32)
793 					mask = 32;
794 			}
795 			else
796 			{
797 				if(mask < 1 || mask > 128)
798 					mask = 128;
799 			}
800 		}
801 		else
802 			mask = (ip.type == NA_IP6) ? 128 : 32;
803 	}
804 
805 	if(ip.type != NA_IP && ip.type != NA_IP6)
806 	{
807 		Com_Printf("Error: Can ban players connected via the internet only.\n");
808 		return;
809 	}
810 
811 	// first check whether a conflicting ban exists that would supersede the new one.
812 	for(index = 0; index < serverBansCount; index++)
813 	{
814 		curban = &serverBans[index];
815 
816 		if(curban->subnet <= mask)
817 		{
818 			if((curban->isexception || !isexception) && NET_CompareBaseAdrMask(curban->ip, ip, curban->subnet))
819 			{
820 				Q_strncpyz(addy2, NET_AdrToString(ip), sizeof(addy2));
821 
822 				Com_Printf("Error: %s %s/%d supersedes %s %s/%d\n", curban->isexception ? "Exception" : "Ban",
823 					   NET_AdrToString(curban->ip), curban->subnet,
824 					   isexception ? "exception" : "ban", addy2, mask);
825 				return;
826 			}
827 		}
828 		if(curban->subnet >= mask)
829 		{
830 			if(!curban->isexception && isexception && NET_CompareBaseAdrMask(curban->ip, ip, mask))
831 			{
832 				Q_strncpyz(addy2, NET_AdrToString(curban->ip), sizeof(addy2));
833 
834 				Com_Printf("Error: %s %s/%d supersedes already existing %s %s/%d\n", isexception ? "Exception" : "Ban",
835 					   NET_AdrToString(ip), mask,
836 					   curban->isexception ? "exception" : "ban", addy2, curban->subnet);
837 				return;
838 			}
839 		}
840 	}
841 
842 	// now delete bans that are superseded by the new one
843 	index = 0;
844 	while(index < serverBansCount)
845 	{
846 		curban = &serverBans[index];
847 
848 		if(curban->subnet > mask && (!curban->isexception || isexception) && NET_CompareBaseAdrMask(curban->ip, ip, mask))
849 			SV_DelBanEntryFromList(index);
850 		else
851 			index++;
852 	}
853 
854 	serverBans[serverBansCount].ip = ip;
855 	serverBans[serverBansCount].subnet = mask;
856 	serverBans[serverBansCount].isexception = isexception;
857 
858 	serverBansCount++;
859 
860 	SV_WriteBans();
861 
862 	Com_Printf("Added %s: %s/%d\n", isexception ? "ban exception" : "ban",
863 		   NET_AdrToString(ip), mask);
864 }
865 
866 /*
867 ==================
868 SV_DelBanFromList
869 
870 Remove a ban or an exception from the list.
871 ==================
872 */
873 
SV_DelBanFromList(qboolean isexception)874 static void SV_DelBanFromList(qboolean isexception)
875 {
876 	int index, count = 0, todel, mask;
877 	netadr_t ip;
878 	char *banstring;
879 
880 	if(Cmd_Argc() != 2)
881 	{
882 		Com_Printf ("Usage: %s (ip[/subnet] | num)\n", Cmd_Argv(0));
883 		return;
884 	}
885 
886 	banstring = Cmd_Argv(1);
887 
888 	if(strchr(banstring, '.') || strchr(banstring, ':'))
889 	{
890 		serverBan_t *curban;
891 
892 		if(SV_ParseCIDRNotation(&ip, &mask, banstring))
893 		{
894 			Com_Printf("Error: Invalid address %s\n", banstring);
895 			return;
896 		}
897 
898 		index = 0;
899 
900 		while(index < serverBansCount)
901 		{
902 			curban = &serverBans[index];
903 
904 			if(curban->isexception == isexception		&&
905 			   curban->subnet >= mask 			&&
906 			   NET_CompareBaseAdrMask(curban->ip, ip, mask))
907 			{
908 				Com_Printf("Deleting %s %s/%d\n",
909 					   isexception ? "exception" : "ban",
910 					   NET_AdrToString(curban->ip), curban->subnet);
911 
912 				SV_DelBanEntryFromList(index);
913 			}
914 			else
915 				index++;
916 		}
917 	}
918 	else
919 	{
920 		todel = atoi(Cmd_Argv(1));
921 
922 		if(todel < 1 || todel > serverBansCount)
923 		{
924 			Com_Printf("Error: Invalid ban number given\n");
925 			return;
926 		}
927 
928 		for(index = 0; index < serverBansCount; index++)
929 		{
930 			if(serverBans[index].isexception == isexception)
931 			{
932 				count++;
933 
934 				if(count == todel)
935 				{
936 					Com_Printf("Deleting %s %s/%d\n",
937 					   isexception ? "exception" : "ban",
938 					   NET_AdrToString(serverBans[index].ip), serverBans[index].subnet);
939 
940 					SV_DelBanEntryFromList(index);
941 
942 					break;
943 				}
944 			}
945 		}
946 	}
947 
948 	SV_WriteBans();
949 }
950 
951 
952 /*
953 ==================
954 SV_ListBans_f
955 
956 List all bans and exceptions on console
957 ==================
958 */
959 
SV_ListBans_f(void)960 static void SV_ListBans_f(void)
961 {
962 	int index, count;
963 	serverBan_t *ban;
964 
965 	// List all bans
966 	for(index = count = 0; index < serverBansCount; index++)
967 	{
968 		ban = &serverBans[index];
969 		if(!ban->isexception)
970 		{
971 			count++;
972 
973 			Com_Printf("Ban #%d: %s/%d\n", count,
974 				    NET_AdrToString(ban->ip), ban->subnet);
975 		}
976 	}
977 	// List all exceptions
978 	for(index = count = 0; index < serverBansCount; index++)
979 	{
980 		ban = &serverBans[index];
981 		if(ban->isexception)
982 		{
983 			count++;
984 
985 			Com_Printf("Except #%d: %s/%d\n", count,
986 				    NET_AdrToString(ban->ip), ban->subnet);
987 		}
988 	}
989 }
990 
991 /*
992 ==================
993 SV_FlushBans_f
994 
995 Delete all bans and exceptions.
996 ==================
997 */
998 
SV_FlushBans_f(void)999 static void SV_FlushBans_f(void)
1000 {
1001 	serverBansCount = 0;
1002 
1003 	// empty the ban file.
1004 	SV_WriteBans();
1005 
1006 	Com_Printf("All bans and exceptions have been deleted.\n");
1007 }
1008 
SV_BanAddr_f(void)1009 static void SV_BanAddr_f(void)
1010 {
1011 	SV_AddBanToList(qfalse);
1012 }
1013 
SV_ExceptAddr_f(void)1014 static void SV_ExceptAddr_f(void)
1015 {
1016 	SV_AddBanToList(qtrue);
1017 }
1018 
SV_BanDel_f(void)1019 static void SV_BanDel_f(void)
1020 {
1021 	SV_DelBanFromList(qfalse);
1022 }
1023 
SV_ExceptDel_f(void)1024 static void SV_ExceptDel_f(void)
1025 {
1026 	SV_DelBanFromList(qtrue);
1027 }
1028 
1029 /*
1030 ==================
1031 SV_KickNum_f
1032 
1033 Kick a user off of the server  FIXME: move to game
1034 ==================
1035 */
SV_KickNum_f(void)1036 static void SV_KickNum_f( void ) {
1037 	client_t	*cl;
1038 
1039 	// make sure server is running
1040 	if ( !com_sv_running->integer ) {
1041 		Com_Printf( "Server is not running.\n" );
1042 		return;
1043 	}
1044 
1045 	if ( Cmd_Argc() != 2 ) {
1046 		Com_Printf ("Usage: kicknum <client number>\n");
1047 		return;
1048 	}
1049 
1050 	cl = SV_GetPlayerByNum();
1051 	if ( !cl ) {
1052 		return;
1053 	}
1054 	if( cl->netchan.remoteAddress.type == NA_LOOPBACK ) {
1055 		SV_SendServerCommand(NULL, "print \"%s\"", "Cannot kick host player\n");
1056 		return;
1057 	}
1058 
1059 	SV_DropClient( cl, "was kicked" );
1060 	cl->lastPacketTime = svs.time;	// in case there is a funny zombie
1061 }
1062 
1063 /*
1064 ================
1065 SV_Status_f
1066 ================
1067 */
SV_Status_f(void)1068 static void SV_Status_f( void ) {
1069 	int			i, j, l;
1070 	client_t	*cl;
1071 	playerState_t	*ps;
1072 	const char		*s;
1073 	int			ping;
1074 
1075 	// make sure server is running
1076 	if ( !com_sv_running->integer ) {
1077 		Com_Printf( "Server is not running.\n" );
1078 		return;
1079 	}
1080 
1081 	Com_Printf ("map: %s\n", sv_mapname->string );
1082 
1083 	Com_Printf ("num score ping name            lastmsg address               qport rate\n");
1084 	Com_Printf ("--- ----- ---- --------------- ------- --------------------- ----- -----\n");
1085 	for (i=0,cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++)
1086 	{
1087 		if (!cl->state)
1088 			continue;
1089 		Com_Printf ("%3i ", i);
1090 		ps = SV_GameClientNum( i );
1091 		Com_Printf ("%5i ", ps->persistant[PERS_SCORE]);
1092 
1093 		if (cl->state == CS_CONNECTED)
1094 			Com_Printf ("CNCT ");
1095 		else if (cl->state == CS_ZOMBIE)
1096 			Com_Printf ("ZMBI ");
1097 		else
1098 		{
1099 			ping = cl->ping < 9999 ? cl->ping : 9999;
1100 			Com_Printf ("%4i ", ping);
1101 		}
1102 
1103 		Com_Printf ("%s", cl->name);
1104 
1105 		// TTimo adding a ^7 to reset the color
1106 		// NOTE: colored names in status breaks the padding (WONTFIX)
1107 		Com_Printf ("^7");
1108 		l = 14 - strlen(cl->name);
1109 		j = 0;
1110 
1111 		do
1112 		{
1113 			Com_Printf (" ");
1114 			j++;
1115 		} while(j < l);
1116 
1117 		Com_Printf ("%7i ", svs.time - cl->lastPacketTime );
1118 
1119 		s = NET_AdrToString( cl->netchan.remoteAddress );
1120 		Com_Printf ("%s", s);
1121 		l = 22 - strlen(s);
1122 		j = 0;
1123 
1124 		do
1125 		{
1126 			Com_Printf(" ");
1127 			j++;
1128 		} while(j < l);
1129 
1130 		Com_Printf ("%5i", cl->netchan.qport);
1131 
1132 		Com_Printf (" %5i", cl->rate);
1133 
1134 		Com_Printf ("\n");
1135 	}
1136 	Com_Printf ("\n");
1137 }
1138 
1139 /*
1140 ==================
1141 SV_ConSay_f
1142 ==================
1143 */
SV_ConSay_f(void)1144 static void SV_ConSay_f(void) {
1145 	char	*p;
1146 	char	text[1024];
1147 
1148 	// make sure server is running
1149 	if ( !com_sv_running->integer ) {
1150 		Com_Printf( "Server is not running.\n" );
1151 		return;
1152 	}
1153 
1154 	if ( Cmd_Argc () < 2 ) {
1155 		return;
1156 	}
1157 
1158 	strcpy (text, "console: ");
1159 	p = Cmd_Args();
1160 
1161 	if ( *p == '"' ) {
1162 		p++;
1163 		p[strlen(p)-1] = 0;
1164 	}
1165 
1166 	strcat(text, p);
1167 
1168 	SV_SendServerCommand(NULL, "chat \"%s\"", text);
1169 }
1170 
1171 
1172 /*
1173 ==================
1174 SV_Heartbeat_f
1175 
1176 Also called by SV_DropClient, SV_DirectConnect, and SV_SpawnServer
1177 ==================
1178 */
SV_Heartbeat_f(void)1179 void SV_Heartbeat_f( void ) {
1180 	svs.nextHeartbeatTime = -9999999;
1181 }
1182 
1183 
1184 /*
1185 ===========
1186 SV_Serverinfo_f
1187 
1188 Examine the serverinfo string
1189 ===========
1190 */
SV_Serverinfo_f(void)1191 static void SV_Serverinfo_f( void ) {
1192 	Com_Printf ("Server info settings:\n");
1193 	Info_Print ( Cvar_InfoString( CVAR_SERVERINFO ) );
1194 }
1195 
1196 
1197 /*
1198 ===========
1199 SV_Systeminfo_f
1200 
1201 Examine or change the serverinfo string
1202 ===========
1203 */
SV_Systeminfo_f(void)1204 static void SV_Systeminfo_f( void ) {
1205 	Com_Printf ("System info settings:\n");
1206 	Info_Print ( Cvar_InfoString_Big( CVAR_SYSTEMINFO ) );
1207 }
1208 
1209 
1210 /*
1211 ===========
1212 SV_DumpUser_f
1213 
1214 Examine all a users info strings FIXME: move to game
1215 ===========
1216 */
SV_DumpUser_f(void)1217 static void SV_DumpUser_f( void ) {
1218 	client_t	*cl;
1219 
1220 	// make sure server is running
1221 	if ( !com_sv_running->integer ) {
1222 		Com_Printf( "Server is not running.\n" );
1223 		return;
1224 	}
1225 
1226 	if ( Cmd_Argc() != 2 ) {
1227 		Com_Printf ("Usage: info <userid>\n");
1228 		return;
1229 	}
1230 
1231 	cl = SV_GetPlayerByHandle();
1232 	if ( !cl ) {
1233 		return;
1234 	}
1235 
1236 	Com_Printf( "userinfo\n" );
1237 	Com_Printf( "--------\n" );
1238 	Info_Print( cl->userinfo );
1239 }
1240 
1241 
1242 /*
1243 =================
1244 SV_KillServer
1245 =================
1246 */
SV_KillServer_f(void)1247 static void SV_KillServer_f( void ) {
1248 	SV_Shutdown( "killserver" );
1249 }
1250 
1251 //===========================================================
1252 
1253 /*
1254 ==================
1255 SV_CompleteMapName
1256 ==================
1257 */
SV_CompleteMapName(char * args,int argNum)1258 static void SV_CompleteMapName( char *args, int argNum ) {
1259 	if( argNum == 2 ) {
1260 		Field_CompleteFilename( "maps", "bsp", qtrue, qfalse );
1261 	}
1262 }
1263 
1264 /*
1265 ==================
1266 SV_AddOperatorCommands
1267 ==================
1268 */
SV_AddOperatorCommands(void)1269 void SV_AddOperatorCommands( void ) {
1270 	static qboolean	initialized;
1271 
1272 	if ( initialized ) {
1273 		return;
1274 	}
1275 	initialized = qtrue;
1276 
1277 	Cmd_AddCommand ("heartbeat", SV_Heartbeat_f);
1278 	Cmd_AddCommand ("kick", SV_Kick_f);
1279 #ifndef STANDALONE
1280 	if(!com_standalone->integer)
1281 	{
1282 		Cmd_AddCommand ("banUser", SV_Ban_f);
1283 		Cmd_AddCommand ("banClient", SV_BanNum_f);
1284 	}
1285 #endif
1286 	Cmd_AddCommand ("clientkick", SV_KickNum_f);
1287 	Cmd_AddCommand ("status", SV_Status_f);
1288 	Cmd_AddCommand ("serverinfo", SV_Serverinfo_f);
1289 	Cmd_AddCommand ("systeminfo", SV_Systeminfo_f);
1290 	Cmd_AddCommand ("dumpuser", SV_DumpUser_f);
1291 	Cmd_AddCommand ("map_restart", SV_MapRestart_f);
1292 	Cmd_AddCommand ("sectorlist", SV_SectorList_f);
1293 	Cmd_AddCommand ("map", SV_Map_f);
1294 	Cmd_SetCommandCompletionFunc( "map", SV_CompleteMapName );
1295 #ifndef PRE_RELEASE_DEMO
1296 	Cmd_AddCommand ("devmap", SV_Map_f);
1297 	Cmd_SetCommandCompletionFunc( "devmap", SV_CompleteMapName );
1298 	Cmd_AddCommand ("spmap", SV_Map_f);
1299 	Cmd_SetCommandCompletionFunc( "spmap", SV_CompleteMapName );
1300 	Cmd_AddCommand ("spdevmap", SV_Map_f);
1301 	Cmd_SetCommandCompletionFunc( "spdevmap", SV_CompleteMapName );
1302 #endif
1303 	Cmd_AddCommand ("killserver", SV_KillServer_f);
1304 	if( com_dedicated->integer ) {
1305 		Cmd_AddCommand ("say", SV_ConSay_f);
1306 	}
1307 
1308 	Cmd_AddCommand("rehashbans", SV_RehashBans_f);
1309 	Cmd_AddCommand("listbans", SV_ListBans_f);
1310 	Cmd_AddCommand("banaddr", SV_BanAddr_f);
1311 	Cmd_AddCommand("exceptaddr", SV_ExceptAddr_f);
1312 	Cmd_AddCommand("bandel", SV_BanDel_f);
1313 	Cmd_AddCommand("exceptdel", SV_ExceptDel_f);
1314 	Cmd_AddCommand("flushbans", SV_FlushBans_f);
1315 }
1316 
1317 /*
1318 ==================
1319 SV_RemoveOperatorCommands
1320 ==================
1321 */
SV_RemoveOperatorCommands(void)1322 void SV_RemoveOperatorCommands( void ) {
1323 #if 0
1324 	// removing these won't let the server start again
1325 	Cmd_RemoveCommand ("heartbeat");
1326 	Cmd_RemoveCommand ("kick");
1327 	Cmd_RemoveCommand ("banUser");
1328 	Cmd_RemoveCommand ("banClient");
1329 	Cmd_RemoveCommand ("status");
1330 	Cmd_RemoveCommand ("serverinfo");
1331 	Cmd_RemoveCommand ("systeminfo");
1332 	Cmd_RemoveCommand ("dumpuser");
1333 	Cmd_RemoveCommand ("map_restart");
1334 	Cmd_RemoveCommand ("sectorlist");
1335 	Cmd_RemoveCommand ("say");
1336 #endif
1337 }
1338 
1339