diff --git a/Cargo.lock b/Cargo.lock index 9344348..1796527 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -62,12 +62,86 @@ dependencies = [ "version_check", ] +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + [[package]] name = "libc" version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom", +] + [[package]] name = "sha1" version = "0.10.6" @@ -79,21 +153,58 @@ dependencies = [ "digest", ] +[[package]] +name = "syn" +version = "2.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "typenum" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + [[package]] name = "version_check" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" + [[package]] name = "ws_client" version = "0.1.0" +dependencies = [ + "base64", + "rand", + "sha1", +] [[package]] name = "ws_core" @@ -107,3 +218,23 @@ dependencies = [ "sha1", "ws_core", ] + +[[package]] +name = "zerocopy" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/ws_client/Cargo.toml b/ws_client/Cargo.toml index 51eea51..d394b8f 100644 --- a/ws_client/Cargo.toml +++ b/ws_client/Cargo.toml @@ -4,3 +4,6 @@ version = "0.1.0" edition = "2024" [dependencies] +rand = "0.9.2" +base64 = "0.22.1" +sha1="0.10.6" \ No newline at end of file diff --git a/ws_client/src/main.rs b/ws_client/src/main.rs index e7a11a9..eed8f0b 100644 --- a/ws_client/src/main.rs +++ b/ws_client/src/main.rs @@ -1,3 +1,105 @@ -fn main() { - println!("Hello, world!"); +use std::io::{Read, Result, Write}; +use std::net::TcpStream; + +use base64::Engine; +use base64::engine::general_purpose; +use rand::Rng; +use sha1::{Digest, Sha1}; + +pub fn handle_server(mut stream: TcpStream) -> Result<()> { + let random_bs64_key =generate_base64_key(16); + //send a http request to the server for upgrading the protocol from normal http to websocket + stream.write_all( + format!( + "GET / HTTP/1.1\r\n + Host: 127.0.0.1:8080\r\n + Upgrade: websocket\r\n\ + Connection: Upgrade\r\n\ + Sec-WebSocket-Key: {}\r\n\ + Sec-WebSocket-Version: 13\r\n\r\n", + random_bs64_key + ) + .as_bytes(), + )?; + + let mut buffer = Vec::new(); + let mut temp = [0u8; 1024]; + + //loop over all the streams and stop when we get CL RF CL RF + // this makes us read all the valid stream and stops us from breaking and reading only half streamed data + loop { + let bytes_read = stream.read(&mut temp).expect("failed to read the stream"); + if bytes_read == 0 { + break; + } + + buffer.extend_from_slice(&temp[..bytes_read]); + + // same as server side + if buffer.windows(4).any(|w| w == b"\r\n\r\n") { + break; + } + } + + let request = String::from_utf8_lossy(&buffer); + + //check if the server accepted the connection + if request.contains("Upgrade: websocket") + && request.contains("Connection: Upgrade") + && request.contains("Sec-WebSocket-Accept") + { + let mut server_key = String::new(); + + //get the server key which server sent for upgrading + for line in request.lines() { + if line.starts_with("Sec-WebSocket-Accept:") { + server_key = line.split(":").nth(1).unwrap().trim().to_string(); + } + } + + //expected key = base64 (SHA1 (random base 64 key + Magic UID)) + let mut hasher = Sha1::new(); + hasher.update(format!( + "{}258EAFA5-E914-47DA-95CA-C5AB0DC85B11", + random_bs64_key + )); + let hashed_key = hasher.finalize(); + let received_key = general_purpose::STANDARD.encode(hashed_key); + + //match the expected key with the server key if yes then create a loop untill the connection closes + if received_key == server_key { + loop { + + } + }else{ + println!("failed to upgrade connection") + } + } + + Ok(()) +} + +pub fn main() -> Result<()> { + //connect the client with the servers tcp connection + match TcpStream::connect("127.0.0.1:8080") { + Ok(stream) => { + println!("Connected Successfully."); + handle_server(stream)?; + } + Err(err) => { + println!("Error Connecting Server : {}", err); + } + } + Ok(()) +} + +// function to create a base 64 key and it is done because the server accepts 16 bytes of base 64 encoded key to upgrade the protocol +fn generate_base64_key(bytes: u8) -> String { + let mut key_bytes = vec![0u8;bytes as usize]; + + rand::rng().fill(&mut key_bytes[..]); + + let encoded = general_purpose::STANDARD.encode(&key_bytes); + + encoded } diff --git a/ws_server/src/main.rs b/ws_server/src/main.rs index 08c2f0f..dc17899 100644 --- a/ws_server/src/main.rs +++ b/ws_server/src/main.rs @@ -37,7 +37,7 @@ pub fn handle_client(mut stream: TcpStream) -> Result<()> { let request = String::from_utf8_lossy(&buffer); //check if the request contains the upgrade websocket - if request.contains("Upgrade: websocket") { + if request.contains("Upgrade: websocket") && request.contains("Sec-WebSocket-Key") && request.contains("Sec-WebSocket-Version") { let mut websocket_key = String::new(); //get the key to upgrade the protocol for line in request.lines() {