diff --git a/vendor/github.com/k-sone/snmpgo/.gitrepo b/vendor/github.com/k-sone/snmpgo/.gitrepo new file mode 100644 index 0000000000..6f95e41371 --- /dev/null +++ b/vendor/github.com/k-sone/snmpgo/.gitrepo @@ -0,0 +1,11 @@ +; DO NOT EDIT (unless you know what you are doing) +; +; This subdirectory is a git "subrepo", and this file is maintained by the +; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme +; +[subrepo] + remote = https://github.com/k-sone/snmpgo.git + branch = master + commit = 1cd4bf6f9c149485731a3cacb6c598f52a2bf98e + parent = c21d0bb5280348660e6dae6b2be7b375652047b2 + cmdver = 0.3.0 diff --git a/vendor/github.com/k-sone/snmpgo/.travis.yml b/vendor/github.com/k-sone/snmpgo/.travis.yml new file mode 100644 index 0000000000..9f72319d9c --- /dev/null +++ b/vendor/github.com/k-sone/snmpgo/.travis.yml @@ -0,0 +1,13 @@ +language: go + +go: + - 1.5 + - 1.6 + - 1.7 + +install: + - go get -d -t -v ./... + +script: + - GOOS=linux GOARCH=amd64 go test -v . + - GOOS=linux GOARCH=386 go test -v . diff --git a/vendor/github.com/k-sone/snmpgo/CHANGES.md b/vendor/github.com/k-sone/snmpgo/CHANGES.md new file mode 100644 index 0000000000..4ae191d367 --- /dev/null +++ b/vendor/github.com/k-sone/snmpgo/CHANGES.md @@ -0,0 +1,36 @@ +## 3.0.2 (2016/12/10) + +- Fix for data race in generating request ids [#12](https://github.com/k-sone/snmpgo/pull/12) + +## 3.0.1 (2016/08/30) + +- Fix a bug that denying NoAuth trap, even when server is NoAuth mode + +## 3.0.0 (2016/08/27) + +- Correction to allow 32 bits compilation [#8](https://github.com/k-sone/snmpgo/pull/8) +- Support for receiving of trap events (V3) [#10](https://github.com/k-sone/snmpgo/pull/10) + +#### Breaking Changes +- Change to return an error from `TrapServer.DeleteSecurity` + +## 2.0.1 (2016/05/26) + +- Raise an error when unmarshalling an invalid SNMP version + +## 2.0.0 (2016/02/11) + +- Support for receiving of trap events (V2c only) [#4](https://github.com/k-sone/snmpgo/pull/4) + +#### Breaking Changes + +- Change to return a pointer of xxError struct [#1](https://github.com/k-sone/snmpgo/pull/1) +- Rename `ResponseError` to `MessageError` + +## 1.0.1 (2015/07/12) + +- Fix validating authoritative engine + +## 1.0.0 (2015/01/24) + +- Initial release diff --git a/vendor/github.com/k-sone/snmpgo/LICENSE b/vendor/github.com/k-sone/snmpgo/LICENSE new file mode 100644 index 0000000000..87c9c56a9f --- /dev/null +++ b/vendor/github.com/k-sone/snmpgo/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2014 Keita Sone + +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. + diff --git a/vendor/github.com/k-sone/snmpgo/README.md b/vendor/github.com/k-sone/snmpgo/README.md new file mode 100644 index 0000000000..7727c4a155 --- /dev/null +++ b/vendor/github.com/k-sone/snmpgo/README.md @@ -0,0 +1,128 @@ +[![Build Status](https://travis-ci.org/k-sone/snmpgo.svg?branch=master)](https://travis-ci.org/k-sone/snmpgo) +[![GoDoc](https://godoc.org/github.com/k-sone/snmpgo?status.svg)](http://godoc.org/github.com/k-sone/snmpgo) + +snmpgo +====== + +snmpgo is a golang implementation for SNMP. + +Supported Message Types +----------------------- + +### Sending + +* SNMP V1 + - GetRequest + - GetNextRequest +* SNMP V2c, V3 + - GetRequest + - GetNextRequest + - GetBulkRequest + - V2Trap + - InformRequest + +### Receiving + +* SNMP V2c + - V2Trap + - InformRequest +* SNMP V3 + - V2Trap + +Examples +-------- + +**[getv2.go](examples/getv2.go), [getv3.go](examples/getv3.go)** + +Example for sending a GetRequest. +Explain how to use basic of the API. + +```go +package main + +import ( + "fmt" + + "github.com/k-sone/snmpgo" +) + +func main() { + snmp, err := snmpgo.NewSNMP(snmpgo.SNMPArguments{ + Version: snmpgo.V2c, + Address: "127.0.0.1:161", + Retries: 1, + Community: "public", + }) + if err != nil { + // Failed to create snmpgo.SNMP object + fmt.Println(err) + return + } + + oids, err := snmpgo.NewOids([]string{ + "1.3.6.1.2.1.1.1.0", + "1.3.6.1.2.1.1.2.0", + "1.3.6.1.2.1.1.3.0", + }) + if err != nil { + // Failed to parse Oids + fmt.Println(err) + return + } + + if err = snmp.Open(); err != nil { + // Failed to open connection + fmt.Println(err) + return + } + defer snmp.Close() + + pdu, err := snmp.GetRequest(oids) + if err != nil { + // Failed to request + fmt.Println(err) + return + } + if pdu.ErrorStatus() != snmpgo.NoError { + // Received an error from the agent + fmt.Println(pdu.ErrorStatus(), pdu.ErrorIndex()) + } + + // get VarBind list + fmt.Println(pdu.VarBinds()) + + // select a VarBind + fmt.Println(pdu.VarBinds().MatchOid(oids[0])) +} +``` + +**[trapv2.go](examples/trapv2.go), [trapv3.go](examples/trapv3.go)** + +Example for sending a V2Trap. +Explain how to build varbinds using API. + +**[multiget.go](examples/multiget.go)** + +Example for sending a GetRequest to multiple agents. + +**[ifstat.go](examples/ifstat.go)** + +This command displays the traffic of agent at regular intervals. +Explain how to process the obtained information. + +**[snmpgoget.go](examples/snmpgoget.go)** + +[snmpget@Net-SNMP](http://www.net-snmp.org/docs/man/snmpget.html) like command. + +**[snmpgobulkwalk.go](examples/snmpgobulkwalk.go)** + +[snmpbulkwalk@Net-SNMP](http://www.net-snmp.org/docs/man/snmpbulkwalk.html) like command. + +**[snmpgotrap.go](examples/snmpgotrap.go)** + +[snmptrap@Net-SNMP](http://www.net-snmp.org/docs/man/snmptrap.html) like command. + +License +------- + +MIT diff --git a/vendor/github.com/k-sone/snmpgo/client.go b/vendor/github.com/k-sone/snmpgo/client.go new file mode 100644 index 0000000000..77d04f2ff5 --- /dev/null +++ b/vendor/github.com/k-sone/snmpgo/client.go @@ -0,0 +1,342 @@ +package snmpgo + +import ( + "fmt" + "math" + "net" + "time" +) + +// An argument for creating a SNMP Object +type SNMPArguments struct { + Version SNMPVersion // SNMP version to use + Network string // See net.Dial parameter (The default is `udp`) + Address string // See net.Dial parameter + Timeout time.Duration // Request timeout (The default is 5sec) + Retries uint // Number of retries (The default is `0`) + MessageMaxSize int // Maximum size of an SNMP message (The default is `1400`) + Community string // Community (V1 or V2c specific) + UserName string // Security name (V3 specific) + SecurityLevel SecurityLevel // Security level (V3 specific) + AuthPassword string // Authentication protocol pass phrase (V3 specific) + AuthProtocol AuthProtocol // Authentication protocol (V3 specific) + PrivPassword string // Privacy protocol pass phrase (V3 specific) + PrivProtocol PrivProtocol // Privacy protocol (V3 specific) + SecurityEngineId string // Security engine ID (V3 specific) + ContextEngineId string // Context engine ID (V3 specific) + ContextName string // Context name (V3 specific) + + authEngineBoots int + authEngineTime int +} + +func (a *SNMPArguments) setDefault() { + if a.Network == "" { + a.Network = "udp" + } + if a.Timeout <= 0 { + a.Timeout = timeoutDefault + } + if a.MessageMaxSize == 0 { + a.MessageMaxSize = msgSizeDefault + } +} + +func (a *SNMPArguments) validate() error { + if v := a.Version; v != V1 && v != V2c && v != V3 { + return &ArgumentError{ + Value: v, + Message: "Unknown SNMP Version", + } + } + // RFC3412 Section 6 + if m := a.MessageMaxSize; (m != 0 && m < msgSizeMinimum) || m > math.MaxInt32 { + return &ArgumentError{ + Value: m, + Message: fmt.Sprintf("MessageMaxSize is range %d..%d", + msgSizeMinimum, math.MaxInt32), + } + } + if a.Version == V3 { + // RFC3414 Section 5 + if l := len(a.UserName); l < 1 || l > 32 { + return &ArgumentError{ + Value: a.UserName, + Message: "UserName length is range 1..32", + } + } + if a.SecurityLevel > NoAuthNoPriv { + // RFC3414 Section 11.2 + if len(a.AuthPassword) < 8 { + return &ArgumentError{ + Value: a.AuthPassword, + Message: "AuthPassword is at least 8 characters in length", + } + } + if p := a.AuthProtocol; p != Md5 && p != Sha { + return &ArgumentError{ + Value: a.AuthProtocol, + Message: "Illegal AuthProtocol", + } + } + } + if a.SecurityLevel > AuthNoPriv { + // RFC3414 Section 11.2 + if len(a.PrivPassword) < 8 { + return &ArgumentError{ + Value: a.PrivPassword, + Message: "PrivPassword is at least 8 characters in length", + } + } + if p := a.PrivProtocol; p != Des && p != Aes { + return &ArgumentError{ + Value: a.PrivProtocol, + Message: "Illegal PrivProtocol", + } + } + } + if a.SecurityEngineId != "" { + a.SecurityEngineId = stripHexPrefix(a.SecurityEngineId) + _, err := engineIdToBytes(a.SecurityEngineId) + if err != nil { + return err + } + } + if a.ContextEngineId != "" { + a.ContextEngineId = stripHexPrefix(a.ContextEngineId) + _, err := engineIdToBytes(a.ContextEngineId) + if err != nil { + return err + } + } + } + return nil +} + +func (a *SNMPArguments) String() string { + return escape(a) +} + +// SNMP Object provides functions for the SNMP Client +type SNMP struct { + conn net.Conn + args *SNMPArguments + engine *snmpEngine +} + +// Open a connection +func (s *SNMP) Open() (err error) { + if s.conn != nil { + return + } + + err = retry(int(s.args.Retries), func() error { + conn, e := net.DialTimeout(s.args.Network, s.args.Address, s.args.Timeout) + if e == nil { + s.conn = conn + } + return e + }) + if err != nil { + return + } + + s.engine = newSNMPEngine(s.args) + if err = s.engine.Discover(s); err != nil { + s.Close() + } + return +} + +// Close a connection +func (s *SNMP) Close() { + if s.conn != nil { + s.conn.Close() + s.conn = nil + s.engine = nil + } +} + +func (s *SNMP) GetRequest(oids Oids) (result Pdu, err error) { + pdu := NewPduWithOids(s.args.Version, GetRequest, oids) + return s.sendPdu(pdu) +} + +func (s *SNMP) GetNextRequest(oids Oids) (result Pdu, err error) { + pdu := NewPduWithOids(s.args.Version, GetNextRequest, oids) + return s.sendPdu(pdu) +} + +func (s *SNMP) GetBulkRequest(oids Oids, nonRepeaters, maxRepetitions int) (result Pdu, err error) { + + if s.args.Version < V2c { + return nil, &ArgumentError{ + Value: s.args.Version, + Message: "Unsupported SNMP Version", + } + } + // RFC 3416 Section 3 + if nonRepeaters < 0 || nonRepeaters > math.MaxInt32 { + return nil, &ArgumentError{ + Value: nonRepeaters, + Message: fmt.Sprintf("NonRepeaters is range %d..%d", 0, math.MaxInt32), + } + } + if maxRepetitions < 0 || maxRepetitions > math.MaxInt32 { + return nil, &ArgumentError{ + Value: maxRepetitions, + Message: fmt.Sprintf("NonRepeaters is range %d..%d", 0, math.MaxInt32), + } + } + + pdu := NewPduWithOids(s.args.Version, GetBulkRequest, oids) + pdu.SetNonrepeaters(nonRepeaters) + pdu.SetMaxRepetitions(maxRepetitions) + return s.sendPdu(pdu) +} + +// This method inquire about OID subtrees by repeatedly using GetBulkRequest. +// Returned PDU contains the varbind list of all subtrees. +// however, if the ErrorStatus of PDU is not the NoError, return only the last query result. +func (s *SNMP) GetBulkWalk(oids Oids, nonRepeaters, maxRepetitions int) (result Pdu, err error) { + var nonRepBinds, resBinds VarBinds + + oids = append(oids[:nonRepeaters], oids[nonRepeaters:].Sort().UniqBase()...) + reqOids := make(Oids, len(oids)) + copy(reqOids, oids) + + for len(reqOids) > 0 { + pdu, err := s.GetBulkRequest(reqOids, nonRepeaters, maxRepetitions) + if err != nil { + return nil, err + } + if s := pdu.ErrorStatus(); s != NoError && + (s != NoSuchName || pdu.ErrorIndex() <= nonRepeaters) { + return pdu, nil + } + + varBinds := pdu.VarBinds() + + if nonRepeaters > 0 { + nonRepBinds = append(nonRepBinds, varBinds[:nonRepeaters]...) + varBinds = varBinds[nonRepeaters:] + oids = oids[nonRepeaters:] + reqOids = reqOids[nonRepeaters:] + nonRepeaters = 0 + } + + filled := len(varBinds) == len(reqOids)*maxRepetitions + varBinds = varBinds.Sort().Uniq() + + for i, _ := range reqOids { + matched := varBinds.MatchBaseOids(oids[i]) + mLength := len(matched) + + if mLength == 0 || resBinds.MatchOid(matched[mLength-1].Oid) != nil { + reqOids[i] = nil + continue + } + + hasError := false + for _, val := range matched { + switch val.Variable.(type) { + case *NoSucheObject, *NoSucheInstance, *EndOfMibView: + hasError = true + default: + resBinds = append(resBinds, val) + reqOids[i] = val.Oid + } + } + + if hasError || (filled && mLength < maxRepetitions) { + reqOids[i] = nil + } + } + + // sweep completed oids + for i := len(reqOids) - 1; i >= 0; i-- { + if reqOids[i] == nil { + reqOids = append(reqOids[:i], reqOids[i+1:]...) + oids = append(oids[:i], oids[i+1:]...) + } + } + } + + resBinds = append(nonRepBinds, resBinds.Sort().Uniq()...) + return NewPduWithVarBinds(s.args.Version, GetResponse, resBinds), nil +} + +func (s *SNMP) V2Trap(varBinds VarBinds) error { + return s.v2trap(SNMPTrapV2, varBinds) +} + +// Send trap with the authoritative engine boots and time when used with SNMP V3. +func (s *SNMP) V2TrapWithBootsTime(varBinds VarBinds, eBoots, eTime int) error { + if eBoots < 0 || eBoots > math.MaxInt32 { + return &ArgumentError{ + Value: eBoots, + Message: fmt.Sprintf("EngineBoots is range %d..%d", 0, math.MaxInt32), + } + } + if eTime < 0 || eTime > math.MaxInt32 { + return &ArgumentError{ + Value: eTime, + Message: fmt.Sprintf("EngineTime is range %d..%d", 0, math.MaxInt32), + } + } + + defer func() { + s.args.authEngineBoots = 0 + s.args.authEngineTime = 0 + }() + s.args.authEngineBoots = eBoots + s.args.authEngineTime = eTime + return s.v2trap(SNMPTrapV2, varBinds) +} + +func (s *SNMP) InformRequest(varBinds VarBinds) error { + return s.v2trap(InformRequest, varBinds) +} + +func (s *SNMP) v2trap(pduType PduType, varBinds VarBinds) (err error) { + if s.args.Version < V2c { + return &ArgumentError{ + Value: s.args.Version, + Message: "Unsupported SNMP Version", + } + } + + pdu := NewPduWithVarBinds(s.args.Version, pduType, varBinds) + _, err = s.sendPdu(pdu) + return +} + +func (s *SNMP) sendPdu(pdu Pdu) (result Pdu, err error) { + if err = s.Open(); err != nil { + return + } + + retry(int(s.args.Retries), func() error { + result, err = s.engine.SendPdu(pdu, s.conn, s.args) + return err + }) + return +} + +func (s *SNMP) String() string { + if s.conn == nil { + return fmt.Sprintf(`{"conn": false, "args": %s, "engine": null}`, s.args.String()) + } else { + return fmt.Sprintf(`{"conn": true, "args": %s, "engine": %s}`, + s.args.String(), s.engine.String()) + } +} + +// Create a SNMP Object +func NewSNMP(args SNMPArguments) (*SNMP, error) { + if err := args.validate(); err != nil { + return nil, err + } + args.setDefault() + return &SNMP{args: &args}, nil +} diff --git a/vendor/github.com/k-sone/snmpgo/client_test.go b/vendor/github.com/k-sone/snmpgo/client_test.go new file mode 100644 index 0000000000..abc68f6fa9 --- /dev/null +++ b/vendor/github.com/k-sone/snmpgo/client_test.go @@ -0,0 +1,114 @@ +package snmpgo_test + +import ( + "math" + "testing" + + "github.com/k-sone/snmpgo" +) + +func TestSNMPArguments(t *testing.T) { + args := &snmpgo.SNMPArguments{Version: 2} + err := snmpgo.ArgsValidate(args) + if err == nil { + t.Error("validate() - version check") + } + + args = &snmpgo.SNMPArguments{MessageMaxSize: -1} + err = snmpgo.ArgsValidate(args) + if err == nil { + t.Error("validate() - message size(min)") + } + + // skip on 32 bit arch + if (^uint(0) >> 63) > 0 { + s := int64(math.MaxInt32) + 1 + args = &snmpgo.SNMPArguments{MessageMaxSize: int(s)} + err = snmpgo.ArgsValidate(args) + if err == nil { + t.Error("validate() - message size(max)") + } + } + + args = &snmpgo.SNMPArguments{Version: snmpgo.V3} + err = snmpgo.ArgsValidate(args) + if err == nil { + t.Error("validate() - user name") + } + + args = &snmpgo.SNMPArguments{ + Version: snmpgo.V3, + UserName: "MyName", + SecurityLevel: snmpgo.AuthNoPriv, + } + err = snmpgo.ArgsValidate(args) + if err == nil { + t.Error("validate() - auth password") + } + + args = &snmpgo.SNMPArguments{ + Version: snmpgo.V3, + UserName: "MyName", + SecurityLevel: snmpgo.AuthNoPriv, + AuthPassword: "aaaaaaaa", + } + err = snmpgo.ArgsValidate(args) + if err == nil { + t.Error("validate() - auth protocol") + } + + args = &snmpgo.SNMPArguments{ + Version: snmpgo.V3, + UserName: "MyName", + SecurityLevel: snmpgo.AuthPriv, + AuthPassword: "aaaaaaaa", + AuthProtocol: snmpgo.Md5, + } + err = snmpgo.ArgsValidate(args) + if err == nil { + t.Error("validate() - priv password") + } + + args = &snmpgo.SNMPArguments{ + Version: snmpgo.V3, + UserName: "MyName", + SecurityLevel: snmpgo.AuthPriv, + AuthPassword: "aaaaaaaa", + AuthProtocol: snmpgo.Md5, + PrivPassword: "bbbbbbbb", + } + err = snmpgo.ArgsValidate(args) + if err == nil { + t.Error("validate() - priv protocol") + } + + args = &snmpgo.SNMPArguments{ + Version: snmpgo.V3, + UserName: "MyName", + SecurityLevel: snmpgo.AuthPriv, + AuthPassword: "aaaaaaaa", + AuthProtocol: snmpgo.Md5, + PrivPassword: "bbbbbbbb", + PrivProtocol: snmpgo.Des, + } + err = snmpgo.ArgsValidate(args) + if err != nil { + t.Errorf("validate() - has error %v", err) + } +} + +func TestSNMP(t *testing.T) { + _, err := snmpgo.NewSNMP(snmpgo.SNMPArguments{ + Version: snmpgo.V3, + UserName: "MyName", + SecurityLevel: snmpgo.AuthPriv, + AuthPassword: "aaaaaaaa", + AuthProtocol: snmpgo.Md5, + PrivPassword: "bbbbbbbb", + PrivProtocol: snmpgo.Des, + }) + + if err != nil { + t.Error("checkPdu() - report oid") + } +} diff --git a/vendor/github.com/k-sone/snmpgo/constants.go b/vendor/github.com/k-sone/snmpgo/constants.go new file mode 100644 index 0000000000..5f0540ec21 --- /dev/null +++ b/vendor/github.com/k-sone/snmpgo/constants.go @@ -0,0 +1,279 @@ +package snmpgo + +import ( + "time" +) + +type SNMPVersion int + +const ( + V1 SNMPVersion = 0 + V2c SNMPVersion = 1 + V3 SNMPVersion = 3 +) + +func (s SNMPVersion) String() string { + switch s { + case V1: + return "1" + case V2c: + return "2c" + case V3: + return "3" + default: + return "Unknown" + } +} + +type PduType int + +const ( + GetRequest PduType = iota + GetNextRequest + GetResponse + SetRequest + Trap + GetBulkRequest + InformRequest + SNMPTrapV2 + Report +) + +func (t PduType) String() string { + switch t { + case GetRequest: + return "GetRequest" + case GetNextRequest: + return "GetNextRequest" + case GetResponse: + return "GetResponse" + case SetRequest: + return "SetRequest" + case Trap: + return "Trap" + case GetBulkRequest: + return "GetBulkRequest" + case InformRequest: + return "InformRequest" + case SNMPTrapV2: + return "SNMPTrapV2" + case Report: + return "Report" + default: + return "Unknown" + } +} + +type ErrorStatus int + +const ( + NoError ErrorStatus = iota + TooBig + NoSuchName + BadValue + ReadOnly + GenError + NoAccess + WrongType + WrongLength + WrongEncoding + WrongValue + NoCreation + InconsistentValue + ResourceUnavailable + CommitFailed + UndoFailed + AuthorizationError + NotWritable + InconsistentName +) + +func (e ErrorStatus) String() string { + switch e { + case NoError: + return "NoError" + case TooBig: + return "TooBig" + case NoSuchName: + return "NoSuchName" + case BadValue: + return "BadValue" + case ReadOnly: + return "ReadOnly" + case GenError: + return "GenError" + case NoAccess: + return "NoAccess" + case WrongType: + return "WrongType" + case WrongLength: + return "WrongLength" + case WrongEncoding: + return "WrongEncoding" + case WrongValue: + return "WrongValue" + case NoCreation: + return "NoCreation" + case InconsistentValue: + return "InconsistentValue" + case ResourceUnavailable: + return "ResourceUnavailable" + case CommitFailed: + return "CommitFailed" + case UndoFailed: + return "UndoFailed" + case AuthorizationError: + return "AuthorizationError" + case NotWritable: + return "NotWritable" + case InconsistentName: + return "InconsistentName" + default: + return "Unknown" + } +} + +type SecurityLevel int + +const ( + NoAuthNoPriv SecurityLevel = iota + AuthNoPriv + AuthPriv +) + +func (s SecurityLevel) String() string { + switch s { + case NoAuthNoPriv: + return "NoAuthNoPriv" + case AuthNoPriv: + return "AuthNoPriv" + case AuthPriv: + return "AuthPriv" + default: + return "Unknown" + } +} + +type AuthProtocol string + +const ( + Md5 AuthProtocol = "MD5" + Sha AuthProtocol = "SHA" +) + +type PrivProtocol string + +const ( + Des PrivProtocol = "DES" + Aes PrivProtocol = "AES" +) + +type securityModel int + +const ( + // RFC 3411 Section 6.1 + securitySNMPv1 = iota + 1 + securitySNMPv2c + securityUsm +) + +func (s securityModel) String() string { + switch s { + case securitySNMPv1: + return "SNMPv1" + case securitySNMPv2c: + return "SNMPv2c" + case securityUsm: + return "USM" + default: + return "Unknown" + } +} + +const ( + timeoutDefault = 5 * time.Second + recvBufferSize = 1 << 11 + msgSizeDefault = 1400 + msgSizeMinimum = 484 + tagMask = 0x1f + mega = 1 << 20 +) + +// ASN.1 Class +const ( + classUniversal = iota + classApplication + classContextSpecific + classPrivate +) + +// ASN.1 Tag +const ( + tagInteger = 0x02 + tagOctetString = 0x04 + tagNull = 0x05 + tagObjectIdentifier = 0x06 + tagSequence = 0x10 + tagIpaddress = 0x40 + tagCounter32 = 0x41 + tagGauge32 = 0x42 + tagTimeTicks = 0x43 + tagOpaque = 0x44 + tagCounter64 = 0x46 + tagNoSucheObject = 0x80 + tagNoSucheInstance = 0x81 + tagEndOfMibView = 0x82 +) + +type reportStatusOid string + +const ( + // RFC 3412 Section 5 + snmpUnknownSecurityModels reportStatusOid = "1.3.6.1.6.3.11.2.1.1.0" + snmpInvalidMsgs reportStatusOid = "1.3.6.1.6.3.11.2.1.2.0" + snmpUnknownPDUHandlers reportStatusOid = "1.3.6.1.6.3.11.2.1.3.0" + // RFC 3413 Section 4.1.2 + snmpUnavailableContexts reportStatusOid = "1.3.6.1.6.3.12.1.4.0" + snmpUnknownContexts reportStatusOid = "1.3.6.1.6.3.12.1.5.0" + // RFC 3414 Section 5 + usmStatsUnsupportedSecLevels reportStatusOid = "1.3.6.1.6.3.15.1.1.1.0" + usmStatsNotInTimeWindows reportStatusOid = "1.3.6.1.6.3.15.1.1.2.0" + usmStatsUnknownUserNames reportStatusOid = "1.3.6.1.6.3.15.1.1.3.0" + usmStatsUnknownEngineIDs reportStatusOid = "1.3.6.1.6.3.15.1.1.4.0" + usmStatsWrongDigests reportStatusOid = "1.3.6.1.6.3.15.1.1.5.0" + usmStatsDecryptionErrors reportStatusOid = "1.3.6.1.6.3.15.1.1.6.0" +) + +func (r reportStatusOid) String() string { + switch r { + case snmpUnknownSecurityModels: + return "SnmpUnknownSecurityModels" + case snmpInvalidMsgs: + return "SnmpInvalidMsgs" + case snmpUnknownPDUHandlers: + return "SnmpUnknownPDUHandlers" + case snmpUnavailableContexts: + return "SnmpUnavailableContexts" + case snmpUnknownContexts: + return "SnmpUnknownContexts" + case usmStatsUnsupportedSecLevels: + return "UsmStatsUnsupportedSecLevels" + case usmStatsNotInTimeWindows: + return "UsmStatsNotInTimeWindows" + case usmStatsUnknownUserNames: + return "UsmStatsUnknownUserNames" + case usmStatsUnknownEngineIDs: + return "UsmStatsUnknownEngineIDs" + case usmStatsWrongDigests: + return "UsmStatsWrongDigests" + case usmStatsDecryptionErrors: + return "UsmStatsDecryptionErrors" + default: + return "Unknown" + } +} + +var ( + OidSysUpTime = MustNewOid("1.3.6.1.2.1.1.3.0") + OidSnmpTrap = MustNewOid("1.3.6.1.6.3.1.1.4.1.0") +) diff --git a/vendor/github.com/k-sone/snmpgo/engine.go b/vendor/github.com/k-sone/snmpgo/engine.go new file mode 100644 index 0000000000..250124da16 --- /dev/null +++ b/vendor/github.com/k-sone/snmpgo/engine.go @@ -0,0 +1,94 @@ +package snmpgo + +import ( + "fmt" + "net" + "time" +) + +type snmpEngine struct { + mp messageProcessing + sec security +} + +func (e *snmpEngine) SendPdu(pdu Pdu, conn net.Conn, args *SNMPArguments) (result Pdu, err error) { + size := args.MessageMaxSize + if size < recvBufferSize { + size = recvBufferSize + } + + var sendMsg message + sendMsg, err = e.mp.PrepareOutgoingMessage(e.sec, pdu, args) + if err != nil { + return + } + + var buf []byte + buf, err = sendMsg.Marshal() + if err != nil { + return + } + + if err = conn.SetDeadline(time.Now().Add(args.Timeout)); err != nil { + return + } + _, err = conn.Write(buf) + if !confirmedType(pdu.PduType()) || err != nil { + return + } + + buf = make([]byte, size) + _, err = conn.Read(buf) + if err != nil { + return + } + + var recvMsg message + if recvMsg, _, err = unmarshalMessage(buf); err != nil { + return nil, &MessageError{ + Cause: err, + Message: "Failed to Unmarshal message", + Detail: fmt.Sprintf("message Bytes - [%s]", toHexStr(buf, " ")), + } + } + + result, err = e.mp.PrepareDataElements(e.sec, recvMsg, sendMsg) + if result != nil && len(pdu.VarBinds()) > 0 { + if err = e.checkPdu(result, args); err != nil { + result = nil + } + } + return +} + +func (e *snmpEngine) checkPdu(pdu Pdu, args *SNMPArguments) (err error) { + varBinds := pdu.VarBinds() + if args.Version == V3 && pdu.PduType() == Report && len(varBinds) > 0 { + oid := varBinds[0].Oid.String() + rep := reportStatusOid(oid) + err = &MessageError{ + Message: fmt.Sprintf("Received a report from the agent - %s(%s)", rep, oid), + Detail: fmt.Sprintf("Pdu - %s", pdu), + } + // perhaps the agent has rebooted after the previous communication + if rep == usmStatsNotInTimeWindows { + err = ¬InTimeWindowError{err} + } + } + return +} + +func (e *snmpEngine) Discover(snmp *SNMP) error { + return e.sec.Discover(snmp) +} + +func (e *snmpEngine) String() string { + return fmt.Sprintf(`{"sec": %s}`, e.sec.String()) +} + +func newSNMPEngine(args *SNMPArguments) *snmpEngine { + return &snmpEngine{ + mp: newMessageProcessing(args.Version), + sec: newSecurity(args), + } +} diff --git a/vendor/github.com/k-sone/snmpgo/engine_test.go b/vendor/github.com/k-sone/snmpgo/engine_test.go new file mode 100644 index 0000000000..c2dd56cb4b --- /dev/null +++ b/vendor/github.com/k-sone/snmpgo/engine_test.go @@ -0,0 +1,33 @@ +package snmpgo_test + +import ( + "testing" + + "github.com/k-sone/snmpgo" +) + +func TestCheckPdu(t *testing.T) { + args := &snmpgo.SNMPArguments{ + Version: snmpgo.V3, + UserName: "MyName", + SecurityLevel: snmpgo.AuthPriv, + AuthPassword: "aaaaaaaa", + AuthProtocol: snmpgo.Md5, + PrivPassword: "bbbbbbbb", + PrivProtocol: snmpgo.Des, + } + + eng := snmpgo.NewSNMPEngine(args) + pdu := snmpgo.NewPdu(snmpgo.V3, snmpgo.Report) + err := snmpgo.CheckPdu(eng, pdu, args) + if err != nil { + t.Errorf("checkPdu() - has error %v", err) + } + + oids, _ := snmpgo.NewOids([]string{"1.3.6.1.6.3.11.2.1.1.0"}) + pdu = snmpgo.NewPduWithOids(snmpgo.V3, snmpgo.Report, oids) + err = snmpgo.CheckPdu(eng, pdu, args) + if err == nil { + t.Error("checkPdu() - report oid") + } +} diff --git a/vendor/github.com/k-sone/snmpgo/errors.go b/vendor/github.com/k-sone/snmpgo/errors.go new file mode 100644 index 0000000000..e449829ad8 --- /dev/null +++ b/vendor/github.com/k-sone/snmpgo/errors.go @@ -0,0 +1,37 @@ +package snmpgo + +import ( + "errors" + "fmt" +) + +var UnsupportedOperation error = errors.New("Unsupported operation") + +// An ArgumentError suggests that the arguments are wrong +type ArgumentError struct { + Value interface{} // Argument that has a problem + Message string // Error message +} + +func (e *ArgumentError) Error() string { + return fmt.Sprintf("%s, value `%v`", e.Message, e.Value) +} + +// A MessageError suggests that the received message is wrong or is not obtained +type MessageError struct { + Cause error // Cause of the error + Message string // Error message + Detail string // Detail of the error for debugging +} + +func (e *MessageError) Error() string { + if e.Cause == nil { + return e.Message + } else { + return fmt.Sprintf("%s, cause `%v`", e.Message, e.Cause) + } +} + +type notInTimeWindowError struct { + error +} diff --git a/vendor/github.com/k-sone/snmpgo/examples/getv2.go b/vendor/github.com/k-sone/snmpgo/examples/getv2.go new file mode 100644 index 0000000000..741e199a33 --- /dev/null +++ b/vendor/github.com/k-sone/snmpgo/examples/getv2.go @@ -0,0 +1,56 @@ +package main + +import ( + "fmt" + + "github.com/k-sone/snmpgo" +) + +func main() { + snmp, err := snmpgo.NewSNMP(snmpgo.SNMPArguments{ + Version: snmpgo.V2c, + Address: "127.0.0.1:161", + Retries: 1, + Community: "public", + }) + if err != nil { + // Failed to create snmpgo.SNMP object + fmt.Println(err) + return + } + + oids, err := snmpgo.NewOids([]string{ + "1.3.6.1.2.1.1.1.0", + "1.3.6.1.2.1.1.2.0", + "1.3.6.1.2.1.1.3.0", + }) + if err != nil { + // Failed to parse Oids + fmt.Println(err) + return + } + + if err = snmp.Open(); err != nil { + // Failed to open connection + fmt.Println(err) + return + } + defer snmp.Close() + + pdu, err := snmp.GetRequest(oids) + if err != nil { + // Failed to request + fmt.Println(err) + return + } + if pdu.ErrorStatus() != snmpgo.NoError { + // Received an error from the agent + fmt.Println(pdu.ErrorStatus(), pdu.ErrorIndex()) + } + + // get VarBind list + fmt.Println(pdu.VarBinds()) + + // select a VarBind + fmt.Println(pdu.VarBinds().MatchOid(oids[0])) +} diff --git a/vendor/github.com/k-sone/snmpgo/examples/getv3.go b/vendor/github.com/k-sone/snmpgo/examples/getv3.go new file mode 100644 index 0000000000..0e535f0e3e --- /dev/null +++ b/vendor/github.com/k-sone/snmpgo/examples/getv3.go @@ -0,0 +1,61 @@ +package main + +import ( + "fmt" + + "github.com/k-sone/snmpgo" +) + +func main() { + snmp, err := snmpgo.NewSNMP(snmpgo.SNMPArguments{ + Version: snmpgo.V3, + Address: "127.0.0.1:161", + Retries: 1, + UserName: "MyName", + SecurityLevel: snmpgo.AuthPriv, + AuthPassword: "aaaaaaaa", + AuthProtocol: snmpgo.Sha, + PrivPassword: "bbbbbbbb", + PrivProtocol: snmpgo.Aes, + }) + if err != nil { + // Failed to create snmpgo.SNMP object + fmt.Println(err) + return + } + + oids, err := snmpgo.NewOids([]string{ + "1.3.6.1.2.1.1.1.0", + "1.3.6.1.2.1.1.2.0", + "1.3.6.1.2.1.1.3.0", + }) + if err != nil { + // Failed to parse Oids + fmt.Println(err) + return + } + + if err = snmp.Open(); err != nil { + // Failed to open connection + fmt.Println(err) + return + } + defer snmp.Close() + + pdu, err := snmp.GetRequest(oids) + if err != nil { + // Failed to request + fmt.Println(err) + return + } + if pdu.ErrorStatus() != snmpgo.NoError { + // Received an error from the agent + fmt.Println(pdu.ErrorStatus(), pdu.ErrorIndex()) + } + + // get VarBind list + fmt.Println(pdu.VarBinds()) + + // select a VarBind + fmt.Println(pdu.VarBinds().MatchOid(oids[0])) +} diff --git a/vendor/github.com/k-sone/snmpgo/examples/ifstat.go b/vendor/github.com/k-sone/snmpgo/examples/ifstat.go new file mode 100644 index 0000000000..65bd4a2c9e --- /dev/null +++ b/vendor/github.com/k-sone/snmpgo/examples/ifstat.go @@ -0,0 +1,335 @@ +package main + +import ( + "bytes" + "flag" + "fmt" + "math/big" + "os" + "regexp" + "strconv" + "time" + + "github.com/k-sone/snmpgo" +) + +var sysDescr *snmpgo.Oid = snmpgo.MustNewOid("1.3.6.1.2.1.1.1") +var ifAdminStatus *snmpgo.Oid = snmpgo.MustNewOid("1.3.6.1.2.1.2.2.1.7") +var ifName *snmpgo.Oid = snmpgo.MustNewOid("1.3.6.1.2.1.31.1.1.1.1") +var ifHCInOctets *snmpgo.Oid = snmpgo.MustNewOid("1.3.6.1.2.1.31.1.1.1.6") +var ifHCOutOctets *snmpgo.Oid = snmpgo.MustNewOid("1.3.6.1.2.1.31.1.1.1.10") + +var delay time.Duration = 30 * time.Second +var iterations int = -1 +var maxRepetitions int +var errMessage string +var ifRegexp *regexp.Regexp +var noHeader bool + +func usage(msg string, code int) { + errMessage = msg + flag.Usage() + os.Exit(code) +} + +func parseArgs() (*snmpgo.SNMPArguments, []string) { + flag.Usage = func() { + if errMessage != "" { + fmt.Fprintf(os.Stderr, "%s\n", errMessage) + } + fmt.Fprintf(os.Stderr, "Usage of %s: [OPTIONS] AGENT [DELAY [COUNT]]\n\n", os.Args[0]) + fmt.Fprintf(os.Stderr, "AGENT:\n hostname:port or ip-address:port\n") + fmt.Fprintf(os.Stderr, "DELAY:\n the delay between updates in seconds (default: 10)\n") + fmt.Fprintf(os.Stderr, "COUNT:\n the number of updates (default: infinity)\n") + fmt.Fprintf(os.Stderr, "OPTIONS:\n") + flag.PrintDefaults() + } + + protocol := flag.String("p", "udp", "Protocol (udp|udp6|tcp|tcp6)") + timeout := flag.Uint("t", 5, "Request timeout (number of seconds)") + retries := flag.Uint("r", 1, "Number of retries") + version := flag.String("v", "2c", "SNMP version to use (2c|3)") + community := flag.String("c", "", "Community") + username := flag.String("u", "", "Security name") + seclevel := flag.String("l", "NoAuthNoPriv", "Security level (NoAuthNoPriv|AuthNoPriv|AuthPriv)") + authproto := flag.String("a", "", "Authentication protocol (MD5|SHA)") + authpass := flag.String("A", "", "Authentication protocol pass phrase") + privproto := flag.String("x", "", "Privacy protocol (DES|AES)") + privpass := flag.String("X", "", "Privacy protocol pass phrase") + secengine := flag.String("e", "", "Security engine ID") + contextengine := flag.String("E", "", "Context engine ID") + contextname := flag.String("n", "", "Context name") + ifRegString := flag.String("ifreg", "", "Regular expression that specifies the interface") + flag.IntVar(&maxRepetitions, "Cr", 10, "Max repetitions") + flag.BoolVar(&noHeader, "noheader", false, "Disable header line") + + flag.Parse() + + args := &snmpgo.SNMPArguments{ + Network: *protocol, + Address: flag.Arg(0), + Timeout: time.Duration(*timeout) * time.Second, + Retries: *retries, + Community: *community, + UserName: *username, + AuthPassword: *authpass, + AuthProtocol: snmpgo.AuthProtocol(*authproto), + PrivPassword: *privpass, + PrivProtocol: snmpgo.PrivProtocol(*privproto), + SecurityEngineId: *secengine, + ContextEngineId: *contextengine, + ContextName: *contextname, + } + + switch *version { + case "2c": + args.Version = snmpgo.V2c + case "3": + args.Version = snmpgo.V3 + default: + usage(fmt.Sprintf("Illegal Version, value `%s`", *version), 2) + } + + switch *seclevel { + case "NoAuthNoPriv": + args.SecurityLevel = snmpgo.NoAuthNoPriv + case "AuthNoPriv": + args.SecurityLevel = snmpgo.AuthNoPriv + case "AuthPriv": + args.SecurityLevel = snmpgo.AuthPriv + default: + usage(fmt.Sprintf("Illegal SecurityLevel, value `%s`", *seclevel), 2) + } + + if *ifRegString != "" { + var err error + if ifRegexp, err = regexp.Compile(*ifRegString); err != nil { + usage(fmt.Sprintf("Illegal Regular expression - %s", err), 2) + } + } + + return args, flag.Args() +} + +type ifInfo struct { + descr string + IfList []int + IfName map[int]string + IfIn map[int]*big.Int + IfOut map[int]*big.Int + LastIfIn map[int]*big.Int + LastIfOut map[int]*big.Int + Time time.Time + LastTime time.Time +} + +func newIfInfo() *ifInfo { + return &ifInfo{ + IfList: make([]int, 0), + IfName: make(map[int]string), + IfIn: make(map[int]*big.Int), + IfOut: make(map[int]*big.Int), + LastIfIn: make(map[int]*big.Int), + LastIfOut: make(map[int]*big.Int), + } +} + +func getIfInfo(snmp *snmpgo.SNMP) (*ifInfo, error) { + pdu, err := snmp.GetBulkWalk(snmpgo.Oids{sysDescr, ifAdminStatus, ifName}, 1, maxRepetitions) + if err != nil { + return nil, err + } + + if pdu.ErrorStatus() != snmpgo.NoError { + return nil, fmt.Errorf( + "failed to get system information - %s(%d)", pdu.ErrorStatus(), pdu.ErrorIndex()) + } + + info := newIfInfo() + if descrs := pdu.VarBinds().MatchBaseOids(sysDescr); len(descrs) > 0 { + info.descr = descrs[0].Variable.String() + } + + ifNameDepth := len(ifName.Value) + for _, varBind := range pdu.VarBinds().MatchBaseOids(ifName) { + // get interface number and name + if len(varBind.Oid.Value) != ifNameDepth+1 { + continue + } + ifNum := varBind.Oid.Value[ifNameDepth] + ifName := varBind.Variable.String() + + if ifRegexp != nil && !ifRegexp.MatchString(ifName) { + continue + } + + // get admin status + oid, err := ifAdminStatus.AppendSubIds([]int{ifNum}) + if err != nil { + return nil, err + } + + // check admin status("1" is up) + if adms := pdu.VarBinds().MatchOid(oid); adms == nil || adms.Variable.String() != "1" { + continue + } + + info.IfList = append(info.IfList, ifNum) + info.IfName[ifNum] = ifName + info.IfIn[ifNum] = nil + info.IfOut[ifNum] = nil + info.LastIfIn[ifNum] = nil + info.LastIfOut[ifNum] = nil + } + + if len(info.IfList) == 0 { + return nil, fmt.Errorf("no interface") + } + + return info, nil +} + +func getIfTraffic(snmp *snmpgo.SNMP, info *ifInfo) error { + pdu, err := snmp.GetBulkWalk(snmpgo.Oids{ifHCInOctets, ifHCOutOctets}, 0, maxRepetitions) + if err != nil { + return err + } + + if pdu.ErrorStatus() != snmpgo.NoError { + return fmt.Errorf( + "failed to get network traffic - %s(%d)", pdu.ErrorStatus(), pdu.ErrorIndex()) + } + + for _, ifNum := range info.IfList { + oid, err := ifHCInOctets.AppendSubIds([]int{ifNum}) + if err != nil { + return err + } + if in := pdu.VarBinds().MatchOid(oid); in != nil { + count, err := in.Variable.BigInt() + if err != nil { + return err + } + info.LastIfIn[ifNum], info.IfIn[ifNum] = info.IfIn[ifNum], count + } else { + info.LastIfIn[ifNum], info.IfIn[ifNum] = info.IfIn[ifNum], nil + } + + oid, err = ifHCOutOctets.AppendSubIds([]int{ifNum}) + if err != nil { + return err + } + if out := pdu.VarBinds().MatchOid(oid); out != nil { + count, err := out.Variable.BigInt() + if err != nil { + return err + } + info.LastIfOut[ifNum], info.IfOut[ifNum] = info.IfOut[ifNum], count + } else { + info.LastIfOut[ifNum], info.IfOut[ifNum] = info.IfOut[ifNum], nil + } + } + + info.LastTime, info.Time = info.Time, time.Now() + return nil +} + +func printHeader(info *ifInfo) { + var buf bytes.Buffer + + if info.descr != "" { + buf.WriteString(info.descr) + buf.WriteString("\n") + } + for _, ifNum := range info.IfList { + buf.WriteString(fmt.Sprintf("%-23s ", info.IfName[ifNum])) + } + buf.WriteString("\n") + for _ = range info.IfList { + buf.WriteString("in out ") + } + fmt.Println(buf.String()) +} + +func printTraffic(info *ifInfo) { + // TODO wrap around support + var buf bytes.Buffer + for _, ifNum := range info.IfList { + var in, out big.Int + if info.IfIn[ifNum] != nil && info.LastIfIn[ifNum] != nil { + in.Sub(info.IfIn[ifNum], info.LastIfIn[ifNum]) + buf.WriteString(fmt.Sprintf("%-11d ", in.Uint64())) + } else { + buf.WriteString("- ") + } + if info.IfOut[ifNum] != nil && info.LastIfOut[ifNum] != nil { + out.Sub(info.IfOut[ifNum], info.LastIfOut[ifNum]) + buf.WriteString(fmt.Sprintf("%-11d ", out.Uint64())) + } else { + buf.WriteString("- ") + } + } + fmt.Println(buf.String()) +} + +func main() { + snmpArgs, cmdArgs := parseArgs() + if l := len(cmdArgs); l < 1 { + usage("required AGENT", 2) + } else { + if l > 1 { + if num, err := strconv.Atoi(cmdArgs[1]); err != nil || num < 1 { + usage(fmt.Sprintf("Illegal delay, value `%s`", cmdArgs[1]), 2) + } else { + delay = time.Duration(num) * time.Second + } + } + if l > 2 { + if num, err := strconv.Atoi(cmdArgs[2]); err != nil || num < 1 { + usage(fmt.Sprintf("Illegal iterations, value `%s`", cmdArgs[2]), 2) + } else { + iterations = num + } + } + } + + snmp, err := snmpgo.NewSNMP(*snmpArgs) + if err != nil { + usage(err.Error(), 2) + } + + if err = snmp.Open(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + defer snmp.Close() + + info, err := getIfInfo(snmp) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + if !noHeader { + printHeader(info) + } + + start := time.Now() + for i := 0; ; i++ { + if err := getIfTraffic(snmp, info); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + printTraffic(info) + + if iterations > 0 && i >= iterations { + break + } + + start = start.Add(delay) + if d := start.Sub(info.Time); d > 0 { + time.Sleep(d) + } + } +} diff --git a/vendor/github.com/k-sone/snmpgo/examples/multiget.go b/vendor/github.com/k-sone/snmpgo/examples/multiget.go new file mode 100644 index 0000000000..5116620e89 --- /dev/null +++ b/vendor/github.com/k-sone/snmpgo/examples/multiget.go @@ -0,0 +1,69 @@ +package main + +import ( + "fmt" + "sync" + + "github.com/k-sone/snmpgo" +) + +func get(agent string, oids snmpgo.Oids) { + // create a SNMP Object for each the agent + snmp, err := snmpgo.NewSNMP(snmpgo.SNMPArguments{ + Version: snmpgo.V2c, + Address: agent, + Retries: 1, + Community: "public", + }) + if err != nil { + fmt.Printf("[%s] : construct error - %s\n", agent, err) + return + } + + if err = snmp.Open(); err != nil { + fmt.Printf("[%s] : open error - %s\n", agent, err) + return + } + defer snmp.Close() + + pdu, err := snmp.GetRequest(oids) + if err != nil { + fmt.Printf("[%s] : get error - %s\n", agent, err) + return + } + + if pdu.ErrorStatus() != snmpgo.NoError { + fmt.Printf("[%s] : error status - %s at %d\n", + agent, pdu.ErrorStatus(), pdu.ErrorIndex()) + } + + fmt.Printf("[%s] : %s\n", agent, pdu.VarBinds()) +} + +func main() { + oids, err := snmpgo.NewOids([]string{ + "1.3.6.1.2.1.1.1.0", + "1.3.6.1.2.1.1.2.0", + "1.3.6.1.2.1.1.3.0", + }) + if err != nil { + fmt.Println(err) + return + } + + agents := []string{ + "192.168.1.1:161", + "192.168.1.2:161", + "192.168.1.3:161", + } + + var wg sync.WaitGroup + for _, agent := range agents { + wg.Add(1) + go func(a string) { + defer wg.Done() + get(a, oids) + }(agent) + } + wg.Wait() +} diff --git a/vendor/github.com/k-sone/snmpgo/examples/snmpgobulkwalk.go b/vendor/github.com/k-sone/snmpgo/examples/snmpgobulkwalk.go new file mode 100644 index 0000000000..83d2170c3b --- /dev/null +++ b/vendor/github.com/k-sone/snmpgo/examples/snmpgobulkwalk.go @@ -0,0 +1,127 @@ +package main + +import ( + "flag" + "fmt" + "os" + "time" + + "github.com/k-sone/snmpgo" +) + +var nonRepeaters int +var maxRepetitions int +var errMessage string + +func usage(msg string, code int) { + errMessage = msg + flag.Usage() + os.Exit(code) +} + +func parseArgs() (*snmpgo.SNMPArguments, []string) { + flag.Usage = func() { + if errMessage != "" { + fmt.Fprintf(os.Stderr, "%s\n", errMessage) + } + fmt.Fprintf(os.Stderr, "Usage of %s: [OPTIONS] AGENT OID [OID]..\n\n", os.Args[0]) + fmt.Fprintf(os.Stderr, "AGENT:\n hostname:port or ip-address:port\n") + fmt.Fprintf(os.Stderr, "OPTIONS:\n") + flag.PrintDefaults() + } + + protocol := flag.String("p", "udp", "Protocol (udp|udp6|tcp|tcp6)") + timeout := flag.Uint("t", 5, "Request timeout (number of seconds)") + retries := flag.Uint("r", 1, "Number of retries") + version := flag.String("v", "2c", "SNMP version to use (2c|3)") + community := flag.String("c", "", "Community") + username := flag.String("u", "", "Security name") + seclevel := flag.String("l", "NoAuthNoPriv", "Security level (NoAuthNoPriv|AuthNoPriv|AuthPriv)") + authproto := flag.String("a", "", "Authentication protocol (MD5|SHA)") + authpass := flag.String("A", "", "Authentication protocol pass phrase") + privproto := flag.String("x", "", "Privacy protocol (DES|AES)") + privpass := flag.String("X", "", "Privacy protocol pass phrase") + secengine := flag.String("e", "", "Security engine ID") + contextengine := flag.String("E", "", "Context engine ID") + contextname := flag.String("n", "", "Context name") + flag.IntVar(&nonRepeaters, "Cn", 0, "Non repeaters") + flag.IntVar(&maxRepetitions, "Cr", 10, "Max repetitions") + + flag.Parse() + + args := &snmpgo.SNMPArguments{ + Network: *protocol, + Address: flag.Arg(0), + Timeout: time.Duration(*timeout) * time.Second, + Retries: *retries, + Community: *community, + UserName: *username, + AuthPassword: *authpass, + AuthProtocol: snmpgo.AuthProtocol(*authproto), + PrivPassword: *privpass, + PrivProtocol: snmpgo.PrivProtocol(*privproto), + SecurityEngineId: *secengine, + ContextEngineId: *contextengine, + ContextName: *contextname, + } + + switch *version { + case "2c": + args.Version = snmpgo.V2c + case "3": + args.Version = snmpgo.V3 + default: + usage(fmt.Sprintf("Illegal Version, value `%s`", *version), 2) + } + + switch *seclevel { + case "NoAuthNoPriv": + args.SecurityLevel = snmpgo.NoAuthNoPriv + case "AuthNoPriv": + args.SecurityLevel = snmpgo.AuthNoPriv + case "AuthPriv": + args.SecurityLevel = snmpgo.AuthPriv + default: + usage(fmt.Sprintf("Illegal SecurityLevel, value `%s`", *seclevel), 2) + } + + return args, flag.Args() +} + +func main() { + snmpArgs, cmdArgs := parseArgs() + if len(cmdArgs) < 2 { + usage("required AGENT and OID", 2) + } + + oids, err := snmpgo.NewOids(cmdArgs[1:]) + if err != nil { + usage(err.Error(), 2) + } + + snmp, err := snmpgo.NewSNMP(*snmpArgs) + if err != nil { + usage(err.Error(), 2) + } + + if err = snmp.Open(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + defer snmp.Close() + + pdu, err := snmp.GetBulkWalk(oids, nonRepeaters, maxRepetitions) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + if pdu.ErrorStatus() != snmpgo.NoError { + fmt.Fprintln(os.Stderr, pdu.ErrorStatus(), pdu.ErrorIndex()) + os.Exit(1) + } + + for _, val := range pdu.VarBinds() { + fmt.Printf("%s = %s: %s\n", val.Oid, val.Variable.Type(), val.Variable) + } +} diff --git a/vendor/github.com/k-sone/snmpgo/examples/snmpgoget.go b/vendor/github.com/k-sone/snmpgo/examples/snmpgoget.go new file mode 100644 index 0000000000..4c1460056e --- /dev/null +++ b/vendor/github.com/k-sone/snmpgo/examples/snmpgoget.go @@ -0,0 +1,125 @@ +package main + +import ( + "flag" + "fmt" + "os" + "time" + + "github.com/k-sone/snmpgo" +) + +var errMessage string + +func usage(msg string, code int) { + errMessage = msg + flag.Usage() + os.Exit(code) +} + +func parseArgs() (*snmpgo.SNMPArguments, []string) { + flag.Usage = func() { + if errMessage != "" { + fmt.Fprintf(os.Stderr, "%s\n", errMessage) + } + fmt.Fprintf(os.Stderr, "Usage of %s: [OPTIONS] AGENT OID [OID]..\n\n", os.Args[0]) + fmt.Fprintf(os.Stderr, "AGENT:\n hostname:port or ip-address:port\n") + fmt.Fprintf(os.Stderr, "OPTIONS:\n") + flag.PrintDefaults() + } + + protocol := flag.String("p", "udp", "Protocol (udp|udp6|tcp|tcp6)") + timeout := flag.Uint("t", 5, "Request timeout (number of seconds)") + retries := flag.Uint("r", 1, "Number of retries") + version := flag.String("v", "1", "SNMP version to use (1|2c|3)") + community := flag.String("c", "", "Community") + username := flag.String("u", "", "Security name") + seclevel := flag.String("l", "NoAuthNoPriv", "Security level (NoAuthNoPriv|AuthNoPriv|AuthPriv)") + authproto := flag.String("a", "", "Authentication protocol (MD5|SHA)") + authpass := flag.String("A", "", "Authentication protocol pass phrase") + privproto := flag.String("x", "", "Privacy protocol (DES|AES)") + privpass := flag.String("X", "", "Privacy protocol pass phrase") + secengine := flag.String("e", "", "Security engine ID") + contextengine := flag.String("E", "", "Context engine ID") + contextname := flag.String("n", "", "Context name") + + flag.Parse() + + args := &snmpgo.SNMPArguments{ + Network: *protocol, + Address: flag.Arg(0), + Timeout: time.Duration(*timeout) * time.Second, + Retries: *retries, + Community: *community, + UserName: *username, + AuthPassword: *authpass, + AuthProtocol: snmpgo.AuthProtocol(*authproto), + PrivPassword: *privpass, + PrivProtocol: snmpgo.PrivProtocol(*privproto), + SecurityEngineId: *secengine, + ContextEngineId: *contextengine, + ContextName: *contextname, + } + + switch *version { + case "1": + args.Version = snmpgo.V1 + case "2c": + args.Version = snmpgo.V2c + case "3": + args.Version = snmpgo.V3 + default: + usage(fmt.Sprintf("Illegal Version, value `%s`", *version), 2) + } + + switch *seclevel { + case "NoAuthNoPriv": + args.SecurityLevel = snmpgo.NoAuthNoPriv + case "AuthNoPriv": + args.SecurityLevel = snmpgo.AuthNoPriv + case "AuthPriv": + args.SecurityLevel = snmpgo.AuthPriv + default: + usage(fmt.Sprintf("Illegal SecurityLevel, value `%s`", *seclevel), 2) + } + + return args, flag.Args() +} + +func main() { + snmpArgs, cmdArgs := parseArgs() + if len(cmdArgs) < 2 { + usage("required AGENT and OID", 2) + } + + oids, err := snmpgo.NewOids(cmdArgs[1:]) + if err != nil { + usage(err.Error(), 2) + } + + snmp, err := snmpgo.NewSNMP(*snmpArgs) + if err != nil { + usage(err.Error(), 2) + } + + if err = snmp.Open(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + defer snmp.Close() + + pdu, err := snmp.GetRequest(oids) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + if pdu.ErrorStatus() != snmpgo.NoError { + fmt.Fprintln(os.Stderr, pdu.ErrorStatus(), pdu.ErrorIndex()) + os.Exit(1) + } + + for _, val := range pdu.VarBinds() { + fmt.Printf("%s = %s: %s\n", val.Oid, val.Variable.Type(), val.Variable) + } +} diff --git a/vendor/github.com/k-sone/snmpgo/examples/snmpgotrap.go b/vendor/github.com/k-sone/snmpgo/examples/snmpgotrap.go new file mode 100644 index 0000000000..fc17bf63cc --- /dev/null +++ b/vendor/github.com/k-sone/snmpgo/examples/snmpgotrap.go @@ -0,0 +1,263 @@ +package main + +import ( + "encoding/hex" + "flag" + "fmt" + "net" + "os" + "regexp" + "strconv" + "strings" + "time" + + "github.com/k-sone/snmpgo" +) + +var hexPrefix *regexp.Regexp = regexp.MustCompile(`^0[xX]`) +var inform bool +var engineBoots int +var engineTime int +var errMessage string + +func usage(msg string, code int) { + errMessage = msg + flag.Usage() + os.Exit(code) +} + +func parseArgs() (*snmpgo.SNMPArguments, []string) { + flag.Usage = func() { + if errMessage != "" { + fmt.Fprintf(os.Stderr, "%s\n", errMessage) + } + fmt.Fprintf(os.Stderr, + "Usage of %s: [OPTIONS] AGENT UPTIME TRAP-OID [OID TYPE VALUE]..\n\n", os.Args[0]) + fmt.Fprintf(os.Stderr, "AGENT:\n hostname:port or ip-address:port\n") + fmt.Fprintf(os.Stderr, "UPTIME:\n system uptime\n") + fmt.Fprintf(os.Stderr, "TYPE:\n") + fmt.Fprintf(os.Stderr, " i - INTEGER u - UNSIGNED c - COUNTER32 C - COUNTER64\n") + fmt.Fprintf(os.Stderr, " t - TIMETICKS a - IPADDRESS o - OID n - NULL\n") + fmt.Fprintf(os.Stderr, " s - STRING x - HEX STRING d - DECIMAL STRING\n") + fmt.Fprintf(os.Stderr, "OPTIONS:\n") + flag.PrintDefaults() + } + + protocol := flag.String("p", "udp", "Protocol (udp|udp6|tcp|tcp6)") + timeout := flag.Uint("t", 5, "Request timeout (number of seconds)") + retries := flag.Uint("r", 1, "Number of retries") + version := flag.String("v", "2c", "SNMP version to use (2c|3)") + community := flag.String("c", "", "Community") + username := flag.String("u", "", "Security name") + seclevel := flag.String("l", "NoAuthNoPriv", "Security level (NoAuthNoPriv|AuthNoPriv|AuthPriv)") + authproto := flag.String("a", "", "Authentication protocol (MD5|SHA)") + authpass := flag.String("A", "", "Authentication protocol pass phrase") + privproto := flag.String("x", "", "Privacy protocol (DES|AES)") + privpass := flag.String("X", "", "Privacy protocol pass phrase") + secengine := flag.String("e", "", "Security engine ID") + contextengine := flag.String("E", "", "Context engine ID") + contextname := flag.String("n", "", "Context name") + bootsTime := flag.String("Z", "", "EngineBoots and EngineTime (boots,time)") + flag.BoolVar(&inform, "Ci", false, "Send an Inform") + + flag.Parse() + + args := &snmpgo.SNMPArguments{ + Network: *protocol, + Address: flag.Arg(0), + Timeout: time.Duration(*timeout) * time.Second, + Retries: *retries, + Community: *community, + UserName: *username, + AuthPassword: *authpass, + AuthProtocol: snmpgo.AuthProtocol(*authproto), + PrivPassword: *privpass, + PrivProtocol: snmpgo.PrivProtocol(*privproto), + SecurityEngineId: *secengine, + ContextEngineId: *contextengine, + ContextName: *contextname, + } + + switch *version { + case "2c": + args.Version = snmpgo.V2c + case "3": + args.Version = snmpgo.V3 + default: + usage(fmt.Sprintf("Illegal Version, value `%s`", *version), 2) + } + + switch *seclevel { + case "NoAuthNoPriv": + args.SecurityLevel = snmpgo.NoAuthNoPriv + case "AuthNoPriv": + args.SecurityLevel = snmpgo.AuthNoPriv + case "AuthPriv": + args.SecurityLevel = snmpgo.AuthPriv + default: + usage(fmt.Sprintf("Illegal SecurityLevel, value `%s`", *seclevel), 2) + } + + if *bootsTime != "" { + var success bool + if engineBoots, engineTime, success = parseBootsTime(*bootsTime); !success { + usage(fmt.Sprintf("Invalid Boots,Time format, value `%s`", *bootsTime), 2) + } + } + + return args, flag.Args() +} + +func parseBootsTime(bt string) (int, int, bool) { + s := strings.Split(bt, ",") + if len(s) == 2 { + b, e1 := strconv.Atoi(s[0]) + t, e2 := strconv.Atoi(s[1]) + if e1 == nil && e2 == nil { + return b, t, true + } + } + return 0, 0, false +} + +func getUptime(s string) uint32 { + if uptime, err := strconv.ParseUint(s, 10, 32); err == nil { + return uint32(uptime) + } + + // The syscall.Sysinfo only works on Linux + // var info syscall.Sysinfo_t + // if err := syscall.Sysinfo(&info); err == nil { + // return uint32(info.Uptime * 100) + // } + + return 0 +} + +func buildVariable(kind string, value string) (val snmpgo.Variable, err error) { + switch kind { + case "i": + var num int64 + if num, err = strconv.ParseInt(value, 10, 32); err == nil { + val = snmpgo.NewInteger(int32(num)) + } + case "u": + var num uint64 + if num, err = strconv.ParseUint(value, 10, 32); err == nil { + val = snmpgo.NewGauge32(uint32(num)) + } + case "c": + var num uint64 + if num, err = strconv.ParseUint(value, 10, 32); err == nil { + val = snmpgo.NewCounter32(uint32(num)) + } + case "C": + var num uint64 + if num, err = strconv.ParseUint(value, 10, 64); err == nil { + val = snmpgo.NewCounter64(num) + } + case "t": + var num uint64 + if num, err = strconv.ParseUint(value, 10, 32); err == nil { + val = snmpgo.NewTimeTicks(uint32(num)) + } + case "a": + if ip := net.ParseIP(value); ip != nil && len(ip) == 4 { + val = snmpgo.NewIpaddress(ip[0], ip[1], ip[2], ip[3]) + } else { + return nil, fmt.Errorf("%s: no valid IP Address", value) + } + case "o": + val, err = snmpgo.NewOid(value) + case "n": + val = snmpgo.NewNull() + case "s": + val = snmpgo.NewOctetString([]byte(value)) + case "x": + var b []byte + hx := hexPrefix.ReplaceAllString(value, "") + if b, err = hex.DecodeString(hx); err == nil { + val = snmpgo.NewOctetString(b) + } else { + return nil, fmt.Errorf("%s: no valid Hex String", value) + } + case "d": + s := strings.Split(value, ".") + b := make([]byte, len(s)) + for i, piece := range s { + var num int + if num, err = strconv.Atoi(piece); err != nil || num > 0xff { + return nil, fmt.Errorf("%s: no valid Decimal String", value) + } + b[i] = byte(num) + } + val = snmpgo.NewOctetString(b) + default: + return nil, fmt.Errorf("%s: unknown TYPE", kind) + } + + return +} + +func buildVarBinds(cmdArgs []string) (snmpgo.VarBinds, error) { + var varBinds snmpgo.VarBinds + + uptime := snmpgo.NewTimeTicks(getUptime(cmdArgs[1])) + varBinds = append(varBinds, snmpgo.NewVarBind(snmpgo.OidSysUpTime, uptime)) + + oid, err := snmpgo.NewOid(cmdArgs[2]) + if err != nil { + return nil, err + } + varBinds = append(varBinds, snmpgo.NewVarBind(snmpgo.OidSnmpTrap, oid)) + + for i := 3; i < len(cmdArgs); i += 3 { + oid, err := snmpgo.NewOid(cmdArgs[i]) + if err != nil { + return nil, err + } + + val, err := buildVariable(cmdArgs[i+1], cmdArgs[i+2]) + if err != nil { + return nil, err + } + + varBinds = append(varBinds, snmpgo.NewVarBind(oid, val)) + } + + return varBinds, nil +} + +func main() { + snmpArgs, cmdArgs := parseArgs() + if l := len(cmdArgs); l < 3 { + usage("required AGENT and UPTIME and TRAP-OID", 2) + } else if l%3 != 0 { + usage(fmt.Sprintf("%s: missing TYPE/VALUE for variable", cmdArgs[l/3*3]), 2) + } + + varBinds, err := buildVarBinds(cmdArgs) + if err != nil { + usage(err.Error(), 2) + } + + snmp, err := snmpgo.NewSNMP(*snmpArgs) + if err != nil { + usage(err.Error(), 2) + } + if err = snmp.Open(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + defer snmp.Close() + + if inform { + err = snmp.InformRequest(varBinds) + } else { + err = snmp.V2TrapWithBootsTime(varBinds, engineBoots, engineTime) + } + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} diff --git a/vendor/github.com/k-sone/snmpgo/examples/trapserver.go b/vendor/github.com/k-sone/snmpgo/examples/trapserver.go new file mode 100644 index 0000000000..940d255640 --- /dev/null +++ b/vendor/github.com/k-sone/snmpgo/examples/trapserver.go @@ -0,0 +1,62 @@ +package main + +import ( + "log" + "os" + + "github.com/k-sone/snmpgo" +) + +type TrapListener struct { + logger *log.Logger +} + +func (l *TrapListener) OnTRAP(trap *snmpgo.TrapRequest) { + // simple logging + if trap.Error == nil { + l.logger.Printf("%v %v", trap.Source, trap.Pdu) + } else { + l.logger.Printf("%v %v", trap.Source, trap.Error) + } +} + +func NewTrapListener() *TrapListener { + return &TrapListener{logger: log.New(os.Stdout, "", log.LstdFlags)} +} + +func main() { + server, err := snmpgo.NewTrapServer(snmpgo.ServerArguments{ + LocalAddr: "127.0.0.1:162", + }) + if err != nil { + log.Fatal(err) + } + + // V2c + err = server.AddSecurity(&snmpgo.SecurityEntry{ + Version: snmpgo.V2c, + Community: "public", + }) + if err != nil { + log.Fatal(err) + } + // V3 + err = server.AddSecurity(&snmpgo.SecurityEntry{ + Version: snmpgo.V3, + UserName: "MyName", + SecurityLevel: snmpgo.AuthPriv, + AuthPassword: "aaaaaaaa", + AuthProtocol: snmpgo.Sha, + PrivPassword: "bbbbbbbb", + PrivProtocol: snmpgo.Aes, + SecurityEngineId: "8000000004736e6d70676f", + }) + if err != nil { + log.Fatal(err) + } + + err = server.Serve(NewTrapListener()) + if err != nil { + log.Fatal(err) + } +} diff --git a/vendor/github.com/k-sone/snmpgo/examples/trapv2.go b/vendor/github.com/k-sone/snmpgo/examples/trapv2.go new file mode 100644 index 0000000000..cc2c64a75f --- /dev/null +++ b/vendor/github.com/k-sone/snmpgo/examples/trapv2.go @@ -0,0 +1,47 @@ +package main + +import ( + "fmt" + + "github.com/k-sone/snmpgo" +) + +func main() { + snmp, err := snmpgo.NewSNMP(snmpgo.SNMPArguments{ + Version: snmpgo.V2c, + Address: "127.0.0.1:162", + Retries: 1, + Community: "public", + }) + if err != nil { + // Failed to create snmpgo.SNMP object + fmt.Println(err) + return + } + + // Build VarBind list + var varBinds snmpgo.VarBinds + varBinds = append(varBinds, snmpgo.NewVarBind(snmpgo.OidSysUpTime, snmpgo.NewTimeTicks(1000))) + + oid, _ := snmpgo.NewOid("1.3.6.1.6.3.1.1.5.3") + varBinds = append(varBinds, snmpgo.NewVarBind(snmpgo.OidSnmpTrap, oid)) + + oid, _ = snmpgo.NewOid("1.3.6.1.2.1.2.2.1.1.2") + varBinds = append(varBinds, snmpgo.NewVarBind(oid, snmpgo.NewInteger(2))) + + oid, _ = snmpgo.NewOid("1.3.6.1.2.1.31.1.1.1.1.2") + varBinds = append(varBinds, snmpgo.NewVarBind(oid, snmpgo.NewOctetString([]byte("eth0")))) + + if err = snmp.Open(); err != nil { + // Failed to open connection + fmt.Println(err) + return + } + defer snmp.Close() + + if err = snmp.V2Trap(varBinds); err != nil { + // Failed to request + fmt.Println(err) + return + } +} diff --git a/vendor/github.com/k-sone/snmpgo/examples/trapv3.go b/vendor/github.com/k-sone/snmpgo/examples/trapv3.go new file mode 100644 index 0000000000..5b7e84026b --- /dev/null +++ b/vendor/github.com/k-sone/snmpgo/examples/trapv3.go @@ -0,0 +1,56 @@ +package main + +import ( + "fmt" + + "github.com/k-sone/snmpgo" +) + +func main() { + // `snmpgo.SNMP.Open` function execute the EngineID Discovery when you use V3. + // Specify the Agent's EngineID to `snmpgo.SNMPArguments.SecurityEngineId` parameter, + // if you want to suppress this behavior. + snmp, err := snmpgo.NewSNMP(snmpgo.SNMPArguments{ + Version: snmpgo.V3, + Address: "127.0.0.1:162", + Retries: 1, + UserName: "MyName", + SecurityLevel: snmpgo.AuthPriv, + AuthPassword: "aaaaaaaa", + AuthProtocol: snmpgo.Sha, + PrivPassword: "bbbbbbbb", + PrivProtocol: snmpgo.Aes, + SecurityEngineId: "8000000004736e6d70676f", + }) + if err != nil { + // Failed to create snmpgo.SNMP object + fmt.Println(err) + return + } + + // Build VarBind list + var varBinds snmpgo.VarBinds + varBinds = append(varBinds, snmpgo.NewVarBind(snmpgo.OidSysUpTime, snmpgo.NewTimeTicks(1000))) + + oid, _ := snmpgo.NewOid("1.3.6.1.6.3.1.1.5.3") + varBinds = append(varBinds, snmpgo.NewVarBind(snmpgo.OidSnmpTrap, oid)) + + oid, _ = snmpgo.NewOid("1.3.6.1.2.1.2.2.1.1.2") + varBinds = append(varBinds, snmpgo.NewVarBind(oid, snmpgo.NewInteger(2))) + + oid, _ = snmpgo.NewOid("1.3.6.1.2.1.31.1.1.1.1.2") + varBinds = append(varBinds, snmpgo.NewVarBind(oid, snmpgo.NewOctetString([]byte("eth0")))) + + if err = snmp.Open(); err != nil { + // Failed to open connection + fmt.Println(err) + return + } + defer snmp.Close() + + if err = snmp.V2Trap(varBinds); err != nil { + // Failed to request + fmt.Println(err) + return + } +} diff --git a/vendor/github.com/k-sone/snmpgo/export_test.go b/vendor/github.com/k-sone/snmpgo/export_test.go new file mode 100644 index 0000000000..fe4d84ef76 --- /dev/null +++ b/vendor/github.com/k-sone/snmpgo/export_test.go @@ -0,0 +1,56 @@ +package snmpgo + +import ( + "time" +) + +var StripHexPrefix = stripHexPrefix +var ToHexStr = toHexStr +var Retry = retry +var GenRequestId = genRequestId +var GenSalt32 = genSalt32 +var GenSalt64 = genSalt64 +var GenMessageId = genMessageId +var NewNotInTimeWindowError = func() error { return ¬InTimeWindowError{&MessageError{}} } + +// For snmpgo testing +var NewSNMPEngine = newSNMPEngine + +func ArgsValidate(args *SNMPArguments) error { return args.validate() } +func CheckPdu(engine *snmpEngine, pdu Pdu, args *SNMPArguments) error { + return engine.checkPdu(pdu, args) +} + +// For message testing +var NewMessage = newMessage +var UnmarshalMessage = unmarshalMessage +var NewMessageWithPdu = newMessageWithPdu +var NewMessageProcessing = newMessageProcessing + +func ToMessageV1(msg message) *messageV1 { return msg.(*messageV1) } +func ToMessageV3(msg message) *messageV3 { return msg.(*messageV3) } +func ToUsm(sec security) *usm { return sec.(*usm) } + +// For security testing +var NewSecurity = newSecurity +var PasswordToKey = passwordToKey +var EncryptDES = encryptDES +var EncryptAES = encryptAES +var DecryptDES = decryptDES +var DecryptAES = decryptAES +var NewSecurityMap = newSecurityMap + +func NewCommunity() *community { return &community{} } +func NewUsm() *usm { return &usm{} } + +// For server +func ListeningUDPAddress(s *TrapServer) string { + for i := 0; i < 12; i++ { + if conn := s.transport.(*packetTransport).conn; conn != nil { + return conn.LocalAddr().String() + } + // XXX Wait until a connection is available, but this code is a kludge + time.Sleep(time.Millisecond * time.Duration(1<= AuthNoPriv { + m.SetAuthentication(true) + if args.SecurityLevel >= AuthPriv { + m.SetPrivacy(true) + } + } + // set boots & time from arguments + // (there is possibility to be overwritten with the usm) + m.AuthEngineBoots = int64(args.authEngineBoots) + m.AuthEngineTime = int64(args.authEngineTime) + + if err := sec.GenerateRequestMessage(msg); err != nil { + return nil, err + } + return msg, nil +} + +func (mp *messageProcessingV3) PrepareResponseMessage( + sec security, pdu Pdu, recvMsg message) (message, error) { + + // TODO support for response message of v3 + return nil, nil +} + +func (mp *messageProcessingV3) PrepareDataElements( + sec security, recvMsg, sendMsg message) (Pdu, error) { + + sm, _ := sendMsg.(*messageV3) + rm := recvMsg.(*messageV3) + if sm != nil { + if sm.Version() != rm.Version() { + return nil, &MessageError{ + Message: fmt.Sprintf( + "SNMPVersion mismatch - expected [%v], actual [%v]", + sm.Version(), rm.Version()), + Detail: fmt.Sprintf("%s vs %s", sm, rm), + } + } + if sm.MessageId != rm.MessageId { + return nil, &MessageError{ + Message: fmt.Sprintf( + "MessageId mismatch - expected [%d], actual [%d]", + sm.MessageId, rm.MessageId), + Detail: fmt.Sprintf("%s vs %s", sm, rm), + } + } + } + if rm.SecurityModel != securityUsm { + return nil, &MessageError{ + Message: fmt.Sprintf("Unknown SecurityModel, value [%d]", rm.SecurityModel), + } + } + + if err := sec.ProcessIncomingMessage(recvMsg); err != nil { + return nil, err + } + + pdu, _ := recvMsg.Pdu().(*ScopedPdu) + if sm != nil { + switch t := pdu.PduType(); t { + case GetResponse: + if sm.Pdu().RequestId() != pdu.RequestId() { + return nil, &MessageError{ + Message: fmt.Sprintf("RequestId mismatch - expected [%d], actual [%d]", + sm.Pdu().RequestId(), pdu.RequestId()), + Detail: fmt.Sprintf("%s vs %s", sm, rm), + } + } + + sPdu := sm.Pdu().(*ScopedPdu) + if !bytes.Equal(sPdu.ContextEngineId, pdu.ContextEngineId) { + return nil, &MessageError{ + Message: fmt.Sprintf("ContextEngineId mismatch - expected [%s], actual [%s]", + toHexStr(sPdu.ContextEngineId, ""), toHexStr(pdu.ContextEngineId, "")), + } + } + + if !bytes.Equal(sPdu.ContextName, pdu.ContextName) { + return nil, &MessageError{ + Message: fmt.Sprintf("ContextName mismatch - expected [%s], actual [%s]", + toHexStr(sPdu.ContextName, ""), toHexStr(pdu.ContextName, "")), + } + } + + if sm.Authentication() && !rm.Authentication() { + return nil, &MessageError{ + Message: "Response message is not authenticated", + } + } + case Report: + if sm.Reportable() { + break + } + fallthrough + default: + return nil, &MessageError{ + Message: fmt.Sprintf("Illegal PduType - expected [%s], actual [%v]", + GetResponse, t), + } + } + } else { + if t := pdu.PduType(); !confirmedType(t) && t != SNMPTrapV2 { + return nil, &MessageError{ + Message: fmt.Sprintf("Illegal PduType - received [%v]", t), + } + } + } + + return pdu, nil +} + +func newMessageProcessing(ver SNMPVersion) (mp messageProcessing) { + switch ver { + case V1, V2c: + mp = &messageProcessingV1{version: ver} + case V3: + mp = &messageProcessingV3{version: ver} + } + return +} diff --git a/vendor/github.com/k-sone/snmpgo/mprocessing_test.go b/vendor/github.com/k-sone/snmpgo/mprocessing_test.go new file mode 100644 index 0000000000..cfd1939da0 --- /dev/null +++ b/vendor/github.com/k-sone/snmpgo/mprocessing_test.go @@ -0,0 +1,249 @@ +package snmpgo_test + +import ( + "bytes" + "encoding/hex" + "testing" + + "github.com/k-sone/snmpgo" +) + +func TestMessageProcessingV1Request(t *testing.T) { + args := &snmpgo.SNMPArguments{ + Version: snmpgo.V2c, + Community: "public", + } + mp := snmpgo.NewMessageProcessing(args.Version) + sec := snmpgo.NewSecurity(args) + pdu := snmpgo.NewPdu(snmpgo.V2c, snmpgo.GetRequest) + + msg, err := mp.PrepareOutgoingMessage(sec, pdu, args) + if err != nil { + t.Errorf("PrepareOutgoingMessage() - has error %v", err) + } + if len(msg.PduBytes()) == 0 { + t.Error("PrepareOutgoingMessage() - pdu bytes") + } + if pdu.RequestId() == 0 { + t.Error("PrepareOutgoingMessage() - request id") + } + requestId := pdu.RequestId() + + _, err = mp.PrepareDataElements(sec, msg, msg) + if err == nil { + t.Error("PrepareDataElements() - pdu type check") + } + + pdu = snmpgo.NewPdu(snmpgo.V2c, snmpgo.GetResponse) + rmsg := snmpgo.ToMessageV1(snmpgo.NewMessageWithPdu(snmpgo.V1, pdu)) + _, err = mp.PrepareDataElements(sec, rmsg, msg) + if err == nil { + t.Error("PrepareDataElements() - version check") + } + + pdu.SetRequestId(requestId) + pduBytes, _ := pdu.Marshal() + rmsg = snmpgo.ToMessageV1(snmpgo.NewMessageWithPdu(snmpgo.V2c, pdu)) + rmsg.Community = []byte("public") + rmsg.SetPduBytes(pduBytes) + _, err = mp.PrepareDataElements(sec, rmsg, msg) + if err != nil { + t.Errorf("PrepareDataElements() - has error %v", err) + } +} + +func TestMessageProcessingV1Receive(t *testing.T) { + args := &snmpgo.SNMPArguments{ + Version: snmpgo.V2c, + Community: "public", + } + mp := snmpgo.NewMessageProcessing(args.Version) + sec := snmpgo.NewSecurity(args) + + pdu := snmpgo.NewPdu(snmpgo.V2c, snmpgo.GetResponse) + pduBytes, _ := pdu.Marshal() + rmsg := snmpgo.ToMessageV1(snmpgo.NewMessageWithPdu(snmpgo.V2c, pdu)) + rmsg.Community = []byte("public") + rmsg.SetPduBytes(pduBytes) + _, err := mp.PrepareDataElements(sec, rmsg, nil) + if err == nil { + t.Error("PrepareDataElements() - pdu type check") + } + + pdu = snmpgo.NewPdu(snmpgo.V2c, snmpgo.SNMPTrapV2) + pduBytes, _ = pdu.Marshal() + rmsg = snmpgo.ToMessageV1(snmpgo.NewMessageWithPdu(snmpgo.V2c, pdu)) + rmsg.Community = []byte("public") + rmsg.SetPduBytes(pduBytes) + _, err = mp.PrepareDataElements(sec, rmsg, nil) + if err != nil { + t.Errorf("PrepareDataElements() - has error %v", err) + } + + pdu = snmpgo.NewPdu(snmpgo.V2c, snmpgo.GetResponse) + pdu.SetRequestId(-1) + smsg, err := mp.PrepareResponseMessage(sec, pdu, rmsg) + if err != nil { + t.Errorf("PrepareResponseMessage() - has error %v", err) + } + if len(smsg.PduBytes()) == 0 { + t.Error("PrepareResponseMessage() - pdu bytes") + } + if pdu.RequestId() != rmsg.Pdu().RequestId() { + t.Error("PrepareResponseMessage() - request id") + } +} + +func TestMessageProcessingV3Request(t *testing.T) { + expEngId := []byte{0x80, 0x00, 0x00, 0x00, 0x01} + expCtxId := []byte{0x80, 0x00, 0x00, 0x00, 0x05} + expCtxName := "myName" + args := &snmpgo.SNMPArguments{ + Version: snmpgo.V3, + UserName: "myName", + SecurityLevel: snmpgo.AuthPriv, + AuthPassword: "aaaaaaaa", + AuthProtocol: snmpgo.Md5, + PrivPassword: "bbbbbbbb", + PrivProtocol: snmpgo.Des, + ContextEngineId: hex.EncodeToString(expCtxId), + ContextName: expCtxName, + } + mp := snmpgo.NewMessageProcessing(args.Version) + sec := snmpgo.NewSecurity(args) + usm := snmpgo.ToUsm(sec) + usm.AuthEngineId = expEngId + usm.AuthKey = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + usm.PrivKey = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + pdu := snmpgo.NewPdu(snmpgo.V3, snmpgo.GetRequest) + + msg, err := mp.PrepareOutgoingMessage(sec, pdu, args) + if err != nil { + t.Errorf("PrepareOutgoingMessage() - has error %v", err) + } + if len(msg.PduBytes()) == 0 { + t.Error("PrepareOutgoingMessage() - pdu bytes") + } + p := pdu.(*snmpgo.ScopedPdu) + if p.RequestId() == 0 { + t.Error("PrepareOutgoingMessage() - request id") + } + if !bytes.Equal(p.ContextEngineId, expCtxId) { + t.Errorf("PrepareOutgoingMessage() - expected [%s], actual [%s]", + snmpgo.ToHexStr(expCtxId, ""), snmpgo.ToHexStr(p.ContextEngineId, "")) + } + if string(p.ContextName) != expCtxName { + t.Errorf("GenerateRequestMessage() - expected [%s], actual [%s]", + expCtxName, string(p.ContextName)) + } + msgv3 := snmpgo.ToMessageV3(msg) + if msgv3.MessageId == 0 { + t.Error("PrepareOutgoingMessage() - message id") + } + if !msgv3.Reportable() || !msgv3.Authentication() || !msgv3.Privacy() { + t.Error("PrepareOutgoingMessage() - security flag") + } + msgv3.SetAuthentication(false) + msgv3.SetPrivacy(false) + msgv3.AuthEngineId = []byte{0, 0, 0, 0, 0} + requestId := pdu.RequestId() + messageId := msgv3.MessageId + + _, err = mp.PrepareDataElements(sec, msg, msg) + if err == nil { + t.Error("PrepareDataElements() - pdu type check") + } + + pdu = snmpgo.NewPdu(snmpgo.V3, snmpgo.GetResponse) + rmsg := snmpgo.ToMessageV3(snmpgo.NewMessageWithPdu(snmpgo.V3, pdu)) + rmsg.AuthEngineId = []byte{0, 0, 0, 0, 0} + rmsg.UserName = []byte("myName") + _, err = mp.PrepareDataElements(sec, rmsg, msg) + if err == nil { + t.Error("PrepareDataElements() - message id check") + } + + rmsg.MessageId = messageId + _, err = mp.PrepareDataElements(sec, rmsg, msg) + if err == nil { + t.Error("PrepareDataElements() - security model check") + } + + pduBytes, _ := pdu.Marshal() + rmsg.SetPduBytes(pduBytes) + rmsg.SecurityModel = 3 + _, err = mp.PrepareDataElements(sec, rmsg, msg) + if err == nil { + t.Error("PrepareDataElements() - request id check") + } + + pdu.SetRequestId(requestId) + pduBytes, _ = pdu.Marshal() + rmsg.SetPduBytes(pduBytes) + _, err = mp.PrepareDataElements(sec, rmsg, msg) + if err == nil { + t.Errorf("PrepareDataElements() - contextEngineId check") + } + + pdu.(*snmpgo.ScopedPdu).ContextEngineId = expCtxId + pduBytes, _ = pdu.Marshal() + rmsg.SetPduBytes(pduBytes) + _, err = mp.PrepareDataElements(sec, rmsg, msg) + if err == nil { + t.Errorf("PrepareDataElements() - contextName check") + } + + pdu.(*snmpgo.ScopedPdu).ContextName = []byte(expCtxName) + pduBytes, _ = pdu.Marshal() + rmsg.SetPduBytes(pduBytes) + + msgv3.SetAuthentication(true) + _, err = mp.PrepareDataElements(sec, rmsg, msg) + if err == nil { + t.Errorf("PrepareDataElements() - response authenticate check") + } + + msgv3.SetAuthentication(false) + _, err = mp.PrepareDataElements(sec, rmsg, msg) + if err != nil { + t.Errorf("PrepareDataElements() - has error %v", err) + } +} + +func TestMessageProcessingV3Receive(t *testing.T) { + secEngId := []byte{0x80, 0x00, 0x00, 0x00, 0x01} + args := &snmpgo.SNMPArguments{ + Version: snmpgo.V3, + UserName: "myName", + SecurityEngineId: hex.EncodeToString(secEngId), + } + mp := snmpgo.NewMessageProcessing(args.Version) + sec := snmpgo.NewSecurity(args) + usm := snmpgo.ToUsm(sec) + usm.SetAuthEngineId(secEngId) + usm.DiscoveryStatus = 3 // remoteReference + + pdu := snmpgo.NewPdu(snmpgo.V3, snmpgo.GetResponse) + pduBytes, _ := pdu.Marshal() + rmsg := snmpgo.ToMessageV3(snmpgo.NewMessageWithPdu(snmpgo.V3, pdu)) + rmsg.AuthEngineId = secEngId + rmsg.UserName = []byte("myName") + rmsg.SecurityModel = 3 // securityUsm + rmsg.SetPduBytes(pduBytes) + _, err := mp.PrepareDataElements(sec, rmsg, nil) + if err == nil { + t.Error("PrepareDataElements() - pdu type check") + } + + pdu = snmpgo.NewPdu(snmpgo.V3, snmpgo.SNMPTrapV2) + pduBytes, _ = pdu.Marshal() + rmsg = snmpgo.ToMessageV3(snmpgo.NewMessageWithPdu(snmpgo.V3, pdu)) + rmsg.AuthEngineId = secEngId + rmsg.UserName = []byte("myName") + rmsg.SecurityModel = 3 // securityUsm + rmsg.SetPduBytes(pduBytes) + _, err = mp.PrepareDataElements(sec, rmsg, nil) + if err != nil { + t.Errorf("PrepareDataElements() - has error %v", err) + } +} diff --git a/vendor/github.com/k-sone/snmpgo/pdu.go b/vendor/github.com/k-sone/snmpgo/pdu.go new file mode 100644 index 0000000000..7f5b2a97d4 --- /dev/null +++ b/vendor/github.com/k-sone/snmpgo/pdu.go @@ -0,0 +1,454 @@ +package snmpgo + +import ( + "encoding/asn1" + "fmt" + "sort" + "strings" +) + +type VarBind struct { + Oid *Oid + Variable Variable +} + +func (v *VarBind) Marshal() (b []byte, err error) { + var buf []byte + raw := asn1.RawValue{Class: classUniversal, Tag: tagSequence, IsCompound: true} + + if v.Oid == nil || v.Variable == nil { + return asn1.Marshal(raw) + } + + buf, err = v.Oid.Marshal() + if err != nil { + return + } + raw.Bytes = buf + + buf, err = v.Variable.Marshal() + if err != nil { + return + } + raw.Bytes = append(raw.Bytes, buf...) + + return asn1.Marshal(raw) +} + +func (v *VarBind) Unmarshal(b []byte) (rest []byte, err error) { + var raw asn1.RawValue + rest, err = asn1.Unmarshal(b, &raw) + if err != nil { + return nil, err + } + if raw.Class != classUniversal || raw.Tag != tagSequence || !raw.IsCompound { + return nil, asn1.StructuralError{fmt.Sprintf( + "Invalid VarBind object - Class [%02x], Tag [%02x] : [%s]", + raw.Class, raw.Tag, toHexStr(b, " "))} + } + + var oid Oid + next, err := (&oid).Unmarshal(raw.Bytes) + if err != nil { + return + } + + variable, _, err := unmarshalVariable(next) + if err != nil { + return + } + + v.Oid = &oid + v.Variable = variable + return +} + +func (v *VarBind) String() string { + var oid, vtype, value string + if v.Oid != nil { + oid = v.Oid.String() + } + if v.Variable != nil { + vtype = v.Variable.Type() + value = escape(v.Variable.String()) + } + return fmt.Sprintf(`{"Oid": "%s", "Variable": {"Type": "%s", "Value": %s}}`, + oid, vtype, value) +} + +func NewVarBind(oid *Oid, val Variable) *VarBind { + return &VarBind{ + Oid: oid, + Variable: val, + } +} + +type VarBinds []*VarBind + +// Gets a VarBind that matches +func (v VarBinds) MatchOid(oid *Oid) *VarBind { + for _, o := range v { + if o.Oid != nil && o.Oid.Equal(oid) { + return o + } + } + return nil +} + +// Gets a VarBind list that matches the prefix +func (v VarBinds) MatchBaseOids(prefix *Oid) VarBinds { + result := make(VarBinds, 0) + for _, o := range v { + if o.Oid != nil && o.Oid.Contains(prefix) { + result = append(result, o) + } + } + return result +} + +// Sort a VarBind list by OID +func (v VarBinds) Sort() VarBinds { + c := make(VarBinds, len(v)) + copy(c, v) + sort.Sort(sortableVarBinds{c}) + return c +} + +func (v VarBinds) uniq(comp func(a, b *VarBind) bool) VarBinds { + var before *VarBind + c := make(VarBinds, 0, len(v)) + for _, val := range v { + if !comp(before, val) { + before = val + c = append(c, val) + } + } + return c +} + +// Filter out adjacent VarBind list +func (v VarBinds) Uniq() VarBinds { + return v.uniq(func(a, b *VarBind) bool { + if b == nil { + return a == nil + } else if b.Oid == nil { + return a != nil && a.Oid == nil + } else { + return a != nil && b.Oid.Equal(a.Oid) + } + }) +} + +func (v VarBinds) String() string { + varBinds := make([]string, len(v)) + for i, o := range v { + varBinds[i] = o.String() + } + return "[" + strings.Join(varBinds, ", ") + "]" +} + +type sortableVarBinds struct { + VarBinds +} + +func (v sortableVarBinds) Len() int { + return len(v.VarBinds) +} + +func (v sortableVarBinds) Swap(i, j int) { + v.VarBinds[i], v.VarBinds[j] = v.VarBinds[j], v.VarBinds[i] +} + +func (v sortableVarBinds) Less(i, j int) bool { + t := v.VarBinds[i] + return t != nil && t.Oid != nil && t.Oid.Compare(v.VarBinds[j].Oid) < 1 +} + +// The protocol data unit of SNMP +type Pdu interface { + PduType() PduType + RequestId() int + SetRequestId(int) + ErrorStatus() ErrorStatus + SetErrorStatus(ErrorStatus) + ErrorIndex() int + SetErrorIndex(int) + SetNonrepeaters(int) + SetMaxRepetitions(int) + AppendVarBind(*Oid, Variable) + VarBinds() VarBinds + Marshal() ([]byte, error) + Unmarshal([]byte) (rest []byte, err error) + String() string +} + +// The PduV1 is used by SNMP V1 and V2c, other than the SNMP V1 Trap +type PduV1 struct { + pduType PduType + requestId int + errorStatus ErrorStatus + errorIndex int + varBinds VarBinds +} + +func (pdu *PduV1) PduType() PduType { + return pdu.pduType +} + +func (pdu *PduV1) RequestId() int { + return pdu.requestId +} + +func (pdu *PduV1) SetRequestId(i int) { + pdu.requestId = i +} + +func (pdu *PduV1) ErrorStatus() ErrorStatus { + return pdu.errorStatus +} + +func (pdu *PduV1) SetErrorStatus(i ErrorStatus) { + pdu.errorStatus = i +} + +func (pdu *PduV1) ErrorIndex() int { + return pdu.errorIndex +} + +func (pdu *PduV1) SetErrorIndex(i int) { + pdu.errorIndex = i +} + +func (pdu *PduV1) SetNonrepeaters(i int) { + pdu.errorStatus = ErrorStatus(i) +} + +func (pdu *PduV1) SetMaxRepetitions(i int) { + pdu.errorIndex = i +} + +func (pdu *PduV1) AppendVarBind(oid *Oid, variable Variable) { + pdu.varBinds = append(pdu.varBinds, &VarBind{ + Oid: oid, + Variable: variable, + }) +} + +func (pdu *PduV1) VarBinds() VarBinds { + return pdu.varBinds +} + +func (pdu *PduV1) Marshal() (b []byte, err error) { + var buf []byte + raw := asn1.RawValue{Class: classContextSpecific, Tag: int(pdu.pduType), IsCompound: true} + + buf, err = asn1.Marshal(pdu.requestId) + if err != nil { + return + } + raw.Bytes = buf + + buf, err = asn1.Marshal(pdu.errorStatus) + if err != nil { + return + } + raw.Bytes = append(raw.Bytes, buf...) + + buf, err = asn1.Marshal(pdu.errorIndex) + if err != nil { + return + } + raw.Bytes = append(raw.Bytes, buf...) + + varBinds := asn1.RawValue{Class: classUniversal, Tag: tagSequence, IsCompound: true} + for i := 0; i < len(pdu.varBinds); i++ { + buf, err = pdu.varBinds[i].Marshal() + if err != nil { + return + } + varBinds.Bytes = append(varBinds.Bytes, buf...) + } + + buf, err = asn1.Marshal(varBinds) + if err != nil { + return + } + raw.Bytes = append(raw.Bytes, buf...) + + return asn1.Marshal(raw) +} + +func (pdu *PduV1) Unmarshal(b []byte) (rest []byte, err error) { + var raw asn1.RawValue + rest, err = asn1.Unmarshal(b, &raw) + if err != nil { + return + } + if raw.Class != classContextSpecific || !raw.IsCompound { + return nil, asn1.StructuralError{fmt.Sprintf( + "Invalid Pdu object - Class [%02x], Tag [%02x] : [%s]", + raw.Class, raw.Tag, toHexStr(b, " "))} + } + + next := raw.Bytes + + var requestId int + next, err = asn1.Unmarshal(next, &requestId) + if err != nil { + return + } + + var errorStatus int + next, err = asn1.Unmarshal(next, &errorStatus) + if err != nil { + return + } + + var errorIndex int + next, err = asn1.Unmarshal(next, &errorIndex) + if err != nil { + return + } + + var varBinds asn1.RawValue + _, err = asn1.Unmarshal(next, &varBinds) + if err != nil { + return + } + if varBinds.Class != classUniversal || varBinds.Tag != tagSequence || !varBinds.IsCompound { + return nil, asn1.StructuralError{fmt.Sprintf( + "Invalid VarBinds object - Class [%02x], Tag [%02x] : [%s]", + varBinds.Class, varBinds.Tag, toHexStr(next, " "))} + } + + next = varBinds.Bytes + for len(next) > 0 { + var varBind VarBind + next, err = (&varBind).Unmarshal(next) + if err != nil { + return + } + pdu.varBinds = append(pdu.varBinds, &varBind) + } + + pdu.pduType = PduType(raw.Tag) + pdu.requestId = requestId + pdu.errorStatus = ErrorStatus(errorStatus) + pdu.errorIndex = errorIndex + return +} + +func (pdu *PduV1) String() string { + return fmt.Sprintf( + `{"Type": "%s", "RequestId": "%d", "ErrorStatus": "%s", `+ + `"ErrorIndex": "%d", "VarBinds": %s}`, + pdu.pduType, pdu.requestId, pdu.errorStatus, pdu.errorIndex, + pdu.varBinds.String()) +} + +// The ScopedPdu is used by SNMP V3. +// Includes the PduV1, and contains a SNMP context parameter +type ScopedPdu struct { + ContextEngineId []byte + ContextName []byte + PduV1 +} + +func (pdu *ScopedPdu) Marshal() (b []byte, err error) { + var buf []byte + raw := asn1.RawValue{Class: classUniversal, Tag: tagSequence, IsCompound: true} + + buf, err = asn1.Marshal(pdu.ContextEngineId) + if err != nil { + return + } + raw.Bytes = buf + + buf, err = asn1.Marshal(pdu.ContextName) + if err != nil { + return + } + raw.Bytes = append(raw.Bytes, buf...) + + buf, err = pdu.PduV1.Marshal() + if err != nil { + return + } + raw.Bytes = append(raw.Bytes, buf...) + + return asn1.Marshal(raw) +} + +func (pdu *ScopedPdu) Unmarshal(b []byte) (rest []byte, err error) { + var raw asn1.RawValue + rest, err = asn1.Unmarshal(b, &raw) + if err != nil { + return nil, err + } + if raw.Class != classUniversal || raw.Tag != tagSequence || !raw.IsCompound { + return nil, asn1.StructuralError{fmt.Sprintf( + "Invalid ScopedPud object - Class [%02x], Tag [%02x] : [%s]", + raw.Class, raw.Tag, toHexStr(b, " "))} + } + + next := raw.Bytes + + var contextEngineId []byte + next, err = asn1.Unmarshal(next, &contextEngineId) + if err != nil { + return + } + + var contextName []byte + next, err = asn1.Unmarshal(next, &contextName) + if err != nil { + return + } + + var pduV1 PduV1 + _, err = (&pduV1).Unmarshal(next) + if err != nil { + return + } + + pdu.ContextEngineId = contextEngineId + pdu.ContextName = contextName + pdu.PduV1 = pduV1 + return +} + +func (pdu *ScopedPdu) String() string { + return fmt.Sprintf( + `{"Type": "%s", "RequestId": "%d", "ErrorStatus": "%s", "ErrorIndex": "%d", `+ + `"ContextEngineId": "%s", "ContextName": %s, "VarBinds": %s}`, + pdu.pduType, pdu.requestId, pdu.errorStatus, pdu.errorIndex, + toHexStr(pdu.ContextEngineId, ""), escape(string(pdu.ContextName)), + pdu.varBinds.String()) +} + +func NewPdu(ver SNMPVersion, t PduType) (pdu Pdu) { + p := PduV1{pduType: t} + switch ver { + case V1, V2c: + pdu = &p + case V3: + pdu = &ScopedPdu{PduV1: p} + } + return +} + +func NewPduWithOids(ver SNMPVersion, t PduType, oids Oids) (pdu Pdu) { + pdu = NewPdu(ver, t) + for _, o := range oids { + pdu.AppendVarBind(o, NewNull()) + } + return +} + +func NewPduWithVarBinds(ver SNMPVersion, t PduType, varBinds VarBinds) (pdu Pdu) { + pdu = NewPdu(ver, t) + for _, v := range varBinds { + pdu.AppendVarBind(v.Oid, v.Variable) + } + return +} diff --git a/vendor/github.com/k-sone/snmpgo/pdu_test.go b/vendor/github.com/k-sone/snmpgo/pdu_test.go new file mode 100644 index 0000000000..5b293ab41b --- /dev/null +++ b/vendor/github.com/k-sone/snmpgo/pdu_test.go @@ -0,0 +1,280 @@ +package snmpgo_test + +import ( + "bytes" + "testing" + + "github.com/k-sone/snmpgo" +) + +func testVarBind(t *testing.T, v *snmpgo.VarBind, expStr string) { + var w snmpgo.VarBind + buf, err := v.Marshal() + if err != nil { + t.Errorf("Marshal() : %v", err) + } + rest, err := (&w).Unmarshal(buf) + if len(rest) != 0 || err != nil { + t.Errorf("Unmarshal() - len[%d] err[%v]", len(rest), err) + } + if expStr != w.String() { + t.Errorf("Unmarshal() - expected [%s], actual [%s]", expStr, w.String()) + } + + buf = append(buf, 0x00) + rest, err = (&w).Unmarshal(buf) + if len(rest) != 1 || err != nil { + t.Errorf("Unmarshal() with rest - len[%d] err[%v]", len(rest), err) + } + if expStr != w.String() { + t.Errorf("Unmarshal() - expected [%s], actual [%s]", expStr, w.String()) + } +} + +func TestVarBind(t *testing.T) { + var v snmpgo.VarBind + oid, _ := snmpgo.NewOid("1.3.6.1.2.1.1.1.0") + v = snmpgo.VarBind{Oid: oid} + + v.Variable = snmpgo.NewInteger(-2147483648) + testVarBind(t, &v, + `{"Oid": "1.3.6.1.2.1.1.1.0", "Variable": {"Type": "Integer", "Value": "-2147483648"}}`) + + v.Variable = snmpgo.NewOctetString([]byte("MyHost")) + testVarBind(t, &v, + `{"Oid": "1.3.6.1.2.1.1.1.0", "Variable": {"Type": "OctetString", "Value": "MyHost"}}`) + + v.Variable = snmpgo.NewNull() + testVarBind(t, &v, `{"Oid": "1.3.6.1.2.1.1.1.0", "Variable": {"Type": "Null", "Value": ""}}`) + + v.Variable = snmpgo.NewCounter32(uint32(4294967295)) + testVarBind(t, &v, + `{"Oid": "1.3.6.1.2.1.1.1.0", "Variable": {"Type": "Counter32", "Value": "4294967295"}}`) + + v.Variable = snmpgo.NewCounter64(uint64(18446744073709551615)) + testVarBind(t, &v, `{"Oid": "1.3.6.1.2.1.1.1.0", `+ + `"Variable": {"Type": "Counter64", "Value": "18446744073709551615"}}`) + + expBuf := []byte{0x30, 0x00} + v = snmpgo.VarBind{} + buf, err := v.Marshal() + if err != nil { + t.Errorf("Marshal() : %v", err) + } + if !bytes.Equal(expBuf, buf) { + t.Errorf("Marshal() - expected [%s], actual [%s]", + snmpgo.ToHexStr(expBuf, " "), snmpgo.ToHexStr(buf, " ")) + } + + buf = []byte{0x00, 0x00} + _, err = (&v).Unmarshal(buf) + if err == nil { + t.Errorf("Unmarshal() : can not validation") + } +} + +func TestVarBinds(t *testing.T) { + var v snmpgo.VarBinds + + oid, _ := snmpgo.NewOid("1.3.6.1.2.1.1.1.0") + v = append(v, snmpgo.NewVarBind(oid, snmpgo.NewOctetString([]byte("MyHost")))) + oid, _ = snmpgo.NewOid("1.3.6.1.2.1.1.2.0") + v = append(v, snmpgo.NewVarBind(oid, snmpgo.NewNull())) + oid, _ = snmpgo.NewOid("1.3.6.1.2.1.1.3.0") + v = append(v, snmpgo.NewVarBind(oid, snmpgo.NewTimeTicks(uint32(11111)))) + + oid, _ = snmpgo.NewOid("1.3.6.1.2.1.1.1.0") + varBind := v.MatchOid(oid) + if varBind == nil || !varBind.Oid.Equal(oid) { + t.Errorf("Failed to MatchOid()") + } + oid, _ = snmpgo.NewOid("1.3.6.1.2.1.1.1.1") + varBind = v.MatchOid(oid) + if varBind != nil { + t.Errorf("Failed to MatchOid() - no match") + } + varBind = v.MatchOid(nil) + if varBind != nil { + t.Errorf("Failed to MatchOid() - nil") + } + + oid, _ = snmpgo.NewOid("1.3.6.1.2.1.1") + varBinds := v.MatchBaseOids(oid) + if len(varBinds) != 3 { + t.Errorf("Failed to MatchBaseOids()") + } + oid, _ = snmpgo.NewOid("1.3.6.1.2.1.1.1.0") + varBinds = v.MatchBaseOids(oid) + if len(varBinds) != 1 || !varBinds[0].Oid.Equal(oid) { + t.Errorf("Failed to MatchBaseOids() - one") + } + oid, _ = snmpgo.NewOid("1.3.6.1.2.1.1.1.1") + varBinds = v.MatchBaseOids(oid) + if len(varBinds) != 0 { + t.Errorf("Failed to MatchBaseOids() - no match") + } + varBinds = v.MatchBaseOids(nil) + if len(varBinds) != 0 { + t.Errorf("Failed to MatchBaseOids() - nil") + } + + var w snmpgo.VarBinds + for _, o := range []string{ + "1.3.6.1.2.1.1.2.0", + "1.3.6.1.2.1.1.1.0", + "1.3.6.1.2.1.1.3.0", + "1.3.6.1.2.1.1", + "1.3.6.1.2.1.1.1.0", + } { + oid, _ = snmpgo.NewOid(o) + w = append(w, snmpgo.NewVarBind(oid, snmpgo.NewNull())) + } + + expOids, _ := snmpgo.NewOids([]string{ + "1.3.6.1.2.1.1", + "1.3.6.1.2.1.1.1.0", + "1.3.6.1.2.1.1.1.0", + "1.3.6.1.2.1.1.2.0", + "1.3.6.1.2.1.1.3.0", + }) + w = w.Sort() + if len(expOids) != len(w) { + t.Errorf("Sort() - expected [%d], actual [%d]", len(expOids), len(w)) + } + for i, o := range expOids { + if !o.Equal(w[i].Oid) { + t.Errorf("Sort() - expected [%s], actual [%s]", o, w[i].Oid) + } + } + + expOids, _ = snmpgo.NewOids([]string{ + "1.3.6.1.2.1.1", + "1.3.6.1.2.1.1.1.0", + "1.3.6.1.2.1.1.2.0", + "1.3.6.1.2.1.1.3.0", + }) + w = w.Sort().Uniq() + if len(expOids) != len(w) { + t.Errorf("Uniq() - expected [%d], actual [%d]", len(expOids), len(w)) + } + for i, o := range expOids { + if !o.Equal(w[i].Oid) { + t.Errorf("Uniq() - expected [%s], actual [%s]", o, w[i].Oid) + } + } +} + +func TestNewPdu(t *testing.T) { + pdu := snmpgo.NewPdu(snmpgo.V1, snmpgo.GetRequest) + if _, ok := pdu.(*snmpgo.PduV1); !ok { + t.Errorf("NewPdu() Invalid Pdu") + } + + pdu = snmpgo.NewPdu(snmpgo.V2c, snmpgo.GetRequest) + if _, ok := pdu.(*snmpgo.PduV1); !ok { + t.Errorf("NewPdu() Invalid Pdu") + } + + pdu = snmpgo.NewPdu(snmpgo.V3, snmpgo.GetRequest) + if _, ok := pdu.(*snmpgo.ScopedPdu); !ok { + t.Errorf("NewPdu() Invalid Pdu") + } +} + +func TestPduV1(t *testing.T) { + pdu := snmpgo.NewPdu(snmpgo.V2c, snmpgo.GetRequest) + pdu.SetRequestId(123) + pdu.SetErrorStatus(snmpgo.TooBig) + pdu.SetErrorIndex(2) + + oid, _ := snmpgo.NewOid("1.3.6.1.2.1.1.1.0") + pdu.AppendVarBind(oid, snmpgo.NewOctetString([]byte("MyHost"))) + oid, _ = snmpgo.NewOid("1.3.6.1.2.1.1.2.0") + pdu.AppendVarBind(oid, snmpgo.NewNull()) + oid, _ = snmpgo.NewOid("1.3.6.1.2.1.1.3.0") + pdu.AppendVarBind(oid, snmpgo.NewTimeTicks(uint32(11111))) + + expBuf := []byte{ + 0xa0, 0x3d, 0x02, 0x01, 0x7b, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02, + 0x30, 0x32, 0x30, 0x12, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x02, 0x01, + 0x01, 0x01, 0x00, 0x04, 0x06, 0x4d, 0x79, 0x48, 0x6f, 0x73, 0x74, + 0x30, 0x0c, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x01, 0x02, + 0x00, 0x05, 0x00, 0x30, 0x0e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x02, + 0x01, 0x01, 0x03, 0x00, 0x43, 0x02, 0x2b, 0x67, + } + buf, err := pdu.Marshal() + if err != nil { + t.Fatal("Marshal() : %v", err) + } + if !bytes.Equal(expBuf, buf) { + t.Errorf("Marshal() - expected [%s], actual [%s]", + snmpgo.ToHexStr(expBuf, " "), snmpgo.ToHexStr(buf, " ")) + } + + expStr := `{"Type": "GetRequest", "RequestId": "123", ` + + `"ErrorStatus": "TooBig", "ErrorIndex": "2", "VarBinds": [` + + `{"Oid": "1.3.6.1.2.1.1.1.0", "Variable": {"Type": "OctetString", "Value": "MyHost"}}, ` + + `{"Oid": "1.3.6.1.2.1.1.2.0", "Variable": {"Type": "Null", "Value": ""}}, ` + + `{"Oid": "1.3.6.1.2.1.1.3.0", "Variable": {"Type": "TimeTicks", "Value": "11111"}}]}` + var w snmpgo.PduV1 + rest, err := (&w).Unmarshal(buf) + if len(rest) != 0 || err != nil { + t.Errorf("Unmarshal() - len[%d] err[%v]", len(rest), err) + } + if expStr != w.String() { + t.Errorf("Unmarshal() - expected [%s], actual [%s]", expStr, w.String()) + } +} + +func TestScopedPdu(t *testing.T) { + pdu := snmpgo.NewPdu(snmpgo.V3, snmpgo.GetRequest) + pdu.SetRequestId(123) + pdu.SetErrorStatus(snmpgo.TooBig) + pdu.SetErrorIndex(2) + + sp := pdu.(*snmpgo.ScopedPdu) + sp.ContextEngineId = []byte{0x80, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07} + sp.ContextName = []byte("MyContext") + + oid, _ := snmpgo.NewOid("1.3.6.1.2.1.1.1.0") + pdu.AppendVarBind(oid, snmpgo.NewOctetString([]byte("MyHost"))) + oid, _ = snmpgo.NewOid("1.3.6.1.2.1.1.2.0") + pdu.AppendVarBind(oid, snmpgo.NewNull()) + oid, _ = snmpgo.NewOid("1.3.6.1.2.1.1.3.0") + pdu.AppendVarBind(oid, snmpgo.NewTimeTicks(uint32(11111))) + + expBuf := []byte{ + 0x30, 0x54, 0x04, 0x08, 0x80, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x04, 0x09, 0x4d, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, + 0xa0, 0x3d, 0x02, 0x01, 0x7b, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02, + 0x30, 0x32, 0x30, 0x12, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x02, 0x01, + 0x01, 0x01, 0x00, 0x04, 0x06, 0x4d, 0x79, 0x48, 0x6f, 0x73, 0x74, + 0x30, 0x0c, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x01, 0x02, + 0x00, 0x05, 0x00, 0x30, 0x0e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x02, + 0x01, 0x01, 0x03, 0x00, 0x43, 0x02, 0x2b, 0x67, + } + buf, err := pdu.Marshal() + if err != nil { + t.Fatal("Marshal() : %v", err) + } + if !bytes.Equal(expBuf, buf) { + t.Errorf("Marshal() - expected [%s], actual [%s]", + snmpgo.ToHexStr(expBuf, " "), snmpgo.ToHexStr(buf, " ")) + } + + expStr := `{"Type": "GetRequest", "RequestId": "123", ` + + `"ErrorStatus": "TooBig", "ErrorIndex": "2", ` + + `"ContextEngineId": "8001020304050607", "ContextName": "MyContext", ` + + `"VarBinds": [` + + `{"Oid": "1.3.6.1.2.1.1.1.0", "Variable": {"Type": "OctetString", "Value": "MyHost"}}, ` + + `{"Oid": "1.3.6.1.2.1.1.2.0", "Variable": {"Type": "Null", "Value": ""}}, ` + + `{"Oid": "1.3.6.1.2.1.1.3.0", "Variable": {"Type": "TimeTicks", "Value": "11111"}}]}` + var w snmpgo.ScopedPdu + rest, err := (&w).Unmarshal(buf) + if len(rest) != 0 || err != nil { + t.Errorf("Unmarshal() - len[%d] err[%v]", len(rest), err) + } + if expStr != w.String() { + t.Errorf("Unmarshal() - expected [%s], actual [%s]", expStr, w.String()) + } +} diff --git a/vendor/github.com/k-sone/snmpgo/security.go b/vendor/github.com/k-sone/snmpgo/security.go new file mode 100644 index 0000000000..8a1a386b4d --- /dev/null +++ b/vendor/github.com/k-sone/snmpgo/security.go @@ -0,0 +1,690 @@ +package snmpgo + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/des" + "crypto/hmac" + "crypto/md5" + "crypto/sha1" + "encoding/asn1" + "encoding/binary" + "fmt" + "hash" + "math" + "sync" + "time" +) + +type security interface { + Identifier() string + GenerateRequestMessage(message) error + GenerateResponseMessage(message) error + ProcessIncomingMessage(message) error + Discover(*SNMP) error + String() string +} + +type community struct { + Community []byte +} + +func (c *community) Identifier() string { + return string(c.Community) +} + +func (c *community) GenerateRequestMessage(sendMsg message) (err error) { + m := sendMsg.(*messageV1) + m.Community = c.Community + + b, err := m.Pdu().Marshal() + if err != nil { + return + } + m.SetPduBytes(b) + + return +} + +func (c *community) GenerateResponseMessage(sendMsg message) (err error) { + return c.GenerateRequestMessage(sendMsg) +} + +func (c *community) ProcessIncomingMessage(recvMsg message) (err error) { + rm := recvMsg.(*messageV1) + + if !bytes.Equal(c.Community, rm.Community) { + return &MessageError{ + Message: fmt.Sprintf( + "Community mismatch - expected [%s], actual [%s]", + toHexStr(c.Community, ""), toHexStr(rm.Community, "")), + Detail: fmt.Sprintf("Message - [%s]", rm), + } + } + + _, err = rm.Pdu().Unmarshal(rm.PduBytes()) + if err != nil { + return &MessageError{ + Cause: err, + Message: "Failed to Unmarshal Pdu", + Detail: fmt.Sprintf("Pdu Bytes - [%s]", toHexStr(rm.PduBytes(), " ")), + } + } + return +} + +func (c *community) Discover(snmp *SNMP) error { + return nil +} + +func (c *community) String() string { + return fmt.Sprintf(`{"Community": "%s"}`, toHexStr(c.Community, "")) +} + +type discoveryStatus int + +const ( + // for client side + noDiscovered discoveryStatus = iota + noSynchronized + discovered + + // for server side + remoteReference +) + +func (d discoveryStatus) String() string { + switch d { + case noDiscovered: + return "noDiscovered" + case noSynchronized: + return "noSynchronized" + case discovered: + return "discovered" + case remoteReference: + return "remoteReference" + default: + return "Unknown" + } +} + +type usm struct { + UserName []byte + DiscoveryStatus discoveryStatus + AuthEngineId []byte + AuthEngineBoots int64 + AuthEngineTime int64 + UpdatedTime time.Time + AuthKey []byte + AuthPassword string + AuthProtocol AuthProtocol + PrivKey []byte + PrivPassword string + PrivProtocol PrivProtocol +} + +func (u *usm) Identifier() string { + id := string(u.AuthEngineId) + ":" + string(u.UserName) + if len(u.AuthPassword) > 0 { + id += ":auth" + } + return id +} + +func (u *usm) GenerateRequestMessage(sendMsg message) (err error) { + // setup message + m := sendMsg.(*messageV3) + + if u.DiscoveryStatus > noDiscovered { + m.UserName = u.UserName + m.AuthEngineId = u.AuthEngineId + } + if u.DiscoveryStatus > noSynchronized { + err = u.UpdateEngineBootsTime() + if err != nil { + return + } + m.AuthEngineBoots = u.AuthEngineBoots + m.AuthEngineTime = u.AuthEngineTime + } + + // setup Pdu + pduBytes, err := sendMsg.Pdu().Marshal() + if err != nil { + return + } + m.SetPduBytes(pduBytes) + + if m.Authentication() { + // encrypt Pdu + if m.Privacy() { + err = encrypt(m, u.PrivProtocol, u.PrivKey) + if err != nil { + return + } + } + + // get digest of whole message + digest, e := mac(m, u.AuthProtocol, u.AuthKey) + if e != nil { + return e + } + m.AuthParameter = digest + } + + return +} + +func (u *usm) GenerateResponseMessage(sendMsg message) (err error) { + return u.GenerateRequestMessage(sendMsg) +} + +func (u *usm) ProcessIncomingMessage(recvMsg message) (err error) { + rm := recvMsg.(*messageV3) + + // RFC3411 Section 5 + if l := len(rm.AuthEngineId); l < 5 || l > 32 { + return &MessageError{ + Message: fmt.Sprintf("AuthEngineId length is range 5..32, value [%s]", + toHexStr(rm.AuthEngineId, "")), + } + } + if rm.AuthEngineBoots < 0 || rm.AuthEngineBoots > math.MaxInt32 { + return &MessageError{ + Message: fmt.Sprintf("AuthEngineBoots is range %d..%d, value [%d]", + 0, math.MaxInt32, rm.AuthEngineBoots), + } + } + if rm.AuthEngineTime < 0 || rm.AuthEngineTime > math.MaxInt32 { + return &MessageError{ + Message: fmt.Sprintf("AuthEngineTime is range %d..%d, value [%d]", + 0, math.MaxInt32, rm.AuthEngineTime), + } + } + if u.DiscoveryStatus > noDiscovered { + if !bytes.Equal(u.AuthEngineId, rm.AuthEngineId) { + return &MessageError{ + Message: fmt.Sprintf( + "AuthEngineId mismatch - expected [%s], actual [%s]", + toHexStr(u.AuthEngineId, ""), toHexStr(rm.AuthEngineId, "")), + Detail: fmt.Sprintf("Message - [%s]", rm), + } + } + if !bytes.Equal(u.UserName, rm.UserName) { + return &MessageError{ + Message: fmt.Sprintf( + "UserName mismatch - expected [%s], actual [%s]", + toHexStr(u.UserName, ""), toHexStr(rm.UserName, "")), + Detail: fmt.Sprintf("Message - [%s]", rm), + } + } + } + + if rm.Authentication() { + // get & check digest of whole message + digest, e := mac(rm, u.AuthProtocol, u.AuthKey) + if e != nil { + return &MessageError{ + Cause: e, + Message: "Can't get a message digest", + } + } + if !hmac.Equal(rm.AuthParameter, digest) { + return &MessageError{ + Message: fmt.Sprintf("Failed to authenticate - expected [%s], actual [%s]", + toHexStr(rm.AuthParameter, ""), toHexStr(digest, "")), + } + } + + // decrypt Pdu + if rm.Privacy() { + e := decrypt(rm, u.PrivProtocol, u.PrivKey, rm.PrivParameter) + if e != nil { + return &MessageError{ + Cause: e, + Message: "Can't decrypt a message", + } + } + } + } + + // update boots & time + switch u.DiscoveryStatus { + case remoteReference: + if rm.Authentication() { + if err = u.CheckTimeliness(rm.AuthEngineBoots, rm.AuthEngineTime); err != nil { + return + } + u.SynchronizeEngineBootsTime(rm.AuthEngineBoots, rm.AuthEngineTime) + } + case discovered: + if rm.Authentication() { + err = u.CheckTimeliness(rm.AuthEngineBoots, rm.AuthEngineTime) + if err != nil { + u.SynchronizeEngineBootsTime(0, 0) + u.DiscoveryStatus = noSynchronized + return + } + } + fallthrough + case noSynchronized: + if rm.Authentication() { + u.SynchronizeEngineBootsTime(rm.AuthEngineBoots, rm.AuthEngineTime) + u.DiscoveryStatus = discovered + } + case noDiscovered: + u.SetAuthEngineId(rm.AuthEngineId) + u.DiscoveryStatus = noSynchronized + } + + _, err = rm.Pdu().Unmarshal(rm.PduBytes()) + if err != nil { + var note string + if rm.Privacy() { + note = " (probably Pdu was unable to decrypt)" + } + return &MessageError{ + Cause: err, + Message: fmt.Sprintf("Failed to Unmarshal Pdu%s", note), + Detail: fmt.Sprintf("Pdu Bytes - [%s]", toHexStr(rm.PduBytes(), " ")), + } + } + return +} + +func (u *usm) Discover(snmp *SNMP) (err error) { + if snmp.args.SecurityEngineId != "" { + securityEngineId, _ := engineIdToBytes(snmp.args.SecurityEngineId) + u.SetAuthEngineId(securityEngineId) + u.DiscoveryStatus = noSynchronized + return + } + + if u.DiscoveryStatus == noDiscovered { + // Send an empty Pdu with the NoAuthNoPriv + orgSecLevel := snmp.args.SecurityLevel + snmp.args.SecurityLevel = NoAuthNoPriv + + pdu := NewPdu(snmp.args.Version, GetRequest) + _, err = snmp.sendPdu(pdu) + + snmp.args.SecurityLevel = orgSecLevel + if err != nil { + return + } + } + + if u.DiscoveryStatus == noSynchronized && snmp.args.SecurityLevel > NoAuthNoPriv { + // Send an empty Pdu + pdu := NewPdu(snmp.args.Version, GetRequest) + _, err = snmp.sendPdu(pdu) + if err != nil { + return + } + } + + return +} + +func (u *usm) SetAuthEngineId(authEngineId []byte) { + u.AuthEngineId = authEngineId + if len(u.AuthPassword) > 0 { + u.AuthKey = passwordToKey(u.AuthProtocol, u.AuthPassword, authEngineId) + } + if len(u.PrivPassword) > 0 { + u.PrivKey = passwordToKey(u.AuthProtocol, u.PrivPassword, authEngineId) + } +} + +func (u *usm) UpdateEngineBootsTime() error { + now := time.Now() + u.AuthEngineTime += int64(now.Sub(u.UpdatedTime).Seconds()) + if u.AuthEngineTime > math.MaxInt32 { + u.AuthEngineBoots++ + // RFC3414 2.2.2 + if u.AuthEngineBoots == math.MaxInt32 { + return fmt.Errorf("EngineBoots reached the max value, [%d]", math.MaxInt32) + } + u.AuthEngineTime -= math.MaxInt32 + } + u.UpdatedTime = now + return nil +} + +func (u *usm) SynchronizeEngineBootsTime(engineBoots, engineTime int64) { + u.AuthEngineBoots = engineBoots + u.AuthEngineTime = engineTime + u.UpdatedTime = time.Now() +} + +func (u *usm) CheckTimeliness(engineBoots, engineTime int64) error { + // RFC3414 Section 3.2 7) b) + if engineBoots == math.MaxInt32 || + engineBoots < u.AuthEngineBoots || + (engineBoots == u.AuthEngineBoots && u.AuthEngineTime-engineTime > 150) { + return &MessageError{ + Message: fmt.Sprintf( + "The message is not in the time window - local [%d/%d], remote [%d/%d]", + engineBoots, engineTime, u.AuthEngineBoots, u.AuthEngineTime), + } + } + return nil +} + +func (u *usm) String() string { + return fmt.Sprintf( + `{"UserName": "%s", "DiscoveryStatus": "%s", "AuthEngineId": "%s", `+ + `"AuthEngineBoots": "%d", "AuthEngineTime": "%d", "UpdatedTime": "%s", `+ + `"AuthKey": "%s", "AuthProtocol": "%s", "PrivKey": "%s", "PrivProtocol": "%s"}`, + toHexStr(u.UserName, ""), u.DiscoveryStatus, toHexStr(u.AuthEngineId, ""), + u.AuthEngineBoots, u.AuthEngineTime, u.UpdatedTime, + toHexStr(u.AuthKey, ""), u.AuthProtocol, toHexStr(u.PrivKey, ""), u.PrivProtocol) +} + +func mac(msg *messageV3, proto AuthProtocol, key []byte) ([]byte, error) { + tmp := msg.AuthParameter + msg.AuthParameter = padding([]byte{}, 12) + msgBytes, err := msg.Marshal() + msg.AuthParameter = tmp + if err != nil { + return nil, err + } + + var h hash.Hash + switch proto { + case Md5: + h = hmac.New(md5.New, key) + case Sha: + h = hmac.New(sha1.New, key) + } + h.Write(msgBytes) + return h.Sum(nil)[:12], nil +} + +func encrypt(msg *messageV3, proto PrivProtocol, key []byte) (err error) { + var dst, priv []byte + src := msg.PduBytes() + + switch proto { + case Des: + dst, priv, err = encryptDES(src, key, int32(msg.AuthEngineBoots), genSalt32()) + case Aes: + dst, priv, err = encryptAES( + src, key, int32(msg.AuthEngineBoots), int32(msg.AuthEngineTime), genSalt64()) + } + if err != nil { + return + } + + raw := asn1.RawValue{Class: classUniversal, Tag: tagOctetString, IsCompound: false} + raw.Bytes = dst + dst, err = asn1.Marshal(raw) + if err == nil { + msg.SetPduBytes(dst) + msg.PrivParameter = priv + } + return +} + +func decrypt(msg *messageV3, proto PrivProtocol, key, privParam []byte) (err error) { + var raw asn1.RawValue + _, err = asn1.Unmarshal(msg.PduBytes(), &raw) + if err != nil { + return + } + if raw.Class != classUniversal || raw.Tag != tagOctetString || raw.IsCompound { + return asn1.StructuralError{fmt.Sprintf( + "Invalid encrypted Pdu object - Class [%02x], Tag [%02x] : [%s]", + raw.Class, raw.Tag, toHexStr(msg.PduBytes(), " "))} + } + + var dst []byte + switch proto { + case Des: + dst, err = decryptDES(raw.Bytes, key, privParam) + case Aes: + dst, err = decryptAES( + raw.Bytes, key, privParam, int32(msg.AuthEngineBoots), int32(msg.AuthEngineTime)) + } + + if err == nil { + msg.SetPduBytes(dst) + } + return +} + +func encryptDES(src, key []byte, engineBoots, salt int32) (dst, privParam []byte, err error) { + block, err := des.NewCipher(key[:8]) + if err != nil { + return + } + + var buf bytes.Buffer + binary.Write(&buf, binary.BigEndian, engineBoots) + binary.Write(&buf, binary.BigEndian, salt) + privParam = buf.Bytes() + iv := xor(key[8:16], privParam) + + src = padding(src, des.BlockSize) + dst = make([]byte, len(src)) + + mode := cipher.NewCBCEncrypter(block, iv) + mode.CryptBlocks(dst, src) + return +} + +func decryptDES(src, key, privParam []byte) (dst []byte, err error) { + if len(src)%des.BlockSize != 0 { + err = &ArgumentError{ + Value: len(src), + Message: "Invalid DES cipher length", + } + return + } + if len(privParam) != 8 { + err = &ArgumentError{ + Value: len(privParam), + Message: "Invalid DES PrivParameter length", + } + return + } + + block, err := des.NewCipher(key[:8]) + if err != nil { + return + } + + iv := xor(key[8:16], privParam) + dst = make([]byte, len(src)) + + mode := cipher.NewCBCDecrypter(block, iv) + mode.CryptBlocks(dst, src) + return +} + +func encryptAES(src, key []byte, engineBoots, engineTime int32, salt int64) ( + dst, privParam []byte, err error) { + + block, err := aes.NewCipher(key[:16]) + if err != nil { + return + } + + var buf1, buf2 bytes.Buffer + binary.Write(&buf1, binary.BigEndian, salt) + privParam = buf1.Bytes() + + binary.Write(&buf2, binary.BigEndian, engineBoots) + binary.Write(&buf2, binary.BigEndian, engineTime) + iv := append(buf2.Bytes(), privParam...) + + src = padding(src, aes.BlockSize) + dst = make([]byte, len(src)) + + mode := cipher.NewCFBEncrypter(block, iv) + mode.XORKeyStream(dst, src) + return +} + +func decryptAES(src, key, privParam []byte, engineBoots, engineTime int32) ( + dst []byte, err error) { + + if len(privParam) != 8 { + err = &ArgumentError{ + Value: len(privParam), + Message: "Invalid AES PrivParameter length", + } + return + } + + block, err := aes.NewCipher(key[:16]) + if err != nil { + return + } + + var buf bytes.Buffer + binary.Write(&buf, binary.BigEndian, engineBoots) + binary.Write(&buf, binary.BigEndian, engineTime) + iv := append(buf.Bytes(), privParam...) + + dst = make([]byte, len(src)) + + mode := cipher.NewCFBDecrypter(block, iv) + mode.XORKeyStream(dst, src) + return +} + +func passwordToKey(proto AuthProtocol, password string, engineId []byte) []byte { + var h hash.Hash + switch proto { + case Md5: + h = md5.New() + case Sha: + h = sha1.New() + } + + pass := []byte(password) + plen := len(pass) + for i := mega / plen; i > 0; i-- { + h.Write(pass) + } + remain := mega % plen + if remain > 0 { + h.Write(pass[:remain]) + } + ku := h.Sum(nil) + + h.Reset() + h.Write(ku) + h.Write(engineId) + h.Write(ku) + return h.Sum(nil) +} + +func newSecurity(args *SNMPArguments) security { + switch args.Version { + case V1, V2c: + return &community{ + Community: []byte(args.Community), + } + case V3: + sec := &usm{ + UserName: []byte(args.UserName), + } + switch args.SecurityLevel { + case AuthPriv: + sec.PrivPassword = args.PrivPassword + sec.PrivProtocol = args.PrivProtocol + fallthrough + case AuthNoPriv: + sec.AuthPassword = args.AuthPassword + sec.AuthProtocol = args.AuthProtocol + } + return sec + default: + return nil + } +} + +func newSecurityFromEntry(entry *SecurityEntry) security { + switch entry.Version { + case V1, V2c: + return &community{ + Community: []byte(entry.Community), + } + case V3: + sec := &usm{ + UserName: []byte(entry.UserName), + } + switch entry.SecurityLevel { + case AuthPriv: + sec.PrivPassword = entry.PrivPassword + sec.PrivProtocol = entry.PrivProtocol + fallthrough + case AuthNoPriv: + sec.AuthPassword = entry.AuthPassword + sec.AuthProtocol = entry.AuthProtocol + } + if len(entry.SecurityEngineId) > 0 { + authEngineId, _ := engineIdToBytes(entry.SecurityEngineId) + sec.SetAuthEngineId(authEngineId) + sec.DiscoveryStatus = remoteReference + } + return sec + default: + return nil + } +} + +type securityMap struct { + lock *sync.RWMutex + objs map[string]security +} + +func (m *securityMap) Set(sec security) { + m.lock.Lock() + defer m.lock.Unlock() + m.objs[sec.Identifier()] = sec +} + +func (m *securityMap) Lookup(msg message) security { + var id string + switch mm := msg.(type) { + case *messageV1: + id = string(mm.Community) + case *messageV3: + id = string(mm.AuthEngineId) + ":" + string(mm.UserName) + if mm.Authentication() { + id += ":auth" + } + } + + m.lock.RLock() + defer m.lock.RUnlock() + return m.objs[id] +} + +func (m *securityMap) List() []security { + ret := make([]security, 0, len(m.objs)) + + m.lock.RLock() + defer m.lock.RUnlock() + for _, v := range m.objs { + ret = append(ret, v) + } + return ret +} + +func (m *securityMap) Delete(sec security) { + m.lock.Lock() + defer m.lock.Unlock() + delete(m.objs, sec.Identifier()) +} + +func newSecurityMap() *securityMap { + return &securityMap{ + lock: new(sync.RWMutex), + objs: map[string]security{}, + } +} diff --git a/vendor/github.com/k-sone/snmpgo/security_test.go b/vendor/github.com/k-sone/snmpgo/security_test.go new file mode 100644 index 0000000000..d801befae7 --- /dev/null +++ b/vendor/github.com/k-sone/snmpgo/security_test.go @@ -0,0 +1,354 @@ +package snmpgo_test + +import ( + "bytes" + "math" + "testing" + "time" + + "github.com/k-sone/snmpgo" +) + +// RFC3414 A.3 +func TestPasswordToKey(t *testing.T) { + password := "maplesyrup" + engineId := []byte{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + } + + expBuf := []byte{ + 0x52, 0x6f, 0x5e, 0xed, 0x9f, 0xcc, 0xe2, 0x6f, + 0x89, 0x64, 0xc2, 0x93, 0x07, 0x87, 0xd8, 0x2b, + } + key := snmpgo.PasswordToKey(snmpgo.Md5, password, engineId) + if !bytes.Equal(expBuf, key) { + t.Errorf("passwordToKey(Md5) - expected [%s], actual [%s]", + snmpgo.ToHexStr(expBuf, " "), snmpgo.ToHexStr(key, " ")) + } + + expBuf = []byte{ + 0x66, 0x95, 0xfe, 0xbc, 0x92, 0x88, 0xe3, 0x62, 0x82, 0x23, + 0x5f, 0xc7, 0x15, 0x1f, 0x12, 0x84, 0x97, 0xb3, 0x8f, 0x3f, + } + key = snmpgo.PasswordToKey(snmpgo.Sha, password, engineId) + if !bytes.Equal(expBuf, key) { + t.Errorf("passwordToKey(Aes) - expected [%s], actual [%s]", + snmpgo.ToHexStr(expBuf, " "), snmpgo.ToHexStr(key, " ")) + } +} + +func TestCipher(t *testing.T) { + original := []byte("my private message.") + password := "maplesyrup" + engineId := []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02} + engineBoots := int32(100) + engineTime := int32(1234567) + + key := snmpgo.PasswordToKey(snmpgo.Sha, password, engineId) + + cipher, priv, err := snmpgo.EncryptDES(original, key, engineBoots, 100) + if err != nil { + t.Errorf("DES Encrypt err %v", err) + } + result, err := snmpgo.DecryptDES(cipher, key, priv) + if err != nil { + t.Errorf("DES Decrypt err %v", err) + } + if bytes.Equal(original, result) { + t.Errorf("DES Encrypt, Decrypt - expected [%s], actual [%s]", original, result) + } + + cipher, priv, err = snmpgo.EncryptAES(original, key, engineBoots, engineTime, 100) + if err != nil { + t.Errorf("AES Encrypt err %v", err) + } + result, err = snmpgo.DecryptAES(cipher, key, priv, engineBoots, engineTime) + if err != nil { + t.Errorf("AES Decrypt err %v", err) + } + if bytes.Equal(original, result) { + t.Errorf("AES Encrypt, Decrypt - expected [%s], actual [%s]", original, result) + } +} + +func TestCommunity(t *testing.T) { + expCom := "public" + sec := snmpgo.NewCommunity() + sec.Community = []byte(expCom) + pdu := snmpgo.NewPdu(snmpgo.V2c, snmpgo.GetRequest) + smsg := snmpgo.ToMessageV1(snmpgo.NewMessageWithPdu(snmpgo.V2c, pdu)) + + err := sec.GenerateRequestMessage(smsg) + if err != nil { + t.Errorf("GenerateRequestMessage() - has error %v", err) + } + if !bytes.Equal(smsg.Community, []byte(expCom)) { + t.Errorf("GenerateRequestMessage() - expected [%s], actual [%s]", expCom, smsg.Community) + } + if len(smsg.PduBytes()) == 0 { + t.Error("GenerateRequestMessage() - pdu marshal") + } + + pdu = snmpgo.NewPdu(snmpgo.V2c, snmpgo.GetResponse) + rmsg := snmpgo.ToMessageV1(snmpgo.NewMessageWithPdu(snmpgo.V2c, pdu)) + + err = sec.ProcessIncomingMessage(rmsg) + if err == nil { + t.Error("ProcessIncomingMessage() - community check") + } + + rmsg.Community = []byte(expCom) + rmsg.SetPduBytes(smsg.PduBytes()) + err = sec.ProcessIncomingMessage(rmsg) + if err != nil { + t.Errorf("ProcessIncomingMessage() - has error %v", err) + } +} + +func TestUsm(t *testing.T) { + expUser := []byte("myUser") + expEngId := []byte{0x80, 0x00, 0x00, 0x00, 0x01} + sec := snmpgo.NewUsm() + sec.UserName = expUser + sec.AuthPassword = "aaaaaaaa" + sec.AuthProtocol = snmpgo.Md5 + sec.PrivPassword = "bbbbbbbb" + sec.PrivProtocol = snmpgo.Des + pdu := snmpgo.NewPdu(snmpgo.V3, snmpgo.GetRequest) + spdu := pdu.(*snmpgo.ScopedPdu) + smsg := snmpgo.ToMessageV3(snmpgo.NewMessageWithPdu(snmpgo.V3, pdu)) + smsg.SetAuthentication(false) + smsg.SetPrivacy(false) + + // Discovery + err := sec.GenerateRequestMessage(smsg) + if err != nil { + t.Errorf("GenerateRequestMessage() - has error %v", err) + } + if len(smsg.PduBytes()) == 0 { + t.Error("GenerateRequestMessage() - pdu marshal") + } + + pdu = snmpgo.NewPdu(snmpgo.V3, snmpgo.Report) + rmsg := snmpgo.ToMessageV3(snmpgo.NewMessageWithPdu(snmpgo.V3, pdu)) + rmsg.SetPduBytes(smsg.PduBytes()) + err = sec.ProcessIncomingMessage(rmsg) + if err == nil { + t.Error("ProcessIncomingMessage() - engineId check") + } + + rmsg.AuthEngineId = expEngId + rmsg.AuthEngineBoots = -1 + err = sec.ProcessIncomingMessage(rmsg) + if err == nil { + t.Error("ProcessIncomingMessage() - boots check") + } + + rmsg.AuthEngineBoots = 1 + rmsg.AuthEngineTime = -1 + err = sec.ProcessIncomingMessage(rmsg) + if err == nil { + t.Error("ProcessIncomingMessage() - time check") + } + + rmsg.AuthEngineTime = 1 + err = sec.ProcessIncomingMessage(rmsg) + if err != nil { + t.Errorf("ProcessIncomingMessage() - has error %v", err) + } + if !bytes.Equal(sec.AuthEngineId, expEngId) { + t.Errorf("ProcessIncomingMessage() - expected [%s], actual [%s]", + sec.AuthEngineId, expEngId) + } + if len(sec.AuthKey) == 0 { + t.Error("ProcessIncomingMessage() - authKey") + } + if len(sec.PrivKey) == 0 { + t.Error("ProcessIncomingMessage() - privKey") + } + + // Synchronize + smsg.SetAuthentication(true) + smsg.SetPrivacy(true) + + err = sec.GenerateRequestMessage(smsg) + if err != nil { + t.Errorf("GenerateRequestMessage() - has error %v", err) + } + if !bytes.Equal(smsg.UserName, expUser) { + t.Errorf("GenerateRequestMessage() - expected [%s], actual [%s]", + expUser, smsg.UserName) + } + if !bytes.Equal(smsg.AuthEngineId, expEngId) { + t.Errorf("GenerateRequestMessage() - expected [%s], actual [%s]", + expEngId, smsg.AuthEngineId) + } + if len(smsg.PrivParameter) == 0 { + t.Error("GenerateRequestMessage() - privParameter") + } + if len(smsg.AuthParameter) == 0 { + t.Error("GenerateRequestMessage() - authParameter") + } + + pdu = snmpgo.NewPdu(snmpgo.V3, snmpgo.Report) + rmsg = snmpgo.ToMessageV3(snmpgo.NewMessageWithPdu(snmpgo.V3, pdu)) + rmsg.SetAuthentication(true) + rmsg.SetPrivacy(true) + rmsg.SetPduBytes(smsg.PduBytes()) + rmsg.AuthEngineId = []byte("foobar") + rmsg.AuthEngineBoots = smsg.AuthEngineBoots + rmsg.AuthEngineTime = smsg.AuthEngineTime + rmsg.PrivParameter = smsg.PrivParameter + rmsg.AuthParameter = smsg.AuthParameter + + err = sec.ProcessIncomingMessage(rmsg) + if err == nil { + t.Error("ProcessIncomingMessage() - userName check") + } + + rmsg.UserName = expUser + err = sec.ProcessIncomingMessage(rmsg) + if err == nil { + t.Error("ProcessIncomingMessage() - authEngine check") + } + + rmsg.AuthEngineId = expEngId + err = sec.ProcessIncomingMessage(rmsg) + if err != nil { + t.Errorf("ProcessIncomingMessage() - has error %v", err) + } + if sec.AuthEngineBoots != rmsg.AuthEngineBoots { + t.Error("ProcessIncomingMessage() - engineBoots") + } + if sec.AuthEngineTime != rmsg.AuthEngineTime { + t.Error("ProcessIncomingMessage() - engineTime") + } + + // Request + sec.AuthEngineBoots = 1 + sec.AuthEngineTime = 1 + + err = sec.GenerateRequestMessage(smsg) + if err != nil { + t.Errorf("GenerateRequestMessage() - has error %v", err) + } + if smsg.AuthEngineBoots != sec.AuthEngineBoots { + t.Errorf("GenerateRequestMessage() - expected [%d], actual [%d]", + sec.AuthEngineBoots, smsg.AuthEngineBoots) + } + if smsg.AuthEngineTime != sec.AuthEngineTime { + t.Errorf("GenerateRequestMessage() - expected [%d], actual [%d]", + sec.AuthEngineTime, smsg.AuthEngineTime) + } + + pdu = snmpgo.NewPdu(snmpgo.V3, snmpgo.GetResponse) + spdu = pdu.(*snmpgo.ScopedPdu) + rmsg = snmpgo.ToMessageV3(snmpgo.NewMessageWithPdu(snmpgo.V3, pdu)) + rmsg.AuthEngineId = expEngId + rmsg.AuthEngineBoots = smsg.AuthEngineBoots + rmsg.AuthEngineTime = smsg.AuthEngineTime + rmsg.UserName = expUser + + // set PduBytes with GetResponse + b, _ := spdu.Marshal() + rmsg.SetPduBytes(b) + + err = sec.ProcessIncomingMessage(rmsg) + if err != nil { + t.Error("ProcessIncomingMessage() - has error %v", err) + } +} + +func TestUsmUpdateEngineBootsTime(t *testing.T) { + sec := snmpgo.NewUsm() + + sec.UpdatedTime = time.Unix(time.Now().Unix()-int64(10), 0) + err := sec.UpdateEngineBootsTime() + if err != nil || sec.AuthEngineTime < 9 || sec.AuthEngineTime > 11 { + t.Error("EngineBootsTime() - update authEnginetime") + } + + sec.UpdatedTime = time.Unix(time.Now().Unix()-int64(10), 0) + sec.AuthEngineTime = math.MaxInt32 + err = sec.UpdateEngineBootsTime() + if err != nil || sec.AuthEngineBoots != 1 || + (sec.AuthEngineTime < 9 || sec.AuthEngineTime > 11) { + t.Error("EngineBootsTime() - carry-over authEngineBoots") + } + + sec.UpdatedTime = time.Unix(time.Now().Unix()-int64(10), 0) + sec.AuthEngineBoots = math.MaxInt32 - 1 + sec.AuthEngineTime = math.MaxInt32 + err = sec.UpdateEngineBootsTime() + if err == nil { + t.Error("EngineBootsTime() - max authEngineBoots") + } +} + +func TestUsmTimeliness(t *testing.T) { + sec := snmpgo.NewUsm() + + err := sec.CheckTimeliness(math.MaxInt32, 0) + if err == nil { + t.Error("Timeliness() - max authEngineBoots") + } + + sec.AuthEngineBoots = 1 + err = sec.CheckTimeliness(0, 0) + if err == nil { + t.Error("Timeliness() - lose authEngineBoots") + } + + sec.AuthEngineBoots = 0 + sec.AuthEngineTime = 1150 + err = sec.CheckTimeliness(0, 999) + if err == nil { + t.Error("Timeliness() - lose authEngineTime") + } + + err = sec.CheckTimeliness(0, 1000) + if err != nil { + t.Errorf("Timeliness() - has error %v", err) + } + + err = sec.CheckTimeliness(0, 2000) + if err != nil { + t.Errorf("Timeliness() - has error %v", err) + } +} + +func TestSecurityMap(t *testing.T) { + sm := snmpgo.NewSecurityMap() + s1 := snmpgo.NewSecurity(&snmpgo.SNMPArguments{ + Version: snmpgo.V2c, + Community: "public", + }) + s2 := snmpgo.NewSecurity(&snmpgo.SNMPArguments{ + Version: snmpgo.V2c, + Community: "private", + }) + m1 := snmpgo.NewMessage(snmpgo.V2c) + snmpgo.ToMessageV1(m1).Community = []byte("public") + m2 := snmpgo.NewMessage(snmpgo.V2c) + snmpgo.ToMessageV1(m2).Community = []byte("private") + + sm.Set(s1) + if sm.Lookup(m1) != s1 { + t.Error("Lookup() - not exists") + } + if sm.Lookup(m2) != nil { + t.Error("Lookup() - exists") + } + + sm.Set(s2) + if sl := sm.List(); len(sl) != 2 { + t.Error("List() - invalid length") + } + + sm.Delete(s1) + if sm.Lookup(m1) != nil { + t.Error("Delete() - failed to delete") + + } +} diff --git a/vendor/github.com/k-sone/snmpgo/server.go b/vendor/github.com/k-sone/snmpgo/server.go new file mode 100644 index 0000000000..2157c043bc --- /dev/null +++ b/vendor/github.com/k-sone/snmpgo/server.go @@ -0,0 +1,342 @@ +package snmpgo + +import ( + "fmt" + "log" + "math" + "net" + "runtime" + "sync" + "time" +) + +const ( + maxTrapSize = 2 << 11 // 2048 bytes +) + +// An argument for creating a Server Object +type ServerArguments struct { + Network string // "udp", "udp4", "udp6" (The default is `udp`) + LocalAddr string // See net.Dial parameter + WriteTimeout time.Duration // Timeout for writing a response (The default is 5sec) + MessageMaxSize int // Maximum size of a SNMP message (The default is 2048) +} + +func (a *ServerArguments) setDefault() { + if a.Network == "" { + a.Network = "udp" + } + if a.WriteTimeout <= 0 { + a.WriteTimeout = timeoutDefault + } + if a.MessageMaxSize == 0 { + a.MessageMaxSize = maxTrapSize + } +} + +func (a *ServerArguments) validate() error { + switch a.Network { + case "", "udp", "udp4", "udp6": + default: + return &ArgumentError{ + Value: a.Network, + Message: fmt.Sprintf("Unsupported Network", a.Network), + } + } + if m := a.MessageMaxSize; (m != 0 && m < msgSizeMinimum) || m > math.MaxInt32 { + return &ArgumentError{ + Value: m, + Message: fmt.Sprintf("MessageMaxSize is range %d..%d", + msgSizeMinimum, math.MaxInt32), + } + } + + return nil +} + +func (a *ServerArguments) String() string { + return escape(a) +} + +// SecurityEntry is used for authentication of the received SNMP message +type SecurityEntry struct { + Version SNMPVersion // SNMP version to use (V2c or V3) + Community string // Community (V2c specific) + UserName string // Security name (V3 specific) + SecurityLevel SecurityLevel // Security level (V3 specific) + AuthPassword string // Authentication protocol pass phrase (V3 specific) + AuthProtocol AuthProtocol // Authentication protocol (V3 specific) + PrivPassword string // Privacy protocol pass phrase (V3 specific) + PrivProtocol PrivProtocol // Privacy protocol (V3 specific) + SecurityEngineId string // Security engine ID (V3 Trap specific) +} + +func (a *SecurityEntry) validate() error { + if v := a.Version; v != V2c && v != V3 { + return &ArgumentError{ + Value: a.Version, + Message: "Unsupported SNMP Version", + } + } + // TODO Refactor(this copied from `SNMPArguments.validate()`) + if a.Version == V3 { + // RFC3414 Section 5 + if l := len(a.UserName); l < 1 || l > 32 { + return &ArgumentError{ + Value: a.UserName, + Message: "UserName length is range 1..32", + } + } + if a.SecurityLevel > NoAuthNoPriv { + // RFC3414 Section 11.2 + if len(a.AuthPassword) < 8 { + return &ArgumentError{ + Value: a.AuthPassword, + Message: "AuthPassword is at least 8 characters in length", + } + } + if p := a.AuthProtocol; p != Md5 && p != Sha { + return &ArgumentError{ + Value: a.AuthProtocol, + Message: "Illegal AuthProtocol", + } + } + } + if a.SecurityLevel > AuthNoPriv { + // RFC3414 Section 11.2 + if len(a.PrivPassword) < 8 { + return &ArgumentError{ + Value: a.PrivPassword, + Message: "PrivPassword is at least 8 characters in length", + } + } + if p := a.PrivProtocol; p != Des && p != Aes { + return &ArgumentError{ + Value: a.PrivProtocol, + Message: "Illegal PrivProtocol", + } + } + } + if a.SecurityEngineId != "" { + a.SecurityEngineId = stripHexPrefix(a.SecurityEngineId) + _, err := engineIdToBytes(a.SecurityEngineId) + if err != nil { + return err + } + } + } + return nil +} + +func (a *SecurityEntry) String() string { + return escape(a) +} + +// TrapListener defines method that need to be implemented by Trap listeners. +// If OnTRAP panics, the server (caller of OnTRAP) assumes that affect of the panic +// is temporary and recovers by the panic and logs trace to the error log. +type TrapListener interface { + OnTRAP(trap *TrapRequest) +} + +// TrapRequest is representing trap request that is send from the network element. +type TrapRequest struct { + // The received PDU + Pdu Pdu + + // The source address of trap + Source net.Addr + + // Error is an optional field used to indicate + // errors which may occur during the decoding + // of the received packet + Error error +} + +// A TrapServer defines parameters for running of TRAP daemon that listens for incoming +// trap messages. +type TrapServer struct { + args *ServerArguments + mps map[SNMPVersion]messageProcessing + secs map[SNMPVersion]*securityMap + transport transport + servingMu sync.RWMutex + serving bool + + // Error Logger which will be used for logging of default errors + ErrorLog *log.Logger +} + +func (s *TrapServer) AddSecurity(entry *SecurityEntry) error { + if err := entry.validate(); err != nil { + return err + } + s.secs[entry.Version].Set(newSecurityFromEntry(entry)) + return nil +} + +func (s *TrapServer) DeleteSecurity(entry *SecurityEntry) error { + if err := entry.validate(); err != nil { + return err + } + s.secs[entry.Version].Delete(newSecurityFromEntry(entry)) + return nil +} + +// Serve starts the SNMP trap receiver. +// Serve blocks, the caller should call Close when finished, to shut it down. +func (s *TrapServer) Serve(listener TrapListener) error { + if listener == nil { + return &ArgumentError{Message: "listener is nil"} + } + s.servingMu.Lock() + s.serving = true + s.servingMu.Unlock() + size := s.args.MessageMaxSize + if size < recvBufferSize { + size = recvBufferSize + } + + for { + conn, err := s.transport.Listen() + s.servingMu.RLock() + serving := s.serving + s.servingMu.RUnlock() + if !serving { + return nil + } + if err != nil { + if e, ok := err.(net.Error); ok && e.Temporary() { + continue + } + return err + } + + go func(conn interface{}) { + defer s.transport.Close(conn) + buf := make([]byte, size) + for { + _, src, msg, err := s.transport.Read(conn, buf) + if _, ok := err.(net.Error); ok { + s.servingMu.RLock() + serving := s.serving + s.servingMu.RUnlock() + if serving { + s.logf("trap: failed to read packet: %v", err) + } + return + } + + go s.handle(listener, conn, msg, src, err) + } + }(conn) + } +} + +// Close shuts down the server. +func (s *TrapServer) Close() error { + s.servingMu.Lock() + s.serving = false + s.servingMu.Unlock() + return s.transport.Close(nil) +} + +// handle a newly received trap +func (s *TrapServer) handle(listener TrapListener, conn interface{}, msg message, src net.Addr, err error) { + defer func() { + if err := recover(); err != nil { + const size = 64 << 10 + logBuf := make([]byte, size) + logBuf = logBuf[:runtime.Stack(logBuf, false)] + s.logf("trap: panic while receiving %v: %v\n%s", src, err, logBuf) + + } + }() + + var pdu Pdu + var mp messageProcessing + var sec security + if msg != nil { + var ok bool + v := msg.Version() + if mp, ok = s.mps[v]; ok { + if sec = s.secs[v].Lookup(msg); sec != nil { + pdu, err = mp.PrepareDataElements(sec, msg, nil) + } else { + err = &MessageError{ + Message: "Authentication failure", + Detail: fmt.Sprintf("Message - [%s]", msg), + } + } + } else { + err = &MessageError{ + Message: fmt.Sprintf("Unsupported SNMP version: %s", v), + Detail: fmt.Sprintf("Message - [%s]", msg), + } + } + } + + if pdu != nil { + switch t := pdu.PduType(); t { + case SNMPTrapV2, InformRequest: + default: + err = &MessageError{ + Message: fmt.Sprintf("Invalid PduType: %s ", t), + Detail: fmt.Sprintf("Message - [%s]", msg), + } + pdu = nil + } + } + + listener.OnTRAP(&TrapRequest{Pdu: pdu, Source: src, Error: err}) + + if pdu != nil && pdu.PduType() == InformRequest { + if err = s.informResponse(conn, src, mp, sec, msg); err != nil && s.serving { + s.logf("trap: failed to send response %v: %v", src, err) + } + } +} + +func (s *TrapServer) informResponse( + conn interface{}, src net.Addr, mp messageProcessing, sec security, msg message) error { + + respPdu := NewPduWithVarBinds(msg.Version(), GetResponse, msg.Pdu().VarBinds()) + respMsg, err := mp.PrepareResponseMessage(sec, respPdu, msg) + if err != nil { + return err + } + pkt, err := respMsg.Marshal() + if err != nil { + return err + } + return s.transport.Write(conn, pkt, src) +} + +func (s *TrapServer) logf(format string, args ...interface{}) { + if l := s.ErrorLog; l != nil { + l.Printf(format, args...) + } else { + log.Printf(format, args...) + } +} + +// NewTrapServer returns a new Server and is using server arguments for configuration. +func NewTrapServer(args ServerArguments) (*TrapServer, error) { + if err := args.validate(); err != nil { + return nil, err + } + args.setDefault() + + return &TrapServer{ + args: &args, + mps: map[SNMPVersion]messageProcessing{ + V2c: newMessageProcessing(V2c), + V3: newMessageProcessing(V3), + }, + secs: map[SNMPVersion]*securityMap{ + V2c: newSecurityMap(), + V3: newSecurityMap(), + }, + transport: newTransport(&args), + }, nil +} diff --git a/vendor/github.com/k-sone/snmpgo/server_test.go b/vendor/github.com/k-sone/snmpgo/server_test.go new file mode 100644 index 0000000000..eb2985b676 --- /dev/null +++ b/vendor/github.com/k-sone/snmpgo/server_test.go @@ -0,0 +1,248 @@ +package snmpgo_test + +import ( + "net" + "reflect" + "testing" + "time" + + "github.com/k-sone/snmpgo" + "github.com/k-sone/snmpgo/snmptest" +) + +type receiveQueue struct { + msg chan *snmpgo.TrapRequest +} + +func (t *receiveQueue) OnTRAP(trap *snmpgo.TrapRequest) { + t.msg <- trap +} + +// takeNextTrap blocks till next trap is received +func (n *receiveQueue) takeNextTrap() *snmpgo.TrapRequest { + limit := time.Duration(2 * time.Second) + select { + case m := <-n.msg: + return m + case <-time.After(limit): + return nil + } +} + +func TestSendV2TrapAndReceiveIt(t *testing.T) { + trapQueue := &receiveQueue{make(chan *snmpgo.TrapRequest)} + s := snmptest.NewTrapServer("localhost:0", trapQueue) + defer s.Close() + + var varBinds snmpgo.VarBinds + oid, _ := snmpgo.NewOid("1.3.6.1.6.3.1.1.5.3") + varBinds = append(varBinds, snmpgo.NewVarBind(snmpgo.OidSnmpTrap, oid)) + + trapSender := snmptest.NewTrapSender(t, snmpgo.ListeningUDPAddress(s)) + trapSender.SendV2TrapWithBindings(true, "public", varBinds) + + trap := trapQueue.takeNextTrap() + if trap == nil { + t.Fatalf("trap is not received") + } + if trap.Error != nil { + t.Fatalf("trap has error: %v", trap.Error) + } + + pdu := trap.Pdu + if pdu.PduType() != snmpgo.SNMPTrapV2 { + t.Fatalf("expected trapv2, got: %s", pdu.PduType()) + } + + if !reflect.DeepEqual(pdu.VarBinds(), varBinds) { + t.Fatalf("expected pdu bindings %v, got %v", varBinds, pdu.VarBinds()) + } +} + +func TestCollectMultipleTraps(t *testing.T) { + trapQueue := &receiveQueue{make(chan *snmpgo.TrapRequest)} + s := snmptest.NewTrapServer("localhost:0", trapQueue) + defer s.Close() + + var varBinds snmpgo.VarBinds + oid, _ := snmpgo.NewOid("1.3.6.1.6.3.1.1.5.3") + varBinds = append(varBinds, snmpgo.NewVarBind(snmpgo.OidSnmpTrap, oid)) + + trapSender := snmptest.NewTrapSender(t, snmpgo.ListeningUDPAddress(s)) + trapSender.SendV2TrapWithBindings(true, "public", varBinds) + trapSender.SendV2TrapWithBindings(true, "public", varBinds) + trapSender.SendV2TrapWithBindings(true, "public", varBinds) + + for i := 0; i < 3; i++ { + if trapQueue.takeNextTrap() == nil { + t.Fatalf("traps are not received at %d", i+1) + } + } +} + +func TestSendInformRequestAndReceiveIt(t *testing.T) { + trapQueue := &receiveQueue{make(chan *snmpgo.TrapRequest)} + s := snmptest.NewTrapServer("localhost:0", trapQueue) + defer s.Close() + + var varBinds snmpgo.VarBinds + oid, _ := snmpgo.NewOid("1.3.6.1.6.3.1.1.5.3") + varBinds = append(varBinds, snmpgo.NewVarBind(snmpgo.OidSnmpTrap, oid)) + + trapSender := snmptest.NewTrapSender(t, snmpgo.ListeningUDPAddress(s)) + go trapSender.SendV2TrapWithBindings(false, "public", varBinds) + + trap := trapQueue.takeNextTrap() + pdu := trap.Pdu + + if pdu.PduType() != snmpgo.InformRequest { + t.Fatalf("expected inform, got: %s", pdu.PduType()) + } + + if !reflect.DeepEqual(pdu.VarBinds(), varBinds) { + t.Fatalf("expected pdu bindings %v, got %v", varBinds, pdu.VarBinds()) + } +} + +func TestSendCommunityMismatch(t *testing.T) { + trapQueue := &receiveQueue{make(chan *snmpgo.TrapRequest)} + s := snmptest.NewTrapServer("localhost:0", trapQueue) + defer s.Close() + + var varBinds snmpgo.VarBinds + oid, _ := snmpgo.NewOid("1.3.6.1.6.3.1.1.5.3") + varBinds = append(varBinds, snmpgo.NewVarBind(snmpgo.OidSnmpTrap, oid)) + + trapSender := snmptest.NewTrapSender(t, snmpgo.ListeningUDPAddress(s)) + trapSender.SendV2TrapWithBindings(true, "private", varBinds) + + trap := trapQueue.takeNextTrap() + if trap == nil { + t.Fatalf("trap is not received") + } + if trap.Error == nil { + t.Fatalf("community validation failed") + } +} + +func TestSendBrokenPacket(t *testing.T) { + trapQueue := &receiveQueue{make(chan *snmpgo.TrapRequest)} + s := snmptest.NewTrapServer("localhost:0", trapQueue) + defer s.Close() + + buf := make([]byte, 128) + conn, err := net.Dial("udp4", snmpgo.ListeningUDPAddress(s)) + if err != nil { + t.Fatalf("dial error %v", err) + } + if _, err = conn.Write(buf); err != nil { + t.Fatalf("send packet error %v", err) + } + + trap := trapQueue.takeNextTrap() + if trap == nil { + t.Fatalf("packet is not received") + } + if trap.Error == nil { + t.Fatalf("packet is not broken") + } +} + +func TestSendV3TrapAndReceiveIt(t *testing.T) { + trapQueue := &receiveQueue{make(chan *snmpgo.TrapRequest)} + s := snmptest.NewTrapServer("localhost:0", trapQueue) + defer s.Close() + + var varBinds snmpgo.VarBinds + oid, _ := snmpgo.NewOid("1.3.6.1.6.3.1.1.5.3") + varBinds = append(varBinds, snmpgo.NewVarBind(snmpgo.OidSnmpTrap, oid)) + + trapSender := snmptest.NewTrapSender(t, snmpgo.ListeningUDPAddress(s)) + trapSender.SendV3TrapWithBindings(varBinds, snmpgo.AuthPriv, "8000000004736e6d70676f", 0, 0) + + trap := trapQueue.takeNextTrap() + if trap == nil { + t.Fatalf("trap is not received") + } + if trap.Error != nil { + t.Fatalf("trap has error: %v", trap.Error) + } + + pdu := trap.Pdu + if pdu.PduType() != snmpgo.SNMPTrapV2 { + t.Fatalf("expected trapv2, got: %s", pdu.PduType()) + } + + if !reflect.DeepEqual(pdu.VarBinds(), varBinds) { + t.Fatalf("expected pdu bindings %v, got %v", varBinds, pdu.VarBinds()) + } + + trapSender.SendV3TrapWithBindings(varBinds, snmpgo.NoAuthNoPriv, "8000000004736e6d70676f5f6e6f61757468", 0, 0) + trap = trapQueue.takeNextTrap() + if trap == nil { + t.Fatalf("trap is not received") + } + if trap.Error != nil { + t.Fatalf("trap has error: %v", trap.Error) + } +} + +func TestSendV3MismatchAuthLevel(t *testing.T) { + trapQueue := &receiveQueue{make(chan *snmpgo.TrapRequest)} + s := snmptest.NewTrapServer("localhost:0", trapQueue) + defer s.Close() + + var varBinds snmpgo.VarBinds + oid, _ := snmpgo.NewOid("1.3.6.1.6.3.1.1.5.3") + varBinds = append(varBinds, snmpgo.NewVarBind(snmpgo.OidSnmpTrap, oid)) + + trapSender := snmptest.NewTrapSender(t, snmpgo.ListeningUDPAddress(s)) + trapSender.SendV3TrapWithBindings(varBinds, snmpgo.NoAuthNoPriv, "8000000004736e6d70676f", 0, 0) + + trap := trapQueue.takeNextTrap() + if trap == nil { + t.Fatalf("trap is not received") + } + if trap.Error == nil { + t.Fatalf("failed of auth level checking") + } +} + +func TestSendV3TimeWindow(t *testing.T) { + trapQueue := &receiveQueue{make(chan *snmpgo.TrapRequest)} + s := snmptest.NewTrapServer("localhost:0", trapQueue) + defer s.Close() + + var varBinds snmpgo.VarBinds + oid, _ := snmpgo.NewOid("1.3.6.1.6.3.1.1.5.3") + varBinds = append(varBinds, snmpgo.NewVarBind(snmpgo.OidSnmpTrap, oid)) + + trapSender := snmptest.NewTrapSender(t, snmpgo.ListeningUDPAddress(s)) + trapSender.SendV3TrapWithBindings(varBinds, snmpgo.AuthPriv, "8000000004736e6d70676f", 10, 1150) + + trap := trapQueue.takeNextTrap() + if trap == nil { + t.Fatal("trap is not received") + } + if trap.Error != nil { + t.Fatalf("trap has error: %v", trap.Error) + } + + trapSender.SendV3TrapWithBindings(varBinds, snmpgo.AuthPriv, "8000000004736e6d70676f", 0, 1150) + trap = trapQueue.takeNextTrap() + if trap == nil { + t.Fatal("trap is not received") + } + if trap.Error == nil { + t.Fatal("turn back the engine boots") + } + + trapSender.SendV3TrapWithBindings(varBinds, snmpgo.AuthPriv, "8000000004736e6d70676f", 10, 999) + trap = trapQueue.takeNextTrap() + if trap == nil { + t.Fatal("trap is not received") + } + if trap.Error == nil { + t.Fatal("turn back the engine time") + } +} diff --git a/vendor/github.com/k-sone/snmpgo/snmptest/sender.go b/vendor/github.com/k-sone/snmpgo/snmptest/sender.go new file mode 100644 index 0000000000..53a4efc6df --- /dev/null +++ b/vendor/github.com/k-sone/snmpgo/snmptest/sender.go @@ -0,0 +1,123 @@ +package snmptest + +import ( + "testing" + + "github.com/k-sone/snmpgo" +) + +type TrapSender struct { + t *testing.T + Address string +} + +func NewTrapSender(t *testing.T, address string) *TrapSender { + return &TrapSender{t: t, Address: address} +} + +func (t *TrapSender) SendV2TrapWithBindings(trap bool, community string, v snmpgo.VarBinds) { + snmp, err := snmpgo.NewSNMP(snmpgo.SNMPArguments{ + Version: snmpgo.V2c, + Address: t.Address, + Network: "udp4", + Retries: 1, + Community: community, + }) + if err != nil { + // Failed to create SNMP object + t.t.Fatal(err) + return + } + + if err = snmp.Open(); err != nil { + // Failed to open connection + t.t.Fatal(err) + return + } + + defer snmp.Close() + + if trap { + err = snmp.V2Trap(v) + } else { + err = snmp.InformRequest(v) + } + + if err != nil { + // Failed to request + t.t.Fatal(err) + return + } +} + +func (t *TrapSender) SendV3TrapWithBindings(v snmpgo.VarBinds, l snmpgo.SecurityLevel, eid string, eBoots, eTime int) { + snmp, err := snmpgo.NewSNMP(snmpgo.SNMPArguments{ + Version: snmpgo.V3, + Address: t.Address, + Network: "udp4", + Retries: 1, + UserName: "MyName", + SecurityLevel: l, + AuthPassword: "aaaaaaaa", + AuthProtocol: snmpgo.Sha, + PrivPassword: "bbbbbbbb", + PrivProtocol: snmpgo.Aes, + SecurityEngineId: eid, + }) + + if err != nil { + // Failed to create SNMP object + t.t.Fatal(err) + return + } + + if err = snmp.Open(); err != nil { + // Failed to open connection + t.t.Fatal(err) + return + } + + defer snmp.Close() + + err = snmp.V2TrapWithBootsTime(v, eBoots, eTime) + if err != nil { + // Failed to request + t.t.Fatal(err) + return + } +} + +// NewTrapServer creates a new Trap Server & Serve +func NewTrapServer(address string, listener snmpgo.TrapListener) *snmpgo.TrapServer { + s, _ := snmpgo.NewTrapServer(snmpgo.ServerArguments{ + LocalAddr: address, + }) + s.AddSecurity(&snmpgo.SecurityEntry{ + Version: snmpgo.V2c, + Community: "public", + }) + s.AddSecurity(&snmpgo.SecurityEntry{ + Version: snmpgo.V3, + UserName: "MyName", + SecurityLevel: snmpgo.AuthPriv, + AuthPassword: "aaaaaaaa", + AuthProtocol: snmpgo.Sha, + PrivPassword: "bbbbbbbb", + PrivProtocol: snmpgo.Aes, + SecurityEngineId: "8000000004736e6d70676f", + }) + s.AddSecurity(&snmpgo.SecurityEntry{ + Version: snmpgo.V3, + UserName: "MyName", + SecurityLevel: snmpgo.NoAuthNoPriv, + SecurityEngineId: "8000000004736e6d70676f5f6e6f61757468", + }) + + ch := make(chan struct{}, 0) + go func() { + ch <- struct{}{} + s.Serve(listener) + }() + <-ch + return s +} diff --git a/vendor/github.com/k-sone/snmpgo/transport.go b/vendor/github.com/k-sone/snmpgo/transport.go new file mode 100644 index 0000000000..79930022b1 --- /dev/null +++ b/vendor/github.com/k-sone/snmpgo/transport.go @@ -0,0 +1,101 @@ +package snmpgo + +import ( + "net" + "sync" + "time" +) + +type transport interface { + Listen() (interface{}, error) + Read(interface{}, []byte) (int, net.Addr, message, error) + Write(interface{}, []byte, net.Addr) error + Close(interface{}) error +} + +type packetTransport struct { + conn net.PacketConn + lock *sync.Mutex + anchor chan struct{} + network string + localAddr string + writeTimeout time.Duration +} + +func (t *packetTransport) Listen() (interface{}, error) { + t.lock.Lock() + c := t.conn + t.lock.Unlock() + if c != nil { + <-t.anchor + return nil, nil + } + + c, err := net.ListenPacket(t.network, t.localAddr) + t.lock.Lock() + t.conn = c + t.lock.Unlock() + return c, err +} + +func (t *packetTransport) Read(conn interface{}, buf []byte) (num int, src net.Addr, msg message, err error) { + c := conn.(net.PacketConn) + for { + num, src, err = c.ReadFrom(buf) + if err != nil { + if e, ok := err.(net.Error); ok && e.Temporary() { + continue + } + return + } + + pkt := make([]byte, num) + copy(pkt, buf) + msg, _, err = unmarshalMessage(pkt) + return + } +} + +func (t *packetTransport) Write(conn interface{}, pkt []byte, dst net.Addr) error { + c := conn.(net.PacketConn) + if err := c.SetWriteDeadline(time.Now().Add(t.writeTimeout)); err != nil { + return err + } + + for { + if _, err := c.WriteTo(pkt, dst); err != nil { + if e, ok := err.(net.Error); ok && e.Temporary() && !e.Timeout() { + continue + } + return err + } + return nil + } +} + +func (t *packetTransport) Close(_ interface{}) error { + t.lock.Lock() + defer t.lock.Unlock() + + if c := t.conn; c != nil { + t.conn = nil + t.anchor <- struct{}{} + return c.Close() + } + return nil +} + +func newTransport(args *ServerArguments) transport { + switch args.Network { + case "udp", "udp4", "udp6": + return &packetTransport{ + lock: new(sync.Mutex), + anchor: make(chan struct{}, 0), + network: args.Network, + localAddr: args.LocalAddr, + writeTimeout: args.WriteTimeout, + } + default: + return nil + } +} diff --git a/vendor/github.com/k-sone/snmpgo/util.go b/vendor/github.com/k-sone/snmpgo/util.go new file mode 100644 index 0000000000..d25ac0a7dc --- /dev/null +++ b/vendor/github.com/k-sone/snmpgo/util.go @@ -0,0 +1,136 @@ +package snmpgo + +import ( + "bytes" + "encoding/hex" + "encoding/json" + "fmt" + "math" + "math/rand" + "net" + "regexp" + "strings" + "sync" + "time" +) + +var random *rand.Rand +var randOnce sync.Once + +func initRandom() { + random = rand.New(rand.NewSource(time.Now().UnixNano())) +} + +var reqMutex sync.Mutex + +func genRequestId() int { + randOnce.Do(initRandom) + reqMutex.Lock() + val := int(random.Int31()) + reqMutex.Unlock() + return val +} + +func genSalt32() int32 { + randOnce.Do(initRandom) + reqMutex.Lock() + val := random.Int31() + reqMutex.Unlock() + return val +} + +func genSalt64() int64 { + randOnce.Do(initRandom) + reqMutex.Lock() + val := random.Int63() + reqMutex.Unlock() + return val +} + +var mesId int = math.MaxInt32 - 1 +var mesMutex sync.Mutex + +func genMessageId() (id int) { + randOnce.Do(initRandom) + mesMutex.Lock() + mesId++ + if mesId == math.MaxInt32 { + reqMutex.Lock() + mesId = int(random.Int31()) + reqMutex.Unlock() + } + id = mesId + mesMutex.Unlock() + return +} + +func retry(retries int, f func() error) (err error) { + for i := 0; i <= retries; i++ { + err = f() + switch e := err.(type) { + case net.Error: + if e.Timeout() { + continue + } + case *notInTimeWindowError: + err = e.error + continue + } + return + } + return +} + +func confirmedType(t PduType) bool { + if t == GetRequest || t == GetNextRequest || t == SetRequest || + t == GetBulkRequest || t == InformRequest { + return true + } + return false +} + +func engineIdToBytes(engineId string) ([]byte, error) { + b, err := hex.DecodeString(engineId) + if l := len(b); err != nil || (l < 5 || l > 32) { + return nil, &ArgumentError{ + Value: engineId, + Message: "EngineId must be a hexadecimal string and length is range 5..32", + } + } + return b, nil +} + +var hexPrefix *regexp.Regexp = regexp.MustCompile(`^0[xX]`) + +func stripHexPrefix(s string) string { + return hexPrefix.ReplaceAllString(s, "") +} + +func toHexStr(a []byte, sep string) string { + s := make([]string, len(a)) + for i, b := range a { + s[i] = fmt.Sprintf("%02x", b) + } + return strings.Join(s, sep) +} + +func escape(s interface{}) string { + r, _ := json.Marshal(s) + return string(r) +} + +func xor(a, b []byte) []byte { + c := make([]byte, len(a)) + for i := 0; i < len(a); i++ { + c[i] = a[i] ^ b[i] + } + return c +} + +func padding(b []byte, size int) []byte { + pad := size - (len(b) % size) + if pad > 0 { + b = append(b, bytes.Repeat([]byte{0x00}, pad)...) + } + return b +} diff --git a/vendor/github.com/k-sone/snmpgo/util_test.go b/vendor/github.com/k-sone/snmpgo/util_test.go new file mode 100644 index 0000000000..1d5a12a3b1 --- /dev/null +++ b/vendor/github.com/k-sone/snmpgo/util_test.go @@ -0,0 +1,83 @@ +package snmpgo_test + +import ( + "fmt" + "runtime" + "sync" + "testing" + + "github.com/k-sone/snmpgo" +) + +func TestStripHexPrefix(t *testing.T) { + expStr := "1234567890abcdef" + + str := expStr + if expStr != snmpgo.StripHexPrefix(str) { + t.Errorf("stripHexPrefix() - expected [%s], actual[%s]", expStr, str) + } + + str = "0x" + expStr + if expStr != snmpgo.StripHexPrefix(str) { + t.Errorf("stripHexPrefix() - expected [%s], actual[%s]", expStr, str) + } + + str = "0X" + expStr + if expStr != snmpgo.StripHexPrefix(str) { + t.Errorf("stripHexPrefix() - expected [%s], actual[%s]", expStr, str) + } +} + +func TestRetry(t *testing.T) { + count := 0 + f := func() error { + count += 1 + return nil + } + if err := snmpgo.Retry(5, f); err != nil || count != 1 { + t.Errorf("retry() - normal: err=%s, count=%d", err, count) + } + + count = 0 + f = func() error { + count += 1 + return fmt.Errorf("error") + } + if err := snmpgo.Retry(5, f); err == nil || count != 1 { + t.Errorf("retry() - not retry: err=%s, count=%d", err, count) + } + + count = 0 + f = func() error { + count += 1 + return snmpgo.NewNotInTimeWindowError() + } + if err := snmpgo.Retry(5, f); err == nil || count != 6 { + t.Errorf("retry() - error: err=%s, count=%d", err, count) + } +} + +func TestRandomRace(t *testing.T) { + if c := runtime.GOMAXPROCS(-1); c < 2 { + runtime.GOMAXPROCS(runtime.NumCPU() * 2) + defer func() { runtime.GOMAXPROCS(c) }() + } + + var wg sync.WaitGroup + ch := make(chan struct{}, 0) + for i := 0; i < runtime.GOMAXPROCS(-1); i++ { + wg.Add(1) + go func() { + defer wg.Done() + <-ch + for i := 0; i < 3000; i++ { + snmpgo.GenRequestId() + snmpgo.GenSalt32() + snmpgo.GenSalt64() + snmpgo.GenMessageId() + } + }() + } + close(ch) + wg.Wait() +} diff --git a/vendor/github.com/k-sone/snmpgo/variables.go b/vendor/github.com/k-sone/snmpgo/variables.go new file mode 100644 index 0000000000..306b2e64c5 --- /dev/null +++ b/vendor/github.com/k-sone/snmpgo/variables.go @@ -0,0 +1,691 @@ +package snmpgo + +import ( + "bytes" + "encoding/asn1" + "fmt" + "math" + "math/big" + "sort" + "strconv" + "strings" +) + +type Variable interface { + // Return a BigInt representation of this Variable if such a representation exists + BigInt() (*big.Int, error) + // Return a string representation of this Variable + String() string + // Return a string of type + Type() string + Marshal() ([]byte, error) + Unmarshal([]byte) (rest []byte, err error) +} + +type Integer struct { + Value int32 +} + +func (v *Integer) BigInt() (*big.Int, error) { + return big.NewInt(int64(v.Value)), nil +} + +func (v *Integer) String() string { + return strconv.FormatInt(int64(v.Value), 10) +} + +func (v *Integer) Type() string { + return "Integer" +} + +func (v *Integer) Marshal() ([]byte, error) { + return asn1.Marshal(v.Value) +} + +func (v *Integer) Unmarshal(b []byte) (rest []byte, err error) { + return asn1.Unmarshal(b, &v.Value) +} + +func NewInteger(i int32) *Integer { + return &Integer{i} +} + +type OctetString struct { + Value []byte +} + +func (v *OctetString) BigInt() (*big.Int, error) { + return nil, UnsupportedOperation +} + +func (v *OctetString) String() string { + for _, c := range v.Value { + if !strconv.IsPrint(rune(c)) { + return toHexStr(v.Value, ":") + } + } + return string(v.Value) +} + +func (v *OctetString) Type() string { + return "OctetString" +} + +func (v *OctetString) Marshal() ([]byte, error) { + return asn1.Marshal(v.Value) +} + +func (v *OctetString) Unmarshal(b []byte) (rest []byte, err error) { + return unmarshalString(b, tagOctetString, func(s []byte) { v.Value = s }) +} + +func NewOctetString(b []byte) *OctetString { + return &OctetString{b} +} + +type Null struct{} + +func (v *Null) BigInt() (*big.Int, error) { + return nil, UnsupportedOperation +} + +func (v *Null) String() string { + return "" +} + +func (v *Null) Type() string { + return "Null" +} + +func (v *Null) Marshal() ([]byte, error) { + return []byte{tagNull, 0}, nil +} + +func (v *Null) Unmarshal(b []byte) (rest []byte, err error) { + return unmarshalEmpty(b, tagNull) +} + +func NewNull() *Null { + return &Null{} +} + +type Oid struct { + Value asn1.ObjectIdentifier +} + +func (v *Oid) BigInt() (*big.Int, error) { + return nil, UnsupportedOperation +} + +func (v *Oid) String() string { + return v.Value.String() +} + +func (v *Oid) Type() string { + return "Oid" +} + +func (v *Oid) Marshal() ([]byte, error) { + return asn1.Marshal(v.Value) +} + +func (v *Oid) Unmarshal(b []byte) (rest []byte, err error) { + var i asn1.ObjectIdentifier + rest, err = asn1.Unmarshal(b, &i) + if err == nil { + v.Value = i + } + return +} + +// Returns true if this OID contains the specified OID +func (v *Oid) Contains(o *Oid) bool { + if o == nil || len(v.Value) < len(o.Value) { + return false + } + for i := 0; i < len(o.Value); i++ { + if v.Value[i] != o.Value[i] { + return false + } + } + return true +} + +// Returns 0 this OID is equal to the specified OID, +// -1 this OID is lexicographically less than the specified OID, +// 1 this OID is lexicographically greater than the specified OID +func (v *Oid) Compare(o *Oid) int { + if o != nil { + vl := len(v.Value) + ol := len(o.Value) + for i := 0; i < vl; i++ { + if ol <= i || v.Value[i] > o.Value[i] { + return 1 + } else if v.Value[i] < o.Value[i] { + return -1 + } + } + if ol == vl { + return 0 + } + } + return -1 +} + +// Returns true if this OID is same the specified OID +func (v *Oid) Equal(o *Oid) bool { + if o == nil { + return false + } + return v.Value.Equal(o.Value) +} + +// Returns Oid with additional sub-ids +func (v *Oid) AppendSubIds(subs []int) (*Oid, error) { + buf := bytes.NewBufferString(v.String()) + for _, i := range subs { + buf.WriteString(".") + buf.WriteString(strconv.Itoa(i)) + } + return NewOid(buf.String()) +} + +func NewOid(s string) (oid *Oid, err error) { + subids := strings.Split(s, ".") + + // leading dot + if subids[0] == "" { + subids = subids[1:] + } + + // RFC2578 Section 3.5 + if len(subids) > 128 { + return nil, &ArgumentError{ + Value: s, + Message: "The sub-identifiers in an OID is up to 128", + } + } + + o := make(asn1.ObjectIdentifier, len(subids)) + for i, v := range subids { + o[i], err = strconv.Atoi(v) + if err != nil || o[i] < 0 || int64(o[i]) > math.MaxUint32 { + return nil, &ArgumentError{ + Value: s, + Message: fmt.Sprintf("The sub-identifiers is range %d..%d", 0, int64(math.MaxUint32)), + } + } + } + + if len(o) > 0 && o[0] > 2 { + return nil, &ArgumentError{ + Value: s, + Message: "The first sub-identifier is range 0..2", + } + } + + // ISO/IEC 8825 Section 8.19.4 + if len(o) < 2 { + return nil, &ArgumentError{ + Value: s, + Message: "The first and second sub-identifier is required", + } + } + + if o[0] < 2 && o[1] >= 40 { + return nil, &ArgumentError{ + Value: s, + Message: "The second sub-identifier is range 0..39", + } + } + + return &Oid{o}, nil +} + +// MustNewOid is like NewOid but panics if argument cannot be parsed +func MustNewOid(s string) *Oid { + if oid, err := NewOid(s); err != nil { + panic(`snmpgo.MustNewOid: ` + err.Error()) + } else { + return oid + } +} + +type Oids []*Oid + +// Sort a Oid list +func (o Oids) Sort() Oids { + c := make(Oids, len(o)) + copy(c, o) + sort.Sort(sortableOids{c}) + return c +} + +func (o Oids) uniq(comp func(a, b *Oid) bool) Oids { + var before *Oid + c := make(Oids, 0, len(o)) + for _, oid := range o { + if !comp(before, oid) { + before = oid + c = append(c, oid) + } + } + return c +} + +// Filter out adjacent OID list +func (o Oids) Uniq() Oids { + return o.uniq(func(a, b *Oid) bool { + if b == nil { + return a == nil + } else { + return b.Equal(a) + } + }) +} + +// Filter out adjacent OID list with the same prefix +func (o Oids) UniqBase() Oids { + return o.uniq(func(a, b *Oid) bool { + if b == nil { + return a == nil + } else { + return b.Contains(a) + } + }) +} + +type sortableOids struct { + Oids +} + +func (o sortableOids) Len() int { + return len(o.Oids) +} + +func (o sortableOids) Swap(i, j int) { + o.Oids[i], o.Oids[j] = o.Oids[j], o.Oids[i] +} + +func (o sortableOids) Less(i, j int) bool { + return o.Oids[i] != nil && o.Oids[i].Compare(o.Oids[j]) < 1 +} + +func NewOids(s []string) (oids Oids, err error) { + for _, l := range s { + o, e := NewOid(l) + if e != nil { + return nil, e + } + oids = append(oids, o) + } + return +} + +type Ipaddress struct { + OctetString +} + +func (v *Ipaddress) BigInt() (*big.Int, error) { + var t uint32 + for i, b := range v.Value { + t = t + (uint32(b) << uint(24-8*i)) + } + return big.NewInt(int64(t)), nil +} + +func (v *Ipaddress) String() string { + s := make([]string, len(v.Value)) + for i, b := range v.Value { + s[i] = strconv.Itoa(int(b)) + } + return strings.Join(s, ".") +} + +func (v *Ipaddress) Type() string { + return "Ipaddress" +} + +func (v *Ipaddress) Marshal() ([]byte, error) { + b, err := asn1.Marshal(v.Value) + if err == nil { + b[0] = tagIpaddress + } + return b, err +} + +func (v *Ipaddress) Unmarshal(b []byte) (rest []byte, err error) { + return unmarshalString(b, tagIpaddress, func(s []byte) { v.Value = s }) +} + +func NewIpaddress(a, b, c, d byte) *Ipaddress { + return &Ipaddress{OctetString{[]byte{a, b, c, d}}} +} + +type Counter32 struct { + Value uint32 +} + +func (v *Counter32) BigInt() (*big.Int, error) { + return big.NewInt(int64(v.Value)), nil +} + +func (v *Counter32) String() string { + return strconv.FormatInt(int64(v.Value), 10) +} + +func (v *Counter32) Type() string { + return "Counter32" +} + +func (v *Counter32) Marshal() ([]byte, error) { + b, err := asn1.Marshal(int64(v.Value)) + if err == nil { + b[0] = tagCounter32 + } + return b, err +} + +func (v *Counter32) Unmarshal(b []byte) (rest []byte, err error) { + return unmarshalInt(b, tagCounter32, func(s *big.Int) { v.Value = uint32(s.Int64()) }) +} + +func NewCounter32(i uint32) *Counter32 { + return &Counter32{i} +} + +type Gauge32 struct { + Counter32 +} + +func (v *Gauge32) Type() string { + return "Gauge32" +} + +func (v *Gauge32) Marshal() ([]byte, error) { + b, err := asn1.Marshal(int64(v.Value)) + if err == nil { + b[0] = tagGauge32 + } + return b, err +} + +func (v *Gauge32) Unmarshal(b []byte) (rest []byte, err error) { + return unmarshalInt(b, tagGauge32, func(s *big.Int) { v.Value = uint32(s.Int64()) }) +} + +func NewGauge32(i uint32) *Gauge32 { + return &Gauge32{Counter32{i}} +} + +type TimeTicks struct { + Counter32 +} + +func (v *TimeTicks) Type() string { + return "TimeTicks" +} + +func (v *TimeTicks) Marshal() ([]byte, error) { + b, err := asn1.Marshal(int64(v.Value)) + if err == nil { + b[0] = tagTimeTicks + } + return b, err +} + +func (v *TimeTicks) Unmarshal(b []byte) (rest []byte, err error) { + return unmarshalInt(b, tagTimeTicks, func(s *big.Int) { v.Value = uint32(s.Int64()) }) +} + +func NewTimeTicks(i uint32) *TimeTicks { + return &TimeTicks{Counter32{i}} +} + +type Opaque struct { + OctetString +} + +func (v *Opaque) String() string { + return toHexStr(v.Value, ":") +} + +func (v *Opaque) Type() string { + return "Opaque" +} + +func (v *Opaque) Marshal() ([]byte, error) { + b, err := asn1.Marshal(v.Value) + if err == nil { + b[0] = tagOpaque + } + return b, err +} + +func (v *Opaque) Unmarshal(b []byte) (rest []byte, err error) { + return unmarshalString(b, tagOpaque, func(s []byte) { v.Value = s }) +} + +func NewOpaque(b []byte) *Opaque { + return &Opaque{OctetString{b}} +} + +type Counter64 struct { + Value uint64 +} + +func (v *Counter64) BigInt() (*big.Int, error) { + return big.NewInt(0).SetUint64(v.Value), nil +} + +func (v *Counter64) String() string { + return strconv.FormatUint(v.Value, 10) +} + +func (v *Counter64) Type() string { + return "Counter64" +} + +func (v *Counter64) Marshal() ([]byte, error) { + i := big.NewInt(0).SetUint64(v.Value) + b, err := asn1.Marshal(i) + if err == nil { + b[0] = tagCounter64 + } + return b, err +} + +func (v *Counter64) Unmarshal(b []byte) (rest []byte, err error) { + return unmarshalInt(b, tagCounter64, func(s *big.Int) { v.Value = s.Uint64() }) +} + +func NewCounter64(i uint64) *Counter64 { + return &Counter64{i} +} + +type NoSucheObject struct { + Null +} + +func (v *NoSucheObject) Type() string { + return "NoSucheObject" +} + +func (v *NoSucheObject) Marshal() ([]byte, error) { + return []byte{tagNoSucheObject, 0}, nil +} + +func (v *NoSucheObject) Unmarshal(b []byte) (rest []byte, err error) { + return unmarshalEmpty(b, tagNoSucheObject) +} + +func NewNoSucheObject() *NoSucheObject { + return &NoSucheObject{Null{}} +} + +type NoSucheInstance struct { + Null +} + +func (v *NoSucheInstance) Type() string { + return "NoSucheInstance" +} + +func (v *NoSucheInstance) Marshal() ([]byte, error) { + return []byte{tagNoSucheInstance, 0}, nil +} + +func (v *NoSucheInstance) Unmarshal(b []byte) (rest []byte, err error) { + return unmarshalEmpty(b, tagNoSucheInstance) +} + +func NewNoSucheInstance() *NoSucheInstance { + return &NoSucheInstance{Null{}} +} + +type EndOfMibView struct { + Null +} + +func (v *EndOfMibView) Type() string { + return "EndOfMibView" +} + +func (v *EndOfMibView) Marshal() ([]byte, error) { + return []byte{tagEndOfMibView, 0}, nil +} + +func (v *EndOfMibView) Unmarshal(b []byte) (rest []byte, err error) { + return unmarshalEmpty(b, tagEndOfMibView) +} + +func NewEndOfMibView() *EndOfMibView { + return &EndOfMibView{Null{}} +} + +func unmarshalVariable(b []byte) (v Variable, rest []byte, err error) { + var raw asn1.RawValue + rest, err = asn1.Unmarshal(b, &raw) + if err != nil { + return + } + + switch raw.Class { + case classUniversal: + switch raw.Tag { + case tagInteger: + var u Integer + v = &u + case tagOctetString: + var u OctetString + v = &u + case tagNull: + var u Null + v = &u + case tagObjectIdentifier: + var u Oid + v = &u + } + case classApplication: + switch raw.Tag { + case tagIpaddress & tagMask: + var u Ipaddress + v = &u + case tagCounter32 & tagMask: + var u Counter32 + v = &u + case tagGauge32 & tagMask: + var u Gauge32 + v = &u + case tagTimeTicks & tagMask: + var u TimeTicks + v = &u + case tagOpaque & tagMask: + var u Opaque + v = &u + case tagCounter64 & tagMask: + var u Counter64 + v = &u + } + case classContextSpecific: + switch raw.Tag { + case tagNoSucheObject & tagMask: + var u NoSucheObject + v = &u + case tagNoSucheInstance & tagMask: + var u NoSucheInstance + v = &u + case tagEndOfMibView & tagMask: + var u EndOfMibView + v = &u + } + } + + if v != nil { + rest, err = v.Unmarshal(b) + if err == nil { + return + } + } else { + err = asn1.StructuralError{fmt.Sprintf( + "Unknown ASN.1 object : %s", toHexStr(b, " "))} + } + + return nil, nil, err +} + +func validateUnmarshal(b []byte, tag byte) error { + if len(b) < 1 { + return asn1.StructuralError{"No bytes"} + } + if b[0] != tag { + return asn1.StructuralError{fmt.Sprintf( + "Invalid ASN.1 object - expected [%02x], actual [%02x] : %s", + tag, b[0], toHexStr(b, " "))} + } + return nil +} + +func unmarshalEmpty(b []byte, tag byte) (rest []byte, err error) { + err = validateUnmarshal(b, tag) + if err != nil { + return nil, err + } + + var raw asn1.RawValue + return asn1.Unmarshal(b, &raw) +} + +func unmarshalInt(b []byte, tag byte, setter func(*big.Int)) (rest []byte, err error) { + err = validateUnmarshal(b, tag) + if err != nil { + return nil, err + } + + temp := b[0] + b[0] = tagInteger + var i *big.Int + rest, err = asn1.Unmarshal(b, &i) + if err == nil { + setter(i) + } + b[0] = temp + return +} + +func unmarshalString(b []byte, tag byte, setter func([]byte)) (rest []byte, err error) { + err = validateUnmarshal(b, tag) + if err != nil { + return nil, err + } + + temp := b[0] + b[0] = tagOctetString + var s []byte + rest, err = asn1.Unmarshal(b, &s) + if err == nil { + setter(s) + } + b[0] = temp + return +} diff --git a/vendor/github.com/k-sone/snmpgo/variables_test.go b/vendor/github.com/k-sone/snmpgo/variables_test.go new file mode 100644 index 0000000000..a6a40b9a6c --- /dev/null +++ b/vendor/github.com/k-sone/snmpgo/variables_test.go @@ -0,0 +1,722 @@ +package snmpgo_test + +import ( + "bytes" + "strconv" + "strings" + "testing" + + "github.com/k-sone/snmpgo" +) + +func TestInteger(t *testing.T) { + expInt := int64(2147483647) + expStr := "2147483647" + expBuf := []byte{0x02, 0x04, 0x7f, 0xff, 0xff, 0xff} + var v snmpgo.Variable = snmpgo.NewInteger(int32(expInt)) + + big, err := v.BigInt() + if err != nil { + t.Errorf("Failed to call BigInt(): %v", err) + } + if expInt != big.Int64() { + t.Errorf("BigInt() - expected [%d], actual [%d]", expInt, big.Int64()) + } + + if expStr != v.String() { + t.Errorf("String() - expected [%s], actual [%s]", expStr, v.String()) + } + + buf, err := v.Marshal() + if err != nil { + t.Errorf("Marshal(): %v", err) + } + if !bytes.Equal(expBuf, buf) { + t.Errorf("Marshal() - expected [%s], actual [%s]", + snmpgo.ToHexStr(expBuf, " "), snmpgo.ToHexStr(buf, " ")) + } + + var w snmpgo.Integer + rest, err := (&w).Unmarshal(buf) + if len(rest) != 0 || err != nil { + t.Errorf("Unmarshal() - len[%d] err[%v]", len(rest), err) + } + if expStr != w.String() { + t.Errorf("Unmarshal() - expected [%s], actual [%s]", expStr, w.String()) + } + + buf = append(buf, 0x00) + rest, err = (&w).Unmarshal(buf) + if len(rest) != 1 || err != nil { + t.Errorf("Unmarshal() with rest - len[%d] err[%v]", len(rest), err) + } + if expStr != w.String() { + t.Errorf("Unmarshal() with rest - expected [%s], actual [%s]", expStr, w.String()) + } +} + +func TestOctetString(t *testing.T) { + expStr := "Test" + expBuf := []byte{0x04, 0x04, 0x54, 0x65, 0x73, 0x74} + var v snmpgo.Variable = snmpgo.NewOctetString([]byte(expStr)) + + _, err := v.BigInt() + if err == nil { + t.Errorf("Failed to call BigInt()") + } + + if expStr != v.String() { + t.Errorf("String() - expected [%s], actual[%s]", expStr, v.String()) + } + + buf, err := v.Marshal() + if err != nil { + t.Errorf("Marshal(): %v", err) + } + if !bytes.Equal(expBuf, buf) { + t.Errorf("Marshal() - expected [%s], actual [%s]", + snmpgo.ToHexStr(expBuf, " "), snmpgo.ToHexStr(buf, " ")) + } + + var w snmpgo.OctetString + rest, err := (&w).Unmarshal(buf) + if len(rest) != 0 || err != nil { + t.Errorf("Unmarshal() - len[%d] err[%v]", len(rest), err) + } + if expStr != w.String() { + t.Errorf("Unmarshal() - expected [%s], actual [%s]", expStr, w.String()) + } + + buf = append(buf, 0x00) + rest, err = (&w).Unmarshal(buf) + if len(rest) != 1 || err != nil { + t.Errorf("Unmarshal() with rest - len[%d] err[%v]", len(rest), err) + } + if expStr != w.String() { + t.Errorf("Unmarshal() with rest - expected [%s], actual [%s]", expStr, w.String()) + } +} + +func TestNull(t *testing.T) { + expStr := "" + expBuf := []byte{0x05, 0x00} + var v snmpgo.Variable = snmpgo.NewNull() + + _, err := v.BigInt() + if err == nil { + t.Errorf("Failed to call BigInt()") + } + + if expStr != v.String() { + t.Errorf("String() - expected [%s], actual[%s]", expStr, v.String()) + } + + buf, err := v.Marshal() + if err != nil { + t.Errorf("Marshal(): %v", err) + } + if !bytes.Equal(expBuf, buf) { + t.Errorf("Marshal() - expected [%s], actual [%s]", + snmpgo.ToHexStr(expBuf, " "), snmpgo.ToHexStr(buf, " ")) + } + + var w snmpgo.Null + rest, err := (&w).Unmarshal(buf) + if len(rest) != 0 || err != nil { + t.Errorf("Unmarshal() - len[%d] err[%v]", len(rest), err) + } + if expStr != w.String() { + t.Errorf("Unmarshal() - expected [%s], actual [%s]", expStr, w.String()) + } + + buf = append(buf, 0x00) + rest, err = (&w).Unmarshal(buf) + if len(rest) != 1 || err != nil { + t.Errorf("Unmarshal() with rest - len[%d] err[%v]", len(rest), err) + } + if expStr != w.String() { + t.Errorf("Unmarshal() with rest - expected [%s], actual [%s]", expStr, w.String()) + } +} + +func TestOid(t *testing.T) { + expStr := "1.3.6.1.2.1.1.1.0" + expBuf := []byte{0x06, 0x08, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x01, 0x01, 0x00} + var v snmpgo.Variable + + v, err := snmpgo.NewOid(expStr) + if err != nil { + t.Errorf("NewOid : %v", err) + } + + _, err = v.BigInt() + if err == nil { + t.Errorf("Failed to call BigInt()") + } + + if expStr != v.String() { + t.Errorf("String() - expected [%s], actual[%s]", expStr, v.String()) + } + + buf, err := v.Marshal() + if err != nil { + t.Errorf("Marshal(): %v", err) + } + if !bytes.Equal(expBuf, buf) { + t.Errorf("Marshal() - expected [%s], actual [%s]", + snmpgo.ToHexStr(expBuf, " "), snmpgo.ToHexStr(buf, " ")) + } + + var w snmpgo.Oid + rest, err := (&w).Unmarshal(buf) + if len(rest) != 0 || err != nil { + t.Errorf("Unmarshal() - len[%d] err[%v]", len(rest), err) + } + if expStr != w.String() { + t.Errorf("Unmarshal() - expected [%s], actual [%s]", expStr, w.String()) + } + + buf = append(buf, 0x00) + rest, err = (&w).Unmarshal(buf) + if len(rest) != 1 || err != nil { + t.Errorf("Unmarshal() with rest - len[%d] err[%v]", len(rest), err) + } + if expStr != w.String() { + t.Errorf("Unmarshal() with rest - expected [%s], actual [%s]", expStr, w.String()) + } +} + +func TestOidOperation(t *testing.T) { + oid, _ := snmpgo.NewOid("1.2.3.4.5.6.7") + + oids, _ := snmpgo.NewOids([]string{"1.2.3.4", "1.2.3.4.5.6.7", + "1.2.3.4.5.6.7.8", "1.1.3.4", "1.3.3.4"}) + + if !oid.Contains(oids[0]) || !oid.Contains(oids[1]) || oid.Contains(oids[2]) || + oid.Contains(oids[3]) || oid.Contains(oids[4]) { + t.Errorf("Failed to Contains()") + } + + if oid.Compare(oids[0]) != 1 || oid.Compare(oids[1]) != 0 || oid.Compare(oids[2]) != -1 || + oid.Compare(oids[3]) != 1 || oid.Compare(oids[4]) != -1 { + t.Errorf("Failed to Compare()") + } + + if oid.Equal(oids[0]) || !oid.Equal(oids[1]) || oid.Equal(oids[2]) || + oid.Equal(oids[3]) || oid.Equal(oids[4]) { + t.Errorf("Failed to Contains()") + } + + oid, _ = oid.AppendSubIds([]int{8, 9, 10}) + if oid.String() != "1.2.3.4.5.6.7.8.9.10" { + t.Errorf("Failed to AppendSubIds()") + } +} + +func TestNewOid(t *testing.T) { + expStr := ".1.3.6.1.2.1.1.1.0" + var v snmpgo.Variable + + v, err := snmpgo.NewOid(expStr) + if err != nil { + t.Errorf("NewOid : %v", err) + } + + if expStr[1:] != v.String() { + t.Errorf("String() - expected [%s], actual[%s]", expStr[1:], v.String()) + } + + var s []string + for i := 0; i <= 128; i++ { + s = append(s, strconv.Itoa(i)) + } + expStr = strings.Join(s, ".") + v, err = snmpgo.NewOid(expStr) + if err == nil { + t.Errorf("NewOid sub-identifiers size") + } + + expStr = "1.3.6.1.2.1.-1.0" + v, err = snmpgo.NewOid(expStr) + if err == nil { + t.Errorf("NewOid sub-identifier range") + } + + expStr = "1.3.6.1.2.1.4294967296.0" + v, err = snmpgo.NewOid(expStr) + if err == nil { + t.Errorf("NewOid sub-identifier range") + } + + expStr = "3.3.6.1.2.1.1.1.0" + v, err = snmpgo.NewOid(expStr) + if err == nil { + t.Errorf("NewOid first sub-identifier range") + } + + expStr = "1" + v, err = snmpgo.NewOid(expStr) + if err == nil { + t.Errorf("NewOid sub-identifiers size") + } + + expStr = "1.40.6.1.2.1.1.1.0" + v, err = snmpgo.NewOid(expStr) + if err == nil { + t.Errorf("NewOid first sub-identifier range") + } +} + +func TestOids(t *testing.T) { + oids, _ := snmpgo.NewOids([]string{ + "1.3.6.1.2.1.1.2.0", + "1.3.6.1.2.1.1.1.0", + "1.3.6.1.2.1.1.3.0", + "1.3.6.1.2.1.1", + "1.3.6.1.2.1.1.1.0", + }) + + expOids, _ := snmpgo.NewOids([]string{ + "1.3.6.1.2.1.1", + "1.3.6.1.2.1.1.1.0", + "1.3.6.1.2.1.1.1.0", + "1.3.6.1.2.1.1.2.0", + "1.3.6.1.2.1.1.3.0", + }) + oids = oids.Sort() + if len(expOids) != len(oids) { + t.Errorf("Sort() - expected [%d], actual [%d]", len(expOids), len(oids)) + } + for i, o := range expOids { + if !o.Equal(oids[i]) { + t.Errorf("Sort() - expected [%s], actual [%s]", o, oids[i]) + } + } + + expOids, _ = snmpgo.NewOids([]string{ + "1.3.6.1.2.1.1", + "1.3.6.1.2.1.1.1.0", + "1.3.6.1.2.1.1.2.0", + "1.3.6.1.2.1.1.3.0", + }) + oids = oids.Sort().Uniq() + if len(expOids) != len(oids) { + t.Errorf("Uniq() - expected [%d], actual [%d]", len(expOids), len(oids)) + } + for i, o := range expOids { + if !o.Equal(oids[i]) { + t.Errorf("Uniq() - expected [%s], actual [%s]", o, oids[i]) + } + } + + expOids, _ = snmpgo.NewOids([]string{ + "1.3.6.1.2.1.1", + }) + oids = oids.Sort().UniqBase() + if len(expOids) != len(oids) { + t.Errorf("Uniq() - expected [%d], actual [%d]", len(expOids), len(oids)) + } + for i, o := range expOids { + if !o.Equal(oids[i]) { + t.Errorf("Uniq() - expected [%s], actual [%s]", o, oids[i]) + } + } +} + +func TestIpaddress(t *testing.T) { + expStr := "192.168.1.1" + expInt := int64(3232235777) + expBuf := []byte{0x40, 0x04, 0xc0, 0xa8, 0x01, 0x01} + var v snmpgo.Variable = snmpgo.NewIpaddress(0xc0, 0xa8, 0x01, 0x01) + + big, err := v.BigInt() + if err != nil { + t.Errorf("Failed to call BigInt(): %v", err) + } + if expInt != big.Int64() { + t.Errorf("BigInt() - expected [%d], actual [%d]", expInt, big.Int64()) + } + + if expStr != v.String() { + t.Errorf("String() - expected [%s], actual[%s]", expStr, v.String()) + } + + buf, err := v.Marshal() + if err != nil { + t.Errorf("Marshal(): %v", err) + } + if !bytes.Equal(expBuf, buf) { + t.Errorf("Marshal() - expected [%s], actual [%s]", + snmpgo.ToHexStr(expBuf, " "), snmpgo.ToHexStr(buf, " ")) + } + + var w snmpgo.Ipaddress + rest, err := (&w).Unmarshal(buf) + if len(rest) != 0 || err != nil { + t.Errorf("Unmarshal() - len[%d] err[%v]", len(rest), err) + } + if expStr != w.String() { + t.Errorf("Unmarshal() - expected [%s], actual [%s]", expStr, w.String()) + } + + buf = append(buf, 0x00) + rest, err = (&w).Unmarshal(buf) + if len(rest) != 1 || err != nil { + t.Errorf("Unmarshal() with rest - len[%d] err[%v]", len(rest), err) + } + if expStr != w.String() { + t.Errorf("Unmarshal() with rest - expected [%s], actual [%s]", expStr, w.String()) + } +} + +func TestCounter32(t *testing.T) { + expInt := int64(4294967295) + expStr := "4294967295" + expBuf := []byte{0x41, 0x05, 0x00, 0xff, 0xff, 0xff, 0xff} + var v snmpgo.Variable = snmpgo.NewCounter32(uint32(expInt)) + + big, err := v.BigInt() + if err != nil { + t.Errorf("Failed to call BigInt(): %v", err) + } + if expInt != big.Int64() { + t.Errorf("BigInt() - expected [%d], actual [%d]", expInt, big.Int64()) + } + + if expStr != v.String() { + t.Errorf("String() - expected [%s], actual [%s]", expStr, v.String()) + } + + buf, err := v.Marshal() + if err != nil { + t.Errorf("Marshal(): %v", err) + } + if !bytes.Equal(expBuf, buf) { + t.Errorf("Marshal() - expected [%s], actual [%s]", + snmpgo.ToHexStr(expBuf, " "), snmpgo.ToHexStr(buf, " ")) + } + + var w snmpgo.Counter32 + rest, err := (&w).Unmarshal(buf) + if len(rest) != 0 || err != nil { + t.Errorf("Unmarshal() - len[%d] err[%v]", len(rest), err) + } + if expStr != w.String() { + t.Errorf("Unmarshal() - expected [%s], actual [%s]", expStr, w.String()) + } + + buf = append(buf, 0x00) + rest, err = (&w).Unmarshal(buf) + if len(rest) != 1 || err != nil { + t.Errorf("Unmarshal() with rest - len[%d] err[%v]", len(rest), err) + } + if expStr != w.String() { + t.Errorf("Unmarshal() with rest - expected [%s], actual [%s]", expStr, w.String()) + } +} + +func TestGauge32(t *testing.T) { + expInt := int64(4294967295) + expStr := "4294967295" + expBuf := []byte{0x42, 0x05, 0x00, 0xff, 0xff, 0xff, 0xff} + var v snmpgo.Variable = snmpgo.NewGauge32(uint32(expInt)) + + big, err := v.BigInt() + if err != nil { + t.Errorf("Failed to call BigInt(): %v", err) + } + if expInt != big.Int64() { + t.Errorf("BigInt() - expected [%d], actual [%d]", expInt, big.Int64()) + } + + if expStr != v.String() { + t.Errorf("String() - expected [%s], actual [%s]", expStr, v.String()) + } + + buf, err := v.Marshal() + if err != nil { + t.Errorf("Marshal(): %v", err) + } + if !bytes.Equal(expBuf, buf) { + t.Errorf("Marshal() - expected [%s], actual [%s]", + snmpgo.ToHexStr(expBuf, " "), snmpgo.ToHexStr(buf, " ")) + } + + var w snmpgo.Gauge32 + rest, err := (&w).Unmarshal(buf) + if len(rest) != 0 || err != nil { + t.Errorf("Unmarshal() - len[%d] err[%v]", len(rest), err) + } + if expStr != w.String() { + t.Errorf("Unmarshal() - expected [%s], actual [%s]", expStr, w.String()) + } + + buf = append(buf, 0x00) + rest, err = (&w).Unmarshal(buf) + if len(rest) != 1 || err != nil { + t.Errorf("Unmarshal() with rest - len[%d] err[%v]", len(rest), err) + } + if expStr != w.String() { + t.Errorf("Unmarshal() with rest - expected [%s], actual [%s]", expStr, w.String()) + } +} + +func TestTimeTicks(t *testing.T) { + expInt := int64(4294967295) + expStr := "4294967295" + expBuf := []byte{0x43, 0x05, 0x00, 0xff, 0xff, 0xff, 0xff} + var v snmpgo.Variable = snmpgo.NewTimeTicks(uint32(expInt)) + + big, err := v.BigInt() + if err != nil { + t.Errorf("Failed to call BigInt(): %v", err) + } + if expInt != big.Int64() { + t.Errorf("BigInt() - expected [%d], actual [%d]", expInt, big.Int64()) + } + + if expStr != v.String() { + t.Errorf("String() - expected [%s], actual [%s]", expStr, v.String()) + } + + buf, err := v.Marshal() + if err != nil { + t.Errorf("Marshal(): %v", err) + } + if !bytes.Equal(expBuf, buf) { + t.Errorf("Marshal() - expected [%s], actual [%s]", + snmpgo.ToHexStr(expBuf, " "), snmpgo.ToHexStr(buf, " ")) + } + + var w snmpgo.TimeTicks + rest, err := (&w).Unmarshal(buf) + if len(rest) != 0 || err != nil { + t.Errorf("Unmarshal() - len[%d] err[%v]", len(rest), err) + } + if expStr != w.String() { + t.Errorf("Unmarshal() - expected [%s], actual [%s]", expStr, w.String()) + } + + buf = append(buf, 0x00) + rest, err = (&w).Unmarshal(buf) + if len(rest) != 1 || err != nil { + t.Errorf("Unmarshal() with rest - len[%d] err[%v]", len(rest), err) + } + if expStr != w.String() { + t.Errorf("Unmarshal() with rest - expected [%s], actual [%s]", expStr, w.String()) + } +} + +func TestOpaque(t *testing.T) { + expStr := "54:65:73:74" + expBuf := []byte{0x44, 0x04, 0x54, 0x65, 0x73, 0x74} + var v snmpgo.Variable = snmpgo.NewOpaque(expBuf[2:]) + + _, err := v.BigInt() + if err == nil { + t.Errorf("Failed to call BigInt()") + } + + if expStr != v.String() { + t.Errorf("String() - expected [%s], actual[%s]", expStr, v.String()) + } + + buf, err := v.Marshal() + if err != nil { + t.Errorf("Marshal(): %v", err) + } + if !bytes.Equal(expBuf, buf) { + t.Errorf("Marshal() - expected [%s], actual [%s]", + snmpgo.ToHexStr(expBuf, " "), snmpgo.ToHexStr(buf, " ")) + } + + var w snmpgo.Opaque + rest, err := (&w).Unmarshal(buf) + if len(rest) != 0 || err != nil { + t.Errorf("Unmarshal() - len[%d] err[%v]", len(rest), err) + } + if expStr != w.String() { + t.Errorf("Unmarshal() - expected [%s], actual [%s]", expStr, w.String()) + } + + buf = append(buf, 0x00) + rest, err = (&w).Unmarshal(buf) + if len(rest) != 1 || err != nil { + t.Errorf("Unmarshal() with rest - len[%d] err[%v]", len(rest), err) + } + if expStr != w.String() { + t.Errorf("Unmarshal() with rest - expected [%s], actual [%s]", expStr, w.String()) + } +} + +func TestCounter64(t *testing.T) { + expInt := uint64(18446744073709551615) + expStr := "18446744073709551615" + expBuf := []byte{0x46, 0x09, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} + var v snmpgo.Variable = snmpgo.NewCounter64(expInt) + + big, err := v.BigInt() + if err != nil { + t.Errorf("Failed to call BigInt(): %v", err) + } + if expInt != big.Uint64() { + t.Errorf("BigInt() - expected [%d], actual [%d]", expInt, big.Int64()) + } + + if expStr != v.String() { + t.Errorf("String() - expected [%s], actual [%s]", expStr, v.String()) + } + + buf, err := v.Marshal() + if err != nil { + t.Errorf("Marshal(): %v", err) + } + if !bytes.Equal(expBuf, buf) { + t.Errorf("Marshal() - expected [%s], actual [%s]", + snmpgo.ToHexStr(expBuf, " "), snmpgo.ToHexStr(buf, " ")) + } + + var w snmpgo.Counter64 + rest, err := (&w).Unmarshal(buf) + if len(rest) != 0 || err != nil { + t.Errorf("Unmarshal() - len[%d] err[%v]", len(rest), err) + } + if expStr != w.String() { + t.Errorf("Unmarshal() - expected [%s], actual [%s]", expStr, w.String()) + } + + buf = append(buf, 0x00) + rest, err = (&w).Unmarshal(buf) + if len(rest) != 1 || err != nil { + t.Errorf("Unmarshal() with rest - len[%d] err[%v]", len(rest), err) + } + if expStr != w.String() { + t.Errorf("Unmarshal() with rest - expected [%s], actual [%s]", expStr, w.String()) + } +} + +func TestNoSucheObject(t *testing.T) { + expStr := "" + expBuf := []byte{0x80, 0x00} + var v snmpgo.Variable = snmpgo.NewNoSucheObject() + + _, err := v.BigInt() + if err == nil { + t.Errorf("Failed to call BigInt()") + } + + if expStr != v.String() { + t.Errorf("String() - expected [%s], actual[%s]", expStr, v.String()) + } + + buf, err := v.Marshal() + if err != nil { + t.Errorf("Marshal(): %v", err) + } + if !bytes.Equal(expBuf, buf) { + t.Errorf("Marshal() - expected [%s], actual [%s]", + snmpgo.ToHexStr(expBuf, " "), snmpgo.ToHexStr(buf, " ")) + } + + var w snmpgo.NoSucheObject + rest, err := (&w).Unmarshal(buf) + if len(rest) != 0 || err != nil { + t.Errorf("Unmarshal() - len[%d] err[%v]", len(rest), err) + } + if expStr != w.String() { + t.Errorf("Unmarshal() - expected [%s], actual [%s]", expStr, w.String()) + } + + buf = append(buf, 0x00) + rest, err = (&w).Unmarshal(buf) + if len(rest) != 1 || err != nil { + t.Errorf("Unmarshal() with rest - len[%d] err[%v]", len(rest), err) + } + if expStr != w.String() { + t.Errorf("Unmarshal() with rest - expected [%s], actual [%s]", expStr, w.String()) + } +} + +func TestNoSucheInstance(t *testing.T) { + expStr := "" + expBuf := []byte{0x81, 0x00} + var v snmpgo.Variable = snmpgo.NewNoSucheInstance() + + _, err := v.BigInt() + if err == nil { + t.Errorf("Failed to call BigInt()") + } + + if expStr != v.String() { + t.Errorf("String() - expected [%s], actual[%s]", expStr, v.String()) + } + + buf, err := v.Marshal() + if err != nil { + t.Errorf("Marshal(): %v", err) + } + if !bytes.Equal(expBuf, buf) { + t.Errorf("Marshal() - expected [%s], actual [%s]", + snmpgo.ToHexStr(expBuf, " "), snmpgo.ToHexStr(buf, " ")) + } + + var w snmpgo.NoSucheInstance + rest, err := (&w).Unmarshal(buf) + if len(rest) != 0 || err != nil { + t.Errorf("Unmarshal() - len[%d] err[%v]", len(rest), err) + } + if expStr != w.String() { + t.Errorf("Unmarshal() - expected [%s], actual [%s]", expStr, w.String()) + } + + buf = append(buf, 0x00) + rest, err = (&w).Unmarshal(buf) + if len(rest) != 1 || err != nil { + t.Errorf("Unmarshal() with rest - len[%d] err[%v]", len(rest), err) + } + if expStr != w.String() { + t.Errorf("Unmarshal() with rest - expected [%s], actual [%s]", expStr, w.String()) + } +} + +func TestEndOfMibView(t *testing.T) { + expStr := "" + expBuf := []byte{0x82, 0x00} + var v snmpgo.Variable = snmpgo.NewEndOfMibView() + + _, err := v.BigInt() + if err == nil { + t.Errorf("Failed to call BigInt()") + } + + if expStr != v.String() { + t.Errorf("String() - expected [%s], actual[%s]", expStr, v.String()) + } + + buf, err := v.Marshal() + if err != nil { + t.Errorf("Marshal(): %v", err) + } + if !bytes.Equal(expBuf, buf) { + t.Errorf("Marshal() - expected [%s], actual [%s]", + snmpgo.ToHexStr(expBuf, " "), snmpgo.ToHexStr(buf, " ")) + } + + var w snmpgo.EndOfMibView + rest, err := (&w).Unmarshal(buf) + if len(rest) != 0 || err != nil { + t.Errorf("Unmarshal() - len[%d] err[%v]", len(rest), err) + } + if expStr != w.String() { + t.Errorf("Unmarshal() - expected [%s], actual [%s]", expStr, w.String()) + } + + buf = append(buf, 0x00) + rest, err = (&w).Unmarshal(buf) + if len(rest) != 1 || err != nil { + t.Errorf("Unmarshal() with rest - len[%d] err[%v]", len(rest), err) + } + if expStr != w.String() { + t.Errorf("Unmarshal() with rest - expected [%s], actual [%s]", expStr, w.String()) + } +}