1use crate::memdx::error::Error;
20use crate::memdx::error::Result;
21use crate::tls_config::TlsConfig;
22use socket2::TcpKeepalive;
23use std::fmt::Debug;
24use std::io;
25use std::net::{IpAddr, Ipv4Addr, SocketAddr};
26use std::time::Duration;
27use tokio::io::{AsyncRead, AsyncWrite};
28use tokio::net::TcpStream;
29use tokio::time::{timeout_at, Instant};
30
31use crate::address::Address;
32#[cfg(all(feature = "rustls-tls", not(feature = "native-tls")))]
33use {
34 tokio_rustls::rustls::pki_types::DnsName, tokio_rustls::rustls::pki_types::ServerName,
35 tokio_rustls::TlsConnector,
36};
37
38#[derive(Debug)]
39pub struct ConnectOptions {
40 pub deadline: Instant,
41 pub tcp_keep_alive_time: Duration,
42}
43
44pub trait Stream: Debug + AsyncWrite + AsyncRead + Send + Sync + Unpin + 'static {}
45
46impl Stream for TcpStream {}
47
48#[derive(Debug)]
49#[allow(clippy::large_enum_variant)]
50pub enum ConnectionType {
51 Tcp(TcpConnection),
52 Tls(TlsConnection),
53}
54
55impl ConnectionType {
56 pub fn into_inner(self) -> Box<dyn Stream> {
57 match self {
58 ConnectionType::Tcp(connection) => Box::new(connection.stream),
59 ConnectionType::Tls(connection) => Box::new(connection.stream),
60 }
61 }
62
63 pub fn local_addr(&self) -> &SocketAddr {
64 match self {
65 ConnectionType::Tcp(connection) => &connection.local_addr,
66 ConnectionType::Tls(connection) => &connection.local_addr,
67 }
68 }
69
70 pub fn peer_addr(&self) -> &SocketAddr {
71 match self {
72 ConnectionType::Tcp(connection) => &connection.peer_addr,
73 ConnectionType::Tls(connection) => &connection.peer_addr,
74 }
75 }
76}
77
78#[derive(Debug)]
79pub struct TcpConnection {
80 stream: TcpStream,
81
82 local_addr: SocketAddr,
83 peer_addr: SocketAddr,
84}
85
86impl TcpConnection {
87 async fn tcp_stream(
88 addr: &str,
89 opts: &ConnectOptions,
90 ) -> Result<(TcpStream, SocketAddr, SocketAddr)> {
91 let tcp_socket = timeout_at(opts.deadline, TcpStream::connect(addr))
92 .await
93 .map_err(|e| {
94 Error::new_connection_failed_error(
95 "failed to connect to server within timeout",
96 Box::new(io::Error::new(io::ErrorKind::TimedOut, e)),
97 )
98 })?
99 .map_err(|e| {
100 Error::new_connection_failed_error("failed to create tcp stream", Box::new(e))
101 })?;
102
103 let local_addr = tcp_socket
104 .local_addr()
105 .unwrap_or_else(|_e| SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0));
106
107 let peer_addr = tcp_socket
108 .peer_addr()
109 .unwrap_or_else(|_e| SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0));
110
111 socket2::SockRef::from(&tcp_socket)
113 .set_tcp_keepalive(&TcpKeepalive::new().with_time(opts.tcp_keep_alive_time))?;
114
115 tcp_socket.set_nodelay(false).map_err(|e| {
116 Error::new_connection_failed_error("failed to set tcp nodelay", Box::new(e))
117 })?;
118
119 Ok((tcp_socket, local_addr, peer_addr))
120 }
121
122 pub async fn connect(addr: Address, opts: ConnectOptions) -> Result<TcpConnection> {
123 let (stream, local_addr, peer_addr) =
124 TcpConnection::tcp_stream(addr.to_string().as_str(), &opts).await?;
125
126 Ok(TcpConnection {
127 stream,
128 local_addr,
129 peer_addr,
130 })
131 }
132
133 fn local_addr(&self) -> &SocketAddr {
134 &self.local_addr
135 }
136
137 fn peer_addr(&self) -> &SocketAddr {
138 &self.peer_addr
139 }
140}
141
142#[derive(Debug)]
143pub struct TlsConnection {
144 #[cfg(all(feature = "rustls-tls", not(feature = "native-tls")))]
145 stream: tokio_rustls::client::TlsStream<TcpStream>,
146 #[cfg(feature = "native-tls")]
147 stream: tokio_native_tls::TlsStream<TcpStream>,
148
149 local_addr: SocketAddr,
150 peer_addr: SocketAddr,
151}
152
153#[cfg(all(feature = "rustls-tls", not(feature = "native-tls")))]
154impl Stream for tokio_rustls::client::TlsStream<TcpStream> {}
155
156#[cfg(feature = "native-tls")]
157impl Stream for tokio_native_tls::TlsStream<TcpStream> {}
158
159impl TlsConnection {
160 #[cfg(all(feature = "rustls-tls", not(feature = "native-tls")))]
161 pub async fn connect(
162 addr: Address,
163 tls_config: TlsConfig,
164 opts: ConnectOptions,
165 ) -> Result<TlsConnection> {
166 let (tcp_socket, local_addr, peer_addr) =
167 TcpConnection::tcp_stream(addr.to_string().as_str(), &opts).await?;
168
169 let connector = TlsConnector::from(tls_config);
170
171 let server_name = match DnsName::try_from(addr.host) {
172 Ok(name) => ServerName::DnsName(name),
173 Err(_e) => ServerName::IpAddress(tokio_rustls::rustls::pki_types::IpAddr::from(
174 peer_addr.ip(),
175 )),
176 };
177
178 let stream = timeout_at(opts.deadline, connector.connect(server_name, tcp_socket))
179 .await
180 .map_err(|e| {
181 Error::new_connection_failed_error(
182 "failed to upgrade tcp stream to tls within timeout",
183 Box::new(io::Error::new(io::ErrorKind::TimedOut, e)),
184 )
185 })?
186 .map_err(|e| {
187 Error::new_connection_failed_error(
188 "failed to upgrade tcp stream to tls",
189 Box::new(e),
190 )
191 })?;
192
193 Ok(TlsConnection {
194 stream,
195 local_addr,
196 peer_addr,
197 })
198 }
199
200 #[cfg(feature = "native-tls")]
201 pub async fn connect(
202 addr: Address,
203 tls_config: TlsConfig,
204 opts: ConnectOptions,
205 ) -> Result<TlsConnection> {
206 let (tcp_socket, local_addr, peer_addr) =
207 TcpConnection::tcp_stream(addr.to_string().as_str(), &opts).await?;
208
209 let tls_connector = tokio_native_tls::TlsConnector::from(tls_config);
210
211 let remote_addr = addr.to_string();
212 let stream = timeout_at(
213 opts.deadline,
214 tls_connector.connect(&remote_addr, tcp_socket),
215 )
216 .await
217 .map_err(|e| {
218 Error::new_connection_failed_error(
219 "failed to upgrade tcp stream to tls within timeout",
220 Box::new(io::Error::new(io::ErrorKind::TimedOut, e)),
221 )
222 })?
223 .map_err(|e| {
224 Error::new_connection_failed_error(
225 "failed to upgrade tcp stream to tls",
226 Box::new(io::Error::other(e)),
227 )
228 })?;
229
230 Ok(TlsConnection {
231 stream,
232 local_addr,
233 peer_addr,
234 })
235 }
236
237 fn local_addr(&self) -> &SocketAddr {
238 &self.local_addr
239 }
240
241 fn peer_addr(&self) -> &SocketAddr {
242 &self.peer_addr
243 }
244}