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