github.com/anacrolix/torrent@v1.61.0/client.go (about)

     1  package torrent
     2  
     3  import (
     4  	"bufio"
     5  	"cmp"
     6  	"context"
     7  	"crypto/rand"
     8  	"encoding/binary"
     9  	"encoding/hex"
    10  	"errors"
    11  	"expvar"
    12  	"fmt"
    13  	"io"
    14  	"log/slog"
    15  	"math"
    16  	"net"
    17  	"net/http"
    18  	"net/netip"
    19  	"runtime"
    20  	"slices"
    21  	"strconv"
    22  	"time"
    23  
    24  	"github.com/RoaringBitmap/roaring"
    25  	"github.com/anacrolix/chansync"
    26  	"github.com/anacrolix/chansync/events"
    27  	"github.com/anacrolix/dht/v2"
    28  	"github.com/anacrolix/dht/v2/krpc"
    29  	. "github.com/anacrolix/generics"
    30  	g "github.com/anacrolix/generics"
    31  	"github.com/anacrolix/log"
    32  	"github.com/anacrolix/missinggo/v2"
    33  	"github.com/anacrolix/missinggo/v2/bitmap"
    34  	"github.com/anacrolix/missinggo/v2/panicif"
    35  	"github.com/anacrolix/missinggo/v2/pproffd"
    36  	"github.com/anacrolix/sync"
    37  	"github.com/anacrolix/torrent/internal/amortize"
    38  	"github.com/anacrolix/torrent/internal/extracmp"
    39  	"github.com/anacrolix/torrent/tracker"
    40  	"github.com/anacrolix/torrent/webtorrent"
    41  	"github.com/cespare/xxhash"
    42  	"github.com/dustin/go-humanize"
    43  	gbtree "github.com/google/btree"
    44  	"github.com/pion/webrtc/v4"
    45  
    46  	"github.com/anacrolix/torrent/bencode"
    47  	"github.com/anacrolix/torrent/internal/check"
    48  	"github.com/anacrolix/torrent/internal/limiter"
    49  	"github.com/anacrolix/torrent/iplist"
    50  	"github.com/anacrolix/torrent/metainfo"
    51  	"github.com/anacrolix/torrent/mse"
    52  	pp "github.com/anacrolix/torrent/peer_protocol"
    53  	"github.com/anacrolix/torrent/storage"
    54  	"github.com/anacrolix/torrent/types/infohash"
    55  	infohash_v2 "github.com/anacrolix/torrent/types/infohash-v2"
    56  )
    57  
    58  const webseedRequestUpdateTimerInterval = 5 * time.Second
    59  
    60  // Clients contain zero or more Torrents. A Client manages a blocklist, the
    61  // TCP/UDP protocol ports, and DHT as desired.
    62  type Client struct {
    63  	// An aggregate of stats over all connections. First in struct to ensure 64-bit alignment of
    64  	// fields. See #262.
    65  	connStats AllConnStats
    66  	counters  TorrentStatCounters
    67  
    68  	_mu            lockWithDeferreds
    69  	unlockHandlers clientUnlockHandlers
    70  	check          amortize.Value
    71  	// Used in constrained situations when the lock is held.
    72  	roaringIntIterator roaring.IntIterator
    73  	event              sync.Cond
    74  	closed             chansync.SetOnce
    75  
    76  	config  *ClientConfig
    77  	logger  log.Logger
    78  	slogger *slog.Logger
    79  
    80  	peerID         PeerID
    81  	defaultStorage *storage.Client
    82  	onClose        []func()
    83  	dialers        []Dialer
    84  	listeners      []Listener
    85  	dhtServers     []DhtServer
    86  	ipBlockList    iplist.Ranger
    87  
    88  	// Set of addresses that have our client ID. This intentionally will
    89  	// include ourselves if we end up trying to connect to our own address
    90  	// through legitimate channels.
    91  	dopplegangerAddrs map[string]struct{}
    92  	badPeerIPs        map[netip.Addr]struct{}
    93  	// All Torrents once.
    94  	torrents map[*Torrent]struct{}
    95  	// All Torrents by their short infohashes (v1 if valid, and truncated v2 if valid). Unless the
    96  	// info has been obtained, there's no knowing if an infohash belongs to v1 or v2. TODO: Make
    97  	// this a weak pointer.
    98  	torrentsByShortHash map[InfoHash]*Torrent
    99  
   100  	// Piece request orderings grouped by storage. Value is value type because all fields are
   101  	// references.
   102  	pieceRequestOrder map[clientPieceRequestOrderKeySumType]clientPieceRequestOrderValue
   103  
   104  	acceptLimiter map[ipStr]int
   105  	numHalfOpen   int
   106  
   107  	websocketTrackers                websocketTrackers
   108  	regularTrackerAnnounceDispatcher regularTrackerAnnounceDispatcher
   109  
   110  	numWebSeedRequests map[webseedHostKeyHandle]int
   111  
   112  	activeAnnounceLimiter limiter.Instance
   113  	// TODO: Move this onto ClientConfig.
   114  	httpClient *http.Client
   115  
   116  	clientHolepunchAddrSets
   117  
   118  	defaultLocalLtepProtocolMap LocalLtepProtocolMap
   119  
   120  	upnpMappings []*upnpMapping
   121  
   122  	clientWebseedState
   123  
   124  	activePieceHashers int
   125  }
   126  
   127  type clientWebseedState struct {
   128  	webseedRequestTimer   *time.Timer
   129  	webseedUpdateReason   updateRequestReason
   130  	activeWebseedRequests map[webseedUniqueRequestKey]*webseedRequest
   131  	aprioriMap            map[webseedUniqueRequestKey]aprioriMapValue
   132  	heapSlice             []webseedRequestHeapElem
   133  }
   134  
   135  type ipStr string
   136  
   137  func (cl *Client) BadPeerIPs() (ips []string) {
   138  	cl.rLock()
   139  	ips = cl.badPeerIPsLocked()
   140  	cl.rUnlock()
   141  	return
   142  }
   143  
   144  func (cl *Client) badPeerIPsLocked() (ips []string) {
   145  	ips = make([]string, len(cl.badPeerIPs))
   146  	i := 0
   147  	for k := range cl.badPeerIPs {
   148  		ips[i] = k.String()
   149  		i += 1
   150  	}
   151  	return
   152  }
   153  
   154  func (cl *Client) PeerID() PeerID {
   155  	return cl.peerID
   156  }
   157  
   158  // Returns the port number for the first listener that has one. No longer assumes that all port
   159  // numbers are the same, due to support for custom listeners. Returns zero if no port number is
   160  // found.
   161  func (cl *Client) LocalPort() (port int) {
   162  	for i := 0; i < len(cl.listeners); i += 1 {
   163  		if port = addrPortOrZero(cl.listeners[i].Addr()); port != 0 {
   164  			return
   165  		}
   166  	}
   167  	return
   168  }
   169  
   170  // Writes out a human-readable status of the client, such as for writing to an HTTP status page.
   171  func (cl *Client) WriteStatus(_w io.Writer) {
   172  	cl.rLock()
   173  	defer cl.rUnlock()
   174  	w := bufio.NewWriter(_w)
   175  	defer w.Flush()
   176  	fmt.Fprintf(w, "Listen port: %d\n", cl.LocalPort())
   177  	fmt.Fprintf(w, "Peer ID: %+q\n", cl.PeerID())
   178  	fmt.Fprintf(w, "Extension bits: %v\n", cl.config.Extensions)
   179  	fmt.Fprintf(w, "Announce key: %x\n", cl.announceKey())
   180  	fmt.Fprintf(w, "Banned IPs: %d\n", len(cl.badPeerIPsLocked()))
   181  	cl.eachDhtServer(func(s DhtServer) {
   182  		fmt.Fprintf(w, "%s DHT server at %s:\n", s.Addr().Network(), s.Addr().String())
   183  		writeDhtServerStatus(w, s)
   184  	})
   185  	dumpStats(w, cl.statsLocked())
   186  	torrentsSlice := cl.torrentsAsSlice()
   187  	incomplete := 0
   188  	for _, t := range torrentsSlice {
   189  		if !t.Complete().Bool() {
   190  			incomplete++
   191  		}
   192  	}
   193  	fmt.Fprintf(w, "# Torrents: %d (%v incomplete)\n", len(torrentsSlice), incomplete)
   194  	fmt.Fprintln(w)
   195  	slices.SortFunc(torrentsSlice, func(a, b *Torrent) int {
   196  		return cmp.Or(
   197  			extracmp.CompareBool(a.haveInfo(), b.haveInfo()),
   198  			func() int {
   199  				if a.haveInfo() && b.haveInfo() {
   200  					return -cmp.Compare(a.bytesLeft(), b.bytesLeft())
   201  				} else {
   202  					return 0
   203  				}
   204  			}(),
   205  			cmp.Compare(a.canonicalShortInfohash().AsString(), b.canonicalShortInfohash().AsString()),
   206  		)
   207  	})
   208  	for _, t := range torrentsSlice {
   209  		if t.name() == "" {
   210  			fmt.Fprint(w, "<unknown name>")
   211  		} else {
   212  			fmt.Fprint(w, t.name())
   213  		}
   214  		fmt.Fprint(w, "\n")
   215  		if t.info != nil {
   216  			fmt.Fprintf(
   217  				w,
   218  				"%f%% of %d bytes (%s)",
   219  				100*t.progressUnitFloat(),
   220  				t.length(),
   221  				humanize.Bytes(uint64(t.length())))
   222  		} else {
   223  			w.WriteString("<missing metainfo>")
   224  		}
   225  		fmt.Fprint(w, "\n")
   226  		t.writeStatus(w)
   227  		fmt.Fprintln(w)
   228  	}
   229  	cl.writeRegularTrackerAnnouncerStatus(w)
   230  }
   231  
   232  func (cl *Client) writeRegularTrackerAnnouncerStatus(w io.Writer) {
   233  	cl.regularTrackerAnnounceDispatcher.writeStatus(w)
   234  }
   235  
   236  func (cl *Client) getLoggers() (log.Logger, *slog.Logger) {
   237  	logger := cl.config.Logger
   238  	slogger := cl.config.Slogger
   239  	// Maintain old behaviour if ClientConfig.Slogger isn't provided. Point Slogger to Logger so it
   240  	// appears unmodified.
   241  	if slogger == nil {
   242  		if logger.IsZero() {
   243  			logger = log.Default
   244  		}
   245  		if cl.config.Debug {
   246  			logger = logger.WithFilterLevel(log.Debug)
   247  		}
   248  		logger = logger.WithValues(cl)
   249  		return logger, logger.Slogger()
   250  	}
   251  	// Point logger to slogger.
   252  	if logger.IsZero() {
   253  		// I see "warning - 1" become info by the time it reaches an Erigon/geth logger. I think
   254  		// we've lost the old default of debug somewhere along the way.
   255  		logger = log.NewLogger().WithDefaultLevel(log.Debug)
   256  		logger.SetHandlers(log.SlogHandlerAsHandler{slogger.Handler()})
   257  	}
   258  	// The unhandled case is that both logger and slogger are set. In this case, use them as normal.
   259  	return logger, slogger
   260  }
   261  
   262  func (cl *Client) initLogger() {
   263  	cl.logger, cl.slogger = cl.getLoggers()
   264  }
   265  
   266  func (cl *Client) announceKey() int32 {
   267  	return int32(binary.BigEndian.Uint32(cl.peerID[16:20]))
   268  }
   269  
   270  // Performs infallible parts of Client initialization. *Client and *ClientConfig must not be nil.
   271  func (cl *Client) init(cfg *ClientConfig) {
   272  	cl.unlockHandlers.init()
   273  	cl.config = cfg
   274  	cl._mu.client = cl
   275  	cl.initLogger()
   276  	cl.regularTrackerAnnounceDispatcher.init(cl)
   277  	cfg.setRateLimiterBursts()
   278  	g.MakeMap(&cl.dopplegangerAddrs)
   279  	g.MakeMap(&cl.torrentsByShortHash)
   280  	g.MakeMap(&cl.torrents)
   281  	cl.torrentsByShortHash = make(map[metainfo.Hash]*Torrent)
   282  	cl.event.L = cl.locker()
   283  	cl.ipBlockList = cfg.IPBlocklist
   284  	cl.httpClient = &http.Client{
   285  		Transport: cfg.WebTransport,
   286  	}
   287  	if cl.httpClient.Transport == nil {
   288  		cl.httpClient.Transport = &http.Transport{
   289  			Proxy:       cfg.HTTPProxy,
   290  			DialContext: cfg.HTTPDialContext,
   291  			// I think this value was observed from some webseeds. It seems reasonable to extend it
   292  			// to other uses of HTTP from the client.
   293  			MaxConnsPerHost: 10,
   294  		}
   295  	}
   296  	cfg.MetainfoSourcesClient = cmp.Or(cfg.MetainfoSourcesClient, cl.httpClient)
   297  	cl.defaultLocalLtepProtocolMap = makeBuiltinLtepProtocols(!cfg.DisablePEX)
   298  	g.MakeMap(&cl.numWebSeedRequests)
   299  
   300  	go cl.acceptLimitClearer()
   301  	//cl.logger.Levelf(log.Critical, "test after init")
   302  
   303  	storageImpl := cfg.DefaultStorage
   304  	if storageImpl == nil {
   305  		// We'd use mmap by default but HFS+ doesn't support sparse files.
   306  		storageImplCloser := storage.NewFile(cfg.DataDir)
   307  		cl.onClose = append(cl.onClose, func() {
   308  			if err := storageImplCloser.Close(); err != nil {
   309  				cl.logger.Printf("error closing default storage: %s", err)
   310  			}
   311  		})
   312  		storageImpl = storageImplCloser
   313  	}
   314  	cl.defaultStorage = storage.NewClient(storageImpl)
   315  
   316  	if cfg.PeerID != "" {
   317  		missinggo.CopyExact(&cl.peerID, cfg.PeerID)
   318  	} else {
   319  		o := copy(cl.peerID[:], cfg.Bep20)
   320  		_, err := rand.Read(cl.peerID[o:])
   321  		if err != nil {
   322  			panic("error generating peer id")
   323  		}
   324  	}
   325  
   326  	cl.websocketTrackers = websocketTrackers{
   327  		PeerId: cl.peerID,
   328  		Logger: cl.logger.WithNames("websocketTrackers"),
   329  		GetAnnounceRequest: func(
   330  			event tracker.AnnounceEvent, infoHash [20]byte,
   331  		) (
   332  			tracker.AnnounceRequest, error,
   333  		) {
   334  			cl.lock()
   335  			defer cl.unlock()
   336  			t, ok := cl.torrentsByShortHash[infoHash]
   337  			if !ok {
   338  				return tracker.AnnounceRequest{}, errors.New("torrent not tracked by client")
   339  			}
   340  			return t.announceRequest(event, infoHash), nil
   341  		},
   342  		Proxy:                      cl.config.HTTPProxy,
   343  		WebsocketTrackerHttpHeader: cl.config.WebsocketTrackerHttpHeader,
   344  		ICEServers:                 cl.ICEServers(),
   345  		DialContext:                cl.config.TrackerDialContext,
   346  		callbacks:                  &cl.config.Callbacks,
   347  		OnConn: func(dc webtorrent.DataChannelConn, dcc webtorrent.DataChannelContext) {
   348  			cl.lock()
   349  			defer cl.unlock()
   350  			t, ok := cl.torrentsByShortHash[dcc.InfoHash]
   351  			if !ok {
   352  				cl.logger.WithDefaultLevel(log.Warning).Printf(
   353  					"got webrtc conn for unloaded torrent with infohash %x",
   354  					dcc.InfoHash,
   355  				)
   356  				dc.Close()
   357  				return
   358  			}
   359  			go t.onWebRtcConn(dc, dcc)
   360  		},
   361  	}
   362  
   363  	cl.webseedRequestTimer = time.AfterFunc(webseedRequestUpdateTimerInterval, cl.updateWebseedRequestsTimerFunc)
   364  }
   365  
   366  // Creates a new Client. Takes ownership of the ClientConfig. Create another one if you want another
   367  // Client.
   368  func NewClient(cfg *ClientConfig) (cl *Client, err error) {
   369  	if cfg == nil {
   370  		cfg = NewDefaultClientConfig()
   371  		cfg.ListenPort = 0
   372  	}
   373  	cl = &Client{}
   374  	cl.init(cfg)
   375  	// Belongs after infallible init
   376  	defer func() {
   377  		if err != nil {
   378  			cl.Close()
   379  			cl = nil
   380  		}
   381  	}()
   382  	builtinListenNetworks := cl.listenNetworks()
   383  	sockets, err := listenAll(
   384  		builtinListenNetworks,
   385  		cl.config.ListenHost,
   386  		cl.config.ListenPort,
   387  		cl.firewallCallback,
   388  		cl.logger,
   389  	)
   390  	if err != nil {
   391  		return
   392  	}
   393  	if len(sockets) == 0 && len(builtinListenNetworks) != 0 {
   394  		err = fmt.Errorf("no sockets created for networks %v", builtinListenNetworks)
   395  		return
   396  	}
   397  
   398  	// Check for panics.
   399  	cl.LocalPort()
   400  
   401  	for _, s := range sockets {
   402  		cl.onClose = append(cl.onClose, func() { go s.Close() })
   403  		if peerNetworkEnabled(parseNetworkString(s.Addr().Network()), cl.config) {
   404  			if cl.config.DialForPeerConns {
   405  				cl.dialers = append(cl.dialers, s)
   406  			}
   407  			cl.listeners = append(cl.listeners, s)
   408  			if cl.config.AcceptPeerConnections {
   409  				go cl.acceptConnections(s)
   410  			}
   411  		}
   412  	}
   413  
   414  	if !cfg.NoDefaultPortForwarding {
   415  		go cl.forwardPort()
   416  	}
   417  	if !cfg.NoDHT {
   418  		for _, s := range sockets {
   419  			if pc, ok := s.(net.PacketConn); ok {
   420  				ds, err := cl.NewAnacrolixDhtServer(pc)
   421  				if err != nil {
   422  					panic(err)
   423  				}
   424  				cl.dhtServers = append(cl.dhtServers, AnacrolixDhtServerWrapper{ds})
   425  				cl.onClose = append(cl.onClose, func() { ds.Close() })
   426  			}
   427  		}
   428  	}
   429  
   430  	err = cl.checkConfig()
   431  	return
   432  }
   433  
   434  func (cl *Client) AddDhtServer(d DhtServer) {
   435  	cl.dhtServers = append(cl.dhtServers, d)
   436  }
   437  
   438  // Adds a Dialer for outgoing connections. All Dialers are used when attempting to connect to a
   439  // given address for any Torrent.
   440  func (cl *Client) AddDialer(d Dialer) {
   441  	cl.lock()
   442  	defer cl.unlock()
   443  	cl.dialers = append(cl.dialers, d)
   444  	for t := range cl.torrents {
   445  		t.openNewConns()
   446  	}
   447  }
   448  
   449  func (cl *Client) Listeners() []Listener {
   450  	return cl.listeners
   451  }
   452  
   453  // Registers a Listener, and starts Accepting on it. You must Close Listeners provided this way
   454  // yourself.
   455  func (cl *Client) AddListener(l Listener) {
   456  	cl.listeners = append(cl.listeners, l)
   457  	if cl.config.AcceptPeerConnections {
   458  		go cl.acceptConnections(l)
   459  	}
   460  }
   461  
   462  func (cl *Client) firewallCallback(net.Addr) bool {
   463  	cl.rLock()
   464  	block := !cl.wantConns() || !cl.config.AcceptPeerConnections
   465  	cl.rUnlock()
   466  	if block {
   467  		torrent.Add("connections firewalled", 1)
   468  	} else {
   469  		torrent.Add("connections not firewalled", 1)
   470  	}
   471  	return block
   472  }
   473  
   474  func (cl *Client) listenOnNetwork(n network) bool {
   475  	if n.Ipv4 && cl.config.DisableIPv4 {
   476  		return false
   477  	}
   478  	if n.Ipv6 && cl.config.DisableIPv6 {
   479  		return false
   480  	}
   481  	if n.Tcp && cl.config.DisableTCP {
   482  		return false
   483  	}
   484  	if n.Udp && cl.config.DisableUTP && cl.config.NoDHT {
   485  		return false
   486  	}
   487  	return true
   488  }
   489  
   490  func (cl *Client) listenNetworks() (ns []network) {
   491  	for _, n := range allPeerNetworks {
   492  		if cl.listenOnNetwork(n) {
   493  			ns = append(ns, n)
   494  		}
   495  	}
   496  	return
   497  }
   498  
   499  // Creates an anacrolix/dht Server, as would be done internally in NewClient, for the given conn.
   500  func (cl *Client) NewAnacrolixDhtServer(conn net.PacketConn) (s *dht.Server, err error) {
   501  	logger := cl.logger.WithNames("dht", conn.LocalAddr().String())
   502  	cfg := dht.ServerConfig{
   503  		IPBlocklist:    cl.ipBlockList,
   504  		Conn:           conn,
   505  		OnAnnouncePeer: cl.onDHTAnnouncePeer,
   506  		PublicIP: func() net.IP {
   507  			if connIsIpv6(conn) && cl.config.PublicIp6 != nil {
   508  				return cl.config.PublicIp6
   509  			}
   510  			return cl.config.PublicIp4
   511  		}(),
   512  		StartingNodes: cl.config.DhtStartingNodes(conn.LocalAddr().Network()),
   513  		OnQuery:       cl.config.DHTOnQuery,
   514  		Logger:        logger,
   515  	}
   516  	if f := cl.config.ConfigureAnacrolixDhtServer; f != nil {
   517  		f(&cfg)
   518  	}
   519  	s, err = dht.NewServer(&cfg)
   520  	if err == nil {
   521  		go s.TableMaintainer()
   522  	}
   523  	return
   524  }
   525  
   526  func (cl *Client) Closed() events.Done {
   527  	return cl.closed.Done()
   528  }
   529  
   530  func (cl *Client) eachDhtServer(f func(DhtServer)) {
   531  	for _, ds := range cl.dhtServers {
   532  		f(ds)
   533  	}
   534  }
   535  
   536  // Stops the client. All connections to peers are closed and all activity will come to a halt.
   537  func (cl *Client) Close() (errs []error) {
   538  	// Close atomically, allow systems to break early if we're contended on the Client lock.
   539  	cl.closed.Set()
   540  	cl.webseedRequestTimer.Stop()
   541  	var closeGroup sync.WaitGroup // For concurrent cleanup to complete before returning
   542  	cl.lock()
   543  	for t := range cl.torrents {
   544  		cl.dropTorrent(t, &closeGroup)
   545  	}
   546  	// Can we not modify cl.torrents as we delete from it?
   547  	panicif.NotZero(len(cl.torrents))
   548  	panicif.NotZero(len(cl.torrentsByShortHash))
   549  	cl.clearPortMappings()
   550  	for i := range cl.onClose {
   551  		cl.onClose[len(cl.onClose)-1-i]()
   552  	}
   553  	cl.unlock()
   554  	cl.event.Broadcast()
   555  	closeGroup.Wait() // defer is LIFO. We want to Wait() after cl.unlock()
   556  	return
   557  }
   558  
   559  func (cl *Client) ipBlockRange(ip net.IP) (r iplist.Range, blocked bool) {
   560  	if cl.ipBlockList == nil {
   561  		return
   562  	}
   563  	return cl.ipBlockList.Lookup(ip)
   564  }
   565  
   566  func (cl *Client) ipIsBlocked(ip net.IP) bool {
   567  	_, blocked := cl.ipBlockRange(ip)
   568  	return blocked
   569  }
   570  
   571  func (cl *Client) wantConns() bool {
   572  	if cl.config.AlwaysWantConns {
   573  		return true
   574  	}
   575  	for t := range cl.torrents {
   576  		if t.wantIncomingConns() {
   577  			return true
   578  		}
   579  	}
   580  	return false
   581  }
   582  
   583  // TODO: Apply filters for non-standard networks, particularly rate-limiting.
   584  func (cl *Client) rejectAccepted(conn net.Conn) error {
   585  	if !cl.wantConns() {
   586  		return errors.New("don't want conns right now")
   587  	}
   588  	ra := conn.RemoteAddr()
   589  	if rip := addrIpOrNil(ra); rip != nil {
   590  		if cl.config.DisableIPv4Peers && rip.To4() != nil {
   591  			return errors.New("ipv4 peers disabled")
   592  		}
   593  		if cl.config.DisableIPv4 && len(rip) == net.IPv4len {
   594  			return errors.New("ipv4 disabled")
   595  		}
   596  		if cl.config.DisableIPv6 && len(rip) == net.IPv6len && rip.To4() == nil {
   597  			return errors.New("ipv6 disabled")
   598  		}
   599  		if cl.rateLimitAccept(rip) {
   600  			return errors.New("source IP accepted rate limited")
   601  		}
   602  		if cl.badPeerIPPort(rip, missinggo.AddrPort(ra)) {
   603  			return errors.New("bad source addr")
   604  		}
   605  	}
   606  	return nil
   607  }
   608  
   609  func (cl *Client) acceptConnections(l Listener) {
   610  	for {
   611  		conn, err := l.Accept()
   612  		torrent.Add("client listener accepts", 1)
   613  		if err == nil {
   614  			holepunchAddr, holepunchErr := addrPortFromPeerRemoteAddr(conn.RemoteAddr())
   615  			if holepunchErr == nil {
   616  				cl.lock()
   617  				if g.MapContains(cl.undialableWithoutHolepunch, holepunchAddr) {
   618  					setAdd(&cl.accepted, holepunchAddr)
   619  				}
   620  				if g.MapContains(
   621  					cl.undialableWithoutHolepunchDialedAfterHolepunchConnect,
   622  					holepunchAddr,
   623  				) {
   624  					setAdd(&cl.probablyOnlyConnectedDueToHolepunch, holepunchAddr)
   625  				}
   626  				cl.unlock()
   627  			}
   628  		}
   629  		conn = pproffd.WrapNetConn(conn)
   630  		cl.rLock()
   631  		closed := cl.closed.IsSet()
   632  		var reject error
   633  		if !closed && conn != nil {
   634  			reject = cl.rejectAccepted(conn)
   635  		}
   636  		cl.rUnlock()
   637  		if closed {
   638  			if conn != nil {
   639  				conn.Close()
   640  			}
   641  			return
   642  		}
   643  		if err != nil {
   644  			log.Fmsg("error accepting connection: %s", err).LogLevel(log.Debug, cl.logger)
   645  			continue
   646  		}
   647  		go func() {
   648  			if reject != nil {
   649  				torrent.Add("rejected accepted connections", 1)
   650  				cl.logger.LazyLog(log.Debug, func() log.Msg {
   651  					return log.Fmsg("rejecting accepted conn: %v", reject)
   652  				})
   653  				conn.Close()
   654  			} else {
   655  				go cl.incomingConnection(conn)
   656  			}
   657  			cl.logger.LazyLog(log.Debug, func() log.Msg {
   658  				return log.Fmsg("accepted %q connection at %q from %q",
   659  					l.Addr().Network(),
   660  					conn.LocalAddr(),
   661  					conn.RemoteAddr(),
   662  				)
   663  			})
   664  			torrent.Add(fmt.Sprintf("accepted conn remote IP len=%d", len(addrIpOrNil(conn.RemoteAddr()))), 1)
   665  			torrent.Add(fmt.Sprintf("accepted conn network=%s", conn.RemoteAddr().Network()), 1)
   666  			torrent.Add(fmt.Sprintf("accepted on %s listener", l.Addr().Network()), 1)
   667  		}()
   668  	}
   669  }
   670  
   671  // Creates the PeerConn.connString for a regular net.Conn PeerConn.
   672  func regularNetConnPeerConnConnString(nc net.Conn) string {
   673  	return fmt.Sprintf("%s-%s", nc.LocalAddr(), nc.RemoteAddr())
   674  }
   675  
   676  func (cl *Client) incomingConnection(nc net.Conn) {
   677  	defer nc.Close()
   678  	if tc, ok := nc.(*net.TCPConn); ok {
   679  		tc.SetLinger(0)
   680  	}
   681  	remoteAddr, _ := tryIpPortFromNetAddr(nc.RemoteAddr())
   682  	c := cl.newConnection(
   683  		nc,
   684  		newConnectionOpts{
   685  			outgoing:        false,
   686  			remoteAddr:      nc.RemoteAddr(),
   687  			localPublicAddr: cl.publicAddr(remoteAddr.IP),
   688  			network:         nc.RemoteAddr().Network(),
   689  			connString:      regularNetConnPeerConnConnString(nc),
   690  		})
   691  	c.Discovery = PeerSourceIncoming
   692  	cl.runReceivedConn(c)
   693  
   694  	cl.lock()
   695  	c.close()
   696  	cl.unlock()
   697  }
   698  
   699  // Returns a handle to the given torrent, if it's present in the client.
   700  func (cl *Client) Torrent(ih metainfo.Hash) (t *Torrent, ok bool) {
   701  	cl.rLock()
   702  	defer cl.rUnlock()
   703  	t, ok = cl.torrentsByShortHash[ih]
   704  	return
   705  }
   706  
   707  type DialResult struct {
   708  	Conn   net.Conn
   709  	Dialer Dialer
   710  }
   711  
   712  func countDialResult(err error) {
   713  	if err == nil {
   714  		torrent.Add("successful dials", 1)
   715  	} else {
   716  		torrent.Add("unsuccessful dials", 1)
   717  	}
   718  }
   719  
   720  func reducedDialTimeout(minDialTimeout, max time.Duration, halfOpenLimit, pendingPeers int) (ret time.Duration) {
   721  	ret = max / time.Duration((pendingPeers+halfOpenLimit)/halfOpenLimit)
   722  	if ret < minDialTimeout {
   723  		ret = minDialTimeout
   724  	}
   725  	return
   726  }
   727  
   728  // Returns whether an address is known to connect to a client with our own ID.
   729  func (cl *Client) dopplegangerAddr(addr string) bool {
   730  	_, ok := cl.dopplegangerAddrs[addr]
   731  	return ok
   732  }
   733  
   734  // Returns a connection over UTP or TCP, whichever is first to connect.
   735  func DialFirst(ctx context.Context, addr string, dialers []Dialer) (res DialResult) {
   736  	pool := dialPool{
   737  		addr: addr,
   738  	}
   739  	defer pool.startDrainer()
   740  	for _, _s := range dialers {
   741  		pool.add(ctx, _s)
   742  	}
   743  	return pool.getFirst()
   744  }
   745  
   746  func dialFromSocket(ctx context.Context, s Dialer, addr string) net.Conn {
   747  	c, err := s.Dial(ctx, addr)
   748  	if err != nil {
   749  		log.ContextLogger(ctx).Levelf(log.Debug, "error dialing %q: %v", addr, err)
   750  	}
   751  	// This is a bit optimistic, but it looks non-trivial to thread this through the proxy code. Set
   752  	// it now in case we close the connection forthwith. Note this is also done in the TCP dialer
   753  	// code to increase the chance it's done.
   754  	if tc, ok := c.(*net.TCPConn); ok {
   755  		tc.SetLinger(0)
   756  	}
   757  	countDialResult(err)
   758  	return c
   759  }
   760  
   761  func (cl *Client) noLongerHalfOpen(t *Torrent, addr string, attemptKey outgoingConnAttemptKey) {
   762  	path := t.getHalfOpenPath(addr, attemptKey)
   763  	if !path.Exists() {
   764  		panic("should exist")
   765  	}
   766  	path.Delete()
   767  	cl.numHalfOpen--
   768  	if cl.numHalfOpen < 0 {
   769  		panic("should not be possible")
   770  	}
   771  	for t := range cl.torrents {
   772  		t.openNewConns()
   773  	}
   774  }
   775  
   776  // Performs initiator handshakes and returns a connection. Returns nil *PeerConn if no connection
   777  // for valid reasons.
   778  func (cl *Client) initiateProtocolHandshakes(
   779  	ctx context.Context,
   780  	nc net.Conn,
   781  	t *Torrent,
   782  	encryptHeader bool,
   783  	newConnOpts newConnectionOpts,
   784  ) (
   785  	c *PeerConn, err error,
   786  ) {
   787  	c = cl.newConnection(nc, newConnOpts)
   788  	c.headerEncrypted = encryptHeader
   789  	ctx, cancel := context.WithTimeout(ctx, cl.config.HandshakesTimeout)
   790  	defer cancel()
   791  	dl, ok := ctx.Deadline()
   792  	if !ok {
   793  		panic(ctx)
   794  	}
   795  	err = nc.SetDeadline(dl)
   796  	if err != nil {
   797  		panic(err)
   798  	}
   799  	err = cl.initiateHandshakes(ctx, c, t)
   800  	return
   801  }
   802  
   803  func doProtocolHandshakeOnDialResult(
   804  	t *Torrent,
   805  	obfuscatedHeader bool,
   806  	addr PeerRemoteAddr,
   807  	dr DialResult,
   808  ) (
   809  	c *PeerConn, err error,
   810  ) {
   811  	cl := t.cl
   812  	nc := dr.Conn
   813  	addrIpPort, _ := tryIpPortFromNetAddr(addr)
   814  
   815  	c, err = cl.initiateProtocolHandshakes(
   816  		context.Background(), nc, t, obfuscatedHeader,
   817  		newConnectionOpts{
   818  			outgoing:   true,
   819  			remoteAddr: addr,
   820  			// It would be possible to retrieve a public IP from the dialer used here?
   821  			localPublicAddr: cl.publicAddr(addrIpPort.IP),
   822  			network:         dr.Dialer.DialerNetwork(),
   823  			connString:      regularNetConnPeerConnConnString(nc),
   824  		})
   825  	if err != nil {
   826  		nc.Close()
   827  	}
   828  	return c, err
   829  }
   830  
   831  // Returns nil connection and nil error if no connection could be established for valid reasons.
   832  func (cl *Client) dialAndCompleteHandshake(opts outgoingConnOpts) (c *PeerConn, err error) {
   833  	// It would be better if dial rate limiting could be tested when considering to open connections
   834  	// instead. Doing it here means if the limit is low, and the half-open limit is high, we could
   835  	// end up with lots of outgoing connection attempts pending that were initiated on stale data.
   836  	{
   837  		dialReservation := cl.config.DialRateLimiter.Reserve()
   838  		if !opts.receivedHolepunchConnect {
   839  			if !dialReservation.OK() {
   840  				err = errors.New("can't make dial limit reservation")
   841  				return
   842  			}
   843  			time.Sleep(dialReservation.Delay())
   844  		}
   845  	}
   846  	torrent.Add("establish outgoing connection", 1)
   847  	addr := opts.peerInfo.Addr
   848  	dialPool := dialPool{
   849  		resCh: make(chan DialResult),
   850  		addr:  addr.String(),
   851  	}
   852  	defer dialPool.startDrainer()
   853  	dialTimeout := opts.t.getDialTimeoutUnlocked()
   854  	{
   855  		ctx, cancel := context.WithTimeout(context.Background(), dialTimeout)
   856  		defer cancel()
   857  		for _, d := range cl.dialers {
   858  			dialPool.add(ctx, d)
   859  		}
   860  	}
   861  	holepunchAddr, holepunchAddrErr := addrPortFromPeerRemoteAddr(addr)
   862  	headerObfuscationPolicy := opts.HeaderObfuscationPolicy
   863  	obfuscatedHeaderFirst := headerObfuscationPolicy.Preferred
   864  	firstDialResult := dialPool.getFirst()
   865  	if firstDialResult.Conn == nil {
   866  		// No dialers worked. Try to initiate a holepunching rendezvous.
   867  		if holepunchAddrErr == nil {
   868  			cl.lock()
   869  			if !opts.receivedHolepunchConnect {
   870  				g.MakeMapIfNilAndSet(&cl.undialableWithoutHolepunch, holepunchAddr, struct{}{})
   871  			}
   872  			if !opts.skipHolepunchRendezvous {
   873  				opts.t.trySendHolepunchRendezvous(holepunchAddr)
   874  			}
   875  			cl.unlock()
   876  		}
   877  		err = fmt.Errorf("all initial dials failed")
   878  		return
   879  	}
   880  	if opts.receivedHolepunchConnect && holepunchAddrErr == nil {
   881  		cl.lock()
   882  		if g.MapContains(cl.undialableWithoutHolepunch, holepunchAddr) {
   883  			g.MakeMapIfNilAndSet(&cl.dialableOnlyAfterHolepunch, holepunchAddr, struct{}{})
   884  		}
   885  		g.MakeMapIfNil(&cl.dialedSuccessfullyAfterHolepunchConnect)
   886  		g.MapInsert(cl.dialedSuccessfullyAfterHolepunchConnect, holepunchAddr, struct{}{})
   887  		cl.unlock()
   888  	}
   889  	c, err = doProtocolHandshakeOnDialResult(
   890  		opts.t,
   891  		obfuscatedHeaderFirst,
   892  		addr,
   893  		firstDialResult,
   894  	)
   895  	if err == nil {
   896  		torrent.Add("initiated conn with preferred header obfuscation", 1)
   897  		return
   898  	}
   899  	c.logger.Levelf(
   900  		log.Debug,
   901  		"error doing protocol handshake with header obfuscation %v",
   902  		obfuscatedHeaderFirst,
   903  	)
   904  	firstDialResult.Conn.Close()
   905  	// We should have just tried with the preferred header obfuscation. If it was required, there's nothing else to try.
   906  	if headerObfuscationPolicy.RequirePreferred {
   907  		return
   908  	}
   909  	// Reuse the dialer that returned already but failed to handshake.
   910  	{
   911  		ctx, cancel := context.WithTimeout(context.Background(), dialTimeout)
   912  		defer cancel()
   913  		dialPool.add(ctx, firstDialResult.Dialer)
   914  	}
   915  	secondDialResult := dialPool.getFirst()
   916  	if secondDialResult.Conn == nil {
   917  		return
   918  	}
   919  	c, err = doProtocolHandshakeOnDialResult(
   920  		opts.t,
   921  		!obfuscatedHeaderFirst,
   922  		addr,
   923  		secondDialResult,
   924  	)
   925  	if err == nil {
   926  		torrent.Add("initiated conn with fallback header obfuscation", 1)
   927  		return
   928  	}
   929  	c.logger.Levelf(
   930  		log.Debug,
   931  		"error doing protocol handshake with header obfuscation %v",
   932  		!obfuscatedHeaderFirst,
   933  	)
   934  	secondDialResult.Conn.Close()
   935  	return
   936  }
   937  
   938  type outgoingConnOpts struct {
   939  	peerInfo PeerInfo
   940  	t        *Torrent
   941  	// Don't attempt to connect unless a connect message is received after initiating a rendezvous.
   942  	requireRendezvous bool
   943  	// Don't send rendezvous requests to eligible relays.
   944  	skipHolepunchRendezvous bool
   945  	// Outgoing connection attempt is in response to holepunch connect message.
   946  	receivedHolepunchConnect bool
   947  	HeaderObfuscationPolicy  HeaderObfuscationPolicy
   948  }
   949  
   950  // Called to dial out and run a connection. The addr we're given is already
   951  // considered half-open.
   952  func (cl *Client) outgoingConnection(
   953  	opts outgoingConnOpts,
   954  	attemptKey outgoingConnAttemptKey,
   955  ) {
   956  	c, err := cl.dialAndCompleteHandshake(opts)
   957  	if err == nil {
   958  		c.conn.SetWriteDeadline(time.Time{})
   959  	}
   960  	cl.lock()
   961  	defer cl.unlock()
   962  	// Don't release lock between here and addPeerConn, unless it's for failure.
   963  	cl.noLongerHalfOpen(opts.t, opts.peerInfo.Addr.String(), attemptKey)
   964  	if err != nil {
   965  		if cl.config.Debug {
   966  			cl.logger.Levelf(
   967  				log.Debug,
   968  				"error establishing outgoing connection to %v: %v",
   969  				opts.peerInfo.Addr,
   970  				err,
   971  			)
   972  		}
   973  		return
   974  	}
   975  	defer c.close()
   976  	c.Discovery = opts.peerInfo.Source
   977  	c.trusted = opts.peerInfo.Trusted
   978  	opts.t.runHandshookConnLoggingErr(c)
   979  }
   980  
   981  // The port number for incoming peer connections. 0 if the client isn't listening.
   982  func (cl *Client) incomingPeerPort() int {
   983  	return cl.LocalPort()
   984  }
   985  
   986  func (cl *Client) initiateHandshakes(ctx context.Context, c *PeerConn, t *Torrent) (err error) {
   987  	if c.headerEncrypted {
   988  		var rw io.ReadWriter
   989  		rw, c.cryptoMethod, err = mse.InitiateHandshakeContext(
   990  			ctx,
   991  			struct {
   992  				io.Reader
   993  				io.Writer
   994  			}{c.r, c.w},
   995  			t.canonicalShortInfohash().Bytes(),
   996  			nil,
   997  			cl.config.CryptoProvides,
   998  		)
   999  		c.setRW(rw)
  1000  		if err != nil {
  1001  			return fmt.Errorf("header obfuscation handshake: %w", err)
  1002  		}
  1003  	}
  1004  	localReservedBits := cl.config.Extensions
  1005  	handshakeIh := *t.canonicalShortInfohash()
  1006  	// If we're sending the v1 infohash, and we know the v2 infohash, set the v2 upgrade bit. This
  1007  	// means the peer can send the v2 infohash in the handshake to upgrade the connection.
  1008  	localReservedBits.SetBit(pp.ExtensionBitV2Upgrade, g.Some(handshakeIh) == t.infoHash && t.infoHashV2.Ok)
  1009  	ih, err := cl.connBtHandshake(context.TODO(), c, &handshakeIh, localReservedBits)
  1010  	if err != nil {
  1011  		return fmt.Errorf("bittorrent protocol handshake: %w", err)
  1012  	}
  1013  	if g.Some(ih) == t.infoHash {
  1014  		return nil
  1015  	}
  1016  	if t.infoHashV2.Ok && *t.infoHashV2.Value.ToShort() == ih {
  1017  		torrent.Add("initiated handshakes upgraded to v2", 1)
  1018  		c.v2 = true
  1019  		return nil
  1020  	}
  1021  	err = errors.New("bittorrent protocol handshake: peer infohash didn't match")
  1022  	return
  1023  }
  1024  
  1025  // Calls f with any secret keys. Note that it takes the Client lock, and so must be used from code
  1026  // that won't also try to take the lock. This saves us copying all the infohashes everytime.
  1027  func (cl *Client) forSkeys(f func([]byte) bool) {
  1028  	cl.rLock()
  1029  	defer cl.rUnlock()
  1030  	if false { // Emulate the bug from #114
  1031  		var firstIh InfoHash
  1032  		for ih := range cl.torrentsByShortHash {
  1033  			firstIh = ih
  1034  			break
  1035  		}
  1036  		for range cl.torrentsByShortHash {
  1037  			if !f(firstIh[:]) {
  1038  				break
  1039  			}
  1040  		}
  1041  		return
  1042  	}
  1043  	for ih := range cl.torrentsByShortHash {
  1044  		if !f(ih[:]) {
  1045  			break
  1046  		}
  1047  	}
  1048  }
  1049  
  1050  func (cl *Client) handshakeReceiverSecretKeys() mse.SecretKeyIter {
  1051  	if ret := cl.config.Callbacks.ReceiveEncryptedHandshakeSkeys; ret != nil {
  1052  		return ret
  1053  	}
  1054  	return cl.forSkeys
  1055  }
  1056  
  1057  // Do encryption and bittorrent handshakes as receiver.
  1058  func (cl *Client) receiveHandshakes(c *PeerConn) (t *Torrent, err error) {
  1059  	var rw io.ReadWriter
  1060  	rw, c.headerEncrypted, c.cryptoMethod, err = handleEncryption(
  1061  		c.rw(),
  1062  		cl.handshakeReceiverSecretKeys(),
  1063  		cl.config.HeaderObfuscationPolicy,
  1064  		cl.config.CryptoSelector,
  1065  	)
  1066  	c.setRW(rw)
  1067  	if err == nil || err == mse.ErrNoSecretKeyMatch {
  1068  		if c.headerEncrypted {
  1069  			torrent.Add("handshakes received encrypted", 1)
  1070  		} else {
  1071  			torrent.Add("handshakes received unencrypted", 1)
  1072  		}
  1073  	} else {
  1074  		torrent.Add("handshakes received with error while handling encryption", 1)
  1075  	}
  1076  	if err != nil {
  1077  		if err == mse.ErrNoSecretKeyMatch {
  1078  			err = nil
  1079  		}
  1080  		return
  1081  	}
  1082  	if cl.config.HeaderObfuscationPolicy.RequirePreferred && c.headerEncrypted != cl.config.HeaderObfuscationPolicy.Preferred {
  1083  		err = errors.New("connection does not have required header obfuscation")
  1084  		return
  1085  	}
  1086  	ih, err := cl.connBtHandshake(context.TODO(), c, nil, cl.config.Extensions)
  1087  	if err != nil {
  1088  		return nil, fmt.Errorf("during bt handshake: %w", err)
  1089  	}
  1090  
  1091  	cl.lock()
  1092  	t = cl.torrentsByShortHash[ih]
  1093  	if t != nil && t.infoHashV2.Ok && *t.infoHashV2.Value.ToShort() == ih {
  1094  		torrent.Add("v2 handshakes received", 1)
  1095  		c.v2 = true
  1096  	}
  1097  	cl.unlock()
  1098  
  1099  	return
  1100  }
  1101  
  1102  var successfulPeerWireProtocolHandshakePeerReservedBytes expvar.Map
  1103  
  1104  func init() {
  1105  	torrent.Set(
  1106  		"successful_peer_wire_protocol_handshake_peer_reserved_bytes",
  1107  		&successfulPeerWireProtocolHandshakePeerReservedBytes)
  1108  }
  1109  
  1110  func (cl *Client) connBtHandshake(ctx context.Context, c *PeerConn, ih *metainfo.Hash, reservedBits PeerExtensionBits) (ret metainfo.Hash, err error) {
  1111  	res, err := pp.Handshake(ctx, c.rw(), ih, cl.peerID, reservedBits)
  1112  	if err != nil {
  1113  		return
  1114  	}
  1115  	successfulPeerWireProtocolHandshakePeerReservedBytes.Add(
  1116  		hex.EncodeToString(res.PeerExtensionBits[:]), 1)
  1117  	ret = res.Hash
  1118  	c.PeerExtensionBytes = res.PeerExtensionBits
  1119  	c.PeerID = res.PeerID
  1120  	c.completedHandshake = time.Now()
  1121  	if cb := cl.config.Callbacks.CompletedHandshake; cb != nil {
  1122  		cb(c, res.Hash)
  1123  	}
  1124  	return
  1125  }
  1126  
  1127  func (cl *Client) runReceivedConn(c *PeerConn) {
  1128  	err := c.conn.SetDeadline(time.Now().Add(cl.config.HandshakesTimeout))
  1129  	if err != nil {
  1130  		panic(err)
  1131  	}
  1132  	t, err := cl.receiveHandshakes(c)
  1133  	if err != nil {
  1134  		cl.logger.LazyLog(log.Debug, func() log.Msg {
  1135  			return log.Fmsg(
  1136  				"error receiving handshakes on %v: %s", c, err,
  1137  			).Add(
  1138  				"network", c.Network,
  1139  			)
  1140  		})
  1141  		torrent.Add("error receiving handshake", 1)
  1142  		cl.lock()
  1143  		cl.onBadAccept(c.RemoteAddr)
  1144  		cl.unlock()
  1145  		return
  1146  	}
  1147  	if t == nil {
  1148  		torrent.Add("received handshake for unloaded torrent", 1)
  1149  		cl.logger.LazyLog(log.Debug, func() log.Msg {
  1150  			return log.Fmsg("received handshake for unloaded torrent")
  1151  		})
  1152  		cl.lock()
  1153  		cl.onBadAccept(c.RemoteAddr)
  1154  		cl.unlock()
  1155  		return
  1156  	}
  1157  	torrent.Add("received handshake for loaded torrent", 1)
  1158  	c.conn.SetWriteDeadline(time.Time{})
  1159  	cl.lock()
  1160  	defer cl.unlock()
  1161  	t.runHandshookConnLoggingErr(c)
  1162  }
  1163  
  1164  // Client lock must be held before entering this.
  1165  func (t *Torrent) runHandshookConn(pc *PeerConn) error {
  1166  	pc.setTorrent(t)
  1167  	cl := t.cl
  1168  	for i, b := range cl.config.MinPeerExtensions {
  1169  		if pc.PeerExtensionBytes[i]&b != b {
  1170  			return fmt.Errorf("peer did not meet minimum peer extensions: %x", pc.PeerExtensionBytes[:])
  1171  		}
  1172  	}
  1173  	if pc.PeerID == cl.peerID {
  1174  		if pc.outgoing {
  1175  			connsToSelf.Add(1)
  1176  			addr := pc.RemoteAddr.String()
  1177  			cl.dopplegangerAddrs[addr] = struct{}{}
  1178  		} /* else {
  1179  			// Because the remote address is not necessarily the same as its client's torrent listen
  1180  			// address, we won't record the remote address as a doppleganger. Instead, the initiator
  1181  			// can record *us* as the doppleganger.
  1182  		} */
  1183  		t.logger.Levelf(log.Debug, "local and remote peer ids are the same")
  1184  		return nil
  1185  	}
  1186  	pc.r = deadlineReader{pc.conn, pc.r}
  1187  	completedHandshakeConnectionFlags.Add(pc.connectionFlags(), 1)
  1188  	if connIsIpv6(pc.conn) {
  1189  		torrent.Add("completed handshake over ipv6", 1)
  1190  	}
  1191  	if err := t.addPeerConn(pc); err != nil {
  1192  		return fmt.Errorf("adding connection: %w", err)
  1193  	}
  1194  	defer t.dropConnection(pc)
  1195  	pc.addBuiltinLtepProtocols(!cl.config.DisablePEX)
  1196  	for _, cb := range pc.callbacks.PeerConnAdded {
  1197  		cb(pc)
  1198  	}
  1199  	pc.startMessageWriter()
  1200  	pc.sendInitialMessages()
  1201  	pc.initUpdateRequestsTimer()
  1202  
  1203  	for _, cb := range pc.callbacks.StatusUpdated {
  1204  		cb(StatusUpdatedEvent{
  1205  			Event:  PeerConnected,
  1206  			PeerId: pc.PeerID,
  1207  		})
  1208  	}
  1209  
  1210  	err := pc.mainReadLoop()
  1211  	if err != nil {
  1212  		for _, cb := range pc.callbacks.StatusUpdated {
  1213  			cb(StatusUpdatedEvent{
  1214  				Event:  PeerDisconnected,
  1215  				Error:  err,
  1216  				PeerId: pc.PeerID,
  1217  			})
  1218  		}
  1219  		return fmt.Errorf("main read loop: %w", err)
  1220  	}
  1221  	return nil
  1222  }
  1223  
  1224  func (p *PeerConn) initUpdateRequestsTimer() {
  1225  	if check.Enabled {
  1226  		if p.updateRequestsTimer != nil {
  1227  			panic(p.updateRequestsTimer)
  1228  		}
  1229  	}
  1230  	if enableUpdateRequestsTimer {
  1231  		p.updateRequestsTimer = time.AfterFunc(math.MaxInt64, p.updateRequestsTimerFunc)
  1232  	}
  1233  }
  1234  
  1235  const peerUpdateRequestsTimerReason = "updateRequestsTimer"
  1236  
  1237  func (c *PeerConn) updateRequestsTimerFunc() {
  1238  	c.locker().Lock()
  1239  	defer c.locker().Unlock()
  1240  	if c.closed.IsSet() {
  1241  		return
  1242  	}
  1243  	if c.isLowOnRequests() {
  1244  		// If there are no outstanding requests, then a request update should have already run.
  1245  		return
  1246  	}
  1247  	if d := time.Since(c.lastRequestUpdate); d < updateRequestsTimerDuration {
  1248  		// These should be benign, Timer.Stop doesn't guarantee that its function won't run if it's
  1249  		// already been fired.
  1250  		torrent.Add("spurious timer requests updates", 1)
  1251  		return
  1252  	}
  1253  	c.onNeedUpdateRequests(peerUpdateRequestsTimerReason)
  1254  }
  1255  
  1256  // Maximum pending requests we allow peers to send us. If peer requests are buffered on read, this
  1257  // instructs the amount of memory that might be used to cache pending writes. Assuming 512KiB
  1258  // (1<<19) cached for sending, for 16KiB (1<<14) chunks.
  1259  const localClientReqq = 1024
  1260  
  1261  // See the order given in Transmission's tr_peerMsgsNew.
  1262  func (pc *PeerConn) sendInitialMessages() {
  1263  	t := pc.t
  1264  	cl := t.cl
  1265  	if pc.PeerExtensionBytes.SupportsExtended() && cl.config.Extensions.SupportsExtended() {
  1266  		pc.write(pp.Message{
  1267  			Type:       pp.Extended,
  1268  			ExtendedID: pp.HandshakeExtendedID,
  1269  			ExtendedPayload: func() []byte {
  1270  				msg := pp.ExtendedHandshakeMessage{
  1271  					V:            cl.config.ExtendedHandshakeClientVersion,
  1272  					Reqq:         localClientReqq,
  1273  					YourIp:       pp.CompactIp(pc.remoteIp()),
  1274  					Encryption:   cl.config.HeaderObfuscationPolicy.Preferred || !cl.config.HeaderObfuscationPolicy.RequirePreferred,
  1275  					Port:         cl.incomingPeerPort(),
  1276  					MetadataSize: t.metadataSize(),
  1277  					// TODO: We can figure these out specific to the socket used.
  1278  					Ipv4: pp.CompactIp(cl.config.PublicIp4.To4()),
  1279  					Ipv6: cl.config.PublicIp6.To16(),
  1280  				}
  1281  				msg.M = pc.LocalLtepProtocolMap.toSupportedExtensionDict()
  1282  				return bencode.MustMarshal(msg)
  1283  			}(),
  1284  		})
  1285  	}
  1286  	func() {
  1287  		if pc.fastEnabled() {
  1288  			if t.haveAllPieces() {
  1289  				pc.write(pp.Message{Type: pp.HaveAll})
  1290  				pc.sentHaves.AddRange(0, bitmap.BitRange(pc.t.NumPieces()))
  1291  				return
  1292  			} else if !t.haveAnyPieces() {
  1293  				pc.write(pp.Message{Type: pp.HaveNone})
  1294  				pc.sentHaves.Clear()
  1295  				return
  1296  			}
  1297  		}
  1298  		pc.postBitfield()
  1299  	}()
  1300  	if pc.PeerExtensionBytes.SupportsDHT() && cl.config.Extensions.SupportsDHT() && cl.haveDhtServer() {
  1301  		pc.write(pp.Message{
  1302  			Type: pp.Port,
  1303  			Port: cl.dhtPort(),
  1304  		})
  1305  	}
  1306  }
  1307  
  1308  func (cl *Client) dhtPort() (ret uint16) {
  1309  	if len(cl.dhtServers) == 0 {
  1310  		return
  1311  	}
  1312  	return uint16(missinggo.AddrPort(cl.dhtServers[len(cl.dhtServers)-1].Addr()))
  1313  }
  1314  
  1315  func (cl *Client) haveDhtServer() bool {
  1316  	return len(cl.dhtServers) > 0
  1317  }
  1318  
  1319  // Process incoming ut_metadata message.
  1320  func (cl *Client) gotMetadataExtensionMsg(payload []byte, t *Torrent, c *PeerConn) error {
  1321  	var d pp.ExtendedMetadataRequestMsg
  1322  	err := bencode.Unmarshal(payload, &d)
  1323  	if _, ok := err.(bencode.ErrUnusedTrailingBytes); ok {
  1324  	} else if err != nil {
  1325  		return fmt.Errorf("error unmarshalling bencode: %s", err)
  1326  	}
  1327  	piece := d.Piece
  1328  	switch d.Type {
  1329  	case pp.DataMetadataExtensionMsgType:
  1330  		c.modifyRelevantConnStats(add(1, func(cs *ConnStats) *Count { return &cs.MetadataChunksRead }))
  1331  		if !c.requestedMetadataPiece(piece) {
  1332  			return fmt.Errorf("got unexpected piece %d", piece)
  1333  		}
  1334  		c.metadataRequests[piece] = false
  1335  		begin := len(payload) - d.PieceSize()
  1336  		if begin < 0 || begin >= len(payload) {
  1337  			return fmt.Errorf("data has bad offset in payload: %d", begin)
  1338  		}
  1339  		t.saveMetadataPiece(piece, payload[begin:])
  1340  		c.lastUsefulChunkReceived = time.Now()
  1341  		err = t.maybeCompleteMetadata()
  1342  		if err != nil {
  1343  			// Log this at the Torrent-level, as we don't partition metadata by Peer yet, so we
  1344  			// don't know who to blame. TODO: Also errors can be returned here that aren't related
  1345  			// to verifying metadata, which should be fixed. This should be tagged with metadata, so
  1346  			// log consumers can filter for this message.
  1347  			t.logger.WithDefaultLevel(log.Warning).Printf("error completing metadata: %v", err)
  1348  		}
  1349  		return err
  1350  	case pp.RequestMetadataExtensionMsgType:
  1351  		if !t.haveMetadataPiece(piece) {
  1352  			c.protocolLogger.WithDefaultLevel(log.Debug).Printf("rejecting metadata piece %d", piece)
  1353  			c.write(t.newMetadataExtensionMessage(c, pp.RejectMetadataExtensionMsgType, d.Piece, nil))
  1354  			return nil
  1355  		}
  1356  		start := (1 << 14) * piece
  1357  		c.protocolLogger.WithDefaultLevel(log.Debug).Printf("sending metadata piece %d", piece)
  1358  		c.write(t.newMetadataExtensionMessage(c, pp.DataMetadataExtensionMsgType, piece, t.metadataBytes[start:start+t.metadataPieceSize(piece)]))
  1359  		return nil
  1360  	case pp.RejectMetadataExtensionMsgType:
  1361  		return nil
  1362  	default:
  1363  		return errors.New("unknown msg_type value")
  1364  	}
  1365  }
  1366  
  1367  func (cl *Client) badPeerAddr(addr PeerRemoteAddr) bool {
  1368  	if ipa, ok := tryIpPortFromNetAddr(addr); ok {
  1369  		return cl.badPeerIPPort(ipa.IP, ipa.Port)
  1370  	}
  1371  	return false
  1372  }
  1373  
  1374  // Returns whether the IP address and port are considered "bad".
  1375  func (cl *Client) badPeerIPPort(ip net.IP, port int) bool {
  1376  	if port == 0 || ip == nil {
  1377  		return true
  1378  	}
  1379  	if cl.dopplegangerAddr(net.JoinHostPort(ip.String(), strconv.FormatInt(int64(port), 10))) {
  1380  		return true
  1381  	}
  1382  	if _, ok := cl.ipBlockRange(ip); ok {
  1383  		return true
  1384  	}
  1385  	ipAddr, ok := netip.AddrFromSlice(ip)
  1386  	if !ok {
  1387  		panic(ip)
  1388  	}
  1389  	if _, ok := cl.badPeerIPs[ipAddr]; ok {
  1390  		return true
  1391  	}
  1392  	return false
  1393  }
  1394  
  1395  // Return a Torrent ready for insertion into a Client.
  1396  func (cl *Client) newTorrent(ih metainfo.Hash, specStorage storage.ClientImpl) (t *Torrent) {
  1397  	return cl.newTorrentOpt(AddTorrentOpts{
  1398  		InfoHash: ih,
  1399  		Storage:  specStorage,
  1400  	})
  1401  }
  1402  
  1403  // Return a Torrent ready for insertion into a Client. This is also the method to call to create
  1404  // Torrents for testing.
  1405  func (cl *Client) newTorrentOpt(opts AddTorrentOpts) (t *Torrent) {
  1406  	var v1InfoHash g.Option[infohash.T]
  1407  	if !opts.InfoHash.IsZero() {
  1408  		v1InfoHash.Set(opts.InfoHash)
  1409  	}
  1410  	if !v1InfoHash.Ok && !opts.InfoHashV2.Ok {
  1411  		panic("v1 infohash must be nonzero or v2 infohash must be set")
  1412  	}
  1413  	// use provided storage, if provided
  1414  	storageClient := cl.defaultStorage
  1415  	if opts.Storage != nil {
  1416  		storageClient = storage.NewClient(opts.Storage)
  1417  	}
  1418  
  1419  	t = &Torrent{
  1420  		cl:         cl,
  1421  		infoHash:   v1InfoHash,
  1422  		infoHashV2: opts.InfoHashV2,
  1423  		peers: prioritizedPeers{
  1424  			om: gbtree.New(32),
  1425  			getPrio: func(p PeerInfo) peerPriority {
  1426  				ipPort := p.addr()
  1427  				return bep40PriorityIgnoreError(cl.publicAddr(ipPort.IP), ipPort)
  1428  			},
  1429  		},
  1430  		conns: make(map[*PeerConn]struct{}, 2*cl.config.EstablishedConnsPerTorrent),
  1431  
  1432  		storageOpener:       storageClient,
  1433  		maxEstablishedConns: cl.config.EstablishedConnsPerTorrent,
  1434  
  1435  		metadataChanged: sync.Cond{
  1436  			L: cl.locker(),
  1437  		},
  1438  		gotMetainfoC: make(chan struct{}),
  1439  
  1440  		ignoreUnverifiedPieceCompletion: opts.IgnoreUnverifiedPieceCompletion,
  1441  		initialPieceCheckDisabled:       opts.DisableInitialPieceCheck,
  1442  	}
  1443  	g.MakeMap(&t.webSeeds)
  1444  	t.closedCtx, t.closedCtxCancel = context.WithCancelCause(context.Background())
  1445  	t.getInfoCtx, t.getInfoCtxCancel = context.WithCancelCause(t.closedCtx)
  1446  	var salt [8]byte
  1447  	rand.Read(salt[:])
  1448  	t.smartBanCache.Hash = func(b []byte) uint64 {
  1449  		h := xxhash.New()
  1450  		h.Write(salt[:])
  1451  		h.Write(b)
  1452  		return h.Sum64()
  1453  	}
  1454  	t.smartBanCache.Init()
  1455  	t.networkingEnabled.Set()
  1456  	ihHex := t.InfoHash().HexString()
  1457  	t.logger = cl.logger.WithDefaultLevel(log.Debug).WithNames(ihHex).WithContextText(ihHex)
  1458  	t.name()
  1459  	t.baseSlogger = cl.slogger
  1460  	if opts.ChunkSize == 0 {
  1461  		opts.ChunkSize = defaultChunkSize
  1462  	}
  1463  	t.setChunkSize(opts.ChunkSize)
  1464  	cl.torrents[t] = struct{}{}
  1465  	return
  1466  }
  1467  
  1468  // A file-like handle to some torrent data resource.
  1469  type Handle interface {
  1470  	io.Reader
  1471  	io.Seeker
  1472  	io.Closer
  1473  	io.ReaderAt
  1474  }
  1475  
  1476  func (cl *Client) AddTorrentInfoHash(infoHash metainfo.Hash) (t *Torrent, new bool) {
  1477  	return cl.AddTorrentInfoHashWithStorage(infoHash, nil)
  1478  }
  1479  
  1480  // Deprecated. Adds a torrent by InfoHash with a custom Storage implementation.
  1481  // If the torrent already exists then this Storage is ignored and the
  1482  // existing torrent returned with `new` set to `false`
  1483  func (cl *Client) AddTorrentInfoHashWithStorage(
  1484  	infoHash metainfo.Hash,
  1485  	specStorage storage.ClientImpl,
  1486  ) (t *Torrent, new bool) {
  1487  	cl.lock()
  1488  	defer cl.unlock()
  1489  	t, ok := cl.torrentsByShortHash[infoHash]
  1490  	if ok {
  1491  		return
  1492  	}
  1493  	new = true
  1494  
  1495  	t = cl.newTorrent(infoHash, specStorage)
  1496  	cl.eachDhtServer(func(s DhtServer) {
  1497  		if cl.config.PeriodicallyAnnounceTorrentsToDht {
  1498  			go t.dhtAnnouncer(s)
  1499  		}
  1500  	})
  1501  	cl.torrentsByShortHash[infoHash] = t
  1502  	cl.torrents[t] = struct{}{}
  1503  	cl.clearAcceptLimits()
  1504  	t.updateWantPeersEvent()
  1505  	// Tickle Client.waitAccept, new torrent may want conns.
  1506  	cl.event.Broadcast()
  1507  	return
  1508  }
  1509  
  1510  // Adds a torrent by InfoHash with a custom Storage implementation. If the torrent already exists
  1511  // then this Storage is ignored and the existing torrent returned with `new` set to `false`.
  1512  func (cl *Client) AddTorrentOpt(opts AddTorrentOpts) (t *Torrent, new bool) {
  1513  	infoHash := opts.InfoHash
  1514  	panicif.Zero(infoHash)
  1515  	cl.lock()
  1516  	defer cl.unlock()
  1517  	t, ok := cl.torrentsByShortHash[infoHash]
  1518  	if ok {
  1519  		return
  1520  	}
  1521  	if opts.InfoHashV2.Ok {
  1522  		t, ok = cl.torrentsByShortHash[*opts.InfoHashV2.Value.ToShort()]
  1523  		if ok {
  1524  			return
  1525  		}
  1526  	}
  1527  	new = true
  1528  
  1529  	t = cl.newTorrentOpt(opts)
  1530  	cl.eachDhtServer(func(s DhtServer) {
  1531  		if cl.config.PeriodicallyAnnounceTorrentsToDht {
  1532  			go t.dhtAnnouncer(s)
  1533  		}
  1534  	})
  1535  	cl.torrentsByShortHash[infoHash] = t
  1536  	t.setInfoBytesLocked(opts.InfoBytes)
  1537  	cl.clearAcceptLimits()
  1538  	t.updateWantPeersEvent()
  1539  	// Tickle Client.waitAccept, new torrent may want conns.
  1540  	cl.event.Broadcast()
  1541  	return
  1542  }
  1543  
  1544  type AddTorrentOpts struct {
  1545  	InfoHash   infohash.T
  1546  	InfoHashV2 g.Option[infohash_v2.T]
  1547  	Storage    storage.ClientImpl
  1548  	// Only applied for new torrents (check Client.AddTorrent* method bool return value). If 0, the
  1549  	// default chunk size is used (16 KiB in current modern BitTorrent clients).
  1550  	ChunkSize pp.Integer
  1551  	InfoBytes []byte
  1552  	// Don't hash data if piece completion is missing. This is useful for very large torrents that
  1553  	// are dropped in place from an external source and trigger a lot of initial piece checks.
  1554  	DisableInitialPieceCheck bool
  1555  	// Require pieces to be checked as soon as info is available. This is because we have no way to
  1556  	// schedule an initial check only, and don't want to race against use of Torrent.Complete.
  1557  	IgnoreUnverifiedPieceCompletion bool
  1558  	// Whether to initially allow data download or upload
  1559  	DisallowDataUpload   bool
  1560  	DisallowDataDownload bool
  1561  }
  1562  
  1563  // Add or merge a torrent spec. Returns new if the torrent wasn't already in the client. See also
  1564  // Torrent.MergeSpec.
  1565  func (cl *Client) AddTorrentSpec(spec *TorrentSpec) (t *Torrent, new bool, err error) {
  1566  	t, new = cl.AddTorrentOpt(spec.AddTorrentOpts)
  1567  	modSpec := *spec
  1568  	// ChunkSize was already applied by adding a new Torrent, and MergeSpec disallows changing it.
  1569  	modSpec.ChunkSize = 0
  1570  	err = t.MergeSpec(&modSpec)
  1571  	if err != nil && new {
  1572  		t.Drop()
  1573  	}
  1574  	return
  1575  }
  1576  
  1577  // The trackers will be merged with the existing ones. If the Info isn't yet known, it will be set.
  1578  // The display name is replaced if the new spec provides one. Note that any `Storage` is ignored.
  1579  // Many fields in the AddTorrentOpts field in TorrentSpec are ignored because the Torrent is already
  1580  // added.
  1581  func (t *Torrent) MergeSpec(spec *TorrentSpec) error {
  1582  	if spec.DisplayName != "" {
  1583  		t.SetDisplayName(spec.DisplayName)
  1584  	}
  1585  	if spec.InfoBytes != nil {
  1586  		err := t.SetInfoBytes(spec.InfoBytes)
  1587  		if err != nil {
  1588  			return err
  1589  		}
  1590  	}
  1591  	cl := t.cl
  1592  	cl.AddDhtNodes(spec.DhtNodes)
  1593  	t.AddSources(spec.Sources)
  1594  	// TODO: The lock should be moved earlier.
  1595  	cl.lock()
  1596  	defer cl.unlock()
  1597  	for _, url := range spec.Webseeds {
  1598  		t.addWebSeed(url)
  1599  	}
  1600  	t.addPeersIter(func(yield func(PeerInfo) bool) {
  1601  		for _, peerAddr := range spec.PeerAddrs {
  1602  			if !yield(PeerInfo{
  1603  				Addr:    StringAddr(peerAddr),
  1604  				Source:  PeerSourceDirect,
  1605  				Trusted: true,
  1606  			}) {
  1607  				return
  1608  			}
  1609  		}
  1610  	})
  1611  	if spec.ChunkSize != 0 {
  1612  		panic("chunk size cannot be changed for existing Torrent")
  1613  	}
  1614  	t.addTrackers(spec.Trackers)
  1615  	t.maybeNewConns()
  1616  	return errors.Join(t.addPieceLayersLocked(spec.PieceLayers)...)
  1617  }
  1618  
  1619  func (cl *Client) dropTorrent(t *Torrent, wg *sync.WaitGroup) {
  1620  	t.close(wg)
  1621  }
  1622  
  1623  func (cl *Client) allTorrentsCompleted() bool {
  1624  	for t := range cl.torrents {
  1625  		if !t.haveInfo() {
  1626  			return false
  1627  		}
  1628  		if !t.haveAllPieces() {
  1629  			return false
  1630  		}
  1631  	}
  1632  	return true
  1633  }
  1634  
  1635  // Returns true when all torrents are completely downloaded and false if the
  1636  // client is stopped before that.
  1637  func (cl *Client) WaitAll() bool {
  1638  	cl.lock()
  1639  	defer cl.unlock()
  1640  	for !cl.allTorrentsCompleted() {
  1641  		if cl.closed.IsSet() {
  1642  			return false
  1643  		}
  1644  		cl.event.Wait()
  1645  	}
  1646  	return true
  1647  }
  1648  
  1649  // Returns handles to all the torrents loaded in the Client.
  1650  func (cl *Client) Torrents() []*Torrent {
  1651  	cl.rLock()
  1652  	defer cl.rUnlock()
  1653  	return cl.torrentsAsSlice()
  1654  }
  1655  
  1656  func (cl *Client) torrentsAsSlice() (ret []*Torrent) {
  1657  	ret = make([]*Torrent, 0, len(cl.torrents))
  1658  	for t := range cl.torrents {
  1659  		ret = append(ret, t)
  1660  	}
  1661  	return
  1662  }
  1663  
  1664  func (cl *Client) AddMagnet(uri string) (T *Torrent, err error) {
  1665  	spec, err := TorrentSpecFromMagnetUri(uri)
  1666  	if err != nil {
  1667  		return
  1668  	}
  1669  	T, _, err = cl.AddTorrentSpec(spec)
  1670  	return
  1671  }
  1672  
  1673  func (cl *Client) AddTorrent(mi *metainfo.MetaInfo) (T *Torrent, err error) {
  1674  	ts, err := TorrentSpecFromMetaInfoErr(mi)
  1675  	if err != nil {
  1676  		return
  1677  	}
  1678  	T, _, err = cl.AddTorrentSpec(ts)
  1679  	return
  1680  }
  1681  
  1682  func (cl *Client) AddTorrentFromFile(filename string) (T *Torrent, err error) {
  1683  	mi, err := metainfo.LoadFromFile(filename)
  1684  	if err != nil {
  1685  		return
  1686  	}
  1687  	return cl.AddTorrent(mi)
  1688  }
  1689  
  1690  func (cl *Client) DhtServers() []DhtServer {
  1691  	return cl.dhtServers
  1692  }
  1693  
  1694  func (cl *Client) AddDhtNodes(nodes []string) {
  1695  	for _, n := range nodes {
  1696  		hmp := missinggo.SplitHostMaybePort(n)
  1697  		ip := net.ParseIP(hmp.Host)
  1698  		if ip == nil {
  1699  			cl.logger.Printf("won't add DHT node with bad IP: %q", hmp.Host)
  1700  			continue
  1701  		}
  1702  		ni := krpc.NodeInfo{
  1703  			Addr: krpc.NodeAddr{
  1704  				IP:   ip,
  1705  				Port: hmp.Port,
  1706  			},
  1707  		}
  1708  		cl.eachDhtServer(func(s DhtServer) {
  1709  			s.AddNode(ni)
  1710  		})
  1711  	}
  1712  }
  1713  
  1714  func (cl *Client) banPeerIP(ip net.IP) {
  1715  	// We can't take this from string, because it will lose netip's v4on6. net.ParseIP parses v4
  1716  	// addresses directly to v4on6, which doesn't compare equal with v4.
  1717  	ipAddr, ok := netip.AddrFromSlice(ip)
  1718  	panicif.False(ok)
  1719  	g.MakeMapIfNil(&cl.badPeerIPs)
  1720  	cl.badPeerIPs[ipAddr] = struct{}{}
  1721  	for t := range cl.torrents {
  1722  		t.iterPeers(func(p *Peer) {
  1723  			if p.remoteIp().Equal(ip) {
  1724  				t.slogger().Debug("dropping peer with banned ip", "peer", p, "ip", ip)
  1725  				// Should this be a close?
  1726  				p.drop()
  1727  			}
  1728  		})
  1729  	}
  1730  }
  1731  
  1732  type newConnectionOpts struct {
  1733  	outgoing        bool
  1734  	remoteAddr      PeerRemoteAddr
  1735  	localPublicAddr peerLocalPublicAddr
  1736  	network         string
  1737  	connString      string
  1738  }
  1739  
  1740  func (cl *Client) newConnection(nc net.Conn, opts newConnectionOpts) (c *PeerConn) {
  1741  	if opts.network == "" {
  1742  		panic(opts.remoteAddr)
  1743  	}
  1744  	c = &PeerConn{
  1745  		Peer: Peer{
  1746  			cl:          cl,
  1747  			outgoing:    opts.outgoing,
  1748  			choking:     true,
  1749  			peerChoking: true,
  1750  
  1751  			RemoteAddr:      opts.remoteAddr,
  1752  			localPublicAddr: opts.localPublicAddr,
  1753  			Network:         opts.network,
  1754  			callbacks:       &cl.config.Callbacks,
  1755  		},
  1756  		PeerMaxRequests: 250,
  1757  		connString:      opts.connString,
  1758  		conn:            nc,
  1759  	}
  1760  	c.initRequestState()
  1761  	// TODO: Need to be much more explicit about this, including allowing non-IP bannable addresses.
  1762  	if opts.remoteAddr != nil {
  1763  		netipAddrPort, err := netip.ParseAddrPort(opts.remoteAddr.String())
  1764  		if err == nil {
  1765  			c.bannableAddr = Some(netipAddrPort.Addr())
  1766  		}
  1767  	}
  1768  	c.legacyPeerImpl = c
  1769  	c.peerImpl = c
  1770  	c.setPeerLoggers(cl.logger, cl.slogger)
  1771  	c.setRW(connStatsReadWriter{nc, c})
  1772  	c.r = cl.newDownloadRateLimitedReader(c.r)
  1773  	c.logger.Levelf(
  1774  		log.Debug,
  1775  		"inited with remoteAddr %v network %v outgoing %t",
  1776  		opts.remoteAddr, opts.network, opts.outgoing,
  1777  	)
  1778  	for _, f := range cl.config.Callbacks.NewPeer {
  1779  		f(&c.Peer)
  1780  	}
  1781  	return
  1782  }
  1783  
  1784  func (cl *Client) newDownloadRateLimitedReader(r io.Reader) io.Reader {
  1785  	return newRateLimitedReader(r, cl.config.DownloadRateLimiter)
  1786  }
  1787  
  1788  func (cl *Client) onDHTAnnouncePeer(ih metainfo.Hash, ip net.IP, port int, portOk bool) {
  1789  	cl.lock()
  1790  	defer cl.unlock()
  1791  	t := cl.torrentsByShortHash[ih]
  1792  	if t == nil {
  1793  		return
  1794  	}
  1795  	t.addPeers([]PeerInfo{{
  1796  		Addr:   ipPortAddr{ip, port},
  1797  		Source: PeerSourceDhtAnnouncePeer,
  1798  	}})
  1799  }
  1800  
  1801  func firstNotNil(ips ...net.IP) net.IP {
  1802  	for _, ip := range ips {
  1803  		if ip != nil {
  1804  			return ip
  1805  		}
  1806  	}
  1807  	return nil
  1808  }
  1809  
  1810  func (cl *Client) eachListener(f func(Listener) bool) {
  1811  	for _, s := range cl.listeners {
  1812  		if !f(s) {
  1813  			break
  1814  		}
  1815  	}
  1816  }
  1817  
  1818  func (cl *Client) findListener(f func(Listener) bool) (ret Listener) {
  1819  	for i := 0; i < len(cl.listeners); i += 1 {
  1820  		if ret = cl.listeners[i]; f(ret) {
  1821  			return
  1822  		}
  1823  	}
  1824  	return nil
  1825  }
  1826  
  1827  func (cl *Client) publicIp(peer net.IP) net.IP {
  1828  	// TODO: Use BEP 10 to determine how peers are seeing us.
  1829  	if peer.To4() != nil {
  1830  		return firstNotNil(
  1831  			cl.config.PublicIp4,
  1832  			cl.findListenerIp(func(ip net.IP) bool { return ip.To4() != nil }),
  1833  		)
  1834  	}
  1835  
  1836  	return firstNotNil(
  1837  		cl.config.PublicIp6,
  1838  		cl.findListenerIp(func(ip net.IP) bool { return ip.To4() == nil }),
  1839  	)
  1840  }
  1841  
  1842  func (cl *Client) findListenerIp(f func(net.IP) bool) net.IP {
  1843  	l := cl.findListener(
  1844  		func(l Listener) bool {
  1845  			return f(addrIpOrNil(l.Addr()))
  1846  		},
  1847  	)
  1848  	if l == nil {
  1849  		return nil
  1850  	}
  1851  	return addrIpOrNil(l.Addr())
  1852  }
  1853  
  1854  // Our IP as a peer should see it.
  1855  func (cl *Client) publicAddr(peer net.IP) IpPort {
  1856  	return IpPort{IP: cl.publicIp(peer), Port: uint16(cl.incomingPeerPort())}
  1857  }
  1858  
  1859  // ListenAddrs addresses currently being listened to.
  1860  func (cl *Client) ListenAddrs() (ret []net.Addr) {
  1861  	cl.lock()
  1862  	ret = make([]net.Addr, len(cl.listeners))
  1863  	for i := 0; i < len(cl.listeners); i += 1 {
  1864  		ret[i] = cl.listeners[i].Addr()
  1865  	}
  1866  	cl.unlock()
  1867  	return
  1868  }
  1869  
  1870  func (cl *Client) PublicIPs() (ips []net.IP) {
  1871  	if ip := cl.config.PublicIp4; ip != nil {
  1872  		ips = append(ips, ip)
  1873  	}
  1874  	if ip := cl.config.PublicIp6; ip != nil {
  1875  		ips = append(ips, ip)
  1876  	}
  1877  	return
  1878  }
  1879  
  1880  func (cl *Client) onBadAccept(addr PeerRemoteAddr) {
  1881  	ipa, ok := tryIpPortFromNetAddr(addr)
  1882  	if !ok {
  1883  		return
  1884  	}
  1885  	ip := maskIpForAcceptLimiting(ipa.IP)
  1886  	if cl.acceptLimiter == nil {
  1887  		cl.acceptLimiter = make(map[ipStr]int)
  1888  	}
  1889  	cl.acceptLimiter[ipStr(ip.String())]++
  1890  }
  1891  
  1892  func maskIpForAcceptLimiting(ip net.IP) net.IP {
  1893  	if ip4 := ip.To4(); ip4 != nil {
  1894  		return ip4.Mask(net.CIDRMask(24, 32))
  1895  	}
  1896  	return ip
  1897  }
  1898  
  1899  func (cl *Client) clearAcceptLimits() {
  1900  	cl.acceptLimiter = nil
  1901  }
  1902  
  1903  func (cl *Client) acceptLimitClearer() {
  1904  	for {
  1905  		select {
  1906  		case <-cl.closed.Done():
  1907  			return
  1908  		case <-time.After(15 * time.Minute):
  1909  			cl.lock()
  1910  			cl.clearAcceptLimits()
  1911  			cl.unlock()
  1912  		}
  1913  	}
  1914  }
  1915  
  1916  func (cl *Client) rateLimitAccept(ip net.IP) bool {
  1917  	if cl.config.DisableAcceptRateLimiting {
  1918  		return false
  1919  	}
  1920  	return cl.acceptLimiter[ipStr(maskIpForAcceptLimiting(ip).String())] > 0
  1921  }
  1922  
  1923  func (cl *Client) rLock() {
  1924  	cl._mu.RLock()
  1925  }
  1926  
  1927  func (cl *Client) rUnlock() {
  1928  	cl._mu.RUnlock()
  1929  }
  1930  
  1931  func (cl *Client) lock() {
  1932  	cl._mu.Lock()
  1933  }
  1934  
  1935  func (cl *Client) unlock() {
  1936  	cl._mu.Unlock()
  1937  }
  1938  
  1939  func (cl *Client) locker() *lockWithDeferreds {
  1940  	return &cl._mu
  1941  }
  1942  
  1943  func (cl *Client) String() string {
  1944  	return fmt.Sprintf("<%[1]T %[1]p>", cl)
  1945  }
  1946  
  1947  func (cl *Client) ICEServers() []webrtc.ICEServer {
  1948  	var ICEServers []webrtc.ICEServer
  1949  	if cl.config.ICEServerList != nil {
  1950  		ICEServers = cl.config.ICEServerList
  1951  	} else if cl.config.ICEServers != nil {
  1952  		ICEServers = []webrtc.ICEServer{{URLs: cl.config.ICEServers}}
  1953  	}
  1954  	return ICEServers
  1955  }
  1956  
  1957  // Returns connection-level aggregate connStats at the Client level. See the comment on
  1958  // TorrentStats.ConnStats. You probably want Client.Stats() instead.
  1959  func (cl *Client) ConnStats() ConnStats {
  1960  	return cl.connStats.ConnStats.Copy()
  1961  }
  1962  
  1963  func (cl *Client) Stats() ClientStats {
  1964  	cl.rLock()
  1965  	defer cl.rUnlock()
  1966  	return cl.statsLocked()
  1967  }
  1968  
  1969  func (cl *Client) underWebSeedHttpRequestLimit(key webseedHostKeyHandle) bool {
  1970  	panicif.Zero(key)
  1971  	return cl.numWebSeedRequests[key] < webseedHostRequestConcurrency
  1972  }
  1973  
  1974  // Check for bad arrangements. This is a candidate for an error state check method.
  1975  func (cl *Client) checkConfig() error {
  1976  	if EffectiveDownloadRateLimit(cl.config.DownloadRateLimiter) == 0 {
  1977  		if len(cl.dialers) != 0 {
  1978  			return errors.New("download rate limit is zero, but dialers are set")
  1979  		}
  1980  		if len(cl.listeners) != 0 && cl.config.AcceptPeerConnections {
  1981  			return errors.New("download rate limit is zero, but listening for peer connections")
  1982  		}
  1983  	}
  1984  	return nil
  1985  }
  1986  
  1987  var maxActivePieceHashers = initIntFromEnv("TORRENT_MAX_ACTIVE_PIECE_HASHERS", runtime.NumCPU(), 0)
  1988  
  1989  func (cl *Client) maxActivePieceHashers() int {
  1990  	return maxActivePieceHashers
  1991  }
  1992  
  1993  func (cl *Client) belowMaxActivePieceHashers() bool {
  1994  	return cl.activePieceHashers < cl.maxActivePieceHashers()
  1995  }
  1996  
  1997  func (cl *Client) canStartPieceHashers() bool {
  1998  	return cl.belowMaxActivePieceHashers()
  1999  }
  2000  
  2001  func (cl *Client) startPieceHashers() {
  2002  	if !cl.canStartPieceHashers() {
  2003  		return
  2004  	}
  2005  	ts := make([]*Torrent, 0, len(cl.torrents))
  2006  	for t := range cl.torrents {
  2007  		if !t.considerStartingHashers() {
  2008  			continue
  2009  		}
  2010  		ts = append(ts, t)
  2011  	}
  2012  	// Sort largest torrents first, as those are preferred by webseeds, and will cause less thrashing.
  2013  	slices.SortFunc(ts, func(a, b *Torrent) int {
  2014  		return -cmp.Compare(a.length(), b.length())
  2015  	})
  2016  	for _, t := range ts {
  2017  		t.startPieceHashers()
  2018  		if !cl.canStartPieceHashers() {
  2019  			break
  2020  		}
  2021  	}
  2022  }