diff --git a/src/realmd/AuthCodes.h b/src/realmd/AuthCodes.h index 274123e7b..37e6e8adb 100644 --- a/src/realmd/AuthCodes.h +++ b/src/realmd/AuthCodes.h @@ -23,6 +23,21 @@ #ifndef _AUTHCODES_H #define _AUTHCODES_H +enum eAuthCmd +{ + AUTH_LOGON_CHALLENGE = 0x00, + AUTH_LOGON_PROOF = 0x01, + AUTH_RECONNECT_CHALLENGE = 0x02, + AUTH_RECONNECT_PROOF = 0x03, + //update srv =4 + REALM_LIST = 0x10, + XFER_INITIATE = 0x30, + XFER_DATA = 0x31, + XFER_ACCEPT = 0x32, + XFER_RESUME = 0x33, + XFER_CANCEL = 0x34 +}; + enum AuthResult { WOW_SUCCESS = 0x00, diff --git a/src/realmd/AuthSocket.cpp b/src/realmd/AuthSocket.cpp index 6d2e24746..6ca0fae93 100644 --- a/src/realmd/AuthSocket.cpp +++ b/src/realmd/AuthSocket.cpp @@ -27,28 +27,17 @@ #include "RealmList.h" #include "AuthSocket.h" #include "AuthCodes.h" +#include "PatchHandler.h" + #include //#include "Util.h" -- for commented utf8ToUpperOnlyLatin +#include +#include +#include + extern DatabaseType loginDatabase; -#define ChunkSize 2048 - -enum eAuthCmd -{ - AUTH_LOGON_CHALLENGE = 0x00, - AUTH_LOGON_PROOF = 0x01, - AUTH_RECONNECT_CHALLENGE = 0x02, - AUTH_RECONNECT_PROOF = 0x03, - //update srv =4 - REALM_LIST = 0x10, - XFER_INITIATE = 0x30, - XFER_DATA = 0x31, - XFER_ACCEPT = 0x32, - XFER_RESUME = 0x33, - XFER_CANCEL = 0x34 -}; - enum eStatus { STATUS_CONNECTED = 0, @@ -154,13 +143,6 @@ typedef struct XFER_INIT uint8 md5[MD5_DIGEST_LENGTH]; // MD5 }XFER_INIT; -typedef struct XFER_DATA -{ - uint8 opcode; - uint16 data_size; - uint8 data[ChunkSize]; -}XFER_DATA_STRUCT; - typedef struct AuthHandler { eAuthCmd cmd; @@ -175,39 +157,6 @@ typedef struct AuthHandler #pragma pack(pop) #endif -/// Launch a thread to transfer a patch to the client -class PatcherRunnable: public ACE_Based::Runnable -{ - public: - PatcherRunnable(class AuthSocket *); - void run(); - - private: - AuthSocket * mySocket; -}; - -typedef struct PATCH_INFO -{ - uint8 md5[MD5_DIGEST_LENGTH]; -}PATCH_INFO; - -/// Caches MD5 hash of client patches present on the server -class Patcher -{ - public: - typedef std::map Patches; - ~Patcher(); - Patcher(); - Patches::const_iterator begin() const { return _patches.begin(); } - Patches::const_iterator end() const { return _patches.end(); } - void LoadPatchMD5(char*); - bool GetHash(char * pat,uint8 mymd5[16]); - - private: - void LoadPatchesInfo(); - Patches _patches; -}; - const AuthHandler table[] = { { AUTH_LOGON_CHALLENGE, STATUS_CONNECTED, &AuthSocket::_HandleLogonChallenge }, @@ -222,67 +171,58 @@ const AuthHandler table[] = #define AUTH_TOTAL_COMMANDS sizeof(table)/sizeof(AuthHandler) -///Holds the MD5 hash of client patches present on the server -Patcher PatchesCache; - /// Constructor - set the N and g values for SRP6 -AuthSocket::AuthSocket(ISocketHandler &h) : TcpSocket(h) +AuthSocket::AuthSocket() { N.SetHexStr("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7"); g.SetDword(7); _authed = false; - pPatch = NULL; _accountSecurityLevel = SEC_PLAYER; _build = 0; + patch_ = ACE_INVALID_HANDLE; } /// Close patch file descriptor before leaving AuthSocket::~AuthSocket() { - ACE_Guard g(patcherLock); - - if(pPatch) - fclose(pPatch); + if(patch_ != ACE_INVALID_HANDLE) + ACE_OS::close(patch_); } /// Accept the connection and set the s random value for SRP6 void AuthSocket::OnAccept() { - BASIC_LOG("Accepting connection from '%s:%d'", - GetRemoteAddress().c_str(), GetRemotePort()); - + BASIC_LOG("Accepting connection from '%s'", get_remote_address().c_str()); } /// Read the packet from the client void AuthSocket::OnRead() { - ///- Read the packet - TcpSocket::OnRead(); uint8 _cmd; while (1) { - if (!ibuf.GetLength()) + if(!recv_soft((char *)&_cmd, 1)) return; - ///- Get the command out of it - ibuf.SoftRead((char *)&_cmd, 1); // UQ1: No longer exists in new net code ??? - size_t i; ///- Circle through known commands and call the correct command handler for (i = 0; i < AUTH_TOTAL_COMMANDS; ++i) { if ((uint8)table[i].cmd == _cmd && - (table[i].status == STATUS_CONNECTED || - (_authed && table[i].status == STATUS_AUTHED))) + (table[i].status == STATUS_CONNECTED || + (_authed && table[i].status == STATUS_AUTHED))) { - DEBUG_LOG("[Auth] got data for cmd %u ibuf length "SIZEFMTD" ", (uint32)_cmd, ibuf.GetLength()); + DEBUG_LOG("[Auth] got data for cmd %u recv length %u", + (uint32)_cmd, (uint32)recv_len()); if (!(*this.*table[i].handler)()) { - DEBUG_LOG("Command handler failed for cmd %u ibuf length "SIZEFMTD" ", (uint32)_cmd, ibuf.GetLength()); + DEBUG_LOG("Command handler failed for cmd %u recv length %u", + (uint32)_cmd, (uint32)recv_len()); + return; } break; @@ -343,7 +283,7 @@ void AuthSocket::SendProof(Sha1Hash sha) proof.error = 0; proof.unk2 = 0x00; - SendBuf((char *)&proof, sizeof(proof)); + send((char *)&proof, sizeof(proof)); break; } case 8606: // 2.4.3 @@ -361,7 +301,7 @@ void AuthSocket::SendProof(Sha1Hash sha) proof.unk2 = 0x00; proof.unk3 = 0x00; - SendBuf((char *)&proof, sizeof(proof)); + send((char *)&proof, sizeof(proof)); break; } } @@ -371,20 +311,20 @@ void AuthSocket::SendProof(Sha1Hash sha) bool AuthSocket::_HandleLogonChallenge() { DEBUG_LOG("Entering _HandleLogonChallenge"); - if (ibuf.GetLength() < sizeof(sAuthLogonChallenge_C)) + if (recv_len() < sizeof(sAuthLogonChallenge_C)) return false; ///- Read the first 4 bytes (header) to get the length of the remaining of the packet std::vector buf; buf.resize(4); - ibuf.Read((char *)&buf[0], 4); + recv((char *)&buf[0], 4); EndianConvert(*((uint16*)(buf[0]))); uint16 remaining = ((sAuthLogonChallenge_C *)&buf[0])->size; DEBUG_LOG("[AuthChallenge] got header, body is %#04x bytes", remaining); - if ((remaining < sizeof(sAuthLogonChallenge_C) - buf.size()) || (ibuf.GetLength() < remaining)) + if ((remaining < sizeof(sAuthLogonChallenge_C) - buf.size()) || (recv_len() < remaining)) return false; //No big fear of memory outage (size is int16, i.e. < 65536) @@ -393,7 +333,7 @@ bool AuthSocket::_HandleLogonChallenge() sAuthLogonChallenge_C *ch = (sAuthLogonChallenge_C*)&buf[0]; ///- Read the remaining of the packet - ibuf.Read((char *)&buf[4], remaining); + recv((char *)&buf[4], remaining); DEBUG_LOG("[AuthChallenge] got full packet, %#04x bytes", ch->size); DEBUG_LOG("[AuthChallenge] name(%d): '%s'", ch->I_len, ch->I); @@ -425,7 +365,7 @@ bool AuthSocket::_HandleLogonChallenge() ///- Verify that this IP is not in the ip_banned table // No SQL injection possible (paste the IP address as passed by the socket) - std::string address = GetRemoteAddress(); + std::string address = get_remote_address(); loginDatabase.escape_string(address); QueryResult *result = loginDatabase.PQuery("SELECT unbandate FROM ip_banned WHERE " // permanent still banned @@ -433,7 +373,7 @@ bool AuthSocket::_HandleLogonChallenge() if (result) { pkt << (uint8)WOW_FAIL_BANNED; - BASIC_LOG("[AuthChallenge] Banned ip %s tries to login!", GetRemoteAddress().c_str()); + BASIC_LOG("[AuthChallenge] Banned ip %s tries to login!", get_remote_address().c_str()); delete result; } else @@ -449,8 +389,8 @@ bool AuthSocket::_HandleLogonChallenge() if((*result)[2].GetUInt8() == 1) // if ip is locked { DEBUG_LOG("[AuthChallenge] Account '%s' is locked to IP - '%s'", _login.c_str(), (*result)[3].GetString()); - DEBUG_LOG("[AuthChallenge] Player address is '%s'", GetRemoteAddress().c_str()); - if ( strcmp((*result)[3].GetString(),GetRemoteAddress().c_str()) ) + DEBUG_LOG("[AuthChallenge] Player address is '%s'", get_remote_address().c_str()); + if ( strcmp((*result)[3].GetString(),get_remote_address().c_str()) ) { DEBUG_LOG("[AuthChallenge] Account IP differs"); pkt << (uint8) WOW_FAIL_SUSPENDED; @@ -566,7 +506,7 @@ bool AuthSocket::_HandleLogonChallenge() pkt<< (uint8) WOW_FAIL_UNKNOWN_ACCOUNT; } } - SendBuf((char const*)pkt.contents(), pkt.size()); + send((char const*)pkt.contents(), pkt.size()); return true; } @@ -575,10 +515,9 @@ bool AuthSocket::_HandleLogonProof() { DEBUG_LOG("Entering _HandleLogonProof"); ///- Read the packet - if (ibuf.GetLength() < sizeof(sAuthLogonProof_C)) - return false; sAuthLogonProof_C lp; - ibuf.Read((char *)&lp, sizeof(sAuthLogonProof_C)); + if(!recv((char *)&lp, sizeof(sAuthLogonProof_C))) + return false; ///- Check if the client has one of the expected version numbers bool valid_version = FindBuildInfo(_build) != NULL; @@ -586,55 +525,60 @@ bool AuthSocket::_HandleLogonProof() ///
  • If the client has no valid version if(!valid_version) { + if (this->patch_ != ACE_INVALID_HANDLE) + return false; + ///- Check if we have the apropriate patch on the disk + // file looks like: 65535enGB.mpq + char tmp[64]; - // 24 = len("./patches/65535enGB.mpq")+1 - char tmp[24]; - // No buffer overflow (fixed length of arguments) - sprintf(tmp, "./patches/%d%s.mpq", _build, _localizationName.c_str()); - // This will be closed at the destruction of the AuthSocket (client disconnection) - FILE *pFile = fopen(tmp, "rb"); + snprintf(tmp, 24, "./patches/%d%s.mpq", _build, _localizationName.c_str()); - if(!pFile) + char filename[PATH_MAX]; + if (ACE_OS::realpath(tmp, filename) != NULL) { + patch_ = ACE_OS::open(filename, GENERIC_READ | FILE_FLAG_SEQUENTIAL_SCAN); + } + + if (patch_ == ACE_INVALID_HANDLE) + { + // no patch found ByteBuffer pkt; pkt << (uint8) AUTH_LOGON_CHALLENGE; pkt << (uint8) 0x00; pkt << (uint8) WOW_FAIL_VERSION_INVALID; DEBUG_LOG("[AuthChallenge] %u is not a valid client version!", _build); DEBUG_LOG("[AuthChallenge] Patch %s not found", tmp); - SendBuf((char const*)pkt.contents(), pkt.size()); + send((char const*)pkt.contents(), pkt.size()); return true; } - else // have patch + + XFER_INIT xferh; + + ACE_OFF_T file_size = ACE_OS::filesize(this->patch_); + + if (file_size == -1) { - pPatch = pFile; - XFER_INIT xferh; - - ///- Get the MD5 hash of the patch file (get it from preloaded Patcher cache or calculate it) - if(PatchesCache.GetHash(tmp, (uint8*)&xferh.md5)) - { - DEBUG_LOG("\n[AuthChallenge] Found precached patch info for patch %s", tmp); - } - else - { // calculate patch md5 - printf("\n[AuthChallenge] Patch info for %s was not cached.", tmp); - PatchesCache.LoadPatchMD5(tmp); - PatchesCache.GetHash(tmp, (uint8*)&xferh.md5); - } - - ///- Send a packet to the client with the file length and MD5 hash - uint8 data[2] = { AUTH_LOGON_PROOF, WOW_FAIL_VERSION_UPDATE }; - SendBuf((const char*)data, sizeof(data)); - - memcpy(&xferh, "0\x05Patch", 7); - xferh.cmd = XFER_INITIATE; - fseek(pPatch, 0, SEEK_END); - xferh.file_size = ftell(pPatch); - - SendBuf((const char*)&xferh, sizeof(xferh)); - return true; + close_connection(); + return false; } + + if (!PatchCache::instance()->GetHash(tmp, (uint8*)&xferh.md5)) + { + // calculate patch md5, happens if patch was added while realmd was running + PatchCache::instance()->LoadPatchMD5(tmp); + PatchCache::instance()->GetHash(tmp, (uint8*)&xferh.md5); + } + + uint8 data[2] = { AUTH_LOGON_PROOF, WOW_FAIL_VERSION_UPDATE}; + send((const char*)data, sizeof(data)); + + memcpy(&xferh, "0\x05Patch", 7); + xferh.cmd = XFER_INITIATE; + xferh.file_size = file_size; + + send((const char*)&xferh, sizeof(xferh)); + return true; } ///
@@ -720,7 +664,7 @@ bool AuthSocket::_HandleLogonProof() ///- Update the sessionkey, last_ip, last login time and reset number of failed logins in the account table for this account // No SQL injection (escaped user name) and IP address as received by socket const char* K_hex = K.AsHexStr(); - loginDatabase.PExecute("UPDATE account SET sessionkey = '%s', last_ip = '%s', last_login = NOW(), locale = '%u', failed_logins = 0 WHERE username = '%s'", K_hex, GetRemoteAddress().c_str(), GetLocaleByName(_localizationName), _safelogin.c_str() ); + loginDatabase.PExecute("UPDATE account SET sessionkey = '%s', last_ip = '%s', last_login = NOW(), locale = '%u', failed_logins = 0 WHERE username = '%s'", K_hex, get_remote_address().c_str(), GetLocaleByName(_localizationName), _safelogin.c_str() ); OPENSSL_free((void*)K_hex); ///- Finish SRP6 and send the final result to the client @@ -736,7 +680,7 @@ bool AuthSocket::_HandleLogonProof() else { char data[4]= { AUTH_LOGON_PROOF, WOW_FAIL_UNKNOWN_ACCOUNT, 3, 0}; - SendBuf(data, sizeof(data)); + send(data, sizeof(data)); BASIC_LOG("[AuthChallenge] account %s tried to login with wrong password!",_login.c_str ()); uint32 MaxWrongPassCount = sConfig.GetIntDefault("WrongPass.MaxCount", 0); @@ -765,7 +709,7 @@ bool AuthSocket::_HandleLogonProof() } else { - std::string current_ip = GetRemoteAddress(); + std::string current_ip = get_remote_address(); loginDatabase.escape_string(current_ip); loginDatabase.PExecute("INSERT INTO ip_banned VALUES ('%s',UNIX_TIMESTAMP(),UNIX_TIMESTAMP()+'%u','MaNGOS realmd','Failed login autoban')", current_ip.c_str(), WrongPassBanTime); @@ -784,20 +728,20 @@ bool AuthSocket::_HandleLogonProof() bool AuthSocket::_HandleReconnectChallenge() { DEBUG_LOG("Entering _HandleReconnectChallenge"); - if (ibuf.GetLength() < sizeof(sAuthLogonChallenge_C)) + if (recv_len() < sizeof(sAuthLogonChallenge_C)) return false; ///- Read the first 4 bytes (header) to get the length of the remaining of the packet std::vector buf; buf.resize(4); - ibuf.Read((char *)&buf[0], 4); + recv((char *)&buf[0], 4); EndianConvert(*((uint16*)(buf[0]))); uint16 remaining = ((sAuthLogonChallenge_C *)&buf[0])->size; DEBUG_LOG("[ReconnectChallenge] got header, body is %#04x bytes", remaining); - if ((remaining < sizeof(sAuthLogonChallenge_C) - buf.size()) || (ibuf.GetLength() < remaining)) + if ((remaining < sizeof(sAuthLogonChallenge_C) - buf.size()) || (recv_len() < remaining)) return false; //No big fear of memory outage (size is int16, i.e. < 65536) @@ -806,7 +750,7 @@ bool AuthSocket::_HandleReconnectChallenge() sAuthLogonChallenge_C *ch = (sAuthLogonChallenge_C*)&buf[0]; ///- Read the remaining of the packet - ibuf.Read((char *)&buf[4], remaining); + recv((char *)&buf[4], remaining); DEBUG_LOG("[ReconnectChallenge] got full packet, %#04x bytes", ch->size); DEBUG_LOG("[ReconnectChallenge] name(%d): '%s'", ch->I_len, ch->I); @@ -816,13 +760,16 @@ bool AuthSocket::_HandleReconnectChallenge() _safelogin = _login; loginDatabase.escape_string(_safelogin); + EndianConvert(ch->build); + _build = ch->build; + QueryResult *result = loginDatabase.PQuery ("SELECT sessionkey FROM account WHERE username = '%s'", _safelogin.c_str ()); // Stop if the account is not found if (!result) { sLog.outError("[ERROR] user %s tried to login and we cannot find his session key in the database.", _login.c_str()); - SetCloseAndDelete(); + close_connection(); return false; } @@ -837,7 +784,7 @@ bool AuthSocket::_HandleReconnectChallenge() _reconnectProof.SetRand(16 * 8); pkt.append(_reconnectProof.AsByteArray(16),16); // 16 bytes random pkt << (uint64) 0x00 << (uint64) 0x00; // 16 bytes zeros - SendBuf((char const*)pkt.contents(), pkt.size()); + send((char const*)pkt.contents(), pkt.size()); return true; } @@ -846,12 +793,12 @@ bool AuthSocket::_HandleReconnectProof() { DEBUG_LOG("Entering _HandleReconnectProof"); ///- Read the packet - if (ibuf.GetLength() < sizeof(sAuthReconnectProof_C)) + sAuthReconnectProof_C lp; + if(!recv((char *)&lp, sizeof(sAuthReconnectProof_C))) return false; + if (_login.empty() || !_reconnectProof.GetNumBytes() || !K.GetNumBytes()) return false; - sAuthReconnectProof_C lp; - ibuf.Read((char *)&lp, sizeof(sAuthReconnectProof_C)); BigNumber t1; t1.SetBinary(lp.R1, 16); @@ -869,7 +816,7 @@ bool AuthSocket::_HandleReconnectProof() pkt << (uint8) AUTH_RECONNECT_PROOF; pkt << (uint8) 0x00; pkt << (uint16) 0x00; // 2 bytes zeros - SendBuf((char const*)pkt.contents(), pkt.size()); + send((char const*)pkt.contents(), pkt.size()); ///- Set _authed to true! _authed = true; @@ -879,7 +826,7 @@ bool AuthSocket::_HandleReconnectProof() else { sLog.outError("[ERROR] user %s tried to login, but session invalid.", _login.c_str()); - SetCloseAndDelete(); + close_connection(); return false; } } @@ -888,10 +835,10 @@ bool AuthSocket::_HandleReconnectProof() bool AuthSocket::_HandleRealmList() { DEBUG_LOG("Entering _HandleRealmList"); - if (ibuf.GetLength() < 5) + if (recv_len() < 5) return false; - ibuf.Remove(5); + recv_skip(5); ///- Get the user id (else close the connection) // No SQL injection (escaped user name) @@ -900,7 +847,7 @@ bool AuthSocket::_HandleRealmList() if(!result) { sLog.outError("[ERROR] user %s tried to login and we cannot find him in the database.",_login.c_str()); - SetCloseAndDelete(); + close_connection(); return false; } @@ -920,7 +867,7 @@ bool AuthSocket::_HandleRealmList() hdr << (uint16)pkt.size(); hdr.append(pkt); - SendBuf((char const*)hdr.contents(), hdr.size()); + send((char const*)hdr.contents(), hdr.size()); return true; } @@ -1057,20 +1004,37 @@ void AuthSocket::LoadRealmlist(ByteBuffer &pkt, uint32 acctid) bool AuthSocket::_HandleXferResume() { DEBUG_LOG("Entering _HandleXferResume"); - ///- Check packet length and patch existence - if (ibuf.GetLength() < 9 || !pPatch) + + if(recv_len() < 9) + return false; + + recv_skip(1); + + uint64 start_pos; + recv((char *)&start_pos, 8); + + if(patch_ == ACE_INVALID_HANDLE) { - sLog.outError("Error while resuming patch transfer (wrong packet)"); + close_connection(); return false; } - ///- Launch a PatcherRunnable thread starting at given patch file offset - uint64 start; - ibuf.Remove(1); - ibuf.Read((char*)&start,sizeof(start)); - fseek(pPatch, start, 0); + ACE_OFF_T file_size = ACE_OS::filesize(patch_); + + if(file_size == -1 || start_pos >= (uint64)file_size) + { + close_connection(); + return false; + } + + if(ACE_OS::lseek(patch_, start_pos, SEEK_SET) == -1) + { + close_connection(); + return false; + } + + InitPatch(); - ACE_Based::Thread u(new PatcherRunnable(this)); return true; } @@ -1079,10 +1043,8 @@ bool AuthSocket::_HandleXferCancel() { DEBUG_LOG("Entering _HandleXferCancel"); - ///- Close and delete the socket - ibuf.Remove(1); // clear input buffer - - SetCloseAndDelete(); + recv_skip(1); + close_connection(); return true; } @@ -1092,161 +1054,23 @@ bool AuthSocket::_HandleXferAccept() { DEBUG_LOG("Entering _HandleXferAccept"); - ///- Check packet length and patch existence - if (!pPatch) - { - sLog.outError("Error while accepting patch transfer (wrong packet)"); - return false; - } + recv_skip(1); - ///- Launch a PatcherRunnable thread, starting at the beginning of the patch file - ibuf.Remove(1); // clear input buffer - fseek(pPatch, 0, 0); + InitPatch(); - ACE_Based::Thread u(new PatcherRunnable(this)); return true; } -/// Check if there is lag on the connection to the client -bool AuthSocket::IsLag() +void AuthSocket::InitPatch() { - return (TCP_BUFSIZE_READ-GetOutputLength() < 2 * ChunkSize); -} + PatchHandler* handler = new PatchHandler(ACE_OS::dup(get_handle()), patch_); -PatcherRunnable::PatcherRunnable(class AuthSocket * as) -{ - mySocket = as; -} + patch_ = ACE_INVALID_HANDLE; -/// Send content of patch file to the client -void PatcherRunnable::run() -{ - ACE_Guard g(mySocket->patcherLock); - - XFER_DATA_STRUCT xfdata; - xfdata.opcode = XFER_DATA; - - while(!feof(mySocket->pPatch) && mySocket->Ready()) + if(handler->open() == -1) { - ///- Wait until output buffer is reasonably empty - while(mySocket->Ready() && mySocket->IsLag()) - { - ACE_Based::Thread::Sleep(1); - } - ///- And send content of the patch file to the client - xfdata.data_size = fread(&xfdata.data, 1, ChunkSize, mySocket->pPatch); - mySocket->SendBuf((const char*)&xfdata, xfdata.data_size + (sizeof(XFER_DATA_STRUCT) - ChunkSize)); + handler->close(); + close_connection(); } } -/// Preload MD5 hashes of existing patch files on server -#ifndef _WIN32 -#include -#include -void Patcher::LoadPatchesInfo() -{ - DIR * dirp; - //int errno; - struct dirent * dp; - dirp = opendir("./patches/"); - if(!dirp) - return; - while (dirp) - { - errno = 0; - if ((dp = 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); - } - else - { - if(errno != 0) - { - closedir(dirp); - return; - } - break; - } - } - - if(dirp) - closedir(dirp); -} - -#else -void Patcher::LoadPatchesInfo() -{ - WIN32_FIND_DATA fil; - HANDLE hFil=FindFirstFile("./patches/*.mpq", &fil); - if(hFil == INVALID_HANDLE_VALUE) - return; // no patches were found - - do - { - LoadPatchMD5(fil.cFileName); - } - while(FindNextFile(hFil, &fil)); -} -#endif - -/// Calculate and store MD5 hash for a given patch file -void Patcher::LoadPatchMD5(char * szFileName) -{ - ///- Try to open the patch file - std::string path = "./patches/"; - path += szFileName; - FILE *pPatch = fopen(path.c_str(), "rb"); - DEBUG_LOG("Loading patch info from %s\n", path.c_str()); - if(!pPatch) - { - sLog.outError("Error loading patch %s\n", path.c_str()); - return; - } - - ///- Calculate the MD5 hash - MD5_CTX ctx; - MD5_Init(&ctx); - uint8* buf = new uint8[512*1024]; - - while (!feof(pPatch)) - { - size_t read = fread(buf, 1, 512*1024, pPatch); - MD5_Update(&ctx, buf, read); - } - delete [] buf; - fclose(pPatch); - - ///- Store the result in the internal patch hash map - _patches[path] = new PATCH_INFO; - MD5_Final((uint8 *)&_patches[path]->md5, &ctx); -} - -/// Get cached MD5 hash for a given patch file -bool Patcher::GetHash(char * pat, uint8 mymd5[16]) -{ - for( Patches::iterator i = _patches.begin(); i != _patches.end(); ++i ) - if(!stricmp(pat, i->first.c_str())) - { - memcpy(mymd5, i->second->md5, 16); - return true; - } - - return false; -} - -/// Launch the patch hashing mechanism on object creation -Patcher::Patcher() -{ - LoadPatchesInfo(); -} - -/// Empty and delete the patch map on termination -Patcher::~Patcher() -{ - for(Patches::iterator i = _patches.begin(); i != _patches.end(); ++i ) - delete i->second; -} diff --git a/src/realmd/AuthSocket.h b/src/realmd/AuthSocket.h index a3ce60d49..afd60231c 100644 --- a/src/realmd/AuthSocket.h +++ b/src/realmd/AuthSocket.h @@ -25,22 +25,18 @@ #include "Common.h" #include "Auth/BigNumber.h" -#include "sockets/TcpSocket.h" -#include "sockets/SocketHandler.h" -#include "sockets/ListenSocket.h" -#include "sockets/Utility.h" -#include "sockets/Parse.h" -#include "sockets/Socket.h" #include "Auth/Sha1.h" #include "ByteBuffer.h" +#include "BufferedSocket.h" + /// Handle login commands -class AuthSocket: public TcpSocket +class AuthSocket: public BufferedSocket { public: const static int s_BYTE_SIZE = 32; - AuthSocket(ISocketHandler& h); + AuthSocket(); ~AuthSocket(); void OnAccept(); @@ -61,10 +57,6 @@ class AuthSocket: public TcpSocket void _SetVSFields(const std::string& rI); - FILE *pPatch; - ACE_Thread_Mutex patcherLock; - bool IsLag(); - private: BigNumber N, s, g, v; @@ -82,6 +74,10 @@ class AuthSocket: public TcpSocket std::string _localizationName; uint16 _build; AccountTypes _accountSecurityLevel; + + ACE_HANDLE patch_; + + void InitPatch(); }; #endif /// @} diff --git a/src/realmd/BufferedSocket.cpp b/src/realmd/BufferedSocket.cpp new file mode 100644 index 000000000..993547811 --- /dev/null +++ b/src/realmd/BufferedSocket.cpp @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2005-2010 MaNGOS + * + * 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 + */ + +/** \file + \ingroup realmd + */ + +#include "BufferedSocket.h" + +#include +#include +#include + +#ifndef MSG_NOSIGNAL +#define MSG_NOSIGNAL 0 +#endif + +BufferedSocket::BufferedSocket(void): + input_buffer_(4096), + remote_address_("") +{ +} + +/*virtual*/ BufferedSocket::~BufferedSocket(void) +{ +} + +/*virtual*/ int BufferedSocket::open(void * arg) +{ + if(Base::open(arg) == -1) + return -1; + + ACE_INET_Addr addr; + + if(peer().get_remote_addr(addr) == -1) + return -1; + + char address[1024]; + + addr.get_host_addr(address, 1024); + + this->remote_address_ = address; + + this->OnAccept(); + + return 0; +} + +const std::string& BufferedSocket::get_remote_address(void) const +{ + return this->remote_address_; +} + +size_t BufferedSocket::recv_len(void) const +{ + return this->input_buffer_.length(); +} + +bool BufferedSocket::recv_soft(char *buf, size_t len) +{ + if(this->input_buffer_.length() < len) + return false; + + ACE_OS::memcpy(buf, this->input_buffer_.rd_ptr(), len); + + return true; +} + +bool BufferedSocket::recv(char *buf, size_t len) +{ + bool ret = this->recv_soft(buf, len); + + if(ret) + this->recv_skip(len); + + return ret; +} + +void BufferedSocket::recv_skip(size_t len) +{ + this->input_buffer_.rd_ptr(len); +} + +ssize_t BufferedSocket::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. + ssize_t n = this->peer().send(message_block.rd_ptr(), len, MSG_NOSIGNAL); + + if(n < 0) + { + if(errno == EWOULDBLOCK) + // Blocking signal + return 0; + else + // Error + return -1; + } + else if(n == 0) + { + // Can this happen ? + return -1; + } + + // return bytes transmitted + return n; +} + +bool BufferedSocket::send(const char *buf, size_t len) +{ + if(buf == NULL || len == 0) + return true; + + ACE_Data_Block db( + len, + ACE_Message_Block::MB_DATA, + (const char*)buf, + 0, + 0, + ACE_Message_Block::DONT_DELETE, + 0); + + ACE_Message_Block message_block( + &db, + ACE_Message_Block::DONT_DELETE, + 0); + + message_block.wr_ptr(len); + + if(this->msg_queue()->is_empty()) + { + // Try to send it directly. + ssize_t n = this->noblk_send(message_block); + + if(n < 0) + return false; + else if(n == len) + return true; + + // adjust how much bytes we sent + message_block.rd_ptr((size_t)n); + + // fall down + } + + // enqueue the message, note: clone is needed cause we cant enqueue stuff on the stack + ACE_Message_Block *mb = message_block.clone(); + + if(this->msg_queue()->enqueue_tail(mb, (ACE_Time_Value *) &ACE_Time_Value::zero) == -1) + { + mb->release(); + return false; + } + + // tell reactor to call handle_output() when we can send more data + if(this->reactor()->schedule_wakeup(this, ACE_Event_Handler::WRITE_MASK) == -1) + return false; + + return true; +} + +/*virtual*/ int BufferedSocket::handle_output(ACE_HANDLE /*= ACE_INVALID_HANDLE*/) +{ + ACE_Message_Block *mb = 0; + + if(this->msg_queue()->is_empty()) + { + // if no more data to send, then cancel notification + this->reactor()->cancel_wakeup(this, ACE_Event_Handler::WRITE_MASK); + return 0; + } + + if(this->msg_queue()->dequeue_head(mb, (ACE_Time_Value *) &ACE_Time_Value::zero) == -1) + return -1; + + ssize_t n = this->noblk_send(*mb); + + if(n < 0) + { + mb->release(); + return -1; + } + else if(n == mb->length()) + { + mb->release(); + return 1; + } + else + { + mb->rd_ptr(n); + + if(this->msg_queue()->enqueue_head(mb, (ACE_Time_Value *) &ACE_Time_Value::zero) == -1) + { + mb->release(); + return -1; + } + + return 0; + } + + ACE_NOTREACHED(return -1); +} + +/*virtual*/ int BufferedSocket::handle_input(ACE_HANDLE /*= ACE_INVALID_HANDLE*/) +{ + const ssize_t space = this->input_buffer_.space(); + + ssize_t n = this->peer().recv(this->input_buffer_.wr_ptr(), space); + + if(n < 0) + { + // blocking signal or error + return errno == EWOULDBLOCK ? 0 : -1; + } + else if(n == 0) + { + // EOF + return -1; + } + + this->input_buffer_.wr_ptr((size_t)n); + + this->OnRead(); + + // move data in the buffer to the beginning of the buffer + this->input_buffer_.crunch(); + + // return 1 in case there might be more data to read from OS + return n == space ? 1 : 0; +} + +/*virtual*/ int BufferedSocket::handle_close(ACE_HANDLE h, ACE_Reactor_Mask m) +{ + this->OnClose(); + + Base::handle_close(); + + return 0; +} + +void BufferedSocket::close_connection(void) +{ + this->peer().close_reader(); + this->peer().close_writer(); +} + diff --git a/src/realmd/BufferedSocket.h b/src/realmd/BufferedSocket.h new file mode 100644 index 000000000..57ee22171 --- /dev/null +++ b/src/realmd/BufferedSocket.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2005-2010 MaNGOS + * + * 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 + */ + +/** \file + \ingroup realmd + */ + +#ifndef _BUFFEREDSOCKET_H_ +#define _BUFFEREDSOCKET_H_ + +#include +#include +#include +#include +#include +#include + +#include + +class BufferedSocket: public ACE_Svc_Handler +{ + protected: + typedef ACE_Svc_Handler Base; + + virtual void OnRead(void) { } + virtual void OnAccept(void) { } + virtual void OnClose(void) { } + + public: + BufferedSocket(void); + virtual ~BufferedSocket(void); + + size_t recv_len(void) 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); + + const std::string& get_remote_address(void) const; + + virtual int open(void *); + + void close_connection(void); + + virtual int handle_input(ACE_HANDLE = ACE_INVALID_HANDLE); + virtual int handle_output(ACE_HANDLE = ACE_INVALID_HANDLE); + + virtual int handle_close(ACE_HANDLE = ACE_INVALID_HANDLE, + ACE_Reactor_Mask = ACE_Event_Handler::ALL_EVENTS_MASK); + + private: + ssize_t noblk_send(ACE_Message_Block &message_block); + + private: + ACE_Message_Block input_buffer_; + + protected: + std::string remote_address_; + +}; + +#endif /* _BUFFEREDSOCKET_H_ */ + diff --git a/src/realmd/Main.cpp b/src/realmd/Main.cpp index 80000bc76..98a27d9e1 100644 --- a/src/realmd/Main.cpp +++ b/src/realmd/Main.cpp @@ -26,7 +26,6 @@ #include "Config/ConfigEnv.h" #include "Log.h" -#include "sockets/ListenSocket.h" #include "AuthSocket.h" #include "SystemConfig.h" #include "revision.h" @@ -36,6 +35,11 @@ #include #include +#include +#include +#include +#include + #ifdef WIN32 #include "ServiceWin32.h" char serviceName[] = "realmd"; @@ -173,6 +177,12 @@ extern int main(int argc, char **argv) DETAIL_LOG("WARNING: Minimal required version [OpenSSL 0.9.8k]"); } +#if defined (ACE_HAS_EVENT_POLL) || defined (ACE_HAS_DEV_POLL) + ACE_Reactor::instance(new ACE_Reactor(new ACE_Dev_Poll_Reactor(ACE::max_handles(), 1), 1), true); +#endif + + sLog.outBasic("Max allowed open files is %d", ACE::max_handles()); + /// realmd PID file creation std::string pidfile = sConfig.GetStringDefault("PidFile", ""); if(!pidfile.empty()) @@ -204,25 +214,25 @@ extern int main(int argc, char **argv) return 1; } - ///- Launch the listening network socket - port_t rmport = sConfig.GetIntDefault( "RealmServerPort", DEFAULT_REALMSERVER_PORT ); - std::string bind_ip = sConfig.GetStringDefault("BindIP", "0.0.0.0"); - - SocketHandler h; - ListenSocket authListenSocket(h); - if ( authListenSocket.Bind(bind_ip.c_str(),rmport)) - { - sLog.outError( "MaNGOS realmd can not bind to %s:%d",bind_ip.c_str(), rmport ); - Log::WaitBeforeContinueIfNeed(); - return 1; - } - // cleanup query - //set expired bans to inactive + // set expired bans to inactive loginDatabase.Execute("UPDATE account_banned SET active = 0 WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate"); loginDatabase.Execute("DELETE FROM ip_banned WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate"); - h.Add(&authListenSocket); + ///- Launch the listening network socket + ACE_Acceptor acceptor; + + uint16 rmport = sConfig.GetIntDefault("RealmServerPort", DEFAULT_REALMSERVER_PORT); + std::string bind_ip = sConfig.GetStringDefault("BindIP", "0.0.0.0"); + + ACE_INET_Addr bind_addr(rmport, bind_ip.c_str()); + + if(acceptor.open(bind_addr, ACE_Reactor::instance(), ACE_NONBLOCK) == -1) + { + sLog.outError("MaNGOS realmd can not bind to %s:%d", bind_ip.c_str(), rmport); + Log::WaitBeforeContinueIfNeed(); + return 1; + } ///- Catch termination signals HookSignals(); @@ -277,8 +287,11 @@ extern int main(int argc, char **argv) ///- Wait for termination signal while (!stopEvent) { + // dont move this outside the loop, the reactor will modify it + ACE_Time_Value interval(0, 100000); - h.Select(0, 100000); + if (ACE_Reactor::instance()->run_reactor_event_loop(interval) == -1) + break; if( (++loopCounter) == numLoops ) { diff --git a/src/realmd/Makefile.am b/src/realmd/Makefile.am index 259bf20b9..a660351ef 100644 --- a/src/realmd/Makefile.am +++ b/src/realmd/Makefile.am @@ -25,7 +25,11 @@ mangos_realmd_SOURCES = \ AuthCodes.h \ AuthSocket.cpp \ AuthSocket.h \ + BufferedSocket.h \ + BufferedSocket.cpp \ Main.cpp \ + PatchHandler.h \ + PatchHandler.cpp \ RealmList.cpp \ RealmList.h @@ -35,10 +39,9 @@ mangos_realmd_LDADD = \ ../shared/Config/libmangosconfig.a \ ../shared/Auth/libmangosauth.a \ ../shared/libmangosshared.a \ - ../framework/libmangosframework.a \ - ../../dep/src/sockets/libmangossockets.a + ../framework/libmangosframework.a -mangos_realmd_LDFLAGS = -L../../dep/src/sockets -L$(libdir) $(MANGOS_LIBS) +mangos_realmd_LDFLAGS = -L$(libdir) $(MANGOS_LIBS) ## Additional files to include when running 'make dist' # Include realm list daemon configuration diff --git a/src/realmd/PatchHandler.cpp b/src/realmd/PatchHandler.cpp new file mode 100644 index 000000000..e1b5413fe --- /dev/null +++ b/src/realmd/PatchHandler.cpp @@ -0,0 +1,194 @@ + +#include "PatchHandler.h" +#include "AuthCodes.h" +#include "Log.h" +#include "Common.h" + +#include +#include +#include +#include + +#include + +#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 = 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::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); +} + diff --git a/src/realmd/PatchHandler.h b/src/realmd/PatchHandler.h new file mode 100644 index 000000000..0151b2079 --- /dev/null +++ b/src/realmd/PatchHandler.h @@ -0,0 +1,72 @@ +#ifndef _PATCHHANDLER_H_ +#define _PATCHHANDLER_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/** + * @brief Caches MD5 hash of client patches present on the server + */ +class PatchCache +{ + public: + ~PatchCache(); + PatchCache(); + + static PatchCache* instance(); + + struct PATCH_INFO + { + ACE_UINT8 md5[MD5_DIGEST_LENGTH]; + }; + + typedef std::map Patches; + + Patches::const_iterator begin() const + { + return patches_.begin(); + } + + Patches::const_iterator end() const + { + return patches_.end(); + } + + void LoadPatchMD5(const char*); + bool GetHash(const char * pat, ACE_UINT8 mymd5[MD5_DIGEST_LENGTH]); + + private: + void LoadPatchesInfo(); + Patches patches_; + +}; + +class PatchHandler: public ACE_Svc_Handler +{ + protected: + typedef ACE_Svc_Handler Base; + + public: + PatchHandler(ACE_HANDLE socket, ACE_HANDLE patch); + virtual ~PatchHandler(); + + int open(void* = 0); + + protected: + virtual int svc(void); + + private: + ACE_HANDLE patch_fd_; + +}; + +#endif /* _BK_PATCHHANDLER_H__ */ + diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index 81e745922..a0f9c38bb 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "9934" + #define REVISION_NR "9935" #endif // __REVISION_NR_H__ diff --git a/win/VC100/realmd.vcxproj b/win/VC100/realmd.vcxproj index 8b8ce7b15..0329837ad 100644 --- a/win/VC100/realmd.vcxproj +++ b/win/VC100/realmd.vcxproj @@ -415,12 +415,16 @@ + + + + diff --git a/win/VC80/realmd.vcproj b/win/VC80/realmd.vcproj index e373ceac7..8670eb229 100644 --- a/win/VC80/realmd.vcproj +++ b/win/VC80/realmd.vcproj @@ -621,10 +621,26 @@ RelativePath="..\..\src\realmd\AuthSocket.h" > + + + + + + + + diff --git a/win/VC90/realmd.vcproj b/win/VC90/realmd.vcproj index 6665e35a7..f78a507dd 100644 --- a/win/VC90/realmd.vcproj +++ b/win/VC90/realmd.vcproj @@ -621,10 +621,26 @@ RelativePath="..\..\src\realmd\AuthSocket.h" > + + + + + + + +