]> Sergey Matveev's repositories - btrtrc.git/blob - socket.go
Pass client logger to anacrolix/go-libutp sockets (#722)
[btrtrc.git] / socket.go
1 package torrent
2
3 import (
4         "context"
5         "net"
6         "strconv"
7
8         "github.com/anacrolix/log"
9         "github.com/anacrolix/missinggo/perf"
10         "github.com/anacrolix/missinggo/v2"
11         "github.com/pkg/errors"
12 )
13
14 type Listener interface {
15         // Accept waits for and returns the next connection to the listener.
16         Accept() (net.Conn, error)
17
18         // Addr returns the listener's network address.
19         Addr() net.Addr
20 }
21
22 type socket interface {
23         Listener
24         Dialer
25         Close() error
26 }
27
28 func listen(n network, addr string, f firewallCallback, logger log.Logger) (socket, error) {
29         switch {
30         case n.Tcp:
31                 return listenTcp(n.String(), addr)
32         case n.Udp:
33                 return listenUtp(n.String(), addr, f, logger)
34         default:
35                 panic(n)
36         }
37 }
38
39 func listenTcp(network, address string) (s socket, err error) {
40         l, err := net.Listen(network, address)
41         return tcpSocket{
42                 Listener: l,
43                 NetworkDialer: NetworkDialer{
44                         Network: network,
45                         Dialer:  DefaultNetDialer,
46                 },
47         }, err
48 }
49
50 type tcpSocket struct {
51         net.Listener
52         NetworkDialer
53 }
54
55 func listenAll(networks []network, getHost func(string) string, port int, f firewallCallback, logger log.Logger) ([]socket, error) {
56         if len(networks) == 0 {
57                 return nil, nil
58         }
59         var nahs []networkAndHost
60         for _, n := range networks {
61                 nahs = append(nahs, networkAndHost{n, getHost(n.String())})
62         }
63         for {
64                 ss, retry, err := listenAllRetry(nahs, port, f, logger)
65                 if !retry {
66                         return ss, err
67                 }
68         }
69 }
70
71 type networkAndHost struct {
72         Network network
73         Host    string
74 }
75
76 func listenAllRetry(nahs []networkAndHost, port int, f firewallCallback, logger log.Logger) (ss []socket, retry bool, err error) {
77         ss = make([]socket, 1, len(nahs))
78         portStr := strconv.FormatInt(int64(port), 10)
79         ss[0], err = listen(nahs[0].Network, net.JoinHostPort(nahs[0].Host, portStr), f, logger)
80         if err != nil {
81                 return nil, false, errors.Wrap(err, "first listen")
82         }
83         defer func() {
84                 if err != nil || retry {
85                         for _, s := range ss {
86                                 s.Close()
87                         }
88                         ss = nil
89                 }
90         }()
91         portStr = strconv.FormatInt(int64(missinggo.AddrPort(ss[0].Addr())), 10)
92         for _, nah := range nahs[1:] {
93                 s, err := listen(nah.Network, net.JoinHostPort(nah.Host, portStr), f, logger)
94                 if err != nil {
95                         return ss,
96                                 missinggo.IsAddrInUse(err) && port == 0,
97                                 errors.Wrap(err, "subsequent listen")
98                 }
99                 ss = append(ss, s)
100         }
101         return
102 }
103
104 // This isn't aliased from go-libutp since that assumes CGO.
105 type firewallCallback func(net.Addr) bool
106
107 func listenUtp(network, addr string, fc firewallCallback, logger log.Logger) (socket, error) {
108         us, err := NewUtpSocket(network, addr, fc, logger)
109         return utpSocketSocket{us, network}, err
110 }
111
112 // utpSocket wrapper, additionally wrapped for the torrent package's socket interface.
113 type utpSocketSocket struct {
114         utpSocket
115         network string
116 }
117
118 func (me utpSocketSocket) DialerNetwork() string {
119         return me.network
120 }
121
122 func (me utpSocketSocket) Dial(ctx context.Context, addr string) (conn net.Conn, err error) {
123         defer perf.ScopeTimerErr(&err)()
124         return me.utpSocket.DialContext(ctx, me.network, addr)
125 }