diff options
| author | Oskar Nyberg <oskar@mullvad.net> | 2022-07-26 13:09:07 +0200 |
|---|---|---|
| committer | Oskar Nyberg <oskar@mullvad.net> | 2022-07-26 13:09:07 +0200 |
| commit | af847806058c21cdb72d6fea6fda91230e2e34c1 (patch) | |
| tree | 973f33dd20049a072d0ba3cdc8436d2785a0b945 | |
| parent | c8bd194d7d9ecad52902fd48f0c7dc507d0c3b01 (diff) | |
| parent | e6725d8e5c60c2218c3856143a748a438e4b6756 (diff) | |
| download | mullvadvpn-af847806058c21cdb72d6fea6fda91230e2e34c1.tar.xz mullvadvpn-af847806058c21cdb72d6fea6fda91230e2e34c1.zip | |
Merge branch 'obfuscation-gui'
| -rw-r--r-- | CHANGELOG.md | 2 | ||||
| -rw-r--r-- | gui/locales/messages.pot | 20 | ||||
| -rw-r--r-- | gui/src/main/daemon-rpc.ts | 64 | ||||
| -rw-r--r-- | gui/src/main/index.ts | 10 | ||||
| -rw-r--r-- | gui/src/renderer/app.tsx | 6 | ||||
| -rw-r--r-- | gui/src/renderer/components/ConnectionPanel.tsx | 4 | ||||
| -rw-r--r-- | gui/src/renderer/components/Filter.tsx | 4 | ||||
| -rw-r--r-- | gui/src/renderer/components/SelectLanguage.tsx | 4 | ||||
| -rw-r--r-- | gui/src/renderer/components/WireguardSettings.tsx | 124 | ||||
| -rw-r--r-- | gui/src/renderer/components/cell/Section.tsx | 15 | ||||
| -rw-r--r-- | gui/src/renderer/components/cell/Selector.tsx | 90 | ||||
| -rw-r--r-- | gui/src/renderer/redux/settings/actions.ts | 20 | ||||
| -rw-r--r-- | gui/src/renderer/redux/settings/reducers.ts | 15 | ||||
| -rw-r--r-- | gui/src/shared/daemon-rpc-types.ts | 20 | ||||
| -rw-r--r-- | gui/src/shared/ipc-schema.ts | 2 |
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>(), |
