* Update anacrolix/go-libutp (v1.1.0 -> v1.2.0) * Pass client logger to anacrolix/go-libutp * Pass logger instead of option The project now compiles properly when CGO is not enabled. Additionally, the new argument (now log.Logger instead of utp.NewSocketOpt) is now required. The tests have been updated to match this change, and now pass logger.Default to NewUtpSocket. * Correct function signature of NewUtpSocket
126 lines
2.9 KiB
Go
126 lines
2.9 KiB
Go
package torrent
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
"strconv"
|
|
|
|
"github.com/anacrolix/log"
|
|
"github.com/anacrolix/missinggo/perf"
|
|
"github.com/anacrolix/missinggo/v2"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
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: DefaultNetDialer,
|
|
},
|
|
}, 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)
|
|
}
|