diff options
| author | Oskar Nyberg <oskar@mullvad.net> | 2024-04-08 12:10:56 +0200 |
|---|---|---|
| committer | Oskar Nyberg <oskar@mullvad.net> | 2024-04-11 17:21:23 +0200 |
| commit | f3221b144564dd70ad0d32354d9451009bb0c14d (patch) | |
| tree | 509cb654b6a781097c90ad68f07c962ea7d0a2e8 | |
| parent | d5dc077984ecf64f9fe7ab31cd32d2a3d881aeea (diff) | |
| download | mullvadvpn-f3221b144564dd70ad0d32354d9451009bb0c14d.tar.xz mullvadvpn-f3221b144564dd70ad0d32354d9451009bb0c14d.zip | |
Improve custom proxy daemon rpc code and types
| -rw-r--r-- | gui/src/main/daemon-rpc.ts | 187 | ||||
| -rw-r--r-- | gui/src/renderer/redux/settings/reducers.ts | 4 | ||||
| -rw-r--r-- | gui/src/shared/daemon-rpc-types.ts | 69 |
3 files changed, 84 insertions, 176 deletions
diff --git a/gui/src/main/daemon-rpc.ts b/gui/src/main/daemon-rpc.ts index 703ad027cc..531ec80ced 100644 --- a/gui/src/main/daemon-rpc.ts +++ b/gui/src/main/daemon-rpc.ts @@ -17,6 +17,7 @@ import { ApiAccessMethodSettings, AuthFailedError, BridgeSettings, + BridgesMethod, BridgeState, BridgeType, ConnectionConfig, @@ -27,6 +28,7 @@ import { DaemonEvent, DeviceEvent, DeviceState, + DirectMethod, EndpointObfuscationType, ErrorState, ErrorStateCause, @@ -82,7 +84,6 @@ const NETWORK_CALL_TIMEOUT = 10000; const CHANNEL_STATE_TIMEOUT = 1000 * 60 * 60; const noConnectionError = new Error('No connection established to daemon'); -const configNotSupported = new Error('Setting custom settings is not supported'); const invalidErrorStateCause = new Error( 'VPN_PERMISSION_DENIED is not a valid error state cause on desktop', ); @@ -347,51 +348,17 @@ export class DaemonRpc { public async setBridgeSettings(bridgeSettings: BridgeSettings): Promise<void> { const grpcBridgeSettings = new grpcTypes.BridgeSettings(); - if (bridgeSettings.type === 'custom') { - throw configNotSupported; - } - - grpcBridgeSettings.setBridgeType(grpcTypes.BridgeSettings.BridgeType.NORMAL); + grpcBridgeSettings.setBridgeType( + bridgeSettings.type === 'normal' + ? grpcTypes.BridgeSettings.BridgeType.NORMAL + : grpcTypes.BridgeSettings.BridgeType.CUSTOM, + ); const normalSettings = convertToNormalBridgeSettings(bridgeSettings.normal); grpcBridgeSettings.setNormal(normalSettings); if (bridgeSettings.custom) { - const customProxy = new grpcTypes.CustomProxy(); - - const customSettings = bridgeSettings.custom; - - if ('local' in customSettings) { - const local = customSettings.local; - const socks5Local = new grpcTypes.Socks5Local(); - socks5Local.setLocalPort(local.localPort); - socks5Local.setRemoteIp(local.remoteIp); - socks5Local.setRemotePort(local.remotePort); - customProxy.setSocks5local(socks5Local); - } - if ('remote' in customSettings) { - const remote = customSettings.remote; - const socks5Remote = new grpcTypes.Socks5Remote(); - if (remote.auth) { - const auth = new grpcTypes.SocksAuth(); - auth.setUsername(remote.auth.username); - auth.setPassword(remote.auth.password); - socks5Remote.setAuth(auth); - } - socks5Remote.setIp(remote.ip); - socks5Remote.setPort(remote.port); - customProxy.setSocks5remote(socks5Remote); - } - if ('shadowsocks' in customSettings) { - const shadowsocks = customSettings.shadowsocks; - const shadowOut = new grpcTypes.Shadowsocks(); - shadowOut.setCipher(shadowsocks.cipher); - shadowOut.setIp(shadowsocks.ip); - shadowOut.setPort(shadowsocks.port); - shadowOut.setPassword(shadowsocks.password); - customProxy.setShadowsocks(shadowOut); - } - + const customProxy = convertToCustomProxy(bridgeSettings.custom); grpcBridgeSettings.setCustom(customProxy); } @@ -1276,41 +1243,8 @@ function convertFromBridgeSettings(bridgeSettings: grpcTypes.BridgeSettings): Br ownership, }; - let custom = undefined; - - if (bridgeSettingsObject.custom) { - const localSettings = bridgeSettingsObject.custom.socks5local; - if (localSettings) { - custom = { - local: { - localPort: localSettings.localPort, - remoteIp: localSettings.remoteIp, - remotePort: localSettings.remotePort, - }, - }; - } - const remoteSettings = bridgeSettingsObject.custom.socks5remote; - if (remoteSettings) { - custom = { - remote: { - ip: remoteSettings.ip, - port: remoteSettings.port, - auth: remoteSettings.auth && { ...remoteSettings.auth }, - }, - }; - } - const shadowsocksSettings = bridgeSettingsObject.custom.shadowsocks; - if (shadowsocksSettings) { - custom = { - shadowsocks: { - ip: shadowsocksSettings.ip, - port: shadowsocksSettings.port, - password: shadowsocksSettings.password, - cipher: shadowsocksSettings.cipher, - }, - }; - } - } + const grpcCustom = bridgeSettings.getCustom(); + const custom = grpcCustom ? convertFromCustomProxy(grpcCustom) : undefined; return { type, normal, custom }; } @@ -1910,15 +1844,16 @@ function convertFromApiAccessMethodSettings( ): ApiAccessMethodSettings { const direct = convertFromApiAccessMethodSetting( ensureExists(accessMethods.getDirect(), "no 'Direct' access method was found"), - ); + ) as AccessMethodSetting<DirectMethod>; const bridges = convertFromApiAccessMethodSetting( ensureExists(accessMethods.getMullvadBridges(), "no 'Mullvad Bridges' access method was found"), - ); - const custom = - accessMethods - .getCustomList() - .filter((setting) => setting.hasId() && setting.hasAccessMethod()) - .map(convertFromApiAccessMethodSetting) ?? []; + ) as AccessMethodSetting<BridgesMethod>; + const custom = accessMethods + .getCustomList() + .filter((setting) => setting.hasId() && setting.hasAccessMethod()) + .map(convertFromApiAccessMethodSetting) + // The last filter helps TypeScript infer the custom proxy type. + .filter(isCustomProxy); return { direct, @@ -1927,6 +1862,12 @@ function convertFromApiAccessMethodSettings( }; } +function isCustomProxy( + accessMethod: AccessMethodSetting, +): accessMethod is AccessMethodSetting<CustomProxy> { + return accessMethod.type !== 'direct' && accessMethod.type !== 'bridges'; +} + function convertFromApiAccessMethodSetting( setting: grpcTypes.AccessMethodSetting, ): AccessMethodSetting { @@ -1948,52 +1889,52 @@ function convertFromAccessMethod(method: grpcTypes.AccessMethod): AccessMethod { case grpcTypes.AccessMethod.AccessMethodCase.BRIDGES: return { type: 'bridges' }; case grpcTypes.AccessMethod.AccessMethodCase.CUSTOM: { - const proxy = method.getCustom()!; - switch (proxy.getProxyMethodCase()) { - case grpcTypes.CustomProxy.ProxyMethodCase.SOCKS5LOCAL: { - const socks5Local = proxy.getSocks5local()!; - return { - type: 'socks5-local', - remoteIp: socks5Local.getRemoteIp(), - remotePort: socks5Local.getRemotePort(), - remoteTransportProtocol: convertFromTransportProtocol( - socks5Local.getRemoteTransportProtocol(), - ), - localPort: socks5Local.getLocalPort(), - }; - } - case grpcTypes.CustomProxy.ProxyMethodCase.SOCKS5REMOTE: { - const socks5Remote = proxy.getSocks5remote()!; - const auth = socks5Remote.getAuth(); - return { - type: 'socks5-remote', - ip: socks5Remote.getIp(), - port: socks5Remote.getPort(), - authentication: auth === undefined ? undefined : convertFromSocksAuth(auth), - }; - } - case grpcTypes.CustomProxy.ProxyMethodCase.SHADOWSOCKS: { - const shadowsocks = proxy.getShadowsocks()!; - return { - type: 'shadowsocks', - ip: shadowsocks.getIp(), - port: shadowsocks.getPort(), - password: shadowsocks.getPassword(), - cipher: shadowsocks.getCipher(), - }; - } - case grpcTypes.CustomProxy.ProxyMethodCase.PROXY_METHOD_NOT_SET: - throw new Error('Custom method not set, which should always be set'); - } - // This break is required to prevent eslint from complainting about fallthrough, even though - // all cases are covered above. - break; + return convertFromCustomProxy(method.getCustom()!); } case grpcTypes.AccessMethod.AccessMethodCase.ACCESS_METHOD_NOT_SET: throw new Error('Access method not set, which should always be set'); } } +function convertFromCustomProxy(proxy: grpcTypes.CustomProxy): CustomProxy { + switch (proxy.getProxyMethodCase()) { + case grpcTypes.CustomProxy.ProxyMethodCase.SOCKS5LOCAL: { + const socks5Local = proxy.getSocks5local()!; + return { + type: 'socks5-local', + remoteIp: socks5Local.getRemoteIp(), + remotePort: socks5Local.getRemotePort(), + remoteTransportProtocol: convertFromTransportProtocol( + socks5Local.getRemoteTransportProtocol(), + ), + localPort: socks5Local.getLocalPort(), + }; + } + case grpcTypes.CustomProxy.ProxyMethodCase.SOCKS5REMOTE: { + const socks5Remote = proxy.getSocks5remote()!; + const auth = socks5Remote.getAuth(); + return { + type: 'socks5-remote', + ip: socks5Remote.getIp(), + port: socks5Remote.getPort(), + authentication: auth === undefined ? undefined : convertFromSocksAuth(auth), + }; + } + case grpcTypes.CustomProxy.ProxyMethodCase.SHADOWSOCKS: { + const shadowsocks = proxy.getShadowsocks()!; + return { + type: 'shadowsocks', + ip: shadowsocks.getIp(), + port: shadowsocks.getPort(), + password: shadowsocks.getPassword(), + cipher: shadowsocks.getCipher(), + }; + } + case grpcTypes.CustomProxy.ProxyMethodCase.PROXY_METHOD_NOT_SET: + throw new Error('Custom method not set, which should always be set'); + } +} + function convertFromSocksAuth(auth: grpcTypes.SocksAuth): SocksAuth { return { username: auth.getUsername(), diff --git a/gui/src/renderer/redux/settings/reducers.ts b/gui/src/renderer/redux/settings/reducers.ts index 210ef38010..e64b0d6323 100644 --- a/gui/src/renderer/redux/settings/reducers.ts +++ b/gui/src/renderer/redux/settings/reducers.ts @@ -6,6 +6,7 @@ import { BridgeState, BridgeType, CustomLists, + CustomProxy, IDnsOptions, IpVersion, IWireguardEndpointData, @@ -13,7 +14,6 @@ import { ObfuscationSettings, ObfuscationType, Ownership, - ProxySettings, RelayEndpointType, RelayLocation, RelayOverride, @@ -62,7 +62,7 @@ export type RelaySettingsRedux = export type BridgeSettingsRedux = { type: BridgeType; normal: NormalBridgeSettingsRedux; - custom?: ProxySettings; + custom?: CustomProxy; }; export interface IRelayLocationRelayRedux { diff --git a/gui/src/shared/daemon-rpc-types.ts b/gui/src/shared/daemon-rpc-types.ts index e24c124f4c..fb1a88cff6 100644 --- a/gui/src/shared/daemon-rpc-types.ts +++ b/gui/src/shared/daemon-rpc-types.ts @@ -352,35 +352,6 @@ export interface IDnsOptions { }; } -export type ProxySettings = - | { local: ILocalProxySettings } - | { remote: IRemoteProxySettings } - | { shadowsocks: IShadowsocksProxySettings }; - -export interface ILocalProxySettings { - localPort: number; - remoteIp: string; - remotePort: number; -} - -export interface IRemoteProxySettings { - ip: string; - port: number; - auth?: IRemoteProxyAuth; -} - -export interface IRemoteProxyAuth { - username: string; - password: string; -} - -export interface IShadowsocksProxySettings { - ip: string; - port: number; - password: string; - cipher: string; -} - export interface IAppVersionInfo { supported: boolean; suggestedUpgrade?: string; @@ -471,7 +442,7 @@ export type BridgeType = 'normal' | 'custom'; export interface BridgeSettings { type: BridgeType; normal: IBridgeConstraints; - custom?: ProxySettings; + custom?: CustomProxy; } export interface ISocketAddress { @@ -488,7 +459,7 @@ export interface SocksAuth { password: string; } -export type Socks5LocalAccessMethod = { +export type Socks5LocalCustomProxy = { type: 'socks5-local'; remoteIp: string; remotePort: number; @@ -496,14 +467,14 @@ export type Socks5LocalAccessMethod = { localPort: number; }; -export type Socks5RemoteAccessMethod = { +export type Socks5RemoteCustomProxy = { type: 'socks5-remote'; ip: string; port: number; authentication?: SocksAuth; }; -export type ShadowsocksAccessMethod = { +export type ShadowsocksCustomProxy = { type: 'shadowsocks'; ip: string; port: number; @@ -511,33 +482,29 @@ export type ShadowsocksAccessMethod = { cipher: string; }; -export type CustomProxy = - | Socks5LocalAccessMethod - | Socks5RemoteAccessMethod - | ShadowsocksAccessMethod; +export type CustomProxy = Socks5LocalCustomProxy | Socks5RemoteCustomProxy | ShadowsocksCustomProxy; +export type NamedCustomProxy = CustomProxy & { name: string }; -export type AccessMethod = - | { - type: 'direct'; - } - | { - type: 'bridges'; - } - | CustomProxy; +export type DirectMethod = { type: 'direct' }; +export type BridgesMethod = { type: 'bridges' }; +export type AccessMethod = DirectMethod | BridgesMethod | CustomProxy; -export type NewAccessMethodSetting = AccessMethod & { - name: string; +export type NamedAccessMethod<T extends AccessMethod> = T & { name: string }; + +export type NewAccessMethodSetting<T extends AccessMethod = AccessMethod> = NamedAccessMethod<T> & { enabled: boolean; }; -export type AccessMethodSetting = NewAccessMethodSetting & { +export type AccessMethodSetting< + T extends AccessMethod = AccessMethod +> = NewAccessMethodSetting<T> & { id: string; }; export type ApiAccessMethodSettings = { - direct: AccessMethodSetting; - mullvadBridges: AccessMethodSetting; - custom: Array<AccessMethodSetting>; + direct: AccessMethodSetting<DirectMethod>; + mullvadBridges: AccessMethodSetting<BridgesMethod>; + custom: Array<AccessMethodSetting<CustomProxy>>; }; export interface RelayOverride { |
