Class: Rex::Proto::Thrift::Client

Inherits:
Object
  • Object
show all
Includes:
Rex::Proto::Thrift
Defined in:
lib/rex/proto/thrift/client.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(host, port, context: {}, ssl: false, ssl_version: nil, comm: nil, timeout: 10) ⇒ Client

Returns a new instance of Client.



22
23
24
25
26
27
28
29
30
# File 'lib/rex/proto/thrift/client.rb', line 22

def initialize(host, port, context: {}, ssl: false, ssl_version: nil, comm: nil, timeout: 10)
  @host = host
  @port = port
  @context = context
  @ssl = ssl
  @ssl_version = ssl_version
  @comm = comm
  @timeout = timeout
end

Instance Attribute Details

#commRex::Socket::Comm (readonly)

Returns An optional, explicit object to use for creating the connection.

Returns:

  • (Rex::Socket::Comm)

    An optional, explicit object to use for creating the connection.



16
17
18
# File 'lib/rex/proto/thrift/client.rb', line 16

def comm
  @comm
end

#hostString (readonly)

Returns The Thrift server host.

Returns:

  • (String)

    The Thrift server host.



7
8
9
# File 'lib/rex/proto/thrift/client.rb', line 7

def host
  @host
end

#portInteger (readonly)

Returns The Thrift server port.

Returns:

  • (Integer)

    The Thrift server port.



10
11
12
# File 'lib/rex/proto/thrift/client.rb', line 10

def port
  @port
end

#sslBoolean (readonly)

Returns Whether or not SSL is used for the connection.

Returns:

  • (Boolean)

    Whether or not SSL is used for the connection.



13
14
15
# File 'lib/rex/proto/thrift/client.rb', line 13

def ssl
  @ssl
end

#timeoutInteger

Returns The communication timeout in seconds.

Returns:

  • (Integer)

    The communication timeout in seconds.



20
21
22
# File 'lib/rex/proto/thrift/client.rb', line 20

def timeout
  @timeout
end

Instance Method Details

#call(method_name, *data, timeout: @timeout) ⇒ Array<Hash>

Call the specific method on the remote peer.

Parameters:

  • method_name (String)

    The method name to call.

  • *data (BinData::Struct, Hash, String)

    The data to send in the method call.

  • timeout (Float) (defaults to: @timeout)

    The timeout to use for this call operation. Defaults to the instance timeout.

Returns:

  • (Array<Hash>)

    The results of the method call.

Raises:

  • (Error::UnexpectedReplyError)

    Raised if the reply was not to the method call.

  • (Rex::TimeoutError)

    Raised when all of the data was not received within the timeout.



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/rex/proto/thrift/client.rb', line 115

def call(method_name, *data, timeout: @timeout)
  tx_header = ThriftHeader.new(method_name: method_name, message_type: ThriftMessageType::CALL)
  tx_data = data.map do |part|
    case part
    when BinData::Struct
      part.to_binary_s
    when Hash
      ThriftData.new(part).to_binary_s
    else
      part
    end
  end

  send_raw(tx_header.to_binary_s + tx_data.join)
  rx_data = recv_raw(timeout: timeout)
  rx_header = ThriftHeader.read(rx_data)
  unless rx_header.message_type == ThriftMessageType::REPLY
    raise Error::UnexpectedReplyError.new(rx_header, 'The received header was not a REPLY message.')
  end

  unless rx_header.method_name == method_name
    raise Error::UnexpectedReplyError.new(rx_header, 'The received header was not to the expected method.')
  end

  ThriftStruct.read(rx_data[rx_header.num_bytes..]).snapshot
end

#closeNilClass

Close the connection to the remote server.

Returns:

  • (NilClass)


55
56
57
58
59
60
61
62
# File 'lib/rex/proto/thrift/client.rb', line 55

def close
  if @conn && !@conn.closed?
    @conn.shutdown
    @conn.close
  end

  @conn = nil
end

#connect(t = -1)) ⇒ NilClass

Establish the connection to the remote server.

Parameters:

  • t (Integer) (defaults to: -1))

    An explicit timeout to use for the connection otherwise the default will be used.

Returns:

  • (NilClass)


36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/rex/proto/thrift/client.rb', line 36

def connect(t = -1)
  timeout = (t.nil? or t == -1) ? @timeout : t

  @conn = Rex::Socket::Tcp.create(
    'PeerHost'   => @host,
    'PeerPort'   => @port.to_i,
    'Context'    => @context,
    'SSL'        => @ssl,
    'SSLVersion' => @ssl_version,
    'Timeout'    => timeout,
    'Comm'       => @comm
  )

  nil
end

#recv_raw(timeout: @timeout) ⇒ String

Receive raw data from the remote peer.

Parameters:

  • timeout (Float) (defaults to: @timeout)

    The timeout to use for this receive operation. Defaults to the instance timeout.

Returns:

  • (String)

    The received data.

Raises:

  • (Rex::TimeoutError)

    Raised when all of the data was not received within the timeout.



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/rex/proto/thrift/client.rb', line 76

def recv_raw(timeout: @timeout)
  remaining = timeout
  frame_size, elapsed_time = Rex::Stopwatch.elapsed_time do
    @conn.get_once(4, remaining)
  end
  remaining -= elapsed_time
  if frame_size.nil? || frame_size.length < 4
    raise Rex::TimeoutError, 'Failed to read the response data length due to timeout.'
  end

  frame_size = frame_size.unpack1('N')
  body = ''
  while (body.size < frame_size) && remaining > 0
    chunk, elapsed_time = Rex::Stopwatch.elapsed_time do
      @conn.read(frame_size - body.size, remaining)
    end
    remaining -= elapsed_time
    body << chunk
  end

  unless body.size == (frame_size)
    if remaining <= 0
      raise Rex::TimeoutError, 'Failed to read the response data due to timeout.'
    end

    raise Error::InvalidFrameError.new
  end

  body
end

#send_raw(data) ⇒ Object

Send raw data to the remote peer.

Parameters:

  • data (String)

    The data to send.



67
68
69
# File 'lib/rex/proto/thrift/client.rb', line 67

def send_raw(data)
  @conn.put([data.length].pack('N') + data)
end