/* =========== ClientBotConnect Cut-down version of ClientConnect. Doesn't do things not relevant to bots (which are local GUIDless clients). ============ */ const char *ClientBotConnect( int clientNum, bool firstTime, team_t team ) { const char *userInfoError; gclient_t *client; char userinfo[ MAX_INFO_STRING ]; gentity_t *ent; ent = &g_entities[ clientNum ]; client = &level.clients[ clientNum ]; ent->client = client; memset( client, 0, sizeof( *client ) ); trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) ); client->pers.localClient = true; G_AddressParse( "localhost", &client->pers.ip ); Q_strncpyz( client->pers.guid, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", sizeof( client->pers.guid ) ); client->pers.admin = nullptr; client->pers.pubkey_authenticated = true; client->pers.connected = CON_CONNECTING; // read or initialize the session data if ( firstTime ) { G_InitSessionData( client, userinfo ); } G_ReadSessionData( client ); // get and distribute relevant parameters G_namelog_connect( client ); userInfoError = ClientUserinfoChanged( clientNum, false ); if ( userInfoError != nullptr ) { return userInfoError; } ent->r.svFlags |= SVF_BOT; // can happen during reconnection if ( !ent->botMind ) { G_BotSetDefaults( clientNum, team, client->sess.botSkill, client->sess.botTree ); } G_LogPrintf( "ClientConnect: %i [%s] (%s) \"%s^7\" \"%s^7\" [BOT]", clientNum, client->pers.ip.str[0] ? client->pers.ip.str : "127.0.0.1", client->pers.guid, client->pers.netname, client->pers.netname ); // don't do the "xxx connected" messages if they were caried over from previous level if ( firstTime ) { trap_SendServerCommand( -1, va( "print_tr %s %s", QQ( N_("$1$^7 connected") ), Quote( client->pers.netname ) ) ); } // count current clients and rank for scoreboard CalculateRanks(); return nullptr; }
/* =========== ClientConnect Called when a player begins connecting to the server. Called again for every map change or tournement restart. The session information will be valid after exit. Return NULL if the client should be allowed, otherwise return a string with the reason for denial. Otherwise, the client will be sent the current gamestate and will eventually get to ClientBegin. firstTime will be qtrue the very first time a client connects to the server machine, but qfalse on map changes and tournement restarts. ============ */ char *ClientConnect( int clientNum, qboolean firstTime ) { char *value; char *userInfoError; gclient_t *client; char userinfo[ MAX_INFO_STRING ]; gentity_t *ent; char reason[ MAX_STRING_CHARS ] = {""}; int i; ent = &g_entities[ clientNum ]; client = &level.clients[ clientNum ]; // ignore if client already connected if( client->pers.connected != CON_DISCONNECTED ) return NULL; ent->client = client; memset( client, 0, sizeof( *client ) ); trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) ); value = Info_ValueForKey( userinfo, "cl_guid" ); Q_strncpyz( client->pers.guid, value, sizeof( client->pers.guid ) ); value = Info_ValueForKey( userinfo, "ip" ); // check for local client if( !strcmp( value, "localhost" ) ) client->pers.localClient = qtrue; G_AddressParse( value, &client->pers.ip ); client->pers.admin = G_admin_admin( client->pers.guid ); // check for admin ban if( G_admin_ban_check( ent, reason, sizeof( reason ) ) ) { return va( "%s", reason ); } // check for a password value = Info_ValueForKey( userinfo, "password" ); if( g_password.string[ 0 ] && Q_stricmp( g_password.string, "none" ) && strcmp( g_password.string, value ) != 0 ) return "Invalid password"; // add guid to session so we don't have to keep parsing userinfo everywhere for( i = 0; i < sizeof( client->pers.guid ) - 1 && isxdigit( client->pers.guid[ i ] ); i++ ); if( i < sizeof( client->pers.guid ) - 1 ) return "Invalid GUID"; for( i = 0; i < level.maxclients; i++ ) { if( level.clients[ i ].pers.connected == CON_DISCONNECTED ) continue; if( !Q_stricmp( client->pers.guid, level.clients[ i ].pers.guid ) ) { if( !G_ClientIsLagging( level.clients + i ) ) { trap_SendServerCommand( i, "cp \"Your GUID is not secure\"" ); return "Duplicate GUID"; } trap_DropClient( i, "Ghost" ); } } client->pers.connected = CON_CONNECTING; // read or initialize the session data if( firstTime || level.newSession ) G_InitSessionData( client, userinfo ); G_ReadSessionData( client ); // get and distribute relevent paramters G_namelog_connect( client ); userInfoError = ClientUserinfoChanged( clientNum, qfalse ); if( userInfoError != NULL ) return userInfoError; G_LogPrintf( "ClientConnect: %i [%s] (%s) \"%s^7\" \"%c%s%c^7\"\n", clientNum, client->pers.ip.str, client->pers.guid, client->pers.netname, DECOLOR_OFF, client->pers.netname, DECOLOR_ON ); // don't do the "xxx connected" messages if they were caried over from previous level if( firstTime ) trap_SendServerCommand( -1, va( "print \"%s" S_COLOR_WHITE " connected\n\"", client->pers.netname ) ); if( client->pers.admin ) G_admin_authlog( ent ); // count current clients and rank for scoreboard CalculateRanks( ); // if this is after !restart keepteams or !restart switchteams, apply said selection if ( client->sess.restartTeam != TEAM_NONE ) { G_ChangeTeam( ent, client->sess.restartTeam ); client->sess.restartTeam = TEAM_NONE; } return NULL; }
/* =========== ClientConnect Called when a player begins connecting to the server. Called again for every map change or tournement restart. The session information will be valid after exit. Return NULL if the client should be allowed, otherwise return a string with the reason for denial. Otherwise, the client will be sent the current gamestate and will eventually get to ClientBegin. firstTime will be qtrue the very first time a client connects to the server machine, but qfalse on map changes and tournement restarts. ============ */ char *ClientConnect( int clientNum, qboolean firstTime, qboolean isBot ) { char *value; // char *areabits; gclient_t *client; char userinfo[MAX_INFO_STRING]; gentity_t *ent; ent = &g_entities[ clientNum ]; trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) ); // IP filtering // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=500 // recommanding PB based IP / GUID banning, the builtin system is pretty limited // check to see if they are on the banned IP list value = Info_ValueForKey (userinfo, "ip"); if ( G_FilterPacket( value ) ) { return "You are banned from this server."; } // we don't check password for bots and local client // NOTE: local client <-> "ip" "localhost" // this means this client is not running in our current process if ( !( ent->r.svFlags & SVF_BOT ) && (strcmp(value, "localhost") != 0)) { // check for a password value = Info_ValueForKey (userinfo, "password"); if ( g_password.string[0] && Q_stricmp( g_password.string, "none" ) && strcmp( g_password.string, value) != 0) { return "Invalid password"; } } // they can connect ent->client = level.clients + clientNum; client = ent->client; // areabits = client->areabits; memset( client, 0, sizeof(*client) ); client->pers.connected = CON_CONNECTING; // read or initialize the session data if ( firstTime || level.newSession ) { G_InitSessionData( client, userinfo ); } G_ReadSessionData( client ); if( isBot ) { ent->r.svFlags |= SVF_BOT; ent->inuse = qtrue; if( !G_BotConnect( clientNum, !firstTime ) ) { return "BotConnectfailed"; } } // get and distribute relevent paramters G_LogPrintf( "ClientConnect: %i\n", clientNum ); ClientUserinfoChanged( clientNum ); // don't do the "xxx connected" messages if they were caried over from previous level if ( firstTime ) { trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " connected\n\"", client->pers.netname) ); } if ( g_gametype.integer >= GT_TEAM && client->sess.sessionTeam != TEAM_SPECTATOR ) { BroadcastTeamChange( client, -1 ); } // count current clients and rank for scoreboard CalculateRanks(); // for statistics // client->areabits = areabits; // if ( !client->areabits ) // client->areabits = G_Alloc( (trap_AAS_PointReachabilityAreaIndex( NULL ) + 7) / 8 ); return NULL; }
/* =========== ClientConnect Called when a player begins connecting to the server. Called again for every map change or tournement restart. The session information will be valid after exit. Return nullptr if the client should be allowed, otherwise return a string with the reason for denial. Otherwise, the client will be sent the current gamestate and will eventually get to ClientBegin. firstTime will be true the very first time a client connects to the server machine, but false on map changes and tournement restarts. ============ */ const char *ClientConnect( int clientNum, bool firstTime ) { const char *value; const char *userInfoError; gclient_t *client; char userinfo[ MAX_INFO_STRING ]; char pubkey[ RSA_STRING_LENGTH ]; gentity_t *ent; char reason[ MAX_STRING_CHARS ] = { "" }; int i; const char *country; ent = &g_entities[ clientNum ]; client = &level.clients[ clientNum ]; // ignore if client already connected if ( client->pers.connected != CON_DISCONNECTED ) { return nullptr; } ent->client = client; memset( client, 0, sizeof( *client ) ); trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) ); value = Info_ValueForKey( userinfo, "ip" ); // check for local client if ( !strcmp( value, "localhost" ) ) { client->pers.localClient = true; } G_AddressParse( value, &client->pers.ip ); trap_GetPlayerPubkey( clientNum, pubkey, sizeof( pubkey ) ); if ( strlen( pubkey ) != RSA_STRING_LENGTH - 1 ) { return "Invalid pubkey key"; } trap_GenFingerprint( pubkey, sizeof( pubkey ), client->pers.guid, sizeof( client->pers.guid ) ); client->pers.admin = G_admin_admin( client->pers.guid ); client->pers.pubkey_authenticated = false; if ( client->pers.admin ) { Com_GMTime( &client->pers.admin->lastSeen ); } // check for admin ban if ( G_admin_ban_check( ent, reason, sizeof( reason ) ) ) { return va( "%s", reason ); // reason is local } // check for a password value = Info_ValueForKey( userinfo, "password" ); if ( g_password.string[ 0 ] && Q_stricmp( g_password.string, "none" ) && strcmp( g_password.string, value ) != 0 ) { return "Invalid password"; } // if a player reconnects quickly after a disconnect, the client disconnect may never be called, thus flag can get lost in the ether if ( ent->inuse ) { G_LogPrintf( "Forcing disconnect on active client: %i", (int)( ent - g_entities ) ); // so lets just fix up anything that should happen on a disconnect ClientDisconnect( ent-g_entities ); } for ( i = 0; i < level.maxclients; i++ ) { if ( level.clients[ i ].pers.connected == CON_DISCONNECTED ) { continue; } if ( !( g_entities[i].r.svFlags & SVF_BOT ) && !Q_stricmp( client->pers.guid, level.clients[ i ].pers.guid ) ) { if ( !G_ClientIsLagging( level.clients + i ) ) { trap_SendServerCommand( i, "cp \"Your GUID is not secure\"" ); return "Duplicate GUID"; } trap_DropClient( i, "Ghost" ); } } client->pers.connected = CON_CONNECTING; // read or initialize the session data if ( firstTime ) { G_InitSessionData( client, userinfo ); } G_ReadSessionData( client ); // get and distribute relevent paramters G_namelog_connect( client ); userInfoError = ClientUserinfoChanged( clientNum, false ); if ( userInfoError != nullptr ) { return userInfoError; } G_LogPrintf( "ClientConnect: %i [%s] (%s) \"%s^7\" \"%s^7\"", clientNum, client->pers.ip.str[0] ? client->pers.ip.str : "127.0.0.1", client->pers.guid, client->pers.netname, client->pers.netname ); country = Info_ValueForKey( userinfo, "geoip" ); Q_strncpyz( client->pers.country, country, sizeof( client->pers.country ) ); G_SendClientPmoveParams(clientNum); // don't do the "xxx connected" messages if they were caried over from previous level if ( firstTime ) { if ( g_geoip.integer && country && *country ) { trap_SendServerCommand( -1, va( "print_tr %s %s %s", QQ( N_("$1$^7 connected from $2$") ), Quote( client->pers.netname ), Quote( country ) ) ); } else { trap_SendServerCommand( -1, va( "print_tr %s %s", QQ( N_("$1$^7 connected") ), Quote( client->pers.netname ) ) ); } } // count current clients and rank for scoreboard CalculateRanks(); // if this is after !restart keepteams or !restart switchteams, apply said selection if ( client->sess.restartTeam != TEAM_NONE ) { G_ChangeTeam( ent, client->sess.restartTeam ); client->sess.restartTeam = TEAM_NONE; } return nullptr; }
/* =========== PlayerConnect Called when a player begins connecting to the server. Called again for every map change or tournement restart. The session information will be valid after exit. Return NULL if the player should be allowed, otherwise return a string with the reason for denial. Otherwise, the player will be sent the current gamestate and will eventually get to PlayerBegin. firstTime will be qtrue the very first time a player connects to the server machine, but qfalse on map changes and tournement restarts. ============ */ char *PlayerConnect( int playerNum, qboolean firstTime, qboolean isBot, int connectionNum, int localPlayerNum ) { char *value; // char *areabits; gplayer_t *player; char userinfo[MAX_INFO_STRING]; gentity_t *ent; qboolean firstConnectionPlayer; ent = &g_entities[ playerNum ]; trap_GetUserinfo( playerNum, userinfo, sizeof( userinfo ) ); // Check if it's the first player on the client (i.e. not a splitscreen player) firstConnectionPlayer = ( level.connections[connectionNum].numLocalPlayers == 0 ); if ( firstConnectionPlayer ) { // IP filtering // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=500 // recommanding PB based IP / GUID banning, the builtin system is pretty limited // check to see if they are on the banned IP list value = Info_ValueForKey (userinfo, "ip"); if ( G_FilterPacket( value ) ) { return "You are banned from this server."; } // we don't check password for bots and local client // NOTE: local client <-> "ip" "localhost" // this means this client is not running in our current process if ( !isBot && (strcmp(value, "localhost") != 0) ) { // check for a password value = Info_ValueForKey (userinfo, "password"); if ( g_password.string[0] && Q_stricmp( g_password.string, "none" ) && strcmp( g_password.string, value) != 0) { return "Invalid password"; } } } else { // Don't allow splitscreen players in single player. if ( g_singlePlayer.integer ) { return "Splitscreen not allowed in single player."; } } // if a player reconnects quickly after a disconnect, the player disconnect may never be called, thus flag can get lost in the ether if (ent->inuse) { G_LogPrintf("Forcing disconnect on active player: %i\n", playerNum); // so lets just fix up anything that should happen on a disconnect PlayerDisconnect(playerNum); } // they can connect ent->player = level.players + playerNum; player = ent->player; // areabits = player->areabits; memset( player, 0, sizeof(*player) ); player->pers.connected = CON_CONNECTING; player->pers.initialSpawn = qtrue; // update player connection info level.connections[connectionNum].numLocalPlayers++; level.connections[connectionNum].localPlayerNums[localPlayerNum] = playerNum; player->pers.connectionNum = connectionNum; player->pers.localPlayerNum = localPlayerNum; // check for local client value = Info_ValueForKey( userinfo, "ip" ); if ( !strcmp( value, "localhost" ) ) { player->pers.localClient = qtrue; } if( isBot ) { ent->r.svFlags |= SVF_BOT; ent->inuse = qtrue; if( !G_BotConnect( playerNum, !firstTime ) ) { return "BotConnectfailed"; } } // read or initialize the session data if ( firstTime || level.newSession ) { G_InitSessionData( player, userinfo ); } G_ReadSessionData( player ); // get and distribute relevent paramters G_LogPrintf( "PlayerConnect: %i\n", playerNum ); PlayerUserinfoChanged( playerNum ); // don't do the "xxx connected" messages if they were caried over from previous level if ( firstTime ) { if ( firstConnectionPlayer ) { trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " connected\n\"", player->pers.netname) ); } else { trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " dropped in\n\"", player->pers.netname) ); } } // count current players and rank for scoreboard CalculateRanks(); // for statistics // player->areabits = areabits; // if ( !player->areabits ) // player->areabits = trap_Alloc( (trap_AAS_PointReachabilityAreaIndex( NULL ) + 7) / 8, NULL ); return NULL; }
/* =========== ClientConnect Called when a player begins connecting to the server. Called again for every map change or tournement restart. The session information will be valid after exit. Return NULL if the client should be allowed, otherwise return a string with the reason for denial. Otherwise, the client will be sent the current gamestate and will eventually get to ClientBegin. firstTime will be qtrue the very first time a client connects to the server machine, but qfalse on map changes and tournement restarts. ============ */ char *ClientConnect( int clientNum, qboolean firstTime ) { char *value; gclient_t *client; char userinfo[ MAX_INFO_STRING ]; gentity_t *ent; char guid[ 33 ]; char reason[ MAX_STRING_CHARS ] = {""}; ent = &g_entities[ clientNum ]; client = &level.clients[ clientNum ]; ent->client = client; memset( client, 0, sizeof( *client ) ); trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) ); value = Info_ValueForKey( userinfo, "cl_guid" ); Q_strncpyz( guid, value, sizeof( guid ) ); // check for admin ban if( G_admin_ban_check( userinfo, reason, sizeof( reason ) ) ) { return va( "%s", reason ); } // IP filtering // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=500 // recommanding PB based IP / GUID banning, the builtin system is pretty limited // check to see if they are on the banned IP list value = Info_ValueForKey( userinfo, "ip" ); Q_strncpyz( client->pers.ip, value, sizeof( client->pers.ip ) ); if( G_FilterPacket( value ) ) return "You are banned from this server."; // check for a password value = Info_ValueForKey( userinfo, "password" ); if( g_password.string[ 0 ] && Q_stricmp( g_password.string, "none" ) && strcmp( g_password.string, value ) != 0 ) return "Invalid password"; // add guid to session so we don't have to keep parsing userinfo everywhere if( !guid[0] ) { Q_strncpyz( client->pers.guid, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", sizeof( client->pers.guid ) ); } else { Q_strncpyz( client->pers.guid, guid, sizeof( client->pers.guid ) ); } // check for local client if( !strcmp( client->pers.ip, "localhost" ) ) client->pers.localClient = qtrue; client->pers.adminLevel = G_admin_level( ent ); client->pers.connected = CON_CONNECTING; // read or initialize the session data if( firstTime || level.newSession ) G_InitSessionData( client, userinfo ); G_ReadSessionData( client ); // get and distribute relevent paramters ClientUserinfoChanged( clientNum ); G_LogPrintf( "ClientConnect: %i [%s] (%s) \"%s\"\n", clientNum, client->pers.ip, client->pers.guid, client->pers.netname ); // don't do the "xxx connected" messages if they were caried over from previous level if( firstTime ) trap_SendServerCommand( -1, va( "print \"%s" S_COLOR_WHITE " connected\n\"", client->pers.netname ) ); // count current clients and rank for scoreboard CalculateRanks( ); G_admin_namelog_update( client, qfalse ); return NULL; }
/* =========== ClientConnect Called when a player begins connecting to the server. Called again for every map change or tournement restart. The session information will be valid after exit. Return NULL if the client should be allowed, otherwise return a string with the reason for denial. Otherwise, the client will be sent the current gamestate and will eventually get to ClientBegin. firstTime will be qtrue the very first time a client connects to the server machine, but qfalse on map changes and tournement restarts. ============ */ char *ClientConnect( int clientNum, qboolean firstTime, qboolean isBot ) { char *value; // char *areabits; gclient_t *client; char userinfo[MAX_INFO_STRING]; gentity_t *ent; gentity_t *te; ent = &g_entities[ clientNum ]; trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) ); // check to see if they are on the banned IP list value = Info_ValueForKey (userinfo, "ip"); if ( G_FilterPacket( value ) ) { return "Banned."; } if ( !( ent->r.svFlags & SVF_BOT ) && !isBot && g_needpass.integer ) { // check for a password value = Info_ValueForKey (userinfo, "password"); if ( g_password.string[0] && Q_stricmp( g_password.string, "none" ) && strcmp( g_password.string, value) != 0) { static char sTemp[1024]; Q_strncpyz(sTemp, G_GetStripEdString("SVINGAME","INVALID_PASSWORD"), sizeof (sTemp) ); return sTemp;// return "Invalid password"; } } // they can connect ent->client = level.clients + clientNum; client = ent->client; // areabits = client->areabits; memset( client, 0, sizeof(*client) ); memset( &bootSession[clientNum], 0, sizeof(bootSession[clientNum]) ); client->pers.connected = CON_CONNECTING; // read or initialize the session data if ( firstTime || level.newSession ) { G_InitSessionData( client, userinfo, isBot ); } G_ReadSessionData( client ); if( isBot ) { ent->r.svFlags |= SVF_BOT; ent->inuse = qtrue; if( !G_BotConnect( clientNum, !firstTime ) ) { return "BotConnectfailed"; } } // get and distribute relevent paramters G_LogPrintf( "ClientConnect: %i\n", clientNum ); ClientUserinfoChanged( clientNum ); // don't do the "xxx connected" messages if they were caried over from previous level if ( firstTime ) { trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " %s\n\"", client->pers.netname, G_GetStripEdString("SVINGAME", "PLCONNECT")) ); } if ( g_gametype.integer >= GT_TEAM && client->sess.sessionTeam != TEAM_SPECTATOR ) { BroadcastTeamChange( client, -1 ); } // count current clients and rank for scoreboard CalculateRanks(); te = G_TempEntity( vec3_origin, EV_CLIENTJOIN ); te->r.svFlags |= SVF_BROADCAST; te->s.eventParm = clientNum; // for statistics // client->areabits = areabits; // if ( !client->areabits ) // client->areabits = G_Alloc( (trap_AAS_PointReachabilityAreaIndex( NULL ) + 7) / 8 ); return NULL; }
/* =========== ClientConnect Called when a player begins connecting to the server. Called again for every map change or tournement restart. The session information will be valid after exit. Return NULL if the client should be allowed, otherwise return a string with the reason for denial. Otherwise, the client will be sent the current gamestate and will eventually get to ClientBegin. firstTime will be qtrue the very first time a client connects to the server machine, but qfalse on map changes and tournement restarts. ============ */ char *ClientConnect( int clientNum, qboolean firstTime ) { char *value; gclient_t *client; char userinfo[ MAX_INFO_STRING ]; gentity_t *ent; char guid[ 33 ]; char ip[ 16 ] = {""}; char reason[ MAX_STRING_CHARS ] = {""}; int i; int retval = 0; ent = &g_entities[ clientNum ]; trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) ); value = Info_ValueForKey( userinfo, "cl_guid" ); Q_strncpyz( guid, value, sizeof( guid ) ); // check for admin ban if( G_admin_ban_check( userinfo, reason, sizeof( reason ) ) ) { return va( "%s", reason ); } // IP filtering // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=500 // recommanding PB based IP / GUID banning, the builtin system is pretty limited // check to see if they are on the banned IP list value = Info_ValueForKey( userinfo, "ip" ); i = 0; while( *value && i < sizeof( ip ) - 2 ) { if( *value != '.' && ( *value < '0' || *value > '9' ) ) break; ip[ i++ ] = *value; value++; } ip[ i ] = '\0'; if( G_FilterPacket( value ) ) return "You are banned from this server."; // check for a password value = Info_ValueForKey( userinfo, "password" ); if( g_password.string[ 0 ] && Q_stricmp( g_password.string, "none" ) && strcmp( g_password.string, value ) != 0 ) return "Invalid password"; // they can connect ent->client = level.clients + clientNum; client = ent->client; memset( client, 0, sizeof(*client) ); // add guid to session so we don't have to keep parsing userinfo everywhere if( !guid[0] ) { Q_strncpyz( client->pers.guid, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", sizeof( client->pers.guid ) ); } else { Q_strncpyz( client->pers.guid, guid, sizeof( client->pers.guid ) ); } Q_strncpyz( client->pers.ip, ip, sizeof( client->pers.ip ) ); client->pers.adminLevel = G_admin_level( ent ); client->pers.connected = CON_CONNECTING; // read or initialize the session data if( firstTime || level.newSession ) G_InitSessionData( client, userinfo ); G_ReadSessionData( client ); if( firstTime ) client->pers.firstConnect = qtrue; else client->pers.firstConnect = qfalse; // get and distribute relevent paramters ClientUserinfoChanged( clientNum ); G_admin_set_adminname( ent ); if( g_decolourLogfiles.integer ) { char decoloured[ MAX_STRING_CHARS ] = ""; if( g_decolourLogfiles.integer == 1 ) { Com_sprintf( decoloured, sizeof(decoloured), " (\"%s^7\")", client->pers.netname ); G_DecolorString( decoloured, decoloured ); G_LogPrintfColoured( "ClientConnect: %i [%s] (%s) \"%s^7\"%s\n", clientNum, client->pers.ip, client->pers.guid, client->pers.netname, decoloured ); } else { G_LogPrintf( "ClientConnect: %i [%s] (%s) \"%s^7\"%s\n", clientNum, client->pers.ip, client->pers.guid, client->pers.netname, decoloured ); } } else { G_LogPrintf( "ClientConnect: %i [%s] (%s) \"%s^7\"\n", clientNum, client->pers.ip, client->pers.guid, client->pers.netname ); } if( client->pers.adminLevel ) { G_LogPrintf( "ClientAuth: %i [%s] \"%s^7\" authenticated to admin level %i using GUID %s (^7%s)\n", clientNum, client->pers.ip, client->pers.netname, client->pers.adminLevel, client->pers.guid, client->pers.adminName ); } // don't do the "xxx connected" messages if they were caried over from previous level retval = G_admin_global_check(userinfo); if((retval & GLOBAL_FORCESPEC) || (retval & GLOBAL_MUTE) || (retval & GLOBAL_DENYBUILD)){trap_SendServerCommand( -1, va( "print \"^3!global: ^7%s^7 has restrictions\n\"", client->pers.netname ) );} else if(retval & GLOBAL_WHITELIST){trap_SendServerCommand( -1, va( "print \"^3!global: ^7%s^7 is whitelisted\n\"", client->pers.netname ) );} if(retval & GLOBAL_FORCESPEC){client->pers.specd = qtrue;} if(retval & GLOBAL_MUTE){client->pers.muted = qtrue;} if(retval & GLOBAL_DENYBUILD){client->pers.denyBuild = qtrue;} if( firstTime ) trap_SendServerCommand( -1, va( "print \"%s" S_COLOR_WHITE " connected\n\"", client->pers.netname ) ); // count current clients and rank for scoreboard CalculateRanks( ); G_admin_namelog_update( client, qfalse ); // if this is after !restart keepteams or !restart switchteams, apply said selection if ( client->sess.restartTeam != PTE_NONE ) { G_ChangeTeam( ent, client->sess.restartTeam ); client->sess.restartTeam = PTE_NONE; } return NULL; }
/* =========== ClientConnect Called when a player begins connecting to the server. Called again for every map change or tournement restart. The session information will be valid after exit. Return NULL if the client should be allowed, otherwise return a string with the reason for denial. Otherwise, the client will be sent the current gamestate and will eventually get to ClientBegin. firstTime will be qtrue the very first time a client connects to the server machine, but qfalse on map changes and tournement restarts. ============ */ char *ClientConnect(int clientNum, qboolean firstTime, qboolean isBot) { char *value; // char *areabits; gclient_t *client; char userinfo[MAX_INFO_STRING]; char reason[MAX_STRING_CHARS] = ""; gentity_t *ent; ent = &g_entities[clientNum]; trap_GetUserinfo(clientNum, userinfo, sizeof(userinfo)); trap_LoadPlayerWeapons(clientNum, rr_weaponsAllowed.string); // IP filtering // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=500 // recommanding PB based IP / GUID banning, the builtin system is pretty limited // check to see if they are on the banned IP list value = Info_ValueForKey(userinfo, "ip"); if(G_FilterPacket(value)) { return "You are banned from this server."; } // we don't check password for bots and local client // NOTE: local client <-> "ip" "localhost" // this means this client is not running in our current process if(!isBot && (strcmp(value, "localhost") != 0)) { // check for a password value = Info_ValueForKey(userinfo, "password"); if(g_password.string[0] && Q_stricmp(g_password.string, "none") && strcmp(g_password.string, value) != 0) { return "Invalid password"; } } #ifdef G_LUA // Lua API callbacks (check with Lua scripts) if(G_LuaHook_ClientConnect(clientNum, firstTime, isBot, reason)) { return "Connection Rejected by lua module."; } #endif // they can connect ent->client = level.clients + clientNum; client = ent->client; // areabits = client->areabits; memset(client, 0, sizeof(*client)); client->pers.connected = CON_CONNECTING; // read or initialize the session data if(firstTime || level.newSession) { G_InitSessionData(client, userinfo); } G_ReadSessionData(client); // Tr3B: add SVF_CAPSULE to players so we can trace against the rotated capsules // in the server entity tracing code SV_ClipToEntity // FIXME UPDATE: this seems to break the box traces against the player capsules by entities like rockets // it should be a bug in CM_TraceBoundingBoxThroughCapsule //ent->r.svFlags |= SVF_CAPSULE; if(isBot) { ent->r.svFlags |= SVF_BOT; ent->inuse = qtrue; #if defined(BRAINWORKS) if(!G_BotConnect(clientNum, !firstTime)) { return "BotConnectfailed"; } #elif defined(ACEBOT) if(!ACESP_BotConnect(clientNum, !firstTime)) { return "BotConnectfailed"; } #else return "BotConnectfailed"; #endif } // get and distribute relevent paramters G_LogPrintf("ClientConnect: %i\n", clientNum); ClientUserinfoChanged(clientNum); // don't do the "xxx connected" messages if they were caried over from previous level if(firstTime) { trap_SendServerCommand(-1, va("print \"%s" S_COLOR_WHITE " connected\n\"", client->pers.netname)); } if(g_gametype.integer >= GT_TEAM && client->sess.sessionTeam != TEAM_SPECTATOR) { BroadcastTeamChange(client, -1); } // count current clients and rank for scoreboard CalculateRanks(); // for statistics // client->areabits = areabits; // if ( !client->areabits ) // client->areabits = G_Alloc( (trap_AAS_PointReachabilityAreaIndex( NULL ) + 7) / 8 ); return NULL; }
/* =========== ClientConnect Called when a player begins connecting to the server. Called again for every map change or tournement restart. The session information will be valid after exit. Return NULL if the client should be allowed, otherwise return a string with the reason for denial. Otherwise, the client will be sent the current gamestate and will eventually get to ClientBegin. firstTime will be qtrue the very first time a client connects to the server machine, but qfalse on map changes and tournement restarts. ============ */ char *ClientConnect(int clientNum, qboolean firstTime) { char *value; gclient_t *client; char userinfo[MAX_INFO_STRING]; gentity_t *ent; char userinfo2[MAX_INFO_STRING]; // Nico, used in connections limit check int i = 0; int clientNum2; // Nico, used in connections limit check int conn_per_ip = 1; // Nico, connections per IP counter char ip[20], ip2[20]; // Nico, used in connections limit check char parsedIp[20], parsedIp2[20]; // Nico, used in connections limit check char cs_name[MAX_NETNAME]; ent = &g_entities[clientNum]; trap_GetUserinfo(clientNum, userinfo, sizeof (userinfo)); // IP filtering // show_bug.cgi?id=500 // recommanding PB based IP / GUID banning, the builtin system is pretty limited // check to see if they are on the banned IP list value = Info_ValueForKey(userinfo, "ip"); if (G_FilterIPBanPacket(value)) { return "You are banned from this server."; } // Nico, check maximum connections per IP (from ETpub) // (prevents fakeplayers DOS http://aluigi.altervista.org/fakep.htm ) // note: value is the client ip if (!getParsedIp(value, parsedIp)) { return "Invalid IP address"; } Q_strncpyz(ip, parsedIp, sizeof (ip)); for (i = 0; i < level.numConnectedClients; ++i) { clientNum2 = level.sortedClients[i]; if (clientNum == clientNum2) { continue; } trap_GetUserinfo(clientNum2, userinfo2, sizeof (userinfo2)); value = Info_ValueForKey(userinfo2, "ip"); if (!getParsedIp(value, parsedIp2)) { continue; } Q_strncpyz(ip2, parsedIp2, sizeof (ip2)); if (strcmp(ip, ip2) == 0) { conn_per_ip++; } } if (conn_per_ip > g_maxConnsPerIP.integer) { G_LogPrintf("%s: possible DoS attack, rejecting client from %s (%d connections already)\n", GAME_VERSION, ip, g_maxConnsPerIP.integer); return "Too many connections from your IP."; } // Nico, end of check maximum connections per IP // Nico, check name value = Info_ValueForKey(userinfo, "name"); Q_strncpyz(cs_name, value, sizeof (cs_name)); if (CheckName(cs_name) != qtrue) { return "Bad name: extended ASCII characters or too long name. Please change your name."; } // we don't check password for bots and local client // NOTE: local client <-> "ip" "localhost" // this means this client is not running in our current process if (strcmp(Info_ValueForKey(userinfo, "ip"), "localhost") != 0) { // check for a password value = Info_ValueForKey(userinfo, "password"); if (g_password.string[0] && Q_stricmp(g_password.string, "none") && strcmp(g_password.string, value) != 0 && (!sv_privatepassword.string[0] || strcmp(sv_privatepassword.string, value) != 0)) { return "Invalid password"; } } // Gordon: porting q3f flag bug fix // If a player reconnects quickly after a disconnect, the client disconnect may never be called, thus flag can get lost in the ether if (ent->inuse) { G_LogPrintf("Forcing disconnect on active client: %d\n", (int)(ent - g_entities)); // so lets just fix up anything that should happen on a disconnect ClientDisconnect(ent - g_entities); } // they can connect ent->client = level.clients + clientNum; client = ent->client; memset(client, 0, sizeof (*client)); client->pers.connected = CON_CONNECTING; // read or initialize the session data if (firstTime) { G_InitSessionData(client); client->pers.enterTime = level.time; client->ps.persistant[PERS_SCORE] = 0; } else { G_ReadSessionData(client); } client->pers.enterTime = level.time; if (firstTime) { // force into spectator client->sess.sessionTeam = TEAM_SPECTATOR; client->sess.spectatorState = SPECTATOR_FREE; client->sess.spectatorClient = 0; // unlink the entity - just in case they were already connected trap_UnlinkEntity(ent); } // Nico, GeoIP if (gidb != NULL) { value = Info_ValueForKey (userinfo, "ip"); if (!strcmp(value, "localhost")) { client->sess.countryCode = 0; } else { char realIP[IP_MAX_LENGTH] = {0};// Nico, used to store IP without :port unsigned long ip; // Nico, remove :port from IP sscanf(value, "%15[0-9.]:%*d", realIP); ip = GeoIP_addr_to_num(realIP); if (((ip & 0xFF000000) == 0x0A000000) || ((ip & 0xFFF00000) == 0xAC100000) || ((ip & 0xFFFF0000) == 0xC0A80000) || ( ip == 0x7F000001) ) { client->sess.countryCode = 246; } else { unsigned int ret = GeoIP_seek_record(gidb, ip); if (ret > 0) { client->sess.countryCode = ret; } else { client->sess.countryCode = 246; G_LogPrintf("GeoIP: This IP:%s cannot be located\n", realIP); } } } } else { client->sess.countryCode = 255; } // Nico, end of GeoIP // get and distribute relevent paramters G_LogPrintf("ClientConnect: %i\n", clientNum); G_UpdateCharacter(client); ClientUserinfoChanged(clientNum); // don't do the "xxx connected" messages if they were caried over from previous level // TAT 12/10/2002 - Don't display connected messages in single player if (firstTime) { trap_SendServerCommand(-1, va("cpm \"%s" S_COLOR_WHITE " connected\n\"", client->pers.netname)); } // count current clients and rank for scoreboard CalculateRanks(); return NULL; }
/** Called when a player begins connecting to the server. Called again for every map change or tournement restart. The session information will be valid after exit. Return NULL if the client should be allowed, otherwise return a string with the reason for denial. Otherwise, the client will be sent the current gamestate and will eventually get to ClientBegin. firstTime will be qtrue the very first time a client connects to the server machine, but qfalse on map changes and tournement restarts. */ char *ClientConnect(int clientNum, qboolean firstTime, qboolean isBot) { char name[MAX_NETNAME]; char *value, *nameError; gclient_t *client; char userinfo[MAX_INFO_STRING]; gentity_t *ent; ent = &g_entities[ clientNum ]; trap_GetUserinfo(clientNum, userinfo, sizeof(userinfo)); if (G_BanCheck(userinfo)) { return "You are banned from this server."; } value = Info_ValueForKey(userinfo, "ip"); nameError = Info_ValueForKey(userinfo, "name"); nameError = ClientCleanName(nameError, name, sizeof name); // we don't check password for bots and local client // NOTE: local client <-> "ip" "localhost" // this means this client is not running in our current process if (!isBot && strcmp(value, "localhost")) { // check for invalid player name if (nameError) { return nameError; } // check for a password value = Info_ValueForKey (userinfo, "password"); if (g_password.string[0] && Q_stricmp(g_password.string, "none") && strcmp(g_password.string, value) != 0) { return "Invalid password"; } } // if a player reconnects quickly after a disconnect, the client disconnect may never be called, thus flag can get lost in the ether if (ent->inuse) { G_LogPrintf("Forcing disconnect on active client: %i\n", clientNum); // so lets just fix up anything that should happen on a disconnect ClientDisconnect(clientNum); } // they can connect ent->client = level.clients + clientNum; client = ent->client; memset(client, 0, sizeof(*client)); client->pers.connected = CON_CONNECTING; // read or initialize the session data if (firstTime || level.newSession) { G_InitSessionData(client, userinfo); } G_ReadSessionData(client); if (isBot) { ent->r.svFlags |= SVF_BOT; ent->inuse = qtrue; if (!G_BotConnect(clientNum, !firstTime)) { return "BotConnectfailed"; } } // don't do the "xxx connected" messages if they were caried over from previous level if (firstTime) { ClientScreenPrint(NULL, "%s ^7connected", name); } // get and distribute relevent paramters G_LogPrintf("ClientConnect: %i\n", clientNum); ClientUserinfoChanged(clientNum); if (g_gametype.integer >= GT_TEAM && client->sess.sessionTeam != TEAM_SPECTATOR) { LogTeamChange(client, -1); } // count current clients and rank for scoreboard CalculateRanks(); return NULL; }