mirror of
https://github.com/davegallant/vpngate.git
synced 2025-08-05 08:13:41 +00:00
106 lines
2.1 KiB
Go
106 lines
2.1 KiB
Go
package vpn
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"bytes"
|
|
"io"
|
|
|
|
"github.com/jszwec/csvutil"
|
|
"github.com/rs/zerolog/log"
|
|
|
|
"github.com/juju/errors"
|
|
)
|
|
|
|
const (
|
|
vpnList = "https://www.vpngate.net/api/iphone/"
|
|
)
|
|
|
|
// Server holds in formation about a vpn relay server
|
|
type Server struct {
|
|
HostName string `csv:"#HostName"`
|
|
CountryLong string `csv:"CountryLong"`
|
|
CountryShort string `csv:"CountryShort"`
|
|
Score int `csv:"Score"`
|
|
IPAddr string `csv:"IP"`
|
|
OpenVpnConfigData string `csv:"OpenVPN_ConfigData_Base64"`
|
|
Ping string `csv:"Ping"`
|
|
}
|
|
|
|
func streamToBytes(stream io.Reader) []byte {
|
|
buf := new(bytes.Buffer)
|
|
_, err := buf.ReadFrom(stream)
|
|
if err != nil {
|
|
log.Error().Msg("Unable to stream bytes")
|
|
}
|
|
return buf.Bytes()
|
|
}
|
|
|
|
// parse csv
|
|
func parseVpnList(r io.Reader) (*[]Server, error) {
|
|
|
|
var servers []Server
|
|
|
|
serverList := streamToBytes(r)
|
|
|
|
// Trim known invalid rows
|
|
serverList = bytes.TrimPrefix(serverList, []byte("*vpn_servers\r\n"))
|
|
serverList = bytes.TrimSuffix(serverList, []byte("*\r\n"))
|
|
|
|
if err := csvutil.Unmarshal(serverList, &servers); err != nil {
|
|
return nil, errors.Annotatef(err, "Unable to parse CSV")
|
|
}
|
|
|
|
return &servers, nil
|
|
|
|
}
|
|
|
|
// GetList returns a list of vpn servers
|
|
func GetList() (*[]Server, error) {
|
|
|
|
cacheExpired := vpnListCacheIsExpired()
|
|
|
|
var servers *[]Server
|
|
|
|
if !cacheExpired {
|
|
servers, err := getVpnListCache()
|
|
|
|
if err != nil {
|
|
log.Info().Msg("Unable to retrieve vpn list from cache")
|
|
} else {
|
|
return servers, nil
|
|
}
|
|
|
|
} else {
|
|
log.Info().Msg("The vpn server list cache has expired")
|
|
}
|
|
|
|
log.Info().Msg("Fetching the latest server list")
|
|
|
|
r, err := http.Get(vpnList)
|
|
|
|
if err != nil {
|
|
return nil, errors.Annotate(err, "Unable to retrieve vpn list")
|
|
}
|
|
|
|
defer r.Body.Close()
|
|
|
|
if r.StatusCode != 200 {
|
|
return nil, errors.Annotatef(err, "Unexpected status code when retrieving vpn list: %d", r.StatusCode)
|
|
}
|
|
|
|
servers, err = parseVpnList(r.Body)
|
|
|
|
if err != nil {
|
|
return nil, errors.Annotate(err, "unable to parse vpn list")
|
|
}
|
|
|
|
err = writeVpnListToCache(*servers)
|
|
|
|
if err != nil {
|
|
log.Warn().Msgf("Unable to write servers to cache: %s", err)
|
|
}
|
|
|
|
return servers, nil
|
|
}
|