├── 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 | --------------------------------------------------------------------------------