glide setup and dependencies addition
This commit is contained in:
5
vendor/gopkg.in/routeros.v2/.gitignore
generated
vendored
Normal file
5
vendor/gopkg.in/routeros.v2/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
.vscode
|
||||
.idea
|
||||
*.exe
|
||||
*.out
|
||||
*.test
|
||||
21
vendor/gopkg.in/routeros.v2/LICENSE
generated
vendored
Normal file
21
vendor/gopkg.in/routeros.v2/LICENSE
generated
vendored
Normal 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
14
vendor/gopkg.in/routeros.v2/README.md
generated
vendored
Normal 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
85
vendor/gopkg.in/routeros.v2/async.go
generated
vendored
Normal 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
22
vendor/gopkg.in/routeros.v2/chan_reply.go
generated
vendored
Normal 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
115
vendor/gopkg.in/routeros.v2/client.go
generated
vendored
Normal 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
120
vendor/gopkg.in/routeros.v2/client_test.go
generated
vendored
Normal 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
35
vendor/gopkg.in/routeros.v2/error.go
generated
vendored
Normal 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
114
vendor/gopkg.in/routeros.v2/examples/listen/main.go
generated
vendored
Normal 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
46
vendor/gopkg.in/routeros.v2/examples/run/main.go
generated
vendored
Normal 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
45
vendor/gopkg.in/routeros.v2/examples/tab/main.go
generated
vendored
Normal 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
91
vendor/gopkg.in/routeros.v2/listen.go
generated
vendored
Normal 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
110
vendor/gopkg.in/routeros.v2/proto/reader.go
generated
vendored
Normal 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
28
vendor/gopkg.in/routeros.v2/proto/reader_test.go
generated
vendored
Normal 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
26
vendor/gopkg.in/routeros.v2/proto/sentence.go
generated
vendored
Normal 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
42
vendor/gopkg.in/routeros.v2/proto/sentence_test.go
generated
vendored
Normal 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
82
vendor/gopkg.in/routeros.v2/proto/writer.go
generated
vendored
Normal 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
24
vendor/gopkg.in/routeros.v2/proto/writer_test.go
generated
vendored
Normal 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
450
vendor/gopkg.in/routeros.v2/proto_test.go
generated
vendored
Normal 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
58
vendor/gopkg.in/routeros.v2/reply.go
generated
vendored
Normal 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
64
vendor/gopkg.in/routeros.v2/run.go
generated
vendored
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user