summaryrefslogtreecommitdiff
path: root/src/network
diff options
context:
space:
mode:
Diffstat (limited to 'src/network')
-rw-r--r--src/network/CMakeLists.txt2
-rw-r--r--src/network/core/CMakeLists.txt2
-rw-r--r--src/network/core/config.h3
-rw-r--r--src/network/core/tcp_coordinator.cpp2
-rw-r--r--src/network/core/tcp_coordinator.h16
-rw-r--r--src/network/core/tcp_turn.cpp71
-rw-r--r--src/network/core/tcp_turn.h79
-rw-r--r--src/network/network_coordinator.cpp83
-rw-r--r--src/network/network_coordinator.h9
-rw-r--r--src/network/network_gui.cpp102
-rw-r--r--src/network/network_gui.h1
-rw-r--r--src/network/network_turn.cpp135
-rw-r--r--src/network/network_turn.h41
13 files changed, 544 insertions, 2 deletions
diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt
index 8b0157918..ac500a22c 100644
--- a/src/network/CMakeLists.txt
+++ b/src/network/CMakeLists.txt
@@ -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
diff --git a/src/network/core/CMakeLists.txt b/src/network/core/CMakeLists.txt
index bcecad38c..756fa9e8f 100644
--- a/src/network/core/CMakeLists.txt
+++ b/src/network/core/CMakeLists.txt
@@ -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
)
diff --git a/src/network/core/config.h b/src/network/core/config.h
index 1b66c26cf..10ec070f0 100644
--- a/src/network/core/config.h
+++ b/src/network/core/config.h
@@ -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)
@@ -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'
diff --git a/src/network/core/tcp_coordinator.cpp b/src/network/core/tcp_coordinator.cpp
index 75f3f246f..44395a905 100644
--- a/src/network/core/tcp_coordinator.cpp
+++ b/src/network/core/tcp_coordinator.cpp
@@ -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);
@@ -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); }
diff --git a/src/network/core/tcp_coordinator.h b/src/network/core/tcp_coordinator.h
index b5395ad73..dea61cdec 100644
--- a/src/network/core/tcp_coordinator.h
+++ b/src/network/core/tcp_coordinator.h
@@ -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)
};
@@ -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.
};
/**
@@ -288,6 +290,20 @@ protected:
*/
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:
/**
diff --git a/src/network/core/tcp_turn.cpp b/src/network/core/tcp_turn.cpp
new file mode 100644
index 000000000..026b64194
--- /dev/null
+++ b/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); }
diff --git a/src/network/core/tcp_turn.h b/src/network/core/tcp_turn.h
new file mode 100644
index 000000000..082373199
--- /dev/null
+++ b/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 */
diff --git a/src/network/network_coordinator.cpp b/src/network/network_coordinator.cpp
index 9fe1e78f0..6456ac9e8 100644
--- a/src/network/network_coordinator.cpp
+++ b/src/network/network_coordinator.cpp
@@ -18,6 +18,7 @@
#include "network.h"
#include "network_coordinator.h"
#include "network_gamelist.h"
+#include "network_gui.h"
#include "network_internal.h"
#include "network_server.h"
#include "network_stun.h"
@@ -193,6 +194,7 @@ bool ClientNetworkCoordinatorSocketHandler::Receive_GC_REGISTER_ACK(Packet *p)
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_TURN: connection_type = "Via relay"; 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.
@@ -355,6 +357,50 @@ bool ClientNetworkCoordinatorSocketHandler::Receive_GC_NEWGRF_LOOKUP(Packet *p)
return true;
}
+bool ClientNetworkCoordinatorSocketHandler::Receive_GC_TURN_CONNECT(Packet *p)
+{
+ std::string token = p->Recv_string(NETWORK_TOKEN_LENGTH);
+ uint8 tracking_number = p->Recv_uint8();
+ std::string ticket = p->Recv_string(NETWORK_TOKEN_LENGTH);
+ std::string connection_string = p->Recv_string(NETWORK_HOSTNAME_PORT_LENGTH);
+
+ /* Ensure all other pending connection attempts are killed. */
+ if (this->game_connecter != nullptr) {
+ this->game_connecter->Kill();
+ this->game_connecter = nullptr;
+ }
+
+ this->turn_handlers[token] = ClientNetworkTurnSocketHandler::Turn(token, tracking_number, ticket, connection_string);
+
+ if (!_network_server) {
+ switch (_settings_client.network.use_relay_service) {
+ case URS_NEVER:
+ this->ConnectFailure(token, 0);
+ break;
+
+ case URS_ASK:
+ ShowNetworkAskRelay(connection_string, token);
+ break;
+
+ case URS_ALLOW:
+ this->StartTurnConnection(token);
+ break;
+ }
+ } else {
+ this->StartTurnConnection(token);
+ }
+
+ return true;
+}
+
+void ClientNetworkCoordinatorSocketHandler::StartTurnConnection(std::string &token)
+{
+ auto turn_it = this->turn_handlers.find(token);
+ if (turn_it == this->turn_handlers.end()) return;
+
+ turn_it->second->Connect();
+}
+
void ClientNetworkCoordinatorSocketHandler::Connect()
{
/* We are either already connected or are trying to connect. */
@@ -580,13 +626,33 @@ void ClientNetworkCoordinatorSocketHandler::CloseStunHandler(const std::string &
}
/**
+ * Close the TURN handler.
+ * @param token The token used for the TURN handler.
+ */
+void ClientNetworkCoordinatorSocketHandler::CloseTurnHandler(const std::string &token)
+{
+ CloseWindowByClass(WC_NETWORK_ASK_RELAY);
+
+ auto turn_it = this->turn_handlers.find(token);
+ if (turn_it == this->turn_handlers.end()) return;
+
+ turn_it->second->CloseConnection();
+ turn_it->second->CloseSocket();
+
+ /* We don't remove turn_handler here, as we can be called from within that
+ * turn_handler instance, so our object cannot be free'd yet. Instead, we
+ * check later if the connection is closed, and free the object then. */
+}
+
+/**
* Close everything related to this connection token.
* @param token The connection token to close.
*/
void ClientNetworkCoordinatorSocketHandler::CloseToken(const std::string &token)
{
- /* Close all remaining STUN connections. */
+ /* Close all remaining STUN / TURN connections. */
this->CloseStunHandler(token);
+ this->CloseTurnHandler(token);
/* Close the caller of the connection attempt. */
auto connecter_it = this->connecter.find(token);
@@ -610,12 +676,14 @@ void ClientNetworkCoordinatorSocketHandler::CloseAllConnections()
/* Mark any pending connecters as failed. */
for (auto &[token, it] : this->connecter) {
this->CloseStunHandler(token);
+ this->CloseTurnHandler(token);
it->SetFailure();
/* Inform the Game Coordinator he can stop trying to connect us to the server. */
this->ConnectFailure(token, 0);
}
this->stun_handlers.clear();
+ this->turn_handlers.clear();
this->connecter.clear();
/* Also close any pending invite-code requests. */
@@ -697,4 +765,17 @@ void ClientNetworkCoordinatorSocketHandler::SendReceive()
stun_handler->SendReceive();
}
}
+
+ /* Check for handlers that are not connecting nor connected. Destroy those objects. */
+ for (auto turn_it = this->turn_handlers.begin(); turn_it != this->turn_handlers.end(); /* nothing */) {
+ if (turn_it->second->connect_started && turn_it->second->connecter == nullptr && !turn_it->second->IsConnected()) {
+ turn_it = this->turn_handlers.erase(turn_it);
+ } else {
+ turn_it++;
+ }
+ }
+
+ for (const auto &[token, turn_handler] : this->turn_handlers) {
+ turn_handler->SendReceive();
+ }
}
diff --git a/src/network/network_coordinator.h b/src/network/network_coordinator.h
index 6a7c79e70..42e16d91d 100644
--- a/src/network/network_coordinator.h
+++ b/src/network/network_coordinator.h
@@ -12,6 +12,7 @@
#include "core/tcp_coordinator.h"
#include "network_stun.h"
+#include "network_turn.h"
#include <map>
/**
@@ -42,6 +43,10 @@
* - a) Server/client connect, client sends CLIENT_CONNECTED to Game Coordinator.
* - b) Server/client connect fails, both send SERCLI_CONNECT_FAILED to Game Coordinator.
* - Game Coordinator tries other combination if available.
+ * 3) TURN?
+ * - Game Coordinator sends GC_TURN_CONNECT to server/client.
+ * - a) Server/client connect, client sends CLIENT_CONNECTED to Game Coordinator.
+ * - b) Server/client connect fails, both send SERCLI_CONNECT_FAILED to Game Coordinator.
* - If all fails, Game Coordinator sends GC_CONNECT_FAILED to indicate no connection is possible.
*/
@@ -52,6 +57,7 @@ private:
std::map<std::string, TCPServerConnecter *> connecter; ///< Based on tokens, the current connecters that are pending.
std::map<std::string, TCPServerConnecter *> connecter_pre; ///< Based on invite codes, the current connecters that are pending.
std::map<std::string, std::map<int, std::unique_ptr<ClientNetworkStunSocketHandler>>> stun_handlers; ///< All pending STUN handlers, stored by token:family.
+ std::map<std::string, std::unique_ptr<ClientNetworkTurnSocketHandler>> turn_handlers; ///< Pending TURN handler (if any), stored by token.
TCPConnecter *game_connecter = nullptr; ///< Pending connecter to the game server.
uint32 newgrf_lookup_table_cursor = 0; ///< Last received cursor for the #GameInfoNewGRFLookupTable updates.
@@ -67,6 +73,7 @@ protected:
bool Receive_GC_STUN_REQUEST(Packet *p) override;
bool Receive_GC_STUN_CONNECT(Packet *p) override;
bool Receive_GC_NEWGRF_LOOKUP(Packet *p) override;
+ bool Receive_GC_TURN_CONNECT(Packet *p) override;
public:
/** The idle timeout; when to close the connection because it's idle. */
@@ -88,12 +95,14 @@ public:
void CloseToken(const std::string &token);
void CloseAllConnections();
void CloseStunHandler(const std::string &token, uint8 family = AF_UNSPEC);
+ void CloseTurnHandler(const std::string &token);
void Register();
void SendServerUpdate();
void GetListing();
void ConnectToServer(const std::string &invite_code, TCPServerConnecter *connecter);
+ void StartTurnConnection(std::string &token);
};
extern ClientNetworkCoordinatorSocketHandler _network_coordinator_client;
diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp
index 1368ac0ad..a535962b5 100644
--- a/src/network/network_gui.cpp
+++ b/src/network/network_gui.cpp
@@ -2699,3 +2699,105 @@ void ShowNetworkCompanyPasswordWindow(Window *parent)
new NetworkCompanyPasswordWindow(&_network_company_password_window_desc, parent);
}
+
+/**
+ * Window used for asking the user if he is okay using a TURN server.
+ */
+struct NetworkAskRelayWindow : public Window {
+ std::string connection_string; ///< The TURN server we want to connect to.
+ std::string token; ///< The token for this connection.
+
+ NetworkAskRelayWindow(WindowDesc *desc, Window *parent, const std::string &connection_string, const std::string &token) : Window(desc), connection_string(connection_string), token(token)
+ {
+ this->parent = parent;
+ this->InitNested(0);
+ }
+
+ void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
+ {
+ if (widget == WID_NAR_TEXT) {
+ *size = GetStringBoundingBox(STR_NETWORK_ASK_RELAY_TEXT);
+ size->height = GetStringHeight(STR_NETWORK_ASK_RELAY_TEXT, size->width - WD_FRAMETEXT_LEFT - WD_FRAMETEXT_RIGHT) + WD_FRAMETEXT_BOTTOM + WD_FRAMETEXT_TOP;
+ }
+ }
+
+ void DrawWidget(const Rect &r, int widget) const override
+ {
+ if (widget == WID_NAR_TEXT) {
+ DrawStringMultiLine(r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT, r.top + WD_FRAMETEXT_TOP, r.bottom - WD_FRAMETEXT_BOTTOM, STR_NETWORK_ASK_RELAY_TEXT, TC_FROMSTRING, SA_CENTER);
+ }
+ }
+
+ void FindWindowPlacementAndResize(int def_width, int def_height) override
+ {
+ /* Position query window over the calling window, ensuring it's within screen bounds. */
+ this->left = Clamp(parent->left + (parent->width / 2) - (this->width / 2), 0, _screen.width - this->width);
+ this->top = Clamp(parent->top + (parent->height / 2) - (this->height / 2), 0, _screen.height - this->height);
+ this->SetDirty();
+ }
+
+ void SetStringParameters(int widget) const override
+ {
+ switch (widget) {
+ case WID_NAR_TEXT:
+ SetDParamStr(0, this->connection_string);
+ break;
+ }
+ }
+
+ void OnClick(Point pt, int widget, int click_count) override
+ {
+ switch (widget) {
+ case WID_NAR_NO:
+ _network_coordinator_client.ConnectFailure(this->token, 0);
+ this->Close();
+ break;
+
+ case WID_NAR_YES_ONCE:
+ _network_coordinator_client.StartTurnConnection(this->token);
+ this->Close();
+ break;
+
+ case WID_NAR_YES_ALWAYS:
+ _settings_client.network.use_relay_service = URS_ALLOW;
+ _network_coordinator_client.StartTurnConnection(this->token);
+ this->Close();
+ break;
+ }
+ }
+};
+
+static const NWidgetPart _nested_network_ask_relay_widgets[] = {
+ NWidget(NWID_HORIZONTAL),
+ NWidget(WWT_CLOSEBOX, COLOUR_RED),
+ NWidget(WWT_CAPTION, COLOUR_RED, WID_NAR_CAPTION), SetDataTip(STR_NETWORK_ASK_RELAY_CAPTION, STR_NULL),
+ EndContainer(),
+ NWidget(WWT_PANEL, COLOUR_RED), SetPIP(0, 0, 8),
+ NWidget(WWT_TEXT, COLOUR_RED, WID_NAR_TEXT), SetAlignment(SA_HOR_CENTER), SetFill(1, 1),
+ NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(10, 15, 10),
+ NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NAR_NO), SetMinimalSize(71, 12), SetFill(1, 1), SetDataTip(STR_NETWORK_ASK_RELAY_NO, STR_NULL),
+ NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NAR_YES_ONCE), SetMinimalSize(71, 12), SetFill(1, 1), SetDataTip(STR_NETWORK_ASK_RELAY_YES_ONCE, STR_NULL),
+ NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_NAR_YES_ALWAYS), SetMinimalSize(71, 12), SetFill(1, 1), SetDataTip(STR_NETWORK_ASK_RELAY_YES_ALWAYS, STR_NULL),
+ EndContainer(),
+ EndContainer(),
+};
+
+static WindowDesc _network_ask_relay_desc(
+ WDP_CENTER, nullptr, 0, 0,
+ WC_NETWORK_ASK_RELAY, WC_NONE,
+ WDF_MODAL,
+ _nested_network_ask_relay_widgets, lengthof(_nested_network_ask_relay_widgets)
+);
+
+/**
+ * Show a modal confirmation window with "no" / "yes, once" / "yes, always" buttons.
+ * @param connection_string The relay server we want to connect to.
+ * @param token The token for this connection.
+ */
+void ShowNetworkAskRelay(const std::string &connection_string, const std::string &token)
+{
+ CloseWindowByClass(WC_NETWORK_ASK_RELAY);
+
+ Window *parent = FindWindowById(WC_MAIN_WINDOW, 0);
+ new NetworkAskRelayWindow(&_network_ask_relay_desc, parent, connection_string, token);
+}
diff --git a/src/network/network_gui.h b/src/network/network_gui.h
index 855d7d53c..06b501cb5 100644
--- a/src/network/network_gui.h
+++ b/src/network/network_gui.h
@@ -39,5 +39,6 @@ struct NetworkCompanyInfo : NetworkCompanyStats {
NetworkCompanyInfo *GetLobbyCompanyInfo(CompanyID company);
NetworkGameList *GetLobbyGameInfo();
+void ShowNetworkAskRelay(const std::string &connection_string, const std::string &token);
#endif /* NETWORK_GUI_H */
diff --git a/src/network/network_turn.cpp b/src/network/network_turn.cpp
new file mode 100644
index 000000000..e04bec47c
--- /dev/null
+++ b/src/network/network_turn.cpp
@@ -0,0 +1,135 @@
+/*
+ * 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 network_turn.cpp TURN sending/receiving part of the network protocol. */
+
+#include "../stdafx.h"
+#include "../debug.h"
+#include "../error.h"
+#include "../strings_func.h"
+#include "network_coordinator.h"
+#include "network_turn.h"
+
+#include "table/strings.h"
+
+#include "../safeguards.h"
+
+/** Connect to the TURN server. */
+class NetworkTurnConnecter : public TCPConnecter {
+private:
+ ClientNetworkTurnSocketHandler *handler;
+
+public:
+ /**
+ * Initiate the connecting.
+ * @param connection_string The address of the TURN server.
+ */
+ NetworkTurnConnecter(ClientNetworkTurnSocketHandler *handler, const std::string &connection_string) : TCPConnecter(connection_string, NETWORK_TURN_SERVER_PORT), handler(handler) {}
+
+ void OnFailure() override
+ {
+ this->handler->connecter = nullptr;
+
+ this->handler->ConnectFailure();
+ }
+
+ void OnConnect(SOCKET s) override
+ {
+ this->handler->connecter = nullptr;
+
+ handler->sock = s;
+ }
+};
+
+bool ClientNetworkTurnSocketHandler::Receive_TURN_ERROR(Packet *p)
+{
+ this->ConnectFailure();
+
+ return false;
+}
+
+bool ClientNetworkTurnSocketHandler::Receive_TURN_CONNECTED(Packet *p)
+{
+ std::string hostname = p->Recv_string(NETWORK_HOSTNAME_LENGTH);
+
+ /* Act like we no longer have a socket, as we are handing it over to the
+ * game handler. */
+ SOCKET game_sock = this->sock;
+ this->sock = INVALID_SOCKET;
+
+ NetworkAddress address = NetworkAddress(hostname, NETWORK_DEFAULT_PORT);
+ _network_coordinator_client.ConnectSuccess(this->token, game_sock, address);
+
+ return false;
+}
+
+/**
+ * Connect to the TURN server.
+ */
+void ClientNetworkTurnSocketHandler::Connect()
+{
+ this->connect_started = true;
+ this->connecter = new NetworkTurnConnecter(this, this->connection_string);
+}
+
+/**
+ * Prepare a TURN connection.
+ * Not until you run Connect() on the resulting instance will it start setting
+ * up the TURN connection.
+ * @param token The token as received from the Game Coordinator.
+ * @param tracking_number The tracking number as recieved from the Game Coordinator.
+ * @param ticket The ticket as received from the Game Coordinator.
+ * @param connection_string Connection string of the TURN server.
+ * @return The handler for this TURN connection.
+ */
+/* static */ std::unique_ptr<ClientNetworkTurnSocketHandler> ClientNetworkTurnSocketHandler::Turn(const std::string &token, uint8 tracking_number, const std::string &ticket, const std::string &connection_string)
+{
+ auto turn_handler = std::make_unique<ClientNetworkTurnSocketHandler>(token, tracking_number, connection_string);
+
+ Packet *p = new Packet(PACKET_TURN_SERCLI_CONNECT);
+ p->Send_uint8(NETWORK_COORDINATOR_VERSION);
+ p->Send_string(ticket);
+
+ turn_handler->SendPacket(p);
+
+ return turn_handler;
+}
+
+void ClientNetworkTurnSocketHandler::ConnectFailure()
+{
+ _network_coordinator_client.ConnectFailure(this->token, this->tracking_number);
+}
+
+NetworkRecvStatus ClientNetworkTurnSocketHandler::CloseConnection(bool error)
+{
+ NetworkTurnSocketHandler::CloseConnection(error);
+
+ /* If our connecter is still pending, shut it down too. Otherwise the
+ * callback of the connecter can call into us, and our object is most
+ * likely about to be destroyed. */
+ if (this->connecter != nullptr) {
+ this->connecter->Kill();
+ this->connecter = nullptr;
+ }
+
+ return NETWORK_RECV_STATUS_OKAY;
+}
+
+/**
+ * Check whether we received/can send some data from/to the TURN server and
+ * when that's the case handle it appropriately
+ */
+void ClientNetworkTurnSocketHandler::SendReceive()
+{
+ if (this->sock == INVALID_SOCKET) return;
+
+ if (this->CanSendReceive()) {
+ this->ReceivePackets();
+ }
+
+ this->SendPackets();
+}
diff --git a/src/network/network_turn.h b/src/network/network_turn.h
new file mode 100644
index 000000000..cc569a977
--- /dev/null
+++ b/src/network/network_turn.h
@@ -0,0 +1,41 @@
+/*
+ * 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 network_turn.h Part of the network protocol handling TURN requests. */
+
+#ifndef NETWORK_TURN_H
+#define NETWORK_TURN_H
+
+#include "core/tcp_turn.h"
+
+/** Class for handling the client side of the TURN connection. */
+class ClientNetworkTurnSocketHandler : public NetworkTurnSocketHandler {
+private:
+ std::string token; ///< Token of this connection.
+ uint8 tracking_number; ///< Tracking number of this connection.
+ std::string connection_string; ///< The connection string of the TURN server we are connecting to.
+
+protected:
+ bool Receive_TURN_ERROR(Packet *p) override;
+ bool Receive_TURN_CONNECTED(Packet *p) override;
+
+public:
+ TCPConnecter *connecter = nullptr; ///< Connecter instance.
+ bool connect_started = false; ///< Whether we started the connection.
+
+ ClientNetworkTurnSocketHandler(const std::string &token, uint8 tracking_number, const std::string &connection_string) : token(token), tracking_number(tracking_number), connection_string(connection_string) {}
+
+ NetworkRecvStatus CloseConnection(bool error = true) override;
+ void SendReceive();
+
+ void Connect();
+ void ConnectFailure();
+
+ static std::unique_ptr<ClientNetworkTurnSocketHandler> Turn(const std::string &token, uint8 tracking_number, const std::string &ticket, const std::string &connection_string);
+};
+
+#endif /* NETWORK_TURN_H */