mod frame; mod message; mod protocol; mod varint; pub use frame::StreamId; pub use message::{AddressType, MessageType}; pub use protocol::{Control, Wire, WireReader, WireSession, WireWriter}; use std::collections::BTreeMap; use std::convert::TryFrom; use std::ops::Deref; use std::str::FromStr; use std::string::FromUtf8Error; use std::{io, mem}; use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt}; use crate::crypto::{PublicKey, Signature, Unverified}; use crate::git; use crate::git::fmt; use crate::identity::RepoId; use crate::node; use crate::node::Alias; use crate::prelude::*; use crate::service::filter; use crate::storage::refs::Refs; use crate::storage::refs::RefsAt; use crate::storage::refs::SignedRefs; /// The default type we use to represent sizes on the wire. /// /// Since wire messages are limited to 64KB by the transport layer, /// two bytes is enough to represent any message. /// /// Note that in certain cases, we may use a smaller type. pub type Size = u16; #[derive(thiserror::Error, Debug)] pub enum Error { #[error("i/o: {0}")] Io(#[from] io::Error), #[error("UTF-8 error: {0}")] FromUtf8(#[from] FromUtf8Error), #[error("invalid size: expected {expected}, got {actual}")] InvalidSize { expected: usize, actual: usize }, #[error("invalid filter size: {0}")] InvalidFilterSize(usize), #[error("invalid channel type {0:x}")] InvalidStreamKind(u8), #[error(transparent)] InvalidRefName(#[from] fmt::Error), #[error(transparent)] InvalidAlias(#[from] node::AliasError), #[error("invalid control message with type `{0}`")] InvalidControlMessage(u8), #[error("invalid protocol version header `{0:x?}`")] InvalidProtocolVersion([u8; 4]), #[error("unknown address type `{0}`")] UnknownAddressType(u8), #[error("unknown message type `{0}`")] UnknownMessageType(u16), #[error("unknown info type `{0}`")] UnknownInfoType(u16), #[error("unexpected bytes")] UnexpectedBytes, } impl Error { /// Whether we've reached the end of file. This will be true when we fail to decode /// a message because there's not enough data in the stream. pub fn is_eof(&self) -> bool { matches!(self, Self::Io(err) if err.kind() == io::ErrorKind::UnexpectedEof) } } /// Things that can be encoded as binary. pub trait Encode { fn encode(&self, writer: &mut W) -> Result; } /// Things that can be decoded from binary. pub trait Decode: Sized { fn decode(reader: &mut R) -> Result; } /// Encode an object into a byte vector. /// /// # Panics /// /// If the encoded object exceeds [`Size::MAX`]. pub fn serialize(data: &T) -> Vec { let mut buffer = Vec::new(); // SAFETY: We expect this to panic if the user passes // in data that exceeds the maximum allowed size. #[allow(clippy::unwrap_used)] let len = data.encode(&mut buffer).unwrap(); debug_assert_eq!(len, buffer.len()); buffer } /// Decode an object from a vector. pub fn deserialize(data: &[u8]) -> Result { let mut cursor = io::Cursor::new(data); let obj = T::decode(&mut cursor)?; if cursor.position() as usize != cursor.get_ref().len() { return Err(Error::UnexpectedBytes); } Ok(obj) } impl Encode for u8 { fn encode(&self, writer: &mut W) -> Result { writer.write_u8(*self)?; Ok(mem::size_of::()) } } impl Encode for u16 { fn encode(&self, writer: &mut W) -> Result { writer.write_u16::(*self)?; Ok(mem::size_of::()) } } impl Encode for u32 { fn encode(&self, writer: &mut W) -> Result { writer.write_u32::(*self)?; Ok(mem::size_of::()) } } impl Encode for u64 { fn encode(&self, writer: &mut W) -> Result { writer.write_u64::(*self)?; Ok(mem::size_of::()) } } impl Encode for PublicKey { fn encode(&self, writer: &mut W) -> Result { self.deref().encode(writer) } } impl Encode for &[u8; T] { fn encode(&self, writer: &mut W) -> Result { writer.write_all(&**self)?; Ok(mem::size_of::()) } } impl Encode for [u8; T] { fn encode(&self, writer: &mut W) -> Result { writer.write_all(self)?; Ok(mem::size_of::()) } } impl Encode for &[T] where T: Encode, { fn encode(&self, writer: &mut W) -> Result { let mut n = (self.len() as Size).encode(writer)?; for item in self.iter() { n += item.encode(writer)?; } Ok(n) } } impl Encode for BoundedVec where T: Encode, { fn encode(&self, writer: &mut W) -> Result { self.as_slice().encode(writer) } } impl Encode for &str { fn encode(&self, writer: &mut W) -> Result { assert!(self.len() <= u8::MAX as usize); let n = (self.len() as u8).encode(writer)?; let bytes = self.as_bytes(); // Nb. Don't use the [`Encode`] instance here for &[u8], because we are prefixing the // length ourselves. writer.write_all(bytes)?; Ok(n + bytes.len()) } } impl Encode for String { fn encode(&self, writer: &mut W) -> Result { self.as_str().encode(writer) } } impl Encode for git::Url { fn encode(&self, writer: &mut W) -> Result { self.to_string().encode(writer) } } impl Encode for RepoId { fn encode(&self, writer: &mut W) -> Result { self.deref().encode(writer) } } impl Encode for Refs { fn encode(&self, writer: &mut W) -> Result { let len: Size = self .len() .try_into() .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?; let mut n = len.encode(writer)?; for (name, oid) in self.iter() { n += name.as_str().encode(writer)?; n += oid.encode(writer)?; } Ok(n) } } impl Encode for Alias { fn encode(&self, writer: &mut W) -> Result { self.as_ref().encode(writer) } } impl Encode for (A, B) where A: Encode, B: Encode, { fn encode(&self, writer: &mut W) -> Result { let mut n = self.0.encode(writer)?; n += self.1.encode(writer)?; Ok(n) } } impl Encode for git::RefString { fn encode(&self, writer: &mut W) -> Result { self.as_str().encode(writer) } } impl Encode for Signature { fn encode(&self, writer: &mut W) -> Result { self.deref().encode(writer) } } impl Encode for git::Oid { fn encode(&self, writer: &mut W) -> Result { // Nb. We use length-encoding here to support future SHA-2 object ids. self.as_bytes().encode(writer) } } //////////////////////////////////////////////////////////////////////////////// impl Decode for PublicKey { fn decode(reader: &mut R) -> Result { let buf: [u8; 32] = Decode::decode(reader)?; Ok(PublicKey::from(buf)) } } impl Decode for Refs { fn decode(reader: &mut R) -> Result { let len = Size::decode(reader)?; let mut refs = BTreeMap::new(); for _ in 0..len { let name = String::decode(reader)?; let name = git::RefString::try_from(name).map_err(Error::from)?; let oid = git::Oid::decode(reader)?; refs.insert(name, oid); } Ok(refs.into()) } } impl Decode for git::RefString { fn decode(reader: &mut R) -> Result { let ref_str = String::decode(reader)?; git::RefString::try_from(ref_str).map_err(Error::from) } } impl Decode for Alias { fn decode(reader: &mut R) -> Result { String::decode(reader).and_then(|s| Alias::from_str(&s).map_err(Error::from)) } } impl Decode for (A, B) where A: Decode, B: Decode, { fn decode(reader: &mut R) -> Result { let a = A::decode(reader)?; let b = B::decode(reader)?; Ok((a, b)) } } impl Decode for git::Oid { fn decode(reader: &mut R) -> Result { let len = Size::decode(reader)? as usize; #[allow(non_upper_case_globals)] const expected: usize = mem::size_of::(); if len != expected { return Err(Error::InvalidSize { expected, actual: len, }); } let buf: [u8; expected] = Decode::decode(reader)?; let oid = git::raw::Oid::from_bytes(&buf).expect("the buffer is exactly the right size"); let oid = git::Oid::from(oid); Ok(oid) } } impl Decode for Signature { fn decode(reader: &mut R) -> Result { let bytes: [u8; 64] = Decode::decode(reader)?; Ok(Signature::from(bytes)) } } impl Decode for u8 { fn decode(reader: &mut R) -> Result { reader.read_u8().map_err(Error::from) } } impl Decode for u16 { fn decode(reader: &mut R) -> Result { reader.read_u16::().map_err(Error::from) } } impl Decode for u32 { fn decode(reader: &mut R) -> Result { reader.read_u32::().map_err(Error::from) } } impl Decode for u64 { fn decode(reader: &mut R) -> Result { reader.read_u64::().map_err(Error::from) } } impl Decode for [u8; N] { fn decode(reader: &mut R) -> Result { let mut ary = [0; N]; reader.read_exact(&mut ary)?; Ok(ary) } } impl Decode for BoundedVec where T: Decode, { fn decode(reader: &mut R) -> Result { let len: usize = Size::decode(reader)? as usize; let mut items = Self::with_capacity(len).map_err(|_| Error::InvalidSize { expected: Self::max(), actual: len, })?; for _ in 0..items.capacity() { let item = T::decode(reader)?; items.push(item).ok(); } Ok(items) } } impl Decode for String { fn decode(reader: &mut R) -> Result { let len = u8::decode(reader)?; let mut bytes = vec![0; len as usize]; reader.read_exact(&mut bytes)?; let string = String::from_utf8(bytes)?; Ok(string) } } impl Decode for RepoId { fn decode(reader: &mut R) -> Result { let oid: git::Oid = Decode::decode(reader)?; Ok(Self::from(oid)) } } impl Encode for filter::Filter { fn encode(&self, writer: &mut W) -> Result { let mut n = 0; n += self.deref().as_bytes().encode(writer)?; Ok(n) } } impl Decode for filter::Filter { fn decode(reader: &mut R) -> Result { let size: usize = Size::decode(reader)? as usize; if !filter::FILTER_SIZES.contains(&size) { return Err(Error::InvalidFilterSize(size)); } let mut bytes = vec![0; size]; reader.read_exact(&mut bytes[..])?; let f = filter::BloomFilter::from(bytes); debug_assert_eq!(f.hashes(), filter::FILTER_HASHES); Ok(Self::from(f)) } } impl Encode for SignedRefs { fn encode(&self, writer: &mut W) -> Result { let mut n = 0; n += self.id.encode(writer)?; n += self.refs.encode(writer)?; n += self.signature.encode(writer)?; Ok(n) } } impl Decode for SignedRefs { fn decode(reader: &mut R) -> Result { let id = NodeId::decode(reader)?; let refs = Refs::decode(reader)?; let signature = Signature::decode(reader)?; Ok(Self::new(refs, id, signature)) } } impl Encode for RefsAt { fn encode(&self, writer: &mut W) -> Result { let mut n = 0; n += self.remote.encode(writer)?; n += self.at.encode(writer)?; Ok(n) } } impl Decode for RefsAt { fn decode(reader: &mut R) -> Result { let remote = NodeId::decode(reader)?; let at = git::Oid::decode(reader)?; Ok(Self { remote, at }) } } impl Encode for node::Features { fn encode(&self, writer: &mut W) -> Result { self.deref().encode(writer) } } impl Decode for node::Features { fn decode(reader: &mut R) -> Result { let features = u64::decode(reader)?; Ok(Self::from(features)) } } #[cfg(test)] mod tests { use super::*; use qcheck; use qcheck_macros::quickcheck; use crate::crypto::Unverified; use crate::storage::refs::SignedRefs; use crate::test::assert_matches; #[quickcheck] fn prop_u8(input: u8) { assert_eq!(deserialize::(&serialize(&input)).unwrap(), input); } #[quickcheck] fn prop_u16(input: u16) { assert_eq!(deserialize::(&serialize(&input)).unwrap(), input); } #[quickcheck] fn prop_u32(input: u32) { assert_eq!(deserialize::(&serialize(&input)).unwrap(), input); } #[quickcheck] fn prop_u64(input: u64) { assert_eq!(deserialize::(&serialize(&input)).unwrap(), input); } #[quickcheck] fn prop_string(input: String) -> qcheck::TestResult { if input.len() > u8::MAX as usize { return qcheck::TestResult::discard(); } assert_eq!(deserialize::(&serialize(&input)).unwrap(), input); qcheck::TestResult::passed() } #[quickcheck] fn prop_vec(input: BoundedVec) { assert_eq!( deserialize::>(&serialize(&input.as_slice())).unwrap(), input ); } #[quickcheck] fn prop_pubkey(input: PublicKey) { assert_eq!(deserialize::(&serialize(&input)).unwrap(), input); } #[quickcheck] fn prop_filter(input: filter::Filter) { assert_eq!( deserialize::(&serialize(&input)).unwrap(), input ); } #[quickcheck] fn prop_id(input: RepoId) { assert_eq!(deserialize::(&serialize(&input)).unwrap(), input); } #[quickcheck] fn prop_refs(input: Refs) { assert_eq!(deserialize::(&serialize(&input)).unwrap(), input); } #[quickcheck] fn prop_tuple(input: (String, String)) { assert_eq!( deserialize::<(String, String)>(&serialize(&input)).unwrap(), input ); } #[quickcheck] fn prop_signature(input: [u8; 64]) { let signature = Signature::from(input); assert_eq!( deserialize::(&serialize(&signature)).unwrap(), signature ); } #[quickcheck] fn prop_oid(input: [u8; 20]) { let oid = git::Oid::try_from(input.as_slice()).unwrap(); assert_eq!(deserialize::(&serialize(&oid)).unwrap(), oid); } #[quickcheck] fn prop_signed_refs(input: SignedRefs) { assert_eq!( deserialize::>(&serialize(&input)).unwrap(), input ); } #[test] fn test_string() { assert_eq!( serialize(&String::from("hello")), vec![5, b'h', b'e', b'l', b'l', b'o'] ); } #[test] fn test_alias() { assert_eq!( serialize(&Alias::from_str("hello").unwrap()), vec![5, b'h', b'e', b'l', b'l', b'o'] ); } #[test] fn test_filter_invalid() { let b = bloomy::BloomFilter::with_size(filter::FILTER_SIZE_M / 3); let f = filter::Filter::from(b); let bytes = serialize(&f); assert_matches!( deserialize::(&bytes).unwrap_err(), Error::InvalidFilterSize(_) ); } #[test] fn test_bounded_vec_limit() { let v: BoundedVec = vec![1, 2].try_into().unwrap(); let buf = serialize(&v); assert_matches!( deserialize::>(&buf), Err(Error::InvalidSize { expected: 1, actual: 2 }), "fail when vector is too small for buffer", ); assert!( deserialize::>(&buf).is_ok(), "successfully decode vector of same size", ); } }