10#include "../../stdafx.h"
13#include "../network_internal.h"
14#include "../../debug.h"
15#include "../../core/string_consumer.hpp"
17#include "../../safeguards.h"
26 if (this->
hostname.empty() && this->address.ss_family != AF_UNSPEC) {
29 getnameinfo((
struct sockaddr *)&this->
address, this->
address_length, buffer,
sizeof(buffer),
nullptr, 0, NI_NUMERICHOST);
41 switch (this->
address.ss_family) {
44 return ntohs(((
const struct sockaddr_in *)&this->
address)->sin_port);
47 return ntohs(((
const struct sockaddr_in6 *)&this->address)->sin6_port);
60 switch (this->
address.ss_family) {
63 ((
struct sockaddr_in*)&this->
address)->sin_port = htons(port);
67 ((
struct sockaddr_in6*)&this->
address)->sin6_port = htons(port);
84 case AF_INET:
return with_family ?
"{}:{} (IPv4)" :
"{}:{}";
85 case AF_INET6:
return with_family ?
"[{}]:{} (IPv6)" :
"[{}]:{}";
86 default:
return with_family ?
"{}:{} (IPv?)" :
"{}:{}";
107 return !INVALID_SOCKET;
138 return this->
address.ss_family == family;
152 int cidr = this->
address.ss_family == AF_INET ? 32 : 128;
159 if (consumer.AnyBytesLeft()) {
160 int tmp_cidr = consumer.ReadIntegerBase(10, cidr);
163 if (tmp_cidr > 0 && tmp_cidr < cidr) cidr = tmp_cidr;
168 switch (this->
address.ss_family) {
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;
175 ip = (uint32_t*)&((
struct sockaddr_in6*)&this->
address)->sin6_addr;
176 mask = (uint32_t*)&((
struct sockaddr_in6*)&mask_address.
address)->sin6_addr;
184 uint32_t msk = cidr >= 32 ? (uint32_t)-1 : htonl(-(1 << (32 - cidr)));
185 if ((*mask++ & msk) != (*ip++ & msk))
return false;
205 struct addrinfo hints{};
206 hints.ai_family = family;
207 hints.ai_flags = flags;
208 hints.ai_socktype = socktype;
211 std::string port_name = fmt::format(
"{}", this->
GetPort());
213 bool reset_hostname =
false;
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" :
"::";
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",
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;
237 if (reset_hostname) this->
hostname.clear();
241 Debug(net, 0,
"getaddrinfo for hostname \"{}\", port {}, address family {} and socket type {} failed: {}",
244 return INVALID_SOCKET;
247 SOCKET sock = INVALID_SOCKET;
248 for (
struct addrinfo *runp = ai; runp !=
nullptr; runp = runp->ai_next) {
252 if (sockets !=
nullptr) {
254 if (std::any_of(sockets->begin(), sockets->end(), [&
address](
const auto &p) { return p.second == address; }))
continue;
257 if (sock == INVALID_SOCKET)
continue;
259 if (sockets ==
nullptr) {
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));
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);
279 (*sockets)[sock] = std::move(addr);
280 sock = INVALID_SOCKET;
296 SOCKET sock = socket(runp->ai_family, runp->ai_socktype, runp->ai_protocol);
297 if (sock == INVALID_SOCKET) {
301 return INVALID_SOCKET;
304 if (runp->ai_socktype == SOCK_STREAM && !
SetNoDelay(sock)) {
313 if (runp->ai_family == AF_INET6 &&
314 setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
reinterpret_cast<const char *
>(&on),
sizeof(on)) == -1) {
318 if (bind(sock, runp->ai_addr, (
int)runp->ai_addrlen) != 0) {
321 return INVALID_SOCKET;
324 if (runp->ai_socktype != SOCK_DGRAM && listen(sock, 1) != 0) {
327 return INVALID_SOCKET;
336 Debug(net, 3,
"Listening on {}", address);
347 assert(sockets !=
nullptr);
353 this->hostname.empty() && this->GetPort() == 0) {
370 case SOCK_STREAM:
return "tcp";
371 case SOCK_DGRAM:
return "udp";
372 default:
return "unsupported";
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";
399 sockaddr_storage addr = {};
400 socklen_t addr_len =
sizeof(addr);
401 if (getpeername(sock, (sockaddr *)&addr, &addr_len) != 0) {
415 sockaddr_storage addr = {};
416 socklen_t addr_len =
sizeof(addr);
417 if (getsockname(sock, (sockaddr *)&addr, &addr_len) != 0) {
451 uint16_t port = default_port;
static SOCKET ListenLoopProc(addrinfo *runp)
Helper function to resolve a listening.
static SOCKET ResolveLoopProc(addrinfo *)
Helper function to resolve without opening a socket.
static std::string_view GetAddressFormatString(uint16_t family, bool with_family)
Helper to get the formatting string of an address for a given family.
Wrapper for network addresses.
@ SERVER_ADDRESS_INVITE_CODE
Server-address is based on an invite code.
@ SERVER_ADDRESS_DIRECT
Server-address is based on an hostname:port.
std::map< SOCKET, NetworkAddress > SocketList
Type for a mapping between address and socket.
Wrapper for (un)resolved network addresses; there's no reason to transform a numeric IP to a string a...
static NetworkAddress GetPeerAddress(SOCKET sock)
Get the peer address of a socket as NetworkAddress.
bool resolved
Whether the address has been (tried to be) resolved.
int GetAddressLength()
Get the (valid) length of the address.
sockaddr_storage address
The resolved address.
static std::string_view AddressFamilyAsString(int family)
Convert the address family into a string.
bool IsFamily(int family)
Checks of this address is of the given family.
bool IsResolved() const
Check whether the IP address has been resolved already.
static const std::string GetPeerName(SOCKET sock)
Get the peer name of a socket in string format.
static std::string_view SocketTypeAsString(int socktype)
Convert the socket type into a string.
static NetworkAddress GetSockAddress(SOCKET sock)
Get the local address of a socket as NetworkAddress.
std::string hostname
The hostname.
uint16_t GetPort() const
Get the port.
void Listen(int socktype, SocketList *sockets)
Make the given socket listen.
SOCKET Resolve(int family, int socktype, int flags, SocketList *sockets, LoopProc func)
Resolve this address into a socket.
void SetPort(uint16_t port)
Set the port.
const sockaddr_storage * GetAddress()
Get the address in its internal representation.
const std::string & GetHostname()
Get the hostname; in case it wasn't given the IPv4 dotted representation is given.
int address_length
The length of the resolved address.
std::string GetAddressAsString(bool with_family=true)
Get the address as a string, e.g.
bool IsInNetmask(std::string_view netmask)
Checks whether this IP address is contained by the given netmask.
static NetworkError GetLast()
Get the last network error.
Address to a game server.
std::string connection_string
The connection string for this ServerAddress.
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,...
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'.
#define Debug(category, level, format_string,...)
Output a line of debugging information.
std::string_view ParseCompanyFromConnectionString(std::string_view connection_string, CompanyID *company_id)
Parse the company part ("#company" postfix) of a connecting string.
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.
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.