1 package tracker
2
3 import (
4 "bufio"
5 "bytes"
6
7 "encoding/binary"
8 "fmt"
9 "github.com/concurrency-8/parser"
10 bencode "github.com/zeebo/bencode"
11 "io/ioutil"
12 "log"
13 "math"
14 "math/rand"
15 "net"
16 "net/http"
17 "net/url"
18 "os"
19 "strconv"
20 "time"
21 )
22
23 var (
24 root string
25 torrents []string
26 err error
27 )
28
29
30 func buildConnReq() []byte {
31 var buffer bytes.Buffer
32 writer := bufio.NewWriter(&buffer)
33 binary.Write(writer, binary.BigEndian, uint64(0x41727101980))
34 binary.Write(writer, binary.BigEndian, uint32(0))
35 binary.Write(writer, binary.BigEndian, getRandomByteArr(4))
36 writer.Flush()
37
38 return buffer.Bytes()
39 }
40
41
42 func respType(response bytes.Buffer) string {
43 action := binary.BigEndian.Uint32(response.Bytes()[0:4])
44 if action == 0 {
45 return "connect"
46 }
47 return "announce"
48 }
49
50
51 func parseConnResp(response bytes.Buffer) ConnectResponse {
52 var connectionResponse ConnectResponse
53 responseBytes := response.Bytes()
54 connectionResponse.Action = binary.BigEndian.Uint32(responseBytes[0:4])
55 connectionResponse.TransactionID = binary.BigEndian.Uint32(responseBytes[4:8])
56 connectionResponse.ConnectionID = binary.BigEndian.Uint64(responseBytes[8:])
57 return connectionResponse
58 }
59
60
61 func getRandomByteArr(size uint) []byte {
62 temp := make([]byte, size)
63 _, err := rand.Read(temp)
64 if err != nil {
65 fmt.Fprintf(os.Stderr, "Unable to generate Crypto random byte array")
66 }
67 return temp
68 }
69
70
71 func buildAnnounceReq(connectionID uint64, report *ClientStatusReport) (buffer *bytes.Buffer, err error) {
72 buffer = new(bytes.Buffer)
73
74
75 err = binary.Write(buffer, binary.BigEndian, connectionID)
76 if err != nil {
77 return
78 }
79
80
81 err = binary.Write(buffer, binary.BigEndian, uint32(1))
82 if err != nil {
83 return
84 }
85
86
87 err = binary.Write(buffer, binary.BigEndian, getRandomByteArr(4))
88 if err != nil {
89 return
90 }
91
92
93 var infoHash [20]byte
94 copy(infoHash[:], report.TorrentFile.InfoHash)
95 err = binary.Write(buffer, binary.BigEndian, infoHash)
96 if err != nil {
97 return
98 }
99
100
101 err = binary.Write(buffer, binary.BigEndian, []byte(report.PeerID))
102 if err != nil {
103 return
104 }
105
106
107 err = binary.Write(buffer, binary.BigEndian, report.Downloaded)
108 if err != nil {
109 return
110 }
111
112
113 err = binary.Write(buffer, binary.BigEndian, report.Left)
114 if err != nil {
115 return
116 }
117
118
119 err = binary.Write(buffer, binary.BigEndian, report.Uploaded)
120 if err != nil {
121 return
122 }
123
124
125 var event uint32
126 if report.Event == "" {
127 event = 0
128 }
129
130 err = binary.Write(buffer, binary.BigEndian, event)
131 if err != nil {
132 return
133 }
134
135
136 err = binary.Write(buffer, binary.BigEndian, uint32(0))
137 if err != nil {
138 return
139 }
140
141
142 err = binary.Write(buffer, binary.BigEndian, uint32(0))
143 if err != nil {
144 return
145 }
146
147
148 err = binary.Write(buffer, binary.BigEndian, int32(-1))
149 if err != nil {
150 return
151 }
152
153
154 err = binary.Write(buffer, binary.BigEndian, report.Port)
155 if err != nil {
156 return
157 }
158
159 return
160 }
161
162
163 func parseAnnounceResp(response bytes.Buffer) *AnnounceResponse {
164 var result AnnounceResponse
165
166 responseBytes := response.Bytes()
167
168 result.Action = binary.BigEndian.Uint32(responseBytes[0:4])
169 result.TransactionID = binary.BigEndian.Uint32(responseBytes[4:8])
170 result.Interval = binary.BigEndian.Uint32(responseBytes[8:12])
171 result.Leechers = binary.BigEndian.Uint32(responseBytes[12:16])
172 result.Seeders = binary.BigEndian.Uint32(responseBytes[16:20])
173
174 result.Peers = make([]Peer, (len(responseBytes)-20)/6)
175
176 for i := 20; i+5 < len(responseBytes); i += 6 {
177 result.Peers[(i-20)/6].IPAdress = binary.BigEndian.Uint32(responseBytes[i : i+4])
178 result.Peers[(i-20)/6].Port = binary.BigEndian.Uint16(responseBytes[i+4 : i+6])
179 }
180
181 return &result
182 }
183
184
185 func getPeersUDP(u *url.URL, report *ClientStatusReport) (resp *AnnounceResponse, err error) {
186 serverAddr, err := net.ResolveUDPAddr("udp", u.Host)
187 if err != nil {
188 return
189 }
190 con, err := net.DialUDP("udp", nil, serverAddr)
191 if err != nil {
192 return
193 }
194 defer con.Close()
195
196 var connectionID uint64
197 for retry := uint(0); retry < uint(8); retry++ {
198
199 err = con.SetDeadline(time.Now().Add(15 * (1 << retry) * time.Second))
200 if err != nil {
201 return
202 }
203
204 connectionID, err = connectToUDPTracker(con)
205 if err == nil {
206 break
207 }
208
209 if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
210 continue
211 }
212
213 if err != nil {
214 return
215 }
216
217 }
218
219 return getAnnouncementFromUDPTracker(con, connectionID, report)
220 }
221
222
223 func connectToUDPTracker(con *net.UDPConn) (connectionID uint64, err error) {
224
225 connRequest := buildConnReq()
226
227 _, err = con.Write(connRequest)
228 if err != nil {
229 return
230 }
231
232 respBytes := make([]byte, 16)
233
234 var respLen int
235 respLen, err = con.Read(respBytes)
236 if err != nil {
237 fmt.Println(err)
238 return
239 }
240
241 if respLen != 16 {
242 err = fmt.Errorf("Unexpected response size %d", respLen)
243 return
244 }
245
246 resp := bytes.NewBuffer(respBytes)
247 var resType string
248 resType = respType(*resp)
249
250 if resType != "connect" {
251 err = fmt.Errorf("Unexpected response action %s", resType)
252 return
253 }
254
255 connResponse := parseConnResp(*resp)
256 connectionID = connResponse.ConnectionID
257 return
258
259 }
260
261
262 func getAnnouncementFromUDPTracker(con *net.UDPConn, connectionID uint64, report *ClientStatusReport) (resp *AnnounceResponse, err error) {
263
264 announceRequest, err := buildAnnounceReq(connectionID, report)
265 if err != nil {
266 return
267 }
268
269 _, err = con.Write(announceRequest.Bytes())
270 if err != nil {
271 fmt.Println(err)
272 return
273 }
274
275 respBuffer := new(bytes.Buffer)
276
277 var respLen int
278 respBytes := make([]byte, 4096)
279 respLen, err = con.Read(respBytes)
280
281 if err != nil {
282 return
283 }
284
285 if respLen == 0 {
286 return
287 }
288
289 binary.Write(respBuffer, binary.BigEndian, respBytes[:respLen])
290
291 resp = parseAnnounceResp(*respBuffer)
292 return
293 }
294
295
296 func (tr *AnnounceResponse) decodePeerBytes() {
297 tr.Peers = make([]Peer, len(tr.PeerBytes)/6)
298
299 for i := 0; i+5 < len(tr.PeerBytes); i += 6 {
300 tr.Peers[i/6].IPAdress = binary.BigEndian.Uint32(tr.PeerBytes[i : i+4])
301 tr.Peers[i/6].Port = binary.BigEndian.Uint16(tr.PeerBytes[i+4 : i+6])
302 }
303 }
304
305
306 func getPeersHTTP(u *url.URL, report *ClientStatusReport) (tr *AnnounceResponse, err error) {
307 uq := u.Query()
308
309 uq.Add("info_hash", report.TorrentFile.InfoHash)
310 uq.Add("peer_id", report.PeerID)
311 uq.Add("port", strconv.FormatUint(uint64(report.Port), 10))
312 uq.Add("uploaded", strconv.FormatUint(report.Uploaded, 10))
313 uq.Add("downloaded", strconv.FormatUint(report.Downloaded, 10))
314 uq.Add("left", strconv.FormatUint(report.Left, 10))
315 uq.Add("compact", "1")
316
317 u.RawQuery = uq.Encode()
318
319 resp, err := http.Get(u.String())
320
321 if err != nil {
322 return
323 }
324
325 defer resp.Body.Close()
326
327 body, err := ioutil.ReadAll(resp.Body)
328
329 if err != nil {
330 return
331 }
332
333 tr = &AnnounceResponse{}
334 err = bencode.DecodeBytes(body, tr)
335 (*tr).decodePeerBytes()
336
337 return
338 }
339
340
341 func GetPeers(u *url.URL, report *ClientStatusReport) (tr *AnnounceResponse, err error) {
342
343 switch u.Scheme {
344 case "http":
345 tr, err = getPeersHTTP(u, report)
346 case "udp":
347 tr, err = getPeersUDP(u, report)
348 default:
349 err = fmt.Errorf("Announce url not recognized")
350 }
351
352 return
353 }
354
355
356 func GetClientStatusReport(torrent parser.TorrentFile, port uint16) (report *ClientStatusReport) {
357
358 report = &ClientStatusReport{}
359 report.TorrentFile = torrent
360 report.PeerID = string(getRandomByteArr(20))
361 report.Left = torrent.Length
362 report.Port = port
363 report.Event = ""
364 report.Data = make([]parser.Piece, len(torrent.Piece)/20)
365
366 for i := range report.Data {
367 report.Data[i].Blocks = make([]parser.PieceBlock, int(math.Ceil(float64(torrent.PieceLength)/float64(parser.BLOCK_LEN))))
368 }
369
370 return
371 }
372 func GetRandomTorrent() parser.TorrentFile {
373 root = "././test_torrents"
374
375 files, err := ioutil.ReadDir(root)
376 if err != nil {
377 log.Fatal(err)
378 }
379
380 for _, f := range files {
381 torrents = append(torrents, f.Name())
382 }
383 rand.Seed(time.Now().Unix())
384 random_torrent := root + "/" + torrents[rand.Intn(len(torrents))]
385 store, _ := parser.ParseFromFile(random_torrent)
386 return store
387 }
388
View as plain text