]> Sergey Matveev's repositories - btrtrc.git/blob - tracker/http.go
443bd58e8de623a1ad9b3dfd5ee3ebdab0d72e7d
[btrtrc.git] / tracker / http.go
1 package tracker
2
3 import (
4         "bytes"
5         "crypto/tls"
6         "fmt"
7         "io"
8         "log"
9         "math"
10         "net"
11         "net/http"
12         "net/url"
13         "strconv"
14
15         "github.com/anacrolix/dht/v2/krpc"
16         "github.com/anacrolix/missinggo/httptoo"
17
18         "github.com/anacrolix/torrent/bencode"
19 )
20
21 type HttpResponse struct {
22         FailureReason string `bencode:"failure reason"`
23         Interval      int32  `bencode:"interval"`
24         TrackerId     string `bencode:"tracker id"`
25         Complete      int32  `bencode:"complete"`
26         Incomplete    int32  `bencode:"incomplete"`
27         Peers         Peers  `bencode:"peers"`
28         // BEP 7
29         Peers6 krpc.CompactIPv6NodeAddrs `bencode:"peers6"`
30 }
31
32 type Peers []Peer
33
34 func (me *Peers) UnmarshalBencode(b []byte) (err error) {
35         var _v interface{}
36         err = bencode.Unmarshal(b, &_v)
37         if err != nil {
38                 return
39         }
40         switch v := _v.(type) {
41         case string:
42                 vars.Add("http responses with string peers", 1)
43                 var cnas krpc.CompactIPv4NodeAddrs
44                 err = cnas.UnmarshalBinary([]byte(v))
45                 if err != nil {
46                         return
47                 }
48                 for _, cp := range cnas {
49                         *me = append(*me, Peer{
50                                 IP:   cp.IP[:],
51                                 Port: int(cp.Port),
52                         })
53                 }
54                 return
55         case []interface{}:
56                 vars.Add("http responses with list peers", 1)
57                 for _, i := range v {
58                         var p Peer
59                         p.FromDictInterface(i.(map[string]interface{}))
60                         *me = append(*me, p)
61                 }
62                 return
63         default:
64                 vars.Add("http responses with unhandled peers type", 1)
65                 err = fmt.Errorf("unsupported type: %T", _v)
66                 return
67         }
68 }
69
70 func setAnnounceParams(_url *url.URL, ar *AnnounceRequest, opts Announce) {
71         q := _url.Query()
72
73         q.Set("key", strconv.FormatInt(int64(ar.Key), 10))
74         q.Set("info_hash", string(ar.InfoHash[:]))
75         q.Set("peer_id", string(ar.PeerId[:]))
76         // AFAICT, port is mandatory, and there's no implied port key.
77         q.Set("port", fmt.Sprintf("%d", ar.Port))
78         q.Set("uploaded", strconv.FormatInt(ar.Uploaded, 10))
79         q.Set("downloaded", strconv.FormatInt(ar.Downloaded, 10))
80
81         // The AWS S3 tracker returns "400 Bad Request: left(-1) was not in the valid range 0 -
82         // 9223372036854775807" if left is out of range, or "500 Internal Server Error: Internal Server
83         // Error" if omitted entirely.
84         left := ar.Left
85         if left < 0 {
86                 left = math.MaxInt64
87         }
88         q.Set("left", strconv.FormatInt(left, 10))
89
90         if ar.Event != None {
91                 q.Set("event", ar.Event.String())
92         }
93         // http://stackoverflow.com/questions/17418004/why-does-tracker-server-not-understand-my-request-bittorrent-protocol
94         q.Set("compact", "1")
95         // According to https://wiki.vuze.com/w/Message_Stream_Encryption. TODO:
96         // Take EncryptionPolicy or something like it as a parameter.
97         q.Set("supportcrypto", "1")
98         doIp := func(versionKey string, ip net.IP) {
99                 if ip == nil {
100                         return
101                 }
102                 ipString := ip.String()
103                 q.Set(versionKey, ipString)
104                 // Let's try listing them. BEP 3 mentions having an "ip" param, and BEP 7 says we can list
105                 // addresses for other address-families, although it's not encouraged.
106                 q.Add("ip", ipString)
107         }
108         doIp("ipv4", opts.ClientIp4.IP)
109         doIp("ipv6", opts.ClientIp6.IP)
110         _url.RawQuery = q.Encode()
111 }
112
113 func announceHTTP(opt Announce, _url *url.URL) (ret AnnounceResponse, err error) {
114         _url = httptoo.CopyURL(_url)
115         setAnnounceParams(_url, &opt.Request, opt)
116         req, err := http.NewRequest("GET", _url.String(), nil)
117         req.Header.Set("User-Agent", opt.UserAgent)
118         req.Host = opt.HostHeader
119         if opt.Context != nil {
120                 req = req.WithContext(opt.Context)
121         }
122         resp, err := (&http.Client{
123                 //Timeout: time.Second * 15,
124                 Transport: &http.Transport{
125                         //Dial: (&net.Dialer{
126                         //      Timeout: 15 * time.Second,
127                         //}).Dial,
128                         Proxy: opt.HTTPProxy,
129                         //TLSHandshakeTimeout: 15 * time.Second,
130                         TLSClientConfig: &tls.Config{
131                                 InsecureSkipVerify: true,
132                                 ServerName:         opt.ServerName,
133                         },
134                         // This is for S3 trackers that hold connections open.
135                         DisableKeepAlives: true,
136                 },
137         }).Do(req)
138         if err != nil {
139                 return
140         }
141         defer resp.Body.Close()
142         var buf bytes.Buffer
143         io.Copy(&buf, resp.Body)
144         if resp.StatusCode != 200 {
145                 err = fmt.Errorf("response from tracker: %s: %s", resp.Status, buf.String())
146                 return
147         }
148         var trackerResponse HttpResponse
149         err = bencode.Unmarshal(buf.Bytes(), &trackerResponse)
150         if _, ok := err.(bencode.ErrUnusedTrailingBytes); ok {
151                 err = nil
152         } else if err != nil {
153                 err = fmt.Errorf("error decoding %q: %s", buf.Bytes(), err)
154                 return
155         }
156         if trackerResponse.FailureReason != "" {
157                 err = fmt.Errorf("tracker gave failure reason: %q", trackerResponse.FailureReason)
158                 return
159         }
160         vars.Add("successful http announces", 1)
161         ret.Interval = trackerResponse.Interval
162         ret.Leechers = trackerResponse.Incomplete
163         ret.Seeders = trackerResponse.Complete
164         if len(trackerResponse.Peers) != 0 {
165                 vars.Add("http responses with nonempty peers key", 1)
166         }
167         ret.Peers = trackerResponse.Peers
168         if len(trackerResponse.Peers6) != 0 {
169                 vars.Add("http responses with nonempty peers6 key", 1)
170         }
171         for _, na := range trackerResponse.Peers6 {
172                 ret.Peers = append(ret.Peers, Peer{
173                         IP:   na.IP,
174                         Port: na.Port,
175                 })
176         }
177         return
178 }