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 candidate list ([`PeerTarget::direct_addrs`]) so
15/// the dialer (`dig-ip`) tries IPv6 first over the local∩peer intersection and falls back to IPv4, or
16/// fails if the peer has no known direct address (then the strategy moves on to the mapping/relay
17/// methods).
18#[derive(Debug, Default, Clone, Copy)]
19pub struct DirectMethod;
20
21#[async_trait]
22impl TraversalMethod for DirectMethod {
23    fn kind(&self) -> TraversalKind {
24        TraversalKind::Direct
25    }
26
27    async fn attempt(&self, peer: &PeerTarget) -> Result<MethodOutcome, MethodError> {
28        let addrs = peer.direct_addrs();
29        if addrs.is_empty() {
30            return Err(MethodError::failed(
31                TraversalKind::Direct,
32                "peer has no known direct address",
33            ));
34        }
35        // Carry the peer's whole candidate list; dig-ip orders it (IPv6-first, family-intersected)
36        // at dial time so the dial can fall back across families.
37        Ok(MethodOutcome::candidates(
38            TraversalKind::Direct,
39            addrs.to_vec(),
40        ))
41    }
42}