summaryrefslogtreecommitdiffhomepage
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
parentc8bd194d7d9ecad52902fd48f0c7dc507d0c3b01 (diff)
parente6725d8e5c60c2218c3856143a748a438e4b6756 (diff)
downloadmullvadvpn-af847806058c21cdb72d6fea6fda91230e2e34c1.tar.xz
mullvadvpn-af847806058c21cdb72d6fea6fda91230e2e34c1.zip
Merge branch 'obfuscation-gui'
-rw-r--r--CHANGELOG.md2
-rw-r--r--gui/locales/messages.pot20
-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
15 files changed, 355 insertions, 45 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e7716c2ebc..59973018ea 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,6 +24,8 @@ Line wrap the file at 100 chars. Th
## [Unreleased]
### Added
+- Add obfuscation settings under "WireGuard settings".
+
#### Android
- Add device management to the Android app. This simplifies knowing which device is which and adds
the option to log other devices out when the account already has five devices.
diff --git a/gui/locales/messages.pot b/gui/locales/messages.pot
index b9cb6d4f98..8f1fd9448a 100644
--- a/gui/locales/messages.pot
+++ b/gui/locales/messages.pot
@@ -1483,6 +1483,18 @@ msgid "MTU"
msgstr ""
msgctxt "wireguard-settings-view"
+msgid "Obfuscation"
+msgstr ""
+
+msgctxt "wireguard-settings-view"
+msgid "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."
+msgstr ""
+
+msgctxt "wireguard-settings-view"
+msgid "On (UDP-over-TCP)"
+msgstr ""
+
+msgctxt "wireguard-settings-view"
msgid "Port"
msgstr ""
@@ -1507,6 +1519,14 @@ msgctxt "wireguard-settings-view"
msgid "This allows access to %(wireguard)s for devices that only support IPv6."
msgstr ""
+msgctxt "wireguard-settings-view"
+msgid "UDP-over-TCP port"
+msgstr ""
+
+msgctxt "wireguard-settings-view"
+msgid "Which TCP port the UDP-over-TCP obfuscation protocol should connect to on the VPN server."
+msgstr ""
+
msgid "Account authentication failed."
msgstr ""
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>(),