From 045e74bb39d7743b774cfd2b889bc6ce1e1ad245 Mon Sep 17 00:00:00 2001 From: Jonathon Orsi Date: Mon, 23 Sep 2019 14:40:38 -0400 Subject: [PATCH] feat: Add Deno.dialTLS() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartek IwaƄczuk --- Cargo.lock | 68 +++++++++++++++++++++++++---------- cli/Cargo.toml | 2 ++ cli/ops/mod.rs | 5 +++ cli/ops/tls.rs | 76 ++++++++++++++++++++++++++++++++++++++++ cli/resources.rs | 13 +++++++ js/deno.ts | 1 + js/dispatch.ts | 2 ++ js/lib.deno_runtime.d.ts | 11 +++++- js/net.ts | 3 +- js/tls.ts | 21 +++++++++++ js/tls_test.ts | 25 +++++++++++++ js/unit_tests.ts | 1 + 12 files changed, 206 insertions(+), 22 deletions(-) create mode 100644 cli/ops/tls.rs create mode 100644 js/tls.ts create mode 100644 js/tls_test.ts diff --git a/Cargo.lock b/Cargo.lock index 9fe95b1fa2..83fd39b7e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -120,7 +120,7 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -188,7 +188,7 @@ dependencies = [ "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "publicsuffix 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "publicsuffix 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", @@ -278,7 +278,7 @@ dependencies = [ "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "fwdansi 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "http 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.12.34 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)", "hyper-rustls 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)", "indexmap 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -286,7 +286,7 @@ dependencies = [ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", "os_pipe 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "reqwest 0.9.20 (registry+https://github.com/rust-lang/crates.io-index)", @@ -307,6 +307,8 @@ dependencies = [ "tokio-threadpool 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "utime 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "webpki 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", + "webpki-roots 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -354,7 +356,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "either" -version = "1.5.2" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -514,7 +516,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "hyper" -version = "0.12.34" +version = "0.12.35" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", @@ -549,7 +551,7 @@ dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "ct-logs 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.12.34 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)", "rustls 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-rustls 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -561,7 +563,7 @@ dependencies = [ name = "hyper_hello" version = "0.0.1" dependencies = [ - "hyper 0.12.34 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)", "ring 0.16.9 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -575,6 +577,16 @@ dependencies = [ "unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "idna" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "indexmap" version = "1.2.0" @@ -835,6 +847,11 @@ name = "percent-encoding" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "ppv-lite86" version = "0.2.5" @@ -858,14 +875,14 @@ dependencies = [ [[package]] name = "publicsuffix" -version = "1.5.2" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", - "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -916,7 +933,7 @@ dependencies = [ [[package]] name = "rand" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1090,7 +1107,7 @@ dependencies = [ "flate2 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "http 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.12.34 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)", "hyper-rustls 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1368,7 +1385,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1437,7 +1454,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1706,6 +1723,16 @@ dependencies = [ "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "url" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "utf8parse" version = "0.1.1" @@ -1963,7 +1990,7 @@ dependencies = [ "checksum dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" "checksum dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b" "checksum dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ea57b42383d091c85abcc2706240b94ab2a8fa1fc81c10ff23c4de06e2a90b5e" -"checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b" +"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" "checksum encoding_rs 0.8.19 (registry+https://github.com/rust-lang/crates.io-index)" = "79906e1ad1f7f8bc48864fcc6ffd58336fb5992e627bf61928099cb25fdf4314" "checksum error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3ab49e9dcb602294bc42f9a7dfc9bc6e936fca4418ea300dbfb84fe16de0b7d9" "checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" @@ -1982,9 +2009,10 @@ dependencies = [ "checksum http 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "372bcb56f939e449117fb0869c2e8fd8753a8223d92a172c6e808cf123a5b6e4" "checksum http-body 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6741c859c1b2463a423a1dbce98d418e6c3c3fc720fb0d45528657320920292d" "checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" -"checksum hyper 0.12.34 (registry+https://github.com/rust-lang/crates.io-index)" = "898a87371a3999b2f731b9af636cd76aa20de10e69c2daf3e71388326b619fe0" +"checksum hyper 0.12.35 (registry+https://github.com/rust-lang/crates.io-index)" = "9dbe6ed1438e1f8ad955a4701e9a944938e9519f6888d12d8558b645e247d5f6" "checksum hyper-rustls 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)" = "719d85c7df4a7f309a77d145340a063ea929dcb2e025bae46a80345cffec2952" "checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" +"checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" "checksum indexmap 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a61202fbe46c4a951e9404a720a0180bcf3212c750d735cb5c4ba4dc551299f3" "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" "checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" @@ -2016,15 +2044,16 @@ dependencies = [ "checksum parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab41b4aed082705d1056416ae4468b6ea99d52599ecf3169b00088d43113e337" "checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" +"checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" "checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b" "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" "checksum proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e98a83a9f9b331f54b924e68a66acb1bb35cb01fb0a23645139967abefb697e8" -"checksum publicsuffix 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5afecba86dcf1e4fd610246f89899d1924fe12e1e89f555eb7c7f710f3c5ad1d" +"checksum publicsuffix 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9bf259a81de2b2eb9850ec990ec78e6a25319715584fd7652b9b26f96fcb1510" "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" "checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" "checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" -"checksum rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d47eab0e83d9693d40f825f86948aa16eff6750ead4bdffc4ab95b8b3a7f052c" +"checksum rand 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "59cea0d944b32347a1863e95942fd6ebdb486afb4f038119494f2860380c1d51" "checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" "checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" @@ -2107,6 +2136,7 @@ dependencies = [ "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" "checksum untrusted 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60369ef7a31de49bcb3f6ca728d4ba7300d9a1658f94c727d4cab8c8d9f4aece" "checksum url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" +"checksum url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "75b414f6c464c879d7f9babf951f23bc3743fb7313c081b2e6ca719067ea9d61" "checksum utf8parse 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8772a4ccbb4e89959023bc5b7cb8623a795caa7092d99f3aa9501b9484d4557d" "checksum utime 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "055058552ca15c566082fc61da433ae678f78986a6f16957e33162d1b218792a" "checksum uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90dbc611eb48397705a6b0f6e917da23ae517e4d127123d2cf7674206627d32a" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index b9a9c1456a..463c9fbc19 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -56,6 +56,8 @@ tokio-rustls = "0.10.0" tokio-threadpool = "0.1.15" url = "1.7.2" utime = "0.2.1" +webpki = "0.21.0" +webpki-roots = "0.17.0" [target.'cfg(windows)'.dependencies] winapi = "0.3.8" diff --git a/cli/ops/mod.rs b/cli/ops/mod.rs index 7a2e9c9f46..ea71fee9fa 100644 --- a/cli/ops/mod.rs +++ b/cli/ops/mod.rs @@ -20,6 +20,7 @@ mod random; mod repl; mod resources; mod timers; +mod tls; mod workers; // Warning! These values are duplicated in the TypeScript code (js/dispatch.ts), @@ -81,6 +82,7 @@ pub const OP_TRUNCATE: OpId = 54; pub const OP_MAKE_TEMP_DIR: OpId = 55; pub const OP_CWD: OpId = 56; pub const OP_FETCH_ASSET: OpId = 57; +pub const OP_DIAL_TLS: OpId = 58; pub fn dispatch( state: &ThreadSafeState, @@ -300,6 +302,9 @@ pub fn dispatch( control, zero_copy, ), + OP_DIAL_TLS => { + dispatch_json::dispatch(tls::op_dial_tls, state, control, zero_copy) + } _ => panic!("bad op_id"), }; diff --git a/cli/ops/tls.rs b/cli/ops/tls.rs new file mode 100644 index 0000000000..2b1d94f2b2 --- /dev/null +++ b/cli/ops/tls.rs @@ -0,0 +1,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, +) -> Result { + 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))) +} diff --git a/cli/resources.rs b/cli/resources.rs index 3bce51afb0..0fdb0e1825 100644 --- a/cli/resources.rs +++ b/cli/resources.rs @@ -36,6 +36,7 @@ use tokio::io::{AsyncRead, AsyncWrite}; use tokio::net::TcpStream; use tokio::sync::mpsc; use tokio_process; +use tokio_rustls::client::TlsStream; pub type ResourceId = u32; // Sometimes referred to RID. @@ -89,6 +90,7 @@ enum Repr { // See: https://github.com/tokio-rs/tokio/issues/846 TcpListener(tokio::net::TcpListener, Option), TcpStream(tokio::net::TcpStream), + TlsStream(Box>), HttpBody(HttpBody), Repl(Arc>), // Enum size is bounded by the largest variant. @@ -134,6 +136,7 @@ fn inspect_repr(repr: &Repr) -> String { Repr::FsFile(_) => "fsFile", Repr::TcpListener(_, _) => "tcpListener", Repr::TcpStream(_) => "tcpStream", + Repr::TlsStream(_) => "tlsStream", Repr::HttpBody(_) => "httpBody", Repr::Repl(_) => "repl", Repr::Child(_) => "child", @@ -249,6 +252,7 @@ impl DenoAsyncRead for Resource { Repr::FsFile(ref mut f) => f.poll_read(buf), Repr::Stdin(ref mut f) => f.poll_read(buf), Repr::TcpStream(ref mut f) => f.poll_read(buf), + Repr::TlsStream(ref mut f) => f.poll_read(buf), Repr::HttpBody(ref mut f) => f.poll_read(buf), Repr::ChildStdout(ref mut f) => f.poll_read(buf), Repr::ChildStderr(ref mut f) => f.poll_read(buf), @@ -289,6 +293,7 @@ impl DenoAsyncWrite for Resource { Repr::Stdout(ref mut f) => f.poll_write(buf), Repr::Stderr(ref mut f) => f.poll_write(buf), Repr::TcpStream(ref mut f) => f.poll_write(buf), + Repr::TlsStream(ref mut f) => f.poll_write(buf), Repr::ChildStdin(ref mut f) => f.poll_write(buf), _ => { return Err(bad_resource()); @@ -332,6 +337,14 @@ pub fn add_tcp_stream(stream: tokio::net::TcpStream) -> Resource { Resource { rid } } +pub fn add_tls_stream(stream: TlsStream) -> Resource { + let rid = new_rid(); + let mut tg = RESOURCE_TABLE.lock().unwrap(); + let r = tg.insert(rid, Repr::TlsStream(Box::new(stream))); + assert!(r.is_none()); + Resource { rid } +} + pub fn add_reqwest_body(body: ReqwestDecoder) -> Resource { let rid = new_rid(); let mut tg = RESOURCE_TABLE.lock().unwrap(); diff --git a/js/deno.ts b/js/deno.ts index 4efa641d36..916b7471a8 100644 --- a/js/deno.ts +++ b/js/deno.ts @@ -75,6 +75,7 @@ export { export { truncateSync, truncate } from "./truncate.ts"; export { FileInfo } from "./file_info.ts"; export { connect, dial, listen, Listener, Conn } from "./net.ts"; +export { dialTLS } from "./tls.ts"; export { metrics, Metrics } from "./metrics.ts"; export { resources } from "./resources.ts"; export { diff --git a/js/dispatch.ts b/js/dispatch.ts index a15da69f48..b5116d68a3 100644 --- a/js/dispatch.ts +++ b/js/dispatch.ts @@ -60,6 +60,7 @@ export const OP_TRUNCATE = 54; export const OP_MAKE_TEMP_DIR = 55; export const OP_CWD = 56; export const OP_FETCH_ASSET = 57; +export const OP_DIAL_TLS = 58; export function asyncMsgFromRust(opId: number, ui8: Uint8Array): void { switch (opId) { @@ -97,6 +98,7 @@ export function asyncMsgFromRust(opId: number, ui8: Uint8Array): void { case OP_READ_LINK: case OP_TRUNCATE: case OP_MAKE_TEMP_DIR: + case OP_DIAL_TLS: json.asyncMsgFromRust(opId, ui8); break; default: diff --git a/js/lib.deno_runtime.d.ts b/js/lib.deno_runtime.d.ts index fc4e6508f8..36e49c9c23 100644 --- a/js/lib.deno_runtime.d.ts +++ b/js/lib.deno_runtime.d.ts @@ -999,8 +999,17 @@ declare namespace Deno { */ export function dial(options: DialOptions): Promise; - // @url js/metrics.d.ts + export interface DialTLSOptions { + port: number; + hostname?: string; + } + /** + * dialTLS establishes a secure connection over TLS (transport layer security). + */ + export function dialTLS(options: DialTLSOptions): Promise; + + // @url js/metrics.d.ts export interface Metrics { opsDispatched: number; opsCompleted: number; diff --git a/js/net.ts b/js/net.ts index 28c8cf8e07..4da6da38ef 100644 --- a/js/net.ts +++ b/js/net.ts @@ -44,7 +44,7 @@ function shutdown(rid: number, how: ShutdownMode): void { sendSync(dispatch.OP_SHUTDOWN, { rid, how }); } -class ConnImpl implements Conn { +export class ConnImpl implements Conn { constructor( readonly rid: number, readonly remoteAddr: string, @@ -187,7 +187,6 @@ const dialDefaults = { hostname: "127.0.0.1", transport: "tcp" }; export async function dial(options: DialOptions): Promise { options = Object.assign(dialDefaults, options); const res = await sendAsync(dispatch.OP_DIAL, options); - // TODO(bartlomieju): add remoteAddr and localAddr on Rust side return new ConnImpl(res.rid, res.remoteAddr!, res.localAddr!); } diff --git a/js/tls.ts b/js/tls.ts new file mode 100644 index 0000000000..ec24b458be --- /dev/null +++ b/js/tls.ts @@ -0,0 +1,21 @@ +// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. +import { sendAsync } from "./dispatch_json.ts"; +import * as dispatch from "./dispatch.ts"; +import { Conn, ConnImpl } from "./net.ts"; + +// TODO(ry) There are many configuration options to add... +// https://docs.rs/rustls/0.16.0/rustls/struct.ClientConfig.html +interface DialTLSOptions { + port: number; + hostname?: string; +} +const dialTLSDefaults = { hostname: "127.0.0.1", transport: "tcp" }; + +/** + * dialTLS establishes a secure connection over TLS (transport layer security). + */ +export async function dialTLS(options: DialTLSOptions): Promise { + options = Object.assign(dialTLSDefaults, options); + const res = await sendAsync(dispatch.OP_DIAL_TLS, options); + return new ConnImpl(res.rid, res.remoteAddr!, res.localAddr!); +} diff --git a/js/tls_test.ts b/js/tls_test.ts new file mode 100644 index 0000000000..25900f8763 --- /dev/null +++ b/js/tls_test.ts @@ -0,0 +1,25 @@ +// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. +import { test, testPerm, assert, assertEquals } from "./test_util.ts"; + +// TODO(ry) The tests in this file use github.com:443, but it would be better to +// not rely on an internet connection and rather use a localhost TLS server. + +test(async function dialTLSNoPerm(): Promise { + let err; + try { + await Deno.dialTLS({ hostname: "github.com", port: 443 }); + } catch (e) { + err = e; + } + assertEquals(err.kind, Deno.ErrorKind.PermissionDenied); + assertEquals(err.name, "PermissionDenied"); +}); + +testPerm({ net: true }, async function dialTLSBasic(): Promise { + const conn = await Deno.dialTLS({ hostname: "github.com", port: 443 }); + assert(conn.rid > 0); + const body = new TextEncoder().encode("GET / HTTP/1.0\r\n\r\n"); + const writeResult = await conn.write(body); + assertEquals(body.length, writeResult); + conn.close(); +}); diff --git a/js/unit_tests.ts b/js/unit_tests.ts index 711a092fdb..2da67c40ed 100644 --- a/js/unit_tests.ts +++ b/js/unit_tests.ts @@ -43,6 +43,7 @@ import "./stat_test.ts"; import "./symlink_test.ts"; import "./text_encoding_test.ts"; import "./timers_test.ts"; +import "./tls_test.ts"; import "./truncate_test.ts"; import "./url_test.ts"; import "./url_search_params_test.ts";