aboutsummaryrefslogtreecommitdiff
path: root/tracker/messages.go
blob: 5f962866a423c38ad071ef5d8be8cd173199a3ab (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package tracker

import (
	"bytes"
	"encoding/binary"
	"io/ioutil"
	"net"
	"net/http"
	"time"

	"dim13.org/btget/bencode"
	"dim13.org/btget/query"
)

type Event string

const (
	NoEvent   Event = ""
	Started   Event = "started"
	Stopped   Event = "stopped"
	Completed Event = "completed"
)

type Request struct {
	InfoHash   []byte     `query:"info_hash"` // info_hash
	PeerID     []byte     `query:"peer_id"`   // peer_id
	Port       int        `query:"port,optional"`
	Uploaded   int        `query:"uploaded,optional"`
	Downloaded int        `query:"downloaded,optional"`
	Left       int        `query:"left,optional"`
	Compact    bool       `query:"compact,optional"` // always true
	NoPeerID   bool       `query:"no_peer_id,optional"`
	Event      Event      `query:"event,optional"`
	IP         net.IPAddr `query:"ip,optional"`
	NumWant    int        `query:"numwant,optional"`
	Key        []byte     `query:"key,optional"`
	TrackerID  []byte     `query:"tracker_id,optional"`
}

// we support only compact mode
type Response struct {
	Complete       int    `bencode:"complete"`
	FalureReason   string `bencode:"failure reason"`
	Incomplete     int    `bencode:"incomplete"`
	Interval       int    `bencode:"interval"`
	MinInterval    int    `bencode:"min interval"`
	Peers          []byte `bencode:"peers"`
	Peers6         []byte `bencode:"peers6"`
	TrackerId      string `bencode:"tracker id"`
	WarningMessage string `bencode:"warning message"`
}

func (r Request) Get(announce string) (Response, error) {
	fail := func(err error) (Response, error) {
		return Response{}, err
	}
	q, err := query.Marshal(r)
	if err != nil {
		return fail(err)
	}
	resp, err := http.Get(announce + q)
	if err != nil {
		return fail(err)
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return fail(err)
	}

	var res Response
	err = bencode.Unmarshal(body, &res)
	if err != nil {
		return fail(err)
	}
	return res, nil
}

func (r Response) IntervalDuration() time.Duration {
	return time.Duration(r.Interval) * time.Second
}

func (r Response) PeerAddr() ([]*net.TCPAddr, error) {
	return peerAddr(r.Peers)
}

func peerAddr(b []byte) ([]*net.TCPAddr, error) {
	n := len(b) / 6
	a := make([]*net.TCPAddr, n)

	var port uint16
	for i := 0; i < n; i++ {
		off := i * 6
		buf := bytes.NewReader(b[off+4 : off+6])
		err := binary.Read(buf, binary.BigEndian, &port)
		if err != nil {
			return nil, err
		}
		a[i] = &net.TCPAddr{
			IP:   net.IP(b[off : off+4]),
			Port: int(port),
		}
	}
	return a, nil
}