summaryrefslogtreecommitdiffhomepage
path: root/gui/src
diff options
context:
space:
mode:
authorOskar Nyberg <oskar@mullvad.net>2022-07-26 13:09:07 +0200
committerOskar Nyberg <oskar@mullvad.net>2022-07-26 13:09:07 +0200
commitaf847806058c21cdb72d6fea6fda91230e2e34c1 (patch)
tree973f33dd20049a072d0ba3cdc8436d2785a0b945 /gui/src
parentc8bd194d7d9ecad52902fd48f0c7dc507d0c3b01 (diff)
parente6725d8e5c60c2218c3856143a748a438e4b6756 (diff)
downloadmullvadvpn-af847806058c21cdb72d6fea6fda91230e2e34c1.tar.xz
mullvadvpn-af847806058c21cdb72d6fea6fda91230e2e34c1.zip
Merge branch 'obfuscation-gui'
Diffstat (limited to 'gui/src')
-rw-r--r--gui/src/main/daemon-rpc.ts64
-rw-r--r--gui/src/main/index.ts10
-rw-r--r--gui/src/renderer/app.tsx6
-rw-r--r--gui/src/renderer/components/ConnectionPanel.tsx4
-rw-r--r--gui/src/renderer/components/Filter.tsx4
-rw-r--r--gui/src/renderer/components/SelectLanguage.tsx4
-rw-r--r--gui/src/renderer/components/WireguardSettings.tsx124
-rw-r--r--gui/src/renderer/components/cell/Section.tsx15
-rw-r--r--gui/src/renderer/components/cell/Selector.tsx90
-rw-r--r--gui/src/renderer/redux/settings/actions.ts20
-rw-r--r--gui/src/renderer/redux/settings/reducers.ts15
-rw-r--r--gui/src/shared/daemon-rpc-types.ts20
-rw-r--r--gui/src/shared/ipc-schema.ts2
13 files changed, 333 insertions, 45 deletions
diff --git a/gui/src/main/daemon-rpc.ts b/gui/src/main/daemon-rpc.ts
index 310a9a5c83..646a09a9c7 100644
--- a/gui/src/main/daemon-rpc.ts
+++ b/gui/src/main/daemon-rpc.ts
@@ -17,6 +17,7 @@ import {
DaemonEvent,
DeviceEvent,
DeviceState,
+ EndpointObfuscationType,
ErrorStateCause,
FirewallPolicyError,
IAccountData,
@@ -40,6 +41,7 @@ import {
IWireguardConstraints,
LoggedInDeviceState,
LoggedOutDeviceState,
+ ObfuscationSettings,
ObfuscationType,
Ownership,
ProxySettings,
@@ -378,6 +380,42 @@ export class DaemonRpc {
);
}
+ public async setObfuscationSettings(obfuscationSettings: ObfuscationSettings): Promise<void> {
+ const grpcObfuscationSettings = new grpcTypes.ObfuscationSettings();
+ switch (obfuscationSettings.selectedObfuscation) {
+ case ObfuscationType.auto:
+ grpcObfuscationSettings.setSelectedObfuscation(
+ grpcTypes.ObfuscationSettings.SelectedObfuscation.AUTO,
+ );
+ break;
+ case ObfuscationType.off:
+ grpcObfuscationSettings.setSelectedObfuscation(
+ grpcTypes.ObfuscationSettings.SelectedObfuscation.OFF,
+ );
+ break;
+ case ObfuscationType.udp2tcp:
+ grpcObfuscationSettings.setSelectedObfuscation(
+ grpcTypes.ObfuscationSettings.SelectedObfuscation.UDP2TCP,
+ );
+ break;
+ }
+
+ if (obfuscationSettings.udp2tcpSettings) {
+ const grpcUdp2tcpSettings = new grpcTypes.Udp2TcpObfuscationSettings();
+ grpcUdp2tcpSettings.setPort(
+ obfuscationSettings.udp2tcpSettings.port === 'any'
+ ? 0
+ : obfuscationSettings.udp2tcpSettings.port.only,
+ );
+ grpcObfuscationSettings.setUdp2tcp(grpcUdp2tcpSettings);
+ }
+
+ await this.call<grpcTypes.ObfuscationSettings, Empty>(
+ this.client.setObfuscationSettings,
+ grpcObfuscationSettings,
+ );
+ }
+
public async setOpenVpnMssfix(mssfix?: number): Promise<void> {
await this.callNumber(this.client.setOpenvpnMssfix, mssfix);
}
@@ -922,7 +960,7 @@ function convertFromProxyEndpoint(proxyEndpoint: grpcTypes.ProxyEndpoint.AsObjec
function convertFromObfuscationEndpoint(
obfuscationEndpoint: grpcTypes.ObfuscationEndpoint.AsObject,
): IObfuscationEndpoint {
- const obfuscationTypes: Record<grpcTypes.ObfuscationType, ObfuscationType> = {
+ const obfuscationTypes: Record<grpcTypes.ObfuscationType, EndpointObfuscationType> = {
[grpcTypes.ObfuscationType.UDP2TCP]: 'udp2tcp',
};
@@ -947,6 +985,7 @@ function convertFromSettings(settings: grpcTypes.Settings): ISettings | undefine
const bridgeSettings = convertFromBridgeSettings(settingsObject.bridgeSettings!);
const tunnelOptions = convertFromTunnelOptions(settingsObject.tunnelOptions!);
const splitTunnel = settingsObject.splitTunnel ?? { enableExclusions: false, appsList: [] };
+ const obfuscationSettings = convertFromObfuscationSettings(settingsObject.obfuscationSettings);
return {
...settings.toObject(),
bridgeState,
@@ -954,6 +993,7 @@ function convertFromSettings(settings: grpcTypes.Settings): ISettings | undefine
bridgeSettings,
tunnelOptions,
splitTunnel,
+ obfuscationSettings,
};
}
@@ -1150,6 +1190,28 @@ function convertFromTunnelOptions(tunnelOptions: grpcTypes.TunnelOptions.AsObjec
};
}
+function convertFromObfuscationSettings(
+ obfuscationSettings?: grpcTypes.ObfuscationSettings.AsObject,
+): ObfuscationSettings {
+ let selectedObfuscationType = ObfuscationType.auto;
+ switch (obfuscationSettings?.selectedObfuscation) {
+ case grpcTypes.ObfuscationSettings.SelectedObfuscation.OFF:
+ selectedObfuscationType = ObfuscationType.off;
+ break;
+ case grpcTypes.ObfuscationSettings.SelectedObfuscation.UDP2TCP:
+ selectedObfuscationType = ObfuscationType.udp2tcp;
+ break;
+ }
+
+ return {
+ selectedObfuscation: selectedObfuscationType,
+ udp2tcpSettings:
+ obfuscationSettings?.udp2tcp && obfuscationSettings.udp2tcp.port !== 0
+ ? { port: { only: obfuscationSettings.udp2tcp.port } }
+ : { port: 'any' },
+ };
+}
+
function convertFromDaemonEvent(data: grpcTypes.DaemonEvent): DaemonEvent {
const tunnelState = data.getTunnelState();
if (tunnelState !== undefined) {
diff --git a/gui/src/main/index.ts b/gui/src/main/index.ts
index 461cac3214..669f406c19 100644
--- a/gui/src/main/index.ts
+++ b/gui/src/main/index.ts
@@ -36,6 +36,7 @@ import {
IRelayList,
ISettings,
liftConstraint,
+ ObfuscationType,
Ownership,
RelaySettings,
RelaySettingsUpdate,
@@ -212,6 +213,12 @@ class ApplicationMain {
},
},
},
+ obfuscationSettings: {
+ selectedObfuscation: ObfuscationType.auto,
+ udp2tcpSettings: {
+ port: 'any',
+ },
+ },
};
private deviceState?: DeviceState;
private guiSettings = new GuiSettings();
@@ -1340,6 +1347,9 @@ class ApplicationMain {
IpcMainEventChannel.autoStart.handleSet((autoStart: boolean) => {
return this.setAutoStart(autoStart);
});
+ IpcMainEventChannel.settings.handleSetObfuscationSettings((obfuscationSettings) => {
+ return this.daemonRpc.setObfuscationSettings(obfuscationSettings);
+ });
IpcMainEventChannel.location.handleGet(() => this.daemonRpc.getLocation());
diff --git a/gui/src/renderer/app.tsx b/gui/src/renderer/app.tsx
index ec69b5d8a7..f8d1796fc1 100644
--- a/gui/src/renderer/app.tsx
+++ b/gui/src/renderer/app.tsx
@@ -17,6 +17,7 @@ import {
ILocation,
ISettings,
liftConstraint,
+ ObfuscationSettings,
RelaySettings,
RelaySettingsUpdate,
TunnelState,
@@ -518,6 +519,10 @@ export default class AppRenderer {
);
}
+ public setObfuscationSettings(obfuscationSettings: ObfuscationSettings) {
+ return IpcRendererEventChannel.settings.setObfuscationSettings(obfuscationSettings);
+ }
+
public collectProblemReport(toRedact?: string): Promise<string> {
return IpcRendererEventChannel.problemReport.collectLogs(toRedact);
}
@@ -812,6 +817,7 @@ export default class AppRenderer {
reduxSettings.updateBridgeState(newSettings.bridgeState);
reduxSettings.updateDnsOptions(newSettings.tunnelOptions.dns);
reduxSettings.updateSplitTunnelingState(newSettings.splitTunnel.enableExclusions);
+ reduxSettings.updateObfuscationSettings(newSettings.obfuscationSettings);
this.setRelaySettings(newSettings.relaySettings);
this.setBridgeSettings(newSettings.bridgeSettings);
diff --git a/gui/src/renderer/components/ConnectionPanel.tsx b/gui/src/renderer/components/ConnectionPanel.tsx
index 7f4c28286b..552a9e3187 100644
--- a/gui/src/renderer/components/ConnectionPanel.tsx
+++ b/gui/src/renderer/components/ConnectionPanel.tsx
@@ -4,7 +4,7 @@ import styled from 'styled-components';
import { colors } from '../../config.json';
import {
- ObfuscationType,
+ EndpointObfuscationType,
ProxyType,
proxyTypeToString,
RelayProtocol,
@@ -31,7 +31,7 @@ export interface IBridgeData extends IEndpoint {
}
export interface IObfuscationData extends IEndpoint {
- obfuscationType: ObfuscationType;
+ obfuscationType: EndpointObfuscationType;
}
export interface IOutAddress {
diff --git a/gui/src/renderer/components/Filter.tsx b/gui/src/renderer/components/Filter.tsx
index 86e0d2dbb3..79253ed7b0 100644
--- a/gui/src/renderer/components/Filter.tsx
+++ b/gui/src/renderer/components/Filter.tsx
@@ -174,9 +174,9 @@ function providersSelector(state: IReduxState): Record<string, boolean> {
);
}
-const StyledSelector = (styled(Selector)({
+const StyledSelector = styled(Selector)({
marginBottom: 0,
-}) as unknown) as new <T>() => Selector<T>;
+}) as typeof Selector;
interface IFilterByOwnershipProps {
ownership: Ownership;
diff --git a/gui/src/renderer/components/SelectLanguage.tsx b/gui/src/renderer/components/SelectLanguage.tsx
index 86d0c8166f..419557f700 100644
--- a/gui/src/renderer/components/SelectLanguage.tsx
+++ b/gui/src/renderer/components/SelectLanguage.tsx
@@ -31,9 +31,9 @@ const StyledNavigationScrollbars = styled(NavigationScrollbars)({
flex: 1,
});
-const StyledSelector = (styled(Selector)({
+const StyledSelector = styled(Selector)({
marginBottom: 0,
-}) as unknown) as new <T>() => Selector<T>;
+}) as typeof Selector;
export default class SelectLanguage extends React.Component<IProps, IState> {
private scrollView = React.createRef<CustomScrollbarsRef>();
diff --git a/gui/src/renderer/components/WireguardSettings.tsx b/gui/src/renderer/components/WireguardSettings.tsx
index a4c7a11e08..222e845001 100644
--- a/gui/src/renderer/components/WireguardSettings.tsx
+++ b/gui/src/renderer/components/WireguardSettings.tsx
@@ -3,7 +3,12 @@ import { sprintf } from 'sprintf-js';
import styled from 'styled-components';
import { strings } from '../../config.json';
-import { IpVersion } from '../../shared/daemon-rpc-types';
+import {
+ IpVersion,
+ liftConstraint,
+ LiftedConstraint,
+ ObfuscationType,
+} from '../../shared/daemon-rpc-types';
import { messages } from '../../shared/gettext';
import log from '../../shared/logging';
import { useAppContext } from '../context';
@@ -18,7 +23,7 @@ import Selector, { ISelectorItem } from './cell/Selector';
import { InfoIcon } from './InfoButton';
import { BackAction } from './KeyboardNavigation';
import { Layout, SettingsContainer } from './Layout';
-import { ModalAlert, ModalAlertType } from './Modal';
+import { ModalAlert, ModalAlertType, ModalMessage } from './Modal';
import {
NavigationBar,
NavigationContainer,
@@ -31,6 +36,7 @@ import SettingsHeader, { HeaderTitle } from './SettingsHeader';
const MIN_WIREGUARD_MTU_VALUE = 1280;
const MAX_WIREGUARD_MTU_VALUE = 1420;
const WIREUGARD_UDP_PORTS = [51820, 53];
+const UDP2TCP_PORTS = [80, 443, 5001];
type OptionalPort = number | undefined;
type OptionalIpVersion = IpVersion | undefined;
@@ -98,6 +104,11 @@ export default function WireguardSettings() {
</Cell.Group>
<Cell.Group>
+ <ObfuscationSettings />
+ <Udp2tcpPortSetting />
+ </Cell.Group>
+
+ <Cell.Group>
<MultihopSetting />
</Cell.Group>
@@ -184,6 +195,115 @@ function PortSelector() {
);
}
+function ObfuscationSettings() {
+ const { setObfuscationSettings } = useAppContext();
+ const obfuscationSettings = useSelector((state) => state.settings.obfuscationSettings);
+
+ const obfuscationType = obfuscationSettings.selectedObfuscation;
+ const obfuscationTypeItems: ISelectorItem<ObfuscationType>[] = useMemo(
+ () => [
+ {
+ label: messages.gettext('Automatic'),
+ value: ObfuscationType.auto,
+ },
+ {
+ label: messages.pgettext('wireguard-settings-view', 'On (UDP-over-TCP)'),
+ value: ObfuscationType.udp2tcp,
+ },
+ {
+ label: messages.gettext('Off'),
+ value: ObfuscationType.off,
+ },
+ ],
+ [],
+ );
+
+ const selectObfuscationType = useCallback(
+ async (value: ObfuscationType) => {
+ await setObfuscationSettings({
+ ...obfuscationSettings,
+ selectedObfuscation: value,
+ });
+ },
+ [setObfuscationSettings, obfuscationSettings],
+ );
+
+ return (
+ <AriaInputGroup>
+ <StyledSelectorContainer>
+ <Selector
+ // TRANSLATORS: The title for the WireGuard obfuscation selector.
+ title={messages.pgettext('wireguard-settings-view', 'Obfuscation')}
+ details={
+ <ModalMessage>
+ {messages.pgettext(
+ 'wireguard-settings-view',
+ 'Obfuscation hides the WireGuard traffic inside another protocol. It can be used to help circumvent censorship and other types of filtering, where a plain WireGuard connect would be blocked.',
+ )}
+ </ModalMessage>
+ }
+ values={obfuscationTypeItems}
+ value={obfuscationType}
+ onSelect={selectObfuscationType}
+ />
+ </StyledSelectorContainer>
+ </AriaInputGroup>
+ );
+}
+
+function Udp2tcpPortSetting() {
+ const { setObfuscationSettings } = useAppContext();
+ const obfuscationSettings = useSelector((state) => state.settings.obfuscationSettings);
+
+ const port = liftConstraint(obfuscationSettings.udp2tcpSettings.port);
+ const portItems: ISelectorItem<LiftedConstraint<number>>[] = useMemo(() => {
+ const automaticItem: ISelectorItem<LiftedConstraint<number>> = {
+ label: messages.gettext('Automatic'),
+ value: 'any',
+ };
+
+ return [automaticItem].concat(UDP2TCP_PORTS.map(mapPortToSelectorItem));
+ }, []);
+
+ const selectPort = useCallback(
+ async (port: LiftedConstraint<number>) => {
+ await setObfuscationSettings({
+ ...obfuscationSettings,
+ udp2tcpSettings: {
+ ...obfuscationSettings.udp2tcpSettings,
+ port: port === 'any' ? 'any' : { only: port },
+ },
+ });
+ },
+ [setObfuscationSettings, obfuscationSettings],
+ );
+
+ return (
+ <AriaInputGroup>
+ <StyledSelectorContainer>
+ <Selector
+ // TRANSLATORS: The title for the UDP-over-TCP port selector.
+ title={messages.pgettext('wireguard-settings-view', 'UDP-over-TCP port')}
+ details={
+ <ModalMessage>
+ {messages.pgettext(
+ 'wireguard-settings-view',
+ 'Which TCP port the UDP-over-TCP obfuscation protocol should connect to on the VPN server.',
+ )}
+ </ModalMessage>
+ }
+ values={portItems}
+ value={port}
+ onSelect={selectPort}
+ disabled={obfuscationSettings.selectedObfuscation === ObfuscationType.off}
+ expandable
+ thinTitle
+ />
+ </StyledSelectorContainer>
+ </AriaInputGroup>
+ );
+}
+
function MultihopSetting() {
const relaySettings = useSelector((state) => state.settings.relaySettings);
const { updateRelaySettings } = useAppContext();
diff --git a/gui/src/renderer/components/cell/Section.tsx b/gui/src/renderer/components/cell/Section.tsx
index 1c1779248f..83c179d3cc 100644
--- a/gui/src/renderer/components/cell/Section.tsx
+++ b/gui/src/renderer/components/cell/Section.tsx
@@ -2,20 +2,29 @@ import React from 'react';
import styled from 'styled-components';
import { colors } from '../../../config.json';
-import { buttonText } from '../common-styles';
+import { buttonText, openSans, sourceSansPro } from '../common-styles';
const StyledSection = styled.div({
display: 'flex',
flexDirection: 'column',
});
-export const SectionTitle = styled.span(buttonText, {
+interface SectionTitleProps {
+ disabled?: boolean;
+ thin?: boolean;
+}
+
+export const SectionTitle = styled.span(buttonText, (props: SectionTitleProps) => ({
display: 'flex',
minHeight: '44px',
alignItems: 'center',
backgroundColor: colors.blue,
padding: '0 16px 0 22px',
-});
+ color: props.disabled ? colors.white20 : colors.white,
+ fontWeight: props.thin ? 400 : 600,
+ fontSize: props.thin ? '15px' : '18px',
+ ...(props.thin ? openSans : sourceSansPro),
+}));
export const CellSectionContext = React.createContext<boolean>(false);
diff --git a/gui/src/renderer/components/cell/Selector.tsx b/gui/src/renderer/components/cell/Selector.tsx
index ea2abb90ef..e815519552 100644
--- a/gui/src/renderer/components/cell/Selector.tsx
+++ b/gui/src/renderer/components/cell/Selector.tsx
@@ -2,10 +2,28 @@ import * as React from 'react';
import styled from 'styled-components';
import { colors } from '../../../config.json';
-import { AriaInput, AriaLabel } from '../AriaGroup';
+import { useBoolean } from '../../lib/utilityHooks';
+import Accordion from '../Accordion';
+import { AriaDetails, AriaInput, AriaLabel } from '../AriaGroup';
+import ChevronButton from '../ChevronButton';
import { normalText } from '../common-styles';
+import InfoButton from '../InfoButton';
import * as Cell from '.';
+const StyledTitle = styled(Cell.Container)({
+ display: 'flex',
+ padding: 0,
+});
+
+const StyledTitleLabel = styled(Cell.SectionTitle)({
+ flex: 1,
+});
+
+const StyledChevronButton = styled(ChevronButton)({
+ padding: 0,
+ marginRight: '16px',
+});
+
export interface ISelectorItem<T> {
label: string;
value: T;
@@ -19,41 +37,55 @@ interface ISelectorProps<T> {
onSelect: (value: T) => void;
selectedCellRef?: React.Ref<HTMLButtonElement>;
className?: string;
+ details?: React.ReactElement;
+ expandable?: boolean;
+ disabled?: boolean;
+ thinTitle?: boolean;
}
-export default class Selector<T> extends React.Component<ISelectorProps<T>> {
- public render() {
- const items = this.props.values.map((item, i) => {
- const selected = item.value === this.props.value;
+export default function Selector<T>(props: ISelectorProps<T>) {
+ const [expanded, , , toggleExpanded] = useBoolean(!props.expandable);
- return (
- <SelectorCell
- key={i}
- value={item.value}
- selected={selected}
- disabled={item.disabled}
- forwardedRef={selected ? this.props.selectedCellRef : undefined}
- onSelect={this.props.onSelect}>
- {item.label}
- </SelectorCell>
- );
- });
+ const items = props.values.map((item, i) => {
+ const selected = item.value === props.value;
- const title = this.props.title && (
+ return (
+ <SelectorCell
+ key={i}
+ value={item.value}
+ selected={selected}
+ disabled={props.disabled || item.disabled}
+ forwardedRef={selected ? props.selectedCellRef : undefined}
+ onSelect={props.onSelect}>
+ {item.label}
+ </SelectorCell>
+ );
+ });
+
+ const title = props.title && (
+ <StyledTitle>
<AriaLabel>
- <Cell.SectionTitle as="label">{this.props.title}</Cell.SectionTitle>
+ <StyledTitleLabel as="label" disabled={props.disabled} thin={props.thinTitle}>
+ {props.title}
+ </StyledTitleLabel>
</AriaLabel>
- );
+ {props.details && (
+ <AriaDetails>
+ <InfoButton>{props.details}</InfoButton>
+ </AriaDetails>
+ )}
+ {props.expandable && <StyledChevronButton up={expanded} onClick={toggleExpanded} />}
+ </StyledTitle>
+ );
- return (
- <AriaInput>
- <Cell.Section role="listbox" className={this.props.className}>
- {title}
- {items}
- </Cell.Section>
- </AriaInput>
- );
- }
+ return (
+ <AriaInput>
+ <Cell.Section role="listbox" className={props.className}>
+ {title}
+ {props.expandable ? <Accordion expanded={expanded}>{items}</Accordion> : items}
+ </Cell.Section>
+ </AriaInput>
+ );
}
const StyledCellIcon = styled(Cell.Icon)((props: { visible: boolean }) => ({
diff --git a/gui/src/renderer/redux/settings/actions.ts b/gui/src/renderer/redux/settings/actions.ts
index 9e12df542d..8126f30205 100644
--- a/gui/src/renderer/redux/settings/actions.ts
+++ b/gui/src/renderer/redux/settings/actions.ts
@@ -1,5 +1,5 @@
import { IWindowsApplication } from '../../../shared/application-types';
-import { BridgeState, IDnsOptions } from '../../../shared/daemon-rpc-types';
+import { BridgeState, IDnsOptions, ObfuscationSettings } from '../../../shared/daemon-rpc-types';
import { IGuiSettingsState } from '../../../shared/gui-settings-state';
import { BridgeSettingsRedux, IRelayLocationRedux, RelaySettingsRedux } from './reducers';
@@ -83,6 +83,11 @@ export interface ISetSplitTunnelingApplicationsAction {
applications: IWindowsApplication[];
}
+export interface ISetObfuscationSettings {
+ type: 'SET_OBFUSCATION_SETTINGS';
+ obfuscationSettings: ObfuscationSettings;
+}
+
export type SettingsAction =
| IUpdateGuiSettingsAction
| IUpdateRelayAction
@@ -99,7 +104,8 @@ export type SettingsAction =
| IUpdateAutoStartAction
| IUpdateDnsOptionsAction
| IUpdateSplitTunnelingStateAction
- | ISetSplitTunnelingApplicationsAction;
+ | ISetSplitTunnelingApplicationsAction
+ | ISetObfuscationSettings;
function updateGuiSettings(guiSettings: IGuiSettingsState): IUpdateGuiSettingsAction {
return {
@@ -219,6 +225,15 @@ function setSplitTunnelingApplications(
};
}
+function updateObfuscationSettings(
+ obfuscationSettings: ObfuscationSettings,
+): ISetObfuscationSettings {
+ return {
+ type: 'SET_OBFUSCATION_SETTINGS',
+ obfuscationSettings,
+ };
+}
+
export default {
updateGuiSettings,
updateRelay,
@@ -236,4 +251,5 @@ export default {
updateDnsOptions,
updateSplitTunnelingState,
setSplitTunnelingApplications,
+ updateObfuscationSettings,
};
diff --git a/gui/src/renderer/redux/settings/reducers.ts b/gui/src/renderer/redux/settings/reducers.ts
index 8e4c922192..ee9d81e37f 100644
--- a/gui/src/renderer/redux/settings/reducers.ts
+++ b/gui/src/renderer/redux/settings/reducers.ts
@@ -4,6 +4,8 @@ import {
IDnsOptions,
IpVersion,
LiftedConstraint,
+ ObfuscationSettings,
+ ObfuscationType,
Ownership,
ProxySettings,
RelayLocation,
@@ -95,6 +97,7 @@ export interface ISettingsReduxState {
dns: IDnsOptions;
splitTunneling: boolean;
splitTunnelingApplications: IWindowsApplication[];
+ obfuscationSettings: ObfuscationSettings;
}
const initialState: ISettingsReduxState = {
@@ -151,6 +154,12 @@ const initialState: ISettingsReduxState = {
},
splitTunneling: false,
splitTunnelingApplications: [],
+ obfuscationSettings: {
+ selectedObfuscation: ObfuscationType.auto,
+ udp2tcpSettings: {
+ port: 'any',
+ },
+ },
};
export default function (
@@ -260,6 +269,12 @@ export default function (
splitTunnelingApplications: action.applications,
};
+ case 'SET_OBFUSCATION_SETTINGS':
+ return {
+ ...state,
+ obfuscationSettings: action.obfuscationSettings,
+ };
+
default:
return state;
}
diff --git a/gui/src/shared/daemon-rpc-types.ts b/gui/src/shared/daemon-rpc-types.ts
index d0507cab98..5893e4def6 100644
--- a/gui/src/shared/daemon-rpc-types.ts
+++ b/gui/src/shared/daemon-rpc-types.ts
@@ -61,7 +61,7 @@ export function tunnelTypeToString(tunnel: TunnelType): string {
}
export type RelayProtocol = 'tcp' | 'udp';
-export type ObfuscationType = 'udp2tcp';
+export type EndpointObfuscationType = 'udp2tcp';
export type Constraint<T> = 'any' | { only: T };
export type LiftedConstraint<T> = 'any' | T;
@@ -107,7 +107,7 @@ export interface IObfuscationEndpoint {
address: string;
port: number;
protocol: RelayProtocol;
- obfuscationType: ObfuscationType;
+ obfuscationType: EndpointObfuscationType;
}
export interface IProxyEndpoint {
@@ -347,6 +347,7 @@ export interface ISettings {
bridgeSettings: BridgeSettings;
bridgeState: BridgeState;
splitTunnel: SplitTunnelSettings;
+ obfuscationSettings: ObfuscationSettings;
}
export type BridgeState = 'auto' | 'on' | 'off';
@@ -356,6 +357,21 @@ export type SplitTunnelSettings = {
appsList: string[];
};
+export type Udp2TcpObfuscationSettings = {
+ port: Constraint<number>;
+};
+
+export enum ObfuscationType {
+ auto,
+ off,
+ udp2tcp,
+}
+
+export type ObfuscationSettings = {
+ selectedObfuscation: ObfuscationType;
+ udp2tcpSettings: Udp2TcpObfuscationSettings;
+};
+
export interface IBridgeConstraints {
location: Constraint<RelayLocation>;
providers: string[];
diff --git a/gui/src/shared/ipc-schema.ts b/gui/src/shared/ipc-schema.ts
index 8dece5168d..33ac82180e 100644
--- a/gui/src/shared/ipc-schema.ts
+++ b/gui/src/shared/ipc-schema.ts
@@ -15,6 +15,7 @@ import {
ILocation,
IRelayList,
ISettings,
+ ObfuscationSettings,
RelaySettingsUpdate,
TunnelState,
VoucherResponse,
@@ -170,6 +171,7 @@ export const ipcSchema = {
updateRelaySettings: invoke<RelaySettingsUpdate, void>(),
updateBridgeSettings: invoke<BridgeSettings, void>(),
setDnsOptions: invoke<IDnsOptions, void>(),
+ setObfuscationSettings: invoke<ObfuscationSettings, void>(),
},
guiSettings: {
'': notifyRenderer<IGuiSettingsState>(),