summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorErik Larkö <erik@mullvad.net>2017-03-02 21:30:44 +0800
committerErik Larkö <erik@mullvad.net>2017-03-02 21:30:44 +0800
commitf3226b945964b18e5cba9df7f7cc1347229ed9a8 (patch)
tree5712973f9518c2ce28e3f001bc9725473fd1c552
parent459196bb22c11ca2943546743f2e0c02d8ee5e05 (diff)
parentf0a0e90676902d4f04d2e1bd38579ce68dd06491 (diff)
downloadmullvadvpn-f3226b945964b18e5cba9df7f7cc1347229ed9a8.tar.xz
mullvadvpn-f3226b945964b18e5cba9df7f7cc1347229ed9a8.zip
Merge branch 'plugin-communication'
-rw-r--r--Cargo.lock10
-rw-r--r--Cargo.toml5
-rwxr-xr-xbuild-deps/zmq/build.sh2
-rwxr-xr-x[-rw-r--r--]build-deps/zmq/install-build-deps-apt.sh0
-rwxr-xr-x[-rw-r--r--]build-deps/zmq/install-build-deps-osx.sh0
-rw-r--r--talpid_ipc/Cargo.toml14
-rw-r--r--talpid_ipc/src/lib.rs31
-rw-r--r--talpid_ipc/src/nop_ipc.rs11
-rw-r--r--talpid_ipc/src/zmq_ipc.rs47
-rw-r--r--talpid_ipc/tests/zmq_integration_tests.rs72
10 files changed, 190 insertions, 2 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 302fcf47dc..be0b367869 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -165,6 +165,16 @@ dependencies = [
"assert_matches 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"clonablechild 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"error-chain 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "talpid_ipc 0.1.0",
+ "zmq 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "talpid_ipc"
+version = "0.1.0"
+dependencies = [
+ "assert_matches 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "error-chain 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"zmq 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
diff --git a/Cargo.toml b/Cargo.toml
index c6f0ad6ee7..7ac25c9748 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -8,6 +8,9 @@ description = "Core backend functionality of the Mullvad VPN client"
clonablechild = "0.1"
error-chain = "0.8"
+[dependencies.talpid_ipc]
+path = "talpid_ipc"
+
[target.'cfg(not(windows))'.dependencies]
zmq = "0.8"
@@ -15,4 +18,4 @@ zmq = "0.8"
assert_matches = "1.0"
[workspace]
-members = ["talpid_openvpn_plugin", "talpid_cli"]
+members = ["talpid_openvpn_plugin", "talpid_cli", "talpid_ipc"]
diff --git a/build-deps/zmq/build.sh b/build-deps/zmq/build.sh
index dc8cc49a84..363d0df446 100755
--- a/build-deps/zmq/build.sh
+++ b/build-deps/zmq/build.sh
@@ -26,7 +26,7 @@ fi
echo "If this fails, make sure that you have installed the packages needed to \
-build zmq, for ubuntu and OS X they can be found in\
+build zmq, for ubuntu and OS X they can be found in \
$WD/install-build-deps-{apt|osx}.sh"
echo ""
diff --git a/build-deps/zmq/install-build-deps-apt.sh b/build-deps/zmq/install-build-deps-apt.sh
index 6a762f603c..6a762f603c 100644..100755
--- a/build-deps/zmq/install-build-deps-apt.sh
+++ b/build-deps/zmq/install-build-deps-apt.sh
diff --git a/build-deps/zmq/install-build-deps-osx.sh b/build-deps/zmq/install-build-deps-osx.sh
index c52dc026e8..c52dc026e8 100644..100755
--- a/build-deps/zmq/install-build-deps-osx.sh
+++ b/build-deps/zmq/install-build-deps-osx.sh
diff --git a/talpid_ipc/Cargo.toml b/talpid_ipc/Cargo.toml
new file mode 100644
index 0000000000..6bacc0f9a7
--- /dev/null
+++ b/talpid_ipc/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "talpid_ipc"
+version = "0.1.0"
+authors = ["Linus Färnstrand <linus@mullvad.net>", "Erik Larkö <erik@mullvad.net>"]
+description = "IPC client and server for talpid"
+
+[dependencies]
+error-chain = "0.8"
+
+[target.'cfg(not(windows))'.dependencies]
+zmq = "0.8"
+
+[dev-dependencies]
+assert_matches = "1.0"
diff --git a/talpid_ipc/src/lib.rs b/talpid_ipc/src/lib.rs
new file mode 100644
index 0000000000..d9b8c53aed
--- /dev/null
+++ b/talpid_ipc/src/lib.rs
@@ -0,0 +1,31 @@
+#[macro_use]
+extern crate error_chain;
+
+#[cfg(windows)]
+#[path = "nop_ipc.rs"]
+mod ipc_impl;
+
+#[cfg(not(windows))]
+#[path = "zmq_ipc.rs"]
+mod ipc_impl;
+
+pub use self::ipc_impl::*;
+
+/// The type signature for functions accepting messages from the server.
+/// If the server fails in delivering the message for any reason it will
+/// put the cause in the Err part of the `Result`.
+pub type OnMessage<MessageType> = FnMut(Result<MessageType>) + Send + 'static;
+
+/// An Id created by the Ipc server that the client can use to connect to it
+type IpcServerId = String;
+
+error_chain!{
+ errors {
+ ReadFailure {
+ description("Could not read IPC message")
+ }
+ CouldNotStartServer {
+ description("Failed to start the IPC server")
+ }
+ }
+}
diff --git a/talpid_ipc/src/nop_ipc.rs b/talpid_ipc/src/nop_ipc.rs
new file mode 100644
index 0000000000..6db87f3b54
--- /dev/null
+++ b/talpid_ipc/src/nop_ipc.rs
@@ -0,0 +1,11 @@
+use super::{OnMessage, ErrorKind, Result, IpcServerId};
+
+/// This implementation only exists because we cannot get ZeroMQ to work on
+/// Windows. This is not a valid IPC implementation and us using
+/// it on Windows will result in a non-functioning client.
+///
+/// We plan on trying with ZMQ again in the future.
+/// Erik, 2017-02-09
+fn start_new_server(_on_message: Box<OnMessage<Vec<u8>>>) -> Result<IpcServerId> {
+ Err(ErrorKind::CouldNotStartServer.into())
+}
diff --git a/talpid_ipc/src/zmq_ipc.rs b/talpid_ipc/src/zmq_ipc.rs
new file mode 100644
index 0000000000..0cbb4bcfd0
--- /dev/null
+++ b/talpid_ipc/src/zmq_ipc.rs
@@ -0,0 +1,47 @@
+extern crate zmq;
+
+use super::{OnMessage, ErrorKind, Result, ResultExt, IpcServerId};
+use std::thread;
+
+/// Starts listening to incoming IPC connections on a random port.
+/// Messages are sent to the `on_message` callback. If anything went wrong
+/// when reading the message, the message will be an `Err`.
+/// NOTE that this does not apply to errors regarding whether the server
+/// could start or not, those are returned directly by this function.
+///
+/// This function is non-blocking and thus spawns a thread where it
+/// listens to messages.
+///
+/// The value returned from this function should be used by the clients to
+/// the server.
+pub fn start_new_server(on_message: Box<OnMessage<Vec<u8>>>) -> Result<IpcServerId> {
+
+ for port in 5000..5010 {
+ let connection_string = format!("tcp://127.0.0.1:{}", port);
+ if let Ok(socket) = start_zmq_server(&connection_string) {
+ let _ = start_receive_loop(socket, on_message);
+ return Ok(connection_string);
+ }
+ }
+
+ return Err(ErrorKind::CouldNotStartServer.into());
+}
+
+fn start_zmq_server(connection_string: &str) -> zmq::Result<zmq::Socket> {
+ let ctx = zmq::Context::new();
+
+ let socket = ctx.socket(zmq::PULL)?;
+ socket.bind(connection_string)?;
+
+ Ok(socket)
+}
+
+fn start_receive_loop(socket: zmq::Socket,
+ mut on_message: Box<OnMessage<Vec<u8>>>)
+ -> thread::JoinHandle<()> {
+
+ thread::spawn(move || loop {
+ let read_res = socket.recv_bytes(0).chain_err(|| ErrorKind::ReadFailure);
+ on_message(read_res);
+ })
+}
diff --git a/talpid_ipc/tests/zmq_integration_tests.rs b/talpid_ipc/tests/zmq_integration_tests.rs
new file mode 100644
index 0000000000..2b976c877d
--- /dev/null
+++ b/talpid_ipc/tests/zmq_integration_tests.rs
@@ -0,0 +1,72 @@
+#[macro_use]
+extern crate error_chain;
+
+#[macro_use]
+extern crate assert_matches;
+
+#[cfg(all(test, not(windows)))]
+mod zmq_integration_tests {
+ extern crate talpid_ipc;
+ extern crate zmq;
+
+
+ use self::talpid_ipc::Result;
+ use std::sync::mpsc::{self, Receiver};
+ use std::time::Duration;
+
+ const A_VALID_MESSAGE: [u8; 1] = [1];
+
+ #[test]
+ fn can_connect_to_server_with_the_returned_id() {
+ let connection_string = talpid_ipc::start_new_server(Box::new(|_| {}))
+ .expect("Unable to start server");
+
+ let connection_res = connect_to_server(&connection_string);
+ assert!(connection_res.is_ok(),
+ "Unable to connect to the server with the given connection string");
+ }
+
+ #[test]
+ fn publishes_incoming_messages_to_channel() {
+ let new_messages_rx = connect_and_send(&A_VALID_MESSAGE);
+
+ let message = new_messages_rx.recv_timeout(Duration::from_millis(1000))
+ .expect("Did not receive a message");
+ assert_matches!(message, Ok(TestMessage::Hello));
+ }
+
+ fn connect_and_send(message: &[u8]) -> Receiver<Result<TestMessage>> {
+ let (tx, rx) = mpsc::channel();
+
+ let connection_string = talpid_ipc::start_new_server(Box::new(move |message| {
+ let _ = tx.send(message.and_then(parse_to_test_enum));
+ })).expect("Could not start the server");
+
+ let socket = connect_to_server(&connection_string)
+ .expect("Could not connect to the server");
+ socket.send(message, 0).expect("Could not send message");
+
+ rx
+ }
+
+ fn connect_to_server(connection_string: &str) -> zmq::Result<zmq::Socket> {
+ let ctx = zmq::Context::new();
+
+ let socket = ctx.socket(zmq::PUSH)?;
+ socket.connect(connection_string)?;
+ Ok(socket)
+ }
+
+ fn parse_to_test_enum(message_as_bytes: Vec<u8>) -> Result<TestMessage> {
+ if message_as_bytes == A_VALID_MESSAGE {
+ Ok(TestMessage::Hello)
+ } else {
+ Err(format!("Invalid message: {:?}", message_as_bytes).into())
+ }
+ }
+
+ #[derive(Debug, PartialEq)]
+ pub enum TestMessage {
+ Hello,
+ }
+}