<commit_before>//! Bindings to winapi's `PCCERT_CONTEXT` APIs.

use std::ffi::OsString;
use std::io;
use std::mem;
use std::os::windows::prelude::*;
use std::ptr;
use std::slice;
use crypt32;
use winapi;

use {Inner, KeyHandlePriv};
use key_handle::KeyHandle;

// FIXME https://github.com/retep998/winapi-rs/pull/318
const CRYPT_ACQUIRE_COMPARE_KEY_FLAG: winapi::DWORD = 0x4;
const CRYPT_ACQUIRE_SILENT_FLAG: winapi::DWORD = 0x40;
const CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG: winapi::DWORD = 0x10000;

// FIXME
const CRYPT_STRING_BASE64HEADER: winapi::DWORD = 0x0;

/// Wrapper of a winapi certificate, or a `PCCERT_CONTEXT`.
#[derive(Debug)]
pub struct CertContext(winapi::PCCERT_CONTEXT);

unsafe impl Sync for CertContext {}
unsafe impl Send for CertContext {}

impl CertContext {
    /// Decodes a DER-formatted X509 certificate.
    pub fn new(data: &[u8]) -> io::Result<CertContext> {
        let ret = unsafe {
            crypt32::CertCreateCertificateContext(winapi::X509_ASN_ENCODING |
                                                  winapi::PKCS_7_ASN_ENCODING,
                                                  data.as_ptr(),
                                                  data.len() as winapi::DWORD)
        };
        if ret.is_null() {
            Err(io::Error::last_os_error())
        } else {
            Ok(CertContext(ret))
        }
    }

    /// Decodes a PEM-formatted X509 certificate.
    pub fn from_pem(pem: &str) -> io::Result<CertContext> {
        unsafe {
            assert!(pem.len() <= winapi::DWORD::max_value() as usize);

            let mut len = 0;
            let ok = crypt32::CryptStringToBinaryA(pem.as_ptr() as winapi::LPCSTR,
                                                   pem.len() as winapi::DWORD,
                                                   CRYPT_STRING_BASE64HEADER,
                                                   ptr::null_mut(),
                                                   &mut len,
                                                   ptr::null_mut(),
                                                   ptr::null_mut());
            if ok != winapi::TRUE {
                return Err(io::Error::last_os_error());
            }

            let mut buf = vec![0; len as usize];
            let ok = crypt32::CryptStringToBinaryA(pem.as_ptr() as winapi::LPCSTR,
                                                   pem.len() as winapi::DWORD,
                                                   CRYPT_STRING_BASE64HEADER,
                                                   buf.as_mut_ptr(),
                                                   &mut len,
                                                   ptr::null_mut(),
                                                   ptr::null_mut());
            if ok != winapi::TRUE {
                return Err(io::Error::last_os_error());
            }

            CertContext::new(&buf)
        }
    }

    /// Returns the sha1 hash of this certificate
    ///
    /// The sha1 is returned as a 20-byte array representing the bits of the
    /// sha1 hash.
    pub fn sha1(&self) -> io::Result<[u8; 20]> {
        unsafe {
            let mut buf = [0; 20];
            let mut len = buf.len() as winapi::DWORD;
            let ret = crypt32::CertGetCertificateContextProperty(self.0,
                                                                 winapi::CERT_SHA1_HASH_PROP_ID,
                                                                 buf.as_mut_ptr() as
                                                                 *mut winapi::c_void,
                                                                 &mut len);
            if ret != winapi::TRUE {
                return Err(io::Error::last_os_error());
            }
            Ok(buf)
        }
    }

    /// Returns the `<SIGNATURE>/<HASH>` string representing the certificate
    /// signature.
    ///
    /// The `<SIGNATURE>` value identifies the CNG public key
    /// algorithm. The `<HASH>` value identifies the CNG hash algorithm.
    ///
    /// Common examples are:
    ///
    /// * `RSA/SHA1`
    /// * `RSA/SHA256`
    /// * `ECDSA/SHA256`
    pub fn sign_hash_algorithms(&self) -> io::Result<String> {
        self.get_string(winapi::CERT_SIGN_HASH_CNG_ALG_PROP_ID)
    }

    /// Returns the signature hash.
    pub fn signature_hash(&self) -> io::Result<Vec<u8>> {
        self.get_bytes(winapi::CERT_SIGNATURE_HASH_PROP_ID)
    }

    /// Returns the property displayed by the certificate UI. This property
    /// allows the user to describe the certificate's use.
    pub fn description(&self) -> io::Result<Vec<u8>> {
        self.get_bytes(winapi::CERT_DESCRIPTION_PROP_ID)
    }

    /// Returns a string that contains the display name for the certificate.
    pub fn friendly_name(&self) -> io::Result<String> {
        self.get_string(winapi::CERT_FRIENDLY_NAME_PROP_ID)
    }

    /// Configures the string that contains the display name for this
    /// certificate.
    pub fn set_friendly_name(&self, name: &str) -> io::Result<()> {
        self.set_string(winapi::CERT_FRIENDLY_NAME_PROP_ID, name)
    }

    /// Verifies the time validity of this certificate relative to the system's
    /// current time.
    pub fn is_time_valid(&self) -> io::Result<bool> {
        let ret = unsafe { crypt32::CertVerifyTimeValidity(ptr::null_mut(), (*self.0).pCertInfo) };
        Ok(ret == 0)
    }

    /// Returns a builder used to acquire the private key corresponding to this certificate.
    pub fn private_key<'a>(&'a self) -> AcquirePrivateKeyOptions<'a> {
        AcquirePrivateKeyOptions {
            cert: self,
            flags: 0,
        }
    }

    /// Deletes this certificate from its certificate store.
    pub fn delete(self) -> io::Result<()> {
        unsafe {
            let ret = crypt32::CertDeleteCertificateFromStore(self.0);
            mem::forget(self);
            if ret == winapi::TRUE {
                Ok(())
            } else {
                Err(io::Error::last_os_error())
            }
        }
    }

    fn get_encoded_bytes(&self) -> &[u8] {
        unsafe {
            let cert_ctx = *self.0;
            slice::from_raw_parts(cert_ctx.pbCertEncoded, cert_ctx.cbCertEncoded as usize)
        }
    }

    fn get_bytes(&self, prop: winapi::DWORD) -> io::Result<Vec<u8>> {
        unsafe {
            let mut len = 0;
            let ret =
                crypt32::CertGetCertificateContextProperty(self.0, prop, ptr::null_mut(), &mut len);
            if ret != winapi::TRUE {
                return Err(io::Error::last_os_error());
            }

            let mut buf = vec![0u8; len as usize];
            let ret = crypt32::CertGetCertificateContextProperty(self.0,
                                                                 prop,
                                                                 buf.as_mut_ptr() as
                                                                 *mut winapi::c_void,
                                                                 &mut len);
            if ret != winapi::TRUE {
                return Err(io::Error::last_os_error());
            }
            Ok(buf)
        }
    }

    fn get_string(&self, prop: winapi::DWORD) -> io::Result<String> {
        unsafe {
            let mut len = 0;
            let ret =
                crypt32::CertGetCertificateContextProperty(self.0, prop, ptr::null_mut(), &mut len);
            if ret != winapi::TRUE {
                return Err(io::Error::last_os_error());
            }

            // Divide by 2 b/c `len` is the byte length, but we're allocating
            // u16 pairs which are 2 bytes each.
            let amt = (len / 2) as usize;
            let mut buf = vec![0u16; amt];
            let ret = crypt32::CertGetCertificateContextProperty(self.0,
                                                                 prop,
                                                                 buf.as_mut_ptr() as
                                                                 *mut winapi::c_void,
                                                                 &mut len);
            if ret != winapi::TRUE {
                return Err(io::Error::last_os_error());
            }

            // Chop off the trailing nul byte
            Ok(OsString::from_wide(&buf[..amt - 1]).into_string().unwrap())
        }
    }

    fn set_string(&self, prop: winapi::DWORD, s: &str) -> io::Result<()> {
        unsafe {
            let data = s.encode_utf16().chain(Some(0)).collect::<Vec<_>>();
            let data = winapi::CRYPT_DATA_BLOB {
                cbData: (data.len() * 2) as winapi::DWORD,
                pbData: data.as_ptr() as *mut _,
            };
            let ret = crypt32::CertSetCertificateContextProperty(self.0,
                                                                 prop,
                                                                 0,
                                                                 &data as *const _ as *const _);
            if ret != winapi::TRUE {
                Err(io::Error::last_os_error())
            } else {
                Ok(())
            }
        }
    }
}

impl PartialEq for CertContext {
    fn eq(&self, other: &CertContext) -> bool {
        self.get_encoded_bytes() == other.get_encoded_bytes()
    }
}

/// A builder type for certificate private key lookup.
pub struct AcquirePrivateKeyOptions<'a> {
    cert: &'a CertContext,
    flags: winapi::DWORD,
}

impl<'a> AcquirePrivateKeyOptions<'a> {
    /// If set, the certificate's public key will be compared with the private key to ensure a
    /// match.
    pub fn compare_key(&mut self, compare_key: bool) -> &mut AcquirePrivateKeyOptions<'a> {
        self.flag(CRYPT_ACQUIRE_COMPARE_KEY_FLAG, compare_key)
    }

    /// If set, the lookup will not display any user interface, even if that causes the lookup to
    /// fail.
    pub fn silent(&mut self, silent: bool) -> &mut AcquirePrivateKeyOptions<'a> {
        self.flag(CRYPT_ACQUIRE_SILENT_FLAG, silent)
    }

    fn flag(&mut self, flag: winapi::DWORD, set: bool) -> &mut AcquirePrivateKeyOptions<'a> {
        if set {
            self.flags |= flag;
        } else {
            self.flags &= !flag;
        }
        self
    }

    /// Acquires the private key handle.
    pub fn acquire(&self) -> io::Result<KeyHandle> {
        unsafe {
            let flags = self.flags | CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG;
            let mut handle = 0;
            let mut spec = 0;
            let mut free = winapi::FALSE;
            let res = crypt32::CryptAcquireCertificatePrivateKey(self.cert.0,
                                                                 flags,
                                                                 ptr::null_mut(),
                                                                 &mut handle,
                                                                 &mut spec,
                                                                 &mut free);
            if res != winapi::TRUE {
                return Err(io::Error::last_os_error());
            }
            assert!(free == winapi::TRUE);
            Ok(KeyHandle::new(handle, spec))
        }
    }
}

impl Clone for CertContext {
    fn clone(&self) -> CertContext {
        unsafe { CertContext(crypt32::CertDuplicateCertificateContext(self.0)) }
    }
}

impl Drop for CertContext {
    fn drop(&mut self) {
        unsafe {
            crypt32::CertFreeCertificateContext(self.0);
        }
    }
}

impl Inner<winapi::PCCERT_CONTEXT> for CertContext {
    unsafe fn from_inner(t: winapi::PCCERT_CONTEXT) -> CertContext {
        CertContext(t)
    }

    fn as_inner(&self) -> winapi::PCCERT_CONTEXT {
        self.0
    }

    fn get_mut(&mut self) -> &mut winapi::PCCERT_CONTEXT {
        &mut self.0
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn decode() {
        let der = include_bytes!("../test/cert.der");
        let pem = include_str!("../test/cert.pem");

        let der = CertContext::new(der).unwrap();
        let pem = CertContext::from_pem(pem).unwrap();
        assert_eq!(der, pem);
    }
}
<commit_msg>Add more generic certificate fingerprinting API (#27)<commit_after>//! Bindings to winapi's `PCCERT_CONTEXT` APIs.

use std::ffi::OsString;
use std::io;
use std::mem;
use std::os::windows::prelude::*;
use std::ptr;
use std::slice;
use crypt32;
use winapi;

use {Inner, KeyHandlePriv};
use key_handle::KeyHandle;

// FIXME https://github.com/retep998/winapi-rs/pull/318
const CRYPT_ACQUIRE_COMPARE_KEY_FLAG: winapi::DWORD = 0x4;
const CRYPT_ACQUIRE_SILENT_FLAG: winapi::DWORD = 0x40;
const CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG: winapi::DWORD = 0x10000;

// FIXME
const CRYPT_STRING_BASE64HEADER: winapi::DWORD = 0x0;

/// Wrapper of a winapi certificate, or a `PCCERT_CONTEXT`.
#[derive(Debug)]
pub struct CertContext(winapi::PCCERT_CONTEXT);

unsafe impl Sync for CertContext {}
unsafe impl Send for CertContext {}

/// A supported hashing algorithm
pub struct HashAlgorithm(winapi::DWORD, usize);

#[allow(missing_docs)]
impl HashAlgorithm {
    pub fn md5() -> HashAlgorithm {
        HashAlgorithm(winapi::CALG_MD5, 16)
    }

    pub fn sha1() -> HashAlgorithm{
        HashAlgorithm(winapi::CALG_SHA1, 20)
    }

    pub fn sha256() -> HashAlgorithm {
        HashAlgorithm(winapi::CALG_SHA_256, 32)
    }

    pub fn sha384() -> HashAlgorithm {
        HashAlgorithm(winapi::CALG_SHA_384, 48)
    }

    pub fn sha512() -> HashAlgorithm {
        HashAlgorithm(winapi::CALG_SHA_512, 64)
    }
}

impl CertContext {
    /// Decodes a DER-formatted X509 certificate.
    pub fn new(data: &[u8]) -> io::Result<CertContext> {
        let ret = unsafe {
            crypt32::CertCreateCertificateContext(winapi::X509_ASN_ENCODING |
                                                  winapi::PKCS_7_ASN_ENCODING,
                                                  data.as_ptr(),
                                                  data.len() as winapi::DWORD)
        };
        if ret.is_null() {
            Err(io::Error::last_os_error())
        } else {
            Ok(CertContext(ret))
        }
    }

    /// Decodes a PEM-formatted X509 certificate.
    pub fn from_pem(pem: &str) -> io::Result<CertContext> {
        unsafe {
            assert!(pem.len() <= winapi::DWORD::max_value() as usize);

            let mut len = 0;
            let ok = crypt32::CryptStringToBinaryA(pem.as_ptr() as winapi::LPCSTR,
                                                   pem.len() as winapi::DWORD,
                                                   CRYPT_STRING_BASE64HEADER,
                                                   ptr::null_mut(),
                                                   &mut len,
                                                   ptr::null_mut(),
                                                   ptr::null_mut());
            if ok != winapi::TRUE {
                return Err(io::Error::last_os_error());
            }

            let mut buf = vec![0; len as usize];
            let ok = crypt32::CryptStringToBinaryA(pem.as_ptr() as winapi::LPCSTR,
                                                   pem.len() as winapi::DWORD,
                                                   CRYPT_STRING_BASE64HEADER,
                                                   buf.as_mut_ptr(),
                                                   &mut len,
                                                   ptr::null_mut(),
                                                   ptr::null_mut());
            if ok != winapi::TRUE {
                return Err(io::Error::last_os_error());
            }

            CertContext::new(&buf)
        }
    }

    /// Returns a hash of this certificate
    pub fn fingerprint(&self, alg: HashAlgorithm) -> io::Result<Vec<u8>> {
        unsafe {
            let mut buf = vec![0u8; alg.1];
            let mut len = buf.len() as winapi::DWORD;

            let ret = crypt32::CryptHashCertificate(0,
                                                    alg.0,
                                                    0,
                                                    (*self.0).pbCertEncoded,
                                                    (*self.0).cbCertEncoded,
                                                    buf.as_mut_ptr(),
                                                    &mut len);

            if ret != winapi::TRUE {
                return Err(io::Error::last_os_error());
            }
            Ok(buf)
        }
    }

    /// Returns the sha1 hash of this certificate
    ///
    /// The sha1 is returned as a 20-byte array representing the bits of the
    /// sha1 hash.
    #[deprecated(note = "please use fingerprint instead")]
    pub fn sha1(&self) -> io::Result<[u8; 20]> {
        let mut out = [0u8; 20];
        out.copy_from_slice(&try!(self.fingerprint(HashAlgorithm::sha1())));
        Ok(out)
    }

    /// Returns the `<SIGNATURE>/<HASH>` string representing the certificate
    /// signature.
    ///
    /// The `<SIGNATURE>` value identifies the CNG public key
    /// algorithm. The `<HASH>` value identifies the CNG hash algorithm.
    ///
    /// Common examples are:
    ///
    /// * `RSA/SHA1`
    /// * `RSA/SHA256`
    /// * `ECDSA/SHA256`
    pub fn sign_hash_algorithms(&self) -> io::Result<String> {
        self.get_string(winapi::CERT_SIGN_HASH_CNG_ALG_PROP_ID)
    }

    /// Returns the signature hash.
    pub fn signature_hash(&self) -> io::Result<Vec<u8>> {
        self.get_bytes(winapi::CERT_SIGNATURE_HASH_PROP_ID)
    }

    /// Returns the property displayed by the certificate UI. This property
    /// allows the user to describe the certificate's use.
    pub fn description(&self) -> io::Result<Vec<u8>> {
        self.get_bytes(winapi::CERT_DESCRIPTION_PROP_ID)
    }

    /// Returns a string that contains the display name for the certificate.
    pub fn friendly_name(&self) -> io::Result<String> {
        self.get_string(winapi::CERT_FRIENDLY_NAME_PROP_ID)
    }

    /// Configures the string that contains the display name for this
    /// certificate.
    pub fn set_friendly_name(&self, name: &str) -> io::Result<()> {
        self.set_string(winapi::CERT_FRIENDLY_NAME_PROP_ID, name)
    }

    /// Verifies the time validity of this certificate relative to the system's
    /// current time.
    pub fn is_time_valid(&self) -> io::Result<bool> {
        let ret = unsafe { crypt32::CertVerifyTimeValidity(ptr::null_mut(), (*self.0).pCertInfo) };
        Ok(ret == 0)
    }

    /// Returns a builder used to acquire the private key corresponding to this certificate.
    pub fn private_key<'a>(&'a self) -> AcquirePrivateKeyOptions<'a> {
        AcquirePrivateKeyOptions {
            cert: self,
            flags: 0,
        }
    }

    /// Deletes this certificate from its certificate store.
    pub fn delete(self) -> io::Result<()> {
        unsafe {
            let ret = crypt32::CertDeleteCertificateFromStore(self.0);
            mem::forget(self);
            if ret == winapi::TRUE {
                Ok(())
            } else {
                Err(io::Error::last_os_error())
            }
        }
    }

    fn get_encoded_bytes(&self) -> &[u8] {
        unsafe {
            let cert_ctx = *self.0;
            slice::from_raw_parts(cert_ctx.pbCertEncoded, cert_ctx.cbCertEncoded as usize)
        }
    }

    fn get_bytes(&self, prop: winapi::DWORD) -> io::Result<Vec<u8>> {
        unsafe {
            let mut len = 0;
            let ret =
                crypt32::CertGetCertificateContextProperty(self.0, prop, ptr::null_mut(), &mut len);
            if ret != winapi::TRUE {
                return Err(io::Error::last_os_error());
            }

            let mut buf = vec![0u8; len as usize];
            let ret = crypt32::CertGetCertificateContextProperty(self.0,
                                                                 prop,
                                                                 buf.as_mut_ptr() as
                                                                 *mut winapi::c_void,
                                                                 &mut len);
            if ret != winapi::TRUE {
                return Err(io::Error::last_os_error());
            }
            Ok(buf)
        }
    }

    fn get_string(&self, prop: winapi::DWORD) -> io::Result<String> {
        unsafe {
            let mut len = 0;
            let ret =
                crypt32::CertGetCertificateContextProperty(self.0, prop, ptr::null_mut(), &mut len);
            if ret != winapi::TRUE {
                return Err(io::Error::last_os_error());
            }

            // Divide by 2 b/c `len` is the byte length, but we're allocating
            // u16 pairs which are 2 bytes each.
            let amt = (len / 2) as usize;
            let mut buf = vec![0u16; amt];
            let ret = crypt32::CertGetCertificateContextProperty(self.0,
                                                                 prop,
                                                                 buf.as_mut_ptr() as
                                                                 *mut winapi::c_void,
                                                                 &mut len);
            if ret != winapi::TRUE {
                return Err(io::Error::last_os_error());
            }

            // Chop off the trailing nul byte
            Ok(OsString::from_wide(&buf[..amt - 1]).into_string().unwrap())
        }
    }

    fn set_string(&self, prop: winapi::DWORD, s: &str) -> io::Result<()> {
        unsafe {
            let data = s.encode_utf16().chain(Some(0)).collect::<Vec<_>>();
            let data = winapi::CRYPT_DATA_BLOB {
                cbData: (data.len() * 2) as winapi::DWORD,
                pbData: data.as_ptr() as *mut _,
            };
            let ret = crypt32::CertSetCertificateContextProperty(self.0,
                                                                 prop,
                                                                 0,
                                                                 &data as *const _ as *const _);
            if ret != winapi::TRUE {
                Err(io::Error::last_os_error())
            } else {
                Ok(())
            }
        }
    }
}

impl PartialEq for CertContext {
    fn eq(&self, other: &CertContext) -> bool {
        self.get_encoded_bytes() == other.get_encoded_bytes()
    }
}

/// A builder type for certificate private key lookup.
pub struct AcquirePrivateKeyOptions<'a> {
    cert: &'a CertContext,
    flags: winapi::DWORD,
}

impl<'a> AcquirePrivateKeyOptions<'a> {
    /// If set, the certificate's public key will be compared with the private key to ensure a
    /// match.
    pub fn compare_key(&mut self, compare_key: bool) -> &mut AcquirePrivateKeyOptions<'a> {
        self.flag(CRYPT_ACQUIRE_COMPARE_KEY_FLAG, compare_key)
    }

    /// If set, the lookup will not display any user interface, even if that causes the lookup to
    /// fail.
    pub fn silent(&mut self, silent: bool) -> &mut AcquirePrivateKeyOptions<'a> {
        self.flag(CRYPT_ACQUIRE_SILENT_FLAG, silent)
    }

    fn flag(&mut self, flag: winapi::DWORD, set: bool) -> &mut AcquirePrivateKeyOptions<'a> {
        if set {
            self.flags |= flag;
        } else {
            self.flags &= !flag;
        }
        self
    }

    /// Acquires the private key handle.
    pub fn acquire(&self) -> io::Result<KeyHandle> {
        unsafe {
            let flags = self.flags | CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG;
            let mut handle = 0;
            let mut spec = 0;
            let mut free = winapi::FALSE;
            let res = crypt32::CryptAcquireCertificatePrivateKey(self.cert.0,
                                                                 flags,
                                                                 ptr::null_mut(),
                                                                 &mut handle,
                                                                 &mut spec,
                                                                 &mut free);
            if res != winapi::TRUE {
                return Err(io::Error::last_os_error());
            }
            assert!(free == winapi::TRUE);
            Ok(KeyHandle::new(handle, spec))
        }
    }
}

impl Clone for CertContext {
    fn clone(&self) -> CertContext {
        unsafe { CertContext(crypt32::CertDuplicateCertificateContext(self.0)) }
    }
}

impl Drop for CertContext {
    fn drop(&mut self) {
        unsafe {
            crypt32::CertFreeCertificateContext(self.0);
        }
    }
}

impl Inner<winapi::PCCERT_CONTEXT> for CertContext {
    unsafe fn from_inner(t: winapi::PCCERT_CONTEXT) -> CertContext {
        CertContext(t)
    }

    fn as_inner(&self) -> winapi::PCCERT_CONTEXT {
        self.0
    }

    fn get_mut(&mut self) -> &mut winapi::PCCERT_CONTEXT {
        &mut self.0
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn decode() {
        let der = include_bytes!("../test/cert.der");
        let pem = include_str!("../test/cert.pem");

        let der = CertContext::new(der).unwrap();
        let pem = CertContext::from_pem(pem).unwrap();
        assert_eq!(der, pem);
    }

    #[test]
    fn fingerprint() {
        let der = include_bytes!("../test/cert.der");
        let pem = include_str!("../test/cert.pem");

        let der = CertContext::new(der).unwrap();
        let pem = CertContext::from_pem(pem).unwrap();

        let hash = der.fingerprint(HashAlgorithm::sha1()).unwrap();
        assert_eq!(hash, vec![
            0x5b, 0x77, 0x9a, 0xc3, 0x23, 0xdc, 0xc4, 0xff, 0xd8, 0xf1,
            0x89, 0x5e, 0xea, 0x73, 0x96, 0x79, 0x84, 0xbd, 0xf6, 0x86
        ]);
        assert_eq!(hash, pem.fingerprint(HashAlgorithm::sha1()).unwrap());

        let hash = der.fingerprint(HashAlgorithm::sha256()).unwrap();
        assert_eq!(hash, vec![
            0x9c, 0xf3, 0x6b, 0x55, 0x56, 0xde, 0x20, 0xd9, 0x69, 0xc0, 0xdd, 0x8f,
            0xca, 0xda, 0xda, 0x9b, 0xb8, 0x51, 0x9, 0x9f, 0x86, 0x8f, 0x85, 0x5d,
            0x90, 0x81, 0x73, 0xb5, 0x7a, 0xe0, 0x5a, 0xdd
        ]);
        assert_eq!(hash, pem.fingerprint(HashAlgorithm::sha256()).unwrap());
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Plugins and related functionality.

pub mod rpc;
mod manifest;
mod catalog;

use std::fmt;
use std::io::BufReader;
use std::path::Path;
use std::process::{Child, Command as ProcCommand, Stdio};
use std::thread;

use serde_json::Value;

use xi_rpc::{self, RpcPeer, RpcLoop};
use xi_trace;

use WeakXiCore;
use config::Table;
use tabs::ViewId;

use self::rpc::{PluginUpdate, PluginBufferInfo};

pub use self::manifest::{PluginDescription, Command, PlaceholderRpc};
pub(crate) use self::catalog::PluginCatalog;

pub type PluginName = String;

/// A process-unique identifier for a running plugin.
///
/// Note: two instances of the same executable will have different identifiers.
/// Note: this identifier is distinct from the OS's process id.
#[derive(Serialize, Deserialize, Default, Debug, Clone, Copy, Hash,
         PartialEq, Eq, PartialOrd, Ord)]
pub struct PluginPid(pub(crate) usize);

pub type PluginId = PluginPid;

impl fmt::Display for PluginPid {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "plugin-{}", self.0)
    }
}

pub struct Plugin {
    peer: RpcPeer,
    pub(crate) id: PluginId,
    pub(crate) name: String,
    #[allow(dead_code)]
    process: Child,
}

impl Plugin {
    //TODO: initialize should be sent automatically during launch,
    //and should only send the plugin_id. We can just use the existing 'new_buffer'
    // RPC for adding views
    pub fn initialize(&self, info: Vec<PluginBufferInfo>) {
        self.peer.send_rpc_notification("initialize",
                                        &json!({
                                         "plugin_id": self.id,
                                         "buffer_info": info,
                                        }))
    }

    pub fn shutdown(&self) {
        self.peer.send_rpc_notification("shutdown", &json!({}));
    }

    // TODO: rethink naming, does this need to be a vec?
    pub fn new_buffer(&self, info: &PluginBufferInfo) {
        self.peer.send_rpc_notification("new_buffer",
                                        &json!({
                                            "buffer_info": [info],
                                        }))
    }

    pub fn close_view(&self, view_id: ViewId) {
        self.peer.send_rpc_notification("did_close",
                                        &json!({
                                            "view_id": view_id,
                                        }))

    }

    pub fn did_save(&self, view_id: ViewId, path: &Path) {
        self.peer.send_rpc_notification("did_save",
                                        &json!({
                                            "view_id": view_id,
                                            "path": path,
                                        }))
    }

    pub fn update<F>(&self, update: &PluginUpdate, callback: F)
where F: FnOnce(Result<Value, xi_rpc::Error>) + Send + 'static
    {
        self.peer.send_rpc_request_async("update", &json!(update),
                                         Box::new(callback))
    }

    pub fn toggle_tracing(&self, enabled: bool) {
        self.peer.send_rpc_notification("tracing_config",
                                        &json!({"enabled": enabled}))
    }

    pub fn collect_trace(&self) -> Result<Value, xi_rpc::Error> {
       self.peer.send_rpc_request("collect_trace", &json!({}))
    }

    pub fn config_changed(&self, view_id: ViewId, changes: &Table) {
        self.peer.send_rpc_notification("config_changed",
                                        &json!({"view_id": view_id,
                                                "changes": changes}))
    }
}

pub(crate) fn start_plugin_process(plugin_desc: PluginDescription,
                                    id: PluginId, core: WeakXiCore) {
    thread::spawn(move || {
        eprintln!("starting plugin {}", &plugin_desc.name);
        let child = ProcCommand::new(&plugin_desc.exec_path)
            .stdin(Stdio::piped())
            .stdout(Stdio::piped())
            .spawn();

        match child {
            Ok(mut child) => {
                let child_stdin = child.stdin.take().unwrap();
                let child_stdout = child.stdout.take().unwrap();
                let mut looper = RpcLoop::new(child_stdin);
                let peer: RpcPeer = Box::new(looper.get_raw_peer());
                let name = plugin_desc.name.clone();
peer.send_rpc_notification("ping", &Value::Array(Vec::new()));
                let plugin = Plugin { peer, process: child, name, id };

                // set tracing immediately
                if xi_trace::is_enabled() {
                    plugin.toggle_tracing(true);
                }

                core.plugin_connect(Ok(plugin));
                //TODO: we could be logging plugin exit results
                let mut core = core;
                let _ = looper.mainloop(|| BufReader::new(child_stdout),
                                        &mut core);
            }
            Err(err) => core.plugin_connect(Err(err)),
        }
    });
}
<commit_msg>Fixed extraneous whitespace in mod.rs<commit_after>// Copyright 2017 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Plugins and related functionality.

pub mod rpc;
mod manifest;
mod catalog;

use std::fmt;
use std::io::BufReader;
use std::path::Path;
use std::process::{Child, Command as ProcCommand, Stdio};
use std::thread;

use serde_json::Value;

use xi_rpc::{self, RpcPeer, RpcLoop};
use xi_trace;

use WeakXiCore;
use config::Table;
use tabs::ViewId;

use self::rpc::{PluginUpdate, PluginBufferInfo};

pub use self::manifest::{PluginDescription, Command, PlaceholderRpc};
pub(crate) use self::catalog::PluginCatalog;

pub type PluginName = String;

/// A process-unique identifier for a running plugin.
///
/// Note: two instances of the same executable will have different identifiers.
/// Note: this identifier is distinct from the OS's process id.
#[derive(Serialize, Deserialize, Default, Debug, Clone, Copy, Hash,
         PartialEq, Eq, PartialOrd, Ord)]
pub struct PluginPid(pub(crate) usize);

pub type PluginId = PluginPid;

impl fmt::Display for PluginPid {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "plugin-{}", self.0)
    }
}

pub struct Plugin {
    peer: RpcPeer,
    pub(crate) id: PluginId,
    pub(crate) name: String,
    #[allow(dead_code)]
    process: Child,
}

impl Plugin {
    //TODO: initialize should be sent automatically during launch,
    //and should only send the plugin_id. We can just use the existing 'new_buffer'
    // RPC for adding views
    pub fn initialize(&self, info: Vec<PluginBufferInfo>) {
        self.peer.send_rpc_notification("initialize",
                                        &json!({
                                         "plugin_id": self.id,
                                         "buffer_info": info,
                                        }))
    }

    pub fn shutdown(&self) {
        self.peer.send_rpc_notification("shutdown", &json!({}));
    }

    // TODO: rethink naming, does this need to be a vec?
    pub fn new_buffer(&self, info: &PluginBufferInfo) {
        self.peer.send_rpc_notification("new_buffer",
                                        &json!({
                                            "buffer_info": [info],
                                        }))
    }

    pub fn close_view(&self, view_id: ViewId) {
        self.peer.send_rpc_notification("did_close",
                                        &json!({
                                            "view_id": view_id,
                                        }))

    }

    pub fn did_save(&self, view_id: ViewId, path: &Path) {
        self.peer.send_rpc_notification("did_save",
                                        &json!({
                                            "view_id": view_id,
                                            "path": path,
                                        }))
    }

    pub fn update<F>(&self, update: &PluginUpdate, callback: F)
where F: FnOnce(Result<Value, xi_rpc::Error>) + Send + 'static
    {
        self.peer.send_rpc_request_async("update", &json!(update),
                                         Box::new(callback))
    }

    pub fn toggle_tracing(&self, enabled: bool) {
        self.peer.send_rpc_notification("tracing_config",
                                        &json!({"enabled": enabled}))
    }

    pub fn collect_trace(&self) -> Result<Value, xi_rpc::Error> {
        self.peer.send_rpc_request("collect_trace", &json!({}))
    }

    pub fn config_changed(&self, view_id: ViewId, changes: &Table) {
        self.peer.send_rpc_notification("config_changed",
                                        &json!({"view_id": view_id,
                                                "changes": changes}))
    }
}

pub(crate) fn start_plugin_process(plugin_desc: PluginDescription,
                                    id: PluginId, core: WeakXiCore) {
    thread::spawn(move || {
        eprintln!("starting plugin {}", &plugin_desc.name);
        let child = ProcCommand::new(&plugin_desc.exec_path)
            .stdin(Stdio::piped())
            .stdout(Stdio::piped())
            .spawn();

        match child {
            Ok(mut child) => {
                let child_stdin = child.stdin.take().unwrap();
                let child_stdout = child.stdout.take().unwrap();
                let mut looper = RpcLoop::new(child_stdin);
                let peer: RpcPeer = Box::new(looper.get_raw_peer());
                let name = plugin_desc.name.clone();
peer.send_rpc_notification("ping", &Value::Array(Vec::new()));
                let plugin = Plugin { peer, process: child, name, id };

                // set tracing immediately
                if xi_trace::is_enabled() {
                    plugin.toggle_tracing(true);
                }

                core.plugin_connect(Ok(plugin));
                //TODO: we could be logging plugin exit results
                let mut core = core;
                let _ = looper.mainloop(|| BufReader::new(child_stdout),
                                        &mut core);
            }
            Err(err) => core.plugin_connect(Err(err)),
        }
    });
}
<|endoftext|>


<commit_before><commit_msg>First workaround for #86<commit_after><|endoftext|>


<commit_before><commit_msg>fill_match_arm works with trivial arm<commit_after><|endoftext|>


<commit_before><commit_msg>Import functions `crc8` and `crc16`<commit_after><|endoftext|>


<commit_before><commit_msg>rand: Fix infinite recursion<commit_after><|endoftext|>


<commit_before>#![allow(non_camel_case_types, unused)]

use crate::convert::TryInto;
use crate::io;
use crate::mem::MaybeUninit;
use crate::os::raw::c_char;

use libc::{c_int, c_void, size_t};

pub type zx_handle_t = u32;
pub type zx_vaddr_t = usize;
pub type zx_rights_t = u32;
pub type zx_status_t = i32;

pub const ZX_HANDLE_INVALID: zx_handle_t = 0;

pub type zx_time_t = i64;
pub const ZX_TIME_INFINITE: zx_time_t = i64::MAX;

pub type zx_signals_t = u32;

pub const ZX_OBJECT_SIGNAL_3: zx_signals_t = 1 << 3;

pub const ZX_TASK_TERMINATED: zx_signals_t = ZX_OBJECT_SIGNAL_3;

pub const ZX_RIGHT_SAME_RIGHTS: zx_rights_t = 1 << 31;

pub type zx_object_info_topic_t = u32;

pub const ZX_INFO_PROCESS: zx_object_info_topic_t = 3;

pub fn zx_cvt<T>(t: T) -> io::Result<T>
where
    T: TryInto<zx_status_t> + Copy,
{
    if let Ok(status) = TryInto::try_into(t) {
        if status < 0 { Err(io::Error::from_raw_os_error(status)) } else { Ok(t) }
    } else {
        Err(io::Error::last_os_error())
    }
}

// Safe wrapper around zx_handle_t
pub struct Handle {
    raw: zx_handle_t,
}

impl Handle {
    pub fn new(raw: zx_handle_t) -> Handle {
        Handle { raw }
    }

    pub fn raw(&self) -> zx_handle_t {
        self.raw
    }
}

impl Drop for Handle {
    fn drop(&mut self) {
        unsafe {
            zx_cvt(zx_handle_close(self.raw)).expect("Failed to close zx_handle_t");
        }
    }
}

// Returned for topic ZX_INFO_PROCESS
#[derive(Default)]
#[repr(C)]
pub struct zx_info_process_t {
    pub return_code: i64,
    pub started: bool,
    pub exited: bool,
    pub debugger_attached: bool,
}

extern "C" {
    pub fn zx_job_default() -> zx_handle_t;

    pub fn zx_task_kill(handle: zx_handle_t) -> zx_status_t;

    pub fn zx_handle_close(handle: zx_handle_t) -> zx_status_t;

    pub fn zx_handle_duplicate(
        handle: zx_handle_t,
        rights: zx_rights_t,
        out: *const zx_handle_t,
    ) -> zx_handle_t;

    pub fn zx_object_wait_one(
        handle: zx_handle_t,
        signals: zx_signals_t,
        timeout: zx_time_t,
        pending: *mut zx_signals_t,
    ) -> zx_status_t;

    pub fn zx_object_get_info(
        handle: zx_handle_t,
        topic: u32,
        buffer: *mut c_void,
        buffer_size: size_t,
        actual_size: *mut size_t,
        avail: *mut size_t,
    ) -> zx_status_t;
}

#[derive(Default)]
#[repr(C)]
pub struct fdio_spawn_action_t {
    pub action: u32,
    pub reserved0: u32,
    pub local_fd: i32,
    pub target_fd: i32,
    pub reserved1: u64,
}

extern "C" {
    pub fn fdio_spawn_etc(
        job: zx_handle_t,
        flags: u32,
        path: *const c_char,
        argv: *const *const c_char,
        envp: *const *const c_char,
        action_count: size_t,
        actions: *const fdio_spawn_action_t,
        process: *mut zx_handle_t,
        err_msg: *mut c_char,
    ) -> zx_status_t;

    pub fn fdio_fd_clone(fd: c_int, out_handle: *mut zx_handle_t) -> zx_status_t;
    pub fn fdio_fd_create(handle: zx_handle_t, fd: *mut c_int) -> zx_status_t;
}

// fdio_spawn_etc flags

pub const FDIO_SPAWN_CLONE_JOB: u32 = 0x0001;
pub const FDIO_SPAWN_CLONE_LDSVC: u32 = 0x0002;
pub const FDIO_SPAWN_CLONE_NAMESPACE: u32 = 0x0004;
pub const FDIO_SPAWN_CLONE_STDIO: u32 = 0x0008;
pub const FDIO_SPAWN_CLONE_ENVIRON: u32 = 0x0010;
pub const FDIO_SPAWN_CLONE_UTC_CLOCK: u32 = 0x0020;
pub const FDIO_SPAWN_CLONE_ALL: u32 = 0xFFFF;

// fdio_spawn_etc actions

pub const FDIO_SPAWN_ACTION_CLONE_FD: u32 = 0x0001;
pub const FDIO_SPAWN_ACTION_TRANSFER_FD: u32 = 0x0002;

// Errors

#[allow(unused)]
pub const ERR_INTERNAL: zx_status_t = -1;

// ERR_NOT_SUPPORTED: The operation is not implemented, supported,
// or enabled.
#[allow(unused)]
pub const ERR_NOT_SUPPORTED: zx_status_t = -2;

// ERR_NO_RESOURCES: The system was not able to allocate some resource
// needed for the operation.
#[allow(unused)]
pub const ERR_NO_RESOURCES: zx_status_t = -3;

// ERR_NO_MEMORY: The system was not able to allocate memory needed
// for the operation.
#[allow(unused)]
pub const ERR_NO_MEMORY: zx_status_t = -4;

// ERR_CALL_FAILED: The second phase of zx_channel_call(; did not complete
// successfully.
#[allow(unused)]
pub const ERR_CALL_FAILED: zx_status_t = -5;

// ERR_INTERRUPTED_RETRY: The system call was interrupted, but should be
// retried.  This should not be seen outside of the VDSO.
#[allow(unused)]
pub const ERR_INTERRUPTED_RETRY: zx_status_t = -6;

// ======= Parameter errors =======
// ERR_INVALID_ARGS: an argument is invalid, ex. null pointer
#[allow(unused)]
pub const ERR_INVALID_ARGS: zx_status_t = -10;

// ERR_BAD_HANDLE: A specified handle value does not refer to a handle.
#[allow(unused)]
pub const ERR_BAD_HANDLE: zx_status_t = -11;

// ERR_WRONG_TYPE: The subject of the operation is the wrong type to
// perform the operation.
// Example: Attempting a message_read on a thread handle.
#[allow(unused)]
pub const ERR_WRONG_TYPE: zx_status_t = -12;

// ERR_BAD_SYSCALL: The specified syscall number is invalid.
#[allow(unused)]
pub const ERR_BAD_SYSCALL: zx_status_t = -13;

// ERR_OUT_OF_RANGE: An argument is outside the valid range for this
// operation.
#[allow(unused)]
pub const ERR_OUT_OF_RANGE: zx_status_t = -14;

// ERR_BUFFER_TOO_SMALL: A caller provided buffer is too small for
// this operation.
#[allow(unused)]
pub const ERR_BUFFER_TOO_SMALL: zx_status_t = -15;

// ======= Precondition or state errors =======
// ERR_BAD_STATE: operation failed because the current state of the
// object does not allow it, or a precondition of the operation is
// not satisfied
#[allow(unused)]
pub const ERR_BAD_STATE: zx_status_t = -20;

// ERR_TIMED_OUT: The time limit for the operation elapsed before
// the operation completed.
#[allow(unused)]
pub const ERR_TIMED_OUT: zx_status_t = -21;

// ERR_SHOULD_WAIT: The operation cannot be performed currently but
// potentially could succeed if the caller waits for a prerequisite
// to be satisfied, for example waiting for a handle to be readable
// or writable.
// Example: Attempting to read from a message pipe that has no
// messages waiting but has an open remote will return ERR_SHOULD_WAIT.
// Attempting to read from a message pipe that has no messages waiting
// and has a closed remote end will return ERR_REMOTE_CLOSED.
#[allow(unused)]
pub const ERR_SHOULD_WAIT: zx_status_t = -22;

// ERR_CANCELED: The in-progress operation (e.g., a wait) has been
// // canceled.
#[allow(unused)]
pub const ERR_CANCELED: zx_status_t = -23;

// ERR_PEER_CLOSED: The operation failed because the remote end
// of the subject of the operation was closed.
#[allow(unused)]
pub const ERR_PEER_CLOSED: zx_status_t = -24;

// ERR_NOT_FOUND: The requested entity is not found.
#[allow(unused)]
pub const ERR_NOT_FOUND: zx_status_t = -25;

// ERR_ALREADY_EXISTS: An object with the specified identifier
// already exists.
// Example: Attempting to create a file when a file already exists
// with that name.
#[allow(unused)]
pub const ERR_ALREADY_EXISTS: zx_status_t = -26;

// ERR_ALREADY_BOUND: The operation failed because the named entity
// is already owned or controlled by another entity. The operation
// could succeed later if the current owner releases the entity.
#[allow(unused)]
pub const ERR_ALREADY_BOUND: zx_status_t = -27;

// ERR_UNAVAILABLE: The subject of the operation is currently unable
// to perform the operation.
// Note: This is used when there's no direct way for the caller to
// observe when the subject will be able to perform the operation
// and should thus retry.
#[allow(unused)]
pub const ERR_UNAVAILABLE: zx_status_t = -28;

// ======= Permission check errors =======
// ERR_ACCESS_DENIED: The caller did not have permission to perform
// the specified operation.
#[allow(unused)]
pub const ERR_ACCESS_DENIED: zx_status_t = -30;

// ======= Input-output errors =======
// ERR_IO: Otherwise unspecified error occurred during I/O.
#[allow(unused)]
pub const ERR_IO: zx_status_t = -40;

// ERR_REFUSED: The entity the I/O operation is being performed on
// rejected the operation.
// Example: an I2C device NAK'ing a transaction or a disk controller
// rejecting an invalid command.
#[allow(unused)]
pub const ERR_IO_REFUSED: zx_status_t = -41;

// ERR_IO_DATA_INTEGRITY: The data in the operation failed an integrity
// check and is possibly corrupted.
// Example: CRC or Parity error.
#[allow(unused)]
pub const ERR_IO_DATA_INTEGRITY: zx_status_t = -42;

// ERR_IO_DATA_LOSS: The data in the operation is currently unavailable
// and may be permanently lost.
// Example: A disk block is irrecoverably damaged.
#[allow(unused)]
pub const ERR_IO_DATA_LOSS: zx_status_t = -43;

// Filesystem specific errors
#[allow(unused)]
pub const ERR_BAD_PATH: zx_status_t = -50;
#[allow(unused)]
pub const ERR_NOT_DIR: zx_status_t = -51;
#[allow(unused)]
pub const ERR_NOT_FILE: zx_status_t = -52;
// ERR_FILE_BIG: A file exceeds a filesystem-specific size limit.
#[allow(unused)]
pub const ERR_FILE_BIG: zx_status_t = -53;
// ERR_NO_SPACE: Filesystem or device space is exhausted.
#[allow(unused)]
pub const ERR_NO_SPACE: zx_status_t = -54;
<commit_msg>[fuchsia] Update process info struct<commit_after>#![allow(non_camel_case_types, unused)]

use crate::convert::TryInto;
use crate::io;
use crate::mem::MaybeUninit;
use crate::os::raw::c_char;

use libc::{c_int, c_void, size_t};

pub type zx_handle_t = u32;
pub type zx_vaddr_t = usize;
pub type zx_rights_t = u32;
pub type zx_status_t = i32;

pub const ZX_HANDLE_INVALID: zx_handle_t = 0;

pub type zx_time_t = i64;
pub const ZX_TIME_INFINITE: zx_time_t = i64::MAX;

pub type zx_signals_t = u32;

pub const ZX_OBJECT_SIGNAL_3: zx_signals_t = 1 << 3;

pub const ZX_TASK_TERMINATED: zx_signals_t = ZX_OBJECT_SIGNAL_3;

pub const ZX_RIGHT_SAME_RIGHTS: zx_rights_t = 1 << 31;

// The upper four bits gives the minor version.
pub type zx_object_info_topic_t = u32;

pub const ZX_INFO_PROCESS: zx_object_info_topic_t = 3 | (1 << 28);

pub type zx_info_process_flags_t = u32;

pub fn zx_cvt<T>(t: T) -> io::Result<T>
where
    T: TryInto<zx_status_t> + Copy,
{
    if let Ok(status) = TryInto::try_into(t) {
        if status < 0 { Err(io::Error::from_raw_os_error(status)) } else { Ok(t) }
    } else {
        Err(io::Error::last_os_error())
    }
}

// Safe wrapper around zx_handle_t
pub struct Handle {
    raw: zx_handle_t,
}

impl Handle {
    pub fn new(raw: zx_handle_t) -> Handle {
        Handle { raw }
    }

    pub fn raw(&self) -> zx_handle_t {
        self.raw
    }
}

impl Drop for Handle {
    fn drop(&mut self) {
        unsafe {
            zx_cvt(zx_handle_close(self.raw)).expect("Failed to close zx_handle_t");
        }
    }
}

// Returned for topic ZX_INFO_PROCESS
#[derive(Default)]
#[repr(C)]
pub struct zx_info_process_t {
    pub return_code: i64,
    pub start_time: zx_time_t,
    pub flags: zx_info_process_flags_t,
    pub reserved1: u32,
}

extern "C" {
    pub fn zx_job_default() -> zx_handle_t;

    pub fn zx_task_kill(handle: zx_handle_t) -> zx_status_t;

    pub fn zx_handle_close(handle: zx_handle_t) -> zx_status_t;

    pub fn zx_handle_duplicate(
        handle: zx_handle_t,
        rights: zx_rights_t,
        out: *const zx_handle_t,
    ) -> zx_handle_t;

    pub fn zx_object_wait_one(
        handle: zx_handle_t,
        signals: zx_signals_t,
        timeout: zx_time_t,
        pending: *mut zx_signals_t,
    ) -> zx_status_t;

    pub fn zx_object_get_info(
        handle: zx_handle_t,
        topic: u32,
        buffer: *mut c_void,
        buffer_size: size_t,
        actual_size: *mut size_t,
        avail: *mut size_t,
    ) -> zx_status_t;
}

#[derive(Default)]
#[repr(C)]
pub struct fdio_spawn_action_t {
    pub action: u32,
    pub reserved0: u32,
    pub local_fd: i32,
    pub target_fd: i32,
    pub reserved1: u64,
}

extern "C" {
    pub fn fdio_spawn_etc(
        job: zx_handle_t,
        flags: u32,
        path: *const c_char,
        argv: *const *const c_char,
        envp: *const *const c_char,
        action_count: size_t,
        actions: *const fdio_spawn_action_t,
        process: *mut zx_handle_t,
        err_msg: *mut c_char,
    ) -> zx_status_t;

    pub fn fdio_fd_clone(fd: c_int, out_handle: *mut zx_handle_t) -> zx_status_t;
    pub fn fdio_fd_create(handle: zx_handle_t, fd: *mut c_int) -> zx_status_t;
}

// fdio_spawn_etc flags

pub const FDIO_SPAWN_CLONE_JOB: u32 = 0x0001;
pub const FDIO_SPAWN_CLONE_LDSVC: u32 = 0x0002;
pub const FDIO_SPAWN_CLONE_NAMESPACE: u32 = 0x0004;
pub const FDIO_SPAWN_CLONE_STDIO: u32 = 0x0008;
pub const FDIO_SPAWN_CLONE_ENVIRON: u32 = 0x0010;
pub const FDIO_SPAWN_CLONE_UTC_CLOCK: u32 = 0x0020;
pub const FDIO_SPAWN_CLONE_ALL: u32 = 0xFFFF;

// fdio_spawn_etc actions

pub const FDIO_SPAWN_ACTION_CLONE_FD: u32 = 0x0001;
pub const FDIO_SPAWN_ACTION_TRANSFER_FD: u32 = 0x0002;

// Errors

#[allow(unused)]
pub const ERR_INTERNAL: zx_status_t = -1;

// ERR_NOT_SUPPORTED: The operation is not implemented, supported,
// or enabled.
#[allow(unused)]
pub const ERR_NOT_SUPPORTED: zx_status_t = -2;

// ERR_NO_RESOURCES: The system was not able to allocate some resource
// needed for the operation.
#[allow(unused)]
pub const ERR_NO_RESOURCES: zx_status_t = -3;

// ERR_NO_MEMORY: The system was not able to allocate memory needed
// for the operation.
#[allow(unused)]
pub const ERR_NO_MEMORY: zx_status_t = -4;

// ERR_CALL_FAILED: The second phase of zx_channel_call(; did not complete
// successfully.
#[allow(unused)]
pub const ERR_CALL_FAILED: zx_status_t = -5;

// ERR_INTERRUPTED_RETRY: The system call was interrupted, but should be
// retried.  This should not be seen outside of the VDSO.
#[allow(unused)]
pub const ERR_INTERRUPTED_RETRY: zx_status_t = -6;

// ======= Parameter errors =======
// ERR_INVALID_ARGS: an argument is invalid, ex. null pointer
#[allow(unused)]
pub const ERR_INVALID_ARGS: zx_status_t = -10;

// ERR_BAD_HANDLE: A specified handle value does not refer to a handle.
#[allow(unused)]
pub const ERR_BAD_HANDLE: zx_status_t = -11;

// ERR_WRONG_TYPE: The subject of the operation is the wrong type to
// perform the operation.
// Example: Attempting a message_read on a thread handle.
#[allow(unused)]
pub const ERR_WRONG_TYPE: zx_status_t = -12;

// ERR_BAD_SYSCALL: The specified syscall number is invalid.
#[allow(unused)]
pub const ERR_BAD_SYSCALL: zx_status_t = -13;

// ERR_OUT_OF_RANGE: An argument is outside the valid range for this
// operation.
#[allow(unused)]
pub const ERR_OUT_OF_RANGE: zx_status_t = -14;

// ERR_BUFFER_TOO_SMALL: A caller provided buffer is too small for
// this operation.
#[allow(unused)]
pub const ERR_BUFFER_TOO_SMALL: zx_status_t = -15;

// ======= Precondition or state errors =======
// ERR_BAD_STATE: operation failed because the current state of the
// object does not allow it, or a precondition of the operation is
// not satisfied
#[allow(unused)]
pub const ERR_BAD_STATE: zx_status_t = -20;

// ERR_TIMED_OUT: The time limit for the operation elapsed before
// the operation completed.
#[allow(unused)]
pub const ERR_TIMED_OUT: zx_status_t = -21;

// ERR_SHOULD_WAIT: The operation cannot be performed currently but
// potentially could succeed if the caller waits for a prerequisite
// to be satisfied, for example waiting for a handle to be readable
// or writable.
// Example: Attempting to read from a message pipe that has no
// messages waiting but has an open remote will return ERR_SHOULD_WAIT.
// Attempting to read from a message pipe that has no messages waiting
// and has a closed remote end will return ERR_REMOTE_CLOSED.
#[allow(unused)]
pub const ERR_SHOULD_WAIT: zx_status_t = -22;

// ERR_CANCELED: The in-progress operation (e.g., a wait) has been
// // canceled.
#[allow(unused)]
pub const ERR_CANCELED: zx_status_t = -23;

// ERR_PEER_CLOSED: The operation failed because the remote end
// of the subject of the operation was closed.
#[allow(unused)]
pub const ERR_PEER_CLOSED: zx_status_t = -24;

// ERR_NOT_FOUND: The requested entity is not found.
#[allow(unused)]
pub const ERR_NOT_FOUND: zx_status_t = -25;

// ERR_ALREADY_EXISTS: An object with the specified identifier
// already exists.
// Example: Attempting to create a file when a file already exists
// with that name.
#[allow(unused)]
pub const ERR_ALREADY_EXISTS: zx_status_t = -26;

// ERR_ALREADY_BOUND: The operation failed because the named entity
// is already owned or controlled by another entity. The operation
// could succeed later if the current owner releases the entity.
#[allow(unused)]
pub const ERR_ALREADY_BOUND: zx_status_t = -27;

// ERR_UNAVAILABLE: The subject of the operation is currently unable
// to perform the operation.
// Note: This is used when there's no direct way for the caller to
// observe when the subject will be able to perform the operation
// and should thus retry.
#[allow(unused)]
pub const ERR_UNAVAILABLE: zx_status_t = -28;

// ======= Permission check errors =======
// ERR_ACCESS_DENIED: The caller did not have permission to perform
// the specified operation.
#[allow(unused)]
pub const ERR_ACCESS_DENIED: zx_status_t = -30;

// ======= Input-output errors =======
// ERR_IO: Otherwise unspecified error occurred during I/O.
#[allow(unused)]
pub const ERR_IO: zx_status_t = -40;

// ERR_REFUSED: The entity the I/O operation is being performed on
// rejected the operation.
// Example: an I2C device NAK'ing a transaction or a disk controller
// rejecting an invalid command.
#[allow(unused)]
pub const ERR_IO_REFUSED: zx_status_t = -41;

// ERR_IO_DATA_INTEGRITY: The data in the operation failed an integrity
// check and is possibly corrupted.
// Example: CRC or Parity error.
#[allow(unused)]
pub const ERR_IO_DATA_INTEGRITY: zx_status_t = -42;

// ERR_IO_DATA_LOSS: The data in the operation is currently unavailable
// and may be permanently lost.
// Example: A disk block is irrecoverably damaged.
#[allow(unused)]
pub const ERR_IO_DATA_LOSS: zx_status_t = -43;

// Filesystem specific errors
#[allow(unused)]
pub const ERR_BAD_PATH: zx_status_t = -50;
#[allow(unused)]
pub const ERR_NOT_DIR: zx_status_t = -51;
#[allow(unused)]
pub const ERR_NOT_FILE: zx_status_t = -52;
// ERR_FILE_BIG: A file exceeds a filesystem-specific size limit.
#[allow(unused)]
pub const ERR_FILE_BIG: zx_status_t = -53;
// ERR_NO_SPACE: Filesystem or device space is exhausted.
#[allow(unused)]
pub const ERR_NO_SPACE: zx_status_t = -54;
<|endoftext|>


<commit_before><commit_msg>[wip] Shape tests OK<commit_after><|endoftext|>


<commit_before>//
// Copyright 2019 The Project Oak Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

//! Test utilities to help with unit testing of Oak SDK code.

use log::info;

use oak_runtime::ChannelEither;
use protobuf::{Message, ProtobufEnum};

use std::process::Command;
use tempdir::TempDir;

use oak_runtime::proto::manager::{
    ApplicationConfiguration, LogConfiguration, NodeConfiguration, WebAssemblyConfiguration,
};

// TODO(#544)

/// Uses cargo to compile a Rust manifest to Wasm bytes. Compilation is performed in a temporary
/// directory.
pub fn compile_rust_wasm(cargo_path: &str, module_name: &str) -> std::io::Result<Vec<u8>> {
    let temp_dir = TempDir::new("")?;
    Command::new("cargo")
        .args(&["build", "--target=wasm32-unknown-unknown"])
        .args(&[
            format!("--manifest-path={}", cargo_path),
            format!("--target-dir={}", temp_dir.path().display()),
        ])
        .spawn()?
        .wait()?;

    let mut path = temp_dir.into_path();
    path.push("wasm32-unknown-unknown/debug");
    path.push(module_name);

    std::fs::read(path)
}

/// Create a simple configuration with collection of Wasm nodes and a logger node.
///
/// - module_name_wasm: Node name and Wasm bytes.
/// - logger_name: Node name to use for a logger configuration.
/// - initial_node: Initial node to run on launch.
/// - entrypoint: Entrypoint in the initial node to run on launch.
// TODO(#563)
pub fn test_configuration(
    module_name_wasm: Vec<(String, Vec<u8>)>,
    logger_name: &str,
    initial_node: &str,
    entrypoint: &str,
) -> ApplicationConfiguration {
    let mut nodes: Vec<NodeConfiguration> = module_name_wasm
        .into_iter()
        .map(|(name, wasm)| {
            let mut node = NodeConfiguration::new();
            node.set_name(name);
            node.set_wasm_config({
                let mut w = WebAssemblyConfiguration::new();
                w.set_module_bytes(wasm);
                w
            });
            node
        })
        .collect();

    nodes.push({
        let mut log_config = NodeConfiguration::new();
        log_config.set_name(logger_name.to_string());
        log_config.set_log_config(LogConfiguration::new());
        log_config
    });

    let mut config = ApplicationConfiguration::new();
    config.set_node_configs(protobuf::RepeatedField::from_vec(nodes));
    config.set_initial_node_config_name(initial_node.to_string());
    config.set_initial_entrypoint_name(entrypoint.to_string());

    config
}

// TODO(#543)
pub fn grpc_request<R, Q>(
    channel: &oak_runtime::ChannelWriter,
    method_name: &str,
    req: R,
) -> oak::grpc::Result<Q>
where
    R: protobuf::Message,
    Q: protobuf::Message,
{
    // Put the request in a GrpcRequest wrapper and serialize into a message.
    let grpc_req = oak::grpc::encap_request(req, method_name).expect("failed to build GrpcRequest");
    let mut req_msg = oak_runtime::Message {
        data: vec![],
        channels: vec![],
    };

    grpc_req
        .write_to_writer(&mut req_msg.data)
        .expect("failed to serialize GrpcRequest message");

    // Create a new channel to hold the request message.
    let (req_write_half, req_read_half) = oak_runtime::channel::new();
    req_write_half
        .write(req_msg)
        .expect("could not write message");

    // Create a new channel for responses to arrive on and also attach that to the message.
    let (rsp_write_half, rsp_read_half) = oak_runtime::channel::new();

    // Create a notification message and attach the method-invocation specific channels to it.
    let notify_msg = oak_runtime::Message {
        data: vec![],
        channels: vec![
            ChannelEither::Reader(req_read_half),
            ChannelEither::Writer(rsp_write_half),
        ],
    };

    // Send the notification message (with attached handles) into the Node under test.
    channel.write(notify_msg).expect("could not write message");

    // Read the serialized, encapsulated response.
    loop {
        let rsp = match rsp_read_half.read() {
            Ok(Some(r)) => r,
            Ok(None) => {
                info!("no pending gRPC response message; poll again soon");
                std::thread::sleep(std::time::Duration::from_millis(100));
                continue;
            }
            Err(e) => {
                panic!("failed to read from response channel: {:?}", e);
            }
        };
        if rsp.data.is_empty() {
            info!("no pending message; poll again soon");
            std::thread::sleep(std::time::Duration::from_millis(100));
            continue;
        }
        let mut rsp: oak::proto::grpc_encap::GrpcResponse =
            protobuf::parse_from_bytes(&rsp.data).expect("failed to parse GrpcResponse message");
        if !rsp.last {
            panic!("Expected single final response");
        }

        if rsp.get_status().code != oak::grpc::Code::OK.value() {
            return Err(rsp.take_status());
        }
        let rsp: Q = protobuf::parse_from_bytes(&rsp.get_rsp_msg().value)
            .expect("Failed to parse response protobuf message");
        return Ok(rsp);
    }
}
<commit_msg>sdk/oak_tests: expand TODO markers<commit_after>//
// Copyright 2019 The Project Oak Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

//! Test utilities to help with unit testing of Oak SDK code.

use log::info;

use oak_runtime::ChannelEither;
use protobuf::{Message, ProtobufEnum};

use std::process::Command;
use tempdir::TempDir;

use oak_runtime::proto::manager::{
    ApplicationConfiguration, LogConfiguration, NodeConfiguration, WebAssemblyConfiguration,
};

// TODO(#544): re-enable unit tests of SDK functionality

/// Uses cargo to compile a Rust manifest to Wasm bytes. Compilation is performed in a temporary
/// directory.
pub fn compile_rust_wasm(cargo_path: &str, module_name: &str) -> std::io::Result<Vec<u8>> {
    let temp_dir = TempDir::new("")?;
    Command::new("cargo")
        .args(&["build", "--target=wasm32-unknown-unknown"])
        .args(&[
            format!("--manifest-path={}", cargo_path),
            format!("--target-dir={}", temp_dir.path().display()),
        ])
        .spawn()?
        .wait()?;

    let mut path = temp_dir.into_path();
    path.push("wasm32-unknown-unknown/debug");
    path.push(module_name);

    std::fs::read(path)
}

/// Create a simple configuration with collection of Wasm nodes and a logger node.
///
/// - module_name_wasm: Node name and Wasm bytes.
/// - logger_name: Node name to use for a logger configuration.
/// - initial_node: Initial node to run on launch.
/// - entrypoint: Entrypoint in the initial node to run on launch.
// TODO(#563): move this somewhere generic as it's not test-specific
pub fn test_configuration(
    module_name_wasm: Vec<(String, Vec<u8>)>,
    logger_name: &str,
    initial_node: &str,
    entrypoint: &str,
) -> ApplicationConfiguration {
    let mut nodes: Vec<NodeConfiguration> = module_name_wasm
        .into_iter()
        .map(|(name, wasm)| {
            let mut node = NodeConfiguration::new();
            node.set_name(name);
            node.set_wasm_config({
                let mut w = WebAssemblyConfiguration::new();
                w.set_module_bytes(wasm);
                w
            });
            node
        })
        .collect();

    nodes.push({
        let mut log_config = NodeConfiguration::new();
        log_config.set_name(logger_name.to_string());
        log_config.set_log_config(LogConfiguration::new());
        log_config
    });

    let mut config = ApplicationConfiguration::new();
    config.set_node_configs(protobuf::RepeatedField::from_vec(nodes));
    config.set_initial_node_config_name(initial_node.to_string());
    config.set_initial_entrypoint_name(entrypoint.to_string());

    config
}

// TODO(#543): move this to oak_runtime as it's not test-specific
pub fn grpc_request<R, Q>(
    channel: &oak_runtime::ChannelWriter,
    method_name: &str,
    req: R,
) -> oak::grpc::Result<Q>
where
    R: protobuf::Message,
    Q: protobuf::Message,
{
    // Put the request in a GrpcRequest wrapper and serialize into a message.
    let grpc_req = oak::grpc::encap_request(req, method_name).expect("failed to build GrpcRequest");
    let mut req_msg = oak_runtime::Message {
        data: vec![],
        channels: vec![],
    };

    grpc_req
        .write_to_writer(&mut req_msg.data)
        .expect("failed to serialize GrpcRequest message");

    // Create a new channel to hold the request message.
    let (req_write_half, req_read_half) = oak_runtime::channel::new();
    req_write_half
        .write(req_msg)
        .expect("could not write message");

    // Create a new channel for responses to arrive on and also attach that to the message.
    let (rsp_write_half, rsp_read_half) = oak_runtime::channel::new();

    // Create a notification message and attach the method-invocation specific channels to it.
    let notify_msg = oak_runtime::Message {
        data: vec![],
        channels: vec![
            ChannelEither::Reader(req_read_half),
            ChannelEither::Writer(rsp_write_half),
        ],
    };

    // Send the notification message (with attached handles) into the Node under test.
    channel.write(notify_msg).expect("could not write message");

    // Read the serialized, encapsulated response.
    loop {
        let rsp = match rsp_read_half.read() {
            Ok(Some(r)) => r,
            Ok(None) => {
                info!("no pending gRPC response message; poll again soon");
                std::thread::sleep(std::time::Duration::from_millis(100));
                continue;
            }
            Err(e) => {
                panic!("failed to read from response channel: {:?}", e);
            }
        };
        if rsp.data.is_empty() {
            info!("no pending message; poll again soon");
            std::thread::sleep(std::time::Duration::from_millis(100));
            continue;
        }
        let mut rsp: oak::proto::grpc_encap::GrpcResponse =
            protobuf::parse_from_bytes(&rsp.data).expect("failed to parse GrpcResponse message");
        if !rsp.last {
            panic!("Expected single final response");
        }

        if rsp.get_status().code != oak::grpc::Code::OK.value() {
            return Err(rsp.take_status());
        }
        let rsp: Q = protobuf::parse_from_bytes(&rsp.get_rsp_msg().value)
            .expect("Failed to parse response protobuf message");
        return Ok(rsp);
    }
}
<|endoftext|>


<commit_before>extern crate difference;
extern crate file;

use std::path::{Path, PathBuf};
use std::fs::read_dir;

use difference::Changeset;

/// Read file and normalize newlines.
///
/// `rustc` seems to always normalize `\r\n` newlines to `\n`:
///
/// ```
/// let s = "
/// ";
/// assert_eq!(s.as_bytes(), &[10]);
/// ```
///
/// so this should always be correct.
fn read_text(path: &Path) -> String {
    file::get_text(path).unwrap().replace("\r\n", "\n")
}

pub fn dir_tests<F>(paths: &[&str], f: F)
where
    F: Fn(&str) -> String,
{
    for path in collect_tests(paths) {
        let actual = {
            let text = read_text(&path);
            f(&text)
        };
        let path = path.with_extension("txt");
        if !path.exists() {
            println!("\nfile: {}", path.display());
            println!("No .txt file with expected result, creating...");
            file::put_text(&path, actual).unwrap();
            panic!("No expected result")
        }
        let expected = read_text(&path);
        let expected = expected.as_str();
        let actual = actual.as_str();
        assert_equal_text(expected, actual, &path);
    }
}

fn assert_equal_text(expected: &str, actual: &str, path: &Path) {
    if expected != actual {
        print_difference(expected, actual, path)
    }
}

fn collect_tests(paths: &[&str]) -> Vec<PathBuf> {
    paths
        .iter()
        .flat_map(|path| {
            let path = test_data_dir().join(path);
            test_from_dir(&path).into_iter()
        })
        .collect()
}

fn test_from_dir(dir: &Path) -> Vec<PathBuf> {
    let mut acc = Vec::new();
    for file in read_dir(&dir).unwrap() {
        let file = file.unwrap();
        let path = file.path();
        if path.extension().unwrap_or_default() == "rs" {
            acc.push(path);
        }
    }
    acc.sort();
    acc
}

fn print_difference(expected: &str, actual: &str, path: &Path) {
    let dir = project_dir();
    let path = path.strip_prefix(&dir).unwrap_or_else(|_| path);
    if expected.trim() == actual.trim() {
        println!("whitespace difference, rewriting");
        file::put_text(path, actual).unwrap();
    } else {
        let changeset = Changeset::new(actual, expected, "\n");
        print!("{}", changeset);
    }
    println!("file: {}\n", path.display());
    panic!("Comparison failed")
}

fn project_dir() -> PathBuf {
    let dir = env!("CARGO_MANIFEST_DIR");
    PathBuf::from(dir)
        .parent()
        .unwrap()
        .parent()
        .unwrap()
        .to_owned()
}

fn test_data_dir() -> PathBuf {
    project_dir().join("tests/data")
}
<commit_msg>Allow to update files en masse<commit_after>extern crate difference;
extern crate file;

use std::path::{Path, PathBuf};
use std::fs::read_dir;

use difference::Changeset;

/// Read file and normalize newlines.
///
/// `rustc` seems to always normalize `\r\n` newlines to `\n`:
///
/// ```
/// let s = "
/// ";
/// assert_eq!(s.as_bytes(), &[10]);
/// ```
///
/// so this should always be correct.
fn read_text(path: &Path) -> String {
    file::get_text(path).unwrap().replace("\r\n", "\n")
}

pub fn dir_tests<F>(paths: &[&str], f: F)
    where
        F: Fn(&str) -> String,
{
    for path in collect_tests(paths) {
        let actual = {
            let text = read_text(&path);
            f(&text)
        };
        let path = path.with_extension("txt");
        if !path.exists() {
            println!("\nfile: {}", path.display());
            println!("No .txt file with expected result, creating...");
            file::put_text(&path, actual).unwrap();
            panic!("No expected result")
        }
        let expected = read_text(&path);
        let expected = expected.as_str();
        let actual = actual.as_str();
        assert_equal_text(expected, actual, &path);
    }
}

fn assert_equal_text(expected: &str, actual: &str, path: &Path) {
    if expected != actual {
        print_difference(expected, actual, path)
    }
}

fn collect_tests(paths: &[&str]) -> Vec<PathBuf> {
    paths
        .iter()
        .flat_map(|path| {
            let path = test_data_dir().join(path);
            test_from_dir(&path).into_iter()
        })
        .collect()
}

fn test_from_dir(dir: &Path) -> Vec<PathBuf> {
    let mut acc = Vec::new();
    for file in read_dir(&dir).unwrap() {
        let file = file.unwrap();
        let path = file.path();
        if path.extension().unwrap_or_default() == "rs" {
            acc.push(path);
        }
    }
    acc.sort();
    acc
}

const REWRITE: bool = false;

fn print_difference(expected: &str, actual: &str, path: &Path) {
    let dir = project_dir();
    let path = path.strip_prefix(&dir).unwrap_or_else(|_| path);
    if expected.trim() == actual.trim() {
        println!("whitespace difference, rewriting");
        println!("file: {}\n", path.display());
        file::put_text(path, actual).unwrap();
        return;
    }
    if REWRITE {
        println!("rewriting {}", path.display());
        file::put_text(path, actual).unwrap();
        return;
    }
    let changeset = Changeset::new(actual, expected, "\n");
    print!("{}", changeset);
    println!("file: {}\n", path.display());
    panic!("Comparison failed")
}

fn project_dir() -> PathBuf {
    let dir = env!("CARGO_MANIFEST_DIR");
    PathBuf::from(dir)
        .parent()
        .unwrap()
        .parent()
        .unwrap()
        .to_owned()
}

fn test_data_dir() -> PathBuf {
    project_dir().join("tests/data")
}
<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

#![forbid(unsafe_code)]

//! This provides a simple networking substrate between a client and server. It is assumed that all
//! operations are blocking and return only complete blocks of data. The intended use case has the
//! server blocking on read.  Upon receiving a payload during a read, the server should process the
//! payload, write a response, and then block on read again. The client should block on read after
//! performing a write. Upon errors or remote disconnections, the call (read, write) will return an
//! error to let the caller know of the event. A follow up call will result in the service
//! attempting to either reconnect in the case of a client or accept a new client in the case of a
//! server.
//!
//! Internally both the client and server leverage a NetworkStream that communications in blocks
//! where a block is a length prefixed array of bytes.

use libra_logger::{debug, trace};
use std::{
    io::{Read, Write},
    net::{Shutdown, SocketAddr, TcpListener, TcpStream},
    thread, time,
};
use thiserror::Error;

#[derive(Debug, Error)]
pub enum Error {
    #[error("Already called shutdown")]
    AlreadyShutdown,
    #[error("Found data that is too large to decode: {0}")]
    DataTooLarge(usize),
    #[error("Internal network error:")]
    NetworkError(#[from] std::io::Error),
    #[error("No active stream")]
    NoActiveStream,
    #[error("Remote stream cleanly closed")]
    RemoteStreamClosed,
}

pub struct NetworkClient {
    server: SocketAddr,
    stream: Option<NetworkStream>,
}

impl NetworkClient {
    pub fn new(server: SocketAddr) -> Self {
        Self {
            server,
            stream: None,
        }
    }

    /// Blocking read until able to successfully read an entire message
    pub fn read(&mut self) -> Result<Vec<u8>, Error> {
        let stream = self.server()?;
        let result = stream.read();
        if result.is_err() {
            debug!("On read, upstream peer disconnected, setting stream to None");
            self.stream = None;
        }
        result
    }

    /// Shutdown the internal network stream
    pub fn shutdown(&mut self) -> Result<(), Error> {
        debug!("Shutdown called");
        let stream = self.stream.take().ok_or_else(|| Error::NoActiveStream)?;
        stream.shutdown()?;
        Ok(())
    }

    /// Blocking write until able to successfully send an entire message
    pub fn write(&mut self, data: &[u8]) -> Result<(), Error> {
        let stream = self.server()?;
        let result = stream.write(data);
        if result.is_err() {
            debug!("On write, upstream peer disconnected, setting stream to None");
            self.stream = None;
        }
        result
    }

    fn server(&mut self) -> Result<&mut NetworkStream, Error> {
        if self.stream.is_none() {
            debug!("Attempting to connect to upstream {}", self.server);
            let mut stream = TcpStream::connect(self.server);

            let sleeptime = time::Duration::from_millis(100);
            while stream.is_err() {
                thread::sleep(sleeptime);
                stream = TcpStream::connect(self.server);
            }

            let stream = stream?;
            stream.set_nodelay(true)?;
            self.stream = Some(NetworkStream::new(stream));
            debug!("Connection established to upstream {}", self.server);
        }

        self.stream.as_mut().ok_or_else(|| Error::NoActiveStream)
    }
}

pub struct NetworkServer {
    listener: Option<TcpListener>,
    stream: Option<NetworkStream>,
}

impl NetworkServer {
    pub fn new(listen: SocketAddr) -> Self {
        let listener = TcpListener::bind(listen).unwrap();
        Self {
            listener: Some(listener),
            stream: None,
        }
    }

    /// If there isn't already a downstream client, it accepts. Otherwise it
    /// blocks until able to successfully read an entire message
    pub fn read(&mut self) -> Result<Vec<u8>, Error> {
        let stream = self.client()?;
        let result = stream.read();
        if result.is_err() {
            debug!("On read, downstream peer disconnected, setting stream to None");
            self.stream = None;
        }
        result
    }

    /// Shutdown the internal network stream
    pub fn shutdown(&mut self) -> Result<(), Error> {
        debug!("Shutdown called");
        self.listener.take().ok_or_else(|| Error::AlreadyShutdown)?;
        let stream = self.stream.take().ok_or_else(|| Error::NoActiveStream)?;
        stream.shutdown()?;
        Ok(())
    }

    /// If there isn't already a downstream client, it accepts. Otherwise it
    /// blocks until it is able to successfully send an entire message.
    pub fn write(&mut self, data: &[u8]) -> Result<(), Error> {
        let stream = self.client()?;
        let result = stream.write(data);
        if result.is_err() {
            debug!("On write, downstream peer disconnected, setting stream to None");
            self.stream = None;
        }
        result
    }

    fn client(&mut self) -> Result<&mut NetworkStream, Error> {
        if self.stream.is_none() {
            debug!("Waiting for downstream to connect");
            let listener = self
                .listener
                .as_mut()
                .ok_or_else(|| Error::AlreadyShutdown)?;
            let (stream, stream_addr) = listener.accept()?;
            debug!("Connection established with downstream {}", stream_addr);
            stream.set_nodelay(true)?;
            self.stream = Some(NetworkStream::new(stream));
        }

        self.stream.as_mut().ok_or_else(|| Error::NoActiveStream)
    }
}

struct NetworkStream {
    stream: TcpStream,
    buffer: Vec<u8>,
    temp_buffer: [u8; 1024],
}

impl NetworkStream {
    pub fn new(stream: TcpStream) -> Self {
        Self {
            stream,
            buffer: Vec::new(),
            temp_buffer: [0; 1024],
        }
    }

    /// Blocking read until able to successfully read an entire message
    pub fn read(&mut self) -> Result<Vec<u8>, Error> {
        let result = self.read_buffer();
        if !result.is_empty() {
            return Ok(result);
        }

        loop {
            trace!("Attempting to read from stream");
            let read = self.stream.read(&mut self.temp_buffer)?;
            trace!("Read {} bytes from stream", read);
            if read == 0 {
                return Err(Error::RemoteStreamClosed);
            }
            self.buffer.extend(self.temp_buffer[..read].to_vec());
            let result = self.read_buffer();
            if !result.is_empty() {
                trace!("Found a message in the stream");
                return Ok(result);
            }
            trace!("Did not find a message yet, reading again");
        }
    }

    /// Terminate the socket
    pub fn shutdown(&self) -> Result<(), Error> {
        Ok(self.stream.shutdown(Shutdown::Both)?)
    }

    /// Blocking write until able to successfully send an entire message
    pub fn write(&mut self, data: &[u8]) -> Result<(), Error> {
        let u32_max = u32::max_value() as usize;
        if u32_max <= data.len() {
            return Err(Error::DataTooLarge(data.len()));
        }
        let data_len = data.len() as u32;
        trace!("Attempting to write length, {},  to the stream", data_len);
        self.write_all(&data_len.to_le_bytes())?;
        trace!("Attempting to write data, {},  to the stream", data_len);
        self.write_all(data)?;
        trace!(
            "Successfully wrote length, {}, and data to the stream",
            data_len
        );
        Ok(())
    }

    /// Data sent on a TCP socket may not necessarily be delivered at the exact time. So a read may
    /// only include a subset of what was sent. This wraps around the TCP read buffer to ensure
    /// that only full messages are received.
    fn read_buffer(&mut self) -> Vec<u8> {
        if self.buffer.len() < 4 {
            return Vec::new();
        }

        let mut u32_bytes = [0; 4];
        u32_bytes.copy_from_slice(&self.buffer[..4]);
        let data_size = u32::from_le_bytes(u32_bytes) as usize;

        let remaining_data = &self.buffer[4..];
        if remaining_data.len() < data_size {
            return Vec::new();
        }

        let returnable_data = remaining_data[..data_size].to_vec();
        self.buffer = remaining_data[data_size..].to_vec();
        returnable_data
    }

    /// Writing to a TCP socket will take in as much data as the underlying buffer has space for.
    /// This wraps around that buffer and blocks until all the data has been pushed.
    fn write_all(&mut self, data: &[u8]) -> Result<(), Error> {
        let mut unwritten = &data[..];
        let mut total_written = 0;

        while !unwritten.is_empty() {
            let written = self.stream.write(unwritten)?;
            total_written += written;
            unwritten = &data[total_written..];
        }
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use libra_config::utils;
    use std::net::{IpAddr, Ipv4Addr, SocketAddr};

    #[test]
    fn test_ping() {
        let server_port = utils::get_available_port();
        let server_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), server_port);
        let mut server = NetworkServer::new(server_addr);
        let mut client = NetworkClient::new(server_addr);

        let data = vec![0, 1, 2, 3];
        client.write(&data).unwrap();
        let result = server.read().unwrap();
        assert_eq!(data, result);

        let data = vec![4, 5, 6, 7];
        server.write(&data).unwrap();
        let result = client.read().unwrap();
        assert_eq!(data, result);
    }

    #[test]
    fn test_client_shutdown() {
        let server_port = utils::get_available_port();
        let server_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), server_port);
        let mut server = NetworkServer::new(server_addr);
        let mut client = NetworkClient::new(server_addr);

        let data = vec![0, 1, 2, 3];
        client.write(&data).unwrap();
        let result = server.read().unwrap();
        assert_eq!(data, result);

        client.shutdown().unwrap();
        let mut client = NetworkClient::new(server_addr);
        assert!(server.read().is_err());

        let data = vec![4, 5, 6, 7];
        client.write(&data).unwrap();
        let result = server.read().unwrap();
        assert_eq!(data, result);
    }

    #[test]
    fn test_server_shutdown() {
        let server_port = utils::get_available_port();
        let server_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), server_port);
        let mut server = NetworkServer::new(server_addr);
        let mut client = NetworkClient::new(server_addr);

        let data = vec![0, 1, 2, 3];
        client.write(&data).unwrap();
        let result = server.read().unwrap();
        assert_eq!(data, result);

        server.shutdown().unwrap();
        let mut server = NetworkServer::new(server_addr);

        let data = vec![4, 5, 6, 7];
        // We aren't notified immediately that a server has shutdown, but it happens eventually
        while client.write(&data).is_ok() {}

        let data = vec![8, 9, 10, 11];
        client.write(&data).unwrap();
        let result = server.read().unwrap();
        assert_eq!(data, result);
    }

    #[test]
    fn test_write_two_messages_buffered() {
        let server_port = utils::get_available_port();
        let server_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), server_port);
        let mut server = NetworkServer::new(server_addr);
        let mut client = NetworkClient::new(server_addr);
        let data1 = vec![0, 1, 2, 3];
        let data2 = vec![4, 5, 6, 7];
        client.write(&data1).unwrap();
        client.write(&data2).unwrap();
        let result1 = server.read().unwrap();
        let result2 = server.read().unwrap();
        assert_eq!(data1, result1);
        assert_eq!(data2, result2);
    }
}
<commit_msg>[network] improve error reporting during connect<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

#![forbid(unsafe_code)]

//! This provides a simple networking substrate between a client and server. It is assumed that all
//! operations are blocking and return only complete blocks of data. The intended use case has the
//! server blocking on read.  Upon receiving a payload during a read, the server should process the
//! payload, write a response, and then block on read again. The client should block on read after
//! performing a write. Upon errors or remote disconnections, the call (read, write) will return an
//! error to let the caller know of the event. A follow up call will result in the service
//! attempting to either reconnect in the case of a client or accept a new client in the case of a
//! server.
//!
//! Internally both the client and server leverage a NetworkStream that communications in blocks
//! where a block is a length prefixed array of bytes.

use libra_logger::{debug, trace};
use std::{
    io::{Read, Write},
    net::{Shutdown, SocketAddr, TcpListener, TcpStream},
    thread, time,
};
use thiserror::Error;

#[derive(Debug, Error)]
pub enum Error {
    #[error("Already called shutdown")]
    AlreadyShutdown,
    #[error("Found data that is too large to decode: {0}")]
    DataTooLarge(usize),
    #[error("Internal network error:")]
    NetworkError(#[from] std::io::Error),
    #[error("No active stream")]
    NoActiveStream,
    #[error("Remote stream cleanly closed")]
    RemoteStreamClosed,
}

pub struct NetworkClient {
    server: SocketAddr,
    stream: Option<NetworkStream>,
}

impl NetworkClient {
    pub fn new(server: SocketAddr) -> Self {
        Self {
            server,
            stream: None,
        }
    }

    /// Blocking read until able to successfully read an entire message
    pub fn read(&mut self) -> Result<Vec<u8>, Error> {
        let stream = self.server()?;
        let result = stream.read();
        if result.is_err() {
            debug!("On read, upstream peer disconnected, setting stream to None");
            self.stream = None;
        }
        result
    }

    /// Shutdown the internal network stream
    pub fn shutdown(&mut self) -> Result<(), Error> {
        debug!("Shutdown called");
        let stream = self.stream.take().ok_or_else(|| Error::NoActiveStream)?;
        stream.shutdown()?;
        Ok(())
    }

    /// Blocking write until able to successfully send an entire message
    pub fn write(&mut self, data: &[u8]) -> Result<(), Error> {
        let stream = self.server()?;
        let result = stream.write(data);
        if result.is_err() {
            debug!("On write, upstream peer disconnected, setting stream to None");
            self.stream = None;
        }
        result
    }

    fn server(&mut self) -> Result<&mut NetworkStream, Error> {
        if self.stream.is_none() {
            debug!("Attempting to connect to upstream {}", self.server);
            let mut stream = TcpStream::connect(self.server);

            let sleeptime = time::Duration::from_millis(100);
            while let Err(e) = stream {
                debug!("Failed to connect to upstream {} {:?}", self.server, e);
                thread::sleep(sleeptime);
                stream = TcpStream::connect(self.server);
            }

            let stream = stream?;
            stream.set_nodelay(true)?;
            self.stream = Some(NetworkStream::new(stream));
            debug!("Connection established to upstream {}", self.server);
        }

        self.stream.as_mut().ok_or_else(|| Error::NoActiveStream)
    }
}

pub struct NetworkServer {
    listener: Option<TcpListener>,
    stream: Option<NetworkStream>,
}

impl NetworkServer {
    pub fn new(listen: SocketAddr) -> Self {
        let listener = TcpListener::bind(listen).unwrap();
        Self {
            listener: Some(listener),
            stream: None,
        }
    }

    /// If there isn't already a downstream client, it accepts. Otherwise it
    /// blocks until able to successfully read an entire message
    pub fn read(&mut self) -> Result<Vec<u8>, Error> {
        let stream = self.client()?;
        let result = stream.read();
        if result.is_err() {
            debug!("On read, downstream peer disconnected, setting stream to None");
            self.stream = None;
        }
        result
    }

    /// Shutdown the internal network stream
    pub fn shutdown(&mut self) -> Result<(), Error> {
        debug!("Shutdown called");
        self.listener.take().ok_or_else(|| Error::AlreadyShutdown)?;
        let stream = self.stream.take().ok_or_else(|| Error::NoActiveStream)?;
        stream.shutdown()?;
        Ok(())
    }

    /// If there isn't already a downstream client, it accepts. Otherwise it
    /// blocks until it is able to successfully send an entire message.
    pub fn write(&mut self, data: &[u8]) -> Result<(), Error> {
        let stream = self.client()?;
        let result = stream.write(data);
        if result.is_err() {
            debug!("On write, downstream peer disconnected, setting stream to None");
            self.stream = None;
        }
        result
    }

    fn client(&mut self) -> Result<&mut NetworkStream, Error> {
        if self.stream.is_none() {
            debug!("Waiting for downstream to connect");
            let listener = self
                .listener
                .as_mut()
                .ok_or_else(|| Error::AlreadyShutdown)?;
            let (stream, stream_addr) = listener.accept()?;
            debug!("Connection established with downstream {}", stream_addr);
            stream.set_nodelay(true)?;
            self.stream = Some(NetworkStream::new(stream));
        }

        self.stream.as_mut().ok_or_else(|| Error::NoActiveStream)
    }
}

struct NetworkStream {
    stream: TcpStream,
    buffer: Vec<u8>,
    temp_buffer: [u8; 1024],
}

impl NetworkStream {
    pub fn new(stream: TcpStream) -> Self {
        Self {
            stream,
            buffer: Vec::new(),
            temp_buffer: [0; 1024],
        }
    }

    /// Blocking read until able to successfully read an entire message
    pub fn read(&mut self) -> Result<Vec<u8>, Error> {
        let result = self.read_buffer();
        if !result.is_empty() {
            return Ok(result);
        }

        loop {
            trace!("Attempting to read from stream");
            let read = self.stream.read(&mut self.temp_buffer)?;
            trace!("Read {} bytes from stream", read);
            if read == 0 {
                return Err(Error::RemoteStreamClosed);
            }
            self.buffer.extend(self.temp_buffer[..read].to_vec());
            let result = self.read_buffer();
            if !result.is_empty() {
                trace!("Found a message in the stream");
                return Ok(result);
            }
            trace!("Did not find a message yet, reading again");
        }
    }

    /// Terminate the socket
    pub fn shutdown(&self) -> Result<(), Error> {
        Ok(self.stream.shutdown(Shutdown::Both)?)
    }

    /// Blocking write until able to successfully send an entire message
    pub fn write(&mut self, data: &[u8]) -> Result<(), Error> {
        let u32_max = u32::max_value() as usize;
        if u32_max <= data.len() {
            return Err(Error::DataTooLarge(data.len()));
        }
        let data_len = data.len() as u32;
        trace!("Attempting to write length, {},  to the stream", data_len);
        self.write_all(&data_len.to_le_bytes())?;
        trace!("Attempting to write data, {},  to the stream", data_len);
        self.write_all(data)?;
        trace!(
            "Successfully wrote length, {}, and data to the stream",
            data_len
        );
        Ok(())
    }

    /// Data sent on a TCP socket may not necessarily be delivered at the exact time. So a read may
    /// only include a subset of what was sent. This wraps around the TCP read buffer to ensure
    /// that only full messages are received.
    fn read_buffer(&mut self) -> Vec<u8> {
        if self.buffer.len() < 4 {
            return Vec::new();
        }

        let mut u32_bytes = [0; 4];
        u32_bytes.copy_from_slice(&self.buffer[..4]);
        let data_size = u32::from_le_bytes(u32_bytes) as usize;

        let remaining_data = &self.buffer[4..];
        if remaining_data.len() < data_size {
            return Vec::new();
        }

        let returnable_data = remaining_data[..data_size].to_vec();
        self.buffer = remaining_data[data_size..].to_vec();
        returnable_data
    }

    /// Writing to a TCP socket will take in as much data as the underlying buffer has space for.
    /// This wraps around that buffer and blocks until all the data has been pushed.
    fn write_all(&mut self, data: &[u8]) -> Result<(), Error> {
        let mut unwritten = &data[..];
        let mut total_written = 0;

        while !unwritten.is_empty() {
            let written = self.stream.write(unwritten)?;
            total_written += written;
            unwritten = &data[total_written..];
        }
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use libra_config::utils;
    use std::net::{IpAddr, Ipv4Addr, SocketAddr};

    #[test]
    fn test_ping() {
        let server_port = utils::get_available_port();
        let server_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), server_port);
        let mut server = NetworkServer::new(server_addr);
        let mut client = NetworkClient::new(server_addr);

        let data = vec![0, 1, 2, 3];
        client.write(&data).unwrap();
        let result = server.read().unwrap();
        assert_eq!(data, result);

        let data = vec![4, 5, 6, 7];
        server.write(&data).unwrap();
        let result = client.read().unwrap();
        assert_eq!(data, result);
    }

    #[test]
    fn test_client_shutdown() {
        let server_port = utils::get_available_port();
        let server_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), server_port);
        let mut server = NetworkServer::new(server_addr);
        let mut client = NetworkClient::new(server_addr);

        let data = vec![0, 1, 2, 3];
        client.write(&data).unwrap();
        let result = server.read().unwrap();
        assert_eq!(data, result);

        client.shutdown().unwrap();
        let mut client = NetworkClient::new(server_addr);
        assert!(server.read().is_err());

        let data = vec![4, 5, 6, 7];
        client.write(&data).unwrap();
        let result = server.read().unwrap();
        assert_eq!(data, result);
    }

    #[test]
    fn test_server_shutdown() {
        let server_port = utils::get_available_port();
        let server_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), server_port);
        let mut server = NetworkServer::new(server_addr);
        let mut client = NetworkClient::new(server_addr);

        let data = vec![0, 1, 2, 3];
        client.write(&data).unwrap();
        let result = server.read().unwrap();
        assert_eq!(data, result);

        server.shutdown().unwrap();
        let mut server = NetworkServer::new(server_addr);

        let data = vec![4, 5, 6, 7];
        // We aren't notified immediately that a server has shutdown, but it happens eventually
        while client.write(&data).is_ok() {}

        let data = vec![8, 9, 10, 11];
        client.write(&data).unwrap();
        let result = server.read().unwrap();
        assert_eq!(data, result);
    }

    #[test]
    fn test_write_two_messages_buffered() {
        let server_port = utils::get_available_port();
        let server_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), server_port);
        let mut server = NetworkServer::new(server_addr);
        let mut client = NetworkClient::new(server_addr);
        let data1 = vec![0, 1, 2, 3];
        let data2 = vec![4, 5, 6, 7];
        client.write(&data1).unwrap();
        client.write(&data2).unwrap();
        let result1 = server.read().unwrap();
        let result2 = server.read().unwrap();
        assert_eq!(data1, result1);
        assert_eq!(data2, result2);
    }
}
<|endoftext|>


<commit_before>// run-pass
#![allow(dead_code)]

struct List<T> {
    value: T,
    next: Option<Box<List<T>>>,
}

fn to_refs<T>(mut list: (&mut List<T>,)) -> Vec<&mut T> {
    let mut result = vec![];
    loop {
        result.push(&mut (list.0).value);
        if let Some(n) = (list.0).next.as_mut() {
            list.0 = n;
        } else {
            return result;
        }
    }
}

fn main() {}
<commit_msg>add comment outlining test.<commit_after>// run-pass

// Issue #62007: assigning over a field projection (`list.0 = n;` in
// this case) should be able to kill all borrows of `list.0`, so that
// `list.0` can be borrowed on the next iteration through the loop.

#![allow(dead_code)]

struct List<T> {
    value: T,
    next: Option<Box<List<T>>>,
}

fn to_refs<T>(mut list: (&mut List<T>,)) -> Vec<&mut T> {
    let mut result = vec![];
    loop {
        result.push(&mut (list.0).value);
        if let Some(n) = (list.0).next.as_mut() {
            list.0 = n;
        } else {
            return result;
        }
    }
}

fn main() {}
<|endoftext|>