]> Sergey Matveev's repositories - btrtrc.git/blob - tracker/udp.go
Extract HTTP tracker client into separate package
[btrtrc.git] / tracker / udp.go
1 package tracker
2
3 import (
4         "encoding"
5         "encoding/binary"
6         "net"
7         "net/url"
8
9         "github.com/anacrolix/dht/v2/krpc"
10         "github.com/anacrolix/missinggo"
11         trHttp "github.com/anacrolix/torrent/tracker/http"
12         "github.com/anacrolix/torrent/tracker/udp"
13 )
14
15 type udpAnnounce struct {
16         url url.URL
17         a   *Announce
18 }
19
20 func (c *udpAnnounce) Close() error {
21         return nil
22 }
23
24 func (c *udpAnnounce) ipv6(conn net.Conn) bool {
25         if c.a.UdpNetwork == "udp6" {
26                 return true
27         }
28         rip := missinggo.AddrIP(conn.RemoteAddr())
29         return rip.To16() != nil && rip.To4() == nil
30 }
31
32 func (c *udpAnnounce) Do(req AnnounceRequest) (res AnnounceResponse, err error) {
33         conn, err := net.Dial(c.dialNetwork(), c.url.Host)
34         if err != nil {
35                 return
36         }
37         defer conn.Close()
38         if c.ipv6(conn) {
39                 // BEP 15
40                 req.IPAddress = 0
41         } else if req.IPAddress == 0 && c.a.ClientIp4.IP != nil {
42                 req.IPAddress = binary.BigEndian.Uint32(c.a.ClientIp4.IP.To4())
43         }
44         d := udp.Dispatcher{}
45         go func() {
46                 for {
47                         b := make([]byte, 0x800)
48                         n, err := conn.Read(b)
49                         if err != nil {
50                                 break
51                         }
52                         d.Dispatch(b[:n])
53                 }
54         }()
55         cl := udp.Client{
56                 Dispatcher: &d,
57                 Writer:     conn,
58         }
59         nas := func() interface {
60                 encoding.BinaryUnmarshaler
61                 NodeAddrs() []krpc.NodeAddr
62         } {
63                 if c.ipv6(conn) {
64                         return &krpc.CompactIPv6NodeAddrs{}
65                 } else {
66                         return &krpc.CompactIPv4NodeAddrs{}
67                 }
68         }()
69         h, err := cl.Announce(c.a.Context, req, nas, udp.Options{RequestUri: c.url.RequestURI()})
70         if err != nil {
71                 return
72         }
73         res.Interval = h.Interval
74         res.Leechers = h.Leechers
75         res.Seeders = h.Seeders
76         for _, cp := range nas.NodeAddrs() {
77                 res.Peers = append(res.Peers, trHttp.Peer{}.FromNodeAddr(cp))
78         }
79         return
80 }
81
82 func (c *udpAnnounce) dialNetwork() string {
83         if c.a.UdpNetwork != "" {
84                 return c.a.UdpNetwork
85         }
86         return "udp"
87 }
88
89 // TODO: Split on IPv6, as BEP 15 says response peer decoding depends on network in use.
90 func announceUDP(opt Announce, _url *url.URL) (AnnounceResponse, error) {
91         ua := udpAnnounce{
92                 url: *_url,
93                 a:   &opt,
94         }
95         defer ua.Close()
96         return ua.Do(opt.Request)
97 }