2018-03-06 20:15:44 -05:00
|
|
|
package dht
|
|
|
|
|
|
|
|
import (
|
2018-03-08 19:50:18 -05:00
|
|
|
"bytes"
|
2018-03-06 20:15:44 -05:00
|
|
|
"container/list"
|
2018-03-08 19:50:18 -05:00
|
|
|
"net"
|
2018-03-06 20:15:44 -05:00
|
|
|
"sort"
|
2018-03-08 19:50:18 -05:00
|
|
|
|
|
|
|
"github.com/lbryio/errors.go"
|
|
|
|
|
|
|
|
"github.com/zeebo/bencode"
|
2018-03-06 20:15:44 -05:00
|
|
|
)
|
|
|
|
|
2018-03-08 19:50:18 -05:00
|
|
|
type Node struct {
|
|
|
|
id bitmap
|
|
|
|
ip net.IP
|
|
|
|
port int
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n Node) MarshalCompact() ([]byte, error) {
|
|
|
|
if n.ip.To4() == nil {
|
|
|
|
return nil, errors.Err("ip not set")
|
|
|
|
}
|
|
|
|
if n.port < 0 || n.port > 65535 {
|
|
|
|
return nil, errors.Err("invalid port")
|
|
|
|
}
|
|
|
|
|
|
|
|
var buf bytes.Buffer
|
|
|
|
buf.Write(n.ip.To4())
|
|
|
|
buf.WriteByte(byte(n.port >> 8))
|
|
|
|
buf.WriteByte(byte(n.port))
|
|
|
|
buf.Write(n.id[:])
|
|
|
|
|
2018-03-09 16:43:30 -05:00
|
|
|
if buf.Len() != compactNodeInfoLength {
|
2018-03-08 19:50:18 -05:00
|
|
|
return nil, errors.Err("i dont know how this happened")
|
|
|
|
}
|
|
|
|
|
|
|
|
return buf.Bytes(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *Node) UnmarshalCompact(b []byte) error {
|
2018-03-09 16:43:30 -05:00
|
|
|
if len(b) != compactNodeInfoLength {
|
|
|
|
return errors.Err("invalid compact length")
|
2018-03-08 19:50:18 -05:00
|
|
|
}
|
2018-03-09 16:43:30 -05:00
|
|
|
n.ip = net.IPv4(b[0], b[1], b[2], b[3])
|
2018-03-08 19:50:18 -05:00
|
|
|
n.port = int(uint16(b[5]) | uint16(b[4])<<8)
|
|
|
|
n.id = newBitmapFromBytes(b[6:])
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n Node) MarshalBencode() ([]byte, error) {
|
|
|
|
return bencode.EncodeBytes([]interface{}{n.id, n.ip.String(), n.port})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *Node) UnmarshalBencode(b []byte) error {
|
|
|
|
var raw []bencode.RawMessage
|
|
|
|
err := bencode.DecodeBytes(b, &raw)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(raw) != 3 {
|
|
|
|
return errors.Err("contact must have 3 elements; got %d", len(raw))
|
|
|
|
}
|
|
|
|
|
|
|
|
err = bencode.DecodeBytes(raw[0], &n.id)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var ipStr string
|
|
|
|
err = bencode.DecodeBytes(raw[1], &ipStr)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
n.ip = net.ParseIP(ipStr).To4()
|
|
|
|
if n.ip == nil {
|
|
|
|
return errors.Err("invalid IP")
|
|
|
|
}
|
|
|
|
|
|
|
|
err = bencode.DecodeBytes(raw[2], &n.port)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type SortedNode struct {
|
|
|
|
node *Node
|
|
|
|
xorDistanceToTarget bitmap
|
|
|
|
}
|
|
|
|
|
|
|
|
type byXorDistance []*SortedNode
|
|
|
|
|
|
|
|
func (a byXorDistance) Len() int { return len(a) }
|
|
|
|
func (a byXorDistance) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
|
|
func (a byXorDistance) Less(i, j int) bool {
|
|
|
|
return a[i].xorDistanceToTarget.Less(a[j].xorDistanceToTarget)
|
|
|
|
}
|
|
|
|
|
2018-03-06 20:15:44 -05:00
|
|
|
type RoutingTable struct {
|
|
|
|
node Node
|
|
|
|
buckets [numBuckets]*list.List
|
|
|
|
}
|
|
|
|
|
2018-03-08 19:50:18 -05:00
|
|
|
func newRoutingTable(node *Node) *RoutingTable {
|
2018-03-06 20:15:44 -05:00
|
|
|
var rt RoutingTable
|
|
|
|
for i := range rt.buckets {
|
|
|
|
rt.buckets[i] = list.New()
|
|
|
|
}
|
|
|
|
rt.node = *node
|
|
|
|
return &rt
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rt *RoutingTable) Update(node *Node) {
|
|
|
|
prefixLength := node.id.Xor(rt.node.id).PrefixLen()
|
|
|
|
bucket := rt.buckets[prefixLength]
|
|
|
|
element := findInList(bucket, rt.node.id)
|
|
|
|
if element == nil {
|
|
|
|
if bucket.Len() <= bucketSize {
|
|
|
|
bucket.PushBack(node)
|
|
|
|
}
|
|
|
|
// TODO: Handle insertion when the list is full by evicting old elements if
|
|
|
|
// they don't respond to a ping.
|
|
|
|
} else {
|
|
|
|
bucket.MoveToBack(element)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rt *RoutingTable) FindClosest(target bitmap, count int) []*Node {
|
2018-03-08 19:50:18 -05:00
|
|
|
var toSort []*SortedNode
|
2018-03-06 20:15:44 -05:00
|
|
|
|
|
|
|
prefixLength := target.Xor(rt.node.id).PrefixLen()
|
|
|
|
bucket := rt.buckets[prefixLength]
|
2018-03-08 19:50:18 -05:00
|
|
|
toSort = appendNodes(toSort, bucket.Front(), nil, target)
|
2018-03-06 20:15:44 -05:00
|
|
|
|
2018-03-15 14:42:57 -04:00
|
|
|
for i := 1; (prefixLength-i >= 0 || prefixLength+i < numBuckets) && len(toSort) < count; i++ {
|
2018-03-06 20:15:44 -05:00
|
|
|
if prefixLength-i >= 0 {
|
|
|
|
bucket = rt.buckets[prefixLength-i]
|
2018-03-08 19:50:18 -05:00
|
|
|
toSort = appendNodes(toSort, bucket.Front(), nil, target)
|
2018-03-06 20:15:44 -05:00
|
|
|
}
|
2018-03-15 14:42:57 -04:00
|
|
|
if prefixLength+i < numBuckets {
|
2018-03-06 20:15:44 -05:00
|
|
|
bucket = rt.buckets[prefixLength+i]
|
2018-03-08 19:50:18 -05:00
|
|
|
toSort = appendNodes(toSort, bucket.Front(), nil, target)
|
2018-03-06 20:15:44 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sort.Sort(byXorDistance(toSort))
|
|
|
|
|
2018-03-08 19:50:18 -05:00
|
|
|
var nodes []*Node
|
2018-03-06 20:15:44 -05:00
|
|
|
for _, c := range toSort {
|
|
|
|
nodes = append(nodes, c.node)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nodes
|
|
|
|
}
|
|
|
|
|
|
|
|
func findInList(bucket *list.List, value bitmap) *list.Element {
|
|
|
|
for curr := bucket.Front(); curr != nil; curr = curr.Next() {
|
|
|
|
if curr.Value.(*Node).id.Equals(value) {
|
|
|
|
return curr
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-03-08 19:50:18 -05:00
|
|
|
func appendNodes(nodes []*SortedNode, start, end *list.Element, target bitmap) []*SortedNode {
|
2018-03-06 20:15:44 -05:00
|
|
|
for curr := start; curr != end; curr = curr.Next() {
|
|
|
|
node := curr.Value.(*Node)
|
2018-03-08 19:50:18 -05:00
|
|
|
nodes = append(nodes, &SortedNode{node, node.id.Xor(target)})
|
2018-03-06 20:15:44 -05:00
|
|
|
}
|
2018-03-08 19:50:18 -05:00
|
|
|
return nodes
|
2018-03-06 20:15:44 -05:00
|
|
|
}
|