From 7005b573f51c04ec6d4a15182f8d8e28b85d1e1c Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Sat, 14 Oct 2023 21:28:02 -0700 Subject: [PATCH] add names to IPS listen sockets --- src/IPStackSimulator.cc | 106 ++++++++++++++++++++++++++++--------- src/IPStackSimulator.hh | 50 ++++++++++------- src/Main.cc | 5 +- src/Server.cc | 14 +++-- system/config.example.json | 12 +++-- 5 files changed, 130 insertions(+), 57 deletions(-) diff --git a/src/IPStackSimulator.cc b/src/IPStackSimulator.cc index ed27bdfb..d029e1f2 100644 --- a/src/IPStackSimulator.cc +++ b/src/IPStackSimulator.cc @@ -62,8 +62,8 @@ string IPStackSimulator::str_for_tcp_connection(shared_ptr c, } IPStackSimulator::IPStackSimulator( - std::shared_ptr base, - std::shared_ptr state) + shared_ptr base, + shared_ptr state) : base(base), state(state), pcap_text_log_file(state->ip_stack_debug ? fopen("IPStackSimulator-Log.txt", "wt") : nullptr) { @@ -77,20 +77,29 @@ IPStackSimulator::~IPStackSimulator() { } } -void IPStackSimulator::listen(const std::string& socket_path) { - this->add_socket(::listen(socket_path, 0, SOMAXCONN)); +void IPStackSimulator::listen(const string& name, const string& socket_path) { + int fd = ::listen(socket_path, 0, SOMAXCONN); + ip_stack_simulator_log.info("Listening on Unix socket %s on fd %d as %s", socket_path.c_str(), fd, name.c_str()); + this->add_socket(name, fd); } -void IPStackSimulator::listen(const std::string& addr, int port) { - this->add_socket(::listen(addr, port, SOMAXCONN)); +void IPStackSimulator::listen(const string& name, const string& addr, int port) { + if (port == 0) { + this->listen(name, addr); + } else { + int fd = ::listen(addr, port, SOMAXCONN); + string netloc_str = render_netloc(addr, port); + ip_stack_simulator_log.info("Listening on TCP interface %s on fd %d as %s", netloc_str.c_str(), fd, name.c_str()); + this->add_socket(name, fd); + } } -void IPStackSimulator::listen(int port) { - this->add_socket(::listen("", port, SOMAXCONN)); +void IPStackSimulator::listen(const string& name, int port) { + this->listen(name, "", port); } -void IPStackSimulator::add_socket(int fd) { - this->listeners.emplace( +void IPStackSimulator::add_socket(const string& name, int fd) { + unique_listener l( evconnlistener_new( this->base.get(), IPStackSimulator::dispatch_on_listen_accept, @@ -99,6 +108,7 @@ void IPStackSimulator::add_socket(int fd) { 0, fd), evconnlistener_free); + this->listening_sockets.emplace(piecewise_construct, forward_as_tuple(fd), forward_as_tuple(name, std::move(l))); } uint32_t IPStackSimulator::connect_address_for_remote_address(uint32_t remote_addr) { @@ -112,10 +122,28 @@ uint32_t IPStackSimulator::connect_address_for_remote_address(uint32_t remote_ad } } -IPStackSimulator::IPClient::IPClient(struct bufferevent* bev) - : bev(bev, bufferevent_free), - ipv4_addr(0) { - this->mac_addr.clear(0); +IPStackSimulator::IPClient::IPClient(shared_ptr sim, struct bufferevent* bev) + : sim(sim), + bev(bev, bufferevent_free), + mac_addr(0), + ipv4_addr(0), + idle_timeout_event(event_new(sim->base.get(), -1, EV_TIMEOUT, &IPStackSimulator::IPClient::dispatch_on_idle_timeout, this), event_free) { + struct timeval tv = usecs_to_timeval(60 * 1000 * 1000); + event_add(this->idle_timeout_event.get(), &tv); +} + +void IPStackSimulator::IPClient::dispatch_on_idle_timeout(evutil_socket_t, short, void* ctx) { + reinterpret_cast(ctx)->on_idle_timeout(); +} + +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()); + } else { + ip_stack_simulator_log.info("Idle timeout expired on virtual network %d, but simulator is missing", bufferevent_getfd(this->bev.get())); + } } static void flush_and_free_bufferevent(struct bufferevent* bev) { @@ -139,6 +167,11 @@ 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::dispatch_on_listen_accept( struct evconnlistener* listener, evutil_socket_t fd, struct sockaddr* address, int socklen, void* ctx) { @@ -149,12 +182,21 @@ void IPStackSimulator::dispatch_on_listen_accept( void IPStackSimulator::on_listen_accept(struct evconnlistener* listener, evutil_socket_t fd, struct sockaddr*, int) { int listen_fd = evconnlistener_get_fd(listener); - ip_stack_simulator_log.info("Virtual network fd %d connected via fd %d", fd, listen_fd); + + const ListeningSocket* listening_socket; + 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); + close(fd); + return; + } + + ip_stack_simulator_log.info("Virtual network %d connected via %s", fd, listening_socket->name.c_str()); struct bufferevent* bev = bufferevent_socket_new(this->base.get(), fd, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS); - shared_ptr c(new IPClient(bev)); - c->sim = this; + shared_ptr c(new IPClient(this->shared_from_this(), bev)); this->bev_to_client.emplace(make_pair(bev, c)); bufferevent_setcb(bev, &IPStackSimulator::dispatch_on_client_input, nullptr, @@ -218,16 +260,14 @@ void IPStackSimulator::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::on_client_error(struct bufferevent* bev, 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)); } if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) { - ip_stack_simulator_log.info("Virtual network fd %d disconnected", bufferevent_getfd(bev)); - this->bev_to_client.erase(bev); + this->disconnect_client(bev); } } @@ -971,7 +1011,12 @@ void IPStackSimulator::dispatch_on_resend_push(evutil_socket_t, short, void* ctx if (!c.get()) { ip_stack_simulator_log.warning("Resend push event triggered for deleted client; ignoring"); } else { - c->sim->on_resend_push(c, *conn); + auto sim = c->sim.lock(); + if (!sim) { + ip_stack_simulator_log.warning("Resend push event triggered for client on deleted simulator; ignoring"); + } else { + sim->on_resend_push(c, *conn); + } } } @@ -985,7 +1030,12 @@ void IPStackSimulator::dispatch_on_server_input(struct bufferevent*, void* ctx) if (!c.get()) { ip_stack_simulator_log.warning("Server input event triggered for deleted client; ignoring"); } else { - c->sim->on_server_input(c, *conn); + auto sim = c->sim.lock(); + if (!sim) { + ip_stack_simulator_log.warning("Server input event triggered for client on deleted simulator; ignoring"); + } else { + sim->on_server_input(c, *conn); + } } } @@ -994,6 +1044,9 @@ void IPStackSimulator::on_server_input(shared_ptr c, IPClient::TCPConn ip_stack_simulator_log.debug("Server input event: 0x%zX bytes to read", evbuffer_get_length(buf)); + struct timeval tv = usecs_to_timeval(60 * 1000 * 1000); + event_add(c->idle_timeout_event.get(), &tv); + evbuffer_add_buffer(conn.pending_data.get(), buf); this->send_pending_push_frame(c, conn); } @@ -1005,7 +1058,12 @@ void IPStackSimulator::dispatch_on_server_error( if (!c.get()) { ip_stack_simulator_log.warning("Server error event triggered for deleted client; ignoring"); } else { - c->sim->on_server_error(c, *conn, events); + auto sim = c->sim.lock(); + if (!sim) { + ip_stack_simulator_log.warning("Server error event triggered for client on deleted simulator; ignoring"); + } else { + sim->on_server_error(c, *conn, events); + } } } diff --git a/src/IPStackSimulator.hh b/src/IPStackSimulator.hh index db0c5214..55f16a44 100644 --- a/src/IPStackSimulator.hh +++ b/src/IPStackSimulator.hh @@ -12,17 +12,17 @@ #include "ServerState.hh" #include "Text.hh" -class IPStackSimulator { +class IPStackSimulator : public std::enable_shared_from_this { public: IPStackSimulator( std::shared_ptr base, std::shared_ptr state); ~IPStackSimulator(); - void listen(const std::string& socket_path); - void listen(const std::string& addr, int port); - void listen(int port); - void add_socket(int fd); + void listen(const std::string& name, const std::string& socket_path); + void listen(const std::string& name, const std::string& addr, int port); + void listen(const std::string& name, int port); + void add_socket(const std::string& name, int fd); static uint32_t connect_address_for_remote_address(uint32_t remote_addr); @@ -36,7 +36,7 @@ private: using unique_event = std::unique_ptr; struct IPClient { - IPStackSimulator* sim; + std::weak_ptr sim; unique_bufferevent bev; parray mac_addr; @@ -73,10 +73,24 @@ private: }; std::unordered_map tcp_connections; - IPClient(struct bufferevent* bev); + unique_event idle_timeout_event; + + IPClient(std::shared_ptr sim, struct bufferevent* bev); + + static void dispatch_on_idle_timeout(evutil_socket_t fd, short events, void* ctx); + void on_idle_timeout(); }; - std::unordered_set listeners; + struct ListeningSocket { + std::string name; + unique_listener listener; + + ListeningSocket(const std::string& name, unique_listener&& l) + : name(name), + listener(std::move(l)) {} + }; + + std::unordered_map listening_sockets; std::unordered_map> bev_to_client; parray host_mac_address_bytes; @@ -84,10 +98,10 @@ private: FILE* pcap_text_log_file; - static uint64_t tcp_conn_key_for_connection( - const IPClient::TCPConnection& conn); - static uint64_t tcp_conn_key_for_client_frame( - const IPv4Header& ipv4, const TCPHeader& tcp); + void disconnect_client(struct bufferevent* bev); + + static uint64_t tcp_conn_key_for_connection(const IPClient::TCPConnection& conn); + static uint64_t tcp_conn_key_for_client_frame(const IPv4Header& ipv4, const TCPHeader& tcp); static uint64_t tcp_conn_key_for_client_frame(const FrameInfo& fi); static std::string str_for_ipv4_netloc(uint32_t addr, uint16_t port); @@ -103,8 +117,7 @@ private: 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); + static void dispatch_on_client_error(struct bufferevent* bev, short events, void* ctx); void on_client_error(struct bufferevent* bev, short events); void on_client_frame(std::shared_ptr c, const std::string& frame); @@ -112,18 +125,15 @@ private: void on_client_udp_frame(std::shared_ptr c, const FrameInfo& fi); void on_client_tcp_frame(std::shared_ptr c, const FrameInfo& fi); - static void dispatch_on_resend_push(evutil_socket_t fd, short events, - void* ctx); + static void dispatch_on_resend_push(evutil_socket_t fd, short events, void* ctx); void on_resend_push(std::shared_ptr c, IPClient::TCPConnection& conn); static void dispatch_on_server_input(struct bufferevent* bev, void* ctx); void on_server_input(std::shared_ptr c, IPClient::TCPConnection& conn); - static void dispatch_on_server_error(struct bufferevent* bev, short events, - void* ctx); + static void dispatch_on_server_error(struct bufferevent* bev, short events, void* ctx); void on_server_error(std::shared_ptr c, IPClient::TCPConnection& conn, short events); - void send_pending_push_frame( - std::shared_ptr c, IPClient::TCPConnection& conn); + void send_pending_push_frame(std::shared_ptr c, IPClient::TCPConnection& conn); void send_tcp_frame( std::shared_ptr c, IPClient::TCPConnection& conn, diff --git a/src/Main.cc b/src/Main.cc index cc8bb677..e4d84d3e 100644 --- a/src/Main.cc +++ b/src/Main.cc @@ -2042,16 +2042,15 @@ int main(int argc, char** argv) { } } -#ifndef PHOSG_WINDOWS if (!state->ip_stack_addresses.empty()) { config_log.info("Starting IP stack simulator"); ip_stack_simulator.reset(new IPStackSimulator(base, state)); for (const auto& it : state->ip_stack_addresses) { auto netloc = parse_netloc(it); - ip_stack_simulator->listen(netloc.first, netloc.second); + string spec = (netloc.second == 0) ? ("T-IPS-" + netloc.first) : string_printf("T-IPS-%hu", netloc.second); + ip_stack_simulator->listen(spec, netloc.first, netloc.second); } } -#endif } else { throw logic_error("invalid behavior"); diff --git a/src/Server.cc b/src/Server.cc index 7a8672d1..54be9350 100644 --- a/src/Server.cc +++ b/src/Server.cc @@ -233,11 +233,15 @@ void Server::listen( int port, GameVersion version, ServerBehavior behavior) { - int fd = ::listen(addr, port, SOMAXCONN); - string netloc_str = render_netloc(addr, port); - server_log.info("Listening on TCP interface %s on fd %d as %s", - netloc_str.c_str(), fd, addr_str.c_str()); - this->add_socket(addr_str, fd, version, behavior); + if (port == 0) { + this->listen(addr_str, addr, version, behavior); + } else { + int fd = ::listen(addr, port, SOMAXCONN); + string netloc_str = render_netloc(addr, port); + server_log.info("Listening on TCP interface %s on fd %d as %s", + netloc_str.c_str(), fd, addr_str.c_str()); + this->add_socket(addr_str, fd, version, behavior); + } } void Server::listen(const std::string& addr_str, int port, GameVersion version, ServerBehavior behavior) { diff --git a/system/config.example.json b/system/config.example.json index 07d6d968..c003c848 100644 --- a/system/config.example.json +++ b/system/config.example.json @@ -127,11 +127,13 @@ // clients to connect, set this to ["/tmp/dolphin-tap"] and configure Dolphin // to use the tapserver type of broadband adapter. You do not need to install // or run tapserver. See README.md for details on how to get PSO to connect - // via this interface. - // If you're doing unusual things, you can also add numbers or "address:port" - // strings to this list to listen for tapserver connections on a TCP port. - // This feature requires Unix sockets, so this option does nothing when - // newserv runs under Windows. + // via this interface. You can also add numbers or "address:port" strings to + // this list to listen for tapserver connections on a TCP port. + // On Windows, Unix sockets are not available, so you can only use TCP sockets + // here. You can get Dolphin to connect locally by adding a port to this list + // and configuring Dolphin to connect to the same port. For example, you could + // set this to ["127.0.0.1:5059"], and configure Dolphin's tapserver BBA to + // connect to 127.0.0.1:5059. "IPStackListen": [], // Other servers to support proxying to. If this is empty for any game