diff options
41 files changed, 382 insertions, 110 deletions
diff --git a/client/client.go b/client/client.go index 07287cf..f00d56e 100644 --- a/client/client.go +++ b/client/client.go @@ -55,6 +55,9 @@ type Client struct { mu sync.Mutex } +// GettingConfig is defined here to satisfy the server.Callbacks interface +// It is called when internally we are getting a config +// We go to the GettingConfig state func (c *Client) GettingConfig() error { if c.FSM.InState(StateGettingConfig) { return nil @@ -63,6 +66,9 @@ func (c *Client) GettingConfig() error { return err } +// InvalidProfile is defined here to satisfy the server.Callbacks interface +// It is called when a profile is invalid +// Here we call the AskProfile transition func (c *Client) InvalidProfile(ctx context.Context, srv *server.Server) (string, error) { // TODO: should this have profiles as a parameter ck := cookie.NewWithContext(ctx) @@ -157,6 +163,8 @@ func New(name string, version string, directory string, stateCallback func(FSMSt return c, nil } +// TriggerAuth is called when authorization is triggered +// This function satisfies the server.Callbacks interface func (c *Client) TriggerAuth(ctx context.Context, url string, wait bool) (string, error) { // Get a reply from the client if wait { @@ -185,6 +193,8 @@ func (c *Client) TriggerAuth(ctx context.Context, url string, wait bool) (string return "", nil } +// AuthDone is called when authorization is done +// This is defined to satisfy the server.Callbacks interface func (c *Client) AuthDone(id string, t srvtypes.Type) { srv, err := c.Servers.GetServer(id, t) if err == nil { @@ -198,6 +208,9 @@ func (c *Client) AuthDone(id string, t srvtypes.Type) { } } +// TokensUpdated is called when tokens are updated +// It updates the cache map and the client tokens +// This is defined to satisfy the server.Callbacks interface func (c *Client) TokensUpdated(id string, t srvtypes.Type, tok eduoauth.Token) { if tok.Access == "" { return @@ -219,7 +232,7 @@ func (c *Client) TokensUpdated(id string, t srvtypes.Type, tok eduoauth.Token) { }) } -// Registering means updating the FSM to get to the initial state correctly +// Register means updating the FSM to get to the initial state correctly func (c *Client) Register() error { err := c.goTransition(StateMain) if err != nil { @@ -289,6 +302,8 @@ func (c *Client) locationCallback(ck *cookie.Cookie, orgID string) error { return nil } +// TrySave tries to save the internal state file +// If an error occurs it logs it func (c *Client) TrySave() { err := c.cfg.Save() if err != nil { @@ -375,7 +390,7 @@ func (c *Client) GetConfig(ck *cookie.Cookie, identifier string, _type srvtypes. if err == nil { // it could be that we are not in getting config yet if we have just done authorization c.FSM.GoTransition(StateGettingConfig) //nolint:errcheck - c.FSM.GoTransition(StateGotConfig) //nolint:errcheck + c.FSM.GoTransition(StateGotConfig) //nolint:errcheck } else if !c.FSM.InState(previousState) { // go back to the previous state if an error occurred c.FSM.GoTransition(previousState) //nolint:errcheck @@ -402,7 +417,7 @@ func (c *Client) GetConfig(ck *cookie.Cookie, identifier string, _type srvtypes. case srvtypes.TypeSecureInternet: srv, err = c.Servers.GetSecure(ck.Context(), identifier, c.cfg.Discovery(), tok, startup) - var cErr *discovery.CountryNotFoundError + var cErr *discovery.ErrCountryNotFound if errors.As(err, &cErr) { err = c.locationCallback(ck, identifier) if err == nil { @@ -431,6 +446,7 @@ func (c *Client) GetConfig(ck *cookie.Cookie, identifier string, _type srvtypes. return cfg, nil } +// RemoveServer removes a server func (c *Client) RemoveServer(identifier string, _type srvtypes.Type) (err error) { identifier, err = c.convertIdentifier(identifier, _type) if err != nil { @@ -443,6 +459,7 @@ func (c *Client) RemoveServer(identifier string, _type srvtypes.Type) (err error return nil } +// CurrentServer gets the current server that is configured func (c *Client) CurrentServer() (*srvtypes.Current, error) { curr, err := c.Servers.PublicCurrent(c.cfg.Discovery()) if err != nil { @@ -451,6 +468,7 @@ func (c *Client) CurrentServer() (*srvtypes.Current, error) { return curr, nil } +// SetProfileID set the profile ID `pID` for the current server func (c *Client) SetProfileID(pID string) error { srv, err := c.Servers.CurrentServer() if err != nil { @@ -481,12 +499,13 @@ func (c *Client) retrieveTokens(sid string, t srvtypes.Type) (*eduoauth.Token, e }, nil } +// Cleanup cleans up the VPN connection by sending a /disconnect func (c *Client) Cleanup(ck *cookie.Cookie) error { srv, err := c.Servers.CurrentServer() if err != nil { return i18nerr.Wrap(err, "The current server was not found when cleaning up the connection") } - tok, err := c.retrieveTokens(srv.T.ID, srv.T.T) + tok, err := c.retrieveTokens(srv.Key.ID, srv.Key.T) if err != nil { return i18nerr.Wrap(err, "No OAuth tokens were found when cleaning up the connection") } @@ -501,6 +520,8 @@ func (c *Client) Cleanup(ck *cookie.Cookie) error { return nil } +// SetSecureLocation sets a secure internet location for +// organization ID `orgID` with country code `countryCode` func (c *Client) SetSecureLocation(orgID string, countryCode string) error { // not supported with Let's Connect! & govVPN if !c.hasDiscovery() { @@ -514,6 +535,8 @@ func (c *Client) SetSecureLocation(orgID string, countryCode string) error { return nil } +// RenewSession is called when the user clicks on the renew session button +// It re-authorized the server by getting a server without passing tokens func (c *Client) RenewSession(ck *cookie.Cookie) error { // getting the current serving with nil tokens means re-authorize srv, err := c.Servers.CurrentServer() @@ -529,6 +552,7 @@ func (c *Client) RenewSession(ck *cookie.Cookie) error { return nil } +// StartFailover starts the failover procedure func (c *Client) StartFailover(ck *cookie.Cookie, gateway string, mtu int, readRxBytes func() (int64, error)) (bool, error) { f := failover.New(readRxBytes) @@ -540,6 +564,7 @@ func (c *Client) StartFailover(ck *cookie.Cookie, gateway string, mtu int, readR return d, nil } +// ServerList gets the list of servers func (c *Client) ServerList() (*srvtypes.List, error) { g := c.cfg.V2.PublicList(c.cfg.Discovery()) return g, nil diff --git a/client/client_test.go b/client/client_test.go index 1d4bf44..a221c93 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -370,7 +370,7 @@ func TestInvalidClientID(t *testing.T) { k, "0.1.0-test", dir, - func(old FSMStateID, new FSMStateID, data interface{}) bool { + func(_ FSMStateID, _ FSMStateID, _ interface{}) bool { return true }, false, diff --git a/client/fsm.go b/client/fsm.go index 42c0029..175b832 100644 --- a/client/fsm.go +++ b/client/fsm.go @@ -9,9 +9,13 @@ import ( ) type ( - FSMStateID = fsm.StateID - FSMStates = fsm.States - FSMState = fsm.State + // FSMStateID is an alias to the fsm state ID type + FSMStateID = fsm.StateID + // FSMStates is an alias to the fsm states type + FSMStates = fsm.States + // FSMState is an alias to the fsm state type + FSMState = fsm.State + // FSMTransition is an alias to the fsm transition type FSMTransition = fsm.Transition ) @@ -53,6 +57,7 @@ const ( StateDisconnected ) +// GetStateName gets the State name for state `s` func GetStateName(s FSMStateID) string { switch s { case StateDeregistered: @@ -166,6 +171,7 @@ func newFSM( return returnedFSM } +// SetState sets the state for the client FSM to `state` func (c *Client) SetState(state FSMStateID) error { c.mu.Lock() defer c.mu.Unlock() @@ -182,6 +188,7 @@ func (c *Client) SetState(state FSMStateID) error { return nil } +// InState returns whether or not the client is in state `state` func (c *Client) InState(state FSMStateID) bool { c.mu.Lock() defer c.mu.Unlock() diff --git a/client/proxy.go b/client/proxy.go index 0e78792..4165c0f 100644 --- a/client/proxy.go +++ b/client/proxy.go @@ -7,17 +7,21 @@ import ( "github.com/eduvpn/eduvpn-common/types/cookie" ) +// ProxyLogger is defined here such that we can update the proxyguard logger type ProxyLogger struct{} +// Logf logs a message with parameters func (pl *ProxyLogger) Logf(msg string, params ...interface{}) { log.Logger.Debugf(msg, params...) } +// Log logs a message func (pl *ProxyLogger) Log(msg string) { log.Logger.Debugf("%s", msg) } func (c *Client) StartProxyguard(ck *cookie.Cookie, listen string, tcpsp int, peer string) error { +// StartProxyguard starts proxyguard for proxied WireGuard connections var err error proxyguard.UpdateLogger(&ProxyLogger{}) err = proxyguard.Client(ck.Context(), listen, tcpsp, peer, -1) diff --git a/client/token.go b/client/token.go index d62308b..03ec1d6 100644 --- a/client/token.go +++ b/client/token.go @@ -10,12 +10,17 @@ import ( type cacheMap map[string]eduoauth.Token +// TokenCacher is a structure that caches tokens for each type of server type TokenCacher struct { + // InstituteAccess is the cached map for institute access servers InstituteAccess cacheMap - CustomServer cacheMap - SecureInternet *eduoauth.Token + // CustomServer is the cached map for custom server + CustomServer cacheMap + // SecureInternet is the cached map for the secure internet server + SecureInternet *eduoauth.Token } +// Get gets tokens from the cache map func (c *cacheMap) Get(id string) (*eduoauth.Token, error) { if c == nil || len(*c) == 0 { return nil, errors.New("no cache map available") @@ -26,6 +31,7 @@ func (c *cacheMap) Get(id string) (*eduoauth.Token, error) { return nil, fmt.Errorf("identifier: '%s' does not exist in token cache map", id) } +// Get gets tokens using a server id and type from the cacher func (tc *TokenCacher) Get(id string, t srvtypes.Type) (*eduoauth.Token, error) { switch t { case srvtypes.TypeCustom: @@ -41,6 +47,7 @@ func (tc *TokenCacher) Get(id string, t srvtypes.Type) (*eduoauth.Token, error) return nil, fmt.Errorf("invalid type for token cacher get: %d", t) } +// Set updates the cache for the server id `id` with tokens `t` func (c *cacheMap) Set(id string, t eduoauth.Token) { if c == nil || len(*c) == 0 { *c = make(cacheMap) @@ -48,6 +55,7 @@ func (c *cacheMap) Set(id string, t eduoauth.Token) { (*c)[id] = t } +// Set updates the top-level cacher for a specific server type func (tc *TokenCacher) Set(id string, t srvtypes.Type, tok eduoauth.Token) error { switch t { case srvtypes.TypeCustom: diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 8696813..f769044 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -1,3 +1,4 @@ +// Package main implements an example CLI client package main import ( diff --git a/exports/exports.go b/exports/exports.go index ea4d4bd..4e08e95 100644 --- a/exports/exports.go +++ b/exports/exports.go @@ -26,7 +26,7 @@ typedef void (*TokenSetter)(const char* server_id, int server_type, const char* static long long int get_read_rx_bytes(ReadRxBytes read) { - return read(); + return read(); } static int call_callback(StateCB callback, int oldstate, int newstate, void* data) { @@ -34,11 +34,12 @@ static int call_callback(StateCB callback, int oldstate, int newstate, void* dat } static void call_token_getter(TokenGetter getter, const char* server_id, int server_type, char* out, size_t len) { - getter(server_id, server_type, out, len); + getter(server_id, server_type, out, len); } static void call_token_setter(TokenSetter setter, const char* server_id, int server_type, const char* tokens) { - setter(server_id, server_type, tokens); + setter(server_id, server_type, tokens); +} } */ import "C" @@ -58,6 +59,7 @@ import ( srvtypes "github.com/eduvpn/eduvpn-common/types/server" ) +// VPNState is the current state of the library var VPNState *client.Client func getCError(err error) *C.char { @@ -884,10 +886,10 @@ func StartFailover(c C.uintptr_t, gateway *C.char, mtu C.int, readRxBytes C.Read // This proxies WireGuard UDP connections over TCP. // These input variables can be gotten from the configuration that is retrieved using the `proxy` JSON key // -// - `c` is the cookie -// - `listen` is the ip:port of the local udp connection, this is what is set to the WireGuard endpoint -// - `tcpsp` is the TCP source port -// - `peer` is the ip:port of the remote server +// - `c` is the cookie +// - `listen` is the ip:port of the local udp connection, this is what is set to the WireGuard endpoint +// - `tcpsp` is the TCP source port +// - `peer` is the ip:port of the remote server // // If the proxy cannot be started it returns an error // @@ -1059,8 +1061,8 @@ func SetTokenHandler(getter C.TokenGetter, setter C.TokenSetter) *C.char { // // - Send a reply to a state transition (ASK_PROFILE and ASK_LOCATION) // -// Functions that take a cookie have it as the first argument - +// # Functions that take a cookie have it as the first argument +// // Example Input: ```CookieNew()``` // // Example Output: ```5``` diff --git a/i18nerr/i18nerr.go b/i18nerr/i18nerr.go index 2dea504..553a7d5 100644 --- a/i18nerr/i18nerr.go +++ b/i18nerr/i18nerr.go @@ -1,4 +1,4 @@ -// package i18nerr implements errors with internationalization using gotext +// Package i18nerr implements errors with internationalization using gotext package i18nerr import ( @@ -138,7 +138,7 @@ func Wrapf(err error, key message.Reference, args ...interface{}) *Error { return &Error{key: key, args: args, wrapped: &Error{key: t, Misc: misc}, Misc: misc} } -// NewInternalf creates an internal localised error from a display string +// NewInternal creates an internal localised error from a display string func NewInternal(disp string) *Error { return Wrap(errors.New(disp), "An internal error occurred") } diff --git a/internal/api/api.go b/internal/api/api.go index a9ea0c0..2268357 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -1,3 +1,4 @@ +// Package api implements version 3 of the eduVPN api: https://docs.eduvpn.org/server/v3/api.html package api import ( @@ -21,12 +22,18 @@ import ( "github.com/eduvpn/eduvpn-common/types/server" ) +// Callbacks is the API callback interface +// It is used to trigger authorization and forward token updates type Callbacks interface { + // TriggerAuth is called when authorization should be triggered TriggerAuth(context.Context, string, bool) (string, error) + // AuthDone is called when authorization has just completed AuthDone(string, server.Type) + // TokensUpdates is called when tokens are updated TokensUpdated(string, server.Type, eduoauth.Token) } +// ServerData is the data for a server that is passed to the API struct type ServerData struct { // ID is the identifier for the server ID string @@ -44,6 +51,7 @@ type ServerData struct { DisableAuthorize bool } +// API is the top-level struct that each method is defined on type API struct { cb Callbacks // oauth is the oauth object @@ -91,6 +99,7 @@ func NewAPI(ctx context.Context, clientID string, sd ServerData, cb Callbacks, t return api, nil } +// ErrAuthorizeDisabled is returned when authorization is disabled but is needed to complete var ErrAuthorizeDisabled = errors.New("cannot authorize as re-authorization is disabled") func (a *API) authorize(ctx context.Context) (err error) { @@ -168,11 +177,14 @@ func (a *API) authorizedRetry(ctx context.Context, method string, endpoint strin return h, body, err } +// Disconnect disconnects a client from the server by sending a /disconnect API call +// This cleans up resources such as WireGuard IP allocation func (a *API) Disconnect(ctx context.Context) error { _, _, err := a.authorized(ctx, http.MethodPost, "/disconnect", &httpw.OptionalParams{Timeout: 5 * time.Second}) return err } +// Info does the /info API call func (a *API) Info(ctx context.Context) (*profiles.Info, error) { _, body, err := a.authorizedRetry(ctx, http.MethodGet, "/info", nil) if err != nil { @@ -185,16 +197,16 @@ func (a *API) Info(ctx context.Context) (*profiles.Info, error) { return &p, nil } -type Proxy struct { - Listen string - Peer string -} - +// ConnectData is the data that is returned when the /connect call completes without error type ConnectData struct { + // Configuration is the VPN configuration Configuration string - Protocol protocol.Protocol - Expires time.Time - Proxy *wireguard.Proxy + // Protocol tells us what protocol it is, OpenVPN or WireGuard (proxied or not) + Protocol protocol.Protocol + // Expires tells us when this configuration expires + Expires time.Time + // Proxy is filled when WireGuard is proxied + Proxy *wireguard.Proxy } // see https://github.com/eduvpn/documentation/blob/v3/API.md#request-1 @@ -345,12 +357,16 @@ func refreshEndpoints(ctx context.Context, sd ServerData) (*endpoints.List, *end return &ep.API.V3, &epauth.API.V3, err } +// OAuthLogger is defined here to update the internal logger +// for the eduoauth library type OAuthLogger struct{} +// Logf logs a message with parameters func (ol *OAuthLogger) Logf(msg string, params ...interface{}) { log.Logger.Debugf(msg, params...) } +// Log logs a message func (ol *OAuthLogger) Log(msg string) { log.Logger.Debugf("%s", msg) } diff --git a/internal/api/endpoints/endpoints.go b/internal/api/endpoints/endpoints.go index 11e244b..c98d2c7 100644 --- a/internal/api/endpoints/endpoints.go +++ b/internal/api/endpoints/endpoints.go @@ -1,3 +1,5 @@ +// Package endpoints defines a wrapper around the various +// endpoints returned by an eduVPN server in well-known package endpoints import ( @@ -5,21 +7,30 @@ import ( "net/url" ) +// List is the list of endpoints as returned by the eduVPN server type List struct { - API string `json:"api_endpoint"` + // API is the API endpoint which we use for calls such as /info, /connect, ... + API string `json:"api_endpoint"` + // Authorization is the authorization endpoint for OAuth Authorization string `json:"authorization_endpoint"` - Token string `json:"token_endpoint"` + // Token is the token endpoint for OAuth + Token string `json:"token_endpoint"` } +// Versions is the endpoints separated by API version type Versions struct { + // V2 is the legacy V2 API, this is not used V2 List `json:"http://eduvpn.org/api#2"` + // V3 is the newest API, which we use V3 List `json:"http://eduvpn.org/api#3"` } // Endpoints defines the json format for /.well-known/vpn-user-portal". type Endpoints struct { + // API defines the API endpoints, split by version API Versions `json:"api"` - V string `json:"v"` + // V is the version string for the server + V string `json:"v"` } // Validate validates the endpoints by parsing them and checking the scheme is HTTP diff --git a/internal/api/profiles/profiles.go b/internal/api/profiles/profiles.go index 111a835..d31bbcc 100644 --- a/internal/api/profiles/profiles.go +++ b/internal/api/profiles/profiles.go @@ -1,3 +1,5 @@ +// Package profiles defines a wrapper around the various profiles +// returned by the /info endpoint package profiles import ( @@ -5,27 +7,43 @@ import ( "github.com/eduvpn/eduvpn-common/types/server" ) +// Profile is the information for a profile type Profile struct { - ID string `json:"profile_id"` - DisplayName string `json:"display_name"` - VPNProtoList []string `json:"vpn_proto_list"` + // 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 bool `json:"default_gateway"` - DNSSearchDomains []string `json:"dns_search_domain_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 { @@ -35,6 +53,8 @@ func (i Info) Get(id string) *Profile { 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] } @@ -48,6 +68,11 @@ func hasProtocol(protos []string, proto protocol.Protocol) bool { 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 { @@ -65,14 +90,17 @@ func (p *Profile) ShouldFailover() bool { 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 { @@ -87,6 +115,7 @@ func (i Info) FilterWireGuard() *Info { } } +// 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 { diff --git a/internal/config/config.go b/internal/config/config.go index 59d47e0..ed424f8 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -16,19 +16,23 @@ import ( const stateFile = "state.json" +// Config represents the config state file type Config struct { directory string - V2 *v2.V2 + // V2 indicates we are version 2 + V2 *v2.V2 } func (c *Config) filename() string { return path.Join(c.directory, stateFile) } +// Discovery gets the discovery list from the state file func (c *Config) Discovery() *discovery.Discovery { return &c.V2.Discovery } +// Save saves the state file to disk func (c *Config) Save() error { if err := util.EnsureDirectory(c.directory); err != nil { return err @@ -45,6 +49,7 @@ func (c *Config) Save() error { return nil } +// Load loads the state file from disk func (c *Config) Load() error { bts, err := os.ReadFile(c.filename()) if err != nil { @@ -64,11 +69,16 @@ func (c *Config) Load() error { return nil } +// Versioned is the final top-level state file that is written to disk type Versioned struct { + // V1 is the version 1 state file that is no longer used but converted from V1 *v1.V1 `json:"v1,omitempty"` + // V2 is the version 2 state file V2 *v2.V2 `json:"v2,omitempty"` } +// NewFromDirectory creates a new config struct from a directory +// It does this by loading the JSON file from disk func NewFromDirectory(dir string) *Config { cfg := Config{ directory: dir, diff --git a/internal/config/v1/v1.go b/internal/config/v1/v1.go index 1e374b1..1d96d13 100644 --- a/internal/config/v1/v1.go +++ b/internal/config/v1/v1.go @@ -12,49 +12,78 @@ import ( "github.com/eduvpn/eduvpn-common/types/server" ) +// Profiles is the list of profiles type Profiles struct { profiles.Info Current string `json:"current_profile"` } +// Base is the base type of a server type Base struct { - BaseURL string `json:"base_url"` - Profiles Profiles `json:"profiles"` - StartTime time.Time `json:"start_time"` + // BaseURL is the base_url from discovery + BaseURL string `json:"base_url"` + // profiles is the last of profile + Profiles Profiles `json:"profiles"` + // StartTime is the time when we started the connection + StartTime time.Time `json:"start_time"` + // StartTimeOAuth is the time when we last started OAuth StartTimeOAuth time.Time `json:"start_time_oauth"` - ExpireTime time.Time `json:"expire_time"` + // ExpireTime is the time when the connection expires + ExpireTime time.Time `json:"expire_time"` } +// InstituteServer is the struct that represents an institute access server type InstituteServer struct { - Base Base `json:"base"` + // Base is the base of the server + Base Base `json:"base"` + // Profiles are the list of profiles Profiles Profiles `json:"profiles"` } +// InstituteServers is a list of institute access servers type InstituteServers struct { - Map map[string]InstituteServer `json:"map"` - CurrentURL string `json:"current_url"` + // Map is the map from base url to an institute access server + Map map[string]InstituteServer `json:"map"` + // CurrentURL is the URL of the currently configured server + CurrentURL string `json:"current_url"` } type ( - CustomServer = InstituteServer + // CustomServer is an alias to InstituteServer + CustomServer = InstituteServer + // CustomServers is an alias to InstituteServers CustomServers = InstituteServers ) +// SecureInternetHome represents a secure internet home server type SecureInternetHome struct { - BaseMap map[string]*Base `json:"base_map"` - DisplayName map[string]string `json:"display_name"` - HomeOrganizationID string `json:"home_organization_id"` - CurrentLocation string `json:"current_location"` + // BaseMap is the map from country code to a server base + BaseMap map[string]*Base `json:"base_map"` + // DisplayName is the map from language code to UI name + DisplayName map[string]string `json:"display_name"` + // HomeOrganizationID is the identifier of the home organization + HomeOrganizationID string `json:"home_organization_id"` + // CurrentLocation is the country code of the currently configured server + CurrentLocation string `json:"current_location"` } +// Servers represents the list of servers type Servers struct { - Custom CustomServers `json:"custom_servers"` - Institute InstituteServers `json:"institute_servers"` + // Custom are the "custom" servers; the servers that are added by the user + Custom CustomServers `json:"custom_servers"` + // Institute are the institute access servers configured from discovery + Institute InstituteServers `json:"institute_servers"` + // SecureInternetHome is the secure internet home server + // Also obtained through discovery SecureInternetHome SecureInternetHome `json:"secure_internet_home"` - IsType server.Type `json:"is_secure_internet"` + // IsType represents which server type was last configured + IsType server.Type `json:"is_secure_internet"` } +// V1 is the top-level struct for the first version of the state file type V1 struct { + // Discovery is the list of discovery servers Discovery discovery.Discovery `json:"discovery"` - Servers Servers `json:"servers"` + // Servers is the list of servers in the app + Servers Servers `json:"servers"` } diff --git a/internal/config/v2/convert.go b/internal/config/v2/convert.go index 0212749..21f39a9 100644 --- a/internal/config/v2/convert.go +++ b/internal/config/v2/convert.go @@ -15,11 +15,11 @@ func v1AuthTime(st time.Time, ost time.Time) time.Time { return ost } -func convertV1Server(list v1.InstituteServers, iscurrent bool, t server.Type) (map[ServerType]*Server, *ServerType) { - ret := make(map[ServerType]*Server) - var lc *ServerType +func convertV1Server(list v1.InstituteServers, iscurrent bool, t server.Type) (map[ServerKey]*Server, *ServerKey) { + ret := make(map[ServerKey]*Server) + var lc *ServerKey for k, v := range list.Map { - key := ServerType{ + key := ServerKey{ T: t, ID: k, } @@ -37,10 +37,11 @@ func convertV1Server(list v1.InstituteServers, iscurrent bool, t server.Type) (m return ret, lc } +// FromV1 converts a version 1 state struct into a v2 one func FromV1(ver1 *v1.V1) *V2 { gsrvs := ver1.Servers - var lc *ServerType + var lc *ServerKey cust, glc := convertV1Server(gsrvs.Custom, gsrvs.IsType == server.TypeCustom, server.TypeCustom) if lc == nil { lc = glc @@ -64,7 +65,7 @@ func FromV1(ver1 *v1.V1) *V2 { } v, ok := sec.BaseMap[sec.CurrentLocation] if v != nil && ok { - t := ServerType{ + t := ServerKey{ T: server.TypeSecureInternet, ID: sec.HomeOrganizationID, } diff --git a/internal/config/v2/v2.go b/internal/config/v2/v2.go index 9608d54..51f7a5f 100644 --- a/internal/config/v2/v2.go +++ b/internal/config/v2/v2.go @@ -1,3 +1,4 @@ +// Package v2 implements version 2 of the state file package v2 import ( @@ -10,41 +11,52 @@ import ( "github.com/eduvpn/eduvpn-common/types/server" ) +// Server is the struct for each server type Server struct { - Profiles server.Profiles `json:"profiles"` - LastAuthorizeTime time.Time `json:"last_authorize_time,omitempty"` - ExpireTime time.Time `json:"expire_time,omitempty"` + // Profiles are the list of profiles + Profiles server.Profiles `json:"profiles"` + // LastAuthorizeTime is the time we last authorized + // This is used for determining when to show e.g. the renew button + LastAuthorizeTime time.Time `json:"last_authorize_time,omitempty"` + // ExpireTime is the time at which the VPN expires + ExpireTime time.Time `json:"expire_time,omitempty"` - // In case of secure internet: + // CountryCode is the country code for the server in case of secure internet + // Otherwise it is an empty string CountryCode string `json:"country_code"` } -type ServerType struct { - T server.Type +// ServerKey is the key type of the server map +type ServerKey struct { + // T is the type of server, e.g. secure internet + T server.Type + // ID is the identifier for the server ID string } const keyFormat = "%d,%s" -func newServerType(key string) (*ServerType, error) { +func newServerType(key string) (*ServerKey, error) { var t server.Type var id string if _, err := fmt.Sscanf(key, keyFormat, &t, &id); err != nil { return nil, err } - return &ServerType{ + return &ServerKey{ T: t, ID: id, }, nil } -func (st ServerType) MarshalText() ([]byte, error) { +// MarshalText convers the server key into one that can be used in a map +func (st ServerKey) MarshalText() ([]byte, error) { k := fmt.Sprintf(keyFormat, st.T, st.ID) return []byte(k), nil } -func (st *ServerType) UnmarshalText(text []byte) error { +// UnmarshalText converts the marshaled key into a ServerType struct +func (st *ServerKey) UnmarshalText(text []byte) error { k := string(text) g, err := newServerType(k) if err != nil { @@ -54,14 +66,21 @@ func (st *ServerType) UnmarshalText(text []byte) error { return nil } +// V2 is the top-level struct for the state file type V2 struct { - List map[ServerType]*Server `json:"server_list,omitempty"` - LastChosen *ServerType `json:"last_chosen_id,omitempty"` - Discovery discovery.Discovery `json:"discovery"` + // List is the list of servers + List map[ServerKey]*Server `json:"server_list,omitempty"` + // LastChosen represents the key of the last chosen server + // A server is chosen if we got a config for it + LastChosen *ServerKey `json:"last_chosen_id,omitempty"` + // Discovery is the cached list of discovery JSON + Discovery discovery.Discovery `json:"discovery"` } +// RemoveServer removes a server with id `id` and type `t` from the V2 struct +// It returns an error if no such server exists func (cfg *V2) RemoveServer(id string, t server.Type) error { - k := ServerType{ + k := ServerKey{ ID: id, T: t, } @@ -78,22 +97,26 @@ func (cfg *V2) RemoveServer(id string, t server.Type) error { return errors.New("server does not exist") } -func (cfg *V2) getServerWithKey(k ServerType) (*Server, error) { +func (cfg *V2) getServerWithKey(k ServerKey) (*Server, error) { if v, ok := cfg.List[k]; ok { return v, nil } return nil, errors.New("server does not exist") } +// GetServer gets a server with id `id` and type `t` +// If the server doesn't exist it returns nil and an error func (cfg *V2) GetServer(id string, t server.Type) (*Server, error) { - k := ServerType{ + k := ServerKey{ ID: id, T: t, } return cfg.getServerWithKey(k) } -func (cfg *V2) CurrentServer() (*Server, *ServerType, error) { +// CurrentServer gets the last chosen server +// It returns the server, the server type and an error if it doesn't exist +func (cfg *V2) CurrentServer() (*Server, *ServerKey, error) { if cfg.LastChosen == nil { return nil, nil, errors.New("no server chosen before") } @@ -104,6 +127,8 @@ func (cfg *V2) CurrentServer() (*Server, *ServerType, error) { return srv, cfg.LastChosen, nil } +// HasSecureInternet returns true whether or not the state file +// has a secure internet server in it func (cfg *V2) HasSecureInternet() bool { for k := range cfg.List { if k.T == server.TypeSecureInternet { @@ -113,21 +138,24 @@ func (cfg *V2) HasSecureInternet() bool { return false } +// AddServer adds a server with id `id`, type `t` and server `srv` func (cfg *V2) AddServer(id string, t server.Type, srv Server) error { if cfg.HasSecureInternet() && t == server.TypeSecureInternet { return errors.New("a secure internet server already exists, remove the other secure internet server first") } - k := ServerType{ + k := ServerKey{ ID: id, T: t, } if cfg.List == nil { - cfg.List = make(map[ServerType]*Server) + cfg.List = make(map[ServerKey]*Server) } cfg.List[k] = &srv return nil } +// PublicCurrent gets the current server as a type that should be returned to the client +// It returns this server or nil and an error if it doesn't exist func (cfg *V2) PublicCurrent(disco *discovery.Discovery) (*server.Current, error) { curr, _, err := cfg.CurrentServer() if err != nil { @@ -207,6 +235,7 @@ func convertSecure(orgID string, countryCode string, disco *discovery.Discovery) }, nil } +// PublicList gets all the servers in a format that is returned to the client func (cfg *V2) PublicList(disco *discovery.Discovery) *server.List { ret := &server.List{} // TODO: profile information? diff --git a/internal/config/v2/v2_test.go b/internal/config/v2/v2_test.go index 5a4c2ea..ce8de01 100644 --- a/internal/config/v2/v2_test.go +++ b/internal/config/v2/v2_test.go @@ -36,7 +36,7 @@ func TestLoad(t *testing.T) { } `, want: &V2{ - List: map[ServerType]*Server{ + List: map[ServerKey]*Server{ {ID: "a", T: server.TypeInstituteAccess}: { Profiles: server.Profiles{ Map: map[string]server.Profile{ @@ -103,7 +103,7 @@ func TestLoad(t *testing.T) { } `, want: &V2{ - List: map[ServerType]*Server{ + List: map[ServerKey]*Server{ {ID: "a", T: server.TypeInstituteAccess}: { Profiles: server.Profiles{ Map: map[string]server.Profile{ diff --git a/internal/discovery/discovery.go b/internal/discovery/discovery.go index 88a37d9..6db9425 100644 --- a/internal/discovery/discovery.go +++ b/internal/discovery/discovery.go @@ -27,6 +27,7 @@ type Discovery struct { ServerList discotypes.Servers `json:"servers"` } +// DiscoURL is the URL used for fetching the discovery files and signatures var DiscoURL = "https://disco.eduvpn.org/v2/" // file is a helper function that gets a disco JSON and fills the structure with it @@ -123,11 +124,12 @@ func (discovery *Discovery) ServerByURL( return nil, fmt.Errorf("no server of type '%s' at URL '%s'", srvType, baseURL) } -type CountryNotFoundError struct { +// ErrCountryNotFound is used when the secure internet country cannot be found +type ErrCountryNotFound struct { CountryCode string } -func (cnf *CountryNotFoundError) Error() string { +func (cnf *ErrCountryNotFound) Error() string { return fmt.Sprintf("no secure internet server with country code: '%s'", cnf.CountryCode) } @@ -139,7 +141,7 @@ func (discovery *Discovery) ServerByCountryCode(countryCode string) (*discotypes return &srv, nil } } - return nil, &CountryNotFoundError{CountryCode: countryCode} + return nil, &ErrCountryNotFound{CountryCode: countryCode} } // orgByID returns the discovery organization by the organization ID diff --git a/internal/failover/failover.go b/internal/failover/failover.go index 97b1da1..bbd1978 100644 --- a/internal/failover/failover.go +++ b/internal/failover/failover.go @@ -1,3 +1,5 @@ +// Package failover implements the failover procedure +// by sending pings and checking if the VPN is up package failover import ( diff --git a/internal/failover/monitor.go b/internal/failover/monitor.go index d9028f2..5d81f22 100644 --- a/internal/failover/monitor.go +++ b/internal/failover/monitor.go @@ -20,6 +20,10 @@ type DroppedConMon struct { readRxBytes func() (int64, error) } +// NewDroppedMonitor creates a new failover monitor +// `pingInterval` is the interval in which to send pings +// `pDropped` is how many pings we need to send before we deem it is dropped +// `readRxBytes` is a function that gets the rx bytes from the client func NewDroppedMonitor(pingInterval time.Duration, pDropped int, readRxBytes func() (int64, error)) *DroppedConMon { return &DroppedConMon{pInterval: pingInterval, pDropped: pDropped, readRxBytes: readRxBytes} } diff --git a/internal/failover/ping.go b/internal/failover/ping.go index 3b5faa8..59dbcc9 100644 --- a/internal/failover/ping.go +++ b/internal/failover/ping.go @@ -13,12 +13,14 @@ import ( // mtuOverhead defines the total MTU overhead for an ICMP ECHO message: 20 bytes IP header + 8 bytes ICMP header var mtuOverhead = 28 +// Pinger sends pings type Pinger struct { listener net.PacketConn buffer []byte gateway net.Addr } +// Read reads from the ping listener with deadline `deadline` func (p Pinger) Read(deadline time.Time) error { // First set the deadline to read err := p.listener.SetReadDeadline(deadline) @@ -43,6 +45,7 @@ func (p Pinger) Read(deadline time.Time) error { } } +// Send sends a single ping func (p Pinger) Send(seq int) error { errorMessage := fmt.Sprintf("failed sending ping, seq %d", seq) // Make a new ICMP message diff --git a/internal/failover/ping_default.go b/internal/failover/ping_default.go index c490869..11401bb 100644 --- a/internal/failover/ping_default.go +++ b/internal/failover/ping_default.go @@ -9,6 +9,7 @@ import ( "golang.org/x/net/icmp" ) +// NewPinger creates a new pinger with gateway `gateway` and size `size` func NewPinger(gateway string, size int) (*Pinger, error) { l, err := icmp.ListenPacket("udp4", "0.0.0.0") if err != nil { diff --git a/internal/fsm/fsm.go b/internal/fsm/fsm.go index f5bebd7..0fe3444 100644 --- a/internal/fsm/fsm.go +++ b/internal/fsm/fsm.go @@ -16,14 +16,17 @@ type ( StateIDSlice []StateID ) +// Len is defined here such that we can sort the slice func (v StateIDSlice) Len() int { return len(v) } +// Less is defined here such that we can sort the slice func (v StateIDSlice) Less(i, j int) bool { return v[i] < v[j] } +// Swap is defined here such that we can sort the slice func (v StateIDSlice) Swap(i, j int) { v[i], v[j] = v[j], v[i] } @@ -36,9 +39,8 @@ type Transition struct { Description string } -type ( - States map[StateID]State -) +// States is the map from state identifier to the state itself +type States map[StateID]State // State represents a single node in the graph. type State struct { @@ -97,6 +99,8 @@ func (fsm *FSM) InState(check StateID) bool { return check == fsm.Current } +// CheckTransition returns an error whether or not a transition to +// state `desired` is possible func (fsm *FSM) CheckTransition(desired StateID) error { // initial or begin state is fine // 0 = deregistered diff --git a/internal/http/http.go b/internal/http/http.go index ba081fd..09f1953 100644 --- a/internal/http/http.go +++ b/internal/http/http.go @@ -146,7 +146,7 @@ type Client struct { Timeout time.Duration } -// Returns a HTTP client with some default settings +// NewClient returns a HTTP client with some default settings func NewClient(client *http.Client) *Client { c := client if c == nil { @@ -169,7 +169,7 @@ func (c *Client) PostWithOpts(ctx context.Context, url string, opts *OptionalPar return c.Do(ctx, http.MethodPost, url, opts) } -// MethodWithOpts Do send a HTTP request using a method (e.g. GET, POST), an url and optional parameters +// Do sends a HTTP request using a method (e.g. GET, POST), an url and optional parameters // It returns the HTTP headers, the body and an error if there is one. func (c *Client) Do(ctx context.Context, method string, urlStr string, opts *OptionalParams) (http.Header, []byte, error) { // Make sure the url contains all the parameters diff --git a/internal/log/log.go b/internal/log/log.go index 8d3fbfb..02373c9 100644 --- a/internal/log/log.go +++ b/internal/log/log.go @@ -24,6 +24,7 @@ type FileLogger struct { // Logger is the global logger instance var Logger *FileLogger +// Level is the level of log, e.g. debug type Level int8 const ( @@ -88,7 +89,7 @@ func (logger *FileLogger) Init(lvl Level, dir string) error { return nil } -// Inheritf logs an error with a message and params using the error level verbosity of the error. +// Inherit logs an error with a message and params using the error level verbosity of the error. // The message is always prefixed with the error. func (logger *FileLogger) Inherit(err error, msg string) { if err == nil { diff --git a/internal/server/custom.go b/internal/server/custom.go index b4b81cb..10e9a28 100644 --- a/internal/server/custom.go +++ b/internal/server/custom.go @@ -10,6 +10,10 @@ import ( "github.com/jwijenbergh/eduoauth-go" ) +// AddCustom adds a custom server to the internal server list +// `ctx` is the context used for cancellation +// `id` is the identifier of the server, the base URL +// `na` specifies whether or not we want to add the server without doing authorization now func (s *Servers) AddCustom(ctx context.Context, id string, na bool) (*Server, error) { sd := api.ServerData{ ID: id, @@ -39,6 +43,11 @@ func (s *Servers) AddCustom(ctx context.Context, id string, na bool) (*Server, e return &cust, nil } +// GetCustom gets a custom server +// `ctx` is the context for cancellation +// `id` is the identifier of the server +// `tok` are the tokens such that we can initialize the API +// `disableAuth` is set to True when authorization should not be triggered func (s *Servers) GetCustom(ctx context.Context, id string, tok *eduoauth.Token, disableAuth bool) (*Server, error) { sd := api.ServerData{ ID: id, diff --git a/internal/server/institute.go b/internal/server/institute.go index 881f96d..aa032c7 100644 --- a/internal/server/institute.go +++ b/internal/server/institute.go @@ -11,6 +11,11 @@ import ( "github.com/jwijenbergh/eduoauth-go" ) +// AddInstitute adds an institute access server +// `ctx` is the context used for cancellation +// `disco` are the discovery servers +// `id` is the identifier for the server, the base url +// `na` is true when authorization should not be triggered func (s *Servers) AddInstitute(ctx context.Context, disco *discovery.Discovery, id string, na bool) (*Server, error) { // This is basically done to double check if the server is part of the institute access section of disco dsrv, err := disco.ServerByURL(id, "institute_access") @@ -43,6 +48,12 @@ func (s *Servers) AddInstitute(ctx context.Context, disco *discovery.Discovery, return &inst, nil } +// GetInstitute gets an institute access server +// `ctx` is the context used for cancellation +// `id` is the identifier for the server, the base url +// `disco` are the discovery servers +// `tok` are the tokens such that we do not have to trigger auth +// `disableAuth` is true when auth should never be triggered func (s *Servers) GetInstitute(ctx context.Context, id string, disco *discovery.Discovery, tok *eduoauth.Token, disableAuth bool) (*Server, error) { // This is basically done to double check if the server is part of the institute access section of disco dsrv, err := disco.ServerByURL(id, "institute_access") diff --git a/internal/server/secureinternet.go b/internal/server/secureinternet.go index 19e75a1..0d06a55 100644 --- a/internal/server/secureinternet.go +++ b/internal/server/secureinternet.go @@ -13,6 +13,11 @@ import ( "github.com/jwijenbergh/eduoauth-go" ) +// AddSecure adds a secure internet server +// `ctx` is the context used for cancellation +// `disco` are the discovery servers +// `orgID` is the organiztaion ID +// `na` specifies whether or not authorization should be triggered when adding func (s *Servers) AddSecure(ctx context.Context, disco *discovery.Discovery, orgID string, na bool) (*Server, error) { if s.config.HasSecureInternet() { return nil, errors.New("a secure internet server already exists") @@ -54,6 +59,12 @@ func (s *Servers) AddSecure(ctx context.Context, disco *discovery.Discovery, org return &sec, nil } +// GetSecure gets a secure internet server +// `ctx` is the context used for cancellation +// `orgID` is the organization ID that identifies the server +// `disco` are the discovery servers +// `tok` are the tokens such that the server can be found without triggering auth +// `disableAuth` is set to true when authorization should not be triggered func (s *Servers) GetSecure(ctx context.Context, orgID string, disco *discovery.Discovery, tok *eduoauth.Token, disableAuth bool) (*Server, error) { srv, err := s.config.GetServer(orgID, server.TypeSecureInternet) if err != nil { diff --git a/internal/server/server.go b/internal/server/server.go index bffeb2c..4b960a5 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -1,3 +1,4 @@ +// Package server implements functions that have to deal with server interaction package server import ( @@ -13,6 +14,7 @@ import ( srvtypes "github.com/eduvpn/eduvpn-common/types/server" ) +// Server is the struct for a single server type Server struct { identifier string t srvtypes.Type @@ -20,8 +22,10 @@ type Server struct { storage *v2.V2 } +// ErrInvalidProfile is an error that is returned when an invalid profile has been chosen var ErrInvalidProfile = errors.New("invalid profile") +// NewServer creates a new server func (s *Servers) NewServer(identifier string, t srvtypes.Type, api *api.API) Server { return Server{ identifier: identifier, @@ -155,6 +159,7 @@ func (s *Server) connect(ctx context.Context, wgSupport bool, pTCP bool) (*srvty }, nil } +// Disconnect sends an API /disconnect to the server func (s *Server) Disconnect(ctx context.Context) error { a, err := s.api() if err != nil { @@ -170,6 +175,7 @@ func (s *Server) cfgServer() (*v2.Server, error) { return s.storage.GetServer(s.identifier, s.t) } +// SetProfileID sets the profile id `id` for the server func (s *Server) SetProfileID(id string) error { cs, err := s.cfgServer() if err != nil { @@ -179,6 +185,7 @@ func (s *Server) SetProfileID(id string) error { return nil } +// SetProfileList sets the profile list `prfs` for the server func (s *Server) SetProfileList(prfs srvtypes.Profiles) error { cs, err := s.cfgServer() if err != nil { @@ -188,6 +195,7 @@ func (s *Server) SetProfileList(prfs srvtypes.Profiles) error { return nil } +// SetExpireTime sets the time `et` when the VPN expires func (s *Server) SetExpireTime(et time.Time) error { cs, err := s.cfgServer() if err != nil { @@ -197,6 +205,7 @@ func (s *Server) SetExpireTime(et time.Time) error { return nil } +// ProfileID gets the profile ID for the server func (s *Server) ProfileID() (string, error) { cs, err := s.cfgServer() if err != nil { @@ -205,6 +214,7 @@ func (s *Server) ProfileID() (string, error) { return cs.Profiles.Current, nil } +// SetLocation sets the secure internet location for the server func (s *Server) SetLocation(loc string) error { if s.t != srvtypes.TypeSecureInternet { return errors.New("changing secure internet location is only possible when the server is a secure location") @@ -217,11 +227,12 @@ func (s *Server) SetLocation(loc string) error { return nil } +// SetCurrent sets the current server in the state file to this one func (s *Server) SetCurrent() error { if s.storage == nil { return errors.New("no storage available") } - s.storage.LastChosen = &v2.ServerType{ + s.storage.LastChosen = &v2.ServerKey{ ID: s.identifier, T: s.t, } diff --git a/internal/server/servers.go b/internal/server/servers.go index fe2550c..64e64b6 100644 --- a/internal/server/servers.go +++ b/internal/server/servers.go @@ -12,23 +12,31 @@ import ( "github.com/jwijenbergh/eduoauth-go" ) +// Callbacks defines the interface for doing certain callback operations type Callbacks interface { + // api.Callbacks is the API callback interface api.Callbacks + // GettingConfig is called when the config is obtained GettingConfig() error + // InvalidProfile is called when an invalid profile is found InvalidProfile(context.Context, *Server) (string, error) } +// Servers is the main struct that contains information for configuring the servers type Servers struct { - clientID string - cb Callbacks + clientID string + cb Callbacks + // WGSupport defines whether or not wireguard support is enabled WGSupport bool config *v2.V2 } +// Remove removes a server with id `identifier` and type `t` func (s *Servers) Remove(identifier string, t srvtypes.Type) error { return s.config.RemoveServer(identifier, t) } +// NewServers creates a new servers struct func NewServers(name string, cb Callbacks, wgSupport bool, cfg *v2.V2) Servers { return Servers{ clientID: name, @@ -38,25 +46,31 @@ func NewServers(name string, cb Callbacks, wgSupport bool, cfg *v2.V2) Servers { } } +// CurrentServer contains the information for the current active server type CurrentServer struct { + // it embeds the state file server *v2.Server - T v2.ServerType + // Key is the server key + Key v2.ServerKey + // srvs refers to the original servers manager srvs *Servers } +// ServerWithCallbacks gets the current server as a server struct and triggers callbacks as needed func (cs *CurrentServer) ServerWithCallbacks(ctx context.Context, disco *discovery.Discovery, tokens *eduoauth.Token, disableAuth bool) (*Server, error) { - switch cs.T.T { + switch cs.Key.T { case srvtypes.TypeInstituteAccess: - return cs.srvs.GetInstitute(ctx, cs.T.ID, disco, tokens, disableAuth) + return cs.srvs.GetInstitute(ctx, cs.Key.ID, disco, tokens, disableAuth) case srvtypes.TypeSecureInternet: - return cs.srvs.GetSecure(ctx, cs.T.ID, disco, tokens, disableAuth) + return cs.srvs.GetSecure(ctx, cs.Key.ID, disco, tokens, disableAuth) case srvtypes.TypeCustom: - return cs.srvs.GetCustom(ctx, cs.T.ID, tokens, disableAuth) + return cs.srvs.GetCustom(ctx, cs.Key.ID, tokens, disableAuth) default: - return nil, fmt.Errorf("no such server type: %d", cs.T.T) + return nil, fmt.Errorf("no such server type: %d", cs.Key.T) } } +// GetServer gets a server from the state file func (s *Servers) GetServer(id string, t srvtypes.Type) (*v2.Server, error) { if s.config == nil { return nil, errors.New("no configuration available") @@ -64,6 +78,7 @@ func (s *Servers) GetServer(id string, t srvtypes.Type) (*v2.Server, error) { return s.config.GetServer(id, t) } +// CurrentServer gets the current server from the state file and wraps it into a neat type func (s *Servers) CurrentServer() (*CurrentServer, error) { curr, k, err := s.config.CurrentServer() if err != nil { @@ -71,15 +86,18 @@ func (s *Servers) CurrentServer() (*CurrentServer, error) { } return &CurrentServer{ Server: curr, - T: *k, + Key: *k, srvs: s, }, nil } +// PublicCurrent gets the current server into a type that we can return to the client func (s *Servers) PublicCurrent(disco *discovery.Discovery) (*srvtypes.Current, error) { return s.config.PublicCurrent(disco) } +// ConnectWithCallbacks handles the /connect flow +// It calls callbacks as needed func (s *Servers) ConnectWithCallbacks(ctx context.Context, srv *Server, pTCP bool) (*srvtypes.Configuration, error) { err := srv.SetCurrent() if err != nil { diff --git a/internal/server/time.go b/internal/server/time.go index 4e54ef6..7369f47 100644 --- a/internal/server/time.go +++ b/internal/server/time.go @@ -28,6 +28,7 @@ func RenewButtonTime(st time.Time, et time.Time) int64 { return t.Unix() } +// CountdownTime returns the time when the countdown timer should be shown func CountdownTime(st time.Time, et time.Time) int64 { d := et.Sub(st) @@ -44,6 +45,8 @@ func CountdownTime(st time.Time, et time.Time) int64 { return t.Unix() } +// NotificationTimes returns the list when the app should +// show a notification when the VPN is (about to) expire(d) func NotificationTimes(st time.Time, et time.Time) []int64 { last := []time.Duration{ time.Duration(0), diff --git a/internal/test/error.go b/internal/test/error.go index 98fa09b..b2ddd12 100644 --- a/internal/test/error.go +++ b/internal/test/error.go @@ -2,6 +2,7 @@ package test import "testing" +// AssertError asserts an error by checking if the `Error()` strings are equal func AssertError(t *testing.T, err error, wantErr string) { gv := "" if err != nil { diff --git a/internal/test/handler.go b/internal/test/handler.go index 3991c6a..bb5f7a0 100644 --- a/internal/test/handler.go +++ b/internal/test/handler.go @@ -11,12 +11,14 @@ type HandlerSet struct { handler http.Handler } +// SetHandler sets the handler to `handler` func (hs *HandlerSet) SetHandler(handler http.Handler) { hs.mu.Lock() hs.handler = handler hs.mu.Unlock() } +// ServeHTTP serves HTTP using the handler func (hs *HandlerSet) ServeHTTP(w http.ResponseWriter, r *http.Request) { hs.mu.Lock() handler := hs.handler diff --git a/internal/test/server.go b/internal/test/server.go index 6d6a0c2..6c1b418 100644 --- a/internal/test/server.go +++ b/internal/test/server.go @@ -10,13 +10,14 @@ import ( httpw "github.com/eduvpn/eduvpn-common/internal/http" ) +// Server wraps a HTTP test server type Server struct { *httptest.Server } +// NewServer creates a new test server func NewServer(handler http.Handler) *Server { s := httptest.NewTLSServer(handler) - return &Server{s} } diff --git a/internal/version/version.go b/internal/version/version.go index 1f029d5..c71af9e 100644 --- a/internal/version/version.go +++ b/internal/version/version.go @@ -1,3 +1,9 @@ +// Package version defines a version string that is used for: +// - building +// - the user agent +// - tagging package version +// Version is the latest version +// Update this when releasing const Version = "2.0.0" diff --git a/internal/wireguard/ini/ini.go b/internal/wireguard/ini/ini.go index 6791336..597fd83 100644 --- a/internal/wireguard/ini/ini.go +++ b/internal/wireguard/ini/ini.go @@ -1,4 +1,4 @@ -// package ini implements an opinionated ini parser that only implements what we exactly need for WireGuard configs +// Package ini implements an opinionated ini parser that only implements what we exactly need for WireGuard configs // - key/values MUST live under a section // - empty section names are NOT allowed // - comments are indicated with a # diff --git a/internal/wireguard/wireguard.go b/internal/wireguard/wireguard.go index af290ea..e94a485 100644 --- a/internal/wireguard/wireguard.go +++ b/internal/wireguard/wireguard.go @@ -36,12 +36,17 @@ func availableUDPPort() (int, error) { return ludp.LocalAddr().(*net.UDPAddr).Port, nil } +// Proxy is the proxyguard information type Proxy struct { + // SourcePort is the source port of the TCP socket SourcePort int - Listen string - Peer string + // Listen is the IP:PORT of the udp listener + Listen string + // Peer is the hostname/ip:port of the WireGuard peer + Peer string } +// Config gets a wireguard config with API config `cfg`, wg key `key` and prefer tcp `tcp` func Config(cfg string, key *wgtypes.Key, tcp bool) (string, *Proxy, error) { // the key is nil if the client does not accept WireGuard if key == nil { diff --git a/types/cookie/cookie.go b/types/cookie/cookie.go index 1714682..48c8dbf 100644 --- a/types/cookie/cookie.go +++ b/types/cookie/cookie.go @@ -1,4 +1,4 @@ -// package cookie implements a specialized version of a context +// Package cookie implements a specialized version of a context // - It is cancellable // - It has a channel associated with it to reply to state callbacks // - It can be marshalled by having a cgo Handle attached to it @@ -12,6 +12,8 @@ import ( "runtime/cgo" ) +// Cookie is the cookie which is just a context with some other data associated with it +// We could potentially only uses contexts with values, but this is nicer for type checking type Cookie struct { c chan string ctx context.Context @@ -19,8 +21,10 @@ type Cookie struct { H cgo.Handle } +// contextt is the context type for the value type contextt int8 +// CONTEXTK is the key of the cookie const CONTEXTK contextt = 0 // NewWithContext creates a new cookie with a context diff --git a/types/discovery/discovery.go b/types/discovery/discovery.go index bc22eab..cadd192 100644 --- a/types/discovery/discovery.go +++ b/types/discovery/discovery.go @@ -1,4 +1,4 @@ -// package discovery defines the public types that have to deal with discovery +// Package discovery defines the public types that have to deal with discovery package discovery import ( @@ -70,7 +70,7 @@ type Server struct { // This library always marshals the data as a map and then makes sure unmarshalling also gives a map type MapOrString map[string]string -// The display name can either be a map or a string in the server list +// UnmarshalJSON unmarshals the display name. It can either be a map or a string in the server list // Unmarshal it by first trying a string and then the map. func (displayName *MapOrString) UnmarshalJSON(data []byte) error { var displayNameString string diff --git a/types/error/error.go b/types/error/error.go index 9697e9d..7723c97 100644 --- a/types/error/error.go +++ b/types/error/error.go @@ -1,3 +1,4 @@ +// Package err defines public error types that are returned to clients package err // Translated defines the type for translated strings diff --git a/types/protocol/protocol.go b/types/protocol/protocol.go index 3967141..85b0733 100644 --- a/types/protocol/protocol.go +++ b/types/protocol/protocol.go @@ -1,4 +1,4 @@ -// package protocol contains hte public type that have to do with VPN protocols +// Package protocol contains hte public type that have to do with VPN protocols package protocol // Protocol defines an 'enumeration' of protocols diff --git a/types/server/server.go b/types/server/server.go index 5e1fa9b..1beb8fb 100644 --- a/types/server/server.go +++ b/types/server/server.go @@ -1,4 +1,4 @@ -// package server defines public types that have to deal with the VPN server +// Package server defines public types that have to deal with the VPN server package server import ( @@ -23,7 +23,7 @@ const ( TypeCustom ) -// This is here to support V1 configs which had the server type as a string +// UnmarshalJSON is set here here to support V1 configs which had the server type as a string func (t *Type) UnmarshalJSON(data []byte) error { // First try to just unmarshal the type var num int8 |
