Working tls handshake

This commit is contained in:
2025-08-16 04:08:31 +02:00
parent 506d57e6f9
commit cf9479cda2
6 changed files with 129 additions and 41 deletions
Generated
+10
View File
@@ -367,6 +367,7 @@ dependencies = [
"rustls", "rustls",
"rustls-native-certs", "rustls-native-certs",
"tokio", "tokio",
"webpki-roots",
] ]
[[package]] [[package]]
@@ -1754,6 +1755,15 @@ dependencies = [
"wasm-bindgen", "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]] [[package]]
name = "which" name = "which"
version = "4.4.2" version = "4.4.2"
+2 -1
View File
@@ -13,4 +13,5 @@ rustls-native-certs = "0.8.1"
reqwest = { version = "0.12.23", features = ["blocking"] } reqwest = { version = "0.12.23", features = ["blocking"] }
hyper = { version = "0.14", features = ["full"] } hyper = { version = "0.14", features = ["full"] }
tokio = { version = "1", features = ["full"] } tokio = { version = "1", features = ["full"] }
webpki-roots = "1.0.2"
+33 -8
View File
@@ -1,14 +1,39 @@
use std::io::{Read, Write}; use std::{
io::{Read, Write},
net::TcpStream,
};
pub mod server; pub mod server;
pub mod tls; pub mod tls;
pub fn request(gateway: &str, addr: &str) -> String { pub struct Request {
let (mut conn, mut tcp) = tls::mask_tls(gateway).unwrap(); host: String,
let mut tls = rustls::Stream::new(&mut conn, &mut tcp); path: String,
tls.write_all(format!("ROUTE {addr}\nGET / HTTP/1.1").as_bytes()) }
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(); .unwrap();
let mut resp = Vec::new(); tls.flush().unwrap();
tls.read_to_end(&mut resp).unwrap(); let mut resp = Vec::new();
String::from_utf8_lossy(&resp).to_string() tls.read_to_end(&mut resp).unwrap();
String::from_utf8(resp).unwrap()
}
} }
+5 -7
View File
@@ -1,8 +1,6 @@
#[tokio::main] use ghostnet_rs::Request;
async fn main() {
ghostnet_rs::server::run().await.unwrap(); fn main() {
// println!( let req = Request::new("example.com");
// "{}", println!("{}", req.send());
// ghostnet_rs::request("ghostnet-rs.onrender.com:443", "https://wikipedia.org")
// );
} }
+6 -4
View File
@@ -10,12 +10,13 @@ use tokio;
const DEFAULT_TARGET: &str = "https://crackmes.one"; const DEFAULT_TARGET: &str = "https://crackmes.one";
const GHOST_ROUTE_HEADER: &str = "ghost-route"; const GHOST_ROUTE_HEADER: &str = "ghost-route";
async fn handle_request(req: Request<Body>) -> Result<Response<Body>, Infallible> { async fn handle_request(mut req: Request<Body>) -> Result<Response<Body>, Infallible> {
let target_url = req let target_url = req
.headers() .headers()
.get(GHOST_ROUTE_HEADER) .get(GHOST_ROUTE_HEADER)
.and_then(|v| v.to_str().ok()) .and_then(|v| v.to_str().ok())
.unwrap_or(DEFAULT_TARGET); .unwrap_or(DEFAULT_TARGET)
.to_string();
let url_params = req let url_params = req
.uri() .uri()
@@ -38,7 +39,7 @@ async fn handle_request(req: Request<Body>) -> Result<Response<Body>, Infallible
( (
HeaderName::from_str("host").unwrap(), HeaderName::from_str("host").unwrap(),
HeaderValue::from_bytes( HeaderValue::from_bytes(
reqwest::Url::from_str(target_url) reqwest::Url::from_str(&target_url)
.unwrap() .unwrap()
.host_str() .host_str()
.unwrap() .unwrap()
@@ -54,7 +55,8 @@ async fn handle_request(req: Request<Body>) -> Result<Response<Body>, Infallible
} }
}) })
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
)); ))
.body(hyper::body::to_bytes(req.body_mut()).await.unwrap());
let response = builder.send().await.unwrap(); let response = builder.send().await.unwrap();
+73 -21
View File
@@ -1,27 +1,79 @@
use rustls::ClientConfig; use std::net::TcpStream;
use rustls::client::ClientConnection; use std::sync::Arc;
use std::{net::TcpStream, sync::Arc};
pub fn mask_tls<'a>( use rustls::client::ClientConfig;
addr: &str, use rustls::pki_types::ServerName;
) -> Result<(ClientConnection, TcpStream), Box<dyn std::error::Error>> { use rustls::{ClientConnection, RootCertStore};
let certs = rustls_native_certs::load_native_certs()
.expect("could not load platform certificate store"); pub fn root_store() -> RootCertStore {
let mut root_store = rustls::RootCertStore::empty(); // Prefer system roots (works on most OSes). If that fails, fall back to webpki-roots.
for cert in certs { let mut store = RootCertStore::empty();
root_store.add(cert).unwrap();
// 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 if store.is_empty() {
let config = ClientConfig::builder() // Fallback: baked-in Mozilla roots via webpki-roots
.with_root_certificates(root_store) store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
}
store
}
pub fn tls13_config() -> Arc<ClientConfig> {
let mut cfg = ClientConfig::builder()
.with_root_certificates(root_store())
.with_no_client_auth(); .with_no_client_auth();
let arc_cfg = Arc::new(config); // ALPN (optional but typical)
let conn = ClientConnection::new( cfg.alpn_protocols = vec![b"http/1.1".to_vec()];
arc_cfg,
addr.split_once(":").unwrap().0.to_string().try_into()?, // Pin to TLS 1.3 only
)?; // cfg.versions = vec![rustls::version::TLS13];
let tcp = TcpStream::connect(addr)?;
Ok((conn, tcp)) Arc::new(cfg)
} }
pub fn tls13_handshake(
host: &str,
tcp: &mut TcpStream,
) -> Result<ClientConnection, Box<dyn std::error::Error>> {
// 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(())
// }