2014-05-24 06:51:56 +00:00
|
|
|
package dht
|
|
|
|
|
|
|
|
import (
|
2014-06-24 13:18:30 +00:00
|
|
|
_ "crypto/sha1"
|
2014-05-25 11:34:29 +00:00
|
|
|
"errors"
|
2014-11-17 07:47:24 +00:00
|
|
|
"math/big"
|
2014-12-07 03:21:20 +00:00
|
|
|
"math/rand"
|
2014-05-24 06:51:56 +00:00
|
|
|
"net"
|
2015-08-17 09:52:47 +00:00
|
|
|
"strconv"
|
2014-05-24 06:51:56 +00:00
|
|
|
"time"
|
2014-12-28 01:51:09 +00:00
|
|
|
|
2015-03-20 05:37:44 +00:00
|
|
|
"github.com/anacrolix/torrent/iplist"
|
2014-05-24 06:51:56 +00:00
|
|
|
)
|
|
|
|
|
2015-02-21 04:00:48 +00:00
|
|
|
const (
|
2015-10-23 01:41:45 +00:00
|
|
|
maxNodes = 320
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2015-02-21 04:00:48 +00:00
|
|
|
queryResendEvery = 5 * time.Second
|
|
|
|
)
|
2014-12-09 01:09:11 +00:00
|
|
|
|
2015-12-06 14:56:46 +00:00
|
|
|
var maxDistance big.Int
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
var zero big.Int
|
|
|
|
maxDistance.SetBit(&zero, 160, 1)
|
|
|
|
}
|
|
|
|
|
2014-12-08 22:59:25 +00:00
|
|
|
// Uniquely identifies a transaction to us.
|
|
|
|
type transactionKey struct {
|
|
|
|
RemoteAddr string // host:port
|
|
|
|
T string // The KRPC transaction ID.
|
|
|
|
}
|
|
|
|
|
2015-12-05 20:40:41 +00:00
|
|
|
// ServerConfig allows to set up a configuration of the `Server` instance
|
|
|
|
// to be created with NewServer
|
2014-08-21 08:07:06 +00:00
|
|
|
type ServerConfig struct {
|
2015-12-16 04:13:32 +00:00
|
|
|
// Listen address. Used if Conn is nil.
|
|
|
|
Addr string
|
2015-12-16 04:15:59 +00:00
|
|
|
|
|
|
|
// 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
|
|
|
|
|
2015-04-01 06:29:55 +00:00
|
|
|
Conn net.PacketConn
|
|
|
|
// Don't respond to queries from other nodes.
|
|
|
|
Passive bool
|
2015-04-02 22:35:30 +00:00
|
|
|
// DHT Bootstrap nodes
|
|
|
|
BootstrapNodes []string
|
2015-12-16 04:13:32 +00:00
|
|
|
// 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
|
|
|
|
|
2015-05-20 12:23:50 +00:00
|
|
|
// Disable the DHT security extension:
|
|
|
|
// http://www.libtorrent.org/dht_sec.html.
|
|
|
|
NoSecurity bool
|
2015-06-02 13:58:49 +00:00
|
|
|
// Initial IP blocklist to use. Applied before serving and bootstrapping
|
|
|
|
// begins.
|
2015-09-23 08:25:22 +00:00
|
|
|
IPBlocklist iplist.Ranger
|
2015-08-03 14:37:16 +00:00
|
|
|
// Used to secure the server's ID. Defaults to the Conn's LocalAddr().
|
|
|
|
PublicIP net.IP
|
2015-12-16 04:20:37 +00:00
|
|
|
|
|
|
|
OnQuery func(*Msg, net.Addr) bool
|
2015-04-01 06:29:55 +00:00
|
|
|
}
|
|
|
|
|
2015-12-05 20:40:41 +00:00
|
|
|
// ServerStats instance is returned by Server.Stats() and stores Server metrics
|
2015-04-01 06:29:55 +00:00
|
|
|
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
|
2015-08-03 14:31:53 +00:00
|
|
|
// Nodes that have been blocked.
|
|
|
|
BadNodes uint
|
2015-04-01 06:29:55 +00:00
|
|
|
}
|
|
|
|
|
2014-08-21 08:07:06 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2014-12-26 06:21:48 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2015-05-20 12:23:50 +00:00
|
|
|
func (nid *nodeID) ByteString() string {
|
|
|
|
var buf [20]byte
|
|
|
|
b := nid.i.Bytes()
|
|
|
|
copy(buf[20-len(b):], b)
|
|
|
|
return string(buf[:])
|
2014-12-26 06:21:48 +00:00
|
|
|
}
|
|
|
|
|
2015-04-01 06:29:55 +00:00
|
|
|
type node struct {
|
2016-02-23 14:50:15 +00:00
|
|
|
addr Addr
|
2014-12-26 06:21:48 +00:00
|
|
|
id nodeID
|
2014-11-16 19:08:33 +00:00
|
|
|
announceToken string
|
2014-12-07 03:21:20 +00:00
|
|
|
|
|
|
|
lastGotQuery time.Time
|
|
|
|
lastGotResponse time.Time
|
|
|
|
lastSentQuery time.Time
|
2014-05-24 06:51:56 +00:00
|
|
|
}
|
|
|
|
|
2015-05-20 12:23:50 +00:00
|
|
|
func (n *node) IsSecure() bool {
|
|
|
|
if n.id.IsUnset() {
|
|
|
|
return false
|
|
|
|
}
|
2015-08-04 16:37:43 +00:00
|
|
|
return NodeIdSecure(n.id.ByteString(), n.addr.IP())
|
2015-05-20 12:23:50 +00:00
|
|
|
}
|
|
|
|
|
2015-04-01 06:29:55 +00:00
|
|
|
func (n *node) idString() string {
|
2015-05-20 12:23:50 +00:00
|
|
|
return n.id.ByteString()
|
2014-12-26 06:21:48 +00:00
|
|
|
}
|
|
|
|
|
2015-04-01 06:29:55 +00:00
|
|
|
func (n *node) SetIDFromBytes(b []byte) {
|
2015-05-20 12:23:50 +00:00
|
|
|
if len(b) != 20 {
|
|
|
|
panic(b)
|
|
|
|
}
|
2014-12-26 06:21:48 +00:00
|
|
|
n.id.i.SetBytes(b)
|
|
|
|
n.id.set = true
|
|
|
|
}
|
|
|
|
|
2015-04-01 06:29:55 +00:00
|
|
|
func (n *node) SetIDFromString(s string) {
|
2015-05-20 12:23:50 +00:00
|
|
|
n.SetIDFromBytes([]byte(s))
|
2014-12-26 06:21:48 +00:00
|
|
|
}
|
|
|
|
|
2015-04-01 06:29:55 +00:00
|
|
|
func (n *node) IDNotSet() bool {
|
2014-12-26 06:21:48 +00:00
|
|
|
return n.id.i.Int64() == 0
|
|
|
|
}
|
|
|
|
|
2015-04-01 06:29:55 +00:00
|
|
|
func (n *node) NodeInfo() (ret NodeInfo) {
|
2014-07-11 15:24:01 +00:00
|
|
|
ret.Addr = n.addr
|
2014-12-26 06:21:48 +00:00
|
|
|
if n := copy(ret.ID[:], n.idString()); n != 20 {
|
2014-07-11 15:24:01 +00:00
|
|
|
panic(n)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2015-04-01 06:29:55 +00:00
|
|
|
func (n *node) DefinitelyGood() bool {
|
2014-12-26 06:21:48 +00:00
|
|
|
if len(n.idString()) != 20 {
|
2014-05-27 06:28:56 +00:00
|
|
|
return false
|
|
|
|
}
|
2014-12-09 03:57:53 +00:00
|
|
|
// No reason to think ill of them if they've never been queried.
|
2014-12-07 03:21:20 +00:00
|
|
|
if n.lastSentQuery.IsZero() {
|
2014-11-18 00:02:16 +00:00
|
|
|
return true
|
|
|
|
}
|
2014-12-07 03:21:20 +00:00
|
|
|
// They answered our last query.
|
|
|
|
if n.lastSentQuery.Before(n.lastGotResponse) {
|
2014-11-18 00:02:16 +00:00
|
|
|
return true
|
|
|
|
}
|
2014-05-27 06:28:56 +00:00
|
|
|
return true
|
|
|
|
}
|
2015-12-16 04:13:32 +00:00
|
|
|
|
2014-12-07 03:21:20 +00:00
|
|
|
func jitterDuration(average time.Duration, plusMinus time.Duration) time.Duration {
|
|
|
|
return average - plusMinus/2 + time.Duration(rand.Int63n(int64(plusMinus)))
|
|
|
|
}
|
|
|
|
|
2015-08-17 09:52:47 +00:00
|
|
|
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))
|
|
|
|
}
|
|
|
|
|
2015-04-02 22:35:30 +00:00
|
|
|
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 {
|
2014-12-19 23:09:11 +00:00
|
|
|
udpAddr, err := net.ResolveUDPAddr("udp4", addrStr)
|
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
addrs = append(addrs, udpAddr)
|
|
|
|
}
|
|
|
|
if len(addrs) == 0 {
|
|
|
|
err = errors.New("nothing resolved")
|
|
|
|
}
|
|
|
|
return
|
2014-11-18 18:38:13 +00:00
|
|
|
}
|