diff --git a/SConstruct b/SConstruct index 78f99b8f6..bc6aaaa92 100644 --- a/SConstruct +++ b/SConstruct @@ -2167,6 +2167,22 @@ where the cast is useless. freeaddrinfo(res); return 0; ''', msg='for getaddrinfo', successflags=_successflags) + @_guarded_test_windows + def check_inet_ntop_present(self,context,_successflags={'CPPDEFINES' : ['DXX_HAVE_INET_NTOP']}): + # Linux and OS X have working inet_ntop on all supported + # platforms. Only Windows sometimes lacks support for this + # function. + if self.Compile(context, text=''' +#include +#include +''', main=''' + struct sockaddr_in sai; + char dbuf[64]; + return inet_ntop(AF_INET, &sai.sin_addr, dbuf, sizeof(dbuf)) ? 0 : 1; +''', msg='for inet_ntop', successflags=_successflags): + return + if self.user_settings.ipv6: + raise SCons.Errors.StopError("IPv6 enabled and inet_ntop not available: disable IPv6 or upgrade headers to support inet_ntop.") @_custom_test def check_timespec_present(self,context,_successflags={'CPPDEFINES' : ['DXX_HAVE_STRUCT_TIMESPEC']}): self.Compile(context, text=''' diff --git a/similar/main/net_udp.cpp b/similar/main/net_udp.cpp index a0c44d729..4c86ca3a7 100644 --- a/similar/main/net_udp.cpp +++ b/similar/main/net_udp.cpp @@ -273,13 +273,68 @@ public: namespace dcx { -static const void *get_addr_from_sockaddr(const _sockaddr &s) +static const char *dxx_ntop(const _sockaddr &sa, array &dbuf) { +#ifdef WIN32 +#ifdef DXX_HAVE_INET_NTOP + /* + * Windows and inet_ntop: copy the in_addr/in6_addr to local + * variables because the Microsoft prototype lacks a const + * qualifier. + */ + union { + in_addr ia; #if DXX_USE_IPv6 - if (s.sa.sa_family == AF_INET6) - return &s.sin6.sin6_addr; + in6_addr ia6; +#endif + }; + const auto addr = +#if DXX_USE_IPv6 + (sa.sa.sa_family == AF_INET6) + ? &(ia6 = sa.sin6.sin6_addr) + : +#endif + static_cast(&(ia = sa.sin.sin_addr)); +#else + /* + * Windows and not inet_ntop: only inet_ntoa available. + * + * SConf check_inet_ntop_present enforces that Windows without + * inet_ntop cannot enable IPv6, so the IPv4 branch must be correct + * here. + * + * The reverse is not true. Windows with inet_ntop might not enable + * IPv6. + */ +#if DXX_USE_IPv6 +#error "IPv6 requires inet_ntop; SConf should prevent this path" +#endif + dbuf.back() = 0; + /* + * Copy the formatted string to the local buffer `dbuf` to guard + * against concurrent uses of `dxx_ntop`. + */ + return reinterpret_cast(memcpy(dbuf.data(), inet_ntoa(sa.sin.sin_addr), dbuf.size() - 1)); +#endif +#else + /* + * Not Windows; assume inet_ntop present. Non-Windows platforms + * declare inet_ntop with a const qualifier, so take a pointer to + * the underlying data. + */ + const auto addr = +#if DXX_USE_IPv6 + (sa.sa.sa_family == AF_INET6) + ? &sa.sin6.sin6_addr + : +#endif + static_cast(&sa.sin.sin_addr); +#endif +#if !defined(WIN32) || defined(DXX_HAVE_INET_NTOP) + if (const auto r = inet_ntop(sa.sa.sa_family, addr, dbuf.data(), dbuf.size())) + return r; + return "address"; #endif - return &s.sin.sin_addr; } } @@ -803,7 +858,7 @@ static int net_udp_game_connect(direct_join *const dj) if (timer_query() >= dj->start_time + (F1_0*10)) { dj->connecting = 0; - char dbuf[_sockaddr::presentation_buffer_size]; + array dbuf; const auto port = #if DXX_USE_IPv6 dj->host_addr.sa.sa_family == AF_INET6 @@ -818,7 +873,7 @@ Possible reasons:\n\ * Host port %hu is not open\n\ * Game is hosted on a different port\n\ * Host uses a game version\n\ - I do not understand", inet_ntop(dj->host_addr.sa.sa_family, get_addr_from_sockaddr(dj->host_addr), dbuf, sizeof(dbuf)) ? dbuf : "address", ntohs(port)); + I do not understand", dxx_ntop(dj->host_addr, dbuf), ntohs(port)); return 0; }