ruby_smb 0.0.21 → 0.0.22

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/examples/net_share_enum_all.rb +5 -2
  5. data/lib/ruby_smb.rb +1 -1
  6. data/lib/ruby_smb/client.rb +4 -35
  7. data/lib/ruby_smb/dcerpc.rb +7 -22
  8. data/lib/ruby_smb/dcerpc/bind.rb +30 -36
  9. data/lib/ruby_smb/dcerpc/bind_ack.rb +72 -0
  10. data/lib/ruby_smb/dcerpc/error.rb +15 -0
  11. data/lib/ruby_smb/dcerpc/ndr.rb +31 -30
  12. data/lib/ruby_smb/dcerpc/p_syntax_id_t.rb +11 -0
  13. data/lib/ruby_smb/dcerpc/pdu_header.rb +29 -0
  14. data/lib/ruby_smb/dcerpc/ptypes.rb +26 -0
  15. data/lib/ruby_smb/dcerpc/request.rb +17 -30
  16. data/lib/ruby_smb/dcerpc/response.rb +15 -34
  17. data/lib/ruby_smb/dcerpc/srvsvc.rb +5 -7
  18. data/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all.rb +8 -4
  19. data/lib/ruby_smb/dcerpc/uuid.rb +31 -13
  20. data/lib/ruby_smb/smb1/bit_field.rb +0 -1
  21. data/lib/ruby_smb/smb1/bit_field/trans_flags.rb +3 -2
  22. data/lib/ruby_smb/smb1/data_block.rb +5 -0
  23. data/lib/ruby_smb/smb1/dcerpc.rb +67 -0
  24. data/lib/ruby_smb/smb1/packet.rb +1 -0
  25. data/lib/ruby_smb/smb1/packet/trans.rb +7 -1
  26. data/lib/ruby_smb/smb1/packet/trans/data_block.rb +19 -7
  27. data/lib/ruby_smb/smb1/packet/trans/request.rb +36 -25
  28. data/lib/ruby_smb/smb1/packet/trans/response.rb +22 -21
  29. data/lib/ruby_smb/smb1/packet/trans/subcommands.rb +1 -0
  30. data/lib/ruby_smb/smb1/packet/trans/transact_nmpipe_request.rb +61 -0
  31. data/lib/ruby_smb/smb1/packet/trans/transact_nmpipe_response.rb +44 -0
  32. data/lib/ruby_smb/smb1/packet/trans2/request.rb +1 -1
  33. data/lib/ruby_smb/smb1/pipe.rb +3 -0
  34. data/lib/ruby_smb/smb2/dcerpc.rb +68 -0
  35. data/lib/ruby_smb/smb2/pipe.rb +3 -0
  36. data/lib/ruby_smb/version.rb +1 -1
  37. data/spec/lib/ruby_smb/client_spec.rb +53 -6
  38. data/spec/lib/ruby_smb/dcerpc/bind_ack_spec.rb +224 -0
  39. data/spec/lib/ruby_smb/dcerpc/bind_spec.rb +255 -7
  40. data/spec/lib/ruby_smb/dcerpc/p_syntax_id_t_spec.rb +31 -0
  41. data/spec/lib/ruby_smb/dcerpc/pdu_header_spec.rb +84 -0
  42. data/spec/lib/ruby_smb/dcerpc/request_spec.rb +106 -13
  43. data/spec/lib/ruby_smb/dcerpc/response_spec.rb +89 -8
  44. data/spec/lib/ruby_smb/dcerpc/srvsvc/net_share_enum_all_spec.rb +176 -0
  45. data/spec/lib/ruby_smb/dcerpc/uuid_spec.rb +97 -1
  46. data/spec/lib/ruby_smb/smb1/data_block_spec.rb +43 -3
  47. data/spec/lib/ruby_smb/smb1/packet/trans/data_block_spec.rb +137 -0
  48. data/spec/lib/ruby_smb/smb1/packet/trans/request_spec.rb +239 -13
  49. data/spec/lib/ruby_smb/smb1/packet/trans/response_spec.rb +122 -13
  50. data/spec/lib/ruby_smb/smb1/packet/trans/transact_nmpipe_request_spec.rb +254 -0
  51. data/spec/lib/ruby_smb/smb1/packet/trans/transact_nmpipe_response_spec.rb +122 -0
  52. data/spec/lib/ruby_smb/smb1/packet/trans2/request_spec.rb +2 -2
  53. data/spec/lib/ruby_smb/smb1/pipe_spec.rb +199 -1
  54. data/spec/lib/ruby_smb/smb2/file_spec.rb +2 -1
  55. data/spec/lib/ruby_smb/smb2/pipe_spec.rb +196 -1
  56. metadata +25 -10
  57. metadata.gz.sig +0 -0
  58. data/lib/ruby_smb/dcerpc/handle.rb +0 -60
  59. data/lib/ruby_smb/smb1/bit_field/trans2_flags.rb +0 -15
  60. data/spec/lib/ruby_smb/dcerpc/handle_spec.rb +0 -31
  61. data/spec/lib/ruby_smb/dcerpc/srvsvc_spec.rb +0 -13
  62. data/spec/lib/ruby_smb/smb1/bit_field/trans2_flags_spec.rb +0 -26
@@ -2,32 +2,31 @@ module RubySMB
2
2
  module SMB1
3
3
  module Packet
4
4
  module Trans
5
- # A SMB1 SMB_COM_TRANSACTION Response Packet as defined in
6
- # [2.2.4.33.2 Response](https://msdn.microsoft.com/en-us/library/ee442061.aspx)
5
+
6
+ # This class represents a generic SMB1 Trans Response Packet as defined in
7
+ # [2.2.4.33.2 Response](https://msdn.microsoft.com/en-us/library/ee442061.aspx)
7
8
  class Response < RubySMB::GenericPacket
8
9
  # A SMB1 Parameter Block
9
10
  class ParameterBlock < RubySMB::SMB1::ParameterBlock
10
- uint16 :total_parameter_count, label: 'Total Parameter Count(bytes)'
11
- uint16 :total_data_count, label: 'Total Data Count(bytes)'
12
- uint16 :reserved, label: 'Reserved Space', initial_value: 0x00
13
- uint16 :parameter_count, label: 'Parameter Count(bytes)', initial_value: -> { parent.data_block.trans_parameters.length }
14
- uint16 :parameter_offset, label: 'Parameter Offset', initial_value: -> { parent.data_block.trans_parameters.abs_offset }
15
- uint16 :parameter_displacement, label: 'Parameter Displacement'
16
- uint16 :data_count, label: 'Data Count(bytes)', initial_value: -> { parent.data_block.trans_data.length }
17
- uint16 :data_offset, label: 'Data Offset', initial_value: -> { parent.data_block.trans_data.abs_offset }
18
- uint16 :data_displacement, label: 'Data Displacement'
19
- uint8 :setup_count, label: 'Setup Count', initial_value: -> { setup.length }
20
- uint8 :reserved2, label: 'Reserved Space', initial_value: 0x00
21
-
22
- array :setup, type: :uint16, initial_length: 0
11
+ uint16 :total_parameter_count, label: 'Total Parameter Count(bytes)', initial_value: -> { parameter_count }
12
+ uint16 :total_data_count, label: 'Total Data Count(bytes)', initial_value: -> { data_count }
13
+ uint16 :reserved, label: 'Reserved Space', value: 0x0000
14
+ uint16 :parameter_count, label: 'Parameter Count(bytes)', initial_value: -> { parent.data_block.trans_parameters.length }
15
+ uint16 :parameter_offset, label: 'Parameter Offset', initial_value: -> { parent.data_block.trans_parameters.abs_offset }
16
+ uint16 :parameter_displacement, label: 'Parameter Displacement'
17
+ uint16 :data_count, label: 'Data Count(bytes)', initial_value: -> { parent.data_block.trans_data.length }
18
+ uint16 :data_offset, label: 'Data Offset', initial_value: -> { parent.data_block.trans_data.abs_offset }
19
+ uint16 :data_displacement, label: 'Data Displacement'
20
+ uint8 :setup_count, label: 'Setup Count', initial_value: -> { setup.length }
21
+ uint8 :reserved2, label: 'Reserved Space', value: 0x00
22
+ array :setup, type: :uint16, initial_length: :setup_count
23
23
  end
24
24
 
25
- # The {RubySMB::SMB1::DataBlock} specific to this packet type.
26
25
  class DataBlock < RubySMB::SMB1::Packet::Trans::DataBlock
27
- string :pad1, length: -> { pad1_length }
28
- string :trans_parameters, label: 'Trans Parameters'
29
- string :pad2, length: -> { pad2_length }
30
- string :trans_data, label: 'Trans Data'
26
+ string :pad1, length: lambda { pad1_length }
27
+ string :trans_parameters, label: 'Trans Parameters', read_length: -> { parent.parameter_block.parameter_count }
28
+ string :pad2, length: lambda { pad2_length }
29
+ string :trans_data, label: 'Trans Data', read_length: -> { parent.parameter_block.data_count }
31
30
  end
32
31
 
33
32
  smb_header :smb_header
@@ -39,8 +38,10 @@ module RubySMB
39
38
  smb_header.command = RubySMB::SMB1::Commands::SMB_COM_TRANSACTION
40
39
  smb_header.flags.reply = 1
41
40
  end
41
+
42
42
  end
43
43
  end
44
44
  end
45
45
  end
46
- end
46
+ end
47
+
@@ -3,6 +3,7 @@ module RubySMB
3
3
  module Packet
4
4
  module Trans
5
5
  module Subcommands
6
+ TRANSACT_NMPIPE = 0x0026
6
7
  PEEK_NMPIPE = 0x0023
7
8
  end
8
9
  end
@@ -0,0 +1,61 @@
1
+ module RubySMB
2
+ module SMB1
3
+ module Packet
4
+ module Trans
5
+
6
+ # A Trans TRANSACT_NMPIPE Request Packet as defined in
7
+ # [2.2.5.6.1 Request](https://msdn.microsoft.com/en-us/library/ee441832.aspx)
8
+ class TransactNmpipeRequest < RubySMB::GenericPacket
9
+
10
+ class ParameterBlock < RubySMB::SMB1::Packet::Trans::Request::ParameterBlock
11
+ end
12
+
13
+ class TransData < BinData::Record
14
+ string :write_data, label: 'Write Data', read_length: -> { parent.parent.parameter_block.data_count }
15
+
16
+ # Returns the length of the TransData struct in number of bytes
17
+ def length
18
+ do_num_bytes
19
+ end
20
+ end
21
+
22
+ class DataBlock < RubySMB::SMB1::Packet::Trans::DataBlock
23
+ # If unicode is set, the name field must be aligned to start on a 2-byte
24
+ # boundary from the start of the SMB header:
25
+ string :pad_name, length: -> { pad_name_length },
26
+ onlyif: -> { parent.smb_header.flags2.unicode.to_i == 1 }
27
+ choice :name, :selection => lambda { parent.smb_header.flags2.unicode.to_i },
28
+ :copy_on_change => true do
29
+ stringz 0, label: 'Name', initial_value: "\\PIPE\\"
30
+ stringz16 1, label: 'Name', initial_value: "\\PIPE\\".encode('utf-16le')
31
+ end
32
+ string :pad1, length: lambda { pad1_length }
33
+ string :trans_parameters, label: 'Trans Parameters', read_length: -> { parent.parameter_block.parameter_count }
34
+ string :pad2, length: lambda { pad2_length }
35
+ trans_data :trans_data, label: 'Trans Data'
36
+ end
37
+
38
+ smb_header :smb_header
39
+ parameter_block :parameter_block
40
+ data_block :data_block
41
+
42
+
43
+ def initialize_instance
44
+ super
45
+ smb_header.command = RubySMB::SMB1::Commands::SMB_COM_TRANSACTION
46
+ parameter_block.max_parameter_count = 0x0000
47
+ parameter_block.max_setup_count = 0x00
48
+ parameter_block.setup << RubySMB::SMB1::Packet::Trans::Subcommands::TRANSACT_NMPIPE
49
+ # FID: must be set to a valid FID from a server response for a
50
+ # previous SMB command to open or create a named pipe.
51
+ parameter_block.setup << 0x0000
52
+ end
53
+
54
+ def set_fid(fid)
55
+ parameter_block.setup[1] = fid
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,44 @@
1
+ module RubySMB
2
+ module SMB1
3
+ module Packet
4
+ module Trans
5
+
6
+ # A Trans TRANSACT_NMPIPE Response Packet as defined in
7
+ # [2.2.5.6.2 Response](https://msdn.microsoft.com/en-us/library/ee442003.aspx)
8
+ class TransactNmpipeResponse < RubySMB::GenericPacket
9
+
10
+ class ParameterBlock < RubySMB::SMB1::Packet::Trans::Response::ParameterBlock
11
+ end
12
+
13
+ class TransData < BinData::Record
14
+ string :read_data, label: 'Read Data', read_length: -> { parent.parameter_block.data_count }
15
+
16
+ # Returns the length of the TransData struct in number of bytes
17
+ def length
18
+ do_num_bytes
19
+ end
20
+ end
21
+
22
+ class DataBlock < RubySMB::SMB1::Packet::Trans::DataBlock
23
+ string :pad1, length: lambda { pad1_length }
24
+ string :trans_parameters, label: 'Trans Parameters', read_length: -> { parent.parameter_block.parameter_count }
25
+ string :pad2, length: lambda { pad2_length }
26
+ trans_data :trans_data, label: 'Trans Data'
27
+ end
28
+
29
+ smb_header :smb_header
30
+ parameter_block :parameter_block
31
+ data_block :data_block
32
+
33
+
34
+ def initialize_instance
35
+ super
36
+ smb_header.command = RubySMB::SMB1::Commands::SMB_COM_TRANSACTION
37
+ smb_header.flags.reply = 1
38
+ end
39
+
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -13,7 +13,7 @@ module RubySMB
13
13
  uint16 :max_data_count, label: 'Max Data Count(bytes)'
14
14
  uint8 :max_setup_count, label: 'Max Setup Count'
15
15
  uint8 :reserved, label: 'Reserved Space', initial_value: 0x00
16
- trans2_flags :flags
16
+ trans_flags :flags
17
17
  uint32 :timeout, label: 'Timeout', initial_value: 0x00000000
18
18
  uint16 :reserved2, label: 'Reserved Space', initial_value: 0x00
19
19
  uint16 :parameter_count, label: 'Parameter Count(bytes)', initial_value: -> { parent.data_block.trans2_parameters.length }
@@ -3,6 +3,9 @@ module RubySMB
3
3
  # Represents a pipe on the Remote server that we can perform
4
4
  # various I/O operations on.
5
5
  class Pipe < File
6
+ require 'ruby_smb/smb1/dcerpc'
7
+
8
+ include RubySMB::SMB1::Dcerpc
6
9
 
7
10
  # Reference: https://msdn.microsoft.com/en-us/library/ee441883.aspx
8
11
  STATUS_DISCONNECTED = 0x0001
@@ -0,0 +1,68 @@
1
+ module RubySMB
2
+ module SMB2
3
+ module Dcerpc
4
+
5
+ def net_share_enum_all(host)
6
+ bind(endpoint: RubySMB::Dcerpc::Srvsvc)
7
+
8
+ response = request(RubySMB::Dcerpc::Srvsvc::NET_SHARE_ENUM_ALL, host: host)
9
+
10
+ shares = RubySMB::Dcerpc::Srvsvc::NetShareEnumAll.parse_response(response.stub.to_binary_s)
11
+ shares.map{|s|{name: s[0], type: s[1], comment: s[2]}}
12
+ end
13
+
14
+ def bind(options={})
15
+ bind_req = RubySMB::Dcerpc::Bind.new(options)
16
+ ioctl_response = ioctl_send_recv(bind_req, options)
17
+ begin
18
+ dcerpc_response = RubySMB::Dcerpc::BindAck.read(ioctl_response.output_data)
19
+ rescue IOError
20
+ raise RubySMB::Dcerpc::Error::InvalidPacket, "Error reading the DCERPC response"
21
+ end
22
+ unless dcerpc_response.pdu_header.ptype == RubySMB::Dcerpc::PTypes::BIND_ACK
23
+ raise RubySMB::Dcerpc::Error::BindError, "Not a BindAck packet"
24
+ end
25
+
26
+ res_list = dcerpc_response.p_result_list
27
+ if res_list.n_results == 0 ||
28
+ res_list.p_results[0].result != RubySMB::Dcerpc::BindAck::ACCEPTANCE
29
+ raise RubySMB::Dcerpc::Error::BindError,
30
+ "Bind Failed (Result: #{res_list.p_results[0].result}, Reason: #{res_list.p_results[0].reason})"
31
+ end
32
+ dcerpc_response
33
+ end
34
+
35
+ def request(opnum, options={})
36
+ dcerpc_request = RubySMB::Dcerpc::Request.new({ :opnum => opnum }, options)
37
+ ioctl_response = ioctl_send_recv(dcerpc_request, options)
38
+ begin
39
+ dcerpc_response = RubySMB::Dcerpc::Response.read(ioctl_response.output_data)
40
+ rescue IOError
41
+ raise RubySMB::Dcerpc::Error::InvalidPacket, "Error reading the DCERPC response"
42
+ end
43
+ unless dcerpc_response.pdu_header.ptype == RubySMB::Dcerpc::PTypes::RESPONSE
44
+ raise RubySMB::Dcerpc::Error::InvalidPacket, "Not a Response packet"
45
+ end
46
+ dcerpc_response
47
+ end
48
+
49
+ def ioctl_send_recv(action, options={})
50
+ request = set_header_fields(RubySMB::SMB2::Packet::IoctlRequest.new(options))
51
+ request.ctl_code = 0x0011C017
52
+ request.flags.is_fsctl = 0x00000001
53
+ request.buffer = action.to_binary_s
54
+ ioctl_raw_response = @tree.client.send_recv(request)
55
+ ioctl_response = RubySMB::SMB2::Packet::IoctlResponse.read(ioctl_raw_response)
56
+ unless ioctl_response.smb2_header.command == RubySMB::SMB2::Commands::IOCTL
57
+ raise RubySMB::Error::InvalidPacket, 'Not a IoctlResponse packet'
58
+ end
59
+ unless ioctl_response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
60
+ raise RubySMB::Error::UnexpectedStatusCode, ioctl_response.status_code.name
61
+ end
62
+ ioctl_response
63
+ end
64
+
65
+ end
66
+ end
67
+ end
68
+
@@ -3,6 +3,9 @@ module RubySMB
3
3
  # Represents a pipe on the Remote server that we can perform
4
4
  # various I/O operations on.
5
5
  class Pipe < File
6
+ require 'ruby_smb/smb2/dcerpc'
7
+
8
+ include RubySMB::SMB2::Dcerpc
6
9
 
7
10
  STATUS_CONNECTED = 0x00000003
8
11
  STATUS_CLOSING = 0x00000004
@@ -1,3 +1,3 @@
1
1
  module RubySMB
2
- VERSION = '0.0.21'.freeze
2
+ VERSION = '0.0.22'.freeze
3
3
  end
@@ -201,8 +201,8 @@ RSpec.describe RubySMB::Client do
201
201
  allow(dispatcher).to receive(:send_packet) do |packet, _opts|
202
202
  expect(packet).to be_a(RubySMB::Nbss::SessionRequest)
203
203
  expect(packet.session_header.session_packet_type).to eq RubySMB::Nbss::SESSION_REQUEST
204
- expect(packet.called_name).to eq encoded_called_name
205
- expect(packet.calling_name).to eq encoded_calling_name
204
+ expect(packet.called_name).to eq encoded_called_name
205
+ expect(packet.calling_name).to eq encoded_calling_name
206
206
  expect(packet.session_header.packet_length).to eq (encoded_called_name.size + encoded_calling_name.size)
207
207
  end
208
208
  client.session_request
@@ -1123,11 +1123,58 @@ RSpec.describe RubySMB::Client do
1123
1123
  end
1124
1124
 
1125
1125
  describe '#net_share_enum_all' do
1126
- let(:shares){{}}
1126
+ let(:tree){ double("Tree") }
1127
+ let(:named_pipe){ double("Named Pipe") }
1127
1128
 
1128
- it 'it calls the smb2 method' do
1129
- expect(smb2_client).to receive(:smb2_net_share_enum_all).with(sock.peeraddr).and_return(shares)
1130
- smb2_client.net_share_enum_all(sock.peeraddr)
1129
+ before :example do
1130
+ allow(tree).to receive(:open_file).and_return(named_pipe)
1131
+ allow(named_pipe).to receive(:net_share_enum_all)
1132
+ end
1133
+
1134
+ context 'with SMB1' do
1135
+ before :example do
1136
+ allow(smb1_client).to receive(:tree_connect).and_return(tree)
1137
+ end
1138
+
1139
+ it 'it calls the #tree_connect method to connect to the "host" IPC$ share' do
1140
+ ipc_share = "\\\\#{sock.peeraddr}\\IPC$"
1141
+ expect(smb1_client).to receive(:tree_connect).with(ipc_share).and_return(tree)
1142
+ smb1_client.net_share_enum_all(sock.peeraddr)
1143
+ end
1144
+
1145
+ it 'it calls the Tree #open_file method to open "srvsvc" named pipe' do
1146
+ expect(tree).to receive(:open_file).with(filename: "srvsvc", write: true, read: true).and_return(named_pipe)
1147
+ smb1_client.net_share_enum_all(sock.peeraddr)
1148
+ end
1149
+
1150
+ it 'it calls the File #net_share_enum_all method with the correct host' do
1151
+ host = "1.2.3.4"
1152
+ expect(named_pipe).to receive(:net_share_enum_all).with(host)
1153
+ smb1_client.net_share_enum_all(host)
1154
+ end
1155
+ end
1156
+
1157
+ context 'with SMB2' do
1158
+ before :example do
1159
+ allow(smb2_client).to receive(:tree_connect).and_return(tree)
1160
+ end
1161
+
1162
+ it 'it calls the #tree_connect method to connect to the "host" IPC$ share' do
1163
+ ipc_share = "\\\\#{sock.peeraddr}\\IPC$"
1164
+ expect(smb2_client).to receive(:tree_connect).with(ipc_share).and_return(tree)
1165
+ smb2_client.net_share_enum_all(sock.peeraddr)
1166
+ end
1167
+
1168
+ it 'it calls the Tree #open_file method to open "srvsvc" named pipe' do
1169
+ expect(tree).to receive(:open_file).with(filename: "srvsvc", write: true, read: true).and_return(named_pipe)
1170
+ smb2_client.net_share_enum_all(sock.peeraddr)
1171
+ end
1172
+
1173
+ it 'it calls the File #net_share_enum_all method with the correct host' do
1174
+ host = "1.2.3.4"
1175
+ expect(named_pipe).to receive(:net_share_enum_all).with(host)
1176
+ smb2_client.net_share_enum_all(host)
1177
+ end
1131
1178
  end
1132
1179
  end
1133
1180
  end
@@ -0,0 +1,224 @@
1
+ RSpec.describe RubySMB::Dcerpc::BindAck do
2
+ subject(:packet) { described_class.new }
3
+
4
+ it { is_expected.to respond_to :pdu_header }
5
+ it { is_expected.to respond_to :max_xmit_frag }
6
+ it { is_expected.to respond_to :max_recv_frag }
7
+ it { is_expected.to respond_to :assoc_group_id }
8
+ it { is_expected.to respond_to :sec_addr }
9
+ it { is_expected.to respond_to :p_result_list }
10
+ it { is_expected.to respond_to :auth_verifier }
11
+
12
+ it 'is little endian' do
13
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
14
+ end
15
+
16
+ describe '#pdu_header' do
17
+ subject(:header) { packet.pdu_header }
18
+
19
+ it 'is a standard PDU Header' do
20
+ expect(header).to be_a RubySMB::Dcerpc::PDUHeader
21
+ end
22
+
23
+ it 'should have the #ptype field set to PTypes::BIND_ACK' do
24
+ expect(header.ptype).to eq RubySMB::Dcerpc::PTypes::BIND_ACK
25
+ end
26
+ end
27
+
28
+ describe '#max_xmit_frag' do
29
+ it 'should be a 16-bit unsigned integer' do
30
+ expect(packet.max_xmit_frag).to be_a BinData::Uint16le
31
+ end
32
+
33
+ it 'should have a default value of 0xFFFF' do
34
+ expect(packet.max_xmit_frag).to eq 0xFFFF
35
+ end
36
+ end
37
+
38
+ describe '#max_recv_frag' do
39
+ it 'should be a 16-bit unsigned integer' do
40
+ expect(packet.max_recv_frag).to be_a BinData::Uint16le
41
+ end
42
+
43
+ it 'should have a default value of 0xFFFF' do
44
+ expect(packet.max_recv_frag).to eq 0xFFFF
45
+ end
46
+ end
47
+
48
+ describe '#assoc_group_id' do
49
+ it 'should be a 32-bit unsigned integer' do
50
+ expect(packet.assoc_group_id).to be_a BinData::Uint32le
51
+ end
52
+ end
53
+
54
+ describe '#pad' do
55
+ it 'should keep #p_result_list 4-byte aligned' do
56
+ packet.sec_addr.port_spec = "test"
57
+ expect(packet.p_result_list.abs_offset % 4).to eq 0
58
+ end
59
+ end
60
+
61
+ describe '#p_result_list' do
62
+ it 'should be a PContListT structure' do
63
+ expect(packet.p_result_list).to be_a RubySMB::Dcerpc::PResultListT
64
+ end
65
+ end
66
+
67
+ describe '#auth_verifier' do
68
+ it 'should be a string' do
69
+ expect(packet.auth_verifier).to be_a BinData::String
70
+ end
71
+
72
+ it 'should not exist if the #auth_length PDU header field is 0' do
73
+ packet.pdu_header.auth_length = 0
74
+ expect(packet.auth_verifier?).to be false
75
+ end
76
+
77
+ it 'should exist only if the #auth_length PDU header field is greater than 0' do
78
+ packet.pdu_header.auth_length = 10
79
+ expect(packet.auth_verifier?).to be true
80
+ end
81
+
82
+ it 'reads #auth_length bytes' do
83
+ auth_verifier = '12345678'
84
+ packet.pdu_header.auth_length = 6
85
+ packet.auth_verifier.read(auth_verifier)
86
+ expect(packet.auth_verifier).to eq(auth_verifier[0,6])
87
+ end
88
+ end
89
+
90
+ describe '#pad_length' do
91
+ it 'returns 0 when #p_result_list is already 4-byte aligned' do
92
+ packet.sec_addr.port_spec = 'align'
93
+ expect(packet.pad_length).to eq 0
94
+ end
95
+
96
+ it 'returns 2 when #p_result_list is only 2-byte aligned' do
97
+ packet.sec_addr.port_spec = 'align' + 'AA'
98
+ expect(packet.pad_length).to eq 2
99
+ end
100
+ end
101
+
102
+ it 'reads its own binary representation and output the same packet' do
103
+ packet.sec_addr.port_spec = "port spec"
104
+ packet.p_result_list.n_results = 2
105
+ packet.auth_verifier = '123456'
106
+ packet.pdu_header.auth_length = 6
107
+ binary = packet.to_binary_s
108
+ expect(described_class.read(binary)).to eq(packet)
109
+ end
110
+ end
111
+
112
+ RSpec.describe RubySMB::Dcerpc::PortAnyT do
113
+ subject(:packet) { described_class.new }
114
+
115
+ it { is_expected.to respond_to :str_length }
116
+ it { is_expected.to respond_to :port_spec }
117
+
118
+ it 'is little endian' do
119
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
120
+ end
121
+
122
+ describe '#str_length' do
123
+ it 'should be a 16-bit unsigned integer' do
124
+ expect(packet.str_length).to be_a BinData::Uint16le
125
+ end
126
+
127
+ it 'should be the size of #port_spec string, including the NULL terminator' do
128
+ str = 'test'
129
+ packet.port_spec = str
130
+ expect(packet.str_length).to eq(str.size + 1)
131
+ end
132
+ end
133
+
134
+ describe '#port_spec' do
135
+ it 'should be a Stringz' do
136
+ expect(packet.port_spec).to be_a BinData::Stringz
137
+ end
138
+ end
139
+
140
+ it 'reads its own binary representation and output the same packet' do
141
+ packet.port_spec = "port spec"
142
+ binary = packet.to_binary_s
143
+ expect(described_class.read(binary)).to eq(packet)
144
+ end
145
+ end
146
+
147
+ RSpec.describe RubySMB::Dcerpc::PResultListT do
148
+ subject(:packet) { described_class.new }
149
+
150
+ it { is_expected.to respond_to :n_results }
151
+ it { is_expected.to respond_to :p_results }
152
+
153
+ it 'is little endian' do
154
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
155
+ end
156
+
157
+ describe '#n_results' do
158
+ it 'should be a 8-bit unsigned integer' do
159
+ expect(packet.n_results).to be_a BinData::Uint8
160
+ end
161
+ end
162
+
163
+ describe '#p_results' do
164
+ it 'should be an array of type PResultT' do
165
+ expect(packet.p_results).to be_a BinData::Array
166
+ type = packet.p_results.get_parameter(:type)
167
+ expect(type.instantiate).to be_a RubySMB::Dcerpc::PResultT
168
+ end
169
+
170
+ it 'should have #n_results elements' do
171
+ n_elements = 4
172
+ packet.n_results = n_elements
173
+ expect(packet.p_results.size).to eq n_elements
174
+ end
175
+ end
176
+
177
+ it 'reads its own binary representation and output the same packet' do
178
+ packet.n_results = 4
179
+ binary = packet.to_binary_s
180
+ expect(described_class.read(binary)).to eq(packet)
181
+ end
182
+ end
183
+
184
+ RSpec.describe RubySMB::Dcerpc::PResultT do
185
+ subject(:packet) { described_class.new }
186
+
187
+ it { is_expected.to respond_to :result }
188
+ it { is_expected.to respond_to :reason }
189
+ it { is_expected.to respond_to :transfer_syntax }
190
+
191
+ it 'is little endian' do
192
+ expect(described_class.fields.instance_variable_get(:@hints)[:endian]).to eq :little
193
+ end
194
+
195
+ describe '#result' do
196
+ it 'should be a 16-bit unsigned integer' do
197
+ expect(packet.result).to be_a BinData::Uint16le
198
+ end
199
+ end
200
+
201
+ describe '#reason' do
202
+ it 'should be a 16-bit unsigned integer' do
203
+ expect(packet.reason).to be_a BinData::Uint16le
204
+ end
205
+ end
206
+
207
+ describe '#transfer_syntax' do
208
+ it 'should be a PSyntaxIdT' do
209
+ expect(packet.transfer_syntax).to be_a RubySMB::Dcerpc::PSyntaxIdT
210
+ end
211
+
212
+ it 'is set to the NDR presentation syntax' do
213
+ expect(packet.transfer_syntax.if_uuid).to eq RubySMB::Dcerpc::Ndr::UUID
214
+ expect(packet.transfer_syntax.if_ver_major).to eq RubySMB::Dcerpc::Ndr::VER_MAJOR
215
+ expect(packet.transfer_syntax.if_ver_minor).to eq RubySMB::Dcerpc::Ndr::VER_MINOR
216
+ end
217
+ end
218
+
219
+ it 'reads its own binary representation and output the same packet' do
220
+ binary = packet.to_binary_s
221
+ expect(described_class.read(binary)).to eq(packet)
222
+ end
223
+ end
224
+