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
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 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
143 typedef struct XFER_DATA
147 uint8 data
[ChunkSize
];
150 typedef struct AuthHandler
154 bool (AuthSocket::*handler
)(void);
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__ )
164 /// Launch a thread to transfer a patch to the client
165 class PatcherRunnable
: public ZThread::Runnable
168 PatcherRunnable(class AuthSocket
*);
172 AuthSocket
* mySocket
;
175 typedef struct PATCH_INFO
177 uint8 md5
[MD5_DIGEST_LENGTH
];
180 /// Caches MD5 hash of client patches present on the server
184 typedef std::map
<std::string
, PATCH_INFO
*> Patches
;
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]);
193 void LoadPatchesInfo();
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");
220 _accountSecurityLevel
= SEC_PLAYER
;
223 /// Close patch file descriptor before leaving
224 AuthSocket::~AuthSocket()
226 ZThread::Guard
<ZThread::Mutex
> g(patcherLock
);
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()
248 if (!ibuf
.GetLength())
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;*/
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());
280 ///- Report unknown commands in the debug log
281 if (i
==AUTH_TOTAL_COMMANDS
)
283 DEBUG_LOG("[Auth] got unknown packet %u", (uint32
)_cmd
);
289 /// Make the SRP6 calculation from hash in dB
290 void AuthSocket::_SetVSFields(std::string rI
)
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
);
304 sha
.UpdateData(s
.AsByteArray(), s
.GetNumBytes());
305 sha
.UpdateData(mDigest
, SHA_DIGEST_LENGTH
);
308 x
.SetBinary(sha
.GetDigest(), sha
.GetLength());
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
))
326 ///- Read the first 4 bytes (header) to get the length of the remaining of the packet
327 std::vector
<uint8
> buf
;
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
))
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
);
361 _login
= (const char*)ch
->I
;
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
370 dbRealmServer
.escape_string(_safelogin
);
372 pkt
<< (uint8
) AUTH_LOGON_CHALLENGE
;
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());
384 pkt
<< (uint8
)REALM_AUTH_ACCOUNT_BANNED
;
385 sLog
.outBasic("[AuthChallenge] Banned ip %s tries to login!",GetRemoteAddress().c_str ());
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 ());
396 ///- If the IP is 'locked', check that the player comes indeed from the correct IP address
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
;
410 DEBUG_LOG("[AuthChallenge] Account IP matches");
415 DEBUG_LOG("[AuthChallenge] Account '%s' is not locked to ip", _login
.c_str());
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());
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 ());
433 pkt
<< (uint8
) REALM_AUTH_ACCOUNT_FREEZED
;
434 sLog
.outBasic("[AuthChallenge] Temporarily banned account %s tries to login!",_login
.c_str ());
441 ///- Get the password from the account table, upper it, and make the SRP6 calculation
442 std::string rI
= (*result
)[0].GetCppString();
446 BigNumber gmod
=g
.ModExp(b
, N
);
447 B
= ((v
* 3) + gmod
) % N
;
449 ASSERT(gmod
.GetNumBytes() <= 32);
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
460 pkt
.append(g
.AsByteArray(), 1);
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
));
481 pkt
<< (uint8
) REALM_AUTH_NO_MATCH
;
484 SendBuf((char const*)pkt
.contents(), pkt
.size());
488 /// Logon Proof command handler
489 bool AuthSocket::_HandleLogonProof()
491 DEBUG_LOG("Entering _HandleLogonProof");
493 if (ibuf
.GetLength() < sizeof(sAuthLogonProof_C
))
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
])
510 /// <ul><li> If the client has no valid version
513 ///- Check if we have the apropriate patch on the disk
515 // 24 = len("./patches/65535enGB.mpq")+1
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");
525 pkt
<< (uint8
) AUTH_LOGON_CHALLENGE
;
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());
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
);
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
));
565 ///- Continue the SRP6 calculation based on data received from the client
567 A
.SetBinary(lp
.A
, 32);
570 sha
.UpdateBigNumbers(&A
, &B
, NULL
);
573 u
.SetBinary(sha
.GetDigest(), 20);
574 BigNumber S
= (A
* (v
.ModExp(u
, N
))).ModExp(b
, N
);
579 memcpy(t
, S
.AsByteArray(), 32);
580 for (int i
= 0; i
< 16; i
++)
585 sha
.UpdateData(t1
, 16);
587 for (int i
= 0; i
< 20; i
++)
589 vK
[i
*2] = sha
.GetDigest()[i
];
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+1] = sha
.GetDigest()[i
];
607 sha
.UpdateBigNumbers(&N
, NULL
);
609 memcpy(hash
, sha
.GetDigest(), 20);
611 sha
.UpdateBigNumbers(&g
, NULL
);
613 for (int i
= 0; i
< 20; i
++)
615 hash
[i
] ^= sha
.GetDigest()[i
];
618 t3
.SetBinary(hash
, 20);
621 sha
.UpdateData(_login
);
623 uint8 t4
[SHA_DIGEST_LENGTH
];
624 memcpy(t4
, sha
.GetDigest(), SHA_DIGEST_LENGTH
);
627 sha
.UpdateBigNumbers(&t3
, NULL
);
628 sha
.UpdateData(t4
, SHA_DIGEST_LENGTH
);
629 sha
.UpdateBigNumbers(&s
, &A
, &B
, &K
, NULL
);
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
647 sha
.UpdateBigNumbers(&A
, &M
, &K
, NULL
);
650 sAuthLogonProof_S proof
;
651 memcpy(proof
.M2
, sha
.GetDigest(), 20);
652 proof
.cmd
= AUTH_LOGON_PROOF
;
654 proof
.unk1
= 0x00800000;
658 SendBuf((char *)&proof
, sizeof(proof
));
660 ///- Set _authed to true!
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);
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
);
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
);
710 /// %Realm List command handler
711 bool AuthSocket::_HandleRealmList()
713 DEBUG_LOG("Entering _HandleRealmList");
714 if (ibuf
.GetLength() < 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());
725 sLog
.outError("[ERROR] user %s tried to login and we cannot find him in the database.",_login
.c_str());
730 uint32 id
= (*result
)[0].GetUInt32();
731 std::string rI
= (*result
)[1].GetCppString();
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)
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
);
750 Field
*fields
= result
->Fetch();
751 AmountOfCharacters
= fields
[0].GetUInt8();
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
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?
773 hdr
<< (uint8
) REALM_LIST
;
774 hdr
<< (uint16
)pkt
.size();
777 SendBuf((char const*)hdr
.contents(), hdr
.size());
779 // Set check field before possible relogin to realm
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)");
795 ///- Launch a PatcherRunnable thread starting at given patch file offset
798 ibuf
.Read((char*)&start
,sizeof(start
));
799 fseek(pPatch
,start
,0);
801 ZThread::Thread
u(new PatcherRunnable(this));
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);
819 /// Accept patch transfer
820 bool AuthSocket::_HandleXferAccept()
822 DEBUG_LOG("Entering _HandleXferAccept");
824 ///- Check packet length and patch existence
827 sLog
.outError("Error while accepting patch transfer (wrong packet)");
831 ///- Launch a PatcherRunnable thread, starting at the begining of the patch file
832 ibuf
.Remove(1); //clear input buffer
835 ZThread::Thread
u(new PatcherRunnable(this));
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
)
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
875 void Patcher::LoadPatchesInfo()
880 dirp
= opendir("./patches/");
886 if ((dp
= readdir(dirp
)) != NULL
)
888 int l
=strlen(dp
->d_name
);
890 if(!memcmp(&dp
->d_name
[l
-4],".mpq",4))
891 LoadPatchMD5(dp
->d_name
);
909 void Patcher::LoadPatchesInfo()
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
));
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/";
930 FILE * pPatch
=fopen(path
.c_str(),"rb");
931 sLog
.outDebug("Loading patch info from %s\n",path
.c_str());
934 sLog
.outError("Error loading patch %s\n",path
.c_str());
938 ///- Calculate the MD5 hash
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
);
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);
969 /// Launch the patch hashing mechanism on object creation
975 /// Empty and delete the patch map on termination
978 for(Patches::iterator i
= _patches
.begin(); i
!= _patches
.end(); i
++ )