2014-05-24 06:51:56 +00:00
|
|
|
package dht
|
|
|
|
|
|
|
|
import (
|
2014-12-26 06:21:48 +00:00
|
|
|
"bitbucket.org/anacrolix/go.torrent/iplist"
|
|
|
|
"bitbucket.org/anacrolix/go.torrent/logonce"
|
|
|
|
"bitbucket.org/anacrolix/go.torrent/util"
|
|
|
|
"bitbucket.org/anacrolix/sync"
|
2014-06-24 13:18:30 +00:00
|
|
|
"crypto"
|
|
|
|
_ "crypto/sha1"
|
2014-05-24 06:51:56 +00:00
|
|
|
"encoding/binary"
|
2014-05-25 11:34:29 +00:00
|
|
|
"errors"
|
2014-05-24 06:51:56 +00:00
|
|
|
"fmt"
|
2014-12-26 06:21:48 +00:00
|
|
|
"github.com/anacrolix/libtorgo/bencode"
|
2014-05-24 06:51:56 +00:00
|
|
|
"io"
|
|
|
|
"log"
|
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"
|
2014-06-24 13:18:30 +00:00
|
|
|
"os"
|
2014-05-24 06:51:56 +00:00
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
2014-12-09 01:09:11 +00:00
|
|
|
const maxNodes = 10000
|
|
|
|
|
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.
|
|
|
|
}
|
|
|
|
|
2014-05-24 06:51:56 +00:00
|
|
|
type Server struct {
|
2014-08-21 08:07:06 +00:00
|
|
|
id string
|
2014-11-17 03:22:29 +00:00
|
|
|
socket net.PacketConn
|
2014-12-08 22:59:25 +00:00
|
|
|
transactions map[transactionKey]*transaction
|
2014-05-24 06:51:56 +00:00
|
|
|
transactionIDInt uint64
|
2014-11-17 03:22:29 +00:00
|
|
|
nodes map[string]*Node // Keyed by dHTAddr.String().
|
2014-05-27 06:28:56 +00:00
|
|
|
mu sync.Mutex
|
2014-07-09 14:13:54 +00:00
|
|
|
closed chan struct{}
|
2014-11-28 18:04:38 +00:00
|
|
|
passive bool // Don't respond to queries.
|
2014-11-29 01:41:53 +00:00
|
|
|
ipBlockList *iplist.IPList
|
2014-11-16 19:08:33 +00:00
|
|
|
|
|
|
|
NumConfirmedAnnounces int
|
2014-07-09 14:13:54 +00:00
|
|
|
}
|
|
|
|
|
2014-11-17 03:22:29 +00:00
|
|
|
type dHTAddr interface {
|
|
|
|
net.Addr
|
2014-12-26 06:21:48 +00:00
|
|
|
UDPAddr() *net.UDPAddr
|
2014-11-17 03:22:29 +00:00
|
|
|
}
|
|
|
|
|
2014-12-26 06:21:48 +00:00
|
|
|
type cachedAddr struct {
|
|
|
|
a net.Addr
|
|
|
|
s string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ca cachedAddr) Network() string {
|
|
|
|
return ca.a.Network()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ca cachedAddr) String() string {
|
|
|
|
return ca.s
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ca cachedAddr) UDPAddr() *net.UDPAddr {
|
|
|
|
return ca.a.(*net.UDPAddr)
|
|
|
|
}
|
|
|
|
|
|
|
|
func newDHTAddr(addr *net.UDPAddr) dHTAddr {
|
|
|
|
return cachedAddr{addr, addr.String()}
|
2014-11-17 03:22:29 +00:00
|
|
|
}
|
|
|
|
|
2014-08-21 08:07:06 +00:00
|
|
|
type ServerConfig struct {
|
2014-11-28 18:04:38 +00:00
|
|
|
Addr string
|
|
|
|
Conn net.PacketConn
|
|
|
|
Passive bool // Don't respond to queries.
|
2014-08-21 08:07:06 +00:00
|
|
|
}
|
|
|
|
|
2014-12-07 03:19:02 +00:00
|
|
|
type serverStats struct {
|
|
|
|
NumGoodNodes int
|
|
|
|
NumNodes int
|
|
|
|
NumOutstandingTransactions int
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Server) Stats() (ss serverStats) {
|
|
|
|
s.mu.Lock()
|
|
|
|
defer s.mu.Unlock()
|
|
|
|
for _, n := range s.nodes {
|
2014-12-09 01:09:11 +00:00
|
|
|
if n.DefinitelyGood() {
|
2014-12-07 03:19:02 +00:00
|
|
|
ss.NumGoodNodes++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ss.NumNodes = len(s.nodes)
|
|
|
|
ss.NumOutstandingTransactions = len(s.transactions)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-08-21 08:07:06 +00:00
|
|
|
func (s *Server) LocalAddr() net.Addr {
|
|
|
|
return s.socket.LocalAddr()
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewServer(c *ServerConfig) (s *Server, err error) {
|
2014-08-21 11:12:50 +00:00
|
|
|
if c == nil {
|
|
|
|
c = &ServerConfig{}
|
|
|
|
}
|
2014-08-21 08:07:06 +00:00
|
|
|
s = &Server{}
|
2014-11-17 05:27:01 +00:00
|
|
|
if c.Conn != nil {
|
|
|
|
s.socket = c.Conn
|
|
|
|
} else {
|
|
|
|
s.socket, err = makeSocket(c.Addr)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
2014-08-21 08:07:06 +00:00
|
|
|
}
|
2014-11-28 18:04:38 +00:00
|
|
|
s.passive = c.Passive
|
2014-08-21 08:07:06 +00:00
|
|
|
err = s.init()
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
go func() {
|
2014-08-21 11:12:50 +00:00
|
|
|
err := s.serve()
|
|
|
|
select {
|
|
|
|
case <-s.closed:
|
|
|
|
return
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2014-08-21 08:07:06 +00:00
|
|
|
}()
|
|
|
|
go func() {
|
|
|
|
err := s.bootstrap()
|
|
|
|
if err != nil {
|
2014-11-29 01:41:53 +00:00
|
|
|
log.Printf("error bootstrapping DHT: %s", err)
|
2014-08-21 08:07:06 +00:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-07-09 14:13:54 +00:00
|
|
|
func (s *Server) String() string {
|
2014-08-21 08:07:06 +00:00
|
|
|
return fmt.Sprintf("dht server on %s", s.socket.LocalAddr())
|
2014-05-24 06:51:56 +00:00
|
|
|
}
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
func (nid *nodeID) String() string {
|
|
|
|
return string(nid.i.Bytes())
|
|
|
|
}
|
|
|
|
|
2014-05-24 06:51:56 +00:00
|
|
|
type Node struct {
|
2014-11-17 03:22:29 +00:00
|
|
|
addr dHTAddr
|
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
|
|
|
}
|
|
|
|
|
2014-12-26 06:21:48 +00:00
|
|
|
func (n *Node) idString() string {
|
|
|
|
return n.id.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *Node) SetIDFromBytes(b []byte) {
|
|
|
|
n.id.i.SetBytes(b)
|
|
|
|
n.id.set = true
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *Node) SetIDFromString(s string) {
|
|
|
|
n.id.i.SetBytes([]byte(s))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *Node) IDNotSet() bool {
|
|
|
|
return n.id.i.Int64() == 0
|
|
|
|
}
|
|
|
|
|
2014-07-11 15:24:01 +00:00
|
|
|
func (n *Node) NodeInfo() (ret NodeInfo) {
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2014-12-09 01:09:11 +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
|
|
|
|
}
|
|
|
|
|
2014-05-24 06:51:56 +00:00
|
|
|
type Msg map[string]interface{}
|
|
|
|
|
|
|
|
var _ fmt.Stringer = Msg{}
|
|
|
|
|
|
|
|
func (m Msg) String() string {
|
|
|
|
return fmt.Sprintf("%#v", m)
|
|
|
|
}
|
|
|
|
|
2014-07-16 07:10:17 +00:00
|
|
|
func (m Msg) T() (t string) {
|
|
|
|
tif, ok := m["t"]
|
|
|
|
if !ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
t, _ = tif.(string)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-12-26 06:21:48 +00:00
|
|
|
func (m Msg) ID() string {
|
|
|
|
defer func() {
|
|
|
|
recover()
|
|
|
|
}()
|
|
|
|
return m[m["y"].(string)].(map[string]interface{})["id"].(string)
|
|
|
|
}
|
|
|
|
|
2014-11-18 18:38:13 +00:00
|
|
|
func (m Msg) Nodes() []NodeInfo {
|
|
|
|
var r findNodeResponse
|
|
|
|
if err := r.UnmarshalKRPCMsg(m); err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return r.Nodes
|
|
|
|
}
|
|
|
|
|
2014-11-16 19:08:33 +00:00
|
|
|
type KRPCError struct {
|
|
|
|
Code int
|
|
|
|
Msg string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (me KRPCError) Error() string {
|
|
|
|
return fmt.Sprintf("KRPC error %d: %s", me.Code, me.Msg)
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ error = KRPCError{}
|
|
|
|
|
2014-11-18 18:38:13 +00:00
|
|
|
func (m Msg) Error() (ret *KRPCError) {
|
2014-11-16 19:08:33 +00:00
|
|
|
if m["y"] != "e" {
|
2014-11-18 18:38:13 +00:00
|
|
|
return
|
2014-11-16 19:08:33 +00:00
|
|
|
}
|
2014-11-18 18:38:13 +00:00
|
|
|
ret = &KRPCError{}
|
|
|
|
switch e := m["e"].(type) {
|
|
|
|
case []interface{}:
|
|
|
|
ret.Code = int(e[0].(int64))
|
|
|
|
ret.Msg = e[1].(string)
|
|
|
|
case string:
|
|
|
|
ret.Msg = e
|
|
|
|
default:
|
|
|
|
logonce.Stderr.Printf(`KRPC error "e" value has unexpected type: %T`, e)
|
|
|
|
}
|
|
|
|
return
|
2014-11-16 19:08:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the token given in response to a get_peers request for future
|
|
|
|
// announce_peer requests to that node.
|
|
|
|
func (m Msg) AnnounceToken() string {
|
|
|
|
defer func() { recover() }()
|
|
|
|
return m["r"].(map[string]interface{})["token"].(string)
|
|
|
|
}
|
|
|
|
|
2014-05-24 06:51:56 +00:00
|
|
|
type transaction struct {
|
2014-12-07 03:21:20 +00:00
|
|
|
mu sync.Mutex
|
|
|
|
remoteAddr dHTAddr
|
|
|
|
t string
|
|
|
|
Response chan Msg
|
2014-12-09 02:00:42 +00:00
|
|
|
onResponse func(Msg) // Called with the server locked.
|
2014-12-07 03:21:20 +00:00
|
|
|
done chan struct{}
|
|
|
|
queryPacket []byte
|
|
|
|
timer *time.Timer
|
|
|
|
s *Server
|
|
|
|
retries int
|
|
|
|
}
|
|
|
|
|
2014-12-08 22:59:25 +00:00
|
|
|
func (t *transaction) Key() transactionKey {
|
|
|
|
return transactionKey{
|
|
|
|
t.remoteAddr.String(),
|
|
|
|
t.t,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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)))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *transaction) startTimer() {
|
|
|
|
t.timer = time.AfterFunc(jitterDuration(20*time.Second, time.Second), t.timerCallback)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *transaction) timerCallback() {
|
|
|
|
t.mu.Lock()
|
|
|
|
defer t.mu.Unlock()
|
|
|
|
select {
|
|
|
|
case <-t.done:
|
|
|
|
return
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
if t.retries == 2 {
|
|
|
|
t.timeout()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
t.retries++
|
|
|
|
t.sendQuery()
|
|
|
|
if t.timer.Reset(jitterDuration(20*time.Second, time.Second)) {
|
|
|
|
panic("timer should have fired to get here")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *transaction) sendQuery() error {
|
|
|
|
return t.s.writeToNode(t.queryPacket, t.remoteAddr)
|
2014-07-17 06:03:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *transaction) timeout() {
|
2014-12-09 01:09:11 +00:00
|
|
|
go func() {
|
|
|
|
t.s.mu.Lock()
|
|
|
|
defer t.s.mu.Unlock()
|
|
|
|
t.s.nodeTimedOut(t.remoteAddr)
|
|
|
|
}()
|
2014-12-07 03:21:20 +00:00
|
|
|
t.close()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *transaction) close() {
|
|
|
|
if t.closing() {
|
|
|
|
return
|
|
|
|
}
|
2014-12-09 01:09:11 +00:00
|
|
|
t.queryPacket = nil
|
2014-12-07 03:21:20 +00:00
|
|
|
close(t.Response)
|
|
|
|
close(t.done)
|
|
|
|
t.timer.Stop()
|
2014-12-08 22:59:25 +00:00
|
|
|
go func() {
|
|
|
|
t.s.mu.Lock()
|
|
|
|
defer t.s.mu.Unlock()
|
|
|
|
t.s.deleteTransaction(t)
|
|
|
|
}()
|
2014-11-18 18:38:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *transaction) closing() bool {
|
|
|
|
select {
|
|
|
|
case <-t.done:
|
|
|
|
return true
|
|
|
|
default:
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *transaction) Close() {
|
|
|
|
t.mu.Lock()
|
|
|
|
defer t.mu.Unlock()
|
2014-12-07 03:21:20 +00:00
|
|
|
t.close()
|
2014-07-09 14:13:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (t *transaction) handleResponse(m Msg) {
|
2014-11-18 18:38:13 +00:00
|
|
|
t.mu.Lock()
|
|
|
|
if t.closing() {
|
|
|
|
t.mu.Unlock()
|
|
|
|
return
|
|
|
|
}
|
2014-07-17 06:03:59 +00:00
|
|
|
close(t.done)
|
2014-11-18 18:38:13 +00:00
|
|
|
t.mu.Unlock()
|
2014-07-09 14:13:54 +00:00
|
|
|
if t.onResponse != nil {
|
2014-12-09 02:00:42 +00:00
|
|
|
t.s.mu.Lock()
|
2014-07-09 14:13:54 +00:00
|
|
|
t.onResponse(m)
|
2014-12-09 02:00:42 +00:00
|
|
|
t.s.mu.Unlock()
|
2014-07-09 14:13:54 +00:00
|
|
|
}
|
2014-12-09 01:09:11 +00:00
|
|
|
t.queryPacket = nil
|
2014-08-25 12:14:10 +00:00
|
|
|
select {
|
|
|
|
case t.Response <- m:
|
|
|
|
default:
|
|
|
|
panic("blocked handling response")
|
|
|
|
}
|
2014-07-09 14:13:54 +00:00
|
|
|
close(t.Response)
|
2014-05-25 11:34:29 +00:00
|
|
|
}
|
|
|
|
|
2014-06-26 14:57:07 +00:00
|
|
|
func (s *Server) setDefaults() (err error) {
|
2014-08-21 08:07:06 +00:00
|
|
|
if s.id == "" {
|
2014-05-24 06:51:56 +00:00
|
|
|
var id [20]byte
|
2014-06-24 13:18:30 +00:00
|
|
|
h := crypto.SHA1.New()
|
|
|
|
ss, err := os.Hostname()
|
2014-05-24 06:51:56 +00:00
|
|
|
if err != nil {
|
2014-06-24 13:18:30 +00:00
|
|
|
log.Print(err)
|
|
|
|
}
|
2014-08-21 08:07:06 +00:00
|
|
|
ss += s.socket.LocalAddr().String()
|
2014-06-24 13:18:30 +00:00
|
|
|
h.Write([]byte(ss))
|
|
|
|
if b := h.Sum(id[:0:20]); len(b) != 20 {
|
|
|
|
panic(len(b))
|
|
|
|
}
|
|
|
|
if len(id) != 20 {
|
|
|
|
panic(len(id))
|
2014-05-24 06:51:56 +00:00
|
|
|
}
|
2014-08-21 08:07:06 +00:00
|
|
|
s.id = string(id[:])
|
2014-05-24 06:51:56 +00:00
|
|
|
}
|
2014-07-03 15:43:04 +00:00
|
|
|
s.nodes = make(map[string]*Node, 10000)
|
2014-06-26 14:57:07 +00:00
|
|
|
return
|
2014-05-24 06:51:56 +00:00
|
|
|
}
|
|
|
|
|
2014-11-29 01:41:53 +00:00
|
|
|
func (s *Server) SetIPBlockList(list *iplist.IPList) {
|
|
|
|
s.mu.Lock()
|
|
|
|
defer s.mu.Unlock()
|
|
|
|
s.ipBlockList = list
|
|
|
|
}
|
|
|
|
|
2014-08-21 08:07:06 +00:00
|
|
|
func (s *Server) init() (err error) {
|
2014-07-09 14:13:54 +00:00
|
|
|
err = s.setDefaults()
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
s.closed = make(chan struct{})
|
2014-12-08 22:59:25 +00:00
|
|
|
s.transactions = make(map[transactionKey]*transaction)
|
2014-07-09 14:13:54 +00:00
|
|
|
return
|
2014-05-24 06:51:56 +00:00
|
|
|
}
|
|
|
|
|
2014-11-18 00:03:03 +00:00
|
|
|
func (s *Server) processPacket(b []byte, addr dHTAddr) {
|
|
|
|
var d Msg
|
|
|
|
err := bencode.Unmarshal(b, &d)
|
|
|
|
if err != nil {
|
2014-12-01 23:01:55 +00:00
|
|
|
func() {
|
|
|
|
if se, ok := err.(*bencode.SyntaxError); ok {
|
2014-12-03 00:43:28 +00:00
|
|
|
// The message was truncated.
|
|
|
|
if int(se.Offset) == len(b) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// Some messages seem to drop to nul chars abrubtly.
|
2014-12-02 01:12:26 +00:00
|
|
|
if int(se.Offset) < len(b) && b[se.Offset] == 0 {
|
2014-12-01 23:01:55 +00:00
|
|
|
return
|
2014-11-19 03:54:53 +00:00
|
|
|
}
|
2014-12-03 00:43:28 +00:00
|
|
|
// The message isn't bencode from the first.
|
2014-12-01 23:01:55 +00:00
|
|
|
if se.Offset == 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2014-12-09 01:09:11 +00:00
|
|
|
log.Printf("%s: received bad krpc message from %s: %s: %q", s, addr, err, b)
|
2014-12-01 23:01:55 +00:00
|
|
|
}()
|
2014-11-18 00:03:03 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
s.mu.Lock()
|
|
|
|
defer s.mu.Unlock()
|
|
|
|
if d["y"] == "q" {
|
|
|
|
s.handleQuery(addr, d)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
t := s.findResponseTransaction(d.T(), addr)
|
|
|
|
if t == nil {
|
|
|
|
//log.Printf("unexpected message: %#v", d)
|
|
|
|
return
|
|
|
|
}
|
2014-12-08 22:59:25 +00:00
|
|
|
node := s.getNode(addr)
|
|
|
|
node.lastGotResponse = time.Now()
|
|
|
|
// TODO: Update node ID as this is an authoritative packet.
|
|
|
|
go t.handleResponse(d)
|
|
|
|
s.deleteTransaction(t)
|
2014-11-18 00:03:03 +00:00
|
|
|
}
|
|
|
|
|
2014-08-21 08:07:06 +00:00
|
|
|
func (s *Server) serve() error {
|
2014-12-03 07:08:18 +00:00
|
|
|
var b [0x10000]byte
|
2014-05-24 06:51:56 +00:00
|
|
|
for {
|
2014-11-18 00:03:03 +00:00
|
|
|
n, addr, err := s.socket.ReadFrom(b[:])
|
2014-05-24 06:51:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-11-19 03:54:53 +00:00
|
|
|
if n == len(b) {
|
|
|
|
logonce.Stderr.Printf("received dht packet exceeds buffer size")
|
|
|
|
continue
|
|
|
|
}
|
2014-11-18 00:03:03 +00:00
|
|
|
s.processPacket(b[:n], newDHTAddr(addr.(*net.UDPAddr)))
|
2014-05-27 06:28:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-29 01:41:53 +00:00
|
|
|
func (s *Server) ipBlocked(ip net.IP) bool {
|
|
|
|
if s.ipBlockList == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return s.ipBlockList.Lookup(ip) != nil
|
|
|
|
}
|
|
|
|
|
2014-05-27 06:28:56 +00:00
|
|
|
func (s *Server) AddNode(ni NodeInfo) {
|
2014-11-18 18:38:13 +00:00
|
|
|
s.mu.Lock()
|
|
|
|
defer s.mu.Unlock()
|
2014-05-27 06:28:56 +00:00
|
|
|
if s.nodes == nil {
|
|
|
|
s.nodes = make(map[string]*Node)
|
|
|
|
}
|
|
|
|
n := s.getNode(ni.Addr)
|
2014-12-26 06:21:48 +00:00
|
|
|
if n.IDNotSet() {
|
|
|
|
n.SetIDFromBytes(ni.ID[:])
|
2014-05-24 06:51:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-11 15:24:01 +00:00
|
|
|
func (s *Server) nodeByID(id string) *Node {
|
|
|
|
for _, node := range s.nodes {
|
2014-12-26 06:21:48 +00:00
|
|
|
if node.idString() == id {
|
2014-07-11 15:24:01 +00:00
|
|
|
return node
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-11-17 03:22:29 +00:00
|
|
|
func (s *Server) handleQuery(source dHTAddr, m Msg) {
|
2014-07-11 15:24:01 +00:00
|
|
|
args := m["a"].(map[string]interface{})
|
2014-12-07 03:21:20 +00:00
|
|
|
node := s.getNode(source)
|
2014-12-26 06:21:48 +00:00
|
|
|
node.SetIDFromString(args["id"].(string))
|
2014-12-07 03:21:20 +00:00
|
|
|
node.lastGotQuery = time.Now()
|
|
|
|
// Don't respond.
|
2014-11-28 18:04:38 +00:00
|
|
|
if s.passive {
|
|
|
|
return
|
|
|
|
}
|
2014-07-11 15:24:01 +00:00
|
|
|
switch m["q"] {
|
|
|
|
case "ping":
|
|
|
|
s.reply(source, m["t"].(string), nil)
|
2014-12-26 06:21:48 +00:00
|
|
|
case "get_peers": // TODO: Extract common behaviour with find_node.
|
2014-07-11 15:24:01 +00:00
|
|
|
targetID := args["info_hash"].(string)
|
2014-12-19 23:10:53 +00:00
|
|
|
if len(targetID) != 20 {
|
|
|
|
break
|
|
|
|
}
|
2014-07-11 15:24:01 +00:00
|
|
|
var rNodes []NodeInfo
|
|
|
|
// TODO: Reply with "values" list if we have peers instead.
|
|
|
|
for _, node := range s.closestGoodNodes(8, targetID) {
|
|
|
|
rNodes = append(rNodes, node.NodeInfo())
|
|
|
|
}
|
|
|
|
nodesBytes := make([]byte, CompactNodeInfoLen*len(rNodes))
|
|
|
|
for i, ni := range rNodes {
|
|
|
|
err := ni.PutCompact(nodesBytes[i*CompactNodeInfoLen : (i+1)*CompactNodeInfoLen])
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s.reply(source, m["t"].(string), map[string]interface{}{
|
|
|
|
"nodes": string(nodesBytes),
|
|
|
|
"token": "hi",
|
|
|
|
})
|
2014-12-26 06:21:48 +00:00
|
|
|
case "find_node": // TODO: Extract common behaviour with get_peers.
|
2014-07-11 15:24:01 +00:00
|
|
|
targetID := args["target"].(string)
|
2014-07-17 16:38:11 +00:00
|
|
|
if len(targetID) != 20 {
|
|
|
|
log.Printf("bad DHT query: %v", m)
|
|
|
|
return
|
|
|
|
}
|
2014-07-11 15:24:01 +00:00
|
|
|
var rNodes []NodeInfo
|
|
|
|
if node := s.nodeByID(targetID); node != nil {
|
|
|
|
rNodes = append(rNodes, node.NodeInfo())
|
|
|
|
} else {
|
|
|
|
for _, node := range s.closestGoodNodes(8, targetID) {
|
|
|
|
rNodes = append(rNodes, node.NodeInfo())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
nodesBytes := make([]byte, CompactNodeInfoLen*len(rNodes))
|
|
|
|
for i, ni := range rNodes {
|
2014-12-26 06:21:48 +00:00
|
|
|
// TODO: Put IPv6 nodes into the correct dict element.
|
|
|
|
if ni.Addr.UDPAddr().IP.To4() == nil {
|
|
|
|
continue
|
|
|
|
}
|
2014-07-11 15:24:01 +00:00
|
|
|
err := ni.PutCompact(nodesBytes[i*CompactNodeInfoLen : (i+1)*CompactNodeInfoLen])
|
|
|
|
if err != nil {
|
2014-12-26 06:21:48 +00:00
|
|
|
log.Printf("error compacting %#v: %s", ni, err)
|
|
|
|
continue
|
2014-07-11 15:24:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
s.reply(source, m["t"].(string), map[string]interface{}{
|
|
|
|
"nodes": string(nodesBytes),
|
|
|
|
})
|
|
|
|
case "announce_peer":
|
2014-07-16 07:10:49 +00:00
|
|
|
// TODO(anacrolix): Implement this lolz.
|
|
|
|
// log.Print(m)
|
|
|
|
case "vote":
|
|
|
|
// TODO(anacrolix): Or reject, I don't think I want this.
|
2014-07-11 15:24:01 +00:00
|
|
|
default:
|
2014-07-09 14:13:54 +00:00
|
|
|
log.Printf("%s: not handling received query: q=%s", s, m["q"])
|
2014-05-25 13:04:55 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-17 03:22:29 +00:00
|
|
|
func (s *Server) reply(addr dHTAddr, t string, r map[string]interface{}) {
|
2014-07-11 15:24:01 +00:00
|
|
|
if r == nil {
|
|
|
|
r = make(map[string]interface{}, 1)
|
|
|
|
}
|
|
|
|
r["id"] = s.IDString()
|
2014-05-25 13:04:55 +00:00
|
|
|
m := map[string]interface{}{
|
|
|
|
"t": t,
|
|
|
|
"y": "r",
|
2014-07-11 15:24:01 +00:00
|
|
|
"r": r,
|
2014-05-25 13:04:55 +00:00
|
|
|
}
|
|
|
|
b, err := bencode.Marshal(m)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2014-06-24 13:19:19 +00:00
|
|
|
err = s.writeToNode(b, addr)
|
2014-05-25 13:04:55 +00:00
|
|
|
if err != nil {
|
2014-11-29 01:41:53 +00:00
|
|
|
log.Printf("error replying to %s: %s", addr, err)
|
2014-05-25 13:04:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-17 03:22:29 +00:00
|
|
|
func (s *Server) getNode(addr dHTAddr) (n *Node) {
|
2014-12-26 06:21:48 +00:00
|
|
|
addrStr := addr.String()
|
|
|
|
n = s.nodes[addrStr]
|
2014-05-24 06:51:56 +00:00
|
|
|
if n == nil {
|
|
|
|
n = &Node{
|
|
|
|
addr: addr,
|
|
|
|
}
|
2014-12-09 01:09:11 +00:00
|
|
|
if len(s.nodes) < maxNodes {
|
2014-12-26 06:21:48 +00:00
|
|
|
s.nodes[addrStr] = n
|
2014-12-09 01:09:11 +00:00
|
|
|
}
|
2014-05-24 06:51:56 +00:00
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
2014-12-09 01:09:11 +00:00
|
|
|
func (s *Server) nodeTimedOut(addr dHTAddr) {
|
|
|
|
node, ok := s.nodes[addr.String()]
|
|
|
|
if !ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if node.DefinitelyGood() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if len(s.nodes) < maxNodes {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
delete(s.nodes, addr.String())
|
|
|
|
}
|
2014-05-24 06:51:56 +00:00
|
|
|
|
2014-11-17 03:22:29 +00:00
|
|
|
func (s *Server) writeToNode(b []byte, node dHTAddr) (err error) {
|
2014-11-29 01:41:53 +00:00
|
|
|
if list := s.ipBlockList; list != nil {
|
2014-12-26 06:21:48 +00:00
|
|
|
if r := list.Lookup(util.AddrIP(node.UDPAddr())); r != nil {
|
2014-11-29 01:41:53 +00:00
|
|
|
err = fmt.Errorf("write to %s blocked: %s", node, r.Description)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2014-12-26 06:21:48 +00:00
|
|
|
n, err := s.socket.WriteTo(b, node.UDPAddr())
|
2014-06-24 13:19:19 +00:00
|
|
|
if err != nil {
|
2014-07-17 06:01:48 +00:00
|
|
|
err = fmt.Errorf("error writing %d bytes to %s: %s", len(b), node, err)
|
2014-06-24 13:19:19 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
if n != len(b) {
|
|
|
|
err = io.ErrShortWrite
|
|
|
|
return
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-11-17 03:22:29 +00:00
|
|
|
func (s *Server) findResponseTransaction(transactionID string, sourceNode dHTAddr) *transaction {
|
2014-12-08 22:59:25 +00:00
|
|
|
return s.transactions[transactionKey{
|
|
|
|
sourceNode.String(),
|
|
|
|
transactionID}]
|
2014-05-24 06:51:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Server) nextTransactionID() string {
|
|
|
|
var b [binary.MaxVarintLen64]byte
|
|
|
|
n := binary.PutUvarint(b[:], s.transactionIDInt)
|
|
|
|
s.transactionIDInt++
|
|
|
|
return string(b[:n])
|
|
|
|
}
|
|
|
|
|
2014-12-08 22:59:25 +00:00
|
|
|
func (s *Server) deleteTransaction(t *transaction) {
|
|
|
|
delete(s.transactions, t.Key())
|
2014-05-24 06:51:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Server) addTransaction(t *transaction) {
|
2014-12-08 22:59:25 +00:00
|
|
|
if _, ok := s.transactions[t.Key()]; ok {
|
|
|
|
panic("transaction not unique")
|
|
|
|
}
|
|
|
|
s.transactions[t.Key()] = t
|
2014-05-24 06:51:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Server) IDString() string {
|
2014-08-21 08:07:06 +00:00
|
|
|
if len(s.id) != 20 {
|
2014-05-24 06:51:56 +00:00
|
|
|
panic("bad node id")
|
|
|
|
}
|
2014-08-21 08:07:06 +00:00
|
|
|
return s.id
|
2014-05-24 06:51:56 +00:00
|
|
|
}
|
|
|
|
|
2014-11-17 03:22:29 +00:00
|
|
|
func (s *Server) query(node dHTAddr, q string, a map[string]interface{}) (t *transaction, err error) {
|
2014-05-24 06:51:56 +00:00
|
|
|
tid := s.nextTransactionID()
|
|
|
|
if a == nil {
|
2014-11-16 19:08:33 +00:00
|
|
|
a = make(map[string]interface{}, 1)
|
2014-05-24 06:51:56 +00:00
|
|
|
}
|
|
|
|
a["id"] = s.IDString()
|
|
|
|
d := map[string]interface{}{
|
|
|
|
"t": tid,
|
|
|
|
"y": "q",
|
|
|
|
"q": q,
|
|
|
|
"a": a,
|
|
|
|
}
|
|
|
|
b, err := bencode.Marshal(d)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
t = &transaction{
|
2014-12-07 03:21:20 +00:00
|
|
|
remoteAddr: node,
|
|
|
|
t: tid,
|
|
|
|
Response: make(chan Msg, 1),
|
|
|
|
done: make(chan struct{}),
|
|
|
|
queryPacket: b,
|
|
|
|
s: s,
|
|
|
|
}
|
|
|
|
err = t.sendQuery()
|
2014-05-24 06:51:56 +00:00
|
|
|
if err != nil {
|
2014-07-17 06:03:59 +00:00
|
|
|
return
|
2014-05-24 06:51:56 +00:00
|
|
|
}
|
2014-12-07 03:21:20 +00:00
|
|
|
s.getNode(node).lastSentQuery = time.Now()
|
|
|
|
t.startTimer()
|
|
|
|
s.addTransaction(t)
|
2014-05-24 06:51:56 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-05-27 06:28:56 +00:00
|
|
|
const CompactNodeInfoLen = 26
|
2014-05-25 11:34:29 +00:00
|
|
|
|
2014-05-27 06:28:56 +00:00
|
|
|
type NodeInfo struct {
|
2014-05-25 11:34:29 +00:00
|
|
|
ID [20]byte
|
2014-11-17 03:22:29 +00:00
|
|
|
Addr dHTAddr
|
2014-05-25 11:34:29 +00:00
|
|
|
}
|
|
|
|
|
2014-05-27 06:28:56 +00:00
|
|
|
func (ni *NodeInfo) PutCompact(b []byte) error {
|
|
|
|
if n := copy(b[:], ni.ID[:]); n != 20 {
|
2014-05-25 11:34:29 +00:00
|
|
|
panic(n)
|
|
|
|
}
|
2014-11-17 03:20:49 +00:00
|
|
|
ip := util.AddrIP(ni.Addr).To4()
|
2014-05-25 11:34:29 +00:00
|
|
|
if len(ip) != 4 {
|
2014-12-26 06:21:48 +00:00
|
|
|
return errors.New("expected ipv4 address")
|
2014-05-25 11:34:29 +00:00
|
|
|
}
|
|
|
|
if n := copy(b[20:], ip); n != 4 {
|
|
|
|
panic(n)
|
|
|
|
}
|
2014-11-17 03:20:49 +00:00
|
|
|
binary.BigEndian.PutUint16(b[24:], uint16(util.AddrPort(ni.Addr)))
|
2014-05-27 06:28:56 +00:00
|
|
|
return nil
|
2014-05-25 11:34:29 +00:00
|
|
|
}
|
2014-05-24 06:51:56 +00:00
|
|
|
|
2014-05-27 06:28:56 +00:00
|
|
|
func (cni *NodeInfo) UnmarshalCompact(b []byte) error {
|
2014-05-25 11:34:29 +00:00
|
|
|
if len(b) != 26 {
|
|
|
|
return errors.New("expected 26 bytes")
|
|
|
|
}
|
|
|
|
if 20 != copy(cni.ID[:], b[:20]) {
|
|
|
|
panic("impossibru!")
|
|
|
|
}
|
2014-11-17 03:22:29 +00:00
|
|
|
cni.Addr = newDHTAddr(&net.UDPAddr{
|
|
|
|
IP: net.IPv4(b[20], b[21], b[22], b[23]),
|
|
|
|
Port: int(binary.BigEndian.Uint16(b[24:26])),
|
|
|
|
})
|
2014-05-25 11:34:29 +00:00
|
|
|
return nil
|
2014-05-24 06:51:56 +00:00
|
|
|
}
|
|
|
|
|
2014-05-25 11:34:29 +00:00
|
|
|
func (s *Server) Ping(node *net.UDPAddr) (*transaction, error) {
|
2014-08-25 12:14:31 +00:00
|
|
|
s.mu.Lock()
|
|
|
|
defer s.mu.Unlock()
|
2014-11-17 03:22:29 +00:00
|
|
|
return s.query(newDHTAddr(node), "ping", nil)
|
2014-05-24 06:51:56 +00:00
|
|
|
}
|
2014-05-25 11:34:29 +00:00
|
|
|
|
2014-11-16 19:08:33 +00:00
|
|
|
// Announce a local peer. This can only be done to nodes that gave us an
|
|
|
|
// announce token, which is received in responses during GetPeers. It's
|
|
|
|
// recommended then that GetPeers is called before this method.
|
|
|
|
func (s *Server) AnnouncePeer(port int, impliedPort bool, infoHash string) (err error) {
|
|
|
|
s.mu.Lock()
|
|
|
|
defer s.mu.Unlock()
|
2014-12-26 06:21:48 +00:00
|
|
|
for _, node := range s.closestNodes(160, nodeIDFromString(infoHash), func(n *Node) bool {
|
2014-12-09 01:09:11 +00:00
|
|
|
return n.announceToken != ""
|
2014-11-16 19:08:33 +00:00
|
|
|
}) {
|
|
|
|
err = s.announcePeer(node.addr, infoHash, port, node.announceToken, impliedPort)
|
|
|
|
if err != nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-11-17 03:22:29 +00:00
|
|
|
func (s *Server) announcePeer(node dHTAddr, infoHash string, port int, token string, impliedPort bool) error {
|
2014-11-16 19:08:33 +00:00
|
|
|
t, err := s.query(node, "announce_peer", map[string]interface{}{
|
|
|
|
"implied_port": func() int {
|
|
|
|
if impliedPort {
|
|
|
|
return 1
|
|
|
|
} else {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
}(),
|
|
|
|
"info_hash": infoHash,
|
|
|
|
"port": port,
|
|
|
|
"token": token,
|
|
|
|
})
|
|
|
|
t.setOnResponse(func(m Msg) {
|
|
|
|
if err := m.Error(); err != nil {
|
|
|
|
logonce.Stderr.Printf("announce_peer response: %s", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
s.NumConfirmedAnnounces++
|
|
|
|
})
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2014-05-25 11:34:29 +00:00
|
|
|
type findNodeResponse struct {
|
2014-05-27 06:28:56 +00:00
|
|
|
Nodes []NodeInfo
|
2014-05-25 11:34:29 +00:00
|
|
|
}
|
|
|
|
|
2014-05-25 13:04:55 +00:00
|
|
|
func getResponseNodes(m Msg) (s string, err error) {
|
|
|
|
defer func() {
|
|
|
|
r := recover()
|
|
|
|
if r == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
err = fmt.Errorf("couldn't get response nodes: %s: %#v", r, m)
|
|
|
|
}()
|
|
|
|
s = m["r"].(map[string]interface{})["nodes"].(string)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-05-25 11:34:29 +00:00
|
|
|
func (me *findNodeResponse) UnmarshalKRPCMsg(m Msg) error {
|
2014-05-25 13:04:55 +00:00
|
|
|
b, err := getResponseNodes(m)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-05-25 11:34:29 +00:00
|
|
|
for i := 0; i < len(b); i += 26 {
|
2014-05-27 06:28:56 +00:00
|
|
|
var n NodeInfo
|
|
|
|
err := n.UnmarshalCompact([]byte(b[i : i+26]))
|
2014-05-25 11:34:29 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
me.Nodes = append(me.Nodes, n)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2014-07-09 14:13:54 +00:00
|
|
|
func (t *transaction) setOnResponse(f func(m Msg)) {
|
|
|
|
if t.onResponse != nil {
|
|
|
|
panic(t.onResponse)
|
|
|
|
}
|
|
|
|
t.onResponse = f
|
|
|
|
}
|
|
|
|
|
|
|
|
func unmarshalNodeInfoBinary(b []byte) (ret []NodeInfo, err error) {
|
|
|
|
if len(b)%26 != 0 {
|
|
|
|
err = errors.New("bad buffer length")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
ret = make([]NodeInfo, 0, len(b)/26)
|
|
|
|
for i := 0; i < len(b); i += 26 {
|
|
|
|
var ni NodeInfo
|
|
|
|
err = ni.UnmarshalCompact(b[i : i+26])
|
|
|
|
if err != nil {
|
2014-07-03 15:43:04 +00:00
|
|
|
return
|
|
|
|
}
|
2014-07-09 14:13:54 +00:00
|
|
|
ret = append(ret, ni)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func extractNodes(d Msg) (nodes []NodeInfo, err error) {
|
|
|
|
if d["y"] != "r" {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
r, ok := d["r"]
|
|
|
|
if !ok {
|
|
|
|
err = errors.New("missing r dict")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
rd, ok := r.(map[string]interface{})
|
|
|
|
if !ok {
|
|
|
|
err = errors.New("bad r value type")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
n, ok := rd["nodes"]
|
|
|
|
if !ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
ns, ok := n.(string)
|
|
|
|
if !ok {
|
|
|
|
err = errors.New("bad nodes value type")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
return unmarshalNodeInfoBinary([]byte(ns))
|
2014-07-03 15:43:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Server) liftNodes(d Msg) {
|
|
|
|
if d["y"] != "r" {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
var r findNodeResponse
|
|
|
|
err := r.UnmarshalKRPCMsg(d)
|
|
|
|
if err != nil {
|
|
|
|
// log.Print(err)
|
|
|
|
} else {
|
|
|
|
for _, cni := range r.Nodes {
|
2014-11-17 03:20:49 +00:00
|
|
|
if util.AddrPort(cni.Addr) == 0 {
|
2014-07-17 16:38:11 +00:00
|
|
|
// TODO: Why would people even do this?
|
|
|
|
continue
|
|
|
|
}
|
2014-11-29 01:41:53 +00:00
|
|
|
if s.ipBlocked(util.AddrIP(cni.Addr)) {
|
|
|
|
continue
|
|
|
|
}
|
2014-07-03 15:43:04 +00:00
|
|
|
n := s.getNode(cni.Addr)
|
2014-12-26 06:21:48 +00:00
|
|
|
n.SetIDFromBytes(cni.ID[:])
|
2014-07-03 15:43:04 +00:00
|
|
|
}
|
|
|
|
// log.Printf("lifted %d nodes", len(r.Nodes))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-24 13:20:07 +00:00
|
|
|
// Sends a find_node query to addr. targetID is the node we're looking for.
|
2014-11-17 03:22:29 +00:00
|
|
|
func (s *Server) findNode(addr dHTAddr, targetID string) (t *transaction, err error) {
|
2014-11-16 19:08:33 +00:00
|
|
|
t, err = s.query(addr, "find_node", map[string]interface{}{"target": targetID})
|
2014-05-25 11:34:29 +00:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
2014-06-24 13:20:07 +00:00
|
|
|
// Scrape peers from the response to put in the server's table before
|
|
|
|
// handing the response back to the caller.
|
2014-07-09 14:13:54 +00:00
|
|
|
t.setOnResponse(func(d Msg) {
|
2014-07-03 15:43:04 +00:00
|
|
|
s.liftNodes(d)
|
|
|
|
})
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-08-21 08:07:06 +00:00
|
|
|
func extractValues(m Msg) (vs []util.CompactPeer) {
|
2014-07-03 15:43:04 +00:00
|
|
|
r, ok := m["r"]
|
|
|
|
if !ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
rd, ok := r.(map[string]interface{})
|
|
|
|
if !ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
v, ok := rd["values"]
|
|
|
|
if !ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
vl, ok := v.([]interface{})
|
|
|
|
if !ok {
|
2014-12-01 09:29:06 +00:00
|
|
|
log.Printf("unexpected krpc values type: %T", v)
|
|
|
|
return
|
2014-07-03 15:43:04 +00:00
|
|
|
}
|
2014-08-21 08:07:06 +00:00
|
|
|
vs = make([]util.CompactPeer, 0, len(vl))
|
2014-07-03 15:43:04 +00:00
|
|
|
for _, i := range vl {
|
|
|
|
s, ok := i.(string)
|
2014-05-25 11:34:29 +00:00
|
|
|
if !ok {
|
2014-07-03 15:43:04 +00:00
|
|
|
panic(i)
|
2014-05-25 11:34:29 +00:00
|
|
|
}
|
2014-08-21 08:07:06 +00:00
|
|
|
var cp util.CompactPeer
|
2014-07-03 15:43:04 +00:00
|
|
|
err := cp.UnmarshalBinary([]byte(s))
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("error decoding values list element: %s", err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
vs = append(vs, cp)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-11-17 03:22:29 +00:00
|
|
|
func (s *Server) getPeers(addr dHTAddr, infoHash string) (t *transaction, err error) {
|
2014-07-03 15:43:04 +00:00
|
|
|
if len(infoHash) != 20 {
|
|
|
|
err = fmt.Errorf("infohash has bad length")
|
|
|
|
return
|
|
|
|
}
|
2014-11-16 19:08:33 +00:00
|
|
|
t, err = s.query(addr, "get_peers", map[string]interface{}{"info_hash": infoHash})
|
2014-07-03 15:43:04 +00:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
2014-07-09 14:13:54 +00:00
|
|
|
t.setOnResponse(func(m Msg) {
|
2014-07-03 15:43:04 +00:00
|
|
|
s.liftNodes(m)
|
2014-11-16 19:08:33 +00:00
|
|
|
s.getNode(addr).announceToken = m.AnnounceToken()
|
2014-07-03 15:43:04 +00:00
|
|
|
})
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-12-19 23:09:11 +00:00
|
|
|
func bootstrapAddrs() (addrs []*net.UDPAddr, err error) {
|
|
|
|
for _, addrStr := range []string{
|
|
|
|
"router.utorrent.com:6881",
|
|
|
|
"router.bittorrent.com:6881",
|
|
|
|
} {
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2014-12-19 23:09:11 +00:00
|
|
|
func (s *Server) addRootNodes() error {
|
|
|
|
addrs, err := bootstrapAddrs()
|
2014-05-27 06:28:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-12-19 23:09:11 +00:00
|
|
|
for _, addr := range addrs {
|
|
|
|
s.nodes[addr.String()] = &Node{
|
|
|
|
addr: newDHTAddr(addr),
|
|
|
|
}
|
2014-05-27 06:28:56 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Populates the node table.
|
2014-08-21 08:07:06 +00:00
|
|
|
func (s *Server) bootstrap() (err error) {
|
2014-05-27 06:28:56 +00:00
|
|
|
s.mu.Lock()
|
|
|
|
defer s.mu.Unlock()
|
2014-05-25 11:34:29 +00:00
|
|
|
if len(s.nodes) == 0 {
|
2014-12-19 23:09:11 +00:00
|
|
|
err = s.addRootNodes()
|
2014-07-09 14:13:54 +00:00
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return
|
2014-05-27 06:28:56 +00:00
|
|
|
}
|
2014-07-03 15:43:04 +00:00
|
|
|
for {
|
2014-07-09 14:13:54 +00:00
|
|
|
var outstanding sync.WaitGroup
|
2014-07-03 15:43:04 +00:00
|
|
|
for _, node := range s.nodes {
|
|
|
|
var t *transaction
|
2014-08-21 08:07:06 +00:00
|
|
|
t, err = s.findNode(node.addr, s.id)
|
2014-07-03 15:43:04 +00:00
|
|
|
if err != nil {
|
2014-11-30 02:31:07 +00:00
|
|
|
log.Printf("error sending find_node: %s", err)
|
|
|
|
continue
|
2014-07-03 15:43:04 +00:00
|
|
|
}
|
2014-07-09 14:13:54 +00:00
|
|
|
outstanding.Add(1)
|
2014-07-03 15:43:04 +00:00
|
|
|
go func() {
|
|
|
|
<-t.Response
|
2014-07-09 14:13:54 +00:00
|
|
|
outstanding.Done()
|
2014-07-03 15:43:04 +00:00
|
|
|
}()
|
|
|
|
}
|
2014-07-09 14:13:54 +00:00
|
|
|
noOutstanding := make(chan struct{})
|
|
|
|
go func() {
|
|
|
|
outstanding.Wait()
|
|
|
|
close(noOutstanding)
|
|
|
|
}()
|
|
|
|
s.mu.Unlock()
|
|
|
|
select {
|
|
|
|
case <-s.closed:
|
|
|
|
s.mu.Lock()
|
|
|
|
return
|
|
|
|
case <-time.After(15 * time.Second):
|
|
|
|
case <-noOutstanding:
|
|
|
|
}
|
|
|
|
s.mu.Lock()
|
2014-07-16 07:08:25 +00:00
|
|
|
// log.Printf("now have %d nodes", len(s.nodes))
|
2014-07-24 03:45:18 +00:00
|
|
|
if s.numGoodNodes() >= 160 {
|
2014-07-03 15:43:04 +00:00
|
|
|
break
|
2014-05-25 11:34:29 +00:00
|
|
|
}
|
|
|
|
}
|
2014-05-27 06:28:56 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-07-24 03:45:18 +00:00
|
|
|
func (s *Server) numGoodNodes() (num int) {
|
|
|
|
for _, n := range s.nodes {
|
2014-12-09 01:09:11 +00:00
|
|
|
if n.DefinitelyGood() {
|
2014-07-24 03:45:18 +00:00
|
|
|
num++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-07-16 07:07:28 +00:00
|
|
|
func (s *Server) NumNodes() int {
|
|
|
|
s.mu.Lock()
|
|
|
|
defer s.mu.Unlock()
|
|
|
|
return len(s.nodes)
|
|
|
|
}
|
|
|
|
|
2014-06-26 14:57:07 +00:00
|
|
|
func (s *Server) Nodes() (nis []NodeInfo) {
|
2014-05-27 06:28:56 +00:00
|
|
|
s.mu.Lock()
|
|
|
|
defer s.mu.Unlock()
|
|
|
|
for _, node := range s.nodes {
|
2014-06-26 14:57:07 +00:00
|
|
|
// if !node.Good() {
|
|
|
|
// continue
|
|
|
|
// }
|
2014-05-27 06:28:56 +00:00
|
|
|
ni := NodeInfo{
|
|
|
|
Addr: node.addr,
|
|
|
|
}
|
2014-12-26 06:21:48 +00:00
|
|
|
if n := copy(ni.ID[:], node.idString()); n != 20 && n != 0 {
|
2014-05-27 06:28:56 +00:00
|
|
|
panic(n)
|
|
|
|
}
|
|
|
|
nis = append(nis, ni)
|
2014-05-25 11:34:29 +00:00
|
|
|
}
|
2014-05-27 06:28:56 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-08-21 11:12:50 +00:00
|
|
|
func (s *Server) Close() {
|
2014-07-09 14:13:54 +00:00
|
|
|
s.mu.Lock()
|
|
|
|
select {
|
|
|
|
case <-s.closed:
|
|
|
|
default:
|
|
|
|
close(s.closed)
|
2014-08-21 11:12:50 +00:00
|
|
|
s.socket.Close()
|
2014-07-09 14:13:54 +00:00
|
|
|
}
|
|
|
|
s.mu.Unlock()
|
2014-05-25 11:34:29 +00:00
|
|
|
}
|
2014-06-24 13:20:49 +00:00
|
|
|
|
2014-12-09 06:25:49 +00:00
|
|
|
var maxDistance big.Int
|
2014-11-20 02:02:03 +00:00
|
|
|
|
2014-12-09 06:25:49 +00:00
|
|
|
func init() {
|
|
|
|
var zero big.Int
|
|
|
|
maxDistance.SetBit(&zero, 160, 1)
|
|
|
|
}
|
|
|
|
|
2014-07-11 15:24:01 +00:00
|
|
|
func (s *Server) closestGoodNodes(k int, targetID string) []*Node {
|
2014-12-26 06:21:48 +00:00
|
|
|
return s.closestNodes(k, nodeIDFromString(targetID), func(n *Node) bool { return n.DefinitelyGood() })
|
2014-11-16 19:08:33 +00:00
|
|
|
}
|
|
|
|
|
2014-12-26 06:21:48 +00:00
|
|
|
func (s *Server) closestNodes(k int, target nodeID, filter func(*Node) bool) []*Node {
|
|
|
|
sel := newKClosestNodesSelector(k, target)
|
2014-07-11 15:24:01 +00:00
|
|
|
idNodes := make(map[string]*Node, len(s.nodes))
|
|
|
|
for _, node := range s.nodes {
|
2014-11-16 19:08:33 +00:00
|
|
|
if !filter(node) {
|
2014-07-11 15:24:01 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
sel.Push(node.id)
|
2014-12-26 06:21:48 +00:00
|
|
|
idNodes[node.idString()] = node
|
2014-07-11 15:24:01 +00:00
|
|
|
}
|
|
|
|
ids := sel.IDs()
|
|
|
|
ret := make([]*Node, 0, len(ids))
|
|
|
|
for _, id := range ids {
|
2014-12-26 06:21:48 +00:00
|
|
|
ret = append(ret, idNodes[id.String()])
|
2014-07-11 15:24:01 +00:00
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|