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