1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4 Copyright (C) 2000-2006 Tim Angus
5
6 This file is part of Tremulous.
7
8 Tremulous is free software; you can redistribute it
9 and/or modify it under the terms of the GNU General Public License as
10 published by the Free Software Foundation; either version 2 of the License,
11 or (at your option) any later version.
12
13 Tremulous is distributed in the hope that it will be
14 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with Tremulous; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
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 // extern vmCvar_t g_banIPs;
63 // extern vmCvar_t g_filterBan;
64
65
66 typedef struct ipFilter_s
67 {
68 unsigned mask;
69 unsigned compare;
70 } ipFilter_t;
71
72 #define MAX_IPFILTERS 1024
73
74 static ipFilter_t ipFilters[ MAX_IPFILTERS ];
75 static int numIPFilters;
76
77 /*
78 =================
79 StringToFilter
80 =================
81 */
StringToFilter(char * s,ipFilter_t * f)82 static qboolean StringToFilter( char *s, ipFilter_t *f )
83 {
84 char num[ 128 ];
85 int i, j;
86 byte b[ 4 ];
87 byte m[ 4 ];
88
89 for( i = 0; i < 4; i++ )
90 {
91 b[ i ] = 0;
92 m[ i ] = 0;
93 }
94
95 for( i = 0; i < 4; i++ )
96 {
97 if( *s < '0' || *s > '9' )
98 {
99 if( *s == '*' ) // 'match any'
100 {
101 //b[ i ] and m[ i ] to 0
102 s++;
103 if ( !*s )
104 break;
105
106 s++;
107 continue;
108 }
109
110 G_Printf( "Bad filter address: %s\n", s );
111 return qfalse;
112 }
113
114 j = 0;
115 while( *s >= '0' && *s <= '9' )
116 num[ j++ ] = *s++;
117
118 num[ j ] = 0;
119 b[ i ] = atoi( num );
120
121 m[ i ] = 255;
122
123 if( !*s )
124 break;
125
126 s++;
127 }
128
129 f->mask = *(unsigned *)m;
130 f->compare = *(unsigned *)b;
131
132 return qtrue;
133 }
134
135 /*
136 =================
137 UpdateIPBans
138 =================
139 */
UpdateIPBans(void)140 static void UpdateIPBans( void )
141 {
142 byte b[ 4 ];
143 byte m[ 4 ];
144 int i, j;
145 char iplist_final[ MAX_CVAR_VALUE_STRING ];
146 char ip[ 64 ];
147
148 *iplist_final = 0;
149
150 for( i = 0 ; i < numIPFilters ; i++ )
151 {
152 if( ipFilters[ i ].compare == 0xffffffff )
153 continue;
154
155 *(unsigned *)b = ipFilters[ i ].compare;
156 *(unsigned *)m = ipFilters[ i ].mask;
157 *ip = 0;
158
159 for( j = 0 ; j < 4 ; j++ )
160 {
161 if( m[ j ] != 255 )
162 Q_strcat( ip, sizeof( ip ), "*" );
163 else
164 Q_strcat( ip, sizeof( ip ), va( "%i", b[ j ] ) );
165
166 Q_strcat( ip, sizeof( ip ), ( j < 3 ) ? "." : " " );
167 }
168
169 if( strlen( iplist_final ) + strlen( ip ) < MAX_CVAR_VALUE_STRING )
170 Q_strcat( iplist_final, sizeof( iplist_final ), ip );
171 else
172 {
173 Com_Printf( "g_banIPs overflowed at MAX_CVAR_VALUE_STRING\n" );
174 break;
175 }
176 }
177
178 trap_Cvar_Set( "g_banIPs", iplist_final );
179 }
180
181 /*
182 =================
183 G_FilterPacket
184 =================
185 */
G_FilterPacket(char * from)186 qboolean G_FilterPacket( char *from )
187 {
188 int i;
189 unsigned in;
190 byte m[ 4 ];
191 char *p;
192
193 i = 0;
194 p = from;
195 while( *p && i < 4 )
196 {
197 m[ i ] = 0;
198 while( *p >= '0' && *p <= '9' )
199 {
200 m[ i ] = m[ i ] * 10 + ( *p - '0' );
201 p++;
202 }
203
204 if( !*p || *p == ':' )
205 break;
206
207 i++, p++;
208 }
209
210 in = *(unsigned *)m;
211
212 for( i = 0; i < numIPFilters; i++ )
213 if( ( in & ipFilters[ i ].mask ) == ipFilters[ i ].compare )
214 return g_filterBan.integer != 0;
215
216 return g_filterBan.integer == 0;
217 }
218
219 /*
220 =================
221 AddIP
222 =================
223 */
AddIP(char * str)224 static void AddIP( char *str )
225 {
226 int i;
227
228 for( i = 0 ; i < numIPFilters ; i++ )
229 if( ipFilters[ i ].compare == 0xffffffff )
230 break; // free spot
231
232 if( i == numIPFilters )
233 {
234 if( numIPFilters == MAX_IPFILTERS )
235 {
236 G_Printf( "IP filter list is full\n" );
237 return;
238 }
239
240 numIPFilters++;
241 }
242
243 if( !StringToFilter( str, &ipFilters[ i ] ) )
244 ipFilters[ i ].compare = 0xffffffffu;
245
246 UpdateIPBans( );
247 }
248
249 /*
250 =================
251 G_ProcessIPBans
252 =================
253 */
G_ProcessIPBans(void)254 void G_ProcessIPBans( void )
255 {
256 char *s, *t;
257 char str[ MAX_CVAR_VALUE_STRING ];
258
259 Q_strncpyz( str, g_banIPs.string, sizeof( str ) );
260
261 for( t = s = g_banIPs.string; *t; /* */ )
262 {
263 s = strchr( s, ' ' );
264
265 if( !s )
266 break;
267
268 while( *s == ' ' )
269 *s++ = 0;
270
271 if( *t )
272 AddIP( t );
273
274 t = s;
275 }
276 }
277
278
279 /*
280 =================
281 Svcmd_AddIP_f
282 =================
283 */
Svcmd_AddIP_f(void)284 void Svcmd_AddIP_f( void )
285 {
286 char str[ MAX_TOKEN_CHARS ];
287
288 if( trap_Argc( ) < 2 )
289 {
290 G_Printf( "Usage: addip <ip-mask>\n" );
291 return;
292 }
293
294 trap_Argv( 1, str, sizeof( str ) );
295
296 AddIP( str );
297 }
298
299 /*
300 =================
301 Svcmd_RemoveIP_f
302 =================
303 */
Svcmd_RemoveIP_f(void)304 void Svcmd_RemoveIP_f( void )
305 {
306 ipFilter_t f;
307 int i;
308 char str[ MAX_TOKEN_CHARS ];
309
310 if( trap_Argc( ) < 2 )
311 {
312 G_Printf( "Usage: sv removeip <ip-mask>\n" );
313 return;
314 }
315
316 trap_Argv( 1, str, sizeof( str ) );
317
318 if( !StringToFilter( str, &f ) )
319 return;
320
321 for( i = 0; i < numIPFilters; i++ )
322 {
323 if( ipFilters[ i ].mask == f.mask &&
324 ipFilters[ i ].compare == f.compare)
325 {
326 ipFilters[ i ].compare = 0xffffffffu;
327 G_Printf ( "Removed.\n" );
328
329 UpdateIPBans( );
330 return;
331 }
332 }
333
334 G_Printf ( "Didn't find %s.\n", str );
335 }
336
337 /*
338 ===================
339 Svcmd_EntityList_f
340 ===================
341 */
Svcmd_EntityList_f(void)342 void Svcmd_EntityList_f( void )
343 {
344 int e;
345 gentity_t *check;
346
347 check = g_entities + 1;
348
349 for( e = 1; e < level.num_entities; e++, check++ )
350 {
351 if( !check->inuse )
352 continue;
353
354 G_Printf( "%3i:", e );
355
356 switch( check->s.eType )
357 {
358 case ET_GENERAL:
359 G_Printf( "ET_GENERAL " );
360 break;
361 case ET_PLAYER:
362 G_Printf( "ET_PLAYER " );
363 break;
364 case ET_ITEM:
365 G_Printf( "ET_ITEM " );
366 break;
367 case ET_BUILDABLE:
368 G_Printf( "ET_BUILDABLE " );
369 break;
370 case ET_MISSILE:
371 G_Printf( "ET_MISSILE " );
372 break;
373 case ET_MOVER:
374 G_Printf( "ET_MOVER " );
375 break;
376 case ET_BEAM:
377 G_Printf( "ET_BEAM " );
378 break;
379 case ET_PORTAL:
380 G_Printf( "ET_PORTAL " );
381 break;
382 case ET_SPEAKER:
383 G_Printf( "ET_SPEAKER " );
384 break;
385 case ET_PUSH_TRIGGER:
386 G_Printf( "ET_PUSH_TRIGGER " );
387 break;
388 case ET_TELEPORT_TRIGGER:
389 G_Printf( "ET_TELEPORT_TRIGGER " );
390 break;
391 case ET_INVISIBLE:
392 G_Printf( "ET_INVISIBLE " );
393 break;
394 case ET_GRAPPLE:
395 G_Printf( "ET_GRAPPLE " );
396 break;
397 default:
398 G_Printf( "%3i ", check->s.eType );
399 break;
400 }
401
402 if( check->classname )
403 G_Printf( "%s", check->classname );
404
405 G_Printf( "\n" );
406 }
407 }
408
ClientForString(const char * s)409 gclient_t *ClientForString( const char *s )
410 {
411 gclient_t *cl;
412 int i;
413 int idnum;
414
415 // numeric values are just slot numbers
416 if( s[ 0 ] >= '0' && s[ 0 ] <= '9' )
417 {
418 idnum = atoi( s );
419
420 if( idnum < 0 || idnum >= level.maxclients )
421 {
422 Com_Printf( "Bad client slot: %i\n", idnum );
423 return NULL;
424 }
425
426 cl = &level.clients[ idnum ];
427
428 if( cl->pers.connected == CON_DISCONNECTED )
429 {
430 G_Printf( "Client %i is not connected\n", idnum );
431 return NULL;
432 }
433
434 return cl;
435 }
436
437 // check for a name match
438 for( i = 0; i < level.maxclients; i++ )
439 {
440 cl = &level.clients[ i ];
441 if( cl->pers.connected == CON_DISCONNECTED )
442 continue;
443
444 if( !Q_stricmp( cl->pers.netname, s ) )
445 return cl;
446 }
447
448 G_Printf( "User %s is not on the server\n", s );
449
450 return NULL;
451 }
452
453 /*
454 ===================
455 Svcmd_ForceTeam_f
456
457 forceteam <player> <team>
458 ===================
459 */
Svcmd_ForceTeam_f(void)460 void Svcmd_ForceTeam_f( void )
461 {
462 gclient_t *cl;
463 char str[ MAX_TOKEN_CHARS ];
464
465 // find the player
466 trap_Argv( 1, str, sizeof( str ) );
467 cl = ClientForString( str );
468
469 if( !cl )
470 return;
471
472 // set the team
473 trap_Argv( 2, str, sizeof( str ) );
474 /*SetTeam( &g_entities[cl - level.clients], str );*/
475 //FIXME: tremulise this
476 }
477
478 char *ConcatArgs( int start );
479
480 /*
481 =================
482 ConsoleCommand
483
484 =================
485 */
ConsoleCommand(void)486 qboolean ConsoleCommand( void )
487 {
488 char cmd[ MAX_TOKEN_CHARS ];
489
490 trap_Argv( 0, cmd, sizeof( cmd ) );
491
492 if( Q_stricmp( cmd, "entitylist" ) == 0 )
493 {
494 Svcmd_EntityList_f( );
495 return qtrue;
496 }
497
498 if( Q_stricmp( cmd, "forceteam" ) == 0 )
499 {
500 Svcmd_ForceTeam_f( );
501 return qtrue;
502 }
503
504 if( Q_stricmp( cmd, "game_memory" ) == 0 )
505 {
506 Svcmd_GameMem_f( );
507 return qtrue;
508 }
509
510 if( Q_stricmp( cmd, "addip" ) == 0 )
511 {
512 Svcmd_AddIP_f( );
513 return qtrue;
514 }
515
516 if( Q_stricmp( cmd, "removeip" ) == 0 )
517 {
518 Svcmd_RemoveIP_f( );
519 return qtrue;
520 }
521
522 if( Q_stricmp( cmd, "listip" ) == 0 )
523 {
524 trap_SendConsoleCommand( EXEC_NOW, "g_banIPs\n" );
525 return qtrue;
526 }
527
528 if( Q_stricmp( cmd, "mapRotation" ) == 0 )
529 {
530 char *rotationName = ConcatArgs( 1 );
531
532 if( !G_StartMapRotation( rotationName, qfalse ) )
533 G_Printf( "Can't find map rotation %s\n", rotationName );
534
535 return qtrue;
536 }
537
538 if( Q_stricmp( cmd, "stopMapRotation" ) == 0 )
539 {
540 G_StopMapRotation( );
541
542 return qtrue;
543 }
544
545 if( Q_stricmp( cmd, "alienWin" ) == 0 )
546 {
547 int i;
548 gentity_t *e;
549
550 for( i = 1, e = g_entities + i; i < level.num_entities; i++, e++ )
551 {
552 if( e->s.modelindex == BA_H_SPAWN )
553 G_Damage( e, NULL, NULL, NULL, NULL, 10000, 0, MOD_SUICIDE );
554 }
555
556 return qtrue;
557 }
558
559 if( Q_stricmp( cmd, "humanWin" ) == 0 )
560 {
561 int i;
562 gentity_t *e;
563
564 for( i = 1, e = g_entities + i; i < level.num_entities; i++, e++ )
565 {
566 if( e->s.modelindex == BA_A_SPAWN )
567 G_Damage( e, NULL, NULL, NULL, NULL, 10000, 0, MOD_SUICIDE );
568 }
569
570 return qtrue;
571 }
572
573 if( g_dedicated.integer )
574 {
575 if( Q_stricmp( cmd, "say" ) == 0 )
576 {
577 G_SendCommandFromServer( -1, va( "print \"server: %s\n\"", ConcatArgs( 1 ) ) );
578 return qtrue;
579 }
580
581 // everything else will also be printed as a say command
582 G_SendCommandFromServer( -1, va( "print \"server: %s\n\"", ConcatArgs( 0 ) ) );
583 return qtrue;
584 }
585
586 return qfalse;
587 }
588
589