binator_network/
lib.rs

1#![doc = include_str!("../readme.md")]
2// #![cfg_attr(not(test), no_std)]
3#![feature(trait_alias)]
4// #![feature(generic_const_exprs)]
5#![warn(missing_docs)]
6#![deny(clippy::default_numeric_fallback)]
7
8mod ether_type;
9pub use ether_type::*;
10mod ethernet;
11pub use ethernet::*;
12mod ip_addr;
13pub use ip_addr::*;
14mod ip_protocol;
15pub use ip_protocol::*;
16mod ipv4;
17pub use ipv4::*;
18mod ipv6;
19pub use ipv6::*;
20mod tcp;
21pub use tcp::*;
22mod udp;
23pub use udp::*;
24
25macro_rules! pascal_name {
26  ($name:ident) => {
27    const_format::map_ascii_case!(const_format::Case::Pascal, core::stringify!($name))
28  };
29}
30
31macro_rules! display_variants {
32  ($struct_name:ident, $field_name:ident: $($variant_name:ident,)*) => {
33    impl core::fmt::Display for $struct_name {
34      fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
35        match self {
36          $(&Self::$variant_name => write!(f, "{}: {}", $crate::pascal_name!($variant_name), self.$field_name),)*
37          _ => write!(f, "Unknown: {}", self.$field_name()),
38        }
39      }
40    }
41  };
42}
43
44macro_rules! decl_variants {
45  ($($(#[$docs:meta])* $variant_name:ident => $variant_value:expr,)*) => {
46    $($(#[$docs])* pub const $variant_name: Self = Self::new($variant_value);)*
47  };
48}
49
50macro_rules! struct_variants {
51  ($struct_name:ident, $field_name:ident, $field_type:ty:
52    $($(#[$variant_docs:meta])* $variant_name:ident => $variant_value:expr,)*
53  ) => {
54    #[doc=stringify!($struct_name)]
55    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
56    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
57    #[repr(transparent)]
58    pub struct $struct_name {
59      $field_name: $field_type,
60    }
61
62    paste::paste! {
63      #[allow(non_camel_case_types)]
64      #[allow(dead_code)]
65      #[allow(clippy::upper_case_acronyms)]
66      enum [<Enum $struct_name>] {
67        $($variant_name = $variant_value,)*
68      }
69    }
70
71    impl $struct_name {
72      $crate::decl_variants!{$($(#[$variant_docs])* $variant_name => $variant_value,)*}
73
74      /// Return $struct_name from $field_type
75      pub const fn new($field_name: $field_type) -> Self {
76        Self { $field_name }
77      }
78
79      /// Return $field_type
80      pub const fn $field_name(&self) -> $field_type {
81        self.$field_name
82      }
83    }
84
85    impl From<$field_type> for $struct_name {
86      fn from($field_name: $field_type) -> Self {
87        Self::new($field_name)
88      }
89    }
90
91    impl From<$struct_name> for $field_type {
92      fn from(this: $struct_name) -> Self {
93        this.$field_name
94      }
95    }
96
97    impl core::str::FromStr for $struct_name {
98      type Err = ();
99      fn from_str(s: &str) -> Result<Self, Self::Err> {
100        match s {
101          $(core::stringify!($crate::pascal_name!($variant_name)) => Ok(Self::$variant_name),)*
102          _ => Err(()),
103        }
104      }
105    }
106
107    $crate::display_variants!{
108      $struct_name, $field_name:
109        $($variant_name,)*
110    }
111  };
112}
113
114pub(crate) use decl_variants;
115pub(crate) use display_variants;
116pub(crate) use pascal_name;
117pub(crate) use struct_variants;
118
119#[cfg(test)]
120mod tests {
121  use core::fmt::Debug;
122
123  use binator::{
124    base::{
125      all,
126      BaseAtom,
127      IntRadixAtom,
128    },
129    context::Tree,
130    utils::UtilsAtom,
131    CoreAtom,
132    Parse,
133    Streaming,
134    Success,
135  };
136  use derive_more::{
137    Display,
138    From,
139  };
140  use pretty_assertions::assert_eq;
141  use test_log::test;
142
143  use crate::{
144    ipv4_header,
145    tcp_header,
146    tcp_options,
147    Ipv4Atom,
148    TcpAtom,
149    TcpOption,
150  };
151
152  #[derive(Display, Debug, Clone, PartialEq, From)]
153  enum FromAtom<
154    Stream: Streaming + Debug,
155    Item: 'static = <Stream as Streaming>::Item,
156    Error = <Stream as Streaming>::Error,
157  > {
158    Core(CoreAtom<Stream, Error>),
159    Utils(UtilsAtom<Stream>),
160    Base(BaseAtom<Item>),
161    U8Radix(IntRadixAtom<u8>),
162    U16Radix(IntRadixAtom<u16>),
163    Tcp(TcpAtom),
164    Ipv4(Ipv4Atom),
165  }
166
167  type HandleAtom<Stream> = Tree<FromAtom<Stream>>;
168
169  #[test]
170  fn parse_tcp_packet() {
171    let bytes = [
172      0x45, 0x00, 0x00, 0x38, 0x76, 0xF4, 0x40, 0x00, 0x40, 0x06, 0x80, 0xD9, 0xC0, 0xA8, 0x00,
173      0x6C, 0xD0, 0x61, 0xB1, 0x7C, 0xB0, 0xC2, 0x00, 0x50, 0xB0, 0xEE, 0x32, 0xA6, 0x04, 0x39,
174      0xAE, 0xE6, 0x50, 0x18, 0x00, 0xE5, 0x76, 0x92, 0x00, 0x00, 0x47, 0x45, 0x54, 0x20, 0x2F,
175      0x69, 0x6E, 0x64, 0x65, 0x78, 0x2E, 0x68, 0x74, 0x6D, 0x6C, 0x0A,
176    ];
177
178    let Success {
179      token: (_ipv4_header, tcp_header, data),
180      stream: _,
181    } = (ipv4_header::<_, HandleAtom<_>>, tcp_header, all)
182      .parse(bytes.as_slice())
183      .unwrap();
184
185    assert_eq!(tcp_header.source_port, 45250);
186    assert_eq!(tcp_header.dest_port, 80);
187    assert_eq!(data, b"GET /index.html\x0a");
188  }
189
190  #[test]
191  fn parse_tcp_packet_with_options() {
192    let bytes = [
193      0x45, 0x20, 0x00, 0x34, 0x78, 0xD6, 0x40, 0x00, 0x35, 0x06, 0x7E, 0x77, 0x45, 0xA4, 0x10,
194      0x00, 0xC0, 0xA8, 0x38, 0x0A, 0x00, 0x50, 0xC2, 0x27, 0x48, 0xF3, 0x02, 0xC2, 0x61, 0xD3,
195      0x16, 0xA8, 0x80, 0x12, 0xFF, 0xFF, 0x9B, 0x80, 0x00, 0x00, 0x02, 0x04, 0x05, 0x3A, 0x01,
196      0x03, 0x03, 0x04, 0x04, 0x02, 0x00, 0x00,
197    ];
198
199    let Success {
200      token: (_ipv4_header, tcp_header),
201      stream,
202    } = (ipv4_header::<_, HandleAtom<_>>, tcp_header)
203      .parse(bytes.as_slice())
204      .unwrap();
205
206    assert_eq!(tcp_header.source_port, 80);
207    assert_eq!(tcp_header.dest_port, 49703);
208
209    assert_eq!(stream, b"");
210
211    let Success {
212      token: options,
213      stream,
214    } = tcp_options::<_, HandleAtom<_>>
215      .parse(tcp_header.options)
216      .unwrap();
217
218    // println!("{ipv4_header:#?}");
219    // println!("{tcp_header:#?}");
220    // println!("{options:#?}");
221
222    assert_eq!(options[0], TcpOption::MaximumSegmentSize(1338));
223    assert_eq!(options[1], TcpOption::Noop);
224    assert_eq!(options[2], TcpOption::WindowScale(4));
225    assert_eq!(options[3], TcpOption::SackPermitted);
226    assert_eq!(options[4], TcpOption::EndOfOption);
227    assert_eq!(options[5], TcpOption::EndOfOption);
228
229    assert_eq!(options.len(), 6);
230    assert_eq!(stream, b"");
231  }
232}