mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-04-07 22:07:16 +00:00
Currently, an error returned by `Tunnel::poll_next_event` is only logged. In other words, they are never fatal. This creates a tricky to understand relationship on what kind of errors should be returned from callbacks. Because connlib is used on multiple operating systems, it has no idea how fatal a particular error is. This PR removes all of these `Result` return values with the following consequences: - For Android, we now panic when a callback fails. This is a slight change in behaviour. I believe that previously, any exception thrown by a callback into Android was caught and returned as an error. Now, we panic because in the FFI layer, we don't have any information on how fatal the error is. For non-fatal errors, the Android app should simply not throw an exception. The panics will cause the connlib task to be shut down which triggers an `on_disconnect`. - For Swift, there is no behaviour change. The FFI layer already did not support `Result`s for those callbacks. I don't know how exceptions from Swift are translated across the FFI layer but there is no change to what we had before. - For the Tauri client: - I chose to log errors on ERROR level and continue gracefully for the DNS resolvers. - We panic in case the controller channel is full / closed. That should really never happen in practice though unless we are currently shutting down the app. Resolves: #4064.
133 lines
4.3 KiB
Rust
133 lines
4.3 KiB
Rust
//! This crates contains shared types and behavior between all the other libraries.
|
|
//!
|
|
//! This includes types provided by external crates, i.e. [boringtun] to make sure that
|
|
//! we are using the same version across our own crates.
|
|
|
|
mod callbacks;
|
|
pub mod error;
|
|
pub mod messages;
|
|
|
|
/// Module to generate and store a persistent device ID on disk
|
|
///
|
|
/// Only properly implemented on Linux and Windows (platforms with Tauri and headless client)
|
|
pub mod device_id;
|
|
|
|
// Must be compiled on Mac so the Mac runner can do `version-check` on `linux-client`
|
|
pub mod linux;
|
|
|
|
#[cfg(target_os = "windows")]
|
|
pub mod windows;
|
|
|
|
pub use boringtun::x25519::PublicKey;
|
|
pub use boringtun::x25519::StaticSecret;
|
|
pub use callbacks::{Callbacks, Cidrv4, Cidrv6};
|
|
pub use error::ConnlibError as Error;
|
|
pub use error::Result;
|
|
pub use phoenix_channel::{LoginUrl, LoginUrlError};
|
|
|
|
use rand_core::OsRng;
|
|
|
|
pub type Dname = domain::base::Dname<Vec<u8>>;
|
|
|
|
/// Bundle ID / App ID that the client uses to distinguish itself from other programs on the system
|
|
///
|
|
/// e.g. In ProgramData and AppData we use this to name our subdirectories for configs and data,
|
|
/// and Windows may use it to track things like the MSI installer, notification titles,
|
|
/// deep link registration, etc.
|
|
///
|
|
/// This should be identical to the `tauri.bundle.identifier` over in `tauri.conf.json`,
|
|
/// but sometimes I need to use this before Tauri has booted up, or in a place where
|
|
/// getting the Tauri app handle would be awkward.
|
|
///
|
|
/// Luckily this is also the AppUserModelId that Windows uses to label notifications,
|
|
/// so if your dev system has Firezone installed by MSI, the notifications will look right.
|
|
/// <https://learn.microsoft.com/en-us/windows/configuration/find-the-application-user-model-id-of-an-installed-app>
|
|
pub const BUNDLE_ID: &str = "dev.firezone.client";
|
|
|
|
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
|
const LIB_NAME: &str = "connlib";
|
|
|
|
pub fn keypair() -> (StaticSecret, PublicKey) {
|
|
let private_key = StaticSecret::random_from_rng(OsRng);
|
|
let public_key = PublicKey::from(&private_key);
|
|
|
|
(private_key, public_key)
|
|
}
|
|
|
|
pub fn get_user_agent(os_version_override: Option<String>) -> 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();
|
|
|
|
// iOS returns "Unknown", but we already know we're on iOS here
|
|
#[cfg(target_os = "ios")]
|
|
let os_type = "iOS";
|
|
#[cfg(not(target_os = "ios"))]
|
|
let os_type = info.os_type();
|
|
|
|
let os_version = os_version_override.unwrap_or(info.version().to_string());
|
|
let additional_info = additional_info();
|
|
let lib_version = VERSION;
|
|
let lib_name = LIB_NAME;
|
|
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<String> {
|
|
None
|
|
}
|
|
|
|
#[cfg(target_family = "unix")]
|
|
fn kernel_version() -> Option<String> {
|
|
#[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;
|
|
}
|
|
|
|
#[cfg_attr(
|
|
all(target_os = "linux", target_arch = "aarch64"),
|
|
allow(clippy::unnecessary_cast)
|
|
)]
|
|
let version: Vec<u8> = utsname
|
|
.release
|
|
.split(|c| *c == 0)
|
|
.next()?
|
|
.iter()
|
|
.map(|x| *x as u8)
|
|
.collect();
|
|
|
|
String::from_utf8(version).ok()
|
|
}
|