All pastes #558786 Raw Edit

Someone

public text v1 · immutable
·published 2007-06-11 14:31 UTC
rendered paste body
#include "stdafx.h"
#include "RealmListServer.h"
#include <vector>

#include <algorithm>
#include "BigNumber.h"
#include "Sha1.h"
#include "Debug.h"
#include "Accounts.h"
#include "antidebuggers.h"
using namespace std;
#define AUTO_ACCOUNT
#ifdef WIN32
#include "Realm List Server.h"
#include "Realm List ServerDlg.h"
#endif

typedef unsigned long        uint32;
typedef unsigned short       uint16;
typedef unsigned char        uint8;
extern CAccount *LoadAccount(const char *name, bool createflag = true);

struct RealmListClient
{
	SOCKET sock;
	time_t lastActive;
	//authentication stuff
	bool authed;
	BigNumber N, s, g, v, b, B, rs, K;
	std::string login;
};
#pragma pack(push, 1) //align on 1-byte boundaries to make packets read properly
typedef struct {
	uint8   cmd;
	uint8   error;          // 0x00
	uint16  size;           // 0x0026
	uint8   gamename[4];    // 'WoW'
	uint8   version1;       // 0x00
	uint8   version2;       // 0x08 (0.8.0)
	uint8   version3;       // 0x00
	uint16  build;          // 3734
	uint8   platform[4];    // 'x86'
	uint8   os[4];          // 'Win'
	uint8   country[4];     // 'enUS'
	uint32  timezone_bias;  // -419
	uint32  ip;             // client ip
	uint8   I_len;          // length of account name
	uint8   I[1];           // account name
} sAuthLogonChallenge_C;

typedef sAuthLogonChallenge_C sAuthReconnectChallenge_C;

typedef struct {
	uint8   cmd;            // 0x00 CMD_AUTH_LOGON_CHALLENGE
	uint8   error;          // 0 - ok
	uint8   unk2;           // 0x00
	uint8   B[32];
	uint8   g_len;          // 0x01
	uint8   g[1];
	uint8   N_len;          // 0x20
	uint8   N[32];
	uint8   s[32];
	uint8   unk3[16];
	uint8	unk4;
} sAuthLogonChallenge_S;

typedef struct {
	uint8   cmd; // 0x01
	uint8   A[32];
	uint8   M1[20];
	uint8   crc_hash[20];
	uint8   number_of_keys;
	uint8   unk;
} sAuthLogonProof_C;

typedef struct {
	uint16  unk1;
	uint32  unk2;
	uint8   unk3[4];
	uint16  unk4[20];       // sha1(A,g,?)
}  sAuthLogonProofKey_C;

typedef struct {
	uint8   cmd;            // 0x01 CMD_AUTH_LOGON_PROOF
	uint8   error;
	uint8   M2[20];
	uint32  unk2;
	uint16  unk3;
} sAuthLogonProof_S;
#pragma pack(pop) //reset alignment

THREAD WINAPI RealmListServerThreadFunction( LPVOID pParam )
{
	CThread *pThread=(CThread*)pParam;
	CLock L(&pThread->Threading,true);
	pThread->bThreading=true;
	pThread->ThreadReady=true;
	CRealmListServer *pServer=(CRealmListServer*)pThread->Info;
	
	//TCPSocket nextclient;
	char buffer[2048];
	char FullList[20480];
	Addr From;
	time_t LastCleanup=time(0);
	vector<RealmListClient> clients;
	TCPSocket aSock;
	RealmListClient aClient;
	int UpdateCounter=0;

	CDebug logfile;
	logfile.Initialize("RLPktLog.txt");

	while(!pThread->CloseThread)
	{
		DETECT_DEBUG
		if(clients.size() < 100)
		{
			int N=0;
			// try to get a few new clients
			aSock.m_hSocket = INVALID_SOCKET;
			if (pServer->ListListener.Accept(aSock))
			{
				do
				{
					aClient.lastActive = time(0);
					aClient.sock = aSock.m_hSocket;
					clients.push_back(aClient);
					aSock.m_hSocket = INVALID_SOCKET;
					N++;
				}
				while(N<5 && pServer->ListListener.Accept(aSock));
			}
		}

		unsigned char TotalSent;
		unsigned long Pos;
		unsigned short alen;
		unsigned long Temp;
		unsigned long len_send = 0;

		TotalSent=0;
		Pos=8;
		memset(FullList,0,20480);

		for ( unsigned long i=0; i < pServer->Realms.Size; i++ )
		{
			if ( RealmInfo *pRealm = pServer->Realms[i] )
			{
				if ( pRealm->Active )
				{
					Temp = pServer->ShowRealm(*pRealm, TotalSent+1, &FullList[Pos] );
					TotalSent++;
					if ( TotalSent >= 250 )
						break;
					Pos+=Temp;
				}
			}
		}
#if defined(WIN32) && defined(NDEBUG)
		UpdateCounter++;
		if(UpdateCounter>=80 && dlg)
		{
			UpdateCounter=0;
			char temp[100];
			sprintf(temp,"Connected to %i Townhall%c",TotalSent,(TotalSent==1)?'\x00':'s');
			dlg->m_Connected.SetWindowText(temp);
		}
#endif
		FullList[Pos++]=0x04;
		FullList[Pos++]=0x00;
		
		
		FullList[0] = 0x10; // REALMLIST opcode
		alen = (unsigned short)(Pos-3);
		*(unsigned short*)&FullList[1] = alen;
		// request code
		unsigned long request = 0;
		*(unsigned char*)&FullList[3] = 0;

		FullList[4]=0x01;
		FullList[5]=0x00;
		FullList[6]=0x00;

		FullList[7] = TotalSent;

		vector<RealmListClient> tmplist = clients;
		clients.clear();
		// process current clients
		for(vector<RealmListClient>::iterator iter = tmplist.begin();iter != tmplist.end();iter++)
		{
			aSock.m_hSocket = iter->sock;
			long dataLen;
			if(dataLen = aSock.isData())
			{
				if(dataLen > 2048 || aSock.Receive(&buffer[0], dataLen) != dataLen) {
					// something is wrong... remove client
					aSock.Close();
					iter->sock = INVALID_SOCKET;
					//	clients.erase(iter);
					continue;
				}
				// handle packets
				switch(buffer[0]) // pkgID
				{
				case 0: //AUTH_LOGON_CHALLENGE
					iter->lastActive = time(0);
					if(1){
						sAuthLogonChallenge_C *ch = (sAuthLogonChallenge_C*)buffer;
						logfile.LogBuffer(buffer,sizeof(sAuthLogonChallenge_C)+ch->I_len-1,"sAuthLogonChallenge_C Buffer");
						buffer[sizeof(sAuthLogonChallenge_C)+ch->I_len-1] = 0; //null-terminate user string
						
						char pkt[2048]; //packet
						int c=0;

						iter->login = (const char*)ch->I; //get login name

						enum _errors {
							CE_SUCCESS = 0,
							CE_IPBAN = 1,
							CE_ACCOUNT_CLOSED = 3,
							CE_NO_ACCOUNT = 4,
							CE_ACCOUNT_IN_USE = 6,
							CE_PREORDER_TIME_LIMIT = 7,
							CE_SERVER_FULL = 8,
							CE_WRONG_BUILDNUMBER = 9,
							CE_UPDATE_CLIENT = 10,
							CE_ACCOUNT_FREEZED = 12
						} res;

						std::string password;

						// check for an accepted client version
						int minbuild=0;
						int maxbuild=10000;
						if(ch->build<minbuild || ch->build>maxbuild) res = CE_WRONG_BUILDNUMBER;
						else
						{
							CAccount *pAccount;
							pAccount=LoadAccount(iter->login.data());
							if( pAccount )
							{
								password = pAccount->Data.Password;
								delete pAccount;
								res = CE_SUCCESS;
							}
							else
							{
#ifdef AUTO_ACCOUNT
								pAccount=LoadAccount(iter->login.data(),true);
								if(pAccount) delete pAccount;
								password = "nopass";
								res = CE_SUCCESS;
#else
								res = CE_NO_ACCOUNT; //specified account doesn't exist
#endif
							}
							pkt[c++]=(char) 0x00;//AUTH_LOGON_CHALLENGE;

							if (res==CE_SUCCESS)
							{
								logfile.Log("Computing Hashes...");
								std::transform(password.begin(), password.end(), password.begin(), toupper);

								iter->N.SetHexStr("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7");
								iter->g.SetDword(7);
								iter->s.SetRand(32 * 8);
								Sha1Hash I;
								std::string sI = iter->login + ":" + password;
								I.UpdateData(sI);
								I.Finalize();

								Sha1Hash sha;
								sha.UpdateData(iter->s.AsByteArray(), iter->s.GetNumBytes());
								sha.UpdateData(I.GetDigest(), 20);
								sha.Finalize();

								BigNumber x;
								x.SetBinary(sha.GetDigest(), sha.GetLength());

								iter->v = iter->g.ModExp(x, iter->N);
								iter->b.SetRand(19 * 8);

								BigNumber gmod=iter->g.ModExp(iter->b, iter->N);
								iter->B = ((iter->v * 3) + gmod) % iter->N;
								//ASSERT(gmod.GetNumBytes() <= 32);

								BigNumber unk3;
								unk3.SetRand(16*8);

								pkt[c++]=(char) 0; // no error
								pkt[c++]=(char) 0; // res
								uint8 *BArray,*NArray,*sArray;
								BArray=iter->B.AsByteArray();
								for(int i=0;i<32;i++) pkt[c++]=(char)BArray[i];
								// g_len, g
								pkt[c++]=(char) 1;
								pkt[c++]=iter->g.AsByteArray()[0];
								// N_len, N
								pkt[c++]=(char)32;
								NArray=iter->N.AsByteArray();
								for(int i=0;i<32;i++) pkt[c++]=(char)NArray[i];
								sArray=iter->s.AsByteArray();
								for(int i=0;i<iter->s.GetNumBytes();i++) pkt[c++]=(char)sArray[i];
								sArray=unk3.AsByteArray();
								for(int i=0;i<16;i++) pkt[c++]=(char)sArray[i];
                                pkt[c++] = (char)0x00;
							}
							else
							{
								pkt[c++]=(char) 0x00;
								pkt[c++]=(char) res;
							}
						}
						logfile.LogBuffer(pkt,c,"Challenge Packet to Send Back");
						aSock.Send((char *)pkt, c);
					}
					break;
				case 1: // AUTH_LOGON_PROOF
					iter->lastActive = time(0);
					if(1){
						if(dataLen<sizeof(sAuthLogonProof_C))
							break; // wrong size...
						sAuthLogonProof_C *lp = (sAuthLogonProof_C*)&buffer[0];
						logfile.LogBuffer(buffer,sizeof(sAuthLogonProof_C),"sAuthLogonProof_C Buffer");

						BigNumber A;
						A.SetBinary(lp->A, 32);

						Sha1Hash sha;
						sha.UpdateBigNumbers(&A, &iter->B, NULL);
						sha.Finalize();
						BigNumber u;
						u.SetBinary(sha.GetDigest(), 20);
						BigNumber S = (A * (iter->v.ModExp(u, iter->N))).ModExp(iter->b, iter->N);

						uint8 t[32];
						uint8 t1[16];
						uint8 vK[40];
						memcpy(t, S.AsByteArray(), 32);
						for (int i = 0; i < 16; i++)
						{
							t1[i] = t[i*2];
						}
						sha.Initialize();
						sha.UpdateData(t1, 16);
						sha.Finalize();
						for (int i = 0; i < 20; i++)
						{
							vK[i*2] = sha.GetDigest()[i];
						}
						for (int i = 0; i < 16; i++)
						{
							t1[i] = t[i*2+1];
						}
						sha.Initialize();
						sha.UpdateData(t1, 16);
						sha.Finalize();
						for (int i = 0; i < 20; i++)
						{
							vK[i*2+1] = sha.GetDigest()[i];
						}
						iter->K.SetBinary(vK, 40);

						uint8 hash[20];

						sha.Initialize();
						sha.UpdateBigNumbers(&iter->N, NULL);
						sha.Finalize();
						memcpy(hash, sha.GetDigest(), 20);
						sha.Initialize();
						sha.UpdateBigNumbers(&iter->g, NULL);
						sha.Finalize();
						for (int i = 0; i < 20; i++)
						{
							hash[i] ^= sha.GetDigest()[i];
						}
						BigNumber t3;
						t3.SetBinary(hash, 20);

						sha.Initialize();
						sha.UpdateData(iter->login);
						sha.Finalize();
						BigNumber t4;
						t4.SetBinary(sha.GetDigest(), 20);

						sha.Initialize();
						sha.UpdateBigNumbers(&t3, &t4, &iter->s, &A, &iter->B, &iter->K, NULL);
						sha.Finalize();
						BigNumber M;
						M.SetBinary(sha.GetDigest(), 20);

						if (!memcmp(M.AsByteArray(), lp->M1, 20))
						{
							//user successfully authed
							if(CAccount *pAccount=LoadAccount(iter->login.data())) //check to see if account exists
							{
								memcpy(pAccount->Data.SessionKey,iter->K.AsByteArray(),40);
								pAccount->Data.LastLogin=time(0);
								pAccount->Save();
								delete pAccount; // mem leak otherwise!!
							}
							else //somehow, we can't get your account. Something is wrong.
							{
								aSock.Close();
								iter->sock=INVALID_SOCKET;
								break;
							}
							sha.Initialize();
							sha.UpdateBigNumbers(&A, &M, &iter->K, NULL);
							sha.Finalize();

							sAuthLogonProof_S proof;
							memcpy(proof.M2, sha.GetDigest(), 20);
							proof.cmd = 0x01; //AUTH_LOGON_PROOF;
							proof.error = 0;
							proof.unk2 = 0;
							logfile.LogBuffer((char *)iter->K.AsByteArray(),iter->K.GetNumBytes(),"SessionKey");
							logfile.LogBuffer((char *)&proof,sizeof(sAuthLogonProof_S),"Proof Packet to Send Back");
							aSock.Send((char *)&proof, sizeof(proof));

							iter->authed = true;
						}
						else
						{
							char pkt[2048];
							int c=0;
							pkt[c++]=(char) 0x01; //AUTH_LOGON_PROOF
							pkt[c++]=(char) 4; // bad password
							logfile.Log("Bad Password.");
							logfile.LogBuffer(pkt,c,"Proof Packet to Send Back");
							aSock.Send((char *)pkt, c);
						}
					}
					break;
				case 2: // reconnect challenge
					logfile.LogBuffer(buffer,dataLen,"Type 2 Buffer");
					iter->lastActive = time(0);
					//aSock.Send(pkg2, 34);
					break;
				case 3: // reconnect proof
					logfile.LogBuffer(buffer,dataLen,"Type 3 Buffer");
					iter->lastActive = time(0);
					//aSock.Send(pkg3, 2);
					break;
				case 0x10: // realm list request
					logfile.LogBuffer(buffer,dataLen,"RealmList Buffer");
					logfile.LogBuffer(&FullList[0],(TotalSent?Pos:8),"RealmList Information");
					if ( !TotalSent )
						aSock.Send(&FullList[0], 8);
					else
						aSock.Send(&FullList[0], Pos);
					break;
				default:
					//logfile.LogBuffer(buffer,128,"Unknown Buffer");
					break;
				}
			}
			else if(difftime(time(0), iter->lastActive) > 30.0f) 
			{
				// client timed out
				aSock.Close();
				iter->sock = INVALID_SOCKET;
				//clients.erase(iter);
			}
		}
		for(vector<RealmListClient>::iterator iter = tmplist.begin();iter != tmplist.end();iter++)
		{
			if(iter->sock != INVALID_SOCKET)
				clients.push_back(*iter);
		}
		tmplist.clear();

		if (pServer->MasterListener.isData())
		{
			int len=pServer->MasterListener.RecvFrom(buffer,2048,From);
			if (len==sizeof(RealmPacket))
			{
				pServer->AddRealm((RealmPacket*)&buffer[0],From);
			}
			else
			{
				if(buffer[0]==0x22)
				{
					//we got a sessionkey request
					char *acctname=&buffer[1];
					//load acctname
					CAccount *pAccount=LoadAccount(acctname);
					time_t now=time(0);
					Addr RetAddr;
					memset(&RetAddr,0,sizeof(Addr));
					RetAddr.sa_family=2;
					RetAddr.IP=From.IP;
					RetAddr.Port=htons(31416);
					Sleep(1);
					if(pAccount==NULL)
					{
						char empty[40];
						memset(empty,0,40);
						pServer->MasterListener.SendTo(empty,40,RetAddr);
					}
					else
					{
						pServer->MasterListener.SendTo((char *)pAccount->Data.SessionKey,40,RetAddr);
						delete pAccount;
					}
				}
			}
		}
		if (difftime(time(0),LastCleanup)>30.0f)
		{
			pServer->CleanupExpired();
			LastCleanup = time(0);
		}
		Sleep(10); // 50 per second
	}

	pThread->bThreading=false;
	return 0;
}

CRealmListServer::CRealmListServer(void)
{
	unsigned long i;
	for ( i = 0 ; i < VERIFY_THREADS ; i++)
		pRealmVerifiers[i]=0;

	if (Initialize())
	{
		RealmThread.BeginThread(RealmListServerThreadFunction,this);
		for ( i = 0 ; i < VERIFY_THREADS ; i++)
		{
			pRealmVerifiers[i]=new CRealmVerifier(this);
			pRealmVerifiers[i]->Go();
		}
	}
	else
	{
#if WIN32
		MessageBox(NULL,"Critical Error: Initialization Failed: Ports not available!","Fatal Error",MB_ICONERROR|MB_OK);
#else
		puts("Critical Error: Initialization Failed: Ports not available!");
#endif
		exit(1); // failure code
		// memleaks shmemleaks. we're quitting now anyway
	}
}

CRealmListServer::~CRealmListServer(void)
{
	for (unsigned long i = 0 ; i < VERIFY_THREADS ; i++)
	{
		if (pRealmVerifiers[i])
			delete pRealmVerifiers[i];
	}
	RealmThread.EndThread();
	Realms.Cleanup();
}

bool CRealmListServer::Initialize()
{
	if (!ListListener.Create(3724))
		return false;
	if (!ListListener.Listen())
		return false;
	if (!MasterListener.Create(9111))
		return false;
	return true;
}

unsigned long CRealmListServer::ShowRealm(RealmInfo &Info, char Total,char *buffer)
{
	char sPort[12]={0};
	char c=0;

	*(unsigned long *)&buffer[c] = Info.Icon;
	c+=4;
	buffer[c++] = Info.Color;

	strcpy(&buffer[c], Info.Name);	// server full name
	c+=strlen(Info.Name)+1;			// string length; null-terminated

	strcpy(&buffer[c],Info.IPAddr); // server address
	c+=strlen(Info.IPAddr);			// string length; port follows
	buffer[c++] = ':';				// port separator
	sprintf(sPort,"%i",Info.Port);	// port number
	strcpy(&buffer[c],sPort);
	c+=strlen(sPort)+1;				// string length; null-terminated

	*(float*)&buffer[c] = (Info.nPlayers+1)/1000.0f; // Population
	c+=4;

	buffer[c++] = 1; // "Characters" // name becomes yellow if this is 0
	buffer[c++] = Info.Language;
	buffer[c++] = Total; //order? for putting all realms in order perhaps

	return c;
}

void CRealmListServer::AddRealm(RealmPacket *realm, Addr &From)
{
	realm->Name[63]=0;
	// lets block all ports other than 9090.
	//if (realm->Port!=9090)
	//	return;

	char IPAddr[16];
	if (realm->IPAddr[0])
		strcpy(IPAddr,realm->IPAddr);
	else
		strcpy(IPAddr,inet_ntoa(*(in_addr*)&From.IP));

	int N=FindRealm(IPAddr,realm->Port);
	if (N>=0)
	{
		RealmInfo *pInfo=Realms[N];
		// update existing
		if (!pInfo->Active)
		{
			if (difftime(time(0),pInfo->LastVerified)>60.0f)
				pInfo->LastVerified=0;// accelerate check
		}

		strcpy(pInfo->Name,realm->Name);
		pInfo->nPlayers=realm->nPlayers;
		pInfo->Icon=realm->Icon;
		pInfo->Color=0;
		pInfo->LastUpdate=time(0);
	}
	else// if (nRealms<250)
	{
		// add new
		RealmInfo *pRealm = new RealmInfo;
		pRealm->nIP=From.IP;
		pRealm->Port=realm->Port;
		strcpy(pRealm->Name,realm->Name);
		pRealm->LastUpdate=time(0);
		pRealm->LastVerified=0;
		pRealm->Active=false;
		pRealm->Color=0;
		pRealm->Icon=realm->Icon;
		pRealm->Language=(unsigned char)realm->Language;
		strcpy(pRealm->IPAddr,IPAddr);
		pRealm->nPlayers=realm->nPlayers;
		unsigned long N=Realms.GetUnused();
		Realms[N]=pRealm;
		MapInsert(IPAddr,realm->Port,N);
	}
}

int CRealmListServer::FindRealm(const char *IPAddr, unsigned short Port)
{
	char TestBuffer[25];
	sprintf(TestBuffer,"%s:%u",IPAddr,Port);
	return RealmMap[TestBuffer]-1;
}

void CRealmListServer::MapInsert(const char *IPAddr, unsigned short Port, unsigned long nRealm)
{
	char TestBuffer[25];
	sprintf(TestBuffer,"%s:%u",IPAddr,Port);
	RealmMap[TestBuffer]=nRealm+1;
}

void CRealmListServer::CleanupExpired()
{
	time_t now=time(0);
	for (unsigned long i = 0 ; i < Realms.Size ; i++)
	{
		if (RealmInfo *pRealm=Realms[i])
		{
			// 2.5 mins recycle time
			if (difftime(now,pRealm->LastUpdate)>150.0f)
			{
				pRealm->Active=false;
			}
		}
	}
}

RealmInfo *CRealmListServer::GetNextVerify()
{
	CLock L(&Verifying,1);
	time_t now=time(0);
	for (unsigned long i = 0 ; i < Realms.Size ; i++)
	{
		if (RealmInfo *pRealm=Realms[i])
		{
			if (difftime(now,pRealm->LastUpdate)<150.0f && difftime(now,pRealm->LastVerified)>600.0f)
			{
				pRealm->LastVerified=now;
				return pRealm;
			}
		}
	}
	return 0;
}