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;
}
}
}