From c81da2d5ee200de6175fe508d3a2ac46d6146a8d Mon Sep 17 00:00:00 2001 From: Matt Joiner Date: Wed, 9 Dec 2020 22:36:57 +1100 Subject: [PATCH] Strictly adher to tracker announce response intervals if a torrent is private --- t.go | 1 + tracker_scraper.go | 33 ++++++++++++++++++++++++++------- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/t.go b/t.go index 9d7ce285..a0b5cb59 100644 --- a/t.go +++ b/t.go @@ -16,6 +16,7 @@ func (t *Torrent) InfoHash() metainfo.Hash { // Returns a channel that is closed when the info (.Info()) for the torrent has become available. func (t *Torrent) GotInfo() <-chan struct{} { + // TODO: We shouldn't need to lock to take a channel here, if the event is only ever set. t.cl.lock() defer t.cl.unlock() return t.gotMetainfo.C() diff --git a/tracker_scraper.go b/tracker_scraper.go index 8ba597e7..65673539 100644 --- a/tracker_scraper.go +++ b/tracker_scraper.go @@ -153,6 +153,22 @@ func (me *trackerScraper) announce(event tracker.AnnounceEvent) (ret trackerAnno return } +// Returns whether we can shorten the interval, and sets notify to a channel that receives when we +// might change our mind, or leaves it if we won't. +func (me *trackerScraper) canIgnoreInterval(notify *<-chan struct{}) bool { + gotInfo := me.t.GotInfo() + select { + case <-gotInfo: + // Private trackers really don't like us announcing more than they specify. They're also + // tracking us very carefully, so it's best to comply. + private := me.t.info.Private + return private == nil || !*private + default: + *notify = gotInfo + return false + } +} + func (me *trackerScraper) Run() { defer me.announceStopped() // make sure first announce is a "started" @@ -165,7 +181,7 @@ func (me *trackerScraper) Run() { me.lastAnnounce = ar me.t.cl.unlock() - wait: + recalculate: // Make sure we don't announce for at least a minute since the last one. interval := ar.Interval if interval < time.Minute { @@ -177,23 +193,26 @@ func (me *trackerScraper) Run() { closed := me.t.closed.C() me.t.cl.unlock() - // If we want peers, reduce the interval to the minimum. + // If we want peers, reduce the interval to the minimum if it's appropriate. + + // A channel that receives when we should reconsider our interval. Starts as nil since that + // never receives. + var reconsider <-chan struct{} select { case <-wantPeers: - if interval > time.Minute { + if interval > time.Minute && me.canIgnoreInterval(&reconsider) { interval = time.Minute } - // Now we're at the minimum, don't trigger on it anymore. - wantPeers = nil default: + reconsider = wantPeers } select { case <-closed: return - case <-wantPeers: + case <-reconsider: // Recalculate the interval. - goto wait + goto recalculate case <-time.After(time.Until(ar.Completed.Add(interval))): } } -- 2.48.1