mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-01-27 10:18:54 +00:00
fix(ios/android): Pass device name and os version as overrides over connect (#3036)
Fixes #3035 Fixes #3037 # Before <img width="738" alt="Screenshot 2023-12-28 at 8 05 31 AM" src="https://github.com/firezone/firezone/assets/167144/c7ab4d74-672c-4536-97fe-f75d8d158bfb"> <img width="546" alt="Screenshot 2023-12-28 at 6 12 30 PM" src="https://github.com/firezone/firezone/assets/167144/1bd4ba98-d11d-4277-bd14-b0afcdf78119"> # After <img width="742" alt="Screenshot 2023-12-28 at 10 48 31 AM" src="https://github.com/firezone/firezone/assets/167144/96054f82-069f-47f7-862c-986455ef76c0"> <img width="744" alt="Screenshot 2023-12-28 at 6 29 37 PM" src="https://github.com/firezone/firezone/assets/167144/4ffc19b6-7c87-4ccb-bcfe-cb0e76fe95b7">
This commit is contained in:
@@ -9,6 +9,7 @@ import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.VpnService
|
||||
import android.os.Build
|
||||
import android.system.OsConstants
|
||||
import android.util.Log
|
||||
import androidx.core.app.NotificationCompat
|
||||
@@ -185,6 +186,8 @@ class TunnelService : VpnService() {
|
||||
apiUrl = config.apiUrl,
|
||||
token = config.token,
|
||||
deviceId = deviceId(),
|
||||
deviceName = Build.MODEL,
|
||||
osVersion = Build.VERSION.RELEASE,
|
||||
logDir = getLogDir(),
|
||||
logFilter = config.logFilter,
|
||||
callback = callback,
|
||||
|
||||
@@ -6,6 +6,8 @@ object TunnelSession {
|
||||
apiUrl: String,
|
||||
token: String,
|
||||
deviceId: String,
|
||||
deviceName: String,
|
||||
osVersion: String,
|
||||
logDir: String,
|
||||
logFilter: String,
|
||||
callback: Any,
|
||||
|
||||
@@ -382,11 +382,16 @@ macro_rules! string_from_jstring {
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: Refactor this when we refactor PhoenixChannel.
|
||||
// See https://github.com/firezone/firezone/issues/2158
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn connect(
|
||||
env: &mut JNIEnv,
|
||||
api_url: JString,
|
||||
token: JString,
|
||||
device_id: JString,
|
||||
device_name: JString,
|
||||
os_version: JString,
|
||||
log_dir: JString,
|
||||
log_filter: JString,
|
||||
callback_handler: GlobalRef,
|
||||
@@ -394,6 +399,8 @@ fn connect(
|
||||
let api_url = string_from_jstring!(env, api_url);
|
||||
let secret = SecretString::from(string_from_jstring!(env, token));
|
||||
let device_id = string_from_jstring!(env, device_id);
|
||||
let device_name = string_from_jstring!(env, device_name);
|
||||
let os_version = string_from_jstring!(env, os_version);
|
||||
let log_dir = string_from_jstring!(env, log_dir);
|
||||
let log_filter = string_from_jstring!(env, log_filter);
|
||||
|
||||
@@ -409,6 +416,8 @@ fn connect(
|
||||
api_url.as_str(),
|
||||
secret,
|
||||
device_id,
|
||||
Some(device_name),
|
||||
Some(os_version),
|
||||
callback_handler,
|
||||
Duration::from_secs(5 * 60),
|
||||
)?;
|
||||
@@ -427,6 +436,8 @@ pub unsafe extern "system" fn Java_dev_firezone_android_tunnel_TunnelSession_con
|
||||
api_url: JString,
|
||||
token: JString,
|
||||
device_id: JString,
|
||||
device_name: JString,
|
||||
os_version: JString,
|
||||
log_dir: JString,
|
||||
log_filter: JString,
|
||||
callback_handler: JObject,
|
||||
@@ -441,6 +452,8 @@ pub unsafe extern "system" fn Java_dev_firezone_android_tunnel_TunnelSession_con
|
||||
api_url,
|
||||
token,
|
||||
device_id,
|
||||
device_name,
|
||||
os_version,
|
||||
log_dir,
|
||||
log_filter,
|
||||
callback_handler,
|
||||
|
||||
@@ -24,6 +24,8 @@ mod ffi {
|
||||
api_url: String,
|
||||
token: String,
|
||||
device_id: String,
|
||||
device_name_override: Option<String>,
|
||||
os_version_override: Option<String>,
|
||||
log_dir: String,
|
||||
log_filter: String,
|
||||
callback_handler: CallbackHandler,
|
||||
@@ -174,10 +176,15 @@ fn init_logging(log_dir: PathBuf, log_filter: String) -> file_logger::Handle {
|
||||
}
|
||||
|
||||
impl WrappedSession {
|
||||
// TODO: Refactor this when we refactor PhoenixChannel.
|
||||
// See https://github.com/firezone/firezone/issues/2158
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn connect(
|
||||
api_url: String,
|
||||
token: String,
|
||||
device_id: String,
|
||||
device_name_override: Option<String>,
|
||||
os_version_override: Option<String>,
|
||||
log_dir: String,
|
||||
log_filter: String,
|
||||
callback_handler: ffi::CallbackHandler,
|
||||
@@ -188,6 +195,8 @@ impl WrappedSession {
|
||||
api_url.as_str(),
|
||||
secret,
|
||||
device_id,
|
||||
device_name_override,
|
||||
os_version_override,
|
||||
CallbackHandler {
|
||||
inner: Arc::new(callback_handler),
|
||||
handle: init_logging(log_dir.into(), log_filter),
|
||||
|
||||
@@ -65,6 +65,8 @@ where
|
||||
api_url: impl TryInto<Url>,
|
||||
token: SecretString,
|
||||
device_id: String,
|
||||
device_name_override: Option<String>,
|
||||
os_version_override: Option<String>,
|
||||
callbacks: CB,
|
||||
max_partition_time: Duration,
|
||||
) -> Result<Self> {
|
||||
@@ -111,6 +113,8 @@ where
|
||||
api_url.try_into().map_err(|_| Error::UriError)?,
|
||||
token,
|
||||
device_id,
|
||||
device_name_override,
|
||||
os_version_override,
|
||||
this.callbacks.clone(),
|
||||
max_partition_time,
|
||||
);
|
||||
@@ -122,18 +126,23 @@ where
|
||||
Ok(this)
|
||||
}
|
||||
|
||||
// TODO: Refactor this when we refactor PhoenixChannel.
|
||||
// See https://github.com/firezone/firezone/issues/2158
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn connect_inner(
|
||||
runtime: &Runtime,
|
||||
runtime_stopper: tokio::sync::mpsc::Sender<StopRuntime>,
|
||||
api_url: Url,
|
||||
token: SecretString,
|
||||
device_id: String,
|
||||
device_name_override: Option<String>,
|
||||
os_version_override: Option<String>,
|
||||
callbacks: CallbackErrorFacade<CB>,
|
||||
max_partition_time: Duration,
|
||||
) {
|
||||
runtime.spawn(async move {
|
||||
let (connect_url, private_key) = fatal_error!(
|
||||
login_url(Mode::Client, api_url, token, device_id, None),
|
||||
login_url(Mode::Client, api_url, token, device_id, device_name_override),
|
||||
runtime_stopper,
|
||||
&callbacks
|
||||
);
|
||||
@@ -143,7 +152,7 @@ where
|
||||
// to force queue ordering.
|
||||
let (control_plane_sender, mut control_plane_receiver) = tokio::sync::mpsc::channel(1);
|
||||
|
||||
let mut connection = PhoenixChannel::<_, IngressMessages, ReplyMessages, Messages>::new(Secret::new(SecureUrl::from_url(connect_url)), move |msg, reference, topic| {
|
||||
let mut connection = PhoenixChannel::<_, IngressMessages, ReplyMessages, Messages>::new(Secret::new(SecureUrl::from_url(connect_url)), os_version_override, move |msg, reference, topic| {
|
||||
let control_plane_sender = control_plane_sender.clone();
|
||||
async move {
|
||||
tracing::trace!(?msg);
|
||||
|
||||
@@ -64,6 +64,7 @@ impl secrecy::Zeroize for SecureUrl {
|
||||
/// `await` it, it will block until you use `close` in a [PhoenixSender], the portal close the connection or something goes wrong.
|
||||
pub struct PhoenixChannel<F, I, R, M> {
|
||||
secret_url: Secret<SecureUrl>,
|
||||
os_version_override: Option<String>,
|
||||
handler: F,
|
||||
sender: Sender<Message>,
|
||||
receiver: Receiver<Message>,
|
||||
@@ -71,7 +72,10 @@ pub struct PhoenixChannel<F, I, R, M> {
|
||||
}
|
||||
|
||||
// This is basically the same as tungstenite does but we add some new headers (namely user-agent)
|
||||
fn make_request(secret_url: &Secret<SecureUrl>) -> Result<Request> {
|
||||
fn make_request(
|
||||
secret_url: &Secret<SecureUrl>,
|
||||
os_version_override: Option<String>,
|
||||
) -> Result<Request> {
|
||||
use secrecy::ExposeSecret;
|
||||
|
||||
let host = secret_url
|
||||
@@ -96,7 +100,7 @@ fn make_request(secret_url: &Secret<SecureUrl>) -> Result<Request> {
|
||||
.header("Upgrade", "websocket")
|
||||
.header("Sec-WebSocket-Version", "13")
|
||||
.header("Sec-WebSocket-Key", key)
|
||||
.header("User-Agent", get_user_agent())
|
||||
.header("User-Agent", get_user_agent(os_version_override))
|
||||
.uri(secret_url.expose_secret().inner.as_str())
|
||||
.body(())?;
|
||||
Ok(req)
|
||||
@@ -127,7 +131,11 @@ where
|
||||
) -> Result<()> {
|
||||
tracing::trace!("Trying to connect to portal...");
|
||||
|
||||
let (ws_stream, _) = connect_async(make_request(&self.secret_url)?).await?;
|
||||
let (ws_stream, _) = connect_async(make_request(
|
||||
&self.secret_url,
|
||||
self.os_version_override.clone(),
|
||||
)?)
|
||||
.await?;
|
||||
|
||||
tracing::trace!("Successfully connected to portal");
|
||||
|
||||
@@ -269,13 +277,18 @@ where
|
||||
/// - `handler`: The handle that will be called for each received message.
|
||||
///
|
||||
/// For more info see [struct-level docs][PhoenixChannel].
|
||||
pub fn new(secret_url: Secret<SecureUrl>, handler: F) -> Self {
|
||||
pub fn new(
|
||||
secret_url: Secret<SecureUrl>,
|
||||
os_version_override: Option<String>,
|
||||
handler: F,
|
||||
) -> Self {
|
||||
let (sender, receiver) = channel(CHANNEL_SIZE);
|
||||
|
||||
Self {
|
||||
sender,
|
||||
receiver,
|
||||
secret_url,
|
||||
os_version_override,
|
||||
handler,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
|
||||
@@ -75,15 +75,21 @@ pub enum Mode {
|
||||
Gateway,
|
||||
}
|
||||
|
||||
pub fn get_user_agent() -> String {
|
||||
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 = info.version();
|
||||
|
||||
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;
|
||||
|
||||
@@ -173,7 +173,7 @@ async fn connect_to_portal(
|
||||
loop {
|
||||
let result = phoenix_channel::init::<InitGateway, _, _>(
|
||||
Secret::new(SecureUrl::from_url(connect_url.clone())),
|
||||
get_user_agent(),
|
||||
get_user_agent(None),
|
||||
PHOENIX_TOPIC,
|
||||
(),
|
||||
)
|
||||
|
||||
@@ -15,6 +15,8 @@ fn main() -> Result<()> {
|
||||
cli.common.api_url,
|
||||
SecretString::from(cli.common.token),
|
||||
cli.firezone_id,
|
||||
None,
|
||||
None,
|
||||
CallbackHandler { handle },
|
||||
cli.max_partition_time.into(),
|
||||
)
|
||||
|
||||
@@ -395,6 +395,8 @@ impl Controller {
|
||||
self.advanced_settings.api_url.clone(),
|
||||
auth_info.token.clone(),
|
||||
self.device_id.clone(),
|
||||
None, // TODO: Send device name here (windows computer name)
|
||||
None,
|
||||
callback_handler.clone(),
|
||||
Duration::from_secs(5 * 60),
|
||||
)?;
|
||||
|
||||
@@ -143,8 +143,15 @@ class Adapter {
|
||||
do {
|
||||
self.state = .startingTunnel(
|
||||
session: try WrappedSession.connect(
|
||||
self.controlPlaneURLString, self.token, self.getDeviceId(), self.connlibLogFolderPath,
|
||||
self.logFilter, self.callbackHandler),
|
||||
self.controlPlaneURLString,
|
||||
self.token,
|
||||
self.getDeviceId(),
|
||||
self.getDeviceName(),
|
||||
self.getOSVersion(),
|
||||
self.connlibLogFolderPath,
|
||||
self.logFilter,
|
||||
self.callbackHandler
|
||||
),
|
||||
onStarted: completionHandler
|
||||
)
|
||||
} catch let error {
|
||||
@@ -219,9 +226,31 @@ class Adapter {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Device unique identifiers
|
||||
// MARK: Device metadata
|
||||
|
||||
extension Adapter {
|
||||
func getDeviceName() -> String? {
|
||||
// Returns a generic device name on iOS 16 and higher
|
||||
// See https://github.com/firezone/firezone/issues/3034
|
||||
#if os(iOS)
|
||||
return UIDevice.current.name
|
||||
#else
|
||||
// Fallback to connlib's gethostname()
|
||||
return nil
|
||||
#endif
|
||||
}
|
||||
|
||||
func getOSVersion() -> String? {
|
||||
// Returns the OS version
|
||||
// See https://github.com/firezone/firezone/issues/3034
|
||||
#if os(iOS)
|
||||
return UIDevice.current.systemVersion
|
||||
#else
|
||||
// Fallback to connlib's osinfo
|
||||
return nil
|
||||
#endif
|
||||
}
|
||||
|
||||
// uuidString and copyMACAddress() *should* reliably return valid Strings, but if
|
||||
// for whatever reason they're nil, return a random UUID instead to prevent
|
||||
// upsert collisions in the portal.
|
||||
@@ -305,9 +334,15 @@ extension Adapter {
|
||||
do {
|
||||
self.state = .startingTunnel(
|
||||
session: try WrappedSession.connect(
|
||||
controlPlaneURLString, token, self.getDeviceId(), self.connlibLogFolderPath,
|
||||
controlPlaneURLString,
|
||||
token,
|
||||
self.getDeviceId(),
|
||||
self.getDeviceName(),
|
||||
self.getOSVersion(),
|
||||
self.connlibLogFolderPath,
|
||||
self.logFilter,
|
||||
self.callbackHandler),
|
||||
self.callbackHandler
|
||||
),
|
||||
onStarted: { error in
|
||||
if let error = error {
|
||||
self.logger.error(
|
||||
|
||||
Reference in New Issue
Block a user