diff --git a/Cargo.toml b/Cargo.toml index d4ac26e..c59acb8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "qtun" -version = "0.1.0" +version = "0.2.0" authors = ["Max Lv "] repository = "https://github.com/madeye/qtun" license = "MIT" @@ -17,10 +17,13 @@ path = "src/server.rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -tokio = { version = "^0.2.7", features = ["full"] } +tokio = { version = "1", features = ["full"] } bytes = "0.5" futures = "0.3" -quinn = "0.6" +rustls = "0.20" +rustls-pemfile = "0.2.1" +rustls-native-certs = "0.6.1" +quinn = "0.8" structopt = "0.3" anyhow = "1.0" tracing = "0.1" diff --git a/src/client.rs b/src/client.rs index aba72b0..f134fdf 100644 --- a/src/client.rs +++ b/src/client.rs @@ -2,7 +2,6 @@ use std::net::SocketAddr; use std::sync::Arc; use tokio::net::{TcpListener, TcpStream}; -use tokio::prelude::*; use anyhow::{anyhow, Result}; use futures::future::try_join; @@ -13,7 +12,8 @@ use structopt::{self, StructOpt}; use env_logger::Builder; use log::LevelFilter; -use qtun::args; +mod args; +mod common; #[derive(StructOpt, Debug)] #[structopt(name = "qtun-client")] @@ -55,11 +55,20 @@ async fn main() -> Result<()> { } } - let mut endpoint = quinn::Endpoint::builder(); - let client_config = quinn::ClientConfigBuilder::default(); - endpoint.default_client_config(client_config.build()); + let mut roots = rustls::RootCertStore::empty(); + for cert in rustls_native_certs::load_native_certs().expect("could not load platform certs") { + roots.add(&rustls::Certificate(cert.0)).unwrap(); + } + + let mut client_crypto = rustls::ClientConfig::builder() + .with_safe_defaults() + .with_root_certificates(roots) + .with_no_client_auth(); + + client_crypto.alpn_protocols = common::ALPN_QUIC_HTTP.iter().map(|&x| x.into()).collect(); - let (endpoint, _) = endpoint.bind(&"[::]:0".parse().unwrap())?; + let mut endpoint = quinn::Endpoint::client("[::]:0".parse().unwrap())?; + endpoint.set_default_client_config(quinn::ClientConfig::new(Arc::new(client_crypto))); let remote = Arc::::from(relay_addr); let host = Arc::::from(host); @@ -67,7 +76,7 @@ async fn main() -> Result<()> { info!("listening on {}", listen_addr); - let mut listener = TcpListener::bind(listen_addr).await?; + let listener = TcpListener::bind(listen_addr).await?; while let Ok((inbound, _)) = listener.accept().await { info!("connection incoming"); @@ -90,7 +99,7 @@ async fn transfer( mut inbound: TcpStream, ) -> Result<()> { let new_conn = endpoint - .connect(&remote, &host)? + .connect(*remote, &host)? .await .map_err(|e| anyhow!("failed to connect: {}", e))?; @@ -104,8 +113,8 @@ async fn transfer( .await .map_err(|e| anyhow!("failed to open stream: {}", e))?; - let client_to_server = io::copy(&mut ri, &mut wo); - let server_to_client = io::copy(&mut ro, &mut wi); + let client_to_server = tokio::io::copy(&mut ri, &mut wo); + let server_to_client = tokio::io::copy(&mut ro, &mut wi); try_join(client_to_server, server_to_client).await?; diff --git a/src/common/mod.rs b/src/common/mod.rs new file mode 100644 index 0000000..17a3140 --- /dev/null +++ b/src/common/mod.rs @@ -0,0 +1,2 @@ +#[allow(unused)] +pub const ALPN_QUIC_HTTP: &[&[u8]] = &[b"hq-29"]; diff --git a/src/lib.rs b/src/lib.rs index 6e10f4a..2f19fba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1 +1,2 @@ pub mod args; +pub mod common; diff --git a/src/server.rs b/src/server.rs index a2002c0..7346734 100644 --- a/src/server.rs +++ b/src/server.rs @@ -12,9 +12,9 @@ use log::LevelFilter; use log::{error, info}; use structopt::{self, StructOpt}; use tokio::net::TcpStream; -use tokio::prelude::*; -use qtun::args; +mod args; +mod common; #[derive(StructOpt, Debug)] #[structopt(name = "qtun-server")] @@ -60,16 +60,6 @@ async fn main() -> Result<()> { let options = Opt::from_args(); - let mut transport_config = quinn::TransportConfig::default(); - transport_config.stream_window_uni(0); - let mut server_config = quinn::ServerConfig::default(); - server_config.transport = Arc::new(transport_config); - let mut server_config = quinn::ServerConfigBuilder::new(server_config); - - if options.stateless_retry { - server_config.use_stateless_retry(true); - } - // init all parameters let mut cert_path = options.cert; let mut key_path = options.key; @@ -106,31 +96,55 @@ async fn main() -> Result<()> { info!("loading cert: {:?}", cert_path); info!("loading key: {:?}", key_path); - // load certificates - let key = fs::read(&key_path).context("failed to read private key")?; + let key = fs::read(key_path.clone()).context("failed to read private key")?; let key = if key_path.extension().map_or(false, |x| x == "der") { - quinn::PrivateKey::from_der(&key)? + rustls::PrivateKey(key) } else { - quinn::PrivateKey::from_pem(&key)? + let pkcs8 = rustls_pemfile::pkcs8_private_keys(&mut &*key) + .context("malformed PKCS #8 private key")?; + match pkcs8.into_iter().next() { + Some(x) => rustls::PrivateKey(x), + None => { + let rsa = rustls_pemfile::rsa_private_keys(&mut &*key) + .context("malformed PKCS #1 private key")?; + match rsa.into_iter().next() { + Some(x) => rustls::PrivateKey(x), + None => { + anyhow::bail!("no private keys found"); + } + } + } + } }; - let cert_chain = fs::read(&cert_path).context("failed to read certificate chain")?; - let cert_chain = if cert_path.extension().map_or(false, |x| x == "der") { - quinn::CertificateChain::from_certs(quinn::Certificate::from_der(&cert_chain)) + let certs = fs::read(cert_path.clone()).context("failed to read certificate chain")?; + let certs = if cert_path.extension().map_or(false, |x| x == "der") { + vec![rustls::Certificate(certs)] } else { - quinn::CertificateChain::from_pem(&cert_chain)? + rustls_pemfile::certs(&mut &*certs) + .context("invalid PEM-encoded certificate")? + .into_iter() + .map(rustls::Certificate) + .collect() }; - server_config.certificate(cert_chain, key)?; - let mut endpoint = quinn::Endpoint::builder(); - endpoint.listen(server_config.build()); + let mut server_crypto = rustls::ServerConfig::builder() + .with_safe_defaults() + .with_no_client_auth() + .with_single_cert(certs, key)?; + server_crypto.alpn_protocols = common::ALPN_QUIC_HTTP.iter().map(|&x| x.into()).collect(); + + let mut server_config = quinn::ServerConfig::with_crypto(Arc::new(server_crypto)); + Arc::get_mut(&mut server_config.transport) + .unwrap() + .max_concurrent_uni_streams(0_u8.into()); + if options.stateless_retry { + server_config.use_retry(true); + } let remote = Arc::::from(relay_addr); - let mut incoming = { - let (endpoint, incoming) = endpoint.bind(&listen_addr)?; - info!("listening on {}", endpoint.local_addr()?); - incoming - }; + let (endpoint, mut incoming) = quinn::Endpoint::server(server_config, listen_addr)?; + eprintln!("listening on {}", endpoint.local_addr()?); while let Some(conn) = incoming.next().await { info!("connection incoming"); @@ -183,8 +197,8 @@ async fn transfer( let (mut wi, mut ri) = inbound; let (mut ro, mut wo) = outbound.split(); - let client_to_server = io::copy(&mut ri, &mut wo); - let server_to_client = io::copy(&mut ro, &mut wi); + let client_to_server = tokio::io::copy(&mut ri, &mut wo); + let server_to_client = tokio::io::copy(&mut ro, &mut wi); try_join(client_to_server, server_to_client).await?;