Feat: IVPN supports TCP and custom port

This commit is contained in:
Quentin McGaw (desktop)
2021-08-23 13:34:00 +00:00
parent c348343b22
commit f41fec57ed
8 changed files with 454 additions and 86 deletions

View File

@@ -1,23 +1,16 @@
package ivpn
import (
"errors"
"github.com/qdm12/gluetun/internal/configuration"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/provider/utils"
)
var ErrProtocolUnsupported = errors.New("network protocol is not supported")
func (i *Ivpn) GetConnection(selection configuration.ServerSelection) (
connection models.Connection, err error) {
const port = 2049
const protocol = constants.UDP
if selection.OpenVPN.TCP {
return connection, ErrProtocolUnsupported
}
port := getPort(selection)
protocol := getProtocol(selection.OpenVPN.TCP)
servers, err := i.filterServers(selection)
if err != nil {
@@ -44,3 +37,22 @@ func (i *Ivpn) GetConnection(selection configuration.ServerSelection) (
return utils.PickRandomConnection(connections, i.randSource), nil
}
func getPort(selection configuration.ServerSelection) (port uint16) {
customPort := selection.OpenVPN.CustomPort
if customPort > 0 {
return customPort
}
port = 1194
if selection.OpenVPN.TCP {
port = 443
}
return port
}
func getProtocol(tcp bool) (protocol string) {
if tcp {
return constants.TCP
}
return constants.UDP
}

View File

@@ -0,0 +1,173 @@
package ivpn
import (
"errors"
"math/rand"
"net"
"testing"
"github.com/qdm12/gluetun/internal/configuration"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_Ivpn_GetConnection(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
servers []models.IvpnServer
selection configuration.ServerSelection
connection models.Connection
err error
}{
"no server available": {
selection: configuration.ServerSelection{
VPN: constants.OpenVPN,
},
err: errors.New("no server found: for VPN openvpn; protocol udp"),
},
"no filter": {
servers: []models.IvpnServer{
{IPs: []net.IP{net.IPv4(1, 1, 1, 1)}, UDP: true},
{IPs: []net.IP{net.IPv4(2, 2, 2, 2)}, UDP: true},
{IPs: []net.IP{net.IPv4(3, 3, 3, 3)}, UDP: true},
},
connection: models.Connection{
IP: net.IPv4(1, 1, 1, 1),
Port: 1194,
Protocol: constants.UDP,
},
},
"target IP": {
selection: configuration.ServerSelection{
TargetIP: net.IPv4(2, 2, 2, 2),
},
servers: []models.IvpnServer{
{IPs: []net.IP{net.IPv4(1, 1, 1, 1)}, UDP: true},
{IPs: []net.IP{net.IPv4(2, 2, 2, 2)}, UDP: true},
{IPs: []net.IP{net.IPv4(3, 3, 3, 3)}, UDP: true},
},
connection: models.Connection{
IP: net.IPv4(2, 2, 2, 2),
Port: 1194,
Protocol: constants.UDP,
},
},
"with filter": {
selection: configuration.ServerSelection{
Hostnames: []string{"b"},
},
servers: []models.IvpnServer{
{Hostname: "a", IPs: []net.IP{net.IPv4(1, 1, 1, 1)}, UDP: true},
{Hostname: "b", IPs: []net.IP{net.IPv4(2, 2, 2, 2)}, UDP: true},
{Hostname: "a", IPs: []net.IP{net.IPv4(3, 3, 3, 3)}, UDP: true},
},
connection: models.Connection{
IP: net.IPv4(2, 2, 2, 2),
Port: 1194,
Protocol: constants.UDP,
Hostname: "b",
},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
randSource := rand.NewSource(0)
m := New(testCase.servers, randSource)
connection, err := m.GetConnection(testCase.selection)
if testCase.err != nil {
require.Error(t, err)
assert.Equal(t, testCase.err.Error(), err.Error())
} else {
assert.NoError(t, err)
}
assert.Equal(t, testCase.connection, connection)
})
}
}
func Test_getPort(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
selection configuration.ServerSelection
port uint16
}{
"default": {
port: 1194,
},
"OpenVPN UDP": {
selection: configuration.ServerSelection{
VPN: constants.OpenVPN,
},
port: 1194,
},
"OpenVPN TCP": {
selection: configuration.ServerSelection{
VPN: constants.OpenVPN,
OpenVPN: configuration.OpenVPNSelection{
TCP: true,
},
},
port: 443,
},
"OpenVPN custom port": {
selection: configuration.ServerSelection{
VPN: constants.OpenVPN,
OpenVPN: configuration.OpenVPNSelection{
CustomPort: 1234,
},
},
port: 1234,
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
port := getPort(testCase.selection)
assert.Equal(t, testCase.port, port)
})
}
}
func Test_getProtocol(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
tcp bool
protocol string
}{
"UDP": {
protocol: constants.UDP,
},
"TCP": {
tcp: true,
protocol: constants.TCP,
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
protocol := getProtocol(testCase.tcp)
assert.Equal(t, testCase.protocol, protocol)
})
}
}

View File

@@ -0,0 +1,132 @@
package ivpn
import (
"errors"
"math/rand"
"testing"
"github.com/qdm12/gluetun/internal/configuration"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_Ivpn_filterServers(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
servers []models.IvpnServer
selection configuration.ServerSelection
filtered []models.IvpnServer
err error
}{
"no server available": {
selection: configuration.ServerSelection{
VPN: constants.OpenVPN,
},
err: errors.New("no server found: for VPN openvpn; protocol udp"),
},
"no filter": {
servers: []models.IvpnServer{
{Hostname: "a", UDP: true},
{Hostname: "b", UDP: true},
{Hostname: "c", UDP: true},
},
filtered: []models.IvpnServer{
{Hostname: "a", UDP: true},
{Hostname: "b", UDP: true},
{Hostname: "c", UDP: true},
},
},
"filter by country": {
selection: configuration.ServerSelection{
Countries: []string{"b"},
},
servers: []models.IvpnServer{
{Country: "a", UDP: true},
{Country: "b", UDP: true},
{Country: "c", UDP: true},
},
filtered: []models.IvpnServer{
{Country: "b", UDP: true},
},
},
"filter by city": {
selection: configuration.ServerSelection{
Cities: []string{"b"},
},
servers: []models.IvpnServer{
{City: "a", UDP: true},
{City: "b", UDP: true},
{City: "c", UDP: true},
},
filtered: []models.IvpnServer{
{City: "b", UDP: true},
},
},
"filter by ISP": {
selection: configuration.ServerSelection{
ISPs: []string{"b"},
},
servers: []models.IvpnServer{
{ISP: "a", UDP: true},
{ISP: "b", UDP: true},
{ISP: "c", UDP: true},
},
filtered: []models.IvpnServer{
{ISP: "b", UDP: true},
},
},
"filter by hostname": {
selection: configuration.ServerSelection{
Hostnames: []string{"b"},
},
servers: []models.IvpnServer{
{Hostname: "a", UDP: true},
{Hostname: "b", UDP: true},
{Hostname: "c", UDP: true},
},
filtered: []models.IvpnServer{
{Hostname: "b", UDP: true},
},
},
"filter by protocol": {
selection: configuration.ServerSelection{
OpenVPN: configuration.OpenVPNSelection{
TCP: true,
},
},
servers: []models.IvpnServer{
{Hostname: "a", UDP: true},
{Hostname: "b", UDP: true, TCP: true},
{Hostname: "c", UDP: true},
},
filtered: []models.IvpnServer{
{Hostname: "b", UDP: true, TCP: true},
},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
randSource := rand.NewSource(0)
m := New(testCase.servers, randSource)
servers, err := m.filterServers(testCase.selection)
if testCase.err != nil {
require.Error(t, err)
assert.Equal(t, testCase.err.Error(), err.Error())
} else {
assert.NoError(t, err)
}
assert.Equal(t, testCase.filtered, servers)
})
}
}