1use crate::{
11 Error,
12 peer::REQUEST_TIMEOUT,
13 peer_codec::Codec,
14 peer_fence::{Candidates, Fenced},
15 peer_limits::{Classes, Limits},
16 peer_protocol::PROTOCOL,
17 peer_punch::Punches,
18 peer_rewaken::Rewaken,
19};
20use libp2p::{
21 Multiaddr, PeerId, StreamProtocol, Swarm, SwarmBuilder, autonat, identify, identity::Keypair,
22 multiaddr::Protocol, noise, quic, relay, request_response, swarm::NetworkBehaviour, tcp, yamux,
23};
24use std::{
25 num::{NonZeroU8, NonZeroUsize},
26 sync::{
27 Arc,
28 atomic::{AtomicBool, Ordering},
29 },
30 time::{Duration, Instant},
31};
32
33#[derive(NetworkBehaviour)]
34pub(crate) struct Behaviour {
35 pub(crate) limits: Limits,
38 pub(crate) records: Fenced<request_response::Behaviour<Codec>>,
39 pub(crate) identify: Fenced<identify::Behaviour>,
40 pub(crate) candidates: Candidates,
41 pub(crate) autonat: Fenced<autonat::v2::client::Behaviour>,
42 pub(crate) autonat_server: Fenced<autonat::v2::server::Behaviour>,
43 pub(crate) relay: Rewaken<relay::Behaviour>,
44 pub(crate) relay_client: relay::client::Behaviour,
45 pub(crate) dcutr: Punches,
46}
47
48impl Behaviour {
49 const IDENTIFY_PROTOCOL: &str = "/urma/node/4";
50 const STREAMS: u32 = 8;
51 const WINDOW: u32 = 256 * 1024;
52 const QUIC_MTU: u16 = 1280;
55}
56
57#[derive(Clone)]
60pub(crate) struct Wiring {
61 pub(crate) classes: Classes,
62 pub(crate) relay_open: Arc<AtomicBool>,
63}
64
65struct RelayGate {
68 open: Arc<AtomicBool>,
69 local: PeerId,
70}
71
72impl relay::RateLimiter for RelayGate {
73 fn try_next(&mut self, peer: PeerId, _: &Multiaddr, _: Instant) -> bool {
74 self.open.load(Ordering::Acquire) && peer != self.local
75 }
76}
77
78fn relay_config(open: &Arc<AtomicBool>, local: PeerId) -> relay::Config {
83 let gate = || RelayGate {
84 open: Arc::clone(open),
85 local,
86 };
87 let mut config = relay::Config {
88 max_reservations: 128,
89 max_reservations_per_peer: 1,
90 max_circuits: 16,
91 max_circuits_per_peer: 3,
92 max_circuit_duration: Duration::from_secs(2 * 60),
93 max_circuit_bytes: 1 << 20,
94 ..relay::Config::default()
95 };
96 config.reservation_rate_limiters.push(Box::new(gate()));
97 config.circuit_src_rate_limiters.push(Box::new(gate()));
98 config
99}
100
101#[derive(Clone, Debug, PartialEq, Eq)]
104pub enum DnsMode {
105 System,
106 Refuse(String),
107}
108
109#[derive(Clone, Debug, PartialEq, Eq)]
111pub enum Dns {
112 Available,
113 Unavailable(String),
114}
115
116impl Dns {
117 pub fn admits(&self, address: &Multiaddr) -> Result<(), Error> {
119 let named = address.iter().any(|part| {
120 matches!(
121 part,
122 Protocol::Dns(_) | Protocol::Dns4(_) | Protocol::Dns6(_) | Protocol::Dnsaddr(_)
123 )
124 });
125 match (self, named) {
126 (Self::Unavailable(cause), true) => Err(Error::Transport(format!(
127 "system DNS resolver unavailable: {cause}"
128 ))),
129 (Self::Unavailable(_) | Self::Available, false) | (Self::Available, true) => Ok(()),
130 }
131 }
132}
133
134fn behaviour(
135 identity: &Keypair,
136 relay_client: relay::client::Behaviour,
137 wiring: &Wiring,
138) -> Behaviour {
139 let local = identity.public().to_peer_id();
140 let records = request_response::Behaviour::with_codec(
141 Codec,
142 [(
143 StreamProtocol::new(PROTOCOL),
144 request_response::ProtocolSupport::Full,
145 )],
146 request_response::Config::default()
147 .with_request_timeout(REQUEST_TIMEOUT)
148 .with_max_concurrent_streams(2),
149 );
150 let identify = identify::Behaviour::new(
151 identify::Config::new(Behaviour::IDENTIFY_PROTOCOL.into(), identity.public())
152 .with_agent_version(format!("urma-node/{}", env!("CARGO_PKG_VERSION")))
153 .with_push_listen_addr_updates(true),
154 );
155 Behaviour {
156 limits: Limits::new(wiring.classes.clone(), Arc::clone(&wiring.relay_open)),
157 records: Fenced::data_only(records, wiring.classes.clone()),
158 identify: Fenced::without_candidates(identify),
159 candidates: Candidates::default(),
160 autonat: Fenced::without_confirmations(autonat::v2::client::Behaviour::default()),
161 autonat_server: Fenced::public_dial_backs(autonat::v2::server::Behaviour::default()),
162 relay: Rewaken(relay::Behaviour::new(
163 local,
164 relay_config(&wiring.relay_open, local),
165 )),
166 relay_client,
167 dcutr: Punches::new(local),
168 }
169}
170
171fn multiplexer() -> yamux::Config {
172 let mut config = yamux::Config::default();
173 config.set_max_num_streams(8);
175 config.set_max_buffer_size(256 * 1024);
176 config.set_receive_window_size(Behaviour::WINDOW);
177 config
178}
179
180fn quic(mut config: quic::Config) -> quic::Config {
182 config.max_concurrent_stream_limit = Behaviour::STREAMS;
183 config.max_stream_data = Behaviour::WINDOW;
184 config.max_connection_data = Behaviour::WINDOW * Behaviour::STREAMS;
185 config.mtu_upper_bound(Behaviour::QUIC_MTU)
186}
187
188fn configure(config: libp2p::swarm::Config) -> libp2p::swarm::Config {
189 config
190 .with_notify_handler_buffer_size(NonZeroUsize::new(2).unwrap())
191 .with_per_connection_event_buffer_size(2)
192 .with_max_negotiating_inbound_streams(2)
193 .with_dial_concurrency_factor(NonZeroU8::MIN)
194 .with_idle_connection_timeout(Duration::from_secs(20))
195}
196
197fn transport_error(error: impl std::fmt::Display) -> Error {
198 Error::Invalid(format!("peer transport: {error}"))
199}
200
201fn behaviour_error(error: impl std::fmt::Display) -> Error {
202 Error::Invalid(format!("peer behaviour: {error}"))
203}
204
205pub(crate) fn swarm(
208 identity: Keypair,
209 mode: &DnsMode,
210 wiring: &Wiring,
211) -> Result<(Swarm<Behaviour>, Dns), Error> {
212 let transports = || {
213 SwarmBuilder::with_existing_identity(identity.clone())
214 .with_tokio()
215 .with_tcp(
216 tcp::Config::default().nodelay(true),
217 noise::Config::new,
218 multiplexer,
219 )
220 .map(|builder| builder.with_quic_config(quic))
221 .map_err(transport_error)
222 };
223 let cause = match mode {
224 DnsMode::System => match transports()?.with_dns() {
225 Ok(resolving) => {
226 let swarm = resolving
227 .with_relay_client(noise::Config::new, multiplexer)
228 .map_err(transport_error)?
229 .with_behaviour(|key, relay| behaviour(key, relay, wiring))
230 .map_err(behaviour_error)?
231 .with_swarm_config(configure)
232 .with_connection_timeout(Duration::from_secs(5))
233 .build();
234 return Ok((swarm, Dns::Available));
235 }
236 Err(error) => error.to_string(),
237 },
238 DnsMode::Refuse(cause) => cause.clone(),
239 };
240 tracing::warn!(%cause, "system DNS resolver unavailable; overlay runs for IP-literal peers only");
241 let swarm = transports()?
242 .with_relay_client(noise::Config::new, multiplexer)
243 .map_err(transport_error)?
244 .with_behaviour(|key, relay| behaviour(key, relay, wiring))
245 .map_err(behaviour_error)?
246 .with_swarm_config(configure)
247 .with_connection_timeout(Duration::from_secs(5))
248 .build();
249 Ok((swarm, Dns::Unavailable(cause)))
250}