Browse Source

Move Kademlia stuff to a more OO model, etc, per reviews

All of the work is done in rex.  The msf mixin just prevents the
desire to call rex directly from the module
Jon Hart 5 years ago
parent
commit
0ed356f71c
No account linked to committer's email address

+ 1
- 104
lib/msf/core/auxiliary/kademlia.rb View File

@@ -1,4 +1,5 @@
1 1
 # -*- coding: binary -*-
2
+
2 3
 require 'rex/proto/kademlia'
3 4
 
4 5
 module Msf
@@ -10,109 +11,5 @@ module Msf
10 11
 ###
11 12
 module Auxiliary::Kademlia
12 13
   include Rex::Proto::Kademlia
13
-
14
-  # Opcode for a BOOTSTRAP request
15
-  BOOTSTRAP_REQ = 0x01
16
-  # Opcode for a BOOTSTRAP response
17
-  BOOTSTRAP_RES = 0x09
18
-  # Opcode for a PING request
19
-  PING = 0x60
20
-  # Opcode for a PING response
21
-  PONG = 0x61
22
-  # The minimum size of a peer in a KADEMLIA2_BOOTSTRAP_RES message:
23
-  # peer ID (16-bytes), IP (4 bytes), UDP port (2 bytes), TCP port (2 bytes)
24
-  # and version (1 byte)
25
-  BOOTSTRAP_PEER_SIZE = 25
26
-
27
-  # Builds a BOOTSTRAP request
28
-  #
29
-  # @return [String] a BOOTSTRAP request
30
-  def bootstrap
31
-    Message.new(BOOTSTRAP_REQ)
32
-  end
33
-
34
-  # Decodes a BOOTSTRAP response
35
-  #
36
-  # @param response [String] the response to decode
37
-  # @return [Array] the discovered peer ID, TCP port, version and a list of peers
38
-  #   if the response if valid, nil otherwise
39
-  def decode_bootstrap_res(response)
40
-    message = Message.from_data(response)
41
-    # abort if this isn't a valid response
42
-    return nil unless message.type = BOOTSTRAP_RES
43
-    return nil unless message.body.size >= 23
44
-    peer_id = decode_peer_id(message.body.slice!(0,16))
45
-    tcp_port, version, num_peers = message.body.slice!(0,5).unpack('vCv')
46
-    # protocol says there are no peers and the body confirms this, so just return with no peers
47
-    return [ tcp_port, version, []] if num_peers == 0 && message.body.blank?
48
-    peers = decode_bootstrap_peers(message.body)
49
-    # abort if the peer data was invalid
50
-    return nil unless peers
51
-    [ peer_id, tcp_port, version, peers ]
52
-  end
53
-
54
-  # Builds a PING request
55
-  #
56
-  # @return [String] a PING request
57
-  def ping
58
-    Message.new(PING)
59
-  end
60
-
61
-  # Decode a PING response, PONG
62
-  #
63
-  # @param response [String] the response to decode
64
-  # @return [Integer] the source port from the PING response if the response is valid, nil otherwise
65
-  def decode_pong(response)
66
-    message = Message.from_data(response)
67
-    # abort if this isn't a pong
68
-    return nil unless message.type == PONG
69
-    # abort if the response is too large/small
70
-    return nil unless message.body && message.body.size == 2
71
-    # this should always be equivalent to the source port from which the PING was received
72
-    message.body.unpack('v')[0]
73
-  end
74
-
75
-  # Decode a list of peers from a BOOTSTRAP response
76
-  #
77
-  # @param peers_data [String] the peers data from a BOOTSTRAP response
78
-  # @return [Array] a list of the peers and their associated metadata extracted
79
-  # from the response if valid, nil otherwise
80
-  def decode_bootstrap_peers(peers_data)
81
-    # sanity check total size
82
-    return nil unless peers_data.size % BOOTSTRAP_PEER_SIZE == 0
83
-    peers = []
84
-    until peers_data.blank?
85
-      peers << decode_bootstrap_peer(peers_data.slice!(0, BOOTSTRAP_PEER_SIZE))
86
-    end
87
-    peers
88
-  end
89
-
90
-  # Decodes a single set of peer data from a BOOTSTRAP reseponse
91
-  #
92
-  # @param peer-data [String] the peer data for one peer from a BOOSTRAP response
93
-  # @return [Array] the peer ID, IPv4 addresss, UDP port, TCP port and version of this peer
94
-  def decode_bootstrap_peer(peer_data)
95
-    # sanity check the size of this peer's data
96
-    return nil unless peer_data.size == BOOTSTRAP_PEER_SIZE
97
-    # TODO; interpret this properly
98
-    peer_id = peer_data.slice!(0, 16)
99
-    ip, udp_port, tcp_port, version = peer_data.unpack('VvvC')
100
-    [ decode_peer_id(peer_id), Rex::Socket.addr_itoa(ip), udp_port, tcp_port, version ]
101
-  end
102
-
103
-  # Decodes an on-the-wire representation of a Kademlia peer to its 16-character hex equivalent
104
-  #
105
-  # @param bytes [String] the on-the-wire representation of a Kademlia peer
106
-  # @return [String] the peer ID if valid, nil otherwise
107
-  def decode_peer_id(bytes)
108
-    peer_id = 0
109
-    return nil unless bytes.size == 16
110
-    bytes.unpack('VVVV').map { |p| peer_id <<= 32; peer_id ^= p; }
111
-    peer_id.to_s(16).upcase
112
-  end
113
-
114
-  # TODO
115
-  # def encode_peer_id(id)
116
-  # end
117 14
 end
118 15
 end

+ 5
- 0
lib/rex/proto/kademlia.rb View File

@@ -1,3 +1,8 @@
1 1
 # -*- coding: binary -*-
2 2
 
3
+require 'rex/proto/kademlia/bootstrap_request'
4
+require 'rex/proto/kademlia/bootstrap_response'
3 5
 require 'rex/proto/kademlia/message'
6
+require 'rex/proto/kademlia/ping'
7
+require 'rex/proto/kademlia/pong'
8
+require 'rex/proto/kademlia/util'

+ 19
- 0
lib/rex/proto/kademlia/bootstrap_request.rb View File

@@ -0,0 +1,19 @@
1
+# -*- coding: binary -*-
2
+
3
+require 'rex/proto/kademlia/message'
4
+
5
+module Rex
6
+module Proto
7
+module Kademlia
8
+  # Opcode for a BOOTSTRAP request
9
+  BOOTSTRAP_REQUEST = 0x01
10
+
11
+  # A Kademlia bootstrap request message
12
+  class BootstrapRequest < Message
13
+    def initialize
14
+      super(BOOTSTRAP_REQUEST)
15
+    end
16
+  end
17
+end
18
+end
19
+end

+ 70
- 0
lib/rex/proto/kademlia/bootstrap_response.rb View File

@@ -0,0 +1,70 @@
1
+# -*- coding: binary -*-
2
+
3
+require 'rex/proto/kademlia/message'
4
+require 'rex/proto/kademlia/util'
5
+
6
+module Rex
7
+module Proto
8
+module Kademlia
9
+  # Opcode for a bootstrap response
10
+  BOOTSTRAP_RESPONSE = 0x09
11
+
12
+  # A Kademlia bootstrap response message
13
+  class BootstrapResponse < Message
14
+    attr_reader :peer_id
15
+    attr_reader :tcp_port
16
+    attr_reader :version
17
+    # An array of hashes containing the peer ID, IP address, UDP and TCP ports as well as the type/version
18
+    attr_reader :peers
19
+
20
+    def initialize(peer_id, tcp_port, version, peers)
21
+      @peer_id = peer_id
22
+      @tcp_port = tcp_port
23
+      @version = version
24
+      @peers = peers
25
+    end
26
+
27
+    # The minimum size of a peer in a KADEMLIA2_BOOTSTRAP_RES message:
28
+    # peer ID (16-bytes), IP (4 bytes), UDP port (2 bytes), TCP port (2 bytes)
29
+    # and version (1 byte)
30
+    BOOTSTRAP_PEER_SIZE = 25
31
+
32
+    # Builds a bootstrap response from given data
33
+    #
34
+    # @param data [String] the data to decode
35
+    # @return [BootstrapResponse] the bootstrap response if the data is valid, nil otherwise
36
+    def self.from_data(data)
37
+      message = Message.from_data(data)
38
+      # abort if this isn't a valid response
39
+      return unless message
40
+      return unless message.type == BOOTSTRAP_RESPONSE
41
+      return unless message.body.size >= 23
42
+      bootstrap_peer_id = Rex::Proto::Kademlia.decode_peer_id(message.body.slice!(0, 16))
43
+      bootstrap_tcp_port, bootstrap_version, num_peers = message.body.slice!(0, 5).unpack('vCv')
44
+      # protocol says there are no peers and the body confirms this, so just return with no peers
45
+      if num_peers == 0 && message.body.blank?
46
+        peers = []
47
+      else
48
+        peers_data = message.body
49
+        # peers data is too long/short, abort
50
+        return if peers_data.size % BOOTSTRAP_PEER_SIZE != 0
51
+        peers = []
52
+        until peers_data.blank?
53
+          peer_data = peers_data.slice!(0, BOOTSTRAP_PEER_SIZE)
54
+          peer_id = Rex::Proto::Kademlia.decode_peer_id(peer_data.slice!(0, 16))
55
+          ip, udp_port, tcp_port, version = peer_data.unpack('VvvC')
56
+          peers << {
57
+            id: peer_id,
58
+            ip: Rex::Socket.addr_itoa(ip),
59
+            tcp_port: tcp_port,
60
+            udp_port: udp_port,
61
+            version: version
62
+          }
63
+        end
64
+      end
65
+      BootstrapResponse.new(bootstrap_peer_id, bootstrap_tcp_port, bootstrap_version, peers)
66
+    end
67
+  end
68
+end
69
+end
70
+end

+ 10
- 6
lib/rex/proto/kademlia/message.rb View File

@@ -17,12 +17,12 @@ module Proto
17 17
 #
18 18
 ##
19 19
 module Kademlia
20
-  # The header that non-compressed Kad messages use
21
-  STANDARD_PACKET = 0xE4
22
-  # The header that compressed Kad messages use, which is currently unsupported
23
-  COMPRESSED_PACKET = 0xE5
24
-
25 20
   class Message
21
+    # The header that non-compressed Kad messages use
22
+    STANDARD_PACKET = 0xE4
23
+    # The header that compressed Kad messages use, which is currently unsupported
24
+    COMPRESSED_PACKET = 0xE5
25
+
26 26
     attr_accessor :type, :body
27 27
 
28 28
     # @param type [String] the message type
@@ -36,7 +36,7 @@ module Kademlia
36 36
       return if data.length < 2
37 37
       header, type = data.unpack('CC')
38 38
       if header == COMPRESSED_PACKET
39
-        fail NotImplementedError, "Unable to handle #{message.length}-byte compressed Kademlia message"
39
+        fail NotImplementedError, "Unable to handle #{data.length}-byte compressed Kademlia message"
40 40
       end
41 41
       return if header != STANDARD_PACKET
42 42
       Message.new(type, data[2, data.length])
@@ -45,6 +45,10 @@ module Kademlia
45 45
     def to_str
46 46
       [STANDARD_PACKET, @type].pack('CC') + @body
47 47
     end
48
+
49
+    def ==(other)
50
+      type == other.type && body == other.body
51
+    end
48 52
   end
49 53
 end
50 54
 end

+ 19
- 0
lib/rex/proto/kademlia/ping.rb View File

@@ -0,0 +1,19 @@
1
+# -*- coding: binary -*-
2
+
3
+require 'rex/proto/kademlia/message'
4
+
5
+module Rex
6
+module Proto
7
+module Kademlia
8
+  # Opcode for a PING request
9
+  PING = 0x60
10
+
11
+  # A Kademlia ping message.
12
+  class Ping < Message
13
+    def initialize
14
+      super(PING)
15
+    end
16
+  end
17
+end
18
+end
19
+end

+ 34
- 0
lib/rex/proto/kademlia/pong.rb View File

@@ -0,0 +1,34 @@
1
+# -*- coding: binary -*-
2
+
3
+require 'rex/proto/kademlia/message'
4
+
5
+module Rex
6
+module Proto
7
+module Kademlia
8
+  # Opcode for a PING response
9
+  PONG = 0x61
10
+
11
+  # A Kademlia pong message.
12
+  class Pong < Message
13
+    # the source port from which the PING was received
14
+    attr_reader :port
15
+
16
+    def initialize(port = nil)
17
+      super(PONG)
18
+      @port = port
19
+    end
20
+
21
+    def self.from_data(data)
22
+      message = super(data)
23
+      return if message.type != PONG
24
+      return if message.body.size != 2
25
+      Pong.new(message.body.unpack('v')[0])
26
+    end
27
+
28
+    def to_str
29
+      super + [@port].pack('v')
30
+    end
31
+  end
32
+end
33
+end
34
+end

+ 22
- 0
lib/rex/proto/kademlia/util.rb View File

@@ -0,0 +1,22 @@
1
+# -*- coding: binary -*-
2
+
3
+module Rex
4
+module Proto
5
+module Kademlia
6
+  # Decodes an on-the-wire representation of a Kademlia peer to its 16-character hex equivalent
7
+  #
8
+  # @param bytes [String] the on-the-wire representation of a Kademlia peer
9
+  # @return [String] the peer ID if valid, nil otherwise
10
+  def self.decode_peer_id(bytes)
11
+    peer_id = 0
12
+    return nil unless bytes.size == 16
13
+    bytes.unpack('VVVV').map { |p| peer_id = ((peer_id << 32) ^ p) }
14
+    peer_id.to_s(16).upcase
15
+  end
16
+
17
+  # TODO
18
+  # def encode_peer_id(id)
19
+  # end
20
+end
21
+end
22
+end

+ 16
- 16
modules/auxiliary/scanner/kademlia/server_info.rb View File

@@ -46,9 +46,9 @@ class Metasploit3 < Msf::Auxiliary
46 46
   def build_probe
47 47
     @probe ||= case action.name
48 48
                when 'BOOTSTRAP'
49
-                 bootstrap
49
+                 BootstrapRequest.new
50 50
                when 'PING'
51
-                 ping
51
+                 Ping.new
52 52
                end
53 53
   end
54 54
 
@@ -58,22 +58,22 @@ class Metasploit3 < Msf::Auxiliary
58 58
 
59 59
     case action.name
60 60
     when 'BOOTSTRAP'
61
-      peer_id, tcp_port, version, peers = decode_bootstrap_res(response)
62
-      info = {
63
-        peer_id: peer_id,
64
-        tcp_port: tcp_port,
65
-        version: version,
66
-        peers: peers
67
-      }
68
-      if datastore['VERBOSE']
69
-      else
70
-        print_good("#{peer} ID #{peer_id}, TCP port #{tcp_port}, version #{version}, #{peers.size} peers")
61
+      if bootstrap_res = BootstrapResponse.from_data(response)
62
+        info = {
63
+          peer_id: bootstrap_res.peer_id,
64
+          tcp_port: bootstrap_res.tcp_port,
65
+          version: bootstrap_res.version,
66
+          peers: bootstrap_res.peers
67
+        }
68
+        print_good("#{peer} ID #{bootstrap_res.peer_id}, TCP port #{bootstrap_res.tcp_port}," +
69
+                   " version #{bootstrap_res.version}, #{bootstrap_res.peers.size} peers")
71 70
       end
72 71
     when 'PING'
73
-      udp_port = decode_pong(response)
74
-      print_good("#{peer} PONG")
75
-      # udp_port should match the port we contacted it from.  TODO: validate this?
76
-      info = { udp_port: udp_port }
72
+      if pong = Pong.from_data(response)
73
+        print_good("#{peer} PONG port #{pong.port}")
74
+        # port should match the port we contacted it from.  TODO: validate this?
75
+        info = { udp_port: pong.port }
76
+      end
77 77
     end
78 78
 
79 79
     return unless info

+ 1
- 105
spec/lib/msf/core/auxiliary/kademlia_spec.rb View File

@@ -1,4 +1,5 @@
1 1
 # -*- coding: binary -*-
2
+#
2 3
 require 'spec_helper'
3 4
 require 'msf/core/auxiliary/kademlia'
4 5
 
@@ -8,109 +9,4 @@ describe Msf::Auxiliary::Kademlia do
8 9
     mod.extend described_class
9 10
     mod
10 11
   end
11
-
12
-  describe '#decode_pong' do
13
-    it 'does not decode overly small pongs' do
14
-      expect(kad.decode_pong("\xE4\x61\x01")).to eq(nil)
15
-    end
16
-
17
-    it 'does not decode overly large pongs' do
18
-      expect(kad.decode_pong("\xE4\x61\x01\x02\x03")).to eq(nil)
19
-    end
20
-
21
-    it 'properly decodes valid pongs' do
22
-      expect(kad.decode_pong("\xE4\x61\x9E\x86")).to eq(34462)
23
-    end
24
-  end
25
-
26
-  describe '#decode_bootstrap_peer' do
27
-    it 'does not decode overly small peer responses' do
28
-      expect(kad.decode_bootstrap_peer("this is too small")).to eq(nil)
29
-    end
30
-
31
-    it 'does not decode overly large peer responses' do
32
-      expect(kad.decode_bootstrap_peer("this is much, much, much too large")).to eq(nil)
33
-    end
34
-
35
-    it 'properly extracts peer info' do
36
-      data =
37
-          "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" + # peer ID
38
-          "\x04\x28\xA8\xC0" + # 192.168.40.4
39
-          "\x31\xd4" + # UDP port 54321
40
-          "\x39\x30" + # TCP port 12345
41
-          "\x08" # peer type
42
-      peer_id, ip, udp_port, tcp_port, type = kad.decode_bootstrap_peer(data)
43
-      expect(peer_id).to eq('3020100070605040B0A09080F0E0D0C')
44
-      expect(ip).to eq('192.168.40.4')
45
-      expect(udp_port).to eq(54321)
46
-      expect(tcp_port).to eq(12345)
47
-      expect(type).to eq(8)
48
-    end
49
-  end
50
-
51
-  describe '#decode_bootstrap_peers' do
52
-    it 'does not decode overly small bootstrap responses' do
53
-      expect(kad.decode_bootstrap_peer("this is too small")).to eq(nil)
54
-    end
55
-
56
-    it 'does not decode overly large bootstrap responses' do
57
-      expect(kad.decode_bootstrap_peer("this is large enough but truncated")).to eq(nil)
58
-    end
59
-
60
-    it 'properly extracts peers info' do
61
-      data =
62
-          "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" + # peer ID
63
-          "\x04\x28\xA8\xC0" + # 192.168.40.4
64
-          "\x31\xd4" + # UDP port 54321
65
-          "\x39\x30" + # TCP port 12345
66
-          "\x08" + # peer type
67
-          "\x01\x01\x02\x02\x03\x03\x04\x04\x05\x05\x06\x06\x07\x07\x08\x08" + # peer ID
68
-          "\x05\x28\xA8\xC0" + # 192.168.40.5
69
-          "\x5c\x11" + # UDP port 4444
70
-          "\xb3\x15" + # TCP port 5555
71
-          "\x09" # peer type
72
-      peers = kad.decode_bootstrap_peers(data)
73
-      expect(peers.size).to eq(2)
74
-      peer1_id, peer1_ip, peer1_udp, peer1_tcp, peer1_type = peers.first
75
-      expect(peer1_id).to eq('3020100070605040B0A09080F0E0D0C')
76
-      expect(peer1_ip).to eq('192.168.40.4')
77
-      expect(peer1_udp).to eq(54321)
78
-      expect(peer1_tcp).to eq(12345)
79
-      expect(peer1_type).to eq(8)
80
-      peer2_id, peer2_ip, peer2_udp, peer2_tcp, peer2_type = peers.last
81
-      expect(peer2_id).to eq('2020101040403030606050508080707')
82
-      expect(peer2_ip).to eq('192.168.40.5')
83
-      expect(peer2_udp).to eq(4444)
84
-      expect(peer2_tcp).to eq(5555)
85
-      expect(peer2_type).to eq(9)
86
-    end
87
-  end
88
-
89
-  describe '#decode_bootstrap_res' do
90
-    it 'properly decodes valid bootstrap responses' do
91
-      data = IO.read(File.join(File.dirname(__FILE__), 'kademlia_bootstrap_res.bin'))
92
-      peer_id, tcp, version, peers = kad.decode_bootstrap_res(data)
93
-      expect(peer_id).to eq('B54A83462529B21EF51FD54B956B07B0')
94
-      expect(tcp).to eq(4662)
95
-      expect(version).to eq(8)
96
-      # don't bother checking every peer
97
-      expect(peers.size).to eq(20)
98
-    end
99
-  end
100
-
101
-  describe '#decode_peer_id' do
102
-    it 'decodes a peer ID properly' do
103
-      bytes = "\x00\x60\x89\x9B\x0A\x0B\xBE\xAE\x45\x35\xCB\x0E\x07\xA1\x77\x71"
104
-      peer_id = "9B896000AEBE0B0A0ECB35457177A107"
105
-      expect(kad.decode_peer_id(bytes)).to eq(peer_id)
106
-    end
107
-  end
108
-
109
-  describe '#encode_peer' do
110
-    skip 'encodes a peer ID properly' do
111
-      bytes = "\x00\x60\x89\x9B\x0A\x0B\xBE\xAE\x45\x35\xCB\x0E\x07\xA1\x77\x71"
112
-      peer_id = "9B896000AEBE0B0A0ECB35457177A107"
113
-      expect(kad.encode_peer_id(peer_id)).to eq(bytes)
114
-    end
115
-  end
116 12
 end

+ 23
- 0
spec/lib/rex/proto/kademlia/bootstrap_request_spec.rb View File

@@ -0,0 +1,23 @@
1
+# -*- coding: binary -*-
2
+
3
+require 'spec_helper'
4
+require 'rex/proto/kademlia/bootstrap_request'
5
+
6
+describe Rex::Proto::Kademlia::BootstrapRequest do
7
+  subject(:bootstrap) do
8
+    described_class.new
9
+  end
10
+
11
+  describe '#initialize' do
12
+    it 'constructs properly' do
13
+      expect(bootstrap.type).to eq(Rex::Proto::Kademlia::BOOTSTRAP_REQUEST)
14
+      expect(bootstrap.body).to eq('')
15
+    end
16
+  end
17
+
18
+  describe '#to_str' do
19
+    it 'packs properly' do
20
+      expect(bootstrap.to_str).to eq("\xE4\x01")
21
+    end
22
+  end
23
+end

+ 38
- 0
spec/lib/rex/proto/kademlia/bootstrap_response_spec.rb View File

@@ -0,0 +1,38 @@
1
+# -*- coding: binary -*-
2
+
3
+require 'spec_helper'
4
+require 'rex/proto/kademlia/bootstrap_response'
5
+
6
+describe Rex::Proto::Kademlia::BootstrapResponse do
7
+  describe '#from_data' do
8
+    it 'properly decodes real valid bootstrap responses' do
9
+      data = IO.read(File.join(File.dirname(__FILE__), 'kademlia_bootstrap_res.bin'))
10
+      response = described_class.from_data(data)
11
+      expect(response.peer_id).to eq('B54A83462529B21EF51FD54B956B07B0')
12
+      expect(response.tcp_port).to eq(4662)
13
+      expect(response.version).to eq(8)
14
+      # don't bother checking every peer
15
+      expect(response.peers.size).to eq(20)
16
+      peer = response.peers.first
17
+      expect(peer[:id]).to eq('B0A5518388D66BC211B0B9F75B3DCB10')
18
+      expect(peer[:ip]).to eq('149.91.116.59')
19
+      expect(peer[:tcp_port]).to eq(4882)
20
+      expect(peer[:udp_port]).to eq(4992)
21
+      expect(peer[:type]).to eq(8)
22
+      peer = response.peers.last
23
+      expect(peer[:id]).to eq('9B896000AEBE0B0A0ECB35457177A107')
24
+      expect(peer[:ip]).to eq('83.46.192.208')
25
+      expect(peer[:tcp_port]).to eq(3662)
26
+      expect(peer[:udp_port]).to eq(3672)
27
+      expect(peer[:type]).to eq(8)
28
+    end
29
+
30
+    it 'does not decode overly small bootstrap responses' do
31
+      expect(described_class.from_data('this is too small')).to eq(nil)
32
+    end
33
+
34
+    it 'does not decode malformed bootstrap responses' do
35
+      expect(described_class.from_data('this is large enough but truncated')).to eq(nil)
36
+    end
37
+  end
38
+end

spec/lib/msf/core/auxiliary/kademlia_bootstrap_res.bin → spec/lib/rex/proto/kademlia/kademlia_bootstrap_res.bin View File


+ 70
- 31
spec/lib/rex/proto/kademlia/message_spec.rb View File

@@ -2,49 +2,88 @@
2 2
 require 'spec_helper'
3 3
 require 'rex/proto/kademlia/message'
4 4
 
5
-describe Rex::Proto::Kademlia do
6
-  subject(:kad) do
7
-    mod = Module.new
8
-    mod.extend described_class
9
-    mod
10
-  end
5
+describe Rex::Proto::Kademlia::Message do
6
+
7
+  context 'with a body' do
8
+    let(:type) { 1 }
9
+    let(:body) { 'test' }
10
+    let(:data) { "\xE4\x01test" }
11
+
12
+    subject(:message) do
13
+      described_class.new(type, body)
14
+    end
11 15
 
12
-  describe '#encode_message' do
13
-    let(:no_body) { "\xE4\x01" }
14
-    let(:body) { "\xE4\x01p2p" }
15
-    it 'properly encodes messages without a body' do
16
-      expect(kad.encode_message(1)).to eq("\xE4\x01")
16
+    describe '#initialize' do
17
+      it 'constructs properly' do
18
+        expect(message.type).to eq(type)
19
+        expect(message.body).to eq(body)
20
+      end
17 21
     end
18
-    it 'properly encodes messages with a body' do
19
-      expect(kad.encode_message(1, 'p2p')).to eq("\xE4\x01p2p")
22
+
23
+    describe '#to_str' do
24
+      it 'packs properly' do
25
+        expect(message.to_str).to eq(data)
26
+      end
20 27
     end
21
-  end
22 28
 
23
-  describe '#decode_message' do
24
-    it 'does not decode overly short messages' do
25
-      expect(kad.decode_message('f')).to eq(nil)
29
+    describe '#from_data' do
30
+      it 'unpacks supported messages properly' do
31
+        unpacked = described_class.from_data(data)
32
+        expect(unpacked.type).to eq(type)
33
+        expect(unpacked.body).to eq(body)
34
+      end
35
+
36
+      it 'raises on compressed messages' do
37
+        expect do
38
+          described_class.from_data("\xE5\x01test")
39
+        end.to raise_error(NotImplementedError)
40
+      end
41
+    end
42
+
43
+    describe '#==' do
44
+      it 'respects equality' do
45
+        expect(described_class.new(1, 'test')).to eq(described_class.new(1, 'test'))
46
+        expect(described_class.new(1, 'test')).not_to eq(described_class.new(1, 'not'))
47
+        expect(described_class.new(1, 'test')).not_to eq(described_class.new(2, 'test'))
48
+        expect(described_class.new(1, 'test')).not_to eq(described_class.new(2, 'not'))
49
+      end
26 50
     end
51
+  end
52
+
53
+  context 'without a body' do
54
+    let(:type) { 2 }
55
+    let(:body) { '' }
56
+    let(:data) { "\xE4\x02" }
27 57
 
28
-    it 'does not decode unknown messages' do
29
-      expect(kad.decode_message("this is not kademlia")).to eq(nil)
58
+    subject(:message) do
59
+      described_class.new(type, body)
30 60
     end
31 61
 
32
-    it 'raises on compressed messages' do
33
-      expect do
34
-        kad.decode_message("\xE5\x01blahblah")
35
-      end.to raise_error(NotImplementedError)
62
+    describe '#initialize' do
63
+      it 'constructs properly' do
64
+        expect(message.type).to eq(type)
65
+        expect(message.body).to eq(body)
66
+      end
36 67
     end
37 68
 
38
-    it 'properly decodes valid messages without a body' do
39
-      type, payload = kad.decode_message("\xE4\xFF")
40
-      expect(type).to eq(0xFF)
41
-      expect(payload).to eq('')
69
+    describe '#to_str' do
70
+      it 'packs properly' do
71
+        expect(message.to_str).to eq(data)
72
+      end
42 73
     end
43 74
 
44
-    it 'properly decodes valid messages wth a body' do
45
-      type, payload = kad.decode_message("\xE4\xFFtesttesttest")
46
-      expect(type).to eq(0xFF)
47
-      expect(payload).to eq('testtesttest')
75
+    describe '#from_data' do
76
+      it 'unpacks supported messages properly' do
77
+        unpacked = described_class.from_data(data)
78
+        expect(unpacked.type).to eq(type)
79
+        expect(unpacked.body).to eq(body)
80
+      end
81
+
82
+      it 'raises on compressed messages' do
83
+        expect do
84
+          described_class.from_data("\xE5\x01")
85
+        end.to raise_error(NotImplementedError)
86
+      end
48 87
     end
49 88
   end
50 89
 end

+ 23
- 0
spec/lib/rex/proto/kademlia/ping_spec.rb View File

@@ -0,0 +1,23 @@
1
+# -*- coding: binary -*-
2
+
3
+require 'spec_helper'
4
+require 'rex/proto/kademlia/ping'
5
+
6
+describe Rex::Proto::Kademlia::Ping do
7
+  subject(:ping) do
8
+    described_class.new
9
+  end
10
+
11
+  describe '#initialize' do
12
+    it 'constructs properly' do
13
+      expect(ping.type).to eq(Rex::Proto::Kademlia::PING)
14
+      expect(ping.body).to eq('')
15
+    end
16
+  end
17
+
18
+  describe '#to_str' do
19
+    it 'packs properly' do
20
+      expect(ping.to_str).to eq("\xE4\x60")
21
+    end
22
+  end
23
+end

+ 40
- 0
spec/lib/rex/proto/kademlia/pong_spec.rb View File

@@ -0,0 +1,40 @@
1
+# -*- coding: binary -*-
2
+
3
+require 'spec_helper'
4
+require 'rex/proto/kademlia/pong'
5
+
6
+describe Rex::Proto::Kademlia::Pong do
7
+  let(:port) { 12345 }
8
+  subject(:pong) do
9
+    described_class.new(port)
10
+  end
11
+
12
+  describe '#initialize' do
13
+    it 'constructs properly' do
14
+      expect(pong.type).to eq(Rex::Proto::Kademlia::PONG)
15
+      expect(pong.port).to eq(port)
16
+    end
17
+  end
18
+
19
+  describe '#to_str' do
20
+    it 'packs properly' do
21
+      expect(pong.to_str).to eq("\xE4\x61\x39\x30")
22
+    end
23
+  end
24
+
25
+  describe '#from_data' do
26
+    it 'unpacks supported valid pongs properly' do
27
+      unpacked = described_class.from_data("\xE4\x61\x9E\x86")
28
+      expect(unpacked.type).to eq(Rex::Proto::Kademlia::PONG)
29
+      expect(unpacked.port).to eq(34462)
30
+    end
31
+
32
+    it 'does not decode overly small pongs' do
33
+      expect(described_class.from_data("\xE4\x61\x01")).to eq(nil)
34
+    end
35
+
36
+    it 'does not decode overly large pongs' do
37
+      expect(described_class.from_data("\xE4\x61\x01\x02\x03")).to eq(nil)
38
+    end
39
+  end
40
+end

+ 23
- 0
spec/lib/rex/proto/kademlia/util_spec.rb View File

@@ -0,0 +1,23 @@
1
+# -*- coding: binary -*-
2
+#
3
+require 'spec_helper'
4
+require 'rex/proto/kademlia/util'
5
+
6
+describe Rex::Proto::Kademlia do
7
+
8
+  describe '#decode_peer_id' do
9
+    subject(:kad) { described_class.decode_peer_id(bytes) }
10
+    let(:bytes) { "\x00\x60\x89\x9B\x0A\x0B\xBE\xAE\x45\x35\xCB\x0E\x07\xA1\x77\x71" }
11
+    it 'decodes a peer ID properly' do
12
+      is_expected.to eq('9B896000AEBE0B0A0ECB35457177A107')
13
+    end
14
+  end
15
+
16
+  describe '#encode_peer' do
17
+    skip 'encodes a peer ID properly' do
18
+      bytes = "\x00\x60\x89\x9B\x0A\x0B\xBE\xAE\x45\x35\xCB\x0E\x07\xA1\x77\x71"
19
+      peer_id = "9B896000AEBE0B0A0ECB35457177A107"
20
+      expect(kad.encode_peer_id(peer_id)).to eq(bytes)
21
+    end
22
+  end
23
+end

Loading…
Cancel
Save