From 5dbb6c3a27cb1fc1685afb7319111183d8bc20ce Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Mon, 6 Jan 2025 00:11:28 -0800 Subject: [PATCH] allow concurrent proxy sessions on the same account --- src/Account.cc | 20 ++++++++++++++++++++ src/Account.hh | 23 +++++++++++++++++++++++ src/ProxyServer.cc | 34 +++++++++++++++++----------------- src/ProxyServer.hh | 5 +++-- src/ReceiveCommands.cc | 2 +- 5 files changed, 64 insertions(+), 20 deletions(-) diff --git a/src/Account.cc b/src/Account.cc index 9766b580..b02a47d8 100644 --- a/src/Account.cc +++ b/src/Account.cc @@ -415,6 +415,26 @@ void Account::delete_file() const { remove(filename.c_str()); } +uint64_t Login::proxy_session_id() const { + uint64_t low_part = 0; + if (this->dc_nte_license) { + low_part = this->dc_nte_license->proxy_session_id_part(); + } else if (this->dc_license) { + low_part = this->dc_license->proxy_session_id_part(); + } else if (this->pc_license) { + low_part = this->pc_license->proxy_session_id_part(); + } else if (this->gc_license) { + low_part = this->gc_license->proxy_session_id_part(); + } else if (this->xb_license) { + low_part = this->xb_license->proxy_session_id_part(); + } else if (this->bb_license) { + low_part = this->bb_license->proxy_session_id_part(); + } else { + throw logic_error("none of the licenses in a Login were present"); + } + return (static_cast(this->account->account_id) << 32) | low_part; +} + string Login::str() const { string ret = phosg::string_printf("Account:%08" PRIX32, this->account->account_id); if (this->account_was_created) { diff --git a/src/Account.hh b/src/Account.hh index aa2ddd46..5e193952 100644 --- a/src/Account.hh +++ b/src/Account.hh @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -16,6 +17,10 @@ struct DCNTELicense { std::string serial_number; std::string access_key; + inline uint64_t proxy_session_id_part() const { + return phosg::fnv1a32(this->serial_number); + } + static std::shared_ptr from_json(const phosg::JSON& json); phosg::JSON json() const; }; @@ -24,6 +29,10 @@ struct V1V2License { uint32_t serial_number = 0; std::string access_key; + inline uint64_t proxy_session_id_part() const { + return this->serial_number; + } + static std::shared_ptr from_json(const phosg::JSON& json); phosg::JSON json() const; }; @@ -33,6 +42,10 @@ struct GCLicense { std::string access_key; std::string password; + inline uint64_t proxy_session_id_part() const { + return this->serial_number; + } + static std::shared_ptr from_json(const phosg::JSON& json); phosg::JSON json() const; }; @@ -42,6 +55,10 @@ struct XBLicense { uint64_t user_id = 0; uint64_t account_id = 0; + inline uint64_t proxy_session_id_part() const { + return phosg::fnv1a32(this->gamertag); + } + static std::shared_ptr from_json(const phosg::JSON& json); phosg::JSON json() const; }; @@ -50,6 +67,10 @@ struct BBLicense { std::string username; std::string password; + inline uint64_t proxy_session_id_part() const { + return phosg::fnv1a32(this->username); + } + static std::shared_ptr from_json(const phosg::JSON& json); phosg::JSON json() const; }; @@ -157,6 +178,8 @@ struct Login { std::shared_ptr xb_license; std::shared_ptr bb_license; + uint64_t proxy_session_id() const; + std::string str() const; }; diff --git a/src/ProxyServer.cc b/src/ProxyServer.cc index 08cd0541..c2b5cf6e 100644 --- a/src/ProxyServer.cc +++ b/src/ProxyServer.cc @@ -41,8 +41,8 @@ ProxyServer::ProxyServer( : base(base), destroy_sessions_ev(event_new(this->base.get(), -1, EV_TIMEOUT, &ProxyServer::dispatch_destroy_sessions, this), event_free), state(state), - next_unlinked_session_id(this->MIN_UNLINKED_SESSION_ID), - next_logged_out_session_id(this->MIN_LINKED_LOGGED_OUT_SESSION_ID) {} + next_unlinked_session_id(this->FIRST_UNLINKED_SESSION_ID), + next_logged_out_session_id(this->FIRST_LINKED_LOGGED_OUT_SESSION_ID) {} void ProxyServer::listen(const std::string& addr, uint16_t port, Version version, const struct sockaddr_storage* default_destination) { auto socket_obj = make_shared(this, addr, port, version, default_destination); @@ -148,8 +148,8 @@ void ProxyServer::on_client_connect( // server. This creates a direct session. if (default_destination && is_patch(version)) { uint64_t session_id = this->next_logged_out_session_id++; - if (this->next_logged_out_session_id == this->MIN_UNLINKED_SESSION_ID) { - this->next_logged_out_session_id = this->MIN_LINKED_LOGGED_OUT_SESSION_ID; + if (this->next_logged_out_session_id == this->FIRST_LINKED_LOGGED_OUT_SESSION_ID) { + this->next_logged_out_session_id = this->FIRST_LINKED_LOGGED_OUT_SESSION_ID; } auto emplace_ret = this->id_to_linked_session.emplace(session_id, make_shared(this->shared_from_this(), session_id, listen_port, version, *default_destination)); @@ -167,8 +167,8 @@ void ProxyServer::on_client_connect( // create an unlinked session - we'll have to get the destination from the // client's config, which we'll get via a 9E command soon. uint64_t session_id = this->next_unlinked_session_id++; - if (this->next_unlinked_session_id == 0) { - this->next_unlinked_session_id = this->MIN_UNLINKED_SESSION_ID; + if (this->next_unlinked_session_id == this->FIRST_LINKED_LOGGED_OUT_SESSION_ID) { + this->next_unlinked_session_id = this->FIRST_UNLINKED_SESSION_ID; } auto emplace_ret = this->id_to_unlinked_session.emplace( @@ -445,7 +445,7 @@ void ProxyServer::UnlinkedSession::on_input(Channel& ch, uint16_t command, uint3 // Look up the linked session for this account (if any) shared_ptr linked_ses; try { - linked_ses = server->id_to_linked_session.at(ses->login->account->account_id); + linked_ses = server->id_to_linked_session.at(ses->login->proxy_session_id()); linked_ses->log.info("Resuming linked session from unlinked session"); } catch (const out_of_range&) { @@ -464,7 +464,7 @@ void ProxyServer::UnlinkedSession::on_input(Channel& ch, uint16_t command, uint3 } if (linked_ses.get()) { - server->id_to_linked_session.emplace(ses->login->account->account_id, linked_ses); + server->id_to_linked_session.emplace(linked_ses->id, linked_ses); // Resume the linked session using the unlinked session try { if (ses->version() == Version::BB_V4) { @@ -514,7 +514,7 @@ ProxyServer::LinkedSession::LinkedSession( Version version) : server(server), id(id), - log(phosg::string_printf("[ProxyServer:LS-%" PRIX64 "] ", this->id), proxy_server_log.min_level), + log(phosg::string_printf("[ProxyServer:LS-%016" PRIX64 "] ", this->id), proxy_server_log.min_level), timeout_event(event_new(server->base.get(), -1, EV_TIMEOUT, &LinkedSession::dispatch_on_timeout, this), event_free), login(nullptr), client_channel( @@ -523,7 +523,7 @@ ProxyServer::LinkedSession::LinkedSession( nullptr, nullptr, this, - phosg::string_printf("LS-%" PRIX64 "-C", this->id), + phosg::string_printf("LS-%016" PRIX64 "-C", this->id), phosg::TerminalFormat::FG_YELLOW, phosg::TerminalFormat::FG_GREEN), server_channel( @@ -532,7 +532,7 @@ ProxyServer::LinkedSession::LinkedSession( nullptr, nullptr, this, - phosg::string_printf("LS-%" PRIX64 "-S", this->id), + phosg::string_printf("LS-%016" PRIX64 "-S", this->id), phosg::TerminalFormat::FG_YELLOW, phosg::TerminalFormat::FG_RED), local_port(local_port), @@ -564,7 +564,7 @@ ProxyServer::LinkedSession::LinkedSession( Version version, shared_ptr login, const Client::Config& config) - : LinkedSession(server, login->account->account_id, local_port, version) { + : LinkedSession(server, login->proxy_session_id(), local_port, version) { this->login = login; this->config = config; memset(&this->next_destination, 0, sizeof(this->next_destination)); @@ -580,7 +580,7 @@ ProxyServer::LinkedSession::LinkedSession( Version version, std::shared_ptr login, const struct sockaddr_storage& next_destination) - : LinkedSession(server, login->account->account_id, local_port, version) { + : LinkedSession(server, login->proxy_session_id(), local_port, version) { this->login = login; this->next_destination = next_destination; } @@ -707,8 +707,8 @@ void ProxyServer::LinkedSession::update_channel_names() { auto client_ip_str = s->format_address_for_channel_name( this->client_channel.remote_addr, this->client_channel.virtual_network_id); auto server_ip_str = s->format_address_for_channel_name(this->server_channel.remote_addr, 0); - this->client_channel.name = phosg::string_printf("LS-%08" PRIX64 "-C @ %s", this->id, client_ip_str.c_str()); - this->server_channel.name = phosg::string_printf("LS-%08" PRIX64 "-S @ %s", this->id, server_ip_str.c_str()); + this->client_channel.name = phosg::string_printf("LS-%016" PRIX64 "-C @ %s", this->id, client_ip_str.c_str()); + this->server_channel.name = phosg::string_printf("LS-%016" PRIX64 "-S @ %s", this->id, server_ip_str.c_str()); } ProxyServer::LinkedSession::SavingFile::SavingFile( @@ -991,9 +991,9 @@ shared_ptr ProxyServer::create_logged_in_session( } void ProxyServer::delete_session(uint64_t id) { - if (id < this->MIN_UNLINKED_SESSION_ID) { + if (id >= this->FIRST_LINKED_LOGGED_OUT_SESSION_ID) { if (this->id_to_linked_session.erase(id)) { - proxy_server_log.info("Closed LS-%08" PRIX64, id); + proxy_server_log.info("Closed LS-%016" PRIX64, id); } } else { auto it = this->id_to_unlinked_session.find(id); diff --git a/src/ProxyServer.hh b/src/ProxyServer.hh index af21200c..89b1d50a 100644 --- a/src/ProxyServer.hh +++ b/src/ProxyServer.hh @@ -307,6 +307,7 @@ private: Version version, const struct sockaddr_storage* default_destination); - static constexpr uint64_t MIN_UNLINKED_SESSION_ID = 0xC000000000000000; - static constexpr uint64_t MIN_LINKED_LOGGED_OUT_SESSION_ID = 0x1000000000000000; + static constexpr uint64_t FIRST_UNLINKED_SESSION_ID = 0x0000000000000001; + static constexpr uint64_t FIRST_LINKED_LOGGED_OUT_SESSION_ID = 0x0000000080000000; + static constexpr uint64_t FIRST_LINKED_LOGGED_IN_SESSION_ID = 0x00000000FFFFFFFF; }; diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index bd1c8e6d..065b0a3d 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -250,7 +250,7 @@ void send_client_to_proxy_server(shared_ptr c) { string port_name = proxy_port_name_for_version(c->version()); uint16_t local_port = s->name_to_port_config.at(port_name)->port; - s->proxy_server->delete_session(c->login->account->account_id); + s->proxy_server->delete_session(c->login->proxy_session_id()); auto ses = s->proxy_server->create_logged_in_session(c->login, local_port, c->version(), c->config); if (!c->can_use_chat_commands()) { ses->config.clear_flag(Client::Flag::PROXY_CHAT_COMMANDS_ENABLED);