]> Sergey Matveev's repositories - btrtrc.git/commitdiff
added proxy support to the library, using ProxyURL parameter. (#256)
authorDenis <denis.kuzmenok@gmail.com>
Fri, 8 Jun 2018 10:52:36 +0000 (13:52 +0300)
committerMatt Joiner <anacrolix@gmail.com>
Fri, 8 Jun 2018 10:52:36 +0000 (20:52 +1000)
client.go
config.go
socket.go

index a0800d58d4a5da0a26890aba71a1682f6911bd71..f5a82300de051abc625a38b3864b77efb6f99147 100644 (file)
--- 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)
index 615e7a2465a39e6acf46dbf8c750d1d2564e01ea..f45a1e028912bab75b65dec6d4c755e195e048bc 100644 (file)
--- 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.
index fb70b280f5ff7222c728c463ef7ba11de340147a..e7d54fb0e5cc5532939031f2df286f3d882e63b3 100644 (file)
--- 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)
 }