7 g "github.com/anacrolix/generics"
13 "github.com/anacrolix/log"
14 "github.com/anacrolix/missinggo/perf"
15 "github.com/anacrolix/missinggo/v2"
18 type Listener interface {
19 // Accept waits for and returns the next connection to the listener.
20 Accept() (net.Conn, error)
22 // Addr returns the listener's network address.
26 type socket interface {
32 func listen(n network, addr string, f firewallCallback, logger log.Logger) (socket, error) {
35 return listenTcp(n.String(), addr)
37 return listenUtp(n.String(), addr, f, logger)
43 // Dialing TCP from a local port limits us to a single outgoing TCP connection to each remote
44 // client. Instead, this should be a last resort if we need to use holepunching, and only then to
45 // connect to other clients that actually try to holepunch TCP.
46 const dialTcpFromListenPort = false
48 var tcpListenConfig = net.ListenConfig{
49 Control: func(network, address string, c syscall.RawConn) (err error) {
50 controlErr := c.Control(func(fd uintptr) {
51 if dialTcpFromListenPort {
52 err = setReusePortSockOpts(fd)
61 // BitTorrent connections manage their own keep-alives.
65 func listenTcp(network, address string) (s socket, err error) {
66 l, err := tcpListenConfig.Listen(context.Background(), network, address)
70 netDialer := net.Dialer{
71 // We don't want fallback, as we explicitly manage the IPv4/IPv6 distinction ourselves,
72 // although it's probably not triggered as I think the network is already constrained to
73 // tcp4 or tcp6 at this point.
75 // BitTorrent connections manage their own keepalives.
76 KeepAlive: tcpListenConfig.KeepAlive,
77 Control: func(network, address string, c syscall.RawConn) (err error) {
78 controlErr := c.Control(func(fd uintptr) {
79 err = setSockNoLinger(fd)
81 // Failing to disable linger is undesirable, but not fatal.
82 log.Levelf(log.Debug, "error setting linger socket option on tcp socket: %v", err)
85 // This is no longer required I think, see
86 // https://github.com/anacrolix/torrent/discussions/856. I added this originally to
87 // allow dialling out from the client's listen port, but that doesn't really work. I
88 // think Linux older than ~2013 doesn't support SO_REUSEPORT.
89 if dialTcpFromListenPort {
90 err = setReusePortSockOpts(fd)
99 if dialTcpFromListenPort {
100 netDialer.LocalAddr = l.Addr()
104 NetworkDialer: NetworkDialer{
112 type tcpSocket struct {
119 getHost func(string) string,
123 ) ([]socket, error) {
124 if len(networks) == 0 {
127 var nahs []networkAndHost
128 for _, n := range networks {
129 nahs = append(nahs, networkAndHost{n, getHost(n.String())})
132 ss, retry, err := listenAllRetry(nahs, port, f, logger)
139 type networkAndHost struct {
144 func isUnsupportedNetworkError(err error) bool {
145 var sysErr *os.SyscallError
146 //spewCfg := spew.NewDefaultConfig()
147 //spewCfg.ContinueOnMethod = true
149 if !errors.As(err, &sysErr) {
152 //spewCfg.Dump(sysErr)
153 //spewCfg.Dump(sysErr.Err.Error())
154 // This might only be Linux specific.
155 return sysErr.Syscall == "bind" && sysErr.Err.Error() == "cannot assign requested address"
159 nahs []networkAndHost,
163 ) (ss []socket, retry bool, err error) {
164 // Close all sockets on error or retry.
166 if err != nil || retry {
167 for _, s := range ss {
173 g.MakeSliceWithCap(&ss, len(nahs))
174 portStr := strconv.FormatInt(int64(port), 10)
175 for _, nah := range nahs {
177 s, err = listen(nah.Network, net.JoinHostPort(nah.Host, portStr), f, logger)
179 if isUnsupportedNetworkError(err) {
184 // First relative to a possibly dynamic port (0).
185 err = fmt.Errorf("first listen: %w", err)
187 err = fmt.Errorf("subsequent listen: %w", err)
189 retry = missinggo.IsAddrInUse(err) && port == 0
193 portStr = strconv.FormatInt(int64(missinggo.AddrPort(ss[0].Addr())), 10)
198 // This isn't aliased from go-libutp since that assumes CGO.
199 type firewallCallback func(net.Addr) bool
201 func listenUtp(network, addr string, fc firewallCallback, logger log.Logger) (socket, error) {
202 us, err := NewUtpSocket(network, addr, fc, logger)
203 return utpSocketSocket{us, network}, err
206 // utpSocket wrapper, additionally wrapped for the torrent package's socket interface.
207 type utpSocketSocket struct {
212 func (me utpSocketSocket) DialerNetwork() string {
216 func (me utpSocketSocket) Dial(ctx context.Context, addr string) (conn net.Conn, err error) {
217 defer perf.ScopeTimerErr(&err)()
218 return me.utpSocket.DialContext(ctx, me.network, addr)