tls_api/
connector_box.rs

1use std::fmt;
2use std::marker;
3
4use crate::assert_send;
5use crate::assert_sync;
6use crate::AsyncSocket;
7use crate::AsyncSocketBox;
8use crate::BoxFuture;
9use crate::ImplInfo;
10use crate::TlsConnector;
11use crate::TlsConnectorBuilder;
12use crate::TlsStream;
13
14// Connector type.
15
16/// Similar to [`TlsConnector`], but it is dynamic, does not require type parameter.
17///
18/// This can be obtained with [`TlsConnector::TYPE_DYN`].
19pub trait TlsConnectorType: fmt::Display + fmt::Debug + 'static {
20    /// Constructor a builder dynamically.
21    fn builder(&self) -> anyhow::Result<TlsConnectorBuilderBox>;
22
23    /// It this connector implemented?
24    ///
25    /// When not implemented, all operations return error.
26    ///
27    /// For example, `tls-api-security-framework` is available on Linux,
28    /// but all operations result in error, so `implemented()` returns `false`
29    /// for that implementation.
30    fn implemented(&self) -> bool;
31
32    /// Is this implementation ALPN negotation?
33    fn supports_alpn(&self) -> bool;
34
35    /// Implementation version.
36    fn info(&self) -> ImplInfo;
37}
38
39pub(crate) struct TlsConnectorTypeImpl<C: TlsConnector>(pub marker::PhantomData<C>);
40
41impl<A: TlsConnector> fmt::Debug for TlsConnectorTypeImpl<A> {
42    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43        fmt::Debug::fmt(&A::info(), f)
44    }
45}
46
47impl<A: TlsConnector> fmt::Display for TlsConnectorTypeImpl<A> {
48    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49        fmt::Debug::fmt(&A::info(), f)
50    }
51}
52
53impl<C: TlsConnector> TlsConnectorType for TlsConnectorTypeImpl<C> {
54    fn builder(&self) -> anyhow::Result<TlsConnectorBuilderBox> {
55        Ok(TlsConnectorBuilderBox(Box::new(C::builder()?)))
56    }
57
58    fn implemented(&self) -> bool {
59        C::IMPLEMENTED
60    }
61
62    fn supports_alpn(&self) -> bool {
63        C::SUPPORTS_ALPN
64    }
65
66    fn info(&self) -> ImplInfo {
67        C::info()
68    }
69}
70
71// Connector builder.
72
73trait TlsConnectorBuilderDyn: Send + 'static {
74    fn set_alpn_protocols(&mut self, protocols: &[&[u8]]) -> anyhow::Result<()>;
75
76    fn set_verify_hostname(&mut self, verify: bool) -> anyhow::Result<()>;
77
78    fn add_root_certificate(&mut self, cert: &[u8]) -> anyhow::Result<()>;
79
80    fn build(self: Box<Self>) -> anyhow::Result<TlsConnectorBox>;
81}
82
83impl<C: TlsConnectorBuilder> TlsConnectorBuilderDyn for C {
84    fn set_alpn_protocols(&mut self, protocols: &[&[u8]]) -> anyhow::Result<()> {
85        self.set_alpn_protocols(protocols)
86    }
87
88    fn set_verify_hostname(&mut self, verify: bool) -> anyhow::Result<()> {
89        self.set_verify_hostname(verify)
90    }
91
92    fn add_root_certificate(&mut self, cert: &[u8]) -> anyhow::Result<()> {
93        self.add_root_certificate(cert)
94    }
95
96    fn build(self: Box<Self>) -> anyhow::Result<TlsConnectorBox> {
97        let connector = (*self).build()?;
98        Ok(TlsConnectorBox(Box::new(connector)))
99    }
100}
101
102/// [`TlsConnector`] without type parameter.
103///
104/// Implementation can be switched without parameterizing every function.
105pub struct TlsConnectorBuilderBox(Box<dyn TlsConnectorBuilderDyn>);
106
107impl TlsConnectorBuilderBox {
108    /// Build a connector.
109    pub fn build(self) -> anyhow::Result<TlsConnectorBox> {
110        self.0.build()
111    }
112
113    /// Set ALPN-protocols to negotiate.
114    ///
115    /// This operations fails is not [`TlsConnector::SUPPORTS_ALPN`].
116    pub fn set_alpn_protocols(&mut self, protocols: &[&[u8]]) -> anyhow::Result<()> {
117        self.0.set_alpn_protocols(protocols)
118    }
119
120    /// Should hostname verification be performed?
121    /// Use carefully, it opens the door to MITM attacks.
122    pub fn set_verify_hostname(&mut self, verify: bool) -> anyhow::Result<()> {
123        self.0.set_verify_hostname(verify)
124    }
125
126    /// Add trusted certificate (e. g. CA).
127    pub fn add_root_certificate(&mut self, cert: &[u8]) -> anyhow::Result<()> {
128        self.0.add_root_certificate(cert)
129    }
130}
131
132// Connector.
133
134trait TlsConnectorDyn: Send + Sync + 'static {
135    fn connect<'a>(
136        &'a self,
137        domain: &'a str,
138        stream: AsyncSocketBox,
139    ) -> BoxFuture<'a, anyhow::Result<TlsStream>>;
140}
141
142impl<C: TlsConnector> TlsConnectorDyn for C {
143    fn connect<'a>(
144        &'a self,
145        domain: &'a str,
146        stream: AsyncSocketBox,
147    ) -> BoxFuture<'a, anyhow::Result<TlsStream>> {
148        self.connect(domain, stream)
149    }
150}
151
152/// Configured connector. This is a dynamic version of [`TlsConnector`].
153///
154/// This can be constructed either with:
155/// * [`TlsConnector::into_dyn`]
156/// * [`TlsConnectorBuilderBox::build`]
157pub struct TlsConnectorBox(Box<dyn TlsConnectorDyn>);
158
159impl TlsConnectorBox {
160    pub(crate) fn new<C: TlsConnector>(connector: C) -> TlsConnectorBox {
161        TlsConnectorBox(Box::new(connector))
162    }
163}
164
165impl TlsConnectorBox {
166    /// Connect.
167    pub fn connect_dyn<'a>(
168        &'a self,
169        domain: &'a str,
170        stream: AsyncSocketBox,
171    ) -> BoxFuture<'a, anyhow::Result<TlsStream>> {
172        self.0.connect(domain, stream)
173    }
174
175    /// Connect.
176    pub fn connect<'a, S: AsyncSocket>(
177        &'a self,
178        domain: &'a str,
179        stream: S,
180    ) -> BoxFuture<'a, anyhow::Result<TlsStream>> {
181        self.connect_dyn(domain, AsyncSocketBox::new(stream))
182    }
183}
184
185fn _assert_kinds() {
186    assert_send::<TlsConnectorBuilderBox>();
187    assert_send::<TlsConnectorBox>();
188    assert_sync::<TlsConnectorBox>();
189}