~jojo/Carth

8f78cbc762fa5c8de50950fbc9a37d9561d94b9d — JoJo 5 months ago 6731d0f
std-rs: use native-tls inst of rustls. Better for gemini

Rustls is too strict for non-web, self signed, TOFU use, so use rustls
instead. Right now, TLS does no verification, but we'll fix this.
7 files changed, 208 insertions(+), 189 deletions(-)

M Makefile
M guix.scm
M src/Compile.hs
M std-rs/Cargo.lock
M std-rs/Cargo.toml
M std-rs/src/lib.rs
M std-rs/src/net.rs
M Makefile => Makefile +1 -1
@@ 22,7 22,7 @@ install-bin: carth-bin

.PHONY: install-lib
install-lib: std-rs
	mkdir -p $(lib_dir) && cp std-rs/target/release/libcarth_foreign_core.a $(lib_dir)
	mkdir -p $(lib_dir) && cp std-rs/target/release/libcarth_std_rs.a $(lib_dir)

.PHONY: install-mods
install-mods:

M guix.scm => guix.scm +8 -8
@@ 36,10 36,10 @@
               ;; Both static and shared libraries are produced, and needed. The
               ;; shared is used in the JIT when doing `cart run`, and the static
               ;; is used when compiling with `carth compile`.
               (copy-file "target/release/libcarth_foreign_core.a"
                          (string-append lib "/libcarth_foreign_core.a"))
               (copy-file "target/release/libcarth_foreign_core.so"
                          (string-append lib "/libcarth_foreign_core.so"))))))))
               (copy-file "target/release/libcarth_std_rs.a"
                          (string-append lib "/libcarth_std_rs.a"))
               (copy-file "target/release/libcarth_std_rs.so"
                          (string-append lib "/libcarth_std_rs.so"))))))))
    (home-page "https://github.com/bryal/carth")
    (synopsis "The Carth foreign core library")
    (description "The core and runtime library for Carth -- a purely functional


@@ 73,16 73,16 @@ programming language with Scheme-like syntax. Work in progress.")
     ;;              (std-rs (assoc-ref inputs "carth-std-rs"))
     ;;              (sigsegv-a (string-append sigsegv "/lib/libsigsegv.a"))
     ;;              (sigsegv-so (string-append sigsegv "/lib/libsigsegv.so"))
     ;;              (foreign-a (string-append std-rs "/lib/libcarth_foreign_core.a"))
     ;;              (foreign-so (string-append std-rs "/lib/libcarth_foreign_core.so")))
     ;;              (foreign-a (string-append std-rs "/lib/libcarth_std_rs.a"))
     ;;              (foreign-so (string-append std-rs "/lib/libcarth_std_rs.so")))
     ;;         (invoke "ls" "-la" "src/Compile.hs")
     ;;         (invoke "cat" "/etc/passwd")
     ;;         (chmod "src/Compile.hs" #o755)
     ;;         (substitute* "src/Compile.hs"
     ;;           (("-l:libcarth_foreign_core.a") (string-append "-l:" foreign-a))
     ;;           (("-l:libcarth_std_rs.a") (string-append "-l:" foreign-a))
     ;;           (("-lsigsegv") (string-append "-l:" sigsegv-a))
     ;;           (("libsigsegv.so") sigsegv-so)
     ;;           (("libcarth_foreign_core.so") foreign-so))))))
     ;;           (("libcarth_std_rs.so") foreign-so))))))
     ))
  (inputs
   `(("ghc-megaparsec" ,ghc-megaparsec)

M src/Compile.hs => src/Compile.hs +4 -2
@@ 103,12 103,14 @@ compileModule cfg tm mod = do
        [ "-o"
        , exefile
        , ofile
        , "-l:libcarth_foreign_core.a"
        , "-l:libcarth_std_rs.a"
        , "-lsigsegv"
        , "-ldl"
        , "-lpthread"
        , "-lm"
        , "-lgc"
        , "-lssl"
        , "-lcrypto"
        ]

foreign import ccall "dynamic"


@@ 117,7 119,7 @@ foreign import ccall "dynamic"
orcJitModule :: RunConfig -> TargetMachine -> Module -> IO ()
orcJitModule cfg tm mod = do
    verbose cfg "   Running with OrcJIT"
    let libs = ["libsigsegv.so", "libcarth_foreign_core.so", "libgc.so"]
    let libs = ["libsigsegv.so", "libcarth_std_rs.so", "libgc.so"]
    forM_ libs $ \lib -> do
        verbose cfg $ "   Loading symbols of " ++ lib
        r <- loadLibraryPermanently (Just lib)

M std-rs/Cargo.lock => std-rs/Cargo.lock +152 -107
@@ 1,24 1,23 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "base64"
version = "0.12.3"
name = "autocfg"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"

[[package]]
name = "bumpalo"
version = "3.4.0"
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"

[[package]]
name = "carth-std-rs"
version = "0.3.1"
dependencies = [
 "libc",
 "rustls",
 "webpki",
 "native-tls",
]

[[package]]


@@ 34,12 33,45 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"

[[package]]
name = "js-sys"
version = "0.3.45"
name = "core-foundation"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca059e81d9486668f12d455a4ea6daa600bd408134cd17e3d3fb5a32d1f016f8"
checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62"
dependencies = [
 "wasm-bindgen",
 "core-foundation-sys",
 "libc",
]

[[package]]
name = "core-foundation-sys"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b"

[[package]]
name = "foreign-types"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
 "foreign-types-shared",
]

[[package]]
name = "foreign-types-shared"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"

[[package]]
name = "getrandom"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6"
dependencies = [
 "cfg-if",
 "libc",
 "wasi",
]

[[package]]


@@ 64,169 96,182 @@ dependencies = [
]

[[package]]
name = "once_cell"
version = "1.4.1"
name = "native-tls"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad"
checksum = "6fcc7939b5edc4e4f86b1b4a04bb1498afaaf871b1a6691838ed06fcb48d3a3f"
dependencies = [
 "lazy_static",
 "libc",
 "log",
 "openssl",
 "openssl-probe",
 "openssl-sys",
 "schannel",
 "security-framework",
 "security-framework-sys",
 "tempfile",
]

[[package]]
name = "proc-macro2"
version = "1.0.24"
name = "openssl"
version = "0.10.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
checksum = "8d575eff3665419f9b83678ff2815858ad9d11567e082f5ac1814baba4e2bcb4"
dependencies = [
 "unicode-xid",
 "bitflags",
 "cfg-if",
 "foreign-types",
 "lazy_static",
 "libc",
 "openssl-sys",
]

[[package]]
name = "quote"
version = "1.0.7"
name = "openssl-probe"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
dependencies = [
 "proc-macro2",
]
checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"

[[package]]
name = "ring"
version = "0.16.15"
name = "openssl-sys"
version = "0.9.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "952cd6b98c85bbc30efa1ba5783b8abf12fec8b3287ffa52605b9432313e34e4"
checksum = "a842db4709b604f0fe5d1170ae3565899be2ad3d9cbc72dedc789ac0511f78de"
dependencies = [
 "autocfg",
 "cc",
 "libc",
 "once_cell",
 "spin",
 "untrusted",
 "web-sys",
 "winapi",
 "pkg-config",
 "vcpkg",
]

[[package]]
name = "rustls"
version = "0.18.1"
name = "pkg-config"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d1126dcf58e93cee7d098dbda643b5f92ed724f1f6a63007c1116eed6700c81"
dependencies = [
 "base64",
 "log",
 "ring",
 "sct",
 "webpki",
]
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"

[[package]]
name = "ppv-lite86"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"

[[package]]
name = "sct"
version = "0.6.0"
name = "rand"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c"
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [
 "ring",
 "untrusted",
 "getrandom",
 "libc",
 "rand_chacha",
 "rand_core",
 "rand_hc",
]

[[package]]
name = "spin"
version = "0.5.2"
name = "rand_chacha"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
dependencies = [
 "ppv-lite86",
 "rand_core",
]

[[package]]
name = "syn"
version = "1.0.48"
name = "rand_core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
dependencies = [
 "proc-macro2",
 "quote",
 "unicode-xid",
 "getrandom",
]

[[package]]
name = "unicode-xid"
version = "0.2.1"
name = "rand_hc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
dependencies = [
 "rand_core",
]

[[package]]
name = "untrusted"
version = "0.7.1"
name = "redox_syscall"
version = "0.1.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"

[[package]]
name = "wasm-bindgen"
version = "0.2.68"
name = "remove_dir_all"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ac64ead5ea5f05873d7c12b545865ca2b8d28adfc50a49b84770a3a97265d42"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [
 "cfg-if",
 "wasm-bindgen-macro",
 "winapi",
]

[[package]]
name = "wasm-bindgen-backend"
version = "0.2.68"
name = "schannel"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f22b422e2a757c35a73774860af8e112bff612ce6cb604224e8e47641a9e4f68"
checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75"
dependencies = [
 "bumpalo",
 "lazy_static",
 "log",
 "proc-macro2",
 "quote",
 "syn",
 "wasm-bindgen-shared",
 "winapi",
]

[[package]]
name = "wasm-bindgen-macro"
version = "0.2.68"
name = "security-framework"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b13312a745c08c469f0b292dd2fcd6411dba5f7160f593da6ef69b64e407038"
checksum = "c1759c2e3c8580017a484a7ac56d3abc5a6c1feadf88db2f3633f12ae4268c69"
dependencies = [
 "quote",
 "wasm-bindgen-macro-support",
 "bitflags",
 "core-foundation",
 "core-foundation-sys",
 "libc",
 "security-framework-sys",
]

[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.68"
name = "security-framework-sys"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f249f06ef7ee334cc3b8ff031bfc11ec99d00f34d86da7498396dc1e3b1498fe"
checksum = "f99b9d5e26d2a71633cc4f2ebae7cc9f874044e0c351a27e17892d76dce5678b"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
 "wasm-bindgen-backend",
 "wasm-bindgen-shared",
 "core-foundation-sys",
 "libc",
]

[[package]]
name = "wasm-bindgen-shared"
version = "0.2.68"
name = "tempfile"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d649a3145108d7d3fbcde896a468d1bd636791823c9921135218ad89be08307"
checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
dependencies = [
 "cfg-if",
 "libc",
 "rand",
 "redox_syscall",
 "remove_dir_all",
 "winapi",
]

[[package]]
name = "web-sys"
version = "0.3.45"
name = "vcpkg"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bf6ef87ad7ae8008e15a355ce696bed26012b7caa21605188cfd8214ab51e2d"
dependencies = [
 "js-sys",
 "wasm-bindgen",
]
checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c"

[[package]]
name = "webpki"
version = "0.21.3"
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab146130f5f790d45f82aeeb09e55a256573373ec64409fc19a6fb82fb1032ae"
dependencies = [
 "ring",
 "untrusted",
]
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"

[[package]]
name = "winapi"

M std-rs/Cargo.toml => std-rs/Cargo.toml +1 -2
@@ 10,8 10,7 @@ license = "AGPL-3.0-or-later"

[dependencies]
libc = "0.2"
rustls = { version = "0.18", features = ["dangerous_configuration"] }
webpki = "0.21"
native-tls = "0.2.6"

[lib]
crate-type = ["staticlib", "cdylib", "rlib"]

M std-rs/src/lib.rs => std-rs/src/lib.rs +1 -1
@@ 205,7 205,7 @@ pub extern "C" fn read_file(fp: Str) -> Maybe<Str> {
//       It seems that if no non-dead code makes use of functions from libm, then rustc or
//       cargo won't link with libm. However, we need that to happen, as when running a
//       Carth program with the JIT, there is no shared library for libm to load, so we
//       need the libm functionality to be included in libforeign_core.so.
//       need the libm functionality to be included in the .so.
//
// TODO: Find some other way of ensuring libm is linked with when creating the shared lib.
#[no_mangle]

M std-rs/src/net.rs => std-rs/src/net.rs +41 -68
@@ 1,34 1,12 @@
use crate::io::*;

use rustls::*;
use webpki;
use native_tls;

use std::net::TcpStream;
use std::sync::Arc;
use std::net::{TcpStream, ToSocketAddrs};
use std::time::Duration;

use crate::*;

#[repr(u8)]
pub enum Verifier {
    DangerousNone,
    Tofu,
}

impl ServerCertVerifier for Verifier {
    fn verify_server_cert(
        &self,
        _: &RootCertStore,
        _: &[Certificate],
        _: webpki::DNSNameRef<'_>,
        _: &[u8],
    ) -> Result<ServerCertVerified, TLSError> {
        match *self {
            Verifier::DangerousNone => Ok(ServerCertVerified::assertion()),
            Verifier::Tofu => todo!(),
        }
    }
}

#[no_mangle]
pub extern "C" fn stdrs_tcp_connect(host: Str, port: u16) -> FfiHandle {
    handle_to_ffi(Box::into_raw(


@@ 36,55 14,50 @@ pub extern "C" fn stdrs_tcp_connect(host: Str, port: u16) -> FfiHandle {
    ))
}

#[no_mangle]
pub extern "C" fn stdrs_tcp_connect_timeout(host: Str, port: u16, ms: u64) -> FfiHandle {
    let timeout = Duration::from_millis(ms);
    let addrs = (host.as_str(), port)
        .to_socket_addrs()
        .unwrap()
        .collect::<Vec<_>>();
    let (last, init) = addrs.split_last().unwrap();
    let con = init
        .iter()
        .filter_map(|addr| TcpStream::connect_timeout(addr, timeout).ok())
        .next()
        .unwrap_or_else(|| TcpStream::connect_timeout(last, timeout).unwrap());
    handle_to_ffi(Box::into_raw(Box::new(con) as _))
}

// Would have loved to use rustls for this, since it's rust, but there are problems that
// prevent it's effecient usage when using self signed certs as we do in gemini. See
// https://github.com/briansmith/webpki/issues/90.
#[no_mangle]
pub unsafe extern "C" fn stdrs_tls_connect(domain: Str, transport: FfiHandle) -> FfiHandle {
    let domain = domain.as_str();
    let transport = handle_from_ffi(transport);
    let mut config = rustls::ClientConfig::new();
    config
        .dangerous()
        .set_certificate_verifier(Arc::new(Verifier::DangerousNone));
    let dns_name = webpki::DNSNameRef::try_from_ascii_str(domain).expect("dns name");
    let sess = rustls::ClientSession::new(&Arc::new(config), dns_name);
    let tls = rustls::StreamOwned::new(sess, Box::from_raw(transport));
    let transport = Box::from_raw(handle_from_ffi(transport));
    // We typically use self signed certs and TOFU in Gemini, but self signed certs are
    // considered "invalid" by default. Therefore, we accept invalid certs, but check for
    // expiration later.
    let connector = native_tls::TlsConnector::builder()
        // TODO: Check for cert expiration date and do TOFU.
        .danger_accept_invalid_certs(true)
        // Rust's native-tls does not yet provide Tlsv13 :(
        .min_protocol_version(Some(native_tls::Protocol::Tlsv12))
        .build()
        .unwrap();
    let tls = connector
        .connect(domain, transport)
        .map_err(show_tls_err)
        .unwrap();
    handle_to_ffi(Box::into_raw(Box::new(tls) as _))
}

// fn main() {
//     // let (domain, path) = ("gus.guru", "/search");
//     let (domain, path) = ("gemini.circumlunar.space", "/");
//     let port = 1965;
//     get_data(domain, path, port);
//     // let header = resp.lines().next().expect("number of response lines >= 1");
//     // let code = header[0..2]
//     //     .parse::<u8>()
//     //     .expect("header beginning with 2-digit status code");
//     // let meta = &header[3..];
//     // if meta.as_bytes().len() > 1024 {
//     //     panic!("Response META longer than 1024 bytes. Error.");
//     // }
//     // let body = &resp[header.len() + 2..];
//     // println!("code: {}, meta: {}, body:\n\n{}", code, meta, body);
// }

// fn get_data(domain: &str, path: &str, port: u16) {
//     let mut config = ClientConfig::new();
//     config
//         .dangerous()
//         .set_certificate_verifier(Arc::new(Verifier::DangerousNone));
//     let dns_name = webpki::DNSNameRef::try_from_ascii_str(domain).expect("dns name");
//     let sess = ClientSession::new(&Arc::new(config), dns_name);
//     let sock = TcpStream::connect((domain, port)).expect("socket");
//     let mut tls = StreamOwned::new(sess, sock);
//     write!(&mut tls, "gemini://{}{}\r\n", domain, path).expect("written gemini message");
//     let code = &mut [0u8; 2][..];
//     tls.read_exact(code).unwrap();
//     let code = str::from_utf8(code).expect("valid utf-8 gemini status code");
//     println!("code: {}", code);
//     let mut resp = Vec::new();
//     println!("read result: {:?}\n", tls.read_to_end(&mut resp));
//     String::from_utf8(resp).expect("valid utf-8 response")
// }
fn show_tls_err<S>(e: native_tls::HandshakeError<S>) -> String {
    use native_tls::HandshakeError::*;
    match e {
        Failure(e) => e.to_string(),
        WouldBlock(_) => "the handshake process was interrupted".to_string(),
    }
}