예제 #1
0
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 );
}
예제 #2
0
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;
}