2 * This file is part of OpenTTD.
3 * 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.
4 * 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.
5 * 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/>.
9 * @file tcp_connect.cpp Basic functions to create connections without blocking.
12 #include "../../stdafx.h"
13 #include "../../thread.h"
16 #include "../network_coordinator.h"
17 #include "../network_internal.h"
19 #include "../../safeguards.h"
21 /* static */ std::vector
<std::shared_ptr
<TCPConnecter
>> TCPConnecter::connecters
;
24 * Create a new connecter for the given address.
25 * @param connection_string The address to connect to.
26 * @param default_port If not indicated in connection_string, what port to use.
27 * @param bind_address The local bind address to use. Defaults to letting the OS find one.
29 TCPConnecter::TCPConnecter(const std::string
&connection_string
, uint16_t default_port
, const NetworkAddress
&bind_address
, int family
) :
30 bind_address(bind_address
),
33 this->connection_string
= NormalizeConnectionString(connection_string
, default_port
);
37 * Create a new connecter for the server.
38 * @param connection_string The address to connect to.
39 * @param default_port If not indicated in connection_string, what port to use.
41 TCPServerConnecter::TCPServerConnecter(const std::string
&connection_string
, uint16_t default_port
) :
42 server_address(ServerAddress::Parse(connection_string
, default_port
))
44 switch (this->server_address
.type
) {
45 case SERVER_ADDRESS_DIRECT
:
46 this->connection_string
= this->server_address
.connection_string
;
49 case SERVER_ADDRESS_INVITE_CODE
:
50 this->status
= Status::Connecting
;
51 _network_coordinator_client
.ConnectToServer(this->server_address
.connection_string
, this);
59 TCPConnecter::~TCPConnecter()
61 if (this->resolve_thread
.joinable()) {
62 this->resolve_thread
.join();
65 for (const auto &socket
: this->sockets
) {
68 this->sockets
.clear();
69 this->sock_to_address
.clear();
71 if (this->ai
!= nullptr) freeaddrinfo(this->ai
);
75 * Kill this connecter.
76 * It will abort as soon as it can and not call any of the callbacks.
78 void TCPConnecter::Kill()
80 /* Delay the removing of the socket till the next CheckActivity(). */
85 * Start a connection to the indicated address.
86 * @param address The address to connection to.
88 void TCPConnecter::Connect(addrinfo
*address
)
90 SOCKET sock
= socket(address
->ai_family
, address
->ai_socktype
, address
->ai_protocol
);
91 if (sock
== INVALID_SOCKET
) {
92 Debug(net
, 0, "Could not create {} {} socket: {}", NetworkAddress::SocketTypeAsString(address
->ai_socktype
), NetworkAddress::AddressFamilyAsString(address
->ai_family
), NetworkError::GetLast().AsString());
96 if (!SetReusePort(sock
)) {
97 Debug(net
, 0, "Setting reuse-port mode failed: {}", NetworkError::GetLast().AsString());
100 if (this->bind_address
.GetPort() > 0) {
101 if (bind(sock
, (const sockaddr
*)this->bind_address
.GetAddress(), this->bind_address
.GetAddressLength()) != 0) {
102 Debug(net
, 1, "Could not bind socket on {}: {}", this->bind_address
.GetAddressAsString(), NetworkError::GetLast().AsString());
108 if (!SetNoDelay(sock
)) {
109 Debug(net
, 1, "Setting TCP_NODELAY failed: {}", NetworkError::GetLast().AsString());
111 if (!SetNonBlocking(sock
)) {
112 Debug(net
, 0, "Setting non-blocking mode failed: {}", NetworkError::GetLast().AsString());
115 NetworkAddress network_address
= NetworkAddress(address
->ai_addr
, (int)address
->ai_addrlen
);
116 Debug(net
, 5, "Attempting to connect to {}", network_address
.GetAddressAsString());
118 int err
= connect(sock
, address
->ai_addr
, (int)address
->ai_addrlen
);
119 if (err
!= 0 && !NetworkError::GetLast().IsConnectInProgress()) {
122 Debug(net
, 1, "Could not connect to {}: {}", network_address
.GetAddressAsString(), NetworkError::GetLast().AsString());
126 this->sock_to_address
[sock
] = network_address
;
127 this->sockets
.push_back(sock
);
131 * Start the connect() for the next address in the list.
132 * @return True iff a new connect() is attempted.
134 bool TCPConnecter::TryNextAddress()
136 if (this->current_address
>= this->addresses
.size()) return false;
138 this->last_attempt
= std::chrono::steady_clock::now();
139 this->Connect(this->addresses
[this->current_address
++]);
145 * Callback when resolving is done.
146 * @param ai A linked-list of address information.
148 void TCPConnecter::OnResolved(addrinfo
*ai
)
150 std::deque
<addrinfo
*> addresses_ipv4
, addresses_ipv6
;
152 /* Apply "Happy Eyeballs" if it is likely IPv6 is functional. */
154 /* Detect if IPv6 is likely to succeed or not. */
155 bool seen_ipv6
= false;
157 for (addrinfo
*runp
= ai
; runp
!= nullptr; runp
= runp
->ai_next
) {
158 if (runp
->ai_family
== AF_INET6
) {
160 } else if (!seen_ipv6
) {
161 /* We see an IPv4 before an IPv6; this most likely means there is
162 * no IPv6 available on the system, so keep the order of this
169 /* Convert the addrinfo into NetworkAddresses. */
170 for (addrinfo
*runp
= ai
; runp
!= nullptr; runp
= runp
->ai_next
) {
171 /* Skip entries if the family is set and it is not matching. */
172 if (this->family
!= AF_UNSPEC
&& this->family
!= runp
->ai_family
) continue;
175 if (runp
->ai_family
== AF_INET6
) {
176 addresses_ipv6
.emplace_back(runp
);
178 addresses_ipv4
.emplace_back(runp
);
181 this->addresses
.emplace_back(runp
);
185 /* If we want to resort, make the list like IPv6 / IPv4 / IPv6 / IPv4 / ..
186 * for how ever many (round-robin) DNS entries we have. */
188 while (!addresses_ipv4
.empty() || !addresses_ipv6
.empty()) {
189 if (!addresses_ipv6
.empty()) {
190 this->addresses
.push_back(addresses_ipv6
.front());
191 addresses_ipv6
.pop_front();
193 if (!addresses_ipv4
.empty()) {
194 this->addresses
.push_back(addresses_ipv4
.front());
195 addresses_ipv4
.pop_front();
200 if (_debug_net_level
>= 6) {
201 if (this->addresses
.empty()) {
202 Debug(net
, 6, "{} did not resolve", this->connection_string
);
204 Debug(net
, 6, "{} resolved in:", this->connection_string
);
205 for (const auto &address
: this->addresses
) {
206 Debug(net
, 6, "- {}", NetworkAddress(address
->ai_addr
, (int)address
->ai_addrlen
).GetAddressAsString());
211 this->current_address
= 0;
215 * Start resolving the hostname.
217 * This function must change "status" to either Status::FAILURE
218 * or Status::CONNECTING before returning.
220 void TCPConnecter::Resolve()
222 /* Port is already guaranteed part of the connection_string. */
223 NetworkAddress address
= ParseConnectionString(this->connection_string
, 0);
226 memset(&hints
, 0, sizeof(hints
));
227 hints
.ai_family
= AF_UNSPEC
;
228 hints
.ai_flags
= AI_ADDRCONFIG
;
229 hints
.ai_socktype
= SOCK_STREAM
;
231 std::string port_name
= std::to_string(address
.GetPort());
233 static bool getaddrinfo_timeout_error_shown
= false;
234 auto start
= std::chrono::steady_clock::now();
237 int error
= getaddrinfo(address
.GetHostname().c_str(), port_name
.c_str(), &hints
, &ai
);
239 auto end
= std::chrono::steady_clock::now();
240 auto duration
= std::chrono::duration_cast
<std::chrono::seconds
>(end
- start
);
241 if (!getaddrinfo_timeout_error_shown
&& duration
>= std::chrono::seconds(5)) {
242 Debug(net
, 0, "getaddrinfo() for address \"{}\" took {} seconds", this->connection_string
, duration
.count());
243 Debug(net
, 0, " This is likely an issue in the DNS name resolver's configuration causing it to time out");
244 getaddrinfo_timeout_error_shown
= true;
248 Debug(net
, 0, "Failed to resolve DNS for {}", this->connection_string
);
249 this->status
= Status::Failure
;
254 this->OnResolved(ai
);
256 this->status
= Status::Connecting
;
260 * Thunk to start Resolve() on the right instance.
262 /* static */ void TCPConnecter::ResolveThunk(TCPConnecter
*connecter
)
264 connecter
->Resolve();
268 * Check if there was activity for this connecter.
269 * @return True iff the TCPConnecter is done and can be cleaned up.
271 bool TCPConnecter::CheckActivity()
273 if (this->killed
) return true;
275 switch (this->status
) {
277 /* Start the thread delayed, so the vtable is loaded. This allows classes
278 * to overload functions used by Resolve() (in case threading is disabled). */
279 if (StartNewThread(&this->resolve_thread
, "ottd:resolve", &TCPConnecter::ResolveThunk
, this)) {
280 this->status
= Status::Resolving
;
284 /* No threads, do a blocking resolve. */
287 /* Continue as we are either failed or can start the first
288 * connection. The rest of this function handles exactly that. */
291 case Status::Resolving
:
292 /* Wait till Resolve() comes back with an answer (in case it runs threaded). */
295 case Status::Failure
:
296 /* Ensure the OnFailure() is called from the game-thread instead of the
297 * resolve-thread, as otherwise we can get into some threading issues. */
301 case Status::Connecting
:
302 case Status::Connected
:
306 /* If there are no attempts pending, connect to the next. */
307 if (this->sockets
.empty()) {
308 if (!this->TryNextAddress()) {
309 /* There were no more addresses to try, so we failed. */
318 for (const auto &socket
: this->sockets
) {
319 FD_SET(socket
, &write_fd
);
325 int n
= select(FD_SETSIZE
, nullptr, &write_fd
, nullptr, &tv
);
326 /* select() failed; hopefully next try it doesn't. */
328 /* select() normally never fails; so hopefully it works next try! */
329 Debug(net
, 1, "select() failed: {}", NetworkError::GetLast().AsString());
333 /* No socket updates. */
335 /* Wait 250ms between attempting another address. */
336 if (std::chrono::steady_clock::now() < this->last_attempt
+ std::chrono::milliseconds(250)) return false;
338 /* Try the next address in the list. */
339 if (this->TryNextAddress()) return false;
341 /* Wait up to 3 seconds since the last connection we started. */
342 if (std::chrono::steady_clock::now() < this->last_attempt
+ std::chrono::milliseconds(3000)) return false;
344 /* More than 3 seconds no socket reported activity, and there are no
345 * more address to try. Timeout the attempt. */
346 Debug(net
, 0, "Timeout while connecting to {}", this->connection_string
);
348 for (const auto &socket
: this->sockets
) {
351 this->sockets
.clear();
352 this->sock_to_address
.clear();
358 /* If a socket is writeable, it is either in error-state or connected.
359 * Remove all sockets that are in error-state and mark the first that is
360 * not in error-state as the socket we will use for our connection. */
361 SOCKET connected_socket
= INVALID_SOCKET
;
362 for (auto it
= this->sockets
.begin(); it
!= this->sockets
.end(); /* nothing */) {
363 NetworkError socket_error
= GetSocketError(*it
);
364 if (socket_error
.HasError()) {
365 Debug(net
, 1, "Could not connect to {}: {}", this->sock_to_address
[*it
].GetAddressAsString(), socket_error
.AsString());
367 this->sock_to_address
.erase(*it
);
368 it
= this->sockets
.erase(it
);
372 /* No error but writeable means connected. */
373 if (connected_socket
== INVALID_SOCKET
&& FD_ISSET(*it
, &write_fd
)) {
374 connected_socket
= *it
;
380 /* All the writable sockets were in error state. So nothing is connected yet. */
381 if (connected_socket
== INVALID_SOCKET
) return false;
383 /* Close all sockets except the one we picked for our connection. */
384 for (auto it
= this->sockets
.begin(); it
!= this->sockets
.end(); /* nothing */) {
385 if (connected_socket
!= *it
) {
388 this->sock_to_address
.erase(*it
);
389 it
= this->sockets
.erase(it
);
392 Debug(net
, 3, "Connected to {}", this->connection_string
);
393 if (_debug_net_level
>= 5) {
394 Debug(net
, 5, "- using {}", NetworkAddress::GetPeerName(connected_socket
));
397 this->OnConnect(connected_socket
);
398 this->status
= Status::Connected
;
403 * Check if there was activity for this connecter.
404 * @return True iff the TCPConnecter is done and can be cleaned up.
406 bool TCPServerConnecter::CheckActivity()
408 if (this->killed
) return true;
410 switch (this->server_address
.type
) {
411 case SERVER_ADDRESS_DIRECT
:
412 return TCPConnecter::CheckActivity();
414 case SERVER_ADDRESS_INVITE_CODE
:
415 /* Check if a result has come in. */
416 switch (this->status
) {
417 case Status::Failure
:
421 case Status::Connected
:
422 this->OnConnect(this->socket
);
437 * The connection was successfully established.
438 * This socket is fully setup and ready to send/recv game protocol packets.
439 * @param sock The socket of the established connection.
441 void TCPServerConnecter::SetConnected(SOCKET sock
)
443 assert(sock
!= INVALID_SOCKET
);
446 this->status
= Status::Connected
;
450 * The connection couldn't be established.
452 void TCPServerConnecter::SetFailure()
454 this->status
= Status::Failure
;
458 * Check whether we need to call the callback, i.e. whether we
459 * have connected or aborted and call the appropriate callback
460 * for that. It's done this way to ease on the locking that
461 * would otherwise be needed everywhere.
463 /* static */ void TCPConnecter::CheckCallbacks()
465 TCPConnecter::connecters
.erase(
466 std::remove_if(TCPConnecter::connecters
.begin(), TCPConnecter::connecters
.end(),
467 [](auto &connecter
) { return connecter
->CheckActivity(); }),
468 TCPConnecter::connecters
.end());
471 /** Kill all connection attempts. */
472 /* static */ void TCPConnecter::KillAll()
474 TCPConnecter::connecters
.clear();