diff --git a/Cargo.lock b/Cargo.lock index 1eac0d3..9afd209 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -346,6 +346,15 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +[[package]] +name = "colored" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -738,6 +747,7 @@ dependencies = [ "chrono", "cipher", "clap", + "colored", "hmac", "orx-concurrent-vec", "pbkdf2", diff --git a/Cargo.toml b/Cargo.toml index c0b6cc4..0bcd961 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ cbc = "0.1.2" chrono = "0.4.41" cipher = { version = "0.4.4", features = ["block-padding", "alloc"] } clap = { version = "4.5.41", features = ["derive"] } +colored = "3.0.0" hmac = "0.12.1" orx-concurrent-vec = "3.6.0" pbkdf2 = "0.12.2" diff --git a/src/client/main.rs b/src/client/main.rs index 98ea3a0..37f6657 100644 --- a/src/client/main.rs +++ b/src/client/main.rs @@ -1,9 +1,17 @@ mod net; +mod tun; mod types; +use colored::Colorize; use pea_2_pea::*; use rand::RngCore; +use rayon::prelude::*; -use std::{net::UdpSocket, process::exit, time::Duration}; +use std::{ + net::UdpSocket, + process::exit, + sync::{Arc, RwLock}, + time::Duration, +}; use crate::types::Network; @@ -42,7 +50,8 @@ fn main() -> std::io::Result<()> { eprintln!("network id cannot have more then 255 charactes"); exit(7); // posix for E2BIG } - { + let mut buf: [u8; UDP_BUFFER_SIZE] = [0; UDP_BUFFER_SIZE]; + let (socket, virtual_network, my_public_sock_addr) = { let socket: UdpSocket = (|| -> std::io::Result { match UdpSocket::bind("0.0.0.0:0") { // bind to OS assigned random port @@ -67,7 +76,6 @@ fn main() -> std::io::Result<()> { .parse() .unwrap(); - let mut buf: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]; // query here let public_sock_addr_raw: String = match net::query_request(&mut buf, &server_SocketAddr, &socket) { @@ -77,7 +85,7 @@ fn main() -> std::io::Result<()> { let mut salt: [u8; SALT_AND_IV_SIZE] = [0u8; SALT_AND_IV_SIZE]; let mut iv: [u8; SALT_AND_IV_SIZE] = [0u8; SALT_AND_IV_SIZE]; - let (public_sock_addr, encryption_key) = match cli.password { + let (mut public_sock_addr, encryption_key) = match cli.password { Some(ref p) => { let mut rng = rand::rng(); rng.fill_bytes(&mut salt); @@ -104,7 +112,7 @@ fn main() -> std::io::Result<()> { ), }; - let virtual_network: Network = { + let virtual_network: Arc> = RwLock::new({ match net::get_request( &mut buf, &server_SocketAddr, @@ -114,14 +122,16 @@ fn main() -> std::io::Result<()> { ) { Ok(n) => { eprintln!("Network exists joining it"); + public_sock_addr = + shared::crypto::encrypt(&n.key, &iv, public_sock_addr_raw.as_bytes()) + .unwrap() + .into_boxed_slice(); let _ = net::send_heartbeat( &mut buf, &server_SocketAddr, &socket, &n, - &shared::crypto::encrypt(&n.key, &iv, public_sock_addr_raw.as_bytes()) - .unwrap() - .into_boxed_slice(), + &public_sock_addr, &iv, ); n @@ -154,7 +164,96 @@ fn main() -> std::io::Result<()> { exit(5); //EIO } } - }; + }) + .into(); + ( + socket, + virtual_network, + types::EncryptablePulicSockAddr::new(iv, public_sock_addr), + ) + }; + + { + // all loops here will be auto skiped if there are no peers yet + let mut ips_used: [bool; u8::MAX as usize + 1] = [false; u8::MAX as usize + 1]; + ips_used[0] = true; // ignore net addr + ips_used[u8::MAX as usize] = true; // ignore broadcast + eprintln!( + "{} reaching to other peers to obtain ip address", + "[LOG]".blue() + ); + virtual_network + .write() + .unwrap() + .peers + .iter_mut() + .for_each(|peer| { + match net::P2P_query(&mut buf, &peer.sock_addr, &socket, virtual_network.clone()) { + Ok(ip) => { + ips_used[ip.octets()[3] as usize] = true; + peer.private_ip = ip; + } + Err(e) => eprintln!( + "{} while getting ip from peer: {}, Error: {}", + "[ERROR]".red(), + peer.sock_addr, + e + ), + }; + }); + + virtual_network.write().unwrap().private_ip = std::net::Ipv4Addr::new( + DEFAULT_NETWORK_PREFIX[0], + DEFAULT_NETWORK_PREFIX[1], + DEFAULT_NETWORK_PREFIX[2], + ips_used.par_iter().position_first(|&b| !b).unwrap() as u8, + ); // find first element that is false + + virtual_network + .write() + .unwrap() + .peers + .retain(|peer| peer.private_ip != std::net::Ipv4Addr::UNSPECIFIED); // remove all peers without ip + + virtual_network + .read() + .unwrap() + .peers + .iter() + .for_each(|peer| { + match net::P2P_hello( + &mut buf, + &peer.sock_addr, + &socket, + virtual_network.read().unwrap().private_ip, + virtual_network.clone(), + ) { + Ok(_) => eprintln!( + "{} registered with peer: {}", + "[SUCCESS]".green(), + peer.sock_addr + ), + Err(e) => eprintln!( + "{} failed to register with peer: {}, Error: {}", + "[ERROR]".red(), + peer.sock_addr, + e + ), + } + }); } + + let tun_iface = match tun::create_tun_interface(virtual_network.read().unwrap().private_ip) { + Ok(t) => t, + Err(e) => { + eprintln!( + "{} failed to create Tun interface, Error: {}, are you running as root?", + "[CRITICAL]".red().bold(), + e + ); + return Err(e); + } + }; + Ok(()) } diff --git a/src/client/net.rs b/src/client/net.rs index 4f6f794..731c742 100644 --- a/src/client/net.rs +++ b/src/client/net.rs @@ -1,16 +1,21 @@ use std::{ io::ErrorKind, - net::{SocketAddr, UdpSocket}, + net::{Ipv4Addr, SocketAddr, UdpSocket}, str::FromStr, + sync::{Arc, RwLock}, }; use pea_2_pea::*; +use rand::{RngCore, rng}; +use tappers::Netmask; + +use crate::types::Peer; use super::types; // return data_lenght and number of retryes pub fn send_and_recv_with_retry( - buf: &mut [u8; BUFFER_SIZE], + buf: &mut [u8; UDP_BUFFER_SIZE], send_buf: &[u8], dst: &SocketAddr, socket: &UdpSocket, @@ -77,7 +82,7 @@ pub fn send_and_recv_with_retry( } pub fn query_request( - buf: &mut [u8; BUFFER_SIZE], + buf: &mut [u8; UDP_BUFFER_SIZE], dst: &SocketAddr, socket: &UdpSocket, ) -> Result { @@ -104,7 +109,7 @@ pub fn query_request( } pub fn register_request( - buf: &mut [u8; BUFFER_SIZE], + buf: &mut [u8; UDP_BUFFER_SIZE], dst: &SocketAddr, socket: &UdpSocket, network: &types::Network, @@ -167,7 +172,7 @@ pub fn register_request( } pub fn get_request( - buf: &mut [u8; BUFFER_SIZE], + buf: &mut [u8; UDP_BUFFER_SIZE], dst: &SocketAddr, socket: &UdpSocket, network_id: &String, @@ -214,7 +219,7 @@ pub fn get_request( .unwrap(); let mut offset: usize = 0; - let mut peers: Vec = Vec::with_capacity(1); // at least one client + let mut peers: Vec = Vec::with_capacity(1); // at least one client let key: [u8; 32] = match password { Some(p) => shared::crypto::derive_key_from_password(p.as_bytes(), &salt), @@ -302,7 +307,7 @@ pub fn get_request( } }; - peers.push(peer); + peers.push(types::Peer::new(peer, None)); break; } offset += SALT_AND_IV_SIZE as usize + sock_addr_len as usize + 1 /*for size byte */; @@ -319,7 +324,7 @@ pub fn get_request( } pub fn send_heartbeat( - buf: &mut [u8; BUFFER_SIZE], + buf: &mut [u8; UDP_BUFFER_SIZE], dst: &SocketAddr, socket: &UdpSocket, network: &types::Network, @@ -371,3 +376,106 @@ pub fn send_heartbeat( Err(e) => return Err(e), } } + +#[allow(non_snake_case)] +pub fn P2P_query( + buf: &mut [u8; UDP_BUFFER_SIZE], + dst: &SocketAddr, + socket: &UdpSocket, + network: Arc>, +) -> Result> { + #[cfg(debug_assertions)] + println!("P2P QUERY method"); + + let (data_lenght, _) = send_and_recv_with_retry( + buf, + &[P2PMethods::PEER_QUERY as u8], + dst, + socket, + STANDARD_RETRY_MAX, + )?; + + let iv: [u8; SALT_AND_IV_SIZE] = buf[P2PStandardDataPositions::IV as usize + ..P2PStandardDataPositions::IV as usize + SALT_AND_IV_SIZE] + .try_into() + .expect("this should never happen"); + + let tmp_decrypted: Vec; + + return Ok(std::net::Ipv4Addr::from_str( + if network.read().unwrap().encrypted { + match shared::crypto::decrypt( + &network.read().unwrap().key, + &iv, + &buf[P2PStandardDataPositions::DATA as usize..data_lenght - 1], + ) { + Ok(decrypted) => { + tmp_decrypted = decrypted; + match std::str::from_utf8(&tmp_decrypted) { + Ok(s) => s, + Err(e) => return Err(Box::new(e)), + } + } + Err(e) => { + return Err(Box::new(ServerErrorResponses::GENERAL_ERROR(format!( + "{}", + e + )))); + } + } + } else { + match std::str::from_utf8( + &buf[P2PStandardDataPositions::DATA as usize..data_lenght - 1], + ) { + Ok(s) => s, + Err(e) => return Err(Box::new(e)), + } + }, + )?); +} + +#[allow(non_snake_case)] +pub fn P2P_hello( + buf: &mut [u8; UDP_BUFFER_SIZE], + dst: &SocketAddr, + socket: &UdpSocket, + private_ip: Ipv4Addr, + network: Arc>, +) -> Result { + let private_ip_str = private_ip.to_string(); + let (private_ip_final, iv) = if network.read().unwrap().encrypted { + let mut rng = rng(); + let mut iv: [u8; SALT_AND_IV_SIZE] = [0u8; SALT_AND_IV_SIZE]; + rng.fill_bytes(&mut iv); + ( + shared::crypto::encrypt( + &network.read().unwrap().key, + &iv, + &private_ip_str.as_bytes(), + ) + .unwrap() + .into_boxed_slice(), + iv, + ) + } else { + ( + private_ip_str.as_bytes().to_vec().into_boxed_slice(), + [0u8; SALT_AND_IV_SIZE], + ) + }; + + let mut send_buf: Box<[u8]> = + vec![0u8; 1 + P2PStandardDataPositions::DATA as usize + private_ip_final.len()].into(); + + send_buf[0] = P2PMethods::PEER_HELLO as u8; + send_buf[P2PStandardDataPositions::IV as usize + ..P2PStandardDataPositions::IV as usize + SALT_AND_IV_SIZE] + .copy_from_slice(&iv); + + send_buf[P2PStandardDataPositions::DATA as usize..].copy_from_slice(&private_ip_final); + + match send_and_recv_with_retry(buf, &send_buf, dst, socket, STANDARD_RETRY_MAX) { + Ok((data_lenght, _)) => return Ok(data_lenght), + Err(e) => return Err(e), + } +} diff --git a/src/client/tun.rs b/src/client/tun.rs new file mode 100644 index 0000000..3f8c316 --- /dev/null +++ b/src/client/tun.rs @@ -0,0 +1,102 @@ +use std::sync::{Arc, RwLock}; + +use pea_2_pea::*; +use rand::RngCore; +use rayon::prelude::*; + +use crate::types::Network; + +pub fn create_tun_interface( + private_ip: std::net::Ipv4Addr, +) -> Result { + let mut tun_iface: tappers::Tun = tappers::Tun::new_named(tappers::Interface::new("pea0")?)?; + let mut addr_req = tappers::AddAddressV4::new(private_ip); + addr_req.set_netmask(24); + let mut broadcast_addr_oct = private_ip.octets(); + broadcast_addr_oct[3] = 255; + addr_req.set_broadcast(std::net::Ipv4Addr::from(broadcast_addr_oct)); + tun_iface.add_addr(private_ip)?; + tun_iface.set_up()?; + return Ok(tun_iface); +} + +pub async fn read_tun_iface( + tun_iface: &tappers::Tun, + socket: std::net::UdpSocket, + network: Arc>, +) { + let mut buf: [u8; IP_BUFFER_SIZE] = [0u8; IP_BUFFER_SIZE]; + + smol::block_on(async { + loop { + let data_lenght = tun_iface.recv(&mut buf).unwrap(); // build in auto termination, isn't it great + smol::spawn(handle_ip_packet( + buf[..data_lenght - 1].to_vec().into(), + network.clone(), + socket.try_clone().expect("couldn't clone the socket"), + )) + .detach(); + } + }); +} + +pub async fn handle_ip_packet( + packet_data: Box<[u8]>, + network: Arc>, + socket: std::net::UdpSocket, +) { + let dst_ip = std::net::Ipv4Addr::from( + match <[u8; 4]>::try_from( + &packet_data[DEST_IN_IPV4_OFFSET..DEST_IN_IPV4_OFFSET + IPV4_SIZE], + ) { + Ok(slice) => slice, + Err(e) => { + eprintln!("Procesing of IP packet failed, Invalid dst IP: {}", e); + return; + } + }, + ); + let mut rng = rand::rng(); + + let mut iv: [u8; SALT_AND_IV_SIZE] = [0u8; SALT_AND_IV_SIZE]; + rng.fill_bytes(&mut iv); + + let mut encrypted_data = + match shared::crypto::encrypt(&network.read().unwrap().key, &iv, &packet_data) { + Ok(cr) => cr, + Err(e) => { + eprintln!("Failed to encrypt packet droping it: {}", e); + return; + } + }; + + encrypted_data.insert(0, P2PMethods::PACKET as u8); + encrypted_data.splice(1..1, iv); + + if dst_ip.octets()[3] == 255 { + network.read().unwrap().peers.par_iter().for_each(|peer| { + // broadcast + match socket.send_to(&encrypted_data, peer.sock_addr) { + Ok(_) => {} + Err(e) => eprintln!("failed to send packet: {}", e), + }; + }); + } else { + let dst = match network + .read() + .unwrap() + .peers + .par_iter() + .find_any(|&p| p.private_ip == dst_ip) + .map(|p| p.sock_addr) + { + Some(sa) => sa, + None => return, + }; + + match socket.send_to(&encrypted_data, dst) { + Ok(_) => {} + Err(e) => eprintln!("failed to send packet: {}", e), + }; + } +} diff --git a/src/client/types.rs b/src/client/types.rs index 41a4cc0..4cc7378 100644 --- a/src/client/types.rs +++ b/src/client/types.rs @@ -1,4 +1,23 @@ use pea_2_pea::*; + +#[readonly::make] +pub struct Peer { + #[readonly] + pub sock_addr: std::net::SocketAddr, + pub private_ip: std::net::Ipv4Addr, +} +impl Peer { + pub fn new(sock_addr: std::net::SocketAddr, private_ip: Option) -> Self { + Peer { + sock_addr, + private_ip: match private_ip { + Some(ip) => ip, + None => std::net::Ipv4Addr::UNSPECIFIED, + }, + } + } +} + #[readonly::make] pub struct Network { #[readonly] @@ -9,8 +28,8 @@ pub struct Network { pub net_id: String, #[readonly] pub salt: [u8; SALT_AND_IV_SIZE as usize], - #[readonly] - pub peers: Vec, + pub peers: Vec, + pub private_ip: std::net::Ipv4Addr, } impl Network { @@ -19,7 +38,7 @@ impl Network { key: [u8; 32], net_id: String, salt: [u8; SALT_AND_IV_SIZE as usize], - peers: Vec, + peers: Vec, ) -> Self { Network { encrypted, @@ -27,6 +46,21 @@ impl Network { net_id, salt, peers, + private_ip: std::net::Ipv4Addr::UNSPECIFIED, } } } + +#[readonly::make] +pub struct EncryptablePulicSockAddr { + #[readonly] + pub iv: [u8; SALT_AND_IV_SIZE], + #[readonly] + pub sock_addr: Box<[u8]>, +} + +impl EncryptablePulicSockAddr { + pub fn new(iv: [u8; SALT_AND_IV_SIZE], sock_addr: Box<[u8]>) -> Self { + EncryptablePulicSockAddr { iv, sock_addr } + } +} diff --git a/src/lib.rs b/src/lib.rs index 0456c66..8b83851 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,12 +1,18 @@ use core::fmt; pub const SERVER_PORT: u16 = 3543; -pub const BUFFER_SIZE: usize = 65535; +pub const UDP_BUFFER_SIZE: usize = 65527; +pub const IP_BUFFER_SIZE: usize = 65535; pub const DEFAULT_TIMEOUT: u64 = 30; pub const VERSION: &str = "v0.1"; pub const SALT_AND_IV_SIZE: usize = 16; pub const STANDARD_RETRY_MAX: usize = 10; +pub const DEST_IN_IPV4_OFFSET: usize = 16; +pub const IPV4_SIZE: usize = 4; + +pub const DEFAULT_NETWORK_PREFIX: [u8; 3] = [172, 22, 44]; + #[repr(u8)] pub enum ServerMethods { QUERY = 0, // return IP and port of the client @@ -106,4 +112,19 @@ pub enum HeartBeatRequestDataPositions { DATA = (HeartBeatRequestDataPositions::IV as usize + SALT_AND_IV_SIZE as usize) as usize, // first ID than sockaddr } +#[allow(non_camel_case_types)] +#[repr(u8)] +pub enum P2PMethods { + PEER_QUERY = 20, // responds with its private ip + PEER_HELLO = 21, // sends private ip encrypted if on + PEER_GOODBYE = 22, // sends private ip encrypted if on + PACKET = 23, // sends IP packet encrypted if on +} +#[repr(usize)] +pub enum P2PStandardDataPositions { + // sould apply to all P2P Methods + IV = 1, + DATA = P2PStandardDataPositions::IV as usize + SALT_AND_IV_SIZE, +} + pub mod shared; diff --git a/src/server/main.rs b/src/server/main.rs index cad0d74..b5e5180 100644 --- a/src/server/main.rs +++ b/src/server/main.rs @@ -24,7 +24,7 @@ fn main() -> std::io::Result<()> { let registration_vector: Arc> = Arc::new(orx_concurrent_vec::ConcurrentVec::new()); - let mut buf: [u8; pea_2_pea::BUFFER_SIZE] = [0u8; pea_2_pea::BUFFER_SIZE]; + let mut buf: [u8; pea_2_pea::UDP_BUFFER_SIZE] = [0u8; pea_2_pea::UDP_BUFFER_SIZE]; smol::block_on(async { loop { buf.fill(0); diff --git a/src/server/net.rs b/src/server/net.rs index 08414ea..592f9db 100644 --- a/src/server/net.rs +++ b/src/server/net.rs @@ -9,7 +9,7 @@ use rayon::prelude::*; use std::sync::Arc; use std::u8; pub async fn handle_request( - buf: [u8; BUFFER_SIZE], + buf: [u8; UDP_BUFFER_SIZE], socket: std::sync::Arc, src: core::net::SocketAddr, data_len: usize, @@ -115,7 +115,7 @@ pub async fn handle_request( send_vec.extend_from_slice(&client.client_sock_addr); }); - if send_vec.len() > BUFFER_SIZE { + if send_vec.len() > UDP_BUFFER_SIZE { send_general_error_to_client( src, std::io::Error::new( @@ -327,7 +327,7 @@ pub async fn handle_request( Some(reg) => { let current_time = chrono::Utc::now().timestamp(); reg.update(|r| {r.last_heart_beat = current_time; - match r.clients.par_iter_mut().find_first(|c| *c.client_sock_addr == *sock_addr && c.iv == iv) { + match r.clients.par_iter_mut().find_any(|c| *c.client_sock_addr == *sock_addr && c.iv == iv) { Some(c) => c.last_heart_beat = current_time, None => {// add new client if it isn't found r.clients.push(types::Client::new(sock_addr.clone(), current_time, iv)); diff --git a/src/shared/crypto.rs b/src/shared/crypto.rs index b36aa5b..a850674 100644 --- a/src/shared/crypto.rs +++ b/src/shared/crypto.rs @@ -36,6 +36,7 @@ pub fn decrypt( } } +#[cfg(debug_assertions)] pub fn test_all_crypto_functions() { // Test data let password = b"test_password_123";