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::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, PublicKeyBytes, 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        std::borrow::Borrow::<PublicKeyBytes>::borrow(self).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        let bytes: [u8; 64] = self.to_bytes();
314        bytes.encode(buf)
315    }
316}
317
318impl Encode for git::Oid {
319    fn encode(&self, buf: &mut impl BufMut) {
320        // Nb. We use length-encoding here to support future SHA-2 object ids.
321        let bytes: &[u8] = self.as_ref();
322        bytes.encode(buf)
323    }
324}
325
326////////////////////////////////////////////////////////////////////////////////
327
328impl Decode for PublicKey {
329    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
330        Ok(PublicKey::from(<[u8; 32]>::decode(buf)?))
331    }
332}
333
334impl Decode for Refs {
335    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
336        let len = Size::decode(buf)?;
337        let mut refs = Refs::new();
338
339        for _ in 0..len {
340            let name = String::decode(buf)?;
341            let name = git::fmt::RefString::try_from(name).map_err(Invalid::from)?;
342            let oid = git::Oid::decode(buf)?;
343
344            refs.insert(name, oid);
345        }
346        Ok(refs)
347    }
348}
349
350impl Decode for git::fmt::RefString {
351    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
352        let ref_str = String::decode(buf)?;
353        Ok(git::fmt::RefString::try_from(ref_str).map_err(Invalid::from)?)
354    }
355}
356
357impl Decode for UserAgent {
358    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
359        let user_agent = String::decode(buf)?;
360        Ok(UserAgent::from_str(&user_agent).map_err(|err| Invalid::InvalidUserAgent { err })?)
361    }
362}
363
364impl Decode for Alias {
365    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
366        let alias = String::decode(buf)?;
367        Ok(Alias::from_str(&alias).map_err(Invalid::from)?)
368    }
369}
370
371impl<A, B> Decode for (A, B)
372where
373    A: Decode,
374    B: Decode,
375{
376    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
377        let a = A::decode(buf)?;
378        let b = B::decode(buf)?;
379        Ok((a, b))
380    }
381}
382
383impl Decode for git::Oid {
384    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
385        let len = Size::decode(buf)? as usize;
386
387        if len != git::Oid::LEN_SHA1 {
388            return Err(Invalid::Oid { actual: len }.into());
389        }
390
391        Ok(git::Oid::Sha1(Decode::decode(buf)?))
392    }
393}
394
395impl Decode for Signature {
396    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
397        let bytes: [u8; 64] = Decode::decode(buf)?;
398
399        Ok(Signature::from(bytes))
400    }
401}
402
403impl Decode for u8 {
404    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
405        Ok(buf.try_get_u8()?)
406    }
407}
408
409impl Decode for u16 {
410    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
411        Ok(buf.try_get_u16()?)
412    }
413}
414
415impl Decode for u32 {
416    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
417        Ok(buf.try_get_u32()?)
418    }
419}
420
421impl Decode for u64 {
422    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
423        Ok(buf.try_get_u64()?)
424    }
425}
426
427impl<const N: usize> Decode for [u8; N] {
428    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
429        let mut ary = [0; N];
430        buf.try_copy_to_slice(&mut ary).map_err(Error::from)?;
431
432        Ok(ary)
433    }
434}
435
436impl<T, const N: usize> Decode for BoundedVec<T, N>
437where
438    T: Decode,
439{
440    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
441        let len: usize = Size::decode(buf)? as usize;
442        let mut items = Self::with_capacity(len).map_err(Invalid::from)?;
443
444        for _ in 0..items.capacity() {
445            let item = T::decode(buf)?;
446            items.push(item).ok();
447        }
448        Ok(items)
449    }
450}
451
452impl Decode for String {
453    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
454        let len = u8::decode(buf)?;
455        let mut bytes = vec![0; len as usize];
456
457        buf.try_copy_to_slice(&mut bytes)?;
458
459        let string = String::from_utf8(bytes).map_err(Invalid::from)?;
460
461        Ok(string)
462    }
463}
464
465impl Decode for RepoId {
466    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
467        let oid: git::Oid = Decode::decode(buf)?;
468
469        Ok(Self::from(oid))
470    }
471}
472
473impl Encode for filter::Filter {
474    fn encode(&self, buf: &mut impl BufMut) {
475        self.deref().as_bytes().encode(buf);
476    }
477}
478
479impl Decode for filter::Filter {
480    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
481        let size: usize = Size::decode(buf)? as usize;
482        if !filter::FILTER_SIZES.contains(&size) {
483            return Err(Invalid::FilterSize { actual: size }.into());
484        }
485
486        let mut bytes = vec![0; size];
487
488        buf.try_copy_to_slice(&mut bytes)?;
489
490        let f = filter::BloomFilter::from(bytes);
491        debug_assert_eq!(f.hashes(), filter::FILTER_HASHES);
492
493        Ok(Self::from(f))
494    }
495}
496
497impl Encode for RefsAt {
498    fn encode(&self, buf: &mut impl BufMut) {
499        self.remote.encode(buf);
500        self.at.encode(buf);
501    }
502}
503
504impl Decode for RefsAt {
505    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
506        let remote = NodeId::decode(buf)?;
507        let at = git::Oid::decode(buf)?;
508        Ok(Self { remote, at })
509    }
510}
511
512impl Encode for node::Features {
513    fn encode(&self, buf: &mut impl BufMut) {
514        self.deref().encode(buf)
515    }
516}
517
518impl Decode for node::Features {
519    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
520        let features = u64::decode(buf)?;
521
522        Ok(Self::from(features))
523    }
524}
525
526#[cfg(feature = "tor")]
527impl Decode for tor::OnionAddrV3 {
528    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
529        let bytes: [u8; tor::ONION_V3_RAW_LEN] = Decode::decode(buf)?;
530        let addr = tor::OnionAddrV3::from_raw_bytes(bytes).map_err(Invalid::from)?;
531
532        Ok(addr)
533    }
534}
535
536#[cfg(feature = "i2p")]
537impl Decode for i2p::I2pAddr {
538    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
539        let s = String::decode(buf)?;
540        let addr = i2p::I2pAddr::from_str(&s).map_err(Invalid::from)?;
541
542        Ok(addr)
543    }
544}
545
546impl Encode for Timestamp {
547    fn encode(&self, buf: &mut impl BufMut) {
548        self.deref().encode(buf)
549    }
550}
551
552impl Decode for Timestamp {
553    fn decode(buf: &mut impl Buf) -> Result<Self, Error> {
554        let millis = u64::decode(buf)?;
555        let ts = Timestamp::try_from(millis).map_err(|value| Invalid::Timestamp {
556            actual_millis: value,
557        })?;
558
559        Ok(ts)
560    }
561}
562
563#[cfg(test)]
564fn roundtrip<T>(value: T)
565where
566    T: Encode + Decode + PartialEq + Debug,
567{
568    let encoded = value.encode_to_vec();
569    assert_eq!(T::decode_exact(&encoded).expect("roundtrip"), value);
570}
571
572#[cfg(test)]
573#[macro_export]
574macro_rules! prop_roundtrip {
575    ($t:ty, $name:tt) => {
576        pastey::paste! {
577            #[quickcheck]
578            fn [< prop_roundtrip_ $name:lower >](v: $t) {
579                $crate::wire::roundtrip(v);
580            }
581        }
582    };
583    ($t:ty) => {
584        pastey::paste! {
585            prop_roundtrip!($t, [< $t >]);
586        }
587    };
588}
589
590#[cfg(test)]
591mod tests {
592    use super::*;
593
594    use qcheck;
595    use qcheck_macros::quickcheck;
596
597    use radicle::assert_matches;
598
599    prop_roundtrip!(u16);
600    prop_roundtrip!(u32);
601    prop_roundtrip!(u64);
602    prop_roundtrip!(BoundedVec<u8, 16>, vec);
603    prop_roundtrip!(PublicKey);
604    prop_roundtrip!(filter::Filter, filter);
605    prop_roundtrip!(RepoId);
606    prop_roundtrip!(Refs);
607    prop_roundtrip!((String, String), tuple);
608
609    #[quickcheck]
610    fn prop_string(input: String) -> qcheck::TestResult {
611        if input.len() > u8::MAX as usize {
612            return qcheck::TestResult::discard();
613        }
614
615        roundtrip(input);
616
617        qcheck::TestResult::passed()
618    }
619
620    #[quickcheck]
621    fn prop_signature(input: [u8; 64]) {
622        roundtrip(Signature::from(input));
623    }
624
625    #[quickcheck]
626    fn prop_oid(input: git::Oid) {
627        roundtrip(input);
628    }
629
630    #[test]
631    fn test_string() {
632        assert_eq!(
633            String::from("hello").encode_to_vec(),
634            vec![5, b'h', b'e', b'l', b'l', b'o']
635        );
636    }
637
638    #[test]
639    fn test_alias() {
640        assert_eq!(
641            Alias::from_str("hello").unwrap().encode_to_vec(),
642            vec![5, b'h', b'e', b'l', b'l', b'o']
643        );
644    }
645
646    #[test]
647    fn test_filter_invalid() {
648        let b = bloomy::BloomFilter::with_size(filter::FILTER_SIZE_M / 3);
649        let f = filter::Filter::from(b);
650        let bytes = f.encode_to_vec();
651
652        assert_matches!(
653            filter::Filter::decode_exact(&bytes).unwrap_err(),
654            Invalid::FilterSize { .. }
655        );
656    }
657
658    #[test]
659    fn test_bounded_vec_limit() {
660        let v: BoundedVec<u8, 2> = vec![1, 2].try_into().unwrap();
661        let buf = &v.encode_to_vec();
662
663        assert_matches!(
664            BoundedVec::<u8, 1>::decode_exact(buf),
665            Err(Invalid::Bounded(crate::bounded::Error::InvalidSize {
666                expected: 1,
667                actual: 2
668            })),
669            "fail when vector is too small for buffer",
670        );
671
672        assert!(
673            BoundedVec::<u8, 2>::decode_exact(buf).is_ok(),
674            "successfully decode vector of same size",
675        );
676    }
677}