<commit_before>// Copyright 2019 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Interface to the operating system's random number generator.
//!
//! # Supported targets
//!
//! | Target            | Target Triple      | Implementation
//! | ----------------- | ------------------ | --------------
//! | Linux, Android    | `*‑linux‑*`        | [`getrandom`][1] system call if available, otherwise [`/dev/urandom`][2] after successfully polling `/dev/random` |
//! | Windows           | `*‑windows‑*`      | [`BCryptGenRandom`][3] |
//! | macOS             | `*‑apple‑darwin`   | [`getentropy()`][19] if available, otherwise [`/dev/random`][20] (identical to `/dev/urandom`)
//! | iOS               | `*‑apple‑ios`      | [`SecRandomCopyBytes`][4]
//! | FreeBSD           | `*‑freebsd`        | [`getrandom()`][21] if available, otherwise [`kern.arandom`][5]
//! | OpenBSD           | `*‑openbsd`        | [`getentropy`][6]
//! | NetBSD            | `*‑netbsd`         | [`kern.arandom`][7]
//! | Dragonfly BSD     | `*‑dragonfly`      | [`/dev/random`][8]
//! | Solaris, illumos  | `*‑solaris`, `*‑illumos` | [`getrandom()`][9] if available, otherwise [`/dev/random`][10]
//! | Fuchsia OS        | `*‑fuchsia`        | [`cprng_draw`][11]
//! | Redox             | `*‑redox`          | [`rand:`][12]
//! | Haiku             | `*‑haiku`          | `/dev/random` (identical to `/dev/urandom`)
//! | SGX               | `x86_64‑*‑sgx`     | [RDRAND][18]
//! | VxWorks           | `*‑wrs‑vxworks‑*`  | `randABytes` after checking entropy pool initialization with `randSecure`
//! | Emscripten        | `*‑emscripten`     | `/dev/random` (identical to `/dev/urandom`)
//! | WASI              | `wasm32‑wasi`      | [`random_get`][17]
//! | Web Browser       | `wasm32‑*‑unknown` | [`Crypto.getRandomValues()`][14], see [WebAssembly support][16]
//! | Node.js           | `wasm32‑*‑unknown` | [`crypto.randomBytes`][15], see [WebAssembly support][16]
//!
//! There is no blanket implementation on `unix` targets that reads from
//! `/dev/urandom`. This ensures all supported targets are using the recommended
//! interface and respect maximum buffer sizes.
//!
//! Pull Requests that add support for new targets to `getrandom` are always welcome.
//!
//! ## Unsupported targets
//!
//! By default, `getrandom` will not compile on unsupported targets, but certain
//! features allow a user to select a "fallback" implementation if no supported
//! implementation exists.
//!
//! All of the below mechanisms only affect unsupported
//! targets. Supported targets will _always_ use their supported implementations.
//! This prevents a crate from overriding a secure source of randomness
//! (either accidentally or intentionally).
//!
//! ### RDRAND on x86
//!
//! *If the `"rdrand"` Cargo feature is enabled*, `getrandom` will fallback to using
//! the [`RDRAND`][18] instruction to get randomness on `no_std` `x86`/`x86_64`
//! targets. This feature has no effect on other CPU architectures.
//!
//! ### WebAssembly support
//!
//! This crate fully supports the
//! [`wasm32-wasi`](https://github.com/CraneStation/wasi) and
//! [`wasm32-unknown-emscripten`](https://www.hellorust.com/setup/emscripten/)
//! targets. However, the `wasm32-unknown-unknown` target is not automatically
//! supported since, from the target name alone, we cannot deduce which
//! JavaScript interface is in use (or if JavaScript is available at all).
//!
//! Instead, *if the `"js"` Cargo feature is enabled*, this crate will assume
//! that you are building for an environment containing JavaScript, and will
//! call the appropriate methods. Both web browser (main window and Web Workers)
//! and Node.js environments are supported, invoking the methods
//! [described above](#supported-targets) using the
//! [wasm-bindgen](https://github.com/rust-lang/rust-bindgen) toolchain.
//!
//! This feature has no effect on targets other than `wasm32-unknown-unknown`.
//!
//! ### Custom implementations
//!
//! The [`register_custom_getrandom!`] macro allows a user to mark their own
//! function as the backing implementation for [`getrandom`]. See the macro's
//! documentation for more information about writing and registering your own
//! custom implementations.
//!
//! Note that registering a custom implementation only has an effect on targets
//! that would otherwise not compile. Any supported targets (including those
//! using `"rdrand"` and `"js"` Cargo features) continue using their normal
//! implementations even if a function is registered.
//!
//! ### Indirect Dependencies
//!
//! If `getrandom` is not a direct dependency of your crate, you can still
//! enable any of the above fallback behaviors by enabling the relevant
//! feature in your root crate's `Cargo.toml`:
//! ```toml
//! [dependencies]
//! getrandom = { version = "0.2", features = ["js"] }
//! ```
//!
//! ## Early boot
//!
//! Sometimes, early in the boot process, the OS has not collected enough
//! entropy to securely seed its RNG. This is especially common on virtual
//! machines, where standard "random" events are hard to come by.
//!
//! Some operating system interfaces always block until the RNG is securely
//! seeded. This can take anywhere from a few seconds to more than a minute.
//! A few (Linux, NetBSD and Solaris) offer a choice between blocking and
//! getting an error; in these cases, we always choose to block.
//!
//! On Linux (when the `getrandom` system call is not available), reading from
//! `/dev/urandom` never blocks, even when the OS hasn't collected enough
//! entropy yet. To avoid returning low-entropy bytes, we first poll
//! `/dev/random` and only switch to `/dev/urandom` once this has succeeded.
//!
//! ## Error handling
//!
//! We always choose failure over returning insecure "random" bytes. In general,
//! on supported platforms, failure is highly unlikely, though not impossible.
//! If an error does occur, then it is likely that it will occur on every call to
//! `getrandom`, hence after the first successful call one can be reasonably
//! confident that no errors will occur.
//!
//! [1]: http://man7.org/linux/man-pages/man2/getrandom.2.html
//! [2]: http://man7.org/linux/man-pages/man4/urandom.4.html
//! [3]: https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom
//! [4]: https://developer.apple.com/documentation/security/1399291-secrandomcopybytes?language=objc
//! [5]: https://www.freebsd.org/cgi/man.cgi?query=random&sektion=4
//! [6]: https://man.openbsd.org/getentropy.2
//! [7]: https://netbsd.gw.com/cgi-bin/man-cgi?sysctl+7+NetBSD-8.0
//! [8]: https://leaf.dragonflybsd.org/cgi/web-man?command=random&section=4
//! [9]: https://docs.oracle.com/cd/E88353_01/html/E37841/getrandom-2.html
//! [10]: https://docs.oracle.com/cd/E86824_01/html/E54777/random-7d.html
//! [11]: https://fuchsia.dev/fuchsia-src/zircon/syscalls/cprng_draw
//! [12]: https://github.com/redox-os/randd/blob/master/src/main.rs
//! [14]: https://www.w3.org/TR/WebCryptoAPI/#Crypto-method-getRandomValues
//! [15]: https://nodejs.org/api/crypto.html#crypto_crypto_randombytes_size_callback
//! [16]: #webassembly-support
//! [17]: https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#-random_getbuf-pointeru8-buf_len-size---errno
//! [18]: https://software.intel.com/en-us/articles/intel-digital-random-number-generator-drng-software-implementation-guide
//! [19]: https://www.unix.com/man-page/mojave/2/getentropy/
//! [20]: https://www.unix.com/man-page/mojave/4/random/
//! [21]: https://www.freebsd.org/cgi/man.cgi?query=getrandom&manpath=FreeBSD+12.0-stable

#![doc(
    html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png",
    html_favicon_url = "https://www.rust-lang.org/favicon.ico",
    html_root_url = "https://docs.rs/getrandom/0.2.2"
)]
#![no_std]
#![warn(rust_2018_idioms, unused_lifetimes, missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg))]

#[macro_use]
extern crate cfg_if;

mod error;
mod util;
// To prevent a breaking change when targets are added, we always export the
// register_custom_getrandom macro, so old Custom RNG crates continue to build.
#[cfg(feature = "custom")]
mod custom;
#[cfg(feature = "std")]
mod error_impls;

pub use crate::error::Error;

// System-specific implementations.
//
// These should all provide getrandom_inner with the same signature as getrandom.
cfg_if! {
    if #[cfg(any(target_os = "dragonfly", target_os = "emscripten",
                 target_os = "haiku",     target_os = "redox"))] {
        mod util_libc;
        #[path = "use_file.rs"] mod imp;
    } else if #[cfg(any(target_os = "android", target_os = "linux"))] {
        mod util_libc;
        mod use_file;
        #[path = "linux_android.rs"] mod imp;
    } else if #[cfg(any(target_os = "illumos", target_os = "solaris"))] {
        mod util_libc;
        mod use_file;
        #[path = "solaris_illumos.rs"] mod imp;
    } else if #[cfg(any(target_os = "freebsd", target_os = "netbsd"))] {
        mod util_libc;
        #[path = "bsd_arandom.rs"] mod imp;
    } else if #[cfg(target_os = "fuchsia")] {
        #[path = "fuchsia.rs"] mod imp;
    } else if #[cfg(target_os = "ios")] {
        #[path = "ios.rs"] mod imp;
    } else if #[cfg(target_os = "macos")] {
        mod util_libc;
        mod use_file;
        #[path = "macos.rs"] mod imp;
    } else if #[cfg(target_os = "openbsd")] {
        mod util_libc;
        #[path = "openbsd.rs"] mod imp;
    } else if #[cfg(target_os = "wasi")] {
        #[path = "wasi.rs"] mod imp;
    } else if #[cfg(target_os = "vxworks")] {
        mod util_libc;
        #[path = "vxworks.rs"] mod imp;
    } else if #[cfg(windows)] {
        #[path = "windows.rs"] mod imp;
    } else if #[cfg(all(target_arch = "x86_64", target_env = "sgx"))] {
        #[path = "rdrand.rs"] mod imp;
    } else if #[cfg(all(feature = "rdrand",
                        any(target_arch = "x86_64", target_arch = "x86")))] {
        #[path = "rdrand.rs"] mod imp;
    } else if #[cfg(all(feature = "js",
                        target_arch = "wasm32", target_os = "unknown"))] {
        #[path = "js.rs"] mod imp;
    } else if #[cfg(feature = "custom")] {
        use custom as imp;
    } else {
        compile_error!("target is not supported, for more information see: \
                        https://docs.rs/getrandom/#unsupported-targets");
    }
}

/// Fill `dest` with random bytes from the system's preferred random number
/// source.
///
/// This function returns an error on any failure, including partial reads. We
/// make no guarantees regarding the contents of `dest` on error. If `dest` is
/// empty, `getrandom` immediately returns success, making no calls to the
/// underlying operating system.
///
/// Blocking is possible, at least during early boot; see module documentation.
///
/// In general, `getrandom` will be fast enough for interactive usage, though
/// significantly slower than a user-space CSPRNG; for the latter consider
/// [`rand::thread_rng`](https://docs.rs/rand/*/rand/fn.thread_rng.html).
pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
    if dest.is_empty() {
        return Ok(());
    }
    imp::getrandom_inner(dest)
}
<commit_msg>Update docs and compile_errors for wasm32-unknown-unknown (#209)<commit_after>// Copyright 2019 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Interface to the operating system's random number generator.
//!
//! # Supported targets
//!
//! | Target            | Target Triple      | Implementation
//! | ----------------- | ------------------ | --------------
//! | Linux, Android    | `*‑linux‑*`        | [`getrandom`][1] system call if available, otherwise [`/dev/urandom`][2] after successfully polling `/dev/random` |
//! | Windows           | `*‑windows‑*`      | [`BCryptGenRandom`][3] |
//! | macOS             | `*‑apple‑darwin`   | [`getentropy()`][19] if available, otherwise [`/dev/random`][20] (identical to `/dev/urandom`)
//! | iOS               | `*‑apple‑ios`      | [`SecRandomCopyBytes`][4]
//! | FreeBSD           | `*‑freebsd`        | [`getrandom()`][21] if available, otherwise [`kern.arandom`][5]
//! | OpenBSD           | `*‑openbsd`        | [`getentropy`][6]
//! | NetBSD            | `*‑netbsd`         | [`kern.arandom`][7]
//! | Dragonfly BSD     | `*‑dragonfly`      | [`/dev/random`][8]
//! | Solaris, illumos  | `*‑solaris`, `*‑illumos` | [`getrandom()`][9] if available, otherwise [`/dev/random`][10]
//! | Fuchsia OS        | `*‑fuchsia`        | [`cprng_draw`][11]
//! | Redox             | `*‑redox`          | [`rand:`][12]
//! | Haiku             | `*‑haiku`          | `/dev/random` (identical to `/dev/urandom`)
//! | SGX               | `x86_64‑*‑sgx`     | [RDRAND][18]
//! | VxWorks           | `*‑wrs‑vxworks‑*`  | `randABytes` after checking entropy pool initialization with `randSecure`
//! | Emscripten        | `*‑emscripten`     | `/dev/random` (identical to `/dev/urandom`)
//! | WASI              | `wasm32‑wasi`      | [`random_get`][17]
//! | Web Browser       | `wasm32‑*‑unknown` | [`Crypto.getRandomValues()`][14], see [WebAssembly support][16]
//! | Node.js           | `wasm32‑*‑unknown` | [`crypto.randomBytes`][15], see [WebAssembly support][16]
//!
//! There is no blanket implementation on `unix` targets that reads from
//! `/dev/urandom`. This ensures all supported targets are using the recommended
//! interface and respect maximum buffer sizes.
//!
//! Pull Requests that add support for new targets to `getrandom` are always welcome.
//!
//! ## Unsupported targets
//!
//! By default, `getrandom` will not compile on unsupported targets, but certain
//! features allow a user to select a "fallback" implementation if no supported
//! implementation exists.
//!
//! All of the below mechanisms only affect unsupported
//! targets. Supported targets will _always_ use their supported implementations.
//! This prevents a crate from overriding a secure source of randomness
//! (either accidentally or intentionally).
//!
//! ### RDRAND on x86
//!
//! *If the `"rdrand"` Cargo feature is enabled*, `getrandom` will fallback to using
//! the [`RDRAND`][18] instruction to get randomness on `no_std` `x86`/`x86_64`
//! targets. This feature has no effect on other CPU architectures.
//!
//! ### WebAssembly support
//!
//! This crate fully supports the
//! [`wasm32-wasi`](https://github.com/CraneStation/wasi) and
//! [`wasm32-unknown-emscripten`](https://www.hellorust.com/setup/emscripten/)
//! targets. However, the `wasm32-unknown-unknown` target (i.e. the target used
//! by `wasm-pack`) is not automatically
//! supported since, from the target name alone, we cannot deduce which
//! JavaScript interface is in use (or if JavaScript is available at all).
//!
//! Instead, *if the `"js"` Cargo feature is enabled*, this crate will assume
//! that you are building for an environment containing JavaScript, and will
//! call the appropriate methods. Both web browser (main window and Web Workers)
//! and Node.js environments are supported, invoking the methods
//! [described above](#supported-targets) using the
//! [wasm-bindgen](https://github.com/rust-lang/rust-bindgen) toolchain.
//!
//! This feature has no effect on targets other than `wasm32-unknown-unknown`.
//!
//! ### Custom implementations
//!
//! The [`register_custom_getrandom!`] macro allows a user to mark their own
//! function as the backing implementation for [`getrandom`]. See the macro's
//! documentation for more information about writing and registering your own
//! custom implementations.
//!
//! Note that registering a custom implementation only has an effect on targets
//! that would otherwise not compile. Any supported targets (including those
//! using `"rdrand"` and `"js"` Cargo features) continue using their normal
//! implementations even if a function is registered.
//!
//! ### Indirect Dependencies
//!
//! If `getrandom` is not a direct dependency of your crate, you can still
//! enable any of the above fallback behaviors by enabling the relevant
//! feature in your root crate's `Cargo.toml`:
//! ```toml
//! [dependencies]
//! getrandom = { version = "0.2", features = ["js"] }
//! ```
//!
//! ## Early boot
//!
//! Sometimes, early in the boot process, the OS has not collected enough
//! entropy to securely seed its RNG. This is especially common on virtual
//! machines, where standard "random" events are hard to come by.
//!
//! Some operating system interfaces always block until the RNG is securely
//! seeded. This can take anywhere from a few seconds to more than a minute.
//! A few (Linux, NetBSD and Solaris) offer a choice between blocking and
//! getting an error; in these cases, we always choose to block.
//!
//! On Linux (when the `getrandom` system call is not available), reading from
//! `/dev/urandom` never blocks, even when the OS hasn't collected enough
//! entropy yet. To avoid returning low-entropy bytes, we first poll
//! `/dev/random` and only switch to `/dev/urandom` once this has succeeded.
//!
//! ## Error handling
//!
//! We always choose failure over returning insecure "random" bytes. In general,
//! on supported platforms, failure is highly unlikely, though not impossible.
//! If an error does occur, then it is likely that it will occur on every call to
//! `getrandom`, hence after the first successful call one can be reasonably
//! confident that no errors will occur.
//!
//! [1]: http://man7.org/linux/man-pages/man2/getrandom.2.html
//! [2]: http://man7.org/linux/man-pages/man4/urandom.4.html
//! [3]: https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom
//! [4]: https://developer.apple.com/documentation/security/1399291-secrandomcopybytes?language=objc
//! [5]: https://www.freebsd.org/cgi/man.cgi?query=random&sektion=4
//! [6]: https://man.openbsd.org/getentropy.2
//! [7]: https://netbsd.gw.com/cgi-bin/man-cgi?sysctl+7+NetBSD-8.0
//! [8]: https://leaf.dragonflybsd.org/cgi/web-man?command=random&section=4
//! [9]: https://docs.oracle.com/cd/E88353_01/html/E37841/getrandom-2.html
//! [10]: https://docs.oracle.com/cd/E86824_01/html/E54777/random-7d.html
//! [11]: https://fuchsia.dev/fuchsia-src/zircon/syscalls/cprng_draw
//! [12]: https://github.com/redox-os/randd/blob/master/src/main.rs
//! [14]: https://www.w3.org/TR/WebCryptoAPI/#Crypto-method-getRandomValues
//! [15]: https://nodejs.org/api/crypto.html#crypto_crypto_randombytes_size_callback
//! [16]: #webassembly-support
//! [17]: https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#-random_getbuf-pointeru8-buf_len-size---errno
//! [18]: https://software.intel.com/en-us/articles/intel-digital-random-number-generator-drng-software-implementation-guide
//! [19]: https://www.unix.com/man-page/mojave/2/getentropy/
//! [20]: https://www.unix.com/man-page/mojave/4/random/
//! [21]: https://www.freebsd.org/cgi/man.cgi?query=getrandom&manpath=FreeBSD+12.0-stable

#![doc(
    html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png",
    html_favicon_url = "https://www.rust-lang.org/favicon.ico",
    html_root_url = "https://docs.rs/getrandom/0.2.2"
)]
#![no_std]
#![warn(rust_2018_idioms, unused_lifetimes, missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg))]

#[macro_use]
extern crate cfg_if;

mod error;
mod util;
// To prevent a breaking change when targets are added, we always export the
// register_custom_getrandom macro, so old Custom RNG crates continue to build.
#[cfg(feature = "custom")]
mod custom;
#[cfg(feature = "std")]
mod error_impls;

pub use crate::error::Error;

// System-specific implementations.
//
// These should all provide getrandom_inner with the same signature as getrandom.
cfg_if! {
    if #[cfg(any(target_os = "dragonfly", target_os = "emscripten",
                 target_os = "haiku",     target_os = "redox"))] {
        mod util_libc;
        #[path = "use_file.rs"] mod imp;
    } else if #[cfg(any(target_os = "android", target_os = "linux"))] {
        mod util_libc;
        mod use_file;
        #[path = "linux_android.rs"] mod imp;
    } else if #[cfg(any(target_os = "illumos", target_os = "solaris"))] {
        mod util_libc;
        mod use_file;
        #[path = "solaris_illumos.rs"] mod imp;
    } else if #[cfg(any(target_os = "freebsd", target_os = "netbsd"))] {
        mod util_libc;
        #[path = "bsd_arandom.rs"] mod imp;
    } else if #[cfg(target_os = "fuchsia")] {
        #[path = "fuchsia.rs"] mod imp;
    } else if #[cfg(target_os = "ios")] {
        #[path = "ios.rs"] mod imp;
    } else if #[cfg(target_os = "macos")] {
        mod util_libc;
        mod use_file;
        #[path = "macos.rs"] mod imp;
    } else if #[cfg(target_os = "openbsd")] {
        mod util_libc;
        #[path = "openbsd.rs"] mod imp;
    } else if #[cfg(target_os = "wasi")] {
        #[path = "wasi.rs"] mod imp;
    } else if #[cfg(target_os = "vxworks")] {
        mod util_libc;
        #[path = "vxworks.rs"] mod imp;
    } else if #[cfg(windows)] {
        #[path = "windows.rs"] mod imp;
    } else if #[cfg(all(target_arch = "x86_64", target_env = "sgx"))] {
        #[path = "rdrand.rs"] mod imp;
    } else if #[cfg(all(feature = "rdrand",
                        any(target_arch = "x86_64", target_arch = "x86")))] {
        #[path = "rdrand.rs"] mod imp;
    } else if #[cfg(all(feature = "js",
                        target_arch = "wasm32", target_os = "unknown"))] {
        #[path = "js.rs"] mod imp;
    } else if #[cfg(feature = "custom")] {
        use custom as imp;
    } else if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] {
        compile_error!("the wasm32-unknown-unknown target is not supported by \
                        default, you may need to enable the \"js\" feature. \
                        For more information see: \
                        https://docs.rs/getrandom/#webassembly-support");
    } else {
        compile_error!("target is not supported, for more information see: \
                        https://docs.rs/getrandom/#unsupported-targets");
    }
}

/// Fill `dest` with random bytes from the system's preferred random number
/// source.
///
/// This function returns an error on any failure, including partial reads. We
/// make no guarantees regarding the contents of `dest` on error. If `dest` is
/// empty, `getrandom` immediately returns success, making no calls to the
/// underlying operating system.
///
/// Blocking is possible, at least during early boot; see module documentation.
///
/// In general, `getrandom` will be fast enough for interactive usage, though
/// significantly slower than a user-space CSPRNG; for the latter consider
/// [`rand::thread_rng`](https://docs.rs/rand/*/rand/fn.thread_rng.html).
pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
    if dest.is_empty() {
        return Ok(());
    }
    imp::getrandom_inner(dest)
}
<|endoftext|>


<commit_before>extern crate crypto;
extern crate rustc_serialize;

//use crypto::{buffer, blockmodes};
//use crypto::aes::{cbc_decryptor, KeySize as AesKeySize};
use crypto::hmac::Hmac;
use crypto::mac::{Mac};
use crypto::sha1::Sha1;
//use crypto::symmetriccipher::{SymmetricCipherError};
//use crypto::pbkdf2::pbkdf2;
use crypto::util::fixed_time_eq;

use rustc_serialize::hex::{FromHex};
use rustc_serialize::base64::FromBase64;

pub struct Verifier<'a> {
    secret_key: &'a[u8]
}

#[derive(Debug, PartialEq)]
pub enum VerifierError {
    MessageParse,
    InvalidSignature,
    DecodePayload
}

//fn create_derived_key(secret: &str, salt: &str, key_iters: u32, key_sz: u32) -> Vec<u8> {
    //let mut mac = Hmac::new(Sha1::new(), secret.as_bytes());
    //let mut result: Vec<u8> = vec![0; key_sz as usize];

    //pbkdf2(&mut mac, salt.as_bytes(), key_iters, &mut result);

    //result
//}

fn split_by_dashes<'a>(message: &'a String) -> Option<(&'a str, &'a str)> {
    let split: Vec<&str> = message.splitn(2, "--").collect();

    match split.len() {
        2 => Some((split.first().unwrap(), split.last().unwrap())),
        _ => None
    }
}

impl<'a> Verifier<'a> {
    pub fn new(secret: &'a str) -> Verifier<'a> {
        Verifier {
            secret_key: &secret.as_bytes()
        }
    }

    pub fn verify(&self, message: String) -> Result<Vec<u8>, VerifierError> {
        match split_by_dashes(&message) {
            Some((data, signature)) => {
                match self.is_valid_message(data, signature) {
                    true => {
                        match data.from_base64() {
                            Ok(decoded) => Ok(decoded),
                            Err(_) => Err(VerifierError::DecodePayload)
                        }
                    },

                    false => Err(VerifierError::InvalidSignature)
                }
            },

            None => Err(VerifierError::MessageParse)
        }
    }

    pub fn is_valid_message(&self, data: &'a str, signature: &'a str) -> bool {
        match signature.from_hex() {
            Ok(sig_bytes) => {
                let mut mac = Hmac::new(Sha1::new(), &self.secret_key);

                mac.input(data.as_bytes());

                fixed_time_eq(mac.result().code(), &sig_bytes)
            },

            Err(_) => false
        }
    }
}

//pub struct Encryptor {
    //derived_key: Vec<u8>,
    //verifier: Verifier
//}

//impl Encryptor {
    //pub fn new(secret: &str, salt: &str, sign_salt: &str, key_iters: u32, key_sz: u32) -> Encryptor {
        //Encryptor {
            //derived_key: create_derived_key(secret, salt, key_iters, key_sz)
            //verifier: Verifier::new(secret, sign_salt, key_iters, key_sz)
        //}
    //}

    //pub fn decrypt_and_verify(value: String) -> Option<bool> {
        //match value.find("--") {
            //Some(index) => {
                //let (data, signature) = value.split_at(index);

            //}

            //// TODO
            //// first verify data against signature with wrapped Verifier

            //// TODO
            //// base64 decode data, split by '--'
            //// message payload is first, IV is second

            //_ => None
        //}
    //}
//}

#[cfg(test)]
mod tests {
    use Verifier;
    use VerifierError;

    #[test]
    fn is_valid_message_returns_true_for_valid_signatures() {
        let data = "eyJrZXkiOiJ2YWx1ZSJ9";
        let sig  = "fa115453dbb4a28277b1ba07ef4c7437621f5d72";

        let verifier = Verifier::new("helloworld");

        assert_eq!(verifier.is_valid_message(data, sig), true);
    }

    #[test]
    fn is_valid_message_returns_false_for_invalid_signatures() {
        let data = "eyJrZXkiOiJ2YWx1ZSJ9";
        let sig  = "05330518df0e21fb9beec7a71a5f5f951c3f5254";

        let verifier = Verifier::new("helloworld");

        assert_eq!(verifier.is_valid_message(data, sig), false);
    }

    #[test]
    fn is_valid_message_returns_false_for_invalid_messages() {
        let data = "baddata";
        let sig  = "badsig";

        let verifier = Verifier::new("helloworld");

        assert_eq!(verifier.is_valid_message(data, sig), false);
    }

    #[test]
    fn verify_returns_decoded_message_for_valid_signatures() {
        let msg = "eyJrZXkiOiJ2YWx1ZSJ9--fa115453dbb4a28277b1ba07ef4c7437621f5d72".to_string();

        let verifier = Verifier::new("helloworld");

        assert_eq!(verifier.verify(msg).unwrap(), "{\"key\":\"value\"}".as_bytes());
    }

    #[test]
    fn verify_returns_error_for_invalid_signatures() {
        let msg = "eyJrZXkiOiJ2YWx1ZSJ9--05330518df0e21fb9beec7a71a5f5f951c3f5254".to_string();

        let verifier = Verifier::new("helloworld");

        assert_eq!(verifier.verify(msg).unwrap_err(), VerifierError::InvalidSignature);
    }

    #[test]
    fn verify_returns_error_for_invalid_data() {
        let msg = "fa115453dbb4a28277b1ba07ef4c7437621f5d72".to_string();

        let verifier = Verifier::new("helloworld");

        assert_eq!(verifier.verify(msg).unwrap_err(), VerifierError::MessageParse);
    }
}
<commit_msg>Implement Encryptor and clean up some stuff<commit_after>extern crate crypto;
extern crate rustc_serialize;

use crypto::{buffer, blockmodes};
use crypto::aes::{cbc_decryptor, KeySize as AesKeySize};
use crypto::buffer::{WriteBuffer, ReadBuffer, BufferResult};
use crypto::hmac::Hmac;
use crypto::mac::{Mac};
use crypto::sha1::Sha1;
use crypto::pbkdf2::pbkdf2;
use crypto::util::fixed_time_eq;

use rustc_serialize::hex::FromHex;
use rustc_serialize::base64::FromBase64;

use std::str::from_utf8;

pub struct Verifier {
    secret_key: Vec<u8>
}

#[derive(Debug, PartialEq)]
pub enum VerifierError {
    MessageParse,
    InvalidSignature,
    DecodePayload
}

pub fn create_derived_keys(secret: &str, salts: &Vec<&str>, key_iters: u32, key_sz: u32) -> Vec<Vec<u8>> {
    let mut mac = Hmac::new(Sha1::new(), secret.as_bytes());

    salts.iter().map(|salt| {
        let mut result: Vec<u8> = vec![0; key_sz as usize];

        pbkdf2(&mut mac, salt.as_bytes(), key_iters, &mut result);

        result
    }).collect()
}

fn split_by_dashes(message: &str) -> Option<(&str, &str)> {
    let split: Vec<&str> = message.splitn(2, "--").collect();

    match split.len() {
        2 => Some((split.first().unwrap(), split.last().unwrap())),
        _ => None
    }
}

fn split_by_dashes_from_u8_slice(slice: &[u8]) -> Option<(&str, &str)> {
    match from_utf8(slice) {
        Ok(string) => split_by_dashes(string),
        Err(_) => None
    }
}

impl Verifier {
    pub fn new(secret: &str) -> Verifier {
        Verifier {
            secret_key: secret.bytes().collect()
        }
    }

    pub fn verify(&self, message: &str) -> Result<Vec<u8>, VerifierError> {
        match split_by_dashes(&message) {
            Some((encoded_data, signature)) => {
                match self.is_valid_message(encoded_data, signature) {
                    true => {
                        match encoded_data.from_base64() {
                            Ok(decoded) => Ok(decoded),
                            Err(_) => Err(VerifierError::DecodePayload)
                        }
                    },

                    false => Err(VerifierError::InvalidSignature)
                }
            },

            None => Err(VerifierError::MessageParse)
        }
    }

    pub fn is_valid_message(&self, encoded_data: &str, signature: &str) -> bool {
        match signature.from_hex() {
            Ok(sig_bytes) => {
                let mut mac = Hmac::new(Sha1::new(), &self.secret_key);

                mac.input(encoded_data.as_bytes());

                fixed_time_eq(mac.result().code(), &sig_bytes)
            },

            Err(_) => false
        }
    }
}

pub struct Encryptor {
    secret_key: Vec<u8>,
    verifier: Verifier
}

#[derive(Debug, PartialEq)]
pub enum EncryptorError {
    InvalidSignature,
    MessageParse,
    MessageDecode,
    DecryptError
}

impl Encryptor {
    pub fn new(secret: &str, salt: &str, sign_salt: &str, key_iters: u32, key_sz: u32) -> Encryptor {
        let salts = vec![salt, sign_salt];
        let keys = create_derived_keys(secret, &salts, key_iters, key_sz);

        match (keys.first(), keys.last()) {
            (Some(cipher_key), Some(sig_key)) => {
                Encryptor {
                    secret_key: cipher_key.to_vec(),
                    verifier: Verifier {
                        secret_key: sig_key.to_vec()
                    }
                }
            }

            _ => panic!("KDF Failed for Encryptor")
        }
    }

    pub fn decrypt_and_verify(&self, message: &str) -> Result<Vec<u8>, EncryptorError>  {
        match self.verifier.verify(message) {
            Ok(decoded) => {
                match split_by_dashes_from_u8_slice(&decoded) {
                    Some((encoded_cipher_text, encoded_iv)) => {
                        println!("{:?}", encoded_cipher_text);
                        println!("{:?}", encoded_iv);

                        match (encoded_cipher_text.from_base64(), encoded_iv.from_base64()) {
                            (Ok(cipher_text), Ok(iv)) => {
                                let key_sz = AesKeySize::KeySize256; // TODO make configurable
                                let mut decryptor = cbc_decryptor(key_sz, &self.secret_key, &iv, blockmodes::PkcsPadding);

                                let mut final_result: Vec<u8> = Vec::new();
                                let mut buffer = [0; 4096];

                                let mut read_buffer = buffer::RefReadBuffer::new(&cipher_text);
                                let mut write_buffer = buffer::RefWriteBuffer::new(&mut buffer);

                                loop {
                                    match decryptor.decrypt(&mut read_buffer, &mut write_buffer, true) {
                                        Ok(buffer_result) => {
                                            final_result.extend(write_buffer.take_read_buffer().take_remaining().iter().map(|&i| i));

                                            match buffer_result {
                                                BufferResult::BufferUnderflow => break,
                                                BufferResult::BufferOverflow => continue
                                            }
                                        }

                                        Err(_) => return Err(EncryptorError::DecryptError)
                                    }
                                }

                                Ok(final_result)
                            },

                            _ => Err(EncryptorError::MessageDecode)
                        }
                    },

                    None => Err(EncryptorError::MessageParse)
                }
            }

            Err(_) => Err(EncryptorError::InvalidSignature)
        }

    }
}

#[cfg(test)]
mod tests {
    use std::str;

    use Verifier;
    use VerifierError;

    use Encryptor;
    use EncryptorError;

    #[test]
    fn is_valid_message_returns_true_for_valid_signatures() {
        let data = "eyJrZXkiOiJ2YWx1ZSJ9";
        let sig  = "fa115453dbb4a28277b1ba07ef4c7437621f5d72";

        let verifier = Verifier::new("helloworld");

        assert_eq!(verifier.is_valid_message(data, sig), true);
    }

    #[test]
    fn is_valid_message_returns_false_for_invalid_signatures() {
        let data = "eyJrZXkiOiJ2YWx1ZSJ9";
        let sig  = "05330518df0e21fb9beec7a71a5f5f951c3f5254";

        let verifier = Verifier::new("helloworld");

        assert_eq!(verifier.is_valid_message(data, sig), false);
    }

    #[test]
    fn is_valid_message_returns_false_for_invalid_messages() {
        let data = "baddata";
        let sig  = "badsig";

        let verifier = Verifier::new("helloworld");

        assert_eq!(verifier.is_valid_message(data, sig), false);
    }

    #[test]
    fn verify_returns_decoded_message_for_valid_signatures() {
        let msg = "eyJrZXkiOiJ2YWx1ZSJ9--fa115453dbb4a28277b1ba07ef4c7437621f5d72";

        let verifier = Verifier::new("helloworld");

        assert_eq!(verifier.verify(msg).unwrap(), "{\"key\":\"value\"}".as_bytes());
    }

    #[test]
    fn verify_returns_invalid_signature_error_for_wrong_key() {
        let msg = "eyJrZXkiOiJ2YWx1ZSJ9--05330518df0e21fb9beec7a71a5f5f951c3f5254";

        let verifier = Verifier::new("helloworld");

        assert_eq!(verifier.verify(msg).unwrap_err(), VerifierError::InvalidSignature);
    }

    #[test]
    fn verify_returns_message_parse_error_for_empty_message() {
        let msg = "";

        let verifier = Verifier::new("helloworld");

        assert_eq!(verifier.verify(msg).unwrap_err(), VerifierError::MessageParse);
    }

    #[test]
    fn decrypt_and_verify_returns_decoded_message_for_valid_messages() {
        let msg = "c20wSnp6Z1o1U2MyWDVjU3BPeWNNQT09LS1JOWNyR25LdDRpZUUvcmoxVTdoSTNRPT0=--a79c9522355e55bf8e4302c66d8bf5638f1a50ec";

        let encryptor = Encryptor::new("helloworld", "test salt", "test signed salt", 1000, 64);

        assert_eq!(encryptor.decrypt_and_verify(msg).unwrap(), "{\"key\":\"value\"}".as_bytes());
    }

    #[test]
    fn decrypt_and_verify_returns_invalid_signature_error_for_wrong_key(){
        let msg = "SnRXQXFhOE9WSGg2QmVGUDdHdkhNZz09LS1vcjFWcm53VU40YmV0SVcwdWFlK2NRPT0=--c879b51cbd92559d4d684c406b3aaebfbc958e9d";

        let encryptor = Encryptor::new("helloworld", "test salt", "test signed salt", 1000, 64);

        assert_eq!(encryptor.decrypt_and_verify(msg).unwrap_err(), EncryptorError::InvalidSignature);
    }

    #[test]
    fn decrypt_and_verify_returns_invalid_signature_for_empty_message(){
        let msg = "";

        let encryptor = Encryptor::new("helloworld", "test salt", "test signed salt", 1000, 64);

        assert_eq!(encryptor.decrypt_and_verify(msg).unwrap_err(), EncryptorError::InvalidSignature);
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright 2015-2019 Ben Ashford
 *
 * 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.
 */

//! A client for ElasticSearch's REST API
//!
//! The `Client` itself is used as the central access point, from which numerous
//! operations are defined implementing each of the specific ElasticSearch APIs.
//!
//! Warning: at the time of writing the majority of such APIs are currently
//! unimplemented.

#[cfg(test)]
#[macro_use]
extern crate doc_comment;

#[cfg(test)]
doctest!("../README.md");

#[macro_use]
pub mod util;

#[macro_use]
pub mod json;

pub mod error;
pub mod operations;
pub mod query;
pub mod units;

use std::time;

use reqwest::{header::CONTENT_TYPE, RequestBuilder, StatusCode, Url};

use serde::{de::DeserializeOwned, ser::Serialize};

use crate::error::EsError;

pub trait EsResponse {
    fn status_code(&self) -> StatusCode;
    fn read_response<R>(self) -> Result<R, EsError>
    where
        R: DeserializeOwned;
}

impl EsResponse for reqwest::Response {
    fn status_code(&self) -> StatusCode {
        self.status()
    }

    fn read_response<R>(self) -> Result<R, EsError>
    where
        R: DeserializeOwned,
    {
        Ok(serde_json::from_reader(self)?)
    }
}

// The client

/// Process the result of an HTTP request, returning the status code and the
/// `Json` result (if the result had a body) or an `EsError` if there were any
/// errors
///
/// This function is exposed to allow extensions to certain operations, it is
/// not expected to be used by consumers of the library
fn do_req(resp: reqwest::Response) -> Result<reqwest::Response, EsError> {
    let mut resp = resp;
    let status = resp.status();
    match status {
        StatusCode::OK | StatusCode::CREATED | StatusCode::NOT_FOUND => Ok(resp),
        _ => Err(EsError::from(&mut resp)),
    }
}

/// The core of the ElasticSearch client, owns a HTTP connection.
///
/// Each instance of `Client` is reusable, but only one thread can use each one
/// at once.  This will be enforced by the borrow-checker as most methods are
/// defined on `&mut self`.
///
/// To create a `Client`, the URL needs to be specified.
///
/// Each ElasticSearch API operation is defined as a method on `Client`.  Any
/// compulsory parameters must be given as arguments to this method.  It returns
/// an operation builder that can be used to add any optional parameters.
///
/// Finally `send` is called to submit the operation:
///
/// # Examples
///
/// ```
/// use rs_es::Client;
///
/// let mut client = Client::init("http://localhost:9200");
/// ```
///
/// See the specific operations and their builder objects for details.
#[derive(Debug, Clone)]
pub struct Client {
    base_url: Url,
    http_client: reqwest::Client,
}

impl Client {
    fn do_es_op(
        &self,
        url: &str,
        action: impl FnOnce(Url) -> RequestBuilder,
    ) -> Result<reqwest::Response, EsError> {
        let url = self.full_url(url);
        let username = self.base_url.username();
        let mut method = action(url);
        if !username.is_empty() {
            method = method.basic_auth(username, self.base_url.password());
        }
        let result = method.header(CONTENT_TYPE, "application/json").send()?;
        do_req(result)
    }
}

/// Create a HTTP function for the given method (GET/PUT/POST/DELETE)
macro_rules! es_op {
    ($n:ident,$cn:ident) => {
        fn $n(&self, url: &str) -> Result<reqwest::Response, EsError> {
            log::info!("Doing {} on {}", stringify!($n), url);
            self.do_es_op(url, |url| self.http_client.$cn(url.clone()))
        }
    }
}

/// Create a HTTP function with a request body for the given method
/// (GET/PUT/POST/DELETE)
///
macro_rules! es_body_op {
    ($n:ident,$cn:ident) => {
        fn $n<E>(&mut self, url: &str, body: &E) -> Result<reqwest::Response, EsError>
            where E: Serialize {

            log::info!("Doing {} on {}", stringify!($n), url);
            let json_string = serde_json::to_string(body)?;
            log::debug!("With body: {}", &json_string);

            self.do_es_op(url, |url| {
                self.http_client.$cn(url.clone()).body(json_string)
            })
        }
    }
}

impl Client {
    /// Create a new client
    pub fn init(url_s: &str) -> Result<Client, reqwest::UrlError> {
        let url = Url::parse(url_s)?;

        Ok(Client {
            http_client: reqwest::Client::new(),
            base_url: url,
        })
    }

    // TODO - this should be replaced with a builder object, especially if more options are going
    // to be allowed
    pub fn init_with_timeout(
        url_s: &str,
        timeout: Option<time::Duration>,
    ) -> Result<Client, reqwest::UrlError> {
        let url = Url::parse(url_s)?;

        Ok(Client {
            http_client: reqwest::Client::builder()
                .timeout(timeout)
                .build()
                .expect("Failed to build client"),
            base_url: url,
        })
    }

    /// Take a nearly complete ElasticSearch URL, and stick
    /// the URL on the front.
    pub fn full_url(&self, suffix: &str) -> Url {
        self.base_url.join(suffix).expect("Invalid URL created")
    }

    es_op!(get_op, get);

    es_op!(post_op, post);
    es_body_op!(post_body_op, post);
    es_op!(put_op, put);
    es_body_op!(put_body_op, put);
    es_op!(delete_op, delete);
}

#[cfg(test)]
pub mod tests {
    use std::env;

    use serde::{Deserialize, Serialize};
    use serde_json::Value;

    use super::{
        operations::{bulk::Action, search::ScanResult},
        query::Query,
        units::Duration,
        Client,
    };

    // test setup

    pub fn make_client() -> Client {
        let hostname = match env::var("ES_HOST") {
            Ok(val) => val,
            Err(_) => "http://localhost:9200".to_owned(),
        };
        Client::init(&hostname).unwrap()
    }

    #[derive(Debug, Serialize, Deserialize)]
    pub struct TestDocument {
        pub str_field: String,
        pub int_field: i64,
        pub bool_field: bool,
    }

    #[allow(clippy::new_without_default)]
    impl TestDocument {
        pub fn new() -> TestDocument {
            TestDocument {
                str_field: "I am a test".to_owned(),
                int_field: 1,
                bool_field: true,
            }
        }

        pub fn with_str_field(mut self, s: &str) -> TestDocument {
            self.str_field = s.to_owned();
            self
        }

        pub fn with_int_field(mut self, i: i64) -> TestDocument {
            self.int_field = i;
            self
        }

        pub fn with_bool_field(mut self, b: bool) -> TestDocument {
            self.bool_field = b;
            self
        }
    }

    pub fn setup_test_data(client: &mut Client, index_name: &str) {
        // TODO - this should use the Bulk API
        let documents = vec![
            TestDocument::new()
                .with_str_field("Document A123")
                .with_int_field(1),
            TestDocument::new()
                .with_str_field("Document B456")
                .with_int_field(2),
            TestDocument::new()
                .with_str_field("Document 1ABC")
                .with_int_field(3),
        ];
        for doc in documents.iter() {
            client
                .index(index_name, "test_type")
                .with_doc(doc)
                .send()
                .unwrap();
        }
        client.refresh().with_indexes(&[index_name]).send().unwrap();
    }

    pub fn clean_db(mut client: &mut Client, test_idx: &str) {
        let scroll = Duration::minutes(1);
        let mut scan: ScanResult<Value> = match client
            .search_query()
            .with_indexes(&[test_idx])
            .with_query(&Query::build_match_all().build())
            .scan(&scroll)
        {
            Ok(scan) => scan,
            Err(e) => {
                log::warn!("Scan error: {:?}", e);
                return; // Ignore not-found errors
            }
        };

        loop {
            let page = scan.scroll(&mut client, &scroll).unwrap();
            let hits = page.hits.hits;
            if hits.is_empty() {
                break;
            }
            let actions: Vec<Action<()>> = hits
                .into_iter()
                .map(|hit| {
                    Action::delete(hit.id)
                        .with_index(test_idx)
                        .with_doc_type(hit.doc_type)
                })
                .collect();
            client.bulk(&actions).send().unwrap();
        }

        scan.close(&mut client).unwrap();
    }
}
<commit_msg>Use delete_index for cleaning the database for tests<commit_after>/*
 * Copyright 2015-2019 Ben Ashford
 *
 * 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.
 */

//! A client for ElasticSearch's REST API
//!
//! The `Client` itself is used as the central access point, from which numerous
//! operations are defined implementing each of the specific ElasticSearch APIs.
//!
//! Warning: at the time of writing the majority of such APIs are currently
//! unimplemented.

#[cfg(test)]
#[macro_use]
extern crate doc_comment;

#[cfg(test)]
doctest!("../README.md");

#[macro_use]
pub mod util;

#[macro_use]
pub mod json;

pub mod error;
pub mod operations;
pub mod query;
pub mod units;

use std::time;

use reqwest::{header::CONTENT_TYPE, RequestBuilder, StatusCode, Url};

use serde::{de::DeserializeOwned, ser::Serialize};

use crate::error::EsError;

pub trait EsResponse {
    fn status_code(&self) -> StatusCode;
    fn read_response<R>(self) -> Result<R, EsError>
    where
        R: DeserializeOwned;
}

impl EsResponse for reqwest::Response {
    fn status_code(&self) -> StatusCode {
        self.status()
    }

    fn read_response<R>(self) -> Result<R, EsError>
    where
        R: DeserializeOwned,
    {
        Ok(serde_json::from_reader(self)?)
    }
}

// The client

/// Process the result of an HTTP request, returning the status code and the
/// `Json` result (if the result had a body) or an `EsError` if there were any
/// errors
///
/// This function is exposed to allow extensions to certain operations, it is
/// not expected to be used by consumers of the library
fn do_req(resp: reqwest::Response) -> Result<reqwest::Response, EsError> {
    let mut resp = resp;
    let status = resp.status();
    match status {
        StatusCode::OK | StatusCode::CREATED | StatusCode::NOT_FOUND => Ok(resp),
        _ => Err(EsError::from(&mut resp)),
    }
}

/// The core of the ElasticSearch client, owns a HTTP connection.
///
/// Each instance of `Client` is reusable, but only one thread can use each one
/// at once.  This will be enforced by the borrow-checker as most methods are
/// defined on `&mut self`.
///
/// To create a `Client`, the URL needs to be specified.
///
/// Each ElasticSearch API operation is defined as a method on `Client`.  Any
/// compulsory parameters must be given as arguments to this method.  It returns
/// an operation builder that can be used to add any optional parameters.
///
/// Finally `send` is called to submit the operation:
///
/// # Examples
///
/// ```
/// use rs_es::Client;
///
/// let mut client = Client::init("http://localhost:9200");
/// ```
///
/// See the specific operations and their builder objects for details.
#[derive(Debug, Clone)]
pub struct Client {
    base_url: Url,
    http_client: reqwest::Client,
}

impl Client {
    fn do_es_op(
        &self,
        url: &str,
        action: impl FnOnce(Url) -> RequestBuilder,
    ) -> Result<reqwest::Response, EsError> {
        let url = self.full_url(url);
        let username = self.base_url.username();
        let mut method = action(url);
        if !username.is_empty() {
            method = method.basic_auth(username, self.base_url.password());
        }
        let result = method.header(CONTENT_TYPE, "application/json").send()?;
        do_req(result)
    }
}

/// Create a HTTP function for the given method (GET/PUT/POST/DELETE)
macro_rules! es_op {
    ($n:ident,$cn:ident) => {
        fn $n(&self, url: &str) -> Result<reqwest::Response, EsError> {
            log::info!("Doing {} on {}", stringify!($n), url);
            self.do_es_op(url, |url| self.http_client.$cn(url.clone()))
        }
    }
}

/// Create a HTTP function with a request body for the given method
/// (GET/PUT/POST/DELETE)
///
macro_rules! es_body_op {
    ($n:ident,$cn:ident) => {
        fn $n<E>(&mut self, url: &str, body: &E) -> Result<reqwest::Response, EsError>
            where E: Serialize {

            log::info!("Doing {} on {}", stringify!($n), url);
            let json_string = serde_json::to_string(body)?;
            log::debug!("With body: {}", &json_string);

            self.do_es_op(url, |url| {
                self.http_client.$cn(url.clone()).body(json_string)
            })
        }
    }
}

impl Client {
    /// Create a new client
    pub fn init(url_s: &str) -> Result<Client, reqwest::UrlError> {
        let url = Url::parse(url_s)?;

        Ok(Client {
            http_client: reqwest::Client::new(),
            base_url: url,
        })
    }

    // TODO - this should be replaced with a builder object, especially if more options are going
    // to be allowed
    pub fn init_with_timeout(
        url_s: &str,
        timeout: Option<time::Duration>,
    ) -> Result<Client, reqwest::UrlError> {
        let url = Url::parse(url_s)?;

        Ok(Client {
            http_client: reqwest::Client::builder()
                .timeout(timeout)
                .build()
                .expect("Failed to build client"),
            base_url: url,
        })
    }

    /// Take a nearly complete ElasticSearch URL, and stick
    /// the URL on the front.
    pub fn full_url(&self, suffix: &str) -> Url {
        self.base_url.join(suffix).expect("Invalid URL created")
    }

    es_op!(get_op, get);

    es_op!(post_op, post);
    es_body_op!(post_body_op, post);
    es_op!(put_op, put);
    es_body_op!(put_body_op, put);
    es_op!(delete_op, delete);
}

#[cfg(test)]
pub mod tests {
    use std::env;

    use serde::{Deserialize, Serialize};

    use super::{
        Client,
        error::EsError,
    };

    // test setup

    pub fn make_client() -> Client {
        let hostname = match env::var("ES_HOST") {
            Ok(val) => val,
            Err(_) => "http://localhost:9200".to_owned(),
        };
        Client::init(&hostname).unwrap()
    }

    #[derive(Debug, Serialize, Deserialize)]
    pub struct TestDocument {
        pub str_field: String,
        pub int_field: i64,
        pub bool_field: bool,
    }

    #[allow(clippy::new_without_default)]
    impl TestDocument {
        pub fn new() -> TestDocument {
            TestDocument {
                str_field: "I am a test".to_owned(),
                int_field: 1,
                bool_field: true,
            }
        }

        pub fn with_str_field(mut self, s: &str) -> TestDocument {
            self.str_field = s.to_owned();
            self
        }

        pub fn with_int_field(mut self, i: i64) -> TestDocument {
            self.int_field = i;
            self
        }

        pub fn with_bool_field(mut self, b: bool) -> TestDocument {
            self.bool_field = b;
            self
        }
    }

    pub fn setup_test_data(client: &mut Client, index_name: &str) {
        // TODO - this should use the Bulk API
        let documents = vec![
            TestDocument::new()
                .with_str_field("Document A123")
                .with_int_field(1),
            TestDocument::new()
                .with_str_field("Document B456")
                .with_int_field(2),
            TestDocument::new()
                .with_str_field("Document 1ABC")
                .with_int_field(3),
        ];
        for doc in documents.iter() {
            client
                .index(index_name, "test_type")
                .with_doc(doc)
                .send()
                .unwrap();
        }
        client.refresh().with_indexes(&[index_name]).send().unwrap();
    }

    pub fn clean_db(client: &mut Client, test_idx: &str) {
        match client.delete_index(test_idx) {
            // Ignore indices which don't exist yet
            Err(EsError::EsError(ref msg)) if msg == "Unexpected status: 404 Not Found" => {},
            Ok(_) => {},
            e => { e.expect(&format!("Failed to clean db for index {:?}", test_idx)); },
        };
    }
}
<|endoftext|>


<commit_before>#![feature(proc_macro)]

#[macro_use]
extern crate log;
extern crate hayaku_http;
#[macro_use(quick_error)]
extern crate quick_error;
extern crate regex;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;

mod error;
mod trie;

use hayaku_http::{Handler, Method, Request, RequestHandler, ResponseWriter, Status};

use std::collections::HashMap;
use std::io::Write;
use std::rc::Rc;

pub use error::Error;

use trie::TrieNode;

type Tree<T> = HashMap<Method, TrieNode<Rc<RequestHandler<T>>>>;

#[derive(Clone)]
pub struct Router<T: Clone> {
    trees: Tree<T>,
    /// Enables automatic redirection if the current route can't be matched but
    /// a handler for the path with (without) the trailing slash exists.
    /// For example if /foo/ is requested but a route only exists for /foo, the
    /// client is redirected to /foo with http status code 301 for GET requests
    /// and 307 for all other request methods.
    pub redirect_trailing_slash: bool,
    /// If enabled, the router tries to fix the current request path, if no
    /// handle is registered for it.
    /// First superfluous path elements like ../ or // are removed.
    /// Afterwards the router does a case-insensitive lookup of the cleaned path.
    /// If a handle can be found for this route, the router makes a redirection
    /// to the corrected path with status code 301 for GET requests and 307 for
    /// all other request methods.
    /// For example /FOO and /..//Foo could be redirected to /foo.
    /// `redirect_trailing_slash` is independent of this option.
    pub redirect_fixed_path: bool,
    /// If enabled, the router checks if another method is allowed for the
    /// current route, if the current request can not be routed.
    /// If this is the case, the request is answered with `Method Not Allowed`
    /// and HTTP status code 405.
    /// If no other Method is allowed, the request is delegated to the NotFound
    /// handler.
    pub handle_method_not_allowed: bool,
    /// If enabled, the router automatically replies to OPTIONS requests.
    /// Custom OPTIONS handlers take priority over automatic replies.
    pub handle_options: bool,
    /// Configurable handler which is called when no matching route is
    /// found. If it is `None`, the default 404 handler is used.
    not_found: Option<Rc<RequestHandler<T>>>,
}

impl<T: Clone> Router<T> {
    pub fn new() -> Self {
        Router {
            trees: HashMap::new(),
            redirect_trailing_slash: true,
            redirect_fixed_path: true,
            handle_method_not_allowed: true,
            handle_options: true,
            not_found: None,
        }
    }

    pub fn set_not_found_handler(&mut self, handler: Rc<RequestHandler<T>>) {
        self.not_found = Some(handler);
    }

    /// `get` is a shortcut for `Self::handle(Method::Get, path, handle)`.
    pub fn get<S: Into<String>>(&mut self,
                                path: S,
                                handle: Rc<RequestHandler<T>>)
                                -> Result<(), Error> {
        self.handle(Method::Get, path, handle)
    }

    /// `head` is a shortcut for `Self::handle(Method::Head, path, handle)`.
    pub fn head<S: Into<String>>(&mut self,
                                 path: S,
                                 handle: Rc<RequestHandler<T>>)
                                 -> Result<(), Error> {
        self.handle(Method::Head, path, handle)
    }

    /// `options` is a shortcut for `Self::handle(Method::Options, path, handle)`.
    pub fn options<S: Into<String>>(&mut self,
                                    path: S,
                                    handle: Rc<RequestHandler<T>>)
                                    -> Result<(), Error> {
        self.handle(Method::Options, path, handle)
    }

    /// `post` is a shortcut for `Self::handle(Method::Post, path, handle)`.
    pub fn post<S: Into<String>>(&mut self,
                                 path: S,
                                 handle: Rc<RequestHandler<T>>)
                                 -> Result<(), Error> {
        self.handle(Method::Post, path, handle)
    }

    /// `put` is a shortcut for `Self::handle(Method::Put, path, handle)`.
    pub fn put<S: Into<String>>(&mut self,
                                path: S,
                                handle: Rc<RequestHandler<T>>)
                                -> Result<(), Error> {
        self.handle(Method::Put, path, handle)
    }

    /// `patch` is a shortcut for `Self::handle(Method::Patch, path, handle)`.
    pub fn patch<S: Into<String>>(&mut self,
                                  path: S,
                                  handle: Rc<RequestHandler<T>>)
                                  -> Result<(), Error> {
        self.handle(Method::Patch, path, handle)
    }

    /// `delete` is a shortcut for `Self::handle(Method::Delete, path, handle)`.
    pub fn delete<S: Into<String>>(&mut self,
                                   path: S,
                                   handle: Rc<RequestHandler<T>>)
                                   -> Result<(), Error> {
        self.handle(Method::Delete, path, handle)
    }

    /// Registers a new request handle with the given path and method.
    ///
    /// For GET, POST, PUT, PATCH, and DELETE requests the respective
    /// shortcut functions can be used.
    ///
    /// This function is intended for bulk loading and to allow the usage
    /// of less frequently used, non-standardized or custom methods
    /// (e.g. for internal communication with a proxy).
    pub fn handle<S: Into<String>>(&mut self,
                                   method: Method,
                                   path: S,
                                   handle: Rc<RequestHandler<T>>)
                                   -> Result<(), Error> {
        let path = path.into();
        if !path.starts_with('/') {
            return Err(Error::StartWithSlash(path.to_string()));
        }

        if self.trees.get(&method).is_none() {
            let root = TrieNode::new();
            self.trees.insert(method.clone(), root);
        }

        // TODO(nokaa): we should probably not unwrap here.
        // It may be possible for the retrieval to fail, even
        // though we check just before this.
        let mut root = self.trees.get_mut(&method).unwrap();
        root.insert(path, handle);

        Ok(())
    }
}

impl<T: Clone> Handler<T> for Router<T> {
    // Handler makes the router implement the fasthttp.ListenAndServe interface.
    fn handler(&self, req: &Request, res: &mut ResponseWriter, ctx: &T) {
        let path = req.path;
        debug!("path: {}", path);
        let method = &req.method;
        debug!("method: {:?}", method);
        if let Some(root) = self.trees.get(method) {
            match root.get(path) {
                Some((val, map)) => {
                    let serialized = serde_json::to_vec(&map).unwrap();
                    *req.user_data.borrow_mut() = serialized;
                    val.unwrap()(req, res, ctx);
                }
                None => {
                    if self.not_found.is_none() {
                        // Default handler
                        res.status(Status::NotFound);
                        let msg = String::from("404, path \"") + path + "\" not found :(";
                        res.write_all(msg.as_bytes()).unwrap();
                    } else {
                        // We have already checked that self.not_found is not
                        // `None`, so unwrapping should be okay.
                        let handle = self.not_found.clone().unwrap();
                        handle(req, res, ctx);
                    }
                }
            }
        }
    }
}
<commit_msg>Switch to hyper<commit_after>#![feature(proc_macro)]

#[macro_use]
extern crate log;
extern crate hayaku_http;
#[macro_use(quick_error)]
extern crate quick_error;
extern crate regex;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;

mod error;
mod trie;

use hayaku_http::{Handler, Method, Request, RequestHandler, Response, Status};

use std::collections::HashMap;

pub use error::Error;

use trie::TrieNode;

type Tree<T> = HashMap<Method, TrieNode<RequestHandler<T>>>;

#[derive(Clone)]
pub struct Router<T: Clone> {
    trees: Tree<T>,
    /// Enables automatic redirection if the current route can't be matched but
    /// a handler for the path with (without) the trailing slash exists.
    /// For example if /foo/ is requested but a route only exists for /foo, the
    /// client is redirected to /foo with http status code 301 for GET requests
    /// and 307 for all other request methods.
    pub redirect_trailing_slash: bool,
    /// If enabled, the router tries to fix the current request path, if no
    /// handle is registered for it.
    /// First superfluous path elements like ../ or // are removed.
    /// Afterwards the router does a case-insensitive lookup of the cleaned path.
    /// If a handle can be found for this route, the router makes a redirection
    /// to the corrected path with status code 301 for GET requests and 307 for
    /// all other request methods.
    /// For example /FOO and /..//Foo could be redirected to /foo.
    /// `redirect_trailing_slash` is independent of this option.
    pub redirect_fixed_path: bool,
    /// If enabled, the router checks if another method is allowed for the
    /// current route, if the current request can not be routed.
    /// If this is the case, the request is answered with `Method Not Allowed`
    /// and HTTP status code 405.
    /// If no other Method is allowed, the request is delegated to the NotFound
    /// handler.
    pub handle_method_not_allowed: bool,
    /// If enabled, the router automatically replies to OPTIONS requests.
    /// Custom OPTIONS handlers take priority over automatic replies.
    pub handle_options: bool,
    /// Configurable handler which is called when no matching route is
    /// found. If it is `None`, the default 404 handler is used.
    not_found: Option<RequestHandler<T>>,
}

impl<T: Clone> Router<T> {
    pub fn new() -> Self {
        Router {
            trees: HashMap::new(),
            redirect_trailing_slash: true,
            redirect_fixed_path: true,
            handle_method_not_allowed: true,
            handle_options: true,
            not_found: None,
        }
    }

    pub fn set_not_found_handler(&mut self, handler: RequestHandler<T>) {
        self.not_found = Some(handler);
    }

    /// `get` is a shortcut for `Self::handle(Method::Get, path, handle)`.
    pub fn get<S: Into<String>>(&mut self,
                                path: S,
                                handle: RequestHandler<T>)
                                -> Result<(), Error> {
        self.handle(Method::Get, path, handle)
    }

    /// `head` is a shortcut for `Self::handle(Method::Head, path, handle)`.
    pub fn head<S: Into<String>>(&mut self,
                                 path: S,
                                 handle: RequestHandler<T>)
                                 -> Result<(), Error> {
        self.handle(Method::Head, path, handle)
    }

    /// `options` is a shortcut for `Self::handle(Method::Options, path, handle)`.
    pub fn options<S: Into<String>>(&mut self,
                                    path: S,
                                    handle: RequestHandler<T>)
                                    -> Result<(), Error> {
        self.handle(Method::Options, path, handle)
    }

    /// `post` is a shortcut for `Self::handle(Method::Post, path, handle)`.
    pub fn post<S: Into<String>>(&mut self,
                                 path: S,
                                 handle: RequestHandler<T>)
                                 -> Result<(), Error> {
        self.handle(Method::Post, path, handle)
    }

    /// `put` is a shortcut for `Self::handle(Method::Put, path, handle)`.
    pub fn put<S: Into<String>>(&mut self,
                                path: S,
                                handle: RequestHandler<T>)
                                -> Result<(), Error> {
        self.handle(Method::Put, path, handle)
    }

    /// `patch` is a shortcut for `Self::handle(Method::Patch, path, handle)`.
    pub fn patch<S: Into<String>>(&mut self,
                                  path: S,
                                  handle: RequestHandler<T>)
                                  -> Result<(), Error> {
        self.handle(Method::Patch, path, handle)
    }

    /// `delete` is a shortcut for `Self::handle(Method::Delete, path, handle)`.
    pub fn delete<S: Into<String>>(&mut self,
                                   path: S,
                                   handle: RequestHandler<T>)
                                   -> Result<(), Error> {
        self.handle(Method::Delete, path, handle)
    }

    /// Registers a new request handle with the given path and method.
    ///
    /// For GET, POST, PUT, PATCH, and DELETE requests the respective
    /// shortcut functions can be used.
    ///
    /// This function is intended for bulk loading and to allow the usage
    /// of less frequently used, non-standardized or custom methods
    /// (e.g. for internal communication with a proxy).
    pub fn handle<S: Into<String>>(&mut self,
                                   method: Method,
                                   path: S,
                                   handle: RequestHandler<T>)
                                   -> Result<(), Error> {
        let path = path.into();
        if !path.starts_with('/') {
            return Err(Error::StartWithSlash(path.to_string()));
        }

        if self.trees.get(&method).is_none() {
            let root = TrieNode::new();
            self.trees.insert(method.clone(), root);
        }

        // TODO(nokaa): we should probably not unwrap here.
        // It may be possible for the retrieval to fail, even
        // though we check just before this.
        let mut root = self.trees.get_mut(&method).unwrap();
        root.insert(path, handle);

        Ok(())
    }
}

impl<T: Clone> Handler<T> for Router<T> {
    // Handler makes the router implement the fasthttp.ListenAndServe interface.
    fn handler(&self, req: &Request, res: &mut Response, ctx: &T) {
        let path = req.path().unwrap();
        debug!("path: {}", path);
        let method = req.method();
        debug!("method: {:?}", method);
        if let Some(root) = self.trees.get(method) {
            match root.get(path) {
                Some((val, map)) => {
                    let serialized = serde_json::to_vec(&map).unwrap();
                    *req.user_data.borrow_mut() = serialized;
                    val.unwrap()(req, res, ctx);
                }
                None => {
                    if self.not_found.is_none() {
                        // Default handler
                        res.status(Status::NotFound);
                        let msg = String::from("404, path \"") + path + "\" not found :(";
                        res.body(msg.into_bytes())
                    } else {
                        // We have already checked that self.not_found is not
                        // `None`, so unwrapping should be okay.
                        let handle = self.not_found.clone().unwrap();
                        handle(req, res, ctx);
                    }
                }
            }
        } else {
            // TODO(nokaa): We want to send a different error than 404
            // for this case. In this case we have an incorrect method being
            // used.
            if self.not_found.is_none() {
                // Default handler
                res.status(Status::NotFound);
                let msg = String::from("404, path \"") + path + "\" not found :(";
                res.body(msg.into_bytes());
            } else {
                // We have already checked that self.not_found is not
                // `None`, so unwrapping should be okay.
                let handle = self.not_found.clone().unwrap();
                handle(req, res, ctx);
            }
        }
    }
}
<|endoftext|>


<commit_before>// ProtocolTypes is the list of all supported protocols.
#[derive(PartialEq, Clone, Copy, Debug)]
pub enum ProtocolTypes {
    IP4,
    TCP,
    UDP,
    DCCP,
    IP6,
    SCTP,
    UTP,
    UDT,
    IPFS,
    HTTP,
    HTTPS,
    ONION,
}

impl ProtocolTypes {
    pub fn to_code(&self) -> u16 {
        match *self {
            ProtocolTypes::IP4   => 4,
	    ProtocolTypes::TCP   => 6,
	    ProtocolTypes::UDP   => 17,
	    ProtocolTypes::DCCP  => 33,
	    ProtocolTypes::IP6   => 41,
	    ProtocolTypes::SCTP  => 132,
	    ProtocolTypes::UTP   => 301,
	    ProtocolTypes::UDT   => 302,
	    ProtocolTypes::IPFS  => 421,
	    ProtocolTypes::HTTP  => 480,
	    ProtocolTypes::HTTPS => 443,
	    ProtocolTypes::ONION => 444,
        }
    }

    // Try to convert a u16 to a protocol
    pub fn from_code(b: u16) -> Option<ProtocolTypes> {
        match b {
            4u16   => Some(ProtocolTypes::IP4),
	    6u16   => Some(ProtocolTypes::TCP),
	    17u16  => Some(ProtocolTypes::UDP),
	    33u16  => Some(ProtocolTypes::DCCP),
	    41u16  => Some(ProtocolTypes::IP6),
	    132u16 => Some(ProtocolTypes::SCTP),
	    301u16 => Some(ProtocolTypes::UTP),
	    302u16 => Some(ProtocolTypes::UDT),
	    421u16 => Some(ProtocolTypes::IPFS),
	    480u16 => Some(ProtocolTypes::HTTP),
	    443u16 => Some(ProtocolTypes::HTTPS),
	    444u16 => Some(ProtocolTypes::ONION),
            _ => None
        }
    }

    pub fn to_size(&self) -> isize {
        match *self {
            ProtocolTypes::IP4   => 32,
	    ProtocolTypes::TCP   => 16,
	    ProtocolTypes::UDP   => 16,
	    ProtocolTypes::DCCP  => 16,
	    ProtocolTypes::IP6   => 128,
	    ProtocolTypes::SCTP  => 16,
	    ProtocolTypes::UTP   => 0,
	    ProtocolTypes::UDT   => 0,
	    ProtocolTypes::IPFS  => -1,
	    ProtocolTypes::HTTP  => 0,
	    ProtocolTypes::HTTPS => 0,
	    ProtocolTypes::ONION => 80,
        }
    }

    pub fn to_name(&self) -> String {
        match *self {
            ProtocolTypes::IP4   => "ip4".to_string(),
	    ProtocolTypes::TCP   => "tcp".to_string(),
	    ProtocolTypes::UDP   => "udp".to_string(),
	    ProtocolTypes::DCCP  => "dccp".to_string(),
	    ProtocolTypes::IP6   => "ip6".to_string(),
	    ProtocolTypes::SCTP  => "sctp".to_string(),
	    ProtocolTypes::UTP   => "utp".to_string(),
	    ProtocolTypes::UDT   => "udt".to_string(),
	    ProtocolTypes::IPFS  => "ipfs".to_string(),
	    ProtocolTypes::HTTP  => "http".to_string(),
	    ProtocolTypes::HTTPS => "https".to_string(),
	    ProtocolTypes::ONION => "onion".to_string(),
        }
    }
}

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

    #[test]
    fn protocols_to_u16() {
        assert_eq!(ProtocolTypes::TCP.to_code(), 6u16);
    }

    #[test]
    fn protocols_from_u16() {
        assert_eq!(ProtocolTypes::from_code(6u16), Some(ProtocolTypes::TCP));
        assert_eq!(ProtocolTypes::from_code(455u16), None);
    }

    #[test]
    fn protocols_to_size() {
        assert_eq!(ProtocolTypes::TCP.to_size(), 16);
    }

    #[test]
    fn protocols_to_name() {
        assert_eq!(ProtocolTypes::TCP.to_name(), "tcp");
    }
}
<commit_msg>Parse strings into bytes, I have no idea what I'm doing..<commit_after>use std::mem;

// ProtocolTypes is the list of all supported protocols.
#[derive(PartialEq, Clone, Copy, Debug)]
pub enum ProtocolTypes {
    IP4,
    TCP,
    UDP,
    DCCP,
    IP6,
    SCTP,
    UTP,
    UDT,
    IPFS,
    HTTP,
    HTTPS,
    ONION,
}

impl ProtocolTypes {
    pub fn to_code(&self) -> u16 {
        match *self {
            ProtocolTypes::IP4   => 4,
	    ProtocolTypes::TCP   => 6,
	    ProtocolTypes::UDP   => 17,
	    ProtocolTypes::DCCP  => 33,
	    ProtocolTypes::IP6   => 41,
	    ProtocolTypes::SCTP  => 132,
	    ProtocolTypes::UTP   => 301,
	    ProtocolTypes::UDT   => 302,
	    ProtocolTypes::IPFS  => 421,
	    ProtocolTypes::HTTP  => 480,
	    ProtocolTypes::HTTPS => 443,
	    ProtocolTypes::ONION => 444,
        }
    }

    // Try to convert a u16 to a protocol
    pub fn from_code(b: u16) -> Option<ProtocolTypes> {
        match b {
            4u16   => Some(ProtocolTypes::IP4),
	    6u16   => Some(ProtocolTypes::TCP),
	    17u16  => Some(ProtocolTypes::UDP),
	    33u16  => Some(ProtocolTypes::DCCP),
	    41u16  => Some(ProtocolTypes::IP6),
	    132u16 => Some(ProtocolTypes::SCTP),
	    301u16 => Some(ProtocolTypes::UTP),
	    302u16 => Some(ProtocolTypes::UDT),
	    421u16 => Some(ProtocolTypes::IPFS),
	    480u16 => Some(ProtocolTypes::HTTP),
	    443u16 => Some(ProtocolTypes::HTTPS),
	    444u16 => Some(ProtocolTypes::ONION),
            _ => None
        }
    }

    pub fn to_size(&self) -> isize {
        match *self {
            ProtocolTypes::IP4   => 32,
	    ProtocolTypes::TCP   => 16,
	    ProtocolTypes::UDP   => 16,
	    ProtocolTypes::DCCP  => 16,
	    ProtocolTypes::IP6   => 128,
	    ProtocolTypes::SCTP  => 16,
	    ProtocolTypes::UTP   => 0,
	    ProtocolTypes::UDT   => 0,
	    ProtocolTypes::IPFS  => -1,
	    ProtocolTypes::HTTP  => 0,
	    ProtocolTypes::HTTPS => 0,
	    ProtocolTypes::ONION => 80,
        }
    }

    pub fn to_name(&self) -> String {
        match *self {
            ProtocolTypes::IP4   => "ip4".to_string(),
	    ProtocolTypes::TCP   => "tcp".to_string(),
	    ProtocolTypes::UDP   => "udp".to_string(),
	    ProtocolTypes::DCCP  => "dccp".to_string(),
	    ProtocolTypes::IP6   => "ip6".to_string(),
	    ProtocolTypes::SCTP  => "sctp".to_string(),
	    ProtocolTypes::UTP   => "utp".to_string(),
	    ProtocolTypes::UDT   => "udt".to_string(),
	    ProtocolTypes::IPFS  => "ipfs".to_string(),
	    ProtocolTypes::HTTP  => "http".to_string(),
	    ProtocolTypes::HTTPS => "https".to_string(),
	    ProtocolTypes::ONION => "onion".to_string(),
        }
    }

    // Try to convert a string to a protocol
    pub fn from_name(s: &str) -> Option<ProtocolTypes> {
        match s {
            "ip4"   => Some(ProtocolTypes::IP4),
	    "tcp"   => Some(ProtocolTypes::TCP),
	    "udp"   => Some(ProtocolTypes::UDP),
	    "dccp"  => Some(ProtocolTypes::DCCP),
	    "ip6"   => Some(ProtocolTypes::IP6),
	    "sctp"  => Some(ProtocolTypes::SCTP),
	    "utp"   => Some(ProtocolTypes::UTP),
	    "udt"   => Some(ProtocolTypes::UDT),
	    "ipfs"  => Some(ProtocolTypes::IPFS),
	    "http"  => Some(ProtocolTypes::HTTP),
	    "https" => Some(ProtocolTypes::HTTPS),
	    "onion" => Some(ProtocolTypes::ONION),
            _ => None
        }
    }
}

pub struct Multiaddr {
    bytes: Vec<u8>
}

impl Multiaddr {
    /// Create a new multiaddr based on a string representation, like
    /// `/ip4/127.0.0.1/udp/1234`.
    pub fn new(address: &str) -> Multiaddr {
        let address = address.to_string();
        let mut bytes: Vec<u8>= vec![];

        for part in address.split("/") {
            if let Some(protocol) = ProtocolTypes::from_name(part) {
                // Is there a way to do this safely?
                unsafe {
                    let code_u16 = protocol.to_code();
                    let code = mem::transmute::<u16, [u8; 2]>(code_u16);

                    if code_u16 > 255 {
                        let mut code = code.iter().cloned().collect();
                        bytes.append(&mut code);
                    } else {
                        bytes.push(code[0])
                    }
                }

                println!("Got protocol {}", protocol.to_name());
                println!("With size {}", protocol.to_size());
            } else if part.len() > 0 {
                let mut part_bytes = part.to_string().into_bytes();
                bytes.append(&mut part_bytes);
                println!("got part {}, {}", part.len(), part);
            }
        }

        Multiaddr {
            bytes: bytes,
        }
    }
}

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

    #[test]
    fn protocols_to_u16() {
        assert_eq!(ProtocolTypes::TCP.to_code(), 6u16);
    }

    #[test]
    fn protocols_from_u16() {
        assert_eq!(ProtocolTypes::from_code(6u16), Some(ProtocolTypes::TCP));
        assert_eq!(ProtocolTypes::from_code(455u16), None);
    }

    #[test]
    fn protocols_to_size() {
        assert_eq!(ProtocolTypes::TCP.to_size(), 16);
    }

    #[test]
    fn protocols_to_name() {
        assert_eq!(ProtocolTypes::TCP.to_name(), "tcp");
    }

    #[test]
    fn multiaddr_from_string() {
        let mut target = vec![4u8];
        target.append(&mut "127.0.0.1".to_string().into_bytes());
        target.push(17u8);
        target.append(&mut "1234".to_string().into_bytes());

        assert_eq!(
            Multiaddr::new("/ip4/127.0.0.1/udp/1234").bytes,
            target
         );
    }
}
<|endoftext|>


<commit_before>//! Bindings to [libFuzzer](http://llvm.org/docs/LibFuzzer.html): a runtime for
//! coverage-guided fuzzing.
//!
//! See [the `cargo-fuzz`
//! guide](https://rust-fuzz.github.io/book/cargo-fuzz.html) for a usage
//! tutorial.
//!
//! The main export of this crate is [the `fuzz_target!`
//! macro](./macro.fuzz_target.html), which allows you to define targets for
//! libFuzzer to exercise.

#![deny(missing_docs, missing_debug_implementations)]

pub use arbitrary;

extern "C" {
    // We do not actually cross the FFI bound here.
    #[allow(improper_ctypes)]
    fn rust_fuzzer_test_input(input: &[u8]);
}

#[doc(hidden)]
#[export_name = "LLVMFuzzerTestOneInput"]
pub fn test_input_wrap(data: *const u8, size: usize) -> i32 {
    let test_input = ::std::panic::catch_unwind(|| unsafe {
        let data_slice = ::std::slice::from_raw_parts(data, size);
        rust_fuzzer_test_input(data_slice);
    });
    if test_input.err().is_some() {
        // hopefully the custom panic hook will be called before and abort the
        // process before the stack frames are unwinded.
        ::std::process::abort();
    }
    0
}

#[doc(hidden)]
#[export_name = "LLVMFuzzerInitialize"]
pub fn initialize(_argc: *const isize, _argv: *const *const *const u8) -> isize {
    // Registers a panic hook that aborts the process before unwinding.
    // It is useful to abort before unwinding so that the fuzzer will then be
    // able to analyse the process stack frames to tell different bugs appart.
    //
    // HACK / FIXME: it would be better to use `-C panic=abort` but it's currently
    // impossible to build code using compiler plugins with this flag.
    // We will be able to remove this code when
    // https://github.com/rust-lang/cargo/issues/5423 is fixed.
    let default_hook = ::std::panic::take_hook();
    ::std::panic::set_hook(Box::new(move |panic_info| {
        default_hook(panic_info);
        ::std::process::abort();
    }));
    0
}

/// Define a fuzz target.
///
/// ## Example
///
/// This example takes a `&[u8]` slice and attempts to parse it. The parsing
/// might fail and return an `Err`, but it shouldn't ever panic or segfault.
///
/// ```no_run
/// #![no_main]
///
/// use libfuzzer_sys::fuzz_target;
///
/// // Note: `|input|` is short for `|input: &[u8]|`.
/// fuzz_target!(|input| {
///     let _result: Result<_, _> = my_crate::parse(input);
/// });
/// # mod my_crate { pub fn parse(_: &[u8]) -> Result<(), ()> { unimplemented!() } }
/// ```
///
/// ## Arbitrary Input Types
///
/// The input is a `&[u8]` slice by default, but you can take arbitrary input
/// types, as long as the type implements [the `arbitrary` crate's `Arbitrary`
/// trait](https://docs.rs/arbitrary/*/arbitrary/trait.Arbitrary.html) (which is
/// also re-exported as `libfuzzer_sys::arbitrary::Arbitrary` for convenience).
///
/// For example, if you wanted to take an arbitrary RGB color, you could do the
/// following:
///
/// ```no_run
/// #![no_main]
///
/// use libfuzzer_sys::{arbitrary::{Arbitrary, Unstructured}, fuzz_target};
///
/// #[derive(Debug)]
/// pub struct Rgb {
///     r: u8,
///     g: u8,
///     b: u8,
/// }
///
/// impl Arbitrary for Rgb {
///     fn arbitrary<U>(raw: &mut U) -> Result<Self, U::Error>
///     where
///         U: Unstructured + ?Sized
///     {
///         let mut buf = [0; 3];
///         raw.fill_buffer(&mut buf)?;
///         let r = buf[0];
///         let g = buf[1];
///         let b = buf[2];
///         Ok(Rgb { r, g, b })
///     }
/// }
///
/// // Write a fuzz target that works with RGB colors instead of raw bytes.
/// fuzz_target!(|color: Rgb| {
///     my_crate::convert_color(color);
/// });
/// # mod my_crate { fn convert_color(_: super::Rgb) {} }
#[macro_export]
macro_rules! fuzz_target {
    (|$bytes:ident| $body:block) => {
        /// Auto-generated function
        #[no_mangle]
        pub extern "C" fn rust_fuzzer_test_input($bytes: &[u8]) {
            // When `RUST_LIBFUZZER_DEBUG_PATH` is set, write the debug
            // formatting of the input to that file. This is only intended for
            // `cargo fuzz`'s use!
            if let Ok(path) = std::env::var("RUST_LIBFUZZER_DEBUG_PATH") {
                use std::io::Write;
                let mut file = std::fs::File::create(path)
                    .expect("failed to create `RUST_LIBFUZZER_DEBUG_PATH` file");
                writeln!(&mut file, "{:?}", $bytes)
                    .expect("failed to write to `RUST_LIBFUZZER_DEBUG_PATH` file");
                return;
            }

            $body
        }
    };

    (|$data:ident: &[u8]| $body:block) => {
        fuzz_target!(|$data| $body);
    };

    (|$data:ident: $dty: ty| $body:block) => {
        #[no_mangle]
        pub extern "C" fn rust_fuzzer_test_input(bytes: &[u8]) {
            use libfuzzer_sys::arbitrary::{Arbitrary, Unstructured};

            // Early exit if we don't have enough bytes for the `Arbitrary`
            // implementation. This helps the fuzzer avoid exploring all the
            // different not-enough-input-bytes paths inside the `Arbitrary`
            // implementation. Additionally, it exits faster, letting the fuzzer
            // get to longer inputs that actually lead to interesting executions
            // quicker.
            if bytes.len() < <$dty as Arbitrary>::size_hint(0).0 {
                return;
            }

            let mut u = Unstructured::new(bytes);
            let data = <$dty as Arbitrary>::arbitrary_take_rest(u);

            // When `RUST_LIBFUZZER_DEBUG_PATH` is set, write the debug
            // formatting of the input to that file. This is only intended for
            // `cargo fuzz`'s use!
            if let Ok(path) = std::env::var("RUST_LIBFUZZER_DEBUG_PATH") {
                use std::io::Write;
                let mut file = std::fs::File::create(path)
                    .expect("failed to create `RUST_LIBFUZZER_DEBUG_PATH` file");
                (match data {
                    Ok(data) => writeln!(&mut file, "{:#?}", data),
                    Err(err) => writeln!(&mut file, "Arbitrary Error: {}", err),
                })
                .expect("failed to write to `RUST_LIBFUZZER_DEBUG_PATH` file");
                return;
            }

            let $data = match data {
                Ok(d) => d,
                Err(_) => return,
            };

            $body
        }
    };
}
<commit_msg>Document `rust_fuzzer_test_input`<commit_after>//! Bindings to [libFuzzer](http://llvm.org/docs/LibFuzzer.html): a runtime for
//! coverage-guided fuzzing.
//!
//! See [the `cargo-fuzz`
//! guide](https://rust-fuzz.github.io/book/cargo-fuzz.html) for a usage
//! tutorial.
//!
//! The main export of this crate is [the `fuzz_target!`
//! macro](./macro.fuzz_target.html), which allows you to define targets for
//! libFuzzer to exercise.

#![deny(missing_docs, missing_debug_implementations)]

pub use arbitrary;

extern "C" {
    // We do not actually cross the FFI bound here.
    #[allow(improper_ctypes)]
    fn rust_fuzzer_test_input(input: &[u8]);
}

#[doc(hidden)]
#[export_name = "LLVMFuzzerTestOneInput"]
pub fn test_input_wrap(data: *const u8, size: usize) -> i32 {
    let test_input = ::std::panic::catch_unwind(|| unsafe {
        let data_slice = ::std::slice::from_raw_parts(data, size);
        rust_fuzzer_test_input(data_slice);
    });
    if test_input.err().is_some() {
        // hopefully the custom panic hook will be called before and abort the
        // process before the stack frames are unwinded.
        ::std::process::abort();
    }
    0
}

#[doc(hidden)]
#[export_name = "LLVMFuzzerInitialize"]
pub fn initialize(_argc: *const isize, _argv: *const *const *const u8) -> isize {
    // Registers a panic hook that aborts the process before unwinding.
    // It is useful to abort before unwinding so that the fuzzer will then be
    // able to analyse the process stack frames to tell different bugs appart.
    //
    // HACK / FIXME: it would be better to use `-C panic=abort` but it's currently
    // impossible to build code using compiler plugins with this flag.
    // We will be able to remove this code when
    // https://github.com/rust-lang/cargo/issues/5423 is fixed.
    let default_hook = ::std::panic::take_hook();
    ::std::panic::set_hook(Box::new(move |panic_info| {
        default_hook(panic_info);
        ::std::process::abort();
    }));
    0
}

/// Define a fuzz target.
///
/// ## Example
///
/// This example takes a `&[u8]` slice and attempts to parse it. The parsing
/// might fail and return an `Err`, but it shouldn't ever panic or segfault.
///
/// ```no_run
/// #![no_main]
///
/// use libfuzzer_sys::fuzz_target;
///
/// // Note: `|input|` is short for `|input: &[u8]|`.
/// fuzz_target!(|input| {
///     let _result: Result<_, _> = my_crate::parse(input);
/// });
/// # mod my_crate { pub fn parse(_: &[u8]) -> Result<(), ()> { unimplemented!() } }
/// ```
///
/// ## Arbitrary Input Types
///
/// The input is a `&[u8]` slice by default, but you can take arbitrary input
/// types, as long as the type implements [the `arbitrary` crate's `Arbitrary`
/// trait](https://docs.rs/arbitrary/*/arbitrary/trait.Arbitrary.html) (which is
/// also re-exported as `libfuzzer_sys::arbitrary::Arbitrary` for convenience).
///
/// For example, if you wanted to take an arbitrary RGB color, you could do the
/// following:
///
/// ```no_run
/// #![no_main]
///
/// use libfuzzer_sys::{arbitrary::{Arbitrary, Unstructured}, fuzz_target};
///
/// #[derive(Debug)]
/// pub struct Rgb {
///     r: u8,
///     g: u8,
///     b: u8,
/// }
///
/// impl Arbitrary for Rgb {
///     fn arbitrary<U>(raw: &mut U) -> Result<Self, U::Error>
///     where
///         U: Unstructured + ?Sized
///     {
///         let mut buf = [0; 3];
///         raw.fill_buffer(&mut buf)?;
///         let r = buf[0];
///         let g = buf[1];
///         let b = buf[2];
///         Ok(Rgb { r, g, b })
///     }
/// }
///
/// // Write a fuzz target that works with RGB colors instead of raw bytes.
/// fuzz_target!(|color: Rgb| {
///     my_crate::convert_color(color);
/// });
/// # mod my_crate { fn convert_color(_: super::Rgb) {} }
#[macro_export]
macro_rules! fuzz_target {
    (|$bytes:ident| $body:block) => {
        /// Auto-generated function
        #[no_mangle]
        pub extern "C" fn rust_fuzzer_test_input($bytes: &[u8]) {
            // When `RUST_LIBFUZZER_DEBUG_PATH` is set, write the debug
            // formatting of the input to that file. This is only intended for
            // `cargo fuzz`'s use!
            if let Ok(path) = std::env::var("RUST_LIBFUZZER_DEBUG_PATH") {
                use std::io::Write;
                let mut file = std::fs::File::create(path)
                    .expect("failed to create `RUST_LIBFUZZER_DEBUG_PATH` file");
                writeln!(&mut file, "{:?}", $bytes)
                    .expect("failed to write to `RUST_LIBFUZZER_DEBUG_PATH` file");
                return;
            }

            $body
        }
    };

    (|$data:ident: &[u8]| $body:block) => {
        fuzz_target!(|$data| $body);
    };

    (|$data:ident: $dty: ty| $body:block) => {
        /// Auto-generated function
        #[no_mangle]
        pub extern "C" fn rust_fuzzer_test_input(bytes: &[u8]) {
            use libfuzzer_sys::arbitrary::{Arbitrary, Unstructured};

            // Early exit if we don't have enough bytes for the `Arbitrary`
            // implementation. This helps the fuzzer avoid exploring all the
            // different not-enough-input-bytes paths inside the `Arbitrary`
            // implementation. Additionally, it exits faster, letting the fuzzer
            // get to longer inputs that actually lead to interesting executions
            // quicker.
            if bytes.len() < <$dty as Arbitrary>::size_hint(0).0 {
                return;
            }

            let mut u = Unstructured::new(bytes);
            let data = <$dty as Arbitrary>::arbitrary_take_rest(u);

            // When `RUST_LIBFUZZER_DEBUG_PATH` is set, write the debug
            // formatting of the input to that file. This is only intended for
            // `cargo fuzz`'s use!
            if let Ok(path) = std::env::var("RUST_LIBFUZZER_DEBUG_PATH") {
                use std::io::Write;
                let mut file = std::fs::File::create(path)
                    .expect("failed to create `RUST_LIBFUZZER_DEBUG_PATH` file");
                (match data {
                    Ok(data) => writeln!(&mut file, "{:#?}", data),
                    Err(err) => writeln!(&mut file, "Arbitrary Error: {}", err),
                })
                .expect("failed to write to `RUST_LIBFUZZER_DEBUG_PATH` file");
                return;
            }

            let $data = match data {
                Ok(d) => d,
                Err(_) => return,
            };

            $body
        }
    };
}
<|endoftext|>


<commit_before>//! Derive a builder for a struct
//!
//! Use in combiantion with [custom_derive][custom_derive].
//!
//! [custom_derive]: https://crates.io/crates/custom_derive
//!
//! # Examples
//!
//! ```rust
//! #[macro_use] extern crate custom_derive;
//! #[macro_use] extern crate derive_builder;
//!
//! custom_derive!{
//!     #[derive(Debug, PartialEq, Default, Builder)]
//!     struct Lorem {
//!         ipsum: String,
//!         dolor: i32,
//!     }
//! }
//!
//! fn main() {
//!     let x = Lorem::default().ipsum("sit").dolor(42);
//!     assert_eq!(x, Lorem { ipsum: "sit".into(), dolor: 42 });
//! }
//! ```
//!
//! ## Generic structs
//!
//! ```rust
//! #[macro_use] extern crate custom_derive;
//! #[macro_use] extern crate derive_builder;
//!
//! custom_derive!{
//!     #[derive(Debug, PartialEq, Default, Builder)]
//!     struct GenLorem<T> {
//!         ipsum: String,
//!         dolor: T,
//!     }
//! }
//!
//! fn main() {
//!     let x = GenLorem::default().ipsum("sit").dolor(42);
//!     assert_eq!(x, GenLorem { ipsum: "sit".into(), dolor: 42 });
//! }
//! ```
//!
//! ## Gotchas
//!
//! - Tuple structs and unit structs are not supported as they have no field
//!   names.
//! - When defining a generic struct, you cannot use `VALUE` as a generic
//!   parameter as this is what all setters are using.

#[cfg(test)]
#[macro_use] extern crate custom_derive;

mod parse_struct;
mod derive_builder;
<commit_msg>cleanup<commit_after>//! Derive a builder for a struct
//!
//! Use in combiantion with [custom_derive][custom_derive].
//!
//! [custom_derive]: https://crates.io/crates/custom_derive
//!
//! # Examples
//!
//! ```rust
//! #[macro_use] extern crate custom_derive;
//! #[macro_use] extern crate derive_builder;
//!
//! custom_derive!{
//!     #[derive(Debug, PartialEq, Default, Builder)]
//!     struct Lorem {
//!         ipsum: String,
//!         dolor: i32,
//!     }
//! }
//!
//! fn main() {
//!     let x = Lorem::default().ipsum("sit").dolor(42);
//!     assert_eq!(x, Lorem { ipsum: "sit".into(), dolor: 42 });
//! }
//! ```
//!
//! ## Generic structs
//!
//! ```rust
//! #[macro_use] extern crate custom_derive;
//! #[macro_use] extern crate derive_builder;
//!
//! custom_derive!{
//!     #[derive(Debug, PartialEq, Default, Builder)]
//!     struct GenLorem<T> {
//!         ipsum: String,
//!         dolor: T,
//!     }
//! }
//!
//! fn main() {
//!     let x = GenLorem::default().ipsum("sit").dolor(42);
//!     assert_eq!(x, GenLorem { ipsum: "sit".into(), dolor: 42 });
//! }
//! ```
//!
//! ## Gotchas
//!
//! - Tuple structs and unit structs are not supported as they have no field
//!   names.
//! - When defining a generic struct, you cannot use `VALUE` as a generic
//!   parameter as this is what all setters are using.

mod parse_struct;
mod derive_builder;
<|endoftext|>