diff options
| author | Erik Larkö <erik@mullvad.net> | 2017-03-02 21:30:44 +0800 |
|---|---|---|
| committer | Erik Larkö <erik@mullvad.net> | 2017-03-02 21:30:44 +0800 |
| commit | f3226b945964b18e5cba9df7f7cc1347229ed9a8 (patch) | |
| tree | 5712973f9518c2ce28e3f001bc9725473fd1c552 | |
| parent | 459196bb22c11ca2943546743f2e0c02d8ee5e05 (diff) | |
| parent | f0a0e90676902d4f04d2e1bd38579ce68dd06491 (diff) | |
| download | mullvadvpn-f3226b945964b18e5cba9df7f7cc1347229ed9a8.tar.xz mullvadvpn-f3226b945964b18e5cba9df7f7cc1347229ed9a8.zip | |
Merge branch 'plugin-communication'
| -rw-r--r-- | Cargo.lock | 10 | ||||
| -rw-r--r-- | Cargo.toml | 5 | ||||
| -rwxr-xr-x | build-deps/zmq/build.sh | 2 | ||||
| -rwxr-xr-x[-rw-r--r--] | build-deps/zmq/install-build-deps-apt.sh | 0 | ||||
| -rwxr-xr-x[-rw-r--r--] | build-deps/zmq/install-build-deps-osx.sh | 0 | ||||
| -rw-r--r-- | talpid_ipc/Cargo.toml | 14 | ||||
| -rw-r--r-- | talpid_ipc/src/lib.rs | 31 | ||||
| -rw-r--r-- | talpid_ipc/src/nop_ipc.rs | 11 | ||||
| -rw-r--r-- | talpid_ipc/src/zmq_ipc.rs | 47 | ||||
| -rw-r--r-- | talpid_ipc/tests/zmq_integration_tests.rs | 72 |
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, + } +} |
