summaryrefslogtreecommitdiffstats
path: root/third_party/rust/nss-gk-api/src/err.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/nss-gk-api/src/err.rs')
-rw-r--r--third_party/rust/nss-gk-api/src/err.rs257
1 files changed, 257 insertions, 0 deletions
diff --git a/third_party/rust/nss-gk-api/src/err.rs b/third_party/rust/nss-gk-api/src/err.rs
new file mode 100644
index 0000000000..cc52b51f63
--- /dev/null
+++ b/third_party/rust/nss-gk-api/src/err.rs
@@ -0,0 +1,257 @@
+// 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.
+
+#![allow(dead_code)]
+#![allow(clippy::upper_case_acronyms)]
+
+use std::os::raw::c_char;
+use std::str::Utf8Error;
+
+use crate::nss_prelude::*;
+use crate::prtypes::*;
+
+include!(concat!(env!("OUT_DIR"), "/nspr_error.rs"));
+mod codes {
+ #![allow(non_snake_case)]
+ include!(concat!(env!("OUT_DIR"), "/nss_secerr.rs"));
+ include!(concat!(env!("OUT_DIR"), "/nss_sslerr.rs"));
+ include!(concat!(env!("OUT_DIR"), "/mozpkix.rs"));
+}
+pub use codes::mozilla_pkix_ErrorCode as mozpkix;
+pub use codes::SECErrorCodes as sec;
+pub use codes::SSLErrorCodes as ssl;
+pub mod nspr {
+ include!(concat!(env!("OUT_DIR"), "/nspr_err.rs"));
+}
+
+pub type Res<T> = Result<T, Error>;
+
+#[derive(Clone, Debug, PartialEq, PartialOrd, Ord, Eq)]
+pub enum Error {
+ AeadError,
+ CertificateLoading,
+ CipherInitFailure,
+ CreateSslSocket,
+ EchRetry(Vec<u8>),
+ HkdfError,
+ InternalError,
+ IntegerOverflow,
+ InvalidEpoch,
+ MixedHandshakeMethod,
+ NoDataAvailable,
+ NssError {
+ name: String,
+ code: PRErrorCode,
+ desc: String,
+ },
+ OverrunError,
+ SelfEncryptFailure,
+ StringError,
+ TimeTravelError,
+ UnsupportedCipher,
+ UnsupportedVersion,
+}
+
+impl Error {
+ pub(crate) fn last_nss_error() -> Self {
+ Self::from(unsafe { PR_GetError() })
+ }
+}
+
+impl std::error::Error for Error {
+ #[must_use]
+ fn cause(&self) -> Option<&dyn std::error::Error> {
+ None
+ }
+ #[must_use]
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ None
+ }
+}
+
+impl std::fmt::Display for Error {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ write!(f, "Error: {:?}", self)
+ }
+}
+
+impl From<std::num::TryFromIntError> for Error {
+ #[must_use]
+ fn from(_: std::num::TryFromIntError) -> Self {
+ Self::IntegerOverflow
+ }
+}
+impl From<std::ffi::NulError> for Error {
+ #[must_use]
+ fn from(_: std::ffi::NulError) -> Self {
+ Self::InternalError
+ }
+}
+impl From<Utf8Error> for Error {
+ fn from(_: Utf8Error) -> Self {
+ Self::StringError
+ }
+}
+impl From<PRErrorCode> for Error {
+ fn from(code: PRErrorCode) -> Self {
+ let name = wrap_str_fn(|| unsafe { PR_ErrorToName(code) }, "UNKNOWN_ERROR");
+ let desc = wrap_str_fn(
+ || unsafe { PR_ErrorToString(code, PR_LANGUAGE_I_DEFAULT) },
+ "...",
+ );
+ Self::NssError { name, code, desc }
+ }
+}
+
+use std::ffi::CStr;
+
+fn wrap_str_fn<F>(f: F, dflt: &str) -> String
+where
+ F: FnOnce() -> *const c_char,
+{
+ unsafe {
+ let p = f();
+ if p.is_null() {
+ return dflt.to_string();
+ }
+ CStr::from_ptr(p).to_string_lossy().into_owned()
+ }
+}
+
+pub fn is_blocked(result: &Res<()>) -> bool {
+ match result {
+ Err(Error::NssError { code, .. }) => *code == nspr::PR_WOULD_BLOCK_ERROR,
+ _ => false,
+ }
+}
+
+pub trait IntoResult
+{
+ /// The `Ok` type for the result.
+ type Ok;
+
+ /// Unsafe in our implementors because they take a pointer and have no way
+ /// to ensure that the pointer is valid. An invalid pointer could cause UB
+ /// in `impl Drop for Scoped`.
+ unsafe fn into_result(self) -> Result<Self::Ok, Error>;
+}
+
+pub unsafe fn into_result<P>(ptr: *mut P) -> Result<*mut P, Error> {
+ if ptr.is_null() {
+ Err(Error::last_nss_error())
+ } else {
+ Ok(ptr)
+ }
+}
+
+// This can be used to implement `IntoResult` for pointer types that do not make
+// sense as smart pointers. For smart pointers use `scoped_ptr!`.
+macro_rules! impl_into_result {
+ ($pointer:ty) => {
+ impl $crate::err::IntoResult for *mut $pointer {
+ type Ok = *mut $pointer;
+
+ unsafe fn into_result(self) -> Result<Self::Ok, $crate::err::Error> {
+ $crate::err::into_result(self)
+ }
+ }
+ }
+}
+
+impl IntoResult for SECStatus {
+ type Ok = ();
+
+ unsafe fn into_result(self) -> Result<(), Error> {
+ if self == SECSuccess {
+ Ok(())
+ } else {
+ Err(Error::last_nss_error())
+ }
+ }
+}
+
+pub fn secstatus_to_res(code: SECStatus) -> Res<()> {
+ // Unsafe in the trait, but this impl should be safe.
+ unsafe { SECStatus::into_result(code) }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::err::{self, is_blocked, secstatus_to_res, Error, PRErrorCode, PR_SetError};
+ use crate::ssl::{SECFailure, SECSuccess};
+ use test_fixture::fixture_init;
+
+ fn set_error_code(code: PRErrorCode) {
+ // This code doesn't work without initializing NSS first.
+ fixture_init();
+ unsafe {
+ PR_SetError(code, 0);
+ }
+ }
+
+ #[test]
+ fn error_code() {
+ fixture_init();
+ assert_eq!(15 - 0x3000, err::ssl::SSL_ERROR_BAD_MAC_READ);
+ assert_eq!(166 - 0x2000, err::sec::SEC_ERROR_LIBPKIX_INTERNAL);
+ assert_eq!(-5998, err::nspr::PR_WOULD_BLOCK_ERROR);
+ }
+
+ #[test]
+ fn is_ok() {
+ assert!(secstatus_to_res(SECSuccess).is_ok());
+ }
+
+ #[test]
+ fn is_err() {
+ set_error_code(err::ssl::SSL_ERROR_BAD_MAC_READ);
+ let r = secstatus_to_res(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() {
+ set_error_code(0);
+ let r = secstatus_to_res(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() {
+ set_error_code(err::nspr::PR_WOULD_BLOCK_ERROR);
+ let r = secstatus_to_res(SECFailure);
+ assert!(r.is_err());
+ assert!(is_blocked(&r));
+ 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"),
+ }
+ }
+}