Skip to main content

dig_nat/method/
direct.rs

1//! Direct method — the peer is already reachable at a known `ip:port` (publicly routable, or its
2//! operator port-forwarded it). No NAT work needed: just hand the strategy the address to dial.
3//!
4//! This is FIRST in the traversal order because when it works it is the cheapest and lowest-latency
5//! path. It "succeeds" merely by having an address; whether the dial then completes is the
6//! strategy's mTLS step (a refused dial there falls through to the next method).
7
8use async_trait::async_trait;
9
10use crate::error::MethodError;
11use crate::method::{MethodOutcome, TraversalKind, TraversalMethod};
12use crate::peer::PeerTarget;
13
14/// The direct-dial method: yields the peer's whole IPv6-first candidate list
15/// ([`PeerTarget::direct_addrs`]) so the dialer tries IPv6 first and falls back to IPv4, or fails if
16/// the peer has no known direct address (then the strategy moves on to the mapping/relay methods).
17#[derive(Debug, Default, Clone, Copy)]
18pub struct DirectMethod;
19
20#[async_trait]
21impl TraversalMethod for DirectMethod {
22    fn kind(&self) -> TraversalKind {
23        TraversalKind::Direct
24    }
25
26    async fn attempt(&self, peer: &PeerTarget) -> Result<MethodOutcome, MethodError> {
27        let addrs = peer.direct_addrs();
28        if addrs.is_empty() {
29            return Err(MethodError::failed(
30                TraversalKind::Direct,
31                "peer has no known direct address",
32            ));
33        }
34        // The peer's candidate list is already IPv6-first; carry all of it so the dial can fall back.
35        Ok(MethodOutcome::candidates(
36            TraversalKind::Direct,
37            addrs.to_vec(),
38        ))
39    }
40}