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