Skip to content

Commit

Permalink
Merge branch 'feature/s121-V2Ray' into beta
Browse files Browse the repository at this point in the history
# Conflicts:
#	daemon/service/preferences/preferences.go
  • Loading branch information
stenya committed Aug 18, 2023
2 parents 56ef01d + 9e7ab3f commit dfe670b
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 49 deletions.
25 changes: 22 additions & 3 deletions daemon/service/preferences/preferences.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import (

"github.com/ivpn/desktop-app/daemon/helpers"
"github.com/ivpn/desktop-app/daemon/logger"
"github.com/ivpn/desktop-app/daemon/obfsproxy"
"github.com/ivpn/desktop-app/daemon/service/platform"
service_types "github.com/ivpn/desktop-app/daemon/service/types"
"github.com/ivpn/desktop-app/daemon/version"
Expand Down Expand Up @@ -214,12 +215,30 @@ func (p *Preferences) LoadPreferences() error {
}

// Convert parameters from v3.11.15 (and releases older than 2023-08-07)
// A new option, WiFiControl.Actions.UnTrustedBlockLan, was introduced.
// It is 'true' by default. However, older versions did not have this functionality.
// Therefore, for users upgrading from v3.11.15, it must be disabled.
if compareVersions(p.Version, "3.11.15") <= 0 {
// if upgrading from "3.11.15" or older version

// A new option, WiFiControl.Actions.UnTrustedBlockLan, was introduced.
// It is 'true' by default. However, older versions did not have this functionality.
// Therefore, for users upgrading from v3.11.15, it must be disabled.
p.WiFiControl.Actions.UnTrustedBlockLan = false

// Obfsproxy configuration was moved to 'LastConnectionParams->OpenVpnParameters' section
type tmp_type_Settings_v3_11_15 struct {
Obfs4proxy struct {
Obfs4Iat obfsproxy.Obfs4IatMode
Version obfsproxy.ObfsProxyVersion
}
}
var tmp_Settings_v3_11_15 tmp_type_Settings_v3_11_15
err = json.Unmarshal(data, &tmp_Settings_v3_11_15)
if err == nil && tmp_Settings_v3_11_15.Obfs4proxy.Version > obfsproxy.None {
p.LastConnectionParams.OpenVpnParameters.Obfs4proxy = obfsproxy.Config{
Version: tmp_Settings_v3_11_15.Obfs4proxy.Version,
Obfs4Iat: tmp_Settings_v3_11_15.Obfs4proxy.Obfs4Iat,
}

}
}

return nil
Expand Down
45 changes: 20 additions & 25 deletions daemon/service/service_connect.go
Original file line number Diff line number Diff line change
Expand Up @@ -918,26 +918,13 @@ func (s *Service) connect(originalEntryServerInfo *svrConnInfo, vpnProc vpn.Proc
return nil
}

// startV2Ray start V2Ray connection
// Please refer to the v2r.V2RayConfig (in v2r/config.go) struct for more information about the V2Ray data flow and configuration
func (s *Service) startV2Ray(params types.ConnectionParams, v2RayType v2r.V2RayTransportType) (
updatedParams types.ConnectionParams,
v2RayWrapper *v2r.V2RayWrapper,
originalEntryServerInfo *svrConnInfo,
err error) {
//
// Info:
// - V2Ray data flow:
// * for Single-Hop: LocalV2RayProxy -> Outbound(EntryServer:V2Ray) -> Inbound(EntryServer:WireGuard)
// * for Multi-Hop: LocalV2RayProxy -> Outbound(EntryServer:V2Ray) -> Inbound(ExitServer:WireGuard)
// - Outbound ports can be any ports (which applicable for the selected VPN type).
// * Preferred outbound ports: 80 for HTTP/VMess/TCP and 443 for HTTPS/VMess/QUIC
// - Inbound ports
// * Single-Hop connections use internal V2Ray ports for inbound connections: svrs.Config.Ports.V2Ray
// * Multi-Hop connections can use any ports for inbound connections (which applicable for the selected VPN type).
//
// Note!
// - Multi-Hop:
// * For V2Ray connections we ignore port-based multihop configuration. Use default ports instead.
// * WireGuard: since the first WG server is the ExitServer - we have to use it's public key in the WireGuard configuration

if v2RayType != v2r.QUIC && v2RayType != v2r.TCP {
return params, nil, nil, nil
Expand Down Expand Up @@ -983,10 +970,12 @@ func (s *Service) startV2Ray(params types.ConnectionParams, v2RayType v2r.V2RayT
inboundIp := "" // for Single-Hop: host IP; for Multi-Hop: exit host IP
inboundPort := 0 // for Single-Hop: internal V2Ray port; for Multi-Hop: exit host port

isTcpLocalPort := isTcpOutboundPort
if params.VpnType == vpn.WireGuard {
isTcpLocalPort = false // WireGuard uses only UDP
}
// isTcpLocalPort - is the local port type (TCP or UDP) of local V2Ray proxy: the inbound port type ([VPN-server PROTOCOL]) should be similar to the local port type.
// In fact, it is VPN connection type:
// - WireGuard uses only UDP;
// - for OpenVPN we use UDP because it is preffered (but TCP is also acceptable)
isTcpLocalPort := false // use UDP for all VPN types

requiredLocalPortTypeStr := "tcp"
if !isTcpLocalPort {
requiredLocalPortTypeStr = "udp"
Expand Down Expand Up @@ -1068,10 +1057,16 @@ func (s *Service) startV2Ray(params types.ConnectionParams, v2RayType v2r.V2RayT
origEntrySvr := &svrConnInfo{V2RayProxyType: v2RayType}
if vpn.Type(params.VpnType) == vpn.OpenVPN {

// set OpenVPN protocol (udp/tcp) according to the local V2Ray port type
updatedParams.OpenVpnParameters.Port.Protocol = 0
if isTcpLocalPort {
updatedParams.OpenVpnParameters.Port.Protocol = 1
}

// We have to return the original information about EntryServer
origEntrySvr.IP = net.ParseIP(updatedParams.OpenVpnParameters.EntryVpnServer.Hosts[0].Host)
origEntrySvr.Port = updatedParams.OpenVpnParameters.Port.Port
origEntrySvr.PortType = updatedParams.OpenVpnParameters.Port.Protocol
origEntrySvr.IP = net.ParseIP(params.OpenVpnParameters.EntryVpnServer.Hosts[0].Host)
origEntrySvr.Port = params.OpenVpnParameters.Port.Port
origEntrySvr.PortType = params.OpenVpnParameters.Port.Protocol

// Specify connection parameters to local V2Ray proxy
updatedParams.OpenVpnParameters.EntryVpnServer.Hosts[0].Host = "127.0.0.1"
Expand All @@ -1087,9 +1082,9 @@ func (s *Service) startV2Ray(params types.ConnectionParams, v2RayType v2r.V2RayT
} else if vpn.Type(params.VpnType) == vpn.WireGuard {

// We have to return the original information about EntryServer
origEntrySvr.IP = net.ParseIP(updatedParams.WireGuardParameters.EntryVpnServer.Hosts[0].Host)
origEntrySvr.Port = updatedParams.WireGuardParameters.Port.Port
origEntrySvr.PortType = updatedParams.WireGuardParameters.Port.Protocol
origEntrySvr.IP = net.ParseIP(params.WireGuardParameters.EntryVpnServer.Hosts[0].Host)
origEntrySvr.Port = params.WireGuardParameters.Port.Port
origEntrySvr.PortType = params.WireGuardParameters.Port.Protocol

// Specify connection parameters to local V2Ray proxy
updatedParams.WireGuardParameters.EntryVpnServer.Hosts[0].Host = "127.0.0.1"
Expand Down
69 changes: 69 additions & 0 deletions daemon/v2r/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,75 @@ const defaultConfigTemplate = `{
]
}`

// V2Ray configuration explanation
//
// V2Ray data flow:
//
// [LOCAL-V2Ray-proxy] → (internet) → [VMESS-server] → (IVPN-backend-infrastructure) → [VPN-server]
//
// Simplified V2Ray configuration:
//
// {
// "inbounds": [
// {
// "port": "61554", // [LOCAL-V2Ray-proxy LOCAL-PORT]
// "settings": {
// "address": "169.150.252.110", // [VPN-server IP]
// "port": 1443, // [VPN-server PORT]
// "network": "udp" // [VPN-server PROTOCOL]
// },
// }
// ],
// "outbounds": [
// {
// "settings": {
// "vnext": [
// {
// "address": "169.150.252.115", // [VMESS-server IP]
// "port": 2049, // [VMESS-server PORT]
// }
// ]
// },
// "streamSettings": {
// "network": "tcp", // [ VMESS-PROTOCOL ]
// }
// }
// ]
// }
//
// Fields explanation:
// * [VPN-server IP] - standard IP address of the VPN server.
// * [VPN-server PORT] and [VPN-server PROTOCOL] - the port:protocol definition of the VPN server
// - Multi-Hop: can be used any standard port:protocol applicable for VPN connection
// - Single-Hop: (applicable V2Ray ports info available in servers.json under config->ports->v2ray)
// -- WireGuard: can be 15351:UDP only
// -- OpenVPN : can be 20501,20502,20503,20504:UDP and 1443:TCP
//
// * [VMESS-server IP] - IP address of VMESS server, taken from v2ray field in host description in servers.json
// * [VMESS-server PORT] - PORT number of VMESS server. Can be ANY standard port from config->ports->openvpn/wireguard (limited only by [ VMESS-PROTOCOL ]):
// - when VMESS/TCP is in use - Can be ANY standard TCP port (UDP ports not supported)
// - when VMESS/QUIC is in use - Can be ANY standard UDP port (TCP ports not supported)
//
// * [ VMESS-PROTOCOL ] - protocol/obfuscation type
// - quick for VMESS/QUICK
// - tcp for VMESS/TCP
//
// Additional info:
// * V2Ray data flow:
// - for Single-Hop: LocalV2RayProxy -> Outbound(EntryServer:V2Ray) -> Inbound(EntryServer:WireGuard)
// - for Multi-Hop: LocalV2RayProxy -> Outbound(EntryServer:V2Ray) -> Inbound(ExitServer:WireGuard)
//
// * Outbound ports can be any ports (which applicable for the selected VPN type).
// - Preferred outbound ports: 80 for HTTP/VMess/TCP and 443 for HTTPS/VMess/QUIC
//
// * Inbound ports
// - Single-Hop connections use internal V2Ray ports for inbound connections: svrs.Config.Ports.V2Ray
// - Multi-Hop connections can use any ports for inbound connections (which applicable for the selected VPN type).
//
// Note!
// * Multi-Hop:
// - For V2Ray connections we ignore port-based multihop configuration. Use default ports instead.
// - WireGuard: since the first WG server is the ExitServer - we have to use it's public key in the WireGuard configuration
type V2RayConfig struct {
Log struct {
Loglevel string `json:"loglevel"`
Expand Down
2 changes: 2 additions & 0 deletions daemon/v2r/v2ray.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ type V2RayWrapper struct {
stoppedChan chan struct{}
}

// CreateV2RayWrapper - creates new V2RayWrapper object
// Please refer to the v2r.V2RayConfig (in v2r/config.go) struct for more information about the V2Ray data flow and configuration
func CreateV2RayWrapper(binary string, tmpConfigFile string, cfg *V2RayConfig) *V2RayWrapper {
return &V2RayWrapper{
binary: binary,
Expand Down
3 changes: 1 addition & 2 deletions daemon/vpn/openvpn/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ package openvpn
import (
"errors"
"fmt"
"io/ioutil"
"net"
"os"
"strings"
Expand Down Expand Up @@ -179,7 +178,7 @@ func (c *ConnectionParams) generateConfiguration(

if len(proxyAuthFileData) > 0 {
proxyAuthFile = "\"" + platform.OpenvpnProxyAuthFile() + "\""
err := ioutil.WriteFile(platform.OpenvpnProxyAuthFile(), []byte(proxyAuthFileData), 0600)
err := os.WriteFile(platform.OpenvpnProxyAuthFile(), []byte(proxyAuthFileData), 0600)
if err != nil {
log.Error(err)
return nil, fmt.Errorf("failed to save file with proxy credentials: %w", err)
Expand Down
73 changes: 54 additions & 19 deletions ui/src/store/module-settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ const getDefaultState = () => {
OpenVPN: defaultPort,
WireGuard: defaultPort,
},
// portWireGuardBackup - the original port value for WireGuard (before it was changed to V2Ray-specific TCP port).
// WireGuard port can be changed to V2Ray-specific TCP port if V2Ray obfuscation is enabled.
// In this case, we need to remember original WireGuard port to be able to restore it back when V2Ray obfuscation is disabled.
portWireGuardBackup: defaultPort,

// custom ports defined by user (based on the applicable port range)
customPorts: [], // [ {type: "UDP/TCP", port: "X", range: {min: X, max: X}}, ... ],
Expand Down Expand Up @@ -331,16 +335,20 @@ export default {
console.log("Warning! setPort() unable to set port. Port not defined.");
return;
}
state.port[enumValueName(VpnTypeEnum, state.vpnType)] = portVal;

let newPort = Object.assign({}, state.port);

if (state.vpnType === VpnTypeEnum.WireGuard) newPort.WireGuard = portVal;
else if (state.vpnType === VpnTypeEnum.OpenVPN) newPort.OpenVPN = portVal;
else {
console.log("Warning! setPort() unable to set port. Unknown VPN type.");
return;
}

doSetPortLogic(state, newPort);
},
port(state, val) {
if (!val || !val.OpenVPN || !val.WireGuard) {
{
console.log("Warning! port: unable to change port. Bad object.");
return;
}
}
state.port = val;
doSetPortLogic(state, val);
},

customPorts(state, val) {
Expand Down Expand Up @@ -872,6 +880,23 @@ export default {
},
};

function doSetPortLogic(state, val) {
if (!val || !val.OpenVPN || !val.WireGuard) {
{
console.log("Warning! port: unable to change port. Bad object.");
return;
}
}

// Save last good UDP port for WireGuard (since WireGuard supports only UDP)
// It can happen than WireGuard port was changed to TCP port (if V2Ray obfuscation is enabled)
// Keeping last good UDP port allows us to restore it back when V2Ray obfuscation is disabled
if (val.WireGuard.type === PortTypeEnum.UDP)
state.portWireGuardBackup = val.WireGuard;

state.port = val;
}

function updateSelectedServers(context, isDenyMultihopServersFromSameCountry) {
// - define selected servers (if not initialized)
// - update selected servers if VPN type changed (try to use vpnType-related servers from the same location [country\city])
Expand Down Expand Up @@ -1115,9 +1140,10 @@ function ensurePortsSelectedCorrectly(ctx) {

const state = ctx.state;

let portOvpn = TestSuitablePort(ctx, VpnTypeEnum.OpenVPN);
let portWg = TestSuitablePort(ctx, VpnTypeEnum.WireGuard);

let cPort = Object.assign({}, state.port);
let portOvpn = TestSuitablePort(ctx, VpnTypeEnum.OpenVPN, cPort.OpenVPN);
let portWg = TestSuitablePort(ctx, VpnTypeEnum.WireGuard, cPort.WireGuard);
if (portOvpn) cPort.OpenVPN = portOvpn;
if (portWg) cPort.WireGuard = portWg;
if (portOvpn || portWg) {
Expand All @@ -1132,7 +1158,12 @@ function ensurePortsSelectedCorrectly(ctx) {
}

// returns null - if port is ok; otherwise - port value which have to be applied
function TestSuitablePort(context, vpnType, currPort) {
function TestSuitablePort(context, vpnType) {
var currPort =
vpnType === VpnTypeEnum.OpenVPN
? context.state.port.OpenVPN
: context.state.port.WireGuard;

const funcGetPorts = context.rootGetters["vpnState/funcGetConnectionPorts"];
const applicablePorts = funcGetPorts(vpnType);
// Check is port applicable (according to current settings)
Expand All @@ -1142,20 +1173,24 @@ function TestSuitablePort(context, vpnType, currPort) {
!isPortExists(applicablePorts, currPort)
) {
console.log(`Selected port `, currPort, "not applicable for VPN ", vpnType);

// get V2Ray type
let v2rayType = context.state.V2RayConfig.WireGuard;
if (vpnType === VpnTypeEnum.OpenVPN)
v2rayType = context.state.V2RayConfig.OpenVPN;

return GetDefaultPort(context, applicablePorts, v2rayType);
return GetDefaultPort(context, applicablePorts, vpnType);
}
return null;
}

function GetDefaultPort(context, ports, v2rayType) {
function GetDefaultPort(context, ports, vpnType) {
let defPort = null;

if (vpnType === VpnTypeEnum.WireGuard) {
let alternatePort = context.state.portWireGuardBackup;
if (isPortExists(ports, alternatePort)) return alternatePort;
}

// get V2Ray type
let v2rayType = context.state.V2RayConfig.WireGuard;
if (vpnType === VpnTypeEnum.OpenVPN)
v2rayType = context.state.V2RayConfig.OpenVPN;

for (const configPort of ports) {
const p = NormalizedConfigPortObject(configPort);
if (p) {
Expand Down

0 comments on commit dfe670b

Please sign in to comment.