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:



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:



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

def read_timeout
  @read_timeout
end

#tcp_socketIO

Returns:



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:

  • passed to TCPSocket.new

  • (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:

  • (defaults to: false)

    whether to include the NetBios Session Service header in the response

Returns:

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

Raises:

  • if there's an error reading the first 4 bytes, which are assumed to be the NetBiosSessionService header.

  • if the read timeout expires or an error occurs when reading the socket



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:

  • 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