This commit is contained in:
2018-11-04 15:58:15 +01:00
commit f956bcee28
1178 changed files with 584552 additions and 0 deletions

View File

@@ -0,0 +1,7 @@
package tracker
import (
"expvar"
)
var vars = expvar.NewMap("tracker")

140
vendor/github.com/anacrolix/torrent/tracker/http.go generated vendored Normal file
View File

@@ -0,0 +1,140 @@
package tracker
import (
"bytes"
"fmt"
"io"
"net/http"
"net/url"
"strconv"
"github.com/anacrolix/dht/krpc"
"github.com/anacrolix/missinggo/httptoo"
"github.com/anacrolix/torrent/bencode"
)
type httpResponse struct {
FailureReason string `bencode:"failure reason"`
Interval int32 `bencode:"interval"`
TrackerId string `bencode:"tracker id"`
Complete int32 `bencode:"complete"`
Incomplete int32 `bencode:"incomplete"`
Peers Peers `bencode:"peers"`
// BEP 7
Peers6 krpc.CompactIPv6NodeAddrs `bencode:"peers6"`
}
type Peers []Peer
func (me *Peers) UnmarshalBencode(b []byte) (err error) {
var _v interface{}
err = bencode.Unmarshal(b, &_v)
if err != nil {
return
}
switch v := _v.(type) {
case string:
vars.Add("http responses with string peers", 1)
var cnas krpc.CompactIPv4NodeAddrs
err = cnas.UnmarshalBinary([]byte(v))
if err != nil {
return
}
for _, cp := range cnas {
*me = append(*me, Peer{
IP: cp.IP[:],
Port: int(cp.Port),
})
}
return
case []interface{}:
vars.Add("http responses with list peers", 1)
for _, i := range v {
var p Peer
p.fromDictInterface(i.(map[string]interface{}))
*me = append(*me, p)
}
return
default:
vars.Add("http responses with unhandled peers type", 1)
err = fmt.Errorf("unsupported type: %T", _v)
return
}
}
func setAnnounceParams(_url *url.URL, ar *AnnounceRequest, opts Announce) {
q := _url.Query()
q.Set("info_hash", string(ar.InfoHash[:]))
q.Set("peer_id", string(ar.PeerId[:]))
// AFAICT, port is mandatory, and there's no implied port key.
q.Set("port", fmt.Sprintf("%d", ar.Port))
q.Set("uploaded", strconv.FormatInt(ar.Uploaded, 10))
q.Set("downloaded", strconv.FormatInt(ar.Downloaded, 10))
q.Set("left", strconv.FormatUint(ar.Left, 10))
if ar.Event != None {
q.Set("event", ar.Event.String())
}
// http://stackoverflow.com/questions/17418004/why-does-tracker-server-not-understand-my-request-bittorrent-protocol
q.Set("compact", "1")
// According to https://wiki.vuze.com/w/Message_Stream_Encryption. TODO:
// Take EncryptionPolicy or something like it as a parameter.
q.Set("supportcrypto", "1")
if opts.ClientIp4.IP != nil {
q.Set("ipv4", opts.ClientIp4.String())
}
if opts.ClientIp6.IP != nil {
q.Set("ipv6", opts.ClientIp6.String())
}
_url.RawQuery = q.Encode()
}
func announceHTTP(opt Announce, _url *url.URL) (ret AnnounceResponse, err error) {
_url = httptoo.CopyURL(_url)
setAnnounceParams(_url, &opt.Request, opt)
req, err := http.NewRequest("GET", _url.String(), nil)
req.Header.Set("User-Agent", opt.UserAgent)
req.Host = opt.HostHeader
resp, err := opt.HttpClient.Do(req)
if err != nil {
return
}
defer resp.Body.Close()
var buf bytes.Buffer
io.Copy(&buf, resp.Body)
if resp.StatusCode != 200 {
err = fmt.Errorf("response from tracker: %s: %s", resp.Status, buf.String())
return
}
var trackerResponse httpResponse
err = bencode.Unmarshal(buf.Bytes(), &trackerResponse)
if _, ok := err.(bencode.ErrUnusedTrailingBytes); ok {
err = nil
} else if err != nil {
err = fmt.Errorf("error decoding %q: %s", buf.Bytes(), err)
return
}
if trackerResponse.FailureReason != "" {
err = fmt.Errorf("tracker gave failure reason: %q", trackerResponse.FailureReason)
return
}
vars.Add("successful http announces", 1)
ret.Interval = trackerResponse.Interval
ret.Leechers = trackerResponse.Incomplete
ret.Seeders = trackerResponse.Complete
if len(trackerResponse.Peers) != 0 {
vars.Add("http responses with nonempty peers key", 1)
}
ret.Peers = trackerResponse.Peers
if len(trackerResponse.Peers6) != 0 {
vars.Add("http responses with nonempty peers6 key", 1)
}
for _, na := range trackerResponse.Peers6 {
ret.Peers = append(ret.Peers, Peer{
IP: na.IP,
Port: na.Port,
})
}
return
}

26
vendor/github.com/anacrolix/torrent/tracker/peer.go generated vendored Normal file
View File

@@ -0,0 +1,26 @@
package tracker
import (
"net"
"github.com/anacrolix/dht/krpc"
)
type Peer struct {
IP net.IP
Port int
ID []byte
}
// Set from the non-compact form in BEP 3.
func (p *Peer) fromDictInterface(d map[string]interface{}) {
p.IP = net.ParseIP(d["ip"].(string))
p.ID = []byte(d["peer id"].(string))
p.Port = int(d["port"].(int64))
}
func (p Peer) FromNodeAddr(na krpc.NodeAddr) Peer {
p.IP = na.IP
p.Port = na.Port
return p
}

124
vendor/github.com/anacrolix/torrent/tracker/server.go generated vendored Normal file
View File

@@ -0,0 +1,124 @@
package tracker
import (
"bytes"
"encoding"
"encoding/binary"
"fmt"
"math/rand"
"net"
"github.com/anacrolix/dht/krpc"
"github.com/anacrolix/missinggo"
)
type torrent struct {
Leechers int32
Seeders int32
Peers []krpc.NodeAddr
}
type server struct {
pc net.PacketConn
conns map[int64]struct{}
t map[[20]byte]torrent
}
func marshal(parts ...interface{}) (ret []byte, err error) {
var buf bytes.Buffer
for _, p := range parts {
err = binary.Write(&buf, binary.BigEndian, p)
if err != nil {
return
}
}
ret = buf.Bytes()
return
}
func (s *server) respond(addr net.Addr, rh ResponseHeader, parts ...interface{}) (err error) {
b, err := marshal(append([]interface{}{rh}, parts...)...)
if err != nil {
return
}
_, err = s.pc.WriteTo(b, addr)
return
}
func (s *server) newConn() (ret int64) {
ret = rand.Int63()
if s.conns == nil {
s.conns = make(map[int64]struct{})
}
s.conns[ret] = struct{}{}
return
}
func (s *server) serveOne() (err error) {
b := make([]byte, 0x10000)
n, addr, err := s.pc.ReadFrom(b)
if err != nil {
return
}
r := bytes.NewReader(b[:n])
var h RequestHeader
err = readBody(r, &h)
if err != nil {
return
}
switch h.Action {
case ActionConnect:
if h.ConnectionId != connectRequestConnectionId {
return
}
connId := s.newConn()
err = s.respond(addr, ResponseHeader{
ActionConnect,
h.TransactionId,
}, ConnectionResponse{
connId,
})
return
case ActionAnnounce:
if _, ok := s.conns[h.ConnectionId]; !ok {
s.respond(addr, ResponseHeader{
TransactionId: h.TransactionId,
Action: ActionError,
}, []byte("not connected"))
return
}
var ar AnnounceRequest
err = readBody(r, &ar)
if err != nil {
return
}
t := s.t[ar.InfoHash]
bm := func() encoding.BinaryMarshaler {
ip := missinggo.AddrIP(addr)
if ip.To4() != nil {
return krpc.CompactIPv4NodeAddrs(t.Peers)
}
return krpc.CompactIPv6NodeAddrs(t.Peers)
}()
b, err = bm.MarshalBinary()
if err != nil {
panic(err)
}
err = s.respond(addr, ResponseHeader{
TransactionId: h.TransactionId,
Action: ActionAnnounce,
}, AnnounceResponseHeader{
Interval: 900,
Leechers: t.Leechers,
Seeders: t.Seeders,
}, b)
return
default:
err = fmt.Errorf("unhandled action: %d", h.Action)
s.respond(addr, ResponseHeader{
TransactionId: h.TransactionId,
Action: ActionError,
}, []byte("unhandled action"))
return
}
}

81
vendor/github.com/anacrolix/torrent/tracker/tracker.go generated vendored Normal file
View File

@@ -0,0 +1,81 @@
package tracker
import (
"errors"
"net/http"
"net/url"
"github.com/anacrolix/dht/krpc"
)
// Marshalled as binary by the UDP client, so be careful making changes.
type AnnounceRequest struct {
InfoHash [20]byte
PeerId [20]byte
Downloaded int64
Left uint64
Uploaded int64
// Apparently this is optional. None can be used for announces done at
// regular intervals.
Event AnnounceEvent
IPAddress uint32
Key int32
NumWant int32 // How many peer addresses are desired. -1 for default.
Port uint16
} // 82 bytes
type AnnounceResponse struct {
Interval int32 // Minimum seconds the local peer should wait before next announce.
Leechers int32
Seeders int32
Peers []Peer
}
type AnnounceEvent int32
func (e AnnounceEvent) String() string {
// See BEP 3, "event".
return []string{"empty", "completed", "started", "stopped"}[e]
}
const (
None AnnounceEvent = iota
Completed // The local peer just completed the torrent.
Started // The local peer has just resumed this torrent.
Stopped // The local peer is leaving the swarm.
)
var (
ErrBadScheme = errors.New("unknown scheme")
)
type Announce struct {
TrackerUrl string
Request AnnounceRequest
HostHeader string
UserAgent string
HttpClient *http.Client
UdpNetwork string
// If the port is zero, it's assumed to be the same as the Request.Port
ClientIp4 krpc.NodeAddr
// If the port is zero, it's assumed to be the same as the Request.Port
ClientIp6 krpc.NodeAddr
}
// In an FP language with currying, what order what you put these params?
func (me Announce) Do() (res AnnounceResponse, err error) {
_url, err := url.Parse(me.TrackerUrl)
if err != nil {
return
}
switch _url.Scheme {
case "http", "https":
return announceHTTP(me, _url)
case "udp", "udp4", "udp6":
return announceUDP(me, _url)
default:
err = ErrBadScheme
return
}
}

298
vendor/github.com/anacrolix/torrent/tracker/udp.go generated vendored Normal file
View File

@@ -0,0 +1,298 @@
package tracker
import (
"bytes"
"encoding"
"encoding/binary"
"errors"
"fmt"
"io"
"math/rand"
"net"
"net/url"
"time"
"github.com/anacrolix/dht/krpc"
"github.com/anacrolix/missinggo"
"github.com/anacrolix/missinggo/pproffd"
)
type Action int32
const (
ActionConnect Action = iota
ActionAnnounce
ActionScrape
ActionError
connectRequestConnectionId = 0x41727101980
// BEP 41
optionTypeEndOfOptions = 0
optionTypeNOP = 1
optionTypeURLData = 2
)
type ConnectionRequest struct {
ConnectionId int64
Action int32
TransctionId int32
}
type ConnectionResponse struct {
ConnectionId int64
}
type ResponseHeader struct {
Action Action
TransactionId int32
}
type RequestHeader struct {
ConnectionId int64
Action Action
TransactionId int32
} // 16 bytes
type AnnounceResponseHeader struct {
Interval int32
Leechers int32
Seeders int32
}
func newTransactionId() int32 {
return int32(rand.Uint32())
}
func timeout(contiguousTimeouts int) (d time.Duration) {
if contiguousTimeouts > 8 {
contiguousTimeouts = 8
}
d = 15 * time.Second
for ; contiguousTimeouts > 0; contiguousTimeouts-- {
d *= 2
}
return
}
type udpAnnounce struct {
contiguousTimeouts int
connectionIdReceived time.Time
connectionId int64
socket net.Conn
url url.URL
a *Announce
}
func (c *udpAnnounce) Close() error {
if c.socket != nil {
return c.socket.Close()
}
return nil
}
func (c *udpAnnounce) ipv6() bool {
if c.a.UdpNetwork == "udp6" {
return true
}
rip := missinggo.AddrIP(c.socket.RemoteAddr())
return rip.To16() != nil && rip.To4() == nil
}
func (c *udpAnnounce) Do(req AnnounceRequest) (res AnnounceResponse, err error) {
err = c.connect()
if err != nil {
return
}
reqURI := c.url.RequestURI()
if c.ipv6() {
// BEP 15
req.IPAddress = 0
} else if req.IPAddress == 0 && c.a.ClientIp4.IP != nil {
req.IPAddress = binary.BigEndian.Uint32(c.a.ClientIp4.IP.To4())
}
// Clearly this limits the request URI to 255 bytes. BEP 41 supports
// longer but I'm not fussed.
options := append([]byte{optionTypeURLData, byte(len(reqURI))}, []byte(reqURI)...)
b, err := c.request(ActionAnnounce, req, options)
if err != nil {
return
}
var h AnnounceResponseHeader
err = readBody(b, &h)
if err != nil {
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
err = fmt.Errorf("error parsing announce response: %s", err)
return
}
res.Interval = h.Interval
res.Leechers = h.Leechers
res.Seeders = h.Seeders
nas := func() interface {
encoding.BinaryUnmarshaler
NodeAddrs() []krpc.NodeAddr
} {
if c.ipv6() {
return &krpc.CompactIPv6NodeAddrs{}
} else {
return &krpc.CompactIPv4NodeAddrs{}
}
}()
err = nas.UnmarshalBinary(b.Bytes())
if err != nil {
return
}
for _, cp := range nas.NodeAddrs() {
res.Peers = append(res.Peers, Peer{}.FromNodeAddr(cp))
}
return
}
// body is the binary serializable request body. trailer is optional data
// following it, such as for BEP 41.
func (c *udpAnnounce) write(h *RequestHeader, body interface{}, trailer []byte) (err error) {
var buf bytes.Buffer
err = binary.Write(&buf, binary.BigEndian, h)
if err != nil {
panic(err)
}
if body != nil {
err = binary.Write(&buf, binary.BigEndian, body)
if err != nil {
panic(err)
}
}
_, err = buf.Write(trailer)
if err != nil {
return
}
n, err := c.socket.Write(buf.Bytes())
if err != nil {
return
}
if n != buf.Len() {
panic("write should send all or error")
}
return
}
func read(r io.Reader, data interface{}) error {
return binary.Read(r, binary.BigEndian, data)
}
func write(w io.Writer, data interface{}) error {
return binary.Write(w, binary.BigEndian, data)
}
// args is the binary serializable request body. trailer is optional data
// following it, such as for BEP 41.
func (c *udpAnnounce) request(action Action, args interface{}, options []byte) (responseBody *bytes.Buffer, err error) {
tid := newTransactionId()
err = c.write(&RequestHeader{
ConnectionId: c.connectionId,
Action: action,
TransactionId: tid,
}, args, options)
if err != nil {
return
}
c.socket.SetReadDeadline(time.Now().Add(timeout(c.contiguousTimeouts)))
b := make([]byte, 0x800) // 2KiB
for {
var n int
n, err = c.socket.Read(b)
if opE, ok := err.(*net.OpError); ok {
if opE.Timeout() {
c.contiguousTimeouts++
return
}
}
if err != nil {
return
}
buf := bytes.NewBuffer(b[:n])
var h ResponseHeader
err = binary.Read(buf, binary.BigEndian, &h)
switch err {
case io.ErrUnexpectedEOF:
continue
case nil:
default:
return
}
if h.TransactionId != tid {
continue
}
c.contiguousTimeouts = 0
if h.Action == ActionError {
err = errors.New(buf.String())
}
responseBody = buf
return
}
}
func readBody(r io.Reader, data ...interface{}) (err error) {
for _, datum := range data {
err = binary.Read(r, binary.BigEndian, datum)
if err != nil {
break
}
}
return
}
func (c *udpAnnounce) connected() bool {
return !c.connectionIdReceived.IsZero() && time.Now().Before(c.connectionIdReceived.Add(time.Minute))
}
func (c *udpAnnounce) dialNetwork() string {
if c.a.UdpNetwork != "" {
return c.a.UdpNetwork
}
return "udp"
}
func (c *udpAnnounce) connect() (err error) {
if c.connected() {
return nil
}
c.connectionId = connectRequestConnectionId
if c.socket == nil {
hmp := missinggo.SplitHostMaybePort(c.url.Host)
if hmp.NoPort {
hmp.NoPort = false
hmp.Port = 80
}
c.socket, err = net.Dial(c.dialNetwork(), hmp.String())
if err != nil {
return
}
c.socket = pproffd.WrapNetConn(c.socket)
}
b, err := c.request(ActionConnect, nil, nil)
if err != nil {
return
}
var res ConnectionResponse
err = readBody(b, &res)
if err != nil {
return
}
c.connectionId = res.ConnectionId
c.connectionIdReceived = time.Now()
return
}
// TODO: Split on IPv6, as BEP 15 says response peer decoding depends on
// network in use.
func announceUDP(opt Announce, _url *url.URL) (AnnounceResponse, error) {
ua := udpAnnounce{
url: *_url,
a: &opt,
}
defer ua.Close()
return ua.Do(opt.Request)
}