Skip to main content

mtorrent_core/utp/
handle.rs

1use super::connection::Connection;
2use super::protocol::{Header, TypeVer, dbg_header_extensions};
3use super::udp;
4use bytes::Bytes;
5use futures_util::{FutureExt, Stream, StreamExt};
6use local_async_utils::prelude::*;
7use log::log_enabled;
8use mtorrent_utils::split_stream::SplitStream;
9use std::hash::RandomState;
10use std::io;
11use std::net::SocketAddr;
12use std::ops::Deref;
13use std::pin::Pin;
14use std::rc::Rc;
15use std::task::{Context, Poll, ready};
16use tokio::io::{AsyncRead, AsyncWrite};
17use tokio::sync::mpsc;
18use tokio::task;
19
20/// Opaque data for an inbound connection attempt, returned by [`InboundListener`] and consumed by
21/// [`EndpointHandle::add_inbound_connection`].
22#[derive(Debug, Clone)]
23pub struct InboundConnectData(Header);
24
25/// Stream of incoming connection attempts (i.e. received SYN packets that don't belong to an
26/// existing connection).
27pub struct InboundListener(local_bounded::Receiver<(SocketAddr, Bytes)>);
28
29impl InboundListener {
30    pub(super) fn new(receiver: local_bounded::Receiver<(SocketAddr, Bytes)>) -> Self {
31        Self(receiver)
32    }
33}
34
35impl Stream for InboundListener {
36    type Item = (SocketAddr, InboundConnectData);
37
38    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
39        let Self(receiver) = self.get_mut();
40        loop {
41            let inbound = ready!(receiver.poll_next_unpin(cx));
42            let Some((source, mut packet)) = inbound else {
43                return Poll::Ready(None);
44            };
45            if let Ok(header) = Header::decode_from(&mut packet) {
46                if log_enabled!(log::Level::Trace) {
47                    log::trace!(
48                        "InboundListener got {:?} from {source}",
49                        dbg_header_extensions(&header, &packet)
50                    );
51                }
52                if let TypeVer::Syn = header.type_ver {
53                    return Poll::Ready(Some((source, InboundConnectData(header))));
54                }
55            }
56        }
57    }
58}
59
60/// Handle for creating new uTP connections.
61#[derive(Clone)]
62pub struct EndpointHandle {
63    cmds: mpsc::Sender<udp::Command>,
64    hasher_state: Rc<RandomState>,
65}
66
67impl EndpointHandle {
68    const PIPE_CAPACITY: usize = crate::pwp::MAX_BLOCK_SIZE;
69    const INGRESS_QUEUE: usize = 64;
70
71    pub(super) fn new(cmds: mpsc::Sender<udp::Command>) -> Self {
72        Self {
73            cmds,
74            hasher_state: Rc::new(RandomState::new()),
75        }
76    }
77
78    /// Establish a new outbound connection to `remote_addr`. Waits for the uTP handshake to
79    /// complete and returns a [`DataStream`] for the new connection.
80    /// # Error
81    /// If [`IoDriver`](super::IoDriver) has been shut down or if uTP handshake failed or if the
82    /// connection already exists.
83    pub async fn add_outbound_connection(&self, remote_addr: SocketAddr) -> io::Result<DataStream> {
84        let (left, right) = local_pipe::duplex_pipe(Self::PIPE_CAPACITY);
85        let (egress_sender, egress_receiver) = local_bounded::channel(1);
86        let (ingress_sender, ingress_receiver) = local_bounded::channel(Self::INGRESS_QUEUE);
87        let handle = udp::ConnectionHandle::new(egress_receiver, ingress_sender);
88        self.cmds
89            .send(udp::Command::AddConnection((remote_addr, handle)))
90            .await
91            .map_err(|_| io::Error::from(io::ErrorKind::BrokenPipe))?;
92
93        let connection = Connection::outbound(
94            remote_addr,
95            right,
96            ingress_receiver,
97            egress_sender,
98            self.hasher_state.deref(),
99        )
100        .await?;
101        log::debug!("Outbound connection to {remote_addr} established");
102
103        let (notifier, receiver) = local_condvar::condvar();
104        task::spawn_local(connection.run(receiver).inspect(move |result| match result {
105            Ok(()) => {
106                log::debug!("Outbound connection to {remote_addr} closed");
107            }
108            Err(e) => {
109                log::error!("Outbound connection to {remote_addr} exited with error: {e}");
110            }
111        }));
112        Ok(DataStream {
113            pipe: left,
114            _canceller: notifier,
115        })
116    }
117
118    /// Accept a new inbound connection from `remote_addr`. Waits for the uTP handshake to
119    /// complete and returns a [`DataStream`] for the new connection.
120    /// # Error
121    /// If [`IoDriver`](super::IoDriver) has been shut down or if uTP handshake failed or if the
122    /// connection already exists.
123    pub async fn add_inbound_connection(
124        &self,
125        remote_addr: SocketAddr,
126        data: InboundConnectData,
127    ) -> io::Result<DataStream> {
128        let InboundConnectData(syn) = data;
129        let (left, right) = local_pipe::duplex_pipe(Self::PIPE_CAPACITY);
130        let (egress_sender, egress_receiver) = local_bounded::channel(1);
131        let (ingress_sender, ingress_receiver) = local_bounded::channel(Self::INGRESS_QUEUE);
132        let handle = udp::ConnectionHandle::new(egress_receiver, ingress_sender);
133        self.cmds
134            .send(udp::Command::AddConnection((remote_addr, handle)))
135            .await
136            .map_err(|_| io::Error::from(io::ErrorKind::BrokenPipe))?;
137
138        let connection =
139            Connection::inbound(remote_addr, right, ingress_receiver, egress_sender, syn).await?;
140        log::debug!("Inbound connection from {remote_addr} established");
141
142        let (notifier, receiver) = local_condvar::condvar();
143        task::spawn_local(connection.run(receiver).inspect(move |result| match result {
144            Ok(()) => {
145                log::debug!("Inbound connection from {remote_addr} closed");
146            }
147            Err(e) => {
148                log::error!("Inbound connection from {remote_addr} exited with error: {e}");
149            }
150        }));
151        Ok(DataStream {
152            pipe: left,
153            _canceller: notifier,
154        })
155    }
156
157    /// Close all connections by sending RESET packets.
158    pub async fn reset_connections(&self) {
159        let _ = self.cmds.send(udp::Command::ResetConnections).await;
160    }
161}
162
163/// Readable and writable channel returned by [`EndpointHandle`] after a successful connection.
164/// Dropping the stream will close the underlying uTP connection by sending a RESET packet.
165#[derive(Debug)]
166pub struct DataStream {
167    pipe: local_pipe::DuplexEnd,
168    _canceller: local_condvar::Sender,
169}
170
171impl AsyncRead for DataStream {
172    fn poll_read(
173        self: Pin<&mut Self>,
174        cx: &mut Context<'_>,
175        buf: &mut tokio::io::ReadBuf<'_>,
176    ) -> Poll<io::Result<()>> {
177        Pin::new(&mut self.get_mut().pipe).poll_read(cx, buf)
178    }
179}
180
181impl AsyncWrite for DataStream {
182    fn poll_write(
183        self: Pin<&mut Self>,
184        cx: &mut Context<'_>,
185        buf: &[u8],
186    ) -> Poll<io::Result<usize>> {
187        Pin::new(&mut self.get_mut().pipe).poll_write(cx, buf)
188    }
189
190    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
191        Pin::new(&mut self.get_mut().pipe).poll_flush(cx)
192    }
193
194    fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
195        Pin::new(&mut self.get_mut().pipe).poll_shutdown(cx)
196    }
197
198    fn poll_write_vectored(
199        self: Pin<&mut Self>,
200        cx: &mut Context<'_>,
201        bufs: &[io::IoSlice<'_>],
202    ) -> Poll<io::Result<usize>> {
203        Pin::new(&mut self.get_mut().pipe).poll_write_vectored(cx, bufs)
204    }
205
206    fn is_write_vectored(&self) -> bool {
207        self.pipe.is_write_vectored()
208    }
209}
210
211impl SplitStream for DataStream {
212    type Ingress<'i> = <local_pipe::DuplexEnd as SplitStream>::Ingress<'i>;
213
214    type Egress<'e> = <local_pipe::DuplexEnd as SplitStream>::Egress<'e>;
215
216    fn split(&mut self) -> (Self::Ingress<'_>, Self::Egress<'_>) {
217        self.pipe.split()
218    }
219}