package dht import ( "github.com/anacrolix/torrent/bencode" ) // Msg represents messages that nodes in the network send to each other as specified by the protocol. // They are also referred to as the KRPC messages. // There are three types of messages: QUERY, RESPONSE, ERROR // The message is a dictionary 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. // 3 message types: QUERY, RESPONSE, ERROR type Msg struct { Q string `bencode:"q,omitempty"` // Query method (one of 4: "ping", "find_node", "get_peers", "announce_peer") A *MsgArgs `bencode:"a,omitempty"` // named arguments sent with a query 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 R *Return `bencode:"r,omitempty"` // RESPONSE type only E *Error `bencode:"e,omitempty"` // ERROR type only IP NodeAddr `bencode:"ip,omitempty"` ReadOnly bool `bencode:"ro,omitempty"` // BEP 43. Sender does not respond to queries. // https://www.libtorrent.org/dht_extensions.html ClientId string `bencode:"v,omitempty"` } const ( YQuery = "q" YResponse = "r" YError = "e" ) type MsgArgs struct { ID ID `bencode:"id"` // ID of the querying Node InfoHash ID `bencode:"info_hash,omitempty"` // InfoHash of the torrent Target ID `bencode:"target,omitempty"` // ID of the node sought // Token received from an earlier get_peers query. Also used in a BEP 44 put. Token string `bencode:"token,omitempty"` Port *int `bencode:"port,omitempty"` // Sender's torrent port ImpliedPort bool `bencode:"implied_port,omitempty"` // Use senders apparent DHT port Want []Want `bencode:"want,omitempty"` // Contains strings like "n4" and "n6" from BEP 32. NoSeed int `bencode:"noseed,omitempty"` // BEP 33 Scrape int `bencode:"scrape,omitempty"` // BEP 33 // BEP 44 // I don't know if we should use bencode.Bytes for this. If we unmarshalled bytes that didn't // marshal back the same, our hashes will not match. But this might also serve to prevent abuse. V interface{} `bencode:"v,omitempty"` // Why is this optional? Because I think we need to know if it wasn't set rather than use a // default value. Seq *int64 `bencode:"seq,omitempty"` Cas int64 `bencode:"cas,omitempty"` K [32]byte `bencode:"k,omitempty"` Salt []byte `bencode:"salt,omitempty"` Sig [64]byte `bencode:"sig,omitempty"` } type Want string const ( WantNodes Want = "n4" WantNodes6 Want = "n6" ) // BEP 51 (DHT Infohash Indexing) type Bep51Return struct { Interval *int64 `bencode:"interval,omitempty"` Num *int64 `bencode:"num,omitempty"` // Nodes supporting this extension should always include the samples field in the response, even // when it is zero-length. This lets indexing nodes to distinguish nodes supporting this // extension from those that respond to unknown query types which contain a target field [2]. Samples *CompactInfohashes `bencode:"samples,omitempty"` } type Bep44Return struct { V bencode.Bytes `bencode:"v,omitempty"` K [32]byte `bencode:"k,omitempty"` Sig [64]byte `bencode:"sig,omitempty"` Seq *int64 `bencode:"seq,omitempty"` } type Return struct { // All returns are supposed to contain an ID, but what if they don't? ID ID `bencode:"id"` // ID of the queried (and responding) node // k closest nodes to the requested target. Included in responses to queries that imply // traversal, for example get_peers, find_nodes, get, sample_infohashes. Nodes CompactIPv4NodeInfo `bencode:"nodes,omitempty"` Nodes6 CompactIPv6NodeInfo `bencode:"nodes6,omitempty"` Token *string `bencode:"token,omitempty"` // Token for future announce_peer or put (BEP 44) Values []NodeAddr `bencode:"values,omitempty"` // Torrent peers // BEP 33 (scrapes) BFsd *ScrapeBloomFilter `bencode:"BFsd,omitempty"` BFpe *ScrapeBloomFilter `bencode:"BFpe,omitempty"` Bep51Return // BEP 44 Bep44Return } type CompactInfohashes []ID func (CompactInfohashes) ElemSize() int { return 20 } func (me CompactInfohashes) MarshalBinary() ([]byte, error) { return marshalBinarySlice(me) } func (me CompactInfohashes) MarshalBencode() ([]byte, error) { return bencodeBytesResult(me.MarshalBinary()) } func (me *CompactInfohashes) UnmarshalBinary(b []byte) error { return unmarshalBinarySlice(me, b) } func (me *CompactInfohashes) UnmarshalBencode(b []byte) error { return unmarshalBencodedBinary(me, b) } type ( CompactIPv6NodeInfo []NodeInfo ) func (CompactIPv6NodeInfo) ElemSize() int { return 38 } func (me CompactIPv6NodeInfo) MarshalBinary() ([]byte, error) { return marshalBinarySlice(me) } func (me CompactIPv6NodeInfo) MarshalBencode() ([]byte, error) { return bencodeBytesResult(me.MarshalBinary()) } func (me *CompactIPv6NodeInfo) UnmarshalBinary(b []byte) error { return unmarshalBinarySlice(me, b) } func (me *CompactIPv6NodeInfo) UnmarshalBencode(b []byte) error { return unmarshalBencodedBinary(me, b) }