lib/ruby_smb/error.rb
module RubySMB
# Contains all the RubySMB specific Error classes.
module Error
# Base class for RubySMB errors
class RubySMBError < StandardError; end
# Raised when there is a length or formatting issue with an ASN1-encoded string
# @see https://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One
# @todo Find an SMB-specific link for ASN1 above
class ASN1Encoding < RubySMBError; end
# Raised when there is a problem with communication over NetBios Session Service
# @see https://wiki.wireshark.org/NetBIOS/NBSS
class NetBiosSessionService < RubySMBError; end
# Raised when trying to parse raw binary into a Packet and the data
# is invalid.
class InvalidPacket < RubySMBError
attr_reader :status_code
def initialize(args = nil)
if args.nil?
super
elsif args.is_a? String
super(args)
elsif args.is_a? Hash
expected_proto = args[:expected_proto] ? translate_protocol(args[:expected_proto]) : '???'
expected_cmd = args[:expected_cmd] || '???'
received_proto = args[:packet]&.packet_smb_version || '???'
received_cmd = get_cmd(args[:packet]) || '???'
@status_code = args[:packet]&.status_code
super(
"Expecting #{expected_proto} protocol "\
"with command=#{expected_cmd}"\
"#{(" (" + args[:expected_custom] + ")") if args[:expected_custom]}, "\
"got #{received_proto} protocol "\
"with command=#{received_cmd}"\
"#{(" (" + args[:received_custom] + ")") if args[:received_custom]}"\
"#{(", Status: #{@status_code}") if @status_code}"
)
else
raise ArgumentError, "InvalidPacket expects a String or a Hash, got a #{args.class}"
end
end
def translate_protocol(proto)
case proto
when RubySMB::SMB1::SMB_PROTOCOL_ID
'SMB1'
when RubySMB::SMB2::SMB2_PROTOCOL_ID
'SMB2'
else
raise ArgumentError, 'Unknown SMB protocol'
end
end
private :translate_protocol
def get_cmd(packet)
return nil unless packet
case packet.packet_smb_version
when 'SMB1'
packet.smb_header.command
when 'SMB2'
packet.smb2_header.command
else
nil
end
end
private :get_cmd
end
# Raised when a response packet has a NTStatus code that was unexpected.
class UnexpectedStatusCode < RubySMBError
module Mixin
attr_reader :status_code
def status_name
@status_code.name
end
private
def status_code=(status_code)
case status_code
when WindowsError::ErrorCode
@status_code = status_code
when Integer
@status_code = WindowsError::NTStatus.find_by_retval(status_code).first
if @status_code.nil?
@status_code = WindowsError::ErrorCode.new("0x#{status_code.to_s(16).rjust(8, '0')}", status_code, "Unknown status: 0x#{status_code.to_s(16).rjust(8, '0')}")
end
else
raise ArgumentError, "Status code must be a WindowsError::ErrorCode or an Integer, got #{status_code.class}"
end
end
end
include Mixin
def initialize(status_code)
self.status_code = status_code
super
end
def to_s
"The server responded with an unexpected status code: #{status_code.name}"
end
end
# Raised when an error occurs with the underlying socket.
class CommunicationError < RubySMBError; end
# Raised when Protocol Negotiation fails, possibly due to an
# unsupported protocol.
class NegotiationFailure < RubySMBError; end
# Raised when Authentication fails, possibly due to an
# unsupported GSS mechanism type.
class AuthenticationFailure < RubySMBError; end
# Raised when trying to parse raw binary into a BitField and the data
# is invalid.
class InvalidBitField < RubySMBError; end
# Raised when an encryption operation fails
class EncryptionError < RubySMBError; end
# Raised when an signing operation fails
class SigningError < RubySMBError; end
end
end