g_svcmds.c (9902B)
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 Foobar; 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 */ 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 */ 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