1 // Copyright (c) 2023 Huawei Device Co., Ltd.
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13
14 use core::any::Any;
15 use core::marker::PhantomData;
16 use core::panic::AssertUnwindSafe;
17 use core::{ptr, slice};
18 use std::io::{self, Read, Write};
19 use std::panic::catch_unwind;
20
21 use libc::{c_char, c_int, c_long, c_void, strlen};
22
23 use super::error::ErrorStack;
24 use super::ffi::bio::{
25 BIO_clear_flags, BIO_free_all, BIO_get_data, BIO_meth_free, BIO_meth_new, BIO_meth_set_create,
26 BIO_meth_set_ctrl, BIO_meth_set_destroy, BIO_meth_set_puts, BIO_meth_set_read,
27 BIO_meth_set_write, BIO_new, BIO_new_mem_buf, BIO_set_data, BIO_set_flags, BIO_set_init, BIO,
28 BIO_METHOD,
29 };
30 use super::{check_ptr, ssl_init};
31
32 #[derive(Debug)]
33 pub struct Bio(*mut BIO);
34
35 impl Drop for Bio {
drop(&mut self)36 fn drop(&mut self) {
37 unsafe {
38 BIO_free_all(self.0);
39 }
40 }
41 }
42
43 #[derive(Debug)]
44 pub struct BioSlice<'a>(*mut BIO, PhantomData<&'a [u8]>);
45
46 impl<'a> BioSlice<'a> {
from_byte(buf: &'a [u8]) -> Result<BioSlice<'a>, ErrorStack>47 pub(crate) fn from_byte(buf: &'a [u8]) -> Result<BioSlice<'a>, ErrorStack> {
48 unsafe {
49 ssl_init();
50 let bio = check_ptr(BIO_new_mem_buf(
51 buf.as_ptr() as *const _,
52 buf.len() as c_int,
53 ))?;
54 Ok(BioSlice(bio, PhantomData))
55 }
56 }
57
as_ptr(&self) -> *mut BIO58 pub(crate) fn as_ptr(&self) -> *mut BIO {
59 self.0
60 }
61 }
62
63 impl<'a> Drop for BioSlice<'a> {
drop(&mut self)64 fn drop(&mut self) {
65 unsafe { BIO_free_all(self.0) }
66 }
67 }
68
69 const BIO_TYPE_NONE: c_int = 0;
70
71 const BIO_CTRL_FLUSH: c_int = 11;
72 const BIO_CTRL_DGRAM_QUERY: c_int = 40;
73
74 const BIO_FLAGS_READ: c_int = 0x01;
75 const BIO_FLAGS_WRITE: c_int = 0x02;
76 const BIO_FLAGS_IO_SPECIAL: c_int = 0x04;
77 const BIO_FLAGS_SHOULD_RETRY: c_int = 0x08;
78 const BIO_FLAGS_RWS: c_int = BIO_FLAGS_READ | BIO_FLAGS_WRITE | BIO_FLAGS_IO_SPECIAL;
79
80 #[derive(Debug)]
81 pub struct BioMethodInner(*mut BIO_METHOD);
82
83 impl BioMethodInner {
new<S: Read + Write>() -> Result<BioMethodInner, ErrorStack>84 fn new<S: Read + Write>() -> Result<BioMethodInner, ErrorStack> {
85 unsafe {
86 let ptr = check_ptr(BIO_meth_new(BIO_TYPE_NONE, b"rust\0".as_ptr() as *const _))?;
87 let bio_method = BioMethodInner(ptr);
88
89 BIO_meth_set_write(ptr, bwrite::<S>);
90 BIO_meth_set_read(ptr, bread::<S>);
91 BIO_meth_set_puts(ptr, bputs::<S>);
92 BIO_meth_set_ctrl(ptr, ctrl::<S>);
93 BIO_meth_set_create(ptr, create);
94 BIO_meth_set_destroy(ptr, destroy::<S>);
95
96 Ok(bio_method)
97 }
98 }
99
get(&self) -> *mut BIO_METHOD100 fn get(&self) -> *mut BIO_METHOD {
101 self.0
102 }
103 }
104
105 unsafe impl Sync for BioMethod {}
106 unsafe impl Send for BioMethod {}
107
108 impl Drop for BioMethodInner {
drop(&mut self)109 fn drop(&mut self) {
110 unsafe { BIO_meth_free(self.0) }
111 }
112 }
113
114 #[derive(Debug)]
115 pub struct BioMethod(BioMethodInner);
116
117 impl BioMethod {
new<S: Read + Write>() -> Result<BioMethod, ErrorStack>118 fn new<S: Read + Write>() -> Result<BioMethod, ErrorStack> {
119 let method = BioMethodInner::new::<S>()?;
120 Ok(BioMethod(method))
121 }
122
get(&self) -> *mut BIO_METHOD123 fn get(&self) -> *mut BIO_METHOD {
124 self.0.get()
125 }
126 }
127
128 pub(crate) struct StreamState<S> {
129 pub(crate) stream: S,
130 pub(crate) error: Option<io::Error>,
131 pub(crate) panic: Option<Box<dyn Any + Send>>,
132 pub(crate) dtls_mtu_size: c_long,
133 }
get_state<'a, S: 'a>(bio: *mut BIO) -> &'a mut StreamState<S>134 unsafe fn get_state<'a, S: 'a>(bio: *mut BIO) -> &'a mut StreamState<S> {
135 &mut *(BIO_get_data(bio) as *mut _)
136 }
137
get_error<S>(bio: *mut BIO) -> Option<io::Error>138 pub(crate) unsafe fn get_error<S>(bio: *mut BIO) -> Option<io::Error> {
139 let state = get_state::<S>(bio);
140 state.error.take()
141 }
142
get_panic<S>(bio: *mut BIO) -> Option<Box<dyn Any + Send>>143 pub(crate) unsafe fn get_panic<S>(bio: *mut BIO) -> Option<Box<dyn Any + Send>> {
144 let state = get_state::<S>(bio);
145 state.panic.take()
146 }
147
get_stream_ref<'a, S: 'a>(bio: *mut BIO) -> &'a S148 pub(crate) unsafe fn get_stream_ref<'a, S: 'a>(bio: *mut BIO) -> &'a S {
149 let state: &'a StreamState<S> = &*(BIO_get_data(bio) as *const StreamState<S>);
150 &state.stream
151 }
152
get_stream_mut<'a, S: 'a>(bio: *mut BIO) -> &'a mut S153 pub(crate) unsafe fn get_stream_mut<'a, S: 'a>(bio: *mut BIO) -> &'a mut S {
154 &mut get_state(bio).stream
155 }
156
new<S: Read + Write>(stream: S) -> Result<(*mut BIO, BioMethod), ErrorStack>157 pub(crate) fn new<S: Read + Write>(stream: S) -> Result<(*mut BIO, BioMethod), ErrorStack> {
158 let bio_method = BioMethod::new::<S>()?;
159
160 let stream_state = Box::new(StreamState {
161 stream,
162 error: None,
163 panic: None,
164 dtls_mtu_size: 0,
165 });
166
167 unsafe {
168 let bio = check_ptr(BIO_new(bio_method.get()))?;
169 BIO_set_data(bio, Box::into_raw(stream_state) as *mut _);
170 BIO_set_init(bio, 1);
171
172 Ok((bio, bio_method))
173 }
174 }
175
retry_error(err: &io::Error) -> bool176 fn retry_error(err: &io::Error) -> bool {
177 matches!(
178 err.kind(),
179 io::ErrorKind::WouldBlock | io::ErrorKind::NotConnected
180 )
181 }
182
ctrl<S: Write>( bio: *mut BIO, ctrl_cmd: c_int, _num: c_long, _ptr: *mut c_void, ) -> c_long183 unsafe extern "C" fn ctrl<S: Write>(
184 bio: *mut BIO,
185 ctrl_cmd: c_int,
186 _num: c_long,
187 _ptr: *mut c_void,
188 ) -> c_long {
189 let state = get_state::<S>(bio);
190
191 if ctrl_cmd == BIO_CTRL_FLUSH {
192 match catch_unwind(AssertUnwindSafe(|| state.stream.flush())) {
193 Ok(Err(err)) => {
194 state.error = Some(err);
195 0
196 }
197 Ok(Ok(())) => 1,
198 Err(err) => {
199 state.panic = Some(err);
200 0
201 }
202 }
203 } else if ctrl_cmd == BIO_CTRL_DGRAM_QUERY {
204 state.dtls_mtu_size
205 } else {
206 0
207 }
208 }
209
210 #[allow(non_snake_case)]
BIO_set_num(_bio: *mut BIO, _num: c_int)211 unsafe fn BIO_set_num(_bio: *mut BIO, _num: c_int) {}
212
create(bio: *mut BIO) -> c_int213 unsafe extern "C" fn create(bio: *mut BIO) -> c_int {
214 BIO_set_init(bio, 0);
215 BIO_set_flags(bio, 0);
216 BIO_set_num(bio, 0);
217 BIO_set_data(bio, ptr::null_mut());
218 1
219 }
220
destroy<S>(bio: *mut BIO) -> c_int221 unsafe extern "C" fn destroy<S>(bio: *mut BIO) -> c_int {
222 if bio.is_null() {
223 return 0;
224 }
225 let data = BIO_get_data(bio);
226 drop(Box::<StreamState<S>>::from_raw(data as *mut _));
227 BIO_set_init(bio, 0);
228 BIO_set_data(bio, ptr::null_mut());
229 1
230 }
231
bwrite<S: Write>(bio: *mut BIO, buf: *const c_char, len: c_int) -> c_int232 unsafe extern "C" fn bwrite<S: Write>(bio: *mut BIO, buf: *const c_char, len: c_int) -> c_int {
233 BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY | BIO_FLAGS_RWS);
234
235 let state = get_state::<S>(bio);
236 if len < 0 {
237 state.error = Some(io::Error::from(io::ErrorKind::InvalidInput));
238 return -1;
239 }
240
241 let buf = slice::from_raw_parts(buf as *const _, len as usize);
242
243 match catch_unwind(AssertUnwindSafe(|| state.stream.write(buf))) {
244 Ok(Err(err)) => {
245 if retry_error(&err) {
246 BIO_set_flags(bio, BIO_FLAGS_SHOULD_RETRY | BIO_FLAGS_WRITE)
247 }
248 state.error = Some(err);
249 -1
250 }
251 Ok(Ok(len)) => len as c_int,
252 Err(err) => {
253 state.panic = Some(err);
254 -1
255 }
256 }
257 }
258
bread<S: Read>(bio: *mut BIO, buf: *mut c_char, len: c_int) -> c_int259 unsafe extern "C" fn bread<S: Read>(bio: *mut BIO, buf: *mut c_char, len: c_int) -> c_int {
260 BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY | BIO_FLAGS_RWS);
261
262 let state = get_state::<S>(bio);
263 let buf = slice::from_raw_parts_mut(buf as *mut _, len as usize);
264
265 match catch_unwind(AssertUnwindSafe(|| state.stream.read(buf))) {
266 Ok(Err(err)) => {
267 if retry_error(&err) {
268 BIO_set_flags(bio, BIO_FLAGS_SHOULD_RETRY | BIO_FLAGS_READ)
269 }
270 state.error = Some(err);
271 -1
272 }
273 Ok(Ok(len)) => len as c_int,
274 Err(err) => {
275 state.panic = Some(err);
276 -1
277 }
278 }
279 }
280
bputs<S: Write>(bio: *mut BIO, buf: *const c_char) -> c_int281 unsafe extern "C" fn bputs<S: Write>(bio: *mut BIO, buf: *const c_char) -> c_int {
282 bwrite::<S>(bio, buf, strlen(buf) as c_int)
283 }
284