ant_quic/high_level/
incoming.rs

1// Copyright 2024 Saorsa Labs Ltd.
2//
3// This Saorsa Network Software is licensed under the General Public License (GPL), version 3.
4// Please see the file LICENSE-GPL, or visit <http://www.gnu.org/licenses/> for the full text.
5//
6// Full details available at https://saorsalabs.com/licenses
7
8use std::{
9    future::{Future, IntoFuture},
10    net::{IpAddr, SocketAddr},
11    pin::Pin,
12    sync::Arc,
13    task::{Context, Poll},
14};
15
16use crate::{ConnectionError, ConnectionId, ServerConfig};
17use thiserror::Error;
18use tracing::error;
19
20use super::{
21    connection::{Connecting, Connection},
22    endpoint::EndpointRef,
23};
24
25/// An incoming connection for which the server has not yet begun its part of the handshake
26#[derive(Debug)]
27pub struct Incoming(Option<State>);
28
29impl Incoming {
30    pub(crate) fn new(inner: crate::Incoming, endpoint: EndpointRef) -> Self {
31        Self(Some(State { inner, endpoint }))
32    }
33
34    /// Attempt to accept this incoming connection (an error may still occur)
35    pub fn accept(mut self) -> Result<Connecting, ConnectionError> {
36        let state = self.0.take().ok_or_else(|| {
37            error!("Incoming connection state already consumed");
38            ConnectionError::LocallyClosed
39        })?;
40        state.endpoint.accept(state.inner, None)
41    }
42
43    /// Accept this incoming connection using a custom configuration
44    ///
45    /// See [`accept()`][Incoming::accept] for more details.
46    pub fn accept_with(
47        mut self,
48        server_config: Arc<ServerConfig>,
49    ) -> Result<Connecting, ConnectionError> {
50        let state = self.0.take().ok_or_else(|| {
51            error!("Incoming connection state already consumed");
52            ConnectionError::LocallyClosed
53        })?;
54        state.endpoint.accept(state.inner, Some(server_config))
55    }
56
57    /// Reject this incoming connection attempt
58    pub fn refuse(mut self) {
59        if let Some(state) = self.0.take() {
60            state.endpoint.refuse(state.inner);
61        } else {
62            error!("Incoming connection state already consumed");
63        }
64    }
65
66    /// Respond with a retry packet, requiring the client to retry with address validation
67    ///
68    /// Errors if `may_retry()` is false.
69    #[allow(clippy::panic)]
70    pub fn retry(mut self) -> Result<(), RetryError> {
71        let state = match self.0.take() {
72            Some(state) => state,
73            None => {
74                error!("Incoming connection state already consumed");
75                // This is a programming error - the connection has already been consumed
76                // In a production system, this should be handled more gracefully
77                // For now, we'll panic since this indicates a bug in the calling code
78                panic!("Incoming connection state already consumed - this is a programming error");
79            }
80        };
81        state.endpoint.retry(state.inner).map_err(|_| {
82            // Since we can't create a proper RetryError without an Incoming,
83            // we'll panic as this indicates a serious internal error
84            panic!("Retry failed due to internal error");
85        })
86    }
87
88    /// Ignore this incoming connection attempt, not sending any packet in response
89    pub fn ignore(mut self) {
90        if let Some(state) = self.0.take() {
91            state.endpoint.ignore(state.inner);
92        } else {
93            error!("Incoming connection state already consumed");
94        }
95    }
96
97    /// The local IP address which was used when the peer established the connection
98    pub fn local_ip(&self) -> Option<IpAddr> {
99        self.0.as_ref()?.inner.local_ip()
100    }
101
102    /// The peer's UDP address
103    #[allow(clippy::panic)]
104    pub fn remote_address(&self) -> SocketAddr {
105        self.0
106            .as_ref()
107            .map(|state| state.inner.remote_address())
108            .unwrap_or_else(|| {
109                "0.0.0.0:0".parse().unwrap_or_else(|_| {
110                    panic!("Failed to parse fallback address");
111                })
112            })
113    }
114
115    /// Whether the socket address that is initiating this connection has been validated
116    ///
117    /// This means that the sender of the initial packet has proved that they can receive traffic
118    /// sent to `self.remote_address()`.
119    ///
120    /// If `self.remote_address_validated()` is false, `self.may_retry()` is guaranteed to be true.
121    /// The inverse is not guaranteed.
122    pub fn remote_address_validated(&self) -> bool {
123        self.0
124            .as_ref()
125            .map(|state| state.inner.remote_address_validated())
126            .unwrap_or(false)
127    }
128
129    /// Whether it is legal to respond with a retry packet
130    ///
131    /// If `self.remote_address_validated()` is false, `self.may_retry()` is guaranteed to be true.
132    /// The inverse is not guaranteed.
133    pub fn may_retry(&self) -> bool {
134        self.0
135            .as_ref()
136            .map(|state| state.inner.may_retry())
137            .unwrap_or(false)
138    }
139
140    /// The original destination CID when initiating the connection
141    pub fn orig_dst_cid(&self) -> ConnectionId {
142        self.0
143            .as_ref()
144            .map(|state| *state.inner.orig_dst_cid())
145            .unwrap_or_else(|| ConnectionId::new(&[0]))
146    }
147}
148
149impl Drop for Incoming {
150    fn drop(&mut self) {
151        // Implicit reject, similar to Connection's implicit close
152        if let Some(state) = self.0.take() {
153            state.endpoint.refuse(state.inner);
154        }
155    }
156}
157
158#[derive(Debug)]
159struct State {
160    inner: crate::Incoming,
161    endpoint: EndpointRef,
162}
163
164/// Error for attempting to retry an [`Incoming`] which already bears a token from a previous retry
165#[derive(Debug, Error)]
166#[error("retry() with validated Incoming")]
167pub struct RetryError(Box<Incoming>);
168
169impl RetryError {
170    /// Get the [`Incoming`]
171    pub fn into_incoming(self) -> Incoming {
172        *self.0
173    }
174}
175
176/// Basic adapter to let [`Incoming`] be `await`-ed like a [`Connecting`]
177#[derive(Debug)]
178pub struct IncomingFuture(Result<Connecting, ConnectionError>);
179
180impl Future for IncomingFuture {
181    type Output = Result<Connection, ConnectionError>;
182
183    fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
184        match &mut self.0 {
185            Ok(connecting) => Pin::new(connecting).poll(cx),
186            Err(e) => Poll::Ready(Err(e.clone())),
187        }
188    }
189}
190
191impl IntoFuture for Incoming {
192    type Output = Result<Connection, ConnectionError>;
193    type IntoFuture = IncomingFuture;
194
195    fn into_future(self) -> Self::IntoFuture {
196        IncomingFuture(self.accept())
197    }
198}