Port stun address resolve to use SocketAddress

This commit is contained in:
Benau
2020-01-29 01:24:38 +08:00
parent 2c78625ef6
commit 0cb62803b2
5 changed files with 110 additions and 206 deletions

View File

@@ -24,6 +24,7 @@
#include "network/network.hpp"
#include "network/rewind_manager.hpp"
#include "network/server_config.hpp"
#include "network/socket_address.hpp"
#include "network/stk_host.hpp"
#include "network/stk_ipv6.hpp"
#include "online/xml_request.hpp"
@@ -191,17 +192,17 @@ void NetworkConfig::detectIPType()
{
m_nat64_prefix_data.fill(-1);
#ifdef ENABLE_IPV6
ENetAddress addr;
addr.host = STKHost::HOST_ANY;
addr.port = STKHost::PORT_ANY;
ENetAddress eaddr;
eaddr.host = STKHost::HOST_ANY;
eaddr.port = STKHost::PORT_ANY;
// We don't need to result of stun, just to check if the socket can be
// used in ipv4 or ipv6
uint8_t stun_tansaction_id[16] = {};
BareNetworkString s = STKHost::getStunRequest(stun_tansaction_id);
setIPv6Socket(0);
auto ipv4 = std::unique_ptr<Network>(new Network(1, 1, 0, 0, &addr));
auto ipv4 = std::unique_ptr<Network>(new Network(1, 1, 0, 0, &eaddr));
setIPv6Socket(1);
auto ipv6 = std::unique_ptr<Network>(new Network(1, 1, 0, 0, &addr));
auto ipv6 = std::unique_ptr<Network>(new Network(1, 1, 0, 0, &eaddr));
setIPv6Socket(0);
auto ipv4_it = UserConfigParams::m_stun_servers_v4.begin();
@@ -211,57 +212,34 @@ void NetworkConfig::detectIPType()
adv = rand() % UserConfigParams::m_stun_servers.size();
std::advance(ipv6_it, adv);
std::vector<std::string> addrv4_and_port =
StringUtils::split(ipv4_it->first, ':');
if (addrv4_and_port.size() != 2)
{
Log::error("NetworkConfig", "Wrong server address and port");
return;
}
struct addrinfo hints;
struct addrinfo* res = NULL;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
int status = getaddrinfo_compat(addrv4_and_port[0].c_str(),
addrv4_and_port[1].c_str(), &hints, &res);
SocketAddress stun_v4(ipv4_it->first, 0/*port specified in addr*/,
AF_INET);
bool sent_ipv4 = false;
if (status == 0 && res != NULL)
if (!stun_v4.isUnset())
{
if (res->ai_family == AF_INET)
{
sendto(ipv4->getENetHost()->socket, s.getData(), s.size(), 0,
res->ai_addr, sizeof(sockaddr_in));
sent_ipv4 = true;
}
freeaddrinfo(res);
sendto(ipv4->getENetHost()->socket, s.getData(), s.size(), 0,
stun_v4.getSockaddr(), stun_v4.getSocklen());
sent_ipv4 = true;
}
else
{
Log::error("NetworkConfig", "Invalid IPv4: %s",
ipv4_it->first.c_str());
}
std::vector<std::string> addrv6_and_port =
StringUtils::split(ipv6_it->first, ':');
if (addrv6_and_port.size() != 2)
{
Log::error("NetworkConfig", "Wrong server address and port");
return;
}
res = NULL;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_STREAM;
status = getaddrinfo_compat(addrv6_and_port[0].c_str(),
addrv6_and_port[1].c_str(), &hints, &res);
SocketAddress stun_v6(ipv6_it->first, 0/*port specified in addr*/,
AF_INET6);
bool sent_ipv6 = false;
if (status == 0 && res != NULL)
if (!stun_v6.isUnset())
{
if (res->ai_family == AF_INET6)
{
sendto(ipv6->getENetHost()->socket, s.getData(), s.size(), 0,
res->ai_addr, sizeof(sockaddr_in6));
sent_ipv6 = true;
}
freeaddrinfo(res);
sendto(ipv6->getENetHost()->socket, s.getData(), s.size(), 0,
stun_v6.getSockaddr(), stun_v6.getSocklen());
sent_ipv6 = true;
}
else
{
Log::error("NetworkConfig", "Invalid IPv6: %s",
ipv6_it->first.c_str());
}
bool has_ipv4 = false;
@@ -291,39 +269,27 @@ void NetworkConfig::detectIPType()
// All IPv4 only stun servers are *.supertuxkart.net which only
// have A record, so the below code which forces to get an AF_INET6
// will return their NAT64 addresses
res = NULL;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_STREAM;
status = getaddrinfo_compat(addrv4_and_port[0].c_str(),
addrv4_and_port[1].c_str(), &hints, &res);
if (status == 0 && res != NULL)
SocketAddress nat64(ipv4_it->first, 0/*port specified in addr*/,
AF_INET6);
if (!nat64.isUnset() && nat64.getFamily() == AF_INET6)
{
if (res->ai_family == AF_INET6)
{
struct sockaddr_in6 nat64 = {};
// Copy first 12 bytes
struct sockaddr_in6* out =
(struct sockaddr_in6*)res->ai_addr;
memcpy(nat64.sin6_addr.s6_addr, out->sin6_addr.s6_addr,
12);
m_nat64_prefix_data[0] =
((uint32_t)(nat64.sin6_addr.s6_addr[0]) << 8) | nat64.sin6_addr.s6_addr[1];
m_nat64_prefix_data[1] =
((uint32_t)(nat64.sin6_addr.s6_addr[2]) << 8) | nat64.sin6_addr.s6_addr[3];
m_nat64_prefix_data[2] =
((uint32_t)(nat64.sin6_addr.s6_addr[4]) << 8) | nat64.sin6_addr.s6_addr[5];
m_nat64_prefix_data[3] =
((uint32_t)(nat64.sin6_addr.s6_addr[6]) << 8) | nat64.sin6_addr.s6_addr[7];
m_nat64_prefix_data[4] =
((uint32_t)(nat64.sin6_addr.s6_addr[8]) << 8) | nat64.sin6_addr.s6_addr[9];
m_nat64_prefix_data[5] =
((uint32_t)(nat64.sin6_addr.s6_addr[10]) << 8) | nat64.sin6_addr.s6_addr[11];
m_nat64_prefix_data[6] = 0;
m_nat64_prefix_data[7] = 0;
m_nat64_prefix = getIPV6ReadableFromIn6(&nat64);
}
freeaddrinfo(res);
// Remove last 4 bytes which is IPv4 format
struct sockaddr_in6* in6 =
(struct sockaddr_in6*)nat64.getSockaddr();
uint8_t* byte = &(in6->sin6_addr.s6_addr[0]);
byte[12] = 0;
byte[13] = 0;
byte[14] = 0;
byte[15] = 0;
m_nat64_prefix_data[0] = ((uint32_t)(byte[0]) << 8) | byte[1];
m_nat64_prefix_data[1] = ((uint32_t)(byte[2]) << 8) | byte[3];
m_nat64_prefix_data[2] = ((uint32_t)(byte[4]) << 8) | byte[5];
m_nat64_prefix_data[3] = ((uint32_t)(byte[6]) << 8) | byte[7];
m_nat64_prefix_data[4] = ((uint32_t)(byte[8]) << 8) | byte[9];
m_nat64_prefix_data[5] = ((uint32_t)(byte[10]) << 8) | byte[11];
m_nat64_prefix_data[6] = 0;
m_nat64_prefix_data[7] = 0;
m_nat64_prefix = getIPV6ReadableFromIn6(in6);
}
}
}

View File

@@ -506,10 +506,15 @@ void SocketAddress::convertForIPv6Socket()
std::string ipv4 = toString(false/*show_port*/);
uint16_t port = getPort();
auto ip_type = NetworkConfig::get()->getIPType();
if (ip_type == NetworkConfig::IP_DUAL_STACK)
ipv4 = std::string("::ffff:") + ipv4;
else if (ip_type == NetworkConfig::IP_V6_NAT64)
if (ip_type == NetworkConfig::IP_V6_NAT64)
{
ipv4 = NetworkConfig::get()->getNAT64Prefix() + ipv4;
}
else
{
// Assume the system has dual stack if it uses an IPv6 socket
ipv4 = std::string("::ffff:") + ipv4;
}
init(ipv4, port);
}
#endif

View File

@@ -58,11 +58,12 @@ public:
// ------------------------------------------------------------------------
/** IPv4 Constructor (4 bytes). */
SocketAddress(uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4,
uint16_t port=0);
uint16_t port = 0);
// ------------------------------------------------------------------------
SocketAddress(const std::string& str, uint16_t port_number = 0)
SocketAddress(const std::string& str, uint16_t port_number = 0,
short family = AF_UNSPEC)
{
init(str, port_number);
init(str, port_number, family);
}
// ------------------------------------------------------------------------
bool operator==(const SocketAddress& other) const;
@@ -122,6 +123,16 @@ public:
// ------------------------------------------------------------------------
short getFamily() const { return m_family; }
// ------------------------------------------------------------------------
void setIPv6(const uint8_t* bytes, uint16_t port = 0)
{
m_family = AF_INET6;
m_sockaddr.fill(0);
sockaddr_in6* in6 = (sockaddr_in6*)m_sockaddr.data();
in6->sin6_family = AF_INET6;
memcpy(in6->sin6_addr.s6_addr, bytes, 16);
setPort(port);
}
// ------------------------------------------------------------------------
bool isPublicAddressLocalhost() const;
// ------------------------------------------------------------------------
/** Return true if this is an IPv6 address, if it's an IPv4 mapped IPv6

View File

@@ -411,92 +411,37 @@ BareNetworkString STKHost::getStunRequest(uint8_t* stun_tansaction_id)
//-----------------------------------------------------------------------------
void STKHost::getIPFromStun(int socket, const std::string& stun_address,
bool ipv4, std::string* ip_string, uint16_t* port)
bool ipv4, SocketAddress* result)
{
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;
short family = ipv4 ? AF_INET : AF_INET6;
// The IPv4 stun address has no AAAA record, so give it an AF_INET6 will
// return one using NAT64 and DNS64
if (isIPv6Socket() && ipv4 &&
NetworkConfig::get()->getIPType() == NetworkConfig::IP_V6_NAT64)
hints.ai_family = 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;
}
family = AF_INET6;
SocketAddress stun(stun_address, 0/*port is specified in address*/,
family);
// We specify ai_family, so only IPv4 or IPv6 is found
if (res == NULL)
if (stun.isUnset())
return;
stun.convertForIPv6Socket();
struct sockaddr* stun_addr = NULL;
if (isIPv6Socket())
// We only need to keep the stun address for server to keep the port open
if (NetworkConfig::get()->isServer())
{
if (res->ai_family == AF_INET)
{
struct sockaddr_in* ipv4_addr = (struct sockaddr_in*)(res->ai_addr);
SocketAddress stun_v4;
stun_v4.setIP(ntohl(ipv4_addr->sin_addr.s_addr));
stun_v4.setPort(ntohs(ipv4_addr->sin_port));
// Change address to ::ffff:IPv4 format for dual stack socket
std::string ipv4_mapped = "::ffff:";
ipv4_mapped += stun_v4.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;
m_stun_ipv4.reset(new SocketAddress());
m_stun_ipv4->setSockAddrIn(AF_INET6, stun_addr,
sizeof(sockaddr_in6));
}
if (stun.isIPv6())
m_stun_ipv6.reset(new SocketAddress(stun));
else
{
stun_addr = res->ai_addr;
m_stun_ipv6.reset(new SocketAddress());
m_stun_ipv6->setSockAddrIn(AF_INET6, stun_addr,
sizeof(sockaddr_in6));
}
}
else if (ipv4)
{
stun_addr = res->ai_addr;
m_stun_ipv4.reset(new SocketAddress());
m_stun_ipv4->setSockAddrIn(AF_INET, stun_addr, sizeof(sockaddr_in));
m_stun_ipv4.reset(new SocketAddress(stun));
}
uint8_t stun_tansaction_id[16];
constexpr uint32_t magic_cookie = 0x2112A442;
BareNetworkString s = getStunRequest(stun_tansaction_id);
sendto(socket, s.getData(), s.size(), 0, stun_addr, isIPv6Socket() ?
sizeof(sockaddr_in6) : sizeof(sockaddr_in));
sendto(socket, s.getData(), s.size(), 0, stun.getSockaddr(),
stun.getSocklen());
// Recieve now
const int LEN = 2048;
@@ -522,12 +467,9 @@ void STKHost::getIPFromStun(int socket, const std::string& stun_address,
if (len <= 0)
{
Log::error("STKHost", "STUN response contains no data at all");
freeaddrinfo(res);
return;
}
freeaddrinfo(res);
// Convert to network string.
BareNetworkString response(buffer, len);
if (response.size() < 20)
@@ -564,8 +506,7 @@ void STKHost::getIPFromStun(int socket, const std::string& stun_address,
// The stun message is valid, so we parse it now:
// Those are the port and the address to be detected
SocketAddress addr, non_xor_addr, xor_addr;
std::string ipv6_addr_non_xor, ipv6_addr_xor;
SocketAddress non_xor_addr, xor_addr;
while (true)
{
if (response.size() < 4)
@@ -609,20 +550,20 @@ void STKHost::getIPFromStun(int socket, const std::string& stun_address,
Log::error("STKHost", "Invalid STUN mapped address length.");
return;
}
*port = response.getUInt16();
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;
port ^= magic_cookie >> 16;
ip ^= magic_cookie;
xor_addr.setIP(ip);
xor_addr.setPort(*port);
xor_addr.setPort(port);
}
else
{
non_xor_addr.setIP(ip);
non_xor_addr.setPort(*port);
non_xor_addr.setPort(port);
}
}
else if (ip_type == ipv6_returned)
@@ -633,23 +574,20 @@ void STKHost::getIPFromStun(int socket, const std::string& stun_address,
Log::error("STKHost", "Invalid STUN mapped address length.");
return;
}
*port = response.getUInt16();
struct sockaddr_in6 ipv6_addr = {};
uint16_t port = response.getUInt16();
uint8_t bytes[16];
for (int i = 0; i < 16; i++)
bytes[i] = response.getUInt8();
if (type == xor_mapped_address)
{
*port ^= magic_cookie >> 16;
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);
xor_addr.setIPv6(bytes, port);
}
else
{
memcpy(&(ipv6_addr.sin6_addr), &bytes[0], 16);
ipv6_addr_non_xor = getIPV6ReadableFromIn6(&ipv6_addr);
non_xor_addr.setIPv6(bytes, port);
}
}
} // type == mapped_address || type == xor_mapped_address
@@ -662,36 +600,21 @@ void STKHost::getIPFromStun(int socket, const std::string& stun_address,
}
} // while true
// Found public address and port
if (ipv4)
if (!xor_addr.isUnset() || !non_xor_addr.isUnset())
{
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())
{
// 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;
}
*result = xor_addr;
}
*ip_string = addr.toString(false/*show_port*/);
}
else
{
if (ipv6_addr_xor.empty() && ipv6_addr_non_xor.empty())
return;
if (ipv6_addr_xor.empty())
else
{
Log::warn("STKHost", "Only non xor-mapped address returned.");
*ip_string = ipv6_addr_non_xor;
*result = non_xor_addr;
}
*ip_string = ipv6_addr_xor;
}
} // getIPFromStun
@@ -726,16 +649,15 @@ void STKHost::setPublicAddress(bool ipv4)
stun_map[server_name] = (uint32_t)-1;
Log::debug("STKHost", "Using STUN server %s", server_name.c_str());
uint64_t ping = StkTime::getMonoTimeMs();
std::string ip_string;
uint16_t port = 0;
SocketAddress result;
getIPFromStun((int)m_network->getENetHost()->socket, server_name, ipv4,
&ip_string, &port);
if (!ip_string.empty())
&result);
if (!result.isUnset())
{
// For dual stack server we get the IPv6 address and then IPv4, if
// their ports differ we discard the public IPv6 address of server
if (NetworkConfig::get()->isServer() && ipv4 &&
port != m_public_address->getPort())
result.getPort() != m_public_address->getPort())
{
if (isIPv6Socket())
{
@@ -745,10 +667,10 @@ void STKHost::setPublicAddress(bool ipv4)
m_public_ipv6_address.clear();
}
if (ipv4)
m_public_address->init(ip_string);
*m_public_address = result;
else
m_public_ipv6_address = ip_string;
m_public_address->setPort(port);
m_public_ipv6_address = result.toString(false/*show_port*/);
m_public_address->setPort(result.getPort());
ping = StkTime::getMonoTimeMs() - ping;
// Succeed, save ping
stun_map[server_name] = (uint32_t)(ping);

View File

@@ -176,7 +176,7 @@ private:
void mainLoop();
// ------------------------------------------------------------------------
void getIPFromStun(int socket, const std::string& stun_address, bool ipv4,
std::string* ip_string, uint16_t* port);
SocketAddress* result);
public:
/** If a network console should be started. */
static bool m_enable_console;