aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax Audron <audron@cocaine.farm>2020-09-26 12:01:27 +0200
committerMax Audron <audron@cocaine.farm>2020-09-26 12:03:01 +0200
commit3bdae21716d10032f70a0a889070766bbbe4d141 (patch)
tree78a1ddf98601fbbc03a9375626dfa7923ab85521
parentadd parsing of signalproxy messages (diff)
split handshake.rs
Diffstat (limited to '')
-rw-r--r--src/message/handshake.rs435
-rw-r--r--src/message/handshake/clientinit.rs91
-rw-r--r--src/message/handshake/clientinitack.rs74
-rw-r--r--src/message/handshake/clientinitreject.rs46
-rw-r--r--src/message/handshake/clientlogin.rs49
-rw-r--r--src/message/handshake/clientloginack.rs35
-rw-r--r--src/message/handshake/clientloginreject.rs46
-rw-r--r--src/message/handshake/connack.rs59
-rw-r--r--src/message/handshake/init.rs61
-rw-r--r--src/message/handshake/mod.rs23
-rw-r--r--src/message/handshake/protocol.rs36
-rw-r--r--src/message/handshake/sessioninit.rs61
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);
+ }
+ }
+}