/* * Ascent MMORPG Server * Copyright (C) 2005-2007 Ascent Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "LogonStdAfx.h" initialiseSingleton(AccountMgr); initialiseSingleton(IPBanner); initialiseSingleton(InformationCore); void AccountMgr::ReloadAccounts(bool silent) { setBusy.Acquire(); if(!silent) sLog.outString("[AccountMgr] Reloading Accounts..."); // Load *all* accounts. QueryResult * result = sLogonSQL->Query("SELECT acct, login, password, gm, flags, banned FROM accounts"); Field * field; string AccountName; set account_list; Account * acct; if(result) { do { field = result->Fetch(); AccountName = field[1].GetString(); // transform to uppercase transform(AccountName.begin(), AccountName.end(), AccountName.begin(), towupper); //Use private __GetAccount, for locks acct = __GetAccount(AccountName); if(acct == 0) { // New account. AddAccount(field); } else { // Update the account with possible changed details. UpdateAccount(acct, field); } // add to our "known" list account_list.insert(AccountName); } while(result->NextRow()); delete result; } // check for any purged/deleted accounts #ifdef WIN32 HM_NAMESPACE::hash_map::iterator itr = AccountDatabase.begin(); HM_NAMESPACE::hash_map::iterator it2; #else std::map::iterator itr = AccountDatabase.begin(); std::map::iterator it2; #endif for(; itr != AccountDatabase.end();) { it2 = itr; ++itr; if(account_list.find(it2->first) == account_list.end()) AccountDatabase.erase(it2); } if(!silent) sLog.outString("[AccountMgr] Found %u accounts.", AccountDatabase.size()); setBusy.Release(); IPBanner::getSingleton().Reload(); } void AccountMgr::AddAccount(Field* field) { Account acct; Sha1Hash hash; acct.AccountId = field[0].GetUInt32(); acct.Username = field[1].GetString(); acct.Password = field[2].GetString(); acct.GMFlags = field[3].GetString(); acct.AccountFlags = field[4].GetUInt32(); acct.Banned = field[5].GetUInt32(); // Convert username/password to uppercase. this is needed ;) transform(acct.Username.begin(), acct.Username.end(), acct.Username.begin(), towupper); transform(acct.Password.begin(), acct.Password.end(), acct.Password.begin(), towupper); // Prehash the I value. hash.UpdateData((acct.Username + ":" + acct.Password)); hash.Finalize(); memcpy(acct.SrpHash, hash.GetDigest(), 20); AccountDatabase[acct.Username] = acct; } void AccountMgr::UpdateAccount(Account * acct, Field * field) { uint32 id = field[0].GetUInt32(); if(id != acct->AccountId) { //printf("Account %u `%s` is a duplicate.\n", id, acct->Username.c_str()); sLog.outColor(TYELLOW, " >> deleting duplicate account %u [%s]...", id, acct->Username.c_str()); sLog.outColor(TNORMAL, "\n"); sLogonSQL->Execute("DELETE FROM accounts WHERE acct=%u", id); return; } acct->AccountId = field[0].GetUInt32(); acct->Username = field[1].GetString(); acct->Password = field[2].GetString(); acct->GMFlags = field[3].GetString(); acct->AccountFlags = field[4].GetUInt32(); acct->Banned = field[5].GetUInt32(); // Convert username/password to uppercase. this is needed ;) transform(acct->Username.begin(), acct->Username.end(), acct->Username.begin(), towupper); transform(acct->Password.begin(), acct->Password.end(), acct->Password.begin(), towupper); Sha1Hash hash; hash.UpdateData((acct->Username + ":" + acct->Password)); hash.Finalize(); memcpy(acct->SrpHash, hash.GetDigest(), 20); } bool AccountMgr::LoadAccount(string Name) { QueryResult * result = sLogonSQL->Query("SELECT acct, login, password, gm, flags, banned FROM account_database WHERE login='%s'", Name.c_str()); if(result == 0) return false; AddAccount(result->Fetch()); delete result; return true; } void AccountMgr::ReloadAccountsCallback() { ReloadAccounts(true); } BAN_STATUS IPBanner::CalculateBanStatus(in_addr ip_address) { setBusy.Acquire(); uint8 b1 = ((uint8*)&ip_address)[0]; uint8 b2 = ((uint8*)&ip_address)[1]; uint8 b3 = ((uint8*)&ip_address)[2]; uint8 b4 = ((uint8*)&ip_address)[3]; // loop storage array set::iterator itr = banList.begin(); uint32 expiretime; bool banned = false; for(; itr != banList.end(); ++itr) { // compare first byte if((*itr)->ip.full.b1 == b1 || (*itr)->ip.full.b1 == 0xFF) { // compare second byte if there was a first match if((*itr)->ip.full.b2 == b2 || (*itr)->ip.full.b2 == 0xFF) { // compare third byte if there was a second match if((*itr)->ip.full.b3 == b3 || (*itr)->ip.full.b3 == 0xFF) { // compare last byte if there was a third match if((*itr)->ip.full.b4 == b4 || (*itr)->ip.full.b4 == 0xFF) { // full IP match banned = true; expiretime = (*itr)->ban_expire_time; break; } } } } } //Release here because we're not touching stored stuff anymore //-except in Remove, which acquires the lock itself (avoiding deadlocks :p) setBusy.Release(); // calculate status if(!banned) { sLog.outDebug("[IPBanner] IP has no ban entry"); return BAN_STATUS_NOT_BANNED; } if (expiretime == 0) { sLog.outDebug("[IPBanner] IP permanently banned"); return BAN_STATUS_PERMANENT_BAN; } time_t rawtime; time( &rawtime ); if(expiretime > (uint32)rawtime) { // temporary ban. time_t expire_time = expiretime; sLog.outDebug("[IPBanner] IP temporary banned, Expires: %s", ctime( &expire_time )); return BAN_STATUS_TIME_LEFT_ON_BAN; } if(expiretime <= (uint32)rawtime) { // ban has expired. erase it from the banlist and database sLog.outDebug("[IPBanner] Expired IP temporary ban has been removed"); Remove(itr); return BAN_STATUS_NOT_BANNED; } // shouldnt get this far, but just in case... sLog.outDebug("[IPBanner] Unknown IP ban state/duration, enforcing anyway"); return BAN_STATUS_PERMANENT_BAN; } void IPBanner::Load() { QueryResult * result = sLogonSQL->Query("SELECT ip, expire FROM ipbans"); Field * fields; IPBan * ban; const char * ip_str; if(result) { do { ban = new IPBan; fields = result->Fetch(); ip_str = fields[0].GetString(); unsigned int b1, b2, b3, b4; if(sscanf(ip_str, "%u.%u.%u.%u", &b1, &b2, &b3, &b4) != 4) { delete ban; continue; } ban->ip.full.b1 = b1; ban->ip.full.b2 = b2; ban->ip.full.b3 = b3; ban->ip.full.b4 = b4; ban->ban_expire_time = fields[1].GetUInt32(); banList.insert( ban ); } while(result->NextRow()); delete result; } } void IPBanner::Reload() { setBusy.Acquire(); banList.clear(); Load(); setBusy.Release(); } void IPBanner::Remove(set::iterator ban) { setBusy.Acquire(); char strIp[16] = {0}; snprintf(strIp, 16, "%u.%u.%u.%u", (*ban)->ip.full.b1, (*ban)->ip.full.b2, (*ban)->ip.full.b3, (*ban)->ip.full.b4 ); sLogonSQL->Execute("DELETE FROM ipbans WHERE ip='%s'", strIp); banList.erase(ban); setBusy.Release(); sLog.outDebug("[IPBanner] Removed expired IPBan for ip '%s'", strIp); } Realm * InformationCore::AddRealm(uint32 realm_id, Realm * rlm) { realmLock.Acquire(); m_realms.insert( make_pair( realm_id, rlm ) ); map::iterator itr = m_realms.find(realm_id); realmLock.Release(); return rlm; } Realm * InformationCore::GetRealm(uint32 realm_id) { Realm * ret = 0; realmLock.Acquire(); map::iterator itr = m_realms.find(realm_id); if(itr != m_realms.end()) { ret = itr->second; } realmLock.Release(); return ret; } void InformationCore::RemoveRealm(uint32 realm_id) { realmLock.Acquire(); map::iterator itr = m_realms.find(realm_id); if(itr != m_realms.end()) { sLog.outString("Removing realm `%s` (%u) due to socket close.", itr->second->Name.c_str(), realm_id); delete itr->second; m_realms.erase(itr); } realmLock.Release(); } void InformationCore::SendRealms(AuthSocket * Socket) { realmLock.Acquire(); // packet header ByteBuffer data(m_realms.size() * 150 + 20); data << uint8(0x10); data << uint16(0); // Size Placeholder // dunno what this is.. data << uint32(0); //sAuthLogonChallenge_C * client = Socket->GetChallenge(); data << uint16(m_realms.size()); // loop realms :/ map::iterator itr = m_realms.begin(); HM_NAMESPACE::hash_map::iterator it; for(; itr != m_realms.end(); ++itr) { data << uint8(itr->second->Colour); data << uint8(0); // Locked Flag data << uint8(itr->second->Icon); // This part is the same for all. data << itr->second->Name; data << itr->second->Address; data << itr->second->Population; /* Get our character count */ it = itr->second->CharacterMap.find(Socket->GetAccountID()); data << uint8( (it == itr->second->CharacterMap.end()) ? 0 : it->second ); data << uint8(itr->second->TimeZone); // time zone data << uint8(0); } realmLock.Release(); data << uint8(0x15); data << uint8(0); // Re-calculate size. #ifdef USING_BIG_ENDIAN *(uint16*)&data.contents()[1] = swap16(uint16(data.size() - 3)); #else *(uint16*)&data.contents()[1] = data.size() - 3; #endif // Send to the socket. Socket->Send((const uint8*)data.contents(), data.size()); } BigNumber * InformationCore::GetSessionKey(uint32 account_id) { m_sessionKeyLock.Acquire(); BigNumber * bn = 0; map::iterator itr = m_sessionkeys.find(account_id); if(itr != m_sessionkeys.end()) { bn = itr->second; } m_sessionKeyLock.Release(); return bn; } void InformationCore::DeleteSessionKey(uint32 account_id) { m_sessionKeyLock.Acquire(); map::iterator itr = m_sessionkeys.find(account_id); if(itr != m_sessionkeys.end()) { delete itr->second; m_sessionkeys.erase(itr); } m_sessionKeyLock.Release(); } void InformationCore::SetSessionKey(uint32 account_id, BigNumber * key) { m_sessionKeyLock.Acquire(); m_sessionkeys[account_id] = key; m_sessionKeyLock.Release(); } void InformationCore::TimeoutSockets() { if(!usepings) return; /* burlex: this is vulnerable to race conditions, adding a mutex to it. */ serverSocketLock.Acquire(); uint32 t = time(NULL); // check the ping time set::iterator itr, it2; LogonCommServerSocket * s; for(itr = m_serverSockets.begin(); itr != m_serverSockets.end();) { s = *itr; it2 = itr; ++itr; if(s->last_ping < t && ((t - s->last_ping) > 60)) { // ping timeout printf("Closing socket due to ping timeout.\n"); s->removed = true; set::iterator itr = s->server_ids.begin(); for(; itr != s->server_ids.end(); ++itr) RemoveRealm(*itr); m_serverSockets.erase(it2); s->Disconnect(); } } serverSocketLock.Release(); }