2018-03-07 02:15:44 +01:00
|
|
|
package dht
|
|
|
|
|
|
|
|
import (
|
2018-03-09 01:50:18 +01:00
|
|
|
"bytes"
|
2018-03-07 02:15:44 +01:00
|
|
|
"container/list"
|
2018-03-24 00:18:00 +01:00
|
|
|
"fmt"
|
2018-03-09 01:50:18 +01:00
|
|
|
"net"
|
2018-03-07 02:15:44 +01:00
|
|
|
"sort"
|
2018-03-24 00:18:00 +01:00
|
|
|
"strings"
|
|
|
|
"sync"
|
2018-03-09 01:50:18 +01:00
|
|
|
|
|
|
|
"github.com/lbryio/errors.go"
|
|
|
|
|
2018-03-24 00:18:00 +01:00
|
|
|
"github.com/lyoshenka/bencode"
|
2018-03-07 02:15:44 +01:00
|
|
|
)
|
|
|
|
|
2018-04-28 02:16:12 +02:00
|
|
|
type Contact struct {
|
|
|
|
id Bitmap
|
|
|
|
ip net.IP
|
|
|
|
port int
|
2018-04-05 22:05:28 +02:00
|
|
|
}
|
|
|
|
|
2018-04-28 02:16:12 +02:00
|
|
|
func (c Contact) Addr() *net.UDPAddr {
|
|
|
|
return &net.UDPAddr{IP: c.ip, Port: c.port}
|
2018-03-09 01:50:18 +01:00
|
|
|
}
|
|
|
|
|
2018-04-28 02:16:12 +02:00
|
|
|
func (c Contact) String() string {
|
|
|
|
return c.id.HexShort() + "@" + c.Addr().String()
|
2018-03-24 00:18:00 +01:00
|
|
|
}
|
|
|
|
|
2018-04-28 02:16:12 +02:00
|
|
|
func (c Contact) MarshalCompact() ([]byte, error) {
|
|
|
|
if c.ip.To4() == nil {
|
2018-03-09 01:50:18 +01:00
|
|
|
return nil, errors.Err("ip not set")
|
|
|
|
}
|
2018-04-28 02:16:12 +02:00
|
|
|
if c.port < 0 || c.port > 65535 {
|
2018-03-09 01:50:18 +01:00
|
|
|
return nil, errors.Err("invalid port")
|
|
|
|
}
|
|
|
|
|
|
|
|
var buf bytes.Buffer
|
2018-04-28 02:16:12 +02:00
|
|
|
buf.Write(c.ip.To4())
|
|
|
|
buf.WriteByte(byte(c.port >> 8))
|
|
|
|
buf.WriteByte(byte(c.port))
|
|
|
|
buf.Write(c.id[:])
|
2018-03-09 01:50:18 +01:00
|
|
|
|
2018-03-09 22:43:30 +01:00
|
|
|
if buf.Len() != compactNodeInfoLength {
|
2018-03-09 01:50:18 +01:00
|
|
|
return nil, errors.Err("i dont know how this happened")
|
|
|
|
}
|
|
|
|
|
|
|
|
return buf.Bytes(), nil
|
|
|
|
}
|
|
|
|
|
2018-04-28 02:16:12 +02:00
|
|
|
func (c *Contact) UnmarshalCompact(b []byte) error {
|
2018-03-09 22:43:30 +01:00
|
|
|
if len(b) != compactNodeInfoLength {
|
|
|
|
return errors.Err("invalid compact length")
|
2018-03-09 01:50:18 +01:00
|
|
|
}
|
2018-04-28 02:16:12 +02:00
|
|
|
c.ip = net.IPv4(b[0], b[1], b[2], b[3]).To4()
|
|
|
|
c.port = int(uint16(b[5]) | uint16(b[4])<<8)
|
|
|
|
c.id = BitmapFromBytesP(b[6:])
|
2018-03-09 01:50:18 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-04-28 02:16:12 +02:00
|
|
|
func (c Contact) MarshalBencode() ([]byte, error) {
|
|
|
|
return bencode.EncodeBytes([]interface{}{c.id, c.ip.String(), c.port})
|
2018-03-09 01:50:18 +01:00
|
|
|
}
|
|
|
|
|
2018-04-28 02:16:12 +02:00
|
|
|
func (c *Contact) UnmarshalBencode(b []byte) error {
|
2018-03-09 01:50:18 +01:00
|
|
|
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))
|
|
|
|
}
|
|
|
|
|
2018-04-28 02:16:12 +02:00
|
|
|
err = bencode.DecodeBytes(raw[0], &c.id)
|
2018-03-09 01:50:18 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var ipStr string
|
|
|
|
err = bencode.DecodeBytes(raw[1], &ipStr)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-04-28 02:16:12 +02:00
|
|
|
c.ip = net.ParseIP(ipStr).To4()
|
|
|
|
if c.ip == nil {
|
2018-03-09 01:50:18 +01:00
|
|
|
return errors.Err("invalid IP")
|
|
|
|
}
|
|
|
|
|
2018-04-28 02:16:12 +02:00
|
|
|
err = bencode.DecodeBytes(raw[2], &c.port)
|
2018-03-09 01:50:18 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-04-28 02:16:12 +02:00
|
|
|
type sortedContact struct {
|
|
|
|
contact Contact
|
2018-04-05 22:05:28 +02:00
|
|
|
xorDistanceToTarget Bitmap
|
2018-03-09 01:50:18 +01:00
|
|
|
}
|
|
|
|
|
2018-04-28 02:16:12 +02:00
|
|
|
type byXorDistance []sortedContact
|
2018-03-09 01:50:18 +01:00
|
|
|
|
|
|
|
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-04-05 17:35:57 +02:00
|
|
|
type routingTable struct {
|
2018-04-28 02:16:12 +02:00
|
|
|
id Bitmap
|
2018-03-07 02:15:44 +01:00
|
|
|
buckets [numBuckets]*list.List
|
2018-03-24 00:18:00 +01:00
|
|
|
lock *sync.RWMutex
|
2018-03-07 02:15:44 +01:00
|
|
|
}
|
|
|
|
|
2018-04-28 02:16:12 +02:00
|
|
|
func newRoutingTable(id Bitmap) *routingTable {
|
2018-04-05 17:35:57 +02:00
|
|
|
var rt routingTable
|
2018-03-07 02:15:44 +01:00
|
|
|
for i := range rt.buckets {
|
|
|
|
rt.buckets[i] = list.New()
|
|
|
|
}
|
2018-04-28 02:16:12 +02:00
|
|
|
rt.id = id
|
2018-03-24 00:18:00 +01:00
|
|
|
rt.lock = &sync.RWMutex{}
|
2018-03-07 02:15:44 +01:00
|
|
|
return &rt
|
|
|
|
}
|
|
|
|
|
2018-04-05 17:35:57 +02:00
|
|
|
func (rt *routingTable) BucketInfo() string {
|
2018-03-24 00:18:00 +01:00
|
|
|
rt.lock.RLock()
|
|
|
|
defer rt.lock.RUnlock()
|
|
|
|
|
2018-04-28 02:16:12 +02:00
|
|
|
var bucketInfo []string
|
2018-03-24 00:18:00 +01:00
|
|
|
for i, b := range rt.buckets {
|
2018-04-05 22:05:28 +02:00
|
|
|
contents := bucketContents(b)
|
|
|
|
if contents != "" {
|
|
|
|
bucketInfo = append(bucketInfo, fmt.Sprintf("Bucket %d: %s", i, contents))
|
2018-03-24 00:18:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(bucketInfo) == 0 {
|
|
|
|
return "buckets are empty"
|
|
|
|
}
|
|
|
|
return strings.Join(bucketInfo, "\n")
|
|
|
|
}
|
|
|
|
|
2018-04-05 22:05:28 +02:00
|
|
|
func bucketContents(b *list.List) string {
|
|
|
|
count := 0
|
|
|
|
ids := ""
|
|
|
|
for curr := b.Front(); curr != nil; curr = curr.Next() {
|
|
|
|
count++
|
|
|
|
if ids != "" {
|
|
|
|
ids += ", "
|
|
|
|
}
|
2018-04-28 02:16:12 +02:00
|
|
|
ids += curr.Value.(Contact).id.HexShort()
|
2018-04-05 22:05:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if count > 0 {
|
|
|
|
return fmt.Sprintf("(%d) %s", count, ids)
|
|
|
|
} else {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-28 02:16:12 +02:00
|
|
|
// Update inserts or refreshes a contact
|
|
|
|
func (rt *routingTable) Update(c Contact) {
|
2018-03-24 00:18:00 +01:00
|
|
|
rt.lock.Lock()
|
|
|
|
defer rt.lock.Unlock()
|
2018-04-28 02:16:12 +02:00
|
|
|
bucketNum := bucketFor(rt.id, c.id)
|
2018-03-24 00:18:00 +01:00
|
|
|
bucket := rt.buckets[bucketNum]
|
2018-04-28 02:16:12 +02:00
|
|
|
element := findInList(bucket, c.id)
|
2018-03-07 02:15:44 +01:00
|
|
|
if element == nil {
|
2018-03-24 00:18:00 +01:00
|
|
|
if bucket.Len() >= bucketSize {
|
2018-04-28 02:16:12 +02:00
|
|
|
// TODO: Ping front contact first. Only remove if it does not respond
|
2018-03-24 00:18:00 +01:00
|
|
|
bucket.Remove(bucket.Front())
|
2018-03-07 02:15:44 +01:00
|
|
|
}
|
2018-04-28 02:16:12 +02:00
|
|
|
bucket.PushBack(c)
|
2018-03-07 02:15:44 +01:00
|
|
|
} else {
|
|
|
|
bucket.MoveToBack(element)
|
2018-04-25 00:12:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-28 02:16:12 +02:00
|
|
|
// UpdateIfExists refreshes a contact if its already in the routing table
|
|
|
|
func (rt *routingTable) UpdateIfExists(c Contact) {
|
2018-04-25 00:12:17 +02:00
|
|
|
rt.lock.Lock()
|
|
|
|
defer rt.lock.Unlock()
|
2018-04-28 02:16:12 +02:00
|
|
|
bucketNum := bucketFor(rt.id, c.id)
|
2018-04-25 00:12:17 +02:00
|
|
|
bucket := rt.buckets[bucketNum]
|
2018-04-28 02:16:12 +02:00
|
|
|
element := findInList(bucket, c.id)
|
2018-04-25 00:12:17 +02:00
|
|
|
if element != nil {
|
|
|
|
bucket.MoveToBack(element)
|
2018-03-07 02:15:44 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-05 22:05:28 +02:00
|
|
|
func (rt *routingTable) RemoveByID(id Bitmap) {
|
2018-03-24 00:18:00 +01:00
|
|
|
rt.lock.Lock()
|
|
|
|
defer rt.lock.Unlock()
|
2018-04-28 02:16:12 +02:00
|
|
|
bucketNum := bucketFor(rt.id, id)
|
2018-03-24 00:18:00 +01:00
|
|
|
bucket := rt.buckets[bucketNum]
|
2018-04-28 02:16:12 +02:00
|
|
|
element := findInList(bucket, rt.id)
|
2018-03-24 00:18:00 +01:00
|
|
|
if element != nil {
|
|
|
|
bucket.Remove(element)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-28 02:16:12 +02:00
|
|
|
func (rt *routingTable) GetClosest(target Bitmap, limit int) []Contact {
|
2018-03-24 00:18:00 +01:00
|
|
|
rt.lock.RLock()
|
|
|
|
defer rt.lock.RUnlock()
|
|
|
|
|
2018-04-28 02:16:12 +02:00
|
|
|
var toSort []sortedContact
|
2018-03-29 03:05:27 +02:00
|
|
|
var bucketNum int
|
|
|
|
|
2018-04-28 02:16:12 +02:00
|
|
|
if rt.id.Equals(target) {
|
2018-03-29 03:05:27 +02:00
|
|
|
bucketNum = 0
|
|
|
|
} else {
|
2018-04-28 02:16:12 +02:00
|
|
|
bucketNum = bucketFor(rt.id, target)
|
2018-03-29 03:05:27 +02:00
|
|
|
}
|
2018-03-07 02:15:44 +01:00
|
|
|
|
2018-03-24 00:18:00 +01:00
|
|
|
bucket := rt.buckets[bucketNum]
|
2018-04-28 02:16:12 +02:00
|
|
|
toSort = appendContacts(toSort, bucket.Front(), target)
|
2018-03-07 02:15:44 +01:00
|
|
|
|
2018-03-24 00:18:00 +01:00
|
|
|
for i := 1; (bucketNum-i >= 0 || bucketNum+i < numBuckets) && len(toSort) < limit; i++ {
|
|
|
|
if bucketNum-i >= 0 {
|
|
|
|
bucket = rt.buckets[bucketNum-i]
|
2018-04-28 02:16:12 +02:00
|
|
|
toSort = appendContacts(toSort, bucket.Front(), target)
|
2018-03-07 02:15:44 +01:00
|
|
|
}
|
2018-03-24 00:18:00 +01:00
|
|
|
if bucketNum+i < numBuckets {
|
|
|
|
bucket = rt.buckets[bucketNum+i]
|
2018-04-28 02:16:12 +02:00
|
|
|
toSort = appendContacts(toSort, bucket.Front(), target)
|
2018-03-07 02:15:44 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sort.Sort(byXorDistance(toSort))
|
|
|
|
|
2018-04-28 02:16:12 +02:00
|
|
|
var contacts []Contact
|
|
|
|
for _, sorted := range toSort {
|
|
|
|
contacts = append(contacts, sorted.contact)
|
|
|
|
if len(contacts) >= limit {
|
2018-03-24 00:18:00 +01:00
|
|
|
break
|
|
|
|
}
|
2018-03-07 02:15:44 +01:00
|
|
|
}
|
|
|
|
|
2018-04-28 02:16:12 +02:00
|
|
|
return contacts
|
2018-03-07 02:15:44 +01:00
|
|
|
}
|
|
|
|
|
2018-04-28 02:16:12 +02:00
|
|
|
// Count returns the number of contacts in the routing table
|
2018-04-25 03:12:32 +02:00
|
|
|
func (rt *routingTable) Count() int {
|
|
|
|
rt.lock.RLock()
|
|
|
|
defer rt.lock.RUnlock()
|
|
|
|
count := 0
|
|
|
|
for _, bucket := range rt.buckets {
|
|
|
|
for curr := bucket.Front(); curr != nil; curr = curr.Next() {
|
|
|
|
count++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return count
|
|
|
|
}
|
|
|
|
|
2018-04-05 22:05:28 +02:00
|
|
|
func findInList(bucket *list.List, value Bitmap) *list.Element {
|
2018-03-07 02:15:44 +01:00
|
|
|
for curr := bucket.Front(); curr != nil; curr = curr.Next() {
|
2018-04-28 02:16:12 +02:00
|
|
|
if curr.Value.(Contact).id.Equals(value) {
|
2018-03-07 02:15:44 +01:00
|
|
|
return curr
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-04-28 02:16:12 +02:00
|
|
|
func appendContacts(contacts []sortedContact, start *list.Element, target Bitmap) []sortedContact {
|
2018-03-24 00:18:00 +01:00
|
|
|
for curr := start; curr != nil; curr = curr.Next() {
|
2018-04-28 02:16:12 +02:00
|
|
|
c := curr.Value.(Contact)
|
|
|
|
contacts = append(contacts, sortedContact{c, c.id.Xor(target)})
|
2018-03-07 02:15:44 +01:00
|
|
|
}
|
2018-04-28 02:16:12 +02:00
|
|
|
return contacts
|
2018-03-07 02:15:44 +01:00
|
|
|
}
|
2018-03-24 00:18:00 +01:00
|
|
|
|
2018-04-05 22:05:28 +02:00
|
|
|
func bucketFor(id Bitmap, target Bitmap) int {
|
2018-03-24 00:18:00 +01:00
|
|
|
if id.Equals(target) {
|
2018-04-28 02:16:12 +02:00
|
|
|
panic("routing table does not have a bucket for its own id")
|
2018-03-24 00:18:00 +01:00
|
|
|
}
|
|
|
|
return numBuckets - 1 - target.Xor(id).PrefixLen()
|
|
|
|
}
|