10 "github.com/anacrolix/missinggo"
11 "github.com/anacrolix/missinggo/perf"
12 "github.com/pkg/errors"
13 "golang.org/x/net/proxy"
16 type dialer interface {
17 dial(_ context.Context, addr string) (net.Conn, error)
20 type socket interface {
25 func getProxyDialer(proxyURL string) (proxy.Dialer, error) {
26 fixedURL, err := url.Parse(proxyURL)
31 return proxy.FromURL(fixedURL, proxy.Direct)
34 func listen(n network, addr, proxyURL string, f firewallCallback) (socket, error) {
37 return listenTcp(n.String(), addr, proxyURL)
39 return listenUtp(n.String(), addr, proxyURL, f)
45 func listenTcp(network, address, proxyURL string) (s socket, err error) {
46 l, err := net.Listen(network, address)
56 // If we don't need the proxy - then we should return default net.Dialer,
57 // otherwise, let's try to parse the proxyURL and return proxy.Dialer
58 if len(proxyURL) != 0 {
60 dl := disabledListener{l}
62 // TODO: The error should be propagated, as proxy may be in use for
63 // security or privacy reasons. Also just pass proxy.Dialer in from
65 if dialer, err := getProxyDialer(proxyURL); err == nil {
66 return tcpSocket{dl, func(ctx context.Context, addr string) (conn net.Conn, err error) {
67 defer perf.ScopeTimerErr(&err)()
68 return dialer.Dial(network, addr)
72 dialer := net.Dialer{}
73 return tcpSocket{l, func(ctx context.Context, addr string) (conn net.Conn, err error) {
74 defer perf.ScopeTimerErr(&err)()
75 return dialer.DialContext(ctx, network, addr)
79 type disabledListener struct {
83 func (dl disabledListener) Accept() (net.Conn, error) {
84 return nil, fmt.Errorf("tcp listener disabled due to proxy")
87 type tcpSocket struct {
89 d func(ctx context.Context, addr string) (net.Conn, error)
92 func (me tcpSocket) dial(ctx context.Context, addr string) (net.Conn, error) {
93 return me.d(ctx, addr)
96 func listenAll(networks []network, getHost func(string) string, port int, proxyURL string, f firewallCallback) ([]socket, error) {
97 if len(networks) == 0 {
100 var nahs []networkAndHost
101 for _, n := range networks {
102 nahs = append(nahs, networkAndHost{n, getHost(n.String())})
105 ss, retry, err := listenAllRetry(nahs, port, proxyURL, f)
112 type networkAndHost struct {
117 func listenAllRetry(nahs []networkAndHost, port int, proxyURL string, f firewallCallback) (ss []socket, retry bool, err error) {
118 ss = make([]socket, 1, len(nahs))
119 portStr := strconv.FormatInt(int64(port), 10)
120 ss[0], err = listen(nahs[0].Network, net.JoinHostPort(nahs[0].Host, portStr), proxyURL, f)
122 return nil, false, errors.Wrap(err, "first listen")
125 if err != nil || retry {
126 for _, s := range ss {
132 portStr = strconv.FormatInt(int64(missinggo.AddrPort(ss[0].Addr())), 10)
133 for _, nah := range nahs[1:] {
134 s, err := listen(nah.Network, net.JoinHostPort(nah.Host, portStr), proxyURL, f)
137 missinggo.IsAddrInUse(err) && port == 0,
138 errors.Wrap(err, "subsequent listen")
145 type firewallCallback func(net.Addr) bool
147 func listenUtp(network, addr, proxyURL string, fc firewallCallback) (s socket, err error) {
148 us, err := NewUtpSocket(network, addr, fc)
153 // If we don't need the proxy - then we should return default net.Dialer,
154 // otherwise, let's try to parse the proxyURL and return proxy.Dialer
155 if len(proxyURL) != 0 {
156 ds := disabledUtpSocket{us}
157 if dialer, err := getProxyDialer(proxyURL); err == nil {
158 return utpSocketSocket{ds, network, dialer}, nil
162 return utpSocketSocket{us, network, nil}, nil
165 type disabledUtpSocket struct {
169 func (ds disabledUtpSocket) Accept() (net.Conn, error) {
170 return nil, fmt.Errorf("utp listener disabled due to proxy")
173 type utpSocketSocket struct {
179 func (me utpSocketSocket) dial(ctx context.Context, addr string) (conn net.Conn, err error) {
180 defer perf.ScopeTimerErr(&err)()
182 return me.d.Dial(me.network, addr)
185 return me.utpSocket.DialContext(ctx, me.network, addr)