fix login commands in tests

This commit is contained in:
Martin Michelsen
2022-07-27 00:09:14 -07:00
parent 415aa88bd3
commit ea62275f89
10 changed files with 210 additions and 50 deletions
+5
View File
@@ -41,6 +41,8 @@ string License::str() const {
LicenseManager::LicenseManager() : filename(""), autosave(false) { }
LicenseManager::LicenseManager(const string& filename)
: filename(filename), autosave(true) {
try {
@@ -65,6 +67,9 @@ LicenseManager::LicenseManager(const string& filename)
}
void LicenseManager::save() const {
if (this->filename.empty()) {
throw logic_error("license manager has no filename; cannot save");
}
auto f = fopen_unique(this->filename, "wb");
for (const auto& it : this->serial_number_to_license) {
if (it.second->privileges & Privilege::TEMPORARY) {
+2 -1
View File
@@ -46,7 +46,8 @@ struct License {
class LicenseManager {
public:
LicenseManager(const std::string& filename);
LicenseManager();
explicit LicenseManager(const std::string& filename);
~LicenseManager() = default;
void save() const;
+14 -3
View File
@@ -288,6 +288,8 @@ int main(int argc, char** argv) {
bool parse_data = false;
bool byteswap_data = false;
const char* replay_log_filename = nullptr;
const char* replay_required_access_key = "";
const char* replay_required_password = "";
struct sockaddr_storage cat_client_remote;
for (int x = 1; x < argc; x++) {
if (!strcmp(argv[x], "--help")) {
@@ -337,6 +339,10 @@ int main(int argc, char** argv) {
} else if (!strncmp(argv[x], "--replay-log=", 13)) {
behavior = Behavior::REPLAY_LOG;
replay_log_filename = &argv[x][13];
} else if (!strncmp(argv[x], "--require-password=", 19)) {
replay_required_password = &argv[x][19];
} else if (!strncmp(argv[x], "--require-access-key=", 21)) {
replay_required_access_key = &argv[x][21];
} else if (!strncmp(argv[x], "--config=", 9)) {
config_filename = &argv[x][9];
} else {
@@ -461,8 +467,12 @@ int main(int argc, char** argv) {
auto config_json = JSONObject::parse(load_file(config_filename));
populate_state_from_config(state, config_json);
config_log.info("Loading license list");
state->license_manager.reset(new LicenseManager("system/licenses.nsi"));
if (!replay_log_filename) {
config_log.info("Loading license list");
state->license_manager.reset(new LicenseManager("system/licenses.nsi"));
} else {
state->license_manager.reset(new LicenseManager());
}
config_log.info("Loading battle parameters");
state->battle_params.reset(new BattleParamTable("system/blueburst/BattleParamEntry"));
@@ -515,7 +525,8 @@ int main(int argc, char** argv) {
state->game_server.reset(new Server(base, state));
auto f = fopen_unique(replay_log_filename, "rt");
replay_session.reset(new ReplaySession(base, f.get(), state));
replay_session.reset(new ReplaySession(
base, f.get(), state, replay_required_access_key, replay_required_password));
replay_session->start();
} else if (behavior == Behavior::RUN_SERVER) {
+139 -1
View File
@@ -44,6 +44,133 @@ shared_ptr<ReplaySession::Event> ReplaySession::create_event(
return event;
}
void ReplaySession::check_for_password(shared_ptr<const Event> ev) const {
auto version = this->clients.at(ev->client_id)->version;
auto check_pw = [&](const string& pw) {
if (!this->required_password.empty() && (pw != this->required_password)) {
throw runtime_error("sent password is incorrect");
}
};
auto check_ak = [&](const string& ak) {
if (this->required_access_key.empty()) {
return;
}
string ref_access_key;
if (version == GameVersion::DC || version == GameVersion::PC || version == GameVersion::PATCH) {
ref_access_key = this->required_access_key.substr(0, 8);
} else {
ref_access_key = this->required_access_key;
}
if (ak != ref_access_key) {
throw runtime_error("sent access key is incorrect");
}
};
auto check_either = [&](const string& s) {
try {
check_ak(s);
} catch (const exception&) {
check_pw(s);
}
};
const void* cmd_data = ev->data.data() + ((version == GameVersion::BB) ? 8 : 4);
size_t cmd_size = ev->data.size() - ((version == GameVersion::BB) ? 8 : 4);
switch (version) {
case GameVersion::DC:
// TODO
throw logic_error("sent passwords cannot be checked on DC");
case GameVersion::PATCH: {
const auto& header = check_size_t<PSOCommandHeaderPC>(
ev->data, sizeof(PSOCommandHeaderPC), 0xFFFF);
if (header.command == 0x04) {
check_either(check_size_t<C_Login_Patch_04>(cmd_data, cmd_size).password);
}
break;
}
case GameVersion::PC: {
const auto& header = check_size_t<PSOCommandHeaderPC>(
ev->data, sizeof(PSOCommandHeaderPC), 0xFFFF);
if (header.command == 0x03) {
check_ak(check_size_t<C_LegacyLogin_PC_V3_03>(cmd_data, cmd_size).access_key2);
} else if (header.command == 0x04) {
check_ak(check_size_t<C_LegacyLogin_PC_V3_04>(cmd_data, cmd_size).access_key);
} else if (header.command == 0x9A) {
const auto& cmd = check_size_t<C_Login_DC_PC_V3_9A>(cmd_data, cmd_size);
check_ak(cmd.access_key);
check_ak(cmd.access_key2);
} else if (header.command == 0x9C) {
const auto& cmd = check_size_t<C_Register_DC_PC_V3_9C>(cmd_data, cmd_size);
check_ak(cmd.access_key);
check_pw(cmd.password);
} else if (header.command == 0x9D) {
const auto& cmd = check_size_t<C_Login_PC_9D>(cmd_data, cmd_size,
sizeof(C_Login_PC_9D), sizeof(C_LoginExtended_PC_9D));
check_ak(cmd.access_key);
check_ak(cmd.access_key2);
}
break;
}
case GameVersion::GC:
case GameVersion::XB: {
const auto& header = check_size_t<PSOCommandHeaderDCV3>(
ev->data, sizeof(PSOCommandHeaderDCV3), 0xFFFF);
if (header.command == 0x03) {
check_ak(check_size_t<C_LegacyLogin_PC_V3_03>(cmd_data, cmd_size).access_key2);
} else if (header.command == 0x04) {
check_ak(check_size_t<C_LegacyLogin_PC_V3_04>(cmd_data, cmd_size).access_key);
} else if (header.command == 0x90) {
check_ak(check_size_t<C_LegacyLogin_V3_90>(cmd_data, cmd_size).access_key);
} else if (header.command == 0x9A) {
const auto& cmd = check_size_t<C_Login_DC_PC_V3_9A>(cmd_data, cmd_size);
check_ak(cmd.access_key);
check_ak(cmd.access_key2);
} else if (header.command == 0x9C) {
const auto& cmd = check_size_t<C_Register_DC_PC_V3_9C>(cmd_data, cmd_size);
check_ak(cmd.access_key);
check_pw(cmd.password);
} else if (header.command == 0x9E) {
if (version == GameVersion::GC) {
const auto& cmd = check_size_t<C_Login_GC_9E>(cmd_data, cmd_size,
sizeof(C_Login_GC_9E), sizeof(C_LoginExtended_GC_9E));
check_ak(cmd.access_key);
check_ak(cmd.access_key2);
} else { // XB
const auto& cmd = check_size_t<C_Login_XB_9E>(cmd_data, cmd_size,
sizeof(C_Login_XB_9E), sizeof(C_LoginExtended_XB_9E));
check_ak(cmd.access_key);
check_ak(cmd.access_key2);
}
} else if (header.command == 0xDB) {
const auto& cmd = check_size_t<C_VerifyLicense_V3_DB>(cmd_data, cmd_size);
check_ak(cmd.access_key);
check_ak(cmd.access_key2);
check_pw(cmd.password);
}
break;
}
case GameVersion::BB: {
const auto& header = check_size_t<PSOCommandHeaderBB>(
ev->data, sizeof(PSOCommandHeaderBB), 0xFFFF);
if (header.command == 0x04) {
check_pw(check_size_t<C_LegacyLogin_BB_04>(cmd_data, cmd_size).password);
} else if (header.command == 0x93) {
check_pw(check_size_t<C_Login_BB_93>(cmd_data, cmd_size).password);
} else if (header.command == 0x9C) {
check_pw(check_size_t<C_Register_BB_9C>(cmd_data, cmd_size).password);
} else if (header.command == 0x9E) {
check_pw(check_size_t<C_LoginExtended_BB_9E>(cmd_data, cmd_size).password);
} else if (header.command == 0xDB) {
check_pw(check_size_t<C_VerifyLicense_BB_DB>(cmd_data, cmd_size).password);
}
break;
}
default:
throw logic_error("invalid game version");
}
}
void ReplaySession::apply_default_mask(shared_ptr<Event> ev) {
auto version = this->clients.at(ev->client_id)->version;
@@ -157,8 +284,12 @@ void ReplaySession::apply_default_mask(shared_ptr<Event> ev) {
ReplaySession::ReplaySession(
shared_ptr<struct event_base> base,
FILE* input_log,
shared_ptr<ServerState> state)
shared_ptr<ServerState> state,
const string& required_access_key,
const string& required_password)
: state(state),
required_access_key(required_access_key),
required_password(required_password),
base(base),
commands_sent(0),
bytes_sent(0),
@@ -191,6 +322,13 @@ ReplaySession::ReplaySession(
} else {
if (parsing_command->type == Event::Type::RECEIVE) {
this->apply_default_mask(parsing_command);
} else if (parsing_command->type == Event::Type::SEND) {
try {
this->check_for_password(parsing_command);
} catch (...) {
print_data(stderr, parsing_command->data);
throw;
}
}
parsing_command = nullptr;
}
+6 -1
View File
@@ -19,7 +19,9 @@ public:
ReplaySession(
std::shared_ptr<struct event_base> base,
FILE* input_log,
std::shared_ptr<ServerState> state);
std::shared_ptr<ServerState> state,
const std::string& required_access_key = "",
const std::string& required_password = "");
ReplaySession(const ReplaySession&) = delete;
ReplaySession(ReplaySession&&) = delete;
ReplaySession& operator=(const ReplaySession&) = delete;
@@ -59,6 +61,8 @@ private:
};
std::shared_ptr<ServerState> state;
std::string required_access_key;
std::string required_password;
std::unordered_map<uint64_t, std::shared_ptr<Client>> clients;
std::unordered_map<Channel*, std::shared_ptr<Client>> channel_to_client;
@@ -79,6 +83,7 @@ private:
void update_timeout_event();
void apply_default_mask(std::shared_ptr<Event> ev);
void check_for_password(std::shared_ptr<const Event> ev) const;
static void dispatch_on_timeout(evutil_socket_t fd, short events, void* ctx);
static void dispatch_on_command_received(