Skip to main content

bgpkit_parser/parser/rpki/
rtr.rs

1//! RPKI-to-Router (RTR) Protocol Parser
2//!
3//! This module provides parsing and encoding functions for RTR protocol PDUs
4//! as defined in RFC 6810 (v0) and RFC 8210 (v1).
5//!
6//! # Parsing
7//!
8//! ```rust
9//! use bgpkit_parser::parser::rpki::rtr::{parse_rtr_pdu, read_rtr_pdu};
10//! use bgpkit_parser::models::rpki::rtr::*;
11//!
12//! // Parse from a byte slice
13//! let bytes = [1, 2, 0, 0, 0, 0, 0, 8]; // Reset Query v1
14//! let (pdu, consumed) = parse_rtr_pdu(&bytes).unwrap();
15//! assert_eq!(consumed, 8);
16//! ```
17//!
18//! # Encoding
19//!
20//! ```rust
21//! use bgpkit_parser::parser::rpki::rtr::RtrEncode;
22//! use bgpkit_parser::models::rpki::rtr::*;
23//!
24//! let query = RtrResetQuery::new_v1();
25//! let bytes = query.encode();
26//! assert_eq!(bytes.len(), 8);
27//! ```
28
29use crate::models::rpki::rtr::*;
30use crate::models::Asn;
31use std::fmt;
32use std::io::{self, Read};
33use std::net::{Ipv4Addr, Ipv6Addr};
34
35// =============================================================================
36// Error Types
37// =============================================================================
38
39/// Errors that can occur during RTR PDU parsing or encoding
40#[derive(Debug)]
41pub enum RtrError {
42    /// I/O error during reading
43    IoError(io::Error),
44    /// PDU is incomplete (need more data)
45    IncompletePdu {
46        /// Number of bytes available
47        available: usize,
48        /// Number of bytes needed
49        needed: usize,
50    },
51    /// Invalid PDU type
52    InvalidPduType(u8),
53    /// Invalid protocol version
54    InvalidProtocolVersion(u8),
55    /// Invalid error code
56    InvalidErrorCode(u16),
57    /// Invalid PDU length
58    InvalidLength {
59        /// Expected length
60        expected: u32,
61        /// Actual length in header
62        actual: u32,
63        /// PDU type
64        pdu_type: u8,
65    },
66    /// Invalid prefix length
67    InvalidPrefixLength {
68        /// Prefix length
69        prefix_len: u8,
70        /// Maximum length
71        max_len: u8,
72        /// Maximum allowed for address family (32 for IPv4, 128 for IPv6)
73        max_allowed: u8,
74    },
75    /// Invalid UTF-8 in error text
76    InvalidUtf8,
77    /// Router Key PDU in v0 (not supported)
78    RouterKeyInV0,
79}
80
81impl fmt::Display for RtrError {
82    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83        match self {
84            RtrError::IoError(e) => write!(f, "I/O error: {}", e),
85            RtrError::IncompletePdu { available, needed } => {
86                write!(
87                    f,
88                    "Incomplete PDU: have {} bytes, need {} bytes",
89                    available, needed
90                )
91            }
92            RtrError::InvalidPduType(t) => write!(f, "Invalid PDU type: {}", t),
93            RtrError::InvalidProtocolVersion(v) => write!(f, "Invalid protocol version: {}", v),
94            RtrError::InvalidErrorCode(c) => write!(f, "Invalid error code: {}", c),
95            RtrError::InvalidLength {
96                expected,
97                actual,
98                pdu_type,
99            } => {
100                write!(
101                    f,
102                    "Invalid length for PDU type {}: expected {}, got {}",
103                    pdu_type, expected, actual
104                )
105            }
106            RtrError::InvalidPrefixLength {
107                prefix_len,
108                max_len,
109                max_allowed,
110            } => {
111                write!(
112                    f,
113                    "Invalid prefix length: prefix_len={}, max_len={}, max_allowed={}",
114                    prefix_len, max_len, max_allowed
115                )
116            }
117            RtrError::InvalidUtf8 => write!(f, "Invalid UTF-8 in error text"),
118            RtrError::RouterKeyInV0 => write!(f, "Router Key PDU is not valid in RTR v0"),
119        }
120    }
121}
122
123impl std::error::Error for RtrError {
124    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
125        match self {
126            RtrError::IoError(e) => Some(e),
127            _ => None,
128        }
129    }
130}
131
132impl From<io::Error> for RtrError {
133    fn from(e: io::Error) -> Self {
134        RtrError::IoError(e)
135    }
136}
137
138// =============================================================================
139// PDU Length Constants
140// =============================================================================
141
142/// RTR PDU header length (common to all PDUs)
143pub const RTR_HEADER_LEN: usize = 8;
144
145/// Serial Notify PDU length
146pub const RTR_SERIAL_NOTIFY_LEN: u32 = 12;
147
148/// Serial Query PDU length
149pub const RTR_SERIAL_QUERY_LEN: u32 = 12;
150
151/// Reset Query PDU length
152pub const RTR_RESET_QUERY_LEN: u32 = 8;
153
154/// Cache Response PDU length
155pub const RTR_CACHE_RESPONSE_LEN: u32 = 8;
156
157/// IPv4 Prefix PDU length
158pub const RTR_IPV4_PREFIX_LEN: u32 = 20;
159
160/// IPv6 Prefix PDU length
161pub const RTR_IPV6_PREFIX_LEN: u32 = 32;
162
163/// End of Data PDU length (v0)
164pub const RTR_END_OF_DATA_V0_LEN: u32 = 12;
165
166/// End of Data PDU length (v1)
167pub const RTR_END_OF_DATA_V1_LEN: u32 = 24;
168
169/// Cache Reset PDU length
170pub const RTR_CACHE_RESET_LEN: u32 = 8;
171
172/// Router Key PDU minimum length (header(8) + flags(1) + zero(1) + SKI(20) + ASN(4) = 34)
173pub const RTR_ROUTER_KEY_MIN_LEN: u32 = 34;
174
175/// Practical maximum length of an RTR PDU other than Error Report.
176///
177/// The wire `length` field is a 32-bit value (up to ~4 GiB), but every RTR
178/// PDU type has a far smaller practical maximum. The largest variable-length
179/// non-error PDU is the ASPA PDU (RFC 8210-bis, §5.12): an 8-byte common
180/// header, a 4-byte Customer ASN, and a list of 4-byte Provider ASNs whose
181/// count is derived from `Length` as `(Length - 12) / 4`. That spec
182/// explicitly states the PDU length "MUST NOT exceed 65,535 octets". Error
183/// Report PDUs can legitimately be larger (see [`RTR_MAX_ERROR_REPORT_LEN`]).
184pub const RTR_MAX_PDU_LEN: usize = 65_535;
185
186/// Maximum accepted RTR PDU length, used to cap the allocation in
187/// [`read_rtr_pdu`] so a crafted header cannot drive a large allocation
188/// (memory-exhaustion DoS).
189///
190/// Sized for the largest PDU: an Error Report (RFC 8210, §5.10), which
191/// encapsulates a copy of the erroneous PDU plus a diagnostic text:
192/// header(8) + encapsulated PDU length(4) + encapsulated PDU + error text
193/// length(4) + error text. The encapsulated PDU is itself at most
194/// [`RTR_MAX_PDU_LEN`]; the RFC places no explicit bound on the text, so the
195/// same 65,535 octets is used as a practical cap. Applied to every PDU type
196/// for simplicity, this keeps the worst-case allocation around 128 KiB.
197pub const RTR_MAX_ERROR_REPORT_LEN: usize = 8 + 4 + RTR_MAX_PDU_LEN + 4 + RTR_MAX_PDU_LEN;
198
199// =============================================================================
200// Parsing Functions
201// =============================================================================
202
203/// Parse a single RTR PDU from a byte slice.
204///
205/// Returns the parsed PDU and the number of bytes consumed.
206///
207/// # Errors
208///
209/// Returns an error if the input is too short, contains invalid data,
210/// or references an unknown PDU type.
211///
212/// # Example
213///
214/// ```rust
215/// use bgpkit_parser::parser::rpki::rtr::parse_rtr_pdu;
216/// use bgpkit_parser::models::rpki::rtr::*;
217///
218/// // Reset Query PDU (v1)
219/// let bytes = [1, 2, 0, 0, 0, 0, 0, 8];
220/// let (pdu, consumed) = parse_rtr_pdu(&bytes).unwrap();
221/// assert!(matches!(pdu, RtrPdu::ResetQuery(_)));
222/// assert_eq!(consumed, 8);
223/// ```
224pub fn parse_rtr_pdu(input: &[u8]) -> Result<(RtrPdu, usize), RtrError> {
225    // Need at least the header
226    if input.len() < RTR_HEADER_LEN {
227        return Err(RtrError::IncompletePdu {
228            available: input.len(),
229            needed: RTR_HEADER_LEN,
230        });
231    }
232
233    // Parse header
234    let version_byte = input[0];
235    let pdu_type_byte = input[1];
236    let session_or_error = u16::from_be_bytes([input[2], input[3]]);
237    let length = u32::from_be_bytes([input[4], input[5], input[6], input[7]]);
238
239    // Validate we have enough data
240    let length_usize = length as usize;
241    if input.len() < length_usize {
242        return Err(RtrError::IncompletePdu {
243            available: input.len(),
244            needed: length_usize,
245        });
246    }
247
248    // Parse version
249    let version = RtrProtocolVersion::from_u8(version_byte)
250        .ok_or(RtrError::InvalidProtocolVersion(version_byte))?;
251
252    // Parse PDU type
253    let pdu_type =
254        RtrPduType::from_u8(pdu_type_byte).ok_or(RtrError::InvalidPduType(pdu_type_byte))?;
255
256    // Parse based on PDU type
257    let pdu = match pdu_type {
258        RtrPduType::SerialNotify => {
259            validate_length(length, RTR_SERIAL_NOTIFY_LEN, pdu_type_byte)?;
260            let serial_number = u32::from_be_bytes([input[8], input[9], input[10], input[11]]);
261            RtrPdu::SerialNotify(RtrSerialNotify {
262                version,
263                session_id: session_or_error,
264                serial_number,
265            })
266        }
267
268        RtrPduType::SerialQuery => {
269            validate_length(length, RTR_SERIAL_QUERY_LEN, pdu_type_byte)?;
270            let serial_number = u32::from_be_bytes([input[8], input[9], input[10], input[11]]);
271            RtrPdu::SerialQuery(RtrSerialQuery {
272                version,
273                session_id: session_or_error,
274                serial_number,
275            })
276        }
277
278        RtrPduType::ResetQuery => {
279            validate_length(length, RTR_RESET_QUERY_LEN, pdu_type_byte)?;
280            RtrPdu::ResetQuery(RtrResetQuery { version })
281        }
282
283        RtrPduType::CacheResponse => {
284            validate_length(length, RTR_CACHE_RESPONSE_LEN, pdu_type_byte)?;
285            RtrPdu::CacheResponse(RtrCacheResponse {
286                version,
287                session_id: session_or_error,
288            })
289        }
290
291        RtrPduType::IPv4Prefix => {
292            validate_length(length, RTR_IPV4_PREFIX_LEN, pdu_type_byte)?;
293            let flags = input[8];
294            let prefix_length = input[9];
295            let max_length = input[10];
296            // input[11] is reserved/zero
297
298            validate_prefix_length(prefix_length, max_length, 32)?;
299
300            let prefix = Ipv4Addr::new(input[12], input[13], input[14], input[15]);
301            let asn = u32::from_be_bytes([input[16], input[17], input[18], input[19]]);
302
303            RtrPdu::IPv4Prefix(RtrIPv4Prefix {
304                version,
305                flags,
306                prefix_length,
307                max_length,
308                prefix,
309                asn: Asn::from(asn),
310            })
311        }
312
313        RtrPduType::IPv6Prefix => {
314            validate_length(length, RTR_IPV6_PREFIX_LEN, pdu_type_byte)?;
315            let flags = input[8];
316            let prefix_length = input[9];
317            let max_length = input[10];
318            // input[11] is reserved/zero
319
320            validate_prefix_length(prefix_length, max_length, 128)?;
321
322            let prefix = Ipv6Addr::from([
323                input[12], input[13], input[14], input[15], input[16], input[17], input[18],
324                input[19], input[20], input[21], input[22], input[23], input[24], input[25],
325                input[26], input[27],
326            ]);
327            let asn = u32::from_be_bytes([input[28], input[29], input[30], input[31]]);
328
329            RtrPdu::IPv6Prefix(RtrIPv6Prefix {
330                version,
331                flags,
332                prefix_length,
333                max_length,
334                prefix,
335                asn: Asn::from(asn),
336            })
337        }
338
339        RtrPduType::EndOfData => {
340            let expected_len = match version {
341                RtrProtocolVersion::V0 => RTR_END_OF_DATA_V0_LEN,
342                RtrProtocolVersion::V1 => RTR_END_OF_DATA_V1_LEN,
343            };
344            validate_length(length, expected_len, pdu_type_byte)?;
345
346            let serial_number = u32::from_be_bytes([input[8], input[9], input[10], input[11]]);
347
348            let (refresh_interval, retry_interval, expire_interval) = match version {
349                RtrProtocolVersion::V0 => (None, None, None),
350                RtrProtocolVersion::V1 => {
351                    let refresh = u32::from_be_bytes([input[12], input[13], input[14], input[15]]);
352                    let retry = u32::from_be_bytes([input[16], input[17], input[18], input[19]]);
353                    let expire = u32::from_be_bytes([input[20], input[21], input[22], input[23]]);
354                    (Some(refresh), Some(retry), Some(expire))
355                }
356            };
357
358            RtrPdu::EndOfData(RtrEndOfData {
359                version,
360                session_id: session_or_error,
361                serial_number,
362                refresh_interval,
363                retry_interval,
364                expire_interval,
365            })
366        }
367
368        RtrPduType::CacheReset => {
369            validate_length(length, RTR_CACHE_RESET_LEN, pdu_type_byte)?;
370            RtrPdu::CacheReset(RtrCacheReset { version })
371        }
372
373        RtrPduType::RouterKey => {
374            // Router Key is v1 only
375            if version == RtrProtocolVersion::V0 {
376                return Err(RtrError::RouterKeyInV0);
377            }
378
379            if length < RTR_ROUTER_KEY_MIN_LEN {
380                return Err(RtrError::InvalidLength {
381                    expected: RTR_ROUTER_KEY_MIN_LEN,
382                    actual: length,
383                    pdu_type: pdu_type_byte,
384                });
385            }
386
387            let flags = input[8];
388            // input[9] is zero
389            let mut ski = [0u8; 20];
390            ski.copy_from_slice(&input[10..30]);
391            let asn = u32::from_be_bytes([input[30], input[31], input[32], input[33]]);
392
393            // SPKI is the rest of the PDU (34 bytes of header + fixed fields already parsed)
394            let spki_len = (length as usize) - 34;
395            let spki = if spki_len > 0 {
396                input[34..34 + spki_len].to_vec()
397            } else {
398                Vec::new()
399            };
400
401            RtrPdu::RouterKey(RtrRouterKey {
402                version,
403                flags,
404                subject_key_identifier: ski,
405                asn: Asn::from(asn),
406                subject_public_key_info: spki,
407            })
408        }
409
410        RtrPduType::ErrorReport => {
411            // Error Report has variable length
412            // Minimum: header (8) + length of encapsulated PDU (4) + length of error text (4) = 16
413            if length < 16 {
414                return Err(RtrError::InvalidLength {
415                    expected: 16,
416                    actual: length,
417                    pdu_type: pdu_type_byte,
418                });
419            }
420
421            let error_code = RtrErrorCode::from_u16(session_or_error)
422                .ok_or(RtrError::InvalidErrorCode(session_or_error))?;
423
424            let encap_pdu_len =
425                u32::from_be_bytes([input[8], input[9], input[10], input[11]]) as usize;
426
427            // Validate encapsulated PDU fits. `encap_pdu_len` is
428            // wire-controlled, so compare by subtraction (`length >= 16` was
429            // checked above) rather than addition, which could overflow
430            // `usize` on 32-bit targets and bypass the check.
431            if encap_pdu_len > length_usize - 16 {
432                return Err(RtrError::InvalidLength {
433                    expected: u32::try_from(encap_pdu_len.saturating_add(16)).unwrap_or(u32::MAX),
434                    actual: length,
435                    pdu_type: pdu_type_byte,
436                });
437            }
438
439            let erroneous_pdu = if encap_pdu_len > 0 {
440                input[12..12 + encap_pdu_len].to_vec()
441            } else {
442                Vec::new()
443            };
444
445            let error_text_len_offset = 12 + encap_pdu_len;
446            let error_text_len = u32::from_be_bytes([
447                input[error_text_len_offset],
448                input[error_text_len_offset + 1],
449                input[error_text_len_offset + 2],
450                input[error_text_len_offset + 3],
451            ]) as usize;
452
453            let error_text_offset = error_text_len_offset + 4;
454
455            // Validate the declared error text actually fits within the PDU
456            // before slicing: `error_text_len` is wire-controlled and
457            // independent of `length`, so an oversized value would otherwise
458            // index past the buffer and panic. Compare by subtraction (the
459            // check above guarantees `error_text_offset <= length_usize`)
460            // rather than addition, which could overflow `usize` on 32-bit
461            // targets and bypass the check.
462            if error_text_len > length_usize - error_text_offset {
463                return Err(RtrError::InvalidLength {
464                    expected: u32::try_from(error_text_offset.saturating_add(error_text_len))
465                        .unwrap_or(u32::MAX),
466                    actual: length,
467                    pdu_type: pdu_type_byte,
468                });
469            }
470
471            let error_text = if error_text_len > 0 {
472                std::str::from_utf8(&input[error_text_offset..error_text_offset + error_text_len])
473                    .map_err(|_| RtrError::InvalidUtf8)?
474                    .to_string()
475            } else {
476                String::new()
477            };
478
479            RtrPdu::ErrorReport(RtrErrorReport {
480                version,
481                error_code,
482                erroneous_pdu,
483                error_text,
484            })
485        }
486    };
487
488    Ok((pdu, length_usize))
489}
490
491/// Read a single RTR PDU from a reader.
492///
493/// This function reads exactly one complete PDU from the reader.
494///
495/// # Errors
496///
497/// Returns an error if reading fails or the PDU is invalid.
498///
499/// # Example
500///
501/// ```rust,no_run
502/// use std::net::TcpStream;
503/// use bgpkit_parser::parser::rpki::rtr::read_rtr_pdu;
504///
505/// let mut stream = TcpStream::connect("rtr.example.com:8282").unwrap();
506/// let pdu = read_rtr_pdu(&mut stream).unwrap();
507/// ```
508pub fn read_rtr_pdu<R: Read>(reader: &mut R) -> Result<RtrPdu, RtrError> {
509    // Read header first
510    let mut header = [0u8; RTR_HEADER_LEN];
511    reader.read_exact(&mut header)?;
512
513    // Get length from header
514    let length = u32::from_be_bytes([header[4], header[5], header[6], header[7]]) as usize;
515
516    if length < RTR_HEADER_LEN {
517        return Err(RtrError::InvalidLength {
518            expected: RTR_HEADER_LEN as u32,
519            actual: length as u32,
520            pdu_type: header[1],
521        });
522    }
523
524    // Reject implausibly large PDUs before allocating, so a crafted header
525    // cannot drive a multi-gigabyte allocation (memory-exhaustion DoS).
526    if length > RTR_MAX_ERROR_REPORT_LEN {
527        return Err(RtrError::InvalidLength {
528            expected: RTR_MAX_ERROR_REPORT_LEN as u32,
529            actual: length as u32,
530            pdu_type: header[1],
531        });
532    }
533
534    // Allocate buffer for full PDU
535    let mut buffer = vec![0u8; length];
536    buffer[..RTR_HEADER_LEN].copy_from_slice(&header);
537
538    // Read remaining bytes
539    if length > RTR_HEADER_LEN {
540        reader.read_exact(&mut buffer[RTR_HEADER_LEN..])?;
541    }
542
543    // Parse the complete PDU
544    let (pdu, _) = parse_rtr_pdu(&buffer)?;
545    Ok(pdu)
546}
547
548fn validate_length(actual: u32, expected: u32, pdu_type: u8) -> Result<(), RtrError> {
549    if actual != expected {
550        Err(RtrError::InvalidLength {
551            expected,
552            actual,
553            pdu_type,
554        })
555    } else {
556        Ok(())
557    }
558}
559
560fn validate_prefix_length(prefix_len: u8, max_len: u8, max_allowed: u8) -> Result<(), RtrError> {
561    if prefix_len > max_len || max_len > max_allowed {
562        Err(RtrError::InvalidPrefixLength {
563            prefix_len,
564            max_len,
565            max_allowed,
566        })
567    } else {
568        Ok(())
569    }
570}
571
572// =============================================================================
573// Encoding Trait and Implementations
574// =============================================================================
575
576/// Trait for encoding RTR PDUs to bytes
577pub trait RtrEncode {
578    /// Encode this PDU to a byte vector
579    fn encode(&self) -> Vec<u8>;
580}
581
582impl RtrEncode for RtrSerialNotify {
583    fn encode(&self) -> Vec<u8> {
584        let mut buf = Vec::with_capacity(RTR_SERIAL_NOTIFY_LEN as usize);
585        buf.push(self.version.to_u8());
586        buf.push(RtrPduType::SerialNotify.to_u8());
587        buf.extend_from_slice(&self.session_id.to_be_bytes());
588        buf.extend_from_slice(&RTR_SERIAL_NOTIFY_LEN.to_be_bytes());
589        buf.extend_from_slice(&self.serial_number.to_be_bytes());
590        buf
591    }
592}
593
594impl RtrEncode for RtrSerialQuery {
595    fn encode(&self) -> Vec<u8> {
596        let mut buf = Vec::with_capacity(RTR_SERIAL_QUERY_LEN as usize);
597        buf.push(self.version.to_u8());
598        buf.push(RtrPduType::SerialQuery.to_u8());
599        buf.extend_from_slice(&self.session_id.to_be_bytes());
600        buf.extend_from_slice(&RTR_SERIAL_QUERY_LEN.to_be_bytes());
601        buf.extend_from_slice(&self.serial_number.to_be_bytes());
602        buf
603    }
604}
605
606impl RtrEncode for RtrResetQuery {
607    fn encode(&self) -> Vec<u8> {
608        let mut buf = Vec::with_capacity(RTR_RESET_QUERY_LEN as usize);
609        buf.push(self.version.to_u8());
610        buf.push(RtrPduType::ResetQuery.to_u8());
611        buf.extend_from_slice(&[0, 0]); // zero
612        buf.extend_from_slice(&RTR_RESET_QUERY_LEN.to_be_bytes());
613        buf
614    }
615}
616
617impl RtrEncode for RtrCacheResponse {
618    fn encode(&self) -> Vec<u8> {
619        let mut buf = Vec::with_capacity(RTR_CACHE_RESPONSE_LEN as usize);
620        buf.push(self.version.to_u8());
621        buf.push(RtrPduType::CacheResponse.to_u8());
622        buf.extend_from_slice(&self.session_id.to_be_bytes());
623        buf.extend_from_slice(&RTR_CACHE_RESPONSE_LEN.to_be_bytes());
624        buf
625    }
626}
627
628impl RtrEncode for RtrIPv4Prefix {
629    fn encode(&self) -> Vec<u8> {
630        let mut buf = Vec::with_capacity(RTR_IPV4_PREFIX_LEN as usize);
631        buf.push(self.version.to_u8());
632        buf.push(RtrPduType::IPv4Prefix.to_u8());
633        buf.extend_from_slice(&[0, 0]); // zero
634        buf.extend_from_slice(&RTR_IPV4_PREFIX_LEN.to_be_bytes());
635        buf.push(self.flags);
636        buf.push(self.prefix_length);
637        buf.push(self.max_length);
638        buf.push(0); // zero
639        buf.extend_from_slice(&self.prefix.octets());
640        buf.extend_from_slice(&self.asn.to_u32().to_be_bytes());
641        buf
642    }
643}
644
645impl RtrEncode for RtrIPv6Prefix {
646    fn encode(&self) -> Vec<u8> {
647        let mut buf = Vec::with_capacity(RTR_IPV6_PREFIX_LEN as usize);
648        buf.push(self.version.to_u8());
649        buf.push(RtrPduType::IPv6Prefix.to_u8());
650        buf.extend_from_slice(&[0, 0]); // zero
651        buf.extend_from_slice(&RTR_IPV6_PREFIX_LEN.to_be_bytes());
652        buf.push(self.flags);
653        buf.push(self.prefix_length);
654        buf.push(self.max_length);
655        buf.push(0); // zero
656        buf.extend_from_slice(&self.prefix.octets());
657        buf.extend_from_slice(&self.asn.to_u32().to_be_bytes());
658        buf
659    }
660}
661
662impl RtrEncode for RtrEndOfData {
663    fn encode(&self) -> Vec<u8> {
664        let length = match self.version {
665            RtrProtocolVersion::V0 => RTR_END_OF_DATA_V0_LEN,
666            RtrProtocolVersion::V1 => RTR_END_OF_DATA_V1_LEN,
667        };
668        let mut buf = Vec::with_capacity(length as usize);
669        buf.push(self.version.to_u8());
670        buf.push(RtrPduType::EndOfData.to_u8());
671        buf.extend_from_slice(&self.session_id.to_be_bytes());
672        buf.extend_from_slice(&length.to_be_bytes());
673        buf.extend_from_slice(&self.serial_number.to_be_bytes());
674
675        if self.version == RtrProtocolVersion::V1 {
676            buf.extend_from_slice(
677                &self
678                    .refresh_interval
679                    .unwrap_or(RtrEndOfData::DEFAULT_REFRESH)
680                    .to_be_bytes(),
681            );
682            buf.extend_from_slice(
683                &self
684                    .retry_interval
685                    .unwrap_or(RtrEndOfData::DEFAULT_RETRY)
686                    .to_be_bytes(),
687            );
688            buf.extend_from_slice(
689                &self
690                    .expire_interval
691                    .unwrap_or(RtrEndOfData::DEFAULT_EXPIRE)
692                    .to_be_bytes(),
693            );
694        }
695        buf
696    }
697}
698
699impl RtrEncode for RtrCacheReset {
700    fn encode(&self) -> Vec<u8> {
701        let mut buf = Vec::with_capacity(RTR_CACHE_RESET_LEN as usize);
702        buf.push(self.version.to_u8());
703        buf.push(RtrPduType::CacheReset.to_u8());
704        buf.extend_from_slice(&[0, 0]); // zero
705        buf.extend_from_slice(&RTR_CACHE_RESET_LEN.to_be_bytes());
706        buf
707    }
708}
709
710impl RtrEncode for RtrRouterKey {
711    fn encode(&self) -> Vec<u8> {
712        let length = RTR_ROUTER_KEY_MIN_LEN + self.subject_public_key_info.len() as u32;
713        let mut buf = Vec::with_capacity(length as usize);
714        buf.push(self.version.to_u8());
715        buf.push(RtrPduType::RouterKey.to_u8());
716        buf.extend_from_slice(&[0, 0]); // zero (session_id field is zero for Router Key)
717        buf.extend_from_slice(&length.to_be_bytes());
718        buf.push(self.flags);
719        buf.push(0); // zero
720        buf.extend_from_slice(&self.subject_key_identifier);
721        buf.extend_from_slice(&self.asn.to_u32().to_be_bytes());
722        buf.extend_from_slice(&self.subject_public_key_info);
723        buf
724    }
725}
726
727impl RtrEncode for RtrErrorReport {
728    fn encode(&self) -> Vec<u8> {
729        let error_text_bytes = self.error_text.as_bytes();
730        let length = 16 + self.erroneous_pdu.len() + error_text_bytes.len();
731        let mut buf = Vec::with_capacity(length);
732        buf.push(self.version.to_u8());
733        buf.push(RtrPduType::ErrorReport.to_u8());
734        buf.extend_from_slice(&self.error_code.to_u16().to_be_bytes());
735        buf.extend_from_slice(&(length as u32).to_be_bytes());
736        buf.extend_from_slice(&(self.erroneous_pdu.len() as u32).to_be_bytes());
737        buf.extend_from_slice(&self.erroneous_pdu);
738        buf.extend_from_slice(&(error_text_bytes.len() as u32).to_be_bytes());
739        buf.extend_from_slice(error_text_bytes);
740        buf
741    }
742}
743
744impl RtrEncode for RtrPdu {
745    fn encode(&self) -> Vec<u8> {
746        match self {
747            RtrPdu::SerialNotify(p) => p.encode(),
748            RtrPdu::SerialQuery(p) => p.encode(),
749            RtrPdu::ResetQuery(p) => p.encode(),
750            RtrPdu::CacheResponse(p) => p.encode(),
751            RtrPdu::IPv4Prefix(p) => p.encode(),
752            RtrPdu::IPv6Prefix(p) => p.encode(),
753            RtrPdu::EndOfData(p) => p.encode(),
754            RtrPdu::CacheReset(p) => p.encode(),
755            RtrPdu::RouterKey(p) => p.encode(),
756            RtrPdu::ErrorReport(p) => p.encode(),
757        }
758    }
759}
760
761// =============================================================================
762// Tests
763// =============================================================================
764
765#[cfg(test)]
766mod tests {
767    use super::*;
768
769    #[test]
770    fn test_reset_query_roundtrip() {
771        let query = RtrResetQuery::new_v1();
772        let bytes = query.encode();
773        assert_eq!(bytes.len(), 8);
774
775        let (pdu, consumed) = parse_rtr_pdu(&bytes).unwrap();
776        assert_eq!(consumed, 8);
777        assert!(matches!(pdu, RtrPdu::ResetQuery(q) if q.version == RtrProtocolVersion::V1));
778    }
779
780    #[test]
781    fn test_reset_query_v0_roundtrip() {
782        let query = RtrResetQuery::new_v0();
783        let bytes = query.encode();
784
785        let (pdu, _) = parse_rtr_pdu(&bytes).unwrap();
786        assert!(matches!(pdu, RtrPdu::ResetQuery(q) if q.version == RtrProtocolVersion::V0));
787    }
788
789    #[test]
790    fn test_serial_query_roundtrip() {
791        let query = RtrSerialQuery::new(RtrProtocolVersion::V1, 12345, 67890);
792        let bytes = query.encode();
793        assert_eq!(bytes.len(), 12);
794
795        let (pdu, consumed) = parse_rtr_pdu(&bytes).unwrap();
796        assert_eq!(consumed, 12);
797        match pdu {
798            RtrPdu::SerialQuery(q) => {
799                assert_eq!(q.session_id, 12345);
800                assert_eq!(q.serial_number, 67890);
801            }
802            _ => panic!("Expected SerialQuery"),
803        }
804    }
805
806    #[test]
807    fn test_serial_notify_roundtrip() {
808        let notify = RtrSerialNotify {
809            version: RtrProtocolVersion::V1,
810            session_id: 100,
811            serial_number: 200,
812        };
813        let bytes = notify.encode();
814
815        let (pdu, _) = parse_rtr_pdu(&bytes).unwrap();
816        match pdu {
817            RtrPdu::SerialNotify(n) => {
818                assert_eq!(n.session_id, 100);
819                assert_eq!(n.serial_number, 200);
820            }
821            _ => panic!("Expected SerialNotify"),
822        }
823    }
824
825    #[test]
826    fn test_cache_response_roundtrip() {
827        let response = RtrCacheResponse {
828            version: RtrProtocolVersion::V1,
829            session_id: 42,
830        };
831        let bytes = response.encode();
832
833        let (pdu, _) = parse_rtr_pdu(&bytes).unwrap();
834        match pdu {
835            RtrPdu::CacheResponse(r) => {
836                assert_eq!(r.session_id, 42);
837            }
838            _ => panic!("Expected CacheResponse"),
839        }
840    }
841
842    #[test]
843    fn test_ipv4_prefix_roundtrip() {
844        let prefix = RtrIPv4Prefix {
845            version: RtrProtocolVersion::V1,
846            flags: 1,
847            prefix_length: 24,
848            max_length: 24,
849            prefix: Ipv4Addr::new(192, 0, 2, 0),
850            asn: Asn::from(65001u32),
851        };
852        let bytes = prefix.encode();
853        assert_eq!(bytes.len(), 20);
854
855        let (pdu, _) = parse_rtr_pdu(&bytes).unwrap();
856        match pdu {
857            RtrPdu::IPv4Prefix(p) => {
858                assert!(p.is_announcement());
859                assert_eq!(p.prefix_length, 24);
860                assert_eq!(p.max_length, 24);
861                assert_eq!(p.prefix, Ipv4Addr::new(192, 0, 2, 0));
862                assert_eq!(p.asn.to_u32(), 65001);
863            }
864            _ => panic!("Expected IPv4Prefix"),
865        }
866    }
867
868    #[test]
869    fn test_ipv4_prefix_withdrawal() {
870        let prefix = RtrIPv4Prefix {
871            version: RtrProtocolVersion::V1,
872            flags: 0, // withdrawal
873            prefix_length: 24,
874            max_length: 24,
875            prefix: Ipv4Addr::new(192, 0, 2, 0),
876            asn: Asn::from(65001u32),
877        };
878        let bytes = prefix.encode();
879
880        let (pdu, _) = parse_rtr_pdu(&bytes).unwrap();
881        match pdu {
882            RtrPdu::IPv4Prefix(p) => {
883                assert!(p.is_withdrawal());
884                assert!(!p.is_announcement());
885            }
886            _ => panic!("Expected IPv4Prefix"),
887        }
888    }
889
890    #[test]
891    fn test_ipv6_prefix_roundtrip() {
892        let prefix = RtrIPv6Prefix {
893            version: RtrProtocolVersion::V1,
894            flags: 1,
895            prefix_length: 48,
896            max_length: 64,
897            prefix: Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0),
898            asn: Asn::from(65002u32),
899        };
900        let bytes = prefix.encode();
901        assert_eq!(bytes.len(), 32);
902
903        let (pdu, _) = parse_rtr_pdu(&bytes).unwrap();
904        match pdu {
905            RtrPdu::IPv6Prefix(p) => {
906                assert!(p.is_announcement());
907                assert_eq!(p.prefix_length, 48);
908                assert_eq!(p.max_length, 64);
909                assert_eq!(p.prefix, Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0));
910                assert_eq!(p.asn.to_u32(), 65002);
911            }
912            _ => panic!("Expected IPv6Prefix"),
913        }
914    }
915
916    #[test]
917    fn test_end_of_data_v0_roundtrip() {
918        let eod = RtrEndOfData {
919            version: RtrProtocolVersion::V0,
920            session_id: 100,
921            serial_number: 200,
922            refresh_interval: None,
923            retry_interval: None,
924            expire_interval: None,
925        };
926        let bytes = eod.encode();
927        assert_eq!(bytes.len(), 12);
928
929        let (pdu, _) = parse_rtr_pdu(&bytes).unwrap();
930        match pdu {
931            RtrPdu::EndOfData(e) => {
932                assert_eq!(e.version, RtrProtocolVersion::V0);
933                assert_eq!(e.session_id, 100);
934                assert_eq!(e.serial_number, 200);
935                assert_eq!(e.refresh_interval, None);
936                assert_eq!(e.retry_interval, None);
937                assert_eq!(e.expire_interval, None);
938            }
939            _ => panic!("Expected EndOfData"),
940        }
941    }
942
943    #[test]
944    fn test_end_of_data_v1_roundtrip() {
945        let eod = RtrEndOfData {
946            version: RtrProtocolVersion::V1,
947            session_id: 100,
948            serial_number: 200,
949            refresh_interval: Some(1800),
950            retry_interval: Some(300),
951            expire_interval: Some(3600),
952        };
953        let bytes = eod.encode();
954        assert_eq!(bytes.len(), 24);
955
956        let (pdu, _) = parse_rtr_pdu(&bytes).unwrap();
957        match pdu {
958            RtrPdu::EndOfData(e) => {
959                assert_eq!(e.version, RtrProtocolVersion::V1);
960                assert_eq!(e.refresh_interval, Some(1800));
961                assert_eq!(e.retry_interval, Some(300));
962                assert_eq!(e.expire_interval, Some(3600));
963            }
964            _ => panic!("Expected EndOfData"),
965        }
966    }
967
968    #[test]
969    fn test_end_of_data_v1_with_defaults() {
970        let eod = RtrEndOfData {
971            version: RtrProtocolVersion::V1,
972            session_id: 100,
973            serial_number: 200,
974            refresh_interval: None, // Will use default when encoding
975            retry_interval: None,
976            expire_interval: None,
977        };
978        let bytes = eod.encode();
979        assert_eq!(bytes.len(), 24);
980
981        let (pdu, _) = parse_rtr_pdu(&bytes).unwrap();
982        match pdu {
983            RtrPdu::EndOfData(e) => {
984                // Since v1 encoding always includes timing, they'll be the defaults
985                assert_eq!(e.refresh_interval, Some(3600));
986                assert_eq!(e.retry_interval, Some(600));
987                assert_eq!(e.expire_interval, Some(7200));
988            }
989            _ => panic!("Expected EndOfData"),
990        }
991    }
992
993    #[test]
994    fn test_cache_reset_roundtrip() {
995        let reset = RtrCacheReset {
996            version: RtrProtocolVersion::V1,
997        };
998        let bytes = reset.encode();
999        assert_eq!(bytes.len(), 8);
1000
1001        let (pdu, _) = parse_rtr_pdu(&bytes).unwrap();
1002        assert!(matches!(pdu, RtrPdu::CacheReset(_)));
1003    }
1004
1005    #[test]
1006    fn test_router_key_roundtrip() {
1007        let key = RtrRouterKey {
1008            version: RtrProtocolVersion::V1,
1009            flags: 1,
1010            subject_key_identifier: [
1011                1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
1012            ],
1013            asn: Asn::from(65003u32),
1014            subject_public_key_info: vec![0xAB, 0xCD, 0xEF],
1015        };
1016        let bytes = key.encode();
1017        assert_eq!(bytes.len(), 37); // 34 min + 3 SPKI
1018
1019        let (pdu, _) = parse_rtr_pdu(&bytes).unwrap();
1020        match pdu {
1021            RtrPdu::RouterKey(k) => {
1022                assert!(k.is_announcement());
1023                assert_eq!(k.subject_key_identifier[0], 1);
1024                assert_eq!(k.subject_key_identifier[19], 20);
1025                assert_eq!(k.asn.to_u32(), 65003);
1026                assert_eq!(k.subject_public_key_info, vec![0xAB, 0xCD, 0xEF]);
1027            }
1028            _ => panic!("Expected RouterKey"),
1029        }
1030    }
1031
1032    #[test]
1033    fn test_router_key_in_v0_error() {
1034        // Manually construct a Router Key PDU with v0 version
1035        let mut bytes = vec![
1036            0, // version 0
1037            9, // type 9 (Router Key)
1038            0, 0, // zero
1039            0, 0, 0, 34, // length = 34 (minimum)
1040            1,  // flags
1041            0,  // zero
1042        ];
1043        bytes.extend_from_slice(&[0u8; 20]); // SKI
1044        bytes.extend_from_slice(&[0, 0, 0, 1]); // ASN
1045
1046        let result = parse_rtr_pdu(&bytes);
1047        assert!(matches!(result, Err(RtrError::RouterKeyInV0)));
1048    }
1049
1050    #[test]
1051    fn test_error_report_roundtrip() {
1052        let error = RtrErrorReport {
1053            version: RtrProtocolVersion::V1,
1054            error_code: RtrErrorCode::UnsupportedProtocolVersion,
1055            erroneous_pdu: vec![99, 2, 0, 0, 0, 0, 0, 8], // Some invalid PDU
1056            error_text: "Test error".to_string(),
1057        };
1058        let bytes = error.encode();
1059
1060        let (pdu, _) = parse_rtr_pdu(&bytes).unwrap();
1061        match pdu {
1062            RtrPdu::ErrorReport(e) => {
1063                assert_eq!(e.error_code, RtrErrorCode::UnsupportedProtocolVersion);
1064                assert_eq!(e.erroneous_pdu, vec![99, 2, 0, 0, 0, 0, 0, 8]);
1065                assert_eq!(e.error_text, "Test error");
1066            }
1067            _ => panic!("Expected ErrorReport"),
1068        }
1069    }
1070
1071    #[test]
1072    fn test_error_report_empty() {
1073        let error = RtrErrorReport {
1074            version: RtrProtocolVersion::V1,
1075            error_code: RtrErrorCode::InternalError,
1076            erroneous_pdu: vec![],
1077            error_text: String::new(),
1078        };
1079        let bytes = error.encode();
1080        assert_eq!(bytes.len(), 16);
1081
1082        let (pdu, _) = parse_rtr_pdu(&bytes).unwrap();
1083        match pdu {
1084            RtrPdu::ErrorReport(e) => {
1085                assert_eq!(e.error_code, RtrErrorCode::InternalError);
1086                assert!(e.erroneous_pdu.is_empty());
1087                assert!(e.error_text.is_empty());
1088            }
1089            _ => panic!("Expected ErrorReport"),
1090        }
1091    }
1092
1093    #[test]
1094    fn test_incomplete_pdu_error() {
1095        let bytes = [1, 2, 0]; // Too short
1096        let result = parse_rtr_pdu(&bytes);
1097        assert!(matches!(result, Err(RtrError::IncompletePdu { .. })));
1098    }
1099
1100    #[test]
1101    fn test_invalid_pdu_type_error() {
1102        let bytes = [1, 5, 0, 0, 0, 0, 0, 8]; // Type 5 doesn't exist
1103        let result = parse_rtr_pdu(&bytes);
1104        assert!(matches!(result, Err(RtrError::InvalidPduType(5))));
1105    }
1106
1107    #[test]
1108    fn test_invalid_protocol_version_error() {
1109        let bytes = [99, 2, 0, 0, 0, 0, 0, 8]; // Version 99 doesn't exist
1110        let result = parse_rtr_pdu(&bytes);
1111        assert!(matches!(result, Err(RtrError::InvalidProtocolVersion(99))));
1112    }
1113
1114    #[test]
1115    fn test_invalid_length_error() {
1116        // Reset Query with wrong length - need full buffer to match declared length
1117        let bytes = [1, 2, 0, 0, 0, 0, 0, 10, 0, 0]; // Length says 10, should be 8
1118        let result = parse_rtr_pdu(&bytes);
1119        assert!(matches!(result, Err(RtrError::InvalidLength { .. })));
1120    }
1121
1122    #[test]
1123    fn test_invalid_prefix_length_error() {
1124        // IPv4 prefix with prefix_len > max_len
1125        let mut bytes = vec![
1126            1, // version
1127            4, // type (IPv4 Prefix)
1128            0, 0, // zero
1129            0, 0, 0, 20, // length
1130            1,  // flags
1131            25, // prefix_length (25)
1132            24, // max_length (24) - INVALID: prefix_len > max_len
1133            0,  // zero
1134        ];
1135        bytes.extend_from_slice(&[192, 0, 2, 0]); // prefix
1136        bytes.extend_from_slice(&[0, 0, 0, 1]); // ASN
1137
1138        let result = parse_rtr_pdu(&bytes);
1139        assert!(matches!(result, Err(RtrError::InvalidPrefixLength { .. })));
1140    }
1141
1142    #[test]
1143    fn test_invalid_max_length_error() {
1144        // IPv4 prefix with max_len > 32
1145        let mut bytes = vec![
1146            1, // version
1147            4, // type (IPv4 Prefix)
1148            0, 0, // zero
1149            0, 0, 0, 20, // length
1150            1,  // flags
1151            24, // prefix_length
1152            33, // max_length (33) - INVALID: > 32 for IPv4
1153            0,  // zero
1154        ];
1155        bytes.extend_from_slice(&[192, 0, 2, 0]); // prefix
1156        bytes.extend_from_slice(&[0, 0, 0, 1]); // ASN
1157
1158        let result = parse_rtr_pdu(&bytes);
1159        assert!(matches!(result, Err(RtrError::InvalidPrefixLength { .. })));
1160    }
1161
1162    #[test]
1163    fn test_read_rtr_pdu_from_cursor() {
1164        use std::io::Cursor;
1165
1166        let query = RtrResetQuery::new_v1();
1167        let bytes = query.encode();
1168        let mut cursor = Cursor::new(bytes);
1169
1170        let pdu = read_rtr_pdu(&mut cursor).unwrap();
1171        assert!(matches!(pdu, RtrPdu::ResetQuery(_)));
1172    }
1173
1174    #[test]
1175    fn test_pdu_enum_encode() {
1176        let pdu = RtrPdu::ResetQuery(RtrResetQuery::new_v1());
1177        let bytes = pdu.encode();
1178        assert_eq!(bytes.len(), 8);
1179
1180        let (parsed, _) = parse_rtr_pdu(&bytes).unwrap();
1181        assert!(matches!(parsed, RtrPdu::ResetQuery(_)));
1182    }
1183
1184    #[test]
1185    fn test_all_pdu_types_roundtrip() {
1186        // Test that all PDU types can be encoded and decoded
1187        let pdus: Vec<RtrPdu> = vec![
1188            RtrPdu::SerialNotify(RtrSerialNotify {
1189                version: RtrProtocolVersion::V1,
1190                session_id: 1,
1191                serial_number: 100,
1192            }),
1193            RtrPdu::SerialQuery(RtrSerialQuery::new(RtrProtocolVersion::V1, 1, 100)),
1194            RtrPdu::ResetQuery(RtrResetQuery::new_v1()),
1195            RtrPdu::CacheResponse(RtrCacheResponse {
1196                version: RtrProtocolVersion::V1,
1197                session_id: 1,
1198            }),
1199            RtrPdu::IPv4Prefix(RtrIPv4Prefix {
1200                version: RtrProtocolVersion::V1,
1201                flags: 1,
1202                prefix_length: 24,
1203                max_length: 24,
1204                prefix: Ipv4Addr::new(10, 0, 0, 0),
1205                asn: Asn::from(65000u32),
1206            }),
1207            RtrPdu::IPv6Prefix(RtrIPv6Prefix {
1208                version: RtrProtocolVersion::V1,
1209                flags: 1,
1210                prefix_length: 48,
1211                max_length: 48,
1212                prefix: Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0),
1213                asn: Asn::from(65000u32),
1214            }),
1215            RtrPdu::EndOfData(RtrEndOfData {
1216                version: RtrProtocolVersion::V1,
1217                session_id: 1,
1218                serial_number: 100,
1219                refresh_interval: Some(3600),
1220                retry_interval: Some(600),
1221                expire_interval: Some(7200),
1222            }),
1223            RtrPdu::CacheReset(RtrCacheReset {
1224                version: RtrProtocolVersion::V1,
1225            }),
1226            RtrPdu::RouterKey(RtrRouterKey {
1227                version: RtrProtocolVersion::V1,
1228                flags: 1,
1229                subject_key_identifier: [0; 20],
1230                asn: Asn::from(65000u32),
1231                subject_public_key_info: vec![1, 2, 3, 4],
1232            }),
1233            RtrPdu::ErrorReport(RtrErrorReport {
1234                version: RtrProtocolVersion::V1,
1235                error_code: RtrErrorCode::NoDataAvailable,
1236                erroneous_pdu: vec![],
1237                error_text: "No data".to_string(),
1238            }),
1239        ];
1240
1241        for original in pdus {
1242            let bytes = original.encode();
1243            let (parsed, consumed) = parse_rtr_pdu(&bytes).unwrap();
1244            assert_eq!(consumed, bytes.len());
1245            assert_eq!(parsed.pdu_type(), original.pdu_type());
1246        }
1247    }
1248
1249    #[test]
1250    fn test_error_display() {
1251        let err = RtrError::InvalidPduType(42);
1252        assert!(err.to_string().contains("42"));
1253
1254        let err = RtrError::IncompletePdu {
1255            available: 4,
1256            needed: 8,
1257        };
1258        assert!(err.to_string().contains("4"));
1259        assert!(err.to_string().contains("8"));
1260    }
1261
1262    #[test]
1263    fn test_error_display_all_variants() {
1264        // Test Display for all RtrError variants
1265        let io_err = RtrError::IoError(std::io::Error::new(
1266            std::io::ErrorKind::ConnectionReset,
1267            "connection reset",
1268        ));
1269        assert!(io_err.to_string().contains("I/O error"));
1270
1271        let incomplete = RtrError::IncompletePdu {
1272            available: 5,
1273            needed: 10,
1274        };
1275        assert!(incomplete.to_string().contains("Incomplete PDU"));
1276        assert!(incomplete.to_string().contains("5"));
1277        assert!(incomplete.to_string().contains("10"));
1278
1279        let invalid_type = RtrError::InvalidPduType(99);
1280        assert!(invalid_type.to_string().contains("Invalid PDU type"));
1281        assert!(invalid_type.to_string().contains("99"));
1282
1283        let invalid_version = RtrError::InvalidProtocolVersion(5);
1284        assert!(invalid_version
1285            .to_string()
1286            .contains("Invalid protocol version"));
1287        assert!(invalid_version.to_string().contains("5"));
1288
1289        let invalid_error_code = RtrError::InvalidErrorCode(100);
1290        assert!(invalid_error_code
1291            .to_string()
1292            .contains("Invalid error code"));
1293        assert!(invalid_error_code.to_string().contains("100"));
1294
1295        let invalid_length = RtrError::InvalidLength {
1296            expected: 20,
1297            actual: 15,
1298            pdu_type: 4,
1299        };
1300        assert!(invalid_length.to_string().contains("Invalid length"));
1301        assert!(invalid_length.to_string().contains("20"));
1302        assert!(invalid_length.to_string().contains("15"));
1303        assert!(invalid_length.to_string().contains("4"));
1304
1305        let invalid_prefix = RtrError::InvalidPrefixLength {
1306            prefix_len: 25,
1307            max_len: 24,
1308            max_allowed: 32,
1309        };
1310        assert!(invalid_prefix.to_string().contains("Invalid prefix length"));
1311        assert!(invalid_prefix.to_string().contains("25"));
1312        assert!(invalid_prefix.to_string().contains("24"));
1313        assert!(invalid_prefix.to_string().contains("32"));
1314
1315        let invalid_utf8 = RtrError::InvalidUtf8;
1316        assert!(invalid_utf8.to_string().contains("Invalid UTF-8"));
1317
1318        let router_key_v0 = RtrError::RouterKeyInV0;
1319        assert!(router_key_v0.to_string().contains("Router Key PDU"));
1320        assert!(router_key_v0.to_string().contains("v0"));
1321    }
1322
1323    #[test]
1324    fn test_error_source() {
1325        use std::error::Error;
1326
1327        // IoError should have a source
1328        let io_err = RtrError::IoError(std::io::Error::new(
1329            std::io::ErrorKind::NotFound,
1330            "file not found",
1331        ));
1332        assert!(io_err.source().is_some());
1333
1334        // Other errors should not have a source
1335        let incomplete = RtrError::IncompletePdu {
1336            available: 1,
1337            needed: 2,
1338        };
1339        assert!(incomplete.source().is_none());
1340
1341        let invalid_type = RtrError::InvalidPduType(5);
1342        assert!(invalid_type.source().is_none());
1343
1344        let invalid_utf8 = RtrError::InvalidUtf8;
1345        assert!(invalid_utf8.source().is_none());
1346    }
1347
1348    #[test]
1349    fn test_error_from_io_error() {
1350        let io_err = std::io::Error::new(std::io::ErrorKind::TimedOut, "timeout");
1351        let rtr_err: RtrError = io_err.into();
1352        assert!(matches!(rtr_err, RtrError::IoError(_)));
1353    }
1354
1355    #[test]
1356    fn test_read_rtr_pdu_short_length() {
1357        use std::io::Cursor;
1358
1359        // PDU with length less than header size
1360        let bytes = vec![
1361            1, // version
1362            2, // type (Reset Query)
1363            0, 0, // zero
1364            0, 0, 0, 4, // length = 4 (less than header)
1365        ];
1366        let mut cursor = Cursor::new(bytes);
1367        let result = read_rtr_pdu(&mut cursor);
1368        assert!(matches!(result, Err(RtrError::InvalidLength { .. })));
1369    }
1370
1371    #[test]
1372    fn test_parse_invalid_error_code() {
1373        // Error Report with invalid error code
1374        let bytes = vec![
1375            1,  // version
1376            10, // type (Error Report)
1377            0, 100, // error code = 100 (invalid)
1378            0, 0, 0, 16, // length = 16 (minimum)
1379            0, 0, 0, 0, // encapsulated PDU length = 0
1380            0, 0, 0, 0, // error text length = 0
1381        ];
1382        let result = parse_rtr_pdu(&bytes);
1383        assert!(matches!(result, Err(RtrError::InvalidErrorCode(100))));
1384    }
1385
1386    #[test]
1387    fn test_parse_error_report_invalid_utf8() {
1388        // Error Report with invalid UTF-8 in error text
1389        let bytes = vec![
1390            1,  // version
1391            10, // type (Error Report)
1392            0, 0, // error code = 0
1393            0, 0, 0, 20, // length = 20
1394            0, 0, 0, 0, // encapsulated PDU length = 0
1395            0, 0, 0, 4, // error text length = 4
1396            0xFF, 0xFE, 0xFF, 0xFE, // invalid UTF-8
1397        ];
1398        let result = parse_rtr_pdu(&bytes);
1399        assert!(matches!(result, Err(RtrError::InvalidUtf8)));
1400    }
1401
1402    #[test]
1403    fn test_parse_error_report_truncated() {
1404        // Error Report with encapsulated PDU length exceeding bounds
1405        let bytes = vec![
1406            1,  // version
1407            10, // type (Error Report)
1408            0, 0, // error code = 0
1409            0, 0, 0, 16, // length = 16
1410            0, 0, 0, 100, // encapsulated PDU length = 100 (too large)
1411            0, 0, 0, 0, // error text length = 0
1412        ];
1413        let result = parse_rtr_pdu(&bytes);
1414        assert!(matches!(result, Err(RtrError::InvalidLength { .. })));
1415    }
1416
1417    #[test]
1418    fn test_parse_router_key_empty_spki() {
1419        // Router Key with no SPKI (minimum length)
1420        let mut bytes = vec![
1421            1, // version
1422            9, // type (Router Key)
1423            0, 0, // zero
1424            0, 0, 0, 34, // length = 34 (minimum)
1425            1,  // flags
1426            0,  // zero
1427        ];
1428        bytes.extend_from_slice(&[0u8; 20]); // SKI
1429        bytes.extend_from_slice(&[0, 0, 0, 1]); // ASN = 1
1430
1431        let (pdu, consumed) = parse_rtr_pdu(&bytes).unwrap();
1432        assert_eq!(consumed, 34);
1433        match pdu {
1434            RtrPdu::RouterKey(k) => {
1435                assert!(k.subject_public_key_info.is_empty());
1436            }
1437            _ => panic!("Expected RouterKey"),
1438        }
1439    }
1440
1441    #[test]
1442    fn test_parse_ipv6_invalid_max_length() {
1443        // IPv6 prefix with max_len > 128
1444        let mut bytes = vec![
1445            1, // version
1446            6, // type (IPv6 Prefix)
1447            0, 0, // zero
1448            0, 0, 0, 32,  // length
1449            1,   // flags
1450            64,  // prefix_length
1451            129, // max_length (129) - INVALID: > 128 for IPv6
1452            0,   // zero
1453        ];
1454        bytes.extend_from_slice(&[0u8; 16]); // prefix
1455        bytes.extend_from_slice(&[0, 0, 0, 1]); // ASN
1456
1457        let result = parse_rtr_pdu(&bytes);
1458        assert!(matches!(result, Err(RtrError::InvalidPrefixLength { .. })));
1459    }
1460
1461    #[test]
1462    fn test_encode_all_pdu_types_v0() {
1463        // Test encoding v0 PDUs
1464        let notify = RtrSerialNotify {
1465            version: RtrProtocolVersion::V0,
1466            session_id: 100,
1467            serial_number: 200,
1468        };
1469        let bytes = notify.encode();
1470        assert_eq!(bytes[0], 0); // version 0
1471
1472        let response = RtrCacheResponse {
1473            version: RtrProtocolVersion::V0,
1474            session_id: 300,
1475        };
1476        let bytes = response.encode();
1477        assert_eq!(bytes[0], 0); // version 0
1478
1479        let reset = RtrCacheReset {
1480            version: RtrProtocolVersion::V0,
1481        };
1482        let bytes = reset.encode();
1483        assert_eq!(bytes[0], 0); // version 0
1484
1485        let prefix4 = RtrIPv4Prefix {
1486            version: RtrProtocolVersion::V0,
1487            flags: 0,
1488            prefix_length: 16,
1489            max_length: 24,
1490            prefix: Ipv4Addr::new(172, 16, 0, 0),
1491            asn: Asn::from(64512u32),
1492        };
1493        let bytes = prefix4.encode();
1494        assert_eq!(bytes[0], 0); // version 0
1495        assert_eq!(bytes.len(), 20);
1496
1497        let prefix6 = RtrIPv6Prefix {
1498            version: RtrProtocolVersion::V0,
1499            flags: 1,
1500            prefix_length: 32,
1501            max_length: 48,
1502            prefix: Ipv6Addr::new(0xfc00, 0, 0, 0, 0, 0, 0, 0),
1503            asn: Asn::from(64513u32),
1504        };
1505        let bytes = prefix6.encode();
1506        assert_eq!(bytes[0], 0); // version 0
1507        assert_eq!(bytes.len(), 32);
1508    }
1509
1510    #[test]
1511    fn test_read_multiple_pdus() {
1512        use std::io::Cursor;
1513
1514        // Create buffer with two PDUs
1515        let query1 = RtrResetQuery::new_v1();
1516        let query2 = RtrSerialQuery::new(RtrProtocolVersion::V1, 100, 200);
1517
1518        let mut buffer = query1.encode();
1519        buffer.extend(query2.encode());
1520
1521        let mut cursor = Cursor::new(buffer);
1522
1523        // Read first PDU
1524        let pdu1 = read_rtr_pdu(&mut cursor).unwrap();
1525        assert!(matches!(pdu1, RtrPdu::ResetQuery(_)));
1526
1527        // Read second PDU
1528        let pdu2 = read_rtr_pdu(&mut cursor).unwrap();
1529        assert!(matches!(pdu2, RtrPdu::SerialQuery(_)));
1530    }
1531
1532    #[test]
1533    fn test_parse_with_extra_bytes() {
1534        // PDU followed by extra bytes - should only consume PDU length
1535        let query = RtrResetQuery::new_v1();
1536        let mut bytes = query.encode();
1537        bytes.extend_from_slice(&[0xDE, 0xAD, 0xBE, 0xEF]); // extra bytes
1538
1539        let (pdu, consumed) = parse_rtr_pdu(&bytes).unwrap();
1540        assert!(matches!(pdu, RtrPdu::ResetQuery(_)));
1541        assert_eq!(consumed, 8); // Only consumed the PDU, not extra bytes
1542    }
1543
1544    #[test]
1545    fn test_error_report_with_pdu_and_text() {
1546        // Full error report with both encapsulated PDU and error text
1547        let error = RtrErrorReport {
1548            version: RtrProtocolVersion::V1,
1549            error_code: RtrErrorCode::CorruptData,
1550            erroneous_pdu: vec![1, 2, 3, 4, 5, 6, 7, 8], // 8 bytes
1551            error_text: "Something went wrong!".to_string(), // 21 bytes
1552        };
1553        let bytes = error.encode();
1554
1555        let (pdu, _) = parse_rtr_pdu(&bytes).unwrap();
1556        match pdu {
1557            RtrPdu::ErrorReport(e) => {
1558                assert_eq!(e.error_code, RtrErrorCode::CorruptData);
1559                assert_eq!(e.erroneous_pdu, vec![1, 2, 3, 4, 5, 6, 7, 8]);
1560                assert_eq!(e.error_text, "Something went wrong!");
1561            }
1562            _ => panic!("Expected ErrorReport"),
1563        }
1564    }
1565
1566    /// Regression: an ErrorReport declaring an `error_text_len` larger than the
1567    /// buffer must return an error rather than panic on an out-of-bounds slice.
1568    #[test]
1569    fn test_error_report_oversized_text_len_no_panic() {
1570        let bytes: Vec<u8> = vec![
1571            0x01, 0x0A, 0x00, 0x00, // ver=1, type=10 (ErrorReport), error_code=0
1572            0x00, 0x00, 0x00, 0x10, // length = 16
1573            0x00, 0x00, 0x00, 0x00, // encap_pdu_len = 0
1574            0xFF, 0xFF, 0xFF, 0xFF, // error_text_len = 0xFFFFFFFF (huge)
1575        ];
1576        assert!(matches!(
1577            parse_rtr_pdu(&bytes),
1578            Err(RtrError::InvalidLength { .. })
1579        ));
1580    }
1581
1582    /// Regression: `read_rtr_pdu` must reject an implausibly large declared
1583    /// length before allocating, rather than attempting a multi-gigabyte
1584    /// allocation (memory-exhaustion DoS).
1585    #[test]
1586    fn test_read_rtr_pdu_rejects_oversized_length() {
1587        // Header claims length = 0xFFFFFFFF but no body follows.
1588        let header: Vec<u8> = vec![
1589            0x01, 0x03, 0x00, 0x00, // ver=1, type=3 (ResetQuery)
1590            0xFF, 0xFF, 0xFF, 0xFF, // length = 0xFFFFFFFF
1591        ];
1592        let mut reader = std::io::Cursor::new(header);
1593        assert!(matches!(
1594            read_rtr_pdu(&mut reader),
1595            Err(RtrError::InvalidLength { .. })
1596        ));
1597    }
1598
1599    /// An ErrorReport encapsulating a maximum-size PDU plus error text is
1600    /// larger than `RTR_MAX_PDU_LEN` but still valid; the allocation cap in
1601    /// `read_rtr_pdu` must not reject it.
1602    #[test]
1603    fn test_read_rtr_pdu_accepts_large_error_report() {
1604        let encap_len = RTR_MAX_PDU_LEN; // 65,535-byte erroneous PDU copy
1605        let text = "diagnostic text".repeat(10);
1606        let length = 8 + 4 + encap_len + 4 + text.len();
1607        assert!(length > RTR_MAX_PDU_LEN);
1608        assert!(length <= RTR_MAX_ERROR_REPORT_LEN);
1609
1610        let mut bytes: Vec<u8> = vec![0x01, 0x0A, 0x00, 0x00]; // ver=1, type=10, error_code=0
1611        bytes.extend((length as u32).to_be_bytes());
1612        bytes.extend((encap_len as u32).to_be_bytes());
1613        bytes.extend(std::iter::repeat_n(0xAAu8, encap_len));
1614        bytes.extend((text.len() as u32).to_be_bytes());
1615        bytes.extend(text.as_bytes());
1616
1617        let mut reader = std::io::Cursor::new(bytes);
1618        match read_rtr_pdu(&mut reader).unwrap() {
1619            RtrPdu::ErrorReport(report) => {
1620                assert_eq!(report.erroneous_pdu.len(), encap_len);
1621                assert_eq!(report.error_text, text);
1622            }
1623            _ => panic!("Expected ErrorReport"),
1624        }
1625    }
1626}