1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
|
// Package profiles defines a wrapper around the various profiles
// returned by the /info endpoint
package profiles
import (
"github.com/eduvpn/eduvpn-common/types/protocol"
"github.com/eduvpn/eduvpn-common/types/server"
)
// Profile is the information for a profile
type Profile struct {
// ID is the identifier of the profile
// Used to select a profile
ID string `json:"profile_id"`
// DisplayName defines the UI friendly name for the profile
DisplayName string `json:"display_name"`
// VPNProtoList defines the list of VPN protocols
// E.g. wireguard, openvpn
VPNProtoList []string `json:"vpn_proto_list"`
// VPNProtoTransportList defines the list of VPN protocols including their transport values
// E.g. wireguard+udp, openvpn+tcp
VPNProtoTransportList []string `json:"vpn_proto_transport_list"`
// DefaultGateway specifies whether or not this profile is a default gateway profile
DefaultGateway bool `json:"default_gateway"`
// DNSSearchDomains specifies the list of dns search domains
// This is provided for a Linux client issue
// See: https://github.com/eduvpn/python-eduvpn-client/issues/550
DNSSearchDomains []string `json:"dns_search_domain_list"`
}
// ListInfo is the struct that has the profile list
type ListInfo struct {
ProfileList []Profile `json:"profile_list"`
}
// Info is the top-level struct for the info endpoint
type Info struct {
Info ListInfo `json:"info"`
}
// Len returns the length of the profile list
func (i Info) Len() int {
return len(i.Info.ProfileList)
}
// Get returns a profile with id `id`, it returns nil if it is not found
func (i Info) Get(id string) *Profile {
for _, p := range i.Info.ProfileList {
if p.ID == id {
return &p
}
}
return nil
}
// MustIndex gets a profile by index
// This index must be in the bounds
func (i Info) MustIndex(n int) Profile {
return i.Info.ProfileList[n]
}
func hasProtocol(protos []string, proto protocol.Protocol) bool {
for _, p := range protos {
if protocol.New(p) == proto {
return true
}
}
return false
}
// ShouldFailover returns whether or not this VPN profile should start a failover procedure
// This is true when the profile supports a TCP connection
// If we cannot determine whether it supports a TCP connection
// (because the server doesn't provide the VPN transport list function yet),
// we will just check if it supports OpenVPN
func (p *Profile) ShouldFailover() bool {
// old servers don't support it, only failover in case OpenVPN is supported
if len(p.VPNProtoTransportList) == 0 {
// this checks VPNProtoList
return p.HasOpenVPN()
}
for _, c := range p.VPNProtoTransportList {
if c == "wireguard+tcp" {
return true
}
if c == "openvpn+tcp" {
return true
}
}
return false
}
// HasOpenVPN returns whether or not the profile has OpenVPN support
func (p *Profile) HasOpenVPN() bool {
return hasProtocol(p.VPNProtoList, protocol.OpenVPN)
}
// HasWireGuard returns whether or not the profile has WireGuard support
func (p *Profile) HasWireGuard() bool {
return hasProtocol(p.VPNProtoList, protocol.WireGuard)
}
// FilterWireGuard gets a profile list but without WireGuard profiles
func (i Info) FilterWireGuard() *Info {
var ret []Profile
for _, p := range i.Info.ProfileList {
if !p.HasOpenVPN() {
continue
}
}
return &Info{
Info: ListInfo{
ProfileList: ret,
},
}
}
// Public gets the server list as a structure that we return to clients
func (i Info) Public() server.Profiles {
m := make(map[string]server.Profile)
for _, p := range i.Info.ProfileList {
m[p.ID] = server.Profile{
DisplayName: map[string]string{
"en": p.DisplayName,
},
DefaultGateway: p.DefaultGateway,
}
}
return server.Profiles{Map: m}
}
|