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
36pub 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 #[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 #[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
116pub trait Encode {
118 fn encode(&self, buffer: &mut impl BufMut);
120
121 fn encode_to_vec(&self) -> Vec<u8> {
124 let mut buf = Vec::new();
125 self.encode(&mut buf);
126 buf
127 }
128}
129
130pub trait Decode: Sized {
132 fn decode(buffer: &mut impl Buf) -> Result<Self, Error>;
133
134 #[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 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 let bytes: &[u8] = self.as_ref();
322 bytes.encode(buf)
323 }
324}
325
326impl 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}