class RubySMB::SMB2::Tree
An SMB2
connected remote Tree
, as returned by a
RubySMB::SMB2::Packet::TreeConnectRequest
Attributes
The client this Tree
is connected through @!attribute [rw] client
@return [RubySMB::Client]
The current Maximal Share Permissions @!attribute [rw] permissions
@return [RubySMB::SMB2::BitField::DirectoryAccessMask]
Whether or not the share associated with this tree connect needs to be encrypted (SMB 3.x) @!attribute [rw] tree_connect_encrypt_data
@return [Boolean]
Public Class Methods
# File lib/ruby_smb/smb2/tree.rb, line 31 def initialize(client:, share:, response:, encrypt: false) @client = client @share = share @id = response.smb2_header.tree_id @permissions = response.maximal_access @share_type = response.share_type @tree_connect_encrypt_data = encrypt end
Public Instance Methods
Disconnects this Tree
from the current session
@return [WindowsError::ErrorCode] the NTStatus sent back by the server. @raise [RubySMB::Error::InvalidPacket] if the response is not a TreeDisconnectResponse packet
# File lib/ruby_smb/smb2/tree.rb, line 44 def disconnect! request = RubySMB::SMB2::Packet::TreeDisconnectRequest.new request = set_header_fields(request) raw_response = client.send_recv(request, encrypt: @tree_connect_encrypt_data) response = RubySMB::SMB2::Packet::TreeDisconnectResponse.read(raw_response) unless response.valid? raise RubySMB::Error::InvalidPacket.new( expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID, expected_cmd: RubySMB::SMB2::Packet::TreeDisconnectResponse::COMMAND, packet: response ) end response.status_code end
List `directory` on the remote share.
@example
tree = client.tree_connect("\\\\192.168.99.134\\Share") tree.list(directory: "path\\to\\directory")
@param directory [String] path to the directory to be listed @param pattern [String] search pattern @param type [Class] file information class @return [Array] array of directory structures @raise [RubySMB::Error::InvalidPacket] if the response is not a QueryDirectoryResponse packet
# File lib/ruby_smb/smb2/tree.rb, line 148 def list(directory: nil, pattern: '*', type: RubySMB::Fscc::FileInformation::FileIdFullDirectoryInformation) create_response = open_directory(directory: directory) file_id = create_response.file_id directory_request = RubySMB::SMB2::Packet::QueryDirectoryRequest.new directory_request.file_information_class = type::CLASS_LEVEL directory_request.file_id = file_id directory_request.name = pattern max_read = client.server_max_read_size max_read = 65536 unless client.server_supports_multi_credit credit_charge = 0 if client.server_supports_multi_credit credit_charge = (max_read - 1) / 65536 + 1 end directory_request.output_length = max_read directory_request.smb2_header.credit_charge = credit_charge directory_request = set_header_fields(directory_request) files = [] loop do response = client.send_recv(directory_request, encrypt: @tree_connect_encrypt_data) directory_response = RubySMB::SMB2::Packet::QueryDirectoryResponse.read(response) unless directory_response.valid? raise RubySMB::Error::InvalidPacket.new( expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID, expected_cmd: RubySMB::SMB2::Packet::QueryDirectoryResponse::COMMAND, packet: directory_response ) end status_code = directory_response.smb2_header.nt_status.to_nt_status break if status_code == WindowsError::NTStatus::STATUS_NO_MORE_FILES unless status_code == WindowsError::NTStatus::STATUS_SUCCESS raise RubySMB::Error::UnexpectedStatusCode, status_code end files += directory_response.results(type) # Reset the message id so the client can update appropriately. directory_request.smb2_header.message_id = 0 end files end
'Opens' a directory file on the remote end, using a CreateRequest. This can be used to open an existing directory, or create a new one, depending on the disposition set.
@param directory [String] the name of the directory file @param disposition [Integer] the create disposition to use, should be one of {RubySMB::Dispositions} @param impersonation [Integer] the impersonation level to use, should be one of {RubySMB::ImpersonationLevels} @param read [Boolean] whether to request read access @param write [Boolean] whether to request write access @param delete [Boolean] whether to request delete access @return [RubySMB::SMB2::Packet::CreateResponse] the response packet returned from the server @raise [RubySMB::Error::InvalidPacket] if the response is not a CreateResponse packet
# File lib/ruby_smb/smb2/tree.rb, line 208 def open_directory(directory: nil, disposition: RubySMB::Dispositions::FILE_OPEN, impersonation: RubySMB::ImpersonationLevels::SEC_IMPERSONATE, read: true, write: false, delete: false) create_request = open_directory_packet(directory: directory, disposition: disposition, impersonation: impersonation, read: read, write: write, delete: delete) raw_response = client.send_recv(create_request, encrypt: @tree_connect_encrypt_data) response = RubySMB::SMB2::Packet::CreateResponse.read(raw_response) unless response.valid? raise RubySMB::Error::InvalidPacket.new( expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID, expected_cmd: RubySMB::SMB2::Packet::CreateResponse::COMMAND, packet: response ) end unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS raise RubySMB::Error::UnexpectedStatusCode, response.status_code end response end
Creates the Packet
for the open_directory
method.
@param directory [String] the name of the directory file @param disposition [Integer] the create disposition to use, should be one of {RubySMB::Dispositions} @param impersonation [Integer] the impersonation level to use, should be one of {RubySMB::ImpersonationLevels} @param read [Boolean] whether to request read access @param write [Boolean] whether to request write access @param delete [Boolean] whether to request delete access @return [RubySMB::SMB2::Packet::CreateRequest] the request packet to send to the server
# File lib/ruby_smb/smb2/tree.rb, line 239 def open_directory_packet(directory: nil, disposition: RubySMB::Dispositions::FILE_OPEN, impersonation: RubySMB::ImpersonationLevels::SEC_IMPERSONATE, read: true, write: false, delete: false) create_request = RubySMB::SMB2::Packet::CreateRequest.new create_request = set_header_fields(create_request) create_request.impersonation_level = impersonation create_request.create_options.directory_file = 1 create_request.file_attributes.directory = 1 create_request.desired_access.list = 1 create_request.share_access.read_access = 1 if read create_request.share_access.write_access = 1 if write create_request.share_access.delete_access = 1 if delete create_request.create_disposition = disposition if directory.nil? || directory.empty? create_request.name = "\x00" create_request.name_length = 0 else create_request.name = directory end create_request end
# File lib/ruby_smb/smb2/tree.rb, line 67 def open_file(filename:, attributes: nil, options: nil, disposition: RubySMB::Dispositions::FILE_OPEN, impersonation: RubySMB::ImpersonationLevels::SEC_IMPERSONATE, read: true, write: false, delete: false) create_request = RubySMB::SMB2::Packet::CreateRequest.new create_request = set_header_fields(create_request) # If the user supplied file attributes, use those, otherwise set some # sane defaults. if attributes create_request.file_attributes = attributes else create_request.file_attributes.directory = 0 create_request.file_attributes.normal = 1 end # If the user supplied Create Options, use those, otherwise set some # sane defaults. if options create_request.create_options = options else create_request.create_options.directory_file = 0 create_request.create_options.non_directory_file = 1 end if read create_request.share_access.read_access = 1 create_request.desired_access.read_data = 1 end if write create_request.share_access.write_access = 1 create_request.desired_access.write_data = 1 create_request.desired_access.append_data = 1 end if delete create_request.share_access.delete_access = 1 create_request.desired_access.delete_access = 1 end create_request.requested_oplock = 0xff create_request.impersonation_level = impersonation create_request.create_disposition = disposition create_request.name = filename raw_response = client.send_recv(create_request, encrypt: @tree_connect_encrypt_data) response = RubySMB::SMB2::Packet::CreateResponse.read(raw_response) unless response.valid? raise RubySMB::Error::InvalidPacket.new( expected_proto: RubySMB::SMB2::SMB2_PROTOCOL_ID, expected_cmd: RubySMB::SMB2::Packet::CreateResponse::COMMAND, packet: response ) end unless response.status_code == WindowsError::NTStatus::STATUS_SUCCESS raise RubySMB::Error::UnexpectedStatusCode, response.status_code end case @share_type when RubySMB::SMB2::Packet::TreeConnectResponse::SMB2_SHARE_TYPE_DISK RubySMB::SMB2::File.new(name: filename, tree: self, response: response, encrypt: @tree_connect_encrypt_data) when RubySMB::SMB2::Packet::TreeConnectResponse::SMB2_SHARE_TYPE_PIPE RubySMB::SMB2::Pipe.new(name: filename, tree: self, response: response) # when RubySMB::SMB2::TreeConnectResponse::SMB2_SHARE_TYPE_PRINT # it's a printer! else raise RubySMB::Error::RubySMBError, 'Unsupported share type' end end
# File lib/ruby_smb/smb2/tree.rb, line 59 def open_pipe(opts) # Make sure we don't modify the caller's hash options opts = opts.dup opts[:filename] = opts[:filename].dup opts[:filename] = opts[:filename][1..-1] if opts[:filename].start_with? '\\' open_file(**opts) end
Sets a few preset header fields that will always be set the same way for Tree
operations. This is, the TreeID, Credits, and Credit Charge.
@param [RubySMB::SMB2::Packet] the request packet to modify @return [RubySMB::SMB2::Packet] the modified packet.
# File lib/ruby_smb/smb2/tree.rb, line 268 def set_header_fields(request) request.smb2_header.tree_id = id request.smb2_header.credits = 256 request end