]> Sergey Matveev's repositories - btrtrc.git/blob - tracker/udp/conn-client.go
Extract protocol agnostic tracker Client
[btrtrc.git] / tracker / udp / conn-client.go
1 package udp
2
3 import (
4         "context"
5         "log"
6         "net"
7
8         "github.com/anacrolix/dht/v2/krpc"
9         "github.com/anacrolix/missinggo/v2"
10 )
11
12 type NewConnClientOpts struct {
13         Network string
14         Host    string
15         Ipv6    *bool
16 }
17
18 type ConnClient struct {
19         cl      Client
20         conn    net.Conn
21         d       Dispatcher
22         readErr error
23         ipv6    bool
24 }
25
26 func (cc *ConnClient) reader() {
27         for {
28                 b := make([]byte, 0x800)
29                 n, err := cc.conn.Read(b)
30                 if err != nil {
31                         // TODO: Do bad things to the dispatcher, and incoming calls to the client if we have a
32                         // read error.
33                         cc.readErr = err
34                         break
35                 }
36                 err = cc.d.Dispatch(b[:n])
37                 if err != nil {
38                         log.Printf("dispatching packet received on %v: %v", cc.conn, err)
39                 }
40         }
41 }
42
43 func ipv6(opt *bool, network string, conn net.Conn) bool {
44         if opt != nil {
45                 return *opt
46         }
47         switch network {
48         case "udp4":
49                 return false
50         case "udp6":
51                 return true
52         }
53         rip := missinggo.AddrIP(conn.RemoteAddr())
54         return rip.To16() != nil && rip.To4() == nil
55 }
56
57 func NewConnClient(opts NewConnClientOpts) (cc *ConnClient, err error) {
58         conn, err := net.Dial(opts.Network, opts.Host)
59         if err != nil {
60                 return
61         }
62         cc = &ConnClient{
63                 cl: Client{
64                         Writer: conn,
65                 },
66                 conn: conn,
67                 ipv6: ipv6(opts.Ipv6, opts.Network, conn),
68         }
69         cc.cl.Dispatcher = &cc.d
70         go cc.reader()
71         return
72 }
73
74 func (c *ConnClient) Close() error {
75         return c.conn.Close()
76 }
77
78 func (c *ConnClient) Announce(
79         ctx context.Context, req AnnounceRequest, opts Options,
80 ) (
81         h AnnounceResponseHeader, nas AnnounceResponsePeers, err error,
82 ) {
83         nas = func() AnnounceResponsePeers {
84                 if c.ipv6 {
85                         return &krpc.CompactIPv6NodeAddrs{}
86                 } else {
87                         return &krpc.CompactIPv4NodeAddrs{}
88                 }
89         }()
90         h, err = c.cl.Announce(ctx, req, nas, opts)
91         return
92 }