diff --git a/lib/enet/unix.c b/lib/enet/unix.c index 39abfa276..58d89a823 100644 --- a/lib/enet/unix.c +++ b/lib/enet/unix.c @@ -60,6 +60,9 @@ static enet_uint32 timeBase = 0; #ifdef IOS_STK extern void iOSInitialize(void); +extern int isIPV6Only(void); +extern void getSynthesizedAddress(const ENetAddress* ea, struct sockaddr_in6* in6); +extern void getIPV4FromSynthesized(const struct sockaddr_in6* in6, ENetAddress* ea); #endif int @@ -196,26 +199,42 @@ enet_address_get_host (const ENetAddress * address, char * name, size_t nameLeng int enet_socket_bind (ENetSocket socket, const ENetAddress * address) { - struct sockaddr_in sin; - - memset (& sin, 0, sizeof (struct sockaddr_in)); - - sin.sin_family = AF_INET; - - if (address != NULL) +#ifdef IOS_STK + // In STK we only bind port and listen to any address + if (isIPV6Only()) { - sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); - sin.sin_addr.s_addr = address -> host; + struct sockaddr_in6 sin; + memset (&sin, 0, sizeof (struct sockaddr_in6)); + sin.sin6_family = AF_INET6; + sin.sin6_addr = in6addr_any; + sin.sin6_port = ENET_HOST_TO_NET_16 (address -> port); + return bind (socket, (struct sockaddr *) & sin, + sizeof (struct sockaddr_in6)); } else +#endif { - sin.sin_port = 0; - sin.sin_addr.s_addr = INADDR_ANY; - } + struct sockaddr_in sin; - return bind (socket, - (struct sockaddr *) & sin, - sizeof (struct sockaddr_in)); + memset (& sin, 0, sizeof (struct sockaddr_in)); + + sin.sin_family = AF_INET; + + if (address != NULL) + { + sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); + sin.sin_addr.s_addr = address -> host; + } + else + { + sin.sin_port = 0; + sin.sin_addr.s_addr = INADDR_ANY; + } + + return bind (socket, + (struct sockaddr *) & sin, + sizeof (struct sockaddr_in)); + } } int @@ -242,7 +261,12 @@ enet_socket_listen (ENetSocket socket, int backlog) ENetSocket enet_socket_create (ENetSocketType type) { - return socket (PF_INET, type == ENET_SOCKET_TYPE_DATAGRAM ? SOCK_DGRAM : SOCK_STREAM, 0); +#ifdef IOS_STK + int af_family = isIPV6Only() == 1 ? PF_INET6 : PF_INET; +#else + int af_family = PF_INET; +#endif + return socket (af_family, type == ENET_SOCKET_TYPE_DATAGRAM ? SOCK_DGRAM : SOCK_STREAM, 0); } int @@ -384,20 +408,34 @@ enet_socket_send (ENetSocket socket, { struct msghdr msgHdr; struct sockaddr_in sin; +#ifdef IOS_STK + struct sockaddr_in6 sin6; +#endif int sentLength; memset (& msgHdr, 0, sizeof (struct msghdr)); if (address != NULL) { - memset (& sin, 0, sizeof (struct sockaddr_in)); +#ifdef IOS_STK + if (isIPV6Only()) + { + getSynthesizedAddress(address, &sin6); + msgHdr.msg_name = & sin6; + msgHdr.msg_namelen = sizeof (struct sockaddr_in6); + } + else +#endif + { + memset (& sin, 0, sizeof (struct sockaddr_in)); - sin.sin_family = AF_INET; - sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); - sin.sin_addr.s_addr = address -> host; + sin.sin_family = AF_INET; + sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); + sin.sin_addr.s_addr = address -> host; - msgHdr.msg_name = & sin; - msgHdr.msg_namelen = sizeof (struct sockaddr_in); + msgHdr.msg_name = & sin; + msgHdr.msg_namelen = sizeof (struct sockaddr_in); + } } msgHdr.msg_iov = (struct iovec *) buffers; @@ -424,14 +462,27 @@ enet_socket_receive (ENetSocket socket, { struct msghdr msgHdr; struct sockaddr_in sin; +#ifdef IOS_STK + struct sockaddr_in6 sin6; +#endif int recvLength; memset (& msgHdr, 0, sizeof (struct msghdr)); if (address != NULL) { - msgHdr.msg_name = & sin; - msgHdr.msg_namelen = sizeof (struct sockaddr_in); +#ifdef IOS_STK + if (isIPV6Only()) + { + msgHdr.msg_name = & sin6; + msgHdr.msg_namelen = sizeof (struct sockaddr_in6); + } + else +#endif + { + msgHdr.msg_name = & sin; + msgHdr.msg_namelen = sizeof (struct sockaddr_in); + } } msgHdr.msg_iov = (struct iovec *) buffers; @@ -454,8 +505,17 @@ enet_socket_receive (ENetSocket socket, if (address != NULL) { - address -> host = (enet_uint32) sin.sin_addr.s_addr; - address -> port = ENET_NET_TO_HOST_16 (sin.sin_port); +#ifdef IOS_STK + if (isIPV6Only()) + { + getIPV4FromSynthesized(&sin6, address); + } + else +#endif + { + address -> host = (enet_uint32) sin.sin_addr.s_addr; + address -> port = ENET_NET_TO_HOST_16 (sin.sin_port); + } } return recvLength; diff --git a/src/network/ios_ipv6.cpp b/src/network/ios_ipv6.cpp index dbaf389e7..ada0a20fc 100644 --- a/src/network/ios_ipv6.cpp +++ b/src/network/ios_ipv6.cpp @@ -20,13 +20,10 @@ #ifdef IOS_STK #include "network/ios_ipv6.hpp" -#include "config/stk_config.hpp" -#include "config/user_config.hpp" +#include "network/transport_address.hpp" #include "utils/string_utils.hpp" #include "utils/log.hpp" -#include - #include #include #include @@ -48,8 +45,9 @@ #define NAME_SVR_LEN (10) -bool g_ipv6_only; -ENetAddress g_connected_address; +int g_ipv6_only; +struct sockaddr_in6 g_connected_ipv6; +ENetAddress g_connected_ipv4; namespace Mars { @@ -273,7 +271,7 @@ namespace Mars } } -bool isIPV6Only() +int isIPV6Only() { return g_ipv6_only; } @@ -281,13 +279,86 @@ bool isIPV6Only() void iOSInitialize() { // Clear previous setting, in case user changed wifi or mobile data - g_ipv6_only = false; - g_connected_address.host = 0; - g_connected_address.port = 0; + g_ipv6_only = 0; + g_connected_ipv4.host = 0; + g_connected_ipv4.port = 0; + memset(&g_connected_ipv6, 0, sizeof(struct sockaddr_in6)); using namespace Mars; int ipstack = local_ipstack_detect(); - g_ipv6_only = ipstack == ELocalIPStack_IPv6; - + if (ipstack == ELocalIPStack_IPv6) + g_ipv6_only = 1; } // iOSInitialize + +void getSynthesizedAddress(const ENetAddress* ea, struct sockaddr_in6* in6) +{ + if (ea->host != g_connected_ipv4.host && + ea->port != g_connected_ipv4.port) + { + g_connected_ipv4.host = ea->host; + g_connected_ipv4.port = ea->port; + memset(&g_connected_ipv6, 0, sizeof(struct sockaddr_in6)); + g_connected_ipv6.sin6_family = AF_INET6; + TransportAddress addr(*ea); + // The ability to synthesize IPv6 addresses was added to getaddrinfo in iOS 9.2 + struct addrinfo hints, *res; + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + // Resolve the stun server name so we can send it a STUN request + const std::string& ipv4 = addr.toString(false/*show_port*/); + int status = getaddrinfo(ipv4.c_str(), StringUtils::toString(ea->port).c_str(), + &hints, &res); + if (status != 0) + { + Log::error("STKHost", "Error in getaddrinfo for synthesizing ipv6" + " %s: %s", ipv4.c_str(), gai_strerror(status)); + return; + } + assert(res != NULL); + for (const struct addrinfo* addr = res; addr != NULL; addr = addr->ai_next) + { + if (addr->ai_family == AF_INET6) + { + struct sockaddr_in6* ipv6 = (struct sockaddr_in6*)addr->ai_addr; + g_connected_ipv6.sin6_addr = ipv6->sin6_addr; + // Workaround in iOS 9 where port is not written (fixed in iOS 10) + if (ipv6->sin6_port == 0) + ipv6->sin6_port = htons(ea->port); + g_connected_ipv6.sin6_port = ipv6->sin6_port; + break; + } + } + freeaddrinfo(res); + } + memcpy(in6, &g_connected_ipv6, sizeof(struct sockaddr_in6)); +} + +bool sameIPV6(const struct in6_addr* a, const struct in6_addr* b) +{ + for (unsigned i = 0; i < sizeof(struct in6_addr); i++) + { + if (a->s6_addr[i] != b->s6_addr[i]) + return false; + } + return true; +} + +void getIPV4FromSynthesized(const struct sockaddr_in6* in6, ENetAddress* ea) +{ + if (sameIPV6(&(in6->sin6_addr), &(g_connected_ipv6.sin6_addr)) && + in6->sin6_port == g_connected_ipv6.sin6_port) + { + ea->host = g_connected_ipv4.host; + ea->port = g_connected_ipv4.port; + } + else + { + ea->host = 0; + ea->port = 0; + } +} + #endif diff --git a/src/network/ios_ipv6.hpp b/src/network/ios_ipv6.hpp index 517ed4581..0b27188a7 100644 --- a/src/network/ios_ipv6.hpp +++ b/src/network/ios_ipv6.hpp @@ -15,11 +15,15 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#include + #ifdef __cplusplus extern "C" { #endif -bool isIPV6Only(); +int isIPV6Only(); void iOSInitialize(); +void getSynthesizedAddress(const ENetAddress* ea, struct sockaddr_in6* in6); +void getIPV4FromSynthesized(const struct sockaddr_in6* in6, ENetAddress* ea); #ifdef __cplusplus } #endif diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp index fc6092c41..618797bde 100644 --- a/src/network/stk_host.cpp +++ b/src/network/stk_host.cpp @@ -23,6 +23,7 @@ #include "io/file_manager.hpp" #include "network/event.hpp" #include "network/game_setup.hpp" +#include "network/ios_ipv6.hpp" #include "network/network_config.hpp" #include "network/network_console.hpp" #include "network/network_player_profile.hpp" @@ -369,6 +370,16 @@ void STKHost::shutdown() */ void STKHost::setPublicAddress() { +#ifdef IOS_STK + if (isIPV6Only()) + { + // IPV6 only in iOS doesn't support connection to firewalled server, + // so no need to test STUN + Log::info("STKHost", "IPV6 only environment detected."); + m_public_address = TransportAddress("169.254.0.0:65535"); + return; + } +#endif std::vector > untried_server; for (auto& p : UserConfigParams::m_stun_servers) untried_server.push_back(p);