diff --git a/.github/workflows/ci_rust.yml b/.github/workflows/ci_rust.yml index 93c8874e12c..b8d8b7690a1 100644 --- a/.github/workflows/ci_rust.yml +++ b/.github/workflows/ci_rust.yml @@ -11,8 +11,9 @@ on: env: # Pin the nightly toolchain to prevent breakage. # This should be occasionally updated. - RUST_NIGHTLY_TOOLCHAIN: nightly-2022-08-03 + RUST_NIGHTLY_TOOLCHAIN: nightly-2024-01-01 ROOT_PATH: bindings/rust + EXAMPLE_WORKSPACE: bindings/rust-examples jobs: generate: @@ -78,6 +79,24 @@ jobs: - name: bench tests working-directory: ${{env.ROOT_PATH}}/bench run: cargo test + + s2n-tls-binding-examples: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - uses: actions-rs/toolchain@v1 + id: toolchain + with: + toolchain: stable + override: true + + - name: generate bindings + run: ${{env.ROOT_PATH}}/generate.sh --skip-tests + + - name: build examples + working-directory: ${{env.EXAMPLE_WORKSPACE}} + run: cargo build generate-openssl-102: runs-on: ubuntu-latest diff --git a/bindings/rust-examples/.gitignore b/bindings/rust-examples/.gitignore new file mode 100644 index 00000000000..9b98c83d9c7 --- /dev/null +++ b/bindings/rust-examples/.gitignore @@ -0,0 +1,2 @@ +*target/ +*Cargo.lock diff --git a/bindings/rust-examples/Cargo.toml b/bindings/rust-examples/Cargo.toml new file mode 100644 index 00000000000..0b4ca8be1aa --- /dev/null +++ b/bindings/rust-examples/Cargo.toml @@ -0,0 +1,12 @@ +[workspace] +members = [ + "client-hello-config-resolution", +] +resolver = "2" + +[workspace.package] +version = "0.0.1" +authors = ["AWS s2n"] +publish = false +license = "Apache-2.0" +edition = "2021" diff --git a/bindings/rust-examples/client-hello-config-resolution/Cargo.toml b/bindings/rust-examples/client-hello-config-resolution/Cargo.toml new file mode 100644 index 00000000000..96f59aa1249 --- /dev/null +++ b/bindings/rust-examples/client-hello-config-resolution/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "client-hello-config-resolution" +version.workspace = true +authors.workspace = true +publish.workspace = true +license.workspace = true +edition.workspace = true + +[dependencies] +clap = { version = "4", features = ["derive"] } +s2n-tls = { path = "../../rust/s2n-tls" } +s2n-tls-tokio = { path = "../../rust/s2n-tls-tokio" } +tokio = { version = "1", features = ["full"] } diff --git a/bindings/rust-examples/client-hello-config-resolution/README.md b/bindings/rust-examples/client-hello-config-resolution/README.md new file mode 100644 index 00000000000..a0e22f99629 --- /dev/null +++ b/bindings/rust-examples/client-hello-config-resolution/README.md @@ -0,0 +1,43 @@ +This example shows how to use the s2n-tls client hello callback to configure different TLS configs based on the Server Name Indication (SNI) in the client hello. The [server](src/bin/server.rs) sets up two configs for two different sni's, `www.wombat.com` and `www.kangaroo.com`. These configs are set up with different cipher preferences so that the allowed TLS versions are dependent on the client SNI. + +To run this example, first start the server in one terminal +``` +cargo run --bin server +``` +Then run the client in another terminal, setting the appropriate SNI. + +### Kangaroo SNI +``` +cargo run --bin client www.kangaroo.com +``` +``` +TlsStream { + connection: Connection { + handshake_type: "NEGOTIATED|FULL_HANDSHAKE|MIDDLEBOX_COMPAT", + cipher_suite: "TLS_AES_128_GCM_SHA256", + actual_protocol_version: TLS13, + selected_curve: "x25519", + .. + }, +} +The server said Hello, you are speaking to www.kangaroo.com +``` +We can see that the server successfully responded with the appropriate `www.kangaroo.com` certificate, resulting in a successful handshake. + +### Wombat SNI +``` +cargo run --bin client www.wombat.com +``` +``` +TlsStream { + connection: Connection { + handshake_type: "NEGOTIATED|FULL_HANDSHAKE|TLS12_PERFECT_FORWARD_SECRECY", + cipher_suite: "ECDHE-ECDSA-AES128-SHA", + actual_protocol_version: TLS12, + selected_curve: "secp256r1", + .. + }, +} +The server said Hello, you are speaking to www.wombat.com +``` +Once again there is a successful handshake showing that the server responded with the proper certificate. In this case, the config that the server configured for `www.wombat.com` did not support TLS 1.3, so the TLS 1.2 was negotiated instead. diff --git a/bindings/rust-examples/client-hello-config-resolution/certs/ca-cert.pem b/bindings/rust-examples/client-hello-config-resolution/certs/ca-cert.pem new file mode 100644 index 00000000000..18adfd4d6e9 --- /dev/null +++ b/bindings/rust-examples/client-hello-config-resolution/certs/ca-cert.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIB3DCCAWKgAwIBAgIUaAjZTaFhJNRyFtFQut1CdrY7RH0wCgYIKoZIzj0EAwMw +HDELMAkGA1UEBhMCVVMxDTALBgNVBAMMBHJvb3QwIBcNMjQwMTI3MDAwODQ4WhgP +MjIwMzA3MDQwMDA4NDhaMBwxCzAJBgNVBAYTAlVTMQ0wCwYDVQQDDARyb290MHYw +EAYHKoZIzj0CAQYFK4EEACIDYgAEclmOmfFLoQR+mupZSc7J3IfZ6OV0IphUHWwv +iH9BvkGh4OX+RZfafa4hw90A5fk0ps520Dt04tHwotLBNkdQcWDJunOhw8ydebIP +TaP0V8OgxFs+P4kpBkMVNB3H+PK6o2MwYTAdBgNVHQ4EFgQU2ic6pZKpiyOr5aPt +YhABB9hJC5QwHwYDVR0jBBgwFoAU2ic6pZKpiyOr5aPtYhABB9hJC5QwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAgQwCgYIKoZIzj0EAwMDaAAwZQIxAMtZ ++QqC0LGdqUxdr2woMr6pUNAaZYaxm6APPqyKsjVqNaKadiSueNbbbc+seKJXbwIw +Zl0HNHzmoNAMkpgx5BCukjL1v07C571diSW4Z/P96t8tUzi/2rUOoFlJYU0B8cib +-----END CERTIFICATE----- diff --git a/bindings/rust-examples/client-hello-config-resolution/certs/generate.sh b/bindings/rust-examples/client-hello-config-resolution/certs/generate.sh new file mode 100755 index 00000000000..bc5df0a11f9 --- /dev/null +++ b/bindings/rust-examples/client-hello-config-resolution/certs/generate.sh @@ -0,0 +1,80 @@ +#!/usr/bin/env bash + +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +# immediately bail if any command fails +set -e + +echo "generating CA" +openssl req -new -noenc -x509 \ + -newkey ec \ + -pkeyopt ec_paramgen_curve:P-384 \ + -keyout ca-key.pem \ + -out ca-cert.pem \ + -days 65536 \ + -SHA384 \ + -subj "/C=US/CN=root" \ + -addext "basicConstraints = critical,CA:true" \ + -addext "keyUsage = critical,keyCertSign" + +echo "generating wombat private key and CSR" +openssl req -new -noenc \ + -newkey ec \ + -pkeyopt ec_paramgen_curve:P-384 \ + -keyout wombat-key.pem \ + -out wombat.csr \ + -subj "/C=US/CN=wombat" \ + -addext "subjectAltName = DNS:www.wombat.com" + +echo "generating kangaroo private key and CSR" +openssl req -new -noenc \ + -newkey ec \ + -pkeyopt ec_paramgen_curve:P-384 \ + -keyout kangaroo-key.pem \ + -out kangaroo.csr \ + -subj "/C=US/CN=kangaroo" \ + -addext "subjectAltName = DNS:www.kangaroo.com" + +echo "generating wombat server certificate and signing it" +openssl x509 -days 65536 \ + -req -in wombat.csr \ + -SHA384 \ + -CA ca-cert.pem \ + -CAkey ca-key.pem \ + -CAcreateserial \ + -out wombat-cert.pem \ + -copy_extensions=copyall + +echo "generating kangaroo certificate and signing it" +openssl x509 -days 65536 \ + -req -in kangaroo.csr \ + -SHA384 \ + -CA ca-cert.pem \ + -CAkey ca-key.pem \ + -CAcreateserial \ + -out kangaroo-cert.pem \ + -copy_extensions=copyall + +touch wombat-chain.pem +cat wombat-cert.pem >> wombat-chain.pem +cat ca-cert.pem >> wombat-chain.pem + +touch kangaroo-chain.pem +cat kangaroo-cert.pem >> kangaroo-chain.pem +cat ca-cert.pem >> kangaroo-chain.pem + +echo "verifying server certificates" +openssl verify -CAfile ca-cert.pem wombat-cert.pem +openssl verify -CAfile ca-cert.pem kangaroo-cert.pem + +# certificate signing requests are never used after the certs are generated +rm wombat.csr +rm kangaroo.csr +rm ca-cert.srl + +# the private keys of the CA are never needed after signing +rm ca-key.pem +rm wombat-cert.pem +rm kangaroo-cert.pem + diff --git a/bindings/rust-examples/client-hello-config-resolution/certs/kangaroo-chain.pem b/bindings/rust-examples/client-hello-config-resolution/certs/kangaroo-chain.pem new file mode 100644 index 00000000000..aae7148621d --- /dev/null +++ b/bindings/rust-examples/client-hello-config-resolution/certs/kangaroo-chain.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIB3TCCAWKgAwIBAgIUJhUgxiGTEOtg0JBtu9SrS7PPvo0wCgYIKoZIzj0EAwMw +HDELMAkGA1UEBhMCVVMxDTALBgNVBAMMBHJvb3QwIBcNMjQwMTI3MDAwODQ4WhgP +MjIwMzA3MDQwMDA4NDhaMCAxCzAJBgNVBAYTAlVTMREwDwYDVQQDDAhrYW5nYXJv +bzB2MBAGByqGSM49AgEGBSuBBAAiA2IABCzesg6GHI5tMP4JuMvpiVHsc+CStyTy +JQQZ4jyj4fVfgqCcPVo6qJq6DjPepMRkm5tLtFrdavl8/ZZpiCi5vLSymUxliFXD +9DD8GO5naaBnW2EmuYCcNrB0FJJfKZurVKNfMF0wGwYDVR0RBBQwEoIQd3d3Lmth +bmdhcm9vLmNvbTAdBgNVHQ4EFgQUNmsIZH0IDGVlSy7V6BYZTE6NX1QwHwYDVR0j +BBgwFoAU2ic6pZKpiyOr5aPtYhABB9hJC5QwCgYIKoZIzj0EAwMDaQAwZgIxAJzE +GC8hKsqTmDxI4r7bewI/vjtKyEUf0BDJfRrSLixPySYRTbx950iHMo6kXB0DEwIx +AO02gaF9weybuklR+DZ/j6EEZk4HlaRvN575vKmdDYIUF4KpFcT/8f85+5klj9Tl +Hg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIB3DCCAWKgAwIBAgIUaAjZTaFhJNRyFtFQut1CdrY7RH0wCgYIKoZIzj0EAwMw +HDELMAkGA1UEBhMCVVMxDTALBgNVBAMMBHJvb3QwIBcNMjQwMTI3MDAwODQ4WhgP +MjIwMzA3MDQwMDA4NDhaMBwxCzAJBgNVBAYTAlVTMQ0wCwYDVQQDDARyb290MHYw +EAYHKoZIzj0CAQYFK4EEACIDYgAEclmOmfFLoQR+mupZSc7J3IfZ6OV0IphUHWwv +iH9BvkGh4OX+RZfafa4hw90A5fk0ps520Dt04tHwotLBNkdQcWDJunOhw8ydebIP +TaP0V8OgxFs+P4kpBkMVNB3H+PK6o2MwYTAdBgNVHQ4EFgQU2ic6pZKpiyOr5aPt +YhABB9hJC5QwHwYDVR0jBBgwFoAU2ic6pZKpiyOr5aPtYhABB9hJC5QwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAgQwCgYIKoZIzj0EAwMDaAAwZQIxAMtZ ++QqC0LGdqUxdr2woMr6pUNAaZYaxm6APPqyKsjVqNaKadiSueNbbbc+seKJXbwIw +Zl0HNHzmoNAMkpgx5BCukjL1v07C571diSW4Z/P96t8tUzi/2rUOoFlJYU0B8cib +-----END CERTIFICATE----- diff --git a/bindings/rust-examples/client-hello-config-resolution/certs/kangaroo-key.pem b/bindings/rust-examples/client-hello-config-resolution/certs/kangaroo-key.pem new file mode 100644 index 00000000000..38a1a1b4258 --- /dev/null +++ b/bindings/rust-examples/client-hello-config-resolution/certs/kangaroo-key.pem @@ -0,0 +1,6 @@ +-----BEGIN PRIVATE KEY----- +MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDB8OJA0z/nzPkogIasW +B8xhhROb0sDbHEqYwStAdDKEWGCLGyy46/5sMprtht8bBpahZANiAAQs3rIOhhyO +bTD+CbjL6YlR7HPgkrck8iUEGeI8o+H1X4KgnD1aOqiaug4z3qTEZJubS7Ra3Wr5 +fP2WaYgouby0splMZYhVw/Qw/BjuZ2mgZ1thJrmAnDawdBSSXymbq1Q= +-----END PRIVATE KEY----- diff --git a/bindings/rust-examples/client-hello-config-resolution/certs/wombat-chain.pem b/bindings/rust-examples/client-hello-config-resolution/certs/wombat-chain.pem new file mode 100644 index 00000000000..303aaaae7f7 --- /dev/null +++ b/bindings/rust-examples/client-hello-config-resolution/certs/wombat-chain.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIB2DCCAV6gAwIBAgIUJhUgxiGTEOtg0JBtu9SrS7PPvowwCgYIKoZIzj0EAwMw +HDELMAkGA1UEBhMCVVMxDTALBgNVBAMMBHJvb3QwIBcNMjQwMTI3MDAwODQ4WhgP +MjIwMzA3MDQwMDA4NDhaMB4xCzAJBgNVBAYTAlVTMQ8wDQYDVQQDDAZ3b21iYXQw +djAQBgcqhkjOPQIBBgUrgQQAIgNiAARUye9Qgw5N7T8nk6DFoUwPVzSnQy9v4v0V +8SOUZmRwBqmFSJ9Vm988BwAcPFHdmQ13Za4XTkDbQvMmgzntIIIziiyaJQAazRFG +Y2Ex4V/YBiIsuh5wPOXjtvOtgVMXBgijXTBbMBkGA1UdEQQSMBCCDnd3dy53b21i +YXQuY29tMB0GA1UdDgQWBBS+Tbl0gagSNimLM5q2EgeBIMEAfzAfBgNVHSMEGDAW +gBTaJzqlkqmLI6vlo+1iEAEH2EkLlDAKBggqhkjOPQQDAwNoADBlAjAKqbrvk9by +G278VLs7F8uvc1mFYYWv/ZnnQIEJT8srO+P57PtC5FBId5oK28P41EUCMQCim4LR +KzY/PcdY8NlAcHu/caWvGH2+FWm7jFyr8As5oXT0swbqYmMqpaK6E2EZNIk= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIB3DCCAWKgAwIBAgIUaAjZTaFhJNRyFtFQut1CdrY7RH0wCgYIKoZIzj0EAwMw +HDELMAkGA1UEBhMCVVMxDTALBgNVBAMMBHJvb3QwIBcNMjQwMTI3MDAwODQ4WhgP +MjIwMzA3MDQwMDA4NDhaMBwxCzAJBgNVBAYTAlVTMQ0wCwYDVQQDDARyb290MHYw +EAYHKoZIzj0CAQYFK4EEACIDYgAEclmOmfFLoQR+mupZSc7J3IfZ6OV0IphUHWwv +iH9BvkGh4OX+RZfafa4hw90A5fk0ps520Dt04tHwotLBNkdQcWDJunOhw8ydebIP +TaP0V8OgxFs+P4kpBkMVNB3H+PK6o2MwYTAdBgNVHQ4EFgQU2ic6pZKpiyOr5aPt +YhABB9hJC5QwHwYDVR0jBBgwFoAU2ic6pZKpiyOr5aPtYhABB9hJC5QwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAgQwCgYIKoZIzj0EAwMDaAAwZQIxAMtZ ++QqC0LGdqUxdr2woMr6pUNAaZYaxm6APPqyKsjVqNaKadiSueNbbbc+seKJXbwIw +Zl0HNHzmoNAMkpgx5BCukjL1v07C571diSW4Z/P96t8tUzi/2rUOoFlJYU0B8cib +-----END CERTIFICATE----- diff --git a/bindings/rust-examples/client-hello-config-resolution/certs/wombat-key.pem b/bindings/rust-examples/client-hello-config-resolution/certs/wombat-key.pem new file mode 100644 index 00000000000..a21d33a6273 --- /dev/null +++ b/bindings/rust-examples/client-hello-config-resolution/certs/wombat-key.pem @@ -0,0 +1,6 @@ +-----BEGIN PRIVATE KEY----- +MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDDO2TkDRDnGfKAvjH9+ +SliejJgLp6ONEdNAgfimOEDWgfMJiyYEPp9WhZkASDVTaB2hZANiAARUye9Qgw5N +7T8nk6DFoUwPVzSnQy9v4v0V8SOUZmRwBqmFSJ9Vm988BwAcPFHdmQ13Za4XTkDb +QvMmgzntIIIziiyaJQAazRFGY2Ex4V/YBiIsuh5wPOXjtvOtgVMXBgg= +-----END PRIVATE KEY----- diff --git a/bindings/rust-examples/client-hello-config-resolution/rust-toolchain b/bindings/rust-examples/client-hello-config-resolution/rust-toolchain new file mode 100644 index 00000000000..2bf5ad0447d --- /dev/null +++ b/bindings/rust-examples/client-hello-config-resolution/rust-toolchain @@ -0,0 +1 @@ +stable diff --git a/bindings/rust-examples/client-hello-config-resolution/src/bin/client.rs b/bindings/rust-examples/client-hello-config-resolution/src/bin/client.rs new file mode 100644 index 00000000000..a9c9518cf8a --- /dev/null +++ b/bindings/rust-examples/client-hello-config-resolution/src/bin/client.rs @@ -0,0 +1,47 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +use clap::Parser; +use s2n_tls::security::DEFAULT_TLS13; + +use std::error::Error; +use tokio::{ + io::{AsyncReadExt, AsyncWriteExt}, + net::*, +}; + +const PORT: u16 = 1738; + +#[derive(Debug, Parser)] +struct Cli { + /// value to specify for the SNI + sni: String, +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + let args = Cli::parse(); + let mut config = s2n_tls::config::Config::builder(); + let ca: Vec = std::fs::read(env!("CARGO_MANIFEST_DIR").to_owned() + "/certs/ca-cert.pem")?; + config.set_security_policy(&DEFAULT_TLS13)?; + config.trust_pem(&ca)?; + + let client = s2n_tls_tokio::TlsConnector::new(config.build()?); + let stream = TcpStream::connect(("127.0.0.1", PORT)).await?; + // request a TLS connection on the TCP stream while setting the sni + let mut tls = match client.connect(&args.sni, stream).await { + Ok(tls) => tls, + Err(e) => { + println!("error during handshake: {:?}", e); + return Ok(()); + } + }; + println!("{:#?}", tls); + + let mut server_response = String::new(); + tls.read_to_string(&mut server_response).await?; + println!("The server said {server_response}"); + tls.shutdown().await?; + + Ok(()) +} diff --git a/bindings/rust-examples/client-hello-config-resolution/src/bin/server.rs b/bindings/rust-examples/client-hello-config-resolution/src/bin/server.rs new file mode 100644 index 00000000000..7ad887cfa0b --- /dev/null +++ b/bindings/rust-examples/client-hello-config-resolution/src/bin/server.rs @@ -0,0 +1,129 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +use s2n_tls::{ + callbacks::{ClientHelloCallback, ConnectionFuture}, + security::{Policy, DEFAULT_TLS13}, +}; +use s2n_tls_tokio::TlsAcceptor; +use std::{collections::HashMap, error::Error, pin::Pin}; +use tokio::{io::AsyncWriteExt, net::*}; + +const PORT: u16 = 1738; + +/// Used by the server to resolve the appropriate config +pub struct AnimalConfigResolver { + // this stores the mapping from sni -> config + configs: HashMap, +} + +impl Default for AnimalConfigResolver { + fn default() -> Self { + let mut configs = HashMap::new(); + configs.insert("www.wombat.com".to_owned(), server_config("wombat")); + configs.insert("www.kangaroo.com".to_owned(), server_config("kangaroo")); + Self { configs } + } +} + +// Servers that wish to do config resolution in an async manner should consider +// using the ConfigResolver: https://docs.rs/s2n-tls/latest/s2n_tls/callbacks/struct.ConfigResolver.html# +// This is useful if servers need to read from disk or make network calls as part +// of the configuration, and want to avoid blocking the tokio task while doing so. +impl ClientHelloCallback for AnimalConfigResolver { + fn on_client_hello( + &self, + connection: &mut s2n_tls::connection::Connection, + ) -> Result>>, s2n_tls::error::Error> { + let sni = match connection.server_name() { + Some(sni) => sni, + None => { + println!("connection contained no SNI"); + return Err(s2n_tls::error::Error::application("no sni".into())); + } + }; + let config_ref = match self.configs.get(sni) { + Some(c) => c, + None => { + println!("unknown sni: {sni}"); + return Err(s2n_tls::error::Error::application("unknown sni".into())); + } + }; + println!("setting connection config associated with {sni}"); + let config = config_ref.clone(); + connection.set_config(config).unwrap(); + // Inform s2n-tls that the server name was used in configuration so that + // the appropriate extension is sent back to the peer. + // From RFC 6066, section 3 + // > A server that receives a client hello containing the "server_name" + // > extension MAY use the information contained in the extension to guide + // > its selection of an appropriate certificate to return to the client, + // > and/or other aspects of security policy. In this event, the server + // > SHALL include an extension of type "server_name" in the (extended) + // > server hello. The "extension_data" field of this extension SHALL be + // > empty. + // https://datatracker.ietf.org/doc/html/rfc6066#section-3 + connection.server_name_extension_used(); + // Ok -> the function completed successfully + // None -> s2n-tls doesn't need to poll this to completion + Ok(None) + } +} + +fn server_config(animal: &str) -> s2n_tls::config::Config { + let cert_path = format!("{}/certs/{}-chain.pem", env!("CARGO_MANIFEST_DIR"), animal); + let key_path = format!("{}/certs/{}-key.pem", env!("CARGO_MANIFEST_DIR"), animal); + let cert = std::fs::read(cert_path).unwrap(); + let key = std::fs::read(key_path).unwrap(); + let mut config = s2n_tls::config::Builder::new(); + + // we can set different policies for different configs. "20190214" doesn't + // support TLS 1.3, so any customer requesting www.wombat.com won't be able + // to negotiate TLS 1.3 + let security_policy = match animal { + "wombat" => Policy::from_version("20190214").unwrap(), + _ => DEFAULT_TLS13, + }; + config.set_security_policy(&security_policy).unwrap(); + config.load_pem(&cert, &key).unwrap(); + config.build().unwrap() +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + let resolver = AnimalConfigResolver::default(); + + // this is the initial config that "receives" the connection. Since we error + // out on an unrecognized SNI, the only important setting on this config is + // the client hello callback that sets the config used for the rest of the + // connection. + let mut initial_config = s2n_tls::config::Builder::new(); + initial_config.set_client_hello_callback(resolver)?; + + let server = TlsAcceptor::new(initial_config.build()?); + + let listener = TcpListener::bind(&format!("0.0.0.0:{PORT}")).await?; + loop { + let server = server.clone(); + let (stream, _) = listener.accept().await?; + tokio::spawn(async move { + // handshake with the client + let handshake = server.accept(stream).await; + let mut tls = match handshake { + Ok(tls) => tls, + Err(e) => { + println!("error during handshake: {:?}", e); + return Ok(()); + } + }; + + let connection = tls.as_ref(); + let offered_sni = connection.server_name().unwrap(); + let _ = tls + .write(format!("Hello, you are speaking to {offered_sni}").as_bytes()) + .await?; + tls.shutdown().await?; + Ok::<(), Box>(()) + }); + } +}