<commit_before>extern crate futures;
extern crate env_logger;
extern crate futuremio;
extern crate ssl;

#[macro_use]
extern crate cfg_if;

use std::io::{Read, Write};

use futures::{Future, Task, Poll};
use futures::stream::Stream;
use futures::io::{self, Ready};

macro_rules! t {
    ($e:expr) => (match $e {
        Ok(e) => e,
        Err(e) => panic!("{} failed with {:?}", stringify!($e), e),
    })
}

cfg_if! {
    if #[cfg(any(feature = "force-openssl",
                 all(not(target_os = "macos"),
                     not(target_os = "windows"))))] {
        extern crate openssl;

        use openssl::x509::X509;
        use openssl::crypto::pkey::PKey;

        use ssl::backend::openssl::ServerContextExt;
        use ssl::backend::openssl::ClientContextExt;

        fn server_cx() -> std::io::Result<ssl::ServerContext> {
            let cert = include_bytes!("server.crt");
            let cert = t!(X509::from_pem(&mut &cert[..]));
            let key = include_bytes!("server.key");
            let key = t!(PKey::private_key_from_pem(&mut &key[..]));
            ssl::ServerContext::new(&cert, &key)
        }

        fn configure_client(cx: &mut ssl::ClientContext) {
            let cert = include_bytes!("server.crt");
            let cert = t!(X509::from_pem(&mut &cert[..]));
            let ssl = cx.ssl_context_mut();
            t!(ssl.set_certificate(&cert));
        }
    } else if #[cfg(target_os = "macos")] {
        extern crate security_framework;

        use std::env;
        use std::fs;
        use std::sync::{Once, ONCE_INIT};

        use security_framework::certificate::SecCertificate;
        use security_framework::import_export::Pkcs12ImportOptions;
        use security_framework::keychain::SecKeychain;
        use security_framework::os::macos::import_export::Pkcs12ImportOptionsExt;
        use security_framework::os::macos::keychain::CreateOptions;
        use security_framework::os::macos::keychain::SecKeychainExt;

        use ssl::backend::secure_transport::ServerContextExt;
        use ssl::backend::secure_transport::ClientContextExt;

        static INIT: Once = ONCE_INIT;

        fn server_cx() -> std::io::Result<ssl::ServerContext> {
            let path = t!(env::current_exe());
            let path = path.parent().unwrap();
            let path = path.join("test.keychain");

            INIT.call_once(|| {
                let _ = fs::remove_file(&path);
                t!(CreateOptions::new()
                    .password("test")
                    .create(&path));
            });

            let mut keychain = t!(SecKeychain::open(&path));
            t!(keychain.unlock(Some("test")));

            let mut options = Pkcs12ImportOptions::new();
            Pkcs12ImportOptionsExt::keychain(&mut options, keychain);
            let identities = t!(options.passphrase("foobar")
                                       .import(include_bytes!("server.p12")));
            assert!(identities.len() == 1);
            ssl::ServerContext::new(&identities[0].identity, &identities[0].cert_chain)
        }

        fn configure_client(cx: &mut ssl::ClientContext) {
            let der = include_bytes!("server.der");
            let cert = SecCertificate::from_der(der).unwrap();
            t!(cx.anchor_certificates(&[cert]));
        }
    } else {
    }
}

fn client_cx() -> std::io::Result<ssl::ClientContext> {
    let mut cx = try!(ssl::ClientContext::new());
    configure_client(&mut cx);
    Ok(cx)
}

const AMT: u64 = 128 * 1024;

#[test]
fn client_to_server() {
    drop(env_logger::init());
    let mut l = t!(futuremio::Loop::new());

    // Create a server listening on a port, then figure out what that port is
    let srv = l.handle().tcp_listen(&"127.0.0.1:0".parse().unwrap());
    let srv = t!(l.run(srv));
    let addr = t!(srv.local_addr());

    // Create a future to accept one socket, connect the ssl stream, and then
    // read all the data from it.
    let socket = srv.incoming().take(1).collect();
    let received = socket.map(|mut socket| {
        socket.remove(0).0
    }).and_then(move |socket| {
        t!(server_cx()).handshake(socket)
    }).and_then(|socket| {
        io::read_to_end(socket, Vec::new())
    });

    // Create a future to connect to our server, connect the ssl stream, and
    // then write a bunch of data to it.
    let client = l.handle().tcp_connect(&addr);
    let sent = client.and_then(move |socket| {
        t!(client_cx()).handshake("localhost", socket)
    }).and_then(|socket| {
        io::copy(io::take(std::io::repeat(9), AMT), socket)
    });

    // Finally, run everything!
    let (amt, data) = t!(l.run(sent.join(received)));
    assert_eq!(amt, AMT);
    assert!(data == vec![9; amt as usize]);
}

#[test]
fn server_to_client() {
    drop(env_logger::init());
    let mut l = t!(futuremio::Loop::new());

    // Create a server listening on a port, then figure out what that port is
    let srv = l.handle().tcp_listen(&"127.0.0.1:0".parse().unwrap());
    let srv = t!(l.run(srv));
    let addr = t!(srv.local_addr());

    let socket = srv.incoming().take(1).collect();
    let sent = socket.map(|mut socket| {
        socket.remove(0).0
    }).and_then(move |socket| {
        t!(server_cx()).handshake(socket)
    }).and_then(|socket| {
        io::copy(io::take(std::io::repeat(9), AMT), socket)
    });

    let client = l.handle().tcp_connect(&addr);
    let received = client.and_then(move |socket| {
        t!(client_cx()).handshake("localhost", socket)
    }).and_then(|socket| {
        io::read_to_end(socket, Vec::new())
    });

    // Finally, run everything!
    let (amt, data) = t!(l.run(sent.join(received)));
    assert_eq!(amt, AMT);
    assert!(data == vec![9; amt as usize]);
}

struct OneByte<S> {
    inner: S,
}

impl<S> Stream for OneByte<S>
    where S: Stream<Item=Ready, Error=std::io::Error>
{
    type Item = Ready;
    type Error = std::io::Error;

    fn poll(&mut self, task: &mut Task) -> Poll<Option<Ready>, std::io::Error> {
        self.inner.poll(task)
    }

    fn schedule(&mut self, task: &mut Task) {
        self.inner.schedule(task)
    }
}

impl<S: Read> Read for OneByte<S> {
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
        self.inner.read(&mut buf[..1])
    }
}

impl<S: Write> Write for OneByte<S> {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        self.inner.write(&buf[..1])
    }

    fn flush(&mut self) -> std::io::Result<()> {
        self.inner.flush()
    }
}

#[test]
fn one_byte_at_a_time() {
    drop(env_logger::init());
    let mut l = t!(futuremio::Loop::new());

    let srv = l.handle().tcp_listen(&"127.0.0.1:0".parse().unwrap());
    let srv = t!(l.run(srv));
    let addr = t!(srv.local_addr());

    let socket = srv.incoming().take(1).collect();
    let sent = socket.map(|mut socket| {
        socket.remove(0).0
    }).and_then(move |socket| {
        t!(server_cx()).handshake(OneByte { inner: socket })
    }).and_then(|socket| {
        io::copy(io::take(std::io::repeat(9), AMT), socket)
    });

    let client = l.handle().tcp_connect(&addr);
    let received = client.and_then(move |socket| {
        t!(client_cx()).handshake("localhost", OneByte { inner: socket })
    }).and_then(|socket| {
        io::read_to_end(socket, Vec::new())
    });

    let (amt, data) = t!(l.run(sent.join(received)));
    assert_eq!(amt, AMT);
    assert!(data == vec![9; amt as usize]);
}
<commit_msg>Use set_CA_file on the OpenSSL client<commit_after>extern crate futures;
extern crate env_logger;
extern crate futuremio;
extern crate ssl;

#[macro_use]
extern crate cfg_if;

use std::io::{Read, Write};

use futures::{Future, Task, Poll};
use futures::stream::Stream;
use futures::io::{self, Ready};

macro_rules! t {
    ($e:expr) => (match $e {
        Ok(e) => e,
        Err(e) => panic!("{} failed with {:?}", stringify!($e), e),
    })
}

cfg_if! {
    if #[cfg(any(feature = "force-openssl",
                 all(not(target_os = "macos"),
                     not(target_os = "windows"))))] {
        extern crate openssl;

        use std::fs::File;
        use std::env;
        use std::sync::{Once, ONCE_INIT};

        use openssl::x509::X509;
        use openssl::crypto::pkey::PKey;

        use ssl::backend::openssl::ServerContextExt;
        use ssl::backend::openssl::ClientContextExt;

        fn server_cx() -> std::io::Result<ssl::ServerContext> {
            let cert = include_bytes!("server.crt");
            let cert = t!(X509::from_pem(&mut &cert[..]));
            let key = include_bytes!("server.key");
            let key = t!(PKey::private_key_from_pem(&mut &key[..]));
            ssl::ServerContext::new(&cert, &key)
        }

        static INIT: Once = ONCE_INIT;

        fn configure_client(cx: &mut ssl::ClientContext) {
            let path = t!(env::current_exe());
            let path = path.parent().unwrap().join("server.crt");
            INIT.call_once(|| {
                t!(t!(File::create(&path)).write_all(include_bytes!("server.crt")));
            });
            let ssl = cx.ssl_context_mut();
            t!(ssl.set_CA_file(&path));
        }
    } else if #[cfg(target_os = "macos")] {
        extern crate security_framework;

        use std::env;
        use std::fs;
        use std::sync::{Once, ONCE_INIT};

        use security_framework::certificate::SecCertificate;
        use security_framework::import_export::Pkcs12ImportOptions;
        use security_framework::keychain::SecKeychain;
        use security_framework::os::macos::import_export::Pkcs12ImportOptionsExt;
        use security_framework::os::macos::keychain::CreateOptions;
        use security_framework::os::macos::keychain::SecKeychainExt;

        use ssl::backend::secure_transport::ServerContextExt;
        use ssl::backend::secure_transport::ClientContextExt;

        static INIT: Once = ONCE_INIT;

        fn server_cx() -> std::io::Result<ssl::ServerContext> {
            let path = t!(env::current_exe());
            let path = path.parent().unwrap();
            let path = path.join("test.keychain");

            INIT.call_once(|| {
                let _ = fs::remove_file(&path);
                t!(CreateOptions::new()
                    .password("test")
                    .create(&path));
            });

            let mut keychain = t!(SecKeychain::open(&path));
            t!(keychain.unlock(Some("test")));

            let mut options = Pkcs12ImportOptions::new();
            Pkcs12ImportOptionsExt::keychain(&mut options, keychain);
            let identities = t!(options.passphrase("foobar")
                                       .import(include_bytes!("server.p12")));
            assert!(identities.len() == 1);
            ssl::ServerContext::new(&identities[0].identity, &identities[0].cert_chain)
        }

        fn configure_client(cx: &mut ssl::ClientContext) {
            let der = include_bytes!("server.der");
            let cert = SecCertificate::from_der(der).unwrap();
            t!(cx.anchor_certificates(&[cert]));
        }
    } else {
    }
}

fn client_cx() -> std::io::Result<ssl::ClientContext> {
    let mut cx = try!(ssl::ClientContext::new());
    configure_client(&mut cx);
    Ok(cx)
}

const AMT: u64 = 128 * 1024;

#[test]
fn client_to_server() {
    drop(env_logger::init());
    let mut l = t!(futuremio::Loop::new());

    // Create a server listening on a port, then figure out what that port is
    let srv = l.handle().tcp_listen(&"127.0.0.1:0".parse().unwrap());
    let srv = t!(l.run(srv));
    let addr = t!(srv.local_addr());

    // Create a future to accept one socket, connect the ssl stream, and then
    // read all the data from it.
    let socket = srv.incoming().take(1).collect();
    let received = socket.map(|mut socket| {
        socket.remove(0).0
    }).and_then(move |socket| {
        t!(server_cx()).handshake(socket)
    }).and_then(|socket| {
        io::read_to_end(socket, Vec::new())
    });

    // Create a future to connect to our server, connect the ssl stream, and
    // then write a bunch of data to it.
    let client = l.handle().tcp_connect(&addr);
    let sent = client.and_then(move |socket| {
        t!(client_cx()).handshake("localhost", socket)
    }).and_then(|socket| {
        io::copy(io::take(std::io::repeat(9), AMT), socket)
    });

    // Finally, run everything!
    let (amt, data) = t!(l.run(sent.join(received)));
    assert_eq!(amt, AMT);
    assert!(data == vec![9; amt as usize]);
}

#[test]
fn server_to_client() {
    drop(env_logger::init());
    let mut l = t!(futuremio::Loop::new());

    // Create a server listening on a port, then figure out what that port is
    let srv = l.handle().tcp_listen(&"127.0.0.1:0".parse().unwrap());
    let srv = t!(l.run(srv));
    let addr = t!(srv.local_addr());

    let socket = srv.incoming().take(1).collect();
    let sent = socket.map(|mut socket| {
        socket.remove(0).0
    }).and_then(move |socket| {
        t!(server_cx()).handshake(socket)
    }).and_then(|socket| {
        io::copy(io::take(std::io::repeat(9), AMT), socket)
    });

    let client = l.handle().tcp_connect(&addr);
    let received = client.and_then(move |socket| {
        t!(client_cx()).handshake("localhost", socket)
    }).and_then(|socket| {
        io::read_to_end(socket, Vec::new())
    });

    // Finally, run everything!
    let (amt, data) = t!(l.run(sent.join(received)));
    assert_eq!(amt, AMT);
    assert!(data == vec![9; amt as usize]);
}

struct OneByte<S> {
    inner: S,
}

impl<S> Stream for OneByte<S>
    where S: Stream<Item=Ready, Error=std::io::Error>
{
    type Item = Ready;
    type Error = std::io::Error;

    fn poll(&mut self, task: &mut Task) -> Poll<Option<Ready>, std::io::Error> {
        self.inner.poll(task)
    }

    fn schedule(&mut self, task: &mut Task) {
        self.inner.schedule(task)
    }
}

impl<S: Read> Read for OneByte<S> {
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
        self.inner.read(&mut buf[..1])
    }
}

impl<S: Write> Write for OneByte<S> {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        self.inner.write(&buf[..1])
    }

    fn flush(&mut self) -> std::io::Result<()> {
        self.inner.flush()
    }
}

#[test]
fn one_byte_at_a_time() {
    drop(env_logger::init());
    let mut l = t!(futuremio::Loop::new());

    let srv = l.handle().tcp_listen(&"127.0.0.1:0".parse().unwrap());
    let srv = t!(l.run(srv));
    let addr = t!(srv.local_addr());

    let socket = srv.incoming().take(1).collect();
    let sent = socket.map(|mut socket| {
        socket.remove(0).0
    }).and_then(move |socket| {
        t!(server_cx()).handshake(OneByte { inner: socket })
    }).and_then(|socket| {
        io::copy(io::take(std::io::repeat(9), AMT), socket)
    });

    let client = l.handle().tcp_connect(&addr);
    let received = client.and_then(move |socket| {
        t!(client_cx()).handshake("localhost", OneByte { inner: socket })
    }).and_then(|socket| {
        io::read_to_end(socket, Vec::new())
    });

    let (amt, data) = t!(l.run(sent.join(received)));
    assert_eq!(amt, AMT);
    assert!(data == vec![9; amt as usize]);
}
<|endoftext|>


<commit_before>//! Functions for creating and evaluating [Haar-like features](https://en.wikipedia.org/wiki/Haar-like_features).

use image::{
    GenericImage,
    Luma
};
use itertools::Itertools;
use std::collections::HashMap;
use std::ops::Mul;

/// Whether the top left region in a Haar filter is counted
/// with positive or negative sign.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Sign { Positive, Negative }

/// A Haar filter whose value on an integral image is the weighted sum
/// of the values of the integral image at the given points.
// TODO: these structs are pretty big. Look into instead just storing
// TODO: the offsets between sample points. We should only need 10 bytes/filter,
// TODO: meaning we could fit a typical cascade in L1 cache.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct HaarFilter {
    points: [u32; 18],
    weights: [i8; 9],
    count: usize
}

impl HaarFilter {

    /// Evaluates the Haar filter on an integral image.
    pub fn evaluate(&self, integral: &I ) -> i32
        where I: GenericImage<Pixel=Luma<u32>> {

        let mut sum = 0i32;
        for i in 0..self.count {
            let p = integral.get_pixel(self.points[2 * i], self.points[2 * i + 1])[0];
            sum += p as i32 * self.weights[i] as i32;
        }
        sum
    }

    /// Returns the following feature (with signs reversed if Sign == Sign::Negative).
    ///

    ///     A   B   C
    ///       +   -
    ///     D   E   F
    ///

    /// A = (top, left), B.x = left + dx1, C.x = B.x + dx2, and D.y = A.y + dy.
    pub fn two_region_horizontal(top: u32, left: u32, dx1: u32, dx2: u32, dy: u32, sign: Sign)
        -> HaarFilter {

        combine_alternating(&[
            eval_points(top, left,       dx1, dy),
            eval_points(top, left + dx1, dx2, dy)]) * multiplier(sign)
    }

    /// Returns the following feature (with signs reversed if Sign == Sign::Negative).
    ///

    ///     A   B
    ///       +
    ///     C   D
    ///       -
    ///     E   F
    ///

    /// A = (top, left), B.x = left + dx, C.y = top + dy1, and E.y = C.y + dy2.
    pub fn two_region_vertical(top: u32, left: u32, dx: u32, dy1: u32, dy2: u32, sign: Sign)
        -> HaarFilter {

        combine_alternating(&[
            eval_points(top,       left, dx, dy1),
            eval_points(top + dy1, left, dx, dy2)]) * multiplier(sign)
    }

    /// Returns the following feature (with signs reversed if Sign == Sign::Negative).
    ///

    ///     A   B   C   D
    ///       +   -   +
    ///     E   F   G   H
    ///

    /// A = (top, left), B.x = left + dx1, C.x = B.x + dx2, D.x = C.x + dx3, and E.y = top + dy.
    pub fn three_region_horizontal(
        top: u32, left: u32, dx1: u32, dx2: u32, dx3: u32, dy: u32, sign: Sign)
            -> HaarFilter {

        combine_alternating(&[
            eval_points(top, left,             dx1, dy),
            eval_points(top, left + dx1,       dx2, dy),
            eval_points(top, left + dx1 + dx2, dx3, dy),
            ]) * multiplier(sign)
    }

    /// Returns the following feature (with signs reversed if Sign == Sign::Negative).
    ///

    ///     A   B
    ///       +
    ///     C   D
    ///       -
    ///     E   F
    ///       +
    ///     G   H
    ///

    /// A = (top, left), B.x = left + dx, C.y = top + dy1, E.y = C.y + dy2, and G.y = E.y + dy3.
    pub fn three_region_vertical(
        top: u32, left: u32, dx: u32, dy1: u32, dy2: u32, dy3: u32, sign: Sign)
            -> HaarFilter {

        combine_alternating(&[
            eval_points(top,             left, dx, dy1),
            eval_points(top + dy1,       left, dx, dy2),
            eval_points(top + dy1 + dy2, left, dx, dy3),
            ]) * multiplier(sign)
    }

    /// Returns the following feature (with signs reversed if Sign == Sign::Negative).
    ///

    ///     A   B   C
    ///       +   -
    ///     D   E   F
    ///       -   +
    ///     G   H   I
    ///

    /// A = (top, left), B.x = left + dx1, C.x = B.x + dx2, D.y = top + dy1, and G.y = D.y + dy2.
    pub fn four_region(
        top: u32, left: u32, dx1: u32, dx2: u32, dy1: u32, dy2: u32, sign: Sign)
            -> HaarFilter {

        combine_alternating(&[
            eval_points(top,       left,       dx1, dy1),
            eval_points(top,       left + dx1, dx2, dy1),
            eval_points(top + dy1, left,       dx1, dy2),
            eval_points(top + dy1, left + dx1, dx2, dy2),
            ]) * multiplier(sign)
    }
}

/// See comment on eval_points.
struct EvalPoints {
    points: [(u32, u32); 4],
    weights: [i8; 4]
}

impl EvalPoints {
    fn new(points: [(u32, u32); 4], weights: [i8; 4]) -> EvalPoints {
        EvalPoints { points: points, weights: weights }
    }
}

impl Mul<i8> for HaarFilter {
    type Output = HaarFilter;

    fn mul(self, rhs: i8) -> HaarFilter {
        let mut copy = self;
        for i in 0..copy.weights.len() {
            copy.weights[i] *= rhs;
        }
        copy
    }
}

/// Points at which to evaluate an integral image to produce the sum of the
/// pixel intensities of all points within a rectangle. Only valid when the
/// rectangle is wholly contained in the image boundaries. The only non-trivial
/// cases are when the rectangle touches the left or top image boundaries. In this
/// case we need to evaluate fewer than four points, and return weights of zero
/// for the other points.
fn eval_points(top: u32, left: u32, width: u32, height: u32) -> EvalPoints {

    let mut points = [
        (0u32, 0u32),
        (0u32, 0u32),
        (0u32, 0u32),
        (left + width - 1, top + height - 1)];

    let mut weights = [0i8, 0i8, 0i8, 1i8];

    if top > 0 && left > 0 {
        points[0] = (left - 1, top - 1);
        weights[0] = 1i8;
    }
    if top > 0 {
        points[1] = (left + width - 1, top - 1);
        weights[1] = -1i8;
    }
    if left > 0 {
        points[2] = (left - 1, top + height - 1);
        weights[2] = -1i8;
    }

    EvalPoints::new(points, weights)
}

/// Combine sets of evaluation points with alternating signs.
/// The first entry of rects is counted with positive sign.
// TODO: check that we don't have too many distinct points. This
// TODO: function isn't exported, so we just need to check the HaarFilter uses
// TODO: haven't messed anything up.
fn combine_alternating(rects: &[EvalPoints]) -> HaarFilter {

    // Aggregate weights of all points, remove any with zero weight, and
    // order lexicographically by location.
    let mut sign = 1i8;
    let sorted_points = rects
        .iter()
        .fold(HashMap::new(), |mut acc, rect| {
            for i in 0..4 {
                *acc.entry(rect.points[i]).or_insert(0) += sign * rect.weights[i];
            }
            sign *= -1i8;
            acc
            })
        .into_iter()
        .filter(|kv| kv.1 != 0)
        .sorted_by(|a, b| Ord::cmp(&((a.0).1, (a.0).0), &((b.0).1, (b.0).0)));

    let mut count = 0;
    let mut points = [0u32; 18];
    let mut weights = [0i8; 9];

    for pw in sorted_points {
        points[2 * count] = (pw.0).0;
        points[2 * count + 1] = (pw.0).1;
        weights[count] = pw.1;
        count += 1;
    }

    HaarFilter {
        points: points,
        weights: weights,
        count: count }
}

fn multiplier(sign: Sign) -> i8 {
    if sign == Sign::Positive {1} else {-1}
}

#[cfg(test)]
mod test {

    use super::{
        combine_alternating,
        EvalPoints,
        HaarFilter,
        Sign
    };
    use image::{
        ImageBuffer
    };
    use integralimage::{
        integral_image
    };

    #[test]
    fn test_combine_alternating() {
        // Three region horizontally aligned filter:
        // A   B   C   D
        //   +   -   +
        // E   F   G   H
        // I(A) - 2I(B) + 2I(C) - I(D) - I(E) + 2I(F) - 2I(G) + I(H).

        let a = (0, 0);
        let b = (1, 0);
        let c = (2, 0);
        let d = (3, 0);
        let e = (0, 1);
        let f = (1, 1);
        let g = (2, 1);
        let h = (3, 1);

        let left  = EvalPoints::new([a, b, e, f], [1, -1, -1, 1]);
        let mid   = EvalPoints::new([b, c, f, g], [1, -1, -1, 1]);
        let right = EvalPoints::new([c, d, g, h], [1, -1, -1, 1]);

        let filter = combine_alternating(&[left, mid, right]);
        let expected = HaarFilter {
            points: [0, 0, 1, 0, 2, 0, 3, 0, 0, 1, 1, 1, 2, 1, 3, 1, 0, 0],
            weights: [1, -2, 2, -1, -1, 2, -2, 1, 0],
            count: 8
        };

        assert_eq!(filter, expected);
    }

    #[test]
    fn test_two_region_horizontal() {
        // Two region horizontally aligned filter:
        // A   B   C
        //   +   -
        // D   E   F
        let image = ImageBuffer::from_raw(5, 5, vec![
            1u8,     2u8, 3u8,     4u8,     5u8,
                 /***+++++++++*****-----***/
            6u8, /**/7u8, 8u8,/**/ 9u8, /**/0u8,
            9u8, /**/8u8, 7u8,/**/ 6u8, /**/5u8,
            4u8, /**/3u8, 2u8,/**/ 1u8, /**/0u8,
                 /***+++++++++*****-----***/
            6u8, 5u8, 4u8, 2u8, 1u8]).unwrap();

        let integral = integral_image(&image);

        let filter = HaarFilter::two_region_horizontal(
            1, 1, 2u32, 1u32, 3u32, Sign::Positive);

        let value = filter.evaluate(&integral);

        assert_eq!(value, 19i32);
    }

    #[test]
    fn test_three_region_vertical() {
        // Three region vertically aligned filter:
        // A   B
        //   +
        // C   D
        //   -
        // E   F
        //   +
        // G   H
        let image = ImageBuffer::from_raw(5, 5, vec![
        /*****************/
        /*-*/1u8, 2u8,/*-*/ 3u8, 4u8, 5u8,
        /*****************/
        /*+*/6u8, 7u8,/*+*/ 8u8, 9u8, 0u8,
        /*+*/9u8, 8u8,/*+*/ 7u8, 6u8, 5u8,
        /*****************/
        /*-*/4u8, 3u8,/*-*/ 2u8, 1u8, 0u8,
        /*****************/
             6u8, 5u8,      4u8, 2u8, 1u8]).unwrap();

        let integral = integral_image(&image);

        let filter = HaarFilter::three_region_vertical(
            0, 0, 2u32, 1u32, 2u32, 1u32, Sign::Negative);

        let value = filter.evaluate(&integral);

        assert_eq!(value, 20i32);
    }
}
<commit_msg>Add tests<commit_after>//! Functions for creating and evaluating [Haar-like features](https://en.wikipedia.org/wiki/Haar-like_features).

use image::{
    GenericImage,
    Luma
};
use itertools::Itertools;
use std::collections::HashMap;
use std::ops::Mul;

/// Whether the top left region in a Haar filter is counted
/// with positive or negative sign.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Sign { Positive, Negative }

/// A Haar filter whose value on an integral image is the weighted sum
/// of the values of the integral image at the given points.
// TODO: these structs are pretty big. Look into instead just storing
// TODO: the offsets between sample points. We should only need 10 bytes/filter,
// TODO: meaning we could fit a typical cascade in L1 cache.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct HaarFilter {
    points: [u32; 18],
    weights: [i8; 9],
    count: usize
}

impl HaarFilter {

    /// Evaluates the Haar filter on an integral image.
    pub fn evaluate(&self, integral: &I ) -> i32
        where I: GenericImage<Pixel=Luma<u32>> {

        let mut sum = 0i32;
        for i in 0..self.count {
            let p = integral.get_pixel(self.points[2 * i], self.points[2 * i + 1])[0];
            sum += p as i32 * self.weights[i] as i32;
        }
        sum
    }

    /// Returns the following feature (with signs reversed if Sign == Sign::Negative).
    ///

    ///     A   B   C
    ///       +   -
    ///     D   E   F
    ///

    /// A = (top, left), B.x = left + dx1, C.x = B.x + dx2, and D.y = A.y + dy.
    pub fn two_region_horizontal(top: u32, left: u32, dx1: u32, dx2: u32, dy: u32, sign: Sign)
        -> HaarFilter {

        combine_alternating(&[
            eval_points(top, left,       dx1, dy),
            eval_points(top, left + dx1, dx2, dy)]) * multiplier(sign)
    }

    /// Returns the following feature (with signs reversed if Sign == Sign::Negative).
    ///

    ///     A   B
    ///       +
    ///     C   D
    ///       -
    ///     E   F
    ///

    /// A = (top, left), B.x = left + dx, C.y = top + dy1, and E.y = C.y + dy2.
    pub fn two_region_vertical(top: u32, left: u32, dx: u32, dy1: u32, dy2: u32, sign: Sign)
        -> HaarFilter {

        combine_alternating(&[
            eval_points(top,       left, dx, dy1),
            eval_points(top + dy1, left, dx, dy2)]) * multiplier(sign)
    }

    /// Returns the following feature (with signs reversed if Sign == Sign::Negative).
    ///

    ///     A   B   C   D
    ///       +   -   +
    ///     E   F   G   H
    ///

    /// A = (top, left), B.x = left + dx1, C.x = B.x + dx2, D.x = C.x + dx3, and E.y = top + dy.
    pub fn three_region_horizontal(
        top: u32, left: u32, dx1: u32, dx2: u32, dx3: u32, dy: u32, sign: Sign)
            -> HaarFilter {

        combine_alternating(&[
            eval_points(top, left,             dx1, dy),
            eval_points(top, left + dx1,       dx2, dy),
            eval_points(top, left + dx1 + dx2, dx3, dy),
            ]) * multiplier(sign)
    }

    /// Returns the following feature (with signs reversed if Sign == Sign::Negative).
    ///

    ///     A   B
    ///       +
    ///     C   D
    ///       -
    ///     E   F
    ///       +
    ///     G   H
    ///

    /// A = (top, left), B.x = left + dx, C.y = top + dy1, E.y = C.y + dy2, and G.y = E.y + dy3.
    pub fn three_region_vertical(
        top: u32, left: u32, dx: u32, dy1: u32, dy2: u32, dy3: u32, sign: Sign)
            -> HaarFilter {

        combine_alternating(&[
            eval_points(top,             left, dx, dy1),
            eval_points(top + dy1,       left, dx, dy2),
            eval_points(top + dy1 + dy2, left, dx, dy3),
            ]) * multiplier(sign)
    }

    /// Returns the following feature (with signs reversed if Sign == Sign::Negative).
    ///

    ///     A   B   C
    ///       +   -
    ///     D   E   F
    ///       -   +
    ///     G   H   I
    ///

    /// A = (top, left), B.x = left + dx1, C.x = B.x + dx2, D.y = top + dy1, and G.y = D.y + dy2.
    pub fn four_region(
        top: u32, left: u32, dx1: u32, dx2: u32, dy1: u32, dy2: u32, sign: Sign)
            -> HaarFilter {

        combine_alternating(&[
            eval_points(top,       left,       dx1, dy1),
            eval_points(top,       left + dx1, dx2, dy1),
            eval_points(top + dy1, left + dx1, dx2, dy2),
            eval_points(top + dy1, left,       dx1, dy2),
            ]) * multiplier(sign)
    }
}

/// See comment on eval_points.
struct EvalPoints {
    points: [(u32, u32); 4],
    weights: [i8; 4]
}

impl EvalPoints {
    fn new(points: [(u32, u32); 4], weights: [i8; 4]) -> EvalPoints {
        EvalPoints { points: points, weights: weights }
    }
}

impl Mul<i8> for HaarFilter {
    type Output = HaarFilter;

    fn mul(self, rhs: i8) -> HaarFilter {
        let mut copy = self;
        for i in 0..copy.weights.len() {
            copy.weights[i] *= rhs;
        }
        copy
    }
}

/// Points at which to evaluate an integral image to produce the sum of the
/// pixel intensities of all points within a rectangle. Only valid when the
/// rectangle is wholly contained in the image boundaries. The only non-trivial
/// cases are when the rectangle touches the left or top image boundaries. In this
/// case we need to evaluate fewer than four points, and return weights of zero
/// for the other points.
fn eval_points(top: u32, left: u32, width: u32, height: u32) -> EvalPoints {

    let mut points = [
        (0u32, 0u32),
        (0u32, 0u32),
        (0u32, 0u32),
        (left + width - 1, top + height - 1)];

    let mut weights = [0i8, 0i8, 0i8, 1i8];

    if top > 0 && left > 0 {
        points[0] = (left - 1, top - 1);
        weights[0] = 1i8;
    }
    if top > 0 {
        points[1] = (left + width - 1, top - 1);
        weights[1] = -1i8;
    }
    if left > 0 {
        points[2] = (left - 1, top + height - 1);
        weights[2] = -1i8;
    }

    EvalPoints::new(points, weights)
}

/// Combine sets of evaluation points with alternating signs.
/// The first entry of rects is counted with positive sign.
// TODO: check that we don't have too many distinct points. This
// TODO: function isn't exported, so we just need to check the HaarFilter uses
// TODO: haven't messed anything up.
fn combine_alternating(rects: &[EvalPoints]) -> HaarFilter {

    // Aggregate weights of all points, remove any with zero weight, and
    // order lexicographically by location.
    let mut sign = 1i8;
    let sorted_points = rects
        .iter()
        .fold(HashMap::new(), |mut acc, rect| {
            for i in 0..4 {
                *acc.entry(rect.points[i]).or_insert(0) += sign * rect.weights[i];
            }
            sign *= -1i8;
            acc
            })
        .into_iter()
        .filter(|kv| kv.1 != 0)
        .sorted_by(|a, b| Ord::cmp(&((a.0).1, (a.0).0), &((b.0).1, (b.0).0)));

    let mut count = 0;
    let mut points = [0u32; 18];
    let mut weights = [0i8; 9];

    for pw in sorted_points {
        points[2 * count] = (pw.0).0;
        points[2 * count + 1] = (pw.0).1;
        weights[count] = pw.1;
        count += 1;
    }

    HaarFilter {
        points: points,
        weights: weights,
        count: count }
}

fn multiplier(sign: Sign) -> i8 {
    if sign == Sign::Positive {1} else {-1}
}

#[cfg(test)]
mod test {

    use super::{
        combine_alternating,
        EvalPoints,
        HaarFilter,
        Sign
    };
    use image::{
        ImageBuffer
    };
    use integralimage::{
        integral_image
    };

    #[test]
    fn test_combine_alternating() {
        let a = (0, 0);
        let b = (1, 0);
        let c = (2, 0);
        let d = (3, 0);
        let e = (0, 1);
        let f = (1, 1);
        let g = (2, 1);
        let h = (3, 1);

        let left  = EvalPoints::new([a, b, e, f], [1, -1, -1, 1]);
        let mid   = EvalPoints::new([b, c, f, g], [1, -1, -1, 1]);
        let right = EvalPoints::new([c, d, g, h], [1, -1, -1, 1]);

        let filter = combine_alternating(&[left, mid, right]);
        let expected = HaarFilter {
            points: [0, 0, 1, 0, 2, 0, 3, 0, 0, 1, 1, 1, 2, 1, 3, 1, 0, 0],
            weights: [1, -2, 2, -1, -1, 2, -2, 1, 0],
            count: 8
        };

        assert_eq!(filter, expected);
    }

    #[test]
    fn test_two_region_horizontal() {
        // Two region horizontally aligned filter:
        // A   B   C
        //   +   -
        // D   E   F
        let image = ImageBuffer::from_raw(5, 5, vec![
            1u8,     2u8, 3u8,     4u8,     5u8,
                 /***+++++++++*****-----***/
            6u8, /**/7u8, 8u8,/**/ 9u8, /**/0u8,
            9u8, /**/8u8, 7u8,/**/ 6u8, /**/5u8,
            4u8, /**/3u8, 2u8,/**/ 1u8, /**/0u8,
                 /***+++++++++*****-----***/
            6u8, 5u8, 4u8, 2u8, 1u8]).unwrap();

        let integral = integral_image(&image);
        let filter = HaarFilter::two_region_horizontal(1, 1, 2, 1, 3, Sign::Positive);
        assert_eq!(filter.evaluate(&integral), 19i32);
    }

    #[test]
    fn test_three_region_vertical() {
        // Three region vertically aligned filter:
        // A   B
        //   +
        // C   D
        //   -
        // E   F
        //   +
        // G   H
        let image = ImageBuffer::from_raw(5, 5, vec![
        /*****************/
        /*-*/1u8, 2u8,/*-*/ 3u8, 4u8, 5u8,
        /*****************/
        /*+*/6u8, 7u8,/*+*/ 8u8, 9u8, 0u8,
        /*+*/9u8, 8u8,/*+*/ 7u8, 6u8, 5u8,
        /*****************/
        /*-*/4u8, 3u8,/*-*/ 2u8, 1u8, 0u8,
        /*****************/
             6u8, 5u8,      4u8, 2u8, 1u8]).unwrap();

        let integral = integral_image(&image);
        let filter = HaarFilter::three_region_vertical(0, 0, 2, 1, 2, 1, Sign::Negative);
        assert_eq!(filter.evaluate(&integral), 20i32);
    }

    #[test]
    fn test_four_region() {
        // Four region filter:
        // A   B   C
        //   +   -
        // D   E   F
        //   -   +
        // G   H   I
        let image = ImageBuffer::from_raw(5, 5, vec![
        1u8,    2u8, 3u8,     4u8,     5u8,
            /************************/
        6u8,/**/7u8, 8u8,/**/ 9u8,/**/ 0u8,
            /************************/
        9u8,/**/8u8, 7u8,/**/ 6u8,/**/ 5u8,
        4u8,/**/3u8, 2u8,/**/ 1u8,/**/ 0u8,
            /************************/
        6u8,    5u8, 4u8,     2u8,     1u8]).unwrap();

        let integral = integral_image(&image);
        let filter = HaarFilter::four_region(1, 1, 2, 1, 1, 2, Sign::Positive);

        assert_eq!(filter.evaluate(&integral), -7i32);
    }
}
<|endoftext|>


<commit_before>use rustc_serialize::hex::ToHex;
use super::Delivery;
use crypto::mac::Mac;
use crypto::hmac::Hmac;
use crypto::sha1::Sha1;

/// H handles webhook deliveries
pub trait Hook: Send + Sync {
    /// Implementations are expected to deliveries here
    fn handle(&self, delivery: &Delivery);
}

/// A delivery authenticator for hooks
pub struct AuthenticateHook<H: Hook + 'static> {
    secret: String,
    hook: H,
}

impl<H: Hook + 'static> AuthenticateHook<H> {
    pub fn new<S>(secret: S, hook: H) -> AuthenticateHook<H>
        where S: Into<String>
    {
        AuthenticateHook {
            secret: secret.into(),
            hook: hook,
        }
    }

    fn authenticate(&self, payload: &str, signature: &str) -> bool {
        let sbytes = self.secret.as_bytes();
        let pbytes = payload.as_bytes();
        let mut mac = Hmac::new(Sha1::new(), &sbytes);
        mac.input(&pbytes);
        mac.result().code().to_hex() == str::replace(signature, "sha1=", "")
    }
}

impl<H: Hook + 'static> Hook for AuthenticateHook<H> {
    fn handle(&self, delivery: &Delivery) {
        if let Some(sig) = delivery.signature {
            if self.authenticate(delivery.unparsed_payload, sig) {
                self.hook.handle(delivery)
            }
        }
    }
}

impl<F> Hook for F
    where F: Fn(&Delivery),
          F: Sync + Send
{
    fn handle(&self, delivery: &Delivery) {
        self(delivery)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use super::super::Delivery;
    use crypto::mac::Mac;
    use crypto::hmac::Hmac;
    use crypto::sha1::Sha1;
    use rustc_serialize::hex::ToHex;

    #[test]
    fn authenticate_signatures() {
        let authenticated = AuthenticateHook::new("secret", |_: &Delivery| {
        });
        let payload = r#"{"zen": "Approachable is better than simple."}"#;
        let secret = "secret";
        let sbytes = secret.as_bytes();
        let pbytes = payload.as_bytes();
        let mut mac = Hmac::new(Sha1::new(), &sbytes);
        mac.input(&pbytes);
        let signature = mac.result().code().to_hex();
        assert!(authenticated.authenticate(payload, format!("sha1={}", signature).as_ref()))
    }
}
<commit_msg>fix rustdoc<commit_after>use rustc_serialize::hex::ToHex;
use super::Delivery;
use crypto::mac::Mac;
use crypto::hmac::Hmac;
use crypto::sha1::Sha1;

/// Handles webhook deliveries
pub trait Hook: Send + Sync {
    /// Implementations are expected to deliveries here
    fn handle(&self, delivery: &Delivery);
}

/// A delivery authenticator for hooks
pub struct AuthenticateHook<H: Hook + 'static> {
    secret: String,
    hook: H,
}

impl<H: Hook + 'static> AuthenticateHook<H> {
    pub fn new<S>(secret: S, hook: H) -> AuthenticateHook<H>
        where S: Into<String>
    {
        AuthenticateHook {
            secret: secret.into(),
            hook: hook,
        }
    }

    fn authenticate(&self, payload: &str, signature: &str) -> bool {
        let sbytes = self.secret.as_bytes();
        let pbytes = payload.as_bytes();
        let mut mac = Hmac::new(Sha1::new(), &sbytes);
        mac.input(&pbytes);
        mac.result().code().to_hex() == str::replace(signature, "sha1=", "")
    }
}

impl<H: Hook + 'static> Hook for AuthenticateHook<H> {
    fn handle(&self, delivery: &Delivery) {
        if let Some(sig) = delivery.signature {
            if self.authenticate(delivery.unparsed_payload, sig) {
                self.hook.handle(delivery)
            }
        }
    }
}

impl<F> Hook for F
    where F: Fn(&Delivery),
          F: Sync + Send
{
    fn handle(&self, delivery: &Delivery) {
        self(delivery)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use super::super::Delivery;
    use crypto::mac::Mac;
    use crypto::hmac::Hmac;
    use crypto::sha1::Sha1;
    use rustc_serialize::hex::ToHex;

    #[test]
    fn authenticate_signatures() {
        let authenticated = AuthenticateHook::new("secret", |_: &Delivery| {
        });
        let payload = r#"{"zen": "Approachable is better than simple."}"#;
        let secret = "secret";
        let sbytes = secret.as_bytes();
        let pbytes = payload.as_bytes();
        let mut mac = Hmac::new(Sha1::new(), &sbytes);
        mac.input(&pbytes);
        let signature = mac.result().code().to_hex();
        assert!(authenticated.authenticate(payload, format!("sha1={}", signature).as_ref()))
    }
}
<|endoftext|>


<commit_before>use crate::gssapi::*;
use crate::rparser::*;
use crate::sasl::parse_sasl_buffer;
use crate::tls::TlsParser;
use crate::{gen_get_variants, Variant};
use der_parser::ber::*;
use der_parser::error::*;
use ldap_parser::ldap::*;
use ldap_parser::*;
use std::collections::BTreeMap;

pub struct LDAPBuilder {}
impl RBuilder for LDAPBuilder {
    fn build(&self) -> Box<dyn RParser> {
        Box::new(LDAPParser::new(b"LDAP"))
    }
    fn get_l4_probe(&self) -> Option<ProbeL4> {
        Some(ldap_probe)
    }
}

pub struct LDAPParser<'a> {
    _name: Option<&'a [u8]>,
    has_sasl_layers: bool,
    bind_dn: Option<String>,
    sasl_mech: Option<String>,
    // operation tag of current request, if present
    protocol_op: Option<ProtocolOpTag>,
    // message ID of LDAP request, if present
    ldap_request: Option<MessageID>,
    // numeric result code of LDAP response, if present
    ldap_result_code: Option<u32>,
    // message ID of TLS request, if present
    tls_request: Option<MessageID>,
    has_starttls: bool,
    transactions: BTreeMap<MessageID, LDAPTransaction>,

    defrag_buffer: Vec<u8>,
    tls_parser: Option<TlsParser<'a>>,
}

#[derive(Debug)]
pub struct LDAPTransaction {
    message_id: MessageID,
    tx_op: ProtocolOpTag,
    result_code: Option<u32>,
    bind_dn: Option<String>,
}

impl<'a> RParser for LDAPParser<'a> {
    fn parse_l4(&mut self, data: &[u8], direction: Direction) -> ParseResult {
        if self.has_starttls {
            return self.parse_ldap_starttls(data, direction);
        }
        // debug: check if we missed TLS
        if self.defrag_buffer.is_empty() && data.len() >= 2 && &data[..2] == b"\x16\x03" {
            warn!("LDAP: missed STARTTLS ?!");
        }
        if self.has_sasl_layers {
            self.parse_ldap_sasl(data, direction)
        } else {
            self.parse_ldap_messages(data, direction)
        }
    }

    gen_get_variants! {LDAPParser, "ldap.",
        ldap_request => |s| {
            s.ldap_request.map(|op| op.0.into())
        },
        ldap_result_code      => map,
        protocol_op => |s| {
            s.protocol_op.map(|op| Variant::OwnedStr(op.to_string()))
        },
        sasl_mech             => map_as_ref,
        has_starttls          => into,
        tls_client_version => |s| {
            s.tls_parser.as_ref().map(|p|
                p.get("tls.client_version")
            ).flatten()
        },
        tls_ssl_record_version => |s| {
            s.tls_parser.as_ref().map(|p|
                p.get("tls.ssl_record_version")
            ).flatten()
        },
        tls_compression => |s| {
            s.tls_parser.as_ref().map(|p|
                p.get("tls.compression")
            ).flatten()
        },
        tls_cipher => |s| {
            s.tls_parser.as_ref().map(|p|
                p.get("tls.cipher")
            ).flatten()
        },
    }
}

impl<'a> LDAPParser<'a> {
    pub fn new(name: &'a [u8]) -> LDAPParser<'a> {
        LDAPParser {
            _name: Some(name),
            has_sasl_layers: false,
            bind_dn: None,
            sasl_mech: None,
            tls_request: None,
            has_starttls: false,
            defrag_buffer: Vec::new(),
            ldap_request: None,
            ldap_result_code: None,
            transactions: BTreeMap::new(),
            protocol_op: None,
            tls_parser: None,
        }
    }

    pub fn parse_ldap_starttls(&mut self, data: &[u8], direction: Direction) -> ParseResult {
        if let Some(parser) = &mut self.tls_parser {
            parser.parse_tcp_level(data, direction)
        } else {
            warn!("Should not happen: could not instantiate TLS parser");
            ParseResult::Fatal
        }
    }

    pub fn parse_ldap_sasl(&mut self, data: &[u8], direction: Direction) -> ParseResult {
        // are we already waiting for more data (defragmentation)?
        if !self.defrag_buffer.is_empty() {
            return self.parse_ldap_messages(data, direction);
        }
        // are we really using SASL?
        // sometimes, SASL is negotiated but the next packets are sent in clear
        //
        // check if first byte is 0 (sasl layer length is 4 bytes, so this is OK if the packet does not exceed 16 MB)
        // if LDAP, first byte should be 0x30 anyway
        if !data.is_empty() && data[0] != 0 {
            warn!("XXX CLEAR TEXT LDAP after SASL");
            return self.parse_ldap_messages(data, direction);
        }
        let mech = match &self.sasl_mech {
            Some(s) => s,
            None => {
                debug!("SASL mechanism unknown, stopping following LDAP session");
                return ParseResult::Stop;
            }
        };
        match mech.as_ref() {
            "GSSAPI" | "GSS-SPNEGO" => {
                // use nom::HexDisplay;
                // debug!("\n{}", data.to_hex(16));
                let (_rem, sasl) = match parse_sasl_buffer(data) {
                    Ok(x) => x,
                    Err(e) => {
                        debug!("LDAP: parsing SASL buffer failed: {:?}", e);
                        return ParseResult::Error;
                    }
                };
                let (_rem, gss) = match parse_gssapi(sasl.0) {
                    Ok(x) => x,
                    Err(e) => {
                        debug!("LDAP: parsing GSS-API buffer failed: {:?}", e);
                        return ParseResult::Error;
                    }
                };
                match &gss {
                    GssApiBuffer::Wrapped(buffer) => {
                        if buffer.token.seal_alg == 0xffff {
                            // no encryption, only integrity
                            // debug!("Integrity-only GSS-API KRB5 encapsulation detected");
                            // XXX remove padding
                            //
                            // integrity-only messages seem to always end up by hex '01', though I could not find why
                            self.parse_ldap_messages(buffer.payload, direction)
                        } else {
                            trace!(
                                "Encrypted GSS-API KRB5 encapsulation detected, activating bypass"
                            );
                            ParseResult::Stop
                        }
                    }
                    GssApiBuffer::GssCfxWrap { token, payload } => {
                        if token.flags & KRB_CFX_FLAG_SEALED != 0 {
                            trace!(
                                "Encrypted GSS-API CFX KRB5 encapsulation detected, activating bypass"
                            );
                            return ParseResult::Stop;
                        }
                        // no encryption, only integrity
                        // debug!("Integrity-only GSS-API KRB5 encapsulation detected");
                        // XXX remove padding
                        //
                        self.parse_ldap_messages(payload, direction)
                    }
                    _ => {
                        trace!("Unknown GSS-API encapsulation, activating bypass");
                        ParseResult::Stop
                    }
                }
            }
            _ => {
                trace!("Unknown SASL layer {}, activating bypass", mech);
                ParseResult::Stop
            }
        }
    }

    pub fn parse_ldap_messages(&mut self, data: &[u8], direction: Direction) -> ParseResult {
        let mut v: Vec<u8>;
        // Check if TCP data is being defragmented
        let defrag_buffer = match self.defrag_buffer.len() {
            0 => data,
            _ => {
                // sanity check vector length to avoid memory exhaustion
                if self.defrag_buffer.len() + data.len() > 16_777_216 {
                    warn!("LDAP: defrag buffer overflow");
                    return ParseResult::Error;
                };
                v = self.defrag_buffer.split_off(0);
                v.extend_from_slice(data);
                v.as_slice()
            }
        };
        let mut cur_i = defrag_buffer;
        loop {
            if cur_i.is_empty() {
                break;
            }
            // read header and see if we have a complete message
            if check_ldap_message_complete(cur_i).is_err() {
                // XXX if bytes are exactly "01", drop? (often happens with GSS-API)
                if cur_i.len() == 1
                    && cur_i[0] == 1
                    && self.sasl_mech == Some("GSS-SPNEGO".to_string())
                {
                    warn!("Fixup for SASL/GSS-API: removing 1 byte");
                    // cur_i = &cur_i[1..];
                    break;
                }
                trace!("Fragmentation required (TCP level)");
                // if not complete, add to defrag buffer
                self.defrag_buffer.extend_from_slice(cur_i);
                // panic!("XXX Fragmented LDAP");
                break;
            }
            match parse_ldap_message(cur_i) {
                Ok((rem, msg)) => {
                    trace!("parse_ldap_message: {:?}", msg);
                    self.handle_message(&msg, direction);
                    cur_i = rem;
                }
                e => {
                    warn!("parse_ldap_message failed: {:?}", e);
                    break;
                }
            };
        }
        trace!("current transactions: {:?}", self.transactions);
        // if !cur_i.is_empty() {
        //     // XXX if bytes are exactly "01", drop? (often happens with GSS-API)
        //     warn!("Extra bytes after LDAP data");
        //     use nom::HexDisplay;
        //     println!("{}", cur_i.to_hex(16));
        // }
        ParseResult::Ok
    }

    pub fn handle_message(&mut self, msg: &LdapMessage, _direction: Direction) {
        let tx_op = msg.protocol_op.tag();
        let bind_dn = &self.bind_dn;
        let mut tx = self
            .transactions
            .entry(msg.message_id)
            .or_insert_with(|| LDAPTransaction::new(msg.message_id, tx_op, bind_dn));
        self.protocol_op = Some(tx_op);
        match &msg.protocol_op {
            ProtocolOp::BindRequest(req) => {
                // store msg_id
                self.ldap_request = Some(msg.message_id);
                // store bind DN
                self.bind_dn = Some(req.name.0.to_string());
                tx.bind_dn = Some(req.name.0.to_string());
                // if SASL, store mechanism
                if let AuthenticationChoice::Sasl(creds) = &req.authentication {
                    self.sasl_mech = Some(creds.mechanism.0.to_string());
                    if let Some(data) = &creds.credentials {
                        if !data.is_empty() {
                            use nom::HexDisplay;
                            println!("XXX\n{}", (&data).to_hex(16));
                            let (_, xxx) = parse_gssapi(data).unwrap(); // XXX
                            println!("{:?}", xxx);
                        }
                    }
                } else {
                    // XXX one connection can bind multiple times (and/or do stuff unbound)
                    self.sasl_mech = None;
                }
            }
            ProtocolOp::BindResponse(resp) => {
                self.ldap_result_code = Some(resp.result.result_code.0);
                tx.result_code = Some(resp.result.result_code.0);
                // check msg_id
                // check for success
                if resp.result.result_code == ResultCode::Success {
                    // if SASL authentication succeeded, SASL layers are now active
                    if self.sasl_mech.is_some() {
                        self.has_sasl_layers = true;
                    }
                } else {
                    self.bind_dn = None;
                }
            }
            ProtocolOp::ExtendedRequest(req) => {
                self.ldap_request = Some(msg.message_id);
                // check for STARTTLS
                if req.request_name.0 == "1.3.6.1.4.1.1466.20037" {
                    self.tls_request = Some(msg.message_id);
                }
            }
            ProtocolOp::ExtendedResponse(res) => {
                if let Some(req_id) = self.tls_request {
                    self.ldap_result_code = Some(res.result.result_code.0);
                    tx.result_code = Some(res.result.result_code.0);
                    if res.result.result_code == ResultCode::Success && msg.message_id == req_id {
                        self.has_starttls = true;
                        self.tls_parser = Some(TlsParser::new(b"LDAP/TLS"));
                    }
                }
            }
            ProtocolOp::SearchRequest(_)
            | ProtocolOp::ModifyRequest(_)
            | ProtocolOp::AddRequest(_)
            | ProtocolOp::ModDnRequest(_)
            | ProtocolOp::CompareRequest(_)
            | ProtocolOp::AbandonRequest(_) => {
                self.ldap_request = Some(msg.message_id);
            }
            ProtocolOp::ModifyResponse(r) => {
                self.ldap_result_code = Some(r.result.result_code.0);
                tx.result_code = Some(r.result.result_code.0);
            }
            ProtocolOp::SearchResultDone(r)
            | ProtocolOp::AddResponse(r)
            | ProtocolOp::ModDnResponse(r)
            | ProtocolOp::CompareResponse(r) => {
                self.ldap_result_code = Some(r.result_code.0);
                tx.result_code = Some(r.result_code.0);
            }
            _ => (),
        }
    }
}

impl LDAPTransaction {
    pub fn new(message_id: MessageID, tx_op: ProtocolOpTag, bind_dn: &Option<String>) -> Self {
        LDAPTransaction {
            message_id,
            tx_op,
            result_code: None,
            bind_dn: bind_dn.clone(),
        }
    }
}

const COMMON_LDAP_PORTS: &[u16] = &[389, 3268];

pub fn ldap_probe(i: &[u8], l4info: &L4Info) -> ProbeResult {
    if i.len() <= 2 {
        return ProbeResult::Unsure;
    }
    // debug!("Probing for LDAP");
    if parse_ldap_message(i).is_ok() {
        if COMMON_LDAP_PORTS.contains(&l4info.src_port)
            || COMMON_LDAP_PORTS.contains(&l4info.dst_port)
        {
            ProbeResult::Certain
        } else {
            ProbeResult::Unsure
        }
    } else {
        ProbeResult::NotForUs
    }
}

fn check_ldap_message_complete(i: &[u8]) -> BerResult<()> {
    let (i, header) = ber_read_element_header(i)?;
    let len = header.len.primitive()?;
    if i.len() >= len {
        Ok((i, ()))
    } else {
        Err(BerError::InvalidLength.into())
    }
}
<commit_msg>LDAP: add variable for bind DN<commit_after>use crate::gssapi::*;
use crate::rparser::*;
use crate::sasl::parse_sasl_buffer;
use crate::tls::TlsParser;
use crate::{gen_get_variants, Variant};
use der_parser::ber::*;
use der_parser::error::*;
use ldap_parser::ldap::*;
use ldap_parser::*;
use std::collections::BTreeMap;

pub struct LDAPBuilder {}
impl RBuilder for LDAPBuilder {
    fn build(&self) -> Box<dyn RParser> {
        Box::new(LDAPParser::new(b"LDAP"))
    }
    fn get_l4_probe(&self) -> Option<ProbeL4> {
        Some(ldap_probe)
    }
}

pub struct LDAPParser<'a> {
    _name: Option<&'a [u8]>,
    has_sasl_layers: bool,
    bind_dn: Option<String>,
    sasl_mech: Option<String>,
    // operation tag of current request, if present
    protocol_op: Option<ProtocolOpTag>,
    // message ID of LDAP request, if present
    ldap_request: Option<MessageID>,
    // numeric result code of LDAP response, if present
    ldap_result_code: Option<u32>,
    // message ID of TLS request, if present
    tls_request: Option<MessageID>,
    has_starttls: bool,
    transactions: BTreeMap<MessageID, LDAPTransaction>,

    defrag_buffer: Vec<u8>,
    tls_parser: Option<TlsParser<'a>>,
}

#[derive(Debug)]
pub struct LDAPTransaction {
    message_id: MessageID,
    tx_op: ProtocolOpTag,
    result_code: Option<u32>,
    bind_dn: Option<String>,
}

impl<'a> RParser for LDAPParser<'a> {
    fn parse_l4(&mut self, data: &[u8], direction: Direction) -> ParseResult {
        if self.has_starttls {
            return self.parse_ldap_starttls(data, direction);
        }
        // debug: check if we missed TLS
        if self.defrag_buffer.is_empty() && data.len() >= 2 && &data[..2] == b"\x16\x03" {
            warn!("LDAP: missed STARTTLS ?!");
        }
        if self.has_sasl_layers {
            self.parse_ldap_sasl(data, direction)
        } else {
            self.parse_ldap_messages(data, direction)
        }
    }

    gen_get_variants! {LDAPParser, "ldap.",
        ldap_request => |s| {
            s.ldap_request.map(|op| op.0.into())
        },
        ldap_result_code      => map,
        protocol_op => |s| {
            s.protocol_op.map(|op| Variant::OwnedStr(op.to_string()))
        },
        bind_dn               => map_as_ref,
        sasl_mech             => map_as_ref,
        has_starttls          => into,
        tls_client_version => |s| {
            s.tls_parser.as_ref().map(|p|
                p.get("tls.client_version")
            ).flatten()
        },
        tls_ssl_record_version => |s| {
            s.tls_parser.as_ref().map(|p|
                p.get("tls.ssl_record_version")
            ).flatten()
        },
        tls_compression => |s| {
            s.tls_parser.as_ref().map(|p|
                p.get("tls.compression")
            ).flatten()
        },
        tls_cipher => |s| {
            s.tls_parser.as_ref().map(|p|
                p.get("tls.cipher")
            ).flatten()
        },
    }
}

impl<'a> LDAPParser<'a> {
    pub fn new(name: &'a [u8]) -> LDAPParser<'a> {
        LDAPParser {
            _name: Some(name),
            has_sasl_layers: false,
            bind_dn: None,
            sasl_mech: None,
            tls_request: None,
            has_starttls: false,
            defrag_buffer: Vec::new(),
            ldap_request: None,
            ldap_result_code: None,
            transactions: BTreeMap::new(),
            protocol_op: None,
            tls_parser: None,
        }
    }

    pub fn parse_ldap_starttls(&mut self, data: &[u8], direction: Direction) -> ParseResult {
        if let Some(parser) = &mut self.tls_parser {
            parser.parse_tcp_level(data, direction)
        } else {
            warn!("Should not happen: could not instantiate TLS parser");
            ParseResult::Fatal
        }
    }

    pub fn parse_ldap_sasl(&mut self, data: &[u8], direction: Direction) -> ParseResult {
        // are we already waiting for more data (defragmentation)?
        if !self.defrag_buffer.is_empty() {
            return self.parse_ldap_messages(data, direction);
        }
        // are we really using SASL?
        // sometimes, SASL is negotiated but the next packets are sent in clear
        //
        // check if first byte is 0 (sasl layer length is 4 bytes, so this is OK if the packet does not exceed 16 MB)
        // if LDAP, first byte should be 0x30 anyway
        if !data.is_empty() && data[0] != 0 {
            warn!("XXX CLEAR TEXT LDAP after SASL");
            return self.parse_ldap_messages(data, direction);
        }
        let mech = match &self.sasl_mech {
            Some(s) => s,
            None => {
                debug!("SASL mechanism unknown, stopping following LDAP session");
                return ParseResult::Stop;
            }
        };
        match mech.as_ref() {
            "GSSAPI" | "GSS-SPNEGO" => {
                // use nom::HexDisplay;
                // debug!("\n{}", data.to_hex(16));
                let (_rem, sasl) = match parse_sasl_buffer(data) {
                    Ok(x) => x,
                    Err(e) => {
                        debug!("LDAP: parsing SASL buffer failed: {:?}", e);
                        return ParseResult::Error;
                    }
                };
                let (_rem, gss) = match parse_gssapi(sasl.0) {
                    Ok(x) => x,
                    Err(e) => {
                        debug!("LDAP: parsing GSS-API buffer failed: {:?}", e);
                        return ParseResult::Error;
                    }
                };
                match &gss {
                    GssApiBuffer::Wrapped(buffer) => {
                        if buffer.token.seal_alg == 0xffff {
                            // no encryption, only integrity
                            // debug!("Integrity-only GSS-API KRB5 encapsulation detected");
                            // XXX remove padding
                            //
                            // integrity-only messages seem to always end up by hex '01', though I could not find why
                            self.parse_ldap_messages(buffer.payload, direction)
                        } else {
                            trace!(
                                "Encrypted GSS-API KRB5 encapsulation detected, activating bypass"
                            );
                            ParseResult::Stop
                        }
                    }
                    GssApiBuffer::GssCfxWrap { token, payload } => {
                        if token.flags & KRB_CFX_FLAG_SEALED != 0 {
                            trace!(
                                "Encrypted GSS-API CFX KRB5 encapsulation detected, activating bypass"
                            );
                            return ParseResult::Stop;
                        }
                        // no encryption, only integrity
                        // debug!("Integrity-only GSS-API KRB5 encapsulation detected");
                        // XXX remove padding
                        //
                        self.parse_ldap_messages(payload, direction)
                    }
                    _ => {
                        trace!("Unknown GSS-API encapsulation, activating bypass");
                        ParseResult::Stop
                    }
                }
            }
            _ => {
                trace!("Unknown SASL layer {}, activating bypass", mech);
                ParseResult::Stop
            }
        }
    }

    pub fn parse_ldap_messages(&mut self, data: &[u8], direction: Direction) -> ParseResult {
        let mut v: Vec<u8>;
        // Check if TCP data is being defragmented
        let defrag_buffer = match self.defrag_buffer.len() {
            0 => data,
            _ => {
                // sanity check vector length to avoid memory exhaustion
                if self.defrag_buffer.len() + data.len() > 16_777_216 {
                    warn!("LDAP: defrag buffer overflow");
                    return ParseResult::Error;
                };
                v = self.defrag_buffer.split_off(0);
                v.extend_from_slice(data);
                v.as_slice()
            }
        };
        let mut cur_i = defrag_buffer;
        loop {
            if cur_i.is_empty() {
                break;
            }
            // read header and see if we have a complete message
            if check_ldap_message_complete(cur_i).is_err() {
                // XXX if bytes are exactly "01", drop? (often happens with GSS-API)
                if cur_i.len() == 1
                    && cur_i[0] == 1
                    && self.sasl_mech == Some("GSS-SPNEGO".to_string())
                {
                    warn!("Fixup for SASL/GSS-API: removing 1 byte");
                    // cur_i = &cur_i[1..];
                    break;
                }
                trace!("Fragmentation required (TCP level)");
                // if not complete, add to defrag buffer
                self.defrag_buffer.extend_from_slice(cur_i);
                // panic!("XXX Fragmented LDAP");
                break;
            }
            match parse_ldap_message(cur_i) {
                Ok((rem, msg)) => {
                    trace!("parse_ldap_message: {:?}", msg);
                    self.handle_message(&msg, direction);
                    cur_i = rem;
                }
                e => {
                    warn!("parse_ldap_message failed: {:?}", e);
                    break;
                }
            };
        }
        trace!("current transactions: {:?}", self.transactions);
        // if !cur_i.is_empty() {
        //     // XXX if bytes are exactly "01", drop? (often happens with GSS-API)
        //     warn!("Extra bytes after LDAP data");
        //     use nom::HexDisplay;
        //     println!("{}", cur_i.to_hex(16));
        // }
        ParseResult::Ok
    }

    pub fn handle_message(&mut self, msg: &LdapMessage, _direction: Direction) {
        let tx_op = msg.protocol_op.tag();
        let bind_dn = &self.bind_dn;
        let mut tx = self
            .transactions
            .entry(msg.message_id)
            .or_insert_with(|| LDAPTransaction::new(msg.message_id, tx_op, bind_dn));
        self.protocol_op = Some(tx_op);
        match &msg.protocol_op {
            ProtocolOp::BindRequest(req) => {
                // store msg_id
                self.ldap_request = Some(msg.message_id);
                // store bind DN
                self.bind_dn = Some(req.name.0.to_string());
                tx.bind_dn = Some(req.name.0.to_string());
                // if SASL, store mechanism
                if let AuthenticationChoice::Sasl(creds) = &req.authentication {
                    self.sasl_mech = Some(creds.mechanism.0.to_string());
                    if let Some(data) = &creds.credentials {
                        if !data.is_empty() {
                            use nom::HexDisplay;
                            println!("XXX\n{}", (&data).to_hex(16));
                            let (_, xxx) = parse_gssapi(data).unwrap(); // XXX
                            println!("{:?}", xxx);
                        }
                    }
                } else {
                    // XXX one connection can bind multiple times (and/or do stuff unbound)
                    self.sasl_mech = None;
                }
            }
            ProtocolOp::BindResponse(resp) => {
                self.ldap_result_code = Some(resp.result.result_code.0);
                tx.result_code = Some(resp.result.result_code.0);
                // check msg_id
                // check for success
                if resp.result.result_code == ResultCode::Success {
                    // if SASL authentication succeeded, SASL layers are now active
                    if self.sasl_mech.is_some() {
                        self.has_sasl_layers = true;
                    }
                } else {
                    self.bind_dn = None;
                }
            }
            ProtocolOp::ExtendedRequest(req) => {
                self.ldap_request = Some(msg.message_id);
                // check for STARTTLS
                if req.request_name.0 == "1.3.6.1.4.1.1466.20037" {
                    self.tls_request = Some(msg.message_id);
                }
            }
            ProtocolOp::ExtendedResponse(res) => {
                if let Some(req_id) = self.tls_request {
                    self.ldap_result_code = Some(res.result.result_code.0);
                    tx.result_code = Some(res.result.result_code.0);
                    if res.result.result_code == ResultCode::Success && msg.message_id == req_id {
                        self.has_starttls = true;
                        self.tls_parser = Some(TlsParser::new(b"LDAP/TLS"));
                    }
                }
            }
            ProtocolOp::SearchRequest(_)
            | ProtocolOp::ModifyRequest(_)
            | ProtocolOp::AddRequest(_)
            | ProtocolOp::ModDnRequest(_)
            | ProtocolOp::CompareRequest(_)
            | ProtocolOp::AbandonRequest(_) => {
                self.ldap_request = Some(msg.message_id);
            }
            ProtocolOp::ModifyResponse(r) => {
                self.ldap_result_code = Some(r.result.result_code.0);
                tx.result_code = Some(r.result.result_code.0);
            }
            ProtocolOp::SearchResultDone(r)
            | ProtocolOp::AddResponse(r)
            | ProtocolOp::ModDnResponse(r)
            | ProtocolOp::CompareResponse(r) => {
                self.ldap_result_code = Some(r.result_code.0);
                tx.result_code = Some(r.result_code.0);
            }
            _ => (),
        }
    }
}

impl LDAPTransaction {
    pub fn new(message_id: MessageID, tx_op: ProtocolOpTag, bind_dn: &Option<String>) -> Self {
        LDAPTransaction {
            message_id,
            tx_op,
            result_code: None,
            bind_dn: bind_dn.clone(),
        }
    }
}

const COMMON_LDAP_PORTS: &[u16] = &[389, 3268];

pub fn ldap_probe(i: &[u8], l4info: &L4Info) -> ProbeResult {
    if i.len() <= 2 {
        return ProbeResult::Unsure;
    }
    // debug!("Probing for LDAP");
    if parse_ldap_message(i).is_ok() {
        if COMMON_LDAP_PORTS.contains(&l4info.src_port)
            || COMMON_LDAP_PORTS.contains(&l4info.dst_port)
        {
            ProbeResult::Certain
        } else {
            ProbeResult::Unsure
        }
    } else {
        ProbeResult::NotForUs
    }
}

fn check_ldap_message_complete(i: &[u8]) -> BerResult<()> {
    let (i, header) = ber_read_element_header(i)?;
    let len = header.len.primitive()?;
    if i.len() >= len {
        Ok((i, ()))
    } else {
        Err(BerError::InvalidLength.into())
    }
}
<|endoftext|>


<commit_before>//! Clients using other programming languages should use the functions
//! in this mod. Please refer to the other chapters of the help for documentation of the functions.
extern crate basic_dsp_vector;
extern crate num_complex;

pub mod facade32;
pub mod facade64;
use basic_dsp_vector::{VoidResult, SingleBuffer, TransRes, PaddingOption, GenDspVec, ScalarResult,
                       ErrorReason};
use basic_dsp_vector::window_functions::*;
use basic_dsp_vector::conv_types::*;
use basic_dsp_vector::numbers::RealNumber;
use num_complex::Complex;

pub struct InteropVec<T>
    where T: RealNumber
{
    buffer: SingleBuffer<T>,
    vec: GenDspVec<Vec<T>, T>,
}

impl<T> InteropVec<T>
    where T: RealNumber
{
    pub fn convert_vec<F>(mut self, op: F) -> VectorInteropResult<InteropVec<T>>
        where F: Fn(&mut GenDspVec<Vec<T>, T>, &mut SingleBuffer<T>) -> VoidResult
    {
        let result = op(&mut self.vec, &mut self.buffer);
        match result {
            Ok(()) => VectorInteropResult{vector: Box::new(self), result_code: 0},
            Err(res) => {
                VectorInteropResult {
                    vector: Box::new(self),
                    result_code: translate_error(res),
                }
            }
        }
    }

    pub fn trans_vec<F>(self, op: F) -> VectorInteropResult<InteropVec<T>>
        where F: Fn(GenDspVec<Vec<T>, T>, &mut SingleBuffer<T>) -> TransRes<GenDspVec<Vec<T>, T>>
    {
        let mut buffer = self.buffer;
        let vec = self.vec;
        let result = op(vec, &mut buffer);
        match result {
            Ok(vec) => {
                VectorInteropResult {
                    vector: Box::new(InteropVec {
                        vec: vec,
                        buffer: buffer,
                    }),
                    result_code: 0,
                }
            }
            Err((err, vec)) => {
                VectorInteropResult {
                    vector: Box::new(InteropVec {
                        vec: vec,
                        buffer: buffer,
                    }),
                    result_code: translate_error(err),
                }
            }
        }
    }

    pub fn convert_scalar<F, TT>(&self, op: F, default: TT) -> ScalarInteropResult<TT>
        where F: Fn(&GenDspVec<Vec<T>, T>) -> ScalarResult<TT>
    {
        let result = op(&self.vec);
        match result {
            Ok(res) => {
                ScalarInteropResult {
                    result: res,
                    result_code: 0,
                }
            }
            Err(res) => {
                ScalarInteropResult {
                    result: default,
                    result_code: translate_error(res),
                }
            }
        }
    }

    pub fn decompose(self) -> (GenDspVec<Vec<T>, T>, SingleBuffer<T>) {
        (self.vec, self.buffer)
    }
}

pub fn convert_void(result: VoidResult) -> i32 {
    match result {
        Ok(()) => 9,
        Err(err) => translate_error(err),
    }
}

/// Error codes:
///
/// 1. VectorsMustHaveTheSameSize
/// 2. VectorMetaDataMustAgree
/// 3. VectorMustBeComplex
/// 4. VectorMustBeReal
/// 5. VectorMustBeInTimeDomain
/// 6. VectorMustBeInFrquencyDomain
/// 7. InvalidArgumentLength
/// 8. VectorMustBeConjSymmetric
/// 9. VectorMustHaveAnOddLength
/// 10. ArgumentFunctionMustBeSymmetric
/// 11. InvalidNumberOfArgumentsForCombinedOp
/// 12. VectorMustNotBeEmpty
///
/// all other values are undefined. If you see a value which isn't listed here then
/// please report a bug.
pub fn translate_error(reason: ErrorReason) -> i32 {
    match reason {
        ErrorReason::InputMustHaveTheSameSize => 1,
        ErrorReason::InputMetaDataMustAgree => 2,
        ErrorReason::InputMustBeComplex => 3,
        ErrorReason::InputMustBeReal => 4,
        ErrorReason::InputMustBeInTimeDomain => 5,
        ErrorReason::InputMustBeInFrequencyDomain => 6,
        ErrorReason::InvalidArgumentLength => 7,
        ErrorReason::InputMustBeConjSymmetric => 8,
        ErrorReason::InputMustHaveAnOddLength => 9,
        ErrorReason::ArgumentFunctionMustBeSymmetric => 10,
        ErrorReason::InvalidNumberOfArgumentsForCombinedOp => 11,
        ErrorReason::InputMustNotBeEmpty => 12,
        ErrorReason::InputMustHaveAnEvenLength => 13,
        ErrorReason::TypeCanNotResize => 14,
    }
}

pub fn translate_to_window_function<T>(value: i32) -> Box<WindowFunction<T>>
    where T: RealNumber
{
    if value == 0 {
        Box::new(TriangularWindow)
    } else {
        Box::new(HammingWindow::default())
    }
}

pub fn translate_to_real_convolution_function<T>(value: i32,
                                                 rolloff: T)
                                                 -> Box<RealImpulseResponse<T>>
    where T: RealNumber
{
    if value == 0 {
        Box::new(SincFunction::new())
    } else {
        Box::new(RaisedCosineFunction::new(rolloff))
    }
}

pub fn translate_to_real_frequency_response<T>(value: i32,
                                               rolloff: T)
                                               -> Box<RealFrequencyResponse<T>>
    where T: RealNumber
{
    if value == 0 {
        Box::new(SincFunction::new())
    } else {
        Box::new(RaisedCosineFunction::new(rolloff))
    }
}

pub fn translate_to_padding_option(value: i32) -> PaddingOption {
    match value {
        0 => PaddingOption::End,
        1 => PaddingOption::Surround,
        _ => PaddingOption::Center,
    }
}

/// Result of a vector operation. Check the ```result_code```.
#[repr(C)]
pub struct VectorInteropResult<T> {
    /// This value is zero in case of error. All other values mean that an error
    /// occurred and the data in the vector might be unchanged or invalid.
    /// Error codes are described in `translate_error`.
    pub result_code: i32,

    /// A pointer to a data vector.
    pub vector: Box<T>,
}

/// Result of a vector operation. Check the ```result_code```.
#[repr(C)]
pub struct BinaryVectorInteropResult<T> {
    /// This value is zero in case of error. All other values mean that an error
    /// occurred and the data in the vector might be unchanged or invalid.
    /// Error codes are described in `translate_error`.
    pub result_code: i32,

    /// A pointer to a data vector.
    pub vector1: Box<T>,

    /// A pointer to a data vector.
    pub vector2: Box<T>,
}

/// Result of a vector operation. Check the ```result_code```.
#[repr(C)]
pub struct ScalarInteropResult<T>
    where T: Sized
{
    /// This value is zero in case of error. All other values mean that an error
    /// occurred and the data in the vector might be unchanged or invalid.
    /// Error codes are described in `translate_error`.
    pub result_code: i32,

    /// The result
    pub result: T,
}

/// A window function which can be constructed outside this crate.
struct ForeignWindowFunction<T>
    where T: RealNumber
{
    /// The window function
    pub window_function: extern "C" fn(*const std::os::raw::c_void, usize, usize) -> T,

    /// The data which is passed to the window function
    ///
    /// Actual data type is a `const* c_void`, but Rust doesn't allow that because it's
    /// unsafe so we store
    /// it as `usize` and transmute it when necessary. Callers should make very sure safety
    /// is guaranteed.
    pub window_data: usize,

    /// Indicates whether this function is symmetric around 0 or not.
    /// Symmetry is defined as `self.window(x) == self.window(-x)`.
    pub is_symmetric: bool,
}

impl<T> WindowFunction<T> for ForeignWindowFunction<T>
    where T: RealNumber
{
    fn is_symmetric(&self) -> bool {
        self.is_symmetric
    }

    fn window(&self, idx: usize, points: usize) -> T {
        let fun = self.window_function;
        fun(self.window_data as *const std::os::raw::c_void, idx, points)
    }
}

/// A real function which can be constructed outside this crate.
struct ForeignRealConvolutionFunction<T>
    where T: RealNumber
{
    /// The function
    pub conv_function: extern "C" fn(*const std::os::raw::c_void, T) -> T,

    /// The data which is passed to the function.
    ///
    /// Actual data type is a `const* c_void`, but Rust doesn't allow that
    /// because it's unsafe so we store
    /// it as `usize` and transmute it when necessary. Callers should make
    /// very sure safety is guaranteed.
    pub conv_data: usize,

    /// Indicates whether this function is symmetric around 0 or not.
    /// Symmetry is defined as `self.calc(x) == self.calc(-x)`.
    pub is_symmetric: bool,
}

impl<T> RealImpulseResponse<T> for ForeignRealConvolutionFunction<T>
    where T: RealNumber
{
    fn is_symmetric(&self) -> bool {
        self.is_symmetric
    }

    fn calc(&self, x: T) -> T {
        let fun = self.conv_function;
        fun(self.conv_data as *const std::os::raw::c_void, x)
    }
}

impl<T> RealFrequencyResponse<T> for ForeignRealConvolutionFunction<T>
    where T: RealNumber
{
    fn is_symmetric(&self) -> bool {
        self.is_symmetric
    }

    fn calc(&self, x: T) -> T {
        let fun = self.conv_function;
        fun(self.conv_data as *const std::os::raw::c_void, x)
    }
}

/// A complex function which can be constructed outside this crate.
struct ForeignComplexConvolutionFunction<T>
    where T: RealNumber
{
    /// The function
    pub conv_function: extern "C" fn(*const std::os::raw::c_void, T) -> Complex<T>,

    /// The data which is passed to the window function
    ///
    /// Actual data type is a `const* c_void`, but Rust doesn't allow that
    /// because it's unsafe so we store
    /// it as `usize` and transmute it when necessary. Callers should make very
    /// sure safety is guaranteed.
    pub conv_data: usize,

    /// Indicates whether this function is symmetric around 0 or not.
    /// Symmetry is defined as `self.calc(x) == self.calc(-x)`.
    pub is_symmetric: bool,
}

impl<T> ComplexImpulseResponse<T> for ForeignComplexConvolutionFunction<T>
    where T: RealNumber
{
    fn is_symmetric(&self) -> bool {
        self.is_symmetric
    }

    fn calc(&self, x: T) -> Complex<T> {
        let fun = self.conv_function;
        fun(self.conv_data as *const std::os::raw::c_void, x)
    }
}

impl<T> ComplexFrequencyResponse<T> for ForeignComplexConvolutionFunction<T>
    where T: RealNumber
{
    fn is_symmetric(&self) -> bool {
        self.is_symmetric
    }

    /// Indicates whether this function is symmetric around 0 or not.
    /// Symmetry is defined as `self.calc(x) == self.calc(-x)`.
    fn calc(&self, x: T) -> Complex<T> {
        let fun = self.conv_function;
        fun(self.conv_data as *const std::os::raw::c_void, x)
    }
}
<commit_msg>Improved error handling in interop crate<commit_after>//! Clients using other programming languages should use the functions
//! in this mod. Please refer to the other chapters of the help for documentation of the functions.
extern crate basic_dsp_vector;
extern crate num_complex;

pub mod facade32;
pub mod facade64;
use basic_dsp_vector::{VoidResult, SingleBuffer, TransRes, PaddingOption, GenDspVec, ScalarResult,
                       ErrorReason};
use basic_dsp_vector::window_functions::*;
use basic_dsp_vector::conv_types::*;
use basic_dsp_vector::numbers::RealNumber;
use num_complex::Complex;

pub struct InteropVec<T>
    where T: RealNumber
{
    buffer: SingleBuffer<T>,
    vec: GenDspVec<Vec<T>, T>,
}

impl<T> InteropVec<T>
    where T: RealNumber
{
    pub fn convert_vec<F>(mut self, op: F) -> VectorInteropResult<InteropVec<T>>
        where F: Fn(&mut GenDspVec<Vec<T>, T>, &mut SingleBuffer<T>) -> VoidResult
    {
        let result = op(&mut self.vec, &mut self.buffer);
        match result {
            Ok(()) => {
                let vector = Box::new(self);
                let result_code = get_error_marker(&vector);
                VectorInteropResult{ vector, result_code }
            },
            Err(res) => {
                VectorInteropResult {
                    vector: Box::new(self),
                    result_code: translate_error(res),
                }
            }
        }
    }

    pub fn trans_vec<F>(self, op: F) -> VectorInteropResult<InteropVec<T>>
        where F: Fn(GenDspVec<Vec<T>, T>, &mut SingleBuffer<T>) -> TransRes<GenDspVec<Vec<T>, T>>
    {
        let mut buffer = self.buffer;
        let vec = self.vec;
        let result = op(vec, &mut buffer);
        match result {
            Ok(vec) => {
                let vector = Box::new(InteropVec {
                    vec: vec,
                    buffer: buffer,
                });
                let result_code = get_error_marker(&vector);
                VectorInteropResult {
                    vector,
                    result_code,
                }
            }
            Err((err, vec)) => {
                VectorInteropResult {
                    vector: Box::new(InteropVec {
                        vec: vec,
                        buffer: buffer,
                    }),
                    result_code: translate_error(err),
                }
            }
        }
    }

    pub fn convert_scalar<F, TT>(&self, op: F, default: TT) -> ScalarInteropResult<TT>
        where F: Fn(&GenDspVec<Vec<T>, T>) -> ScalarResult<TT>
    {
        let result = op(&self.vec);
        match result {
            Ok(res) => {
                ScalarInteropResult {
                    result: res,
                    result_code: get_error_marker(&self),
                }
            }
            Err(res) => {
                ScalarInteropResult {
                    result: default,
                    result_code: translate_error(res),
                }
            }
        }
    }

    pub fn decompose(self) -> (GenDspVec<Vec<T>, T>, SingleBuffer<T>) {
        (self.vec, self.buffer)
    }
}

pub fn convert_void(result: VoidResult) -> i32 {
    match result {
        Ok(()) => 9,
        Err(err) => translate_error(err),
    }
}

/// Error codes:
///
/// -1. Operation isn't valid for the given vector types, check real/complex and time/freq data
/// 1. VectorsMustHaveTheSameSize
/// 2. VectorMetaDataMustAgree
/// 3. VectorMustBeComplex
/// 4. VectorMustBeReal
/// 5. VectorMustBeInTimeDomain
/// 6. VectorMustBeInFrquencyDomain
/// 7. InvalidArgumentLength
/// 8. VectorMustBeConjSymmetric
/// 9. VectorMustHaveAnOddLength
/// 10. ArgumentFunctionMustBeSymmetric
/// 11. InvalidNumberOfArgumentsForCombinedOp
/// 12. VectorMustNotBeEmpty
///
/// all other values are undefined. If you see a value which isn't listed here then
/// please report a bug.
pub fn translate_error(reason: ErrorReason) -> i32 {
    match reason {
        ErrorReason::InputMustHaveTheSameSize => 1,
        ErrorReason::InputMetaDataMustAgree => 2,
        ErrorReason::InputMustBeComplex => 3,
        ErrorReason::InputMustBeReal => 4,
        ErrorReason::InputMustBeInTimeDomain => 5,
        ErrorReason::InputMustBeInFrequencyDomain => 6,
        ErrorReason::InvalidArgumentLength => 7,
        ErrorReason::InputMustBeConjSymmetric => 8,
        ErrorReason::InputMustHaveAnOddLength => 9,
        ErrorReason::ArgumentFunctionMustBeSymmetric => 10,
        ErrorReason::InvalidNumberOfArgumentsForCombinedOp => 11,
        ErrorReason::InputMustNotBeEmpty => 12,
        ErrorReason::InputMustHaveAnEvenLength => 13,
        ErrorReason::TypeCanNotResize => 14,
    }
}

/// Returns the error code depending on the error marker of a vector.
pub fn get_error_marker<T: RealNumber>(vec: &InteropVec<T>) -> i32 {
    if vec.vec.is_erroneous() { -1 } else { 0 }
}

pub fn translate_to_window_function<T>(value: i32) -> Box<WindowFunction<T>>
    where T: RealNumber
{
    if value == 0 {
        Box::new(TriangularWindow)
    } else {
        Box::new(HammingWindow::default())
    }
}

pub fn translate_to_real_convolution_function<T>(value: i32,
                                                 rolloff: T)
                                                 -> Box<RealImpulseResponse<T>>
    where T: RealNumber
{
    if value == 0 {
        Box::new(SincFunction::new())
    } else {
        Box::new(RaisedCosineFunction::new(rolloff))
    }
}

pub fn translate_to_real_frequency_response<T>(value: i32,
                                               rolloff: T)
                                               -> Box<RealFrequencyResponse<T>>
    where T: RealNumber
{
    if value == 0 {
        Box::new(SincFunction::new())
    } else {
        Box::new(RaisedCosineFunction::new(rolloff))
    }
}

pub fn translate_to_padding_option(value: i32) -> PaddingOption {
    match value {
        0 => PaddingOption::End,
        1 => PaddingOption::Surround,
        _ => PaddingOption::Center,
    }
}

/// Result of a vector operation. Check the ```result_code```.
#[repr(C)]
pub struct VectorInteropResult<T> {
    /// This value is zero in case of error. All other values mean that an error
    /// occurred and the data in the vector might be unchanged or invalid.
    /// Error codes are described in `translate_error`.
    pub result_code: i32,

    /// A pointer to a data vector.
    pub vector: Box<T>,
}

/// Result of a vector operation. Check the ```result_code```.
#[repr(C)]
pub struct BinaryVectorInteropResult<T> {
    /// This value is zero in case of error. All other values mean that an error
    /// occurred and the data in the vector might be unchanged or invalid.
    /// Error codes are described in `translate_error`.
    pub result_code: i32,

    /// A pointer to a data vector.
    pub vector1: Box<T>,

    /// A pointer to a data vector.
    pub vector2: Box<T>,
}

/// Result of a vector operation. Check the ```result_code```.
#[repr(C)]
pub struct ScalarInteropResult<T>
    where T: Sized
{
    /// This value is zero in case of error. All other values mean that an error
    /// occurred and the data in the vector might be unchanged or invalid.
    /// Error codes are described in `translate_error`.
    pub result_code: i32,

    /// The result
    pub result: T,
}

/// A window function which can be constructed outside this crate.
struct ForeignWindowFunction<T>
    where T: RealNumber
{
    /// The window function
    pub window_function: extern "C" fn(*const std::os::raw::c_void, usize, usize) -> T,

    /// The data which is passed to the window function
    ///
    /// Actual data type is a `const* c_void`, but Rust doesn't allow that because it's
    /// unsafe so we store
    /// it as `usize` and transmute it when necessary. Callers should make very sure safety
    /// is guaranteed.
    pub window_data: usize,

    /// Indicates whether this function is symmetric around 0 or not.
    /// Symmetry is defined as `self.window(x) == self.window(-x)`.
    pub is_symmetric: bool,
}

impl<T> WindowFunction<T> for ForeignWindowFunction<T>
    where T: RealNumber
{
    fn is_symmetric(&self) -> bool {
        self.is_symmetric
    }

    fn window(&self, idx: usize, points: usize) -> T {
        let fun = self.window_function;
        fun(self.window_data as *const std::os::raw::c_void, idx, points)
    }
}

/// A real function which can be constructed outside this crate.
struct ForeignRealConvolutionFunction<T>
    where T: RealNumber
{
    /// The function
    pub conv_function: extern "C" fn(*const std::os::raw::c_void, T) -> T,

    /// The data which is passed to the function.
    ///
    /// Actual data type is a `const* c_void`, but Rust doesn't allow that
    /// because it's unsafe so we store
    /// it as `usize` and transmute it when necessary. Callers should make
    /// very sure safety is guaranteed.
    pub conv_data: usize,

    /// Indicates whether this function is symmetric around 0 or not.
    /// Symmetry is defined as `self.calc(x) == self.calc(-x)`.
    pub is_symmetric: bool,
}

impl<T> RealImpulseResponse<T> for ForeignRealConvolutionFunction<T>
    where T: RealNumber
{
    fn is_symmetric(&self) -> bool {
        self.is_symmetric
    }

    fn calc(&self, x: T) -> T {
        let fun = self.conv_function;
        fun(self.conv_data as *const std::os::raw::c_void, x)
    }
}

impl<T> RealFrequencyResponse<T> for ForeignRealConvolutionFunction<T>
    where T: RealNumber
{
    fn is_symmetric(&self) -> bool {
        self.is_symmetric
    }

    fn calc(&self, x: T) -> T {
        let fun = self.conv_function;
        fun(self.conv_data as *const std::os::raw::c_void, x)
    }
}

/// A complex function which can be constructed outside this crate.
struct ForeignComplexConvolutionFunction<T>
    where T: RealNumber
{
    /// The function
    pub conv_function: extern "C" fn(*const std::os::raw::c_void, T) -> Complex<T>,

    /// The data which is passed to the window function
    ///
    /// Actual data type is a `const* c_void`, but Rust doesn't allow that
    /// because it's unsafe so we store
    /// it as `usize` and transmute it when necessary. Callers should make very
    /// sure safety is guaranteed.
    pub conv_data: usize,

    /// Indicates whether this function is symmetric around 0 or not.
    /// Symmetry is defined as `self.calc(x) == self.calc(-x)`.
    pub is_symmetric: bool,
}

impl<T> ComplexImpulseResponse<T> for ForeignComplexConvolutionFunction<T>
    where T: RealNumber
{
    fn is_symmetric(&self) -> bool {
        self.is_symmetric
    }

    fn calc(&self, x: T) -> Complex<T> {
        let fun = self.conv_function;
        fun(self.conv_data as *const std::os::raw::c_void, x)
    }
}

impl<T> ComplexFrequencyResponse<T> for ForeignComplexConvolutionFunction<T>
    where T: RealNumber
{
    fn is_symmetric(&self) -> bool {
        self.is_symmetric
    }

    /// Indicates whether this function is symmetric around 0 or not.
    /// Symmetry is defined as `self.calc(x) == self.calc(-x)`.
    fn calc(&self, x: T) -> Complex<T> {
        let fun = self.conv_function;
        fun(self.conv_data as *const std::os::raw::c_void, x)
    }
}
<|endoftext|>


<commit_before>use array_macro::array;
use core::convert::Infallible;

/// Enum mapping type
///
/// This trait is internally used by `#[derive(Enum)]`. `Enum<T>` is
/// implemented by any enum type where V is a generic type representing a
/// value. The purpose of this generic type is to allow providing a value
/// type for arrays, as Rust currently does not support higher kinded types.
///
/// This trait is also implemented by `bool` and `u8`. While `u8` is
/// strictly speaking not an actual enum, there are good reasons to consider
/// it like one, as array of `u8` keys is a relatively common pattern.
pub trait Enum<V>: Sized {
    /// Representation of an enum map for type `V`, usually an array.
    type Array: Array<V>;
    /// Takes an usize, and returns an element matching `to_usize` function.
    fn from_usize(value: usize) -> Self;
    /// Returns an unique identifier for a value within range of `0..Array::LENGTH`.
    fn to_usize(self) -> usize;
    /// Creates an array using a function called for each argument.
    fn from_function<F: FnMut(Self) -> V>(f: F) -> Self::Array;
}

pub trait Array<V> {
    const LENGTH: usize;
    fn slice(&self) -> &[V];
    fn slice_mut(&mut self) -> &mut [V];
}

impl<V, const N: usize> Array<V> for [V; N] {
    const LENGTH: usize = N;
    fn slice(&self) -> &[V] {
        self
    }
    fn slice_mut(&mut self) -> &mut [V] {
        self
    }
}

impl<T> Enum<T> for bool {
    type Array = [T; 2];
    #[inline]
    fn from_usize(value: usize) -> Self {
        match value {
            0 => false,
            1 => true,
            _ => unreachable!(),
        }
    }
    #[inline]
    fn to_usize(self) -> usize {
        self as usize
    }
    #[inline]
    fn from_function<F: FnMut(Self) -> T>(mut f: F) -> [T; 2] {
        [f(false), f(true)]
    }
}

impl<T> Enum<T> for u8 {
    type Array = [T; 256];
    #[inline]
    fn from_usize(value: usize) -> Self {
        value as u8
    }
    #[inline]
    fn to_usize(self) -> usize {
        self as usize
    }
    #[inline]
    fn from_function<F: FnMut(Self) -> T>(mut f: F) -> [T; 256] {
        array![|i| f(i as u8); 256]
    }
}

impl<T> Enum<T> for Infallible {
    type Array = [T; 0];
    #[inline]
    fn from_usize(_: usize) -> Self {
        unreachable!();
    }
    #[inline]
    fn to_usize(self) -> usize {
        match self {}
    }
    #[inline]
    fn from_function<F: FnMut(Self) -> T>(_: F) -> [T; 0] {
        []
    }
}
<commit_msg>Remove "usually an array" from Array documentation<commit_after>use array_macro::array;
use core::convert::Infallible;

/// Enum mapping type
///
/// This trait is internally used by `#[derive(Enum)]`. `Enum<T>` is
/// implemented by any enum type where V is a generic type representing a
/// value. The purpose of this generic type is to allow providing a value
/// type for arrays, as Rust currently does not support higher kinded types.
///
/// This trait is also implemented by `bool` and `u8`. While `u8` is
/// strictly speaking not an actual enum, there are good reasons to consider
/// it like one, as array of `u8` keys is a relatively common pattern.
pub trait Enum<V>: Sized {
    /// Representation of an enum map for type `V`.
    type Array: Array<V>;
    /// Takes an usize, and returns an element matching `to_usize` function.
    fn from_usize(value: usize) -> Self;
    /// Returns an unique identifier for a value within range of `0..Array::LENGTH`.
    fn to_usize(self) -> usize;
    /// Creates an array using a function called for each argument.
    fn from_function<F: FnMut(Self) -> V>(f: F) -> Self::Array;
}

pub trait Array<V> {
    const LENGTH: usize;
    fn slice(&self) -> &[V];
    fn slice_mut(&mut self) -> &mut [V];
}

impl<V, const N: usize> Array<V> for [V; N] {
    const LENGTH: usize = N;
    fn slice(&self) -> &[V] {
        self
    }
    fn slice_mut(&mut self) -> &mut [V] {
        self
    }
}

impl<T> Enum<T> for bool {
    type Array = [T; 2];
    #[inline]
    fn from_usize(value: usize) -> Self {
        match value {
            0 => false,
            1 => true,
            _ => unreachable!(),
        }
    }
    #[inline]
    fn to_usize(self) -> usize {
        self as usize
    }
    #[inline]
    fn from_function<F: FnMut(Self) -> T>(mut f: F) -> [T; 2] {
        [f(false), f(true)]
    }
}

impl<T> Enum<T> for u8 {
    type Array = [T; 256];
    #[inline]
    fn from_usize(value: usize) -> Self {
        value as u8
    }
    #[inline]
    fn to_usize(self) -> usize {
        self as usize
    }
    #[inline]
    fn from_function<F: FnMut(Self) -> T>(mut f: F) -> [T; 256] {
        array![|i| f(i as u8); 256]
    }
}

impl<T> Enum<T> for Infallible {
    type Array = [T; 0];
    #[inline]
    fn from_usize(_: usize) -> Self {
        unreachable!();
    }
    #[inline]
    fn to_usize(self) -> usize {
        match self {}
    }
    #[inline]
    fn from_function<F: FnMut(Self) -> T>(_: F) -> [T; 0] {
        []
    }
}
<|endoftext|>