ruby_smb 0.0.21 → 0.0.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+