lbry.go/dht/message.go

474 lines
11 KiB
Go
Raw Normal View History

2018-03-07 22:15:58 +01:00
package dht
import (
2018-04-03 19:38:01 +02:00
"crypto/rand"
"encoding/hex"
2018-04-03 19:38:01 +02:00
"reflect"
2018-04-05 17:35:57 +02:00
"strconv"
2018-04-03 18:14:04 +02:00
"strings"
"github.com/lbryio/lbry.go/v3/dht/bits"
2018-03-07 22:15:58 +01:00
"github.com/cockroachdb/errors"
"github.com/lyoshenka/bencode"
2018-03-07 22:15:58 +01:00
"github.com/spf13/cast"
)
const (
pingMethod = "ping"
storeMethod = "store"
findNodeMethod = "findNode"
findValueMethod = "findValue"
)
const (
pingSuccessResponse = "pong"
storeSuccessResponse = "OK"
)
const (
requestType = 0
responseType = 1
errorType = 2
)
const (
// these are strings because bencode requires bytestring keys
headerTypeField = "0"
headerMessageIDField = "1" // message id is 20 bytes long
headerNodeIDField = "2" // node id is 48 bytes long
headerPayloadField = "3"
headerArgsField = "4"
contactsField = "contacts"
pageField = "p"
tokenField = "token"
2018-06-21 19:03:25 +02:00
protocolVersionField = "protocolVersion"
2018-03-07 22:15:58 +01:00
)
2018-07-13 19:31:54 +02:00
// Message is a DHT message
2018-03-07 22:15:58 +01:00
type Message interface {
bencode.Marshaler
}
2018-04-03 19:38:01 +02:00
type messageID [messageIDLength]byte
// HexShort returns the first 8 hex characters of the hex encoded message id.
2018-04-03 19:38:01 +02:00
func (m messageID) HexShort() string {
return hex.EncodeToString(m[:])[:8]
}
// UnmarshalBencode takes a byte slice and unmarshals the message id.
2018-04-03 19:38:01 +02:00
func (m *messageID) UnmarshalBencode(encoded []byte) error {
var str string
err := bencode.DecodeBytes(encoded, &str)
if err != nil {
2022-10-14 17:18:38 +02:00
return errors.Wrap(err, "")
2018-04-03 19:38:01 +02:00
}
copy(m[:], str)
return nil
}
// MarshalBencode returns the encoded byte slice of the message id.
2018-04-03 19:38:01 +02:00
func (m messageID) MarshalBencode() ([]byte, error) {
str := string(m[:])
return bencode.EncodeBytes(str)
}
func newMessageID() messageID {
var m messageID
_, err := rand.Read(m[:])
if err != nil {
panic(err)
}
return m
}
2018-07-13 19:31:54 +02:00
// Request represents a DHT request message
2018-03-07 22:15:58 +01:00
type Request struct {
2018-06-21 19:03:25 +02:00
ID messageID
NodeID bits.Bitmap
Method string
Arg *bits.Bitmap
StoreArgs *storeArgs
ProtocolVersion int
2018-03-07 22:15:58 +01:00
}
// MarshalBencode returns the serialized byte slice representation of the request
2018-03-07 22:15:58 +01:00
func (r Request) MarshalBencode() ([]byte, error) {
var args interface{}
if r.StoreArgs != nil {
args = r.StoreArgs
2018-04-04 18:01:44 +02:00
} else if r.Arg != nil {
2018-06-14 17:48:02 +02:00
args = []bits.Bitmap{*r.Arg}
2018-04-24 23:19:16 +02:00
} else {
args = []string{} // request must always have keys 0-4, so we use an empty list for PING
2018-03-07 22:15:58 +01:00
}
2022-10-14 17:18:38 +02:00
b, err := bencode.EncodeBytes(map[string]interface{}{
2018-03-07 22:15:58 +01:00
headerTypeField: requestType,
headerMessageIDField: r.ID,
headerNodeIDField: r.NodeID,
headerPayloadField: r.Method,
headerArgsField: args,
})
2022-10-14 17:18:38 +02:00
return b, errors.Wrap(err, "bencode")
2018-03-07 22:15:58 +01:00
}
// UnmarshalBencode unmarshals the serialized byte slice into the appropriate fields of the request.
2018-03-07 22:15:58 +01:00
func (r *Request) UnmarshalBencode(b []byte) error {
var raw struct {
2018-04-03 19:38:01 +02:00
ID messageID `bencode:"1"`
2018-06-14 17:48:02 +02:00
NodeID bits.Bitmap `bencode:"2"`
2018-03-07 22:15:58 +01:00
Method string `bencode:"3"`
Args bencode.RawMessage `bencode:"4"`
}
err := bencode.DecodeBytes(b, &raw)
if err != nil {
2022-10-14 17:18:38 +02:00
return errors.Wrap(err, "request unmarshal")
2018-03-07 22:15:58 +01:00
}
r.ID = raw.ID
r.NodeID = raw.NodeID
r.Method = raw.Method
if r.Method == storeMethod {
2018-03-08 01:49:33 +01:00
r.StoreArgs = &storeArgs{} // bencode wont find the unmarshaler on a null pointer. need to fix it.
2018-03-07 22:15:58 +01:00
err = bencode.DecodeBytes(raw.Args, &r.StoreArgs)
2018-04-04 18:01:44 +02:00
if err != nil {
2022-10-14 17:18:38 +02:00
return errors.Wrap(err, "request unmarshal")
2018-04-04 18:01:44 +02:00
}
} else if len(raw.Args) > 2 { // 2 because an empty list is `le`
2018-06-21 19:03:25 +02:00
r.Arg, r.ProtocolVersion, err = processArgsAndProtoVersion(raw.Args)
2018-04-04 18:01:44 +02:00
if err != nil {
return errors.WithMessage(err, "request unmarshal")
2018-04-04 18:01:44 +02:00
}
2018-03-07 22:15:58 +01:00
}
return nil
}
2018-06-21 19:03:25 +02:00
func processArgsAndProtoVersion(raw bencode.RawMessage) (arg *bits.Bitmap, version int, err error) {
var args []bencode.RawMessage
err = bencode.DecodeBytes(raw, &args)
if err != nil {
2022-10-14 17:18:38 +02:00
return nil, 0, errors.Wrap(err, "")
2018-06-21 19:03:25 +02:00
}
if len(args) == 0 {
return nil, 0, nil
}
var extras map[string]int
err = bencode.DecodeBytes(args[len(args)-1], &extras)
if err == nil {
if v, exists := extras[protocolVersionField]; exists {
version = v
args = args[:len(args)-1]
}
}
if len(args) > 0 {
var b bits.Bitmap
err = bencode.DecodeBytes(args[0], &b)
if err != nil {
2022-10-14 17:18:38 +02:00
return nil, 0, errors.Wrap(err, "")
2018-06-21 19:03:25 +02:00
}
arg = &b
}
return arg, version, nil
}
func (r Request) argsDebug() string {
2018-04-05 17:35:57 +02:00
if r.StoreArgs != nil {
return r.StoreArgs.BlobHash.HexShort() + ", " + r.StoreArgs.Value.LbryID.HexShort() + ":" + strconv.Itoa(r.StoreArgs.Value.Port)
} else if r.Arg != nil {
2018-04-04 18:01:44 +02:00
return r.Arg.HexShort()
2018-04-04 17:43:27 +02:00
}
2018-04-04 18:01:44 +02:00
return ""
2018-04-04 17:43:27 +02:00
}
2018-04-03 19:38:01 +02:00
type storeArgsValue struct {
2018-06-14 17:48:02 +02:00
Token string `bencode:"token"`
LbryID bits.Bitmap `bencode:"lbryid"`
Port int `bencode:"port"`
2018-04-03 19:38:01 +02:00
}
2018-03-07 22:15:58 +01:00
type storeArgs struct {
2018-06-14 17:48:02 +02:00
BlobHash bits.Bitmap
2018-04-03 19:38:01 +02:00
Value storeArgsValue
2018-06-14 17:48:02 +02:00
NodeID bits.Bitmap // original publisher id? I think this is getting fixed in the new dht stuff
SelfStore bool // this is an int on the wire
2018-03-07 22:15:58 +01:00
}
// MarshalBencode returns the serialized byte slice representation of the storage arguments.
2018-03-08 01:49:33 +01:00
func (s storeArgs) MarshalBencode() ([]byte, error) {
2018-03-07 22:15:58 +01:00
encodedValue, err := bencode.EncodeString(s.Value)
if err != nil {
return nil, err
}
selfStoreStr := 0
if s.SelfStore {
selfStoreStr = 1
}
return bencode.EncodeBytes([]interface{}{
s.BlobHash,
bencode.RawMessage(encodedValue),
s.NodeID,
selfStoreStr,
})
}
// UnmarshalBencode unmarshals the serialized byte slice into the appropriate fields of the store arguments.
2018-03-07 22:15:58 +01:00
func (s *storeArgs) UnmarshalBencode(b []byte) error {
var argsInt []bencode.RawMessage
err := bencode.DecodeBytes(b, &argsInt)
if err != nil {
2022-10-14 17:18:38 +02:00
return errors.Wrap(err, "storeArgs unmarshal")
2018-03-07 22:15:58 +01:00
}
if len(argsInt) != 4 {
2022-10-14 17:18:38 +02:00
return errors.Wrap(errors.Newf("unexpected number of fields for store args. got %d", len(argsInt)), "")
2018-03-07 22:15:58 +01:00
}
err = bencode.DecodeBytes(argsInt[0], &s.BlobHash)
if err != nil {
2022-10-14 17:18:38 +02:00
return errors.Wrap(err, "storeArgs unmarshal")
2018-03-07 22:15:58 +01:00
}
err = bencode.DecodeBytes(argsInt[1], &s.Value)
if err != nil {
2022-10-14 17:18:38 +02:00
return errors.Wrap(err, "storeArgs unmarshal")
2018-03-07 22:15:58 +01:00
}
err = bencode.DecodeBytes(argsInt[2], &s.NodeID)
if err != nil {
2022-10-14 17:18:38 +02:00
return errors.Wrap(err, "storeArgs unmarshal")
2018-03-07 22:15:58 +01:00
}
var selfStore int
err = bencode.DecodeBytes(argsInt[3], &selfStore)
if err != nil {
2022-10-14 17:18:38 +02:00
return errors.Wrap(err, "storeArgs unmarshal")
2018-03-07 22:15:58 +01:00
}
if selfStore == 0 {
s.SelfStore = false
} else if selfStore == 1 {
s.SelfStore = true
} else {
2022-10-14 17:18:38 +02:00
return errors.Wrap(errors.New("selfstore must be 1 or 0"), "")
2018-03-07 22:15:58 +01:00
}
return nil
}
2018-07-13 19:31:54 +02:00
// Response represents a DHT response message
2018-03-07 22:15:58 +01:00
type Response struct {
2018-06-21 19:03:25 +02:00
ID messageID
NodeID bits.Bitmap
Data string
Contacts []Contact
FindValueKey string
Token string
ProtocolVersion int
Page uint8
2018-03-07 22:15:58 +01:00
}
func (r Response) argsDebug() string {
2018-04-03 18:14:04 +02:00
if r.Data != "" {
return r.Data
}
str := "contacts "
if r.FindValueKey != "" {
2018-04-03 18:14:04 +02:00
str = "value for " + hex.EncodeToString([]byte(r.FindValueKey))[:8] + " "
}
2018-04-03 18:14:04 +02:00
str += "|"
2018-04-28 02:16:12 +02:00
for _, c := range r.Contacts {
2018-06-21 19:03:25 +02:00
str += c.String() + ","
}
2018-04-03 18:14:04 +02:00
str = strings.TrimRight(str, ",") + "|"
if r.Token != "" {
str += " token: " + hex.EncodeToString([]byte(r.Token))[:8]
}
2018-04-03 18:14:04 +02:00
return str
}
// MarshalBencode returns the serialized byte slice representation of the response.
2018-03-07 22:15:58 +01:00
func (r Response) MarshalBencode() ([]byte, error) {
data := map[string]interface{}{
headerTypeField: responseType,
headerMessageIDField: r.ID,
headerNodeIDField: r.NodeID,
}
2018-03-07 22:15:58 +01:00
if r.Data != "" {
// ping or store
2018-03-07 22:15:58 +01:00
data[headerPayloadField] = r.Data
2018-03-09 22:43:30 +01:00
} else if r.FindValueKey != "" {
// findValue success
if r.Token == "" {
return nil, errors.WithStack(errors.New("response to findValue must have a token"))
}
2018-03-09 22:43:30 +01:00
var contacts [][]byte
2018-04-28 02:16:12 +02:00
for _, c := range r.Contacts {
compact, err := c.MarshalCompact()
2018-03-09 22:43:30 +01:00
if err != nil {
return nil, err
}
contacts = append(contacts, compact)
}
data[headerPayloadField] = map[string]interface{}{
r.FindValueKey: contacts,
tokenField: r.Token,
}
} else if r.Token != "" {
// findValue failure falling back to findNode
data[headerPayloadField] = map[string]interface{}{
2018-04-28 02:16:12 +02:00
contactsField: r.Contacts,
tokenField: r.Token,
}
2018-03-07 22:15:58 +01:00
} else {
// straight up findNode
2018-04-28 02:16:12 +02:00
data[headerPayloadField] = r.Contacts
2018-03-07 22:15:58 +01:00
}
return bencode.EncodeBytes(data)
}
// UnmarshalBencode unmarshals the serialized byte slice into the appropriate fields of the store arguments.
2018-03-07 22:15:58 +01:00
func (r *Response) UnmarshalBencode(b []byte) error {
var raw struct {
2018-04-03 19:38:01 +02:00
ID messageID `bencode:"1"`
2018-06-14 17:48:02 +02:00
NodeID bits.Bitmap `bencode:"2"`
Data bencode.RawMessage `bencode:"3"`
2018-03-07 22:15:58 +01:00
}
err := bencode.DecodeBytes(b, &raw)
if err != nil {
return err
}
r.ID = raw.ID
r.NodeID = raw.NodeID
// maybe data is a string (response to ping or store)?
2018-03-07 22:15:58 +01:00
err = bencode.DecodeBytes(raw.Data, &r.Data)
if err == nil {
return nil
}
2018-05-01 22:18:38 +02:00
// maybe data is a list of contacts (response to findNode)?
2018-04-28 02:16:12 +02:00
err = bencode.DecodeBytes(raw.Data, &r.Contacts)
if err == nil {
return nil
}
// it must be a response to findValue
var rawData map[string]bencode.RawMessage
err = bencode.DecodeBytes(raw.Data, &rawData)
2018-03-07 22:15:58 +01:00
if err != nil {
return err
}
if token, ok := rawData[tokenField]; ok {
err = bencode.DecodeBytes(token, &r.Token)
2018-03-29 03:05:27 +02:00
if err != nil {
return err
}
2018-06-21 19:03:25 +02:00
delete(rawData, tokenField) // so it doesnt mess up findValue key finding below
}
if protocolVersion, ok := rawData[protocolVersionField]; ok {
err = bencode.DecodeBytes(protocolVersion, &r.ProtocolVersion)
if err != nil {
return err
}
delete(rawData, protocolVersionField) // so it doesnt mess up findValue key finding below
}
2018-03-29 03:05:27 +02:00
if contacts, ok := rawData[contactsField]; ok {
2018-04-28 02:16:12 +02:00
err = bencode.DecodeBytes(contacts, &r.Contacts)
delete(rawData, contactsField) // so it doesnt mess up findValue key finding below
if err != nil {
return err
}
}
if page, ok := rawData[pageField]; ok {
err = bencode.DecodeBytes(page, &r.Page)
delete(rawData, pageField) // so it doesnt mess up findValue key finding below
if err != nil {
return err
}
}
for k, v := range rawData {
r.FindValueKey = k
var compactContacts [][]byte
err = bencode.DecodeBytes(v, &compactContacts)
if err != nil {
return err
}
for _, compact := range compactContacts {
var c Contact
err = c.UnmarshalCompact(compact)
2018-04-03 18:14:04 +02:00
if err != nil {
return err
}
r.Contacts = append(r.Contacts, c)
2018-03-29 03:05:27 +02:00
}
break
2018-03-07 22:15:58 +01:00
}
2018-03-08 01:49:33 +01:00
return nil
2018-03-07 22:15:58 +01:00
}
2018-07-13 19:31:54 +02:00
// Error represents a DHT error response
2018-03-07 22:15:58 +01:00
type Error struct {
2018-04-03 19:38:01 +02:00
ID messageID
2018-06-14 17:48:02 +02:00
NodeID bits.Bitmap
2018-03-07 22:15:58 +01:00
ExceptionType string
2018-04-03 19:38:01 +02:00
Response []string
2018-03-07 22:15:58 +01:00
}
// MarshalBencode returns the serialized byte slice representation of an error message.
2018-03-07 22:15:58 +01:00
func (e Error) MarshalBencode() ([]byte, error) {
return bencode.EncodeBytes(map[string]interface{}{
headerTypeField: errorType,
headerMessageIDField: e.ID,
headerNodeIDField: e.NodeID,
headerPayloadField: e.ExceptionType,
headerArgsField: e.Response,
})
}
2018-04-03 19:38:01 +02:00
// UnmarshalBencode unmarshals the serialized byte slice into the appropriate fields of the error message.
2018-04-03 19:38:01 +02:00
func (e *Error) UnmarshalBencode(b []byte) error {
var raw struct {
ID messageID `bencode:"1"`
2018-06-14 17:48:02 +02:00
NodeID bits.Bitmap `bencode:"2"`
2018-04-03 19:38:01 +02:00
ExceptionType string `bencode:"3"`
Args interface{} `bencode:"4"`
}
err := bencode.DecodeBytes(b, &raw)
if err != nil {
2022-10-14 17:18:38 +02:00
return errors.Wrap(err, "")
2018-04-03 19:38:01 +02:00
}
e.ID = raw.ID
e.NodeID = raw.NodeID
e.ExceptionType = raw.ExceptionType
if reflect.TypeOf(raw.Args).Kind() == reflect.Slice {
v := reflect.ValueOf(raw.Args)
for i := 0; i < v.Len(); i++ {
e.Response = append(e.Response, cast.ToString(v.Index(i).Interface()))
}
}
return nil
}