8 "github.com/anacrolix/log"
9 "github.com/anacrolix/missinggo/perf"
10 "github.com/anacrolix/missinggo/v2"
11 "github.com/pkg/errors"
13 "github.com/anacrolix/torrent/dialer"
16 type Listener interface {
17 // Accept waits for and returns the next connection to the listener.
18 Accept() (net.Conn, error)
20 // Addr returns the listener's network address.
24 type socket interface {
30 func listen(n network, addr string, f firewallCallback, logger log.Logger) (socket, error) {
33 return listenTcp(n.String(), addr)
35 return listenUtp(n.String(), addr, f, logger)
41 func listenTcp(network, address string) (s socket, err error) {
42 l, err := net.Listen(network, address)
45 NetworkDialer: NetworkDialer{
47 Dialer: dialer.Default,
52 type tcpSocket struct {
57 func listenAll(networks []network, getHost func(string) string, port int, f firewallCallback, logger log.Logger) ([]socket, error) {
58 if len(networks) == 0 {
61 var nahs []networkAndHost
62 for _, n := range networks {
63 nahs = append(nahs, networkAndHost{n, getHost(n.String())})
66 ss, retry, err := listenAllRetry(nahs, port, f, logger)
73 type networkAndHost struct {
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)
83 return nil, false, errors.Wrap(err, "first listen")
86 if err != nil || retry {
87 for _, s := range ss {
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)
98 missinggo.IsAddrInUse(err) && port == 0,
99 errors.Wrap(err, "subsequent listen")
106 // This isn't aliased from go-libutp since that assumes CGO.
107 type firewallCallback func(net.Addr) bool
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
114 // utpSocket wrapper, additionally wrapped for the torrent package's socket interface.
115 type utpSocketSocket struct {
120 func (me utpSocketSocket) DialerNetwork() string {
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)