diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b83d222 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/Cargo.toml b/Cargo.toml index e24c35e..3f9409b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,9 +13,10 @@ keywords = ["gnunet", "gns", "p2p"] unix_socket = ">= 0.3.0" rand = ">= 0.3" byteorder = ">= 0.3.10" -error_def = ">= 0.3.6" +quick-error = ">= 1.2.2" rust-crypto = ">= 0.2.31" num = ">= 0.1.24" -regex = ">= 0.1.8" -regex_macros = ">= 0.1.8" +regex = ">= 1.0.0" +lazy_static = ">= 1.1.0" +libc = ">= 0.2.43" diff --git a/src/configuration/configuration.rs b/src/configuration/configuration.rs index 6f89bb1..7478f9e 100644 --- a/src/configuration/configuration.rs +++ b/src/configuration/configuration.rs @@ -1,101 +1,206 @@ +use paths; +use regex::Regex; use std; +use std::borrow::{Borrow, Cow}; use std::collections::{hash_map, HashMap}; -use std::borrow::{Borrow, IntoCow}; -use std::io::{self, Read, BufRead, BufReader}; -use std::num::{ParseIntError, ParseFloatError}; -use std::path::{Path, PathBuf}; -use std::fs::File; use std::ffi::OsStr; +use std::fs::File; +use std::io::{self, BufRead, BufReader, Read}; +use std::num::{ParseFloatError, ParseIntError}; +use std::path::{Path, PathBuf}; use std::str::FromStr; -use util; -use paths; use time; +use util; pub struct Cfg { data: HashMap>, } -error_def! CfgDefaultError { - NoDataDir - => "Failed to determine GNUnet installation data directory", - ReadDataDir { #[from] cause: io::Error } - => "Failed to read Gnunet installation data directory" ("Reason: {}", cause), - LoadFile { #[from] cause: CfgLoadRawError } - => "Failed to load config file" ("Reason: {}", cause), +quick_error! { + #[derive(Debug)] + pub enum ToolchainError { + InvalidToolchainName ( name: String ) { + display("invalid toolchain name: {}", name) + } + + UnknownToolchainVersion { version: String } { + display("unknown toolchain version: {}", version) + } + } } -error_def! CfgLoadRawError { - FileOpen { #[from] cause: io::Error } - => "Failed to open file" ("Reason: {}", cause), - Deserialize { #[from] cause: CfgDeserializeError } - => "Failed to deserialize config" ("Reason: {}", cause), +quick_error! { + #[derive(Debug)] + pub enum CfgDefaultError { + NoDataDir { + display("Failed to determine GNUnet installation data directory") + } + + ReadDataDir { cause: io::Error } { + display("Failed to read Gnunet installation data directory. Reason: {}", cause) + from(cause: io::Error) -> { cause: cause } + cause(cause) + } + + LoadFile { cause: CfgLoadRawError } { + display("Failed to load config file. Reason: {}", cause) + cause(cause) + from(cause: CfgLoadRawError) -> { cause: cause } + } + } } -error_def! CfgDeserializeError { - Io { #[from] cause: io::Error } - => "I/O error reading from reader" ("Specifically: {}", cause), - LoadInline { - cause: Box, - line_number: usize, - filename: String, - } => "Failed to load inline configuration file" ("line {}: Failed to load \"{}\" ({})", line_number, filename, cause), - InlineDisabled { - line_number: usize, - filename: String, - } => "@INLINE@ directive in config but allow_inline is disabled" ("line {}: Will not load file \"{}\"", line_number, filename), - Syntax { - line_number: usize, - line: String, - } => "Syntax error in configuration" ("line {}: Failed to parse \"{}\"", line_number, line), +quick_error! { + #[derive(Debug)] + pub enum CfgLoadRawError { + FileOpen { cause: io::Error } { + cause(cause) + from(cause: io::Error) -> { cause: cause } + display("Failed to open file. Reason: {}", cause) + } + + Deserialize { cause: CfgDeserializeError } { + cause(cause) + from(cause: CfgDeserializeError) -> { cause: cause } + display("Failed to deserialize config. Reason: {}", cause) + } + } } -error_def! CfgLoadError { - LoadDefault { #[from] cause: CfgDefaultError } - => "Failed to load system default configuration" ("Reason: {}", cause), - LoadFile { #[from] cause: CfgLoadRawError } - => "Failed to load the config file" ("Reason: {}", cause), +quick_error! { + #[derive(Debug)] + pub enum CfgDeserializeError { + Io { cause: io::Error }{ + display("I/O error reading from reader. Specifically: {}", cause) + from(cause: io::Error) -> { cause: cause } + cause(cause) + } + + LoadInline { cause: Box, line_number: usize, filename: String }{ + display("Failed to load inline configuration file. line {}: Failed to load \"{}\" ({})", line_number, filename, cause) + } + + InlineDisabled { line_number: usize, filename: String } { + display("@INLINE@ directive in config but allow_inline is disabled. line {}: Will not load file \"{}\"", line_number, filename) + } + + Syntax { line_number: usize, line: String } { + display("Syntax error in configuration. line {}: Failed to parse \"{}\"", line_number, line) + } + } } -error_def! CfgGetIntError { - NoSection => "The config does not contain a section with that name", - NoKey => "The config section does contain that key", - Parse { #[from] cause: ParseIntError } - => "The value is not a valid u64" ("Details: {}", cause), +quick_error! { + #[derive(Debug)] + pub enum CfgLoadError { + LoadDefault { cause: CfgDefaultError } { + display("Failed to load system default configuration. Reason: {}", cause) + cause(cause) + from(cause: CfgDefaultError) -> { cause: cause } + } + + LoadFile { cause: CfgLoadRawError } { + display("Failed to load the config file. Reason: {}", cause) + cause(cause) + from(cause: CfgLoadRawError) -> { cause: cause } + } + } } -error_def! CfgGetFloatError { - NoSection => "The config does not contain a section with that name", - NoKey => "The config section does contain that key", - Parse { #[from] cause: ParseFloatError } - => "The value is not a valid f32" ("Details: {}", cause), +quick_error! { + #[derive(Debug)] + pub enum CfgGetIntError { + NoSection { + display("The config does not contain a section with that name") + } + + NoKey { + display("The config section does contain that key") + } + + Parse { cause: ParseIntError } { + cause(cause) + display("The value is not a valid u64. Details: {}", cause) + from(cause: ParseIntError) -> { cause: cause } + } + } } -error_def! CfgGetRelativeTimeError { - NoSection => "The config does not contain a section with that name", - NoKey => "The config section does contain that key", - Parse { #[from] cause: util::strings::ParseQuantityWithUnitsError } - => "The value is not a valid relative time" ("Reason: {}", cause), +quick_error! { + #[derive(Debug)] + pub enum CfgGetFloatError { + NoSection { + display("The config does not contain a section with that name") + } + + NoKey { + display("The config section does contain that key") + } + + Parse { cause: ParseFloatError } { + display("The value is not a valid f32. Details: {}", cause) + cause(cause) + from(cause: ParseFloatError) -> { cause: cause } + } + } } -error_def! CfgGetFilenameError { - NoSection => "The config does not contain a section with that name", - NoKey => "The config section does contain that key", - ExpandDollar { #[from] cause: CfgExpandDollarError } - => "Failed to '$'-expand the config entry" ("Reason: {}", cause), +quick_error! { + #[derive(Debug)] + pub enum CfgGetRelativeTimeError { + NoSection { + display("The config does not contain a section with that name") + } + + NoKey { + display("The config section does contain that key") + } + + Parse { cause: util::strings::ParseQuantityWithUnitsError } { + cause(cause) + display("The value is not a valid relative time. Reason: {}", cause) + from(cause: util::strings::ParseQuantityWithUnitsError) -> { cause: cause } + } + } } +quick_error! { + #[derive(Debug)] + pub enum CfgGetFilenameError { + NoSection { + display("The config does not contain a section with that name") + } -error_def! CfgExpandDollarError { - NonUnicodeEnvVar { var_name: String } - => "Tried to expand to an environment variable containing invalid unicode" - ("variable: \"{}\"", var_name), - Syntax { pos: usize } - => "Syntax error in '$'-expansion" - ("Error at byte position {}", pos), - UnknownVariable { var_name: String } - => "Failed to expand variable" - ("Variable not found in PATHS section or process environment: {}", var_name), - UnclosedBraces - => "'$'-expansion includes an unclosed '{{'", + NoKey { + display("The config section does contain that key") + } + + ExpandDollar { cause: CfgExpandDollarError } { + display("Failed to '$'-expand the config entry. Reason: {}", cause) + cause(cause) + from(cause: CfgExpandDollarError) -> { cause: cause } + } + } +} + +quick_error! { + #[derive(Debug)] + pub enum CfgExpandDollarError { + NonUnicodeEnvVar { var_name: String } { + display("Tried to expand to an environment variable containing invalid unicode. variable: \"{}\"", var_name) + } + + Syntax { pos: usize } { + display("Syntax error in '$'-expansion. Error at byte position {}", pos) + } + + UnknownVariable { var_name: String } { + display("Failed to expand variable. Variable not found in PATHS section or process environment: {}", var_name) + } + + UnclosedBraces { + display("'$'-expansion includes an unclosed '{{'") + } + } } impl Cfg { @@ -113,13 +218,19 @@ impl Cfg { pub fn deserialize(read: R, allow_inline: bool) -> Result { use self::CfgDeserializeError::*; + lazy_static! { + static ref RE_KEY_VALUE: Regex = Regex::new(r"^(.+)=(.*)$").unwrap(); + static ref RE_SECTION: Regex = Regex::new(r"^\[(.+)\]$").unwrap(); + static ref RE_INLINE: Regex = Regex::new(r"^(?i)@inline@ (.+)$").unwrap(); + } + let mut cfg = Cfg::empty(); let mut section = String::new(); let br = BufReader::new(read); for (i, res_line) in br.lines().enumerate() { let line_num = i + 1; let line_buf = try!(res_line); - + { let line = line_buf.trim(); @@ -129,62 +240,61 @@ impl Cfg { } // ignore comments - if line.starts_with('#') || - line.starts_with('%') { + if line.starts_with('#') || line.starts_with('%') { continue; } - let re_inline = regex!(r"^(?i)@inline@ (.+)$"); - if let Some(caps) = re_inline.captures(line) { - let filename = caps.at(1).unwrap().trim(); // panic is logically impossible + if let Some(caps) = RE_INLINE.captures(line) { + let filename = caps.get(1).unwrap().as_str().trim(); // panic is logically impossible if allow_inline { let cfg_raw = match Cfg::load_raw(filename) { Ok(cfg_raw) => cfg_raw, - Err(e) => return Err(LoadInline { - cause: Box::new(e), - line_number: line_num, - filename: filename.to_string(), - }) + Err(e) => { + return Err(LoadInline { + cause: Box::new(e), + line_number: line_num, + filename: filename.to_string(), + }) + } }; cfg.merge(cfg_raw); - } - else { + } else { return Err(InlineDisabled { line_number: line_num, filename: filename.to_string(), - }) + }); } continue; } - let re_section = regex!(r"^\[(.+)\]$"); - if let Some(caps) = re_section.captures(line) { - section = caps.at(1).unwrap().to_string(); // panic is logically impossible + if let Some(caps) = RE_SECTION.captures(line) { + section = caps.get(1).unwrap().as_str().to_string(); // panic is logically impossible continue; } - let re_key_value = regex!(r"^(.+)=(.*)$"); - if let Some(caps) = re_key_value.captures(line) { - let key = caps.at(1).unwrap().trim(); - let value = caps.at(2).unwrap().trim(); + if let Some(caps) = RE_KEY_VALUE.captures(line) { + let key = caps.get(1).unwrap().as_str().trim(); + let value = caps.get(2).unwrap().as_str().trim(); /* * TODO: Make this less yukk. There's a whole bunch of unnecessary allocation * and copying happening here. */ match cfg.data.entry(section.clone()) { - hash_map::Entry::Occupied(mut soe) => match soe.get_mut().entry(key.to_string()) { - hash_map::Entry::Occupied(mut koe) => { - koe.insert(value.to_string()); - }, - hash_map::Entry::Vacant(kve) => { - kve.insert(value.to_string()); - }, - }, - hash_map::Entry::Vacant(sve) => { + hash_map::Entry::Occupied(mut soe) => { + match soe.get_mut().entry(key.to_string()) { + hash_map::Entry::Occupied(mut koe) => { + koe.insert(value.to_string()); + } + hash_map::Entry::Vacant(kve) => { + kve.insert(value.to_string()); + } + } + } + hash_map::Entry::Vacant(sve) => { let map = sve.insert(HashMap::new()); map.insert(key.to_string(), value.to_string()); - }, + } } continue; }; @@ -193,7 +303,7 @@ impl Cfg { return Err(Syntax { line_number: line_num, line: line_buf, - }) + }); } Ok(cfg) } @@ -201,15 +311,15 @@ impl Cfg { pub fn merge(&mut self, mut other: Cfg) { for (k, mut v) in other.data.drain() { match self.data.entry(k) { - hash_map::Entry::Occupied(oe) => { + hash_map::Entry::Occupied(oe) => { let map = oe.into_mut(); for (k, v) in v.drain() { map.insert(k, v); } - }, + } hash_map::Entry::Vacant(ve) => { ve.insert(v); - }, + } } } } @@ -218,21 +328,21 @@ impl Cfg { use self::CfgDefaultError::*; let mut data_dir = match paths::data_dir() { - Some(dd) => dd, - None => return Err(NoDataDir), + Some(dd) => dd, + None => return Err(NoDataDir), }; data_dir.push("config.d"); let mut cfg = Cfg::empty(); let rd = match std::fs::read_dir(data_dir) { - Ok(dirent) => dirent, - Err(e) => return Err(ReadDataDir { cause: e }), + Ok(dirent) => dirent, + Err(e) => return Err(ReadDataDir { cause: e }), }; for res_dirent in rd { let dirent = match res_dirent { - Ok(dirent) => dirent, - Err(e) => return Err(ReadDataDir { cause: e }), + Ok(dirent) => dirent, + Err(e) => return Err(ReadDataDir { cause: e }), }; let path = dirent.path(); if let Ok(file_type) = dirent.file_type() { @@ -241,7 +351,7 @@ impl Cfg { cfg.merge(cfg_raw); } } - }; + } Ok(cfg) } @@ -259,9 +369,9 @@ impl Cfg { match self.data.get(section) { Some(map) => match map.get(key) { Some(value) => Ok(try!(u64::from_str(value))), - None => Err(NoKey), + None => Err(NoKey), }, - None => Err(NoSection), + None => Err(NoSection), } } @@ -271,21 +381,25 @@ impl Cfg { match self.data.get(section) { Some(map) => match map.get(key) { Some(value) => Ok(try!(f32::from_str(value))), - None => Err(NoKey), + None => Err(NoKey), }, - None => Err(NoSection), + None => Err(NoSection), } } - pub fn get_relative_time(&self, section: &str, key: &str) -> Result { + pub fn get_relative_time( + &self, + section: &str, + key: &str, + ) -> Result { use self::CfgGetRelativeTimeError::*; match self.data.get(section) { Some(map) => match map.get(key) { Some(value) => Ok(try!(time::Relative::from_str(value))), - None => Err(NoKey), + None => Err(NoKey), }, - None => Err(NoSection), + None => Err(NoSection), } } @@ -297,21 +411,20 @@ impl Cfg { Some(value) => { let expanded = try!(self.expand_dollar(value)); Ok(PathBuf::from(expanded)) - }, - None => Err(NoKey), + } + None => Err(NoKey), }, - None => Err(NoSection), + None => Err(NoSection), } } - pub fn set_string<'a, S, K>(&mut self, section: S, key: K, mut value: String) -> Option - where S: IntoCow<'a, str>, - K: IntoCow<'a, str> - { - let section = section.into_cow(); - let key = key.into_cow(); - - if let Some(mut map) = self.data.get_mut(&*section) { + pub fn set_string<'a, S, K>( + &mut self, + section: Cow<'a, str>, + key: Cow<'a, str>, + mut value: String, + ) -> Option { + if let Some(map) = self.data.get_mut(&*section) { if let Some(mut val) = map.get_mut(&*key) { std::mem::swap(val, &mut value); return Some(value); @@ -334,13 +447,17 @@ impl Cfg { match self.data.get("PATHS").and_then(|m| m.get(name)) { Some(v) => Some(self.expand_dollar(v)), - None => match std::env::var(name) { - Ok(s) => Some(self.expand_dollar(s.borrow())), - Err(e) => match e { - VarError::NotPresent => return None, - VarError::NotUnicode(_) => return Some(Err(NonUnicodeEnvVar { var_name: name.to_string() })), - } - } + None => match std::env::var(name) { + Ok(s) => Some(self.expand_dollar(s.borrow())), + Err(e) => match e { + VarError::NotPresent => return None, + VarError::NotUnicode(_) => { + return Some(Err(NonUnicodeEnvVar { + var_name: name.to_string(), + })) + } + }, + }, } }; @@ -357,15 +474,14 @@ impl Cfg { }; loop { if let Some(&(end, c)) = chars.peek() { - if ! (c.is_alphanumeric() || c == '_') { + if !(c.is_alphanumeric() || c == '_') { let name = unsafe { orig.slice_unchecked(start, end) }; return (name, chars); } chars.next(); - } - else { + } else { let name = unsafe { orig.slice_unchecked(start, orig.len()) }; - return (name, chars) + return (name, chars); } } }; @@ -380,12 +496,14 @@ impl Cfg { } if let Some((pos, c)) = chars.next() { match c { - '}' => { - match lookup(name) { - Some(expanded) => ret.push_str(try!(expanded).borrow()), - None => return Err(UnknownVariable { var_name: name.to_string() }), + '}' => match lookup(name) { + Some(expanded) => ret.push_str(try!(expanded).borrow()), + None => { + return Err(UnknownVariable { + var_name: name.to_string(), + }) } - } + }, ':' => { if let Some((pos, c)) = chars.next() { if c != '-' { @@ -402,15 +520,13 @@ impl Cfg { if depth == 0 { end = e; break; - } - else { + } else { depth -= 1; } - }, - _ => (), + } + _ => (), } - } - else { + } else { return Err(UnclosedBraces); } } @@ -418,52 +534,51 @@ impl Cfg { // have "${name:-def}" and we were able to // resolve `name` to `expanded` ret.push_str(try!(expanded).borrow()); - } - else { + } else { // have "${name:-def}" and we were not able // to resolve name - let def = unsafe { orig.slice_unchecked(start, end) }; - ret.push_str(try!(self.expand_dollar(def)).borrow()); + let def = + unsafe { orig.slice_unchecked(start, end) }; + ret.push_str( + try!(self.expand_dollar(def)).borrow(), + ); } - } - else { + } else { // string ended after "${name:-" return Err(UnclosedBraces); } - } - else { + } else { // string ended after "${name:" return Err(UnclosedBraces); } - }, - _ => { + } + _ => { // got string "${name_" where _ is an invalid character return Err(Syntax { pos: pos }); - }, + } } - } - else { + } else { return Err(UnclosedBraces); } - } - else { + } else { return Err(UnclosedBraces); } - } - else { + } else { let (name, nchars) = get_name(chars); chars = nchars; match lookup(name) { - Some(expanded) => ret.push_str(try!(expanded).borrow()), - None => return Err(UnknownVariable { var_name: name.to_string() }), + Some(expanded) => ret.push_str(try!(expanded).borrow()), + None => { + return Err(UnknownVariable { + var_name: name.to_string(), + }) + } } } - } - else { + } else { return Err(Syntax { pos: orig.len() }); } - } - else { + } else { ret.push(c); } } @@ -473,8 +588,8 @@ impl Cfg { #[cfg(test)] mod tests { - use std; use super::*; + use std; #[test] fn test_expand_dollar() { @@ -489,4 +604,3 @@ mod tests { assert_eq!(expanded, "foo in_paths in_env in_env_wub_blah"); } } - diff --git a/src/crypto/ecdsa.rs b/src/crypto/ecdsa.rs index 2be574f..d210cc9 100644 --- a/src/crypto/ecdsa.rs +++ b/src/crypto/ecdsa.rs @@ -1,133 +1,146 @@ -use std::str::FromStr; -use std::mem; +use libc::{c_char, c_void, size_t}; use std::fmt::{self, Debug, Formatter}; -use std::mem::{uninitialized, size_of, size_of_val}; -use std::str::from_utf8; -use std::slice::from_raw_parts; use std::io::{self, Read, Write}; -use libc::{c_void, size_t, c_char}; +use std::mem; +use std::mem::{size_of, size_of_val, uninitialized}; +use std::slice::from_raw_parts; +use std::str::from_utf8; +use std::str::FromStr; -use ll; use crypto::hashcode::HashCode; +use ll; /// A 256bit ECDSA public key. #[derive(Copy, Clone)] pub struct EcdsaPublicKey { - data: ll::Struct_GNUNET_CRYPTO_EcdsaPublicKey, + data: ll::Struct_GNUNET_CRYPTO_EcdsaPublicKey, } impl EcdsaPublicKey { - /// Serialize key to a byte stream. - pub fn serialize(&self, w: &mut T) -> Result<(), io::Error> where T: Write { - w.write_all(&self.data.q_y) - } + /// Serialize key to a byte stream. + pub fn serialize(&self, w: &mut T) -> Result<(), io::Error> + where + T: Write, + { + w.write_all(&self.data.q_y) + } - /// Compute the hash of this key. - pub fn hash(&self) -> HashCode { - unsafe { - HashCode::from_buffer(from_raw_parts( - &self.data as *const ll::Struct_GNUNET_CRYPTO_EcdsaPublicKey as *const u8, - size_of::() - )) + /// Compute the hash of this key. + pub fn hash(&self) -> HashCode { + unsafe { + HashCode::from_buffer(from_raw_parts( + &self.data as *const ll::Struct_GNUNET_CRYPTO_EcdsaPublicKey as *const u8, + size_of::(), + )) + } } - } } /// Error generated when attempting to parse an ecdsa public key -error_def! EcdsaPublicKeyFromStrError { - ParsingFailed => "Failed to parse the string as an ecdsa public key", +quick_error! { + #[derive(Debug)] + pub enum EcdsaPublicKeyFromStrError { + ParsingFailed { + display("Failed to parse the string as an ecdsa public key") + } + } } impl FromStr for EcdsaPublicKey { - type Err = EcdsaPublicKeyFromStrError; - - fn from_str(s: &str) -> Result { - let bytes = s.as_bytes(); - unsafe { - let mut ret: EcdsaPublicKey = mem::uninitialized(); - let res = ll::GNUNET_CRYPTO_ecdsa_public_key_from_string( - bytes.as_ptr() as *const i8, - bytes.len() as usize, - &mut ret.data); - match res { - ll::GNUNET_OK => Ok(ret), - _ => Err(EcdsaPublicKeyFromStrError::ParsingFailed), - } + type Err = EcdsaPublicKeyFromStrError; + + fn from_str(s: &str) -> Result { + let bytes = s.as_bytes(); + unsafe { + let mut ret: EcdsaPublicKey = mem::uninitialized(); + let res = ll::GNUNET_CRYPTO_ecdsa_public_key_from_string( + bytes.as_ptr() as *const i8, + bytes.len() as usize, + &mut ret.data, + ); + match res { + ll::GNUNET_OK => Ok(ret), + _ => Err(EcdsaPublicKeyFromStrError::ParsingFailed), + } + } } - } } impl Debug for EcdsaPublicKey { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - unsafe { - const LEN: usize = 52usize; - assert!(LEN == (size_of_val(&self.data.q_y) * 8 + 4) / 5); - let mut enc: [u8; LEN] = uninitialized(); - let res = ll::GNUNET_STRINGS_data_to_string(self.data.q_y.as_ptr() as *const c_void, - self.data.q_y.len() as size_t, - enc.as_mut_ptr() as *mut c_char, - enc.len() as size_t); - assert!(!res.is_null()); - fmt::Display::fmt(from_utf8(&enc).unwrap(), f) + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + unsafe { + const LEN: usize = 52usize; + assert!(LEN == (size_of_val(&self.data.q_y) * 8 + 4) / 5); + let mut enc: [u8; LEN] = uninitialized(); + let res = ll::GNUNET_STRINGS_data_to_string( + self.data.q_y.as_ptr() as *const c_void, + self.data.q_y.len() as size_t, + enc.as_mut_ptr() as *mut c_char, + enc.len() as size_t, + ); + assert!(!res.is_null()); + fmt::Display::fmt(from_utf8(&enc).unwrap(), f) + } } - } } impl fmt::Display for EcdsaPublicKey { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - Debug::fmt(self, f) - } + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + Debug::fmt(self, f) + } } /// A 256bit ECDSA private key. #[derive(Copy)] pub struct EcdsaPrivateKey { - data: ll::Struct_GNUNET_CRYPTO_EcdsaPrivateKey, + data: ll::Struct_GNUNET_CRYPTO_EcdsaPrivateKey, } impl EcdsaPrivateKey { - /// Serialize this key to a byte stream. - pub fn serialize(&self, w: &mut T) -> Result<(), io::Error> where T: Write { - w.write_all(&self.data.d) - } + /// Serialize this key to a byte stream. + pub fn serialize(&self, w: &mut T) -> Result<(), io::Error> + where + T: Write, + { + w.write_all(&self.data.d) + } - /// Deserialize a from a byte stream. - pub fn deserialize(r: &mut T) -> Result where T: Read { - let mut ret: EcdsaPrivateKey = unsafe { uninitialized() }; - try!(r.read_exact(&mut ret.data.d[..])); - Ok(ret) - } + /// Deserialize a from a byte stream. + pub fn deserialize(r: &mut T) -> Result + where + T: Read, + { + let mut ret: EcdsaPrivateKey = unsafe { uninitialized() }; + try!(r.read_exact(&mut ret.data.d[..])); + Ok(ret) + } - /// Get the corresponding public key to this private key. - pub fn get_public(&self) -> EcdsaPublicKey { - unsafe { - let mut ret: ll::Struct_GNUNET_CRYPTO_EcdsaPublicKey = uninitialized(); - ll::GNUNET_CRYPTO_ecdsa_key_get_public(&self.data, &mut ret); - EcdsaPublicKey { - data: ret, - } + /// Get the corresponding public key to this private key. + pub fn get_public(&self) -> EcdsaPublicKey { + unsafe { + let mut ret: ll::Struct_GNUNET_CRYPTO_EcdsaPublicKey = uninitialized(); + ll::GNUNET_CRYPTO_ecdsa_key_get_public(&self.data, &mut ret); + EcdsaPublicKey { data: ret } + } } - } - /// Return the private key of the global, anonymous user. - pub fn anonymous() -> EcdsaPrivateKey { - //let anon = ll::GNUNET_CRYPTO_ecdsa_key_get_anonymous(); - unsafe { - EcdsaPrivateKey { - data: *ll::GNUNET_CRYPTO_ecdsa_key_get_anonymous(), - } + /// Return the private key of the global, anonymous user. + pub fn anonymous() -> EcdsaPrivateKey { + //let anon = ll::GNUNET_CRYPTO_ecdsa_key_get_anonymous(); + unsafe { + EcdsaPrivateKey { + data: *ll::GNUNET_CRYPTO_ecdsa_key_get_anonymous(), + } + } } - } } impl Clone for EcdsaPrivateKey { - fn clone(&self) -> EcdsaPrivateKey { - EcdsaPrivateKey { - data: ll::Struct_GNUNET_CRYPTO_EcdsaPrivateKey { - d: self.data.d, - }, + fn clone(&self) -> EcdsaPrivateKey { + EcdsaPrivateKey { + data: ll::Struct_GNUNET_CRYPTO_EcdsaPrivateKey { d: self.data.d }, + } } - } } /* @@ -151,14 +164,13 @@ impl FromStr for EcdsaPrivateKey { #[test] fn test_ecdsa_to_from_string() { - use EcdsaPublicKey; - - //let s0: &str = "JK55QA8JLAL64MBO8UM209KE93M9JBBO7M2UB8M3M03FKRFSUOMG"; - let s0: &str = "JK55QA8J1A164MB08VM209KE93M9JBB07M2VB8M3M03FKRFSV0MG"; - let key: EcdsaPublicKey = FromStr::from_str(s0).unwrap(); - let s1: String = format!("{}", key); - println!("{} {}", s0, s0.len()); - println!("{} {}", s1, s1.len()); - assert!(s0 == &s1[..]); + use EcdsaPublicKey; + + //let s0: &str = "JK55QA8JLAL64MBO8UM209KE93M9JBBO7M2UB8M3M03FKRFSUOMG"; + let s0: &str = "JK55QA8J1A164MB08VM209KE93M9JBB07M2VB8M3M03FKRFSV0MG"; + let key: EcdsaPublicKey = FromStr::from_str(s0).unwrap(); + let s1: String = format!("{}", key); + println!("{} {}", s0, s0.len()); + println!("{} {}", s1, s1.len()); + assert!(s0 == &s1[..]); } - diff --git a/src/crypto/hashcode.rs b/src/crypto/hashcode.rs index ba052f1..2511837 100644 --- a/src/crypto/hashcode.rs +++ b/src/crypto/hashcode.rs @@ -1,219 +1,218 @@ +use rcrypto::digest::Digest; +use rcrypto::sha2::Sha512; use std::cmp::Ordering; -use std::num::Wrapping; use std::fmt; -use std::slice; -use std::mem; use std::hash; +use std::mem; +use std::num::Wrapping; +use std::ops::{Add, BitXor, Sub}; +use std::slice; use std::str::FromStr; -use std::ops::{Add, Sub, BitXor}; -use rcrypto::sha2::Sha512; -use rcrypto::digest::Digest; -use rand::{Rand, Rng}; use data; /// A 512-bit hashcode used in various places throughout GNUnet. #[derive(PartialEq, Eq, Clone, PartialOrd, Ord)] pub struct HashCode { - data: [u32; 16], + data: [u32; 16], } impl HashCode { - /// Get the data underlying buffer as a buffer - pub fn as_slice(&self) -> &[u8] { - unsafe { - slice::from_raw_parts(self.data.as_ptr() as *const u8, 64) - } - } - - /// Get the data underlying buffer as a mutable buffer - pub fn as_mut_slice(&mut self) -> &mut [u8] { - unsafe { - slice::from_raw_parts_mut(self.data.as_mut_ptr() as *mut u8, 64) - } - } - - /// Create a HashCode by computing the sha512 hash of a buffer. - pub fn from_buffer(buf: &[u8]) -> HashCode { - let mut ret = HashCode { - data: unsafe { mem::uninitialized() }, - }; - let mut hasher = Sha512::new(); - hasher.input(buf); - hasher.result(ret.as_mut_slice()); - ret - } - - /// Compute the distance between two hashes. - pub fn distance(&self, other: &HashCode) -> u32 { - let a1 = Wrapping(self.data[1]); - let b1 = Wrapping(other.data[1]); - let x1 = (a1 - b1) >> 16; - let x2 = (b1 - a1) >> 16; - (x1 * x2).0 - } - - /// Get the nth bit of a HashCode. - /// - /// # Panics - /// - /// Panics if `idx >= 512`. - pub fn get_bit(&self, idx: u32) -> bool { - assert!(idx < 512); - let idx = idx as usize; - (self.as_slice()[idx >> 3] & (1 << (idx & 7))) > 0 - } - - /// Compute the length (in bits) of the common prefix of two hashes. ie. two identical hashes - /// will return a value of 512u32 while two hashes that vary in the first bit will return a value - /// of 0u32. - pub fn matching_prefix_len(&self, other: &HashCode) -> u32 { - for i in 0..512 { - if self.get_bit(i) != other.get_bit(i) { - return i; - }; - }; - 512 - } - - /// Determine which hash is closer to `self` in the XOR metric (Kademlia). Returns `Less` if - /// `h1` is smaller than `h2` relative to `self`. ie. if `(h1 ^ self) < (h2 ^ self)`. Otherwise - /// returns `Greater` or `Equal` if `h1` is greater than or equal to `h2` relative to `self`. - pub fn xor_cmp(&self, h0: &HashCode, h1: &HashCode) -> Ordering { - use std::cmp::Ordering::*; - - let mut i = 16; - while i > 0 { - i -= 1; - let s = self.data[i]; - let x0 = h0.data[i]; - let x1 = h1.data[i]; - let d0 = x0 ^ s; - let d1 = x1 ^ s; - match d0.cmp(&d1) { - Less => return Less, - Greater => return Greater, - _ => (), - } - } - Equal - } + /// Get the data underlying buffer as a buffer + pub fn as_slice(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.data.as_ptr() as *const u8, 64) } + } + + /// Get the data underlying buffer as a mutable buffer + pub fn as_mut_slice(&mut self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut(self.data.as_mut_ptr() as *mut u8, 64) } + } + + /// Create a HashCode by computing the sha512 hash of a buffer. + pub fn from_buffer(buf: &[u8]) -> HashCode { + let mut ret = HashCode { + data: unsafe { mem::uninitialized() }, + }; + let mut hasher = Sha512::new(); + hasher.input(buf); + hasher.result(ret.as_mut_slice()); + ret + } + + /// Compute the distance between two hashes. + pub fn distance(&self, other: &HashCode) -> u32 { + let a1 = Wrapping(self.data[1]); + let b1 = Wrapping(other.data[1]); + let x1 = (a1 - b1) >> 16; + let x2 = (b1 - a1) >> 16; + (x1 * x2).0 + } + + /// Get the nth bit of a HashCode. + /// + /// # Panics + /// + /// Panics if `idx >= 512`. + pub fn get_bit(&self, idx: u32) -> bool { + assert!(idx < 512); + let idx = idx as usize; + (self.as_slice()[idx >> 3] & (1 << (idx & 7))) > 0 + } + + /// Compute the length (in bits) of the common prefix of two hashes. ie. two identical hashes + /// will return a value of 512u32 while two hashes that vary in the first bit will return a value + /// of 0u32. + pub fn matching_prefix_len(&self, other: &HashCode) -> u32 { + for i in 0..512 { + if self.get_bit(i) != other.get_bit(i) { + return i; + }; + } + 512 + } + + /// Determine which hash is closer to `self` in the XOR metric (Kademlia). Returns `Less` if + /// `h1` is smaller than `h2` relative to `self`. ie. if `(h1 ^ self) < (h2 ^ self)`. Otherwise + /// returns `Greater` or `Equal` if `h1` is greater than or equal to `h2` relative to `self`. + pub fn xor_cmp(&self, h0: &HashCode, h1: &HashCode) -> Ordering { + use std::cmp::Ordering::*; + + let mut i = 16; + while i > 0 { + i -= 1; + let s = self.data[i]; + let x0 = h0.data[i]; + let x1 = h1.data[i]; + let d0 = x0 ^ s; + let d1 = x1 ^ s; + match d0.cmp(&d1) { + Less => return Less, + Greater => return Greater, + _ => (), + } + } + Equal + } } impl fmt::Display for HashCode { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - data::crockford_encode_fmt(f, self.as_slice()) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + data::crockford_encode_fmt(f, self.as_slice()) + } } impl fmt::Debug for HashCode { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(self, f) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, f) + } } impl FromStr for HashCode { - type Err = data::CrockfordDecodeError; - - fn from_str(s: &str) -> Result { - let mut ret = HashCode { - data: unsafe { mem::uninitialized() }, - }; - try!(data::crockford_decode(s, ret.as_mut_slice())); - Ok(ret) - } + type Err = data::CrockfordDecodeError; + + fn from_str(s: &str) -> Result { + let mut ret = HashCode { + data: unsafe { mem::uninitialized() }, + }; + try!(data::crockford_decode(s, ret.as_mut_slice())); + Ok(ret) + } } -impl Rand for HashCode { - fn rand(rng: &mut R) -> HashCode - where R: Rng - { - let mut ret = HashCode { - data: unsafe { mem::uninitialized() }, - }; - for u in ret.data.iter_mut() { - *u = rng.next_u32(); - } - ret - } +/* +impl rand::Rand for HashCode { + fn rand(rng: &mut R) -> HashCode + where + R: rand::Rng, + { + let mut ret = HashCode { + data: unsafe { mem::uninitialized() }, + }; + for u in ret.data.iter_mut() { + *u = rng.next_u32(); + } + ret + } } +*/ impl Add for HashCode { - type Output = HashCode; - - fn add(self, rhs: HashCode) -> HashCode { - let mut ret = HashCode { - data: unsafe { mem::uninitialized() }, - }; - for i in 0..ret.data.len() { - ret.data[i] = (Wrapping(self.data[i]) + Wrapping(rhs.data[i])).0; + type Output = HashCode; + + fn add(self, rhs: HashCode) -> HashCode { + let mut ret = HashCode { + data: unsafe { mem::uninitialized() }, + }; + for i in 0..ret.data.len() { + ret.data[i] = (Wrapping(self.data[i]) + Wrapping(rhs.data[i])).0; + } + ret } - ret - } } impl Sub for HashCode { - type Output = HashCode; - - fn sub(self, rhs: HashCode) -> HashCode { - let mut ret = HashCode { - data: unsafe { mem::uninitialized() }, - }; - for i in 0..ret.data.len() { - ret.data[i] = (Wrapping(self.data[i]) - Wrapping(rhs.data[i])).0; + type Output = HashCode; + + fn sub(self, rhs: HashCode) -> HashCode { + let mut ret = HashCode { + data: unsafe { mem::uninitialized() }, + }; + for i in 0..ret.data.len() { + ret.data[i] = (Wrapping(self.data[i]) - Wrapping(rhs.data[i])).0; + } + ret } - ret - } } impl BitXor for HashCode { - type Output = HashCode; - - fn bitxor(self, rhs: HashCode) -> HashCode { - let mut ret = HashCode { - data: unsafe { mem::uninitialized() }, - }; - for i in 0..ret.data.len() { - ret.data[i] = self.data[i] ^ rhs.data[i]; + type Output = HashCode; + + fn bitxor(self, rhs: HashCode) -> HashCode { + let mut ret = HashCode { + data: unsafe { mem::uninitialized() }, + }; + for i in 0..ret.data.len() { + ret.data[i] = self.data[i] ^ rhs.data[i]; + } + ret } - ret - } } impl hash::Hash for HashCode { - fn hash(&self, state: &mut H) - where H: hash::Hasher - { - self.data.hash(state) - } - - fn hash_slice(data: &[HashCode], state: &mut H) - where H: hash::Hasher - { - for h in data.iter() { - h.hash(state); - } - } + fn hash(&self, state: &mut H) + where + H: hash::Hasher, + { + self.data.hash(state) + } + + fn hash_slice(data: &[HashCode], state: &mut H) + where + H: hash::Hasher, + { + for h in data.iter() { + h.hash(state); + } + } } #[test] fn test_hashcode_to_from_string() { - let s0: &str = "RMKN0V1JNA3PVC1148D6J10STVG94A8A651N0K849CF1RT6BGF26AMMT14GMDMNRDFSJRJME61KJ31DFBV12R1TPQJE64155132QN5G"; - let hc: HashCode = FromStr::from_str(s0).unwrap(); - let s: String = format!("{}", hc); - let s1: &str = &s[..]; - assert!(s0 == s1, "s0 == {}, s1 == {}", s0, s1); + let s0: &str = "RMKN0V1JNA3PVC1148D6J10STVG94A8A651N0K849CF1RT6BGF26AMMT14GMDMNRDFSJRJME61KJ31DFBV12R1TPQJE64155132QN5G"; + let hc: HashCode = FromStr::from_str(s0).unwrap(); + let s: String = format!("{}", hc); + let s1: &str = &s[..]; + assert!(s0 == s1, "s0 == {}, s1 == {}", s0, s1); } #[test] fn test_hashcode_rand_add_sub() { - use rand::weak_rng; - - let mut rng = weak_rng(); - let h0: HashCode = rng.gen(); - let h1: HashCode = rng.gen(); - let diff = h1.clone() - h0.clone(); - let sum = h0.clone() + diff; - assert!(sum == h1); + use rand::weak_rng; + + let mut rng = weak_rng(); + let h0: HashCode = rng.gen(); + let h1: HashCode = rng.gen(); + let diff = h1.clone() - h0.clone(); + let sum = h0.clone() + diff; + assert!(sum == h1); } - diff --git a/src/data.rs b/src/data.rs index 30bc1ae..80d926c 100644 --- a/src/data.rs +++ b/src/data.rs @@ -1,155 +1,162 @@ use std::fmt::{self, Write}; -static ENCODE_CHARS: [char; 32] = ['0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', - 'G', 'H', 'J', 'K', 'M', 'N', 'P', 'Q', - 'R', 'S', 'T', 'V', 'W', 'X', 'Y', 'Z']; +static ENCODE_CHARS: [char; 32] = [ + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', + 'K', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'X', 'Y', 'Z', +]; /// Used to wrap a byte slice which can then be crockford base32 encoded using `std::fmt::Display`. pub struct CrockfordEncode<'a>(pub &'a [u8]); impl<'a> fmt::Display for CrockfordEncode<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - &CrockfordEncode(buf) => crockford_encode_fmt(f, buf) + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + &CrockfordEncode(buf) => crockford_encode_fmt(f, buf), + } } - } } /// Encodes a byte slice to printable ascii using crockford base32 encoding and returns the result as a /// `String`. pub fn crockford_encode(buf: &[u8]) -> String { - let enc_len = (buf.len() * 8 + 4) / 5; - let mut ret = String::with_capacity(enc_len); - write!(ret, "{}", CrockfordEncode(buf)).unwrap(); - ret + let enc_len = (buf.len() * 8 + 4) / 5; + let mut ret = String::with_capacity(enc_len); + write!(ret, "{}", CrockfordEncode(buf)).unwrap(); + ret } /// Encodes a byte slice to printable ascii using crockford base32 encoding and writes the encoded /// data to a `std::fmt::Formatter`. pub fn crockford_encode_fmt(f: &mut fmt::Formatter, buf: &[u8]) -> fmt::Result { - let mut shift: i32 = 3; - let mut next_char: u8 = 0; - for b in buf.iter() { - while shift >= 0 { - next_char |= (*b >> shift) & 0x1f; - let c = ENCODE_CHARS[next_char as usize]; - try!(fmt::Display::fmt(&c, f)); - next_char = 0; - shift -= 5; - }; - next_char |= (*b << (-shift)) & 0x1f; - shift += 8; - } - if shift > 3 { - let c = ENCODE_CHARS[next_char as usize]; - try!(fmt::Display::fmt(&c, f)); - } - - Ok(()) + let mut shift: i32 = 3; + let mut next_char: u8 = 0; + for b in buf.iter() { + while shift >= 0 { + next_char |= (*b >> shift) & 0x1f; + let c = ENCODE_CHARS[next_char as usize]; + try!(fmt::Display::fmt(&c, f)); + next_char = 0; + shift -= 5; + } + next_char |= (*b << (-shift)) & 0x1f; + shift += 8; + } + if shift > 3 { + let c = ENCODE_CHARS[next_char as usize]; + try!(fmt::Display::fmt(&c, f)); + } + + Ok(()) } -/// Errors that occur trying to decode Crockford base32 encoded data. -error_def! CrockfordDecodeError { - SizeMismatch { - encoded_size: usize, - target_size: usize, - } => "The size of the encoded data did not match the size of the target buffer" - ("There are {} chars of encoded data but the target buffer is {} bytes long.", encoded_size, target_size), - InvalidChar { ch: char } - => "There was an invalid character in the encoded data" ("'{}' is not a valid Crockford base32 encoded character. See http://www.crockford.com/wrmg/base32.htm for more info.", ch), - TrailingBits - => "There were trailing 1 bits in the encoded data past the logical end of the data", +quick_error! { + /// Errors that occur trying to decode Crockford base32 encoded data. + #[derive(Debug)] + pub enum CrockfordDecodeError { + SizeMismatch { + encoded_size: usize, + target_size: usize, + } { + display("The size of the encoded data did not match the size of the target buffer. There are {} chars of encoded data but the target buffer is {} bytes long.", encoded_size, target_size) + } + + InvalidChar { ch: char } { + display("There was an invalid character in the encoded data. '{}' is not a valid Crockford base32 encoded character. See http://www.crockford.com/wrmg/base32.htm for more info.", ch) + } + + TrailingBits { + display("There were trailing 1 bits in the encoded data past the logical end of the data") + } + } } /// Decodes crockford base32 encoded data and writes the result to a mutable byte slice. pub fn crockford_decode(enc: &str, dec: &mut [u8]) -> Result<(), CrockfordDecodeError> { - let enc_len = enc.len(); - let dec_len = dec.len(); - - if (enc_len * 5) / 8 != dec_len { - return Err(CrockfordDecodeError::SizeMismatch { - encoded_size: enc_len, - target_size: dec_len, - }); - }; - - for b in dec.iter_mut() { - *b = 0u8; - } - - let mut shift: i32 = 3; - let mut dp: usize = 0; - for c in enc.chars() { - let d = match c { - '0' | 'O' | 'o' => 0, - '1' | 'I' | 'i' | 'L' | 'l' => 1, - '2' => 2, - '3' => 3, - '4' => 4, - '5' => 5, - '6' => 6, - '7' => 7, - '8' => 8, - '9' => 9, - 'a' | 'A' => 10, - 'b' | 'B' => 11, - 'c' | 'C' => 12, - 'd' | 'D' => 13, - 'e' | 'E' => 14, - 'f' | 'F' => 15, - 'g' | 'G' => 16, - 'h' | 'H' => 17, - 'j' | 'J' => 18, - 'k' | 'K' => 19, - 'm' | 'M' => 20, - 'n' | 'N' => 21, - 'p' | 'P' => 22, - 'q' | 'Q' => 23, - 'r' | 'R' => 24, - 's' | 'S' => 25, - 't' | 'T' => 26, - 'u' | 'U' | 'v' | 'V' => 27, - 'w' | 'W' => 28, - 'x' | 'X' => 29, - 'y' | 'Y' => 30, - 'z' | 'Z' => 31, - c => return Err(CrockfordDecodeError::InvalidChar { ch: c }), - }; - if shift < 0 { - dec[dp] |= d >> (-shift); - dp += 1; - shift += 8; - if dp == dec_len { - return match d << shift { - 0u8 => Ok(()), - _ => Err(CrockfordDecodeError::TrailingBits), - } - } + let enc_len = enc.len(); + let dec_len = dec.len(); + + if (enc_len * 5) / 8 != dec_len { + return Err(CrockfordDecodeError::SizeMismatch { + encoded_size: enc_len, + target_size: dec_len, + }); }; - dec[dp] |= d << shift; - shift -= 5; - } - Ok(()) + + for b in dec.iter_mut() { + *b = 0u8; + } + + let mut shift: i32 = 3; + let mut dp: usize = 0; + for c in enc.chars() { + let d = match c { + '0' | 'O' | 'o' => 0, + '1' | 'I' | 'i' | 'L' | 'l' => 1, + '2' => 2, + '3' => 3, + '4' => 4, + '5' => 5, + '6' => 6, + '7' => 7, + '8' => 8, + '9' => 9, + 'a' | 'A' => 10, + 'b' | 'B' => 11, + 'c' | 'C' => 12, + 'd' | 'D' => 13, + 'e' | 'E' => 14, + 'f' | 'F' => 15, + 'g' | 'G' => 16, + 'h' | 'H' => 17, + 'j' | 'J' => 18, + 'k' | 'K' => 19, + 'm' | 'M' => 20, + 'n' | 'N' => 21, + 'p' | 'P' => 22, + 'q' | 'Q' => 23, + 'r' | 'R' => 24, + 's' | 'S' => 25, + 't' | 'T' => 26, + 'u' | 'U' | 'v' | 'V' => 27, + 'w' | 'W' => 28, + 'x' | 'X' => 29, + 'y' | 'Y' => 30, + 'z' | 'Z' => 31, + c => return Err(CrockfordDecodeError::InvalidChar { ch: c }), + }; + if shift < 0 { + dec[dp] |= d >> (-shift); + dp += 1; + shift += 8; + if dp == dec_len { + return match d << shift { + 0u8 => Ok(()), + _ => Err(CrockfordDecodeError::TrailingBits), + }; + } + }; + dec[dp] |= d << shift; + shift -= 5; + } + Ok(()) } #[cfg(test)] mod tests { - use ::data::*; - - fn decode_encode(s0: &str, buf: &mut [u8]) { - println!("decoding: {}", s0); - crockford_decode(s0, buf).unwrap(); - let s1 = crockford_encode(&buf[..]); - assert!(s0 == &s1[..], "s0 == \"{}\", s1 == \"{}\"", s0, s1); - } - - #[test] - fn tests() { - let mut buf = [0u8; 6]; - decode_encode("ABCDEFG", &mut buf[..4]); - decode_encode("ABCDEFGH", &mut buf[..5]); - decode_encode("ABCDEFGHJ4", &mut buf[..6]); - } -} + use data::*; + + fn decode_encode(s0: &str, buf: &mut [u8]) { + println!("decoding: {}", s0); + crockford_decode(s0, buf).unwrap(); + let s1 = crockford_encode(&buf[..]); + assert!(s0 == &s1[..], "s0 == \"{}\", s1 == \"{}\"", s0, s1); + } + #[test] + fn tests() { + let mut buf = [0u8; 6]; + decode_encode("ABCDEFG", &mut buf[..4]); + decode_encode("ABCDEFGH", &mut buf[..5]); + decode_encode("ABCDEFGHJ4", &mut buf[..6]); + } +} diff --git a/src/gns/mod.rs b/src/gns/mod.rs index 058d005..f08f8a4 100644 --- a/src/gns/mod.rs +++ b/src/gns/mod.rs @@ -1,184 +1,207 @@ -use std::collections::HashMap; -use std::marker::PhantomData; -use std::sync::mpsc::{channel, Sender, Receiver, TryRecvError}; -use std::io::{self, Write, Cursor}; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use num::ToPrimitive; +use std::collections::HashMap; +use std::io::{self, Cursor, Write}; +use std::marker::PhantomData; +use std::sync::mpsc::{channel, Receiver, Sender, TryRecvError}; +pub use self::record::*; use identity; use ll; -use service::{self, ServiceReadLoop, ServiceWriter, ProcessMessageResult}; -use EcdsaPublicKey; -use EcdsaPrivateKey; +use service::{self, ProcessMessageResult, ServiceReadLoop, ServiceWriter}; use Cfg; -pub use self::record::*; +use EcdsaPrivateKey; +use EcdsaPublicKey; mod record; /// A handle to a locally-running instance of the GNS daemon. pub struct GNS { - service_writer: ServiceWriter, - _callback_loop: ServiceReadLoop, - lookup_id: u32, - lookup_tx: Sender<(u32, Sender)>, + service_writer: ServiceWriter, + _callback_loop: ServiceReadLoop, + lookup_id: u32, + lookup_tx: Sender<(u32, Sender)>, } /// Options for GNS lookups. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum LocalOptions { - /// Default behaviour. Look in the local cache, then in the DHT. - Default = 0, - /// Do not look in the DHT, keep the request to the local cache. - NoDHT = 1, - /// For domains controlled by our master zone only look in the cache. Otherwise look in the - /// cache, then in the DHT. - LocalMaster = 2, + /// Default behaviour. Look in the local cache, then in the DHT. + Default = 0, + /// Do not look in the DHT, keep the request to the local cache. + NoDHT = 1, + /// For domains controlled by our master zone only look in the cache. Otherwise look in the + /// cache, then in the DHT. + LocalMaster = 2, } -/// Possible errors returned by the GNS lookup functions. -error_def! LookupError { - NameTooLong { name: String } - => "The domain name was too long" ("The domain name \"{}\" is too long to lookup.", name), - Io { #[from] cause: io::Error } - => "There was an I/O error communicating with the service" ("Specifically {}", cause), +quick_error! { + /// Possible errors returned by the GNS lookup functions. + #[derive(Debug)] + pub enum LookupError { + NameTooLong { name: String } { + display("The domain name was too long. The domain name \"{}\" is too long to lookup.", name) + } + + Io { cause: io::Error } { + cause(cause) + from(cause: io::Error) -> { cause: cause } + display("There was an I/O error communicating with the service. Specifically {}", cause) + } + } } impl GNS { - /// Connect to the GNS service. - /// - /// Returns either a handle to the GNS service or a `service::ConnectError`. `cfg` contains the - /// configuration to use to connect to the service. - pub fn connect(cfg: &Cfg) -> Result { - let (lookup_tx, lookup_rx) = channel::<(u32, Sender)>(); - let mut handles: HashMap> = HashMap::new(); + /// Connect to the GNS service. + /// + /// Returns either a handle to the GNS service or a `service::ConnectError`. `cfg` contains the + /// configuration to use to connect to the service. + pub fn connect(cfg: &Cfg) -> Result { + let (lookup_tx, lookup_rx) = channel::<(u32, Sender)>(); + let mut handles: HashMap> = HashMap::new(); - let (service_reader, service_writer) = try!(service::connect(cfg, "gns")); - let callback_loop = try!(service_reader.spawn_callback_loop(move |tpe: u16, mut reader: Cursor>| -> ProcessMessageResult { - println!("GNS got message!"); - loop { - match lookup_rx.try_recv() { - Ok((id, sender)) => { - handles.insert(id, sender); - }, - Err(e) => match e { - TryRecvError::Empty => break, - TryRecvError::Disconnected => return ProcessMessageResult::Shutdown, - }, - } - } + let (service_reader, service_writer) = try!(service::connect(cfg, "gns")); + let callback_loop = try!(service_reader.spawn_callback_loop( + move |tpe: u16, mut reader: Cursor>| -> ProcessMessageResult { + println!("GNS got message!"); + loop { + match lookup_rx.try_recv() { + Ok((id, sender)) => { + handles.insert(id, sender); + } + Err(e) => match e { + TryRecvError::Empty => break, + TryRecvError::Disconnected => return ProcessMessageResult::Shutdown, + }, + } + } - println!("tpe == {}", tpe); + println!("tpe == {}", tpe); - // TODO: drop expired senders, this currently leaks memory as `handles` only gets bigger - // need a way to detect when the remote Receiver has hung up - match tpe { - ll::GNUNET_MESSAGE_TYPE_GNS_LOOKUP_RESULT => { - let id = match reader.read_u32::() { - Ok(id) => id, - Err(_) => return ProcessMessageResult::Reconnect, - }; - println!("WOW id == {}", id); - match handles.get(&id) { - Some(sender) => { - println!("WOW there's a sender for that"); - let rd_count = match reader.read_u32::() { - Ok(x) => x, - Err(_) => return ProcessMessageResult::Reconnect, - }; - println!("WOW rd_count == {}", rd_count); - for _ in 0..rd_count { - let rec = match Record::deserialize(&mut reader) { - Ok(r) => r, - Err(_) => return ProcessMessageResult::Reconnect, + // TODO: drop expired senders, this currently leaks memory as `handles` only gets bigger + // need a way to detect when the remote Receiver has hung up + match tpe { + ll::GNUNET_MESSAGE_TYPE_GNS_LOOKUP_RESULT => { + let id = match reader.read_u32::() { + Ok(id) => id, + Err(_) => return ProcessMessageResult::Reconnect, + }; + println!("WOW id == {}", id); + match handles.get(&id) { + Some(sender) => { + println!("WOW there's a sender for that"); + let rd_count = match reader.read_u32::() { + Ok(x) => x, + Err(_) => return ProcessMessageResult::Reconnect, + }; + println!("WOW rd_count == {}", rd_count); + for _ in 0..rd_count { + let rec = match Record::deserialize(&mut reader) { + Ok(r) => r, + Err(_) => return ProcessMessageResult::Reconnect, + }; + println!("WOW we deserialised it"); + let _ = sender.send(rec); + } + } + _ => (), + }; + } + _ => return ProcessMessageResult::Reconnect, }; - println!("WOW we deserialised it"); - let _ = sender.send(rec); - }; - }, - _ => (), - }; - }, - _ => return ProcessMessageResult::Reconnect, - }; - ProcessMessageResult::Continue - })); - Ok(GNS { - service_writer: service_writer, - _callback_loop: callback_loop, - lookup_id: 0, - lookup_tx: lookup_tx, - }) - } + ProcessMessageResult::Continue + } + )); + Ok(GNS { + service_writer: service_writer, + _callback_loop: callback_loop, + lookup_id: 0, + lookup_tx: lookup_tx, + }) + } - /// Lookup a GNS record in the given zone. - /// - /// If `shorten` is not `None` then the result is added to the given shorten zone. Returns - /// immediately with a handle that can be queried for results. - /// - /// # Example - /// - /// ```rust - /// use gnunet::{Cfg, IdentityService, GNS, gns}; - /// - /// let config = Cfg::default().unwrap(); - /// let mut ids = IdentityService::connect(&config).unwrap(); - /// let gns_ego = ids.get_default_ego("gns-master").unwrap(); - /// let mut gns = GNS::connect(&config).unwrap(); - /// let mut lh = gns.lookup("www.gnu", - /// &gns_ego.get_public_key(), - /// gns::RecordType::A, - /// gns::LocalOptions::LocalMaster, - /// None).unwrap(); - /// let record = lh.recv(); - /// println!("Got the IPv4 record for www.gnu: {}", record); - /// ``` - pub fn lookup<'a>( - &'a mut self, - name: &str, - zone: &EcdsaPublicKey, - record_type: RecordType, - options: LocalOptions, - shorten: Option<&EcdsaPrivateKey> + /// Lookup a GNS record in the given zone. + /// + /// If `shorten` is not `None` then the result is added to the given shorten zone. Returns + /// immediately with a handle that can be queried for results. + /// + /// # Example + /// + /// ```rust + /// use gnunet::{Cfg, IdentityService, GNS, gns}; + /// + /// let config = Cfg::default().unwrap(); + /// let mut ids = IdentityService::connect(&config).unwrap(); + /// let gns_ego = ids.get_default_ego("gns-master").unwrap(); + /// let mut gns = GNS::connect(&config).unwrap(); + /// let mut lh = gns.lookup("www.gnu", + /// &gns_ego.get_public_key(), + /// gns::RecordType::A, + /// gns::LocalOptions::LocalMaster, + /// None).unwrap(); + /// let record = lh.recv(); + /// println!("Got the IPv4 record for www.gnu: {}", record); + /// ``` + pub fn lookup<'a>( + &'a mut self, + name: &str, + zone: &EcdsaPublicKey, + record_type: RecordType, + options: LocalOptions, + shorten: Option<&EcdsaPrivateKey>, ) -> Result, LookupError> { + let name_len = name.len(); + if name_len > ll::GNUNET_DNSPARSER_MAX_NAME_LENGTH as usize { + return Err(LookupError::NameTooLong { + name: name.to_string(), + }); + }; - let name_len = name.len(); - if name_len > ll::GNUNET_DNSPARSER_MAX_NAME_LENGTH as usize { - return Err(LookupError::NameTooLong { name: name.to_string() }); - }; - - let id = self.lookup_id; - self.lookup_id += 1; + let id = self.lookup_id; + self.lookup_id += 1; - let msg_length = (80 + name_len + 1).to_u16().unwrap(); - let mut mw = self.service_writer.write_message(msg_length, ll::GNUNET_MESSAGE_TYPE_GNS_LOOKUP); - mw.write_u32::(id).unwrap(); - zone.serialize(&mut mw).unwrap(); - mw.write_i16::(options as i16).unwrap(); - mw.write_i16::(shorten.is_some() as i16).unwrap(); - mw.write_i32::(record_type as i32).unwrap(); - match shorten { - Some(z) => z.serialize(&mut mw).unwrap(), - None => mw.write_all(&[0u8; 32]).unwrap(), - }; - mw.write_all(name.as_bytes()).unwrap(); - mw.write_u8(0u8).unwrap(); + let msg_length = (80 + name_len + 1).to_u16().unwrap(); + let mut mw = self + .service_writer + .write_message(msg_length, ll::GNUNET_MESSAGE_TYPE_GNS_LOOKUP); + mw.write_u32::(id).unwrap(); + zone.serialize(&mut mw).unwrap(); + mw.write_i16::(options as i16).unwrap(); + mw.write_i16::(shorten.is_some() as i16).unwrap(); + mw.write_i32::(record_type as i32).unwrap(); + match shorten { + Some(z) => z.serialize(&mut mw).unwrap(), + None => mw.write_all(&[0u8; 32]).unwrap(), + }; + mw.write_all(name.as_bytes()).unwrap(); + mw.write_u8(0u8).unwrap(); - let (tx, rx) = channel::(); - self.lookup_tx.send((id, tx)).unwrap(); // panics if the callback loop has panicked - try!(mw.send()); - Ok(LookupHandle { - marker: PhantomData, - receiver: rx, - }) - } + let (tx, rx) = channel::(); + self.lookup_tx.send((id, tx)).unwrap(); // panics if the callback loop has panicked + try!(mw.send()); + Ok(LookupHandle { + marker: PhantomData, + receiver: rx, + }) + } } -/// Errors returned by `gns::lookup`. -error_def! ConnectLookupError { - Connect { #[from] cause: service::ConnectError } - => "Failed to connect to the GNS service" ("Reason: {}", cause), - Lookup { #[from] cause: LookupError } - => "Failed to perform the lookup." ("Reason: {}", cause), +quick_error! { + /// Errors returned by `gns::lookup`. + #[derive(Debug)] + pub enum ConnectLookupError { + Connect { cause: service::ConnectError } { + display("Failed to connect to the GNS service. Reason: {}", cause) + cause(cause) + from(cause: service::ConnectError) -> { cause: cause } + } + + Lookup { cause: LookupError } { + display("Failed to perform the lookup. Reason: {}", cause) + cause(cause) + from(cause: LookupError) -> { cause: cause } + } + } } /// Lookup a GNS record in the given zone. @@ -213,21 +236,32 @@ pub fn lookup( zone: &EcdsaPublicKey, record_type: RecordType, options: LocalOptions, - shorten: Option<&EcdsaPrivateKey>) -> Result { - println!("connecting to GNS"); - let mut gns = try!(GNS::connect(cfg)); - println!("connected to GNS"); - let mut h = try!(gns.lookup(name, zone, record_type, options, shorten)); - println!("doing lookup"); - Ok(h.recv()) + shorten: Option<&EcdsaPrivateKey>, +) -> Result { + println!("connecting to GNS"); + let mut gns = try!(GNS::connect(cfg)); + println!("connected to GNS"); + let mut h = try!(gns.lookup(name, zone, record_type, options, shorten)); + println!("doing lookup"); + Ok(h.recv()) } -/// Errors returned by `gns::lookup_in_master`. -error_def! ConnectLookupInMasterError { - GnsLookup { #[from] cause: ConnectLookupError } - => "Failed to connect to the GNS service and perform the lookup" ("Reason: {}", cause), - IdentityGetDefaultEgo { #[from] cause: identity::ConnectGetDefaultEgoError } - => "Failed to retrieve the default identity for gns-master from the identity service" ("Reason: {}", cause), +quick_error! { + /// Errors returned by `gns::lookup_in_master`. + #[derive(Debug)] + pub enum ConnectLookupInMasterError { + GnsLookup { cause: ConnectLookupError } { + cause(cause) + from(cause :ConnectLookupError) -> { cause: cause } + display("Failed to connect to the GNS service and perform the lookup. Reason: {}", cause) + } + + IdentityGetDefaultEgo { cause: identity::ConnectGetDefaultEgoError } { + cause(cause) + from(cause: identity::ConnectGetDefaultEgoError) -> { cause: cause } + display("Failed to retrieve the default identity for gns-master from the identity service. Reason: {}", cause) + } + } } /// Lookup a GNS record in the master zone. @@ -257,38 +291,38 @@ pub fn lookup_in_master( cfg: &Cfg, name: &str, record_type: RecordType, - shorten: Option<&EcdsaPrivateKey>) -> Result { - println!("Getting default ego"); - let ego = try!(identity::get_default_ego(cfg, "gns-master")); - println!("got default ego: {}", ego); - let pk = ego.get_public_key(); - let mut it = name.split('.'); - let opt = match (it.next(), it.next(), it.next()) { - (Some(_), Some("gnu"), None) => LocalOptions::NoDHT, - _ => LocalOptions::LocalMaster, - }; - println!("doing lookup"); - let ret = try!(lookup(cfg, name, &pk, record_type, opt, shorten)); - println!("lookup succeeded"); - Ok(ret) + shorten: Option<&EcdsaPrivateKey>, +) -> Result { + println!("Getting default ego"); + let ego = try!(identity::get_default_ego(cfg, "gns-master")); + println!("got default ego: {}", ego); + let pk = ego.get_public_key(); + let mut it = name.split('.'); + let opt = match (it.next(), it.next(), it.next()) { + (Some(_), Some("gnu"), None) => LocalOptions::NoDHT, + _ => LocalOptions::LocalMaster, + }; + println!("doing lookup"); + let ret = try!(lookup(cfg, name, &pk, record_type, opt, shorten)); + println!("lookup succeeded"); + Ok(ret) } /// A handle returned by `GNS::lookup`. /// /// Used to retrieve the results of a lookup. pub struct LookupHandle<'a> { - marker: PhantomData<&'a GNS>, - receiver: Receiver, + marker: PhantomData<&'a GNS>, + receiver: Receiver, } impl<'a> LookupHandle<'a> { - /// Receive a single result from a lookup. - /// - /// Blocks until a result is available. This function can be called multiple times on a handle to - /// receive multiple results. - pub fn recv(&mut self) -> Record { - // unwrap is safe because the LookupHandle cannot outlive the remote sender. - self.receiver.recv().unwrap() - } + /// Receive a single result from a lookup. + /// + /// Blocks until a result is available. This function can be called multiple times on a handle to + /// receive multiple results. + pub fn recv(&mut self) -> Record { + // unwrap is safe because the LookupHandle cannot outlive the remote sender. + self.receiver.recv().unwrap() + } } - diff --git a/src/gns/record.rs b/src/gns/record.rs index 2ed3bd7..303acb3 100644 --- a/src/gns/record.rs +++ b/src/gns/record.rs @@ -1,15 +1,15 @@ -use std::str::FromStr; -use std::fmt::{Debug, Formatter}; -use std::fmt; -use std::str::from_utf8; use std::ffi::CStr; +use std::fmt; +use std::fmt::{Debug, Formatter}; use std::io::{self, Read}; +use std::str::from_utf8; +use std::str::FromStr; //use std::c_str::CString; use byteorder::{BigEndian, ReadBytesExt}; -use libc::{free, c_char, c_void}; +use libc::{c_char, c_void, free}; -use ll; use self::RecordType::*; +use ll; use util::io::ReadUtil; /// An enum of the different GNS record types. @@ -18,172 +18,179 @@ use util::io::ReadUtil; /// to GNS. These are marked **Legacy** and **GNS** respectively. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum RecordType { - /// **Legacy.** Address record. Stores a 32bit IPv4 address. - A = 1, - /// **Legacy.** Name server record. Delegates a DNS zone to use the given authoritative name servers. - NS = 2, - /// **Legacy.** Canonical name record. Alias of one name to another. - CNAME = 5, - /// **Legacy.** Start of authority record. Specifies authoritative information about a DNS zone. - SOA = 6, - /// **Legacy.** Pointer record. Pointer to a canonical name. - PTR = 12, - /// **Legacy.** Mail exchange record. Maps a domain name to a list of message transfer agents for that - /// domain. - MX = 15, - /// **Legacy.** Text record. Used to store human-readable data and various forms of machine-readable data. - TXT = 16, - /// **Legacy.** Address record. Stores a 128bit IPv6 address. - AAAA = 28, - /// **Legacy.** TLSA certificate association. A record for DNS-based Authentication of Named Entities (DANE). - TLSA = 52, - - /// **GNS.** Petname key record. Used to delegate to other users' zones and give those zones a petname. - PKEY = 65536, - /// **GNS.** Nickname record. Used to give a zone a name. - NICK = 65537, - /// **GNS.** Legacy hostname record. - LEHO = 65538, - /// **GNS.** Virtual public network record. - VPN = 65539, - /// **GNS.** GNS2DNS record. Used to delegate authority to a legacy DNS zone. - GNS2DNS = 65540, + /// **Legacy.** Address record. Stores a 32bit IPv4 address. + A = 1, + /// **Legacy.** Name server record. Delegates a DNS zone to use the given authoritative name servers. + NS = 2, + /// **Legacy.** Canonical name record. Alias of one name to another. + CNAME = 5, + /// **Legacy.** Start of authority record. Specifies authoritative information about a DNS zone. + SOA = 6, + /// **Legacy.** Pointer record. Pointer to a canonical name. + PTR = 12, + /// **Legacy.** Mail exchange record. Maps a domain name to a list of message transfer agents for that + /// domain. + MX = 15, + /// **Legacy.** Text record. Used to store human-readable data and various forms of machine-readable data. + TXT = 16, + /// **Legacy.** Address record. Stores a 128bit IPv6 address. + AAAA = 28, + /// **Legacy.** TLSA certificate association. A record for DNS-based Authentication of Named Entities (DANE). + TLSA = 52, + + /// **GNS.** Petname key record. Used to delegate to other users' zones and give those zones a petname. + PKEY = 65536, + /// **GNS.** Nickname record. Used to give a zone a name. + NICK = 65537, + /// **GNS.** Legacy hostname record. + LEHO = 65538, + /// **GNS.** Virtual public network record. + VPN = 65539, + /// **GNS.** GNS2DNS record. Used to delegate authority to a legacy DNS zone. + GNS2DNS = 65540, } impl RecordType { - /// Creates a RecordType from it's record type number. - /// - /// # Example - /// - /// ```rust - /// use gnunet::gns::RecordType::{self, A}; - /// - /// let x = RecordType::from_u32(1); - /// let y = RecordType::from_u32(1234); - /// assert!(x == Some(A)); - /// assert!(y == None); - /// ``` - pub fn from_u32(x: u32) -> Option { - Some(match x { - 1 => A, - 2 => NS, - 5 => CNAME, - 6 => SOA, - 12 => PTR, - 15 => MX, - 16 => TXT, - 28 => AAAA, - 52 => TLSA, - - 65536 => PKEY, - 65537 => NICK, - 65538 => LEHO, - 65539 => VPN, - 65540 => GNS2DNS, - - _ => return None, - }) - } + /// Creates a RecordType from it's record type number. + /// + /// # Example + /// + /// ```rust + /// use gnunet::gns::RecordType::{self, A}; + /// + /// let x = RecordType::from_u32(1); + /// let y = RecordType::from_u32(1234); + /// assert!(x == Some(A)); + /// assert!(y == None); + /// ``` + pub fn from_u32(x: u32) -> Option { + Some(match x { + 1 => A, + 2 => NS, + 5 => CNAME, + 6 => SOA, + 12 => PTR, + 15 => MX, + 16 => TXT, + 28 => AAAA, + 52 => TLSA, + + 65536 => PKEY, + 65537 => NICK, + 65538 => LEHO, + 65539 => VPN, + 65540 => GNS2DNS, + + _ => return None, + }) + } } -/// Error generated when attempting to parse a `RecordType` -error_def! RecordTypeFromStrError { - ParsingFailed => "Failed to parse the string as a RecordType", +quick_error! { + /// Error generated when attempting to parse a `RecordType` + #[derive(Debug)] + pub enum RecordTypeFromStrError { + ParsingFailed { + display("Failed to parse the string as a RecordType") + } + } } impl FromStr for RecordType { - type Err = RecordTypeFromStrError; - - fn from_str(s: &str) -> Result { - match s { - "A" => Ok(A), - "NS" => Ok(NS), - "CNAME" => Ok(CNAME), - "SOA" => Ok(SOA), - "PTR" => Ok(PTR), - "MX" => Ok(MX), - "TXT" => Ok(TXT), - "AAAA" => Ok(AAAA), - "TLSA" => Ok(TLSA), - - "PKEY" => Ok(PKEY), - "NICK" => Ok(NICK), - "LEHO" => Ok(LEHO), - "VPN" => Ok(VPN), - "GNS2DNS" => Ok(GNS2DNS), - _ => Err(RecordTypeFromStrError::ParsingFailed), + type Err = RecordTypeFromStrError; + + fn from_str(s: &str) -> Result { + match s { + "A" => Ok(A), + "NS" => Ok(NS), + "CNAME" => Ok(CNAME), + "SOA" => Ok(SOA), + "PTR" => Ok(PTR), + "MX" => Ok(MX), + "TXT" => Ok(TXT), + "AAAA" => Ok(AAAA), + "TLSA" => Ok(TLSA), + + "PKEY" => Ok(PKEY), + "NICK" => Ok(NICK), + "LEHO" => Ok(LEHO), + "VPN" => Ok(VPN), + "GNS2DNS" => Ok(GNS2DNS), + _ => Err(RecordTypeFromStrError::ParsingFailed), + } } - } } impl fmt::Display for RecordType { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - Debug::fmt(self, f) - } + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + Debug::fmt(self, f) + } } /// A record in the GNU Name System. #[allow(dead_code)] pub struct Record { - data: ll::Struct_GNUNET_GNSRECORD_Data, - buff: Vec, + data: ll::Struct_GNUNET_GNSRECORD_Data, + buff: Vec, } impl Record { - /// Deserialize a record from a byte stream. - pub fn deserialize(reader: &mut T) -> Result where T: Read { - let expiration_time = try!(reader.read_u64::()); - let data_size = try!(reader.read_u32::()) as u64; - let record_type = try!(reader.read_u32::()); - let flags = try!(reader.read_u32::()); - let buff = try!(reader.read_exact_alloc(data_size as usize)); - let data = buff.as_ptr() as *const c_void; - - Ok(Record { - data: ll::Struct_GNUNET_GNSRECORD_Data { - data: data, - expiration_time: expiration_time, - data_size: data_size as usize, - record_type: record_type, - flags: flags, - }, - buff: buff, - }) - } + /// Deserialize a record from a byte stream. + pub fn deserialize(reader: &mut T) -> Result + where + T: Read, + { + let expiration_time = try!(reader.read_u64::()); + let data_size = try!(reader.read_u32::()) as u64; + let record_type = try!(reader.read_u32::()); + let flags = try!(reader.read_u32::()); + let buff = try!(reader.read_exact_alloc(data_size as usize)); + let data = buff.as_ptr() as *const c_void; + + Ok(Record { + data: ll::Struct_GNUNET_GNSRECORD_Data { + data: data, + expiration_time: expiration_time, + data_size: data_size as usize, + record_type: record_type, + flags: flags, + }, + buff: buff, + }) + } - /// Get the type of a record. - pub fn record_type(&self) -> RecordType { - RecordType::from_u32(self.data.record_type).unwrap() - } + /// Get the type of a record. + pub fn record_type(&self) -> RecordType { + RecordType::from_u32(self.data.record_type).unwrap() + } } impl Debug for Record { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let tpe = self.data.record_type; - try!(write!(f, "{:?}: ", RecordType::from_u32(tpe).unwrap())); - unsafe { - let cs = ll::GNUNET_GNSRECORD_value_to_string(tpe, self.data.data, self.data.data_size); - match cs.is_null() { - true => write!(f, ""), - false => { - let constified = cs as *const c_char; - let s = from_utf8(CStr::from_ptr(constified).to_bytes()); - let ret = match s { - Ok(ss) => write!(f, "{}", ss), - Err(_) => write!(f, ""), - }; - // TODO: use the c-string wrapper that automatically dealloces when it exists - free(cs as *mut c_void); - ret - }, - } + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let tpe = self.data.record_type; + try!(write!(f, "{:?}: ", RecordType::from_u32(tpe).unwrap())); + unsafe { + let cs = ll::GNUNET_GNSRECORD_value_to_string(tpe, self.data.data, self.data.data_size); + match cs.is_null() { + true => write!(f, ""), + false => { + let constified = cs as *const c_char; + let s = from_utf8(CStr::from_ptr(constified).to_bytes()); + let ret = match s { + Ok(ss) => write!(f, "{}", ss), + Err(_) => write!(f, ""), + }; + // TODO: use the c-string wrapper that automatically dealloces when it exists + free(cs as *mut c_void); + ret + } + } + } } - } } impl fmt::Display for Record { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - Debug::fmt(self, f) - } + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + Debug::fmt(self, f) + } } - diff --git a/src/hello.rs b/src/hello.rs index 8bb0eb2..8d9bfa6 100644 --- a/src/hello.rs +++ b/src/hello.rs @@ -1,47 +1,57 @@ +use byteorder::{BigEndian, ReadBytesExt}; use std::fmt; use std::io::{self, Read}; -use byteorder::{self, ReadBytesExt, BigEndian}; use PeerIdentity; #[derive(Debug)] pub struct Hello { - /// Use this peer in F2F mode. Do not gossip this hello. - pub friend_only: bool, + /// Use this peer in F2F mode. Do not gossip this hello. + pub friend_only: bool, - /// The identity of the peer. - pub id: PeerIdentity, + /// The identity of the peer. + pub id: PeerIdentity, } -error_def! HelloDeserializeError { - ShortMessage - => "Unexpected EOF when deserializing the hello", - Io { #[from] cause: io::Error } - => "There was an I/O error reading the hello" ("Error: {}", cause), +quick_error! { + #[derive(Debug)] + pub enum HelloDeserializeError { + ShortMessage { + display("Unexpected EOF when deserializing the hello") + } + + Io { cause: io::Error } { + display("There was an I/O error reading the hello. Error: {}", cause) + cause(cause) + from(cause: io::Error) -> { cause: cause } + } + } } impl Hello { - pub fn deserialize(r: &mut R) -> Result - where R: Read - { - let friend_only = match r.read_u32::() { - Ok(x) => x != 0, - Err(e) => return Err(match e { - byteorder::Error::UnexpectedEOF => HelloDeserializeError::ShortMessage, - byteorder::Error::Io(e) => HelloDeserializeError::Io { cause: e }, - }), - }; - let id = try!(PeerIdentity::deserialize(r)); - Ok(Hello { - friend_only: friend_only, - id: id, - }) - } + pub fn deserialize(r: &mut R) -> Result + where + R: Read, + { + let friend_only = match r.read_u32::() { + Ok(x) => x != 0, + Err(e) => { + return Err(match e.kind() { + ::std::io::ErrorKind::UnexpectedEof => HelloDeserializeError::ShortMessage, + _ => HelloDeserializeError::Io { cause: e }, + }) + } + }; + let id = try!(PeerIdentity::deserialize(r)); + Ok(Hello { + friend_only: friend_only, + id: id, + }) + } } impl fmt::Display for Hello { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Hello!") - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Hello!") + } } - diff --git a/src/identity/mod.rs b/src/identity/mod.rs index 196ab14..6af0731 100644 --- a/src/identity/mod.rs +++ b/src/identity/mod.rs @@ -1,125 +1,175 @@ -use std::string; -use std::collections::HashMap; -use std::io::{self, Read, Write}; -use std::fmt; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use num::ToPrimitive; +use std::collections::HashMap; +use std::fmt; +use std::io::{self, Read, Write}; +use std::string; +use configuration::Cfg; use ll; +use service::{self, ServiceReader, ServiceWriter}; +use util::{ReadCString, ReadCStringError, ReadCStringWithLenError}; use EcdsaPrivateKey; use EcdsaPublicKey; use HashCode; -use service::{self, ServiceReader, ServiceWriter}; -use configuration::Cfg; -use util::{ReadCString, ReadCStringError, ReadCStringWithLenError}; /// A GNUnet identity. /// /// An ego consists of a public/private key pair and a name. #[derive(Clone)] pub struct Ego { - pk: EcdsaPrivateKey, - name: Option, - id: HashCode, + pk: EcdsaPrivateKey, + name: Option, + id: HashCode, } impl Ego { - /// Get a copy of the global, anonymous ego. - pub fn anonymous() -> Ego { - let pk = EcdsaPrivateKey::anonymous(); - let id = pk.get_public().hash(); - Ego { - pk: pk, - name: None, - id: id, + /// Get a copy of the global, anonymous ego. + pub fn anonymous() -> Ego { + let pk = EcdsaPrivateKey::anonymous(); + let id = pk.get_public().hash(); + Ego { + pk: pk, + name: None, + id: id, + } } - } - /// Get the public key of an ego. - pub fn get_public_key(&self) -> EcdsaPublicKey { - self.pk.get_public() - } + /// Get the public key of an ego. + pub fn get_public_key(&self) -> EcdsaPublicKey { + self.pk.get_public() + } - /// Get the private key of an ego. - pub fn get_private_key(&self) -> EcdsaPrivateKey { - self.pk.clone() - } + /// Get the private key of an ego. + pub fn get_private_key(&self) -> EcdsaPrivateKey { + self.pk.clone() + } - /// Get the name of an ego. - pub fn get_name(&self) -> Option { - self.name.clone() - } + /// Get the name of an ego. + pub fn get_name(&self) -> Option { + self.name.clone() + } - /// Get the unique id of an ego. This is a hash of the ego's public key. - pub fn get_id(&self) -> &HashCode { - &self.id - } + /// Get the unique id of an ego. This is a hash of the ego's public key. + pub fn get_id(&self) -> &HashCode { + &self.id + } } impl fmt::Display for Ego { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let name = match self.name { - Some(ref n) => &**n, - None => "", - }; - write!(f, "{} ({})", name, self.id) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let name = match self.name { + Some(ref n) => &**n, + None => "", + }; + write!(f, "{} ({})", name, self.id) + } } /// A handle to the identity service. pub struct IdentityService { - service_reader: ServiceReader, - service_writer: ServiceWriter, - egos: HashMap, + service_reader: ServiceReader, + service_writer: ServiceWriter, + egos: HashMap, } -/// Errors returned by `IdentityService::connect` -error_def! ConnectError { - Connect { #[from] cause: service::ConnectError } - => "Failed to connect to the service" ("Reason: {}", cause), - Disconnected - => "The service disconnected unexpectedly", - Io { #[from] cause: io::Error } - => "An I/O error occured while communicating with the identity service" ("Specifically: {}", cause), - ReadMessage { #[from] cause: service::ReadMessageError } - => "Failed to read a message from the server" ("Specifically: {}", cause), - InvalidName { #[from] cause: string::FromUtf8Error } - => "The service responded with a name containing invalid utf-8 during initial exchange. *(It is a bug to see this error)*" ("Utf8-error: {}", cause), - UnexpectedMessageType { ty: u16 } - => "Received an unexpected message from the service during initial exchange. *(It is a bug to see this error)*" ("Message type {} was not expected.", ty) +quick_error! { + /// Errors returned by `IdentityService::connect` + #[derive(Debug)] + pub enum ConnectError { + Connect { cause: service::ConnectError } { + display("Failed to connect to the service. Reason: {}", cause) + cause(cause) + from(cause: service::ConnectError) -> { cause: cause } + } + + Disconnected { + display("The service disconnected unexpectedly") + } + + Io { cause: io::Error } { + display("An I/O error occured while communicating with the identity service. Specifically: {}", cause) + cause(cause) + } + + ReadMessage { cause: service::ReadMessageError } { + cause(cause) + display("Failed to read a message from the server. Specifically: {}", cause) + from(cause: service::ReadMessageError) -> { cause: cause } + } + + InvalidName { cause: string::FromUtf8Error } { + display("The service responded with a name containing invalid utf-8 during initial exchange. *(It is a bug to see this error)*. Utf8-error: {}", cause) + cause(cause) + from(cause: string::FromUtf8Error) -> { cause: cause } + } + + UnexpectedMessageType { ty: u16 } { + display("Received an unexpected message from the service during initial exchange. *(It is a bug to see this error)*. Message type {} was not expected.", ty) + } + } } byteorder_error_chain! {ConnectError} -/// Errors returned by `IdentityService::get_default_ego` -error_def! GetDefaultEgoError { - NameTooLong { name: String } - => "The name of the service was too long" ("\"{}\" is too long to be the name of a service.", name), - Io { #[from] cause: io::Error } - => "An I/O error occured while communicating with the identity service" ("Specifically: {}", cause), - ReadMessage { #[from] cause: service::ReadMessageError } - => "Failed to read a message from the server" ("Specifically: {}", cause), - ServiceResponse { response: String } - => "The service responded with an error message" ("Error: \"{}\"", response), - MalformedErrorResponse { #[from] cause: string::FromUtf8Error } - => "The service responded with an error message but the message contained invalid utf-8" ("Utf8-error: {}", cause), - ReceiveName { #[from] cause: ReadCStringWithLenError } - => "Failed to receive the identity name from the service" ("Reason: {}", cause), - Connect { #[from] cause: ConnectError } - => "Failed to connect to the identity service" ("Reason: {}", cause), - InvalidResponse - => "The service response was incoherent. You should file a bug-report if you encounter this error.", - Disconnected - => "The service disconnected unexpectedly", +quick_error! { + /// Errors returned by `IdentityService::get_default_ego` + #[derive(Debug)] + pub enum GetDefaultEgoError { + NameTooLong { name: String } { + display("The name of the service was too long. \"{}\" is too long to be the name of a service.", name) + } + + Io { cause: io::Error } { + cause(cause) + display("An I/O error occured while communicating with the identity service. Specifically: {}", cause) + } + + ReadMessage { cause: service::ReadMessageError } { + cause(cause) + from(cause: service::ReadMessageError) -> { cause: cause } + display("Failed to read a message from the server. Specifically: {}", cause) + } + + ServiceResponse { response: String } { + display("The service responded with an error message. Error: \"{}\"", response) + } + + MalformedErrorResponse { cause: string::FromUtf8Error } { + cause(cause) + from(cause: string::FromUtf8Error) -> { cause: cause } + display("The service responded with an error message but the message contained invalid utf-8. Utf8-error: {}", cause) + } + + ReceiveName { cause: ReadCStringWithLenError } { + cause(cause) + from(cause: ReadCStringWithLenError) -> { cause: cause } + display("Failed to receive the identity name from the service. Reason: {}", cause) + } + + Connect { cause: ConnectError } { + cause(cause) + from(cause: ConnectError) -> { cause: cause } + display("Failed to connect to the identity service. Reason: {}", cause) + } + + InvalidResponse { + display("The service response was incoherent. You should file a bug-report if you encounter this error.") + } + + Disconnected { + display("The service disconnected unexpectedly") + } + } } byteorder_error_chain! {GetDefaultEgoError} impl IdentityService { - /// Connect to the identity service. - /// - /// Returns either a handle to the identity service or a `ServiceConnectError`. `cfg` contains - /// the configuration to use to connect to the service. - pub fn connect(cfg: &Cfg) -> Result { - /* + /// Connect to the identity service. + /// + /// Returns either a handle to the identity service or a `ServiceConnectError`. `cfg` contains + /// the configuration to use to connect to the service. + pub fn connect(cfg: &Cfg) -> Result { + /* let (get_tx, get_rx) = channel::<(String, Sender>>(); let service = try!(Service::connect("identity", move |&mut: tpe: u16, mut reader: LimitReader| -> ProcessMessageResult { loop { @@ -127,124 +177,150 @@ impl IdentityService { } })); */ - let (mut service_reader, mut service_writer) = try!(service::connect(cfg, "identity")); - let mut egos: HashMap = HashMap::new(); - { - let mw = service_writer.write_message(4, ll::GNUNET_MESSAGE_TYPE_IDENTITY_START); - try!(mw.send()); - }; - loop { - let (tpe, mut mr) = try!(service_reader.read_message()); - match tpe { - ll::GNUNET_MESSAGE_TYPE_IDENTITY_UPDATE => { - let name_len = try!(mr.read_u16::()); - let eol = try!(mr.read_u16::()); - if eol != 0 { - break; - }; - let pk = try!(EcdsaPrivateKey::deserialize(&mut mr)); - let mut v: Vec = Vec::with_capacity(name_len as usize); - for r in mr.bytes() { - let b = try!(r); - if b == 0u8 { - break; - } - v.push(b) - }; - let name = match String::from_utf8(v) { - Ok(n) => n, - Err(v) => return Err(ConnectError::InvalidName { cause: v }), - }; - let id = pk.get_public().hash(); - egos.insert(id.clone(), Ego { - pk: pk, - name: Some(name), - id: id, - }); - }, - _ => return Err(ConnectError::UnexpectedMessageType { ty: tpe }), - }; - }; - Ok(IdentityService { - service_reader: service_reader, - service_writer: service_writer, - egos: egos, - }) - } + let (mut service_reader, mut service_writer) = try!(service::connect(cfg, "identity")); + let mut egos: HashMap = HashMap::new(); + { + let mw = service_writer.write_message(4, ll::GNUNET_MESSAGE_TYPE_IDENTITY_START); + try!(mw.send()); + }; + loop { + let (tpe, mut mr) = try!(service_reader.read_message()); + match tpe { + ll::GNUNET_MESSAGE_TYPE_IDENTITY_UPDATE => { + let name_len = try!(mr.read_u16::()); + let eol = try!(mr.read_u16::()); + if eol != 0 { + break; + }; + let pk = try!(EcdsaPrivateKey::deserialize(&mut mr)); + let mut v: Vec = Vec::with_capacity(name_len as usize); + for r in mr.bytes() { + let b = try!(r); + if b == 0u8 { + break; + } + v.push(b) + } + let name = match String::from_utf8(v) { + Ok(n) => n, + Err(v) => return Err(ConnectError::InvalidName { cause: v }), + }; + let id = pk.get_public().hash(); + egos.insert( + id.clone(), + Ego { + pk: pk, + name: Some(name), + id: id, + }, + ); + } + _ => return Err(ConnectError::UnexpectedMessageType { ty: tpe }), + }; + } + Ok(IdentityService { + service_reader: service_reader, + service_writer: service_writer, + egos: egos, + }) + } - /// Get the default identity associated with a service. - /// - /// # Example - /// - /// Get the ego for the default master zone. - /// - /// ```rust - /// use gnunet::{Cfg, IdentityService}; - /// - /// let config = Cfg::default().unwrap(); - /// let mut ids = IdentityService::connect(&config).unwrap(); - /// let ego = ids.get_default_ego("gns-master").unwrap(); - /// ``` - pub fn get_default_ego(&mut self, name: &str) -> Result { - let name_len = name.len(); + /// Get the default identity associated with a service. + /// + /// # Example + /// + /// Get the ego for the default master zone. + /// + /// ```rust + /// use gnunet::{Cfg, IdentityService}; + /// + /// let config = Cfg::default().unwrap(); + /// let mut ids = IdentityService::connect(&config).unwrap(); + /// let ego = ids.get_default_ego("gns-master").unwrap(); + /// ``` + pub fn get_default_ego(&mut self, name: &str) -> Result { + let name_len = name.len(); - let msg_length = match (8 + name_len + 1).to_u16() { - Some(l) => l, - None => return Err(GetDefaultEgoError::NameTooLong { name: name.to_string() }), - }; - { - let mut mw = self.service_writer.write_message(msg_length, ll::GNUNET_MESSAGE_TYPE_IDENTITY_GET_DEFAULT); - mw.write_u16::((name_len + 1) as u16).unwrap(); - mw.write_u16::(0).unwrap(); - mw.write_all(name.as_bytes()).unwrap(); - mw.write_u8(0u8).unwrap(); - try!(mw.send()); - }; + let msg_length = match (8 + name_len + 1).to_u16() { + Some(l) => l, + None => { + return Err(GetDefaultEgoError::NameTooLong { + name: name.to_string(), + }) + } + }; + { + let mut mw = self + .service_writer + .write_message(msg_length, ll::GNUNET_MESSAGE_TYPE_IDENTITY_GET_DEFAULT); + mw.write_u16::((name_len + 1) as u16).unwrap(); + mw.write_u16::(0).unwrap(); + mw.write_all(name.as_bytes()).unwrap(); + mw.write_u8(0u8).unwrap(); + try!(mw.send()); + }; - let (tpe, mut mr) = try!(self.service_reader.read_message()); - match tpe { - ll::GNUNET_MESSAGE_TYPE_IDENTITY_RESULT_CODE => { - try!(mr.read_u32::()); - match mr.read_c_string() { - Err(e) => match e { - ReadCStringError::Io { cause } => Err(GetDefaultEgoError::Io { cause: cause }), - ReadCStringError::FromUtf8 { cause } => Err(GetDefaultEgoError::MalformedErrorResponse { cause: cause }), - ReadCStringError::Disconnected => Err(GetDefaultEgoError::Disconnected), - }, - Ok(s) => Err(GetDefaultEgoError::ServiceResponse { response: s }), - } - }, - ll::GNUNET_MESSAGE_TYPE_IDENTITY_SET_DEFAULT => match try!(mr.read_u16::()) { - 0 => Err(GetDefaultEgoError::InvalidResponse), - reply_name_len => { - let zero = try!(mr.read_u16::()); - match zero { - 0 => { - let pk = try!(EcdsaPrivateKey::deserialize(&mut mr)); - let s = try!(mr.read_c_string_with_len((reply_name_len - 1) as usize)); - match &s[..] == name { - true => { - let id = pk.get_public().hash(); - Ok(self.egos[&id].clone()) - }, - false => Err(GetDefaultEgoError::InvalidResponse), - } - }, + let (tpe, mut mr) = try!(self.service_reader.read_message()); + match tpe { + ll::GNUNET_MESSAGE_TYPE_IDENTITY_RESULT_CODE => { + try!(mr.read_u32::()); + match mr.read_c_string() { + Err(e) => match e { + ReadCStringError::Io { cause } => { + Err(GetDefaultEgoError::Io { cause: cause }) + } + ReadCStringError::FromUtf8 { cause } => { + Err(GetDefaultEgoError::MalformedErrorResponse { cause: cause }) + } + ReadCStringError::Disconnected => Err(GetDefaultEgoError::Disconnected), + }, + Ok(s) => Err(GetDefaultEgoError::ServiceResponse { response: s }), + } + } + ll::GNUNET_MESSAGE_TYPE_IDENTITY_SET_DEFAULT => { + match try!(mr.read_u16::()) { + 0 => Err(GetDefaultEgoError::InvalidResponse), + reply_name_len => { + let zero = try!(mr.read_u16::()); + match zero { + 0 => { + let pk = try!(EcdsaPrivateKey::deserialize(&mut mr)); + let s = + try!(mr.read_c_string_with_len((reply_name_len - 1) as usize)); + match &s[..] == name { + true => { + let id = pk.get_public().hash(); + Ok(self.egos[&id].clone()) + } + false => Err(GetDefaultEgoError::InvalidResponse), + } + } + _ => Err(GetDefaultEgoError::InvalidResponse), + } + } + } + } _ => Err(GetDefaultEgoError::InvalidResponse), - } - }, - }, - _ => Err(GetDefaultEgoError::InvalidResponse), + } } - } } -/// Errors returned by `identity::get_default_ego` -error_def! ConnectGetDefaultEgoError { - GetDefaultEgo { #[from] cause: GetDefaultEgoError } - => "Ego lookup failed" ("Reason: {}", cause), - Connect { #[from] cause: ConnectError } - => "Failed to connect to the service and perform initialization" ("Reason: {}", cause), +quick_error! { + /// Errors returned by `identity::get_default_ego` + #[derive(Debug)] + pub enum ConnectGetDefaultEgoError { + GetDefaultEgo { cause: GetDefaultEgoError }{ + display("Ego lookup failed. Reason: {}", cause) + cause(cause) + from(cause: GetDefaultEgoError) -> { cause: cause } + } + + Connect { cause: ConnectError }{ + cause(cause) + display("Failed to connect to the service and perform initialization. Reason: {}", cause) + from(cause: ConnectError) -> { cause: cause } + } + } } /// Get the default identity associated with a service. @@ -265,11 +341,8 @@ error_def! ConnectGetDefaultEgoError { /// This a convenience function that connects to the identity service, does the query, then /// disconnects. If you want to do multiple queries you should connect to the service with /// `IdentityService::connect` then use that handle to do the queries. -pub fn get_default_ego( - cfg: &Cfg, - name: &str) -> Result { - let mut is = try!(IdentityService::connect(cfg)); - let ret = try!(is.get_default_ego(name)); - Ok(ret) +pub fn get_default_ego(cfg: &Cfg, name: &str) -> Result { + let mut is = try!(IdentityService::connect(cfg)); + let ret = try!(is.get_default_ego(name)); + Ok(ret) } - diff --git a/src/lib.rs b/src/lib.rs index 432fbf1..fdd14b1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,31 +15,32 @@ //! println!("Got the following IPv4 record for www.gnu: {}", r); //! ``` -#![feature(unboxed_closures)] -#![feature(libc)] -#![feature(plugin)] -#![feature(into_cow)] - -#![plugin(error_def)] -#![plugin(regex_macros)] - +//#![feature(unboxed_closures)] +//#![feature(libc)] +//#![feature(plugin)] +//#![feature(into_cow)] #![crate_name = "gnunet"] -extern crate libc; -extern crate unix_socket; -extern crate rand; extern crate byteorder; extern crate crypto as rcrypto; +extern crate libc; extern crate num; +extern crate rand; extern crate regex; +extern crate unix_socket; + +#[macro_use] +extern crate quick_error; +#[macro_use] +extern crate lazy_static; pub use configuration::Cfg; -pub use crypto::{EcdsaPublicKey, EcdsaPrivateKey, HashCode}; +pub use crypto::{EcdsaPrivateKey, EcdsaPublicKey, HashCode}; +pub use gns::{LocalOptions, GNS}; pub use gns::{Record, RecordType}; -pub use gns::{GNS, LocalOptions}; -pub use identity::{Ego, IdentityService}; pub use hello::Hello; +pub use identity::{Ego, IdentityService}; pub use peerinfo::{iterate_peers, self_id, PeerIdentity}; //pub use dht::DHT; @@ -70,30 +71,32 @@ macro_rules! byteorder_error_chain { */ macro_rules! byteorder_error_chain { - ($t:ident) => ( - impl From<::byteorder::Error> for $t { - #[inline] - fn from(e: ::byteorder::Error) -> $t { - match e { - ::byteorder::Error::UnexpectedEOF => $t::Disconnected, - ::byteorder::Error::Io(e) => $t::Io { cause: e }, + ($t:ident) => { + impl From<::std::io::Error> for $t { + #[inline] + fn from(e: ::std::io::Error) -> $t { + match e.kind() { + ::std::io::ErrorKind::UnexpectedEof => $t::Disconnected, + _ => $t::Io { cause: e }, + } + } } - } - } - ) + }; } +/* macro_rules! unwrap_result { - ($e:expr) => ( - match $e { - Ok(o) => o, - Err(ref e) => { - ::print_error(e, file!(), line!()); - panic!(); - } - } - ) + ($e:expr) => { + match $e { + Ok(o) => o, + Err(ref e) => { + ::print_error(e, file!(), line!()); + panic!(); + } + } + }; } +*/ #[cfg(test)] fn print_error(error: &E, file: &str, line: u32) { @@ -110,18 +113,17 @@ fn print_error(error: &E, file: &str, line: u32) { #[allow(dead_code, non_camel_case_types, non_snake_case, non_upper_case_globals)] mod ll; -pub mod service; pub mod configuration; -pub mod time; -pub mod paths; pub mod gns; +pub mod paths; +pub mod service; +pub mod time; //pub mod dht; mod crypto; +pub mod hello; pub mod identity; -mod util; pub mod peerinfo; -pub mod hello; +mod util; //pub mod cadet; pub mod data; pub mod transport; - diff --git a/src/peerinfo/peerinfo.rs b/src/peerinfo/peerinfo.rs index aaf7c44..35563b2 100644 --- a/src/peerinfo/peerinfo.rs +++ b/src/peerinfo/peerinfo.rs @@ -1,122 +1,159 @@ -use std::mem::{uninitialized, size_of_val}; +use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use libc::{c_char, c_void, size_t}; use std::fmt; -use std::str::{from_utf8, FromStr}; use std::io::{self, Read, Write}; -use libc::{c_void, c_char, size_t}; -use byteorder::{self, BigEndian, ReadBytesExt, WriteBytesExt}; +use std::mem::{size_of_val, uninitialized}; +use std::str::{from_utf8, FromStr}; use ll; +use service::{self, connect, ReadMessageError, ServiceReader}; +use transport::{self, TransportServiceInitError}; use Cfg; -use service::{self, connect, ServiceReader, ReadMessageError}; use Hello; -use transport::{self, TransportServiceInitError}; /// The identity of a GNUnet peer. pub struct PeerIdentity { - data: ll::Struct_GNUNET_PeerIdentity, + data: ll::Struct_GNUNET_PeerIdentity, } impl PeerIdentity { - pub fn deserialize(r: &mut R) -> Result where R: Read { - let mut ret: PeerIdentity = unsafe { uninitialized() }; - try!(r.read_exact(&mut ret.data.public_key.q_y[..])); - Ok(ret) - } + pub fn deserialize(r: &mut R) -> Result + where + R: Read, + { + let mut ret: PeerIdentity = unsafe { uninitialized() }; + try!(r.read_exact(&mut ret.data.public_key.q_y[..])); + Ok(ret) + } - pub fn serialize(&self, w: &mut T) -> Result<(), io::Error> where T: Write { - w.write_all(&self.data.public_key.q_y[..]) - } + pub fn serialize(&self, w: &mut T) -> Result<(), io::Error> + where + T: Write, + { + w.write_all(&self.data.public_key.q_y[..]) + } } -/// Error generated when attempting to parse a PeerIdentity -error_def! PeerIdentityFromStrError { - ParsingFailed => "Failed to parse the string as a PeerIdentity" +quick_error! { + /// Error generated when attempting to parse a PeerIdentity + #[derive(Debug)] + pub enum PeerIdentityFromStrError { + ParsingFailed { + display("Failed to parse the string as a PeerIdentity") + } + } } impl FromStr for PeerIdentity { - type Err = PeerIdentityFromStrError; - - fn from_str(s: &str) -> Result { - unsafe { - let ret: ll::Struct_GNUNET_PeerIdentity = uninitialized(); - let res = ll::GNUNET_STRINGS_string_to_data(s.as_ptr() as *const i8, s.len() as size_t, ret.public_key.q_y.as_ptr() as *mut c_void, ret.public_key.q_y.len() as size_t); - match res { - ll::GNUNET_OK => Ok(PeerIdentity { - data: ret, - }), - _ => Err(PeerIdentityFromStrError::ParsingFailed), - } + type Err = PeerIdentityFromStrError; + + fn from_str(s: &str) -> Result { + unsafe { + let ret: ll::Struct_GNUNET_PeerIdentity = uninitialized(); + let res = ll::GNUNET_STRINGS_string_to_data( + s.as_ptr() as *const i8, + s.len() as size_t, + ret.public_key.q_y.as_ptr() as *mut c_void, + ret.public_key.q_y.len() as size_t, + ); + match res { + ll::GNUNET_OK => Ok(PeerIdentity { data: ret }), + _ => Err(PeerIdentityFromStrError::ParsingFailed), + } + } } - } } -/// Errors returned by `iterate_peers`. -error_def! IteratePeersError { - Io { #[from] cause: io::Error } - => "There as an I/O error communicating with the peerinfo service" ("Specifically: {}", cause), - Connect { #[from] cause: service::ConnectError } - => "Failed to connect to the peerinfo service" ("Reason: {}", cause) +quick_error! { + /// Errors returned by `iterate_peers`. + #[derive(Debug)] + pub enum IteratePeersError { + Io { cause: io::Error } { + from(cause: io::Error) -> { cause: cause } + display("There as an I/O error communicating with the peerinfo service. Specifically: {}", cause) + cause(cause) + } + + Connect { cause: service::ConnectError } { + from(cause: service::ConnectError) -> { cause: cause } + display("Failed to connect to the peerinfo service. Reason: {}", cause) + cause(cause) + } + } } /// Iterate over all the currently connected peers. pub fn iterate_peers(cfg: &Cfg) -> Result { - let (sr, mut sw) = try!(connect(cfg, "peerinfo")); - - let msg_length = 8u16; - let mut mw = sw.write_message(msg_length, ll::GNUNET_MESSAGE_TYPE_PEERINFO_GET_ALL); - mw.write_u32::(0).unwrap(); - try!(mw.send()); - Ok(Peers { - service: sr, - }) -} + let (sr, mut sw) = try!(connect(cfg, "peerinfo")); + + let msg_length = 8u16; + let mut mw = sw.write_message(msg_length, ll::GNUNET_MESSAGE_TYPE_PEERINFO_GET_ALL); + mw.write_u32::(0).unwrap(); + try!(mw.send()); + Ok(Peers { service: sr }) +} pub fn self_id(cfg: &Cfg) -> Result { - let hello = try!(transport::self_hello(cfg)); - Ok(hello.id) + let hello = try!(transport::self_hello(cfg)); + Ok(hello.id) } /// An iterator over all the currently connected peers. pub struct Peers { - service: ServiceReader, + service: ServiceReader, } -/// Errors returned by `Peers::next`. -error_def! NextPeerError { - InvalidResponse - => "The response from the gnunet-peerinfo service was incoherent", - UnexpectedMessageType { ty: u16 } - => "The peerinfo service sent an unexpected response message type" ("Message type {} was not expected", ty), - Io { #[from] cause: io::Error } - => "There was an I/O error communicating with the peerinfo service" ("Specifically: {}", cause), - ReadMessage { #[from] cause: ReadMessageError } - => "Failed to receive the response from the peerinfo service" ("Reason: {}", cause), - Disconnected - => "The service disconnected unexpectedly" +quick_error! { + /// Errors returned by `Peers::next`. + #[derive(Debug)] + pub enum NextPeerError { + InvalidResponse { + display("The response from the gnunet-peerinfo service was incoherent") + } + + UnexpectedMessageType { ty: u16 } { + display("The peerinfo service sent an unexpected response message type. Message type {} was not expected", ty) + } + + Io { cause: io::Error } { + cause(cause) + display("There was an I/O error communicating with the peerinfo service. Specifically: {}", cause) + } + + ReadMessage { cause: ReadMessageError } { + display("Failed to receive the response from the peerinfo service. Reason: {}", cause) + cause(cause) + from(cause: ReadMessageError) -> { cause: cause } + } + + Disconnected { + display("The service disconnected unexpectedly") + } + } } byteorder_error_chain! {NextPeerError} impl Iterator for Peers { - type Item = Result<(PeerIdentity, Option), NextPeerError>; - - fn next(&mut self) -> Option), NextPeerError>> { - let (tpe, mut mr) = match self.service.read_message() { - Err(e) => return Some(Err(NextPeerError::ReadMessage { cause: e })), - Ok(x) => x, - }; - match tpe { - ll::GNUNET_MESSAGE_TYPE_PEERINFO_INFO => match mr.read_u32::() { - Err(e) => match e { - byteorder::Error::UnexpectedEOF => Some(Err(NextPeerError::Disconnected)), - byteorder::Error::Io(e) => Some(Err(NextPeerError::Io { cause: e })), - }, - Ok(x) => match x == 0 { - false => Some(Err(NextPeerError::InvalidResponse)), - true => match PeerIdentity::deserialize(&mut mr) { - Err(e) => Some(Err(NextPeerError::Io { cause: e })), - Ok(pi) => { - Some(Ok((pi, None))) - /* + type Item = Result<(PeerIdentity, Option), NextPeerError>; + + fn next(&mut self) -> Option), NextPeerError>> { + let (tpe, mut mr) = match self.service.read_message() { + Err(e) => return Some(Err(NextPeerError::ReadMessage { cause: e })), + Ok(x) => x, + }; + match tpe { + ll::GNUNET_MESSAGE_TYPE_PEERINFO_INFO => match mr.read_u32::() { + Err(e) => match e.kind() { + ::std::io::ErrorKind::UnexpectedEof => Some(Err(NextPeerError::Disconnected)), + _ => Some(Err(NextPeerError::Io { cause: e })), + }, + Ok(x) => match x == 0 { + false => Some(Err(NextPeerError::InvalidResponse)), + true => match PeerIdentity::deserialize(&mut mr) { + Err(e) => Some(Err(NextPeerError::Io { cause: e })), + Ok(pi) => { + Some(Ok((pi, None))) + /* * when we have hello parsing match mr.eof() { true => Some(Ok(pi, None)), @@ -125,35 +162,36 @@ impl Iterator for Peers { }, } */ + } + }, + }, }, - }, - }, - }, - ll::GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END => None, - x => Some(Err(NextPeerError::UnexpectedMessageType { ty: x })), + ll::GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END => None, + x => Some(Err(NextPeerError::UnexpectedMessageType { ty: x })), + } } - } } impl fmt::Debug for PeerIdentity { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - unsafe { - const LEN: usize = 52usize; - assert!(LEN == (size_of_val(&self.data.public_key.q_y) * 8 + 4) / 5); - let mut enc: [u8; LEN] = uninitialized(); - let res = ll::GNUNET_STRINGS_data_to_string(self.data.public_key.q_y.as_ptr() as *const c_void, - self.data.public_key.q_y.len() as size_t, - enc.as_mut_ptr() as *mut c_char, - enc.len() as size_t); - assert!(!res.is_null()); - fmt::Display::fmt(from_utf8(&enc).unwrap(), f) + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + unsafe { + const LEN: usize = 52usize; + assert!(LEN == (size_of_val(&self.data.public_key.q_y) * 8 + 4) / 5); + let mut enc: [u8; LEN] = uninitialized(); + let res = ll::GNUNET_STRINGS_data_to_string( + self.data.public_key.q_y.as_ptr() as *const c_void, + self.data.public_key.q_y.len() as size_t, + enc.as_mut_ptr() as *mut c_char, + enc.len() as size_t, + ); + assert!(!res.is_null()); + fmt::Display::fmt(from_utf8(&enc).unwrap(), f) + } } - } } impl fmt::Display for PeerIdentity { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(self, f) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(self, f) + } } - diff --git a/src/service/mod.rs b/src/service/mod.rs index d2b8ed5..7273d6c 100644 --- a/src/service/mod.rs +++ b/src/service/mod.rs @@ -1,11 +1,11 @@ //! Module for communicating with GNUnet services. Implements the parts of the GNUnet IPC protocols //! that are common to all services. -use std::io::{self, Write, Cursor}; -use std::thread; +use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use std::io::{self, Cursor, Write}; use std::net::Shutdown; +use std::thread; use unix_socket::UnixStream; -use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use configuration::{self, Cfg}; use util::io::ReadUtil; @@ -35,21 +35,30 @@ pub struct ServiceWriter { /// tell the callback loop what action to take next. #[derive(Copy, Clone)] pub enum ProcessMessageResult { - /// Continue talking to the service and passing received messages to the callback. - Continue, - /// Attempt to reconnect to the service. - Reconnect, - /// Exit the callback loop, shutting down it's thread. - Shutdown, -} + /// Continue talking to the service and passing received messages to the callback. + Continue, + /// Attempt to reconnect to the service. + Reconnect, + /// Exit the callback loop, shutting down it's thread. + Shutdown, +} + +quick_error! { + /// Error that can be generated when attempting to connect to a service. + #[derive(Debug)] + pub enum ConnectError { + NotConfigured { cause: configuration::CfgGetFilenameError }{ + cause(cause) + from(cause: configuration::CfgGetFilenameError) -> { cause: cause } + display("The configuration does not describe how to connect to the service. Config does not contain an entry for UNIXPATH in the service's section: {}", cause) + } -/// Error that can be generated when attempting to connect to a service. -error_def! ConnectError { - NotConfigured { #[from] cause: configuration::CfgGetFilenameError } - => "The configuration does not describe how to connect to the service" - ("Config does not contain an entry for UNIXPATH in the service's section: {}", cause), - Io { #[from] cause: io::Error } - => "There was an I/O error communicating with the service" ("Specifically {}", cause), + Io { cause: io::Error } { + from(cause: io::Error) -> { cause: cause } + cause(cause) + display("There was an I/O error communicating with the service.Specifically {}", cause) + } + } } /// Attempt to connect to the local GNUnet service named `name`. @@ -57,117 +66,130 @@ error_def! ConnectError { /// eg. `connect(cfg, "arm")` will attempt to connect to the locally-running `gnunet-arm` service /// using the congfiguration details (eg. socket address, port etc.) in `cfg`. pub fn connect(cfg: &Cfg, name: &str) -> Result<(ServiceReader, ServiceWriter), ConnectError> { - let unixpath = try!(cfg.get_filename(name, "UNIXPATH")); + let unixpath = try!(cfg.get_filename(name, "UNIXPATH")); - // TODO: use UnixStream::split() instead when it exists - let path = unixpath.into_os_string().into_string().unwrap(); - let in_stream = try!(UnixStream::connect(path)); - let out_stream = try!(in_stream.try_clone()); + // TODO: use UnixStream::split() instead when it exists + let path = unixpath.into_os_string().into_string().unwrap(); + let in_stream = try!(UnixStream::connect(path)); + let out_stream = try!(in_stream.try_clone()); - let r = ServiceReader { - connection: in_stream, - }; - let w = ServiceWriter { - connection: out_stream, - }; - Ok((r, w)) + let r = ServiceReader { + connection: in_stream, + }; + let w = ServiceWriter { + connection: out_stream, + }; + Ok((r, w)) } -/// Error that can be generated when attempting to receive data from a service. -error_def! ReadMessageError { - Io { #[from] cause: io::Error } => "There was an I/O error communicating with the service" ("Specifically {}", cause), - ShortMessage { len: u16 } => "The message received from the service was too short" ("Length was {} bytes.", len), - Disconnected => "The service disconnected unexpectedly", +quick_error! { + /// Error that can be generated when attempting to receive data from a service. + #[derive(Debug)] + pub enum ReadMessageError { + Io { cause: io::Error } { + display("There was an I/O error communicating with the service. Specifically {}", cause) + cause(cause) + } + + ShortMessage { len: u16 } { + display("The message received from the service was too short. Length was {} bytes.", len) + } + + Disconnected { + display("The service disconnected unexpectedly") + } + } } byteorder_error_chain! {ReadMessageError} impl ServiceReader { - pub fn spawn_callback_loop(mut self, mut cb: F) -> Result - where F: FnMut(u16, Cursor>) -> ProcessMessageResult, - F: Send, - F: 'static - { - let reader = try!(self.connection.try_clone()); - let callback_loop = thread::spawn(move || -> ServiceReader { - //TODO: implement reconnection (currently fails) - loop { - let (tpe, mr) = match self.read_message() { - Ok(x) => x, - Err(_) => return self, // TODO: reconnect - }; - match cb(tpe, mr) { - ProcessMessageResult::Continue => (), - ProcessMessageResult::Reconnect => return self, //TODO: auto reconnect - ProcessMessageResult::Shutdown => return self, - }; - } - }); - Ok(ServiceReadLoop { - reader: reader, - _callback_loop: callback_loop, - }) - } + pub fn spawn_callback_loop(mut self, mut cb: F) -> Result + where + F: FnMut(u16, Cursor>) -> ProcessMessageResult, + F: Send, + F: 'static, + { + let reader = try!(self.connection.try_clone()); + let callback_loop = thread::spawn(move || -> ServiceReader { + //TODO: implement reconnection (currently fails) + loop { + let (tpe, mr) = match self.read_message() { + Ok(x) => x, + Err(_) => return self, // TODO: reconnect + }; + match cb(tpe, mr) { + ProcessMessageResult::Continue => (), + ProcessMessageResult::Reconnect => return self, //TODO: auto reconnect + ProcessMessageResult::Shutdown => return self, + }; + } + }); + Ok(ServiceReadLoop { + reader: reader, + _callback_loop: callback_loop, + }) + } - pub fn read_message(&mut self) -> Result<(u16, Cursor>), ReadMessageError> { - let len = try!(self.connection.read_u16::()); - if len < 4 { - return Err(ReadMessageError::ShortMessage { len: len }); - }; - let v = try!(self.connection.read_exact_alloc(len as usize - 2)); - let mut mr = Cursor::new(v); - let tpe = try!(mr.read_u16::()); - Ok((tpe, mr)) - } + pub fn read_message(&mut self) -> Result<(u16, Cursor>), ReadMessageError> { + let len = try!(self.connection.read_u16::()); + if len < 4 { + return Err(ReadMessageError::ShortMessage { len: len }); + }; + let v = try!(self.connection.read_exact_alloc(len as usize - 2)); + let mut mr = Cursor::new(v); + let tpe = try!(mr.read_u16::()); + Ok((tpe, mr)) + } } impl ServiceWriter { - pub fn write_message<'a>(&'a mut self, len: u16, tpe: u16) -> MessageWriter<'a> { - assert!(len >= 4); - let v = Vec::with_capacity(len as usize); - let mut mw = Cursor::new(v); - mw.write_u16::(len).unwrap(); - mw.write_u16::(tpe).unwrap(); - MessageWriter { - service_writer: self, - mw: mw, + pub fn write_message<'a>(&'a mut self, len: u16, tpe: u16) -> MessageWriter<'a> { + assert!(len >= 4); + let v = Vec::with_capacity(len as usize); + let mut mw = Cursor::new(v); + mw.write_u16::(len).unwrap(); + mw.write_u16::(tpe).unwrap(); + MessageWriter { + service_writer: self, + mw: mw, + } } - } } /// Used to form messsages before sending them to the GNUnet service. pub struct MessageWriter<'a> { - service_writer: &'a mut ServiceWriter, - mw: Cursor>, + service_writer: &'a mut ServiceWriter, + mw: Cursor>, } impl<'a> MessageWriter<'a> { - /// Finish the message and transmit it to the service. - pub fn send(self) -> Result<(), io::Error> { - let v = self.mw.into_inner(); - assert!(v.len() == v.capacity()); - self.service_writer.connection.write_all(&v[..]) - } + /// Finish the message and transmit it to the service. + pub fn send(self) -> Result<(), io::Error> { + let v = self.mw.into_inner(); + assert!(v.len() == v.capacity()); + self.service_writer.connection.write_all(&v[..]) + } } impl<'a> Write for MessageWriter<'a> { - fn write(&mut self, buf: &[u8]) -> Result { - self.mw.write(buf) - } + fn write(&mut self, buf: &[u8]) -> Result { + self.mw.write(buf) + } - fn flush(&mut self) -> Result<(), io::Error> { - Ok(()) - } + fn flush(&mut self) -> Result<(), io::Error> { + Ok(()) + } } /// A thread that loops, recieving messages from the service and passing them to a callback. /// Created with `ServiceReader::spawn_callback_loop`. pub struct ServiceReadLoop { - reader: UnixStream, - _callback_loop: thread::JoinHandle, + reader: UnixStream, + _callback_loop: thread::JoinHandle, } impl ServiceReadLoop { - /* + /* fn join(mut self) -> ServiceReader { let _ = self.reader.shutdown(Shutdown::Read); self.callback_loop.join().unwrap() @@ -176,10 +198,10 @@ impl ServiceReadLoop { } impl Drop for ServiceReadLoop { - fn drop(&mut self) { - let _ = self.reader.shutdown(Shutdown::Read); - //let _ = self.callback_loop.join(); - } + fn drop(&mut self) { + let _ = self.reader.shutdown(Shutdown::Read); + //let _ = self.callback_loop.join(); + } } /* @@ -192,4 +214,3 @@ impl Drop for ServiceReader { } } */ - diff --git a/src/transport.rs b/src/transport.rs index b549d81..d756f7b 100644 --- a/src/transport.rs +++ b/src/transport.rs @@ -1,57 +1,76 @@ +use byteorder::{BigEndian, WriteBytesExt}; use std::io::{self, Write}; -use byteorder::{WriteBytesExt, BigEndian}; -use service::{self, ReadMessageError}; use hello::HelloDeserializeError; -use Hello; -use Cfg; use ll; +use service::{self, ReadMessageError}; +use Cfg; +use Hello; pub struct TransportService { - //service_reader: ServiceReader, - //service_writer: ServiceWriter, - our_hello: Hello, + //service_reader: ServiceReader, + //service_writer: ServiceWriter, + our_hello: Hello, } -error_def! TransportServiceInitError { - NonHelloMessage { ty: u16 } - => "Expected a HELLO message from the service but received a different message type" ("Received message type {} instead.", ty), - Io { #[from] cause: io::Error } - => "There was an I/O error communicating with the service" ("Error: {}", cause), - ReadMessage { #[from] cause: ReadMessageError } - => "Failed to receive a message from the service" ("Reason: {}", cause), - Connect { #[from] cause: service::ConnectError } - => "Failed to connect to the transport service" ("Reason: {}", cause), - HelloDeserialize { #[from] cause: HelloDeserializeError } - => "Failed to serialize the hello message from the service" ("Reason {}", cause), +quick_error! { + #[derive(Debug)] + pub enum TransportServiceInitError { + NonHelloMessage { ty: u16 } { + display("Expected a HELLO message from the service but received a different message type. Received message type {} instead.", ty) + } + + Io { cause: io::Error } { + cause(cause) + from(cause: io::Error) -> { cause: cause } + display("There was an I/O error communicating with the service. Error: {}", cause) + } + + ReadMessage { cause: ReadMessageError } { + display("Failed to receive a message from the service. Reason: {}", cause) + cause(cause) + from(cause: ReadMessageError) -> { cause: cause } + } + + Connect { cause: service::ConnectError } { + cause(cause) + from(cause: service::ConnectError) -> { cause: cause } + display("Failed to connect to the transport service. Reason: {}", cause) + } + + HelloDeserialize { cause: HelloDeserializeError } { + display("Failed to serialize the hello message from the service. Reason {}", cause) + cause(cause) + from(cause: HelloDeserializeError) -> { cause: cause } + } + } } impl TransportService { - pub fn init(cfg: &Cfg) -> Result { - let (mut sr, mut sw) = try!(service::connect(cfg, "transport")); - let msg_length = 2 + 4 + 32; - { - let mut mw = sw.write_message(msg_length, ll::GNUNET_MESSAGE_TYPE_TRANSPORT_START); - mw.write_u32::(0).unwrap(); - let null_peer_id = [0; 32]; - mw.write(&null_peer_id[..]).unwrap(); - try!(mw.send()); - }; - let (ty, mut mr) = try!(sr.read_message()); - if ty != ll::GNUNET_MESSAGE_TYPE_HELLO { - return Err(TransportServiceInitError::NonHelloMessage { ty: ty }); - }; - let hello = try!(Hello::deserialize(&mut mr)); - Ok(TransportService { - //service_reader: sr, - //service_writer: sw, - our_hello: hello, - }) - } + pub fn init(cfg: &Cfg) -> Result { + let (mut sr, mut sw) = try!(service::connect(cfg, "transport")); + let msg_length = 2 + 4 + 32; + { + let mut mw = sw.write_message(msg_length, ll::GNUNET_MESSAGE_TYPE_TRANSPORT_START); + mw.write_u32::(0).unwrap(); + let null_peer_id = [0; 32]; + mw.write(&null_peer_id[..]).unwrap(); + try!(mw.send()); + }; + let (ty, mut mr) = try!(sr.read_message()); + if ty != ll::GNUNET_MESSAGE_TYPE_HELLO { + return Err(TransportServiceInitError::NonHelloMessage { ty: ty }); + }; + let hello = try!(Hello::deserialize(&mut mr)); + Ok(TransportService { + //service_reader: sr, + //service_writer: sw, + our_hello: hello, + }) + } } pub fn self_hello(cfg: &Cfg) -> Result { - let ts = try!(TransportService::init(cfg)); - Ok(ts.our_hello) + let ts = try!(TransportService::init(cfg)); + Ok(ts.our_hello) } - diff --git a/src/util/c_strings.rs b/src/util/c_strings.rs index 7c5ec54..4fb262e 100644 --- a/src/util/c_strings.rs +++ b/src/util/c_strings.rs @@ -1,72 +1,98 @@ //use std::path::Path; //use std::ffi::{CString, NulError}; +use byteorder::ReadBytesExt; use std::io::{self, Read}; use std::string::FromUtf8Error; -use byteorder::ReadBytesExt; -/// Error generated when reading a C-style NUL-terminated string from a service -error_def! ReadCStringError { - Io { #[from] cause: io::Error } - => "There was an I/O error communicating with the service" ("Specifically: {}", cause), - FromUtf8 { #[from] cause: FromUtf8Error } - => "The string contained invalid utf-8" ("Utf8-error: {}", cause), - Disconnected - => "The remote service disconnected unexpectedly", +quick_error! { + /// Error generated when reading a C-style NUL-terminated string from a service + #[derive(Debug)] + pub enum ReadCStringError { + Io { cause: io::Error } { + display("There was an I/O error communicating with the service. Specifically: {}", cause) + cause(cause) + } + + FromUtf8 { cause: FromUtf8Error } { + cause(cause) + from(cause: FromUtf8Error) -> { cause: cause } + display("The string contained invalid utf-8. Utf8-error: {}", cause) + } + + Disconnected { + display("The remote service disconnected unexpectedly") + } + } } byteorder_error_chain! {ReadCStringError} -/// Error generated when attempting to read a C-style NUL-terminated string of known length from a -/// service. -error_def! ReadCStringWithLenError { - Io { #[from] cause: io::Error } - => "There was an I/O error communicating with the service" ("Specifically: {}", cause), - FromUtf8 { #[from] cause: FromUtf8Error } - => "The string contained invalid utf-8" ("Utf8-error: {}", cause), - Disconnected - => "The remote service disconnected unexpectedly", - InteriorNul { pos: usize } - => "The string contained an interior NUL byte" ("The offending byte is at position {}", pos), - NoTerminator - => "The string was not NUL-terminated", +quick_error! { + /// Error generated when attempting to read a C-style NUL-terminated string of known length from a + /// service. + #[derive(Debug)] + pub enum ReadCStringWithLenError { + Io { cause: io::Error } { + display("There was an I/O error communicating with the service. Specifically: {}", cause) + cause(cause) + } + + FromUtf8 { cause: FromUtf8Error }{ + display("The string contained invalid utf-8. Utf8-error: {}", cause) + cause(cause) + from(cause: FromUtf8Error) -> { cause: cause } + } + + Disconnected { + display("The remote service disconnected unexpectedly") + } + + InteriorNul { pos: usize } { + display("The string contained an interior NUL byte. The offending byte is at position {}", pos) + } + + NoTerminator { + display("The string was not NUL-terminated") + } + } } byteorder_error_chain! {ReadCStringWithLenError} pub trait ReadCString: Read { - fn read_c_string(&mut self) -> Result { - let mut v: Vec = Vec::new(); - loop { - let b = try!(self.read_u8()); - if b == 0u8 { - break; - } - v.push(b); + fn read_c_string(&mut self) -> Result { + let mut v: Vec = Vec::new(); + loop { + let b = try!(self.read_u8()); + if b == 0u8 { + break; + } + v.push(b); + } + match String::from_utf8(v) { + Ok(s) => Ok(s), + Err(e) => Err(ReadCStringError::FromUtf8 { cause: e }), + } } - match String::from_utf8(v) { - Ok(s) => Ok(s), - Err(e) => Err(ReadCStringError::FromUtf8 { cause: e }), - } - } - fn read_c_string_with_len(&mut self, len: usize) -> Result { - let mut v: Vec = Vec::with_capacity(len); - for i in 0..len { - let b = try!(self.read_u8()); - if b == 0u8 { - // must not contain embedded NULs - return Err(ReadCStringWithLenError::InteriorNul { pos: i }); - } - v.push(b); - } - let b = try!(self.read_u8()); - if b != 0u8 { - // must be NUL-terminated - return Err(ReadCStringWithLenError::NoTerminator); + fn read_c_string_with_len(&mut self, len: usize) -> Result { + let mut v: Vec = Vec::with_capacity(len); + for i in 0..len { + let b = try!(self.read_u8()); + if b == 0u8 { + // must not contain embedded NULs + return Err(ReadCStringWithLenError::InteriorNul { pos: i }); + } + v.push(b); + } + let b = try!(self.read_u8()); + if b != 0u8 { + // must be NUL-terminated + return Err(ReadCStringWithLenError::NoTerminator); + } + match String::from_utf8(v) { + Ok(s) => Ok(s), + Err(e) => Err(ReadCStringWithLenError::FromUtf8 { cause: e }), + } } - match String::from_utf8(v) { - Ok(s) => Ok(s), - Err(e) => Err(ReadCStringWithLenError::FromUtf8 { cause: e }), - } - } } impl ReadCString for T where T: Read {} @@ -75,12 +101,20 @@ impl ReadCString for T where T: Read {} * * Currently not used anymore. * +quock_error! { /// A `std::path::Path` could not be converted to a utf-8 CString. -error_def! ToCPathError { - InvalidUnicode - => "The path contains invalid unicode", - InteriorNul { #[from] cause: NulError } - => "The path contains an interior NUL byte" ("Specifically: {}", cause) +#[derive(Debug)] +pub enum ToCPathError { + InvalidUnicode { + display("The path contains invalid unicode") + } + + InteriorNul { cause: NulError } { + display("The path contains an interior NUL byte. Specifically: {}", cause) + cause(cause) + from(cause: NulError) -> { cause: cause } + } +} } pub fn to_c_path(path: &P) -> Result @@ -98,4 +132,3 @@ pub fn to_c_path(path: &P) -> Result Ok(path) } */ - diff --git a/src/util/io.rs b/src/util/io.rs index e06ac50..cc88e1c 100644 --- a/src/util/io.rs +++ b/src/util/io.rs @@ -1,22 +1,20 @@ -use std::mem; +use std::io; use std::io::Read; -use byteorder; +use std::mem; fn uninitialised_vec(len: usize) -> Vec { - let mut buf: Vec = Vec::with_capacity(len); - let ret = unsafe { Vec::from_raw_parts(buf.as_mut_ptr(), len, buf.capacity()) }; - mem::forget(buf); - ret + let mut buf: Vec = Vec::with_capacity(len); + let ret = unsafe { Vec::from_raw_parts(buf.as_mut_ptr(), len, buf.capacity()) }; + mem::forget(buf); + ret } pub trait ReadUtil: Read { - fn read_exact_alloc(&mut self, len: usize) -> Result, byteorder::Error> { - let mut ret = uninitialised_vec(len); - try!(self.read_exact(&mut ret[..])); - Ok(ret) - } -} - -impl ReadUtil for R where R: Read { + fn read_exact_alloc(&mut self, len: usize) -> Result, io::Error> { + let mut ret = uninitialised_vec(len); + try!(self.read_exact(&mut ret[..])); + Ok(ret) + } } +impl ReadUtil for R where R: Read {} diff --git a/src/util/strings.rs b/src/util/strings.rs index e2f462d..027ea27 100644 --- a/src/util/strings.rs +++ b/src/util/strings.rs @@ -1,18 +1,33 @@ use std::num::ParseIntError; use std::str::FromStr; -error_def! ParseQuantityWithUnitsError { - ParseInt { #[from] cause: ParseIntError } - => "Failed to parse a number" ("Specifically: {}", cause), - EmptyString - => "Empty string given as argument", - MissingUnit - => "Missing unit on the final number", - NoSuchUnit { unit: String } - => "Unrecognized unit" ("\"{}\" is not a valid unit", unit), +quick_error! { + #[derive(Debug)] + pub enum ParseQuantityWithUnitsError { + ParseInt { cause: ParseIntError } { + display("Failed to parse a number. Specifically: {}", cause) + cause(cause) + from(cause: ParseIntError) -> { cause: cause } + } + + EmptyString { + display("Empty string given as argument") + } + + MissingUnit { + display("Missing unit on the final number") + } + + NoSuchUnit { unit: String } { + display("Unrecognized unit. \"{}\" is not a valid unit", unit) + } + } } -pub fn parse_quantity_with_units<'a>(s: &'a str, units: &[(&str, u64)]) -> Result { +pub fn parse_quantity_with_units<'a>( + s: &'a str, + units: &[(&str, u64)], +) -> Result { use self::ParseQuantityWithUnitsError::*; if s.trim().is_empty() { @@ -23,21 +38,19 @@ pub fn parse_quantity_with_units<'a>(s: &'a str, units: &[(&str, u64)]) -> Resul let mut iter = s.split(' '); loop { match iter.next() { - None => return Ok(result), + None => return Ok(result), Some(amount_str) => { if amount_str.is_empty() { continue; - } - else { + } else { let amount = try!(u64::from_str(amount_str)); loop { match iter.next() { - None => return Err(MissingUnit), + None => return Err(MissingUnit), Some(unit) => { if unit.is_empty() { continue; - } - else { + } else { let mut found = false; for &(u, multiplier) in units.iter() { if u == unit { @@ -48,9 +61,10 @@ pub fn parse_quantity_with_units<'a>(s: &'a str, units: &[(&str, u64)]) -> Resul } if found { break; - } - else { - return Err(NoSuchUnit { unit: unit.to_string() }); + } else { + return Err(NoSuchUnit { + unit: unit.to_string(), + }); } } } @@ -61,4 +75,3 @@ pub fn parse_quantity_with_units<'a>(s: &'a str, units: &[(&str, u64)]) -> Resul } } } -