Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 44 additions & 15 deletions .github/workflows/core_windows_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ name: Windows Build (MSVC)
permissions:
contents: read

env:
OPENSSL_VERSION: 3.6.3
OPENSSL_SHA512: 120A2E9A3E8B961484CB94D52F85D48DC2C8AF777B5B3B9E26BF49B4408797281F7C0FB2D543FD7796B3C3232ADFAA0F675E7122C0E5C4225B3B02E767197AC6

on:
push:
branches: [master, devel]
Expand Down Expand Up @@ -34,20 +38,50 @@ jobs:
id: cache-openssl
uses: actions/cache@v4
with:
path: |
C:\Program Files\OpenSSL
C:\Program Files\OpenSSL-Win64
key: ${{ runner.os }}-openssl-v1
path: C:\Program Files\OpenSSL-Win64
key: ${{ runner.os }}-openssl-${{ env.OPENSSL_VERSION }}-v1


- name: Install full OpenSSL (developer)
if: steps.cache-openssl.outputs.cache-hit != 'true'
shell: powershell
run: |
Write-Host "Installing latest full OpenSSL for development..."
choco upgrade openssl -y --no-progress
$ver = & openssl version
Write-Host "Installed OpenSSL version: $ver"
$versionSlug = $env:OPENSSL_VERSION.Replace('.', '_')
$installer = Join-Path $env:RUNNER_TEMP "Win64OpenSSL-$versionSlug.exe"
$url = "https://slproweb.com/download/Win64OpenSSL-$versionSlug.exe"

Write-Host "Downloading OpenSSL $env:OPENSSL_VERSION for development..."
curl.exe --fail --location --retry 3 --output $installer $url
if ($LASTEXITCODE -ne 0) {
throw "OpenSSL download failed with exit code $LASTEXITCODE"
}

$actualHash = (Get-FileHash -Algorithm SHA512 $installer).Hash
if ($actualHash -ne $env:OPENSSL_SHA512) {
throw "OpenSSL installer checksum mismatch: $actualHash"
}

$installArgs = '/VERYSILENT /SUPPRESSMSGBOXES /NORESTART /SP- /DIR="C:\Program Files\OpenSSL-Win64"'
$process = Start-Process -FilePath $installer -ArgumentList $installArgs -Wait -PassThru
if ($process.ExitCode -ne 0) {
throw "OpenSSL installer failed with exit code $($process.ExitCode)"
}


- name: Verify OpenSSL major version
shell: powershell
run: |
$openssl = "C:\Program Files\OpenSSL-Win64\bin\openssl.exe"

if (-not (Test-Path $openssl)) {
throw "OpenSSL executable not found at $openssl"
}

$version = & $openssl version
Write-Host "Installed OpenSSL version: $version"
if ($version -notmatch '^OpenSSL 3\.') {
throw "Windows CI requires OpenSSL 3.x, found: $version"
}


- name: Setup Windows 10 SDK
Expand All @@ -59,18 +93,13 @@ jobs:
- name: Configure OpenSSL environment
shell: bash
run: |
if [ -d "C:/Program Files/OpenSSL/lib/VC" ]; then
echo "OPENSSL_ROOT_DIR=C:/Program Files/OpenSSL" >> $GITHUB_ENV
echo "OPENSSL_INCLUDE_DIR=C:/Program Files/OpenSSL/include" >> $GITHUB_ENV
echo "OPENSSL_CRYPTO_LIBRARY=C:/Program Files/OpenSSL/lib/VC/libcrypto64MT.lib" >> $GITHUB_ENV
echo "OPENSSL_SSL_LIBRARY=C:/Program Files/OpenSSL/lib/VC/libssl64MT.lib" >> $GITHUB_ENV
elif [ -d "C:/Program Files/OpenSSL-Win64/lib/VC" ]; then
if [ -d "C:/Program Files/OpenSSL-Win64/lib/VC" ]; then
echo "OPENSSL_ROOT_DIR=C:/Program Files/OpenSSL-Win64" >> $GITHUB_ENV
echo "OPENSSL_INCLUDE_DIR=C:/Program Files/OpenSSL-Win64/include" >> $GITHUB_ENV
echo "OPENSSL_CRYPTO_LIBRARY=C:/Program Files/OpenSSL-Win64/lib/VC/libcrypto64MT.lib" >> $GITHUB_ENV
echo "OPENSSL_SSL_LIBRARY=C:/Program Files/OpenSSL-Win64/lib/VC/libssl64MT.lib" >> $GITHUB_ENV
else
echo "::error::OpenSSL not found in either expected location"
echo "::error::OpenSSL developer libraries not found"
exit 1
fi

Expand Down
11 changes: 11 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ option(PCH "Enable precompiled headers" ON)
option(DEBUG "Enable debug build (only on non IDEs)" OFF)
option(WITHOUT_GIT "Disable Git revision detection" OFF)
option(BUILD_AH_SERVICE "Build the out-of-process auction-house service" ON)
option(BUILD_TESTING "Build focused regression tests" OFF)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Run the new regression tests in CI

Because this new option defaults to OFF and neither the Windows nor Linux workflow configure commands pass -DBUILD_TESTING=ON or run ctest, the regression targets added in this change are skipped by normal PR CI. That leaves the IOCP/provider/lease coverage described by the patch unused unless a developer opts in locally; enable the option in CI or default it on when the required dependencies are present.

Useful? React with 👍 / 👎.

#==================================================================================
message("")
message(
Expand Down Expand Up @@ -146,6 +147,11 @@ endif()

find_package(Threads REQUIRED)
find_package(OpenSSL 3.0 REQUIRED)
# OpenSSL 4.x support is intentionally deferred until the OpenSSL 4.2 LTS migration.
if(OPENSSL_VERSION VERSION_GREATER_EQUAL "4.0.0")
message(FATAL_ERROR
"Unsupported OpenSSL ${OPENSSL_VERSION}: MaNGOS currently requires >=3.0.0 and <4.0.0")
endif()
find_package(MySQL REQUIRED)

# =============================================================================
Expand Down Expand Up @@ -218,6 +224,11 @@ else()
message(WARNING "Source directory 'src' not found. Nothing to build.")
endif()

if(BUILD_TESTING)
enable_testing()
add_subdirectory(tests)
endif()

# =============================================================================
# Final info and summary
# =============================================================================
Expand Down
7 changes: 6 additions & 1 deletion src/game/Server/WorldSession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ bool WorldSessionFilter::Process(WorldPacket* packet)
/// WorldSession constructor
WorldSession::WorldSession(uint32 id, std::shared_ptr<WorldSocket> sock, AccountTypes sec, time_t mute_time, LocaleConstant locale)
: m_muteTime(mute_time),
_player(NULL), m_Socket(std::move(sock)), _security(sec), _accountId(id), _warden(NULL), _build(0), _logoutTime(0),
_player(NULL), m_OwningSocket(sock), m_Socket(std::move(sock)), _security(sec), _accountId(id), _warden(NULL), _build(0), _logoutTime(0),
m_inQueue(false), m_playerLoading(false), m_playerLogout(false), m_playerRecentlyLogout(false), m_playerSave(false),
m_sessionDbcLocale(sWorld.GetAvailableDbcLocale(locale)), m_sessionDbLocaleIndex(sObjectMgr.GetIndexForLocale(locale)),
m_latency(0), m_clientTimeDelay(0), m_tutorialState(TUTORIALDATA_UNCHANGED), m_npcWatchLastGuid()
Expand All @@ -161,6 +161,11 @@ WorldSession::WorldSession(uint32 id, std::shared_ptr<WorldSocket> sock, Account
/// WorldSession destructor
WorldSession::~WorldSession()
{
if (std::shared_ptr<WorldSocket> socket = m_OwningSocket.lock())
{
socket->DetachSessionAndWait();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Close the socket before making the session unauthenticated

When a WorldSession is destroyed while its socket is still open (for example a duplicate/login-loading removal or shutdown cleanup), this detaches the session before CloseSocket() runs later in the destructor. During the intervening logout work, WorldSocket::ProcessIncoming() sees no session and accepts a new CMSG_AUTH_SESSION on the same connection instead of rejecting it as a repeated auth, after which the destructor closes/detaches that newly published session. Mark the socket closed or close it before unpublishing the session while still waiting for active leases before freeing the object.

Useful? React with 👍 / 👎.

}

///- unload player if not unloaded
if (_player)
{
Expand Down
1 change: 1 addition & 0 deletions src/game/Server/WorldSession.h
Original file line number Diff line number Diff line change
Expand Up @@ -893,6 +893,7 @@ class WorldSession
void LogUnprocessedTail(WorldPacket* packet);

Player* _player;
std::weak_ptr<WorldSocket> m_OwningSocket;
std::shared_ptr<WorldSocket> m_Socket;
std::string m_Address;

Expand Down
34 changes: 19 additions & 15 deletions src/game/Server/WorldSocket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,7 @@ std::atomic<uint32> WorldSocket::s_openConnections{0};
#endif

WorldSocket::WorldSocket()
: m_Session(0),
m_closed(false),
: m_closed(false),
m_recvBuf(),
m_headerPending(false),
m_recvOpcode(0),
Expand All @@ -98,16 +97,19 @@ WorldSocket::~WorldSocket()
#endif
}

WorldSession* WorldSocket::GetSession()
WorldSocket::SessionLease WorldSocket::GetSession()
{
std::lock_guard<std::mutex> guard(m_SessionLock);
return m_Session;
return m_session.acquire();
}

void WorldSocket::SetSession(WorldSession* session)
{
std::lock_guard<std::mutex> guard(m_SessionLock);
m_Session = session;
m_session.publish(session);
}

void WorldSocket::DetachSessionAndWait()
{
m_session.detachAndWait();
}

void WorldSocket::CloseSocket()
Expand All @@ -117,7 +119,7 @@ void WorldSocket::CloseSocket()
return;
}

SetSession(NULL);
m_session.detach();

if (m_closer)
{
Expand Down Expand Up @@ -185,7 +187,7 @@ std::vector<uint8_t> WorldSocket::onConnect()
void WorldSocket::onClose()
{
m_closed.store(true);
SetSession(NULL);
m_session.detach();
}

std::vector<uint8_t> WorldSocket::onData(const uint8_t* data, size_t len)
Expand Down Expand Up @@ -287,7 +289,9 @@ int WorldSocket::ProcessIncoming(WorldPacket* new_pct)
sLog.outWorldPacketDump(0, new_pct->GetOpcode(), new_pct->GetOpcodeName(), new_pct, true);
}

WorldSession* session = GetSession();
// HandlePing acquires its own narrow leases. In particular, do not keep a
// ProcessIncoming lease alive across its close-capable paths.
SessionLease session = opcode == CMSG_PING ? SessionLease{} : GetSession();

try
{
Expand All @@ -304,7 +308,7 @@ int WorldSocket::ProcessIncoming(WorldPacket* new_pct)
#ifdef ENABLE_ELUNA
if (Eluna* e = sWorld.GetEluna())
{
if (!e->OnPacketReceive(session, *new_pct))
if (!e->OnPacketReceive(session.get(), *new_pct))
{
return 0;
}
Expand All @@ -316,12 +320,12 @@ int WorldSocket::ProcessIncoming(WorldPacket* new_pct)
#ifdef ENABLE_ELUNA
if (Eluna* e = sWorld.GetEluna())
{
e->OnPacketReceive(session, *new_pct);
e->OnPacketReceive(session.get(), *new_pct);
}
#endif /* ENABLE_ELUNA */
return 0;
default:
if (session != NULL)
if (session)
{
aptr.release();
session->QueuePacket(new_pct);
Expand Down Expand Up @@ -591,7 +595,7 @@ int WorldSocket::HandlePing(WorldPacket& recvPacket)

if (max_count && m_OverSpeedPings > max_count)
{
WorldSession* session = GetSession();
SessionLease session = GetSession();
if (session && session->GetSecurity() == SEC_PLAYER)
{
sLog.outError("WorldSocket::HandlePing: Player kicked for overspeeded pings address = %s",
Expand All @@ -606,7 +610,7 @@ int WorldSocket::HandlePing(WorldPacket& recvPacket)
}
}

WorldSession* session = GetSession();
SessionLease session = GetSession();
if (session)
{
session->SetLatency(latency);
Expand Down
12 changes: 8 additions & 4 deletions src/game/Server/WorldSocket.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "Common.h"
#include "Auth/AuthCrypt.h"
#include "Auth/Sha1.h"
#include "Threading/LeasedPtr.h"

#include "net/ISession.hpp"

Expand Down Expand Up @@ -92,21 +93,24 @@ class WorldSocket : public net::ISession

private:

friend class WorldSession;
using SessionLease = LeasedPtr<WorldSession>::Lease;

std::vector<uint8_t> EncodePacket(const WorldPacket& pct);
int ProcessIncoming(WorldPacket* new_pct);
int HandleAuthSession(WorldPacket& recvPacket);
int HandlePing(WorldPacket& recvPacket);

WorldSession* GetSession();
void SetSession(WorldSession* session);
SessionLease GetSession();
void SetSession(WorldSession* session);
void DetachSessionAndWait();

std::string m_Address;

AuthCrypt m_Crypt;
std::mutex m_CryptSendLock; ///< serialises header encryption on send

std::mutex m_SessionLock;
WorldSession* m_Session;
LeasedPtr<WorldSession> m_session;

std::atomic<bool> m_closed;

Expand Down
2 changes: 1 addition & 1 deletion src/mangosd/mangosd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ int main(int argc, char** argv)
if (!providerManager.IsInitialized())
{
Log::WaitBeforeContinueIfNeed();
return 0;
return 1;
}

///- Set progress bars show mode
Expand Down
2 changes: 1 addition & 1 deletion src/realmd
Submodule realmd updated 1 files
+1 −1 Main.cpp
Loading
Loading