From f6ed514c3b73c2da451c5a55b0ffa4b2d0924341 Mon Sep 17 00:00:00 2001 From: Denis Date: Fri, 8 Jun 2018 13:52:36 +0300 Subject: [PATCH] added proxy support to the library, using ProxyURL parameter. (#256) --- client.go | 8 +++---- config.go | 21 ++++++++++++++++++ socket.go | 64 ++++++++++++++++++++++++++++++++++++++++++++----------- 3 files changed, 77 insertions(+), 16 deletions(-) diff --git a/client.go b/client.go index a0800d58..f5a82300 100644 --- a/client.go +++ b/client.go @@ -224,7 +224,7 @@ func NewClient(cfg *Config) (cl *Client, err error) { } } - cl.conns, err = listenAll(cl.enabledPeerNetworks(), cl.config.ListenHost, cl.config.ListenPort) + cl.conns, err = listenAll(cl.enabledPeerNetworks(), cl.config.ListenHost, cl.config.ListenPort, cl.config.ProxyURL) if err != nil { return } @@ -457,10 +457,10 @@ func (cl *Client) dopplegangerAddr(addr string) bool { func (cl *Client) dialTCP(ctx context.Context, addr string) (c net.Conn, err error) { d := net.Dialer{ - // Can't bind to the listen address, even though we intend to create an - // endpoint pair that is distinct. Oh well. + // Can't bind to the listen address, even though we intend to create an + // endpoint pair that is distinct. Oh well. - // LocalAddr: cl.tcpListener.Addr(), + // LocalAddr: cl.tcpListener.Addr(), } c, err = d.DialContext(ctx, "tcp"+ipNetworkSuffix(!cl.config.DisableIPv4 && !cl.config.DisableIPv4Peers, !cl.config.DisableIPv6), addr) countDialResult(err) diff --git a/config.go b/config.go index 615e7a24..f45a1e02 100644 --- a/config.go +++ b/config.go @@ -4,6 +4,7 @@ import ( "crypto/tls" "net" "net/http" + "net/url" "time" "golang.org/x/time/rate" @@ -79,6 +80,10 @@ type Config struct { EncryptionPolicy + // Sets usage of Socks5 Proxy. Authentication should be included in the url if needed. + // Example of setting: "socks5://demo:demo@192.168.99.100:1080" + ProxyURL string + IPBlocklist iplist.Ranger DisableIPv6 bool `long:"disable-ipv6"` DisableIPv4 bool @@ -126,6 +131,9 @@ func (cfg *Config) SetListenAddr(addr string) *Config { func (cfg *Config) setDefaults() { if cfg.HTTP == nil { cfg.HTTP = DefaultHTTPClient + if cfg.ProxyURL != "" { + cfg.setProxyURL() + } } if cfg.HTTPUserAgent == "" { cfg.HTTPUserAgent = DefaultHTTPUserAgent @@ -166,6 +174,19 @@ func (cfg *Config) setDefaults() { } } +func (cfg *Config) setProxyURL() { + fixedURL, err := url.Parse(cfg.ProxyURL) + if err != nil { + return + } + + cfg.HTTP.Transport = &http.Transport{ + Proxy: http.ProxyURL(fixedURL), + TLSHandshakeTimeout: 15 * time.Second, + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } +} + type EncryptionPolicy struct { DisableEncryption bool ForceEncryption bool // Don't allow unobfuscated connections. diff --git a/socket.go b/socket.go index fb70b280..e7d54fb0 100644 --- a/socket.go +++ b/socket.go @@ -4,9 +4,12 @@ import ( "context" "fmt" "net" + "net/url" "strconv" "strings" + "golang.org/x/net/proxy" + "github.com/anacrolix/missinggo" ) @@ -19,11 +22,20 @@ type socket interface { dialer } -func listen(network, addr string) (socket, error) { +func getProxyDialer(proxyURL string) (proxy.Dialer, error) { + fixedURL, err := url.Parse(proxyURL) + if err != nil { + return nil, err + } + + return proxy.FromURL(fixedURL, proxy.Direct) +} + +func listen(network, addr, proxyURL string) (socket, error) { if isTcpNetwork(network) { - return listenTcp(network, addr) + return listenTcp(network, addr, proxyURL) } else if isUtpNetwork(network) { - return listenUtp(network, addr) + return listenUtp(network, addr, proxyURL) } else { panic(fmt.Sprintf("unknown network %q", network)) } @@ -37,19 +49,33 @@ func isUtpNetwork(s string) bool { return strings.Contains(s, "utp") || strings.Contains(s, "udp") } -func listenTcp(network, address string) (s socket, err error) { +func listenTcp(network, address, proxyURL string) (s socket, err error) { l, err := net.Listen(network, address) if err != nil { return } - return tcpSocket{l}, nil + + // If we don't need the proxy - then we should return default net.Dialer, + // otherwise, let's try to parse the proxyURL and return proxy.Dialer + if len(proxyURL) != 0 { + if dialer, err := getProxyDialer(proxyURL); err == nil { + return tcpSocket{l, dialer}, nil + } + } + + return tcpSocket{l, nil}, nil } type tcpSocket struct { net.Listener + d proxy.Dialer } func (me tcpSocket) dial(ctx context.Context, addr string) (net.Conn, error) { + if me.d != nil { + return me.d.Dial(me.Addr().Network(), addr) + } + return net.Dial(me.Addr().Network(), addr) } @@ -61,7 +87,7 @@ func setPort(addr string, port int) string { return net.JoinHostPort(host, strconv.FormatInt(int64(port), 10)) } -func listenAll(networks []string, getHost func(string) string, port int) ([]socket, error) { +func listenAll(networks []string, getHost func(string) string, port int, proxyURL string) ([]socket, error) { if len(networks) == 0 { return nil, nil } @@ -70,7 +96,7 @@ func listenAll(networks []string, getHost func(string) string, port int) ([]sock nahs = append(nahs, networkAndHost{n, getHost(n)}) } for { - ss, retry, err := listenAllRetry(nahs, port) + ss, retry, err := listenAllRetry(nahs, port, proxyURL) if !retry { return ss, err } @@ -82,10 +108,10 @@ type networkAndHost struct { Host string } -func listenAllRetry(nahs []networkAndHost, port int) (ss []socket, retry bool, err error) { +func listenAllRetry(nahs []networkAndHost, port int, proxyURL string) (ss []socket, retry bool, err error) { ss = make([]socket, 1, len(nahs)) portStr := strconv.FormatInt(int64(port), 10) - ss[0], err = listen(nahs[0].Network, net.JoinHostPort(nahs[0].Host, portStr)) + ss[0], err = listen(nahs[0].Network, net.JoinHostPort(nahs[0].Host, portStr), proxyURL) if err != nil { return nil, false, fmt.Errorf("first listen: %s", err) } @@ -99,7 +125,7 @@ func listenAllRetry(nahs []networkAndHost, port int) (ss []socket, retry bool, e }() portStr = strconv.FormatInt(int64(missinggo.AddrPort(ss[0].Addr())), 10) for _, nah := range nahs[1:] { - s, err := listen(nah.Network, net.JoinHostPort(nah.Host, portStr)) + s, err := listen(nah.Network, net.JoinHostPort(nah.Host, portStr), proxyURL) if err != nil { return ss, missinggo.IsAddrInUse(err) && port == 0, @@ -110,19 +136,33 @@ func listenAllRetry(nahs []networkAndHost, port int) (ss []socket, retry bool, e return } -func listenUtp(network, addr string) (s socket, err error) { +func listenUtp(network, addr, proxyURL string) (s socket, err error) { us, err := NewUtpSocket(network, addr) if err != nil { return } - return utpSocketSocket{us, network}, nil + + // If we don't need the proxy - then we should return default net.Dialer, + // otherwise, let's try to parse the proxyURL and return proxy.Dialer + if len(proxyURL) != 0 { + if dialer, err := getProxyDialer(proxyURL); err == nil { + return utpSocketSocket{us, network, dialer}, nil + } + } + + return utpSocketSocket{us, network, nil}, nil } type utpSocketSocket struct { utpSocket network string + d proxy.Dialer } func (me utpSocketSocket) dial(ctx context.Context, addr string) (net.Conn, error) { + if me.d != nil { + return me.d.Dial(me.network, addr) + } + return me.utpSocket.DialContext(ctx, me.network, addr) } -- 2.48.1