add remote address to command log messages

This commit is contained in:
Martin Michelsen
2024-04-21 15:19:16 -07:00
parent 245df782b9
commit d08aaef0f8
17 changed files with 266 additions and 168 deletions
+1 -1
View File
@@ -56,7 +56,7 @@ CatSession::CatSession(
if (!bev) { if (!bev) {
throw runtime_error(string_printf("failed to open socket (%d)", EVUTIL_SOCKET_ERROR())); 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(), if (bufferevent_socket_connect(this->channel.bev.get(),
reinterpret_cast<const sockaddr*>(&remote), sizeof(struct sockaddr_in)) != 0) { reinterpret_cast<const sockaddr*>(&remote), sizeof(struct sockaddr_in)) != 0) {
+8 -11
View File
@@ -32,6 +32,7 @@ Channel::Channel(
TerminalFormat terminal_send_color, TerminalFormat terminal_send_color,
TerminalFormat terminal_recv_color) TerminalFormat terminal_recv_color)
: bev(nullptr, flush_and_free_bufferevent), : bev(nullptr, flush_and_free_bufferevent),
virtual_network_id(0),
version(version), version(version),
language(language), language(language),
name(name), name(name),
@@ -44,6 +45,7 @@ Channel::Channel(
Channel::Channel( Channel::Channel(
struct bufferevent* bev, struct bufferevent* bev,
uint64_t virtual_network_id,
Version version, Version version,
uint8_t language, uint8_t language,
on_command_received_t on_command_received, on_command_received_t on_command_received,
@@ -61,7 +63,7 @@ Channel::Channel(
on_command_received(on_command_received), on_command_received(on_command_received),
on_error(on_error), on_error(on_error),
context_obj(context_obj) { context_obj(context_obj) {
this->set_bufferevent(bev); this->set_bufferevent(bev, virtual_network_id);
} }
void Channel::replace_with( void Channel::replace_with(
@@ -70,10 +72,9 @@ void Channel::replace_with(
on_error_t on_error, on_error_t on_error,
void* context_obj, void* context_obj,
const std::string& name) { 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->local_addr = other.local_addr;
this->remote_addr = other.remote_addr; this->remote_addr = other.remote_addr;
this->is_virtual_connection = other.is_virtual_connection;
this->version = other.version; this->version = other.version;
this->language = other.language; this->language = other.language;
this->crypt_in = other.crypt_in; this->crypt_in = other.crypt_in;
@@ -87,27 +88,23 @@ void Channel::replace_with(
other.disconnect(); // Clears crypts, addrs, etc. 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->bev.reset(bev);
this->virtual_network_id = virtual_network_id;
if (this->bev.get()) { if (this->bev.get()) {
int fd = bufferevent_getfd(this->bev.get()); int fd = bufferevent_getfd(this->bev.get());
if (fd < 0) { if (fd < 0) {
this->is_virtual_connection = true;
memset(&this->local_addr, 0, sizeof(this->local_addr)); memset(&this->local_addr, 0, sizeof(this->local_addr));
memset(&this->remote_addr, 0, sizeof(this->remote_addr)); memset(&this->remote_addr, 0, sizeof(this->remote_addr));
} else { } else {
this->is_virtual_connection = false;
get_socket_addresses(fd, &this->local_addr, &this->remote_addr); get_socket_addresses(fd, &this->local_addr, &this->remote_addr);
} }
bufferevent_setcb(this->bev.get(), bufferevent_setcb(this->bev.get(), &Channel::dispatch_on_input, nullptr, &Channel::dispatch_on_error, this);
&Channel::dispatch_on_input, nullptr,
&Channel::dispatch_on_error, this);
bufferevent_enable(this->bev.get(), EV_READ | EV_WRITE); bufferevent_enable(this->bev.get(), EV_READ | EV_WRITE);
} else { } else {
this->is_virtual_connection = false;
memset(&this->local_addr, 0, sizeof(this->local_addr)); memset(&this->local_addr, 0, sizeof(this->local_addr));
memset(&this->remote_addr, 0, sizeof(this->remote_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->local_addr, 0, sizeof(this->local_addr));
memset(&this->remote_addr, 0, sizeof(this->remote_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_in.reset();
this->crypt_out.reset(); this->crypt_out.reset();
} }
+3 -2
View File
@@ -13,7 +13,7 @@ struct Channel {
std::unique_ptr<struct bufferevent, void (*)(struct bufferevent*)> bev; std::unique_ptr<struct bufferevent, void (*)(struct bufferevent*)> bev;
struct sockaddr_storage local_addr; struct sockaddr_storage local_addr;
struct sockaddr_storage remote_addr; struct sockaddr_storage remote_addr;
bool is_virtual_connection; uint64_t virtual_network_id; // 0 = normal TCP connection
Version version; Version version;
uint8_t language; uint8_t language;
@@ -50,6 +50,7 @@ struct Channel {
// Creates a connected channel // Creates a connected channel
Channel( Channel(
struct bufferevent* bev, struct bufferevent* bev,
uint64_t virtual_network_id,
Version version, Version version,
uint8_t language, uint8_t language,
on_command_received_t on_command_received, on_command_received_t on_command_received,
@@ -70,7 +71,7 @@ struct Channel {
void* context_obj, void* context_obj,
const std::string& name = ""); 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 { inline bool connected() const {
return this->bev.get() != nullptr; return this->bev.get() != nullptr;
+18 -1
View File
@@ -11,6 +11,7 @@
#include <phosg/Network.hh> #include <phosg/Network.hh>
#include <phosg/Time.hh> #include <phosg/Time.hh>
#include "IPStackSimulator.hh"
#include "Loggers.hh" #include "Loggers.hh"
#include "Server.hh" #include "Server.hh"
#include "Version.hh" #include "Version.hh"
@@ -169,12 +170,13 @@ bool Client::Config::should_update_vs(const Config& other) const {
Client::Client( Client::Client(
shared_ptr<Server> server, shared_ptr<Server> server,
struct bufferevent* bev, struct bufferevent* bev,
uint64_t virtual_network_id,
Version version, Version version,
ServerBehavior server_behavior) ServerBehavior server_behavior)
: server(server), : server(server),
id(next_id++), id(next_id++),
log(string_printf("[C-%" PRIX64 "] ", this->id), client_log.min_level), 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), server_behavior(server_behavior),
should_disconnect(false), should_disconnect(false),
should_send_to_lobby_server(false), should_send_to_lobby_server(false),
@@ -214,6 +216,8 @@ Client::Client(
dol_base_addr(0), dol_base_addr(0),
external_bank_character_index(-1), external_bank_character_index(-1),
last_play_time_update(0) { last_play_time_update(0) {
this->update_channel_name();
this->config.set_flags_for_version(version, -1); this->config.set_flags_for_version(version, -1);
auto s = server->get_state(); 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) { 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"); 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() { void Client::reschedule_save_game_data_event() {
if (this->version() == Version::BB_V4) { if (this->version() == Version::BB_V4) {
struct timeval tv = usecs_to_timeval(60000000); // 1 minute struct timeval tv = usecs_to_timeval(60000000); // 1 minute
+3
View File
@@ -268,10 +268,13 @@ public:
Client( Client(
std::shared_ptr<Server> server, std::shared_ptr<Server> server,
struct bufferevent* bev, struct bufferevent* bev,
uint64_t virtual_network_id,
Version version, Version version,
ServerBehavior server_behavior); ServerBehavior server_behavior);
~Client(); ~Client();
void update_channel_name();
void reschedule_save_game_data_event(); void reschedule_save_game_data_event();
void reschedule_ping_and_timeout_events(); void reschedule_ping_and_timeout_events();
+46 -47
View File
@@ -124,6 +124,7 @@ IPStackSimulator::IPStackSimulator(
shared_ptr<ServerState> state) shared_ptr<ServerState> state)
: base(base), : base(base),
state(state), state(state),
next_network_id(1),
pcap_text_log_file(state->ip_stack_debug ? fopen("IPStackSimulator-Log.txt", "wt") : nullptr) { pcap_text_log_file(state->ip_stack_debug ? fopen("IPStackSimulator-Log.txt", "wt") : nullptr) {
this->host_mac_address_bytes.clear(0x90); this->host_mac_address_bytes.clear(0x90);
this->broadcast_mac_address_bytes.clear(0xFF); 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))); this->listening_sockets.emplace(piecewise_construct, forward_as_tuple(fd), forward_as_tuple(name, proto, std::move(l)));
} }
shared_ptr<IPStackSimulator::IPClient> 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) { 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 // 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 // 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<IPStackSimulator> sim, Protocol protocol, struct bufferevent* bev) IPStackSimulator::IPClient::IPClient(
shared_ptr<IPStackSimulator> sim, uint64_t network_id, Protocol protocol, struct bufferevent* bev)
: sim(sim), : sim(sim),
network_id(network_id),
bev(bev, bufferevent_free), bev(bev, bufferevent_free),
protocol(protocol), protocol(protocol),
mac_addr(0), mac_addr(0),
@@ -200,7 +207,7 @@ void IPStackSimulator::IPClient::on_idle_timeout() {
auto sim = this->sim.lock(); auto sim = this->sim.lock();
if (sim) { if (sim) {
ip_stack_simulator_log.info("Idle timeout expired on virtual network %d", bufferevent_getfd(this->bev.get())); 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 { } else {
ip_stack_simulator_log.info("Idle timeout expired on virtual network %d, but simulator is missing", bufferevent_getfd(this->bev.get())); 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_received(0),
bytes_sent(0) {} bytes_sent(0) {}
void IPStackSimulator::disconnect_client(struct bufferevent* bev) { void IPStackSimulator::disconnect_client(uint64_t network_id) {
ip_stack_simulator_log.info("Virtual network %d disconnected", bufferevent_getfd(bev)); ip_stack_simulator_log.info("Virtual network N-%" PRIu64 " disconnected", network_id);
this->bev_to_client.erase(bev); this->network_id_to_client.erase(network_id);
} }
void IPStackSimulator::dispatch_on_listen_accept( void IPStackSimulator::dispatch_on_listen_accept(
@@ -252,20 +259,20 @@ void IPStackSimulator::on_listen_accept(struct evconnlistener* listener, evutil_
try { try {
listening_socket = &this->listening_sockets.at(listen_fd); listening_socket = &this->listening_sockets.at(listen_fd);
} catch (const out_of_range&) { } 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); close(fd);
return; 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, struct bufferevent* bev = bufferevent_socket_new(this->base.get(), fd, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS); auto c = make_shared<IPClient>(this->shared_from_this(), network_id, listening_socket->protocol, bev);
auto c = make_shared<IPClient>(this->shared_from_this(), listening_socket->protocol, bev); this->network_id_to_client.emplace(c->network_id, c);
this->bev_to_client.emplace(make_pair(bev, c));
bufferevent_setcb(bev, &IPStackSimulator::dispatch_on_client_input, nullptr, bufferevent_setcb(bev, &IPStackSimulator::IPClient::dispatch_on_client_input, nullptr,
&IPStackSimulator::dispatch_on_client_error, this); &IPStackSimulator::IPClient::dispatch_on_client_error, c.get());
bufferevent_enable(bev, EV_READ | EV_WRITE); 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); event_base_loopexit(this->base.get(), nullptr);
} }
void IPStackSimulator::dispatch_on_client_input( void IPStackSimulator::IPClient::dispatch_on_client_input(struct bufferevent* bev, void* ctx) {
struct bufferevent* bev, void* ctx) { reinterpret_cast<IPClient*>(ctx)->on_client_input(bev);
reinterpret_cast<IPStackSimulator*>(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); struct evbuffer* buf = bufferevent_get_input(bev);
shared_ptr<IPClient> c; auto sim = this->sim.lock();
try { if (!sim) {
c = this->bev_to_client.at(bev);
} catch (const out_of_range&) {
size_t bytes = evbuffer_get_length(buf); size_t bytes = evbuffer_get_length(buf);
ip_stack_simulator_log.warning("Ignoring data received from unregistered virtual network (0x%zX bytes)", ip_stack_simulator_log.warning("Ignoring data from unregistered virtual network (0x%zX bytes)", bytes);
bytes);
evbuffer_drain(buf, bytes); evbuffer_drain(buf, bytes);
return; return;
} }
auto sim = c->sim.lock();
uint64_t idle_timeout_usecs = sim ? sim->state->client_idle_timeout_usecs : 60000000; uint64_t idle_timeout_usecs = sim ? sim->state->client_idle_timeout_usecs : 60000000;
struct timeval tv = usecs_to_timeval(idle_timeout_usecs); 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::ETHERNET_TAPSERVER:
case Protocol::HDLC_TAPSERVER: case Protocol::HDLC_TAPSERVER:
while (evbuffer_get_length(buf) >= 2) { 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()); evbuffer_remove(buf, frame.data(), frame.size());
try { try {
this->on_client_frame(c, frame); sim->on_client_frame(this->shared_from_this(), frame);
} catch (const exception& e) { } catch (const exception& e) {
if (ip_stack_simulator_log.warning("Failed to process frame: %s", e.what())) { if (ip_stack_simulator_log.warning("Failed to process frame: %s", e.what())) {
print_data(stderr, frame); print_data(stderr, frame);
@@ -355,7 +357,7 @@ void IPStackSimulator::on_client_input(struct bufferevent* bev) {
evbuffer_remove(buf, frame.data(), frame.size()); evbuffer_remove(buf, frame.data(), frame.size());
try { try {
this->on_client_frame(c, frame); sim->on_client_frame(this->shared_from_this(), frame);
} catch (const exception& e) { } catch (const exception& e) {
if (ip_stack_simulator_log.warning("Failed to process frame: %s", e.what())) { if (ip_stack_simulator_log.warning("Failed to process frame: %s", e.what())) {
print_data(stderr, frame); print_data(stderr, frame);
@@ -366,18 +368,19 @@ void IPStackSimulator::on_client_input(struct bufferevent* bev) {
} }
} }
void IPStackSimulator::dispatch_on_client_error( void IPStackSimulator::IPClient::dispatch_on_client_error(struct bufferevent* bev, short events, void* ctx) {
struct bufferevent* bev, short events, void* ctx) { reinterpret_cast<IPClient*>(ctx)->on_client_error(bev, events);
reinterpret_cast<IPStackSimulator*>(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) { if (events & BEV_EVENT_ERROR) {
int err = EVUTIL_SOCKET_ERROR(); int err = EVUTIL_SOCKET_ERROR();
ip_stack_simulator_log.warning("Virtual network caused error %d (%s)", err, ip_stack_simulator_log.warning("Virtual network caused error %d (%s)", err, evutil_socket_error_to_string(err));
evutil_socket_error_to_string(err));
} }
if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) { 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<IPClient> c, IPClient::
string conn_str = this->str_for_tcp_connection(c, conn); string conn_str = this->str_for_tcp_connection(c, conn);
if (port_config->behavior == ServerBehavior::PROXY_SERVER) { if (port_config->behavior == ServerBehavior::PROXY_SERVER) {
if (!this->state->proxy_server.get()) { if (!this->state->proxy_server.get()) {
ip_stack_simulator_log.error("TCP connection %s is to non-running proxy server", ip_stack_simulator_log.error("TCP connection %s is to non-running proxy server", conn_str.c_str());
conn_str.c_str());
flush_and_free_bufferevent(bevs[1]); flush_and_free_bufferevent(bevs[1]);
} else { } else {
this->state->proxy_server->connect_client(bevs[1], conn.server_port); 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", ip_stack_simulator_log.info("Connected TCP connection %s to proxy server", conn_str.c_str());
conn_str.c_str());
} }
} else if (this->state->game_server.get()) { } else if (this->state->game_server.get()) {
this->state->game_server->connect_client(bevs[1], c->ipv4_addr, this->state->game_server->connect_virtual_client(
conn.client_port, conn.server_port, port_config->version, bevs[1], c->network_id, c->ipv4_addr, conn.client_port,
port_config->behavior); conn.server_port, port_config->version, port_config->behavior);
ip_stack_simulator_log.info("Connected TCP connection %s to game server", ip_stack_simulator_log.info("Connected TCP connection %s to game server", conn_str.c_str());
conn_str.c_str());
} else { } else {
ip_stack_simulator_log.error("No server available for TCP connection %s", ip_stack_simulator_log.error("No server available for TCP connection %s", conn_str.c_str());
conn_str.c_str());
flush_and_free_bufferevent(bevs[1]); flush_and_free_bufferevent(bevs[1]);
} }
} }
+14 -11
View File
@@ -27,8 +27,9 @@ public:
using unique_evbuffer = std::unique_ptr<struct evbuffer, void (*)(struct evbuffer*)>; using unique_evbuffer = std::unique_ptr<struct evbuffer, void (*)(struct evbuffer*)>;
using unique_event = std::unique_ptr<struct event, void (*)(struct event*)>; using unique_event = std::unique_ptr<struct event, void (*)(struct event*)>;
struct IPClient { struct IPClient : std::enable_shared_from_this<IPClient> {
std::weak_ptr<IPStackSimulator> sim; std::weak_ptr<IPStackSimulator> sim;
uint64_t network_id;
unique_bufferevent bev; unique_bufferevent bev;
Protocol protocol; Protocol protocol;
@@ -70,7 +71,12 @@ public:
unique_event idle_timeout_event; unique_event idle_timeout_event;
IPClient(std::shared_ptr<IPStackSimulator> sim, Protocol protocol, struct bufferevent* bev); IPClient(std::shared_ptr<IPStackSimulator> 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); static void dispatch_on_idle_timeout(evutil_socket_t fd, short events, void* ctx);
void on_idle_timeout(); void on_idle_timeout();
@@ -88,15 +94,17 @@ public:
static uint32_t connect_address_for_remote_address(uint32_t remote_addr); static uint32_t connect_address_for_remote_address(uint32_t remote_addr);
inline const std::unordered_map<struct bufferevent*, std::shared_ptr<IPClient>>& all_clients() const { std::shared_ptr<IPClient> get_network(uint64_t network_id) const;
return this->bev_to_client; inline const std::unordered_map<uint64_t, std::shared_ptr<IPClient>>& all_networks() const {
return this->network_id_to_client;
} }
void disconnect_client(struct bufferevent* bev); void disconnect_client(uint64_t network_id);
private: private:
std::shared_ptr<struct event_base> base; std::shared_ptr<struct event_base> base;
std::shared_ptr<ServerState> state; std::shared_ptr<ServerState> state;
uint64_t next_network_id;
struct ListeningSocket { struct ListeningSocket {
std::string name; std::string name;
@@ -110,7 +118,7 @@ private:
}; };
std::unordered_map<int, ListeningSocket> listening_sockets; std::unordered_map<int, ListeningSocket> listening_sockets;
std::unordered_map<struct bufferevent*, std::shared_ptr<IPClient>> bev_to_client; std::unordered_map<uint64_t, std::shared_ptr<IPClient>> network_id_to_client;
parray<uint8_t, 6> host_mac_address_bytes; parray<uint8_t, 6> host_mac_address_bytes;
parray<uint8_t, 6> broadcast_mac_address_bytes; parray<uint8_t, 6> broadcast_mac_address_bytes;
@@ -130,11 +138,6 @@ private:
static void dispatch_on_listen_error(struct evconnlistener* listener, void* ctx); static void dispatch_on_listen_error(struct evconnlistener* listener, void* ctx);
void on_listen_error(struct evconnlistener* listener); 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<IPClient> c, FrameInfo::Protocol proto, const std::string& data) const; void send_layer3_frame(std::shared_ptr<IPClient> c, FrameInfo::Protocol proto, const std::string& data) const;
void send_layer3_frame(std::shared_ptr<IPClient> c, FrameInfo::Protocol proto, const void* data, size_t size) const; void send_layer3_frame(std::shared_ptr<IPClient> c, FrameInfo::Protocol proto, const void* data, size_t size) const;
+2 -2
View File
@@ -10,6 +10,7 @@
#include <memory> #include <memory>
#include <phosg/Encoding.hh> #include <phosg/Encoding.hh>
#include <phosg/Network.hh>
#include <phosg/Strings.hh> #include <phosg/Strings.hh>
#include <stdexcept> #include <stdexcept>
@@ -23,8 +24,7 @@ uint32_t resolve_address(const char* address) {
"can\'t resolve hostname %s: %s", address, e.c_str())); "can\'t resolve hostname %s: %s", address, e.c_str()));
} }
std::unique_ptr<struct addrinfo, void (*)(struct addrinfo*)> res0_unique( std::unique_ptr<struct addrinfo, void (*)(struct addrinfo*)> res0_unique(res0, freeaddrinfo);
res0, freeaddrinfo);
struct addrinfo* res4 = nullptr; struct addrinfo* res4 = nullptr;
for (struct addrinfo* res = res0; res; res = res->ai_next) { for (struct addrinfo* res = res0; res; res = res->ai_next) {
if (res->ai_family == AF_INET) { if (res->ai_family == AF_INET) {
+5 -5
View File
@@ -38,7 +38,7 @@ PatchServer::Client::Client(
: server(server), : server(server),
id(next_id++), id(next_id++),
log(string_printf("[C-%" PRIX64 "] ", this->id), client_log.min_level), 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_usecs(idle_timeout_usecs),
idle_timeout_event( idle_timeout_event(
event_new(bufferevent_get_base(bev), -1, EV_TIMEOUT, &PatchServer::Client::dispatch_idle_timeout, this), 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<Client> c, string&) {
} }
void PatchServer::disconnect_client(shared_ptr<Client> c) { void PatchServer::disconnect_client(shared_ptr<Client> c) {
if (c->channel.is_virtual_connection) { if (c->channel.virtual_network_id) {
server_log.info("Client disconnected: C-%" PRIX64 " on virtual connection %p", c->id, c->channel.bev.get()); server_log.info("Client disconnected: C-%" PRIX64 " on N-%" PRIu64, c->id, c->channel.virtual_network_id);
} else if (c->channel.bev) { } 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 { } 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); this->channel_to_client.erase(&c->channel);
+78 -49
View File
@@ -41,7 +41,8 @@ ProxyServer::ProxyServer(
: base(base), : base(base),
destroy_sessions_ev(event_new(this->base.get(), -1, EV_TIMEOUT, &ProxyServer::dispatch_destroy_sessions, this), event_free), destroy_sessions_ev(event_new(this->base.get(), -1, EV_TIMEOUT, &ProxyServer::dispatch_destroy_sessions, this), event_free),
state(state), 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) { void ProxyServer::listen(const std::string& addr, uint16_t port, Version version, const struct sockaddr_storage* default_destination) {
auto socket_obj = make_shared<ListeningSocket>(this, addr, port, version, default_destination); auto socket_obj = make_shared<ListeningSocket>(this, addr, port, version, default_destination);
@@ -57,7 +58,7 @@ ProxyServer::ListeningSocket::ListeningSocket(
Version version, Version version,
const struct sockaddr_storage* default_destination) const struct sockaddr_storage* default_destination)
: server(server), : 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), port(port),
fd(::listen(addr, port, SOMAXCONN)), fd(::listen(addr, port, SOMAXCONN)),
listener(nullptr, evconnlistener_free), 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)); 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); 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); (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); 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. // 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) // We don't support default-destination proxying for virtual connections (yet)
Version version; Version version;
try { try {
version = this->listeners.at(server_port)->version; version = this->listeners.at(server_port)->version;
} catch (const out_of_range&) { } catch (const out_of_range&) {
proxy_server_log.info("Virtual connection received on unregistered port %hu; closing it", proxy_server_log.info("Virtual connection received on unregistered port %hu; closing it", server_port);
server_port);
bufferevent_flush(bev, EV_READ | EV_WRITE, BEV_FINISHED); bufferevent_flush(bev, EV_READ | EV_WRITE, BEV_FINISHED);
bufferevent_free(bev); bufferevent_free(bev);
return; return;
} }
proxy_server_log.info("Client connected on virtual connection %p (port %hu)", bev, proxy_server_log.info("Client connected on virtual connection %p (port %hu)", bev, server_port);
server_port); this->on_client_connect(bev, virtual_network_id, server_port, version, nullptr);
this->on_client_connect(bev, server_port, version, nullptr);
} }
void ProxyServer::on_client_connect( void ProxyServer::on_client_connect(
struct bufferevent* bev, struct bufferevent* bev,
uint64_t virtual_network_id,
uint16_t listen_port, uint16_t listen_port,
Version version, Version version,
const struct sockaddr_storage* default_destination) { const struct sockaddr_storage* default_destination) {
@@ -148,25 +148,31 @@ void ProxyServer::on_client_connect(
// server. This creates a direct session. // server. This creates a direct session.
if (default_destination && is_patch(version)) { if (default_destination && is_patch(version)) {
uint64_t session_id = this->next_logged_out_session_id++; uint64_t session_id = this->next_logged_out_session_id++;
if (this->next_logged_out_session_id == 0) { if (this->next_logged_out_session_id == this->MIN_UNLINKED_SESSION_ID) {
this->next_logged_out_session_id = 0xFF00000000000001; 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<LinkedSession>(this->shared_from_this(), session_id, listen_port, version, *default_destination)); auto emplace_ret = this->id_to_linked_session.emplace(session_id, make_shared<LinkedSession>(this->shared_from_this(), session_id, listen_port, version, *default_destination));
if (!emplace_ret.second) { if (!emplace_ret.second) {
throw logic_error("linked session already exists for logged-out client"); throw logic_error("linked session already exists for logged-out client");
} }
auto ses = emplace_ret.first->second; auto ses = emplace_ret.first->second;
ses->log.info("Opened linked session"); 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)); ses->resume(std::move(ch));
} else {
// If no default destination exists, or the client is not a patch client, // 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 // 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. // client's config, which we'll get via a 9E command soon.
} else { uint64_t session_id = this->next_unlinked_session_id++;
auto emplace_ret = this->bev_to_unlinked_session.emplace(bev, make_shared<UnlinkedSession>(this->shared_from_this(), bev, listen_port, version)); 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<UnlinkedSession>(this->shared_from_this(), session_id, bev, virtual_network_id, listen_port, version));
if (!emplace_ret.second) { if (!emplace_ret.second) {
throw logic_error("stale unlinked session exists"); throw logic_error("stale unlinked session exists");
} }
@@ -235,22 +241,28 @@ void ProxyServer::on_client_connect(
ProxyServer::UnlinkedSession::UnlinkedSession( ProxyServer::UnlinkedSession::UnlinkedSession(
shared_ptr<ProxyServer> server, shared_ptr<ProxyServer> server,
uint64_t id,
struct bufferevent* bev, struct bufferevent* bev,
uint64_t virtual_network_id,
uint16_t local_port, uint16_t local_port,
Version version) Version version)
: server(server), : 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( channel(
bev, bev,
virtual_network_id,
version, version,
1, 1,
ProxyServer::UnlinkedSession::on_input, ProxyServer::UnlinkedSession::on_input,
ProxyServer::UnlinkedSession::on_error, ProxyServer::UnlinkedSession::on_error,
this, this,
string_printf("UnlinkedSession:%p", bev), "",
TerminalFormat::FG_YELLOW, TerminalFormat::FG_YELLOW,
TerminalFormat::FG_GREEN), TerminalFormat::FG_GREEN),
local_port(local_port) { 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)); 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; should_close_unlinked_session = true;
} }
// Note that ch.bev will be moved from when the linked session is resumed, so uint64_t unlinked_session_id = ses->id;
// we need to retain a copy of it in order to close the unlinked session
// afterward.
struct bufferevent* session_key = ch.bev.get();
// If login is present, then the client has credentials and can be connected // If login is present, then the client has credentials and can be connected
// to the remote lobby server. // 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) // Look up the linked session for this account (if any)
shared_ptr<LinkedSession> linked_ses; shared_ptr<LinkedSession> linked_ses;
try { 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"); linked_ses->log.info("Resuming linked session from unlinked session");
} catch (const out_of_range&) { } catch (const out_of_range&) {
@@ -447,7 +456,7 @@ void ProxyServer::UnlinkedSession::on_input(Channel& ch, uint16_t command, uint3
} }
if (linked_ses.get()) { 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 // Resume the linked session using the unlinked session
try { try {
if (ses->version() == Version::BB_V4) { 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) { 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)) { if (events & (BEV_EVENT_ERROR | BEV_EVENT_EOF)) {
ses->log.info("Client has disconnected"); 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) Version version)
: server(server), : server(server),
id(id), 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), timeout_event(event_new(server->base.get(), -1, EV_TIMEOUT, &LinkedSession::dispatch_on_timeout, this), event_free),
login(nullptr), login(nullptr),
client_channel( client_channel(
@@ -506,7 +515,7 @@ ProxyServer::LinkedSession::LinkedSession(
nullptr, nullptr,
nullptr, nullptr,
this, this,
string_printf("LinkedSession:%08" PRIX64 ":client", this->id), string_printf("LS-%" PRIX64 "-C", this->id),
TerminalFormat::FG_YELLOW, TerminalFormat::FG_YELLOW,
TerminalFormat::FG_GREEN), TerminalFormat::FG_GREEN),
server_channel( server_channel(
@@ -515,7 +524,7 @@ ProxyServer::LinkedSession::LinkedSession(
nullptr, nullptr,
nullptr, nullptr,
this, this,
string_printf("LinkedSession:%08" PRIX64 ":server", this->id), string_printf("LS-%" PRIX64 "-S", this->id),
TerminalFormat::FG_YELLOW, TerminalFormat::FG_YELLOW,
TerminalFormat::FG_RED), TerminalFormat::FG_RED),
local_port(local_port), 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"); 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( this->client_channel.replace_with(
std::move(client_channel), std::move(client_channel),
ProxyServer::LinkedSession::on_input, ProxyServer::LinkedSession::on_input,
ProxyServer::LinkedSession::on_error, ProxyServer::LinkedSession::on_error,
this, this,
string_printf("LinkedSession:%08" PRIX64 ":client", this->id)); "");
this->server_channel.language = this->client_channel.language; this->server_channel.language = this->client_channel.language;
this->server_channel.version = this->client_channel.version; 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); string netloc_str = render_sockaddr_storage(this->next_destination);
this->log.info("Connecting to %s", netloc_str.c_str()); this->log.info("Connecting to %s", netloc_str.c_str());
this->server_channel.set_bufferevent(bufferevent_socket_new( this->server_channel.set_bufferevent(
this->require_server()->base.get(), -1, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS)); 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(), if (bufferevent_socket_connect(this->server_channel.bev.get(),
reinterpret_cast<const sockaddr*>(dest_sin), sizeof(*dest_sin)) != 0) { reinterpret_cast<const sockaddr*>(dest_sin), sizeof(*dest_sin)) != 0) {
throw runtime_error(string_printf("failed to connect (%d)", EVUTIL_SOCKET_ERROR())); 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.on_error = ProxyServer::LinkedSession::on_error;
this->server_channel.context_obj = this; this->server_channel.context_obj = this;
this->update_channel_names();
// Cancel the session delete timeout // Cancel the session delete timeout
event_del(this->timeout_event.get()); 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( ProxyServer::LinkedSession::SavingFile::SavingFile(
const string& basename, const string& basename,
const string& output_filename, const string& output_filename,
@@ -704,6 +729,10 @@ void ProxyServer::LinkedSession::on_error(Channel& ch, short events) {
if (events & BEV_EVENT_CONNECTED) { if (events & BEV_EVENT_CONNECTED) {
ses->log.info("%s channel connected", is_server_stream ? "Server" : "Client"); 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()))) { 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); 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 // 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 // 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<struct sockaddr_in*>(&this->next_destination); struct sockaddr_in* dest_sin = reinterpret_cast<struct sockaddr_in*>(&this->next_destination);
if (dest_sin->sin_family != AF_INET) { if (dest_sin->sin_family != AF_INET) {
throw logic_error("ss not 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::LinkedSession> ProxyServer::get_session() const { shared_ptr<ProxyServer::LinkedSession> ProxyServer::get_session() const {
if (this->id_to_session.empty()) { if (this->id_to_linked_session.empty()) {
throw runtime_error("no sessions exist"); 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"); throw runtime_error("multiple sessions exist");
} }
return this->id_to_session.begin()->second; return this->id_to_linked_session.begin()->second;
} }
shared_ptr<ProxyServer::LinkedSession> ProxyServer::get_session_by_name(const std::string& name) const { shared_ptr<ProxyServer::LinkedSession> ProxyServer::get_session_by_name(const std::string& name) const {
try { try {
uint64_t session_id = stoull(name, nullptr, 16); 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&) { } catch (const invalid_argument&) {
throw runtime_error("invalid session name"); throw runtime_error("invalid session name");
} catch (const out_of_range&) { } catch (const out_of_range&) {
@@ -938,7 +967,7 @@ shared_ptr<ProxyServer::LinkedSession> ProxyServer::get_session_by_name(const st
} }
const unordered_map<uint64_t, shared_ptr<ProxyServer::LinkedSession>>& ProxyServer::all_sessions() const { const unordered_map<uint64_t, shared_ptr<ProxyServer::LinkedSession>>& ProxyServer::all_sessions() const {
return this->id_to_session; return this->id_to_linked_session;
} }
shared_ptr<ProxyServer::LinkedSession> ProxyServer::create_logged_in_session( shared_ptr<ProxyServer::LinkedSession> ProxyServer::create_logged_in_session(
@@ -947,7 +976,7 @@ shared_ptr<ProxyServer::LinkedSession> ProxyServer::create_logged_in_session(
Version version, Version version,
const Client::Config& config) { const Client::Config& config) {
auto session = make_shared<LinkedSession>(this->shared_from_this(), local_port, version, login, config); auto session = make_shared<LinkedSession>(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) { if (!emplace_ret.second) {
throw runtime_error("session already exists for this account"); throw runtime_error("session already exists for this account");
} }
@@ -956,23 +985,23 @@ shared_ptr<ProxyServer::LinkedSession> ProxyServer::create_logged_in_session(
} }
void ProxyServer::delete_session(uint64_t id) { void ProxyServer::delete_session(uint64_t id) {
if (this->id_to_session.erase(id)) { if (id < this->MIN_UNLINKED_SESSION_ID) {
proxy_server_log.info("Closed LinkedSession:%08" PRIX64, 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);
void ProxyServer::delete_session(struct bufferevent* bev) { if (it == this->id_to_unlinked_session.end()) {
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"); throw logic_error("unlinked session exists but is not registered");
} }
it->second->log.info("Closing session"); it->second->log.info("Closing session");
this->unlinked_sessions_to_destroy.emplace(std::move(it->second)); this->unlinked_sessions_to_destroy.emplace(std::move(it->second));
this->bev_to_unlinked_session.erase(it); this->id_to_unlinked_session.erase(it);
auto tv = usecs_to_timeval(0); auto tv = usecs_to_timeval(0);
event_add(this->destroy_sessions_ev.get(), &tv); event_add(this->destroy_sessions_ev.get(), &tv);
} }
}
void ProxyServer::dispatch_destroy_sessions(evutil_socket_t, short, void* ctx) { void ProxyServer::dispatch_destroy_sessions(evutil_socket_t, short, void* ctx) {
reinterpret_cast<ProxyServer*>(ctx)->destroy_sessions(); reinterpret_cast<ProxyServer*>(ctx)->destroy_sessions();
@@ -983,14 +1012,14 @@ void ProxyServer::destroy_sessions() {
} }
size_t ProxyServer::num_sessions() const { 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 ProxyServer::delete_disconnected_sessions() {
size_t count = 0; 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()) { if (!it->second->is_connected()) {
it = this->id_to_session.erase(it); it = this->id_to_linked_session.erase(it);
count++; count++;
} else { } else {
it++; it++;
+18 -5
View File
@@ -28,7 +28,7 @@ public:
void listen(const std::string& addr, uint16_t port, Version version, const struct sockaddr_storage* default_destination = nullptr); 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<LinkedSession> { struct LinkedSession : std::enable_shared_from_this<LinkedSession> {
std::weak_ptr<ProxyServer> server; std::weak_ptr<ProxyServer> server;
@@ -186,6 +186,8 @@ public:
static void on_error(Channel& ch, short events); static void on_error(Channel& ch, short events);
void on_timeout(); void on_timeout();
void update_channel_names();
void clear_lobby_players(size_t num_slots); void clear_lobby_players(size_t num_slots);
void set_drop_mode(DropMode new_mode); void set_drop_mode(DropMode new_mode);
@@ -205,7 +207,6 @@ public:
Version version, Version version,
const Client::Config& config); const Client::Config& config);
void delete_session(uint64_t id); void delete_session(uint64_t id);
void delete_session(struct bufferevent* bev);
size_t num_sessions() const; size_t num_sessions() const;
@@ -238,6 +239,7 @@ private:
struct UnlinkedSession { struct UnlinkedSession {
std::weak_ptr<ProxyServer> server; std::weak_ptr<ProxyServer> server;
uint64_t id;
PrefixedLogger log; PrefixedLogger log;
Channel channel; Channel channel;
@@ -259,7 +261,13 @@ private:
XBNetworkLocation xb_netloc; XBNetworkLocation xb_netloc;
parray<le_uint32_t, 3> xb_9E_unknown_a1a; parray<le_uint32_t, 3> xb_9E_unknown_a1a;
UnlinkedSession(std::shared_ptr<ProxyServer> server, struct bufferevent* bev, uint16_t port, Version version); UnlinkedSession(
std::shared_ptr<ProxyServer> server,
uint64_t id,
struct bufferevent* bev,
uint64_t virtual_network_id,
uint16_t port,
Version version);
std::shared_ptr<ProxyServer> require_server() const; std::shared_ptr<ProxyServer> require_server() const;
std::shared_ptr<ServerState> require_server_state() const; std::shared_ptr<ServerState> require_server_state() const;
@@ -278,9 +286,10 @@ private:
std::shared_ptr<struct event> destroy_sessions_ev; std::shared_ptr<struct event> destroy_sessions_ev;
std::shared_ptr<ServerState> state; std::shared_ptr<ServerState> state;
std::map<int, std::shared_ptr<ListeningSocket>> listeners; std::map<int, std::shared_ptr<ListeningSocket>> listeners;
std::unordered_map<struct bufferevent*, std::shared_ptr<UnlinkedSession>> bev_to_unlinked_session; std::unordered_map<uint64_t, std::shared_ptr<UnlinkedSession>> id_to_unlinked_session;
std::unordered_set<std::shared_ptr<UnlinkedSession>> unlinked_sessions_to_destroy; std::unordered_set<std::shared_ptr<UnlinkedSession>> unlinked_sessions_to_destroy;
std::unordered_map<uint64_t, std::shared_ptr<LinkedSession>> id_to_session; std::unordered_map<uint64_t, std::shared_ptr<LinkedSession>> id_to_linked_session;
uint64_t next_unlinked_session_id;
uint64_t next_logged_out_session_id; uint64_t next_logged_out_session_id;
static void dispatch_destroy_sessions(evutil_socket_t, short, void* ctx); static void dispatch_destroy_sessions(evutil_socket_t, short, void* ctx);
@@ -288,7 +297,11 @@ private:
void on_client_connect( void on_client_connect(
struct bufferevent* bev, struct bufferevent* bev,
uint64_t virtual_network_id,
uint16_t listen_port, uint16_t listen_port,
Version version, Version version,
const struct sockaddr_storage* default_destination); 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;
}; };
+1 -2
View File
@@ -3114,8 +3114,7 @@ static void on_61_98(shared_ptr<Client> c, uint16_t command, uint32_t flag, stri
c->channel.language = player->inventory.language; c->channel.language = player->inventory.language;
c->login->account->save(); c->login->account->save();
string name_str = player->disp.name.decode(c->language()); c->update_channel_name();
c->channel.name = string_printf("C-%" PRIX64 " (%s)", c->id, name_str.c_str());
// 98 should only be sent when leaving a game, and we should leave the client // 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). // in no lobby (they will send an 84 soon afterward to choose a lobby).
+2 -2
View File
@@ -630,7 +630,7 @@ void ReplaySession::execute_pending_events() {
struct bufferevent* bevs[2]; struct bufferevent* bevs[2];
bufferevent_pair_new(this->base.get(), 0, bevs); 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); this->channel_to_client.emplace(&c->channel, c);
shared_ptr<const PortConfiguration> port_config; shared_ptr<const PortConfiguration> port_config;
@@ -645,7 +645,7 @@ void ReplaySession::execute_pending_events() {
// TODO: We should support this at some point in the future // 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)); 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()) { } 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); 1025, c->port, port_config->version, port_config->behavior);
} else { } else {
throw runtime_error(string_printf("(ev-line %zu) no server available for connection", this->first_event->line_num)); throw runtime_error(string_printf("(ev-line %zu) no server available for connection", this->first_event->line_num));
+16 -11
View File
@@ -28,10 +28,10 @@ using namespace std;
using namespace std::placeholders; using namespace std::placeholders;
void Server::disconnect_client(shared_ptr<Client> c) { void Server::disconnect_client(shared_ptr<Client> c) {
if (c->channel.is_virtual_connection) { if (c->channel.virtual_network_id) {
server_log.info("Client disconnected: C-%" PRIX64 " on virtual connection %p", c->id, c->channel.bev.get()); server_log.info("Client disconnected: C-%" PRIX64 " on N-%" PRIu64, c->id, c->channel.virtual_network_id);
} else if (c->channel.bev) { } 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 { } else {
server_log.info("Client C-%" PRIX64 " removed from game server", c->id); 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); struct bufferevent* bev = bufferevent_socket_new(this->base.get(), fd, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
auto c = make_shared<Client>(this->shared_from_this(), bev, listening_socket->version, listening_socket->behavior); auto c = make_shared<Client>(this->shared_from_this(), bev, 0, listening_socket->version, listening_socket->behavior);
c->channel.on_command_received = Server::on_client_input; c->channel.on_command_received = Server::on_client_input;
c->channel.on_error = Server::on_client_error; c->channel.on_error = Server::on_client_error;
c->channel.context_obj = this; 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( void Server::connect_virtual_client(
struct bufferevent* bev, uint32_t address, uint16_t client_port, struct bufferevent* bev,
uint16_t server_port, Version version, ServerBehavior initial_state) { uint64_t virtual_network_id,
auto c = make_shared<Client>(this->shared_from_this(), bev, version, initial_state); uint32_t address,
uint16_t client_port,
uint16_t server_port,
Version version,
ServerBehavior initial_state) {
auto c = make_shared<Client>(this->shared_from_this(), bev, virtual_network_id, version, initial_state);
c->channel.on_command_received = Server::on_client_input; c->channel.on_command_received = Server::on_client_input;
c->channel.on_error = Server::on_client_error; c->channel.on_error = Server::on_client_error;
c->channel.context_obj = this; c->channel.context_obj = this;
this->state->channel_to_client.emplace(&c->channel, c); this->state->channel_to_client.emplace(&c->channel, c);
server_log.info( 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, c->id,
bev, virtual_network_id,
server_port, server_port,
name_for_enum(version), name_for_enum(version),
name_for_enum(initial_state)); name_for_enum(initial_state));
@@ -161,7 +166,7 @@ void Server::connect_client(
} }
} }
void Server::connect_client(shared_ptr<Client> c, Channel&& ch) { void Server::connect_virtual_client(shared_ptr<Client> 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)); 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); this->state->channel_to_client.emplace(&c->channel, c);
server_log.info("Client C-%" PRIX64 " added to game server", c->id); server_log.info("Client C-%" PRIX64 " added to game server", c->id);
+9 -4
View File
@@ -23,10 +23,15 @@ public:
void listen(const std::string& addr_str, int port, Version version, ServerBehavior initial_state); 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 add_socket(const std::string& addr_str, int fd, Version version, ServerBehavior initial_state);
void connect_client(struct bufferevent* bev, uint32_t address, void connect_virtual_client(
uint16_t client_port, uint16_t server_port, struct bufferevent* bev,
Version version, ServerBehavior initial_state); uint64_t virtual_network_id,
void connect_client(std::shared_ptr<Client> c, Channel&& ch); uint32_t address,
uint16_t client_port,
uint16_t server_port,
Version version,
ServerBehavior initial_state);
void connect_virtual_client(std::shared_ptr<Client> c, Channel&& ch);
void disconnect_client(std::shared_ptr<Client> c); void disconnect_client(std::shared_ptr<Client> c);
std::shared_ptr<Client> get_client() const; std::shared_ptr<Client> get_client() const;
+34 -9
View File
@@ -247,13 +247,12 @@ shared_ptr<Client> ServerState::find_client(const string* identifier, uint64_t a
} }
uint32_t ServerState::connect_address_for_client(shared_ptr<Client> c) const { uint32_t ServerState::connect_address_for_client(shared_ptr<Client> c) const {
if (c->channel.is_virtual_connection) { if (c->channel.virtual_network_id) {
if (c->channel.remote_addr.ss_family != AF_INET) { if (c->channel.remote_addr.ss_family != AF_INET) {
throw logic_error("virtual connection is missing remote IPv4 address"); throw logic_error("virtual connection is missing remote IPv4 address");
} }
const auto* sin = reinterpret_cast<const sockaddr_in*>(&c->channel.remote_addr); const auto* sin = reinterpret_cast<const sockaddr_in*>(&c->channel.remote_addr);
return IPStackSimulator::connect_address_for_remote_address( return IPStackSimulator::connect_address_for_remote_address(ntohl(sin->sin_addr.s_addr));
ntohl(sin->sin_addr.s_addr));
} else { } else {
// TODO: we can do something smarter here, like use the sockname to find // 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 // 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 // IP stack simulator (IP bans only; account bans will presumably be handled
// by one of the above cases) // by one of the above cases)
if (this->ip_stack_simulator) { if (this->ip_stack_simulator) {
vector<bufferevent*> bevs_to_disconnect; vector<uint64_t> ids_to_disconnect;
for (const auto& it : this->ip_stack_simulator->all_clients()) { for (const auto& it : this->ip_stack_simulator->all_networks()) {
int fd = bufferevent_getfd(it.first); int fd = bufferevent_getfd(it.second->bev.get());
if (fd < 0) { if (fd < 0) {
continue; continue;
} }
struct sockaddr_storage remote_ss; struct sockaddr_storage remote_ss;
get_socket_addresses(fd, nullptr, &remote_ss); get_socket_addresses(fd, nullptr, &remote_ss);
if (this->banned_ipv4_ranges->check(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) { for (uint64_t id : ids_to_disconnect) {
this->ip_stack_simulator->disconnect_client(bev); 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__";
} }
} }
} }
+2
View File
@@ -387,4 +387,6 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
static void dispatch_destroy_lobbies(evutil_socket_t, short, void* ctx); static void dispatch_destroy_lobbies(evutil_socket_t, short, void* ctx);
void disconnect_all_banned_clients(); void disconnect_all_banned_clients();
std::string format_address_for_channel_name(const struct sockaddr_storage& remote_ss, uint64_t virtual_network);
}; };