aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2024-11-12 13:54:43 +0100
committerShauren <shauren.trinity@gmail.com>2024-11-12 13:54:43 +0100
commitaf4dcc93ed04c4f2219c14821b25cb9efeb7e781 (patch)
tree4fc5ac7ef91398ebf7d0684ab5a97ef392e2a2bc
parent280af853374b3cabcc2514d9604deeb39d03908b (diff)
Core/Networking: Support IPv6
-rw-r--r--sql/base/auth_database.sql7
-rw-r--r--sql/updates/auth/master/2024_11_12_00_auth.sql5
-rw-r--r--src/common/Asio/Resolver.h17
-rw-r--r--src/server/bnetserver/REST/LoginRESTService.cpp37
-rw-r--r--src/server/bnetserver/REST/LoginRESTService.h3
-rw-r--r--src/server/bnetserver/bnetserver.conf.dist1
-rw-r--r--src/server/database/Database/Implementation/LoginDatabase.cpp2
-rw-r--r--src/server/game/Server/WorldSession.cpp19
-rw-r--r--src/server/game/Server/WorldSocket.cpp2
-rw-r--r--src/server/shared/Networking/AsyncAcceptor.h6
-rw-r--r--src/server/shared/Realm/Realm.cpp4
-rw-r--r--src/server/shared/Realm/RealmList.cpp71
-rw-r--r--src/server/shared/Realm/RealmList.h2
-rw-r--r--src/server/worldserver/worldserver.conf.dist1
14 files changed, 118 insertions, 59 deletions
diff --git a/sql/base/auth_database.sql b/sql/base/auth_database.sql
index cf3fc5c4a76..66955e08680 100644
--- a/sql/base/auth_database.sql
+++ b/sql/base/auth_database.sql
@@ -2799,6 +2799,8 @@ CREATE TABLE `realmlist` (
`name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '127.0.0.1',
`localAddress` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '127.0.0.1',
+ `address3` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ `address4` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`localSubnetMask` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '255.255.255.0',
`port` smallint unsigned NOT NULL DEFAULT '8085',
`icon` tinyint unsigned NOT NULL DEFAULT '0',
@@ -2821,7 +2823,7 @@ CREATE TABLE `realmlist` (
LOCK TABLES `realmlist` WRITE;
/*!40000 ALTER TABLE `realmlist` DISABLE KEYS */;
INSERT INTO `realmlist` VALUES
-(1,'Trinity','127.0.0.1','127.0.0.1','255.255.255.0',8085,0,0,1,0,0,57388,1,1);
+(1,'Trinity','127.0.0.1','127.0.0.1',NULL,NULL,'255.255.255.0',8085,0,0,1,0,0,57388,1,1);
/*!40000 ALTER TABLE `realmlist` ENABLE KEYS */;
UNLOCK TABLES;
@@ -3288,7 +3290,8 @@ INSERT INTO `updates` VALUES
('2024_10_23_01_auth.sql','06257BB5FF23564549529C14822DD5780AA7F7C3','RELEASED','2024-10-23 22:30:54',0),
('2024_10_30_00_auth.sql','B4C930B0CE499704CEBF208A71871061DC27008C','RELEASED','2024-10-30 13:04:40',0),
('2024_11_01_00_auth.sql','62EF7FC9CD2DCD8D24CE1BCED3BF9E2917B6674B','RELEASED','2024-11-01 13:26:56',0),
-('2024_11_11_00_auth.sql','F47CDFB857DB4105306739AF4FBBB3C92CA43363','RELEASED','2024-11-11 13:34:09',0);
+('2024_11_11_00_auth.sql','F47CDFB857DB4105306739AF4FBBB3C92CA43363','RELEASED','2024-11-11 13:34:09',0),
+('2024_11_12_00_auth.sql','5A236A557291758C0F2C46FDEC02692E7C53F751','RELEASED','2024-11-12 11:42:13',0);
/*!40000 ALTER TABLE `updates` ENABLE KEYS */;
UNLOCK TABLES;
diff --git a/sql/updates/auth/master/2024_11_12_00_auth.sql b/sql/updates/auth/master/2024_11_12_00_auth.sql
new file mode 100644
index 00000000000..3eb0af6c898
--- /dev/null
+++ b/sql/updates/auth/master/2024_11_12_00_auth.sql
@@ -0,0 +1,5 @@
+ALTER TABLE `realmlist`
+ MODIFY `address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '127.0.0.1' AFTER `name`,
+ MODIFY `localAddress` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '127.0.0.1' AFTER `address`,
+ ADD `address3` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL AFTER `localAddress`,
+ ADD `address4` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL AFTER `address3`;
diff --git a/src/common/Asio/Resolver.h b/src/common/Asio/Resolver.h
index 01399e435e5..01fd3127abe 100644
--- a/src/common/Asio/Resolver.h
+++ b/src/common/Asio/Resolver.h
@@ -21,7 +21,8 @@
#include "IoContext.h"
#include "Optional.h"
#include <boost/asio/ip/tcp.hpp>
-#include <string>
+#include <string_view>
+#include <vector>
namespace Trinity
{
@@ -35,7 +36,7 @@ namespace Trinity
public:
explicit Resolver(IoContext& ioContext) : _impl(ioContext) { }
- Optional<boost::asio::ip::tcp::endpoint> Resolve(boost::asio::ip::tcp const& protocol, std::string const& host, std::string const& service)
+ Optional<boost::asio::ip::tcp::endpoint> Resolve(boost::asio::ip::tcp const& protocol, std::string_view host, std::string_view service)
{
boost::system::error_code ec;
boost::asio::ip::resolver_base::flags flagsResolver = boost::asio::ip::resolver_base::all_matching;
@@ -46,6 +47,18 @@ namespace Trinity
return results.begin()->endpoint();
}
+ std::vector<boost::asio::ip::tcp::endpoint> ResolveAll(std::string_view host, std::string_view service)
+ {
+ boost::system::error_code ec;
+ boost::asio::ip::resolver_base::flags flagsResolver = boost::asio::ip::resolver_base::all_matching;
+ boost::asio::ip::tcp::resolver::results_type results = _impl.resolve(host, service, flagsResolver, ec);
+ std::vector<boost::asio::ip::tcp::endpoint> result;
+ if (!ec)
+ std::ranges::transform(results, std::back_inserter(result), [](boost::asio::ip::tcp::resolver::results_type::value_type const& entry) { return entry.endpoint(); });
+
+ return result;
+ }
+
private:
boost::asio::ip::tcp::resolver _impl;
};
diff --git a/src/server/bnetserver/REST/LoginRESTService.cpp b/src/server/bnetserver/REST/LoginRESTService.cpp
index c9aabb248a3..b2ce7ddab56 100644
--- a/src/server/bnetserver/REST/LoginRESTService.cpp
+++ b/src/server/bnetserver/REST/LoginRESTService.cpp
@@ -77,28 +77,26 @@ bool LoginRESTService::StartNetwork(Trinity::Asio::IoContext& ioContext, std::st
_bindIP = bindIp;
_port = port;
+ using namespace std::string_literals;
+ std::array<std::string, 2> configKeys = { { "LoginREST.ExternalAddress"s, "LoginREST.LocalAddress"s } };
+
Trinity::Asio::Resolver resolver(ioContext);
- _hostnames[0] = sConfigMgr->GetStringDefault("LoginREST.ExternalAddress", "127.0.0.1");
- Optional<boost::asio::ip::tcp::endpoint> externalAddress = resolver.Resolve(boost::asio::ip::tcp::v4(), _hostnames[0], std::to_string(_port));
- if (!externalAddress)
+ for (std::size_t i = 0; i < _hostnames.size(); ++i)
{
- TC_LOG_ERROR("server.http.login", "Could not resolve LoginREST.ExternalAddress {}", _hostnames[0]);
- return false;
- }
+ _hostnames[i].first = sConfigMgr->GetStringDefault(configKeys[i], "127.0.0.1");
- _addresses[0] = externalAddress->address();
+ std::ranges::transform(resolver.ResolveAll(_hostnames[i].first, ""),
+ std::back_inserter(_hostnames[i].second),
+ [](boost::asio::ip::tcp::endpoint const& endpoint) { return endpoint.address(); });
- _hostnames[1] = sConfigMgr->GetStringDefault("LoginREST.LocalAddress", "127.0.0.1");
- Optional<boost::asio::ip::tcp::endpoint> localAddress = resolver.Resolve(boost::asio::ip::tcp::v4(), _hostnames[1], std::to_string(_port));
- if (!localAddress)
- {
- TC_LOG_ERROR("server.http.login", "Could not resolve LoginREST.LocalAddress {}", _hostnames[1]);
- return false;
+ if (_hostnames[i].second.empty())
+ {
+ TC_LOG_ERROR("server.http.login", "Could not resolve {} {}", configKeys[i], _hostnames[i].first);
+ return false;
+ }
}
- _addresses[1] = localAddress->address();
-
// set up form inputs
JSON::Login::FormInput* input;
_formInputs.set_type(JSON::Login::LOGIN_FORM);
@@ -129,13 +127,14 @@ bool LoginRESTService::StartNetwork(Trinity::Asio::IoContext& ioContext, std::st
std::string const& LoginRESTService::GetHostnameForClient(boost::asio::ip::address const& address) const
{
- if (auto addressIndex = Trinity::Net::SelectAddressForClient(address, _addresses))
- return _hostnames[*addressIndex];
+ for (std::size_t i = 0; i < _hostnames.size(); ++i)
+ if (Trinity::Net::SelectAddressForClient(address, _hostnames[i].second))
+ return _hostnames[i].first;
if (address.is_loopback())
- return _hostnames[1];
+ return _hostnames[1].first;
- return _hostnames[0];
+ return _hostnames[0].first;
}
std::string LoginRESTService::ExtractAuthorization(HttpRequest const& request)
diff --git a/src/server/bnetserver/REST/LoginRESTService.h b/src/server/bnetserver/REST/LoginRESTService.h
index bcc99808505..079bb6fca9c 100644
--- a/src/server/bnetserver/REST/LoginRESTService.h
+++ b/src/server/bnetserver/REST/LoginRESTService.h
@@ -83,8 +83,7 @@ private:
JSON::Login::FormInputs _formInputs;
std::string _bindIP;
uint16 _port;
- std::array<std::string, 2> _hostnames;
- std::array<boost::asio::ip::address, 2> _addresses;
+ std::array<std::pair<std::string, std::vector<boost::asio::ip::address>>, 2> _hostnames;
uint32 _loginTicketDuration;
};
}
diff --git a/src/server/bnetserver/bnetserver.conf.dist b/src/server/bnetserver/bnetserver.conf.dist
index 94fd3964590..1936d57d3fa 100644
--- a/src/server/bnetserver/bnetserver.conf.dist
+++ b/src/server/bnetserver/bnetserver.conf.dist
@@ -83,6 +83,7 @@ LoginREST.TicketDuration=3600
#
# BindIP
# Description: Bind auth server to IP/hostname
+# Using IPv6 address (such as "::") will enable both IPv4 and IPv6 connections
# Default: "0.0.0.0" - (Bind to all IPs on the system)
BindIP = "0.0.0.0"
diff --git a/src/server/database/Database/Implementation/LoginDatabase.cpp b/src/server/database/Database/Implementation/LoginDatabase.cpp
index c916a0ac458..ca1930e4b06 100644
--- a/src/server/database/Database/Implementation/LoginDatabase.cpp
+++ b/src/server/database/Database/Implementation/LoginDatabase.cpp
@@ -23,7 +23,7 @@ void LoginDatabaseConnection::DoPrepareStatements()
if (!m_reconnecting)
m_stmts.resize(MAX_LOGINDATABASE_STATEMENTS);
- PrepareStatement(LOGIN_SEL_REALMLIST, "SELECT id, name, address, localAddress, port, icon, flag, timezone, allowedSecurityLevel, population, gamebuild, Region, Battlegroup FROM realmlist WHERE flag <> 3 ORDER BY name", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_SEL_REALMLIST, "SELECT id, name, address, localAddress, address3, address4, port, icon, flag, timezone, allowedSecurityLevel, population, gamebuild, Region, Battlegroup FROM realmlist WHERE flag <> 3 ORDER BY name", CONNECTION_SYNCH);
PrepareStatement(LOGIN_UPD_REALM_POPULATION, "UPDATE realmlist SET population = ? WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_DEL_EXPIRED_IP_BANS, "DELETE FROM ip_banned WHERE unbandate<>bandate AND unbandate<=UNIX_TIMESTAMP()", CONNECTION_ASYNC);
PrepareStatement(LOGIN_UPD_EXPIRED_ACCOUNT_BANS, "UPDATE account_banned SET active = 0 WHERE active = 1 AND unbandate<>bandate AND unbandate<=UNIX_TIMESTAMP()", CONNECTION_ASYNC);
diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp
index 55cd3379a58..0f19c3115aa 100644
--- a/src/server/game/Server/WorldSession.cpp
+++ b/src/server/game/Server/WorldSession.cpp
@@ -818,8 +818,23 @@ void WorldSession::SendConnectToInstance(WorldPackets::Auth::ConnectToSerial ser
}
else
{
- memcpy(connectTo.Payload.Where.Address.V6.data(), instanceAddress.to_v6().to_bytes().data(), 16);
- connectTo.Payload.Where.Type = WorldPackets::Auth::ConnectTo::IPv6;
+ // client always uses v4 address for loopback and v4 mapped addresses
+ boost::asio::ip::address_v6 v6 = instanceAddress.to_v6();
+ if (v6.is_loopback())
+ {
+ memcpy(connectTo.Payload.Where.Address.V4.data(), boost::asio::ip::address_v4::loopback().to_bytes().data(), 4);
+ connectTo.Payload.Where.Type = WorldPackets::Auth::ConnectTo::IPv4;
+ }
+ else if (v6.is_v4_mapped())
+ {
+ memcpy(connectTo.Payload.Where.Address.V4.data(), Trinity::Net::make_address_v4(boost::asio::ip::v4_mapped, v6).to_bytes().data(), 4);
+ connectTo.Payload.Where.Type = WorldPackets::Auth::ConnectTo::IPv4;
+ }
+ else
+ {
+ memcpy(connectTo.Payload.Where.Address.V6.data(), v6.to_bytes().data(), 16);
+ connectTo.Payload.Where.Type = WorldPackets::Auth::ConnectTo::IPv6;
+ }
}
connectTo.Con = CONNECTION_TYPE_INSTANCE;
diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp
index 601cb950af5..359482110d6 100644
--- a/src/server/game/Server/WorldSocket.cpp
+++ b/src/server/game/Server/WorldSocket.cpp
@@ -445,7 +445,7 @@ WorldSocket::ReadDataHandlerResult WorldSocket::ReadDataHandler()
return ReadDataHandlerResult::Error;
case CMSG_LOG_DISCONNECT:
LogOpcodeText(opcode, sessionGuard);
- packet.rfinish(); // contains uint32 disconnectReason;
+ TC_LOG_DEBUG("network", "WorldSocket::ReadDataHandler: client {} sent CMSG_LOG_DISCONNECT reason {}", GetRemoteIpAddress().to_string(), packet.read<uint32>());
break;
case CMSG_ENABLE_NAGLE:
LogOpcodeText(opcode, sessionGuard);
diff --git a/src/server/shared/Networking/AsyncAcceptor.h b/src/server/shared/Networking/AsyncAcceptor.h
index 7f1cb34204c..95e25e02166 100644
--- a/src/server/shared/Networking/AsyncAcceptor.h
+++ b/src/server/shared/Networking/AsyncAcceptor.h
@@ -22,6 +22,7 @@
#include "IpAddress.h"
#include "Log.h"
#include <boost/asio/ip/tcp.hpp>
+#include <boost/asio/ip/v6_only.hpp>
#include <atomic>
#include <functional>
@@ -88,6 +89,11 @@ public:
}
#endif
+ // v6_only is enabled on some *BSD distributions by default
+ // we want to allow both v4 and v6 connections to the same listener
+ if (_endpoint.protocol() == boost::asio::ip::tcp::v6())
+ _acceptor.set_option(boost::asio::ip::v6_only(false));
+
_acceptor.bind(_endpoint, errorCode);
if (errorCode)
{
diff --git a/src/server/shared/Realm/Realm.cpp b/src/server/shared/Realm/Realm.cpp
index 42ac0c3f64b..c06a53dfe9c 100644
--- a/src/server/shared/Realm/Realm.cpp
+++ b/src/server/shared/Realm/Realm.cpp
@@ -26,7 +26,7 @@ void Realm::SetName(std::string name)
{
Name = name;
NormalizedName = std::move(name);
- NormalizedName.erase(std::remove_if(NormalizedName.begin(), NormalizedName.end(), ::isspace), NormalizedName.end());
+ std::erase_if(NormalizedName, [](char c) { return std::isspace(static_cast<unsigned char>(c)); });
}
boost::asio::ip::address Realm::GetAddressForClient(boost::asio::ip::address const& clientAddr) const
@@ -34,7 +34,7 @@ boost::asio::ip::address Realm::GetAddressForClient(boost::asio::ip::address con
if (auto addressIndex = Trinity::Net::SelectAddressForClient(clientAddr, Addresses))
return Addresses[*addressIndex];
- if (clientAddr.is_loopback())
+ if (Addresses.size() > 1 && clientAddr.is_loopback())
return Addresses[1];
return Addresses[0];
diff --git a/src/server/shared/Realm/RealmList.cpp b/src/server/shared/Realm/RealmList.cpp
index fcee58c47d0..9fe48277353 100644
--- a/src/server/shared/Realm/RealmList.cpp
+++ b/src/server/shared/Realm/RealmList.cpp
@@ -80,7 +80,7 @@ void RealmList::Close()
}
void RealmList::UpdateRealm(Realm& realm, Battlenet::RealmHandle const& id, uint32 build, std::string const& name,
- boost::asio::ip::address&& address, boost::asio::ip::address&& localAddr,
+ std::vector<boost::asio::ip::address>&& addresses,
uint16 port, uint8 icon, RealmFlags flag, uint8 timezone, AccountTypes allowedSecurityLevel,
RealmPopulationState population)
{
@@ -93,9 +93,7 @@ void RealmList::UpdateRealm(Realm& realm, Battlenet::RealmHandle const& id, uint
realm.Timezone = timezone;
realm.AllowedSecurityLevel = allowedSecurityLevel;
realm.PopulationLevel = population;
- realm.Addresses.resize(2);
- realm.Addresses[0] = std::move(address);
- realm.Addresses[1] = std::move(localAddr);
+ realm.Addresses = std::move(addresses);
realm.Port = port;
}
@@ -121,48 +119,65 @@ void RealmList::UpdateRealms()
Field* fields = result->Fetch();
uint32 realmId = fields[0].GetUInt32();
std::string name = fields[1].GetString();
- std::string externalAddressString = fields[2].GetString();
- std::string localAddressString = fields[3].GetString();
+ std::vector<boost::asio::ip::address> addresses;
- Optional<boost::asio::ip::tcp::endpoint> externalAddress = _resolver->Resolve(boost::asio::ip::tcp::v4(), externalAddressString, "");
- if (!externalAddress)
+ for (std::size_t i = 0; i < 4; ++i)
{
- TC_LOG_ERROR("realmlist", "Could not resolve address {} for realm \"{}\" id {}", externalAddressString, name, realmId);
- continue;
+ if (fields[2 + i].IsNull())
+ continue;
+
+ for (boost::asio::ip::tcp::endpoint const& endpoint : _resolver->ResolveAll(fields[2 + i].GetStringView(), ""))
+ {
+ boost::asio::ip::address address = endpoint.address();
+ if (std::ranges::find(addresses, address) != addresses.end())
+ continue;
+
+ addresses.push_back(std::move(address));
+ }
}
- Optional<boost::asio::ip::tcp::endpoint> localAddress = _resolver->Resolve(boost::asio::ip::tcp::v4(), localAddressString, "");
- if (!localAddress)
+ if (addresses.empty())
{
- TC_LOG_ERROR("realmlist", "Could not resolve localAddress {} for realm \"{}\" id {}", localAddressString, name, realmId);
+ TC_LOG_ERROR("realmlist", "Could not resolve any address for realm \"{}\" id {}", name, realmId);
continue;
}
- uint16 port = fields[4].GetUInt16();
- uint8 icon = fields[5].GetUInt8();
+ uint16 port = fields[6].GetUInt16();
+ uint8 icon = fields[7].GetUInt8();
if (icon == REALM_TYPE_FFA_PVP)
icon = REALM_TYPE_PVP;
if (icon >= MAX_CLIENT_REALM_TYPE)
icon = REALM_TYPE_NORMAL;
- RealmFlags flag = ConvertLegacyRealmFlags(Trinity::Legacy::RealmFlags(fields[6].GetUInt8()));
- uint8 timezone = fields[7].GetUInt8();
- uint8 allowedSecurityLevel = fields[8].GetUInt8();
- RealmPopulationState pop = ConvertLegacyPopulationState(Trinity::Legacy::RealmFlags(fields[6].GetUInt8()), fields[9].GetFloat());
- uint32 build = fields[10].GetUInt32();
- uint8 region = fields[11].GetUInt8();
- uint8 battlegroup = fields[12].GetUInt8();
+ RealmFlags flag = ConvertLegacyRealmFlags(Trinity::Legacy::RealmFlags(fields[8].GetUInt8()));
+ uint8 timezone = fields[9].GetUInt8();
+ uint8 allowedSecurityLevel = fields[10].GetUInt8();
+ RealmPopulationState pop = ConvertLegacyPopulationState(Trinity::Legacy::RealmFlags(fields[8].GetUInt8()), fields[11].GetFloat());
+ uint32 build = fields[12].GetUInt32();
+ uint8 region = fields[13].GetUInt8();
+ uint8 battlegroup = fields[14].GetUInt8();
Battlenet::RealmHandle id{ region, battlegroup, realmId };
- UpdateRealm(*newRealms.try_emplace(id, std::make_shared<Realm>()).first->second, id, build, name, externalAddress->address(), localAddress->address(), port, icon,
+ UpdateRealm(*newRealms.try_emplace(id, std::make_shared<Realm>()).first->second, id, build, name, std::move(addresses), port, icon,
flag, timezone, (allowedSecurityLevel <= SEC_ADMINISTRATOR ? AccountTypes(allowedSecurityLevel) : SEC_ADMINISTRATOR), pop);
newSubRegions.insert(Battlenet::RealmHandle{ region, battlegroup, 0 }.GetAddressString());
+ auto buildAddressesLogText = [&]
+ {
+ std::string text;
+ for (boost::asio::ip::address const& address : newRealms[id]->Addresses)
+ {
+ text += address.to_string();
+ text += ' ';
+ }
+ return text;
+ };
+
if (!existingRealms.erase(id))
- TC_LOG_INFO("realmlist", "Added realm \"{}\" at {}:{}.", name, externalAddressString, port);
+ TC_LOG_INFO("realmlist", "Added realm \"{}\" at {}(port {}).", name, buildAddressesLogText(), port);
else
- TC_LOG_DEBUG("realmlist", "Updating realm \"{}\" at {}:{}.", name, externalAddressString, port);
+ TC_LOG_DEBUG("realmlist", "Updating realm \"{}\" at {}(port {}).", name, buildAddressesLogText(), port);
}
while (result->NextRow());
}
@@ -322,12 +337,14 @@ uint32 RealmList::JoinRealm(uint32 realmAddress, uint32 build, ClientBuild::Vari
if (realm->PopulationLevel == RealmPopulationState::Offline || realm->Build != build || accountSecurityLevel < realm->AllowedSecurityLevel)
return ERROR_USER_SERVER_NOT_PERMITTED_ON_REALM;
+ boost::asio::ip::address addressForClient = realm->GetAddressForClient(clientAddress);
+
JSON::RealmList::RealmListServerIPAddresses serverAddresses;
JSON::RealmList::RealmIPAddressFamily* addressFamily = serverAddresses.add_families();
- addressFamily->set_family(1);
+ addressFamily->set_family(addressForClient.is_v6() ? 2 : 1);
JSON::RealmList::IPAddress* address = addressFamily->add_addresses();
- address->set_ip(realm->GetAddressForClient(clientAddress).to_string());
+ address->set_ip(addressForClient.to_string());
address->set_port(realm->Port);
std::string json = "JSONRealmListServerIPAddresses:" + JSON::Serialize(serverAddresses);
diff --git a/src/server/shared/Realm/RealmList.h b/src/server/shared/Realm/RealmList.h
index 4ece6124a51..ddb42ae7e1f 100644
--- a/src/server/shared/Realm/RealmList.h
+++ b/src/server/shared/Realm/RealmList.h
@@ -76,7 +76,7 @@ private:
void UpdateRealms();
static void UpdateRealm(Realm& realm, Battlenet::RealmHandle const& id, uint32 build, std::string const& name,
- boost::asio::ip::address&& address, boost::asio::ip::address&& localAddr,
+ std::vector<boost::asio::ip::address>&& addresses,
uint16 port, uint8 icon, RealmFlags flag, uint8 timezone, AccountTypes allowedSecurityLevel, RealmPopulationState population);
void FillRealmEntry(Realm const& realm, uint32 clientBuild, AccountTypes accountSecurityLevel, JSON::RealmList::RealmEntry* realmEntry) const;
diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist
index 3a88d4c6785..8ba0fbbceab 100644
--- a/src/server/worldserver/worldserver.conf.dist
+++ b/src/server/worldserver/worldserver.conf.dist
@@ -176,6 +176,7 @@ InstanceServerPort = 8086
#
# BindIP
# Description: Bind world server to IP/hostname.
+# Using IPv6 address (such as "::") will enable both IPv4 and IPv6 connections
# Default: "0.0.0.0" - (Bind to all IPs on the system)
BindIP = "0.0.0.0"