* Fixed mangos-realmds autopatching system
[auctionmangos.git] / src / realmd / AuthSocket.cpp
blob93fc8da0d038a5942c8f941d07823ce60072e51e
1 /*
2 * Copyright (C) 2005-2008 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 /** \file
20 \ingroup realmd
23 #include "Common.h"
24 #include "Database/DatabaseEnv.h"
25 #include "ByteBuffer.h"
26 #include "Config/ConfigEnv.h"
27 #include "Log.h"
28 #include "RealmList.h"
29 #include "AuthSocket.h"
30 #include "AuthCodes.h"
31 #include <openssl/md5.h>
32 #include "Auth/Sha1.h"
33 //#include "Util.h" -- for commented utf8ToUpperOnlyLatin
35 extern RealmList m_realmList;
37 extern DatabaseType dbRealmServer;
39 #define ChunkSize 2048
41 enum eAuthCmd
43 //AUTH_NO_CMD = 0xFF,
44 AUTH_LOGON_CHALLENGE = 0x00,
45 AUTH_LOGON_PROOF = 0x01,
46 //AUTH_RECONNECT_CHALLENGE = 0x02,
47 //AUTH_RECONNECT_PROOF = 0x03,
48 //update srv =4
49 REALM_LIST = 0x10,
50 XFER_INITIATE = 0x30,
51 XFER_DATA = 0x31,
52 XFER_ACCEPT = 0x32,
53 XFER_RESUME = 0x33,
54 XFER_CANCEL = 0x34
57 enum eStatus
59 STATUS_CONNECTED = 0,
60 STATUS_AUTHED
63 // GCC have alternative #pragma pack(N) syntax and old gcc version not support pack(push,N), also any gcc version not support it at some paltform
64 #if defined( __GNUC__ )
65 #pragma pack(1)
66 #else
67 #pragma pack(push,1)
68 #endif
70 typedef struct AUTH_LOGON_CHALLENGE_C
72 uint8 cmd;
73 uint8 error;
74 uint16 size;
75 uint8 gamename[4];
76 uint8 version1;
77 uint8 version2;
78 uint8 version3;
79 uint16 build;
80 uint8 platform[4];
81 uint8 os[4];
82 uint8 country[4];
83 uint32 timezone_bias;
84 uint32 ip;
85 uint8 I_len;
86 uint8 I[1];
87 } sAuthLogonChallenge_C;
89 //typedef sAuthLogonChallenge_C sAuthReconnectChallenge_C;
91 typedef struct
93 uint8 cmd;
94 uint8 error;
95 uint8 unk2;
96 uint8 B[32];
97 uint8 g_len;
98 uint8 g[1];
99 uint8 N_len;
100 uint8 N[32];
101 uint8 s[32];
102 uint8 unk3[16];
103 } sAuthLogonChallenge_S;
106 typedef struct AUTH_LOGON_PROOF_C
108 uint8 cmd;
109 uint8 A[32];
110 uint8 M1[20];
111 uint8 crc_hash[20];
112 uint8 number_of_keys;
113 uint8 unk; // Added in 1.12.x client branch
114 } sAuthLogonProof_C;
116 typedef struct
118 uint16 unk1;
119 uint32 unk2;
120 uint8 unk3[4];
121 uint16 unk4[20];
122 } sAuthLogonProofKey_C;
124 typedef struct AUTH_LOGON_PROOF_S
126 uint8 cmd;
127 uint8 error;
128 uint8 M2[20];
129 uint32 unk1;
130 uint32 unk2;
131 uint16 unk3;
132 } sAuthLogonProof_S;
134 typedef struct XFER_INIT
136 uint8 cmd; // XFER_INITIATE
137 uint8 fileNameLen; // strlen(fileName);
138 uint8 fileName[5]; // fileName[fileNameLen]
139 uint64 file_size; // file size (bytes)
140 uint8 md5[MD5_DIGEST_LENGTH]; // MD5
141 }XFER_INIT;
143 typedef struct XFER_DATA
145 uint8 opcode;
146 uint16 data_size;
147 uint8 data[ChunkSize];
148 }XFER_DATA_STRUCT;
150 typedef struct AuthHandler
152 eAuthCmd cmd;
153 uint32 status;
154 bool (AuthSocket::*handler)(void);
155 }AuthHandler;
157 // GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some paltform
158 #if defined( __GNUC__ )
159 #pragma pack()
160 #else
161 #pragma pack(pop)
162 #endif
164 /// Launch a thread to transfer a patch to the client
165 class PatcherRunnable: public ZThread::Runnable
167 public:
168 PatcherRunnable(class AuthSocket *);
169 void run();
171 private:
172 AuthSocket * mySocket;
175 typedef struct PATCH_INFO
177 uint8 md5[MD5_DIGEST_LENGTH];
178 }PATCH_INFO;
180 /// Caches MD5 hash of client patches present on the server
181 class Patcher
183 public:
184 typedef std::map<std::string, PATCH_INFO*> Patches;
185 ~Patcher();
186 Patcher();
187 Patches::const_iterator begin() const { return _patches.begin(); }
188 Patches::const_iterator end() const { return _patches.end(); }
189 void LoadPatchMD5(char*);
190 bool GetHash(char * pat,uint8 mymd5[16]);
192 private:
193 void LoadPatchesInfo();
194 Patches _patches;
197 const AuthHandler table[] =
199 { AUTH_LOGON_CHALLENGE, STATUS_CONNECTED, &AuthSocket::_HandleLogonChallenge},
200 { AUTH_LOGON_PROOF, STATUS_CONNECTED, &AuthSocket::_HandleLogonProof },
201 { REALM_LIST, STATUS_AUTHED, &AuthSocket::_HandleRealmList },
202 { XFER_ACCEPT, STATUS_CONNECTED, &AuthSocket::_HandleXferAccept },
203 { XFER_RESUME, STATUS_CONNECTED, &AuthSocket::_HandleXferResume },
204 { XFER_CANCEL, STATUS_CONNECTED, &AuthSocket::_HandleXferCancel }
207 #define AUTH_TOTAL_COMMANDS sizeof(table)/sizeof(AuthHandler)
209 ///Holds the MD5 hash of client patches present on the server
210 Patcher PatchesCache;
212 /// Constructor - set the N and g values for SRP6
213 AuthSocket::AuthSocket(ISocketHandler &h) : TcpSocket(h)
215 N.SetHexStr("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7");
216 g.SetDword(7);
217 _authed = false;
218 pPatch = NULL;
220 _accountSecurityLevel = SEC_PLAYER;
223 /// Close patch file descriptor before leaving
224 AuthSocket::~AuthSocket()
226 ZThread::Guard<ZThread::Mutex> g(patcherLock);
227 if(pPatch)
228 fclose(pPatch);
231 /// Accept the connection and set the s random value for SRP6
232 void AuthSocket::OnAccept()
234 sLog.outBasic("Accepting connection from '%s:%d'",
235 GetRemoteAddress().c_str(), GetRemotePort());
237 s.SetRand(s_BYTE_SIZE * 8);
240 /// Read the packet from the client
241 void AuthSocket::OnRead()
243 ///- Read the packet
244 TcpSocket::OnRead();
245 uint8 _cmd;
246 while (1)
248 if (!ibuf.GetLength())
249 return;
251 ///- Get the command out of it
252 ibuf.SoftRead((char *)&_cmd, 1); // UQ1: No longer exists in new net code ???
253 //ibuf.Read((char *)&_cmd, 1);
254 /*char *command = (char *)malloc(1);
256 ibuf.Read(command, 1);
258 _cmd = (uint8)command;*/
259 // assert(0);
260 size_t i;
262 ///- Circle through known commands and call the correct command handler
263 for (i=0;i<AUTH_TOTAL_COMMANDS; i++)
265 if ((uint8)table[i].cmd == _cmd &&
266 (table[i].status == STATUS_CONNECTED ||
267 (_authed && table[i].status == STATUS_AUTHED)))
269 DEBUG_LOG("[Auth] got data for cmd %u ibuf length %u", (uint32)_cmd, ibuf.GetLength());
271 if (!(*this.*table[i].handler)())
273 DEBUG_LOG("Command handler failed for cmd %u ibuf length %u", (uint32)_cmd, ibuf.GetLength());
274 return;
276 break;
280 ///- Report unknown commands in the debug log
281 if (i==AUTH_TOTAL_COMMANDS)
283 DEBUG_LOG("[Auth] got unknown packet %u", (uint32)_cmd);
284 return;
289 /// Make the SRP6 calculation from hash in dB
290 void AuthSocket::_SetVSFields(std::string rI)
292 BigNumber I;
293 I.SetHexStr(rI.c_str());
295 //In case of leading zeroes in the rI hash, restore them
296 uint8 mDigest[SHA_DIGEST_LENGTH];
297 memset(mDigest,0,SHA_DIGEST_LENGTH);
298 if (I.GetNumBytes() <= SHA_DIGEST_LENGTH)
299 memcpy(mDigest,I.AsByteArray(),I.GetNumBytes());
301 std::reverse(mDigest,mDigest+SHA_DIGEST_LENGTH);
303 Sha1Hash sha;
304 sha.UpdateData(s.AsByteArray(), s.GetNumBytes());
305 sha.UpdateData(mDigest, SHA_DIGEST_LENGTH);
306 sha.Finalize();
307 BigNumber x;
308 x.SetBinary(sha.GetDigest(), sha.GetLength());
309 v = g.ModExp(x, N);
310 // No SQL injection (username escaped)
311 const char *v_hex, *s_hex;
312 v_hex = v.AsHexStr();
313 s_hex = s.AsHexStr();
314 dbRealmServer.PExecute("UPDATE account SET v = '%s', s = '%s' WHERE username = '%s'",v_hex,s_hex, _safelogin.c_str() );
315 OPENSSL_free((void*)v_hex);
316 OPENSSL_free((void*)s_hex);
319 /// Logon Challenge command handler
320 bool AuthSocket::_HandleLogonChallenge()
322 DEBUG_LOG("Entering _HandleLogonChallenge");
323 if (ibuf.GetLength() < sizeof(sAuthLogonChallenge_C))
324 return false;
326 ///- Read the first 4 bytes (header) to get the length of the remaining of the packet
327 std::vector<uint8> buf;
328 buf.resize(4);
330 ibuf.Read((char *)&buf[0], 4);
332 EndianConvert(*((uint16*)(buf[0])));
333 uint16 remaining = ((sAuthLogonChallenge_C *)&buf[0])->size;
334 DEBUG_LOG("[AuthChallenge] got header, body is %#04x bytes", remaining);
336 if ((remaining < sizeof(sAuthLogonChallenge_C) - buf.size()) || (ibuf.GetLength() < remaining))
337 return false;
339 //No big fear of memory outage (size is int16, i.e. < 65536)
340 buf.resize(remaining + buf.size() + 1);
341 buf[buf.size() - 1] = 0;
342 sAuthLogonChallenge_C *ch = (sAuthLogonChallenge_C*)&buf[0];
344 // BigEndian code, nop in little endian case
345 // size already converted
346 EndianConvert(*((uint32*)(&ch->gamename[0])));
347 EndianConvert(ch->build);
348 EndianConvert(*((uint32*)(&ch->platform[0])));
349 EndianConvert(*((uint32*)(&ch->os[0])));
350 EndianConvert(*((uint32*)(&ch->country[0])));
351 EndianConvert(ch->timezone_bias);
352 EndianConvert(ch->ip);
354 ///- Read the remaining of the packet
355 ibuf.Read((char *)&buf[4], remaining);
356 DEBUG_LOG("[AuthChallenge] got full packet, %#04x bytes", ch->size);
357 DEBUG_LOG("[AuthChallenge] name(%d): '%s'", ch->I_len, ch->I);
359 ByteBuffer pkt;
361 _login = (const char*)ch->I;
362 _build = ch->build;
364 ///- Normalize account name
365 //utf8ToUpperOnlyLatin(_login); -- client already send account in expected form
367 //Escape the user login to avoid further SQL injection
368 //Memory will be freed on AuthSocket object destruction
369 _safelogin=_login;
370 dbRealmServer.escape_string(_safelogin);
372 pkt << (uint8) AUTH_LOGON_CHALLENGE;
373 pkt << (uint8) 0x00;
375 ///- Verify that this IP is not in the ip_banned table
376 // No SQL injection possible (paste the IP address as passed by the socket)
377 dbRealmServer.Execute("DELETE FROM ip_banned WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate");
379 std::string address = GetRemoteAddress();
380 dbRealmServer.escape_string(address);
381 QueryResult *result = dbRealmServer.PQuery( "SELECT * FROM ip_banned WHERE ip = '%s'",address.c_str());
382 if(result)
384 pkt << (uint8)REALM_AUTH_ACCOUNT_BANNED;
385 sLog.outBasic("[AuthChallenge] Banned ip %s tries to login!",GetRemoteAddress().c_str ());
386 delete result;
388 else
390 ///- Get the account details from the account table
391 // No SQL injection (escaped user name)
393 result = dbRealmServer.PQuery("SELECT sha_pass_hash,id,locked,last_ip,gmlevel FROM account WHERE username = '%s'",_safelogin.c_str ());
394 if( result )
396 ///- If the IP is 'locked', check that the player comes indeed from the correct IP address
397 bool locked = false;
398 if((*result)[2].GetUInt8() == 1) // if ip is locked
400 DEBUG_LOG("[AuthChallenge] Account '%s' is locked to IP - '%s'", _login.c_str(), (*result)[3].GetString());
401 DEBUG_LOG("[AuthChallenge] Player address is '%s'", GetRemoteAddress().c_str());
402 if ( strcmp((*result)[3].GetString(),GetRemoteAddress().c_str()) )
404 DEBUG_LOG("[AuthChallenge] Account IP differs");
405 pkt << (uint8) REALM_AUTH_ACCOUNT_FREEZED;
406 locked=true;
408 else
410 DEBUG_LOG("[AuthChallenge] Account IP matches");
413 else
415 DEBUG_LOG("[AuthChallenge] Account '%s' is not locked to ip", _login.c_str());
418 if (!locked)
420 //set expired bans to inactive
421 dbRealmServer.Execute("UPDATE account_banned SET active = 0 WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate");
422 ///- If the account is banned, reject the logon attempt
423 QueryResult *banresult = dbRealmServer.PQuery("SELECT bandate,unbandate FROM account_banned WHERE id = %u AND active = 1", (*result)[1].GetUInt32());
424 if(banresult)
426 if((*banresult)[0].GetUInt64() == (*banresult)[1].GetUInt64())
428 pkt << (uint8) REALM_AUTH_ACCOUNT_BANNED;
429 sLog.outBasic("[AuthChallenge] Banned account %s tries to login!",_login.c_str ());
431 else
433 pkt << (uint8) REALM_AUTH_ACCOUNT_FREEZED;
434 sLog.outBasic("[AuthChallenge] Temporarily banned account %s tries to login!",_login.c_str ());
437 delete banresult;
439 else
441 ///- Get the password from the account table, upper it, and make the SRP6 calculation
442 std::string rI = (*result)[0].GetCppString();
443 _SetVSFields(rI);
445 b.SetRand(19 * 8);
446 BigNumber gmod=g.ModExp(b, N);
447 B = ((v * 3) + gmod) % N;
449 ASSERT(gmod.GetNumBytes() <= 32);
451 BigNumber unk3;
452 unk3.SetRand(16*8);
454 ///- Fill the response packet with the result
455 pkt << (uint8)REALM_AUTH_SUCCESS;
457 // B may be calculated < 32B so we force minnimal length to 32B
458 pkt.append(B.AsByteArray(32), 32); // 32 bytes
459 pkt << (uint8)1;
460 pkt.append(g.AsByteArray(), 1);
461 pkt << (uint8)32;
462 pkt.append(N.AsByteArray(), 32);
463 pkt.append(s.AsByteArray(), s.GetNumBytes()); // 32 bytes
464 pkt.append(unk3.AsByteArray(), 16);
465 pkt << (uint8)0; // Added in 1.12.x client branch
467 uint8 secLevel = (*result)[4].GetUInt8();
468 _accountSecurityLevel = secLevel <= SEC_ADMINISTRATOR ? AccountTypes(secLevel) : SEC_ADMINISTRATOR;
470 _localizationName.resize(4);
471 for(int i = 0; i <4; ++i)
472 _localizationName[i] = ch->country[4-i-1];
474 sLog.outBasic("[AuthChallenge] account %s is using '%c%c%c%c' locale (%u)", _login.c_str (), ch->country[3],ch->country[2],ch->country[1],ch->country[0], GetLocaleByName(_localizationName));
477 delete result;
479 else //no account
481 pkt<< (uint8) REALM_AUTH_NO_MATCH;
484 SendBuf((char const*)pkt.contents(), pkt.size());
485 return true;
488 /// Logon Proof command handler
489 bool AuthSocket::_HandleLogonProof()
491 DEBUG_LOG("Entering _HandleLogonProof");
492 ///- Read the packet
493 if (ibuf.GetLength() < sizeof(sAuthLogonProof_C))
494 return false;
495 sAuthLogonProof_C lp;
496 ibuf.Read((char *)&lp, sizeof(sAuthLogonProof_C));
498 ///- Check if the client has one of the expected version numbers
499 bool valid_version=false;
500 int accepted_versions[]=EXPECTED_MANGOS_CLIENT_BUILD;
501 for(int i=0;accepted_versions[i];i++)
503 if(_build==accepted_versions[i])
505 valid_version=true;
506 break;
510 /// <ul><li> If the client has no valid version
511 if(!valid_version)
513 ///- Check if we have the apropriate patch on the disk
515 // 24 = len("./patches/65535enGB.mpq")+1
516 char tmp[24];
517 // No buffer overflow (fixed length of arguments)
518 sprintf(tmp,"./patches/%d%s.mpq",_build, _localizationName.c_str());
519 // This will be closed at the destruction of the AuthSocket (client deconnection)
520 FILE *pFile=fopen(tmp,"rb");
522 if(!pFile)
524 ByteBuffer pkt;
525 pkt << (uint8) AUTH_LOGON_CHALLENGE;
526 pkt << (uint8) 0x00;
527 pkt << (uint8) REALM_AUTH_WRONG_BUILD_NUMBER;
528 DEBUG_LOG("[AuthChallenge] %u is not a valid client version!", _build);
529 DEBUG_LOG("[AuthChallenge] Patch %s not found",tmp);
530 SendBuf((char const*)pkt.contents(), pkt.size());
531 return true;
533 else // have patch
535 pPatch=pFile;
536 XFER_INIT xferh;
538 ///- Get the MD5 hash of the patch file (get it from preloaded Patcher cache or calculate it)
539 if(PatchesCache.GetHash(tmp,(uint8*)&xferh.md5))
541 DEBUG_LOG("\n[AuthChallenge] Found precached patch info for patch %s",tmp);
543 else
544 { //calculate patch md5
545 printf("\n[AuthChallenge] Patch info for %s was not cached.",tmp);
546 PatchesCache.LoadPatchMD5(tmp);
547 PatchesCache.GetHash(tmp,(uint8*)&xferh.md5);
550 ///- Send a packet to the client with the file length and MD5 hash
551 uint8 data[2]={AUTH_LOGON_PROOF,REALM_AUTH_UPDATE_CLIENT};
552 SendBuf((const char*)data,sizeof(data));
554 memcpy(&xferh,"0\x05Patch",7);
555 xferh.cmd=XFER_INITIATE;
556 fseek(pPatch,0,SEEK_END);
557 xferh.file_size=ftell(pPatch);
559 SendBuf((const char*)&xferh,sizeof(xferh));
560 return true;
563 /// </ul>
565 ///- Continue the SRP6 calculation based on data received from the client
566 BigNumber A;
567 A.SetBinary(lp.A, 32);
569 Sha1Hash sha;
570 sha.UpdateBigNumbers(&A, &B, NULL);
571 sha.Finalize();
572 BigNumber u;
573 u.SetBinary(sha.GetDigest(), 20);
574 BigNumber S = (A * (v.ModExp(u, N))).ModExp(b, N);
576 uint8 t[32];
577 uint8 t1[16];
578 uint8 vK[40];
579 memcpy(t, S.AsByteArray(), 32);
580 for (int i = 0; i < 16; i++)
582 t1[i] = t[i*2];
584 sha.Initialize();
585 sha.UpdateData(t1, 16);
586 sha.Finalize();
587 for (int i = 0; i < 20; i++)
589 vK[i*2] = sha.GetDigest()[i];
591 for (int i = 0; i < 16; i++)
593 t1[i] = t[i*2+1];
595 sha.Initialize();
596 sha.UpdateData(t1, 16);
597 sha.Finalize();
598 for (int i = 0; i < 20; i++)
600 vK[i*2+1] = sha.GetDigest()[i];
602 K.SetBinary(vK, 40);
604 uint8 hash[20];
606 sha.Initialize();
607 sha.UpdateBigNumbers(&N, NULL);
608 sha.Finalize();
609 memcpy(hash, sha.GetDigest(), 20);
610 sha.Initialize();
611 sha.UpdateBigNumbers(&g, NULL);
612 sha.Finalize();
613 for (int i = 0; i < 20; i++)
615 hash[i] ^= sha.GetDigest()[i];
617 BigNumber t3;
618 t3.SetBinary(hash, 20);
620 sha.Initialize();
621 sha.UpdateData(_login);
622 sha.Finalize();
623 uint8 t4[SHA_DIGEST_LENGTH];
624 memcpy(t4, sha.GetDigest(), SHA_DIGEST_LENGTH);
626 sha.Initialize();
627 sha.UpdateBigNumbers(&t3, NULL);
628 sha.UpdateData(t4, SHA_DIGEST_LENGTH);
629 sha.UpdateBigNumbers(&s, &A, &B, &K, NULL);
630 sha.Finalize();
631 BigNumber M;
632 M.SetBinary(sha.GetDigest(), 20);
634 ///- Check if SRP6 results match (password is correct), else send an error
635 if (!memcmp(M.AsByteArray(), lp.M1, 20))
637 sLog.outBasic("User '%s' successfully authenticated", _login.c_str());
639 ///- Update the sessionkey, last_ip, last login time and reset number of failed logins in the account table for this account
640 // No SQL injection (escaped user name) and IP address as received by socket
641 const char* K_hex = K.AsHexStr();
642 dbRealmServer.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() );
643 OPENSSL_free((void*)K_hex);
645 ///- Finish SRP6 and send the final result to the client
646 sha.Initialize();
647 sha.UpdateBigNumbers(&A, &M, &K, NULL);
648 sha.Finalize();
650 sAuthLogonProof_S proof;
651 memcpy(proof.M2, sha.GetDigest(), 20);
652 proof.cmd = AUTH_LOGON_PROOF;
653 proof.error = 0;
654 proof.unk1 = 0x00800000;
655 proof.unk2 = 0x00;
656 proof.unk3 = 0x00;
658 SendBuf((char *)&proof, sizeof(proof));
660 ///- Set _authed to true!
661 _authed = true;
663 else
665 char data[4]={AUTH_LOGON_PROOF,REALM_AUTH_NO_MATCH,3,0};
666 SendBuf(data,sizeof(data));
667 sLog.outBasic("[AuthChallenge] account %s tried to login with wrong password!",_login.c_str ());
669 uint32 MaxWrongPassCount = sConfig.GetIntDefault("WrongPass.MaxCount", 0);
670 if(MaxWrongPassCount > 0)
672 //Increment number of failed logins by one and if it reaches the limit temporarily ban that account or IP
673 dbRealmServer.PExecute("UPDATE account SET failed_logins = failed_logins + 1 WHERE username = '%s'",_safelogin.c_str());
675 if(QueryResult *loginfail = dbRealmServer.PQuery("SELECT id, failed_logins FROM account WHERE username = '%s'", _safelogin.c_str()))
677 Field* fields = loginfail->Fetch();
678 uint32 failed_logins = fields[1].GetUInt32();
680 if( failed_logins >= MaxWrongPassCount )
682 uint32 WrongPassBanTime = sConfig.GetIntDefault("WrongPass.BanTime", 600);
683 bool WrongPassBanType = sConfig.GetBoolDefault("WrongPass.BanType", false);
685 if(WrongPassBanType)
687 uint32 acc_id = fields[0].GetUInt32();
688 dbRealmServer.PExecute("INSERT INTO account_banned VALUES ('%u',UNIX_TIMESTAMP(),UNIX_TIMESTAMP()+'%u','MaNGOS realmd','Failed login autoban',1)",
689 acc_id, WrongPassBanTime);
690 sLog.outBasic("[AuthChallenge] account %s got banned for '%u' seconds because it failed to authenticate '%u' times",
691 _login.c_str(), WrongPassBanTime, failed_logins);
693 else
695 std::string current_ip = GetRemoteAddress();
696 dbRealmServer.escape_string(current_ip);
697 dbRealmServer.PExecute("INSERT INTO ip_banned VALUES ('%s',UNIX_TIMESTAMP(),UNIX_TIMESTAMP()+'%u','MaNGOS realmd','Failed login autoban')",
698 current_ip.c_str(), WrongPassBanTime);
699 sLog.outBasic("[AuthChallenge] IP %s got banned for '%u' seconds because account %s failed to authenticate '%u' times",
700 current_ip.c_str(), WrongPassBanTime, _login.c_str(), failed_logins);
703 delete loginfail;
707 return true;
710 /// %Realm List command handler
711 bool AuthSocket::_HandleRealmList()
713 DEBUG_LOG("Entering _HandleRealmList");
714 if (ibuf.GetLength() < 5)
715 return false;
717 ibuf.Remove(5);
719 ///- Get the user id (else close the connection)
720 // No SQL injection (escaped user name)
722 QueryResult *result = dbRealmServer.PQuery("SELECT id,sha_pass_hash FROM account WHERE username = '%s'",_safelogin.c_str());
723 if(!result)
725 sLog.outError("[ERROR] user %s tried to login and we cannot find him in the database.",_login.c_str());
726 SetCloseAndDelete();
727 return false;
730 uint32 id = (*result)[0].GetUInt32();
731 std::string rI = (*result)[1].GetCppString();
732 delete result;
734 ///- Update realm list if need
735 m_realmList.UpdateIfNeed();
737 ///- Circle through realms in the RealmList and construct the return packet (including # of user characters in each realm)
738 ByteBuffer pkt;
739 pkt << (uint32) 0;
740 pkt << (uint16) m_realmList.size();
741 RealmList::RealmMap::const_iterator i;
742 for( i = m_realmList.begin(); i != m_realmList.end(); i++ )
744 uint8 AmountOfCharacters;
746 // No SQL injection. id of realm is controlled by the database.
747 result = dbRealmServer.PQuery( "SELECT numchars FROM realmcharacters WHERE realmid = '%d' AND acctid='%u'",i->second.m_ID,id);
748 if( result )
750 Field *fields = result->Fetch();
751 AmountOfCharacters = fields[0].GetUInt8();
752 delete result;
754 else
755 AmountOfCharacters = 0;
757 uint8 lock = (i->second.allowedSecurityLevel > _accountSecurityLevel) ? 1 : 0;
759 pkt << i->second.icon; // realm type
760 pkt << lock; // if 1, then realm locked
761 pkt << i->second.color; // if 2, then realm is offline
762 pkt << i->first;
763 pkt << i->second.address;
764 pkt << i->second.populationLevel;
765 pkt << AmountOfCharacters;
766 pkt << i->second.timezone; // realm category
767 pkt << (uint8) 0x2C; // unk, may be realm number/id?
769 pkt << (uint8) 0x10;
770 pkt << (uint8) 0x00;
772 ByteBuffer hdr;
773 hdr << (uint8) REALM_LIST;
774 hdr << (uint16)pkt.size();
775 hdr.append(pkt);
777 SendBuf((char const*)hdr.contents(), hdr.size());
779 // Set check field before possible relogin to realm
780 _SetVSFields(rI);
781 return true;
784 /// Resume patch transfer
785 bool AuthSocket::_HandleXferResume()
787 DEBUG_LOG("Entering _HandleXferResume");
788 ///- Check packet length and patch existence
789 if (ibuf.GetLength()<9 || !pPatch)
791 sLog.outError("Error while resuming patch transfer (wrong packet)");
792 return false;
795 ///- Launch a PatcherRunnable thread starting at given patch file offset
796 uint64 start;
797 ibuf.Remove(1);
798 ibuf.Read((char*)&start,sizeof(start));
799 fseek(pPatch,start,0);
801 ZThread::Thread u(new PatcherRunnable(this));
802 return true;
805 /// Cancel patch transfer
806 bool AuthSocket::_HandleXferCancel()
808 DEBUG_LOG("Entering _HandleXferCancel");
810 ///- Close and delete the socket
811 ibuf.Remove(1); //clear input buffer
813 //ZThread::Thread::sleep(15);
814 SetCloseAndDelete();
816 return true;
819 /// Accept patch transfer
820 bool AuthSocket::_HandleXferAccept()
822 DEBUG_LOG("Entering _HandleXferAccept");
824 ///- Check packet length and patch existence
825 if (!pPatch)
827 sLog.outError("Error while accepting patch transfer (wrong packet)");
828 return false;
831 ///- Launch a PatcherRunnable thread, starting at the begining of the patch file
832 ibuf.Remove(1); //clear input buffer
833 fseek(pPatch,0,0);
835 ZThread::Thread u(new PatcherRunnable(this));
837 return true;
840 /// Check if there is lag on the connection to the client
841 bool AuthSocket::IsLag()
843 return (TCP_BUFSIZE_READ-GetOutputLength()< 2*ChunkSize);
846 PatcherRunnable::PatcherRunnable(class AuthSocket * as)
848 mySocket=as;
851 /// Send content of patch file to the client
852 void PatcherRunnable::run()
854 ZThread::Guard<ZThread::Mutex> g(mySocket->patcherLock);
855 XFER_DATA_STRUCT xfdata;
856 xfdata.opcode = XFER_DATA;
858 while(!feof(mySocket->pPatch) && mySocket->Ready())
860 ///- Wait until output buffer is reasonably empty
861 while(mySocket->Ready() && mySocket->IsLag())
863 ZThread::Thread::sleep(1);
865 ///- And send content of the patch file to the client
866 xfdata.data_size=fread(&xfdata.data,1,ChunkSize,mySocket->pPatch);
867 mySocket->SendBuf((const char*)&xfdata,xfdata.data_size +(sizeof(XFER_DATA_STRUCT)-ChunkSize));
871 /// Preload MD5 hashes of existing patch files on server
872 #ifndef _WIN32
873 #include <dirent.h>
874 #include <errno.h>
875 void Patcher::LoadPatchesInfo()
877 DIR * dirp;
878 //int errno;
879 struct dirent * dp;
880 dirp = opendir("./patches/");
881 if(!dirp)
882 return;
883 while (dirp)
885 errno = 0;
886 if ((dp = readdir(dirp)) != NULL)
888 int l=strlen(dp->d_name);
889 if(l<8)continue;
890 if(!memcmp(&dp->d_name[l-4],".mpq",4))
891 LoadPatchMD5(dp->d_name);
893 else
895 if(errno != 0)
897 closedir(dirp);
898 return;
900 break;
904 if(dirp)
905 closedir(dirp);
908 #else
909 void Patcher::LoadPatchesInfo()
911 WIN32_FIND_DATA fil;
912 HANDLE hFil=FindFirstFile("./patches/*.mpq",&fil);
913 if(hFil==INVALID_HANDLE_VALUE)
914 return; //no patches were found
918 LoadPatchMD5(fil.cFileName);
920 while(FindNextFile(hFil,&fil));
922 #endif
924 /// Calculate and store MD5 hash for a given patch file
925 void Patcher::LoadPatchMD5(char * szFileName)
927 ///- Try to open the patch file
928 std::string path = "./patches/";
929 path += szFileName;
930 FILE * pPatch=fopen(path.c_str(),"rb");
931 sLog.outDebug("Loading patch info from %s\n",path.c_str());
932 if(!pPatch)
934 sLog.outError("Error loading patch %s\n",path.c_str());
935 return;
938 ///- Calculate the MD5 hash
939 MD5_CTX ctx;
940 MD5_Init(&ctx);
941 uint8* buf = new uint8[512*1024];
943 while (!feof(pPatch))
945 size_t read = fread(buf, 1, 512*1024, pPatch);
946 MD5_Update(&ctx, buf, read);
948 delete [] buf;
949 fclose(pPatch);
951 ///- Store the result in the internal patch hash map
952 _patches[path] = new PATCH_INFO;
953 MD5_Final((uint8 *)&_patches[path]->md5 , &ctx);
956 /// Get cached MD5 hash for a given patch file
957 bool Patcher::GetHash(char * pat,uint8 mymd5[16])
959 for( Patches::iterator i = _patches.begin(); i != _patches.end(); i++ )
960 if(!stricmp(pat,i->first.c_str () ))
962 memcpy(mymd5,i->second->md5,16);
963 return true;
966 return false;
969 /// Launch the patch hashing mechanism on object creation
970 Patcher::Patcher()
972 LoadPatchesInfo();
975 /// Empty and delete the patch map on termination
976 Patcher::~Patcher()
978 for(Patches::iterator i = _patches.begin(); i != _patches.end(); i++ )
979 delete i->second;