Class: RubySMB::Dispatcher::Socket

Inherits:
Base
  • Object
show all
Defined in:
lib/ruby_smb/dispatcher/socket.rb

Overview

This class provides a wrapper around a Socket for the packet Dispatcher. It allows for dependency injection of different Socket implementations.

Constant Summary collapse

READ_TIMEOUT =
30

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Base

#nbss

Constructor Details

#initialize(tcp_socket, read_timeout: READ_TIMEOUT) ⇒ Socket

Returns a new instance of Socket.

Parameters:

  • tcp_socket (IO)


20
21
22
23
24
# File 'lib/ruby_smb/dispatcher/socket.rb', line 20

def initialize(tcp_socket, read_timeout: READ_TIMEOUT)
  @tcp_socket = tcp_socket
  @tcp_socket.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_KEEPALIVE, true) if @tcp_socket.respond_to?(:setsockopt)
  @read_timeout = read_timeout
end

Instance Attribute Details

#read_timeoutInteger

Returns:

  • (Integer)


17
18
19
# File 'lib/ruby_smb/dispatcher/socket.rb', line 17

def read_timeout
  @read_timeout
end

#tcp_socketIO

Returns:

  • (IO)


12
13
14
# File 'lib/ruby_smb/dispatcher/socket.rb', line 12

def tcp_socket
  @tcp_socket
end

Class Method Details

.connect(host, port: 445, socket: TCPSocket.new(host, port)) ⇒ Object

Parameters:

  • host (String)

    passed to TCPSocket.new

  • port (Fixnum) (defaults to: 445)

    passed to TCPSocket.new



28
29
30
# File 'lib/ruby_smb/dispatcher/socket.rb', line 28

def self.connect(host, port: 445, socket: TCPSocket.new(host, port))
  new(socket)
end

Instance Method Details

#recv_packet(full_response: false) ⇒ String

Read a packet off the wire and parse it into a string

Parameters:

  • full_response (Boolean) (defaults to: false)

    whether to include the NetBios Session Service header in the response

Returns:

  • (String)

    the raw response (including the NetBios Session Service header if full_response is true)

Raises:



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/ruby_smb/dispatcher/socket.rb', line 63

def recv_packet(full_response: false)
  raise RubySMB::Error::CommunicationError, 'Connection has already been closed' if @tcp_socket.closed?
  if IO.select([@tcp_socket], nil, nil, @read_timeout).nil?
    raise RubySMB::Error::CommunicationError, "Read timeout expired when reading from the Socket (timeout=#{@read_timeout})"
  end

  begin
    nbss_data = @tcp_socket.read(4)
    raise RubySMB::Error::CommunicationError, 'Socket read returned nil' if nbss_data.nil?
    nbss_header = RubySMB::Nbss::SessionHeader.read(nbss_data)
  rescue IOError
    raise ::RubySMB::Error::NetBiosSessionService, 'NBSS Header is missing'
  end

  length = nbss_header.stream_protocol_length
  data = full_response ? nbss_header.to_binary_s : ''
  if length > 0
    if IO.select([@tcp_socket], nil, nil, @read_timeout).nil?
      raise RubySMB::Error::CommunicationError, "Read timeout expired when reading from the Socket (timeout=#{@read_timeout})"
    end
    data << @tcp_socket.read(length)
    data << @tcp_socket.read(length - data.length) while data.length < length
  end
  data
rescue Errno::EINVAL, Errno::ECONNABORTED, Errno::ECONNRESET, TypeError, NoMethodError => e
  raise RubySMB::Error::CommunicationError, "An error occurred reading from the Socket #{e.message}"
end

#send_packet(packet, nbss_header: true) ⇒ void

This method returns an undefined value.

Parameters:

  • packet (SMB2::Packet, #to_s)
  • nbss (Boolean)

    wether to include the NetBIOS Session header



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/ruby_smb/dispatcher/socket.rb', line 35

def send_packet(packet, nbss_header: true)
  data = nbss_header ? nbss(packet) : ''
  data << packet.to_binary_s
  bytes_written = 0
  begin
    while bytes_written < data.size
      retval = @tcp_socket.write(data[bytes_written..-1])

      if retval == nil
        raise RubySMB::Error::CommunicationError
      else
        bytes_written += retval
      end
    end

  rescue IOError, Errno::ECONNABORTED, Errno::ECONNRESET => e
    raise RubySMB::Error::CommunicationError, "An error occurred writing to the Socket: #{e.message}"
  end
  nil
end