sawp_diameter/
lib.rs

1//! Protocol References:
2//!     https://tools.ietf.org/html/rfc6733
3
4use sawp::error::{NomError, Result};
5use sawp::parser::{Direction, Parse};
6use sawp::probe::Probe;
7use sawp::protocol::Protocol;
8use sawp_flags::{BitFlags, Flag, Flags};
9
10use nom::bytes::streaming::tag;
11use nom::bytes::streaming::take;
12use nom::combinator;
13use nom::error::ErrorKind;
14use nom::multi::many0;
15use nom::number::streaming::{be_u24, be_u32, be_u64, be_u8};
16use nom::IResult;
17
18use num_enum::TryFromPrimitive;
19use std::convert::TryFrom;
20use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
21
22#[derive(Debug)]
23pub struct Diameter {}
24
25#[derive(Debug, PartialEq, Eq)]
26pub struct Header {
27    version: u8,
28    length: u32, // Actually u24
29    flags: u8,
30    code: u32, // Actually u24
31    app_id: u32,
32    hop_id: u32,
33    end_id: u32,
34}
35
36/// AVP Attribute Names as stated in the [protocol reference](https://tools.ietf.org/html/rfc6733#section-4.5)
37#[derive(Debug, PartialEq, Eq, TryFromPrimitive)]
38#[repr(u32)]
39pub enum AttributeCode {
40    Unknown = 0,
41    AcctInterimInterval = 85,
42    AccountingRealtimeRequired = 483,
43    AcctMultiSessionId = 50,
44    AccountingRecordNumber = 485,
45    AccountingRecordType = 480,
46    AcctSessionId = 44,
47    AccountingSubSessionId = 287,
48    AcctApplicationId = 259,
49    AuthApplicationId = 258,
50    AuthRequestType = 274,
51    AuthorizationLifetime = 291,
52    AuthGracePeriod = 276,
53    AuthSessionState = 277,
54    ReAuthRequestType = 285,
55    Class = 25,
56    DestinationHost = 293,
57    DestinationRealm = 235,
58    DisconnectCause = 273,
59    ErrorMessage = 281,
60    ErrorReportingHost = 294,
61    EventTimestamp = 55,
62    ExperimentalResult = 297,
63    ExperimentalResultCode = 298,
64    FailedAVP = 279,
65    FirmwareRevision = 267,
66    HostIPAddress = 257,
67    InbandSecurityId = 299,
68    MultiRoundTimeOut = 272,
69    OriginHost = 264,
70    OriginRealm = 296,
71    OriginStateId = 278,
72    ProductName = 269,
73    ProxyHost = 280,
74    ProxyInfo = 284,
75    ProxyState = 33,
76    RedirectHost = 292,
77    RedirectHostUsage = 261,
78    RedirectMaxCacheTime = 262,
79    ResultCode = 268,
80    RouteRecord = 282,
81    SessionId = 263,
82    SessionTimeout = 27,
83    SessionBinding = 270,
84    SessionServerFailover = 271,
85    SupportedVendorId = 265,
86    TerminationCause = 295,
87    UserName = 1,
88    VendorId = 266,
89    VendorSpecificApplicationId = 260,
90}
91
92#[derive(Debug, PartialEq, Eq)]
93pub struct Attribute {
94    /// Value of the code in AVP header
95    raw: u32,
96    /// Attribute name associated with raw value
97    code: AttributeCode,
98}
99
100impl Attribute {
101    pub fn new(val: u32) -> Self {
102        Attribute {
103            raw: val,
104            code: AttributeCode::try_from(val).unwrap_or(AttributeCode::Unknown),
105        }
106    }
107}
108
109/// AVP Data Format as specified in the [protocol reference](https://tools.ietf.org/html/rfc6733#section-4.2)
110#[derive(Debug, PartialEq)]
111pub enum Value {
112    Unhandled(Vec<u8>),
113    OctetString(Vec<u8>),
114    Integer32(i32),
115    Integer64(i64),
116    Unsigned32(u32),
117    Unsigned64(u64),
118    Float32(f32),
119    Float64(f64),
120    Grouped(Vec<AVP>),
121    Enumerated(u32),
122    UTF8String(String),
123    DiameterIdentity(String),
124    DiameterURI(String),
125    Address(std::net::IpAddr),
126    Time(u32),
127}
128
129impl Value {
130    pub fn new<'a>(
131        code: &AttributeCode,
132        data: &'a [u8],
133    ) -> IResult<&'a [u8], (Self, Flags<ErrorFlags>)> {
134        match code {
135            AttributeCode::AcctSessionId | AttributeCode::ProxyState => {
136                Ok((&[], (Value::OctetString(data.into()), ErrorFlags::none())))
137            }
138            AttributeCode::AcctInterimInterval
139            | AttributeCode::AccountingRecordNumber
140            | AttributeCode::AcctApplicationId
141            | AttributeCode::AuthApplicationId
142            | AttributeCode::AuthorizationLifetime
143            | AttributeCode::AuthGracePeriod
144            | AttributeCode::ExperimentalResultCode
145            | AttributeCode::FirmwareRevision
146            | AttributeCode::InbandSecurityId
147            | AttributeCode::MultiRoundTimeOut
148            | AttributeCode::OriginStateId
149            | AttributeCode::RedirectMaxCacheTime
150            | AttributeCode::ResultCode
151            | AttributeCode::SessionTimeout
152            | AttributeCode::SessionBinding
153            | AttributeCode::SupportedVendorId
154            | AttributeCode::VendorId => {
155                let (input, val) = be_u32(data)?;
156                Ok((input, (Value::Unsigned32(val), ErrorFlags::none())))
157            }
158            AttributeCode::AccountingSubSessionId => {
159                let (input, val) = be_u64(data)?;
160                Ok((input, (Value::Unsigned64(val), ErrorFlags::none())))
161            }
162            AttributeCode::AccountingRealtimeRequired
163            | AttributeCode::AccountingRecordType
164            | AttributeCode::AuthRequestType
165            | AttributeCode::AuthSessionState
166            | AttributeCode::ReAuthRequestType
167            | AttributeCode::DisconnectCause
168            | AttributeCode::RedirectHostUsage
169            | AttributeCode::SessionServerFailover
170            | AttributeCode::TerminationCause => {
171                let (input, val) = be_u32(data)?;
172                Ok((input, (Value::Enumerated(val), ErrorFlags::none())))
173            }
174            AttributeCode::ExperimentalResult
175            | AttributeCode::FailedAVP
176            | AttributeCode::ProxyInfo
177            | AttributeCode::VendorSpecificApplicationId => {
178                let (input, (avps, error_flags)) = parse_avps(data)?;
179                Ok((input, (Value::Grouped(avps), error_flags)))
180            }
181            AttributeCode::AcctMultiSessionId
182            | AttributeCode::Class
183            | AttributeCode::ErrorMessage
184            | AttributeCode::ProductName
185            | AttributeCode::SessionId
186            | AttributeCode::UserName => match String::from_utf8(data.to_vec()) {
187                Ok(string) => Ok((&[], (Value::UTF8String(string), ErrorFlags::none()))),
188                Err(_) => Ok((
189                    &[],
190                    (Value::Unhandled(data.into()), ErrorFlags::DataValue.into()),
191                )),
192            },
193            AttributeCode::DestinationHost
194            | AttributeCode::DestinationRealm
195            | AttributeCode::ErrorReportingHost
196            | AttributeCode::OriginHost
197            | AttributeCode::OriginRealm
198            | AttributeCode::ProxyHost
199            | AttributeCode::RouteRecord => match String::from_utf8(data.to_vec()) {
200                Ok(string) => Ok((&[], (Value::DiameterIdentity(string), ErrorFlags::none()))),
201                Err(_) => Ok((
202                    &[],
203                    (Value::Unhandled(data.into()), ErrorFlags::DataValue.into()),
204                )),
205            },
206            AttributeCode::RedirectHost => match String::from_utf8(data.to_vec()) {
207                Ok(string) => Ok((&[], (Value::DiameterURI(string), ErrorFlags::none()))),
208                Err(_) => Ok((
209                    &[],
210                    (Value::Unhandled(data.into()), ErrorFlags::DataValue.into()),
211                )),
212            },
213            AttributeCode::HostIPAddress => match data.len() {
214                4 => Ok((
215                    &[],
216                    (
217                        // unwrap shouldn't panic, since we check length
218                        Value::Address(IpAddr::V4(Ipv4Addr::from(
219                            <[u8; 4]>::try_from(data).unwrap(),
220                        ))),
221                        ErrorFlags::none(),
222                    ),
223                )),
224                16 => Ok((
225                    &[],
226                    (
227                        // unwrap shouldn't panic, since we check length
228                        Value::Address(IpAddr::V6(Ipv6Addr::from(
229                            <[u8; 16]>::try_from(data).unwrap(),
230                        ))),
231                        ErrorFlags::none(),
232                    ),
233                )),
234                _ => Ok((
235                    &[],
236                    (Value::Unhandled(data.into()), ErrorFlags::DataLength.into()),
237                )),
238            },
239            AttributeCode::EventTimestamp => {
240                let (input, seconds) = be_u32(data)?;
241                Ok((input, (Value::Time(seconds), ErrorFlags::none())))
242            }
243            _ => Ok((&[], (Value::Unhandled(data.into()), ErrorFlags::none()))),
244        }
245    }
246}
247
248#[derive(Debug, PartialEq)]
249pub struct AVP {
250    attribute: Attribute,
251    flags: u8,
252    length: u32, // Actually u24
253    vendor_id: Option<u32>,
254    value: Value,
255    padding: Vec<u8>,
256}
257
258/// Flags identify messages which parse successfully
259/// but contain invalid data. The caller can use the message's
260/// error flags to see if and what errors were in the
261/// pack of bytes and take action using this information.
262#[repr(u8)]
263#[derive(Clone, Copy, Debug, PartialEq, Eq, BitFlags)]
264pub enum ErrorFlags {
265    DataValue = 0b0000_0001,
266    DataLength = 0b0000_0010,
267    NonZeroReserved = 0b0000_0100,
268    NonZeroPadding = 0b0000_1000,
269}
270
271#[derive(Debug, PartialEq)]
272pub struct Message {
273    pub header: Header,
274    pub avps: Vec<AVP>,
275    pub error_flags: Flags<ErrorFlags>,
276}
277
278/// Create a parser to read diameter length and ensure input is long enough
279/// # Arguments
280/// * `read` - How many bytes of length have already been read
281///
282fn length(read: usize) -> impl Fn(&[u8]) -> IResult<&[u8], u32> {
283    move |input: &[u8]| {
284        let (input, length) = be_u24(input)?;
285        let len = length as usize;
286        if len < read {
287            Err(nom::Err::Error(NomError::new(
288                input,
289                ErrorKind::LengthValue,
290            )))
291        } else if len > (input.len() + read) {
292            Err(nom::Err::Incomplete(nom::Needed::new(
293                len - (input.len() + read),
294            )))
295        } else {
296            Ok((input, length))
297        }
298    }
299}
300
301impl Header {
302    const SIZE: usize = 20;
303    // Number of bytes included in length that are before and
304    // including the length field
305    const PRE_LENGTH_SIZE: usize = 4;
306
307    // Flags
308    pub const REQUEST_FLAG: u8 = 0b1000_0000;
309    pub const PROXIABLE_FLAG: u8 = 0b0100_0000;
310    pub const ERROR_FLAG: u8 = 0b0010_0000;
311    pub const POTENTIALLY_RETRANSMITTED_FLAG: u8 = 0b0001_0000;
312    pub const RESERVED_MASK: u8 = 0b0000_1111;
313
314    fn reserved_set(flags: u8) -> bool {
315        flags & Self::RESERVED_MASK != 0
316    }
317
318    ///  If set, the message is a request.  If cleared, the message is
319    /// an answer.
320    pub fn is_request(&self) -> bool {
321        self.flags & Self::REQUEST_FLAG != 0
322    }
323
324    /// If set, the message MAY be proxied, relayed, or redirected.  If
325    /// cleared, the message MUST be locally processed.
326    pub fn is_proxiable(&self) -> bool {
327        self.flags & Self::PROXIABLE_FLAG != 0
328    }
329
330    /// If set, the message contains a protocol error, and the message
331    /// will not conform to the CCF described for this command.
332    /// Messages with the 'E' bit set are commonly referred to as error
333    /// messages.  This bit MUST NOT be set in request messages
334    pub fn is_error(&self) -> bool {
335        self.flags & Self::ERROR_FLAG != 0
336    }
337
338    /// This flag is set after a link failover procedure, to aid the
339    /// removal of duplicate requests.  It is set when resending
340    /// requests not yet acknowledged, as an indication of a possible
341    /// duplicate due to a link failure.
342    pub fn is_potentially_retransmitted(&self) -> bool {
343        self.flags & Self::POTENTIALLY_RETRANSMITTED_FLAG != 0
344    }
345
346    /// These flag bits are reserved for future use; they MUST be set
347    /// to zero and ignored by the receiver.
348    pub fn get_reserved(&self) -> u8 {
349        self.flags & Self::RESERVED_MASK
350    }
351
352    /// Length of AVPs
353    pub fn length(&self) -> usize {
354        (self.length as usize) - Self::SIZE
355    }
356
357    pub fn parse(input: &[u8]) -> IResult<&[u8], (Self, Flags<ErrorFlags>)> {
358        let mut error_flags = ErrorFlags::none();
359        let (input, version) = tag(&[1u8])(input)?;
360        let (input, length) = length(Self::PRE_LENGTH_SIZE)(input)?;
361        if (length as usize) < Self::SIZE {
362            return Err(nom::Err::Error(NomError::new(
363                input,
364                ErrorKind::LengthValue,
365            )));
366        }
367        let (input, flags) = be_u8(input)?;
368        if Self::reserved_set(flags) {
369            error_flags |= ErrorFlags::NonZeroReserved;
370        }
371        let (input, code) = be_u24(input)?;
372        let (input, app_id) = be_u32(input)?;
373        let (input, hop_id) = be_u32(input)?;
374        let (input, end_id) = be_u32(input)?;
375
376        Ok((
377            input,
378            (
379                Self {
380                    version: version[0],
381                    length,
382                    flags,
383                    code,
384                    app_id,
385                    hop_id,
386                    end_id,
387                },
388                error_flags,
389            ),
390        ))
391    }
392}
393
394impl AVP {
395    // Number of bytes included in length that are before and
396    // including the length field
397    const PRE_LENGTH_SIZE: usize = 8;
398
399    // Flags
400    pub const VENDOR_SPECIFIC_FLAG: u8 = 0b1000_0000;
401    pub const MANDATORY_FLAG: u8 = 0b0100_0000;
402    pub const PROTECTED_FLAG: u8 = 0b0010_0000;
403    pub const RESERVED_MASK: u8 = 0b0001_1111;
404
405    fn vendor_specific_flag(flags: u8) -> bool {
406        flags & Self::VENDOR_SPECIFIC_FLAG != 0
407    }
408
409    fn reserved_set(flags: u8) -> bool {
410        flags & Self::RESERVED_MASK != 0
411    }
412
413    fn padding(length: usize) -> usize {
414        match length % 4 {
415            0 => 0,
416            n => 4 - n,
417        }
418    }
419
420    /// The 'V' bit, known as the Vendor-Specific bit, indicates whether
421    /// the optional Vendor-ID field is present in the AVP header.  When
422    /// set, the AVP Code belongs to the specific vendor code address
423    /// space.
424    pub fn is_vendor_specific(&self) -> bool {
425        Self::vendor_specific_flag(self.flags)
426    }
427
428    /// The 'M' bit, known as the Mandatory bit, indicates whether the
429    /// receiver of the AVP MUST parse and understand the semantics of the
430    /// AVP including its content.
431    pub fn is_mandatory(&self) -> bool {
432        self.flags & Self::MANDATORY_FLAG != 0
433    }
434
435    /// The 'P' bit, known as the Protected bit, has been reserved for
436    /// future usage of end-to-end security
437    pub fn is_protected(&self) -> bool {
438        self.flags & Self::PROTECTED_FLAG != 0
439    }
440
441    /// The sender of the AVP MUST set 'R' (reserved) bits to 0 and the
442    /// receiver SHOULD ignore all 'R' (reserved) bits.
443    pub fn get_reserved(&self) -> u8 {
444        self.flags & Self::RESERVED_MASK
445    }
446
447    pub fn parse(input: &[u8]) -> IResult<&[u8], (Self, Flags<ErrorFlags>)> {
448        let mut error_flags = ErrorFlags::none();
449        let (input, raw_code) = be_u32(input)?;
450        let (input, flags) = be_u8(input)?;
451        if Self::reserved_set(flags) {
452            error_flags |= ErrorFlags::NonZeroReserved;
453        }
454        let (input, length) = length(Self::PRE_LENGTH_SIZE)(input)?;
455        let header_size = if Self::vendor_specific_flag(flags) {
456            Self::PRE_LENGTH_SIZE + 4
457        } else {
458            Self::PRE_LENGTH_SIZE
459        };
460        if (length as usize) < header_size {
461            return Err(nom::Err::Error(NomError::new(
462                input,
463                ErrorKind::LengthValue,
464            )));
465        }
466        let data_length = (length as usize) - header_size;
467        let (input, vendor_id) = if Self::vendor_specific_flag(flags) {
468            let (input, v) = be_u32(input)?;
469            (input, Some(v))
470        } else {
471            (input, None)
472        };
473
474        let (input, data) = take(data_length)(input)?;
475        let (input, padding) = take(Self::padding(data_length))(input)?;
476        if !padding.iter().all(|&item| item == 0) {
477            error_flags |= ErrorFlags::NonZeroPadding;
478        }
479        let attribute = Attribute::new(raw_code);
480        let value = match Value::new(&attribute.code, data) {
481            Ok((rest, (value, flags))) => {
482                if !rest.is_empty() {
483                    error_flags |= ErrorFlags::DataLength;
484                }
485                error_flags |= flags;
486                value
487            }
488            Err(nom::Err::Error(NomError {
489                input: _,
490                code: ErrorKind::LengthValue,
491            }))
492            | Err(nom::Err::Incomplete(_)) => {
493                error_flags |= ErrorFlags::DataLength;
494                Value::Unhandled(data.into())
495            }
496            Err(_) => {
497                error_flags |= ErrorFlags::DataValue;
498                Value::Unhandled(data.into())
499            }
500        };
501
502        Ok((
503            input,
504            (
505                Self {
506                    attribute,
507                    flags,
508                    length,
509                    vendor_id,
510                    value,
511                    padding: padding.into(),
512                },
513                error_flags,
514            ),
515        ))
516    }
517}
518
519impl Protocol<'_> for Diameter {
520    type Message = Message;
521
522    fn name() -> &'static str {
523        "diameter"
524    }
525}
526
527fn parse_avps(input: &[u8]) -> IResult<&[u8], (Vec<AVP>, Flags<ErrorFlags>)> {
528    let (rest, avps_flags) = many0(combinator::complete(AVP::parse))(input)?;
529    if !rest.is_empty() {
530        // many0 will stop if subparser fails, but should read all
531        Err(nom::Err::Error(NomError::new(input, ErrorKind::Many0)))
532    } else {
533        let mut error_flags = ErrorFlags::none();
534        let mut avps = Vec::new();
535        for (avp, flag) in avps_flags {
536            error_flags |= flag;
537            avps.push(avp)
538        }
539
540        Ok((rest, (avps, error_flags)))
541    }
542}
543
544impl<'a> Parse<'a> for Diameter {
545    fn parse(
546        &self,
547        input: &'a [u8],
548        _direction: Direction,
549    ) -> Result<(&'a [u8], Option<Self::Message>)> {
550        let mut error_flags = ErrorFlags::none();
551        let (input, (header, flags)) = Header::parse(input)?;
552        error_flags |= flags;
553
554        // Don't have to worry about splitting slice causing incomplete
555        // Because we have verified the length in Header::parse
556        let (input, avps_input) = combinator::complete(take(header.length()))(input)?;
557        let (_, (avps, flags)) = parse_avps(avps_input)?;
558        error_flags |= flags;
559        Ok((
560            input,
561            Some(Message {
562                header,
563                avps,
564                error_flags,
565            }),
566        ))
567    }
568}
569
570impl<'a> Probe<'a> for Diameter {}
571
572#[cfg(test)]
573mod tests {
574    use super::*;
575    use rstest::rstest;
576    use sawp::error;
577    use sawp::probe::Status;
578
579    #[test]
580    fn test_name() {
581        assert_eq!(Diameter::name(), "diameter");
582    }
583
584    #[rstest(
585        input,
586        expected,
587        case::empty(b"", Err(nom::Err::Incomplete(nom::Needed::new(1)))),
588        case::hello_world(b"hello world", Err(nom::Err::Error(NomError::new(b"hello world" as &[u8], ErrorKind::Tag)))),
589        case::invalid_length(
590            &[
591                // Version: 1
592                0x01,
593                // Length: 12
594                0x00, 0x00, 0x0c,
595                // Flags: 128 (Request)
596                0x80,
597                // Code: 257 (Capability-Exchange)
598                0x00, 0x01, 0x01,
599                // Application ID: 0 (Diameter Common Messages)
600                0x00, 0x00, 0x00, 0x00,
601                // Hop-by-Hop ID: 0x53cafe6a
602                0x53, 0xca, 0xfe, 0x6a,
603                // End-to-End ID: 0x7dc0a11b
604                0x7d, 0xc0, 0xa1, 0x1b,
605            ],
606            Err(nom::Err::Error(NomError::new(
607                &[
608                    // Flags: 128 (Request)
609                    0x80_u8,
610                    // Code: 257 (Capability-Exchange)
611                    0x00, 0x01, 0x01,
612                    // Application ID: 0 (Diameter Common Messages)
613                    0x00, 0x00, 0x00, 0x00,
614                    // Hop-by-Hop ID: 0x53cafe6a
615                    0x53, 0xca, 0xfe, 0x6a,
616                    // End-to-End ID: 0x7dc0a11b
617                    0x7d, 0xc0, 0xa1, 0x1b,
618                ] as &[u8],
619                ErrorKind::LengthValue))
620            )
621        ),
622        case::diagnostic(
623            &[
624                // Version: 1
625                0x01,
626                // Length: 20
627                0x00, 0x00, 0x14,
628                // Flags: 128 (Request)
629                0x80,
630                // Code: 257 (Capability-Exchange)
631                0x00, 0x01, 0x01,
632                // Application ID: 0 (Diameter Common Messages)
633                0x00, 0x00, 0x00, 0x00,
634                // Hop-by-Hop ID: 0x53cafe6a
635                0x53, 0xca, 0xfe, 0x6a,
636                // End-to-End ID: 0x7dc0a11b
637                0x7d, 0xc0, 0xa1, 0x1b,
638            ],
639            Ok((&[] as &[u8],
640            (
641                Header {
642                    version: 1,
643                    length: 20,
644                    flags: 128,
645                    code: 257,
646                    app_id: 0,
647                    hop_id: 0x53ca_fe6a,
648                    end_id: 0x7dc0_a11b,
649                },
650                ErrorFlags::none(),
651            )))
652        ),
653        case::reserved_set(
654            &[
655                // Version: 1
656                0x01,
657                // Length: 20
658                0x00, 0x00, 0x14,
659                // Flags: 128 (Request)
660                0x0f,
661                // Code: 257 (Capability-Exchange)
662                0x00, 0x01, 0x01,
663                // Application ID: 0 (Diameter Common Messages)
664                0x00, 0x00, 0x00, 0x00,
665                // Hop-by-Hop ID: 0x53cafe6a
666                0x53, 0xca, 0xfe, 0x6a,
667                // End-to-End ID: 0x7dc0a11b
668                0x7d, 0xc0, 0xa1, 0x1b,
669            ],
670            Ok((&[] as &[u8],
671            (
672                Header {
673                    version: 1,
674                    length: 20,
675                    flags: 15,
676                    code: 257,
677                    app_id: 0,
678                    hop_id: 0x53ca_fe6a,
679                    end_id: 0x7dc0_a11b,
680                },
681                ErrorFlags::NonZeroReserved.into(),
682            )))
683        ),
684        case::diagnostic(
685            &[
686                // Version: 1
687                0x01,
688                // Length: 24
689                0x00, 0x00, 0x18,
690                // Flags: 128 (Request)
691                0x80,
692                // Code: 257 (Capability-Exchange)
693                0x00, 0x01, 0x01,
694                // Application ID: 0 (Diameter Common Messages)
695                0x00, 0x00, 0x00, 0x00,
696                // Hop-by-Hop ID: 0x53cafe6a
697                0x53, 0xca, 0xfe, 0x6a,
698                // End-to-End ID: 0x7dc0a11b
699                0x7d, 0xc0, 0xa1, 0x1b,
700            ],
701            Err(nom::Err::Incomplete(nom::Needed::new(4)))
702        ),
703    )]
704    fn test_header(input: &[u8], expected: IResult<&[u8], (Header, Flags<ErrorFlags>)>) {
705        assert_eq!(Header::parse(input), expected);
706    }
707
708    #[rstest(
709        input,
710        expected,
711        case::empty(b"", Err(nom::Err::Incomplete(nom::Needed::new(4)))),
712        case::diagnostic(
713            &[
714                // Code: 264 (Origin-Host)
715                0x00, 0x00, 0x01, 0x08,
716                // Flags: 40 (Mandatory)
717                0x40,
718                // Length: 31
719                0x00, 0x00, 0x1f,
720                // Data: "backend.eap.testbed.aaa"
721                0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e,
722                0x65, 0x61, 0x70, 0x2e, 0x74, 0x65, 0x73, 0x74,
723                0x62, 0x65, 0x64, 0x2e, 0x61, 0x61, 0x61,
724                // Padding: 1
725                0x00,
726            ],
727            Ok((&[] as &[u8],
728            (
729                AVP {
730                    attribute: Attribute {
731                        raw: 264,
732                        code: AttributeCode::OriginHost,
733                    },
734                    flags: 0x40,
735                    length: 31,
736                    vendor_id: None,
737                    value: Value::DiameterIdentity("backend.eap.testbed.aaa".into()),
738                    padding: vec![0x00],
739                },
740                ErrorFlags::none(),
741            )))
742        ),
743        case::diagnostic_vendor_id(
744            &[
745                // Code: 264 (Origin-Host)
746                0x00, 0x00, 0x01, 0x08,
747                // Flags: 0x80 (Vendor-Id)
748                0x80,
749                // Length: 12
750                0x00, 0x00, 0x0c,
751                // Vendor-Id: 1234567890
752                0x49, 0x96, 0x02, 0xd2,
753                // Data:
754                // Padding:
755            ],
756            Ok((&[] as &[u8],
757            (
758                AVP {
759                    attribute: Attribute {
760                        raw: 264,
761                        code: AttributeCode::OriginHost,
762                    },
763                    flags: 0x80,
764                    length: 12,
765                    vendor_id: Some(1_234_567_890u32),
766                    value: Value::DiameterIdentity("".into()),
767                    padding: Vec::new(),
768                },
769                ErrorFlags::none(),
770            )))
771        ),
772        case::unsigned_32_format(
773            &[
774               // Code: 266 (Vendor-Id)
775               0x00, 0x00, 0x01, 0x0a,
776               // Flags: 0x00
777               0x00,
778               // Length: 13,
779               0x00, 0x00, 0x0d,
780               // Vendor-Id:
781               // Data:
782               0x00, 0x00, 0x00, 0x7b,
783               0x01,
784               // Padding
785               0x00, 0x00, 0x00,
786            ],
787            Ok((&[] as &[u8],
788            (
789                AVP {
790                    attribute: Attribute {
791                        raw: 266,
792                        code: AttributeCode::VendorId,
793                    },
794                    flags: 0x00,
795                    length: 13,
796                    vendor_id: None,
797                    value: Value::Unsigned32(123),
798
799                    padding: vec![0x00, 0x00, 0x00],
800                },
801                ErrorFlags::DataLength.into(),
802            )))
803        ),
804        case::unsigned_64_format(
805            &[
806               // Code: 287 (Accouting-Realtime-Required)
807               0x00, 0x00, 0x01, 0x1f,
808               // Flags: 0x00
809               0x00,
810               // Length: 16,
811               0x00, 0x00, 0x10,
812               // Vendor-Id:
813               // Data:
814               0x00, 0x00, 0x00, 0x7B,
815               0x01, 0x02, 0x02, 0x03,
816            ],
817            Ok((&[] as &[u8],
818            (
819                AVP {
820                    attribute: Attribute {
821                        raw: 287,
822                        code: AttributeCode::AccountingSubSessionId,
823                    },
824                    flags: 0x00,
825                    length: 16,
826                    vendor_id: None,
827                    value: Value::Unsigned64(528_297_886_211),
828                    padding: Vec::new(),
829                },
830                ErrorFlags::none(),
831            )))
832        ),
833        case::enumerated_format(
834            &[
835               // Code: 483 (Accounting-Realtime-Required)
836               0x00, 0x00, 0x01, 0xe3,
837               // Flags: 0x00
838               0x00,
839               // Length: 12,
840               0x00, 0x00, 0x0c,
841               // Vendor-Id:
842               // Data: Grant-And-Store (2)
843               0x00, 0x00, 0x00, 0x02,
844            ],
845            Ok((&[] as &[u8],
846            (
847                AVP {
848                    attribute: Attribute {
849                        raw: 483,
850                        code: AttributeCode::AccountingRealtimeRequired,
851                    },
852                    flags: 0x00,
853                    length: 12,
854                    vendor_id: None,
855                    value: Value::Enumerated(2),
856                    padding: Vec::new(),
857                },
858                ErrorFlags::none(),
859            )))
860        ),
861        case::octet_string_format(
862            &[
863               // Code: 44 (AcctSessionId)
864               0x00, 0x00, 0x00, 0x2c,
865               // Flags: 0x00
866               0x00,
867               // Length: 15,
868               0x00, 0x00, 0x0f,
869               // Vendor-Id:
870               // Data:
871               0x01, 0x02, 0x03, 0x04,
872               0x05, 0x06, 0x07,
873               // Padding:
874               0xef,
875            ],
876            Ok((&[] as &[u8],
877            (
878                AVP {
879                    attribute: Attribute {
880                        raw: 44,
881                        code: AttributeCode::AcctSessionId,
882                    },
883                    flags: 0x00,
884                    length: 15,
885                    vendor_id: None,
886                    value: Value::OctetString(vec![0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]),
887                    padding: vec![0xef],
888                },
889                ErrorFlags::NonZeroPadding.into(),
890            )))
891        ),
892        case::utf8_string_format(
893            &[
894               // Code: 1 (Username)
895               0x00, 0x00, 0x00, 0x01,
896               // Flags: 0x00
897               0x00,
898               // Length: 20,
899               0x00, 0x00, 0x14,
900               // Vendor-Id:
901               // Data: Hello World!
902               0x48, 0x65, 0x6c, 0x6c,
903               0x6f, 0x20, 0x57, 0x6f,
904               0x72, 0x6c, 0x64, 0x21,
905            ],
906            Ok((&[] as &[u8],
907            (
908                AVP {
909                    attribute: Attribute {
910                        raw: 1,
911                        code: AttributeCode::UserName,
912                    },
913                    flags: 0x00,
914                    length: 20,
915                    vendor_id: None,
916                    value: Value::UTF8String("Hello World!".into()),
917                    padding: Vec::new(),
918                },
919                ErrorFlags::none(),
920            )))
921        ),
922        case::diameter_uri_format(
923            &[
924               // Code: 292 (RedirectHost)
925               0x00, 0x00, 0x01, 0x24,
926               // Flags: 0x00
927               0x00,
928                   // Length: 19,
929               0x00, 0x00, 0x13,
930               // Vendor-Id:
931            // Data: example.com
932            0x65, 0x78, 0x61, 0x6d,
933            0x70, 0x6c, 0x65, 0x2e,
934            0x63, 0x6f, 0x6d,
935            // Padding:
936            0x00,
937            ],
938            Ok((&[] as &[u8],
939            (
940                AVP {
941                    attribute: Attribute {
942                        raw: 292,
943                        code: AttributeCode::RedirectHost,
944                    },
945                    flags: 0x00,
946                    length: 19,
947                    vendor_id: None,
948                    value: Value::DiameterURI("example.com".into()),
949                    padding: vec![0x00],
950                },
951                ErrorFlags::none(),
952            )))
953        ),
954        case::address_v4_format(
955            &[
956               // Code: 257 (HostIPAddress)
957               0x00, 0x00, 0x01, 0x01,
958               // Flags: 0x0f
959               0x0f,
960               // Length: 12,
961               0x00, 0x00, 0x0c,
962               // Vendor-Id:
963               // Data: 10.10.0.1
964               0x0a, 0x0a, 0x00, 0x01,
965            ],
966            Ok((&[] as &[u8],
967            (
968                AVP {
969                    attribute: Attribute {
970                        raw: 257,
971                        code: AttributeCode::HostIPAddress,
972                    },
973                    flags: 0x0f,
974                    length: 12,
975                    vendor_id: None,
976                    value: Value::Address(IpAddr::V4(Ipv4Addr::new(10, 10, 0, 1))),
977                    padding: Vec::new(),
978                },
979                ErrorFlags::NonZeroReserved.into(),
980            )))
981        ),
982        case::address_v6_format(
983            &[
984               // Code: 257 (HostIPAddress)
985               0x00, 0x00, 0x01, 0x01,
986               // Flags: 0x00
987               0x00,
988               // Length: 24,
989               0x00, 0x00, 0x18,
990               // Vendor-Id:
991               // Data: 2001:0db8:85a3:0000:0000:8a2e:0370:7334
992               0x20, 0x01, 0x0d, 0xb8,
993               0x85, 0xa3, 0x00, 0x00,
994               0x00, 0x00, 0x8a, 0x2e,
995               0x03, 0x70, 0x73, 0x34,
996            ],
997            Ok((&[] as &[u8],
998            (
999                AVP {
1000                    attribute: Attribute {
1001                        raw: 257,
1002                        code: AttributeCode::HostIPAddress,
1003                    },
1004                    flags: 0x00,
1005                    length: 24,
1006                    vendor_id: None,
1007                    value: Value::Address(IpAddr::V6(Ipv6Addr::new(
1008                                0x2001, 0x0db8, 0x85a3, 0x0000,
1009                                0x0000, 0x8a2e, 0x0370, 0x7334))),
1010                    padding: Vec::new(),
1011                },
1012                ErrorFlags::none(),
1013            )))
1014        ),
1015        case::time_format(
1016            &[
1017               // Code: 55 (EventTimestamp)
1018               0x00, 0x00, 0x00, 0x37,
1019               // Flags: 0x00
1020               0x00,
1021               // Length: 12,
1022               0x00, 0x00, 0x0c,
1023               // Vendor-Id:
1024               // Data: 3794601600 (March 31, 2021)
1025               0xe2, 0x2d, 0x06, 0x80
1026            ],
1027            Ok((&[] as &[u8],
1028            (
1029                AVP {
1030                    attribute: Attribute {
1031                        raw: 55,
1032                        code: AttributeCode::EventTimestamp,
1033                    },
1034                    flags: 0x00,
1035                    length: 12,
1036                    vendor_id: None,
1037                    value: Value::Time(3_794_601_600),
1038                    padding: Vec::new(),
1039                },
1040                ErrorFlags::none(),
1041            )))
1042        ),
1043        case::grouped_format(
1044            &[
1045            // Code: 297 (ExperimentalResult)
1046            0x00, 0x00, 0x01, 0x29,
1047            // Flags: 0x00
1048            0x00,
1049            // Length: 44,
1050            0x00, 0x00, 0x2c,
1051            // Vendor-Id:
1052            // Data:
1053
1054            // AVPs[0]
1055            // Code: 264 (OriginHost)
1056            0x00, 0x00, 0x01, 0x08,
1057            // Flags: 0x00
1058            0x00,
1059            // Length: 19,
1060            0x00, 0x00, 0x13,
1061            // Vendor-Id:
1062            // Data: example.com
1063            0x65, 0x78, 0x61, 0x6d,
1064            0x70, 0x6c, 0x65, 0x2e,
1065            0x63, 0x6f, 0x6d,
1066            // Padding:
1067            0x01,
1068
1069            // AVPs[1]
1070            // Code: 44 ( AcctSessionId)
1071            0x00, 0x00, 0x00, 0x2c,
1072            // Flags: 0x0f,
1073            0x0f,
1074            // Length: 15,
1075            0x00, 0x00, 0x0f,
1076            // Vendor-Id:
1077            // Data:
1078            0x01, 0x02, 0x03, 0x04,
1079            0x05, 0x06, 0x07,
1080            // Padding:
1081            0x00,
1082            ],
1083            Ok((&[] as &[u8],
1084            (
1085                AVP {
1086                    attribute: Attribute {
1087                        raw: 297,
1088                        code: AttributeCode::ExperimentalResult,
1089                    },
1090                    flags: 0x00,
1091                    length: 44,
1092                    vendor_id: None,
1093                    value: Value::Grouped(vec![
1094                        AVP {
1095                            attribute: Attribute {
1096                                raw: 264,
1097                                code: AttributeCode::OriginHost,
1098                            },
1099                            flags: 0x00,
1100                            length: 19,
1101                            vendor_id: None,
1102                            value: Value::DiameterIdentity("example.com".into()),
1103                            padding: vec![0x01],
1104                        },
1105                        AVP {
1106                            attribute: Attribute {
1107                                raw: 44,
1108                                code: AttributeCode::AcctSessionId,
1109                            },
1110                            flags: 0x0f,
1111                            length: 15,
1112                            vendor_id: None,
1113                            value: Value::OctetString(vec![0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]),
1114                            padding: vec![0x00],
1115                        }]),
1116                    padding: Vec::new(),
1117                },
1118                ErrorFlags::NonZeroPadding | ErrorFlags::NonZeroReserved
1119            )))
1120        ),
1121        case::invalid_utf8(
1122            &[
1123               // Code: 1 (Username)
1124               0x00, 0x00, 0x00, 0x01,
1125               // Flags: 0x00
1126               0x00,
1127               // Length: 12,
1128               0x00, 0x00, 0x0c,
1129               // Vendor-Id:
1130               // Data:
1131               0xfe, 0xfe, 0xff, 0xff,
1132            ],
1133            Ok((&[] as &[u8],
1134            (
1135                AVP {
1136                    attribute: Attribute {
1137                        raw: 1,
1138                        code: AttributeCode::UserName,
1139                    },
1140                    flags: 0x00,
1141                    length: 12,
1142                    vendor_id: None,
1143                    value: Value::Unhandled(vec![0xfe, 0xfe, 0xff, 0xff]),
1144                    padding: Vec::new(),
1145                },
1146                ErrorFlags::DataValue.into(),
1147            )))
1148        ),
1149        case::invalid_address(
1150            &[
1151               // Code: 257 (HostIPAddress)
1152               0x00, 0x00, 0x01, 0x01,
1153               // Flags: 0x00
1154               0x00,
1155               // Length: 13,
1156               0x00, 0x00, 0x0d,
1157               // Vendor-Id:
1158               // Data: 10.10.0.1.1 (Invalid)
1159               0x0a, 0x0a, 0x00, 0x01, 0x01,
1160               // Padding
1161               0x00, 0x00, 0x00,
1162            ],
1163            Ok((&[] as &[u8],
1164            (
1165                AVP {
1166                    attribute: Attribute {
1167                        raw: 257,
1168                        code: AttributeCode::HostIPAddress,
1169                    },
1170                    flags: 0x00,
1171                    length: 13,
1172                    vendor_id: None,
1173                    value: Value::Unhandled(vec![0x0a, 0x0a, 0x00, 0x01, 0x01]),
1174                    padding: vec![0x00, 0x00, 0x00],
1175                },
1176                ErrorFlags::DataLength.into(),
1177            )))
1178        ),
1179        case::unhandled(
1180            &[
1181               // Code: 2
1182               0x00, 0x00, 0x00, 0x02,
1183               // Flags: 0x00
1184               0x00,
1185               // Length: 13,
1186               0x00, 0x00, 0x0d,
1187               // Vendor-Id:
1188               // Data: 10.10.0.1.1 (Invalid)
1189               0x0a, 0x0a, 0x00, 0x01, 0x01,
1190               // Padding
1191               0x00, 0x00, 0x00,
1192            ],
1193            Ok((&[] as &[u8],
1194            (
1195                    AVP {
1196                    attribute: Attribute {
1197                        raw: 2,
1198                        code: AttributeCode::Unknown,
1199                    },
1200                    flags: 0x00,
1201                    length: 13,
1202                    vendor_id: None,
1203                    value: Value::Unhandled(vec![0x0a, 0x0a, 0x00, 0x01, 0x01]),
1204                    padding: vec![0x00, 0x00, 0x00],
1205                },
1206                ErrorFlags::none(),
1207            )))
1208        )
1209    )]
1210    fn test_avp(input: &[u8], expected: IResult<&[u8], (AVP, Flags<ErrorFlags>)>) {
1211        assert_eq!(AVP::parse(input), expected);
1212    }
1213
1214    #[rstest(
1215        input,
1216        expected,
1217        case::empty(b"", Err(error::Error::incomplete_needed(1))),
1218        case::header(
1219        &[
1220            // Version: 1
1221            0x01,
1222            // Length: 20
1223            0x00, 0x00, 0x14,
1224            // Flags: 128 (Request)
1225            0x80,
1226            // Code: 257 (Capability-Exchange)
1227            0x00, 0x01, 0x01,
1228            // Application ID: 0 (Diameter Common Messages)
1229            0x00, 0x00, 0x00, 0x00,
1230            // Hop-by-Hop ID: 0x53cafe6a
1231            0x53, 0xca, 0xfe, 0x6a,
1232            // End-to-End ID: 0x7dc0a11b
1233            0x7d, 0xc0, 0xa1, 0x1b,
1234            ],
1235            Ok((&[] as &[u8],
1236                Some(Message {
1237                    header: Header {
1238                        version: 1,
1239                        length: 20,
1240                        flags: 128,
1241                        code: 257,
1242                        app_id: 0,
1243                        hop_id: 0x53ca_fe6a,
1244                        end_id: 0x7dc0_a11b,
1245                    },
1246                    avps: Vec::new(),
1247                    error_flags: ErrorFlags::none(),
1248                })
1249            ))
1250        ),
1251        case::full_message(
1252        &[
1253            // Header
1254            // Version: 1
1255            0x01,
1256            // Length: 64
1257            0x00, 0x00, 0x40,
1258            // Flags: 128 (Request)
1259            0x8f,
1260            // Code: 257 (Capability-Exchange)
1261            0x00, 0x01, 0x01,
1262            // Application ID: 0 (Diameter Common Messages)
1263            0x00, 0x00, 0x00, 0x00,
1264            // Hop-by-Hop ID: 0x53cafe6a
1265            0x53, 0xca, 0xfe, 0x6a,
1266            // End-to-End ID: 0x7dc0a11b
1267            0x7d, 0xc0, 0xa1, 0x1b,
1268
1269            //AVPs[0]
1270            // Code: 264 (Origin-Host)
1271            0x00, 0x00, 0x01, 0x08,
1272            // Flags: 40 (Mandatory)
1273            0x40,
1274            // Length: 31
1275            0x00, 0x00, 0x1f,
1276            // Data: "backend.eap.testbed.aaa"
1277            0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e,
1278            0x65, 0x61, 0x70, 0x2e, 0x74, 0x65, 0x73, 0x74,
1279            0x62, 0x65, 0x64, 0x2e, 0x61, 0x61, 0x61,
1280            // Padding: 1
1281            0x01,
1282
1283            // AVPS[1]
1284            // Code: 264 (Origin-Host)
1285            0x00, 0x00, 0x01, 0x08,
1286            // Flags: 0x80 (Vendor-Id)
1287            0x80,
1288            // Length: 12
1289            0x00, 0x00, 0x0c,
1290            // Vendor-Id: 1234567890
1291            0x49, 0x96, 0x02, 0xd2,
1292            // Data:
1293            // Padding:
1294        ],
1295        Ok((&[] as &[u8],
1296            Some(Message {
1297                header: Header {
1298                    version: 1,
1299                    length: 64,
1300                    flags: 143,
1301                    code: 257,
1302                    app_id: 0,
1303                    hop_id: 0x53ca_fe6a,
1304                    end_id: 0x7dc0_a11b,
1305                },
1306                avps: vec![
1307                    AVP {
1308                        attribute: Attribute {
1309                            raw: 264,
1310                            code: AttributeCode::OriginHost,
1311                        },
1312                        flags: 0x40,
1313                        length: 31,
1314                        vendor_id: None,
1315                        value: Value::DiameterIdentity("backend.eap.testbed.aaa".into()),
1316                        padding: vec![0x01],
1317                    },
1318                    AVP {
1319                        attribute: Attribute {
1320                            raw: 264,
1321                            code: AttributeCode::OriginHost,
1322                        },
1323                        flags: 0x80,
1324                        length: 12,
1325                        vendor_id: Some(1_234_567_890u32),
1326                        value: Value::DiameterIdentity("".into()),
1327                        padding: Vec::new(),
1328                    },
1329                ],
1330                error_flags : ErrorFlags::NonZeroReserved | ErrorFlags::NonZeroPadding,
1331            })
1332        ))),
1333        case::incomplete(
1334            &[
1335                // Header
1336                // Version: 1
1337                0x01,
1338                // Length: 66
1339                0x00, 0x00, 0x42,
1340                // Flags: 128 (Request)
1341                0x80,
1342                // Code: 257 (Capability-Exchange)
1343                0x00, 0x01, 0x01,
1344                // Application ID: 0 (Diameter Common Messages)
1345                0x00, 0x00, 0x00, 0x00,
1346                // Hop-by-Hop ID: 0x53cafe6a
1347                0x53, 0xca, 0xfe, 0x6a,
1348                // End-to-End ID: 0x7dc0a11b
1349                0x7d, 0xc0, 0xa1, 0x1b,
1350
1351                //AVPs[0]
1352                // Code: 264 (Origin-Host)
1353                0x00, 0x00, 0x01, 0x08,
1354                // Flags: 40 (Mandatory)
1355                0x40,
1356                // Length: 31
1357                0x00, 0x00, 0x1f,
1358                // Data: "backend.eap.testbed.aaa"
1359                0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e,
1360                0x65, 0x61, 0x70, 0x2e, 0x74, 0x65, 0x73, 0x74,
1361                0x62, 0x65, 0x64, 0x2e, 0x61, 0x61, 0x61,
1362                // Padding: 1
1363                0x00,
1364
1365                // AVPS[1]
1366                // Code: 264 (Origin-Host)
1367                0x00, 0x00, 0x01, 0x08,
1368                // Flags: 0x80 (Vendor-Id)
1369                0x80,
1370                // Length: 14
1371                0x00, 0x00, 0x0e,
1372                // Vendor-Id: 1234567890
1373                0x49, 0x96, 0x02, 0xd2,
1374                // Data:
1375                // Padding:
1376            ],
1377            Err(error::Error::incomplete_needed(2))
1378        ),
1379        case::invalid_avp(
1380            &[
1381                // Header
1382                // Version: 1
1383                0x01,
1384                // Length: 64
1385                0x00, 0x00, 0x40,
1386                // Flags: 128 (Request)
1387                0x80,
1388                // Code: 257 (Capability-Exchange)
1389                0x00, 0x01, 0x01,
1390                // Application ID: 0 (Diameter Common Messages)
1391                0x00, 0x00, 0x00, 0x00,
1392                // Hop-by-Hop ID: 0x53cafe6a
1393                0x53, 0xca, 0xfe, 0x6a,
1394                // End-to-End ID: 0x7dc0a11b
1395                0x7d, 0xc0, 0xa1, 0x1b,
1396
1397                //AVPs[0]
1398                // Code: 264 (Origin-Host)
1399                0x00, 0x00, 0x01, 0x08,
1400                // Flags: 40 (Mandatory)
1401                0x40,
1402                // Length: 31
1403                0x00, 0x00, 0x1f,
1404                // Data: "backend.eap.testbed.aaa"
1405                0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2e,
1406                0x65, 0x61, 0x70, 0x2e, 0x74, 0x65, 0x73, 0x74,
1407                0x62, 0x65, 0x64, 0x2e, 0x61, 0x61, 0x61,
1408                // Padding: 1
1409                0x00,
1410
1411                // AVPS[1]
1412                // Code: 264 (Origin-Host)
1413                0x00, 0x00, 0x01, 0x08,
1414                // Flags: 0x80 (Vendor-Id)
1415                0x80,
1416                // Length: 14
1417                0x00, 0x00, 0x0e,
1418                // Vendor-Id: 1234567890
1419                0x49, 0x96, 0x02, 0xd2,
1420                // Data:
1421                // Padding:
1422            ],
1423            Err(error::Error::parse(Some("Many0".to_string()))),
1424        ),
1425    )]
1426    fn test_parse(input: &[u8], expected: Result<(&[u8], Option<Message>)>) {
1427        let diameter = Diameter {};
1428
1429        assert_eq!(diameter.parse(input, Direction::Unknown), expected);
1430    }
1431
1432    #[rstest(
1433        input,
1434        expected,
1435        case::empty(b"", Status::Incomplete),
1436        case::hello_world(b"hello world", Status::Unrecognized),
1437        case::header(
1438        &[
1439            // Version: 1
1440            0x01,
1441            // Length: 20
1442            0x00, 0x00, 0x14,
1443            // Flags: 128 (Request)
1444            0x80,
1445            // Code: 257 (Capability-Exchange)
1446            0x00, 0x01, 0x01,
1447            // Application ID: 0 (Diameter Common Messages)
1448            0x00, 0x00, 0x00, 0x00,
1449            // Hop-by-Hop ID: 0x53cafe6a
1450            0x53, 0xca, 0xfe, 0x6a,
1451            // End-to-End ID: 0x7dc0a11b
1452            0x7d, 0xc0, 0xa1, 0x1b,
1453            ],
1454            Status::Recognized
1455        ),
1456    )]
1457    fn test_probe(input: &[u8], expected: Status) {
1458        let diameter = Diameter {};
1459
1460        assert_eq!(diameter.probe(input, Direction::Unknown), expected);
1461    }
1462}