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(())
+// }