diff --git a/Cargo.toml b/Cargo.toml index 672a4ddf..55c4f478 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,8 @@ serde = { version = "1.0", features = ["derive"] } bincode = "1.1.3" rust_sodium = "0.10.2" openssl = { version = "0.10", features = ["vendored"] } +lazy_static = "1.2.0" +log = "0.4" [dev-dependencies] criterion = "0.2.11" diff --git a/hooks/pre-commit b/hooks/pre-commit index 1768075b..ec5b30dd 100755 --- a/hooks/pre-commit +++ b/hooks/pre-commit @@ -13,17 +13,18 @@ SUCCESS="${GREEN}ok${RC}" # Check if rustfmt is installed printf "${PREFIX} Checking for rustfmt ... " -command -v cargo fmt &>/dev/null -if [[ $? == 0 ]]; then +OUT=$(cargo +stable fmt --version 2>&1 > /dev/null ) # Save the output of the command to OUT for later printing +if [[ $? -eq 0 ]]; then printf "${SUCCESS}\n" else printf "${FAILURE}\n" + printf "${OUT}\n" exit 1 fi # Check rustfmt against the git tree printf "${PREFIX} Checking formatting ... " -command cargo fmt -- --check > /dev/null +command cargo +stable fmt -- --check > /dev/null if [[ $? == 0 ]]; then printf "${SUCCESS}\n" exit 0 diff --git a/src/community/mod.rs b/src/community/mod.rs new file mode 100644 index 00000000..4ab95106 --- /dev/null +++ b/src/community/mod.rs @@ -0,0 +1,209 @@ +use crate::serialization::{Packet, PacketDeserializer}; +use crate::serialization::header::Header; +use std::error::Error; +use std::collections::HashMap; +use crate::networking::address::Address; +use std::fmt; + +pub mod peer; + +create_error!( + HeaderUnwrapError, + "The community experienced an error trying to deserialize the header of a packet" +); +create_error!(MidError, "Failed to get the mid"); +create_error!( + UnknownCommunityError, + "No community with matching mid found" +); +create_error!(InsertionError, "Error inserting community into hashmap"); + +/// Example Community +/// +/// _**Note:** Try to avoid the use of .unwrap() in actual production code, this is just an example_ +/// ``` +/// use ipv8::community::peer::Peer; +/// use ipv8::community::Community; +/// use ipv8::serialization::header::Header; +/// use ipv8::serialization::{PacketDeserializer, Packet}; +/// use std::net::Ipv4Addr; +/// use ipv8::networking::address::Address; +/// use std::error::Error; +/// use ipv8::IPv8; +/// use ipv8::configuration::Config; +/// use ipv8::serialization::header::HeaderVersion::PyIPV8Header; +/// use ipv8::crypto::keytypes::PublicKey; +/// +/// pub struct TestCommunity{ +/// peer: Peer +/// } +/// +/// impl TestCommunity{ +/// fn new() -> Option { +/// +/// // Use the highest available key +/// let pk: PublicKey = PublicKey::from_vec(vec![ +/// 48, 129, 167, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 39, 3, +/// 129, 146, 0, 4, 2, 86, 251, 75, 206, 159, 133, 120, 63, 176, 235, 178, 14, 8, 197, 59, +/// 107, 51, 179, 139, 3, 155, 20, 194, 112, 113, 15, 40, 67, 115, 37, 223, 152, 7, 102, +/// 154, 214, 90, 110, 180, 226, 5, 190, 99, 163, 54, 116, 173, 121, 40, 80, 129, 142, 82, +/// 118, 154, 96, 127, 164, 248, 217, 91, 13, 80, 91, 94, 210, 16, 110, 108, 41, 57, 4, +/// 243, 49, 52, 194, 254, 130, 98, 229, 50, 84, 21, 206, 134, 223, 157, 189, 133, 50, 210, +/// 181, 93, 229, 32, 179, 228, 179, 132, 143, 147, 96, 207, 68, 48, 184, 160, 47, 227, 70, +/// 147, 23, 159, 213, 105, 134, 60, 211, 226, 8, 235, 186, 20, 241, 85, 170, 4, 3, 40, +/// 183, 98, 103, 80, 164, 128, 87, 205, 101, 67, 254, 83, 142, 133, +/// ])?; +/// +/// // Actually create the community +/// Some(TestCommunity { +/// peer: Peer::new( +/// pk, +/// Address{ +/// address: Ipv4Addr::new(0,0,0,0), +/// port: 0 +/// }, +/// true, +/// ) +/// }) +/// } +/// } +/// +/// impl Community for TestCommunity{ +/// +/// // Returns the hash of our master peer +/// fn get_mid(&self) -> Option> { +/// Some(self.peer.get_sha1()?.to_vec()) +/// } +/// +/// // The function which will be called when the community receives a packet +/// fn on_receive(&self, header: Header, deserializer: PacketDeserializer, address: Address) -> Result<(),Box>{ +/// # assert_eq!(header.mid_hash, self.get_mid()); +/// # assert_eq!(header.version, PyIPV8Header); +/// # assert_eq!(header.message_type, Some(42)); +/// // Do some stuff here like to distribute the message based on it's message_type (in the header) +/// Ok(()) +/// } +/// } +/// +/// let mut config = Config::default(); +/// let community = TestCommunity::new().unwrap(); +/// let mid = community.get_mid(); +/// config.communities.add_community(Box::new(community)); +/// let ipv8 = IPv8::new(config); +/// +/// // now simulate a packet coming in +/// +/// // Create a packet to test the community with +/// let packet = Packet::new(Header{ +/// size: 23, +/// version: PyIPV8Header, +/// mid_hash: mid, +/// message_type: Some(42), +/// }).unwrap(); +/// +/// // Normally you would want to sign the packet here +/// +/// // Yeet the packet +/// ipv8.config.communities.forward_message(packet,Address{ +/// address: Ipv4Addr::new(42,42,42,42), +/// port: 42, +/// }); +/// +/// ``` +pub trait Community { + // fn new() -> Option + // where + // Self: Sized; + + /// Returns the hash of our master peer public key + fn get_mid(&self) -> Option>; + + /// Gets called whenever a packet is received directed at this community + /// DO NOT OVERRIDE + fn receive( + &self, + header: Header, + deserializer: PacketDeserializer, + address: Address, + ) -> Result<(), Box> { + // DO NOT OVERRIDE + //! used to pre-decode the header and filter out messages + + fn warn_deprecated(message: &str, address: Address) -> Result<(), Box> { + warn!( + "Received deprecated message {} from ({:?})", + message, address + ); + Ok(()) + } + match header.message_type.ok_or(HeaderUnwrapError)? { + 255 => warn_deprecated("reserved-255", address), + 254 => warn_deprecated("on-missing-sequence", address), + 253 => warn_deprecated("missing-proof", address), + 252 => warn_deprecated("signature-request", address), + 251 => warn_deprecated("signature-response", address), + 248 => warn_deprecated("on-identity", address), + 247 => warn_deprecated("on-missing-identity", address), + 244 => warn_deprecated("destroy-community", address), + 243 => warn_deprecated("authorize", address), + 242 => warn_deprecated("revoke", address), + 241 => warn_deprecated("subjective-set", address), + 240 => warn_deprecated("missing-subjective-set", address), + 239 => warn_deprecated("on-missing-message", address), + 238 => warn_deprecated("undo-own", address), + 237 => warn_deprecated("undo-other", address), + 236 => warn_deprecated("dynamic-settings", address), + 235 => warn_deprecated("missing-last-message", address), + _ => self.on_receive(header, deserializer, address), + } + } + + fn on_receive( + &self, + header: Header, + deserializer: PacketDeserializer, + address: Address, + ) -> Result<(), Box>; +} + +pub struct CommunityRegistry { + // mid, community + communities: HashMap, Box>, +} + +impl CommunityRegistry { + pub fn add_community(&mut self, item: Box) -> Result<(), Box> { + self.communities + .insert(item.get_mid().ok_or(MidError)?, item) + .ok_or(InsertionError)?; + Ok(()) + } + + /// Forwards the message to the corresponding community + pub fn forward_message(&self, packet: Packet, address: Address) -> Result<(), Box> { + // deserialize the header + let deserializer = packet.start_deserialize(); + + // We use peek here instead of get, even though we give the header along with the receive call + // this is because at this point, the header is not verified yet so we still assume the message is valid. + // We can't verify the header here yet as not all messages have a signature. Communities will have to decide + // on their own if they want to verify the header. We do give it along as only having to deserialize the header once + // makes it slightly more efficient. + let header = deserializer.peek_header()?; + + // get the mid from the header and use it for a hashtable lookup + let mid = header.mid_hash.as_ref().ok_or(MidError)?; + let community = self.communities.get(mid).ok_or(UnknownCommunityError)?; + + // Actually forward it + community.receive(header, deserializer, address) + } +} + +impl Default for CommunityRegistry { + fn default() -> Self { + Self { + communities: HashMap::new(), + } + } +} diff --git a/src/community/peer.rs b/src/community/peer.rs new file mode 100644 index 00000000..583e69d0 --- /dev/null +++ b/src/community/peer.rs @@ -0,0 +1,22 @@ +use crate::crypto::keytypes::PublicKey; +use crate::networking::address::Address; + +pub struct Peer { + key: PublicKey, + address: Address, + intro: bool, +} + +impl Peer { + pub fn new(key: PublicKey, address: Address, intro: bool) -> Self { + Self { + key, + address, + intro, + } + } + + pub fn get_sha1(&self) -> Option<[u8; 20]> { + self.key.sha1() + } +} diff --git a/src/configuration.rs b/src/configuration.rs index c9f56a48..dc7a0e77 100644 --- a/src/configuration.rs +++ b/src/configuration.rs @@ -1,5 +1,6 @@ use crate::networking::address::Address; use std::net::Ipv4Addr; +use crate::community::CommunityRegistry; pub struct Config { /// Default list of host used for peer discovery @@ -7,6 +8,9 @@ pub struct Config { /// from py-ipv8 configuration. UDP socket address. /// There split up in "address" and "port" pub socketaddress: Address, + + /// The registry containing all the communities + pub communities: CommunityRegistry, } impl Default for Config { @@ -17,6 +21,8 @@ impl Default for Config { port: 8090, }, + communities: CommunityRegistry::default(), + default_hosts: vec![ // Dispersy Address { diff --git a/src/crypto/keytypes.rs b/src/crypto/keytypes.rs index 0fb8b772..a3e95301 100644 --- a/src/crypto/keytypes.rs +++ b/src/crypto/keytypes.rs @@ -16,9 +16,11 @@ //! | HIGH_SIGNATURE_LENGTH | 144 | 153 | 9 | 144 | //! | ED25519_SIGNATURE_LENGTH | 64 | 64 | 0 | 64 | +use std::fmt; + use openssl; +use openssl::sha::sha1; use rust_sodium::crypto::sign::ed25519; -use std::fmt; // TODO: when ed25519 becomes available for rust OpenSSL, rust_sodium will be removed. @@ -129,6 +131,10 @@ impl PublicKey { Some(m) } } + + pub fn sha1(&self) -> Option<[u8; 20]> { + Some(sha1(&self.to_vec()?)) + } } impl PartialEq for PublicKey { @@ -350,4 +356,25 @@ mod tests { keyvec ); } + + #[test] + fn test_sha1() { + let keyvec = vec![ + 48, 64, 48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 5, 43, 129, 4, 0, 1, 3, 44, 0, 4, + 0, 80, 239, 172, 104, 165, 76, 172, 6, 229, 136, 156, 105, 23, 249, 46, 30, 148, 87, + 105, 57, 6, 105, 134, 2, 229, 115, 169, 44, 162, 41, 190, 228, 56, 20, 100, 64, 79, + 167, 224, 118, 14, + ]; + + let key = PublicKey::from_vec(keyvec.clone()).unwrap(); + let sha1sum = key.sha1().unwrap(); + + assert_eq!( + [ + 254, 127, 138, 49, 67, 126, 206, 58, 169, 85, 132, 14, 211, 247, 13, 170, 244, 166, + 152, 180, + ], + sha1sum + ) + } } diff --git a/src/lib.rs b/src/lib.rs index 479d1463..52882b82 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,10 @@ +#[macro_use] +extern crate log; + pub mod error; pub mod serialization; +pub mod community; pub mod configuration; pub mod crypto; pub mod event; @@ -17,10 +21,10 @@ use configuration::Config; /// use ipv8::IPv8; /// use ipv8::configuration::Config; /// -/// let ipv8_instance = IPv8::new(Config::default()); +/// let ipv8 = IPv8::new(Config::default()); /// ``` pub struct IPv8 { - config: Config, + pub config: Config, } impl IPv8 { diff --git a/src/serialization/mod.rs b/src/serialization/mod.rs index 8e20ed62..634ea851 100644 --- a/src/serialization/mod.rs +++ b/src/serialization/mod.rs @@ -22,13 +22,13 @@ create_error!(HeaderError, "The supplied header was invalid"); pub struct Packet(pub Vec); #[derive(Debug, PartialEq)] -pub struct PacketIterator { +pub struct PacketDeserializer { pub pntr: Packet, pub index: usize, } /// iterates over a packet to extract it's possibly multiple payloads -impl PacketIterator { +impl PacketDeserializer { /// Deserializes a stream of bytes into an ipv8 payload. Which payload is inferred by the type of T which is generic. /// T has to be deserializable and implement the Ipv8Payload trait. pub fn next_payload(&mut self) -> Result> @@ -57,19 +57,21 @@ impl PacketIterator { Ok(res) } - pub fn get_header(&mut self) -> Result> { + pub fn peek_header(&self) -> Result> { let res: Header = bincode::config() .big_endian() .deserialize(&self.pntr.0[self.index..])?; + Ok(res) + } + + pub fn pop_header(&mut self) -> Result> { + let res = self.peek_header()?; self.index += res.size; Ok(res) } pub fn skip_header(mut self) -> Result> { - let res: Header = bincode::config() - .big_endian() - .deserialize(&self.pntr.0[self.index..])?; - self.index += res.size; + self.pop_header()?; Ok(self) } @@ -135,8 +137,8 @@ impl Packet { } /// Deserializes a stream of bytes into ipv8 payloads. - pub fn start_deserialize(self) -> PacketIterator { - PacketIterator { + pub fn start_deserialize(self) -> PacketDeserializer { + PacketDeserializer { pntr: self, index: 0, } @@ -221,6 +223,16 @@ mod tests { // }); // } + #[test] + fn test_peek_header() { + let packet = Packet::new(create_test_header!()).unwrap(); + let deserializer = packet.start_deserialize(); + let header1 = deserializer.peek_header().unwrap(); + let header2 = deserializer.peek_header().unwrap(); + + assert_eq!(header1, header2); + } + #[test] fn test_sign_verify_verylow() { let a = TestPayload1 { test: 42 }; diff --git a/tests/testpyipv8packets.rs b/tests/testpyipv8packets.rs index b306619c..caa42c9b 100644 --- a/tests/testpyipv8packets.rs +++ b/tests/testpyipv8packets.rs @@ -32,7 +32,7 @@ fn test_packet_1() { ]); let mut deserializer = data.start_deserialize(); - let header: Header = deserializer.get_header().unwrap(); + let header: Header = deserializer.pop_header().unwrap(); assert_eq!( header, Header::py_ipv8_header( @@ -102,7 +102,7 @@ fn test_packet_2() { ]); let mut deserializer = data.start_deserialize(); - let header: Header = deserializer.get_header().unwrap(); + let header: Header = deserializer.pop_header().unwrap(); assert_eq!( header, Header::py_ipv8_header( @@ -156,7 +156,7 @@ fn test_packet_3() { ]); let mut deserializer = data.start_deserialize(); - let header: Header = deserializer.get_header().unwrap(); + let header: Header = deserializer.pop_header().unwrap(); assert_eq!( header, Header::py_ipv8_header( @@ -211,7 +211,7 @@ fn test_packet_4() { ]); let mut deserializer = data.start_deserialize(); - let header: Header = deserializer.get_header().unwrap(); + let header: Header = deserializer.pop_header().unwrap(); assert_eq!( header, Header::py_ipv8_header( @@ -266,7 +266,7 @@ fn test_packet_5() { ]); let mut deserializer = data.start_deserialize(); - let header: Header = deserializer.get_header().unwrap(); + let header: Header = deserializer.pop_header().unwrap(); assert_eq!( header, Header::py_ipv8_header( @@ -321,7 +321,7 @@ fn test_packet_6() { ]); let mut deserializer = data.start_deserialize(); - let header: Header = deserializer.get_header().unwrap(); + let header: Header = deserializer.pop_header().unwrap(); assert_eq!( header, Header::py_ipv8_header( @@ -376,7 +376,7 @@ fn test_packet_7() { ]); let mut deserializer = data.start_deserialize(); - let header: Header = deserializer.get_header().unwrap(); + let header: Header = deserializer.pop_header().unwrap(); assert_eq!( header, Header::py_ipv8_header( @@ -431,7 +431,7 @@ fn test_packet_8() { 0xc1, 0xb3, 0x53, 0x92, 0x1f, 0x58, 0xb6, 0x81, 0x0d, ]); let mut deserializer = data.start_deserialize(); - let header: Header = deserializer.get_header().unwrap(); + let header: Header = deserializer.pop_header().unwrap(); assert_eq!( header, @@ -503,7 +503,7 @@ fn test_packet_9() { 0xe7, 0x6f, 0xea, 0x90, 0x43, 0xfd, 0x5f, 0x93, 0x04, ]); let mut deserializer = data.start_deserialize(); - let header: Header = deserializer.get_header().unwrap(); + let header: Header = deserializer.pop_header().unwrap(); assert_eq!( header, @@ -581,7 +581,7 @@ fn test_packet_10() { 0xa2, ]); let mut deserializer = data.start_deserialize(); - let header: Header = deserializer.get_header().unwrap(); + let header: Header = deserializer.pop_header().unwrap(); assert!(deserializer.verify()); @@ -652,7 +652,7 @@ fn test_packet_11() { 0xa3, ]); let mut deserializer = data.start_deserialize(); - let header: Header = deserializer.get_header().unwrap(); + let header: Header = deserializer.pop_header().unwrap(); assert!(deserializer.verify()); @@ -723,7 +723,7 @@ fn test_packet_12() { 0xb8, ]); let mut deserializer = data.start_deserialize(); - let header: Header = deserializer.get_header().unwrap(); + let header: Header = deserializer.pop_header().unwrap(); assert!(deserializer.verify()); @@ -793,7 +793,7 @@ fn test_packet_13() { 0x56, ]); let mut deserializer = data.start_deserialize(); - let header: Header = deserializer.get_header().unwrap(); + let header: Header = deserializer.pop_header().unwrap(); assert!(deserializer.verify()); @@ -863,7 +863,7 @@ fn test_packet_14() { 0xd8, ]); let mut deserializer = data.start_deserialize(); - let header: Header = deserializer.get_header().unwrap(); + let header: Header = deserializer.pop_header().unwrap(); assert!(deserializer.verify()); @@ -928,7 +928,7 @@ fn test_packet_15() { 0x0b, ]); let mut deserializer = data.start_deserialize(); - let header: Header = deserializer.get_header().unwrap(); + let header: Header = deserializer.pop_header().unwrap(); assert!(deserializer.verify());