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