diff --git a/h3i/src/client/sync_client.rs b/h3i/src/client/sync_client.rs index 4ed2c9ef1e..eb6059c88d 100644 --- a/h3i/src/client/sync_client.rs +++ b/h3i/src/client/sync_client.rs @@ -67,7 +67,11 @@ pub fn connect( let mut events = mio::Events::with_capacity(1024); // We'll only connect to one server. - let connect_url = args.host_port.split(':').next().unwrap(); + let connect_url = if !args.omit_sni { + args.host_port.split(':').next() + } else { + None + }; // Resolve server address. let peer_addr = if let Some(addr) = &args.connect_to { @@ -148,14 +152,9 @@ pub fn connect( let local_addr = socket.local_addr().unwrap(); // Create a QUIC connection and initiate handshake. - let mut conn = quiche::connect( - Some(connect_url), - &scid, - local_addr, - peer_addr, - &mut config, - ) - .unwrap(); + let mut conn = + quiche::connect(connect_url, &scid, local_addr, peer_addr, &mut config) + .unwrap(); if let Some(keylog) = &mut keylog { if let Ok(keylog) = keylog.try_clone() { diff --git a/h3i/src/config.rs b/h3i/src/config.rs index d876f8c652..77fbb8dba0 100644 --- a/h3i/src/config.rs +++ b/h3i/src/config.rs @@ -33,6 +33,8 @@ pub struct Config { /// A string representing the host and port to connect to using the format /// `:`. pub host_port: String, + /// If the SNI should be omitted during the TLS handshake. + pub omit_sni: bool, /// Set a specific IP address to connect to, rather than use DNS resolution. pub connect_to: Option, /// The source port to use when connecting to a server. @@ -72,6 +74,11 @@ impl Config { self } + pub fn omit_sni(mut self) -> Self { + self.omit_sni = true; + self + } + pub fn with_connect_to(mut self, connect_to: String) -> Self { self.connect_to = Some(connect_to); self @@ -146,6 +153,7 @@ impl Config { Ok(Config { host_port: self.host_port, + omit_sni: self.omit_sni, connect_to: self.connect_to, source_port: self.source_port, verify_peer: self.verify_peer, diff --git a/h3i/src/main.rs b/h3i/src/main.rs index c99f35fda6..309cd7ca75 100644 --- a/h3i/src/main.rs +++ b/h3i/src/main.rs @@ -96,6 +96,13 @@ fn config_from_clap() -> std::result::Result { .required(true) .index(1), ) + .arg( + Arg::with_name("omit-sni") + .long("omit-sni") + .help("Omit the SNI from the TLS handshake") + // Requires an OsStr, so we can parse to empty later on + .takes_value(false) + ) .arg( Arg::with_name("connect-to") .long("connect-to") @@ -184,6 +191,7 @@ fn config_from_clap() -> std::result::Result { .get_matches(); let host_port = matches.value_of("host:port").unwrap().to_string(); + let omit_sni = matches.is_present("omit-sni"); let connect_to: Option = matches.value_of("connect-to").map(|s| s.to_string()); let verify_peer = !matches.is_present("no-verify"); @@ -252,6 +260,7 @@ fn config_from_clap() -> std::result::Result { let library_config = h3i::config::Config { host_port, + omit_sni, connect_to, source_port: 0, verify_peer, diff --git a/h3i/src/prompts/h3/errors.rs b/h3i/src/prompts/h3/errors.rs index a158176775..dac4a3766e 100644 --- a/h3i/src/prompts/h3/errors.rs +++ b/h3i/src/prompts/h3/errors.rs @@ -1,3 +1,30 @@ +// Copyright (C) 2024, Cloudflare, Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +//! QUIC and HTTP/3 errors for the h3i client. use inquire::error::InquireResult; use inquire::validator::Validation; use inquire::CustomUserError; diff --git a/h3i/src/prompts/h3/headers.rs b/h3i/src/prompts/h3/headers.rs index 0ad71fe3a2..bcabd714a1 100644 --- a/h3i/src/prompts/h3/headers.rs +++ b/h3i/src/prompts/h3/headers.rs @@ -117,6 +117,7 @@ pub fn prompt_push_promise() -> InquireResult { fn pseudo_headers(host_port: &str) -> InquireResult> { let method = Text::new("method:") .with_autocomplete(&method_suggester) + .with_default("GET") .with_help_message(ESC_TO_RET) .prompt()?; @@ -126,10 +127,10 @@ fn pseudo_headers(host_port: &str) -> InquireResult> { .with_help_message(&help) .prompt()?; - let path = Text::new("path:").prompt()?; + let path = Text::new("path:").with_default("/").prompt()?; let scheme = Text::new("scheme:") - .with_autocomplete(&scheme_suggester) + .with_default("https") .with_help_message(ESC_TO_RET) .prompt()?; @@ -170,12 +171,6 @@ fn method_suggester(val: &str) -> SuggestionResult> { squish_suggester(&suggestions, val) } -fn scheme_suggester(val: &str) -> SuggestionResult> { - let suggestions = ["https"]; - - squish_suggester(&suggestions, val) -} - fn validate_stream_id(id: &str) -> SuggestionResult { if id.is_empty() { return Ok(Validation::Valid);