Fix & feat: Cyberghost server groups
- Allow multiple comma separated values for CYBERGHOST_GROUP - Defaults to all UDP groups - If TCP is enabled, defaults to all TCP groups - Check groups specified match the protocol - Default Cyberghost group to empty - Adjust formatting and messages
This commit is contained in:
@@ -4,11 +4,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
"github.com/qdm12/golibs/params"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (settings *Provider) cyberghostLines() (lines []string) {
|
func (settings *Provider) cyberghostLines() (lines []string) {
|
||||||
lines = append(lines, lastIndent+"Server group: "+settings.ServerSelection.Group)
|
lines = append(lines, lastIndent+"Server groups: "+commaJoin(settings.ServerSelection.Groups))
|
||||||
|
|
||||||
if len(settings.ServerSelection.Regions) > 0 {
|
if len(settings.ServerSelection.Regions) > 0 {
|
||||||
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
|
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
|
||||||
@@ -52,8 +51,8 @@ func (settings *Provider) readCyberghost(r reader) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.ServerSelection.Group, err = r.env.Inside("CYBERGHOST_GROUP",
|
settings.ServerSelection.Groups, err = r.env.CSVInside("CYBERGHOST_GROUP",
|
||||||
constants.CyberghostGroupChoices(), params.Default("Premium UDP Europe"))
|
constants.CyberghostGroupChoices())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("environment variable CYBERGHOST_GROUP: %w", err)
|
return fmt.Errorf("environment variable CYBERGHOST_GROUP: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ func Test_OpenVPN_JSON(t *testing.T) {
|
|||||||
"server_selection": {
|
"server_selection": {
|
||||||
"tcp": false,
|
"tcp": false,
|
||||||
"regions": null,
|
"regions": null,
|
||||||
"group": "",
|
"groups": null,
|
||||||
"countries": null,
|
"countries": null,
|
||||||
"cities": null,
|
"cities": null,
|
||||||
"hostnames": null,
|
"hostnames": null,
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ func Test_Provider_lines(t *testing.T) {
|
|||||||
settings: Provider{
|
settings: Provider{
|
||||||
Name: constants.Cyberghost,
|
Name: constants.Cyberghost,
|
||||||
ServerSelection: ServerSelection{
|
ServerSelection: ServerSelection{
|
||||||
Group: "group",
|
Groups: []string{"group"},
|
||||||
Regions: []string{"a", "El country"},
|
Regions: []string{"a", "El country"},
|
||||||
},
|
},
|
||||||
ExtraConfigOptions: ExtraConfigOptions{
|
ExtraConfigOptions: ExtraConfigOptions{
|
||||||
@@ -35,7 +35,7 @@ func Test_Provider_lines(t *testing.T) {
|
|||||||
lines: []string{
|
lines: []string{
|
||||||
"|--Cyberghost settings:",
|
"|--Cyberghost settings:",
|
||||||
" |--Network protocol: udp",
|
" |--Network protocol: udp",
|
||||||
" |--Server group: group",
|
" |--Server groups: group",
|
||||||
" |--Regions: a, El country",
|
" |--Regions: a, El country",
|
||||||
" |--Client key is set",
|
" |--Client key is set",
|
||||||
" |--Client certificate is set",
|
" |--Client certificate is set",
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ type ServerSelection struct { //nolint:maligned
|
|||||||
Regions []string `json:"regions"`
|
Regions []string `json:"regions"`
|
||||||
|
|
||||||
// Cyberghost
|
// Cyberghost
|
||||||
Group string `json:"group"`
|
Groups []string `json:"groups"`
|
||||||
|
|
||||||
// Fastestvpn, HideMyAss, IPVanish, IVPN, Mullvad, PrivateVPN, Protonvpn, PureVPN, VPNUnlimited
|
// Fastestvpn, HideMyAss, IPVanish, IVPN, Mullvad, PrivateVPN, Protonvpn, PureVPN, VPNUnlimited
|
||||||
Countries []string `json:"countries"`
|
Countries []string `json:"countries"`
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package constants
|
package constants
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sort"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -20,11 +22,20 @@ func CyberghostRegionChoices() (choices []string) {
|
|||||||
|
|
||||||
func CyberghostGroupChoices() (choices []string) {
|
func CyberghostGroupChoices() (choices []string) {
|
||||||
servers := CyberghostServers()
|
servers := CyberghostServers()
|
||||||
choices = make([]string, len(servers))
|
uniqueChoices := map[string]struct{}{}
|
||||||
for i := range servers {
|
for _, server := range servers {
|
||||||
choices[i] = servers[i].Group
|
uniqueChoices[server.Group] = struct{}{}
|
||||||
}
|
}
|
||||||
return makeUnique(choices)
|
|
||||||
|
choices = make([]string, 0, len(uniqueChoices))
|
||||||
|
for choice := range uniqueChoices {
|
||||||
|
choices = append(choices, choice)
|
||||||
|
}
|
||||||
|
|
||||||
|
sortable := sort.StringSlice(choices)
|
||||||
|
sortable.Sort()
|
||||||
|
|
||||||
|
return sortable
|
||||||
}
|
}
|
||||||
|
|
||||||
func CyberghostHostnameChoices() (choices []string) {
|
func CyberghostHostnameChoices() (choices []string) {
|
||||||
|
|||||||
18
internal/constants/cyberghost_test.go
Normal file
18
internal/constants/cyberghost_test.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package constants
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_CyberghostGroupChoices(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
expected := []string{"Premium TCP Asia", "Premium TCP Europe",
|
||||||
|
"Premium TCP USA", "Premium UDP Asia", "Premium UDP Europe",
|
||||||
|
"Premium UDP USA"}
|
||||||
|
choices := CyberghostGroupChoices()
|
||||||
|
|
||||||
|
assert.Equal(t, expected, choices)
|
||||||
|
}
|
||||||
@@ -1,18 +1,41 @@
|
|||||||
package cyberghost
|
package cyberghost
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration"
|
"github.com/qdm12/gluetun/internal/configuration"
|
||||||
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
"github.com/qdm12/gluetun/internal/provider/utils"
|
"github.com/qdm12/gluetun/internal/provider/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var ErrGroupMismatchesProtocol = errors.New("server group does not match protocol")
|
||||||
|
|
||||||
func (c *Cyberghost) filterServers(selection configuration.ServerSelection) (
|
func (c *Cyberghost) filterServers(selection configuration.ServerSelection) (
|
||||||
servers []models.CyberghostServer, err error) {
|
servers []models.CyberghostServer, err error) {
|
||||||
|
if len(selection.Groups) == 0 {
|
||||||
|
if selection.TCP {
|
||||||
|
selection.Groups = tcpGroupChoices()
|
||||||
|
} else {
|
||||||
|
selection.Groups = udpGroupChoices()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check each group match the protocol
|
||||||
|
groupsCheckFn := groupsAreAllUDP
|
||||||
|
if selection.TCP {
|
||||||
|
groupsCheckFn = groupsAreAllTCP
|
||||||
|
}
|
||||||
|
if err := groupsCheckFn(selection.Groups); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
for _, server := range c.servers {
|
for _, server := range c.servers {
|
||||||
switch {
|
switch {
|
||||||
case selection.Group != "" && !strings.EqualFold(selection.Group, server.Group), // TODO make CSV
|
case
|
||||||
|
utils.FilterByPossibilities(server.Group, selection.Groups),
|
||||||
utils.FilterByPossibilities(server.Region, selection.Regions),
|
utils.FilterByPossibilities(server.Region, selection.Regions),
|
||||||
utils.FilterByPossibilities(server.Hostname, selection.Hostnames):
|
utils.FilterByPossibilities(server.Hostname, selection.Hostnames):
|
||||||
default:
|
default:
|
||||||
@@ -26,3 +49,51 @@ func (c *Cyberghost) filterServers(selection configuration.ServerSelection) (
|
|||||||
|
|
||||||
return servers, nil
|
return servers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func tcpGroupChoices() (choices []string) {
|
||||||
|
const tcp = true
|
||||||
|
return groupsForTCP(tcp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func udpGroupChoices() (choices []string) {
|
||||||
|
const tcp = false
|
||||||
|
return groupsForTCP(tcp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func groupsForTCP(tcp bool) (choices []string) {
|
||||||
|
allGroups := constants.CyberghostGroupChoices()
|
||||||
|
choices = make([]string, 0, len(allGroups))
|
||||||
|
for _, group := range allGroups {
|
||||||
|
switch {
|
||||||
|
case tcp && groupIsTCP(group):
|
||||||
|
choices = append(choices, group)
|
||||||
|
case !tcp && !groupIsTCP(group):
|
||||||
|
choices = append(choices, group)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return choices
|
||||||
|
}
|
||||||
|
|
||||||
|
func groupIsTCP(group string) bool {
|
||||||
|
return strings.Contains(strings.ToLower(group), "tcp")
|
||||||
|
}
|
||||||
|
|
||||||
|
func groupsAreAllTCP(groups []string) error {
|
||||||
|
for _, group := range groups {
|
||||||
|
if !groupIsTCP(group) {
|
||||||
|
return fmt.Errorf("%w: group %s for protocol TCP",
|
||||||
|
ErrGroupMismatchesProtocol, group)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func groupsAreAllUDP(groups []string) error {
|
||||||
|
for _, group := range groups {
|
||||||
|
if groupIsTCP(group) {
|
||||||
|
return fmt.Errorf("%w: group %s for protocol UDP",
|
||||||
|
ErrGroupMismatchesProtocol, group)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -21,77 +21,102 @@ func Test_Cyberghost_filterServers(t *testing.T) {
|
|||||||
"no servers": {
|
"no servers": {
|
||||||
err: errors.New("no server found: for protocol udp"),
|
err: errors.New("no server found: for protocol udp"),
|
||||||
},
|
},
|
||||||
"servers without filter": {
|
"servers without filter defaults to UDP": {
|
||||||
servers: []models.CyberghostServer{
|
servers: []models.CyberghostServer{
|
||||||
{Region: "a", Group: "1"},
|
{Region: "a", Group: "Premium TCP Asia"},
|
||||||
{Region: "b", Group: "1"},
|
{Region: "b", Group: "Premium TCP Europe"},
|
||||||
{Region: "c", Group: "2"},
|
{Region: "c", Group: "Premium UDP Asia"},
|
||||||
{Region: "d", Group: "2"},
|
{Region: "d", Group: "Premium UDP Europe"},
|
||||||
},
|
},
|
||||||
filteredServers: []models.CyberghostServer{
|
filteredServers: []models.CyberghostServer{
|
||||||
{Region: "a", Group: "1"},
|
{Region: "c", Group: "Premium UDP Asia"},
|
||||||
{Region: "b", Group: "1"},
|
{Region: "d", Group: "Premium UDP Europe"},
|
||||||
{Region: "c", Group: "2"},
|
},
|
||||||
{Region: "d", Group: "2"},
|
},
|
||||||
|
"servers with TCP selection": {
|
||||||
|
servers: []models.CyberghostServer{
|
||||||
|
{Region: "a", Group: "Premium TCP Asia"},
|
||||||
|
{Region: "b", Group: "Premium TCP Europe"},
|
||||||
|
{Region: "c", Group: "Premium UDP Asia"},
|
||||||
|
{Region: "d", Group: "Premium UDP Europe"},
|
||||||
|
},
|
||||||
|
selection: configuration.ServerSelection{
|
||||||
|
TCP: true,
|
||||||
|
},
|
||||||
|
filteredServers: []models.CyberghostServer{
|
||||||
|
{Region: "a", Group: "Premium TCP Asia"},
|
||||||
|
{Region: "b", Group: "Premium TCP Europe"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"servers with regions filter": {
|
"servers with regions filter": {
|
||||||
servers: []models.CyberghostServer{
|
servers: []models.CyberghostServer{
|
||||||
{Region: "a", Group: "1"},
|
{Region: "a", Group: "Premium UDP Asia"},
|
||||||
{Region: "b", Group: "1"},
|
{Region: "b", Group: "Premium UDP Asia"},
|
||||||
{Region: "c", Group: "2"},
|
{Region: "c", Group: "Premium UDP Asia"},
|
||||||
{Region: "d", Group: "2"},
|
{Region: "d", Group: "Premium UDP Asia"},
|
||||||
},
|
},
|
||||||
selection: configuration.ServerSelection{
|
selection: configuration.ServerSelection{
|
||||||
Regions: []string{"a", "c"},
|
Regions: []string{"a", "c"},
|
||||||
},
|
},
|
||||||
filteredServers: []models.CyberghostServer{
|
filteredServers: []models.CyberghostServer{
|
||||||
{Region: "a", Group: "1"},
|
{Region: "a", Group: "Premium UDP Asia"},
|
||||||
{Region: "c", Group: "2"},
|
{Region: "c", Group: "Premium UDP Asia"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"servers with group filter": {
|
"servers with group filter": {
|
||||||
servers: []models.CyberghostServer{
|
servers: []models.CyberghostServer{
|
||||||
{Region: "a", Group: "1"},
|
{Region: "a", Group: "Premium UDP Europe"},
|
||||||
{Region: "b", Group: "1"},
|
{Region: "b", Group: "Premium UDP Europe"},
|
||||||
{Region: "c", Group: "2"},
|
{Region: "c", Group: "Premium TCP Europe"},
|
||||||
{Region: "d", Group: "2"},
|
{Region: "d", Group: "Premium TCP Europe"},
|
||||||
},
|
},
|
||||||
selection: configuration.ServerSelection{
|
selection: configuration.ServerSelection{
|
||||||
Group: "1",
|
Groups: []string{"Premium UDP Europe"},
|
||||||
},
|
},
|
||||||
filteredServers: []models.CyberghostServer{
|
filteredServers: []models.CyberghostServer{
|
||||||
{Region: "a", Group: "1"},
|
{Region: "a", Group: "Premium UDP Europe"},
|
||||||
{Region: "b", Group: "1"},
|
{Region: "b", Group: "Premium UDP Europe"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"servers with bad group filter": {
|
||||||
|
servers: []models.CyberghostServer{
|
||||||
|
{Region: "a", Group: "Premium TCP Europe"},
|
||||||
|
{Region: "b", Group: "Premium TCP Europe"},
|
||||||
|
{Region: "c", Group: "Premium UDP Europe"},
|
||||||
|
{Region: "d", Group: "Premium UDP Europe"},
|
||||||
|
},
|
||||||
|
selection: configuration.ServerSelection{
|
||||||
|
Groups: []string{"Premium TCP Europe"},
|
||||||
|
},
|
||||||
|
err: errors.New("server group does not match protocol: group Premium TCP Europe for protocol UDP"),
|
||||||
|
},
|
||||||
"servers with regions and group filter": {
|
"servers with regions and group filter": {
|
||||||
servers: []models.CyberghostServer{
|
servers: []models.CyberghostServer{
|
||||||
{Region: "a", Group: "1"},
|
{Region: "a", Group: "Premium UDP Europe"},
|
||||||
{Region: "b", Group: "1"},
|
{Region: "b", Group: "Premium TCP Europe"},
|
||||||
{Region: "c", Group: "2"},
|
{Region: "c", Group: "Premium UDP Asia"},
|
||||||
{Region: "d", Group: "2"},
|
{Region: "d", Group: "Premium TCP Asia"},
|
||||||
},
|
},
|
||||||
selection: configuration.ServerSelection{
|
selection: configuration.ServerSelection{
|
||||||
Regions: []string{"a", "c"},
|
Regions: []string{"a", "c"},
|
||||||
Group: "1",
|
Groups: []string{"Premium UDP Europe"},
|
||||||
},
|
},
|
||||||
filteredServers: []models.CyberghostServer{
|
filteredServers: []models.CyberghostServer{
|
||||||
{Region: "a", Group: "1"},
|
{Region: "a", Group: "Premium UDP Europe"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"servers with hostnames filter": {
|
"servers with hostnames filter": {
|
||||||
servers: []models.CyberghostServer{
|
servers: []models.CyberghostServer{
|
||||||
{Hostname: "a"},
|
{Hostname: "a", Group: "Premium UDP Asia"},
|
||||||
{Hostname: "b"},
|
{Hostname: "b", Group: "Premium UDP Asia"},
|
||||||
{Hostname: "c"},
|
{Hostname: "c", Group: "Premium UDP Asia"},
|
||||||
},
|
},
|
||||||
selection: configuration.ServerSelection{
|
selection: configuration.ServerSelection{
|
||||||
Hostnames: []string{"a", "c"},
|
Hostnames: []string{"a", "c"},
|
||||||
},
|
},
|
||||||
filteredServers: []models.CyberghostServer{
|
filteredServers: []models.CyberghostServer{
|
||||||
{Hostname: "a"},
|
{Hostname: "a", Group: "Premium UDP Asia"},
|
||||||
{Hostname: "c"},
|
{Hostname: "c", Group: "Premium UDP Asia"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -113,3 +138,25 @@ func Test_Cyberghost_filterServers(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_tcpGroupChoices(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
expected := []string{
|
||||||
|
"Premium TCP Asia", "Premium TCP Europe", "Premium TCP USA",
|
||||||
|
}
|
||||||
|
choices := tcpGroupChoices()
|
||||||
|
|
||||||
|
assert.Equal(t, expected, choices)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_udpGroupChoices(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
expected := []string{
|
||||||
|
"Premium UDP Asia", "Premium UDP Europe", "Premium UDP USA",
|
||||||
|
}
|
||||||
|
choices := udpGroupChoices()
|
||||||
|
|
||||||
|
assert.Equal(t, expected, choices)
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,8 +25,13 @@ func NoServerFoundError(selection configuration.ServerSelection) (err error) {
|
|||||||
}
|
}
|
||||||
messageParts = append(messageParts, "protocol "+protocol)
|
messageParts = append(messageParts, "protocol "+protocol)
|
||||||
|
|
||||||
if selection.Group != "" {
|
switch len(selection.Countries) {
|
||||||
part := "group " + selection.Group
|
case 0:
|
||||||
|
case 1:
|
||||||
|
part := "group " + selection.Groups[0]
|
||||||
|
messageParts = append(messageParts, part)
|
||||||
|
default:
|
||||||
|
part := "groups " + commaJoin(selection.Groups)
|
||||||
messageParts = append(messageParts, part)
|
messageParts = append(messageParts, part)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user