dig_nat/accept.rs
1//! The RESPONDER side of a relayed connection — run the server half of the mTLS handshake over an
2//! INTRODUCED relay circuit.
3//!
4//! A relay circuit needs exactly one mTLS **client** and one mTLS **server**. The [`crate::dialer`]
5//! ([`MtlsDialer::dial`](crate::dialer::MtlsDialer)) is the client: it opens a tunnel to the peer and
6//! runs [`PeerSession::client`]. This module is the missing counterpart: the reservation-HOLDER that
7//! RECEIVES an introduced circuit ([`RelayStatus::enable_accept`](crate::relay::RelayStatus::enable_accept)
8//! surfaces it as a [`RelayTunnel`]) accepts it here and runs [`PeerSession::server`] behind a
9//! [`TlsAcceptor`]. Without this, both ends of a relay circuit acted as TLS client and the handshake
10//! deadlocked (`got ClientHello when expecting ServerHello`, #1536).
11//!
12//! The accepted connection carries the IDENTICAL dig-tls mTLS as a direct one — the server verifies
13//! the client's `peer_id = SHA-256(SPKI DER)` + rustls proof-of-possession + the #1204 BLS binding.
14//! A server does NOT pin a specific caller (it accepts any authenticated DIG peer); the caller's
15//! verified identity is read from the handshake and reported on the returned [`PeerConnection`].
16
17use std::net::SocketAddr;
18use std::sync::Arc;
19
20use dig_tls::{BindingPolicy, NodeCert};
21use tokio_rustls::TlsAcceptor;
22
23use crate::error::MethodError;
24use crate::method::TraversalKind;
25use crate::mux::PeerSession;
26use crate::peer::PeerConnection;
27use crate::relay::RelayTunnel;
28use crate::tunnel::RelayTunnelStream;
29
30/// The unspecified address recorded as [`PeerConnection::remote_addr`] for an accepted relayed
31/// circuit when no relay endpoint was supplied — the byte path is the relay tunnel, not an IP the
32/// responder dialed, so the address is observability-only. Set a real relay endpoint with
33/// [`RelayAcceptor::with_relay_endpoint`] when one is known.
34fn unspecified_addr() -> SocketAddr {
35 SocketAddr::from(([0u8; 16], 0))
36}
37
38/// Accepts INTRODUCED relay circuits: turns a server-role [`RelayTunnel`] (delivered by
39/// [`RelayStatus::enable_accept`](crate::relay::RelayStatus::enable_accept)) into an authenticated
40/// [`PeerConnection`] by running the SERVER half of the dig-tls mTLS handshake over it.
41///
42/// Holds this node's [`NodeCert`] (presented as the server cert) and the [`BindingPolicy`] applied
43/// to the connecting peer's #1204 cert binding — the mirror image of
44/// [`MtlsDialer`](crate::dialer::MtlsDialer). The [`NodeCert`] is shared behind an [`Arc`] (its
45/// private key is held in a scrubbing wrapper), so cloning the acceptor never copies key material.
46#[derive(Clone)]
47pub struct RelayAcceptor {
48 node: Arc<NodeCert>,
49 binding_policy: BindingPolicy,
50 relay_endpoint: SocketAddr,
51}
52
53impl RelayAcceptor {
54 /// Build an acceptor that authenticates as `node` (presents its dig-tls cert as the mTLS server
55 /// cert) with the default [`BindingPolicy::Opportunistic`] cert-binding stance. The recorded
56 /// remote address defaults to unspecified (set one with [`with_relay_endpoint`](Self::with_relay_endpoint)).
57 pub fn new(node: Arc<NodeCert>) -> Self {
58 RelayAcceptor {
59 node,
60 binding_policy: BindingPolicy::default(),
61 relay_endpoint: unspecified_addr(),
62 }
63 }
64
65 /// Set the BLS cert-binding verification stance (#1204) for peer certs this acceptor verifies.
66 pub fn with_binding_policy(mut self, policy: BindingPolicy) -> Self {
67 self.binding_policy = policy;
68 self
69 }
70
71 /// Record the relay endpoint the accepted circuits are forwarded through — used only as
72 /// [`PeerConnection::remote_addr`] (observability); it never affects the mTLS identity check.
73 pub fn with_relay_endpoint(mut self, endpoint: SocketAddr) -> Self {
74 self.relay_endpoint = endpoint;
75 self
76 }
77
78 /// Accept one introduced relay circuit: run the dig-tls mTLS SERVER handshake over `tunnel`,
79 /// then wrap the authenticated byte stream in a yamux [`PeerSession::server`] so the caller can
80 /// accept the peer's inbound streams (availability + range fetches). The returned
81 /// [`PeerConnection`] reports the connecting peer's VERIFIED `peer_id` (from the client cert) and
82 /// its #1204 BLS binding — the same authentication a direct inbound connection gets.
83 ///
84 /// Uses [`dig_tls::server_config_spki_pinned`] so a live §5.2 self-signed peer leaf is accepted
85 /// (the #1378 CA-everywhere migration is deferred) — mirroring the dialer's
86 /// [`client_config_spki_pinned`](dig_tls::client_config_spki_pinned).
87 pub async fn accept(&self, tunnel: RelayTunnel) -> Result<PeerConnection, MethodError> {
88 let kind = TraversalKind::Relayed;
89 let server_tls = dig_tls::server_config_spki_pinned(&self.node, self.binding_policy)
90 .map_err(|e| MethodError::failed(kind, format!("server cert config: {e}")))?;
91 let captured = server_tls.captured_peer_id;
92 let captured_bls = server_tls.captured_bls;
93 let acceptor = TlsAcceptor::from(server_tls.config);
94
95 let stream = RelayTunnelStream::new(tunnel);
96 let tls = acceptor
97 .accept(stream)
98 .await
99 .map_err(|e| MethodError::failed(kind, format!("mtls accept: {e}")))?;
100
101 // The client-cert verifier populated the connecting peer's identity during the handshake.
102 let verified = captured
103 .get()
104 .ok_or_else(|| MethodError::failed(kind, "peer presented no certificate"))?;
105
106 let session = PeerSession::server(tls);
107 Ok(PeerConnection {
108 peer_id: verified,
109 method: kind,
110 remote_addr: self.relay_endpoint,
111 peer_bls_pub: captured_bls.get(),
112 session,
113 })
114 }
115}
116
117impl std::fmt::Debug for RelayAcceptor {
118 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119 f.debug_struct("RelayAcceptor")
120 .field("binding_policy", &self.binding_policy)
121 .field("relay_endpoint", &self.relay_endpoint)
122 .finish_non_exhaustive()
123 }
124}