1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
//! In this module, all communities are defined. Currently none as
//! communities are still handled by py-ipv8 but the tools to move them soon
//! are already in place.

use crate::serialization::{Packet, PacketDeserializer};
use crate::serialization::header::Header;
use std::error::Error;
use std::collections::HashMap;
use crate::networking::address::Address;
use crate::networking::NetworkSender;

#[cfg(test)]
use std::sync::atomic::AtomicUsize;

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"
);

#[cfg(test)]
static WARN_DEPRECATED_CALLS: AtomicUsize = AtomicUsize::new(0);

/// # Community struct
/// This is the main struct defining a community
///
/// ## Example Community
/// This is an example of how to create a community
///
/// _**Note:** Try to avoid the use of .unwrap() in actual production code, this is just an example_
///
pub trait Community {
    /// Every community should have a constructor.
    /// It will receive an endpoint from the [NetworkSender](crate::networking::NetworkSender) which constructs
    /// the community. An endpoint is used to send messages over the network to other
    /// communities.
    fn new(endpoint: &NetworkSender) -> Result<Self, Box<dyn Error>>
    where
        Self: Sized;

    /// Returns a unique (currently 20 byte) sequence identifying a community.
    ///
    /// This is used to be the SHA1 hash of its public key. You are free to choose whatever.
    ///
    /// As OpenSSL keys are deprecated, this library provides no way of calculating the sha1 of an OpenSSL key.
    /// Master peer keys still can be OpenSSL keys. This SHA1 has to be hardcoded for communities that are
    /// compatible with old communities. New communities recommended to use ED25519 in the future which
    /// sha1 hashes can be calculated.
    ///
    /// The sha1 of a key does not serve any purpose besides uniquely identifying communities and as such can be any
    /// unique 20 byte sequence.
    fn get_mid(&self) -> Vec<u8>;

    /// Gets called whenever a packet is received directed at this community
    /// DO NOT OVERRIDE
    #[doc(hidden)]
    fn receive(
        &self,
        header: Header,
        deserializer: PacketDeserializer,
        address: Address,
    ) -> Result<(), Box<dyn Error>> {
        // DO NOT OVERRIDE
        //! used to pre-decode the header and filter out messages
        //!

        // Used in on_receive. Has to be outside to be testable.
        #[doc(hidden)]
        fn warn_deprecated(message: &str, address: Address) -> Result<(), Box<dyn Error>> {
            warn!(
                "Received deprecated message {} from ({:?})",
                message, address
            );

            #[cfg(test)]
            {
                WARN_DEPRECATED_CALLS.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
            }

            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),
        }
    }

    /// This method called for every incoming message, directed at this community, which is not captured.
    ///
    /// Messages are captured whenever they have a reserved message_type (235 ~ 255). These are used for legacy support
    /// and some default responses which every community should give.
    fn on_receive(
        &self,
        header: Header,
        deserializer: PacketDeserializer,
        address: Address,
    ) -> Result<(), Box<dyn Error>>;
}

/// Every different kind of community is registered here with it's MID.
///
/// So that incoming messages can be distributed to the right communities. Makes use of a hashmap to achieve
/// O(1) lookup time.
pub struct CommunityRegistry {
    // mid, community
    #[cfg(test)]
    pub communities: HashMap<Vec<u8>, Box<dyn Community>>,
    #[cfg(not(test))]
    /// A HashMap of all the communities so we can know who to send what packet
    communities: HashMap<Vec<u8>, Box<dyn Community>>,
}

impl CommunityRegistry {
    /// Adds a community to the registry.
    pub fn add_community(&mut self, item: Box<dyn Community>) -> Result<(), Box<dyn Error>> {
        match self.communities.insert(item.get_mid(), item) {
            // none means the key wasn't already present in the map, some means it was and it returns it.
            // We don't care about this.
            _ => Ok(()),
        }
    }

    /// Forwards the message to the corresponding community
    pub fn forward_message(&self, packet: Packet, address: Address) -> Result<(), Box<dyn Error>> {
        // 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 {
    /// Returns a new community registry with all the built-in communities already registered.
    /// All custom communities can be added with the [add_community](#method.add_community) method.
    fn default() -> Self {
        Self {
            communities: HashMap::new(),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::networking::address::Address;
    use std::net::{SocketAddr, IpAddr};
    use std::error::Error;
    use crate::networking::NetworkSender;
    use crate::community::peer::Peer;
    use crate::community::{Community, CommunityRegistry};
    use crate::serialization::header::Header;
    use crate::serialization::{PacketDeserializer, Packet};
    use std::net::Ipv4Addr;
    use crate::IPv8;
    use crate::configuration::Config;
    use crate::serialization::header::HeaderVersion::PyIPV8Header;
    use std::sync::atomic::Ordering;
    use crate::networking::test_helper::localhost;
    use crate::crypto::signature::KeyPair;

    pub struct TestCommunity {
        peer: Peer,
    }

    impl Community for TestCommunity {
        fn new(_endpoint: &NetworkSender) -> Result<Self, Box<dyn Error>> {
            let pk = KeyPair::from_seed_unchecked(&[
                0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
                23, 24, 25, 26, 27, 28, 29, 30, 31,
            ])
            .unwrap();
            // Actually create the community
            Ok(TestCommunity {
                peer: Peer::new(
                    pk.public_key().unwrap(),
                    Address(SocketAddr::new(
                        IpAddr::V4(Ipv4Addr::new(42, 42, 42, 42)),
                        8000,
                    )),
                    true,
                ),
            })
        }

        // Returns the hash of our master peer
        fn get_mid(&self) -> Vec<u8> {
            self.peer.get_sha1()
        }

        // The function which will be called when the community receives a packet
        fn on_receive(
            &self,
            header: Header,
            _deserializer: PacketDeserializer,
            _address: Address,
        ) -> Result<(), Box<dyn Error>> {
            assert_eq!(header.mid_hash.unwrap(), self.get_mid());
            assert_eq!(header.version, PyIPV8Header);
            assert_eq!(header.message_type, Some(42));
            Ok(())
        }
    }

    #[test]
    fn test_deprecated() {
        let mut config = Config::default();
        config.sending_address = Address(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0));
        config.receiving_address =
            Address(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0));

        let ipv8 = IPv8::new(config).unwrap();

        let community = TestCommunity::new(&ipv8.network_sender).unwrap();
        for i in &[
            255, 254, 253, 252, 251, 248, 247, 244, 243, 242, 241, 240, 239, 238, 237, 236, 235,
        ] {
            let address = Address(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 0));

            let packet = Packet::new(Header {
                size: 23,
                version: PyIPV8Header,
                mid_hash: Some(community.get_mid()),
                message_type: Some(*i),
            })
            .unwrap();
            let deser = packet.start_deserialize();
            let header = deser.peek_header().unwrap();
            community.receive(header, deser, address).unwrap();
        }
        assert_eq!(17, WARN_DEPRECATED_CALLS.load(Ordering::SeqCst))
    }

    #[test]
    fn test_add_community() {
        let config = Config::default();
        let ipv8 = IPv8::new(config).unwrap();
        let community = Box::new(TestCommunity::new(&ipv8.network_sender).unwrap());
        let the_same = Box::new(TestCommunity::new(&ipv8.network_sender).unwrap());
        let mid = &*community.get_mid();
        let mut registry: CommunityRegistry = CommunityRegistry::default();

        registry.add_community(community).unwrap();

        let get = registry.communities.get(mid).unwrap();

        assert_eq!(the_same.get_mid(), get.get_mid()); // TODO: More thorough comparison
    }

    #[test]
    fn test_networking() {
        let mut config = Config::default();
        config.receiving_address = localhost();
        config.sending_address = localhost();
        config.buffersize = 2048;

        let mut ipv8 = IPv8::new(config).unwrap();

        let community = TestCommunity::new(&ipv8.network_sender).unwrap();
        let mid = community.get_mid();

        ipv8.communities.add_community(Box::new(community)).unwrap();

        // 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: Some(mid),
            message_type: Some(42),
        })
        .unwrap();

        // Normally you would want to sign the packet here

        // Send the packet
        ipv8.communities
            .forward_message(
                packet,
                Address(SocketAddr::new(
                    IpAddr::V4(Ipv4Addr::new(42, 42, 42, 42)),
                    42,
                )),
            )
            .unwrap();
    }
}