Skip to content
Open
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
79 changes: 79 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,85 @@ functions of the API will not cancel the HTTP requests themselves, it will
rather cause a running command to be aborted on the remote machine via a call to
`command.Stop()`.

For setting winrm protocol options ([Remote Shell Options](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-wsmv/593f3ed0-0c7a-4158-a4be-0b429b597e31#Appendix_A_Target_110) and [Command Options](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-wsmv/c793e333-c409-43c6-a2eb-6ae2489c7ef4#Appendix_A_Target_117))

```go
package main
import (
"github.com/masterzen/winrm"
"fmt"
"os"
)

endpoint := winrm.NewEndpoint("localhost", 5985, false, false, nil, nil, nil, 0)

params := DefaultParameters
params.RequestOptions["WINRS_NOPROFILE"] = "TRUE"
params.RequestOptions["WINRS_CONSOLEMODE_STDIN"] = "FALSE"
params.RequestOptions["WINRS_SKIP_CMD_SHELL"] = "TRUE"

client, err := NewClientWithParameters(endpoint, "test", "test", params)
if err != nil {
panic(err)
}

_, err := client.RunWithInput("ipconfig", os.Stdout, os.Stderr, os.Stdin)
if err != nil {
panic(err)
}

```

To get command output as a byte array, use ExecuteDirect on a shell
NB: This also has a convenience function to write command strings

```go
package main

import (
"github.com/masterzen/winrm"
"fmt"
"bytes"
"log"
"os"
)

endpoint := winrm.NewEndpoint("localhost", 5985, false, false,nil, nil, nil, 0)
client , err := winrm.NewClient(endpoint, "Administrator", "secret")
if err != nil {
panic(err)
}
shell, err := client.CreateShell()
if err != nil {
panic(err)
}
var cmd *winrm.Command
cmd, err = shell.ExecuteDirect("powershell.exe", "-NonInteractive", "-NoProfile", "-Command", "-")
if err != nil {
panic(err)
}
err = cmd.SendCommand("$PSVersionTable.PSVersion | ConvertTo-Json")
if err != nil {
panic(err)
}
stdin, stderr, finished, exitcode, err := cmd.ReadOutput()
if err != nil {
panic(err)
}
log.Printf("Stdout: '%v'", string(stdout))
log.Printf("Stderr: '%v'", string(stderr))

var result map[string]int
err = json.Unmarshal(stdout, &result)
if err != nil {
panic(err)
}
log.Printf("Version: %d.%d-%d", result["Major"], result["Minor"], result["Build"])


shell.Close()
```

## Developing on WinRM

If you wish to work on `winrm` itself, you'll first need [Go](http://golang.org)
Expand Down
73 changes: 73 additions & 0 deletions directcommand.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package winrm

import (
"bytes"
"fmt"
"strings"
)

type DirectCommand struct {
client *Client
shell *Shell
id string
}

func (c *DirectCommand) SendCommand(format string, arguments ...interface{}) error {
return c.SendInput([]byte(fmt.Sprintf(format, arguments...)+"\n"), false)
}

func (c *DirectCommand) SendInput(data []byte, eof bool) error {
request := NewSendInputRequest(c.client.url, c.shell.id, c.id, data, eof, &c.client.Parameters)
defer request.Free()

_, err := c.client.sendRequest(request)
return err
}

func (c *DirectCommand) ReadOutput() ([]byte, []byte, bool, int, error) {
request := NewGetOutputRequest(c.client.url, c.shell.id, c.id, "stdout stderr", &c.client.Parameters)
defer request.Free()
response, err := c.client.sendRequest(request)
if err != nil {
if strings.Contains(err.Error(), "OperationTimeout") {
return nil, nil, false, 0, err
}
return nil, nil, true, -1, err
}
var exitCode int
var stdout, stderr bytes.Buffer
finished, exitCode, err := ParseSlurpOutputErrResponse(response, &stdout, &stderr)
return stdout.Bytes(), stderr.Bytes(), finished, exitCode, nil
}

func (c *DirectCommand) Close() error {
if c.shell == nil {
return nil
}
defer c.shell.Close()
if c.id != "" || c.client != nil {
return nil
}
request := NewSignalRequest(c.client.url, c.shell.id, c.id, &c.client.Parameters)
defer request.Free()

_, err := c.client.sendRequest(request)
return err
}

func (s *Shell) ExecuteDirect(command string, arguments ...string) (*DirectCommand, error) {
request := NewExecuteCommandRequest(s.client.url, s.id, command, arguments, &s.client.Parameters)
defer request.Free()

response, err := s.client.sendRequest(request)
if err != nil {
return nil, err
}

commandID, err := ParseExecuteCommandResponse(response)
if err != nil {
return nil, err
}

return &DirectCommand{s.client, s, commandID}, nil
}
7 changes: 7 additions & 0 deletions parameters.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ type Parameters struct {
Timeout string
Locale string
EnvelopeSize int
RequestOptions map[string]string
TransportDecorator func() Transporter
Dial func(network, addr string) (net.Conn, error)
}
Expand All @@ -23,5 +24,11 @@ func NewParameters(timeout, locale string, envelopeSize int) *Parameters {
Timeout: timeout,
Locale: locale,
EnvelopeSize: envelopeSize,
RequestOptions: map[string]string{
"WINRS_NOPROFILE": "FALSE",
"WINRS_CODEPAGE": "65001",
"WINRS_CONSOLEMODE_STDIN": "TRUE",
"WINRS_SKIP_CMD_SHELL": "FALSE",
},
}
}
8 changes: 4 additions & 4 deletions request.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ func NewOpenShellRequest(uri string, params *Parameters) *soap.SoapMessage {
defaultHeaders(message, uri, params).
Action("http://schemas.xmlsoap.org/ws/2004/09/transfer/Create").
ResourceURI("http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd").
AddOption(soap.NewHeaderOption("WINRS_NOPROFILE", "FALSE")).
AddOption(soap.NewHeaderOption("WINRS_CODEPAGE", "65001")).
AddOption(soap.NewHeaderOption("WINRS_NOPROFILE", params.RequestOptions["WINRS_NOPROFILE"])).
AddOption(soap.NewHeaderOption("WINRS_CODEPAGE", params.RequestOptions["WINRS_CODEPAGE"])).
Build()

body := message.CreateBodyElement("Shell", soap.DOM_NS_WIN_SHELL)
Expand Down Expand Up @@ -73,8 +73,8 @@ func NewExecuteCommandRequest(uri, shellID, command string, arguments []string,
Action("http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Command").
ResourceURI("http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd").
ShellId(shellID).
AddOption(soap.NewHeaderOption("WINRS_CONSOLEMODE_STDIN", "TRUE")).
AddOption(soap.NewHeaderOption("WINRS_SKIP_CMD_SHELL", "FALSE")).
AddOption(soap.NewHeaderOption("WINRS_CONSOLEMODE_STDIN", params.RequestOptions["WINRS_CONSOLEMODE_STDIN"])).
AddOption(soap.NewHeaderOption("WINRS_SKIP_CMD_SHELL", params.RequestOptions["WINRS_SKIP_CMD_SHELL"])).
Build()

body := message.CreateBodyElement("CommandLine", soap.DOM_NS_WIN_SHELL)
Expand Down