1use super::ProtocolError;
4use crate::NodeAddr;
5use std::fmt;
6
7#[derive(Clone, Copy, Debug, PartialEq, Eq)]
17#[repr(u8)]
18pub enum HandshakeMessageType {
19 NoiseIKMsg1 = 0x01,
22
23 NoiseIKMsg2 = 0x02,
26}
27
28impl HandshakeMessageType {
29 pub fn from_byte(b: u8) -> Option<Self> {
31 match b {
32 0x01 => Some(HandshakeMessageType::NoiseIKMsg1),
33 0x02 => Some(HandshakeMessageType::NoiseIKMsg2),
34 _ => None,
35 }
36 }
37
38 pub fn to_byte(self) -> u8 {
40 self as u8
41 }
42
43 pub fn is_handshake(b: u8) -> bool {
45 matches!(b, 0x01 | 0x02)
46 }
47}
48
49impl fmt::Display for HandshakeMessageType {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 let name = match self {
52 HandshakeMessageType::NoiseIKMsg1 => "NoiseIKMsg1",
53 HandshakeMessageType::NoiseIKMsg2 => "NoiseIKMsg2",
54 };
55 write!(f, "{}", name)
56 }
57}
58
59#[derive(Clone, Copy, Debug, PartialEq, Eq)]
69#[repr(u8)]
70pub enum LinkMessageType {
71 SessionDatagram = 0x00,
75
76 SenderReport = 0x01,
79 ReceiverReport = 0x02,
81 TreeAnnounce = 0x10,
87
88 FilterAnnounce = 0x20,
91
92 LookupRequest = 0x30,
95 LookupResponse = 0x31,
97
98 Disconnect = 0x50,
101 Heartbeat = 0x51,
104}
105
106impl LinkMessageType {
107 pub fn from_byte(b: u8) -> Option<Self> {
109 match b {
110 0x00 => Some(LinkMessageType::SessionDatagram),
111 0x01 => Some(LinkMessageType::SenderReport),
112 0x02 => Some(LinkMessageType::ReceiverReport),
113 0x10 => Some(LinkMessageType::TreeAnnounce),
114 0x20 => Some(LinkMessageType::FilterAnnounce),
115 0x30 => Some(LinkMessageType::LookupRequest),
116 0x31 => Some(LinkMessageType::LookupResponse),
117 0x50 => Some(LinkMessageType::Disconnect),
118 0x51 => Some(LinkMessageType::Heartbeat),
119 _ => None,
120 }
121 }
122
123 pub fn to_byte(self) -> u8 {
125 self as u8
126 }
127}
128
129impl fmt::Display for LinkMessageType {
130 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131 let name = match self {
132 LinkMessageType::SessionDatagram => "SessionDatagram",
133 LinkMessageType::SenderReport => "SenderReport",
134 LinkMessageType::ReceiverReport => "ReceiverReport",
135 LinkMessageType::TreeAnnounce => "TreeAnnounce",
136 LinkMessageType::FilterAnnounce => "FilterAnnounce",
137 LinkMessageType::LookupRequest => "LookupRequest",
138 LinkMessageType::LookupResponse => "LookupResponse",
139 LinkMessageType::Disconnect => "Disconnect",
140 LinkMessageType::Heartbeat => "Heartbeat",
141 };
142 write!(f, "{}", name)
143 }
144}
145
146#[derive(Clone, Copy, Debug, PartialEq, Eq)]
152#[repr(u8)]
153pub enum DisconnectReason {
154 Shutdown = 0x00,
156 Restart = 0x01,
158 ProtocolError = 0x02,
160 TransportFailure = 0x03,
162 ResourceExhaustion = 0x04,
164 SecurityViolation = 0x05,
166 ConfigurationChange = 0x06,
168 Timeout = 0x07,
170 Other = 0xFF,
172}
173
174impl DisconnectReason {
175 pub fn from_byte(b: u8) -> Option<Self> {
177 match b {
178 0x00 => Some(DisconnectReason::Shutdown),
179 0x01 => Some(DisconnectReason::Restart),
180 0x02 => Some(DisconnectReason::ProtocolError),
181 0x03 => Some(DisconnectReason::TransportFailure),
182 0x04 => Some(DisconnectReason::ResourceExhaustion),
183 0x05 => Some(DisconnectReason::SecurityViolation),
184 0x06 => Some(DisconnectReason::ConfigurationChange),
185 0x07 => Some(DisconnectReason::Timeout),
186 0xFF => Some(DisconnectReason::Other),
187 _ => None,
188 }
189 }
190
191 pub fn to_byte(self) -> u8 {
193 self as u8
194 }
195}
196
197impl fmt::Display for DisconnectReason {
198 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199 let name = match self {
200 DisconnectReason::Shutdown => "Shutdown",
201 DisconnectReason::Restart => "Restart",
202 DisconnectReason::ProtocolError => "ProtocolError",
203 DisconnectReason::TransportFailure => "TransportFailure",
204 DisconnectReason::ResourceExhaustion => "ResourceExhaustion",
205 DisconnectReason::SecurityViolation => "SecurityViolation",
206 DisconnectReason::ConfigurationChange => "ConfigurationChange",
207 DisconnectReason::Timeout => "Timeout",
208 DisconnectReason::Other => "Other",
209 };
210 write!(f, "{}", name)
211 }
212}
213
214#[derive(Clone, Debug)]
231pub struct Disconnect {
232 pub reason: DisconnectReason,
234}
235
236impl Disconnect {
237 pub fn new(reason: DisconnectReason) -> Self {
239 Self { reason }
240 }
241
242 pub fn encode(&self) -> [u8; 2] {
244 [LinkMessageType::Disconnect.to_byte(), self.reason.to_byte()]
245 }
246
247 pub fn decode(payload: &[u8]) -> Result<Self, ProtocolError> {
249 if payload.is_empty() {
250 return Err(ProtocolError::MessageTooShort {
251 expected: 1,
252 got: 0,
253 });
254 }
255 let reason = DisconnectReason::from_byte(payload[0]).unwrap_or(DisconnectReason::Other);
256 Ok(Self { reason })
257 }
258}
259
260#[derive(Clone, Debug)]
286pub struct SessionDatagram {
287 pub src_addr: NodeAddr,
291 pub dest_addr: NodeAddr,
293 pub ttl: u8,
295 pub path_mtu: u16,
298 pub payload: Vec<u8>,
300}
301
302pub const SESSION_DATAGRAM_HEADER_SIZE: usize = 36;
304
305impl SessionDatagram {
306 pub fn new(src_addr: NodeAddr, dest_addr: NodeAddr, payload: Vec<u8>) -> Self {
308 Self {
309 src_addr,
310 dest_addr,
311 ttl: 64,
312 path_mtu: u16::MAX,
313 payload,
314 }
315 }
316
317 pub fn with_ttl(mut self, ttl: u8) -> Self {
319 self.ttl = ttl;
320 self
321 }
322
323 pub fn with_path_mtu(mut self, path_mtu: u16) -> Self {
325 self.path_mtu = path_mtu;
326 self
327 }
328
329 pub fn decrement_ttl(&mut self) -> bool {
331 if self.ttl > 0 {
332 self.ttl -= 1;
333 true
334 } else {
335 false
336 }
337 }
338
339 pub fn can_forward(&self) -> bool {
341 self.ttl > 0
342 }
343
344 pub fn encode(&self) -> Vec<u8> {
346 let mut buf = Vec::with_capacity(SESSION_DATAGRAM_HEADER_SIZE + self.payload.len());
347 buf.push(LinkMessageType::SessionDatagram.to_byte());
348 buf.push(self.ttl);
349 buf.extend_from_slice(&self.path_mtu.to_le_bytes());
350 buf.extend_from_slice(self.src_addr.as_bytes());
351 buf.extend_from_slice(self.dest_addr.as_bytes());
352 buf.extend_from_slice(&self.payload);
353 buf
354 }
355
356 pub fn decode(payload: &[u8]) -> Result<Self, ProtocolError> {
362 let r = SessionDatagramRef::decode(payload)?;
363 Ok(Self {
364 src_addr: r.src_addr,
365 dest_addr: r.dest_addr,
366 ttl: r.ttl,
367 path_mtu: r.path_mtu,
368 payload: r.payload.to_vec(),
369 })
370 }
371}
372
373#[derive(Debug, Clone, Copy)]
379pub struct SessionDatagramRef<'a> {
380 pub src_addr: NodeAddr,
381 pub dest_addr: NodeAddr,
382 pub ttl: u8,
383 pub path_mtu: u16,
384 pub payload: &'a [u8],
385}
386
387impl<'a> SessionDatagramRef<'a> {
388 pub fn decode(buf: &'a [u8]) -> Result<Self, ProtocolError> {
391 if buf.len() < 35 {
393 return Err(ProtocolError::MessageTooShort {
394 expected: 35,
395 got: buf.len(),
396 });
397 }
398 let ttl = buf[0];
399 let path_mtu = u16::from_le_bytes([buf[1], buf[2]]);
400 let mut src_bytes = [0u8; 16];
401 src_bytes.copy_from_slice(&buf[3..19]);
402 let mut dest_bytes = [0u8; 16];
403 dest_bytes.copy_from_slice(&buf[19..35]);
404 Ok(Self {
405 src_addr: NodeAddr::from_bytes(src_bytes),
406 dest_addr: NodeAddr::from_bytes(dest_bytes),
407 ttl,
408 path_mtu,
409 payload: &buf[35..],
410 })
411 }
412
413 pub const HEADER_LEN: usize = 35;
416}
417
418#[deprecated(note = "Use LinkMessageType or SessionMessageType instead")]
420pub type MessageType = LinkMessageType;
421
422#[cfg(test)]
423mod tests {
424 use super::*;
425
426 #[test]
429 fn test_handshake_message_type_roundtrip() {
430 let types = [
431 HandshakeMessageType::NoiseIKMsg1,
432 HandshakeMessageType::NoiseIKMsg2,
433 ];
434
435 for ty in types {
436 let byte = ty.to_byte();
437 let restored = HandshakeMessageType::from_byte(byte);
438 assert_eq!(restored, Some(ty));
439 }
440 }
441
442 #[test]
443 fn test_handshake_message_type_invalid() {
444 assert!(HandshakeMessageType::from_byte(0x00).is_none());
445 assert!(HandshakeMessageType::from_byte(0x03).is_none());
446 assert!(HandshakeMessageType::from_byte(0x10).is_none());
447 }
448
449 #[test]
450 fn test_handshake_message_type_is_handshake() {
451 assert!(HandshakeMessageType::is_handshake(0x01));
452 assert!(HandshakeMessageType::is_handshake(0x02));
453 assert!(!HandshakeMessageType::is_handshake(0x00));
454 assert!(!HandshakeMessageType::is_handshake(0x10));
455 }
456
457 #[test]
460 fn test_link_message_type_roundtrip() {
461 let types = [
462 LinkMessageType::TreeAnnounce,
463 LinkMessageType::FilterAnnounce,
464 LinkMessageType::LookupRequest,
465 LinkMessageType::LookupResponse,
466 LinkMessageType::SessionDatagram,
467 LinkMessageType::Disconnect,
468 LinkMessageType::Heartbeat,
469 ];
470
471 for ty in types {
472 let byte = ty.to_byte();
473 let restored = LinkMessageType::from_byte(byte);
474 assert_eq!(restored, Some(ty));
475 }
476 }
477
478 #[test]
479 fn test_link_message_type_invalid() {
480 assert!(LinkMessageType::from_byte(0xFF).is_none());
481 assert!(LinkMessageType::from_byte(0x03).is_none());
482 assert!(LinkMessageType::from_byte(0x04).is_none());
483 assert!(LinkMessageType::from_byte(0x40).is_none());
484 }
485
486 #[test]
489 fn test_disconnect_reason_roundtrip() {
490 let reasons = [
491 DisconnectReason::Shutdown,
492 DisconnectReason::Restart,
493 DisconnectReason::ProtocolError,
494 DisconnectReason::TransportFailure,
495 DisconnectReason::ResourceExhaustion,
496 DisconnectReason::SecurityViolation,
497 DisconnectReason::ConfigurationChange,
498 DisconnectReason::Timeout,
499 DisconnectReason::Other,
500 ];
501
502 for reason in reasons {
503 let byte = reason.to_byte();
504 let restored = DisconnectReason::from_byte(byte);
505 assert_eq!(restored, Some(reason));
506 }
507 }
508
509 #[test]
510 fn test_disconnect_reason_unknown_byte() {
511 assert!(DisconnectReason::from_byte(0x08).is_none());
512 assert!(DisconnectReason::from_byte(0x80).is_none());
513 assert!(DisconnectReason::from_byte(0xFE).is_none());
514 }
515
516 #[test]
519 fn test_disconnect_encode_decode() {
520 let msg = Disconnect::new(DisconnectReason::Shutdown);
521 let encoded = msg.encode();
522
523 assert_eq!(encoded.len(), 2);
524 assert_eq!(encoded[0], 0x50); assert_eq!(encoded[1], 0x00); let decoded = Disconnect::decode(&encoded[1..]).unwrap();
529 assert_eq!(decoded.reason, DisconnectReason::Shutdown);
530 }
531
532 #[test]
533 fn test_disconnect_all_reasons() {
534 let reasons = [
535 DisconnectReason::Shutdown,
536 DisconnectReason::Restart,
537 DisconnectReason::ProtocolError,
538 DisconnectReason::Other,
539 ];
540
541 for reason in reasons {
542 let msg = Disconnect::new(reason);
543 let encoded = msg.encode();
544 let decoded = Disconnect::decode(&encoded[1..]).unwrap();
545 assert_eq!(decoded.reason, reason);
546 }
547 }
548
549 #[test]
550 fn test_disconnect_decode_empty_payload() {
551 let result = Disconnect::decode(&[]);
552 assert!(result.is_err());
553 }
554
555 #[test]
556 fn test_disconnect_decode_unknown_reason() {
557 let decoded = Disconnect::decode(&[0x80]).unwrap();
558 assert_eq!(decoded.reason, DisconnectReason::Other);
559 }
560
561 fn make_node_addr(val: u8) -> NodeAddr {
564 let mut bytes = [0u8; 16];
565 bytes[0] = val;
566 NodeAddr::from_bytes(bytes)
567 }
568
569 #[test]
570 fn test_session_datagram_encode_decode() {
571 let src = make_node_addr(0xAA);
572 let dest = make_node_addr(0xBB);
573 let payload = vec![0x10, 0x00, 0x05, 0x00, 1, 2, 3, 4, 5]; let dg = SessionDatagram::new(src, dest, payload.clone()).with_ttl(32);
575
576 let encoded = dg.encode();
577 assert_eq!(encoded[0], 0x00); assert_eq!(encoded.len(), SESSION_DATAGRAM_HEADER_SIZE + payload.len());
579
580 let decoded = SessionDatagram::decode(&encoded[1..]).unwrap();
582 assert_eq!(decoded.src_addr, src);
583 assert_eq!(decoded.dest_addr, dest);
584 assert_eq!(decoded.ttl, 32);
585 assert_eq!(decoded.payload, payload);
586 }
587
588 #[test]
589 fn test_session_datagram_empty_payload() {
590 let dg = SessionDatagram::new(make_node_addr(1), make_node_addr(2), Vec::new());
591
592 let encoded = dg.encode();
593 assert_eq!(encoded.len(), SESSION_DATAGRAM_HEADER_SIZE);
594
595 let decoded = SessionDatagram::decode(&encoded[1..]).unwrap();
596 assert!(decoded.payload.is_empty());
597 }
598
599 #[test]
600 fn test_session_datagram_decode_too_short() {
601 assert!(SessionDatagram::decode(&[]).is_err());
602 assert!(SessionDatagram::decode(&[0x00; 20]).is_err());
603 }
604
605 #[test]
606 fn test_session_datagram_ttl_roundtrip() {
607 for hop in [0u8, 1, 64, 128, 255] {
608 let dg = SessionDatagram::new(make_node_addr(1), make_node_addr(2), vec![0x42])
609 .with_ttl(hop);
610
611 let encoded = dg.encode();
612 let decoded = SessionDatagram::decode(&encoded[1..]).unwrap();
613 assert_eq!(decoded.ttl, hop);
614 }
615 }
616}