summaryrefslogtreecommitdiffhomepage
path: root/mullvad-rpc/src
diff options
context:
space:
mode:
authorJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2018-04-16 13:22:49 -0300
committerJanito Vaqueiro Ferreira Filho <janito@mullvad.net>2018-04-17 07:56:25 -0300
commit4df4c6c48950f6a5652ea0a4e0cd7da33d2aa554 (patch)
tree1e61a84e0fcc12b5b86e72332c479e6e8f340433 /mullvad-rpc/src
parent41a5db158de9ade895f85dd252e8cbba37080e30 (diff)
downloadmullvadvpn-4df4c6c48950f6a5652ea0a4e0cd7da33d2aa554.tar.xz
mullvadvpn-4df4c6c48950f6a5652ea0a4e0cd7da33d2aa554.zip
Configure SNI domain for TLS handshake with master
Diffstat (limited to 'mullvad-rpc/src')
-rw-r--r--mullvad-rpc/src/https_client_with_sni.rs157
-rw-r--r--mullvad-rpc/src/lib.rs9
2 files changed, 164 insertions, 2 deletions
diff --git a/mullvad-rpc/src/https_client_with_sni.rs b/mullvad-rpc/src/https_client_with_sni.rs
new file mode 100644
index 0000000000..c7a082c989
--- /dev/null
+++ b/mullvad-rpc/src/https_client_with_sni.rs
@@ -0,0 +1,157 @@
+extern crate tokio_service;
+extern crate tokio_tls;
+
+use std::fmt;
+use std::io;
+use std::str;
+use std::sync::Arc;
+
+use futures::{Future, Poll};
+use hyper::client::{Client, Connect, HttpConnector};
+use hyper::{Body, Uri};
+use hyper_tls::MaybeHttpsStream;
+use jsonrpc_client_http::ClientCreator;
+pub use native_tls::Error;
+use native_tls::TlsConnector;
+use tokio_core::reactor::Handle;
+
+use self::tokio_service::Service;
+use self::tokio_tls::TlsConnectorExt;
+
+/// Number of threads in the thread pool doing DNS resolutions.
+/// Since DNS is resolved via blocking syscall they must be run on separate threads.
+static DNS_THREADS: usize = 2;
+
+pub struct HttpsClientWithSni {
+ sni_hostname: String,
+}
+
+impl HttpsClientWithSni {
+ pub fn new(sni_hostname: String) -> Self {
+ HttpsClientWithSni { sni_hostname }
+ }
+}
+
+impl ClientCreator for HttpsClientWithSni {
+ type Connect = HttpsConnectorWithSni<HttpConnector>;
+ type Error = Error;
+
+ fn create(&self, handle: &Handle) -> Result<Client<Self::Connect, Body>, Self::Error> {
+ let mut connector = HttpsConnectorWithSni::new(DNS_THREADS, handle)?;
+ connector.set_sni_hostname(Some(self.sni_hostname.clone()));
+ let client = Client::configure().connector(connector).build(handle);
+ Ok(client)
+ }
+}
+
+/// A Connector for the `https` scheme.
+#[derive(Clone)]
+pub struct HttpsConnectorWithSni<T> {
+ sni_hostname: Option<String>,
+ http: T,
+ tls: Arc<TlsConnector>,
+}
+
+impl HttpsConnectorWithSni<HttpConnector> {
+ /// Construct a new HttpsConnectorWithSni.
+ ///
+ /// Takes number of DNS worker threads.
+ ///
+ /// This uses hyper's default `HttpConnector`, and default `TlsConnector`.
+ /// If you wish to use something besides the defaults, use `From::from`.
+ fn new(threads: usize, handle: &Handle) -> Result<Self, Error> {
+ let mut http = HttpConnector::new(threads, handle);
+ http.enforce_http(false);
+ let tls = TlsConnector::builder()?.build()?;
+ Ok(HttpsConnectorWithSni::from((http, tls)))
+ }
+}
+
+impl<T> HttpsConnectorWithSni<T>
+where
+ T: Connect,
+{
+ /// Configure a hostname to use with SNI.
+ ///
+ /// Configures the TLS connection handshake to request a certificate for a given domain,
+ /// instead of the domain obtained from the URI. Use `None` to use the domain from the URI.
+ fn set_sni_hostname(&mut self, hostname: Option<String>) {
+ self.sni_hostname = hostname;
+ }
+}
+
+impl<T> From<(T, TlsConnector)> for HttpsConnectorWithSni<T> {
+ fn from(args: (T, TlsConnector)) -> HttpsConnectorWithSni<T> {
+ HttpsConnectorWithSni {
+ sni_hostname: None,
+ http: args.0,
+ tls: Arc::new(args.1),
+ }
+ }
+}
+
+impl<T> fmt::Debug for HttpsConnectorWithSni<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("HttpsConnectorWithSni").finish()
+ }
+}
+
+impl<T: Connect> Service for HttpsConnectorWithSni<T> {
+ type Request = Uri;
+ type Response = MaybeHttpsStream<T::Output>;
+ type Error = io::Error;
+ type Future = HttpsConnecting<T::Output>;
+
+ fn call(&self, uri: Uri) -> Self::Future {
+ let is_https = uri.scheme() == Some("https");
+ let maybe_host = self.sni_hostname
+ .as_ref()
+ .map(String::as_str)
+ .or_else(|| uri.host())
+ .map(str::to_owned);
+ let host = match maybe_host {
+ Some(host) => host,
+ None => {
+ return HttpsConnecting(Box::new(::futures::future::err(io::Error::new(
+ io::ErrorKind::InvalidInput,
+ "invalid url, missing host",
+ ))));
+ }
+ };
+ let connecting = self.http.connect(uri);
+ let tls = self.tls.clone();
+
+ let fut: BoxedFut<T::Output> = if is_https {
+ let fut = connecting.and_then(move |tcp| {
+ tls.connect_async(&host, tcp)
+ .map(|conn| MaybeHttpsStream::Https(conn))
+ .map_err(|e| io::Error::new(io::ErrorKind::Other, e))
+ });
+ Box::new(fut)
+ } else {
+ Box::new(connecting.map(|tcp| MaybeHttpsStream::Http(tcp)))
+ };
+ HttpsConnecting(fut)
+ }
+}
+
+type BoxedFut<T> = Box<Future<Item = MaybeHttpsStream<T>, Error = io::Error>>;
+
+/// A Future representing work to connect to a URL, and a TLS handshake.
+pub struct HttpsConnecting<T>(BoxedFut<T>);
+
+
+impl<T> Future for HttpsConnecting<T> {
+ type Item = MaybeHttpsStream<T>;
+ type Error = io::Error;
+
+ fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
+ self.0.poll()
+ }
+}
+
+impl<T> fmt::Debug for HttpsConnecting<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.pad("HttpsConnecting")
+ }
+}
diff --git a/mullvad-rpc/src/lib.rs b/mullvad-rpc/src/lib.rs
index 6a1a682a1b..82f26b9f28 100644
--- a/mullvad-rpc/src/lib.rs
+++ b/mullvad-rpc/src/lib.rs
@@ -46,6 +46,9 @@ pub mod rest;
mod cached_dns_resolver;
use cached_dns_resolver::CachedDnsResolver;
+mod https_client_with_sni;
+use https_client_with_sni::HttpsClientWithSni;
+
static MASTER_API_HOST: &str = "api.mullvad.net";
@@ -77,7 +80,8 @@ impl MullvadRpcFactory {
/// Spawns a tokio core on a new thread and returns a `HttpHandle` running on that core.
pub fn new_connection(&mut self) -> Result<HttpHandle, HttpError> {
- self.setup_connection(HttpTransport::new()?)
+ let client = HttpsClientWithSni::new(MASTER_API_HOST.to_owned());
+ self.setup_connection(HttpTransport::with_client(client)?)
}
/// Create and returns a `HttpHandle` running on the given core handle.
@@ -85,7 +89,8 @@ impl MullvadRpcFactory {
&mut self,
handle: &Handle,
) -> Result<HttpHandle, HttpError> {
- self.setup_connection(HttpTransport::shared(handle)?)
+ let client = HttpsClientWithSni::new(MASTER_API_HOST.to_owned());
+ self.setup_connection(HttpTransport::with_client_shared(client, handle)?)
}
fn setup_connection(&mut self, transport: HttpTransport) -> Result<HttpHandle, HttpError> {