Skip to content

Commit

Permalink
Remove GetCurrentLocation.
Browse files Browse the repository at this point in the history
Make the daemon send two tunnel state
updates, one with out IP being empty, and another with
it being filled when am.i.mullvad.net responds.

Update CLI for this change. Other front ends are left out.
  • Loading branch information
Serock3 committed Dec 15, 2023
1 parent 750b824 commit 29c316f
Show file tree
Hide file tree
Showing 10 changed files with 137 additions and 153 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,12 @@ Line wrap the file at 100 chars. Th

### Fixed
#### Linux
- "Out IP" missing forever when am.i.mullvad.net returns error
- Prevent fragmentation when multihop is enabled by setting a default route MTU.

### Changed
- Remove `--location` flag from `mullvad status` CLI. Location and IP will now always
be printed (if available). `mullvad status listen` does no longer print location info.
#### Android
- Migrated to Compose Navigation
- Allow for full rotation
Expand Down
63 changes: 20 additions & 43 deletions mullvad-cli/src/cmds/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,33 +18,37 @@ pub struct StatusArgs {
#[arg(long, short = 'v')]
verbose: bool,

/// Print the current location and IP, based on GeoIP lookups
#[arg(long, short = 'l')]
location: bool,

// TODO: changelog about removing location flag
/// Enable debug output
#[arg(long, short = 'd')]
debug: bool,
}

impl Status {
pub async fn listen(mut rpc: MullvadProxyClient, args: StatusArgs) -> Result<()> {
let mut previous_tunnel_state = None;

while let Some(event) = rpc.events_listen().await?.next().await {
match event? {
DaemonEvent::TunnelState(new_state) => {
if args.debug {
println!("New tunnel state: {new_state:#?}");
} else {
format::print_state(&new_state, args.verbose);
}

match new_state {
TunnelState::Connected { .. } | TunnelState::Disconnected => {
if args.location {
print_location(&mut rpc).await?;
}
// When we enter the connected or disconnected state, am.i.mullvad.net will
// be polled to get IP information. When it arrives, we will get another
// tunnel state of the same enum type, but with the IP filled in. This
// match statement checks for duplicate tunnel states and skips the second
// print to avoid spamming the user.
match (&previous_tunnel_state, &new_state) {
(Some(TunnelState::Disconnected(_)), TunnelState::Disconnected(_))
| (
Some(TunnelState::Connected { .. }),
TunnelState::Connected { .. },
) => continue,
_ => {}
}
_ => {}
format::print_state(&new_state, args.verbose);
previous_tunnel_state = Some(new_state);
}
}
DaemonEvent::Settings(settings) => {
Expand Down Expand Up @@ -88,11 +92,9 @@ pub async fn handle(cmd: Option<Status>, args: StatusArgs) -> Result<()> {
if args.debug {
println!("Tunnel state: {state:#?}");
} else {
// TODO: respect location arg?
format::print_state(&state, args.verbose);
}

if args.location {
print_location(&mut rpc).await?;
format::print_location(&state);
}

if cmd == Some(Status::Listen) {
Expand All @@ -101,31 +103,6 @@ pub async fn handle(cmd: Option<Status>, args: StatusArgs) -> Result<()> {
Ok(())
}

async fn print_location(rpc: &mut MullvadProxyClient) -> Result<()> {
let location = match rpc.get_current_location().await {
Ok(location) => location,
Err(error) => match &error {
mullvad_management_interface::Error::NoLocationData => {
println!("Location data unavailable");
return Ok(());
}
_ => return Err(error.into()),
},
};
if let Some(ipv4) = location.ipv4 {
println!("IPv4: {ipv4}");
}
if let Some(ipv6) = location.ipv6 {
println!("IPv6: {ipv6}");
}

println!(
"Position: {:.5}°N, {:.5}°W",
location.latitude, location.longitude
);
Ok(())
}

fn print_account_loggedout(state: &TunnelState, device: &DeviceState) {
match state {
TunnelState::Connecting { .. } | TunnelState::Connected { .. } | TunnelState::Error(_) => {
Expand All @@ -137,6 +114,6 @@ fn print_account_loggedout(state: &TunnelState, device: &DeviceState) {
DeviceState::LoggedIn(_) => (),
}
}
TunnelState::Disconnected | TunnelState::Disconnecting(_) => (),
TunnelState::Disconnected(_) | TunnelState::Disconnecting(_) => (),
}
}
25 changes: 24 additions & 1 deletion mullvad-cli/src/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,34 @@ pub fn print_state(state: &TunnelState, verbose: bool) {
format_relay_connection(endpoint, location.as_ref(), verbose)
);
}
Disconnected => println!("Disconnected"),
Disconnected(_) => {
println!("Disconnected");
}
Disconnecting(_) => println!("Disconnecting..."),
}
}

pub fn print_location(state: &TunnelState) {
let location = match state {
TunnelState::Disconnected(location) => location,
TunnelState::Connected { location, .. } => location,
_ => return,
};
if let Some(location) = location {
print!("Your connection appears from: {}", location.country);
if let Some(city) = &location.city {
print!(", {}", city);
}
if let Some(ipv4) = location.ipv4 {
print!(". IPv4: {ipv4}");
}
if let Some(ipv6) = location.ipv6 {
print!(", IPv6: {ipv6}");
}
println!();
}
}

fn format_relay_connection(
endpoint: &TunnelEndpoint,
location: Option<&GeoIpLocation>,
Expand Down
13 changes: 3 additions & 10 deletions mullvad-daemon/src/geoip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,13 @@ const LOCATION_RETRY_STRATEGY: Jittered<ExponentialBackoff> =
Jittered::jitter(ExponentialBackoff::new(Duration::from_secs(1), 4));

/// Fetch the current `GeoIpLocation` with retrys
pub async fn get_geo_location(
pub async fn get_geo_location_with_retry(
rest_service: RequestServiceHandle,
use_ipv6: bool,
api_handle: ApiAvailabilityHandle,
) -> Option<GeoIpLocation> {
) -> Result<GeoIpLocation, Error> {
log::debug!("Fetching GeoIpLocation");
match retry_future(
retry_future(
move || send_location_request(rest_service.clone(), use_ipv6),
move |result| match result {
Err(error) if error.is_network_error() => !api_handle.get_state().is_offline(),
Expand All @@ -59,13 +59,6 @@ pub async fn get_geo_location(
LOCATION_RETRY_STRATEGY,
)
.await
{
Ok(loc) => Some(loc),
Err(e) => {
log::warn!("Unable to fetch GeoIP location: {}", e.display_chain());
None
}
}
}

async fn send_location_request(
Expand Down
Loading

0 comments on commit 29c316f

Please sign in to comment.