Skip to main content

fips_core/transport/
handle.rs

1//! Enum wrapper for concrete transport implementations.
2
3#[cfg(target_os = "linux")]
4use super::ble::DefaultBleTransport;
5#[cfg(any(target_os = "linux", target_os = "macos"))]
6use super::ethernet::EthernetTransport;
7#[cfg(feature = "sim-transport")]
8use super::sim::SimTransport;
9use super::tcp::TcpTransport;
10use super::tor::TorTransport;
11use super::tor::control::TorMonitoringInfo;
12use super::udp::UdpTransport;
13#[cfg(feature = "webrtc-transport")]
14use super::webrtc::WebRtcTransport;
15use super::{
16    ConnectionState, DiscoveredPeer, Transport, TransportAddr, TransportCongestion, TransportError,
17    TransportId, TransportState, TransportType,
18};
19
20/// Wrapper enum for concrete transport implementations.
21///
22/// This enables polymorphic transport handling without trait objects,
23/// supporting async methods that the sync Transport trait cannot express.
24pub enum TransportHandle {
25    /// UDP/IP transport.
26    Udp(UdpTransport),
27    /// In-memory simulated packet transport.
28    #[cfg(feature = "sim-transport")]
29    Sim(SimTransport),
30    /// Raw Ethernet transport.
31    #[cfg(any(target_os = "linux", target_os = "macos"))]
32    Ethernet(EthernetTransport),
33    /// TCP/IP transport.
34    Tcp(TcpTransport),
35    /// Tor transport (via SOCKS5).
36    Tor(TorTransport),
37    /// WebRTC DataChannel transport.
38    #[cfg(feature = "webrtc-transport")]
39    WebRtc(Box<WebRtcTransport>),
40    /// BLE L2CAP transport.
41    #[cfg(target_os = "linux")]
42    Ble(DefaultBleTransport),
43}
44
45impl TransportHandle {
46    /// Start the transport asynchronously.
47    pub async fn start(&mut self) -> Result<(), TransportError> {
48        match self {
49            TransportHandle::Udp(t) => t.start_async().await,
50            #[cfg(feature = "sim-transport")]
51            TransportHandle::Sim(t) => t.start_async().await,
52            #[cfg(any(target_os = "linux", target_os = "macos"))]
53            TransportHandle::Ethernet(t) => t.start_async().await,
54            TransportHandle::Tcp(t) => t.start_async().await,
55            TransportHandle::Tor(t) => t.start_async().await,
56            #[cfg(feature = "webrtc-transport")]
57            TransportHandle::WebRtc(t) => t.start_async().await,
58            #[cfg(target_os = "linux")]
59            TransportHandle::Ble(t) => t.start_async().await,
60        }
61    }
62
63    /// Stop the transport asynchronously.
64    pub async fn stop(&mut self) -> Result<(), TransportError> {
65        match self {
66            TransportHandle::Udp(t) => t.stop_async().await,
67            #[cfg(feature = "sim-transport")]
68            TransportHandle::Sim(t) => t.stop_async().await,
69            #[cfg(any(target_os = "linux", target_os = "macos"))]
70            TransportHandle::Ethernet(t) => t.stop_async().await,
71            TransportHandle::Tcp(t) => t.stop_async().await,
72            TransportHandle::Tor(t) => t.stop_async().await,
73            #[cfg(feature = "webrtc-transport")]
74            TransportHandle::WebRtc(t) => t.stop_async().await,
75            #[cfg(target_os = "linux")]
76            TransportHandle::Ble(t) => t.stop_async().await,
77        }
78    }
79
80    /// Send data to a remote address asynchronously.
81    pub async fn send(&self, addr: &TransportAddr, data: &[u8]) -> Result<usize, TransportError> {
82        match self {
83            TransportHandle::Udp(t) => t.send_async(addr, data).await,
84            #[cfg(feature = "sim-transport")]
85            TransportHandle::Sim(t) => t.send_async(addr, data).await,
86            #[cfg(any(target_os = "linux", target_os = "macos"))]
87            TransportHandle::Ethernet(t) => t.send_async(addr, data).await,
88            TransportHandle::Tcp(t) => t.send_async(addr, data).await,
89            TransportHandle::Tor(t) => t.send_async(addr, data).await,
90            #[cfg(feature = "webrtc-transport")]
91            TransportHandle::WebRtc(t) => t.send_async(addr, data).await,
92            #[cfg(target_os = "linux")]
93            TransportHandle::Ble(t) => t.send_async(addr, data).await,
94        }
95    }
96
97    /// Get the transport ID.
98    pub fn transport_id(&self) -> TransportId {
99        match self {
100            TransportHandle::Udp(t) => t.transport_id(),
101            #[cfg(feature = "sim-transport")]
102            TransportHandle::Sim(t) => t.transport_id(),
103            #[cfg(any(target_os = "linux", target_os = "macos"))]
104            TransportHandle::Ethernet(t) => t.transport_id(),
105            TransportHandle::Tcp(t) => t.transport_id(),
106            TransportHandle::Tor(t) => t.transport_id(),
107            #[cfg(feature = "webrtc-transport")]
108            TransportHandle::WebRtc(t) => t.transport_id(),
109            #[cfg(target_os = "linux")]
110            TransportHandle::Ble(t) => t.transport_id(),
111        }
112    }
113
114    /// Get the instance name (if configured as a named instance).
115    pub fn name(&self) -> Option<&str> {
116        match self {
117            TransportHandle::Udp(t) => t.name(),
118            #[cfg(feature = "sim-transport")]
119            TransportHandle::Sim(t) => t.name(),
120            #[cfg(any(target_os = "linux", target_os = "macos"))]
121            TransportHandle::Ethernet(t) => t.name(),
122            TransportHandle::Tcp(t) => t.name(),
123            TransportHandle::Tor(t) => t.name(),
124            #[cfg(feature = "webrtc-transport")]
125            TransportHandle::WebRtc(t) => t.name(),
126            #[cfg(target_os = "linux")]
127            TransportHandle::Ble(t) => t.name(),
128        }
129    }
130
131    /// Get the transport type metadata.
132    pub fn transport_type(&self) -> &TransportType {
133        match self {
134            TransportHandle::Udp(t) => t.transport_type(),
135            #[cfg(feature = "sim-transport")]
136            TransportHandle::Sim(t) => t.transport_type(),
137            #[cfg(any(target_os = "linux", target_os = "macos"))]
138            TransportHandle::Ethernet(t) => t.transport_type(),
139            TransportHandle::Tcp(t) => t.transport_type(),
140            TransportHandle::Tor(t) => t.transport_type(),
141            #[cfg(feature = "webrtc-transport")]
142            TransportHandle::WebRtc(t) => t.transport_type(),
143            #[cfg(target_os = "linux")]
144            TransportHandle::Ble(t) => t.transport_type(),
145        }
146    }
147
148    /// Get current transport state.
149    pub fn state(&self) -> TransportState {
150        match self {
151            TransportHandle::Udp(t) => t.state(),
152            #[cfg(feature = "sim-transport")]
153            TransportHandle::Sim(t) => t.state(),
154            #[cfg(any(target_os = "linux", target_os = "macos"))]
155            TransportHandle::Ethernet(t) => t.state(),
156            TransportHandle::Tcp(t) => t.state(),
157            TransportHandle::Tor(t) => t.state(),
158            #[cfg(feature = "webrtc-transport")]
159            TransportHandle::WebRtc(t) => t.state(),
160            #[cfg(target_os = "linux")]
161            TransportHandle::Ble(t) => t.state(),
162        }
163    }
164
165    /// Get the transport MTU.
166    pub fn mtu(&self) -> u16 {
167        match self {
168            TransportHandle::Udp(t) => t.mtu(),
169            #[cfg(feature = "sim-transport")]
170            TransportHandle::Sim(t) => t.mtu(),
171            #[cfg(any(target_os = "linux", target_os = "macos"))]
172            TransportHandle::Ethernet(t) => t.mtu(),
173            TransportHandle::Tcp(t) => t.mtu(),
174            TransportHandle::Tor(t) => t.mtu(),
175            #[cfg(feature = "webrtc-transport")]
176            TransportHandle::WebRtc(t) => t.mtu(),
177            #[cfg(target_os = "linux")]
178            TransportHandle::Ble(t) => t.mtu(),
179        }
180    }
181
182    /// Get the MTU for a specific link address.
183    ///
184    /// Falls back to transport-wide MTU if the transport doesn't
185    /// support per-link MTU or the address is unknown.
186    pub fn link_mtu(&self, addr: &TransportAddr) -> u16 {
187        match self {
188            TransportHandle::Udp(t) => t.link_mtu(addr),
189            #[cfg(feature = "sim-transport")]
190            TransportHandle::Sim(t) => t.link_mtu(addr),
191            #[cfg(any(target_os = "linux", target_os = "macos"))]
192            TransportHandle::Ethernet(t) => t.link_mtu(addr),
193            TransportHandle::Tcp(t) => t.link_mtu(addr),
194            TransportHandle::Tor(t) => t.link_mtu(addr),
195            #[cfg(feature = "webrtc-transport")]
196            TransportHandle::WebRtc(t) => t.link_mtu(addr),
197            #[cfg(target_os = "linux")]
198            TransportHandle::Ble(t) => t.link_mtu(addr),
199        }
200    }
201
202    /// Get the local bound address (UDP/TCP only, returns None for other transports).
203    pub fn local_addr(&self) -> Option<std::net::SocketAddr> {
204        match self {
205            TransportHandle::Udp(t) => t.local_addr(),
206            #[cfg(feature = "sim-transport")]
207            TransportHandle::Sim(_) => None,
208            #[cfg(any(target_os = "linux", target_os = "macos"))]
209            TransportHandle::Ethernet(_) => None,
210            TransportHandle::Tcp(t) => t.local_addr(),
211            TransportHandle::Tor(_) => None,
212            #[cfg(feature = "webrtc-transport")]
213            TransportHandle::WebRtc(_) => None,
214            #[cfg(target_os = "linux")]
215            TransportHandle::Ble(_) => None,
216        }
217    }
218
219    /// Resolve a UDP target only if doing so cannot block on DNS.
220    ///
221    /// Numeric UDP addresses are returned directly; hostnames are returned
222    /// only when the UDP transport already has a fresh cached resolution.
223    pub(crate) fn resolved_udp_socket_addr_if_cached(
224        &self,
225        addr: &TransportAddr,
226    ) -> Option<std::net::SocketAddr> {
227        match self {
228            TransportHandle::Udp(t) => t.resolved_socket_addr_if_cached(addr),
229            _ => None,
230        }
231    }
232
233    /// Get the interface name (Ethernet only, returns None for other transports).
234    pub fn interface_name(&self) -> Option<&str> {
235        match self {
236            TransportHandle::Udp(_) => None,
237            #[cfg(feature = "sim-transport")]
238            TransportHandle::Sim(_) => None,
239            #[cfg(any(target_os = "linux", target_os = "macos"))]
240            TransportHandle::Ethernet(t) => Some(t.interface_name()),
241            TransportHandle::Tcp(_) => None,
242            TransportHandle::Tor(_) => None,
243            #[cfg(feature = "webrtc-transport")]
244            TransportHandle::WebRtc(_) => None,
245            #[cfg(target_os = "linux")]
246            TransportHandle::Ble(_) => None,
247        }
248    }
249
250    /// Get the onion service address (Tor only, returns None for other transports).
251    pub fn onion_address(&self) -> Option<&str> {
252        match self {
253            TransportHandle::Tor(t) => t.onion_address(),
254            _ => None,
255        }
256    }
257
258    /// Get cached Tor daemon monitoring info (Tor only).
259    pub fn tor_monitoring(&self) -> Option<TorMonitoringInfo> {
260        match self {
261            TransportHandle::Tor(t) => t.cached_monitoring(),
262            _ => None,
263        }
264    }
265
266    /// Get the Tor transport mode (Tor only).
267    pub fn tor_mode(&self) -> Option<&str> {
268        match self {
269            TransportHandle::Tor(t) => Some(t.mode()),
270            _ => None,
271        }
272    }
273
274    /// Drain discovered peers from this transport.
275    pub fn discover(&self) -> Result<Vec<DiscoveredPeer>, TransportError> {
276        match self {
277            TransportHandle::Udp(t) => t.discover(),
278            #[cfg(feature = "sim-transport")]
279            TransportHandle::Sim(t) => t.discover(),
280            #[cfg(any(target_os = "linux", target_os = "macos"))]
281            TransportHandle::Ethernet(t) => t.discover(),
282            TransportHandle::Tcp(t) => t.discover(),
283            TransportHandle::Tor(t) => t.discover(),
284            #[cfg(feature = "webrtc-transport")]
285            TransportHandle::WebRtc(t) => t.discover(),
286            #[cfg(target_os = "linux")]
287            TransportHandle::Ble(t) => t.discover(),
288        }
289    }
290
291    /// Whether this transport auto-connects to discovered peers.
292    pub fn auto_connect(&self) -> bool {
293        match self {
294            TransportHandle::Udp(t) => t.auto_connect(),
295            #[cfg(feature = "sim-transport")]
296            TransportHandle::Sim(t) => t.auto_connect(),
297            #[cfg(any(target_os = "linux", target_os = "macos"))]
298            TransportHandle::Ethernet(t) => t.auto_connect(),
299            TransportHandle::Tcp(t) => t.auto_connect(),
300            TransportHandle::Tor(t) => t.auto_connect(),
301            #[cfg(feature = "webrtc-transport")]
302            TransportHandle::WebRtc(t) => t.auto_connect(),
303            #[cfg(target_os = "linux")]
304            TransportHandle::Ble(t) => t.auto_connect(),
305        }
306    }
307
308    /// Whether this transport accepts inbound connections.
309    pub fn accept_connections(&self) -> bool {
310        match self {
311            TransportHandle::Udp(t) => t.accept_connections(),
312            #[cfg(feature = "sim-transport")]
313            TransportHandle::Sim(t) => t.accept_connections(),
314            #[cfg(any(target_os = "linux", target_os = "macos"))]
315            TransportHandle::Ethernet(t) => t.accept_connections(),
316            TransportHandle::Tcp(t) => t.accept_connections(),
317            TransportHandle::Tor(t) => t.accept_connections(),
318            #[cfg(feature = "webrtc-transport")]
319            TransportHandle::WebRtc(t) => t.accept_connections(),
320            #[cfg(target_os = "linux")]
321            TransportHandle::Ble(t) => t.accept_connections(),
322        }
323    }
324
325    /// Initiate a non-blocking connection to a remote address.
326    ///
327    /// For connection-oriented transports (TCP, Tor), spawns a background
328    /// task to establish the connection. For connectionless transports
329    /// (UDP, Ethernet), this is a no-op that returns Ok immediately.
330    ///
331    /// Poll `connection_state()` to check when the connection is ready.
332    pub async fn connect(&self, addr: &TransportAddr) -> Result<(), TransportError> {
333        match self {
334            TransportHandle::Udp(_) => Ok(()), // connectionless
335            #[cfg(feature = "sim-transport")]
336            TransportHandle::Sim(_) => Ok(()), // connectionless
337            #[cfg(any(target_os = "linux", target_os = "macos"))]
338            TransportHandle::Ethernet(_) => Ok(()), // connectionless
339            TransportHandle::Tcp(t) => t.connect_async(addr).await,
340            TransportHandle::Tor(t) => t.connect_async(addr).await,
341            #[cfg(feature = "webrtc-transport")]
342            TransportHandle::WebRtc(t) => t.connect_async(addr).await,
343            #[cfg(target_os = "linux")]
344            TransportHandle::Ble(t) => t.connect_async(addr).await,
345        }
346    }
347
348    /// Query the state of a connection attempt to a remote address.
349    ///
350    /// For connectionless transports, always returns `ConnectionState::Connected`
351    /// (they are always "connected"). For connection-oriented transports, returns
352    /// the current state of the background connection attempt.
353    pub fn connection_state(&self, addr: &TransportAddr) -> ConnectionState {
354        match self {
355            TransportHandle::Udp(_) => ConnectionState::Connected,
356            #[cfg(feature = "sim-transport")]
357            TransportHandle::Sim(_) => ConnectionState::Connected,
358            #[cfg(any(target_os = "linux", target_os = "macos"))]
359            TransportHandle::Ethernet(_) => ConnectionState::Connected,
360            TransportHandle::Tcp(t) => t.connection_state_sync(addr),
361            TransportHandle::Tor(t) => t.connection_state_sync(addr),
362            #[cfg(feature = "webrtc-transport")]
363            TransportHandle::WebRtc(t) => t.connection_state_sync(addr),
364            #[cfg(target_os = "linux")]
365            TransportHandle::Ble(t) => t.connection_state_sync(addr),
366        }
367    }
368
369    /// Close a specific connection on this transport.
370    ///
371    /// No-op for connectionless transports. For TCP/Tor, removes the
372    /// connection from the pool and drops the stream.
373    pub async fn close_connection(&self, addr: &TransportAddr) {
374        match self {
375            TransportHandle::Udp(t) => t.close_connection(addr),
376            #[cfg(feature = "sim-transport")]
377            TransportHandle::Sim(t) => t.close_connection(addr),
378            #[cfg(any(target_os = "linux", target_os = "macos"))]
379            TransportHandle::Ethernet(t) => t.close_connection(addr),
380            TransportHandle::Tcp(t) => t.close_connection_async(addr).await,
381            TransportHandle::Tor(t) => t.close_connection_async(addr).await,
382            #[cfg(feature = "webrtc-transport")]
383            TransportHandle::WebRtc(t) => t.close_connection_async(addr).await,
384            #[cfg(target_os = "linux")]
385            TransportHandle::Ble(t) => t.close_connection_async(addr).await,
386        }
387    }
388
389    /// Check if transport is operational.
390    pub fn is_operational(&self) -> bool {
391        self.state().is_operational()
392    }
393
394    /// Query transport-local congestion indicators.
395    ///
396    /// Returns a snapshot of congestion signals that the transport can
397    /// observe locally (e.g., kernel receive buffer drops). Fields are
398    /// `None` when the transport doesn't support that signal.
399    pub fn congestion(&self) -> TransportCongestion {
400        match self {
401            TransportHandle::Udp(t) => t.congestion(),
402            #[cfg(feature = "sim-transport")]
403            TransportHandle::Sim(_) => TransportCongestion::default(),
404            #[cfg(any(target_os = "linux", target_os = "macos"))]
405            TransportHandle::Ethernet(_) => TransportCongestion::default(),
406            TransportHandle::Tcp(_) => TransportCongestion::default(),
407            TransportHandle::Tor(_) => TransportCongestion::default(),
408            #[cfg(feature = "webrtc-transport")]
409            TransportHandle::WebRtc(_) => TransportCongestion::default(),
410            #[cfg(target_os = "linux")]
411            TransportHandle::Ble(_) => TransportCongestion::default(),
412        }
413    }
414
415    /// Get transport-specific stats as a JSON value.
416    ///
417    /// Returns a snapshot of counters for the specific transport type.
418    pub fn transport_stats(&self) -> serde_json::Value {
419        match self {
420            TransportHandle::Udp(t) => {
421                serde_json::to_value(t.stats().snapshot()).unwrap_or_default()
422            }
423            #[cfg(feature = "sim-transport")]
424            TransportHandle::Sim(t) => serde_json::to_value(t.stats()).unwrap_or_default(),
425            #[cfg(any(target_os = "linux", target_os = "macos"))]
426            TransportHandle::Ethernet(t) => {
427                let snap = t.stats().snapshot();
428                serde_json::json!({
429                    "frames_sent": snap.frames_sent,
430                    "frames_recv": snap.frames_recv,
431                    "bytes_sent": snap.bytes_sent,
432                    "bytes_recv": snap.bytes_recv,
433                    "send_errors": snap.send_errors,
434                    "recv_errors": snap.recv_errors,
435                    "beacons_sent": snap.beacons_sent,
436                    "beacons_recv": snap.beacons_recv,
437                    "frames_too_short": snap.frames_too_short,
438                    "frames_too_long": snap.frames_too_long,
439                })
440            }
441            TransportHandle::Tcp(t) => {
442                serde_json::to_value(t.stats().snapshot()).unwrap_or_default()
443            }
444            TransportHandle::Tor(t) => {
445                serde_json::to_value(t.stats().snapshot()).unwrap_or_default()
446            }
447            #[cfg(feature = "webrtc-transport")]
448            TransportHandle::WebRtc(t) => serde_json::json!({
449                "mtu": t.mtu(),
450                "state": t.state().to_string(),
451            }),
452            #[cfg(target_os = "linux")]
453            TransportHandle::Ble(t) => {
454                serde_json::to_value(t.stats().snapshot()).unwrap_or_default()
455            }
456        }
457    }
458}