2 * Copyright (C) 2005-2009 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
24 #include "Database/DatabaseEnv.h"
25 #include "ByteBuffer.h"
26 #include "Config/ConfigEnv.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
44 AUTH_LOGON_CHALLENGE
= 0x00,
45 AUTH_LOGON_PROOF
= 0x01,
46 AUTH_RECONNECT_CHALLENGE
= 0x02,
47 AUTH_RECONNECT_PROOF
= 0x03,
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__ )
70 typedef struct AUTH_LOGON_CHALLENGE_C
87 } sAuthLogonChallenge_C
;
89 //typedef sAuthLogonChallenge_C sAuthReconnectChallenge_C;
103 } sAuthLogonChallenge_S;
106 typedef struct AUTH_LOGON_PROOF_C
112 uint8 number_of_keys
;
113 uint8 unk
; // Added in 1.12.x client branch
122 } sAuthLogonProofKey_C;
124 typedef struct AUTH_LOGON_PROOF_S
134 typedef struct AUTH_RECONNECT_PROOF_C
140 uint8 number_of_keys
;
141 } sAuthReconnectProof_C
;
143 typedef struct XFER_INIT
145 uint8 cmd
; // XFER_INITIATE
146 uint8 fileNameLen
; // strlen(fileName);
147 uint8 fileName
[5]; // fileName[fileNameLen]
148 uint64 file_size
; // file size (bytes)
149 uint8 md5
[MD5_DIGEST_LENGTH
]; // MD5
152 typedef struct XFER_DATA
156 uint8 data
[ChunkSize
];
159 typedef struct AuthHandler
163 bool (AuthSocket::*handler
)(void);
166 // GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some paltform
167 #if defined( __GNUC__ )
173 /// Launch a thread to transfer a patch to the client
174 class PatcherRunnable
: public ZThread::Runnable
177 PatcherRunnable(class AuthSocket
*);
181 AuthSocket
* mySocket
;
184 typedef struct PATCH_INFO
186 uint8 md5
[MD5_DIGEST_LENGTH
];
189 /// Caches MD5 hash of client patches present on the server
193 typedef std::map
<std::string
, PATCH_INFO
*> Patches
;
196 Patches::const_iterator
begin() const { return _patches
.begin(); }
197 Patches::const_iterator
end() const { return _patches
.end(); }
198 void LoadPatchMD5(char*);
199 bool GetHash(char * pat
,uint8 mymd5
[16]);
202 void LoadPatchesInfo();
206 const AuthHandler table
[] =
208 { AUTH_LOGON_CHALLENGE
, STATUS_CONNECTED
, &AuthSocket::_HandleLogonChallenge
},
209 { AUTH_LOGON_PROOF
, STATUS_CONNECTED
, &AuthSocket::_HandleLogonProof
},
210 { AUTH_RECONNECT_CHALLENGE
, STATUS_CONNECTED
, &AuthSocket::_HandleReconnectChallenge
},
211 { AUTH_RECONNECT_PROOF
, STATUS_CONNECTED
, &AuthSocket::_HandleReconnectProof
},
212 { REALM_LIST
, STATUS_AUTHED
, &AuthSocket::_HandleRealmList
},
213 { XFER_ACCEPT
, STATUS_CONNECTED
, &AuthSocket::_HandleXferAccept
},
214 { XFER_RESUME
, STATUS_CONNECTED
, &AuthSocket::_HandleXferResume
},
215 { XFER_CANCEL
, STATUS_CONNECTED
, &AuthSocket::_HandleXferCancel
}
218 #define AUTH_TOTAL_COMMANDS sizeof(table)/sizeof(AuthHandler)
220 ///Holds the MD5 hash of client patches present on the server
221 Patcher PatchesCache
;
223 /// Constructor - set the N and g values for SRP6
224 AuthSocket::AuthSocket(ISocketHandler
&h
) : TcpSocket(h
)
226 N
.SetHexStr("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7");
231 _accountSecurityLevel
= SEC_PLAYER
;
234 /// Close patch file descriptor before leaving
235 AuthSocket::~AuthSocket()
237 ZThread::Guard
<ZThread::Mutex
> g(patcherLock
);
242 /// Accept the connection and set the s random value for SRP6
243 void AuthSocket::OnAccept()
245 sLog
.outBasic("Accepting connection from '%s:%d'",
246 GetRemoteAddress().c_str(), GetRemotePort());
248 s
.SetRand(s_BYTE_SIZE
* 8);
251 /// Read the packet from the client
252 void AuthSocket::OnRead()
259 if (!ibuf
.GetLength())
262 ///- Get the command out of it
263 ibuf
.SoftRead((char *)&_cmd
, 1); // UQ1: No longer exists in new net code ???
264 //ibuf.Read((char *)&_cmd, 1);
265 /*char *command = (char *)malloc(1);
267 ibuf.Read(command, 1);
269 _cmd = (uint8)command;*/
273 ///- Circle through known commands and call the correct command handler
274 for (i
=0;i
<AUTH_TOTAL_COMMANDS
; i
++)
276 if ((uint8
)table
[i
].cmd
== _cmd
&&
277 (table
[i
].status
== STATUS_CONNECTED
||
278 (_authed
&& table
[i
].status
== STATUS_AUTHED
)))
280 DEBUG_LOG("[Auth] got data for cmd %u ibuf length %u", (uint32
)_cmd
, ibuf
.GetLength());
282 if (!(*this.*table
[i
].handler
)())
284 DEBUG_LOG("Command handler failed for cmd %u ibuf length %u", (uint32
)_cmd
, ibuf
.GetLength());
291 ///- Report unknown commands in the debug log
292 if (i
==AUTH_TOTAL_COMMANDS
)
294 DEBUG_LOG("[Auth] got unknown packet %u", (uint32
)_cmd
);
300 /// Make the SRP6 calculation from hash in dB
301 void AuthSocket::_SetVSFields(const std::string
& rI
)
304 I
.SetHexStr(rI
.c_str());
306 //In case of leading zeroes in the rI hash, restore them
307 uint8 mDigest
[SHA_DIGEST_LENGTH
];
308 memset(mDigest
,0,SHA_DIGEST_LENGTH
);
309 if (I
.GetNumBytes() <= SHA_DIGEST_LENGTH
)
310 memcpy(mDigest
,I
.AsByteArray(),I
.GetNumBytes());
312 std::reverse(mDigest
,mDigest
+SHA_DIGEST_LENGTH
);
315 sha
.UpdateData(s
.AsByteArray(), s
.GetNumBytes());
316 sha
.UpdateData(mDigest
, SHA_DIGEST_LENGTH
);
319 x
.SetBinary(sha
.GetDigest(), sha
.GetLength());
321 // No SQL injection (username escaped)
322 const char *v_hex
, *s_hex
;
323 v_hex
= v
.AsHexStr();
324 s_hex
= s
.AsHexStr();
325 dbRealmServer
.PExecute("UPDATE account SET v = '%s', s = '%s' WHERE username = '%s'",v_hex
,s_hex
, _safelogin
.c_str() );
326 OPENSSL_free((void*)v_hex
);
327 OPENSSL_free((void*)s_hex
);
330 /// Logon Challenge command handler
331 bool AuthSocket::_HandleLogonChallenge()
333 DEBUG_LOG("Entering _HandleLogonChallenge");
334 if (ibuf
.GetLength() < sizeof(sAuthLogonChallenge_C
))
337 ///- Read the first 4 bytes (header) to get the length of the remaining of the packet
338 std::vector
<uint8
> buf
;
341 ibuf
.Read((char *)&buf
[0], 4);
343 EndianConvert(*((uint16
*)(buf
[0])));
344 uint16 remaining
= ((sAuthLogonChallenge_C
*)&buf
[0])->size
;
345 DEBUG_LOG("[AuthChallenge] got header, body is %#04x bytes", remaining
);
347 if ((remaining
< sizeof(sAuthLogonChallenge_C
) - buf
.size()) || (ibuf
.GetLength() < remaining
))
350 //No big fear of memory outage (size is int16, i.e. < 65536)
351 buf
.resize(remaining
+ buf
.size() + 1);
352 buf
[buf
.size() - 1] = 0;
353 sAuthLogonChallenge_C
*ch
= (sAuthLogonChallenge_C
*)&buf
[0];
355 // BigEndian code, nop in little endian case
356 // size already converted
357 EndianConvert(*((uint32
*)(&ch
->gamename
[0])));
358 EndianConvert(ch
->build
);
359 EndianConvert(*((uint32
*)(&ch
->platform
[0])));
360 EndianConvert(*((uint32
*)(&ch
->os
[0])));
361 EndianConvert(*((uint32
*)(&ch
->country
[0])));
362 EndianConvert(ch
->timezone_bias
);
363 EndianConvert(ch
->ip
);
365 ///- Read the remaining of the packet
366 ibuf
.Read((char *)&buf
[4], remaining
);
367 DEBUG_LOG("[AuthChallenge] got full packet, %#04x bytes", ch
->size
);
368 DEBUG_LOG("[AuthChallenge] name(%d): '%s'", ch
->I_len
, ch
->I
);
372 _login
= (const char*)ch
->I
;
375 ///- Normalize account name
376 //utf8ToUpperOnlyLatin(_login); -- client already send account in expected form
378 //Escape the user login to avoid further SQL injection
379 //Memory will be freed on AuthSocket object destruction
381 dbRealmServer
.escape_string(_safelogin
);
383 pkt
<< (uint8
) AUTH_LOGON_CHALLENGE
;
386 ///- Verify that this IP is not in the ip_banned table
387 // No SQL injection possible (paste the IP address as passed by the socket)
388 dbRealmServer
.Execute("DELETE FROM ip_banned WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate");
390 std::string address
= GetRemoteAddress();
391 dbRealmServer
.escape_string(address
);
392 QueryResult
*result
= dbRealmServer
.PQuery( "SELECT * FROM ip_banned WHERE ip = '%s'",address
.c_str());
395 pkt
<< (uint8
)REALM_AUTH_ACCOUNT_BANNED
;
396 sLog
.outBasic("[AuthChallenge] Banned ip %s tries to login!",GetRemoteAddress().c_str ());
401 ///- Get the account details from the account table
402 // No SQL injection (escaped user name)
404 result
= dbRealmServer
.PQuery("SELECT sha_pass_hash,id,locked,last_ip,gmlevel FROM account WHERE username = '%s'",_safelogin
.c_str ());
407 ///- If the IP is 'locked', check that the player comes indeed from the correct IP address
409 if((*result
)[2].GetUInt8() == 1) // if ip is locked
411 DEBUG_LOG("[AuthChallenge] Account '%s' is locked to IP - '%s'", _login
.c_str(), (*result
)[3].GetString());
412 DEBUG_LOG("[AuthChallenge] Player address is '%s'", GetRemoteAddress().c_str());
413 if ( strcmp((*result
)[3].GetString(),GetRemoteAddress().c_str()) )
415 DEBUG_LOG("[AuthChallenge] Account IP differs");
416 pkt
<< (uint8
) REALM_AUTH_ACCOUNT_FREEZED
;
421 DEBUG_LOG("[AuthChallenge] Account IP matches");
426 DEBUG_LOG("[AuthChallenge] Account '%s' is not locked to ip", _login
.c_str());
431 //set expired bans to inactive
432 dbRealmServer
.Execute("UPDATE account_banned SET active = 0 WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate");
433 ///- If the account is banned, reject the logon attempt
434 QueryResult
*banresult
= dbRealmServer
.PQuery("SELECT bandate,unbandate FROM account_banned WHERE id = %u AND active = 1", (*result
)[1].GetUInt32());
437 if((*banresult
)[0].GetUInt64() == (*banresult
)[1].GetUInt64())
439 pkt
<< (uint8
) REALM_AUTH_ACCOUNT_BANNED
;
440 sLog
.outBasic("[AuthChallenge] Banned account %s tries to login!",_login
.c_str ());
444 pkt
<< (uint8
) REALM_AUTH_ACCOUNT_FREEZED
;
445 sLog
.outBasic("[AuthChallenge] Temporarily banned account %s tries to login!",_login
.c_str ());
452 ///- Get the password from the account table, upper it, and make the SRP6 calculation
453 std::string rI
= (*result
)[0].GetCppString();
457 BigNumber gmod
=g
.ModExp(b
, N
);
458 B
= ((v
* 3) + gmod
) % N
;
460 ASSERT(gmod
.GetNumBytes() <= 32);
465 ///- Fill the response packet with the result
466 pkt
<< (uint8
)REALM_AUTH_SUCCESS
;
468 // B may be calculated < 32B so we force minnimal length to 32B
469 pkt
.append(B
.AsByteArray(32), 32); // 32 bytes
471 pkt
.append(g
.AsByteArray(), 1);
473 pkt
.append(N
.AsByteArray(), 32);
474 pkt
.append(s
.AsByteArray(), s
.GetNumBytes()); // 32 bytes
475 pkt
.append(unk3
.AsByteArray(), 16);
476 pkt
<< (uint8
)0; // Added in 1.12.x client branch
478 uint8 secLevel
= (*result
)[4].GetUInt8();
479 _accountSecurityLevel
= secLevel
<= SEC_ADMINISTRATOR
? AccountTypes(secLevel
) : SEC_ADMINISTRATOR
;
481 _localizationName
.resize(4);
482 for(int i
= 0; i
<4; ++i
)
483 _localizationName
[i
] = ch
->country
[4-i
-1];
485 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
));
492 pkt
<< (uint8
) REALM_AUTH_NO_MATCH
;
495 SendBuf((char const*)pkt
.contents(), pkt
.size());
499 /// Logon Proof command handler
500 bool AuthSocket::_HandleLogonProof()
502 DEBUG_LOG("Entering _HandleLogonProof");
504 if (ibuf
.GetLength() < sizeof(sAuthLogonProof_C
))
506 sAuthLogonProof_C lp
;
507 ibuf
.Read((char *)&lp
, sizeof(sAuthLogonProof_C
));
509 ///- Check if the client has one of the expected version numbers
510 bool valid_version
=false;
511 int accepted_versions
[]=EXPECTED_MANGOS_CLIENT_BUILD
;
512 for(int i
=0;accepted_versions
[i
];i
++)
514 if(_build
==accepted_versions
[i
])
521 /// <ul><li> If the client has no valid version
524 ///- Check if we have the apropriate patch on the disk
526 // 24 = len("./patches/65535enGB.mpq")+1
528 // No buffer overflow (fixed length of arguments)
529 sprintf(tmp
,"./patches/%d%s.mpq",_build
, _localizationName
.c_str());
530 // This will be closed at the destruction of the AuthSocket (client deconnection)
531 FILE *pFile
=fopen(tmp
,"rb");
536 pkt
<< (uint8
) AUTH_LOGON_CHALLENGE
;
538 pkt
<< (uint8
) REALM_AUTH_WRONG_BUILD_NUMBER
;
539 DEBUG_LOG("[AuthChallenge] %u is not a valid client version!", _build
);
540 DEBUG_LOG("[AuthChallenge] Patch %s not found",tmp
);
541 SendBuf((char const*)pkt
.contents(), pkt
.size());
549 ///- Get the MD5 hash of the patch file (get it from preloaded Patcher cache or calculate it)
550 if(PatchesCache
.GetHash(tmp
,(uint8
*)&xferh
.md5
))
552 DEBUG_LOG("\n[AuthChallenge] Found precached patch info for patch %s",tmp
);
555 { //calculate patch md5
556 printf("\n[AuthChallenge] Patch info for %s was not cached.",tmp
);
557 PatchesCache
.LoadPatchMD5(tmp
);
558 PatchesCache
.GetHash(tmp
,(uint8
*)&xferh
.md5
);
561 ///- Send a packet to the client with the file length and MD5 hash
562 uint8 data
[2]={AUTH_LOGON_PROOF
,REALM_AUTH_UPDATE_CLIENT
};
563 SendBuf((const char*)data
,sizeof(data
));
565 memcpy(&xferh
,"0\x05Patch",7);
566 xferh
.cmd
=XFER_INITIATE
;
567 fseek(pPatch
,0,SEEK_END
);
568 xferh
.file_size
=ftell(pPatch
);
570 SendBuf((const char*)&xferh
,sizeof(xferh
));
576 ///- Continue the SRP6 calculation based on data received from the client
578 A
.SetBinary(lp
.A
, 32);
581 sha
.UpdateBigNumbers(&A
, &B
, NULL
);
584 u
.SetBinary(sha
.GetDigest(), 20);
585 BigNumber S
= (A
* (v
.ModExp(u
, N
))).ModExp(b
, N
);
590 memcpy(t
, S
.AsByteArray(), 32);
591 for (int i
= 0; i
< 16; i
++)
596 sha
.UpdateData(t1
, 16);
598 for (int i
= 0; i
< 20; i
++)
600 vK
[i
*2] = sha
.GetDigest()[i
];
602 for (int i
= 0; i
< 16; i
++)
607 sha
.UpdateData(t1
, 16);
609 for (int i
= 0; i
< 20; i
++)
611 vK
[i
*2+1] = sha
.GetDigest()[i
];
618 sha
.UpdateBigNumbers(&N
, NULL
);
620 memcpy(hash
, sha
.GetDigest(), 20);
622 sha
.UpdateBigNumbers(&g
, NULL
);
624 for (int i
= 0; i
< 20; i
++)
626 hash
[i
] ^= sha
.GetDigest()[i
];
629 t3
.SetBinary(hash
, 20);
632 sha
.UpdateData(_login
);
634 uint8 t4
[SHA_DIGEST_LENGTH
];
635 memcpy(t4
, sha
.GetDigest(), SHA_DIGEST_LENGTH
);
638 sha
.UpdateBigNumbers(&t3
, NULL
);
639 sha
.UpdateData(t4
, SHA_DIGEST_LENGTH
);
640 sha
.UpdateBigNumbers(&s
, &A
, &B
, &K
, NULL
);
643 M
.SetBinary(sha
.GetDigest(), 20);
645 ///- Check if SRP6 results match (password is correct), else send an error
646 if (!memcmp(M
.AsByteArray(), lp
.M1
, 20))
648 sLog
.outBasic("User '%s' successfully authenticated", _login
.c_str());
650 ///- Update the sessionkey, last_ip, last login time and reset number of failed logins in the account table for this account
651 // No SQL injection (escaped user name) and IP address as received by socket
652 const char* K_hex
= K
.AsHexStr();
653 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() );
654 OPENSSL_free((void*)K_hex
);
656 ///- Finish SRP6 and send the final result to the client
658 sha
.UpdateBigNumbers(&A
, &M
, &K
, NULL
);
661 sAuthLogonProof_S proof
;
662 memcpy(proof
.M2
, sha
.GetDigest(), 20);
663 proof
.cmd
= AUTH_LOGON_PROOF
;
665 proof
.unk1
= 0x00800000;
669 SendBuf((char *)&proof
, sizeof(proof
));
671 ///- Set _authed to true!
676 char data
[4]={AUTH_LOGON_PROOF
,REALM_AUTH_NO_MATCH
,3,0};
677 SendBuf(data
,sizeof(data
));
678 sLog
.outBasic("[AuthChallenge] account %s tried to login with wrong password!",_login
.c_str ());
680 uint32 MaxWrongPassCount
= sConfig
.GetIntDefault("WrongPass.MaxCount", 0);
681 if(MaxWrongPassCount
> 0)
683 //Increment number of failed logins by one and if it reaches the limit temporarily ban that account or IP
684 dbRealmServer
.PExecute("UPDATE account SET failed_logins = failed_logins + 1 WHERE username = '%s'",_safelogin
.c_str());
686 if(QueryResult
*loginfail
= dbRealmServer
.PQuery("SELECT id, failed_logins FROM account WHERE username = '%s'", _safelogin
.c_str()))
688 Field
* fields
= loginfail
->Fetch();
689 uint32 failed_logins
= fields
[1].GetUInt32();
691 if( failed_logins
>= MaxWrongPassCount
)
693 uint32 WrongPassBanTime
= sConfig
.GetIntDefault("WrongPass.BanTime", 600);
694 bool WrongPassBanType
= sConfig
.GetBoolDefault("WrongPass.BanType", false);
698 uint32 acc_id
= fields
[0].GetUInt32();
699 dbRealmServer
.PExecute("INSERT INTO account_banned VALUES ('%u',UNIX_TIMESTAMP(),UNIX_TIMESTAMP()+'%u','MaNGOS realmd','Failed login autoban',1)",
700 acc_id
, WrongPassBanTime
);
701 sLog
.outBasic("[AuthChallenge] account %s got banned for '%u' seconds because it failed to authenticate '%u' times",
702 _login
.c_str(), WrongPassBanTime
, failed_logins
);
706 std::string current_ip
= GetRemoteAddress();
707 dbRealmServer
.escape_string(current_ip
);
708 dbRealmServer
.PExecute("INSERT INTO ip_banned VALUES ('%s',UNIX_TIMESTAMP(),UNIX_TIMESTAMP()+'%u','MaNGOS realmd','Failed login autoban')",
709 current_ip
.c_str(), WrongPassBanTime
);
710 sLog
.outBasic("[AuthChallenge] IP %s got banned for '%u' seconds because account %s failed to authenticate '%u' times",
711 current_ip
.c_str(), WrongPassBanTime
, _login
.c_str(), failed_logins
);
721 /// Reconnect Challenge command handler
722 bool AuthSocket::_HandleReconnectChallenge()
724 DEBUG_LOG("Entering _HandleReconnectChallenge");
725 if (ibuf
.GetLength() < sizeof(sAuthLogonChallenge_C
))
728 ///- Read the first 4 bytes (header) to get the length of the remaining of the packet
729 std::vector
<uint8
> buf
;
732 ibuf
.Read((char *)&buf
[0], 4);
734 EndianConvert(*((uint16
*)(buf
[0])));
735 uint16 remaining
= ((sAuthLogonChallenge_C
*)&buf
[0])->size
;
736 DEBUG_LOG("[ReconnectChallenge] got header, body is %#04x bytes", remaining
);
738 if ((remaining
< sizeof(sAuthLogonChallenge_C
) - buf
.size()) || (ibuf
.GetLength() < remaining
))
741 //No big fear of memory outage (size is int16, i.e. < 65536)
742 buf
.resize(remaining
+ buf
.size() + 1);
743 buf
[buf
.size() - 1] = 0;
744 sAuthLogonChallenge_C
*ch
= (sAuthLogonChallenge_C
*)&buf
[0];
746 ///- Read the remaining of the packet
747 ibuf
.Read((char *)&buf
[4], remaining
);
748 DEBUG_LOG("[ReconnectChallenge] got full packet, %#04x bytes", ch
->size
);
749 DEBUG_LOG("[ReconnectChallenge] name(%d): '%s'", ch
->I_len
, ch
->I
);
751 _login
= (const char*)ch
->I
;
754 QueryResult
*result
= dbRealmServer
.PQuery ("SELECT sessionkey FROM account WHERE username = '%s'", _safelogin
.c_str ());
756 // Stop if the account is not found
759 sLog
.outError("[ERROR] user %s tried to login and we cannot find his session key in the database.", _login
.c_str());
764 Field
* fields
= result
->Fetch ();
765 K
.SetHexStr (fields
[0].GetString ());
768 ///- Sending response
770 pkt
<< (uint8
) AUTH_RECONNECT_CHALLENGE
;
772 _reconnectProof
.SetRand(16*8);
773 pkt
.append(_reconnectProof
.AsByteBuffer()); // 16 bytes random
774 pkt
<< (uint64
) 0x00 << (uint64
) 0x00; // 16 bytes zeros
775 SendBuf((char const*)pkt
.contents(), pkt
.size());
779 /// Reconnect Proof command handler
780 bool AuthSocket::_HandleReconnectProof()
782 DEBUG_LOG("Entering _HandleReconnectProof");
784 if (ibuf
.GetLength() < sizeof(sAuthReconnectProof_C
))
786 if (_login
.empty() || !_reconnectProof
.GetNumBytes() || !K
.GetNumBytes())
788 sAuthReconnectProof_C lp
;
789 ibuf
.Read((char *)&lp
, sizeof(sAuthReconnectProof_C
));
792 t1
.SetBinary(lp
.R1
, 16);
796 sha
.UpdateData(_login
);
797 sha
.UpdateBigNumbers(&t1
, &_reconnectProof
, &K
, NULL
);
800 if (!memcmp(sha
.GetDigest(), lp
.R2
, SHA_DIGEST_LENGTH
))
802 ///- Sending response
804 pkt
<< (uint8
) AUTH_RECONNECT_PROOF
;
806 pkt
<< (uint16
) 0x00; // 2 bytes zeros
807 SendBuf((char const*)pkt
.contents(), pkt
.size());
809 ///- Set _authed to true!
816 sLog
.outError("[ERROR] user %s tried to login, but session invalid.", _login
.c_str());
822 /// %Realm List command handler
823 bool AuthSocket::_HandleRealmList()
825 DEBUG_LOG("Entering _HandleRealmList");
826 if (ibuf
.GetLength() < 5)
831 ///- Get the user id (else close the connection)
832 // No SQL injection (escaped user name)
834 QueryResult
*result
= dbRealmServer
.PQuery("SELECT id,sha_pass_hash FROM account WHERE username = '%s'",_safelogin
.c_str());
837 sLog
.outError("[ERROR] user %s tried to login and we cannot find him in the database.",_login
.c_str());
842 uint32 id
= (*result
)[0].GetUInt32();
843 std::string rI
= (*result
)[1].GetCppString();
846 ///- Update realm list if need
847 m_realmList
.UpdateIfNeed();
849 ///- Circle through realms in the RealmList and construct the return packet (including # of user characters in each realm)
852 pkt
<< (uint16
) m_realmList
.size();
853 RealmList::RealmMap::const_iterator i
;
854 for( i
= m_realmList
.begin(); i
!= m_realmList
.end(); i
++ )
856 uint8 AmountOfCharacters
;
858 // No SQL injection. id of realm is controlled by the database.
859 result
= dbRealmServer
.PQuery( "SELECT numchars FROM realmcharacters WHERE realmid = '%d' AND acctid='%u'",i
->second
.m_ID
,id
);
862 Field
*fields
= result
->Fetch();
863 AmountOfCharacters
= fields
[0].GetUInt8();
867 AmountOfCharacters
= 0;
869 uint8 lock
= (i
->second
.allowedSecurityLevel
> _accountSecurityLevel
) ? 1 : 0;
871 pkt
<< i
->second
.icon
; // realm type
872 pkt
<< lock
; // if 1, then realm locked
873 pkt
<< i
->second
.color
; // if 2, then realm is offline
875 pkt
<< i
->second
.address
;
876 pkt
<< i
->second
.populationLevel
;
877 pkt
<< AmountOfCharacters
;
878 pkt
<< i
->second
.timezone
; // realm category
879 pkt
<< (uint8
) 0x2C; // unk, may be realm number/id?
885 hdr
<< (uint8
) REALM_LIST
;
886 hdr
<< (uint16
)pkt
.size();
889 SendBuf((char const*)hdr
.contents(), hdr
.size());
891 // Set check field before possible relogin to realm
896 /// Resume patch transfer
897 bool AuthSocket::_HandleXferResume()
899 DEBUG_LOG("Entering _HandleXferResume");
900 ///- Check packet length and patch existence
901 if (ibuf
.GetLength()<9 || !pPatch
)
903 sLog
.outError("Error while resuming patch transfer (wrong packet)");
907 ///- Launch a PatcherRunnable thread starting at given patch file offset
910 ibuf
.Read((char*)&start
,sizeof(start
));
911 fseek(pPatch
,start
,0);
913 ZThread::Thread
u(new PatcherRunnable(this));
917 /// Cancel patch transfer
918 bool AuthSocket::_HandleXferCancel()
920 DEBUG_LOG("Entering _HandleXferCancel");
922 ///- Close and delete the socket
923 ibuf
.Remove(1); //clear input buffer
925 //ZThread::Thread::sleep(15);
931 /// Accept patch transfer
932 bool AuthSocket::_HandleXferAccept()
934 DEBUG_LOG("Entering _HandleXferAccept");
936 ///- Check packet length and patch existence
939 sLog
.outError("Error while accepting patch transfer (wrong packet)");
943 ///- Launch a PatcherRunnable thread, starting at the begining of the patch file
944 ibuf
.Remove(1); //clear input buffer
947 ZThread::Thread
u(new PatcherRunnable(this));
952 /// Check if there is lag on the connection to the client
953 bool AuthSocket::IsLag()
955 return (TCP_BUFSIZE_READ
-GetOutputLength()< 2*ChunkSize
);
958 PatcherRunnable::PatcherRunnable(class AuthSocket
* as
)
963 /// Send content of patch file to the client
964 void PatcherRunnable::run()
966 ZThread::Guard
<ZThread::Mutex
> g(mySocket
->patcherLock
);
967 XFER_DATA_STRUCT xfdata
;
968 xfdata
.opcode
= XFER_DATA
;
970 while(!feof(mySocket
->pPatch
) && mySocket
->Ready())
972 ///- Wait until output buffer is reasonably empty
973 while(mySocket
->Ready() && mySocket
->IsLag())
975 ZThread::Thread::sleep(1);
977 ///- And send content of the patch file to the client
978 xfdata
.data_size
=fread(&xfdata
.data
,1,ChunkSize
,mySocket
->pPatch
);
979 mySocket
->SendBuf((const char*)&xfdata
,xfdata
.data_size
+(sizeof(XFER_DATA_STRUCT
)-ChunkSize
));
983 /// Preload MD5 hashes of existing patch files on server
987 void Patcher::LoadPatchesInfo()
992 dirp
= opendir("./patches/");
998 if ((dp
= readdir(dirp
)) != NULL
)
1000 int l
=strlen(dp
->d_name
);
1002 if(!memcmp(&dp
->d_name
[l
-4],".mpq",4))
1003 LoadPatchMD5(dp
->d_name
);
1021 void Patcher::LoadPatchesInfo()
1023 WIN32_FIND_DATA fil
;
1024 HANDLE hFil
=FindFirstFile("./patches/*.mpq",&fil
);
1025 if(hFil
==INVALID_HANDLE_VALUE
)
1026 return; //no patches were found
1030 LoadPatchMD5(fil
.cFileName
);
1032 while(FindNextFile(hFil
,&fil
));
1036 /// Calculate and store MD5 hash for a given patch file
1037 void Patcher::LoadPatchMD5(char * szFileName
)
1039 ///- Try to open the patch file
1040 std::string path
= "./patches/";
1042 FILE * pPatch
=fopen(path
.c_str(),"rb");
1043 sLog
.outDebug("Loading patch info from %s\n",path
.c_str());
1046 sLog
.outError("Error loading patch %s\n",path
.c_str());
1050 ///- Calculate the MD5 hash
1053 uint8
* buf
= new uint8
[512*1024];
1055 while (!feof(pPatch
))
1057 size_t read
= fread(buf
, 1, 512*1024, pPatch
);
1058 MD5_Update(&ctx
, buf
, read
);
1063 ///- Store the result in the internal patch hash map
1064 _patches
[path
] = new PATCH_INFO
;
1065 MD5_Final((uint8
*)&_patches
[path
]->md5
, &ctx
);
1068 /// Get cached MD5 hash for a given patch file
1069 bool Patcher::GetHash(char * pat
,uint8 mymd5
[16])
1071 for( Patches::iterator i
= _patches
.begin(); i
!= _patches
.end(); i
++ )
1072 if(!stricmp(pat
,i
->first
.c_str () ))
1074 memcpy(mymd5
,i
->second
->md5
,16);
1081 /// Launch the patch hashing mechanism on object creation
1087 /// Empty and delete the patch map on termination
1090 for(Patches::iterator i
= _patches
.begin(); i
!= _patches
.end(); i
++ )