├── README.md
├── protocol.go
├── protocol_test.go
├── response.go
├── response_test.go
├── start_test.go
├── transport.go
├── transport_test.go
├── util.go
├── util_test.go
└── xml.go
/README.md:
--------------------------------------------------------------------------------
1 | # WinRM
2 |
3 | This is a Go module for interacting with the Windows Remote Management system (WinRM)
4 |
5 |
6 | Here is a quick usage example with Basic Authentication:
7 |
8 | ```go
9 | package main
10 |
11 |
12 | // We use a forked version of net/http and crypto/tls
13 | // because the standard libs do now support renegotiation
14 |
15 | import (
16 | "fmt"
17 | "launchpad.net/gwacl/fork/http"
18 | "github.com/trobert2/winrm"
19 | )
20 |
21 |
22 | func main(){
23 | p := winrm.ShellParams{}
24 | Soap := winrm.SoapRequest{
25 | Endpoint:"https://192.168.100.154:5986/wsman",
26 | Username:"Administrator",
27 | Passwd:"Passw0rd",
28 | AuthType:"BasicAuth",
29 | HttpInsecure:true,
30 | HttpClient: &http.Client{},
31 | }
32 | cmdParam := winrm.CmdParams{
33 | Cmd: "dir",
34 | Args: "c:\\ /A",
35 | }
36 |
37 | v := &winrm.Envelope{}
38 | strdout, stderr, ret_code, _ := v.RunCommand(p, cmdParam, Soap)
39 | fmt.Printf("Output:%s\nError: %s\nCode:%v\n", strdout, stderr, ret_code)
40 | }
41 | ```
42 |
43 |
44 | And one with Client side certificates:
45 |
46 | ```Go
47 | package main
48 |
49 | // We use a forked version of net/http and crypto/tls
50 | // because the standard libs do now support renegotiation
51 |
52 | import (
53 | "fmt"
54 | "launchpad.net/gwacl/fork/http"
55 | "github.com/trobert2/winrm"
56 | )
57 |
58 |
59 | func main(){
60 | p := winrm.ShellParams{}
61 | Soap := winrm.SoapRequest{
62 | Endpoint:"https://192.168.100.155:5986/wsman",
63 | AuthType:"CertAuth",
64 | HttpInsecure:true,
65 | CertAuth: &winrm.CertificateCredentials{
66 | Cert: "/home/ubuntu/maas/SSL/certs/testing.pfx.pem",
67 | Key: "/home/ubuntu/maas/SSL/certs/dec.key",
68 | },
69 | HttpClient: &http.Client{},
70 | }
71 | cmdParam := winrm.CmdParams{
72 | Cmd: "dir",
73 | Args: "c:\\ /A",
74 | }
75 |
76 | v := &winrm.Envelope{}
77 | strdout, stderr, ret_code, err := v.RunCommand(p, cmdParam, Soap)
78 | fmt.Printf("Output:%s\nError: %s\nCode:%v\nERROR:%s\n", strdout, stderr, ret_code, err)
79 | }
80 | ```
--------------------------------------------------------------------------------
/protocol.go:
--------------------------------------------------------------------------------
1 | package winrm
2 |
3 | import (
4 | "encoding/xml"
5 | // "io/ioutil"
6 | "errors"
7 | "fmt"
8 | )
9 |
10 | type Envelope struct {
11 | XMLName xml.Name `xml:"env:Envelope"`
12 | EnvelopeAttrs
13 | Headers *Headers `xml:"env:Header,omitempty"`
14 | Body *BodyStruct `xml:"env:Body,omitempty"`
15 | }
16 |
17 | type ShellParams struct {
18 | IStream string
19 | OStream string
20 | WorkingDir string
21 | EnvVars *Environment
22 | NoProfile bool
23 | Codepage string
24 | }
25 |
26 | type HeaderParams struct {
27 | ResourceURI string
28 | Action string
29 | ShellID string
30 | MessageID string
31 | }
32 |
33 | type CmdParams struct {
34 | ShellID string
35 | Cmd string
36 | Args string
37 | Timeout string
38 | }
39 |
40 | func (envelope *Envelope) RunCommand(shellParams ShellParams, params CmdParams, soap SoapRequest) (string, string, int, error) {
41 | shell, err_shell := envelope.GetShell(shellParams, soap)
42 | if err_shell != nil {
43 | return "", "", 0, err_shell
44 | }
45 | params.ShellID = shell
46 | commID, commErr := envelope.SendCommand(params, soap)
47 | if commErr != nil {
48 | return "", "", 0, commErr
49 | }
50 | strdout, stderr, ret_code, err := envelope.GetCommandOutput(params.ShellID, commID, soap)
51 | if err != nil {
52 | return "", "", 0, err
53 | }
54 | err_clean := envelope.CleanupShell(params.ShellID, commID, soap)
55 | if err_clean != nil {
56 | return "", "", 0, err
57 | }
58 | err_close := envelope.CloseShell(params.ShellID, soap)
59 | if err_close != nil {
60 | return "", "", 0, err
61 | }
62 | return strdout, stderr, ret_code, err
63 | }
64 |
65 | // Generate SOAP envelope headers
66 | func (envelope *Envelope) GetSoapHeaders(params HeaderParams) error {
67 | envelope.Headers = &Headers{
68 | OperationTimeout: "PT60S",
69 | To: "http://windows-host:5985/wsman",
70 | ReplyTo: &ReplyAddress{
71 | ValueMustUnderstand{
72 | Value: "http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous",
73 | Attr: "true",
74 | },
75 | },
76 | DataLocale: &LocaleAttr{
77 | MustUnderstand: "true",
78 | Lang: "en-US",
79 | },
80 | Locale: &LocaleAttr{
81 | MustUnderstand: "true",
82 | Lang: "en-US",
83 | },
84 | MaxEnvelopeSize: &ValueMustUnderstand{
85 | Value: "153600",
86 | Attr: "true",
87 | },
88 | }
89 |
90 | if params.ResourceURI != "" {
91 | envelope.Headers.ResourceURI = &ValueMustUnderstand{
92 | Value: params.ResourceURI,
93 | Attr: "true",
94 | }
95 | }
96 |
97 | if params.Action != "" {
98 | envelope.Headers.Action = &ValueMustUnderstand{
99 | Value: params.Action,
100 | Attr: "true",
101 | }
102 | }
103 |
104 | if params.ShellID != "" {
105 | envelope.Headers.SelectorSet = &Selector{
106 | ValueName{
107 | Value: params.ShellID,
108 | Attr: "ShellId",
109 | },
110 | }
111 | }
112 |
113 | if params.MessageID == "" {
114 | uuid, err := Uuid()
115 | if err != nil {
116 | return err
117 | }
118 | params.MessageID = fmt.Sprintf("uuid:%s", uuid)
119 | }
120 | envelope.Headers.MessageID = params.MessageID
121 | return nil
122 | }
123 |
124 | // TODO: Do a soap request and return ShellID
125 | func (envelope *Envelope) GetShell(params ShellParams, soap SoapRequest) (string, error) {
126 | HeadParams := HeaderParams{
127 | ResourceURI: "http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd",
128 | Action: "http://schemas.xmlsoap.org/ws/2004/09/transfer/Create",
129 | }
130 | // envelope := Envelope{}
131 | envelope.GetSoapHeaders(HeadParams)
132 |
133 | if params.Codepage == "" {
134 | params.Codepage = "437"
135 | }
136 | envelope.Headers.OptionSet = &OptionSet{
137 | []ValueName{
138 | ValueName{Attr: "WINRS_NOPROFILE", Value: "FALSE"},
139 | ValueName{Attr: "WINRS_CODEPAGE", Value: params.Codepage},
140 | },
141 | }
142 | var Body BodyStruct = BodyStruct{}
143 | var ShellVars Shell = Shell{}
144 |
145 | if params.IStream == "" {
146 | ShellVars.InputStreams = "stdin"
147 | } else {
148 | ShellVars.InputStreams = params.IStream
149 | }
150 |
151 | if params.OStream == "" {
152 | ShellVars.OutputStreams = "stdout stderr"
153 | } else {
154 | ShellVars.OutputStreams = params.OStream
155 | }
156 |
157 | if params.EnvVars != nil {
158 | ShellVars.Environment = params.EnvVars
159 | }
160 |
161 | // send request to WinRm
162 | Body.Shell = &ShellVars
163 | envelope.Body = &Body
164 | envelope.EnvelopeAttrs = Namespaces
165 |
166 | // response from WinRM
167 | resp, err := soap.SendMessage(envelope)
168 | if err != nil {
169 | return "", err
170 | }
171 | defer resp.Body.Close()
172 |
173 | respObj, err := GetObjectFromXML(resp.Body)
174 | //fmt.Printf("%v\n", respObj)
175 | if err != nil {
176 | return "", err
177 | }
178 | shellID := respObj.Body.Shell.ShellId
179 |
180 | return shellID, err
181 | }
182 |
183 | func (envelope *Envelope) SendCommand(params CmdParams, soap SoapRequest) (string, error) {
184 | HeadParams := HeaderParams{
185 | ResourceURI: "http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd",
186 | Action: "http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Command",
187 | }
188 | if params.ShellID == "" {
189 | return "", errors.New("Invalid ShellId")
190 | }
191 | HeadParams.ShellID = params.ShellID
192 | envelope.GetSoapHeaders(HeadParams)
193 |
194 | envelope.Headers.OptionSet = &OptionSet{
195 | []ValueName{
196 | ValueName{Attr: "WINRS_CONSOLEMODE_STDIN", Value: "TRUE"},
197 | ValueName{Attr: "WINRS_SKIP_CMD_SHELL", Value: "FALSE"},
198 | },
199 | }
200 |
201 | if params.Timeout == "" {
202 | envelope.Headers.OperationTimeout = "PT3600S"
203 | } else {
204 | envelope.Headers.OperationTimeout = params.Timeout
205 | }
206 | // var Body BodyStruct = BodyStruct{}
207 |
208 | envelope.EnvelopeAttrs = Namespaces
209 | if params.Cmd == "" {
210 | return "", errors.New("Invalid command")
211 | }
212 | envelope.Body = &BodyStruct{
213 | CommandLine: &Command{
214 | Command: params.Cmd,
215 | },
216 | }
217 |
218 | if params.Args != "" {
219 | envelope.Body.CommandLine.Arguments = params.Args
220 | }
221 |
222 | // fmt.Printf("%s\n", output)
223 | resp, err := soap.SendMessage(envelope)
224 | if err != nil {
225 | return "", err
226 | }
227 | defer resp.Body.Close()
228 |
229 | respObj, err := GetObjectFromXML(resp.Body)
230 | if err != nil {
231 | return "", err
232 | }
233 | // contents, _ := ioutil.ReadAll(resp.Body)
234 | // fmt.Printf("REQ:%s\n\nRESP:%s\n\nSHELL:%s\n\n", output, contents, shellID)
235 | return respObj.Body.CommandResponse.CommandId, nil
236 | }
237 |
238 | func (envelope *Envelope) GetCommandOutput(shellID, commandID string, soap SoapRequest) (string, string, int, error) {
239 | HeadParams := HeaderParams{
240 | ResourceURI: "http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd",
241 | Action: "http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Receive",
242 | ShellID: shellID,
243 | }
244 | envelope.GetSoapHeaders(HeadParams)
245 | envelope.EnvelopeAttrs = Namespaces
246 | envelope.Body = &BodyStruct{
247 | Receive: &Receive{
248 | DesiredStream: DesiredStreamProps{
249 | Value: "stdout stderr",
250 | Attr: commandID,
251 | },
252 | },
253 | }
254 |
255 | resp, err := soap.SendMessage(envelope)
256 | if err != nil {
257 | return "", "", 0, err
258 | }
259 | defer resp.Body.Close()
260 |
261 | stdout, stderr, retCode, err := ParseCommandOutput(resp.Body)
262 | // fmt.Printf("%s\n", output)
263 | return stdout, stderr, retCode, nil
264 | }
265 |
266 | func (envelope *Envelope) CleanupShell(shellID, commandID string, soap SoapRequest) error {
267 | HeadParams := HeaderParams{
268 | ResourceURI: "http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd",
269 | Action: "http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Signal",
270 | ShellID: shellID,
271 | }
272 | envelope.GetSoapHeaders(HeadParams)
273 | envelope.EnvelopeAttrs = Namespaces
274 | sig := Signal{
275 | Attr: commandID,
276 | Code: "http://schemas.microsoft.com/wbem/wsman/1/windows/shell/signal/terminate",
277 | }
278 | envelope.Body = &BodyStruct{
279 | Signal: &sig,
280 | }
281 |
282 | resp, err := soap.SendMessage(envelope)
283 | if err != nil {
284 | return err
285 | }
286 | defer resp.Body.Close()
287 | // contents, err2 := ioutil.ReadAll(resp.Body)
288 | // fmt.Printf("%s --> %s", contents, err2)
289 | return nil
290 | }
291 |
292 | func (envelope *Envelope) CloseShell(shellID string, soap SoapRequest) error {
293 | HeadParams := HeaderParams{
294 | ResourceURI: "http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd",
295 | Action: "http://schemas.xmlsoap.org/ws/2004/09/transfer/Delete",
296 | ShellID: shellID,
297 | }
298 | var Body BodyStruct = BodyStruct{}
299 |
300 | envelope.EnvelopeAttrs = Namespaces
301 | envelope.GetSoapHeaders(HeadParams)
302 | envelope.Body = &Body
303 |
304 | resp, err := soap.SendMessage(envelope)
305 | // contents, err := ioutil.ReadAll(resp.Body)
306 | // fmt.Printf("REQ:%s\n\nRESP:%s\n\nSHELL:%s\n\n", output, contents, shellID)
307 | if err != nil {
308 | return err
309 | }
310 | defer resp.Body.Close()
311 | return nil
312 | }
313 |
--------------------------------------------------------------------------------
/protocol_test.go:
--------------------------------------------------------------------------------
1 | package winrm
2 |
3 | import gc "launchpad.net/gocheck"
4 |
5 | type ProtocolSuite struct{}
6 |
7 | var _ = gc.Suite(ProtocolSuite{})
8 |
9 | // tests adding ONLY ResourceURI to Envelope in GetSoapHeaders
10 | func (ProtocolSuite) TestGetSoapHeadersResourceURI(c *gc.C) {
11 | env := Envelope{}
12 | params := HeaderParams{ResourceURI: "something"}
13 | exp := &ValueMustUnderstand{params.ResourceURI, "true"}
14 |
15 | env.GetSoapHeaders(params)
16 | c.Assert(env.Headers.ResourceURI, gc.DeepEquals, exp)
17 | c.Assert(env.Headers.MessageID, gc.Not(gc.Equals), "")
18 | c.Assert(env.Headers.SelectorSet, gc.IsNil)
19 | c.Assert(env.Headers.Action, gc.IsNil)
20 | }
21 |
22 | // tests adding ONLY Action to Envelope in GetSoapHeaders
23 | func (ProtocolSuite) TestGetSoapHeadersAction(c *gc.C) {
24 | env := Envelope{}
25 | params := HeaderParams{Action: "yes"}
26 | exp := &ValueMustUnderstand{params.Action, "true"}
27 |
28 | env.GetSoapHeaders(params)
29 | c.Assert(env.Headers.Action, gc.DeepEquals, exp)
30 | c.Assert(env.Headers.MessageID, gc.Not(gc.Equals), "")
31 | c.Assert(env.Headers.ResourceURI, gc.IsNil)
32 | c.Assert(env.Headers.SelectorSet, gc.IsNil)
33 | }
34 |
35 | // tests adding ONLY ShellID to Envelope in GetSoapHeaders
36 | func (ProtocolSuite) TestGetSoapHeadersShellID(c *gc.C) {
37 | env := Envelope{}
38 | params := HeaderParams{ShellID: "Not power shell"}
39 | exp := &Selector{ValueName{params.ShellID, "ShellId"}}
40 |
41 | env.GetSoapHeaders(params)
42 | c.Assert(env.Headers.SelectorSet, gc.DeepEquals, exp)
43 | c.Assert(env.Headers.MessageID, gc.Not(gc.Equals), "")
44 | c.Assert(env.Headers.ResourceURI, gc.IsNil)
45 | c.Assert(env.Headers.Action, gc.IsNil)
46 | }
47 |
48 | // test adding ONLY MessageID to Envelope in GetSoapHeaders
49 | func (ProtocolSuite) TestGetSoapHeadersMessageID(c *gc.C) {
50 | env := Envelope{}
51 | params := HeaderParams{MessageID: "I am unique!"}
52 |
53 | env.GetSoapHeaders(params)
54 | c.Assert(params.MessageID, gc.NotNil)
55 | c.Assert(env.Headers.MessageID, gc.Equals, params.MessageID)
56 | c.Assert(env.Headers.ResourceURI, gc.IsNil)
57 | c.Assert(env.Headers.SelectorSet, gc.IsNil)
58 | c.Assert(env.Headers.Action, gc.IsNil)
59 | }
60 |
61 | // tests if Envelope attributes are succesfully configured by GetShell
62 | func (ProtocolSuite) TestGetShellMakeEnvelope(c *gc.C) {
63 | req := SoapRequest{}
64 | env := Envelope{}
65 | params := ShellParams{EnvVars: &Environment{Variable: []EnvVariable{EnvVariable{Value: "Yes", Name: "Johann"}}}}
66 | expparams := ShellParams{IStream: "stdin", OStream: "stdout stderr", EnvVars: &Environment{[]EnvVariable{EnvVariable{Value: "Yes", Name: "Johann"}}}}
67 |
68 | _, _ = env.GetShell(params, req)
69 |
70 | // c.Assert(err, gc.IsNil)
71 | // c.Assert(shell, gc.NotNil)
72 | c.Assert(env.EnvelopeAttrs, gc.Equals, Namespaces)
73 | c.Assert(env.Body.Shell, gc.DeepEquals, &Shell{InputStreams: expparams.IStream, OutputStreams: expparams.OStream, Environment: expparams.EnvVars})
74 | }
75 |
76 | // tests if missing ShellID parameter is signaled by SendCommand
77 | func (ProtocolSuite) TestSendCommandNoShellId(c *gc.C) {
78 | env := Envelope{}
79 | params := CmdParams{}
80 | req := SoapRequest{}
81 |
82 | comId, err := env.SendCommand(params, req)
83 | c.Assert(comId, gc.Equals, "")
84 | c.Assert(err, gc.ErrorMatches, "Invalid ShellId")
85 | }
86 |
87 | // tests if Timeout parameter succesfully added to Envelope by SendCommand
88 | func (ProtocolSuite) TestSendCommandTimeoutSetting(c *gc.C) {
89 | env := Envelope{}
90 | params := CmdParams{Timeout: "Some timeout", ShellID: "Something"}
91 | req := SoapRequest{}
92 |
93 | _, _ = env.SendCommand(params, req)
94 | c.Assert(env.Headers.OperationTimeout, gc.Equals, params.Timeout)
95 | }
96 |
97 | // tests if default Timeout is set by SendCommand when none is provided
98 | func (ProtocolSuite) TestSendCommandDefaultTimeout(c *gc.C) {
99 | env := Envelope{}
100 | params := CmdParams{ShellID: "Something"}
101 | req := SoapRequest{}
102 |
103 | _, _ = env.SendCommand(params, req)
104 | c.Assert(env.Headers.OperationTimeout, gc.Equals, "PT3600S")
105 | }
106 |
107 | // tests if empty Command parameter succesfully signaled by SendCommand
108 | func (ProtocolSuite) TestSendCommandMissingCommand(c *gc.C) {
109 | env := Envelope{}
110 | params := CmdParams{ShellID: "Absolutely Something"}
111 | req := SoapRequest{}
112 |
113 | comId, err := env.SendCommand(params, req)
114 | c.Assert(comId, gc.Equals, "")
115 | c.Assert(err, gc.ErrorMatches, "Invalid command")
116 | }
117 |
118 | // tests if Args parameter succesfully added to envelope by SendCommand
119 | func (ProtocolSuite) TestSendCommandArgsPassing(c *gc.C) {
120 | env := Envelope{}
121 | params := CmdParams{ShellID: "Something", Args: "Pepsi is better that Coke", Cmd: "More Something"}
122 | req := SoapRequest{}
123 |
124 | _, _ = env.SendCommand(params, req)
125 | c.Assert(env.Body.CommandLine.Arguments, gc.Equals, params.Args)
126 | }
127 |
128 | // tests if Envelope attributes succesfully updated by GetCommandOutput
129 | func (ProtocolSuite) TestGetCommandOutputMakeEnvelope(c *gc.C) {
130 | env := Envelope{}
131 | req := SoapRequest{}
132 |
133 | expparams := HeaderParams{ResourceURI: "http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd", Action: "http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Recieve", ShellID: ""}
134 | exprec := Receive{DesiredStream: DesiredStreamProps{Value: "stdout stderr", Attr: ""}}
135 | expenv := Envelope{EnvelopeAttrs: Namespaces, Body: &BodyStruct{Receive: &exprec}}
136 | expenv.GetSoapHeaders(expparams)
137 |
138 | _, _, _, _ = env.GetCommandOutput("", "", req)
139 | c.Assert(env.Body, gc.DeepEquals, expenv.Body)
140 | c.Assert(env.EnvelopeAttrs, gc.Equals, expenv.EnvelopeAttrs)
141 | c.Assert(env.Headers.Locale, gc.DeepEquals, expenv.Headers.Locale)
142 | c.Assert(env.Headers.ReplyTo, gc.DeepEquals, expenv.Headers.ReplyTo)
143 | c.Assert(env.Headers.DataLocale, gc.DeepEquals, expenv.Headers.DataLocale)
144 | }
145 |
146 | // tests if Envelope attributes succesfully updated by CleanupShell
147 | func (ProtocolSuite) TestCleanupShellMakeEnvelope(c *gc.C) {
148 | env := Envelope{}
149 | req := SoapRequest{}
150 |
151 | expparams := HeaderParams{ResourceURI: "http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd", Action: "http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Signal", ShellID: ""}
152 | expsig := Signal{Attr: "", Code: "http://schemas.microsoft.com/wbem/wsman/1/windows/shell/signal/terminate"}
153 | expenv := Envelope{EnvelopeAttrs: Namespaces, Body: &BodyStruct{Signal: &expsig}}
154 | expenv.GetSoapHeaders(expparams)
155 |
156 | _ = env.CleanupShell("", "", req)
157 | c.Assert(env.Body, gc.DeepEquals, expenv.Body)
158 | c.Assert(env.EnvelopeAttrs, gc.Equals, expenv.EnvelopeAttrs)
159 | c.Assert(env.Headers.Locale, gc.DeepEquals, expenv.Headers.Locale)
160 | c.Assert(env.Headers.ReplyTo, gc.DeepEquals, expenv.Headers.ReplyTo)
161 | c.Assert(env.Headers.DataLocale, gc.DeepEquals, expenv.Headers.DataLocale)
162 | }
163 |
164 | // tests if Envelope attributes succesfully updated by CloseShell
165 | func (ProtocolSuite) TestCloseShellMakeEnvelope(c *gc.C) {
166 | env := Envelope{}
167 | req := SoapRequest{}
168 |
169 | expparams := HeaderParams{ResourceURI: "http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd", Action: "http://schemas.xmlsoap.org/ws/2004/09/transfer/Delete", ShellID: ""}
170 | expenv := Envelope{EnvelopeAttrs: Namespaces, Body: &BodyStruct{}}
171 | expenv.GetSoapHeaders(expparams)
172 |
173 | _ = env.CloseShell("", req)
174 | c.Assert(env.Body, gc.DeepEquals, expenv.Body)
175 | c.Assert(env.EnvelopeAttrs, gc.Equals, expenv.EnvelopeAttrs)
176 | c.Assert(env.Headers.Locale, gc.DeepEquals, expenv.Headers.Locale)
177 | c.Assert(env.Headers.ReplyTo, gc.DeepEquals, expenv.Headers.ReplyTo)
178 | c.Assert(env.Headers.DataLocale, gc.DeepEquals, expenv.Headers.DataLocale)
179 | }
180 |
--------------------------------------------------------------------------------
/response.go:
--------------------------------------------------------------------------------
1 | package winrm
2 |
3 | import (
4 | "bytes"
5 | "encoding/base64"
6 | "encoding/xml"
7 | "errors"
8 | "io"
9 | "io/ioutil"
10 | )
11 |
12 | type ResponseSelector struct {
13 | Value string `xml:",innerxml"`
14 | Name string `xml:"Name,attr"`
15 | }
16 |
17 | type ResponseSelectorSet struct {
18 | Selector *ResponseSelector `xml"w:Selector"`
19 | }
20 |
21 | type ReferenceParameters struct {
22 | ResourceURI string `xml"w:ResourceURI"`
23 | SelectorSet *ResponseSelectorSet `xml"w:SelectorSet"`
24 | }
25 |
26 | type ResourceCreated struct {
27 | Address string `xml"a:Address"`
28 | ReferenceParameters *ReferenceParameters `xml"a:ReferenceParameters"`
29 | }
30 |
31 | type ResponseHeader struct {
32 | Action string `xml"a:Action"`
33 | MessageID string `xml"a:MessageID"`
34 | To string `xml"a:To"`
35 | RelatesTo string `xml"a:RelatesTo"`
36 | }
37 |
38 | type CommandResponse struct {
39 | CommandId string `xml"rsp:CommandId"`
40 | }
41 |
42 | type ResponseShell struct {
43 | // xmlnsRsp string `xml:"xmlns:rsp,attr"`
44 | ShellId string `xml"rsp:ShellId`
45 | ResourceUri string `xml"rsp:ResourceUri"`
46 | Owner string `xml"rsp:Owner"`
47 | ClientIP string `xml"rsp:ClientIP"`
48 | IdleTimeOut string `xml"rsp:IdleTimeOut"`
49 | InputStreams string `xml"rsp:InputStreams"`
50 | OutputStreams string `xml"rsp:OutputStreams"`
51 | ShellRunTime string `xml"rsp:OutputStreams"`
52 | ShellInactivity string `xml"rsp:OutputStreams"`
53 | }
54 |
55 | type ResponseStream struct {
56 | Value string `xml:",innerxml"`
57 | Name string `xml:"Name,attr"`
58 | End string `xml:"End,attr"`
59 | }
60 |
61 | type ResponseCommandState struct {
62 | ExitCode int `xml"rsp:ExitCode"`
63 | CommandId string `xml:"CommandId,attr"`
64 | State string `xml:"State,attr"`
65 | }
66 |
67 | type ReceiveResponse struct {
68 | Stream []ResponseStream `xml"rsp:Stream"`
69 | CommandState *ResponseCommandState `xml"rsp:CommandState"`
70 | }
71 |
72 | type ResponseBody struct {
73 | CommandResponse *CommandResponse `xml"rsp:CommandResponse"`
74 | ResourceCreated *ResourceCreated `xml"x:ResourceCreated"`
75 | Shell *ResponseShell `xml"rsp:Shell"`
76 | ReceiveResponse *ReceiveResponse `xml"rsp:ReceiveResponse"`
77 | }
78 |
79 | type ResponseEnvelope struct {
80 | XMLName xml.Name `xml"s:Envelope"`
81 | // xmlnsS string `xml"xmlns:s,attr"`
82 | // xmlnsA string `xml"xmlns:a,attr"`
83 | // xmlnsX string `xml"xmlns:x,attr"`
84 | // xmlnsW string `xml"xmlns:w,attr"`
85 | // xmlnsRsp string `xml"xmlns:rsp,attr"`
86 | // xmlnsP string `xml"xmlns:p,attr"`
87 | // xmlnsLang string `xml"xmlns:lang,attr"`
88 | Header *ResponseHeader `xml"s:Header"`
89 | Body *ResponseBody `xml"s:Body"`
90 | }
91 |
92 | func GetObjectFromXML(XMLinput io.Reader) (ResponseEnvelope, error) {
93 | b, err := ioutil.ReadAll(XMLinput)
94 | var response ResponseEnvelope
95 | if err != nil {
96 | return response, err
97 | } else {
98 | xml.Unmarshal(b, &response)
99 | }
100 | x := ResponseEnvelope{}
101 | if response == x {
102 | return x, errors.New("Invalid server response")
103 | }
104 | return response, nil
105 | }
106 |
107 | var parseXML = GetObjectFromXML
108 |
109 | func ParseCommandOutput(XMLinput io.Reader) (stdout, stderr string, exitcode int, err error) {
110 | //fmt.Printf("%s\n\n\n", XMLinput)
111 | object, err := parseXML(XMLinput)
112 | //fmt.Printf("%s", object.Body.ReceiveResponse.Stream)
113 | if err != nil {
114 | return "", "", 0, errors.New("Error parsing XML")
115 | }
116 | var stdout_b bytes.Buffer
117 | var stderr_b bytes.Buffer
118 | for _, value := range object.Body.ReceiveResponse.Stream {
119 | if value.End == "" && value.Name == "stdout" {
120 | tmp, err := base64.StdEncoding.DecodeString(value.Value)
121 | if err != nil {
122 | return "", "", 0, errors.New("Error decoding stdout")
123 | }
124 | stdout_b.Write(tmp)
125 | } else if value.End == "" && value.Name == "stderr" {
126 | tmp, err := base64.StdEncoding.DecodeString(value.Value)
127 | if err != nil {
128 | return "", "", 0, errors.New("Error decoding stderr")
129 | }
130 | stderr_b.Write(tmp)
131 | } else {
132 | break
133 | }
134 | }
135 | exitcode = object.Body.ReceiveResponse.CommandState.ExitCode
136 | stdout = stdout_b.String()
137 | stderr = stderr_b.String()
138 | err = nil
139 | return
140 | }
141 |
--------------------------------------------------------------------------------
/response_test.go:
--------------------------------------------------------------------------------
1 | package winrm
2 |
3 | import (
4 | "bytes"
5 | "errors"
6 | "io"
7 |
8 | gc "launchpad.net/gocheck"
9 | )
10 |
11 | type responseSuite struct{}
12 |
13 | var _ = gc.Suite(responseSuite{})
14 |
15 | func (responseSuite) TestGetFromXML(c *gc.C) {
16 | xmlin := `http://schemas.microsoft.com/wbem/wsman/1/windows/shell/CommandResponseuuid:EC452E31-2872-4921-8C0C-C76398695407http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousuuid:7261e275-6d36-a627-8de0-e382e3a3cc5a6D0A426F-4B4A-44F8-AF20-C35365258FEB`
17 | res, err := GetObjectFromXML(bytes.NewBufferString(xmlin))
18 | c.Assert(err, gc.IsNil)
19 | c.Assert(res.Body.CommandResponse.CommandId, gc.Equals, "6D0A426F-4B4A-44F8-AF20-C35365258FEB")
20 | }
21 |
22 | func (responseSuite) TestGetFromXMLShellID(c *gc.C) {
23 | xmlin := `http://schemas.xmlsoap.org/ws/2004/09/transfer/CreateResponseuuid:986227D1-1F7F-410D-8FCE-D45971E61B81http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousuuid:c7f03012-ba76-3191-6990-74cea9dd327dhttp://windows-host:5985/wsmanhttp://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd9731F5BD-E90B-403B-A8DB-010396CEBB4D9731F5BD-E90B-403B-A8DB-010396CEBB4Dhttp://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmdubuntu@ubuntu192.168.1.1PT7200.000Sstdinstdout stderrP0DT0H0M0SP0DT0H0M0S`
24 | res, err := GetObjectFromXML(bytes.NewBufferString(xmlin))
25 | c.Assert(err, gc.IsNil)
26 | c.Assert(res.Body.Shell.ShellId, gc.Equals, "9731F5BD-E90B-403B-A8DB-010396CEBB4D")
27 | }
28 |
29 | func (responseSuite) TestGerFromXMLError(c *gc.C) {
30 | xmlin := "Random junk"
31 | res, err := GetObjectFromXML(bytes.NewBufferString(xmlin))
32 | var _ = res
33 | c.Assert(res, gc.Equals, ResponseEnvelope{})
34 | c.Assert(err, gc.ErrorMatches, "Invalid server response")
35 | }
36 |
37 | func MockStdOut(XMLinput io.Reader) (ResponseEnvelope, error) {
38 | a := make([]ResponseStream, 3)
39 | a[0] = ResponseStream{Value: "c3VjaCBncmVhdA==", Name: "stdout", End: ""}
40 | a[1] = ResponseStream{Value: "", Name: "stdout", End: "true"}
41 | a[2] = ResponseStream{Value: "", Name: "stderr", End: "true"}
42 | return ResponseEnvelope{Body: &ResponseBody{ReceiveResponse: &ReceiveResponse{Stream: a, CommandState: &ResponseCommandState{ExitCode: 666}}}}, nil
43 | }
44 |
45 | func (responseSuite) TestStdOutParseCommandOutput(c *gc.C) {
46 | parseXML = MockStdOut
47 | stdout, stderr, exitcode, err := ParseCommandOutput(bytes.NewBufferString("mocked"))
48 | c.Assert(err, gc.IsNil)
49 | c.Assert(stdout, gc.Equals, "such great")
50 | c.Assert(stderr, gc.Equals, "")
51 | c.Assert(exitcode, gc.Equals, 666)
52 | }
53 |
54 | func MockStdErr(XMLinput io.Reader) (ResponseEnvelope, error) {
55 | a := make([]ResponseStream, 3)
56 | a[0] = ResponseStream{Value: "J3R5cGVyJyBpcyBub3QgcmVjb2duaXplZCBhcyBhbiBpbnRlcm5hbCBvciBleHRlcm5hbCBjb21tYW5kLA0Kb3BlcmFibGUgcHJvZ3JhbSBvciBiYXRjaCBmaWxlLg0K", Name: "stderr", End: ""}
57 | a[1] = ResponseStream{Value: "", Name: "stdout", End: "true"}
58 | a[2] = ResponseStream{Value: "", Name: "stderr", End: "true"}
59 | return ResponseEnvelope{Body: &ResponseBody{ReceiveResponse: &ReceiveResponse{Stream: a, CommandState: &ResponseCommandState{ExitCode: 666}}}}, nil
60 | }
61 |
62 | func (responseSuite) TestStdErrParseCommandOutput(c *gc.C) {
63 | parseXML = MockStdErr
64 | stdout, stderr, exitcode, err := ParseCommandOutput(bytes.NewBufferString("mocked"))
65 | c.Assert(err, gc.IsNil)
66 | c.Assert(stdout, gc.Equals, "")
67 | c.Assert(stderr, gc.Equals, "'typer' is not recognized as an internal or external command,\r\noperable program or batch file.\r\n")
68 | c.Assert(exitcode, gc.Equals, 666)
69 | }
70 |
71 | func MockMultipleStdOut(XMLinput io.Reader) (ResponseEnvelope, error) {
72 | a := make([]ResponseStream, 4)
73 | a[0] = ResponseStream{Value: "c3VjaCBncmVhdA0K", Name: "stdout", End: ""}
74 | a[1] = ResponseStream{Value: "bmVlZHMgbW9yZSBsaW5lcw==", Name: "stdout", End: ""}
75 | a[2] = ResponseStream{Value: "", Name: "stdout", End: "true"}
76 | a[3] = ResponseStream{Value: "", Name: "stderr", End: "true"}
77 | return ResponseEnvelope{Body: &ResponseBody{ReceiveResponse: &ReceiveResponse{Stream: a, CommandState: &ResponseCommandState{ExitCode: 666}}}}, nil
78 | }
79 |
80 | func (responseSuite) TestMultipleStdOutParseCommandOutput(c *gc.C) {
81 | parseXML = MockMultipleStdOut
82 | stdout, stderr, exitcode, err := ParseCommandOutput(bytes.NewBufferString("mocked"))
83 | c.Assert(err, gc.IsNil)
84 | c.Assert(stdout, gc.Equals, "such great\r\nneeds more lines")
85 | c.Assert(stderr, gc.Equals, "")
86 | c.Assert(exitcode, gc.Equals, 666)
87 | }
88 |
89 | func MockStdErrAndOut(XMLinput io.Reader) (ResponseEnvelope, error) {
90 | a := make([]ResponseStream, 4)
91 | a[0] = ResponseStream{Value: "c3VjaCBncmVhdA0K", Name: "stdout", End: ""}
92 | a[1] = ResponseStream{Value: "bmVlZHMgbW9yZSBsaW5lcw==", Name: "stderr", End: ""}
93 | a[2] = ResponseStream{Value: "", Name: "stdout", End: "true"}
94 | a[3] = ResponseStream{Value: "", Name: "stderr", End: "true"}
95 | return ResponseEnvelope{Body: &ResponseBody{ReceiveResponse: &ReceiveResponse{Stream: a, CommandState: &ResponseCommandState{ExitCode: 666}}}}, nil
96 | }
97 |
98 | func (responseSuite) TestStdErrAndOutParseCommandOutput(c *gc.C) {
99 | parseXML = MockStdErrAndOut
100 | stdout, stderr, exitcode, err := ParseCommandOutput(bytes.NewBufferString("mocked"))
101 | c.Assert(err, gc.IsNil)
102 | c.Assert(stdout, gc.Equals, "such great\r\n")
103 | c.Assert(stderr, gc.Equals, "needs more lines")
104 | c.Assert(exitcode, gc.Equals, 666)
105 | }
106 |
107 | func MockBreak(XMLinput io.Reader) (ResponseEnvelope, error) {
108 | a := make([]ResponseStream, 4)
109 | a[0] = ResponseStream{Value: "c3VjaCBncmVhdA0K", Name: "stdout", End: ""}
110 | a[1] = ResponseStream{Value: "", Name: "stdout", End: "true"}
111 | a[2] = ResponseStream{Value: "bmVlZHMgbW9yZSBsaW5lcw==", Name: "stdout", End: ""}
112 | a[3] = ResponseStream{Value: "", Name: "stderr", End: "true"}
113 | return ResponseEnvelope{Body: &ResponseBody{ReceiveResponse: &ReceiveResponse{Stream: a, CommandState: &ResponseCommandState{ExitCode: 666}}}}, nil
114 | }
115 |
116 | func (responseSuite) TestBreakParseCommandOutput(c *gc.C) {
117 | parseXML = MockBreak
118 | stdout, stderr, exitcode, err := ParseCommandOutput(bytes.NewBufferString("mocked"))
119 | c.Assert(err, gc.IsNil)
120 | c.Assert(stdout, gc.Equals, "such great\r\n")
121 | c.Assert(stderr, gc.Equals, "")
122 | c.Assert(exitcode, gc.Equals, 666)
123 | }
124 |
125 | func MockInvalidInput(XMLinput io.Reader) (ResponseEnvelope, error) {
126 | return ResponseEnvelope{}, errors.New("mock")
127 | }
128 |
129 | func (responseSuite) TestInvalidInputParseCommandOutput(c *gc.C) {
130 | parseXML = MockInvalidInput
131 | stdout, stderr, exitcode, err := ParseCommandOutput(bytes.NewBufferString("mocked"))
132 | c.Assert(err, gc.ErrorMatches, "Error parsing XML")
133 | c.Assert(stdout, gc.Equals, "")
134 | c.Assert(stderr, gc.Equals, "")
135 | c.Assert(exitcode, gc.Equals, 0)
136 | }
137 |
138 | func MockInvalidStdOut(XMLinput io.Reader) (ResponseEnvelope, error) {
139 | a := make([]ResponseStream, 3)
140 | a[0] = ResponseStream{Value: "0", Name: "stdout", End: ""}
141 | a[1] = ResponseStream{Value: "", Name: "stdout", End: "true"}
142 | a[2] = ResponseStream{Value: "", Name: "stderr", End: "true"}
143 | return ResponseEnvelope{Body: &ResponseBody{ReceiveResponse: &ReceiveResponse{Stream: a, CommandState: &ResponseCommandState{ExitCode: 666}}}}, nil
144 | }
145 |
146 | func (responseSuite) TestInvalidStdOutParseCommandOutput(c *gc.C) {
147 | parseXML = MockInvalidStdOut
148 | stdout, stderr, exitcode, err := ParseCommandOutput(bytes.NewBufferString("mocked"))
149 | c.Assert(err, gc.ErrorMatches, "Error decoding stdout")
150 | c.Assert(stdout, gc.Equals, "")
151 | c.Assert(stderr, gc.Equals, "")
152 | c.Assert(exitcode, gc.Equals, 0)
153 | }
154 |
155 | func MockInvalidStdErr(XMLinput io.Reader) (ResponseEnvelope, error) {
156 | a := make([]ResponseStream, 3)
157 | a[0] = ResponseStream{Value: "0", Name: "stderr", End: ""}
158 | a[1] = ResponseStream{Value: "", Name: "stdout", End: "true"}
159 | a[2] = ResponseStream{Value: "", Name: "stderr", End: "true"}
160 | return ResponseEnvelope{Body: &ResponseBody{ReceiveResponse: &ReceiveResponse{Stream: a, CommandState: &ResponseCommandState{ExitCode: 666}}}}, nil
161 | }
162 |
163 | func (responseSuite) TestInvalidStdErrParseCommandOutput(c *gc.C) {
164 | parseXML = MockInvalidStdErr
165 | stdout, stderr, exitcode, err := ParseCommandOutput(bytes.NewBufferString("mocked"))
166 | c.Assert(err, gc.ErrorMatches, "Error decoding stderr")
167 | c.Assert(stdout, gc.Equals, "")
168 | c.Assert(stderr, gc.Equals, "")
169 | c.Assert(exitcode, gc.Equals, 0)
170 | }
171 |
--------------------------------------------------------------------------------
/start_test.go:
--------------------------------------------------------------------------------
1 | package winrm
2 |
3 | import (
4 | "testing"
5 |
6 | gc "launchpad.net/gocheck"
7 | )
8 |
9 | func Test_start(t *testing.T) { gc.TestingT(t) }
10 |
--------------------------------------------------------------------------------
/transport.go:
--------------------------------------------------------------------------------
1 | package winrm
2 |
3 | import (
4 | "bytes"
5 | "encoding/xml"
6 | "errors"
7 | "fmt"
8 | "strings"
9 |
10 | "launchpad.net/gwacl/fork/http"
11 | "launchpad.net/gwacl/fork/tls"
12 | )
13 |
14 | type CertificateCredentials struct {
15 | Cert string
16 | Key string
17 | CA string
18 | }
19 |
20 | type SoapRequest struct {
21 | Endpoint string
22 | AuthType string
23 | Username string
24 | Passwd string
25 | HttpInsecure bool
26 | CertAuth *CertificateCredentials
27 | HttpClient *http.Client
28 | }
29 |
30 | func (conf *SoapRequest) SendMessage(envelope *Envelope) (*http.Response, error) {
31 | output, err := xml.MarshalIndent(envelope, " ", " ")
32 | if err != nil {
33 | return nil, err
34 | }
35 |
36 | if conf.AuthType == "BasicAuth" {
37 | if conf.Username == "" || conf.Passwd == "" {
38 | // fmt.Errorf("AuthType BasicAuth needs Username and Passwd")
39 | return nil, errors.New("AuthType BasicAuth needs Username and Passwd")
40 | }
41 | return conf.HttpBasicAuth(output)
42 | } else if conf.AuthType == "CertAuth" {
43 | return conf.HttpCertAuth(output)
44 | }
45 | return nil, errors.New(fmt.Sprintf("Invalid transport: %s", conf.AuthType))
46 | }
47 |
48 | func (conf *SoapRequest) GetHttpHeader() map[string]string {
49 | header := make(map[string]string)
50 | header["Content-Type"] = "application/soap+xml;charset=UTF-8"
51 | header["User-Agent"] = "Go WinRM client"
52 | return header
53 | }
54 |
55 | func (conf *SoapRequest) HttpCertAuth(data []byte) (*http.Response, error) {
56 | protocol := strings.Split(conf.Endpoint, ":")
57 | if protocol[0] != "http" && protocol[0] != "https" {
58 | return nil, errors.New("Invalid protocol. Expected http or https")
59 | }
60 | header := conf.GetHttpHeader()
61 | header["Authorization"] = "http://schemas.dmtf.org/wbem/wsman/1/wsman/secprofile/https/mutual"
62 |
63 | if conf.HttpClient == nil {
64 | conf.HttpClient = &http.Client{}
65 | }
66 |
67 | if protocol[0] != "https" {
68 | return nil, errors.New("Invalid protocol for this transport type")
69 | }
70 |
71 | cert, err := tls.LoadX509KeyPair(conf.CertAuth.Cert, conf.CertAuth.Key)
72 | if err != nil {
73 | return nil, err
74 | }
75 |
76 | tlsConfig := &tls.Config{
77 | InsecureSkipVerify: true,
78 | Certificates: []tls.Certificate{
79 | cert,
80 | },
81 | }
82 |
83 | tr := &http.Transport{
84 | TLSClientConfig: tlsConfig,
85 | }
86 | conf.HttpClient.Transport = tr
87 | body := bytes.NewBuffer(data)
88 | req, err := http.NewRequest("POST", conf.Endpoint, body)
89 | req.ContentLength = int64(len(data))
90 | for k, v := range header {
91 | req.Header.Add(k, v)
92 | }
93 | resp, err := conf.HttpClient.Do(req)
94 | if err != nil {
95 | return nil, err
96 | }
97 | if resp.StatusCode != 200 {
98 | return nil, errors.New(fmt.Sprintf("Remote host returned error status code: %d", resp.StatusCode))
99 | }
100 | //fmt.Printf("%v\n%v\n", resp, err)
101 | return resp, err
102 | }
103 |
104 | func (conf *SoapRequest) HttpBasicAuth(data []byte) (*http.Response, error) {
105 | protocol := strings.Split(conf.Endpoint, ":")
106 | if protocol[0] != "http" && protocol[0] != "https" {
107 | return nil, errors.New("Invalid protocol. Expected http or https")
108 | }
109 |
110 | header := conf.GetHttpHeader()
111 |
112 | if conf.HttpClient == nil {
113 | conf.HttpClient = &http.Client{}
114 | }
115 | // Ignore SSL certificate errors
116 | if protocol[0] == "https" {
117 | tr := &http.Transport{
118 | TLSClientConfig: &tls.Config{InsecureSkipVerify: conf.HttpInsecure},
119 | }
120 | conf.HttpClient.Transport = tr
121 | }
122 | body := bytes.NewBuffer(data)
123 | req, err := http.NewRequest("POST", conf.Endpoint, body)
124 | req.ContentLength = int64(len(data))
125 | req.SetBasicAuth(conf.Username, conf.Passwd)
126 |
127 | for k, v := range header {
128 | req.Header.Add(k, v)
129 | }
130 |
131 | resp, err := conf.HttpClient.Do(req)
132 | if err != nil {
133 | return nil, err
134 | }
135 | if resp.StatusCode != 200 {
136 | return nil, errors.New(fmt.Sprintf("Remote host returned error status code: %d", resp.StatusCode))
137 | }
138 | return resp, err
139 | }
140 |
--------------------------------------------------------------------------------
/transport_test.go:
--------------------------------------------------------------------------------
1 | package winrm
2 |
3 | import (
4 | "encoding/xml"
5 | "fmt"
6 | "io/ioutil"
7 | "net/http"
8 | "net/http/httptest"
9 | "syscall"
10 |
11 | gc "launchpad.net/gocheck"
12 | )
13 |
14 | type TransportSuite struct{}
15 |
16 | var _ = gc.Suite(TransportSuite{})
17 |
18 | //tests that GetHttpHeader outputs the desired values
19 | func (TransportSuite) TestGetHttpHeader(c *gc.C) {
20 | req := SoapRequest{}
21 | header := req.GetHttpHeader()
22 | want := make(map[string]string)
23 | want["Content-Type"] = "application/soap+xml;charset=UTF-8"
24 | want["User-Agent"] = "Go WinRM client"
25 | c.Assert(header, gc.DeepEquals, want)
26 | }
27 |
28 | // tests for completely invalid protocol in HttpCertAuth
29 | func (TransportSuite) TestHttpCertAuthInvalidProtocol(c *gc.C) {
30 | req := SoapRequest{
31 | AuthType: "CertAuth",
32 | Endpoint: "nothttp://whatever.com",
33 | }
34 |
35 | resp, err := req.HttpCertAuth(nil)
36 | c.Assert(resp, gc.IsNil)
37 | c.Assert(err, gc.ErrorMatches, "Invalid protocol. Expected http or https")
38 | }
39 |
40 | // tests that https protocol is specifically asked for in HttpCertAuth
41 | func (TransportSuite) TestHttpCertAuthNotHttps(c *gc.C) {
42 | req := SoapRequest{
43 | AuthType: "CertAuth",
44 | Endpoint: "http://something.smth",
45 | }
46 |
47 | resp, err := req.HttpCertAuth(nil)
48 | c.Assert(resp, gc.IsNil)
49 | c.Assert(err, gc.ErrorMatches, "Invalid protocol for this transport type")
50 | }
51 |
52 | // test for invalid key-value pair in HttpCertAuth
53 | func (TransportSuite) TestHttpCertAuthKeypairFailure(c *gc.C) {
54 | // must insert invalid certificate fields into this one:
55 | cert := CertificateCredentials{}
56 | req := SoapRequest{
57 | AuthType: "CertAuth",
58 | CertAuth: &cert,
59 | Endpoint: "https://something.good",
60 | }
61 |
62 | resp, err := req.HttpCertAuth(nil)
63 | c.Assert(resp, gc.IsNil)
64 | c.Assert(err, gc.NotNil)
65 | }
66 |
67 | // test for completely invalid protocol in HttpBasicAuth
68 | func (TransportSuite) TestHttpBasicAuthInvalidProtocol(c *gc.C) {
69 | req := SoapRequest{
70 | AuthType: "BasicAuth",
71 | Endpoint: "nothttp://whatevs",
72 | }
73 |
74 | resp, err := req.HttpBasicAuth(nil)
75 | c.Assert(resp, gc.IsNil)
76 | c.Assert(err, gc.ErrorMatches, "Invalid protocol. Expected http or https")
77 | }
78 |
79 | // IRRELEVANT TEST
80 | // compiler won't allow sending of envelope which is not a struct, thus the
81 | // MarshalIndent never throws the error
82 | // ...
83 | // when Envelope is anything but a struct(will pass even if not of type Envelope)
84 | // func (TransportSuite) TestSendBadEnvelope(c *gc.C) {
85 | // req := SoapRequest{}
86 | // envelope := make(chan int)
87 |
88 | // res, err := req.SendMessage(&envelope)
89 | // c.Assert(res, gc.IsNil)
90 | // c.Assert(err, gc.NotNil)
91 | // }
92 |
93 | // tests that alert is raised in case of BasicAuth request with empty user/pass in SendMessage
94 | func (TransportSuite) TestSendMessageEmptyAuthRequest(c *gc.C) {
95 | req := SoapRequest{AuthType: "BasicAuth"}
96 | envelope := &Envelope{}
97 |
98 | resp, err := req.SendMessage(envelope)
99 | c.Assert(resp, gc.IsNil)
100 | c.Assert(err, gc.ErrorMatches, "AuthType BasicAuth needs Username and Passwd")
101 | }
102 |
103 | // tests that valid BasicAuth case is recognized in SendMessage
104 | func (TransportSuite) TestSendMessageBasicAuth(c *gc.C) {
105 | req := SoapRequest{
106 | AuthType: "BasicAuth",
107 | Username: "Leeroy",
108 | Passwd: "Jenkins"}
109 | envelope := &Envelope{}
110 |
111 | resp, err := req.SendMessage(envelope)
112 | xmld, _ := xml.MarshalIndent(envelope, " ", " ")
113 | expresp, experr := req.HttpBasicAuth(xmld)
114 |
115 | c.Assert(resp, gc.DeepEquals, expresp)
116 | c.Assert(err, gc.DeepEquals, experr)
117 | }
118 |
119 | // test that valid CertAuth case is recognized in SendMessage
120 | func (TransportSuite) TestSendMessageCertAuth(c *gc.C) {
121 | req := SoapRequest{AuthType: "CertAuth"}
122 | envelope := &Envelope{}
123 |
124 | resp, err := req.SendMessage(envelope)
125 | xmld, _ := xml.MarshalIndent(envelope, " ", " ")
126 | expresp, experr := req.HttpCertAuth(xmld)
127 |
128 | c.Assert(resp, gc.DeepEquals, expresp)
129 | c.Assert(err, gc.DeepEquals, experr)
130 | }
131 |
132 | // tests that SoapRequest with bogus AuthType is rejected in SendMessage
133 | func (TransportSuite) TestSendMessageBadAuth(c *gc.C) {
134 | req := SoapRequest{
135 | AuthType: "SomeStupidShit",
136 | }
137 | envelope := &Envelope{}
138 |
139 | resp, err := req.SendMessage(envelope)
140 | c.Assert(resp, gc.IsNil)
141 | c.Assert(err, gc.ErrorMatches, fmt.Sprintf("Invalid transport: %s", req.AuthType))
142 | }
143 |
144 | func (TransportSuite) TestHttpBasicRequestOK(c *gc.C) {
145 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
146 | c.Assert(r.Method, gc.Equals, "POST")
147 |
148 | body, err := ioutil.ReadAll(r.Body)
149 | c.Assert(err, gc.IsNil)
150 | c.Assert(string(body), gc.Equals, "trololol")
151 |
152 | c.Assert(r.ContentLength, gc.Equals, int64(len(body)))
153 |
154 | c.Assert(r.Header.Get("User-Agent"), gc.Equals, "Go WinRM client")
155 | c.Assert(r.Header.Get("Content-Type"), gc.Equals, "application/soap+xml;charset=UTF-8")
156 | c.Assert(r.Header.Get("Authorization"), gc.Equals, "Basic bGVlcm95OmplbmtpbnM=")
157 | }))
158 | defer server.Close()
159 |
160 | req := SoapRequest{
161 | Endpoint: server.URL,
162 | AuthType: "BasicAuth",
163 | HttpClient: nil,
164 | Username: "leeroy",
165 | Passwd: "jenkins",
166 | }
167 | body := []byte("trololol")
168 |
169 | resp, err := req.HttpBasicAuth(body)
170 | c.Assert(err, gc.IsNil)
171 | c.Assert(resp, gc.NotNil)
172 | }
173 |
174 | func (TransportSuite) TestHttpBasicServerError(c *gc.C) {
175 | server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
176 | http.Error(w, "fail", http.StatusInternalServerError)
177 | }))
178 | defer server.Close()
179 |
180 | req := SoapRequest{
181 | Endpoint: server.URL,
182 | AuthType: "BasicAuth",
183 | HttpClient: nil,
184 | Username: "leeroy",
185 | Passwd: "jenkins",
186 | }
187 | body := []byte("trololol")
188 |
189 | resp, err := req.HttpBasicAuth(body)
190 | c.Assert(err, gc.ErrorMatches, "Remote host returned error status code: 500")
191 | c.Assert(resp, gc.IsNil)
192 | }
193 |
194 | func (TransportSuite) TestHttpBasicError(c *gc.C) {
195 | req := SoapRequest{
196 | Endpoint: "http://doesnotexist",
197 | AuthType: "BasicAuth",
198 | HttpClient: nil,
199 | Username: "leeroy",
200 | Passwd: "jenkins",
201 | }
202 |
203 | body := []byte("trololol")
204 |
205 | resp, err := req.HttpBasicAuth(body)
206 | c.Assert(err, gc.ErrorMatches, "dial tcp: lookup doesnotexist: no such host")
207 | c.Assert(resp, gc.IsNil)
208 | }
209 |
210 | func (TransportSuite) TestHttpCertRequestOK(c *gc.C) {
211 |
212 | pem, err := ioutil.TempFile("", "pem")
213 | if err != nil {
214 | panic(err)
215 | }
216 | defer syscall.Unlink(pem.Name())
217 | ioutil.WriteFile(pem.Name(), []byte(cert_pem), 0644)
218 |
219 | key, err := ioutil.TempFile("", "key")
220 | if err != nil {
221 | panic(err)
222 | }
223 | defer syscall.Unlink(key.Name())
224 | ioutil.WriteFile(key.Name(), []byte(cert_key), 0644)
225 |
226 | server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
227 | c.Assert(r.Method, gc.Equals, "POST")
228 |
229 | body, err := ioutil.ReadAll(r.Body)
230 | c.Assert(err, gc.IsNil)
231 | c.Assert(string(body), gc.Equals, "trololol")
232 |
233 | c.Assert(r.ContentLength, gc.Equals, int64(len(body)))
234 |
235 | c.Assert(r.Header.Get("User-Agent"), gc.Equals, "Go WinRM client")
236 | c.Assert(r.Header.Get("Content-Type"), gc.Equals, "application/soap+xml;charset=UTF-8")
237 | c.Assert(r.Header.Get("Authorization"), gc.Equals, "http://schemas.dmtf.org/wbem/wsman/1/wsman/secprofile/https/mutual")
238 | }))
239 | defer server.Close()
240 |
241 | req := SoapRequest{
242 | Endpoint: server.URL,
243 | AuthType: "CertAuth",
244 | HttpClient: nil,
245 | CertAuth: &CertificateCredentials{
246 | Cert: pem.Name(),
247 | Key: key.Name(),
248 | }}
249 | body := []byte("trololol")
250 |
251 | resp, err := req.HttpCertAuth(body)
252 | c.Assert(err, gc.IsNil)
253 | c.Assert(resp, gc.NotNil)
254 | }
255 |
256 | func (TransportSuite) TestHttpCertRequestServerError(c *gc.C) {
257 | server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
258 | http.Error(w, "fail", http.StatusInternalServerError)
259 | }))
260 | defer server.Close()
261 |
262 | pem, err := ioutil.TempFile("", "pem")
263 | if err != nil {
264 | panic(err)
265 | }
266 | defer syscall.Unlink(pem.Name())
267 | ioutil.WriteFile(pem.Name(), []byte(cert_pem), 0644)
268 |
269 | key, err := ioutil.TempFile("", "key")
270 | if err != nil {
271 | panic(err)
272 | }
273 | defer syscall.Unlink(key.Name())
274 | ioutil.WriteFile(key.Name(), []byte(cert_key), 0644)
275 |
276 | req := SoapRequest{
277 | Endpoint: server.URL + "/a",
278 | AuthType: "CertAuth",
279 | HttpClient: nil,
280 | CertAuth: &CertificateCredentials{
281 | Cert: pem.Name(),
282 | Key: key.Name(),
283 | }}
284 | body := []byte("trololol")
285 |
286 | resp, err := req.HttpCertAuth(body)
287 | c.Assert(err, gc.ErrorMatches, "Remote host returned error status code: 500")
288 | c.Assert(resp, gc.IsNil)
289 | }
290 |
291 | func (TransportSuite) TestHttpCertRequestError(c *gc.C) {
292 | pem, err := ioutil.TempFile("", "pem")
293 | if err != nil {
294 | panic(err)
295 | }
296 | defer syscall.Unlink(pem.Name())
297 | ioutil.WriteFile(pem.Name(), []byte(cert_pem), 0644)
298 |
299 | key, err := ioutil.TempFile("", "key")
300 | if err != nil {
301 | panic(err)
302 | }
303 | defer syscall.Unlink(key.Name())
304 | ioutil.WriteFile(key.Name(), []byte(cert_key), 0644)
305 |
306 | req := SoapRequest{
307 | Endpoint: "https://doesnotexist",
308 | AuthType: "CertAuth",
309 | HttpClient: nil,
310 | CertAuth: &CertificateCredentials{
311 | Cert: pem.Name(),
312 | Key: key.Name(),
313 | }}
314 | body := []byte("trololol")
315 |
316 | resp, err := req.HttpCertAuth(body)
317 | c.Assert(err, gc.ErrorMatches, "dial tcp: lookup doesnotexist: no such host")
318 | c.Assert(resp, gc.IsNil)
319 | }
320 |
321 | var cert_pem = `-----BEGIN CERTIFICATE-----
322 | MIIC8DCCAdigAwIBAwICA+gwDQYJKoZIhvcNAQEFBQAwGDEWMBQGA1UEAxQNdWJ1
323 | bnR1QHVidW50dTAeFw0xNDA3MTgxMjEyMzlaFw0yNDA3MTUxMjEyMzlaMBgxFjAU
324 | BgNVBAMUDXVidW50dUB1YnVudHUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
325 | AoIBAQDJ6tBRTRs6VZDYB3tVY0r2CPtzxrSNbKjvsaYdgqYwx3eX7bBhpM9/ijLZ
326 | iycMM/O8SBxzTxZv5nF82RXVXY7dAUq8lPbacwRoo1PAAQX1CZrsmsdyEjXn7M9n
327 | E65FPKTz3yEyEVYK9f43lmVwAntACHenl0bH+0dxgo/eQTGVrVYPJp5aa4OpIUQD
328 | Ph2txDx4oyYgdGP78W4/4tPAwxyljy00F6tdo5OD7Wjw1C01sp50iO6dRb83twAN
329 | KnNxVwQqnWgn3XuzXaNFwOqlQ1yalgUk2Ky/yrMe2FhmNIaGnYv5z0lBS9jBbsmx
330 | l0FZ5btN6QTJAM/IlC+XZaa3ftlrAgMBAAGjRDBCMBYGA1UdJQEB/wQMMAoGCCsG
331 | AQUFBwMCMCgGA1UdEQQhMB+gHQYKKwYBBAGCNxQCA6APDA11YnVudHVAdWJ1bnR1
332 | MA0GCSqGSIb3DQEBBQUAA4IBAQAih7AxVMFHevX2GbcjlU9s5i6+ZKKhh5Jo0iMH
333 | voS8dk0E3V36TABZwqc4jY3BmHvit0esBkpQOP2I4F634ByUEe7462rtUBrgIBHd
334 | WFPEHx/dwq7S+iktOyOvnk2uEyGCH8B4EMeiCpsLzC9g3bjsuySAB5l1HJJROAVH
335 | sjKgBsCgJvdFah0UKv1xXzSZdBjMWw8b4tVdIGJ6N3S4bLfZbwrR8c/Ym9SJdXfM
336 | PLWfcd5kJD8awgKIDUrmCn4LJpCLFSfRrbi1Y+AzsdVvsq3bpzfD5ZMFqxa7m6sr
337 | YKmtPl60PWQcfxP3yWUJNQB9hPTav1u3+NlUxUcP/Vw+6A+U
338 | -----END CERTIFICATE-----`
339 |
340 | var cert_key = `-----BEGIN PRIVATE KEY-----
341 | MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDJ6tBRTRs6VZDY
342 | B3tVY0r2CPtzxrSNbKjvsaYdgqYwx3eX7bBhpM9/ijLZiycMM/O8SBxzTxZv5nF8
343 | 2RXVXY7dAUq8lPbacwRoo1PAAQX1CZrsmsdyEjXn7M9nE65FPKTz3yEyEVYK9f43
344 | lmVwAntACHenl0bH+0dxgo/eQTGVrVYPJp5aa4OpIUQDPh2txDx4oyYgdGP78W4/
345 | 4tPAwxyljy00F6tdo5OD7Wjw1C01sp50iO6dRb83twANKnNxVwQqnWgn3XuzXaNF
346 | wOqlQ1yalgUk2Ky/yrMe2FhmNIaGnYv5z0lBS9jBbsmxl0FZ5btN6QTJAM/IlC+X
347 | Zaa3ftlrAgMBAAECggEBAICLRpebmOvoMU/2Y2QW1FARo9Mu+x7VwC7oT7KVzCtd
348 | sRs9rH5dJ+QwHPM1jWRNZqvE1Kfr/4K5mCI9KZMt/pdgDS5FP2oOsw3SfKzNefdn
349 | aAOc/b/3K+48akVa2CUn2HOQ51cyhi5wMKk+y9ElI0W+nj5JJjyGEhOHZQO/SUvZ
350 | blcLLjdyIpj+g5eKkNTDSXo80M3lkedPewFb9ihFH4NF9wa5RP93DbiA85FFdCHc
351 | 0k6nKReKJAXyc6VUTJ3O0Bl1m64ByAonuIBMqJ9LYKzk9gCwIAGeTnj153x7wc7o
352 | eebtxfjRB5TMTlh/wn9t855aGMbTr6+URbQh2z6GvxECgYEA6BYR+0K+NgZZsDCp
353 | e87BkMAWnrR/2Ihc89G17G0JVF0rgxpFkcbwO9MkrG658gttG+auTInw9cgWqFj6
354 | 0ZXK8PPelIpdoPIIrVJivZNFoN249Z/uD1WmNekw/wj6nCPvo8LOfP4yHQ+BjWwF
355 | 3IuFas109B1QfBuF2TEXHFiAGycCgYEA3rjznlPE81iT+8zl2BqZ4Zi6A4iIpBAo
356 | jxc8miL5grltW3Wl5iQk1hRdaFj2IaTouPt6/TBI7Dzw+F3IBckRycD2oZ9npGRW
357 | oCvcE/y5ar4qowb4n/she2K2jQzZ6Yjd+48HsGbXgzvl7T/S+tt0gdtQl80NfZBX
358 | KYUAQ11Zyh0CgYEA5W8MD6yHhbj5aShyJCbdTE/ZDMO7rz//RDoI8tVH59LDdTO/
359 | msFkNIAjPSOpRxLspiyCGsAzKYbIf1yXeCHxIgqz+3xd2wHqeg1795VjvAf1FT0p
360 | hpdRXPJOsZEazsjn2qh2oTJaMEhn9nrXwJNdLZw3Bi0Ep+w9gdz5z9fdrPkCgYBa
361 | 4lAPQJGy12dzrdXwzFIU29S0Emfnwuw6D7pcD3+Pl4kHdEehVQhvD1padUriyb9p
362 | lL1ISgbH18phHyu7KKSIlqRNqZWKYKN0stEYmt0ysK0HX5Xe+oRcLBjgD+lwQbiL
363 | qX7yvdSdqbiWip/WW+z7/HmzqCokHd1jhPFpi9NTBQKBgEcVlVHVqSe9WOTYzx8q
364 | O/tYCr2cszggryOeI9CgqX8KzvaGpXWc/w6iWM8JBJ6qZd+hoKKnxuhjPLxyrZPj
365 | 9zeMBJcPd67cHzd8ZC55dMVucHQYMr4rHeua8qouat3iPLAfCtr37pY888+jDZvn
366 | eB6Glbz65UWCjd0GL8v8WRfV
367 | -----END PRIVATE KEY-----`
368 |
--------------------------------------------------------------------------------
/util.go:
--------------------------------------------------------------------------------
1 | package winrm
2 |
3 | import (
4 | "crypto/rand"
5 | "fmt"
6 | "io"
7 | )
8 |
9 | func Uuid() (string, error) {
10 | b := make([]byte, 16)
11 | _, err := io.ReadFull(rand.Reader, b)
12 | if err != nil {
13 | return "", err
14 | }
15 | uuid := fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
16 | return uuid, nil
17 | }
18 |
--------------------------------------------------------------------------------
/util_test.go:
--------------------------------------------------------------------------------
1 | package winrm
2 |
3 | import (
4 | "regexp"
5 |
6 | gc "launchpad.net/gocheck"
7 |
8 | jc "launchpad.net/juju-core/testing/checkers"
9 | )
10 |
11 | type utilSuite struct{}
12 |
13 | var _ = gc.Suite(utilSuite{})
14 |
15 | func IsValidUUID(s string) bool {
16 | var validUUID = regexp.MustCompile("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}")
17 |
18 | return validUUID.MatchString(s)
19 | }
20 |
21 | func (utilSuite) TestUUID(c *gc.C) {
22 | uuid, err := Uuid()
23 | c.Assert(err, gc.IsNil)
24 | c.Assert(uuid, jc.Satisfies, IsValidUUID)
25 | }
26 |
--------------------------------------------------------------------------------
/xml.go:
--------------------------------------------------------------------------------
1 | package winrm
2 |
3 | type EnvelopeAttrs struct {
4 | Xsd string `xml:"xmlns:xsd,attr,omitempty"`
5 | Xsi string `xml:"xmlns:xsi,attr,omitempty"`
6 | Rsp string `xml:"xmlns:rsp,attr,omitempty"`
7 | P string `xml:"xmlns:p,attr,omitempty"`
8 | W string `xml:"xmlns:w,attr,omitempty"`
9 | X string `xml:"xmlns:x,attr,omitempty"`
10 | A string `xml:"xmlns:a,attr,omitempty"`
11 | B string `xml:"xmlns:b,attr,omitempty"`
12 | Env string `xml:"xmlns:env,attr,omitempty"`
13 | Cfg string `xml:"xmlns:cfg,attr,omitempty"`
14 | N string `xml:"xmlns:n,attr,omitempty"`
15 | }
16 |
17 | type ValueName struct {
18 | Value string `xml:",innerxml"`
19 | Attr string `xml:"Name,attr"`
20 | }
21 |
22 | type OptionSet struct {
23 | Option []ValueName `xml:"w:Option"`
24 | }
25 |
26 | type ValueMustUnderstand struct {
27 | Value string `xml:",innerxml"`
28 | Attr string `xml:"mustUnderstand,attr"`
29 | }
30 |
31 | type LocaleAttr struct {
32 | MustUnderstand string `xml:"mustUnderstand,attr"`
33 | Lang string `xml:"xml:lang,attr"`
34 | }
35 |
36 | type ReplyAddress struct {
37 | Address ValueMustUnderstand `xml:"a:Address"`
38 | }
39 |
40 | type Selector struct {
41 | Set ValueName `xml:"w:Selector"`
42 | }
43 |
44 | type Headers struct {
45 | To string `xml:"a:To"`
46 | OptionSet *OptionSet `xml:"w:OptionSet,omitempty"`
47 | ReplyTo *ReplyAddress `xml:"a:ReplyTo,omitempty"`
48 | MaxEnvelopeSize *ValueMustUnderstand `xml:"w:MaxEnvelopeSize,omitempty"`
49 | MessageID string `xml:"a:MessageID,omitempty"`
50 | Locale *LocaleAttr `xml:"p:Locale,omitempty"`
51 | DataLocale *LocaleAttr `xml:"p:DataLocale,omitempty"`
52 | OperationTimeout string `xml:"w:OperationTimeout"`
53 | ResourceURI *ValueMustUnderstand `xml:"w:ResourceURI,omitempty"`
54 | Action *ValueMustUnderstand `xml:"a:Action,omitempty"`
55 | SelectorSet *Selector `xml:"w:SelectorSet,omitempty"`
56 | }
57 |
58 | type Command struct {
59 | Command string `xml:"rsp:Command"`
60 | Arguments string `xml:"rsp:Arguments,omitempty"`
61 | }
62 |
63 | type DesiredStreamProps struct {
64 | Value string `xml:",innerxml"`
65 | Attr string `xml:"CommandId,attr"`
66 | }
67 |
68 | type Receive struct {
69 | DesiredStream DesiredStreamProps `xml:"rsp:DesiredStream"`
70 | }
71 |
72 | type Signal struct {
73 | Attr string `xml:"CommandId,attr"`
74 | Code string `xml:"rsp:Code"`
75 | }
76 |
77 | type EnvVariable struct {
78 | Value string `xml:",innerxml"`
79 | Name string `xml:"Name,attr"`
80 | }
81 |
82 | type Environment struct {
83 | Variable []EnvVariable `xml:"rsp:Variable"`
84 | }
85 |
86 | type Shell struct {
87 | InputStreams string `xml:"rsp:InputStreams,omitempty"`
88 | OutputStreams string `xml:"rsp:OutputStreams,omitempty"`
89 | WorkingDirectory string `xml:"rsp:WorkingDirectory,omitempty"`
90 | IdleTimeOut string `xml:"rsp:IdleTimeOut,omitempty"`
91 | Environment *Environment `xml:"rsp:Environment,omitempty"`
92 | }
93 |
94 | type BodyStruct struct {
95 | CommandLine *Command `xml:"rsp:CommandLine,omitempty"`
96 | Receive *Receive `xml:"rsp:Receive,omitempty"`
97 | Signal *Signal `xml:"rsp:Signal,omitempty"`
98 | Shell *Shell `xml:"rsp:Shell"`
99 | }
100 |
101 | var Namespaces EnvelopeAttrs = EnvelopeAttrs{
102 | Xsd: "http://www.w3.org/2001/XMLSchema",
103 | Xsi: "http://www.w3.org/2001/XMLSchema-instance",
104 | Rsp: "http://schemas.microsoft.com/wbem/wsman/1/windows/shell",
105 | P: "http://schemas.microsoft.com/wbem/wsman/1/wsman.xsd",
106 | W: "http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd",
107 | X: "http://schemas.xmlsoap.org/ws/2004/09/transfer",
108 | A: "http://schemas.xmlsoap.org/ws/2004/08/addressing",
109 | B: "http://schemas.dmtf.org/wbem/wsman/1/cimbinding.xsd",
110 | Env: "http://www.w3.org/2003/05/soap-envelope",
111 | Cfg: "http://schemas.microsoft.com/wbem/wsman/1/config",
112 | N: "http://schemas.xmlsoap.org/ws/2004/09/enumeration",
113 | }
114 |
--------------------------------------------------------------------------------