168 lines
4.0 KiB
Go
168 lines
4.0 KiB
Go
// Package tclient provides simple telnet client library, mainly designed for iteractions with
|
|
// various network equipment such as switches, routers, etc.
|
|
package tclient
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"time"
|
|
"regexp"
|
|
"bytes"
|
|
)
|
|
|
|
type callbackPattern struct {
|
|
Re *regexp.Regexp
|
|
Cb func()
|
|
SkipLine bool
|
|
}
|
|
|
|
// TelnetClient is telnet client struct itself
|
|
type TelnetClient struct {
|
|
// Timeout of read/write operations
|
|
Timeout int
|
|
// TimeoutGlobal is global operation timeout; i.e. like stucked in DLink-like refreshing pagination
|
|
TimeoutGlobal int
|
|
login string
|
|
password string
|
|
prompt string
|
|
conn net.Conn
|
|
closed bool
|
|
Options []int
|
|
|
|
loginPrompt string
|
|
passwordPrompt string
|
|
|
|
buf bytes.Buffer
|
|
|
|
patterns []callbackPattern
|
|
}
|
|
|
|
// New func creates new TelnetClient instance.
|
|
// First argument is network (r/w) timeout, second is prompt. Default prompt is "(?msi:[\$%#>]$)"
|
|
func New(tout int, login string, password string, prompt string) *TelnetClient {
|
|
if tout < 1 {
|
|
tout = 1
|
|
}
|
|
c := TelnetClient{
|
|
Timeout: tout,
|
|
login: login,
|
|
password: password,
|
|
prompt: `([\$|%|#|]|(\+>)(\s+)?$)`,
|
|
loginPrompt: `([Uu]ser(\s)?|[Nn]ame(\s+)?|[Ll]ogin(\s+)?)\:(\s+)?$`,
|
|
passwordPrompt: `[Pp]ass[Ww]ord(\s+)?\:$`,
|
|
heatMsg: ``,
|
|
closed: false,
|
|
Options: make([]int,0),
|
|
}
|
|
|
|
if prompt != "" {
|
|
c.prompt = prompt
|
|
}
|
|
|
|
// Global timeout defaults to 3 * rw timeout
|
|
c.TimeoutGlobal = c.Timeout * 2
|
|
|
|
// set default options
|
|
// we will accept an offer from remote sidie for it to echo and suppress goaheads
|
|
c.SetOpts([]int{TELOPT_ECHO, TELOPT_SGA})
|
|
|
|
return &c
|
|
}
|
|
|
|
// GlobalTimeout sets timeout for app operations, where net.Conn deadline could not be useful.
|
|
// For example stucking in pagination, while some network devices refreshing their telnet screen - so
|
|
// we cannot reach read timeout.
|
|
func (c *TelnetClient) GlobalTimeout(t int) {
|
|
c.TimeoutGlobal = t
|
|
}
|
|
|
|
// You may need to change password for enable (because prompt is same as login)
|
|
func (c *TelnetClient) SetPassword(pw string) {
|
|
c.password = pw
|
|
}
|
|
|
|
// SetPrompt allows you to change prompt without re-creating ssh client
|
|
func (c *TelnetClient) SetPrompt(prompt string) {
|
|
c.prompt = prompt
|
|
}
|
|
|
|
// SetLoginPrompt sets custom login prompt. Default is "[Uu]ser[Nn]ame\:$"
|
|
func (c *TelnetClient) SetLoginPrompt(s string) {
|
|
c.loginPrompt = s
|
|
}
|
|
|
|
func (c *TelnetClient) SetLogin(login string) {
|
|
c.login = login
|
|
}
|
|
|
|
// SetPasswordPrompt sets custom password prompt. Default is "[Pp]ass[Ww]ord\:$"
|
|
func (c *TelnetClient) SetPasswordPrompt(s string) {
|
|
c.passwordPrompt = s
|
|
}
|
|
|
|
// Close closes telnet connection. You can use it with defer.
|
|
func (c *TelnetClient) Close() {
|
|
if !c.closed {
|
|
c.conn.Close()
|
|
}
|
|
c.closed = false
|
|
}
|
|
|
|
// FlushOpts flushes default options set
|
|
func (c *TelnetClient) FlushOpts() {
|
|
c.Options = make([]int, 0)
|
|
}
|
|
|
|
// SetOpts prepares default options set
|
|
func (c *TelnetClient) SetOpts(opts []int) error {
|
|
for _, opt := range opts {
|
|
if opt > 255 {
|
|
return fmt.Errorf("Bad telnet option %d: option > 255", opt)
|
|
}
|
|
|
|
c.Options = append(c.Options, opt)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Open tcp connection
|
|
func (c *TelnetClient) Open(host string, port int) error {
|
|
addr := fmt.Sprintf("%s:%d", host, port)
|
|
|
|
var err error
|
|
c.conn, err = net.DialTimeout("tcp", addr, time.Second * time.Duration(c.Timeout))
|
|
if err != nil {
|
|
c.closed = true
|
|
return err
|
|
}
|
|
|
|
c.closed = false
|
|
|
|
// and login right now
|
|
_, err = c.Login(c.login, c.password)
|
|
|
|
return err
|
|
}
|
|
|
|
// RegisterCallback registers new callback based on regex string. When current output string matches given
|
|
// regex, callback is called. Returns error if regex cannot be compiled.
|
|
func (c *TelnetClient) RegisterCallback(pattern string, callback func()) error {
|
|
re, err := regexp.Compile(pattern)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
c.patterns = append(c.patterns, callbackPattern{
|
|
Cb:callback,
|
|
Re:re,
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetBuffer returns current buffer from reader as a string
|
|
func (c *TelnetClient) GetBuffer() string {
|
|
return c.buf.String()
|
|
}
|