#include "stdafx.h" #include "ConfigIni.h" #include "Logger.h" #include "LoginClient.h" extern class CConfig g_cfg; /** * Function replaces IP and port of server with ID = serverID * in ServerList packet. * Usage: * unsigned char IP_new = {127,0,0,1}; * int port_new = 7777; * unsigned int serverID = 1; * bool ret = FL_ReplaceServerIPPort( rawBytes, rawBytesLen, serverID, IP_new, port_new ); * * returns true on success, false on error */ bool FL_ReplaceServerIPPort( unsigned char *b, unsigned int blen ) { if( !b || (blen<26) ) return false; unsigned char fake_gsip[4] = {0,0,0,0}; unsigned long lInetAddr = L2PNet_inet_addr( g_cfg.FakeListenGameIP ); memcpy( (void *)&fake_gsip, (const void *)&lInetAddr, sizeof(lInetAddr) ); unsigned short int sport = (unsigned short int)g_cfg.FakeListenGamePort; unsigned char cport[2] = { (unsigned char)( sport & 0x00FF), (unsigned char)((sport & 0xFF00) >> 8) }; int gscount = (int)b[3]; const int ServerList_Header_Size = 5; const int ServerList_OneServerInfo_Size = 21; int pos = ServerList_Header_Size; // pass packet header int i; //while( pos < (int)blen ) log_error( LOG_DEBUG, "FL_ReplaceServerIPPort(): replacing IPs and ports for %d servers in list\n", gscount ); for( i=0; isetBytes( b, sizeof(b) ); } */ LoginClient::LoginClient() { memset( sessionKey1, 0, sizeof(sessionKey1) ); memset( sessionKey2, 0, sizeof(sessionKey2) ); gsIP[0] = gsIP[1] = gsIP[2] = gsIP[3] = 0; gsPort = 0; g_login_client = this; state = LCST_OFFLINE; } LoginClient::~LoginClient() { g_login_client = NULL; memset( sessionKey1, 0, sizeof(sessionKey1) ); memset( sessionKey2, 0, sizeof(sessionKey2) ); gsIP[0] = gsIP[1] = gsIP[2] = gsIP[3] = 0; gsPort = 0; state = LCST_OFFLINE; } bool LoginClient::getSessionKeys( unsigned char *sKey1, unsigned char *sKey2 ) { if( !sKey1 || !sKey2 ) return false; memcpy( sKey1, this->sessionKey1, 8 ); memcpy( sKey2, this->sessionKey2, 8 ); return true; } bool LoginClient::getPlayServerAddress( unsigned char *ip, unsigned short int *port ) { if( !ip || !port ) return false; memcpy( ip, this->gsIP, sizeof(this->gsIP) ); (*port) = this->gsPort; return true; } bool LoginClient::ProcessClient( SOCKET scl ) { bool retVal = false; state = LCST_OFFLINE; // connect to real login server log_error( LOG_DEBUG, "Establishing redirecting connection to %s:%d ", g_cfg.RealLoginServerIP, g_cfg.RealLoginServerPort ); SOCKET sr = L2PNet_TCPsocket_create( true ); if( sr == INVALID_SOCKET ) { log_error_np( LOG_ERROR, "\n" ); log_error( LOG_ERROR, "ProcessClient(): cannot create socket!\n" ); return false; } L2PNet_connect( sr, g_cfg.RealLoginServerIP, (unsigned short)g_cfg.RealLoginServerPort ); int nTries = 20; int r = 0; while( nTries > 0 ) { log_error_np( LOG_DEBUG, "." ); r = L2PNet_select( sr, L2PNET_SELECT_WRITE, 20000, NULL, NULL ); if( r == 1 ) break; nTries--; } if( r == 1 ) { log_error_np( LOG_DEBUG, " Connected. Tries left: %d\n", nTries ); state = LCST_CONNECTED; } else { state = LCST_OFFLINE; // close sockets L2PNet_shutdown( scl ); L2PNet_closesocket( scl ); L2PNet_shutdown( sr ); L2PNet_closesocket( sr ); log_error_np( LOG_DEBUG, " Not connected.\n" ); return false; } this->postNotify( LCN_STATECHANGE, 1 ); // connected // TODO: Login server session vars // login server session vars unsigned char ls_sessionID[4] = {0,0,0,0}; unsigned int ls_sessionIDUInt = 0; unsigned int ls_protoVer = 0; unsigned char ls_RSA_pubKeyMod[128]; unsigned char ls_ggShit[16] = {0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0}; unsigned char ls_newBFKey[16] = {0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0}; unsigned int ls_ggAuthResponse = 0; unsigned char ls_sessionKey1[8] = {0,0,0,0, 0,0,0,0}; unsigned char ls_sessionKey2[8] = {0,0,0,0, 0,0,0,0}; unsigned char ls_nGameServersCount = 0; unsigned char ls_lastServerID = 0; // last server ID unsigned char myGameServerIP[4] = {0,0,0,0}; //char sMyGSIP[16] = {0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0}; unsigned short int myGameServerPort = 0; int ls_chosenGameServer = -1; // login server session vars memset memset( ls_RSA_pubKeyMod, 0, sizeof(ls_RSA_pubKeyMod) ); // network vars int netTimeout = 30; int i = 0; unsigned char ptype = 0; // packet type byte unsigned char *rraw = NULL; // raw packet bytes unsigned int rcvdLen = 0; unsigned int sentLen = 0; // packet objects L2LoginPacket *pack = NULL; L2Login_Init *p_init = NULL; //L2Login_RequestGGAuth *p_rgga = NULL; L2Login_GGAuthResponse *p_ggar = NULL; L2Login_LoginOK *p_lok = NULL; //L2Login_LoginFail *p_lfail = NULL; L2Login_ServerList *p_sl = NULL; L2Login_RequestServerLogin *p_rgsl = NULL; L2Login_PlayOK *p_pok = NULL; //L2Login_PlayFail *p_pfail = NULL; // ServerInfo struct struct L2GameServerInfo gsi; memset( &gsi, 0, sizeof(gsi) ); struct L2GameServerInfo gameservers_info[32]; memset( gameservers_info, 0, sizeof(gameservers_info) ); // debug file //FILE *f = NULL; // Init: receive Init packet from real login server rcvdLen = 0; rraw = L2PacketReceive_malloc( sr, netTimeout*1000, &rcvdLen ); if( !rraw ) { log_error( LOG_ERROR, "receive failed!\n" ); goto l_netError; } log_error( LOG_DEBUG, "ProcessClient(): rcvd %u bytes of Init packet from server.\n", rcvdLen ); // resend it to client sentLen = 0; L2PacketSend2( rraw, scl, netTimeout*1000, &sentLen ); log_error( LOG_DEBUG, "ProcessClient(): resent %u bytes of Init packet to client.\n", sentLen ); if( sentLen != rcvdLen ) { log_error( LOG_ERROR, "rcvd != sent! (%u != %u)\n", rcvdLen, sentLen ); goto l_netError; } // decode and parse server's Init packet p_init = new L2Login_Init( rraw, rcvdLen ); if( p_init->decodeBlowfish( true ) ) // true - Use Static BF Key { if( p_init->decodeXOR() ) { // read packet data p_init->read_SessionID( ls_sessionID ); memcpy( &ls_sessionIDUInt, ls_sessionID, sizeof(ls_sessionID) ); ls_protoVer = p_init->read_ProtoVer(); p_init->read_RSA_pubKeyMod( ls_RSA_pubKeyMod ); p_init->read_GGShit( ls_ggShit ); p_init->read_DynamicBFKey( ls_newBFKey ); // printfs log_error( LOG_DEBUGDUMP, "SessionID: %02X %02X %02X %02X\n", ls_sessionID[0], ls_sessionID[1], ls_sessionID[2], ls_sessionID[3] ); log_error( LOG_DEBUGDUMP, "Protocol ver: %08X\n", ls_protoVer ); p_init->unscramble_RSA_PubKeyMod( ls_RSA_pubKeyMod ); log_error( LOG_DEBUGDUMP, "Dynamic Blowfish key: " ); i = 0; while( i < 16 ) log_error_np( LOG_DEBUGDUMP, "%02X ", ls_newBFKey[i++] ); log_error_np( LOG_DEBUGDUMP, "\n" ); } else log_error( LOG_ERROR, "XOR decode failed\n" ); } else log_error( LOG_ERROR, "BF decode failed\n" ); delete p_init; p_init = NULL; // free raw pack buffer free( rraw ); rraw = NULL; // RequestGGAuth // receive client's answer to server's Init packet. probably it is RequestGGAuth packet rcvdLen = 0; rraw = L2PacketReceive_malloc( scl, netTimeout*1000, &rcvdLen ); if( !rraw ) { log_error( LOG_ERROR, "receive failed!\n" ); goto l_netError; } log_error( LOG_DEBUG, "ProcessClient(): rcvd %u bytes of RequestGGAuth packet from client.\n", rcvdLen ); // resend it to server sentLen = 0; L2PacketSend2( rraw, sr, netTimeout*1000, &sentLen ); log_error( LOG_DEBUG, "ProcessClient(): resent %u bytes of RequestGGAuth packet to server.\n", sentLen ); if( sentLen != rcvdLen ) { log_error( LOG_ERROR, "rcvd != sent! (%u != %u)\n", rcvdLen, sentLen ); goto l_netError; } // try to decode and parse client's response //p_rgga = new L2Login_RequestGGAuth( rraw, rcvdLen ); //p_rgga->setDynamicBFKey( ls_newBFKey, 16 ); //p_rgga->decodeBlowfish( false ); //p_rgga->dumpToFile( stdout ); //delete p_rgga; //p_rgga = NULL; // free raw pack buffer free( rraw ); rraw = NULL; // GGAuthResponse // receive server's answer to client's RequstGGAuth packet rcvdLen = 0; rraw = L2PacketReceive_malloc( sr, netTimeout*1000, &rcvdLen ); if( !rraw ) { log_error( LOG_ERROR, "receive failed!\n" ); goto l_netError; } log_error( LOG_DEBUG, "ProcessClient(): rcvd %u bytes of GGAuthResponse packet from server.\n", rcvdLen ); // resend it to client sentLen = 0; L2PacketSend2( rraw, scl, netTimeout*1000, &sentLen ); log_error( LOG_DEBUG, "ProcessClient(): resent %u bytes of GGAuthResponse packet to client.\n", sentLen ); if( sentLen != rcvdLen ) { log_error( LOG_ERROR, "rcvd != sent! (%u != %u)\n", rcvdLen, sentLen ); goto l_netError; } p_ggar = new L2Login_GGAuthResponse( rraw, rcvdLen ); //if( p_ggar ) printf( "GGAuthResponse object created\n" ); //printf( "Setting bytes 0x%p %u len\n", rraw, rcvdLen ); //if( p_ggar->setBytes( rraw, rcvdLen ) ) // printf( "ProcessClient(): GGAuthResponse bytes set OK\n" ); p_ggar->setDynamicBFKey( ls_newBFKey, 16 ); p_ggar->decodeBlowfish( false ); ptype = p_ggar->getPacketType(); if( ptype != 0x0B ) log_error( LOG_WARNING, "Unknown GGAuthResponse code 0x%02X!!!!\n", (unsigned int)ptype ); else log_error( LOG_OK, "GGAuthResponse: bypass GameGuard authorization...\n" ); ls_ggAuthResponse = p_ggar->read_Response(); if( ls_ggAuthResponse != ls_sessionIDUInt ) { log_error( LOG_WARNING, "sessionID != ggAuthResponse (%04X != %04X)\n", ls_sessionIDUInt, ls_ggAuthResponse ); } else log_error( LOG_DEBUG, "++++ SessionID == ggAuthResponse ++++\n" ); delete p_ggar; p_ggar = NULL; // free raw pack buffer free( rraw ); rraw = NULL; state = LCST_AUTHED_GG; // RequestAuthLogin // client must now attempt to login to server with its login and password rcvdLen = 0; rraw = L2PacketReceive_malloc( scl, netTimeout*1000, &rcvdLen ); if( !rraw ) { log_error( LOG_ERROR, "receive failed!\n" ); goto l_netError; } log_error( LOG_DEBUG, "ProcessClient(): rcvd %u bytes of RequestAuthLogin packet from client.\n", rcvdLen ); // resend it to server sentLen = 0; //L2PacketSend( rraw, sr, netTimeout, 0, &sentLen ); L2PacketSend2( rraw, sr, netTimeout*1000, &sentLen ); log_error( LOG_DEBUG, "ProcessClient(): resent %u bytes of RequestAuthLogin packet to server.\n", sentLen ); if( sentLen != rcvdLen ) { log_error( LOG_ERROR, "rcvd != sent! (%u != %u)\n", rcvdLen, sentLen ); goto l_netError; } // display RequestAuthLogin contents //pack = new L2LoginPacket(); //pack->setBytes( rraw, sentLen ); //pack->setDynamicBFKey( ls_newBFKey, 16 ); //pack->decodeBlowfish( false ); //pack->dumpToFile( stdout ); //delete pack; //pack = NULL; // free raw pack buffer free( rraw ); rraw = NULL; // LoginOK / LoginFail // Login server must now reply to RequestAuthLogin and tell us about result rcvdLen = 0; rraw = L2PacketReceive_malloc( sr, netTimeout*1000, &rcvdLen ); if( !rraw ) { log_error( LOG_ERROR, "receive failed!\n" ); goto l_netError; } log_error( LOG_DEBUG, "ProcessClient(): rcvd %u bytes of response to RequestAuthLogin from server.\n", rcvdLen ); // resend it to client sentLen = 0; L2PacketSend2( rraw, scl, netTimeout*1000, &sentLen ); log_error( LOG_DEBUG, "ProcessClient(): resent %u bytes of response to client.\n", sentLen ); if( sentLen != rcvdLen ) { log_error( LOG_ERROR, "rcvd != sent! (%u != %u)\n", rcvdLen, sentLen ); goto l_netError; } log_error( LOG_DEBUG, "Server answer to RequestAuthLogin:\n" ); pack = new L2LoginPacket( rraw, rcvdLen ); pack->setDynamicBFKey( ls_newBFKey, 16 ); pack->decodeBlowfish( false ); //pack->dumpToFile( stdout ); ptype = pack->getPacketType(); if( ptype == 0x03 ) { log_error( LOG_DEBUG, "++++ Login OK! ++++\n" ); state = LCST_AUTHED_LOGIN; p_lok = new L2Login_LoginOK(); p_lok->setBytes( pack->getBytesPtr(), pack->getPacketSize() ); p_lok->read_sessionKey1( ls_sessionKey1 ); delete p_lok; p_lok = NULL; log_error( LOG_DEBUGDUMP, "SessionKey1: " ); for( i=0; isetBytes( rraw, rcvdLen ); // out RAW //printf( "RAW ServerList packet:\n" ); //p_sl->dumpToFile( stdout ); p_sl->setDynamicBFKey( ls_newBFKey, 16 ); p_sl->decodeBlowfish( false ); // out BF decoded //printf( "BF Decoded ServerList packet:\n" ); //p_sl->dumpToFile( stdout ); // save to file :) //f = fopen( "ServerList.txt", "wt" ); //if( f ) p_sl->dumpToFile( f ); //fclose( f ); // parse ptype = p_sl->getPacketType(); if( ptype == 0x04 ) log_error( LOG_DEBUG, "++++ ServerList ++++\n" ); p_sl->read_header( &ls_nGameServersCount, &ls_lastServerID ); log_error_np( LOG_DEBUGDUMP, "\n" ); log_error( LOG_DEBUGDUMP, "ServerList: %d game servers, last server: %d\n", (int)ls_nGameServersCount, (int)ls_lastServerID ); for( i=0; iread_next_GS_Info( &gsi ) ) { log_error( LOG_DEBUGDUMP, "*** Server %d Info ***\n", i ); log_error( LOG_DEBUGDUMP, "*** Server ID: %d\n", (int)gsi.gsID ); log_error( LOG_DEBUGDUMP, "*** Server addr: %d.%d.%d.%d:%d\n", (int)gsi.gsIP[0], (int)gsi.gsIP[1], (int)gsi.gsIP[2], (int)gsi.gsIP[3], (int)gsi.gsPort ); log_error( LOG_DEBUGDUMP, "*** Online: %d / %d (up: %d)\n", (int)gsi.gsPlayersOnline, (int)gsi.gsPlayersMax, (int)gsi.gsIsUp ); log_error_np( LOG_DEBUGDUMP, "\n" ); // save this gameserver in array memcpy( (&gameservers_info[i]), &gsi, sizeof(L2GameServerInfo) ); // is this GS chosen to play on? // ignore /*if( ((int)gsi.gsID) == g_cfg.PlayGameServerNo ) { log_error( LOG_DEBUGDUMP, "^^^^ we play here! ^^^^\n\n" ); memcpy( myGameServerIP, gsi.gsIP, 4 ); myGameServerPort = gsi.gsPort; }*/ } else { log_error( LOG_ERROR, "p_sl->read_next_GS_Info( &gsi ) failed!\n" ); } } // replace server IP and port to what we need to intercept GS connection if( FL_ReplaceServerIPPort( (unsigned char *)p_sl->getBytesPtr(), p_sl->getPacketSize() ) ) { log_error( LOG_DEBUG, "[++++] OK: ServerList packet modified\n" ); // packet modified, we must update checksum in it if( FL_RecalculateChechksum( (unsigned char *)p_sl->getBytesPtr(), p_sl->getPacketSize() ) ) { log_error( LOG_DEBUGDUMP, "[++++] OK: ServerList packet checksum recalculated\n" ); } else { log_error( LOG_ERROR, "ServerList packet checksum recalculate error!\n" ); } } else log_error( LOG_ERROR, "ServerList packet modify error\n" ); //GenerateFakePacket( p_sl ); // was called for debug only //printf( "================\n" ); //printf( "[I] ServerList packet after replacing its IP & port:\n" ); //p_sl->dumpToFile( stdout ); //printf( "=================\n" ); // encode modified packet again p_sl->encodeBlowfish( false ); // out encoded //printf( "BF Encoded ServerList packet:\n" ); //p_sl->dumpToFile( stdout ); //rcvdLen = p_sl->getPacketSize(); // resend it to client log_error( LOG_DEBUG, "Now resend %u bytes of ServerList to client...\n", rcvdLen ); sentLen = 0; //L2PacketSend( p_sl->getBytesPtr(), scl, netTimeout, 0, &sentLen ); L2PacketSend2( p_sl->getBytesPtr(), scl, netTimeout*1000, &sentLen ); log_error( LOG_DEBUG, "ProcessClient(): resent %u bytes of ServerList packet to client.\n", sentLen ); if( sentLen != rcvdLen ) { log_error( LOG_ERROR, "rcvd != sent! (%u != %u)\n", rcvdLen, sentLen ); goto l_netError; } delete p_sl; p_sl = NULL; // free raw pack buffer free( rraw ); rraw = NULL; // RequestServerLogin // client now must request server login rcvdLen = 0; //rraw = L2PacketReceive( scl, netTimeout, 0, &rcvdLen ); rraw = L2PacketReceive_malloc( scl, netTimeout*1000, &rcvdLen ); if( !rraw ) { log_error( LOG_ERROR, "receive failed!\n" ); goto l_netError; } log_error( LOG_DEBUG, "ProcessClient(): rcvd %u bytes of RequestServerLogin packet from client.\n", rcvdLen ); // resend it to server sentLen = 0; //L2PacketSend( rraw, sr, netTimeout, 0, &sentLen ); L2PacketSend2( rraw, sr, netTimeout*1000, &sentLen ); log_error( LOG_DEBUG, "ProcessClient(): resent %u bytes of RequestServerLogin packet to server.\n", sentLen ); if( sentLen != rcvdLen ) { log_error( LOG_ERROR, "rcvd != sent! (%u != %u)\n", rcvdLen, sentLen ); goto l_netError; } // dump info about RequestServerLogin packet p_rgsl = new L2Login_RequestServerLogin( rraw, rcvdLen ); p_rgsl->setDynamicBFKey( ls_newBFKey, 16 ); p_rgsl->decodeBlowfish( false ); // out //pack->dumpToFile( stdout ); ptype = p_rgsl->getPacketType(); if( ptype == 0x02 ) log_error( LOG_DEBUG, "++++ RequestServerLogin ++++\n" ); p_rgsl->readInt(); p_rgsl->readInt(); // pass sessionKey1 ls_chosenGameServer = (int)p_rgsl->read_GameServerID(); log_error( LOG_DEBUG, "[INFO]: Client chooses to play on gameserver %d\n", ls_chosenGameServer ); delete p_rgsl; p_rgsl = NULL; // free raw pack buffer free( rraw ); rraw = NULL; // copy info about chosen game server if( (ls_chosenGameServer>=1) && (ls_chosenGameServer<=255) ) { for( i=0; i<(int)ls_nGameServersCount; i++ ) { if( gameservers_info[i].gsID == ls_chosenGameServer ) { memcpy( &(myGameServerIP), &(gameservers_info[i].gsIP), 4 ); myGameServerPort = gameservers_info[i].gsPort; break; } } //g_cfg.PlayGameServerNo = ls_chosenGameServer; // PlayGameServerNo removed from config log_error( LOG_DEBUG, "we play on GS #%d: %d.%d.%d.%d:%d\n", ls_chosenGameServer, (int)myGameServerIP[0], (int)myGameServerIP[1], (int)myGameServerIP[2], (int)myGameServerIP[3], myGameServerPort ); } else { log_error( LOG_ERROR, "Client choosed GS ID %d, which is INVALID! must be [1..255]\n", ls_chosenGameServer ); } // PlayOK / PlayFail // login server now sends to client if client can play o selected server rcvdLen = 0; //rraw = L2PacketReceive( sr, netTimeout, 0, &rcvdLen ); rraw = L2PacketReceive_malloc( sr, netTimeout*1000, &rcvdLen ); if( !rraw ) { log_error( LOG_ERROR, "receive failed!\n" ); goto l_netError; } log_error( LOG_DEBUG, "ProcessClient(): rcvd %u bytes of response to RequestServerLogin packet from server.\n", rcvdLen ); // resend it to client sentLen = 0; L2PacketSend2( rraw, scl, netTimeout*1000, &sentLen ); log_error( LOG_DEBUG, "ProcessClient(): resent %u bytes of response to RequestServerLogin packet to client.\n", sentLen ); if( sentLen != rcvdLen ) { log_error( LOG_ERROR, "rcvd != sent! (%u != %u)\n", rcvdLen, sentLen ); goto l_netError; } pack = new L2LoginPacket(); pack->setBytes( rraw, sentLen ); pack->setDynamicBFKey( ls_newBFKey, 16 ); pack->decodeBlowfish( false ); // out... //printf( "\nResponse to RequestServerLogin:\n" ); //pack->dumpToFile( stdout ); // parse result ptype = pack->getPacketType(); if( ptype == 0x06 ) // PlayFail { log_error( LOG_ERROR, "---- PlayFail! ----\n" ); goto l_netError; } else if( ptype == 0x07 ) // PlayOK! { log_error( LOG_DEBUG, "++++ PlayOK! ++++\n" ); p_pok = new L2Login_PlayOK(); p_pok->setBytes( pack->getBytesPtr(), pack->getPacketSize() ); p_pok->read_sessionKey2( ls_sessionKey2 ); delete p_pok; p_pok = NULL; log_error( LOG_DEBUGDUMP, "SessionKey#2: " ); for( i=0; i<8; i++ ) log_error_np( LOG_DEBUGDUMP, "%02X ", ls_sessionKey2[i] ); log_error_np( LOG_DEBUGDUMP, "\n" ); } else { log_error( LOG_ERROR, "=====================================\n" ); log_error( LOG_ERROR, "???? Unknown packet type 0x%02X! ????\n", (unsigned int)ptype ); pack->dumpToFile( stdout ); log_error( LOG_ERROR, "=====================================\n" ); goto l_netError; } // delete... delete pack; pack = NULL; // free raw packet data free( rraw ); rraw = NULL; // mark as connected OK retVal = true; goto l_closeSockets; // jump over netError handler l_netError: log_error( LOG_ERROR, "ProcessClient(): some network error :\\\n" ); if( rraw ) free( rraw ); rraw = NULL; retVal = false; // mark as connection failed l_closeSockets: // close sockets L2PNet_shutdown( scl ); L2PNet_closesocket( scl ); L2PNet_shutdown( sr ); L2PNet_closesocket( sr ); state = LCST_OFFLINE; // save data memcpy( this->sessionKey1, ls_sessionKey1, sizeof(ls_sessionKey1) ); memcpy( this->sessionKey2, ls_sessionKey2, sizeof(ls_sessionKey2) ); this->gsPort = myGameServerPort; memcpy( this->gsIP, myGameServerIP, sizeof(myGameServerIP) ); // notify radar window about disconnection this->postNotify( LCN_STATECHANGE, 0 ); // disconnected log_error( LOG_DEBUG, "ProcessClient(): ended client processing. Returning %d\n", (int)retVal ); // return indicator return retVal; } void LoginClient::setNotifyWindow( HWND notifyHwnd, UINT notifyMsg ) { this->hWndNotify = notifyHwnd; this->uMsgNotify = notifyMsg; } extern HWND g_radardll_hwnd; void LoginClient::postNotify( WPARAM wParam, LPARAM lParam ) { //if( hWndNotify == NULL || (uMsgNotify < WM_USER) ) return; //PostMessage( hWndNotify, uMsgNotify, wParam, lParam ); PostMessage( g_radardll_hwnd, (WM_USER+106), wParam, lParam ); } LoginClient *g_login_client = NULL;