Coverage Report

Created: 2024-04-26 06:25

/rust/registry/src/index.crates.io-6f17d22bba15001f/openssl-0.10.62/src/ssl/bio.rs
Line
Count
Source (jump to first uncovered line)
1
use cfg_if::cfg_if;
2
use 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
};
6
use libc::{c_char, c_int, c_long, c_void, strlen};
7
use std::any::Any;
8
use std::io;
9
use std::io::prelude::*;
10
use std::panic::{catch_unwind, AssertUnwindSafe};
11
use std::ptr;
12
use std::slice;
13
14
use crate::cvt_p;
15
use crate::error::ErrorStack;
16
17
pub 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`
25
pub struct BioMethod(BIO_METHOD);
26
27
impl BioMethod {
28
0
    fn new<S: Read + Write>() -> Result<BioMethod, ErrorStack> {
29
0
        BIO_METHOD::new::<S>().map(BioMethod)
30
0
    }
31
}
32
33
unsafe impl Sync for BioMethod {}
34
unsafe impl Send for BioMethod {}
35
36
0
pub fn new<S: Read + Write>(stream: S) -> Result<(*mut BIO, BioMethod), ErrorStack> {
37
0
    let method = BioMethod::new::<S>()?;
38
39
0
    let state = Box::new(StreamState {
40
0
        stream,
41
0
        error: None,
42
0
        panic: None,
43
0
        dtls_mtu_size: 0,
44
0
    });
45
46
    unsafe {
47
0
        let bio = cvt_p(BIO_new(method.0.get()))?;
48
0
        BIO_set_data(bio, Box::into_raw(state) as *mut _);
49
0
        BIO_set_init(bio, 1);
50
0
51
0
        Ok((bio, method))
52
    }
53
0
}
54
55
0
pub unsafe fn take_error<S>(bio: *mut BIO) -> Option<io::Error> {
56
0
    let state = state::<S>(bio);
57
0
    state.error.take()
58
0
}
59
60
0
pub unsafe fn take_panic<S>(bio: *mut BIO) -> Option<Box<dyn Any + Send>> {
61
0
    let state = state::<S>(bio);
62
0
    state.panic.take()
63
0
}
64
65
0
pub unsafe fn get_ref<'a, S: 'a>(bio: *mut BIO) -> &'a S {
66
0
    let state = &*(BIO_get_data(bio) as *const StreamState<S>);
67
0
    &state.stream
68
0
}
69
70
0
pub unsafe fn get_mut<'a, S: 'a>(bio: *mut BIO) -> &'a mut S {
71
0
    &mut state(bio).stream
72
0
}
73
74
0
pub unsafe fn set_dtls_mtu_size<S>(bio: *mut BIO, mtu_size: usize) {
75
0
    if mtu_size as u64 > c_long::max_value() as u64 {
76
0
        panic!(
77
0
            "Given MTU size {} can't be represented in a positive `c_long` range",
78
0
            mtu_size
79
0
        )
80
0
    }
81
0
    state::<S>(bio).dtls_mtu_size = mtu_size as c_long;
82
0
}
83
84
0
unsafe fn state<'a, S: 'a>(bio: *mut BIO) -> &'a mut StreamState<S> {
85
0
    &mut *(BIO_get_data(bio) as *mut _)
86
0
}
87
88
0
unsafe extern "C" fn bwrite<S: Write>(bio: *mut BIO, buf: *const c_char, len: c_int) -> c_int {
89
0
    BIO_clear_retry_flags(bio);
90
0
91
0
    let state = state::<S>(bio);
92
0
    let buf = slice::from_raw_parts(buf as *const _, len as usize);
93
0
94
0
    match catch_unwind(AssertUnwindSafe(|| state.stream.write(buf))) {
95
0
        Ok(Ok(len)) => len as c_int,
96
0
        Ok(Err(err)) => {
97
0
            if retriable_error(&err) {
98
0
                BIO_set_retry_write(bio);
99
0
            }
100
0
            state.error = Some(err);
101
0
            -1
102
        }
103
0
        Err(err) => {
104
0
            state.panic = Some(err);
105
0
            -1
106
        }
107
    }
108
0
}
109
110
0
unsafe extern "C" fn bread<S: Read>(bio: *mut BIO, buf: *mut c_char, len: c_int) -> c_int {
111
0
    BIO_clear_retry_flags(bio);
112
0
113
0
    let state = state::<S>(bio);
114
0
    let buf = slice::from_raw_parts_mut(buf as *mut _, len as usize);
115
0
116
0
    match catch_unwind(AssertUnwindSafe(|| state.stream.read(buf))) {
117
0
        Ok(Ok(len)) => len as c_int,
118
0
        Ok(Err(err)) => {
119
0
            if retriable_error(&err) {
120
0
                BIO_set_retry_read(bio);
121
0
            }
122
0
            state.error = Some(err);
123
0
            -1
124
        }
125
0
        Err(err) => {
126
0
            state.panic = Some(err);
127
0
            -1
128
        }
129
    }
130
0
}
131
132
#[allow(clippy::match_like_matches_macro)] // matches macro requires rust 1.42.0
133
0
fn retriable_error(err: &io::Error) -> bool {
134
0
    match err.kind() {
135
0
        io::ErrorKind::WouldBlock | io::ErrorKind::NotConnected => true,
136
0
        _ => false,
137
    }
138
0
}
139
140
0
unsafe extern "C" fn bputs<S: Write>(bio: *mut BIO, s: *const c_char) -> c_int {
141
0
    bwrite::<S>(bio, s, strlen(s) as c_int)
142
0
}
143
144
0
unsafe extern "C" fn ctrl<S: Write>(
145
0
    bio: *mut BIO,
146
0
    cmd: c_int,
147
0
    _num: c_long,
148
0
    _ptr: *mut c_void,
149
0
) -> c_long {
150
0
    let state = state::<S>(bio);
151
0
152
0
    if cmd == BIO_CTRL_FLUSH {
153
0
        match catch_unwind(AssertUnwindSafe(|| state.stream.flush())) {
154
0
            Ok(Ok(())) => 1,
155
0
            Ok(Err(err)) => {
156
0
                state.error = Some(err);
157
0
                0
158
            }
159
0
            Err(err) => {
160
0
                state.panic = Some(err);
161
0
                0
162
            }
163
        }
164
0
    } else if cmd == BIO_CTRL_DGRAM_QUERY_MTU {
165
0
        state.dtls_mtu_size
166
    } else {
167
0
        0
168
    }
169
0
}
170
171
0
unsafe extern "C" fn create(bio: *mut BIO) -> c_int {
172
0
    BIO_set_init(bio, 0);
173
0
    BIO_set_num(bio, 0);
174
0
    BIO_set_data(bio, ptr::null_mut());
175
0
    BIO_set_flags(bio, 0);
176
0
    1
177
0
}
178
179
0
unsafe extern "C" fn destroy<S>(bio: *mut BIO) -> c_int {
180
0
    if bio.is_null() {
181
0
        return 0;
182
0
    }
183
0
184
0
    let data = BIO_get_data(bio);
185
0
    assert!(!data.is_null());
186
0
    let _ = Box::<StreamState<S>>::from_raw(data as *mut _);
187
0
    BIO_set_data(bio, ptr::null_mut());
188
0
    BIO_set_init(bio, 0);
189
0
    1
190
0
}
191
192
cfg_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
0
        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
0
            fn new<S: Read + Write>() -> Result<BIO_METHOD, ErrorStack> {
205
                unsafe {
206
0
                    let ptr = cvt_p(ffi::BIO_meth_new(ffi::BIO_TYPE_NONE, b"rust\0".as_ptr() as *const _))?;
207
0
                    let method = BIO_METHOD(ptr);
208
0
                    cvt(ffi::BIO_meth_set_write__fixed_rust(method.0, Some(bwrite::<S>)))?;
209
0
                    cvt(ffi::BIO_meth_set_read__fixed_rust(method.0, Some(bread::<S>)))?;
210
0
                    cvt(ffi::BIO_meth_set_puts__fixed_rust(method.0, Some(bputs::<S>)))?;
211
0
                    cvt(ffi::BIO_meth_set_ctrl__fixed_rust(method.0, Some(ctrl::<S>)))?;
212
0
                    cvt(ffi::BIO_meth_set_create__fixed_rust(method.0, Some(create)))?;
213
0
                    cvt(ffi::BIO_meth_set_destroy__fixed_rust(method.0, Some(destroy::<S>)))?;
214
0
                    Ok(method)
215
                }
216
0
            }
217
218
0
            fn get(&self) -> *mut ffi::BIO_METHOD {
219
0
                self.0
220
0
            }
221
        }
222
223
        impl Drop for BIO_METHOD {
224
0
            fn drop(&mut self) {
225
0
                unsafe {
226
0
                    ffi::BIO_meth_free(self.0);
227
0
                }
228
0
            }
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
}