# -*- coding: binary -*- require 'rex/proto/kademlia/message' require 'rex/proto/kademlia/util' module Rex module Proto module Kademlia # Opcode for a bootstrap response BOOTSTRAP_RESPONSE = 0x09 # A Kademlia bootstrap response message class BootstrapResponse < Message # @return [String] the ID of the peer that send the bootstrap response attr_reader :peer_id # @return [Integer] the TCP port that the responding peer is listening on attr_reader :tcp_port # @return [Integer] the version of this peer attr_reader :version # @return [Array>] the peer ID, IP address, UDP/TCP ports and version of each peer attr_reader :peers # Constructs a new bootstrap response # # @param peer_id [String] the ID of this peer # @param tcp_port [Integer] the TCP port that this peer is listening on # @param version [Integer] the version of this peer # @param peers [Array>] the peer ID, IP address, UDP/TCP ports and version of each peer def initialize(peer_id, tcp_port, version, peers) @peer_id = peer_id @tcp_port = tcp_port @version = version @peers = peers end # The minimum size of a peer in a KADEMLIA2_BOOTSTRAP_RES message: # peer ID (16-bytes), IP (4 bytes), UDP port (2 bytes), TCP port (2 bytes) # and version (1 byte) BOOTSTRAP_PEER_SIZE = 25 # Builds a bootstrap response from given data # # @param data [String] the data to decode # @return [BootstrapResponse] the bootstrap response if the data is valid, nil otherwise def self.from_data(data) message = Message.from_data(data) # abort if this isn't a valid response return unless message return unless message.type == BOOTSTRAP_RESPONSE return unless message.body.size >= 23 bootstrap_peer_id = Rex::Proto::Kademlia.decode_peer_id(message.body.slice!(0, 16)) bootstrap_tcp_port, bootstrap_version, num_peers = message.body.slice!(0, 5).unpack('vCv') # protocol says there are no peers and the body confirms this, so just return with no peers if num_peers == 0 && message.body.blank? peers = [] else peers_data = message.body # peers data is too long/short, abort return if peers_data.size % BOOTSTRAP_PEER_SIZE != 0 peers = [] until peers_data.blank? peer_data = peers_data.slice!(0, BOOTSTRAP_PEER_SIZE) peer_id = Rex::Proto::Kademlia.decode_peer_id(peer_data.slice!(0, 16)) ip, udp_port, tcp_port, version = peer_data.unpack('VvvC') peers << { id: peer_id, ip: Rex::Socket.addr_itoa(ip), tcp_port: tcp_port, udp_port: udp_port, version: version } end end BootstrapResponse.new(bootstrap_peer_id, bootstrap_tcp_port, bootstrap_version, peers) end end end end end