diff --git a/console.go b/console.go index 320d7d1..8ec4eab 100644 --- a/console.go +++ b/console.go @@ -20,3 +20,7 @@ func newPty() (console.Console, string, error) { func setProcAttr(attr *syscall.SysProcAttr) { } + +func setTermios(fd int, args ssh.TerminalModes) error { + return errors.New("not supported") +} diff --git a/console_unix.go b/console_unix.go index 552e001..7bdf8e9 100644 --- a/console_unix.go +++ b/console_unix.go @@ -10,6 +10,7 @@ import ( "github.com/containerd/console" "github.com/fangdingjun/go-log/v5" "golang.org/x/crypto/ssh" + "golang.org/x/sys/unix" ) func consoleChange(_console console.Console, session *ssh.Session) { @@ -37,3 +38,63 @@ func setProcAttr(attr *syscall.SysProcAttr) { attr.Setsid = true attr.Setctty = true } + +func setFlag(f *uint32, k uint8, v uint32) { + v1, ok := termiosMap[k] + if !ok { + return + } + if v != 0 { + *f |= v1 + return + } + *f &^= v1 +} + +func applyTermios(flag *unix.Termios, t ssh.TerminalModes) { + for k, v := range t { + switch k { + case ssh.IGNPAR, ssh.PARMRK, ssh.INPCK, ssh.ISTRIP, ssh.INLCR, ssh.IGNCR, ssh.ICRNL, ssh.IUCLC, ssh.IXON, ssh.IXANY, ssh.IXOFF, ssh.IMAXBEL: + setFlag(&flag.Iflag, k, v) + case ssh.OPOST, ssh.OLCUC, ssh.ONLCR, ssh.OCRNL, ssh.ONOCR, ssh.ONLRET: + setFlag(&flag.Oflag, k, v) + case ssh.CS7, ssh.CS8, ssh.PARENB, ssh.PARODD: + setFlag(&flag.Cflag, k, v) + case ssh.ISIG, ssh.ICANON, ssh.XCASE, ssh.ECHO, ssh.ECHOE, ssh.ECHOK, ssh.ECHONL, ssh.ECHOCTL, ssh.ECHOKE, ssh.NOFLSH, ssh.TOSTOP, ssh.PENDIN, ssh.IEXTEN: + setFlag(&flag.Lflag, k, v) + case ssh.VEOF, ssh.VEOL, ssh.VEOL2, ssh.VDISCARD, ssh.VDSUSP, ssh.VERASE, ssh.VINTR, ssh.VKILL, ssh.VLNEXT, ssh.VQUIT, ssh.VREPRINT, ssh.VSTART, ssh.VSTATUS, ssh.VSTOP, ssh.VSUSP, ssh.VSWTCH, ssh.VWERASE: + v1, ok := termiosMap[k] + if ok { + flag.Cc[v1] = uint8(v) + } + case ssh.TTY_OP_ISPEED: + flag.Ispeed = v + case ssh.TTY_OP_OSPEED: + flag.Ospeed = v + } + } +} + +func setTermios(fd int, args ssh.TerminalModes) error { + t1, err := unix.IoctlGetTermios(fd, unix.TCGETS) + if err != nil { + return err + } + + log.Debugf("before %+v", t1) + applyTermios(t1, args) + + err = unix.IoctlSetTermios(fd, unix.TCSETS, t1) + if err != nil { + return err + } + + t1, err = unix.IoctlGetTermios(fd, unix.TCGETS) + if err != nil { + return err + } + + log.Debugf("after %+v", t1) + + return nil +} diff --git a/go.mod b/go.mod index 1e7c5e5..cad3379 100644 --- a/go.mod +++ b/go.mod @@ -12,4 +12,5 @@ require ( github.com/kr/fs v0.1.0 github.com/pkg/sftp v1.11.0 golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 + golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e ) diff --git a/server.go b/server.go index 33a67a5..6340761 100644 --- a/server.go +++ b/server.go @@ -1,6 +1,7 @@ package obfssh import ( + "encoding/binary" "fmt" "io" "net" @@ -164,6 +165,22 @@ type windowChange struct { Height uint32 } +func parseTerminalModes(s string) ssh.TerminalModes { + // log.Debugf("%x", s) + s1 := []byte(s) + t := ssh.TerminalModes{} + for i := 0; i < len(s1); i += 5 { + k := uint8(s1[i]) + if k == 0 { + break + } + v := binary.BigEndian.Uint32(s1[i+1 : i+5]) + t[k] = v + // log.Debugf("k %d, v %d", k, v) + } + return t +} + func (sc *Server) handleSession(newch ssh.NewChannel) { ch, req, err := newch.Accept() if err != nil { @@ -232,11 +249,17 @@ func (sc *Server) handleSession(newch ssh.NewChannel) { log.Errorln(err) ret = false } - log.Debugf("pty req %+v", _ptyReq) + log.Debugf("pty req Rows: %d, Columns: %d, Mode: %x", _ptyReq.Rows, _ptyReq.Columns, _ptyReq.Mode) if err == nil && (runtime.GOOS == "unix" || runtime.GOOS == "linux") { + termios := parseTerminalModes(_ptyReq.Mode) + log.Debugf("parsed terminal mode %+v", termios) _console, ptsname, err = newPty() if err == nil { log.Debugf("allocate pty %s", ptsname) + log.Debugf("set termios") + if err1 := setTermios(int(_console.Fd()), termios); err1 != nil { + log.Errorln(err) + } env = append(env, fmt.Sprintf("SSH_TTY=%s", ptsname)) ws, err := _console.Size() if err != nil { diff --git a/termios.go b/termios.go new file mode 100644 index 0000000..01cf27f --- /dev/null +++ b/termios.go @@ -0,0 +1,63 @@ +package obfssh + +import ( + "golang.org/x/crypto/ssh" +) + +var termiosMap = map[uint8]uint32{ + ssh.VINTR: 0, + ssh.VQUIT: 1, + ssh.VERASE: 2, + ssh.VKILL: 3, + ssh.VEOF: 4, + ssh.VSTART: 8, + ssh.VSTOP: 9, + ssh.VSUSP: 10, + ssh.VEOL: 11, + ssh.VREPRINT: 12, + ssh.VDISCARD: 13, + ssh.VWERASE: 14, + ssh.VLNEXT: 15, + ssh.VEOL2: 16, + ssh.IGNPAR: 0000004, + ssh.PARMRK: 0000010, + ssh.INPCK: 0000020, + ssh.ISTRIP: 0000040, + ssh.INLCR: 0000100, + ssh.IGNCR: 0000200, + ssh.ICRNL: 0000400, + ssh.IUCLC: 0001000, + ssh.IXON: 0002000, + ssh.IXANY: 0004000, + ssh.IXOFF: 0010000, + ssh.IMAXBEL: 0020000, + ssh.OPOST: 0000001, + ssh.PARENB: 0000400, + ssh.OLCUC: 0000002, + ssh.ONLCR: 0000004, + ssh.OCRNL: 0000010, + ssh.ONOCR: 0000020, + ssh.ONLRET: 0000040, + ssh.CS7: 0000040, + ssh.CS8: 0000060, + ssh.PARODD: 0001000, + ssh.ISIG: 0000001, + ssh.ICANON: 0000002, + ssh.XCASE: 0000004, + ssh.ECHO: 0000010, + ssh.ECHOE: 0000020, + ssh.ECHOK: 0000040, + ssh.ECHONL: 0000100, + ssh.NOFLSH: 0000200, + ssh.TOSTOP: 0000400, + ssh.ECHOCTL: 0001000, + ssh.ECHOKE: 0004000, + ssh.PENDIN: 0040000, + ssh.IEXTEN: 0100000, + /* + ssh.VFLUSH: 0, + ssh.VSWTCH: 0, + ssh.VSTATUS: 0, + ssh.VDSUSP: 0, + */ +}