tabbyssl/libcrypto/
bio.rs

1/*
2 * Copyright (c) 2019, Yiming Jing
3 * Copyright (c) 2017-2019, The MesaLink Authors
4 * All rights reserved.
5 *
6 * This work is licensed under the terms of the BSD 3-Clause License.
7 * For a copy, see the LICENSE file.
8 *
9 */
10
11use crate::error_san::*;
12use crate::libcrypto::{CRYPTO_FAILURE, CRYPTO_SUCCESS};
13use crate::libssl::err::{InnerResult, OpensslError};
14use crate::{OpaquePointerGuard, MAGIC, MAGIC_SIZE};
15use libc::{c_char, c_int, c_long, c_void};
16use std::{ffi, fs, io, mem, ptr, slice};
17
18// Trait imports
19use std::io::{Read, Seek, Write};
20use std::ops::{Deref, DerefMut};
21#[cfg(unix)]
22use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
23#[cfg(windows)]
24use std::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle};
25
26#[doc(hidden)]
27pub trait BioRW: Read + Write + Seek {}
28impl<T> BioRW for T where T: Read + Write + Seek + ?Sized {}
29
30////////////////////////////////////////////////////
31///
32/// BIO inner for file BIO and MEM bio
33///
34/// ////////////////////////////////////////////////
35
36#[doc(hidden)]
37pub enum MesalinkBioInner<'a> {
38    File(fs::File),
39    Mem(io::Cursor<&'a mut [u8]>),
40    Unspecified,
41}
42
43impl<'a> Deref for MesalinkBioInner<'a> {
44    type Target = dyn BioRW + 'a;
45
46    fn deref(&self) -> &Self::Target {
47        unreachable!()
48    }
49}
50
51impl<'a> DerefMut for MesalinkBioInner<'a> {
52    fn deref_mut(&mut self) -> &mut Self::Target {
53        match self {
54            MesalinkBioInner::File(ref mut f) => f,
55            MesalinkBioInner::Mem(ref mut m) => m,
56            _ => unimplemented!(),
57        }
58    }
59}
60
61/// A structure used for the implementation of new BIO types
62#[allow(non_camel_case_types)]
63#[repr(C)]
64#[derive(PartialEq)]
65pub enum TABBY_BIO_METHOD {
66    File,
67    Mem,
68    Unspecified,
69}
70
71static TABBY_BIO_METHOD_FILE: TABBY_BIO_METHOD = TABBY_BIO_METHOD::File;
72static TABBY_BIO_METHOD_MEM: TABBY_BIO_METHOD = TABBY_BIO_METHOD::Mem;
73
74#[doc(hidden)]
75#[allow(non_camel_case_types)]
76pub struct MesalinkBioFunctions<'a> {
77    pub read: Box<dyn Fn(&mut MesalinkBioInner<'a>, &mut [u8]) -> io::Result<usize>>,
78    pub write: Box<dyn Fn(&mut MesalinkBioInner<'a>, &[u8]) -> io::Result<usize>>,
79    pub gets: Box<dyn Fn(&mut MesalinkBioInner<'a>, &mut [u8]) -> io::Result<usize>>,
80    pub puts: Box<dyn Fn(&mut MesalinkBioInner<'a>, &[u8]) -> io::Result<usize>>,
81}
82
83fn generic_read<'a>(b: &mut MesalinkBioInner<'a>, buf: &mut [u8]) -> io::Result<usize> {
84    b.read(buf)
85}
86
87fn generic_write<'a>(b: &mut MesalinkBioInner<'a>, buf: &[u8]) -> io::Result<usize> {
88    b.write(buf)
89}
90
91fn file_gets<'a>(inner: &mut MesalinkBioInner<'a>, buf: &mut [u8]) -> io::Result<usize> {
92    if buf.is_empty() {
93        return Ok(0);
94    }
95    let file_bytes = if let MesalinkBioInner::File(ref mut f) = inner {
96        f.bytes()
97    } else {
98        return Err(io::Error::new(io::ErrorKind::Other, "BIO not supported"));
99    };
100    let mut pos = 0usize;
101    for byte in file_bytes.take(buf.len() - 1) {
102        let b = byte?;
103        if b == b'\0' || b == b'\n' {
104            break;
105        }
106        buf[pos] = b;
107        pos += 1;
108    }
109    buf[pos] = b'\0';
110    Ok(pos + 1) // include '\0' at the end
111}
112
113fn mem_gets<'a>(inner: &mut MesalinkBioInner<'a>, buf: &mut [u8]) -> io::Result<usize> {
114    if buf.is_empty() {
115        return Ok(0);
116    }
117    let mem_bytes = if let MesalinkBioInner::Mem(ref mut m) = inner {
118        m.bytes()
119    } else {
120        return Err(io::Error::new(io::ErrorKind::Other, "BIO not supported"));
121    };
122    let mut pos = 0usize;
123    for byte in mem_bytes.take(buf.len() - 1) {
124        let b = byte?;
125        if b == b'\0' || b == b'\n' {
126            break;
127        }
128        buf[pos] = b;
129        pos += 1;
130    }
131    buf[pos] = b'\0';
132    Ok(pos + 1) // include '\0' at the end
133}
134
135impl<'a> From<&TABBY_BIO_METHOD> for MesalinkBioFunctions<'a> {
136    fn from(m: &TABBY_BIO_METHOD) -> MesalinkBioFunctions<'a> {
137        let gets = match *m {
138            TABBY_BIO_METHOD::File => file_gets,
139            TABBY_BIO_METHOD::Mem => mem_gets,
140            _ => unimplemented!(),
141        };
142        MesalinkBioFunctions {
143            read: Box::new(generic_read),
144            write: Box::new(generic_write),
145            gets: Box::new(gets),
146            puts: Box::new(generic_write),
147        }
148    }
149}
150
151////////////////////////////////////////////////////
152///
153/// BIO
154///
155/// ////////////////////////////////////////////////
156use bitflags::bitflags;
157bitflags! {
158    #[derive(Default)]
159    struct BioFlags: u32 {
160        const BIO_NOCLOSE = 0x00;
161        const BIO_CLOSE   = 0x01;
162        const BIO_FLAGS_MEM_RDONLY = 0x200;
163        const BIO_FLAGS_NONCLEAR_RST = 0x400;
164    }
165}
166
167/// An I/O abstraction, it hides many of the underlying I/O details from an
168/// application.
169#[allow(non_camel_case_types)]
170pub struct TABBY_BIO<'a> {
171    magic: [u8; MAGIC_SIZE],
172    inner: MesalinkBioInner<'a>,
173    method: MesalinkBioFunctions<'a>,
174    flags: BioFlags,
175}
176
177impl<'a> OpaquePointerGuard for TABBY_BIO<'a> {
178    fn check_magic(&self) -> bool {
179        self.magic == *MAGIC
180    }
181}
182
183impl<'a> TABBY_BIO<'a> {
184    fn is_initialized(&self) -> bool {
185        match self.inner {
186            MesalinkBioInner::File(_) | MesalinkBioInner::Mem(_) => true,
187            _ => false,
188        }
189    }
190}
191
192impl<'a> Read for TABBY_BIO<'a> {
193    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
194        self.inner.read(buf)
195    }
196}
197
198impl<'a> Write for TABBY_BIO<'a> {
199    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
200        self.inner.write(buf)
201    }
202
203    fn flush(&mut self) -> io::Result<()> {
204        self.inner.flush()
205    }
206}
207
208impl<'a> Seek for TABBY_BIO<'a> {
209    fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
210        self.inner.seek(pos)
211    }
212}
213
214/// `BIO_new()` returns a new BIO using method `type`
215///
216/// ```c
217/// #include <tabbyssl/openssl/bio.h>
218///
219/// BIO *BIO_new(BIO_METHOD *type);
220/// ```
221#[no_mangle]
222pub extern "C" fn tabby_BIO_new<'a>(method_ptr: *const TABBY_BIO_METHOD) -> *mut TABBY_BIO<'a> {
223    check_inner_result!(inner_tabby_bio_new(method_ptr), ptr::null_mut())
224}
225
226fn inner_tabby_bio_new<'a>(method_ptr: *const TABBY_BIO_METHOD) -> InnerResult<*mut TABBY_BIO<'a>> {
227    if method_ptr.is_null() {
228        return Err(error!(OpensslError::NullPointer.into()));
229    }
230    if method_ptr != (&TABBY_BIO_METHOD_FILE as *const TABBY_BIO_METHOD)
231        && method_ptr != (&TABBY_BIO_METHOD_MEM as *const TABBY_BIO_METHOD)
232    {
233        return Err(error!(OpensslError::BadFuncArg.into()));
234    }
235    let method = unsafe { &*method_ptr };
236    let bio = TABBY_BIO {
237        magic: *MAGIC,
238        inner: MesalinkBioInner::Unspecified,
239        method: method.into(),
240        flags: BioFlags::BIO_CLOSE,
241    };
242    let bio_ptr = Box::into_raw(Box::new(bio)) as *mut TABBY_BIO<'_>;
243    Ok(bio_ptr)
244}
245
246/// `BIO_free()` frees a BIO
247///
248/// ```c
249/// #include <tabbyssl/openssl/bio.h>
250///
251/// int BIO_free(BIO *a);
252/// ```
253#[no_mangle]
254pub extern "C" fn tabby_BIO_free(bio_ptr: *mut TABBY_BIO<'_>) {
255    let _ = check_inner_result!(inner_tabby_bio_free(bio_ptr), CRYPTO_FAILURE);
256}
257
258fn inner_tabby_bio_free(bio_ptr: *mut TABBY_BIO<'_>) -> InnerResult<c_int> {
259    let _ = sanitize_ptr_for_mut_ref(bio_ptr)?;
260    let mut bio = unsafe { Box::from_raw(bio_ptr) };
261    let inner = mem::replace(&mut bio.inner, MesalinkBioInner::Unspecified);
262    if BioFlags::BIO_NOCLOSE == bio.flags & BioFlags::BIO_NOCLOSE {
263        if let MesalinkBioInner::File(f) = inner {
264            #[cfg(unix)]
265            let _ = f.into_raw_fd();
266            #[cfg(windows)]
267            let _ = f.into_raw_handle();
268        }
269    }
270    Ok(CRYPTO_SUCCESS)
271}
272
273/// `BIO_read` attempts to read *len* bytes from BIO *b* and places the data in
274/// *buf*。
275///
276/// ```c
277/// #include <openssl/bio.h>
278///
279/// int BIO_read(BIO *b, void *buf, int len);
280/// ```
281#[no_mangle]
282pub extern "C" fn tabby_BIO_read(
283    bio_ptr: *mut TABBY_BIO<'_>,
284    buf_ptr: *mut c_void,
285    len: c_int,
286) -> c_int {
287    check_inner_result!(inner_tabby_bio_read(bio_ptr, buf_ptr, len), -1)
288}
289
290fn inner_tabby_bio_read(
291    bio_ptr: *mut TABBY_BIO<'_>,
292    buf_ptr: *mut c_void,
293    len: c_int,
294) -> InnerResult<c_int> {
295    let bio = sanitize_ptr_for_mut_ref(bio_ptr)?;
296    if !bio.is_initialized() {
297        // Mem or file not assigned yet
298        return Err(error!(OpensslError::BadFuncArg.into()));
299    }
300    if buf_ptr.is_null() {
301        return Err(error!(OpensslError::NullPointer.into()));
302    }
303    let buf_ptr = buf_ptr as *mut u8;
304    let mut buf = unsafe { slice::from_raw_parts_mut(buf_ptr, len as usize) };
305    let read_fn = &bio.method.read;
306    let ret = read_fn(&mut bio.inner, &mut buf).map_err(|e| error!(e.into()))?;
307    Ok(ret as c_int)
308}
309
310/// `BIO_gets` attempts to read a line of data from the BIO *b* of maximum
311/// length *len* and places teh data in *buf*.
312/// ```c
313/// #include <openssl/bio.h>
314///
315/// int BIO_gets(BIO *b, char *buf, int size);
316/// ```
317#[no_mangle]
318pub extern "C" fn tabby_BIO_gets(
319    bio_ptr: *mut TABBY_BIO<'_>,
320    buf_ptr: *mut c_char,
321    size: c_int,
322) -> c_int {
323    check_inner_result!(inner_tabby_bio_gets(bio_ptr, buf_ptr, size), -1)
324}
325
326fn inner_tabby_bio_gets(
327    bio_ptr: *mut TABBY_BIO<'_>,
328    buf_ptr: *mut c_char,
329    size: c_int,
330) -> InnerResult<c_int> {
331    let bio = sanitize_ptr_for_mut_ref(bio_ptr)?;
332    if !bio.is_initialized() {
333        // Mem or file not assigned yet
334        return Err(error!(OpensslError::BadFuncArg.into()));
335    }
336    if buf_ptr.is_null() {
337        return Err(error!(OpensslError::NullPointer.into()));
338    }
339    let buf_ptr = buf_ptr as *mut u8;
340    let mut buf = unsafe { slice::from_raw_parts_mut(buf_ptr, size as usize) };
341    let gets_fn = &bio.method.gets;
342    let ret = gets_fn(&mut bio.inner, &mut buf).map_err(|e| error!(e.into()))?;
343    Ok(ret as c_int)
344}
345
346/// `BIO_write` attempts to write *len* bytes from *buf* to BIO *b*.
347///
348/// ```c
349/// #include <openssl/bio.h>
350///
351/// int BIO_write(BIO *b, void *buf, int len);
352/// ```
353#[no_mangle]
354pub extern "C" fn tabby_BIO_write(
355    bio_ptr: *mut TABBY_BIO<'_>,
356    buf_ptr: *const c_void,
357    len: c_int,
358) -> c_int {
359    check_inner_result!(inner_tabby_bio_write(bio_ptr, buf_ptr, len), -1)
360}
361
362fn inner_tabby_bio_write(
363    bio_ptr: *mut TABBY_BIO<'_>,
364    buf_ptr: *const c_void,
365    len: c_int,
366) -> InnerResult<c_int> {
367    let bio = sanitize_ptr_for_mut_ref(bio_ptr)?;
368    if !bio.is_initialized() {
369        // Mem or file not assigned yet
370        return Err(error!(OpensslError::BadFuncArg.into()));
371    }
372    if buf_ptr.is_null() {
373        return Err(error!(OpensslError::NullPointer.into()));
374    }
375    let buf_ptr = buf_ptr as *const u8;
376    let buf = unsafe { slice::from_raw_parts(buf_ptr, len as usize) };
377    let write_fn = &bio.method.write;
378    let ret = write_fn(&mut bio.inner, &buf).map_err(|e| error!(e.into()))?;
379    Ok(ret as c_int)
380}
381
382/// `BIO_puts` attempts to write a null terminated string *buf* to BIO *b*.
383///
384/// ```c
385/// #include <openssl/bio.h>
386///
387/// int BIO_puts(BIO *b, const char *buf);
388/// ```
389#[no_mangle]
390pub extern "C" fn tabby_BIO_puts(bio_ptr: *mut TABBY_BIO<'_>, buf_ptr: *const c_char) -> c_int {
391    check_inner_result!(inner_tabby_bio_puts(bio_ptr, buf_ptr), -1)
392}
393
394fn inner_tabby_bio_puts(bio_ptr: *mut TABBY_BIO<'_>, buf_ptr: *const c_char) -> InnerResult<c_int> {
395    let bio = sanitize_ptr_for_mut_ref(bio_ptr)?;
396    if !bio.is_initialized() {
397        // Mem or file not assigned yet
398        return Err(error!(OpensslError::BadFuncArg.into()));
399    }
400    if buf_ptr.is_null() {
401        return Err(error!(OpensslError::NullPointer.into()));
402    }
403    let strlen = unsafe { libc::strlen(buf_ptr) };
404    let buf_ptr = buf_ptr as *const u8;
405    let buf = unsafe { slice::from_raw_parts(buf_ptr, strlen + 1) };
406    let puts_fn = &bio.method.puts;
407    let ret = puts_fn(&mut bio.inner, &buf).map_err(|e| error!(e.into()))?;
408    Ok(ret as c_int)
409}
410
411/// `BIO_s_file()` returns the BIO file method.
412///
413/// ```c
414/// #include <tabbyssl/openssl/bio.h>
415///
416/// BIO_METHOD *BIO_s_file(void);
417/// ```
418#[no_mangle]
419pub extern "C" fn tabby_BIO_s_file() -> *const TABBY_BIO_METHOD {
420    &TABBY_BIO_METHOD_FILE as *const TABBY_BIO_METHOD
421}
422
423/// `BIO_new_file()` creates a new file BIO with mode mode the meaning of mode
424/// is the same as the stdio function fopen(). The BIO_CLOSE flag is set on the
425/// returned BIO.
426///
427/// ```c
428/// #include <tabbyssl/openssl/bio.h>
429///
430/// BIO *BIO_new_file(const char *filename, const char *mode);
431/// ```
432#[no_mangle]
433pub extern "C" fn tabby_BIO_new_file<'a>(
434    filename_ptr: *const c_char,
435    mode_ptr: *const c_char,
436) -> *mut TABBY_BIO<'a> {
437    check_inner_result!(
438        inner_tabby_bio_new_filename(filename_ptr, mode_ptr),
439        ptr::null_mut()
440    )
441}
442
443fn inner_tabby_bio_new_filename<'a>(
444    filename_ptr: *const c_char,
445    mode_ptr: *const c_char,
446) -> InnerResult<*mut TABBY_BIO<'a>> {
447    let file = open_file_from_filename_and_mode(filename_ptr, mode_ptr)?;
448    let bio = TABBY_BIO {
449        magic: *MAGIC,
450        inner: MesalinkBioInner::File(file),
451        method: (&TABBY_BIO_METHOD_FILE).into(),
452        flags: BioFlags::BIO_CLOSE,
453    };
454    Ok(Box::into_raw(Box::new(bio)) as *mut TABBY_BIO<'_>)
455}
456
457fn open_file_from_filename_and_mode(
458    filename_ptr: *const c_char,
459    mode_ptr: *const c_char,
460) -> InnerResult<fs::File> {
461    if filename_ptr.is_null() || mode_ptr.is_null() {
462        return Err(error!(OpensslError::NullPointer.into()));
463    }
464    let mode = unsafe {
465        ffi::CStr::from_ptr(mode_ptr)
466            .to_str()
467            .map_err(|_| error!(OpensslError::BadFuncArg.into()))?
468    };
469    let mut open_mode = fs::OpenOptions::new();
470    let open_mode = match mode {
471        "r" | "rb" => open_mode.read(true),
472        "w" | "wb" => open_mode.write(true).create(true).truncate(true),
473        "a" | "ab" => open_mode.write(true).create(true).append(true),
474        "r+" | "r+b" | "rb+" => open_mode.read(true).write(true),
475        "w+" | "w+b" | "wb+" => open_mode.read(true).write(true).create(true).truncate(true),
476        "a+" | "a+b" | "ab+" => open_mode.read(true).write(true).create(true).append(true),
477        _ => return Err(error!(OpensslError::BadFuncArg.into())),
478    };
479    let filename = unsafe {
480        ffi::CStr::from_ptr(filename_ptr)
481            .to_str()
482            .map_err(|_| error!(OpensslError::BadFuncArg.into()))?
483    };
484    open_mode.open(filename).map_err(|e| error!(e.into()))
485}
486
487/// `BIO_read_filename()` sets the file BIO b to use file name for reading.
488///
489/// ```c
490/// #include <tabbyssl/openssl/bio.h>
491///
492/// BIO *BIO_read_file(const char *filename);
493/// ```
494#[no_mangle]
495pub extern "C" fn tabby_BIO_read_filename(
496    bio_ptr: *mut TABBY_BIO<'_>,
497    filename_ptr: *const c_char,
498) -> c_int {
499    check_inner_result!(
500        inner_tabby_bio_set_filename(bio_ptr, filename_ptr, b"r\0".as_ptr() as *const c_char),
501        CRYPTO_FAILURE
502    )
503}
504
505fn inner_tabby_bio_set_filename(
506    bio_ptr: *mut TABBY_BIO<'_>,
507    filename_ptr: *const c_char,
508    mode_ptr: *const c_char,
509) -> InnerResult<c_int> {
510    let file = open_file_from_filename_and_mode(filename_ptr, mode_ptr)?;
511    let bio = sanitize_ptr_for_mut_ref(bio_ptr)?;
512    bio.inner = MesalinkBioInner::File(file);
513    Ok(CRYPTO_SUCCESS)
514}
515
516/// `BIO_write_filename()` sets the file BIO b to use file name for writing.
517///
518/// ```c
519/// #include <tabbyssl/openssl/bio.h>
520///
521/// BIO *BIO_write_file(const char *filename);
522/// ```
523#[no_mangle]
524pub extern "C" fn tabby_BIO_write_filename(
525    bio_ptr: *mut TABBY_BIO<'_>,
526    filename_ptr: *const c_char,
527) -> c_int {
528    check_inner_result!(
529        inner_tabby_bio_set_filename(bio_ptr, filename_ptr, b"w\0".as_ptr() as *const c_char),
530        CRYPTO_FAILURE
531    )
532}
533
534/// `BIO_append_filename()` sets the file BIO b to use file name for appending.
535///
536/// ```c
537/// #include <tabbyssl/openssl/bio.h>
538///
539/// BIO *BIO_append_filename(const char *filename);
540/// ```
541#[no_mangle]
542pub extern "C" fn tabby_BIO_append_filename(
543    bio_ptr: *mut TABBY_BIO<'_>,
544    filename_ptr: *const c_char,
545) -> c_int {
546    check_inner_result!(
547        inner_tabby_bio_set_filename(bio_ptr, filename_ptr, b"a\0".as_ptr() as *const c_char),
548        CRYPTO_FAILURE
549    )
550}
551
552/// `BIO_rw_filename()` sets the file BIO b to use file name for reading and
553/// writing.
554///
555/// ```c
556/// #include <tabbyssl/openssl/bio.h>
557///
558/// BIO *BIO_rw_file(const char *filename);
559/// ```
560#[no_mangle]
561pub extern "C" fn tabby_BIO_rw_filename(
562    bio_ptr: *mut TABBY_BIO<'_>,
563    filename_ptr: *const c_char,
564) -> c_int {
565    check_inner_result!(
566        inner_tabby_bio_set_filename(bio_ptr, filename_ptr, b"r+\0".as_ptr() as *const c_char),
567        CRYPTO_FAILURE
568    )
569}
570
571/// `BIO_new_fp()` screates a file BIO wrapping `stream`
572///
573/// ```c
574/// #include <tabbyssl/openssl/bio.h>
575///
576/// BIO *BIO_new_fp(FILE *stream, int flags);
577/// ```
578#[no_mangle]
579pub extern "C" fn tabby_BIO_new_fp<'a>(
580    stream: *mut libc::FILE,
581    flags: c_int,
582) -> *mut TABBY_BIO<'a> {
583    check_inner_result!(inner_tabby_bio_new_fp(stream, flags), ptr::null_mut())
584}
585
586fn inner_tabby_bio_new_fp<'a>(
587    stream: *mut libc::FILE,
588    flags: c_int,
589) -> InnerResult<*mut TABBY_BIO<'a>> {
590    if stream.is_null() {
591        return Err(error!(OpensslError::NullPointer.into()));
592    }
593    let file = unsafe { fs::File::from_file_stream(stream) };
594    let flags = BioFlags::from_bits(flags as u32).ok_or(error!(OpensslError::BadFuncArg.into()))?;
595    let bio = TABBY_BIO {
596        magic: *MAGIC,
597        inner: MesalinkBioInner::File(file),
598        method: (&TABBY_BIO_METHOD_FILE).into(),
599        flags,
600    };
601    Ok(Box::into_raw(Box::new(bio)) as *mut TABBY_BIO<'_>)
602}
603
604/// `BIO_set_fp()` sets the fp of a file BIO to `fp`.
605///
606/// ```c
607/// #include <tabbyssl/openssl/bio.h>
608///
609/// BIO_set_fp(BIO *b,FILE *fp, int flags);
610/// ```
611#[no_mangle]
612pub extern "C" fn tabby_BIO_set_fp(bio_ptr: *mut TABBY_BIO<'_>, fp: *mut libc::FILE, flags: c_int) {
613    let _ = check_inner_result!(inner_tabby_bio_set_fp(bio_ptr, fp, flags), CRYPTO_FAILURE);
614}
615
616fn inner_tabby_bio_set_fp(
617    bio_ptr: *mut TABBY_BIO<'_>,
618    fp: *mut libc::FILE,
619    flags: c_int,
620) -> InnerResult<c_int> {
621    let bio = sanitize_ptr_for_mut_ref(bio_ptr)?;
622    let file = unsafe { fs::File::from_file_stream(fp) };
623    let flags = BioFlags::from_bits(flags as u32).ok_or(error!(OpensslError::BadFuncArg.into()))?;
624    bio.inner = MesalinkBioInner::File(file);
625    bio.flags = flags;
626    Ok(CRYPTO_SUCCESS)
627}
628
629/// `BIO_get_close()` returns the BIOs close flag.
630///
631/// ```c
632/// #include <tabbyssl/openssl/bio.h>
633///
634/// int BIO_get_close(BIO *b);
635/// ```
636#[no_mangle]
637pub extern "C" fn tabby_BIO_get_close(bio_ptr: *mut TABBY_BIO<'_>) -> c_int {
638    check_inner_result!(
639        inner_tabby_bio_get_close(bio_ptr),
640        BioFlags::default().bits() as c_int
641    )
642}
643
644fn inner_tabby_bio_get_close(bio_ptr: *mut TABBY_BIO<'_>) -> InnerResult<c_int> {
645    let bio = sanitize_ptr_for_mut_ref(bio_ptr)?;
646    Ok(bio.flags.bits() as c_int)
647}
648
649/// `BIO_set_close()` sets the BIO *b* close flag to *flag*
650///
651/// ```c
652/// #include <tabbyssl/openssl/bio.h>
653///
654/// int BIO_set_close(BIO *b, long flag);
655/// ```
656#[no_mangle]
657pub extern "C" fn tabby_BIO_set_close(bio_ptr: *mut TABBY_BIO<'_>, flag: c_long) -> c_int {
658    let _ = check_inner_result!(
659        inner_tabby_bio_set_close(bio_ptr, flag),
660        BioFlags::default().bits() as c_int
661    );
662    CRYPTO_SUCCESS
663}
664
665fn inner_tabby_bio_set_close(bio_ptr: *mut TABBY_BIO<'_>, flag: c_long) -> InnerResult<c_int> {
666    let bio = sanitize_ptr_for_mut_ref(bio_ptr)?;
667    let flag = BioFlags::from_bits(flag as u32).ok_or(error!(OpensslError::BadFuncArg.into()))?;
668    bio.flags = flag;
669    Ok(CRYPTO_SUCCESS)
670}
671
672/// `BIO_s_file()` returns the BIO memory method.
673///
674/// ```c
675/// #include <tabbyssl/openssl/bio.h>
676///
677/// BIO_METHOD *BIO_s_mem(void);
678/// ```
679#[no_mangle]
680pub extern "C" fn tabby_BIO_s_mem() -> *const TABBY_BIO_METHOD {
681    &TABBY_BIO_METHOD_MEM as *const TABBY_BIO_METHOD
682}
683
684/// `BIO_new_mem_buf()` creates a memory BIO using `len` bytes of data at `buf`
685///
686/// ```c
687/// #include <tabbyssl/openssl/bio.h>
688///
689/// BIO *BIO_new_mem_buf(const void *buf, int len);
690/// ```
691#[no_mangle]
692pub extern "C" fn tabby_BIO_new_mem_buf<'a>(
693    buf_ptr: *mut c_void,
694    len: c_int,
695) -> *mut TABBY_BIO<'a> {
696    if buf_ptr.is_null() {
697        return ptr::null_mut();
698    }
699    let buflen = if len < 0 {
700        unsafe { libc::strlen(buf_ptr as *const c_char) }
701    } else {
702        len as usize
703    };
704    let buf_ptr = buf_ptr as *mut u8;
705    let buf = unsafe { slice::from_raw_parts_mut(buf_ptr, buflen) };
706    let bio = TABBY_BIO {
707        magic: *MAGIC,
708        inner: MesalinkBioInner::Mem(io::Cursor::new(buf)),
709        method: (&TABBY_BIO_METHOD_MEM).into(),
710        flags: BioFlags::default(), // TODO: support BIO_FLAGS_MEM_RDONLY
711    };
712    Box::into_raw(Box::new(bio)) as *mut TABBY_BIO<'_>
713}
714
715/// Helper trait for converting from FILE* in libc.
716pub(crate) trait FromFileStream {
717    unsafe fn from_file_stream(stream: *mut libc::FILE) -> Self;
718}
719
720#[cfg(unix)]
721impl<T: FromRawFd> FromFileStream for T {
722    unsafe fn from_file_stream(stream: *mut libc::FILE) -> Self {
723        Self::from_raw_fd(libc::fileno(stream))
724    }
725}
726
727#[cfg(windows)]
728impl<T: FromRawHandle> FromFileStream for T {
729    unsafe fn from_file_stream(stream: *mut libc::FILE) -> Self {
730        let fd = libc::fileno(stream);
731        let osf_handle = libc::get_osfhandle(fd);
732        Self::from_raw_handle(osf_handle as *mut _)
733    }
734}
735
736/// Helper trait for converting to FILE* in libc.
737pub(crate) trait OpenFileStream {
738    unsafe fn open_file_stream_r(&self) -> *mut libc::FILE;
739
740    unsafe fn open_file_stream_w(&self) -> *mut libc::FILE;
741}
742
743#[cfg(unix)]
744impl<T: AsRawFd> OpenFileStream for T {
745    unsafe fn open_file_stream_r(&self) -> *mut libc::FILE {
746        libc::fdopen(self.as_raw_fd(), b"r\0".as_ptr() as *const c_char)
747    }
748
749    unsafe fn open_file_stream_w(&self) -> *mut libc::FILE {
750        libc::fdopen(self.as_raw_fd(), b"w\0".as_ptr() as *const c_char)
751    }
752}
753
754#[cfg(windows)]
755impl<T: AsRawHandle> OpenFileStream for T {
756    unsafe fn open_file_stream_r(&self) -> *mut libc::FILE {
757        let handle = self.as_raw_handle();
758        match libc::open_osfhandle(handle as libc::intptr_t, 0) {
759            -1 => ptr::null_mut(),
760            fd => libc::fdopen(fd, b"r\0".as_ptr() as *const c_char),
761        }
762    }
763
764    unsafe fn open_file_stream_w(&self) -> *mut libc::FILE {
765        let handle = self.as_raw_handle();
766        match libc::open_osfhandle(handle as libc::intptr_t, 0) {
767            -1 => ptr::null_mut(),
768            fd => libc::fdopen(fd, b"w\0".as_ptr() as *const c_char),
769        }
770    }
771}
772
773#[cfg(test)]
774mod tests {
775    use super::*;
776    use libc::{self, c_char, c_void};
777    use std::fs;
778
779    #[test]
780    fn bio_methods() {
781        assert_ne!(tabby_BIO_s_file(), ptr::null());
782        assert_ne!(tabby_BIO_s_mem(), ptr::null());
783    }
784
785    #[test]
786    fn bio_create_from_method() {
787        let bio_ptr_f = tabby_BIO_new(tabby_BIO_s_mem());
788        assert_ne!(bio_ptr_f, ptr::null_mut());
789        tabby_BIO_free(bio_ptr_f);
790        let bio_ptr_m = tabby_BIO_new(tabby_BIO_s_file());
791        assert_ne!(bio_ptr_m, ptr::null_mut());
792        tabby_BIO_free(bio_ptr_m);
793    }
794
795    #[test]
796    fn bio_null_ptr() {
797        let bio_ptr = tabby_BIO_new(ptr::null());
798        assert_eq!(bio_ptr, ptr::null_mut());
799
800        let invalid_method_ptr = "hello".as_ptr() as *const TABBY_BIO_METHOD;
801        let bio_ptr = tabby_BIO_new(invalid_method_ptr);
802        assert_eq!(bio_ptr, ptr::null_mut());
803    }
804
805    #[test]
806    fn bio_uninitialized() {
807        let bio_ptr = tabby_BIO_new(tabby_BIO_s_mem());
808        let buf_ptr = [0u8; 32].as_ptr() as *mut c_void;
809        let len = tabby_BIO_read(bio_ptr, buf_ptr, 32);
810        assert_eq!(-1, len);
811        let len = tabby_BIO_write(bio_ptr, buf_ptr, 32);
812        assert_eq!(-1, len);
813        let buf_ptr = buf_ptr as *mut c_char;
814        let len = tabby_BIO_gets(bio_ptr, buf_ptr, 32);
815        assert_eq!(-1, len);
816        let len = tabby_BIO_puts(bio_ptr, buf_ptr);
817        assert_eq!(-1, len);
818        tabby_BIO_free(bio_ptr);
819    }
820
821    #[test]
822    fn bio_null_buf() {
823        let bio_ptr = tabby_BIO_new_mem_buf(ptr::null_mut(), 10);
824        assert_eq!(bio_ptr, ptr::null_mut());
825        let bio_ptr = tabby_BIO_new_mem_buf(b"hello\0".as_ptr() as *mut c_void, -1);
826        let buf_ptr = ptr::null_mut() as *mut c_void;
827        let len = tabby_BIO_read(bio_ptr, buf_ptr, 5);
828        assert_eq!(-1, len);
829        let len = tabby_BIO_write(bio_ptr, buf_ptr, 5);
830        assert_eq!(-1, len);
831        let buf_ptr = buf_ptr as *mut c_char;
832        let len = tabby_BIO_gets(bio_ptr, buf_ptr, 5);
833        assert_eq!(-1, len);
834        let len = tabby_BIO_puts(bio_ptr, buf_ptr);
835        assert_eq!(-1, len);
836        tabby_BIO_free(bio_ptr);
837    }
838
839    #[test]
840    fn bio_mem() {
841        let buf = [0u8; 10];
842        let bio_ptr_m = tabby_BIO_new_mem_buf(buf.as_ptr() as *mut c_void, 10);
843        assert_ne!(bio_ptr_m, ptr::null_mut());
844        let src = [1u8, 2, 3, 4, 5];
845        let ret = tabby_BIO_write(bio_ptr_m, src.as_ptr() as *const c_void, 5);
846        assert_eq!(ret, 5);
847        tabby_BIO_free(bio_ptr_m);
848
849        let buf = [1u8, 2, 3, 4, 5];
850        let bio_ptr_m = tabby_BIO_new_mem_buf(buf.as_ptr() as *mut c_void, 5);
851        let dst = [0u8; 10];
852        let ret = tabby_BIO_read(bio_ptr_m, dst.as_ptr() as *mut c_void, 5);
853        assert_eq!(ret, 5);
854        assert_eq!(dst, [1u8, 2, 3, 4, 5, 0, 0, 0, 0, 0]);
855        tabby_BIO_free(bio_ptr_m);
856
857        let buf = [0u8; 10];
858        let bio_ptr_m = tabby_BIO_new_mem_buf(buf.as_ptr() as *mut c_void, 10);
859        assert_ne!(bio_ptr_m, ptr::null_mut());
860        let src = b"hello\0";
861        let ret = tabby_BIO_puts(bio_ptr_m, src.as_ptr() as *const c_char);
862        assert_eq!(ret, 6);
863        tabby_BIO_free(bio_ptr_m);
864
865        let buf = [1u8, 2, 0, 4, 5];
866        let bio_ptr_m = tabby_BIO_new_mem_buf(buf.as_ptr() as *mut c_void, 5);
867        assert_ne!(bio_ptr_m, ptr::null_mut());
868        let dst = [0u8; 5];
869        let ret = tabby_BIO_gets(bio_ptr_m, dst.as_ptr() as *mut c_char, 5);
870        assert_eq!(ret, 3);
871        assert_eq!(dst, [1u8, 2, 0, 0, 0]);
872        tabby_BIO_free(bio_ptr_m);
873    }
874
875    #[test]
876    fn bio_file_new_fp() {
877        let bio_ptr_f = tabby_BIO_new_fp(ptr::null_mut(), 0);
878        assert_eq!(bio_ptr_f, ptr::null_mut());
879
880        let file = fs::File::open("tests/ca.cert").unwrap(); // Read-only, "r"
881        let fp = unsafe { file.open_file_stream_r() };
882        assert_ne!(fp, ptr::null_mut());
883
884        let bio_ptr_f = tabby_BIO_new_fp(fp, 0);
885        assert_ne!(bio_ptr_f, ptr::null_mut());
886        tabby_BIO_free(bio_ptr_f);
887    }
888
889    #[test]
890    fn bio_file_set_fp() {
891        let file = fs::File::open("tests/ca.cert").unwrap(); // Read-only, "r"
892        let fp = unsafe { file.open_file_stream_r() };
893        assert_ne!(fp, ptr::null_mut());
894
895        let bio_ptr_f = tabby_BIO_new(tabby_BIO_s_file());
896        assert_ne!(bio_ptr_f, ptr::null_mut());
897        assert_eq!(0x1, tabby_BIO_get_close(bio_ptr_f)); // BIO_CLOSE by default
898        tabby_BIO_set_fp(bio_ptr_f, fp, 0);
899        assert_eq!(0x0, tabby_BIO_get_close(bio_ptr_f)); // BIO_NOCLOSE after set_fp
900        assert_eq!(CRYPTO_SUCCESS, tabby_BIO_set_close(bio_ptr_f, 0x0));
901        let buf = [0u8; 1024];
902        let ret = tabby_BIO_gets(bio_ptr_f, buf.as_ptr() as *mut c_char, 1024);
903        assert_eq!(ret, 28); // gets returns the first line
904        tabby_BIO_free(bio_ptr_f);
905    }
906
907    #[test]
908    fn bio_file_new_from_path() {
909        let path_ptr = b"tests/deleteme\0".as_ptr() as *const c_char;
910
911        let bio_ptr_f = tabby_BIO_new(tabby_BIO_s_file());
912        assert_ne!(bio_ptr_f, ptr::null_mut());
913
914        let ret = tabby_BIO_write_filename(bio_ptr_f, path_ptr);
915        assert_eq!(ret, CRYPTO_SUCCESS);
916
917        let ret = tabby_BIO_rw_filename(bio_ptr_f, path_ptr);
918        assert_eq!(ret, CRYPTO_SUCCESS);
919
920        let ret = tabby_BIO_read_filename(bio_ptr_f, path_ptr);
921        assert_eq!(ret, CRYPTO_SUCCESS);
922
923        let ret = tabby_BIO_append_filename(bio_ptr_f, path_ptr);
924        assert_eq!(ret, CRYPTO_SUCCESS);
925
926        tabby_BIO_free(bio_ptr_f);
927        let _ = fs::remove_file("tests/deleteme");
928    }
929}