diff options
| author | Max Audron <audron@cocaine.farm> | 2020-05-02 18:32:28 +0200 |
|---|---|---|
| committer | Max Audron <audron@cocaine.farm> | 2020-05-02 18:32:28 +0200 |
| commit | b69cdca85d2a2e7563b086426ab0165b45ea5eb4 (patch) | |
| tree | 944744fcae385e5f82985f368386cd4e96e3555e /src | |
| parent | rewrite of datetime parsing to use the time crate (diff) | |
add parsing of signalproxy messages
Diffstat (limited to 'src')
| -rw-r--r-- | src/bin/quassel-client.rs | 8 | ||||
| -rw-r--r-- | src/client/mod.rs | 17 | ||||
| -rw-r--r-- | src/message/mod.rs | 2 | ||||
| -rw-r--r-- | src/message/signalproxy.rs | 309 | ||||
| -rw-r--r-- | src/primitive/datetime.rs | 171 | ||||
| -rw-r--r-- | src/primitive/variant.rs | 13 | ||||
| -rw-r--r-- | src/tests/mod.rs | 1 | ||||
| -rw-r--r-- | src/util.rs | 5 |
8 files changed, 454 insertions, 72 deletions
diff --git a/src/bin/quassel-client.rs b/src/bin/quassel-client.rs index 9d25088..233d3ae 100644 --- a/src/bin/quassel-client.rs +++ b/src/bin/quassel-client.rs @@ -16,10 +16,18 @@ async fn main() -> Result<(), Error> { // true, // ).await.unwrap(); + let username = std::env::args().nth(1).expect("no username given"); + let password = std::env::args().nth(2).expect("no password given"); + + let mut client = client::Client::<tokio_tls::TlsStream<tokio::net::TcpStream>>::connect_tls( "cocaine.farm", 4242, true, + client::User { + name: username, + password: password, + } ).await.unwrap(); client.run().await; diff --git a/src/client/mod.rs b/src/client/mod.rs index b43115b..9f0d66c 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -30,6 +30,12 @@ pub struct Client<T: AsyncRead + AsyncWrite + Unpin> { pub tls: bool, pub compression: bool, pub state: ClientState, + pub user: User, +} + +pub struct User { + pub name: String, + pub password: String, } pub enum ClientState { @@ -69,7 +75,7 @@ impl <T: AsyncRead + AsyncWrite + Unpin> Client<T> { }; } - pub async fn connect(address: &'static str, port: u64, compression: bool) -> Result<Client<TcpStream>, Error> { + pub async fn connect(address: &'static str, port: u64, compression: bool, user: User) -> Result<Client<TcpStream>, Error> { let mut stream = TcpStream::connect(format!("{}:{}", address, port)).await?; info!(target: "init", "Establishing Connection"); @@ -90,10 +96,11 @@ impl <T: AsyncRead + AsyncWrite + Unpin> Client<T> { tls: false, compression, state: ClientState::Handshake, + user, }); } - pub async fn connect_tls(address: &'static str, port: u64, compression: bool) -> Result<Client<TlsStream<TcpStream>>, Error> { + pub async fn connect_tls(address: &'static str, port: u64, compression: bool, user: User) -> Result<Client<TlsStream<TcpStream>>, Error> { let mut stream: TcpStream = TcpStream::connect(format!("{}:{}", address, port)).await?; info!(target: "init", "Establishing Connection"); @@ -118,6 +125,7 @@ impl <T: AsyncRead + AsyncWrite + Unpin> Client<T> { tls: true, compression, state: ClientState::Handshake, + user, }); } @@ -137,7 +145,7 @@ pub async fn handle_login_message<T: AsyncRead + AsyncWrite + Unpin>(client: &mu "ClientInitAck" => { info!(target: "init", "Initialization successfull"); info!(target: "login", "Starting Login"); - let login = ClientLogin {user: "audron".to_string(), password: "audron".to_string()}; + let login = ClientLogin {user: client.user.name.clone(), password: client.user.password.clone()}; client.stream.send(login.serialize()?).await?; }, "ClientInitReject" => { @@ -162,12 +170,13 @@ pub async fn handle_login_message<T: AsyncRead + AsyncWrite + Unpin>(client: &mu } pub async fn handle_message<T: AsyncRead + AsyncWrite + Unpin>(client: &mut Client<T>, buf: &[u8]) -> Result<(), Error> { + use crate::message::Message; use crate::primitive::VariantList; use crate::Deserialize; use crate::Serialize; trace!(target: "message", "Received bytes: {:x?}", buf); - let (_, res) = VariantList::parse(buf)?; + let (_, res) = Message::parse(buf)?; debug!(target: "init", "Received Messsage: {:#?}", res); return Ok(()); diff --git a/src/message/mod.rs b/src/message/mod.rs index 16f2088..96cc910 100644 --- a/src/message/mod.rs +++ b/src/message/mod.rs @@ -1,3 +1,5 @@ mod handshake; +mod signalproxy; pub use handshake::*; +pub use signalproxy::*; diff --git a/src/message/signalproxy.rs b/src/message/signalproxy.rs new file mode 100644 index 0000000..22a498a --- /dev/null +++ b/src/message/signalproxy.rs @@ -0,0 +1,309 @@ +use crate::primitive::{DateTime, Variant, VariantList}; +use crate::{Deserialize, Serialize}; + +#[derive(Clone, Debug, std::cmp::PartialEq)] +pub enum Message { + /// Bidirectional + SyncMessage(SyncMessage), + /// Bidirectional + RpcCall(RpcCall), + InitRequest(InitRequest), + InitData(InitData), + /// Bidirectional + HeartBeat(HeartBeat), + /// Bidirectional + HeartBeatReply(HeartBeatReply), +} + +impl Serialize for Message { + fn serialize(&self) -> Result<Vec<std::primitive::u8>, failure::Error> { + match &self { + Message::SyncMessage(value) => value.serialize(), + Message::RpcCall(value) => value.serialize(), + Message::InitRequest(value) => value.serialize(), + Message::InitData(value) => value.serialize(), + Message::HeartBeat(value) => value.serialize(), + Message::HeartBeatReply(value) => value.serialize(), + } + } +} + +impl Deserialize for Message { + fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error> { + let (_, message_type) = i32::parse(&b[9..13])?; + + match MessageType::from(message_type) { + MessageType::SyncMessage => { + let (size, res) = SyncMessage::parse(&b)?; + + Ok((size, Message::SyncMessage(res))) + } + MessageType::RpcCall => { + let (size, res) = RpcCall::parse(&b)?; + + Ok((size, Message::RpcCall(res))) + } + MessageType::InitRequest => { + let (size, res) = InitRequest::parse(&b)?; + + Ok((size, Message::InitRequest(res))) + } + MessageType::InitData => { + let (size, res) = InitData::parse(&b)?; + + Ok((size, Message::InitData(res))) + } + MessageType::HeartBeat => { + let (size, res) = HeartBeat::parse(&b)?; + + Ok((size, Message::HeartBeat(res))) + } + MessageType::HeartBeatReply => { + let (size, res) = HeartBeatReply::parse(&b)?; + + Ok((size, Message::HeartBeatReply(res))) + } + } + } +} + +/// Type of an SignalProxy Message +/// The first element in the VariantList that is received +#[repr(i32)] +#[derive(Copy, Clone, Debug, std::cmp::PartialEq)] +pub enum MessageType { + /// Bidirectional + SyncMessage = 0x00000001, + /// Bidirectional + RpcCall = 0x00000002, + InitRequest = 0x00000003, + InitData = 0x00000004, + /// Bidirectional + HeartBeat = 0x00000005, + /// Bidirectional + HeartBeatReply = 0x00000006, +} + +impl From<i32> for MessageType { + fn from(val: i32) -> Self { + match val { + 0x00000001 => MessageType::SyncMessage, + 0x00000002 => MessageType::RpcCall, + 0x00000003 => MessageType::InitRequest, + 0x00000004 => MessageType::InitData, + 0x00000005 => MessageType::HeartBeat, + 0x00000006 => MessageType::HeartBeatReply, + _ => unimplemented!(), + } + } +} + +#[derive(Clone, Debug, std::cmp::PartialEq)] +pub struct SyncMessage { + class_name: String, + object_name: String, + slot_name: String, + params: VariantList, +} + +impl Serialize for SyncMessage { + fn serialize(&self) -> Result<Vec<std::primitive::u8>, failure::Error> { + let mut res = VariantList::new(); + + res.push(Variant::i32(MessageType::SyncMessage as i32)); + res.push(Variant::StringUTF8(self.class_name.clone())); + res.push(Variant::StringUTF8(self.object_name.clone())); + res.push(Variant::StringUTF8(self.slot_name.clone())); + + res.append(&mut self.params.clone()); + + res.serialize() + } +} + +impl Deserialize for SyncMessage { + fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error> { + let (size, mut res) = VariantList::parse(&b)?; + + res.remove(0); + + Ok(( + size, + Self { + class_name: match_variant!(res.remove(0), Variant::StringUTF8), + object_name: match_variant!(res.remove(0), Variant::StringUTF8), + slot_name: match_variant!(res.remove(0), Variant::StringUTF8), + params: res, + }, + )) + } +} + +#[derive(Clone, Debug, std::cmp::PartialEq)] +pub struct RpcCall { + slot_name: String, + params: VariantList, +} + +impl Serialize for RpcCall { + fn serialize(&self) -> Result<Vec<std::primitive::u8>, failure::Error> { + let mut res = VariantList::new(); + + res.push(Variant::i32(MessageType::RpcCall as i32)); + res.push(Variant::StringUTF8(self.slot_name.clone())); + + res.append(&mut self.params.clone()); + + res.serialize() + } +} + +impl Deserialize for RpcCall { + fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error> { + let (size, mut res) = VariantList::parse(&b)?; + + res.remove(0); + + Ok(( + size, + Self { + slot_name: match_variant!(res.remove(0), Variant::StringUTF8), + params: res, + }, + )) + } +} + +#[derive(Clone, Debug, std::cmp::PartialEq)] +pub struct InitRequest { + class_name: String, + object_name: String, +} + +impl Serialize for InitRequest { + fn serialize(&self) -> Result<Vec<std::primitive::u8>, failure::Error> { + let mut res = VariantList::new(); + + res.push(Variant::i32(MessageType::InitRequest as i32)); + res.push(Variant::StringUTF8(self.class_name.clone())); + res.push(Variant::StringUTF8(self.object_name.clone())); + + res.serialize() + } +} + +impl Deserialize for InitRequest { + fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error> { + let (size, mut res) = VariantList::parse(&b)?; + + res.remove(0); + + Ok(( + size, + Self { + class_name: match_variant!(res.remove(0), Variant::StringUTF8), + object_name: match_variant!(res.remove(0), Variant::StringUTF8), + }, + )) + } +} + +#[derive(Clone, Debug, std::cmp::PartialEq)] +pub struct InitData { + class_name: String, + object_name: String, + init_data: VariantList, +} + +impl Serialize for InitData { + fn serialize(&self) -> Result<Vec<std::primitive::u8>, failure::Error> { + let mut res = VariantList::new(); + + res.push(Variant::i32(MessageType::InitData as i32)); + res.push(Variant::StringUTF8(self.class_name.clone())); + res.push(Variant::StringUTF8(self.object_name.clone())); + + res.append(&mut self.init_data.clone()); + + res.serialize() + } +} + +impl Deserialize for InitData { + fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error> { + let (size, mut res) = VariantList::parse(&b)?; + + res.remove(0); + + Ok(( + size, + Self { + class_name: match_variant!(res.remove(0), Variant::StringUTF8), + object_name: match_variant!(res.remove(0), Variant::StringUTF8), + init_data: res, + }, + )) + } +} + +#[derive(Clone, Debug, std::cmp::PartialEq)] +pub struct HeartBeat { + timestamp: DateTime, +} + +impl Serialize for HeartBeat { + fn serialize(&self) -> Result<Vec<std::primitive::u8>, failure::Error> { + let mut res = VariantList::new(); + + res.push(Variant::i32(MessageType::HeartBeat as i32)); + res.push(Variant::DateTime(self.timestamp.clone())); + + res.serialize() + } +} + +impl Deserialize for HeartBeat { + fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error> { + let (size, mut res) = VariantList::parse(&b)?; + + res.remove(0); + + Ok(( + size, + Self { + timestamp: match_variant!(res.remove(0), Variant::DateTime), + }, + )) + } +} + +#[derive(Clone, Debug, std::cmp::PartialEq)] +pub struct HeartBeatReply { + timestamp: DateTime, +} + +impl Serialize for HeartBeatReply { + fn serialize(&self) -> Result<Vec<std::primitive::u8>, failure::Error> { + let mut res = VariantList::new(); + + res.push(Variant::i32(MessageType::HeartBeatReply as i32)); + res.push(Variant::DateTime(self.timestamp.clone())); + + res.serialize() + } +} + +impl Deserialize for HeartBeatReply { + fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error> { + let (size, mut res) = VariantList::parse(&b)?; + + res.remove(0); + + Ok(( + size, + Self { + timestamp: match_variant!(res.remove(0), Variant::DateTime), + }, + )) + } +} diff --git a/src/primitive/datetime.rs b/src/primitive/datetime.rs index e6946b9..cbcdd51 100644 --- a/src/primitive/datetime.rs +++ b/src/primitive/datetime.rs @@ -1,107 +1,160 @@ use crate::Deserialize; use crate::Serialize; -/// The DateTime struct represents a DateTime as received in IRC -/// -/// DateTime is, like all other struct based types, serialized sequentially. -#[derive(Clone, Debug, std::cmp::PartialEq)] -pub struct DateTime { - /// Day in Julian calendar, unknown if signed or unsigned - julian_day: i32, - /// Milliseconds since start of day - millis_of_day: i32, - /// Timezone of DateTime, 0x00 is local, 0x01 is UTC - zone: u8, +use time::{OffsetDateTime, PrimitiveDateTime, UtcOffset}; + +// The DateTime struct represents a DateTime as received in IRC +// +// DateTime is, like all other struct based types, serialized sequentially. +// #[derive(Clone, Debug, std::cmp::PartialEq)] +// pub struct DateTime { +// /// Day in Julian calendar, unknown if signed or unsigned +// julian_day: i32, +// /// Milliseconds since start of day +// millis_of_day: i32, +// /// Timezone of DateTime, 0x00 is local, 0x01 is UTC +// zone: u8, +// } + +pub type DateTime = OffsetDateTime; +pub use time::{Date, Time}; + +/// TimeSpec specifies whether the time is a local time, daylightsaving local time or a form of UTC Offset +#[repr(i8)] +#[derive(Copy, Clone, Debug, std::cmp::PartialEq)] +pub enum TimeSpec { + LocalUnknown = -0x01, + LocalStandard = 0x00, + LocalDST = 0x01, + UTC = 0x02, + OffsetFromUTC = 0x03, } -impl Serialize for DateTime { - fn serialize(&self) -> Result<Vec<std::primitive::u8>, failure::Error> { +impl From<i8> for TimeSpec { + fn from(val: i8) -> Self { + match val { + -0x01 => TimeSpec::LocalUnknown, + 0x00 => TimeSpec::LocalStandard, + 0x01 => TimeSpec::LocalDST, + 0x02 => TimeSpec::UTC, + 0x03 => TimeSpec::OffsetFromUTC, + _ => unimplemented!(), + } + } +} + +impl Serialize for OffsetDateTime { + fn serialize(&self) -> Result<Vec<u8>, failure::Error> { let mut values: Vec<u8> = Vec::new(); - values.append(&mut i32::serialize(&self.julian_day)?); - values.append(&mut i32::serialize(&self.millis_of_day)?); - values.append(&mut u8::serialize(&(self.zone))?); + values.extend(i32::serialize(&(self.date().julian_day() as i32))?); + + let time: i32 = { + let hour: i32 = self.time().hour() as i32; + let minute: i32 = self.time().minute() as i32; + let second: i32 = self.time().second() as i32; + let milli: i32 = self.time().millisecond() as i32; + + milli + (second * 1000) + (minute * 60000) + (hour * 60 * 60000) + }; + + values.extend(i32::serialize(&time)?); + values.extend(u8::serialize(&(TimeSpec::OffsetFromUTC as u8))?); + values.extend(i32::serialize(&self.offset().as_seconds())?); Ok(values) } } -impl Deserialize for DateTime { - fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error> - where - Self: Sized, - { +impl Deserialize for OffsetDateTime { + fn parse(b: &[u8]) -> Result<(usize, Self), failure::Error> { let (_, julian_day) = i32::parse(&b[0..4])?; let (_, millis_of_day) = i32::parse(&b[4..8])?; let (_, zone) = u8::parse(&b[8..9])?; - return Ok(( - 9, - DateTime { - julian_day, - millis_of_day, - zone, - }, - )); - } -} + let mut pos = 9; + + let zone = TimeSpec::from(zone as i8); + + let offset: UtcOffset; + match zone { + TimeSpec::LocalUnknown | TimeSpec::LocalStandard | TimeSpec::LocalDST => { + offset = UtcOffset::current_local_offset() + } + TimeSpec::UTC => offset = UtcOffset::UTC, + TimeSpec::OffsetFromUTC => { + let (_, tmp_offset) = i32::parse(&b[9..13])?; + pos += 4; + offset = UtcOffset::seconds(tmp_offset) + } + } -/// The Date struct represents a Date as received in IRC -/// -/// Date is, like all other struct based types, serialized sequentially. -#[derive(Clone, Debug, std::cmp::PartialEq)] -pub struct Date { - /// Day in Julian calendar, unknown if signed or unsigned - julian_day: i32, + let date = Date::from_julian_day(julian_day as i64); + + let hour = millis_of_day / 60 / 60000; + let minute = (millis_of_day - (hour * 60 * 60000)) / 60000; + let seconds = (millis_of_day - (hour * 60 * 60000) - (minute * 60000)) / 1000; + let millis = millis_of_day - (hour * 60 * 60000) - (minute * 60000) - (seconds * 1000); + + let time = + Time::try_from_hms_milli(hour as u8, minute as u8, seconds as u8, millis as u16)?; + let primitivedatetime = PrimitiveDateTime::new(date, time); + let datetime = primitivedatetime.assume_offset(offset); + + Ok((pos, datetime)) + } } impl Serialize for Date { fn serialize(&self) -> Result<Vec<std::primitive::u8>, failure::Error> { let mut values: Vec<u8> = Vec::new(); - values.append(&mut i32::serialize(&self.julian_day)?); + values.extend(i32::serialize(&(self.julian_day() as i32))?); Ok(values) } } impl Deserialize for Date { - fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error> - where - Self: Sized, - { + fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error> { let (_, julian_day) = i32::parse(&b[0..4])?; + let date = Date::from_julian_day(julian_day as i64); - return Ok((9, Date { julian_day })); + Ok((4, date)) } } -/// The Time struct represents a Time as received in IRC -/// -/// Time is, like all other struct based types, serialized sequentially. -#[derive(Clone, Debug, std::cmp::PartialEq)] -pub struct Time { - /// Milliseconds since start of day - millis_of_day: i32, -} - impl Serialize for Time { fn serialize(&self) -> Result<Vec<std::primitive::u8>, failure::Error> { let mut values: Vec<u8> = Vec::new(); - values.append(&mut i32::serialize(&self.millis_of_day)?); + let time: i32 = { + let hour: i32 = self.hour() as i32; + let minute: i32 = self.minute() as i32; + let second: i32 = self.second() as i32; + let milli: i32 = self.millisecond() as i32; + + milli + (second * 1000) + (minute * 60000) + (hour * 60 * 60000) + }; + + values.extend(i32::serialize(&time)?); Ok(values) } } impl Deserialize for Time { - fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error> - where - Self: Sized, - { + fn parse(b: &[std::primitive::u8]) -> Result<(std::primitive::usize, Self), failure::Error> { let (_, millis_of_day) = i32::parse(&b[0..4])?; - return Ok((4, Time { millis_of_day })); + let hour = millis_of_day / 60 / 60000; + let minute = (millis_of_day - (hour * 60 * 60000)) / 60000; + let seconds = (millis_of_day - (hour * 60 * 60000) - (minute * 60000)) / 1000; + let millis = millis_of_day - (hour * 60 * 60000) - (minute * 60000) - (seconds * 1000); + + let time = + Time::try_from_hms_milli(hour as u8, minute as u8, seconds as u8, millis as u16)?; + + Ok((4, time)) } } diff --git a/src/primitive/variant.rs b/src/primitive/variant.rs index 71ddc4a..9242d90 100644 --- a/src/primitive/variant.rs +++ b/src/primitive/variant.rs @@ -12,9 +12,7 @@ use crate::{Serialize, SerializeUTF8}; extern crate bytes; -use crate::primitive::{ - BufferInfo, Date, DateTime, Message, Time, VariantList, VariantMap, -}; +use crate::primitive::{BufferInfo, Date, DateTime, Message, Time, VariantList, VariantMap}; /// Variant represents the possible types we can receive /// @@ -204,17 +202,18 @@ impl Deserialize for Variant { } primitive::QDATETIME => { trace!(target: "primitive::Variant", "Parsing Variant: Date"); - let (vlen, value) = Date::parse(&b[len..])?; - return Ok((len + vlen, Variant::Date(value.clone()))); + // let (vlen, value) = DateTime::parse(&b[len..])?; + let (vlen, value): (usize, DateTime) = Deserialize::parse(&b[len..])?; + return Ok((len + vlen, Variant::DateTime(value.clone()))); } primitive::QDATE => { trace!(target: "primitive::Variant", "Parsing Variant: Date"); - let (vlen, value) = Date::parse(&b[len..])?; + let (vlen, value): (usize, Date) = Deserialize::parse(&b[len..])?; return Ok((len + vlen, Variant::Date(value.clone()))); } primitive::QTIME => { trace!(target: "primitive::Variant", "Parsing Variant: Time"); - let (vlen, value) = Time::parse(&b[len..])?; + let (vlen, value): (usize, Time) = Deserialize::parse(&b[len..])?; return Ok((len + vlen, Variant::Time(value.clone()))); } primitive::BOOL => { diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 16fd124..1e99bfe 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -1,4 +1,5 @@ pub mod base_types; +pub mod datetime; #[allow(unused_imports)] #[allow(unused_macros)] diff --git a/src/util.rs b/src/util.rs index 192e270..eeeda4e 100644 --- a/src/util.rs +++ b/src/util.rs @@ -3,9 +3,10 @@ /// # Example /// /// ``` +/// use libquassel::match_variant; /// use libquassel::primitive::{VariantMap, Variant}; /// -/// let var = Variant::String("test string"); +/// let var = Variant::String("test string".to_string()); /// let result = match_variant!(var, Variant::String); /// ``` #[macro_export] @@ -13,7 +14,7 @@ macro_rules! match_variant { ( $values:expr, $x:path ) => { match &$values { $x(x) => Ok(x.clone()), - _ => Err(""), + err => Err(err), } .unwrap(); }; |
