From a0e90c14ba9a30ee45ac51b91c84377930059e02 Mon Sep 17 00:00:00 2001 From: Russ Magee Date: Sat, 8 Dec 2018 21:44:06 -0800 Subject: [PATCH] Praise Bob! Signed-off-by: Russ Magee --- hkexnet/hkexnet.go | 142 ------------------------------------------- hkexsh/hkexsh.go | 147 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 145 insertions(+), 144 deletions(-) diff --git a/hkexnet/hkexnet.go b/hkexnet/hkexnet.go index 2c8a8e4..776d90e 100644 --- a/hkexnet/hkexnet.go +++ b/hkexnet/hkexnet.go @@ -42,7 +42,6 @@ import ( "math/big" "math/rand" "net" - "os" "strings" "sync" "time" @@ -56,32 +55,6 @@ import ( const PAD_SZ = 32 // max size of padding applied to each packet const HMAC_CHK_SZ = 4 // leading bytes of HMAC to xmit for verification -const Bob = string("\r\n\r\n" + - "@@@@@@@^^~~~~~~~~~~~~~~~~~~~~^@@@@@@@@@\r\n" + - "@@@@@@^ ~^ @ @@ @ @ @ I ~^@@@@@@\r\n" + - "@@@@@ ~ ~~ ~I @@@@@\r\n" + - "@@@@' ' _,w@< @@@@\r\n" + - "@@@@ @@@@@@@@w___,w@@@@@@@@ @ @@@\r\n" + - "@@@@ @@@@@@@@@@@@@@@@@@@@@@ I @@@\r\n" + - "@@@@ @@@@@@@@@@@@@@@@@@@@*@[ i @@@\r\n" + - "@@@@ @@@@@@@@@@@@@@@@@@@@[][ | ]@@@\r\n" + - "@@@@ ~_,,_ ~@@@@@@@~ ____~ @ @@@\r\n" + - "@@@@ _~ , , `@@@~ _ _`@ ]L J@@@\r\n" + - "@@@@ , @@w@ww+ @@@ww``,,@w@ ][ @@@@\r\n" + - "@@@@, @@@@www@@@ @@@@@@@ww@@@@@[ @@@@\r\n" + - "@@@@@_|| @@@@@@P' @@P@@@@@@@@@@@[|c@@@@\r\n" + - "@@@@@@w| '@@P~ P]@@@-~, ~Y@@^'],@@@@@@\r\n" + - "@@@@@@@[ _ _J@@Tk ]]@@@@@@\r\n" + - "@@@@@@@@,@ @@, c,,,,,,,y ,w@@[ ,@@@@@@@\r\n" + - "@@@@@@@@@ i @w ====--_@@@@@ @@@@@@@@\r\n" + - "@@@@@@@@@@`,P~ _ ~^^^^Y@@@@@ @@@@@@@@@\r\n" + - "@@@@^^=^@@^ ^' ,ww,w@@@@@ _@@@@@@@@@@\r\n" + - "@@@_xJ~ ~ , @@@@@@@P~_@@@@@@@@@@@@\r\n" + - "@@ @, ,@@@,_____ _,J@@@@@@@@@@@@@\r\n" + - "@@L `' ,@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\r\n" + - "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\r\n" + - "\r\n") - type ( WinSize struct { Rows uint16 @@ -122,9 +95,6 @@ type ( wm hash.Hash dBuf *bytes.Buffer //decrypt buffer for Read() } - - EscHandler func(io.Writer) - EscSeqs map[byte]EscHandler ) var ( @@ -1114,115 +1084,3 @@ func (hc *Conn) chaffHelper() { } }() } - -//////////////////////////////////////////////////// - -// Copy copies from src to dst until either EOF is reached -// on src or an error occurs. It returns the number of bytes -// copied and the first error encountered while copying, if any. -// -// A successful Copy returns err == nil, not err == EOF. -// Because Copy is defined to read from src until EOF, it does -// not treat an EOF from Read as an error to be reported. -// -// If src implements the WriterTo interface, -// the copy is implemented by calling src.WriteTo(dst). -// Otherwise, if dst implements the ReaderFrom interface, -// the copy is implemented by calling dst.ReadFrom(src). -func Copy(dst io.Writer, src io.Reader) (written int64, err error) { - written, err = copyBuffer(dst, src, nil) - return -} - -// copyBuffer is the actual implementation of Copy and CopyBuffer. -// if buf is nil, one is allocated. -func copyBuffer(dst io.Writer, src io.Reader, buf []byte) (written int64, err error) { - // NOTE: using dst.Write() in these esc funcs will cause the output - // to function as a 'macro', outputting as if user typed the sequence. - // - // Using os.Stdout outputs to the client's term w/o it or the server - // 'seeing' the output. - // - // TODO: Devise a way to signal to main client thread that - // a goroutine should be spawned to do long-lived tasks for - // some esc sequences (eg., a time ticker in the corner of terminal, - // or tunnel traffic indicator - note we cannot just spawn a goroutine - // here, as copyBuffer() returns after each burst of data. Scope must - // outlive individual copyBuffer calls). - // (Note that since this custom copyBuffer func is used only by - // the hkexsh client, it should eventually be moved to client.) - escs := EscSeqs{ - 'i': func(io.Writer) { os.Stdout.Write([]byte("\x1b[s\x1b[2;1H\x1b[1;31m[HKEXSH]\x1b[39;49m\x1b[u")) }, - 't': func(io.Writer) { os.Stdout.Write([]byte("\x1b[1;32m[HKEXSH]\x1b[39;49m")) }, - 'B': func(io.Writer) { os.Stdout.Write([]byte("\x1b[1;32m" + Bob + "\x1b[39;49m")) }, - } - - /* - // If the reader has a WriteTo method, use it to do the copy. - // Avoids an allocation and a copy. - if wt, ok := src.(io.WriterTo); ok { - return wt.WriteTo(dst) - } - // Similarly, if the writer has a ReadFrom method, use it to do the copy. - if rt, ok := dst.(io.ReaderFrom); ok { - return rt.ReadFrom(src) - } - */ - if buf == nil { - size := 32 * 1024 - if l, ok := src.(*io.LimitedReader); ok && int64(size) > l.N { - if l.N < 1 { - size = 1 - } else { - size = int(l.N) - } - } - buf = make([]byte, size) - } - - var seqPos int - for { - nr, er := src.Read(buf) - if nr > 0 { - // Look for sequences to trigger client-side diags - // A repeat of 4 keys (conveniently 'dead' chars for most - // interactive shells; here CTRL-]) shall introduce - // some special responses or actions on the client side. - if seqPos < 4 { - if buf[0] == 0x1d { - seqPos++ - } - } else /* seqPos > 0 */ { - if v, ok := escs[buf[0]]; ok { - v(dst) - nr-- - buf = buf[1:] - } - seqPos = 0 - } - - nw, ew := dst.Write(buf[0:nr]) - if nw > 0 { - written += int64(nw) - } - if ew != nil { - err = ew - break - } - if nr != nw { - err = io.ErrShortWrite - break - } - } - if er != nil { - if er != io.EOF { - err = er - } - break - } - } - //_,_ = dst.Write([]byte{0x2f}) - return written, err -} - -//////////////////////////////////////////////////// diff --git a/hkexsh/hkexsh.go b/hkexsh/hkexsh.go index 26c4678..5ea77af 100755 --- a/hkexsh/hkexsh.go +++ b/hkexsh/hkexsh.go @@ -41,6 +41,149 @@ var ( Log *logger.Writer ) +//////////////////////////////////////////////////// + +const Bob = string("\r\n\r\n" + + "@@@@@@@^^~~~~~~~~~~~~~~~~~~~~^@@@@@@@@@\r\n" + + "@@@@@@^ ~^ @ @@ @ @ @ I ~^@@@@@@\r\n" + + "@@@@@ ~ ~~ ~I @@@@@\r\n" + + "@@@@' ' _,w@< @@@@\r\n" + + "@@@@ @@@@@@@@w___,w@@@@@@@@ @ @@@\r\n" + + "@@@@ @@@@@@@@@@@@@@@@@@@@@@ I @@@\r\n" + + "@@@@ @@@@@@@@@@@@@@@@@@@@*@[ i @@@\r\n" + + "@@@@ @@@@@@@@@@@@@@@@@@@@[][ | ]@@@\r\n" + + "@@@@ ~_,,_ ~@@@@@@@~ ____~ @ @@@\r\n" + + "@@@@ _~ , , `@@@~ _ _`@ ]L J@@@\r\n" + + "@@@@ , @@w@ww+ @@@ww``,,@w@ ][ @@@@\r\n" + + "@@@@, @@@@www@@@ @@@@@@@ww@@@@@[ @@@@\r\n" + + "@@@@@_|| @@@@@@P' @@P@@@@@@@@@@@[|c@@@@\r\n" + + "@@@@@@w| '@@P~ P]@@@-~, ~Y@@^'],@@@@@@\r\n" + + "@@@@@@@[ _ _J@@Tk ]]@@@@@@\r\n" + + "@@@@@@@@,@ @@, c,,,,,,,y ,w@@[ ,@@@@@@@\r\n" + + "@@@@@@@@@ i @w ====--_@@@@@ @@@@@@@@\r\n" + + "@@@@@@@@@@`,P~ _ ~^^^^Y@@@@@ @@@@@@@@@\r\n" + + "@@@@^^=^@@^ ^' ,ww,w@@@@@ _@@@@@@@@@@\r\n" + + "@@@_xJ~ ~ , @@@@@@@P~_@@@@@@@@@@@@\r\n" + + "@@ @, ,@@@,_____ _,J@@@@@@@@@@@@@\r\n" + + "@@L `' ,@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\r\n" + + "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\r\n" + + "\r\n") + +type ( + EscHandler func(io.Writer) + EscSeqs map[byte]EscHandler +) + +// Copy copies from src to dst until either EOF is reached +// on src or an error occurs. It returns the number of bytes +// copied and the first error encountered while copying, if any. +// +// A successful Copy returns err == nil, not err == EOF. +// Because Copy is defined to read from src until EOF, it does +// not treat an EOF from Read as an error to be reported. +// +// If src implements the WriterTo interface, +// the copy is implemented by calling src.WriteTo(dst). +// Otherwise, if dst implements the ReaderFrom interface, +// the copy is implemented by calling dst.ReadFrom(src). +func Copy(dst io.Writer, src io.Reader) (written int64, err error) { + written, err = copyBuffer(dst, src, nil) + return +} + +// copyBuffer is the actual implementation of Copy and CopyBuffer. +// if buf is nil, one is allocated. +func copyBuffer(dst io.Writer, src io.Reader, buf []byte) (written int64, err error) { + // NOTE: using dst.Write() in these esc funcs will cause the output + // to function as a 'macro', outputting as if user typed the sequence. + // + // Using os.Stdout outputs to the client's term w/o it or the server + // 'seeing' the output. + // + // TODO: Devise a way to signal to main client thread that + // a goroutine should be spawned to do long-lived tasks for + // some esc sequences (eg., a time ticker in the corner of terminal, + // or tunnel traffic indicator - note we cannot just spawn a goroutine + // here, as copyBuffer() returns after each burst of data. Scope must + // outlive individual copyBuffer calls). + // (Note that since this custom copyBuffer func is used only by + // the hkexsh client, it should eventually be moved to client.) + escs := EscSeqs{ + 'i': func(io.Writer) { os.Stdout.Write([]byte("\x1b[s\x1b[2;1H\x1b[1;31m[HKEXSH]\x1b[39;49m\x1b[u")) }, + 't': func(io.Writer) { os.Stdout.Write([]byte("\x1b[1;32m[HKEXSH]\x1b[39;49m")) }, + 'B': func(io.Writer) { os.Stdout.Write([]byte("\x1b[1;32m" + Bob + "\x1b[39;49m")) }, + } + + /* + // If the reader has a WriteTo method, use it to do the copy. + // Avoids an allocation and a copy. + if wt, ok := src.(io.WriterTo); ok { + return wt.WriteTo(dst) + } + // Similarly, if the writer has a ReadFrom method, use it to do the copy. + if rt, ok := dst.(io.ReaderFrom); ok { + return rt.ReadFrom(src) + } + */ + if buf == nil { + size := 32 * 1024 + if l, ok := src.(*io.LimitedReader); ok && int64(size) > l.N { + if l.N < 1 { + size = 1 + } else { + size = int(l.N) + } + } + buf = make([]byte, size) + } + + var seqPos int + for { + nr, er := src.Read(buf) + if nr > 0 { + // Look for sequences to trigger client-side diags + // A repeat of 4 keys (conveniently 'dead' chars for most + // interactive shells; here CTRL-]) shall introduce + // some special responses or actions on the client side. + if seqPos < 4 { + if buf[0] == 0x1d { + seqPos++ + } + } else /* seqPos > 0 */ { + if v, ok := escs[buf[0]]; ok { + v(dst) + nr-- + buf = buf[1:] + } + seqPos = 0 + } + + nw, ew := dst.Write(buf[0:nr]) + if nw > 0 { + written += int64(nw) + } + if ew != nil { + err = ew + break + } + if nr != nw { + err = io.ErrShortWrite + break + } + } + if er != nil { + if er != io.EOF { + err = er + } + break + } + } + //_,_ = dst.Write([]byte{0x2f}) + return written, err +} + +//////////////////////////////////////////////////// + // GetSize gets the terminal size using 'stty' command func GetSize() (cols, rows int, err error) { cmd := exec.Command("stty", "size") // #nosec @@ -288,7 +431,7 @@ func doShellMode(isInteractive bool, conn *hkexnet.Conn, oldState *hkexsh.State, _, outerr := func(conn *hkexnet.Conn, r io.Reader) (w int64, e error) { // Copy() expects EOF so this will // exit with outerr == nil - w, e = hkexnet.Copy(conn, r) + w, e = Copy(conn, r) return w, e }(conn, os.Stdin) @@ -728,7 +871,7 @@ func main() { } if rec.Status() != 0 { - _ = hkexsh.Restore(int(os.Stdin.Fd()), oldState) // nolint: errcheck,gosec + _ = hkexsh.Restore(int(os.Stdin.Fd()), oldState) // nolint: errcheck,gosec fmt.Fprintln(os.Stderr, "Session exited with status:", rec.Status()) // nolint: errcheck } }