]> Sergey Matveev's repositories - btrtrc.git/blob - tracker/server.go
refactor struct identifiers to follow conventional go names
[btrtrc.git] / tracker / server.go
1 package tracker
2
3 import (
4         "bytes"
5         "encoding/binary"
6         "fmt"
7         "math/rand"
8         "net"
9
10         "github.com/anacrolix/torrent/util"
11 )
12
13 type torrent struct {
14         Leechers int32
15         Seeders  int32
16         Peers    util.CompactIPv4Peers
17 }
18
19 type server struct {
20         pc    net.PacketConn
21         conns map[int64]struct{}
22         t     map[[20]byte]torrent
23 }
24
25 func marshal(parts ...interface{}) (ret []byte, err error) {
26         var buf bytes.Buffer
27         for _, p := range parts {
28                 err = binary.Write(&buf, binary.BigEndian, p)
29                 if err != nil {
30                         return
31                 }
32         }
33         ret = buf.Bytes()
34         return
35 }
36
37 func (s *server) respond(addr net.Addr, rh ResponseHeader, parts ...interface{}) (err error) {
38         b, err := marshal(append([]interface{}{rh}, parts...)...)
39         if err != nil {
40                 return
41         }
42         _, err = s.pc.WriteTo(b, addr)
43         return
44 }
45
46 func (s *server) newConn() (ret int64) {
47         ret = rand.Int63()
48         if s.conns == nil {
49                 s.conns = make(map[int64]struct{})
50         }
51         s.conns[ret] = struct{}{}
52         return
53 }
54
55 func (s *server) serveOne() (err error) {
56         b := make([]byte, 0x10000)
57         n, addr, err := s.pc.ReadFrom(b)
58         if err != nil {
59                 return
60         }
61         r := bytes.NewReader(b[:n])
62         var h RequestHeader
63         err = readBody(r, &h)
64         if err != nil {
65                 return
66         }
67         switch h.Action {
68         case ActionConnect:
69                 if h.ConnectionId != connectRequestConnectionId {
70                         return
71                 }
72                 connId := s.newConn()
73                 err = s.respond(addr, ResponseHeader{
74                         ActionConnect,
75                         h.TransactionId,
76                 }, ConnectionResponse{
77                         connId,
78                 })
79                 return
80         case ActionAnnounce:
81                 if _, ok := s.conns[h.ConnectionId]; !ok {
82                         s.respond(addr, ResponseHeader{
83                                 TransactionId: h.TransactionId,
84                                 Action:        ActionError,
85                         }, []byte("not connected"))
86                         return
87                 }
88                 var ar AnnounceRequest
89                 err = readBody(r, &ar)
90                 if err != nil {
91                         return
92                 }
93                 t := s.t[ar.InfoHash]
94                 b, err = t.Peers.MarshalBinary()
95                 if err != nil {
96                         panic(err)
97                 }
98                 err = s.respond(addr, ResponseHeader{
99                         TransactionId: h.TransactionId,
100                         Action:        ActionAnnounce,
101                 }, AnnounceResponseHeader{
102                         Interval: 900,
103                         Leechers: t.Leechers,
104                         Seeders:  t.Seeders,
105                 }, b)
106                 return
107         default:
108                 err = fmt.Errorf("unhandled action: %d", h.Action)
109                 s.respond(addr, ResponseHeader{
110                         TransactionId: h.TransactionId,
111                         Action:        ActionError,
112                 }, []byte("unhandled action"))
113                 return
114         }
115 }