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