Skip to content

Commit 94e5ba1

Browse files
committed
YARD and spec cleanup
1 parent b6b288a commit 94e5ba1

File tree

3 files changed

+114
-63
lines changed

3 files changed

+114
-63
lines changed

lib/rex/proto/kademlia/message.rb

Lines changed: 65 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -17,70 +17,100 @@ module Proto
1717
#
1818
##
1919
module Kademlia
20+
# The header that non-compressed Kad messages use
2021
STANDARD_PACKET = 0xE4
21-
# TODO: support this format
22+
# The header that compressed Kad messages use, which is currently unsupported
2223
COMPRESSED_PACKET = 0xE5
23-
24+
# Opcode for a BOOTSTRAP request
2425
BOOTSTRAP_REQ = 0x01
26+
# Opcode for a BOOTSTRAP response
2527
BOOTSTRAP_RES = 0x09
28+
# Opcode for a PING request
2629
PING = 0x60
30+
# Opcode for a PING response
2731
PONG = 0x61
28-
2932
# The minimum size of a peer in a KADEMLIA2_BOOTSTRAP_RES message:
3033
# peer ID (16-bytes), IP (4 bytes), UDP port (2 bytes), TCP port (2 bytes)
3134
# and version (1 byte)
3235
BOOTSTRAP_PEER_SIZE = 25
3336

37+
# Decodes a Kademlia message.
38+
#
39+
# @param message [String] the message to decode
40+
# @return [Array] the message type and body if valid, nil otherwise
3441
def decode_message(message)
35-
# minimum size is header (1) + opcode (1) + stuff (0+)
42+
# minimum size is header (1) + type (1) + stuff (0+)
3643
return if message.length < 2
37-
header, opcode = message.unpack('CC')
44+
header, type = message.unpack('CC')
3845
if header == COMPRESSED_PACKET
39-
fail NotImplementedError, "Unable to handle compressed #{message.length}-byte compressed Kademlia message"
46+
fail NotImplementedError, "Unable to handle #{message.length}-byte compressed Kademlia message"
4047
end
4148
return if header != STANDARD_PACKET
42-
[opcode, message[2, message.length]]
49+
[type, message[2, message.length]]
4350
end
4451

45-
def encode_message(type, payload = '')
46-
[STANDARD_PACKET, type].pack('CC') + payload
52+
# Encodes a Kademlia message.
53+
#
54+
# @param type [String] the message type
55+
# @param body [String] the message body
56+
# @return [String] the encoded Kademlia message
57+
def encode_message(type, body = '')
58+
[STANDARD_PACKET, type].pack('CC') + body
4759
end
4860

61+
# Builds a BOOTSTRAP request
62+
#
63+
# @return [String] a BOOTSTRAP request
4964
def bootstrap
5065
encode_message(BOOTSTRAP_REQ)
5166
end
5267

53-
def decode_bootstrap_res(message)
54-
opcode, payload = decode_message(message)
68+
# Decodes a BOOTSTRAP response
69+
#
70+
# @param response [String] the response to decode
71+
# @return [Array] the discovered peer ID, TCP port, version and a list of peers
72+
# if the response if valid, nil otherwise
73+
def decode_bootstrap_res(response)
74+
type, body = decode_message(response)
5575
# abort if this isn't a valid response
56-
return nil unless opcode = BOOTSTRAP_RES
57-
return nil unless payload.size >= 23
58-
peer_id = decode_peer_id(payload.slice!(0,16))
59-
tcp_port, version, num_peers = payload.slice!(0,5).unpack('vCv')
60-
# protocol says there are no peers and the payload confirms this, so just return with no peers
61-
return [ tcp_port, version, []] if num_peers == 0 && payload.blank?
62-
peers = decode_bootstrap_peers(payload)
76+
return nil unless type = BOOTSTRAP_RES
77+
return nil unless body.size >= 23
78+
peer_id = decode_peer_id(body.slice!(0,16))
79+
tcp_port, version, num_peers = body.slice!(0,5).unpack('vCv')
80+
# protocol says there are no peers and the body confirms this, so just return with no peers
81+
return [ tcp_port, version, []] if num_peers == 0 && body.blank?
82+
peers = decode_bootstrap_peers(body)
6383
# abort if the peer data was invalid
6484
return nil unless peers
6585
[ peer_id, tcp_port, version, peers ]
6686
end
6787

68-
# Returns a PING message
88+
# Builds a PING request
89+
#
90+
# @return [String] a PING request
6991
def ping
7092
encode_message(PING)
7193
end
7294

73-
# Decodes a PONG message, returning the port used by the peer
74-
def decode_pong(message)
75-
opcode, port = decode_message(message)
95+
# Decode a PING response, PONG
96+
#
97+
# @param response [String] the response to decode
98+
# @return [Integer] the source port from the PING response if the response is valid, nil otherwise
99+
def decode_pong(response)
100+
type, port = decode_message(response)
76101
# abort if this isn't a pong
77-
return nil unless opcode == PONG
102+
return nil unless type == PONG
78103
# abort if the response is too large/small
79104
return nil unless port && port.size == 2
80105
# this should always be equivalent to the source port from which the PING was received
81106
port.unpack('v')[0]
82107
end
83108

109+
# Decode a list of peers from a BOOTSTRAP response
110+
#
111+
# @param peers_data [String] the peers data from a BOOTSTRAP response
112+
# @return [Array] a list of the peers and their associated metadata extracted
113+
# from the response if valid, nil otherwise
84114
def decode_bootstrap_peers(peers_data)
85115
# sanity check total size
86116
return nil unless peers_data.size % BOOTSTRAP_PEER_SIZE == 0
@@ -91,6 +121,10 @@ def decode_bootstrap_peers(peers_data)
91121
peers
92122
end
93123

124+
# Decodes a single set of peer data from a BOOTSTRAP reseponse
125+
#
126+
# @param peer-data [String] the peer data for one peer from a BOOSTRAP response
127+
# @return [Array] the peer ID, IPv4 addresss, UDP port, TCP port and version of this peer
94128
def decode_bootstrap_peer(peer_data)
95129
# sanity check the size of this peer's data
96130
return nil unless peer_data.size == BOOTSTRAP_PEER_SIZE
@@ -100,17 +134,20 @@ def decode_bootstrap_peer(peer_data)
100134
[ decode_peer_id(peer_id), Rex::Socket.addr_itoa(ip), udp_port, tcp_port, version ]
101135
end
102136

103-
137+
# Decodes an on-the-wire representation of a Kademlia peer to its 16-character hex equivalent
138+
#
139+
# @param bytes [String] the on-the-wire representation of a Kademlia peer
140+
# @return [String] the peer ID if valid, nil otherwise
104141
def decode_peer_id(bytes)
105142
peer_id = 0
106143
return nil unless bytes.size == 16
107144
bytes.unpack('VVVV').map { |p| peer_id <<= 32; peer_id ^= p; }
108145
peer_id.to_s(16).upcase
109146
end
110147

111-
# TODO?
112-
def encode_peer_id(id)
113-
end
148+
# TODO
149+
# def encode_peer_id(id)
150+
# end
151+
end
114152
end
115153
end
116-
end

modules/auxiliary/scanner/kademlia/server_info.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def initialize(info = {})
2222
typically belonging to eMule/eDonkey/BitTorrent servers or other P2P
2323
applications.
2424
),
25-
'Author' => 'Jon Hart <jon_hart[at]rapid7.com',
25+
'Author' => 'Jon Hart <jon_hart[at]rapid7.com>',
2626
'References' =>
2727
[
2828
# There are lots of academic papers on the protocol but they tend to lack usable

spec/lib/rex/proto/kademlia/message_spec.rb

Lines changed: 48 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,69 +3,80 @@
33
require 'rex/proto/kademlia/message'
44

55
describe Rex::Proto::Kademlia do
6-
subject do
6+
subject(:kad) do
77
mod = Module.new
88
mod.extend described_class
99
mod
1010
end
1111

1212
describe '#encode_message' do
13-
it 'should properly encode messages' do
14-
expect(subject.encode_message(1)).to eq("\xE4\x01")
15-
expect(subject.encode_message(1, 'p2p')).to eq("\xE4\x01p2p")
13+
it 'properly encodes messages without a body' do
14+
expect(kad.encode_message(1)).to eq("\xE4\x01")
15+
end
16+
it 'properly encodes messages with a body' do
17+
expect(kad.encode_message(1, 'p2p')).to eq("\xE4\x01p2p")
1618
end
1719
end
1820

1921
describe '#decode_message' do
20-
it 'should not decode overly short messages' do
21-
expect(subject.decode_message('f')).to eq(nil)
22+
it 'does not decode overly short messages' do
23+
expect(kad.decode_message('f')).to eq(nil)
2224
end
2325

24-
it 'should not decode unknown messages' do
25-
expect(subject.decode_message("this is not kademlia")).to eq(nil)
26+
it 'does not decode unknown messages' do
27+
expect(kad.decode_message("this is not kademlia")).to eq(nil)
2628
end
2729

28-
it 'should raise on compressed messages' do
30+
it 'raises on compressed messages' do
2931
expect do
30-
subject.decode_message("\xE5\x01blahblah")
32+
kad.decode_message("\xE5\x01blahblah")
3133
end.to raise_error(NotImplementedError)
3234
end
3335

34-
it 'should properly decode valid messages' do
35-
type, payload = subject.decode_message("\xE4\xFF")
36+
it 'properly decodes valid messages without a body' do
37+
type, payload = kad.decode_message("\xE4\xFF")
3638
expect(type).to eq(0xFF)
3739
expect(payload).to eq('')
40+
end
3841

39-
_, payload = subject.decode_message("\xE4\xFFtesttesttest")
42+
it 'properly decodes valid messages wth a body' do
43+
type, payload = kad.decode_message("\xE4\xFFtesttesttest")
44+
expect(type).to eq(0xFF)
4045
expect(payload).to eq('testtesttest')
4146
end
4247
end
4348

4449
describe '#decode_pong' do
45-
it 'should not decode overly large/small pongs' do
46-
expect(subject.decode_pong("\xE4\x61\x01")).to eq(nil)
47-
expect(subject.decode_pong("\xE4\x61\x01\x02\x03")).to eq(nil)
50+
it 'does not decode overly small pongs' do
51+
expect(kad.decode_pong("\xE4\x61\x01")).to eq(nil)
52+
end
53+
54+
it 'does not decode overly large pongs' do
55+
expect(kad.decode_pong("\xE4\x61\x01\x02\x03")).to eq(nil)
4856
end
4957

50-
it 'should properly decode valid pongs' do
51-
expect(subject.decode_pong("\xE4\x61\x9E\x86")).to eq(34462)
58+
it 'properly decodes valid pongs' do
59+
expect(kad.decode_pong("\xE4\x61\x9E\x86")).to eq(34462)
5260
end
5361
end
5462

5563
describe '#decode_bootstrap_peer' do
56-
it 'should not decode overly large/small peer' do
57-
expect(subject.decode_bootstrap_peer("this is too small")).to eq(nil)
58-
expect(subject.decode_bootstrap_peer("this is much, much, much too large")).to eq(nil)
64+
it 'does not decode overly small peer responses' do
65+
expect(kad.decode_bootstrap_peer("this is too small")).to eq(nil)
5966
end
6067

61-
it 'should properly extract peer info' do
68+
it 'does not decode overly large peer responses' do
69+
expect(kad.decode_bootstrap_peer("this is much, much, much too large")).to eq(nil)
70+
end
71+
72+
it 'properly extracts peer info' do
6273
data =
6374
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" + # peer ID
6475
"\x04\x28\xA8\xC0" + # 192.168.40.4
6576
"\x31\xd4" + # UDP port 54321
6677
"\x39\x30" + # TCP port 12345
6778
"\x08" # peer type
68-
peer_id, ip, udp_port, tcp_port, type = subject.decode_bootstrap_peer(data)
79+
peer_id, ip, udp_port, tcp_port, type = kad.decode_bootstrap_peer(data)
6980
expect(peer_id).to eq('3020100070605040B0A09080F0E0D0C')
7081
expect(ip).to eq('192.168.40.4')
7182
expect(udp_port).to eq(54321)
@@ -75,12 +86,15 @@
7586
end
7687

7788
describe '#decode_bootstrap_peers' do
78-
it 'should not decode overly small peers' do
79-
expect(subject.decode_bootstrap_peer("this is too small")).to eq(nil)
80-
expect(subject.decode_bootstrap_peer("this is large enough but truncated")).to eq(nil)
89+
it 'does not decode overly small bootstrap responses' do
90+
expect(kad.decode_bootstrap_peer("this is too small")).to eq(nil)
91+
end
92+
93+
it 'does not decode overly large bootstrap responses' do
94+
expect(kad.decode_bootstrap_peer("this is large enough but truncated")).to eq(nil)
8195
end
8296

83-
it 'should properly extract peers info' do
97+
it 'properly extracts peers info' do
8498
data =
8599
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" + # peer ID
86100
"\x04\x28\xA8\xC0" + # 192.168.40.4
@@ -92,7 +106,7 @@
92106
"\x5c\x11" + # UDP port 4444
93107
"\xb3\x15" + # TCP port 5555
94108
"\x09" # peer type
95-
peers = subject.decode_bootstrap_peers(data)
109+
peers = kad.decode_bootstrap_peers(data)
96110
expect(peers.size).to eq(2)
97111
peer1_id, peer1_ip, peer1_udp, peer1_tcp, peer1_type = peers.first
98112
expect(peer1_id).to eq('3020100070605040B0A09080F0E0D0C')
@@ -110,9 +124,9 @@
110124
end
111125

112126
describe '#decode_bootstrap_res' do
113-
it 'should properly decode valid bootstrap responses' do
127+
it 'properly decodes valid bootstrap responses' do
114128
data = IO.read(File.join(File.dirname(__FILE__), 'kademlia_bootstrap_res.bin'))
115-
peer_id, tcp, version, peers = subject.decode_bootstrap_res(data)
129+
peer_id, tcp, version, peers = kad.decode_bootstrap_res(data)
116130
expect(peer_id).to eq('B54A83462529B21EF51FD54B956B07B0')
117131
expect(tcp).to eq(4662)
118132
expect(version).to eq(8)
@@ -122,18 +136,18 @@
122136
end
123137

124138
describe '#decode_peer_id' do
125-
it 'should decode a peer ID properly' do
139+
it 'decodes a peer ID properly' do
126140
bytes = "\x00\x60\x89\x9B\x0A\x0B\xBE\xAE\x45\x35\xCB\x0E\x07\xA1\x77\x71"
127141
peer_id = "9B896000AEBE0B0A0ECB35457177A107"
128-
expect(subject.decode_peer_id(bytes)).to eq(peer_id)
142+
expect(kad.decode_peer_id(bytes)).to eq(peer_id)
129143
end
130144
end
131145

132146
describe '#encode_peer' do
133-
skip 'should encode a peer ID properly' do
147+
skip 'encodes a peer ID properly' do
134148
bytes = "\x00\x60\x89\x9B\x0A\x0B\xBE\xAE\x45\x35\xCB\x0E\x07\xA1\x77\x71"
135149
peer_id = "9B896000AEBE0B0A0ECB35457177A107"
136-
expect(subject.encode_peer_id(peer_id)).to eq(bytes)
150+
expect(kad.encode_peer_id(peer_id)).to eq(bytes)
137151
end
138152
end
139153
end

0 commit comments

Comments
 (0)