2013-06-22 03:43:11 +02:00
|
|
|
// Copyright 2013 The Chihaya Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by the BSD 2-Clause license,
|
|
|
|
// which can be found in the LICENSE file.
|
|
|
|
|
2013-06-22 01:31:32 +02:00
|
|
|
package server
|
|
|
|
|
|
|
|
import (
|
2014-06-24 04:47:43 +02:00
|
|
|
"io"
|
2013-11-24 06:49:20 +01:00
|
|
|
"net/http"
|
|
|
|
"strconv"
|
|
|
|
|
2014-06-24 04:47:43 +02:00
|
|
|
log "github.com/golang/glog"
|
|
|
|
|
|
|
|
"github.com/chihaya/chihaya/bencode"
|
|
|
|
"github.com/chihaya/chihaya/drivers/tracker"
|
|
|
|
"github.com/chihaya/chihaya/models"
|
2013-06-22 01:31:32 +02:00
|
|
|
)
|
|
|
|
|
2013-07-24 06:18:43 +02:00
|
|
|
func (s Server) serveAnnounce(w http.ResponseWriter, r *http.Request) {
|
2014-06-24 04:47:43 +02:00
|
|
|
announce, err := models.NewAnnounce(r, s.conf)
|
2013-11-24 06:49:20 +01:00
|
|
|
if err != nil {
|
|
|
|
fail(err, w, r)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-02-23 05:47:11 +01:00
|
|
|
conn, err := s.trackerPool.Get()
|
2013-11-24 06:49:20 +01:00
|
|
|
if err != nil {
|
2014-06-24 04:47:43 +02:00
|
|
|
fail(err, w, r)
|
|
|
|
return
|
2013-11-24 06:49:20 +01:00
|
|
|
}
|
|
|
|
|
2014-06-24 04:47:43 +02:00
|
|
|
err = conn.ClientWhitelisted(announce.ClientID())
|
2013-11-24 06:49:20 +01:00
|
|
|
if err != nil {
|
|
|
|
fail(err, w, r)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-06-24 04:47:43 +02:00
|
|
|
var user *models.User
|
|
|
|
if s.conf.Private {
|
|
|
|
user, err = conn.FindUser(announce.Passkey)
|
|
|
|
if err != nil {
|
|
|
|
fail(err, w, r)
|
|
|
|
return
|
|
|
|
}
|
2013-11-24 06:49:20 +01:00
|
|
|
}
|
2014-06-24 04:47:43 +02:00
|
|
|
|
|
|
|
torrent, err := conn.FindTorrent(announce.Infohash)
|
|
|
|
if err != nil {
|
|
|
|
fail(err, w, r)
|
2013-11-24 06:49:20 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-06-24 09:59:30 +02:00
|
|
|
peer := models.NewPeer(announce, user, torrent)
|
2014-06-24 04:47:43 +02:00
|
|
|
|
2014-06-24 09:59:30 +02:00
|
|
|
created, err := updateTorrent(conn, announce, peer, torrent)
|
2013-11-24 06:49:20 +01:00
|
|
|
if err != nil {
|
2014-06-24 04:47:43 +02:00
|
|
|
fail(err, w, r)
|
2013-11-24 06:49:20 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-06-24 09:59:30 +02:00
|
|
|
snatched, err := handleEvent(conn, announce, peer, user, torrent)
|
2014-06-24 04:47:43 +02:00
|
|
|
if err != nil {
|
|
|
|
fail(err, w, r)
|
|
|
|
return
|
2013-11-24 06:49:20 +01:00
|
|
|
}
|
|
|
|
|
2014-06-26 20:26:58 +02:00
|
|
|
if s.conf.Private {
|
|
|
|
delta := models.NewAnnounceDelta(announce, peer, user, torrent, created, snatched)
|
|
|
|
s.backendConn.RecordAnnounce(delta)
|
|
|
|
}
|
2013-11-24 06:49:20 +01:00
|
|
|
|
2014-06-26 20:26:58 +02:00
|
|
|
writeAnnounceResponse(w, announce, user, torrent)
|
2013-11-24 06:49:20 +01:00
|
|
|
|
2014-06-26 23:10:39 +02:00
|
|
|
w.(http.Flusher).Flush()
|
|
|
|
|
2014-06-29 02:44:43 +02:00
|
|
|
log.V(5).Infof(
|
|
|
|
"announce: ip: %s, user: %s, torrent: %s",
|
|
|
|
announce.IP,
|
|
|
|
user.ID,
|
|
|
|
torrent.ID,
|
|
|
|
)
|
2014-06-24 04:47:43 +02:00
|
|
|
}
|
|
|
|
|
2014-06-24 09:59:30 +02:00
|
|
|
func updateTorrent(c tracker.Conn, a *models.Announce, p *models.Peer, t *models.Torrent) (created bool, err error) {
|
2014-06-24 04:47:43 +02:00
|
|
|
if !t.Active && a.Left == 0 {
|
2014-06-24 09:59:30 +02:00
|
|
|
err = c.MarkActive(t)
|
2014-06-24 04:47:43 +02:00
|
|
|
if err != nil {
|
|
|
|
return
|
2013-11-24 06:49:20 +01:00
|
|
|
}
|
2014-06-24 04:47:43 +02:00
|
|
|
}
|
2013-11-24 06:49:20 +01:00
|
|
|
|
2014-06-24 04:47:43 +02:00
|
|
|
switch {
|
|
|
|
case t.InSeederPool(p):
|
2014-06-24 09:59:30 +02:00
|
|
|
err = c.SetSeeder(t, p)
|
2013-11-24 06:49:20 +01:00
|
|
|
if err != nil {
|
2014-06-24 04:47:43 +02:00
|
|
|
return
|
2013-11-24 06:49:20 +01:00
|
|
|
}
|
|
|
|
|
2014-06-24 04:47:43 +02:00
|
|
|
case t.InLeecherPool(p):
|
2014-06-24 09:59:30 +02:00
|
|
|
err = c.SetLeecher(t, p)
|
2013-11-24 06:49:20 +01:00
|
|
|
if err != nil {
|
2014-06-24 04:47:43 +02:00
|
|
|
return
|
2013-11-24 06:49:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
2014-06-24 04:47:43 +02:00
|
|
|
if a.Left == 0 {
|
2014-06-24 09:59:30 +02:00
|
|
|
err = c.AddSeeder(t, p)
|
2013-11-24 06:49:20 +01:00
|
|
|
if err != nil {
|
2014-06-24 04:47:43 +02:00
|
|
|
return
|
2013-11-24 06:49:20 +01:00
|
|
|
}
|
|
|
|
} else {
|
2014-06-24 09:59:30 +02:00
|
|
|
err = c.AddLeecher(t, p)
|
2013-11-24 06:49:20 +01:00
|
|
|
if err != nil {
|
2014-06-24 04:47:43 +02:00
|
|
|
return
|
2013-11-24 06:49:20 +01:00
|
|
|
}
|
|
|
|
}
|
2014-06-24 04:47:43 +02:00
|
|
|
created = true
|
2013-11-24 06:49:20 +01:00
|
|
|
}
|
|
|
|
|
2014-06-24 04:47:43 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-06-24 09:59:30 +02:00
|
|
|
func handleEvent(c tracker.Conn, a *models.Announce, p *models.Peer, u *models.User, t *models.Torrent) (snatched bool, err error) {
|
2013-11-24 06:49:20 +01:00
|
|
|
switch {
|
2014-06-24 04:47:43 +02:00
|
|
|
case a.Event == "stopped" || a.Event == "paused":
|
|
|
|
if t.InSeederPool(p) {
|
2014-06-24 09:59:30 +02:00
|
|
|
err = c.RemoveSeeder(t, p)
|
2013-11-24 06:49:20 +01:00
|
|
|
if err != nil {
|
2014-06-24 04:47:43 +02:00
|
|
|
return
|
2013-11-24 06:49:20 +01:00
|
|
|
}
|
|
|
|
}
|
2014-06-24 04:47:43 +02:00
|
|
|
if t.InLeecherPool(p) {
|
2014-06-24 09:59:30 +02:00
|
|
|
err = c.RemoveLeecher(t, p)
|
2013-11-24 06:49:20 +01:00
|
|
|
if err != nil {
|
2014-06-24 04:47:43 +02:00
|
|
|
return
|
2013-11-24 06:49:20 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-24 04:47:43 +02:00
|
|
|
case a.Event == "completed":
|
2014-06-24 09:59:30 +02:00
|
|
|
err = c.IncrementSnatches(t)
|
2013-11-24 06:49:20 +01:00
|
|
|
if err != nil {
|
2014-06-24 04:47:43 +02:00
|
|
|
return
|
2013-11-24 06:49:20 +01:00
|
|
|
}
|
2014-06-24 04:47:43 +02:00
|
|
|
snatched = true
|
|
|
|
|
|
|
|
if t.InLeecherPool(p) {
|
2014-06-24 09:59:30 +02:00
|
|
|
err = tracker.LeecherFinished(c, t, p)
|
2013-11-24 06:49:20 +01:00
|
|
|
if err != nil {
|
2014-06-24 04:47:43 +02:00
|
|
|
return
|
2013-11-24 06:49:20 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-24 04:47:43 +02:00
|
|
|
case t.InLeecherPool(p) && a.Left == 0:
|
2013-11-24 06:49:20 +01:00
|
|
|
// A leecher completed but the event was never received
|
2014-06-24 09:59:30 +02:00
|
|
|
err = tracker.LeecherFinished(c, t, p)
|
2013-11-24 06:49:20 +01:00
|
|
|
if err != nil {
|
2014-06-24 04:47:43 +02:00
|
|
|
return
|
2013-11-24 06:49:20 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-24 04:47:43 +02:00
|
|
|
return
|
|
|
|
}
|
2013-11-24 06:49:20 +01:00
|
|
|
|
2014-06-24 04:47:43 +02:00
|
|
|
func writeAnnounceResponse(w io.Writer, a *models.Announce, u *models.User, t *models.Torrent) {
|
|
|
|
seedCount := len(t.Seeders)
|
|
|
|
leechCount := len(t.Leechers)
|
|
|
|
|
2014-06-29 02:44:43 +02:00
|
|
|
var peerCount int
|
|
|
|
if a.Left == 0 {
|
|
|
|
peerCount = minInt(a.NumWant, leechCount)
|
|
|
|
} else {
|
|
|
|
peerCount = minInt(a.NumWant, leechCount+seedCount-1)
|
|
|
|
}
|
|
|
|
|
|
|
|
bencoder := bencode.NewEncoder(w)
|
2014-06-24 04:47:43 +02:00
|
|
|
bencoder.Encode("d")
|
|
|
|
bencoder.Encode("complete")
|
|
|
|
bencoder.Encode(seedCount)
|
|
|
|
bencoder.Encode("incomplete")
|
|
|
|
bencoder.Encode(leechCount)
|
|
|
|
bencoder.Encode("interval")
|
|
|
|
bencoder.Encode(a.Config.Announce.Duration)
|
|
|
|
bencoder.Encode("min interval")
|
|
|
|
bencoder.Encode(a.Config.MinAnnounce.Duration)
|
|
|
|
|
|
|
|
if a.NumWant > 0 && a.Event != "stopped" && a.Event != "paused" {
|
2014-06-29 02:44:43 +02:00
|
|
|
if a.Compact {
|
|
|
|
writePeersCompact(w, a, u, t, peerCount)
|
|
|
|
} else {
|
|
|
|
writePeersList(w, a, u, t, peerCount)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bencoder.Encode("e")
|
|
|
|
}
|
|
|
|
|
|
|
|
func writePeersCompact(w io.Writer, a *models.Announce, u *models.User, t *models.Torrent, peerCount int) {
|
|
|
|
ipv4s, ipv6s := getPeers(a, u, t, peerCount)
|
|
|
|
bencoder := bencode.NewEncoder(w)
|
|
|
|
|
|
|
|
if len(ipv4s) > 0 {
|
|
|
|
// 6 is the number of bytes that represents 1 compact IPv4 address.
|
2014-06-24 04:47:43 +02:00
|
|
|
bencoder.Encode("peers")
|
2014-06-29 02:44:43 +02:00
|
|
|
bencoder.Encode(strconv.Itoa(len(ipv4s) * 6))
|
|
|
|
bencoder.Encode(":")
|
|
|
|
for _, peer := range ipv4s {
|
|
|
|
if ip := peer.IP.To4(); ip != nil {
|
|
|
|
w.Write(ip)
|
|
|
|
w.Write([]byte{byte(peer.Port >> 8), byte(peer.Port & 0xff)})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-06-24 04:47:43 +02:00
|
|
|
|
2014-06-29 02:44:43 +02:00
|
|
|
if len(ipv6s) > 0 {
|
|
|
|
// 18 is the number of bytes that represents 1 compact IPv6 address.
|
|
|
|
bencoder.Encode("peers6")
|
|
|
|
bencoder.Encode(strconv.Itoa(len(ipv6s) * 18))
|
|
|
|
for _, peer := range ipv6s {
|
|
|
|
if ip := peer.IP.To16(); ip != nil {
|
|
|
|
w.Write(ip)
|
|
|
|
w.Write([]byte{byte(peer.Port >> 8), byte(peer.Port & 0xff)})
|
|
|
|
}
|
2014-06-26 23:10:39 +02:00
|
|
|
}
|
2014-06-29 02:44:43 +02:00
|
|
|
}
|
|
|
|
}
|
2014-06-26 23:10:39 +02:00
|
|
|
|
2014-06-29 02:44:43 +02:00
|
|
|
func getPeers(a *models.Announce, u *models.User, t *models.Torrent, peerCount int) (ipv4s, ipv6s []*models.Peer) {
|
|
|
|
if a.Left == 0 {
|
|
|
|
// If they're seeding, give them only leechers.
|
2014-06-29 07:23:55 +02:00
|
|
|
splitPeers(&ipv4s, &ipv6s, u, t.Leechers, peerCount)
|
2014-06-29 02:44:43 +02:00
|
|
|
} else {
|
|
|
|
// If they're leeching, prioritize giving them seeders.
|
2014-06-29 07:23:55 +02:00
|
|
|
count := splitPeers(&ipv4s, &ipv6s, u, t.Seeders, peerCount)
|
|
|
|
splitPeers(&ipv4s, &ipv6s, u, t.Leechers, peerCount-count)
|
2014-06-29 02:44:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-06-29 07:23:55 +02:00
|
|
|
func splitPeers(ipv4s, ipv6s *[]*models.Peer, u *models.User, peers map[string]models.Peer, peerCount int) (count int) {
|
2014-06-29 02:44:43 +02:00
|
|
|
for _, peer := range peers {
|
|
|
|
if count >= peerCount {
|
|
|
|
break
|
2013-11-24 06:49:20 +01:00
|
|
|
}
|
|
|
|
|
2014-06-29 02:44:43 +02:00
|
|
|
if peer.UserID == u.ID {
|
|
|
|
continue
|
2013-11-24 06:49:20 +01:00
|
|
|
}
|
|
|
|
|
2014-06-29 02:44:43 +02:00
|
|
|
if ip := peer.IP.To4(); ip != nil {
|
2014-06-29 07:23:55 +02:00
|
|
|
*ipv4s = append(*ipv4s, &peer)
|
2014-06-29 02:44:43 +02:00
|
|
|
} else {
|
2014-06-29 07:23:55 +02:00
|
|
|
*ipv6s = append(*ipv6s, &peer)
|
2013-11-24 06:49:20 +01:00
|
|
|
}
|
2014-06-29 02:44:43 +02:00
|
|
|
|
|
|
|
count++
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func writePeersList(w io.Writer, a *models.Announce, u *models.User, t *models.Torrent, peerCount int) {
|
|
|
|
bencoder := bencode.NewEncoder(w)
|
|
|
|
bencoder.Encode("peers")
|
|
|
|
bencoder.Encode("l")
|
|
|
|
|
|
|
|
if a.Left == 0 {
|
|
|
|
// If they're seeding, give them only leechers
|
|
|
|
writePeerDicts(w, u, t.Leechers, peerCount)
|
|
|
|
} else {
|
|
|
|
// If they're leeching, prioritize giving them seeders
|
|
|
|
count := writePeerDicts(w, u, t.Seeders, peerCount)
|
|
|
|
writePeerDicts(w, u, t.Leechers, peerCount-count)
|
2013-11-24 06:49:20 +01:00
|
|
|
}
|
2014-06-29 02:44:43 +02:00
|
|
|
|
2014-06-24 04:47:43 +02:00
|
|
|
bencoder.Encode("e")
|
2013-08-04 21:56:31 +02:00
|
|
|
}
|
|
|
|
|
2014-06-29 02:44:43 +02:00
|
|
|
func writePeerDicts(w io.Writer, u *models.User, peers map[string]models.Peer, peerCount int) (count int) {
|
2014-06-24 04:47:43 +02:00
|
|
|
bencoder := bencode.NewEncoder(w)
|
2014-06-29 02:44:43 +02:00
|
|
|
|
2014-06-24 04:47:43 +02:00
|
|
|
for _, peer := range peers {
|
2014-06-29 02:44:43 +02:00
|
|
|
if count >= peerCount {
|
2013-11-24 06:49:20 +01:00
|
|
|
break
|
|
|
|
}
|
2014-05-08 07:09:02 +02:00
|
|
|
|
2014-06-29 02:44:43 +02:00
|
|
|
if peer.UserID == u.ID {
|
2013-12-06 02:37:19 +01:00
|
|
|
continue
|
|
|
|
}
|
2014-05-08 07:09:02 +02:00
|
|
|
|
2014-06-29 02:44:43 +02:00
|
|
|
bencoder.Encode("d")
|
|
|
|
bencoder.Encode("ip")
|
|
|
|
bencoder.Encode(peer.IP)
|
|
|
|
bencoder.Encode("peer id")
|
|
|
|
bencoder.Encode(peer.ID)
|
|
|
|
bencoder.Encode("port")
|
|
|
|
bencoder.Encode(peer.Port)
|
|
|
|
bencoder.Encode("e")
|
|
|
|
|
2013-11-24 06:49:20 +01:00
|
|
|
count++
|
|
|
|
}
|
2014-05-08 07:09:02 +02:00
|
|
|
|
2014-06-29 02:44:43 +02:00
|
|
|
return count
|
2013-08-04 21:56:31 +02:00
|
|
|
}
|
|
|
|
|
2014-06-24 04:47:43 +02:00
|
|
|
func minInt(a, b int) int {
|
|
|
|
if a < b {
|
|
|
|
return a
|
2013-11-24 06:49:20 +01:00
|
|
|
}
|
2014-05-08 07:09:02 +02:00
|
|
|
|
2014-06-24 04:47:43 +02:00
|
|
|
return b
|
2013-07-12 06:36:24 +02:00
|
|
|
}
|