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 DirectEndpointData = 0x03,
87
88 TreeAnnounce = 0x10,
91
92 FilterAnnounce = 0x20,
95
96 LookupRequest = 0x30,
99 LookupResponse = 0x31,
101
102 Disconnect = 0x50,
105 Heartbeat = 0x51,
108}
109
110impl LinkMessageType {
111 pub fn from_byte(b: u8) -> Option<Self> {
113 match b {
114 0x00 => Some(LinkMessageType::SessionDatagram),
115 0x01 => Some(LinkMessageType::SenderReport),
116 0x02 => Some(LinkMessageType::ReceiverReport),
117 0x03 => Some(LinkMessageType::DirectEndpointData),
118 0x10 => Some(LinkMessageType::TreeAnnounce),
119 0x20 => Some(LinkMessageType::FilterAnnounce),
120 0x30 => Some(LinkMessageType::LookupRequest),
121 0x31 => Some(LinkMessageType::LookupResponse),
122 0x50 => Some(LinkMessageType::Disconnect),
123 0x51 => Some(LinkMessageType::Heartbeat),
124 _ => None,
125 }
126 }
127
128 pub fn to_byte(self) -> u8 {
130 self as u8
131 }
132}
133
134impl fmt::Display for LinkMessageType {
135 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136 let name = match self {
137 LinkMessageType::SessionDatagram => "SessionDatagram",
138 LinkMessageType::SenderReport => "SenderReport",
139 LinkMessageType::ReceiverReport => "ReceiverReport",
140 LinkMessageType::DirectEndpointData => "DirectEndpointData",
141 LinkMessageType::TreeAnnounce => "TreeAnnounce",
142 LinkMessageType::FilterAnnounce => "FilterAnnounce",
143 LinkMessageType::LookupRequest => "LookupRequest",
144 LinkMessageType::LookupResponse => "LookupResponse",
145 LinkMessageType::Disconnect => "Disconnect",
146 LinkMessageType::Heartbeat => "Heartbeat",
147 };
148 write!(f, "{}", name)
149 }
150}
151
152#[derive(Clone, Copy, Debug, PartialEq, Eq)]
158#[repr(u8)]
159pub enum DisconnectReason {
160 Shutdown = 0x00,
162 Restart = 0x01,
164 ProtocolError = 0x02,
166 TransportFailure = 0x03,
168 ResourceExhaustion = 0x04,
170 SecurityViolation = 0x05,
172 ConfigurationChange = 0x06,
174 Timeout = 0x07,
176 Other = 0xFF,
178}
179
180impl DisconnectReason {
181 pub fn from_byte(b: u8) -> Option<Self> {
183 match b {
184 0x00 => Some(DisconnectReason::Shutdown),
185 0x01 => Some(DisconnectReason::Restart),
186 0x02 => Some(DisconnectReason::ProtocolError),
187 0x03 => Some(DisconnectReason::TransportFailure),
188 0x04 => Some(DisconnectReason::ResourceExhaustion),
189 0x05 => Some(DisconnectReason::SecurityViolation),
190 0x06 => Some(DisconnectReason::ConfigurationChange),
191 0x07 => Some(DisconnectReason::Timeout),
192 0xFF => Some(DisconnectReason::Other),
193 _ => None,
194 }
195 }
196
197 pub fn to_byte(self) -> u8 {
199 self as u8
200 }
201}
202
203impl fmt::Display for DisconnectReason {
204 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
205 let name = match self {
206 DisconnectReason::Shutdown => "Shutdown",
207 DisconnectReason::Restart => "Restart",
208 DisconnectReason::ProtocolError => "ProtocolError",
209 DisconnectReason::TransportFailure => "TransportFailure",
210 DisconnectReason::ResourceExhaustion => "ResourceExhaustion",
211 DisconnectReason::SecurityViolation => "SecurityViolation",
212 DisconnectReason::ConfigurationChange => "ConfigurationChange",
213 DisconnectReason::Timeout => "Timeout",
214 DisconnectReason::Other => "Other",
215 };
216 write!(f, "{}", name)
217 }
218}
219
220#[derive(Clone, Debug)]
237pub struct Disconnect {
238 pub reason: DisconnectReason,
240}
241
242impl Disconnect {
243 pub fn new(reason: DisconnectReason) -> Self {
245 Self { reason }
246 }
247
248 pub fn encode(&self) -> [u8; 2] {
250 [LinkMessageType::Disconnect.to_byte(), self.reason.to_byte()]
251 }
252
253 pub fn decode(payload: &[u8]) -> Result<Self, ProtocolError> {
255 if payload.is_empty() {
256 return Err(ProtocolError::MessageTooShort {
257 expected: 1,
258 got: 0,
259 });
260 }
261 let reason = DisconnectReason::from_byte(payload[0]).unwrap_or(DisconnectReason::Other);
262 Ok(Self { reason })
263 }
264}
265
266#[derive(Clone, Debug)]
292pub struct SessionDatagram {
293 pub src_addr: NodeAddr,
297 pub dest_addr: NodeAddr,
299 pub ttl: u8,
301 pub path_mtu: u16,
304 pub payload: Vec<u8>,
306}
307
308pub const SESSION_DATAGRAM_HEADER_SIZE: usize = 36;
310
311impl SessionDatagram {
312 pub fn new(src_addr: NodeAddr, dest_addr: NodeAddr, payload: Vec<u8>) -> Self {
314 Self {
315 src_addr,
316 dest_addr,
317 ttl: 64,
318 path_mtu: u16::MAX,
319 payload,
320 }
321 }
322
323 pub fn with_ttl(mut self, ttl: u8) -> Self {
325 self.ttl = ttl;
326 self
327 }
328
329 pub fn with_path_mtu(mut self, path_mtu: u16) -> Self {
331 self.path_mtu = path_mtu;
332 self
333 }
334
335 pub fn decrement_ttl(&mut self) -> bool {
337 if self.ttl > 0 {
338 self.ttl -= 1;
339 true
340 } else {
341 false
342 }
343 }
344
345 pub fn can_forward(&self) -> bool {
347 self.ttl > 0
348 }
349
350 pub fn encode(&self) -> Vec<u8> {
352 let mut buf = Vec::with_capacity(SESSION_DATAGRAM_HEADER_SIZE + self.payload.len());
353 buf.push(LinkMessageType::SessionDatagram.to_byte());
354 buf.push(self.ttl);
355 buf.extend_from_slice(&self.path_mtu.to_le_bytes());
356 buf.extend_from_slice(self.src_addr.as_bytes());
357 buf.extend_from_slice(self.dest_addr.as_bytes());
358 buf.extend_from_slice(&self.payload);
359 buf
360 }
361
362 pub fn decode(payload: &[u8]) -> Result<Self, ProtocolError> {
368 let r = SessionDatagramRef::decode(payload)?;
369 Ok(Self {
370 src_addr: r.src_addr,
371 dest_addr: r.dest_addr,
372 ttl: r.ttl,
373 path_mtu: r.path_mtu,
374 payload: r.payload.to_vec(),
375 })
376 }
377}
378
379#[derive(Debug, Clone, Copy)]
385pub struct SessionDatagramRef<'a> {
386 pub src_addr: NodeAddr,
387 pub dest_addr: NodeAddr,
388 pub ttl: u8,
389 pub path_mtu: u16,
390 pub payload: &'a [u8],
391}
392
393impl<'a> SessionDatagramRef<'a> {
394 pub fn decode(buf: &'a [u8]) -> Result<Self, ProtocolError> {
397 if buf.len() < 35 {
399 return Err(ProtocolError::MessageTooShort {
400 expected: 35,
401 got: buf.len(),
402 });
403 }
404 let ttl = buf[0];
405 let path_mtu = u16::from_le_bytes([buf[1], buf[2]]);
406 let mut src_bytes = [0u8; 16];
407 src_bytes.copy_from_slice(&buf[3..19]);
408 let mut dest_bytes = [0u8; 16];
409 dest_bytes.copy_from_slice(&buf[19..35]);
410 Ok(Self {
411 src_addr: NodeAddr::from_bytes(src_bytes),
412 dest_addr: NodeAddr::from_bytes(dest_bytes),
413 ttl,
414 path_mtu,
415 payload: &buf[35..],
416 })
417 }
418
419 pub const HEADER_LEN: usize = 35;
422}
423
424#[deprecated(note = "Use LinkMessageType or SessionMessageType instead")]
426pub type MessageType = LinkMessageType;
427
428#[cfg(test)]
429mod tests {
430 use super::*;
431
432 #[test]
435 fn test_handshake_message_type_roundtrip() {
436 let types = [
437 HandshakeMessageType::NoiseIKMsg1,
438 HandshakeMessageType::NoiseIKMsg2,
439 ];
440
441 for ty in types {
442 let byte = ty.to_byte();
443 let restored = HandshakeMessageType::from_byte(byte);
444 assert_eq!(restored, Some(ty));
445 }
446 }
447
448 #[test]
449 fn test_handshake_message_type_invalid() {
450 assert!(HandshakeMessageType::from_byte(0x00).is_none());
451 assert!(HandshakeMessageType::from_byte(0x03).is_none());
452 assert!(HandshakeMessageType::from_byte(0x10).is_none());
453 }
454
455 #[test]
456 fn test_handshake_message_type_is_handshake() {
457 assert!(HandshakeMessageType::is_handshake(0x01));
458 assert!(HandshakeMessageType::is_handshake(0x02));
459 assert!(!HandshakeMessageType::is_handshake(0x00));
460 assert!(!HandshakeMessageType::is_handshake(0x10));
461 }
462
463 #[test]
466 fn test_link_message_type_roundtrip() {
467 let types = [
468 LinkMessageType::TreeAnnounce,
469 LinkMessageType::FilterAnnounce,
470 LinkMessageType::LookupRequest,
471 LinkMessageType::LookupResponse,
472 LinkMessageType::SessionDatagram,
473 LinkMessageType::DirectEndpointData,
474 LinkMessageType::Disconnect,
475 LinkMessageType::Heartbeat,
476 ];
477
478 for ty in types {
479 let byte = ty.to_byte();
480 let restored = LinkMessageType::from_byte(byte);
481 assert_eq!(restored, Some(ty));
482 }
483 }
484
485 #[test]
486 fn test_link_message_type_invalid() {
487 assert!(LinkMessageType::from_byte(0xFF).is_none());
488 assert!(LinkMessageType::from_byte(0x04).is_none());
489 assert!(LinkMessageType::from_byte(0x40).is_none());
490 }
491
492 #[test]
495 fn test_disconnect_reason_roundtrip() {
496 let reasons = [
497 DisconnectReason::Shutdown,
498 DisconnectReason::Restart,
499 DisconnectReason::ProtocolError,
500 DisconnectReason::TransportFailure,
501 DisconnectReason::ResourceExhaustion,
502 DisconnectReason::SecurityViolation,
503 DisconnectReason::ConfigurationChange,
504 DisconnectReason::Timeout,
505 DisconnectReason::Other,
506 ];
507
508 for reason in reasons {
509 let byte = reason.to_byte();
510 let restored = DisconnectReason::from_byte(byte);
511 assert_eq!(restored, Some(reason));
512 }
513 }
514
515 #[test]
516 fn test_disconnect_reason_unknown_byte() {
517 assert!(DisconnectReason::from_byte(0x08).is_none());
518 assert!(DisconnectReason::from_byte(0x80).is_none());
519 assert!(DisconnectReason::from_byte(0xFE).is_none());
520 }
521
522 #[test]
525 fn test_disconnect_encode_decode() {
526 let msg = Disconnect::new(DisconnectReason::Shutdown);
527 let encoded = msg.encode();
528
529 assert_eq!(encoded.len(), 2);
530 assert_eq!(encoded[0], 0x50); assert_eq!(encoded[1], 0x00); let decoded = Disconnect::decode(&encoded[1..]).unwrap();
535 assert_eq!(decoded.reason, DisconnectReason::Shutdown);
536 }
537
538 #[test]
539 fn test_disconnect_all_reasons() {
540 let reasons = [
541 DisconnectReason::Shutdown,
542 DisconnectReason::Restart,
543 DisconnectReason::ProtocolError,
544 DisconnectReason::Other,
545 ];
546
547 for reason in reasons {
548 let msg = Disconnect::new(reason);
549 let encoded = msg.encode();
550 let decoded = Disconnect::decode(&encoded[1..]).unwrap();
551 assert_eq!(decoded.reason, reason);
552 }
553 }
554
555 #[test]
556 fn test_disconnect_decode_empty_payload() {
557 let result = Disconnect::decode(&[]);
558 assert!(result.is_err());
559 }
560
561 #[test]
562 fn test_disconnect_decode_unknown_reason() {
563 let decoded = Disconnect::decode(&[0x80]).unwrap();
564 assert_eq!(decoded.reason, DisconnectReason::Other);
565 }
566
567 fn make_node_addr(val: u8) -> NodeAddr {
570 let mut bytes = [0u8; 16];
571 bytes[0] = val;
572 NodeAddr::from_bytes(bytes)
573 }
574
575 #[test]
576 fn test_session_datagram_encode_decode() {
577 let src = make_node_addr(0xAA);
578 let dest = make_node_addr(0xBB);
579 let payload = vec![0x10, 0x00, 0x05, 0x00, 1, 2, 3, 4, 5]; let dg = SessionDatagram::new(src, dest, payload.clone()).with_ttl(32);
581
582 let encoded = dg.encode();
583 assert_eq!(encoded[0], 0x00); assert_eq!(encoded.len(), SESSION_DATAGRAM_HEADER_SIZE + payload.len());
585
586 let decoded = SessionDatagram::decode(&encoded[1..]).unwrap();
588 assert_eq!(decoded.src_addr, src);
589 assert_eq!(decoded.dest_addr, dest);
590 assert_eq!(decoded.ttl, 32);
591 assert_eq!(decoded.payload, payload);
592 }
593
594 #[test]
595 fn test_session_datagram_empty_payload() {
596 let dg = SessionDatagram::new(make_node_addr(1), make_node_addr(2), Vec::new());
597
598 let encoded = dg.encode();
599 assert_eq!(encoded.len(), SESSION_DATAGRAM_HEADER_SIZE);
600
601 let decoded = SessionDatagram::decode(&encoded[1..]).unwrap();
602 assert!(decoded.payload.is_empty());
603 }
604
605 #[test]
606 fn test_session_datagram_decode_too_short() {
607 assert!(SessionDatagram::decode(&[]).is_err());
608 assert!(SessionDatagram::decode(&[0x00; 20]).is_err());
609 }
610
611 #[test]
612 fn test_session_datagram_ttl_roundtrip() {
613 for hop in [0u8, 1, 64, 128, 255] {
614 let dg = SessionDatagram::new(make_node_addr(1), make_node_addr(2), vec![0x42])
615 .with_ttl(hop);
616
617 let encoded = dg.encode();
618 let decoded = SessionDatagram::decode(&encoded[1..]).unwrap();
619 assert_eq!(decoded.ttl, hop);
620 }
621 }
622}