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