diff options
| author | Linus Färnstrand <linus@mullvad.net> | 2016-12-07 14:32:18 +0100 |
|---|---|---|
| committer | Linus Färnstrand <linus@mullvad.net> | 2016-12-07 14:32:18 +0100 |
| commit | 03d3693b6b33990b583468afcd9568678ab15268 (patch) | |
| tree | aef3464d1db8c9f81841112617acf9104cdf30eb /src | |
| parent | 876441779e8bdb64abe7822969a822db81f5c7ac (diff) | |
| parent | 6d8e98afb08d067b98616d5c7f9ebe3570fdc838 (diff) | |
| download | mullvadvpn-03d3693b6b33990b583468afcd9568678ab15268.tar.xz mullvadvpn-03d3693b6b33990b583468afcd9568678ab15268.zip | |
Merge branch 'add-convenience'
Diffstat (limited to 'src')
| -rw-r--r-- | src/lib.rs | 2 | ||||
| -rw-r--r-- | src/net.rs | 119 | ||||
| -rw-r--r-- | src/process.rs | 26 |
3 files changed, 147 insertions, 0 deletions
diff --git a/src/lib.rs b/src/lib.rs index c65c221aec..7683c6dc3b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,8 @@ //! The core components of the talpidaemon VPN client. +extern crate regex; + /// Working with processes. pub mod process; diff --git a/src/net.rs b/src/net.rs index 0906426f7b..250720782e 100644 --- a/src/net.rs +++ b/src/net.rs @@ -1,3 +1,10 @@ +use regex::Regex; + +use std::error::Error; +use std::fmt; +use std::net::SocketAddr; +use std::str::FromStr; + /// Representation of a TCP or UDP endpoint. The host is represented as a String since it can be /// both a hostname/domain as well as an IP. #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -26,9 +33,62 @@ impl RemoteAddr { } } +impl From<SocketAddr> for RemoteAddr { + fn from(socket_addr: SocketAddr) -> Self { + RemoteAddr { + address: socket_addr.ip().to_string(), + port: socket_addr.port(), + } + } +} + +impl FromStr for RemoteAddr { + type Err = AddrParseError; + fn from_str(s: &str) -> Result<Self, Self::Err> { + let (address, port_str) = split_remote_addr_string(s).map_err(|_| AddrParseError(()))?; + let port = u16::from_str(port_str).map_err(|_| AddrParseError(()))?; + Ok(RemoteAddr { + address: address.to_owned(), + port: port, + }) + } +} + +fn split_remote_addr_string(s: &str) -> Result<(&str, &str), ()> { + let with_brackets = Regex::new(r"^\[([^\]]+)\]:([0-9]+)$").unwrap(); + let without_brackets = Regex::new(r"^([^:]+):([0-9]+)$").unwrap(); + let captures = with_brackets.captures(s).or(without_brackets.captures(s)); + captures.map(|cs| (cs.at(1).unwrap(), cs.at(2).unwrap())).ok_or(()) +} + +impl fmt::Display for RemoteAddr { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "{}:{}", self.address, self.port) + } +} + +/// Representation of the errors that can happen when parsing a string into a `RemoteAddr`. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct AddrParseError(()); + +impl fmt::Display for AddrParseError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str(self.description()) + } +} + +impl Error for AddrParseError { + fn description(&self) -> &str { + "Invalid remote address format" + } +} + #[cfg(test)] mod tests { + use std::net::SocketAddr; + use std::str::FromStr; + use super::*; #[test] @@ -37,4 +97,63 @@ mod tests { assert_eq!("a_domain", remote_addr.address()); assert_eq!(543, remote_addr.port()); } + + #[test] + fn remote_addr_from_socket_addr() { + let socket_addr = SocketAddr::from_str("10.0.1.1:76").unwrap(); + let remote_addr: RemoteAddr = socket_addr.into(); + assert_eq!("10.0.1.1", remote_addr.address()); + assert_eq!(76, remote_addr.port()); + } + + #[test] + fn remote_addr_from_str() { + let remote_addr = RemoteAddr::from_str("example.com:3333").unwrap(); + assert_eq!("example.com", remote_addr.address()); + assert_eq!(3333, remote_addr.port()); + } + + #[test] + fn remote_addr_from_ipv6_str_without_brackets() { + assert!(RemoteAddr::from_str("fe80::1:1337").is_err()); + } + + #[test] + fn remote_addr_from_ipv6_str_with_brackets() { + let remote_addr = RemoteAddr::from_str("[fe80::1]:1337").unwrap(); + assert_eq!("fe80::1", remote_addr.address()); + assert_eq!(1337, remote_addr.port()); + } + + #[test] + fn remote_addr_from_ipv6_str_without_port() { + assert!(RemoteAddr::from_str("fe80::1").is_err()); + } + + #[test] + fn remote_addr_from_str_no_colon() { + assert!(RemoteAddr::from_str("example.com").is_err()); + } + + #[test] + fn remote_addr_from_str_invalid_port_large() { + assert!(RemoteAddr::from_str("example.com:99999").is_err()); + } + + #[test] + fn remote_addr_from_str_empty_address() { + assert!(RemoteAddr::from_str(":100").is_err()); + } + + #[test] + fn remote_addr_from_str_empty_port() { + assert!(RemoteAddr::from_str("example.com:").is_err()); + } + + #[test] + fn remote_addr_to_string() { + let formatted_remote = "10.98.150.255:1337"; + let remote_addr = RemoteAddr::from_str(formatted_remote).unwrap(); + assert_eq!(formatted_remote, remote_addr.to_string()); + } } diff --git a/src/process.rs b/src/process.rs index eb372de04e..b350517b2a 100644 --- a/src/process.rs +++ b/src/process.rs @@ -1,6 +1,7 @@ use net::RemoteAddr; use std::ffi::{OsString, OsStr}; +use std::fmt; use std::io; use std::path::{Path, PathBuf}; use std::process::{Command, Child, Stdio}; @@ -69,6 +70,31 @@ impl OpenVpnBuilder { } } +impl fmt::Display for OpenVpnBuilder { + /// Format the program and arguments of an `OpenVpnBuilder` for display. Any non-utf8 data is + /// lossily converted using the utf8 replacement character. + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str(&self.openvpn_bin.to_string_lossy())?; + for arg in self.get_arguments().iter().map(|arg| arg.to_string_lossy()) { + write_argument(fmt, &arg)?; + } + Ok(()) + } +} + +fn write_argument(fmt: &mut fmt::Formatter, arg: &str) -> fmt::Result { + fmt.write_str(" ")?; + let quote = arg.contains(char::is_whitespace); + if quote { + fmt.write_str("\"")?; + } + fmt.write_str(&arg)?; + if quote { + fmt.write_str("\"")?; + } + Ok(()) +} + #[cfg(test)] mod tests { use net::RemoteAddr; |
