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