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

时间:2018-06-21 18:18:13

标签: regex go network-programming cisco

因此,思科的正则表达式允许使用问号字符。但是要注意的是,必须先在Ctrl-Shift-v之前键入问号,才能将其解释为问号而不是帮助命令... Link to Cisco regex guidelines

我有一个Go程序,该程序登录到一组设备并在每个设备上运行一组命令。但是,当尝试使用包含问号的正则表达式时,Cisco设备始终将问号解释为帮助命令。在Go中使用字符串文字不能解决问题,也不能以字节的形式发送命令。

例如,如果我尝试发送命令show boot | include (c|cat)[0-9]+[a-zA-Z]?,Cisco CLI返回

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

switch-1#

而不是将问号解释为[a-zA-Z]组的正则表达式匹配为0或1。

但是,使用命令ssh user@switch-1 'show boot | include (c|cat)[0-9]+[a-zA-Z]?'可以正常工作并正确解释正则表达式模式。

如何复制ssh命令的行为?是否可以在每个问号之前发送Ctrl-Shift-v或转义每个问号字符?

我要求的代码:

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+"\n"); 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\n", 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\n", res.Host, res.Err)
            continue
        }
        fmt.Printf("Host %s\n%s\n%s\n", res.Host, res.Output, strings.Repeat("-", 50))
    }
}

1 个答案:

答案 0 :(得分:0)

尝试将IOS终端服务器强制设为行模式(与字符模式相对)。发送以下telnet协商序列:

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

请参阅:https://tools.ietf.org/html/rfc858