aboutsummaryrefslogtreecommitdiff
path: root/tracker/messages.go
blob: b32dd8a754eb1a27dd25755ad55ad4b35b8bb51a (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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package tracker

import (
	"bytes"
	"crypto/sha1"
	"encoding/binary"
	"fmt"
	"io/ioutil"
	"log"
	"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   [sha1.Size]byte `query:"info_hash"` // info_hash
	PeerID     []byte          `query:"peer_id"`   // peer_id
	Port       int             `query:"port"`
	Uploaded   int             `query:"uploaded"`
	Downloaded int             `query:"downloaded"`
	Left       int             `query:"left"`
	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       time.Duration `bencode:"interval"`
	MinInterval    time.Duration `bencode:"min interval"`
	Peers          Peers         `bencode:"peers"` // can be []byte or []Peer
	Peers6         []byte        `bencode:"peers6"`
	TrackerID      string        `bencode:"tracker id"`
	WarningMessage string        `bencode:"warning message"`
}

type Peer struct {
	IP   string `bencode:"ip"`
	ID   []byte `bencode:"peer id"`
	Port int    `bencode:"port"`
}

func (p Peer) String() string {
	return fmt.Sprintf("%v %s %v", p.IP, p.ID, p.Port)
}

type Peers []Peer

func (p *Peers) UnmarshalBencode(b []byte) (int, error) {
	if b[0] == 'l' {
		log.Println("verbose")
		var tmp []Peer
		n, err := bencode.Unmarshal(b, &tmp)
		*p = Peers(tmp)
		return n, err
	}
	log.Println("compact")
	var tmp []byte
	n, err := bencode.Unmarshal(b, &tmp)
	addr, err := peerAddr(tmp)
	*p = make(Peers, len(addr))
	for i, v := range addr {
		peer := Peer{
			IP:   v.IP.String(),
			Port: int(v.Port),
		}
		(*p)[i] = peer
	}
	return n, err
}

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
}