12 "github.com/anacrolix/dht/v2/krpc"
13 "github.com/anacrolix/log"
15 "github.com/anacrolix/torrent/tracker"
18 // Announces a torrent to a tracker at regular intervals, when peers are
20 type trackerScraper struct {
23 lastAnnounce trackerAnnounceResult
27 type torrentTrackerAnnouncer interface {
32 func (me trackerScraper) URL() *url.URL {
36 func (ts *trackerScraper) statusLine() string {
38 fmt.Fprintf(&w, "next ann: %v, last ann: %v",
40 na := time.Until(ts.lastAnnounce.Completed.Add(ts.lastAnnounce.Interval))
50 if ts.lastAnnounce.Err != nil {
51 return ts.lastAnnounce.Err.Error()
53 if ts.lastAnnounce.Completed.IsZero() {
56 return fmt.Sprintf("%d peers", ts.lastAnnounce.NumPeers)
62 type trackerAnnounceResult struct {
65 Interval time.Duration
69 func (me *trackerScraper) getIp() (ip net.IP, err error) {
70 ips, err := net.LookupIP(me.u.Hostname())
75 err = errors.New("no ips")
78 for _, ip = range ips {
79 if me.t.cl.ipIsBlocked(ip) {
94 err = errors.New("no acceptable ips")
98 func (me *trackerScraper) trackerUrl(ip net.IP) string {
101 u.Host = net.JoinHostPort(ip.String(), u.Port())
106 // Return how long to wait before trying again. For most errors, we return 5
107 // minutes, a relatively quick turn around for DNS changes.
108 func (me *trackerScraper) announce(event tracker.AnnounceEvent) (ret trackerAnnounceResult) {
110 ret.Completed = time.Now()
112 ret.Interval = time.Minute
115 ip, err := me.getIp()
117 ret.Err = fmt.Errorf("error getting ip: %s", err)
121 req := me.t.announceRequest(event)
123 ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
125 me.t.logger.WithDefaultLevel(log.Debug).Printf("announcing to %q: %#v", me.u.String(), req)
126 res, err := tracker.Announce{
128 HTTPProxy: me.t.cl.config.HTTPProxy,
129 UserAgent: me.t.cl.config.HTTPUserAgent,
130 TrackerUrl: me.trackerUrl(ip),
132 HostHeader: me.u.Host,
133 ServerName: me.u.Hostname(),
134 UdpNetwork: me.u.Scheme,
135 ClientIp4: krpc.NodeAddr{IP: me.t.cl.config.PublicIp4},
136 ClientIp6: krpc.NodeAddr{IP: me.t.cl.config.PublicIp6},
139 ret.Err = fmt.Errorf("error announcing: %s", err)
142 me.t.AddPeers(peerInfos(nil).AppendFromTracker(res.Peers))
143 ret.NumPeers = len(res.Peers)
144 ret.Interval = time.Duration(res.Interval) * time.Second
148 func (me *trackerScraper) Run() {
149 defer me.announceStopped()
150 // make sure first announce is a "started"
154 // after first announce, get back to regular "none"
161 // Make sure we don't announce for at least a minute since the last one.
162 interval := ar.Interval
163 if interval < time.Minute {
164 interval = time.Minute
168 wantPeers := me.t.wantPeersEvent.C()
169 closed := me.t.closed.C()
172 // If we want peers, reduce the interval to the minimum.
175 if interval > time.Minute {
176 interval = time.Minute
178 // Now we're at the minimum, don't trigger on it anymore.
187 // Recalculate the interval.
189 case <-time.After(time.Until(ar.Completed.Add(interval))):
194 func (me *trackerScraper) announceStopped() {
195 me.announce(tracker.Stopped)