Skip to content

Commit

Permalink
version 0.1
Browse files Browse the repository at this point in the history
  • Loading branch information
jgivc committed Dec 1, 2021
1 parent 1504d9f commit 1ba1e14
Show file tree
Hide file tree
Showing 18 changed files with 1,748 additions and 2 deletions.
64 changes: 62 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,62 @@
# console
Uniform interface for interacting with hardware via telnet/ssh
# jgivc/console

This package provides a uniform interface for interacting with hardware via telnet/ssh
This package uses part of [reiver/go-telnet package](https://github.com/reiver/go-telnet) for handle telnet connection.

## Usage

```go
package main

import (
"fmt"
"log"

"github.com/jgivc/console"
)

func main() {
hosts := []console.Host{
{
Host: "192.168.1.10",
Port: 22,
TransportType: console.TransportSSH,
Account: console.Account{
Username: "admin",
Password: "pass",
},
},
{
Host: "192.168.1.20",
Port: 22,
TransportType: console.TransportTELNET,
Account: console.Account{
Username: "admin",
Password: "pass",
},
},
}

for _, h := range hosts {
c := console.New()
if err := c.Open(&h); err != nil {
log.Fatal(err)
}
defer c.Close()

if err := c.Run("term le 0"); err != nil {
log.Fatal(err)
}

out, err := c.Execute("sh ver")
if err != nil {
log.Fatal(err)
}

fmt.Println(out)

c.Send("q")
}

}
```
148 changes: 148 additions & 0 deletions console.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package console

import (
"strings"
"time"
)

const (
promptWaitTimeout = 5 * time.Second
promptMatchLen int = 15
readBufferSize = 200

authPattern = `(?mi)(user\w+|pass\w+|[\w-()\s]+)[#>:]+(?:\s+)?`
promptPattern = `(?mi)([\w-()\s]{2,})[#>]+(?:\s+)?\z`

userPromptPart = "user"
passwordPromptPart = "pass"
)

var (
cmdEnd = []byte("\r")
)

type Console interface {
Open(host *Host) error
Execute(cmd string) (string, error)
Run(cmd string) error // Just run command and read and omit output
Send(cmd string) error
SetPrompt(pattern string)
Close() error
}

type console struct {
h *Host
tr transport
pm promptMatcher
buf []byte
}

func (c *console) tryAuth() error {
c.pm = newPromptRegexpMatcher(authPattern)
for {
_, err := c.readToPrompt()
if err != nil {
return err
}

m := c.pm.getMatched()
if m != nil {
if ss, ok := m.([]string); ok {
line := ss[1]
line = strings.ToLower(line)
if strings.Contains(line, userPromptPart) {
c.Send(c.h.Username)
} else if strings.Contains(line, passwordPromptPart) {
c.Send(c.h.Password)
} else {
break
}
}
}
}

c.pm = newPromptRegexpMatcher(promptPattern)
return nil
}

func (c *console) readToPrompt() (string, error) {
b := make([]byte, 0)
start := time.Now()

for {
n, err := c.tr.Read(c.buf)
if err != nil {
if err == errorRreadTimeout {
if time.Since(start) < promptWaitTimeout {

continue
}

return "", err
}
}

b = append(b, c.buf[:n]...)
if l := len(b); l > promptMatchLen {
if c.pm.match(string(b[l-promptMatchLen:])) {
break
}
}
}

return string(b), nil
}

func (c *console) Open(host *Host) error {
var err error
c.tr, err = newTransport(host.TransportType)
if err != nil {
return err
}

if err := c.tr.Open(host); err != nil {
return err
}

c.h = host

if err := c.tryAuth(); err != nil {
return err
}

return nil

}

func (c *console) Execute(cmd string) (string, error) {
c.Send(cmd)
return c.readToPrompt()
}

func (c *console) Run(cmd string) error {
c.Send(cmd)
_, err := c.readToPrompt()

return err
}

func (c *console) SetPrompt(pattern string) {
c.pm = newPromptRegexpMatcher(pattern)
}

func (c *console) Close() error {
return c.tr.Close()
}

func (c *console) Send(cmd string) error {
c.tr.Write([]byte(cmd))
c.tr.Write(cmdEnd)

return nil
}

func New() Console {
return &console{
buf: make([]byte, readBufferSize),
}
}
8 changes: 8 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module github.com/jgivc/console

go 1.16

require (
github.com/reiver/go-oi v1.0.0
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871
)
13 changes: 13 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
github.com/reiver/go-oi v1.0.0 h1:nvECWD7LF+vOs8leNGV/ww+F2iZKf3EYjYZ527turzM=
github.com/reiver/go-oi v1.0.0/go.mod h1:RrDBct90BAhoDTxB1fenZwfykqeGvhI6LsNfStJoEkI=
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 h1:/pEO3GD/ABYAjuakUS6xSEmmlyVS4kxBNkeA9tLJiTI=
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
22 changes: 22 additions & 0 deletions host.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package console

import "fmt"

type Account struct {
Username, Password, EnablePassword string
}

type Host struct {
Host string
Port int
TransportType int
Account
}

func (h *Host) GetHostPort() string {
return fmt.Sprintf("%s:%d", h.Host, h.Port)
}

func (h *Host) HasAccount() bool {
return !(h.Account == (Account{}))
}
35 changes: 35 additions & 0 deletions prompt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package console

import (
"regexp"
)

type promptMatcher interface {
match(line string) bool
getMatched() interface{}
}

type promptMatcherRegexp struct {
re *regexp.Regexp
matched interface{}
}

func (m *promptMatcherRegexp) match(line string) bool {
m.matched = nil
if arr := m.re.FindStringSubmatch(line); arr != nil {
m.matched = arr
return true
}

return false
}

func (m *promptMatcherRegexp) getMatched() interface{} {
return m.matched
}

func newPromptRegexpMatcher(pattern string) promptMatcher {
return &promptMatcherRegexp{
re: regexp.MustCompile(pattern),
}
}
Loading

0 comments on commit 1ba1e14

Please sign in to comment.