Skip to main content

radicle_protocol/
wire.rs

1pub mod frame;
2pub mod message;
3pub mod varint;
4
5pub use frame::StreamId;
6pub use message::{AddressType, MessageType};
7
8use std::convert::TryFrom;
9use std::fmt::Debug;
10use std::ops::Deref;
11use std::str::FromStr;
12use std::string::FromUtf8Error;
13
14use bytes::{Buf, BufMut};
15
16#[cfg(feature = "i2p")]
17use cypheraddr::i2p;
18#[cfg(feature = "tor")]
19use cypheraddr::tor;
20
21use radicle::crypto::{PublicKey, Signature};
22use radicle::git;
23use radicle::git::fmt;
24use radicle::identity::RepoId;
25use radicle::node;
26use radicle::node::Alias;
27use radicle::node::NodeId;
28use radicle::node::Timestamp;
29use radicle::node::UserAgent;
30use radicle::storage::refs::Refs;
31use radicle::storage::refs::RefsAt;
32
33use crate::bounded::BoundedVec;
34use crate::service::filter;
35
36/// The default type we use to represent sizes on the wire.
37///
38/// Since wire messages are limited to 64KB by the transport layer,
39/// two bytes is enough to represent any message.
40///
41/// Note that in certain cases, we may use a smaller type.
42pub type Size = u16;
43
44#[derive(thiserror::Error, Debug)]
45pub enum Invalid {
46    #[error(
47        "invalid Git object identifier size: expected {}, got {actual}",
48        git::Oid::LEN_SHA1
49    )]
50    Oid { actual: usize },
51    #[error(transparent)]
52    Bounded(#[from] crate::bounded::Error),
53    #[error("invalid filter size: {actual}")]
54    FilterSize { actual: usize },
55    #[error("UTF-8 error: {0}")]
56    FromUtf8(#[from] FromUtf8Error),
57    #[error(transparent)]
58    RefName(#[from] fmt::Error),
59    #[error(transparent)]
60    Alias(#[from] node::AliasError),
61    #[error("invalid user agent string: {err}")]
62    InvalidUserAgent { err: String },
63    #[cfg(feature = "tor")]
64    #[error("invalid onion address: {0}")]
65    OnionAddr(#[from] tor::OnionAddrDecodeError),
66    #[cfg(feature = "i2p")]
67    #[error("invalid i2p address: {0}")]
68    I2pAddr(#[from] i2p::I2pAddrParseError),
69    #[error("invalid timestamp: {actual_millis} millis")]
70    Timestamp { actual_millis: u64 },
71
72    // Message types
73    #[error("invalid control message type: {actual:x}")]
74    ControlType { actual: u8 },
75    #[error("invalid stream type: {actual:x}")]
76    StreamType { actual: u8 },
77    #[error("invalid address type: {actual:x}")]
78    AddressType { actual: u8 },
79    #[error("invalid message type: {actual:x}")]
80    MessageType { actual: u16 },
81    #[error("invalid info message type: {actual:x}")]
82    InfoMessageType { actual: u16 },
83
84    // Protocol version handling
85    #[error("invalid protocol version string: {actual:x?}")]
86    ProtocolVersion { actual: [u8; 4] },
87    #[error("unsupported protocol version: {actual}")]
88    ProtocolVersionUnsupported { actual: u8 },
89}
90
91#[derive(thiserror::Error, Debug)]
92pub enum Error {
93    #[error(transparent)]
94    Invalid(#[from] Invalid),
95
96    #[error(
97        "unexpected end of buffer, requested {requested} more bytes but only {available} are available"
98    )]
99    UnexpectedEnd { available: usize, requested: usize },
100}
101
102impl From<bytes::TryGetError> for Error {
103    fn from(
104        bytes::TryGetError {
105            available,
106            requested,
107        }: bytes::TryGetError,
108    ) -> Self {
109        Self::UnexpectedEnd {
110            available,
111            requested,
112        }
113    }
114}
115
116/// Things that can be encoded as binary.
117pub trait Encode {
118    /// Encode self by writing it to the given buffer.
119    fn encode(&self, buffer: &mut impl BufMut);
120
121    /// A convenience wrapper around [`Encode::encode`]
122    /// that allocates a [`Vec`].
123    fn encode_to_vec(&self) -> Vec<u8> {
124        let mut buf = Vec::new();
125        self.encode(&mut buf);
126        buf
127    }
128}
129
130/// Things that can be decoded from binary.
131pub trait Decode: Sized {
132    fn decode(buffer: &mut impl Buf) -> Result<Self, Error>;
133
134    /// A convenience wrapper around [`Decode::decode`] to decode
135    /// from a slice exactly.
136    ///
137    /// # Panics
138    ///
139    ///  - If decoding failed because there were not enough bytes.
140    ///  - If there are any bytes left after decoding.
141    #[cfg(test)]
142    fn decode_exact(mut data: &[u8]) -> Result<Self, Invalid> {
143        match Self::decode(&mut data) {
144            Ok(value) => {
145                if !data.is_empty() {
146                    panic!("{} bytes left in buffer", data.len());
147                }
148                Ok(value)
149            }
150            Err(err @ Error::UnexpectedEnd { .. }) => {
151                panic!("{}", err);
152            }
153            Err(Error::Invalid(e)) => Err(e),
154        }
155    }
156}
157
158impl Encode for u8 {
159    fn encode(&self, buf: &mut impl BufMut) {
160        buf.put_u8(*self);
161    }
162}
163
164impl Encode for u16 {
165    fn encode(&self, buf: &mut impl BufMut) {
166        buf.put_u16(*self);
167    }
168}
169
170impl Encode for u32 {
171    fn encode(&self, buf: &mut impl BufMut) {
172        buf.put_u32(*self);
173    }
174}
175
176impl Encode for u64 {
177    fn encode(&self, buf: &mut impl BufMut) {
178        buf.put_u64(*self);
179    }
180}
181
182impl Encode for PublicKey {
183    fn encode(&self, buf: &mut impl BufMut) {
184        self.to_byte_array().encode(buf)
185    }
186}
187
188impl<const T: usize> Encode for &[u8; T] {
189    fn encode(&self, buf: &mut impl BufMut) {
190        buf.put_slice(&**self);
191    }
192}
193
194impl<const T: usize> Encode for [u8; T] {
195    fn encode(&self, buf: &mut impl BufMut) {
196        buf.put_slice(self);
197    }
198}
199
200impl<T> Encode for &[T]
201where
202    T: Encode,
203{
204    fn encode(&self, buf: &mut impl BufMut) {
205        (self.len() as Size).encode(buf);
206
207        for item in self.iter() {
208            item.encode(buf);
209        }
210    }
211}
212
213impl<T, const N: usize> Encode for BoundedVec<T, N>
214where
215    T: Encode,
216{
217    fn encode(&self, buf: &mut impl BufMut) {
218        self.as_slice().encode(buf)
219    }
220}
221
222impl Encode for &str {
223    fn encode(&self, buf: &mut impl BufMut) {
224        assert!(self.len() <= u8::MAX as usize);
225
226        (self.len() as u8).encode(buf);
227        let bytes = self.as_bytes();
228
229        // Nb. Don't use the [`Encode`] instance here for &[u8], because we are prefixing the
230        // length ourselves.
231        buf.put_slice(bytes);
232    }
233}
234
235impl Encode for String {
236    fn encode(&self, buf: &mut impl BufMut) {
237        self.as_str().encode(buf)
238    }
239}
240
241impl Encode for git::Url {
242    fn encode(&self, buf: &mut impl BufMut) {
243        self.to_string().encode(buf)
244    }
245}
246
247impl Encode for RepoId {
248    fn encode(&self, buf: &mut impl BufMut) {
249        self.deref().encode(buf)
250    }
251}
252
253impl Encode for Refs {
254    fn encode(&self, buf: &mut impl BufMut) {
255        let len: Size = self
256            .len()
257            .try_into()
258            .expect("`Refs::len()` must be less than or equal to `Size::MAX`");
259        len.encode(buf);
260
261        for (name, oid) in self.iter() {
262            name.as_str().encode(buf);
263            oid.encode(buf);
264        }
265    }
266}
267
268#[cfg(feature = "tor")]
269impl Encode for cypheraddr::tor::OnionAddrV3 {
270    fn encode(&self, buf: &mut impl BufMut) {
271        self.into_raw_bytes().encode(buf)
272    }
273}
274
275#[cfg(feature = "i2p")]
276impl Encode for i2p::I2pAddr {
277    fn encode(&self, buf: &mut impl BufMut) {
278        self.to_string().encode(buf)
279    }
280}
281
282impl Encode for UserAgent {
283    fn encode(&self, buf: &mut impl BufMut) {
284        self.as_ref().encode(buf)
285    }
286}
287
288impl Encode for Alias {
289    fn encode(&self, buf: &mut impl BufMut) {
290        self.as_ref().encode(buf)
291    }
292}
293
294impl<A, B> Encode for (A, B)
295where
296    A: Encode,
297    B: Encode,
298{
299    fn encode(&self, buf: &mut impl BufMut) {
300        self.0.encode(buf);
301        self.1.encode(buf);
302    }
303}
304
305impl Encode for git::fmt::RefString {
306    fn encode(&self, buf: &mut impl BufMut) {
307        self.as_str().encode(buf)
308    }
309}
310
311impl Encode for Signature {
312    fn encode(&self, buf: &mut impl BufMut) {
313        self.deref().encode(buf)
314    }
315}
316
317impl Encode for git::Oid {
318    fn encode(&self, buf: &mut impl BufMut) {
319        // Nb. We use length-encoding here to support future SHA-2 object ids.
320        let bytes: &[u8] = self.as_ref();
321        bytes.encode(buf)
322    }
323}
324
325////////////////////////////////////////////////////////////////////////////////
326
327impl Decode for PublicKey {
328    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
329        let buf: [u8; 32] = Decode::decode(buf)?;
330
331        Ok(PublicKey::from(buf))
332    }
333}
334
335impl Decode for Refs {
336    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
337        let len = Size::decode(buf)?;
338        let mut refs = Refs::new();
339
340        for _ in 0..len {
341            let name = String::decode(buf)?;
342            let name = git::fmt::RefString::try_from(name).map_err(Invalid::from)?;
343            let oid = git::Oid::decode(buf)?;
344
345            refs.insert(name, oid);
346        }
347        Ok(refs)
348    }
349}
350
351impl Decode for git::fmt::RefString {
352    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
353        let ref_str = String::decode(buf)?;
354        Ok(git::fmt::RefString::try_from(ref_str).map_err(Invalid::from)?)
355    }
356}
357
358impl Decode for UserAgent {
359    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
360        let user_agent = String::decode(buf)?;
361        Ok(UserAgent::from_str(&user_agent).map_err(|err| Invalid::InvalidUserAgent { err })?)
362    }
363}
364
365impl Decode for Alias {
366    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
367        let alias = String::decode(buf)?;
368        Ok(Alias::from_str(&alias).map_err(Invalid::from)?)
369    }
370}
371
372impl<A, B> Decode for (A, B)
373where
374    A: Decode,
375    B: Decode,
376{
377    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
378        let a = A::decode(buf)?;
379        let b = B::decode(buf)?;
380        Ok((a, b))
381    }
382}
383
384impl Decode for git::Oid {
385    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
386        let len = Size::decode(buf)? as usize;
387
388        if len != git::Oid::LEN_SHA1 {
389            return Err(Invalid::Oid { actual: len }.into());
390        }
391
392        Ok(git::Oid::Sha1(Decode::decode(buf)?))
393    }
394}
395
396impl Decode for Signature {
397    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
398        let bytes: [u8; 64] = Decode::decode(buf)?;
399
400        Ok(Signature::from(bytes))
401    }
402}
403
404impl Decode for u8 {
405    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
406        Ok(buf.try_get_u8()?)
407    }
408}
409
410impl Decode for u16 {
411    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
412        Ok(buf.try_get_u16()?)
413    }
414}
415
416impl Decode for u32 {
417    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
418        Ok(buf.try_get_u32()?)
419    }
420}
421
422impl Decode for u64 {
423    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
424        Ok(buf.try_get_u64()?)
425    }
426}
427
428impl<const N: usize> Decode for [u8; N] {
429    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
430        let mut ary = [0; N];
431        buf.try_copy_to_slice(&mut ary).map_err(Error::from)?;
432
433        Ok(ary)
434    }
435}
436
437impl<T, const N: usize> Decode for BoundedVec<T, N>
438where
439    T: Decode,
440{
441    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
442        let len: usize = Size::decode(buf)? as usize;
443        let mut items = Self::with_capacity(len).map_err(Invalid::from)?;
444
445        for _ in 0..items.capacity() {
446            let item = T::decode(buf)?;
447            items.push(item).ok();
448        }
449        Ok(items)
450    }
451}
452
453impl Decode for String {
454    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
455        let len = u8::decode(buf)?;
456        let mut bytes = vec![0; len as usize];
457
458        buf.try_copy_to_slice(&mut bytes)?;
459
460        let string = String::from_utf8(bytes).map_err(Invalid::from)?;
461
462        Ok(string)
463    }
464}
465
466impl Decode for RepoId {
467    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
468        let oid: git::Oid = Decode::decode(buf)?;
469
470        Ok(Self::from(oid))
471    }
472}
473
474impl Encode for filter::Filter {
475    fn encode(&self, buf: &mut impl BufMut) {
476        self.deref().as_bytes().encode(buf);
477    }
478}
479
480impl Decode for filter::Filter {
481    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
482        let size: usize = Size::decode(buf)? as usize;
483        if !filter::FILTER_SIZES.contains(&size) {
484            return Err(Invalid::FilterSize { actual: size }.into());
485        }
486
487        let mut bytes = vec![0; size];
488
489        buf.try_copy_to_slice(&mut bytes)?;
490
491        let f = filter::BloomFilter::from(bytes);
492        debug_assert_eq!(f.hashes(), filter::FILTER_HASHES);
493
494        Ok(Self::from(f))
495    }
496}
497
498impl Encode for RefsAt {
499    fn encode(&self, buf: &mut impl BufMut) {
500        self.remote.encode(buf);
501        self.at.encode(buf);
502    }
503}
504
505impl Decode for RefsAt {
506    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
507        let remote = NodeId::decode(buf)?;
508        let at = git::Oid::decode(buf)?;
509        Ok(Self { remote, at })
510    }
511}
512
513impl Encode for node::Features {
514    fn encode(&self, buf: &mut impl BufMut) {
515        self.deref().encode(buf)
516    }
517}
518
519impl Decode for node::Features {
520    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
521        let features = u64::decode(buf)?;
522
523        Ok(Self::from(features))
524    }
525}
526
527#[cfg(feature = "tor")]
528impl Decode for tor::OnionAddrV3 {
529    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
530        let bytes: [u8; tor::ONION_V3_RAW_LEN] = Decode::decode(buf)?;
531        let addr = tor::OnionAddrV3::from_raw_bytes(bytes).map_err(Invalid::from)?;
532
533        Ok(addr)
534    }
535}
536
537#[cfg(feature = "i2p")]
538impl Decode for i2p::I2pAddr {
539    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
540        let s = String::decode(buf)?;
541        let addr = i2p::I2pAddr::from_str(&s).map_err(Invalid::from)?;
542
543        Ok(addr)
544    }
545}
546
547impl Encode for Timestamp {
548    fn encode(&self, buf: &mut impl BufMut) {
549        self.deref().encode(buf)
550    }
551}
552
553impl Decode for Timestamp {
554    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
555        let millis = u64::decode(buf)?;
556        let ts = Timestamp::try_from(millis).map_err(|value| Invalid::Timestamp {
557            actual_millis: value,
558        })?;
559
560        Ok(ts)
561    }
562}
563
564#[cfg(test)]
565fn roundtrip<T>(value: T)
566where
567    T: Encode + Decode + PartialEq + Debug,
568{
569    let encoded = value.encode_to_vec();
570    assert_eq!(T::decode_exact(&encoded).expect("roundtrip"), value);
571}
572
573#[cfg(test)]
574#[macro_export]
575macro_rules! prop_roundtrip {
576    ($t:ty, $name:tt) => {
577        pastey::paste! {
578            #[quickcheck]
579            fn [< prop_roundtrip_ $name:lower >](v: $t) {
580                $crate::wire::roundtrip(v);
581            }
582        }
583    };
584    ($t:ty) => {
585        pastey::paste! {
586            prop_roundtrip!($t, [< $t >]);
587        }
588    };
589}
590
591#[cfg(test)]
592mod tests {
593    use super::*;
594
595    use qcheck;
596    use qcheck_macros::quickcheck;
597
598    use radicle::assert_matches;
599
600    prop_roundtrip!(u16);
601    prop_roundtrip!(u32);
602    prop_roundtrip!(u64);
603    prop_roundtrip!(BoundedVec<u8, 16>, vec);
604    prop_roundtrip!(PublicKey);
605    prop_roundtrip!(filter::Filter, filter);
606    prop_roundtrip!(RepoId);
607    prop_roundtrip!(Refs);
608    prop_roundtrip!((String, String), tuple);
609
610    #[quickcheck]
611    fn prop_string(input: String) -> qcheck::TestResult {
612        if input.len() > u8::MAX as usize {
613            return qcheck::TestResult::discard();
614        }
615
616        roundtrip(input);
617
618        qcheck::TestResult::passed()
619    }
620
621    #[quickcheck]
622    fn prop_signature(input: [u8; 64]) {
623        roundtrip(Signature::from(input));
624    }
625
626    #[quickcheck]
627    fn prop_oid(input: git::Oid) {
628        roundtrip(input);
629    }
630
631    #[test]
632    fn test_string() {
633        assert_eq!(
634            String::from("hello").encode_to_vec(),
635            vec![5, b'h', b'e', b'l', b'l', b'o']
636        );
637    }
638
639    #[test]
640    fn test_alias() {
641        assert_eq!(
642            Alias::from_str("hello").unwrap().encode_to_vec(),
643            vec![5, b'h', b'e', b'l', b'l', b'o']
644        );
645    }
646
647    #[test]
648    fn test_filter_invalid() {
649        let b = bloomy::BloomFilter::with_size(filter::FILTER_SIZE_M / 3);
650        let f = filter::Filter::from(b);
651        let bytes = f.encode_to_vec();
652
653        assert_matches!(
654            filter::Filter::decode_exact(&bytes).unwrap_err(),
655            Invalid::FilterSize { .. }
656        );
657    }
658
659    #[test]
660    fn test_bounded_vec_limit() {
661        let v: BoundedVec<u8, 2> = vec![1, 2].try_into().unwrap();
662        let buf = &v.encode_to_vec();
663
664        assert_matches!(
665            BoundedVec::<u8, 1>::decode_exact(buf),
666            Err(Invalid::Bounded(crate::bounded::Error::InvalidSize {
667                expected: 1,
668                actual: 2
669            })),
670            "fail when vector is too small for buffer",
671        );
672
673        assert!(
674            BoundedVec::<u8, 2>::decode_exact(buf).is_ok(),
675            "successfully decode vector of same size",
676        );
677    }
678}