summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOskar Nyberg <oskar@mullvad.net>2024-04-08 12:10:56 +0200
committerOskar Nyberg <oskar@mullvad.net>2024-04-11 17:21:23 +0200
commitf3221b144564dd70ad0d32354d9451009bb0c14d (patch)
tree509cb654b6a781097c90ad68f07c962ea7d0a2e8
parentd5dc077984ecf64f9fe7ab31cd32d2a3d881aeea (diff)
downloadmullvadvpn-f3221b144564dd70ad0d32354d9451009bb0c14d.tar.xz
mullvadvpn-f3221b144564dd70ad0d32354d9451009bb0c14d.zip
Improve custom proxy daemon rpc code and types
-rw-r--r--gui/src/main/daemon-rpc.ts187
-rw-r--r--gui/src/renderer/redux/settings/reducers.ts4
-rw-r--r--gui/src/shared/daemon-rpc-types.ts69
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 {