Skip to content

Commit

Permalink
added proxy support to the library, using ProxyURL parameter. (#256)
Browse files Browse the repository at this point in the history
  • Loading branch information
elgatito authored and anacrolix committed Jun 8, 2018
1 parent 37fc800 commit f6ed514
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 16 deletions.
8 changes: 4 additions & 4 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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)
Expand Down
21 changes: 21 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"crypto/tls"
"net"
"net/http"
"net/url"
"time"

"golang.org/x/time/rate"
Expand Down Expand Up @@ -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:[email protected]:1080"
ProxyURL string

IPBlocklist iplist.Ranger
DisableIPv6 bool `long:"disable-ipv6"`
DisableIPv4 bool
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down
64 changes: 52 additions & 12 deletions socket.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ import (
"context"
"fmt"
"net"
"net/url"
"strconv"
"strings"

"golang.org/x/net/proxy"

"github.com/anacrolix/missinggo"
)

Expand All @@ -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))
}
Expand All @@ -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)
}

Expand All @@ -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
}
Expand All @@ -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
}
Expand All @@ -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)
}
Expand All @@ -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,
Expand All @@ -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)
}

0 comments on commit f6ed514

Please sign in to comment.