1 use ffi::{
2     self, BIO_clear_retry_flags, BIO_new, BIO_set_retry_read, BIO_set_retry_write, BIO,
3     BIO_CTRL_DGRAM_QUERY_MTU, BIO_CTRL_FLUSH,
4 };
5 use libc::{c_char, c_int, c_long, c_void, strlen};
6 use std::any::Any;
7 use std::io;
8 use std::io::prelude::*;
9 use std::panic::{catch_unwind, AssertUnwindSafe};
10 use std::ptr;
11 use std::slice;
12 
13 use cvt_p;
14 use error::ErrorStack;
15 
16 pub struct StreamState<S> {
17     pub stream: S,
18     pub error: Option<io::Error>,
19     pub panic: Option<Box<dyn Any + Send>>,
20     pub dtls_mtu_size: c_long,
21 }
22 
23 /// Safe wrapper for BIO_METHOD
24 pub struct BioMethod(BIO_METHOD);
25 
26 impl BioMethod {
new<S: Read + Write>() -> Result<BioMethod, ErrorStack>27     fn new<S: Read + Write>() -> Result<BioMethod, ErrorStack> {
28         BIO_METHOD::new::<S>().map(BioMethod)
29     }
30 }
31 
32 unsafe impl Sync for BioMethod {}
33 unsafe impl Send for BioMethod {}
34 
new<S: Read + Write>(stream: S) -> Result<(*mut BIO, BioMethod), ErrorStack>35 pub fn new<S: Read + Write>(stream: S) -> Result<(*mut BIO, BioMethod), ErrorStack> {
36     let method = BioMethod::new::<S>()?;
37 
38     let state = Box::new(StreamState {
39         stream,
40         error: None,
41         panic: None,
42         dtls_mtu_size: 0,
43     });
44 
45     unsafe {
46         let bio = cvt_p(BIO_new(method.0.get()))?;
47         BIO_set_data(bio, Box::into_raw(state) as *mut _);
48         BIO_set_init(bio, 1);
49 
50         Ok((bio, method))
51     }
52 }
53 
take_error<S>(bio: *mut BIO) -> Option<io::Error>54 pub unsafe fn take_error<S>(bio: *mut BIO) -> Option<io::Error> {
55     let state = state::<S>(bio);
56     state.error.take()
57 }
58 
take_panic<S>(bio: *mut BIO) -> Option<Box<dyn Any + Send>>59 pub unsafe fn take_panic<S>(bio: *mut BIO) -> Option<Box<dyn Any + Send>> {
60     let state = state::<S>(bio);
61     state.panic.take()
62 }
63 
get_ref<'a, S: 'a>(bio: *mut BIO) -> &'a S64 pub unsafe fn get_ref<'a, S: 'a>(bio: *mut BIO) -> &'a S {
65     let state = &*(BIO_get_data(bio) as *const StreamState<S>);
66     &state.stream
67 }
68 
get_mut<'a, S: 'a>(bio: *mut BIO) -> &'a mut S69 pub unsafe fn get_mut<'a, S: 'a>(bio: *mut BIO) -> &'a mut S {
70     &mut state(bio).stream
71 }
72 
set_dtls_mtu_size<S>(bio: *mut BIO, mtu_size: usize)73 pub unsafe fn set_dtls_mtu_size<S>(bio: *mut BIO, mtu_size: usize) {
74     if mtu_size as u64 > c_long::max_value() as u64 {
75         panic!(
76             "Given MTU size {} can't be represented in a positive `c_long` range",
77             mtu_size
78         )
79     }
80     state::<S>(bio).dtls_mtu_size = mtu_size as c_long;
81 }
82 
state<'a, S: 'a>(bio: *mut BIO) -> &'a mut StreamState<S>83 unsafe fn state<'a, S: 'a>(bio: *mut BIO) -> &'a mut StreamState<S> {
84     &mut *(BIO_get_data(bio) as *mut _)
85 }
86 
bwrite<S: Write>(bio: *mut BIO, buf: *const c_char, len: c_int) -> c_int87 unsafe extern "C" fn bwrite<S: Write>(bio: *mut BIO, buf: *const c_char, len: c_int) -> c_int {
88     BIO_clear_retry_flags(bio);
89 
90     let state = state::<S>(bio);
91     let buf = slice::from_raw_parts(buf as *const _, len as usize);
92 
93     match catch_unwind(AssertUnwindSafe(|| state.stream.write(buf))) {
94         Ok(Ok(len)) => len as c_int,
95         Ok(Err(err)) => {
96             if retriable_error(&err) {
97                 BIO_set_retry_write(bio);
98             }
99             state.error = Some(err);
100             -1
101         }
102         Err(err) => {
103             state.panic = Some(err);
104             -1
105         }
106     }
107 }
108 
bread<S: Read>(bio: *mut BIO, buf: *mut c_char, len: c_int) -> c_int109 unsafe extern "C" fn bread<S: Read>(bio: *mut BIO, buf: *mut c_char, len: c_int) -> c_int {
110     BIO_clear_retry_flags(bio);
111 
112     let state = state::<S>(bio);
113     let buf = slice::from_raw_parts_mut(buf as *mut _, len as usize);
114 
115     match catch_unwind(AssertUnwindSafe(|| state.stream.read(buf))) {
116         Ok(Ok(len)) => len as c_int,
117         Ok(Err(err)) => {
118             if retriable_error(&err) {
119                 BIO_set_retry_read(bio);
120             }
121             state.error = Some(err);
122             -1
123         }
124         Err(err) => {
125             state.panic = Some(err);
126             -1
127         }
128     }
129 }
130 
131 #[allow(clippy::match_like_matches_macro)] // matches macro requires rust 1.42.0
retriable_error(err: &io::Error) -> bool132 fn retriable_error(err: &io::Error) -> bool {
133     match err.kind() {
134         io::ErrorKind::WouldBlock | io::ErrorKind::NotConnected => true,
135         _ => false,
136     }
137 }
138 
bputs<S: Write>(bio: *mut BIO, s: *const c_char) -> c_int139 unsafe extern "C" fn bputs<S: Write>(bio: *mut BIO, s: *const c_char) -> c_int {
140     bwrite::<S>(bio, s, strlen(s) as c_int)
141 }
142 
ctrl<S: Write>( bio: *mut BIO, cmd: c_int, _num: c_long, _ptr: *mut c_void, ) -> c_long143 unsafe extern "C" fn ctrl<S: Write>(
144     bio: *mut BIO,
145     cmd: c_int,
146     _num: c_long,
147     _ptr: *mut c_void,
148 ) -> c_long {
149     let state = state::<S>(bio);
150 
151     if cmd == BIO_CTRL_FLUSH {
152         match catch_unwind(AssertUnwindSafe(|| state.stream.flush())) {
153             Ok(Ok(())) => 1,
154             Ok(Err(err)) => {
155                 state.error = Some(err);
156                 0
157             }
158             Err(err) => {
159                 state.panic = Some(err);
160                 0
161             }
162         }
163     } else if cmd == BIO_CTRL_DGRAM_QUERY_MTU {
164         state.dtls_mtu_size
165     } else {
166         0
167     }
168 }
169 
create(bio: *mut BIO) -> c_int170 unsafe extern "C" fn create(bio: *mut BIO) -> c_int {
171     BIO_set_init(bio, 0);
172     BIO_set_num(bio, 0);
173     BIO_set_data(bio, ptr::null_mut());
174     BIO_set_flags(bio, 0);
175     1
176 }
177 
destroy<S>(bio: *mut BIO) -> c_int178 unsafe extern "C" fn destroy<S>(bio: *mut BIO) -> c_int {
179     if bio.is_null() {
180         return 0;
181     }
182 
183     let data = BIO_get_data(bio);
184     assert!(!data.is_null());
185     Box::<StreamState<S>>::from_raw(data as *mut _);
186     BIO_set_data(bio, ptr::null_mut());
187     BIO_set_init(bio, 0);
188     1
189 }
190 
191 cfg_if! {
192     if #[cfg(any(ossl110, libressl273))] {
193         use ffi::{BIO_get_data, BIO_set_data, BIO_set_flags, BIO_set_init};
194         use cvt;
195 
196         #[allow(bad_style)]
197         unsafe fn BIO_set_num(_bio: *mut ffi::BIO, _num: c_int) {}
198 
199         #[allow(bad_style)]
200         struct BIO_METHOD(*mut ffi::BIO_METHOD);
201 
202         impl BIO_METHOD {
203             fn new<S: Read + Write>() -> Result<BIO_METHOD, ErrorStack> {
204                 unsafe {
205                     let ptr = cvt_p(ffi::BIO_meth_new(ffi::BIO_TYPE_NONE, b"rust\0".as_ptr() as *const _))?;
206                     let method = BIO_METHOD(ptr);
207                     cvt(ffi::BIO_meth_set_write(method.0, bwrite::<S>))?;
208                     cvt(ffi::BIO_meth_set_read(method.0, bread::<S>))?;
209                     cvt(ffi::BIO_meth_set_puts(method.0, bputs::<S>))?;
210                     cvt(ffi::BIO_meth_set_ctrl(method.0, ctrl::<S>))?;
211                     cvt(ffi::BIO_meth_set_create(method.0, create))?;
212                     cvt(ffi::BIO_meth_set_destroy(method.0, destroy::<S>))?;
213                     Ok(method)
214                 }
215             }
216 
217             fn get(&self) -> *mut ffi::BIO_METHOD {
218                 self.0
219             }
220         }
221 
222         impl Drop for BIO_METHOD {
223             fn drop(&mut self) {
224                 unsafe {
225                     ffi::BIO_meth_free(self.0);
226                 }
227             }
228         }
229     } else {
230         #[allow(bad_style)]
231         struct BIO_METHOD(*mut ffi::BIO_METHOD);
232 
233         impl BIO_METHOD {
234             fn new<S: Read + Write>() -> Result<BIO_METHOD, ErrorStack> {
235                 let ptr = Box::new(ffi::BIO_METHOD {
236                     type_: ffi::BIO_TYPE_NONE,
237                     name: b"rust\0".as_ptr() as *const _,
238                     bwrite: Some(bwrite::<S>),
239                     bread: Some(bread::<S>),
240                     bputs: Some(bputs::<S>),
241                     bgets: None,
242                     ctrl: Some(ctrl::<S>),
243                     create: Some(create),
244                     destroy: Some(destroy::<S>),
245                     callback_ctrl: None,
246                 });
247 
248                 Ok(BIO_METHOD(Box::into_raw(ptr)))
249             }
250 
251             fn get(&self) -> *mut ffi::BIO_METHOD {
252                 self.0
253             }
254         }
255 
256         impl Drop for BIO_METHOD {
257             fn drop(&mut self) {
258                 unsafe {
259                     Box::<ffi::BIO_METHOD>::from_raw(self.0);
260                 }
261             }
262         }
263 
264         #[allow(bad_style)]
265         unsafe fn BIO_set_init(bio: *mut ffi::BIO, init: c_int) {
266             (*bio).init = init;
267         }
268 
269         #[allow(bad_style)]
270         unsafe fn BIO_set_flags(bio: *mut ffi::BIO, flags: c_int) {
271             (*bio).flags = flags;
272         }
273 
274         #[allow(bad_style)]
275         unsafe fn BIO_get_data(bio: *mut ffi::BIO) -> *mut c_void {
276             (*bio).ptr
277         }
278 
279         #[allow(bad_style)]
280         unsafe fn BIO_set_data(bio: *mut ffi::BIO, data: *mut c_void) {
281             (*bio).ptr = data;
282         }
283 
284         #[allow(bad_style)]
285         unsafe fn BIO_set_num(bio: *mut ffi::BIO, num: c_int) {
286             (*bio).num = num;
287         }
288     }
289 }
290