]> Sergey Matveev's repositories - btrtrc.git/blob - tracker/udp.go
Merge branch 'master' of https://github.com/lovedboy/torrent
[btrtrc.git] / tracker / udp.go
1 package tracker
2
3 import (
4         "bytes"
5         "encoding/binary"
6         "errors"
7         "fmt"
8         "io"
9         "math/rand"
10         "net"
11         "net/url"
12         "time"
13
14         "github.com/anacrolix/missinggo"
15         "github.com/anacrolix/missinggo/pproffd"
16
17         "github.com/anacrolix/torrent/util"
18 )
19
20 type Action int32
21
22 const (
23         ActionConnect Action = iota
24         ActionAnnounce
25         ActionScrape
26         ActionError
27
28         connectRequestConnectionId = 0x41727101980
29
30         // BEP 41
31         optionTypeEndOfOptions = 0
32         optionTypeNOP          = 1
33         optionTypeURLData      = 2
34 )
35
36 type ConnectionRequest struct {
37         ConnectionId int64
38         Action       int32
39         TransctionId int32
40 }
41
42 type ConnectionResponse struct {
43         ConnectionId int64
44 }
45
46 type ResponseHeader struct {
47         Action        Action
48         TransactionId int32
49 }
50
51 type RequestHeader struct {
52         ConnectionId  int64
53         Action        Action
54         TransactionId int32
55 } // 16 bytes
56
57 type AnnounceResponseHeader struct {
58         Interval int32
59         Leechers int32
60         Seeders  int32
61 }
62
63 func newTransactionId() int32 {
64         return int32(rand.Uint32())
65 }
66
67 func timeout(contiguousTimeouts int) (d time.Duration) {
68         if contiguousTimeouts > 8 {
69                 contiguousTimeouts = 8
70         }
71         d = 15 * time.Second
72         for ; contiguousTimeouts > 0; contiguousTimeouts-- {
73                 d *= 2
74         }
75         return
76 }
77
78 type udpAnnounce struct {
79         contiguousTimeouts   int
80         connectionIdReceived time.Time
81         connectionId         int64
82         socket               net.Conn
83         url                  url.URL
84 }
85
86 func (c *udpAnnounce) Close() error {
87         if c.socket != nil {
88                 return c.socket.Close()
89         }
90         return nil
91 }
92
93 func (c *udpAnnounce) Do(req *AnnounceRequest) (res AnnounceResponse, err error) {
94         err = c.connect()
95         if err != nil {
96                 return
97         }
98         reqURI := c.url.RequestURI()
99         // Clearly this limits the request URI to 255 bytes. BEP 41 supports
100         // longer but I'm not fussed.
101         options := append([]byte{optionTypeURLData, byte(len(reqURI))}, []byte(reqURI)...)
102         b, err := c.request(ActionAnnounce, req, options)
103         if err != nil {
104                 return
105         }
106         var h AnnounceResponseHeader
107         err = readBody(b, &h)
108         if err != nil {
109                 if err == io.EOF {
110                         err = io.ErrUnexpectedEOF
111                 }
112                 err = fmt.Errorf("error parsing announce response: %s", err)
113                 return
114         }
115         res.Interval = h.Interval
116         res.Leechers = h.Leechers
117         res.Seeders = h.Seeders
118         cps, err := util.UnmarshalIPv4CompactPeers(b.Bytes())
119         if err != nil {
120                 return
121         }
122         for _, cp := range cps {
123                 res.Peers = append(res.Peers, Peer{
124                         IP:   cp.IP[:],
125                         Port: int(cp.Port),
126                 })
127         }
128         return
129 }
130
131 // body is the binary serializable request body. trailer is optional data
132 // following it, such as for BEP 41.
133 func (c *udpAnnounce) write(h *RequestHeader, body interface{}, trailer []byte) (err error) {
134         var buf bytes.Buffer
135         err = binary.Write(&buf, binary.BigEndian, h)
136         if err != nil {
137                 panic(err)
138         }
139         if body != nil {
140                 err = binary.Write(&buf, binary.BigEndian, body)
141                 if err != nil {
142                         panic(err)
143                 }
144         }
145         _, err = buf.Write(trailer)
146         if err != nil {
147                 return
148         }
149         n, err := c.socket.Write(buf.Bytes())
150         if err != nil {
151                 return
152         }
153         if n != buf.Len() {
154                 panic("write should send all or error")
155         }
156         return
157 }
158
159 func read(r io.Reader, data interface{}) error {
160         return binary.Read(r, binary.BigEndian, data)
161 }
162
163 func write(w io.Writer, data interface{}) error {
164         return binary.Write(w, binary.BigEndian, data)
165 }
166
167 // args is the binary serializable request body. trailer is optional data
168 // following it, such as for BEP 41.
169 func (c *udpAnnounce) request(action Action, args interface{}, options []byte) (responseBody *bytes.Buffer, err error) {
170         tid := newTransactionId()
171         err = c.write(&RequestHeader{
172                 ConnectionId:  c.connectionId,
173                 Action:        action,
174                 TransactionId: tid,
175         }, args, options)
176         if err != nil {
177                 return
178         }
179         c.socket.SetReadDeadline(time.Now().Add(timeout(c.contiguousTimeouts)))
180         b := make([]byte, 0x800) // 2KiB
181         for {
182                 var n int
183                 n, err = c.socket.Read(b)
184                 if opE, ok := err.(*net.OpError); ok {
185                         if opE.Timeout() {
186                                 c.contiguousTimeouts++
187                                 return
188                         }
189                 }
190                 if err != nil {
191                         return
192                 }
193                 buf := bytes.NewBuffer(b[:n])
194                 var h ResponseHeader
195                 err = binary.Read(buf, binary.BigEndian, &h)
196                 switch err {
197                 case io.ErrUnexpectedEOF:
198                         continue
199                 case nil:
200                 default:
201                         return
202                 }
203                 if h.TransactionId != tid {
204                         continue
205                 }
206                 c.contiguousTimeouts = 0
207                 if h.Action == ActionError {
208                         err = errors.New(buf.String())
209                 }
210                 responseBody = buf
211                 return
212         }
213 }
214
215 func readBody(r io.Reader, data ...interface{}) (err error) {
216         for _, datum := range data {
217                 err = binary.Read(r, binary.BigEndian, datum)
218                 if err != nil {
219                         break
220                 }
221         }
222         return
223 }
224
225 func (c *udpAnnounce) connected() bool {
226         return !c.connectionIdReceived.IsZero() && time.Now().Before(c.connectionIdReceived.Add(time.Minute))
227 }
228
229 func (c *udpAnnounce) connect() (err error) {
230         if c.connected() {
231                 return nil
232         }
233         c.connectionId = connectRequestConnectionId
234         if c.socket == nil {
235                 hmp := missinggo.SplitHostMaybePort(c.url.Host)
236                 if hmp.NoPort {
237                         hmp.NoPort = false
238                         hmp.Port = 80
239                 }
240                 c.socket, err = net.Dial("udp", hmp.String())
241                 if err != nil {
242                         return
243                 }
244                 c.socket = pproffd.WrapNetConn(c.socket)
245         }
246         b, err := c.request(ActionConnect, nil, nil)
247         if err != nil {
248                 return
249         }
250         var res ConnectionResponse
251         err = readBody(b, &res)
252         if err != nil {
253                 return
254         }
255         c.connectionId = res.ConnectionId
256         c.connectionIdReceived = time.Now()
257         return
258 }
259
260 func announceUDP(ar *AnnounceRequest, _url *url.URL) (AnnounceResponse, error) {
261         ua := udpAnnounce{
262                 url: *_url,
263         }
264         defer ua.Close()
265         return ua.Do(ar)
266 }