mirror of
https://github.com/mangosfour/server.git
synced 2025-12-13 04:37:00 +00:00
221 lines
5 KiB
C++
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);
|
|
}
|
|
|