/** * Tibia GIMUD Server - a free and open-source MMORPG server emulator * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 2 of the License, or * (at your option) 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, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "otpch.h" #include "protocollogin.h" #include "outputmessage.h" #include "tasks.h" #include "configmanager.h" #include "iologindata.h" #include "ban.h" #include "game.h" extern ConfigManager g_config; extern Game g_game; void ProtocolLogin::disconnectClient(const std::string& message, uint16_t version) { auto output = OutputMessagePool::getOutputMessage(); output->addByte(version >= 1076 ? 0x0B : 0x0A); output->addString(message); send(output); disconnect(); } void ProtocolLogin::getCharacterList(uint32_t accountNumber, const std::string& password, uint16_t version) { Account account; if (!IOLoginData::loginserverAuthentication(accountNumber, password, account)) { disconnectClient("Accountnumber or password is not correct.", version); return; } auto output = OutputMessagePool::getOutputMessage(); //Update premium days Game::updatePremium(account); const std::string& motd = g_config.getString(ConfigManager::MOTD); if (!motd.empty()) { //Add MOTD output->addByte(0x14); std::ostringstream ss; ss << g_game.getMotdNum() << "\n" << motd; output->addString(ss.str()); } //Add char list output->addByte(0x64); uint8_t size = std::min(std::numeric_limits::max(), account.characters.size()); output->addByte(size); for (uint8_t i = 0; i < size; i++) { output->addString(account.characters[i]); output->addString(g_config.getString(ConfigManager::SERVER_NAME)); output->add(inet_addr(g_config.getString(ConfigManager::IP).c_str())); output->add(g_config.getNumber(ConfigManager::GAME_PORT)); } //Add premium days if (g_config.getBoolean(ConfigManager::FREE_PREMIUM)) { output->add(0xFFFF); } else { output->add(account.premiumDays); } send(output); disconnect(); } void ProtocolLogin::onRecvFirstMessage(NetworkMessage& msg) { if (g_game.getGameState() == GAME_STATE_SHUTDOWN) { disconnect(); return; } msg.skipBytes(2); // client OS uint16_t version = msg.get(); if (version >= 971) { msg.skipBytes(17); } else { msg.skipBytes(12); } /* * Skipped bytes: * 4 bytes: protocolVersion * 12 bytes: dat, spr, pic signatures (4 bytes each) * 1 byte: 0 */ if (version <= 760) { std::ostringstream ss; ss << "Only clients with protocol " << CLIENT_VERSION_STR << " allowed!"; disconnectClient(ss.str(), version); return; } if (!Protocol::RSA_decrypt(msg)) { disconnect(); return; } uint32_t key[4]; key[0] = msg.get(); key[1] = msg.get(); key[2] = msg.get(); key[3] = msg.get(); enableXTEAEncryption(); setXTEAKey(key); if (version < CLIENT_VERSION_MIN || version > CLIENT_VERSION_MAX) { std::ostringstream ss; ss << "Only clients with protocol " << CLIENT_VERSION_STR << " allowed!"; disconnectClient(ss.str(), version); return; } if (g_game.getGameState() == GAME_STATE_STARTUP) { disconnectClient("Gameworld is starting up. Please wait.", version); return; } if (g_game.getGameState() == GAME_STATE_MAINTAIN) { disconnectClient("Gameworld is under maintenance.\nPlease re-connect in a while.", version); return; } BanInfo banInfo; auto connection = getConnection(); if (!connection) { return; } if (IOBan::isIpBanned(connection->getIP(), banInfo)) { if (banInfo.reason.empty()) { banInfo.reason = "(none)"; } std::ostringstream ss; ss << "Your IP has been banned until " << formatDateShort(banInfo.expiresAt) << " by " << banInfo.bannedBy << ".\n\nReason specified:\n" << banInfo.reason; disconnectClient(ss.str(), version); return; } uint32_t accountNumber = msg.get(); if (!accountNumber) { disconnectClient("Invalid account number.", version); return; } std::string password = msg.getString(); if (password.empty()) { disconnectClient("Invalid password.", version); return; } auto thisPtr = std::static_pointer_cast(shared_from_this()); g_dispatcher.addTask(createTask(std::bind(&ProtocolLogin::getCharacterList, thisPtr, accountNumber, password, version))); }