diff --git a/Cargo.lock b/Cargo.lock index 3dc6e306..91b2dfa9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -387,7 +387,7 @@ dependencies = [ [[package]] name = "boringtun" version = "0.6.0" -source = "git+https://github.com/KKRainbow/boringtun.git#449204c3eca736dc23b075d81426527a357e2f2a" +source = "git+https://github.com/EasyTier/boringtun.git#449204c3eca736dc23b075d81426527a357e2f2a" dependencies = [ "aead", "atomic-shim", @@ -1309,6 +1309,7 @@ dependencies = [ "quinn", "rand 0.8.5", "rcgen", + "regex", "reqwest", "ring 0.17.8", "rstest", @@ -1348,6 +1349,7 @@ dependencies = [ "chrono", "dashmap", "easytier", + "gethostname", "once_cell", "privilege", "serde", diff --git a/easytier-gui/locales/cn.yml b/easytier-gui/locales/cn.yml index 4e6be22b..63cb2bb2 100644 --- a/easytier-gui/locales/cn.yml +++ b/easytier-gui/locales/cn.yml @@ -32,6 +32,7 @@ settings: 设置 exchange_language: Switch to English exit: 退出 chips_placeholder: 例如: {0}, 按回车添加 +hostname_placeholder: '留空默认为主机名: {0}' off_text: 点击关闭 on_text: 点击开启 show_config: 显示配置 diff --git a/easytier-gui/locales/en.yml b/easytier-gui/locales/en.yml index 6c80c27e..b19c362d 100644 --- a/easytier-gui/locales/en.yml +++ b/easytier-gui/locales/en.yml @@ -33,6 +33,7 @@ exchange_language: 切换中文 exit: Exit chips_placeholder: 'e.g: {0}, press Enter to add' +hostname_placeholder: 'Leave blank and default to host name: {0}' off_text: Press to disable on_text: Press to enable show_config: Show Config diff --git a/easytier-gui/src-tauri/Cargo.toml b/easytier-gui/src-tauri/Cargo.toml index c98b2315..bf702f88 100644 --- a/easytier-gui/src-tauri/Cargo.toml +++ b/easytier-gui/src-tauri/Cargo.toml @@ -11,7 +11,11 @@ edition = "2021" tauri-build = { version = "1", features = [] } [dependencies] -tauri = { version = "1", features = [ "process-exit", "system-tray", "shell-open"] } +tauri = { version = "1", features = [ + "process-exit", + "system-tray", + "shell-open", +] } serde = { version = "1", features = ["derive"] } serde_json = "1" @@ -24,7 +28,7 @@ once_cell = "1.18.0" dashmap = "5.5.3" privilege = "0.3" - +gethostname = "0.4.3" [features] # This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!! custom-protocol = ["tauri/custom-protocol"] diff --git a/easytier-gui/src-tauri/src/main.rs b/easytier-gui/src-tauri/src/main.rs index ad4a4723..6ce7ce36 100644 --- a/easytier-gui/src-tauri/src/main.rs +++ b/easytier-gui/src-tauri/src/main.rs @@ -42,6 +42,7 @@ struct NetworkConfig { instance_id: String, virtual_ipv4: String, + hostname: Option, network_name: String, network_secret: String, networking_method: NetworkingMethod, @@ -70,6 +71,7 @@ impl NetworkConfig { .parse() .with_context(|| format!("failed to parse instance id: {}", self.instance_id))?, ); + cfg.set_hostname(self.hostname.clone()); cfg.set_inst_name(self.network_name.clone()); cfg.set_network_identity(NetworkIdentity::new( self.network_name.clone(), @@ -281,6 +283,11 @@ fn collect_network_infos() -> Result { Ok(serde_json::to_string(&ret).map_err(|e| e.to_string())?) } +#[tauri::command] +fn get_os_hostname() -> Result { + Ok(gethostname::gethostname().to_string_lossy().to_string()) +} + fn toggle_window_visibility(window: &Window) { if window.is_visible().unwrap() { window.hide().unwrap(); @@ -318,7 +325,8 @@ fn main() { parse_network_config, run_network_instance, retain_network_instance, - collect_network_infos + collect_network_infos, + get_os_hostname ]) .system_tray(SystemTray::new().with_menu(tray_menu)) .on_system_tray_event(|app, event| match event { diff --git a/easytier-gui/src/auto-imports.d.ts b/easytier-gui/src/auto-imports.d.ts index 9fbc786e..d337cfbb 100644 --- a/easytier-gui/src/auto-imports.d.ts +++ b/easytier-gui/src/auto-imports.d.ts @@ -20,6 +20,7 @@ declare global { const getActivePinia: typeof import('pinia')['getActivePinia'] const getCurrentInstance: typeof import('vue')['getCurrentInstance'] const getCurrentScope: typeof import('vue')['getCurrentScope'] + const getOsHostname: typeof import('./composables/network')['getOsHostname'] const h: typeof import('vue')['h'] const inject: typeof import('vue')['inject'] const isProxy: typeof import('vue')['isProxy'] @@ -108,6 +109,7 @@ declare module 'vue' { readonly getActivePinia: UnwrapRef readonly getCurrentInstance: UnwrapRef readonly getCurrentScope: UnwrapRef + readonly getOsHostname: UnwrapRef readonly h: UnwrapRef readonly inject: UnwrapRef readonly isProxy: UnwrapRef @@ -189,6 +191,7 @@ declare module '@vue/runtime-core' { readonly getActivePinia: UnwrapRef readonly getCurrentInstance: UnwrapRef readonly getCurrentScope: UnwrapRef + readonly getOsHostname: UnwrapRef readonly h: UnwrapRef readonly inject: UnwrapRef readonly isProxy: UnwrapRef diff --git a/easytier-gui/src/components/Config.vue b/easytier-gui/src/components/Config.vue index 947a1a88..aac1b777 100644 --- a/easytier-gui/src/components/Config.vue +++ b/easytier-gui/src/components/Config.vue @@ -1,6 +1,7 @@ diff --git a/easytier-gui/src/composables/network.ts b/easytier-gui/src/composables/network.ts index 90455eb0..7314803d 100644 --- a/easytier-gui/src/composables/network.ts +++ b/easytier-gui/src/composables/network.ts @@ -20,3 +20,7 @@ export async function collectNetworkInfos() { const ret: string = await invoke('collect_network_infos', {}) return JSON.parse(ret) } + +export async function getOsHostname(): Promise { + return await invoke('get_os_hostname') +} \ No newline at end of file diff --git a/easytier-gui/src/types/network.ts b/easytier-gui/src/types/network.ts index 4785c976..9b664903 100644 --- a/easytier-gui/src/types/network.ts +++ b/easytier-gui/src/types/network.ts @@ -10,6 +10,7 @@ export interface NetworkConfig { instance_id: string virtual_ipv4: string + hostname?: string network_name: string network_secret: string diff --git a/easytier/Cargo.toml b/easytier/Cargo.toml index 73822665..e78fca7b 100644 --- a/easytier/Cargo.toml +++ b/easytier/Cargo.toml @@ -58,6 +58,8 @@ async-trait = "0.1.74" dashmap = "5.5.3" timedmap = "=1.0.1" +regex = "1" + # for full-path zero-copy zerocopy = { version = "0.7.32", features = ["derive", "simd"] } bytes = "1.5.0" diff --git a/easytier/src/common/config.rs b/easytier/src/common/config.rs index 5564a959..9604e373 100644 --- a/easytier/src/common/config.rs +++ b/easytier/src/common/config.rs @@ -14,6 +14,9 @@ pub trait ConfigLoader: Send + Sync { fn get_id(&self) -> uuid::Uuid; fn set_id(&self, id: uuid::Uuid); + fn get_hostname(&self) -> String; + fn set_hostname(&self, name: Option); + fn get_inst_name(&self) -> String; fn set_inst_name(&self, name: String); @@ -152,6 +155,7 @@ pub struct Flags { #[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] struct Config { netns: Option, + hostname: Option, instance_name: Option, instance_id: Option, ipv4: Option, @@ -190,6 +194,7 @@ impl TomlConfigLoader { config_str, config_str ) })?; + Ok(TomlConfigLoader { config: Arc::new(Mutex::new(config)), }) @@ -216,6 +221,36 @@ impl ConfigLoader for TomlConfigLoader { self.config.lock().unwrap().instance_name = Some(name); } + fn get_hostname(&self) -> String { + let hostname = self.config.lock().unwrap().hostname.clone(); + + match hostname { + Some(hostname) => { + if !hostname.is_empty() { + let re = regex::Regex::new(r"[^\u4E00-\u9FA5a-zA-Z0-9\-]*").unwrap(); + let mut name = re.replace_all(&hostname, "").to_string(); + + if name.len() > 32 { + name = name.chars().take(32).collect::(); + } + + if hostname != name { + self.set_hostname(Some(name.clone())); + } + name + } else { + self.set_hostname(None); + gethostname::gethostname().to_string_lossy().to_string() + } + } + None => gethostname::gethostname().to_string_lossy().to_string(), + } + } + + fn set_hostname(&self, name: Option) { + self.config.lock().unwrap().hostname = name; + } + fn get_netns(&self) -> Option { self.config.lock().unwrap().netns.clone() } diff --git a/easytier/src/common/global_ctx.rs b/easytier/src/common/global_ctx.rs index 9a71f10f..d574311e 100644 --- a/easytier/src/common/global_ctx.rs +++ b/easytier/src/common/global_ctx.rs @@ -54,7 +54,7 @@ pub struct GlobalCtx { ip_collector: Arc, - hotname: AtomicCell>, + hostname: String, stun_info_collection: Box, @@ -80,6 +80,7 @@ impl GlobalCtx { let id = config_fs.get_id(); let network = config_fs.get_network_identity(); let net_ns = NetNS::new(config_fs.get_netns()); + let hostname = config_fs.get_hostname(); let (event_bus, _) = tokio::sync::broadcast::channel(100); @@ -96,7 +97,7 @@ impl GlobalCtx { ip_collector: Arc::new(IPCollector::new(net_ns)), - hotname: AtomicCell::new(None), + hostname, stun_info_collection: Box::new(StunInfoCollector::new_with_default_servers()), @@ -165,15 +166,8 @@ impl GlobalCtx { self.ip_collector.clone() } - pub fn get_hostname(&self) -> Option { - if let Some(hostname) = self.hotname.take() { - self.hotname.store(Some(hostname.clone())); - return Some(hostname); - } - - let hostname = gethostname::gethostname().to_string_lossy().to_string(); - self.hotname.store(Some(hostname.clone())); - return Some(hostname); + pub fn get_hostname(&self) -> String { + return self.hostname.clone(); } pub fn get_stun_info_collector(&self) -> impl StunInfoCollectorTrait + '_ { diff --git a/easytier/src/easytier-core.rs b/easytier/src/easytier-core.rs index 3bbc25fa..3860664a 100644 --- a/easytier/src/easytier-core.rs +++ b/easytier/src/easytier-core.rs @@ -111,6 +111,9 @@ struct Cli { #[arg(long, help = "directory to store log files")] file_log_dir: Option, + #[arg(long, help = "host name to identify this device")] + hostname: Option, + #[arg( short = 'm', long, @@ -177,6 +180,9 @@ impl From for TomlConfigLoader { let cfg = TomlConfigLoader::default(); cfg.set_inst_name(cli.instance_name.clone()); + + cfg.set_hostname(cli.hostname.clone()); + cfg.set_network_identity(NetworkIdentity::new( cli.network_name.clone(), cli.network_secret.clone(), diff --git a/easytier/src/peers/peer_ospf_route.rs b/easytier/src/peers/peer_ospf_route.rs index b7b9624c..59bdbf43 100644 --- a/easytier/src/peers/peer_ospf_route.rs +++ b/easytier/src/peers/peer_ospf_route.rs @@ -101,7 +101,7 @@ impl RoutePeerInfo { .map(|x| x.to_string()) .chain(global_ctx.get_vpn_portal_cidr().map(|x| x.to_string())) .collect(), - hostname: global_ctx.get_hostname(), + hostname: Some(global_ctx.get_hostname()), udp_stun_info: global_ctx .get_stun_info_collector() .get_stun_info() @@ -138,11 +138,7 @@ impl Into for RoutePeerInfo { next_hop_peer_id: 0, cost: self.cost as i32, proxy_cidrs: self.proxy_cidrs.clone(), - hostname: if let Some(hostname) = &self.hostname { - hostname.clone() - } else { - "".to_string() - }, + hostname: self.hostname.unwrap_or_default(), stun_info: { let mut stun_info = StunInfo::default(); if let Ok(udp_nat_type) = NatType::try_from(self.udp_stun_info as i32) { diff --git a/easytier/src/peers/peer_rip_route.rs b/easytier/src/peers/peer_rip_route.rs index fda9e5a3..2ce0a05d 100644 --- a/easytier/src/peers/peer_rip_route.rs +++ b/easytier/src/peers/peer_rip_route.rs @@ -52,7 +52,7 @@ impl SyncPeerInfo { .map(|x| x.to_string()) .chain(global_ctx.get_vpn_portal_cidr().map(|x| x.to_string())) .collect(), - hostname: global_ctx.get_hostname(), + hostname: Some(global_ctx.get_hostname()), udp_stun_info: global_ctx .get_stun_info_collector() .get_stun_info() @@ -585,11 +585,7 @@ impl Route for BasicRoute { route.next_hop_peer_id = route_info.peer_id; route.cost = route_info.cost as i32; route.proxy_cidrs = route_info.proxy_cidrs.clone(); - route.hostname = if let Some(hostname) = &route_info.hostname { - hostname.clone() - } else { - "".to_string() - }; + route.hostname = route_info.hostname.clone().unwrap_or_default(); let mut stun_info = StunInfo::default(); if let Ok(udp_nat_type) = NatType::try_from(route_info.udp_stun_info as i32) {