use actual remote address when ip stack clients connect to proxy
This commit is contained in:
+21
-15
@@ -83,6 +83,7 @@ IPStackSimulator::IPStackSimulator(
|
|||||||
game_server(game_server),
|
game_server(game_server),
|
||||||
proxy_server(proxy_server),
|
proxy_server(proxy_server),
|
||||||
state(state),
|
state(state),
|
||||||
|
proxy_destination_address(0),
|
||||||
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) {
|
||||||
memset(this->host_mac_address_bytes, 0x90, 6);
|
memset(this->host_mac_address_bytes, 0x90, 6);
|
||||||
memset(this->broadcast_mac_address_bytes, 0xFF, 6);
|
memset(this->broadcast_mac_address_bytes, 0xFF, 6);
|
||||||
@@ -123,7 +124,7 @@ void IPStackSimulator::add_socket(int fd) {
|
|||||||
|
|
||||||
|
|
||||||
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 and 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
|
||||||
// the connection.
|
// the connection.
|
||||||
if ((remote_addr & 0xFF000000) != 0x23000000) {
|
if ((remote_addr & 0xFF000000) != 0x23000000) {
|
||||||
@@ -420,7 +421,9 @@ void IPStackSimulator::on_client_udp_frame(
|
|||||||
// r_udp.size filled in later
|
// r_udp.size filled in later
|
||||||
// r_udp.checksum filled in later
|
// r_udp.checksum filled in later
|
||||||
|
|
||||||
uint32_t resolved_address = this->connect_address_for_remote_address(c->ipv4_addr);
|
uint32_t resolved_address = this->proxy_destination_address
|
||||||
|
? this->proxy_destination_address
|
||||||
|
: this->connect_address_for_remote_address(c->ipv4_addr);
|
||||||
|
|
||||||
string r_data = DNSServer::response_for_query(
|
string r_data = DNSServer::response_for_query(
|
||||||
fi.payload, fi.payload_size, resolved_address);
|
fi.payload, fi.payload_size, resolved_address);
|
||||||
@@ -743,13 +746,6 @@ void IPStackSimulator::open_server_connection(
|
|||||||
throw logic_error("server connection is already open");
|
throw logic_error("server connection is already open");
|
||||||
}
|
}
|
||||||
|
|
||||||
const PortConfiguration* port_config;
|
|
||||||
try {
|
|
||||||
port_config = &this->state->numbered_port_configuration.at(conn.server_port);
|
|
||||||
} catch (const out_of_range&) {
|
|
||||||
throw logic_error("client connected to port missing from configuration");
|
|
||||||
}
|
|
||||||
|
|
||||||
struct bufferevent* bevs[2];
|
struct bufferevent* bevs[2];
|
||||||
bufferevent_pair_new(this->base.get(), 0, bevs);
|
bufferevent_pair_new(this->base.get(), 0, bevs);
|
||||||
|
|
||||||
@@ -762,16 +758,26 @@ void IPStackSimulator::open_server_connection(
|
|||||||
// Link the client to the server - the server sees this as a normal TCP
|
// Link the client to the server - the server sees this as a normal TCP
|
||||||
// connection and treats it as if the client connected to one of its listening
|
// connection and treats it as if the client connected to one of its listening
|
||||||
// sockets
|
// sockets
|
||||||
|
string conn_str = this->str_for_tcp_connection(c, conn);
|
||||||
if (this->game_server.get()) {
|
if (this->game_server.get()) {
|
||||||
|
const PortConfiguration* port_config;
|
||||||
|
try {
|
||||||
|
port_config = &this->state->numbered_port_configuration.at(conn.server_port);
|
||||||
|
} catch (const out_of_range&) {
|
||||||
|
bufferevent_free(bevs[1]);
|
||||||
|
throw logic_error("client connected to port missing from configuration");
|
||||||
|
}
|
||||||
|
|
||||||
this->game_server->connect_client(bevs[1], c->ipv4_addr, conn.client_port,
|
this->game_server->connect_client(bevs[1], c->ipv4_addr, conn.client_port,
|
||||||
port_config->version, port_config->behavior);
|
port_config->version, port_config->behavior);
|
||||||
} else if (this->proxy_server.get()) {
|
log(INFO, "[IPStackSimulator] Connected TCP connection %s to game server",
|
||||||
this->proxy_server->connect_client(bevs[1]);
|
conn_str.c_str());
|
||||||
}
|
|
||||||
|
|
||||||
string conn_str = this->str_for_tcp_connection(c, conn);
|
} else if (this->proxy_server.get()) {
|
||||||
log(INFO, "[IPStackSimulator] Connected TCP connection %s to game server",
|
this->proxy_server->connect_client(bevs[1], conn.server_addr, conn.server_port);
|
||||||
conn_str.c_str());
|
log(INFO, "[IPStackSimulator] Connected TCP connection %s to proxy server",
|
||||||
|
conn_str.c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void IPStackSimulator::send_pending_push_frame(
|
void IPStackSimulator::send_pending_push_frame(
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ public:
|
|||||||
void listen(int port);
|
void listen(int port);
|
||||||
void add_socket(int fd);
|
void add_socket(int fd);
|
||||||
|
|
||||||
|
inline void set_proxy_destination_address(uint32_t addr) {
|
||||||
|
this->proxy_destination_address = addr;
|
||||||
|
}
|
||||||
|
|
||||||
static uint32_t connect_address_for_remote_address(uint32_t remote_addr);
|
static uint32_t connect_address_for_remote_address(uint32_t remote_addr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -34,6 +38,7 @@ private:
|
|||||||
std::shared_ptr<Server> game_server;
|
std::shared_ptr<Server> game_server;
|
||||||
std::shared_ptr<ProxyServer> proxy_server;
|
std::shared_ptr<ProxyServer> proxy_server;
|
||||||
std::shared_ptr<ServerState> state;
|
std::shared_ptr<ServerState> state;
|
||||||
|
uint32_t proxy_destination_address;
|
||||||
|
|
||||||
using unique_listener = std::unique_ptr<struct evconnlistener, void(*)(struct evconnlistener*)>;
|
using unique_listener = std::unique_ptr<struct evconnlistener, void(*)(struct evconnlistener*)>;
|
||||||
using unique_bufferevent = std::unique_ptr<struct bufferevent, void(*)(struct bufferevent*)>;
|
using unique_bufferevent = std::unique_ptr<struct bufferevent, void(*)(struct bufferevent*)>;
|
||||||
|
|||||||
+8
-2
@@ -261,12 +261,17 @@ int main(int argc, char* argv[]) {
|
|||||||
|
|
||||||
shared_ptr<ProxyServer> proxy_server;
|
shared_ptr<ProxyServer> proxy_server;
|
||||||
shared_ptr<Server> game_server;
|
shared_ptr<Server> game_server;
|
||||||
|
uint32_t proxy_destination_address = 0;
|
||||||
if (proxy_port) {
|
if (proxy_port) {
|
||||||
log(INFO, "Starting proxy server");
|
log(INFO, "Starting proxy server");
|
||||||
sockaddr_storage proxy_destination_ss = make_sockaddr_storage(
|
sockaddr_storage proxy_destination_ss = make_sockaddr_storage(
|
||||||
proxy_hostname, proxy_port).first;
|
proxy_hostname, proxy_port).first;
|
||||||
proxy_server.reset(new ProxyServer(base, proxy_destination_ss,
|
if (proxy_destination_ss.ss_family != AF_INET) {
|
||||||
proxy_version));
|
throw runtime_error("proxy destination address is not IPv4");
|
||||||
|
}
|
||||||
|
proxy_destination_address = ntohl(
|
||||||
|
reinterpret_cast<struct sockaddr_in*>(&proxy_destination_ss)->sin_addr.s_addr);
|
||||||
|
proxy_server.reset(new ProxyServer(base, proxy_destination_ss, proxy_version));
|
||||||
proxy_server->listen(proxy_port);
|
proxy_server->listen(proxy_port);
|
||||||
if (proxy_version == GameVersion::BB) {
|
if (proxy_version == GameVersion::BB) {
|
||||||
proxy_server->listen(proxy_port + 1);
|
proxy_server->listen(proxy_port + 1);
|
||||||
@@ -297,6 +302,7 @@ int main(int argc, char* argv[]) {
|
|||||||
log(INFO, "Starting IP stack simulator");
|
log(INFO, "Starting IP stack simulator");
|
||||||
ip_stack_simulator.reset(new IPStackSimulator(
|
ip_stack_simulator.reset(new IPStackSimulator(
|
||||||
base, game_server, proxy_server, state));
|
base, game_server, proxy_server, state));
|
||||||
|
ip_stack_simulator->set_proxy_destination_address(proxy_destination_address);
|
||||||
for (const auto& it : state->ip_stack_addresses) {
|
for (const auto& it : state->ip_stack_addresses) {
|
||||||
auto netloc = parse_netloc(it);
|
auto netloc = parse_netloc(it);
|
||||||
ip_stack_simulator->listen(netloc.first, netloc.second);
|
ip_stack_simulator->listen(netloc.first, netloc.second);
|
||||||
|
|||||||
+30
-21
@@ -44,6 +44,7 @@ ProxyServer::ProxyServer(
|
|||||||
client_bev(nullptr, flush_and_free_bufferevent),
|
client_bev(nullptr, flush_and_free_bufferevent),
|
||||||
server_bev(nullptr, flush_and_free_bufferevent),
|
server_bev(nullptr, flush_and_free_bufferevent),
|
||||||
next_destination(initial_destination),
|
next_destination(initial_destination),
|
||||||
|
default_destination(initial_destination),
|
||||||
version(version),
|
version(version),
|
||||||
header_size((version == GameVersion::BB) ? 8 : 4),
|
header_size((version == GameVersion::BB) ? 8 : 4),
|
||||||
save_quests(false) {
|
save_quests(false) {
|
||||||
@@ -150,7 +151,8 @@ void ProxyServer::on_listen_accept(struct evconnlistener*, evutil_socket_t fd,
|
|||||||
BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS));
|
BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxyServer::connect_client(struct bufferevent* bev) {
|
void ProxyServer::connect_client(
|
||||||
|
struct bufferevent* bev, uint32_t server_ipv4_addr, uint16_t server_port) {
|
||||||
if (this->client_bev.get()) {
|
if (this->client_bev.get()) {
|
||||||
log(WARNING, "[ProxyServer] Ignoring client virtual connection because client already exists");
|
log(WARNING, "[ProxyServer] Ignoring client virtual connection because client already exists");
|
||||||
bufferevent_flush(bev, EV_WRITE, BEV_FINISHED);
|
bufferevent_flush(bev, EV_WRITE, BEV_FINISHED);
|
||||||
@@ -158,10 +160,12 @@ void ProxyServer::connect_client(struct bufferevent* bev) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log(INFO, "[ProxyServer] Client connected on virtual connection %p", bev);
|
log(INFO, "[ProxyServer] Client connected on virtual connection %p", bev);
|
||||||
this->on_client_connect(bev);
|
|
||||||
|
this->on_client_connect(bev, server_ipv4_addr, server_port);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxyServer::on_client_connect(struct bufferevent* bev) {
|
void ProxyServer::on_client_connect(struct bufferevent* bev,
|
||||||
|
uint32_t server_ipv4_addr, uint16_t server_port) {
|
||||||
this->client_bev.reset(bev);
|
this->client_bev.reset(bev);
|
||||||
|
|
||||||
bufferevent_setcb(this->client_bev.get(),
|
bufferevent_setcb(this->client_bev.get(),
|
||||||
@@ -173,22 +177,26 @@ void ProxyServer::on_client_connect(struct bufferevent* bev) {
|
|||||||
this->server_bev.reset(bufferevent_socket_new(this->base.get(), -1,
|
this->server_bev.reset(bufferevent_socket_new(this->base.get(), -1,
|
||||||
BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS));
|
BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS));
|
||||||
|
|
||||||
// TODO: figure out why this copy is necessary... shouldn't we just be able to
|
struct sockaddr_storage local_ss;
|
||||||
// use the sockaddr_storage directly?
|
struct sockaddr_in* local_sin = reinterpret_cast<struct sockaddr_in*>(&local_ss);
|
||||||
const struct sockaddr_in* sin_ss = reinterpret_cast<const sockaddr_in*>(&this->next_destination);
|
memset(local_sin, 0, sizeof(*local_sin));
|
||||||
if (sin_ss->sin_family != AF_INET) {
|
local_sin->sin_family = AF_INET;
|
||||||
throw logic_error("ss not AF_INET");
|
if (server_ipv4_addr && server_port) {
|
||||||
|
local_sin->sin_port = htons(server_port);
|
||||||
|
local_sin->sin_addr.s_addr = htonl(server_ipv4_addr);
|
||||||
|
} else {
|
||||||
|
const struct sockaddr_in* dest_sin = reinterpret_cast<const sockaddr_in*>(&this->next_destination);
|
||||||
|
if (dest_sin->sin_family != AF_INET) {
|
||||||
|
throw logic_error("ss not AF_INET");
|
||||||
|
}
|
||||||
|
local_sin->sin_port = dest_sin->sin_port;
|
||||||
|
local_sin->sin_addr.s_addr = dest_sin->sin_addr.s_addr;
|
||||||
}
|
}
|
||||||
struct sockaddr_in sin;
|
|
||||||
memset(&sin, 0, sizeof(sin));
|
|
||||||
sin.sin_family = AF_INET;
|
|
||||||
sin.sin_port = sin_ss->sin_port;
|
|
||||||
sin.sin_addr.s_addr = sin_ss->sin_addr.s_addr;
|
|
||||||
|
|
||||||
string netloc_str = render_sockaddr_storage(this->next_destination);
|
string netloc_str = render_sockaddr_storage(local_ss);
|
||||||
log(INFO, "[ProxyServer] Connecting to %s", netloc_str.c_str());
|
log(INFO, "[ProxyServer] Connecting to %s", netloc_str.c_str());
|
||||||
if (bufferevent_socket_connect(this->server_bev.get(),
|
if (bufferevent_socket_connect(this->server_bev.get(),
|
||||||
reinterpret_cast<const sockaddr*>(&sin), sizeof(sin)) != 0) {
|
reinterpret_cast<const sockaddr*>(local_sin), sizeof(*local_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()));
|
||||||
}
|
}
|
||||||
bufferevent_setcb(this->server_bev.get(),
|
bufferevent_setcb(this->server_bev.get(),
|
||||||
@@ -410,13 +418,14 @@ void ProxyServer::receive_and_process_commands(bool from_server) {
|
|||||||
if (!dest_bev) {
|
if (!dest_bev) {
|
||||||
log(WARNING, "[ProxyServer] Received reconnect command with no destination present");
|
log(WARNING, "[ProxyServer] Received reconnect command with no destination present");
|
||||||
} else {
|
} else {
|
||||||
struct sockaddr_storage sockname_ss;
|
// If the client is on a virtual connection (fd < 0), we don't need
|
||||||
socklen_t len = sizeof(sockname_ss);
|
// to do anything - we assume the client will connect again via a
|
||||||
|
// virtual connection, and we can just use the destination
|
||||||
|
// address/port from their connection request.
|
||||||
int fd = bufferevent_getfd(dest_bev);
|
int fd = bufferevent_getfd(dest_bev);
|
||||||
if (fd < 0) { // virtual connection
|
if (fd >= 0) {
|
||||||
args->address = 0x23232323; // TODO: apply the different-network logic here too
|
struct sockaddr_storage sockname_ss;
|
||||||
args->port = 9000;
|
socklen_t len = sizeof(sockname_ss);
|
||||||
} else {
|
|
||||||
getsockname(fd, reinterpret_cast<struct sockaddr*>(&sockname_ss), &len);
|
getsockname(fd, reinterpret_cast<struct sockaddr*>(&sockname_ss), &len);
|
||||||
if (sockname_ss.ss_family != AF_INET) {
|
if (sockname_ss.ss_family != AF_INET) {
|
||||||
throw logic_error("existing connection is not ipv4");
|
throw logic_error("existing connection is not ipv4");
|
||||||
|
|||||||
+9
-4
@@ -20,13 +20,16 @@ public:
|
|||||||
ProxyServer() = delete;
|
ProxyServer() = delete;
|
||||||
ProxyServer(const ProxyServer&) = delete;
|
ProxyServer(const ProxyServer&) = delete;
|
||||||
ProxyServer(ProxyServer&&) = delete;
|
ProxyServer(ProxyServer&&) = delete;
|
||||||
ProxyServer(std::shared_ptr<struct event_base> base,
|
ProxyServer(
|
||||||
const struct sockaddr_storage& initial_destination, GameVersion version);
|
std::shared_ptr<struct event_base> base,
|
||||||
|
const struct sockaddr_storage& initial_destination,
|
||||||
|
GameVersion version);
|
||||||
virtual ~ProxyServer() = default;
|
virtual ~ProxyServer() = default;
|
||||||
|
|
||||||
void listen(int port);
|
void listen(int port);
|
||||||
|
|
||||||
void connect_client(struct bufferevent* bev);
|
void connect_client(struct bufferevent* bev, uint32_t server_ipv4_addr,
|
||||||
|
uint16_t server_port);
|
||||||
|
|
||||||
void send_to_client(const std::string& data);
|
void send_to_client(const std::string& data);
|
||||||
void send_to_server(const std::string& data);
|
void send_to_server(const std::string& data);
|
||||||
@@ -39,6 +42,7 @@ private:
|
|||||||
std::unique_ptr<struct bufferevent, void(*)(struct bufferevent*)> client_bev;
|
std::unique_ptr<struct bufferevent, void(*)(struct bufferevent*)> client_bev;
|
||||||
std::unique_ptr<struct bufferevent, void(*)(struct bufferevent*)> server_bev;
|
std::unique_ptr<struct bufferevent, void(*)(struct bufferevent*)> server_bev;
|
||||||
struct sockaddr_storage next_destination;
|
struct sockaddr_storage next_destination;
|
||||||
|
struct sockaddr_storage default_destination;
|
||||||
int listen_port;
|
int listen_port;
|
||||||
GameVersion version;
|
GameVersion version;
|
||||||
|
|
||||||
@@ -84,7 +88,8 @@ private:
|
|||||||
void on_server_input(struct bufferevent* bev);
|
void on_server_input(struct bufferevent* bev);
|
||||||
void on_server_error(struct bufferevent* bev, short events);
|
void on_server_error(struct bufferevent* bev, short events);
|
||||||
|
|
||||||
void on_client_connect(struct bufferevent* bev);
|
void on_client_connect(struct bufferevent* bev,
|
||||||
|
uint32_t server_ipv4_addr = 0, uint16_t server_port = 0);
|
||||||
|
|
||||||
size_t get_size_field(const PSOCommandHeader* header);
|
size_t get_size_field(const PSOCommandHeader* header);
|
||||||
size_t get_command_field(const PSOCommandHeader* header);
|
size_t get_command_field(const PSOCommandHeader* header);
|
||||||
|
|||||||
Reference in New Issue
Block a user