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