From d08aaef0f899edf6a6640f88bc958ef5623aef05 Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Sun, 21 Apr 2024 15:19:16 -0700 Subject: [PATCH] add remote address to command log messages --- src/CatSession.cc | 2 +- src/Channel.cc | 19 +++--- src/Channel.hh | 5 +- src/Client.cc | 19 +++++- src/Client.hh | 3 + src/IPStackSimulator.cc | 93 +++++++++++++-------------- src/IPStackSimulator.hh | 25 ++++---- src/NetworkAddresses.cc | 4 +- src/PatchServer.cc | 10 +-- src/ProxyServer.cc | 139 ++++++++++++++++++++++++---------------- src/ProxyServer.hh | 23 +++++-- src/ReceiveCommands.cc | 3 +- src/ReplaySession.cc | 4 +- src/Server.cc | 27 ++++---- src/Server.hh | 13 ++-- src/ServerState.cc | 43 ++++++++++--- src/ServerState.hh | 2 + 17 files changed, 266 insertions(+), 168 deletions(-) diff --git a/src/CatSession.cc b/src/CatSession.cc index cddd981b..57bf8e10 100644 --- a/src/CatSession.cc +++ b/src/CatSession.cc @@ -56,7 +56,7 @@ CatSession::CatSession( if (!bev) { throw runtime_error(string_printf("failed to open socket (%d)", EVUTIL_SOCKET_ERROR())); } - this->channel.set_bufferevent(bev); + this->channel.set_bufferevent(bev, 0); if (bufferevent_socket_connect(this->channel.bev.get(), reinterpret_cast(&remote), sizeof(struct sockaddr_in)) != 0) { diff --git a/src/Channel.cc b/src/Channel.cc index 028aafcb..762b00e4 100644 --- a/src/Channel.cc +++ b/src/Channel.cc @@ -32,6 +32,7 @@ Channel::Channel( TerminalFormat terminal_send_color, TerminalFormat terminal_recv_color) : bev(nullptr, flush_and_free_bufferevent), + virtual_network_id(0), version(version), language(language), name(name), @@ -44,6 +45,7 @@ Channel::Channel( Channel::Channel( struct bufferevent* bev, + uint64_t virtual_network_id, Version version, uint8_t language, on_command_received_t on_command_received, @@ -61,7 +63,7 @@ Channel::Channel( on_command_received(on_command_received), on_error(on_error), context_obj(context_obj) { - this->set_bufferevent(bev); + this->set_bufferevent(bev, virtual_network_id); } void Channel::replace_with( @@ -70,10 +72,9 @@ void Channel::replace_with( on_error_t on_error, void* context_obj, const std::string& name) { - this->set_bufferevent(other.bev.release()); + this->set_bufferevent(other.bev.release(), other.virtual_network_id); this->local_addr = other.local_addr; this->remote_addr = other.remote_addr; - this->is_virtual_connection = other.is_virtual_connection; this->version = other.version; this->language = other.language; this->crypt_in = other.crypt_in; @@ -87,27 +88,23 @@ void Channel::replace_with( other.disconnect(); // Clears crypts, addrs, etc. } -void Channel::set_bufferevent(struct bufferevent* bev) { +void Channel::set_bufferevent(struct bufferevent* bev, uint64_t virtual_network_id) { this->bev.reset(bev); + this->virtual_network_id = virtual_network_id; if (this->bev.get()) { int fd = bufferevent_getfd(this->bev.get()); if (fd < 0) { - this->is_virtual_connection = true; memset(&this->local_addr, 0, sizeof(this->local_addr)); memset(&this->remote_addr, 0, sizeof(this->remote_addr)); } else { - this->is_virtual_connection = false; get_socket_addresses(fd, &this->local_addr, &this->remote_addr); } - bufferevent_setcb(this->bev.get(), - &Channel::dispatch_on_input, nullptr, - &Channel::dispatch_on_error, this); + bufferevent_setcb(this->bev.get(), &Channel::dispatch_on_input, nullptr, &Channel::dispatch_on_error, this); bufferevent_enable(this->bev.get(), EV_READ | EV_WRITE); } else { - this->is_virtual_connection = false; memset(&this->local_addr, 0, sizeof(this->local_addr)); memset(&this->remote_addr, 0, sizeof(this->remote_addr)); } @@ -149,7 +146,7 @@ void Channel::disconnect() { memset(&this->local_addr, 0, sizeof(this->local_addr)); memset(&this->remote_addr, 0, sizeof(this->remote_addr)); - this->is_virtual_connection = false; + this->virtual_network_id = false; this->crypt_in.reset(); this->crypt_out.reset(); } diff --git a/src/Channel.hh b/src/Channel.hh index ddc15641..f5a02f5d 100644 --- a/src/Channel.hh +++ b/src/Channel.hh @@ -13,7 +13,7 @@ struct Channel { std::unique_ptr bev; struct sockaddr_storage local_addr; struct sockaddr_storage remote_addr; - bool is_virtual_connection; + uint64_t virtual_network_id; // 0 = normal TCP connection Version version; uint8_t language; @@ -50,6 +50,7 @@ struct Channel { // Creates a connected channel Channel( struct bufferevent* bev, + uint64_t virtual_network_id, Version version, uint8_t language, on_command_received_t on_command_received, @@ -70,7 +71,7 @@ struct Channel { void* context_obj, const std::string& name = ""); - void set_bufferevent(struct bufferevent* bev); + void set_bufferevent(struct bufferevent* bev, uint64_t virtual_network_id); inline bool connected() const { return this->bev.get() != nullptr; diff --git a/src/Client.cc b/src/Client.cc index e5caf2c6..3061d6af 100644 --- a/src/Client.cc +++ b/src/Client.cc @@ -11,6 +11,7 @@ #include #include +#include "IPStackSimulator.hh" #include "Loggers.hh" #include "Server.hh" #include "Version.hh" @@ -169,12 +170,13 @@ bool Client::Config::should_update_vs(const Config& other) const { Client::Client( shared_ptr server, struct bufferevent* bev, + uint64_t virtual_network_id, Version version, ServerBehavior server_behavior) : server(server), id(next_id++), log(string_printf("[C-%" PRIX64 "] ", this->id), client_log.min_level), - channel(bev, version, 1, nullptr, nullptr, this, string_printf("C-%" PRIX64, this->id), TerminalFormat::FG_YELLOW, TerminalFormat::FG_GREEN), + channel(bev, virtual_network_id, version, 1, nullptr, nullptr, this, "", TerminalFormat::FG_YELLOW, TerminalFormat::FG_GREEN), server_behavior(server_behavior), should_disconnect(false), should_send_to_lobby_server(false), @@ -214,6 +216,8 @@ Client::Client( dol_base_addr(0), external_bank_character_index(-1), last_play_time_update(0) { + this->update_channel_name(); + this->config.set_flags_for_version(version, -1); auto s = server->get_state(); if (is_v1_or_v2(this->version()) ? s->default_rare_notifs_enabled_v1_v2 : s->default_rare_notifs_enabled_v3_v4) { @@ -252,6 +256,19 @@ Client::~Client() { this->log.info("Deleted"); } +void Client::update_channel_name() { + string ip_str = this->require_server_state()->format_address_for_channel_name( + this->channel.remote_addr, this->channel.virtual_network_id); + + auto player = this->character(false, false); + if (player) { + string name_str = player->disp.name.decode(this->language()); + this->channel.name = string_printf("C-%" PRIX64 " (%s) @ %s", this->id, name_str.c_str(), ip_str.c_str()); + } else { + this->channel.name = string_printf("C-%" PRIX64 " @ %s", this->id, ip_str.c_str()); + } +} + void Client::reschedule_save_game_data_event() { if (this->version() == Version::BB_V4) { struct timeval tv = usecs_to_timeval(60000000); // 1 minute diff --git a/src/Client.hh b/src/Client.hh index d8018274..c5fd28a5 100644 --- a/src/Client.hh +++ b/src/Client.hh @@ -268,10 +268,13 @@ public: Client( std::shared_ptr server, struct bufferevent* bev, + uint64_t virtual_network_id, Version version, ServerBehavior server_behavior); ~Client(); + void update_channel_name(); + void reschedule_save_game_data_event(); void reschedule_ping_and_timeout_events(); diff --git a/src/IPStackSimulator.cc b/src/IPStackSimulator.cc index 2fd9a529..e07f1d7d 100644 --- a/src/IPStackSimulator.cc +++ b/src/IPStackSimulator.cc @@ -124,6 +124,7 @@ IPStackSimulator::IPStackSimulator( shared_ptr state) : base(base), state(state), + next_network_id(1), pcap_text_log_file(state->ip_stack_debug ? fopen("IPStackSimulator-Log.txt", "wt") : nullptr) { this->host_mac_address_bytes.clear(0x90); this->broadcast_mac_address_bytes.clear(0xFF); @@ -169,6 +170,10 @@ void IPStackSimulator::add_socket(const string& name, int fd, Protocol proto) { this->listening_sockets.emplace(piecewise_construct, forward_as_tuple(fd), forward_as_tuple(name, proto, std::move(l))); } +shared_ptr IPStackSimulator::get_network(uint64_t network_id) const { + return this->network_id_to_client.at(network_id); +} + uint32_t IPStackSimulator::connect_address_for_remote_address(uint32_t remote_addr) { // Use an address not on the same subnet as the client, so that PSO Plus and // Episode III will think they're talking to a remote network and won't reject @@ -180,8 +185,10 @@ uint32_t IPStackSimulator::connect_address_for_remote_address(uint32_t remote_ad } } -IPStackSimulator::IPClient::IPClient(shared_ptr sim, Protocol protocol, struct bufferevent* bev) +IPStackSimulator::IPClient::IPClient( + shared_ptr sim, uint64_t network_id, Protocol protocol, struct bufferevent* bev) : sim(sim), + network_id(network_id), bev(bev, bufferevent_free), protocol(protocol), mac_addr(0), @@ -200,7 +207,7 @@ void IPStackSimulator::IPClient::on_idle_timeout() { auto sim = this->sim.lock(); if (sim) { ip_stack_simulator_log.info("Idle timeout expired on virtual network %d", bufferevent_getfd(this->bev.get())); - sim->disconnect_client(this->bev.get()); + sim->disconnect_client(this->network_id); } else { ip_stack_simulator_log.info("Idle timeout expired on virtual network %d, but simulator is missing", bufferevent_getfd(this->bev.get())); } @@ -227,9 +234,9 @@ IPStackSimulator::IPClient::TCPConnection::TCPConnection() bytes_received(0), bytes_sent(0) {} -void IPStackSimulator::disconnect_client(struct bufferevent* bev) { - ip_stack_simulator_log.info("Virtual network %d disconnected", bufferevent_getfd(bev)); - this->bev_to_client.erase(bev); +void IPStackSimulator::disconnect_client(uint64_t network_id) { + ip_stack_simulator_log.info("Virtual network N-%" PRIu64 " disconnected", network_id); + this->network_id_to_client.erase(network_id); } void IPStackSimulator::dispatch_on_listen_accept( @@ -252,20 +259,20 @@ void IPStackSimulator::on_listen_accept(struct evconnlistener* listener, evutil_ try { listening_socket = &this->listening_sockets.at(listen_fd); } catch (const out_of_range&) { - ip_stack_simulator_log.info("Virtual network %d connected via unknown listener %d; disconnecting", fd, listen_fd); + ip_stack_simulator_log.info("Virtual network fd %d connected via unknown listener %d; disconnecting", fd, listen_fd); close(fd); return; } - ip_stack_simulator_log.info("Virtual network %d connected via %s", fd, listening_socket->name.c_str()); + uint64_t network_id = this->next_network_id++; + ip_stack_simulator_log.info("Virtual network N-%" PRIu64 " connected via %s", network_id, listening_socket->name.c_str()); - struct bufferevent* bev = bufferevent_socket_new(this->base.get(), fd, - BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS); - auto c = make_shared(this->shared_from_this(), listening_socket->protocol, bev); - this->bev_to_client.emplace(make_pair(bev, c)); + struct bufferevent* bev = bufferevent_socket_new(this->base.get(), fd, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS); + auto c = make_shared(this->shared_from_this(), network_id, listening_socket->protocol, bev); + this->network_id_to_client.emplace(c->network_id, c); - bufferevent_setcb(bev, &IPStackSimulator::dispatch_on_client_input, nullptr, - &IPStackSimulator::dispatch_on_client_error, this); + bufferevent_setcb(bev, &IPStackSimulator::IPClient::dispatch_on_client_input, nullptr, + &IPStackSimulator::IPClient::dispatch_on_client_error, c.get()); bufferevent_enable(bev, EV_READ | EV_WRITE); } @@ -281,31 +288,26 @@ void IPStackSimulator::on_listen_error(struct evconnlistener* listener) { event_base_loopexit(this->base.get(), nullptr); } -void IPStackSimulator::dispatch_on_client_input( - struct bufferevent* bev, void* ctx) { - reinterpret_cast(ctx)->on_client_input(bev); +void IPStackSimulator::IPClient::dispatch_on_client_input(struct bufferevent* bev, void* ctx) { + reinterpret_cast(ctx)->on_client_input(bev); } -void IPStackSimulator::on_client_input(struct bufferevent* bev) { +void IPStackSimulator::IPClient::on_client_input(struct bufferevent* bev) { struct evbuffer* buf = bufferevent_get_input(bev); - shared_ptr c; - try { - c = this->bev_to_client.at(bev); - } catch (const out_of_range&) { + auto sim = this->sim.lock(); + if (!sim) { size_t bytes = evbuffer_get_length(buf); - ip_stack_simulator_log.warning("Ignoring data received from unregistered virtual network (0x%zX bytes)", - bytes); + ip_stack_simulator_log.warning("Ignoring data from unregistered virtual network (0x%zX bytes)", bytes); evbuffer_drain(buf, bytes); return; } - auto sim = c->sim.lock(); uint64_t idle_timeout_usecs = sim ? sim->state->client_idle_timeout_usecs : 60000000; struct timeval tv = usecs_to_timeval(idle_timeout_usecs); - event_add(c->idle_timeout_event.get(), &tv); + event_add(this->idle_timeout_event.get(), &tv); - switch (c->protocol) { + switch (this->protocol) { case Protocol::ETHERNET_TAPSERVER: case Protocol::HDLC_TAPSERVER: while (evbuffer_get_length(buf) >= 2) { @@ -320,7 +322,7 @@ void IPStackSimulator::on_client_input(struct bufferevent* bev) { evbuffer_remove(buf, frame.data(), frame.size()); try { - this->on_client_frame(c, frame); + sim->on_client_frame(this->shared_from_this(), frame); } catch (const exception& e) { if (ip_stack_simulator_log.warning("Failed to process frame: %s", e.what())) { print_data(stderr, frame); @@ -355,7 +357,7 @@ void IPStackSimulator::on_client_input(struct bufferevent* bev) { evbuffer_remove(buf, frame.data(), frame.size()); try { - this->on_client_frame(c, frame); + sim->on_client_frame(this->shared_from_this(), frame); } catch (const exception& e) { if (ip_stack_simulator_log.warning("Failed to process frame: %s", e.what())) { print_data(stderr, frame); @@ -366,18 +368,19 @@ void IPStackSimulator::on_client_input(struct bufferevent* bev) { } } -void IPStackSimulator::dispatch_on_client_error( - struct bufferevent* bev, short events, void* ctx) { - reinterpret_cast(ctx)->on_client_error(bev, events); +void IPStackSimulator::IPClient::dispatch_on_client_error(struct bufferevent* bev, short events, void* ctx) { + reinterpret_cast(ctx)->on_client_error(bev, events); } -void IPStackSimulator::on_client_error(struct bufferevent* bev, short events) { +void IPStackSimulator::IPClient::on_client_error(struct bufferevent*, short events) { if (events & BEV_EVENT_ERROR) { int err = EVUTIL_SOCKET_ERROR(); - ip_stack_simulator_log.warning("Virtual network caused error %d (%s)", err, - evutil_socket_error_to_string(err)); + ip_stack_simulator_log.warning("Virtual network caused error %d (%s)", err, evutil_socket_error_to_string(err)); } if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) { - this->disconnect_client(bev); + auto sim = this->sim.lock(); + if (sim) { + sim->disconnect_client(this->network_id); + } } } @@ -1340,23 +1343,19 @@ void IPStackSimulator::open_server_connection(shared_ptr c, IPClient:: string conn_str = this->str_for_tcp_connection(c, conn); if (port_config->behavior == ServerBehavior::PROXY_SERVER) { if (!this->state->proxy_server.get()) { - ip_stack_simulator_log.error("TCP connection %s is to non-running proxy server", - conn_str.c_str()); + ip_stack_simulator_log.error("TCP connection %s is to non-running proxy server", conn_str.c_str()); flush_and_free_bufferevent(bevs[1]); } else { - this->state->proxy_server->connect_client(bevs[1], conn.server_port); - ip_stack_simulator_log.info("Connected TCP connection %s to proxy server", - conn_str.c_str()); + this->state->proxy_server->connect_virtual_client(bevs[1], c->network_id, conn.server_port); + ip_stack_simulator_log.info("Connected TCP connection %s to proxy server", conn_str.c_str()); } } else if (this->state->game_server.get()) { - this->state->game_server->connect_client(bevs[1], c->ipv4_addr, - conn.client_port, conn.server_port, port_config->version, - port_config->behavior); - ip_stack_simulator_log.info("Connected TCP connection %s to game server", - conn_str.c_str()); + this->state->game_server->connect_virtual_client( + bevs[1], c->network_id, c->ipv4_addr, conn.client_port, + conn.server_port, port_config->version, port_config->behavior); + ip_stack_simulator_log.info("Connected TCP connection %s to game server", conn_str.c_str()); } else { - ip_stack_simulator_log.error("No server available for TCP connection %s", - conn_str.c_str()); + ip_stack_simulator_log.error("No server available for TCP connection %s", conn_str.c_str()); flush_and_free_bufferevent(bevs[1]); } } diff --git a/src/IPStackSimulator.hh b/src/IPStackSimulator.hh index be9a9a6b..376c411e 100644 --- a/src/IPStackSimulator.hh +++ b/src/IPStackSimulator.hh @@ -27,8 +27,9 @@ public: using unique_evbuffer = std::unique_ptr; using unique_event = std::unique_ptr; - struct IPClient { + struct IPClient : std::enable_shared_from_this { std::weak_ptr sim; + uint64_t network_id; unique_bufferevent bev; Protocol protocol; @@ -70,7 +71,12 @@ public: unique_event idle_timeout_event; - IPClient(std::shared_ptr sim, Protocol protocol, struct bufferevent* bev); + IPClient(std::shared_ptr sim, uint64_t network_id, Protocol protocol, struct bufferevent* bev); + + static void dispatch_on_client_input(struct bufferevent* bev, void* ctx); + void on_client_input(struct bufferevent* bev); + static void dispatch_on_client_error(struct bufferevent* bev, short events, void* ctx); + void on_client_error(struct bufferevent* bev, short events); static void dispatch_on_idle_timeout(evutil_socket_t fd, short events, void* ctx); void on_idle_timeout(); @@ -88,15 +94,17 @@ public: static uint32_t connect_address_for_remote_address(uint32_t remote_addr); - inline const std::unordered_map>& all_clients() const { - return this->bev_to_client; + std::shared_ptr get_network(uint64_t network_id) const; + inline const std::unordered_map>& all_networks() const { + return this->network_id_to_client; } - void disconnect_client(struct bufferevent* bev); + void disconnect_client(uint64_t network_id); private: std::shared_ptr base; std::shared_ptr state; + uint64_t next_network_id; struct ListeningSocket { std::string name; @@ -110,7 +118,7 @@ private: }; std::unordered_map listening_sockets; - std::unordered_map> bev_to_client; + std::unordered_map> network_id_to_client; parray host_mac_address_bytes; parray broadcast_mac_address_bytes; @@ -130,11 +138,6 @@ private: static void dispatch_on_listen_error(struct evconnlistener* listener, void* ctx); void on_listen_error(struct evconnlistener* listener); - static void dispatch_on_client_input(struct bufferevent* bev, void* ctx); - void on_client_input(struct bufferevent* bev); - static void dispatch_on_client_error(struct bufferevent* bev, short events, void* ctx); - void on_client_error(struct bufferevent* bev, short events); - void send_layer3_frame(std::shared_ptr c, FrameInfo::Protocol proto, const std::string& data) const; void send_layer3_frame(std::shared_ptr c, FrameInfo::Protocol proto, const void* data, size_t size) const; diff --git a/src/NetworkAddresses.cc b/src/NetworkAddresses.cc index 6554fe28..cd7c49ac 100644 --- a/src/NetworkAddresses.cc +++ b/src/NetworkAddresses.cc @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -23,8 +24,7 @@ uint32_t resolve_address(const char* address) { "can\'t resolve hostname %s: %s", address, e.c_str())); } - std::unique_ptr res0_unique( - res0, freeaddrinfo); + std::unique_ptr res0_unique(res0, freeaddrinfo); struct addrinfo* res4 = nullptr; for (struct addrinfo* res = res0; res; res = res->ai_next) { if (res->ai_family == AF_INET) { diff --git a/src/PatchServer.cc b/src/PatchServer.cc index 8fbf760c..ded22426 100644 --- a/src/PatchServer.cc +++ b/src/PatchServer.cc @@ -38,7 +38,7 @@ PatchServer::Client::Client( : server(server), id(next_id++), log(string_printf("[C-%" PRIX64 "] ", this->id), client_log.min_level), - channel(bev, version, 1, nullptr, nullptr, this, string_printf("C-%" PRIX64, this->id), TerminalFormat::FG_YELLOW, TerminalFormat::FG_GREEN), + channel(bev, 0, version, 1, nullptr, nullptr, this, string_printf("C-%" PRIX64, this->id), TerminalFormat::FG_YELLOW, TerminalFormat::FG_GREEN), idle_timeout_usecs(idle_timeout_usecs), idle_timeout_event( event_new(bufferevent_get_base(bev), -1, EV_TIMEOUT, &PatchServer::Client::dispatch_idle_timeout, this), @@ -279,12 +279,12 @@ void PatchServer::on_10(shared_ptr c, string&) { } void PatchServer::disconnect_client(shared_ptr c) { - if (c->channel.is_virtual_connection) { - server_log.info("Client disconnected: C-%" PRIX64 " on virtual connection %p", c->id, c->channel.bev.get()); + if (c->channel.virtual_network_id) { + server_log.info("Client disconnected: C-%" PRIX64 " on N-%" PRIu64, c->id, c->channel.virtual_network_id); } else if (c->channel.bev) { - server_log.info("Client disconnected: C-%" PRIX64 " on fd %d", c->id, bufferevent_getfd(c->channel.bev.get())); + server_log.info("Client disconnected: C-%" PRIX64, c->id); } else { - server_log.info("Client C-%" PRIX64 " removed from game server", c->id); + server_log.info("Client C-%" PRIX64 " removed from patch server", c->id); } this->channel_to_client.erase(&c->channel); diff --git a/src/ProxyServer.cc b/src/ProxyServer.cc index 0328bfa1..32e3e81f 100644 --- a/src/ProxyServer.cc +++ b/src/ProxyServer.cc @@ -41,7 +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_logged_out_session_id(0xFF00000000000001) {} + next_unlinked_session_id(this->MIN_UNLINKED_SESSION_ID), + next_logged_out_session_id(this->MIN_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); @@ -57,7 +58,7 @@ ProxyServer::ListeningSocket::ListeningSocket( Version version, const struct sockaddr_storage* default_destination) : server(server), - log(string_printf("[ProxyServer:ListeningSocket:%hu] ", port), proxy_server_log.min_level), + log(string_printf("[ProxyServer:T-%hu] ", port), proxy_server_log.min_level), port(port), fd(::listen(addr, port, SOMAXCONN)), listener(nullptr, evconnlistener_free), @@ -107,7 +108,7 @@ void ProxyServer::ListeningSocket::on_listen_accept(int fd) { this->log.info("Client connected on fd %d (port %hu, version %s)", fd, this->port, name_for_enum(this->version)); auto* bev = bufferevent_socket_new(this->server->base.get(), fd, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS); - this->server->on_client_connect(bev, this->port, this->version, + this->server->on_client_connect(bev, 0, this->port, this->version, (this->default_destination.ss_family == AF_INET) ? &this->default_destination : nullptr); } @@ -119,27 +120,26 @@ void ProxyServer::ListeningSocket::on_listen_error() { event_base_loopexit(this->server->base.get(), nullptr); } -void ProxyServer::connect_client(struct bufferevent* bev, uint16_t server_port) { +void ProxyServer::connect_virtual_client(struct bufferevent* bev, uint64_t virtual_network_id, uint16_t server_port) { // Look up the listening socket for the given port, and use that game version. // We don't support default-destination proxying for virtual connections (yet) Version version; try { version = this->listeners.at(server_port)->version; } catch (const out_of_range&) { - proxy_server_log.info("Virtual connection received on unregistered port %hu; closing it", - server_port); + proxy_server_log.info("Virtual connection received on unregistered port %hu; closing it", server_port); bufferevent_flush(bev, EV_READ | EV_WRITE, BEV_FINISHED); bufferevent_free(bev); return; } - proxy_server_log.info("Client connected on virtual connection %p (port %hu)", bev, - server_port); - this->on_client_connect(bev, server_port, version, nullptr); + proxy_server_log.info("Client connected on virtual connection %p (port %hu)", bev, server_port); + this->on_client_connect(bev, virtual_network_id, server_port, version, nullptr); } void ProxyServer::on_client_connect( struct bufferevent* bev, + uint64_t virtual_network_id, uint16_t listen_port, Version version, const struct sockaddr_storage* default_destination) { @@ -148,25 +148,31 @@ 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 == 0) { - this->next_logged_out_session_id = 0xFF00000000000001; + 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; } - auto emplace_ret = this->id_to_session.emplace(session_id, make_shared(this->shared_from_this(), session_id, listen_port, version, *default_destination)); + auto emplace_ret = this->id_to_linked_session.emplace(session_id, make_shared(this->shared_from_this(), session_id, listen_port, version, *default_destination)); if (!emplace_ret.second) { throw logic_error("linked session already exists for logged-out client"); } auto ses = emplace_ret.first->second; ses->log.info("Opened linked session"); - Channel ch(bev, version, 1, nullptr, nullptr, ses.get(), "", TerminalFormat::FG_YELLOW, TerminalFormat::FG_GREEN); + Channel ch(bev, virtual_network_id, version, 1, nullptr, nullptr, ses.get(), "", TerminalFormat::FG_YELLOW, TerminalFormat::FG_GREEN); ses->resume(std::move(ch)); + } else { // If no default destination exists, or the client is not a patch client, // 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. - } else { - auto emplace_ret = this->bev_to_unlinked_session.emplace(bev, make_shared(this->shared_from_this(), bev, listen_port, version)); + 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; + } + + auto emplace_ret = this->id_to_unlinked_session.emplace( + session_id, make_shared(this->shared_from_this(), session_id, bev, virtual_network_id, listen_port, version)); if (!emplace_ret.second) { throw logic_error("stale unlinked session exists"); } @@ -235,22 +241,28 @@ void ProxyServer::on_client_connect( ProxyServer::UnlinkedSession::UnlinkedSession( shared_ptr server, + uint64_t id, struct bufferevent* bev, + uint64_t virtual_network_id, uint16_t local_port, Version version) : server(server), - log(string_printf("[ProxyServer:UnlinkedSession:%p] ", bev), proxy_server_log.min_level), + id(id), + log(string_printf("[ProxyServer:US-%" PRIX64 "] ", this->id), proxy_server_log.min_level), channel( bev, + virtual_network_id, version, 1, ProxyServer::UnlinkedSession::on_input, ProxyServer::UnlinkedSession::on_error, this, - string_printf("UnlinkedSession:%p", bev), + "", TerminalFormat::FG_YELLOW, TerminalFormat::FG_GREEN), local_port(local_port) { + string ip_str = server->state->format_address_for_channel_name(this->channel.remote_addr, this->channel.virtual_network_id); + this->channel.name = string_printf("US-%" PRIX64 " @ %s", this->id, ip_str.c_str()); memset(&this->next_destination, 0, sizeof(this->next_destination)); } @@ -413,10 +425,7 @@ void ProxyServer::UnlinkedSession::on_input(Channel& ch, uint16_t command, uint3 should_close_unlinked_session = true; } - // Note that ch.bev will be moved from when the linked session is resumed, so - // we need to retain a copy of it in order to close the unlinked session - // afterward. - struct bufferevent* session_key = ch.bev.get(); + uint64_t unlinked_session_id = ses->id; // If login is present, then the client has credentials and can be connected // to the remote lobby server. @@ -428,7 +437,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_session.at(ses->login->account->account_id); + linked_ses = server->id_to_linked_session.at(ses->login->account->account_id); linked_ses->log.info("Resuming linked session from unlinked session"); } catch (const out_of_range&) { @@ -447,7 +456,7 @@ void ProxyServer::UnlinkedSession::on_input(Channel& ch, uint16_t command, uint3 } if (linked_ses.get()) { - server->id_to_session.emplace(ses->login->account->account_id, linked_ses); + server->id_to_linked_session.emplace(ses->login->account->account_id, linked_ses); // Resume the linked session using the unlinked session try { if (ses->version() == Version::BB_V4) { @@ -472,7 +481,7 @@ void ProxyServer::UnlinkedSession::on_input(Channel& ch, uint16_t command, uint3 } if (should_close_unlinked_session) { - server->delete_session(session_key); + server->delete_session(unlinked_session_id); } } @@ -486,7 +495,7 @@ void ProxyServer::UnlinkedSession::on_error(Channel& ch, short events) { } if (events & (BEV_EVENT_ERROR | BEV_EVENT_EOF)) { ses->log.info("Client has disconnected"); - ses->require_server()->delete_session(ses->channel.bev.get()); + ses->require_server()->delete_session(ses->id); } } @@ -497,7 +506,7 @@ ProxyServer::LinkedSession::LinkedSession( Version version) : server(server), id(id), - log(string_printf("[ProxyServer:LinkedSession:%08" PRIX64 "] ", this->id), proxy_server_log.min_level), + log(string_printf("[ProxyServer:LS-%" 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( @@ -506,7 +515,7 @@ ProxyServer::LinkedSession::LinkedSession( nullptr, nullptr, this, - string_printf("LinkedSession:%08" PRIX64 ":client", this->id), + string_printf("LS-%" PRIX64 "-C", this->id), TerminalFormat::FG_YELLOW, TerminalFormat::FG_GREEN), server_channel( @@ -515,7 +524,7 @@ ProxyServer::LinkedSession::LinkedSession( nullptr, nullptr, this, - string_printf("LinkedSession:%08" PRIX64 ":server", this->id), + string_printf("LS-%" PRIX64 "-S", this->id), TerminalFormat::FG_YELLOW, TerminalFormat::FG_RED), local_port(local_port), @@ -637,12 +646,17 @@ void ProxyServer::LinkedSession::resume_inner( throw logic_error("attempted to resume an logged-out linked session without destination set"); } + auto s = this->server.lock(); + if (!s) { + throw logic_error("ProxyServer is missing during LinkedSession resume"); + } + this->client_channel.replace_with( std::move(client_channel), ProxyServer::LinkedSession::on_input, ProxyServer::LinkedSession::on_error, this, - string_printf("LinkedSession:%08" PRIX64 ":client", this->id)); + ""); this->server_channel.language = this->client_channel.language; this->server_channel.version = this->client_channel.version; @@ -665,8 +679,8 @@ void ProxyServer::LinkedSession::connect() { string netloc_str = render_sockaddr_storage(this->next_destination); this->log.info("Connecting to %s", netloc_str.c_str()); - this->server_channel.set_bufferevent(bufferevent_socket_new( - this->require_server()->base.get(), -1, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS)); + this->server_channel.set_bufferevent( + bufferevent_socket_new(this->require_server()->base.get(), -1, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS), 0); if (bufferevent_socket_connect(this->server_channel.bev.get(), reinterpret_cast(dest_sin), sizeof(*dest_sin)) != 0) { throw runtime_error(string_printf("failed to connect (%d)", EVUTIL_SOCKET_ERROR())); @@ -676,10 +690,21 @@ void ProxyServer::LinkedSession::connect() { this->server_channel.on_error = ProxyServer::LinkedSession::on_error; this->server_channel.context_obj = this; + this->update_channel_names(); + // Cancel the session delete timeout event_del(this->timeout_event.get()); } +void ProxyServer::LinkedSession::update_channel_names() { + auto s = this->require_server_state(); + 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 = string_printf("LS-%08" PRIX64 "-C @ %s", this->id, client_ip_str.c_str()); + this->server_channel.name = string_printf("LS-%08" PRIX64 "-S @ %s", this->id, server_ip_str.c_str()); +} + ProxyServer::LinkedSession::SavingFile::SavingFile( const string& basename, const string& output_filename, @@ -704,6 +729,10 @@ void ProxyServer::LinkedSession::on_error(Channel& ch, short events) { if (events & BEV_EVENT_CONNECTED) { ses->log.info("%s channel connected", is_server_stream ? "Server" : "Client"); + if (is_server_stream) { + get_socket_addresses(bufferevent_getfd(ch.bev.get()), &ch.local_addr, &ch.remote_addr); + ses->update_channel_names(); + } if (is_server_stream && (ses->config.override_lobby_event != 0xFF) && (is_v3(ses->version()) || is_v4(ses->version()))) { ses->client_channel.send(0xDA, ses->config.override_lobby_event); @@ -844,7 +873,7 @@ void ProxyServer::LinkedSession::send_to_game_server(const char* error_message) // If the client is on a virtual connection, we can use any address // here and they should be able to connect back to the game server - if (this->client_channel.is_virtual_connection) { + if (this->client_channel.virtual_network_id) { struct sockaddr_in* dest_sin = reinterpret_cast(&this->next_destination); if (dest_sin->sin_family != AF_INET) { throw logic_error("ss not AF_INET"); @@ -917,19 +946,19 @@ void ProxyServer::LinkedSession::on_input(Channel& ch, uint16_t command, uint32_ } shared_ptr ProxyServer::get_session() const { - if (this->id_to_session.empty()) { + if (this->id_to_linked_session.empty()) { throw runtime_error("no sessions exist"); } - if (this->id_to_session.size() > 1) { + if (this->id_to_linked_session.size() > 1) { throw runtime_error("multiple sessions exist"); } - return this->id_to_session.begin()->second; + return this->id_to_linked_session.begin()->second; } shared_ptr ProxyServer::get_session_by_name(const std::string& name) const { try { uint64_t session_id = stoull(name, nullptr, 16); - return this->id_to_session.at(session_id); + return this->id_to_linked_session.at(session_id); } catch (const invalid_argument&) { throw runtime_error("invalid session name"); } catch (const out_of_range&) { @@ -938,7 +967,7 @@ shared_ptr ProxyServer::get_session_by_name(const st } const unordered_map>& ProxyServer::all_sessions() const { - return this->id_to_session; + return this->id_to_linked_session; } shared_ptr ProxyServer::create_logged_in_session( @@ -947,7 +976,7 @@ shared_ptr ProxyServer::create_logged_in_session( Version version, const Client::Config& config) { auto session = make_shared(this->shared_from_this(), local_port, version, login, config); - auto emplace_ret = this->id_to_session.emplace(session->id, session); + auto emplace_ret = this->id_to_linked_session.emplace(session->id, session); if (!emplace_ret.second) { throw runtime_error("session already exists for this account"); } @@ -956,22 +985,22 @@ shared_ptr ProxyServer::create_logged_in_session( } void ProxyServer::delete_session(uint64_t id) { - if (this->id_to_session.erase(id)) { - proxy_server_log.info("Closed LinkedSession:%08" PRIX64, id); - } -} + if (id < this->MIN_UNLINKED_SESSION_ID) { + if (this->id_to_linked_session.erase(id)) { + proxy_server_log.info("Closed LS-%08" PRIX64, id); + } + } else { + auto it = this->id_to_unlinked_session.find(id); + if (it == this->id_to_unlinked_session.end()) { + throw logic_error("unlinked session exists but is not registered"); + } + it->second->log.info("Closing session"); + this->unlinked_sessions_to_destroy.emplace(std::move(it->second)); + this->id_to_unlinked_session.erase(it); -void ProxyServer::delete_session(struct bufferevent* bev) { - auto it = this->bev_to_unlinked_session.find(bev); - if (it == this->bev_to_unlinked_session.end()) { - throw logic_error("unlinked session exists but is not registered"); + auto tv = usecs_to_timeval(0); + event_add(this->destroy_sessions_ev.get(), &tv); } - it->second->log.info("Closing session"); - this->unlinked_sessions_to_destroy.emplace(std::move(it->second)); - this->bev_to_unlinked_session.erase(it); - - auto tv = usecs_to_timeval(0); - event_add(this->destroy_sessions_ev.get(), &tv); } void ProxyServer::dispatch_destroy_sessions(evutil_socket_t, short, void* ctx) { @@ -983,14 +1012,14 @@ void ProxyServer::destroy_sessions() { } size_t ProxyServer::num_sessions() const { - return this->id_to_session.size(); + return this->id_to_linked_session.size(); } size_t ProxyServer::delete_disconnected_sessions() { size_t count = 0; - for (auto it = this->id_to_session.begin(); it != this->id_to_session.end();) { + for (auto it = this->id_to_linked_session.begin(); it != this->id_to_linked_session.end();) { if (!it->second->is_connected()) { - it = this->id_to_session.erase(it); + it = this->id_to_linked_session.erase(it); count++; } else { it++; diff --git a/src/ProxyServer.hh b/src/ProxyServer.hh index f20c70ac..9378050e 100644 --- a/src/ProxyServer.hh +++ b/src/ProxyServer.hh @@ -28,7 +28,7 @@ public: void listen(const std::string& addr, uint16_t port, Version version, const struct sockaddr_storage* default_destination = nullptr); - void connect_client(struct bufferevent* bev, uint16_t server_port); + void connect_virtual_client(struct bufferevent* bev, uint64_t virtual_network_id, uint16_t server_port); struct LinkedSession : std::enable_shared_from_this { std::weak_ptr server; @@ -186,6 +186,8 @@ public: static void on_error(Channel& ch, short events); void on_timeout(); + void update_channel_names(); + void clear_lobby_players(size_t num_slots); void set_drop_mode(DropMode new_mode); @@ -205,7 +207,6 @@ public: Version version, const Client::Config& config); void delete_session(uint64_t id); - void delete_session(struct bufferevent* bev); size_t num_sessions() const; @@ -238,6 +239,7 @@ private: struct UnlinkedSession { std::weak_ptr server; + uint64_t id; PrefixedLogger log; Channel channel; @@ -259,7 +261,13 @@ private: XBNetworkLocation xb_netloc; parray xb_9E_unknown_a1a; - UnlinkedSession(std::shared_ptr server, struct bufferevent* bev, uint16_t port, Version version); + UnlinkedSession( + std::shared_ptr server, + uint64_t id, + struct bufferevent* bev, + uint64_t virtual_network_id, + uint16_t port, + Version version); std::shared_ptr require_server() const; std::shared_ptr require_server_state() const; @@ -278,9 +286,10 @@ private: std::shared_ptr destroy_sessions_ev; std::shared_ptr state; std::map> listeners; - std::unordered_map> bev_to_unlinked_session; + std::unordered_map> id_to_unlinked_session; std::unordered_set> unlinked_sessions_to_destroy; - std::unordered_map> id_to_session; + std::unordered_map> id_to_linked_session; + uint64_t next_unlinked_session_id; uint64_t next_logged_out_session_id; static void dispatch_destroy_sessions(evutil_socket_t, short, void* ctx); @@ -288,7 +297,11 @@ private: void on_client_connect( struct bufferevent* bev, + uint64_t virtual_network_id, uint16_t listen_port, 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; }; diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index a8c9cd4d..be3a1bac 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -3114,8 +3114,7 @@ static void on_61_98(shared_ptr c, uint16_t command, uint32_t flag, stri c->channel.language = player->inventory.language; c->login->account->save(); - string name_str = player->disp.name.decode(c->language()); - c->channel.name = string_printf("C-%" PRIX64 " (%s)", c->id, name_str.c_str()); + c->update_channel_name(); // 98 should only be sent when leaving a game, and we should leave the client // in no lobby (they will send an 84 soon afterward to choose a lobby). diff --git a/src/ReplaySession.cc b/src/ReplaySession.cc index 27e8afa7..a1c2f5f2 100644 --- a/src/ReplaySession.cc +++ b/src/ReplaySession.cc @@ -630,7 +630,7 @@ void ReplaySession::execute_pending_events() { struct bufferevent* bevs[2]; bufferevent_pair_new(this->base.get(), 0, bevs); - c->channel.set_bufferevent(bevs[0]); + c->channel.set_bufferevent(bevs[0], 0); this->channel_to_client.emplace(&c->channel, c); shared_ptr port_config; @@ -645,7 +645,7 @@ void ReplaySession::execute_pending_events() { // TODO: We should support this at some point in the future throw runtime_error(string_printf("(ev-line %zu) client connected to proxy server", this->first_event->line_num)); } else if (this->state->game_server.get()) { - this->state->game_server->connect_client(bevs[1], 0x20202020, + this->state->game_server->connect_virtual_client(bevs[1], 0, 0x20202020, 1025, c->port, port_config->version, port_config->behavior); } else { throw runtime_error(string_printf("(ev-line %zu) no server available for connection", this->first_event->line_num)); diff --git a/src/Server.cc b/src/Server.cc index 7b1b477b..068136c3 100644 --- a/src/Server.cc +++ b/src/Server.cc @@ -28,10 +28,10 @@ using namespace std; using namespace std::placeholders; void Server::disconnect_client(shared_ptr c) { - if (c->channel.is_virtual_connection) { - server_log.info("Client disconnected: C-%" PRIX64 " on virtual connection %p", c->id, c->channel.bev.get()); + if (c->channel.virtual_network_id) { + server_log.info("Client disconnected: C-%" PRIX64 " on N-%" PRIu64, c->id, c->channel.virtual_network_id); } else if (c->channel.bev) { - server_log.info("Client disconnected: C-%" PRIX64 " on fd %d", c->id, bufferevent_getfd(c->channel.bev.get())); + server_log.info("Client disconnected: C-%" PRIX64, c->id); } else { server_log.info("Client C-%" PRIX64 " removed from game server", c->id); } @@ -112,7 +112,7 @@ void Server::on_listen_accept(struct evconnlistener* listener, evutil_socket_t f } struct bufferevent* bev = bufferevent_socket_new(this->base.get(), fd, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS); - auto c = make_shared(this->shared_from_this(), bev, listening_socket->version, listening_socket->behavior); + auto c = make_shared(this->shared_from_this(), bev, 0, listening_socket->version, listening_socket->behavior); c->channel.on_command_received = Server::on_client_input; c->channel.on_error = Server::on_client_error; c->channel.context_obj = this; @@ -129,19 +129,24 @@ void Server::on_listen_accept(struct evconnlistener* listener, evutil_socket_t f } } -void Server::connect_client( - struct bufferevent* bev, uint32_t address, uint16_t client_port, - uint16_t server_port, Version version, ServerBehavior initial_state) { - auto c = make_shared(this->shared_from_this(), bev, version, initial_state); +void Server::connect_virtual_client( + struct bufferevent* bev, + uint64_t virtual_network_id, + uint32_t address, + uint16_t client_port, + uint16_t server_port, + Version version, + ServerBehavior initial_state) { + auto c = make_shared(this->shared_from_this(), bev, virtual_network_id, version, initial_state); c->channel.on_command_received = Server::on_client_input; c->channel.on_error = Server::on_client_error; c->channel.context_obj = this; this->state->channel_to_client.emplace(&c->channel, c); server_log.info( - "Client connected: C-%" PRIX64 " on virtual connection %p via T-%hu-%s-%s-VI", + "Client connected: C-%" PRIX64 " on virtual network N-%" PRIu64 " via T-%hu-%s-%s-VI", c->id, - bev, + virtual_network_id, server_port, name_for_enum(version), name_for_enum(initial_state)); @@ -161,7 +166,7 @@ void Server::connect_client( } } -void Server::connect_client(shared_ptr c, Channel&& ch) { +void Server::connect_virtual_client(shared_ptr c, Channel&& ch) { c->channel.replace_with(std::move(ch), Server::on_client_input, Server::on_client_error, this, string_printf("C-%" PRIX64, c->id)); this->state->channel_to_client.emplace(&c->channel, c); server_log.info("Client C-%" PRIX64 " added to game server", c->id); diff --git a/src/Server.hh b/src/Server.hh index a8101178..a75b2033 100644 --- a/src/Server.hh +++ b/src/Server.hh @@ -23,10 +23,15 @@ public: void listen(const std::string& addr_str, int port, Version version, ServerBehavior initial_state); void add_socket(const std::string& addr_str, int fd, Version version, ServerBehavior initial_state); - void connect_client(struct bufferevent* bev, uint32_t address, - uint16_t client_port, uint16_t server_port, - Version version, ServerBehavior initial_state); - void connect_client(std::shared_ptr c, Channel&& ch); + void connect_virtual_client( + struct bufferevent* bev, + uint64_t virtual_network_id, + uint32_t address, + uint16_t client_port, + uint16_t server_port, + Version version, + ServerBehavior initial_state); + void connect_virtual_client(std::shared_ptr c, Channel&& ch); void disconnect_client(std::shared_ptr c); std::shared_ptr get_client() const; diff --git a/src/ServerState.cc b/src/ServerState.cc index b40c6ab4..b12834ea 100644 --- a/src/ServerState.cc +++ b/src/ServerState.cc @@ -247,13 +247,12 @@ shared_ptr ServerState::find_client(const string* identifier, uint64_t a } uint32_t ServerState::connect_address_for_client(shared_ptr c) const { - if (c->channel.is_virtual_connection) { + if (c->channel.virtual_network_id) { if (c->channel.remote_addr.ss_family != AF_INET) { throw logic_error("virtual connection is missing remote IPv4 address"); } const auto* sin = reinterpret_cast(&c->channel.remote_addr); - return IPStackSimulator::connect_address_for_remote_address( - ntohl(sin->sin_addr.s_addr)); + return IPStackSimulator::connect_address_for_remote_address(ntohl(sin->sin_addr.s_addr)); } else { // TODO: we can do something smarter here, like use the sockname to find // out which interface the client is connected to, and return that address @@ -1908,20 +1907,46 @@ void ServerState::disconnect_all_banned_clients() { // IP stack simulator (IP bans only; account bans will presumably be handled // by one of the above cases) if (this->ip_stack_simulator) { - vector bevs_to_disconnect; - for (const auto& it : this->ip_stack_simulator->all_clients()) { - int fd = bufferevent_getfd(it.first); + vector ids_to_disconnect; + for (const auto& it : this->ip_stack_simulator->all_networks()) { + int fd = bufferevent_getfd(it.second->bev.get()); if (fd < 0) { continue; } struct sockaddr_storage remote_ss; get_socket_addresses(fd, nullptr, &remote_ss); if (this->banned_ipv4_ranges->check(remote_ss)) { - bevs_to_disconnect.emplace_back(it.first); + ids_to_disconnect.emplace_back(it.second->network_id); } } - for (auto* bev : bevs_to_disconnect) { - this->ip_stack_simulator->disconnect_client(bev); + for (uint64_t id : ids_to_disconnect) { + this->ip_stack_simulator->disconnect_client(id); + } + } +} + +string ServerState::format_address_for_channel_name( + const struct sockaddr_storage& remote_ss, uint64_t virtual_network_id) { + if (!virtual_network_id) { + if (remote_ss.ss_family == 0) { + return "__invalid_address__"; + } else { + return "ipv4:" + render_sockaddr_storage(remote_ss); + } + } else { + if (this->ip_stack_simulator) { + auto network = this->ip_stack_simulator->get_network(virtual_network_id); + int fd = bufferevent_getfd(network->bev.get()); + if (fd < 0) { + return string_printf("ipss:N-%" PRIu64 ":__unknown_address__", network->network_id); + } else { + struct sockaddr_storage remote_ss; + get_socket_addresses(fd, nullptr, &remote_ss); + string addr_str = render_sockaddr_storage(remote_ss); + return string_printf("ipss:N-%" PRIu64 ":%s", network->network_id, addr_str.c_str()); + } + } else { + return "__unknown_address__"; } } } diff --git a/src/ServerState.hh b/src/ServerState.hh index 4382f685..172959e1 100644 --- a/src/ServerState.hh +++ b/src/ServerState.hh @@ -387,4 +387,6 @@ struct ServerState : public std::enable_shared_from_this { static void dispatch_destroy_lobbies(evutil_socket_t, short, void* ctx); void disconnect_all_banned_clients(); + + std::string format_address_for_channel_name(const struct sockaddr_storage& remote_ss, uint64_t virtual_network); };