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>() -> BioMethod27     fn new<S: Read + Write>() -> BioMethod {
28         BioMethod(BIO_METHOD::new::<S>())
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 
retriable_error(err: &io::Error) -> bool131 fn retriable_error(err: &io::Error) -> bool {
132     match err.kind() {
133         io::ErrorKind::WouldBlock | io::ErrorKind::NotConnected => true,
134         _ => false,
135     }
136 }
137 
bputs<S: Write>(bio: *mut BIO, s: *const c_char) -> c_int138 unsafe extern "C" fn bputs<S: Write>(bio: *mut BIO, s: *const c_char) -> c_int {
139     bwrite::<S>(bio, s, strlen(s) as c_int)
140 }
141 
ctrl<S: Write>( bio: *mut BIO, cmd: c_int, _num: c_long, _ptr: *mut c_void, ) -> c_long142 unsafe extern "C" fn ctrl<S: Write>(
143     bio: *mut BIO,
144     cmd: c_int,
145     _num: c_long,
146     _ptr: *mut c_void,
147 ) -> c_long {
148     let state = state::<S>(bio);
149 
150     if cmd == BIO_CTRL_FLUSH {
151         match catch_unwind(AssertUnwindSafe(|| state.stream.flush())) {
152             Ok(Ok(())) => 1,
153             Ok(Err(err)) => {
154                 state.error = Some(err);
155                 0
156             }
157             Err(err) => {
158                 state.panic = Some(err);
159                 0
160             }
161         }
162     } else if cmd == BIO_CTRL_DGRAM_QUERY_MTU {
163         state.dtls_mtu_size
164     } else {
165         0
166     }
167 }
168 
create(bio: *mut BIO) -> c_int169 unsafe extern "C" fn create(bio: *mut BIO) -> c_int {
170     BIO_set_init(bio, 0);
171     BIO_set_num(bio, 0);
172     BIO_set_data(bio, ptr::null_mut());
173     BIO_set_flags(bio, 0);
174     1
175 }
176 
destroy<S>(bio: *mut BIO) -> c_int177 unsafe extern "C" fn destroy<S>(bio: *mut BIO) -> c_int {
178     if bio.is_null() {
179         return 0;
180     }
181 
182     let data = BIO_get_data(bio);
183     assert!(!data.is_null());
184     Box::<StreamState<S>>::from_raw(data as *mut _);
185     BIO_set_data(bio, ptr::null_mut());
186     BIO_set_init(bio, 0);
187     1
188 }
189 
190 cfg_if! {
191     if #[cfg(any(ossl110, libressl273))] {
192         use ffi::{BIO_get_data, BIO_set_data, BIO_set_flags, BIO_set_init};
193 
194         #[allow(bad_style)]
195         unsafe fn BIO_set_num(_bio: *mut ffi::BIO, _num: c_int) {}
196 
197         #[allow(bad_style)]
198         struct BIO_METHOD(*mut ffi::BIO_METHOD);
199 
200         impl BIO_METHOD {
201             fn new<S: Read + Write>() -> BIO_METHOD {
202                 unsafe {
203                     let ptr = ffi::BIO_meth_new(ffi::BIO_TYPE_NONE, b"rust\0".as_ptr() as *const _);
204                     assert!(!ptr.is_null());
205                     let ret = BIO_METHOD(ptr);
206                     assert!(ffi::BIO_meth_set_write(ptr, bwrite::<S>) != 0);
207                     assert!(ffi::BIO_meth_set_read(ptr, bread::<S>) != 0);
208                     assert!(ffi::BIO_meth_set_puts(ptr, bputs::<S>) != 0);
209                     assert!(ffi::BIO_meth_set_ctrl(ptr, ctrl::<S>) != 0);
210                     assert!(ffi::BIO_meth_set_create(ptr, create) != 0);
211                     assert!(ffi::BIO_meth_set_destroy(ptr, destroy::<S>) != 0);
212                     ret
213                 }
214             }
215 
216             fn get(&self) -> *mut ffi::BIO_METHOD {
217                 self.0
218             }
219         }
220 
221         impl Drop for BIO_METHOD {
222             fn drop(&mut self) {
223                 unsafe {
224                     ffi::BIO_meth_free(self.0);
225                 }
226             }
227         }
228     } else {
229         #[allow(bad_style)]
230         struct BIO_METHOD(*mut ffi::BIO_METHOD);
231 
232         impl BIO_METHOD {
233             fn new<S: Read + Write>() -> BIO_METHOD {
234                 let ptr = Box::new(ffi::BIO_METHOD {
235                     type_: ffi::BIO_TYPE_NONE,
236                     name: b"rust\0".as_ptr() as *const _,
237                     bwrite: Some(bwrite::<S>),
238                     bread: Some(bread::<S>),
239                     bputs: Some(bputs::<S>),
240                     bgets: None,
241                     ctrl: Some(ctrl::<S>),
242                     create: Some(create),
243                     destroy: Some(destroy::<S>),
244                     callback_ctrl: None,
245                 });
246 
247                 BIO_METHOD(Box::into_raw(ptr))
248             }
249 
250             fn get(&self) -> *mut ffi::BIO_METHOD {
251                 self.0
252             }
253         }
254 
255         impl Drop for BIO_METHOD {
256             fn drop(&mut self) {
257                 unsafe {
258                     Box::<ffi::BIO_METHOD>::from_raw(self.0);
259                 }
260             }
261         }
262 
263         #[allow(bad_style)]
264         unsafe fn BIO_set_init(bio: *mut ffi::BIO, init: c_int) {
265             (*bio).init = init;
266         }
267 
268         #[allow(bad_style)]
269         unsafe fn BIO_set_flags(bio: *mut ffi::BIO, flags: c_int) {
270             (*bio).flags = flags;
271         }
272 
273         #[allow(bad_style)]
274         unsafe fn BIO_get_data(bio: *mut ffi::BIO) -> *mut c_void {
275             (*bio).ptr
276         }
277 
278         #[allow(bad_style)]
279         unsafe fn BIO_set_data(bio: *mut ffi::BIO, data: *mut c_void) {
280             (*bio).ptr = data;
281         }
282 
283         #[allow(bad_style)]
284         unsafe fn BIO_set_num(bio: *mut ffi::BIO, num: c_int) {
285             (*bio).num = num;
286         }
287     }
288 }
289