duanchai0028 2018-06-21 18:18
浏览 164

思科正则表达式的Golang转义问号字符

So, Cisco's regex allows the question mark character. But the catch is that you have to precede typing a question mark with Ctrl-Shift-v in order for it to be interpreted as a question mark and not a help command... Link to Cisco regex guidelines

I have a Go program that logs into a set of devices and runs a set of commands on each device. When trying to use a regex containing a question mark, though, the Cisco device always interprets the question mark as a help command. Using string literals in Go does not fix the problem nor does sending the command as a slice of bytes.

For example, if I try to send the command show boot | include (c|cat)[0-9]+[a-zA-Z]? the Cisco CLI returns

switch-1#show boot | include (c|cat)[0-9]+[a-zA-Z]?
LINE    <cr>

switch-1#

instead of interpreting the question mark as a regex match of 0 or 1 for the [a-zA-Z] group.

However, using the command ssh user@switch-1 'show boot | include (c|cat)[0-9]+[a-zA-Z]?' works as expected and interprets the regex pattern correctly.

How can I replicate the behaviour of the ssh command? Is there a way to send Ctrl-Shift-v before each question mark or escape each question mark character?

My code as requested:

package main

import (
    "golang.org/x/crypto/ssh"
    "net"
    "fmt"
    "os"
    "bufio"
    "time"
    "golang.org/x/crypto/ssh/terminal"
    "io"
    "io/ioutil"
    "sync"
    "strings"
)

// ReadLines reads a file line-by-line and returns a slice of the lines.
func ReadLines(filename string) ([]string, error) {
    f, err := os.Open(filename)
    if err != nil {
        return nil, fmt.Errorf("failed to open file: %v", err)
    }
    defer f.Close()

    var lines []string
    s := bufio.NewScanner(f)
    for s.Scan() {
        lines = append(lines, s.Text())
    }
    if err := s.Err(); err != nil {
        return nil, err
    }
    return lines, nil
}

// Type Result represents the result of running the Configure method.
type Result struct {
    Host   string // Hostname of device
    Output []byte // Remote shell's stdin and stderr output
    Err    error  // Remote shell errors
}

// Configure logs into a device, starts a remote shell, runs the set of
// commands, and waits for the remote shell to return or timeout.
func Configure(host string, config *ssh.ClientConfig, cmds []string, results chan<- *Result, wg *sync.WaitGroup) {
    defer wg.Done()

    res := &Result{
        Host:   host,
        Output: nil,
        Err:    nil,
    }

    // Create client connection
    client, err := ssh.Dial("tcp", net.JoinHostPort(host, "22"), config)
    if err != nil {
        res.Err = fmt.Errorf("failed to dial: %v", err)
        results <- res
        return
    }
    defer client.Close()

    // Create new session
    session, err := client.NewSession()
    if err != nil {
        res.Err = fmt.Errorf("failed to create session: %v", err)
        results <- res
        return
    }
    defer session.Close()

    // Set session IO
    stdin, err := session.StdinPipe()
    if err != nil {
        res.Err = fmt.Errorf("failed to create pipe to stdin: %v", err)
        results <- res
        return
    }
    defer stdin.Close()

    stdout, err := session.StdoutPipe()
    if err != nil {
        res.Err = fmt.Errorf("failed to create pipe to stdout: %v", err)
        results <- res
        return
    }
    stderr, err := session.StderrPipe()
    if err != nil {
        res.Err = fmt.Errorf("failed to create pipe to stderr: %v", err)
        results <- res
        return
    }

    // Start remote shell
    if err := session.RequestPty("vt100", 0, 0, ssh.TerminalModes{
        ssh.ECHO:          0,
        ssh.TTY_OP_ISPEED: 14400,
        ssh.TTY_OP_OSPEED: 14400,
    }); err != nil {
        res.Err = fmt.Errorf("failed to request pseudoterminal: %v", err)
        results <- res
        return
    }
    if err := session.Shell(); err != nil {
        res.Err = fmt.Errorf("failed to start remote shell: %v", err)
        results <- res
        return
    }

    // Run commands
    for _, cmd := range cmds {
        if _, err := io.WriteString(stdin, cmd+"
"); err != nil {
            res.Err = fmt.Errorf("failed to run: %v", err)
            results <- res
            return
        }
    }

    // Wait for remote commands to return or timeout
    exit := make(chan error, 1)
    go func(exit chan<- error) {
        exit <- session.Wait()
    }(exit)
    timeout := time.After(1 * time.Minute)
    select {
    case <-exit:
        output, err := ioutil.ReadAll(io.MultiReader(stdout, stderr))
        if err != nil {
            res.Err = fmt.Errorf("failed to read output: %v", err)
            results <- res
            return
        }
        res.Output = output
        results <- res
        return
    case <-timeout:
        res.Err = fmt.Errorf("session timed out")
        results <- res
        return
    }
}

func main() {
    hosts, err := ReadLines(os.Args[1])
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
    cmds, err := ReadLines(os.Args[2])
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    fmt.Fprint(os.Stderr, "Password: ")
    secret, err := terminal.ReadPassword(int(os.Stdin.Fd()))
    if err != nil {
        fmt.Fprintf(os.Stderr, "failed to read password: %v
", err)
        os.Exit(1)
    }
    fmt.Fprintln(os.Stderr)

    config := &ssh.ClientConfig{
        User:            "user",
        Auth:            []ssh.AuthMethod{ssh.Password(string(secret))},
        HostKeyCallback: ssh.InsecureIgnoreHostKey(),
        Timeout:         30 * time.Second,
    }
    config.SetDefaults()
    config.Ciphers = append(config.Ciphers, "aes128-cbc", "3des-cbc", "aes192-cbc", "aes256-cbc")

    results := make(chan *Result, len(hosts))

    var wg sync.WaitGroup
    wg.Add(len(hosts))
    for _, host := range hosts {
        go Configure(host, config, cmds, results, &wg)
    }
    wg.Wait()
    close(results)

    for res := range results {
        if res.Err != nil {
            fmt.Fprintf(os.Stderr, "Error %s: %v
", res.Host, res.Err)
            continue
        }
        fmt.Printf("Host %s
%s
%s
", res.Host, res.Output, strings.Repeat("-", 50))
    }
}
  • 写回答

1条回答 默认 最新

  • dspx15491 2018-06-22 01:42
    关注

    Try forcing the IOS terminal server into line mode (as opposed to character mode). Send these telnet negotiation sequences:

    IAC DONT ECHO
    IAC WONT ECHO
    IAC DONT SUPPRESS-GO-AHEAD
    IAC WONT SUPPRESS-GO-AHEAD
    

    See: https://tools.ietf.org/html/rfc858

    评论

报告相同问题?

悬赏问题

  • ¥100 set_link_state
  • ¥15 虚幻5 UE美术毛发渲染
  • ¥15 CVRP 图论 物流运输优化
  • ¥15 Tableau online 嵌入ppt失败
  • ¥100 支付宝网页转账系统不识别账号
  • ¥15 基于单片机的靶位控制系统
  • ¥15 真我手机蓝牙传输进度消息被关闭了,怎么打开?(关键词-消息通知)
  • ¥15 装 pytorch 的时候出了好多问题,遇到这种情况怎么处理?
  • ¥20 IOS游览器某宝手机网页版自动立即购买JavaScript脚本
  • ¥15 手机接入宽带网线,如何释放宽带全部速度