diff --git a/Cargo.lock b/Cargo.lock index 9afd209..cd6911b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -749,6 +749,7 @@ dependencies = [ "clap", "colored", "hmac", + "libc", "orx-concurrent-vec", "pbkdf2", "rand", @@ -757,6 +758,7 @@ dependencies = [ "sha2", "smol", "tappers", + "winapi", ] [[package]] @@ -1076,6 +1078,28 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-core" version = "0.61.2" diff --git a/Cargo.toml b/Cargo.toml index 0bcd961..782f211 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,5 +29,12 @@ sha2 = "0.10.9" smol = "2.0.2" tappers = "0.4.2" +[target.'cfg(windows)'.dependencies] +winapi = { version = "0.3", features = ["winsock2", "mswsock", "minwindef"] } + +[target.'cfg(unix)'.dependencies] +libc = "0.2" + + [features] no-timeout = [] diff --git a/src/client/config.rs b/src/client/config.rs new file mode 100644 index 0000000..2dfe3fc --- /dev/null +++ b/src/client/config.rs @@ -0,0 +1,113 @@ +use std::io::ErrorKind; +use std::net::{SocketAddr, UdpSocket}; + +#[cfg(target_os = "windows")] +use std::os::windows::io::AsRawSocket; +#[cfg(target_os = "windows")] +use winapi::shared::minwindef::{BOOL, DWORD, FALSE}; +#[cfg(target_os = "windows")] +use winapi::um::mswsock::SIO_UDP_CONNRESET; +#[cfg(target_os = "windows")] +use winapi::um::winsock2::{SOCKET_ERROR, WSAIoctl}; + +#[cfg(target_os = "linux")] +use std::os::unix::io::AsRawFd; + +#[cfg(target_os = "windows")] +fn enable_icmp_errors(socket: &UdpSocket) -> std::io::Result<()> { + let socket_handle = socket.as_raw_socket(); + let mut bytes_returned: DWORD = 0; + let enable: BOOL = FALSE; + + let result = unsafe { + WSAIoctl( + socket_handle as usize, + SIO_UDP_CONNRESET, + &enable as *const _ as *mut _, + std::mem::size_of::() as DWORD, + std::ptr::null_mut(), + 0, + &mut bytes_returned, + std::ptr::null_mut(), + None, + ) + }; + + if result == SOCKET_ERROR { + Err(std::io::Error::last_os_error()) + } else { + Ok(()) + } +} + +#[cfg(target_os = "linux")] +fn enable_icmp_errors(socket: &UdpSocket) -> std::io::Result<()> { + let fd = socket.as_raw_fd(); + let optval: libc::c_int = 1; + + let ret = unsafe { + libc::setsockopt( + fd, + libc::SOL_IP, + libc::IP_RECVERR, + &optval as *const _ as *const libc::c_void, + std::mem::size_of::() as libc::socklen_t, + ) + }; + + if ret < 0 { + Err(std::io::Error::last_os_error()) + } else { + Ok(()) + } +} + +#[cfg(target_os = "linux")] +fn check_icmp_error_queue(socket: &UdpSocket) -> std::io::Result<()> { + use libc::{MSG_ERRQUEUE, iovec, msghdr, recvmsg}; + + let fd = socket.as_raw_fd(); + let mut buf = [0u8; 1024]; + let mut control_buf = [0u8; 1024]; + + let mut iov = iovec { + iov_base: buf.as_mut_ptr() as *mut libc::c_void, + iov_len: buf.len(), + }; + + let mut msg: msghdr = unsafe { std::mem::zeroed() }; + msg.msg_iov = &mut iov; + msg.msg_iovlen = 1; + msg.msg_control = control_buf.as_mut_ptr() as *mut libc::c_void; + msg.msg_controllen = control_buf.len(); + + let result = unsafe { recvmsg(fd, &mut msg, MSG_ERRQUEUE) }; + + if result < 0 { + let error = std::io::Error::last_os_error(); + if error.kind() == std::io::ErrorKind::WouldBlock { + return Ok(()); + } + return Err(error); + } + + Err(std::io::Error::new( + std::io::ErrorKind::NetworkUnreachable, + "ICMP destination unreachable received", + )) +} + +#[cfg(target_os = "windows")] +fn check_icmp_error_queue(_socket: &UdpSocket) -> std::io::Result<()> { + Ok(()) +} + +#[cfg(not(any(target_os = "linux", target_os = "windows")))] +fn enable_icmp_errors(_socket: &UdpSocket) -> std::io::Result<()> { + Ok(()) +} + +#[cfg(not(any(target_os = "linux", target_os = "windows")))] +fn check_icmp_error_queue(_socket: &UdpSocket) -> std::io::Result<()> { + Ok(()) +} diff --git a/src/client/main.rs b/src/client/main.rs index 37f6657..99aa9b6 100644 --- a/src/client/main.rs +++ b/src/client/main.rs @@ -1,3 +1,4 @@ +mod config; mod net; mod tun; mod types; @@ -52,14 +53,15 @@ fn main() -> std::io::Result<()> { } 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 { + let socket: Arc = Arc::new(|| -> std::io::Result { match UdpSocket::bind("0.0.0.0:0") { // bind to OS assigned random port Ok(socket) => return Ok(socket), Err(e) => Err(e), // exit on error } })() - .expect("Failed to bind to any available port"); + .expect("Failed to bind to any available port") + .into(); #[cfg(not(feature = "no-timeout"))] socket.set_read_timeout(Some(Duration::new(10, 0)))?; // set timeout to 10 seconds @@ -178,7 +180,7 @@ fn main() -> std::io::Result<()> { 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!( + println!( "{} reaching to other peers to obtain ip address", "[LOG]".blue() ); @@ -243,17 +245,52 @@ fn main() -> std::io::Result<()> { }); } - 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); + let tun_iface = Arc::new( + 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); + } + }, + ); + + smol::block_on(async { + smol::spawn(tun::read_tun_iface( + tun_iface.clone(), + socket.clone(), + virtual_network.clone(), + )) + .detach(); + + loop { + buf.fill(0); + match socket.recv_from(&mut buf) { + Ok((data_lenght, src)) => { + smol::spawn(net::handle_incoming_connection( + buf, + src, + virtual_network.clone(), + tun_iface.clone(), + data_lenght, + )) + .detach(); + } + Err(e) => { + eprint!( + "{} failed to read from socket Error: {}", + "[ERROR]".red(), + e + ); + exit(5); //EIO + } + } } - }; + }); Ok(()) } diff --git a/src/client/net.rs b/src/client/net.rs index 731c742..9b5ff8c 100644 --- a/src/client/net.rs +++ b/src/client/net.rs @@ -5,6 +5,7 @@ use std::{ sync::{Arc, RwLock}, }; +use colored::Colorize; use pea_2_pea::*; use rand::{RngCore, rng}; use tappers::Netmask; @@ -479,3 +480,131 @@ pub fn P2P_hello( Err(e) => return Err(e), } } + +pub async fn handle_incoming_connection( + buf: [u8; UDP_BUFFER_SIZE], + src: SocketAddr, + network: Arc>, + tun_iface: Arc, + data_lenght: usize, +) { + match buf[0] { + x if x == P2PMethods::PACKET as u8 => { + #[cfg(debug_assertions)] + println!("PACKET from difernt peer receved"); + + if network.read().unwrap().encrypted { + match shared::crypto::decrypt( + &network.read().unwrap().key, + &buf[P2PStandardDataPositions::IV as usize + ..P2PStandardDataPositions::IV as usize + SALT_AND_IV_SIZE], + &buf[P2PStandardDataPositions::DATA as usize..data_lenght as usize-1 /*compensate for size and index diference*/], + ) { + Ok(data) => match tun_iface.send(&data) { + Ok(_) => {} + Err(e) => eprintln!( + "{} failed to write packet to tun interface, Error: {}", + "[WARNING]".yellow(), + e + ), + }, + Err(e) => eprintln!( + "{} failed to decrypt packet, Error: {}", + "[WARNING]".yellow(), + e + ), + } + } else { + match tun_iface.send(&buf[P2PStandardDataPositions::DATA as usize..data_lenght as usize-1 /*compensate for size and index diference*/]) { + Ok(_) => {}, + Err(e) => eprintln!("{} failed to write packet to tun interface, Error: {}", "[WARNING]".yellow(), e), + }; + } + } + x if x == P2PMethods::PEER_HELLO as u8 => { + println!("{} peer hello receved from: {}", "[LOG]".blue(), src); + + let tmp_data: Vec; + { + let mut network_write_lock = network.write().unwrap(); + let key: [u8; 32] = network_write_lock.key; + let encrypted: bool = network_write_lock.encrypted; + network_write_lock.peers.push(Peer::new( + src, + Some( + match std::net::Ipv4Addr::from_str( + match std::str::from_utf8(if encrypted { + match shared::crypto::decrypt(&key, &buf[P2PStandardDataPositions::IV as usize + ..P2PStandardDataPositions::IV as usize + SALT_AND_IV_SIZE], &buf[P2PStandardDataPositions::DATA as usize..data_lenght as usize-1 /*compensate for size and index diference*/]) { + Ok(data) => {tmp_data = data; &tmp_data}, + Err(e) => { + eprintln!( + "{} failed to decrypt ip from peer, ignoring it Error: {}", + "[WARNING]".yellow(), + e + ); + return; + }, + } + } else { + &buf[P2PStandardDataPositions::DATA as usize..data_lenght as usize-1 /*compensate for size and index diference*/] + }) { + Ok(s) => s, + Err(e) => { + eprintln!( + "{} failed to parse ip from peer, ignoring it Error: {}", + "[WARNING]".yellow(), + e + ); + return; + } + }, + ) { + Ok(ip) => ip, + Err(e) => { + eprintln!( + "{} failed to parse ip from peer, ignoring it Error: {}", + "[WARNING]".yellow(), + e + ); + return; + } + }, + ), + )); + } + } + x if x == P2PMethods::PEER_GOODBYE as u8 => { + println!("{} peer goodbye receved from: {}", "[LOG]".blue(), src); + + let mut network_lock = network.write().unwrap(); + + let key = network_lock.key; + let encrypted: bool = network_lock.encrypted; + + let mut data_tmp: Vec = Vec::with_capacity(SALT_AND_IV_SIZE); // block size + + network_lock.peers.retain(|peer| !{peer.private_ip == match std::net::Ipv4Addr::from_str(match std::str::from_utf8( if encrypted { + match shared::crypto::decrypt(&key, &buf[P2PStandardDataPositions::IV as usize..P2PStandardDataPositions::IV as usize + SALT_AND_IV_SIZE], &buf[P2PStandardDataPositions::DATA as usize..data_lenght as usize-1 /*compensate for size and index diference*/]) { + Ok(data) => {data_tmp = data; + &data_tmp}, + Err(e) => {eprintln!("{} error parsing ip, Error: {}", "[ERROR]".red(), e); return false;}, + } + } else {&buf[P2PStandardDataPositions::DATA as usize..data_lenght as usize-1 /*compensate for size and index diference*/]}) { + Ok(s) => s, + Err(e) => {eprintln!("{} error parsing ip, Error: {}", "[ERROR]".red(), e); return false;}, + }) { + Ok(ip) => ip, + Err(e) => {eprintln!("{} error parsing ip, Error: {}", "[ERROR]".red(), e); return false;}, + } && peer.sock_addr == src}); + } + + _ => { + eprintln!( + "{} unknown method ID: 0x{:02x}, Droping!", + "[WARNING]".bright_yellow(), + buf[0] + ) + } + } +} diff --git a/src/client/tun.rs b/src/client/tun.rs index 3f8c316..a24f3f7 100644 --- a/src/client/tun.rs +++ b/src/client/tun.rs @@ -21,8 +21,8 @@ pub fn create_tun_interface( } pub async fn read_tun_iface( - tun_iface: &tappers::Tun, - socket: std::net::UdpSocket, + tun_iface: Arc, + socket: Arc, network: Arc>, ) { let mut buf: [u8; IP_BUFFER_SIZE] = [0u8; IP_BUFFER_SIZE]; @@ -33,7 +33,7 @@ pub async fn read_tun_iface( smol::spawn(handle_ip_packet( buf[..data_lenght - 1].to_vec().into(), network.clone(), - socket.try_clone().expect("couldn't clone the socket"), + socket.clone(), )) .detach(); } @@ -43,7 +43,7 @@ pub async fn read_tun_iface( pub async fn handle_ip_packet( packet_data: Box<[u8]>, network: Arc>, - socket: std::net::UdpSocket, + socket: Arc, ) { let dst_ip = std::net::Ipv4Addr::from( match <[u8; 4]>::try_from(