diff options
| author | Emīls Piņķis <emils@mullvad.net> | 2019-01-30 14:02:13 +0000 |
|---|---|---|
| committer | Emīls Piņķis <emils@mullvad.net> | 2019-01-30 14:02:13 +0000 |
| commit | 2139dfb9c37f0d7d6b5e823631bac23b511b38f6 (patch) | |
| tree | 68254aa2fea067b92cd55fcce9195c87292df6e5 | |
| parent | 8c43e0cd550b84c8688270edad525c022d49cd78 (diff) | |
| parent | 68dd1990e1555ea074d408adeda3cd502abb2e8f (diff) | |
| download | mullvadvpn-2139dfb9c37f0d7d6b5e823631bac23b511b38f6.tar.xz mullvadvpn-2139dfb9c37f0d7d6b5e823631bac23b511b38f6.zip | |
Merge branch 'refactor-talpid-types'
37 files changed, 1128 insertions, 833 deletions
diff --git a/Cargo.lock b/Cargo.lock index 2c905c1322..973752aab2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -158,14 +158,6 @@ dependencies = [ ] [[package]] -name = "clear_on_drop" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] name = "cloudabi" version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -237,19 +229,6 @@ dependencies = [ ] [[package]] -name = "curve25519-dalek" -version = "0.19.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", - "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] name = "dbus" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -287,14 +266,6 @@ dependencies = [ ] [[package]] -name = "digest" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] name = "dirs" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -489,14 +460,6 @@ version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "generic-array" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] name = "globset" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1141,6 +1104,7 @@ version = "0.1.0" dependencies = [ "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ipnetwork 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "mullvad-paths 0.1.0", @@ -1740,11 +1704,6 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "subtle" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] name = "syn" version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1892,9 +1851,10 @@ name = "talpid-types" version = "0.1.0" dependencies = [ "base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "ipnetwork 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", - "x25519-dalek 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2205,11 +2165,6 @@ dependencies = [ ] [[package]] -name = "typenum" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] name = "ucd-util" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2400,15 +2355,6 @@ dependencies = [ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "x25519-dalek" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "curve25519-dalek 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - [metadata] "checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" @@ -2430,7 +2376,6 @@ dependencies = [ "checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4" "checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878" "checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e" -"checksum clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "97276801e127ffb46b66ce23f35cc96bd454fa311294bced4bbace7baa8b1d17" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" "checksum colored 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc0a60679001b62fb628c4da80e574b9645ab4646056d7c9018885efffe45533" "checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e" @@ -2440,12 +2385,10 @@ dependencies = [ "checksum crossbeam-epoch 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "30fecfcac6abfef8771151f8be4abc9e4edc112c2bcb233314cafde2680536e9" "checksum crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "677d453a17e8bd2b913fa38e8b9cf04bcdbb5be790aa294f2389661d72036015" "checksum ctrlc 3.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "630391922b1b893692c6334369ff528dcc3a9d8061ccf4c803aa8f83cb13db5e" -"checksum curve25519-dalek 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)" = "15d6d81c070d8090389f752510ce22c7d571100a78fa4e7c06e6f6d95585bb49" "checksum dbus 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3d975a175aa2dced1a6cd410b89a1bf23918f301eab2b6f7c5e608291b757639" "checksum derive-try-from-primitive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "81dbd65eb15734b6d50dc6ac86f14f928462be0a5df6bda17761e909071ede5d" "checksum derive_builder 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c998e6ab02a828dd9735c18f154e14100e674ed08cb4e1938f0e4177543f439" "checksum derive_builder_core 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "735e24ee9e5fa8e16b86da5007856e97d592e11867e45d76e0c0d0a164a0b757" -"checksum digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90" "checksum dirs 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "88972de891f6118092b643d85a0b28e0678e0f948d7f879aa32f2d5aafe97d2a" "checksum duct 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f87f5af80601599209bff21146e4113e8c54471151049deebc37e75b78e42729" "checksum env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)" = "15b0a4d2e39f8420210be8b27eeda28029729e2fd4291019455016c348240c38" @@ -2469,7 +2412,6 @@ dependencies = [ "checksum futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)" = "49e7653e374fe0d0c12de4250f0bdb60680b8c80eed558c5c7538eec9c89e21b" "checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" "checksum gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)" = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" -"checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d" "checksum globset 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90d069fe6beb9be359ef505650b3f73228c5591a3c4b1f32be2f4f44459ffa3a" "checksum globset 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4743617a7464bbda3c8aec8558ff2f9429047e025771037df561d383337ff865" "checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" @@ -2588,7 +2530,6 @@ dependencies = [ "checksum socket2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c4d11a52082057d87cb5caa31ad812f4504b97ab44732cd8359df2e9ff9f48e7" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" -"checksum subtle 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5938f1b89f10d6356339f071eca74209deeae0b6891c2678d655feb78637e369" "checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" "checksum syn 0.15.18 (registry+https://github.com/rust-lang/crates.io-index)" = "90c39a061e2f412a9f869540471ab679e85e50c6b05604daf28bc3060f75c430" "checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" @@ -2623,7 +2564,6 @@ dependencies = [ "checksum toml 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "4a2ecc31b0351ea18b3fe11274b8db6e4d82bce861bbb22e6dbed40417902c65" "checksum try-lock 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee2aa4715743892880f70885373966c83d73ef1b0838a664ef0c76fffd35e7c2" "checksum tun 0.4.2 (git+https://github.com/pinkisemils/rust-tun?branch=add-raw-fd-traits)" = "<none>" -"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" "checksum ucd-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d0f8bfa9ff0cadcd210129ad9d2c5f145c13e9ced3d3e5d948a6213487d52444" "checksum unicase 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9d3218ea14b4edcaccfa0df0a64a3792a2c32cc706f1b336e48867f9d3147f90" "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" @@ -2652,4 +2592,3 @@ dependencies = [ "checksum winreg 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a27a759395c1195c4cc5cda607ef6f8f6498f64e78f7900f5de0a127a424704a" "checksum winres 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f07dabda4e79413ecac65bc9a2234ad3d85dc49f9d289f868cd9d8611d88f28d" "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" -"checksum x25519-dalek 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "538296831e9794ec5b3ec9d4a1a8c3e3c86271e9635a0e776e3214fec9c727de" diff --git a/gui/packages/desktop/src/main/daemon-rpc.js b/gui/packages/desktop/src/main/daemon-rpc.js index 000dc544b9..6b3ec60135 100644 --- a/gui/packages/desktop/src/main/daemon-rpc.js +++ b/gui/packages/desktop/src/main/daemon-rpc.js @@ -53,12 +53,32 @@ const constraint = <T>(constraintValue: SchemaNode<T>) => { ); }; -const TunnelEndpointDataSchema = partialObject({ - openvpn: partialObject({ - port: number, - protocol: enumeration('udp', 'tcp'), +const CustomTunnelEndpoint = oneOf( + object({ + openvpn: object({ + endpoint: object({ + address: string, + protocol: enumeration('udp', 'tcp'), + }), + username: string, + password: string, + }), }), -}); + object({ + wireguard: object({ + tunnel: object({ + private_key: string, + addresses: arrayOf(string), + }), + peer: object({ + public_key: string, + allowed_ips: arrayOf(string), + endpoint: string, + }), + gateway: string, + }), + }), +); const RelaySettingsSchema = oneOf( object({ @@ -89,7 +109,7 @@ const RelaySettingsSchema = oneOf( object({ custom_tunnel_endpoint: partialObject({ host: string, - tunnel: TunnelEndpointDataSchema, + config: CustomTunnelEndpoint, }), }), ); @@ -142,11 +162,18 @@ const OpenVpnProxySchema = maybe( ); const TunnelOptionsSchema = partialObject({ - enable_ipv6: boolean, openvpn: partialObject({ mssfix: maybe(number), proxy: OpenVpnProxySchema, }), + wireguard: partialObject({ + mtu: maybe(number), + // only relevant on linux + fmwark: maybe(number), + }), + generic: partialObject({ + enable_ipv6: boolean, + }), }); const AccountDataSchema = partialObject({ @@ -162,7 +189,8 @@ const TunnelStateTransitionSchema = oneOf( state: enumeration('connecting', 'connected'), details: partialObject({ address: string, - tunnel: TunnelEndpointDataSchema, + protocol: enumeration('tcp', 'udp'), + tunnel_type: enumeration('wireguard', 'openvpn'), }), }), object({ diff --git a/gui/packages/desktop/src/main/index.js b/gui/packages/desktop/src/main/index.js index 4d20e98d0a..d13956cbc5 100644 --- a/gui/packages/desktop/src/main/index.js +++ b/gui/packages/desktop/src/main/index.js @@ -77,11 +77,17 @@ const ApplicationMain = { }, }, tunnelOptions: { - enableIpv6: false, + generic: { + enableIpv6: false, + }, openvpn: { mssfix: null, + proxy: null, + }, + wireguard: { + mtu: null, + fwmark: null, }, - proxy: null, }, }: Settings), _guiSettings: new GuiSettings(), diff --git a/gui/packages/desktop/src/renderer/app.js b/gui/packages/desktop/src/renderer/app.js index afea9e9651..280c815ba8 100644 --- a/gui/packages/desktop/src/renderer/app.js +++ b/gui/packages/desktop/src/renderer/app.js @@ -30,6 +30,7 @@ import { IpcRendererEventChannel } from '../shared/ipc-event-channel'; import type { AccountToken, AccountData, + ConnectionConfig, Location, RelayList, RelaySettingsUpdate, @@ -274,12 +275,19 @@ export default class AppRenderer { }); } else if (relaySettings.customTunnelEndpoint) { const customTunnelEndpoint = relaySettings.customTunnelEndpoint; - const { - host, - tunnel: { - openvpn: { port, protocol }, - }, - } = customTunnelEndpoint; + const host = customTunnelEndpoint.host; + const config: ConnectionConfig = customTunnelEndpoint.config; + + let port = 0; + let protocol = 'udp'; + if (config.openvpn) { + port = config.openvpn.endpoint.port; + protocol = config.openvpn.endpoint.protocol; + } + + if (config.wireguard) { + // TODO: handle wireguard + } actions.settings.updateRelay({ customTunnelEndpoint: { @@ -449,7 +457,7 @@ export default class AppRenderer { const reduxAccount = this._reduxActions.account; reduxSettings.updateAllowLan(newSettings.allowLan); - reduxSettings.updateEnableIpv6(newSettings.tunnelOptions.enableIpv6); + reduxSettings.updateEnableIpv6(newSettings.tunnelOptions.generic.enableIpv6); reduxSettings.updateBlockWhenDisconnected(newSettings.blockWhenDisconnected); reduxSettings.updateOpenVpnMssfix(newSettings.tunnelOptions.openvpn.mssfix); diff --git a/gui/packages/desktop/src/renderer/components/Connect.js b/gui/packages/desktop/src/renderer/components/Connect.js index c4d26d15db..08636ad567 100644 --- a/gui/packages/desktop/src/renderer/components/Connect.js +++ b/gui/packages/desktop/src/renderer/components/Connect.js @@ -10,6 +10,7 @@ import TunnelControl from './TunnelControl'; import Map from './Map'; import styles from './ConnectStyles'; import { NoCreditError, NoInternetError } from '../../main/errors'; +import { parseSocketAddress } from '../../shared/daemon-rpc-types'; import type { RelayOutAddress, RelayInAddress } from './TunnelControl'; import type AccountExpiry from '../lib/account-expiry'; @@ -170,10 +171,11 @@ export default class Connect extends Component<Props> { let relayInAddress: ?RelayInAddress = null; if ((tunnelState === 'connecting' || tunnelState === 'connected') && details) { + const socketAddr = parseSocketAddress(details.address); relayInAddress = { - ip: details.address, - port: details.tunnel.openvpn.port, - protocol: details.tunnel.openvpn.protocol, + ip: socketAddr.host, + port: socketAddr.port, + protocol: details.protocol, }; } diff --git a/gui/packages/desktop/src/renderer/lib/relay-settings-builder.js b/gui/packages/desktop/src/renderer/lib/relay-settings-builder.js index d81c4d6fac..3397f543df 100644 --- a/gui/packages/desktop/src/renderer/lib/relay-settings-builder.js +++ b/gui/packages/desktop/src/renderer/lib/relay-settings-builder.js @@ -5,7 +5,6 @@ import type { RelayProtocol, RelaySettingsUpdate, RelaySettingsNormalUpdate, - RelaySettingsCustom, } from '../../shared/daemon-rpc-types'; type LocationBuilder<Self> = { @@ -139,65 +138,6 @@ class NormalRelaySettingsBuilder { } } -type CustomOpenVPNConfigurator<Self> = { - port: (port: number) => Self, - protocol: (protocol: RelayProtocol) => Self, -}; - -type CustomTunnelBuilder<Self> = { - openvpn: (configurator: (CustomOpenVPNConfigurator<*>) => void) => Self, -}; - -class CustomRelaySettingsBuilder { - _payload: RelaySettingsCustom = { - host: '', - tunnel: { - openvpn: { - port: 0, - protocol: 'udp', - }, - }, - }; - - build(): RelaySettingsUpdate { - return { - customTunnelEndpoint: this._payload, - }; - } - - host(value: string) { - this._payload.host = value; - return this; - } - - get tunnel(): CustomTunnelBuilder<CustomRelaySettingsBuilder> { - const updateOpenvpn = (next) => { - const tunnel = this._payload.tunnel || {}; - const prev = tunnel.openvpn || {}; - this._payload.tunnel = { - openvpn: { ...prev, ...next }, - }; - }; - - return { - openvpn: (configurator) => { - configurator({ - port: function(port: number) { - updateOpenvpn({ port }); - return this; - }, - protocol: function(protocol: RelayProtocol) { - updateOpenvpn({ protocol }); - return this; - }, - }); - return this; - }, - }; - } -} - export default { normal: () => new NormalRelaySettingsBuilder(), - custom: () => new CustomRelaySettingsBuilder(), }; diff --git a/gui/packages/desktop/src/shared/daemon-rpc-types.js b/gui/packages/desktop/src/shared/daemon-rpc-types.js index 82e52a7603..9b39ba90c2 100644 --- a/gui/packages/desktop/src/shared/daemon-rpc-types.js +++ b/gui/packages/desktop/src/shared/daemon-rpc-types.js @@ -1,5 +1,4 @@ // @flow - export type AccountData = { expiry: string }; export type AccountToken = string; export type Ip = string; @@ -30,16 +29,14 @@ export type AfterDisconnect = 'nothing' | 'block' | 'reconnect'; export type TunnelState = 'connecting' | 'connected' | 'disconnecting' | 'disconnected' | 'blocked'; +export type TunnelType = 'wireguard' | 'openvpn'; + +export type RelayProtocol = 'tcp' | 'udp'; + export type TunnelEndpoint = { address: string, - tunnel: TunnelEndpointData, -}; - -export type TunnelEndpointData = { - openvpn: { - port: number, - protocol: RelayProtocol, - }, + protocol: RelayProtocol, + tunnel: TunnelType, }; export type TunnelStateTransition = @@ -49,7 +46,6 @@ export type TunnelStateTransition = | { state: 'disconnecting', details: AfterDisconnect } | { state: 'blocked', details: BlockReason }; -export type RelayProtocol = 'tcp' | 'udp'; export type RelayLocation = | {| hostname: [string, string, string] |} | {| city: [string, string] |} @@ -77,10 +73,36 @@ type RelaySettingsNormal<TTunnelConstraints> = { }, }; +export type ConnectionConfig = + | {| + openvpn: { + endpoint: { + ip: string, + port: number, + protocol: RelayProtocol, + }, + username: string, + }, + |} + | {| + wireguard: { + tunnel: { + private_key: string, + addresses: Array<string>, + }, + peer: { + public_key: string, + addresses: Array<string>, + endpoint: string, + }, + gateway: string, + }, + |}; + // types describing the structure of RelaySettings export type RelaySettingsCustom = { host: string, - tunnel: TunnelEndpointData, + config: ConnectionConfig, }; export type RelaySettings = | {| @@ -128,11 +150,18 @@ export type RelayListHostname = { }; export type TunnelOptions = { - enableIpv6: boolean, openvpn: { mssfix: ?number, + proxy: ?ProxySettings, + }, + wireguard: { + mtu: ?number, + // Only relevant on Linux + fwmark: ?number, + }, + generic: { + enableIpv6: boolean, }, - proxy: ?ProxySettings, }; export type ProxySettings = LocalProxySettings | RemoteProxySettings; @@ -168,3 +197,19 @@ export type Settings = { relaySettings: RelaySettings, tunnelOptions: TunnelOptions, }; + +export type SocketAddress = { host: string, port: number }; + +export function parseSocketAddress(socketAddrStr: string): SocketAddress { + const re = new RegExp(/(.+):(\d+)$/); + const matches = socketAddrStr.match(re); + + if (!matches || matches.length < 3) { + throw new Error(`Failed to parse socket address from address string '${socketAddrStr}'`); + } + const socketAddress: SocketAddress = { + host: matches[1], + port: Number(matches[2]), + }; + return socketAddress; +} diff --git a/gui/packages/desktop/test/relay-settings-builder.spec.js b/gui/packages/desktop/test/relay-settings-builder.spec.js index 56126adca5..0782476fe8 100644 --- a/gui/packages/desktop/test/relay-settings-builder.spec.js +++ b/gui/packages/desktop/test/relay-settings-builder.spec.js @@ -122,25 +122,4 @@ describe('Relay settings builder', () => { }, }); }); - - it('should set custom endpoint settings', () => { - expect( - RelaySettingsBuilder.custom() - .host('se2.mullvad.net') - .tunnel.openvpn((openvpn) => { - openvpn.port(80).protocol('tcp'); - }) - .build(), - ).to.deep.equal({ - customTunnelEndpoint: { - host: 'se2.mullvad.net', - tunnel: { - openvpn: { - port: 80, - protocol: 'tcp', - }, - }, - }, - }); - }); }); diff --git a/mullvad-cli/src/cmds/relay.rs b/mullvad-cli/src/cmds/relay.rs index 5acee6fd39..2f3d276fce 100644 --- a/mullvad-cli/src/cmds/relay.rs +++ b/mullvad-cli/src/cmds/relay.rs @@ -1,17 +1,15 @@ use crate::{new_rpc_client, Command, Result, ResultExt}; use clap::value_t; -use std::str::FromStr; +use std::{net::Ipv4Addr, str::FromStr}; use mullvad_types::{ relay_constraints::{ Constraint, LocationConstraint, OpenVpnConstraints, RelayConstraintsUpdate, RelaySettingsUpdate, TunnelConstraints, }, - CustomTunnelEndpoint, -}; -use talpid_types::net::{ - OpenVpnEndpointData, TransportProtocol, TunnelEndpointData, WireguardEndpointData, + ConnectionConfig, CustomTunnelEndpoint, }; +use talpid_types::net::{openvpn, Endpoint, TransportProtocol}; pub struct Relay; @@ -57,6 +55,16 @@ impl Command for Relay { .index(4) .default_value("udp") .possible_values(&["udp", "tcp"]), + ) + .arg( + clap::Arg::with_name("username") + .help("Username to be used with the OpenVpn relay") + .index(5), + ) + .arg( + clap::Arg::with_name("password") + .help("Password to be used with the OpenVpn relay") + .index(6), ), ) .subcommand( @@ -146,17 +154,26 @@ impl Relay { fn set_custom(&self, matches: &clap::ArgMatches) -> Result<()> { let host = value_t!(matches.value_of("host"), String).unwrap_or_else(|e| e.exit()); let port = value_t!(matches.value_of("port"), u16).unwrap_or_else(|e| e.exit()); - let tunnel = match matches.value_of("tunnel").unwrap() { - "openvpn" => TunnelEndpointData::OpenVpn(OpenVpnEndpointData { - port, - protocol: value_t!(matches.value_of("protocol"), TransportProtocol).unwrap(), - }), + let config = match matches.value_of("tunnel").unwrap() { + "openvpn" => { + let username = + value_t!(matches.value_of("username"), String).unwrap_or_else(|e| e.exit()); + let password = + value_t!(matches.value_of("password"), String).unwrap_or_else(|e| e.exit()); + let protocol = value_t!(matches.value_of("protocol"), TransportProtocol) + .unwrap_or_else(|e| e.exit()); + ConnectionConfig::OpenVpn(openvpn::ConnectionConfig { + endpoint: Endpoint::new(Ipv4Addr::UNSPECIFIED, port, protocol), + username, + password, + }) + } // TODO: Gather all the data to build a WireguardEndpointData properly. // "wireguard" => TunnelEndpointData::Wireguard(WireguardEndpointData { port }), _ => unreachable!("Invalid tunnel protocol"), }; self.update_constraints(RelaySettingsUpdate::CustomTunnelEndpoint( - CustomTunnelEndpoint { host, tunnel }, + CustomTunnelEndpoint::new(host, config), )) } diff --git a/mullvad-cli/src/cmds/tunnel.rs b/mullvad-cli/src/cmds/tunnel.rs index a45ae31c17..33e75d26d4 100644 --- a/mullvad-cli/src/cmds/tunnel.rs +++ b/mullvad-cli/src/cmds/tunnel.rs @@ -1,10 +1,8 @@ use crate::{new_rpc_client, Command, Result}; use clap::value_t; -use talpid_types::net::{ - LocalOpenVpnProxySettings, OpenVpnProxyAuth, OpenVpnProxySettings, - OpenVpnProxySettingsValidation, RemoteOpenVpnProxySettings, TunnelOptions, -}; +use mullvad_types::settings::TunnelOptions; +use talpid_types::net::openvpn; use std::net::{IpAddr, SocketAddr}; @@ -102,11 +100,13 @@ fn create_openvpn_proxy_subcommand() -> clap::App<'static, 'static> { .arg( clap::Arg::with_name("username") .help("Specifies the username for remote authentication") + .required(true) .index(3), ) .arg( clap::Arg::with_name("password") .help("Specifies the password for remote authentication") + .required(true) .index(4), ), ), @@ -207,9 +207,9 @@ impl Tunnel { fn process_openvpn_proxy_get() -> Result<()> { let tunnel_options = Self::get_tunnel_options()?; if let Some(proxy) = tunnel_options.openvpn.proxy { - if let OpenVpnProxySettings::Local(local_proxy) = proxy { + if let openvpn::ProxySettings::Local(local_proxy) = proxy { Self::print_local_proxy(&local_proxy) - } else if let OpenVpnProxySettings::Remote(remote_proxy) = proxy { + } else if let openvpn::ProxySettings::Remote(remote_proxy) = proxy { Self::print_remote_proxy(&remote_proxy) } else { unreachable!("unhandled proxy type"); @@ -220,14 +220,14 @@ impl Tunnel { Ok(()) } - fn print_local_proxy(proxy: &LocalOpenVpnProxySettings) { + fn print_local_proxy(proxy: &openvpn::LocalProxySettings) { println!("proxy: local"); println!(" local port: {}", proxy.port); println!(" peer IP: {}", proxy.peer.ip()); println!(" peer port: {}", proxy.peer.port()); } - fn print_remote_proxy(proxy: &RemoteOpenVpnProxySettings) { + fn print_remote_proxy(proxy: &openvpn::RemoteProxySettings) { println!("proxy: remote"); println!(" server IP: {}", proxy.address.ip()); println!(" server port: {}", proxy.address.port()); @@ -256,14 +256,14 @@ impl Tunnel { let remote_port = value_t!(args.value_of("remote-port"), u16).unwrap_or_else(|e| e.exit()); - let proxy = LocalOpenVpnProxySettings { + let proxy = openvpn::LocalProxySettings { port: local_port, peer: SocketAddr::new(remote_ip, remote_port), }; - let packed_proxy = OpenVpnProxySettings::Local(proxy); + let packed_proxy = openvpn::ProxySettings::Local(proxy); - if let Err(error) = OpenVpnProxySettingsValidation::validate(&packed_proxy) { + if let Err(error) = openvpn::ProxySettingsValidation::validate(&packed_proxy) { panic!(error); } @@ -278,21 +278,21 @@ impl Tunnel { let password = args.value_of("password"); let auth = match (username, password) { - (Some(username), Some(password)) => Some(OpenVpnProxyAuth { + (Some(username), Some(password)) => Some(openvpn::ProxyAuth { username: username.to_string(), password: password.to_string(), }), _ => None, }; - let proxy = RemoteOpenVpnProxySettings { + let proxy = openvpn::RemoteProxySettings { address: SocketAddr::new(remote_ip, remote_port), auth, }; - let packed_proxy = OpenVpnProxySettings::Remote(proxy); + let packed_proxy = openvpn::ProxySettings::Remote(proxy); - if let Err(error) = OpenVpnProxySettingsValidation::validate(&packed_proxy) { + if let Err(error) = openvpn::ProxySettingsValidation::validate(&packed_proxy) { panic!(error); } @@ -311,7 +311,7 @@ impl Tunnel { let tunnel_options = Self::get_tunnel_options()?; println!( "IPv6: {}", - if tunnel_options.enable_ipv6 { + if tunnel_options.generic.enable_ipv6 { "on" } else { "off" diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs index 63dee4e536..eeb0f5755a 100644 --- a/mullvad-daemon/src/lib.rs +++ b/mullvad-daemon/src/lib.rs @@ -29,6 +29,7 @@ use log::{debug, error, info, warn}; use mullvad_rpc::{AccountsProxy, AppVersionProxy, HttpHandle}; use mullvad_types::{ account::{AccountData, AccountToken}, + endpoint::MullvadEndpoint, location::GeoIpLocation, relay_constraints::{ Constraint, OpenVpnConstraints, RelayConstraintsUpdate, RelaySettings, RelaySettingsUpdate, @@ -42,10 +43,10 @@ use mullvad_types::{ use std::{mem, path::PathBuf, sync::mpsc, thread, time::Duration}; use talpid_core::{ mpsc::IntoSender, - tunnel_state_machine::{self, TunnelCommand, TunnelParameters, TunnelParametersGenerator}, + tunnel_state_machine::{self, TunnelCommand, TunnelParametersGenerator}, }; use talpid_types::{ - net::{OpenVpnProxySettings, TransportProtocol}, + net::{openvpn, TransportProtocol, TunnelParameters}, tunnel::{BlockReason, TunnelStateTransition}, }; @@ -58,6 +59,9 @@ error_chain! { DaemonIsAlreadyRunning { description("Another instance of the daemon is already running") } + UnsupportedTunnel { + description("Unsupported tunnel") + } ManagementInterfaceError(msg: &'static str) { description("Error in the management interface") display("Management interface error: {}", msg) @@ -340,24 +344,20 @@ impl Daemon { .map(|account_token| { match self.settings.get_relay_settings() { RelaySettings::CustomTunnelEndpoint(custom_relay) => custom_relay - .to_tunnel_endpoint() + .to_tunnel_parameters(self.settings.get_tunnel_options().clone()) .chain_err(|| "Custom tunnel endpoint could not be resolved"), RelaySettings::Normal(constraints) => self .relay_selector .get_tunnel_endpoint(&constraints, retry_attempt) .chain_err(|| "No valid relay servers match the current settings") - .map(|(relay, endpoint)| { + .and_then(|(relay, endpoint)| { self.last_generated_relay = Some(relay); - endpoint + self.create_tunnel_parameters(endpoint, account_token) }), } - .map(|endpoint| { + .map(|tunnel_params| { tunnel_parameters_tx - .send(TunnelParameters { - endpoint, - options: self.settings.get_tunnel_options().clone(), - username: account_token, - }) + .send(tunnel_params) .map_err(|_| Error::from("Tunnel parameters receiver stopped listening")) }) }); @@ -366,6 +366,26 @@ impl Daemon { } } + fn create_tunnel_parameters( + &self, + endpoint: MullvadEndpoint, + account_token: String, + ) -> Result<TunnelParameters> { + let tunnel_options = self.settings.get_tunnel_options().clone(); + match endpoint { + MullvadEndpoint::OpenVpn(endpoint) => Ok(openvpn::TunnelParameters { + config: openvpn::ConnectionConfig::new(endpoint, account_token, "-".to_string()), + options: tunnel_options.openvpn, + generic_options: tunnel_options.generic, + } + .into()), + MullvadEndpoint::Wireguard { + peer: _, + gateway: _, + } => Err(ErrorKind::UnsupportedTunnel.into()), + } + } + fn schedule_reconnect(&mut self, delay: Duration) { let tunnel_command_tx = self.tx.clone(); let (tx, rx) = mpsc::channel(); @@ -639,7 +659,7 @@ impl Daemon { fn on_set_openvpn_proxy( &mut self, tx: oneshot::Sender<::std::result::Result<(), settings::Error>>, - proxy: Option<OpenVpnProxySettings>, + proxy: Option<openvpn::ProxySettings>, ) { let constraints_result = match proxy { Some(_) => self.apply_proxy_constraints(), diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs index c68c8ff967..cea4d6a75d 100644 --- a/mullvad-daemon/src/management_interface.rs +++ b/mullvad-daemon/src/management_interface.rs @@ -30,7 +30,7 @@ use std::{ }; use talpid_core::mpsc::IntoSender; use talpid_ipc; -use talpid_types::{net::OpenVpnProxySettings, tunnel::TunnelStateTransition}; +use talpid_types::{net::openvpn, tunnel::TunnelStateTransition}; use uuid; /// FIXME(linus): This is here just because the futures crate has deprecated it and jsonrpc_core @@ -115,7 +115,7 @@ build_rpc_trait! { /// Sets proxy details for OpenVPN #[rpc(meta, name = "set_openvpn_proxy")] - fn set_openvpn_proxy(&self, Self::Metadata, Option<OpenVpnProxySettings>) -> BoxFuture<(), Error>; + fn set_openvpn_proxy(&self, Self::Metadata, Option<openvpn::ProxySettings>) -> BoxFuture<(), Error>; /// Set if IPv6 is enabled in the tunnel #[rpc(meta, name = "set_enable_ipv6")] @@ -194,7 +194,7 @@ pub enum ManagementCommand { /// Set proxy details for OpenVPN SetOpenVpnProxy( OneshotSender<Result<(), settings::Error>>, - Option<OpenVpnProxySettings>, + Option<openvpn::ProxySettings>, ), /// Set if IPv6 should be enabled in the tunnel SetEnableIpv6(OneshotSender<()>, bool), @@ -587,7 +587,7 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi fn set_openvpn_proxy( &self, _: Self::Metadata, - proxy: Option<OpenVpnProxySettings>, + proxy: Option<openvpn::ProxySettings>, ) -> BoxFuture<(), Error> { log::debug!("set_openvpn_proxy({:?})", proxy); let (tx, rx) = sync::oneshot::channel(); diff --git a/mullvad-daemon/src/relays.rs b/mullvad-daemon/src/relays.rs index 0dba9dd9a3..11c0d49930 100644 --- a/mullvad-daemon/src/relays.rs +++ b/mullvad-daemon/src/relays.rs @@ -4,6 +4,7 @@ use futures::Future; use mullvad_rpc::{HttpHandle, RelayListProxy}; use mullvad_types::{ + endpoint::{MullvadEndpoint, TunnelEndpointData}, location::Location, relay_constraints::{ Constraint, LocationConstraint, Match, OpenVpnConstraints, RelayConstraints, @@ -14,12 +15,11 @@ use mullvad_types::{ use serde_json; -use talpid_types::net::{TransportProtocol, TunnelEndpoint, TunnelEndpointData}; +use talpid_types::net::TransportProtocol; use std::{ fs::File, io, - net::IpAddr, path::{Path, PathBuf}, sync::{mpsc, Arc, Mutex, MutexGuard}, thread, @@ -196,7 +196,7 @@ impl RelaySelector { &mut self, constraints: &RelayConstraints, retry_attempt: u32, - ) -> Result<(Relay, TunnelEndpoint)> { + ) -> Result<(Relay, MullvadEndpoint)> { let preferred_constraints = Self::preferred_constraints(constraints, retry_attempt); if let Some((relay, endpoint)) = self.get_tunnel_endpoint_internal(&preferred_constraints) { debug!( @@ -264,7 +264,7 @@ impl RelaySelector { fn get_tunnel_endpoint_internal( &mut self, constraints: &RelayConstraints, - ) -> Option<(Relay, TunnelEndpoint)> { + ) -> Option<(Relay, MullvadEndpoint)> { let matching_relays: Vec<Relay> = self .lock_parsed_relays() .relays() @@ -280,10 +280,8 @@ impl RelaySelector { ); self.get_random_tunnel(&selected_relay.tunnels) .map(|tunnel_parameters| { - let endpoint = TunnelEndpoint { - address: IpAddr::V4(selected_relay.ipv4_addr_in), - tunnel: tunnel_parameters, - }; + let endpoint = tunnel_parameters + .to_mullvad_endpoint(selected_relay.ipv4_addr_in.into()); (selected_relay.clone(), endpoint) }) }) diff --git a/mullvad-ipc-client/src/lib.rs b/mullvad-ipc-client/src/lib.rs index 99f9ff6815..d7eaf653ef 100644 --- a/mullvad-ipc-client/src/lib.rs +++ b/mullvad-ipc-client/src/lib.rs @@ -12,15 +12,12 @@ use mullvad_types::{ location::GeoIpLocation, relay_constraints::{RelaySettings, RelaySettingsUpdate}, relay_list::RelayList, - settings::Settings, + settings::{Settings, TunnelOptions}, version::AppVersionInfo, }; use serde::{Deserialize, Serialize}; use std::{path::Path, sync::mpsc, thread, time::Duration}; -use talpid_types::{ - net::{OpenVpnProxySettings, TunnelOptions}, - tunnel::TunnelStateTransition, -}; +use talpid_types::{net::openvpn, tunnel::TunnelStateTransition}; pub use jsonrpc_client_core::{Error as RpcError, ErrorKind as RpcErrorKind}; @@ -196,7 +193,7 @@ impl DaemonRpcClient { self.call("set_openvpn_mssfix", &[mssfix]) } - pub fn set_openvpn_proxy(&mut self, proxy: Option<OpenVpnProxySettings>) -> Result<()> { + pub fn set_openvpn_proxy(&mut self, proxy: Option<openvpn::ProxySettings>) -> Result<()> { self.call("set_openvpn_proxy", &[proxy]) } diff --git a/mullvad-types/Cargo.toml b/mullvad-types/Cargo.toml index 8239696b9b..c0202ed1b6 100644 --- a/mullvad-types/Cargo.toml +++ b/mullvad-types/Cargo.toml @@ -14,6 +14,7 @@ error-chain = "0.12" log = "0.4" regex = "1" lazy_static = "1.1.0" +ipnetwork = "0.13" talpid-types = { path = "../talpid-types" } mullvad-paths = { path = "../mullvad-paths" } diff --git a/mullvad-types/src/custom_tunnel.rs b/mullvad-types/src/custom_tunnel.rs index 475dfa2eaf..83211ba64a 100644 --- a/mullvad-types/src/custom_tunnel.rs +++ b/mullvad-types/src/custom_tunnel.rs @@ -1,40 +1,77 @@ +use crate::settings::TunnelOptions; use serde::{Deserialize, Serialize}; use std::{ fmt, - net::{IpAddr, ToSocketAddrs}, + net::{IpAddr, SocketAddr, ToSocketAddrs}, }; -use talpid_types::net::{TunnelEndpoint, TunnelEndpointData}; +use talpid_types::net::{openvpn, wireguard, TunnelParameters}; error_chain! { errors { InvalidHost(host: String) { display("Invalid host: {}", host) } + Unsupported { + description("Tunnel type not supported") + } } } #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] pub struct CustomTunnelEndpoint { - pub host: String, - pub tunnel: TunnelEndpointData, + host: String, + config: ConnectionConfig, } impl CustomTunnelEndpoint { - pub fn to_tunnel_endpoint(&self) -> Result<TunnelEndpoint> { - Ok(TunnelEndpoint { - address: resolve_to_ip(&self.host)?, - tunnel: self.tunnel.clone(), - }) + pub fn new(host: String, config: ConnectionConfig) -> Self { + Self { host, config } + } + + pub fn to_tunnel_parameters(&self, tunnel_options: TunnelOptions) -> Result<TunnelParameters> { + let ip = resolve_to_ip(&self.host)?; + let mut config = self.config.clone(); + config.set_ip(ip); + + let parameters = match config { + ConnectionConfig::OpenVpn(config) => openvpn::TunnelParameters { + config, + options: tunnel_options.openvpn.clone(), + generic_options: tunnel_options.generic.clone(), + } + .into(), + ConnectionConfig::Wireguard(connection) => wireguard::TunnelParameters { + connection, + options: tunnel_options.wireguard.clone(), + generic_options: tunnel_options.generic.clone(), + } + .into(), + }; + Ok(parameters) } } impl fmt::Display for CustomTunnelEndpoint { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{} over {}", self.host, self.tunnel) + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match &self.config { + ConnectionConfig::OpenVpn(config) => write!( + f, + "OpenVPN relay - {}:{} {}", + self.host, + config.endpoint.address.port(), + config.endpoint.protocol + ), + ConnectionConfig::Wireguard(connection) => write!( + f, + "WireGuard relay - {} with public key {}", + connection.peer.endpoint, connection.peer.public_key + ), + } } } + /// Does a DNS lookup if the host isn't an IP. /// Returns the first IPv4 address if one exists, otherwise the first IPv6 address. /// Rust only provides means to resolve a socket addr, not just a host, for some reason. So @@ -53,3 +90,25 @@ fn resolve_to_ip(host: &str) -> Result<IpAddr> { }) .ok_or_else(|| ErrorKind::InvalidHost(host.to_owned()).into()) } + +#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] +#[serde(rename = "connection_config")] +pub enum ConnectionConfig { + #[serde(rename = "openvpn")] + OpenVpn(openvpn::ConnectionConfig), + #[serde(rename = "wireguard")] + Wireguard(wireguard::ConnectionConfig), +} + +impl ConnectionConfig { + fn set_ip(&mut self, ip: IpAddr) { + match self { + ConnectionConfig::OpenVpn(config) => { + config.endpoint.address = SocketAddr::new(ip, config.endpoint.address.port()); + } + ConnectionConfig::Wireguard(config) => { + config.peer.endpoint = SocketAddr::new(ip, config.peer.endpoint.port()) + } + } + } +} diff --git a/mullvad-types/src/endpoint.rs b/mullvad-types/src/endpoint.rs new file mode 100644 index 0000000000..311ca267ba --- /dev/null +++ b/mullvad-types/src/endpoint.rs @@ -0,0 +1,111 @@ +use ipnetwork::IpNetwork; +use serde::{Deserialize, Serialize}; +use std::{ + fmt, + net::{IpAddr, SocketAddr}, +}; +use talpid_types::net::{wireguard, Endpoint, TransportProtocol}; + +use crate::relay_list::{OpenVpnEndpointData, WireguardEndpointData}; + +/// Contains server data needed to conenct to a single mullvad endpoint +#[derive(Debug, Clone)] +pub enum MullvadEndpoint { + OpenVpn(Endpoint), + Wireguard { + peer: wireguard::PeerConfig, + gateway: IpAddr, + }, +} + +impl MullvadEndpoint { + /// Returns this tunnel endpoint as an `Endpoint`. + pub fn to_endpoint(&self) -> Endpoint { + match self { + MullvadEndpoint::OpenVpn(endpoint) => *endpoint, + MullvadEndpoint::Wireguard { peer, gateway: _ } => Endpoint::new( + peer.endpoint.ip(), + peer.endpoint.port(), + TransportProtocol::Udp, + ), + } + } +} +/// TunnelEndpointData contains data required to connect to a given tunnel endpoint. +/// Different endpoint types can require different types of data. +#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)] +pub enum TunnelEndpointData { + /// Extra parameters for an OpenVPN tunnel endpoint. + #[serde(rename = "openvpn")] + OpenVpn(OpenVpnEndpointData), + /// Extra parameters for a Wireguard tunnel endpoint. + #[serde(rename = "wireguard")] + Wireguard(WireguardEndpointData), +} +impl From<OpenVpnEndpointData> for TunnelEndpointData { + fn from(endpoint_data: OpenVpnEndpointData) -> TunnelEndpointData { + TunnelEndpointData::OpenVpn(endpoint_data) + } +} + +impl From<WireguardEndpointData> for TunnelEndpointData { + fn from(endpoint_data: WireguardEndpointData) -> TunnelEndpointData { + TunnelEndpointData::Wireguard(endpoint_data) + } +} + +impl fmt::Display for TunnelEndpointData { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + match self { + TunnelEndpointData::OpenVpn(openvpn_data) => { + write!(f, "OpenVPN ")?; + openvpn_data.fmt(f) + } + TunnelEndpointData::Wireguard(wireguard_data) => { + write!(f, "Wireguard ")?; + wireguard_data.fmt(f) + } + } + } +} + +impl TunnelEndpointData { + pub fn to_mullvad_endpoint(self, host: IpAddr) -> MullvadEndpoint { + match self { + TunnelEndpointData::OpenVpn(metadata) => { + MullvadEndpoint::OpenVpn(Endpoint::new(host, metadata.port, metadata.protocol)) + } + TunnelEndpointData::Wireguard(metadata) => { + let peer_config = wireguard::PeerConfig { + public_key: metadata.peer_public_key, + endpoint: SocketAddr::new(host, metadata.port), + allowed_ips: all_of_the_internet(), + }; + MullvadEndpoint::Wireguard { + peer: peer_config, + gateway: metadata.gateway, + } + } + } + } + pub fn port(&self) -> u16 { + match self { + TunnelEndpointData::OpenVpn(metadata) => metadata.port, + TunnelEndpointData::Wireguard(metadata) => metadata.port, + } + } + + pub fn transport_protocol(&self) -> TransportProtocol { + match self { + TunnelEndpointData::OpenVpn(metadata) => metadata.protocol, + TunnelEndpointData::Wireguard(_) => TransportProtocol::Udp, + } + } +} + +fn all_of_the_internet() -> Vec<IpNetwork> { + vec![ + "0.0.0.0/0".parse().expect("Failed to parse ipv6 network"), + "::0/0".parse().expect("Failed to parse ipv6 network"), + ] +} diff --git a/mullvad-types/src/lib.rs b/mullvad-types/src/lib.rs index a972719460..11e6ac4005 100644 --- a/mullvad-types/src/lib.rs +++ b/mullvad-types/src/lib.rs @@ -11,6 +11,7 @@ extern crate error_chain; pub mod account; pub mod auth_failed; +pub mod endpoint; pub mod location; pub mod relay_constraints; pub mod relay_list; diff --git a/mullvad-types/src/relay_constraints.rs b/mullvad-types/src/relay_constraints.rs index 52f22a9229..41305115c2 100644 --- a/mullvad-types/src/relay_constraints.rs +++ b/mullvad-types/src/relay_constraints.rs @@ -1,10 +1,11 @@ use crate::{ location::{CityCode, CountryCode, Hostname}, + relay_list::{OpenVpnEndpointData, WireguardEndpointData}, CustomTunnelEndpoint, }; use serde::{Deserialize, Serialize}; use std::fmt; -use talpid_types::net::{OpenVpnEndpointData, TransportProtocol, WireguardEndpointData}; +use talpid_types::net::TransportProtocol; pub trait Match<T> { diff --git a/mullvad-types/src/relay_list.rs b/mullvad-types/src/relay_list.rs index bc5d66eb80..1e16dcb6ba 100644 --- a/mullvad-types/src/relay_list.rs +++ b/mullvad-types/src/relay_list.rs @@ -1,7 +1,10 @@ use crate::location::{CityCode, CountryCode, Location}; use serde::{Deserialize, Serialize}; -use std::net::Ipv4Addr; -use talpid_types::net::{OpenVpnEndpointData, WireguardEndpointData}; +use std::{ + fmt, + net::{IpAddr, Ipv4Addr}, +}; +use talpid_types::net::{wireguard, TransportProtocol}; #[derive(Debug, Clone, Deserialize, Serialize)] @@ -63,3 +66,45 @@ impl RelayTunnels { self.wireguard.clear(); } } + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Deserialize, Serialize)] +pub struct OpenVpnEndpointData { + pub port: u16, + pub protocol: TransportProtocol, +} + +impl fmt::Display for OpenVpnEndpointData { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "{} port {}", self.protocol, self.port) + } +} + +#[derive(Clone, Eq, PartialEq, Hash, Deserialize, Serialize)] +pub struct WireguardEndpointData { + /// Port to connect to + pub port: u16, + /// Peer's IP address + pub gateway: IpAddr, + /// The peer's public key + pub peer_public_key: wireguard::PublicKey, +} + +impl fmt::Debug for WireguardEndpointData { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + f.debug_struct(&"WireguardEndpointData") + .field("port", &self.port) + .field("gateway", &self.gateway) + .field("peer_public_key", &self.peer_public_key) + .finish() + } +} + +impl fmt::Display for WireguardEndpointData { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!( + f, + "gateway {} port {} peer_public_key {}", + self.gateway, self.port, self.peer_public_key, + ) + } +} diff --git a/mullvad-types/src/settings.rs b/mullvad-types/src/settings.rs index 70f5d6b35e..cfc445b3c9 100644 --- a/mullvad-types/src/settings.rs +++ b/mullvad-types/src/settings.rs @@ -5,7 +5,7 @@ use log::{debug, info}; use serde::{Deserialize, Serialize}; use serde_json; use std::{fs::File, io, path::PathBuf}; -use talpid_types::net::{OpenVpnProxySettings, OpenVpnProxySettingsValidation, TunnelOptions}; +use talpid_types::net::{openvpn, wireguard, GenericTunnelOptions}; error_chain! { errors { @@ -201,9 +201,9 @@ impl Settings { } } - pub fn set_openvpn_proxy(&mut self, proxy: Option<OpenVpnProxySettings>) -> Result<bool> { + pub fn set_openvpn_proxy(&mut self, proxy: Option<openvpn::ProxySettings>) -> Result<bool> { if let Some(ref settings) = proxy { - if let Err(validation_error) = OpenVpnProxySettingsValidation::validate(settings) { + if let Err(validation_error) = openvpn::ProxySettingsValidation::validate(settings) { bail!(ErrorKind::InvalidProxyData(validation_error)); } } @@ -217,8 +217,8 @@ impl Settings { } pub fn set_enable_ipv6(&mut self, enable_ipv6: bool) -> Result<bool> { - if self.tunnel_options.enable_ipv6 != enable_ipv6 { - self.tunnel_options.enable_ipv6 = enable_ipv6; + if self.tunnel_options.generic.enable_ipv6 != enable_ipv6 { + self.tunnel_options.generic.enable_ipv6 = enable_ipv6; self.save().map(|_| true) } else { Ok(false) @@ -229,3 +229,29 @@ impl Settings { &self.tunnel_options } } + +/// TunnelOptions holds configuration data that applies to all kinds of tunnels. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(default)] +pub struct TunnelOptions { + /// openvpn holds OpenVPN specific tunnel options. + pub openvpn: openvpn::TunnelOptions, + /// Contains wireguard tunnel options. + pub wireguard: wireguard::TunnelOptions, + /// Contains generic tunnel options that may apply to more than a single tunnel type. + pub generic: GenericTunnelOptions, +} + +impl Default for TunnelOptions { + fn default() -> Self { + TunnelOptions { + openvpn: openvpn::TunnelOptions::default(), + wireguard: wireguard::TunnelOptions { + mtu: None, + #[cfg(target_os = "linux")] + fwmark: 78_78_78, + }, + generic: GenericTunnelOptions { enable_ipv6: false }, + } + } +} diff --git a/talpid-core/src/process/openvpn.rs b/talpid-core/src/process/openvpn.rs index 104471db2b..a55d5d3453 100644 --- a/talpid-core/src/process/openvpn.rs +++ b/talpid-core/src/process/openvpn.rs @@ -61,7 +61,7 @@ pub struct OpenVpnCommand { iproute_bin: Option<OsString>, plugin: Option<(PathBuf, Vec<String>)>, log: Option<PathBuf>, - tunnel_options: net::OpenVpnTunnelOptions, + tunnel_options: net::openvpn::TunnelOptions, tunnel_alias: Option<OsString>, enable_ipv6: bool, } @@ -81,7 +81,7 @@ impl OpenVpnCommand { iproute_bin: None, plugin: None, log: None, - tunnel_options: net::OpenVpnTunnelOptions::default(), + tunnel_options: net::openvpn::TunnelOptions::default(), tunnel_alias: None, enable_ipv6: true, } @@ -150,7 +150,7 @@ impl OpenVpnCommand { } /// Sets extra options - pub fn tunnel_options(&mut self, tunnel_options: &net::OpenVpnTunnelOptions) -> &mut Self { + pub fn tunnel_options(&mut self, tunnel_options: &net::openvpn::TunnelOptions) -> &mut Self { self.tunnel_options = tunnel_options.clone(); self } @@ -275,7 +275,7 @@ impl OpenVpnCommand { fn proxy_arguments(&self) -> Vec<String> { let mut args = vec![]; match self.tunnel_options.proxy { - Some(net::OpenVpnProxySettings::Local(ref local_proxy)) => { + Some(net::openvpn::ProxySettings::Local(ref local_proxy)) => { args.push("--socks-proxy".to_owned()); args.push("127.0.0.1".to_owned()); args.push(local_proxy.port.to_string()); @@ -284,7 +284,7 @@ impl OpenVpnCommand { args.push("255.255.255.255".to_owned()); args.push("net_gateway".to_owned()); } - Some(net::OpenVpnProxySettings::Remote(ref remote_proxy)) => { + Some(net::openvpn::ProxySettings::Remote(ref remote_proxy)) => { args.push("--socks-proxy".to_owned()); args.push(remote_proxy.address.ip().to_string()); args.push(remote_proxy.address.port().to_string()); diff --git a/talpid-core/src/security/macos/mod.rs b/talpid-core/src/security/macos/mod.rs index b4d300e66f..cc14fbfd6e 100644 --- a/talpid-core/src/security/macos/mod.rs +++ b/talpid-core/src/security/macos/mod.rs @@ -1,6 +1,9 @@ use super::{NetworkSecurityT, SecurityPolicy}; use pfctl::FilterRuleAction; -use std::{env, net::Ipv4Addr}; +use std::{ + env, + net::{Ipv4Addr, SocketAddr}, +}; use talpid_types::net; mod dns; diff --git a/talpid-core/src/tunnel/mod.rs b/talpid-core/src/tunnel/mod.rs index 4011db3e84..c0f1abcde2 100644 --- a/talpid-core/src/tunnel/mod.rs +++ b/talpid-core/src/tunnel/mod.rs @@ -6,7 +6,9 @@ use std::{ path::{Path, PathBuf}, }; -use talpid_types::net::{TunnelEndpoint, TunnelEndpointData, TunnelOptions}; +#[cfg(unix)] +use talpid_types::net::wireguard as wireguard_types; +use talpid_types::net::{openvpn as openvpn_types, GenericTunnelOptions, TunnelParameters}; /// A module for all OpenVPN related tunnel management. pub mod openvpn; @@ -113,10 +115,8 @@ impl TunnelMonitor { /// Creates a new `TunnelMonitor` that connects to the given remote and notifies `on_event` /// on tunnel state changes. pub fn start<L>( - tunnel_endpoint: TunnelEndpoint, - tunnel_options: &TunnelOptions, + tunnel_parameters: &TunnelParameters, tunnel_alias: Option<OsString>, - username: &str, log: Option<PathBuf>, resource_dir: &Path, on_event: L, @@ -124,60 +124,46 @@ impl TunnelMonitor { where L: Fn(TunnelEvent) + Send + Sync + 'static, { - Self::ensure_ipv6_can_be_used_if_enabled(tunnel_options)?; - match &tunnel_endpoint.tunnel { - TunnelEndpointData::OpenVpn(_) => Self::start_openvpn_tunnel( - tunnel_endpoint, - tunnel_options, - tunnel_alias, - username, - log, - resource_dir, - on_event, - ), + Self::ensure_ipv6_can_be_used_if_enabled(&tunnel_parameters.get_generic_options())?; + + match tunnel_parameters { + TunnelParameters::OpenVpn(config) => { + Self::start_openvpn_tunnel(&config, tunnel_alias, log, resource_dir, on_event) + } #[cfg(unix)] - TunnelEndpointData::Wireguard(_) => { - Self::start_wireguard_tunnel(tunnel_endpoint, tunnel_options, log, on_event) + TunnelParameters::Wireguard(config) => { + Self::start_wireguard_tunnel(&config, log, on_event) } #[cfg(windows)] - TunnelEndpointData::Wireguard(_) => bail!(ErrorKind::UnsupportedPlatform), + TunnelParameters::Wireguard(_) => bail!(ErrorKind::UnsupportedPlatform), } } #[cfg(unix)] fn start_wireguard_tunnel<L>( - tunnel_endpoint: TunnelEndpoint, - tunnel_options: &TunnelOptions, + params: &wireguard_types::TunnelParameters, log: Option<PathBuf>, on_event: L, ) -> Result<Self> where L: Fn(TunnelEvent) + Send + Sync + 'static, { - let TunnelEndpoint { address, tunnel } = tunnel_endpoint; - let data = match tunnel { - TunnelEndpointData::Wireguard(data) => data, - _ => unreachable!("expected wireguard endpoint data"), - }; - + let config = wireguard::config::Config::from_parameters(¶ms) + .chain_err(|| ErrorKind::TunnelMonitoringError)?; let monitor = wireguard::WireguardMonitor::start( - address, - data, - tunnel_options, + &config, log.as_ref().map(|p| p.as_path()), on_event, ) - .chain_err(|| ErrorKind::TunnelMonitoringError)?; + .chain_err(|| ErrorKind::TunnelMonitorSetUpError)?; Ok(TunnelMonitor { monitor: InternalTunnelMonitor::Wireguard(monitor), }) } fn start_openvpn_tunnel<L>( - tunnel_endpoint: TunnelEndpoint, - tunnel_options: &TunnelOptions, + config: &openvpn_types::TunnelParameters, tunnel_alias: Option<OsString>, - username: &str, log: Option<PathBuf>, resource_dir: &Path, on_event: L, @@ -185,22 +171,15 @@ impl TunnelMonitor { where L: Fn(TunnelEvent) + Send + Sync + 'static, { - let monitor = openvpn::OpenVpnMonitor::start( - on_event, - tunnel_endpoint.to_endpoint(), - tunnel_options, - tunnel_alias, - log, - resource_dir, - username, - ) - .chain_err(|| ErrorKind::TunnelMonitorSetUpError)?; + let monitor = + openvpn::OpenVpnMonitor::start(on_event, config, tunnel_alias, log, resource_dir) + .chain_err(|| ErrorKind::TunnelMonitorSetUpError)?; Ok(TunnelMonitor { monitor: InternalTunnelMonitor::OpenVpn(monitor), }) } - fn ensure_ipv6_can_be_used_if_enabled(tunnel_options: &TunnelOptions) -> Result<()> { + fn ensure_ipv6_can_be_used_if_enabled(tunnel_options: &GenericTunnelOptions) -> Result<()> { if tunnel_options.enable_ipv6 && !is_ipv6_enabled_in_os() { bail!(ErrorKind::EnableIpv6Error); } else { diff --git a/talpid-core/src/tunnel/openvpn.rs b/talpid-core/src/tunnel/openvpn.rs index fadb035966..38aee11755 100644 --- a/talpid-core/src/tunnel/openvpn.rs +++ b/talpid-core/src/tunnel/openvpn.rs @@ -23,7 +23,7 @@ use std::{ time::Duration, }; use talpid_ipc; -use talpid_types::net::{Endpoint, OpenVpnProxySettings, TunnelOptions}; +use talpid_types::net::openvpn; #[cfg(target_os = "linux")] use which; @@ -107,20 +107,19 @@ impl OpenVpnMonitor<OpenVpnCommand> { /// path. pub fn start<L>( on_event: L, - endpoint: Endpoint, - tunnel_options: &TunnelOptions, + params: &openvpn::TunnelParameters, tunnel_alias: Option<OsString>, log_path: Option<PathBuf>, resource_dir: &Path, - username: &str, ) -> Result<Self> where L: Fn(TunnelEvent) + Send + Sync + 'static, { - let user_pass_file = Self::create_credentials_file(username, "-") - .chain_err(|| ErrorKind::CredentialsWriteError)?; + let user_pass_file = + Self::create_credentials_file(¶ms.config.username, ¶ms.config.password) + .chain_err(|| ErrorKind::CredentialsWriteError)?; - let proxy_auth_file = Self::create_proxy_auth_file(&tunnel_options.openvpn.proxy) + let proxy_auth_file = Self::create_proxy_auth_file(¶ms.options.proxy) .chain_err(|| ErrorKind::CredentialsWriteError)?; @@ -147,9 +146,8 @@ impl OpenVpnMonitor<OpenVpnCommand> { } }; let cmd = Self::create_openvpn_cmd( - endpoint, + params, tunnel_alias, - &tunnel_options, user_pass_file.as_ref(), match proxy_auth_file { Some(ref file) => Some(file.as_ref()), @@ -293,9 +291,9 @@ impl<C: OpenVpnBuilder> OpenVpnMonitor<C> { } fn create_proxy_auth_file( - proxy: &Option<OpenVpnProxySettings>, + proxy: &Option<openvpn::ProxySettings>, ) -> ::std::result::Result<Option<mktemp::TempFile>, io::Error> { - if let Some(OpenVpnProxySettings::Remote(ref remote_proxy)) = proxy { + if let Some(openvpn::ProxySettings::Remote(ref remote_proxy)) = proxy { if let Some(ref proxy_auth) = remote_proxy.auth { return Ok(Some(Self::create_credentials_file( &proxy_auth.username, @@ -339,9 +337,8 @@ impl<C: OpenVpnBuilder> OpenVpnMonitor<C> { } fn create_openvpn_cmd( - remote: Endpoint, + params: &openvpn::TunnelParameters, tunnel_alias: Option<OsString>, - options: &TunnelOptions, user_pass_file: &Path, proxy_auth_file: Option<&Path>, resource_dir: &Path, @@ -356,10 +353,10 @@ impl<C: OpenVpnBuilder> OpenVpnMonitor<C> { .compat() .chain_err(|| ErrorKind::IpRouteNotFound)?, ); - cmd.remote(remote) + cmd.remote(params.config.get_tunnel_endpoint().endpoint) .user_pass(user_pass_file) - .tunnel_options(&options.openvpn) - .enable_ipv6(options.enable_ipv6) + .tunnel_options(¶ms.options) + .enable_ipv6(params.generic_options.enable_ipv6) .tunnel_alias(tunnel_alias) .ca(resource_dir.join("ca.crt")); if let Some(proxy_auth_file) = proxy_auth_file { diff --git a/talpid-core/src/tunnel/wireguard/config.rs b/talpid-core/src/tunnel/wireguard/config.rs index a99e46994f..a15f377165 100644 --- a/talpid-core/src/tunnel/wireguard/config.rs +++ b/talpid-core/src/tunnel/wireguard/config.rs @@ -1,60 +1,83 @@ -use super::{ErrorKind, Result}; -use ipnetwork::IpNetwork; -use std::{ - borrow::Cow, - ffi::CString, - net::{IpAddr, SocketAddr}, -}; -use talpid_types::net::{TunnelOptions, WgPrivateKey, WgPublicKey, WireguardEndpointData}; +use std::{borrow::Cow, ffi::CString, net::IpAddr}; +use talpid_types::net::{wireguard, GenericTunnelOptions}; pub struct Config { - pub interface: TunnelConfig, + pub tunnel: wireguard::TunnelConfig, + pub peers: Vec<wireguard::PeerConfig>, pub gateway: IpAddr, - pub preferred_name: Option<String>, + pub mtu: u16, + #[cfg(target_os = "linux")] + pub fwmark: i32, +} + +/// Smallest MTU that supports IPv6 +const SMALLEST_IPV6_MTU: u16 = 1420; +const DEFAULT_MTU: u16 = SMALLEST_IPV6_MTU; + +error_chain! { + errors { + InvalidTunnelIpError { + description("No valid tunnel IP"), + } + + InvalidPeerIpError { + description("Supplied peer has no valid IPs") + } + + NoPeersSuppliedError{ + description("No peers supplied") + } + } } -// Smallest MTU that supports IPv6 -const MIN_IPV6_MTU: u16 = 1420; -const DEFAULT_MTU: u16 = MIN_IPV6_MTU; impl Config { - pub fn from_data( - ip: IpAddr, - data: WireguardEndpointData, - options: &TunnelOptions, + pub fn from_parameters(params: &wireguard::TunnelParameters) -> Result<Config> { + let tunnel = params.connection.tunnel.clone(); + let peer = vec![params.connection.peer.clone()]; + Self::new( + tunnel, + peer, + params.connection.gateway, + ¶ms.options, + ¶ms.generic_options, + ) + } + + pub fn new( + mut tunnel: wireguard::TunnelConfig, + mut peers: Vec<wireguard::PeerConfig>, + gateway: IpAddr, + wg_options: &wireguard::TunnelOptions, + generic_options: &GenericTunnelOptions, ) -> Result<Config> { - let private_key = match data.client_private_key { - Some(private_key) => private_key, - None => bail!(ErrorKind::NoKeyError), - }; + ensure!(peers.is_empty(), ErrorKind::NoPeersSuppliedError); + let mtu = wg_options.mtu.unwrap_or(DEFAULT_MTU); + let is_ipv6_enabled = mtu >= SMALLEST_IPV6_MTU && generic_options.enable_ipv6; - let mtu = options.wireguard.mtu.unwrap_or(DEFAULT_MTU); - let ipv6_enabled = options.enable_ipv6 && mtu >= MIN_IPV6_MTU; - let peer = PeerConfig { - public_key: data.peer_public_key, - allowed_ips: all_of_the_internet() - .into_iter() - .filter(|ip| ip.is_ipv4() || ipv6_enabled) - .collect(), - endpoint: SocketAddr::new(ip, data.port), - }; + for peer in &mut peers { + peer.allowed_ips = peer + .allowed_ips + .iter() + .cloned() + .filter(|ip| ip.is_ipv4() || is_ipv6_enabled) + .collect(); + ensure!(peer.allowed_ips.is_empty(), ErrorKind::InvalidPeerIpError); + } - let tunnel_config = TunnelConfig { - private_key, - addresses: data - .addresses - .into_iter() - .filter(|ip| ip.is_ipv4() || ipv6_enabled) - .collect(), - mtu, - #[cfg(target_os = "linux")] - fwmark: options.wireguard.fwmark, - peers: vec![peer], - }; + tunnel.addresses = tunnel + .addresses + .into_iter() + .filter(|ip| ip.is_ipv4() || is_ipv6_enabled) + .collect(); + ensure!(tunnel.addresses.is_empty(), ErrorKind::InvalidTunnelIpError); Ok(Config { - interface: tunnel_config, - gateway: data.gateway, - preferred_name: Some("talpid".to_string()), + tunnel, + peers, + gateway, + mtu, + #[cfg(target_os = "linux")] + fwmark: wg_options.fwmark, }) } @@ -63,20 +86,17 @@ impl Config { // the order of insertion matters, public key entry denotes a new peer entry let mut wg_conf = WgConfigBuffer::new(); wg_conf - .add( - "private_key", - self.interface.private_key.as_bytes().as_ref(), - ) + .add("private_key", self.tunnel.private_key.as_bytes().as_ref()) .add("listen_port", "0"); #[cfg(target_os = "linux")] { - wg_conf.add("fwmark", self.interface.fwmark.to_string().as_str()); + wg_conf.add("fwmark", self.fwmark.to_string().as_str()); } wg_conf.add("replace_peers", "true"); - for peer in &self.interface.peers { + for peer in &self.peers { wg_conf .add("public_key", peer.public_key.as_bytes().as_ref()) .add("endpoint", peer.endpoint.to_string().as_str()) @@ -91,29 +111,6 @@ impl Config { } } -pub struct PeerConfig { - pub public_key: WgPublicKey, - pub allowed_ips: Vec<IpNetwork>, - pub endpoint: SocketAddr, -} - -pub struct TunnelConfig { - pub private_key: WgPrivateKey, - pub addresses: Vec<IpAddr>, - #[cfg(target_os = "linux")] - pub fwmark: i32, - pub mtu: u16, - pub peers: Vec<PeerConfig>, -} - - -fn all_of_the_internet() -> Vec<IpNetwork> { - vec![ - "::0/0".parse().expect("Failed to parse ipv6 network"), - "0.0.0.0/0".parse().expect("Failed to parse ipv4 network"), - ] -} - pub enum ConfValue<'a> { String(&'a str), Bytes(&'a [u8]), diff --git a/talpid-core/src/tunnel/wireguard/mod.rs b/talpid-core/src/tunnel/wireguard/mod.rs index c74dc8d41e..cc60f71c38 100644 --- a/talpid-core/src/tunnel/wireguard/mod.rs +++ b/talpid-core/src/tunnel/wireguard/mod.rs @@ -1,8 +1,7 @@ use self::config::Config; use super::{TunnelEvent, TunnelMetadata}; use crate::routing; -use std::{net::IpAddr, path::Path, sync::mpsc}; -use talpid_types::net::{TunnelOptions, WireguardEndpointData}; +use std::{path::Path, sync::mpsc}; pub mod config; mod ping_monitor; @@ -15,6 +14,10 @@ const PING_TIMEOUT: u16 = 5; error_chain! { errors { + /// Config error + ConfigError{ + description("Invalid configuration") + } /// Failed to setup a tunnel device SetupTunnelDeviceError { description("Failed to create tunnel device") @@ -64,13 +67,10 @@ pub struct WireguardMonitor { impl WireguardMonitor { pub fn start<F: Fn(TunnelEvent) + Send + Sync + 'static>( - address: IpAddr, - data: WireguardEndpointData, - options: &TunnelOptions, + config: &Config, log_path: Option<&Path>, on_event: F, ) -> Result<WireguardMonitor> { - let config = Config::from_data(address, data.clone(), options)?; let tunnel = Box::new(WgGoTunnel::start_tunnel(&config, log_path)?); let router = routing::RouteManager::new().chain_err(|| ErrorKind::SetupRoutingError)?; let event_callback = Box::new(on_event); @@ -84,7 +84,7 @@ impl WireguardMonitor { }; monitor.setup_routing(&config)?; monitor.start_pinger(&config); - monitor.tunnel_up(data); + monitor.tunnel_up(&config); Ok(monitor) } @@ -111,7 +111,6 @@ impl WireguardMonitor { fn setup_routing(&mut self, config: &Config) -> Result<()> { let iface_name = self.tunnel.get_interface_name(); let mut routes: Vec<_> = config - .interface .peers .iter() .flat_map(|peer| peer.allowed_ips.iter()) @@ -129,11 +128,12 @@ impl WireguardMonitor { .router .get_default_route_node() .chain_err(|| ErrorKind::SetupRoutingError)?; + // route endpoints with specific routes - for peer in config.interface.peers.iter() { + for peer in config.peers.iter() { let default_route = routing::Route::new( - peer.endpoint.ip().clone().into(), - routing::NetNode::Address(default_node.clone()), + peer.endpoint.ip().into(), + routing::NetNode::Address(default_node), ); routes.push(default_route); } @@ -142,7 +142,7 @@ impl WireguardMonitor { let required_routes = routing::RequiredRoutes { routes, #[cfg(target_os = "linux")] - fwmark: Some(config.interface.fwmark.to_string()), + fwmark: Some(config.fwmark.to_string()), }; self.router .add_routes(required_routes) @@ -162,12 +162,12 @@ impl WireguardMonitor { ) } - fn tunnel_up(&self, data: WireguardEndpointData) { + fn tunnel_up(&self, config: &Config) { let interface_name = self.tunnel.get_interface_name(); let metadata = TunnelMetadata { interface: interface_name.to_string(), - ips: data.addresses, - gateway: data.gateway, + ips: config.tunnel.addresses.clone(), + gateway: config.gateway, }; (self.event_callback)(TunnelEvent::Up(metadata)); } diff --git a/talpid-core/src/tunnel/wireguard/wireguard_go.rs b/talpid-core/src/tunnel/wireguard/wireguard_go.rs index 32d82889cc..e9531e88be 100644 --- a/talpid-core/src/tunnel/wireguard/wireguard_go.rs +++ b/talpid-core/src/tunnel/wireguard/wireguard_go.rs @@ -20,7 +20,7 @@ impl WgGoTunnel { let mut tunnel_device = TunnelDevice::new().chain_err(|| ErrorKind::SetupTunnelDeviceError)?; - for ip in config.interface.addresses.iter() { + for ip in config.tunnel.addresses.iter() { tunnel_device .set_ip(*ip) .chain_err(|| ErrorKind::SetupTunnelDeviceError)?; @@ -40,7 +40,7 @@ impl WgGoTunnel { let handle = unsafe { wgTurnOnWithFd( iface_name.as_ptr(), - config.interface.mtu as i64, + config.mtu as i64, wg_config_str.as_ptr(), tunnel_device.as_raw_fd(), log_file.as_raw_fd(), diff --git a/talpid-core/src/tunnel_state_machine/connected_state.rs b/talpid-core/src/tunnel_state_machine/connected_state.rs index 4a65e7fadd..b5a88c7181 100644 --- a/talpid-core/src/tunnel_state_machine/connected_state.rs +++ b/talpid-core/src/tunnel_state_machine/connected_state.rs @@ -4,14 +4,14 @@ use futures::{ Async, Future, Stream, }; use talpid_types::{ - net::{Endpoint, OpenVpnProxySettings, TransportProtocol}, + net::{Endpoint, TunnelParameters}, tunnel::BlockReason, }; use super::{ AfterDisconnect, BlockedState, ConnectingState, DisconnectingState, EventConsequence, Result, - ResultExt, SharedTunnelStateValues, TunnelCommand, TunnelParameters, TunnelState, - TunnelStateTransition, TunnelStateWrapper, + ResultExt, SharedTunnelStateValues, TunnelCommand, TunnelState, TunnelStateTransition, + TunnelStateWrapper, }; use crate::{ security::SecurityPolicy, @@ -48,17 +48,7 @@ impl ConnectedState { fn set_security_policy(&self, shared_values: &mut SharedTunnelStateValues) -> Result<()> { // If a proxy is specified we need to pass it on as the peer endpoint. - let peer_endpoint = match self.tunnel_parameters.options.openvpn.proxy { - Some(OpenVpnProxySettings::Local(ref local_proxy)) => Endpoint { - address: local_proxy.peer, - protocol: TransportProtocol::Tcp, - }, - Some(OpenVpnProxySettings::Remote(ref remote_proxy)) => Endpoint { - address: remote_proxy.address, - protocol: TransportProtocol::Tcp, - }, - _ => self.tunnel_parameters.endpoint.to_endpoint(), - }; + let peer_endpoint = self.get_endpoint_from_params(); let policy = SecurityPolicy::Connected { peer_endpoint, @@ -71,6 +61,16 @@ impl ConnectedState { .chain_err(|| "Failed to apply security policy for connected state") } + fn get_endpoint_from_params(&self) -> Endpoint { + match self.tunnel_parameters { + TunnelParameters::OpenVpn(ref config) => match config.options.proxy { + Some(ref proxy_settings) => proxy_settings.get_endpoint(), + None => self.tunnel_parameters.get_tunnel_endpoint().endpoint, + }, + _ => self.tunnel_parameters.get_tunnel_endpoint().endpoint, + } + } + fn set_dns(&self, shared_values: &mut SharedTunnelStateValues) -> Result<()> { shared_values .dns_monitor @@ -192,8 +192,8 @@ impl TunnelState for ConnectedState { shared_values: &mut SharedTunnelStateValues, bootstrap: Self::Bootstrap, ) -> (TunnelStateWrapper, TunnelStateTransition) { - let tunnel_endpoint = bootstrap.tunnel_parameters.endpoint.clone(); let connected_state = ConnectedState::from(bootstrap); + let tunnel_endpoint = connected_state.tunnel_parameters.get_tunnel_endpoint(); if let Err(error) = connected_state.set_security_policy(shared_values) { log::error!("{}", error.display_chain()); diff --git a/talpid-core/src/tunnel_state_machine/connecting_state.rs b/talpid-core/src/tunnel_state_machine/connecting_state.rs index 3a4ea3d37a..ad3712144a 100644 --- a/talpid-core/src/tunnel_state_machine/connecting_state.rs +++ b/talpid-core/src/tunnel_state_machine/connecting_state.rs @@ -12,14 +12,14 @@ use futures::{ }; use log::{debug, error, info, trace, warn}; use talpid_types::{ - net::{Endpoint, OpenVpnProxySettings, TransportProtocol, TunnelEndpoint, TunnelEndpointData}, + net::{openvpn, Endpoint, TunnelParameters}, tunnel::BlockReason, }; use super::{ AfterDisconnect, BlockedState, ConnectedState, ConnectedStateBootstrap, DisconnectingState, - EventConsequence, SharedTunnelStateValues, TunnelCommand, TunnelParameters, TunnelState, - TunnelStateTransition, TunnelStateWrapper, + EventConsequence, SharedTunnelStateValues, TunnelCommand, TunnelState, TunnelStateTransition, + TunnelStateWrapper, }; use crate::{ logging, @@ -62,20 +62,13 @@ pub struct ConnectingState { impl ConnectingState { fn set_security_policy( shared_values: &mut SharedTunnelStateValues, - proxy: &Option<OpenVpnProxySettings>, - endpoint: TunnelEndpoint, + proxy: &Option<openvpn::ProxySettings>, + endpoint: Endpoint, ) -> Result<()> { // If a proxy is specified we need to pass it on as the peer endpoint. let peer_endpoint = match proxy { - Some(OpenVpnProxySettings::Local(ref local_proxy)) => Endpoint { - address: local_proxy.peer, - protocol: TransportProtocol::Tcp, - }, - Some(OpenVpnProxySettings::Remote(ref remote_proxy)) => Endpoint { - address: remote_proxy.address, - protocol: TransportProtocol::Tcp, - }, - _ => endpoint.to_endpoint(), + Some(proxy_settings) => proxy_settings.get_endpoint(), + None => endpoint, }; let policy = SecurityPolicy::Connecting { @@ -120,10 +113,8 @@ impl ConnectingState { let log_file = Self::prepare_tunnel_log_file(¶meters, log_dir)?; Ok(TunnelMonitor::start( - parameters.endpoint.clone(), - ¶meters.options, + ¶meters, TUNNEL_INTERFACE_ALIAS.to_owned().map(OsString::from), - ¶meters.username, log_file.clone(), resource_dir, on_tunnel_event, @@ -135,9 +126,9 @@ impl ConnectingState { log_dir: &Option<PathBuf>, ) -> Result<Option<PathBuf>> { if let Some(ref log_dir) = log_dir { - let filename = match parameters.endpoint.tunnel { - TunnelEndpointData::OpenVpn(_) => OPENVPN_LOG_FILENAME, - TunnelEndpointData::Wireguard(_) => WIREGUARD_LOG_FILENAME, + let filename = match parameters { + TunnelParameters::OpenVpn(_) => OPENVPN_LOG_FILENAME, + TunnelParameters::Wireguard(_) => WIREGUARD_LOG_FILENAME, }; let tunnel_log = log_dir.join(filename); logging::rotate_log(&tunnel_log).chain_err(|| ErrorKind::RotateLogError)?; @@ -228,8 +219,8 @@ impl ConnectingState { shared_values.allow_lan = allow_lan; match Self::set_security_policy( shared_values, - &self.tunnel_parameters.options.openvpn.proxy, - self.tunnel_parameters.endpoint.clone(), + &get_openvpn_proxy_settings(&self.tunnel_parameters), + self.tunnel_parameters.get_tunnel_endpoint().endpoint, ) { Ok(()) => SameState(self), Err(error) => { @@ -292,6 +283,7 @@ impl ConnectingState { } } + fn handle_tunnel_events( mut self, shared_values: &mut SharedTunnelStateValues, @@ -351,6 +343,15 @@ impl ConnectingState { } } +fn get_openvpn_proxy_settings( + tunnel_parameters: &TunnelParameters, +) -> &Option<openvpn::ProxySettings> { + match tunnel_parameters { + TunnelParameters::OpenVpn(ref config) => &config.options.proxy, + _ => &None, + } +} + impl TunnelState for ConnectingState { type Bootstrap = u32; @@ -367,11 +368,11 @@ impl TunnelState for ConnectingState { { None => BlockedState::enter(shared_values, BlockReason::NoMatchingRelay), Some(tunnel_parameters) => { - let tunnel_endpoint = tunnel_parameters.endpoint.clone(); + let endpoint = tunnel_parameters.get_tunnel_endpoint().endpoint; if let Err(error) = Self::set_security_policy( shared_values, - &tunnel_parameters.options.openvpn.proxy, - tunnel_endpoint.clone(), + &get_openvpn_proxy_settings(&tunnel_parameters), + endpoint, ) { error!("{}", error.display_chain()); BlockedState::enter(shared_values, BlockReason::StartTunnelError) @@ -382,10 +383,13 @@ impl TunnelState for ConnectingState { &shared_values.resource_dir, retry_attempt, ) { - Ok(connecting_state) => ( - TunnelStateWrapper::from(connecting_state), - TunnelStateTransition::Connecting(tunnel_endpoint), - ), + Ok(connecting_state) => { + let params = connecting_state.tunnel_parameters.clone(); + ( + TunnelStateWrapper::from(connecting_state), + TunnelStateTransition::Connecting(params.get_tunnel_endpoint()), + ) + } Err(error) => { let block_reason = match *error.kind() { ErrorKind::TunnelMonitorError( diff --git a/talpid-core/src/tunnel_state_machine/mod.rs b/talpid-core/src/tunnel_state_machine/mod.rs index 53d58060d3..8e20e3a159 100644 --- a/talpid-core/src/tunnel_state_machine/mod.rs +++ b/talpid-core/src/tunnel_state_machine/mod.rs @@ -18,7 +18,7 @@ use futures::{sync::mpsc, Async, Future, Poll, Stream}; use tokio_core::reactor::Core; use talpid_types::{ - net::{TunnelEndpoint, TunnelOptions}, + net::TunnelParameters, tunnel::{BlockReason, TunnelStateTransition}, }; @@ -162,17 +162,6 @@ pub enum TunnelCommand { Block(BlockReason), } -/// Information necessary to open a tunnel. -#[derive(Debug, PartialEq)] -pub struct TunnelParameters { - /// Tunnel enpoint to connect to. - pub endpoint: TunnelEndpoint, - /// Tunnel connection options. - pub options: TunnelOptions, - /// Username to use for setting up the tunnel. - pub username: String, -} - /// Asynchronous handling of the tunnel state machine. /// /// This type implements `Stream`, and attempts to advance the state machine based on the events diff --git a/talpid-types/Cargo.toml b/talpid-types/Cargo.toml index 8bf28fdcc7..f8023d8e86 100644 --- a/talpid-types/Cargo.toml +++ b/talpid-types/Cargo.toml @@ -10,4 +10,5 @@ edition = "2018" serde = { version = "1.0", features = ["derive"] } ipnetwork = "0.13" base64 = "0.10" -x25519-dalek = { version = "0.3", default-features = false, features = ["u64_backend"]} +hex = "0.3" +error-chain = "0.12" diff --git a/talpid-types/src/net.rs b/talpid-types/src/net.rs deleted file mode 100644 index f63705154d..0000000000 --- a/talpid-types/src/net.rs +++ /dev/null @@ -1,363 +0,0 @@ -use serde::{Deserialize, Serialize}; -use std::{ - error::Error, - fmt, - net::{IpAddr, SocketAddr}, - str::FromStr, -}; - -/// Represents one tunnel endpoint. Address, plus extra parameters specific to tunnel protocol. -#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)] -pub struct TunnelEndpoint { - pub address: IpAddr, - pub tunnel: TunnelEndpointData, -} - -impl TunnelEndpoint { - /// Returns this tunnel endpoint as an `Endpoint`. - pub fn to_endpoint(&self) -> Endpoint { - Endpoint::new( - self.address, - self.tunnel.port(), - self.tunnel.transport_protocol(), - ) - } -} - -/// TunnelEndpointData contains data required to connect to a given tunnel endpoint. -/// Different endpoint types can require different types of data. -#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)] -pub enum TunnelEndpointData { - /// Extra parameters for an OpenVPN tunnel endpoint. - #[serde(rename = "openvpn")] - OpenVpn(OpenVpnEndpointData), - /// Extra parameters for a Wireguard tunnel endpoint. - #[serde(rename = "wireguard")] - Wireguard(WireguardEndpointData), -} - -impl fmt::Display for TunnelEndpointData { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - match self { - TunnelEndpointData::OpenVpn(openvpn_data) => { - write!(f, "OpenVPN ")?; - openvpn_data.fmt(f) - } - TunnelEndpointData::Wireguard(wireguard_data) => { - write!(f, "Wireguard ")?; - wireguard_data.fmt(f) - } - } - } -} - -impl TunnelEndpointData { - pub fn port(&self) -> u16 { - match self { - TunnelEndpointData::OpenVpn(metadata) => metadata.port, - TunnelEndpointData::Wireguard(metadata) => metadata.port, - } - } - - pub fn transport_protocol(&self) -> TransportProtocol { - match self { - TunnelEndpointData::OpenVpn(metadata) => metadata.protocol, - TunnelEndpointData::Wireguard(_) => TransportProtocol::Udp, - } - } -} - -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Deserialize, Serialize)] -pub struct OpenVpnEndpointData { - pub port: u16, - pub protocol: TransportProtocol, -} - -impl fmt::Display for OpenVpnEndpointData { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, "{} port {}", self.protocol, self.port) - } -} - -#[derive(Clone, Eq, PartialEq, Hash, Deserialize, Serialize)] -pub struct WireguardEndpointData { - /// Port to connect to - pub port: u16, - /// Link addresses - pub addresses: Vec<IpAddr>, - /// Peer's IP address - pub gateway: IpAddr, - #[serde(skip)] - /// Client's private key - pub client_private_key: Option<WgPrivateKey>, - /// The peer's public key - pub peer_public_key: WgPublicKey, -} - -impl fmt::Debug for WireguardEndpointData { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - f.debug_struct(&"WireguardEndpointData") - .field("port", &self.port) - .field("addresses", &self.addresses) - .field("gateway", &self.gateway) - .field("peer_public_key", &self.peer_public_key) - .finish() - } -} - -impl fmt::Display for WireguardEndpointData { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!( - f, - "gateway {} port {} peer_public_key {} addresses {}", - self.gateway, - self.port, - self.peer_public_key, - self.addresses - .iter() - .map(|a| a.to_string()) - .collect::<Vec<_>>() - .join(",") - ) - } -} - - -/// Represents a network layer IP address together with the transport layer protocol and port. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct Endpoint { - /// The address part of this endpoint, contains the IP and port. - pub address: SocketAddr, - /// The protocol part of this endpoint. - pub protocol: TransportProtocol, -} - -impl Endpoint { - /// Constructs a new `Endpoint` from the given parameters. - pub fn new(address: impl Into<IpAddr>, port: u16, protocol: TransportProtocol) -> Self { - Endpoint { - address: SocketAddr::new(address.into(), port), - protocol, - } - } -} - -impl fmt::Display for Endpoint { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, "{}:{}", self.address, self.protocol) - } -} - -/// Representation of a transport protocol, either UDP or TCP. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[serde(rename_all = "snake_case")] -pub enum TransportProtocol { - /// Represents the UDP transport protocol. - Udp, - /// Represents the TCP transport protocol. - Tcp, -} - -impl FromStr for TransportProtocol { - type Err = TransportProtocolParseError; - - fn from_str(s: &str) -> ::std::result::Result<TransportProtocol, Self::Err> { - match s { - "udp" => Ok(TransportProtocol::Udp), - "tcp" => Ok(TransportProtocol::Tcp), - _ => Err(TransportProtocolParseError), - } - } -} - -impl fmt::Display for TransportProtocol { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - TransportProtocol::Udp => "UDP".fmt(fmt), - TransportProtocol::Tcp => "TCP".fmt(fmt), - } - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct TransportProtocolParseError; - -impl fmt::Display for TransportProtocolParseError { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.write_str(self.description()) - } -} - -impl Error for TransportProtocolParseError { - fn description(&self) -> &str { - "Not a valid transport protocol" - } -} - -/// TunnelOptions holds optional settings for tunnels, that are to be applied to any tunnel of the -/// appropriate type. -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -#[serde(default)] -pub struct TunnelOptions { - /// openvpn holds OpenVPN specific tunnel options. - pub openvpn: OpenVpnTunnelOptions, - /// Contains wireguard tunnel options. - pub wireguard: WireguardTunnelOptions, - /// Enable configuration of IPv6 on the tunnel interface, allowing IPv6 communication to be - /// forwarded through the tunnel. By default, this is set to `true`. - pub enable_ipv6: bool, -} - -impl Default for TunnelOptions { - fn default() -> Self { - TunnelOptions { - openvpn: OpenVpnTunnelOptions::default(), - wireguard: WireguardTunnelOptions::default(), - enable_ipv6: false, - } - } -} - -/// OpenVpnTunnelOptions contains options for an openvpn tunnel that should be applied irrespective -/// of the relay parameters - i.e. have nothing to do with the particular OpenVPN server, but do -/// affect the connection. -#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Default)] -#[serde(default)] -pub struct OpenVpnTunnelOptions { - /// Optional argument for openvpn to try and limit TCP packet size, - /// as discussed [here](https://openvpn.net/archive/openvpn-users/2003-11/msg00154.html) - pub mssfix: Option<u16>, - /// Proxy settings, for when the relay connection should be via a proxy. - pub proxy: Option<OpenVpnProxySettings>, -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)] -#[serde(rename_all = "snake_case")] -pub enum OpenVpnProxySettings { - Local(LocalOpenVpnProxySettings), - Remote(RemoteOpenVpnProxySettings), -} - -#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize)] -pub struct LocalOpenVpnProxySettings { - pub port: u16, - pub peer: SocketAddr, -} - -#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize)] -pub struct RemoteOpenVpnProxySettings { - pub address: SocketAddr, - pub auth: Option<OpenVpnProxyAuth>, -} - -#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize)] -pub struct OpenVpnProxyAuth { - pub username: String, - pub password: String, -} - -pub struct OpenVpnProxySettingsValidation; - -impl OpenVpnProxySettingsValidation { - pub fn validate(proxy: &OpenVpnProxySettings) -> Result<(), String> { - match proxy { - OpenVpnProxySettings::Local(local) => { - if local.port == 0 { - return Err(String::from("Invalid local port number")); - } - if local.peer.ip().is_loopback() { - return Err(String::from( - "localhost is not a valid peer in this context", - )); - } - if local.peer.port() == 0 { - return Err(String::from("Invalid remote port number")); - } - } - OpenVpnProxySettings::Remote(remote) => { - if remote.address.port() == 0 { - return Err(String::from("Invalid port number")); - } - if remote.address.ip().is_loopback() { - return Err(String::from("localhost is not a valid remote server")); - } - } - }; - Ok(()) - } -} - -/// Wireguard tunnel options -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -#[serde(default)] -pub struct WireguardTunnelOptions { - /// MTU for the wireguard tunnel - pub mtu: Option<u16>, - /// firewall mark - #[cfg(target_os = "linux")] - pub fwmark: i32, -} - -impl Default for WireguardTunnelOptions { - fn default() -> WireguardTunnelOptions { - Self { - mtu: None, - // Magic value that should be different for different end user applications, used as a - // firewall marker on Linux. - #[cfg(target_os = "linux")] - fwmark: 787878, - } - } -} - -/// Wireguard x25519 private key -#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Hash)] -pub struct WgPrivateKey([u8; 32]); - -impl WgPrivateKey { - /// Get private key as bytes - pub fn as_bytes(&self) -> &[u8; 32] { - &self.0 - } - - /// Get public key from private key - pub fn public_key(&self) -> WgPublicKey { - WgPublicKey(x25519_dalek::generate_public(self.as_bytes()).to_bytes()) - } -} - -impl From<[u8; 32]> for WgPrivateKey { - fn from(private_key: [u8; 32]) -> WgPrivateKey { - WgPrivateKey(private_key) - } -} - -/// Wireguard x25519 public key -#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Hash)] -pub struct WgPublicKey([u8; 32]); - -impl WgPublicKey { - /// Get the public key as bytes - pub fn as_bytes(&self) -> &[u8; 32] { - &self.0 - } -} - - -impl From<[u8; 32]> for WgPublicKey { - fn from(public_key: [u8; 32]) -> WgPublicKey { - WgPublicKey(public_key) - } -} - -impl fmt::Debug for WgPublicKey { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, "{}", &self) - } -} - -impl fmt::Display for WgPublicKey { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, "{}", &base64::encode(&self.0)) - } -} diff --git a/talpid-types/src/net/mod.rs b/talpid-types/src/net/mod.rs new file mode 100644 index 0000000000..74b3e8bfb1 --- /dev/null +++ b/talpid-types/src/net/mod.rs @@ -0,0 +1,164 @@ +use serde::{Deserialize, Serialize}; +use std::{ + error::Error, + fmt, + net::{IpAddr, SocketAddr}, + str::FromStr, +}; + +pub mod openvpn; +pub mod wireguard; + +/// TunnelParameters are used to encapsulate all the data needed to start a tunnel. This is enum +/// should be generated by implementations of the trait +/// `talpid-core::tunnel_state_machine::TunnelParametersGenerator` +#[derive(Clone, Eq, PartialEq, Deserialize, Serialize, Debug)] +pub enum TunnelParameters { + OpenVpn(openvpn::TunnelParameters), + Wireguard(wireguard::TunnelParameters), +} + + +impl TunnelParameters { + pub fn get_tunnel_endpoint(&self) -> TunnelEndpoint { + match self { + TunnelParameters::OpenVpn(params) => params.config.get_tunnel_endpoint(), + TunnelParameters::Wireguard(params) => params.connection.get_tunnel_endpoint(), + } + } + + pub fn get_generic_options(&self) -> &GenericTunnelOptions { + match &self { + TunnelParameters::OpenVpn(params) => ¶ms.generic_options, + TunnelParameters::Wireguard(params) => ¶ms.generic_options, + } + } +} + +impl From<wireguard::TunnelParameters> for TunnelParameters { + fn from(wg_params: wireguard::TunnelParameters) -> TunnelParameters { + TunnelParameters::Wireguard(wg_params) + } +} + +impl From<openvpn::TunnelParameters> for TunnelParameters { + fn from(params: openvpn::TunnelParameters) -> TunnelParameters { + TunnelParameters::OpenVpn(params) + } +} + +/// Type of the tunnel +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename = "tunnel_type")] +pub enum TunnelType { + #[serde(rename = "openvpn")] + OpenVpn, + #[serde(rename = "wireguard")] + Wireguard, +} + +impl fmt::Display for TunnelType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + let tunnel = match self { + TunnelType::OpenVpn => "OpenVPN", + TunnelType::Wireguard => "WireGuard", + }; + write!(f, "{}", tunnel) + } +} + +/// A tunnel endpoint is broadcast during the connecting and connected states of the tunnel state +/// machine. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct TunnelEndpoint { + #[serde(flatten)] + pub endpoint: Endpoint, + /// Type of the tunnel + pub tunnel_type: TunnelType, +} + +impl fmt::Display for TunnelEndpoint { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "{} - {}", self.tunnel_type, self.endpoint,) + } +} + + +/// Represents a network layer IP address together with the transport layer protocol and port. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct Endpoint { + /// The socket address for the endpoint + pub address: SocketAddr, + /// The protocol part of this endpoint. + pub protocol: TransportProtocol, +} + +impl Endpoint { + /// Constructs a new `Endpoint` from the given parameters. + pub fn new(address: impl Into<IpAddr>, port: u16, protocol: TransportProtocol) -> Self { + Endpoint { + address: SocketAddr::new(address.into(), port), + protocol, + } + } +} + +impl fmt::Display for Endpoint { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "{} over {}", self.address, self.protocol) + } +} + +/// Representation of a transport protocol, either UDP or TCP. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum TransportProtocol { + /// Represents the UDP transport protocol. + Udp, + /// Represents the TCP transport protocol. + Tcp, +} + +impl FromStr for TransportProtocol { + type Err = TransportProtocolParseError; + + fn from_str(s: &str) -> ::std::result::Result<TransportProtocol, Self::Err> { + match s { + "udp" => Ok(TransportProtocol::Udp), + "tcp" => Ok(TransportProtocol::Tcp), + _ => Err(TransportProtocolParseError), + } + } +} + +impl fmt::Display for TransportProtocol { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + TransportProtocol::Udp => "UDP".fmt(fmt), + TransportProtocol::Tcp => "TCP".fmt(fmt), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct TransportProtocolParseError; + +impl fmt::Display for TransportProtocolParseError { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.write_str(self.description()) + } +} + +impl Error for TransportProtocolParseError { + fn description(&self) -> &str { + "Not a valid transport protocol" + } +} + +/// Holds optional settings that can apply to different kinds of tunnels +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)] +pub struct GenericTunnelOptions { + /// Enable configuration of IPv6 on the tunnel interface, allowing IPv6 communication to be + /// forwarded through the tunnel. + pub enable_ipv6: bool, +} diff --git a/talpid-types/src/net/openvpn.rs b/talpid-types/src/net/openvpn.rs new file mode 100644 index 0000000000..2f8c92f65e --- /dev/null +++ b/talpid-types/src/net/openvpn.rs @@ -0,0 +1,128 @@ +use crate::net::{Endpoint, GenericTunnelOptions, TransportProtocol, TunnelEndpoint, TunnelType}; +use serde::{Deserialize, Serialize}; +use std::net::SocketAddr; + +#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize)] +pub struct TunnelParameters { + pub config: ConnectionConfig, + pub options: TunnelOptions, + pub generic_options: GenericTunnelOptions, +} + +#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize)] +pub struct ConnectionConfig { + pub endpoint: Endpoint, + pub username: String, + pub password: String, +} + +impl ConnectionConfig { + pub fn new(endpoint: Endpoint, username: String, password: String) -> ConnectionConfig { + Self { + endpoint, + username, + password, + } + } + pub fn get_tunnel_endpoint(&self) -> TunnelEndpoint { + TunnelEndpoint { + tunnel_type: TunnelType::OpenVpn, + endpoint: self.endpoint, + } + } +} + +/// TunnelOptions contains options for an openvpn tunnel that should be applied +/// irrespective of the relay parameters - i.e. have nothing to do with the particular +/// OpenVPN server, but do affect the connection. +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Default)] +pub struct TunnelOptions { + /// Optional argument for openvpn to try and limit TCP packet size, + /// as discussed [here](https://openvpn.net/archive/openvpn-users/2003-11/msg00154.html) + pub mssfix: Option<u16>, + /// Proxy settings, for when the relay connection should be via a proxy. + pub proxy: Option<ProxySettings>, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)] +#[serde(rename_all = "snake_case")] +pub enum ProxySettings { + Local(LocalProxySettings), + Remote(RemoteProxySettings), +} + +impl ProxySettings { + pub fn get_endpoint(&self) -> Endpoint { + match self { + ProxySettings::Local(settings) => settings.get_endpoint(), + ProxySettings::Remote(settings) => settings.get_endpoint(), + } + } +} + +#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize)] +pub struct LocalProxySettings { + pub port: u16, + pub peer: SocketAddr, +} + +impl LocalProxySettings { + pub fn get_endpoint(&self) -> Endpoint { + Endpoint { + address: self.peer, + protocol: TransportProtocol::Tcp, + } + } +} + +#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize)] +pub struct RemoteProxySettings { + pub address: SocketAddr, + pub auth: Option<ProxyAuth>, +} + +impl RemoteProxySettings { + pub fn get_endpoint(&self) -> Endpoint { + Endpoint { + address: self.address, + protocol: TransportProtocol::Tcp, + } + } +} + +#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize)] +pub struct ProxyAuth { + pub username: String, + pub password: String, +} + +pub struct ProxySettingsValidation; + +impl ProxySettingsValidation { + pub fn validate(proxy: &ProxySettings) -> Result<(), String> { + match proxy { + ProxySettings::Local(local) => { + if local.port == 0 { + return Err(String::from("Invalid local port number")); + } + if local.peer.ip().is_loopback() { + return Err(String::from( + "localhost is not a valid peer in this context", + )); + } + if local.peer.port() == 0 { + return Err(String::from("Invalid remote port number")); + } + } + ProxySettings::Remote(remote) => { + if remote.address.port() == 0 { + return Err(String::from("Invalid port number")); + } + if remote.address.ip().is_loopback() { + return Err(String::from("localhost is not a valid remote server")); + } + } + }; + Ok(()) + } +} diff --git a/talpid-types/src/net/wireguard.rs b/talpid-types/src/net/wireguard.rs new file mode 100644 index 0000000000..d24ffee0e3 --- /dev/null +++ b/talpid-types/src/net/wireguard.rs @@ -0,0 +1,173 @@ +use crate::net::{Endpoint, GenericTunnelOptions, TransportProtocol, TunnelEndpoint, TunnelType}; +use ipnetwork::IpNetwork; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use std::{ + fmt, + net::{IpAddr, SocketAddr}, +}; + + +#[derive(Clone, Eq, PartialEq, Deserialize, Serialize, Debug)] +/// Wireguard tunnel parameters +pub struct TunnelParameters { + pub connection: ConnectionConfig, + pub options: TunnelOptions, + pub generic_options: GenericTunnelOptions, +} + +#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] +pub struct ConnectionConfig { + pub tunnel: TunnelConfig, + pub peer: PeerConfig, + pub gateway: IpAddr, +} + +impl ConnectionConfig { + pub fn get_tunnel_endpoint(&self) -> TunnelEndpoint { + let host = self.peer.endpoint; + TunnelEndpoint { + tunnel_type: TunnelType::Wireguard, + endpoint: Endpoint { + address: host, + protocol: TransportProtocol::Udp, + }, + } + } +} + +#[derive(Clone, Eq, PartialEq, Deserialize, Serialize, Debug, Hash)] +pub struct PeerConfig { + pub public_key: PublicKey, + pub allowed_ips: Vec<IpNetwork>, + pub endpoint: SocketAddr, +} + +#[derive(Clone, Eq, PartialEq, Deserialize, Serialize, Debug)] +pub struct TunnelConfig { + pub private_key: PrivateKey, + pub addresses: Vec<IpAddr>, +} + +/// Wireguard tunnel options +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct TunnelOptions { + /// MTU for the wireguard tunnel + pub mtu: Option<u16>, + /// firewall mark + #[cfg(target_os = "linux")] + pub fwmark: i32, +} + +/// Wireguard x25519 private key +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct PrivateKey([u8; 32]); + +impl PrivateKey { + /// Get private key as bytes + pub fn as_bytes(&self) -> &[u8; 32] { + &self.0 + } +} + +impl From<[u8; 32]> for PrivateKey { + fn from(private_key: [u8; 32]) -> PrivateKey { + PrivateKey(private_key) + } +} + +impl Serialize for PrivateKey { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + serialize_key(&self.0, serializer) + } +} + + +impl<'de> Deserialize<'de> for PrivateKey { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: Deserializer<'de>, + { + deserialize_key(deserializer) + } +} + +/// Wireguard x25519 public key +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct PublicKey([u8; 32]); + +impl PublicKey { + /// Get the public key as bytes + pub fn as_bytes(&self) -> &[u8; 32] { + &self.0 + } +} + + +impl From<[u8; 32]> for PublicKey { + fn from(public_key: [u8; 32]) -> PublicKey { + PublicKey(public_key) + } +} + +impl Serialize for PublicKey { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + serialize_key(&self.0, serializer) + } +} + + +impl<'de> Deserialize<'de> for PublicKey { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: Deserializer<'de>, + { + deserialize_key(deserializer) + } +} + +impl fmt::Debug for PublicKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", &self) + } +} + +impl fmt::Display for PublicKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", &base64::encode(&self.0)) + } +} + +fn serialize_key<S>(key: &[u8; 32], serializer: S) -> Result<S::Ok, S::Error> +where + S: Serializer, +{ + serializer.serialize_str(&base64::encode(&key)) +} + +fn deserialize_key<'de, D, K>(deserializer: D) -> Result<K, D::Error> +where + D: Deserializer<'de>, + K: From<[u8; 32]>, +{ + use serde::de::Error; + + String::deserialize(deserializer) + .and_then(|string| base64::decode(&string).map_err(|err| Error::custom(err.to_string()))) + .and_then(|buffer| { + let mut key = [0u8; 32]; + if buffer.len() != 32 { + return Err(Error::custom(format!( + "Key has unexpected length: {}", + buffer.len() + ))); + } + key.copy_from_slice(&buffer); + Ok(From::from(key)) + }) +} diff --git a/talpid-types/src/tunnel.rs b/talpid-types/src/tunnel.rs index 613204ab19..07d0675bde 100644 --- a/talpid-types/src/tunnel.rs +++ b/talpid-types/src/tunnel.rs @@ -1,4 +1,4 @@ -use super::net::TunnelEndpoint; +use crate::net::TunnelEndpoint; use serde::{Deserialize, Serialize}; use std::fmt; |
