nng_c/
tls.rs

1//!TLS module
2
3use crate::socket;
4use crate::str::String;
5use crate::options::Options;
6use crate::defs::MAX_HOSTNAME_LEN;
7use crate::error::{error, ErrorCode};
8
9use core::ptr::{self, NonNull};
10use core::ffi::CStr;
11
12use nng_c_sys as sys;
13use sys::{nng_tls_mode, nng_tls_auth_mode, nng_tls_version};
14use sys::nng_tls_config;
15
16///Get available TLS engine
17pub fn get_engine_name() -> &'static str {
18    //This never fails
19    let name = unsafe {
20        CStr::from_ptr(
21            nng_c_sys::nng_tls_engine_name()
22        )
23    };
24
25    name.to_str().unwrap_or("unknown")
26}
27
28#[derive(Debug, Clone)]
29///Certificate authority to validate remote peer
30pub struct CA<'a> {
31    ///PEM encoded certificate or chain
32    pub cert: String<'a>,
33    ///Optional PEM encoded revocation list
34    pub crl: Option<String<'a>>,
35}
36
37#[derive(Debug, Clone)]
38///Local certificate input
39pub struct OwnCert<'a> {
40    ///PEM encoded certificate or chain
41    pub cert: String<'a>,
42    ///PEM encoded private key.
43    pub key: String<'a>,
44    ///Optional passphrase to decrypt private key.
45    pub pass: Option<String<'a>>,
46}
47
48///Authentication mode
49#[derive(Copy, Clone, Debug)]
50#[repr(i32)]
51pub enum Auth {
52    ///No authentication of the TLS peer is performed. This is the default for TLS servers, which most typically do not authenticate their clients.
53    None = nng_tls_auth_mode::NNG_TLS_AUTH_MODE_NONE,
54    ///If a certificate is presented by the peer, then it is validated. However, if the peer does not present a valid certificate, then the session is allowed to proceed without authentication.
55    Optional = nng_tls_auth_mode::NNG_TLS_AUTH_MODE_OPTIONAL,
56    ///A check is made to ensure that the peer has presented a valid certificate used for the session. If the peer’s certificate is invalid or missing, then the session is refused. This is the default for clients.
57    Required = nng_tls_auth_mode::NNG_TLS_AUTH_MODE_REQUIRED,
58}
59
60///TLS version
61#[derive(Copy, Clone, Debug)]
62#[repr(i32)]
63pub enum Version {
64    ///TLS 1.0
65    Tls1_0 = nng_tls_version::NNG_TLS_1_0,
66    ///TLS 1.1
67    Tls1_1 = nng_tls_version::NNG_TLS_1_1,
68    ///TLS 1.2
69    Tls1_2 = nng_tls_version::NNG_TLS_1_2,
70    ///TLS 1.3
71    Tls1_3 = nng_tls_version::NNG_TLS_1_3,
72}
73
74///TLS Config container
75///
76///This object is reference counted pointer so it can be cloned safely.
77///
78///It can be applied when listening or connecting.
79pub struct Config(NonNull<nng_tls_config>);
80
81impl Config {
82    fn new(mode: nng_tls_mode::Type) -> Option<Self> {
83        let mut ptr = ptr::null_mut();
84        unsafe {
85            sys::nng_tls_config_alloc(&mut ptr, mode);
86        }
87
88        NonNull::new(ptr).map(Self)
89    }
90
91    #[inline(always)]
92    ///Creates client configuration.
93    ///
94    ///Returns `None` when cannot allocate memory
95    pub fn client() -> Option<Self> {
96        Self::new(nng_tls_mode::NNG_TLS_MODE_CLIENT)
97    }
98
99    #[inline(always)]
100    ///Creates server configuration.
101    ///
102    ///Returns `None` when cannot allocate memory
103    pub fn server() -> Option<Self> {
104        Self::new(nng_tls_mode::NNG_TLS_MODE_SERVER)
105    }
106
107    ///Sets `Auth` mode of the configuration
108    ///
109    ///Defaults:
110    ///- None for server side (listener)
111    ///- Required for client side (connecting)
112    pub fn auth_mode(&self, mode: Auth) -> Result<(), ErrorCode> {
113        let result = unsafe {
114            sys::nng_tls_config_auth_mode(self.0.as_ptr(), mode as _)
115        };
116
117        match result {
118            0 => Ok(()),
119            code => Err(error(code)),
120        }
121    }
122
123    ///Sets range of supported TLS `Version`s
124    pub fn versions(&self, min: Version, max: Version) -> Result<(), ErrorCode> {
125        let result = unsafe {
126            sys::nng_tls_config_version(self.0.as_ptr(), min as _, max as _)
127        };
128
129        match result {
130            0 => Ok(()),
131            code => Err(error(code)),
132        }
133    }
134
135    ///Sets server `name` for the client connection
136    pub fn server_name(&self, name: &str) -> Result<(), ErrorCode> {
137        if name.len() > MAX_HOSTNAME_LEN {
138            return Err(error(sys::nng_errno_enum::NNG_EADDRINVAL));
139        }
140
141        let mut buffer = [0u8; MAX_HOSTNAME_LEN + 1];
142        let result = unsafe {
143            ptr::copy_nonoverlapping(name.as_ptr(), buffer.as_mut_ptr(), name.len());
144            sys::nng_tls_config_server_name(self.0.as_ptr(), buffer.as_ptr() as _)
145        };
146
147        match result {
148            0 => Ok(()),
149            code => Err(error(code)),
150        }
151    }
152
153    ///Sets CA certificate used in TLS handshake
154    pub fn ca_cert(&self, cert: &CA<'_>) -> Result<(), ErrorCode> {
155        let crl = match cert.crl.as_ref() {
156            Some(crl) => crl.as_ptr(),
157            None => ptr::null()
158        };
159        let cert = cert.cert.as_ptr();
160        let result = unsafe {
161            sys::nng_tls_config_ca_chain(self.0.as_ptr(), cert as _, crl as _)
162        };
163
164        match result {
165            0 => Ok(()),
166            code => Err(error(code)),
167        }
168    }
169
170    ///Sets local certificate used in TLS handshake
171    pub fn own_cert(&self, cert: &OwnCert<'_>) -> Result<(), ErrorCode> {
172        let pass = match cert.pass.as_ref() {
173            Some(pass) => pass.as_ptr(),
174            None => ptr::null()
175        };
176        let key = cert.key.as_ptr();
177        let cert = cert.cert.as_ptr();
178        let result = unsafe {
179            sys::nng_tls_config_own_cert(self.0.as_ptr(), cert as _, key as _, pass as _)
180        };
181
182        match result {
183            0 => Ok(()),
184            code => Err(error(code)),
185        }
186    }
187}
188
189impl Clone for Config {
190    #[inline]
191    fn clone(&self) -> Self {
192        unsafe {
193            sys::nng_tls_config_hold(self.0.as_ptr())
194        }
195        Self(self.0)
196    }
197}
198
199impl Drop for Config {
200    #[inline(always)]
201    fn drop(&mut self) {
202        unsafe {
203            sys::nng_tls_config_free(self.0.as_ptr())
204        }
205    }
206}
207
208impl Options<socket::Listener> for Config {
209    #[inline]
210    fn apply(&self, target: &socket::Listener) -> Result<(), ErrorCode> {
211        let result = unsafe {
212            sys::nng_listener_set_ptr(target.0, sys::NNG_OPT_TLS_CONFIG.as_ptr() as _, self.0.as_ptr() as _)
213        };
214
215        match result {
216            0 => Ok(()),
217            code => Err(error(code)),
218        }
219    }
220}
221
222impl Options<socket::Dialer> for Config {
223    #[inline]
224    fn apply(&self, target: &socket::Dialer) -> Result<(), ErrorCode> {
225        let result = unsafe {
226            sys::nng_dialer_set_ptr(target.0, sys::NNG_OPT_TLS_CONFIG.as_ptr() as _, self.0.as_ptr() as _)
227        };
228
229        match result {
230            0 => Ok(()),
231            code => Err(error(code)),
232        }
233    }
234}