<commit_before>//! Cookie factory
#![feature(trace_macros)]
//macros:
//* write_vec!(vec, ...) -> Result(nb of bytes)
//* write_slice!(s, ...) -> Result(nb of bytes)
//* write_iterator!(it, ...) -> Result(nb of bytes)
//
//* config can be endianness
// combinator: c!(writer, config, conbinator/serializer)

/// doc
///
enum WriteMode {
  Slice,
  Byte
}

#[macro_export]
macro_rules! write_vec (
  ($vec: expr,  $submac:ident!($($args:tt)*)) => (
    {
      $submac!(write_vec_impl!($vec), $($args)*)
    }
  )
);

#[macro_export]
macro_rules! write_vec_impl (
  ($vec:expr, $config:expr, Slice, $value: expr) => (
    ($vec).extend(($value).iter().cloned());
  );
  ($vec:expr, $config:expr, Byte, $value: expr) => (
    ($vec).push($value);
  );
);

#[macro_export]
macro_rules! write_slice (
  ($sl:expr, $submac:ident!($($args:tt)*)) => (
    {
      let mut pos = 0;
      $submac!(write_slice_impl!(($sl, pos)), $($args)*)
    }
  );
);

#[macro_export]
macro_rules! write_slice_impl (
  (($sl:expr, $pos:expr), $config:expr, Slice, $value: expr) => (
    {
      std::slice::bytes::copy_memory($value, &mut ($sl)[$pos..($value.length)]);
      $pos += value.length;
    }
  );
  (($sl:expr, $pos:expr), $config:expr, Byte, $value: expr) => (
    {
      $sl[$pos] = $value as u8;
      $pos += 1;
    }
  );
);

#[macro_export]
macro_rules! opt (
  ($writer:ident!($($wargs:tt)*), $config:expr, $submac:ident!( $($args:tt)* )) => (
    {
      match $submac!($writer!($($wargs)*), $config, $($args)*) {
        Ok(nb) => Ok(nb),
        Err(_) => Ok(0)
      }
    }
  )
);

#[macro_export]
macro_rules! s_u8 (
  ($writer:ident!($($wargs:tt)*), $config:expr, $value:expr) => (
    {
      $writer!($($wargs)*, $config, Byte, $value)
    }
  );
);

#[macro_export]
macro_rules! s_u16 (
  ($writer:ident!($($wargs:tt)*), $config:expr, $value:expr) => (
    {
      $writer!($($wargs)*, $config, Byte, ($value >> 8) as u8);
      $writer!($($wargs)*, $config, Byte, $value as u8);
    }
  );
);

#[macro_export]
macro_rules! s_u32 (
  ($writer:ident!($($wargs:tt)*), $config:expr, $value:expr) => (
    {
      $writer!($($wargs)*, $config, Byte, ($value >> 24) as u8);
      $writer!($($wargs)*, $config, Byte, ($value >> 16) as u8);
      $writer!($($wargs)*, $config, Byte, ($value >> 8) as u8);
      $writer!($($wargs)*, $config, Byte, $value as u8);
    }
  );
);

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

  #[test]
  fn writer_vec() {
    trace_macros!(true);
    let mut v = Vec::new();
    let res = write_vec!(v, s_u32!((), 2147483647_u32));
    trace_macros!(false);
    println!("res: {:?}", res);
    println!("vec: {:?}", v);
    assert_eq!(&v[..], &[0x7f, 0xff, 0xff, 0xff]);
  }

  #[test]
  fn writer_slice() {
    trace_macros!(true);
    let mut v   = Vec::with_capacity(4);
    v.extend(iter::repeat(0).take(4));
    let res = write_slice!(&mut v[..], s_u32!((), 2147483647_u32));
    trace_macros!(false);
    println!("res: {:?}", res);
    assert_eq!(&v[..], &[0x7f, 0xff, 0xff, 0xff]);
  }
}
<commit_msg>serialize arrays<commit_after>//! Cookie factory
#![feature(trace_macros)]
//macros:
//* write_vec!(vec, ...) -> Result(nb of bytes)
//* write_slice!(s, ...) -> Result(nb of bytes)
//* write_iterator!(it, ...) -> Result(nb of bytes)
//
//* config can be endianness
// combinator: c!(writer, config, conbinator/serializer)

/// doc
///
enum WriteMode {
  Slice,
  Byte
}

#[macro_export]
macro_rules! write_vec (
  ($vec: expr,  $submac:ident!($($args:tt)*)) => (
    {
      $submac!(write_vec_impl!($vec), $($args)*)
    }
  )
);

#[macro_export]
macro_rules! write_vec_impl (
  ($vec:expr, $config:expr, Slice, $value: expr) => (
    ($vec).extend(($value).iter().cloned());
  );
  ($vec:expr, $config:expr, Byte, $value: expr) => (
    ($vec).push($value);
  );
);

#[macro_export]
macro_rules! write_slice (
  ($sl:expr, $submac:ident!($($args:tt)*)) => (
    {
      let mut pos = 0;
      $submac!(write_slice_impl!(($sl, pos)), $($args)*)
    }
  );
);

#[macro_export]
macro_rules! write_slice_impl (
  (($sl:expr, $pos:expr), $config:expr, Slice, $value: expr) => (
    {
      std::slice::bytes::copy_memory($value, &mut ($sl)[$pos..($value.length)]);
      $pos += value.length;
    }
  );
  (($sl:expr, $pos:expr), $config:expr, Byte, $value: expr) => (
    {
      $sl[$pos] = $value as u8;
      $pos += 1;
    }
  );
);

#[macro_export]
macro_rules! opt (
  ($writer:ident!($($wargs:tt)*), $config:expr, $submac:ident!( $($args:tt)* )) => (
    {
      match $submac!($writer!($($wargs)*), $config, $($args)*) {
        Ok(nb) => Ok(nb),
        Err(_) => Ok(0)
      }
    }
  )
);

#[macro_export]
macro_rules! many (
  ($writer:ident!($($wargs:tt)*), $config:expr, $value:expr, $submac:ident!( $($args:tt)* )) => (
    {
      for el in $value {
        $submac:ident!( $writer!($($wargs)*), $config, $($args:tt)*, el )
      }
    }
  );
);

#[macro_export]
macro_rules! s_u8 (
  ($writer:ident!($($wargs:tt)*), $config:expr, $value:expr) => (
    {
      $writer!($($wargs)*, $config, Byte, $value)
    }
  );
);

#[macro_export]
macro_rules! s_u16 (
  ($writer:ident!($($wargs:tt)*), $config:expr, $value:expr) => (
    {
      $writer!($($wargs)*, $config, Byte, ($value >> 8) as u8);
      $writer!($($wargs)*, $config, Byte, $value as u8);
    }
  );
);

#[macro_export]
macro_rules! s_u32 (
  ($writer:ident!($($wargs:tt)*), $config:expr, $value:expr) => (
    {
      $writer!($($wargs)*, $config, Byte, ($value >> 24) as u8);
      $writer!($($wargs)*, $config, Byte, ($value >> 16) as u8);
      $writer!($($wargs)*, $config, Byte, ($value >> 8) as u8);
      $writer!($($wargs)*, $config, Byte, $value as u8);
    }
  );
);

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

  #[test]
  fn writer_vec() {
    trace_macros!(true);
    let mut v = Vec::new();
    let res = write_vec!(v, s_u32!((), 2147483647_u32));
    trace_macros!(false);
    println!("res: {:?}", res);
    println!("vec: {:?}", v);
    assert_eq!(&v[..], &[0x7f, 0xff, 0xff, 0xff]);
  }

  #[test]
  fn writer_slice() {
    trace_macros!(true);
    let mut v   = Vec::with_capacity(4);
    v.extend(iter::repeat(0).take(4));
    let res = write_slice!(&mut v[..], s_u32!((), 2147483647_u32));
    trace_macros!(false);
    println!("res: {:?}", res);
    println!("vec: {:?}", v);
    assert_eq!(&v[..], &[0x7f, 0xff, 0xff, 0xff]);
  }

  #[test]
  fn writer_array() {
    trace_macros!(true);
    let mut v = Vec::new();
    let input = vec![0x7f, 0xff, 0xff, 0xff];

    let res = write_vec!(v, s_u32!((), 2147483647_u32));
    trace_macros!(false);
    println!("res: {:?}", res);
    println!("vec: {:?}", v);
    assert_eq!(&v[..], &[0x7f, 0xff, 0xff, 0xff]);
  }
}
<|endoftext|>


<commit_before>// Copyright 2015 Keegan McAllister.
// Copyright 2016 Corey Farwell.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// See `LICENSE` in this repository.

use std::io::{self, Read};
use std::{panic, process};

pub mod common;

/// Utility that reads a `Vec` of bytes from standard input (stdin)
/// and passes it to `closure`. All panics that occur within
/// `closure` will be treated as aborts. This is done so that
/// AFL considers a panic to be a crash.
///
/// # Examples
///
/// ```no_run
/// extern crate afl;
/// # struct Image;
/// # impl Image {
/// #     fn parse(_: &[u8]) {}
/// # }
///
/// fn main() {
///     afl::read_stdio_bytes(|read| {
///         Image::parse(&read)
///     })
/// }
/// ```
#[deprecated(
    since = "0.3.3",
    note = "This function does not use the `persistent mode` and `defered forkserver mode` and is therefore very slow.
Please use fuzz() or fuzz!() instead."
)]
pub fn read_stdio_bytes<F>(closure: F)
where
    F: Fn(Vec<u8>) + panic::RefUnwindSafe,
{
    let mut input = vec![];
    let result = io::stdin().read_to_end(&mut input);
    if result.is_err() {
        return;
    }
    let was_panic = panic::catch_unwind(|| {
        closure(input);
    });
    if was_panic.is_err() {
        process::abort();
    }
}

/// Utility that reads a `String` from standard input (stdin) and
/// passes it to `closure`. If a `String` cannot be constructed from
/// the data provided by standard input, `closure` will _not_ be
/// called. All panics that occur within `closure` will be treated as
/// aborts. This is done so that AFL considers a panic to be a crash.
///
/// # Examples
///
/// ```no_run
/// extern crate afl;
/// # struct Url;
/// # impl Url {
/// #     fn parse(_: &str) {}
/// # }
///
/// fn main() {
///     afl::read_stdio_string(|string| {
///         Url::parse(&string)
///     })
/// }
/// ```
#[deprecated(
    since = "0.3.3",
    note = "This function does not use the `persistent mode` and `defered forkserver mode` and is therefore very slow.
Please use fuzz() or fuzz!() instead."
)]
pub fn read_stdio_string<F>(closure: F)
where
    F: Fn(String) + panic::RefUnwindSafe,
{
    let mut input = String::new();
    let result = io::stdin().read_to_string(&mut input);
    if result.is_err() {
        return;
    }
    let was_panic = panic::catch_unwind(|| {
        closure(input);
    });
    if was_panic.is_err() {
        process::abort();
    }
}

// those functions are provided by the afl-llvm-rt static library
extern "C" {
    fn __afl_persistent_loop(counter: usize) -> isize;
    fn __afl_manual_init();
}

/// Fuzz a closure by passing it a `&[u8]`
///
/// This slice contains a "random" quantity of "random" data.
///
/// ```rust,no_run
/// # extern crate afl;
/// # use afl::fuzz;
/// # fn main() {
/// fuzz(true, |data|{
///     if data.len() != 6 {return}
///     if data[0] != b'q' {return}
///     if data[1] != b'w' {return}
///     if data[2] != b'e' {return}
///     if data[3] != b'r' {return}
///     if data[4] != b't' {return}
///     if data[5] != b'y' {return}
///     panic!("BOOM")
/// });
/// # }
/// ```
pub fn fuzz<F>(hook: bool, closure: F)
where
    F: Fn(&[u8]) + std::panic::RefUnwindSafe,
{
    // this marker strings needs to be in the produced executable for
    // afl-fuzz to detect `persistent mode` and `defered mode`
    static PERSIST_MARKER: &'static str = "##SIG_AFL_PERSISTENT##\0";
    static DEFERED_MARKER: &'static str = "##SIG_AFL_DEFER_FORKSRV##\0";

    // we now need a fake instruction to prevent the compiler from optimizing out
    // those marker strings
    unsafe { std::ptr::read_volatile(&PERSIST_MARKER) }; // hack used in https://github.com/bluss/bencher for black_box()
    unsafe { std::ptr::read_volatile(&DEFERED_MARKER) };
    // unsafe { asm!("" : : "r"(&PERSIST_MARKER)) }; // hack used in nightly's back_box(), requires feature asm
    // unsafe { asm!("" : : "r"(&DEFERED_MARKER)) };

    if hook {
      // sets panic hook to abort
      std::panic::set_hook(Box::new(|_| {
          std::process::abort();
      }));
    }

    let mut input = vec![];

    // initialize forkserver there
    unsafe { __afl_manual_init() };

    while unsafe { __afl_persistent_loop(1000) } != 0 {
        // get buffer from AFL through stdin
        let result = io::stdin().read_to_end(&mut input);
        if result.is_err() {
            return;
        }

        // We still catch unwinding panics just in case the fuzzed code modifies
        // the panic hook.
        // If so, the fuzzer will be unable to tell different bugs appart and you will
        // only be able to find one bug at a time before fixing it to then find a new one.
        let did_panic = std::panic::catch_unwind(|| {
            closure(&input);
        })
        .is_err();

        if did_panic {
            // hopefully the custom panic hook will be called before and abort the
            // process before the stack frames are unwinded.
            std::process::abort();
        }
        input.clear();
    }
}

/// Fuzz a closure-like block of code by passing it an object of arbitrary type.
///
/// You can choose the type of the argument using the syntax as in the example below.
/// Please check out the `arbitrary` crate to see which types are available.
///
/// For performance reasons, it is recommended that you use the native type `&[u8]` when possible.
///
/// ```rust,no_run
/// # #[macro_use] extern crate afl;
/// # fn main() {
/// fuzz!(|data: &[u8]| {
///     if data.len() != 6 {return}
///     if data[0] != b'q' {return}
///     if data[1] != b'w' {return}
///     if data[2] != b'e' {return}
///     if data[3] != b'r' {return}
///     if data[4] != b't' {return}
///     if data[5] != b'y' {return}
///     panic!("BOOM")
/// });
/// # }
/// ```
#[macro_export]
macro_rules! fuzz {
    (|$buf:ident| $body:block) => {
        afl::fuzz(true, |$buf| $body);
    };
    (|$buf:ident: &[u8]| $body:block) => {
        afl::fuzz(true, |$buf| $body);
    };
    (|$buf:ident: $dty: ty| $body:block) => {
        afl::fuzz(true, |$buf| {
            let $buf: $dty = {
                use arbitrary::{Arbitrary, RingBuffer};
                if let Ok(d) = RingBuffer::new($buf, $buf.len()).and_then(|mut b|{
                        Arbitrary::arbitrary(&mut b).map_err(|_| "")
                    }) {
                    d
                } else {
                    return
                }
            };

            $body
        });
    };
}

/// Fuzz a closure-like block of code by passing it an object of arbitrary type. Panics that are
/// caught inside the fuzzed code are not turned into crashes.
///
/// You can choose the type of the argument using the syntax as in the example below.
/// Please check out the `arbitrary` crate to see which types are available.
///
/// For performance reasons, it is recommended that you use the native type `&[u8]` when possible.
///
/// ```rust,no_run
/// # #[macro_use] extern crate afl;
/// # fn main() {
/// fuzz!(|data: &[u8]| {
///     if data.len() != 6 {return}
///     if data[0] != b'q' {return}
///     if data[1] != b'w' {return}
///     if data[2] != b'e' {return}
///     if data[3] != b'r' {return}
///     if data[4] != b't' {return}
///     if data[5] != b'y' {return}
///     panic!("BOOM")
/// });
/// # }
/// ```
#[macro_export]
macro_rules! fuzz_nohook {
    (|$buf:ident| $body:block) => {
        afl::fuzz(false, |$buf| $body);
    };
    (|$buf:ident: &[u8]| $body:block) => {
        afl::fuzz(false, |$buf| $body);
    };
    (|$buf:ident: $dty: ty| $body:block) => {
        afl::fuzz(false, |$buf| {
            let $buf: $dty = {
                use arbitrary::{Arbitrary, RingBuffer};
                if let Ok(d) = RingBuffer::new($buf, $buf.len())
                    .and_then(|mut b| Arbitrary::arbitrary(&mut b).map_err(|_| ""))
                {
                    d
                } else {
                    return;
                }
            };

            $body
        });
    };
}

#[cfg(test)]
mod test {
    /*
    use std::path::PathBuf;
    use std::process::{Command, Stdio};
    use std::thread;
    use std::time;

    extern crate libc;
    extern crate tempdir;

    fn target_path() -> PathBuf {
        if PathBuf::from("../target/debug/cargo-afl-fuzz").exists() {
            PathBuf::from("../target/debug/")
        } else if PathBuf::from("target/debug/cargo-afl-fuzz").exists() {
            PathBuf::from("target/debug/")
        } else {
            panic!("Could not find cargo-afl-fuzz!");
        }
    }

    #[test]
    fn test_cargo_afl_fuzz() {
        let temp_dir = tempdir::TempDir::new("aflrs").expect("Could not create temporary directory");
        let temp_dir_path = temp_dir.path();
        let mut child = Command::new(target_path().join("cargo-afl-fuzz"))
            .stdout(Stdio::inherit())
            .stderr(Stdio::inherit())
            .arg("-i")
            .arg(".")
            .arg("-o")
            .arg(temp_dir_path)
            .arg(target_path().join("examples").join("hello"))
            .spawn()
            .expect("Could not run cargo-afl-fuzz");
        thread::sleep(time::Duration::from_secs(7));
        child.kill().unwrap();
        assert!(temp_dir_path.join("fuzzer_stats").is_file());
    }
    */
}
<commit_msg>Update lib.rs<commit_after>// Copyright 2015 Keegan McAllister.
// Copyright 2016 Corey Farwell.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// See `LICENSE` in this repository.

use std::io::{self, Read};
use std::{panic, process};

mod common;
pub use common::*;

/// Utility that reads a `Vec` of bytes from standard input (stdin)
/// and passes it to `closure`. All panics that occur within
/// `closure` will be treated as aborts. This is done so that
/// AFL considers a panic to be a crash.
///
/// # Examples
///
/// ```no_run
/// extern crate afl;
/// # struct Image;
/// # impl Image {
/// #     fn parse(_: &[u8]) {}
/// # }
///
/// fn main() {
///     afl::read_stdio_bytes(|read| {
///         Image::parse(&read)
///     })
/// }
/// ```
#[deprecated(
    since = "0.3.3",
    note = "This function does not use the `persistent mode` and `defered forkserver mode` and is therefore very slow.
Please use fuzz() or fuzz!() instead."
)]
pub fn read_stdio_bytes<F>(closure: F)
where
    F: Fn(Vec<u8>) + panic::RefUnwindSafe,
{
    let mut input = vec![];
    let result = io::stdin().read_to_end(&mut input);
    if result.is_err() {
        return;
    }
    let was_panic = panic::catch_unwind(|| {
        closure(input);
    });
    if was_panic.is_err() {
        process::abort();
    }
}

/// Utility that reads a `String` from standard input (stdin) and
/// passes it to `closure`. If a `String` cannot be constructed from
/// the data provided by standard input, `closure` will _not_ be
/// called. All panics that occur within `closure` will be treated as
/// aborts. This is done so that AFL considers a panic to be a crash.
///
/// # Examples
///
/// ```no_run
/// extern crate afl;
/// # struct Url;
/// # impl Url {
/// #     fn parse(_: &str) {}
/// # }
///
/// fn main() {
///     afl::read_stdio_string(|string| {
///         Url::parse(&string)
///     })
/// }
/// ```
#[deprecated(
    since = "0.3.3",
    note = "This function does not use the `persistent mode` and `defered forkserver mode` and is therefore very slow.
Please use fuzz() or fuzz!() instead."
)]
pub fn read_stdio_string<F>(closure: F)
where
    F: Fn(String) + panic::RefUnwindSafe,
{
    let mut input = String::new();
    let result = io::stdin().read_to_string(&mut input);
    if result.is_err() {
        return;
    }
    let was_panic = panic::catch_unwind(|| {
        closure(input);
    });
    if was_panic.is_err() {
        process::abort();
    }
}

// those functions are provided by the afl-llvm-rt static library
extern "C" {
    fn __afl_persistent_loop(counter: usize) -> isize;
    fn __afl_manual_init();
}

/// Fuzz a closure by passing it a `&[u8]`
///
/// This slice contains a "random" quantity of "random" data.
///
/// ```rust,no_run
/// # extern crate afl;
/// # use afl::fuzz;
/// # fn main() {
/// fuzz(true, |data|{
///     if data.len() != 6 {return}
///     if data[0] != b'q' {return}
///     if data[1] != b'w' {return}
///     if data[2] != b'e' {return}
///     if data[3] != b'r' {return}
///     if data[4] != b't' {return}
///     if data[5] != b'y' {return}
///     panic!("BOOM")
/// });
/// # }
/// ```
pub fn fuzz<F>(hook: bool, closure: F)
where
    F: Fn(&[u8]) + std::panic::RefUnwindSafe,
{
    // this marker strings needs to be in the produced executable for
    // afl-fuzz to detect `persistent mode` and `defered mode`
    static PERSIST_MARKER: &'static str = "##SIG_AFL_PERSISTENT##\0";
    static DEFERED_MARKER: &'static str = "##SIG_AFL_DEFER_FORKSRV##\0";

    // we now need a fake instruction to prevent the compiler from optimizing out
    // those marker strings
    unsafe { std::ptr::read_volatile(&PERSIST_MARKER) }; // hack used in https://github.com/bluss/bencher for black_box()
    unsafe { std::ptr::read_volatile(&DEFERED_MARKER) };
    // unsafe { asm!("" : : "r"(&PERSIST_MARKER)) }; // hack used in nightly's back_box(), requires feature asm
    // unsafe { asm!("" : : "r"(&DEFERED_MARKER)) };

    if hook {
      // sets panic hook to abort
      std::panic::set_hook(Box::new(|_| {
          std::process::abort();
      }));
    }

    let mut input = vec![];

    // initialize forkserver there
    unsafe { __afl_manual_init() };

    while unsafe { __afl_persistent_loop(1000) } != 0 {
        // get buffer from AFL through stdin
        let result = io::stdin().read_to_end(&mut input);
        if result.is_err() {
            return;
        }

        // We still catch unwinding panics just in case the fuzzed code modifies
        // the panic hook.
        // If so, the fuzzer will be unable to tell different bugs appart and you will
        // only be able to find one bug at a time before fixing it to then find a new one.
        let did_panic = std::panic::catch_unwind(|| {
            closure(&input);
        })
        .is_err();

        if did_panic {
            // hopefully the custom panic hook will be called before and abort the
            // process before the stack frames are unwinded.
            std::process::abort();
        }
        input.clear();
    }
}

/// Fuzz a closure-like block of code by passing it an object of arbitrary type.
///
/// You can choose the type of the argument using the syntax as in the example below.
/// Please check out the `arbitrary` crate to see which types are available.
///
/// For performance reasons, it is recommended that you use the native type `&[u8]` when possible.
///
/// ```rust,no_run
/// # #[macro_use] extern crate afl;
/// # fn main() {
/// fuzz!(|data: &[u8]| {
///     if data.len() != 6 {return}
///     if data[0] != b'q' {return}
///     if data[1] != b'w' {return}
///     if data[2] != b'e' {return}
///     if data[3] != b'r' {return}
///     if data[4] != b't' {return}
///     if data[5] != b'y' {return}
///     panic!("BOOM")
/// });
/// # }
/// ```
#[macro_export]
macro_rules! fuzz {
    (|$buf:ident| $body:block) => {
        afl::fuzz(true, |$buf| $body);
    };
    (|$buf:ident: &[u8]| $body:block) => {
        afl::fuzz(true, |$buf| $body);
    };
    (|$buf:ident: $dty: ty| $body:block) => {
        afl::fuzz(true, |$buf| {
            let $buf: $dty = {
                use arbitrary::{Arbitrary, RingBuffer};
                if let Ok(d) = RingBuffer::new($buf, $buf.len()).and_then(|mut b|{
                        Arbitrary::arbitrary(&mut b).map_err(|_| "")
                    }) {
                    d
                } else {
                    return
                }
            };

            $body
        });
    };
}

/// Fuzz a closure-like block of code by passing it an object of arbitrary type. Panics that are
/// caught inside the fuzzed code are not turned into crashes.
///
/// You can choose the type of the argument using the syntax as in the example below.
/// Please check out the `arbitrary` crate to see which types are available.
///
/// For performance reasons, it is recommended that you use the native type `&[u8]` when possible.
///
/// ```rust,no_run
/// # #[macro_use] extern crate afl;
/// # fn main() {
/// fuzz!(|data: &[u8]| {
///     if data.len() != 6 {return}
///     if data[0] != b'q' {return}
///     if data[1] != b'w' {return}
///     if data[2] != b'e' {return}
///     if data[3] != b'r' {return}
///     if data[4] != b't' {return}
///     if data[5] != b'y' {return}
///     panic!("BOOM")
/// });
/// # }
/// ```
#[macro_export]
macro_rules! fuzz_nohook {
    (|$buf:ident| $body:block) => {
        afl::fuzz(false, |$buf| $body);
    };
    (|$buf:ident: &[u8]| $body:block) => {
        afl::fuzz(false, |$buf| $body);
    };
    (|$buf:ident: $dty: ty| $body:block) => {
        afl::fuzz(false, |$buf| {
            let $buf: $dty = {
                use arbitrary::{Arbitrary, RingBuffer};
                if let Ok(d) = RingBuffer::new($buf, $buf.len())
                    .and_then(|mut b| Arbitrary::arbitrary(&mut b).map_err(|_| ""))
                {
                    d
                } else {
                    return;
                }
            };

            $body
        });
    };
}

#[cfg(test)]
mod test {
    /*
    use std::path::PathBuf;
    use std::process::{Command, Stdio};
    use std::thread;
    use std::time;

    extern crate libc;
    extern crate tempdir;

    fn target_path() -> PathBuf {
        if PathBuf::from("../target/debug/cargo-afl-fuzz").exists() {
            PathBuf::from("../target/debug/")
        } else if PathBuf::from("target/debug/cargo-afl-fuzz").exists() {
            PathBuf::from("target/debug/")
        } else {
            panic!("Could not find cargo-afl-fuzz!");
        }
    }

    #[test]
    fn test_cargo_afl_fuzz() {
        let temp_dir = tempdir::TempDir::new("aflrs").expect("Could not create temporary directory");
        let temp_dir_path = temp_dir.path();
        let mut child = Command::new(target_path().join("cargo-afl-fuzz"))
            .stdout(Stdio::inherit())
            .stderr(Stdio::inherit())
            .arg("-i")
            .arg(".")
            .arg("-o")
            .arg(temp_dir_path)
            .arg(target_path().join("examples").join("hello"))
            .spawn()
            .expect("Could not run cargo-afl-fuzz");
        thread::sleep(time::Duration::from_secs(7));
        child.kill().unwrap();
        assert!(temp_dir_path.join("fuzzer_stats").is_file());
    }
    */
}
<|endoftext|>


<commit_before>extern crate toml;
extern crate rustc_serialize;

use rustc_serialize::json::{ Json, ToJson };
use std::collections::BTreeMap;
use std::io::Read;

pub struct Toml;
impl Toml {

  pub fn as_json(read: &mut Read) -> Option<Json> {
    let mut src = String::new();
    let _ = read.read_to_string(&mut src);
    let mut parser = toml::Parser::new(&src);

    fn value_as_json(toml: &toml::Value) -> Json {
      match *toml {
        toml::Value::Table(ref value)    => table_as_json(value),
        toml::Value::Array(ref array)    => {
          let mut vec = Vec::new();
          for value in array.iter() {
            vec.push(value_as_json(value));
          };
          vec.to_json()
        },
        toml::Value::String(ref value)   => value.to_json(),
        toml::Value::Integer(ref value)  => value.to_json(),
        toml::Value::Float(ref value)    => value.to_json(),
        toml::Value::Boolean(ref value)  => value.to_json(),
        toml::Value::Datetime(ref value) => value.to_json()
      }
    }

    fn table_as_json(table: &toml::Table) -> Json {
      let mut map = BTreeMap::new();
      for (k,v) in table.iter() {
        map.insert(k.to_string(), value_as_json(v));
      };
      map.to_json()
    }

    parser.parse().map(|tbl| table_as_json(&tbl))
  }

}

<commit_msg>j to t<commit_after>extern crate toml;
extern crate rustc_serialize;

use rustc_serialize::json;
use rustc_serialize::json::{ ToJson };
use std::collections::BTreeMap;
use std::io::Read;

pub struct Json;

impl Json {
  /// convert Json to Toml
  pub fn as_toml(read: &mut Read) -> Option<toml::Value> {
    let mut src = String::new();
    let _ = read.read_to_string(&mut src);
    let json = json::Json::from_str(&src);
    fn json_as_toml(value: &json::Json) -> toml::Value {
      match *value {
        json::Json::I64(ref v)     => toml::Value::Integer(v.clone()),
        json::Json::U64(ref v)     => toml::Value::Integer(v.clone() as i64),
        json::Json::F64(ref v)     => toml::Value::Float(v.clone()),
        json::Json::String(ref v)  => toml::Value::String(v.clone()),
        json::Json::Boolean(ref v) => toml::Value::Boolean(v.clone()),
        json::Json::Array(ref v)   => {
          let mut tl = Vec::<toml::Value>::new();
          for jv in v.iter() {
            tl.push(json_as_toml(jv));
          }
          toml::Value::Array(tl)
        },
        json::Json::Object(ref v)  => {
          let mut tm = BTreeMap::new();
          for (k,v) in v.iter() {
            tm.insert(k.clone(), json_as_toml(v));
          }
          toml::Value::Table(tm)
        },
        json::Json::Null           => toml::Value::String("".to_string())
      }
    }
    json.ok().map(|value| json_as_toml(&value))
  }
}

pub struct Toml;

impl Toml {
  /// Convert Toml to Json
  pub fn as_json(read: &mut Read) -> Option<json::Json> {
    let mut src = String::new();
    let _ = read.read_to_string(&mut src);
    let mut parser = toml::Parser::new(&src);

    fn value_as_json(toml: &toml::Value) -> json::Json {
      match *toml {
        toml::Value::Table(ref value)    => table_as_json(value),
        toml::Value::Array(ref array)    => {
          let mut vec = Vec::new();
          for value in array.iter() {
            vec.push(value_as_json(value));
          };
          vec.to_json()
        },
        toml::Value::String(ref value)   => value.to_json(),
        toml::Value::Integer(ref value)  => value.to_json(),
        toml::Value::Float(ref value)    => value.to_json(),
        toml::Value::Boolean(ref value)  => value.to_json(),
        toml::Value::Datetime(ref value) => value.to_json()
      }
    }

    fn table_as_json(table: &toml::Table) -> json::Json {
      let mut map = BTreeMap::new();
      for (k,v) in table.iter() {
        map.insert(k.to_string(), value_as_json(v));
      };
      map.to_json()
    }

    parser.parse().map(|tbl| table_as_json(&tbl))
  }

}

<|endoftext|>


<commit_before>extern crate toml;
extern crate reqwest;
extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
extern crate glob;
extern crate nix;

#[macro_use]
extern crate lazy_static;

use std::io::{Read,Write,Seek,SeekFrom,BufReader,BufRead};
use std::fs::{File,OpenOptions};
use std::ffi::CStr;
extern crate libc;

mod structs;
mod ghclient;
use ghclient::GithubClient;
mod buffer;
use buffer::Buffer;
mod cstructs;
use cstructs::{Passwd,Spwd,Group};

lazy_static! {
    static ref CLIENT:GithubClient = GithubClient::new(
        std::env::var("GHTEAMAUTH_CONFIG")
                 .unwrap_or(String::from("/etc/ghteam-auth.conf"))
                 .as_str()
    ).unwrap();
}

const RUN_DIR:&str = "/var/run/ghteam-auth";

#[allow(dead_code)]
enum NssStatus {
    TryAgain,
    Unavail,
    NotFound,
    Success,
}

impl From<NssStatus> for libc::c_int {
    fn from(status:NssStatus) -> libc::c_int {
        match status {
            NssStatus::TryAgain => -2,
            NssStatus::Unavail  => -1,
            NssStatus::NotFound => 0,
            NssStatus::Success  => 1,
        }
    }
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_getpwnam_r(cnameptr: *const libc::c_char,
                                         pw: *mut Passwd,
                                         buf: *mut libc::c_char,
                                         buflen: libc::size_t,
                                         _: *mut libc::c_int) -> libc::c_int {
    let mut buffer = Buffer::new(buf,buflen);
    let cname: &CStr = unsafe {CStr::from_ptr(cnameptr)};
    let name = String::from(cname.to_str().unwrap());
    let (_,members) = CLIENT.get_team_members().unwrap();
    match members.get(&name) {
        Some(member) => {
            match unsafe{(*pw).pack(
                    &mut buffer,
                    &member.login,
                    "x",
                    member.id as libc::uid_t,
                    CLIENT.conf.gid as libc::gid_t,
                    "",
                    &CLIENT.conf.home.replace("{}",member.login.as_str()),
                    &CLIENT.conf.sh,
            )} {
                Ok(_) => libc::c_int::from(NssStatus::Success),
                Err(_) => nix::Errno::ERANGE as libc::c_int
            }
        },
        None => libc::c_int::from(NssStatus::NotFound)
    }
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_getpwuid_r(uid: libc::uid_t,
                                         pw: *mut Passwd,
                                         buf: *mut libc::c_char,
                                         buflen: libc::size_t,
                                         _: *mut libc::c_int) -> libc::c_int {
    let mut buffer = Buffer::new(buf,buflen);
    let (_,members) = CLIENT.get_team_members().unwrap();
    for member in members.values() {
        if uid == member.id as libc::uid_t {
            match unsafe {(*pw).pack(
                    &mut buffer,
                    &member.login,
                    "x",
                    member.id as libc::uid_t,
                    CLIENT.conf.gid as libc::gid_t,
                    "",
                    &CLIENT.conf.home.replace("{}",member.login.as_str()),
                    &CLIENT.conf.sh,
                )} {
                Ok(_) => return libc::c_int::from(NssStatus::Success),
                Err(_) => return nix::Errno::ERANGE as libc::c_int
            }
        }
    }
    libc::c_int::from(NssStatus::NotFound)
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_setpwent() -> libc::c_int {
    let pid = unsafe { libc::getpid() };
    std::fs::create_dir_all(RUN_DIR).unwrap();
    let mut idx_file:File = File::create(format!("{}/{}.index",RUN_DIR,pid)).unwrap();
    idx_file.write(b"0").unwrap();
    let mut list_file:File = File::create(format!("{}/{}.list",RUN_DIR,pid)).unwrap();
    let (_,members) = CLIENT.get_team_members().unwrap();
    for member in members.values () {
        list_file.write(format!("{}\t{}\n",member.login,member.id).as_bytes()).unwrap();
    }
    libc::c_int::from(NssStatus::Success)
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_getpwent_r(
    pwbuf: *mut Passwd,
    buf: *mut libc::c_char,
    buflen: libc::size_t,
    pwbufp: *mut *mut Passwd,
    ) -> libc::c_int {
    let pid = unsafe { libc::getpid() };
    let mut idx_file:File =  OpenOptions::new().read(true).write(true).open(format!("{}/{}.index",RUN_DIR,pid)).unwrap();
    let mut idx_string = String::new();
    idx_file.read_to_string(&mut idx_string).unwrap();
    let idx:usize = idx_string.parse().unwrap();
    let list = BufReader::new(File::open(format!("{}/{}.list",RUN_DIR,pid)).unwrap());
    for (i,line) in list.lines().enumerate() {
        if i!=idx { continue }
        if let Ok(l) = line {
            let mut buffer = Buffer::new(buf,buflen);
            let words:Vec<&str> = l.split("\t").collect();
            match unsafe {(*pwbuf).pack(
                &mut buffer,
                words[0],
                "x",
                words[1].parse::<usize>().unwrap() as libc::uid_t,
                CLIENT.conf.gid as libc::gid_t,
                "",
                &CLIENT.conf.home.replace("{}",words[0]),
                &CLIENT.conf.sh,
            )} {
                Ok(_) => {
                    idx_file.seek(SeekFrom::Start(0)).unwrap();
                    idx_file.write(format!("{}",idx+1).as_bytes()).unwrap();
                    unsafe {*pwbufp = pwbuf};
                    return libc::c_int::from(NssStatus::Success)
                },
                Err(_) => {
                    unsafe {*pwbufp = std::ptr::null_mut()};
                    return nix::Errno::ERANGE as libc::c_int
                }
            }
        }
    }
    libc::c_int::from(NssStatus::Unavail)
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_endpwent() -> libc::c_int {
    let pid = unsafe { libc::getpid() };
    std::fs::remove_file(format!("{}/{}.index",RUN_DIR,pid)).unwrap_or(());
    std::fs::remove_file(format!("{}/{}.list",RUN_DIR,pid)).unwrap_or(());
    libc::c_int::from(NssStatus::Success)
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_getspnam_r(cnameptr: *const libc::c_char,
                                         spwd: *mut Spwd,
                                         buf: *mut libc::c_char,
                                         buflen: libc::size_t,
                                         _: *mut libc::c_int) -> libc::c_int {
    let mut buffer = Buffer::new(buf,buflen);
    let cname: &CStr = unsafe {CStr::from_ptr(cnameptr)};
    let name = String::from(cname.to_str().unwrap());
    let (_,members) = CLIENT.get_team_members().unwrap();
    match members.get(&name) {
        Some(member) => {
            match unsafe {(*spwd).pack(
                    &mut buffer,
                    &member.login,
                    "!!",
                    -1,-1,-1,-1,-1,-1,0
                )} {
                Ok(_) => libc::c_int::from(NssStatus::Success),
                Err(_) => nix::Errno::ERANGE as libc::c_int
            }

        },
        None => libc::c_int::from(NssStatus::NotFound)
    }
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_setspent() -> libc::c_int {
    let pid = unsafe { libc::getpid() };
    std::fs::create_dir_all(RUN_DIR).unwrap();
    let mut idx_file:File = File::create(format!("{}/{}.index",RUN_DIR,pid)).unwrap();
    idx_file.write(b"0").unwrap();
    let mut list_file:File = File::create(format!("{}/{}.list",RUN_DIR,pid)).unwrap();
    let (_,members) = CLIENT.get_team_members().unwrap();
    for member in members.values () {
        list_file.write(format!("{}\t{}\n",member.login,member.id).as_bytes()).unwrap();
    }
    libc::c_int::from(NssStatus::Success)
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_getspent_r(
    spbuf: *mut Spwd,
    buf: *mut libc::c_char,
    buflen: libc::size_t,
    spbufp: *mut *mut Spwd,
    ) -> libc::c_int {
    let pid = unsafe { libc::getpid() };
    let mut idx_file:File =  OpenOptions::new().read(true).write(true).open(format!("{}/{}.index",RUN_DIR,pid)).unwrap();
    let mut idx_string = String::new();
    idx_file.read_to_string(&mut idx_string).unwrap();
    let idx:usize = idx_string.parse().unwrap();
    let list = BufReader::new(File::open(format!("{}/{}.list",RUN_DIR,pid)).unwrap());
    for (i,line) in list.lines().enumerate() {
        if i!=idx { continue }
        if let Ok(l) = line {
            let mut buffer = Buffer::new(buf,buflen);
            let words:Vec<&str> = l.split("\t").collect();
            match unsafe {(*spbuf).pack(
                &mut buffer,
                words[0],
                "!!",
                -1,-1,-1,-1,-1,-1,0
            )} {
                Ok(_) => {
                    idx_file.seek(SeekFrom::Start(0)).unwrap();
                    idx_file.write(format!("{}",idx+1).as_bytes()).unwrap();
                    unsafe {*spbufp = spbuf};
                    return libc::c_int::from(NssStatus::Success)
                },
                Err(_) => {
                    unsafe {*spbufp = std::ptr::null_mut()};
                    return nix::Errno::ERANGE as libc::c_int
                }
            }
        }
    }
    libc::c_int::from(NssStatus::Unavail)
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_endspent() -> libc::c_int {
    let pid = unsafe { libc::getpid() };
    std::fs::remove_file(format!("{}/{}.index",RUN_DIR,pid)).unwrap_or(());
    std::fs::remove_file(format!("{}/{}.list",RUN_DIR,pid)).unwrap_or(());
    libc::c_int::from(NssStatus::Success)
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_getgrgid_r(gid: libc::gid_t,
                                         group: *mut Group,
                                         buf: *mut libc::c_char,
                                         buflen: libc::size_t,
                                         _: *mut libc::c_int) -> libc::c_int {
    let mut buffer = Buffer::new(buf,buflen);
    let (team,members) = CLIENT.get_team_members().unwrap();
    let members:Vec<&str> = members.values().map(|m| m.login.as_str()).collect();
    if gid == CLIENT.conf.gid as libc::gid_t {
        match unsafe{(*group).pack(
                &mut buffer,
                &team.name,
                "x",
                gid,
                &members)} {
            Ok(_) => libc::c_int::from(NssStatus::Success),
            Err(_) => nix::Errno::ERANGE as libc::c_int,
        }
    } else {
        libc::c_int::from(NssStatus::NotFound)
    }
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_getgrnam_r(cnameptr: *const libc::c_char,
                                         group: *mut Group,
                                         buf: *mut libc::c_char,
                                         buflen: libc::size_t,
                                         _: *mut libc::c_int) -> libc::c_int {
    let mut buffer = Buffer::new(buf,buflen);
    let cname: &CStr = unsafe {CStr::from_ptr(cnameptr)};
    let name = String::from(cname.to_str().unwrap());
    let (team,members) = CLIENT.get_team_members().unwrap();
    let members:Vec<&str> = members.values().map(|m| m.login.as_str()).collect();
    if name == CLIENT.conf.team {
        match unsafe{(*group).pack(
                &mut buffer,
                &team.name,
                "x",
                team.id as libc::gid_t,
                &members
            )} {
            Ok(_) => libc::c_int::from(NssStatus::Success),
            Err(_) => nix::Errno::ERANGE as libc::c_int
        }

    } else {
        libc::c_int::from(NssStatus::NotFound)
    }
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_setgrent() -> libc::c_int { libc::c_int::from(NssStatus::Success) }

#[no_mangle]
pub extern "C" fn _nss_ghteam_getgrent_r() -> libc::c_int { libc::c_int::from(NssStatus::Unavail) }

#[no_mangle]
pub extern "C" fn _nss_ghteam_endgrent() -> libc::c_int { libc::c_int::from(NssStatus::Success) }
<commit_msg>Add (set|get|end)grent implementation<commit_after>extern crate toml;
extern crate reqwest;
extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
extern crate glob;
extern crate nix;

#[macro_use]
extern crate lazy_static;

use std::io::{Read,Write,Seek,SeekFrom,BufReader,BufRead};
use std::fs::{File,OpenOptions};
use std::ffi::CStr;
extern crate libc;

mod structs;
mod ghclient;
use ghclient::GithubClient;
mod buffer;
use buffer::Buffer;
mod cstructs;
use cstructs::{Passwd,Spwd,Group};

lazy_static! {
    static ref CLIENT:GithubClient = GithubClient::new(
        std::env::var("GHTEAMAUTH_CONFIG")
                 .unwrap_or(String::from("/etc/ghteam-auth.conf"))
                 .as_str()
    ).unwrap();
}

const RUN_DIR:&str = "/var/run/ghteam-auth";

#[allow(dead_code)]
enum NssStatus {
    TryAgain,
    Unavail,
    NotFound,
    Success,
}

impl From<NssStatus> for libc::c_int {
    fn from(status:NssStatus) -> libc::c_int {
        match status {
            NssStatus::TryAgain => -2,
            NssStatus::Unavail  => -1,
            NssStatus::NotFound => 0,
            NssStatus::Success  => 1,
        }
    }
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_getpwnam_r(cnameptr: *const libc::c_char,
                                         pw: *mut Passwd,
                                         buf: *mut libc::c_char,
                                         buflen: libc::size_t,
                                         _: *mut libc::c_int) -> libc::c_int {
    let mut buffer = Buffer::new(buf,buflen);
    let cname: &CStr = unsafe {CStr::from_ptr(cnameptr)};
    let name = String::from(cname.to_str().unwrap());
    let (_,members) = CLIENT.get_team_members().unwrap();
    match members.get(&name) {
        Some(member) => {
            match unsafe{(*pw).pack(
                    &mut buffer,
                    &member.login,
                    "x",
                    member.id as libc::uid_t,
                    CLIENT.conf.gid as libc::gid_t,
                    "",
                    &CLIENT.conf.home.replace("{}",member.login.as_str()),
                    &CLIENT.conf.sh,
            )} {
                Ok(_) => libc::c_int::from(NssStatus::Success),
                Err(_) => nix::Errno::ERANGE as libc::c_int
            }
        },
        None => libc::c_int::from(NssStatus::NotFound)
    }
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_getpwuid_r(uid: libc::uid_t,
                                         pw: *mut Passwd,
                                         buf: *mut libc::c_char,
                                         buflen: libc::size_t,
                                         _: *mut libc::c_int) -> libc::c_int {
    let mut buffer = Buffer::new(buf,buflen);
    let (_,members) = CLIENT.get_team_members().unwrap();
    for member in members.values() {
        if uid == member.id as libc::uid_t {
            match unsafe {(*pw).pack(
                    &mut buffer,
                    &member.login,
                    "x",
                    member.id as libc::uid_t,
                    CLIENT.conf.gid as libc::gid_t,
                    "",
                    &CLIENT.conf.home.replace("{}",member.login.as_str()),
                    &CLIENT.conf.sh,
                )} {
                Ok(_) => return libc::c_int::from(NssStatus::Success),
                Err(_) => return nix::Errno::ERANGE as libc::c_int
            }
        }
    }
    libc::c_int::from(NssStatus::NotFound)
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_setpwent() -> libc::c_int {
    let pid = unsafe { libc::getpid() };
    std::fs::create_dir_all(RUN_DIR).unwrap();
    let mut idx_file:File = File::create(format!("{}/{}.index",RUN_DIR,pid)).unwrap();
    idx_file.write(b"0").unwrap();
    let mut list_file:File = File::create(format!("{}/{}.list",RUN_DIR,pid)).unwrap();
    let (_,members) = CLIENT.get_team_members().unwrap();
    for member in members.values () {
        list_file.write(format!("{}\t{}\n",member.login,member.id).as_bytes()).unwrap();
    }
    libc::c_int::from(NssStatus::Success)
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_getpwent_r(
    pwbuf: *mut Passwd,
    buf: *mut libc::c_char,
    buflen: libc::size_t,
    pwbufp: *mut *mut Passwd,
    ) -> libc::c_int {
    let pid = unsafe { libc::getpid() };
    let mut idx_file:File =  OpenOptions::new().read(true).write(true).open(format!("{}/{}.index",RUN_DIR,pid)).unwrap();
    let mut idx_string = String::new();
    idx_file.read_to_string(&mut idx_string).unwrap();
    let idx:usize = idx_string.parse().unwrap();
    let list = BufReader::new(File::open(format!("{}/{}.list",RUN_DIR,pid)).unwrap());
    for (i,line) in list.lines().enumerate() {
        if i!=idx { continue }
        if let Ok(l) = line {
            let mut buffer = Buffer::new(buf,buflen);
            let words:Vec<&str> = l.split("\t").collect();
            match unsafe {(*pwbuf).pack(
                &mut buffer,
                words[0],
                "x",
                words[1].parse::<usize>().unwrap() as libc::uid_t,
                CLIENT.conf.gid as libc::gid_t,
                "",
                &CLIENT.conf.home.replace("{}",words[0]),
                &CLIENT.conf.sh,
            )} {
                Ok(_) => {
                    idx_file.seek(SeekFrom::Start(0)).unwrap();
                    idx_file.write(format!("{}",idx+1).as_bytes()).unwrap();
                    unsafe {*pwbufp = pwbuf};
                    return libc::c_int::from(NssStatus::Success)
                },
                Err(_) => {
                    unsafe {*pwbufp = std::ptr::null_mut()};
                    return nix::Errno::ERANGE as libc::c_int
                }
            }
        }
    }
    libc::c_int::from(NssStatus::Unavail)
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_endpwent() -> libc::c_int {
    let pid = unsafe { libc::getpid() };
    std::fs::remove_file(format!("{}/{}.index",RUN_DIR,pid)).unwrap_or(());
    std::fs::remove_file(format!("{}/{}.list",RUN_DIR,pid)).unwrap_or(());
    libc::c_int::from(NssStatus::Success)
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_getspnam_r(cnameptr: *const libc::c_char,
                                         spwd: *mut Spwd,
                                         buf: *mut libc::c_char,
                                         buflen: libc::size_t,
                                         _: *mut libc::c_int) -> libc::c_int {
    let mut buffer = Buffer::new(buf,buflen);
    let cname: &CStr = unsafe {CStr::from_ptr(cnameptr)};
    let name = String::from(cname.to_str().unwrap());
    let (_,members) = CLIENT.get_team_members().unwrap();
    match members.get(&name) {
        Some(member) => {
            match unsafe {(*spwd).pack(
                    &mut buffer,
                    &member.login,
                    "!!",
                    -1,-1,-1,-1,-1,-1,0
                )} {
                Ok(_) => libc::c_int::from(NssStatus::Success),
                Err(_) => nix::Errno::ERANGE as libc::c_int
            }

        },
        None => libc::c_int::from(NssStatus::NotFound)
    }
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_setspent() -> libc::c_int {
    let pid = unsafe { libc::getpid() };
    std::fs::create_dir_all(RUN_DIR).unwrap();
    let mut idx_file:File = File::create(format!("{}/{}.index",RUN_DIR,pid)).unwrap();
    idx_file.write(b"0").unwrap();
    let mut list_file:File = File::create(format!("{}/{}.list",RUN_DIR,pid)).unwrap();
    let (_,members) = CLIENT.get_team_members().unwrap();
    for member in members.values () {
        list_file.write(format!("{}\t{}\n",member.login,member.id).as_bytes()).unwrap();
    }
    libc::c_int::from(NssStatus::Success)
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_getspent_r(
    spbuf: *mut Spwd,
    buf: *mut libc::c_char,
    buflen: libc::size_t,
    spbufp: *mut *mut Spwd,
    ) -> libc::c_int {
    let pid = unsafe { libc::getpid() };
    let mut idx_file:File =  OpenOptions::new().read(true).write(true).open(format!("{}/{}.index",RUN_DIR,pid)).unwrap();
    let mut idx_string = String::new();
    idx_file.read_to_string(&mut idx_string).unwrap();
    let idx:usize = idx_string.parse().unwrap();
    let list = BufReader::new(File::open(format!("{}/{}.list",RUN_DIR,pid)).unwrap());
    for (i,line) in list.lines().enumerate() {
        if i!=idx { continue }
        if let Ok(l) = line {
            let mut buffer = Buffer::new(buf,buflen);
            let words:Vec<&str> = l.split("\t").collect();
            match unsafe {(*spbuf).pack(
                &mut buffer,
                words[0],
                "!!",
                -1,-1,-1,-1,-1,-1,0
            )} {
                Ok(_) => {
                    idx_file.seek(SeekFrom::Start(0)).unwrap();
                    idx_file.write(format!("{}",idx+1).as_bytes()).unwrap();
                    unsafe {*spbufp = spbuf};
                    return libc::c_int::from(NssStatus::Success)
                },
                Err(_) => {
                    unsafe {*spbufp = std::ptr::null_mut()};
                    return nix::Errno::ERANGE as libc::c_int
                }
            }
        }
    }
    libc::c_int::from(NssStatus::Unavail)
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_endspent() -> libc::c_int {
    let pid = unsafe { libc::getpid() };
    std::fs::remove_file(format!("{}/{}.index",RUN_DIR,pid)).unwrap_or(());
    std::fs::remove_file(format!("{}/{}.list",RUN_DIR,pid)).unwrap_or(());
    libc::c_int::from(NssStatus::Success)
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_getgrgid_r(gid: libc::gid_t,
                                         group: *mut Group,
                                         buf: *mut libc::c_char,
                                         buflen: libc::size_t,
                                         _: *mut libc::c_int) -> libc::c_int {
    let mut buffer = Buffer::new(buf,buflen);
    let (team,members) = CLIENT.get_team_members().unwrap();
    let members:Vec<&str> = members.values().map(|m| m.login.as_str()).collect();
    if gid == CLIENT.conf.gid as libc::gid_t {
        match unsafe{(*group).pack(
                &mut buffer,
                &team.name,
                "x",
                gid,
                &members)} {
            Ok(_) => libc::c_int::from(NssStatus::Success),
            Err(_) => nix::Errno::ERANGE as libc::c_int,
        }
    } else {
        libc::c_int::from(NssStatus::NotFound)
    }
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_getgrnam_r(cnameptr: *const libc::c_char,
                                         group: *mut Group,
                                         buf: *mut libc::c_char,
                                         buflen: libc::size_t,
                                         _: *mut libc::c_int) -> libc::c_int {
    let mut buffer = Buffer::new(buf,buflen);
    let cname: &CStr = unsafe {CStr::from_ptr(cnameptr)};
    let name = String::from(cname.to_str().unwrap());
    let (team,members) = CLIENT.get_team_members().unwrap();
    let members:Vec<&str> = members.values().map(|m| m.login.as_str()).collect();
    if name == CLIENT.conf.team {
        match unsafe{(*group).pack(
                &mut buffer,
                &team.name,
                "x",
                team.id as libc::gid_t,
                &members
            )} {
            Ok(_) => libc::c_int::from(NssStatus::Success),
            Err(_) => nix::Errno::ERANGE as libc::c_int
        }

    } else {
        libc::c_int::from(NssStatus::NotFound)
    }
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_setgrent() -> libc::c_int {
    let pid = unsafe { libc::getpid() };
    std::fs::create_dir_all(RUN_DIR).unwrap();
    let mut idx_file:File = File::create(format!("{}/{}.index",RUN_DIR,pid)).unwrap();
    idx_file.write(b"0").unwrap();
    let mut list_file:File = File::create(format!("{}/{}.glist",RUN_DIR,pid)).unwrap();
    let (team,members) = CLIENT.get_team_members().unwrap();
    let member_names = members.values().map(|x| x.login.as_str()).collect::<Vec<&str>>().join(" ");
    list_file.write(format!("{}\t{}\t{}\n", team.name, team.id, member_names).as_bytes()).unwrap();
    libc::c_int::from(NssStatus::Success)
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_getgrent_r(
    grbuf: *mut Group,
    buf: *mut libc::c_char,
    buflen: libc::size_t,
    grbufp: *mut *mut Group,
    ) -> libc::c_int {
    let pid = unsafe { libc::getpid() };
    let mut idx_file:File =  OpenOptions::new().read(true).write(true).open(format!("{}/{}.index",RUN_DIR,pid)).unwrap();
    let mut idx_string = String::new();
    idx_file.read_to_string(&mut idx_string).unwrap();
    let idx:usize = idx_string.parse().unwrap();
    let list = BufReader::new(File::open(format!("{}/{}.glist",RUN_DIR,pid)).unwrap());
    for (i,line) in list.lines().enumerate() {
        if i!=idx { continue }
        if let Ok(l) = line {
            let mut buffer = Buffer::new(buf,buflen);
            let words:Vec<&str> = l.split("\t").collect();
            let member_names:Vec<&str> = words[2].split(" ").collect();
            match unsafe {(*grbuf).pack(
                &mut buffer,
                words[0],
                "x",
                words[1].parse::<usize>().unwrap() as libc::gid_t,
                &member_names
            )} {
                Ok(_) => {
                    idx_file.seek(SeekFrom::Start(0)).unwrap();
                    idx_file.write(format!("{}",idx+1).as_bytes()).unwrap();
                    unsafe {*grbufp = grbuf};
                    return libc::c_int::from(NssStatus::Success)
                },
                Err(_) => {
                    unsafe {*grbufp = std::ptr::null_mut()};
                    return nix::Errno::ERANGE as libc::c_int
                }
            }
        }
    }
    libc::c_int::from(NssStatus::Unavail)
}

#[no_mangle]
pub extern "C" fn _nss_ghteam_endgrent() -> libc::c_int {
    let pid = unsafe { libc::getpid() };
    std::fs::remove_file(format!("{}/{}.index",RUN_DIR,pid)).unwrap_or(());
    std::fs::remove_file(format!("{}/{}.glist",RUN_DIR,pid)).unwrap_or(());
    libc::c_int::from(NssStatus::Success)
}
<|endoftext|>


<commit_before>use types::*;

use nom::{be_i8, be_u8, be_i16, be_u16, be_i32, be_u32, be_i64, be_u64, be_f32, be_f64};

/* FIXME: we convert to a Some so that nom can happily handle some _ case which would otherwise fail as we're exhaustive */
named!(pub parse_value<AMQPValue>,                 switch!(map!(parse_type, |t| Some(t)),
    Some(AMQPType::Boolean)        => map!(call!(parse_boolean),          |b| AMQPValue::Boolean(b))        |
    Some(AMQPType::ShortShortInt)  => map!(call!(parse_short_short_int),  |i| AMQPValue::ShortShortInt(i))  |
    Some(AMQPType::ShortShortUInt) => map!(call!(parse_short_short_uint), |u| AMQPValue::ShortShortUInt(u)) |
    Some(AMQPType::ShortInt)       => map!(call!(parse_short_int),        |i| AMQPValue::ShortInt(i))       |
    Some(AMQPType::ShortUInt)      => map!(call!(parse_short_uint),       |u| AMQPValue::ShortUInt(u))      |
    Some(AMQPType::LongInt)        => map!(call!(parse_long_int),         |i| AMQPValue::LongInt(i))        |
    Some(AMQPType::LongUInt)       => map!(call!(parse_long_uint),        |u| AMQPValue::LongUInt(u))       |
    Some(AMQPType::LongLongInt)    => map!(call!(parse_long_long_int),    |i| AMQPValue::LongLongInt(i))    |
    Some(AMQPType::LongLongUInt)   => map!(call!(parse_long_long_uint),   |u| AMQPValue::LongLongUInt(u))   |
    Some(AMQPType::Float)          => map!(call!(parse_float),            |f| AMQPValue::Float(f))          |
    Some(AMQPType::Double)         => map!(call!(parse_double),           |d| AMQPValue::Double(d))         |
    Some(AMQPType::DecimalValue)   => map!(call!(parse_decimal_value),    |d| AMQPValue::DecimalValue(d))   |
    Some(AMQPType::ShortString)    => map!(call!(parse_short_string),     |s| AMQPValue::ShortString(s))    |
    Some(AMQPType::LongString)     => map!(call!(parse_long_string),      |s| AMQPValue::LongString(s))     |
    Some(AMQPType::FieldArray)     => map!(call!(parse_field_array),      |a| AMQPValue::FieldArray(a))     |
    Some(AMQPType::Timestamp)      => map!(call!(parse_timestamp),        |t| AMQPValue::Timestamp(t))      |
    Some(AMQPType::FieldTable)     => map!(call!(parse_field_table),      |t| AMQPValue::FieldTable(t))     |
    Some(AMQPType::Void)           => value!(AMQPValue::Void)
));

named!(pub parse_type<AMQPType>,                   map_opt!(be_u8, |t| AMQPType::from_id(t as char)));

named!(pub parse_boolean<Boolean>,                 map!(be_u8, |b| b != 0));
named!(pub parse_short_short_int<ShortShortInt>,   call!(be_i8));
named!(pub parse_short_short_uint<ShortShortUInt>, call!(be_u8));
named!(pub parse_short_int<ShortInt>,              call!(be_i16));
named!(pub parse_short_uint<ShortUInt>,            call!(be_u16));
named!(pub parse_long_int<LongInt>,                call!(be_i32));
named!(pub parse_long_uint<LongUInt>,              call!(be_u32));
named!(pub parse_long_long_int<LongLongInt>,       call!(be_i64));
named!(pub parse_long_long_uint<LongLongUInt>,     call!(be_u64));
named!(pub parse_float<Float>,                     call!(be_f32));
named!(pub parse_double<Double>,                   call!(be_f64));
named!(pub parse_decimal_value<DecimalValue>,      do_parse!(scale: parse_short_short_uint >> value: parse_long_uint >> (DecimalValue { scale: scale, value: value, })));
named!(pub parse_short_string<ShortString>,        do_parse!(length: parse_short_short_uint >> s: take_str!(length) >> (s.to_string())));
named!(pub parse_long_string<LongString>,          do_parse!(length: parse_long_uint >> s: take_str!(length) >> (s.to_string())));
named!(pub parse_field_array<FieldArray>,          do_parse!(length: parse_long_int >> array: count!(parse_value, length as usize) >> (array)));
named!(pub parse_timestamp<Timestamp>,             call!(parse_long_long_uint));
named!(pub parse_field_table<FieldTable>,          do_parse!(length: parse_long_uint >> table: flat_map!(take!(length as usize), fold_many0!(complete!(pair!(parse_short_string, parse_value)), FieldTable::new(), |mut acc: FieldTable, (key, value)| {
    acc.insert(key, value);
    acc
})) >> (table)));

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

    use nom::IResult;

    const EMPTY: &'static [u8] = b"";

    #[test]
    fn test_parse_value() {
        assert_eq!(parse_value(&[84, 42, 42, 42, 42, 42,  42,  42,  42]),  IResult::Done(EMPTY, AMQPValue::Timestamp(3038287259199220266)));
        assert_eq!(parse_value(&[83, 0,  0,  0,  4,  116, 101, 115, 116]), IResult::Done(EMPTY, AMQPValue::LongString("test".to_string())));
    }

    #[test]
    fn test_parse_type() {
        assert_eq!(parse_type(&[116]), IResult::Done(EMPTY, AMQPType::Boolean));
        assert_eq!(parse_type(&[102]), IResult::Done(EMPTY, AMQPType::Float));
    }

    #[test]
    fn test_parse_boolean() {
        assert_eq!(parse_boolean(&[0]), IResult::Done(EMPTY, false));
        assert_eq!(parse_boolean(&[1]), IResult::Done(EMPTY, true));
    }

    #[test]
    fn test_parse_short_short_int() {
        assert_eq!(parse_short_short_int(&[0]),   IResult::Done(EMPTY, 0));
        assert_eq!(parse_short_short_int(&[255]), IResult::Done(EMPTY, -1));
    }

    #[test]
    fn test_parse_short_short_uint() {
        assert_eq!(parse_short_short_uint(&[0]),   IResult::Done(EMPTY, 0));
        assert_eq!(parse_short_short_uint(&[255]), IResult::Done(EMPTY, 255));
    }

    #[test]
    fn test_parse_short_int() {
        assert_eq!(parse_short_int(&[0,   0]),   IResult::Done(EMPTY, 0));
        assert_eq!(parse_short_int(&[255, 255]), IResult::Done(EMPTY, -1));
    }

    #[test]
    fn test_parse_short_uint() {
        assert_eq!(parse_short_uint(&[0,   0]),   IResult::Done(EMPTY, 0));
        assert_eq!(parse_short_uint(&[255, 255]), IResult::Done(EMPTY, 65535));
    }

    #[test]
    fn test_parse_long_int() {
        assert_eq!(parse_long_int(&[0,   0,   0,   0]),   IResult::Done(EMPTY, 0));
        assert_eq!(parse_long_int(&[255, 255, 255, 255]), IResult::Done(EMPTY, -1));
    }

    #[test]
    fn test_parse_long_uint() {
        assert_eq!(parse_long_uint(&[0,   0,   0,   0]),   IResult::Done(EMPTY, 0));
        assert_eq!(parse_long_uint(&[255, 255, 255, 255]), IResult::Done(EMPTY, 4294967295));
    }

    #[test]
    fn test_parse_long_long_int() {
        assert_eq!(parse_long_long_int(&[0,   0,   0,   0,   0,   0,   0,   0]),   IResult::Done(EMPTY, 0));
        assert_eq!(parse_long_long_int(&[255, 255, 255, 255, 255, 255, 255, 255]), IResult::Done(EMPTY, -1));
    }

    #[test]
    fn test_parse_long_long_uint() {
        assert_eq!(parse_long_long_uint(&[0,   0,   0,   0,   0,   0,   0,   0]),   IResult::Done(EMPTY, 0));
        assert_eq!(parse_long_long_uint(&[255, 255, 255, 255, 255, 255, 255, 255]), IResult::Done(EMPTY, 18446744073709551615));
    }

    #[test]
    fn test_parse_float() {
        assert_eq!(parse_float(&[0,  0,  0,  0]),  IResult::Done(EMPTY, 0.));
        assert_eq!(parse_float(&[42, 42, 42, 42]), IResult::Done(EMPTY, 0.00000000000015113662));
    }

    #[test]
    fn test_parse_double() {
        assert_eq!(parse_double(&[0,  0,  0,  0,  0,  0,  0,  0]),  IResult::Done(EMPTY, 0.));
        assert_eq!(parse_double(&[42, 42, 42, 42, 42, 42, 42, 42]), IResult::Done(EMPTY, 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014260258159703532));
    }

    #[test]
    fn test_parse_decimal_value() {
        assert_eq!(parse_decimal_value(&[0,   0,   0,   0,   0]),   IResult::Done(EMPTY, DecimalValue { scale: 0,   value: 0          }));
        assert_eq!(parse_decimal_value(&[255, 255, 255, 255, 255]), IResult::Done(EMPTY, DecimalValue { scale: 255, value: 4294967295 }));
    }

    #[test]
    fn test_parse_short_string() {
        assert_eq!(parse_short_string(&[0]),                     IResult::Done(EMPTY, ShortString::new()));
        assert_eq!(parse_short_string(&[4, 116, 101, 115, 116]), IResult::Done(EMPTY, "test".to_string()));
    }

    #[test]
    fn test_parse_long_string() {
        assert_eq!(parse_long_string(&[0, 0, 0, 0]),                     IResult::Done(EMPTY, LongString::new()));
        assert_eq!(parse_long_string(&[0, 0, 0, 4, 116, 101, 115, 116]), IResult::Done(EMPTY, "test".to_string()));
    }

    #[test]
    fn test_parse_field_array() {
        assert_eq!(parse_field_array(&[0, 0, 0, 0]),                                 IResult::Done(EMPTY, FieldArray::new()));
        assert_eq!(parse_field_array(&[0, 0, 0, 2, 115, 4, 116, 101, 115, 116, 86]), IResult::Done(EMPTY, vec![AMQPValue::ShortString("test".to_string()), AMQPValue::Void]));
    }

    #[test]
    fn test_parse_timestamp() {
        assert_eq!(parse_timestamp(&[0,   0,   0,   0,   0,   0,   0,   0]),   IResult::Done(EMPTY, 0));
        assert_eq!(parse_timestamp(&[255, 255, 255, 255, 255, 255, 255, 255]), IResult::Done(EMPTY, 18446744073709551615));
    }

    #[test]
    fn test_parse_field_table() {
        let mut table = FieldTable::new();
        table.insert("test".to_string(), AMQPValue::ShortString("test".to_string()));
        table.insert("tt".to_string(),   AMQPValue::Void);
        assert_eq!(parse_field_table(&[0, 0, 0, 0]),                                                                      IResult::Done(EMPTY, FieldTable::new()));
        assert_eq!(parse_field_table(&[0, 0, 0, 15, 4, 116, 101, 115, 116, 115, 4, 116, 101, 115, 116, 2, 116, 116, 86]), IResult::Done(EMPTY, table));
    }
}
<commit_msg>types: better float parsing tests<commit_after>use types::*;

use nom::{be_i8, be_u8, be_i16, be_u16, be_i32, be_u32, be_i64, be_u64, be_f32, be_f64};

/* FIXME: we convert to a Some so that nom can happily handle some _ case which would otherwise fail as we're exhaustive */
named!(pub parse_value<AMQPValue>,                 switch!(map!(parse_type, |t| Some(t)),
    Some(AMQPType::Boolean)        => map!(call!(parse_boolean),          |b| AMQPValue::Boolean(b))        |
    Some(AMQPType::ShortShortInt)  => map!(call!(parse_short_short_int),  |i| AMQPValue::ShortShortInt(i))  |
    Some(AMQPType::ShortShortUInt) => map!(call!(parse_short_short_uint), |u| AMQPValue::ShortShortUInt(u)) |
    Some(AMQPType::ShortInt)       => map!(call!(parse_short_int),        |i| AMQPValue::ShortInt(i))       |
    Some(AMQPType::ShortUInt)      => map!(call!(parse_short_uint),       |u| AMQPValue::ShortUInt(u))      |
    Some(AMQPType::LongInt)        => map!(call!(parse_long_int),         |i| AMQPValue::LongInt(i))        |
    Some(AMQPType::LongUInt)       => map!(call!(parse_long_uint),        |u| AMQPValue::LongUInt(u))       |
    Some(AMQPType::LongLongInt)    => map!(call!(parse_long_long_int),    |i| AMQPValue::LongLongInt(i))    |
    Some(AMQPType::LongLongUInt)   => map!(call!(parse_long_long_uint),   |u| AMQPValue::LongLongUInt(u))   |
    Some(AMQPType::Float)          => map!(call!(parse_float),            |f| AMQPValue::Float(f))          |
    Some(AMQPType::Double)         => map!(call!(parse_double),           |d| AMQPValue::Double(d))         |
    Some(AMQPType::DecimalValue)   => map!(call!(parse_decimal_value),    |d| AMQPValue::DecimalValue(d))   |
    Some(AMQPType::ShortString)    => map!(call!(parse_short_string),     |s| AMQPValue::ShortString(s))    |
    Some(AMQPType::LongString)     => map!(call!(parse_long_string),      |s| AMQPValue::LongString(s))     |
    Some(AMQPType::FieldArray)     => map!(call!(parse_field_array),      |a| AMQPValue::FieldArray(a))     |
    Some(AMQPType::Timestamp)      => map!(call!(parse_timestamp),        |t| AMQPValue::Timestamp(t))      |
    Some(AMQPType::FieldTable)     => map!(call!(parse_field_table),      |t| AMQPValue::FieldTable(t))     |
    Some(AMQPType::Void)           => value!(AMQPValue::Void)
));

named!(pub parse_type<AMQPType>,                   map_opt!(be_u8, |t| AMQPType::from_id(t as char)));

named!(pub parse_boolean<Boolean>,                 map!(be_u8, |b| b != 0));
named!(pub parse_short_short_int<ShortShortInt>,   call!(be_i8));
named!(pub parse_short_short_uint<ShortShortUInt>, call!(be_u8));
named!(pub parse_short_int<ShortInt>,              call!(be_i16));
named!(pub parse_short_uint<ShortUInt>,            call!(be_u16));
named!(pub parse_long_int<LongInt>,                call!(be_i32));
named!(pub parse_long_uint<LongUInt>,              call!(be_u32));
named!(pub parse_long_long_int<LongLongInt>,       call!(be_i64));
named!(pub parse_long_long_uint<LongLongUInt>,     call!(be_u64));
named!(pub parse_float<Float>,                     call!(be_f32));
named!(pub parse_double<Double>,                   call!(be_f64));
named!(pub parse_decimal_value<DecimalValue>,      do_parse!(scale: parse_short_short_uint >> value: parse_long_uint >> (DecimalValue { scale: scale, value: value, })));
named!(pub parse_short_string<ShortString>,        do_parse!(length: parse_short_short_uint >> s: take_str!(length) >> (s.to_string())));
named!(pub parse_long_string<LongString>,          do_parse!(length: parse_long_uint >> s: take_str!(length) >> (s.to_string())));
named!(pub parse_field_array<FieldArray>,          do_parse!(length: parse_long_int >> array: count!(parse_value, length as usize) >> (array)));
named!(pub parse_timestamp<Timestamp>,             call!(parse_long_long_uint));
named!(pub parse_field_table<FieldTable>,          do_parse!(length: parse_long_uint >> table: flat_map!(take!(length as usize), fold_many0!(complete!(pair!(parse_short_string, parse_value)), FieldTable::new(), |mut acc: FieldTable, (key, value)| {
    acc.insert(key, value);
    acc
})) >> (table)));

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

    use nom::IResult;

    const EMPTY: &'static [u8] = b"";

    #[test]
    fn test_parse_value() {
        assert_eq!(parse_value(&[84, 42, 42, 42, 42, 42,  42,  42,  42]),  IResult::Done(EMPTY, AMQPValue::Timestamp(3038287259199220266)));
        assert_eq!(parse_value(&[83, 0,  0,  0,  4,  116, 101, 115, 116]), IResult::Done(EMPTY, AMQPValue::LongString("test".to_string())));
    }

    #[test]
    fn test_parse_type() {
        assert_eq!(parse_type(&[116]), IResult::Done(EMPTY, AMQPType::Boolean));
        assert_eq!(parse_type(&[102]), IResult::Done(EMPTY, AMQPType::Float));
    }

    #[test]
    fn test_parse_boolean() {
        assert_eq!(parse_boolean(&[0]), IResult::Done(EMPTY, false));
        assert_eq!(parse_boolean(&[1]), IResult::Done(EMPTY, true));
    }

    #[test]
    fn test_parse_short_short_int() {
        assert_eq!(parse_short_short_int(&[0]),   IResult::Done(EMPTY, 0));
        assert_eq!(parse_short_short_int(&[255]), IResult::Done(EMPTY, -1));
    }

    #[test]
    fn test_parse_short_short_uint() {
        assert_eq!(parse_short_short_uint(&[0]),   IResult::Done(EMPTY, 0));
        assert_eq!(parse_short_short_uint(&[255]), IResult::Done(EMPTY, 255));
    }

    #[test]
    fn test_parse_short_int() {
        assert_eq!(parse_short_int(&[0,   0]),   IResult::Done(EMPTY, 0));
        assert_eq!(parse_short_int(&[255, 255]), IResult::Done(EMPTY, -1));
    }

    #[test]
    fn test_parse_short_uint() {
        assert_eq!(parse_short_uint(&[0,   0]),   IResult::Done(EMPTY, 0));
        assert_eq!(parse_short_uint(&[255, 255]), IResult::Done(EMPTY, 65535));
    }

    #[test]
    fn test_parse_long_int() {
        assert_eq!(parse_long_int(&[0,   0,   0,   0]),   IResult::Done(EMPTY, 0));
        assert_eq!(parse_long_int(&[255, 255, 255, 255]), IResult::Done(EMPTY, -1));
    }

    #[test]
    fn test_parse_long_uint() {
        assert_eq!(parse_long_uint(&[0,   0,   0,   0]),   IResult::Done(EMPTY, 0));
        assert_eq!(parse_long_uint(&[255, 255, 255, 255]), IResult::Done(EMPTY, 4294967295));
    }

    #[test]
    fn test_parse_long_long_int() {
        assert_eq!(parse_long_long_int(&[0,   0,   0,   0,   0,   0,   0,   0]),   IResult::Done(EMPTY, 0));
        assert_eq!(parse_long_long_int(&[255, 255, 255, 255, 255, 255, 255, 255]), IResult::Done(EMPTY, -1));
    }

    #[test]
    fn test_parse_long_long_uint() {
        assert_eq!(parse_long_long_uint(&[0,   0,   0,   0,   0,   0,   0,   0]),   IResult::Done(EMPTY, 0));
        assert_eq!(parse_long_long_uint(&[255, 255, 255, 255, 255, 255, 255, 255]), IResult::Done(EMPTY, 18446744073709551615));
    }

    #[test]
    fn test_parse_float() {
        assert_eq!(parse_float(&[0,  0,  0,   0]),  IResult::Done(EMPTY, 0.));
        assert_eq!(parse_float(&[66, 41, 174, 20]), IResult::Done(EMPTY, 42.42));
    }

    #[test]
    fn test_parse_double() {
        assert_eq!(parse_double(&[0,  0,  0,  0,   0,   0,  0,  0]),   IResult::Done(EMPTY, 0.));
        assert_eq!(parse_double(&[64, 69, 53, 194, 143, 92, 40, 246]), IResult::Done(EMPTY, 42.42));
    }

    #[test]
    fn test_parse_decimal_value() {
        assert_eq!(parse_decimal_value(&[0,   0,   0,   0,   0]),   IResult::Done(EMPTY, DecimalValue { scale: 0,   value: 0          }));
        assert_eq!(parse_decimal_value(&[255, 255, 255, 255, 255]), IResult::Done(EMPTY, DecimalValue { scale: 255, value: 4294967295 }));
    }

    #[test]
    fn test_parse_short_string() {
        assert_eq!(parse_short_string(&[0]),                     IResult::Done(EMPTY, ShortString::new()));
        assert_eq!(parse_short_string(&[4, 116, 101, 115, 116]), IResult::Done(EMPTY, "test".to_string()));
    }

    #[test]
    fn test_parse_long_string() {
        assert_eq!(parse_long_string(&[0, 0, 0, 0]),                     IResult::Done(EMPTY, LongString::new()));
        assert_eq!(parse_long_string(&[0, 0, 0, 4, 116, 101, 115, 116]), IResult::Done(EMPTY, "test".to_string()));
    }

    #[test]
    fn test_parse_field_array() {
        assert_eq!(parse_field_array(&[0, 0, 0, 0]),                                 IResult::Done(EMPTY, FieldArray::new()));
        assert_eq!(parse_field_array(&[0, 0, 0, 2, 115, 4, 116, 101, 115, 116, 86]), IResult::Done(EMPTY, vec![AMQPValue::ShortString("test".to_string()), AMQPValue::Void]));
    }

    #[test]
    fn test_parse_timestamp() {
        assert_eq!(parse_timestamp(&[0,   0,   0,   0,   0,   0,   0,   0]),   IResult::Done(EMPTY, 0));
        assert_eq!(parse_timestamp(&[255, 255, 255, 255, 255, 255, 255, 255]), IResult::Done(EMPTY, 18446744073709551615));
    }

    #[test]
    fn test_parse_field_table() {
        let mut table = FieldTable::new();
        table.insert("test".to_string(), AMQPValue::ShortString("test".to_string()));
        table.insert("tt".to_string(),   AMQPValue::Void);
        assert_eq!(parse_field_table(&[0, 0, 0, 0]),                                                                      IResult::Done(EMPTY, FieldTable::new()));
        assert_eq!(parse_field_table(&[0, 0, 0, 15, 4, 116, 101, 115, 116, 115, 4, 116, 101, 115, 116, 2, 116, 116, 86]), IResult::Done(EMPTY, table));
    }
}
<|endoftext|>


<commit_before>// Copyright (C) 2017, 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

use self::hook::{run_hook, run_hooks_from_dir};

use failure::Fail;
use serde::Serialize;
use std::path::Path;

mod hook;
mod metadata_value;

pub mod installation_set;

#[cfg(test)]
pub mod tests;

const PRODUCT_UID_HOOK: &str = "product-uid";
const VERSION_HOOK: &str = "version";
const HARDWARE_HOOK: &str = "hardware";
const DEVICE_IDENTITY_DIR: &str = "device-identity.d";
const DEVICE_ATTRIBUTES_DIR: &str = "device-attributes.d";

#[derive(Fail, Debug)]
pub enum Error {
    #[fail(display = "Invalid product UID")]
    InvalidProductUid,
    #[fail(display = "Product UID is missing")]
    MissingProductUid,
    #[fail(display = "Device Identity is missing")]
    MissingDeviceIdentity,
}

/// Metadata stores the firmware metadata information. It is
/// organized in multiple fields.
///
/// The Metadata is created loading its information from the running
/// firmware. It uses the `load` method for that.
#[derive(Debug, Serialize, PartialEq, Clone)]
#[serde(rename_all = "kebab-case")]
pub struct Metadata {
    /// Product UID which identifies the firmware on the management system
    pub product_uid: String,

    /// Version of firmware
    pub version: String,

    /// Hardware where the firmware is running
    pub hardware: String,

    /// Device Identity
    pub device_identity: metadata_value::MetadataValue,

    /// Device Attributes
    pub device_attributes: metadata_value::MetadataValue,
}

impl Metadata {
    pub fn from_path(path: &Path) -> Result<Self, failure::Error> {
        let product_uid_hook = path.join(PRODUCT_UID_HOOK);
        let version_hook = path.join(VERSION_HOOK);
        let hardware_hook = path.join(HARDWARE_HOOK);
        let device_identity_dir = path.join(DEVICE_IDENTITY_DIR);
        let device_attributes_dir = path.join(DEVICE_ATTRIBUTES_DIR);

        let metadata = Self {
            product_uid: run_hook(&product_uid_hook)?,
            version: run_hook(&version_hook)?,
            hardware: run_hook(&hardware_hook)?,
            device_identity: run_hooks_from_dir(&device_identity_dir)?,
            device_attributes: run_hooks_from_dir(&device_attributes_dir)?,
        };

        if metadata.product_uid.is_empty() {
            return Err(Error::MissingProductUid.into());
        }

        if metadata.product_uid.len() != 64 {
            return Err(Error::InvalidProductUid.into());
        }

        if metadata.device_identity.is_empty() {
            return Err(Error::MissingDeviceIdentity.into());
        }

        Ok(metadata)
    }
}
<commit_msg>firmware: Allow no device attributes to be valid<commit_after>// Copyright (C) 2017, 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

use self::hook::{run_hook, run_hooks_from_dir};

use failure::Fail;
use serde::Serialize;
use std::path::Path;

mod hook;
mod metadata_value;

pub mod installation_set;

#[cfg(test)]
pub mod tests;

const PRODUCT_UID_HOOK: &str = "product-uid";
const VERSION_HOOK: &str = "version";
const HARDWARE_HOOK: &str = "hardware";
const DEVICE_IDENTITY_DIR: &str = "device-identity.d";
const DEVICE_ATTRIBUTES_DIR: &str = "device-attributes.d";

#[derive(Fail, Debug)]
pub enum Error {
    #[fail(display = "Invalid product UID")]
    InvalidProductUid,
    #[fail(display = "Product UID is missing")]
    MissingProductUid,
    #[fail(display = "Device Identity is missing")]
    MissingDeviceIdentity,
}

/// Metadata stores the firmware metadata information. It is
/// organized in multiple fields.
///
/// The Metadata is created loading its information from the running
/// firmware. It uses the `load` method for that.
#[derive(Debug, Serialize, PartialEq, Clone)]
#[serde(rename_all = "kebab-case")]
pub struct Metadata {
    /// Product UID which identifies the firmware on the management system
    pub product_uid: String,

    /// Version of firmware
    pub version: String,

    /// Hardware where the firmware is running
    pub hardware: String,

    /// Device Identity
    pub device_identity: metadata_value::MetadataValue,

    /// Device Attributes
    pub device_attributes: metadata_value::MetadataValue,
}

impl Metadata {
    pub fn from_path(path: &Path) -> Result<Self, failure::Error> {
        let product_uid_hook = path.join(PRODUCT_UID_HOOK);
        let version_hook = path.join(VERSION_HOOK);
        let hardware_hook = path.join(HARDWARE_HOOK);
        let device_identity_dir = path.join(DEVICE_IDENTITY_DIR);
        let device_attributes_dir = path.join(DEVICE_ATTRIBUTES_DIR);

        let metadata = Self {
            product_uid: run_hook(&product_uid_hook)?,
            version: run_hook(&version_hook)?,
            hardware: run_hook(&hardware_hook)?,
            device_identity: run_hooks_from_dir(&device_identity_dir)?,
            device_attributes: run_hooks_from_dir(&device_attributes_dir).unwrap_or_default(),
        };

        if metadata.product_uid.is_empty() {
            return Err(Error::MissingProductUid.into());
        }

        if metadata.product_uid.len() != 64 {
            return Err(Error::InvalidProductUid.into());
        }

        if metadata.device_identity.is_empty() {
            return Err(Error::MissingDeviceIdentity.into());
        }

        Ok(metadata)
    }
}
<|endoftext|>