forked from Mirrors/openclonk
Compare commits
18 Commits
Author | SHA1 | Date |
---|---|---|
Lukas Werling | d7659713dc | |
Lukas Werling | f9c97e91f0 | |
Lukas Werling | b595e96b83 | |
Lukas Werling | cc1335fef9 | |
Lukas Werling | 6addce2f95 | |
Lukas Werling | 7411e458a0 | |
Lukas Werling | 23078c6e69 | |
Lukas Werling | 76327b62a7 | |
Lukas Werling | fd857ef771 | |
Lukas Werling | 16b511b75d | |
Lukas Werling | 33a32cb6bd | |
Lukas Werling | c94b4cc3b0 | |
Lukas Werling | 60560125dc | |
Lukas Werling | 65d8c11450 | |
Lukas Werling | 725e99bb9c | |
Lukas Werling | f4bfd8c080 | |
Lukas Werling | 758e7ca41e | |
Lukas Werling | 0137c5f929 |
|
@ -76,7 +76,8 @@ namespace std {
|
|||
auto unpack = make_tuple(v6.sin6_family, v6.sin6_port, v6.sin6_flowinfo, std::string((char*) v6.sin6_addr.s6_addr, 16), v6.sin6_scope_id);
|
||||
return hash<decltype(unpack)>()(unpack);
|
||||
}
|
||||
default:
|
||||
case C4NetIO::HostAddress::UnknownFamily:
|
||||
assert(!"Unexpected address family");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,11 @@
|
|||
#include "network/C4Network2Address.h"
|
||||
#include <sstream>
|
||||
|
||||
void C4NetpuncherID::CompileFunc(StdCompiler *pComp) {
|
||||
pComp->Value(mkNamingAdapt(v4, "IPv4", 0u));
|
||||
pComp->Value(mkNamingAdapt(v6, "IPv6", 0u));
|
||||
}
|
||||
|
||||
std::unique_ptr<C4NetpuncherPacket> C4NetpuncherPacket::Construct(const C4NetIOPacket& rpack) {
|
||||
if (!rpack.getPData()) return nullptr;
|
||||
try {
|
||||
|
|
|
@ -27,7 +27,14 @@ enum C4NetpuncherPacketType {
|
|||
// extend this with exchanging ICE parameters, some day?
|
||||
};
|
||||
|
||||
typedef uint32_t C4NetpuncherID_t;
|
||||
struct C4NetpuncherID {
|
||||
typedef uint32_t value;
|
||||
|
||||
value v4 = 0, v6 = 0;
|
||||
|
||||
void CompileFunc(StdCompiler *pComp);
|
||||
bool operator==(const C4NetpuncherID& other) const { return v4 == other.v4 && v6 == other.v6; }
|
||||
};
|
||||
|
||||
class C4NetpuncherPacket {
|
||||
public:
|
||||
|
@ -38,7 +45,7 @@ public:
|
|||
C4NetIOPacket PackTo(const C4NetIO::addr_t&) const;
|
||||
protected:
|
||||
virtual StdBuf PackInto() const = 0;
|
||||
typedef C4NetpuncherID_t CID;
|
||||
typedef C4NetpuncherID::value CID;
|
||||
};
|
||||
|
||||
template<C4NetpuncherPacketType TYPE>
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
class C4PuncherServer : public C4NetIOUDP, private C4NetIO::CBClass
|
||||
{
|
||||
public:
|
||||
typedef C4NetpuncherID_t CID;
|
||||
typedef C4NetpuncherID::value CID;
|
||||
C4PuncherServer() {
|
||||
C4NetIOUDP::SetCallback(this);
|
||||
rng = std::bind(std::uniform_int_distribution<CID>(1/*, max*/), std::ref(random_device));
|
||||
|
@ -96,7 +96,11 @@ int main(int argc, char * argv[])
|
|||
printf("Listening on port %d...\n", iPort);
|
||||
|
||||
// Execute forever
|
||||
Puncher.ExecuteUntil(-1);
|
||||
for (;;)
|
||||
{
|
||||
Puncher.ExecuteUntil(-1);
|
||||
fprintf(stderr, "ERROR: %s\n", Puncher.GetError());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
|
||||
#include <process.h>
|
||||
#include <share.h>
|
||||
#include <winsock2.h>
|
||||
#include <iphlpapi.h>
|
||||
|
||||
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 <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <stdlib.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <net/if.h>
|
||||
|
||||
#define ioctlsocket ioctl
|
||||
#define closesocket close
|
||||
|
@ -50,11 +54,16 @@ int pipe(int *phandles) { return _pipe(phandles, 10, O_BINARY); }
|
|||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning (disable : 4355)
|
||||
#endif
|
||||
|
||||
// These are named differently on mac.
|
||||
#if !defined(IPV6_ADD_MEMBERSHIP) && defined(IPV6_JOIN_GROUP)
|
||||
#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
|
||||
#define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
|
||||
#endif
|
||||
|
||||
// constants definition
|
||||
const int C4NetIO::TO_INF = -1;
|
||||
|
||||
|
@ -244,6 +253,16 @@ bool C4NetIO::HostAddress::IsLoopback() const
|
|||
return false;
|
||||
}
|
||||
|
||||
bool C4NetIO::HostAddress::IsLocal() const
|
||||
{
|
||||
if (gen.sa_family == AF_INET6)
|
||||
return IN6_IS_ADDR_LINKLOCAL(&v6.sin6_addr) != 0;
|
||||
// We don't really care about local 169.256.0.0/16 addresses here as users will either have a
|
||||
// router doing DHCP (which will prevent usage of these addresses) or have a network that
|
||||
// doesn't care about IP and IPv6 link-local addresses will work.
|
||||
return false;
|
||||
}
|
||||
|
||||
void C4NetIO::HostAddress::SetScopeId(int scopeId)
|
||||
{
|
||||
if (gen.sa_family != AF_INET6) return;
|
||||
|
@ -280,11 +299,27 @@ C4NetIO::HostAddress C4NetIO::HostAddress::AsIPv6() const
|
|||
return nrv;
|
||||
}
|
||||
|
||||
C4NetIO::HostAddress C4NetIO::HostAddress::AsIPv4() const
|
||||
{
|
||||
HostAddress nrv(*this);
|
||||
if (gen.sa_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&v6.sin6_addr))
|
||||
{
|
||||
nrv.v4.sin_family = AF_INET;
|
||||
memcpy((char*) &nrv.v4.sin_addr, (char*) &v6.sin6_addr.s6_addr[12], sizeof(v4.sin_addr));
|
||||
}
|
||||
return nrv;
|
||||
}
|
||||
|
||||
C4NetIO::EndpointAddress C4NetIO::EndpointAddress::AsIPv6() const
|
||||
{
|
||||
return EndpointAddress(HostAddress::AsIPv6(), GetPort());
|
||||
}
|
||||
|
||||
C4NetIO::EndpointAddress C4NetIO::EndpointAddress::AsIPv4() const
|
||||
{
|
||||
return EndpointAddress(HostAddress::AsIPv4(), GetPort());
|
||||
}
|
||||
|
||||
void C4NetIO::HostAddress::SetHost(const sockaddr *addr)
|
||||
{
|
||||
// Copy all but port number
|
||||
|
@ -346,7 +381,19 @@ void C4NetIO::HostAddress::SetHost(uint32_t v4addr)
|
|||
memset(&v4.sin_zero, 0, sizeof(v4.sin_zero));
|
||||
}
|
||||
|
||||
void C4NetIO::EndpointAddress::SetAddress(const StdStrBuf &addr)
|
||||
void C4NetIO::HostAddress::SetHost(const StdStrBuf &addr, AddressFamily family)
|
||||
{
|
||||
addrinfo hints = addrinfo();
|
||||
hints.ai_family = family;
|
||||
addrinfo *addresses = nullptr;
|
||||
if (getaddrinfo(addr.getData(), nullptr, &hints, &addresses) != 0)
|
||||
// GAI failed
|
||||
return;
|
||||
SetHost(addresses->ai_addr);
|
||||
freeaddrinfo(addresses);
|
||||
}
|
||||
|
||||
void C4NetIO::EndpointAddress::SetAddress(const StdStrBuf &addr, AddressFamily family)
|
||||
{
|
||||
Clear();
|
||||
|
||||
|
@ -402,6 +449,7 @@ void C4NetIO::EndpointAddress::SetAddress(const StdStrBuf &addr)
|
|||
}
|
||||
|
||||
addrinfo hints = addrinfo();
|
||||
hints.ai_family = family;
|
||||
//hints.ai_flags = AI_NUMERICHOST;
|
||||
addrinfo *addresses = nullptr;
|
||||
if (getaddrinfo(std::string(ab, ae).c_str(), pb != end ? std::string(pb, pe).c_str() : nullptr, &hints, &addresses) != 0)
|
||||
|
@ -462,6 +510,12 @@ void C4NetIO::EndpointAddress::SetPort(uint16_t port)
|
|||
}
|
||||
}
|
||||
|
||||
void C4NetIO::EndpointAddress::SetDefaultPort(uint16_t port)
|
||||
{
|
||||
if (GetPort() == IPPORT_NONE)
|
||||
SetPort(port);
|
||||
}
|
||||
|
||||
uint16_t C4NetIO::EndpointAddress::GetPort() const
|
||||
{
|
||||
switch (gen.sa_family)
|
||||
|
@ -546,6 +600,66 @@ void C4NetIO::EndpointAddress::CompileFunc(StdCompiler *comp)
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<C4NetIO::HostAddress> C4NetIO::GetLocalAddresses()
|
||||
{
|
||||
std::vector<HostAddress> 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
|
||||
|
@ -560,6 +674,17 @@ C4NetIO::~C4NetIO()
|
|||
|
||||
}
|
||||
|
||||
bool C4NetIO::EnableDualStack(SOCKET socket)
|
||||
{
|
||||
int opt = 0;
|
||||
if (setsockopt(socket, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char*>(&opt), sizeof(opt)) == SOCKET_ERROR)
|
||||
{
|
||||
SetError("could not enable dual-stack socket", true);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void C4NetIO::SetError(const char *strnError, bool fSockErr)
|
||||
{
|
||||
fSockErr &= HaveSocketError();
|
||||
|
@ -1310,6 +1435,8 @@ bool C4NetIOTCP::Listen(uint16_t inListenPort)
|
|||
SetError("socket creation failed", true);
|
||||
return false;
|
||||
}
|
||||
if (!EnableDualStack(lsock))
|
||||
return false;
|
||||
// To be able to reuse the port after close
|
||||
#if !defined(_DEBUG) && !defined(_WIN32)
|
||||
int reuseaddr = 1;
|
||||
|
@ -1682,6 +1809,9 @@ bool C4NetIOSimpleUDP::Init(uint16_t inPort)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!EnableDualStack(sock))
|
||||
return false;
|
||||
|
||||
// set reuse socket option
|
||||
if (::setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char *>(&fAllowReUse), sizeof fAllowReUse) == SOCKET_ERROR)
|
||||
{
|
||||
|
@ -1757,9 +1887,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)
|
||||
|
@ -1769,8 +1899,8 @@ bool C4NetIOSimpleUDP::InitBroadcast(addr_t *pBroadcastAddr)
|
|||
}
|
||||
|
||||
// set mc ttl to somewhat about "same net"
|
||||
int iTTL = 16;
|
||||
if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, reinterpret_cast<char*>(&iTTL), sizeof(iTTL)) == SOCKET_ERROR)
|
||||
int TTL = 16;
|
||||
if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, reinterpret_cast<char*>(&TTL), sizeof(TTL)) == SOCKET_ERROR)
|
||||
{
|
||||
SetError("could not set mc ttl", true);
|
||||
return false;
|
||||
|
@ -1778,11 +1908,12 @@ bool C4NetIOSimpleUDP::InitBroadcast(addr_t *pBroadcastAddr)
|
|||
|
||||
// set up multicast group information
|
||||
this->MCAddr = *pBroadcastAddr;
|
||||
MCGrpInfo.imr_multiaddr = static_cast<sockaddr_in*>(&MCAddr)->sin_addr;
|
||||
MCGrpInfo.imr_interface.s_addr = INADDR_ANY;
|
||||
MCGrpInfo.ipv6mr_multiaddr = static_cast<sockaddr_in6>(MCAddr).sin6_addr;
|
||||
// TODO: do multicast on all interfaces?
|
||||
MCGrpInfo.ipv6mr_interface = 0; // default interface
|
||||
|
||||
// join multicast group
|
||||
if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
|
||||
if (setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
|
||||
reinterpret_cast<const char *>(&MCGrpInfo), sizeof(MCGrpInfo)) == SOCKET_ERROR)
|
||||
{
|
||||
SetError("could not join multicast group"); // to do: more error information
|
||||
|
@ -1844,10 +1975,10 @@ bool C4NetIOSimpleUDP::CloseBroadcast()
|
|||
if (!fMultiCast) return true;
|
||||
|
||||
// leave multicast group
|
||||
if (setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP,
|
||||
if (setsockopt(sock, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP,
|
||||
reinterpret_cast<const char *>(&MCGrpInfo), sizeof(MCGrpInfo)) == SOCKET_ERROR)
|
||||
{
|
||||
SetError("could not join multicast group"); // to do: more error information
|
||||
SetError("could not leave multicast group"); // to do: more error information
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -2049,10 +2180,10 @@ enum C4NetIOSimpleUDP::WaitResult C4NetIOSimpleUDP::WaitForSocket(int iTimeout)
|
|||
bool C4NetIOSimpleUDP::SetMCLoopback(int fLoopback)
|
||||
{
|
||||
// enable/disable MC loopback
|
||||
setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, reinterpret_cast<char *>(&fLoopback), sizeof fLoopback);
|
||||
setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, reinterpret_cast<char *>(&fLoopback), sizeof fLoopback);
|
||||
// read result
|
||||
socklen_t iSize = sizeof(fLoopback);
|
||||
if (getsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, reinterpret_cast<char *>(&fLoopback), &iSize) == SOCKET_ERROR)
|
||||
if (getsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, reinterpret_cast<char *>(&fLoopback), &iSize) == SOCKET_ERROR)
|
||||
return false;
|
||||
fMCLoopback = !! fLoopback;
|
||||
return true;
|
||||
|
@ -2086,6 +2217,76 @@ const unsigned int C4NetIOUDP::iUDPHeaderSize = 8 + 24; // (bytes)
|
|||
|
||||
#pragma pack (push, 1)
|
||||
|
||||
// We need to adapt C4NetIO::addr_t to put it in our UDP packages.
|
||||
// Previously, the sockaddr_in struct was just put in directly. This is
|
||||
// horribly non-portable though, especially as the value of AF_INET6 differs
|
||||
// between platforms.
|
||||
struct C4NetIOUDP::BinAddr
|
||||
{
|
||||
BinAddr() : type(0) {}
|
||||
BinAddr(const C4NetIO::addr_t& addr)
|
||||
{
|
||||
switch (addr.GetFamily())
|
||||
{
|
||||
case C4NetIO::HostAddress::IPv4:
|
||||
{
|
||||
type = 1;
|
||||
auto addr4 = static_cast<const sockaddr_in*>(&addr);
|
||||
static_assert(sizeof(v4) == sizeof(addr4->sin_addr), "unexpected IPv4 address size");
|
||||
memcpy(&v4, &addr4->sin_addr, sizeof(v4));
|
||||
break;
|
||||
}
|
||||
case C4NetIO::HostAddress::IPv6:
|
||||
{
|
||||
type = 2;
|
||||
auto addr6 = static_cast<const sockaddr_in6*>(&addr);
|
||||
static_assert(sizeof(v6) == sizeof(addr6->sin6_addr), "unexpected IPv6 address size");
|
||||
memcpy(&v6, &addr6->sin6_addr, sizeof(v6));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(!"Unexpected address family");
|
||||
}
|
||||
port = addr.GetPort();
|
||||
}
|
||||
|
||||
operator C4NetIO::addr_t() const
|
||||
{
|
||||
C4NetIO::addr_t result;
|
||||
switch (type)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
sockaddr_in addr4 = sockaddr_in();
|
||||
addr4.sin_family = AF_INET;
|
||||
memcpy(&addr4.sin_addr, &v4, sizeof(v4));
|
||||
result.SetAddress(reinterpret_cast<sockaddr*>(&addr4));
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
sockaddr_in6 addr6 = sockaddr_in6();
|
||||
addr6.sin6_family = AF_INET6;
|
||||
memcpy(&addr6.sin6_addr, &v6, sizeof(v6));
|
||||
result.SetAddress(reinterpret_cast<sockaddr*>(&addr6));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(!"Invalid address type");
|
||||
}
|
||||
result.SetPort(port);
|
||||
return result;
|
||||
}
|
||||
|
||||
uint16_t port;
|
||||
uint8_t type;
|
||||
union
|
||||
{
|
||||
uint8_t v4[4];
|
||||
uint8_t v6[16];
|
||||
};
|
||||
};
|
||||
|
||||
// packet structures
|
||||
struct C4NetIOUDP::PacketHdr
|
||||
{
|
||||
|
@ -2096,20 +2297,20 @@ struct C4NetIOUDP::PacketHdr
|
|||
struct C4NetIOUDP::ConnPacket : public PacketHdr
|
||||
{
|
||||
uint32_t ProtocolVer;
|
||||
C4NetIO::addr_t Addr;
|
||||
C4NetIO::addr_t MCAddr;
|
||||
BinAddr Addr;
|
||||
BinAddr MCAddr;
|
||||
};
|
||||
|
||||
struct C4NetIOUDP::ConnOKPacket : public PacketHdr
|
||||
{
|
||||
enum { MCM_NoMC, MCM_MC, MCM_MCOK } MCMode;
|
||||
C4NetIO::addr_t Addr;
|
||||
BinAddr Addr;
|
||||
};
|
||||
|
||||
struct C4NetIOUDP::AddAddrPacket : public PacketHdr
|
||||
{
|
||||
C4NetIO::addr_t Addr;
|
||||
C4NetIO::addr_t NewAddr;
|
||||
BinAddr Addr;
|
||||
BinAddr NewAddr;
|
||||
};
|
||||
|
||||
struct C4NetIOUDP::DataPacketHdr : public PacketHdr
|
||||
|
@ -2126,7 +2327,7 @@ struct C4NetIOUDP::CheckPacketHdr : public PacketHdr
|
|||
|
||||
struct C4NetIOUDP::ClosePacket : public PacketHdr
|
||||
{
|
||||
C4NetIO::addr_t Addr;
|
||||
BinAddr Addr;
|
||||
};
|
||||
|
||||
|
||||
|
@ -2214,13 +2415,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<sockaddr_in6*>(&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;
|
||||
|
@ -2977,14 +3205,16 @@ void C4NetIOUDP::Peer::OnRecv(const C4NetIOPacket &rPacket) // (mt-safe)
|
|||
iLastPacketAsked = iLastMCPacketAsked = 0;
|
||||
// Activate Multicast?
|
||||
if (!pParent->fMultiCast)
|
||||
if (!pPkt->MCAddr.IsNull())
|
||||
{
|
||||
addr_t MCAddr = pPkt->MCAddr;
|
||||
if (!MCAddr.IsNull())
|
||||
{
|
||||
addr_t MCAddr = pPkt->MCAddr;
|
||||
// Init Broadcast (with delayed loopback test)
|
||||
pParent->fDelayedLoopbackTest = true;
|
||||
if (!pParent->InitBroadcast(&MCAddr))
|
||||
pParent->fDelayedLoopbackTest = false;
|
||||
}
|
||||
}
|
||||
// build ConnOk Packet
|
||||
ConnOKPacket nPack;
|
||||
|
||||
|
@ -3158,7 +3388,7 @@ bool C4NetIOUDP::Peer::DoConn(bool fMC) // (mt-safe)
|
|||
if (pParent->fMultiCast)
|
||||
Pkt.MCAddr = pParent->C4NetIOSimpleUDP::getMCAddr();
|
||||
else
|
||||
Pkt.MCAddr.Clear();
|
||||
Pkt.MCAddr = C4NetIO::addr_t();
|
||||
return SendDirect(C4NetIOPacket(&Pkt, sizeof(Pkt), false, addr));
|
||||
}
|
||||
|
||||
|
@ -3693,50 +3923,3 @@ void C4NetIOMan::EnlargeIO(int iBy)
|
|||
delete[] ppNetIO;
|
||||
ppNetIO = ppnNetIO;
|
||||
}
|
||||
|
||||
// *** helpers
|
||||
|
||||
bool ResolveAddress(const char *szAddress, C4NetIO::addr_t *paddr, uint16_t iPort)
|
||||
{
|
||||
assert(szAddress && paddr);
|
||||
// port?
|
||||
StdStrBuf Buf;
|
||||
const char *pColon = strchr(szAddress, ':');
|
||||
if (pColon)
|
||||
{
|
||||
// get port
|
||||
iPort = atoi(pColon + 1);
|
||||
// copy address
|
||||
Buf.CopyUntil(szAddress, ':');
|
||||
szAddress = Buf.getData();
|
||||
}
|
||||
// set up address
|
||||
sockaddr_in raddr; ZeroMem(&raddr, sizeof raddr);
|
||||
raddr.sin_family = AF_INET;
|
||||
raddr.sin_port = htons(iPort);
|
||||
// no plain IP address?
|
||||
if ((raddr.sin_addr.s_addr = inet_addr(szAddress)) == INADDR_NONE)
|
||||
{
|
||||
#ifdef HAVE_WINSOCK
|
||||
if (!AcquireWinSock()) return false;
|
||||
#endif
|
||||
// resolve
|
||||
hostent *pHost;
|
||||
if (!(pHost = gethostbyname(szAddress)))
|
||||
#ifdef HAVE_WINSOCK
|
||||
{ ReleaseWinSock(); return false; }
|
||||
ReleaseWinSock();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
// correct type?
|
||||
if (pHost->h_addrtype != AF_INET || pHost->h_length != sizeof(in_addr))
|
||||
return false;
|
||||
// get address
|
||||
raddr.sin_addr = *reinterpret_cast<in_addr *>(pHost->h_addr_list[0]);
|
||||
}
|
||||
// ok
|
||||
paddr->SetAddress(reinterpret_cast<sockaddr*>(&raddr));
|
||||
paddr->SetPort(iPort);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
@ -102,15 +103,17 @@ public:
|
|||
void SetHost(const sockaddr *addr);
|
||||
void SetHost(const HostAddress &host);
|
||||
void SetHost(SpecialAddress host);
|
||||
void SetHost(const StdStrBuf &host);
|
||||
void SetHost(const StdStrBuf &host, AddressFamily family = UnknownFamily);
|
||||
void SetHost(uint32_t host);
|
||||
|
||||
C4NetIO::HostAddress AsIPv6() const; // convert an IPv4 address to an IPv6-mapped IPv4 address
|
||||
C4NetIO::HostAddress AsIPv4() const; // try to convert an IPv6-mapped IPv4 address to an IPv4 address (returns unchanged address if not possible)
|
||||
|
||||
// General categories
|
||||
bool IsNull() const;
|
||||
bool IsMulticast() const;
|
||||
bool IsLoopback() const;
|
||||
bool IsLocal() const;
|
||||
// bool IsBroadcast() const;
|
||||
|
||||
StdStrBuf ToString(int flags = 0) const;
|
||||
|
@ -146,12 +149,14 @@ public:
|
|||
void SetAddress(const EndpointAddress &other);
|
||||
void SetAddress(HostAddress::SpecialAddress addr, uint16_t port = IPPORT_NONE);
|
||||
void SetAddress(const HostAddress &host, uint16_t port = IPPORT_NONE);
|
||||
void SetAddress(const StdStrBuf &addr);
|
||||
void SetAddress(const StdStrBuf &addr, AddressFamily family = UnknownFamily);
|
||||
|
||||
HostAddress GetHost() const { return *this; } // HostAddress copy ctor slices off port information
|
||||
EndpointAddress AsIPv6() const; // convert an IPv4 address to an IPv6-mapped IPv4 address
|
||||
EndpointAddress AsIPv4() const; // try to convert an IPv6-mapped IPv4 address to an IPv4 address (returns unchanged address if not possible)
|
||||
|
||||
void SetPort(uint16_t port);
|
||||
void SetDefaultPort(uint16_t port); // set a port only if there is none
|
||||
uint16_t GetPort() const;
|
||||
|
||||
bool IsNull() const;
|
||||
|
@ -192,8 +197,8 @@ public:
|
|||
|
||||
// conversions
|
||||
operator sockaddr() const { return gen; }
|
||||
/* operator sockaddr_in() const { assert(gen.sa_family == AF_INET); return v4; }
|
||||
operator sockaddr_in6() const { assert(gen.sa_family == AF_INET6); return v6; }*/
|
||||
operator sockaddr_in() const { assert(gen.sa_family == AF_INET); return v4; }
|
||||
operator sockaddr_in6() const { assert(gen.sa_family == AF_INET6); return v6; }
|
||||
|
||||
// StdCompiler
|
||||
void CompileFunc(StdCompiler *comp);
|
||||
|
@ -205,6 +210,8 @@ public:
|
|||
};
|
||||
typedef EndpointAddress addr_t;
|
||||
|
||||
static std::vector<HostAddress> GetLocalAddresses();
|
||||
|
||||
// callback class
|
||||
class CBClass
|
||||
{
|
||||
|
@ -266,6 +273,9 @@ public:
|
|||
protected:
|
||||
// virtual SOCKET CreateSocket() = 0;
|
||||
|
||||
// Makes IPv4 connections from an IPv6 socket work.
|
||||
bool EnableDualStack(SOCKET socket);
|
||||
|
||||
// *** errors
|
||||
protected:
|
||||
StdCopyStrBuf Error;
|
||||
|
@ -532,7 +542,7 @@ private:
|
|||
#endif
|
||||
|
||||
// multicast
|
||||
addr_t MCAddr; ip_mreq MCGrpInfo;
|
||||
addr_t MCAddr; ipv6_mreq MCGrpInfo;
|
||||
bool fMCLoopback;
|
||||
|
||||
// multibind
|
||||
|
@ -616,6 +626,7 @@ protected:
|
|||
};
|
||||
|
||||
// packet structures
|
||||
struct BinAddr;
|
||||
struct PacketHdr; struct TestPacket; struct ConnPacket; struct ConnOKPacket; struct AddAddrPacket;
|
||||
struct DataPacketHdr; struct CheckPacketHdr; struct ClosePacket;
|
||||
|
||||
|
@ -943,26 +954,9 @@ private:
|
|||
void EnlargeIO(int iBy);
|
||||
};
|
||||
|
||||
// helpers
|
||||
// there seems to be no standard way to get these numbers, so let's do it the dirty way...
|
||||
inline uint8_t &in_addr_b(in_addr &addr, int i)
|
||||
{
|
||||
assert(0 <= i && i < 4);
|
||||
return *(reinterpret_cast<uint8_t *>(&addr.s_addr) + i);
|
||||
}
|
||||
|
||||
inline void CompileFunc(in_addr &ip, StdCompiler *pComp)
|
||||
{
|
||||
pComp->Value(in_addr_b(ip, 0)); pComp->Separator(StdCompiler::SEP_PART);
|
||||
pComp->Value(in_addr_b(ip, 1)); pComp->Separator(StdCompiler::SEP_PART);
|
||||
pComp->Value(in_addr_b(ip, 2)); pComp->Separator(StdCompiler::SEP_PART);
|
||||
pComp->Value(in_addr_b(ip, 3));
|
||||
}
|
||||
|
||||
#ifdef HAVE_WINSOCK
|
||||
bool AcquireWinSock();
|
||||
void ReleaseWinSock();
|
||||
#endif
|
||||
bool ResolveAddress(const char *szAddress, C4NetIO::addr_t *paddr, uint16_t iPort);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -148,7 +148,7 @@ C4Network2::C4Network2()
|
|||
fPausedForVote(false),
|
||||
iLastOwnVoting(0),
|
||||
fStreaming(false),
|
||||
NetpuncherGameID(0)
|
||||
NetpuncherGameID(C4NetpuncherID())
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -169,7 +169,7 @@ bool C4Network2::InitHost(bool fLobby)
|
|||
fChasing = false;
|
||||
fAllowJoin = false;
|
||||
iNextClientID = C4ClientIDStart;
|
||||
NetpuncherGameID = 0;
|
||||
NetpuncherGameID = C4NetpuncherID();
|
||||
NetpuncherAddr = ::Config.Network.PuncherAddress;
|
||||
// initialize client list
|
||||
Clients.Init(&Game.Clients, true);
|
||||
|
@ -310,9 +310,25 @@ C4Network2::InitResult C4Network2::InitClient(const class C4Network2Address *pAd
|
|||
for (int i = 0; i < iAddrCount; i++)
|
||||
if (!pAddrs[i].isIPNull())
|
||||
{
|
||||
auto addr = pAddrs[i].getAddr();
|
||||
std::vector<C4NetIO::addr_t> addrs;
|
||||
if (addr.IsLocal())
|
||||
{
|
||||
// Local IPv6 addresses need a scope id.
|
||||
for (auto& id : Clients.GetLocal()->getInterfaceIDs())
|
||||
{
|
||||
addr.SetScopeId(id);
|
||||
addrs.push_back(addr);
|
||||
}
|
||||
}
|
||||
else
|
||||
addrs.push_back(addr);
|
||||
// connection
|
||||
if (!NetIO.Connect(pAddrs[i].getAddr(), pAddrs[i].getProtocol(), HostCore, szPassword))
|
||||
continue;
|
||||
int cnt = 0;
|
||||
for (auto& a : addrs)
|
||||
if (NetIO.Connect(a, pAddrs[i].getProtocol(), HostCore, szPassword))
|
||||
cnt++;
|
||||
if (cnt == 0) continue;
|
||||
// format for message
|
||||
if (strAddresses.getLength())
|
||||
strAddresses.Append(", ");
|
||||
|
@ -673,7 +689,7 @@ void C4Network2::Clear()
|
|||
delete pVoteDialog; pVoteDialog = nullptr;
|
||||
fPausedForVote = false;
|
||||
iLastOwnVoting = 0;
|
||||
NetpuncherGameID = 0;
|
||||
NetpuncherGameID = C4NetpuncherID();
|
||||
Votes.Clear();
|
||||
// don't clear fPasswordNeeded here, it's needed by InitClient
|
||||
}
|
||||
|
@ -891,7 +907,7 @@ void C4Network2::HandleLobbyPacket(char cStatus, const C4PacketBase *pBasePkt, C
|
|||
if (pLobby) pLobby->HandlePacket(cStatus, pBasePkt, pClient);
|
||||
}
|
||||
|
||||
bool C4Network2::HandlePuncherPacket(C4NetpuncherPacket::uptr pkt)
|
||||
bool C4Network2::HandlePuncherPacket(C4NetpuncherPacket::uptr pkt, C4NetIO::HostAddress::AddressFamily family)
|
||||
{
|
||||
// TODO: is this all thread-safe?
|
||||
assert(pkt);
|
||||
|
@ -912,26 +928,48 @@ bool C4Network2::HandlePuncherPacket(C4NetpuncherPacket::uptr pkt)
|
|||
case PID_Puncher_AssID:
|
||||
if (isHost())
|
||||
{
|
||||
NetpuncherGameID = GETPKT(AssID)->GetID();
|
||||
getNetpuncherGameID(family) = GETPKT(AssID)->GetID();
|
||||
InvalidateReference();
|
||||
}
|
||||
else
|
||||
{
|
||||
// While we don't need the ID as a client, this nicely serves as the signal that we can start using the netpuncher
|
||||
if (Status.getState() == GS_Init && getNetpuncherGameID())
|
||||
NetIO.SendPuncherPacket(C4NetpuncherPacketSReq(getNetpuncherGameID()));
|
||||
if (Status.getState() == GS_Init && getNetpuncherGameID(family))
|
||||
NetIO.SendPuncherPacket(C4NetpuncherPacketSReq(getNetpuncherGameID(family)), family);
|
||||
}
|
||||
return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
C4NetpuncherID::value& C4Network2::getNetpuncherGameID(C4NetIO::HostAddress::AddressFamily family)
|
||||
{
|
||||
switch (family)
|
||||
{
|
||||
case C4NetIO::HostAddress::IPv4: return NetpuncherGameID.v4;
|
||||
case C4NetIO::HostAddress::IPv6: return NetpuncherGameID.v6;
|
||||
case C4NetIO::HostAddress::UnknownFamily: assert(!"Unexpected address family");
|
||||
}
|
||||
// We need to return a valid reference to satisfy the compiler, even though the code here is unreachable.
|
||||
return NetpuncherGameID.v4;
|
||||
}
|
||||
|
||||
void C4Network2::InitPuncher()
|
||||
{
|
||||
// We have an internet connection, so let's punch the puncher server here in order to open an udp port
|
||||
C4NetIO::addr_t PuncherAddr;
|
||||
if (ResolveAddress(getNetpuncherAddr().getData(), &PuncherAddr, C4NetStdPortPuncher))
|
||||
NetIO.InitPuncher(PuncherAddr);
|
||||
PuncherAddr.SetAddress(getNetpuncherAddr(), C4NetIO::HostAddress::IPv4);
|
||||
if (!PuncherAddr.IsNull())
|
||||
{
|
||||
PuncherAddr.SetDefaultPort(C4NetStdPortPuncher);
|
||||
NetIO.InitPuncher(PuncherAddr);
|
||||
}
|
||||
PuncherAddr.SetAddress(getNetpuncherAddr(), C4NetIO::HostAddress::IPv6);
|
||||
if (!PuncherAddr.IsNull())
|
||||
{
|
||||
PuncherAddr.SetDefaultPort(C4NetStdPortPuncher);
|
||||
NetIO.InitPuncher(PuncherAddr);
|
||||
}
|
||||
}
|
||||
|
||||
void C4Network2::OnGameSynchronized()
|
||||
|
@ -1198,9 +1236,6 @@ bool C4Network2::CheckConn(const C4ClientCore &CCore, C4Network2IOConnection *pC
|
|||
// check core
|
||||
if (CCore.getDiffLevel(pClient->getCore()) > C4ClientCoreDL_IDMatch)
|
||||
{ *szReply = "wrong client core"; return false; }
|
||||
// check address
|
||||
if (pClient->isConnected() && pClient->getMsgConn()->getPeerAddr() != pConn->getPeerAddr())
|
||||
{ *szReply = "wrong address"; return false; }
|
||||
// accept
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -194,7 +194,7 @@ protected:
|
|||
unsigned int iCurrentStreamAmount, iCurrentStreamPosition;
|
||||
|
||||
// puncher
|
||||
C4NetpuncherID_t NetpuncherGameID;
|
||||
C4NetpuncherID NetpuncherGameID;
|
||||
StdCopyStrBuf NetpuncherAddr;
|
||||
|
||||
public:
|
||||
|
@ -257,7 +257,7 @@ public:
|
|||
void OnDisconn(C4Network2IOConnection *pConn);
|
||||
void HandlePacket(char cStatus, const C4PacketBase *pBasePkt, C4Network2IOConnection *pConn);
|
||||
void HandleLobbyPacket(char cStatus, const C4PacketBase *pBasePkt, C4Network2IOConnection *pConn);
|
||||
bool HandlePuncherPacket(C4NetpuncherPacket::uptr);
|
||||
bool HandlePuncherPacket(C4NetpuncherPacket::uptr, C4NetIO::HostAddress::AddressFamily family);
|
||||
|
||||
// runtime join stuff
|
||||
void OnGameSynchronized();
|
||||
|
@ -303,7 +303,8 @@ public:
|
|||
bool StopStreaming();
|
||||
|
||||
// netpuncher
|
||||
C4NetpuncherID_t getNetpuncherGameID() const { return NetpuncherGameID; }
|
||||
C4NetpuncherID::value& getNetpuncherGameID(C4NetIO::HostAddress::AddressFamily family);
|
||||
C4NetpuncherID getNetpuncherGameID() const { return NetpuncherGameID; };
|
||||
StdStrBuf getNetpuncherAddr() const { return NetpuncherAddr; }
|
||||
|
||||
protected:
|
||||
|
|
|
@ -25,16 +25,6 @@
|
|||
#include "game/C4Game.h"
|
||||
#include "player/C4PlayerList.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <iphlpapi.h>
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <net/if.h>
|
||||
#endif
|
||||
|
||||
// *** C4Network2Client
|
||||
|
||||
C4Network2Client::C4Network2Client(C4Client *pClient)
|
||||
|
@ -147,10 +137,22 @@ bool C4Network2Client::DoConnectAttempt(C4Network2IO *pIO)
|
|||
{ iNextConnAttempt = time(nullptr) + 10; return true; }
|
||||
// save attempt
|
||||
AddrAttempts[iBestAddress]++; iNextConnAttempt = time(nullptr) + C4NetClientConnectInterval;
|
||||
// log
|
||||
LogSilentF("Network: connecting client %s on %s...", getName(), Addr[iBestAddress].toString().getData());
|
||||
// connect
|
||||
return pIO->Connect(Addr[iBestAddress].getAddr(), Addr[iBestAddress].getProtocol(), pClient->getCore());
|
||||
auto addr = Addr[iBestAddress].getAddr();
|
||||
std::set<int> interfaceIDs;
|
||||
if (addr.IsLocal())
|
||||
interfaceIDs = Network.Clients.GetLocal()->getInterfaceIDs();
|
||||
else
|
||||
interfaceIDs = {0};
|
||||
for (auto id : interfaceIDs)
|
||||
{
|
||||
addr.SetScopeId(id);
|
||||
// log
|
||||
LogSilentF("Network: connecting client %s on %s...", getName(), addr.ToString().getData());
|
||||
// connect
|
||||
if (pIO->Connect(addr, Addr[iBestAddress].getProtocol(), pClient->getCore()))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool C4Network2Client::hasAddr(const C4Network2Address &addr) const
|
||||
|
@ -187,80 +189,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);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
freeifaddrs(addrs);
|
||||
#endif
|
||||
}
|
||||
|
||||
void C4Network2Client::SendAddresses(C4Network2IOConnection *pConn)
|
||||
|
|
|
@ -57,6 +57,9 @@ protected:
|
|||
int32_t AddrAttempts[C4ClientMaxAddr];
|
||||
int32_t iAddrCnt;
|
||||
|
||||
// interface ids
|
||||
std::set<int> InterfaceIDs;
|
||||
|
||||
// status
|
||||
C4Network2ClientStatus eStatus;
|
||||
|
||||
|
@ -88,6 +91,8 @@ public:
|
|||
int32_t getAddrCnt() const { return iAddrCnt; }
|
||||
const C4Network2Address &getAddr(int32_t i) const { return Addr[i]; }
|
||||
|
||||
const std::set<int> &getInterfaceIDs() const { return InterfaceIDs; }
|
||||
|
||||
C4Network2ClientStatus getStatus() const { return eStatus; }
|
||||
bool hasJoinData() const { return getStatus() != NCS_Joining; }
|
||||
bool isChasing() const { return getStatus() == NCS_Chasing; }
|
||||
|
|
|
@ -17,6 +17,17 @@
|
|||
#include "network/C4Network2Discover.h"
|
||||
|
||||
// *** C4Network2IODiscover
|
||||
//
|
||||
// Quick multicast discovery guide by Luchs:
|
||||
//
|
||||
// All engines in network mode join a multicast group (defined by C4NetDiscoveryAddress).
|
||||
//
|
||||
// Engines searching for a game ("client") send a single byte c = 3 to that multicast group. This
|
||||
// happens while on the network list on each refresh.
|
||||
//
|
||||
// Engines hosting a game (when going into the lobby) send a byte c = 4 plus their reference server
|
||||
// port to the multicast group. Additionally, they listen for the c = 3 bytes and will reply with
|
||||
// another multicast answer.
|
||||
|
||||
struct C4Network2IODiscoverReply
|
||||
{
|
||||
|
@ -53,7 +64,7 @@ bool C4Network2IODiscover::Init(uint16_t iPort)
|
|||
bool C4Network2IODiscover::Announce()
|
||||
{
|
||||
// Announce our presence
|
||||
C4Network2IODiscoverReply Reply = { 4, htons(iRefServerPort) };
|
||||
C4Network2IODiscoverReply Reply = { 4, iRefServerPort };
|
||||
return Send(C4NetIOPacket(&Reply, sizeof(Reply), false, DiscoveryAddr));
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
const int C4NetMaxDiscover = 64;
|
||||
|
||||
const C4NetIO::HostAddress C4NetDiscoveryAddress = C4NetIO::HostAddress(0xef000000); // 239.0.0.0
|
||||
const C4NetIO::HostAddress C4NetDiscoveryAddress = C4NetIO::HostAddress(StdStrBuf("ff02::1"));
|
||||
|
||||
class C4Network2IODiscover : public C4NetIOSimpleUDP, private C4NetIO::CBClass
|
||||
{
|
||||
|
|
|
@ -460,28 +460,49 @@ bool C4Network2IO::InitPuncher(C4NetIO::addr_t nPuncherAddr)
|
|||
if (!pNetIO_UDP)
|
||||
return false;
|
||||
// save address
|
||||
PuncherAddr = nPuncherAddr;
|
||||
switch (nPuncherAddr.GetFamily())
|
||||
{
|
||||
case C4NetIO::HostAddress::IPv4:
|
||||
PuncherAddrIPv4 = nPuncherAddr;
|
||||
break;
|
||||
case C4NetIO::HostAddress::IPv6:
|
||||
PuncherAddrIPv6 = nPuncherAddr;
|
||||
break;
|
||||
case C4NetIO::HostAddress::UnknownFamily:
|
||||
assert(!"Unexpected address family");
|
||||
}
|
||||
// let's punch
|
||||
return pNetIO_UDP->Connect(PuncherAddr);
|
||||
return pNetIO_UDP->Connect(nPuncherAddr);
|
||||
}
|
||||
|
||||
void C4Network2IO::Punch(const C4NetIO::addr_t &punchee_addr) {
|
||||
void C4Network2IO::Punch(const C4NetIO::addr_t &punchee_addr)
|
||||
{
|
||||
if (!pNetIO_UDP)
|
||||
return;
|
||||
C4PacketPing PktPeng;
|
||||
dynamic_cast<C4NetIOUDP*>(pNetIO_UDP)->SendDirect(MkC4NetIOPacket(PID_Pong, PktPeng, punchee_addr));
|
||||
}
|
||||
|
||||
void C4Network2IO::SendPuncherPacket(const C4NetpuncherPacket& p) {
|
||||
if (!pNetIO_UDP || PuncherAddr.IsNull()) return;
|
||||
pNetIO_UDP->Send(p.PackTo(PuncherAddr));
|
||||
void C4Network2IO::SendPuncherPacket(const C4NetpuncherPacket& p, C4NetIO::HostAddress::AddressFamily family)
|
||||
{
|
||||
if (!pNetIO_UDP) return;
|
||||
if (family == C4NetIO::HostAddress::IPv4 && !PuncherAddrIPv4.IsNull())
|
||||
pNetIO_UDP->Send(p.PackTo(PuncherAddrIPv4));
|
||||
else if (family == C4NetIO::HostAddress::IPv6 && !PuncherAddrIPv6.IsNull())
|
||||
pNetIO_UDP->Send(p.PackTo(PuncherAddrIPv6));
|
||||
}
|
||||
|
||||
bool C4Network2IO::IsPuncherAddr(const C4NetIO::addr_t& addr) const
|
||||
{
|
||||
return (!PuncherAddrIPv4.IsNull() && PuncherAddrIPv4 == addr)
|
||||
|| (!PuncherAddrIPv6.IsNull() && PuncherAddrIPv6 == addr);
|
||||
}
|
||||
|
||||
// C4NetIO interface
|
||||
bool C4Network2IO::OnConn(const C4NetIO::addr_t &PeerAddr, const C4NetIO::addr_t &ConnectAddr, const C4NetIO::addr_t *pOwnAddr, C4NetIO *pNetIO)
|
||||
{
|
||||
// puncher answer?
|
||||
if (pNetIO == pNetIO_UDP && !PuncherAddr.IsNull() && PuncherAddr == ConnectAddr)
|
||||
if (pNetIO == pNetIO_UDP && IsPuncherAddr(ConnectAddr))
|
||||
{
|
||||
// got an address?
|
||||
if (pOwnAddr)
|
||||
|
@ -537,9 +558,12 @@ bool C4Network2IO::OnConn(const C4NetIO::addr_t &PeerAddr, const C4NetIO::addr_t
|
|||
void C4Network2IO::OnDisconn(const C4NetIO::addr_t &addr, C4NetIO *pNetIO, const char *szReason)
|
||||
{
|
||||
// punch?
|
||||
if (pNetIO == pNetIO_UDP && !PuncherAddr.IsNull() && PuncherAddr == addr)
|
||||
if (pNetIO == pNetIO_UDP && IsPuncherAddr(addr))
|
||||
{
|
||||
PuncherAddr.Clear();
|
||||
if (PuncherAddrIPv4 == addr)
|
||||
PuncherAddrIPv4.Clear();
|
||||
else
|
||||
PuncherAddrIPv6.Clear();
|
||||
return;
|
||||
}
|
||||
#if(C4NET2IO_DUMP_LEVEL > 1)
|
||||
|
@ -579,7 +603,7 @@ void C4Network2IO::OnPacket(const class C4NetIOPacket &rPacket, C4NetIO *pNetIO)
|
|||
C4TimeMilliseconds::Now().AsString().getData(),
|
||||
rPacket.getStatus(), getNetIOName(pNetIO));
|
||||
#endif
|
||||
if (pNetIO == pNetIO_UDP && !PuncherAddr.IsNull() && PuncherAddr == rPacket.getAddr())
|
||||
if (pNetIO == pNetIO_UDP && IsPuncherAddr(rPacket.getAddr()))
|
||||
{
|
||||
HandlePuncherPacket(rPacket);
|
||||
return;
|
||||
|
@ -1141,7 +1165,7 @@ void C4Network2IO::HandleFwdReq(const C4PacketFwd &rFwd, C4Network2IOConnection
|
|||
void C4Network2IO::HandlePuncherPacket(const C4NetIOPacket& rPacket)
|
||||
{
|
||||
auto pkt = C4NetpuncherPacket::Construct(rPacket);
|
||||
if (pkt && ::Network.HandlePuncherPacket(move(pkt)));
|
||||
if (pkt && ::Network.HandlePuncherPacket(move(pkt), rPacket.getAddr().GetFamily()));
|
||||
else
|
||||
{
|
||||
assert(pNetIO_UDP);
|
||||
|
@ -1268,15 +1292,23 @@ void C4Network2IO::SendConnPackets()
|
|||
|
||||
void C4Network2IO::OnPuncherConnect(C4NetIO::addr_t addr)
|
||||
{
|
||||
// Sanity check
|
||||
if (addr.GetFamily() != C4NetIO::HostAddress::IPv4)
|
||||
return;
|
||||
Application.InteractiveThread.ThreadLogS("Adding address from puncher: %s", addr.ToString().getData());
|
||||
// NAT punching is only relevant for IPv4, so convert here to show a proper address.
|
||||
auto maybe_v4 = addr.AsIPv4();
|
||||
Application.InteractiveThread.ThreadLogS("Adding address from puncher: %s", maybe_v4.ToString().getData());
|
||||
// Add for local client
|
||||
C4Network2Client *pLocal = ::Network.Clients.GetLocal();
|
||||
if (pLocal)
|
||||
pLocal->AddAddr(C4Network2Address(addr, P_UDP), true);
|
||||
{
|
||||
pLocal->AddAddr(C4Network2Address(maybe_v4, P_UDP), true);
|
||||
// If the outside port matches the inside port, there is no port translation and the
|
||||
// TCP address will probably work as well.
|
||||
if (addr.GetPort() == Config.Network.PortUDP && Config.Network.PortTCP > 0)
|
||||
{
|
||||
maybe_v4.SetPort(Config.Network.PortTCP);
|
||||
pLocal->AddAddr(C4Network2Address(maybe_v4, P_TCP), true);
|
||||
}
|
||||
// Do not ::Network.InvalidateReference(); yet, we're expecting an ID from the netpuncher
|
||||
}
|
||||
}
|
||||
|
||||
// *** C4Network2IOConnection
|
||||
|
|
|
@ -97,7 +97,8 @@ protected:
|
|||
iUDPIRate, iUDPORate, iUDPBCRate;
|
||||
|
||||
// punching
|
||||
C4NetIO::addr_t PuncherAddr;
|
||||
C4NetIO::addr_t PuncherAddrIPv4, PuncherAddrIPv6;
|
||||
bool IsPuncherAddr(const C4NetIO::addr_t& addr) const;
|
||||
|
||||
public:
|
||||
|
||||
|
@ -137,7 +138,7 @@ public:
|
|||
|
||||
// punch
|
||||
bool InitPuncher(C4NetIO::addr_t PuncherAddr); // by main thread
|
||||
void SendPuncherPacket(const C4NetpuncherPacket&);
|
||||
void SendPuncherPacket(const C4NetpuncherPacket&, C4NetIO::HostAddress::AddressFamily family);
|
||||
void Punch(const C4NetIO::addr_t&); // sends a ping packet
|
||||
|
||||
// stuff
|
||||
|
|
|
@ -318,8 +318,10 @@ bool C4Network2IRCClient::Connect(const char *szServer, const char *szNick, cons
|
|||
if (!Init())
|
||||
return false;
|
||||
// Resolve address
|
||||
if (!ResolveAddress(szServer, &ServerAddr, 6666))
|
||||
ServerAddr.SetAddress(StdStrBuf(szServer));
|
||||
if (ServerAddr.IsNull())
|
||||
{ SetError("Could no resolve server address!"); return false; }
|
||||
ServerAddr.SetDefaultPort(6666);
|
||||
// Set connection data
|
||||
Nick = szNick; RealName = szRealName;
|
||||
Password = szPassword; AutoJoin = szAutoJoin;
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
C4Network2Reference::C4Network2Reference()
|
||||
: Icon(0), GameMode(), Time(0), Frame(0), StartTime(0), LeaguePerformance(0),
|
||||
JoinAllowed(true), ObservingAllowed(true), PasswordNeeded(false), OfficialServer(false),
|
||||
IsEditor(false), iAddrCnt(0), NetpuncherGameID(0)
|
||||
IsEditor(false), iAddrCnt(0), NetpuncherGameID(C4NetpuncherID())
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -127,7 +127,7 @@ void C4Network2Reference::CompileFunc(StdCompiler *pComp)
|
|||
pComp->Value(mkNamingAdapt(Game.sEngineName, "Game", "None"));
|
||||
pComp->Value(mkNamingAdapt(mkArrayAdaptDM(Game.iVer,0),"Version" ));
|
||||
pComp->Value(mkNamingAdapt(OfficialServer, "OfficialServer", false));
|
||||
pComp->Value(mkNamingAdapt(NetpuncherGameID, "NetpuncherID", 0, false, false));
|
||||
pComp->Value(mkNamingAdapt(NetpuncherGameID, "NetpuncherID", C4NetpuncherID(), false, false));
|
||||
pComp->Value(mkNamingAdapt(NetpuncherAddr, "NetpuncherAddr", "", false, false));
|
||||
|
||||
pComp->Value(Parameters);
|
||||
|
@ -407,7 +407,7 @@ bool C4Network2HTTPClient::Decompress(StdBuf *pData)
|
|||
bool C4Network2HTTPClient::OnConn(const C4NetIO::addr_t &AddrPeer, const C4NetIO::addr_t &AddrConnect, const C4NetIO::addr_t *pOwnAddr, C4NetIO *pNetIO)
|
||||
{
|
||||
// Make sure we're actually waiting for this connection
|
||||
if (AddrConnect != ServerAddr)
|
||||
if (fConnected || (AddrConnect != ServerAddr && AddrConnect != ServerAddrFallback))
|
||||
return false;
|
||||
// Save pack peer address
|
||||
PeerAddr = AddrPeer;
|
||||
|
@ -445,10 +445,19 @@ void C4Network2HTTPClient::OnPacket(const class C4NetIOPacket &rPacket, C4NetIO
|
|||
bool C4Network2HTTPClient::Execute(int iMaxTime)
|
||||
{
|
||||
// Check timeout
|
||||
if (fBusy && time(nullptr) > iRequestTimeout)
|
||||
if (fBusy)
|
||||
{
|
||||
Cancel("Request timeout");
|
||||
return true;
|
||||
if (C4TimeMilliseconds::Now() > HappyEyeballsTimeout)
|
||||
{
|
||||
HappyEyeballsTimeout = C4TimeMilliseconds::PositiveInfinity;
|
||||
Application.InteractiveThread.ThreadLogS("HTTP: Starting fallback connection to %s (%s)", Server.getData(), ServerAddrFallback.ToString().getData());
|
||||
Connect(ServerAddrFallback);
|
||||
}
|
||||
if (time(nullptr) > iRequestTimeout)
|
||||
{
|
||||
Cancel("Request timeout");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Execute normally
|
||||
return C4NetIOTCP::Execute(iMaxTime);
|
||||
|
@ -462,7 +471,9 @@ C4TimeMilliseconds C4Network2HTTPClient::GetNextTick(C4TimeMilliseconds tNow)
|
|||
|
||||
C4TimeMilliseconds tHTTPClientTick = tNow + 1000 * std::max<time_t>(iRequestTimeout - time(nullptr), 0);
|
||||
|
||||
return std::max(tNetIOTCPTick, tHTTPClientTick);
|
||||
C4TimeMilliseconds HappyEyeballsTick = tNow + std::max(HappyEyeballsTimeout - C4TimeMilliseconds::Now(), 0);
|
||||
|
||||
return std::min({tNetIOTCPTick, tHTTPClientTick, HappyEyeballsTick});
|
||||
}
|
||||
|
||||
bool C4Network2HTTPClient::Query(const StdBuf &Data, bool fBinary)
|
||||
|
@ -512,6 +523,11 @@ bool C4Network2HTTPClient::Query(const StdBuf &Data, bool fBinary)
|
|||
// Start connecting
|
||||
if (!Connect(ServerAddr))
|
||||
return false;
|
||||
// Also try the fallback address after some time (if there is one)
|
||||
if (!ServerAddrFallback.IsNull())
|
||||
HappyEyeballsTimeout = C4TimeMilliseconds::Now() + C4Network2HTTPHappyEyeballsTimeout;
|
||||
else
|
||||
HappyEyeballsTimeout = C4TimeMilliseconds::PositiveInfinity;
|
||||
// Okay, request will be performed when connection is complete
|
||||
fBusy = true;
|
||||
iDataOffset = 0;
|
||||
|
@ -529,7 +545,7 @@ void C4Network2HTTPClient::ResetRequestTimeout()
|
|||
void C4Network2HTTPClient::Cancel(const char *szReason)
|
||||
{
|
||||
// Close connection - and connection attempt
|
||||
Close(ServerAddr); Close(PeerAddr);
|
||||
Close(ServerAddr); Close(ServerAddrFallback); Close(PeerAddr);
|
||||
// Reset flags
|
||||
fBusy = fSuccess = fConnected = fBinary = false;
|
||||
iDownloadedSize = iTotalSize = iDataOffset = 0;
|
||||
|
@ -566,14 +582,23 @@ bool C4Network2HTTPClient::SetServer(const char *szServerAddress)
|
|||
SetError(FormatString("Could not resolve server address %s!", Server.getData()).getData());
|
||||
return false;
|
||||
}
|
||||
if (ServerAddr.GetPort() == C4NetIO::EndpointAddress::IPPORT_NONE)
|
||||
ServerAddr.SetDefaultPort(GetDefaultPort());
|
||||
if (ServerAddr.GetFamily() == C4NetIO::HostAddress::IPv6)
|
||||
{
|
||||
ServerAddr.SetPort(GetDefaultPort());
|
||||
// Try to find a fallback IPv4 address for Happy Eyeballs.
|
||||
ServerAddrFallback.SetAddress(Server, C4NetIO::HostAddress::IPv4);
|
||||
ServerAddrFallback.SetDefaultPort(GetDefaultPort());
|
||||
}
|
||||
else
|
||||
ServerAddrFallback.Clear();
|
||||
// Remove port
|
||||
const char *pColon = strchr(Server.getData(), ':');
|
||||
if (pColon)
|
||||
Server.SetLength(pColon - Server.getData());
|
||||
const char *firstColon = strchr(Server.getData(), ':');
|
||||
const char *lastColon = strrchr(Server.getData(), ':');
|
||||
if (firstColon)
|
||||
// hostname/IPv4 address or IPv6 address with port (e.g. [::1]:1234)
|
||||
if (firstColon == lastColon || (Server[0] == '[' && *(lastColon - 1) == ']'))
|
||||
Server.SetLength(lastColon - Server.getData());
|
||||
|
||||
// Done
|
||||
ResetError();
|
||||
return true;
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "lib/C4InputValidation.h"
|
||||
|
||||
const int C4Network2HTTPQueryTimeout = 10; // (s)
|
||||
const uint32_t C4Network2HTTPHappyEyeballsTimeout = 300; // (ms)
|
||||
|
||||
// Session data
|
||||
class C4Network2Reference
|
||||
|
@ -51,7 +52,7 @@ private:
|
|||
bool PasswordNeeded;
|
||||
bool OfficialServer;
|
||||
bool IsEditor;
|
||||
C4NetpuncherID_t NetpuncherGameID;
|
||||
C4NetpuncherID NetpuncherGameID;
|
||||
StdCopyStrBuf NetpuncherAddr;
|
||||
|
||||
// Engine information
|
||||
|
@ -79,7 +80,7 @@ public:
|
|||
int32_t getStartTime() const { return StartTime; }
|
||||
StdStrBuf getGameGoalString() const;
|
||||
bool isEditor() const { return IsEditor; }
|
||||
C4NetpuncherID_t getNetpuncherGameID() const { return NetpuncherGameID; }
|
||||
C4NetpuncherID getNetpuncherGameID() const { return NetpuncherGameID; }
|
||||
StdStrBuf getNetpuncherAddr() const { return NetpuncherAddr; }
|
||||
|
||||
void SetSourceAddress(const C4NetIO::EndpointAddress &ip);
|
||||
|
@ -129,7 +130,7 @@ public:
|
|||
private:
|
||||
|
||||
// Address information
|
||||
C4NetIO::addr_t ServerAddr, PeerAddr;
|
||||
C4NetIO::addr_t ServerAddr, ServerAddrFallback, PeerAddr;
|
||||
StdCopyStrBuf Server, RequestPath;
|
||||
|
||||
bool fBinary;
|
||||
|
@ -137,6 +138,7 @@ private:
|
|||
size_t iDataOffset;
|
||||
StdCopyBuf Request;
|
||||
time_t iRequestTimeout;
|
||||
C4TimeMilliseconds HappyEyeballsTimeout;
|
||||
|
||||
// Response header data
|
||||
size_t iDownloadedSize, iTotalSize;
|
||||
|
|
|
@ -158,7 +158,8 @@ bool C4AulDebug::SetAllowed(const char *szHost)
|
|||
// No host?
|
||||
if (!szHost || !*szHost) return true;
|
||||
// Resolve the address
|
||||
return ResolveAddress(szHost, &AllowedAddr, 0);
|
||||
AllowedAddr.SetAddress(StdStrBuf(szHost));
|
||||
return !AllowedAddr.IsNull();
|
||||
}
|
||||
|
||||
bool C4AulDebug::Init(uint16_t iPort)
|
||||
|
|
Loading…
Reference in New Issue