File diff r25826:89dc24729f74 → r25827:5a9ded1a0c1a
src/network/network_coordinator.cpp
Show inline comments
 
@@ -19,6 +19,8 @@
 
#include "network_coordinator.h"
 
#include "network_gamelist.h"
 
#include "network_internal.h"
 
#include "network_server.h"
 
#include "network_stun.h"
 
#include "table/strings.h"
 

	
 
#include "../safeguards.h"
 
@@ -51,7 +53,48 @@ public:
 

	
 
	void OnConnect(SOCKET s) override
 
	{
 
		_network_coordinator_client.ConnectSuccess(this->token, s);
 
		NetworkAddress address = NetworkAddress::GetPeerAddress(s);
 
		_network_coordinator_client.ConnectSuccess(this->token, s, address);
 
	}
 
};
 

	
 
/** Connecter used after STUN exchange to connect from both sides to each other. */
 
class NetworkReuseStunConnecter : public TCPConnecter {
 
private:
 
	std::string token;     ///< Token of this connection.
 
	uint8 tracking_number; ///< Tracking number of this connection.
 
	uint8 family;          ///< Family of this connection.
 

	
 
public:
 
	/**
 
	 * Try to establish a STUN-based connection.
 
	 * @param hostname The hostname of the peer.
 
	 * @param port The port of the peer.
 
	 * @param bind_address The local bind address used for this connection.
 
	 * @param token The connection token.
 
	 * @param tracking_number The tracking number of the connection.
 
	 * @param family The family this connection is using.
 
	 */
 
	NetworkReuseStunConnecter(const std::string &hostname, uint16 port, const NetworkAddress &bind_address, std::string token, uint8 tracking_number, uint8 family) :
 
		TCPConnecter(hostname, port, bind_address),
 
		token(token),
 
		tracking_number(tracking_number),
 
		family(family)
 
	{
 
	}
 

	
 
	void OnFailure() override
 
	{
 
		/* Close the STUN connection too, as it is no longer of use. */
 
		_network_coordinator_client.CloseStunHandler(this->token, this->family);
 

	
 
		_network_coordinator_client.ConnectFailure(this->token, this->tracking_number);
 
	}
 

	
 
	void OnConnect(SOCKET s) override
 
	{
 
		NetworkAddress address = NetworkAddress::GetPeerAddress(s);
 
		_network_coordinator_client.ConnectSuccess(this->token, s, address);
 
	}
 
};
 

	
 
@@ -101,10 +144,7 @@ bool ClientNetworkCoordinatorSocketHandl
 
			return false;
 

	
 
		case NETWORK_COORDINATOR_ERROR_INVALID_INVITE_CODE: {
 
			/* Find the connecter based on the invite code. */
 
			auto connecter_it = this->connecter_pre.find(detail);
 
			if (connecter_it == this->connecter_pre.end()) return true;
 
			this->connecter_pre.erase(connecter_it);
 
			this->CloseToken(detail);
 

	
 
			/* Mark the server as offline. */
 
			NetworkGameList *item = NetworkGameListAddItem(detail);
 
@@ -148,6 +188,7 @@ bool ClientNetworkCoordinatorSocketHandl
 
		switch (_network_server_connection_type) {
 
			case CONNECTION_TYPE_ISOLATED: connection_type = "Remote players can't connect"; break;
 
			case CONNECTION_TYPE_DIRECT:   connection_type = "Public"; break;
 
			case CONNECTION_TYPE_STUN:     connection_type = "Behind NAT"; break;
 

	
 
			case CONNECTION_TYPE_UNKNOWN: // Never returned from Game Coordinator.
 
			default: connection_type = "Unknown"; break; // Should never happen, but don't fail if it does.
 
@@ -263,6 +304,49 @@ bool ClientNetworkCoordinatorSocketHandl
 
	return true;
 
}
 

	
 
bool ClientNetworkCoordinatorSocketHandler::Receive_GC_STUN_REQUEST(Packet *p)
 
{
 
	std::string token = p->Recv_string(NETWORK_TOKEN_LENGTH);
 

	
 
	this->stun_handlers[token][AF_INET6] = ClientNetworkStunSocketHandler::Stun(token, AF_INET6);
 
	this->stun_handlers[token][AF_INET] = ClientNetworkStunSocketHandler::Stun(token, AF_INET);
 
	return true;
 
}
 

	
 
bool ClientNetworkCoordinatorSocketHandler::Receive_GC_STUN_CONNECT(Packet *p)
 
{
 
	std::string token = p->Recv_string(NETWORK_TOKEN_LENGTH);
 
	uint8 tracking_number = p->Recv_uint8();
 
	uint8 family = p->Recv_uint8();
 
	std::string host = p->Recv_string(NETWORK_HOSTNAME_PORT_LENGTH);
 
	uint16 port = p->Recv_uint16();
 

	
 
	/* Check if we know this token. */
 
	auto stun_it = this->stun_handlers.find(token);
 
	if (stun_it == this->stun_handlers.end()) return true;
 
	auto family_it = stun_it->second.find(family);
 
	if (family_it == stun_it->second.end()) return true;
 

	
 
	/* Ensure all other pending connection attempts are killed. */
 
	if (this->game_connecter != nullptr) {
 
		this->game_connecter->Kill();
 
		this->game_connecter = nullptr;
 
	}
 

	
 
	/* We now mark the connection as closed, but we do not really close the
 
	 * socket yet. We do this when the NetworkReuseStunConnecter is connected.
 
	 * This prevents any NAT to already remove the route while we create the
 
	 * second connection on top of the first. */
 
	family_it->second->CloseConnection(false);
 

	
 
	/* Connect to our peer from the same local address as we use for the
 
	 * STUN server. This means that if there is any NAT in the local network,
 
	 * the public ip:port is still pointing to the local address, and as such
 
	 * a connection can be established. */
 
	this->game_connecter = new NetworkReuseStunConnecter(host, port, family_it->second->local_addr, token, tracking_number, family);
 
	return true;
 
}
 

	
 
void ClientNetworkCoordinatorSocketHandler::Connect()
 
{
 
	/* We are either already connected or are trying to connect. */
 
@@ -399,11 +483,8 @@ void ClientNetworkCoordinatorSocketHandl
 

	
 
	this->SendPacket(p);
 

	
 
	auto connecter_it = this->connecter.find(token);
 
	assert(connecter_it != this->connecter.end());
 

	
 
	connecter_it->second->SetFailure();
 
	this->connecter.erase(connecter_it);
 
	/* We do not close the associated connecter here yet, as the
 
	 * Game Coordinator might have other methods of connecting available. */
 
}
 

	
 
/**
 
@@ -412,29 +493,75 @@ void ClientNetworkCoordinatorSocketHandl
 
 * @param token Token of the connecter that succeeded.
 
 * @param sock The socket that the connecter can now use.
 
 */
 
void ClientNetworkCoordinatorSocketHandler::ConnectSuccess(const std::string &token, SOCKET sock)
 
void ClientNetworkCoordinatorSocketHandler::ConnectSuccess(const std::string &token, SOCKET sock, NetworkAddress &address)
 
{
 
	/* Connecter will destroy itself. */
 
	this->game_connecter = nullptr;
 

	
 
	assert(!_network_server);
 
	if (_network_server) {
 
		if (!ServerNetworkGameSocketHandler::ValidateClient(sock, address)) return;
 
		Debug(net, 3, "[{}] Client connected from {} on frame {}", ServerNetworkGameSocketHandler::GetName(), address.GetHostname(), _frame_counter);
 
		ServerNetworkGameSocketHandler::AcceptConnection(sock, address);
 
	} else {
 
		/* The client informs the Game Coordinator about the success. The server
 
		 * doesn't have to, as it is implied by the client telling. */
 
		Packet *p = new Packet(PACKET_COORDINATOR_CLIENT_CONNECTED);
 
		p->Send_uint8(NETWORK_COORDINATOR_VERSION);
 
		p->Send_string(token);
 
		this->SendPacket(p);
 

	
 
	Packet *p = new Packet(PACKET_COORDINATOR_CLIENT_CONNECTED);
 
	p->Send_uint8(NETWORK_COORDINATOR_VERSION);
 
	p->Send_string(token);
 
	this->SendPacket(p);
 
		auto connecter_it = this->connecter.find(token);
 
		assert(connecter_it != this->connecter.end());
 

	
 
	auto connecter_it = this->connecter.find(token);
 
	assert(connecter_it != this->connecter.end());
 

	
 
	connecter_it->second->SetConnected(sock);
 
	this->connecter.erase(connecter_it);
 
		connecter_it->second->SetConnected(sock);
 
		this->connecter.erase(connecter_it);
 
	}
 

	
 
	/* Close all remaining connections. */
 
	this->CloseToken(token);
 
}
 

	
 
/**
 
 * Callback from the STUN connecter to inform the Game Coordinator about the
 
 * result of the STUN.
 
 *
 
 * This helps the Game Coordinator not to wait for a timeout on its end, but
 
 * rather react as soon as the client/server knows the result.
 
 */
 
void ClientNetworkCoordinatorSocketHandler::StunResult(const std::string &token, uint8 family, bool result)
 
{
 
	Packet *p = new Packet(PACKET_COORDINATOR_SERCLI_STUN_RESULT);
 
	p->Send_uint8(NETWORK_COORDINATOR_VERSION);
 
	p->Send_string(token);
 
	p->Send_uint8(family);
 
	p->Send_bool(result);
 
	this->SendPacket(p);
 
}
 

	
 
void ClientNetworkCoordinatorSocketHandler::CloseStunHandler(const std::string &token, uint8 family)
 
{
 
	auto stun_it = this->stun_handlers.find(token);
 
	if (stun_it == this->stun_handlers.end()) return;
 

	
 
	if (family == AF_UNSPEC) {
 
		for (auto &[family, stun_handler] : stun_it->second) {
 
			stun_handler->CloseConnection();
 
			stun_handler->CloseSocket();
 
		}
 

	
 
		this->stun_handlers.erase(stun_it);
 
	} else {
 
		auto family_it = stun_it->second.find(family);
 
		if (family_it == stun_it->second.end()) return;
 

	
 
		family_it->second->CloseConnection();
 
		family_it->second->CloseSocket();
 

	
 
		stun_it->second.erase(family_it);
 
	}
 
}
 

	
 
/**
 
 * Close everything related to this connection token.
 
 * @param token The connection token to close.
 
 */
 
@@ -445,6 +572,21 @@ void ClientNetworkCoordinatorSocketHandl
 
		this->game_connecter->Kill();
 
		this->game_connecter = nullptr;
 
	}
 

	
 
	/* Close all remaining STUN connections. */
 
	this->CloseStunHandler(token);
 

	
 
	/* Close the caller of the connection attempt. */
 
	auto connecter_it = this->connecter.find(token);
 
	if (connecter_it != this->connecter.end()) {
 
		connecter_it->second->SetFailure();
 
		this->connecter.erase(connecter_it);
 
	}
 
	auto connecter_pre_it = this->connecter_pre.find(token);
 
	if (connecter_pre_it != this->connecter_pre.end()) {
 
		connecter_pre_it->second->SetFailure();
 
		this->connecter_pre.erase(connecter_pre_it);
 
	}
 
}
 

	
 
/**
 
@@ -460,6 +602,7 @@ void ClientNetworkCoordinatorSocketHandl
 

	
 
	/* Mark any pending connecters as failed. */
 
	for (auto &[token, it] : this->connecter) {
 
		this->CloseStunHandler(token);
 
		it->SetFailure();
 
	}
 
	for (auto &[invite_code, it] : this->connecter_pre) {
 
@@ -535,4 +678,10 @@ void ClientNetworkCoordinatorSocketHandl
 
	}
 

	
 
	this->SendPackets();
 

	
 
	for (const auto &[token, families] : this->stun_handlers) {
 
		for (const auto &[family, stun_handler] : families) {
 
			stun_handler->SendReceive();
 
		}
 
	}
 
}