improve BB proxy functionality

This commit is contained in:
Martin Michelsen
2025-05-26 18:43:46 -07:00
parent 2e158a1df8
commit 33b0ab3ed3
8 changed files with 83 additions and 68 deletions
+13
View File
@@ -663,6 +663,19 @@ Action a_generate_pc_v2_registry(
write_output_data(args, output_data.data(), output_data.size(), "reg"); write_output_data(args, output_data.data(), output_data.size(), "reg");
}); });
Action a_encrypt_challenge_time(
"encrypt-challenge-time", nullptr, +[](phosg::Arguments& args) {
uint16_t time = args.get<uint16_t>(1);
uint32_t ret = encrypt_challenge_time(time);
phosg::fwrite_fmt(stderr, "{} => {:08X}\n", phosg::format_duration(time * 1000000), ret);
});
Action a_decrypt_challenge_time(
"decrypt-challenge-time", nullptr, +[](phosg::Arguments& args) {
uint32_t time = args.get<uint32_t>(1, phosg::Arguments::IntFormat::HEX);
uint16_t ret = decrypt_challenge_time(time);
phosg::fwrite_fmt(stderr, "{:08X} => {}\n", time, phosg::format_duration(ret * 1000000));
});
Action a_encrypt_challenge_data( Action a_encrypt_challenge_data(
"encrypt-challenge-data", nullptr, +[](phosg::Arguments& args) { "encrypt-challenge-data", nullptr, +[](phosg::Arguments& args) {
string data = read_input_data(args); string data = read_input_data(args);
+7 -6
View File
@@ -706,7 +706,11 @@ void PSOBBMultiKeyDetectorEncryption::encrypt(void* data, size_t size) {
} }
void PSOBBMultiKeyDetectorEncryption::decrypt(void* data, size_t size) { void PSOBBMultiKeyDetectorEncryption::decrypt(void* data, size_t size) {
if (!this->active_crypt.get()) { if (this->active_crypt.get()) {
this->active_crypt->decrypt(data, size);
return;
}
if (size != 8) { if (size != 8) {
throw logic_error("initial decryption size does not match expected first data size"); throw logic_error("initial decryption size does not match expected first data size");
} }
@@ -717,16 +721,13 @@ void PSOBBMultiKeyDetectorEncryption::decrypt(void* data, size_t size) {
string test_data(reinterpret_cast<const char*>(data), size); string test_data(reinterpret_cast<const char*>(data), size);
this->active_crypt->decrypt(test_data.data(), test_data.size()); this->active_crypt->decrypt(test_data.data(), test_data.size());
if (this->expected_first_data.count(test_data)) { if (this->expected_first_data.count(test_data)) {
break; memcpy(data, test_data.data(), size);
return;
} }
this->active_key.reset(); this->active_key.reset();
this->active_crypt.reset(); this->active_crypt.reset();
} }
if (!this->active_crypt.get()) {
throw runtime_error("none of the registered private keys are valid for this client"); throw runtime_error("none of the registered private keys are valid for this client");
}
}
this->active_crypt->decrypt(data, size);
} }
PSOEncryption::Type PSOBBMultiKeyDetectorEncryption::type() const { PSOEncryption::Type PSOBBMultiKeyDetectorEncryption::type() const {
+15 -21
View File
@@ -356,11 +356,11 @@ static asio::awaitable<HandlerResult> S_B_03(shared_ptr<Client> c, Channel::Mess
resp.character_slot = c->bb_character_index; resp.character_slot = c->bb_character_index;
resp.connection_phase = c->bb_connection_phase; resp.connection_phase = c->bb_connection_phase;
resp.client_code = c->bb_client_code; resp.client_code = c->bb_client_code;
resp.security_token = c->proxy_session->remote_bb_security_token; resp.security_token = c->bb_security_token;
resp.username.encode(c->username, c->language()); resp.username.encode(c->username, c->language());
resp.password.encode(c->password, c->language()); resp.password.encode(c->password, c->language());
resp.hardware_id = c->hardware_id; resp.hardware_id = c->hardware_id;
resp.client_config = c->proxy_session->remote_client_config_data; resp.client_config = c->bb_client_config;
if (c->proxy_session->enable_remote_ip_crc_patch) { if (c->proxy_session->enable_remote_ip_crc_patch) {
*reinterpret_cast<le_uint32_t*>(resp.client_config.data() + 0x10) = *reinterpret_cast<le_uint32_t*>(resp.client_config.data() + 0x10) =
c->proxy_session->remote_ip_crc ^ (1309539928UL + 1248334810UL); c->proxy_session->remote_ip_crc ^ (1309539928UL + 1248334810UL);
@@ -373,27 +373,17 @@ static asio::awaitable<HandlerResult> S_B_03(shared_ptr<Client> c, Channel::Mess
static asio::awaitable<HandlerResult> S_B_E6(shared_ptr<Client> c, Channel::Message& msg) { static asio::awaitable<HandlerResult> S_B_E6(shared_ptr<Client> c, Channel::Message& msg) {
const auto& cmd = msg.check_size_t<S_ClientInit_BB_00E6>(0xFFFF); const auto& cmd = msg.check_size_t<S_ClientInit_BB_00E6>(0xFFFF);
c->proxy_session->remote_guild_card_number = cmd.guild_card_number; c->proxy_session->remote_guild_card_number = cmd.guild_card_number;
c->proxy_session->remote_bb_security_token = cmd.security_token; c->bb_security_token = cmd.security_token;
c->proxy_session->remote_client_config_data = cmd.client_config; c->bb_client_config = cmd.client_config;
auto s = c->require_server_state(); auto s = c->require_server_state();
auto& pc = s->proxy_persistent_configs[c->login->account->account_id]; auto& pc = s->proxy_persistent_configs[c->login->account->account_id];
pc.account_id = c->login->account->account_id; pc.account_id = c->login->account->account_id;
pc.remote_guild_card_number = c->proxy_session->remote_guild_card_number; pc.remote_guild_card_number = c->proxy_session->remote_guild_card_number;
pc.remote_bb_security_token = c->proxy_session->remote_bb_security_token;
pc.remote_client_config_data = c->proxy_session->remote_client_config_data;
pc.enable_remote_ip_crc_patch = c->proxy_session->enable_remote_ip_crc_patch; pc.enable_remote_ip_crc_patch = c->proxy_session->enable_remote_ip_crc_patch;
c->log.info_f("Updated persistent config for proxy session"); c->log.info_f("Updated persistent config for proxy session");
if ((c->bb_connection_phase == 0) && c->proxy_session->received_reconnect) { co_return HandlerResult::FORWARD;
c->proxy_session->server_channel->send(0x00E0); // Request system file
}
co_return HandlerResult::SUPPRESS;
}
static asio::awaitable<HandlerResult> C_B_E0(shared_ptr<Client>, Channel::Message&) {
co_return HandlerResult::SUPPRESS;
} }
static asio::awaitable<HandlerResult> S_V123_04(shared_ptr<Client> c, Channel::Message& msg) { static asio::awaitable<HandlerResult> S_V123_04(shared_ptr<Client> c, Channel::Message& msg) {
@@ -748,18 +738,18 @@ static asio::awaitable<HandlerResult> S_G_E4(shared_ptr<Client> c, Channel::Mess
static asio::awaitable<HandlerResult> S_B_22(shared_ptr<Client> c, Channel::Message& msg) { static asio::awaitable<HandlerResult> S_B_22(shared_ptr<Client> c, Channel::Message& msg) {
// We use this command (which is sent before the init encryption command) to // We use this command (which is sent before the init encryption command) to
// detect a particular server behavior that we'll have to work around later. // detect a particular server behavior that we'll have to work around later.
// It looks like this command's existence is another anti-proxy measure, since // It looks like this command's existence is an anti-proxy measure, since
// this command is 0x34 bytes in total, and the logic that adds padding bytes // this command is 0x34 bytes in total, and the logic that adds padding bytes
// when the command size isn't a multiple of 8 is only active when encryption // when the command size isn't a multiple of 8 is only active when encryption
// is enabled. Presumably some simpler proxies would get this wrong. // is enabled. Presumably some simpler proxies would get this wrong.
// Editor's note: There's an unsavory message in this command's data field, // Editor's note: There's an unsavory message in this command's data field,
// hence the hash here instead of a direct string comparison. I'd love to hear // hence the hash here instead of a direct string comparison. I'd love to
// the story behind why they put that string there. // hear the story behind why they put that string there.
if ((msg.data.size() == 0x2C) && (phosg::fnv1a64(msg.data.data(), msg.data.size()) == 0x8AF8314316A27994)) { if ((msg.data.size() == 0x2C) && (phosg::fnv1a64(msg.data.data(), msg.data.size()) == 0x8AF8314316A27994)) {
c->log.info_f("Enabling remote IP CRC patch"); c->log.info_f("Enabling remote IP CRC patch");
c->proxy_session->enable_remote_ip_crc_patch = true; c->proxy_session->enable_remote_ip_crc_patch = true;
} }
co_return HandlerResult::FORWARD; co_return HandlerResult::SUPPRESS;
} }
static asio::awaitable<HandlerResult> S_19_U_14(shared_ptr<Client> c, Channel::Message& msg) { static asio::awaitable<HandlerResult> S_19_U_14(shared_ptr<Client> c, Channel::Message& msg) {
@@ -2249,7 +2239,7 @@ static on_message_t handlers[0x100][NUM_VERSIONS][2] = {
/* DE */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {nullptr, nullptr}}, /* DE */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {nullptr, nullptr}},
/* DF */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}}, /* DF */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}},
// CMD S_PC_PATCH C S_BB_PATCH C S_DC_NTE C S_DC_12_2000 C S_DC_V1 C S_DC_V2 C S_PC_NTE C S_PC_V2 C S_GC_NTE C S_GC_V3 C S_GC_EP3_NTE C S_GC_EP3 C S_XB_V3 C S_BB_V4 C // CMD S_PC_PATCH C S_BB_PATCH C S_DC_NTE C S_DC_12_2000 C S_DC_V1 C S_DC_V2 C S_PC_NTE C S_PC_V2 C S_GC_NTE C S_GC_V3 C S_GC_EP3_NTE C S_GC_EP3 C S_XB_V3 C S_BB_V4 C
/* E0 */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {S_invalid, nullptr}, {nullptr, C_B_E0}}, /* E0 */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {S_invalid, nullptr}, {nullptr, nullptr}},
/* E1 */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {S_invalid, nullptr}, {nullptr, nullptr}}, /* E1 */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {S_invalid, nullptr}, {nullptr, nullptr}},
/* E2 */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {S_invalid, nullptr}, {S_B_E2, nullptr}}, /* E2 */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {S_invalid, nullptr}, {S_B_E2, nullptr}},
/* E3 */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {S_invalid, nullptr}, {nullptr, nullptr}}, /* E3 */ {{S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {S_invalid, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {nullptr, nullptr}, {S_invalid, nullptr}, {nullptr, nullptr}},
@@ -2317,7 +2307,8 @@ asio::awaitable<void> on_proxy_command(shared_ptr<Client> c, bool from_server, u
} }
} }
asio::awaitable<void> handle_proxy_server_commands(shared_ptr<Client> c, shared_ptr<ProxySession> ses, shared_ptr<Channel> channel) { asio::awaitable<void> handle_proxy_server_commands(
shared_ptr<Client> c, shared_ptr<ProxySession> ses, shared_ptr<Channel> channel) {
std::string error_str; std::string error_str;
// server_channel can be changed by receiving a 19 command, hence the // server_channel can be changed by receiving a 19 command, hence the
// exception handler is inside the loop here // exception handler is inside the loop here
@@ -2326,6 +2317,9 @@ asio::awaitable<void> handle_proxy_server_commands(shared_ptr<Client> c, shared_
try { try {
msg = make_unique<Channel::Message>(co_await channel->recv()); msg = make_unique<Channel::Message>(co_await channel->recv());
if (c->proxy_session == ses) { if (c->proxy_session == ses) {
for (size_t z = 0; z < std::min<size_t>(c->proxy_session->prev_server_command_bytes.size(), msg->data.size()); z++) {
c->proxy_session->prev_server_command_bytes[z] = msg->data[z];
}
asio::co_spawn(co_await asio::this_coro::executor, on_proxy_command(c, true, std::move(msg)), asio::detached); asio::co_spawn(co_await asio::this_coro::executor, on_proxy_command(c, true, std::move(msg)), asio::detached);
} }
} catch (const std::system_error& e) { } catch (const std::system_error& e) {
-2
View File
@@ -10,8 +10,6 @@ ProxySession::ProxySession(shared_ptr<Channel> server_channel, const PersistentC
: server_channel(server_channel) { : server_channel(server_channel) {
if (pc) { if (pc) {
this->remote_guild_card_number = pc->remote_guild_card_number; this->remote_guild_card_number = pc->remote_guild_card_number;
this->remote_bb_security_token = pc->remote_bb_security_token;
this->remote_client_config_data = pc->remote_client_config_data;
this->enable_remote_ip_crc_patch = pc->enable_remote_ip_crc_patch; this->enable_remote_ip_crc_patch = pc->enable_remote_ip_crc_patch;
} else if (is_v4(this->server_channel->version)) { } else if (is_v4(this->server_channel->version)) {
this->remote_guild_card_number = 0; this->remote_guild_card_number = 0;
-3
View File
@@ -44,7 +44,6 @@ struct ProxySession {
uint64_t server_ping_start_time = 0; uint64_t server_ping_start_time = 0;
int64_t remote_guild_card_number = -1; int64_t remote_guild_card_number = -1;
uint32_t remote_bb_security_token = 0;
parray<uint8_t, 0x28> remote_client_config_data; parray<uint8_t, 0x28> remote_client_config_data;
enum class DropMode { enum class DropMode {
@@ -64,8 +63,6 @@ struct ProxySession {
struct PersistentConfig { struct PersistentConfig {
uint32_t account_id; uint32_t account_id;
uint32_t remote_guild_card_number; uint32_t remote_guild_card_number;
uint32_t remote_bb_security_token;
parray<uint8_t, 0x28> remote_client_config_data;
bool enable_remote_ip_crc_patch; bool enable_remote_ip_crc_patch;
std::unique_ptr<asio::steady_timer> expire_timer; std::unique_ptr<asio::steady_timer> expire_timer;
}; };
+29 -19
View File
@@ -454,7 +454,7 @@ asio::awaitable<void> start_proxy_session(shared_ptr<Client> c, const string& ho
send_proxy_destinations_menu(c); send_proxy_destinations_menu(c);
} }
} else { } else {
// Get persistent config if client is BB // Get persistent config if available
ProxySession::PersistentConfig* pc = nullptr; ProxySession::PersistentConfig* pc = nullptr;
if (use_persistent_config) { if (use_persistent_config) {
try { try {
@@ -477,8 +477,8 @@ asio::awaitable<void> start_proxy_session(shared_ptr<Client> c, const string& ho
std::format("C-{} proxy remote server at {}", c->id, netloc_str), std::format("C-{} proxy remote server at {}", c->id, netloc_str),
phosg::TerminalFormat::FG_YELLOW, phosg::TerminalFormat::FG_YELLOW,
phosg::TerminalFormat::FG_RED); phosg::TerminalFormat::FG_RED);
c->proxy_session = make_shared<ProxySession>(channel, pc); c->proxy_session = make_shared<ProxySession>(channel, pc);
c->log.info_f("Server channel connected"); c->log.info_f("Server channel connected");
asio::co_spawn(*s->io_context, handle_proxy_server_commands(c, c->proxy_session, channel), asio::detached); asio::co_spawn(*s->io_context, handle_proxy_server_commands(c, c->proxy_session, channel), asio::detached);
} }
@@ -507,15 +507,6 @@ asio::awaitable<void> end_proxy_session(shared_ptr<Client> c, const std::string&
} catch (const out_of_range&) { } catch (const out_of_range&) {
} }
bool is_in_game = c->proxy_session->is_in_game;
c->proxy_session->server_channel->disconnect();
c->proxy_session.reset();
if (is_v4(c->version())) {
c->channel->disconnect();
co_return;
}
// Delete all the other players // Delete all the other players
for (size_t x = 0; x < c->proxy_session->lobby_players.size(); x++) { for (size_t x = 0; x < c->proxy_session->lobby_players.size(); x++) {
if (c->proxy_session->lobby_players[x].guild_card_number == 0) { if (c->proxy_session->lobby_players[x].guild_card_number == 0) {
@@ -527,6 +518,15 @@ asio::awaitable<void> end_proxy_session(shared_ptr<Client> c, const std::string&
c->channel->send(c->proxy_session->is_in_game ? 0x66 : 0x69, leaving_id, &cmd, sizeof(cmd)); c->channel->send(c->proxy_session->is_in_game ? 0x66 : 0x69, leaving_id, &cmd, sizeof(cmd));
} }
bool is_in_game = c->proxy_session->is_in_game;
c->proxy_session->server_channel->disconnect();
c->proxy_session.reset();
if (is_v4(c->version())) {
c->channel->disconnect();
co_return;
}
if (is_in_game) { if (is_in_game) {
string msg = std::format("You cannot return\nto $C6{}$C7\nwhile in a game.\n\n{}", string msg = std::format("You cannot return\nto $C6{}$C7\nwhile in a game.\n\n{}",
s->name, error_message); s->name, error_message);
@@ -1480,24 +1480,34 @@ static asio::awaitable<void> on_93_BB(shared_ptr<Client> c, Channel::Message& ms
c->preferred_lobby_id = base_cmd.preferred_lobby_id; c->preferred_lobby_id = base_cmd.preferred_lobby_id;
} }
send_client_init_bb(c, 0); if (base_cmd.guild_card_number == 0) {
if (!c->bb_client_config.is_filled_with(0xFF)) {
// There is a (bug? feature?) in the BB client such that it has to receive // There is a (bug? feature?) in the BB client such that it has to receive
// a reconnect command during the data server phase, or else it won't know // a reconnect command during the data server phase, or else it won't know
// where to connect to during character selection. It's not clear why they // where to connect to during character selection. It's not clear why they
// didn't just make it use the initial connection address by default... // didn't just make it use the initial connection address by default...
send_client_init_bb(c, 0);
send_reconnect(c, s->connect_address_for_client(c), s->name_to_port_config.at("bb-data1")->port); send_reconnect(c, s->connect_address_for_client(c), s->name_to_port_config.at("bb-data1")->port);
co_return;
} else if (s->proxy_destination_bb.has_value()) { } else if (s->proxy_destination_bb.has_value()) {
// Start a proxy session if there's a destination configured Ignore the // Start a proxy session immediately if there's a destination set. Two
// persistent config if this is the first data server connection, to // things to watch out for:
// prevent quick reconnects from incorrectly reusing the old session's // - Ignore the persistent config if this is the first data server
// state. // connection, to prevent quick reconnects from incorrectly reusing the
// old session's state.
// - We don't send 00E6 (send_client_init_bb) in this case. This is because
// the login command is resent to the remote server, and we forward its
// response back to the client directly.
const auto& [host, port] = *s->proxy_destination_bb; const auto& [host, port] = *s->proxy_destination_bb;
co_await start_proxy_session(c, host, port, c->bb_connection_phase != 0); co_await start_proxy_session(c, host, port, c->bb_connection_phase != 0);
c->proxy_session->remote_client_config_data = c->bb_client_config;
co_return;
} else if (c->bb_connection_phase >= 0x04) { } else {
send_client_init_bb(c, 0);
}
if (c->bb_connection_phase >= 0x04) {
// This means the client is done with the data server phase and is in the // This means the client is done with the data server phase and is in the
// game server phase; we should send the ship select menu or a lobby join // game server phase; we should send the ship select menu or a lobby join
// command. // command.
-2
View File
@@ -237,8 +237,6 @@ void send_server_init_bb(shared_ptr<Client> c, uint8_t flags) {
auto cmd = prepare_server_init_contents_bb(server_key, client_key, flags); auto cmd = prepare_server_init_contents_bb(server_key, client_key, flags);
send_command_t(c, use_secondary_message ? 0x9B : 0x03, 0x00, cmd); send_command_t(c, use_secondary_message ? 0x9B : 0x03, 0x00, cmd);
static const string primary_expected_first_data("\xB4\x00\x93\x00\x00\x00\x00\x00", 8);
static const string secondary_expected_first_data("\xDC\x00\xDB\x00\x00\x00\x00\x00", 8);
c->bb_detector_crypt = make_shared<PSOBBMultiKeyDetectorEncryption>( c->bb_detector_crypt = make_shared<PSOBBMultiKeyDetectorEncryption>(
c->require_server_state()->bb_private_keys, c->require_server_state()->bb_private_keys,
bb_crypt_initial_client_commands, bb_crypt_initial_client_commands,
+4
View File
@@ -908,7 +908,11 @@ asio::awaitable<deque<string>> f_sc_ss(ShellCommand::Args& args) {
auto c = args.get_client(); auto c = args.get_client();
if (args.command[1] == 's') { if (args.command[1] == 's') {
if (c->proxy_session) {
send_command_with_header(c->proxy_session->server_channel, data.data(), data.size());
} else {
co_await on_command_with_header(c, data); co_await on_command_with_header(c, data);
}
} else { } else {
send_command_with_header(c->channel, data.data(), data.size()); send_command_with_header(c->channel, data.data(), data.size());
} }