From cf9479cda2d6c886e9d38d63ddd2b1a7952d9984 Mon Sep 17 00:00:00 2001 From: Kleo Dev Date: Sat, 16 Aug 2025 04:08:31 +0200 Subject: [PATCH] Working tls handshake --- Cargo.lock | 10 ++++++ Cargo.toml | 3 +- src/lib.rs | 41 +++++++++++++++++----- src/main.rs | 12 +++---- src/server.rs | 10 +++--- src/tls.rs | 94 +++++++++++++++++++++++++++++++++++++++------------ 6 files changed, 129 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f92e7a5..e73cbe3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -367,6 +367,7 @@ dependencies = [ "rustls", "rustls-native-certs", "tokio", + "webpki-roots", ] [[package]] @@ -1754,6 +1755,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki-roots" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "which" version = "4.4.2" diff --git a/Cargo.toml b/Cargo.toml index 4d07d47..40d352d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,4 +13,5 @@ rustls-native-certs = "0.8.1" reqwest = { version = "0.12.23", features = ["blocking"] } hyper = { version = "0.14", features = ["full"] } -tokio = { version = "1", features = ["full"] } \ No newline at end of file +tokio = { version = "1", features = ["full"] } +webpki-roots = "1.0.2" diff --git a/src/lib.rs b/src/lib.rs index 6c037c6..b77729c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,14 +1,39 @@ -use std::io::{Read, Write}; +use std::{ + io::{Read, Write}, + net::TcpStream, +}; pub mod server; pub mod tls; -pub fn request(gateway: &str, addr: &str) -> String { - let (mut conn, mut tcp) = tls::mask_tls(gateway).unwrap(); - let mut tls = rustls::Stream::new(&mut conn, &mut tcp); - tls.write_all(format!("ROUTE {addr}\nGET / HTTP/1.1").as_bytes()) +pub struct Request { + host: String, + path: String, +} + +impl Request { + pub fn new(url: &str) -> Self { + Self { + host: url.to_string(), + path: String::from("/"), + } + } +} + +impl Request { + pub fn send(&self) -> String { + let mut tcp = TcpStream::connect((self.host.as_str(), 443)).unwrap(); + let mut conn = tls::tls13_handshake(&self.host, &mut tcp).unwrap(); + let mut tls = rustls::Stream::new(&mut conn, &mut tcp); + write!( + tls, + "GET {} HTTP/1.1\r\nHost: {}\r\nConnection: close\r\nUser-Agent: rustls/0.23\r\n\r\n", + self.path, self.host + ) .unwrap(); - let mut resp = Vec::new(); - tls.read_to_end(&mut resp).unwrap(); - String::from_utf8_lossy(&resp).to_string() + tls.flush().unwrap(); + let mut resp = Vec::new(); + tls.read_to_end(&mut resp).unwrap(); + String::from_utf8(resp).unwrap() + } } diff --git a/src/main.rs b/src/main.rs index 193e36f..34c2747 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,6 @@ -#[tokio::main] -async fn main() { - ghostnet_rs::server::run().await.unwrap(); - // println!( - // "{}", - // ghostnet_rs::request("ghostnet-rs.onrender.com:443", "https://wikipedia.org") - // ); +use ghostnet_rs::Request; + +fn main() { + let req = Request::new("example.com"); + println!("{}", req.send()); } diff --git a/src/server.rs b/src/server.rs index f06708e..0d130ad 100644 --- a/src/server.rs +++ b/src/server.rs @@ -10,12 +10,13 @@ use tokio; const DEFAULT_TARGET: &str = "https://crackmes.one"; const GHOST_ROUTE_HEADER: &str = "ghost-route"; -async fn handle_request(req: Request) -> Result, Infallible> { +async fn handle_request(mut req: Request) -> Result, Infallible> { let target_url = req .headers() .get(GHOST_ROUTE_HEADER) .and_then(|v| v.to_str().ok()) - .unwrap_or(DEFAULT_TARGET); + .unwrap_or(DEFAULT_TARGET) + .to_string(); let url_params = req .uri() @@ -38,7 +39,7 @@ async fn handle_request(req: Request) -> Result, Infallible ( HeaderName::from_str("host").unwrap(), HeaderValue::from_bytes( - reqwest::Url::from_str(target_url) + reqwest::Url::from_str(&target_url) .unwrap() .host_str() .unwrap() @@ -54,7 +55,8 @@ async fn handle_request(req: Request) -> Result, Infallible } }) .collect::>(), - )); + )) + .body(hyper::body::to_bytes(req.body_mut()).await.unwrap()); let response = builder.send().await.unwrap(); diff --git a/src/tls.rs b/src/tls.rs index 7b4ff67..be3d7a5 100644 --- a/src/tls.rs +++ b/src/tls.rs @@ -1,27 +1,79 @@ -use rustls::ClientConfig; -use rustls::client::ClientConnection; -use std::{net::TcpStream, sync::Arc}; +use std::net::TcpStream; +use std::sync::Arc; -pub fn mask_tls<'a>( - addr: &str, -) -> Result<(ClientConnection, TcpStream), Box> { - let certs = rustls_native_certs::load_native_certs() - .expect("could not load platform certificate store"); - let mut root_store = rustls::RootCertStore::empty(); - for cert in certs { - root_store.add(cert).unwrap(); +use rustls::client::ClientConfig; +use rustls::pki_types::ServerName; +use rustls::{ClientConnection, RootCertStore}; + +pub fn root_store() -> RootCertStore { + // Prefer system roots (works on most OSes). If that fails, fall back to webpki-roots. + let mut store = RootCertStore::empty(); + + // Try load native (ignore per-cert errors, just skip bad ones) + for cert in rustls_native_certs::load_native_certs().certs { + let _ = store.add(cert); } - // Build TLS client config - let config = ClientConfig::builder() - .with_root_certificates(root_store) + if store.is_empty() { + // Fallback: baked-in Mozilla roots via webpki-roots + store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned()); + } + + store +} + +pub fn tls13_config() -> Arc { + let mut cfg = ClientConfig::builder() + .with_root_certificates(root_store()) .with_no_client_auth(); - let arc_cfg = Arc::new(config); - let conn = ClientConnection::new( - arc_cfg, - addr.split_once(":").unwrap().0.to_string().try_into()?, - )?; - let tcp = TcpStream::connect(addr)?; - Ok((conn, tcp)) + // ALPN (optional but typical) + cfg.alpn_protocols = vec![b"http/1.1".to_vec()]; + + // Pin to TLS 1.3 only + // cfg.versions = vec![rustls::version::TLS13]; + + Arc::new(cfg) } + +pub fn tls13_handshake( + host: &str, + tcp: &mut TcpStream, +) -> Result> { + // SNI + config + let server_name = ServerName::try_from(host.to_string())?; + let mut conn = ClientConnection::new(tls13_config(), server_name)?; + + // Drive the handshake to completion (blocking) + while conn.is_handshaking() { + // complete_io performs any pending write(s) and then tries to read. + // It returns Ok((nw, nr)) when some I/O happened; errors propagate. + let _ = conn.complete_io(tcp)?; + } + + Ok(conn) +} + +// fn main() -> anyhow::Result<()> { +// let host = "example.com"; + +// // 1) TLS 1.3 handshake over a TcpStream +// let (mut conn, mut tcp) = tls13_handshake(host, 443)?; + +// // 2) After handshake, you can wrap into a rustls::Stream to do Read/Write of app data +// let mut tls = rustls::Stream::new(&mut conn, &mut tcp); + +// // Simple HTTP/1.1 GET (for demonstration) +// write!( +// tls, +// "GET / HTTP/1.1\r\nHost: {host}\r\nConnection: close\r\nUser-Agent: rustls/0.23\r\n\r\n" +// )?; +// tls.flush()?; + +// // Read response +// let mut resp = Vec::new(); +// tls.read_to_end(&mut resp)?; +// println!("{}", String::from_utf8_lossy(&resp)); + +// Ok(()) +// }