mojo_client/src/Client/Realm/RealmSocket.cpp
False.Genesis b4b4a80e2d
2007-01-05 18:50:12 +00:00

472 lines
14 KiB
C++

#include "common.h"
#include "PseuWoW.h"
#include "Auth/ByteBuffer.h"
#include "Auth/Sha1.h"
#include "Auth/BigNumber.h"
#include "RealmSocket.h"
enum AuthCmd
{
AUTH_LOGON_CHALLENGE = 0x00,
AUTH_LOGON_PROOF = 0x01,
REALM_LIST = 0x10,
};
struct SRealmHeader
{
uint8 cmd; // OP code = CMD_REALM_LIST
uint16 size; // size of the rest of packet, without this part
uint32 unknown; // 0x00 00 00 00
uint8 count; // quantity of realms
};
struct SRealmInfo
{
uint32 icon; // icon near realm
uint8 color; // color of record
std::string name; // Text zero terminated name of Realm
std::string addr_port; // Text zero terminated address of Realm ("ip:port")
float population; // 1.6 -> population value. lower == lower population and vice versa
uint8 chars_here; // number of characters on this server
uint8 timezone; // timezone
uint8 unknown; //
};
struct AuthHandler
{
uint32 cmd;
void (RealmSocket::*handler)(void);
};
struct sAuthLogonChallenge_S
{
uint8 cmd;
uint8 error;
uint8 unk2;
uint8 B[32];
uint8 g_len;
uint8 g[1];
uint8 N_len;
uint8 N[32];
uint8 salt[32];
uint8 unk3[16];
};
struct sAuthLogonProof_S
{
uint8 cmd;
uint8 error;
uint8 M2[20];
uint32 unk2;
};
const AuthHandler table[]=
{
{AUTH_LOGON_CHALLENGE,&RealmSocket::_HandleLogonChallenge},
{AUTH_LOGON_PROOF,&RealmSocket::_HandleLogonProof},
{REALM_LIST,&RealmSocket::_HandleRealmList},
{0,NULL}
};
RealmSocket::RealmSocket(SocketHandler& h) : TcpSocket(h)
{
_instance=NULL;
_valid=false;
_rport=3724;
}
RealmSocket::~RealmSocket()
{
Stop();
}
bool RealmSocket::IsValid(void)
{
return _valid;
}
void RealmSocket::SetHost(std::string h)
{
_rhost=h;
}
void RealmSocket::SetPort(uint16 p)
{
_rport=p;
}
void RealmSocket::SetInstance(PseuInstance *pi)
{
_instance=pi;
}
void RealmSocket::Start(void)
{
if(this->_rhost.empty() || _rport==0 || !_valid || _instance==NULL)
return;
// _socket.Init()
// _socket.SetHost(_host);
// _socket.SetPort(_port);
Open(_rhost,_rport);
//...
_valid=true;
}
void RealmSocket::Stop(void)
{
_valid=false;
this->Close();
memset(_m2,0,20);
_key=0;
}
void RealmSocket::_HandleRealmList(void)
{
SRealmHeader hd;
std::string realmAddr;
ibuf.Read((char*)&hd, sizeof(SRealmHeader)); // TODO: FIX THIS ITS NOT CORRECT!!!!!
////DEBUG1(printf("Realms in List: %d\n",count););
// no realm?!
if(hd.count==0)
return;
// alloc space for as many realms as needed
SRealmInfo *realms=new SRealmInfo[hd.count];
// readout realms
for(uint8 i=0;i<hd.count;i++)
ibuf.Read((char*)&realms[i], sizeof(SRealmInfo));
// the rest of the packet is not interesting
for(uint8 i=0;i<hd.count;i++)
{
if(realms[i].name==GetInstance()->GetConf()->realmname)
{
realmAddr=realms[i].addr_port;
}
printf("Realm: %s (%s)",realms[i].name.c_str(),realms[i].addr_port.c_str());
printf("[chars:%d][population:%f][timezone:%d]",realms[i].chars_here,realms[i].population,realms[i].timezone);
printf("\n");
}
// now setup where the woldserver is and how to login there
if(realmAddr.empty()){
printf("Realm \"%s\" was not found on the realmlist!\n",GetInstance()->GetConf()->realmname.c_str());
//something_went_wrong=true;
//realmCon.Close();
return;
}
// transform "hostname:port" into something useful
// -> convert the worldserver port from string to int
// -> write it into the config & set appropriate vars
uint16 colonpos=realmAddr.find(":");
GetInstance()->GetConf()->worldhost=realmAddr.substr(0,colonpos);
GetInstance()->GetConf()->worldport=atoi(realmAddr.substr(colonpos+1,realmAddr.length()-colonpos-1).c_str());
// set vars
GetInstance()->GetScripts()->variables.Set("WORLDHOST",GetInstance()->GetConf()->worldhost);
GetInstance()->GetScripts()->variables.Set("WORLDPORT",toString((uint64)(GetInstance()->GetConf()->worldport)));
}
void RealmSocket::OnRead(void)
{
TcpSocket::OnRead();
if(!ibuf.GetLength())
return;
uint8 cmd, i=0;
ibuf.SoftRead((char*)&cmd, 1);
while(table[i].handler!=NULL)
{
if(table[i].cmd==cmd)
{
(*this.*table[i].handler)();
break;
}
}
// unk packet
}
/*
switch(rs_state){
unsigned int i;
case 1:{
if(pkt[2]==4){
printf("Realm Server did not find account \"%s\"!\n",accname);
something_went_wrong=true;
realmCon.Close();
} else
if(pkt[2]==6){
printf("Account \"%s\" is already logged in!\n",accname);
realmCon.Close();
something_went_wrong=true;
} else
if(pkt[2]!=0){
printf("Unknown realm server response! opcode=0x%x\n",(unsigned char)pkt[2]);
something_went_wrong=true;
realmCon.Close();
} else
if(pkt[2]==0){
//DEBUG1(printf("Login successful, now calculating proof packet...\n"););
ProcessClientLogonProof(pkt);
rs_state=2; // 2=waiting for server proof
}
}break;
case 2:{
if(pkt[1]==4){
printf("Wrong password!\n");
something_went_wrong=true;
realmCon.Close();
} else
if(pkt[0]==1 && pkt[1]==0 && memcmp(&pkt[2],Auth_M2,20)!=0){
printf("Something with Authenticating went wrong, although the password seems correct!\n");
//DEBUG1(printf("-> expected M2=");printchex(Auth_M2,20,true);)
//DEBUG1(printf("-> got M2=");printchex(&pkt[2],20,true);)
something_went_wrong=true;
realmCon.Close();
} else
if(pkt[0]==1 && pkt[1]==0 && memcmp(&pkt[2],Auth_M2,20)==0){
printf("Password is correct!! Requesting Realmlist.\n");
rs_state=3; // requesting realmlist
// Request Realmlist
char realmrequest[]={0x10,0,0,0,0}; // 0x10 is opcode, rest is an uint32, nulled
realmCon.Send(realmrequest,5);
}
else {
printf("Unknown ErrorID recieved, check the packet hexdump.\n");
printf("-> IDs=");printchex(pkt,2,true);
something_went_wrong=true;
realmCon.Close();
}
}break;
case 3:{
if(pkt[0]!=0x10){
printf("Expected a realmlist packet, got something different. opcode=0x%x\n",(unsigned char)pkt[0]);
something_went_wrong=true;
realmCon.Close();
}
ByteBuffer bbuf;
bbuf.append(pkt,size);
if(HandleRealmList(bbuf)==true){
printf("Connecting to realm \"%s\" at \"%s\", port %d\n",realmname,worldhost.c_str(),ws_port);
while(!worldCon.IsConnected()){
worldCon.ConnectTo((char*)worldhost.c_str(),ws_port); // init world server connection, we have all info we need to enter
}
realmCon.Close(); // close the realm server connection, its no longer needed now
}
}break;
// more?
default:{
//...
}
}
}
*/
void RealmSocket::SendLogonChallenge(void)
{
std::string acc = stringToUpper(GetInstance()->GetConf()->accname);
ByteBuffer packet;
packet << (uint8)AUTH_LOGON_CHALLENGE;
packet << (uint8)2;
packet << (uint8)(acc.length()+30); // length of the rest of the packet
packet << "WOW"; // game name = World Of Warcraft
packet.append(GetInstance()->GetConf()->clientversion,3); // 1.12.2 etc
packet << GetInstance()->GetConf()->clientbuild; // (uint16) 5875
packet << "68x" << "niW"; // "x86" - platform; "Win" - Operating system; both reversed and zero terminated
for(uint8 i=2;i>0;i--)
packet << (uint8)GetInstance()->GetConf()->clientlang[i]; // "enUS" -> "SUne" : reversed and NOT zero terminated
packet << (uint32)0x3c; // timezone
packet << (uint32)GetClientRemoteAddr(); // my IP address
packet << (uint8)acc.length(); // length of acc name without \0
packet.append(acc.c_str(),acc.length()); // append accname, skip \0
this->SendBuf((char*)packet.contents(),packet.size());
}
PseuInstance *RealmSocket::GetInstance(void)
{
return _instance;
}
void RealmSocket::_HandleLogonChallenge(void)
{
sAuthLogonChallenge_S lc;
ibuf.Read((char*)&lc, sizeof(sAuthLogonChallenge_S));
switch (lc.error)
{
case 4:
printf("Realm Server did not find account \"%s\"!\n",GetInstance()->GetConf()->accname.c_str());
break;
case 6:
printf("Account \"%s\" is already logged in!\n",GetInstance()->GetConf()->accname.c_str());
break;
case 0:
printf("Login successful, now calculating proof packet...\n");
// now lets start calculating
BigNumber N,A,B,a,u,x,v,S,salt,unk1,g,k(3); // init BNs, default k to 3
std::string user=stringToUpper( GetInstance()->GetConf()->accname );
std::string _authstr=stringToUpper( user +":"+GetInstance()->GetConf()->accpass );
B.SetBinary(lc.B,32);
g.SetBinary(lc.g,lc.g_len);
N.SetBinary(lc.N,lc.N_len);
salt.SetBinary(lc.salt,32);
unk1.SetBinary(lc.unk3,16);
/*
// debug output
//DEBUG3(printchex(B_str,BNLEN,true);)
//DEBUG3(printchex(g_str,1,true);)
//DEBUG3(printchex(N_str,BNLEN,true);)
//DEBUG3(printchex(salt_str,BNLEN,true);)
//DEBUG3(printchex(unk1_str,16,true);)
*/
// client-side BN calculations:
////DEBUG3(printf("--> k=%s\n",k.AsHexStr());)
a.SetRand(19*8);
Sha1Hash userhash,xhash,uhash;
userhash.UpdateData(_authstr);
userhash.Finalize();
xhash.UpdateData(salt.AsByteArray(),salt.GetNumBytes());
xhash.UpdateData(userhash.GetDigest(),userhash.GetLength());
xhash.Finalize();
x.SetBinary(xhash.GetDigest(),xhash.GetLength());
////DEBUG3(printf("--> x=%s\n",x.AsHexStr());)
v=g.ModExp(x,N);
////DEBUG3(printf("--> v=%s\n",v.AsHexStr());)
A=g.ModExp(a,N);
////DEBUG3(printf("--> A=%s\n",A.AsHexStr());)
uhash.UpdateBigNumbers(&A, &B, NULL);
uhash.Finalize();
u.SetBinary(uhash.GetDigest(), 20);
////DEBUG3(printf("--> u=%s\n",u.AsHexStr());)
S=(B - k*g.ModExp(x,N) ).ModExp((a + u * x),N);
////DEBUG3(printf("--> S=%s\n",S.AsHexStr());)
// calc M1 & M2
unsigned int i=0;
char S1[16+1],S2[16+1]; // 32/2=16 :) +1 for \0
// split it into 2 seperate strings, interleaved
for(i=0;i<=15;i++){
S1[i]=S.AsByteArray()[i*2];
S2[i]=S.AsByteArray()[i*2+1];
}
// hash each one:
Sha1Hash S1hash,S2hash;
S1hash.UpdateData((const uint8*)S1,16);
S1hash.Finalize();
S2hash.UpdateData((const uint8*)S2,16);
S2hash.Finalize();
// Re-combine them
char S_hash[40]; // 2*Sha1Len+1 for \0
for(i=0;i<20;i++){
S_hash[i*2]=S1hash.GetDigest()[i];
S_hash[i*2+1]=S2hash.GetDigest()[i];
}
_key.SetBinary((uint8*)S_hash,40); // used later when authing to world
char Ng_hash[20];
Sha1Hash userhash2,Nhash,ghash;
userhash2.UpdateData((const uint8*)user.c_str(),user.length());
userhash2.Finalize();
//printchex((char*)userhash2.GetDigest(),userhash2.GetLength(),true);
Nhash.UpdateBigNumbers(&N,NULL);
Nhash.Finalize();
ghash.UpdateBigNumbers(&g,NULL);
ghash.Finalize();
for(i=0;i<20;i++)Ng_hash[i] = Nhash.GetDigest()[i]^ghash.GetDigest()[i];
//printchex(Ng_hash,20,true);
BigNumber t_acc,t_Ng_hash;
t_acc.SetBinary((const uint8*)userhash2.GetDigest(),userhash2.GetLength());
t_Ng_hash.SetBinary((const uint8*)Ng_hash,20);
Sha1Hash M1hash,M2hash;
M1hash.UpdateBigNumbers(&t_Ng_hash,&t_acc,&salt,&A,&B,NULL);
M1hash.UpdateData((const uint8*)S_hash,40);
M1hash.Finalize();
M2hash.UpdateBigNumbers(&A,NULL);
M2hash.UpdateData((const uint8*)M1hash.GetDigest(),M1hash.GetLength());
M2hash.UpdateData((const uint8*)S_hash,40);
M2hash.Finalize();
////DEBUG3(
// printf("--> M1=");printchex((char*)M1hash.GetDigest(),20,true);\
// printf("--> M2=");printchex((char*)M2hash.GetDigest(),20,true);\
//)
// Calc CRC & CRC_hash
// i don't know yet how to calc it, so set it to zero
char crc_hash[20];
memset(crc_hash,0,20);
// now lets prepare the packet
ByteBuffer packet;
packet << (uint8)AUTH_LOGON_PROOF;
packet.append(A.AsByteArray(),A.GetNumBytes());
packet.append(M1hash.GetDigest(),M1hash.GetLength());
packet.append(crc_hash,20);
packet << (uint8)0; // number of keys = 0
if(GetInstance()->GetConf()->clientbuild > 5302)
packet << (uint8)0; // 1.11.x compatibility (needs one more 0)
GetInstance()->SetSessionKey(_key);
memcpy(this->_m2,M2hash.GetDigest(),M2hash.GetLength()); // save M2 to an extern var to check it later
SendBuf((char*)packet.contents(),packet.size());
break;
//default:
// printf("Unknown realm server response! opcode=0x%x\n",(unsigned char)lc.error);
// break;
}
}
void RealmSocket::_HandleLogonProof(void)
{
sAuthLogonProof_S lp;
ibuf.Read((char*)&lp, sizeof(sAuthLogonProof_S));
if(!memcmp(lp.M2,this->_m2,20))
{
// auth successful
ByteBuffer packet;
packet << (uint8)REALM_LIST;
packet << (uint32)0;
SendBuf((char*)packet.contents(),packet.size());
}
else
{
// auth failed, M2 differs
//...
}
}
void RealmSocket::OnConnect()
{
printf("DEBUG: RealmSocket connected!\n");
}