glide setup and dependencies addition

This commit is contained in:
Steve Brunton
2017-08-27 23:27:11 -04:00
commit b0e0be5688
74 changed files with 11533 additions and 0 deletions

5
vendor/gopkg.in/routeros.v2/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,5 @@
.vscode
.idea
*.exe
*.out
*.test

21
vendor/gopkg.in/routeros.v2/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 André Luiz dos Santos
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

14
vendor/gopkg.in/routeros.v2/README.md generated vendored Normal file
View File

@@ -0,0 +1,14 @@
# RouterOS Client for the Go language
Go library for accessing Mikrotik devices using the RouterOS API.
Look in the examples directory to learn how to use this library:
[run](examples/run/main.go),
[listen](examples/listen/main.go),
[tab](examples/tab/main.go).
API documentation is available at [godoc.org](http://godoc.org/gopkg.in/routeros.v2).
To install it, run:
go get gopkg.in/routeros.v2

85
vendor/gopkg.in/routeros.v2/async.go generated vendored Normal file
View File

@@ -0,0 +1,85 @@
package routeros
import "gopkg.in/routeros.v2/proto"
type sentenceProcessor interface {
processSentence(sen *proto.Sentence) (bool, error)
}
type replyCloser interface {
close(err error)
}
// Async starts asynchronous mode and returns immediately.
func (c *Client) Async() <-chan error {
c.mu.Lock()
defer c.mu.Unlock()
errC := make(chan error, 1)
if c.async {
errC <- errAlreadyAsync
close(errC)
return errC
}
c.async = true
c.tags = make(map[string]sentenceProcessor)
go c.asyncLoopChan(errC)
return errC
}
func (c *Client) asyncLoopChan(errC chan<- error) {
defer close(errC)
// If c.Close() has been called, c.closing will be true, and
// err will be “use of closed network connection”. Ignore that error.
err := c.asyncLoop()
if err != nil {
c.mu.Lock()
closing := c.closing
c.mu.Unlock()
if !closing {
errC <- err
}
}
}
func (c *Client) asyncLoop() error {
for {
sen, err := c.r.ReadSentence()
if err != nil {
c.closeTags(err)
return err
}
c.mu.Lock()
r, ok := c.tags[sen.Tag]
c.mu.Unlock()
if !ok {
continue
}
done, err := r.processSentence(sen)
if done || err != nil {
c.mu.Lock()
delete(c.tags, sen.Tag)
c.mu.Unlock()
closeReply(r, err)
}
}
}
func (c *Client) closeTags(err error) {
c.mu.Lock()
defer c.mu.Unlock()
for _, r := range c.tags {
closeReply(r, err)
}
c.tags = nil
}
func closeReply(r sentenceProcessor, err error) {
rr, ok := r.(replyCloser)
if ok {
rr.close(err)
}
}

22
vendor/gopkg.in/routeros.v2/chan_reply.go generated vendored Normal file
View File

@@ -0,0 +1,22 @@
package routeros
import "gopkg.in/routeros.v2/proto"
// chanReply is shared between ListenReply and AsyncReply.
type chanReply struct {
tag string
err error
reC chan *proto.Sentence
}
// Err returns the first error that happened processing sentences with tag.
func (a *chanReply) Err() error {
return a.err
}
func (a *chanReply) close(err error) {
if a.err == nil {
a.err = err
}
close(a.reC)
}

115
vendor/gopkg.in/routeros.v2/client.go generated vendored Normal file
View File

@@ -0,0 +1,115 @@
/*
Package routeros is a pure Go client library for accessing Mikrotik devices using the RouterOS API.
*/
package routeros
import (
"crypto/md5"
"crypto/tls"
"encoding/hex"
"errors"
"fmt"
"io"
"net"
"sync"
"gopkg.in/routeros.v2/proto"
)
// Client is a RouterOS API client.
type Client struct {
Queue int
rwc io.ReadWriteCloser
r proto.Reader
w proto.Writer
closing bool
async bool
nextTag int64
tags map[string]sentenceProcessor
mu sync.Mutex
}
// NewClient returns a new Client over rwc. Login must be called.
func NewClient(rwc io.ReadWriteCloser) (*Client, error) {
return &Client{
rwc: rwc,
r: proto.NewReader(rwc),
w: proto.NewWriter(rwc),
}, nil
}
// Dial connects and logs in to a RouterOS device.
func Dial(address, username, password string) (*Client, error) {
conn, err := net.Dial("tcp", address)
if err != nil {
return nil, err
}
return newClientAndLogin(conn, address, username, password)
}
// DialTLS connects and logs in to a RouterOS device using TLS.
func DialTLS(address, username, password string, tlsConfig *tls.Config) (*Client, error) {
conn, err := tls.Dial("tcp", address, tlsConfig)
if err != nil {
return nil, err
}
return newClientAndLogin(conn, address, username, password)
}
func newClientAndLogin(rwc io.ReadWriteCloser, address, username, password string) (*Client, error) {
c, err := NewClient(rwc)
if err != nil {
rwc.Close()
return nil, err
}
err = c.Login(username, password)
if err != nil {
c.Close()
return nil, err
}
return c, nil
}
// Close closes the connection to the RouterOS device.
func (c *Client) Close() {
c.mu.Lock()
if c.closing {
c.mu.Unlock()
return
}
c.closing = true
c.mu.Unlock()
c.rwc.Close()
}
// Login runs the /login command. Dial and DialTLS call this automatically.
func (c *Client) Login(username, password string) error {
r, err := c.Run("/login")
if err != nil {
return err
}
ret, ok := r.Done.Map["ret"]
if !ok {
return errors.New("RouterOS: /login: no ret (challenge) received")
}
b, err := hex.DecodeString(ret)
if err != nil {
return fmt.Errorf("RouterOS: /login: invalid ret (challenge) hex string received: %s", err)
}
r, err = c.Run("/login", "=name="+username, "=response="+c.challengeResponse(b, password))
if err != nil {
return err
}
return nil
}
func (c *Client) challengeResponse(cha []byte, password string) string {
h := md5.New()
h.Write([]byte{0})
io.WriteString(h, password)
h.Write(cha)
return fmt.Sprintf("00%x", h.Sum(nil))
}

120
vendor/gopkg.in/routeros.v2/client_test.go generated vendored Normal file
View File

@@ -0,0 +1,120 @@
package routeros
import (
"flag"
"testing"
)
var (
routerosAddress = flag.String("routeros.address", "", "RouterOS address:port")
routerosUsername = flag.String("routeros.username", "admin", "RouterOS user name")
routerosPassword = flag.String("routeros.password", "admin", "RouterOS password")
)
type liveTest struct {
*testing.T
c *Client
}
func newLiveTest(t *testing.T) *liveTest {
tt := &liveTest{T: t}
tt.connect()
return tt
}
func (t *liveTest) connect() {
if *routerosAddress == "" {
t.Skip("Flag -routeros.address not set")
}
var err error
t.c, err = Dial(*routerosAddress, *routerosUsername, *routerosPassword)
if err != nil {
t.Fatal(err)
}
}
func (t *liveTest) run(sentence ...string) *Reply {
t.Logf("Run: %#q", sentence)
r, err := t.c.RunArgs(sentence)
if err != nil {
t.Fatal(err)
}
t.Logf("Reply: %s", r)
return r
}
func (t *liveTest) getUptime() {
r := t.run("/system/resource/print")
if len(r.Re) != 1 {
t.Fatalf("len(!re)=%d; want 1", len(r.Re))
}
_, ok := r.Re[0].Map["uptime"]
if !ok {
t.Fatal("Missing uptime")
}
}
func TestRunSync(tt *testing.T) {
t := newLiveTest(tt)
defer t.c.Close()
t.getUptime()
}
func TestRunAsync(tt *testing.T) {
t := newLiveTest(tt)
defer t.c.Close()
t.c.Async()
t.getUptime()
}
func TestRunError(tt *testing.T) {
t := newLiveTest(tt)
defer t.c.Close()
for i, sentence := range [][]string{
{"/xxx"},
{"/ip/address/add", "=address=127.0.0.2/32", "=interface=xxx"},
} {
t.Logf("#%d: Run: %#q", i, sentence)
_, err := t.c.RunArgs(sentence)
if err == nil {
t.Error("Success; want error from RouterOS device trying to run an invalid command")
}
}
}
func TestDialInvalidPort(t *testing.T) {
c, err := Dial("127.0.0.1:xxx", "x", "x")
if err == nil {
c.Close()
t.Fatalf("Dial succeeded; want error")
}
if err.Error() != "dial tcp: lookup tcp/xxx: getaddrinfow: The specified class was not found." {
t.Fatal(err)
}
}
func TestDialTLSInvalidPort(t *testing.T) {
c, err := DialTLS("127.0.0.1:xxx", "x", "x", nil)
if err == nil {
c.Close()
t.Fatalf("Dial succeeded; want error")
}
if err.Error() != "dial tcp: lookup tcp/xxx: getaddrinfow: The specified class was not found." {
t.Fatal(err)
}
}
func TestInvalidLogin(t *testing.T) {
if *routerosAddress == "" {
t.Skip("Flag -routeros.address not set")
}
var err error
c, err := Dial(*routerosAddress, "xxx", "APasswordThatWillNeverExistir")
if err == nil {
c.Close()
t.Fatalf("Dial succeeded; want error")
}
if err.Error() != "from RouterOS device: cannot log in" {
t.Fatal(err)
}
}

35
vendor/gopkg.in/routeros.v2/error.go generated vendored Normal file
View File

@@ -0,0 +1,35 @@
package routeros
import (
"errors"
"gopkg.in/routeros.v2/proto"
)
var (
errAlreadyAsync = errors.New("Async() has already been called")
errAsyncLoopEnded = errors.New("Async() loop has ended - probably read error")
)
// UnknownReplyError records the sentence whose Word is unknown.
type UnknownReplyError struct {
Sentence *proto.Sentence
}
func (err *UnknownReplyError) Error() string {
return "unknown RouterOS reply word: " + err.Sentence.Word
}
// DeviceError records the sentence containing the error received from the device.
// The sentence may have Word !trap or !fatal.
type DeviceError struct {
Sentence *proto.Sentence
}
func (err *DeviceError) Error() string {
m := err.Sentence.Map["message"]
if m == "" {
m = "unknown error: " + err.Sentence.String()
}
return "from RouterOS device: " + m
}

114
vendor/gopkg.in/routeros.v2/examples/listen/main.go generated vendored Normal file
View File

@@ -0,0 +1,114 @@
package main
import (
"flag"
"log"
"strings"
"time"
"gopkg.in/routeros.v2"
)
var (
command = flag.String("command", "/ip/firewall/address-list/listen", "RouterOS command")
address = flag.String("address", "127.0.0.1:8728", "RouterOS address and port")
username = flag.String("username", "admin", "User name")
password = flag.String("password", "admin", "Password")
timeout = flag.Duration("timeout", 10*time.Second, "Cancel after")
async = flag.Bool("async", false, "Call Async()")
useTLS = flag.Bool("tls", false, "Use TLS")
)
func dial() (*routeros.Client, error) {
if *useTLS {
return routeros.DialTLS(*address, *username, *password, nil)
}
return routeros.Dial(*address, *username, *password)
}
func main() {
flag.Parse()
c, err := dial()
if err != nil {
log.Fatal(err)
}
defer c.Close()
c.Queue = 100
if *async {
explicitAsync(c)
} else {
implicitAsync(c)
}
}
func explicitAsync(c *routeros.Client) {
errC := c.Async()
go func() {
l, err := c.ListenArgs(strings.Split(*command, " "))
if err != nil {
log.Fatal(err)
}
go func() {
time.Sleep(*timeout)
log.Print("Cancelling the RouterOS command...")
_, err := l.Cancel()
if err != nil {
log.Fatal(err)
}
}()
log.Print("Waiting for !re...")
for sen := range l.Chan() {
log.Printf("Update: %s", sen)
}
err = l.Err()
if err != nil {
log.Fatal(err)
}
log.Print("Done!")
c.Close()
}()
err := <-errC
if err != nil {
log.Fatal(err)
}
}
func implicitAsync(c *routeros.Client) {
l, err := c.ListenArgs(strings.Split(*command, " "))
if err != nil {
log.Fatal(err)
}
go func() {
time.Sleep(*timeout)
log.Print("Cancelling the RouterOS command...")
_, err := l.Cancel()
if err != nil {
log.Fatal(err)
}
}()
log.Print("Waiting for !re...")
for sen := range l.Chan() {
log.Printf("Update: %s", sen)
}
err = l.Err()
if err != nil {
log.Fatal(err)
}
log.Print("Done!")
c.Close()
}

46
vendor/gopkg.in/routeros.v2/examples/run/main.go generated vendored Normal file
View File

@@ -0,0 +1,46 @@
package main
import (
"flag"
"log"
"strings"
"gopkg.in/routeros.v2"
)
var (
command = flag.String("command", "/system/resource/print", "RouterOS command")
address = flag.String("address", "127.0.0.1:8728", "RouterOS address and port")
username = flag.String("username", "admin", "User name")
password = flag.String("password", "admin", "Password")
async = flag.Bool("async", false, "Use async code")
useTLS = flag.Bool("tls", false, "Use TLS")
)
func dial() (*routeros.Client, error) {
if *useTLS {
return routeros.DialTLS(*address, *username, *password, nil)
}
return routeros.Dial(*address, *username, *password)
}
func main() {
flag.Parse()
c, err := dial()
if err != nil {
log.Fatal(err)
}
defer c.Close()
if *async {
c.Async()
}
r, err := c.RunArgs(strings.Split(*command, " "))
if err != nil {
log.Fatal(err)
}
log.Print(r)
}

45
vendor/gopkg.in/routeros.v2/examples/tab/main.go generated vendored Normal file
View File

@@ -0,0 +1,45 @@
package main
import (
"flag"
"fmt"
"log"
"strings"
"time"
"gopkg.in/routeros.v2"
)
var (
address = flag.String("address", "192.168.0.1:8728", "Address")
username = flag.String("username", "admin", "Username")
password = flag.String("password", "admin", "Password")
properties = flag.String("properties", "name,rx-byte,tx-byte,rx-packet,tx-packet", "Properties")
interval = flag.Duration("interval", 1*time.Second, "Interval")
)
func main() {
flag.Parse()
c, err := routeros.Dial(*address, *username, *password)
if err != nil {
log.Fatal(err)
}
for {
reply, err := c.Run("/interface/print", "?disabled=false", "?running=true", "=.proplist="+*properties)
if err != nil {
log.Fatal(err)
}
for _, re := range reply.Re {
for _, p := range strings.Split(*properties, ",") {
fmt.Print(re.Map[p], "\t")
}
fmt.Print("\n")
}
fmt.Print("\n")
time.Sleep(*interval)
}
}

91
vendor/gopkg.in/routeros.v2/listen.go generated vendored Normal file
View File

@@ -0,0 +1,91 @@
package routeros
import (
"fmt"
"gopkg.in/routeros.v2/proto"
)
// ListenReply is the struct returned by the Listen*() functions.
// When the channel returned by Chan() is closed, Done is set to the
// RouterOS sentence that caused it to be closed.
type ListenReply struct {
chanReply
Done *proto.Sentence
c *Client
}
// Chan returns a channel for receiving !re RouterOS sentences.
// To close the channel, call Cancel() on l.
func (l *ListenReply) Chan() <-chan *proto.Sentence {
return l.reC
}
// Cancel sends a cancel command to the RouterOS device.
func (l *ListenReply) Cancel() (*Reply, error) {
return l.c.Run("/cancel", "=tag="+l.tag)
}
// Listen simply calls ListenArgsQueue() with queueSize set to c.Queue.
func (c *Client) Listen(sentence ...string) (*ListenReply, error) {
return c.ListenArgsQueue(sentence, c.Queue)
}
// ListenArgs simply calls ListenArgsQueue() with queueSize set to c.Queue.
func (c *Client) ListenArgs(sentence []string) (*ListenReply, error) {
return c.ListenArgsQueue(sentence, c.Queue)
}
// ListenArgsQueue sends a sentence to the RouterOS device and returns immediately.
func (c *Client) ListenArgsQueue(sentence []string, queueSize int) (*ListenReply, error) {
if !c.async {
c.Async()
}
c.nextTag++
l := &ListenReply{c: c}
l.tag = fmt.Sprintf("l%d", c.nextTag)
l.reC = make(chan *proto.Sentence, queueSize)
c.w.BeginSentence()
for _, word := range sentence {
c.w.WriteWord(word)
}
c.w.WriteWord(".tag=" + l.tag)
c.mu.Lock()
defer c.mu.Unlock()
err := c.w.EndSentence()
if err != nil {
return nil, err
}
if c.tags == nil {
return nil, errAsyncLoopEnded
}
c.tags[l.tag] = l
return l, nil
}
func (l *ListenReply) processSentence(sen *proto.Sentence) (bool, error) {
switch sen.Word {
case "!re":
l.reC <- sen
case "!done":
l.Done = sen
return true, nil
case "!trap":
if sen.Map["category"] == "2" {
l.Done = sen // "execution of command interrupted"
return true, nil
}
return true, &DeviceError{sen}
case "!fatal":
return true, &DeviceError{sen}
case "":
// API docs say that empty sentences should be ignored
default:
return true, &UnknownReplyError{sen}
}
return false, nil
}

110
vendor/gopkg.in/routeros.v2/proto/reader.go generated vendored Normal file
View File

@@ -0,0 +1,110 @@
package proto
import (
"bufio"
"bytes"
"fmt"
"io"
)
// Reader reads sentences from a RouterOS device.
type Reader interface {
ReadSentence() (*Sentence, error)
}
type reader struct {
*bufio.Reader
}
// NewReader returns a new Reader to read from r.
func NewReader(r io.Reader) Reader {
return &reader{bufio.NewReader(r)}
}
// ReadSentence reads a sentence.
func (r *reader) ReadSentence() (*Sentence, error) {
sen := NewSentence()
for {
b, err := r.readWord()
if err != nil {
return nil, err
}
if len(b) == 0 {
return sen, nil
}
// Ex.: !re, !done
if sen.Word == "" {
sen.Word = string(b)
continue
}
// Command tag.
if bytes.HasPrefix(b, []byte(".tag=")) {
sen.Tag = string(b[5:])
continue
}
// Ex.: =key=value, =key
if bytes.HasPrefix(b, []byte("=")) {
t := bytes.SplitN(b[1:], []byte("="), 2)
if len(t) == 1 {
t = append(t, []byte{})
}
p := Pair{string(t[0]), string(t[1])}
sen.List = append(sen.List, p)
sen.Map[p.Key] = p.Value
continue
}
return nil, fmt.Errorf("invalid RouterOS sentence word: %#q", b)
}
}
func (r *reader) readNumber(size int) (int64, error) {
b := make([]byte, size)
_, err := io.ReadFull(r, b)
if err != nil {
return -1, err
}
var num int64
for _, ch := range b {
num = num<<8 | int64(ch)
}
return num, nil
}
func (r *reader) readLength() (int64, error) {
l, err := r.readNumber(1)
if err != nil {
return -1, err
}
var n int64
switch {
case l&0x80 == 0x00:
case (l & 0xC0) == 0x80:
n, err = r.readNumber(1)
l = l & ^0xC0 << 8 | n
case l&0xE0 == 0xC0:
n, err = r.readNumber(2)
l = l & ^0xE0 << 16 | n
case l&0xF0 == 0xE0:
n, err = r.readNumber(3)
l = l & ^0xF0 << 24 | n
case l&0xF8 == 0xF0:
l, err = r.readNumber(4)
}
if err != nil {
return -1, err
}
return l, nil
}
func (r *reader) readWord() ([]byte, error) {
l, err := r.readLength()
if err != nil {
return nil, err
}
b := make([]byte, l)
_, err = io.ReadFull(r, b)
if err != nil {
return nil, err
}
return b, nil
}

28
vendor/gopkg.in/routeros.v2/proto/reader_test.go generated vendored Normal file
View File

@@ -0,0 +1,28 @@
package proto
import (
"bytes"
"testing"
)
func TestReadLength(t *testing.T) {
for _, d := range []struct {
length int64
rawBytes []byte
}{
{0x00000001, []byte{0x01}},
{0x00000087, []byte{0x80, 0x87}},
{0x00004321, []byte{0xC0, 0x43, 0x21}},
{0x002acdef, []byte{0xE0, 0x2a, 0xcd, 0xef}},
{0x10000080, []byte{0xF0, 0x10, 0x00, 0x00, 0x80}},
} {
r := NewReader(bytes.NewBuffer(d.rawBytes)).(*reader)
l, err := r.readLength()
if err != nil {
t.Fatalf("readLength error: %s", err)
}
if l != d.length {
t.Fatalf("Expected len=%X for input %#v, got %X", d.length, d.rawBytes, l)
}
}
}

26
vendor/gopkg.in/routeros.v2/proto/sentence.go generated vendored Normal file
View File

@@ -0,0 +1,26 @@
package proto
import "fmt"
// Sentence is a line read from a RouterOS device.
type Sentence struct {
// Word that begins with !
Word string
Tag string
List []Pair
Map map[string]string
}
type Pair struct {
Key, Value string
}
func NewSentence() *Sentence {
return &Sentence{
Map: make(map[string]string),
}
}
func (sen *Sentence) String() string {
return fmt.Sprintf("%s @%s %#q", sen.Word, sen.Tag, sen.List)
}

42
vendor/gopkg.in/routeros.v2/proto/sentence_test.go generated vendored Normal file
View File

@@ -0,0 +1,42 @@
package proto
import (
"bytes"
"fmt"
"strings"
"testing"
)
func TestReadWrite(t *testing.T) {
for i, test := range []struct {
in []string
out string
tag string
}{
{[]string{"!done"}, `[]`, ""},
{[]string{"!done", ".tag=abc123"}, `[]`, "abc123"},
{strings.Split("!re =tx-byte=123456789 =only-key", " "), "[{`tx-byte` `123456789`} {`only-key` ``}]", ""},
} {
buf := &bytes.Buffer{}
// Write sentence into buf.
w := NewWriter(buf)
for _, word := range test.in {
w.WriteWord(word)
}
w.WriteWord("")
// Read sentence from buf.
r := NewReader(buf)
sen, err := r.ReadSentence()
if err != nil {
t.Errorf("#%d: Input(%#q)=%#v", i, test.in, err)
continue
}
x := fmt.Sprintf("%#q", sen.List)
if x != test.out {
t.Errorf("#%d: Input(%#q)=%s; want %s", i, test.in, x, test.out)
}
if sen.Tag != test.tag {
t.Errorf("#%d: Input(%#q)=%s; want %s", i, test.in, sen.Tag, test.tag)
}
}
}

82
vendor/gopkg.in/routeros.v2/proto/writer.go generated vendored Normal file
View File

@@ -0,0 +1,82 @@
package proto
import (
"bufio"
"io"
"sync"
)
// Writer writes words to a RouterOS device.
type Writer interface {
BeginSentence()
WriteWord(word string)
EndSentence() error
}
type writer struct {
*bufio.Writer
err error
sync.Mutex
}
// NewWriter returns a new Writer to write to w.
func NewWriter(w io.Writer) Writer {
return &writer{Writer: bufio.NewWriter(w)}
}
// BeginSentence prepares w for writing a sentence.
func (w *writer) BeginSentence() {
w.Lock()
}
// EndSentence writes the end-of-sentence marker (an empty word).
// It returns the first error that occurred on calls to methods on w.
func (w *writer) EndSentence() error {
w.WriteWord("")
w.flush()
err := w.err
w.Unlock()
return err
}
// WriteWord writes one word.
func (w *writer) WriteWord(word string) {
b := []byte(word)
w.write(encodeLength(len(b)))
w.write(b)
}
func (w *writer) flush() {
if w.err != nil {
return
}
err := w.Flush()
if err != nil {
w.err = err
}
}
func (w *writer) write(b []byte) {
if w.err != nil {
return
}
_, err := w.Write(b)
if err != nil {
w.err = err
}
}
func encodeLength(l int) []byte {
switch {
case l < 0x80:
return []byte{byte(l)}
case l < 0x4000:
return []byte{byte(l>>8) | 0x80, byte(l)}
case l < 0x200000:
return []byte{byte(l>>16) | 0xC0, byte(l >> 8), byte(l)}
case l < 0x10000000:
return []byte{byte(l>>24) | 0xE0, byte(l >> 16), byte(l >> 8), byte(l)}
default:
return []byte{0xF0, byte(l >> 24), byte(l >> 16), byte(l >> 8), byte(l)}
}
}

24
vendor/gopkg.in/routeros.v2/proto/writer_test.go generated vendored Normal file
View File

@@ -0,0 +1,24 @@
package proto
import (
"bytes"
"testing"
)
func TestEncodeLength(t *testing.T) {
for _, d := range []struct {
length int
rawBytes []byte
}{
{0x00000001, []byte{0x01}},
{0x00000087, []byte{0x80, 0x87}},
{0x00004321, []byte{0xC0, 0x43, 0x21}},
{0x002acdef, []byte{0xE0, 0x2a, 0xcd, 0xef}},
{0x10000080, []byte{0xF0, 0x10, 0x00, 0x00, 0x80}},
} {
b := encodeLength(d.length)
if bytes.Compare(b, d.rawBytes) != 0 {
t.Fatalf("Expected output %#v for len=%d, got %#v", d.rawBytes, d.length, b)
}
}
}

450
vendor/gopkg.in/routeros.v2/proto_test.go generated vendored Normal file
View File

@@ -0,0 +1,450 @@
package routeros_test
import (
"fmt"
"io"
"testing"
"gopkg.in/routeros.v2"
"gopkg.in/routeros.v2/proto"
)
func TestLogin(t *testing.T) {
c, s := newPair(t)
defer c.Close()
go func() {
defer s.Close()
s.readSentence(t, "/login @ []")
s.writeSentence(t, "!done", "=ret=abc123")
s.readSentence(t, "/login @ [{`name` `userTest`} {`response` `0021277bff9ac7caf06aa608e46616d47f`}]")
s.writeSentence(t, "!done")
}()
err := c.Login("userTest", "passTest")
if err != nil {
t.Fatal(err)
}
}
func TestLoginIncorrect(t *testing.T) {
c, s := newPair(t)
defer c.Close()
go func() {
defer s.Close()
s.readSentence(t, "/login @ []")
s.writeSentence(t, "!done", "=ret=abc123")
s.readSentence(t, "/login @ [{`name` `userTest`} {`response` `0021277bff9ac7caf06aa608e46616d47f`}]")
s.writeSentence(t, "!trap", "=message=incorrect login")
}()
err := c.Login("userTest", "passTest")
if err == nil {
t.Fatalf("Login succeeded; want error")
}
if err.Error() != "from RouterOS device: incorrect login" {
t.Fatal(err)
}
}
func TestLoginNoChallenge(t *testing.T) {
c, s := newPair(t)
defer c.Close()
go func() {
defer s.Close()
s.readSentence(t, "/login @ []")
s.writeSentence(t, "!done")
}()
err := c.Login("userTest", "passTest")
if err == nil {
t.Fatalf("Login succeeded; want error")
}
if err.Error() != "RouterOS: /login: no ret (challenge) received" {
t.Fatal(err)
}
}
func TestLoginInvalidChallenge(t *testing.T) {
c, s := newPair(t)
defer c.Close()
go func() {
defer s.Close()
s.readSentence(t, "/login @ []")
s.writeSentence(t, "!done", "=ret=Invalid Hex String")
}()
err := c.Login("userTest", "passTest")
if err == nil {
t.Fatalf("Login succeeded; want error")
}
if err.Error() != "RouterOS: /login: invalid ret (challenge) hex string received: encoding/hex: invalid byte: U+0049 'I'" {
t.Fatal(err)
}
}
func TestLoginEOF(t *testing.T) {
c, s := newPair(t)
defer c.Close()
s.Close()
err := c.Login("userTest", "passTest")
if err == nil {
t.Fatalf("Login succeeded; want error")
}
if err.Error() != "io: read/write on closed pipe" {
t.Fatal(err)
}
}
func TestCloseTwice(t *testing.T) {
c, s := newPair(t)
defer s.Close()
c.Close()
c.Close()
}
func TestAsyncTwice(t *testing.T) {
c, s := newPair(t)
defer c.Close()
defer s.Close()
c.Async()
errC := c.Async()
err := <-errC
want := "Async() has already been called"
if err.Error() != want {
t.Fatalf("Second Async()=%#q; want %#q", err, want)
}
err = <-errC
if err != nil {
t.Fatalf("Async() channel should be closed after error; got %#q", err)
}
}
func TestRun(t *testing.T) {
c, s := newPair(t)
defer c.Close()
go func() {
defer s.Close()
s.readSentence(t, "/ip/address @ []")
s.writeSentence(t, "!re", "=address=1.2.3.4/32")
s.writeSentence(t, "!done")
}()
sen, err := c.Run("/ip/address")
if err != nil {
t.Fatal(err)
}
want := "!re @ [{`address` `1.2.3.4/32`}]\n!done @ []"
if sen.String() != want {
t.Fatalf("/ip/address (%s); want (%s)", sen, want)
}
}
func TestRunWithListen(t *testing.T) {
c, s := newPair(t)
defer c.Close()
go func() {
defer s.Close()
s.readSentence(t, "/ip/address @l1 []")
s.writeSentence(t, "!re", ".tag=l1", "=address=1.2.3.4/32")
s.writeSentence(t, "!done", ".tag=l1")
}()
listen, err := c.Listen("/ip/address")
if err != nil {
t.Fatal(err)
}
sen := <-listen.Chan()
want := "!re @l1 [{`address` `1.2.3.4/32`}]"
if fmt.Sprintf("%s", sen) != want {
t.Fatalf("/ip/address (%s); want (%s)", sen, want)
}
sen = <-listen.Chan()
if sen != nil {
t.Fatalf("Listen() channel should be closed after EOF; got %#q", sen)
}
err = listen.Err()
if err != nil {
t.Fatal(err)
}
}
func TestRunAsync(t *testing.T) {
c, s := newPair(t)
defer c.Close()
c.Async()
go func() {
defer s.Close()
s.readSentence(t, "/ip/address @r1 []")
s.writeSentence(t, "!re", ".tag=r1", "=address=1.2.3.4/32")
s.writeSentence(t, "!done", ".tag=r1")
}()
sen, err := c.Run("/ip/address")
if err != nil {
t.Fatal(err)
}
want := "!re @r1 [{`address` `1.2.3.4/32`}]\n!done @r1 []"
if sen.String() != want {
t.Fatalf("/ip/address (%s); want (%s)", sen, want)
}
}
func TestRunEmptySentence(t *testing.T) {
c, s := newPair(t)
defer c.Close()
go func() {
defer s.Close()
s.readSentence(t, "/ip/address @ []")
s.writeSentence(t)
s.writeSentence(t, "!re", "=address=1.2.3.4/32")
s.writeSentence(t, "!done")
}()
sen, err := c.Run("/ip/address")
if err != nil {
t.Fatal(err)
}
want := "!re @ [{`address` `1.2.3.4/32`}]\n!done @ []"
if sen.String() != want {
t.Fatalf("/ip/address (%s); want (%s)", sen, want)
}
}
func TestRunEOF(t *testing.T) {
c, s := newPair(t)
defer c.Close()
go func() {
defer s.Close()
s.readSentence(t, "/ip/address @ []")
}()
_, err := c.Run("/ip/address")
if err == nil {
t.Fatalf("Run succeeded; want error")
}
if err != io.EOF {
t.Fatal(err)
}
}
func TestRunEOFAsync(t *testing.T) {
c, s := newPair(t)
defer c.Close()
c.Async()
go func() {
defer s.Close()
s.readSentence(t, "/ip/address @r1 []")
s.writeSentence(t, "!re", "=address=1.2.3.4/32")
}()
_, err := c.Run("/ip/address")
if err == nil {
t.Fatalf("Run succeeded; want error")
}
if err != io.EOF {
t.Fatal(err)
}
}
func TestRunInvalidSentence(t *testing.T) {
c, s := newPair(t)
defer c.Close()
go func() {
defer s.Close()
s.readSentence(t, "/ip/address @ []")
s.writeSentence(t, "!xxx")
}()
_, err := c.Run("/ip/address")
if err == nil {
t.Fatalf("Run succeeded; want error")
}
if err.Error() != "unknown RouterOS reply word: !xxx" {
t.Fatal(err)
}
}
func TestRunTrap(t *testing.T) {
c, s := newPair(t)
defer c.Close()
go func() {
defer s.Close()
s.readSentence(t, "/ip/address @ []")
s.writeSentence(t, "!trap", "=message=Some device error message")
}()
_, err := c.Run("/ip/address")
if err == nil {
t.Fatalf("Run succeeded; want error")
}
if err.Error() != "from RouterOS device: Some device error message" {
t.Fatal(err)
}
}
func TestRunMesagelessTrap(t *testing.T) {
c, s := newPair(t)
defer c.Close()
go func() {
defer s.Close()
s.readSentence(t, "/ip/address @ []")
s.writeSentence(t, "!trap", "=some=unknown key")
}()
_, err := c.Run("/ip/address")
if err == nil {
t.Fatalf("Run succeeded; want error")
}
if err.Error() != "from RouterOS device: unknown error: !trap @ [{`some` `unknown key`}]" {
t.Fatal(err)
}
}
func TestRunFatal(t *testing.T) {
c, s := newPair(t)
defer c.Close()
go func() {
defer s.Close()
s.readSentence(t, "/ip/address @ []")
s.writeSentence(t, "!fatal", "=message=Some device error message")
}()
_, err := c.Run("/ip/address")
if err == nil {
t.Fatalf("Run succeeded; want error")
}
if err.Error() != "from RouterOS device: Some device error message" {
t.Fatal(err)
}
}
func TestRunAfterClose(t *testing.T) {
c, s := newPair(t)
c.Close()
s.Close()
_, err := c.Run("/ip/address")
if err == nil {
t.Fatalf("Run succeeded; want error")
}
if err.Error() != "io: read/write on closed pipe" {
t.Fatal(err)
}
}
func TestListen(t *testing.T) {
c, s := newPair(t)
defer c.Close()
go func() {
defer s.Close()
s.readSentence(t, "/ip/address/listen @l1 []")
s.writeSentence(t, "!re", ".tag=l1", "=address=1.2.3.4/32")
s.readSentence(t, "/cancel @r2 [{`tag` `l1`}]")
s.writeSentence(t, "!trap", "=category=2", ".tag=l1")
s.writeSentence(t, "!done", "=tag=r2")
s.writeSentence(t, "!done", "=tag=l1")
}()
c.Queue = 1
listen, err := c.Listen("/ip/address/listen")
if err != nil {
t.Fatal(err)
}
reC := listen.Chan()
listen.Cancel()
sen := <-reC
want := "!re @l1 [{`address` `1.2.3.4/32`}]"
if fmt.Sprintf("%s", sen) != want {
t.Fatalf("/ip/address/listen (%s); want (%s)", sen, want)
}
sen = <-reC
if sen != nil {
t.Fatalf("Listen() channel should be closed after Close(); got %#q", sen)
}
err = listen.Err()
if err != nil {
t.Fatal(err)
}
}
type conn struct {
*io.PipeReader
*io.PipeWriter
}
func (c *conn) Close() error {
c.PipeReader.Close()
c.PipeWriter.Close()
return nil
}
func newPair(t *testing.T) (*routeros.Client, *fakeServer) {
ar, aw := io.Pipe()
br, bw := io.Pipe()
c, err := routeros.NewClient(&conn{ar, bw})
if err != nil {
t.Fatal(err)
}
s := &fakeServer{
proto.NewReader(br),
proto.NewWriter(aw),
&conn{br, aw},
}
return c, s
}
type fakeServer struct {
r proto.Reader
w proto.Writer
io.Closer
}
func (f *fakeServer) readSentence(t *testing.T, want string) {
sen, err := f.r.ReadSentence()
if err != nil {
t.Fatal(err)
}
if sen.String() != want {
t.Fatalf("Sentence (%s); want (%s)", sen.String(), want)
}
t.Logf("< %s\n", sen)
}
func (f *fakeServer) writeSentence(t *testing.T, sentence ...string) {
t.Logf("> %#q\n", sentence)
f.w.BeginSentence()
for _, word := range sentence {
f.w.WriteWord(word)
}
err := f.w.EndSentence()
if err != nil {
t.Fatal(err)
}
}

58
vendor/gopkg.in/routeros.v2/reply.go generated vendored Normal file
View File

@@ -0,0 +1,58 @@
package routeros
import (
"bytes"
"fmt"
"gopkg.in/routeros.v2/proto"
)
// Reply has all the sentences from a reply.
type Reply struct {
Re []*proto.Sentence
Done *proto.Sentence
}
func (r *Reply) String() string {
b := &bytes.Buffer{}
for _, re := range r.Re {
fmt.Fprintf(b, "%s\n", re)
}
fmt.Fprintf(b, "%s", r.Done)
return b.String()
}
// readReply reads one reply synchronously. It returns the reply.
func (c *Client) readReply() (*Reply, error) {
r := &Reply{}
for {
sen, err := c.r.ReadSentence()
if err != nil {
return nil, err
}
done, err := r.processSentence(sen)
if err != nil {
return nil, err
}
if done {
return r, nil
}
}
}
func (r *Reply) processSentence(sen *proto.Sentence) (bool, error) {
switch sen.Word {
case "!re":
r.Re = append(r.Re, sen)
case "!done":
r.Done = sen
return true, nil
case "!trap", "!fatal":
return true, &DeviceError{sen}
case "":
// API docs say that empty sentences should be ignored
default:
return true, &UnknownReplyError{sen}
}
return false, nil
}

64
vendor/gopkg.in/routeros.v2/run.go generated vendored Normal file
View File

@@ -0,0 +1,64 @@
package routeros
import (
"fmt"
"gopkg.in/routeros.v2/proto"
)
type asyncReply struct {
chanReply
Reply
}
// Run simply calls RunArgs().
func (c *Client) Run(sentence ...string) (*Reply, error) {
return c.RunArgs(sentence)
}
// RunArgs sends a sentence to the RouterOS device and waits for the reply.
func (c *Client) RunArgs(sentence []string) (*Reply, error) {
c.w.BeginSentence()
for _, word := range sentence {
c.w.WriteWord(word)
}
if !c.async {
return c.endCommandSync()
}
a, err := c.endCommandAsync()
if err != nil {
return nil, err
}
for range a.reC {
}
return &a.Reply, a.err
}
func (c *Client) endCommandSync() (*Reply, error) {
err := c.w.EndSentence()
if err != nil {
return nil, err
}
return c.readReply()
}
func (c *Client) endCommandAsync() (*asyncReply, error) {
c.nextTag++
a := &asyncReply{}
a.reC = make(chan *proto.Sentence)
a.tag = fmt.Sprintf("r%d", c.nextTag)
c.w.WriteWord(".tag=" + a.tag)
c.mu.Lock()
defer c.mu.Unlock()
err := c.w.EndSentence()
if err != nil {
return nil, err
}
if c.tags == nil {
return nil, errAsyncLoopEnded
}
c.tags[a.tag] = a
return a, nil
}