Compare commits

...

3 commits

4 changed files with 56 additions and 52 deletions

View file

@ -12,15 +12,25 @@ categories = ["network-programming"]
[dependencies] [dependencies]
base16ct = "0.2.0" base16ct = "0.2.0"
base64ct = "1.6.0" base64ct = "1.6.0"
bytes = "1.7.1"
dashmap = { version = "6.0.1", optional = true }
mime = "0.3.17"
num_enum = "0.7.3"
sha2 = "0.10.8" sha2 = "0.10.8"
num_enum = "0.7.3"
bytes = "1.7.1"
mime = "0.3.17"
url = "2.5.2"
tokio = { version = "1.39.2", features = ["io-util", "net"] } tokio = { version = "1.39.2", features = ["io-util", "net"] }
tokio-rustls = { version = "0.26.0", default-features = false, features = ["ring"] } tokio-rustls = { version = "0.26.0", default-features = false, features = ["ring"] }
url = "2.5.2" webpki-roots = { version = "0.26.3", optional = true }
webpki-roots = "0.26.3"
dashmap = { version = "6.0.1", optional = true }
[dev-dependencies]
tokio = { version = "1.39.2", features = ["macros", "rt-multi-thread"] }
[features]
webpki = ["dep:webpki-roots"]
file-sscv = ["dep:dashmap", "tokio/fs"]
[[example]] [[example]]
name = "simple" name = "simple"
@ -30,9 +40,3 @@ path = "examples/simple.rs"
name = "main" name = "main"
path = "examples/main.rs" path = "examples/main.rs"
required-features = ["file-sscv"] required-features = ["file-sscv"]
[dev-dependencies]
tokio = { version = "1.39.2", features = ["macros", "rt-multi-thread"] }
[features]
file-sscv = ["dep:dashmap", "tokio/fs"]

View file

@ -24,15 +24,21 @@ impl ServerCertVerifier for CustomCertVerifier {
fn verify_server_cert( fn verify_server_cert(
&self, &self,
end_entity: &CertificateDer<'_>, end_entity: &CertificateDer<'_>,
intermediates: &[CertificateDer<'_>], _intermediates: &[CertificateDer<'_>],
server_name: &ServerName<'_>, server_name: &ServerName<'_>,
ocsp_response: &[u8], _ocsp_response: &[u8],
now: UnixTime, now: UnixTime,
) -> Result<ServerCertVerified, rustls::Error> { ) -> Result<ServerCertVerified, rustls::Error> {
// if webpki CA certs enabled // if webpki CA certs enabled
#[cfg(feature = "webpki")]
if let Some(wv) = &self.webpki_verifier { if let Some(wv) = &self.webpki_verifier {
match wv.verify_server_cert(end_entity, intermediates, server_name, ocsp_response, now) match wv.verify_server_cert(
{ end_entity,
_intermediates,
server_name,
_ocsp_response,
now,
) {
Ok(verified) => { Ok(verified) => {
return Ok(verified); return Ok(verified);
} }

View file

@ -7,12 +7,10 @@ use crate::{
Client, Client,
}; };
use tokio_rustls::rustls::{ use tokio_rustls::rustls::{self, client::danger::ServerCertVerifier, SupportedProtocolVersion};
self,
client::{danger::ServerCertVerifier, WebPkiServerVerifier}, #[cfg(feature = "webpki")]
pki_types::TrustAnchor, use tokio_rustls::rustls::{client::WebPkiServerVerifier, pki_types::TrustAnchor};
SupportedProtocolVersion,
};
/// Builder for creating configured [`Client`] instance /// Builder for creating configured [`Client`] instance
pub struct ClientBuilder { pub struct ClientBuilder {
@ -58,24 +56,32 @@ impl ClientBuilder {
let tls_config = if let Some(cv) = self.custom_verifier { let tls_config = if let Some(cv) = self.custom_verifier {
tls_config.dangerous().with_custom_certificate_verifier(cv) tls_config.dangerous().with_custom_certificate_verifier(cv)
} else if let Some(ssv) = self.ss_verifier { } else if let Some(ssv) = self.ss_verifier {
let webpki_verifier = {
#[cfg(feature = "webpki")]
if !self.root_certs.is_empty() {
Some(
WebPkiServerVerifier::builder_with_provider(
Arc::new(self.root_certs),
provider.clone(),
)
.build()
// panics only if roots are empty (that is checked above)
// or CRLs couldn't be parsed (we didn't provide any)
.unwrap(),
)
} else {
None
}
#[cfg(not(feature = "webpki"))]
None
};
tls_config tls_config
.dangerous() .dangerous()
.with_custom_certificate_verifier(Arc::new(CustomCertVerifier { .with_custom_certificate_verifier(Arc::new(CustomCertVerifier {
provider: provider.clone(), provider: provider.clone(),
webpki_verifier: if !self.root_certs.is_empty() { webpki_verifier,
Some(
WebPkiServerVerifier::builder_with_provider(
Arc::new(self.root_certs),
provider,
)
.build()
// panics only if roots are empty (that is checked above)
// or CRLs couldn't be parsed (we didn't provide any)
.unwrap(),
)
} else {
None
},
ss_allowed: true, ss_allowed: true,
ss_verifier: ssv, ss_verifier: ssv,
})) }))
@ -102,6 +108,7 @@ impl ClientBuilder {
/// Include webpki trust anchors. /// Include webpki trust anchors.
/// Not recommended (useless) as most Gemini capsules use self-signed /// Not recommended (useless) as most Gemini capsules use self-signed
/// TLS certs and properly configured TOFU policy is enough. /// TLS certs and properly configured TOFU policy is enough.
#[cfg(feature = "webpki")]
pub fn with_webpki_roots(mut self) -> Self { pub fn with_webpki_roots(mut self) -> Self {
self.root_certs self.root_certs
.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned()); .extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
@ -110,6 +117,7 @@ impl ClientBuilder {
/// Include custom trust anchors. /// Include custom trust anchors.
/// Not recommended (useless), see note for [`ClientBuilder::with_webpki_roots`]. /// Not recommended (useless), see note for [`ClientBuilder::with_webpki_roots`].
#[cfg(feature = "webpki")]
pub fn with_custom_roots( pub fn with_custom_roots(
mut self, mut self,
iter: impl IntoIterator<Item = TrustAnchor<'static>>, iter: impl IntoIterator<Item = TrustAnchor<'static>>,

View file

@ -8,7 +8,6 @@ pub use response::Response;
use crate::{error::*, status::*}; use crate::{error::*, status::*};
use builder::ClientBuilder; use builder::ClientBuilder;
use std::net::ToSocketAddrs;
use std::sync::Arc; use std::sync::Arc;
use tokio::{ use tokio::{
@ -25,19 +24,6 @@ pub struct Client {
connector: TlsConnector, connector: TlsConnector,
} }
impl Default for Client {
/// Create a Client with webpki_roots trust anchors and no client auth cert.
/// Will be possibly removed in next versions.
fn default() -> Self {
let roots =
rustls::RootCertStore::from_iter(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
let config = rustls::ClientConfig::builder()
.with_root_certificates(roots)
.with_no_client_auth();
Client::from(config)
}
}
impl From<rustls::ClientConfig> for Client { impl From<rustls::ClientConfig> for Client {
/// Create a Client from a Rustls config. /// Create a Client from a Rustls config.
#[inline] #[inline]
@ -107,8 +93,8 @@ impl Client {
host: &str, host: &str,
port: u16, port: u16,
) -> Result<Response, LibError> { ) -> Result<Response, LibError> {
let addr = (host, port) let addr = tokio::net::lookup_host((host, port))
.to_socket_addrs()? .await?
.next() .next()
.ok_or(InvalidUrl::ConvertError)?; .ok_or(InvalidUrl::ConvertError)?;