aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock7
-rw-r--r--Cargo.toml1
-rw-r--r--src/main.rs14
-rw-r--r--src/net.rs25
-rw-r--r--src/types.rs13
-rw-r--r--src/types/basic.rs154
-rw-r--r--src/types/handshake.rs62
-rw-r--r--src/types/variant.rs298
-rw-r--r--src/util.rs15
9 files changed, 551 insertions, 38 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 9e6c93e..1cc25f8 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,6 +1,11 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
+name = "byteorder"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -9,6 +14,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "libquassel"
version = "0.1.0"
dependencies = [
+ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -21,5 +27,6 @@ dependencies = [
]
[metadata]
+"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
diff --git a/Cargo.toml b/Cargo.toml
index 5163920..cab9236 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -8,3 +8,4 @@ edition = "2018"
[dependencies]
log = "0.4"
+byteorder = "1.3.2"
diff --git a/src/main.rs b/src/main.rs
index 96324d7..cd45c87 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,19 +1,27 @@
mod consts;
mod net;
-mod util;
mod types;
+#[macro_use]
+mod util;
+
//use util::Hex;
fn main() -> std::io::Result<()> {
- let server = net::connect(
+ let mut server = net::connect(
"localhost",
4242,
false,
false,
)?;
- //server.login("audron", "audron");
+ let client = types::handshake::ClientInit {
+ client_version: String::from("Rust 0.0.0"),
+ build_date: String::from("today"),
+ client_features: 0x00000000,
+ feature_list: types::StringList::new()
+ };
+ server.login("audron", "audron", client);
Ok(())
} // the stream is closed here
diff --git a/src/net.rs b/src/net.rs
index 94cae21..3d56437 100644
--- a/src/net.rs
+++ b/src/net.rs
@@ -6,25 +6,26 @@ use std::net::TcpStream;
use std::vec::Vec;
extern crate log;
-use log::{info, warn, debug};
+// use log::{info, warn, debug};
use super::types;
+use super::types::Serialize;
-pub struct Server {
- TcpStream: TcpStream,
+pub struct Client {
+ tcp_stream: TcpStream,
pub address: &'static str,
pub port: u32,
pub tls: bool,
pub compression: bool,
}
-// impl Server {
-// pub fn login(&self, user: &'static str, pass: &'static str) {
-// println!("{:?}", basic::Void);
-// }
-// }
+impl Client {
+ pub fn login(&mut self, user: &'static str, pass: &'static str, client: types::handshake::ClientInit) {
+ self.tcp_stream.write(&client.serialize()).unwrap();
+ }
+}
-pub fn connect(address: &'static str, port: u32, tls: bool, compression: bool) -> Result<Server, Error> {
+pub fn connect(address: &'static str, port: u32, tls: bool, compression: bool) -> Result<Client, Error> {
//let mut s = BufWriter::new(TcpStream::connect(format!("{}:{}", address, port)).unwrap());
let mut s = TcpStream::connect(format!("{}:{}", address, port)).unwrap();
@@ -46,10 +47,10 @@ pub fn connect(address: &'static str, port: u32, tls: bool, compression: bool) -
let mut buf = [0; 4];
s.read_exact(&mut buf)?;
- println!("Received: {:?}", types::basic::VOID);
+ println!("Received: {:?}", buf);
- let mut server: Server = Server {
- TcpStream: s,
+ let server: Client = Client {
+ tcp_stream: s,
address: address,
port: port,
tls: tls,
diff --git a/src/types.rs b/src/types.rs
index 9836248..1c22ce6 100644
--- a/src/types.rs
+++ b/src/types.rs
@@ -1,4 +1,17 @@
+#[allow(dead_code)]
pub mod basic;
+pub mod variant;
+pub mod handshake;
+
+pub use basic::*;
+
+pub trait Serialize {
+ fn serialize(&self) -> Vec<u8>;
+}
+
+pub trait Deserialize {
+ fn parse(&mut self, b: &[u8]) -> usize;
+}
// 78 da 6c 54 6d 6b d3 50 14 7e 44 41 d1 09
diff --git a/src/types/basic.rs b/src/types/basic.rs
index f7a505a..dc5e8d5 100644
--- a/src/types/basic.rs
+++ b/src/types/basic.rs
@@ -1,41 +1,149 @@
+#[allow(dead_code)]
pub const VOID: u32 = 0x00000000;
pub const BOOL: u32 = 0x00000001;
-pub const INT: u32 = 0x00000002; // int32_t
-pub const UINT: u32 = 0x00000003; // uint32_t
pub const QCHAR: u32 = 0x00000007;
+
+pub const QVARIANT: u32 = 0x00000090;
pub const QVARIANTMAP: u32 = 0x00000008;
pub const QVARIANTLIST: u32 = 0x00000009;
+
pub const QSTRING: u32 = 0x0000000a;
pub const QSTRINGLIST: u32 = 0x0000000b;
pub const QBYTEARRAY: u32 = 0x0000000c;
+
pub const QTIME: u32 = 0x0000000f;
pub const QDATETIME: u32 = 0x00000010;
pub const USERTYPE: u32 = 0x0000007f;
+
+
+// Basic types
pub const LONG: u32 = 0x00000081; // int64_t
+pub const INT: u32 = 0x00000002; // int32_t
pub const SHORT: u32 = 0x00000082; // int16_t
pub const CHAR: u32 = 0x00000083; // int8_t
+
pub const ULONG: u32 = 0x00000084; // uint64_t
+pub const UINT: u32 = 0x00000003; // uint32_t
pub const USHORT: u32 = 0x00000085; // uint16_t
pub const UCHAR: u32 = 0x00000086; // uint8_t
-pub const QVARIANT: u32 = 0x00000090;
-// Void 0x00000000
-// Bool 0x00000001
-// Int 0x00000002 int32_t
-// UInt 0x00000003 uint32_t
-// QChar 0x00000007
-// QVariantMap 0x00000008
-// QVariantList 0x00000009
-// QString 0x0000000a
-// QStringList 0x0000000b
-// QByteArray 0x0000000c
-// QTime 0x0000000f
-// QDateTime 0x00000010
-// UserType 0x0000007f
-// Long 0x00000081 int64_t
-// Short 0x00000082 int16_t
-// Char 0x00000083 int8_t
-// ULong 0x00000084 uint64_t
-// UShort 0x00000085 uint16_t
-// UChar 0x00000086 uint8_t
-// QVariant 0x00000090
+extern crate byteorder;
+use byteorder::{ByteOrder, BigEndian};
+
+use std::vec::Vec;
+use std::convert::TryInto;
+
+use crate::types;
+
+impl types::Deserialize for bool {
+ fn parse(&mut self, b: &[u8]) -> usize {
+ if b[0] == 0 { *self = false } else { *self = true };
+ return 1;
+ }
+}
+
+impl types::Deserialize for u64 {
+ fn parse(&mut self, b: &[u8]) -> usize {
+ *self = BigEndian::read_u64(b);
+ return 8;
+ }
+}
+impl types::Deserialize for u32 {
+ fn parse(&mut self, b: &[u8]) -> usize {
+ // self = &rdr.read_u32::<BigEndian>().unwrap()
+ *self = BigEndian::read_u32(b);
+ return 4;
+ }
+}
+impl types::Deserialize for u16 {
+ fn parse(&mut self, b: &[u8]) -> usize {
+ *self = BigEndian::read_u16(b);
+ return 2;
+ }
+}
+impl types::Deserialize for u8 {
+ fn parse(&mut self, b: &[u8]) -> usize {
+ *self = b[0];
+ return 1;
+ }
+}
+
+impl types::Deserialize for i64 {
+ fn parse(&mut self, b: &[u8]) -> usize {
+ *self = BigEndian::read_i64(b);
+ return 8;
+ }
+}
+impl types::Deserialize for i32 {
+ fn parse(&mut self, b: &[u8]) -> usize {
+ *self = BigEndian::read_i32(b);
+ return 4;
+ }
+}
+impl types::Deserialize for i16 {
+ fn parse(&mut self, b: &[u8]) -> usize {
+ *self = BigEndian::read_i16(b);
+ return 2;
+ }
+}
+impl types::Deserialize for i8 {
+ fn parse(&mut self, b: &[u8]) -> usize {
+ *self = b[0].try_into().unwrap();
+ return 1;
+ }
+}
+
+
+pub type String = std::string::String;
+impl types::Serialize for String {
+ fn serialize(&self) -> Vec<u8> {
+ let len: i32 = self.len().try_into().unwrap();
+ let mut res: Vec<u8> = Vec::new();
+
+ res.extend(len.to_be_bytes().iter());
+ res.extend(self.parse::<u16>().unwrap().to_be_bytes().iter());
+
+ return res;
+ }
+}
+
+impl types::Deserialize for String {
+ fn parse(&mut self, b: &[u8]) -> usize {
+ let mut len: i32 = 0;
+ len.parse(&b[0..4]);
+ let ulen: usize = len as usize;
+ *self = BigEndian::read_u16(&b[(5)..(5+ulen)]).to_string();
+ b.len()
+ }
+}
+
+pub type StringList = Vec<String>;
+impl types::Serialize for StringList {
+ fn serialize(&self) -> Vec<u8> {
+ let len: i32 = self.len().try_into().unwrap();
+ let mut res: Vec<u8> = Vec::new();
+
+ res.extend(len.to_be_bytes().iter());
+ for x in self {
+ res.extend(x.parse::<u16>().unwrap().to_be_bytes().iter());
+ }
+
+ return res;
+ }
+}
+
+impl types::Deserialize for StringList {
+ fn parse(&mut self, b: &[u8]) -> usize {
+ let len: i32 = self.len().try_into().unwrap();
+ let res: StringList = StringList::new();
+
+ let mut pos: usize = 0;
+ for _ in 0..len {
+ let mut val: String = String::new();
+ pos = pos + val.parse(&b[pos..]);
+ }
+
+ *self = res;
+ return pos;
+ }
+}
diff --git a/src/types/handshake.rs b/src/types/handshake.rs
new file mode 100644
index 0000000..cd79d29
--- /dev/null
+++ b/src/types/handshake.rs
@@ -0,0 +1,62 @@
+use crate::types::{Serialize, Deserialize};
+use crate::types::variant::{Variant, VariantMap, VariantList};
+use crate::types::basic::{String, StringList};
+
+pub struct ClientInit {
+ pub client_version: String, // Version of the client
+ pub build_date: String, // Build date of the client
+ pub client_features: u32, // Flags of supported legacy features
+ pub feature_list: StringList // List of supported extended features
+}
+
+impl Serialize for ClientInit {
+ fn serialize(&self) -> Vec<u8> {
+ 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("BuildDate".to_string(), Variant::String(self.build_date.clone()));
+ values.insert("ClientFeatures".to_string(), Variant::u32(self.client_features));
+ values.insert("FeatureList".to_string(), Variant::StringList(self.feature_list.clone()));
+ return values.serialize();
+ }
+}
+
+// impl Deserialize for ClientInit {
+// fn deserialize(&mut self, b: &[u8]) {
+//
+// }
+// }
+
+pub struct ClientInitReject {
+ pub error_string: String
+}
+
+impl Serialize for ClientInitReject {
+ fn serialize(&self) -> Vec<u8> {
+ let mut values: VariantMap = VariantMap::with_capacity(2);
+ values.insert("MsgTypes".to_string(), Variant::String("ClientInitReject".to_string()));
+ values.insert("ErrorString".to_string(), Variant::String(self.error_string.clone()));
+ return values.serialize();
+ }
+}
+
+pub struct ClientInitAck {
+ pub core_features: u32, // Flags of supported legacy features
+ pub core_configured: bool, // If the core has already been configured
+ pub backend_info: VariantList, // List of VariantMaps of info on available backends
+ pub authenticator_info: VariantList, // List of VariantMaps of info on available authenticators
+ pub feature_list: StringList, // List of supported extended features
+}
+
+impl Serialize for ClientInitAck {
+ fn serialize(&self) -> Vec<u8> {
+ let mut values: VariantMap = VariantMap::with_capacity(2);
+ values.insert("MsgTypes".to_string(), Variant::String("ClientInitAck".to_string()));
+ values.insert("CoreFeatures".to_string(), Variant::u32(self.core_features));
+ values.insert("CoreConfigured".to_string(), Variant::bool(self.core_configured));
+ values.insert("BackendInfo".to_string(), Variant::VariantList(self.backend_info.clone()));
+ values.insert("AuthenticatorInfo".to_string(), Variant::VariantList(self.authenticator_info.clone()));
+ values.insert("FeatureList".to_string(), Variant::StringList(self.feature_list.clone()));
+ return values.serialize();
+ }
+}
diff --git a/src/types/variant.rs b/src/types/variant.rs
new file mode 100644
index 0000000..59c287f
--- /dev/null
+++ b/src/types/variant.rs
@@ -0,0 +1,298 @@
+use std::vec::Vec;
+use std::convert::TryInto;
+use std::collections::HashMap;
+
+use crate::types::{Serialize, Deserialize};
+use crate::types::basic;
+use crate::types::basic::*;
+use crate::types::basic::{String,StringList};
+
+pub type VariantMap = HashMap<String, Variant>;
+impl Serialize for VariantMap {
+ fn serialize<'a>(&'a self) -> Vec<u8> {
+ let len: i32 = self.len().try_into().unwrap();
+ let mut res: Vec<u8> = Vec::new();
+
+ res.extend(len.to_be_bytes().iter());
+ for (k, v) in self {
+ res.extend(k.serialize().iter());
+ res.extend(v.serialize().iter());
+ }
+
+ return res;
+ }
+}
+
+impl Deserialize for VariantMap {
+ fn parse(&mut self, b: &[u8]) -> usize {
+ let mut len: i32 = 0;
+ len.parse(&b[0..3]);
+
+ let mut pos = 4;
+ let map = VariantMap::new();
+ for _ in 0..len {
+ let mut name: String = String::new();
+ name.parse(&b[(pos)..(pos+2)]);
+
+ let mut value: Variant = Variant::Unknown;
+ let res = value.parse(&b[(pos+3)..]);
+
+ pos = pos + 2 + res;
+ }
+
+ *self = map;
+ return pos;
+ }
+}
+
+pub type VariantList = Vec<Variant>;
+impl Serialize for VariantList {
+ fn serialize(&self) -> Vec<u8> {
+ let len: i32 = self.len().try_into().unwrap();
+ let mut res: Vec<u8> = Vec::new();
+
+ res.extend(len.to_be_bytes().iter());
+ for v in self {
+ res.extend(v.serialize().iter());
+ }
+
+ return res;
+ }
+}
+
+impl Deserialize for VariantList {
+ fn parse(&mut self, b: &[u8]) -> usize {
+ let len: i32 = self.len().try_into().unwrap();
+ let res: VariantList = VariantList::new();
+
+ let mut pos: usize = 0;
+ for _ in 0..len {
+ let mut val: Variant = Variant::Unknown;
+ pos = pos + val.parse(&b[pos..]);
+ }
+
+ *self = res;
+ return pos;
+ }
+}
+
+#[allow(non_camel_case_types, dead_code)]
+#[derive(Clone)]
+pub enum Variant {
+ Unknown,
+ VariantMap(VariantMap),
+ VariantList(VariantList),
+ String(String),
+ StringList(StringList),
+ bool(bool),
+ u64(u64),
+ u32(u32),
+ u16(u16),
+ u8(u8),
+ i64(i64),
+ i32(i32),
+ i16(i16),
+ i8(i8),
+}
+
+impl Serialize for Variant {
+ fn serialize(&self) -> Vec<u8> {
+ let unknown: u32 = 0x00000000;
+ let mut res: Vec<u8> = Vec::new();
+
+ match self {
+ Variant::Unknown => {
+ return res;
+ },
+ Variant::VariantMap(v) => {
+ res.extend(basic::QVARIANTMAP.to_be_bytes().iter());
+ res.extend(unknown.to_be_bytes().iter());
+ res.extend(v.serialize().iter());
+ },
+ Variant::VariantList(v) => {
+ res.extend(basic::QVARIANTLIST.to_be_bytes().iter());
+ res.extend(unknown.to_be_bytes().iter());
+ res.extend(v.serialize().iter());
+ },
+ Variant::String(v) => {
+ res.extend(basic::QSTRING.to_be_bytes().iter());
+ res.extend(unknown.to_be_bytes().iter());
+ res.extend(v.serialize().iter());
+ },
+ Variant::StringList(v) => {
+ res.extend(basic::QSTRINGLIST.to_be_bytes().iter());
+ res.extend(unknown.to_be_bytes().iter());
+ res.extend(v.serialize().iter());
+ },
+ Variant::bool(v) => {
+ res.extend(basic::BOOL.to_be_bytes().iter());
+ res.extend(unknown.to_be_bytes().iter());
+ let i = *v as i8;
+ res.extend(i.to_be_bytes().iter());
+ },
+ Variant::u64(v) => {
+ res.extend(basic::ULONG.to_be_bytes().iter());
+ res.extend(unknown.to_be_bytes().iter());
+ res.extend(v.to_be_bytes().iter());
+ },
+ Variant::u32(v) => {
+ res.extend(basic::UINT.to_be_bytes().iter());
+ res.extend(unknown.to_be_bytes().iter());
+ res.extend(v.to_be_bytes().iter());
+ },
+ Variant::u16(v) => {
+ res.extend(basic::USHORT.to_be_bytes().iter());
+ res.extend(unknown.to_be_bytes().iter());
+ res.extend(v.to_be_bytes().iter());
+ },
+ Variant::u8(v) => {
+ res.extend(basic::UCHAR.to_be_bytes().iter());
+ res.extend(unknown.to_be_bytes().iter());
+ res.extend(v.to_be_bytes().iter());
+ },
+ Variant::i64(v) => {
+ res.extend(basic::LONG.to_be_bytes().iter());
+ res.extend(unknown.to_be_bytes().iter());
+ res.extend(v.to_be_bytes().iter());
+ },
+ Variant::i32(v) => {
+ res.extend(basic::INT.to_be_bytes().iter());
+ res.extend(unknown.to_be_bytes().iter());
+ res.extend(v.to_be_bytes().iter());
+ },
+ Variant::i16(v) => {
+ res.extend(basic::SHORT.to_be_bytes().iter());
+ res.extend(unknown.to_be_bytes().iter());
+ res.extend(v.to_be_bytes().iter());
+ },
+ Variant::i8(v) => {
+ res.extend(basic::CHAR.to_be_bytes().iter());
+ res.extend(unknown.to_be_bytes().iter());
+ res.extend(v.to_be_bytes().iter());
+ },
+ }
+
+ return res
+ }
+}
+
+impl Deserialize for Variant {
+ fn parse(&mut self, b: &[u8]) -> usize {
+ let mut qtype: i32 = 0;
+ qtype.parse(&b[0..6]);
+ let qtype: u32 = qtype as u32;
+
+ #[allow(unused_variables)]
+ let unknown: u8 = b[7];
+
+ match qtype {
+ QVARIANTMAP => {
+ let mut value: VariantMap = VariantMap::new();
+
+ let len = value.parse(&b[8..]);
+ *self = Variant::VariantMap(value.clone());
+
+ return len;
+ },
+ QVARIANTLIST => {
+ let mut value: VariantList = VariantList::new();
+
+ let len = value.parse(&b[8..]);
+ *self = Variant::VariantList(value.clone());
+
+ return len;
+ },
+ QSTRING => {
+ let mut value: String = String::new();
+
+ let len = value.parse(&b[8..]);
+ *self = Variant::String(value.clone());
+
+ return len;
+ },
+ QSTRINGLIST => {
+ let mut value: StringList = StringList::new();
+
+ let len = value.parse(&b[8..]);
+ *self = Variant::StringList(value.clone());
+
+ return len;
+ },
+ BOOL => {
+ let mut value: bool = false;
+
+ let len = value.parse(&b[8..]);
+ *self = Variant::bool(value);
+
+ return len;
+ },
+ ULONG => {
+ let mut value: u64 = 0;
+
+ let len = value.parse(&b[8..]);
+ *self = Variant::u64(value);
+
+ return len;
+ },
+ UINT => {
+ let mut value: u32 = 0;
+
+ let len = value.parse(&b[8..]);
+ *self = Variant::u32(value);
+
+ return len;
+ },
+ USHORT => {
+ let mut value: u16 = 0;
+
+ let len = value.parse(&b[8..]);
+ *self = Variant::u16(value);
+
+ return len;
+ },
+ UCHAR => {
+ let mut value: u8 = 0;
+
+ let len = value.parse(&b[8..]);
+ *self = Variant::u8(value);
+
+ return len;
+ },
+ LONG => {
+ let mut value: i64 = 0;
+
+ let len = value.parse(&b[8..]);
+ *self = Variant::i64(value);
+
+ return len;
+ },
+ INT => {
+ let mut value: i32 = 0;
+
+ let len = value.parse(&b[8..]);
+ *self = Variant::i32(value);
+
+ return len;
+ },
+ SHORT => {
+ let mut value: i16 = 0;
+
+ let len = value.parse(&b[8..]);
+ *self = Variant::i16(value);
+
+ return len;
+ },
+ CHAR => {
+ let mut value: i8 = 0;
+
+ let len = value.parse(&b[8..]);
+ *self = Variant::i8(value);
+
+ return len;
+ },
+ _ => {
+ return 0;
+ }
+ }
+ }
+}
diff --git a/src/util.rs b/src/util.rs
index e69de29..809287f 100644
--- a/src/util.rs
+++ b/src/util.rs
@@ -0,0 +1,15 @@
+#[macro_export]
+macro_rules! parse_match {
+ ( $matchee:expr, $pos:expr, $map:expr, $bytes:expr, $name:expr, $(($pattern:pat, $type:ty, $variant:expr)),* ) => {
+ match $matchee {
+ $(
+ $pattern => {
+ let value: $type;
+
+ $pos = $pos + value.parse(&$bytes[($pos)..]);
+ $map.insert($name, $variant(value));
+ },
+ )*
+ };
+ };
+}