diff options
Diffstat (limited to 'src/message')
| -rw-r--r-- | src/message/handshake.rs | 435 | ||||
| -rw-r--r-- | src/message/handshake/types.rs | 56 | ||||
| -rw-r--r-- | src/message/login.rs | 1 | ||||
| -rw-r--r-- | src/message/mod.rs | 5 |
4 files changed, 497 insertions, 0 deletions
diff --git a/src/message/handshake.rs b/src/message/handshake.rs new file mode 100644 index 0000000..753488e --- /dev/null +++ b/src/message/handshake.rs @@ -0,0 +1,435 @@ +use failure::Error; +use std::result::Result; + +use crate::error::ProtocolError; +use crate::primitive::{StringList, Variant, VariantList}; +mod types; +use crate::primitive::VariantMap; +use crate::{HandshakeDeserialize, HandshakeSerialize}; + +use crate::match_variant; + +/// Data received right after initializing the connection +/// +/// ConnAck is serialized sequentially +#[derive(Debug)] +pub struct ConnAck { + /// The Flag 0x01 for TLS + /// and 0x02 for Deflate Compression + flags: u8, + /// Some extra protocol version specific data + /// So far unused + extra: i16, + /// The version of the protocol + /// 0x00000001 for the legacy protocol + /// 0x00000002 for the datastream protocol + /// + /// Only the datastream protocol is supported by this crate + version: i8, +} + +impl Default for ConnAck { + fn default() -> Self { + Self { + flags: 0x00, + extra: 0x00, + version: 0x00000002, + } + } +} + +impl crate::Serialize for ConnAck { + fn serialize(&self) -> Result<Vec<std::primitive::u8>, Error> { + let mut bytes: Vec<u8> = Vec::new(); + + bytes.append(&mut self.flags.serialize()?); + bytes.append(&mut self.extra.serialize()?); + bytes.append(&mut self.version.serialize()?); + + Ok(bytes) + } +} + +impl crate::Deserialize for ConnAck { + fn parse(b: &[u8]) -> Result<(usize, Self), Error> { + let (flen, flags) = u8::parse(b)?; + let (elen, extra) = i16::parse(&b[flen..])?; + let (vlen, version) = i8::parse(&b[(flen + elen)..])?; + + return Ok(( + flen + elen + vlen, + Self { + flags, + extra, + version, + }, + )); + } +} + +/// ClientInit is the Initial message send to the core after establishing a base layer comunication. +/// +/// Features +/// +/// | Flag | Name | Description | +/// | ---- | ---- | ----------- | +/// | 0x00000001 | SynchronizedMarkerLine | -- | +/// | 0x00000002 | SaslAuthentication | -- | +/// | 0x00000004 | SaslExternal | -- | +/// | 0x00000008 | HideInactiveNetworks | -- | +/// | 0x00000010 | PasswordChange | -- | +/// | 0x00000020 | CapNegotiation | IRCv3 capability negotiation, account tracking | +/// | 0x00000040 | VerifyServerSSL | IRC server SSL validation | +/// | 0x00000080 | CustomRateLimits | IRC server custom message rate limits | +/// | 0x00000100 | DccFileTransfer | Currently not supported | +/// | 0x00000200 | AwayFormatTimestamp | Timestamp formatting in away (e.g. %%hh:mm%%) | +/// | 0x00000400 | Authenticators | Support for exchangeable auth backends | +/// | 0x00000800 | BufferActivitySync | Sync buffer activity status | +/// | 0x00001000 | CoreSideHighlights | Core-Side highlight configuration and matching | +/// | 0x00002000 | SenderPrefixes | Show prefixes for senders in backlog | +/// | 0x00004000 | RemoteDisconnect | Supports RPC call disconnectFromCore to remotely disconnect a client | +/// | 0x00008000 | ExtendedFeatures | Transmit features as list of strings | +/// | -- | LongTime | Serialize message time as 64-bit | +/// | -- | RichMessages | Real Name and Avatar URL in backlog | +/// | -- | BacklogFilterType | Backlogmanager supports filtering backlog by messagetype | +/// | -- | EcdsaCertfpKeys | ECDSA keys for CertFP in identities | +/// | -- | LongMessageId | 64-bit IDs for messages | +/// | -- | SyncedCoreInfo | CoreInfo dynamically updated using signals | +#[derive(Debug)] +pub struct ClientInit { + /// Version of the client + pub client_version: String, + /// Build date of the client + pub client_date: String, + /// supported features as bitflags + pub client_features: u32, + /// List of supported extended features + pub feature_list: StringList, +} + +impl HandshakeSerialize for ClientInit { + fn serialize(&self) -> Result<Vec<u8>, Error> { + let mut values: VariantMap = VariantMap::with_capacity(5); + values.insert( + "MsgType".to_string(), + Variant::String("ClientInit".to_string()), + ); + values.insert( + "ClientVersion".to_string(), + Variant::String(self.client_version.clone()), + ); + values.insert( + "ClientDate".to_string(), + Variant::String(self.client_date.clone()), + ); + values.insert("Features".to_string(), Variant::u32(self.client_features)); + values.insert( + "FeatureList".to_string(), + Variant::StringList(self.feature_list.clone()), + ); + return HandshakeSerialize::serialize(&values); + } +} + +impl HandshakeDeserialize for ClientInit { + fn parse(b: &[u8]) -> Result<(usize, Self), Error> { + let (len, values): (usize, VariantMap) = HandshakeDeserialize::parse(b)?; + + let msgtype = match_variant!(&values["MsgType"], Variant::StringUTF8); + + if msgtype == "ClientInit" { + return Ok(( + len, + Self { + client_version: match_variant!(values["ClientVersion"], Variant::String), + client_date: match_variant!(values["ClientDate"], Variant::String), + feature_list: match_variant!(values["FeatureList"], Variant::StringList), + client_features: match_variant!(values["Features"], Variant::u32), + }, + )); + } else { + bail!(ProtocolError::WrongMsgType); + } + } +} + +/// ClientInitReject is received when the initialization fails +#[derive(Debug)] +pub struct ClientInitReject { + /// String with an error message of what went wrong + pub error_string: String, +} + +impl HandshakeSerialize for ClientInitReject { + fn serialize(&self) -> Result<Vec<u8>, Error> { + let mut values: VariantMap = VariantMap::with_capacity(2); + values.insert( + "MsgType".to_string(), + Variant::String("ClientInitReject".to_string()), + ); + values.insert( + "ErrorString".to_string(), + Variant::String(self.error_string.clone()), + ); + return HandshakeSerialize::serialize(&values); + } +} + +impl HandshakeDeserialize for ClientInitReject { + fn parse(b: &[u8]) -> Result<(usize, Self), Error> { + let (len, values): (usize, VariantMap) = HandshakeDeserialize::parse(b)?; + + let msgtype = match_variant!(&values["MsgType"], Variant::StringUTF8); + + if msgtype == "ClientInitReject" { + return Ok(( + len, + Self { + error_string: match_variant!(values["ErrorString"], Variant::String), + }, + )); + } else { + bail!(ProtocolError::WrongMsgType); + } + } +} + +/// ClientInitAck is received when the initialization was successfull +#[derive(Debug)] +pub struct ClientInitAck { + /// Flags of supported legacy features + pub core_features: u32, + /// If the core has already been configured + pub core_configured: bool, + /// List of VariantMaps of info on available backends + pub storage_backends: VariantList, + /// List of VariantMaps of info on available authenticators + pub authenticators: VariantList, + /// List of supported extended features + pub feature_list: StringList, +} + +impl HandshakeSerialize for ClientInitAck { + fn serialize(&self) -> Result<Vec<u8>, Error> { + let mut values: VariantMap = VariantMap::with_capacity(6); + values.insert( + "MsgType".to_string(), + Variant::String("ClientInitAck".to_string()), + ); + values.insert("CoreFeatures".to_string(), Variant::u32(self.core_features)); + values.insert( + "Configured".to_string(), + Variant::bool(self.core_configured), + ); + values.insert( + "StorageBackends".to_string(), + Variant::VariantList(self.storage_backends.clone()), + ); + values.insert( + "Authenticators".to_string(), + Variant::VariantList(self.authenticators.clone()), + ); + values.insert( + "FeatureList".to_string(), + Variant::StringList(self.feature_list.clone()), + ); + return HandshakeSerialize::serialize(&values); + } +} + +impl HandshakeDeserialize for ClientInitAck { + fn parse(b: &[u8]) -> Result<(usize, Self), Error> { + let (len, values): (usize, VariantMap) = HandshakeDeserialize::parse(b)?; + + let msgtype = match_variant!(&values["MsgType"], Variant::StringUTF8); + + if msgtype == "ClientInitAck" { + return Ok(( + len, + Self { + core_features: 0x00008000, + core_configured: match_variant!(values["Configured"], Variant::bool), + storage_backends: match_variant!( + values["StorageBackends"], + Variant::VariantList + ), + authenticators: match_variant!(values["Authenticators"], Variant::VariantList), + feature_list: match_variant!(values["FeatureList"], Variant::StringList), + }, + )); + } else { + bail!(ProtocolError::WrongMsgType); + } + } +} + +/// Login to the core with user data +/// username and password are transmitted in plain text +#[derive(Debug)] +pub struct ClientLogin { + pub user: String, + pub password: String, +} + +impl HandshakeSerialize for ClientLogin { + fn serialize(&self) -> Result<Vec<u8>, Error> { + let mut values: VariantMap = VariantMap::new(); + values.insert( + "MsgType".to_string(), + Variant::String("ClientLogin".to_string()), + ); + values.insert("User".to_string(), Variant::String(self.user.clone())); + values.insert( + "Password".to_string(), + Variant::String(self.password.clone()), + ); + return HandshakeSerialize::serialize(&values); + } +} + +impl HandshakeDeserialize for ClientLogin { + fn parse(b: &[u8]) -> Result<(usize, Self), Error> { + let (len, values): (usize, VariantMap) = HandshakeDeserialize::parse(b)?; + + let msgtype = match_variant!(&values["MsgType"], Variant::StringUTF8); + + if msgtype == "ClientLogin" { + return Ok(( + len, + Self { + user: match_variant!(values["User"], Variant::String), + password: match_variant!(values["Password"], Variant::String), + }, + )); + } else { + bail!(ProtocolError::WrongMsgType); + } + } +} + +/// ClientLoginAck is received after the client has successfully logged in +/// it has no fields +#[derive(Debug)] +pub struct ClientLoginAck; + +impl HandshakeSerialize for ClientLoginAck { + fn serialize(&self) -> Result<Vec<u8>, Error> { + let mut values: VariantMap = VariantMap::with_capacity(1); + values.insert( + "MsgType".to_string(), + Variant::String("ClientLoginAck".to_string()), + ); + return HandshakeSerialize::serialize(&values); + } +} + +impl HandshakeDeserialize for ClientLoginAck { + fn parse(b: &[u8]) -> Result<(usize, Self), Error> { + let (len, values): (usize, VariantMap) = HandshakeDeserialize::parse(b)?; + + let msgtype = match_variant!(&values["MsgType"], Variant::StringUTF8); + + if msgtype == "ClientLogin" { + return Ok((len, Self {})); + } else { + bail!(ProtocolError::WrongMsgType); + } + } +} + +/// ClientLoginReject is received after the client failed to login +/// It contains an error message as String +#[derive(Debug)] +pub struct ClientLoginReject { + error: String, +} + +impl HandshakeSerialize for ClientLoginReject { + fn serialize(&self) -> Result<Vec<u8>, Error> { + let mut values: VariantMap = VariantMap::with_capacity(1); + values.insert( + "MsgType".to_string(), + Variant::String("ClientLoginReject".to_string()), + ); + values.insert( + "ErrorString".to_string(), + Variant::String(self.error.clone()), + ); + return HandshakeSerialize::serialize(&values); + } +} + +impl HandshakeDeserialize for ClientLoginReject { + fn parse(b: &[u8]) -> Result<(usize, Self), Error> { + let (len, values): (usize, VariantMap) = HandshakeDeserialize::parse(b)?; + + let msgtype = match_variant!(&values["MsgType"], Variant::StringUTF8); + + if msgtype == "ClientLogin" { + return Ok(( + len, + Self { + error: match_variant!(values["ErrorString"], Variant::String), + }, + )); + } else { + bail!(ProtocolError::WrongMsgType); + } + } +} + +/// SessionInit is received along with ClientLoginAck to initialize that user Session +// TODO Replace with proper types +#[derive(Debug)] +pub struct SessionInit { + /// List of all configured identities + identities: VariantList, + /// List of all existing buffers + buffers: VariantList, + /// Ids of all networks + network_ids: VariantList, +} + +impl HandshakeSerialize for SessionInit { + fn serialize(&self) -> Result<Vec<u8>, Error> { + let mut values: VariantMap = VariantMap::with_capacity(1); + values.insert( + "MsgType".to_string(), + Variant::String("SessionInit".to_string()), + ); + values.insert( + "Identities".to_string(), + Variant::VariantList(self.identities.clone()), + ); + values.insert( + "BufferInfos".to_string(), + Variant::VariantList(self.buffers.clone()), + ); + values.insert( + "NetworkIds".to_string(), + Variant::VariantList(self.network_ids.clone()), + ); + return HandshakeSerialize::serialize(&values); + } +} + +impl HandshakeDeserialize for SessionInit { + fn parse(b: &[u8]) -> Result<(usize, Self), Error> { + let (len, values): (usize, VariantMap) = HandshakeDeserialize::parse(b)?; + + let msgtype = match_variant!(&values["MsgType"], Variant::StringUTF8); + + if msgtype == "ClientLogin" { + return Ok(( + len, + Self { + identities: match_variant!(values["Identities"], Variant::VariantList), + buffers: match_variant!(values["BufferInfos"], Variant::VariantList), + network_ids: match_variant!(values["NetworkIds"], Variant::VariantList), + }, + )); + } else { + bail!(ProtocolError::WrongMsgType); + } + } +} diff --git a/src/message/handshake/types.rs b/src/message/handshake/types.rs new file mode 100644 index 0000000..04e1dd0 --- /dev/null +++ b/src/message/handshake/types.rs @@ -0,0 +1,56 @@ +use std::convert::TryInto; +use std::result::Result; +use std::vec::Vec; + +use failure::Error; + +use crate::error::ProtocolError; +use crate::primitive::Variant; +use crate::Deserialize; +use crate::Serialize; +use crate::util; + +use crate::primitive::VariantMap; +use crate::{HandshakeDeserialize, HandshakeSerialize}; + +impl HandshakeSerialize for VariantMap { + fn serialize<'a>(&'a self) -> Result<Vec<u8>, Error> { + let mut res: Vec<u8> = Vec::new(); + + for (k, v) in self { + let key = Variant::String(k.clone()); + res.extend(key.serialize()?); + res.extend(v.serialize()?); + } + + let len: i32 = (self.len() * 2).try_into().unwrap(); + util::insert_bytes(0, &mut res, &mut (len).to_be_bytes()); + + return Ok(res); + } +} + +impl HandshakeDeserialize for VariantMap { + fn parse(b: &[u8]) -> Result<(usize, Self), Error> { + let (_, len) = i32::parse(&b[0..4])?; + + let mut pos: usize = 4; + let mut map = VariantMap::new(); + + for _ in 0..(len / 2) { + let (nlen, name) = Variant::parse(&b[pos..])?; + pos += nlen; + + let (vlen, value) = Variant::parse(&b[pos..])?; + pos += vlen; + + match name { + Variant::String(x) => map.insert(x, value), + Variant::StringUTF8(x) => map.insert(x, value), + _ => bail!(ProtocolError::WrongVariant), + }; + } + + return Ok((pos, map)); + } +} diff --git a/src/message/login.rs b/src/message/login.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/message/login.rs @@ -0,0 +1 @@ + diff --git a/src/message/mod.rs b/src/message/mod.rs new file mode 100644 index 0000000..b390e2f --- /dev/null +++ b/src/message/mod.rs @@ -0,0 +1,5 @@ +mod handshake; +mod login; + +pub use handshake::*; +pub use login::*; |
