diff --git a/README.md b/README.md index 4a92a12..0301a14 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ This is a client for [vpngate.net](https://www.vpngate.net/). This client fetches the list of available relay servers provided by vpngate.net, and allows you to filter and connect to a server of your liking. -You can check out your current IP address and region at https://ipinfo.io, or run the following: +You can check out your current IP address and region at , or run the following: ```shell curl ipinfo.io @@ -79,6 +79,28 @@ If the country doesn't matter, a random server can be selected: sudo vpngate connect --random ``` +#### Proxy + +In some cases, anonymity is necessary to populate the list of available VPN servers. + +A proxy is a way to bypass restrictions and in some cases, internet censorship. + +##### HTTP/HTTPS + +Use the specified HTTP/HTTPS proxy to fetch the server list. + +```shell +sudo vpngate connect --proxy "http://localhost:8080" +``` + +##### SOCKS5 + +Use the specified SOCKS5 proxy to fetch the server list. + +```shell +sudo vpngate connect --socks5 "127.0.0.1:1080" +``` + ## Notes - I do not maintain any of the servers on vpngate.net (connect to these servers at your own discretion) diff --git a/cmd/connect.go b/cmd/connect.go index ae57b94..9c1c9bc 100644 --- a/cmd/connect.go +++ b/cmd/connect.go @@ -16,24 +16,28 @@ import ( ) var ( - flagRandom bool - flagReconnect bool + flagRandom bool + flagReconnect bool + flagProxy string + flagSocks5Proxy string ) func init() { connectCmd.Flags().BoolVarP(&flagRandom, "random", "r", false, "connect to a random server") connectCmd.Flags().BoolVarP(&flagReconnect, "reconnect", "t", false, "continually attempt to connect to the server") + connectCmd.Flags().StringVarP(&flagProxy, "proxy", "p", "", "provide a http/https proxy server to make requests through (i.e. 127.0.0.1:8080") + connectCmd.Flags().StringVarP(&flagSocks5Proxy, "socks5", "s", "", "provide a socks5 proxy server to make requests through (i.e. 127.0.0.1:1080") rootCmd.AddCommand(connectCmd) } var connectCmd = &cobra.Command{ - Use: "connect", - + Use: "connect", + Short: "Connect to a vpn server (survey selection appears if hostname is not provided)", Long: `Connect to a vpn from a list of relay servers`, Args: cobra.RangeArgs(0, 1), Run: func(cmd *cobra.Command, args []string) { - vpnServers, err := vpn.GetList() + vpnServers, err := vpn.GetList(flagProxy, flagSocks5Proxy) if err != nil { log.Fatal().Msgf(err.Error()) os.Exit(1) diff --git a/cmd/list.go b/cmd/list.go index db2339e..50ae746 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -22,7 +22,7 @@ var listCmd = &cobra.Command{ Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { - vpnServers, err := vpn.GetList() + vpnServers, err := vpn.GetList(flagProxy, flagSocks5Proxy) if err != nil { log.Fatal().Msgf(err.Error()) os.Exit(1) diff --git a/go.mod b/go.mod index f5287d5..b4d1ae0 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/spf13/afero v1.11.0 github.com/spf13/cobra v1.8.1 github.com/stretchr/testify v1.9.0 + golang.org/x/net v0.19.0 ) require ( @@ -29,7 +30,7 @@ require ( github.com/rogpeppe/go-internal v1.9.0 // indirect github.com/spf13/pflag v1.0.5 // indirect golang.org/x/sys v0.15.0 // indirect - golang.org/x/term v0.13.0 // indirect + golang.org/x/term v0.15.0 // indirect golang.org/x/text v0.14.0 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index c0cc134..e5b6dae 100644 --- a/go.sum +++ b/go.sum @@ -18,8 +18,6 @@ github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jszwec/csvutil v1.9.0 h1:iTmq9G1P0e+AUq/MkFg6tetJ+1BH3fOX8Xi0RAcwiGc= -github.com/jszwec/csvutil v1.9.0/go.mod h1:/E4ONrmGkwmWsk9ae9jpXnv9QT8pLHEPcCirMFhxG9I= github.com/jszwec/csvutil v1.10.0 h1:upMDUxhQKqZ5ZDCs/wy+8Kib8rZR8I8lOR34yJkdqhI= github.com/jszwec/csvutil v1.10.0/go.mod h1:/E4ONrmGkwmWsk9ae9jpXnv9QT8pLHEPcCirMFhxG9I= github.com/juju/errors v1.0.0 h1:yiq7kjCLll1BiaRuNY53MGI0+EQ3rF6GB+wvboZDefM= @@ -60,10 +58,6 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A= -github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= -github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0= -github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -77,8 +71,6 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= @@ -88,6 +80,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -104,8 +98,8 @@ golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= +golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= diff --git a/pkg/vpn/list.go b/pkg/vpn/list.go index 0fe3bda..1adc404 100644 --- a/pkg/vpn/list.go +++ b/pkg/vpn/list.go @@ -4,9 +4,12 @@ import ( "bytes" "io" "net/http" + "net/url" + "os" "github.com/jszwec/csvutil" "github.com/rs/zerolog/log" + "golang.org/x/net/proxy" "github.com/davegallant/vpngate/pkg/util" "github.com/juju/errors" @@ -55,10 +58,11 @@ func parseVpnList(r io.Reader) (*[]Server, error) { } // GetList returns a list of vpn servers -func GetList() (*[]Server, error) { +func GetList(httpProxy string, socks5Proxy string) (*[]Server, error) { cacheExpired := vpnListCacheIsExpired() var servers *[]Server + var client *http.Client if !cacheExpired { servers, err := getVpnListCache() @@ -75,11 +79,43 @@ func GetList() (*[]Server, error) { log.Info().Msg("Fetching the latest server list") + if httpProxy != "" { + proxyURL, err := url.Parse(httpProxy) + if err != nil { + log.Error().Msgf("Error parsing proxy: %s", err) + os.Exit(1) + } + transport := &http.Transport{ + Proxy: http.ProxyURL(proxyURL), + } + + client = &http.Client{ + Transport: transport, + } + + } else if socks5Proxy != "" { + dialer, err := proxy.SOCKS5("tcp", socks5Proxy, nil, proxy.Direct) + if err != nil { + log.Error().Msgf("Error creating SOCKS5 dialer: %v", err) + os.Exit(1) + } + + httpTransport := &http.Transport{ + Dial: dialer.Dial, + } + + client = &http.Client{ + Transport: httpTransport, + } + } else { + client = &http.Client{} + } + var r *http.Response err := util.Retry(5, 1, func() error { var err error - r, err = http.Get(vpnList) + r, err = client.Get(vpnList) if err != nil { return err } diff --git a/pkg/vpn/list_test.go b/pkg/vpn/list_test.go index 661431b..e0ef0e5 100644 --- a/pkg/vpn/list_test.go +++ b/pkg/vpn/list_test.go @@ -9,7 +9,7 @@ import ( // TestGetListReal tests getting the real list of vpn servers func TestGetListReal(t *testing.T) { - _, err := GetList() + _, err := GetList("", "") assert.NoError(t, err) }