Files
go-i2p-bt/krpc/message.go

441 lines
12 KiB
Go
Raw Normal View History

2020-06-07 13:43:15 +08:00
// Copyright 2020 xgfone
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package krpc
import (
"bytes"
"fmt"
"github.com/xgfone/bt/bencode"
"github.com/xgfone/bt/metainfo"
)
// Predefine some error code.
const (
// BEP 5
ErrorCodeGeneric = 201
ErrorCodeServer = 202
ErrorCodeProtocol = 203
ErrorCodeMethodUnknown = 204
// BEP 44
ErrorCodeMessageValueFieldTooBig = 205
ErrorCodeInvalidSignature = 206
ErrorCodeSaltFieldTooBig = 207
ErrorCodeCasHashMismatched = 301
ErrorCodeSequenceNumberLessThanCurrent = 302
)
// Message represents messages that nodes in the network send to each other
// as specified by the KRPC protocol, which are also referred to as the KRPC
// messages.
//
// There are three types of messages: QUERY, RESPONSE, ERROR
// The message is a dictonary that is then "bencoded"
// (serialization & compression format adopted by the BitTorrent)
// and sent via the UDP connection to peers.
//
// A KRPC message is a single dictionary with two keys common to every message
// and additional keys depending on the type of message. Every message has a key
// "t" with a string value representing a transaction ID. This transaction ID
// is generated by the querying node and is echoed in the response, so responses
// may be correlated with multiple queries to the same node. The transaction ID
// should be encoded as a short string of binary numbers, typically 2 characters
// are enough as they cover 2^16 outstanding queries. The other key contained
// in every KRPC message is "y" with a single character value describing
// the type of message. The value of the "y" key is one of "q" for query,
// "r" for response, or "e" for error.
type Message struct {
T string `bencode:"t"` // required: transaction ID
Y string `bencode:"y"` // required: type of the message: q for QUERY, r for RESPONSE, e for ERROR
Q string `bencode:"q,omitempty"` // Query method (one of 4: "ping", "find_node", "get_peers", "announce_peer")
A QueryArg `bencode:"a,omitempty"` // named arguments sent with a query
R ResponseResult `bencode:"r,omitempty"` // RESPONSE type only
E Error `bencode:"e,omitempty"` // ERROR type only
RO bool `bencode:"ro,omitempty"` // BEP 43: ReadOnly
}
// NewQueryMsg return a QUERY message.
func NewQueryMsg(tid, method string, arg QueryArg) Message {
return Message{T: tid, Y: "q", Q: method, A: arg}
}
// NewResponseMsg return a RESPONSE message.
func NewResponseMsg(tid string, data ResponseResult) Message {
return Message{T: tid, Y: "r", R: data}
}
// NewErrorMsg return a ERROR message.
func NewErrorMsg(tid string, code int, reason string) Message {
return Message{T: tid, Y: "e", E: Error{Code: code, Reason: reason}}
}
// IsQuery reports whether the message is an QUERY.
func (m Message) IsQuery() bool {
return m.Y == "q"
}
// IsResponse reports whether the message is an RESPONSE.
func (m Message) IsResponse() bool {
return m.Y == "r"
}
// IsError reports whether the message is an ERROR.
func (m Message) IsError() bool {
return m.Y == "e"
}
// RID returns the value named "id" in "r".
//
// Return "" instead if no "id".
func (m Message) RID() metainfo.Hash {
return m.R.ID
}
// QID returns the value named "id" in "a", that's, the query arguments.
//
// Return "" instead if no "id".
func (m Message) QID() metainfo.Hash {
return m.A.ID
}
// ID returns the QID or RID.
func (m Message) ID() metainfo.Hash {
switch m.Y {
case "q":
return m.QID()
case "r":
return m.RID()
default:
panic(fmt.Errorf("unknown message type '%s'", m.Y))
}
}
// Error represents a response error.
type Error struct {
Code int
Reason string
}
// NewError returns a new Error.
func NewError(code int, reason string) Error {
return Error{Code: code, Reason: reason}
}
func (e *Error) decode(vs []interface{}) (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("unpacking %#v: %v", vs, r)
}
}()
e.Code = int(vs[0].(int64))
e.Reason = vs[1].(string)
return
}
// UnmarshalBencode implements the interface bencode.Unmarshaler.
func (e *Error) UnmarshalBencode(b []byte) (err error) {
var iface interface{}
if err = bencode.NewDecoder(bytes.NewBuffer(b)).Decode(&iface); err != nil {
return
}
switch v := iface.(type) {
case []interface{}:
err = e.decode(v)
case string:
e.Reason = v
default:
err = fmt.Errorf(`KRPC error bencode value has unexpected type: %T`, v)
}
return
}
// MarshalBencode implements the interface bencode.Marshaler.
func (e Error) MarshalBencode() (ret []byte, err error) {
if e.Code == 0 && e.Reason == "" {
return nil, nil
}
buf := bytes.NewBuffer(nil)
buf.Grow(32)
err = bencode.NewEncoder(buf).Encode([]interface{}{e.Code, e.Reason})
if err != nil {
ret = buf.Bytes()
}
return
}
func (e Error) Error() string {
return fmt.Sprintf("KRPC error %d: %s", e.Code, e.Reason)
}
// Want represents the type of nodes that the request wants.
//
// BEP 32
type Want string
// Predefine some wants.
//
// BEP 32
const (
WantNodes Want = "n4"
WantNodes6 Want = "n6"
)
// QueryArg represents the arguments used by the QUERY message.
type QueryArg struct {
// ID is used to identify a querying node.
ID metainfo.Hash `bencode:"id"` // BEP 5
// Target is used to identify the node sought by the queryer.
//
// find_node
Target metainfo.Hash `bencode:"target,omitempty"` // BEP 5
// InfoHash is the infohash of the torrent file.
//
// get_peers, announce_peer
InfoHash metainfo.Hash `bencode:"info_hash,omitempty"` // BEP 5
// Port is the port on where sender is listening to allow others
// to download the torrent.
//
// announce_peer
Port uint16 `bencode:"port,omitempty"` // BEP 5
// Token is the received one from an earlier "get_peers" query.
//
// announce_peer
Token string `bencode:"token,omitempty"` // BEP 5
// ImpliedPort is used by the senders to apparent DHT port
// to improve NAT support.
//
// announce_peer
ImpliedPort bool `bencode:"implied_port,omitempty"` // BEP 5
// Wants is only used to represent to expect "nodes" for "n4"
// or "nodes6" for "n6".
//
// Notice: It only governs the presence of the "nodes" and "nodes6"
// parameters, not the interpretation of "values".
//
// find_node, get_peers
Wants []Want `bencode:"want,omitempty"` // BEP 32
}
// ContainsWant reports whether the request contains the given Want.
func (a QueryArg) ContainsWant(w Want) bool {
for _, want := range a.Wants {
if want == w {
return true
}
}
return false
}
// GetPort returns the real port of the peer.
func (a QueryArg) GetPort(port int) uint16 {
if a.ImpliedPort {
return uint16(port)
}
return a.Port
}
// ResponseResult represents the results used by the RESPONSE message.
type ResponseResult struct {
// ID is used to indentify the queried node, that's, the response node.
ID metainfo.Hash `bencode:"id"` // BEP 5
// Nodes is a string containing the compact node information for the list
// of the ipv4 target node, or the K(8) closest good nodes in routing table
// of the requested ipv4 target.
//
// find_node
Nodes CompactIPv4Node `bencode:"nodes,omitempty"` // BEP 5
// Nodes6 is a string containing the compact node information for the list
// of the ipv6 target node, or the K(8) closest good nodes in routing table
// of the requested ipv6 target.
//
// find_node
Nodes6 CompactIPv6Node `bencode:"nodes6,omitempty"` // BEP 32
// Token is used for future "announce_peer".
//
// get_peers
Token string `bencode:"token,omitempty"` // BEP 5
// Values is a list of the torrent peers.
//
// get_peers
Values CompactAddresses `bencode:"values,omitempty"` // BEP 5
}
/// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// CompactAddresses represents a group of the compact addresses.
type CompactAddresses []metainfo.Address
// MarshalBinary implements the interface binary.BinaryMarshaler.
func (cas CompactAddresses) MarshalBinary() ([]byte, error) {
ss := make([]string, len(cas))
for i, addr := range cas {
data, err := addr.MarshalBinary()
if err != nil {
return nil, err
}
ss[i] = string(data)
}
return bencode.EncodeBytes(ss)
}
// UnmarshalBinary implements the interface binary.BinaryUnmarshaler.
func (cas *CompactAddresses) UnmarshalBinary(b []byte) (err error) {
var ss []string
if err = bencode.DecodeBytes(b, &ss); err == nil {
addrs := make(CompactAddresses, len(ss))
for i, s := range ss {
var addr metainfo.Address
if err = addr.UnmarshalBinary([]byte(s)); err != nil {
return
}
addrs[i] = addr
}
}
return
}
/// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// CompactIPv4Node is a set of IPv4 Nodes.
type CompactIPv4Node []Node
// MarshalBinary implements the interface binary.BinaryMarshaler.
func (cn CompactIPv4Node) MarshalBinary() ([]byte, error) {
buf := bytes.NewBuffer(nil)
buf.Grow(26 * len(cn))
for _, ni := range cn {
if ni.Addr.IP = ni.Addr.IP.To4(); len(ni.Addr.IP) == 0 {
continue
}
if n, err := ni.WriteBinary(buf); err != nil {
return nil, err
} else if n != 26 {
panic(fmt.Errorf("CompactIPv4NodeInfo: the invalid NodeInfo length '%d'", n))
}
}
return buf.Bytes(), nil
}
// UnmarshalBinary implements the interface binary.BinaryUnmarshaler.
func (cn *CompactIPv4Node) UnmarshalBinary(b []byte) (err error) {
_len := len(b)
if _len%26 != 0 {
return fmt.Errorf("CompactIPv4NodeInfo: invalid bytes length '%d'", _len)
}
nis := make([]Node, 0, _len/26)
for i := 0; i < _len; i += 26 {
var ni Node
if err = ni.UnmarshalBinary(b[i : i+26]); err != nil {
return
}
nis = append(nis, ni)
}
*cn = nis
return
}
// MarshalBencode implements the interface bencode.Marshaler.
func (cn CompactIPv4Node) MarshalBencode() (b []byte, err error) {
if b, err = cn.MarshalBinary(); err == nil {
b, err = bencode.EncodeBytes(b)
}
return
}
// UnmarshalBencode implements the interface bencode.Unmarshaler.
func (cn *CompactIPv4Node) UnmarshalBencode(b []byte) (err error) {
var s string
if err = bencode.DecodeBytes(b, &s); err == nil {
err = cn.UnmarshalBinary([]byte(s))
}
return
}
/// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// CompactIPv6Node is a set of IPv6 Nodes.
type CompactIPv6Node []Node
// MarshalBinary implements the interface binary.BinaryMarshaler.
func (cn CompactIPv6Node) MarshalBinary() ([]byte, error) {
buf := bytes.NewBuffer(nil)
buf.Grow(38 * len(cn))
for _, ni := range cn {
ni.Addr.IP = ni.Addr.IP.To16()
if n, err := ni.WriteBinary(buf); err != nil {
return nil, err
} else if n != 38 {
panic(fmt.Errorf("CompactIPv6NodeInfo: the invalid NodeInfo length '%d'", n))
}
}
return buf.Bytes(), nil
}
// UnmarshalBinary implements the interface binary.BinaryUnmarshaler.
func (cn *CompactIPv6Node) UnmarshalBinary(b []byte) (err error) {
_len := len(b)
if _len%38 != 0 {
return fmt.Errorf("CompactIPv6NodeInfo: invalid bytes length '%d'", _len)
}
nis := make([]Node, 0, _len/38)
for i := 0; i < _len; i += 38 {
var ni Node
if err = ni.UnmarshalBinary(b[i : i+38]); err != nil {
return
}
nis = append(nis, ni)
}
*cn = nis
return
}
// MarshalBencode implements the interface bencode.Marshaler.
func (cn CompactIPv6Node) MarshalBencode() (b []byte, err error) {
if b, err = cn.MarshalBinary(); err == nil {
b, err = bencode.EncodeBytes(b)
}
return
}
// UnmarshalBencode implements the interface bencode.Unmarshaler.
func (cn *CompactIPv6Node) UnmarshalBencode(b []byte) (err error) {
var s string
if err = bencode.DecodeBytes(b, &s); err == nil {
err = cn.UnmarshalBinary([]byte(s))
}
return
}