Allow getting public IPV6 address with STUN for server
This commit is contained in:
parent
0c5166ac39
commit
3f0db672d6
@ -1867,7 +1867,13 @@ bool ServerLobby::registerServer(bool now)
|
|||||||
|
|
||||||
Log::info("ServerLobby", "Public server address %s",
|
Log::info("ServerLobby", "Public server address %s",
|
||||||
m_server_address.toString().c_str());
|
m_server_address.toString().c_str());
|
||||||
|
if (!STKHost::get()->getPublicIPV6Address().empty())
|
||||||
|
{
|
||||||
|
request->addParameter("address_ipv6",
|
||||||
|
STKHost::get()->getPublicIPV6Address());
|
||||||
|
Log::info("ServerLobby", "Public IPV6 server address %s",
|
||||||
|
STKHost::get()->getPublicIPV6Address().c_str());
|
||||||
|
}
|
||||||
if (now)
|
if (now)
|
||||||
{
|
{
|
||||||
request->executeNow();
|
request->executeNow();
|
||||||
|
@ -365,14 +365,335 @@ void STKHost::shutdown()
|
|||||||
destroy();
|
destroy();
|
||||||
} // shutdown
|
} // shutdown
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
std::string STKHost::getIPFromStun(int socket, const std::string& stun_address,
|
||||||
|
bool ipv4)
|
||||||
|
{
|
||||||
|
std::vector<std::string> addr_and_port =
|
||||||
|
StringUtils::split(stun_address, ':');
|
||||||
|
if (addr_and_port.size() != 2)
|
||||||
|
{
|
||||||
|
Log::error("STKHost", "Wrong server address and port");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
struct addrinfo hints;
|
||||||
|
struct addrinfo* res = NULL;
|
||||||
|
|
||||||
|
memset(&hints, 0, sizeof hints);
|
||||||
|
hints.ai_family = ipv4 ? AF_INET : AF_INET6;
|
||||||
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
|
||||||
|
// Resolve the stun server name so we can send it a STUN request
|
||||||
|
int status = getaddrinfo_compat(addr_and_port[0].c_str(),
|
||||||
|
addr_and_port[1].c_str(), &hints, &res);
|
||||||
|
if (status != 0)
|
||||||
|
{
|
||||||
|
Log::error("STKHost", "Error in getaddrinfo for stun server"
|
||||||
|
" %s: %s", addr_and_port[0].c_str(), gai_strerror(status));
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// We specify ai_family, so only ipv4 or ipv6 is found
|
||||||
|
if (res == NULL)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
struct sockaddr* stun_addr = NULL;
|
||||||
|
if (isIPV6())
|
||||||
|
{
|
||||||
|
if (ipv4)
|
||||||
|
{
|
||||||
|
struct sockaddr_in* ipv4_addr = (struct sockaddr_in*)(res->ai_addr);
|
||||||
|
m_stun_address.setIP(ntohl(ipv4_addr->sin_addr.s_addr));
|
||||||
|
m_stun_address.setPort(ntohs(ipv4_addr->sin_port));
|
||||||
|
// Change address to ::ffff:ipv4 format for ipv6 socket
|
||||||
|
std::string ipv4_mapped = "::ffff:";
|
||||||
|
ipv4_mapped += m_stun_address.toString(false/*show_port*/);
|
||||||
|
freeaddrinfo(res);
|
||||||
|
res = NULL;
|
||||||
|
hints.ai_family = AF_INET6;
|
||||||
|
status = getaddrinfo_compat(ipv4_mapped.c_str(),
|
||||||
|
addr_and_port[1].c_str(), &hints, &res);
|
||||||
|
if (status != 0 || res == NULL)
|
||||||
|
{
|
||||||
|
Log::error("STKHost", "Error in getaddrinfo for stun server"
|
||||||
|
" %s: %s", addr_and_port[0].c_str(), gai_strerror(status));
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
stun_addr = res->ai_addr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stun_addr = res->ai_addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ipv4)
|
||||||
|
{
|
||||||
|
stun_addr = res->ai_addr;
|
||||||
|
struct sockaddr_in* ipv4_addr = (struct sockaddr_in*)(res->ai_addr);
|
||||||
|
m_stun_address.setIP(ntohl(ipv4_addr->sin_addr.s_addr));
|
||||||
|
m_stun_address.setPort(ntohs(ipv4_addr->sin_port));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assemble the message for the stun server
|
||||||
|
BareNetworkString s(20);
|
||||||
|
|
||||||
|
constexpr uint32_t magic_cookie = 0x2112A442;
|
||||||
|
// bytes 0-1: the type of the message
|
||||||
|
// bytes 2-3: message length added to header (attributes)
|
||||||
|
uint16_t message_type = 0x0001; // binding request
|
||||||
|
uint16_t message_length = 0x0000;
|
||||||
|
s.addUInt16(message_type).addUInt16(message_length)
|
||||||
|
.addUInt32(magic_cookie);
|
||||||
|
|
||||||
|
uint8_t stun_tansaction_id[16];
|
||||||
|
stun_tansaction_id[0] = 0x21;
|
||||||
|
stun_tansaction_id[1] = 0x12;
|
||||||
|
stun_tansaction_id[2] = 0xA4;
|
||||||
|
stun_tansaction_id[3] = 0x42;
|
||||||
|
// bytes 8-19: the transaction id
|
||||||
|
for (int i = 0; i < 12; i++)
|
||||||
|
{
|
||||||
|
uint8_t random_byte = rand() % 256;
|
||||||
|
s.addUInt8(random_byte);
|
||||||
|
stun_tansaction_id[i + 4] = random_byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
sendto(socket, s.getData(), s.size(), 0, stun_addr, isIPV6() ?
|
||||||
|
sizeof(sockaddr_in6) : sizeof(sockaddr_in));
|
||||||
|
|
||||||
|
// Recieve now
|
||||||
|
TransportAddress sender;
|
||||||
|
const int LEN = 2048;
|
||||||
|
char buffer[LEN];
|
||||||
|
|
||||||
|
struct sockaddr_in addr4_rev;
|
||||||
|
struct sockaddr_in6 addr6_rev;
|
||||||
|
struct sockaddr* addr_rev = isIPV6() ?
|
||||||
|
(struct sockaddr*)(&addr6_rev) : (struct sockaddr*)(&addr4_rev);
|
||||||
|
socklen_t from_len = isIPV6() ? sizeof(addr6_rev) : sizeof(addr4_rev);
|
||||||
|
int len = -1;
|
||||||
|
int count = 0;
|
||||||
|
while (len < 0 && count < 2000)
|
||||||
|
{
|
||||||
|
len = recvfrom(socket, buffer, LEN, 0, addr_rev,
|
||||||
|
&from_len);
|
||||||
|
if (len > 0)
|
||||||
|
break;
|
||||||
|
count++;
|
||||||
|
StkTime::sleep(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len <= 0)
|
||||||
|
{
|
||||||
|
Log::error("STKHost", "STUN response contains no data at all");
|
||||||
|
freeaddrinfo(res);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isIPV6())
|
||||||
|
{
|
||||||
|
if (!sameIPV6((sockaddr_in6*)stun_addr, &addr6_rev))
|
||||||
|
{
|
||||||
|
Log::warn("STKHost",
|
||||||
|
"Received stun response from %s instead of %s.",
|
||||||
|
getIPV6ReadableFromIn6((sockaddr_in6*)stun_addr).c_str(),
|
||||||
|
getIPV6ReadableFromIn6(&addr6_rev).c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TransportAddress sender;
|
||||||
|
sender.setIP(ntohl(addr4_rev.sin_addr.s_addr));
|
||||||
|
sender.setPort(ntohs(addr4_rev.sin_port));
|
||||||
|
if (sender != m_stun_address)
|
||||||
|
{
|
||||||
|
Log::warn("STKHost",
|
||||||
|
"Received stun response from %s instead of %s.",
|
||||||
|
sender.toString().c_str(), m_stun_address.toString().c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
freeaddrinfo(res);
|
||||||
|
|
||||||
|
// Convert to network string.
|
||||||
|
BareNetworkString response(buffer, len);
|
||||||
|
if (response.size() < 20)
|
||||||
|
{
|
||||||
|
Log::error("STKHost", "STUN response should be at least 20 bytes.");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.getUInt16() != 0x0101)
|
||||||
|
{
|
||||||
|
Log::error("STKHost", "STUN has no binding success response.");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip message size
|
||||||
|
response.getUInt16();
|
||||||
|
|
||||||
|
if (response.getUInt32() != magic_cookie)
|
||||||
|
{
|
||||||
|
Log::error("STKHost", "STUN response doesn't contain the magic "
|
||||||
|
"cookie");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 12; i++)
|
||||||
|
{
|
||||||
|
if (response.getUInt8() != stun_tansaction_id[i + 4])
|
||||||
|
{
|
||||||
|
Log::error("STKHost", "STUN response doesn't contain the "
|
||||||
|
"transaction ID");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The stun message is valid, so we parse it now:
|
||||||
|
// Those are the port and the address to be detected
|
||||||
|
TransportAddress addr, non_xor_addr, xor_addr;
|
||||||
|
std::string ipv6_addr_non_xor, ipv6_addr_xor;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (response.size() < 4)
|
||||||
|
break;
|
||||||
|
|
||||||
|
unsigned type = response.getUInt16();
|
||||||
|
unsigned size = response.getUInt16();
|
||||||
|
|
||||||
|
// Bit determining whether comprehension of an attribute is optional.
|
||||||
|
// Described in section 15 of RFC 5389.
|
||||||
|
constexpr uint16_t comprehension_optional = 0x1 << 15;
|
||||||
|
|
||||||
|
// Bit determining whether the bit was assigned by IETF Review.
|
||||||
|
// Described in section 18.1. of RFC 5389.
|
||||||
|
constexpr uint16_t IETF_review = 0x1 << 14;
|
||||||
|
|
||||||
|
// Defined in section 15.1 of RFC 5389
|
||||||
|
constexpr uint8_t ipv4_returned = 0x01;
|
||||||
|
constexpr uint8_t ipv6_returned = 0x02;
|
||||||
|
|
||||||
|
// Defined in section 18.2 of RFC 5389
|
||||||
|
constexpr uint16_t mapped_address = 0x001;
|
||||||
|
constexpr uint16_t xor_mapped_address = 0x0020;
|
||||||
|
// The first two bits are irrelevant to the type
|
||||||
|
type &= ~(comprehension_optional | IETF_review);
|
||||||
|
if (type == mapped_address || type == xor_mapped_address)
|
||||||
|
{
|
||||||
|
if (response.size() < 2)
|
||||||
|
{
|
||||||
|
Log::error("STKHost", "Invalid STUN mapped address length.");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
// Ignore the first byte as mentioned in Section 15.1 of RFC 5389.
|
||||||
|
uint8_t ip_type = response.getUInt8();
|
||||||
|
ip_type = response.getUInt8();
|
||||||
|
if (ip_type == ipv4_returned)
|
||||||
|
{
|
||||||
|
// Above got 2 bytes
|
||||||
|
if (size != 8 || response.size() < 6)
|
||||||
|
{
|
||||||
|
Log::error("STKHost", "Invalid STUN mapped address length.");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
uint16_t port = response.getUInt16();
|
||||||
|
uint32_t ip = response.getUInt32();
|
||||||
|
if (type == xor_mapped_address)
|
||||||
|
{
|
||||||
|
// Obfuscation is described in Section 15.2 of RFC 5389.
|
||||||
|
port ^= magic_cookie >> 16;
|
||||||
|
ip ^= magic_cookie;
|
||||||
|
xor_addr.setPort(port);
|
||||||
|
xor_addr.setIP(ip);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
non_xor_addr.setPort(port);
|
||||||
|
non_xor_addr.setIP(ip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ip_type == ipv6_returned)
|
||||||
|
{
|
||||||
|
// Above got 2 bytes
|
||||||
|
if (size != 20 || response.size() < 18)
|
||||||
|
{
|
||||||
|
Log::error("STKHost", "Invalid STUN mapped address length.");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
uint16_t port = response.getUInt16();
|
||||||
|
struct sockaddr_in6 ipv6_addr = {};
|
||||||
|
uint8_t bytes[16];
|
||||||
|
for (int i = 0; i < 16; i++)
|
||||||
|
bytes[i] = response.getUInt8();
|
||||||
|
if (type == xor_mapped_address)
|
||||||
|
{
|
||||||
|
port ^= magic_cookie >> 16;
|
||||||
|
for (int i = 0; i < 16; i++)
|
||||||
|
bytes[i] ^= stun_tansaction_id[i];
|
||||||
|
memcpy(&(ipv6_addr.sin6_addr), &bytes[0], 16);
|
||||||
|
ipv6_addr_xor = getIPV6ReadableFromIn6(&ipv6_addr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memcpy(&(ipv6_addr.sin6_addr), &bytes[0], 16);
|
||||||
|
ipv6_addr_non_xor = getIPV6ReadableFromIn6(&ipv6_addr);
|
||||||
|
}
|
||||||
|
if (port != m_public_address.getPort())
|
||||||
|
{
|
||||||
|
Log::error("STKHost", "IPV6 has different port than IPV4.");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // type == mapped_address || type == xor_mapped_address
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response.skip(size);
|
||||||
|
int padding = size % 4;
|
||||||
|
if (padding != 0)
|
||||||
|
response.skip(4 - padding);
|
||||||
|
}
|
||||||
|
} // while true
|
||||||
|
// Found public address and port
|
||||||
|
if (ipv4)
|
||||||
|
{
|
||||||
|
if (!xor_addr.isUnset() || !non_xor_addr.isUnset())
|
||||||
|
{
|
||||||
|
// Use XOR mapped address when possible to avoid translation of
|
||||||
|
// the packet content by application layer gateways (ALGs) that
|
||||||
|
// perform deep packet inspection in an attempt to perform
|
||||||
|
// alternate NAT traversal methods.
|
||||||
|
if (!xor_addr.isUnset())
|
||||||
|
{
|
||||||
|
addr = xor_addr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log::warn("STKHost", "Only non xor-mapped address returned.");
|
||||||
|
addr = non_xor_addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return addr.toString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (ipv6_addr_xor.empty() && ipv6_addr_non_xor.empty())
|
||||||
|
return "";
|
||||||
|
if (ipv6_addr_xor.empty())
|
||||||
|
{
|
||||||
|
Log::warn("STKHost", "Only non xor-mapped address returned.");
|
||||||
|
return ipv6_addr_non_xor;
|
||||||
|
}
|
||||||
|
return ipv6_addr_xor;
|
||||||
|
}
|
||||||
|
} // getIPFromStun
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
/** Set the public address using stun protocol.
|
/** Set the public address using stun protocol.
|
||||||
*/
|
*/
|
||||||
void STKHost::setPublicAddress()
|
void STKHost::setPublicAddress()
|
||||||
{
|
{
|
||||||
if (isIPV6())
|
if (isIPV6() && NetworkConfig::get()->isClient())
|
||||||
{
|
{
|
||||||
// IPV6 only in iOS doesn't support connection to firewalled server,
|
// IPV6 client doesn't support connection to firewalled server,
|
||||||
// so no need to test STUN
|
// so no need to test STUN
|
||||||
Log::info("STKHost", "IPV6 only environment detected.");
|
Log::info("STKHost", "IPV6 only environment detected.");
|
||||||
m_public_address = TransportAddress("169.254.0.0:65535");
|
m_public_address = TransportAddress("169.254.0.0:65535");
|
||||||
@ -402,209 +723,31 @@ void STKHost::setPublicAddress()
|
|||||||
std::string server_name = untried_server.back().first.c_str();
|
std::string server_name = untried_server.back().first.c_str();
|
||||||
UserConfigParams::m_stun_servers[server_name] = (uint32_t)-1;
|
UserConfigParams::m_stun_servers[server_name] = (uint32_t)-1;
|
||||||
Log::debug("STKHost", "Using STUN server %s", server_name.c_str());
|
Log::debug("STKHost", "Using STUN server %s", server_name.c_str());
|
||||||
|
|
||||||
std::vector<std::string> addr_and_port =
|
|
||||||
StringUtils::split(server_name, ':');
|
|
||||||
uint16_t port = 0;
|
|
||||||
if (addr_and_port.size() != 2 ||
|
|
||||||
!StringUtils::fromString(addr_and_port[1], port))
|
|
||||||
{
|
|
||||||
Log::error("STKHost", "Wrong server address and port");
|
|
||||||
untried_server.pop_back();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
struct addrinfo hints, *res;
|
|
||||||
|
|
||||||
memset(&hints, 0, sizeof hints);
|
|
||||||
hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version
|
|
||||||
hints.ai_socktype = SOCK_STREAM;
|
|
||||||
|
|
||||||
// Resolve the stun server name so we can send it a STUN request
|
|
||||||
int status = getaddrinfo(addr_and_port[0].c_str(), NULL, &hints, &res);
|
|
||||||
if (status != 0)
|
|
||||||
{
|
|
||||||
Log::error("STKHost", "Error in getaddrinfo for stun server"
|
|
||||||
" %s: %s", addr_and_port[0].c_str(), gai_strerror(status));
|
|
||||||
untried_server.pop_back();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
untried_server.pop_back();
|
|
||||||
// documentation says it points to "one or more addrinfo structures"
|
|
||||||
assert(res != NULL);
|
|
||||||
struct sockaddr_in* current_interface = (struct sockaddr_in*)(res->ai_addr);
|
|
||||||
m_stun_address.setIP(ntohl(current_interface->sin_addr.s_addr));
|
|
||||||
m_stun_address.setPort(port);
|
|
||||||
|
|
||||||
// Assemble the message for the stun server
|
|
||||||
BareNetworkString s(20);
|
|
||||||
|
|
||||||
constexpr uint32_t magic_cookie = 0x2112A442;
|
|
||||||
// bytes 0-1: the type of the message
|
|
||||||
// bytes 2-3: message length added to header (attributes)
|
|
||||||
uint16_t message_type = 0x0001; // binding request
|
|
||||||
uint16_t message_length = 0x0000;
|
|
||||||
s.addUInt16(message_type).addUInt16(message_length)
|
|
||||||
.addUInt32(magic_cookie);
|
|
||||||
uint8_t stun_tansaction_id[12];
|
|
||||||
// bytes 8-19: the transaction id
|
|
||||||
for (int i = 0; i < 12; i++)
|
|
||||||
{
|
|
||||||
uint8_t random_byte = rand() % 256;
|
|
||||||
s.addUInt8(random_byte);
|
|
||||||
stun_tansaction_id[i] = random_byte;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_network->sendRawPacket(s, m_stun_address);
|
|
||||||
uint64_t ping = StkTime::getMonoTimeMs();
|
uint64_t ping = StkTime::getMonoTimeMs();
|
||||||
freeaddrinfo(res);
|
std::string ipv4_string = getIPFromStun(
|
||||||
|
m_network->getENetHost()->socket, server_name, true/*ipv4*/);
|
||||||
// Recieve now
|
TransportAddress addr(ipv4_string);
|
||||||
TransportAddress sender;
|
if (!addr.isUnset())
|
||||||
const int LEN = 2048;
|
{
|
||||||
char buffer[LEN];
|
m_public_address = addr;
|
||||||
int len = m_network->receiveRawPacket(buffer, LEN, &sender, 2000);
|
|
||||||
ping = StkTime::getMonoTimeMs() - ping;
|
ping = StkTime::getMonoTimeMs() - ping;
|
||||||
|
|
||||||
if (sender.getIP() != m_stun_address.getIP())
|
|
||||||
{
|
|
||||||
Log::warn("STKHost",
|
|
||||||
"Received stun response from %s instead of %s.",
|
|
||||||
sender.toString().c_str(), m_stun_address.toString().c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (len <= 0)
|
|
||||||
{
|
|
||||||
Log::error("STKHost", "STUN response contains no data at all");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert to network string.
|
|
||||||
BareNetworkString response(buffer, len);
|
|
||||||
if (response.size() < 20)
|
|
||||||
{
|
|
||||||
Log::error("STKHost", "STUN response should be at least 20 bytes.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.getUInt16() != 0x0101)
|
|
||||||
{
|
|
||||||
Log::error("STKHost", "STUN has no binding success response.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip message size
|
|
||||||
response.getUInt16();
|
|
||||||
|
|
||||||
if (response.getUInt32() != magic_cookie)
|
|
||||||
{
|
|
||||||
Log::error("STKHost", "STUN response doesn't contain the magic "
|
|
||||||
"cookie");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < 12; i++)
|
|
||||||
{
|
|
||||||
if (response.getUInt8() != stun_tansaction_id[i])
|
|
||||||
{
|
|
||||||
Log::error("STKHost", "STUN response doesn't contain the "
|
|
||||||
"transaction ID");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Log::debug("GetPublicAddress",
|
|
||||||
"The STUN server responded with a valid answer");
|
|
||||||
|
|
||||||
// The stun message is valid, so we parse it now:
|
|
||||||
// Those are the port and the address to be detected
|
|
||||||
TransportAddress non_xor_addr, xor_addr;
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
if (response.size() < 4)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
unsigned type = response.getUInt16();
|
|
||||||
unsigned size = response.getUInt16();
|
|
||||||
|
|
||||||
// Bit determining whether comprehension of an attribute is optional.
|
|
||||||
// Described in section 15 of RFC 5389.
|
|
||||||
constexpr uint16_t comprehension_optional = 0x1 << 15;
|
|
||||||
|
|
||||||
// Bit determining whether the bit was assigned by IETF Review.
|
|
||||||
// Described in section 18.1. of RFC 5389.
|
|
||||||
constexpr uint16_t IETF_review = 0x1 << 14;
|
|
||||||
|
|
||||||
// Defined in section 15.1 of RFC 5389
|
|
||||||
constexpr uint8_t ipv4 = 0x01;
|
|
||||||
|
|
||||||
// Defined in section 18.2 of RFC 5389
|
|
||||||
constexpr uint16_t mapped_address = 0x001;
|
|
||||||
constexpr uint16_t xor_mapped_address = 0x0020;
|
|
||||||
// The first two bits are irrelevant to the type
|
|
||||||
type &= ~(comprehension_optional | IETF_review);
|
|
||||||
if (type == mapped_address || type == xor_mapped_address)
|
|
||||||
{
|
|
||||||
if (size != 8 || response.size() < 8)
|
|
||||||
{
|
|
||||||
Log::error("STKHost", "Invalid STUN mapped address "
|
|
||||||
"length");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Ignore the first byte as mentioned in Section 15.1 of RFC
|
|
||||||
// 5389.
|
|
||||||
uint8_t ip_type = response.getUInt8();
|
|
||||||
ip_type = response.getUInt8();
|
|
||||||
if (ip_type != ipv4)
|
|
||||||
{
|
|
||||||
Log::error("STKHost", "Only IPv4 is supported");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t port = response.getUInt16();
|
|
||||||
uint32_t ip = response.getUInt32();
|
|
||||||
if (type == xor_mapped_address)
|
|
||||||
{
|
|
||||||
// Obfuscation is described in Section 15.2 of RFC 5389.
|
|
||||||
port ^= magic_cookie >> 16;
|
|
||||||
ip ^= magic_cookie;
|
|
||||||
xor_addr.setPort(port);
|
|
||||||
xor_addr.setIP(ip);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
non_xor_addr.setPort(port);
|
|
||||||
non_xor_addr.setIP(ip);
|
|
||||||
}
|
|
||||||
} // type == mapped_address || type == xor_mapped_address
|
|
||||||
else
|
|
||||||
{
|
|
||||||
response.skip(size);
|
|
||||||
int padding = size % 4;
|
|
||||||
if (padding != 0)
|
|
||||||
response.skip(4 - padding);
|
|
||||||
}
|
|
||||||
} // while true
|
|
||||||
// Found public address and port
|
|
||||||
if (!xor_addr.isUnset() || !non_xor_addr.isUnset())
|
|
||||||
{
|
|
||||||
// Use XOR mapped address when possible to avoid translation of
|
|
||||||
// the packet content by application layer gateways (ALGs) that
|
|
||||||
// perform deep packet inspection in an attempt to perform
|
|
||||||
// alternate NAT traversal methods.
|
|
||||||
if (!xor_addr.isUnset())
|
|
||||||
{
|
|
||||||
m_public_address = xor_addr;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log::warn("STKHost", "Only non xor-mapped address returned.");
|
|
||||||
m_public_address = non_xor_addr;
|
|
||||||
}
|
|
||||||
// Succeed, save ping
|
// Succeed, save ping
|
||||||
UserConfigParams::m_stun_servers[server_name] = (uint32_t)(ping);
|
UserConfigParams::m_stun_servers[server_name] = (uint32_t)(ping);
|
||||||
|
if (isIPV6())
|
||||||
|
{
|
||||||
|
m_public_ipv6_address = getIPFromStun(
|
||||||
|
m_network->getENetHost()->socket, server_name,
|
||||||
|
false/*ipv4*/);
|
||||||
|
if (m_public_ipv6_address.empty())
|
||||||
|
{
|
||||||
|
Log::warn("STKHost", "Failed to get public ipv6 address "
|
||||||
|
"for this host.");
|
||||||
|
}
|
||||||
|
}
|
||||||
untried_server.clear();
|
untried_server.clear();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
untried_server.pop_back();
|
||||||
}
|
}
|
||||||
} // setPublicAddress
|
} // setPublicAddress
|
||||||
|
|
||||||
|
@ -134,6 +134,9 @@ private:
|
|||||||
/** The public address found by stun (if WAN is used). */
|
/** The public address found by stun (if WAN is used). */
|
||||||
TransportAddress m_public_address;
|
TransportAddress m_public_address;
|
||||||
|
|
||||||
|
/** The public ipv6 address found by stun (if WAN is used). */
|
||||||
|
std::string m_public_ipv6_address;
|
||||||
|
|
||||||
/** The public address stun server used. */
|
/** The public address stun server used. */
|
||||||
TransportAddress m_stun_address;
|
TransportAddress m_stun_address;
|
||||||
|
|
||||||
@ -167,7 +170,9 @@ private:
|
|||||||
std::map<std::string, uint64_t>& ctp);
|
std::map<std::string, uint64_t>& ctp);
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void mainLoop();
|
void mainLoop();
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
std::string getIPFromStun(int socket, const std::string& stun_address,
|
||||||
|
bool ipv4);
|
||||||
public:
|
public:
|
||||||
/** If a network console should be started. */
|
/** If a network console should be started. */
|
||||||
static bool m_enable_console;
|
static bool m_enable_console;
|
||||||
@ -197,6 +202,9 @@ public:
|
|||||||
const TransportAddress& getPublicAddress() const
|
const TransportAddress& getPublicAddress() const
|
||||||
{ return m_public_address; }
|
{ return m_public_address; }
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
const std::string& getPublicIPV6Address() const
|
||||||
|
{ return m_public_ipv6_address; }
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
const TransportAddress& getStunAddress() const { return m_stun_address; }
|
const TransportAddress& getStunAddress() const { return m_stun_address; }
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
uint16_t getPrivatePort() const { return m_private_port; }
|
uint16_t getPrivatePort() const { return m_private_port; }
|
||||||
|
@ -16,8 +16,26 @@
|
|||||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
#include "ws2tcpip.h"
|
#ifdef __GNUC__
|
||||||
|
# include <ws2tcpip.h> // Mingw / gcc on windows
|
||||||
|
# undef _WIN32_WINNT
|
||||||
|
# define _WIN32_WINNT 0x501
|
||||||
|
# include <winsock2.h>
|
||||||
|
# include <ws2tcpip.h>
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
WINSOCK_API_LINKAGE PCSTR WSAAPI inet_ntop(INT Family, PVOID pAddr, PSTR pStringBuf, size_t StringBufSize);
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
# include <winsock2.h>
|
||||||
|
# include <in6addr.h>
|
||||||
|
# include <ws2tcpip.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <err.h>
|
#include <err.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
@ -26,6 +44,40 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
std::string getIPV6ReadableFromIn6(const struct sockaddr_in6* in)
|
||||||
|
{
|
||||||
|
std::string result;
|
||||||
|
char ipv6[INET6_ADDRSTRLEN] = {};
|
||||||
|
#ifdef WIN32
|
||||||
|
struct sockaddr_in6 copied = *in;
|
||||||
|
inet_ntop(AF_INET6, &copied, ipv6, INET6_ADDRSTRLEN);
|
||||||
|
#else
|
||||||
|
inet_ntop(AF_INET6, &(in->sin6_addr), ipv6, INET6_ADDRSTRLEN);
|
||||||
|
#endif
|
||||||
|
result = ipv6;
|
||||||
|
return result;
|
||||||
|
} // getIPV6ReadableFromIn6
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
bool sameIPV6(const struct sockaddr_in6* in_1, const struct sockaddr_in6* in_2)
|
||||||
|
{
|
||||||
|
// Check port first, then address
|
||||||
|
if (in_1->sin6_port != in_2->sin6_port)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const struct in6_addr* a = &(in_1->sin6_addr);
|
||||||
|
const struct in6_addr* b = &(in_2->sin6_addr);
|
||||||
|
for (unsigned i = 0; i < sizeof(struct in6_addr); i++)
|
||||||
|
{
|
||||||
|
if (a->s6_addr[i] != b->s6_addr[i])
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} // sameIPV6
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
/** Workaround of a bug in iOS 9 where port number is not written. */
|
/** Workaround of a bug in iOS 9 where port number is not written. */
|
||||||
extern "C" int getaddrinfo_compat(const char* hostname,
|
extern "C" int getaddrinfo_compat(const char* hostname,
|
||||||
@ -132,16 +184,6 @@ void unixInitialize()
|
|||||||
g_mapped_ips.clear();
|
g_mapped_ips.clear();
|
||||||
} // unixInitialize
|
} // unixInitialize
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
std::string getIPV6ReadableFromIn6(const struct sockaddr_in6* in)
|
|
||||||
{
|
|
||||||
std::string result;
|
|
||||||
char ipv6[INET6_ADDRSTRLEN] = {};
|
|
||||||
inet_ntop(AF_INET6, &(in->sin6_addr), ipv6, INET6_ADDRSTRLEN);
|
|
||||||
result = ipv6;
|
|
||||||
return result;
|
|
||||||
} // getIPV6ReadableFromIn6
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
/* Called when a peer is disconnected and we remove its reference to the ipv6.
|
/* Called when a peer is disconnected and we remove its reference to the ipv6.
|
||||||
*/
|
*/
|
||||||
@ -206,23 +248,6 @@ void getIPV6FromMappedAddress(const ENetAddress* ea, struct sockaddr_in6* in6)
|
|||||||
memset(in6, 0, sizeof(struct sockaddr_in6));
|
memset(in6, 0, sizeof(struct sockaddr_in6));
|
||||||
} // getIPV6FromMappedAddress
|
} // getIPV6FromMappedAddress
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
bool sameIPV6(const struct sockaddr_in6* in_1, const struct sockaddr_in6* in_2)
|
|
||||||
{
|
|
||||||
// Check port first, then address
|
|
||||||
if (in_1->sin6_port != in_2->sin6_port)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const struct in6_addr* a = &(in_1->sin6_addr);
|
|
||||||
const struct in6_addr* b = &(in_2->sin6_addr);
|
|
||||||
for (unsigned i = 0; i < sizeof(struct in6_addr); i++)
|
|
||||||
{
|
|
||||||
if (a->s6_addr[i] != b->s6_addr[i])
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} // sameIPV6
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
/* This is called when enet recieved a packet from its socket, we create an
|
/* This is called when enet recieved a packet from its socket, we create an
|
||||||
* real ipv4 address out of it or a fake one if it's from ipv6 connection.
|
* real ipv4 address out of it or a fake one if it's from ipv6 connection.
|
||||||
|
@ -35,3 +35,5 @@ int getaddrinfo_compat(const char* hostname, const char* servname,
|
|||||||
#endif
|
#endif
|
||||||
std::string getIPV6ReadableFromMappedAddress(const ENetAddress* ea);
|
std::string getIPV6ReadableFromMappedAddress(const ENetAddress* ea);
|
||||||
std::string getIPV6ReadableFromIn6(const struct sockaddr_in6* in);
|
std::string getIPV6ReadableFromIn6(const struct sockaddr_in6* in);
|
||||||
|
bool sameIPV6(const struct sockaddr_in6* in_1,
|
||||||
|
const struct sockaddr_in6* in_2);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user