From 1b0047a4a4e6d6e7a871868664835827bd5366e5 Mon Sep 17 00:00:00 2001 From: Igor Katson Date: Sat, 10 Jul 2021 12:26:37 +0100 Subject: [PATCH] DHT bencode protocol more or less done --- Cargo.lock | 2 + crates/bencode/src/bencode_value.rs | 4 +- crates/dht/Cargo.toml | 4 +- crates/dht/src/bprotocol.rs | 407 ++++++++++++++++++++++++++++ crates/dht/src/lib.rs | 65 ++++- 5 files changed, 471 insertions(+), 11 deletions(-) create mode 100644 crates/dht/src/bprotocol.rs diff --git a/Cargo.lock b/Cargo.lock index e000004..dca0d1b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -274,9 +274,11 @@ dependencies = [ name = "dht" version = "0.1.0" dependencies = [ + "anyhow", "bencode", "hex 0.4.3", "kad", + "serde", "tokio", ] diff --git a/crates/bencode/src/bencode_value.rs b/crates/bencode/src/bencode_value.rs index f8458d8..b765d16 100644 --- a/crates/bencode/src/bencode_value.rs +++ b/crates/bencode/src/bencode_value.rs @@ -146,7 +146,7 @@ mod tests { #[test] fn test_deserialize_torrent_dyn() { let mut buf = Vec::new(); - let filename = "resources/ubuntu-21.04-desktop-amd64.iso.torrent"; + let filename = "../librqbit/resources/ubuntu-21.04-desktop-amd64.iso.torrent"; std::fs::File::open(filename) .unwrap() .read_to_end(&mut buf) @@ -161,7 +161,7 @@ mod tests { #[test] fn test_serialize_torrent_dyn() { let mut buf = Vec::new(); - let filename = "resources/ubuntu-21.04-desktop-amd64.iso.torrent"; + let filename = "../librqbit/resources/ubuntu-21.04-desktop-amd64.iso.torrent"; std::fs::File::open(filename) .unwrap() .read_to_end(&mut buf) diff --git a/crates/dht/Cargo.toml b/crates/dht/Cargo.toml index 76095e6..c219f43 100644 --- a/crates/dht/Cargo.toml +++ b/crates/dht/Cargo.toml @@ -8,5 +8,7 @@ edition = "2018" [dependencies] kad = "0.6" tokio = {version = "1", features = ["macros", "rt"]} +serde = {version = "1", features = ["derive"]} hex = "0.4" -bencode = {path = "../bencode"} \ No newline at end of file +bencode = {path = "../bencode"} +anyhow = "1" \ No newline at end of file diff --git a/crates/dht/src/bprotocol.rs b/crates/dht/src/bprotocol.rs new file mode 100644 index 0000000..619075a --- /dev/null +++ b/crates/dht/src/bprotocol.rs @@ -0,0 +1,407 @@ +use std::{ + marker::PhantomData, + net::{Ipv4Addr, SocketAddrV4}, +}; + +use bencode::ByteBuf; +use serde::{ + de::{IgnoredAny, Unexpected}, + Deserialize, Deserializer, Serialize, +}; + +#[derive(Debug)] +enum MessageType { + Request, + Response, + Error, +} + +pub struct Id20(pub [u8; 20]); + +impl std::fmt::Debug for Id20 { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "<")?; + for byte in self.0 { + write!(f, "{:02x?}", byte)?; + } + write!(f, ">")?; + Ok(()) + } +} + +impl Serialize for Id20 { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_bytes(&self.0) + } +} + +impl<'de> Deserialize<'de> for Id20 { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct Visitor; + impl<'de> serde::de::Visitor<'de> for Visitor { + type Value = Id20; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "a 20 byte slice") + } + fn visit_bytes(self, v: &[u8]) -> Result + where + E: serde::de::Error, + { + if v.len() != 20 { + return Err(E::invalid_length(20, &self)); + } + let mut buf = [0u8; 20]; + buf.copy_from_slice(&v); + Ok(Id20(buf)) + } + } + deserializer.deserialize_bytes(Visitor {}) + } +} + +impl<'de> Deserialize<'de> for MessageType { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct Visitor; + impl<'de> serde::de::Visitor<'de> for Visitor { + type Value = MessageType; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, r#""q", "e" or "r" bencode string"#) + } + fn visit_bytes(self, v: &[u8]) -> Result + where + E: serde::de::Error, + { + let msg = match v { + b"q" => MessageType::Request, + b"r" => MessageType::Response, + b"e" => MessageType::Error, + _ => return Err(E::invalid_value(Unexpected::Bytes(v), &self)), + }; + Ok(msg) + } + } + deserializer.deserialize_bytes(Visitor {}) + } +} + +impl Serialize for MessageType { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + match self { + MessageType::Request => serializer.serialize_bytes(b"q"), + MessageType::Response => serializer.serialize_bytes(b"r"), + MessageType::Error => serializer.serialize_bytes(b"e"), + } + } +} + +#[derive(Debug)] +struct ErrorDescription { + code: i32, + description: BufT, +} + +impl Serialize for ErrorDescription +where + BufT: Serialize, +{ + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + use serde::ser::SerializeSeq; + let mut seq = serializer.serialize_seq(Some(2))?; + seq.serialize_element(&self.code)?; + seq.serialize_element(&self.description)?; + seq.end() + } +} + +impl<'de, BufT> Deserialize<'de> for ErrorDescription +where + BufT: Deserialize<'de>, +{ + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct Visitor { + phantom: PhantomData, + } + impl<'de, BufT> serde::de::Visitor<'de> for Visitor + where + BufT: Deserialize<'de>, + { + type Value = ErrorDescription; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, r#"a list [i32, string]"#) + } + fn visit_seq(self, mut seq: A) -> Result + where + A: serde::de::SeqAccess<'de>, + { + use serde::de::Error; + let code = match seq.next_element::()? { + Some(code) => code, + None => return Err(A::Error::invalid_length(0, &self)), + }; + let description = match seq.next_element::()? { + Some(code) => code, + None => return Err(A::Error::invalid_length(1, &self)), + }; + // The type doesn't matter here, we are just making sure the list is over. + if seq.next_element::()?.is_some() { + return Err(A::Error::invalid_length(3, &self)); + } + Ok(ErrorDescription { code, description }) + } + } + deserializer.deserialize_seq(Visitor { + phantom: PhantomData, + }) + } +} + +#[derive(Debug, Serialize, Deserialize)] +struct RawMessage { + #[serde(rename = "y")] + message_type: MessageType, + #[serde(rename = "t")] + transaction_id: BufT, + #[serde(rename = "e", skip_serializing_if = "Option::is_none")] + error: Option>, + #[serde(rename = "r", skip_serializing_if = "Option::is_none")] + response: Option, + #[serde(rename = "q", skip_serializing_if = "Option::is_none")] + method_name: Option, + #[serde(rename = "a", skip_serializing_if = "Option::is_none")] + arguments: Option, +} + +#[derive(Debug)] +pub struct Node { + pub id: Id20, + pub addr: SocketAddrV4, +} + +#[derive(Debug)] +pub struct CompactNodeInfo { + pub nodes: Vec, +} + +impl Serialize for CompactNodeInfo { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut buf = Vec::::with_capacity(self.nodes.len() * 26); + for node in self.nodes.iter() { + buf.extend_from_slice(&node.id.0); + let ip_octets = node.addr.ip().octets(); + let port = node.addr.port(); + buf.extend_from_slice(&ip_octets); + // BE encoding for port. + buf.push((port >> 8) as u8); + buf.push((port & 0xff) as u8); + } + serializer.serialize_bytes(&buf) + } +} + +impl<'de> Deserialize<'de> for CompactNodeInfo { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct Visitor; + impl<'de> serde::de::Visitor<'de> for Visitor { + type Value = CompactNodeInfo; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "compact node info with length multiple of 26") + } + fn visit_borrowed_bytes(self, v: &'de [u8]) -> Result + where + E: serde::de::Error, + { + if v.len() % 26 != 0 { + return Err(E::invalid_length(v.len(), &self)); + } + let mut buf = Vec::::with_capacity(v.len() / 26); + for chunk in v.chunks_exact(26) { + let mut node_id = [0u8; 20]; + node_id.copy_from_slice(&chunk[..20]); + let ip = Ipv4Addr::new(chunk[20], chunk[21], chunk[22], chunk[23]); + let port = ((chunk[24] as u16) << 8) + chunk[25] as u16; + buf.push(Node { + id: Id20(node_id), + addr: SocketAddrV4::new(ip, port), + }) + } + Ok(CompactNodeInfo { nodes: buf }) + } + } + deserializer.deserialize_bytes(Visitor) + } +} + +#[derive(Debug)] +pub struct CompactPeerInfo { + pub addr: SocketAddrV4, +} + +impl Serialize for CompactPeerInfo { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let octets = self.addr.ip().octets(); + let port = self.addr.port(); + let buf = [ + octets[0], + octets[1], + octets[2], + octets[3], + (port >> 8) as u8, + (port & 0xff) as u8, + ]; + serializer.serialize_bytes(&buf) + } +} + +impl<'de> Deserialize<'de> for CompactPeerInfo { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct Visitor; + impl<'de> serde::de::Visitor<'de> for Visitor { + type Value = CompactPeerInfo; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "6 bytes of peer info") + } + fn visit_bytes(self, v: &[u8]) -> Result + where + E: serde::de::Error, + { + if v.len() != 6 { + return Err(E::invalid_length(6, &self)); + } + let ip = Ipv4Addr::new(v[0], v[1], v[2], v[3]); + let port = ((v[4] as u16) << 8) + v[5] as u16; + Ok(CompactPeerInfo { + addr: SocketAddrV4::new(ip, port), + }) + } + } + deserializer.deserialize_bytes(Visitor {}) + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct FindNodeRequest { + pub id: Id20, + pub target: Id20, +} + +#[derive(Debug, Serialize, Deserialize)] +struct Response { + pub id: Id20, + #[serde(skip_serializing_if = "Option::is_none")] + pub nodes: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub values: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub token: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct GetPeersRequest { + pub id: Id20, + pub info_hash: Id20, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(bound(serialize = "BufT: AsRef<[u8]> + Serialize"))] +#[serde(bound(deserialize = "BufT: From<&'de [u8]> + Deserialize<'de>"))] +pub struct GetPeersResponse { + pub id: Id20, + pub token: BufT, + #[serde(skip_serializing_if = "Option::is_none")] + pub values: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub nodes: Option, +} + +#[derive(Debug)] +pub struct Message { + pub transaction_id: BufT, + pub kind: MessageKind, +} + +#[derive(Debug)] +pub enum MessageKind { + Error(ErrorDescription), + GetPeersRequest(GetPeersRequest), + FindNodeRequest(FindNodeRequest), + Response(Response), +} + +pub fn deserialize_message<'de, BufT>(buf: &'de [u8]) -> anyhow::Result> +where + BufT: Deserialize<'de> + AsRef<[u8]>, +{ + let de: RawMessage = bencode::from_bytes(buf)?; + match de.message_type { + MessageType::Request => match (de.arguments, de.method_name, de.response, de.error) { + (Some(_), Some(method_name), None, None) => match method_name.as_ref() { + b"find_node" => { + let de: RawMessage = bencode::from_bytes(buf)?; + Ok(Message { + transaction_id: de.transaction_id, + kind: MessageKind::FindNodeRequest(de.arguments.unwrap()), + }) + } + b"get_peers" => { + let de: RawMessage = bencode::from_bytes(buf)?; + Ok(Message { + transaction_id: de.transaction_id, + kind: MessageKind::GetPeersRequest(de.arguments.unwrap()), + }) + } + other => anyhow::bail!("unsupported method {:?}", ByteBuf(other)), + }, + _ => anyhow::bail!( + "cannot deserialize message as request, expected exactly \"a\" and \"q\" to be set" + ), + }, + MessageType::Response => match (de.arguments, de.method_name, de.response, de.error) { + (None, None, Some(_), None) => { + let de: RawMessage> = bencode::from_bytes(buf)?; + Ok(Message { + transaction_id: de.transaction_id, + kind: MessageKind::Response(de.response.unwrap()), + }) + } + _ => anyhow::bail!( + "cannot deserialize message as response, expected exactly \"r\" to be set" + ), + }, + MessageType::Error => todo!(), + } +} diff --git a/crates/dht/src/lib.rs b/crates/dht/src/lib.rs index 2e3cb3a..d624dbd 100644 --- a/crates/dht/src/lib.rs +++ b/crates/dht/src/lib.rs @@ -1,23 +1,72 @@ +mod bprotocol; + #[cfg(test)] mod tests { + use super::bprotocol; use bencode::ByteBuf; + // Dumped with wireshark. + const FIND_NODE_REQUEST: &[u8] = b"64313a6164323a696432303abd7b477cfbcd10f30b705da20201e7101d8df155363a74617267657432303abd7b477cfbcd10f30b705da20201e7101d8df15565313a71393a66696e645f6e6f6465313a74323a0005313a79313a7165"; + const GET_PEERS_REQUEST: &[u8] = b"64313a6164323a696432303abd7b477cfbcd10f30b705da20201e7101d8df155393a696e666f5f6861736832303acab507494d02ebb1178b38f2e9d7be299c86b86265313a71393a6765745f7065657273313a74323a0006313a79313a7165"; + const FIND_NODE_RESPONSE: &[u8] = b"64313a7264323a696432303a3c00727348b3b8ed70baa1e1411b3869d8481321353a6e6f6465733230383a67a312defb7d429086bfdcd5a209684ee13f59615cbe360bc8d567a312defb7d429086bfdcd5a209684ee13f59615cbe360bc8d567a312defb7d429086bfdcd5a209684ee13f59615cbe360bc8d567a312defb7d429086bfdcd5a209684ee13f59615cbe360bc8d567a312defb7d429086bfdcd5a209684ee13f59615cbe360bc8d567a312defb7d429086bfdcd5a209684ee13f59615cbe360bc8d567a312defb7d429086bfdcd5a209684ee13f59615cbe360bc8d567a312defb7d429086bfdcd5a209684ee13f59615cbe360bc8d565313a74323a0005313a76343a4a420000313a79313a7265"; + const FIND_NODE_RESPONSE_2: &[u8] = b"64323a6970363a081ab440e935313a7264323a696432303a32f54e697351ff4aec29cdbaabf2fbe3467cc267353a6e6f6465733431363a54133f7f6d77567ff210fe88d49839107d1a955956aaa625e9ee438e4a0af6b324d9672886052c856b26b25835a689afbbdf5436b643eb20605e1d18f848b32cd275a117afb52d3a474d18541ae18dd20d3fbd936983af4ea87135d785d0661de2f4c4bf7925c59269105c05caa68658851c018d8890f73604e334afdfb8e556fd7ca8f3e0211bd2af91c4af4eee69415a273c0bd1c2b02e8b9ba827139b6c6ebc6dcb6ee53aac3c5147530a432e1b62c9116e1316e9364d7fd2f10f2499f47e862d847937e39a51aed74bb6e8f1c491d520868f1893aaa007d1af19b5328f1b4840759e5743aa59a6bf090c76b846145c6895303b7a49be387fd609a9212eb6541b1ae1fd2ddcf776b4688dd359c8157120809ac8b6651e5e6e8d58b4a80fa124e1f4ed536d61e4ee25d5a702fc8ab70cdf45852708c999215cc406c4caa862bcd0a6b88e58128d2b280ac74631b3591ae1fa4484a5560c31de4fc046b97b4c6ac31dc324ab2ef20952049bfcecdbc8cf79e4cfd378a89779c605559b79b8ae25ba326249e5629f7b9cc0ad33143832e1bca63da63cdb8a940117f0adc2c41965313a74323a0002313a79313a7265"; + const FIND_NODE_RESPONSE_3: &[u8] = b"64323a6970363a081ab440e935313a7264323a696432303a32f54e697351ff4aec29cdbaabf2fbe3467cc267353a6e6f6465733431363a26d4302a32aecf28f3fee9f6caf8867d762e28b963b5a531c4917373b33fb43c9d7c0d3daf45ee22ab947d4511c054364d4a904464878fc4a31e88b41d7ea953f7dc91d8017dafee5d0f8a4d2fa19fd3ec1c37c6807cad0a5601698909e7a487532fb9408928afaa7ca5e376bee87c4caafa88f2f9a9cc2ed992cd48be68771b48bb6efc225561c00dc3f40d04ab08d93c21a1b89097bd06fa4d1d122d6f1d86e041a5525a69b26d265d039cd52c8bebc923bf1bc3e9f71c7ed05e349d54465cca22233147f21d4c1cc531e461254249ea653909abe367bc25efab70bbe28cd38cbafc2e6db11df5d66bc20bc8a4c9490d84bf29f09ceb44c230dd2ced8b5cec47c71ae1ff66e9ed230e165873b0bef32163ad52c66edce28a7c9c8ae8647af27ba1eac73737ac167e21ed9116b1ef8104a7c28f89606be6f36d7584b791128793e8f8a0e6b48897a6463532547e400ef3a7067237d4d77bf40f1c09773ea85dd269adf35eeebca89b6993cdb116c0512abc2cbc74973d5e5f09940d0bbdf4e047ce15101ae13d794b1230188404a9fd2a5a10ccefb0622057bc6d7eeae5fb8565313a74323a0003313a79313a7265"; + + const WHAT_IS_THAT: &[u8]= b"64313a6164323a696432303abd7b477cfbcd10f30b705da20201e7101d8df155393a696e666f5f6861736832303acab507494d02ebb1178b38f2e9d7be299c86b86265313a71393a6765745f7065657273313a74323a0007313a79313a7165"; + fn debug_hex_bencode(name: &str, data: &[u8]) { println!("{}", name); let data = hex::decode(data).unwrap(); + println!( "{:#?}", bencode::dyn_from_bytes::(data.as_slice()).unwrap() ); } - #[tokio::test] - async fn it_works() { - debug_hex_bencode("req: find_node", b"64313a6164323a696432303abd7b477cfbcd10f30b705da20201e7101d8df155363a74617267657432303abd7b477cfbcd10f30b705da20201e7101d8df15565313a71393a66696e645f6e6f6465313a74323a0005313a79313a7165"); - debug_hex_bencode("req: get_peers", b"64313a6164323a696432303abd7b477cfbcd10f30b705da20201e7101d8df155393a696e666f5f6861736832303acab507494d02ebb1178b38f2e9d7be299c86b86265313a71393a6765745f7065657273313a74323a0006313a79313a7165"); - debug_hex_bencode("resp from the requesting node", b"64313a7264323a696432303a3c00727348b3b8ed70baa1e1411b3869d8481321353a6e6f6465733230383a67a312defb7d429086bfdcd5a209684ee13f59615cbe360bc8d567a312defb7d429086bfdcd5a209684ee13f59615cbe360bc8d567a312defb7d429086bfdcd5a209684ee13f59615cbe360bc8d567a312defb7d429086bfdcd5a209684ee13f59615cbe360bc8d567a312defb7d429086bfdcd5a209684ee13f59615cbe360bc8d567a312defb7d429086bfdcd5a209684ee13f59615cbe360bc8d567a312defb7d429086bfdcd5a209684ee13f59615cbe360bc8d567a312defb7d429086bfdcd5a209684ee13f59615cbe360bc8d565313a74323a0005313a76343a4a420000313a79313a7265"); - debug_hex_bencode("resp from some random IP", b"64323a6970363a081ab440e935313a7264323a696432303a32f54e697351ff4aec29cdbaabf2fbe3467cc267353a6e6f6465733431363a54133f7f6d77567ff210fe88d49839107d1a955956aaa625e9ee438e4a0af6b324d9672886052c856b26b25835a689afbbdf5436b643eb20605e1d18f848b32cd275a117afb52d3a474d18541ae18dd20d3fbd936983af4ea87135d785d0661de2f4c4bf7925c59269105c05caa68658851c018d8890f73604e334afdfb8e556fd7ca8f3e0211bd2af91c4af4eee69415a273c0bd1c2b02e8b9ba827139b6c6ebc6dcb6ee53aac3c5147530a432e1b62c9116e1316e9364d7fd2f10f2499f47e862d847937e39a51aed74bb6e8f1c491d520868f1893aaa007d1af19b5328f1b4840759e5743aa59a6bf090c76b846145c6895303b7a49be387fd609a9212eb6541b1ae1fd2ddcf776b4688dd359c8157120809ac8b6651e5e6e8d58b4a80fa124e1f4ed536d61e4ee25d5a702fc8ab70cdf45852708c999215cc406c4caa862bcd0a6b88e58128d2b280ac74631b3591ae1fa4484a5560c31de4fc046b97b4c6ac31dc324ab2ef20952049bfcecdbc8cf79e4cfd378a89779c605559b79b8ae25ba326249e5629f7b9cc0ad33143832e1bca63da63cdb8a940117f0adc2c41965313a74323a0002313a79313a7265"); - debug_hex_bencode("another resp from some random IP", b"64323a6970363a081ab440e935313a7264323a696432303a32f54e697351ff4aec29cdbaabf2fbe3467cc267353a6e6f6465733431363a26d4302a32aecf28f3fee9f6caf8867d762e28b963b5a531c4917373b33fb43c9d7c0d3daf45ee22ab947d4511c054364d4a904464878fc4a31e88b41d7ea953f7dc91d8017dafee5d0f8a4d2fa19fd3ec1c37c6807cad0a5601698909e7a487532fb9408928afaa7ca5e376bee87c4caafa88f2f9a9cc2ed992cd48be68771b48bb6efc225561c00dc3f40d04ab08d93c21a1b89097bd06fa4d1d122d6f1d86e041a5525a69b26d265d039cd52c8bebc923bf1bc3e9f71c7ed05e349d54465cca22233147f21d4c1cc531e461254249ea653909abe367bc25efab70bbe28cd38cbafc2e6db11df5d66bc20bc8a4c9490d84bf29f09ceb44c230dd2ced8b5cec47c71ae1ff66e9ed230e165873b0bef32163ad52c66edce28a7c9c8ae8647af27ba1eac73737ac167e21ed9116b1ef8104a7c28f89606be6f36d7584b791128793e8f8a0e6b48897a6463532547e400ef3a7067237d4d77bf40f1c09773ea85dd269adf35eeebca89b6993cdb116c0512abc2cbc74973d5e5f09940d0bbdf4e047ce15101ae13d794b1230188404a9fd2a5a10ccefb0622057bc6d7eeae5fb8565313a74323a0003313a79313a7265"); - debug_hex_bencode("req to another node", b"64313a6164323a696432303abd7b477cfbcd10f30b705da20201e7101d8df155393a696e666f5f6861736832303acab507494d02ebb1178b38f2e9d7be299c86b86265313a71393a6765745f7065657273313a74323a0007313a79313a7165"); + #[test] + fn deserialize_request_find_node() { + let req = hex::decode(FIND_NODE_REQUEST).unwrap(); + dbg!(bprotocol::deserialize_message::(&req).unwrap()); + } + + #[test] + fn deserialize_request_get_peers() { + let req = hex::decode(GET_PEERS_REQUEST).unwrap(); + dbg!(bprotocol::deserialize_message::(&req).unwrap()); + } + + #[test] + fn deserialize_response_find_node() { + let req = hex::decode(FIND_NODE_RESPONSE).unwrap(); + dbg!(bprotocol::deserialize_message::(&req).unwrap()); + } + + #[test] + fn deserialize_response_find_node_2() { + let req = hex::decode(FIND_NODE_RESPONSE_2).unwrap(); + dbg!(bprotocol::deserialize_message::(&req).unwrap()); + } + + #[test] + fn deserialize_response_find_node_3() { + let req = hex::decode(FIND_NODE_RESPONSE_3).unwrap(); + dbg!(bprotocol::deserialize_message::(&req).unwrap()); + } + + #[test] + fn deserialize_request_what_is_that() { + let req = hex::decode(WHAT_IS_THAT).unwrap(); + dbg!(bprotocol::deserialize_message::(&req).unwrap()); + } + + #[test] + fn deserialize_bencode_packets_captured_from_wireshark() { + debug_hex_bencode("req: find_node", FIND_NODE_REQUEST); + debug_hex_bencode("req: get_peers", GET_PEERS_REQUEST); + debug_hex_bencode("resp from the requesting node", FIND_NODE_RESPONSE); + debug_hex_bencode("resp from some random IP", FIND_NODE_RESPONSE_2); + debug_hex_bencode("another resp from some random IP", FIND_NODE_RESPONSE_3); + debug_hex_bencode("req to another node", WHAT_IS_THAT); } }