use std::net; use byteorder::NetworkEndian; use serde::{Deserialize, Serialize}; use crate::crypto; use crate::git; use crate::identity::Id; use crate::protocol::wire; use crate::protocol::{Context, NodeId, Timestamp, PROTOCOL_VERSION}; use crate::storage; use crate::storage::refs::SignedRefs; /// Message envelope. All messages sent over the network are wrapped in this type. #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] pub struct Envelope { /// Network magic constant. Used to differentiate networks. pub magic: u32, /// The message payload. pub msg: Message, } /// Advertized node feature. Signals what services the node supports. pub type NodeFeatures = [u8; 32]; #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] // TODO: We should check the length and charset when deserializing. pub struct Hostname(String); /// Peer public protocol address. #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] pub enum Address { Ip { ip: net::IpAddr, port: u16, }, Hostname { host: Hostname, port: u16, }, /// Tor V3 onion address. Onion { key: crypto::PublicKey, port: u16, checksum: u16, version: u8, }, } impl wire::Encode for Envelope { fn encode(&self, writer: &mut W) -> Result { let mut n = 0; n += self.magic.encode(writer)?; n += self.msg.encode(writer)?; Ok(n) } } impl wire::Decode for Envelope { fn decode(reader: &mut R) -> Result { let magic = u32::decode(reader)?; let msg = Message::decode(reader)?; Ok(Self { magic, msg }) } } impl wire::Encode for Address { fn encode(&self, writer: &mut W) -> Result { let mut n = 0; match self { Self::Ip { ip, port } => { match ip { net::IpAddr::V4(addr) => { n += 1u8.encode(writer)?; n += addr.octets().encode(writer)?; } net::IpAddr::V6(addr) => { n += 2u8.encode(writer)?; n += addr.octets().encode(writer)?; } } n += port.encode(writer)?; } Self::Hostname { .. } => todo!(), Self::Onion { .. } => todo!(), } Ok(n) } } impl wire::Decode for Address { fn decode(reader: &mut R) -> Result { use byteorder::ReadBytesExt; match reader.read_u8()? { 1 => { let octets: [u8; 4] = wire::Decode::decode(reader)?; let ip = net::IpAddr::from(net::Ipv4Addr::from(octets)); let port = u16::decode(reader)?; Ok(Self::Ip { ip, port }) } 2 => { let octets: [u8; 16] = wire::Decode::decode(reader)?; let ip = net::IpAddr::from(net::Ipv6Addr::from(octets)); let port = u16::decode(reader)?; Ok(Self::Ip { ip, port }) } _ => { todo!(); } } } } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] pub struct NodeAnnouncement { /// Node identifier. id: NodeId, /// Advertized features. features: NodeFeatures, /// Monotonic timestamp. timestamp: Timestamp, /// Non-unique alias. Must be valid UTF-8. alias: [u8; 32], /// Announced addresses. addresses: Vec
, } impl NodeAnnouncement { /// Verify a signature on this message. pub fn verify(&self, signature: &crypto::Signature) -> bool { // TODO: Use binary serialization. let msg = serde_json::to_vec(self).unwrap(); self.id.verify(signature, &msg).is_ok() } } /// Message payload. /// These are the messages peers send to each other. #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] pub enum Message { /// Say hello to a peer. This is the first message sent to a peer after connection. Hello { // TODO: This is currently untrusted. id: NodeId, timestamp: Timestamp, version: u32, addrs: Vec
, git: git::Url, }, Node { /// Signature over the announcement, by the node being announced. signature: crypto::Signature, /// Unsigned node announcement. announcement: NodeAnnouncement, }, /// Get a peer's inventory. GetInventory { ids: Vec }, /// Send our inventory to a peer. Sent in response to [`Message::GetInventory`]. /// Nb. This should be the whole inventory, not a partial update. Inventory { node: NodeId, inv: Vec, timestamp: Timestamp, }, /// Project refs were updated. Includes the signature of the user who updated /// their view of the project. RefsUpdate { /// Project under which the refs were updated. id: Id, /// Signing key. signer: crypto::PublicKey, /// Updated refs. refs: SignedRefs, }, } impl Message { pub fn hello(id: NodeId, timestamp: Timestamp, addrs: Vec
, git: git::Url) -> Self { Self::Hello { id, timestamp, version: PROTOCOL_VERSION, addrs, git, } } pub fn node(announcement: NodeAnnouncement, signer: S) -> Self { let msg = serde_json::to_vec(&announcement).unwrap(); let signature = signer.sign(&msg); Self::Node { signature, announcement, } } pub fn inventory(ctx: &Context) -> Result where T: storage::ReadStorage, G: crypto::Signer, { let timestamp = ctx.timestamp(); let inv = ctx.storage.inventory()?; Ok(Self::Inventory { node: ctx.id(), inv, timestamp, }) } pub fn get_inventory(ids: impl Into>) -> Self { Self::GetInventory { ids: ids.into() } } pub fn type_id(&self) -> u16 { match self { Self::Hello { .. } => 0, Self::Node { .. } => 2, Self::GetInventory { .. } => 4, Self::Inventory { .. } => 6, Self::RefsUpdate { .. } => 8, } } } impl wire::Encode for Message { fn encode(&self, writer: &mut W) -> Result { let mut n = self.type_id().encode(writer)?; match self { Self::Hello { id, timestamp, version, addrs, git, } => { n += id.encode(writer)?; n += timestamp.encode(writer)?; n += version.encode(writer)?; n += addrs.as_slice().encode(writer)?; n += git.encode(writer)?; } Self::RefsUpdate { id, signer, refs } => { n += id.encode(writer)?; n += signer.encode(writer)?; n += refs.encode(writer)?; } Self::GetInventory { ids } => { n += ids.as_slice().encode(writer)?; } Self::Inventory { node, inv, timestamp, } => { n += node.encode(writer)?; n += inv.as_slice().encode(writer)?; n += timestamp.encode(writer)?; } Self::Node { .. } => { todo!(); } } Ok(n) } } impl wire::Decode for Message { fn decode(reader: &mut R) -> Result { use byteorder::ReadBytesExt; let type_id = reader.read_u16::()?; match type_id { 0 => { let id = NodeId::decode(reader)?; let timestamp = Timestamp::decode(reader)?; let version = u32::decode(reader)?; let addrs = Vec::
::decode(reader)?; let git = git::Url::decode(reader)?; Ok(Self::Hello { id, timestamp, version, addrs, git, }) } 2 => { todo!(); } 4 => { let ids = Vec::::decode(reader)?; Ok(Self::GetInventory { ids }) } 6 => { let node = NodeId::decode(reader)?; let inv = Vec::::decode(reader)?; let timestamp = Timestamp::decode(reader)?; Ok(Self::Inventory { node, inv, timestamp, }) } 8 => { let id = Id::decode(reader)?; let signer = crypto::PublicKey::decode(reader)?; let refs = SignedRefs::decode(reader)?; Ok(Self::RefsUpdate { id, signer, refs }) } n => { todo!("Mesage type {} is not yet implemented", n); } } } } #[cfg(test)] mod tests { use super::*; use quickcheck_macros::quickcheck; use crate::decoder::Decoder; use crate::protocol::wire::{self, Encode}; #[quickcheck] fn prop_message_encode_decode(message: Message) { assert_eq!( wire::deserialize::(&wire::serialize(&message)).unwrap(), message ); } #[quickcheck] fn prop_envelope_encode_decode(envelope: Envelope) { assert_eq!( wire::deserialize::(&wire::serialize(&envelope)).unwrap(), envelope ); } #[test] fn prop_envelope_decoder() { fn property(items: Vec) { let mut decoder = Decoder::::new(8); for item in &items { item.encode(&mut decoder).unwrap(); } for item in items { assert_eq!(decoder.next().unwrap().unwrap(), item); } } quickcheck::QuickCheck::new() .gen(quickcheck::Gen::new(16)) .quickcheck(property as fn(items: Vec)); } #[quickcheck] fn prop_addr(addr: Address) { assert_eq!( wire::deserialize::
(&wire::serialize(&addr)).unwrap(), addr ); } }