1 package udpTrackerServer
13 "github.com/anacrolix/dht/v2/krpc"
14 "github.com/anacrolix/log"
16 "github.com/anacrolix/torrent/tracker"
17 "github.com/anacrolix/torrent/tracker/udp"
20 type ConnectionTrackerAddr = string
22 type ConnectionTracker interface {
23 Add(ctx context.Context, addr ConnectionTrackerAddr, id udp.ConnectionId) error
24 Check(ctx context.Context, addr ConnectionTrackerAddr, id udp.ConnectionId) (bool, error)
27 type InfoHash = [20]byte
29 type AnnounceTracker = tracker.AnnounceTracker
32 ConnTracker ConnectionTracker
33 SendResponse func(data []byte, addr net.Addr) (int, error)
34 Announce tracker.AnnounceHandler
37 type RequestSourceAddr = net.Addr
39 func (me *Server) HandleRequest(
41 family udp.AddrFamily,
42 source RequestSourceAddr,
45 var h udp.RequestHeader
48 err := udp.Read(&r, &h)
50 err = fmt.Errorf("reading request header: %w", err)
54 case udp.ActionConnect:
55 err = me.handleConnect(ctx, source, h.TransactionId)
56 case udp.ActionAnnounce:
57 err = me.handleAnnounce(ctx, family, source, h.ConnectionId, h.TransactionId, &r)
59 err = fmt.Errorf("unimplemented")
62 err = fmt.Errorf("handling action %v: %w", h.Action, err)
67 func (me *Server) handleAnnounce(
69 addrFamily udp.AddrFamily,
70 source RequestSourceAddr,
71 connId udp.ConnectionId,
72 tid udp.TransactionId,
75 // Should we set a timeout of 10s or something for the entire response, so that we give up if a
78 ok, err := me.ConnTracker.Check(ctx, source.String(), connId)
80 err = fmt.Errorf("checking conn id: %w", err)
84 return fmt.Errorf("invalid connection id: %v", connId)
86 var req udp.AnnounceRequest
87 err = udp.Read(r, &req)
91 // TODO: This should be done asynchronously to responding to the announce.
92 announceAddr, err := netip.ParseAddrPort(source.String())
94 err = fmt.Errorf("converting source net.Addr to AnnounceAddr: %w", err)
97 peers, err := me.Announce.Serve(ctx, req, announceAddr)
101 nodeAddrs := make([]krpc.NodeAddr, 0, len(peers))
102 for _, p := range peers {
107 case udp.AddrFamilyIpv4:
108 if !p.Addr().Unmap().Is4() {
111 ipBuf := p.Addr().As4()
113 case udp.AddrFamilyIpv6:
114 ipBuf := p.Addr().As16()
117 nodeAddrs = append(nodeAddrs, krpc.NodeAddr{
123 err = udp.Write(&buf, udp.ResponseHeader{
124 Action: udp.ActionAnnounce,
130 err = udp.Write(&buf, udp.AnnounceResponseHeader{})
134 b, err := udp.GetNodeAddrsCompactMarshaler(nodeAddrs, addrFamily).MarshalBinary()
136 err = fmt.Errorf("marshalling compact node addrs: %w", err)
140 n, err := me.SendResponse(buf.Bytes(), source)
145 err = io.ErrShortWrite
150 func (me *Server) handleConnect(ctx context.Context, source RequestSourceAddr, tid udp.TransactionId) error {
151 connId := randomConnectionId()
152 err := me.ConnTracker.Add(ctx, source.String(), connId)
154 err = fmt.Errorf("recording conn id: %w", err)
158 udp.Write(&buf, udp.ResponseHeader{
159 Action: udp.ActionConnect,
162 udp.Write(&buf, udp.ConnectionResponse{connId})
163 n, err := me.SendResponse(buf.Bytes(), source)
168 err = io.ErrShortWrite
173 func randomConnectionId() udp.ConnectionId {
175 _, err := rand.Read(b[:])
179 return int64(binary.BigEndian.Uint64(b[:]))
182 func RunSimple(ctx context.Context, s *Server, pc net.PacketConn, family udp.AddrFamily) error {
183 ctx, cancel := context.WithCancel(ctx)
187 n, addr, err := pc.ReadFrom(b[:])
192 err := s.HandleRequest(ctx, family, addr, b[:n])
194 log.Printf("error handling %v byte request from %v: %v", n, addr, err)