While implement ssh client using golang, record keyboard input

600 views
Skip to first unread message

Sun Frank

unread,
Oct 22, 2018, 4:57:50 PM10/22/18
to golang-nuts
Hi Everyone:

I came across a problem which puzzled me for couple of days. I am trying to write a SSH client with a Interactive Shell using golang, but i also need to record the command(or keystroke history) of the user typed, like the history feature, here's my working code: 

ce := func(err error, msg string) {
   
if err != nil {
      log
.Fatalf("%s error: %v", msg, err)
   
}
}
var (
   auth        
[]ssh.AuthMethod SSH client with a
   addr        
string
   clientConfig *ssh.ClientConfig
   client      
*ssh.Client
   config       ssh
.Config
   session      
*ssh.Session
   err          
error
   key string
)
user
:= "vagrant"
addr = "localhost:2222"
key = "my_vagrant_private_key_path"
auth = make([]ssh.AuthMethod, 0)
pemBytes
, err := ioutil.ReadFile(key)
if err != nil {
   
panic(err)
}
var signer ssh.Signer
signer
, err = ssh.ParsePrivateKey(pemBytes)
if err != nil {
   
panic("parse error")
}
auth
= append(auth, ssh.PublicKeys(signer))
if err != nil {
   
panic("public key append error")
}
auth
= append(auth, ssh.PublicKeys(signer))
config
= ssh.Config{
   
Ciphers: []string{"aes128-ctr", "aes192-ctr", "aes256-ctr", "aes12...@openssh.com", "arcfour256", "arcfour128", "aes128-cbc", "3des-cbc", "aes192-cbc", "aes256-cbc"},
}
clientConfig
= &ssh.ClientConfig{
   
User:    user,
   Auth:    auth,
   Timeout: 30 * time.Second,
   Config:  config,
   HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
     
return nil
   
},
}
client
, err = ssh.Dial("tcp", addr,clientConfig)
ce
(err, "dial")
session
, err = client.NewSession()
ce
(err, "new session")
defer session.Close()

session
.Stdout = os.Stdout
session
.Stderr = os.Stderr
session
.Stdin = os.Stdin
modes
:= ssh.TerminalModes{
   ssh
.ECHO:          1,
   ssh.ECHOCTL:       0,
   ssh.TTY_OP_ISPEED: 14400,
   ssh.TTY_OP_OSPEED: 14400,
}
termFD
:= int(os.Stdin.Fd())
w
, h, _ := terminal.GetSize(termFD)
termState
, _ := terminal.MakeRaw(termFD)
defer terminal.Restore(termFD, termState)
err
= session.RequestPty("xterm-256color", h, w, modes)
ce
(err, "request pty") SSH client with a
err
= session.Shell()
ce
(err, "start shell")

err
= session.Wait()
ce
(err, "return")


So far, it works well as a SSH client which provide an interactive shell, which handle arrow keys, key combination like Ctrl+L(clear screen), Ctrl+r(show command history), ctrl+c correctly.
My problem is I cannot find the way to log, or record command history, if I copied os.Stdin, and pass that to Session, it shows like "session's stdin is not a terminal" warning, and the arrow keys, key combanition support is lost, when I press arrow key, it shows something like [[A on the screen.  

I've searched the internet a lot, and tried to read some pty/tty articles, but not get a clue. Anyone knows how to record command history to it, while make the key handle properly at the moment? (like the real SSH client we use) 

Michael Jones

unread,
Oct 22, 2018, 6:11:14 PM10/22/18
to Sun Frank, golang-nuts
Seems natural to have an architecture where one loop reads the input and passes complete lines to a consumer. This reading loop would store lines in a list (array, buffer,...) and thus be able to resubmit them to the consumer. 

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
--
Michael T. Jones
michae...@gmail.com

zwb...@gmail.com

unread,
Oct 26, 2018, 11:24:35 PM10/26/18
to golang-nuts
HI , you can capture and store the sdtin buffer and  handle it  then, for example:
// Set IO
session.Stdout = os.Stdout
session.Stderr = os.Stderr
in, _ := session.StdinPipe()

go func() {
for {
var buffer [1024]byte
n, err := os.Stdin.Read(buffer[:])
if err != nil {
fmt.Println("read error:", err)
}
// fmt.Println(string(buffer[:n]))
in.Write(buffer[:n])
}
}()

Also ,we can store the bufeer and write them to files at other time cyclicity。

在 2018年10月23日星期二 UTC+8上午4:57:50,Sun Frank写道:

sc39...@gmail.com

unread,
Sep 15, 2020, 11:41:55 AM9/15/20
to golang-nuts
Hi Everyone:

      I have the same problem, How do I get user input command, with SSH client with a Interactive Shell


在 2018年10月23日星期二 UTC+8上午4:57:50,Sun Frank写道:
Hi Everyone:

Marvin Renich

unread,
Sep 15, 2020, 12:28:17 PM9/15/20
to golang-nuts
* sc39...@gmail.com <sc39...@gmail.com> [200915 11:41]:
> Hi Everyone:
>
> I have the same problem, How do I get user input command, with *SSH
> client with a Interactive Shell*
>
> 在 2018年10月23日星期二 UTC+8上午4:57:50,Sun Frank写道:
> >
> > Hi Everyone:
> >
> > I came across a problem which puzzled me for couple of days. I am trying
> > to write a* SSH client with a Interactive Shell *using golang, but i also
> > need to record the command(or keystroke history) of the user typed, like
> > the history feature, here's my working code:
> >
[snip]
> >
> > session.Stdout = os.Stdout
> > session.Stderr = os.Stderr
> > session.Stdin = os.Stdin

Instead of using os.Stdin, create an io.Pipe, and use the returned
*PipeReader for session.Stdin. Then, read os.Stdin yourself, log or
otherwise process the user's input, and write the processed input to the
*PipeWriter.

The same technique can be used to log Stdout and Stderr.

...Marvin

Reply all
Reply to author
Forward
0 new messages