ruby_smb 1.0.3 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/lib/ruby_smb/client.rb +26 -4
  5. data/lib/ruby_smb/client/authentication.rb +43 -25
  6. data/lib/ruby_smb/client/echo.rb +20 -2
  7. data/lib/ruby_smb/client/negotiation.rb +27 -12
  8. data/lib/ruby_smb/client/tree_connect.rb +20 -14
  9. data/lib/ruby_smb/error.rb +40 -1
  10. data/lib/ruby_smb/generic_packet.rb +33 -4
  11. data/lib/ruby_smb/smb1/dcerpc.rb +7 -2
  12. data/lib/ruby_smb/smb1/file.rb +60 -11
  13. data/lib/ruby_smb/smb1/packet/close_request.rb +2 -5
  14. data/lib/ruby_smb/smb1/packet/close_response.rb +2 -1
  15. data/lib/ruby_smb/smb1/packet/echo_request.rb +2 -4
  16. data/lib/ruby_smb/smb1/packet/echo_response.rb +2 -1
  17. data/lib/ruby_smb/smb1/packet/empty_packet.rb +7 -0
  18. data/lib/ruby_smb/smb1/packet/logoff_request.rb +2 -4
  19. data/lib/ruby_smb/smb1/packet/logoff_response.rb +2 -1
  20. data/lib/ruby_smb/smb1/packet/negotiate_request.rb +2 -5
  21. data/lib/ruby_smb/smb1/packet/negotiate_response.rb +3 -7
  22. data/lib/ruby_smb/smb1/packet/negotiate_response_extended.rb +4 -4
  23. data/lib/ruby_smb/smb1/packet/nt_create_andx_request.rb +2 -4
  24. data/lib/ruby_smb/smb1/packet/nt_create_andx_response.rb +2 -1
  25. data/lib/ruby_smb/smb1/packet/nt_trans/create_request.rb +2 -1
  26. data/lib/ruby_smb/smb1/packet/nt_trans/create_response.rb +2 -1
  27. data/lib/ruby_smb/smb1/packet/nt_trans/request.rb +2 -4
  28. data/lib/ruby_smb/smb1/packet/nt_trans/response.rb +2 -1
  29. data/lib/ruby_smb/smb1/packet/read_andx_request.rb +2 -5
  30. data/lib/ruby_smb/smb1/packet/read_andx_response.rb +2 -1
  31. data/lib/ruby_smb/smb1/packet/session_setup_legacy_request.rb +2 -1
  32. data/lib/ruby_smb/smb1/packet/session_setup_legacy_response.rb +2 -1
  33. data/lib/ruby_smb/smb1/packet/session_setup_request.rb +2 -5
  34. data/lib/ruby_smb/smb1/packet/session_setup_response.rb +2 -1
  35. data/lib/ruby_smb/smb1/packet/trans/peek_nmpipe_request.rb +0 -1
  36. data/lib/ruby_smb/smb1/packet/trans/peek_nmpipe_response.rb +3 -2
  37. data/lib/ruby_smb/smb1/packet/trans/request.rb +2 -5
  38. data/lib/ruby_smb/smb1/packet/trans/response.rb +2 -1
  39. data/lib/ruby_smb/smb1/packet/trans/transact_nmpipe_request.rb +1 -1
  40. data/lib/ruby_smb/smb1/packet/trans/transact_nmpipe_response.rb +1 -1
  41. data/lib/ruby_smb/smb1/packet/trans2/find_first2_request.rb +2 -1
  42. data/lib/ruby_smb/smb1/packet/trans2/find_first2_response.rb +8 -2
  43. data/lib/ruby_smb/smb1/packet/trans2/find_next2_request.rb +2 -1
  44. data/lib/ruby_smb/smb1/packet/trans2/find_next2_response.rb +8 -2
  45. data/lib/ruby_smb/smb1/packet/trans2/open2_request.rb +2 -1
  46. data/lib/ruby_smb/smb1/packet/trans2/open2_response.rb +2 -1
  47. data/lib/ruby_smb/smb1/packet/trans2/request.rb +2 -4
  48. data/lib/ruby_smb/smb1/packet/trans2/request_secondary.rb +2 -4
  49. data/lib/ruby_smb/smb1/packet/trans2/response.rb +2 -1
  50. data/lib/ruby_smb/smb1/packet/trans2/set_file_information_request.rb +2 -1
  51. data/lib/ruby_smb/smb1/packet/trans2/set_file_information_response.rb +2 -1
  52. data/lib/ruby_smb/smb1/packet/tree_connect_request.rb +2 -4
  53. data/lib/ruby_smb/smb1/packet/tree_connect_response.rb +13 -3
  54. data/lib/ruby_smb/smb1/packet/tree_disconnect_request.rb +2 -4
  55. data/lib/ruby_smb/smb1/packet/tree_disconnect_response.rb +2 -1
  56. data/lib/ruby_smb/smb1/packet/write_andx_request.rb +2 -5
  57. data/lib/ruby_smb/smb1/packet/write_andx_response.rb +2 -1
  58. data/lib/ruby_smb/smb1/pipe.rb +8 -3
  59. data/lib/ruby_smb/smb1/tree.rb +40 -2
  60. data/lib/ruby_smb/smb2/dcerpc.rb +7 -2
  61. data/lib/ruby_smb/smb2/file.rb +97 -1
  62. data/lib/ruby_smb/smb2/packet/close_request.rb +2 -4
  63. data/lib/ruby_smb/smb2/packet/close_response.rb +2 -1
  64. data/lib/ruby_smb/smb2/packet/create_request.rb +2 -4
  65. data/lib/ruby_smb/smb2/packet/create_response.rb +2 -1
  66. data/lib/ruby_smb/smb2/packet/echo_request.rb +2 -4
  67. data/lib/ruby_smb/smb2/packet/echo_response.rb +2 -1
  68. data/lib/ruby_smb/smb2/packet/error_packet.rb +7 -0
  69. data/lib/ruby_smb/smb2/packet/ioctl_request.rb +2 -5
  70. data/lib/ruby_smb/smb2/packet/ioctl_response.rb +2 -1
  71. data/lib/ruby_smb/smb2/packet/logoff_request.rb +2 -4
  72. data/lib/ruby_smb/smb2/packet/logoff_response.rb +2 -1
  73. data/lib/ruby_smb/smb2/packet/negotiate_request.rb +2 -5
  74. data/lib/ruby_smb/smb2/packet/negotiate_response.rb +2 -1
  75. data/lib/ruby_smb/smb2/packet/query_directory_request.rb +2 -4
  76. data/lib/ruby_smb/smb2/packet/query_directory_response.rb +8 -2
  77. data/lib/ruby_smb/smb2/packet/read_request.rb +2 -4
  78. data/lib/ruby_smb/smb2/packet/read_response.rb +2 -1
  79. data/lib/ruby_smb/smb2/packet/session_setup_request.rb +2 -5
  80. data/lib/ruby_smb/smb2/packet/session_setup_response.rb +2 -1
  81. data/lib/ruby_smb/smb2/packet/set_info_request.rb +2 -4
  82. data/lib/ruby_smb/smb2/packet/set_info_response.rb +2 -1
  83. data/lib/ruby_smb/smb2/packet/tree_connect_request.rb +2 -5
  84. data/lib/ruby_smb/smb2/packet/tree_connect_response.rb +8 -2
  85. data/lib/ruby_smb/smb2/packet/tree_disconnect_request.rb +2 -4
  86. data/lib/ruby_smb/smb2/packet/tree_disconnect_response.rb +2 -1
  87. data/lib/ruby_smb/smb2/packet/write_request.rb +2 -4
  88. data/lib/ruby_smb/smb2/packet/write_response.rb +2 -1
  89. data/lib/ruby_smb/smb2/pipe.rb +9 -9
  90. data/lib/ruby_smb/smb2/tree.rb +44 -6
  91. data/lib/ruby_smb/version.rb +1 -1
  92. data/spec/lib/ruby_smb/client_spec.rb +123 -11
  93. data/spec/lib/ruby_smb/generic_packet_spec.rb +52 -4
  94. data/spec/lib/ruby_smb/smb1/file_spec.rb +182 -1
  95. data/spec/lib/ruby_smb/smb1/packet/{error_packet_spec.rb → empty_packet_spec.rb} +21 -0
  96. data/spec/lib/ruby_smb/smb1/packet/trans2/find_first2_response_spec.rb +11 -2
  97. data/spec/lib/ruby_smb/smb1/packet/trans2/find_next2_response_spec.rb +11 -2
  98. data/spec/lib/ruby_smb/smb1/packet/tree_connect_response_spec.rb +40 -0
  99. data/spec/lib/ruby_smb/smb1/pipe_spec.rb +63 -2
  100. data/spec/lib/ruby_smb/smb1/tree_spec.rb +44 -7
  101. data/spec/lib/ruby_smb/smb2/file_spec.rb +295 -2
  102. data/spec/lib/ruby_smb/smb2/packet/error_packet_spec.rb +51 -0
  103. data/spec/lib/ruby_smb/smb2/packet/query_directory_response_spec.rb +8 -0
  104. data/spec/lib/ruby_smb/smb2/packet/tree_connect_response_spec.rb +8 -0
  105. data/spec/lib/ruby_smb/smb2/pipe_spec.rb +69 -3
  106. data/spec/lib/ruby_smb/smb2/tree_spec.rb +214 -0
  107. metadata +6 -4
  108. metadata.gz.sig +0 -0
@@ -4,15 +4,13 @@ module RubySMB
4
4
  # An SMB2 TreeDisconnectRequest Packet as defined in
5
5
  # [2.2.11 SMB2 TREE_DISCONNECT Request](https://msdn.microsoft.com/en-us/library/cc246500.aspx)
6
6
  class TreeDisconnectRequest < RubySMB::GenericPacket
7
+ COMMAND = RubySMB::SMB2::Commands::TREE_DISCONNECT
8
+
7
9
  endian :little
8
10
  smb2_header :smb2_header
9
11
  uint16 :structure_size, label: 'Structure Size', initial_value: 4
10
12
  uint16 :reserved, label: 'Reserved', initial_value: 0
11
13
 
12
- def initialize_instance
13
- super
14
- smb2_header.command = RubySMB::SMB2::Commands::TREE_DISCONNECT
15
- end
16
14
  end
17
15
  end
18
16
  end
@@ -4,13 +4,14 @@ module RubySMB
4
4
  # An SMB2 TreeDisconnectResponse Packet as defined in
5
5
  # [2.2.12 SMB2 TREE_DISCONNECT Response](https://msdn.microsoft.com/en-us/library/cc246501.aspx)
6
6
  class TreeDisconnectResponse < RubySMB::GenericPacket
7
+ COMMAND = RubySMB::SMB2::Commands::TREE_DISCONNECT
8
+
7
9
  endian :little
8
10
  smb2_header :smb2_header
9
11
  uint16 :structure_size, label: 'Structure Size', initial_value: 4
10
12
 
11
13
  def initialize_instance
12
14
  super
13
- smb2_header.command = RubySMB::SMB2::Commands::TREE_DISCONNECT
14
15
  smb2_header.flags.reply = 1
15
16
  end
16
17
  end
@@ -4,6 +4,8 @@ module RubySMB
4
4
  # An SMB2 Write Request Packet as defined in
5
5
  # [2.2.21 SMB2 WRITE Request](https://msdn.microsoft.com/en-us/library/cc246532.aspx)
6
6
  class WriteRequest < RubySMB::GenericPacket
7
+ COMMAND = RubySMB::SMB2::Commands::WRITE
8
+
7
9
  endian :little
8
10
 
9
11
  smb2_header :smb2_header
@@ -19,10 +21,6 @@ module RubySMB
19
21
  uint32 :flags, label: 'Flags'
20
22
  string :buffer, label: 'Write Data Buffer'
21
23
 
22
- def initialize_instance
23
- super
24
- smb2_header.command = RubySMB::SMB2::Commands::WRITE
25
- end
26
24
  end
27
25
  end
28
26
  end
@@ -4,6 +4,8 @@ module RubySMB
4
4
  # An SMB2 Write Response Packet as defined in
5
5
  # [2.2.22 SMB2 WRITE Response](https://msdn.microsoft.com/en-us/library/cc246533.aspx)
6
6
  class WriteResponse < RubySMB::GenericPacket
7
+ COMMAND = RubySMB::SMB2::Commands::WRITE
8
+
7
9
  endian :little
8
10
 
9
11
  smb2_header :smb2_header
@@ -17,7 +19,6 @@ module RubySMB
17
19
 
18
20
  def initialize_instance
19
21
  super
20
- smb2_header.command = RubySMB::SMB2::Commands::WRITE
21
22
  smb2_header.flags.reply = 1
22
23
  end
23
24
  end
@@ -14,7 +14,7 @@ module RubySMB
14
14
  #
15
15
  # @param peek_size [Integer] Amount of data to peek
16
16
  # @return [RubySMB::SMB2::Packet::IoctlResponse]
17
- # @raise [RubySMB::Error::InvalidPacket] if not a valid FSCTL_PIPE_PEEK response
17
+ # @raise [RubySMB::Error::InvalidPacket] if not a valid FIoctlResponse response
18
18
  # @raise [RubySMB::Error::UnexpectedStatusCode] If status is not STATUS_BUFFER_OVERFLOW or STATUS_SUCCESS
19
19
  def peek(peek_size: 0)
20
20
  packet = RubySMB::SMB2::Packet::IoctlRequest.new
@@ -24,19 +24,19 @@ module RubySMB
24
24
  packet.max_output_response = 16 + peek_size
25
25
  packet = set_header_fields(packet)
26
26
  raw_response = @tree.client.send_recv(packet)
27
- begin
28
- response = RubySMB::SMB2::Packet::IoctlResponse.read(raw_response)
29
- rescue IOError
30
- response = RubySMB::SMB2::Packet::ErrorPacket.read(raw_response)
27
+ response = RubySMB::SMB2::Packet::IoctlResponse.read(raw_response)
28
+ unless response.valid?
29
+ raise RubySMB::Error::InvalidPacket.new(
30
+ expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
31
+ expected_cmd: RubySMB::SMB2::Packet::IoctlResponse::COMMAND,
32
+ received_proto: response.smb2_header.protocol,
33
+ received_cmd: response.smb2_header.command
34
+ )
31
35
  end
32
36
 
33
37
  unless response.status_code == WindowsError::NTStatus::STATUS_BUFFER_OVERFLOW or response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
34
38
  raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
35
39
  end
36
-
37
- unless response.smb2_header.command == RubySMB::SMB2::Commands::IOCTL
38
- raise RubySMB::Error::InvalidPacket, 'Not an IoctlResponse packet'
39
- end
40
40
  response
41
41
  end
42
42
 
@@ -34,11 +34,20 @@ module RubySMB
34
34
  # Disconnects this Tree from the current session
35
35
  #
36
36
  # @return [WindowsError::ErrorCode] the NTStatus sent back by the server.
37
+ # @raise [RubySMB::Error::InvalidPacket] if the response is not a TreeDisconnectResponse packet
37
38
  def disconnect!
38
39
  request = RubySMB::SMB2::Packet::TreeDisconnectRequest.new
39
40
  request = set_header_fields(request)
40
41
  raw_response = client.send_recv(request)
41
42
  response = RubySMB::SMB2::Packet::TreeDisconnectResponse.read(raw_response)
43
+ unless response.valid?
44
+ raise RubySMB::Error::InvalidPacket.new(
45
+ expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
46
+ expected_cmd: RubySMB::SMB2::Packet::TreeDisconnectResponse::COMMAND,
47
+ received_proto: response.smb2_header.protocol,
48
+ received_cmd: response.smb2_header.command
49
+ )
50
+ end
42
51
  response.status_code
43
52
  end
44
53
 
@@ -89,9 +98,16 @@ module RubySMB
89
98
 
90
99
  raw_response = client.send_recv(create_request)
91
100
  response = RubySMB::SMB2::Packet::CreateResponse.read(raw_response)
92
-
93
- if response.is_a?(RubySMB::SMB2::Packet::ErrorPacket)
94
- raise RubySMB::Error::RubySMBError
101
+ unless response.valid?
102
+ raise RubySMB::Error::InvalidPacket.new(
103
+ expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
104
+ expected_cmd: RubySMB::SMB2::Packet::CreateResponse::COMMAND,
105
+ received_proto: response.smb2_header.protocol,
106
+ received_cmd: response.smb2_header.command
107
+ )
108
+ end
109
+ unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
110
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
95
111
  end
96
112
 
97
113
  case @share_type
@@ -102,7 +118,7 @@ module RubySMB
102
118
  # when 0x03
103
119
  # it's a printer!
104
120
  else
105
- raise RubySMB::Error::RubySMBError
121
+ raise RubySMB::Error::RubySMBError, 'Unsupported share type'
106
122
  end
107
123
  end
108
124
 
@@ -116,6 +132,7 @@ module RubySMB
116
132
  # @param pattern [String] search pattern
117
133
  # @param type [Class] file information class
118
134
  # @return [Array] array of directory structures
135
+ # @raise [RubySMB::Error::InvalidPacket] if the response is not a QueryDirectoryResponse packet
119
136
  def list(directory: nil, pattern: '*', type: RubySMB::Fscc::FileInformation::FileIdFullDirectoryInformation)
120
137
  create_response = open_directory(directory: directory)
121
138
  file_id = create_response.file_id
@@ -133,13 +150,20 @@ module RubySMB
133
150
  loop do
134
151
  response = client.send_recv(directory_request)
135
152
  directory_response = RubySMB::SMB2::Packet::QueryDirectoryResponse.read(response)
153
+ unless directory_response.valid?
154
+ raise RubySMB::Error::InvalidPacket.new(
155
+ expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
156
+ expected_cmd: RubySMB::SMB2::Packet::QueryDirectoryResponse::COMMAND,
157
+ received_proto: directory_response.smb2_header.protocol,
158
+ received_cmd: directory_response.smb2_header.command
159
+ )
160
+ end
136
161
 
137
162
  status_code = directory_response.smb2_header.nt_status.to_nt_status
138
163
 
139
164
  break if status_code == WindowsError::NTStatus::STATUS_NO_MORE_FILES
140
165
 
141
166
  unless status_code == WindowsError::NTStatus::STATUS_SUCCESS
142
-
143
167
  raise RubySMB::Error::UnexpectedStatusCode, status_code.to_s
144
168
  end
145
169
 
@@ -162,6 +186,7 @@ module RubySMB
162
186
  # @param write [Boolean] whether to request write access
163
187
  # @param delete [Boolean] whether to request delete access
164
188
  # @return [RubySMB::SMB2::Packet::CreateResponse] the response packet returned from the server
189
+ # @raise [RubySMB::Error::InvalidPacket] if the response is not a CreateResponse packet
165
190
  def open_directory(directory: nil, disposition: RubySMB::Dispositions::FILE_OPEN,
166
191
  impersonation: RubySMB::ImpersonationLevels::SEC_IMPERSONATE,
167
192
  read: true, write: false, delete: false)
@@ -169,7 +194,20 @@ module RubySMB
169
194
  create_request = open_directory_packet(directory: directory, disposition: disposition,
170
195
  impersonation: impersonation, read: read, write: write, delete: delete)
171
196
  raw_response = client.send_recv(create_request)
172
- RubySMB::SMB2::Packet::CreateResponse.read(raw_response)
197
+ response = RubySMB::SMB2::Packet::CreateResponse.read(raw_response)
198
+ unless response.valid?
199
+ raise RubySMB::Error::InvalidPacket.new(
200
+ expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID,
201
+ expected_cmd: RubySMB::SMB2::Packet::CreateResponse::COMMAND,
202
+ received_proto: response.smb2_header.protocol,
203
+ received_cmd: response.smb2_header.command
204
+ )
205
+ end
206
+ unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS
207
+ raise RubySMB::Error::UnexpectedStatusCode, response.status_code.name
208
+ end
209
+
210
+ response
173
211
  end
174
212
 
175
213
  # Creates the Packet for the #open_directory method.
@@ -1,3 +1,3 @@
1
1
  module RubySMB
2
- VERSION = '1.0.3'.freeze
2
+ VERSION = '1.0.4'.freeze
3
3
  end
@@ -9,6 +9,7 @@ RSpec.describe RubySMB::Client do
9
9
  let(:smb1_client) { described_class.new(dispatcher, smb2: false, username: username, password: password) }
10
10
  let(:smb2_client) { described_class.new(dispatcher, smb1: false, username: username, password: password) }
11
11
  let(:empty_packet) { RubySMB::SMB1::Packet::EmptyPacket.new }
12
+ let(:error_packet) { RubySMB::SMB2::Packet::ErrorPacket.new }
12
13
 
13
14
  describe '#initialize' do
14
15
  it 'should raise an ArgumentError without a valid dispatcher' do
@@ -153,6 +154,94 @@ RSpec.describe RubySMB::Client do
153
154
  end
154
155
  end
155
156
 
157
+ describe '#logoff!' do
158
+ context 'with SMB1' do
159
+ let(:raw_response) { double('Raw response') }
160
+ let(:logoff_response) {
161
+ RubySMB::SMB1::Packet::LogoffResponse.new(smb_header: {:command => RubySMB::SMB1::Commands::SMB_COM_LOGOFF} )
162
+ }
163
+ before :example do
164
+ allow(smb1_client).to receive(:send_recv).and_return(raw_response)
165
+ allow(RubySMB::SMB1::Packet::LogoffResponse).to receive(:read).and_return(logoff_response)
166
+ allow(smb1_client).to receive(:wipe_state!)
167
+ end
168
+
169
+ it 'creates a LogoffRequest packet' do
170
+ expect(RubySMB::SMB1::Packet::LogoffRequest).to receive(:new).and_call_original
171
+ smb1_client.logoff!
172
+ end
173
+
174
+ it 'calls #send_recv' do
175
+ expect(smb1_client).to receive(:send_recv)
176
+ smb1_client.logoff!
177
+ end
178
+
179
+ it 'reads the raw response as a LogoffResponse packet' do
180
+ expect(RubySMB::SMB1::Packet::LogoffResponse).to receive(:read).with(raw_response)
181
+ smb1_client.logoff!
182
+ end
183
+
184
+ it 'raise an InvalidPacket exception when the response is an empty packet' do
185
+ allow(RubySMB::SMB1::Packet::LogoffResponse).to receive(:read).and_return(RubySMB::SMB1::Packet::EmptyPacket.new)
186
+ expect {smb1_client.logoff!}.to raise_error(RubySMB::Error::InvalidPacket)
187
+ end
188
+
189
+ it 'raise an InvalidPacket exception when the response is not valid' do
190
+ allow(logoff_response).to receive(:valid?).and_return(false)
191
+ expect {smb1_client.logoff!}.to raise_error(RubySMB::Error::InvalidPacket)
192
+ end
193
+
194
+ it 'calls #wipe_state!' do
195
+ expect(smb1_client).to receive(:wipe_state!)
196
+ smb1_client.logoff!
197
+ end
198
+
199
+ it 'returns the expected status code' do
200
+ logoff_response.smb_header.nt_status = WindowsError::NTStatus::STATUS_PENDING.value
201
+ allow(RubySMB::SMB1::Packet::LogoffResponse).to receive(:read).and_return(logoff_response)
202
+ expect(smb1_client.logoff!).to eq(WindowsError::NTStatus::STATUS_PENDING)
203
+ end
204
+ end
205
+
206
+ context 'with SMB2' do
207
+ let(:raw_response) { double('Raw response') }
208
+ let(:logoff_response) {
209
+ RubySMB::SMB2::Packet::LogoffResponse.new(smb_header: {:command => RubySMB::SMB2::Commands::LOGOFF} )
210
+ }
211
+ before :example do
212
+ allow(smb2_client).to receive(:send_recv).and_return(raw_response)
213
+ allow(RubySMB::SMB2::Packet::LogoffResponse).to receive(:read).and_return(logoff_response)
214
+ allow(smb2_client).to receive(:wipe_state!)
215
+ end
216
+
217
+ it 'creates a LogoffRequest packet' do
218
+ expect(RubySMB::SMB2::Packet::LogoffRequest).to receive(:new).and_call_original
219
+ smb2_client.logoff!
220
+ end
221
+
222
+ it 'calls #send_recv' do
223
+ expect(smb2_client).to receive(:send_recv)
224
+ smb2_client.logoff!
225
+ end
226
+
227
+ it 'reads the raw response as a LogoffResponse packet' do
228
+ expect(RubySMB::SMB2::Packet::LogoffResponse).to receive(:read).with(raw_response)
229
+ smb2_client.logoff!
230
+ end
231
+
232
+ it 'raise an InvalidPacket exception when the response is an error packet' do
233
+ allow(RubySMB::SMB2::Packet::LogoffResponse).to receive(:read).and_return(RubySMB::SMB2::Packet::ErrorPacket.new)
234
+ expect {smb2_client.logoff!}.to raise_error(RubySMB::Error::InvalidPacket)
235
+ end
236
+
237
+ it 'raise an InvalidPacket exception when the response is not a LOGOFF command' do
238
+ logoff_response.smb2_header.command = RubySMB::SMB2::Commands::ECHO
239
+ allow(RubySMB::SMB2::Packet::LogoffResponse).to receive(:read).and_return(logoff_response)
240
+ expect {smb2_client.logoff!}.to raise_error(RubySMB::Error::InvalidPacket)
241
+ end
242
+ end
243
+ end
244
+
156
245
  context 'NetBIOS Session Service' do
157
246
  describe '#session_request' do
158
247
  let(:session_header) { RubySMB::Nbss::SessionHeader.new }
@@ -197,6 +286,11 @@ RSpec.describe RubySMB::Client do
197
286
  allow(dispatcher).to receive(:recv_packet).and_return(negative_session_response.to_binary_s)
198
287
  expect { client.session_request }.to raise_error(RubySMB::Error::NetBiosSessionService)
199
288
  end
289
+
290
+ it 'raises an InvalidPacket exception when an error occurs while reading' do
291
+ allow(RubySMB::Nbss::SessionHeader).to receive(:read).and_raise(IOError)
292
+ expect { client.session_request }.to raise_error(RubySMB::Error::InvalidPacket)
293
+ end
200
294
  end
201
295
 
202
296
  describe '#session_request_packet' do
@@ -332,10 +426,15 @@ RSpec.describe RubySMB::Client do
332
426
  expect(smb1_client.negotiate_response(smb1_extended_response_raw)).to eq smb1_extended_response
333
427
  end
334
428
 
335
- it 'raises an exception if the Response is invalid' do
429
+ it 'raises an exception if the response is not a SMB packet' do
336
430
  expect { smb1_client.negotiate_response(random_junk) }.to raise_error(RubySMB::Error::InvalidPacket)
337
431
  end
338
432
 
433
+ it 'raises an InvalidPacket error if the response is not a valid response' do
434
+ empty_packet.smb_header.command = RubySMB::SMB2::Commands::NEGOTIATE
435
+ expect { smb1_client.negotiate_response(empty_packet.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
436
+ end
437
+
339
438
  it 'considers the response invalid if it is not an actual Negotiate Response' do
340
439
  bogus_response = smb1_extended_response
341
440
  bogus_response.smb_header.command = 0xff
@@ -357,6 +456,12 @@ RSpec.describe RubySMB::Client do
357
456
  it 'raises an exception if the Response is invalid' do
358
457
  expect { smb2_client.negotiate_response(random_junk) }.to raise_error(RubySMB::Error::InvalidPacket)
359
458
  end
459
+
460
+ it 'considers the response invalid if it is not an actual Negotiate Response' do
461
+ bogus_response = smb2_response
462
+ bogus_response.smb2_header.command = RubySMB::SMB2::Commands::ECHO
463
+ expect { smb2_client.negotiate_response(bogus_response.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
464
+ end
360
465
  end
361
466
 
362
467
  context 'with SMB1 and SMB2 enabled' do
@@ -624,7 +729,7 @@ RSpec.describe RubySMB::Client do
624
729
  expect { smb1_client.smb1_ntlmssp_challenge_packet(response.to_binary_s) }.to raise_error(RubySMB::Error::UnexpectedStatusCode)
625
730
  end
626
731
 
627
- it 'raises an InvalidPacket if the Command field is wrong' do
732
+ it 'raise an InvalidPacket exception when the response is not valid' do
628
733
  expect { smb1_client.smb1_ntlmssp_challenge_packet(wrong_command.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
629
734
  end
630
735
  end
@@ -645,7 +750,7 @@ RSpec.describe RubySMB::Client do
645
750
  expect(smb1_client.smb1_ntlmssp_final_packet(response.to_binary_s)).to eq response
646
751
  end
647
752
 
648
- it 'raises an InvalidPacket if the Command field is wrong' do
753
+ it 'raise an InvalidPacket exception when the response is not valid' do
649
754
  expect { smb1_client.smb1_ntlmssp_final_packet(wrong_command.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
650
755
  end
651
756
  end
@@ -717,12 +822,7 @@ RSpec.describe RubySMB::Client do
717
822
  expect(smb1_client.smb1_anonymous_auth_response(anonymous_response.to_binary_s)).to eq anonymous_response
718
823
  end
719
824
 
720
- it 'returns an empty packet if the raw data is not a valid response' do
721
- empty_packet.smb_header.command = RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP
722
- expect(smb1_client.smb1_anonymous_auth_response(empty_packet.to_binary_s)).to eq empty_packet
723
- end
724
-
725
- it 'raises an InvalidPacket error if the command is wrong' do
825
+ it 'raise an InvalidPacket exception when the response is not valid' do
726
826
  anonymous_response.smb_header.command = RubySMB::SMB1::Commands::SMB_COM_NEGOTIATE
727
827
  expect { smb1_client.smb1_anonymous_auth_response(anonymous_response.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
728
828
  end
@@ -829,7 +929,7 @@ RSpec.describe RubySMB::Client do
829
929
  expect { smb2_client.smb2_ntlmssp_challenge_packet(response.to_binary_s) }.to raise_error(RubySMB::Error::UnexpectedStatusCode)
830
930
  end
831
931
 
832
- it 'raises an InvalidPacket if the Command field is wrong' do
932
+ it 'raise an InvalidPacket exception when the response is not valid' do
833
933
  expect { smb2_client.smb2_ntlmssp_challenge_packet(wrong_command.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
834
934
  end
835
935
  end
@@ -888,7 +988,7 @@ RSpec.describe RubySMB::Client do
888
988
  expect(smb2_client.smb2_ntlmssp_final_packet(response.to_binary_s)).to eq response
889
989
  end
890
990
 
891
- it 'raises an InvalidPacket if the Command field is wrong' do
991
+ it 'raise an InvalidPacket exception when the response is not valid' do
892
992
  expect { smb2_client.smb2_ntlmssp_final_packet(wrong_command.to_binary_s) }.to raise_error(RubySMB::Error::InvalidPacket)
893
993
  end
894
994
  end
@@ -1199,6 +1299,12 @@ RSpec.describe RubySMB::Client do
1199
1299
  expect(smb1_client).to receive(:send_recv).and_return(echo_response.to_binary_s)
1200
1300
  expect(smb1_client.echo).to eq WindowsError::NTStatus::STATUS_ABANDONED
1201
1301
  end
1302
+
1303
+ it 'raise an InvalidPacket exception when the response is not valid' do
1304
+ echo_response.smb_header.command = RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP
1305
+ allow(smb1_client).to receive(:send_recv).and_return(echo_response.to_binary_s)
1306
+ expect { smb1_client.echo }.to raise_error(RubySMB::Error::InvalidPacket)
1307
+ end
1202
1308
  end
1203
1309
 
1204
1310
  context 'with SMB2' do
@@ -1210,6 +1316,12 @@ RSpec.describe RubySMB::Client do
1210
1316
  expect(smb2_client).to receive(:send_recv).with(echo_request).and_return(echo_response.to_binary_s)
1211
1317
  expect(smb2_client.smb2_echo).to eq echo_response
1212
1318
  end
1319
+
1320
+ it 'raise an InvalidPacket exception when the response is not valid' do
1321
+ echo_response.smb2_header.command = RubySMB::SMB2::Commands::SESSION_SETUP
1322
+ allow(smb2_client).to receive(:send_recv).and_return(echo_response.to_binary_s)
1323
+ expect { smb2_client.smb2_echo }.to raise_error(RubySMB::Error::InvalidPacket)
1324
+ end
1213
1325
  end
1214
1326
  end
1215
1327
 
@@ -63,8 +63,13 @@ RSpec.describe RubySMB::GenericPacket do
63
63
  expect(RubySMB::SMB1::Packet::NegotiateResponse.read(smb1_error_packet.to_binary_s)).to be_a RubySMB::SMB1::Packet::EmptyPacket
64
64
  end
65
65
 
66
- it 'reraises the exception if it is not a valid error packet either' do
67
- expect{RubySMB::SMB1::Packet::NegotiateResponse.read('a')}.to raise_error(EOFError)
66
+ it 'raises an InvaliPacket exception if it is not a valid error packet either' do
67
+ expect{RubySMB::SMB1::Packet::NegotiateResponse.read('a')}.to raise_error(RubySMB::Error::InvalidPacket)
68
+ end
69
+
70
+ it 'sets the EmptyPacket#original_command attribute to the original COMMAND' do
71
+ packet = RubySMB::SMB1::Packet::NegotiateResponse.read(smb1_error_packet.to_binary_s)
72
+ expect(packet.original_command).to eq RubySMB::SMB1::Packet::NegotiateResponse::COMMAND
68
73
  end
69
74
  end
70
75
 
@@ -75,8 +80,51 @@ RSpec.describe RubySMB::GenericPacket do
75
80
  expect(RubySMB::SMB2::Packet::NegotiateResponse.read(smb2_error_packet.to_binary_s)).to be_a RubySMB::SMB2::Packet::ErrorPacket
76
81
  end
77
82
 
78
- it 'reraises the exception if it is not a valid error packet either' do
79
- expect{RubySMB::SMB2::Packet::NegotiateResponse.read('a')}.to raise_error(EOFError)
83
+ it 'raises an InvaliPacket exception if it is not a valid error packet either' do
84
+ expect{RubySMB::SMB2::Packet::NegotiateResponse.read('a')}.to raise_error(RubySMB::Error::InvalidPacket)
85
+ end
86
+
87
+ it 'sets the ErrorPacket#original_command attribute to the original COMMAND' do
88
+ packet = RubySMB::SMB2::Packet::NegotiateResponse.read(smb2_error_packet.to_binary_s)
89
+ expect(packet.original_command).to eq RubySMB::SMB2::Packet::NegotiateResponse::COMMAND
90
+ end
91
+ end
92
+ end
93
+
94
+ describe '#valid?' do
95
+ context 'when reading an SMB1 packet' do
96
+ let(:packet) { RubySMB::SMB1::Packet::NegotiateResponse.new }
97
+
98
+ it 'returns true if the packet protocol ID and header command are valid' do
99
+ expect(packet).to be_valid
100
+ end
101
+
102
+ it 'returns false if the packet protocol ID is wrong' do
103
+ packet.smb_header.protocol = RubySMB::SMB2::SMB2_PROTOCOL_ID
104
+ expect(packet).to_not be_valid
105
+ end
106
+
107
+ it 'returns false if the packet header command is wrong' do
108
+ packet.smb_header.command = RubySMB::SMB1::Commands::SMB_COM_TREE_CONNECT
109
+ expect(packet).to_not be_valid
110
+ end
111
+ end
112
+
113
+ context 'when reading an SMB2 packet' do
114
+ let(:packet) { RubySMB::SMB2::Packet::NegotiateResponse.new }
115
+
116
+ it 'returns true if the packet protocol ID and header command are valid' do
117
+ expect(packet).to be_valid
118
+ end
119
+
120
+ it 'returns false if the packet protocol ID is wrong' do
121
+ packet.smb2_header.protocol = RubySMB::SMB1::SMB_PROTOCOL_ID
122
+ expect(packet).to_not be_valid
123
+ end
124
+
125
+ it 'returns false if the packet header command is wrong' do
126
+ packet.smb2_header.command = RubySMB::SMB2::Commands::TREE_CONNECT
127
+ expect(packet).to_not be_valid
80
128
  end
81
129
  end
82
130
  end