add some new proxy commands

This commit is contained in:
Martin Michelsen
2022-03-17 19:54:42 -07:00
parent 2a836e9a04
commit 7ec3bb0f6f
3 changed files with 170 additions and 16 deletions
+113 -15
View File
@@ -16,6 +16,7 @@
#include <algorithm>
#include <iostream>
#include <phosg/Encoding.hh>
#include <phosg/Filesystem.hh>
#include <phosg/Network.hh>
#include <phosg/Strings.hh>
#include <phosg/Time.hh>
@@ -34,12 +35,17 @@ static void flush_and_free_bufferevent(struct bufferevent* bev) {
ProxyServer::ProxyServer(shared_ptr<struct event_base> base,
const struct sockaddr_storage& initial_destination, GameVersion version) :
base(base), client_bev(nullptr, flush_and_free_bufferevent),
ProxyServer::ProxyServer(
shared_ptr<struct event_base> base,
const struct sockaddr_storage& initial_destination,
GameVersion version)
: base(base),
client_bev(nullptr, flush_and_free_bufferevent),
server_bev(nullptr, flush_and_free_bufferevent),
next_destination(initial_destination), version(version),
header_size((version == GameVersion::BB) ? 8 : 4) {
next_destination(initial_destination),
version(version),
header_size((version == GameVersion::BB) ? 8 : 4),
save_quests(false) {
memset(&this->client_input_header, 0, sizeof(this->client_input_header));
memset(&this->server_input_header, 0, sizeof(this->server_input_header));
}
@@ -53,6 +59,21 @@ void ProxyServer::listen(int port) {
this->listeners.emplace(port, move(listener));
}
void ProxyServer::set_save_quests(bool save_quests) {
this->save_quests = save_quests;
}
ProxyServer::SavingQuestFile::SavingQuestFile(
const std::string& basename,
const std::string& output_filename,
uint32_t remaining_bytes)
: basename(basename),
output_filename(output_filename),
remaining_bytes(remaining_bytes),
f(fopen_unique(this->output_filename, "wb")) { }
void ProxyServer::send_to_client(const std::string& data) {
@@ -274,14 +295,10 @@ void ProxyServer::receive_and_process_commands(bool from_server) {
if (this->get_size_field(input_header) == 0) {
ssize_t bytes = evbuffer_copyout(source_buf, input_header,
this->header_size);
//log(INFO, "[ProxyServer-debug] %zd bytes copied for header", bytes);
if (bytes < static_cast<ssize_t>(this->header_size)) {
break;
}
//log(INFO, "[ProxyServer-debug] Received encrypted header");
//print_data(stderr, input_header, this->header_size);
if (source_crypt) {
source_crypt->decrypt(input_header, this->header_size);
}
@@ -289,7 +306,6 @@ void ProxyServer::receive_and_process_commands(bool from_server) {
size_t command_size = this->get_size_field(input_header);
if (evbuffer_get_length(source_buf) < command_size) {
//log(INFO, "[ProxyServer-debug] Insufficient data for command (%zX/%hX bytes)", evbuffer_get_length(source_buf), this->get_size_field(input_header));
break;
}
@@ -298,13 +314,8 @@ void ProxyServer::receive_and_process_commands(bool from_server) {
if (bytes < static_cast<ssize_t>(command_size)) {
throw logic_error("enough bytes available, but could not remove them");
}
//log(INFO, "[ProxyServer-debug] Read command (%zX bytes)", bytes);
// overwrite the header with the already-decrypted header
memcpy(command.data(), input_header, this->header_size);
//log(INFO, "[ProxyServer-debug] Received encrypted command with pre-decrypted header");
//print_data(stderr, command);
if (source_crypt) {
source_crypt->decrypt(command.data() + this->header_size,
command_size - this->header_size);
@@ -395,6 +406,93 @@ void ProxyServer::receive_and_process_commands(bool from_server) {
}
break;
}
case 0x44:
case 0xA6: { // open quest file
if (!this->save_quests) {
break;
}
bool is_download_quest = this->get_command_field(input_header) == 0xA6;
struct OpenFileCommand {
char name[0x20];
uint16_t unused;
uint16_t flags;
char filename[0x10];
uint32_t file_size;
};
if (command.size() < sizeof(OpenFileCommand)) {
log(WARNING, "[ProxyServer] Open file command is too small");
break;
}
const auto* cmd = reinterpret_cast<const OpenFileCommand*>(command.data() + this->header_size);
string output_filename = string_printf("%s.%s.%" PRIu64,
cmd->filename, is_download_quest ? "download" : "online", now());
for (size_t x = 0; x < output_filename.size(); x++) {
if (output_filename[x] < 0x20 || output_filename[x] > 0x7E || output_filename[x] == '/') {
output_filename[x] = '_';
}
}
if (output_filename[0] == '.') {
output_filename[0] = '_';
}
SavingQuestFile sqf(cmd->filename, output_filename, cmd->file_size);
this->saving_quest_files.emplace(cmd->filename, move(sqf));
log(INFO, "[ProxyServer] Opened quest file %s", output_filename.c_str());
break;
}
case 0x13:
case 0xA7: { // quest data segment
if (!this->save_quests) {
break;
}
struct WriteFileCommand {
char filename[0x10];
uint8_t data[0x400];
uint32_t data_size;
};
if (command.size() < sizeof(WriteFileCommand)) {
log(WARNING, "[ProxyServer] Write file command is too small");
break;
}
const auto* cmd = reinterpret_cast<const WriteFileCommand*>(command.data() + this->header_size);
SavingQuestFile* sqf = nullptr;
try {
sqf = &this->saving_quest_files.at(cmd->filename);
} catch (const out_of_range&) {
log(WARNING, "[ProxyServer] Can\'t find saving quest file %s",
cmd->filename);
break;
}
size_t bytes_to_write = cmd->data_size;
if (bytes_to_write > 0x400) {
log(WARNING, "[ProxyServer] Chunk data size is invalid; truncating to 0x400");
bytes_to_write = 0x400;
}
log(INFO, "[ProxyServer] Writing %zu bytes to %s", bytes_to_write,
sqf->output_filename.c_str());
fwritex(sqf->f.get(), cmd->data, bytes_to_write);
if (bytes_to_write > sqf->remaining_bytes) {
log(WARNING, "[ProxyServer] Chunk size extends beyond original file size; file may be truncated");
sqf->remaining_bytes = 0;
} else {
sqf->remaining_bytes -= bytes_to_write;
}
if (sqf->remaining_bytes == 0) {
log(INFO, "[ProxyServer] File %s is complete", sqf->output_filename.c_str());
this->saving_quest_files.erase(cmd->filename);
}
break;
}
}
}
+17
View File
@@ -3,6 +3,7 @@
#include <event2/event.h>
#include <map>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <string>
@@ -29,6 +30,8 @@ public:
void send_to_client(const std::string& data);
void send_to_server(const std::string& data);
void set_save_quests(bool save_quests);
private:
std::shared_ptr<struct event_base> base;
std::map<int, std::unique_ptr<struct evconnlistener, void(*)(struct evconnlistener*)>> listeners;
@@ -46,6 +49,20 @@ private:
std::shared_ptr<PSOEncryption> server_input_crypt;
std::shared_ptr<PSOEncryption> server_output_crypt;
struct SavingQuestFile {
std::string basename;
std::string output_filename;
uint32_t remaining_bytes;
std::unique_ptr<FILE, std::function<void(FILE*)>> f;
SavingQuestFile(
const std::string& basename,
const std::string& output_filename,
uint32_t remaining_bytes);
};
bool save_quests;
std::unordered_map<std::string, SavingQuestFile> saving_quest_files;
void send_to_end(const std::string& data, bool to_server);
static void dispatch_on_listen_accept(struct evconnlistener* listener,
+40 -1
View File
@@ -39,10 +39,16 @@ Commands:\n\
Send a chat message to the server.\n\
dchat <data>\n\
Send a chat message to the server with arbitrary data in it.\n\
info-board <text>\n\
Set your info board contents.\n\
info-board-data <data>\n\
Set your info board contents with arbitrary data.\n\
marker <color-id>\n\
Send a lobby marker message to the server.\n\
event <event-id>\n\
Send a lobby event update to yourself.\n\
ship\n\
Request the ship select menu from the server.\n\
");
} else if ((command_name == "sc") || (command_name == "ss")) {
@@ -97,10 +103,43 @@ Commands:\n\
string data("\xDA\x00\x04\x00", 4);
data[1] = stod(command_args);
log(INFO, "server (from proxy):");
log(INFO, "Server (from proxy):");
print_data(stderr, data);
this->proxy_server->send_to_client(data);
} else if (command_name == "ship") {
static const string data("\xA0\x00\x04\x00", 4);
log(INFO, "Server (from proxy):");
print_data(stderr, data);
this->proxy_server->send_to_server(data);
} else if ((command_name == "info-board") || (command_name == "info-board-data")) {
string data(4, '\0');
data[0] = 0xD9;
if (command_name == "info-board-data") {
data += parse_data_string(command_args);
} else {
data += command_args;
}
data.push_back('\0');
data.resize((data.size() + 3) & (~3));
uint16_t* size_field = reinterpret_cast<uint16_t*>(data.data() + 2);
*size_field = data.size();
log(INFO, "Client (from proxy):");
print_data(stderr, data);
this->proxy_server->send_to_server(data);
} else if (command_name == "set-save-quests") {
if (command_args == "on") {
this->proxy_server->set_save_quests(true);
} else if (command_args == "off") {
this->proxy_server->set_save_quests(false);
} else {
throw invalid_argument("argument must be \"on\" or \"off\"");
}
} else {
throw invalid_argument("unknown command; try \'help\'");
}