Example #1
0
func (c *Conn) AddSeeder(t *storage.Torrent, p *storage.Peer) error {
	c.torrentsM.Lock()
	defer c.torrentsM.Unlock()
	torrent, ok := c.torrents[t.Infohash]
	if !ok {
		return tracker.ErrMissingResource
	}
	torrent.Leechers[storage.PeerMapKey(p)] = *p
	t.Leechers[storage.PeerMapKey(p)] = *p
	return nil
}
Example #2
0
func (c *Conn) RemoveSeeder(t *storage.Torrent, p *storage.Peer) error {
	c.torrentsM.Lock()
	defer c.torrentsM.Unlock()
	torrent, ok := c.torrents[t.Infohash]
	if !ok {
		return tracker.ErrMissingResource
	}
	delete(torrent.Seeders, storage.PeerMapKey(p))
	delete(t.Seeders, storage.PeerMapKey(p))
	return nil
}
Example #3
0
func (c *Conn) LeecherFinished(t *storage.Torrent, p *storage.Peer) error {
	c.torrentsM.Lock()
	defer c.torrentsM.Unlock()

	torrent, ok := c.torrents[t.Infohash]
	if !ok {
		return tracker.ErrMissingResource
	}

	torrent.Seeders[storage.PeerMapKey(p)] = *p
	delete(torrent.Leechers, storage.PeerMapKey(p))
	t.Seeders[storage.PeerMapKey(p)] = *p
	delete(t.Leechers, storage.PeerMapKey(p))
	return nil
}
Example #4
0
func (s Server) serveAnnounce(w http.ResponseWriter, r *http.Request) {
	// Parse the required parameters off of a query
	compact, numWant, infohash, peerID, event, ip, port, uploaded, downloaded, left, err := s.validateAnnounceQuery(r)
	if err != nil {
		fail(err, w, r)
		return
	}

	// Get a connection to the tracker db
	conn, err := s.dbConnPool.Get()
	if err != nil {
		log.Panicf("server: %s", err)
	}

	// Validate the user's passkey
	passkey, _ := path.Split(r.URL.Path)
	user, err := validateUser(conn, passkey)
	if err != nil {
		fail(err, w, r)
		return
	}

	// Check if the user's client is whitelisted
	whitelisted, err := conn.ClientWhitelisted(parsePeerID(peerID))
	if err != nil {
		log.Panicf("server: %s", err)
	}
	if !whitelisted {
		fail(errors.New("Your client is not approved"), w, r)
		return
	}

	// Find the specified torrent
	torrent, exists, err := conn.FindTorrent(infohash)
	if err != nil {
		log.Panicf("server: %s", err)
	}
	if !exists {
		fail(errors.New("This torrent does not exist"), w, r)
		return
	}

	// If the torrent was pruned and the user is seeding, unprune it
	if !torrent.Active && left == 0 {
		err := conn.MarkActive(torrent)
		if err != nil {
			log.Panicf("server: %s", err)
		}
	}

	// Create a new peer object from the request
	peer := &storage.Peer{
		ID:           peerID,
		UserID:       user.ID,
		TorrentID:    torrent.ID,
		IP:           ip,
		Port:         port,
		Uploaded:     uploaded,
		Downloaded:   downloaded,
		Left:         left,
		LastAnnounce: time.Now().Unix(),
	}

	// Look for the user in in the pool of seeders and leechers
	_, seeder := torrent.Seeders[storage.PeerMapKey(peer)]
	_, leecher := torrent.Leechers[storage.PeerMapKey(peer)]

	switch {
	// Guarantee that no user is in both pools
	case seeder && leecher:
		if left == 0 {
			err := conn.RemoveLeecher(torrent, peer)
			if err != nil {
				log.Panicf("server: %s", err)
			}
			leecher = false
		} else {
			err := conn.RemoveSeeder(torrent, peer)
			if err != nil {
				log.Panicf("server: %s", err)
			}
			seeder = false
		}

	case seeder:
		// Update the peer with the stats from the request
		err := conn.SetSeeder(torrent, peer)
		if err != nil {
			log.Panicf("server: %s", err)
		}

	case leecher:
		// Update the peer with the stats from the request
		err := conn.SetLeecher(torrent, peer)
		if err != nil {
			log.Panicf("server: %s", err)
		}

	default:
		if left == 0 {
			// Save the peer as a new seeder
			err := conn.AddSeeder(torrent, peer)
			if err != nil {
				log.Panicf("server: %s", err)
			}
		} else {
			err = conn.AddLeecher(torrent, peer)
			if err != nil {
				log.Panicf("server: %s", err)
			}
		}
	}

	// Handle any events in the request
	switch {
	case event == "stopped" || event == "paused":
		if seeder {
			err := conn.RemoveSeeder(torrent, peer)
			if err != nil {
				log.Panicf("server: %s", err)
			}
		}
		if leecher {
			err := conn.RemoveLeecher(torrent, peer)
			if err != nil {
				log.Panicf("server: %s", err)
			}
		}

	case event == "completed":
		err := conn.RecordSnatch(user, torrent)
		if err != nil {
			log.Panicf("server: %s", err)
		}
		if leecher {
			err := conn.LeecherFinished(torrent, peer)
			if err != nil {
				log.Panicf("server: %s", err)
			}
		}

	case leecher && left == 0:
		// A leecher completed but the event was never received
		err := conn.LeecherFinished(torrent, peer)
		if err != nil {
			log.Panicf("server: %s", err)
		}
	}

	if ip != peer.IP || port != peer.Port {
		peer.Port = port
		peer.IP = ip
	}

	// Generate the response
	seedCount := len(torrent.Seeders)
	leechCount := len(torrent.Leechers)

	writeBencoded(w, "d")
	writeBencoded(w, "complete")
	writeBencoded(w, seedCount)
	writeBencoded(w, "incomplete")
	writeBencoded(w, leechCount)
	writeBencoded(w, "interval")
	writeBencoded(w, s.conf.Announce.Duration)
	writeBencoded(w, "min interval")
	writeBencoded(w, s.conf.MinAnnounce.Duration)

	if numWant > 0 && event != "stopped" && event != "paused" {
		writeBencoded(w, "peers")
		var peerCount, count int

		if compact {
			if left > 0 {
				peerCount = minInt(numWant, leechCount)
			} else {
				peerCount = minInt(numWant, leechCount+seedCount-1)
			}
			writeBencoded(w, strconv.Itoa(peerCount*6))
			writeBencoded(w, ":")
		} else {
			writeBencoded(w, "l")
		}

		if left > 0 {
			// If they're seeding, give them only leechers
			count += writeLeechers(w, user, torrent, numWant, compact)
		} else {
			// If they're leeching, prioritize giving them seeders
			count += writeSeeders(w, user, torrent, numWant, compact)
			count += writeLeechers(w, user, torrent, numWant-count, compact)
		}

		if compact && peerCount != count {
			log.Panicf("Calculated peer count (%d) != real count (%d)", peerCount, count)
		}

		if !compact {
			writeBencoded(w, "e")
		}
	}
	writeBencoded(w, "e")
}