bool Connection::send(OutputMessage_ptr msg) { m_connectionLock.lock(); if(m_closeState == CLOSE_STATE_CLOSING || m_writeError) { m_connectionLock.unlock(); return false; } msg->getProtocol()->onSendMessage(msg); if(m_pendingWrite == 0) { #ifdef __DEBUG_NET_DETAIL__ std::clog << "Connection::send " << msg->getMessageLength() << std::endl; #endif internalSend(msg); } else { #ifdef __DEBUG_NET__ std::clog << "Connection::send Adding to queue " << msg->getMessageLength() << std::endl; #endif m_outputQueue.push_back(msg); } m_connectionLock.unlock(); return true; }
void Connection::internalSend(OutputMessage_ptr msg) { m_pendingWrite++; boost::asio::async_write(m_socket, boost::asio::buffer(msg->getOutputBuffer(), msg->getMessageLength()), boost::bind(&Connection::onWriteOperation, this, msg, boost::asio::placeholders::error)); }
void OutputMessagePool::sendAll() { boost::recursive_mutex::scoped_lock lockClass(m_outputPoolLock); OutputMessageMessageList::iterator it; for(it = m_toAddQueue.begin(); it != m_toAddQueue.end();) { //drop messages that are older than 10 seconds if(OTSYS_TIME() - (*it)->getFrame() > 10 * 1000) { (*it)->getProtocol()->onSendMessage(*it); it = m_toAddQueue.erase(it); continue; } (*it)->setState(OutputMessage::STATE_ALLOCATED); m_autoSendOutputMessages.push_back(*it); ++it; } m_toAddQueue.clear(); for(it = m_autoSendOutputMessages.begin(); it != m_autoSendOutputMessages.end();) { OutputMessage_ptr omsg = *it; #ifdef __NO_PLAYER_SENDBUFFER__ //use this define only for debugging bool v = 1; #else //It will send only messages bigger then 1 kb or with a lifetime greater than 10 ms bool v = omsg->getMessageLength() > 1024 || (m_frameTime - omsg->getFrame() > 10); #endif if(v) { #ifdef __DEBUG_NET_DETAIL__ std::cout << "Sending message - ALL" << std::endl; #endif if(omsg->getConnection()) { if(!omsg->getConnection()->send(omsg)) { // Send only fails when connection is closing (or in error state) // This call will free the message omsg->getProtocol()->onSendMessage(omsg); } } else { #ifdef __DEBUG_NET__ std::cout << "Error: [OutputMessagePool::send] NULL connection." << std::endl; #endif } it = m_autoSendOutputMessages.erase(it); } else ++it; } }
void OutputMessagePool::sendAll() { OTSYS_THREAD_LOCK_CLASS lockClass(m_outputPoolLock); OutputMessageList::iterator it; for(it = m_toAddQueue.begin(); it != m_toAddQueue.end();) { //drop messages that are older than 10 seconds if(OTSYS_TIME() - (*it)->getFrame() > 10000) { if((*it)->getProtocol()) (*it)->getProtocol()->onSendMessage(*it); it = m_toAddQueue.erase(it); continue; } (*it)->setState(OutputMessage::STATE_ALLOCATED); m_autoSendOutputMessages.push_back(*it); ++it; } m_toAddQueue.clear(); for(it = m_autoSendOutputMessages.begin(); it != m_autoSendOutputMessages.end(); ) { OutputMessage_ptr omsg = (*it); #ifdef __NO_PLAYER_SENDBUFFER__ //use this define only for debugging if(true) #else //It will send only messages bigger then 1 kb or with a lifetime greater than 10 ms if(omsg->getMessageLength() > 1024 || (m_frameTime - omsg->getFrame() > 10)) #endif { #ifdef __DEBUG_NET_DETAIL__ std::cout << "Sending message - ALL" << std::endl; #endif if(omsg->getConnection()) { if(!omsg->getConnection()->send(omsg) && omsg->getProtocol()) omsg->getProtocol()->onSendMessage(omsg); } #ifdef __DEBUG_NET__ else std::cout << "Error: [OutputMessagePool::send] NULL connection." << std::endl; #endif it = m_autoSendOutputMessages.erase(it); } else ++it; } }
bool Connection::send(OutputMessage_ptr msg) { #ifdef __DEBUG_NET_DETAIL__ std::cout << "Connection::send init" << std::endl; #endif m_connectionLock.lock(); if(m_connectionState != CONNECTION_STATE_OPEN || m_writeError){ m_connectionLock.unlock(); return false; } if(m_pendingWrite == 0){ msg->getProtocol()->onSendMessage(msg); TRACK_MESSAGE(msg); #ifdef __DEBUG_NET_DETAIL__ std::cout << "Connection::send " << msg->getMessageLength() << std::endl; #endif internalSend(msg); } else{ #ifdef __DEBUG_NET__ std::cout << "Connection::send Adding to queue " << msg->getMessageLength() << std::endl; #endif TRACK_MESSAGE(msg); OutputMessagePool* outputPool = OutputMessagePool::getInstance(); outputPool->addToAutoSend(msg); } m_connectionLock.unlock(); return true; }
void Connection::onWriteOperation(OutputMessage_ptr msg, const boost::system::error_code& error) { #ifdef __DEBUG_NET_DETAIL__ std::clog << "onWriteOperation" << std::endl; #endif msg.reset(); m_connectionLock.lock(); if(!error) { if(m_pendingWrite > 0) { if(!m_outputQueue.empty()) { OutputMessage_ptr msg = m_outputQueue.front(); m_outputQueue.pop_front(); internalSend(msg); #ifdef __DEBUG_NET_DETAIL__ std::clog << "Connection::onWriteOperation send " << msg->getMessageLength() << std::endl; #endif } m_pendingWrite--; } else { std::clog << "Error: [Connection::onWriteOperation] Getting unexpected notification!" << std::endl; // Error. Pending operations counter is 0, but we are getting a // notification!! } } else { m_pendingWrite--; handleWriteError(error); } if(m_closeState == CLOSE_STATE_CLOSING) { if(!closingConnection()) m_connectionLock.lock(); return; } m_connectionLock.unlock(); }
void Connection::internalSend(OutputMessage_ptr msg) { try { ++m_pendingWrite; m_writeTimer.expires_from_now(boost::posix_time::seconds(Connection::write_timeout)); m_writeTimer.async_wait( std::bind(&Connection::handleWriteTimeout, std::weak_ptr<Connection>(shared_from_this()), std::placeholders::_1)); boost::asio::async_write(getHandle(), boost::asio::buffer(msg->getOutputBuffer(), msg->getMessageLength()), std::bind(&Connection::onWriteOperation, shared_from_this(), msg, std::placeholders::_1)); } catch (boost::system::system_error& e) { if (m_logError) { std::cout << "[Network error - Connection::internalSend] " << e.what() << std::endl; m_logError = false; } } }
void Connection::internalSend(OutputMessage_ptr msg) { try { ++m_pendingWrite; m_writeTimer.expires_from_now(boost::posix_time::seconds(Connection::write_timeout)); 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->getMessageLength()), boost::bind(&Connection::onWriteOperation, shared_from_this(), msg, boost::asio::placeholders::error)); } catch (boost::system::system_error& e) { if (m_logError) { LOG_MESSAGE("NETWORK", LOGTYPE_ERROR, 1, e.what()); m_logError = false; } } }
void ProtocolAdmin::parsePacket(NetworkMessage& msg) { if (g_game.getGameState() == GAME_STATE_SHUTDOWN) { getConnection()->closeConnection(); return; } uint8_t recvbyte = msg.GetByte(); OutputMessagePool* outputPool = OutputMessagePool::getInstance(); OutputMessage_ptr output = outputPool->getOutputMessage(this, false); if (!output) { return; } switch (m_state) { case ENCRYPTION_NO_SET: { if (g_adminConfig->requireEncryption()) { if ((time(NULL) - m_startTime) > 30000) { getConnection()->closeConnection(); addLogLine(this, LOGTYPE_WARNING, 1, "encryption timeout"); return; } if (recvbyte != AP_MSG_ENCRYPTION && recvbyte != AP_MSG_KEY_EXCHANGE) { output->AddByte(AP_MSG_ERROR); output->AddString("encryption needed"); outputPool->send(output); getConnection()->closeConnection(); addLogLine(this, LOGTYPE_WARNING, 1, "wrong command while ENCRYPTION_NO_SET"); return; } break; } else { m_state = NO_LOGGED_IN; } } case NO_LOGGED_IN: { if (g_adminConfig->requireLogin()) { if ((time(NULL) - m_startTime) > 30000) { //login timeout getConnection()->closeConnection(); addLogLine(this, LOGTYPE_WARNING, 1, "login timeout"); return; } if (m_loginTries > 3) { output->AddByte(AP_MSG_ERROR); output->AddString("too many login tries"); outputPool->send(output); getConnection()->closeConnection(); addLogLine(this, LOGTYPE_WARNING, 1, "too many login tries"); return; } if (recvbyte != AP_MSG_LOGIN) { output->AddByte(AP_MSG_ERROR); output->AddString("you are not logged in"); outputPool->send(output); getConnection()->closeConnection(); addLogLine(this, LOGTYPE_WARNING, 1, "wrong command while NO_LOGGED_IN"); return; } break; } else { m_state = LOGGED_IN; } } case LOGGED_IN: { //can execute commands break; } default: { getConnection()->closeConnection(); return; } } m_lastCommand = time(NULL); switch (recvbyte) { case AP_MSG_LOGIN: { if (m_state == NO_LOGGED_IN && g_adminConfig->requireLogin()) { std::string password = msg.GetString(); if (g_adminConfig->passwordMatch(password)) { m_state = LOGGED_IN; output->AddByte(AP_MSG_LOGIN_OK); addLogLine(this, LOGTYPE_EVENT, 1, "login ok"); } else { m_loginTries++; output->AddByte(AP_MSG_LOGIN_FAILED); output->AddString("wrong password"); addLogLine(this, LOGTYPE_WARNING, 1, "login failed.(" + password + ")"); } } else { output->AddByte(AP_MSG_LOGIN_FAILED); output->AddString("can not login"); addLogLine(this, LOGTYPE_WARNING, 1, "wrong state at login"); } break; } case AP_MSG_ENCRYPTION: { if (m_state == ENCRYPTION_NO_SET && g_adminConfig->requireEncryption()) { uint8_t keyType = msg.GetByte(); if (keyType == ENCRYPTION_RSA1024XTEA) { RSA* rsa = g_adminConfig->getRSAKey(ENCRYPTION_RSA1024XTEA); if (!rsa) { output->AddByte(AP_MSG_ENCRYPTION_FAILED); addLogLine(this, LOGTYPE_WARNING, 1, "no valid server key type"); break; } if (RSA_decrypt(rsa, msg)) { m_state = NO_LOGGED_IN; uint32_t k[4]; k[0] = msg.GetU32(); k[1] = msg.GetU32(); k[2] = msg.GetU32(); k[3] = msg.GetU32(); //use for in/out the new key we have enableXTEAEncryption(); setXTEAKey(k); output->AddByte(AP_MSG_ENCRYPTION_OK); addLogLine(this, LOGTYPE_EVENT, 1, "encryption ok"); } else { output->AddByte(AP_MSG_ENCRYPTION_FAILED); output->AddString("wrong encrypted packet"); addLogLine(this, LOGTYPE_WARNING, 1, "wrong encrypted packet"); } } else { output->AddByte(AP_MSG_ENCRYPTION_FAILED); output->AddString("no valid key type"); addLogLine(this, LOGTYPE_WARNING, 1, "no valid client key type"); } } else { output->AddByte(AP_MSG_ENCRYPTION_FAILED); output->AddString("can not set encryption"); addLogLine(this, LOGTYPE_EVENT, 1, "can not set encryption"); } break; } case AP_MSG_KEY_EXCHANGE: { if (m_state == ENCRYPTION_NO_SET && g_adminConfig->requireEncryption()) { uint8_t keyType = msg.GetByte(); if (keyType == ENCRYPTION_RSA1024XTEA) { RSA* rsa = g_adminConfig->getRSAKey(ENCRYPTION_RSA1024XTEA); if (!rsa) { output->AddByte(AP_MSG_KEY_EXCHANGE_FAILED); addLogLine(this, LOGTYPE_WARNING, 1, "no valid server key type"); break; } output->AddByte(AP_MSG_KEY_EXCHANGE_OK); output->AddByte(ENCRYPTION_RSA1024XTEA); char RSAPublicKey[128]; rsa->getPublicKey(RSAPublicKey); output->AddBytes(RSAPublicKey, 128); } else { output->AddByte(AP_MSG_KEY_EXCHANGE_FAILED); addLogLine(this, LOGTYPE_WARNING, 1, "no valid client key type"); } } else { output->AddByte(AP_MSG_KEY_EXCHANGE_FAILED); output->AddString("can not get public key"); addLogLine(this, LOGTYPE_WARNING, 1, "can not get public key"); } break; } case AP_MSG_COMMAND: { if (m_state != LOGGED_IN) { addLogLine(this, LOGTYPE_ERROR, 1, "recvbyte == AP_MSG_COMMAND && m_state != LOGGED_IN !!!"); // We should never reach this point break; } uint8_t command = msg.GetByte(); switch (command) { case CMD_BROADCAST: { const std::string message = msg.GetString(); addLogLine(this, LOGTYPE_EVENT, 1, "broadcast: " + message); g_dispatcher.addTask(createTask(boost::bind(&Game::broadcastMessage, &g_game, message, MSG_STATUS_WARNING))); output->AddByte(AP_MSG_COMMAND_OK); break; } case CMD_CLOSE_SERVER: { g_dispatcher.addTask(createTask(boost::bind(&ProtocolAdmin::adminCommandCloseServer, this))); break; } case CMD_PAY_HOUSES: { g_dispatcher.addTask(createTask(boost::bind(&ProtocolAdmin::adminCommandPayHouses, this))); break; } case CMD_OPEN_SERVER: { g_dispatcher.addTask(createTask(boost::bind(&ProtocolAdmin::adminCommandOpenServer, this))); break; } case CMD_SHUTDOWN_SERVER: { g_dispatcher.addTask(createTask(boost::bind(&ProtocolAdmin::adminCommandShutdownServer, this))); getConnection()->closeConnection(); return; } case CMD_KICK: { const std::string name = msg.GetString(); g_dispatcher.addTask(createTask(boost::bind(&ProtocolAdmin::adminCommandKickPlayer, this, name))); break; } case CMD_SETOWNER: { const std::string param = msg.GetString(); g_dispatcher.addTask(createTask(boost::bind(&ProtocolAdmin::adminCommandSetOwner, this, param))); break; } default: { output->AddByte(AP_MSG_COMMAND_FAILED); output->AddString("not known server command"); addLogLine(this, LOGTYPE_WARNING, 1, "not known server command"); break; } } break; } case AP_MSG_PING: { output->AddByte(AP_MSG_PING_OK); break; } default: { output->AddByte(AP_MSG_ERROR); output->AddString("not known command byte"); addLogLine(this, LOGTYPE_WARNING, 1, "not known command byte"); break; } } if (output->getMessageLength() > 0) { outputPool->send(output); } }