Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions command.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,11 @@ func (c *Command) ExitCode() int {
return c.exitCode
}

// Error returns command execution error if any
func (c *Command) Error() error {
return c.err
}

// Wait function will block the current goroutine until the remote command terminates.
func (c *Command) Wait() {
// block until finished
Expand Down
34 changes: 34 additions & 0 deletions fixture_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,40 @@ var (
</s:Body>
</s:Envelope>`

createShellResponseWithError = `<s:Envelope xml:lang="en-US"
xmlns:s="http://www.w3.org/2003/05/soap-envelope"
xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing"
xmlns:x="http://schemas.xmlsoap.org/ws/2004/09/transfer"
xmlns:e="http://schemas.xmlsoap.org/ws/2004/08/eventing"
xmlns:n="http://schemas.xmlsoap.org/ws/2004/09/enumeration"
xmlns:w="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd"
xmlns:p="http://schemas.microsoft.com/wbem/wsman/1/wsman.xsd">
<s:Header>
<a:Action>http://schemas.dmtf.org/wbem/wsman/1/wsman/fault</a:Action>
<a:MessageID>uuid:0A888267-33ED-4F08-98E6-DDEBDE2067DE</a:MessageID>
<a:To>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:To>
<a:RelatesTo>uuid:1893210b-91a7-45dc-aa90-b0c42e0dd740</a:RelatesTo>
</s:Header>
<s:Body>
<s:Fault>
<s:Code>
<s:Value>s:Receiver</s:Value>
<s:Subcode><s:Value>w:InternalError</s:Value></s:Subcode>
</s:Code>
<s:Reason>
<s:Text xml:lang="en-US">Illegal operation attempted on a registry key that has been marked for deletion. </s:Text>
</s:Reason>
<s:Detail>
<f:WSManFault xmlns:f="http://schemas.microsoft.com/wbem/wsman/1/wsmanfault" Code="2147943418" Machine="34.134.189.249">
<f:Message>
<f:ProviderFault provider="Shell cmd plugin" path="%!s(MISSING)ystemroot%!\(MISSING)system32\winrscmd.dll">Illegal operation attempted on a registry key that has been marked for deletion. </f:ProviderFault>
</f:Message>
</f:WSManFault>
</s:Detail>
</s:Fault>
</s:Body>
</s:Envelope>`

executeCommandResponse = `<s:Envelope xml:lang="en-US" xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:x="http://schemas.xmlsoap.org/ws/2004/09/transfer" xmlns:w="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd" xmlns:rsp="http://schemas.microsoft.com/wbem/wsman/1/windows/shell" xmlns:p="http://schemas.microsoft.com/wbem/wsman/1/wsman.xsd"><s:Header><a:Action>http://schemas.microsoft.com/wbem/wsman/1/windows/shell/CommandResponse</a:Action><a:MessageID>uuid:D9E108AA-E32B-45E3-8601-E9C70999D3BA</a:MessageID><a:To>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:To><a:RelatesTo>uuid:F530804C-6D02-4FA9-AE78-1997750594BA</a:RelatesTo></s:Header><s:Body><rsp:CommandResponse><rsp:CommandId>1A6DEE6B-EC68-4DD6-87E9-030C0048ECC4</rsp:CommandId></rsp:CommandResponse></s:Body></s:Envelope>`

executeCommandResponseWithError = `<s:Envelope xml:lang="en-US" xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:x="http://schemas.xmlsoap.org/ws/2004/09/transfer" xmlns:e="http://schemas.xmlsoap.org/ws/2004/08/eventing" xmlns:n="http://schemas.xmlsoap.org/ws/2004/09/enumeration" xmlns:w="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd" xmlns:p="http://schemas.microsoft.com/wbem/wsman/1/wsman.xsd"><s:Header><a:Action>http://schemas.dmtf.org/wbem/wsman/1/wsman/fault</a:Action><a:MessageID>uuid:C85E435B-712E-45E8-AC64-C53F9FEE7164</a:MessageID><a:To>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:To><a:RelatesTo>uuid:23d79ab4-f145-4ac5-baa3-084d67265a24</a:RelatesTo></s:Header><s:Body><s:Fault><s:Code><s:Value>s:Receiver</s:Value><s:Subcode><s:Value>w:InternalError</s:Value></s:Subcode></s:Code><s:Reason><s:Text xml:lang="">The filename or extension is too long. </s:Text></s:Reason><s:Detail><f:WSManFault xmlns:f="http://schemas.microsoft.com/wbem/wsman/1/wsmanfault" Code="2147942606" Machine="52.12.219.136"><f:Message><f:ProviderFault provider="Shell cmd plugin" path="%systemroot%\system32\winrscmd.dll">The filename or extension is too long. </f:ProviderFault></f:Message></f:WSManFault></s:Detail></s:Fault></s:Body></s:Envelope>`
Expand Down
53 changes: 28 additions & 25 deletions response.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,45 +66,48 @@ func xPath(node tree.Node, xpath string) (tree.NodeSet, error) {
return nodes, nil
}

// ParseOpenShellResponse ParseOpenShellResponse
func ParseOpenShellResponse(response string) (string, error) {
doc, err := xmltree.ParseXML(strings.NewReader(response))
if err != nil {
return "", err
}
return first(doc, "//w:Selector[@Name='ShellId']")
func newExecuteCommandError(response string, format string, args ...interface{}) *ExecuteCommandError {
return &ExecuteCommandError{fmt.Errorf(format, args...), response}
}

// ParseExecuteCommandResponse ParseExecuteCommandResponse
func ParseExecuteCommandResponse(response string) (commandId string, err error) {
defer func() {
if err != nil {
err = &ExecuteCommandError{Inner: err, Body: response}
}
}()

func parseResponse(response, expectedAction, idXPath string) (string, error) {
doc, err := xmltree.ParseXML(strings.NewReader(response))
if err != nil {
return "", fmt.Errorf("parsing xml response: %w", err)
return "", newExecuteCommandError(response, "parsing xml response: %w", err)
}

action, err := first(doc, "//a:Action")
if err != nil {
return "", fmt.Errorf("getting response action: %w", err)
return "", newExecuteCommandError(response, "getting response action: %w", err)
}

switch action {
case "http://schemas.microsoft.com/wbem/wsman/1/windows/shell/CommandResponse":
commandId, err = first(doc, "//rsp:CommandId")
if action == "http://schemas.dmtf.org/wbem/wsman/1/wsman/fault" {
return "", newExecuteCommandError(response, "received error response")
}
if action == expectedAction {
id, err := first(doc, idXPath)
if err != nil {
return "", fmt.Errorf("finding command id: %w", err)
return "", newExecuteCommandError(response, "finding %v: %w", idXPath, err)
}
return id, nil
}
return "", newExecuteCommandError(response, "unsupported action: %v", action)
}

return commandId, nil
func ParseOpenShellResponse(response string) (string, error) {
return parseResponse(
response,
"http://schemas.xmlsoap.org/ws/2004/09/transfer/CreateResponse",
"//rsp:ShellId",
)
}

default:
return "", fmt.Errorf("unsupported action: %v", action)
}
func ParseExecuteCommandResponse(response string) (string, error) {
return parseResponse(
response,
"http://schemas.microsoft.com/wbem/wsman/1/windows/shell/CommandResponse",
"//rsp:CommandId",
)
}

// ParseSlurpOutputErrResponse ParseSlurpOutputErrResponse
Expand Down
14 changes: 14 additions & 0 deletions response_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,20 @@ func (s *WinRMSuite) TestOpenShellResponse(c *C) {
c.Assert("67A74734-DD32-4F10-89DE-49A060483810", Equals, shellID)
}

func (s *WinRMSuite) TestOpenShellResponseError(c *C) {
response := createShellResponseWithError
shellId, err := ParseOpenShellResponse(response)
if err == nil {
c.Fatal("expected error")
}
c.Assert(shellId, Equals, "")

var execCmdRespErr *ExecuteCommandError
if !errors.As(err, &execCmdRespErr) {
c.Fatal("expected err to be of type ExecuteCommandError")
}
}

func (s *WinRMSuite) TestExecuteCommandResponse(c *C) {
response := executeCommandResponse

Expand Down