diff --git a/src/network/C4NetIO.cpp b/src/network/C4NetIO.cpp index b9d9f4308..3437eb4aa 100644 --- a/src/network/C4NetIO.cpp +++ b/src/network/C4NetIO.cpp @@ -31,6 +31,8 @@ #include #include +#include +#include typedef int socklen_t; int pipe(int *phandles) { return _pipe(phandles, 10, O_BINARY); } @@ -43,6 +45,8 @@ int pipe(int *phandles) { return _pipe(phandles, 10, O_BINARY); } #include #include #include +#include +#include #define ioctlsocket ioctl #define closesocket close @@ -50,7 +54,6 @@ int pipe(int *phandles) { return _pipe(phandles, 10, O_BINARY); } #endif - #ifdef _MSC_VER #pragma warning (disable : 4355) #endif @@ -591,6 +594,66 @@ void C4NetIO::EndpointAddress::CompileFunc(StdCompiler *comp) } } +std::vector C4NetIO::GetLocalAddresses() +{ + std::vector result; + +#ifdef HAVE_WINSOCK + HostAddress addr; + const size_t BUFFER_SIZE = 16000; + PIP_ADAPTER_ADDRESSES addresses = nullptr; + for (int i = 0; i < 3; ++i) + { + addresses = (PIP_ADAPTER_ADDRESSES) realloc(addresses, BUFFER_SIZE * (i+1)); + if (!addresses) + // allocation failed + return result; + ULONG bufsz = BUFFER_SIZE * (i+1); + DWORD rv = GetAdaptersAddresses(AF_UNSPEC, + GAA_FLAG_SKIP_ANYCAST|GAA_FLAG_SKIP_MULTICAST|GAA_FLAG_SKIP_DNS_SERVER|GAA_FLAG_SKIP_FRIENDLY_NAME, + nullptr, addresses, &bufsz); + if (rv == ERROR_BUFFER_OVERFLOW) + // too little space, try again + continue; + if (rv != NO_ERROR) + { + // Something else happened + free(addresses); + return result; + } + // All okay, add addresses + for (PIP_ADAPTER_ADDRESSES address = addresses; address; address = address->Next) + { + for (PIP_ADAPTER_UNICAST_ADDRESS unicast = address->FirstUnicastAddress; unicast; unicast = unicast->Next) + { + addr.SetHost(unicast->Address.lpSockaddr); + if (addr.IsLoopback()) + continue; + result.push_back(addr); + } + } + } + free(addresses); +#else + struct ifaddrs* addrs; + if (getifaddrs(&addrs) < 0) + return result; + for (struct ifaddrs* ifaddr = addrs; ifaddr != nullptr; ifaddr = ifaddr->ifa_next) + { + struct sockaddr* ad = ifaddr->ifa_addr; + if (ad == nullptr) continue; + + if ((ad->sa_family == AF_INET || ad->sa_family == AF_INET6) && (~ifaddr->ifa_flags & IFF_LOOPBACK)) // Choose only non-loopback IPv4/6 devices + { + result.emplace_back(ad); + } + } + freeifaddrs(addrs); +#endif + + return result; +} + // *** C4NetIO // construction / destruction @@ -1802,9 +1865,9 @@ bool C4NetIOSimpleUDP::InitBroadcast(addr_t *pBroadcastAddr) if (fMultiCast) CloseBroadcast(); // broadcast addr valid? - if (!pBroadcastAddr->IsMulticast()) + if (!pBroadcastAddr->IsMulticast() || pBroadcastAddr->GetFamily() != HostAddress::IPv6) { - SetError("invalid broadcast address"); + SetError("invalid broadcast address (only IPv6 multicast addresses are supported)"); return false; } if (pBroadcastAddr->GetPort() != iPort) @@ -2260,13 +2323,40 @@ bool C4NetIOUDP::InitBroadcast(addr_t *pBroadcastAddr) SetError("broadcast address is not valid"); return false; } - // set up adress - MCAddr.SetAddress(addr_t::AnyIPv4, iPort); - // search for a free one + // Set up address as unicast-prefix-based IPv6 multicast address (RFC 3306). + sockaddr_in6 saddrgen = sockaddr_in6(); + saddrgen.sin6_family = AF_INET6; + uint8_t *addrgen = saddrgen.sin6_addr.s6_addr; + // ff3e ("global multicast based on network prefix") : 64 (length of network prefix) + static const uint8_t mcast_prefix[4] = { 0xff, 0x3e, 0, 64}; + memcpy(addrgen, mcast_prefix, sizeof(mcast_prefix)); + addrgen += sizeof(mcast_prefix); + // 64 bit network prefix + addr_t prefixAddr; + for (auto& addr : GetLocalAddresses()) + if (addr.GetFamily() == HostAddress::IPv6 && !addr.IsLocal()) + { + prefixAddr.SetAddress(addr); + break; + } + if (prefixAddr.IsNull()) + { + SetError("no IPv6 unicast address available"); + return false; + } + static const size_t network_prefix_size = 8; + memcpy(addrgen, &static_cast(&prefixAddr)->sin6_addr, network_prefix_size); + addrgen += network_prefix_size; + // 32 bit group id: search for a free one for (int iRetries = 1000; iRetries; iRetries--) { + uint32_t rnd = UnsyncedRandom(); + memcpy(addrgen, &rnd, sizeof(rnd)); + // "high-order bit of the Group ID will be the same value as the T flag" + addrgen[0] |= 0x80; // create new - random - address - MCAddr.SetAddress(C4NetIO::HostAddress(0x000000ef | (UnsyncedRandom(0x1000000) << 8))); + MCAddr.SetAddress((sockaddr*) &saddrgen); + MCAddr.SetPort(iPort); // init broadcast if (!C4NetIOSimpleUDP::InitBroadcast(&MCAddr)) return false; diff --git a/src/network/C4NetIO.h b/src/network/C4NetIO.h index 4765b9f43..aa8063832 100644 --- a/src/network/C4NetIO.h +++ b/src/network/C4NetIO.h @@ -92,6 +92,7 @@ public: HostAddress(SpecialAddress addr) { SetHost(addr); } explicit HostAddress(uint32_t addr) { SetHost(addr); } HostAddress(const StdStrBuf &addr) { SetHost(addr); } + HostAddress(const sockaddr *addr) { SetHost(addr); } AddressFamily GetFamily() const; @@ -209,6 +210,8 @@ public: }; typedef EndpointAddress addr_t; + static std::vector GetLocalAddresses(); + // callback class class CBClass { diff --git a/src/network/C4Network2Client.cpp b/src/network/C4Network2Client.cpp index f239fd8b4..be3a77628 100644 --- a/src/network/C4Network2Client.cpp +++ b/src/network/C4Network2Client.cpp @@ -25,16 +25,6 @@ #include "game/C4Game.h" #include "player/C4PlayerList.h" -#ifdef _WIN32 -#include -#include -#else -#include -#include -#include -#include -#endif - // *** C4Network2Client C4Network2Client::C4Network2Client(C4Client *pClient) @@ -187,84 +177,24 @@ bool C4Network2Client::AddAddr(const C4Network2Address &addr, bool fAnnounce) void C4Network2Client::AddLocalAddrs(int16_t iPortTCP, int16_t iPortUDP) { - // set up address struct C4NetIO::addr_t addr; - // get local address(es) -#ifdef HAVE_WINSOCK - const size_t BUFFER_SIZE = 16000; - PIP_ADAPTER_ADDRESSES addresses = nullptr; - for (int i = 0; i < 3; ++i) + for (auto& ha : C4NetIO::GetLocalAddresses()) { - addresses = (PIP_ADAPTER_ADDRESSES) realloc(addresses, BUFFER_SIZE * (i+1)); - if (!addresses) - // allocation failed - return; - ULONG bufsz = BUFFER_SIZE * (i+1); - DWORD rv = GetAdaptersAddresses(AF_UNSPEC, - GAA_FLAG_SKIP_ANYCAST|GAA_FLAG_SKIP_MULTICAST|GAA_FLAG_SKIP_DNS_SERVER|GAA_FLAG_SKIP_FRIENDLY_NAME, - nullptr, addresses, &bufsz); - if (rv == ERROR_BUFFER_OVERFLOW) - // too little space, try again - continue; - if (rv != NO_ERROR) + addr.SetAddress(ha); + if (iPortTCP) { - // Something else happened - free(addresses); - return; + addr.SetPort(iPortTCP); + AddAddr(C4Network2Address(addr, P_TCP), false); } - // All okay, add addresses - for (PIP_ADAPTER_ADDRESSES address = addresses; address; address = address->Next) + if (iPortUDP) { - for (PIP_ADAPTER_UNICAST_ADDRESS unicast = address->FirstUnicastAddress; unicast; unicast = unicast->Next) - { - addr.SetHost(unicast->Address.lpSockaddr); - if (addr.IsLoopback()) - continue; - if (iPortTCP) - { - addr.SetPort(iPortTCP); - AddAddr(C4Network2Address(addr, P_TCP), false); - } - if (iPortUDP) - { - addr.SetPort(iPortUDP); - AddAddr(C4Network2Address(addr, P_UDP), false); - } - if (addr.GetScopeId()) - InterfaceIDs.insert(addr.GetScopeId()); - } + addr.SetPort(iPortUDP); + AddAddr(C4Network2Address(addr, P_UDP), false); } + if (addr.GetScopeId()) + InterfaceIDs.insert(addr.GetScopeId()); } - free(addresses); -#else - struct ifaddrs* addrs; - if (getifaddrs(&addrs) < 0) - return; - for (struct ifaddrs* ifaddr = addrs; ifaddr != nullptr; ifaddr = ifaddr->ifa_next) - { - struct sockaddr* ad = ifaddr->ifa_addr; - if (ad == nullptr) continue; - - if ((ad->sa_family == AF_INET || ad->sa_family == AF_INET6) && (~ifaddr->ifa_flags & IFF_LOOPBACK)) // Choose only non-loopback IPv4/6 devices - { - addr.SetHost(ad); - if (iPortTCP >= 0) - { - addr.SetPort(iPortTCP); - AddAddr(C4Network2Address(addr, P_TCP), false); - } - if (iPortUDP >= 0) - { - addr.SetPort(iPortUDP); - AddAddr(C4Network2Address(addr, P_UDP), false); - } - if (addr.GetScopeId()) - InterfaceIDs.insert(addr.GetScopeId()); - } - } - freeifaddrs(addrs); -#endif } void C4Network2Client::SendAddresses(C4Network2IOConnection *pConn)