Skip to content

Commit

Permalink
Merge c1606ca into e0a4237
Browse files Browse the repository at this point in the history
  • Loading branch information
joshuef committed Jan 18, 2022
2 parents e0a4237 + c1606ca commit c418115
Show file tree
Hide file tree
Showing 9 changed files with 166 additions and 464 deletions.
11 changes: 6 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,22 @@ bincode = "1.2.1"
bytes = { version = "1.0.1", features = ["serde"] }
futures = "~0.3.8"
igd = { version = "0.12.0", optional = true, features = ["aio"] }
quinn = { version = "0.7.0", default-features = false, features = ["tls-rustls"] }
quinn-proto = "0.7.3"
quinn = { version = "0.8.0", default-features = false, features = ["tls-rustls"] }
quinn-proto = "0.8.0"
rcgen = "~0.8.4"
rustls = { version = "0.19.0", features = ["dangerous_configuration"] }
serde = { version = "1.0.117", features = ["derive"] }
thiserror = "1.0.23"
tokio = { version = "1.2.0", features = ["sync"] }
tokio = { version = "1.12.0", features = ["sync"] }
tracing = "~0.1.26"
webpki = "~0.21.3"
rustls = { version = "0.20.2", default-features = false, features = ["quic", "dangerous_configuration"] }

[dev-dependencies]
color-eyre = "0.5.11"
ctor = "0.1.20"
rand = "~0.7.3"
tiny-keccak = { version = "2.0.2", features = ["sha3"] }
tokio = { version = "1.2.0", features = ["macros"] }
tokio = { version = "1.12.0", features = ["macros", "rt-multi-thread"] }
tracing-subscriber = "0.2.19"
tracing-test = "0.1.0"
quinn = { version = "0.8.0", default-features = false, features = ["tls-rustls", "native-certs"] }
2 changes: 1 addition & 1 deletion examples/p2p_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ async fn main() -> Result<()> {
let args: Vec<String> = env::args().collect();

// create an endpoint for us to listen on and send from.
let (node, mut incoming_conns, _contact) = Endpoint::new(
let (node, mut incoming_conns, _contact) = Endpoint::new_peer(
SocketAddr::from((Ipv4Addr::LOCALHOST, 0)),
&[],
Config {
Expand Down
130 changes: 73 additions & 57 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,12 @@

//! Configuration for `Endpoint`s.

use quinn::IdleTimeout;

use rustls::{Certificate, ClientConfig, ServerName};
use serde::{Deserialize, Serialize};
use std::{future::Future, net::IpAddr, sync::Arc, time::Duration};

/// Default for [`Config::idle_timeout`] (1 minute).
///
/// This is based on average time in which routers would close the UDP mapping to the peer if they
/// see no conversation between them.
pub const DEFAULT_IDLE_TIMEOUT: Duration = Duration::from_secs(60);

/// Default for [`Config::upnp_lease_duration`] (2 minutes).
pub const DEFAULT_UPNP_LEASE_DURATION: Duration = Duration::from_secs(120);

Expand All @@ -27,6 +24,12 @@ pub const DEFAULT_UPNP_LEASE_DURATION: Duration = Duration::from_secs(120);
/// gives 5-6 retries in ~30 s total retry time.
pub const DEFAULT_INITIAL_RETRY_INTERVAL: Duration = Duration::from_millis(500);

/// Default for [`Config::idle_timeout`] (1 minute).
///
/// This is based on average time in which routers would close the UDP mapping to the peer if they
/// see no conversation between them.
pub const DEFAULT_IDLE_TIMEOUT: Duration = Duration::from_secs(60);

/// Default for [`RetryConfig::max_retry_interval`] (15 s).
///
/// Together with the default min and multiplier,
Expand Down Expand Up @@ -57,6 +60,15 @@ pub enum ConfigError {
/// An error occurred when generating the TLS certificate.
#[error("An error occurred when generating the TLS certificate")]
CertificateGeneration(#[from] CertificateGenerationError),
/// Invalid idle timeout
#[error("An error occurred parsing idle timeout duration")]
InvalidIdleTimeout(#[from] quinn_proto::VarIntBoundsExceeded),
/// rustls error
#[error("An error occurred within rustls")]
Rustls(#[from] rustls::Error),
/// rustls error
#[error("An error occurred generaeting client config certificates")]
Webpki,
}

impl From<rcgen::RcgenError> for ConfigError {
Expand All @@ -65,17 +77,17 @@ impl From<rcgen::RcgenError> for ConfigError {
}
}

impl From<quinn::ParseError> for ConfigError {
fn from(error: quinn::ParseError) -> Self {
Self::CertificateGeneration(CertificateGenerationError(error.into()))
}
}
// impl From<quinn::ParseError> for ConfigError {
// fn from(error: quinn::ParseError) -> Self {
// Self::CertificateGeneration(CertificateGenerationError(error.into()))
// }
// }

impl From<rustls::TLSError> for ConfigError {
fn from(error: rustls::TLSError) -> Self {
Self::CertificateGeneration(CertificateGenerationError(error.into()))
}
}
// impl From<TlsError> for ConfigError {
// fn from(error: TlsError) -> Self {
// Self::CertificateGeneration(CertificateGenerationError(error.into()))
// }
// }

/// An error that occured when generating the TLS certificate.
#[derive(Debug, thiserror::Error)]
Expand Down Expand Up @@ -223,14 +235,40 @@ pub(crate) struct InternalConfig {

impl InternalConfig {
pub(crate) fn try_from_config(config: Config) -> Result<Self> {
let idle_timeout = config.idle_timeout.unwrap_or(DEFAULT_IDLE_TIMEOUT);
let default_idle_timeout: IdleTimeout = IdleTimeout::try_from(DEFAULT_IDLE_TIMEOUT)?; // 60s

// let's convert our duration to quinn's IdleTimeout
let idle_timeout = config
.idle_timeout
.map(IdleTimeout::try_from)
.unwrap_or(Ok(default_idle_timeout))
.map_err(ConfigError::from)?;
let upnp_lease_duration = config
.upnp_lease_duration
.unwrap_or(DEFAULT_UPNP_LEASE_DURATION);

let transport = Self::new_transport_config(idle_timeout, config.keep_alive_interval);
let client = Self::new_client_config(transport.clone());
let server = Self::new_server_config(transport)?;

// setup certificates
let mut roots = rustls::RootCertStore::empty();
let (cert, key) = Self::generate_cert()?;
roots.add(&cert).map_err(|_e| ConfigError::Webpki)?;

let mut client_crypto = ClientConfig::builder()
.with_safe_defaults()
.with_root_certificates(roots)
.with_no_client_auth();

// allow client to connect to unknown certificates, eg those generated above
client_crypto
.dangerous()
.set_certificate_verifier(Arc::new(SkipCertificateVerification));

let mut server = quinn::ServerConfig::with_single_cert(vec![cert], key)?;
server.transport = transport.clone();

let mut client = quinn::ClientConfig::new(Arc::new(client_crypto));
client.transport = transport;

Ok(Self {
client,
Expand All @@ -245,66 +283,44 @@ impl InternalConfig {
}

fn new_transport_config(
idle_timeout: Duration,
idle_timeout: IdleTimeout,
keep_alive_interval: Option<Duration>,
) -> Arc<quinn::TransportConfig> {
let mut config = quinn::TransportConfig::default();

// QUIC encodes idle timeout in a varint with max size 2^62, which is below what can be
// represented by Duration. For now, just ignore too large idle timeouts.
// FIXME: don't ignore (e.g. clamp/error/panic)?
let _ = config.max_idle_timeout(Some(idle_timeout)).ok();
let _ = config.max_idle_timeout(Some(idle_timeout));
let _ = config.keep_alive_interval(keep_alive_interval);

Arc::new(config)
}

fn new_client_config(transport: Arc<quinn::TransportConfig>) -> quinn::ClientConfig {
let mut config = quinn::ClientConfig {
transport,
..Default::default()
};
Arc::make_mut(&mut config.crypto)
.dangerous()
.set_certificate_verifier(Arc::new(SkipCertificateVerification));
config
}

fn new_server_config(transport: Arc<quinn::TransportConfig>) -> Result<quinn::ServerConfig> {
let (cert, key) = Self::generate_cert()?;

let mut config = quinn::ServerConfig::default();
config.transport = transport;

let mut config = quinn::ServerConfigBuilder::new(config);
let _ = config.certificate(quinn::CertificateChain::from_certs(vec![cert]), key)?;

Ok(config.build())
}

fn generate_cert() -> Result<(quinn::Certificate, quinn::PrivateKey)> {
fn generate_cert() -> Result<(Certificate, rustls::PrivateKey)> {
let cert = rcgen::generate_simple_self_signed(vec![SERVER_NAME.to_string()])?;

let cert_der = cert.serialize_der()?;
let key_der = cert.serialize_private_key_der();
let key = cert.serialize_private_key_der();
let cert = cert.serialize_der().unwrap();

Ok((
quinn::Certificate::from_der(&cert_der)?,
quinn::PrivateKey::from_der(&key_der)?,
))
let key = rustls::PrivateKey(key);
let cert = Certificate(cert);
Ok((cert, key))
}
}

struct SkipCertificateVerification;

impl rustls::ServerCertVerifier for SkipCertificateVerification {
impl rustls::client::ServerCertVerifier for SkipCertificateVerification {
fn verify_server_cert(
&self,
_roots: &rustls::RootCertStore,
_presented_certs: &[rustls::Certificate],
_dns_name: webpki::DNSNameRef,
_end_entity: &Certificate,
_intermediates: &[Certificate],
_server_name: &ServerName,
_scts: &mut dyn Iterator<Item = &[u8]>,
_ocsp_response: &[u8],
) -> Result<rustls::ServerCertVerified, rustls::TLSError> {
Ok(rustls::ServerCertVerified::assertion())
_now: std::time::SystemTime,
) -> Result<rustls::client::ServerCertVerified, rustls::Error> {
Ok(rustls::client::ServerCertVerified::assertion())
}
}
73 changes: 26 additions & 47 deletions src/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -531,7 +531,7 @@ async fn handle_endpoint_verification(
addr
);
let connection = endpoint
.connect(&addr, SERVER_NAME)
.connect(addr, SERVER_NAME)
.map_err(ConnectionError::from)?
.await?;

Expand Down Expand Up @@ -612,30 +612,25 @@ mod tests {
use bytes::Bytes;
use color_eyre::eyre::{bail, Result};
use futures::{StreamExt, TryStreamExt};
use quinn::Endpoint as QuinnEndpoint;
use std::time::Duration;

#[tokio::test]
#[tracing_test::traced_test]
async fn basic_usage() -> Result<()> {
let config = InternalConfig::try_from_config(Default::default())?;

let mut builder = quinn::Endpoint::builder();
let _ = builder
.listen(config.server.clone())
.default_client_config(config.client.clone());
let (peer1, _) = builder.bind(&local_addr())?;
let (mut peer1, _peer1_incoming) =
QuinnEndpoint::server(config.server.clone(), local_addr())?;
peer1.set_default_client_config(config.client);

let mut builder = quinn::Endpoint::builder();
let _ = builder
.listen(config.server.clone())
.default_client_config(config.client.clone());
let (peer2, peer2_incoming) = builder.bind(&local_addr())?;
let (peer2, peer2_incoming) = QuinnEndpoint::server(config.server.clone(), local_addr())?;

{
let (p1_tx, mut p1_rx) = Connection::new(
peer1.clone(),
None,
peer1.connect(&peer2.local_addr()?, SERVER_NAME)?.await?,
peer1.connect(peer2.local_addr()?, SERVER_NAME)?.await?,
);

let (p2_tx, mut p2_rx) =
Expand Down Expand Up @@ -685,23 +680,17 @@ mod tests {
..Default::default()
})?;

let mut builder = quinn::Endpoint::builder();
let _ = builder
.listen(config.server.clone())
.default_client_config(config.client.clone());
let (peer1, _) = builder.bind(&local_addr())?;
let (mut peer1, _peer1_incoming) =
QuinnEndpoint::server(config.server.clone(), local_addr())?;
peer1.set_default_client_config(config.client);

let mut builder = quinn::Endpoint::builder();
let _ = builder
.listen(config.server.clone())
.default_client_config(config.client.clone());
let (peer2, peer2_incoming) = builder.bind(&local_addr())?;
let (peer2, peer2_incoming) = QuinnEndpoint::server(config.server.clone(), local_addr())?;

// open a connection between the two peers
let (p1_tx, _) = Connection::new(
peer1.clone(),
None,
peer1.connect(&peer2.local_addr()?, SERVER_NAME)?.await?,
peer1.connect(peer2.local_addr()?, SERVER_NAME)?.await?,
);

let (_, mut p2_rx) =
Expand Down Expand Up @@ -730,26 +719,20 @@ mod tests {
}

#[tokio::test]
async fn endpoint_echo() -> Result<()> {
let config = InternalConfig::try_from_config(Default::default())?;
async fn test_endpoint_echo() -> Result<()> {
let config = InternalConfig::try_from_config(Config::default())?;

let mut builder = quinn::Endpoint::builder();
let _ = builder
.listen(config.server.clone())
.default_client_config(config.client.clone());
let (peer1, _) = builder.bind(&local_addr())?;
let (mut peer1, _peer1_incoming) =
QuinnEndpoint::server(config.server.clone(), local_addr())?;
peer1.set_default_client_config(config.client);

let mut builder = quinn::Endpoint::builder();
let _ = builder
.listen(config.server.clone())
.default_client_config(config.client.clone());
let (peer2, peer2_incoming) = builder.bind(&local_addr())?;
let (peer2, peer2_incoming) = QuinnEndpoint::server(config.server.clone(), local_addr())?;

{
let (p1_tx, _) = Connection::new(
peer1.clone(),
None,
peer1.connect(&peer2.local_addr()?, SERVER_NAME)?.await?,
peer1.connect(peer2.local_addr()?, SERVER_NAME)?.await?,
);

// we need to accept the connection on p2, or the message won't be processed
Expand Down Expand Up @@ -789,23 +772,19 @@ mod tests {
async fn endpoint_verification() -> Result<()> {
let config = InternalConfig::try_from_config(Default::default())?;

let mut builder = quinn::Endpoint::builder();
let _ = builder
.listen(config.server.clone())
.default_client_config(config.client.clone());
let (peer1, peer1_incoming) = builder.bind(&local_addr())?;
let (mut peer1, peer1_incoming) =
QuinnEndpoint::server(config.server.clone(), local_addr())?;
peer1.set_default_client_config(config.client.clone());

let mut builder = quinn::Endpoint::builder();
let _ = builder
.listen(config.server.clone())
.default_client_config(config.client.clone());
let (peer2, peer2_incoming) = builder.bind(&local_addr())?;
let (mut peer2, peer2_incoming) =
QuinnEndpoint::server(config.server.clone(), local_addr())?;
peer2.set_default_client_config(config.client);

{
let (p1_tx, _) = Connection::new(
peer1.clone(),
None,
peer1.connect(&peer2.local_addr()?, SERVER_NAME)?.await?,
peer1.connect(peer2.local_addr()?, SERVER_NAME)?.await?,
);

// we need to accept the connection on p2, or the message won't be processed
Expand Down

0 comments on commit c418115

Please sign in to comment.