From eed9608dd06d0c7c57c8b8ccd30cbd4e9030fd46 Mon Sep 17 00:00:00 2001 From: Gabi Date: Fri, 22 Dec 2023 00:03:08 -0300 Subject: [PATCH] connlib: add arch and kernel version to user agent (#2987) Fixes #2470, now for linux it looks like: ``` Alpine Linux/3.19.0 (x86_64;5.15.133.1-microsoft-standard-WSL2;) connlib/1.0.0 ``` For macos it looks like: ``` Mac OS/13.4.1 (arm64;22.5.0;) connlib/1.0.0 ``` and this is how it looks on android: ``` Android/Unknown 6.1.23-android14-4-00257-g7e35917775b8-ab9964412 connlib/1.0.0 ``` note: seems like in android emulator at least we can't get the architecture so easily --- rust/connlib/shared/src/lib.rs | 60 ++++++++++++++++++- .../tunnel/src/control_protocol/client.rs | 1 + 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/rust/connlib/shared/src/lib.rs b/rust/connlib/shared/src/lib.rs index 8a74c6bd9..9b1f64239 100644 --- a/rust/connlib/shared/src/lib.rs +++ b/rust/connlib/shared/src/lib.rs @@ -76,12 +76,70 @@ pub enum Mode { } pub fn get_user_agent() -> String { + // Note: we could switch to sys-info and get the hostname + // but we lose the arch + // and neither of the libraries provide the kernel version. + // so I rather keep os_info which seems like the most popular + // and keep implementing things that we are missing on top let info = os_info::get(); let os_type = info.os_type(); let os_version = info.version(); + let additional_info = additional_info(); let lib_version = VERSION; let lib_name = LIB_NAME; - format!("{os_type}/{os_version} {lib_name}/{lib_version}") + format!("{os_type}/{os_version}{additional_info}{lib_name}/{lib_version}") +} + +fn additional_info() -> String { + let info = os_info::get(); + match (info.architecture(), kernel_version()) { + (None, None) => " ".to_string(), + (None, Some(k)) => format!(" {k} "), + (Some(a), None) => format!(" {a} "), + (Some(a), Some(k)) => format!(" ({a};{k};) "), + } +} + +#[cfg(not(target_family = "unix"))] +fn kernel_version() -> Option { + None +} + +#[cfg(target_family = "unix")] +fn kernel_version() -> Option { + #[cfg(any(target_os = "android", target_os = "linux"))] + let mut utsname = libc::utsname { + sysname: [0; 65], + nodename: [0; 65], + release: [0; 65], + version: [0; 65], + machine: [0; 65], + domainname: [0; 65], + }; + + #[cfg(any(target_os = "macos", target_os = "ios"))] + let mut utsname = libc::utsname { + sysname: [0; 256], + nodename: [0; 256], + release: [0; 256], + version: [0; 256], + machine: [0; 256], + }; + + // SAFETY: we just allocated the pointer + if unsafe { libc::uname(&mut utsname as *mut _) } != 0 { + return None; + } + + let version: Vec = utsname + .release + .split(|c| *c == 0) + .next()? + .iter() + .map(|x| *x as u8) + .collect(); + + String::from_utf8(version).ok() } #[cfg(not(target_os = "windows"))] diff --git a/rust/connlib/tunnel/src/control_protocol/client.rs b/rust/connlib/tunnel/src/control_protocol/client.rs index 822f31e7e..a7ed8b5d1 100644 --- a/rust/connlib/tunnel/src/control_protocol/client.rs +++ b/rust/connlib/tunnel/src/control_protocol/client.rs @@ -260,6 +260,7 @@ where // We should never get a domain_response for a CIDR resource! return Err(Error::ControlProtocolError); }; + let resource_description = DnsResource::from_description(&resource_description, domain_response.domain.clone());