Allow setting public address for IPv6 client

This commit is contained in:
Benau 2020-01-23 01:16:02 +08:00
parent aed64dfd1f
commit 8486b11da6
6 changed files with 101 additions and 94 deletions

View File

@ -1431,6 +1431,7 @@ int handleCmdLine(bool has_server_config, bool has_parent_process)
// Server owner online account will keep online as long as
// server is live
Online::RequestManager::m_disable_polling = true;
NetworkConfig::get()->detectIPType();
NetworkConfig::get()->setIsWAN();
NetworkConfig::get()->setIsPublicServer();
ServerConfig::loadServerLobbyFromConfig();

View File

@ -224,14 +224,27 @@ void ConnectToServer::asynchronousUpdate()
}
servers.clear();
}
if (m_server->useIPV6Connection())
// Auto enable IPv6 socket in client with nat64, so in
// connect to server it will change the ipv4 address to nat64 one
if (m_server->useIPV6Connection() ||
NetworkConfig::get()->getIPType() == NetworkConfig::IP_V6_NAT64)
{
// Disable STUN if using IPv6 (check in setPublicAddress)
// Free the bound socket first
delete STKHost::get()->getNetwork();
setIPv6Socket(1);
ENetAddress addr;
addr.host = STKHost::HOST_ANY;
addr.port = NetworkConfig::get()->getClientPort();
auto new_network = new Network(/*peer_count*/1,
/*channel_limit*/EVENT_CHANNEL_COUNT,
/*max_in_bandwidth*/0, /*max_out_bandwidth*/0, &addr,
true/*change_port_if_bound*/);
STKHost::get()->replaceNetwork(new_network);
}
if (m_server->supportsEncryption())
{
STKHost::get()->setPublicAddress();
STKHost::get()->setPublicAddress(
!m_server->useIPV6Connection());
registerWithSTKServer();
}
// Set to DONE will stop STKHost is not connected
@ -466,6 +479,8 @@ void ConnectToServer::registerWithSTKServer()
NetworkConfig::get()->setServerDetails(request, "join-server-key");
request->addParameter("server-id", m_server->getServerId());
request->addParameter("address", addr.getIP());
request->addParameter("address_ipv6",
STKHost::get()->getPublicIPV6Address());
request->addParameter("port", addr.getPort());
Crypto::initClientAES();
@ -473,7 +488,9 @@ void ConnectToServer::registerWithSTKServer()
request->addParameter("aes-iv", Crypto::getClientIV());
Log::info("ConnectToServer", "Registering addr %s",
addr.toString().c_str());
STKHost::get()->getPublicIPV6Address().empty() ?
addr.toString().c_str() :
STKHost::get()->getPublicIPV6Address().c_str());
// This can be done blocking: till we are registered with the
// stk server, there is no need to to react to any other

View File

@ -1357,7 +1357,17 @@ void ServerLobby::asynchronousUpdate()
createServerIdFile();
return;
}
STKHost::get()->setPublicAddress();
auto ip_type = NetworkConfig::get()->getIPType();
// Set the IPv6 address first for possible IPv6 only server
if (ip_type >= NetworkConfig::IP_V6)
{
STKHost::get()->setPublicAddress(false/*ipv4*/);
}
if (ip_type == NetworkConfig::IP_V4 ||
ip_type == NetworkConfig::IP_DUAL_STACK)
{
STKHost::get()->setPublicAddress(true/*ipv4*/);
}
if (STKHost::get()->getPublicAddress().isUnset())
{
m_state = ERROR_LEAVE;

View File

@ -400,21 +400,28 @@ BareNetworkString STKHost::getStunRequest(uint8_t* stun_tansaction_id)
} // getStunRequest
//-----------------------------------------------------------------------------
std::string STKHost::getIPFromStun(int socket, const std::string& stun_address,
bool ipv4)
void STKHost::getIPFromStun(int socket, const std::string& stun_address,
bool ipv4, std::string* ip_string, uint16_t* port)
{
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 "";
return;
}
struct addrinfo hints;
struct addrinfo* res = NULL;
memset(&hints, 0, sizeof hints);
hints.ai_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
@ -424,22 +431,22 @@ std::string STKHost::getIPFromStun(int socket, const std::string& stun_address,
{
Log::error("STKHost", "Error in getaddrinfo for stun server"
" %s: %s", addr_and_port[0].c_str(), gai_strerror(status));
return "";
return;
}
// We specify ai_family, so only IPv4 or IPv6 is found
if (res == NULL)
return "";
return;
struct sockaddr* stun_addr = NULL;
if (isIPv6Socket())
{
if (ipv4)
if (res->ai_family == AF_INET)
{
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
// Change address to ::ffff:IPv4 format for dual stack socket
std::string ipv4_mapped = "::ffff:";
ipv4_mapped += m_stun_address.toString(false/*show_port*/);
freeaddrinfo(res);
@ -451,7 +458,7 @@ std::string STKHost::getIPFromStun(int socket, const std::string& stun_address,
{
Log::error("STKHost", "Error in getaddrinfo for stun server"
" %s: %s", addr_and_port[0].c_str(), gai_strerror(status));
return "";
return;
}
stun_addr = res->ai_addr;
}
@ -501,31 +508,9 @@ std::string STKHost::getIPFromStun(int socket, const std::string& stun_address,
{
Log::error("STKHost", "STUN response contains no data at all");
freeaddrinfo(res);
return "";
return;
}
if (isIPv6Socket())
{
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.
@ -533,13 +518,13 @@ std::string STKHost::getIPFromStun(int socket, const std::string& stun_address,
if (response.size() < 20)
{
Log::error("STKHost", "STUN response should be at least 20 bytes.");
return "";
return;
}
if (response.getUInt16() != 0x0101)
{
Log::error("STKHost", "STUN has no binding success response.");
return "";
return;
}
// Skip message size
@ -549,7 +534,7 @@ std::string STKHost::getIPFromStun(int socket, const std::string& stun_address,
{
Log::error("STKHost", "STUN response doesn't contain the magic "
"cookie");
return "";
return;
}
for (int i = 0; i < 12; i++)
@ -558,7 +543,7 @@ std::string STKHost::getIPFromStun(int socket, const std::string& stun_address,
{
Log::error("STKHost", "STUN response doesn't contain the "
"transaction ID");
return "";
return;
}
}
@ -596,7 +581,7 @@ std::string STKHost::getIPFromStun(int socket, const std::string& stun_address,
if (response.size() < 2)
{
Log::error("STKHost", "Invalid STUN mapped address length.");
return "";
return;
}
// Ignore the first byte as mentioned in Section 15.1 of RFC 5389.
uint8_t ip_type = response.getUInt8();
@ -607,22 +592,22 @@ std::string STKHost::getIPFromStun(int socket, const std::string& stun_address,
if (size != 8 || response.size() < 6)
{
Log::error("STKHost", "Invalid STUN mapped address length.");
return "";
return;
}
uint16_t port = response.getUInt16();
*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.setPort(port);
xor_addr.setIP(ip);
xor_addr.setPort(*port);
}
else
{
non_xor_addr.setPort(port);
non_xor_addr.setIP(ip);
non_xor_addr.setPort(*port);
}
}
else if (ip_type == ipv6_returned)
@ -631,16 +616,16 @@ std::string STKHost::getIPFromStun(int socket, const std::string& stun_address,
if (size != 20 || response.size() < 18)
{
Log::error("STKHost", "Invalid STUN mapped address length.");
return "";
return;
}
uint16_t port = response.getUInt16();
*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;
*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);
@ -651,11 +636,6 @@ std::string STKHost::getIPFromStun(int socket, const std::string& stun_address,
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
@ -685,37 +665,36 @@ std::string STKHost::getIPFromStun(int socket, const std::string& stun_address,
addr = non_xor_addr;
}
}
return addr.toString();
*ip_string = addr.toString(false/*show_port*/);
}
else
{
if (ipv6_addr_xor.empty() && ipv6_addr_non_xor.empty())
return "";
return;
if (ipv6_addr_xor.empty())
{
Log::warn("STKHost", "Only non xor-mapped address returned.");
return ipv6_addr_non_xor;
*ip_string = ipv6_addr_non_xor;
}
return ipv6_addr_xor;
*ip_string = ipv6_addr_xor;
}
} // getIPFromStun
//-----------------------------------------------------------------------------
/** Set the public address using stun protocol.
*/
void STKHost::setPublicAddress()
void STKHost::setPublicAddress(bool ipv4)
{
if (isIPv6Socket() && NetworkConfig::get()->isClient())
if (isIPv6Socket() && !ipv4)
{
// IPv6 client doesn't support connection to firewalled server,
// so no need to test STUN
Log::info("STKHost", "IPv6 only environment detected.");
// Set an unused IPv4 first for possible IPv6 only network
m_public_address = TransportAddress("169.254.0.0:65535");
return;
}
auto& stun_map = ipv4 ? UserConfigParams::m_stun_servers_v4 :
UserConfigParams::m_stun_servers;
std::vector<std::pair<std::string, uint32_t> > untried_server;
for (auto& p : UserConfigParams::m_stun_servers)
for (auto& p : stun_map)
untried_server.push_back(p);
assert(untried_server.size() > 2);
@ -735,29 +714,31 @@ void STKHost::setPublicAddress()
{
// Pick last element in untried servers
std::string server_name = untried_server.back().first.c_str();
UserConfigParams::m_stun_servers[server_name] = (uint32_t)-1;
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 ipv4_string = getIPFromStun(
(int)m_network->getENetHost()->socket, server_name, true/*IPv4*/);
TransportAddress addr(ipv4_string);
if (!addr.isUnset())
std::string ip_string;
uint16_t port = 0;
getIPFromStun((int)m_network->getENetHost()->socket, server_name, ipv4,
&ip_string, &port);
if (!ip_string.empty())
{
m_public_address = addr;
// 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())
{
Log::error("STKHost", "IPv6 has different port than IPv4.");
m_public_ipv6_address.clear();
}
if (ipv4)
m_public_address = ip_string;
else
m_public_ipv6_address = ip_string;
m_public_address.setPort(port);
ping = StkTime::getMonoTimeMs() - ping;
// Succeed, save ping
UserConfigParams::m_stun_servers[server_name] = (uint32_t)(ping);
if (isIPv6Socket())
{
m_public_ipv6_address = getIPFromStun(
(int)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.");
}
}
stun_map[server_name] = (uint32_t)(ping);
untried_server.clear();
}
else

View File

@ -172,8 +172,8 @@ private:
// ------------------------------------------------------------------------
void mainLoop();
// ------------------------------------------------------------------------
std::string getIPFromStun(int socket, const std::string& stun_address,
bool ipv4);
void getIPFromStun(int socket, const std::string& stun_address, bool ipv4,
std::string* ip_string, uint16_t* port);
public:
/** If a network console should be started. */
static bool m_enable_console;
@ -212,7 +212,7 @@ public:
// ------------------------------------------------------------------------
void setPrivatePort();
// ------------------------------------------------------------------------
void setPublicAddress();
void setPublicAddress(bool ipv4);
// ------------------------------------------------------------------------
void disconnectAllPeers(bool timeout_waiting = false);
// ------------------------------------------------------------------------
@ -394,6 +394,12 @@ public:
std::vector<std::shared_ptr<NetworkPlayerProfile> >
getPlayersForNewGame() const;
// ------------------------------------------------------------------------
void replaceNetwork(Network* new_network)
{
m_network = new_network;
setPrivatePort();
}
// ------------------------------------------------------------------------
static BareNetworkString getStunRequest(uint8_t* stun_tansaction_id);
}; // class STKHost

View File

@ -271,14 +271,6 @@ void stkInitialize()
g_mapped_ipv6_used = 0;
g_ipv6 = 0;
g_mapped_ips.clear();
// For now only auto enable ipv6 socket in client with nat64, so in
// connect to server it will change the ipv4 address to nat64 one
if (NetworkConfig::get()->isClient())
{
if (NetworkConfig::get()->getIPType() == NetworkConfig::IP_V6_NAT64)
g_ipv6 = 1;
}
} // stkInitialize
// ----------------------------------------------------------------------------