Esempio n. 1
2
func fetchPubkey() (*rsa.PublicKey, error) {
	resp, err := http.Get("https://s3o.ft.com/publickey")
	if err != nil || resp.StatusCode != http.StatusOK {
		return nil, errors.New("failed to read s3o public key")
	}
	defer func() {
		_, _ = io.Copy(ioutil.Discard, resp.Body)
		_ = resp.Body.Close()
	}()
	var buf bytes.Buffer
	if _, err := io.Copy(&buf, resp.Body); err != nil {
		return nil, errors.New("failed to read s3o public key")
	}
	dec := make([]byte, 8192) // should be enough for a while.
	i, err := base64.StdEncoding.Decode(dec, buf.Bytes())
	if err != nil {
		return nil, errors.New("failed to base64 decode s3o public key")
	}

	pub, err := x509.ParsePKIXPublicKey(dec[0:i])
	if err != nil {
		return nil, errors.New("failed to parse s3o public key")
	}
	return pub.(*rsa.PublicKey), nil
}
Esempio n. 2
1
func handleClient(p1, p2 net.Conn) {
	log.Println("stream opened")
	defer log.Println("stream closed")
	defer p1.Close()
	defer p2.Close()

	// start tunnel
	p1die := make(chan struct{})
	go func() {
		io.Copy(p1, p2)
		close(p1die)
	}()

	p2die := make(chan struct{})
	go func() {
		io.Copy(p2, p1)
		close(p2die)
	}()

	// wait for tunnel termination
	select {
	case <-p1die:
	case <-p2die:
	}
}
Esempio n. 3
1
func (tp *TCPProxy) serve(in net.Conn) {
	var (
		err error
		out net.Conn
	)

	for i := 0; i < tp.numRemotes(); i++ {
		remote := tp.pick()
		if !remote.isActive() {
			continue
		}
		// TODO: add timeout
		out, err = net.Dial("tcp", remote.addr)
		if err == nil {
			break
		}
		remote.inactivate()
		plog.Warningf("deactivated endpoint [%s] due to %v for %v", remote.addr, err, tp.MonitorInterval)
	}

	if out == nil {
		in.Close()
		return
	}

	go func() {
		io.Copy(in, out)
		in.Close()
		out.Close()
	}()

	io.Copy(out, in)
	out.Close()
	in.Close()
}
Esempio n. 4
1
func (container *Container) startPty() error {
	ptyMaster, ptySlave, err := pty.Open()
	if err != nil {
		return err
	}
	container.ptyMaster = ptyMaster
	container.cmd.Stdout = ptySlave
	container.cmd.Stderr = ptySlave

	// Copy the PTYs to our broadcasters
	go func() {
		defer container.stdout.CloseWriters()
		utils.Debugf("[startPty] Begin of stdout pipe")
		io.Copy(container.stdout, ptyMaster)
		utils.Debugf("[startPty] End of stdout pipe")
	}()

	// stdin
	if container.Config.OpenStdin {
		container.cmd.Stdin = ptySlave
		container.cmd.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true}
		go func() {
			defer container.stdin.Close()
			utils.Debugf("[startPty] Begin of stdin pipe")
			io.Copy(ptyMaster, container.stdin)
			utils.Debugf("[startPty] End of stdin pipe")
		}()
	}
	if err := container.cmd.Start(); err != nil {
		return err
	}
	ptySlave.Close()
	return nil
}
Esempio n. 5
1
func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Println(r.URL.Path)
		p := r.URL.Path[1:]
		if p == "" {
			p = "index.html"
		}
		fi, err := os.Open(p)
		if err == nil {
			defer fi.Close()
			io.Copy(w, fi)
			return
		}

		resp, err := http.Get("http://localhost:8080" + r.URL.Path)
		if err != nil {
			http.Error(w, err.Error(), 404)
			return
		}

		defer resp.Body.Close()
		io.Copy(w, resp.Body)
	})

	panic(http.ListenAndServe(":9117", nil))
}
Esempio n. 6
1
// pipeStream relays over a stream to a remote peer. It's like `cat`
func (rs *RelayService) pipeStream(src, dst peer.ID, s inet.Stream) error {
	s2, err := rs.openStreamToPeer(dst)
	if err != nil {
		return fmt.Errorf("failed to open stream to peer: %s -- %s", dst, err)
	}

	if err := WriteHeader(s2, src, dst); err != nil {
		return err
	}

	// connect the series of tubes.
	done := make(chan retio, 2)
	go func() {
		n, err := io.Copy(s2, s)
		done <- retio{n, err}
	}()
	go func() {
		n, err := io.Copy(s, s2)
		done <- retio{n, err}
	}()

	r1 := <-done
	r2 := <-done
	log.Infof("%s relayed %d/%d bytes between %s and %s", rs.host.ID(), r1.n, r2.n, src, dst)

	if r1.err != nil {
		return r1.err
	}
	return r2.err
}
Esempio n. 7
0
File: term.go Progetto: 98pm/docker
func (t *dockerTtyTerm) Attach(cmd *exec.Cmd) error {
	go io.Copy(t.pipes.Stdout, t.MasterPty)
	if t.pipes.Stdin != nil {
		go io.Copy(t.MasterPty, t.pipes.Stdin)
	}
	return nil
}
Esempio n. 8
0
func (f *Fuzzer) run() error {
	f.cmd = exec.Command(f.path(),
		"-o", "output",
		"-i", "input",
		"-S", f.Id,
		"./target",
	)
	stdout, err := f.cmd.StdoutPipe()
	if err != nil {
		log.Fatalf("Couldn't get stdout handle", err)
	}

	stderr, err := f.cmd.StderrPipe()
	if err != nil {
		log.Fatalf("Couldn't get stderr handle", err)
	}

	if err := f.cmd.Start(); err != nil {
		log.Fatalf("Couldn't start fuzzer", err)
	}

	go func() {
		io.Copy(os.Stdout, stdout)
	}()

	go func() {
		io.Copy(os.Stderr, stderr)
	}()

	log.Printf("Waiting for fuzzer to exit")
	f.started = true

	return f.cmd.Wait()
}
Esempio n. 9
0
func runBin(bin string, args []string) error {
	command = exec.Command(bin, args...)
	stdout, err := command.StdoutPipe()
	if err != nil {
		return err
	}
	stderr, err := command.StderrPipe()
	if err != nil {
		return err
	}

	err = command.Start()
	if err != nil {
		return err
	}

	startTime = time.Now()

	go io.Copy(writer, stdout)
	go io.Copy(writer, stderr)

	go func() {
		done <- command.Wait()
	}()

	return nil
}
Esempio n. 10
0
func (t *TtyConsole) AttachPipes(command *exec.Cmd, pipes *execdriver.Pipes) error {
	command.Stdout = t.SlavePty
	command.Stderr = t.SlavePty

	go func() {
		if wb, ok := pipes.Stdout.(interface {
			CloseWriters() error
		}); ok {
			defer wb.CloseWriters()
		}

		io.Copy(pipes.Stdout, t.MasterPty)
	}()

	if pipes.Stdin != nil {
		command.Stdin = t.SlavePty
		command.SysProcAttr.Setctty = true

		go func() {
			io.Copy(t.MasterPty, pipes.Stdin)

			pipes.Stdin.Close()
		}()
	}
	return nil
}
Esempio n. 11
0
func (ln wslistener) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	websocket.Handler(func(wcon *websocket.Conn) {

		// It appears we mustn't pass wcon to external users as is.
		// We'll pass a pipe instead, because the only way to know if a wcon
		// was closed remotely is to read from it until EOF.

		ch := make(chan struct{})
		p1, p2 := net.Pipe()

		go func() {
			io.Copy(wcon, p1)
			wcon.Close()
		}()
		go func() {
			io.Copy(p1, wcon)
			p1.Close()

			close(ch)
		}()

		select {
		case ln.acceptCh <- p2:
		case <-ln.closeCh:
		}

		// As soon as we return from this function, websocket library will
		// close wcon. So we'll wait until p2 or wcon is closed.
		<-ch

	}).ServeHTTP(w, r)
}
Esempio n. 12
0
// Receive receives single frame from ws, unmarshaled by cd.Unmarshal and stores in v.
func (cd Codec) Receive(ws *Conn, v interface{}) (err error) {
	ws.rio.Lock()
	defer ws.rio.Unlock()
	if ws.frameReader != nil {
		_, err = io.Copy(ioutil.Discard, ws.frameReader)
		if err != nil {
			return err
		}
		ws.frameReader = nil
	}

	var data bytes.Buffer
again:
	frame, err := ws.frameReaderFactory.NewFrameReader()
	if err != nil {
		return err
	}
	frame, err = ws.frameHandler.HandleFrame(frame)
	if err != nil {
		return err
	}
	if frame == nil {
		goto again
	}
	payloadType := frame.PayloadType()

	if _, err = io.Copy(&data, frame); err != nil {
		return err
	}
	if !frame.IsFin() {
		goto again
	}
	return cd.Unmarshal(data.Bytes(), payloadType, v)
}
Esempio n. 13
0
func serve(listener net.Listener) {
	certificate, err := tls.LoadX509KeyPair("server.crt", "server.key")
	if err != nil {
		panic(err)
	}

	for {
		c, err := listener.Accept()
		if err != nil {
			log.Printf("accept: %s", err)
		}

		tlsConn := tls.Server(c, &tls.Config{
			Certificates: []tls.Certificate{certificate},
		})
		if err := tlsConn.Handshake(); err != nil {
			log.Printf("tls: %s", err)
		}

		go func() {
			io.Copy(os.Stdout, tlsConn)
			c.Close()
		}()

		go func() {
			io.Copy(tlsConn, os.Stdin)
			c.Close()
		}()
	}
}
Esempio n. 14
0
func (d *simpleDatabase) ImportFromReader(r io.Reader) network.StaticId {
	f, err := ioutil.TempFile(d.dirname, "import-")
	if err != nil {
		panic(err)
	}
	len, err2 := io.Copy(f, r)
	if err2 != nil {
		panic(err2)
	}
	hasher := hashtree.NewFile()
	hashFile := d.innerHashListenerFile(hasher, hashtree.Bytes(len))

	f.Seek(0, os.SEEK_SET)
	io.Copy(hasher, f)
	id := network.StaticId{
		Hash:   hasher.Sum(nil),
		Length: &len,
	}
	f.Close()
	hashFile.Close()

	moveOrRemoveFile(f.Name(), d.fileNameForId(id))
	moveOrRemoveFile(hashFile.Name(), d.hashFileNameForId(id))
	return id
}
Esempio n. 15
0
func compile(w *World) *bytes.Buffer {
	ioutil.WriteFile(TEMPPATH+".go", []byte(w.source()), 0644)

	err := new(bytes.Buffer)

	re, e, _ := os.Pipe()

	os.ForkExec(
		bin+"/"+arch+"g",
		[]string{bin + "/" + arch + "g", "-o", TEMPPATH + ".6", TEMPPATH + ".go"},
		os.Environ(),
		"",
		[]*os.File{nil, e, nil})

	e.Close()
	io.Copy(err, re)

	if err.Len() > 0 {
		return err
	}

	re, e, _ = os.Pipe()
	os.ForkExec(
		bin+"/"+arch+"l",
		[]string{bin + "/" + arch + "l", "-o", TEMPPATH + "", TEMPPATH + ".6"},
		os.Environ(),
		"",
		[]*os.File{nil, e, nil})

	e.Close()
	io.Copy(err, re)

	return err
}
Esempio n. 16
0
func run() (*bytes.Buffer, *bytes.Buffer) {
	out := new(bytes.Buffer)
	err := new(bytes.Buffer)

	re, e, _ := os.Pipe()
	ro, o, _ := os.Pipe()
	os.ForkExec(
		TEMPPATH,
		[]string{TEMPPATH},
		os.Environ(),
		"",
		[]*os.File{nil, o, e})

	e.Close()
	io.Copy(err, re)

	if err.Len() > 0 {
		return nil, err
	}

	o.Close()
	io.Copy(out, ro)

	return out, err
}
func BenchmarkKiSS(b *testing.B) {
	go run_single_kiss_echo("127.0.0.1:1234")
	time.Sleep(time.Second)
	lololol, err := net.Dial("tcp", "127.0.0.1:1234")
	if err != nil {
		panic("wtf")
	}
	defer lololol.Close()
	xaxa, err := Obfs3fHandshake(lololol, false)
	if err != nil {
		panic(err.Error())
	}
	xaxa, err = TransportHandshake(dh_gen_key(2048), xaxa,
		func([]byte) bool { return true })
	xaxa, err = TransportHandshake(dh_gen_key(2048), xaxa,
		func([]byte) bool { return true })
	xaxa, err = TransportHandshake(dh_gen_key(2048), xaxa,
		func([]byte) bool { return true })
	xaxa, err = TransportHandshake(dh_gen_key(2048), xaxa,
		func([]byte) bool { return true })
	if err != nil {
		panic(err.Error())
	}
	defer xaxa.Close()
	fmt.Printf("xaxa!\n")
	listener, _ := net.Listen("tcp", "127.0.0.1:5555")
	client, _ := listener.Accept()
	defer client.Close()
	go io.Copy(client, xaxa)
	io.Copy(xaxa, client)
}
Esempio n. 18
0
func TestAsyncWriteTo(t *testing.T) {
	buf := ioutil.NopCloser(bytes.NewBufferString("Testbuffer"))
	ar, err := newAsyncReader(buf, 4, 10000)
	if err != nil {
		t.Fatal("error when creating:", err)
	}

	var dst = &bytes.Buffer{}
	n, err := io.Copy(dst, ar)
	if err != io.EOF {
		t.Fatal("error when reading:", err)
	}
	if n != 10 {
		t.Fatal("unexpected length, expected 10, got ", n)
	}

	// Should still return EOF
	n, err = io.Copy(dst, ar)
	if err != io.EOF {
		t.Fatal("expected io.EOF, got", err)
	}
	if n != 0 {
		t.Fatal("unexpected length, expected 0, got ", n)
	}

	err = ar.Close()
	if err != nil {
		t.Fatal("error when closing:", err)
	}
}
Esempio n. 19
0
File: ucat.go Progetto: rht/ipget
func netcat(c net.Conn) {
	log("piping stdio to connection")

	done := make(chan struct{})

	go func() {
		n, _ := io.Copy(c, os.Stdin)
		log("sent %d bytes", n)
		done <- struct{}{}
	}()
	go func() {
		n, _ := io.Copy(os.Stdout, c)
		log("received %d bytes", n)
		done <- struct{}{}
	}()

	// wait until we exit.
	sigc := make(chan os.Signal, 1)
	signal.Notify(sigc, syscall.SIGHUP, syscall.SIGINT,
		syscall.SIGTERM, syscall.SIGQUIT)
	select {
	case <-done:
	case <-sigc:
	}
}
Esempio n. 20
0
// Generate a human readable sha1 hash of the given file path
func hashFile(path string) (hashes FileInfo, err error) {
	f, err := os.Open(path)
	if err != nil {
		return
	}
	defer f.Close()

	reader := bufio.NewReader(f)

	if GetConfig().Hashes.SHA1 {
		sha1Hash := sha1.New()
		_, err = io.Copy(sha1Hash, reader)
		if err == nil {
			hashes.Sha1 = hex.EncodeToString(sha1Hash.Sum(nil))
		}
	}
	if GetConfig().Hashes.SHA256 {
		sha256Hash := sha256.New()
		_, err = io.Copy(sha256Hash, reader)
		if err == nil {
			hashes.Sha256 = hex.EncodeToString(sha256Hash.Sum(nil))
		}
	}
	if GetConfig().Hashes.MD5 {
		md5Hash := md5.New()
		_, err = io.Copy(md5Hash, reader)
		if err == nil {
			hashes.Md5 = hex.EncodeToString(md5Hash.Sum(nil))
		}
	}
	return
}
Esempio n. 21
0
func (container *Container) setupPty() error {
	ptyMaster, ptySlave, err := pty.Open()
	if err != nil {
		return err
	}
	container.ptyMaster = ptyMaster
	container.command.Stdout = ptySlave
	container.command.Stderr = ptySlave

	// Copy the PTYs to our broadcasters
	go func() {
		defer container.stdout.CloseWriters()
		utils.Debugf("startPty: begin of stdout pipe")
		io.Copy(container.stdout, ptyMaster)
		utils.Debugf("startPty: end of stdout pipe")
	}()

	// stdin
	if container.Config.OpenStdin {
		container.command.Stdin = ptySlave
		container.command.SysProcAttr.Setctty = true
		go func() {
			defer container.stdin.Close()
			utils.Debugf("startPty: begin of stdin pipe")
			io.Copy(ptyMaster, container.stdin)
			utils.Debugf("startPty: end of stdin pipe")
		}()
	}
	return nil
}
Esempio n. 22
0
func runDevelopmentServer(path string) {
	if len(path) < 1 {
		printHelpCommand("A GAEA project directory must be specified:")
		return
	}

	verified := verifyAppServerExists()
	if !verified {
		return
	}
	fmt.Fprintln(os.Stdout, "Running Dev App Server through GAEA...")
	cmd := exec.Command("dev_appserver.py", path)
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		log.Fatal(err)
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		log.Fatal(err)
	}
	if err := cmd.Start(); err != nil {
		log.Fatal(err)
	}
	go io.Copy(os.Stdout, stdout)
	go io.Copy(os.Stderr, stderr)
	if err := cmd.Wait(); err != nil {
		log.Fatal(err)
	}
}
Esempio n. 23
0
func main() {
	if len(os.Args) < 2 {
		fmt.Printf("Please specify a host.\n")
		os.Exit(1)
	}
	if len(os.Args) > 3 {
		fmt.Printf("Too many arguments.\n")
		os.Exit(1)
	}
	host := os.Args[1]
	port := "443"
	if len(os.Args) == 3 {
		port = os.Args[2]
	}
	config := &tls.Config{
		InsecureSkipVerify: true,
	}
	conn, err := tls.Dial("tcp", host+":"+port, config)
	if err != nil {
		fmt.Printf("Error dialing host: %v\n", err)
		os.Exit(1)
	}
	go io.Copy(conn, os.Stdin)
	io.Copy(os.Stdout, conn)
}
Esempio n. 24
0
func (dict *Dict) Fprint(w io.Writer) error {
	coeffLen := dict.longestCoeff("%+-.4g")
	coeff := "%+-" + fmt.Sprintf("%d", coeffLen) + ".4g"
	indexLen := dict.longestIndex("%d")
	index := "%" + fmt.Sprintf("%d", indexLen) + "d"

	var b bytes.Buffer
	for i := range dict.Basic {
		fmt.Fprintf(&b, "x"+index+" =", dict.Basic[i])
		fmt.Fprintf(&b, " "+coeff, dict.B[i])
		for j := range dict.NonBasic {
			fmt.Fprintf(&b, "  "+coeff+" x"+index, dict.A[i][j], dict.NonBasic[j])
		}
		b.WriteString("\n")
		if _, err := io.Copy(w, &b); err != nil {
			return err
		}
	}

	spacer := strings.Repeat(" ", indexLen)
	fmt.Fprint(&b, "z"+spacer+" =")
	fmt.Fprintf(&b, " "+coeff, dict.D)
	for j := range dict.NonBasic {
		fmt.Fprintf(&b, "  "+coeff+" x"+index, dict.C[j], dict.NonBasic[j])
	}
	b.WriteString("\n")
	if _, err := io.Copy(w, &b); err != nil {
		return err
	}
	return nil
}
Esempio n. 25
0
// RunCommandWithStdoutStderr execs a command and returns its output.
func RunCommandWithStdoutStderr(cmd *exec.Cmd) (bytes.Buffer, bytes.Buffer, error) {
	var stdout, stderr bytes.Buffer
	stderrPipe, err := cmd.StderrPipe()
	stdoutPipe, err := cmd.StdoutPipe()

	cmd.Env = os.Environ()
	if err != nil {
		fmt.Println("error at io pipes")
	}

	err = cmd.Start()
	if err != nil {
		fmt.Println("error at command start")
	}

	go func() {
		io.Copy(&stdout, stdoutPipe)
		fmt.Println(stdout.String())
	}()
	go func() {
		io.Copy(&stderr, stderrPipe)
		fmt.Println(stderr.String())
	}()
	time.Sleep(2000 * time.Millisecond)
	err = cmd.Wait()
	if err != nil {
		fmt.Println("error at command wait")
	}
	return stdout, stderr, err
}
Esempio n. 26
0
func tcpShim(inbound, outbound *net.TCPConn, connEvent *events.Connection, eh events.Handler) error {
	eh.Connection(connEvent)
	ch := make(chan error, 1)
	go func() {
		var err error
		defer func() { ch <- err }()
		_, err = io.Copy(inbound, outbound)
		outbound.CloseRead()
		inbound.CloseWrite()
	}()

	_, err1 := io.Copy(outbound, inbound)
	inbound.CloseRead()
	outbound.CloseWrite()

	err2 := <-ch
	inbound.Close()
	outbound.Close()

	if err1 != nil {
		return err1
	} else {
		return err2
	}
}
Esempio n. 27
0
File: runner.go Progetto: achiku/wbs
// Serve execute binary with configured options
func (r *WbsRunner) Serve() error {
	evaledCommand, err := shellParser.Parse(r.StartCommand)
	evaledOptions, err := shellParser.Parse(strings.Join(r.StartOptions, " "))
	if err != nil {
		return err
	}
	runnerLog("starting server: %s %s", evaledCommand, evaledOptions)
	cmd := exec.Command(evaledCommand[0], evaledOptions...)
	stderr, err := cmd.StderrPipe()
	if err != nil {
		runnerLog(err.Error())
		return err
	}
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		runnerLog(err.Error())
		return err
	}
	rl := runnerLogWriter{}
	go io.Copy(rl, stderr)
	go io.Copy(rl, stdout)

	err = cmd.Start()
	if err != nil {
		runnerLog(err.Error())
		return err
	}
	r.Pid = cmd.Process.Pid
	runnerLog("server started: PID %d", r.Pid)
	return nil
}
Esempio n. 28
0
func copyLoop(a net.Conn, b net.Conn) error {
	// Note: b is always the pt connection.  a is the SOCKS/ORPort connection.
	errChan := make(chan error, 2)

	var wg sync.WaitGroup
	wg.Add(2)

	go func() {
		defer wg.Done()
		defer b.Close()
		defer a.Close()
		_, err := io.Copy(b, a)
		errChan <- err
	}()
	go func() {
		defer wg.Done()
		defer a.Close()
		defer b.Close()
		_, err := io.Copy(a, b)
		errChan <- err
	}()

	// Wait for both upstream and downstream to close.  Since one side
	// terminating closes the other, the second error in the channel will be
	// something like EINVAL (though io.Copy() will swallow EOF), so only the
	// first error is returned.
	wg.Wait()
	if len(errChan) > 0 {
		return <-errChan
	}

	return nil
}
Esempio n. 29
0
func (srv *Server) CmdLogs(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
	cmd := rcli.Subcmd(stdout, "logs", "CONTAINER", "Fetch the logs of a container")
	if err := cmd.Parse(args); err != nil {
		return nil
	}
	if cmd.NArg() != 1 {
		cmd.Usage()
		return nil
	}
	name := cmd.Arg(0)
	if container := srv.runtime.Get(name); container != nil {
		logStdout, err := container.ReadLog("stdout")
		if err != nil {
			return err
		}
		logStderr, err := container.ReadLog("stderr")
		if err != nil {
			return err
		}
		// FIXME: Interpolate stdout and stderr instead of concatenating them
		// FIXME: Differentiate stdout and stderr in the remote protocol
		if _, err := io.Copy(stdout, logStdout); err != nil {
			return err
		}
		if _, err := io.Copy(stdout, logStderr); err != nil {
			return err
		}
		return nil
	}
	return fmt.Errorf("No such container: %s", cmd.Arg(0))
}
Esempio n. 30
-1
func pipe(ch ssh.Channel, client *ssh.Client, session *ssh.Session, command string) (int, error) {
	targetStderr, err := session.StderrPipe()
	if err != nil {
		return -1, errors.New("fail to pipe stderr: " + err.Error())
	}
	targetStdout, err := session.StdoutPipe()
	if err != nil {
		return -1, errors.New("fail to pipe stdout: " + err.Error())
	}
	targetStdin, err := session.StdinPipe()
	if err != nil {
		return -1, errors.New("fail to pipe stdin: " + err.Error())
	}

	go io.Copy(targetStdin, ch)
	go io.Copy(ch.Stderr(), targetStderr)
	go io.Copy(ch, targetStdout)

	err = session.Start(command)
	if err != nil {
		ch.Write([]byte("Error when starting '" + command + "': " + err.Error()))
		ch.Close()
	}

	err = session.Wait()
	if err != nil {
		if err, ok := err.(*ssh.ExitError); ok {
			return err.ExitStatus(), nil
		} else {
			return -1, errors.New("failed to wait ssh command: " + err.Error())
		}
	}

	return 0, nil
}