OpenTTD Source 20250524-master-gc366e6a48e
address.cpp
Go to the documentation of this file.
1/*
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/>.
6 */
7
10#include "../../stdafx.h"
11
12#include "address.h"
13#include "../network_internal.h"
14#include "../../debug.h"
15#include "../../core/string_consumer.hpp"
16
17#include "../../safeguards.h"
18
24const std::string &NetworkAddress::GetHostname()
25{
26 if (this->hostname.empty() && this->address.ss_family != AF_UNSPEC) {
27 assert(this->address_length != 0);
28 char buffer[NETWORK_HOSTNAME_LENGTH];
29 getnameinfo((struct sockaddr *)&this->address, this->address_length, buffer, sizeof(buffer), nullptr, 0, NI_NUMERICHOST);
30 this->hostname = buffer;
31 }
32 return this->hostname;
33}
34
40{
41 switch (this->address.ss_family) {
42 case AF_UNSPEC:
43 case AF_INET:
44 return ntohs(((const struct sockaddr_in *)&this->address)->sin_port);
45
46 case AF_INET6:
47 return ntohs(((const struct sockaddr_in6 *)&this->address)->sin6_port);
48
49 default:
50 NOT_REACHED();
51 }
52}
53
58void NetworkAddress::SetPort(uint16_t port)
59{
60 switch (this->address.ss_family) {
61 case AF_UNSPEC:
62 case AF_INET:
63 ((struct sockaddr_in*)&this->address)->sin_port = htons(port);
64 break;
65
66 case AF_INET6:
67 ((struct sockaddr_in6*)&this->address)->sin6_port = htons(port);
68 break;
69
70 default:
71 NOT_REACHED();
72 }
73}
74
81static std::string_view GetAddressFormatString(uint16_t family, bool with_family)
82{
83 switch (family) {
84 case AF_INET: return with_family ? "{}:{} (IPv4)" : "{}:{}";
85 case AF_INET6: return with_family ? "[{}]:{} (IPv6)" : "[{}]:{}";
86 default: return with_family ? "{}:{} (IPv?)" : "{}:{}";
87 }
88}
89
95std::string NetworkAddress::GetAddressAsString(bool with_family)
96{
97 return fmt::format(fmt::runtime(GetAddressFormatString(this->GetAddress()->ss_family, with_family)), this->GetHostname(), this->GetPort());
98}
99
104static SOCKET ResolveLoopProc(addrinfo *)
105{
106 /* We just want the first 'entry', so return a valid socket. */
107 return !INVALID_SOCKET;
108}
109
114const sockaddr_storage *NetworkAddress::GetAddress()
115{
116 if (!this->IsResolved()) {
117 /* Here we try to resolve a network address. We use SOCK_STREAM as
118 * socket type because some stupid OSes, like Solaris, cannot be
119 * bothered to implement the specifications and allow '0' as value
120 * that means "don't care whether it is SOCK_STREAM or SOCK_DGRAM".
121 */
122 this->Resolve(this->address.ss_family, SOCK_STREAM, AI_ADDRCONFIG, nullptr, ResolveLoopProc);
123 this->resolved = true;
124 }
125 return &this->address;
126}
127
134{
135 if (!this->IsResolved()) {
136 this->Resolve(family, SOCK_STREAM, AI_ADDRCONFIG, nullptr, ResolveLoopProc);
137 }
138 return this->address.ss_family == family;
139}
140
147bool NetworkAddress::IsInNetmask(std::string_view netmask)
148{
149 /* Resolve it if we didn't do it already */
150 if (!this->IsResolved()) this->GetAddress();
151
152 int cidr = this->address.ss_family == AF_INET ? 32 : 128;
153
154 StringConsumer consumer{netmask};
155 /* Check for CIDR separator */
156 NetworkAddress mask_address(consumer.ReadUntilChar('/', StringConsumer::SKIP_ONE_SEPARATOR), 0, this->address.ss_family);
157 if (mask_address.GetAddressLength() == 0) return false;
158
159 if (consumer.AnyBytesLeft()) {
160 int tmp_cidr = consumer.ReadIntegerBase(10, cidr);
161
162 /* Invalid CIDR, treat as single host */
163 if (tmp_cidr > 0 && tmp_cidr < cidr) cidr = tmp_cidr;
164 }
165
166 uint32_t *ip;
167 uint32_t *mask;
168 switch (this->address.ss_family) {
169 case AF_INET:
170 ip = (uint32_t*)&((struct sockaddr_in*)&this->address)->sin_addr.s_addr;
171 mask = (uint32_t*)&((struct sockaddr_in*)&mask_address.address)->sin_addr.s_addr;
172 break;
173
174 case AF_INET6:
175 ip = (uint32_t*)&((struct sockaddr_in6*)&this->address)->sin6_addr;
176 mask = (uint32_t*)&((struct sockaddr_in6*)&mask_address.address)->sin6_addr;
177 break;
178
179 default:
180 NOT_REACHED();
181 }
182
183 while (cidr > 0) {
184 uint32_t msk = cidr >= 32 ? (uint32_t)-1 : htonl(-(1 << (32 - cidr)));
185 if ((*mask++ & msk) != (*ip++ & msk)) return false;
186
187 cidr -= 32;
188 }
189
190 return true;
191}
192
202SOCKET NetworkAddress::Resolve(int family, int socktype, int flags, SocketList *sockets, LoopProc func)
203{
204 struct addrinfo *ai;
205 struct addrinfo hints{};
206 hints.ai_family = family;
207 hints.ai_flags = flags;
208 hints.ai_socktype = socktype;
209
210 /* The port needs to be a string. Six is enough to contain all characters + '\0'. */
211 std::string port_name = fmt::format("{}", this->GetPort());
212
213 bool reset_hostname = false;
214 /* Setting both hostname to nullptr and port to 0 is not allowed.
215 * As port 0 means bind to any port, the other must mean that
216 * we want to bind to 'all' IPs. */
217 if (this->hostname.empty() && this->address_length == 0 && this->GetPort() == 0) {
218 reset_hostname = true;
219 int fam = this->address.ss_family;
220 if (fam == AF_UNSPEC) fam = family;
221 this->hostname = fam == AF_INET ? "0.0.0.0" : "::";
222 }
223
224 static bool _resolve_timeout_error_message_shown = false;
225 auto start = std::chrono::steady_clock::now();
226 int e = getaddrinfo(this->hostname.empty() ? nullptr : this->hostname.c_str(), port_name.c_str(), &hints, &ai);
227 auto end = std::chrono::steady_clock::now();
228 std::chrono::seconds duration = std::chrono::duration_cast<std::chrono::seconds>(end - start);
229 if (!_resolve_timeout_error_message_shown && duration >= std::chrono::seconds(5)) {
230 Debug(net, 0, "getaddrinfo for hostname \"{}\", port {}, address family {} and socket type {} took {} seconds",
231 this->hostname, port_name, AddressFamilyAsString(family), SocketTypeAsString(socktype), duration.count());
232 Debug(net, 0, " this is likely an issue in the DNS name resolver's configuration causing it to time out");
233 _resolve_timeout_error_message_shown = true;
234 }
235
236
237 if (reset_hostname) this->hostname.clear();
238
239 if (e != 0) {
240 if (func != ResolveLoopProc) {
241 Debug(net, 0, "getaddrinfo for hostname \"{}\", port {}, address family {} and socket type {} failed: {}",
242 this->hostname, port_name, AddressFamilyAsString(family), SocketTypeAsString(socktype), FS2OTTD(gai_strerror(e)));
243 }
244 return INVALID_SOCKET;
245 }
246
247 SOCKET sock = INVALID_SOCKET;
248 for (struct addrinfo *runp = ai; runp != nullptr; runp = runp->ai_next) {
249 /* When we are binding to multiple sockets, make sure we do not
250 * connect to one with exactly the same address twice. That's
251 * of course totally unneeded ;) */
252 if (sockets != nullptr) {
253 NetworkAddress address(runp->ai_addr, (int)runp->ai_addrlen);
254 if (std::any_of(sockets->begin(), sockets->end(), [&address](const auto &p) { return p.second == address; })) continue;
255 }
256 sock = func(runp);
257 if (sock == INVALID_SOCKET) continue;
258
259 if (sockets == nullptr) {
260 this->address_length = (int)runp->ai_addrlen;
261 assert(sizeof(this->address) >= runp->ai_addrlen);
262 std::copy_n(reinterpret_cast<const std::byte *>(runp->ai_addr), runp->ai_addrlen, reinterpret_cast<std::byte *>(&this->address));
263#ifdef __EMSCRIPTEN__
264 /* Emscripten doesn't zero sin_zero, but as we compare addresses
265 * to see if they are the same address, we need them to be zero'd.
266 * Emscripten is, as far as we know, the only OS not doing this.
267 *
268 * https://github.com/emscripten-core/emscripten/issues/12998
269 */
270 if (this->address.ss_family == AF_INET) {
271 sockaddr_in *address_ipv4 = (sockaddr_in *)&this->address;
272 std::ranges::fill(address_ipv4->sin_zero, 0);
273 }
274#endif
275 break;
276 }
277
278 NetworkAddress addr(runp->ai_addr, (int)runp->ai_addrlen);
279 (*sockets)[sock] = std::move(addr);
280 sock = INVALID_SOCKET;
281 }
282 freeaddrinfo (ai);
283
284 return sock;
285}
286
292static SOCKET ListenLoopProc(addrinfo *runp)
293{
294 std::string address = NetworkAddress(runp->ai_addr, (int)runp->ai_addrlen).GetAddressAsString();
295
296 SOCKET sock = socket(runp->ai_family, runp->ai_socktype, runp->ai_protocol);
297 if (sock == INVALID_SOCKET) {
298 std::string_view type = NetworkAddress::SocketTypeAsString(runp->ai_socktype);
299 std::string_view family = NetworkAddress::AddressFamilyAsString(runp->ai_family);
300 Debug(net, 0, "Could not create {} {} socket: {}", type, family, NetworkError::GetLast().AsString());
301 return INVALID_SOCKET;
302 }
303
304 if (runp->ai_socktype == SOCK_STREAM && !SetNoDelay(sock)) {
305 Debug(net, 1, "Setting no-delay mode failed: {}", NetworkError::GetLast().AsString());
306 }
307
308 if (!SetReusePort(sock)) {
309 Debug(net, 0, "Setting reuse-address mode failed: {}", NetworkError::GetLast().AsString());
310 }
311
312 int on = 1;
313 if (runp->ai_family == AF_INET6 &&
314 setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const char *>(&on), sizeof(on)) == -1) {
315 Debug(net, 3, "Could not disable IPv4 over IPv6: {}", NetworkError::GetLast().AsString());
316 }
317
318 if (bind(sock, runp->ai_addr, (int)runp->ai_addrlen) != 0) {
319 Debug(net, 0, "Could not bind socket on {}: {}", address, NetworkError::GetLast().AsString());
320 closesocket(sock);
321 return INVALID_SOCKET;
322 }
323
324 if (runp->ai_socktype != SOCK_DGRAM && listen(sock, 1) != 0) {
325 Debug(net, 0, "Could not listen on socket: {}", NetworkError::GetLast().AsString());
326 closesocket(sock);
327 return INVALID_SOCKET;
328 }
329
330 /* Connection succeeded */
331
332 if (!SetNonBlocking(sock)) {
333 Debug(net, 0, "Setting non-blocking mode failed: {}", NetworkError::GetLast().AsString());
334 }
335
336 Debug(net, 3, "Listening on {}", address);
337 return sock;
338}
339
345void NetworkAddress::Listen(int socktype, SocketList *sockets)
346{
347 assert(sockets != nullptr);
348
349 /* Setting both hostname to "" and port to 0 is not allowed.
350 * As port 0 means bind to any port, the other must mean that
351 * we want to bind to 'all' IPs. */
352 if (this->address_length == 0 && this->address.ss_family == AF_UNSPEC &&
353 this->hostname.empty() && this->GetPort() == 0) {
354 this->Resolve(AF_INET, socktype, AI_ADDRCONFIG | AI_PASSIVE, sockets, ListenLoopProc);
355 this->Resolve(AF_INET6, socktype, AI_ADDRCONFIG | AI_PASSIVE, sockets, ListenLoopProc);
356 } else {
357 this->Resolve(AF_UNSPEC, socktype, AI_ADDRCONFIG | AI_PASSIVE, sockets, ListenLoopProc);
358 }
359}
360
367/* static */ std::string_view NetworkAddress::SocketTypeAsString(int socktype)
368{
369 switch (socktype) {
370 case SOCK_STREAM: return "tcp";
371 case SOCK_DGRAM: return "udp";
372 default: return "unsupported";
373 }
374}
375
382/* static */ std::string_view NetworkAddress::AddressFamilyAsString(int family)
383{
384 switch (family) {
385 case AF_UNSPEC: return "either IPv4 or IPv6";
386 case AF_INET: return "IPv4";
387 case AF_INET6: return "IPv6";
388 default: return "unsupported";
389 }
390}
391
398{
399 sockaddr_storage addr = {};
400 socklen_t addr_len = sizeof(addr);
401 if (getpeername(sock, (sockaddr *)&addr, &addr_len) != 0) {
402 Debug(net, 0, "Failed to get address of the peer: {}", NetworkError::GetLast().AsString());
403 return NetworkAddress();
404 }
405 return NetworkAddress(addr, addr_len);
406}
407
414{
415 sockaddr_storage addr = {};
416 socklen_t addr_len = sizeof(addr);
417 if (getsockname(sock, (sockaddr *)&addr, &addr_len) != 0) {
418 Debug(net, 0, "Failed to get address of the socket: {}", NetworkError::GetLast().AsString());
419 return NetworkAddress();
420 }
421 return NetworkAddress(addr, addr_len);
422}
423
429/* static */ const std::string NetworkAddress::GetPeerName(SOCKET sock)
430{
432}
433
444/* static */ ServerAddress ServerAddress::Parse(std::string_view connection_string, uint16_t default_port, CompanyID *company_id)
445{
446 if (connection_string.starts_with("+")) {
447 std::string_view invite_code = ParseCompanyFromConnectionString(connection_string, company_id);
448 return ServerAddress(SERVER_ADDRESS_INVITE_CODE, std::string(invite_code));
449 }
450
451 uint16_t port = default_port;
452 std::string_view ip = ParseFullConnectionString(connection_string, port, company_id);
453 return ServerAddress(SERVER_ADDRESS_DIRECT, fmt::format("{}:{}", ip, port));
454}
static SOCKET ListenLoopProc(addrinfo *runp)
Helper function to resolve a listening.
Definition address.cpp:292
static SOCKET ResolveLoopProc(addrinfo *)
Helper function to resolve without opening a socket.
Definition address.cpp:104
static std::string_view GetAddressFormatString(uint16_t family, bool with_family)
Helper to get the formatting string of an address for a given family.
Definition address.cpp:81
Wrapper for network addresses.
@ SERVER_ADDRESS_INVITE_CODE
Server-address is based on an invite code.
Definition address.h:176
@ SERVER_ADDRESS_DIRECT
Server-address is based on an hostname:port.
Definition address.h:175
std::map< SOCKET, NetworkAddress > SocketList
Type for a mapping between address and socket.
Definition address.h:21
Wrapper for (un)resolved network addresses; there's no reason to transform a numeric IP to a string a...
Definition address.h:28
static NetworkAddress GetPeerAddress(SOCKET sock)
Get the peer address of a socket as NetworkAddress.
Definition address.cpp:397
bool resolved
Whether the address has been (tried to be) resolved.
Definition address.h:33
int GetAddressLength()
Get the (valid) length of the address.
Definition address.h:95
sockaddr_storage address
The resolved address.
Definition address.h:32
static std::string_view AddressFamilyAsString(int family)
Convert the address family into a string.
Definition address.cpp:382
bool IsFamily(int family)
Checks of this address is of the given family.
Definition address.cpp:133
bool IsResolved() const
Check whether the IP address has been resolved already.
Definition address.h:109
static const std::string GetPeerName(SOCKET sock)
Get the peer name of a socket in string format.
Definition address.cpp:429
static std::string_view SocketTypeAsString(int socktype)
Convert the socket type into a string.
Definition address.cpp:367
static NetworkAddress GetSockAddress(SOCKET sock)
Get the local address of a socket as NetworkAddress.
Definition address.cpp:413
std::string hostname
The hostname.
Definition address.h:30
uint16_t GetPort() const
Get the port.
Definition address.cpp:39
void Listen(int socktype, SocketList *sockets)
Make the given socket listen.
Definition address.cpp:345
SOCKET Resolve(int family, int socktype, int flags, SocketList *sockets, LoopProc func)
Resolve this address into a socket.
Definition address.cpp:202
void SetPort(uint16_t port)
Set the port.
Definition address.cpp:58
const sockaddr_storage * GetAddress()
Get the address in its internal representation.
Definition address.cpp:114
const std::string & GetHostname()
Get the hostname; in case it wasn't given the IPv4 dotted representation is given.
Definition address.cpp:24
int address_length
The length of the resolved address.
Definition address.h:31
std::string GetAddressAsString(bool with_family=true)
Get the address as a string, e.g.
Definition address.cpp:95
bool IsInNetmask(std::string_view netmask)
Checks whether this IP address is contained by the given netmask.
Definition address.cpp:147
static NetworkError GetLast()
Get the last network error.
Address to a game server.
Definition address.h:184
std::string connection_string
The connection string for this ServerAddress.
Definition address.h:198
static ServerAddress Parse(std::string_view connection_string, uint16_t default_port, CompanyID *company_id=nullptr)
Convert a string containing either "hostname", "hostname:port" or invite code to a ServerAddress,...
Definition address.cpp:444
Parse data from a string / buffer.
@ SKIP_ONE_SEPARATOR
Read and discard one separator, do not include it in the result.
static const uint NETWORK_HOSTNAME_LENGTH
The maximum length of the host name, in bytes including '\0'.
Definition config.h:54
#define Debug(category, level, format_string,...)
Output a line of debugging information.
Definition debug.h:37
std::string_view ParseCompanyFromConnectionString(std::string_view connection_string, CompanyID *company_id)
Parse the company part ("#company" postfix) of a connecting string.
Definition network.cpp:476
std::string_view ParseFullConnectionString(std::string_view connection_string, uint16_t &port, CompanyID *company_id)
Converts a string to ip/port/company Format: IP:port::company.
Definition network.cpp:520
bool SetReusePort(SOCKET d)
Try to set the socket to reuse ports.
bool SetNoDelay(SOCKET d)
Try to set the socket to not delay sending.
bool SetNonBlocking(SOCKET d)
Try to set the socket into non-blocking mode.
std::string FS2OTTD(std::wstring_view name)
Convert to OpenTTD's encoding from a wide string.
Definition win32.cpp:337