diff options
| author | Max Audron <audron@cocaine.farm> | 2020-09-26 12:01:27 +0200 |
|---|---|---|
| committer | Max Audron <audron@cocaine.farm> | 2020-09-26 12:03:01 +0200 |
| commit | 3bdae21716d10032f70a0a889070766bbbe4d141 (patch) | |
| tree | 78a1ddf98601fbbc03a9375626dfa7923ab85521 /src/message | |
| parent | add parsing of signalproxy messages (diff) | |
split handshake.rs
Diffstat (limited to '')
| -rw-r--r-- | src/message/handshake.rs | 435 | ||||
| -rw-r--r-- | src/message/handshake/clientinit.rs | 91 | ||||
| -rw-r--r-- | src/message/handshake/clientinitack.rs | 74 | ||||
| -rw-r--r-- | src/message/handshake/clientinitreject.rs | 46 | ||||
| -rw-r--r-- | src/message/handshake/clientlogin.rs | 49 | ||||
| -rw-r--r-- | src/message/handshake/clientloginack.rs | 35 | ||||
| -rw-r--r-- | src/message/handshake/clientloginreject.rs | 46 | ||||
| -rw-r--r-- | src/message/handshake/connack.rs | 59 | ||||
| -rw-r--r-- | src/message/handshake/init.rs | 61 | ||||
| -rw-r--r-- | src/message/handshake/mod.rs | 23 | ||||
| -rw-r--r-- | src/message/handshake/protocol.rs | 36 | ||||
| -rw-r--r-- | src/message/handshake/sessioninit.rs | 61 |
12 files changed, 581 insertions, 435 deletions
diff --git a/src/message/handshake.rs b/src/message/handshake.rs deleted file mode 100644 index 753488e..0000000 --- a/src/message/handshake.rs +++ /dev/null @@ -1,435 +0,0 @@ -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/clientinit.rs b/src/message/handshake/clientinit.rs new file mode 100644 index 0000000..17ec9a1 --- /dev/null +++ b/src/message/handshake/clientinit.rs @@ -0,0 +1,91 @@ +use crate::error::ProtocolError; +use crate::primitive::{StringList, Variant, VariantMap}; +use crate::{HandshakeDeserialize, HandshakeSerialize}; + +use failure::Error; + +/// 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); + } + } +} diff --git a/src/message/handshake/clientinitack.rs b/src/message/handshake/clientinitack.rs new file mode 100644 index 0000000..637b989 --- /dev/null +++ b/src/message/handshake/clientinitack.rs @@ -0,0 +1,74 @@ +use crate::error::ProtocolError; +use crate::primitive::{StringList, Variant, VariantList, VariantMap}; +use crate::{HandshakeDeserialize, HandshakeSerialize}; + +use failure::Error; + +/// 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); + } + } +} diff --git a/src/message/handshake/clientinitreject.rs b/src/message/handshake/clientinitreject.rs new file mode 100644 index 0000000..06960b7 --- /dev/null +++ b/src/message/handshake/clientinitreject.rs @@ -0,0 +1,46 @@ +use crate::error::ProtocolError; +use crate::primitive::{Variant, VariantMap}; +use crate::{HandshakeDeserialize, HandshakeSerialize}; + +use failure::Error; + +/// 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); + } + } +} diff --git a/src/message/handshake/clientlogin.rs b/src/message/handshake/clientlogin.rs new file mode 100644 index 0000000..769245b --- /dev/null +++ b/src/message/handshake/clientlogin.rs @@ -0,0 +1,49 @@ +use crate::error::ProtocolError; +use crate::primitive::{Variant, VariantMap}; +use crate::{HandshakeDeserialize, HandshakeSerialize}; + +use failure::Error; + +/// 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); + } + } +} diff --git a/src/message/handshake/clientloginack.rs b/src/message/handshake/clientloginack.rs new file mode 100644 index 0000000..674d307 --- /dev/null +++ b/src/message/handshake/clientloginack.rs @@ -0,0 +1,35 @@ +use crate::error::ProtocolError; +use crate::primitive::{Variant, VariantMap}; +use crate::{HandshakeDeserialize, HandshakeSerialize}; + +use failure::Error; + +/// 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); + } + } +} diff --git a/src/message/handshake/clientloginreject.rs b/src/message/handshake/clientloginreject.rs new file mode 100644 index 0000000..e8380d6 --- /dev/null +++ b/src/message/handshake/clientloginreject.rs @@ -0,0 +1,46 @@ +use crate::error::ProtocolError; +use crate::primitive::{Variant, VariantMap}; +use crate::{HandshakeDeserialize, HandshakeSerialize}; + +use failure::Error; + +/// 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); + } + } +} diff --git a/src/message/handshake/connack.rs b/src/message/handshake/connack.rs new file mode 100644 index 0000000..222c08c --- /dev/null +++ b/src/message/handshake/connack.rs @@ -0,0 +1,59 @@ +use failure::Error; + +/// 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 + pub flags: u8, + /// Some extra protocol version specific data + /// So far unused + pub 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 + pub 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, + }, + )); + } +} diff --git a/src/message/handshake/init.rs b/src/message/handshake/init.rs new file mode 100644 index 0000000..e4c0fa9 --- /dev/null +++ b/src/message/handshake/init.rs @@ -0,0 +1,61 @@ +use crate::Deserialize; +use crate::Serialize; + +/// The first few bytes sent to the core to initialize the connection and setup if we want to use tls and compression +pub struct Init { + pub tls: bool, + pub compression: bool, +} + +impl Init { + pub fn new() -> Self { + Self { + tls: false, + compression: false, + } + } + + pub fn compression(mut self, v: bool) { + self.compression = v + } + + pub fn tls(mut self, v: bool) { + self.tls = v + } + + pub fn serialize(self) -> Vec<u8> { + // The handshake message + let mut handshake: u32 = 0x42b33f00; + + // If TLS is enabled set the TLS bit on the handshake + if self.tls { + handshake |= 0x01; + } + + // If COMPRESSION is enabled set the COMPRESSION bit on the handshake + if self.compression { + handshake |= 0x02; + } + + return handshake.serialize().unwrap(); + } + + pub fn parse(buf: &[u8]) -> Self { + let (_, handshake) = u32::parse(&buf[0..4]).unwrap(); + + let mut init = Self { + tls: false, + compression: false, + }; + + if (handshake & 0x01) >= 1 { + init.tls = true + } + + if (handshake & 0x02) >= 1 { + init.tls = true + } + + return init; + } +} diff --git a/src/message/handshake/mod.rs b/src/message/handshake/mod.rs new file mode 100644 index 0000000..9b3bcee --- /dev/null +++ b/src/message/handshake/mod.rs @@ -0,0 +1,23 @@ +mod clientinit; +mod clientinitack; +mod clientinitreject; +mod clientlogin; +mod clientloginack; +mod clientloginreject; +mod connack; +mod init; +mod protocol; +mod sessioninit; +mod types; + +pub use clientinit::*; +pub use clientinitack::*; +pub use clientinitreject::*; +pub use clientlogin::*; +pub use clientloginack::*; +pub use clientloginreject::*; +pub use connack::*; +pub use init::*; +pub use protocol::*; +pub use sessioninit::*; +pub use types::*; diff --git a/src/message/handshake/protocol.rs b/src/message/handshake/protocol.rs new file mode 100644 index 0000000..d020f33 --- /dev/null +++ b/src/message/handshake/protocol.rs @@ -0,0 +1,36 @@ +use crate::Deserialize; +use crate::Serialize; + +pub enum Protocol { + Legacy = 0x00000001, + Datastream = 0x00000002, +} + +impl Protocol { + pub fn new() -> Self { + Protocol::Datastream + } + + pub fn serialize(self) -> Vec<u8> { + let proto: u32 = 0x80000002; + + proto.serialize().unwrap() + } + + pub fn parse(buf: &[u8]) -> Self { + let mut protolist: Vec<u32> = Vec::new(); + let mut pos = 0; + loop { + let (_, proto) = u32::parse(&buf[pos..(pos + 4)]).unwrap(); + if (proto & 0x80000000) >= 1 { + protolist.push(proto - 0x80000000); + break; + } else { + protolist.push(proto); + pos += 4; + } + } + + Protocol::Datastream + } +} diff --git a/src/message/handshake/sessioninit.rs b/src/message/handshake/sessioninit.rs new file mode 100644 index 0000000..eca4c10 --- /dev/null +++ b/src/message/handshake/sessioninit.rs @@ -0,0 +1,61 @@ +use crate::error::ProtocolError; +use crate::primitive::{Variant, VariantList, VariantMap}; +use crate::{HandshakeDeserialize, HandshakeSerialize}; + +use failure::Error; + +/// 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); + } + } +} |
