pub mod compress; pub mod platform; pub mod protos; pub use bytes; use config::Config; pub use futures; pub use protobuf; pub use protos::message as message_proto; pub use protos::rendezvous as rendezvous_proto; use std::{ fs::File, io::{self, BufRead}, net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4}, path::Path, time::{self, SystemTime, UNIX_EPOCH}, }; pub use tokio; pub use tokio_util; pub mod socket_client; pub mod tcp; pub mod udp; pub use env_logger; pub use log; pub mod bytes_codec; #[cfg(feature = "quic")] pub mod quic; pub use anyhow::{self, bail}; pub use futures_util; pub mod config; pub mod fs; pub use lazy_static; #[cfg(not(any(target_os = "android", target_os = "ios")))] pub use mac_address; pub use rand; pub use regex; pub use sodiumoxide; pub use tokio_socks; pub use tokio_socks::IntoTargetAddr; pub use tokio_socks::TargetAddr; pub mod password_security; pub use chrono; pub use directories_next; pub use libc; pub mod keyboard; #[cfg(not(any(target_os = "android", target_os = "ios")))] pub use dlopen; #[cfg(not(any(target_os = "android", target_os = "ios")))] pub use machine_uid; pub use serde_derive; pub use serde_json; pub use sysinfo; pub use toml; pub use uuid; #[cfg(feature = "quic")] pub type Stream = quic::Connection; #[cfg(not(feature = "quic"))] pub type Stream = tcp::FramedStream; pub type SessionID = uuid::Uuid; #[inline] pub async fn sleep(sec: f32) { tokio::time::sleep(time::Duration::from_secs_f32(sec)).await; } #[macro_export] macro_rules! allow_err { ($e:expr) => { if let Err(err) = $e { log::debug!( "{:?}, {}:{}:{}:{}", err, module_path!(), file!(), line!(), column!() ); } else { } }; ($e:expr, $($arg:tt)*) => { if let Err(err) = $e { log::debug!( "{:?}, {}, {}:{}:{}:{}", err, format_args!($($arg)*), module_path!(), file!(), line!(), column!() ); } else { } }; } #[inline] pub fn timeout(ms: u64, future: T) -> tokio::time::Timeout { tokio::time::timeout(std::time::Duration::from_millis(ms), future) } pub type ResultType = anyhow::Result; /// Certain router and firewalls scan the packet and if they /// find an IP address belonging to their pool that they use to do the NAT mapping/translation, so here we mangle the ip address pub struct AddrMangle(); #[inline] pub fn try_into_v4(addr: SocketAddr) -> SocketAddr { match addr { SocketAddr::V6(v6) if !addr.ip().is_loopback() => { if let Some(v4) = v6.ip().to_ipv4() { SocketAddr::new(IpAddr::V4(v4), addr.port()) } else { addr } } _ => addr, } } impl AddrMangle { pub fn encode(addr: SocketAddr) -> Vec { // not work with [:1]: let addr = try_into_v4(addr); match addr { SocketAddr::V4(addr_v4) => { let tm = (SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap_or(std::time::Duration::ZERO) .as_micros() as u32) as u128; let ip = u32::from_le_bytes(addr_v4.ip().octets()) as u128; let port = addr.port() as u128; let v = ((ip + tm) << 49) | (tm << 17) | (port + (tm & 0xFFFF)); let bytes = v.to_le_bytes(); let mut n_padding = 0; for i in bytes.iter().rev() { if i == &0u8 { n_padding += 1; } else { break; } } bytes[..(16 - n_padding)].to_vec() } SocketAddr::V6(addr_v6) => { let mut x = addr_v6.ip().octets().to_vec(); let port: [u8; 2] = addr_v6.port().to_le_bytes(); x.push(port[0]); x.push(port[1]); x } } } pub fn decode(bytes: &[u8]) -> SocketAddr { use std::convert::TryInto; if bytes.len() > 16 { if bytes.len() != 18 { return Config::get_any_listen_addr(false); } let tmp: [u8; 2] = bytes[16..].try_into().unwrap_or_default(); let port = u16::from_le_bytes(tmp); let tmp: [u8; 16] = bytes[..16].try_into().unwrap_or_default(); let ip = std::net::Ipv6Addr::from(tmp); return SocketAddr::new(IpAddr::V6(ip), port); } let mut padded = [0u8; 16]; padded[..bytes.len()].copy_from_slice(bytes); let number = u128::from_le_bytes(padded); let tm = (number >> 17) & (u32::max_value() as u128); let ip = (((number >> 49) - tm) as u32).to_le_bytes(); let port = (number & 0xFFFFFF) - (tm & 0xFFFF); SocketAddr::V4(SocketAddrV4::new( Ipv4Addr::new(ip[0], ip[1], ip[2], ip[3]), port as u16, )) } } pub fn get_version_from_url(url: &str) -> String { let n = url.chars().count(); let a = url.chars().rev().position(|x| x == '-'); if let Some(a) = a { let b = url.chars().rev().position(|x| x == '.'); if let Some(b) = b { if a > b { if url .chars() .skip(n - b) .collect::() .parse::() .is_ok() { return url.chars().skip(n - a).collect(); } else { return url.chars().skip(n - a).take(a - b - 1).collect(); } } else { return url.chars().skip(n - a).collect(); } } } "".to_owned() } pub fn gen_version() { println!("cargo:rerun-if-changed=Cargo.toml"); use std::io::prelude::*; let mut file = File::create("./src/version.rs").unwrap(); for line in read_lines("Cargo.toml").unwrap().flatten() { let ab: Vec<&str> = line.split('=').map(|x| x.trim()).collect(); if ab.len() == 2 && ab[0] == "version" { file.write_all(format!("pub const VERSION: &str = {};\n", ab[1]).as_bytes()) .ok(); break; } } // generate build date let build_date = format!("{}", chrono::Local::now().format("%Y-%m-%d %H:%M")); file.write_all( format!("#[allow(dead_code)]\npub const BUILD_DATE: &str = \"{build_date}\";\n").as_bytes(), ) .ok(); file.sync_all().ok(); } fn read_lines

(filename: P) -> io::Result>> where P: AsRef, { let file = File::open(filename)?; Ok(io::BufReader::new(file).lines()) } pub fn is_valid_custom_id(id: &str) -> bool { regex::Regex::new(r"^[a-zA-Z]\w{5,15}$") .unwrap() .is_match(id) } // Support 1.1.10-1, the number after - is a patch version. pub fn get_version_number(v: &str) -> i64 { let mut versions = v.split('-'); let mut n = 0; // The first part is the version number. // 1.1.10 -> 1001100, 1.2.3 -> 1001030, multiple the last number by 10 // to leave space for patch version. if let Some(v) = versions.next() { let mut last = 0; for x in v.split('.') { last = x.parse::().unwrap_or(0); n = n * 1000 + last; } n -= last; n += last * 10; } if let Some(v) = versions.next() { n += v.parse::().unwrap_or(0); } // Ignore the rest n } pub fn get_modified_time(path: &std::path::Path) -> SystemTime { std::fs::metadata(path) .map(|m| m.modified().unwrap_or(UNIX_EPOCH)) .unwrap_or(UNIX_EPOCH) } pub fn get_created_time(path: &std::path::Path) -> SystemTime { std::fs::metadata(path) .map(|m| m.created().unwrap_or(UNIX_EPOCH)) .unwrap_or(UNIX_EPOCH) } pub fn get_exe_time() -> SystemTime { std::env::current_exe().map_or(UNIX_EPOCH, |path| { let m = get_modified_time(&path); let c = get_created_time(&path); if m > c { m } else { c } }) } pub fn get_uuid() -> Vec { #[cfg(not(any(target_os = "android", target_os = "ios")))] if let Ok(id) = machine_uid::get() { return id.into(); } Config::get_key_pair().1 } #[inline] pub fn get_time() -> i64 { std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .map(|d| d.as_millis()) .unwrap_or(0) as _ } #[inline] pub fn is_ipv4_str(id: &str) -> bool { if let Ok(reg) = regex::Regex::new( r"^(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(:\d+)?$", ) { reg.is_match(id) } else { false } } #[inline] pub fn is_ipv6_str(id: &str) -> bool { if let Ok(reg) = regex::Regex::new( r"^((([a-fA-F0-9]{1,4}:{1,2})+[a-fA-F0-9]{1,4})|(\[([a-fA-F0-9]{1,4}:{1,2})+[a-fA-F0-9]{1,4}\]:\d+))$", ) { reg.is_match(id) } else { false } } #[inline] pub fn is_ip_str(id: &str) -> bool { is_ipv4_str(id) || is_ipv6_str(id) } #[inline] pub fn is_domain_port_str(id: &str) -> bool { // modified regex for RFC1123 hostname. check https://stackoverflow.com/a/106223 for original version for hostname. // according to [TLD List](https://data.iana.org/TLD/tlds-alpha-by-domain.txt) version 2023011700, // there is no digits in TLD, and length is 2~63. if let Ok(reg) = regex::Regex::new( r"(?i)^([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z][a-z-]{0,61}[a-z]:\d{1,5}$", ) { reg.is_match(id) } else { false } } pub fn init_log(_is_async: bool, _name: &str) -> Option { static INIT: std::sync::Once = std::sync::Once::new(); #[allow(unused_mut)] let mut logger_holder: Option = None; INIT.call_once(|| { #[cfg(debug_assertions)] { use env_logger::*; init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info")); } #[cfg(not(debug_assertions))] { // https://docs.rs/flexi_logger/latest/flexi_logger/error_info/index.html#write // though async logger more efficient, but it also causes more problems, disable it for now let mut path = config::Config::log_path(); #[cfg(target_os = "android")] if !config::Config::get_home().exists() { return; } if !_name.is_empty() { path.push(_name); } use flexi_logger::*; if let Ok(x) = Logger::try_with_env_or_str("debug") { logger_holder = x .log_to_file(FileSpec::default().directory(path)) .write_mode(if _is_async { WriteMode::Async } else { WriteMode::Direct }) .format(opt_format) .rotate( Criterion::Age(Age::Day), Naming::Timestamps, Cleanup::KeepLogFiles(6), ) .start() .ok(); } } }); logger_holder } #[cfg(test)] mod test { use super::*; #[test] fn test_mangle() { let addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(192, 168, 16, 32), 21116)); assert_eq!(addr, AddrMangle::decode(&AddrMangle::encode(addr))); let addr = "[2001:db8::1]:8080".parse::().unwrap(); assert_eq!(addr, AddrMangle::decode(&AddrMangle::encode(addr))); let addr = "[2001:db8:ff::1111]:80".parse::().unwrap(); assert_eq!(addr, AddrMangle::decode(&AddrMangle::encode(addr))); } #[test] fn test_allow_err() { allow_err!(Err("test err") as Result<(), &str>); allow_err!( Err("test err with msg") as Result<(), &str>, "prompt {}", "failed" ); } #[test] fn test_ipv6() { assert!(is_ipv6_str("1:2:3")); assert!(is_ipv6_str("[ab:2:3]:12")); assert!(is_ipv6_str("[ABEF:2a:3]:12")); assert!(!is_ipv6_str("[ABEG:2a:3]:12")); assert!(!is_ipv6_str("1[ab:2:3]:12")); assert!(!is_ipv6_str("1.1.1.1")); assert!(is_ip_str("1.1.1.1")); assert!(!is_ipv6_str("1:2:")); assert!(is_ipv6_str("1:2::0")); assert!(is_ipv6_str("[1:2::0]:1")); assert!(!is_ipv6_str("[1:2::0]:")); assert!(!is_ipv6_str("1:2::0]:1")); } #[test] fn test_ipv4() { assert!(is_ipv4_str("1.2.3.4")); assert!(is_ipv4_str("1.2.3.4:90")); assert!(is_ipv4_str("192.168.0.1")); assert!(is_ipv4_str("0.0.0.0")); assert!(is_ipv4_str("255.255.255.255")); assert!(!is_ipv4_str("256.0.0.0")); assert!(!is_ipv4_str("256.256.256.256")); assert!(!is_ipv4_str("1:2:")); assert!(!is_ipv4_str("192.168.0.256")); assert!(!is_ipv4_str("192.168.0.1/24")); assert!(!is_ipv4_str("192.168.0.")); assert!(!is_ipv4_str("192.168..1")); } #[test] fn test_hostname_port() { assert!(!is_domain_port_str("a:12")); assert!(!is_domain_port_str("a.b.c:12")); assert!(is_domain_port_str("test.com:12")); assert!(is_domain_port_str("test-UPPER.com:12")); assert!(is_domain_port_str("some-other.domain.com:12")); assert!(!is_domain_port_str("under_score:12")); assert!(!is_domain_port_str("a@bc:12")); assert!(!is_domain_port_str("1.1.1.1:12")); assert!(!is_domain_port_str("1.2.3:12")); assert!(!is_domain_port_str("1.2.3.45:12")); assert!(!is_domain_port_str("a.b.c:123456")); assert!(!is_domain_port_str("---:12")); assert!(!is_domain_port_str(".:12")); // todo: should we also check for these edge cases? // out-of-range port assert!(is_domain_port_str("test.com:0")); assert!(is_domain_port_str("test.com:98989")); } #[test] fn test_mangle2() { let addr = "[::ffff:127.0.0.1]:8080".parse().unwrap(); let addr_v4 = "127.0.0.1:8080".parse().unwrap(); assert_eq!(AddrMangle::decode(&AddrMangle::encode(addr)), addr_v4); assert_eq!( AddrMangle::decode(&AddrMangle::encode("[::127.0.0.1]:8080".parse().unwrap())), addr_v4 ); assert_eq!(AddrMangle::decode(&AddrMangle::encode(addr_v4)), addr_v4); let addr_v6 = "[ef::fe]:8080".parse().unwrap(); assert_eq!(AddrMangle::decode(&AddrMangle::encode(addr_v6)), addr_v6); let addr_v6 = "[::1]:8080".parse().unwrap(); assert_eq!(AddrMangle::decode(&AddrMangle::encode(addr_v6)), addr_v6); } #[test] fn test_get_version_number() { assert_eq!(get_version_number("1.1.10"), 1001100); assert_eq!(get_version_number("1.1.10-1"), 1001101); assert_eq!(get_version_number("1.1.11-1"), 1001111); assert_eq!(get_version_number("1.2.3"), 1002030); } }