feat(Core): replace ACE network with Boost.Asio (#6574)

This commit is contained in:
Kargatum
2021-07-16 15:43:56 +07:00
committed by GitHub
parent 7449496bb5
commit 8568c4fb33
64 changed files with 3242 additions and 4712 deletions

View File

@@ -16,9 +16,9 @@
using boost::asio::ip::tcp;
#if BOOST_VERSION >= 106600
#define WARHEAD_MAX_LISTEN_CONNECTIONS boost::asio::socket_base::max_listen_connections
#define ACORE_MAX_LISTEN_CONNECTIONS boost::asio::socket_base::max_listen_connections
#else
#define WARHEAD_MAX_LISTEN_CONNECTIONS boost::asio::socket_base::max_connections
#define ACORE_MAX_LISTEN_CONNECTIONS boost::asio::socket_base::max_connections
#endif
class AsyncAcceptor
@@ -72,7 +72,7 @@ public:
return false;
}
#if WARHEAD_PLATFORM != WARHEAD_PLATFORM_WINDOWS
#if AC_PLATFORM != AC_PLATFORM_WINDOWS
_acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true), errorCode);
if (errorCode)
{
@@ -88,7 +88,7 @@ public:
return false;
}
_acceptor.listen(WARHEAD_MAX_LISTEN_CONNECTIONS, errorCode);
_acceptor.listen(ACORE_MAX_LISTEN_CONNECTIONS, errorCode);
if (errorCode)
{
LOG_INFO("network", "Failed to start listening on %s:%u %s", _endpoint.address().to_string().c_str(), _endpoint.port(), errorCode.message().c_str());

View File

@@ -1,277 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*/
#include "Log.h"
#include "RealmSocket.h"
#include <ace/INET_Addr.h>
#include <ace/OS_NS_string.h>
RealmSocket::Session::Session() = default;
RealmSocket::Session::~Session() = default;
RealmSocket::RealmSocket() :
input_buffer_(4096),
_remoteAddress()
{
reference_counting_policy().value(ACE_Event_Handler::Reference_Counting_Policy::ENABLED);
msg_queue()->high_water_mark(8 * 1024 * 1024);
msg_queue()->low_water_mark(8 * 1024 * 1024);
}
RealmSocket::~RealmSocket()
{
if (msg_queue())
msg_queue()->close();
// delete RealmSocketObject must never be called from our code.
closing_ = true;
delete session_;
peer().close();
}
int RealmSocket::open(void* arg)
{
ACE_INET_Addr addr;
if (peer().get_remote_addr(addr) == -1)
{
LOG_ERROR("network", "Error %s while opening realm socket!", ACE_OS::strerror(errno));
return -1;
}
_remoteAddress = addr.get_host_addr();
_remotePort = addr.get_port_number();
// Register with ACE Reactor
if (Base::open(arg) == -1)
return -1;
if (session_)
session_->OnAccept();
// reactor takes care of the socket from now on
remove_reference();
return 0;
}
int RealmSocket::close(u_long)
{
shutdown();
closing_ = true;
remove_reference();
return 0;
}
const std::string& RealmSocket::getRemoteAddress() const
{
return _remoteAddress;
}
uint16 RealmSocket::getRemotePort() const
{
return _remotePort;
}
size_t RealmSocket::recv_len() const
{
return input_buffer_.length();
}
bool RealmSocket::recv_soft(char* buf, size_t len)
{
if (input_buffer_.length() < len)
return false;
ACE_OS::memcpy(buf, input_buffer_.rd_ptr(), len);
return true;
}
bool RealmSocket::recv(char* buf, size_t len)
{
bool ret = recv_soft(buf, len);
if (ret)
recv_skip(len);
return ret;
}
void RealmSocket::recv_skip(size_t len)
{
input_buffer_.rd_ptr(len);
}
ssize_t RealmSocket::noblk_send(ACE_Message_Block& message_block)
{
const size_t len = message_block.length();
if (len == 0)
return -1;
// Try to send the message directly.
#ifdef MSG_NOSIGNAL
ssize_t n = peer().send(message_block.rd_ptr(), len, MSG_NOSIGNAL);
#else
ssize_t n = peer().send(message_block.rd_ptr(), len);
#endif // MSG_NOSIGNAL
if (n < 0)
{
if (errno == EWOULDBLOCK) // Blocking signal
return 0;
else // Error happened
return -1;
}
else if (n == 0)
{
// Can this happen ?
return -1;
}
// return bytes transmitted
return n;
}
bool RealmSocket::send(const char* buf, size_t len)
{
if (buf == nullptr || len == 0)
return true;
ACE_Data_Block db(len, ACE_Message_Block::MB_DATA, (const char*)buf, nullptr, nullptr, ACE_Message_Block::DONT_DELETE, nullptr);
ACE_Message_Block message_block(&db, ACE_Message_Block::DONT_DELETE, nullptr);
message_block.wr_ptr(len);
if (msg_queue()->is_empty())
{
// Try to send it directly.
ssize_t n = noblk_send(message_block);
if (n < 0)
return false;
size_t un = size_t(n);
if (un == len)
return true;
// fall down
message_block.rd_ptr(un);
}
ACE_Message_Block* mb = message_block.clone();
if (msg_queue()->enqueue_tail(mb, (ACE_Time_Value*)(&ACE_Time_Value::zero)) == -1)
{
mb->release();
return false;
}
if (reactor()->schedule_wakeup(this, ACE_Event_Handler::WRITE_MASK) == -1)
return false;
return true;
}
int RealmSocket::handle_output(ACE_HANDLE)
{
if (closing_)
return -1;
ACE_Message_Block* mb = nullptr;
if (msg_queue()->is_empty())
{
reactor()->cancel_wakeup(this, ACE_Event_Handler::WRITE_MASK);
return 0;
}
if (msg_queue()->dequeue_head(mb, (ACE_Time_Value*)(&ACE_Time_Value::zero)) == -1)
return -1;
ssize_t n = noblk_send(*mb);
if (n < 0)
{
mb->release();
return -1;
}
else if (size_t(n) == mb->length())
{
mb->release();
return 1;
}
else
{
mb->rd_ptr(n);
if (msg_queue()->enqueue_head(mb, (ACE_Time_Value*) &ACE_Time_Value::zero) == -1)
{
mb->release();
return -1;
}
return 0;
}
ACE_NOTREACHED(return -1);
}
int RealmSocket::handle_close(ACE_HANDLE h, ACE_Reactor_Mask)
{
// As opposed to WorldSocket::handle_close, we don't need locks here.
closing_ = true;
if (h == ACE_INVALID_HANDLE)
peer().close_writer();
if (session_)
session_->OnClose();
reactor()->remove_handler(this, ACE_Event_Handler::DONT_CALL | ACE_Event_Handler::ALL_EVENTS_MASK);
return 0;
}
int RealmSocket::handle_input(ACE_HANDLE)
{
if (closing_)
return -1;
const ssize_t space = input_buffer_.space();
ssize_t n = peer().recv(input_buffer_.wr_ptr(), space);
if (n < 0)
return errno == EWOULDBLOCK ? 0 : -1;
else if (n == 0) // EOF
return -1;
input_buffer_.wr_ptr((size_t)n);
if (session_ != nullptr)
{
session_->OnRead();
input_buffer_.crunch();
}
// return 1 in case there is more data to read from OS
return n == space ? 1 : 0;
}
void RealmSocket::set_session(Session* session)
{
delete session_;
session_ = session;
}

View File

@@ -1,67 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*/
#ifndef __REALMSOCKET_H__
#define __REALMSOCKET_H__
#include "Common.h"
#include <ace/Message_Block.h>
#include <ace/SOCK_Stream.h>
#include <ace/Svc_Handler.h>
#include <ace/Synch_Traits.h>
class RealmSocket : public ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH>
{
private:
typedef ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> Base;
public:
class Session
{
public:
Session();
virtual ~Session();
virtual void OnRead() = 0;
virtual void OnAccept() = 0;
virtual void OnClose() = 0;
};
RealmSocket();
~RealmSocket() override;
[[nodiscard]] size_t recv_len() const;
bool recv_soft(char* buf, size_t len);
bool recv(char* buf, size_t len);
void recv_skip(size_t len);
bool send(const char* buf, size_t len);
[[nodiscard]] const std::string& getRemoteAddress() const;
[[nodiscard]] uint16 getRemotePort() const;
int open(void*) override;
int close(u_long) override;
int handle_input(ACE_HANDLE = ACE_INVALID_HANDLE) override;
int handle_output(ACE_HANDLE = ACE_INVALID_HANDLE) override;
int handle_close(ACE_HANDLE = ACE_INVALID_HANDLE, ACE_Reactor_Mask = ACE_Event_Handler::ALL_EVENTS_MASK) override;
void set_session(Session* session);
private:
ssize_t noblk_send(ACE_Message_Block& message_block);
ACE_Message_Block input_buffer_;
Session* session_{nullptr};
std::string _remoteAddress;
uint16 _remotePort{0};
};
#endif /* __REALMSOCKET_H__ */

View File

@@ -0,0 +1,44 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#include "Realm.h"
#include "IpAddress.h"
#include "IpNetwork.h"
#include <boost/asio/ip/tcp.hpp>
boost::asio::ip::tcp_endpoint Realm::GetAddressForClient(boost::asio::ip::address const& clientAddr) const
{
boost::asio::ip::address realmIp;
// Attempt to send best address for client
if (clientAddr.is_loopback())
{
// Try guessing if realm is also connected locally
if (LocalAddress->is_loopback() || ExternalAddress->is_loopback())
{
realmIp = clientAddr;
}
else
{
// Assume that user connecting from the machine that bnetserver is located on
// has all realms available in his local network
realmIp = *LocalAddress;
}
}
else
{
if (clientAddr.is_v4() && Acore::Net::IsInNetwork(LocalAddress->to_v4(), LocalSubnetMask->to_v4(), clientAddr.to_v4()))
{
realmIp = *LocalAddress;
}
else
{
realmIp = *ExternalAddress;
}
}
// Return external IP
return boost::asio::ip::tcp_endpoint(realmIp, Port);
}

View File

@@ -6,9 +6,8 @@
#ifndef Realm_h__
#define Realm_h__
#include "AsioHacksFwd.h"
#include "Common.h"
// #include "AsioHacksFwd.h"
#include <ace/INET_Addr.h>
enum RealmFlags
{
@@ -56,9 +55,9 @@ struct AC_SHARED_API Realm
{
RealmHandle Id;
uint32 Build;
std::unique_ptr<ACE_INET_Addr> ExternalAddress;
std::unique_ptr<ACE_INET_Addr> LocalAddress;
std::unique_ptr<ACE_INET_Addr> LocalSubnetMask;
std::unique_ptr<boost::asio::ip::address> ExternalAddress;
std::unique_ptr<boost::asio::ip::address> LocalAddress;
std::unique_ptr<boost::asio::ip::address> LocalSubnetMask;
uint16 Port;
std::string Name;
uint8 Type;
@@ -67,7 +66,7 @@ struct AC_SHARED_API Realm
AccountTypes AllowedSecurityLevel;
float PopulationLevel;
// boost::asio::ip::tcp_endpoint GetAddressForClient(boost::asio::ip::address const& clientAddr) const;
boost::asio::ip::tcp_endpoint GetAddressForClient(boost::asio::ip::address const& clientAddr) const;
};
#endif // Realm_h__

View File

@@ -6,28 +6,38 @@
#include "RealmList.h"
#include "DatabaseEnv.h"
#include "DeadlineTimer.h"
#include "IoContext.h"
#include "Log.h"
#include "Optional.h"
#include "Resolver.h"
#include "Util.h"
#include <boost/asio/ip/tcp.hpp>
RealmList::RealmList() :
_updateInterval(0) { }
RealmList* RealmList::instance()
RealmList* RealmList::Instance()
{
static RealmList instance;
return &instance;
}
// Load the realm list from the database
void RealmList::Initialize(uint32 updateInterval)
void RealmList::Initialize(Acore::Asio::IoContext& ioContext, uint32 updateInterval)
{
_updateInterval = updateInterval;
_updateTimer = std::make_unique<Acore::Asio::DeadlineTimer>(ioContext);
_resolver = std::make_unique<Acore::Asio::Resolver>(ioContext);
LoadBuildInfo();
// Get the content of the realmlist table in the database
UpdateRealms();
UpdateRealms(boost::system::error_code());
}
void RealmList::Close()
{
_updateTimer->cancel();
}
void RealmList::LoadBuildInfo()
@@ -72,7 +82,7 @@ void RealmList::LoadBuildInfo()
}
void RealmList::UpdateRealm(RealmHandle const& id, uint32 build, std::string const& name,
ACE_INET_Addr&& address, ACE_INET_Addr&& localAddr, ACE_INET_Addr&& localSubmask,
boost::asio::ip::address&& address, boost::asio::ip::address&& localAddr, boost::asio::ip::address&& localSubmask,
uint16 port, uint8 icon, RealmFlags flag, uint8 timezone, AccountTypes allowedSecurityLevel, float population)
{
// Create new if not exist or update existed
@@ -89,38 +99,30 @@ void RealmList::UpdateRealm(RealmHandle const& id, uint32 build, std::string con
if (!realm.ExternalAddress || *realm.ExternalAddress != address)
{
realm.ExternalAddress = std::make_unique<ACE_INET_Addr>(std::move(address));
realm.ExternalAddress = std::make_unique<boost::asio::ip::address>(std::move(address));
}
if (!realm.LocalAddress || *realm.LocalAddress != localAddr)
{
realm.LocalAddress = std::make_unique<ACE_INET_Addr>(std::move(localAddr));
realm.LocalAddress = std::make_unique<boost::asio::ip::address>(std::move(localAddr));
}
if (!realm.LocalSubnetMask || *realm.LocalSubnetMask != localSubmask)
{
realm.LocalSubnetMask = std::make_unique<ACE_INET_Addr>(std::move(localSubmask));
realm.LocalSubnetMask = std::make_unique<boost::asio::ip::address>(std::move(localSubmask));
}
realm.Port = port;
}
void RealmList::UpdateIfNeed()
void RealmList::UpdateRealms(boost::system::error_code const& error)
{
// maybe disabled or updated recently
if (!_updateInterval || _nextUpdateTime > time(nullptr))
if (error)
{
// Skip update if have errors
return;
}
_nextUpdateTime = time(nullptr) + _updateInterval;
// Get the content of the realmlist table in the database
UpdateRealms();
}
void RealmList::UpdateRealms()
{
LOG_DEBUG("server.authserver", "Updating Realm List...");
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_REALMLIST);
@@ -139,70 +141,87 @@ void RealmList::UpdateRealms()
{
do
{
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::string localSubmaskString = fields[4].GetString();
uint16 port = fields[5].GetUInt16();
Optional<ACE_INET_Addr> externalAddress = ACE_INET_Addr(port, externalAddressString.c_str(), AF_INET);
if (!externalAddress)
try
{
LOG_ERROR("server.authserver", "Could not resolve address %s for realm \"%s\" id %u", externalAddressString.c_str(), name.c_str(), realmId);
continue;
}
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::string localSubmaskString = fields[4].GetString();
uint16 port = fields[5].GetUInt16();
Optional<ACE_INET_Addr> localAddress = ACE_INET_Addr(port, localAddressString.c_str(), AF_INET);
if (!localAddress)
Optional<boost::asio::ip::tcp::endpoint> externalAddress = _resolver->Resolve(boost::asio::ip::tcp::v4(), externalAddressString, "");
if (!externalAddress)
{
LOG_ERROR("server.authserver", "Could not resolve address %s for realm \"%s\" id %u", externalAddressString.c_str(), name.c_str(), realmId);
continue;
}
Optional<boost::asio::ip::tcp::endpoint> localAddress = _resolver->Resolve(boost::asio::ip::tcp::v4(), localAddressString, "");
if (!localAddress)
{
LOG_ERROR("server.authserver", "Could not resolve localAddress %s for realm \"%s\" id %u", localAddressString.c_str(), name.c_str(), realmId);
continue;
}
Optional<boost::asio::ip::tcp::endpoint> localSubmask = _resolver->Resolve(boost::asio::ip::tcp::v4(), localSubmaskString, "");
if (!localSubmask)
{
LOG_ERROR("server.authserver", "Could not resolve localSubnetMask %s for realm \"%s\" id %u", localSubmaskString.c_str(), name.c_str(), realmId);
continue;
}
uint8 icon = fields[6].GetUInt8();
if (icon == REALM_TYPE_FFA_PVP)
{
icon = REALM_TYPE_PVP;
}
if (icon >= MAX_CLIENT_REALM_TYPE)
{
icon = REALM_TYPE_NORMAL;
}
RealmFlags flag = RealmFlags(fields[7].GetUInt8());
uint8 timezone = fields[8].GetUInt8();
uint8 allowedSecurityLevel = fields[9].GetUInt8();
float pop = fields[10].GetFloat();
uint32 build = fields[11].GetUInt32();
RealmHandle id{ realmId };
UpdateRealm(id, build, name, externalAddress->address(), localAddress->address(), localSubmask->address(), port, icon, flag,
timezone, (allowedSecurityLevel <= SEC_ADMINISTRATOR ? AccountTypes(allowedSecurityLevel) : SEC_ADMINISTRATOR), pop);
if (!existingRealms.count(id))
{
LOG_INFO("server.authserver", "Added realm \"%s\" at %s:%u.", name.c_str(), externalAddressString.c_str(), port);
}
else
{
LOG_DEBUG("server.authserver", "Updating realm \"%s\" at %s:%u.", name.c_str(), externalAddressString.c_str(), port);
}
existingRealms.erase(id);
}
catch (std::exception const& ex)
{
LOG_ERROR("server.authserver", "Could not resolve localAddress %s for realm \"%s\" id %u", localAddressString.c_str(), name.c_str(), realmId);
continue;
LOG_ERROR("server.authserver", "Realmlist::UpdateRealms has thrown an exception: %s", ex.what());
ABORT();
}
Optional<ACE_INET_Addr> localSubmask = ACE_INET_Addr(0, localSubmaskString.c_str(), AF_INET);
if (!localSubmask)
{
LOG_ERROR("server.authserver", "Could not resolve localSubnetMask %s for realm \"%s\" id %u", localSubmaskString.c_str(), name.c_str(), realmId);
continue;
}
uint8 icon = fields[6].GetUInt8();
if (icon == REALM_TYPE_FFA_PVP)
{
icon = REALM_TYPE_PVP;
}
if (icon >= MAX_CLIENT_REALM_TYPE)
{
icon = REALM_TYPE_NORMAL;
}
RealmFlags flag = RealmFlags(fields[7].GetUInt8());
uint8 timezone = fields[8].GetUInt8();
uint8 allowedSecurityLevel = fields[9].GetUInt8();
float pop = fields[10].GetFloat();
uint32 build = fields[11].GetUInt32();
RealmHandle id{ realmId };
UpdateRealm(id, build, name, std::move(externalAddress.value()), std::move(localAddress.value()), std::move(localSubmask.value()), port, icon, flag,
timezone, (allowedSecurityLevel <= SEC_ADMINISTRATOR ? AccountTypes(allowedSecurityLevel) : SEC_ADMINISTRATOR), pop);
if (!existingRealms.count(id))
{
LOG_INFO("server.authserver", "Added realm \"%s\" at %s:%u.", name.c_str(), externalAddressString.c_str(), port);
}
else
{
LOG_DEBUG("server.authserver", "Updating realm \"%s\" at %s:%u.", name.c_str(), externalAddressString.c_str(), port);
}
existingRealms.erase(id);
} while (result->NextRow());
}
for (auto itr = existingRealms.begin(); itr != existingRealms.end(); ++itr)
LOG_INFO("server.authserver", "Removed realm \"%s\".", itr->second.c_str());
if (_updateInterval)
{
_updateTimer->expires_from_now(boost::posix_time::seconds(_updateInterval));
_updateTimer->async_wait(std::bind(&RealmList::UpdateRealms, this, std::placeholders::_1));
}
}
Realm const* RealmList::GetRealm(RealmHandle const& id) const

View File

@@ -11,8 +11,8 @@
#include "Realm.h"
#include <array>
#include <map>
#include <vector>
#include <unordered_set>
#include <vector>
struct RealmBuildInfo
{
@@ -25,19 +25,21 @@ struct RealmBuildInfo
std::array<uint8, 20> MacHash;
};
namespace boost::system
{
class error_code;
}
/// Storage object for the list of realms on the server
class AC_SHARED_API RealmList
{
public:
typedef std::map<RealmHandle, Realm> RealmMap;
RealmList();
~RealmList() = default;
static RealmList* Instance();
static RealmList* instance();
void Initialize(uint32 updateInterval);
void UpdateIfNeed();
void Initialize(Acore::Asio::IoContext& ioContext, uint32 updateInterval);
void Close();
RealmMap const& GetRealms() const { return _realms; }
Realm const* GetRealm(RealmHandle const& id) const;
@@ -45,18 +47,22 @@ public:
RealmBuildInfo const* GetBuildInfo(uint32 build) const;
private:
RealmList();
~RealmList() = default;
void LoadBuildInfo();
void UpdateRealms();
void UpdateRealms(boost::system::error_code const& error);
void UpdateRealm(RealmHandle const& id, uint32 build, std::string const& name,
ACE_INET_Addr&& address, ACE_INET_Addr&& localAddr, ACE_INET_Addr&& localSubmask,
boost::asio::ip::address&& address, boost::asio::ip::address&& localAddr, boost::asio::ip::address&& localSubmask,
uint16 port, uint8 icon, RealmFlags flag, uint8 timezone, AccountTypes allowedSecurityLevel, float population);
std::vector<RealmBuildInfo> _builds;
RealmMap _realms;
uint32 _updateInterval;
time_t _nextUpdateTime;
std::unique_ptr<Acore::Asio::DeadlineTimer> _updateTimer;
std::unique_ptr<Acore::Asio::Resolver> _resolver;
};
#define sRealmList RealmList::instance()
#define sRealmList RealmList::Instance()
#endif