diff options
| author | Emīls <emils@mullvad.net> | 2019-12-09 11:24:37 +0000 |
|---|---|---|
| committer | Emīls <emils@mullvad.net> | 2019-12-09 11:24:37 +0000 |
| commit | be89341733b58f2e992141fe99713e4d6e4ba7fd (patch) | |
| tree | 5c5c7413fab96d3c874cc882b7f80e253892cef0 | |
| parent | 345bebcf9cd75dc29328b1355c2feacbf7b65aad (diff) | |
| parent | 3a041298b1433ec8b81656d77bc2379978af2691 (diff) | |
| download | mullvadvpn-be89341733b58f2e992141fe99713e4d6e4ba7fd.tar.xz mullvadvpn-be89341733b58f2e992141fe99713e4d6e4ba7fd.zip | |
Merge branch 'add-error-state'
35 files changed, 323 insertions, 241 deletions
diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ConnectActionButton.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ConnectActionButton.kt index 4789d27a2c..cdaa302a71 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ConnectActionButton.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ConnectActionButton.kt @@ -26,7 +26,7 @@ class ConnectActionButton(val parentView: View) { } is TunnelState.Connecting -> connecting() is TunnelState.Connected -> connected() - is TunnelState.Blocked -> connected() + is TunnelState.Error -> connected() } field = value @@ -46,7 +46,7 @@ class ConnectActionButton(val parentView: View) { is TunnelState.Disconnecting -> onConnect?.invoke() is TunnelState.Connecting -> onCancel?.invoke() is TunnelState.Connected -> onDisconnect?.invoke() - is TunnelState.Blocked -> onDisconnect?.invoke() + is TunnelState.Error -> onDisconnect?.invoke() } } diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ConnectionStatus.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ConnectionStatus.kt index 664156dd04..8fd95fab1f 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ConnectionStatus.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ConnectionStatus.kt @@ -26,7 +26,7 @@ class ConnectionStatus(val parentView: View, val resources: Resources) { is TunnelState.Disconnected -> disconnected() is TunnelState.Connecting -> connecting() is TunnelState.Connected -> connected() - is TunnelState.Blocked -> blocked() + is TunnelState.Error -> errorState(state.errorState.isBlocking) } } @@ -51,10 +51,16 @@ class ConnectionStatus(val parentView: View, val resources: Resources) { text.setText(R.string.secure_connection) } - private fun blocked() { + private fun errorState(isBlocking: Boolean) { spinner.visibility = View.GONE + // TODO: revise how to best inform the user about us not blocking + // traffic text.setTextColor(securedTextColor) - text.setText(R.string.blocked_connection) + if (isBlocking) { + text.setText(R.string.blocked_connection) + } else { + text.setText(R.string.blocked_connection) + } } } diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/ForegroundNotificationManager.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/ForegroundNotificationManager.kt index 80890e56ca..e7c3886873 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/ForegroundNotificationManager.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/ForegroundNotificationManager.kt @@ -66,7 +66,14 @@ class ForegroundNotificationManager(val service: Service, val connectionProxy: C else -> R.string.disconnecting } } - is TunnelState.Blocked -> R.string.blocking_all_connections + is TunnelState.Error -> { + if (state.errorState.isBlocking) { + R.string.blocking_all_connections + } else { + // TODO Revise use of message when the app fails to block traffic + R.string.unsecured + } + } } } @@ -84,7 +91,7 @@ class ForegroundNotificationManager(val service: Service, val connectionProxy: C else -> R.string.connect } } - is TunnelState.Blocked -> R.string.disconnect + is TunnelState.Error -> R.string.disconnect } } @@ -102,7 +109,7 @@ class ForegroundNotificationManager(val service: Service, val connectionProxy: C else -> KEY_CONNECT_ACTION } } - is TunnelState.Blocked -> KEY_DISCONNECT_ACTION + is TunnelState.Error -> KEY_DISCONNECT_ACTION } } diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/HeaderBar.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/HeaderBar.kt index b89b13e08a..38022e1ef2 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/HeaderBar.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/HeaderBar.kt @@ -16,7 +16,13 @@ class HeaderBar(val parentView: View, val resources: Resources) { is TunnelState.Connecting -> secured() is TunnelState.Connected -> secured() is TunnelState.Disconnecting -> secured() - is TunnelState.Blocked -> secured() + is TunnelState.Error -> { + if (state.errorState.isBlocking) { + secured() + } else { + unsecured() + } + } } } diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/NotificationBanner.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/NotificationBanner.kt index c5cd816ce2..77133f929d 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/NotificationBanner.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/NotificationBanner.kt @@ -16,7 +16,8 @@ import net.mullvad.mullvadvpn.dataproxy.WwwAuthTokenRetriever import net.mullvad.mullvadvpn.model.KeygenEvent import net.mullvad.mullvadvpn.model.TunnelState import net.mullvad.talpid.tunnel.ActionAfterDisconnect -import net.mullvad.talpid.tunnel.BlockReason +import net.mullvad.talpid.tunnel.ErrorState +import net.mullvad.talpid.tunnel.ErrorStateCause import net.mullvad.talpid.tunnel.ParameterGenerationError class NotificationBanner( @@ -132,7 +133,7 @@ class NotificationBanner( is TunnelState.Disconnected -> return false is TunnelState.Connecting -> showBlocking(null) is TunnelState.Connected -> return false - is TunnelState.Blocked -> showBlocking(state.reason) + is TunnelState.Error -> showBlocking(state.errorState) } return true @@ -167,18 +168,20 @@ class NotificationBanner( return true } - private fun showBlocking(reason: BlockReason?) { - val messageText = when (reason) { + private fun showBlocking(errorState: ErrorState?) { + val cause = errorState?.cause + + val messageText = when (cause) { null -> null - is BlockReason.AuthFailed -> R.string.auth_failed - is BlockReason.Ipv6Unavailable -> R.string.ipv6_unavailable - is BlockReason.SetFirewallPolicyError -> R.string.set_firewall_policy_error - is BlockReason.SetDnsError -> R.string.set_dns_error - is BlockReason.StartTunnelError -> R.string.start_tunnel_error - is BlockReason.IsOffline -> R.string.is_offline - is BlockReason.TapAdapterProblem -> R.string.tap_adapter_problem - is BlockReason.TunnelParameterError -> { - when (reason.error) { + is ErrorStateCause.AuthFailed -> R.string.auth_failed + is ErrorStateCause.Ipv6Unavailable -> R.string.ipv6_unavailable + is ErrorStateCause.SetFirewallPolicyError -> R.string.set_firewall_policy_error + is ErrorStateCause.SetDnsError -> R.string.set_dns_error + is ErrorStateCause.StartTunnelError -> R.string.start_tunnel_error + is ErrorStateCause.IsOffline -> R.string.is_offline + is ErrorStateCause.TapAdapterProblem -> R.string.tap_adapter_problem + is ErrorStateCause.TunnelParameterError -> { + when (cause.error) { ParameterGenerationError.NoMatchingRelay -> R.string.no_matching_relay ParameterGenerationError.NoMatchingBridgeRelay -> { R.string.no_matching_bridge_relay @@ -190,7 +193,15 @@ class NotificationBanner( } } } - showError(R.string.blocking_internet, messageText) + + // if the error state is null, we can assume that we are secure + val blockMessage = if (errorState?.isBlocking ?: true) { + R.string.blocking_internet + } else { + R.string.not_blocking_internet + } + + showError(blockMessage, messageText) } private fun showError(titleText: Int, messageText: Int?) { diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/SwitchLocationButton.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/SwitchLocationButton.kt index 930c1ff626..581fae4772 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/SwitchLocationButton.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/SwitchLocationButton.kt @@ -64,7 +64,7 @@ class SwitchLocationButton(val parentView: View, val resources: Resources) { } is TunnelState.Connecting -> showLabel() is TunnelState.Connected -> showLabel() - is TunnelState.Blocked -> showLocation() + is TunnelState.Error -> showLocation() } } } diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/WireguardKeyFragment.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/WireguardKeyFragment.kt index 11f27d2b53..c0237fa7d6 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/WireguardKeyFragment.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/WireguardKeyFragment.kt @@ -91,7 +91,7 @@ class WireguardKeyFragment : Fragment() { urlController = BlockingController( object : BlockableView { override fun setEnabled(enabled: Boolean) { - if (!enabled || tunnelState is TunnelState.Blocked) { + if (!enabled || tunnelState is TunnelState.Error) { visitWebsiteView.setClickable(false) visitWebsiteView.setAlpha(0.5f) } else { @@ -251,7 +251,7 @@ class WireguardKeyFragment : Fragment() { verifyButton.visibility = View.GONE verifySpinner.visibility = View.VISIBLE } - is TunnelState.Blocked -> { + is TunnelState.Error -> { statusMessage.setText(R.string.wireguard_key_blocked_state_message) statusMessage.visibility = View.VISIBLE generateButton.setClickable(false) diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/LocationInfoCache.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/LocationInfoCache.kt index 9c9e339541..41fb1ccbf3 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/LocationInfoCache.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/dataproxy/LocationInfoCache.kt @@ -64,7 +64,7 @@ class LocationInfoCache( ActionAfterDisconnect.Reconnect -> location = locationFromSelectedRelay() } } - is TunnelState.Blocked -> location = null + is TunnelState.Error -> location = null } } diff --git a/android/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelState.kt b/android/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelState.kt index 040c4997c9..556517720d 100644 --- a/android/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelState.kt +++ b/android/src/main/kotlin/net/mullvad/mullvadvpn/model/TunnelState.kt @@ -2,12 +2,12 @@ package net.mullvad.mullvadvpn.model import net.mullvad.talpid.net.TunnelEndpoint import net.mullvad.talpid.tunnel.ActionAfterDisconnect -import net.mullvad.talpid.tunnel.BlockReason +import net.mullvad.talpid.tunnel.ErrorState sealed class TunnelState() { class Disconnected() : TunnelState() class Connecting(val endpoint: TunnelEndpoint?, val location: GeoIpLocation?) : TunnelState() class Connected(val endpoint: TunnelEndpoint, val location: GeoIpLocation?) : TunnelState() class Disconnecting(val actionAfterDisconnect: ActionAfterDisconnect) : TunnelState() - class Blocked(val reason: BlockReason) : TunnelState() + class Error(val errorState: ErrorState) : TunnelState() } diff --git a/android/src/main/kotlin/net/mullvad/talpid/tunnel/BlockReason.kt b/android/src/main/kotlin/net/mullvad/talpid/tunnel/BlockReason.kt deleted file mode 100644 index bed6177fb0..0000000000 --- a/android/src/main/kotlin/net/mullvad/talpid/tunnel/BlockReason.kt +++ /dev/null @@ -1,12 +0,0 @@ -package net.mullvad.talpid.tunnel - -sealed class BlockReason { - class AuthFailed(val reason: String?) : BlockReason() - class Ipv6Unavailable : BlockReason() - class SetFirewallPolicyError : BlockReason() - class SetDnsError : BlockReason() - class StartTunnelError : BlockReason() - class TunnelParameterError(val error: ParameterGenerationError) : BlockReason() - class IsOffline : BlockReason() - class TapAdapterProblem : BlockReason() -} diff --git a/android/src/main/kotlin/net/mullvad/talpid/tunnel/ErrorState.kt b/android/src/main/kotlin/net/mullvad/talpid/tunnel/ErrorState.kt new file mode 100644 index 0000000000..c88a932887 --- /dev/null +++ b/android/src/main/kotlin/net/mullvad/talpid/tunnel/ErrorState.kt @@ -0,0 +1,3 @@ +package net.mullvad.talpid.tunnel + +data class ErrorState(val cause: ErrorStateCause, val isBlocking: Boolean) diff --git a/android/src/main/kotlin/net/mullvad/talpid/tunnel/ErrorStateCause.kt b/android/src/main/kotlin/net/mullvad/talpid/tunnel/ErrorStateCause.kt new file mode 100644 index 0000000000..e289b59551 --- /dev/null +++ b/android/src/main/kotlin/net/mullvad/talpid/tunnel/ErrorStateCause.kt @@ -0,0 +1,12 @@ +package net.mullvad.talpid.tunnel + +sealed class ErrorStateCause { + class AuthFailed(val reason: String?) : ErrorStateCause() + class Ipv6Unavailable : ErrorStateCause() + class SetFirewallPolicyError : ErrorStateCause() + class SetDnsError : ErrorStateCause() + class StartTunnelError : ErrorStateCause() + class TunnelParameterError(val error: ParameterGenerationError) : ErrorStateCause() + class IsOffline : ErrorStateCause() + class TapAdapterProblem : ErrorStateCause() +} diff --git a/android/src/main/res/values/strings.xml b/android/src/main/res/values/strings.xml index 02064541a1..d019f68b17 100644 --- a/android/src/main/res/values/strings.xml +++ b/android/src/main/res/values/strings.xml @@ -70,6 +70,7 @@ <string name="creating_secure_connection">Creating secure connection</string> <string name="secure_connection">Secure connection</string> <string name="blocked_connection">Blocked connection</string> + <string name="error_state">Failed to secure connection</string> <string name="connect">Secure my connection</string> <string name="cancel">Cancel</string> <string name="disconnect">Disconnect</string> @@ -81,6 +82,7 @@ <string name="out_address">Out %1$s</string> <string name="blocking_internet">Blocking internet</string> + <string name="not_blocking_internet">Failed to block internet</string> <string name="auth_failed">Account authentication failed.</string> <string name="ipv6_unavailable">Could not configure IPv6</string> <string name="set_firewall_policy_error"> diff --git a/gui/src/main/daemon-rpc.ts b/gui/src/main/daemon-rpc.ts index 5fcc9171e0..78748b70ab 100644 --- a/gui/src/main/daemon-rpc.ts +++ b/gui/src/main/daemon-rpc.ts @@ -251,32 +251,35 @@ const tunnelStateSchema = oneOf( }), }), object({ - state: enumeration('blocked'), - details: oneOf( - object({ - reason: enumeration( - 'ipv6_unavailable', - 'set_firewall_policy_error', - 'set_dns_error', - 'start_tunnel_error', - 'is_offline', - 'tap_adapter_problem', - ), - }), - object({ - reason: enumeration('auth_failed'), - details: maybe(string), - }), - object({ - reason: enumeration('tunnel_parameter_error'), - details: enumeration( - 'no_matching_relay', - 'no_matching_bridge_relay', - 'no_wireguard_key', - 'custom_tunnel_host_resultion_error', - ), - }), - ), + state: enumeration('error'), + details: object({ + is_blocking: boolean, + cause: oneOf( + object({ + reason: enumeration( + 'ipv6_unavailable', + 'set_firewall_policy_error', + 'set_dns_error', + 'start_tunnel_error', + 'is_offline', + 'tap_adapter_problem', + ), + }), + object({ + reason: enumeration('auth_failed'), + details: maybe(string), + }), + object({ + reason: enumeration('tunnel_parameter_error'), + details: enumeration( + 'no_matching_relay', + 'no_matching_bridge_relay', + 'no_wireguard_key', + 'custom_tunnel_host_resultion_error', + ), + }), + ), + }), }), object({ state: enumeration('connected', 'connecting', 'disconnected'), diff --git a/gui/src/main/index.ts b/gui/src/main/index.ts index cba1caac5a..0ef641ec15 100644 --- a/gui/src/main/index.ts +++ b/gui/src/main/index.ts @@ -851,14 +851,12 @@ class ApplicationMain { case 'connecting': return 'securing'; - case 'blocked': - switch (tunnelState.details.reason) { - case 'set_firewall_policy_error': - return 'unsecured'; - default: - return 'securing'; + case 'error': + if (tunnelState.details.isBlocking) { + return 'securing'; + } else { + return 'unsecured'; } - case 'disconnecting': return 'securing'; diff --git a/gui/src/main/notification-controller.ts b/gui/src/main/notification-controller.ts index 515918f50c..99af3bcf45 100644 --- a/gui/src/main/notification-controller.ts +++ b/gui/src/main/notification-controller.ts @@ -75,18 +75,15 @@ export default class NotificationController { case 'disconnected': this.showTunnelStateNotification(messages.pgettext('notifications', 'Unsecured')); break; - case 'blocked': - switch (tunnelState.details.reason) { - case 'set_firewall_policy_error': - this.showTunnelStateNotification( - messages.pgettext('notifications', 'Critical failure - Unsecured'), - ); - break; - default: - this.showTunnelStateNotification( - messages.pgettext('notifications', 'Blocked all connections'), - ); - break; + case 'error': + if (tunnelState.details.isBlocking) { + this.showTunnelStateNotification( + messages.pgettext('notifications', 'Blocked all connections'), + ); + } else { + this.showTunnelStateNotification( + messages.pgettext('notifications', 'Critical failure - Unsecured'), + ); } break; case 'disconnecting': diff --git a/gui/src/renderer/app.tsx b/gui/src/renderer/app.tsx index ae906868a2..b3a1b257bd 100644 --- a/gui/src/renderer/app.tsx +++ b/gui/src/renderer/app.tsx @@ -264,7 +264,7 @@ export default class AppRenderer { const state = this.tunnelState.state; // connect only if tunnel is disconnected or blocked. - if (state === 'disconnecting' || state === 'disconnected' || state === 'blocked') { + if (state === 'disconnecting' || state === 'disconnected' || state === 'error') { // switch to the connecting state ahead of time to make the app look more responsive this.reduxActions.connection.connecting(); @@ -549,7 +549,7 @@ export default class AppRenderer { actions.connection.disconnected(); break; - case 'blocked': + case 'error': actions.connection.blocked(tunnelState.details); break; } @@ -597,8 +597,8 @@ export default class AppRenderer { actions.updateBlockState(true); break; - case 'blocked': - actions.updateBlockState(tunnelState.details.reason !== 'set_firewall_policy_error'); + case 'error': + actions.updateBlockState(tunnelState.details.isBlocking); break; } } diff --git a/gui/src/renderer/components/Connect.tsx b/gui/src/renderer/components/Connect.tsx index e8d5c0e096..7f73fb7ea0 100644 --- a/gui/src/renderer/components/Connect.tsx +++ b/gui/src/renderer/components/Connect.tsx @@ -117,9 +117,9 @@ export default class Connect extends Component<IProps, IState> { // Blocked with auth failure / expired account if ( - tunnelState.state === 'blocked' && - tunnelState.details.reason === 'auth_failed' && - parseAuthFailure(tunnelState.details.details).kind === AuthFailureKind.expiredAccount + tunnelState.state === 'error' && + tunnelState.details.cause.reason === 'auth_failed' && + parseAuthFailure(tunnelState.details.cause.reason).kind === AuthFailureKind.expiredAccount ) { return true; } @@ -200,8 +200,8 @@ export default class Connect extends Component<IProps, IState> { case 'connecting': case 'connected': return HeaderBarStyle.success; - case 'blocked': - switch (status.details.reason) { + case 'error': + switch (status.details.cause.reason) { case 'set_firewall_policy_error': return HeaderBarStyle.error; default: @@ -259,8 +259,8 @@ export default class Connect extends Component<IProps, IState> { case 'connecting': case 'connected': return MarkerStyle.secure; - case 'blocked': - switch (status.details.reason) { + case 'error': + switch (status.details.cause.reason) { case 'set_firewall_policy_error': return MarkerStyle.unsecure; default: diff --git a/gui/src/renderer/components/NotificationArea.tsx b/gui/src/renderer/components/NotificationArea.tsx index 1d65306ef9..c459695a30 100644 --- a/gui/src/renderer/components/NotificationArea.tsx +++ b/gui/src/renderer/components/NotificationArea.tsx @@ -13,7 +13,7 @@ import { NotificationTitle, } from './NotificationBanner'; -import { BlockReason, TunnelParameterError, TunnelState } from '../../shared/daemon-rpc-types'; +import { ErrorStateCause, TunnelParameterError, TunnelState } from '../../shared/daemon-rpc-types'; import AccountExpiry from '../lib/account-expiry'; import { parseAuthFailure } from '../lib/auth-failure'; import { IVersionReduxState } from '../redux/version/reducers'; @@ -63,7 +63,7 @@ function getTunnelParameterMessage(err: TunnelParameterError): string { } } -function getBlockReasonMessage(blockReason: BlockReason): string { +function getErrorCauseMessage(blockReason: ErrorStateCause): string { switch (blockReason.reason) { case 'auth_failed': return parseAuthFailure(blockReason.details).message; @@ -124,20 +124,19 @@ export default class NotificationArea extends Component<IProps, State> { reason: '', }; - case 'blocked': - switch (tunnelState.details.reason) { - case 'set_firewall_policy_error': - return { - visible: true, - type: 'failure-unsecured', - reason: getBlockReasonMessage(tunnelState.details), - }; - default: - return { - visible: true, - type: 'blocking', - reason: getBlockReasonMessage(tunnelState.details), - }; + case 'error': + if (tunnelState.details.isBlocking) { + return { + visible: true, + type: 'blocking', + reason: getErrorCauseMessage(tunnelState.details.cause), + }; + } else { + return { + visible: true, + type: 'failure-unsecured', + reason: getErrorCauseMessage(tunnelState.details.cause), + }; } case 'disconnecting': diff --git a/gui/src/renderer/components/TunnelControl.tsx b/gui/src/renderer/components/TunnelControl.tsx index 4acc2bd1ef..700603bd2e 100644 --- a/gui/src/renderer/components/TunnelControl.tsx +++ b/gui/src/renderer/components/TunnelControl.tsx @@ -115,21 +115,14 @@ export default class TunnelControl extends Component<ITunnelControlProps> { let state = this.props.tunnelState.state; switch (this.props.tunnelState.state) { - case 'blocked': - switch (this.props.tunnelState.details.reason) { - case 'set_firewall_policy_error': - state = 'disconnected'; - break; - default: - state = 'blocked'; - break; - } + case 'error': + state = this.props.tunnelState.details.isBlocking ? 'error' : 'disconnected'; break; case 'disconnecting': switch (this.props.tunnelState.details) { case 'block': - state = 'blocked'; + state = 'error'; break; case 'reconnect': state = 'connecting'; @@ -177,7 +170,7 @@ export default class TunnelControl extends Component<ITunnelControlProps> { </Wrapper> ); - case 'blocked': + case 'error': return ( <Wrapper> <Body> diff --git a/gui/src/renderer/redux/connection/actions.ts b/gui/src/renderer/redux/connection/actions.ts index 8f5b2e62bb..95d2332ed3 100644 --- a/gui/src/renderer/redux/connection/actions.ts +++ b/gui/src/renderer/redux/connection/actions.ts @@ -1,6 +1,6 @@ import { AfterDisconnect, - BlockReason, + IErrorState, ILocation, ITunnelStateRelayInfo, } from '../../../shared/daemon-rpc-types'; @@ -25,8 +25,8 @@ interface IDisconnectingAction { } interface IBlockedAction { - type: 'BLOCKED'; - reason: BlockReason; + type: 'TUNNEL_ERROR'; + errorState: IErrorState; } interface INewLocationAction { @@ -75,10 +75,10 @@ function disconnecting(afterDisconnect: AfterDisconnect): IDisconnectingAction { }; } -function blocked(reason: BlockReason): IBlockedAction { +function blocked(errorState: IErrorState): IBlockedAction { return { - type: 'BLOCKED', - reason, + type: 'TUNNEL_ERROR', + errorState, }; } diff --git a/gui/src/renderer/redux/connection/reducers.ts b/gui/src/renderer/redux/connection/reducers.ts index 4772df8fc5..e7f2f0c2cf 100644 --- a/gui/src/renderer/redux/connection/reducers.ts +++ b/gui/src/renderer/redux/connection/reducers.ts @@ -62,10 +62,13 @@ export default function( status: { state: 'disconnecting', details: action.afterDisconnect }, }; - case 'BLOCKED': + case 'TUNNEL_ERROR': return { ...state, - status: { state: 'blocked', details: action.reason }, + status: { + state: 'error', + details: action.errorState, + }, }; default: diff --git a/gui/src/shared/daemon-rpc-types.ts b/gui/src/shared/daemon-rpc-types.ts index 7ce5e93111..404762d962 100644 --- a/gui/src/shared/daemon-rpc-types.ts +++ b/gui/src/shared/daemon-rpc-types.ts @@ -21,7 +21,7 @@ export type TunnelParameterError = | 'no_wireguard_key' | 'custom_tunnel_host_resultion_error'; -export type BlockReason = +export type ErrorStateCause = | { reason: | 'ipv6_unavailable' @@ -99,7 +99,12 @@ export type TunnelState = | { state: 'connecting'; details?: ITunnelStateRelayInfo } | { state: 'connected'; details: ITunnelStateRelayInfo } | { state: 'disconnecting'; details: AfterDisconnect } - | { state: 'blocked'; details: BlockReason }; + | { state: 'error'; details: IErrorState }; + +export interface IErrorState { + isBlocking: boolean; + cause: ErrorStateCause; +} export type RelayLocation = | { hostname: [string, string, string] } diff --git a/gui/test/components/NotificationArea.spec.tsx b/gui/test/components/NotificationArea.spec.tsx index 713c30faff..6aa7621724 100644 --- a/gui/test/components/NotificationArea.spec.tsx +++ b/gui/test/components/NotificationArea.spec.tsx @@ -145,10 +145,13 @@ describe('components/NotificationArea', () => { const component = shallow( <NotificationArea tunnelState={{ - state: 'blocked', + state: 'error', details: { - reason: 'tunnel_parameter_error', - details: 'no_matching_relay', + isBlocking: true, + cause: { + reason: 'tunnel_parameter_error', + details: 'no_matching_relay', + }, }, }} version={defaultVersion} diff --git a/mullvad-cli/src/cmds/status.rs b/mullvad-cli/src/cmds/status.rs index bd68ae64ae..10834b60b3 100644 --- a/mullvad-cli/src/cmds/status.rs +++ b/mullvad-cli/src/cmds/status.rs @@ -2,7 +2,7 @@ use crate::{new_rpc_client, Command, Error, Result}; use futures::{Future, Stream}; use mullvad_ipc_client::DaemonRpcClient; use mullvad_types::{auth_failed::AuthFailed, states::TunnelState, DaemonEvent}; -use talpid_types::tunnel::BlockReason; +use talpid_types::tunnel::{ErrorState, ErrorStateCause}; pub struct Status; @@ -91,7 +91,7 @@ fn print_state(state: &TunnelState) { use self::TunnelState::*; print!("Tunnel status: "); match state { - Blocked(reason) => print_blocked_reason(reason), + Error(reason) => print_error_state(reason), Connected { endpoint, .. } => { println!("Connected to {}", endpoint); } @@ -101,9 +101,18 @@ fn print_state(state: &TunnelState) { } } -fn print_blocked_reason(reason: &BlockReason) { +fn print_error_state(error_state: &ErrorState) { + if !error_state.is_blocking() { + eprintln!("Mullvad daemon failed to setup firewall rules!"); + eprintln!("Deamon cannot block traffic from flowing, non-local traffic will leak"); + } + + print_blocked_reason(error_state.cause()); +} + +fn print_blocked_reason(reason: &ErrorStateCause) { match reason { - BlockReason::AuthFailed(ref auth_failure) => { + ErrorStateCause::AuthFailed(ref auth_failure) => { let auth_failure_str = auth_failure .as_ref() .map(|s| s.as_str()) @@ -111,8 +120,8 @@ fn print_blocked_reason(reason: &BlockReason) { println!("Blocked: {}", AuthFailed::from(auth_failure_str)); } #[cfg(target_os = "linux")] - BlockReason::SetFirewallPolicyError => { - println!("Blocked: {}", BlockReason::SetFirewallPolicyError); + ErrorStateCause::SetFirewallPolicyError => { + println!("Blocked: {}", ErrorStateCause::SetFirewallPolicyError); println!("Your kernel might be terribly out of date or missing nftables"); } other => println!("Blocked: {}", other), diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs index e78649ac63..aabcbb6952 100644 --- a/mullvad-daemon/src/lib.rs +++ b/mullvad-daemon/src/lib.rs @@ -64,7 +64,7 @@ use talpid_core::{ use talpid_types::android::AndroidContext; use talpid_types::{ net::{openvpn, TransportProtocol, TunnelParameters}, - tunnel::{BlockReason, ParameterGenerationError, TunnelStateTransition}, + tunnel::{ErrorStateCause, ParameterGenerationError, TunnelStateTransition}, ErrorExt, }; @@ -565,7 +565,7 @@ where TunnelStateTransition::Disconnecting(after_disconnect) => { TunnelState::Disconnecting(after_disconnect) } - TunnelStateTransition::Blocked(reason) => TunnelState::Blocked(reason.clone()), + TunnelStateTransition::Error(error_state) => TunnelState::Error(error_state.clone()), }; self.unschedule_reconnect(); @@ -573,10 +573,20 @@ where debug!("New tunnel state: {:?}", tunnel_state); match tunnel_state { TunnelState::Disconnected => self.state.disconnected(), - TunnelState::Blocked(ref reason) => { - info!("Blocking all network connections, reason: {}", reason); + TunnelState::Error(ref error_state) => { + if error_state.is_blocking() { + info!( + "Blocking all network connections, reason: {}", + error_state.cause() + ); + } else { + error!( + "FAILED TO BLOCK NETWORK CONNECTIONS, ENTERED ERROR STATE BECAUSE: {}", + error_state.cause() + ); + } - if let BlockReason::AuthFailed(_) = reason { + if let ErrorStateCause::AuthFailed(_) = error_state.cause() { self.schedule_reconnect(Duration::from_secs(60)) } } @@ -925,7 +935,7 @@ where } fn on_reconnect(&mut self) { - if self.target_state == TargetState::Secured || self.tunnel_state.is_blocked() { + if self.target_state == TargetState::Secured || self.tunnel_state.is_in_error_state() { self.connect_tunnel(); } else { debug!("Ignoring reconnect command. Currently not in secured state"); @@ -955,7 +965,7 @@ where .map(Some), ) } - Blocked(..) => { + Error(..) => { // We are not online at all at this stage so no location data is available. Box::new(future::result(Ok(None))) } @@ -1485,7 +1495,7 @@ where /// progress towards that state. /// Returns an error if trying to set secured state, but no account token is present. fn set_target_state(&mut self, new_state: TargetState) { - if new_state != self.target_state || self.tunnel_state.is_blocked() { + if new_state != self.target_state || self.tunnel_state.is_in_error_state() { debug!("Target state {:?} => {:?}", self.target_state, new_state); self.target_state = new_state; match self.target_state { diff --git a/mullvad-jni/src/classes.rs b/mullvad-jni/src/classes.rs index d2e37b0804..6669f2b403 100644 --- a/mullvad-jni/src/classes.rs +++ b/mullvad-jni/src/classes.rs @@ -30,7 +30,7 @@ pub const CLASSES: &[&str] = &[ "net/mullvad/mullvadvpn/model/RelaySettingsUpdate$RelayConstraintsUpdate", "net/mullvad/mullvadvpn/model/RelayTunnels", "net/mullvad/mullvadvpn/model/Settings", - "net/mullvad/mullvadvpn/model/TunnelState$Blocked", + "net/mullvad/mullvadvpn/model/TunnelState$Error", "net/mullvad/mullvadvpn/model/TunnelState$Connected", "net/mullvad/mullvadvpn/model/TunnelState$Connecting", "net/mullvad/mullvadvpn/model/TunnelState$Disconnected", @@ -44,14 +44,15 @@ pub const CLASSES: &[&str] = &[ "net/mullvad/talpid/tun_provider/InetNetwork", "net/mullvad/talpid/tun_provider/TunConfig", "net/mullvad/talpid/tunnel/ActionAfterDisconnect", - "net/mullvad/talpid/tunnel/BlockReason$AuthFailed", - "net/mullvad/talpid/tunnel/BlockReason$Ipv6Unavailable", - "net/mullvad/talpid/tunnel/BlockReason$SetFirewallPolicyError", - "net/mullvad/talpid/tunnel/BlockReason$SetDnsError", - "net/mullvad/talpid/tunnel/BlockReason$StartTunnelError", - "net/mullvad/talpid/tunnel/BlockReason$TunnelParameterError", - "net/mullvad/talpid/tunnel/BlockReason$IsOffline", - "net/mullvad/talpid/tunnel/BlockReason$TapAdapterProblem", + "net/mullvad/talpid/tunnel/ErrorState", + "net/mullvad/talpid/tunnel/ErrorStateCause$AuthFailed", + "net/mullvad/talpid/tunnel/ErrorStateCause$Ipv6Unavailable", + "net/mullvad/talpid/tunnel/ErrorStateCause$SetFirewallPolicyError", + "net/mullvad/talpid/tunnel/ErrorStateCause$SetDnsError", + "net/mullvad/talpid/tunnel/ErrorStateCause$StartTunnelError", + "net/mullvad/talpid/tunnel/ErrorStateCause$TunnelParameterError", + "net/mullvad/talpid/tunnel/ErrorStateCause$IsOffline", + "net/mullvad/talpid/tunnel/ErrorStateCause$TapAdapterProblem", "net/mullvad/talpid/tunnel/ParameterGenerationError", "net/mullvad/talpid/ConnectivityListener", "net/mullvad/talpid/TalpidVpnService", diff --git a/mullvad-types/src/states.rs b/mullvad-types/src/states.rs index e905f329c5..805210a7aa 100644 --- a/mullvad-types/src/states.rs +++ b/mullvad-types/src/states.rs @@ -4,7 +4,7 @@ use jnix::IntoJava; use serde::{Deserialize, Serialize}; use talpid_types::{ net::TunnelEndpoint, - tunnel::{ActionAfterDisconnect, BlockReason}, + tunnel::{ActionAfterDisconnect, ErrorState}, }; /// Represents the state the client strives towards. @@ -34,14 +34,14 @@ pub enum TunnelState { location: Option<GeoIpLocation>, }, Disconnecting(ActionAfterDisconnect), - Blocked(BlockReason), + Error(ErrorState), } impl TunnelState { - /// Returns true if the tunnel state is the blocked state. - pub fn is_blocked(&self) -> bool { + /// Returns true if the tunnel state is in the error state. + pub fn is_in_error_state(&self) -> bool { match self { - TunnelState::Blocked(_) => true, + TunnelState::Error(_) => true, _ => false, } } diff --git a/talpid-core/src/tunnel_state_machine/connected_state.rs b/talpid-core/src/tunnel_state_machine/connected_state.rs index fd26b20bc3..3ce80db030 100644 --- a/talpid-core/src/tunnel_state_machine/connected_state.rs +++ b/talpid-core/src/tunnel_state_machine/connected_state.rs @@ -1,5 +1,5 @@ use super::{ - AfterDisconnect, BlockedState, ConnectingState, DisconnectingState, EventConsequence, + AfterDisconnect, ConnectingState, DisconnectingState, ErrorState, EventConsequence, SharedTunnelStateValues, TunnelCommand, TunnelState, TunnelStateTransition, TunnelStateWrapper, }; use crate::{ @@ -12,7 +12,7 @@ use futures::{ }; use talpid_types::{ net::{Endpoint, TunnelParameters}, - tunnel::BlockReason, + tunnel::ErrorStateCause, ErrorExt, }; @@ -20,7 +20,7 @@ pub struct ConnectedStateBootstrap { pub metadata: TunnelMetadata, pub tunnel_events: mpsc::UnboundedReceiver<TunnelEvent>, pub tunnel_parameters: TunnelParameters, - pub tunnel_close_event: Option<oneshot::Receiver<Option<BlockReason>>>, + pub tunnel_close_event: Option<oneshot::Receiver<Option<ErrorStateCause>>>, pub close_handle: Option<CloseHandle>, } @@ -29,7 +29,7 @@ pub struct ConnectedState { metadata: TunnelMetadata, tunnel_events: mpsc::UnboundedReceiver<TunnelEvent>, tunnel_parameters: TunnelParameters, - tunnel_close_event: Option<oneshot::Receiver<Option<BlockReason>>>, + tunnel_close_event: Option<oneshot::Receiver<Option<ErrorStateCause>>>, close_handle: Option<CloseHandle>, } @@ -123,7 +123,7 @@ impl ConnectedState { ); self.disconnect( shared_values, - AfterDisconnect::Block(BlockReason::SetFirewallPolicyError), + AfterDisconnect::Block(ErrorStateCause::SetFirewallPolicyError), ) } } @@ -137,7 +137,7 @@ impl ConnectedState { if is_offline { self.disconnect( shared_values, - AfterDisconnect::Block(BlockReason::IsOffline), + AfterDisconnect::Block(ErrorStateCause::IsOffline), ) } else { SameState(self) @@ -183,7 +183,7 @@ impl ConnectedState { match poll_result { Ok(Async::Ready(block_reason)) => { if let Some(reason) = block_reason { - return NewState(BlockedState::enter(shared_values, reason)); + return NewState(ErrorState::enter(shared_values, reason)); } } Ok(Async::NotReady) => return NoEvents(self), @@ -217,7 +217,7 @@ impl TunnelState for ConnectedState { ( connected_state.close_handle, connected_state.tunnel_close_event, - AfterDisconnect::Block(BlockReason::SetFirewallPolicyError), + AfterDisconnect::Block(ErrorStateCause::SetFirewallPolicyError), ), ) } else if let Err(error) = connected_state.set_dns(shared_values) { @@ -230,7 +230,7 @@ impl TunnelState for ConnectedState { ( connected_state.close_handle, connected_state.tunnel_close_event, - AfterDisconnect::Block(BlockReason::SetDnsError), + AfterDisconnect::Block(ErrorStateCause::SetDnsError), ), ) } else { diff --git a/talpid-core/src/tunnel_state_machine/connecting_state.rs b/talpid-core/src/tunnel_state_machine/connecting_state.rs index 67036ea98e..01e39b0ebc 100644 --- a/talpid-core/src/tunnel_state_machine/connecting_state.rs +++ b/talpid-core/src/tunnel_state_machine/connecting_state.rs @@ -1,5 +1,5 @@ use super::{ - AfterDisconnect, BlockedState, ConnectedState, ConnectedStateBootstrap, DisconnectingState, + AfterDisconnect, ConnectedState, ConnectedStateBootstrap, DisconnectingState, ErrorState, EventConsequence, SharedTunnelStateValues, TunnelCommand, TunnelState, TunnelStateTransition, TunnelStateWrapper, }; @@ -22,7 +22,7 @@ use std::{ }; use talpid_types::{ net::{openvpn, TunnelParameters}, - tunnel::BlockReason, + tunnel::ErrorStateCause, ErrorExt, }; @@ -34,7 +34,7 @@ const MIN_TUNNEL_ALIVE_TIME: Duration = Duration::from_millis(1000); pub struct ConnectingState { tunnel_events: mpsc::UnboundedReceiver<TunnelEvent>, tunnel_parameters: TunnelParameters, - tunnel_close_event: Option<oneshot::Receiver<Option<BlockReason>>>, + tunnel_close_event: Option<oneshot::Receiver<Option<ErrorStateCause>>>, close_handle: Option<CloseHandle>, retry_attempt: u32, } @@ -92,7 +92,7 @@ impl ConnectingState { fn spawn_tunnel_monitor_wait_thread( tunnel_monitor: TunnelMonitor, - ) -> Option<oneshot::Receiver<Option<BlockReason>>> { + ) -> Option<oneshot::Receiver<Option<ErrorStateCause>>> { let (tunnel_close_event_tx, tunnel_close_event_rx) = oneshot::channel(); thread::spawn(move || { @@ -120,7 +120,7 @@ impl ConnectingState { Some(tunnel_close_event_rx) } - fn wait_for_tunnel_monitor(tunnel_monitor: TunnelMonitor) -> Option<BlockReason> { + fn wait_for_tunnel_monitor(tunnel_monitor: TunnelMonitor) -> Option<ErrorStateCause> { match tunnel_monitor.wait() { Ok(_) => None, Err(error) => match error { @@ -135,7 +135,7 @@ impl ConnectingState { "{}", error.display_chain_with_msg("TAP adapter problem detected") ); - Some(BlockReason::TapAdapterProblem) + Some(ErrorStateCause::TapAdapterProblem) } error => { warn!( @@ -183,7 +183,7 @@ impl ConnectingState { ( self.close_handle, self.tunnel_close_event, - AfterDisconnect::Block(BlockReason::SetFirewallPolicyError), + AfterDisconnect::Block(ErrorStateCause::SetFirewallPolicyError), ), )) } @@ -201,7 +201,7 @@ impl ConnectingState { ( self.close_handle, self.tunnel_close_event, - AfterDisconnect::Block(BlockReason::IsOffline), + AfterDisconnect::Block(ErrorStateCause::IsOffline), ), )) } else { @@ -248,7 +248,7 @@ impl ConnectingState { ( self.close_handle, self.tunnel_close_event, - AfterDisconnect::Block(BlockReason::AuthFailed(reason)), + AfterDisconnect::Block(ErrorStateCause::AuthFailed(reason)), ), )), Ok(TunnelEvent::Up(metadata)) => NewState(ConnectedState::enter( @@ -282,7 +282,7 @@ impl ConnectingState { match poll_result { Ok(Async::Ready(block_reason)) => { if let Some(reason) = block_reason { - return EventConsequence::NewState(BlockedState::enter(shared_values, reason)); + return EventConsequence::NewState(ErrorState::enter(shared_values, reason)); } } Ok(Async::NotReady) => return EventConsequence::NoEvents(self), @@ -333,13 +333,15 @@ impl TunnelState for ConnectingState { retry_attempt: u32, ) -> (TunnelStateWrapper, TunnelStateTransition) { if shared_values.is_offline { - return BlockedState::enter(shared_values, BlockReason::IsOffline); + return ErrorState::enter(shared_values, ErrorStateCause::IsOffline); } match shared_values .tunnel_parameters_generator .generate(retry_attempt) { - Err(err) => BlockedState::enter(shared_values, BlockReason::TunnelParameterError(err)), + Err(err) => { + ErrorState::enter(shared_values, ErrorStateCause::TunnelParameterError(err)) + } Ok(tunnel_parameters) => { if let Err(error) = Self::set_firewall_policy(shared_values, &tunnel_parameters) { error!( @@ -348,7 +350,7 @@ impl TunnelState for ConnectingState { "Failed to apply firewall policy for connecting state" ) ); - BlockedState::enter(shared_values, BlockReason::StartTunnelError) + ErrorState::enter(shared_values, ErrorStateCause::StartTunnelError) } else { #[cfg(target_os = "android")] { @@ -394,10 +396,12 @@ impl TunnelState for ConnectingState { error.display_chain_with_msg("Failed to start tunnel") ); let block_reason = match error { - tunnel::Error::EnableIpv6Error => BlockReason::Ipv6Unavailable, - _ => BlockReason::StartTunnelError, + tunnel::Error::EnableIpv6Error => { + ErrorStateCause::Ipv6Unavailable + } + _ => ErrorStateCause::StartTunnelError, }; - BlockedState::enter(shared_values, block_reason) + ErrorState::enter(shared_values, block_reason) } } } diff --git a/talpid-core/src/tunnel_state_machine/disconnected_state.rs b/talpid-core/src/tunnel_state_machine/disconnected_state.rs index 6b95548ddd..f183a7c78c 100644 --- a/talpid-core/src/tunnel_state_machine/disconnected_state.rs +++ b/talpid-core/src/tunnel_state_machine/disconnected_state.rs @@ -1,5 +1,5 @@ use super::{ - BlockedState, ConnectingState, EventConsequence, SharedTunnelStateValues, TunnelCommand, + ConnectingState, ErrorState, EventConsequence, SharedTunnelStateValues, TunnelCommand, TunnelState, TunnelStateTransition, TunnelStateWrapper, }; use crate::firewall::FirewallPolicy; @@ -76,9 +76,7 @@ impl TunnelState for DisconnectedState { SameState(self) } Ok(TunnelCommand::Connect) => NewState(ConnectingState::enter(shared_values, 0)), - Ok(TunnelCommand::Block(reason)) => { - NewState(BlockedState::enter(shared_values, reason)) - } + Ok(TunnelCommand::Block(reason)) => NewState(ErrorState::enter(shared_values, reason)), Ok(_) => SameState(self), Err(_) => Finished, } diff --git a/talpid-core/src/tunnel_state_machine/disconnecting_state.rs b/talpid-core/src/tunnel_state_machine/disconnecting_state.rs index 733a6e2448..d18fdeb6ee 100644 --- a/talpid-core/src/tunnel_state_machine/disconnecting_state.rs +++ b/talpid-core/src/tunnel_state_machine/disconnecting_state.rs @@ -1,5 +1,5 @@ use super::{ - BlockedState, ConnectingState, DisconnectedState, EventConsequence, SharedTunnelStateValues, + ConnectingState, DisconnectedState, ErrorState, EventConsequence, SharedTunnelStateValues, TunnelCommand, TunnelState, TunnelStateTransition, TunnelStateWrapper, }; use crate::tunnel::CloseHandle; @@ -9,14 +9,14 @@ use futures::{ }; use std::thread; use talpid_types::{ - tunnel::{ActionAfterDisconnect, BlockReason}, + tunnel::{ActionAfterDisconnect, ErrorStateCause}, ErrorExt, }; /// This state is active from when we manually trigger a tunnel kill until the tunnel wait /// operation (TunnelExit) returned. pub struct DisconnectingState { - exited: Option<oneshot::Receiver<Option<BlockReason>>>, + exited: Option<oneshot::Receiver<Option<ErrorStateCause>>>, after_disconnect: AfterDisconnect, } @@ -58,7 +58,7 @@ impl DisconnectingState { } Ok(TunnelCommand::IsOffline(is_offline)) => { shared_values.is_offline = is_offline; - if !is_offline && reason == BlockReason::IsOffline { + if !is_offline && reason == ErrorStateCause::IsOffline { AfterDisconnect::Reconnect(0) } else { AfterDisconnect::Block(reason) @@ -81,7 +81,7 @@ impl DisconnectingState { Ok(TunnelCommand::IsOffline(is_offline)) => { shared_values.is_offline = is_offline; if is_offline { - AfterDisconnect::Block(BlockReason::IsOffline) + AfterDisconnect::Block(ErrorStateCause::IsOffline) } else { AfterDisconnect::Reconnect(retry_attempt) } @@ -117,16 +117,16 @@ impl DisconnectingState { fn after_disconnect( self, - block_reason: Option<BlockReason>, + block_reason: Option<ErrorStateCause>, shared_values: &mut SharedTunnelStateValues, ) -> (TunnelStateWrapper, TunnelStateTransition) { if let Some(reason) = block_reason { - return BlockedState::enter(shared_values, reason); + return ErrorState::enter(shared_values, reason); } match self.after_disconnect { AfterDisconnect::Nothing => DisconnectedState::enter(shared_values, ()), - AfterDisconnect::Block(reason) => BlockedState::enter(shared_values, reason), + AfterDisconnect::Block(cause) => ErrorState::enter(shared_values, cause), AfterDisconnect::Reconnect(retry_attempt) => { ConnectingState::enter(shared_values, retry_attempt) } @@ -137,7 +137,7 @@ impl DisconnectingState { impl TunnelState for DisconnectingState { type Bootstrap = ( Option<CloseHandle>, - Option<oneshot::Receiver<Option<BlockReason>>>, + Option<oneshot::Receiver<Option<ErrorStateCause>>>, AfterDisconnect, ); @@ -180,7 +180,7 @@ impl TunnelState for DisconnectingState { /// Which state should be transitioned to after disconnection is complete. pub enum AfterDisconnect { Nothing, - Block(BlockReason), + Block(ErrorStateCause), Reconnect(u32), } diff --git a/talpid-core/src/tunnel_state_machine/blocked_state.rs b/talpid-core/src/tunnel_state_machine/error_state.rs index bdf054ab2c..9d8402997d 100644 --- a/talpid-core/src/tunnel_state_machine/blocked_state.rs +++ b/talpid-core/src/tunnel_state_machine/error_state.rs @@ -4,21 +4,25 @@ use super::{ }; use crate::firewall::FirewallPolicy; use futures::{sync::mpsc, Stream}; -use talpid_types::{tunnel::BlockReason, ErrorExt}; +use talpid_types::{ + tunnel::{self as talpid_tunnel, ErrorStateCause}, + ErrorExt, +}; /// No tunnel is running and all network connections are blocked. -pub struct BlockedState { - block_reason: BlockReason, +pub struct ErrorState { + block_reason: ErrorStateCause, } -impl BlockedState { - fn set_firewall_policy(shared_values: &mut SharedTunnelStateValues) -> Option<BlockReason> { +impl ErrorState { + /// Returns true if firewall policy was applied successfully + fn set_firewall_policy(shared_values: &mut SharedTunnelStateValues) -> bool { let policy = FirewallPolicy::Blocked { allow_lan: shared_values.allow_lan, }; match shared_values.firewall.apply_policy(policy) { - Ok(()) => None, + Ok(()) => true, Err(error) => { log::error!( "{}", @@ -26,15 +30,16 @@ impl BlockedState { "Failed to apply firewall policy for blocked state" ) ); - Some(BlockReason::SetFirewallPolicyError) + false } } } + /// Returns true if a new tunnel device was successfully created. #[cfg(target_os = "android")] - fn create_blocking_tun(shared_values: &mut SharedTunnelStateValues) -> Option<BlockReason> { + fn create_blocking_tun(shared_values: &mut SharedTunnelStateValues) -> bool { match shared_values.tun_provider.create_tun_if_closed() { - Ok(()) => None, + Ok(()) => true, Err(error) => { log::error!( "{}", @@ -42,28 +47,28 @@ impl BlockedState { "Failed to open tunnel adapter to drop packets for blocked state" ) ); - Some(BlockReason::SetFirewallPolicyError) + false } } } } -impl TunnelState for BlockedState { - type Bootstrap = BlockReason; +impl TunnelState for ErrorState { + type Bootstrap = ErrorStateCause; fn enter( shared_values: &mut SharedTunnelStateValues, block_reason: Self::Bootstrap, ) -> (TunnelStateWrapper, TunnelStateTransition) { - let block_reason = Self::set_firewall_policy(shared_values).unwrap_or_else(|| block_reason); + #[cfg(not(target_os = "android"))] + let is_blocking = Self::set_firewall_policy(shared_values); #[cfg(target_os = "android")] - let block_reason = Self::create_blocking_tun(shared_values).unwrap_or_else(|| block_reason); - + let is_blocking = Self::create_blocking_tun(shared_values); ( - TunnelStateWrapper::from(BlockedState { + TunnelStateWrapper::from(ErrorState { block_reason: block_reason.clone(), }), - TunnelStateTransition::Blocked(block_reason), + TunnelStateTransition::Error(talpid_tunnel::ErrorState::new(block_reason, is_blocking)), ) } @@ -86,7 +91,7 @@ impl TunnelState for BlockedState { } Ok(TunnelCommand::IsOffline(is_offline)) => { shared_values.is_offline = is_offline; - if !is_offline && self.block_reason == BlockReason::IsOffline { + if !is_offline && self.block_reason == ErrorStateCause::IsOffline { NewState(ConnectingState::enter(shared_values, 0)) } else { SameState(self) @@ -96,9 +101,7 @@ impl TunnelState for BlockedState { Ok(TunnelCommand::Disconnect) | Err(_) => { NewState(DisconnectedState::enter(shared_values, ())) } - Ok(TunnelCommand::Block(reason)) => { - NewState(BlockedState::enter(shared_values, reason)) - } + Ok(TunnelCommand::Block(reason)) => NewState(ErrorState::enter(shared_values, reason)), } } } diff --git a/talpid-core/src/tunnel_state_machine/mod.rs b/talpid-core/src/tunnel_state_machine/mod.rs index b86795491d..76bd62a1d4 100644 --- a/talpid-core/src/tunnel_state_machine/mod.rs +++ b/talpid-core/src/tunnel_state_machine/mod.rs @@ -1,18 +1,18 @@ #[macro_use] mod macros; -mod blocked_state; mod connected_state; mod connecting_state; mod disconnected_state; mod disconnecting_state; +mod error_state; use self::{ - blocked_state::BlockedState, connected_state::{ConnectedState, ConnectedStateBootstrap}, connecting_state::ConnectingState, disconnected_state::DisconnectedState, disconnecting_state::{AfterDisconnect, DisconnectingState}, + error_state::ErrorState, }; use crate::{ dns::DnsMonitor, @@ -32,7 +32,7 @@ use std::{ use talpid_types::android::AndroidContext; use talpid_types::{ net::TunnelParameters, - tunnel::{BlockReason, ParameterGenerationError, TunnelStateTransition}, + tunnel::{ErrorStateCause, ParameterGenerationError, TunnelStateTransition}, ErrorExt, }; use tokio_core::reactor::Core; @@ -182,7 +182,7 @@ pub enum TunnelCommand { /// Close tunnel connection. Disconnect, /// Disconnect any open tunnel and block all network access - Block(BlockReason), + Block(ErrorStateCause), } /// Asynchronous handling of the tunnel state machine. @@ -297,7 +297,7 @@ impl<T: TunnelState> From<EventConsequence<T>> for TunnelStateMachineAction { pub trait TunnelParametersGenerator: Send + 'static { /// Given the number of consecutive failed retry attempts, it should yield a `TunnelParameters` /// to establish a tunnel with. - /// If this returns `None` then the state machine goes into the `Blocked` state. + /// If this returns `None` then the state machine goes into the `Error` state. fn generate( &mut self, retry_attempt: u32, @@ -427,6 +427,6 @@ state_wrapper! { Connecting(ConnectingState), Connected(ConnectedState), Disconnecting(DisconnectingState), - Blocked(BlockedState), + Error(ErrorState), } } diff --git a/talpid-types/src/tunnel.rs b/talpid-types/src/tunnel.rs index 9ab3ed6d36..fad6be14a4 100644 --- a/talpid-types/src/tunnel.rs +++ b/talpid-types/src/tunnel.rs @@ -18,7 +18,7 @@ pub enum TunnelStateTransition { /// Disconnecting tunnel. Disconnecting(ActionAfterDisconnect), /// Tunnel is disconnected but secured by blocking all connections. - Blocked(BlockReason), + Error(ErrorState), } /// Action that will be taken after disconnection is complete. @@ -32,22 +32,43 @@ pub enum ActionAfterDisconnect { Reconnect, } -impl TunnelStateTransition { - pub fn is_blocked(&self) -> bool { - match self { - TunnelStateTransition::Blocked(_) => true, - _ => false, - } +/// Error state +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +#[cfg_attr(target_os = "android", derive(IntoJava))] +#[cfg_attr(target_os = "android", jnix(package = "net.mullvad.talpid.tunnel"))] +pub struct ErrorState { + /// Reason why the tunnel state machine ended up in the error state + cause: ErrorStateCause, + /// Indicates whether the daemon is currently blocking all traffic. This _should_ always be + /// true - in the case it is not, the user should be notified that no traffic is being blocked. + /// A false value means there was a serious error and the intended security properties are not + /// being upheld. + is_blocking: bool, +} + +impl ErrorState { + pub fn new(cause: ErrorStateCause, is_blocking: bool) -> Self { + Self { cause, is_blocking } + } + + pub fn is_blocking(&self) -> bool { + self.is_blocking + } + + pub fn cause(&self) -> &ErrorStateCause { + &self.cause } } + /// Reason for entering the blocked state. #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] #[serde(tag = "reason", content = "details")] #[cfg_attr(target_os = "android", derive(IntoJava))] #[cfg_attr(target_os = "android", jnix(package = "net.mullvad.talpid.tunnel"))] -pub enum BlockReason { +pub enum ErrorStateCause { /// Authentication with remote server failed. AuthFailed(Option<String>), /// Failed to configure IPv6 because it's disabled in the platform. @@ -86,9 +107,9 @@ pub enum ParameterGenerationError { CustomTunnelHostResultionError, } -impl fmt::Display for BlockReason { +impl fmt::Display for ErrorStateCause { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use self::BlockReason::*; + use self::ErrorStateCause::*; let description = match *self { AuthFailed(ref reason) => { return write!( |
