summaryrefslogtreecommitdiff
path: root/cli/ops/tls.rs
blob: 2b1d94f2b202fc4ac08253c044c84071e5619e2f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
use super::dispatch_json::{Deserialize, JsonOp, Value};
use crate::resolve_addr::resolve_addr;
use crate::resources;
use crate::state::ThreadSafeState;
use deno::*;
use futures::Future;
use std;
use std::convert::From;
use std::sync::Arc;
use tokio;
use tokio::net::TcpStream;
use tokio_rustls::{rustls::ClientConfig, TlsConnector};
use webpki;
use webpki::DNSNameRef;
use webpki_roots;

#[derive(Deserialize)]
struct DialTLSArgs {
  hostname: String,
  port: u16,
}

pub fn op_dial_tls(
  state: &ThreadSafeState,
  args: Value,
  _zero_copy: Option<PinnedBuf>,
) -> Result<JsonOp, ErrBox> {
  let args: DialTLSArgs = serde_json::from_value(args)?;

  // TODO(ry) Using format! is suboptimal here. Better would be if
  // state.check_net and resolve_addr() took hostname and port directly.
  let address = format!("{}:{}", args.hostname, args.port);

  state.check_net(&address)?;

  let mut domain = args.hostname;
  if domain.is_empty() {
    domain.push_str("localhost");
  }

  let op = resolve_addr(&address).and_then(move |addr| {
    TcpStream::connect(&addr)
      .and_then(move |tcp_stream| {
        let local_addr = tcp_stream.local_addr()?;
        let remote_addr = tcp_stream.peer_addr()?;
        let mut config = ClientConfig::new();
        config
          .root_store
          .add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS);

        let tls_connector = TlsConnector::from(Arc::new(config));
        Ok((tls_connector, tcp_stream, local_addr, remote_addr))
      })
      .map_err(ErrBox::from)
      .and_then(
        move |(tls_connector, tcp_stream, local_addr, remote_addr)| {
          let dnsname = DNSNameRef::try_from_ascii_str(&domain)
            .expect("Invalid DNS lookup");
          tls_connector
            .connect(dnsname, tcp_stream)
            .map_err(ErrBox::from)
            .and_then(move |tls_stream| {
              let tls_stream_resource = resources::add_tls_stream(tls_stream);
              futures::future::ok(json!({
                "rid": tls_stream_resource.rid,
                "localAddr": local_addr.to_string(),
                "remoteAddr": remote_addr.to_string(),
              }))
            })
        },
      )
  });

  Ok(JsonOp::Async(Box::new(op)))
}