Skip to content

Commit

Permalink
Feature: allow the use of TURN to connect client and server together
Browse files Browse the repository at this point in the history
TURN is a last resort, used only if all other methods failed.
TURN is a relay approach to connect client and server together, where
openttd.org (by default) is the middleman.

It is very unlikely either the client or server cannot connect to
the STUN server, as they are both already connected to the Game
Coordinator. But in the odd case it does fail, estabilishing the
connection fails without any further possibility to recover.
  • Loading branch information
TrueBrain committed Jul 20, 2021
1 parent 97cd81c commit 60e1362
Show file tree
Hide file tree
Showing 21 changed files with 620 additions and 2 deletions.
19 changes: 19 additions & 0 deletions docs/game_coordinator.md
Expand Up @@ -62,3 +62,22 @@ server can continue to talk to each other.
Some NAT gateways do not allow this method; for those this attempt will fail,
and this also means that it is not possible to create a connection between
the client and server.

## 3) Via TURN

As a last resort, the Game Coordinator can decide to connect the client and
server together via TURN. TURN is a relay service, relaying the messages
between client and server.

As the client and server can already connect to the Game Coordinator, it is
very likely this is successful.

It is important to note that a relay service has full view of the traffic
send between client and server, and as such it is important that you trust
the relay service used.
For official binaries, this relay service is hosted by openttd.org. The relay
service as hosted by openttd.org only validates it is relaying valid OpenTTD
packets and does no further inspection of the payload itself.
Although in our experience most patch-packs also use the services as offered
by openttd.org, it is possible they use different services. Please be mindful
about this.
14 changes: 14 additions & 0 deletions src/lang/english.txt
Expand Up @@ -1439,6 +1439,12 @@ STR_CONFIG_SETTING_OSK_ACTIVATION_DOUBLE_CLICK :Double click
STR_CONFIG_SETTING_OSK_ACTIVATION_SINGLE_CLICK_FOCUS :Single click (when focussed)
STR_CONFIG_SETTING_OSK_ACTIVATION_SINGLE_CLICK :Single click (immediately)

STR_CONFIG_SETTING_USE_RELAY_SERVICE :Use relay service: {STRING2}
STR_CONFIG_SETTING_USE_RELAY_SERVICE_HELPTEXT :If creating a connection to the server fails, one can use a relay service to create a connection. "Never" disallows this, "ask" will ask first, "allow" will allow it without asking
STR_CONFIG_SETTING_USE_RELAY_SERVICE_NEVER :Never
STR_CONFIG_SETTING_USE_RELAY_SERVICE_ASK :Ask
STR_CONFIG_SETTING_USE_RELAY_SERVICE_ALLOW :Allow

STR_CONFIG_SETTING_RIGHT_MOUSE_BTN_EMU :Right-click emulation: {STRING2}
STR_CONFIG_SETTING_RIGHT_MOUSE_BTN_EMU_HELPTEXT :Select the method to emulate right mouse-button clicks
STR_CONFIG_SETTING_RIGHT_MOUSE_BTN_EMU_COMMAND :Command+Click
Expand Down Expand Up @@ -1791,6 +1797,7 @@ STR_CONFIG_SETTING_ENVIRONMENT_INDUSTRIES :{ORANGE}Industr
STR_CONFIG_SETTING_ENVIRONMENT_CARGODIST :{ORANGE}Cargo distribution
STR_CONFIG_SETTING_AI :{ORANGE}Competitors
STR_CONFIG_SETTING_AI_NPC :{ORANGE}Computer players
STR_CONFIG_SETTING_NETWORK :{ORANGE}Network

STR_CONFIG_SETTING_PATHFINDER_NPF :NPF
STR_CONFIG_SETTING_PATHFINDER_YAPF_RECOMMENDED :YAPF {BLUE}(Recommended)
Expand Down Expand Up @@ -2167,6 +2174,7 @@ STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_UNKNOWN :{BLACK}Local
STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_ISOLATED :{RED}Remote players can't connect
STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_DIRECT :{BLACK}Public
STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_STUN :{BLACK}Behind NAT
STR_NETWORK_CLIENT_LIST_SERVER_CONNECTION_TYPE_TURN :{BLACK}Via relay
############ End of ConnectionType

STR_NETWORK_CLIENT_LIST_ADMIN_CLIENT_KICK :Kick
Expand All @@ -2180,6 +2188,12 @@ STR_NETWORK_CLIENT_LIST_ASK_CLIENT_BAN :{YELLOW}Are you
STR_NETWORK_CLIENT_LIST_ASK_COMPANY_RESET :{YELLOW}Are you sure you want to delete company '{COMPANY}'?
STR_NETWORK_CLIENT_LIST_ASK_COMPANY_UNLOCK :{YELLOW}Are you sure you want to reset the password of company '{COMPANY}'?

STR_NETWORK_ASK_RELAY_CAPTION :{WHITE}Use relay?
STR_NETWORK_ASK_RELAY_TEXT :{YELLOW}Failed to establish a connection between you and the server.{}Would you like to relay this session via '{RAW_STRING}'?
STR_NETWORK_ASK_RELAY_NO :{BLACK}No
STR_NETWORK_ASK_RELAY_YES_ONCE :{BLACK}Yes, this once
STR_NETWORK_ASK_RELAY_YES_ALWAYS :{BLACK}Yes, don't ask again

STR_NETWORK_SERVER :Server
STR_NETWORK_CLIENT :Client
STR_NETWORK_SPECTATORS :Spectators
Expand Down
2 changes: 2 additions & 0 deletions src/network/CMakeLists.txt
Expand Up @@ -26,6 +26,8 @@ add_files(
network_server.h
network_stun.cpp
network_stun.h
network_turn.cpp
network_turn.h
network_type.h
network_udp.cpp
network_udp.h
Expand Down
2 changes: 2 additions & 0 deletions src/network/core/CMakeLists.txt
Expand Up @@ -30,6 +30,8 @@ add_files(
tcp_listen.h
tcp_stun.cpp
tcp_stun.h
tcp_turn.cpp
tcp_turn.h
udp.cpp
udp.h
)
3 changes: 2 additions & 1 deletion src/network/core/config.h
Expand Up @@ -22,6 +22,7 @@ static const char * const NETWORK_CONTENT_MIRROR_URL = "/bananas";

static const uint16 NETWORK_COORDINATOR_SERVER_PORT = 3976; ///< The default port of the Game Coordinator server (TCP)
static const uint16 NETWORK_STUN_SERVER_PORT = 3975; ///< The default port of the STUN server (TCP)
static const uint16 NETWORK_TURN_SERVER_PORT = 3974; ///< The default port of the TURN server (TCP)
static const uint16 NETWORK_CONTENT_SERVER_PORT = 3978; ///< The default port of the content server (TCP)
static const uint16 NETWORK_CONTENT_MIRROR_PORT = 80; ///< The default port of the content mirror (TCP)
static const uint16 NETWORK_DEFAULT_PORT = 3979; ///< The default port of the game server (TCP & UDP)
Expand Down Expand Up @@ -49,7 +50,7 @@ static const uint16 COMPAT_MTU = 1460; ///< Numbe
static const byte NETWORK_GAME_ADMIN_VERSION = 1; ///< What version of the admin network do we use?
static const byte NETWORK_GAME_INFO_VERSION = 6; ///< What version of game-info do we use?
static const byte NETWORK_COMPANY_INFO_VERSION = 6; ///< What version of company info is this?
static const byte NETWORK_COORDINATOR_VERSION = 4; ///< What version of game-coordinator-protocol do we use?
static const byte NETWORK_COORDINATOR_VERSION = 5; ///< What version of game-coordinator-protocol do we use?

static const uint NETWORK_NAME_LENGTH = 80; ///< The maximum length of the server name and map name, in bytes including '\0'
static const uint NETWORK_COMPANY_NAME_LENGTH = 128; ///< The maximum length of the company name, in bytes including '\0'
Expand Down
2 changes: 2 additions & 0 deletions src/network/core/tcp_coordinator.cpp
Expand Up @@ -43,6 +43,7 @@ bool NetworkCoordinatorSocketHandler::HandlePacket(Packet *p)
case PACKET_COORDINATOR_SERCLI_STUN_RESULT: return this->Receive_SERCLI_STUN_RESULT(p);
case PACKET_COORDINATOR_GC_STUN_CONNECT: return this->Receive_GC_STUN_CONNECT(p);
case PACKET_COORDINATOR_GC_NEWGRF_LOOKUP: return this->Receive_GC_NEWGRF_LOOKUP(p);
case PACKET_COORDINATOR_GC_TURN_CONNECT: return this->Receive_GC_TURN_CONNECT(p);

default:
Debug(net, 0, "[tcp/coordinator] Received invalid packet type {}", type);
Expand Down Expand Up @@ -102,3 +103,4 @@ bool NetworkCoordinatorSocketHandler::Receive_GC_STUN_REQUEST(Packet *p) { retur
bool NetworkCoordinatorSocketHandler::Receive_SERCLI_STUN_RESULT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_SERCLI_STUN_RESULT); }
bool NetworkCoordinatorSocketHandler::Receive_GC_STUN_CONNECT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_GC_STUN_CONNECT); }
bool NetworkCoordinatorSocketHandler::Receive_GC_NEWGRF_LOOKUP(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_GC_NEWGRF_LOOKUP); }
bool NetworkCoordinatorSocketHandler::Receive_GC_TURN_CONNECT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_COORDINATOR_GC_TURN_CONNECT); }
16 changes: 16 additions & 0 deletions src/network/core/tcp_coordinator.h
Expand Up @@ -42,6 +42,7 @@ enum PacketCoordinatorType {
PACKET_COORDINATOR_SERCLI_STUN_RESULT, ///< Client/server informs the Game Coordinator of the result of the STUN request.
PACKET_COORDINATOR_GC_STUN_CONNECT, ///< Game Coordinator tells client/server to connect() reusing the STUN local address.
PACKET_COORDINATOR_GC_NEWGRF_LOOKUP, ///< Game Coordinator informs client about NewGRF lookup table updates needed for GC_LISTING.
PACKET_COORDINATOR_GC_TURN_CONNECT, ///< Game Coordinator tells client/server to connect to a specific TURN server.
PACKET_COORDINATOR_END, ///< Must ALWAYS be on the end of this list!! (period)
};

Expand All @@ -53,6 +54,7 @@ enum ConnectionType {
CONNECTION_TYPE_ISOLATED, ///< The Game Coordinator failed to find a way to connect to your server. Nobody will be able to join.
CONNECTION_TYPE_DIRECT, ///< The Game Coordinator can directly connect to your server.
CONNECTION_TYPE_STUN, ///< The Game Coordinator can connect to your server via a STUN request.
CONNECTION_TYPE_TURN, ///< The Game Coordinator needs you to connect to a relay.
};

/**
Expand Down Expand Up @@ -288,6 +290,20 @@ class NetworkCoordinatorSocketHandler : public NetworkTCPSocketHandler {
*/
virtual bool Receive_GC_NEWGRF_LOOKUP(Packet *p);

/**
* Game Coordinator requests that we make a connection to the indicated
* peer, which is a TURN server.
*
* string Token to track the current connect request.
* uint8 Tracking number to track current connect request.
* string Ticket to hand over to the TURN server.
* string Connection string of the TURN server.
*
* @param p The packet that was just received.
* @return True upon success, otherwise false.
*/
virtual bool Receive_GC_TURN_CONNECT(Packet *p);

bool HandlePacket(Packet *p);
public:
/**
Expand Down
71 changes: 71 additions & 0 deletions src/network/core/tcp_turn.cpp
@@ -0,0 +1,71 @@
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/

/**
* @file tcp_turn.cpp Basic functions to receive and send TURN packets.
*/

#include "../../stdafx.h"
#include "../../date_func.h"
#include "../../debug.h"
#include "tcp_turn.h"

#include "../../safeguards.h"

/**
* Handle the given packet, i.e. pass it to the right
* parser receive command.
* @param p the packet to handle
* @return true if we should immediately handle further packets, false otherwise
*/
bool NetworkTurnSocketHandler::HandlePacket(Packet *p)
{
PacketTurnType type = (PacketTurnType)p->Recv_uint8();

switch (type) {
case PACKET_TURN_TURN_ERROR: return this->Receive_TURN_ERROR(p);
case PACKET_TURN_SERCLI_CONNECT: return this->Receive_SERCLI_CONNECT(p);
case PACKET_TURN_TURN_CONNECTED: return this->Receive_TURN_CONNECTED(p);

default:
Debug(net, 0, "[tcp/turn] Received invalid packet type {}", type);
return false;
}
}

/**
* Receive a packet at TCP level
* @return Whether at least one packet was received.
*/
bool NetworkTurnSocketHandler::ReceivePackets()
{
Packet *p;
static const int MAX_PACKETS_TO_RECEIVE = 4;
int i = MAX_PACKETS_TO_RECEIVE;
while (--i != 0 && (p = this->ReceivePacket()) != nullptr) {
bool cont = this->HandlePacket(p);
delete p;
if (!cont) return true;
}

return i != MAX_PACKETS_TO_RECEIVE - 1;
}

/**
* Helper for logging receiving invalid packets.
* @param type The received packet type.
* @return Always false, as it's an error.
*/
bool NetworkTurnSocketHandler::ReceiveInvalidPacket(PacketTurnType type)
{
Debug(net, 0, "[tcp/turn] Received illegal packet type {}", type);
return false;
}

bool NetworkTurnSocketHandler::Receive_TURN_ERROR(Packet *p) { return this->ReceiveInvalidPacket(PACKET_TURN_TURN_ERROR); }
bool NetworkTurnSocketHandler::Receive_SERCLI_CONNECT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_TURN_SERCLI_CONNECT); }
bool NetworkTurnSocketHandler::Receive_TURN_CONNECTED(Packet *p) { return this->ReceiveInvalidPacket(PACKET_TURN_TURN_CONNECTED); }
79 changes: 79 additions & 0 deletions src/network/core/tcp_turn.h
@@ -0,0 +1,79 @@
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/

/**
* @file tcp_turn.h Basic functions to receive and send TCP packets to/from the TURN server.
*/

#ifndef NETWORK_CORE_TCP_TURN_H
#define NETWORK_CORE_TCP_TURN_H

#include "os_abstraction.h"
#include "tcp.h"
#include "packet.h"
#include "game_info.h"

/** Enum with all types of TCP TURN packets. The order MUST not be changed. **/
enum PacketTurnType {
PACKET_TURN_TURN_ERROR, ///< TURN server is unable to relay.
PACKET_TURN_SERCLI_CONNECT, ///< Client or server is connecting to the TURN server.
PACKET_TURN_TURN_CONNECTED, ///< TURN server indicates the socket is now being relayed.
PACKET_TURN_END, ///< Must ALWAYS be on the end of this list!! (period)
};

/** Base socket handler for all TURN TCP sockets. */
class NetworkTurnSocketHandler : public NetworkTCPSocketHandler {
protected:
bool ReceiveInvalidPacket(PacketTurnType type);

/**
* TURN server was unable to connect the client or server based on the
* token. Most likely cause is an invalid token or the other side that
* hasn't connected in a reasonable amount of time.
*
* @param p The packet that was just received.
* @return True upon success, otherwise false.
*/
virtual bool Receive_TURN_ERROR(Packet *p);

/**
* Client or servers wants to connect to the TURN server (on request by
* the Game Coordinator).
*
* uint8 Game Coordinator protocol version.
* string Token to track the current TURN request.
*
* @param p The packet that was just received.
* @return True upon success, otherwise false.
*/
virtual bool Receive_SERCLI_CONNECT(Packet *p);

/**
* TURN server has connected client and server together and will now relay
* all packets to each other. No further TURN packets should be send over
* this socket, and the socket should be handed over to the game protocol.
*
* string Hostname of the peer. This can be used to check if a client is not banned etc.
*
* @param p The packet that was just received.
* @return True upon success, otherwise false.
*/
virtual bool Receive_TURN_CONNECTED(Packet *p);

bool HandlePacket(Packet *p);
public:
/**
* Create a new cs socket handler for a given cs.
* @param s the socket we are connected with.
* @param address IP etc. of the client.
*/
NetworkTurnSocketHandler(SOCKET s = INVALID_SOCKET) : NetworkTCPSocketHandler(s) {}

bool ReceivePackets();
};

#endif /* NETWORK_CORE_TCP_TURN_H */

0 comments on commit 60e1362

Please sign in to comment.