-
Notifications
You must be signed in to change notification settings - Fork 0
/
dht.go
220 lines (188 loc) · 4.63 KB
/
dht.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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
package dht
import (
_ "crypto/sha1"
"errors"
"math/big"
"math/rand"
"net"
"strconv"
"time"
"github.com/anacrolix/torrent/iplist"
)
const (
maxNodes = 320
)
var (
queryResendEvery = 5 * time.Second
)
var maxDistance big.Int
func init() {
var zero big.Int
maxDistance.SetBit(&zero, 160, 1)
}
// Uniquely identifies a transaction to us.
type transactionKey struct {
RemoteAddr string // host:port
T string // The KRPC transaction ID.
}
// ServerConfig allows to set up a configuration of the `Server` instance
// to be created with NewServer
type ServerConfig struct {
// Listen address. Used if Conn is nil.
Addr string
// Set NodeId Manually. Caller must ensure that, if NodeId does not
// conform to DHT Security Extensions, that NoSecurity is also set. This
// should be given as a HEX string.
NodeIdHex string
Conn net.PacketConn
// Don't respond to queries from other nodes.
Passive bool
// DHT Bootstrap nodes
BootstrapNodes []string
// Disable bootstrapping from global servers even if given no BootstrapNodes.
// This creates a solitary node that awaits other nodes; it's only useful if
// you're creating your own DHT and want to avoid accidental crossover, without
// spoofing a bootstrap node and filling your logs with connection errors.
NoDefaultBootstrap bool
// Disable the DHT security extension:
// http://www.libtorrent.org/dht_sec.html.
NoSecurity bool
// Initial IP blocklist to use. Applied before serving and bootstrapping
// begins.
IPBlocklist iplist.Ranger
// Used to secure the server's ID. Defaults to the Conn's LocalAddr().
PublicIP net.IP
OnQuery func(*Msg, net.Addr) bool
}
// ServerStats instance is returned by Server.Stats() and stores Server metrics
type ServerStats struct {
// Count of nodes in the node table that responded to our last query or
// haven't yet been queried.
GoodNodes int
// Count of nodes in the node table.
Nodes int
// Transactions awaiting a response.
OutstandingTransactions int
// Individual announce_peer requests that got a success response.
ConfirmedAnnounces int
// Nodes that have been blocked.
BadNodes uint
}
func makeSocket(addr string) (socket *net.UDPConn, err error) {
addr_, err := net.ResolveUDPAddr("", addr)
if err != nil {
return
}
socket, err = net.ListenUDP("udp", addr_)
return
}
type nodeID struct {
i big.Int
set bool
}
func (nid *nodeID) IsUnset() bool {
return !nid.set
}
func nodeIDFromString(s string) (ret nodeID) {
if s == "" {
return
}
ret.i.SetBytes([]byte(s))
ret.set = true
return
}
func (nid0 *nodeID) Distance(nid1 *nodeID) (ret big.Int) {
if nid0.IsUnset() != nid1.IsUnset() {
ret = maxDistance
return
}
ret.Xor(&nid0.i, &nid1.i)
return
}
func (nid *nodeID) ByteString() string {
var buf [20]byte
b := nid.i.Bytes()
copy(buf[20-len(b):], b)
return string(buf[:])
}
type node struct {
addr Addr
id nodeID
announceToken string
lastGotQuery time.Time
lastGotResponse time.Time
lastSentQuery time.Time
}
func (n *node) IsSecure() bool {
if n.id.IsUnset() {
return false
}
return NodeIdSecure(n.id.ByteString(), n.addr.UDPAddr().IP)
}
func (n *node) idString() string {
return n.id.ByteString()
}
func (n *node) SetIDFromBytes(b []byte) {
if len(b) != 20 {
panic(b)
}
n.id.i.SetBytes(b)
n.id.set = true
}
func (n *node) SetIDFromString(s string) {
n.SetIDFromBytes([]byte(s))
}
func (n *node) IDNotSet() bool {
return n.id.i.Int64() == 0
}
func (n *node) NodeInfo() (ret NodeInfo) {
ret.Addr = n.addr
if n := copy(ret.ID[:], n.idString()); n != 20 {
panic(n)
}
return
}
func (n *node) DefinitelyGood() bool {
if len(n.idString()) != 20 {
return false
}
// No reason to think ill of them if they've never been queried.
if n.lastSentQuery.IsZero() {
return true
}
// They answered our last query.
if n.lastSentQuery.Before(n.lastGotResponse) {
return true
}
return true
}
func jitterDuration(average time.Duration, plusMinus time.Duration) time.Duration {
return average - plusMinus/2 + time.Duration(rand.Int63n(int64(plusMinus)))
}
type Peer struct {
IP net.IP
Port int
}
func (me *Peer) String() string {
return net.JoinHostPort(me.IP.String(), strconv.FormatInt(int64(me.Port), 10))
}
func bootstrapAddrs(nodeAddrs []string) (addrs []*net.UDPAddr, err error) {
bootstrapNodes := nodeAddrs
if len(bootstrapNodes) == 0 {
bootstrapNodes = []string{
"router.utorrent.com:6881",
"router.bittorrent.com:6881",
}
}
for _, addrStr := range bootstrapNodes {
udpAddr, err := net.ResolveUDPAddr("udp4", addrStr)
if err != nil {
continue
}
addrs = append(addrs, udpAddr)
}
if len(addrs) == 0 {
err = errors.New("nothing resolved")
}
return
}