stackaddr/segment/
protocol.rs

1//! Protocol segment
2//!
3//! Defines the `Protocol` enum, which represents individual protocols
4//! used in layered network addressing. These include:
5//!
6//! - **Link-layer**: e.g. `/mac/aa:bb:cc:dd:ee:ff`
7//! - **Network-layer**: e.g. `/ip4/`, `/ip6/`
8//! - **Transport-layer**: e.g. `/tcp/`, `/udp/`, `/tls/`, `/quic/`
9//! - **Application-layer**: e.g. `/http/`, `/wtr/`, `/webrtc/`, `/onion/...`
10//!
11//! All variants serialize to a self-describing string form via `Display`, e.g.
12//! `/ip4/192.168.0.1/tcp/443/tls/http`.
13//!
14//! This enum is designed for composability within a [`StackAddr`](crate::StackAddr).
15
16use netdev::mac::MacAddr;
17use std::{
18    fmt,
19    net::{Ipv4Addr, Ipv6Addr},
20};
21
22#[cfg(feature = "serde")]
23use serde::{Deserialize, Serialize};
24
25/// A protocol segment used to build layered network addresses.
26///
27/// Each variant represents a well-known protocol at different layers (L2–L7),
28/// or a custom protocol. All variants are rendered as `/<name>/<value>` strings.
29#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
30#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
31pub enum Protocol {
32    /// MAC address (layer 2)
33    Mac(MacAddr),
34    /// IPv4 address (layer 3)
35    Ip4(Ipv4Addr),
36    /// IPv6 address (layer 3)
37    Ip6(Ipv6Addr),
38    /// DNS (unspecified family)
39    Dns(String),
40    /// DNS (IPv4)
41    Dns4(String),
42    /// DNS (IPv6)
43    Dns6(String),
44    /// TCP port (layer 4)
45    Tcp(u16),
46    /// UDP port (layer 4)
47    Udp(u16),
48    /// TLS (over TCP)
49    Tls,
50    /// QUIC (over UDP)
51    Quic,
52    /// HTTP protocol
53    Http,
54    /// HTTPS (alias for `/tls/http`)
55    Https,
56    /// WebSocket (with port)
57    Ws(u16),
58    /// Secure WebSocket (with port)
59    Wss(u16),
60    /// WebTransport (over QUIC or HTTP/3)
61    WebTransport(u16),
62    /// WebRTC
63    WebRTC,
64    /// Tor Onion address (v2 or v3)
65    Onion(String),
66    /// Arbitrary custom protocol
67    Custom(String),
68}
69
70impl fmt::Display for Protocol {
71    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72        use Protocol::*;
73        match self {
74            Ip4(addr) => write!(f, "/ip4/{}", addr),
75            Ip6(addr) => write!(f, "/ip6/{}", addr),
76            Dns(name) => write!(f, "/dns/{}", name),
77            Dns4(name) => write!(f, "/dns4/{}", name),
78            Dns6(name) => write!(f, "/dns6/{}", name),
79            Mac(addr) => write!(f, "/mac/{}", addr),
80            Tcp(port) => write!(f, "/tcp/{}", port),
81            Udp(port) => write!(f, "/udp/{}", port),
82            Tls => write!(f, "/tls"),
83            Quic => write!(f, "/quic"),
84            Http => write!(f, "/http"),
85            Https => write!(f, "/https"),
86            Ws(port) => write!(f, "/ws/{}", port),
87            Wss(port) => write!(f, "/wss/{}", port),
88            WebTransport(port) => write!(f, "/wtr/{}", port),
89            WebRTC => write!(f, "/webrtc"),
90            Onion(addr) => write!(f, "/onion/{}", addr),
91            Custom(name) => write!(f, "/custom/{}", name),
92        }
93    }
94}
95
96#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
97#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
98pub enum TransportProtocol {
99    /// TCP port
100    Tcp(u16),
101    /// UDP port
102    Udp(u16),
103    /// TLS (over TCP)
104    TlsOverTcp(u16),
105    /// QUIC (over UDP)
106    Quic(u16),
107    /// WebSocket with port
108    Ws(u16),
109    /// Secure WebSocket with port
110    Wss(u16),
111    /// WebTransport with port
112    WebTransport(u16),
113}
114
115impl TransportProtocol {
116    /// Get the port number associated with the transport protocol.
117    pub fn port(&self) -> u16 {
118        match self {
119            TransportProtocol::Tcp(p)
120            | TransportProtocol::Udp(p)
121            | TransportProtocol::TlsOverTcp(p)
122            | TransportProtocol::Quic(p)
123            | TransportProtocol::Ws(p)
124            | TransportProtocol::Wss(p)
125            | TransportProtocol::WebTransport(p) => *p,
126        }
127    }
128    /// Check if the transport protocol is secure. (by TLS)
129    pub fn is_secure(&self) -> bool {
130        matches!(
131            self,
132            TransportProtocol::TlsOverTcp(_)
133                | TransportProtocol::Quic(_)
134                | TransportProtocol::Wss(_)
135                | TransportProtocol::WebTransport(_)
136        )
137    }
138}
139
140impl fmt::Display for TransportProtocol {
141    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142        use TransportProtocol::*;
143        match self {
144            Tcp(port) => write!(f, "tcp/{}", port),
145            Udp(port) => write!(f, "udp/{}", port),
146            TlsOverTcp(port) => write!(f, "tls/tcp/{}", port),
147            Quic(port) => write!(f, "quic/{}", port),
148            Ws(port) => write!(f, "ws/{}", port),
149            Wss(port) => write!(f, "wss/{}", port),
150            WebTransport(port) => write!(f, "wtr/{}", port),
151        }
152    }
153}
154
155#[cfg(test)]
156mod tests {
157    use super::*;
158
159    #[test]
160    fn test_display_macaddr() {
161        use netdev::mac::MacAddr;
162        let mac = MacAddr::new(0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff);
163        let proto = vec![
164            Protocol::Mac(mac),
165            Protocol::Ip4("192.168.10.10".parse().unwrap()),
166        ];
167        let text = proto.iter().map(|p| p.to_string()).collect::<String>();
168        assert_eq!(text, "/mac/aa:bb:cc:dd:ee:ff/ip4/192.168.10.10");
169    }
170
171    #[test]
172    fn test_display_ip4_quic() {
173        let proto = vec![
174            Protocol::Ip4("127.0.0.1".parse().unwrap()),
175            Protocol::Udp(4433),
176            Protocol::Quic,
177        ];
178        let text = proto.iter().map(|p| p.to_string()).collect::<String>();
179        assert_eq!(text, "/ip4/127.0.0.1/udp/4433/quic");
180    }
181
182    #[test]
183    fn test_display_ip6_tcp_https() {
184        let proto = vec![
185            Protocol::Ip6("::1".parse().unwrap()),
186            Protocol::Tcp(443),
187            Protocol::Https,
188        ];
189        let text = proto.iter().map(|p| p.to_string()).collect::<String>();
190        assert_eq!(text, "/ip6/::1/tcp/443/https");
191    }
192}