Skip to main content

snarkos_node_tcp/
tcp.rs

1// Copyright (c) 2019-2026 Provable Inc.
2// This file is part of the snarkOS library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7
8// http://www.apache.org/licenses/LICENSE-2.0
9
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use std::{
17    collections::{HashMap, HashSet},
18    fmt,
19    io,
20    net::{IpAddr, SocketAddr},
21    ops::Deref,
22    sync::{
23        Arc,
24        atomic::{AtomicUsize, Ordering::*},
25    },
26    time::{Duration, Instant},
27};
28
29use anyhow::anyhow;
30#[cfg(feature = "locktick")]
31use locktick::parking_lot::Mutex;
32use once_cell::sync::OnceCell;
33#[cfg(not(feature = "locktick"))]
34use parking_lot::Mutex;
35use tokio::{
36    io::split,
37    net::{TcpListener, TcpSocket, TcpStream},
38    sync::{OwnedSemaphorePermit, Semaphore, oneshot},
39    task::{JoinHandle, JoinSet},
40    time::timeout,
41};
42use tracing::*;
43
44use crate::{
45    BannedPeers,
46    Config,
47    Stats,
48    connections::{Connection, ConnectionSide, Connections, DisconnectOrigin, canonical_ip, create_connection_span},
49    protocols::{Protocol, Protocols},
50};
51
52// A sequential numeric identifier assigned to `Tcp`s that were not provided with a name.
53static SEQUENTIAL_NODE_ID: AtomicUsize = AtomicUsize::new(0);
54
55/// The central object responsible for handling connections.
56#[derive(Clone)]
57pub struct Tcp(Arc<InnerTcp>);
58
59impl Deref for Tcp {
60    type Target = Arc<InnerTcp>;
61
62    fn deref(&self) -> &Self::Target {
63        &self.0
64    }
65}
66
67/// A custom application error that can be returned by the `Tcp` stack.
68pub trait ApplicationError: Send + Sync + std::fmt::Debug + std::fmt::Display + 'static {}
69
70/// Error types for the `Tcp::connect` function.
71#[allow(missing_docs)]
72#[derive(thiserror::Error, Debug)]
73pub enum ConnectError {
74    #[error("already reached the maximum number of {limit} connections")]
75    MaximumConnectionsReached { limit: u16 },
76    #[error("already reached the maximum number of {limit} connections with IP '{ip}'")]
77    MaximumConnectionsPerIpReached { ip: IpAddr, limit: u16 },
78    #[error("already connecting to node at {address:?}")]
79    AlreadyConnecting { address: SocketAddr },
80    #[error("already connected to node at {address:?}")]
81    AlreadyConnected { address: SocketAddr },
82    #[error("attempt to self-connect (at address {address:?}")]
83    SelfConnect { address: SocketAddr },
84    #[error("rejected a connection attempt from a banned IP '{ip}'")]
85    BannedIp { ip: IpAddr },
86    // Socket errors, such as "connection refused".
87    #[error(transparent)]
88    IoError(std::io::Error),
89    // An application-specific reason to reject the connection or abort the handshake.
90    // For snarkOS, this is either a `DisconnectReason` or a `PeeringError`, which do not fully implement `std::error::Error`.
91    #[error("{0}")]
92    ApplicationError(Box<dyn ApplicationError>),
93    /// An unexpected error at the application layer and certain deserialization errors.
94    /// TODO(kaimast): (some of) these should be treated with higher severity, as they indicate a bug or corrupted state,
95    ///                and deserialization errors should not be included in this "other" category.
96    #[error(transparent)]
97    Other(#[from] Box<dyn std::error::Error + Send + Sync>),
98}
99
100impl ConnectError {
101    /// Pass an application-level error to the `Tcp` stack.
102    pub fn application<E: ApplicationError>(err: E) -> Self {
103        Self::ApplicationError(Box::new(err))
104    }
105
106    /// A generic error that can be returned by the `Tcp` stack.
107    pub fn other<E: Into<Box<dyn std::error::Error + Send + Sync>>>(err: E) -> Self {
108        Self::Other(err.into())
109    }
110}
111
112impl From<ConnectError> for std::io::Error {
113    fn from(err: ConnectError) -> Self {
114        match err {
115            ConnectError::IoError(err) => err,
116            ConnectError::Other(err) => std::io::Error::other(err),
117            err => std::io::Error::other(err.to_string()),
118        }
119    }
120}
121
122impl From<std::io::Error> for ConnectError {
123    fn from(err: std::io::Error) -> Self {
124        // Other error are usually checks that fail when snarkVM deserializes a message.
125        if err.kind() == std::io::ErrorKind::Other {
126            // This unwrap should always succeed.
127            let inner = err.into_inner().unwrap_or_else(|| anyhow!("Unknown error").into());
128            ConnectError::other(inner)
129        } else {
130            ConnectError::IoError(err)
131        }
132    }
133}
134
135#[doc(hidden)]
136pub struct InnerTcp {
137    /// The tracing span.
138    span: Span,
139    /// The node's configuration.
140    config: Config,
141    /// The node's listening address.
142    listening_addr: OnceCell<SocketAddr>,
143    /// Contains objects used by the protocols implemented by the node.
144    pub(crate) protocols: Protocols,
145    /// A set of connections that have not been finalized yet.
146    connecting: Mutex<HashSet<SocketAddr>>,
147    /// Contains objects related to the node's active connections.
148    pub(crate) connections: Connections,
149    /// Contains the set of currently banned peers.
150    banned_peers: BannedPeers,
151    /// Collects statistics related to the node itself.
152    stats: Stats,
153    /// The node's tasks.
154    pub(crate) tasks: Mutex<Vec<JoinHandle<()>>>,
155}
156
157impl Tcp {
158    /// Creates a new [`Tcp`] using the given [`Config`].
159    pub fn new(mut config: Config) -> Self {
160        // If there is no pre-configured name, assign a sequential numeric identifier.
161        if config.name.is_none() {
162            config.name = Some(SEQUENTIAL_NODE_ID.fetch_add(1, Relaxed).to_string());
163        }
164
165        // Create a tracing span containing the node's name.
166        let span = crate::helpers::create_span(config.name.as_deref().unwrap());
167
168        // A zero here would render the node inoperable, and would do so obscurely (at the first
169        // connection) rather than up front.
170        assert!(config.max_connections_per_ip != 0, "Config::max_connections_per_ip must not be 0");
171
172        // Initialize the Tcp stack.
173        let tcp = Tcp(Arc::new(InnerTcp {
174            span,
175            config,
176            listening_addr: Default::default(),
177            protocols: Default::default(),
178            connecting: Default::default(),
179            connections: Default::default(),
180            banned_peers: Default::default(),
181            stats: Stats::new(Instant::now()),
182            tasks: Default::default(),
183        }));
184
185        debug!(parent: tcp.span(), "The node is ready");
186
187        tcp
188    }
189
190    /// How long has this node accepting connections?
191    pub fn uptime(&self) -> Duration {
192        self.stats.created().elapsed()
193    }
194
195    /// Returns the name assigned.
196    #[inline]
197    pub fn name(&self) -> &str {
198        // safe; can be set as None in Config, but receives a default value on Tcp creation
199        self.config.name.as_deref().unwrap()
200    }
201
202    /// Returns a reference to the configuration.
203    #[inline]
204    pub fn config(&self) -> &Config {
205        &self.config
206    }
207
208    /// Returns the listening address; returns an error if Tcp was not configured
209    /// to listen for inbound connections.
210    pub fn listening_addr(&self) -> io::Result<SocketAddr> {
211        self.listening_addr.get().copied().ok_or_else(|| io::ErrorKind::AddrNotAvailable.into())
212    }
213
214    /// Checks whether the provided address is connected.
215    pub fn is_connected(&self, addr: SocketAddr) -> bool {
216        self.connections.is_connected(addr)
217    }
218
219    /// Checks if Tcp is currently setting up a connection with the provided address.
220    pub fn is_connecting(&self, addr: SocketAddr) -> bool {
221        self.connecting.lock().contains(&addr)
222    }
223
224    /// Returns the number of active connections.
225    pub fn num_connected(&self) -> usize {
226        self.connections.num_connected()
227    }
228
229    /// Returns the number of connections that are currently being set up.
230    pub fn num_connecting(&self) -> usize {
231        self.connecting.lock().len()
232    }
233
234    /// Returns a list containing addresses of active connections.
235    pub fn connected_addrs(&self) -> Vec<SocketAddr> {
236        self.connections.addrs()
237    }
238
239    /// Returns a list containing addresses of pending connections.
240    pub fn connecting_addrs(&self) -> Vec<SocketAddr> {
241        self.connecting.lock().iter().copied().collect()
242    }
243
244    /// Returns the statistics of the active connection with the given address, if any.
245    pub fn connection_stats(&self, addr: SocketAddr) -> Option<Arc<Stats>> {
246        self.connections.stats(addr)
247    }
248
249    /// Returns the statistics of every active connection.
250    pub fn connection_stats_snapshot(&self) -> HashMap<SocketAddr, Arc<Stats>> {
251        self.connections.stats_snapshot()
252    }
253
254    /// Returns a reference to the set of currently banned peers.
255    #[inline]
256    pub fn banned_peers(&self) -> &BannedPeers {
257        &self.banned_peers
258    }
259
260    /// Returns a reference to the statistics.
261    #[inline]
262    pub fn stats(&self) -> &Stats {
263        &self.stats
264    }
265
266    /// Returns the tracing [`Span`] associated with Tcp.
267    #[inline]
268    pub fn span(&self) -> &Span {
269        &self.span
270    }
271
272    /// Gracefully shuts down the stack.
273    pub async fn shut_down(&self) {
274        debug!(parent: self.span(), "Shutting down the TCP stack");
275
276        // Retrieve all tasks.
277        let mut tasks = std::mem::take(&mut *self.tasks.lock()).into_iter();
278
279        // Abort the listening task first.
280        if let Some(listening_task) = tasks.next() {
281            listening_task.abort(); // abort the listening task first
282        }
283
284        // Disconnect from all connected peers.
285        let mut disconnect_tasks = JoinSet::new();
286        for addr in self.connected_addrs() {
287            let node = self.clone();
288            disconnect_tasks.spawn(async move {
289                node.disconnect_w_origin(addr, DisconnectOrigin::Shutdown).await;
290            });
291        }
292        while disconnect_tasks.join_next().await.is_some() {}
293
294        // Abort all remaining tasks.
295        for handle in tasks {
296            handle.abort();
297        }
298    }
299}
300
301impl Tcp {
302    /// Connects to the provided `SocketAddr`.
303    pub async fn connect(&self, addr: SocketAddr) -> Result<(), ConnectError> {
304        if let Ok(listening_addr) = self.listening_addr() {
305            // TODO(nkls): maybe this first check can be dropped; though it might be best to keep just in case.
306            if addr == listening_addr || self.is_self_connect(addr) {
307                error!(parent: self.span(), "Attempted to self-connect ({addr})");
308                return Err(ConnectError::SelfConnect { address: addr });
309            }
310        }
311
312        self.can_add_connection(addr)?;
313
314        if self.is_connected(addr) {
315            trace!(parent: self.span(), "Already connected to {addr}");
316            return Err(ConnectError::AlreadyConnected { address: addr });
317        }
318
319        if !self.connecting.lock().insert(addr) {
320            debug!(parent: self.span(), "Already connecting to {addr}");
321            return Err(ConnectError::AlreadyConnecting { address: addr });
322        }
323
324        let timeout_duration = Duration::from_millis(self.config().connection_timeout_ms.into());
325
326        // Bind the tcp socket to the configured listener ip if it's set.
327        // Otherwise default to the system's default interface.
328        let res = if let Some(listen_ip) = self.config().listener_ip {
329            timeout(timeout_duration, self.connect_with_specific_interface(listen_ip, addr)).await
330        } else {
331            timeout(timeout_duration, TcpStream::connect(addr)).await
332        };
333
334        let stream = match res {
335            Ok(Ok(stream)) => Ok(stream),
336            Ok(err) => {
337                self.connecting.lock().remove(&addr);
338                err
339            }
340            Err(err) => {
341                self.connecting.lock().remove(&addr);
342                error!("connection timeout error: {}", err);
343                Err(io::ErrorKind::TimedOut.into())
344            }
345        }?;
346
347        let ret = self.adapt_stream(stream, addr, ConnectionSide::Initiator).await;
348
349        if let Err(ref e) = ret {
350            self.connecting.lock().remove(&addr);
351            error!(parent: self.span(), "Unable to initiate a connection with {addr}: {e}");
352        }
353
354        ret.map_err(|err| err.into())
355    }
356
357    async fn connect_with_specific_interface(&self, listen_ip: IpAddr, addr: SocketAddr) -> io::Result<TcpStream> {
358        let sock = if listen_ip.is_ipv4() { TcpSocket::new_v4()? } else { TcpSocket::new_v6()? };
359        // Lock the socket to a specific interface.
360        sock.bind(SocketAddr::new(listen_ip, 0))?;
361        sock.connect(addr).await
362    }
363
364    /// Disconnects from the provided `SocketAddr`.
365    ///
366    /// Returns true if the we were connected to the given address.
367    pub async fn disconnect(&self, addr: SocketAddr) -> bool {
368        self.disconnect_w_origin(addr, DisconnectOrigin::User).await
369    }
370
371    pub(crate) async fn disconnect_w_origin(&self, addr: SocketAddr, origin: DisconnectOrigin) -> bool {
372        // claim the disconnect to avoid duplicate executions, or return early if already claimed
373        if let Some(conn) = self.connections.0.read().get(&addr) {
374            if conn.disconnecting.swap(true, AcqRel) {
375                // valid connection, but someone else is already disconnecting it
376                return false;
377            }
378        } else {
379            // not connected
380            return false;
381        };
382
383        if let Some(handler) = self.protocols.disconnect.get() {
384            let (sender, receiver) = oneshot::channel();
385            handler.trigger(((addr, origin), sender)).await;
386            if let Ok((handle, waiter)) = receiver.await {
387                // register the associated task with the connection, in case
388                // it gets terminated before its completion
389                if let Some(conn) = self.connections.0.write().get_mut(&addr) {
390                    conn.tasks.push(handle);
391                }
392                // wait for the OnDisconnect protocol to perform its specified actions
393                let _ = waiter.await;
394            }
395        }
396
397        let conn = self.connections.remove(addr);
398        let disconnected = conn.is_some();
399
400        if let Some(conn) = conn {
401            debug!(parent: self.span(), "Disconnecting from {addr}");
402
403            // Shut down the associated tasks of the peer.
404            drop(conn);
405
406            debug!(parent: self.span(), "Disconnected from {addr}");
407        } else {
408            warn!(parent: self.span(), "Failed to disconnect, was not connected to {addr}");
409        }
410
411        disconnected
412    }
413}
414
415impl Tcp {
416    /// Spawns a task that listens for incoming connections.
417    pub async fn enable_listener(&self) -> io::Result<SocketAddr> {
418        // Retrieve the listening IP address, which must be set.
419        let listener_ip =
420            self.config().listener_ip.expect("Tcp::enable_listener was called, but Config::listener_ip is not set");
421
422        // Initialize the TCP listener.
423        let listener = self.create_listener(listener_ip).await?;
424
425        // Discover the port, if it was unspecified.
426        let port = listener.local_addr()?.port();
427
428        // Set the listening IP address.
429        let listening_addr = (listener_ip, port).into();
430        self.listening_addr.set(listening_addr).expect("The node's listener was started more than once");
431
432        // Use a channel to know when the listening task is ready.
433        let (tx, rx) = oneshot::channel();
434
435        // Cap the number of in-flight inbound connection handlers; the hard
436        // connection limits are still enforced inside `can_add_connection`;
437        // this bound exists separately to prevent per-SYN task-creation overhead
438        // from being unbounded under flood.
439        let inbound_permits = Arc::new(Semaphore::new(self.config.max_connections as usize));
440
441        let tcp = self.clone();
442        let listening_task = tokio::spawn(async move {
443            trace!(parent: tcp.span(), "Spawned the listening task");
444            tx.send(()).unwrap(); // safe; the channel was just opened
445
446            loop {
447                // Wait for capacity before accepting.
448                let permit = match inbound_permits.clone().acquire_owned().await {
449                    Ok(p) => p,
450                    Err(_) => {
451                        // semaphore is never closed in practice; bail defensively
452                        error!(parent: tcp.span(), "Inbound permit semaphore closed unexpectedly");
453                        return;
454                    }
455                };
456
457                // Await connection requests from peers.
458                match listener.accept().await {
459                    Ok((stream, addr)) => tcp.handle_connection(stream, addr, permit),
460                    Err(e) => {
461                        // Free the permit immediately.
462                        drop(permit);
463
464                        match e.kind() {
465                            // A peer aborted/reset before accept completed; no backoff - the listener is healthy.
466                            io::ErrorKind::ConnectionAborted | io::ErrorKind::ConnectionReset => {
467                                debug!(parent: tcp.span(), "Transient accept error: {e}");
468                            }
469                            // Otherwise, assume fd / memory exhaustion (EMFILE, ENFILE, ENOBUFS, ...)
470                            // and back off so we don't spin at 100% CPU waiting for a slot to free.
471                            _ => {
472                                error!(parent: tcp.span(), "Couldn't accept a connection: {e}");
473                                tokio::time::sleep(Duration::from_millis(500)).await;
474                            }
475                        }
476                    }
477                }
478            }
479        });
480        self.tasks.lock().push(listening_task);
481        let _ = rx.await;
482        debug!(parent: self.span(), "Listening on {listening_addr}");
483
484        Ok(listening_addr)
485    }
486
487    /// Creates an instance of `TcpListener` based on the node's configuration.
488    async fn create_listener(&self, listener_ip: IpAddr) -> io::Result<TcpListener> {
489        debug!("Creating a TCP listener on {listener_ip}...");
490        let listener = if let Some(port) = self.config().desired_listening_port {
491            // Construct the desired listening IP address.
492            let desired_listening_addr = SocketAddr::new(listener_ip, port);
493            // If a desired listening port is set, try to bind to it.
494            match TcpListener::bind(desired_listening_addr).await {
495                Ok(listener) => listener,
496                Err(e) => {
497                    if self.config().allow_random_port {
498                        warn!(
499                            parent: self.span(),
500                            "Trying any listening port, as the desired port is unavailable: {e}"
501                        );
502                        let random_available_addr = SocketAddr::new(listener_ip, 0);
503                        TcpListener::bind(random_available_addr).await?
504                    } else {
505                        error!(parent: self.span(), "The desired listening port is unavailable: {e}");
506                        return Err(e);
507                    }
508                }
509            }
510        } else if self.config().allow_random_port {
511            let random_available_addr = SocketAddr::new(listener_ip, 0);
512            TcpListener::bind(random_available_addr).await?
513        } else {
514            panic!("As 'listener_ip' is set, either 'desired_listening_port' or 'allow_random_port' must be set");
515        };
516
517        Ok(listener)
518    }
519
520    /// Handles a new inbound connection.
521    fn handle_connection(&self, stream: TcpStream, addr: SocketAddr, permit: OwnedSemaphorePermit) {
522        debug!(parent: self.span(), "Received a connection from {addr}");
523
524        if self.can_add_connection(addr).is_err() || self.is_self_connect(addr) {
525            debug!(parent: self.span(), "Rejecting the connection from {addr}");
526            return;
527        }
528
529        self.connecting.lock().insert(addr);
530
531        let tcp = self.clone();
532        tokio::spawn(async move {
533            // The permit is released when the connection is accepted or fails.
534            let _permit = permit;
535
536            if let Err(e) = tcp.adapt_stream(stream, addr, ConnectionSide::Responder).await {
537                tcp.connecting.lock().remove(&addr);
538                error!(parent: tcp.span(), "Failed to connect with {addr}: {e}");
539            }
540        });
541    }
542
543    /// Checks if the given IP address is the same as the listening address of this `Tcp`.
544    fn is_self_connect(&self, addr: SocketAddr) -> bool {
545        // SAFETY: if we're opening connections, this should never fail.
546        let listening_addr = self.listening_addr().unwrap();
547
548        match listening_addr.ip().is_loopback() {
549            // If localhost, check the ports, this only works on outbound connections, since we
550            // don't know the ephemeral port a peer might be using if they initiate the connection.
551            true => listening_addr.port() == addr.port(),
552            // If it's not localhost, matching IPs indicate a self-connect in both directions.
553            false => listening_addr.ip() == addr.ip(),
554        }
555    }
556
557    /// Checks whether the `Tcp` can handle an additional connection with the given address.
558    ///
559    /// Pending connections count towards both the global and the per-IP limit, as they are all
560    /// expected to conclude successfully.
561    fn can_add_connection(&self, addr: SocketAddr) -> Result<(), ConnectError> {
562        // Retrieve the number of connected peers.
563        let num_connected = self.num_connected();
564        // Retrieve the maximum number of connected peers.
565        let limit = self.config.max_connections as usize;
566
567        if num_connected >= limit {
568            warn!(parent: self.span(), "Maximum number of active connections ({limit}) reached");
569            return Err(ConnectError::MaximumConnectionsReached { limit: self.config.max_connections });
570        } else if num_connected + self.num_connecting() >= limit {
571            warn!(parent: self.span(), "Maximum number of active & pending connections ({limit}) reached");
572            return Err(ConnectError::MaximumConnectionsReached { limit: self.config.max_connections });
573        }
574
575        // Retrieve the number of connections already charged to the address' IP.
576        let num_with_ip = self.num_connections_with_ip(addr);
577        // Retrieve the maximum number of connections permitted per IP.
578        let ip_limit = self.config.max_connections_per_ip as usize;
579
580        if num_with_ip >= ip_limit {
581            warn!(
582                parent: self.span(),
583                "Maximum number of connections ({ip_limit}) with IP '{}' reached", addr.ip(),
584            );
585            return Err(ConnectError::MaximumConnectionsPerIpReached {
586                ip: addr.ip(),
587                limit: self.config.max_connections_per_ip,
588            });
589        }
590
591        Ok(())
592    }
593
594    /// Returns the number of active and pending connections charged to the given address' IP.
595    ///
596    /// Both are counted, as a pending connection is expected to conclude successfully. IPs are
597    /// compared canonically (see [`canonical_ip`]), so the native and IPv4-mapped spellings of a
598    /// host share one allowance.
599    fn num_connections_with_ip(&self, addr: SocketAddr) -> usize {
600        let ip = canonical_ip(addr);
601
602        // The two collections are counted under separate locks, in the same order as elsewhere, so
603        // that no lock is held while the other is acquired.
604        let num_connected = self.connections.num_with_ip(addr);
605        let num_connecting = self.connecting.lock().iter().filter(|addr| canonical_ip(**addr) == ip).count();
606
607        num_connected.saturating_add(num_connecting)
608    }
609
610    /// Prepares the freshly acquired connection to handle the protocols the Tcp implements.
611    async fn adapt_stream(&self, stream: TcpStream, peer_addr: SocketAddr, own_side: ConnectionSide) -> io::Result<()> {
612        // Register the port seen by the peer.
613        if own_side == ConnectionSide::Initiator {
614            if let Ok(addr) = stream.local_addr() {
615                debug!(
616                    parent: self.span(), "establishing connection with {}; the peer is connected on port {}",
617                    peer_addr, addr.port()
618                );
619            } else {
620                warn!(parent: self.span(), "couldn't determine the peer's port");
621            }
622        }
623
624        let conn_span = create_connection_span(peer_addr, self.span());
625        let connection = Connection::new(peer_addr, stream, !own_side, conn_span);
626
627        // Enact the enabled protocols.
628        let mut connection = self.enable_protocols(connection).await?;
629
630        // if Reading is enabled, we'll notify the related task when the connection is fully ready.
631        let conn_ready_tx = connection.readiness_notifier.take();
632
633        self.connections.add(connection);
634        self.connecting.lock().remove(&peer_addr);
635
636        // Send the aforementioned notification so that reading from the socket can commence.
637        if let Some(tx) = conn_ready_tx {
638            let _ = tx.send(());
639        }
640
641        // If enabled, enact OnConnect.
642        if let Some(handler) = self.protocols.on_connect.get() {
643            let (sender, receiver) = oneshot::channel();
644            handler.trigger((peer_addr, sender)).await;
645            // Receive the handle for the running task.
646            if let Ok(handle) = receiver.await {
647                // Add the task to the connection so it gets aborted on disconnect.
648                if let Some(conn) = self.connections.0.write().get_mut(&peer_addr) {
649                    conn.tasks.push(handle);
650                } else {
651                    // The connection has just been terminated; abort the OnConnect work.
652                    handle.abort();
653                }
654            }
655        }
656
657        Ok(())
658    }
659
660    /// Enacts the enabled protocols on the provided connection.
661    async fn enable_protocols(&self, conn: Connection) -> io::Result<Connection> {
662        /// A helper macro to enable a protocol on a connection.
663        macro_rules! enable_protocol {
664            ($handler_type: ident, $node:expr, $conn: expr) => {
665                if let Some(handler) = $node.protocols.$handler_type.get() {
666                    let (conn_returner, conn_retriever) = oneshot::channel();
667
668                    handler.trigger(($conn, conn_returner)).await;
669
670                    match conn_retriever.await {
671                        Ok(Ok(conn)) => conn,
672                        Err(_) => return Err(io::ErrorKind::BrokenPipe.into()),
673                        Ok(e) => return e,
674                    }
675                } else {
676                    $conn
677                }
678            };
679        }
680
681        let mut conn = enable_protocol!(handshake, self, conn);
682
683        // Split the stream after the handshake (if not done before).
684        if let Some(stream) = conn.stream.take() {
685            let (reader, writer) = split(stream);
686            conn.reader = Some(Box::new(reader));
687            conn.writer = Some(Box::new(writer));
688        }
689
690        let conn = enable_protocol!(reading, self, conn);
691        let conn = enable_protocol!(writing, self, conn);
692
693        Ok(conn)
694    }
695}
696
697impl fmt::Debug for Tcp {
698    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
699        write!(f, "The TCP stack config: {:?}", self.config)
700    }
701}
702
703#[cfg(test)]
704mod tests {
705    use super::*;
706
707    use std::{
708        net::{IpAddr, Ipv4Addr},
709        str::FromStr,
710    };
711
712    #[tokio::test]
713    async fn test_new() {
714        let tcp = Tcp::new(Config {
715            listener_ip: Some(IpAddr::V4(Ipv4Addr::LOCALHOST)),
716            max_connections: 200,
717            ..Default::default()
718        });
719
720        assert_eq!(tcp.config.max_connections, 200);
721        assert_eq!(tcp.config.listener_ip, Some(IpAddr::V4(Ipv4Addr::LOCALHOST)));
722        assert_eq!(tcp.enable_listener().await.unwrap().ip(), IpAddr::V4(Ipv4Addr::LOCALHOST));
723
724        assert_eq!(tcp.num_connected(), 0);
725        assert_eq!(tcp.num_connecting(), 0);
726    }
727
728    #[tokio::test]
729    async fn test_connect() {
730        let tcp = Tcp::new(Config::default());
731        let node_ip = tcp.enable_listener().await.unwrap();
732
733        // Ensure self-connecting is not possible.
734        let result = tcp.connect(node_ip).await;
735        assert!(matches!(result, Err(ConnectError::SelfConnect { .. })));
736
737        assert_eq!(tcp.num_connected(), 0);
738        assert_eq!(tcp.num_connecting(), 0);
739        assert!(!tcp.is_connected(node_ip));
740        assert!(!tcp.is_connecting(node_ip));
741
742        // Initialize the peer.
743        let peer = Tcp::new(Config {
744            listener_ip: Some(IpAddr::V4(Ipv4Addr::LOCALHOST)),
745            desired_listening_port: Some(0),
746            max_connections: 1,
747            ..Default::default()
748        });
749        let peer_ip = peer.enable_listener().await.unwrap();
750
751        // Connect to the peer.
752        tcp.connect(peer_ip).await.unwrap();
753        assert_eq!(tcp.num_connected(), 1);
754        assert_eq!(tcp.num_connecting(), 0);
755        assert!(tcp.is_connected(peer_ip));
756        assert!(!tcp.is_connecting(peer_ip));
757    }
758
759    #[tokio::test]
760    async fn test_disconnect() {
761        let tcp = Tcp::new(Config::default());
762        let _node_ip = tcp.enable_listener().await.unwrap();
763
764        // Initialize the peer.
765        let peer = Tcp::new(Config {
766            listener_ip: Some(IpAddr::V4(Ipv4Addr::LOCALHOST)),
767            desired_listening_port: Some(0),
768            max_connections: 1,
769            ..Default::default()
770        });
771        let peer_ip = peer.enable_listener().await.unwrap();
772
773        // Connect to the peer.
774        tcp.connect(peer_ip).await.unwrap();
775        assert_eq!(tcp.num_connected(), 1);
776        assert_eq!(tcp.num_connecting(), 0);
777        assert!(tcp.is_connected(peer_ip));
778        assert!(!tcp.is_connecting(peer_ip));
779
780        // Disconnect from the peer.
781        let has_disconnected = tcp.disconnect(peer_ip).await;
782        assert!(has_disconnected);
783        assert_eq!(tcp.num_connected(), 0);
784        assert_eq!(tcp.num_connecting(), 0);
785        assert!(!tcp.is_connected(peer_ip));
786        assert!(!tcp.is_connecting(peer_ip));
787
788        // Ensure disconnecting from the peer a second time is okay.
789        let has_disconnected = tcp.disconnect(peer_ip).await;
790        assert!(!has_disconnected);
791        assert_eq!(tcp.num_connected(), 0);
792        assert_eq!(tcp.num_connecting(), 0);
793        assert!(!tcp.is_connected(peer_ip));
794        assert!(!tcp.is_connecting(peer_ip));
795    }
796
797    #[tokio::test]
798    async fn test_can_add_connection() {
799        let tcp = Tcp::new(Config { max_connections: 1, ..Default::default() });
800
801        // Initialize the peer.
802        let peer = Tcp::new(Config {
803            listener_ip: Some(IpAddr::V4(Ipv4Addr::LOCALHOST)),
804            desired_listening_port: Some(0),
805            max_connections: 1,
806            ..Default::default()
807        });
808        let peer_ip = peer.enable_listener().await.unwrap();
809
810        assert!(tcp.can_add_connection(peer_ip).is_ok());
811
812        // Simulate an active connection.
813        let stream = TcpStream::connect(peer_ip).await.unwrap();
814        tcp.connections.add(Connection::new(peer_ip, stream, ConnectionSide::Initiator, Span::none()));
815        assert!(tcp.can_add_connection(peer_ip).is_err());
816
817        // Ensure that we cannot invoke connect() successfully in this case.
818        // Use a non-local IP, to ensure it is never qual to peer IP.
819        let another_ip = SocketAddr::from_str("1.2.3.4:4242").unwrap();
820        let result = tcp.connect(another_ip).await;
821        assert!(matches!(result, Err(ConnectError::MaximumConnectionsReached { .. })));
822
823        // Remove the active connection.
824        tcp.connections.remove(peer_ip);
825        assert!(tcp.can_add_connection(peer_ip).is_ok());
826
827        // Simulate a pending connection.
828        tcp.connecting.lock().insert(peer_ip);
829        assert!(tcp.can_add_connection(peer_ip).is_err());
830
831        // Ensure that we cannot invoke connect() successfully in this case either.
832        let another_ip = SocketAddr::from_str("1.2.3.4:4242").unwrap();
833        let result = tcp.connect(another_ip).await;
834        assert!(matches!(result, Err(ConnectError::MaximumConnectionsReached { .. })));
835
836        // Remove the pending connection.
837        tcp.connecting.lock().remove(&peer_ip);
838        assert!(tcp.can_add_connection(peer_ip).is_ok());
839
840        // Simulate an active and a pending connection (this case should never occur).
841        let stream = TcpStream::connect(peer_ip).await.unwrap();
842        tcp.connections.add(Connection::new(peer_ip, stream, ConnectionSide::Responder, Span::none()));
843        tcp.connecting.lock().insert(peer_ip);
844        assert!(tcp.can_add_connection(peer_ip).is_err());
845
846        // Remove the active and pending connection.
847        tcp.connections.remove(peer_ip);
848        tcp.connecting.lock().remove(&peer_ip);
849        assert!(tcp.can_add_connection(peer_ip).is_ok());
850    }
851
852    #[tokio::test]
853    async fn test_max_connections_per_ip() {
854        let tcp = Tcp::new(Config { max_connections: 10, max_connections_per_ip: 2, ..Default::default() });
855
856        // Initialize a listener to source real streams from; the addresses the connections are
857        // registered under are independent of it.
858        let peer = Tcp::new(Config {
859            listener_ip: Some(IpAddr::V4(Ipv4Addr::LOCALHOST)),
860            desired_listening_port: Some(0),
861            ..Default::default()
862        });
863        let peer_ip = peer.enable_listener().await.unwrap();
864
865        // Two addresses sharing an IP, and a third on a different IP.
866        let first = SocketAddr::from_str("1.2.3.4:1111").unwrap();
867        let second = SocketAddr::from_str("1.2.3.4:2222").unwrap();
868        let third = SocketAddr::from_str("1.2.3.4:3333").unwrap();
869        let other_ip = SocketAddr::from_str("5.6.7.8:1111").unwrap();
870
871        // The per-IP limit counts active connections...
872        for addr in [first, second] {
873            assert!(tcp.can_add_connection(addr).is_ok());
874            let stream = TcpStream::connect(peer_ip).await.unwrap();
875            tcp.connections.add(Connection::new(addr, stream, ConnectionSide::Initiator, Span::none()));
876        }
877        assert_eq!(tcp.num_connections_with_ip(first), 2);
878        assert!(matches!(
879            tcp.can_add_connection(third),
880            Err(ConnectError::MaximumConnectionsPerIpReached { limit: 2, .. })
881        ));
882
883        // ...while leaving the global limit, and therefore other IPs, unaffected.
884        assert!(tcp.can_add_connection(other_ip).is_ok());
885
886        // Pending connections count towards the limit too.
887        tcp.connections.remove(second);
888        assert!(tcp.can_add_connection(third).is_ok());
889        tcp.connecting.lock().insert(second);
890        assert_eq!(tcp.num_connections_with_ip(first), 2);
891        assert!(matches!(
892            tcp.can_add_connection(third),
893            Err(ConnectError::MaximumConnectionsPerIpReached { limit: 2, .. })
894        ));
895    }
896
897    #[tokio::test]
898    async fn test_max_connections_per_ip_canonicalizes_mapped_addresses() {
899        let tcp = Tcp::new(Config { max_connections: 10, max_connections_per_ip: 2, ..Default::default() });
900
901        let peer = Tcp::new(Config {
902            listener_ip: Some(IpAddr::V4(Ipv4Addr::LOCALHOST)),
903            desired_listening_port: Some(0),
904            ..Default::default()
905        });
906        let peer_ip = peer.enable_listener().await.unwrap();
907
908        // The same host in its native form and as a dual-stack listener reports it.
909        let native = SocketAddr::from_str("1.2.3.4:1111").unwrap();
910        let mapped = SocketAddr::from_str("[::ffff:1.2.3.4]:2222").unwrap();
911        let also_mapped = SocketAddr::from_str("[::ffff:1.2.3.4]:3333").unwrap();
912
913        // Sanity: as raw IP addresses these are two distinct keys, which is the bypass being closed.
914        assert_ne!(native.ip(), mapped.ip());
915
916        for addr in [native, mapped] {
917            assert!(tcp.can_add_connection(addr).is_ok());
918            let stream = TcpStream::connect(peer_ip).await.unwrap();
919            tcp.connections.add(Connection::new(addr, stream, ConnectionSide::Initiator, Span::none()));
920        }
921
922        // Both spellings are charged to one bucket, so the allowance cannot be claimed twice.
923        assert_eq!(tcp.num_connections_with_ip(native), 2);
924        assert_eq!(tcp.num_connections_with_ip(mapped), 2);
925        for addr in [native, also_mapped] {
926            assert!(matches!(
927                tcp.can_add_connection(addr),
928                Err(ConnectError::MaximumConnectionsPerIpReached { limit: 2, .. })
929            ));
930        }
931    }
932
933    #[tokio::test]
934    async fn test_handle_connection() {
935        let tcp = Tcp::new(Config {
936            listener_ip: Some(IpAddr::V4(Ipv4Addr::LOCALHOST)),
937            max_connections: 1,
938            ..Default::default()
939        });
940
941        // Initialize peer 1.
942        let peer1 = Tcp::new(Config {
943            listener_ip: Some(IpAddr::V4(Ipv4Addr::LOCALHOST)),
944            desired_listening_port: Some(0),
945            max_connections: 1,
946            ..Default::default()
947        });
948        let peer1_ip = peer1.enable_listener().await.unwrap();
949
950        // Simulate an active connection.
951        let stream = TcpStream::connect(peer1_ip).await.unwrap();
952        tcp.connections.add(Connection::new(peer1_ip, stream, ConnectionSide::Responder, Span::none()));
953        assert!(tcp.can_add_connection(peer1_ip).is_err());
954        assert_eq!(tcp.num_connected(), 1);
955        assert_eq!(tcp.num_connecting(), 0);
956        assert!(tcp.is_connected(peer1_ip));
957        assert!(!tcp.is_connecting(peer1_ip));
958
959        // Initialize peer 2.
960        let peer2 = Tcp::new(Config {
961            listener_ip: Some(IpAddr::V4(Ipv4Addr::LOCALHOST)),
962            desired_listening_port: Some(0),
963            max_connections: 1,
964            ..Default::default()
965        });
966        let peer2_ip = peer2.enable_listener().await.unwrap();
967
968        // Handle the connection.
969        let stream = TcpStream::connect(peer2_ip).await.unwrap();
970        let inbound_permits = Arc::new(Semaphore::new(1));
971        let permit = inbound_permits.clone().acquire_owned().await.unwrap();
972        tcp.handle_connection(stream, peer2_ip, permit);
973        assert!(tcp.can_add_connection(peer1_ip).is_err());
974        assert_eq!(tcp.num_connected(), 1);
975        assert_eq!(tcp.num_connecting(), 0);
976        assert!(tcp.is_connected(peer1_ip));
977        assert!(!tcp.is_connected(peer2_ip));
978        assert!(!tcp.is_connecting(peer1_ip));
979        assert!(!tcp.is_connecting(peer2_ip));
980    }
981
982    #[tokio::test]
983    async fn test_adapt_stream() {
984        let tcp = Tcp::new(Config { max_connections: 1, ..Default::default() });
985
986        // Initialize the peer.
987        let peer = Tcp::new(Config {
988            listener_ip: Some(IpAddr::V4(Ipv4Addr::LOCALHOST)),
989            desired_listening_port: Some(0),
990            max_connections: 1,
991            ..Default::default()
992        });
993        let peer_ip = peer.enable_listener().await.unwrap();
994
995        // Simulate a pending connection.
996        tcp.connecting.lock().insert(peer_ip);
997        assert_eq!(tcp.num_connected(), 0);
998        assert_eq!(tcp.num_connecting(), 1);
999        assert!(!tcp.is_connected(peer_ip));
1000        assert!(tcp.is_connecting(peer_ip));
1001
1002        // Simulate a new connection.
1003        let stream = TcpStream::connect(peer_ip).await.unwrap();
1004        tcp.adapt_stream(stream, peer_ip, ConnectionSide::Responder).await.unwrap();
1005        assert_eq!(tcp.num_connected(), 1);
1006        assert_eq!(tcp.num_connecting(), 0);
1007        assert!(tcp.is_connected(peer_ip));
1008        assert!(!tcp.is_connecting(peer_ip));
1009    }
1010}