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