1 package udpTrackerServer
13 "github.com/anacrolix/dht/v2/krpc"
14 "github.com/anacrolix/generics"
15 "github.com/anacrolix/log"
17 "github.com/anacrolix/torrent/tracker"
18 "github.com/anacrolix/torrent/tracker/udp"
21 type ConnectionTrackerAddr = string
23 type ConnectionTracker interface {
24 Add(ctx context.Context, addr ConnectionTrackerAddr, id udp.ConnectionId) error
25 Check(ctx context.Context, addr ConnectionTrackerAddr, id udp.ConnectionId) (bool, error)
28 type InfoHash = [20]byte
30 type AnnounceTracker = tracker.AnnounceTracker
33 ConnTracker ConnectionTracker
34 SendResponse func(data []byte, addr net.Addr) (int, error)
35 Announce tracker.AnnounceHandler
38 type RequestSourceAddr = net.Addr
40 func (me *Server) HandleRequest(
42 family udp.AddrFamily,
43 source RequestSourceAddr,
46 var h udp.RequestHeader
49 err := udp.Read(&r, &h)
51 err = fmt.Errorf("reading request header: %w", err)
55 case udp.ActionConnect:
56 err = me.handleConnect(ctx, source, h.TransactionId)
57 case udp.ActionAnnounce:
58 err = me.handleAnnounce(ctx, family, source, h.ConnectionId, h.TransactionId, &r)
60 err = fmt.Errorf("unimplemented")
63 err = fmt.Errorf("handling action %v: %w", h.Action, err)
68 func (me *Server) handleAnnounce(
70 addrFamily udp.AddrFamily,
71 source RequestSourceAddr,
72 connId udp.ConnectionId,
73 tid udp.TransactionId,
76 // Should we set a timeout of 10s or something for the entire response, so that we give up if a
79 ok, err := me.ConnTracker.Check(ctx, source.String(), connId)
81 err = fmt.Errorf("checking conn id: %w", err)
85 return fmt.Errorf("invalid connection id: %v", connId)
87 var req udp.AnnounceRequest
88 err = udp.Read(r, &req)
92 // TODO: This should be done asynchronously to responding to the announce.
93 announceAddr, err := netip.ParseAddrPort(source.String())
95 err = fmt.Errorf("converting source net.Addr to AnnounceAddr: %w", err)
98 opts := tracker.GetPeersOpts{MaxCount: generics.Some[uint](50)}
99 if addrFamily == udp.AddrFamilyIpv4 {
100 opts.MaxCount = generics.Some[uint](150)
102 peers, err := me.Announce.Serve(ctx, req, announceAddr, opts)
106 nodeAddrs := make([]krpc.NodeAddr, 0, len(peers))
107 for _, p := range peers {
112 case udp.AddrFamilyIpv4:
113 if !p.Addr().Unmap().Is4() {
116 ipBuf := p.Addr().As4()
118 case udp.AddrFamilyIpv6:
119 ipBuf := p.Addr().As16()
122 nodeAddrs = append(nodeAddrs, krpc.NodeAddr{
128 err = udp.Write(&buf, udp.ResponseHeader{
129 Action: udp.ActionAnnounce,
135 err = udp.Write(&buf, udp.AnnounceResponseHeader{})
139 b, err := udp.GetNodeAddrsCompactMarshaler(nodeAddrs, addrFamily).MarshalBinary()
141 err = fmt.Errorf("marshalling compact node addrs: %w", err)
145 n, err := me.SendResponse(buf.Bytes(), source)
150 err = io.ErrShortWrite
155 func (me *Server) handleConnect(ctx context.Context, source RequestSourceAddr, tid udp.TransactionId) error {
156 connId := randomConnectionId()
157 err := me.ConnTracker.Add(ctx, source.String(), connId)
159 err = fmt.Errorf("recording conn id: %w", err)
163 udp.Write(&buf, udp.ResponseHeader{
164 Action: udp.ActionConnect,
167 udp.Write(&buf, udp.ConnectionResponse{connId})
168 n, err := me.SendResponse(buf.Bytes(), source)
173 err = io.ErrShortWrite
178 func randomConnectionId() udp.ConnectionId {
180 _, err := rand.Read(b[:])
184 return binary.BigEndian.Uint64(b[:])
187 func RunSimple(ctx context.Context, s *Server, pc net.PacketConn, family udp.AddrFamily) error {
188 ctx, cancel := context.WithCancel(ctx)
192 n, addr, err := pc.ReadFrom(b[:])
197 err := s.HandleRequest(ctx, family, addr, b[:n])
199 log.Printf("error handling %v byte request from %v: %v", n, addr, err)