diff options
| author | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2018-09-03 08:09:30 -0300 |
|---|---|---|
| committer | Janito Vaqueiro Ferreira Filho <janito@mullvad.net> | 2018-09-03 08:09:30 -0300 |
| commit | 9e440721700a7ab6a3d0d5c81c4d42949ea7108c (patch) | |
| tree | 159c1ac066c679e3d9204c79b15e118426678e4f /gui | |
| parent | a23afed45caa953cb9765991a4692b66b3a39329 (diff) | |
| parent | 13e272fe2c22a5b026aa4dc8cae80e9a68df4dc2 (diff) | |
| download | mullvadvpn-9e440721700a7ab6a3d0d5c81c4d42949ea7108c.tar.xz mullvadvpn-9e440721700a7ab6a3d0d5c81c4d42949ea7108c.zip | |
Merge branch 'blocked-state'
Diffstat (limited to 'gui')
6 files changed, 125 insertions, 29 deletions
diff --git a/gui/packages/desktop/src/renderer/app.js b/gui/packages/desktop/src/renderer/app.js index e88d3d619c..b693a84544 100644 --- a/gui/packages/desktop/src/renderer/app.js +++ b/gui/packages/desktop/src/renderer/app.js @@ -402,10 +402,9 @@ export default class AppRenderer { actions.settings.updateAutoConnect(autoConnect); } - async _fetchSecurityState() { - const securityState = await this._daemonRpc.getState(); - const connectionState = this._tunnelStateToConnectionState(securityState); - this._updateConnectionState(connectionState); + async _fetchTunnelState() { + const tunnelState = await this._daemonRpc.getState(); + this._updateConnectionState(tunnelState); } async _fetchTunnelOptions() { @@ -502,11 +501,9 @@ export default class AppRenderer { } if (newState) { - const connectionState = this._tunnelStateToConnectionState(newState); + log.debug(`Got new state from daemon '${JSON.stringify(newState)}'`); - log.debug(`Got new state from daemon '${newState}', translated to '${connectionState}'`); - - this._updateConnectionState(connectionState); + this._updateConnectionState(newState); this._refreshStateOnChange(); } }); @@ -514,7 +511,7 @@ export default class AppRenderer { _fetchInitialState() { return Promise.all([ - this._fetchSecurityState(), + this._fetchTunnelState(), this.fetchRelaySettings(), this._fetchRelayLocations(), this._fetchAllowLan(), @@ -530,6 +527,7 @@ export default class AppRenderer { const iconTypes: { [ConnectionState]: TrayIconType } = { connected: 'secured', connecting: 'securing', + blocked: 'securing', }; const type = iconTypes[connectionState] || 'unsecured'; @@ -545,28 +543,44 @@ export default class AppRenderer { } _tunnelStateToConnectionState(tunnelState: TunnelState): ConnectionState { - if (tunnelState === 'disconnected' || tunnelState === 'disconnecting') { - return 'disconnected'; - } else if (tunnelState === 'connected' || tunnelState === 'connecting') { - return tunnelState; + switch (tunnelState.state) { + case 'disconnected': + // Fall through + case 'disconnecting': + return 'disconnected'; + case 'connected': + return 'connected'; + case 'connecting': + return 'connecting'; + case 'blocked': + return 'blocked'; + default: + throw new Error('Unknown tunnel state: ' + (tunnelState: empty)); } - throw new Error('Unsupported state/target state combination: ' + JSON.stringify(tunnelState)); } - _updateConnectionState(connectionState: ConnectionState) { + _updateConnectionState(tunnelState: TunnelState) { const actions = this._reduxActions; - switch (connectionState) { + switch (tunnelState.state) { case 'connecting': actions.connection.connecting(); break; case 'connected': actions.connection.connected(); break; + case 'disconnecting': + // Fall through case 'disconnected': actions.connection.disconnected(); break; + case 'blocked': + actions.connection.blocked(tunnelState.details); + break; + default: + log.error(`Unexpected TunnelState: ${(tunnelState: empty)}`); } + const connectionState = this._tunnelStateToConnectionState(tunnelState); this._updateTrayIcon(connectionState); this._showNotification(connectionState); } @@ -582,6 +596,9 @@ export default class AppRenderer { case 'disconnected': this._notificationController.show('Unsecured'); break; + case 'blocked': + this._notificationController.show('Blocked all connections'); + break; default: log.error(`Unexpected ConnectionState: ${(connectionState: empty)}`); return; diff --git a/gui/packages/desktop/src/renderer/components/Connect.js b/gui/packages/desktop/src/renderer/components/Connect.js index d9c3768df6..bf08000ce5 100644 --- a/gui/packages/desktop/src/renderer/components/Connect.js +++ b/gui/packages/desktop/src/renderer/components/Connect.js @@ -10,7 +10,7 @@ import * as AppButton from './AppButton'; import Img from './Img'; import Map from './Map'; import styles from './ConnectStyles'; -import { NoCreditError, NoInternetError } from '../errors'; +import { BlockedError, NoCreditError, NoInternetError } from '../errors'; import WindowStateObserver from '../lib/window-state-observer'; import type { HeaderBarStyle } from './HeaderBar'; @@ -103,6 +103,11 @@ export default class Connect extends Component<Props, State> { message = 'Your internet connection will be secured when you get back online'; } + if (error instanceof BlockedError) { + title = 'Blocked'; + message = error.message; + } + return ( <View style={styles.connect}> <View style={styles.status_icon}> @@ -345,6 +350,7 @@ export default class Connect extends Component<Props, State> { return 'error'; case 'connecting': case 'connected': + case 'blocked': return 'success'; default: throw new Error(`Invalid ConnectionState: ${(status: empty)}`); @@ -392,6 +398,11 @@ export default class Connect extends Component<Props, State> { return new NoCreditError(); } + // Tunnel is blocked due to an error? + if (this.props.connection.status === 'blocked') { + return new BlockedError(this.props.connection.blockReason); + } + return null; } } diff --git a/gui/packages/desktop/src/renderer/errors.js b/gui/packages/desktop/src/renderer/errors.js index 7464851a1f..ddf0c239bb 100644 --- a/gui/packages/desktop/src/renderer/errors.js +++ b/gui/packages/desktop/src/renderer/errors.js @@ -1,5 +1,22 @@ // @flow +import type { BlockReason } from './lib/daemon-rpc'; + +export class BlockedError extends Error { + constructor(reason: BlockReason) { + switch (reason) { + case 'set_security_policy_error': + super('Failed to apply security policy'); + break; + case 'start_tunnel_error': + super('Failed to start tunnel connection'); + break; + default: + super(`Unknown error: ${(reason: empty)}`); + } + } +} + export class NoCreditError extends Error { constructor() { super("Account doesn't have enough credit available for connection"); diff --git a/gui/packages/desktop/src/renderer/lib/daemon-rpc.js b/gui/packages/desktop/src/renderer/lib/daemon-rpc.js index e590534d69..ae2e501268 100644 --- a/gui/packages/desktop/src/renderer/lib/daemon-rpc.js +++ b/gui/packages/desktop/src/renderer/lib/daemon-rpc.js @@ -41,7 +41,29 @@ const LocationSchema = object({ mullvad_exit_ip: boolean, }); -export type TunnelState = 'disconnected' | 'connecting' | 'connected' | 'disconnecting'; +export type BlockReason = 'set_security_policy_error' | 'start_tunnel_error'; +export type DisconnectedState = { + state: 'disconnected', +}; +export type ConnectingState = { + state: 'connecting', +}; +export type ConnectedState = { + state: 'connected', +}; +export type DisconnectingState = { + state: 'disconnecting', +}; +export type BlockedState = { + state: 'blocked', + details: BlockReason, +}; +export type TunnelState = + | DisconnectedState + | ConnectingState + | ConnectedState + | DisconnectingState + | BlockedState; export type RelayProtocol = 'tcp' | 'udp'; export type RelayLocation = {| city: [string, string] |} | {| country: string |}; @@ -196,13 +218,23 @@ const AccountDataSchema = object({ expiry: string, }); -const allTunnelStates: Array<TunnelState> = [ - 'disconnected', - 'connecting', - 'connected', - 'disconnecting', -]; -const TunnelStateSchema = enumeration(...allTunnelStates); +const allBlockReasons: Array<BlockReason> = ['set_security_policy_error', 'start_tunnel_error']; +const BlockedStateSchema = object({ + state: enumeration('blocked'), + details: enumeration(...allBlockReasons), +}); +const ConnectedStateSchema = object({ state: enumeration('connected') }); +const ConnectingStateSchema = object({ state: enumeration('connecting') }); +const DisconnectedStateSchema = object({ state: enumeration('disconnected') }); +const DisconnectingStateSchema = object({ state: enumeration('disconnecting') }); + +const TunnelStateSchema = oneOf( + BlockedStateSchema, + ConnectedStateSchema, + ConnectingStateSchema, + DisconnectedStateSchema, + DisconnectingStateSchema, +); export type AppVersionInfo = { currentIsSupported: boolean, diff --git a/gui/packages/desktop/src/renderer/redux/connection/actions.js b/gui/packages/desktop/src/renderer/redux/connection/actions.js index a4c5c68890..052d206986 100644 --- a/gui/packages/desktop/src/renderer/redux/connection/actions.js +++ b/gui/packages/desktop/src/renderer/redux/connection/actions.js @@ -14,6 +14,11 @@ type DisconnectedAction = { type: 'DISCONNECTED', }; +type BlockedAction = { + type: 'BLOCKED', + reason: string, +}; + type NewLocationAction = { type: 'NEW_LOCATION', newLocation: { @@ -39,6 +44,7 @@ export type ConnectionAction = | ConnectingAction | ConnectedAction | DisconnectedAction + | BlockedAction | OnlineAction | OfflineAction; @@ -60,6 +66,13 @@ function disconnected(): DisconnectedAction { }; } +function blocked(reason: string): BlockedAction { + return { + type: 'BLOCKED', + reason, + }; +} + function newLocation(newLoc: $PropertyType<NewLocationAction, 'newLocation'>): NewLocationAction { return { type: 'NEW_LOCATION', @@ -84,6 +97,7 @@ export default { connecting, connected, disconnected, + blocked, online, offline, }; diff --git a/gui/packages/desktop/src/renderer/redux/connection/reducers.js b/gui/packages/desktop/src/renderer/redux/connection/reducers.js index c75d0f2a8f..d425dec438 100644 --- a/gui/packages/desktop/src/renderer/redux/connection/reducers.js +++ b/gui/packages/desktop/src/renderer/redux/connection/reducers.js @@ -3,7 +3,7 @@ import type { ReduxAction } from '../store'; import type { Ip } from '../../lib/daemon-rpc'; -export type ConnectionState = 'disconnected' | 'connecting' | 'connected'; +export type ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'blocked'; export type ConnectionReduxState = { status: ConnectionState, isOnline: boolean, @@ -12,6 +12,7 @@ export type ConnectionReduxState = { longitude: ?number, country: ?string, city: ?string, + blockReason: ?string, }; const initialState: ConnectionReduxState = { @@ -22,6 +23,7 @@ const initialState: ConnectionReduxState = { longitude: null, country: null, city: null, + blockReason: null, }; export default function( @@ -33,13 +35,16 @@ export default function( return { ...state, ...action.newLocation }; case 'CONNECTING': - return { ...state, ...{ status: 'connecting' } }; + return { ...state, ...{ status: 'connecting', blockReason: null } }; case 'CONNECTED': - return { ...state, ...{ status: 'connected' } }; + return { ...state, ...{ status: 'connected', blockReason: null } }; case 'DISCONNECTED': - return { ...state, ...{ status: 'disconnected' } }; + return { ...state, ...{ status: 'disconnected', blockReason: null } }; + + case 'BLOCKED': + return { ...state, ...{ status: 'blocked', blockReason: action.reason } }; case 'ONLINE': return { ...state, ...{ isOnline: true } }; |
