bool Connection::send(OutputMessage_ptr msg) { #ifdef __DEBUG_NET_DETAIL__ std::clog << "Connection::send init" << std::endl; #endif m_connectionLock.lock(); if(m_connectionState != CONNECTION_STATE_OPEN || m_writeError) { m_connectionLock.unlock(); return false; } TRACK_MESSAGE(msg); if(!m_pendingWrite) { if(msg->getProtocol()) msg->getProtocol()->onSendMessage(msg); #ifdef __DEBUG_NET_DETAIL__ std::clog << "Connection::send " << msg->size() << std::endl; #endif internalSend(msg); } else if(m_pendingWrite > 100 && g_config.getBool(ConfigManager::FORCE_CLOSE_SLOW_CONNECTION)) { std::clog << "NOTICE: Forcing slow connection to disconnect!" << std::endl; close(); } else { #ifdef __DEBUG_NET__ std::clog << "Connection::send Adding to queue " << msg->size() << std::endl; #endif OutputMessagePool::getInstance()->autoSend(msg); } m_connectionLock.unlock(); return true; }
void Connection::internalSend(OutputMessage_ptr msg) { TRACK_MESSAGE(msg); try { ++m_pendingWrite; m_writeTimer.expires_from_now(boost::posix_time::seconds(Connection::writeTimeout)); m_writeTimer.async_wait(boost::bind(&Connection::handleWriteTimeout, boost::weak_ptr<Connection>(shared_from_this()), boost::asio::placeholders::error)); boost::asio::async_write(getHandle(), boost::asio::buffer(msg->getOutputBuffer(), msg->size()), boost::bind(&Connection::onWrite, shared_from_this(), msg, boost::asio::placeholders::error)); } catch(boost::system::system_error& e) { if(m_logError) { LOG_MESSAGE(LOGTYPE_ERROR, e.what(), "NETWORK"); m_logError = false; } } }
void ProtocolAdmin::parsePacket(NetworkMessage& msg) { if(g_game.getGameState() == GAMESTATE_SHUTDOWN) { getConnection()->close(); return; } uint8_t recvbyte = msg.get<char>(); OutputMessage_ptr output = OutputMessagePool::getInstance()->getOutputMessage(this, false); if(!output) return; TRACK_MESSAGE(output); switch(m_state) { case ENCRYPTION_NO_SET: { if(Admin::getInstance()->isEncypted()) { if((time(NULL) - m_startTime) > 30000) { getConnection()->close(); addLogLine(LOGTYPE_EVENT, "encryption timeout"); return; } if(recvbyte != AP_MSG_ENCRYPTION && recvbyte != AP_MSG_KEY_EXCHANGE) { output->put<char>(AP_MSG_ERROR); output->putString("encryption needed"); OutputMessagePool::getInstance()->send(output); getConnection()->close(); addLogLine(LOGTYPE_EVENT, "wrong command while ENCRYPTION_NO_SET"); return; } } else m_state = NO_LOGGED_IN; break; } case NO_LOGGED_IN: { if(g_config.getBool(ConfigManager::ADMIN_REQUIRE_LOGIN)) { if((time(NULL) - m_startTime) > 30000) { //login timeout getConnection()->close(); addLogLine(LOGTYPE_EVENT, "login timeout"); return; } if(m_loginTries > 3) { output->put<char>(AP_MSG_ERROR); output->putString("too many login tries"); OutputMessagePool::getInstance()->send(output); getConnection()->close(); addLogLine(LOGTYPE_EVENT, "too many login tries"); return; } if(recvbyte != AP_MSG_LOGIN) { output->put<char>(AP_MSG_ERROR); output->putString("you are not logged in"); OutputMessagePool::getInstance()->send(output); getConnection()->close(); addLogLine(LOGTYPE_EVENT, "wrong command while NO_LOGGED_IN"); return; } } else m_state = LOGGED_IN; break; } case LOGGED_IN: break; default: { getConnection()->close(); addLogLine(LOGTYPE_EVENT, "no valid connection state!!!"); return; } } m_lastCommand = time(NULL); switch(recvbyte) { case AP_MSG_LOGIN: { if(m_state == NO_LOGGED_IN && g_config.getBool(ConfigManager::ADMIN_REQUIRE_LOGIN)) { std::string pass = msg.getString(), word = g_config.getString(ConfigManager::ADMIN_PASSWORD); _encrypt(word, false); if(pass == word) { m_state = LOGGED_IN; output->put<char>(AP_MSG_LOGIN_OK); addLogLine(LOGTYPE_EVENT, "login ok"); } else { m_loginTries++; output->put<char>(AP_MSG_LOGIN_FAILED); output->putString("wrong password"); addLogLine(LOGTYPE_EVENT, "login failed.("+ pass + ")"); } } else { output->put<char>(AP_MSG_LOGIN_FAILED); output->putString("cannot login"); addLogLine(LOGTYPE_EVENT, "wrong state at login"); } break; } case AP_MSG_ENCRYPTION: { if(m_state == ENCRYPTION_NO_SET && Admin::getInstance()->isEncypted()) { uint8_t keyType = msg.get<char>(); switch(keyType) { case ENCRYPTION_RSA1024XTEA: { RSA* rsa = Admin::getInstance()->getRSAKey(ENCRYPTION_RSA1024XTEA); if(!rsa) { output->put<char>(AP_MSG_ENCRYPTION_FAILED); addLogLine(LOGTYPE_EVENT, "no valid server key type"); break; } if(RSA_decrypt(rsa, msg)) { m_state = NO_LOGGED_IN; uint32_t k[4]= {msg.get<uint32_t>(), msg.get<uint32_t>(), msg.get<uint32_t>(), msg.get<uint32_t>()}; //use for in/out the new key we have enableXTEAEncryption(); setXTEAKey(k); output->put<char>(AP_MSG_ENCRYPTION_OK); addLogLine(LOGTYPE_EVENT, "encryption ok"); } else { output->put<char>(AP_MSG_ENCRYPTION_FAILED); output->putString("wrong encrypted packet"); addLogLine(LOGTYPE_EVENT, "wrong encrypted packet"); } break; } default: { output->put<char>(AP_MSG_ENCRYPTION_FAILED); output->putString("no valid key type"); addLogLine(LOGTYPE_EVENT, "no valid client key type"); break; } } } else { output->put<char>(AP_MSG_ENCRYPTION_FAILED); output->putString("cannot set encryption"); addLogLine(LOGTYPE_EVENT, "cannot set encryption"); } break; } case AP_MSG_KEY_EXCHANGE: { if(m_state == ENCRYPTION_NO_SET && Admin::getInstance()->isEncypted()) { uint8_t keyType = msg.get<char>(); switch(keyType) { case ENCRYPTION_RSA1024XTEA: { RSA* rsa = Admin::getInstance()->getRSAKey(ENCRYPTION_RSA1024XTEA); if(!rsa) { output->put<char>(AP_MSG_KEY_EXCHANGE_FAILED); addLogLine(LOGTYPE_EVENT, "no valid server key type"); break; } output->put<char>(AP_MSG_KEY_EXCHANGE_OK); output->put<char>(ENCRYPTION_RSA1024XTEA); char RSAPublicKey[128]; rsa->getPublicKey(RSAPublicKey); output->put<char>s(RSAPublicKey, 128); break; } default: { output->put<char>(AP_MSG_KEY_EXCHANGE_FAILED); addLogLine(LOGTYPE_EVENT, "no valid client key type"); break; } } } else { output->put<char>(AP_MSG_KEY_EXCHANGE_FAILED); output->putString("cannot get public key"); addLogLine(LOGTYPE_EVENT, "cannot get public key"); } break; } case AP_MSG_COMMAND: { if(m_state != LOGGED_IN) { addLogLine(LOGTYPE_EVENT, "recvbyte == AP_MSG_COMMAND && m_state != LOGGED_IN !!!"); break; } uint8_t command = msg.get<char>(); switch(command) { case CMD_SAVE_SERVER: case CMD_SHALLOW_SAVE_SERVER: { uint8_t flags = (uint8_t)SAVE_PLAYERS | (uint8_t)SAVE_MAP | (uint8_t)SAVE_STATE; if(command == CMD_SHALLOW_SAVE_SERVER) flags |= SAVE_PLAYERS_SHALLOW; addLogLine(LOGTYPE_EVENT, "saving server"); Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::saveGameState, &g_game, flags))); output->put<char>(AP_MSG_COMMAND_OK); break; } case CMD_CLOSE_SERVER: { addLogLine(LOGTYPE_EVENT, "closing server"); Dispatcher::getInstance().addTask(createTask(boost::bind( &Game::setGameState, &g_game, GAMESTATE_CLOSED))); output->put<char>(AP_MSG_COMMAND_OK); break; } case CMD_OPEN_SERVER: { addLogLine(LOGTYPE_EVENT, "opening server"); g_game.setGameState(GAMESTATE_NORMAL); output->put<char>(AP_MSG_COMMAND_OK); break; } case CMD_SHUTDOWN_SERVER: { addLogLine(LOGTYPE_EVENT, "shutting down server"); Dispatcher::getInstance().addTask(createTask(boost::bind( &Game::setGameState, &g_game, GAMESTATE_SHUTDOWN))); output->put<char>(AP_MSG_COMMAND_OK); break; } case CMD_PAY_HOUSES: { Dispatcher::getInstance().addTask(createTask(boost::bind( &ProtocolAdmin::adminCommandPayHouses, this))); break; } case CMD_RELOAD_SCRIPTS: { const int8_t reload = msg.get<char>(); Dispatcher::getInstance().addTask(createTask(boost::bind( &ProtocolAdmin::adminCommandReload, this, reload))); break; } case CMD_KICK: { const std::string param = msg.getString(); Dispatcher::getInstance().addTask(createTask(boost::bind( &ProtocolAdmin::adminCommandKickPlayer, this, param))); break; } case CMD_SEND_MAIL: { const std::string xmlData = msg.getString(); Dispatcher::getInstance().addTask(createTask(boost::bind( &ProtocolAdmin::adminCommandSendMail, this, xmlData))); break; } case CMD_BROADCAST: { const std::string param = msg.getString(); addLogLine(LOGTYPE_EVENT, "broadcasting: " + param); Dispatcher::getInstance().addTask(createTask(boost::bind( &Game::broadcastMessage, &g_game, param, MSG_STATUS_WARNING))); output->put<char>(AP_MSG_COMMAND_OK); break; } default: { output->put<char>(AP_MSG_COMMAND_FAILED); output->putString("not known server command"); addLogLine(LOGTYPE_EVENT, "not known server command"); } } break; } case AP_MSG_PING: output->put<char>(AP_MSG_PING_OK); break; case AP_MSG_KEEP_ALIVE: break; default: { output->put<char>(AP_MSG_ERROR); output->putString("not known command byte"); addLogLine(LOGTYPE_EVENT, "not known command byte"); break; } } if(output->size() > 0) OutputMessagePool::getInstance()->send(output); }