-
-
Notifications
You must be signed in to change notification settings - Fork 613
/
conn-client.go
132 lines (118 loc) · 2.82 KB
/
conn-client.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package udp
import (
"context"
"net"
"github.com/anacrolix/log"
"github.com/anacrolix/missinggo/v2"
)
type listenPacketFunc func(network, addr string) (net.PacketConn, error)
type NewConnClientOpts struct {
// The network to operate to use, such as "udp4", "udp", "udp6".
Network string
// Tracker address
Host string
// If non-nil, forces either IPv4 or IPv6 in the UDP tracker wire protocol.
Ipv6 *bool
// Logger to use for internal errors.
Logger log.Logger
// Custom function to use as a substitute for net.ListenPacket
ListenPacket listenPacketFunc
}
// Manages a Client with a specific connection.
type ConnClient struct {
Client Client
conn net.PacketConn
d Dispatcher
readErr error
closed bool
newOpts NewConnClientOpts
}
func (cc *ConnClient) reader() {
b := make([]byte, 0x800)
for {
n, addr, err := cc.conn.ReadFrom(b)
if err != nil {
// TODO: Do bad things to the dispatcher, and incoming calls to the client if we have a
// read error.
cc.readErr = err
if !cc.closed {
panic(err)
}
break
}
err = cc.d.Dispatch(b[:n], addr)
if err != nil {
cc.newOpts.Logger.Levelf(log.Debug, "dispatching packet received on %v: %v", cc.conn.LocalAddr(), err)
}
}
}
func ipv6(opt *bool, network string, remoteAddr net.Addr) bool {
if opt != nil {
return *opt
}
switch network {
case "udp4":
return false
case "udp6":
return true
}
rip := missinggo.AddrIP(remoteAddr)
return rip.To16() != nil && rip.To4() == nil
}
// Allows a UDP Client to write packets to an endpoint without knowing about the network specifics.
type clientWriter struct {
pc net.PacketConn
network string
address string
}
func (me clientWriter) Write(p []byte) (n int, err error) {
addr, err := net.ResolveUDPAddr(me.network, me.address)
if err != nil {
return
}
return me.pc.WriteTo(p, addr)
}
func NewConnClient(opts NewConnClientOpts) (cc *ConnClient, err error) {
var conn net.PacketConn
if opts.ListenPacket != nil {
conn, err = opts.ListenPacket(opts.Network, ":0")
} else {
conn, err = net.ListenPacket(opts.Network, ":0")
}
if err != nil {
return
}
if opts.Logger.IsZero() {
opts.Logger = log.Default
}
cc = &ConnClient{
Client: Client{
Writer: clientWriter{
pc: conn,
network: opts.Network,
address: opts.Host,
},
},
conn: conn,
newOpts: opts,
}
cc.Client.Dispatcher = &cc.d
go cc.reader()
return
}
func (cc *ConnClient) Close() error {
cc.closed = true
return cc.conn.Close()
}
func (cc *ConnClient) Announce(
ctx context.Context, req AnnounceRequest, opts Options,
) (
h AnnounceResponseHeader, nas AnnounceResponsePeers, err error,
) {
return cc.Client.Announce(ctx, req, opts, func(addr net.Addr) bool {
return ipv6(cc.newOpts.Ipv6, cc.newOpts.Network, addr)
})
}
func (cc *ConnClient) LocalAddr() net.Addr {
return cc.conn.LocalAddr()
}