doushai7225 2017-06-09 04:53
浏览 52

输入ssh提示数据

So I'm able to ssh into the machine, but i'm having trouble entering data into the prompt.

...
    sshConfig := &ssh.ClientConfig{
        User: user,
        Auth: []ssh.AuthMethod{
            ssh.Password(password),
        },
        HostKeyCallback: KeyPrint,
    }

    connection, err := ssh.Dial("tcp", connStr, sshConfig)
    if err != nil {

        log.Fatalln(err)
    }

    session, err := connection.NewSession()
    if err != nil {
        log.Fatalln(err)
    }

    modes := ssh.TerminalModes{
        ssh.ECHO:          0,     // disable echoing
        ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
        ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
    }

    if err := session.RequestPty("xterm", 80, 40, modes); err != nil {
        session.Close()
        log.Fatalf("request for pseudo terminal failed: %s", err)
    }

    stdin, err := session.StdinPipe()
    if err != nil {
        log.Fatalf("Unable to setup stdin for session: %v", err)
    }
    go io.Copy(stdin, os.Stdin)

    stdout, err := session.StdoutPipe()
    if err != nil {
        log.Fatalf("Unable to setup stdout for session: %v", err)
    }
    go io.Copy(os.Stdout, stdout)

    stderr, err := session.StderrPipe()
    if err != nil {
        log.Fatalf("Unable to setup stderr for session: %v", err)
    }
    go io.Copy(os.Stderr, stderr)

    // err = session.Run("1")

    session.Run("") // running it allows me to interact with the remote machines terminal in my own terminal.. session.Start("") exits and session.Wait() doesn't display the Welcome screen that normally greats users, and the prompt doesn't appear.

    stdin.Write([]byte("10000"))

    os.Stdin.WriteString("110000")

    // log.Fatalln(n, err)
    // os.Stdin.WriteString("1")
    // for {
    //  session.Run("1")
    //  go os.Stdin.WriteString("1")
    //  go stdin.Write([]byte("10000"))

    // }
...

The above code snippet gets me into the machine and the machine's prompt is displayed on my screen as if I ssh'ed into manually. I can type in the shell... but i need to be able to have Go type in the shell for me. The prompt that I'm interacting with is a text based game so I can't just issue commands e.g no (ls, echo, grep, etc..) the only thing I'm allow to pass in are numbers. How do I send input to the ssh session? I've tried many ways and none of the input seems to be going through.

I'm also attaching a screenshot of the prompt, just incase the description above is confusion in trying to portray the type of session this is.

ssh session screen

UPDATE: I think I've found a way to send the data, at least once.

session, err := connection.NewSession()
    if err != nil {
        log.Fatalln(err)
    }

    // ---------------------------------

    modes := ssh.TerminalModes{
        ssh.ECHO:          0,     // disable echoing
        ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
        ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
    }

    if err := session.RequestPty("xterm", 80, 40, modes); err != nil {
        session.Close()
        log.Fatalf("request for pseudo terminal failed: %s", err)
    }

    stdin, err := session.StdinPipe()
    if err != nil {
        log.Fatalf("Unable to setup stdin for session: %v", err)
    }
    go io.Copy(stdin, os.Stdin)

    stdout, err := session.StdoutPipe()
    if err != nil {
        log.Fatalf("Unable to setup stdout for session: %v", err)
    }
    go io.Copy(os.Stdout, stdout)

    stderr, err := session.StderrPipe()
    if err != nil {
        log.Fatalf("Unable to setup stderr for session: %v", err)
    }
    go io.Copy(os.Stderr, stderr)

    go session.Start("")
    for {
        stdin.Write([]byte("10000000
"))
        break
    }
    session.Wait()

I start the session with go session.Start("") remember that there is no point is passing in command because all i'm doing is entering data in response to a prompt. I then use session.Wait() at the end of a for loop...kinda like one does when using channels and waitgroups.. inside the for loop i send data with stdin.Write([]byte("10000000 ")) where the important thing to note is to add the delimiter to simulate hitting enter on the keyboard..

If there are any better ways to achieve what i'm trying to achieve please feel free. Next steps are to parse the stdout for a response and reply accordingly.

  • 写回答

1条回答

  • duancheng6500 2017-11-11 20:56
    关注

    An empty Start will work, however within the ssh package, Start, Run, and Shell are all calls to, basically, the same thing. Start("cmd") executes a command within a shell, Run("cmd") is a simple call to Start("cmd") that then invokes Wait() for you (giving the feel of executing without concurrency), and Shell opens a shell (like Start), but without a command passed. It's six of one, half a dozen of the other, really, but using Shell() is probably the cleanest way to go about that.

    Also, bear in mind that Start() and Shell() both leverage concurrency without the explicit invocation of "go". It might free an additional millisecond or so to invoke concurrency manually, but if that isn't of significance to you, then you should be able to drop that. The automatic concurrency of Start and Shell is the reason for the Wait() and Run("cmd") methods.

    If you have no need to interpret your output (stdout/err), then you can map these without the pipe() call or io.Copy(), which is easier and more efficient. I did this in the example below, but bear in mind that if you do interpret the output, it's probably easier to work with the Pipe(). You can send multiple commands (or numbers) sequentially without reading for a prompt in most cases, but some things (like passwords prompts) clear the input buffer. If this happens for you, then you'll need to read the Stdout to find your prompt or leverage an expect tool like goexpect (https://github.com/google/goexpect). There are several expect-like packages for Go, but this one is from Google and (as of this posting) still fairly recently maintained.

    StdinPipe() exposes a writeCloser that can be leveraged without io.Copy(), which should be more efficient.

    Your for loop that writes to the StdinPipe() should allow you to enter several commands (or in your case, sets of numbers)... as an example, I have this reading commands (numbers, etc) from os.Args and iterating through them.

    Lastly, you should probably add a session.Close()for healthy completion (you already have a call for errors). That said, this is what I would recommend (based on your last example):

    modes := ssh.TerminalModes{
        ssh.ECHO:          0,     // disable echoing
        ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
        ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
    }
    
    if err := session.RequestPty("xterm", 80, 40, modes); err != nil {
        session.Close()
        log.Fatalf("request for pseudo terminal failed: %s", err)
    }
    defer session.Close()
    
    stdin, err := session.StdinPipe()
    if err != nil {
        log.Fatalf("Unable to setup stdin for session: %v", err)
    }
    
    session.Stdout = os.Stdout
    session.Stderr = os.Stderr
    
    err = session.Shell()
    if err != nil {
        log.Fatalf("Unable to setup stdin for session: %v", err)
    }
    for _, cmd := range os.Args {
        stdin.Write([]byte(cmd + "
    "))
    }
    
    session.Wait()
    

    Oh, one more item to note is that the Wait() method relies on an unchecked channel that retrieves the exitStatus from your command, and this does hang on rare occasion (it is particularly problematic when connecting to cisco gear, but can happen with others as well). If you find that you encounter this (or you'd just like to be careful), you might want to wrap Wait() inside of some sort of timeout methodology such as by invoking Wait() with concurrency and reading the response through a channel that can be cased along with time.After() (I can provide an example, if that would be helpful).

    评论

报告相同问题?

悬赏问题

  • ¥15 安装svn网络有问题怎么办
  • ¥15 Python爬取指定微博话题下的内容,保存为txt
  • ¥15 vue2登录调用后端接口如何实现
  • ¥65 永磁型步进电机PID算法
  • ¥15 sqlite 附加(attach database)加密数据库时,返回26是什么原因呢?
  • ¥88 找成都本地经验丰富懂小程序开发的技术大咖
  • ¥15 如何处理复杂数据表格的除法运算
  • ¥15 如何用stc8h1k08的片子做485数据透传的功能?(关键词-串口)
  • ¥15 有兄弟姐妹会用word插图功能制作类似citespace的图片吗?
  • ¥15 latex怎么处理论文引理引用参考文献