From ed1a89fccdb41810c4d634005a1b1b1ac8f40c8f Mon Sep 17 00:00:00 2001 From: "M. Kristall" Date: Tue, 25 Sep 2007 06:44:19 +0000 Subject: * (bug 3307) use boundary checking to prevent potential buffer overflow * (bug 2969) limit number of players to g_maxGameClients * use G_TeamCommand() for team vote messages --- src/game/g_admin.c | 32 ++++++++--------- src/game/g_cmds.c | 101 ++++++++++++++++++++++------------------------------- src/game/g_local.h | 4 +-- src/game/g_main.c | 10 +++--- 4 files changed, 65 insertions(+), 82 deletions(-) (limited to 'src/game') diff --git a/src/game/g_admin.c b/src/game/g_admin.c index 4200d3cb..cffefc54 100644 --- a/src/game/g_admin.c +++ b/src/game/g_admin.c @@ -699,7 +699,7 @@ static void admin_log( gentity_t *admin, char *cmd, int skiparg ) if( G_SayArgc() > 1 + skiparg ) { G_SayArgv( 1 + skiparg, name, sizeof( name ) ); - if( G_ClientNumbersFromString( name, pids ) == 1 ) + if( G_ClientNumbersFromString( name, pids, MAX_CLIENTS ) == 1 ) { victim = &g_entities[ pids[ 0 ] ]; } @@ -1603,7 +1603,7 @@ static qboolean admin_create_ban( gentity_t *ent, qboolean G_admin_kick( gentity_t *ent, int skiparg ) { - int pids[ MAX_CLIENTS ]; + int pids[ MAX_CLIENTS ], found; char name[ MAX_NAME_LENGTH ], *reason, err[ MAX_STRING_CHARS ]; int minargc; gentity_t *vic; @@ -1619,9 +1619,9 @@ qboolean G_admin_kick( gentity_t *ent, int skiparg ) } G_SayArgv( 1 + skiparg, name, sizeof( name ) ); reason = G_SayConcatArgs( 2 + skiparg ); - if( G_ClientNumbersFromString( name, pids ) != 1 ) + if( ( found = G_ClientNumbersFromString( name, pids, MAX_CLIENTS ) ) != 1 ) { - G_MatchOnePlayer( pids, err, sizeof( err ) ); + G_MatchOnePlayer( pids, found, err, sizeof( err ) ); ADMP( va( "^3!kick: ^7%s\n", err ) ); return qfalse; } @@ -1902,7 +1902,7 @@ qboolean G_admin_unban( gentity_t *ent, int skiparg ) qboolean G_admin_putteam( gentity_t *ent, int skiparg ) { - int pids[ MAX_CLIENTS ]; + int pids[ MAX_CLIENTS ], found; char name[ MAX_NAME_LENGTH ], team[ 7 ], err[ MAX_STRING_CHARS ]; gentity_t *vic; pTeam_t teamnum = PTE_NONE; @@ -1916,9 +1916,9 @@ qboolean G_admin_putteam( gentity_t *ent, int skiparg ) return qfalse; } - if( G_ClientNumbersFromString( name, pids ) != 1 ) + if( ( found = G_ClientNumbersFromString( name, pids, MAX_CLIENTS ) ) != 1 ) { - G_MatchOnePlayer( pids, err, sizeof( err ) ); + G_MatchOnePlayer( pids, found, err, sizeof( err ) ); ADMP( va( "^3!putteam: ^7%s\n", err ) ); return qfalse; } @@ -2001,7 +2001,7 @@ qboolean G_admin_map( gentity_t *ent, int skiparg ) qboolean G_admin_mute( gentity_t *ent, int skiparg ) { - int pids[ MAX_CLIENTS ]; + int pids[ MAX_CLIENTS ], found; char name[ MAX_NAME_LENGTH ], err[ MAX_STRING_CHARS ]; char command[ MAX_ADMIN_CMD_LEN ], *cmd; gentity_t *vic; @@ -2016,9 +2016,9 @@ qboolean G_admin_mute( gentity_t *ent, int skiparg ) if( cmd && *cmd == '!' ) cmd++; G_SayArgv( 1 + skiparg, name, sizeof( name ) ); - if( G_ClientNumbersFromString( name, pids ) != 1 ) + if( ( found = G_ClientNumbersFromString( name, pids, MAX_CLIENTS ) ) != 1 ) { - G_MatchOnePlayer( pids, err, sizeof( err ) ); + G_MatchOnePlayer( pids, found, err, sizeof( err ) ); ADMP( va( "^3!mute: ^7%s\n", err ) ); return qfalse; } @@ -2061,7 +2061,7 @@ qboolean G_admin_mute( gentity_t *ent, int skiparg ) qboolean G_admin_denybuild( gentity_t *ent, int skiparg ) { - int pids[ MAX_CLIENTS ]; + int pids[ MAX_CLIENTS ], found; char name[ MAX_NAME_LENGTH ], err[ MAX_STRING_CHARS ]; char command[ MAX_ADMIN_CMD_LEN ], *cmd; gentity_t *vic; @@ -2076,9 +2076,9 @@ qboolean G_admin_denybuild( gentity_t *ent, int skiparg ) return qfalse; } G_SayArgv( 1 + skiparg, name, sizeof( name ) ); - if( G_ClientNumbersFromString( name, pids ) != 1 ) + if( ( found = G_ClientNumbersFromString( name, pids, MAX_CLIENTS ) ) != 1 ) { - G_MatchOnePlayer( pids, err, sizeof( err ) ); + G_MatchOnePlayer( pids, found, err, sizeof( err ) ); ADMP( va( "^3!%s: ^7%s\n", cmd, err ) ); return qfalse; } @@ -2725,7 +2725,7 @@ qboolean G_admin_spec999( gentity_t *ent, int skiparg ) qboolean G_admin_rename( gentity_t *ent, int skiparg ) { - int pids[ MAX_CLIENTS ]; + int pids[ MAX_CLIENTS ], found; char name[ MAX_NAME_LENGTH ]; char newname[ MAX_NAME_LENGTH ]; char oldname[ MAX_NAME_LENGTH ]; @@ -2742,9 +2742,9 @@ qboolean G_admin_rename( gentity_t *ent, int skiparg ) G_SayArgv( 1 + skiparg, name, sizeof( name ) ); s = G_SayConcatArgs( 2 + skiparg ); Q_strncpyz( newname, s, sizeof( newname ) ); - if( G_ClientNumbersFromString( name, pids ) != 1 ) + if( ( found = G_ClientNumbersFromString( name, pids, MAX_CLIENTS ) ) != 1 ) { - G_MatchOnePlayer( pids, err, sizeof( err ) ); + G_MatchOnePlayer( pids, found, err, sizeof( err ) ); ADMP( va( "^3!rename: ^7%s\n", err ) ); return qfalse; } diff --git a/src/game/g_cmds.c b/src/game/g_cmds.c index dac9a9a5..4b82b9e8 100644 --- a/src/game/g_cmds.c +++ b/src/game/g_cmds.c @@ -126,41 +126,36 @@ G_MatchOnePlayer This is a companion function to G_ClientNumbersFromString() -returns qtrue if the int array plist only has one client id, false otherwise -In the case of false, err will be populated with an error message. +err will be populated with an error message. ================== */ -qboolean G_MatchOnePlayer( int *plist, char *err, int len ) +void G_MatchOnePlayer( int *plist, int num, char *err, int len ) { gclient_t *cl; - int *p; + int i; char line[ MAX_NAME_LENGTH + 10 ] = {""}; err[ 0 ] = '\0'; - if( plist[ 0 ] == -1 ) + if( num == 0 ) { Q_strcat( err, len, "no connected player by that name or slot #" ); - return qfalse; } - if( plist[ 1 ] != -1 ) + else if( num > 1 ) { Q_strcat( err, len, "more than one player name matches. " "be more specific or use the slot #:\n" ); - for( p = plist; *p != -1; p++ ) + for( i = 0; i < num; i++ ) { - cl = &level.clients[ *p ]; - if( cl->pers.connected == CON_CONNECTED ) - { - Com_sprintf( line, sizeof( line ), "%2i - %s^7\n", - *p, cl->pers.netname ); - if( strlen( err ) + strlen( line ) > len ) - break; - Q_strcat( err, len, line ); - } + cl = &level.clients[ plist[ i ] ]; + if( cl->pers.connected == CON_DISCONNECTED ) + continue; + Com_sprintf( line, sizeof( line ), "%2i - %s^7\n", + plist[ i ], cl->pers.netname ); + if( strlen( err ) + strlen( line ) > len ) + break; + Q_strcat( err, len, line ); } - return qfalse; } - return qtrue; } /* @@ -168,40 +163,29 @@ qboolean G_MatchOnePlayer( int *plist, char *err, int len ) G_ClientNumbersFromString Sets plist to an array of integers that represent client numbers that have -names that are a partial match for s. List is terminated by a -1. +names that are a partial match for s. -Returns number of matching clientids. +Returns number of matching clientids up to max. ================== */ -int G_ClientNumbersFromString( char *s, int *plist ) +int G_ClientNumbersFromString( char *s, int *plist, int max ) { gclient_t *p; int i, found = 0; - char n2[ MAX_NAME_LENGTH ] = {""}; - char s2[ MAX_NAME_LENGTH ] = {""}; - qboolean is_slot = qtrue; - - *plist = -1; + char n2[ MAX_NAME_LENGTH ] = {""}; + char s2[ MAX_NAME_LENGTH ] = {""}; // if a number is provided, it might be a slot # - for( i = 0; i < (int)strlen( s ); i++ ) + for( i = 0; s[ i ] && isdigit( s[ i ] ); i++ ); + if( !s[ i ] ) { - if( s[i] < '0' || s[i] > '9' ) - { - is_slot = qfalse; - break; - } - } - - if( is_slot ) { i = atoi( s ); - if( i >= 0 && i < level.maxclients ) { + if( i >= 0 && i < level.maxclients ) + { p = &level.clients[ i ]; - if( p->pers.connected == CON_CONNECTED || - p->pers.connected == CON_CONNECTING ) + if( p->pers.connected != CON_DISCONNECTED ) { - *plist++ = i; - *plist = -1; + *plist = i; return 1; } } @@ -213,11 +197,10 @@ int G_ClientNumbersFromString( char *s, int *plist ) G_SanitiseName( s, s2 ); if( strlen( s2 ) < 1 ) return 0; - for( i = 0; i < level.maxclients; i++ ) + for( i = 0; i < level.maxclients && found <= max; i++ ) { p = &level.clients[ i ]; - if(p->pers.connected != CON_CONNECTED - && p->pers.connected != CON_CONNECTING) + if( p->pers.connected == CON_DISCONNECTED ) { continue; } @@ -228,7 +211,6 @@ int G_ClientNumbersFromString( char *s, int *plist ) found++; } } - *plist = -1; return found; } @@ -676,6 +658,14 @@ void Cmd_Team_f( gentity_t *ent ) if( !Q_stricmp( s, "spectate" ) ) team = PTE_NONE; + else if( !force && oldteam == PTE_NONE && g_maxGameClients.integer && + level.numPlayingClients >= g_maxGameClients.integer ) + { + trap_SendServerCommand( ent-g_entities, va( "print \"The maximum number of " + "playing clients has been reached (g_maxGameClients = %d)\n\"", + g_maxGameClients.integer ) ); + return; + } else if( !Q_stricmp( s, "aliens" ) ) { if( level.alienTeamLocked ) @@ -1051,7 +1041,7 @@ void Cmd_CallVote_f( gentity_t *ent ) return; } - if( G_ClientNumbersFromString( arg2, clientNums ) == 1 ) + if( G_ClientNumbersFromString( arg2, clientNums, MAX_CLIENTS ) == 1 ) { // there was only one partial name match clientNum = clientNums[ 0 ]; @@ -1281,7 +1271,7 @@ void Cmd_CallTeamVote_f( gentity_t *ent ) return; } - if( G_ClientNumbersFromString( arg2, clientNums ) == 1 ) + if( G_ClientNumbersFromString( arg2, clientNums, MAX_CLIENTS ) == 1 ) { // there was only one partial name match clientNum = clientNums[ 0 ]; @@ -1393,15 +1383,8 @@ void Cmd_CallTeamVote_f( gentity_t *ent ) } ent->client->pers.voteCount++; - for( i = 0 ; i < level.maxclients ; i++ ) - { - if( level.clients[ i ].pers.connected == CON_DISCONNECTED ) - continue; - - if( level.clients[ i ].ps.stats[ STAT_PTEAM ] == team ) - trap_SendServerCommand( i, va("print \"%s " S_COLOR_WHITE - "called a team vote\n\"", ent->client->pers.netname ) ); - } + G_TeamCommand( team, va( "print \"%s " S_COLOR_WHITE "called a team vote\n\"", + ent->client->pers.netname ) ); // start the voting, the caller autoamtically votes yes level.teamVoteTime[ cs_offset ] = level.time; @@ -2593,7 +2576,7 @@ void Cmd_Follow_f( gentity_t *ent ) ent->client->sess.spectatorState == SPECTATOR_FOLLOW ) { trap_Argv( 1, arg, sizeof( arg ) ); - if( G_ClientNumbersFromString( arg, pids ) == 1 ) + if( G_ClientNumbersFromString( arg, pids, MAX_CLIENTS ) == 1 ) { i = pids[ 0 ]; } @@ -2758,7 +2741,7 @@ static void Cmd_Ignore_f( gentity_t *ent ) } Q_strncpyz( name, ConcatArgs( 1 ), sizeof( name ) ); - matches = G_ClientNumbersFromString( name, pids ); + matches = G_ClientNumbersFromString( name, pids, MAX_CLIENTS ); if( matches < 1 ) { trap_SendServerCommand( ent-g_entities, va( "print \"[skipnotify]" @@ -3102,7 +3085,7 @@ void G_PrivateMessage( gentity_t *ent ) G_SayArgv( 1+skipargs, name, sizeof( name ) ); msg = G_SayConcatArgs( 2+skipargs ); - pcount = G_ClientNumbersFromString( name, pids ); + pcount = G_ClientNumbersFromString( name, pids, MAX_CLIENTS ); if( ent ) { diff --git a/src/game/g_local.h b/src/game/g_local.h index 59e39eec..45f649d0 100644 --- a/src/game/g_local.h +++ b/src/game/g_local.h @@ -696,8 +696,8 @@ void Cmd_Score_f( gentity_t *ent ); void G_StopFollowing( gentity_t *ent ); qboolean G_FollowNewClient( gentity_t *ent, int dir ); void G_ToggleFollow( gentity_t *ent ); -qboolean G_MatchOnePlayer( int *plist, char *err, int len ); -int G_ClientNumbersFromString( char *s, int *plist ); +void G_MatchOnePlayer( int *plist, int num, char *err, int len ); +int G_ClientNumbersFromString( char *s, int *plist, int max ); int G_SayArgc( void ); qboolean G_SayArgv( int n, char *buffer, int bufferLength ); char *G_SayConcatArgs( int start ); diff --git a/src/game/g_main.c b/src/game/g_main.c index 14986dbb..84209315 100644 --- a/src/game/g_main.c +++ b/src/game/g_main.c @@ -148,9 +148,10 @@ static cvarTable_t gameCvarTable[ ] = // latched vars { &g_maxclients, "sv_maxclients", "8", CVAR_SERVERINFO | CVAR_LATCH | CVAR_ARCHIVE, 0, qfalse }, - { &g_maxGameClients, "g_maxGameClients", "0", CVAR_SERVERINFO | CVAR_LATCH | CVAR_ARCHIVE, 0, qfalse }, // change anytime vars + { &g_maxGameClients, "g_maxGameClients", "0", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qfalse }, + { &g_timelimit, "timelimit", "0", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue }, { &g_suddenDeathTime, "g_suddenDeathTime", "0", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue }, @@ -2106,21 +2107,20 @@ void CheckTeamVote( int team ) if( level.time - level.teamVoteTime[ cs_offset ] >= VOTE_TIME ) { - trap_SendServerCommand( -1, "print \"Team vote failed\n\"" ); + G_TeamCommand( team, "print \"Team vote failed\n\"" ); } else { if( level.teamVoteYes[ cs_offset ] > level.numteamVotingClients[ cs_offset ] / 2 ) { // execute the command, then remove the vote - trap_SendServerCommand( -1, "print \"Team vote passed\n\"" ); - // + G_TeamCommand( team, "print \"Team vote passed\n\"" ); trap_SendConsoleCommand( EXEC_APPEND, va( "%s\n", level.teamVoteString[ cs_offset ] ) ); } else if( level.teamVoteNo[ cs_offset ] >= level.numteamVotingClients[ cs_offset ] / 2 ) { // same behavior as a timeout - trap_SendServerCommand( -1, "print \"Team vote failed\n\"" ); + G_TeamCommand( team, "print \"Team vote failed\n\"" ); } else { -- cgit