package tracker import ( "bytes" "encoding/binary" "io/ioutil" "net" "net/http" "time" "dim13.org/btget/bencode" "dim13.org/btget/query" ) type Event string const ( NoEvent Event = "" Started Event = "started" Stopped Event = "stopped" Completed Event = "completed" ) type Request struct { InfoHash []byte `query:"info_hash"` // info_hash PeerID []byte `query:"peer_id"` // peer_id Port int `query:"port,optional"` Uploaded int `query:"uploaded,optional"` Downloaded int `query:"downloaded,optional"` Left int `query:"left,optional"` Compact bool `query:"compact,optional"` // always true NoPeerID bool `query:"no_peer_id,optional"` Event Event `query:"event,optional"` IP net.IPAddr `query:"ip,optional"` NumWant int `query:"numwant,optional"` Key []byte `query:"key,optional"` TrackerID []byte `query:"tracker_id,optional"` } // we support only compact mode type Response struct { Complete int `bencode:"complete"` FalureReason string `bencode:"failure reason"` Incomplete int `bencode:"incomplete"` Interval int `bencode:"interval"` MinInterval int `bencode:"min interval"` Peers []byte `bencode:"peers"` Peers6 []byte `bencode:"peers6"` TrackerId string `bencode:"tracker id"` WarningMessage string `bencode:"warning message"` } func (r Request) Get(announce string) (Response, error) { fail := func(err error) (Response, error) { return Response{}, err } q, err := query.Marshal(r) if err != nil { return fail(err) } resp, err := http.Get(announce + q) if err != nil { return fail(err) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { return fail(err) } var res Response err = bencode.Unmarshal(body, &res) if err != nil { return fail(err) } return res, nil } func (r Response) IntervalDuration() time.Duration { return time.Duration(r.Interval) * time.Second } func (r Response) PeerAddr() ([]*net.TCPAddr, error) { return peerAddr(r.Peers) } func peerAddr(b []byte) ([]*net.TCPAddr, error) { n := len(b) / 6 a := make([]*net.TCPAddr, n) var port uint16 for i := 0; i < n; i++ { off := i * 6 buf := bytes.NewReader(b[off+4 : off+6]) err := binary.Read(buf, binary.BigEndian, &port) if err != nil { return nil, err } a[i] = &net.TCPAddr{ IP: net.IP(b[off : off+4]), Port: int(port), } } return a, nil }