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 
24 // this file holds commands that can be executed by the server console, but not remote clients
25 
26 #include "g_local.h"
27 
28 
29 /*
30 ==============================================================================
31 
32 PACKET FILTERING
33 
34 
35 You can add or remove addresses from the filter list with:
36 
37 addip <ip>
38 removeip <ip>
39 
40 The ip address is specified in dot format, and you can use '*' to match any value
41 so you can specify an entire class C network with "addip 192.246.40.*"
42 
43 Removeip will only remove an address specified exactly the same way.  You cannot addip a subnet, then removeip a single host.
44 
45 listip
46 Prints the current list of filters.
47 
48 g_filterban <0 or 1>
49 
50 If 1 (the default), then ip addresses matching the current list will be prohibited from entering the game.  This is the default setting.
51 
52 If 0, then only addresses matching the list will be allowed.  This lets you easily set up a private game, or a game that only allows players from your local network.
53 
54 TTimo NOTE: for persistence, bans are stored in g_banIPs cvar MAX_CVAR_VALUE_STRING
55 The size of the cvar string buffer is limiting the banning to around 20 masks
56 this could be improved by putting some g_banIPs2 g_banIps3 etc. maybe
57 still, you should rely on PB for banning instead
58 
59 ==============================================================================
60 */
61 
62 typedef struct ipFilter_s
63 {
64 	unsigned	mask;
65 	unsigned	compare;
66 } ipFilter_t;
67 
68 #define	MAX_IPFILTERS	1024
69 
70 static ipFilter_t	ipFilters[MAX_IPFILTERS];
71 static int			numIPFilters;
72 
73 /*
74 =================
75 StringToFilter
76 =================
77 */
StringToFilter(char * s,ipFilter_t * f)78 static qboolean StringToFilter (char *s, ipFilter_t *f)
79 {
80 	char	num[128];
81 	int		i, j;
82 	byte	b[4];
83 	byte	m[4];
84 
85 	for (i=0 ; i<4 ; i++)
86 	{
87 		b[i] = 0;
88 		m[i] = 0;
89 	}
90 
91 	for (i=0 ; i<4 ; i++)
92 	{
93 		if (*s < '0' || *s > '9')
94 		{
95 			if (*s == '*') // 'match any'
96 			{
97 				// b[i] and m[i] to 0
98 				s++;
99 				if (!*s)
100 					break;
101 				s++;
102 				continue;
103 			}
104 			G_Printf( "Bad filter address: %s\n", s );
105 			return qfalse;
106 		}
107 
108 		j = 0;
109 		while (*s >= '0' && *s <= '9')
110 		{
111 			num[j++] = *s++;
112 		}
113 		num[j] = 0;
114 		b[i] = atoi(num);
115 		m[i] = 255;
116 
117 		if (!*s)
118 			break;
119 		s++;
120 	}
121 
122 	f->mask = *(unsigned *)m;
123 	f->compare = *(unsigned *)b;
124 
125 	return qtrue;
126 }
127 
128 /*
129 =================
130 UpdateIPBans
131 =================
132 */
UpdateIPBans(void)133 static void UpdateIPBans (void)
134 {
135 	byte	b[4];
136 	byte	m[4];
137 	int		i,j;
138 	char	iplist_final[MAX_CVAR_VALUE_STRING];
139 	char	ip[64];
140 
141 	*iplist_final = 0;
142 	for (i = 0 ; i < numIPFilters ; i++)
143 	{
144 		if (ipFilters[i].compare == 0xffffffff)
145 			continue;
146 
147 		*(unsigned *)b = ipFilters[i].compare;
148 		*(unsigned *)m = ipFilters[i].mask;
149 		*ip = 0;
150 		for (j = 0 ; j < 4 ; j++)
151 		{
152 			if (m[j]!=255)
153 				Q_strcat(ip, sizeof(ip), "*");
154 			else
155 				Q_strcat(ip, sizeof(ip), va("%i", b[j]));
156 			Q_strcat(ip, sizeof(ip), (j<3) ? "." : " ");
157 		}
158 		if (strlen(iplist_final)+strlen(ip) < MAX_CVAR_VALUE_STRING)
159 		{
160 			Q_strcat( iplist_final, sizeof(iplist_final), ip);
161 		}
162 		else
163 		{
164 			Com_Printf("g_banIPs overflowed at MAX_CVAR_VALUE_STRING\n");
165 			break;
166 		}
167 	}
168 
169 	trap_Cvar_Set( "g_banIPs", iplist_final );
170 }
171 
172 /*
173 =================
174 G_FilterPacket
175 =================
176 */
G_FilterPacket(char * from)177 qboolean G_FilterPacket (char *from)
178 {
179 	int		i;
180 	unsigned	in;
181 	byte m[4];
182 	char *p;
183 
184 	i = 0;
185 	p = from;
186 	while (*p && i < 4) {
187 		m[i] = 0;
188 		while (*p >= '0' && *p <= '9') {
189 			m[i] = m[i]*10 + (*p - '0');
190 			p++;
191 		}
192 		if (!*p || *p == ':')
193 			break;
194 		i++, p++;
195 	}
196 
197 	in = *(unsigned *)m;
198 
199 	for (i=0 ; i<numIPFilters ; i++)
200 		if ( (in & ipFilters[i].mask) == ipFilters[i].compare)
201 			return g_filterBan.integer != 0;
202 
203 	return g_filterBan.integer == 0;
204 }
205 
206 /*
207 =================
208 AddIP
209 =================
210 */
AddIP(char * str)211 static void AddIP( char *str )
212 {
213 	int		i;
214 
215 	for (i = 0 ; i < numIPFilters ; i++)
216 		if (ipFilters[i].compare == 0xffffffff)
217 			break;		// free spot
218 	if (i == numIPFilters)
219 	{
220 		if (numIPFilters == MAX_IPFILTERS)
221 		{
222 			G_Printf ("IP filter list is full\n");
223 			return;
224 		}
225 		numIPFilters++;
226 	}
227 
228 	if (!StringToFilter (str, &ipFilters[i]))
229 		ipFilters[i].compare = 0xffffffffu;
230 
231 	UpdateIPBans();
232 }
233 
234 /*
235 =================
236 G_ProcessIPBans
237 =================
238 */
G_ProcessIPBans(void)239 void G_ProcessIPBans(void)
240 {
241 	char *s, *t;
242 	char		str[MAX_CVAR_VALUE_STRING];
243 
244 	Q_strncpyz( str, g_banIPs.string, sizeof(str) );
245 
246 	for (t = s = g_banIPs.string; *t; /* */ ) {
247 		s = strchr(s, ' ');
248 		if (!s)
249 			break;
250 		while (*s == ' ')
251 			*s++ = 0;
252 		if (*t)
253 			AddIP( t );
254 		t = s;
255 	}
256 }
257 
258 
259 /*
260 =================
261 Svcmd_AddIP_f
262 =================
263 */
Svcmd_AddIP_f(void)264 void Svcmd_AddIP_f (void)
265 {
266 	char		str[MAX_TOKEN_CHARS];
267 
268 	if ( trap_Argc() < 2 ) {
269 		G_Printf("Usage:  addip <ip-mask>\n");
270 		return;
271 	}
272 
273 	trap_Argv( 1, str, sizeof( str ) );
274 
275 	AddIP( str );
276 
277 }
278 
279 /*
280 =================
281 Svcmd_RemoveIP_f
282 =================
283 */
Svcmd_RemoveIP_f(void)284 void Svcmd_RemoveIP_f (void)
285 {
286 	ipFilter_t	f;
287 	int			i;
288 	char		str[MAX_TOKEN_CHARS];
289 
290 	if ( trap_Argc() < 2 ) {
291 		G_Printf("Usage:  sv removeip <ip-mask>\n");
292 		return;
293 	}
294 
295 	trap_Argv( 1, str, sizeof( str ) );
296 
297 	if (!StringToFilter (str, &f))
298 		return;
299 
300 	for (i=0 ; i<numIPFilters ; i++) {
301 		if (ipFilters[i].mask == f.mask	&&
302 			ipFilters[i].compare == f.compare) {
303 			ipFilters[i].compare = 0xffffffffu;
304 			G_Printf ("Removed.\n");
305 
306 			UpdateIPBans();
307 			return;
308 		}
309 	}
310 
311 	G_Printf ( "Didn't find %s.\n", str );
312 }
313 
314 /*
315 ===================
316 Svcmd_EntityList_f
317 ===================
318 */
Svcmd_EntityList_f(void)319 void	Svcmd_EntityList_f (void) {
320 	int			e;
321 	gentity_t		*check;
322 
323 	check = g_entities+1;
324 	for (e = 1; e < level.num_entities ; e++, check++) {
325 		if ( !check->inuse ) {
326 			continue;
327 		}
328 		G_Printf("%3i:", e);
329 		switch ( check->s.eType ) {
330 		case ET_GENERAL:
331 			G_Printf("ET_GENERAL          ");
332 			break;
333 		case ET_PLAYER:
334 			G_Printf("ET_PLAYER           ");
335 			break;
336 		case ET_ITEM:
337 			G_Printf("ET_ITEM             ");
338 			break;
339 		case ET_MISSILE:
340 			G_Printf("ET_MISSILE          ");
341 			break;
342 		case ET_MOVER:
343 			G_Printf("ET_MOVER            ");
344 			break;
345 		case ET_BEAM:
346 			G_Printf("ET_BEAM             ");
347 			break;
348 		case ET_PORTAL:
349 			G_Printf("ET_PORTAL           ");
350 			break;
351 		case ET_SPEAKER:
352 			G_Printf("ET_SPEAKER          ");
353 			break;
354 		case ET_PUSH_TRIGGER:
355 			G_Printf("ET_PUSH_TRIGGER     ");
356 			break;
357 		case ET_TELEPORT_TRIGGER:
358 			G_Printf("ET_TELEPORT_TRIGGER ");
359 			break;
360 		case ET_INVISIBLE:
361 			G_Printf("ET_INVISIBLE        ");
362 			break;
363 		case ET_GRAPPLE:
364 			G_Printf("ET_GRAPPLE          ");
365 			break;
366 		default:
367 			G_Printf("%3i                 ", check->s.eType);
368 			break;
369 		}
370 
371 		if ( check->classname ) {
372 			G_Printf("%s", check->classname);
373 		}
374 		G_Printf("\n");
375 	}
376 }
377 
ClientForString(const char * s)378 gclient_t	*ClientForString( const char *s ) {
379 	gclient_t	*cl;
380 	int			i;
381 	int			idnum;
382 
383 	// numeric values are just slot numbers
384 	if ( s[0] >= '0' && s[0] <= '9' ) {
385 		idnum = atoi( s );
386 		if ( idnum < 0 || idnum >= level.maxclients ) {
387 			Com_Printf( "Bad client slot: %i\n", idnum );
388 			return NULL;
389 		}
390 
391 		cl = &level.clients[idnum];
392 		if ( cl->pers.connected == CON_DISCONNECTED ) {
393 			G_Printf( "Client %i is not connected\n", idnum );
394 			return NULL;
395 		}
396 		return cl;
397 	}
398 
399 	// check for a name match
400 	for ( i=0 ; i < level.maxclients ; i++ ) {
401 		cl = &level.clients[i];
402 		if ( cl->pers.connected == CON_DISCONNECTED ) {
403 			continue;
404 		}
405 		if ( !Q_stricmp( cl->pers.netname, s ) ) {
406 			return cl;
407 		}
408 	}
409 
410 	G_Printf( "User %s is not on the server\n", s );
411 
412 	return NULL;
413 }
414 
415 /*
416 ===================
417 Svcmd_ForceTeam_f
418 
419 forceteam <player> <team>
420 ===================
421 */
Svcmd_ForceTeam_f(void)422 void	Svcmd_ForceTeam_f( void ) {
423 	gclient_t	*cl;
424 	char		str[MAX_TOKEN_CHARS];
425 
426 	// find the player
427 	trap_Argv( 1, str, sizeof( str ) );
428 	cl = ClientForString( str );
429 	if ( !cl ) {
430 		return;
431 	}
432 
433 	// set the team
434 	trap_Argv( 2, str, sizeof( str ) );
435 	SetTeam( &g_entities[cl - level.clients], str );
436 }
437 
438 char	*ConcatArgs( int start );
439 
440 /*
441 =================
442 ConsoleCommand
443 
444 =================
445 */
ConsoleCommand(void)446 qboolean	ConsoleCommand( void ) {
447 	char	cmd[MAX_TOKEN_CHARS];
448 
449 	trap_Argv( 0, cmd, sizeof( cmd ) );
450 
451 	if ( Q_stricmp (cmd, "entitylist") == 0 ) {
452 		Svcmd_EntityList_f();
453 		return qtrue;
454 	}
455 
456 	if ( Q_stricmp (cmd, "forceteam") == 0 ) {
457 		Svcmd_ForceTeam_f();
458 		return qtrue;
459 	}
460 
461 	if (Q_stricmp (cmd, "game_memory") == 0) {
462 		Svcmd_GameMem_f();
463 		return qtrue;
464 	}
465 
466 	if (Q_stricmp (cmd, "addbot") == 0) {
467 		Svcmd_AddBot_f();
468 		return qtrue;
469 	}
470 
471 	if (Q_stricmp (cmd, "botlist") == 0) {
472 		Svcmd_BotList_f();
473 		return qtrue;
474 	}
475 
476 	if (Q_stricmp (cmd, "abort_podium") == 0) {
477 		Svcmd_AbortPodium_f();
478 		return qtrue;
479 	}
480 
481 	if (Q_stricmp (cmd, "addip") == 0) {
482 		Svcmd_AddIP_f();
483 		return qtrue;
484 	}
485 
486 	if (Q_stricmp (cmd, "removeip") == 0) {
487 		Svcmd_RemoveIP_f();
488 		return qtrue;
489 	}
490 
491 	if (Q_stricmp (cmd, "listip") == 0) {
492 		trap_SendConsoleCommand( EXEC_NOW, "g_banIPs\n" );
493 		return qtrue;
494 	}
495 
496 	if (g_dedicated.integer) {
497 		if (Q_stricmp (cmd, "say") == 0) {
498 			trap_SendServerCommand( -1, va("print \"server: %s\"", ConcatArgs(1) ) );
499 			return qtrue;
500 		}
501 		// everything else will also be printed as a say command
502 		trap_SendServerCommand( -1, va("print \"server: %s\"", ConcatArgs(0) ) );
503 		return qtrue;
504 	}
505 
506 	return qfalse;
507 }
508 
509