github.com/anacrolix/torrent@v1.61.0/pexconn.go (about) 1 package torrent 2 3 import ( 4 "fmt" 5 "net/netip" 6 "time" 7 8 g "github.com/anacrolix/generics" 9 "github.com/anacrolix/log" 10 11 pp "github.com/anacrolix/torrent/peer_protocol" 12 ) 13 14 const ( 15 pexRetryDelay = 10 * time.Second 16 pexInterval = 1 * time.Minute 17 ) 18 19 // per-connection PEX state 20 type pexConnState struct { 21 enabled bool 22 xid pp.ExtensionNumber 23 last *pexEvent 24 timer *time.Timer 25 gate chan struct{} 26 readyfn func() 27 torrent *Torrent 28 Listed bool 29 logger log.Logger 30 // Running record of live connections the remote end of the connection purports to have. 31 remoteLiveConns map[netip.AddrPort]g.Option[pp.PexPeerFlags] 32 lastRecv time.Time 33 } 34 35 func (s *pexConnState) IsEnabled() bool { 36 return s.enabled 37 } 38 39 // Init is called from the reader goroutine upon the extended handshake completion 40 func (s *pexConnState) Init(c *PeerConn) { 41 xid, ok := c.PeerExtensionIDs[pp.ExtensionNamePex] 42 if !ok || xid == 0 || c.t.cl.config.DisablePEX { 43 return 44 } 45 s.xid = xid 46 s.last = nil 47 s.torrent = c.t 48 s.logger = c.logger.WithDefaultLevel(log.Debug).WithNames("pex") 49 s.readyfn = c.tickleWriter 50 s.gate = make(chan struct{}, 1) 51 s.timer = time.AfterFunc(0, func() { 52 s.gate <- struct{}{} 53 s.readyfn() // wake up the writer 54 }) 55 s.enabled = true 56 } 57 58 // schedule next PEX message 59 func (s *pexConnState) sched(delay time.Duration) { 60 s.timer.Reset(delay) 61 } 62 63 // generate next PEX message for the peer; returns nil if nothing yet to send 64 func (s *pexConnState) genmsg() *pp.PexMsg { 65 tx, last := s.torrent.pex.Genmsg(s.last) 66 if tx.Len() == 0 { 67 return nil 68 } 69 s.last = last 70 return &tx 71 } 72 73 func (s *pexConnState) numPending() int { 74 if s.torrent == nil { 75 return 0 76 } 77 return s.torrent.pex.numPending(s.last) 78 } 79 80 // Share is called from the writer goroutine if when it is woken up with the write buffers empty 81 // Returns whether there's more room on the send buffer to write to. 82 func (s *pexConnState) Share(postfn messageWriter) bool { 83 select { 84 case <-s.gate: 85 if tx := s.genmsg(); tx != nil { 86 s.logger.Print("sending PEX message: ", tx) 87 flow := postfn(tx.Message(s.xid)) 88 s.sched(pexInterval) 89 return flow 90 } else { 91 // no PEX to send this time - try again shortly 92 s.sched(pexRetryDelay) 93 } 94 default: 95 } 96 return true 97 } 98 99 func (s *pexConnState) updateRemoteLiveConns(rx pp.PexMsg) (errs []error) { 100 for _, dropped := range rx.Dropped { 101 addrPort, _ := ipv4AddrPortFromKrpcNodeAddr(dropped) 102 delete(s.remoteLiveConns, addrPort) 103 } 104 for _, dropped := range rx.Dropped6 { 105 addrPort, _ := ipv6AddrPortFromKrpcNodeAddr(dropped) 106 delete(s.remoteLiveConns, addrPort) 107 } 108 for i, added := range rx.Added { 109 addr := netip.AddrFrom4(*(*[4]byte)(added.IP.To4())) 110 addrPort := netip.AddrPortFrom(addr, uint16(added.Port)) 111 flags := g.SliceGet(rx.AddedFlags, i) 112 g.MakeMapIfNilAndSet(&s.remoteLiveConns, addrPort, flags) 113 } 114 for i, added := range rx.Added6 { 115 addr := netip.AddrFrom16(*(*[16]byte)(added.IP.To16())) 116 addrPort := netip.AddrPortFrom(addr, uint16(added.Port)) 117 flags := g.SliceGet(rx.Added6Flags, i) 118 g.MakeMapIfNilAndSet(&s.remoteLiveConns, addrPort, flags) 119 } 120 return 121 } 122 123 // Recv is called from the reader goroutine 124 func (s *pexConnState) Recv(payload []byte) error { 125 rx, err := pp.LoadPexMsg(payload) 126 if err != nil { 127 return fmt.Errorf("unmarshalling pex message: %w", err) 128 } 129 s.logger.Printf("received pex message: %v", rx) 130 torrent.Add("pex added peers received", int64(len(rx.Added))) 131 torrent.Add("pex added6 peers received", int64(len(rx.Added6))) 132 133 // "Clients must batch updates to send no more than 1 PEX message per minute." 134 timeSinceLastRecv := time.Since(s.lastRecv) 135 if timeSinceLastRecv < 45*time.Second { 136 return fmt.Errorf("last received only %v ago", timeSinceLastRecv) 137 } 138 s.lastRecv = time.Now() 139 s.updateRemoteLiveConns(rx) 140 141 var peers peerInfos 142 peers.AppendFromPex(rx.Added6, rx.Added6Flags) 143 peers.AppendFromPex(rx.Added, rx.AddedFlags) 144 if time.Now().Before(s.torrent.pex.rest) { 145 s.logger.Printf("in cooldown period, incoming PEX discarded") 146 return nil 147 } 148 added := s.torrent.addPeers(peers) 149 s.logger.Printf("got %v peers over pex, added %v", len(peers), added) 150 151 if len(peers) > 0 { 152 s.torrent.pex.rest = time.Now().Add(pexInterval) 153 } 154 155 // one day we may also want to: 156 // - handle drops somehow 157 // - detect malicious peers 158 159 return nil 160 } 161 162 func (s *pexConnState) Close() { 163 if s.timer != nil { 164 s.timer.Stop() 165 } 166 }