Compare commits

..
10 Commits
Author SHA1 Message Date
selimaj-dev 7aefdbcc40 Working client and server 2025-08-19 14:25:40 +02:00
selimaj-dev 53449db7c6 Fixed len 0 but index 0 2025-08-16 05:34:30 +02:00
selimaj-dev 1800f2acea Updated protocol and tls 2025-08-16 05:22:27 +02:00
selimaj-dev cf9479cda2 Working tls handshake 2025-08-16 04:08:31 +02:00
selimaj-dev 506d57e6f9 Working proxy with headers (not the best for privacy) 2025-08-15 23:15:27 +02:00
selimaj-dev 92bfad7f5e Simple logging 2025-08-15 20:17:48 +02:00
selimaj-dev 918898558a ok 2025-08-15 20:15:49 +02:00
selimaj-dev fa985a69c5 Working example 2025-08-15 11:58:16 +02:00
selimaj-dev 95b20dae76 Added server 2025-08-15 09:44:52 +02:00
selimaj-dev 0c3873c1d4 Simple client 2025-08-14 20:40:45 +02:00
7 changed files with 300 additions and 35 deletions
Generated
+10
View File
@@ -182,6 +182,7 @@ version = "0.0.1-alpha"
dependencies = [ dependencies = [
"rustls", "rustls",
"rustls-native-certs", "rustls-native-certs",
"webpki-roots",
] ]
[[package]] [[package]]
@@ -518,6 +519,15 @@ dependencies = [
"wit-bindgen-rt", "wit-bindgen-rt",
] ]
[[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"
+1
View File
@@ -9,3 +9,4 @@ name = "ghostnet-rs"
[dependencies] [dependencies]
rustls = "0.23.31" rustls = "0.23.31"
rustls-native-certs = "0.8.1" rustls-native-certs = "0.8.1"
webpki-roots = "1.0.2"
+7 -1
View File
@@ -24,4 +24,10 @@ A router manages the clients and routes messages from one client to another to e
## Todo ## Todo
- End-To-End Encryption - End-To-End Encryption
- Mask the communications like **HTTPS** - Mask the communications like **HTTPS**
## Common install issues on linux
```bash
sudo apt install pkg-config -y
sudo apt install libssl-dev -y
```
+126 -1
View File
@@ -1,3 +1,128 @@
use std::{
io::{Read, Write},
net::TcpStream,
};
#[derive(Debug)]
pub enum Method {
Get,
Post,
Put,
Delete,
Head,
Options,
Patch,
Connect,
Trace,
}
pub mod server;
pub mod tls; pub mod tls;
pub fn request(gateway: &str) {} #[derive(Debug)]
pub struct Request {
host: String,
path: String,
method: Method,
}
impl Request {
pub fn new(url: &str) -> Self {
Self {
host: url.to_string(),
path: String::from("/"),
method: Method::Get,
}
}
pub fn from_str(host: &str, req: &str) -> Self {
let first_line = req.lines().next().unwrap_or_default();
let mut parts = first_line.split_whitespace();
let method = parts.next().unwrap_or_default();
let path = parts.next().unwrap_or("/");
Self {
host: host.to_string(),
path: path.to_string(),
method: match method {
"GET" => Method::Get,
"POST" => Method::Post,
"PUT" => Method::Put,
"DELETE" => Method::Delete,
"HEAD" => Method::Head,
"OPTIONS" => Method::Options,
"PATCH" => Method::Patch,
"CONNECT" => Method::Connect,
"TRACE" => Method::Trace,
_ => Method::Get,
},
}
}
}
impl Request {
pub fn send_bytes(&self) -> Vec<u8> {
let tcp = TcpStream::connect((self.host.as_str(), 443)).unwrap();
send_request(tcp, &self.host, &self.to_string())
}
pub fn send(&self) -> String {
let tcp = TcpStream::connect((self.host.as_str(), 443)).unwrap();
String::from_utf8(send_request(tcp, &self.host, &self.to_string())).unwrap()
}
pub fn send_routed(&self, router: &str) -> String {
let mut tcp = TcpStream::connect(router).unwrap();
tcp.write_all(format!("ROUTE {}:443", self.host).as_bytes())
.unwrap();
tcp.read(&mut [0; 32]).unwrap();
String::from_utf8(send_request(tcp, &self.host, &self.to_string())).unwrap()
}
pub fn send_routed_secure(&self, router: &str) -> String {
let tcp = TcpStream::connect((router, 443)).unwrap();
send_request(
tcp.try_clone().unwrap(),
&router,
&format!("ROUTE {}:443", self.host),
);
String::from_utf8(send_request(tcp, &self.host, &self.to_string())).unwrap()
}
pub fn to_string(&self) -> String {
format!(
"{} {} HTTP/1.1\r\nHost: {}\r\nConnection: close\r\nUser-Agent: rustls/0.23\r\n\r\n",
self.method, self.path, self.host
)
}
}
pub fn send_request(mut stream: TcpStream, host: &str, send: &str) -> Vec<u8> {
let mut conn = tls::tls13_handshake(&host, &mut stream).unwrap();
let mut tls = rustls::Stream::new(&mut conn, &mut stream);
tls.write_all(send.as_bytes()).unwrap();
tls.flush().unwrap();
let mut resp = Vec::new();
tls.read_to_end(&mut resp).unwrap();
resp
}
impl std::fmt::Display for Method {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Self::Get => "GET",
Self::Post => "POST",
Self::Put => "PUT",
Self::Delete => "DELETE",
Self::Head => "HEAD",
Self::Options => "OPTIONS",
Self::Patch => "PATCH",
Self::Connect => "CONNECT",
Self::Trace => "TRACE",
}
)
}
}
+5 -14
View File
@@ -1,16 +1,7 @@
use std::io::{Read, Write};
use ghostnet_rs::tls;
use rustls::Stream;
fn main() { fn main() {
let (mut conn, mut tcp) = tls::mask_tls("github.com").unwrap(); ghostnet_rs::server::run();
let mut tls = Stream::new(&mut conn, &mut tcp); // println!(
// "{}",
tls.write_all("GET / HTTP/1.0\r\nHost: github.com\r\n\r\n".as_bytes()) // ghostnet_rs::Request::new("example.com").send_routed("localhost:6930")
.unwrap(); // );
let mut resp = Vec::new();
tls.read_to_end(&mut resp).unwrap();
println!("{}", String::from_utf8_lossy(&resp));
} }
+78
View File
@@ -0,0 +1,78 @@
use std::{
io::{Read, Write},
net::{TcpListener, TcpStream},
};
const DEFAULT_HOST: &str = "osui.netlify.app";
pub fn run() {
let server = TcpListener::bind(format!(
"0.0.0.0:{}",
std::env::var("PORT").unwrap_or("6930".to_string())
))
.unwrap();
loop {
for stream in server.incoming() {
match stream {
Ok(mut client) => {
std::thread::spawn(move || {
println!("Client connected");
let mut buf = [0; 1024];
let buf_len = client.read(&mut buf).unwrap();
let req = String::from_utf8_lossy(&buf[..buf_len]);
let req = req.lines().collect::<Vec<_>>();
if req.len() == 0 {
return;
}
if req[0].starts_with("ROUTE") {
println!("Route mode");
let addr = req[0].split_once(' ').unwrap().1;
println!("Connecting to {addr}");
let mut target = TcpStream::connect(addr).unwrap();
let mut target_t = target.try_clone().unwrap();
let mut client_t = client.try_clone().unwrap();
println!("Connected to {addr}");
client.write_all("CONN EST".as_bytes()).unwrap();
std::thread::spawn(move || {
loop {
let mut buf = [0; 2048];
let buf_len = client_t.read(&mut buf).unwrap();
if buf_len == 0 {
target_t.shutdown(std::net::Shutdown::Both).unwrap();
break;
}
target_t.write_all(&buf[..buf_len]).unwrap();
}
});
loop {
let mut buf = [0; 2048];
let buf_len = target.read(&mut buf).unwrap();
if buf_len == 0 {
client.shutdown(std::net::Shutdown::Both).unwrap();
break;
}
client.write_all(&buf[..buf_len]).unwrap();
}
} else {
let req = crate::Request::from_str(
DEFAULT_HOST,
&String::from_utf8_lossy(&buf[..buf_len]),
);
client.write_all(&req.send_bytes()).unwrap();
client.flush().unwrap();
}
});
}
Err(_) => {}
}
}
}
}
+73 -19
View File
@@ -1,25 +1,79 @@
use rustls::ClientConfig; use std::net::TcpStream;
use rustls::client::ClientConnection; use std::sync::Arc;
use rustls_native_certs;
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(arc_cfg, addr.to_owned().try_into()?)?; cfg.alpn_protocols = vec![b"http/1.1".to_vec()];
let tcp = TcpStream::connect(addr.to_owned() + ":443")?;
Ok((conn, tcp)) // 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<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(())
// }