Beispiel #1
0
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);
	}
}
void ProtocolStatus::sendStatusString()
{
	OutputMessage_ptr output = OutputMessagePool::getInstance()->getOutputMessage(this, false);
	if (!output) {
		getConnection()->closeConnection();
		return;
	}

	setRawMessages(true);

	pugi::xml_document doc;

	pugi::xml_node decl = doc.prepend_child(pugi::node_declaration);
	decl.append_attribute("version") = "1.0";

	pugi::xml_node tsqp = doc.append_child("tsqp");
	tsqp.append_attribute("version") = "1.0";

	pugi::xml_node serverinfo = tsqp.append_child("serverinfo");
	uint64_t uptime = (OTSYS_TIME() - ProtocolStatus::start) / 1000;
	serverinfo.append_attribute("uptime") = std::to_string(uptime).c_str();
	serverinfo.append_attribute("ip") = g_config.getString(ConfigManager::IP).c_str();
	serverinfo.append_attribute("servername") = g_config.getString(ConfigManager::SERVER_NAME).c_str();
	serverinfo.append_attribute("port") = std::to_string((uint32_t)g_config.getNumber(ConfigManager::LOGIN_PORT)).c_str();
	serverinfo.append_attribute("location") = g_config.getString(ConfigManager::LOCATION).c_str();
	serverinfo.append_attribute("url") = g_config.getString(ConfigManager::URL).c_str();
	serverinfo.append_attribute("server") = STATUS_SERVER_NAME;
	serverinfo.append_attribute("version") = STATUS_SERVER_VERSION;
	serverinfo.append_attribute("client") = CLIENT_VERSION_STR;

	pugi::xml_node owner = tsqp.append_child("owner");
	owner.append_attribute("name") = g_config.getString(ConfigManager::OWNER_NAME).c_str();
	owner.append_attribute("email") = g_config.getString(ConfigManager::OWNER_EMAIL).c_str();

	pugi::xml_node players = tsqp.append_child("players");
	players.append_attribute("online") = std::to_string(g_game.getPlayersOnline()).c_str();
	players.append_attribute("max") = std::to_string((uint32_t)g_config.getNumber(ConfigManager::MAX_PLAYERS)).c_str();
	players.append_attribute("peak") = std::to_string(g_game.getPlayersRecord()).c_str();

	pugi::xml_node monsters = tsqp.append_child("monsters");
	monsters.append_attribute("total") = std::to_string(g_game.getMonstersOnline()).c_str();

	pugi::xml_node npcs = tsqp.append_child("npcs");
	npcs.append_attribute("total") = std::to_string(g_game.getNpcsOnline()).c_str();

	pugi::xml_node rates = tsqp.append_child("rates");
	rates.append_attribute("experience") = std::to_string((uint32_t)g_config.getNumber(ConfigManager::RATE_EXPERIENCE)).c_str();
	rates.append_attribute("skill") = std::to_string((uint32_t)g_config.getNumber(ConfigManager::RATE_SKILL)).c_str();
	rates.append_attribute("loot") = std::to_string((uint32_t)g_config.getNumber(ConfigManager::RATE_LOOT)).c_str();
	rates.append_attribute("magic") = std::to_string((uint32_t)g_config.getNumber(ConfigManager::RATE_MAGIC)).c_str();
	rates.append_attribute("spawn") = std::to_string((uint32_t)g_config.getNumber(ConfigManager::RATE_SPAWN)).c_str();

	pugi::xml_node map = tsqp.append_child("map");
	map.append_attribute("name") = g_config.getString(ConfigManager::MAP_NAME).c_str();
	map.append_attribute("author") = g_config.getString(ConfigManager::MAP_AUTHOR).c_str();

	uint32_t mapWidth, mapHeight;
	g_game.getMapDimensions(mapWidth, mapHeight);
	map.append_attribute("width") = std::to_string(mapWidth).c_str();
	map.append_attribute("height") = std::to_string(mapHeight).c_str();

	pugi::xml_node motd = tsqp.append_child("motd");
	motd.text() = g_config.getString(ConfigManager::MOTD).c_str();

	std::ostringstream ss;
	doc.save(ss, "", pugi::format_raw);

	std::string data = ss.str();
	output->AddBytes(data.c_str(), data.size());
	OutputMessagePool::getInstance()->send(output);
	getConnection()->closeConnection();
}