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