]> Sergey Matveev's repositories - btrtrc.git/blob - socket.go
Remove unused code
[btrtrc.git] / socket.go
1 package torrent
2
3 import (
4         "context"
5         "fmt"
6         "net"
7         "net/url"
8         "strconv"
9         "strings"
10
11         "github.com/anacrolix/missinggo"
12         "github.com/anacrolix/missinggo/perf"
13         "github.com/pkg/errors"
14         "golang.org/x/net/proxy"
15 )
16
17 type dialer interface {
18         dial(_ context.Context, addr string) (net.Conn, error)
19 }
20
21 type socket interface {
22         net.Listener
23         dialer
24 }
25
26 func getProxyDialer(proxyURL string) (proxy.Dialer, error) {
27         fixedURL, err := url.Parse(proxyURL)
28         if err != nil {
29                 return nil, err
30         }
31
32         return proxy.FromURL(fixedURL, proxy.Direct)
33 }
34
35 func listen(network, addr, proxyURL string, f firewallCallback) (socket, error) {
36         if isTcpNetwork(network) {
37                 return listenTcp(network, addr, proxyURL)
38         } else if isUtpNetwork(network) {
39                 return listenUtp(network, addr, proxyURL, f)
40         } else {
41                 panic(fmt.Sprintf("unknown network %q", network))
42         }
43 }
44
45 func isTcpNetwork(s string) bool {
46         return strings.Contains(s, "tcp")
47 }
48
49 func isUtpNetwork(s string) bool {
50         return strings.Contains(s, "utp") || strings.Contains(s, "udp")
51 }
52
53 func listenTcp(network, address, proxyURL string) (s socket, err error) {
54         l, err := net.Listen(network, address)
55         if err != nil {
56                 return
57         }
58         defer func() {
59                 if err != nil {
60                         l.Close()
61                 }
62         }()
63
64         // If we don't need the proxy - then we should return default net.Dialer,
65         // otherwise, let's try to parse the proxyURL and return proxy.Dialer
66         if len(proxyURL) != 0 {
67                 // TODO: The error should be propagated, as proxy may be in use for
68                 // security or privacy reasons. Also just pass proxy.Dialer in from
69                 // the Config.
70                 if dialer, err := getProxyDialer(proxyURL); err == nil {
71                         return tcpSocket{l, func(ctx context.Context, addr string) (conn net.Conn, err error) {
72                                 defer perf.ScopeTimerErr(&err)()
73                                 return dialer.Dial(network, addr)
74                         }}, nil
75                 }
76         }
77         dialer := net.Dialer{}
78         return tcpSocket{l, func(ctx context.Context, addr string) (conn net.Conn, err error) {
79                 defer perf.ScopeTimerErr(&err)()
80                 return dialer.DialContext(ctx, network, addr)
81         }}, nil
82 }
83
84 type tcpSocket struct {
85         net.Listener
86         d func(ctx context.Context, addr string) (net.Conn, error)
87 }
88
89 func (me tcpSocket) dial(ctx context.Context, addr string) (net.Conn, error) {
90         return me.d(ctx, addr)
91 }
92
93 func listenAll(networks []string, getHost func(string) string, port int, proxyURL string, f firewallCallback) ([]socket, error) {
94         if len(networks) == 0 {
95                 return nil, nil
96         }
97         var nahs []networkAndHost
98         for _, n := range networks {
99                 nahs = append(nahs, networkAndHost{n, getHost(n)})
100         }
101         for {
102                 ss, retry, err := listenAllRetry(nahs, port, proxyURL, f)
103                 if !retry {
104                         return ss, err
105                 }
106         }
107 }
108
109 type networkAndHost struct {
110         Network string
111         Host    string
112 }
113
114 func listenAllRetry(nahs []networkAndHost, port int, proxyURL string, f firewallCallback) (ss []socket, retry bool, err error) {
115         ss = make([]socket, 1, len(nahs))
116         portStr := strconv.FormatInt(int64(port), 10)
117         ss[0], err = listen(nahs[0].Network, net.JoinHostPort(nahs[0].Host, portStr), proxyURL, f)
118         if err != nil {
119                 return nil, false, errors.Wrap(err, "first listen")
120         }
121         defer func() {
122                 if err != nil || retry {
123                         for _, s := range ss {
124                                 s.Close()
125                         }
126                         ss = nil
127                 }
128         }()
129         portStr = strconv.FormatInt(int64(missinggo.AddrPort(ss[0].Addr())), 10)
130         for _, nah := range nahs[1:] {
131                 s, err := listen(nah.Network, net.JoinHostPort(nah.Host, portStr), proxyURL, f)
132                 if err != nil {
133                         return ss,
134                                 missinggo.IsAddrInUse(err) && port == 0,
135                                 errors.Wrap(err, "subsequent listen")
136                 }
137                 ss = append(ss, s)
138         }
139         return
140 }
141
142 type firewallCallback func(net.Addr) bool
143
144 func listenUtp(network, addr, proxyURL string, fc firewallCallback) (s socket, err error) {
145         us, err := NewUtpSocket(network, addr, fc)
146         if err != nil {
147                 return
148         }
149
150         // If we don't need the proxy - then we should return default net.Dialer,
151         // otherwise, let's try to parse the proxyURL and return proxy.Dialer
152         if len(proxyURL) != 0 {
153                 if dialer, err := getProxyDialer(proxyURL); err == nil {
154                         return utpSocketSocket{us, network, dialer}, nil
155                 }
156         }
157
158         return utpSocketSocket{us, network, nil}, nil
159 }
160
161 type utpSocketSocket struct {
162         utpSocket
163         network string
164         d       proxy.Dialer
165 }
166
167 func (me utpSocketSocket) dial(ctx context.Context, addr string) (conn net.Conn, err error) {
168         defer perf.ScopeTimerErr(&err)()
169         if me.d != nil {
170                 return me.d.Dial(me.network, addr)
171         }
172
173         return me.utpSocket.DialContext(ctx, me.network, addr)
174 }