Merge pull request #90 from m1m1sha/feat/custom-hostname

Feat/custom hostname
This commit is contained in:
Sijie.Sun
2024-05-08 21:44:01 +08:00
committed by GitHub
16 changed files with 116 additions and 35 deletions
Generated
+3 -1
View File
@@ -387,7 +387,7 @@ dependencies = [
[[package]] [[package]]
name = "boringtun" name = "boringtun"
version = "0.6.0" version = "0.6.0"
source = "git+https://github.com/KKRainbow/boringtun.git#449204c3eca736dc23b075d81426527a357e2f2a" source = "git+https://github.com/EasyTier/boringtun.git#449204c3eca736dc23b075d81426527a357e2f2a"
dependencies = [ dependencies = [
"aead", "aead",
"atomic-shim", "atomic-shim",
@@ -1309,6 +1309,7 @@ dependencies = [
"quinn", "quinn",
"rand 0.8.5", "rand 0.8.5",
"rcgen", "rcgen",
"regex",
"reqwest", "reqwest",
"ring 0.17.8", "ring 0.17.8",
"rstest", "rstest",
@@ -1348,6 +1349,7 @@ dependencies = [
"chrono", "chrono",
"dashmap", "dashmap",
"easytier", "easytier",
"gethostname",
"once_cell", "once_cell",
"privilege", "privilege",
"serde", "serde",
+1
View File
@@ -32,6 +32,7 @@ settings: 设置
exchange_language: Switch to English exchange_language: Switch to English
exit: 退出 exit: 退出
chips_placeholder: 例如: {0}, 按回车添加 chips_placeholder: 例如: {0}, 按回车添加
hostname_placeholder: '留空默认为主机名: {0}'
off_text: 点击关闭 off_text: 点击关闭
on_text: 点击开启 on_text: 点击开启
show_config: 显示配置 show_config: 显示配置
+1
View File
@@ -33,6 +33,7 @@ exchange_language: 切换中文
exit: Exit exit: Exit
chips_placeholder: 'e.g: {0}, press Enter to add' chips_placeholder: 'e.g: {0}, press Enter to add'
hostname_placeholder: 'Leave blank and default to host name: {0}'
off_text: Press to disable off_text: Press to disable
on_text: Press to enable on_text: Press to enable
show_config: Show Config show_config: Show Config
+6 -2
View File
@@ -11,7 +11,11 @@ edition = "2021"
tauri-build = { version = "1", features = [] } tauri-build = { version = "1", features = [] }
[dependencies] [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 = { version = "1", features = ["derive"] }
serde_json = "1" serde_json = "1"
@@ -24,7 +28,7 @@ once_cell = "1.18.0"
dashmap = "5.5.3" dashmap = "5.5.3"
privilege = "0.3" privilege = "0.3"
gethostname = "0.4.3"
[features] [features]
# This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!! # This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!!
custom-protocol = ["tauri/custom-protocol"] custom-protocol = ["tauri/custom-protocol"]
+9 -1
View File
@@ -42,6 +42,7 @@ struct NetworkConfig {
instance_id: String, instance_id: String,
virtual_ipv4: String, virtual_ipv4: String,
hostname: Option<String>,
network_name: String, network_name: String,
network_secret: String, network_secret: String,
networking_method: NetworkingMethod, networking_method: NetworkingMethod,
@@ -70,6 +71,7 @@ impl NetworkConfig {
.parse() .parse()
.with_context(|| format!("failed to parse instance id: {}", self.instance_id))?, .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_inst_name(self.network_name.clone());
cfg.set_network_identity(NetworkIdentity::new( cfg.set_network_identity(NetworkIdentity::new(
self.network_name.clone(), self.network_name.clone(),
@@ -281,6 +283,11 @@ fn collect_network_infos() -> Result<String, String> {
Ok(serde_json::to_string(&ret).map_err(|e| e.to_string())?) Ok(serde_json::to_string(&ret).map_err(|e| e.to_string())?)
} }
#[tauri::command]
fn get_os_hostname() -> Result<String, String> {
Ok(gethostname::gethostname().to_string_lossy().to_string())
}
fn toggle_window_visibility(window: &Window) { fn toggle_window_visibility(window: &Window) {
if window.is_visible().unwrap() { if window.is_visible().unwrap() {
window.hide().unwrap(); window.hide().unwrap();
@@ -318,7 +325,8 @@ fn main() {
parse_network_config, parse_network_config,
run_network_instance, run_network_instance,
retain_network_instance, retain_network_instance,
collect_network_infos collect_network_infos,
get_os_hostname
]) ])
.system_tray(SystemTray::new().with_menu(tray_menu)) .system_tray(SystemTray::new().with_menu(tray_menu))
.on_system_tray_event(|app, event| match event { .on_system_tray_event(|app, event| match event {
+3
View File
@@ -20,6 +20,7 @@ declare global {
const getActivePinia: typeof import('pinia')['getActivePinia'] const getActivePinia: typeof import('pinia')['getActivePinia']
const getCurrentInstance: typeof import('vue')['getCurrentInstance'] const getCurrentInstance: typeof import('vue')['getCurrentInstance']
const getCurrentScope: typeof import('vue')['getCurrentScope'] const getCurrentScope: typeof import('vue')['getCurrentScope']
const getOsHostname: typeof import('./composables/network')['getOsHostname']
const h: typeof import('vue')['h'] const h: typeof import('vue')['h']
const inject: typeof import('vue')['inject'] const inject: typeof import('vue')['inject']
const isProxy: typeof import('vue')['isProxy'] const isProxy: typeof import('vue')['isProxy']
@@ -108,6 +109,7 @@ declare module 'vue' {
readonly getActivePinia: UnwrapRef<typeof import('pinia')['getActivePinia']> readonly getActivePinia: UnwrapRef<typeof import('pinia')['getActivePinia']>
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']> readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']> readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
readonly getOsHostname: UnwrapRef<typeof import('./composables/network')['getOsHostname']>
readonly h: UnwrapRef<typeof import('vue')['h']> readonly h: UnwrapRef<typeof import('vue')['h']>
readonly inject: UnwrapRef<typeof import('vue')['inject']> readonly inject: UnwrapRef<typeof import('vue')['inject']>
readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']> readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']>
@@ -189,6 +191,7 @@ declare module '@vue/runtime-core' {
readonly getActivePinia: UnwrapRef<typeof import('pinia')['getActivePinia']> readonly getActivePinia: UnwrapRef<typeof import('pinia')['getActivePinia']>
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']> readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']> readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
readonly getOsHostname: UnwrapRef<typeof import('./composables/network')['getOsHostname']>
readonly h: UnwrapRef<typeof import('vue')['h']> readonly h: UnwrapRef<typeof import('vue')['h']>
readonly inject: UnwrapRef<typeof import('vue')['inject']> readonly inject: UnwrapRef<typeof import('vue')['inject']>
readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']> readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']>
+28
View File
@@ -1,6 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import InputGroup from 'primevue/inputgroup' import InputGroup from 'primevue/inputgroup'
import InputGroupAddon from 'primevue/inputgroupaddon' import InputGroupAddon from 'primevue/inputgroupaddon'
import { getOsHostname } from '~/composables/network'
import { i18n } from '~/modules/i18n' import { i18n } from '~/modules/i18n'
import { NetworkingMethod } from '~/types/network' import { NetworkingMethod } from '~/types/network'
@@ -32,6 +33,24 @@ const curNetwork = computed(() => {
const presetPublicServers = [ const presetPublicServers = [
'tcp://easytier.public.kkrainbow.top:11010', 'tcp://easytier.public.kkrainbow.top:11010',
] ]
function validateHostname() {
if (curNetwork.value.hostname) {
// eslint no-useless-escape
let name = curNetwork.value.hostname!.replaceAll(/[^\u4E00-\u9FA5a-zA-Z0-9\-]*/g, '')
if (name.length > 32)
name = name.substring(0, 32)
if (curNetwork.value.hostname !== name)
curNetwork.value.hostname = name
}
}
const osHostname = ref<string>('')
onMounted(async () => {
osHostname.value = await getOsHostname()
})
</script> </script>
<template> <template>
@@ -151,6 +170,15 @@ const presetPublicServers = [
/> />
</div> </div>
</div> </div>
<div class="flex flex-row gap-x-9 flex-wrap">
<div class="flex flex-column gap-2 basis-5/12 grow">
<label for="hostname">{{ $t('hostname') }}</label>
<InputText
id="hostname" v-model="curNetwork.hostname" aria-describedby="hostname-help" :format="true"
:placeholder="$t('hostname_placeholder', [osHostname])" @blur="validateHostname"
/>
</div>
</div>
</div> </div>
</Panel> </Panel>
+8 -8
View File
@@ -348,14 +348,14 @@ function showEventLogs() {
{{ $t('peer_info') }} {{ $t('peer_info') }}
</template> </template>
<template #content> <template #content>
<DataTable :value="peerRouteInfos" table-style="min-width: 50rem"> <DataTable :value="peerRouteInfos" column-resize-mode="fit" table-style="width: 100%">
<Column field="route.ipv4_addr" :header="$t('virtual_ipv4')" /> <Column field="route.ipv4_addr" style="width: 100px;" :header="$t('virtual_ipv4')" />
<Column field="route.hostname" :header="$t('hostname')" /> <Column field="route.hostname" style="max-width: 250px;" :header="$t('hostname')" />
<Column :field="routeCost" :header="$t('route_cost')" /> <Column :field="routeCost" style="width: 60px;" :header="$t('route_cost')" />
<Column :field="latencyMs" :header="$t('latency')" /> <Column :field="latencyMs" style="width: 80px;" :header="$t('latency')" />
<Column :field="txBytes" :header="$t('upload_bytes')" /> <Column :field="txBytes" style="width: 80px;" :header="$t('upload_bytes')" />
<Column :field="rxBytes" :header="$t('download_bytes')" /> <Column :field="rxBytes" style="width: 80px;" :header="$t('download_bytes')" />
<Column :field="lossRate" :header="$t('loss_rate')" /> <Column :field="lossRate" style="width: 60px;" :header="$t('loss_rate')" />
</DataTable> </DataTable>
</template> </template>
</Card> </Card>
+4
View File
@@ -20,3 +20,7 @@ export async function collectNetworkInfos() {
const ret: string = await invoke('collect_network_infos', {}) const ret: string = await invoke('collect_network_infos', {})
return JSON.parse(ret) return JSON.parse(ret)
} }
export async function getOsHostname(): Promise<string> {
return await invoke('get_os_hostname')
}
+1
View File
@@ -10,6 +10,7 @@ export interface NetworkConfig {
instance_id: string instance_id: string
virtual_ipv4: string virtual_ipv4: string
hostname?: string
network_name: string network_name: string
network_secret: string network_secret: string
+2
View File
@@ -58,6 +58,8 @@ async-trait = "0.1.74"
dashmap = "5.5.3" dashmap = "5.5.3"
timedmap = "=1.0.1" timedmap = "=1.0.1"
regex = "1"
# for full-path zero-copy # for full-path zero-copy
zerocopy = { version = "0.7.32", features = ["derive", "simd"] } zerocopy = { version = "0.7.32", features = ["derive", "simd"] }
bytes = "1.5.0" bytes = "1.5.0"
+35
View File
@@ -14,6 +14,9 @@ pub trait ConfigLoader: Send + Sync {
fn get_id(&self) -> uuid::Uuid; fn get_id(&self) -> uuid::Uuid;
fn set_id(&self, id: uuid::Uuid); fn set_id(&self, id: uuid::Uuid);
fn get_hostname(&self) -> String;
fn set_hostname(&self, name: Option<String>);
fn get_inst_name(&self) -> String; fn get_inst_name(&self) -> String;
fn set_inst_name(&self, name: String); fn set_inst_name(&self, name: String);
@@ -152,6 +155,7 @@ pub struct Flags {
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] #[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
struct Config { struct Config {
netns: Option<String>, netns: Option<String>,
hostname: Option<String>,
instance_name: Option<String>, instance_name: Option<String>,
instance_id: Option<uuid::Uuid>, instance_id: Option<uuid::Uuid>,
ipv4: Option<String>, ipv4: Option<String>,
@@ -190,6 +194,7 @@ impl TomlConfigLoader {
config_str, config_str config_str, config_str
) )
})?; })?;
Ok(TomlConfigLoader { Ok(TomlConfigLoader {
config: Arc::new(Mutex::new(config)), config: Arc::new(Mutex::new(config)),
}) })
@@ -216,6 +221,36 @@ impl ConfigLoader for TomlConfigLoader {
self.config.lock().unwrap().instance_name = Some(name); 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::<String>();
}
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<String>) {
self.config.lock().unwrap().hostname = name;
}
fn get_netns(&self) -> Option<String> { fn get_netns(&self) -> Option<String> {
self.config.lock().unwrap().netns.clone() self.config.lock().unwrap().netns.clone()
} }
+5 -11
View File
@@ -54,7 +54,7 @@ pub struct GlobalCtx {
ip_collector: Arc<IPCollector>, ip_collector: Arc<IPCollector>,
hotname: AtomicCell<Option<String>>, hostname: String,
stun_info_collection: Box<dyn StunInfoCollectorTrait>, stun_info_collection: Box<dyn StunInfoCollectorTrait>,
@@ -80,6 +80,7 @@ impl GlobalCtx {
let id = config_fs.get_id(); let id = config_fs.get_id();
let network = config_fs.get_network_identity(); let network = config_fs.get_network_identity();
let net_ns = NetNS::new(config_fs.get_netns()); let net_ns = NetNS::new(config_fs.get_netns());
let hostname = config_fs.get_hostname();
let (event_bus, _) = tokio::sync::broadcast::channel(100); let (event_bus, _) = tokio::sync::broadcast::channel(100);
@@ -96,7 +97,7 @@ impl GlobalCtx {
ip_collector: Arc::new(IPCollector::new(net_ns)), ip_collector: Arc::new(IPCollector::new(net_ns)),
hotname: AtomicCell::new(None), hostname,
stun_info_collection: Box::new(StunInfoCollector::new_with_default_servers()), stun_info_collection: Box::new(StunInfoCollector::new_with_default_servers()),
@@ -165,15 +166,8 @@ impl GlobalCtx {
self.ip_collector.clone() self.ip_collector.clone()
} }
pub fn get_hostname(&self) -> Option<String> { pub fn get_hostname(&self) -> String {
if let Some(hostname) = self.hotname.take() { return self.hostname.clone();
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_stun_info_collector(&self) -> impl StunInfoCollectorTrait + '_ { pub fn get_stun_info_collector(&self) -> impl StunInfoCollectorTrait + '_ {
+6
View File
@@ -111,6 +111,9 @@ struct Cli {
#[arg(long, help = "directory to store log files")] #[arg(long, help = "directory to store log files")]
file_log_dir: Option<String>, file_log_dir: Option<String>,
#[arg(long, help = "host name to identify this device")]
hostname: Option<String>,
#[arg( #[arg(
short = 'm', short = 'm',
long, long,
@@ -177,6 +180,9 @@ impl From<Cli> for TomlConfigLoader {
let cfg = TomlConfigLoader::default(); let cfg = TomlConfigLoader::default();
cfg.set_inst_name(cli.instance_name.clone()); cfg.set_inst_name(cli.instance_name.clone());
cfg.set_hostname(cli.hostname.clone());
cfg.set_network_identity(NetworkIdentity::new( cfg.set_network_identity(NetworkIdentity::new(
cli.network_name.clone(), cli.network_name.clone(),
cli.network_secret.clone(), cli.network_secret.clone(),
+2 -6
View File
@@ -101,7 +101,7 @@ impl RoutePeerInfo {
.map(|x| x.to_string()) .map(|x| x.to_string())
.chain(global_ctx.get_vpn_portal_cidr().map(|x| x.to_string())) .chain(global_ctx.get_vpn_portal_cidr().map(|x| x.to_string()))
.collect(), .collect(),
hostname: global_ctx.get_hostname(), hostname: Some(global_ctx.get_hostname()),
udp_stun_info: global_ctx udp_stun_info: global_ctx
.get_stun_info_collector() .get_stun_info_collector()
.get_stun_info() .get_stun_info()
@@ -138,11 +138,7 @@ impl Into<crate::rpc::Route> for RoutePeerInfo {
next_hop_peer_id: 0, next_hop_peer_id: 0,
cost: self.cost as i32, cost: self.cost as i32,
proxy_cidrs: self.proxy_cidrs.clone(), proxy_cidrs: self.proxy_cidrs.clone(),
hostname: if let Some(hostname) = &self.hostname { hostname: self.hostname.unwrap_or_default(),
hostname.clone()
} else {
"".to_string()
},
stun_info: { stun_info: {
let mut stun_info = StunInfo::default(); let mut stun_info = StunInfo::default();
if let Ok(udp_nat_type) = NatType::try_from(self.udp_stun_info as i32) { if let Ok(udp_nat_type) = NatType::try_from(self.udp_stun_info as i32) {
+2 -6
View File
@@ -52,7 +52,7 @@ impl SyncPeerInfo {
.map(|x| x.to_string()) .map(|x| x.to_string())
.chain(global_ctx.get_vpn_portal_cidr().map(|x| x.to_string())) .chain(global_ctx.get_vpn_portal_cidr().map(|x| x.to_string()))
.collect(), .collect(),
hostname: global_ctx.get_hostname(), hostname: Some(global_ctx.get_hostname()),
udp_stun_info: global_ctx udp_stun_info: global_ctx
.get_stun_info_collector() .get_stun_info_collector()
.get_stun_info() .get_stun_info()
@@ -585,11 +585,7 @@ impl Route for BasicRoute {
route.next_hop_peer_id = route_info.peer_id; route.next_hop_peer_id = route_info.peer_id;
route.cost = route_info.cost as i32; route.cost = route_info.cost as i32;
route.proxy_cidrs = route_info.proxy_cidrs.clone(); route.proxy_cidrs = route_info.proxy_cidrs.clone();
route.hostname = if let Some(hostname) = &route_info.hostname { route.hostname = route_info.hostname.clone().unwrap_or_default();
hostname.clone()
} else {
"".to_string()
};
let mut stun_info = StunInfo::default(); let mut stun_info = StunInfo::default();
if let Ok(udp_nat_type) = NatType::try_from(route_info.udp_stun_info as i32) { if let Ok(udp_nat_type) = NatType::try_from(route_info.udp_stun_info as i32) {