bool PGameServer::HandleAuthenticate( PClient *Client, PGameState *State, const u8 *Packet, int PacketSize ) { ConnectionTCP *Socket = Client->getTCPConn(); if ( PacketSize > 20 && *( u16* )&Packet[3] == 0x8084 ) { // authentication method #1 const u8 *Key = &Packet[5]; // password key u16 ULen = *( u16* ) & Packet[16]; // username length u16 PLen = *( u16* ) & Packet[18]; // password length char *UserName = ( char* ) & Packet[20]; // account name const u8 *PW = &Packet[20+ULen]; // encoded password // Safety controls if ( 15 + ULen + PLen > PacketSize ) { Console->Print( RED, BLACK, "[Notice] Gameserver protocol error (GS_AUTHENTICATE): Exessive internal size fields" ); return false; } if ( strnlen( UserName, ULen ) == ULen ) { Console->Print( RED, BLACK, "[Notice] Gameserver protocol error (GS_AUTHENTICATE): non-terminated username field" ); return false; } bool Failed = false; PAccount Account( UserName ); if ( Account.GetID() == 0 ) { Console->Print( YELLOW, BLACK, "[Info] Gameserver: Unknown user %s", UserName ); Failed = true; } else if ( !Account.Authenticate( PW, PLen, Key ) ) { Console->Print( YELLOW, BLACK, "[Info] Gameserver: User '%s': authentication failed", UserName ); Failed = true; } if ( !Failed ) { Console->Print( GREEN, BLACK, "[Info] Gameserver: User '%s' authentication successful", UserName ); if ( Account.GetLevel() == PAL_BANNED ) { Console->Print( YELLOW, BLACK, "[Info] User %s is banned, connection refused", UserName ); // TODO: ban ip for an adjustable time span? Failed = true; // player is banned } if ( Account.GetLevel() == PAL_UNREGPLAYER || Account.GetLevel() == PAL_REGPLAYER ) { if ( Server->GetNumClients() > Server->GetMaxClients() ) { Console->Print( YELLOW, BLACK, "[Info] Server full, refusing connection from user '%s'", UserName ); Failed = true; // server full } } } if ( Failed ) { // TODO: is this packet correct here? u8 AUTHFAILED[15] = {0xfe, 0x0c, 0x00, 0x83, 0x86, 0x05, 0x00, 0x06, 0x00, 'E', 'R', 'R', 'O', 'R', 0 }; // TODO: send actual reason instead of ERROR Socket->write( AUTHFAILED, 15 ); FinalizeClientDelayed( Client, State ); State->TCP.mState = PGameState::TCP::GS_UNKNOWN; Console->Print( YELLOW, BLACK, "[Info] Gameserver: User '%s' login refused", UserName ); } else { Client->LoggedIn( &Account ); u8 AUTHOK[28] = {0xfe, 0x19, 0x00, 0x83, 0x81, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; *( u32* )&AUTHOK[5] = Account.GetID(); Socket->write( AUTHOK, 28 ); State->TCP.mState = PGameState::TCP::GS_GAMEDATA; Console->Print( GREEN, BLACK, "[Info] Gameserver: User '%s' logged in", UserName ); } } else if ( PacketSize > 29 && *( u16* )&Packet[3] == 0x0183 ) { // authentication method #2, sent when game starts const u8 *Key = &Packet[13]; // password key u16 PLen = *( u16* ) & Packet[25]; // password length u16 ULen = *( u16* ) & Packet[27]; // username length char *UserName = ( char* ) & Packet[29]; // account name const u8 *PW = &Packet[29+ULen]; // encoded password // Safety controls if ( 24 + ULen + PLen > PacketSize ) { Console->Print( RED, BLACK, "[Notice] Gameserver protocol error (GS_AUTHENTICATE): Exessive internal size fields" ); return false; } if ( strnlen( UserName, ULen ) == ULen ) { Console->Print( RED, BLACK, "[Notice] Gameserver protocol error (GS_AUTHENTICATE): non-terminated username field" ); return false; } bool Failed = false; PAccount Account( UserName ); if ( Account.GetID() == 0 ) { Console->Print( YELLOW, BLACK, "[Info] Gameserver: Unknown user %s", UserName ); Failed = true; } else if ( !Account.Authenticate( PW, PLen, Key ) ) { Console->Print( YELLOW, BLACK, "[Info] Gameserver: User '%s': authentication failed", UserName ); Failed = true; } if ( !Failed ) { if ( Account.GetLevel() == PAL_BANNED ) { Console->Print( YELLOW, BLACK, "[Info] User %s is banned, connection refused", UserName ); // TODO: ban ip for an adjustable time span? Failed = true; // player is banned } if ( Account.GetLevel() == PAL_UNREGPLAYER || Account.GetLevel() == PAL_REGPLAYER ) { if ( Server->GetNumClients() > ( Server->GetMaxClients() - Server->GetGMSlots() ) ) { Console->Print( YELLOW, BLACK, "[Info] Server full, refusing connection from regular user '%s'", UserName ); Failed = true; // server full for non-GM users } } else if ( Config->GetOptionInt( "require_validation" ) == 1 && Account.GetLevel() == PAL_UNREGPLAYER ) { Console->Print( YELLOW, BLACK, "[Info] Rejecting connection from regular user '%s', account not activated yet", UserName ); Failed = true; } else if ( Config->GetOptionInt( "minlevel" ) > Account.GetLevel() ) { Console->Print( YELLOW, BLACK, "[Info] Rejecting connection from regular user '%s', insufficient level %d vs %d required", UserName, Account.GetLevel(), Config->GetOptionInt( "minlevel" ) ); Failed = true; } else if ( Server->GetNumClients() > Server->GetMaxClients() ) { Console->Print( YELLOW, BLACK, "[Info] Server full, refusing connection from privileged user '%s'", UserName ); Failed = true; // server full even for GM users } } if ( !Failed ) { int value = *( u32* ) & Packet[21];//i think here we must read u32 instead of u8 u32 CharID = Account.GetCharIdBySlot( value ); if ( Chars->LoadChar( CharID ) ) { Client->SetCharID( CharID ); } else { Failed = true; } } if ( Failed ) // something strange happened FinalizeClientDelayed( Client, State ); else { Client->LoggedIn( &Account ); /*u8 AUTHOK[28]={0xfe, 0x19, 0x00, 0x83, 0x81, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; *(u32*)&AUTHOK[5]=Account->GetID(); Socket->Write(AUTHOK, 28);*/ //Client->SetRemoteUDPAddr(*(u32*)&Packet[5], *(u16*)&Packet[9]); State->TCP.mState = PGameState::TCP::GS_GETSTATUS; Console->Print( "Gameserver: User '%s' entered game (%08x:%04x)", UserName, *( u32* )&Packet[5], *( u16* )&Packet[9] ); Client->SetRemoteUDPPort( *( int* )&Packet[9] ); } } else { Console->Print( RED, BLACK, "[Notice] Gameserver protocol error (GS_AUTHENTICATE): invalid packet [%04x]", *( u16* )&Packet[3] ); return ( false ); } return ( true ); }
bool PPatchServer::HandleFileRequests(PClient *Client, PPatchState *State, const u8 *Packet, int PacketSize) { static u8 STARTPATCH[13]={0xfe, 0x0a, 0x00, 0x38, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; static u8 STARTFILE[13]={0xfe, 0x0a, 0x00, 0x3b, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; static u8 FILEERROR[9]={0xfe, 0x06, 0x00, 0x3d, 0x02, 0x00, 0x00, 0x00, 0x00}; ConnectionTCP *Socket = Client->getTCPConn(); // request patch if(PacketSize==13 && *(u16*)&Packet[3]==0x007c) { int nmax = Config->GetOptionInt("max_file_xfers"); if(mNumFileTransfers>=nmax) { Console->Print("Patchserver: max file xfers exceed, killing client %i", Client->GetIndex()); return false; } if(State->mPatchFile) { std::fclose(State->mPatchFile); State->mPatchFile=0; --mNumFileTransfers; } if(State->mSendFile) { Filesystem->Close(State->mSendFile); State->mSendFile=0; --mNumFileTransfers; } State->mSerial = *(u16*)&Packet[7]; State->mCurrentPatch = *(u32*)&Packet[9]; Console->Print("Patchserver: Patch request from client %i (v%i)", Client->GetIndex(), State->mCurrentPatch); if((bool)(State->mPatchSize = StartPatch(State))) { Console->Print("Patchserver: Patch is available, %d bytes", State->mPatchSize); *(u16*)&STARTPATCH[7]=State->mSerial; *(u32*)&STARTPATCH[9]=State->mPatchSize; Socket->write(STARTPATCH, 13); State->mState = PPatchState::PS_SENDPATCH; } else { Console->Print("Patchserver: Patch not available"); *(u16*)&FILEERROR[7]=State->mSerial; Socket->write(FILEERROR, 9); FinalizeClientDelayed(Client, State); State->mState=PPatchState::PS_UNKNOWN; return true; } } else // request file if(PacketSize > 9 && *(u16*)&Packet[3]==0x004d) { int nmax = Config->GetOptionInt("max_file_xfers"); if(mNumFileTransfers>=nmax) { Console->Print("Patchserver: max file xfers exceed, killing client %i", Client->GetIndex()); return false; } if(State->mPatchFile) { std::fclose(State->mPatchFile); State->mPatchFile=0; --mNumFileTransfers; } if(State->mSendFile) { Filesystem->Close(State->mSendFile); State->mSendFile=0; --mNumFileTransfers; } // request file State->mSerial = *(u16*)&Packet[7]; char fn[256]; strncpy(fn, (const char*)&Packet[10], Packet[9]); fn[Packet[9]]=0; State->mCurrentFile = fn; Console->Print("Patchserver: File request from client %i (%s)", Client->GetIndex(), fn); if((bool)(State->mFileSize = StartFile(State))) { Console->Print("Patchserver: File %s is available, %d bytes", State->mCurrentFile.c_str(), State->mFileSize); *(u16*)&STARTFILE[7]=State->mSerial; *(u32*)&STARTFILE[9]=State->mFileSize; Socket->write(STARTFILE, 13); State->mState = PPatchState::PS_SENDFILE; } else { Console->Print("Patchserver: Requested file %s not available", State->mCurrentFile.c_str()); *(u16*)&FILEERROR[7]=State->mSerial; Socket->write(FILEERROR, 9); FinalizeClientDelayed(Client, State); State->mState=PPatchState::PS_UNKNOWN; return true; } } else // send patch data if(PacketSize==17 && *(u16*)&Packet[3]==0x007d) { State->mSerial = *(u16*)&Packet[7]; State->mCurrentPatch = *(u32*)&Packet[9]; State->mPatchOffset = *(u32*)&Packet[13]; if(!SendPatchData(Client, State)) { Console->Print("Patchserver: SendPatchData failed on client %i", Client->GetIndex()); Console->Print("Patchserver: (probably due to garbage packets)"); // state is undefined now, kill this client return false; } } else // send file data if(PacketSize > 13 && *(u16*)&Packet[3]==0x00037) { State->mSerial = *(u16*)&Packet[7]; State->mFileOffset = *(u32*)&Packet[9]; if(!SendFileData(Client, State)) { Console->Print("Patchserver: SendFileData failed on client %i", Client->GetIndex()); Console->Print("Patchserver: (probably due to garbage packets)"); // state is undefined now, kill this client return false; } } else { Console->Print("Patchserver protocol error (PS_GETPATCHORFILE): unknown packet"); return false; } return true; }