diff options
| author | David Lönnhager <david.l@mullvad.net> | 2024-04-16 14:47:34 +0200 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2024-04-16 14:47:34 +0200 |
| commit | 58dadaae40815729b6d279b7187751f1519093fb (patch) | |
| tree | 55fada0bc30320897c9fdfa6b3dcdc7c96cafdc0 /gui/src | |
| parent | b9a59074141024a9d40723dca5c58cfe15fccbbc (diff) | |
| parent | 1832ccc05c4f1898d36b2647e254229c821a4674 (diff) | |
| download | mullvadvpn-58dadaae40815729b6d279b7187751f1519093fb.tar.xz mullvadvpn-58dadaae40815729b6d279b7187751f1519093fb.zip | |
Merge branch 'add-wg-nt-daita'
Diffstat (limited to 'gui/src')
| -rw-r--r-- | gui/src/config.json | 4 | ||||
| -rw-r--r-- | gui/src/main/daemon-rpc.ts | 48 | ||||
| -rw-r--r-- | gui/src/main/settings.ts | 3 | ||||
| -rw-r--r-- | gui/src/renderer/app.tsx | 4 | ||||
| -rw-r--r-- | gui/src/renderer/components/BetaLabel.tsx | 26 | ||||
| -rw-r--r-- | gui/src/renderer/components/ConnectionPanel.tsx | 33 | ||||
| -rw-r--r-- | gui/src/renderer/components/WireguardSettings.tsx | 110 | ||||
| -rw-r--r-- | gui/src/renderer/components/YellowLabel.tsx | 18 | ||||
| -rw-r--r-- | gui/src/renderer/components/cell/Label.tsx | 2 | ||||
| -rw-r--r-- | gui/src/renderer/components/select-location/RelayListContext.tsx | 20 | ||||
| -rw-r--r-- | gui/src/renderer/components/select-location/SelectLocation.tsx | 20 | ||||
| -rw-r--r-- | gui/src/renderer/containers/ConnectionPanelContainer.tsx | 6 | ||||
| -rw-r--r-- | gui/src/renderer/lib/filter-locations.ts | 33 | ||||
| -rw-r--r-- | gui/src/renderer/redux/settings/actions.ts | 15 | ||||
| -rw-r--r-- | gui/src/renderer/redux/settings/reducers.ts | 11 | ||||
| -rw-r--r-- | gui/src/shared/daemon-rpc-types.ts | 7 | ||||
| -rw-r--r-- | gui/src/shared/ipc-schema.ts | 2 |
17 files changed, 295 insertions, 67 deletions
diff --git a/gui/src/config.json b/gui/src/config.json index c1ddcd6cac..a77c47de45 100644 --- a/gui/src/config.json +++ b/gui/src/config.json @@ -41,6 +41,8 @@ "strings": { "wireguard": "WireGuard", "openvpn": "OpenVPN", - "splitTunneling": "Split tunneling" + "splitTunneling": "Split tunneling", + "daita": "DAITA", + "daitaFull": "Defence against AI-guided Traffic Analysis" } } diff --git a/gui/src/main/daemon-rpc.ts b/gui/src/main/daemon-rpc.ts index 531ec80ced..ba913fea87 100644 --- a/gui/src/main/daemon-rpc.ts +++ b/gui/src/main/daemon-rpc.ts @@ -37,6 +37,7 @@ import { IAppVersionInfo, IBridgeConstraints, ICustomList, + IDaitaSettings, IDevice, IDeviceRemoval, IDnsOptions, @@ -561,6 +562,15 @@ export class DaemonRpc { await this.callEmpty(this.client.updateDevice); } + public async setDaitaSettings(daitaSettings: IDaitaSettings): Promise<void> { + const grpcDaitaSettings = new grpcTypes.DaitaSettings(); + grpcDaitaSettings.setEnabled(daitaSettings.enabled); + await this.call<grpcTypes.DaitaSettings, Empty>( + this.client.setDaitaSettings, + grpcDaitaSettings, + ); + } + public async listDevices(accountToken: AccountToken): Promise<Array<IDevice>> { try { const response = await this.callString<grpcTypes.DeviceList>( @@ -847,9 +857,7 @@ function convertFromRelayList(relayList: grpcTypes.RelayList): IRelayListWithEnd relayList: { countries: relayList .getCountriesList() - .map((country: grpcTypes.RelayListCountry) => - convertFromRelayListCountry(country.toObject()), - ), + .map((country: grpcTypes.RelayListCountry) => convertFromRelayListCountry(country)), }, wireguardEndpointData: convertWireguardEndpointData(relayList.getWireguard()!), }; @@ -864,26 +872,37 @@ function convertWireguardEndpointData( }; } -function convertFromRelayListCountry( - country: grpcTypes.RelayListCountry.AsObject, -): IRelayListCountry { +function convertFromRelayListCountry(country: grpcTypes.RelayListCountry): IRelayListCountry { + const countryObject = country.toObject(); return { - ...country, - cities: country.citiesList.map(convertFromRelayListCity), + ...countryObject, + cities: country.getCitiesList().map(convertFromRelayListCity), }; } -function convertFromRelayListCity(city: grpcTypes.RelayListCity.AsObject): IRelayListCity { +function convertFromRelayListCity(city: grpcTypes.RelayListCity): IRelayListCity { + const cityObject = city.toObject(); return { - ...city, - relays: city.relaysList.map(convertFromRelayListRelay), + ...cityObject, + relays: city.getRelaysList().map(convertFromRelayListRelay), }; } -function convertFromRelayListRelay(relay: grpcTypes.Relay.AsObject): IRelayListHostname { +function convertFromRelayListRelay(relay: grpcTypes.Relay): IRelayListHostname { + const relayObject = relay.toObject(); + + let daita = false; + if (relayObject.endpointType === grpcTypes.Relay.RelayType.WIREGUARD) { + const endpointDataU8 = relay.getEndpointData()?.getValue_asU8(); + if (endpointDataU8) { + daita = grpcTypes.WireguardRelayEndpointData.deserializeBinary(endpointDataU8).getDaita(); + } + } + return { - ...relay, - endpointType: convertFromRelayType(relay.endpointType), + ...relayObject, + endpointType: convertFromRelayType(relayObject.endpointType), + daita, }; } @@ -1333,6 +1352,7 @@ function convertFromTunnelOptions(tunnelOptions: grpcTypes.TunnelOptions.AsObjec quantumResistant: convertFromQuantumResistantState( tunnelOptions.wireguard?.quantumResistant?.state, ), + daita: tunnelOptions.wireguard!.daita, }, generic: { enableIpv6: tunnelOptions.generic!.enableIpv6, diff --git a/gui/src/main/settings.ts b/gui/src/main/settings.ts index 8bf60d9108..22238c72c4 100644 --- a/gui/src/main/settings.ts +++ b/gui/src/main/settings.ts @@ -107,6 +107,9 @@ export default class Settings implements Readonly<ISettings> { const settings = await fs.readFile(path); return this.daemonRpc.applyJsonSettings(settings.toString()); }); + IpcMainEventChannel.settings.handleSetDaitaSettings((daitaSettings) => { + return this.daemonRpc.setDaitaSettings(daitaSettings); + }); IpcMainEventChannel.guiSettings.handleSetEnableSystemNotifications((flag: boolean) => { this.guiSettings.enableSystemNotifications = flag; diff --git a/gui/src/renderer/app.tsx b/gui/src/renderer/app.tsx index a8c1901ae8..92c4cb5698 100644 --- a/gui/src/renderer/app.tsx +++ b/gui/src/renderer/app.tsx @@ -16,6 +16,7 @@ import { IAccountData, IAppVersionInfo, ICustomList, + IDaitaSettings, IDevice, IDeviceRemoval, IDnsOptions, @@ -340,6 +341,8 @@ export default class AppRenderer { IpcRendererEventChannel.windowsSplitTunneling.forgetManuallyAddedApplication(application); public setObfuscationSettings = (obfuscationSettings: ObfuscationSettings) => IpcRendererEventChannel.settings.setObfuscationSettings(obfuscationSettings); + public setDaitaSettings = (daitaSettings: IDaitaSettings) => + IpcRendererEventChannel.settings.setDaitaSettings(daitaSettings); public collectProblemReport = (toRedact: string | undefined) => IpcRendererEventChannel.problemReport.collectLogs(toRedact); public viewLog = (path: string) => IpcRendererEventChannel.problemReport.viewLog(path); @@ -812,6 +815,7 @@ export default class AppRenderer { reduxSettings.updateWireguardQuantumResistant( newSettings.tunnelOptions.wireguard.quantumResistant, ); + reduxSettings.updateWireguardDaita(newSettings.tunnelOptions.wireguard.daita); reduxSettings.updateBridgeState(newSettings.bridgeState); reduxSettings.updateDnsOptions(newSettings.tunnelOptions.dns); reduxSettings.updateSplitTunnelingState(newSettings.splitTunnel.enableExclusions); diff --git a/gui/src/renderer/components/BetaLabel.tsx b/gui/src/renderer/components/BetaLabel.tsx deleted file mode 100644 index ca19a9ef4b..0000000000 --- a/gui/src/renderer/components/BetaLabel.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import styled from 'styled-components'; - -import { colors } from '../../config.json'; -import { messages } from '../../shared/gettext'; - -const StyledBetaLabel = styled.span({ - display: 'inline-block', - fontFamily: 'Open Sans', - color: colors.blue, - fontSize: '13px', - fontWeight: 800, - lineHeight: '20px', - padding: '2px 0', - background: colors.yellow, - borderRadius: '5px', - width: '50px', - textAlign: 'center', -}); - -interface IBetaLabelProps { - className?: string; -} - -export default function BetaLabel(props: IBetaLabelProps) { - return <StyledBetaLabel {...props}>{messages.gettext('BETA')}</StyledBetaLabel>; -} diff --git a/gui/src/renderer/components/ConnectionPanel.tsx b/gui/src/renderer/components/ConnectionPanel.tsx index a99140bb43..4c709b7204 100644 --- a/gui/src/renderer/components/ConnectionPanel.tsx +++ b/gui/src/renderer/components/ConnectionPanel.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { sprintf } from 'sprintf-js'; import styled from 'styled-components'; -import { colors } from '../../config.json'; +import { colors, strings } from '../../config.json'; import { EndpointObfuscationType, ProxyType, @@ -48,6 +48,7 @@ interface IProps { bridgeInfo?: IBridgeData; outAddress?: IOutAddress; obfuscationEndpoint?: IObfuscationData; + daita: boolean; onToggle: () => void; className?: string; } @@ -146,13 +147,15 @@ export default class ConnectionPanel extends React.Component<IProps> { } private hostnameLine() { + let hostname = ''; + if (this.props.hostname && this.props.bridgeHostname) { - return sprintf(messages.pgettext('connection-info', '%(relay)s via %(entry)s'), { + hostname = sprintf(messages.pgettext('connection-info', '%(relay)s via %(entry)s'), { relay: this.props.hostname, entry: this.props.bridgeHostname, }); } else if (this.props.hostname && this.props.entryHostname) { - return sprintf( + hostname = sprintf( // TRANSLATORS: The hostname line displayed below the country on the main screen // TRANSLATORS: Available placeholders: // TRANSLATORS: %(relay)s - the relay hostname @@ -163,13 +166,31 @@ export default class ConnectionPanel extends React.Component<IProps> { entry: this.props.entryHostname, }, ); + } else if (this.props.bridgeInfo?.ip) { + hostname = sprintf(messages.pgettext('connection-info', '%(relay)s via %(entry)s'), { + relay: this.props.hostname, + }); } else if (this.props.bridgeInfo !== undefined) { - return sprintf(messages.pgettext('connection-info', '%(relay)s via Custom bridge'), { + hostname = sprintf(messages.pgettext('connection-info', '%(relay)s via Custom bridge'), { relay: this.props.hostname, }); - } else { - return this.props.hostname || ''; + } else if (this.props.hostname) { + hostname = this.props.hostname; + } + + if (hostname !== '' && this.props.daita) { + hostname = sprintf( + // TRANSLATORS: %(hostname)s - The current server the app is connected to, e.g. "se-got-wg-001 using DAITA" + // TRANSLATORS: %(daita)s - Will be replaced with "DAITA" + messages.pgettext('connection-info', '%(hostname)s using %(daita)s'), + { + hostname, + daita: strings.daita, + }, + ); } + + return hostname; } private transportLine() { diff --git a/gui/src/renderer/components/WireguardSettings.tsx b/gui/src/renderer/components/WireguardSettings.tsx index 7cd93b98bd..d315485659 100644 --- a/gui/src/renderer/components/WireguardSettings.tsx +++ b/gui/src/renderer/components/WireguardSettings.tsx @@ -22,7 +22,7 @@ import * as AppButton from './AppButton'; import { AriaDescription, AriaInput, AriaInputGroup, AriaLabel } from './AriaGroup'; import * as Cell from './cell'; import Selector, { SelectorItem, SelectorWithCustomItem } from './cell/Selector'; -import { InfoIcon } from './InfoButton'; +import InfoButton from './InfoButton'; import { BackAction } from './KeyboardNavigation'; import { Layout, SettingsContainer } from './Layout'; import { ModalAlert, ModalAlertType, ModalMessage } from './Modal'; @@ -34,6 +34,7 @@ import { TitleBarItem, } from './NavigationBar'; import SettingsHeader, { HeaderTitle } from './SettingsHeader'; +import YellowLabel from './YellowLabel'; const MIN_WIREGUARD_MTU_VALUE = 1280; const MAX_WIREGUARD_MTU_VALUE = 1420; @@ -44,22 +45,14 @@ function mapPortToSelectorItem(value: number): SelectorItem<number> { return { label: value.toString(), value }; } -export const StyledContent = styled.div({ +const StyledContent = styled.div({ display: 'flex', flexDirection: 'column', flex: 1, marginBottom: '2px', }); -export const StyledCellIcon = styled(Cell.UntintedIcon)({ - marginRight: '8px', -}); - -export const StyledInfoIcon = styled(InfoIcon)({ - marginRight: '16px', -}); - -export const StyledSelectorContainer = styled.div({ +const StyledSelectorContainer = styled.div({ flex: 0, }); @@ -107,6 +100,12 @@ export default function WireguardSettings() { <Udp2tcpPortSetting /> </Cell.Group> + {window.env.platform === 'win32' && ( + <Cell.Group> + <DaitaSettings /> + </Cell.Group> + )} + <Cell.Group> <QuantumResistantSetting /> </Cell.Group> @@ -558,6 +557,95 @@ function MtuSetting() { ); } +function DaitaSettings() { + const { setDaitaSettings } = useAppContext(); + const daita = useSelector((state) => state.settings.wireguard.daita?.enabled ?? false); + + const [confirmationDialogVisible, showConfirmationDialog, hideConfirmationDialog] = useBoolean(); + + const setDaita = useCallback((value: boolean) => { + if (value) { + showConfirmationDialog(); + } else { + void setDaitaSettings({ enabled: value }); + } + }, []); + + const confirmDaita = useCallback(() => { + void setDaitaSettings({ enabled: true }); + hideConfirmationDialog(); + }, []); + + return ( + <> + <AriaInputGroup> + <Cell.Container> + <AriaLabel> + <Cell.InputLabel> + {strings.daita} + <YellowLabel>{messages.gettext('BETA')}</YellowLabel> + </Cell.InputLabel> + </AriaLabel> + <InfoButton> + <ModalMessage> + {sprintf( + messages.pgettext( + 'wireguard-settings-view', + '%(daita)s (%(daitaFull)s) hides patterns in your encrypted VPN traffic. If anyone is monitoring your connection, this makes it significantly harder for them to identify what websites you are visiting. It does this by carefully adding network noise and making all network packets the same size.', + ), + { daita: strings.daita, daitaFull: strings.daitaFull }, + )} + </ModalMessage> + <ModalMessage> + {sprintf( + messages.pgettext( + 'wireguard-settings-view', + 'Attention: Since this increases your total network traffic, be cautious if you have a limited data plan. It can also negatively impact your network speed. Please consider this if you want to enable %(daita)s.', + ), + { daita: strings.daita }, + )} + </ModalMessage> + </InfoButton> + <AriaInput> + <Cell.Switch isOn={daita} onChange={setDaita} /> + </AriaInput> + </Cell.Container> + </AriaInputGroup> + <ModalAlert + isOpen={confirmationDialogVisible} + type={ModalAlertType.caution} + buttons={[ + <AppButton.BlueButton key="confirm" onClick={confirmDaita}> + {messages.gettext('Enable anyway')} + </AppButton.BlueButton>, + <AppButton.BlueButton key="back" onClick={hideConfirmationDialog}> + {messages.gettext('Back')} + </AppButton.BlueButton>, + ]} + close={hideConfirmationDialog}> + <ModalMessage> + { + // TRANSLATORS: Warning text in a dialog that is displayed after a setting is toggled. + messages.pgettext( + 'wireguard-settings-view', + "This feature isn't available on all servers. You might need to change location after enabling.", + ) + } + </ModalMessage> + <ModalMessage> + {sprintf( + messages.pgettext( + 'wireguard-settings-view', + 'Attention: Since this increases your total network traffic, be cautious if you have a limited data plan. It can also negatively impact your network speed. Please consider this if you want to enable %(daita)s.', + ), + { daita: strings.daita }, + )} + </ModalMessage> + </ModalAlert> + </> + ); +} + function QuantumResistantSetting() { const { setWireguardQuantumResistant } = useAppContext(); const quantumResistant = useSelector((state) => state.settings.wireguard.quantumResistant); diff --git a/gui/src/renderer/components/YellowLabel.tsx b/gui/src/renderer/components/YellowLabel.tsx new file mode 100644 index 0000000000..55059e223f --- /dev/null +++ b/gui/src/renderer/components/YellowLabel.tsx @@ -0,0 +1,18 @@ +import styled from 'styled-components'; + +import { colors } from '../../config.json'; + +export default styled.span({ + display: 'inline-block', + fontFamily: 'Open Sans', + color: colors.blue, + fontSize: '12px', + fontWeight: 800, + lineHeight: '20px', + padding: '1px 8px', + marginLeft: '8px', + background: colors.yellow, + borderRadius: '5px', + textAlign: 'center', + verticalAlign: 'middle', +}); diff --git a/gui/src/renderer/components/cell/Label.tsx b/gui/src/renderer/components/cell/Label.tsx index 729b7dc31d..f74e57b39c 100644 --- a/gui/src/renderer/components/cell/Label.tsx +++ b/gui/src/renderer/components/cell/Label.tsx @@ -8,6 +8,8 @@ import { CellButton } from './CellButton'; import { CellDisabledContext } from './Container'; const StyledLabel = styled.div<{ disabled: boolean }>(buttonText, (props) => ({ + display: 'flex', + alignItems: 'center', margin: '10px 0', flex: 1, color: props.disabled ? colors.white40 : colors.white, diff --git a/gui/src/renderer/components/select-location/RelayListContext.tsx b/gui/src/renderer/components/select-location/RelayListContext.tsx index 86dd7e5467..13e2dc28e8 100644 --- a/gui/src/renderer/components/select-location/RelayListContext.tsx +++ b/gui/src/renderer/components/select-location/RelayListContext.tsx @@ -4,6 +4,7 @@ import { compareRelayLocation, RelayLocation } from '../../../shared/daemon-rpc- import { EndpointType, filterLocations, + filterLocationsByDaita, filterLocationsByEndPointType, getLocationsExpandedBySearch, searchForLocations, @@ -60,6 +61,7 @@ interface RelayListContextProviderProps { export function RelayListContextProvider(props: RelayListContextProviderProps) { const { locationType, searchTerm } = useSelectLocationContext(); + const daita = useSelector((state) => state.settings.wireguard.daita?.enabled ?? false); const fullRelayList = useSelector((state) => state.settings.relayLocations); const relaySettings = useNormalRelaySettings(); @@ -71,15 +73,21 @@ export function RelayListContextProvider(props: RelayListContextProviderProps) { return filterLocationsByEndPointType(fullRelayList, endpointType, relaySettings); }, [fullRelayList, locationType, relaySettings?.tunnelProtocol]); + const relayListForDaita = useMemo(() => { + return filterLocationsByDaita( + relayListForEndpointType, + daita, + locationType, + relaySettings?.tunnelProtocol ?? 'any', + relaySettings?.wireguard.useMultihop ?? false, + ); + }, [daita, relayListForEndpointType]); + // Filters the relays to only keep the relays matching the currently selected filters, e.g. // ownership and providers const relayListForFilters = useMemo(() => { - return filterLocations( - relayListForEndpointType, - relaySettings?.ownership, - relaySettings?.providers, - ); - }, [relaySettings?.ownership, relaySettings?.providers, relayListForEndpointType]); + return filterLocations(relayListForDaita, relaySettings?.ownership, relaySettings?.providers); + }, [relaySettings?.ownership, relaySettings?.providers, relayListForDaita]); // Filters the relays based on the provided search term const relayListForSearch = useMemo(() => { diff --git a/gui/src/renderer/components/select-location/SelectLocation.tsx b/gui/src/renderer/components/select-location/SelectLocation.tsx index 5ba0203939..3f3535225e 100644 --- a/gui/src/renderer/components/select-location/SelectLocation.tsx +++ b/gui/src/renderer/components/select-location/SelectLocation.tsx @@ -5,7 +5,7 @@ import { colors } from '../../../config.json'; import { Ownership } from '../../../shared/daemon-rpc-types'; import { messages } from '../../../shared/gettext'; import { useRelaySettingsUpdater } from '../../lib/constraint-updater'; -import { filterSpecialLocations } from '../../lib/filter-locations'; +import { daitaFilterActive, filterSpecialLocations } from '../../lib/filter-locations'; import { useHistory } from '../../lib/history'; import { formatHtml } from '../../lib/html-formatter'; import { RoutePath } from '../../lib/routes'; @@ -71,6 +71,13 @@ export default function SelectLocation() { const ownership = relaySettings?.ownership ?? Ownership.any; const providers = relaySettings?.providers ?? []; const filteredProviders = useFilteredProviders(providers, ownership); + const daita = useSelector((state) => state.settings.wireguard.daita?.enabled ?? false); + const showDaitaFilter = daitaFilterActive( + daita, + locationType, + relaySettings?.tunnelProtocol ?? 'any', + relaySettings?.wireguard.useMultihop ?? false, + ); const [searchValue, setSearchValue] = useState(''); @@ -122,7 +129,7 @@ export default function SelectLocation() { const showOwnershipFilter = ownership !== Ownership.any; const showProvidersFilter = providers.length > 0; - const showFilters = showOwnershipFilter || showProvidersFilter; + const showFilters = showOwnershipFilter || showProvidersFilter || showDaitaFilter; return ( <BackAction action={onClose}> <Layout> @@ -220,6 +227,15 @@ export default function SelectLocation() { </StyledClearFilterButton> </StyledFilter> )} + + {showDaitaFilter && ( + <StyledFilter> + {sprintf( + messages.pgettext('select-location-view', 'Setting: %(settingName)s'), + { settingName: 'DAITA' }, + )} + </StyledFilter> + )} </StyledFilterRow> )} diff --git a/gui/src/renderer/containers/ConnectionPanelContainer.tsx b/gui/src/renderer/containers/ConnectionPanelContainer.tsx index 5311f1a8e4..b12902bc5d 100644 --- a/gui/src/renderer/containers/ConnectionPanelContainer.tsx +++ b/gui/src/renderer/containers/ConnectionPanelContainer.tsx @@ -94,6 +94,11 @@ const mapStateToProps = (state: IReduxState) => { ? tunnelEndpointToObfuscationEndpoint(status.details.endpoint) : undefined; + const daita = + ((status.state === 'connected' || status.state === 'connecting') && + status.details?.endpoint.daita) ?? + false; + return { isOpen: state.userInterface.connectionPanelVisible, hostname: state.connection.hostname, @@ -104,6 +109,7 @@ const mapStateToProps = (state: IReduxState) => { bridgeInfo, outAddress, obfuscationEndpoint, + daita, }; }; diff --git a/gui/src/renderer/lib/filter-locations.ts b/gui/src/renderer/lib/filter-locations.ts index 63ca56cbf2..78126f1fb4 100644 --- a/gui/src/renderer/lib/filter-locations.ts +++ b/gui/src/renderer/lib/filter-locations.ts @@ -1,6 +1,13 @@ -import { Ownership, RelayEndpointType, RelayLocation } from '../../shared/daemon-rpc-types'; +import { + LiftedConstraint, + Ownership, + RelayEndpointType, + RelayLocation, + TunnelProtocol, +} from '../../shared/daemon-rpc-types'; import { relayLocations } from '../../shared/gettext'; import { + LocationType, RelayLocationCityWithVisibility, RelayLocationCountryWithVisibility, RelayLocationRelayWithVisibility, @@ -27,6 +34,30 @@ export function filterLocationsByEndPointType( return filterLocationsImpl(locations, getTunnelProtocolFilter(endpointType, relaySettings)); } +export function filterLocationsByDaita( + locations: IRelayLocationCountryRedux[], + daita: boolean, + locationType: LocationType, + tunnelProtocol: LiftedConstraint<TunnelProtocol>, + multihop: boolean, +): IRelayLocationCountryRedux[] { + return daitaFilterActive(daita, locationType, tunnelProtocol, multihop) + ? filterLocationsImpl(locations, (relay: IRelayLocationRelayRedux) => relay.daita) + : locations; +} + +export function daitaFilterActive( + daita: boolean, + locationType: LocationType, + tunnelProtocol: LiftedConstraint<TunnelProtocol>, + multihop: boolean, +) { + const isEntry = multihop + ? locationType === LocationType.entry + : locationType === LocationType.exit; + return daita && isEntry && tunnelProtocol !== 'openvpn'; +} + export function filterLocations( locations: IRelayLocationCountryRedux[], ownership?: Ownership, diff --git a/gui/src/renderer/redux/settings/actions.ts b/gui/src/renderer/redux/settings/actions.ts index aa4e460e6f..ba6e4ce5a8 100644 --- a/gui/src/renderer/redux/settings/actions.ts +++ b/gui/src/renderer/redux/settings/actions.ts @@ -4,6 +4,7 @@ import { ApiAccessMethodSettings, BridgeState, CustomLists, + IDaitaSettings, IDnsOptions, IWireguardEndpointData, ObfuscationSettings, @@ -77,6 +78,11 @@ export interface IUpdateWireguardQuantumResistantAction { quantumResistant?: boolean; } +export interface IUpdateWireguardDaitaAction { + type: 'UPDATE_WIREGUARD_DAITA'; + daita?: IDaitaSettings; +} + export interface IUpdateAutoStartAction { type: 'UPDATE_AUTO_START'; autoStart: boolean; @@ -136,6 +142,7 @@ export type SettingsAction = | IUpdateOpenVpnMssfixAction | IUpdateWireguardMtuAction | IUpdateWireguardQuantumResistantAction + | IUpdateWireguardDaitaAction | IUpdateAutoStartAction | IUpdateDnsOptionsAction | IUpdateSplitTunnelingStateAction @@ -245,6 +252,13 @@ function updateWireguardQuantumResistant( }; } +function updateWireguardDaita(daita?: IDaitaSettings): IUpdateWireguardDaitaAction { + return { + type: 'UPDATE_WIREGUARD_DAITA', + daita, + }; +} + function updateAutoStart(autoStart: boolean): IUpdateAutoStartAction { return { type: 'UPDATE_AUTO_START', @@ -326,6 +340,7 @@ export default { updateOpenVpnMssfix, updateWireguardMtu, updateWireguardQuantumResistant, + updateWireguardDaita, updateAutoStart, updateDnsOptions, updateSplitTunnelingState, diff --git a/gui/src/renderer/redux/settings/reducers.ts b/gui/src/renderer/redux/settings/reducers.ts index 3c502ec103..18873eea20 100644 --- a/gui/src/renderer/redux/settings/reducers.ts +++ b/gui/src/renderer/redux/settings/reducers.ts @@ -7,6 +7,7 @@ import { BridgeType, CustomLists, CustomProxy, + IDaitaSettings, IDnsOptions, IpVersion, IWireguardEndpointData, @@ -75,6 +76,7 @@ export interface IRelayLocationRelayRedux { owned: boolean; weight: number; endpointType: RelayEndpointType; + daita: boolean; } export interface IRelayLocationCityRedux { @@ -109,6 +111,7 @@ export interface ISettingsReduxState { wireguard: { mtu?: number; quantumResistant?: boolean; + daita?: IDaitaSettings; }; dns: IDnsOptions; splitTunneling: boolean; @@ -271,6 +274,14 @@ export default function ( quantumResistant: action.quantumResistant, }, }; + case 'UPDATE_WIREGUARD_DAITA': + return { + ...state, + wireguard: { + ...state.wireguard, + daita: action.daita, + }, + }; case 'UPDATE_AUTO_START': return { diff --git a/gui/src/shared/daemon-rpc-types.ts b/gui/src/shared/daemon-rpc-types.ts index 738eef5e95..761dd01a4c 100644 --- a/gui/src/shared/daemon-rpc-types.ts +++ b/gui/src/shared/daemon-rpc-types.ts @@ -143,6 +143,7 @@ export interface ITunnelEndpoint { proxy?: IProxyEndpoint; obfuscationEndpoint?: IObfuscationEndpoint; entryEndpoint?: IEndpoint; + daita: boolean; } export interface IEndpoint { @@ -309,6 +310,7 @@ export interface IRelayListHostname { weight: number; owned: boolean; endpointType: RelayEndpointType; + daita: boolean; } export type RelayEndpointType = 'wireguard' | 'openvpn' | 'bridge'; @@ -320,6 +322,7 @@ export interface ITunnelOptions { wireguard: { mtu?: number; quantumResistant?: boolean; + daita?: IDaitaSettings; }; generic: { enableIpv6: boolean; @@ -503,6 +506,10 @@ export interface RelayOverride { ipv6AddrIn?: string; } +export interface IDaitaSettings { + enabled: boolean; +} + export function parseSocketAddress(socketAddrStr: string): ISocketAddress { const re = new RegExp(/(.+):(\d+)$/); const matches = socketAddrStr.match(re); diff --git a/gui/src/shared/ipc-schema.ts b/gui/src/shared/ipc-schema.ts index d90957764e..561ec924a0 100644 --- a/gui/src/shared/ipc-schema.ts +++ b/gui/src/shared/ipc-schema.ts @@ -14,6 +14,7 @@ import { IAccountData, IAppVersionInfo, ICustomList, + IDaitaSettings, IDevice, IDeviceRemoval, IDnsOptions, @@ -192,6 +193,7 @@ export const ipcSchema = { testApiAccessMethodById: invoke<string, boolean>(), testCustomApiAccessMethod: invoke<CustomProxy, boolean>(), clearAllRelayOverrides: invoke<void, void>(), + setDaitaSettings: invoke<IDaitaSettings, void>(), }, guiSettings: { '': notifyRenderer<IGuiSettingsState>(), |
