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