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