OpenTTD Source 20241224-master-gf74b0cf984
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
16#include "../../safeguards.h"
17
23const std::string &NetworkAddress::GetHostname()
24{
25 if (this->hostname.empty() && this->address.ss_family != AF_UNSPEC) {
26 assert(this->address_length != 0);
27 char buffer[NETWORK_HOSTNAME_LENGTH];
28 getnameinfo((struct sockaddr *)&this->address, this->address_length, buffer, sizeof(buffer), nullptr, 0, NI_NUMERICHOST);
29 this->hostname = buffer;
30 }
31 return this->hostname;
32}
33
39{
40 switch (this->address.ss_family) {
41 case AF_UNSPEC:
42 case AF_INET:
43 return ntohs(((const struct sockaddr_in *)&this->address)->sin_port);
44
45 case AF_INET6:
46 return ntohs(((const struct sockaddr_in6 *)&this->address)->sin6_port);
47
48 default:
49 NOT_REACHED();
50 }
51}
52
57void NetworkAddress::SetPort(uint16_t port)
58{
59 switch (this->address.ss_family) {
60 case AF_UNSPEC:
61 case AF_INET:
62 ((struct sockaddr_in*)&this->address)->sin_port = htons(port);
63 break;
64
65 case AF_INET6:
66 ((struct sockaddr_in6*)&this->address)->sin6_port = htons(port);
67 break;
68
69 default:
70 NOT_REACHED();
71 }
72}
73
80static const char *GetAddressFormatString(uint16_t family, bool with_family)
81{
82 switch (family) {
83 case AF_INET: return with_family ? "{}:{} (IPv4)" : "{}:{}";
84 case AF_INET6: return with_family ? "[{}]:{} (IPv6)" : "[{}]:{}";
85 default: return with_family ? "{}:{} (IPv?)" : "{}:{}";
86 }
87}
88
94std::string NetworkAddress::GetAddressAsString(bool with_family)
95{
96 return fmt::format(fmt::runtime(GetAddressFormatString(this->GetAddress()->ss_family, with_family)), this->GetHostname(), this->GetPort());
97}
98
103static SOCKET ResolveLoopProc(addrinfo *)
104{
105 /* We just want the first 'entry', so return a valid socket. */
106 return !INVALID_SOCKET;
107}
108
113const sockaddr_storage *NetworkAddress::GetAddress()
114{
115 if (!this->IsResolved()) {
116 /* Here we try to resolve a network address. We use SOCK_STREAM as
117 * socket type because some stupid OSes, like Solaris, cannot be
118 * bothered to implement the specifications and allow '0' as value
119 * that means "don't care whether it is SOCK_STREAM or SOCK_DGRAM".
120 */
121 this->Resolve(this->address.ss_family, SOCK_STREAM, AI_ADDRCONFIG, nullptr, ResolveLoopProc);
122 this->resolved = true;
123 }
124 return &this->address;
125}
126
133{
134 if (!this->IsResolved()) {
135 this->Resolve(family, SOCK_STREAM, AI_ADDRCONFIG, nullptr, ResolveLoopProc);
136 }
137 return this->address.ss_family == family;
138}
139
146bool NetworkAddress::IsInNetmask(const std::string &netmask)
147{
148 /* Resolve it if we didn't do it already */
149 if (!this->IsResolved()) this->GetAddress();
150
151 int cidr = this->address.ss_family == AF_INET ? 32 : 128;
152
153 NetworkAddress mask_address;
154
155 /* Check for CIDR separator */
156 auto cidr_separator_location = netmask.find('/');
157 if (cidr_separator_location != std::string::npos) {
158 int tmp_cidr = atoi(netmask.substr(cidr_separator_location + 1).c_str());
159
160 /* Invalid CIDR, treat as single host */
161 if (tmp_cidr > 0 && tmp_cidr < cidr) cidr = tmp_cidr;
162
163 /* Remove the / so that NetworkAddress works on the IP portion */
164 mask_address = NetworkAddress(netmask.substr(0, cidr_separator_location), 0, this->address.ss_family);
165 } else {
166 mask_address = NetworkAddress(netmask, 0, this->address.ss_family);
167 }
168
169 if (mask_address.GetAddressLength() == 0) return false;
170
171 uint32_t *ip;
172 uint32_t *mask;
173 switch (this->address.ss_family) {
174 case AF_INET:
175 ip = (uint32_t*)&((struct sockaddr_in*)&this->address)->sin_addr.s_addr;
176 mask = (uint32_t*)&((struct sockaddr_in*)&mask_address.address)->sin_addr.s_addr;
177 break;
178
179 case AF_INET6:
180 ip = (uint32_t*)&((struct sockaddr_in6*)&this->address)->sin6_addr;
181 mask = (uint32_t*)&((struct sockaddr_in6*)&mask_address.address)->sin6_addr;
182 break;
183
184 default:
185 NOT_REACHED();
186 }
187
188 while (cidr > 0) {
189 uint32_t msk = cidr >= 32 ? (uint32_t)-1 : htonl(-(1 << (32 - cidr)));
190 if ((*mask++ & msk) != (*ip++ & msk)) return false;
191
192 cidr -= 32;
193 }
194
195 return true;
196}
197
207SOCKET NetworkAddress::Resolve(int family, int socktype, int flags, SocketList *sockets, LoopProc func)
208{
209 struct addrinfo *ai;
210 struct addrinfo hints;
211 memset(&hints, 0, sizeof (hints));
212 hints.ai_family = family;
213 hints.ai_flags = flags;
214 hints.ai_socktype = socktype;
215
216 /* The port needs to be a string. Six is enough to contain all characters + '\0'. */
217 std::string port_name = std::to_string(this->GetPort());
218
219 bool reset_hostname = false;
220 /* Setting both hostname to nullptr and port to 0 is not allowed.
221 * As port 0 means bind to any port, the other must mean that
222 * we want to bind to 'all' IPs. */
223 if (this->hostname.empty() && this->address_length == 0 && this->GetPort() == 0) {
224 reset_hostname = true;
225 int fam = this->address.ss_family;
226 if (fam == AF_UNSPEC) fam = family;
227 this->hostname = fam == AF_INET ? "0.0.0.0" : "::";
228 }
229
230 static bool _resolve_timeout_error_message_shown = false;
231 auto start = std::chrono::steady_clock::now();
232 int e = getaddrinfo(this->hostname.empty() ? nullptr : this->hostname.c_str(), port_name.c_str(), &hints, &ai);
233 auto end = std::chrono::steady_clock::now();
234 std::chrono::seconds duration = std::chrono::duration_cast<std::chrono::seconds>(end - start);
235 if (!_resolve_timeout_error_message_shown && duration >= std::chrono::seconds(5)) {
236 Debug(net, 0, "getaddrinfo for hostname \"{}\", port {}, address family {} and socket type {} took {} seconds",
237 this->hostname, port_name, AddressFamilyAsString(family), SocketTypeAsString(socktype), duration.count());
238 Debug(net, 0, " this is likely an issue in the DNS name resolver's configuration causing it to time out");
239 _resolve_timeout_error_message_shown = true;
240 }
241
242
243 if (reset_hostname) this->hostname.clear();
244
245 if (e != 0) {
246 if (func != ResolveLoopProc) {
247 Debug(net, 0, "getaddrinfo for hostname \"{}\", port {}, address family {} and socket type {} failed: {}",
248 this->hostname, port_name, AddressFamilyAsString(family), SocketTypeAsString(socktype), FS2OTTD(gai_strerror(e)));
249 }
250 return INVALID_SOCKET;
251 }
252
253 SOCKET sock = INVALID_SOCKET;
254 for (struct addrinfo *runp = ai; runp != nullptr; runp = runp->ai_next) {
255 /* When we are binding to multiple sockets, make sure we do not
256 * connect to one with exactly the same address twice. That's
257 * of course totally unneeded ;) */
258 if (sockets != nullptr) {
259 NetworkAddress address(runp->ai_addr, (int)runp->ai_addrlen);
260 if (std::any_of(sockets->begin(), sockets->end(), [&address](const auto &p) { return p.second == address; })) continue;
261 }
262 sock = func(runp);
263 if (sock == INVALID_SOCKET) continue;
264
265 if (sockets == nullptr) {
266 this->address_length = (int)runp->ai_addrlen;
267 assert(sizeof(this->address) >= runp->ai_addrlen);
268 memcpy(&this->address, runp->ai_addr, runp->ai_addrlen);
269#ifdef __EMSCRIPTEN__
270 /* Emscripten doesn't zero sin_zero, but as we compare addresses
271 * to see if they are the same address, we need them to be zero'd.
272 * Emscripten is, as far as we know, the only OS not doing this.
273 *
274 * https://github.com/emscripten-core/emscripten/issues/12998
275 */
276 if (this->address.ss_family == AF_INET) {
277 sockaddr_in *address_ipv4 = (sockaddr_in *)&this->address;
278 memset(address_ipv4->sin_zero, 0, sizeof(address_ipv4->sin_zero));
279 }
280#endif
281 break;
282 }
283
284 NetworkAddress addr(runp->ai_addr, (int)runp->ai_addrlen);
285 (*sockets)[sock] = addr;
286 sock = INVALID_SOCKET;
287 }
288 freeaddrinfo (ai);
289
290 return sock;
291}
292
298static SOCKET ListenLoopProc(addrinfo *runp)
299{
300 std::string address = NetworkAddress(runp->ai_addr, (int)runp->ai_addrlen).GetAddressAsString();
301
302 SOCKET sock = socket(runp->ai_family, runp->ai_socktype, runp->ai_protocol);
303 if (sock == INVALID_SOCKET) {
304 const char *type = NetworkAddress::SocketTypeAsString(runp->ai_socktype);
305 const char *family = NetworkAddress::AddressFamilyAsString(runp->ai_family);
306 Debug(net, 0, "Could not create {} {} socket: {}", type, family, NetworkError::GetLast().AsString());
307 return INVALID_SOCKET;
308 }
309
310 if (runp->ai_socktype == SOCK_STREAM && !SetNoDelay(sock)) {
311 Debug(net, 1, "Setting no-delay mode failed: {}", NetworkError::GetLast().AsString());
312 }
313
314 if (!SetReusePort(sock)) {
315 Debug(net, 0, "Setting reuse-address mode failed: {}", NetworkError::GetLast().AsString());
316 }
317
318 int on = 1;
319 if (runp->ai_family == AF_INET6 &&
320 setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&on, sizeof(on)) == -1) {
321 Debug(net, 3, "Could not disable IPv4 over IPv6: {}", NetworkError::GetLast().AsString());
322 }
323
324 if (bind(sock, runp->ai_addr, (int)runp->ai_addrlen) != 0) {
325 Debug(net, 0, "Could not bind socket on {}: {}", address, NetworkError::GetLast().AsString());
326 closesocket(sock);
327 return INVALID_SOCKET;
328 }
329
330 if (runp->ai_socktype != SOCK_DGRAM && listen(sock, 1) != 0) {
331 Debug(net, 0, "Could not listen on socket: {}", NetworkError::GetLast().AsString());
332 closesocket(sock);
333 return INVALID_SOCKET;
334 }
335
336 /* Connection succeeded */
337
338 if (!SetNonBlocking(sock)) {
339 Debug(net, 0, "Setting non-blocking mode failed: {}", NetworkError::GetLast().AsString());
340 }
341
342 Debug(net, 3, "Listening on {}", address);
343 return sock;
344}
345
351void NetworkAddress::Listen(int socktype, SocketList *sockets)
352{
353 assert(sockets != nullptr);
354
355 /* Setting both hostname to "" and port to 0 is not allowed.
356 * As port 0 means bind to any port, the other must mean that
357 * we want to bind to 'all' IPs. */
358 if (this->address_length == 0 && this->address.ss_family == AF_UNSPEC &&
359 this->hostname.empty() && this->GetPort() == 0) {
360 this->Resolve(AF_INET, socktype, AI_ADDRCONFIG | AI_PASSIVE, sockets, ListenLoopProc);
361 this->Resolve(AF_INET6, socktype, AI_ADDRCONFIG | AI_PASSIVE, sockets, ListenLoopProc);
362 } else {
363 this->Resolve(AF_UNSPEC, socktype, AI_ADDRCONFIG | AI_PASSIVE, sockets, ListenLoopProc);
364 }
365}
366
373/* static */ const char *NetworkAddress::SocketTypeAsString(int socktype)
374{
375 switch (socktype) {
376 case SOCK_STREAM: return "tcp";
377 case SOCK_DGRAM: return "udp";
378 default: return "unsupported";
379 }
380}
381
388/* static */ const char *NetworkAddress::AddressFamilyAsString(int family)
389{
390 switch (family) {
391 case AF_UNSPEC: return "either IPv4 or IPv6";
392 case AF_INET: return "IPv4";
393 case AF_INET6: return "IPv6";
394 default: return "unsupported";
395 }
396}
397
404{
405 sockaddr_storage addr = {};
406 socklen_t addr_len = sizeof(addr);
407 if (getpeername(sock, (sockaddr *)&addr, &addr_len) != 0) {
408 Debug(net, 0, "Failed to get address of the peer: {}", NetworkError::GetLast().AsString());
409 return NetworkAddress();
410 }
411 return NetworkAddress(addr, addr_len);
412}
413
420{
421 sockaddr_storage addr = {};
422 socklen_t addr_len = sizeof(addr);
423 if (getsockname(sock, (sockaddr *)&addr, &addr_len) != 0) {
424 Debug(net, 0, "Failed to get address of the socket: {}", NetworkError::GetLast().AsString());
425 return NetworkAddress();
426 }
427 return NetworkAddress(addr, addr_len);
428}
429
435/* static */ const std::string NetworkAddress::GetPeerName(SOCKET sock)
436{
438}
439
450/* static */ ServerAddress ServerAddress::Parse(const std::string &connection_string, uint16_t default_port, CompanyID *company_id)
451{
452 if (connection_string.starts_with("+")) {
453 std::string_view invite_code = ParseCompanyFromConnectionString(connection_string, company_id);
454 return ServerAddress(SERVER_ADDRESS_INVITE_CODE, std::string(invite_code));
455 }
456
457 uint16_t port = default_port;
458 std::string_view ip = ParseFullConnectionString(connection_string, port, company_id);
459 return ServerAddress(SERVER_ADDRESS_DIRECT, std::string(ip) + ":" + std::to_string(port));
460}
static const char * GetAddressFormatString(uint16_t family, bool with_family)
Helper to get the formatting string of an address for a given family.
Definition address.cpp:80
static SOCKET ListenLoopProc(addrinfo *runp)
Helper function to resolve a listening.
Definition address.cpp:298
static SOCKET ResolveLoopProc(addrinfo *)
Helper function to resolve without opening a socket.
Definition address.cpp:103
Wrapper for network addresses.
@ SERVER_ADDRESS_INVITE_CODE
Server-address is based on an invite code.
Definition address.h:188
@ SERVER_ADDRESS_DIRECT
Server-address is based on an hostname:port.
Definition address.h:187
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:403
static const char * AddressFamilyAsString(int family)
Convert the address family into a string.
Definition address.cpp:388
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:98
static const char * SocketTypeAsString(int socktype)
Convert the socket type into a string.
Definition address.cpp:373
sockaddr_storage address
The resolved address.
Definition address.h:32
bool IsFamily(int family)
Checks of this address is of the given family.
Definition address.cpp:132
bool IsResolved() const
Check whether the IP address has been resolved already.
Definition address.h:112
static const std::string GetPeerName(SOCKET sock)
Get the peer name of a socket in string format.
Definition address.cpp:435
static NetworkAddress GetSockAddress(SOCKET sock)
Get the local address of a socket as NetworkAddress.
Definition address.cpp:419
bool IsInNetmask(const std::string &netmask)
Checks whether this IP address is contained by the given netmask.
Definition address.cpp:146
std::string hostname
The hostname.
Definition address.h:30
uint16_t GetPort() const
Get the port.
Definition address.cpp:38
void Listen(int socktype, SocketList *sockets)
Make the given socket listen.
Definition address.cpp:351
SOCKET Resolve(int family, int socktype, int flags, SocketList *sockets, LoopProc func)
Resolve this address into a socket.
Definition address.cpp:207
void SetPort(uint16_t port)
Set the port.
Definition address.cpp:57
const sockaddr_storage * GetAddress()
Get the address in its internal representation.
Definition address.cpp:113
const std::string & GetHostname()
Get the hostname; in case it wasn't given the IPv4 dotted representation is given.
Definition address.cpp:23
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:94
static NetworkError GetLast()
Get the last network error.
Address to a game server.
Definition address.h:196
std::string connection_string
The connection string for this ServerAddress.
Definition address.h:210
static ServerAddress Parse(const std::string &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:450
Owner
Enum for all companies/owners.
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,...)
Ouptut a line of debugging information.
Definition debug.h:37
std::string_view ParseCompanyFromConnectionString(const std::string &connection_string, CompanyID *company_id)
Parse the company part ("#company" postfix) of a connecting string.
Definition network.cpp:477
std::string_view ParseFullConnectionString(const std::string &connection_string, uint16_t &port, CompanyID *company_id)
Converts a string to ip/port/company Format: IP:port::company.
Definition network.cpp:521
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(const std::wstring &name)
Convert to OpenTTD's encoding from a wide string.
Definition win32.cpp:337