package httpTrackerServer import ( "fmt" "net" "net/http" "net/netip" "net/url" "strconv" "github.com/anacrolix/dht/v2/krpc" "github.com/anacrolix/generics" "github.com/anacrolix/log" "github.com/anacrolix/torrent/bencode" "github.com/anacrolix/torrent/tracker" httpTracker "github.com/anacrolix/torrent/tracker/http" ) type Handler struct { Announce *tracker.AnnounceHandler // Called to derive an announcer's IP if non-nil. If not specified, the Request.RemoteAddr is // used. Necessary for instances running behind reverse proxies for example. RequestHost func(r *http.Request) (netip.Addr, error) } func unmarshalQueryKeyToArray(w http.ResponseWriter, key string, query url.Values) (ret [20]byte, ok bool) { str := query.Get(key) if len(str) != len(ret) { http.Error(w, fmt.Sprintf("%v has wrong length", key), http.StatusBadRequest) return } copy(ret[:], str) ok = true return } // Returns false if there was an error and it was served. func (me Handler) requestHostAddr(r *http.Request) (_ netip.Addr, err error) { if me.RequestHost != nil { return me.RequestHost(r) } host, _, err := net.SplitHostPort(r.RemoteAddr) if err != nil { return } return netip.ParseAddr(host) } var requestHeadersLogger = log.Default.WithNames("request", "headers") func (me Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { vs := r.URL.Query() var event tracker.AnnounceEvent err := event.UnmarshalText([]byte(vs.Get("event"))) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } infoHash, ok := unmarshalQueryKeyToArray(w, "info_hash", vs) if !ok { return } peerId, ok := unmarshalQueryKeyToArray(w, "peer_id", vs) if !ok { return } requestHeadersLogger.Levelf(log.Debug, "request RemoteAddr=%q, header=%q", r.RemoteAddr, r.Header) addr, err := me.requestHostAddr(r) if err != nil { log.Printf("error getting requester IP: %v", err) http.Error(w, "error determining your IP", http.StatusBadGateway) return } portU64, err := strconv.ParseUint(vs.Get("port"), 0, 16) addrPort := netip.AddrPortFrom(addr, uint16(portU64)) peers, err := me.Announce.Serve(r.Context(), tracker.AnnounceRequest{ InfoHash: infoHash, PeerId: peerId, Event: event, Port: addrPort.Port(), NumWant: -1, }, addrPort, tracker.GetPeersOpts{generics.Some[uint](200)}) if err != nil { log.Printf("error serving announce: %v", err) http.Error(w, "error handling announce", http.StatusInternalServerError) return } var resp httpTracker.HttpResponse resp.Interval = 5 * 60 resp.Peers.Compact = true for _, peer := range peers { if peer.Addr().Is4() { resp.Peers.List = append(resp.Peers.List, tracker.Peer{ IP: peer.Addr().AsSlice(), Port: int(peer.Port()), }) } else if peer.Addr().Is6() { resp.Peers6 = append(resp.Peers6, krpc.NodeAddr{ IP: peer.Addr().AsSlice(), Port: int(peer.Port()), }) } } err = bencode.NewEncoder(w).Encode(resp) if err != nil { log.Printf("error encoding and writing response body: %v", err) } }