summaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorLinus Färnstrand <linus@mullvad.net>2016-12-07 14:32:18 +0100
committerLinus Färnstrand <linus@mullvad.net>2016-12-07 14:32:18 +0100
commit03d3693b6b33990b583468afcd9568678ab15268 (patch)
treeaef3464d1db8c9f81841112617acf9104cdf30eb /src
parent876441779e8bdb64abe7822969a822db81f5c7ac (diff)
parent6d8e98afb08d067b98616d5c7f9ebe3570fdc838 (diff)
downloadmullvadvpn-03d3693b6b33990b583468afcd9568678ab15268.tar.xz
mullvadvpn-03d3693b6b33990b583468afcd9568678ab15268.zip
Merge branch 'add-convenience'
Diffstat (limited to 'src')
-rw-r--r--src/lib.rs2
-rw-r--r--src/net.rs119
-rw-r--r--src/process.rs26
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;