diff options
Diffstat (limited to 'third_party/rust/neqo-crypto/src/result.rs')
-rw-r--r-- | third_party/rust/neqo-crypto/src/result.rs | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/third_party/rust/neqo-crypto/src/result.rs b/third_party/rust/neqo-crypto/src/result.rs new file mode 100644 index 0000000000..e6148dd054 --- /dev/null +++ b/third_party/rust/neqo-crypto/src/result.rs @@ -0,0 +1,133 @@ +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use crate::err::{ + nspr, Error, PR_ErrorToName, PR_ErrorToString, PR_GetError, Res, PR_LANGUAGE_I_DEFAULT, +}; +use crate::ssl; + +use std::ffi::CStr; + +pub fn result(rv: ssl::SECStatus) -> Res<()> { + let _ = result_helper(rv, false)?; + Ok(()) +} + +pub fn result_or_blocked(rv: ssl::SECStatus) -> Res<bool> { + result_helper(rv, true) +} + +fn wrap_str_fn<F>(f: F, dflt: &str) -> String +where + F: FnOnce() -> *const i8, +{ + unsafe { + let p = f(); + if p.is_null() { + return dflt.to_string(); + } + CStr::from_ptr(p).to_string_lossy().into_owned() + } +} + +fn result_helper(rv: ssl::SECStatus, allow_blocked: bool) -> Res<bool> { + if rv == ssl::_SECStatus_SECSuccess { + return Ok(false); + } + + let code = unsafe { PR_GetError() }; + if allow_blocked && code == nspr::PR_WOULD_BLOCK_ERROR { + return Ok(true); + } + + let name = wrap_str_fn(|| unsafe { PR_ErrorToName(code) }, "UNKNOWN_ERROR"); + let desc = wrap_str_fn( + || unsafe { PR_ErrorToString(code, PR_LANGUAGE_I_DEFAULT) }, + "...", + ); + Err(Error::NssError { name, code, desc }) +} + +#[cfg(test)] +mod tests { + use super::{result, result_or_blocked}; + use crate::err::{self, nspr, Error, PRErrorCode, PR_SetError}; + use crate::ssl; + use test_fixture::fixture_init; + + fn set_error_code(code: PRErrorCode) { + unsafe { PR_SetError(code, 0) }; + } + + #[test] + fn is_ok() { + assert!(result(ssl::SECSuccess).is_ok()); + } + + #[test] + fn is_err() { + // This code doesn't work without initializing NSS first. + fixture_init(); + + set_error_code(err::ssl::SSL_ERROR_BAD_MAC_READ); + let r = result(ssl::SECFailure); + assert!(r.is_err()); + match r.unwrap_err() { + Error::NssError { name, code, desc } => { + assert_eq!(name, "SSL_ERROR_BAD_MAC_READ"); + assert_eq!(code, -12273); + assert_eq!( + desc, + "SSL received a record with an incorrect Message Authentication Code." + ); + } + _ => unreachable!(), + } + } + + #[test] + fn is_err_zero_code() { + // This code doesn't work without initializing NSS first. + fixture_init(); + + set_error_code(0); + let r = result(ssl::SECFailure); + assert!(r.is_err()); + match r.unwrap_err() { + Error::NssError { name, code, .. } => { + assert_eq!(name, "UNKNOWN_ERROR"); + assert_eq!(code, 0); + // Note that we don't test |desc| here because that comes from + // strerror(0), which is platform-dependent. + } + _ => unreachable!(), + } + } + + #[test] + fn blocked_as_error() { + // This code doesn't work without initializing NSS first. + fixture_init(); + + set_error_code(nspr::PR_WOULD_BLOCK_ERROR); + let r = result(ssl::SECFailure); + assert!(r.is_err()); + match r.unwrap_err() { + Error::NssError { name, code, desc } => { + assert_eq!(name, "PR_WOULD_BLOCK_ERROR"); + assert_eq!(code, -5998); + assert_eq!(desc, "The operation would have blocked"); + } + _ => panic!("bad error type"), + } + } + + #[test] + fn is_blocked() { + set_error_code(nspr::PR_WOULD_BLOCK_ERROR); + assert!(result_or_blocked(ssl::SECFailure).unwrap()); + } +} |