fix exec and shell pty issue

ws
dingjun 4 years ago
parent d8a17f217f
commit bcf8b9f481

@ -123,21 +123,20 @@ func (cc *Client) Close() {
} }
// RunCmd run a single command on server // RunCmd run a single command on server
func (cc *Client) RunCmd(cmd string) ([]byte, error) { func (cc *Client) RunCmd(cmd string) error {
log.Debugf("run command %s", cmd) log.Debugf("run command %s", cmd)
session, err := cc.client.NewSession() session, err := cc.client.NewSession()
if err != nil { if err != nil {
log.Debugf("command exited with error: %s", err.Error()) log.Debugf("new session error: %s", err.Error())
} else { return err
log.Debugf("command exited with no error")
} }
if err != nil { session.Stdin = os.Stdin
return nil, err session.Stderr = os.Stderr
} session.Stdout = os.Stdout
d, err := session.CombinedOutput(cmd) err = session.Run(cmd)
session.Close() session.Close()
return d, err return err
} }
// Shell start a login shell on server // Shell start a login shell on server

@ -5,8 +5,15 @@ import (
"net" "net"
"testing" "testing"
"time" "time"
"github.com/fangdingjun/go-log"
) )
func TestTimedOutConn(t *testing.T) {
testTimedOutConn(t, true)
testTimedOutConn(t, false)
}
func testTimedOutConn(t *testing.T, _timeout bool) { func testTimedOutConn(t *testing.T, _timeout bool) {
l, err := net.Listen("tcp", "127.0.0.1:0") l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil { if err != nil {
@ -23,7 +30,8 @@ func testTimedOutConn(t *testing.T, _timeout bool) {
go func() { go func() {
s, err := l.Accept() s, err := l.Accept()
if err != nil { if err != nil {
t.Fatalf("accept failed: %s", err) log.Errorf("accept failed: %s", err)
return
} }
defer s.Close() defer s.Close()
@ -34,7 +42,8 @@ func testTimedOutConn(t *testing.T, _timeout bool) {
n, err := sConn.Read(buf) n, err := sConn.Read(buf)
if err != nil { if err != nil {
t.Fatalf("server read failed: %s", err) log.Errorf("server read failed: %s", err)
return
} }
if _timeout { if _timeout {

@ -4,13 +4,13 @@ go 1.13
require ( require (
github.com/bgentry/speakeasy v0.1.0 github.com/bgentry/speakeasy v0.1.0
github.com/creack/pty v1.1.7
github.com/fangdingjun/go-log v0.0.0-20190821073628-ae332053d6dc github.com/fangdingjun/go-log v0.0.0-20190821073628-ae332053d6dc
github.com/fangdingjun/protolistener v0.0.0-20190821093313-6d5d2138f296 github.com/fangdingjun/protolistener v0.0.0-20190821093313-6d5d2138f296
github.com/fangdingjun/socks-go v0.0.0-20180926100003-fc6f0a9ee1f4 github.com/fangdingjun/socks-go v0.0.0-20180926100003-fc6f0a9ee1f4
github.com/go-yaml/yaml v2.1.0+incompatible github.com/go-yaml/yaml v2.1.0+incompatible
github.com/kr/fs v0.1.0 github.com/kr/fs v0.1.0
github.com/kr/pretty v0.1.0 // indirect github.com/kr/pretty v0.1.0 // indirect
github.com/kr/pty v1.1.8
github.com/pkg/errors v0.8.1 // indirect github.com/pkg/errors v0.8.1 // indirect
github.com/pkg/sftp v1.10.0 github.com/pkg/sftp v1.10.0
github.com/stretchr/testify v1.4.0 // indirect github.com/stretchr/testify v1.4.0 // indirect

@ -17,8 +17,6 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/pires/go-proxyproto v0.0.0-20190111085350-4d51b51e3bfc h1:lNOt1SMsgHXTdpuGw+RpnJtzUcCb/oRKZP65pBy9pr8= github.com/pires/go-proxyproto v0.0.0-20190111085350-4d51b51e3bfc h1:lNOt1SMsgHXTdpuGw+RpnJtzUcCb/oRKZP65pBy9pr8=

@ -331,12 +331,9 @@ func main() {
if !cfg.NotRunCmd { if !cfg.NotRunCmd {
if cmd != "" { if cmd != "" {
if d, err := client.RunCmd(cmd); err != nil { if err := client.RunCmd(cmd); err != nil {
log.Errorln(err) log.Errorln(err)
hasErr = true hasErr = true
} else {
//log.Printf("%s", string(d))
fmt.Printf("%s", string(d))
} }
} else { } else {
if err := client.Shell(); err != nil { if err := client.Shell(); err != nil {

@ -152,7 +152,6 @@ func main() {
defer c.Close() defer c.Close()
sc, err := obfssh.NewServer(c, config, sconf) sc, err := obfssh.NewServer(c, config, sconf)
if err != nil { if err != nil {
c.Close()
log.Errorf("%s", err.Error()) log.Errorf("%s", err.Error())
return return
} }

@ -1,14 +0,0 @@
// +build linux darwin
package obfssh
import (
"io"
"os/exec"
"github.com/kr/pty"
)
func startPty(cmd *exec.Cmd) (io.ReadWriteCloser, error) {
return pty.Start(cmd)
}

@ -1,11 +0,0 @@
package obfssh
import (
"errors"
"io"
"os/exec"
)
func startPty(cmd *exec.Cmd) (io.ReadWriteCloser, error) {
return nil, errors.New("not implement")
}

@ -4,11 +4,13 @@ import (
"fmt" "fmt"
"io" "io"
"net" "net"
"os"
"os/exec" "os/exec"
"runtime" "runtime"
"syscall" "syscall"
"time" "time"
"github.com/creack/pty"
"github.com/fangdingjun/go-log" "github.com/fangdingjun/go-log"
"github.com/pkg/sftp" "github.com/pkg/sftp"
"golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh"
@ -146,6 +148,22 @@ type exitStatus struct {
Status uint32 Status uint32
} }
type ptyReq struct {
Term string
Columns uint32
Rows uint32
Width uint32
Height uint32
Mode string
}
type windowChange struct {
Columns uint32
Rows uint32
Width uint32
Height uint32
}
func (sc *Server) handleSession(newch ssh.NewChannel) { func (sc *Server) handleSession(newch ssh.NewChannel) {
ch, req, err := newch.Accept() ch, req, err := newch.Accept()
if err != nil { if err != nil {
@ -153,11 +171,14 @@ func (sc *Server) handleSession(newch ssh.NewChannel) {
return return
} }
var _cmd args
ret := false ret := false
var _cmd args
var cmd *exec.Cmd var cmd *exec.Cmd
var env []string var env []string
var _ptyReq ptyReq
var _windowChange windowChange
var _pty, _tty *os.File
for r := range req { for r := range req {
switch r.Type { switch r.Type {
@ -168,6 +189,7 @@ func (sc *Server) handleSession(newch ssh.NewChannel) {
log.Debugf("handle sftp request") log.Debugf("handle sftp request")
go serveSFTP(ch) go serveSFTP(ch)
} else { } else {
ret = false
log.Debugln("subsystem", _cmd.Arg, "not support") log.Debugln("subsystem", _cmd.Arg, "not support")
} }
} else { } else {
@ -182,7 +204,7 @@ func (sc *Server) handleSession(newch ssh.NewChannel) {
cmd = exec.Command("bash", "-l") cmd = exec.Command("bash", "-l")
} }
cmd.Env = env cmd.Env = env
go handleShell(cmd, ch) go handleShell(cmd, ch, _pty, _tty)
case "signal": case "signal":
log.Debugln("got signal") log.Debugln("got signal")
ret = true ret = true
@ -204,6 +226,35 @@ func (sc *Server) handleSession(newch ssh.NewChannel) {
} }
case "pty-req": case "pty-req":
ret = true ret = true
err = ssh.Unmarshal(r.Payload, &_ptyReq)
if err != nil {
log.Errorln(err)
ret = false
}
log.Debugf("pty req %+v", _ptyReq)
if err == nil && (runtime.GOOS == "unix" || runtime.GOOS == "linux") {
_pty, _tty, err = pty.Open()
if err != nil {
log.Errorln(err)
ret = false
} else {
env = append(env, fmt.Sprintf("TERM=%s", _ptyReq.Term))
size, err := pty.GetsizeFull(_pty)
if err == nil {
log.Debugf("term size %+v", size)
size.Rows = uint16(_ptyReq.Rows)
size.Cols = uint16(_ptyReq.Columns)
if err = pty.Setsize(_pty, size); err != nil {
log.Errorln(err)
}
if err = pty.Setsize(_tty, size); err != nil {
log.Errorln(err)
}
} else {
log.Errorln(err)
}
}
}
case "env": case "env":
var arg envArgs var arg envArgs
ret = true ret = true
@ -216,6 +267,28 @@ func (sc *Server) handleSession(newch ssh.NewChannel) {
} }
case "window-change": case "window-change":
ret = true ret = true
err = ssh.Unmarshal(r.Payload, &_windowChange)
if err != nil {
ret = false
log.Errorln(err)
}
if err == nil && _pty != nil {
size, err := pty.GetsizeFull(_pty)
if err == nil {
log.Debugf("term size %+v", size)
size.Rows = uint16(_ptyReq.Rows)
size.Cols = uint16(_ptyReq.Columns)
if err = pty.Setsize(_pty, size); err != nil {
log.Errorln(err)
}
if err = pty.Setsize(_tty, size); err != nil {
log.Errorln(err)
}
} else {
log.Errorln(err)
}
}
log.Debugf("window change %+v", _windowChange)
default: default:
ret = false ret = false
} }
@ -228,32 +301,36 @@ func (sc *Server) handleSession(newch ssh.NewChannel) {
} }
} }
func handleShell(cmd *exec.Cmd, ch ssh.Channel) { func handleShell(cmd *exec.Cmd, ch ssh.Channel, _pty, _tty *os.File) {
defer ch.Close() defer func() {
ch.Close()
if _pty != nil {
_pty.Close()
_tty.Close()
}
}()
var _pty io.ReadWriteCloser
var err error var err error
log.Infoln("start shell") log.Infoln("start shell")
//_pty, err = pty.Start(cmd) if _tty != nil {
if runtime.GOOS == "unix" || runtime.GOOS == "linux" { cmd.Stderr = _tty
_pty, err = startPty(cmd) cmd.Stdout = _tty
if err != nil { cmd.Stdin = _tty
log.Debugln("start pty", err) if cmd.SysProcAttr == nil {
ch.SendRequest("exit-status", false, cmd.SysProcAttr = &syscall.SysProcAttr{}
ssh.Marshal(exitStatus{Status: 127}))
return
}
} }
cmd.SysProcAttr.Setsid = true
cmd.SysProcAttr.Setctty = true
cmd.SysProcAttr.Ctty = int(_tty.Fd())
if runtime.GOOS == "unix" || runtime.GOOS == "linux" {
defer _pty.Close()
go io.Copy(ch, _pty) go io.Copy(ch, _pty)
go io.Copy(_pty, ch) go io.Copy(_pty, ch)
} else { // windows } else {
cmd.Stderr = ch cmd.Stderr = ch
cmd.Stdout = ch cmd.Stdout = ch
in, err := cmd.StdinPipe() in, err := cmd.StdinPipe()
if err != nil { if err != nil {
ch.SendRequest("exit-status", false, ch.SendRequest("exit-status", false,
@ -263,14 +340,16 @@ func handleShell(cmd *exec.Cmd, ch ssh.Channel) {
go func() { go func() {
defer in.Close() defer in.Close()
io.Copy(in, ch) io.Copy(in, ch)
}() }()
}
if err := cmd.Start(); err != nil { if err := cmd.Start(); err != nil {
log.Debugln("start command ", err) log.Debugln("start command ", err)
ch.SendRequest("exit-status", false, ch.SendRequest("exit-status", false,
ssh.Marshal(exitStatus{Status: 126})) ssh.Marshal(exitStatus{Status: 126}))
return return
} }
}
code := 0 code := 0
if err = cmd.Wait(); err != nil { if err = cmd.Wait(); err != nil {
log.Debugln(err) log.Debugln(err)

Loading…
Cancel
Save