summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2022-11-30 10:29:45 +0100
committerDavid Lönnhager <david.l@mullvad.net>2022-11-30 10:29:45 +0100
commitd424bdbedeb1c8e44b693eac02d169e11b7ac92a (patch)
tree40c6dfe3f82333d6884da8cc81308317e49e9a48
parentc3a18445c5265cee590d20894eb9135d2c05b3d6 (diff)
parentefb49034ab60a27d341a1f03dcda5418363202ed (diff)
downloadmullvadvpn-d424bdbedeb1c8e44b693eac02d169e11b7ac92a.tar.xz
mullvadvpn-d424bdbedeb1c8e44b693eac02d169e11b7ac92a.zip
Merge branch 'refactor-auth-failed-error'
-rw-r--r--mullvad-cli/src/format.rs22
-rw-r--r--mullvad-daemon/src/lib.rs3
-rw-r--r--mullvad-management-interface/proto/management_interface.proto9
-rw-r--r--mullvad-management-interface/src/types/conversions/states.rs63
-rw-r--r--mullvad-types/src/auth_failed.rs95
5 files changed, 127 insertions, 65 deletions
diff --git a/mullvad-cli/src/format.rs b/mullvad-cli/src/format.rs
index 693f5c9c7e..b4aa96a719 100644
--- a/mullvad-cli/src/format.rs
+++ b/mullvad-cli/src/format.rs
@@ -1,4 +1,4 @@
-use mullvad_types::{location::GeoIpLocation, states::TunnelState};
+use mullvad_types::{auth_failed::AuthFailed, location::GeoIpLocation, states::TunnelState};
use talpid_types::{
net::{Endpoint, TunnelEndpoint},
tunnel::ErrorState,
@@ -166,6 +166,26 @@ fn print_error_state(error_state: &ErrorState) {
println!("Blocked: {}", cause);
println!("Your kernel might be terribly out of date or missing nftables");
}
+ talpid_types::tunnel::ErrorStateCause::AuthFailed(Some(auth_failed)) => {
+ println!(
+ "Blocked: Authentication with remote server failed: {}",
+ get_auth_failed_message(AuthFailed::from(auth_failed.as_str()))
+ );
+ }
cause => println!("Blocked: {}", cause),
}
}
+
+const fn get_auth_failed_message(auth_failed: AuthFailed) -> &'static str {
+ const INVALID_ACCOUNT_MSG: &str = "You've logged in with an account number that is not valid. Please log out and try another one.";
+ const EXPIRED_ACCOUNT_MSG: &str = "You have no more VPN time left on this account. Please log in on our website to buy more credit.";
+ const TOO_MANY_CONNECTIONS_MSG: &str = "This account has too many simultaneous connections. Disconnect another device or try connecting again shortly.";
+ const UNKNOWN_MSG: &str = "Unknown error.";
+
+ match auth_failed {
+ AuthFailed::InvalidAccount => INVALID_ACCOUNT_MSG,
+ AuthFailed::ExpiredAccount => EXPIRED_ACCOUNT_MSG,
+ AuthFailed::TooManyConnections => TOO_MANY_CONNECTIONS_MSG,
+ AuthFailed::Unknown => UNKNOWN_MSG,
+ }
+}
diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs
index c6875d3401..3ea3cdbd27 100644
--- a/mullvad-daemon/src/lib.rs
+++ b/mullvad-daemon/src/lib.rs
@@ -41,6 +41,7 @@ use mullvad_relay_selector::{
};
use mullvad_types::{
account::{AccountData, AccountToken, VoucherSubmission},
+ auth_failed::AuthFailed,
device::{Device, DeviceEvent, DeviceEventCause, DeviceId, DeviceState, RemoveDeviceEvent},
location::GeoIpLocation,
relay_constraints::{BridgeSettings, BridgeState, ObfuscationSettings, RelaySettingsUpdate},
@@ -1099,7 +1100,7 @@ where
} else if self.get_target_tunnel_type() == Some(TunnelType::Wireguard) {
log::debug!("Entering blocking state since the account is out of time");
self.send_tunnel_command(TunnelCommand::Block(ErrorStateCause::AuthFailed(
- None,
+ Some(AuthFailed::ExpiredAccount.as_str().to_string()),
)))
}
}
diff --git a/mullvad-management-interface/proto/management_interface.proto b/mullvad-management-interface/proto/management_interface.proto
index 5789728fce..cd9141981c 100644
--- a/mullvad-management-interface/proto/management_interface.proto
+++ b/mullvad-management-interface/proto/management_interface.proto
@@ -126,6 +126,13 @@ message ErrorState {
SPLIT_TUNNEL_ERROR = 8;
}
+ enum AuthFailedError {
+ UNKNOWN = 0;
+ INVALID_ACCOUNT = 1;
+ EXPIRED_ACCOUNT = 2;
+ TOO_MANY_CONNECTIONS = 3;
+ }
+
enum GenerationError {
NO_MATCHING_RELAY = 0;
NO_MATCHING_BRIDGE_RELAY = 1;
@@ -149,7 +156,7 @@ message ErrorState {
FirewallPolicyError blocking_error = 2;
// AUTH_FAILED
- string auth_fail_reason = 3;
+ AuthFailedError auth_failed_error = 3;
// TUNNEL_PARAMETER_ERROR
GenerationError parameter_error = 4;
// SET_FIREWALL_POLICY_ERROR
diff --git a/mullvad-management-interface/src/types/conversions/states.rs b/mullvad-management-interface/src/types/conversions/states.rs
index c462e77aa6..c4f5a4de50 100644
--- a/mullvad-management-interface/src/types/conversions/states.rs
+++ b/mullvad-management-interface/src/types/conversions/states.rs
@@ -1,4 +1,6 @@
-use crate::types::{conversions::option_from_proto_string, proto, FromProtobufTypeError};
+#[cfg(windows)]
+use crate::types::conversions::option_from_proto_string;
+use crate::types::{proto, FromProtobufTypeError};
impl From<mullvad_types::states::TunnelState> for proto::TunnelState {
fn from(state: mullvad_types::states::TunnelState) -> Self {
@@ -101,14 +103,14 @@ impl From<mullvad_types::states::TunnelState> for proto::TunnelState {
}
},
blocking_error: error_state.block_failure().map(map_firewall_error),
- auth_fail_reason: if let talpid_tunnel::ErrorStateCause::AuthFailed(
- reason,
- ) = error_state.cause()
- {
- reason.clone().unwrap_or_default()
- } else {
- "".to_string()
- },
+ auth_failed_error: mullvad_types::auth_failed::AuthFailed::try_from(
+ error_state.cause(),
+ )
+ .ok()
+ .map(|auth_failed| {
+ i32::from(proto::error_state::AuthFailedError::from(auth_failed))
+ })
+ .unwrap_or(0i32),
parameter_error:
if let talpid_tunnel::ErrorStateCause::TunnelParameterError(reason) =
error_state.cause()
@@ -147,6 +149,42 @@ impl From<mullvad_types::states::TunnelState> for proto::TunnelState {
}
}
+impl From<mullvad_types::auth_failed::AuthFailed> for proto::error_state::AuthFailedError {
+ fn from(auth_failed: mullvad_types::auth_failed::AuthFailed) -> Self {
+ use mullvad_types::auth_failed::AuthFailed;
+ use proto::error_state;
+ match auth_failed {
+ AuthFailed::InvalidAccount => error_state::AuthFailedError::InvalidAccount,
+ AuthFailed::ExpiredAccount => error_state::AuthFailedError::ExpiredAccount,
+ AuthFailed::TooManyConnections => error_state::AuthFailedError::TooManyConnections,
+ AuthFailed::Unknown => error_state::AuthFailedError::Unknown,
+ }
+ }
+}
+
+fn try_auth_failed_from_i32(
+ auth_failed_error: i32,
+) -> Result<mullvad_types::auth_failed::AuthFailed, FromProtobufTypeError> {
+ proto::error_state::AuthFailedError::from_i32(auth_failed_error)
+ .map(mullvad_types::auth_failed::AuthFailed::from)
+ .ok_or(FromProtobufTypeError::InvalidArgument(
+ "invalid auth failed error",
+ ))
+}
+
+impl From<proto::error_state::AuthFailedError> for mullvad_types::auth_failed::AuthFailed {
+ fn from(auth_failed: proto::error_state::AuthFailedError) -> Self {
+ use mullvad_types::auth_failed::AuthFailed;
+ use proto::error_state;
+ match auth_failed {
+ error_state::AuthFailedError::InvalidAccount => AuthFailed::InvalidAccount,
+ error_state::AuthFailedError::ExpiredAccount => AuthFailed::ExpiredAccount,
+ error_state::AuthFailedError::TooManyConnections => AuthFailed::TooManyConnections,
+ error_state::AuthFailedError::Unknown => AuthFailed::Unknown,
+ }
+ }
+}
+
impl TryFrom<proto::TunnelState> for mullvad_types::states::TunnelState {
type Error = FromProtobufTypeError;
@@ -205,15 +243,16 @@ impl TryFrom<proto::TunnelState> for mullvad_types::states::TunnelState {
Some(proto::ErrorState {
cause,
blocking_error,
- auth_fail_reason,
+ auth_failed_error,
parameter_error,
policy_error,
}),
})) => {
let cause = match proto::error_state::Cause::from_i32(cause) {
Some(proto::error_state::Cause::AuthFailed) => {
- talpid_tunnel::ErrorStateCause::AuthFailed(option_from_proto_string(
- auth_fail_reason,
+ let auth_failed = try_auth_failed_from_i32(auth_failed_error)?;
+ talpid_tunnel::ErrorStateCause::AuthFailed(Some(
+ auth_failed.as_str().to_string(),
))
}
Some(proto::error_state::Cause::Ipv6Unavailable) => {
diff --git a/mullvad-types/src/auth_failed.rs b/mullvad-types/src/auth_failed.rs
index fd8ff2b7b5..795bbc24e3 100644
--- a/mullvad-types/src/auth_failed.rs
+++ b/mullvad-types/src/auth_failed.rs
@@ -1,66 +1,63 @@
use lazy_static::lazy_static;
use regex::Regex;
-use std::fmt;
+use talpid_types::tunnel::ErrorStateCause;
-/// Used by frontends to parse [`talpid_types::tunnel::ErrorStateCause::AuthFailed`], which may be
-/// returned in [`talpid_types::tunnel::ErrorStateCause`] when there is a failure to authenticate
+/// Used to parse [`talpid_types::tunnel::ErrorStateCause::AuthFailed`], which may be returned
+/// in [`talpid_types::tunnel::ErrorStateCause`] when there is a failure to authenticate
/// with a remote server.
#[derive(Debug)]
-pub struct AuthFailed {
- reason: AuthFailedInner,
-}
-
-#[derive(Debug)]
-enum AuthFailedInner {
+pub enum AuthFailed {
InvalidAccount,
ExpiredAccount,
- TooManyConnectons,
- Unknown(String, String),
+ TooManyConnections,
+ Unknown,
}
-// These strings should match up with gui/packages/desktop/src/renderer/lib/auth-failure.js
-const INVALID_ACCOUNT_MSG: &str = "You've logged in with an account number that is not valid. Please log out and try another one.";
-const EXPIRED_ACCOUNT_MSG: &str = "You have no more VPN time left on this account. Please log in on our website to buy more credit.";
-const TOO_MANY_CONNECTIONS_MSG: &str = "This account has too many simultaneous connections. Disconnect another device or try connecting again shortly.";
-
-impl<'a> From<&'a str> for AuthFailedInner {
- fn from(reason: &'a str) -> AuthFailedInner {
- use self::AuthFailedInner::*;
+impl<'a> From<&'a str> for AuthFailed {
+ fn from(reason: &'a str) -> AuthFailed {
+ use AuthFailed::*;
match parse_string(reason) {
- Some(("INVALID_ACCOUNT", _)) => InvalidAccount,
- Some(("EXPIRED_ACCOUNT", _)) => ExpiredAccount,
- Some(("TOO_MANY_CONNECTIONS", _)) => TooManyConnectons,
- Some((unknown_reason, message)) => {
+ Some("INVALID_ACCOUNT") => InvalidAccount,
+ Some("EXPIRED_ACCOUNT") => ExpiredAccount,
+ Some("TOO_MANY_CONNECTIONS") => TooManyConnections,
+ Some(fail_id) => {
log::warn!(
- "Received AUTH_FAILED message with unknown reason: {}",
- reason
+ "Received AUTH_FAILED message with unknown failure ID: {}",
+ fail_id
);
- Unknown(unknown_reason.to_string(), message.to_string())
+ Unknown
}
None => {
log::warn!("Received invalid AUTH_FAILED message: {}", reason);
- Unknown("UNKNOWN".to_string(), reason.to_string())
+ Unknown
}
}
}
}
-impl<'a> From<&'a str> for AuthFailed {
- fn from(reason: &'a str) -> AuthFailed {
- AuthFailed {
- reason: AuthFailedInner::from(reason),
+impl AuthFailed {
+ pub fn as_str(&self) -> &'static str {
+ use AuthFailed::*;
+ match self {
+ InvalidAccount => "[INVALID_ACCOUNT]",
+ ExpiredAccount => "[EXPIRED_ACCOUNT]",
+ TooManyConnections => "[TOO_MANY_CONNECTIONS]",
+ Unknown => "[Unknown]",
}
}
}
-impl fmt::Display for AuthFailed {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- use self::AuthFailedInner::*;
- match self.reason {
- InvalidAccount => write!(f, "{}", INVALID_ACCOUNT_MSG),
- ExpiredAccount => write!(f, "{}", EXPIRED_ACCOUNT_MSG),
- TooManyConnectons => write!(f, "{}", TOO_MANY_CONNECTIONS_MSG),
- Unknown(_, ref reason) => write!(f, "{}", reason),
+#[derive(Debug)]
+pub struct UnexpectedErrorStateCause(());
+
+impl TryFrom<&ErrorStateCause> for AuthFailed {
+ type Error = UnexpectedErrorStateCause;
+
+ fn try_from(cause: &ErrorStateCause) -> Result<Self, Self::Error> {
+ match cause {
+ ErrorStateCause::AuthFailed(Some(reason)) => Ok(AuthFailed::from(reason.as_str())),
+ ErrorStateCause::AuthFailed(None) => Ok(AuthFailed::Unknown),
+ _ => Err(UnexpectedErrorStateCause(())),
}
}
}
@@ -68,16 +65,14 @@ impl fmt::Display for AuthFailed {
// Expects to take a string like "[INVALID_ACCOUNT] This is not a valid Mullvad account".
// The example input string would be split into:
// * "INVALID_ACCOUNT" - the ID of the failure reason.
-// * "This is not a valid Mullvad account" - the human readable message of the failure reason.
+// * "This is not a valid Mullvad account" - human-readable message (ignored).
// In the case that the message has preceeding whitespace, it will be trimmed.
-fn parse_string(reason: &str) -> Option<(&str, &str)> {
+fn parse_string(reason: &str) -> Option<&str> {
lazy_static! {
- static ref REASON_REGEX: Regex = Regex::new(r"^\[(\w+)\]\s*(.*)$").unwrap();
+ static ref REASON_REGEX: Regex = Regex::new(r"^\[(\w+)\]\s*").unwrap();
}
let captures = REASON_REGEX.captures(reason)?;
- let reason = captures.get(1).map(|m| m.as_str())?;
- let message = captures.get(2).map(|m| m.as_str())?;
- Some((reason, message))
+ captures.get(1).map(|m| m.as_str())
}
#[cfg(test)]
@@ -87,15 +82,15 @@ mod tests {
#[test]
fn test_parsing() {
let tests = vec![
- (Some(("INVALID_ACCOUNT", "This is not a valid Mullvad account" )),
+ (Some("INVALID_ACCOUNT"),
"[INVALID_ACCOUNT] This is not a valid Mullvad account"),
- (Some(("EXPIRED_ACCOUNT", "This account has no time left")),
+ (Some("EXPIRED_ACCOUNT"),
"[EXPIRED_ACCOUNT] This account has no time left"),
- (Some(("TOO_MANY_CONNECTIONS", "This Mullvad account is already used by the maximum number of simultaneous connections")),
+ (Some("TOO_MANY_CONNECTIONS"),
"[TOO_MANY_CONNECTIONS] This Mullvad account is already used by the maximum number of simultaneous connections"),
(None, "[Incomplete String"),
- (Some(("REASON_REASON", "")), "[REASON_REASON]"),
- (Some(("REASON_REASON", "A")), "[REASON_REASON]A"),
+ (Some("REASON_REASON"), "[REASON_REASON]"),
+ (Some("REASON_REASON"), "[REASON_REASON]A"),
(None, "incomplete]"),
(None, ""),
];