func ParseSlurpOutputErrResponse(response string, stdout io.Writer, stderr io.Writer) (finished bool, exitCode int, err error) { doc, err := xmlpath.Parse(strings.NewReader(response)) stdouts, _ := xpath(doc, "//rsp:Stream[@Name='stdout']") for _, node := range stdouts { content, _ := base64.StdEncoding.DecodeString(node.String()) stdout.Write(content) } stderrs, _ := xpath(doc, "//rsp:Stream[@Name='stderr']") for _, node := range stderrs { content, _ := base64.StdEncoding.DecodeString(node.String()) stderr.Write(content) } ended, _ := any(doc, "//*[@State='http://schemas.microsoft.com/wbem/wsman/1/windows/shell/CommandState/Done']") if ended { finished = ended if exitBool, _ := any(doc, "//rsp:ExitCode"); exitBool { exit, _ := first(doc, "//rsp:ExitCode") exitCode, _ = strconv.Atoi(exit) } } else { finished = false } return }
func Test_executing_a_regex_command(t *testing.T) { w := &wsman{} id := w.HandleCommand(MatchPattern(`echo .* >> C:\file.cmd`), func(out, err io.Writer) int { return 0 }) res := httptest.NewRecorder() req, _ := http.NewRequest("POST", "", strings.NewReader(fmt.Sprintf(` <env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:rsp="http://schemas.microsoft.com/wbem/wsman/1/windows/shell"> <env:Header> <a:Action mustUnderstand="true">http://schemas.xmlsoap.org/ws/2004/09/shell/Command</a:Action> </env:Header> <env:Body> <rsp:CommandLine><rsp:Command>"echo %d >> C:\file.cmd"</rsp:Command></rsp:CommandLine> </env:Body> </env:Envelope>`, uuid.NewV4().String()))) w.ServeHTTP(res, req) if res.Code != http.StatusOK { t.Errorf("Expected 200 OK but was %d.\n", res.Code) } env, err := xmlpath.Parse(res.Body) if err != nil { t.Error("Couldn't compile the SOAP response.") } xpath, _ := xmlpath.CompileWithNamespace( "//rsp:CommandId", soap.GetAllNamespaces()) result, _ := xpath.String(env) if result != id { t.Errorf("Expected CommandId=%s but was \"%s\"", id, result) } }
func parseXPath(doc *dom.Document, request string) (*xmlpath.Node, *xmlpath.Path, error) { content := strings.NewReader(doc.String()) node, err := xmlpath.Parse(content) if err != nil { return nil, nil, err } path, err := xmlpath.CompileWithNamespace(request, soap.GetAllNamespaces()) if err != nil { return nil, nil, err } return node, path, nil }
func assertXPath(c *C, node *dom.Document, request string, expected string) { content := strings.NewReader(node.String()) path, err := xmlpath.CompileWithNamespace(request, soap.GetAllNamespaces()) if err != nil { c.Fatalf("Xpath %s gives error %s", request, err) } var root *xmlpath.Node root, err = xmlpath.Parse(content) if err != nil { c.Fatalf("Xpath %s gives error %s", request, err) } var e string var ok bool e, ok = path.String(root) c.Assert(ok, Equals, true) c.Assert(e, Equals, expected) }
func Test_creating_a_shell(t *testing.T) { w := &wsman{} res := httptest.NewRecorder() req, _ := http.NewRequest("POST", "", strings.NewReader(` <env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing"> <env:Header> <a:Action mustUnderstand="true">http://schemas.xmlsoap.org/ws/2004/09/transfer/Create</a:Action> </env:Header> <env:Body> <rsp:Shell> <rsp:InputStream>stdin</rsp:InputStream> <rsp:OutputStreams>stdout stderr</rsp:OutputStreams> </rsp:Shell> </env:Body> </env:Envelope>`)) w.ServeHTTP(res, req) if res.Code != http.StatusOK { t.Errorf("Expected 200 OK but was %d.\n", res.Code) } if contentType := res.HeaderMap.Get("Content-Type"); contentType != "application/soap+xml" { t.Errorf("Expected ContentType application/soap+xml was %s.\n", contentType) } env, err := xmlpath.Parse(res.Body) if err != nil { t.Error("Couldn't compile the SOAP response.") } xpath, _ := xmlpath.CompileWithNamespace( "//rsp:ShellId", soap.GetAllNamespaces()) if _, found := xpath.String(env); !found { t.Error("Expected a Shell identifier.") } }
func Test_receiving_command_results(t *testing.T) { w := &wsman{} id := w.HandleCommand(MatchText("echo tacos"), func(out, err io.Writer) int { out.Write([]byte("tacos")) return 0 }) res := httptest.NewRecorder() req, _ := http.NewRequest("POST", "", strings.NewReader(fmt.Sprintf(` <env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:rsp="http://schemas.microsoft.com/wbem/wsman/1/windows/shell"> <env:Header> <a:Action mustUnderstand="true">http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Receive</a:Action> </env:Header> <env:Body> <rsp:Receive><rsp:DesiredStream CommandId="%s">stdout stderr</rsp:DesiredStream></rsp:Receive> </env:Body> </env:Envelope>`, id))) w.ServeHTTP(res, req) if res.Code != http.StatusOK { t.Errorf("Expected 200 OK but was %d.\n", res.Code) } env, err := xmlpath.Parse(res.Body) if err != nil { t.Error("Couldn't compile the SOAP response.") } xpath, _ := xmlpath.CompileWithNamespace("//rsp:ReceiveResponse", soap.GetAllNamespaces()) iter := xpath.Iter(env) if !iter.Next() { t.Error("Expected a ReceiveResponse element.") } xresp := iter.Node() xpath, _ = xmlpath.CompileWithNamespace( fmt.Sprintf("rsp:Stream[@CommandId='%s']", id), soap.GetAllNamespaces()) iter = xpath.Iter(xresp) if !iter.Next() || !nodeHasAttribute(iter.Node(), "Name", "stdout") || iter.Node().String() != "dGFjb3M=" { t.Error("Expected an stdout Stream with the text \"dGFjb3M=\".") } if !iter.Next() || !nodeHasAttribute(iter.Node(), "Name", "stdout") || !nodeHasAttribute(iter.Node(), "End", "true") { t.Error("Expected an stdout Stream with an \"End\" attribute.") } if !iter.Next() || !nodeHasAttribute(iter.Node(), "Name", "stderr") || !nodeHasAttribute(iter.Node(), "End", "true") { t.Error("Expected an stderr Stream with an \"End\" attribute.") } xpath, _ = xmlpath.CompileWithNamespace( "//rsp:CommandState[@State='http://schemas.microsoft.com/wbem/wsman/1/windows/shell/CommandState/Done']", soap.GetAllNamespaces()) if _, found := xpath.String(env); !found { t.Error("Expected CommandState=\"Done\"") } xpath, _ = xmlpath.CompileWithNamespace("//rsp:CommandState/rsp:ExitCode", soap.GetAllNamespaces()) if code, _ := xpath.String(env); code != "0" { t.Errorf("Expected ExitCode=0 but found \"%s\"\n", code) } }
func ParseExecuteCommandResponse(response string) (commandId string, err error) { doc, err := xmlpath.Parse(strings.NewReader(response)) commandId, err = first(doc, "//rsp:CommandId") return }
func ParseOpenShellResponse(response string) (shellId string, err error) { doc, err := xmlpath.Parse(strings.NewReader(response)) shellId, err = first(doc, "//w:Selector[@Name='ShellId']") return }
func (w *wsman) ServeHTTP(rw http.ResponseWriter, r *http.Request) { rw.Header().Add("Content-Type", "application/soap+xml") defer r.Body.Close() env, err := xmlpath.Parse(r.Body) if err != nil { return } action := readAction(env) switch { case strings.HasSuffix(action, "transfer/Create"): // create a new shell rw.Write([]byte(` <env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:rsp="http://schemas.microsoft.com/wbem/wsman/1/windows/shell"> <rsp:ShellId>123</rsp:ShellId> </env:Envelope>`)) case strings.HasSuffix(action, "shell/Command"): // execute on behalf of the client text := readCommand(env) cmd := w.CommandByText(text) if cmd == nil { fmt.Printf("I don't know this command: Command=%s\n", text) rw.WriteHeader(http.StatusInternalServerError) return } rw.Write([]byte(fmt.Sprintf(` <env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:rsp="http://schemas.microsoft.com/wbem/wsman/1/windows/shell"> <rsp:CommandId>%s</rsp:CommandId> </env:Envelope>`, cmd.id))) case strings.HasSuffix(action, "shell/Receive"): // client ready to receive the results id := readCommandIDFromDesiredStream(env) cmd := w.CommandByID(id) if cmd == nil { fmt.Printf("I don't know this command: CommandId=%s\n", id) rw.WriteHeader(http.StatusInternalServerError) return } stdout := new(bytes.Buffer) stderr := new(bytes.Buffer) result := cmd.handler(stdout, stderr) content := base64.StdEncoding.EncodeToString(stdout.Bytes()) rw.Write([]byte(fmt.Sprintf(` <env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:rsp="http://schemas.microsoft.com/wbem/wsman/1/windows/shell"> <rsp:ReceiveResponse> <rsp:Stream Name="stdout" CommandId="%s">%s</rsp:Stream> <rsp:Stream Name="stdout" CommandId="%s" End="true"></rsp:Stream> <rsp:Stream Name="stderr" CommandId="%s" End="true"></rsp:Stream> <rsp:CommandState State="http://schemas.microsoft.com/wbem/wsman/1/windows/shell/CommandState/Done"> <rsp:ExitCode>%d</rsp:ExitCode> </rsp:CommandState> </rsp:ReceiveResponse> </env:Envelope>`, id, content, id, id, result))) case strings.HasSuffix(action, "shell/Signal"): // end of the shell command rw.WriteHeader(http.StatusOK) case strings.HasSuffix(action, "transfer/Delete"): // end of the session rw.WriteHeader(http.StatusOK) default: fmt.Printf("I don't know this action: %s\n", action) rw.WriteHeader(http.StatusInternalServerError) } }