Skip to content

Commit

Permalink
Further improve LoginResponse (#13)
Browse files Browse the repository at this point in the history
* Add deserialization for LoginResponse

* Rename auth logs to stamps

* Store the rest of the deserialized fields

* Fix capture parser

* Convert spaces to tabs as requested
  • Loading branch information
enteryournamehere authored Jan 24, 2023
1 parent 1383e0c commit 556f936
Show file tree
Hide file tree
Showing 5 changed files with 309 additions and 20 deletions.
17 changes: 17 additions & 0 deletions examples/capture_parser/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use std::time::Instant;

use lu_packets::{
auth::server::Message as AuthServerMessage,
auth::client::Message as AuthClientMessage,
world::Lot,
world::server::Message as WorldServerMessage,
world::client::Message as WorldClientMessage,
Expand Down Expand Up @@ -70,6 +71,22 @@ fn parse(path: &Path, cdclient: &mut Cdclient) -> Res<usize> {
dbg!(msg);
}
packet_count += 1
} else if file.name().contains("[53-05-00-00]") {
let mut ctx = ZipContext { zip: file, comps: &mut comps, cdclient, assert_fully_read: true };
let msg: AuthClientMessage = ctx.read().expect(&format!("Zip: {}, Filename: {}, {} bytes", path.to_str().unwrap(), ctx.zip.name(), ctx.zip.size()));
file = ctx.zip;
if unsafe { PRINT_PACKETS } {
dbg!(&msg);
}
packet_count += 1;

if ctx.assert_fully_read {
// assert fully read
let mut rest = vec![];
std::io::Read::read_to_end(&mut file, &mut rest).unwrap();
assert_eq!(rest, vec![], "Zip: {}, Filename: {}, {} bytes", path.to_str().unwrap(), file.name(), file.size());
}
i += 1; continue
} else if file.name().contains("[53-04-")
&& !file.name().contains("[53-04-00-16]")
&& !file.name().contains("[e6-00]")
Expand Down
171 changes: 151 additions & 20 deletions src/auth/client/mod.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
//! Client-received auth messages.
use std::io::Result as Res;
use std::io::{Error, ErrorKind::InvalidData, Result as Res, Read};

use endio::{LEWrite, Serialize};
use endio::{LEWrite, LERead, Deserialize, Serialize};
use endio::LittleEndian as LE;
use lu_packets_derive::MessageFromVariants;
use lu_packets_derive::VariantTests;

use crate::common::{LuString33, LuVarWString, LuWString33, ServiceId};
use crate::common::{LuString3, LuString33, LuString37, LuVarWString, LuWString33, ServiceId};
use crate::general::client::{DisconnectNotify, Handshake, GeneralMessage};
use crate::world::server::Language;

/// All messages that can be received by a client from an auth server.
pub type Message = crate::raknet::client::Message<LuMessage>;

/// All LU messages that can be received by a client from an auth server.
#[derive(Debug, MessageFromVariants, PartialEq, Serialize)]
#[derive(Debug, MessageFromVariants, PartialEq, Serialize, Deserialize, VariantTests)]
#[repr(u16)]
pub enum LuMessage {
General(GeneralMessage) = ServiceId::General as u16,
Expand All @@ -38,7 +40,7 @@ impl From<DisconnectNotify> for Message {
}

/// All client-received auth messages.
#[derive(Debug, MessageFromVariants, PartialEq, Serialize)]
#[derive(Debug, MessageFromVariants, PartialEq, Serialize, Deserialize)]
#[post_disc_padding=1]
#[repr(u32)]
pub enum ClientMessage {
Expand Down Expand Up @@ -70,56 +72,185 @@ pub enum ClientMessage {
pub enum LoginResponse {
/// The login was successful.
Ok {
// Strings used for event gating.
events: (LuString33, LuString33, LuString33, LuString33, LuString33, LuString33, LuString33, LuString33),
/// Used for version gating.
version: (u16, u16, u16),
/// The session key to be used for authenticating with world servers (to be passed in [`ClientValidation::session_key`](crate::world::server::ClientValidation::session_key)).
session_key: LuWString33,
/// The address of a world server available for further service.
redirect_address: (LuString33, u16),
/// The address of the chat server (unused).
chat_server_address: (LuString33, u16),
cdn_key: LuString33,
cdn_ticket: LuString37,
/// Language of the server.
language: Language,
/// Used for the cdclient SubscriptionPricing table.
country_code: LuString3,
/// Whether the account is connecting as a paid account for the first time.
just_upgraded_from_ftp: bool,
/// Whether the account is in free trial mode.
is_ftp: bool,
/// Time remaining in seconds for free-to-play (unused).
time_remaining_in_ftp: u64,
/// Logs from the auth server.
stamps: Vec<Stamp>,
} = 1,
/// The login failed in an unusual way. More information can be found in the attached message.
CustomMessage(LuVarWString<u16>) = 5,
/// Username or password was incorrect.
InvalidUsernamePassword = 6,
}

#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct Stamp {
pub type_: u32,
pub value: u32,
pub timestamp: u64,
}

impl<'a, W: LEWrite> Serialize<LE, W> for &'a LoginResponse
where u8: Serialize<LE, W>,
u16: Serialize<LE, W>,
u32: Serialize<LE, W>,
&'a bool: Serialize<LE, W>,
&'a [u8]: Serialize<LE, W>,
&'a LuString33: Serialize<LE, W>,
&'a LuWString33: Serialize<LE, W>,
&'a LuVarWString<u16>: Serialize<LE, W> {
fn serialize(self, writer: &mut W) -> Res<()> {
where
u8: Serialize<LE, W>,
u16: Serialize<LE, W>,
u32: Serialize<LE, W>,
u64: Serialize<LE, W>,
&'a bool: Serialize<LE, W>,
&'a [u8]: Serialize<LE, W>,
&'a LuString33: Serialize<LE, W>,
&'a LuString33: Serialize<LE, W>,
&'a LuWString33: Serialize<LE, W>,
&'a LuString37: Serialize<LE, W>,
&'a LuString3: Serialize<LE, W>,
&'a Language: Serialize<LE, W>,
&'a Stamp: Serialize<LE, W>,
&'a LuVarWString<u16>: Serialize<LE, W>,
{
fn serialize(self, writer: &mut W) -> Res<()> {
let disc = unsafe { *(self as *const LoginResponse as *const u8) };
writer.write(disc)?;
match self {
LoginResponse::Ok { version, session_key, redirect_address, is_ftp } => {
writer.write(&[0; 264][..])?;
LoginResponse::Ok {
events,
version,
session_key,
redirect_address,
chat_server_address,
cdn_key,
cdn_ticket,
language,
country_code,
just_upgraded_from_ftp,
is_ftp,
time_remaining_in_ftp,
stamps,
} => {
writer.write(&events.0)?;
writer.write(&events.1)?;
writer.write(&events.2)?;
writer.write(&events.3)?;
writer.write(&events.4)?;
writer.write(&events.5)?;
writer.write(&events.6)?;
writer.write(&events.7)?;
writer.write(version.0)?;
writer.write(version.1)?;
writer.write(version.2)?;
writer.write(session_key)?;
writer.write(&redirect_address.0)?;
writer.write(&[0; 33][..])?;
writer.write(&chat_server_address.0)?;
writer.write(redirect_address.1)?;
writer.write(&[0; 81][..])?;
writer.write(chat_server_address.1)?;
writer.write(cdn_key)?;
writer.write(cdn_ticket)?;
writer.write(language)?;
writer.write(country_code)?;
writer.write(just_upgraded_from_ftp)?;
writer.write(is_ftp)?;
writer.write(&[0; 10][..])?;
writer.write(*time_remaining_in_ftp)?;
// custom message
writer.write(0u16)?;
writer.write((stamps.len() * 16) as u32 + 4)?;
for stamp in stamps {
writer.write(stamp)?;
}
}
LoginResponse::CustomMessage(msg) => {
writer.write(&[0; 493][..])?;
writer.write(msg)?;
writer.write(4u32)?;
}
LoginResponse::InvalidUsernamePassword => {
writer.write(&[0; 495][..])?;
writer.write(4u32)?;
}
}
writer.write(4u32)?;
Ok(())
}
}

impl<R: Read + LERead> Deserialize<LE, R> for LoginResponse {
fn deserialize(reader: &mut R) -> Res<Self> {
let disc = LERead::read::<u8>(reader)?;
match disc {
1 => {
let events: (LuString33, LuString33, LuString33, LuString33, LuString33, LuString33, LuString33, LuString33)
= (LERead::read(reader)?, LERead::read(reader)?, LERead::read(reader)?, LERead::read(reader)?,
LERead::read(reader)?, LERead::read(reader)?, LERead::read(reader)?, LERead::read(reader)?);
let version: (u16, u16, u16) = (
LERead::read(reader)?,
LERead::read(reader)?,
LERead::read(reader)?,
);
let session_key: LuWString33 = LERead::read(reader)?;
let redirect_address: LuString33 = LERead::read(reader)?;
let chat_address: LuString33 = LERead::read(reader)?;
let redirect_port: u16 = LERead::read(reader)?;
let chat_port: u16 = LERead::read(reader)?;
let cdn_key: LuString33 = LERead::read(reader)?;
let cdn_ticket: LuString37 = LERead::read(reader)?;
let language: Language = LERead::read(reader)?;
let country_code: LuString3 = LERead::read(reader)?;
let just_upgraded_from_ftp: bool = LERead::read(reader)?;
let is_ftp: bool = LERead::read(reader)?;
let time_remaining_in_ftp: u64 = LERead::read(reader)?;
let _custom_message: LuVarWString<u16> = LERead::read(reader)?;
let buffer_len_plus_four: u32 = LERead::read(reader)?;
let mut stamps: Vec<Stamp> = Vec::new();
let stamp_count = (buffer_len_plus_four - 4) / 16;
for _i in 0..stamp_count {
let stamp: Stamp = LERead::read(reader)?;
stamps.push(stamp);
}
Ok(Self::Ok {
events,
version,
session_key,
redirect_address: (redirect_address, redirect_port),
chat_server_address: (chat_address, chat_port),
cdn_key,
cdn_ticket,
language,
country_code,
just_upgraded_from_ftp,
is_ftp,
time_remaining_in_ftp,
stamps,
})
}
5 => {
let mut padding = [0; 493];
Read::read_exact(reader, &mut padding)?;
let msg = LERead::read::<LuVarWString<u16>>(reader)?;
Ok(Self::CustomMessage(msg))
}
6 => {
let mut padding = [0; 495];
Read::read_exact(reader, &mut padding)?;
Ok(Self::InvalidUsernamePassword)
}
_ => Err(Error::new(InvalidData, "invalid login response type")),
}
}
}
Binary file added src/auth/client/tests/Client.bin
Binary file not shown.
Loading

0 comments on commit 556f936

Please sign in to comment.