Class: PSRP::WSMV::CommandOutputProcessor

Inherits:
Object
  • Object
show all
Defined in:
lib/wsmv/command_output_processor.rb

Overview

Class to handle getting all the output of a command until it completes

Constant Summary collapse

INPUT_REQUIRED =
[
  PSRP::Message::MESSAGE_TYPES[:PIPELINE_HOST_CALL],
  PSRP::Message::MESSAGE_TYPES[:RUNSPACEPOOL_HOST_CALL]
]

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(connection_opts, transport, out_opts = {}) ⇒ CommandOutputProcessor

Creates a new command output processor

Parameters:

  • connection_opts (ConnectionOpts)

    The WinRM connection options

  • transport (HttpTransport)

    The WinRM SOAP transport

  • out_opts (Hash) (defaults to: {})

    Additional output options



37
38
39
40
41
42
43
44
45
# File 'lib/wsmv/command_output_processor.rb', line 37

def initialize(connection_opts, transport, out_opts = {})
  @connection_opts = connection_opts
  @transport = transport
  @out_opts = out_opts
  @has_error = false
  @command_done = false
  @input_required = false
  @msgs = {}
end

Instance Attribute Details

#msgsObject (readonly)

Returns the value of attribute msgs.



26
27
28
# File 'lib/wsmv/command_output_processor.rb', line 26

def msgs
  @msgs
end

Instance Method Details

#command_done?Boolean

Returns:

  • (Boolean)


139
140
141
# File 'lib/wsmv/command_output_processor.rb', line 139

def command_done?
  @command_done
end

#command_output(shell_id, command_id, reset = false) ⇒ Object

Gets the command output from the remote shell

Parameters:

  • shell_id (UUID)

    The remote shell id running the command

  • command_id (UUID)

    The command id to get output for

  • block

    Optional callback for any output



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/wsmv/command_output_processor.rb', line 51

def command_output(shell_id, command_id, reset = false)
  if reset == true
    @has_error = false
    @command_done = false
    @input_required = false
  end

  out_message = command_output_message(shell_id, command_id)
  resp_doc = send_get_output_message(out_message)
  streams(resp_doc)
  @command_done = REXML::XPath.match(
    resp_doc,
    "//*[@State='http://schemas.microsoft.com/wbem/wsman/1/windows/shell/" \
    "CommandState/Done']").any?
  resp_doc
end

#command_output_message(shell_id, command_id) ⇒ Object



68
69
70
71
72
73
74
75
76
# File 'lib/wsmv/command_output_processor.rb', line 68

def command_output_message(shell_id, command_id)
  cmd_out_opts = {
    shell_id: shell_id
  }.merge(@out_opts)
  if command_id
    cmd_out_opts[:command_id] = command_id
  end
  PSRP::WSMV::ReceiveOutput.new(@connection_opts, cmd_out_opts).build
end

#decode(raw_output) ⇒ String

Returns The decoded output.

Parameters:

  • raw_output (String)

    The raw encoded output

Returns:

  • (String)

    The decoded output



147
148
149
150
# File 'lib/wsmv/command_output_processor.rb', line 147

def decode(raw_output)
  # TODO: Add better decoding based on MS-PSRP 2.2.5
  PSRP::MessageDecoder.new(raw_output)
end

#defragmentedObject

Defragment Messages



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/wsmv/command_output_processor.rb', line 114

def defragmented
  datas = {}
  @msgs.each do |msg_id, msg_list|
    msg_list.sort do |a, b|
      a.fragment_id <=> b.fragment_id
    end
    unfragmented = msg_list.inject('') do |data, msg|
      data += msg.data
    end
    datas[msg_id] = {
      message_type: msg_list[0].message_type,
      data: unfragmented,
    }
  end
  datas
end

#exit_code(resp_doc) ⇒ Object



93
94
95
# File 'lib/wsmv/command_output_processor.rb', line 93

def exit_code(resp_doc)
  REXML::XPath.first(resp_doc, "//#{NS_WIN_SHELL}:ExitCode").text.to_i
end

#has_error?Boolean

Returns:

  • (Boolean)


131
132
133
# File 'lib/wsmv/command_output_processor.rb', line 131

def has_error?
  @has_error
end

#input_required?Boolean

Returns:

  • (Boolean)


135
136
137
# File 'lib/wsmv/command_output_processor.rb', line 135

def input_required?
  @input_required
end

#send_get_output_message(message) ⇒ Object



78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/wsmv/command_output_processor.rb', line 78

def send_get_output_message(message)
  resp_doc = @transport.send_request(message)
rescue PSRP::WSManFault => e
  # If no output is available before the wsman:OperationTimeout expires,
  # the server MUST return a WSManFault with the Code attribute equal to
  # 2150858793. When the client receives this fault, it SHOULD issue
  # another Receive request.
  # http://msdn.microsoft.com/en-us/library/cc251676.aspx
  if e.fault_code == '2150858793'
    retry
  else
    raise
  end
end

#streams(resp_doc) ⇒ Object



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/wsmv/command_output_processor.rb', line 97

def streams(resp_doc)
  REXML::XPath.match(resp_doc, "//#{PSRP::WSMV::NS_WIN_SHELL}:Stream").each do |n|
    next if n.text.nil? || n.text.empty?
    msg = decode(n.text)
    if not @msgs.has_key? msg.message_id
      @msgs[msg.message_id] = []
    end
    @msgs[msg.message_id].push(msg)
    if INPUT_REQUIRED.include? msg.message_type
      @input_required = true
    elsif msg.message_type == PSRP::Message::MESSAGE_TYPES[:ERROR_RECORD]
      @has_error = true
    end
  end
end