server/src/realmd/PatchHandler.cpp
2020-02-17 09:22:36 +00:00

221 lines
5 KiB
C++

/**
* MaNGOS is a full featured server for World of Warcraft, supporting
* the following clients: 1.12.x, 2.4.3, 3.3.5a, 4.3.4a and 5.4.8
*
* Copyright (C) 2005-2015 MaNGOS project <http://getmangos.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* World of Warcraft, and all World of Warcraft or Warcraft art, images,
* and lore are copyrighted by Blizzard Entertainment, Inc.
*/
/** \file
\ingroup realmd
*/
#include "Common.h"
#include "PatchHandler.h"
#include "AuthCodes.h"
#include "Log.h"
#include <ace/OS_NS_sys_socket.h>
#include <ace/OS_NS_dirent.h>
#include <ace/OS_NS_errno.h>
#include <ace/OS_NS_unistd.h>
#include <ace/os_include/netinet/os_tcp.h>
#ifndef MSG_NOSIGNAL
#define MSG_NOSIGNAL 0
#endif
#if defined( __GNUC__ )
#pragma pack(1)
#else
#pragma pack(push,1)
#endif
struct Chunk
{
ACE_UINT8 cmd;
ACE_UINT16 data_size;
ACE_UINT8 data[4096]; // 4096 - page size on most arch
};
#if defined( __GNUC__ )
#pragma pack()
#else
#pragma pack(pop)
#endif
PatchHandler::PatchHandler(ACE_HANDLE socket, ACE_HANDLE patch)
{
reactor(NULL);
set_handle(socket);
patch_fd_ = patch;
}
PatchHandler::~PatchHandler()
{
if (patch_fd_ != ACE_INVALID_HANDLE)
{ ACE_OS::close(patch_fd_); }
}
int PatchHandler::open(void*)
{
if (get_handle() == ACE_INVALID_HANDLE || patch_fd_ == ACE_INVALID_HANDLE)
{ return -1; }
int nodelay = 0;
if (-1 == peer().set_option(ACE_IPPROTO_TCP,
TCP_NODELAY,
&nodelay,
sizeof(nodelay)))
{
return -1;
}
#if defined(TCP_CORK)
int cork = 1;
if (-1 == peer().set_option(ACE_IPPROTO_TCP,
TCP_CORK,
&cork,
sizeof(cork)))
{
return -1;
}
#endif // TCP_CORK
(void) peer().disable(ACE_NONBLOCK);
return activate(THR_NEW_LWP | THR_DETACHED | THR_INHERIT_SCHED);
}
int PatchHandler::svc(void)
{
// Do 1 second sleep, similar to the one in game/WorldSocket.cpp
// Seems client have problems with too fast sends.
ACE_OS::sleep(1);
int flags = MSG_NOSIGNAL;
Chunk data;
data.cmd = CMD_XFER_DATA;
ssize_t r;
while ((r = ACE_OS::read(patch_fd_, data.data, sizeof(data.data))) > 0)
{
data.data_size = (ACE_UINT16)r;
if (peer().send((const char*)&data,
((size_t) r) + sizeof(data) - sizeof(data.data),
flags) == -1)
{
return -1;
}
}
if (r == -1)
{
return -1;
}
return 0;
}
PatchCache::~PatchCache()
{
for (Patches::iterator i = patches_.begin(); i != patches_.end(); ++i)
{ delete i->second; }
}
PatchCache::PatchCache()
{
LoadPatchesInfo();
}
PatchCache* PatchCache::instance()
{
return ACE_Singleton<PatchCache, ACE_Thread_Mutex>::instance();
}
void PatchCache::LoadPatchMD5(const char* szFileName)
{
// Try to open the patch file
std::string path = "./patches/";
path += szFileName;
FILE* pPatch = fopen(path.c_str(), "rb");
sLog.outDebug("Loading patch info from %s", path.c_str());
if (!pPatch)
{ return; }
// Calculate the MD5 hash
MD5_CTX ctx;
MD5_Init(&ctx);
const size_t check_chunk_size = 4 * 1024;
ACE_UINT8 buf[check_chunk_size];
while (!feof(pPatch))
{
size_t read = fread(buf, 1, check_chunk_size, pPatch);
MD5_Update(&ctx, buf, read);
}
fclose(pPatch);
// Store the result in the internal patch hash map
patches_[path] = new PATCH_INFO;
MD5_Final((ACE_UINT8*) & patches_[path]->md5, &ctx);
}
bool PatchCache::GetHash(const char* pat, ACE_UINT8 mymd5[MD5_DIGEST_LENGTH])
{
for (Patches::iterator i = patches_.begin(); i != patches_.end(); ++i)
if (!stricmp(pat, i->first.c_str()))
{
memcpy(mymd5, i->second->md5, MD5_DIGEST_LENGTH);
return true;
}
return false;
}
void PatchCache::LoadPatchesInfo()
{
ACE_DIR* dirp = ACE_OS::opendir(ACE_TEXT("./patches/"));
if (!dirp)
{ return; }
ACE_DIRENT* dp;
while ((dp = ACE_OS::readdir(dirp)) != NULL)
{
int l = strlen(dp->d_name);
if (l < 8)
{ continue; }
if (!memcmp(&dp->d_name[l - 4], ".mpq", 4))
{ LoadPatchMD5(dp->d_name); }
}
ACE_OS::closedir(dirp);
}