1#![doc = include_str!("../readme.md")]
2#![feature(trait_alias)]
4#![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 pub const fn new($field_name: $field_type) -> Self {
76 Self { $field_name }
77 }
78
79 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 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}