torrent/socket.go

125 lines
2.8 KiB
Go
Raw Normal View History

package torrent
import (
"context"
"net"
"strconv"
"github.com/anacrolix/missinggo/perf"
2021-06-23 07:24:50 +00:00
"github.com/anacrolix/missinggo/v2"
"github.com/pkg/errors"
)
type Listener interface {
2020-11-08 23:56:27 +00:00
// 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
}
2020-02-19 23:57:02 +00:00
type socket interface {
Listener
Dialer
2020-11-08 23:56:27 +00:00
Close() error
}
2020-02-19 23:57:02 +00:00
func listen(n network, addr string, f firewallCallback) (socket, error) {
switch {
case n.Tcp:
2020-02-19 23:57:02 +00:00
return listenTcp(n.String(), addr)
case n.Udp:
2020-02-19 23:57:02 +00:00
return listenUtp(n.String(), addr, f)
default:
panic(n)
}
}
2020-02-19 23:57:02 +00:00
func listenTcp(network, address string) (s socket, err error) {
l, err := net.Listen(network, address)
2020-02-19 23:57:02 +00:00
return tcpSocket{
Listener: l,
2021-06-21 03:29:26 +00:00
NetworkDialer: NetworkDialer{
Network: network,
2021-06-21 03:29:26 +00:00
Dialer: DefaultNetDialer,
},
2020-02-19 23:57:02 +00:00
}, err
}
2020-02-19 23:57:02 +00:00
type tcpSocket struct {
net.Listener
2021-06-21 03:29:26 +00:00
NetworkDialer
}
2020-02-19 23:57:02 +00:00
func listenAll(networks []network, getHost func(string) string, port int, f firewallCallback) ([]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 {
2020-02-19 23:57:02 +00:00
ss, retry, err := listenAllRetry(nahs, port, f)
if !retry {
return ss, err
}
}
}
type networkAndHost struct {
Network network
Host string
}
2020-02-19 23:57:02 +00:00
func listenAllRetry(nahs []networkAndHost, port int, f firewallCallback) (ss []socket, retry bool, err error) {
ss = make([]socket, 1, len(nahs))
portStr := strconv.FormatInt(int64(port), 10)
2020-02-19 23:57:02 +00:00
ss[0], err = listen(nahs[0].Network, net.JoinHostPort(nahs[0].Host, portStr), f)
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:] {
2020-02-19 23:57:02 +00:00
s, err := listen(nah.Network, net.JoinHostPort(nah.Host, portStr), f)
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
2020-02-19 23:57:02 +00:00
func listenUtp(network, addr string, fc firewallCallback) (socket, error) {
us, err := NewUtpSocket(network, addr, fc)
2020-02-19 23:57:02 +00:00
return utpSocketSocket{us, network}, err
}
2021-06-21 02:54:57 +00:00
// utpSocket wrapper, additionally wrapped for the torrent package's socket interface.
type utpSocketSocket struct {
utpSocket
network string
}
2021-06-21 02:54:57 +00:00
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)
}