// Copyright 2012 The GoSNMP Authors. All rights reserved.  Use of this
// source code is governed by a BSD-style license that can be found in the
// LICENSE file.

//go:build all || marshal

package gosnmp

import (
	"bytes"
	"encoding/hex"
	"fmt"
	"io"
	"log"
	"net"
	"reflect"
	"runtime"
	"strconv"
	"strings"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

// Tests in alphabetical order of function being tested

// -- Enmarshal ----------------------------------------------------------------

// "Enmarshal" not "Marshal" - easier to select tests via a regex

type testsEnmarshalVarbindPosition struct {
	oid string

	/*
		start and finish position of bytes are calculated with application layer
		starting at byte 0. There are two ways to understand Wireshark dumps,
		switch between them:

		1) the standard decode of the full packet - easier to understand
		what's actually happening

		2) for counting byte positions: select "Simple Network Management
		Protocol" line in Wiresharks middle pane, then right click and choose
		"Export Packet Bytes..." (as .raw). Open the capture in wireshark, it
		will decode as "BER Encoded File". Click on each varbind and the
		"packet bytes" window will highlight the corresponding bytes, then the
		start and end positions can be found.
	*/

	/*
		go-bindata has changed output format. Old style is needed:

		go get -u github.com/jteeuwen/go-bindata/...
		git co 79847ab
		rm ~/go/bin/go-bindata  # belts and braces
		go install
		~/go/bin/go-bindata -uncompressed *.pcap
	*/

	start    int
	finish   int
	pduType  Asn1BER
	pduValue interface{}
}

type testsEnmarshalT struct {
	version     SnmpVersion
	community   string
	requestType PDUType
	requestid   uint32
	msgid       uint32
	// function and function name returning bytes from tcpdump
	goodBytes func() []byte
	funcName  string // could do this via reflection
	// start position of the pdu
	pduStart int
	// start position of the vbl
	vblStart int
	// finish position of pdu, vbl and message - all the same
	finish int
	// a slice of positions containing start and finish of each varbind
	vbPositions []testsEnmarshalVarbindPosition
}

var testsEnmarshal = []testsEnmarshalT{
	{
		Version2c,
		"public",
		GetRequest,
		1871507044,
		0,
		kyoceraRequestBytes,
		"kyocera_request",
		0x0e, // pdu start
		0x1d, // vbl start
		0xa0, // finish
		[]testsEnmarshalVarbindPosition{
			{".1.3.6.1.2.1.1.7.0", 0x20, 0x2d, Null, nil},
			{".1.3.6.1.2.1.2.2.1.10.1", 0x2e, 0x3d, Null, nil},
			{".1.3.6.1.2.1.2.2.1.5.1", 0x3e, 0x4d, Null, nil},
			{".1.3.6.1.2.1.1.4.0", 0x4e, 0x5b, Null, nil},
			{".1.3.6.1.2.1.43.5.1.1.15.1", 0x5c, 0x6c, Null, nil},
			{".1.3.6.1.2.1.4.21.1.1.127.0.0.1", 0x6d, 0x7f, Null, nil},
			{".1.3.6.1.4.1.23.2.5.1.1.1.4.2", 0x80, 0x92, Null, nil},
			{".1.3.6.1.2.1.1.3.0", 0x93, 0xa0, Null, nil},
		},
	},
	{
		Version1,
		"privatelab",
		SetRequest,
		526895288,
		0,
		portOnOutgoing1,
		"portOnOutgoing1",
		0x11, // pdu start
		0x1f, // vbl start
		0x36, // finish
		[]testsEnmarshalVarbindPosition{
			{".1.3.6.1.4.1.318.1.1.4.4.2.1.3.5", 0x21, 0x36, Integer, 1},
		},
	},
	{
		Version1,
		"privatelab",
		SetRequest,
		1826072803,
		0,
		portOffOutgoing1,
		"portOffOutgoing1",
		0x11, // pdu start
		0x1f, // vbl start
		0x36, // finish
		[]testsEnmarshalVarbindPosition{
			{".1.3.6.1.4.1.318.1.1.4.4.2.1.3.5", 0x21, 0x36, Integer, 2},
		},
	},
	// MrSpock Set stuff
	{
		Version2c,
		"private",
		SetRequest,
		756726019,
		0,
		setOctet1,
		"setOctet1",
		0x0e, // pdu start
		0x1c, // vbl start
		0x32, // finish
		[]testsEnmarshalVarbindPosition{
			{".1.3.6.1.4.1.2863.205.1.1.75.1.0",
				0x1e, 0x32, OctetString, []byte{0x80}},
		},
	},
	{
		Version2c,
		"private",
		SetRequest,
		1000552357,
		0,
		setOctet2,
		"setOctet2",
		0x0e, // pdu start
		0x1c, // vbl start
		0x37, // finish
		[]testsEnmarshalVarbindPosition{
			{".1.3.6.1.4.1.2863.205.1.1.75.2.0",
				0x1e, 0x36, OctetString, []byte("telnet")},
		},
	},
	// MrSpock Set stuff
	{
		Version2c,
		"private",
		SetRequest,
		1664317637,
		0,
		setInteger1,
		"setInteger1",
		0x0e, // pdu start
		0x1c, // vbl start
		0x7f, // finish
		[]testsEnmarshalVarbindPosition{
			{".1.3.6.1.4.1.2863.205.10.1.33.2.5.1.2.2", 0x1e, 0x36, Integer, 5001},
			{".1.3.6.1.4.1.2863.205.10.1.33.2.5.1.3.2", 0x37, 0x4f, Integer, 5001},
			{".1.3.6.1.4.1.2863.205.10.1.33.2.5.1.4.2", 0x50, 0x67, Integer, 2},
			{".1.3.6.1.4.1.2863.205.10.1.33.2.5.1.5.2", 0x68, 0x7f, Integer, 1},
		},
	},
	// Issue 35, empty responses.
	{
		Version2c,
		"public",
		GetRequest,
		1883298028,
		0,
		emptyErrRequest,
		"emptyErrRequest",
		0x0d, // pdu start
		0x1b, // vbl start
		0x1c, // finish
		[]testsEnmarshalVarbindPosition{},
	},
	// trap - TimeTicks
	// snmptrap different with timetick 2, integer 5

	// trap1 - capture is from frame - less work, decode easier
	// varbinds - because Wireshark is decoding as BER's, need to subtract 2
	// from start of varbinds
	{
		Version2c,
		"public",
		SNMPv2Trap,
		1918693186,
		0,
		trap1,
		"trap1",
		0x0e, // pdu start
		0x1c, // vbl start
		0x82, // finish
		[]testsEnmarshalVarbindPosition{
			{".1.3.6.1.2.1.1.3.0", 0x1e, 0x2f, TimeTicks, uint32(18542501)},
			{".1.3.6.1.6.3.1.1.4.1.0", 0x30, 0x45, ObjectIdentifier, ".1.3.6.1.2.1.1"},
			{".1.3.6.1.2.1.1.1.0", 0x46, 0x59, OctetString, "red laptop"},
			{".1.3.6.1.2.1.1.7.0", 0x5e, 0x6c, Integer, 5},
			{".1.3.6.1.2.1.1.2", 0x6d, 0x82, ObjectIdentifier, ".1.3.6.1.4.1.2.3.4.5"},
		},
	},
}

// helpers for Enmarshal tests

// vbPosPdus returns a slice of oids in the given test
func vbPosPdus(test testsEnmarshalT) (pdus []SnmpPDU) {
	for _, vbp := range test.vbPositions {
		pdu := SnmpPDU{Name: vbp.oid, Type: vbp.pduType, Value: vbp.pduValue}
		pdus = append(pdus, pdu)
	}
	return
}

// checkByteEquality walks the bytes in testBytes, and compares them to goodBytes
func checkByteEquality(t *testing.T, test testsEnmarshalT, testBytes []byte,
	start int, finish int) {

	testBytesLen := len(testBytes)

	goodBytes := test.goodBytes()
	goodBytes = goodBytes[start : finish+1]
	for cursor := range goodBytes {
		if testBytesLen < cursor {
			t.Errorf("%s: testBytesLen (%d) < cursor (%d)", test.funcName,
				testBytesLen, cursor)
			break
		}
		if testBytes[cursor] != goodBytes[cursor] {
			t.Errorf("%s: cursor %d: testBytes != goodBytes:\n%s\n%s",
				test.funcName,
				cursor,
				dumpBytes2("good", goodBytes, cursor),
				dumpBytes2("test", testBytes, cursor))
			break
		}
	}
}

// Enmarshal tests in order that should be used for troubleshooting
// ie check each varbind is working, then the varbind list, etc

func TestEnmarshalVarbind(t *testing.T) {
	Default.Logger = NewLogger(log.New(io.Discard, "", 0))

	for _, test := range testsEnmarshal {
		for j, test2 := range test.vbPositions {
			snmppdu := &SnmpPDU{Name: test2.oid, Type: test2.pduType, Value: test2.pduValue}
			testBytes, err := marshalVarbind(snmppdu)
			if err != nil {
				t.Errorf("#%s:%d:%s err returned: %v",
					test.funcName, j, test2.oid, err)
			}

			checkByteEquality(t, test, testBytes, test2.start, test2.finish)
		}
	}
}

func TestEnmarshalVBL(t *testing.T) {
	Default.Logger = NewLogger(log.New(io.Discard, "", 0))

	for _, test := range testsEnmarshal {
		x := &SnmpPacket{
			Community: test.community,
			Version:   test.version,
			RequestID: test.requestid,
			Variables: vbPosPdus(test),
		}

		testBytes, err := x.marshalVBL()
		if err != nil {
			t.Errorf("#%s: marshalVBL() err returned: %v", test.funcName, err)
		}

		checkByteEquality(t, test, testBytes, test.vblStart, test.finish)
	}
}

func TestEnmarshalPDU(t *testing.T) {
	Default.Logger = NewLogger(log.New(io.Discard, "", 0))

	for _, test := range testsEnmarshal {
		x := &SnmpPacket{
			Community: test.community,
			Version:   test.version,
			PDUType:   test.requestType,
			RequestID: test.requestid,
			Variables: vbPosPdus(test),
		}

		testBytes, err := x.marshalPDU()
		if err != nil {
			t.Errorf("#%s: marshalPDU() err returned: %v", test.funcName, err)
		}

		checkByteEquality(t, test, testBytes, test.pduStart, test.finish)
	}
}

func TestEnmarshalMsg(t *testing.T) {
	Default.Logger = NewLogger(log.New(io.Discard, "", 0))

	for _, test := range testsEnmarshal {
		x := &SnmpPacket{
			Community: test.community,
			Version:   test.version,
			PDUType:   test.requestType,
			RequestID: test.requestid,
			MsgID:     test.msgid,
			Variables: vbPosPdus(test),
		}

		testBytes, err := x.marshalMsg()
		if err != nil {
			t.Errorf("#%s: marshal() err returned: %v", test.funcName, err)
		}
		checkByteEquality(t, test, testBytes, 0, test.finish)
		t.Run(fmt.Sprintf("TestEnmarshalMsgUnmarshal/PDU[%v]/RequestID[%v]", test.requestType, test.requestid), func(t *testing.T) {
			vhandle := GoSNMP{}
			vhandle.Logger = Default.Logger
			result, err := vhandle.SnmpDecodePacket(testBytes)
			if err != nil {
				t.Errorf("#%s: SnmpDecodePacket() err returned: %v", test.funcName, err)
			}
			newResultTestBytes, err := result.marshalMsg()
			if err != nil {
				t.Errorf("#%s: marshal() err returned: %v", test.funcName, err)
			}
			if len(newResultTestBytes) == 0 {
				t.Errorf("#%s: marshal() length of result is 0 : %v", test.funcName, (newResultTestBytes))
				return
			}
			checkByteEquality(t, test, newResultTestBytes, 0, test.finish)
		})
	}
}

// -- Unmarshal -----------------------------------------------------------------

var testsUnmarshalErr = []struct {
	in func() []byte
}{
	{
		panicUnmarshalHeader,
	},
	{
		panicUnmarshalV3Header,
	},
	{
		panicUnmarshalUserSecurityModelPacketLen,
	},
	{
		panicUnmarshalV3HeaderFlagLen,
	},
	{
		panicUnmarshalParseFloat32,
	},
	{
		panicUnmarshalParseFloat64,
	},
	{
		panicUnmarshalParseRawFieldTimeTicks,
	},
	{
		panicUnmarshalDecryptPacketIndex,
	},
	{
		panicUnmarshalDecryptNoPriv,
	},
	{
		panicUnmarshalV3Overflow,
	},
}

var testsUnmarshal = []struct {
	in  func() []byte
	out *SnmpPacket
}{
	{kyoceraResponseBytes,
		&SnmpPacket{
			Version:    Version2c,
			Community:  "public",
			PDUType:    GetResponse,
			RequestID:  1066889284,
			Error:      0,
			ErrorIndex: 0,
			Variables: []SnmpPDU{
				{
					Name:  ".1.3.6.1.2.1.1.7.0",
					Type:  Integer,
					Value: 104,
				},
				{
					Name:  ".1.3.6.1.2.1.2.2.1.10.1",
					Type:  Counter32,
					Value: 271070065,
				},
				{
					Name:  ".1.3.6.1.2.1.2.2.1.5.1",
					Type:  Gauge32,
					Value: 100000000,
				},
				{
					Name:  ".1.3.6.1.2.1.1.4.0",
					Type:  OctetString,
					Value: []byte("Administrator"),
				},
				{
					Name:  ".1.3.6.1.2.1.43.5.1.1.15.1",
					Type:  Null,
					Value: nil,
				},
				{
					Name:  ".1.3.6.1.2.1.4.21.1.1.127.0.0.1",
					Type:  IPAddress,
					Value: "127.0.0.1",
				},
				{
					Name:  ".1.3.6.1.4.1.23.2.5.1.1.1.4.2",
					Type:  OctetString,
					Value: []byte{0x00, 0x15, 0x99, 0x37, 0x76, 0x2b},
				},
				{
					Name:  ".1.3.6.1.2.1.1.3.0",
					Type:  TimeTicks,
					Value: uint32(318870100),
				},
			},
		},
	},
	{ciscoResponseBytes,
		&SnmpPacket{
			Version:    Version2c,
			Community:  "public",
			PDUType:    GetResponse,
			RequestID:  4876669,
			Error:      0,
			ErrorIndex: 0,
			Variables: []SnmpPDU{
				{
					Name:  ".1.3.6.1.2.1.1.7.0",
					Type:  Integer,
					Value: 78,
				},
				{
					Name:  ".1.3.6.1.2.1.2.2.1.2.6",
					Type:  OctetString,
					Value: []byte("GigabitEthernet0"),
				},
				{
					Name:  ".1.3.6.1.2.1.2.2.1.5.3",
					Type:  Gauge32,
					Value: uint(4294967295),
				},
				{
					Name:  ".1.3.6.1.2.1.2.2.1.7.2",
					Type:  NoSuchInstance,
					Value: nil,
				},
				{
					Name:  ".1.3.6.1.2.1.2.2.1.9.3",
					Type:  TimeTicks,
					Value: uint32(2970),
				},
				{
					Name:  ".1.3.6.1.2.1.3.1.1.2.10.1.10.11.0.17",
					Type:  OctetString,
					Value: []byte{0x00, 0x07, 0x7d, 0x4d, 0x09, 0x00},
				},
				{
					Name:  ".1.3.6.1.2.1.3.1.1.3.10.1.10.11.0.2",
					Type:  IPAddress,
					Value: "10.11.0.2",
				},
				{
					Name:  ".1.3.6.1.2.1.4.20.1.1.110.143.197.1",
					Type:  IPAddress,
					Value: "110.143.197.1",
				},
				{
					Name:  ".1.3.6.1.66.1",
					Type:  NoSuchObject,
					Value: nil,
				},
				{
					Name:  ".1.3.6.1.2.1.1.2.0",
					Type:  ObjectIdentifier,
					Value: ".1.3.6.1.4.1.9.1.1166",
				},
			},
		},
	},
	{portOnIncoming1,
		&SnmpPacket{
			Version:    Version1,
			Community:  "privatelab",
			PDUType:    GetResponse,
			RequestID:  526895288,
			Error:      0,
			ErrorIndex: 0,
			Variables: []SnmpPDU{
				{
					Name:  ".1.3.6.1.4.1.318.1.1.4.4.2.1.3.5",
					Type:  Integer,
					Value: 1,
				},
			},
		},
	},
	{portOffIncoming1,
		&SnmpPacket{
			Version:    Version1,
			Community:  "privatelab",
			PDUType:    GetResponse,
			RequestID:  1826072803,
			Error:      0,
			ErrorIndex: 0,
			Variables: []SnmpPDU{
				{
					Name:  ".1.3.6.1.4.1.318.1.1.4.4.2.1.3.5",
					Type:  Integer,
					Value: 2,
				},
			},
		},
	},
	{ciscoGetnextResponseBytes,
		&SnmpPacket{
			Version:    Version2c,
			Community:  "public",
			PDUType:    GetResponse,
			RequestID:  1528674030,
			Error:      0,
			ErrorIndex: 0,
			Variables: []SnmpPDU{
				{
					Name:  ".1.3.6.1.2.1.3.1.1.3.2.1.192.168.104.2",
					Type:  IPAddress,
					Value: "192.168.104.2",
				},
				{
					Name:  ".1.3.6.1.2.1.92.1.2.1.0",
					Type:  Counter32,
					Value: 0,
				},
				{
					Name:  ".1.3.6.1.2.1.1.9.1.3.3",
					Type:  OctetString,
					Value: []byte("The MIB module for managing IP and ICMP implementations"),
				},
				{
					Name:  ".1.3.6.1.2.1.1.9.1.4.2",
					Type:  TimeTicks,
					Value: 21,
				},
				{
					Name:  ".1.3.6.1.2.1.2.1.0",
					Type:  Integer,
					Value: 3,
				},
				{
					Name:  ".1.3.6.1.2.1.1.2.0",
					Type:  ObjectIdentifier,
					Value: ".1.3.6.1.4.1.8072.3.2.10",
				},
			},
		},
	},
	{ciscoGetbulkResponseBytes,
		&SnmpPacket{
			Version:        Version2c,
			Community:      "public",
			PDUType:        GetResponse,
			RequestID:      250000266,
			NonRepeaters:   0,
			MaxRepetitions: 10,
			Variables: []SnmpPDU{
				{
					Name:  ".1.3.6.1.2.1.1.9.1.4.1",
					Type:  TimeTicks,
					Value: 21,
				},
				{
					Name:  ".1.3.6.1.2.1.1.9.1.4.2",
					Type:  TimeTicks,
					Value: 21,
				},
				{
					Name:  ".1.3.6.1.2.1.1.9.1.4.3",
					Type:  TimeTicks,
					Value: 21,
				},
				{
					Name:  ".1.3.6.1.2.1.1.9.1.4.4",
					Type:  TimeTicks,
					Value: 21,
				},
				{
					Name:  ".1.3.6.1.2.1.1.9.1.4.5",
					Type:  TimeTicks,
					Value: 21,
				},
				{
					Name:  ".1.3.6.1.2.1.1.9.1.4.6",
					Type:  TimeTicks,
					Value: 23,
				},
				{
					Name:  ".1.3.6.1.2.1.1.9.1.4.7",
					Type:  TimeTicks,
					Value: 23,
				},
				{
					Name:  ".1.3.6.1.2.1.1.9.1.4.8",
					Type:  TimeTicks,
					Value: 23,
				},
				{
					Name:  ".1.3.6.1.2.1.2.1.0",
					Type:  Integer,
					Value: 3,
				},
				{
					Name:  ".1.3.6.1.2.1.2.2.1.1.1",
					Type:  Integer,
					Value: 1,
				},
			},
		},
	},
	{emptyErrResponse,
		&SnmpPacket{
			Version:   Version2c,
			Community: "public",
			PDUType:   GetResponse,
			RequestID: 1883298028,
			Error:     0,
			Variables: []SnmpPDU{},
		},
	},
	{counter64Response,
		&SnmpPacket{
			Version:    Version2c,
			Community:  "public",
			PDUType:    GetResponse,
			RequestID:  190378322,
			Error:      0,
			ErrorIndex: 0,
			Variables: []SnmpPDU{
				{
					Name:  ".1.3.6.1.2.1.31.1.1.1.10.1",
					Type:  Counter64,
					Value: uint64(1527943),
				},
			},
		},
	},
	{opaqueFloatResponse,
		&SnmpPacket{
			Version:    Version2c,
			Community:  "public",
			PDUType:    GetResponse,
			RequestID:  601216773,
			Error:      0,
			ErrorIndex: 0,
			Variables: []SnmpPDU{
				{
					Name:  ".1.3.6.1.4.1.6574.4.2.12.1.0",
					Type:  OpaqueFloat,
					Value: float32(10.0),
				},
			},
		},
	},
	{opaqueResponse,
		&SnmpPacket{
			Version:    Version1,
			Community:  "public",
			PDUType:    GetResponse,
			RequestID:  2033938493,
			Error:      0,
			ErrorIndex: 0,
			Variables: []SnmpPDU{
				{
					Name:  ".1.3.6.1.4.1.34187.74195.2.1.24590",
					Type:  Opaque,
					Value: []byte{0x41, 0xf0, 0x00, 0x00},
				},
			},
		},
	},
	{opaqueDoubleResponse,
		&SnmpPacket{
			Version:    Version2c,
			Community:  "public",
			PDUType:    GetResponse,
			RequestID:  601216773,
			Error:      0,
			ErrorIndex: 0,
			Variables: []SnmpPDU{
				{
					Name:  ".1.3.6.1.4.1.6574.4.2.12.1.0",
					Type:  OpaqueDouble,
					Value: float64(10.0),
				},
			},
		},
	},
	{snmpv3HelloRequest,
		&SnmpPacket{
			Version:    Version3,
			PDUType:    GetRequest,
			MsgID:      91040642,
			RequestID:  1157240545,
			Error:      0,
			ErrorIndex: 0,
			Variables:  []SnmpPDU{},
		},
	},
	{snmpv3HelloResponse,
		&SnmpPacket{
			Version:    Version3,
			PDUType:    Report,
			MsgID:      91040642,
			RequestID:  1157240545,
			Error:      0,
			ErrorIndex: 0,
			Variables: []SnmpPDU{
				{
					Name:  ".1.3.6.1.6.3.15.1.1.4.0",
					Type:  Counter32,
					Value: 21,
				},
			},
		},
	},
}

func TestUnmarshalErrors(t *testing.T) {
	Default.Logger = NewLogger(log.New(io.Discard, "", 0))

	for i, test := range testsUnmarshalErr {
		funcName := runtime.FuncForPC(reflect.ValueOf(test.in).Pointer()).Name()
		splitedFuncName := strings.Split(funcName, ".")
		funcName = splitedFuncName[len(splitedFuncName)-1]
		t.Run(fmt.Sprintf("%v-%v", i, funcName), func(t *testing.T) {
			vhandle := GoSNMP{}
			vhandle.Logger = Default.Logger
			testBytes := test.in()
			_, err := vhandle.SnmpDecodePacket(testBytes)
			if err == nil {
				t.Errorf("#%s: SnmpDecodePacket() err expected, but not returned", funcName)
			}
		})
	}
}

func FuzzUnmarshal(f *testing.F) {
	for _, test := range testsUnmarshalErr {
		f.Add(test.in())
	}

	for _, test := range testsUnmarshal {
		f.Add(test.in())
	}

	vhandle := GoSNMP{}
	vhandle.Logger = Default.Logger
	f.Fuzz(func(t *testing.T, data []byte) {
		stime := time.Now()
		_, _ = vhandle.SnmpDecodePacket(data)

		if e := time.Since(stime); e > (time.Second * 1) {
			t.Errorf("SnmpDecodePacket() took too long: %s", e)
		}
	})
}

func TestUnmarshal(t *testing.T) {
	Default.Logger = NewLogger(log.New(io.Discard, "", 0))

	for i, test := range testsUnmarshal {
		funcName := runtime.FuncForPC(reflect.ValueOf(test.in).Pointer()).Name()
		splitedFuncName := strings.Split(funcName, ".")
		funcName = splitedFuncName[len(splitedFuncName)-1]
		t.Run(fmt.Sprintf("%v-%v", i, funcName), func(t *testing.T) {
			vhandle := GoSNMP{}
			vhandle.Logger = Default.Logger
			testBytes := test.in()
			res, err := vhandle.SnmpDecodePacket(testBytes)
			if err != nil {
				t.Errorf("#%s: SnmpDecodePacket() err returned: %v", funcName, err)
			}
			t.Run("unmarshal", func(t *testing.T) {
				// test "header" fields
				if res.Version != test.out.Version {
					t.Errorf("#%d Version result: %v, test: %v", i, res.Version, test.out.Version)
				}
				if res.Community != test.out.Community {
					t.Errorf("#%d Community result: %v, test: %v", i, res.Community, test.out.Community)
				}
				if res.PDUType != test.out.PDUType {
					t.Errorf("#%d PDUType result: %v, test: %v", i, res.PDUType, test.out.PDUType)
				}
				if res.RequestID != test.out.RequestID {
					t.Errorf("#%d RequestID result: %v, test: %v", i, res.RequestID, test.out.RequestID)
				}
				if res.Error != test.out.Error {
					t.Errorf("#%d Error result: %v, test: %v", i, res.Error, test.out.Error)
				}
				if res.ErrorIndex != test.out.ErrorIndex {
					t.Errorf("#%d ErrorIndex result: %v, test: %v", i, res.ErrorIndex, test.out.ErrorIndex)
				}

				// test varbind values
				for n, vb := range test.out.Variables {
					if len(res.Variables) < n {
						t.Errorf("#%d:%d ran out of varbind results", i, n)
						return
					}
					vbr := res.Variables[n]

					if vbr.Name != vb.Name {
						t.Errorf("#%d:%d Name result: %v, test: %v", i, n, vbr.Name, vb.Name)
					}
					if vbr.Type != vb.Type {
						t.Errorf("#%d:%d Type result: %v, test: %v", i, n, vbr.Type, vb.Type)
					}

					switch vb.Type {
					case Integer, Gauge32, Counter32, TimeTicks, Counter64:
						vbval := ToBigInt(vb.Value)
						vbrval := ToBigInt(vbr.Value)
						if vbval.Cmp(vbrval) != 0 {
							t.Errorf("#%d:%d Value result: %v, test: %v", i, n, vbr.Value, vb.Value)
						}
					case OctetString, Opaque:
						if !bytes.Equal(vb.Value.([]byte), vbr.Value.([]byte)) {
							t.Errorf("#%d:%d Value result: %v, test: %v", i, n, vbr.Value, vb.Value)
						}
					case IPAddress, ObjectIdentifier:
						if vb.Value != vbr.Value {
							t.Errorf("#%d:%d Value result: %v, test: %v", i, n, vbr.Value, vb.Value)
						}
					case Null, NoSuchObject, NoSuchInstance:
						if (vb.Value != nil) || (vbr.Value != nil) {
							t.Errorf("#%d:%d Value result: %v, test: %v", i, n, vbr.Value, vb.Value)
						}
					case OpaqueFloat:
						if vb.Value.(float32) != vbr.Value.(float32) {
							t.Errorf("#%d:%d Value result: %v, test: %v", i, n, vbr.Value, vb.Value)
						}
					case OpaqueDouble:
						if vb.Value.(float64) != vbr.Value.(float64) {
							t.Errorf("#%d:%d Value result: %v, test: %v", i, n, vbr.Value, vb.Value)
						}
					default:
						t.Errorf("#%d:%d Unhandled case result: %v, test: %v", i, n, vbr.Value, vb.Value)
					}

				}
			})
			t.Run("remarshal", func(t *testing.T) {
				result, err := res.marshalMsg()
				if err != nil {
					t.Fatalf("#%s: marshalMsg() err returned: %v", funcName, err)
				}
				resNew, err := vhandle.SnmpDecodePacket(result)
				if err != nil {
					t.Fatalf("#%s: SnmpDecodePacket() err returned: %v", funcName, err)
				}
				assert.EqualValues(t, res, resNew)

			})
		})

	}
}

// -----------------------------------------------------------------------------

/*

* byte dumps generated using tcpdump and github.com/jteeuwen/go-bindata eg
  `sudo tcpdump -s 0 -i eth0 -w cisco.pcap host 203.50.251.17 and port 161`

* Frame, Ethernet II, IP and UDP layers removed from generated bytes
*/

/*
panicUnmarshalHeader tests a boundary condition that results in a panic
when unmarshalling the SNMP header (see also https://github.com/gosnmp/gosnmp/issues/440)
*/
func panicUnmarshalHeader() []byte {
	return []byte("0\x04\x02\x020\x03")
}

/*
panicUnmarshalV3Header tests a boundary condition that results in a panic
when unmarshalling the SNMPv3.
*/
func panicUnmarshalV3Header() []byte {
	return []byte{
		0x30, 0x81, 0x95, 0x02, 0x01, 0x03, 0x30, 0x30, 0x43, 0x04, 0x30, 0x30, 0x30, 0x30, 0x43, 0x03,
		0x30, 0x30, 0x30, 0x04, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x30, 0x30, 0x04, 0x51, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
	}
}

/*
panicUnmarshalUserSecurityModelPacketLen() tests a boundary condition that results in a panic
when indexing into the packet when processing the User Security Model.
*/
func panicUnmarshalUserSecurityModelPacketLen() []byte {
	return []byte{
		0x30, 0x81, 0x95, 0x02, 0x01, 0x03, 0x30, 0x30, 0x43, 0x04, 0x30, 0x30, 0x30, 0x30, 0x43, 0x03,
		0x30, 0x30, 0x30, 0x43, 0x01, 0x30, 0x43, 0x01, 0x30, 0x04, 0xfd, 0x30, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
	}
}

/*
panicUnmarshalV3HeaderFlagLen tests a boundary condition that results in a panic
when indexing into Flags without checking the length.
*/
func panicUnmarshalV3HeaderFlagLen() []byte {
	return []byte{
		0x30, 0x7e, 0x02, 0x01, 0x03, 0x30, 0x30, 0x43, 0x04, 0x30, 0x30, 0x30, 0x30, 0x43, 0x03, 0x30,
		0x30, 0x30, 0x04, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
	}
}

/*
panicUnmarshalParseFloat32() tests a boundary condition that results in a panic
in parseFloat32 when handling malformed data.
*/
func panicUnmarshalParseFloat32() []byte {
	return []byte{
		0x30, 0x34, 0x43, 0x01, 0x30, 0x43, 0x06, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xa2, 0x27, 0x43,
		0x04, 0x30, 0x30, 0x30, 0x30, 0x43, 0x01, 0x30, 0x43, 0x01, 0x30, 0x30, 0x19, 0x30, 0x30, 0x06,
		0x0c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x44, 0x07, 0x9f,
		0x78, 0x00, 0x30, 0x30, 0x30, 0x30,
	}
}

/*
panicUnmarshalParseFloat64() tests a boundary condition that results in a panic
in parseFloat64 when handling malformed data.
*/
func panicUnmarshalParseFloat64() []byte {
	return []byte{
		0x30, 0x38, 0x43, 0x01, 0x30, 0x43, 0x06, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xa2, 0x2b, 0x43,
		0x04, 0x30, 0x30, 0x30, 0x30, 0x43, 0x01, 0x30, 0x43, 0x01, 0x30, 0x30, 0x1d, 0x30, 0x30, 0x06,
		0x0c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x44, 0x0b, 0x9f,
		0x79, 0x80, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
	}
}

/*
panicUnmarshalParseRawFieldTimeTicks() tests a boundary condition that results in a panic
in parseRawField TimeTicks type when parseLength overflows the length value returning a value
for cursor that is higher than length.
*/
func panicUnmarshalParseRawFieldTimeTicks() []byte {
	return []byte{
		0x30, 0x81, 0xc5, 0x43, 0x01, 0x30, 0x43, 0x06, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xa2, 0x81,
		0xb7, 0x43, 0x04, 0x30, 0x30, 0x30, 0x30, 0x43, 0x01, 0x30, 0x43, 0xeb, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xff,
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
	}
}

/*
panicUnmarshalDecryptPacketIndex() tests a boundary condition that results in a panic
in decryptPacket when handling malformed data.
*/
func panicUnmarshalDecryptPacketIndex() []byte {
	return []byte{
		0x30, 0x52, 0x02, 0x01, 0x03, 0x30, 0x30, 0x43, 0x04, 0x30, 0x30, 0x30, 0x30, 0x43, 0x03, 0x30,
		0x30, 0x30, 0x43, 0x01, 0x30, 0x43, 0x01, 0x30, 0x04, 0x30, 0x30, 0x30, 0x43, 0x00, 0x43, 0x01,
		0x30, 0x43, 0x01, 0x30, 0x43, 0x00, 0x43, 0x00, 0x04, 0x2a, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x30,
	}
}

/*
panicUnmarshalDecryptPacketIndex() tests a boundary condition that results in a panic
in UsmSecurityParameters.decryptPacket() when handling malformed data.
*/
func panicUnmarshalDecryptNoPriv() []byte {
	return []byte{
		0x30, 0x52, 0x02, 0x01, 0x03, 0x30, 0x30, 0x43, 0x04, 0x30, 0x30, 0x30, 0x30, 0x43, 0x03, 0x30,
		0x30, 0x30, 0x43, 0x01, 0x30, 0x43, 0x01, 0x30, 0x04, 0x30, 0x30, 0x30, 0x43, 0x00, 0x43, 0x01,
		0x30, 0x43, 0x01, 0x30, 0x43, 0x00, 0x43, 0x00, 0x43, 0x00, 0x04, 0x30, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x30,
	}
}

/*
panicUnmarshalV3Overflow tests a boundary condition that caused a panic
due to cursor index out of range (negative cursor from overflow) in SNMPv3
(see also https://github.com/gosnmp/gosnmp/issues/552).
*/
func panicUnmarshalV3Overflow() []byte {
	return []byte{
		0x30, 0x50, 0x02, 0x01, 0x01, 0x04, 0x06, 0x73, 0x64, 0x66, 0x73, 0x64, 0x66,
		0xa3, 0x43, 0x02, 0x04, 0x00, 0x00, 0x00, 0x01, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00,
		0x30, 0x35, 0x30, 0x0e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x01, 0x05, 0x00,
		0x30, 0x88, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xee, 0xa4, 0x30, 0x05, 0x06,
		0x08, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x01, 0x05, 0x00, 0x44, 0x09, 0x9f, 0x78, 0x04,
		0x7f, 0x80, 0x00, 0x00, 0x30, 0xf7, 0xf9, 0xf9, 0x02, 0x01, 0x69, 0x43, 0x00,
	}
}

/*
kyoceraResponseBytes corresponds to the response section of this snmpget

Simple Network Management Protocol
  version: v2c (1)
  community: public
  data: get-response (2)
    get-response
      request-id: 1066889284
      error-status: noError (0)
      error-index: 0
      variable-bindings: 8 items
        1.3.6.1.2.1.1.7.0: 104
        1.3.6.1.2.1.2.2.1.10.1: 271070065
        1.3.6.1.2.1.2.2.1.5.1: 100000000
        1.3.6.1.2.1.1.4.0: 41646d696e6973747261746f72
        1.3.6.1.2.1.43.5.1.1.15.1: Value (Null)
        1.3.6.1.2.1.4.21.1.1.127.0.0.1: 127.0.0.1 (127.0.0.1)
        1.3.6.1.4.1.23.2.5.1.1.1.4.2: 00159937762b
        1.3.6.1.2.1.1.3.0: 318870100
*/

func kyoceraResponseBytes() []byte {
	return []byte{
		0x30, 0x81, 0xc2, 0x02, 0x01, 0x01, 0x04, 0x06, 0x70, 0x75, 0x62, 0x6c,
		0x69, 0x63, 0xa2, 0x81, 0xb4, 0x02, 0x04, 0x3f, 0x97, 0x70, 0x44, 0x02,
		0x01, 0x00, 0x02, 0x01, 0x00, 0x30, 0x81, 0xa5, 0x30, 0x0d, 0x06, 0x08,
		0x2b, 0x06, 0x01, 0x02, 0x01, 0x01, 0x07, 0x00, 0x02, 0x01, 0x68, 0x30,
		0x12, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01, 0x0a,
		0x01, 0x41, 0x04, 0x10, 0x28, 0x33, 0x71, 0x30, 0x12, 0x06, 0x0a, 0x2b,
		0x06, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01, 0x05, 0x01, 0x42, 0x04, 0x05,
		0xf5, 0xe1, 0x00, 0x30, 0x19, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x02, 0x01,
		0x01, 0x04, 0x00, 0x04, 0x0d, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x69, 0x73,
		0x74, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x30, 0x0f, 0x06, 0x0b, 0x2b, 0x06,
		0x01, 0x02, 0x01, 0x2b, 0x05, 0x01, 0x01, 0x0f, 0x01, 0x05, 0x00, 0x30,
		0x15, 0x06, 0x0d, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x04, 0x15, 0x01, 0x01,
		0x7f, 0x00, 0x00, 0x01, 0x40, 0x04, 0x7f, 0x00, 0x00, 0x01, 0x30, 0x17,
		0x06, 0x0d, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x17, 0x02, 0x05, 0x01, 0x01,
		0x01, 0x04, 0x02, 0x04, 0x06, 0x00, 0x15, 0x99, 0x37, 0x76, 0x2b, 0x30,
		0x10, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x01, 0x03, 0x00, 0x43,
		0x04, 0x13, 0x01, 0x92, 0x54,
	}
}

/*
ciscoResponseBytes corresponds to the response section of this snmpget:

% snmpget -On -v2c -c public 203.50.251.17 1.3.6.1.2.1.1.7.0 1.3.6.1.2.1.2.2.1.2.6 1.3.6.1.2.1.2.2.1.5.3 1.3.6.1.2.1.2.2.1.7.2 1.3.6.1.2.1.2.2.1.9.3 1.3.6.1.2.1.3.1.1.2.10.1.10.11.0.17 1.3.6.1.2.1.3.1.1.3.10.1.10.11.0.2 1.3.6.1.2.1.4.20.1.1.110.143.197.1 1.3.6.1.66.1 1.3.6.1.2.1.1.2.0
.1.3.6.1.2.1.1.7.0 = INTEGER: 78
.1.3.6.1.2.1.2.2.1.2.6 = STRING: GigabitEthernet0
.1.3.6.1.2.1.2.2.1.5.3 = Gauge32: 4294967295
.1.3.6.1.2.1.2.2.1.7.2 = No Such Instance currently exists at this OID
.1.3.6.1.2.1.2.2.1.9.3 = Timeticks: (2970) 0:00:29.70
.1.3.6.1.2.1.3.1.1.2.10.1.10.11.0.17 = Hex-STRING: 00 07 7D 4D 09 00
.1.3.6.1.2.1.3.1.1.3.10.1.10.11.0.2 = Network Address: 0A:0B:00:02
.1.3.6.1.2.1.4.20.1.1.110.143.197.1 = IPAddress: 110.143.197.1
.1.3.6.1.66.1 = No Such Object available on this agent at this OID
.1.3.6.1.2.1.1.2.0 = OID: .1.3.6.1.4.1.9.1.1166
*/

func ciscoResponseBytes() []byte {
	return []byte{
		0x30, 0x81,
		0xf1, 0x02, 0x01, 0x01, 0x04, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63,
		0xa2, 0x81, 0xe3, 0x02, 0x03, 0x4a, 0x69, 0x7d, 0x02, 0x01, 0x00, 0x02,
		0x01, 0x00, 0x30, 0x81, 0xd5, 0x30, 0x0d, 0x06, 0x08, 0x2b, 0x06, 0x01,
		0x02, 0x01, 0x01, 0x07, 0x00, 0x02, 0x01, 0x4e, 0x30, 0x1e, 0x06, 0x0a,
		0x2b, 0x06, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01, 0x02, 0x06, 0x04, 0x10,
		0x47, 0x69, 0x67, 0x61, 0x62, 0x69, 0x74, 0x45, 0x74, 0x68, 0x65, 0x72,
		0x6e, 0x65, 0x74, 0x30, 0x30, 0x13, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x02,
		0x01, 0x02, 0x02, 0x01, 0x05, 0x03, 0x42, 0x05, 0x00, 0xff, 0xff, 0xff,
		0xff, 0x30, 0x0e, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x02, 0x02,
		0x01, 0x07, 0x02, 0x81, 0x00, 0x30, 0x10, 0x06, 0x0a, 0x2b, 0x06, 0x01,
		0x02, 0x01, 0x02, 0x02, 0x01, 0x09, 0x03, 0x43, 0x02, 0x0b, 0x9a, 0x30,
		0x19, 0x06, 0x0f, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x03, 0x01, 0x01, 0x02,
		0x0a, 0x01, 0x0a, 0x0b, 0x00, 0x11, 0x04, 0x06, 0x00, 0x07, 0x7d, 0x4d,
		0x09, 0x00, 0x30, 0x17, 0x06, 0x0f, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x03,
		0x01, 0x01, 0x03, 0x0a, 0x01, 0x0a, 0x0b, 0x00, 0x02, 0x40, 0x04, 0x0a,
		0x0b, 0x00, 0x02, 0x30, 0x17, 0x06, 0x0f, 0x2b, 0x06, 0x01, 0x02, 0x01,
		0x04, 0x14, 0x01, 0x01, 0x6e, 0x81, 0x0f, 0x81, 0x45, 0x01, 0x40, 0x04,
		0x6e, 0x8f, 0xc5, 0x01, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x06, 0x01, 0x42,
		0x01, 0x80, 0x00, 0x30, 0x15, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x02, 0x01,
		0x01, 0x02, 0x00, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x09, 0x01,
		0x89, 0x0e,
	}
}

/*
kyoceraRequestBytes corresponds to the request section of this snmpget:

snmpget -On -v2c -c public 192.168.1.10 1.3.6.1.2.1.1.7.0 1.3.6.1.2.1.2.2.1.10.1 1.3.6.1.2.1.2.2.1.5.1 1.3.6.1.2.1.1.4.0 1.3.6.1.2.1.43.5.1.1.15.1 1.3.6.1.2.1.4.21.1.1.127.0.0.1 1.3.6.1.4.1.23.2.5.1.1.1.4.2 1.3.6.1.2.1.1.3.0
.1.3.6.1.2.1.1.7.0 = INTEGER: 104
.1.3.6.1.2.1.2.2.1.10.1 = Counter32: 144058856
.1.3.6.1.2.1.2.2.1.5.1 = Gauge32: 100000000
.1.3.6.1.2.1.1.4.0 = STRING: "Administrator"
.1.3.6.1.2.1.43.5.1.1.15.1 = NULL
.1.3.6.1.2.1.4.21.1.1.127.0.0.1 = IPAddress: 127.0.0.1
.1.3.6.1.4.1.23.2.5.1.1.1.4.2 = Hex-STRING: 00 15 99 37 76 2B
.1.3.6.1.2.1.1.3.0 = Timeticks: (120394900) 13 days, 22:25:49.00
*/

func kyoceraRequestBytes() []byte {
	return []byte{
		0x30, 0x81,
		0x9e, 0x02, 0x01, 0x01, 0x04, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63,
		0xa0, 0x81, 0x90, 0x02, 0x04, 0x6f, 0x8c, 0xee, 0x64, 0x02, 0x01, 0x00,
		0x02, 0x01, 0x00, 0x30, 0x81, 0x81, 0x30, 0x0c, 0x06, 0x08, 0x2b, 0x06,
		0x01, 0x02, 0x01, 0x01, 0x07, 0x00, 0x05, 0x00, 0x30, 0x0e, 0x06, 0x0a,
		0x2b, 0x06, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01, 0x0a, 0x01, 0x05, 0x00,
		0x30, 0x0e, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01,
		0x05, 0x01, 0x05, 0x00, 0x30, 0x0c, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x02,
		0x01, 0x01, 0x04, 0x00, 0x05, 0x00, 0x30, 0x0f, 0x06, 0x0b, 0x2b, 0x06,
		0x01, 0x02, 0x01, 0x2b, 0x05, 0x01, 0x01, 0x0f, 0x01, 0x05, 0x00, 0x30,
		0x11, 0x06, 0x0d, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x04, 0x15, 0x01, 0x01,
		0x7f, 0x00, 0x00, 0x01, 0x05, 0x00, 0x30, 0x11, 0x06, 0x0d, 0x2b, 0x06,
		0x01, 0x04, 0x01, 0x17, 0x02, 0x05, 0x01, 0x01, 0x01, 0x04, 0x02, 0x05,
		0x00, 0x30, 0x0c, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x01, 0x03,
		0x00, 0x05, 0x00,
	}
}

// === snmpset dumps ===

/*
port_on_*1() correspond to this snmpset and response:

snmpset -v 1 -c privatelab 192.168.100.124 .1.3.6.1.4.1.318.1.1.4.4.2.1.3.5 i 1

Simple Network Management Protocol
  version: version-1 (0)
  community: privatelab
  data: set-request (3)
    set-request
      request-id: 526895288
      error-status: noError (0)
      error-index: 0
      variable-bindings: 1 item
        1.3.6.1.4.1.318.1.1.4.4.2.1.3.5:
          Object Name: 1.3.6.1.4.1.318.1.1.4.4.2.1.3.5 (iso.3.6.1.4.1.318.1.1.4.4.2.1.3.5)
          Value (Integer32): 1

Simple Network Management Protocol
  version: version-1 (0)
  community: privatelab
  data: get-response (2)
    get-response
      request-id: 526895288
      error-status: noError (0)
      error-index: 0
      variable-bindings: 1 item
        1.3.6.1.4.1.318.1.1.4.4.2.1.3.5:
          Object Name: 1.3.6.1.4.1.318.1.1.4.4.2.1.3.5 (iso.3.6.1.4.1.318.1.1.4.4.2.1.3.5)
          Value (Integer32): 1
*/

func portOnOutgoing1() []byte {
	return []byte{
		0x30, 0x35, 0x02, 0x01, 0x00, 0x04, 0x0a, 0x70, 0x72, 0x69, 0x76, 0x61,
		0x74, 0x65, 0x6c, 0x61, 0x62, 0xa3, 0x24, 0x02, 0x04, 0x1f, 0x67, 0xc8,
		0xb8, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x30, 0x16, 0x30, 0x14, 0x06,
		0x0f, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x3e, 0x01, 0x01, 0x04, 0x04,
		0x02, 0x01, 0x03, 0x05, 0x02, 0x01, 0x01,
	}
}

func portOnIncoming1() []byte {
	return []byte{
		0x30, 0x82, 0x00, 0x35, 0x02, 0x01, 0x00, 0x04, 0x0a, 0x70, 0x72, 0x69,
		0x76, 0x61, 0x74, 0x65, 0x6c, 0x61, 0x62, 0xa2, 0x24, 0x02, 0x04, 0x1f,
		0x67, 0xc8, 0xb8, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x30, 0x16, 0x30,
		0x14, 0x06, 0x0f, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x3e, 0x01, 0x01,
		0x04, 0x04, 0x02, 0x01, 0x03, 0x05, 0x02, 0x01, 0x01,
	}
}

/*
port_off_*1() correspond to this snmpset and response:

snmpset -v 1 -c privatelab 192.168.100.124 .1.3.6.1.4.1.318.1.1.4.4.2.1.3.5 i 2

Simple Network Management Protocol
  version: version-1 (0)
  community: privatelab
  data: set-request (3)
    set-request
      request-id: 1826072803
      error-status: noError (0)
      error-index: 0
      variable-bindings: 1 item
        1.3.6.1.4.1.318.1.1.4.4.2.1.3.5:
          Object Name: 1.3.6.1.4.1.318.1.1.4.4.2.1.3.5 (iso.3.6.1.4.1.318.1.1.4.4.2.1.3.5)
          Value (Integer32): 2

Simple Network Management Protocol
  version: version-1 (0)
  community: privatelab
  data: get-response (2)
    get-response
      request-id: 1826072803
      error-status: noError (0)
      error-index: 0
      variable-bindings: 1 item
        1.3.6.1.4.1.318.1.1.4.4.2.1.3.5:
          Object Name: 1.3.6.1.4.1.318.1.1.4.4.2.1.3.5 (iso.3.6.1.4.1.318.1.1.4.4.2.1.3.5)
          Value (Integer32): 2
*/

func portOffOutgoing1() []byte {
	return []byte{
		0x30, 0x35, 0x02, 0x01, 0x00, 0x04, 0x0a, 0x70, 0x72, 0x69, 0x76, 0x61,
		0x74, 0x65, 0x6c, 0x61, 0x62, 0xa3, 0x24, 0x02, 0x04, 0x6c, 0xd7, 0xa8,
		0xe3, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x30, 0x16, 0x30, 0x14, 0x06,
		0x0f, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x3e, 0x01, 0x01, 0x04, 0x04,
		0x02, 0x01, 0x03, 0x05, 0x02, 0x01, 0x02,
	}
}

func portOffIncoming1() []byte {
	return []byte{
		0x30, 0x82, 0x00, 0x35, 0x02, 0x01, 0x00, 0x04, 0x0a, 0x70, 0x72, 0x69,
		0x76, 0x61, 0x74, 0x65, 0x6c, 0x61, 0x62, 0xa2, 0x24, 0x02, 0x04, 0x6c,
		0xd7, 0xa8, 0xe3, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x30, 0x16, 0x30,
		0x14, 0x06, 0x0f, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x3e, 0x01, 0x01,
		0x04, 0x04, 0x02, 0x01, 0x03, 0x05, 0x02, 0x01, 0x02,
	}
}

// MrSpock START

/*
setOctet1:
Simple Network Management Protocol
  version: v2c (1)
  community: private
  data: set-request (3)
    set-request
      request-id: 756726019
      error-status: noError (0)
      error-index: 0
      variable-bindings: 1 item
        1.3.6.1.4.1.2863.205.1.1.75.1.0: 80
          Object Name: 1.3.6.1.4.1.2863.205.1.1.75.1.0 (iso.3.6.1.4.1.2863.205.1.1.75.1.0)
          Value (OctetString): 80

setOctet2:
Simple Network Management Protocol
    version: v2c (1)
    community: private
    data: set-request (3)
        set-request
            request-id: 1000552357
            error-status: noError (0)
            error-index: 0
            variable-bindings: 1 item
                1.3.6.1.4.1.2863.205.1.1.75.2.0: 74656c6e6574
                    Object Name: 1.3.6.1.4.1.2863.205.1.1.75.2.0 (iso.3.6.1.4.1.2863.205.1.1.75.2.0)
                    Value (OctetString): 74656c6e6574 ("telnet")
*/

func setOctet1() []byte {
	return []byte{
		0x30, 0x31, 0x02, 0x01, 0x01, 0x04, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61,
		0x74, 0x65, 0xa3, 0x23, 0x02, 0x04, 0x2d, 0x1a, 0xb9, 0x03, 0x02, 0x01,
		0x00, 0x02, 0x01, 0x00, 0x30, 0x15, 0x30, 0x13, 0x06, 0x0e, 0x2b, 0x06,
		0x01, 0x04, 0x01, 0x96, 0x2f, 0x81, 0x4d, 0x01, 0x01, 0x4b, 0x01, 0x00,
		0x04, 0x01, 0x80,
	}
}

func setOctet2() []byte {
	return []byte{
		0x30, 0x36, 0x02, 0x01, 0x01, 0x04, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61,
		0x74, 0x65, 0xa3, 0x28, 0x02, 0x04, 0x3b, 0xa3, 0x37, 0xa5, 0x02, 0x01,
		0x00, 0x02, 0x01, 0x00, 0x30, 0x1a, 0x30, 0x18, 0x06, 0x0e, 0x2b, 0x06,
		0x01, 0x04, 0x01, 0x96, 0x2f, 0x81, 0x4d, 0x01, 0x01, 0x4b, 0x02, 0x00,
		0x04, 0x06, 0x74, 0x65, 0x6c, 0x6e, 0x65, 0x74,
	}
}

/* setInteger1:
snmpset -c private -v2c 10.80.0.14 \
	.1.3.6.1.4.1.2863.205.10.1.33.2.5.1.2.2 i 5001 \
	.1.3.6.1.4.1.2863.205.10.1.33.2.5.1.3.2 i 5001 \
	.1.3.6.1.4.1.2863.205.10.1.33.2.5.1.4.2 i 2 \
	.1.3.6.1.4.1.2863.205.10.1.33.2.5.1.5.2 i 1

Simple Network Management Protocol
 version: v2c (1)
 community: private
 data: set-request (3)
  set-request
   request-id: 1664317637
   error-status: noError (0)
   error-index: 0
   variable-bindings: 4 items
    1.3.6.1.4.1.2863.205.10.1.33.2.5.1.2.2:
     Object Name: 1.3.6.1.4.1.2863.205.10.1.33.2.5.1.2.2 (iso.3.6.1.4.1.2863.205.10.1.33.2.5.1.2.2)
     Value (Integer32): 5001
    1.3.6.1.4.1.2863.205.10.1.33.2.5.1.3.2:
     Object Name: 1.3.6.1.4.1.2863.205.10.1.33.2.5.1.3.2 (iso.3.6.1.4.1.2863.205.10.1.33.2.5.1.3.2)
     Value (Integer32): 5001
    1.3.6.1.4.1.2863.205.10.1.33.2.5.1.4.2:
     Object Name: 1.3.6.1.4.1.2863.205.10.1.33.2.5.1.4.2 (iso.3.6.1.4.1.2863.205.10.1.33.2.5.1.4.2)
     Value (Integer32): 2
    1.3.6.1.4.1.2863.205.10.1.33.2.5.1.5.2:
     Object Name: 1.3.6.1.4.1.2863.205.10.1.33.2.5.1.5.2 (iso.3.6.1.4.1.2863.205.10.1.33.2.5.1.5.2)
     Value (Integer32): 1
*/

func setInteger1() []byte {
	return []byte{
		0x30, 0x7e, 0x02, 0x01, 0x01, 0x04, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61,
		0x74, 0x65, 0xa3, 0x70, 0x02, 0x04, 0x63, 0x33, 0x78, 0xc5, 0x02, 0x01,
		0x00, 0x02, 0x01, 0x00, 0x30, 0x62, 0x30, 0x17, 0x06, 0x11, 0x2b, 0x06,
		0x01, 0x04, 0x01, 0x96, 0x2f, 0x81, 0x4d, 0x0a, 0x01, 0x21, 0x02, 0x05,
		0x01, 0x02, 0x02, 0x02, 0x02, 0x13, 0x89, 0x30, 0x17, 0x06, 0x11, 0x2b,
		0x06, 0x01, 0x04, 0x01, 0x96, 0x2f, 0x81, 0x4d, 0x0a, 0x01, 0x21, 0x02,
		0x05, 0x01, 0x03, 0x02, 0x02, 0x02, 0x13, 0x89, 0x30, 0x16, 0x06, 0x11,
		0x2b, 0x06, 0x01, 0x04, 0x01, 0x96, 0x2f, 0x81, 0x4d, 0x0a, 0x01, 0x21,
		0x02, 0x05, 0x01, 0x04, 0x02, 0x02, 0x01, 0x02, 0x30, 0x16, 0x06, 0x11,
		0x2b, 0x06, 0x01, 0x04, 0x01, 0x96, 0x2f, 0x81, 0x4d, 0x0a, 0x01, 0x21,
		0x02, 0x05, 0x01, 0x05, 0x02, 0x02, 0x01, 0x01,
	}
}

// MrSpock FINISH

func ciscoGetnextResponseBytes() []byte {
	return []byte{
		0x30, 0x81,
		0xc8, 0x02, 0x01, 0x01, 0x04, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63,
		0xa2, 0x81, 0xba, 0x02, 0x04, 0x5b, 0x1d, 0xb6, 0xee, 0x02, 0x01, 0x00,
		0x02, 0x01, 0x00, 0x30, 0x81, 0xab, 0x30, 0x19, 0x06, 0x11, 0x2b, 0x06,
		0x01, 0x02, 0x01, 0x03, 0x01, 0x01, 0x03, 0x02, 0x01, 0x81, 0x40, 0x81,
		0x28, 0x68, 0x02, 0x40, 0x04, 0xc0, 0xa8, 0x68, 0x02, 0x30, 0x0f, 0x06,
		0x0a, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x5c, 0x01, 0x02, 0x01, 0x00, 0x41,
		0x01, 0x00, 0x30, 0x45, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x01,
		0x09, 0x01, 0x03, 0x03, 0x04, 0x37, 0x54, 0x68, 0x65, 0x20, 0x4d, 0x49,
		0x42, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x66, 0x6f, 0x72,
		0x20, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x20, 0x49, 0x50,
		0x20, 0x61, 0x6e, 0x64, 0x20, 0x49, 0x43, 0x4d, 0x50, 0x20, 0x69, 0x6d,
		0x70, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e,
		0x73, 0x30, 0x0f, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x01, 0x09,
		0x01, 0x04, 0x02, 0x43, 0x01, 0x15, 0x30, 0x0d, 0x06, 0x08, 0x2b, 0x06,
		0x01, 0x02, 0x01, 0x02, 0x01, 0x00, 0x02, 0x01, 0x03, 0x30, 0x16, 0x06,
		0x08, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x01, 0x02, 0x00, 0x06, 0x0a, 0x2b,
		0x06, 0x01, 0x04, 0x01, 0xbf, 0x08, 0x03, 0x02, 0x0a,
	}
}

func ciscoGetnextRequestBytes() []byte {
	return []byte{
		0x30, 0x7e,
		0x02, 0x01, 0x01, 0x04, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0xa1,
		0x71, 0x02, 0x04, 0x5b, 0x1d, 0xb6, 0xee, 0x02, 0x01, 0x00, 0x02, 0x01,
		0x00, 0x30, 0x63, 0x30, 0x15, 0x06, 0x11, 0x2b, 0x06, 0x01, 0x02, 0x01,
		0x03, 0x01, 0x01, 0x03, 0x02, 0x01, 0x81, 0x40, 0x81, 0x28, 0x68, 0x01,
		0x05, 0x00, 0x30, 0x0c, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x5c,
		0x01, 0x02, 0x05, 0x00, 0x30, 0x0e, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x02,
		0x01, 0x01, 0x09, 0x01, 0x03, 0x02, 0x05, 0x00, 0x30, 0x0e, 0x06, 0x0a,
		0x2b, 0x06, 0x01, 0x02, 0x01, 0x01, 0x09, 0x01, 0x04, 0x01, 0x05, 0x00,
		0x30, 0x0e, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x01, 0x09, 0x01,
		0x04, 0x08, 0x05, 0x00, 0x30, 0x0c, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x02,
		0x01, 0x01, 0x01, 0x00, 0x05, 0x00,
	}
}

/*
	cisco getbulk bytes corresponds to this snmpbulkget command:

$ snmpbulkget -v2c -cpublic  127.0.0.1:161 1.3.6.1.2.1.1.9.1.3.52
iso.3.6.1.2.1.1.9.1.4.1 = Timeticks: (21) 0:00:00.21
iso.3.6.1.2.1.1.9.1.4.2 = Timeticks: (21) 0:00:00.21
iso.3.6.1.2.1.1.9.1.4.3 = Timeticks: (21) 0:00:00.21
iso.3.6.1.2.1.1.9.1.4.4 = Timeticks: (21) 0:00:00.21
iso.3.6.1.2.1.1.9.1.4.5 = Timeticks: (21) 0:00:00.21
iso.3.6.1.2.1.1.9.1.4.6 = Timeticks: (23) 0:00:00.23
iso.3.6.1.2.1.1.9.1.4.7 = Timeticks: (23) 0:00:00.23
iso.3.6.1.2.1.1.9.1.4.8 = Timeticks: (23) 0:00:00.23
iso.3.6.1.2.1.2.1.0 = INTEGER: 3
iso.3.6.1.2.1.2.2.1.1.1 = INTEGER: 1
*/
func ciscoGetbulkRequestBytes() []byte {
	return []byte{
		0x30, 0x2b,
		0x02, 0x01, 0x01, 0x04, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0xa5,
		0x1e, 0x02, 0x04, 0x7d, 0x89, 0x68, 0xda, 0x02, 0x01, 0x00, 0x02, 0x01,
		0x0a, 0x30, 0x10, 0x30, 0x0e, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x02, 0x01,
		0x01, 0x09, 0x01, 0x03, 0x34, 0x05, 0x00, 0x00,
	}
}

func ciscoGetbulkResponseBytes() []byte {
	return []byte{
		0x30, 0x81,
		0xc5, 0x02, 0x01, 0x01, 0x04, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63,
		0xa2, 0x81, 0xb7, 0x02, 0x04, 0x0e, 0xe6, 0xb3, 0x8a, 0x02, 0x01, 0x00,
		0x02, 0x01, 0x00, 0x30, 0x81, 0xa8, 0x30, 0x0f, 0x06, 0x0a, 0x2b, 0x06,
		0x01, 0x02, 0x01, 0x01, 0x09, 0x01, 0x04, 0x01, 0x43, 0x01, 0x15, 0x30,
		0x0f, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x01, 0x09, 0x01, 0x04,
		0x02, 0x43, 0x01, 0x15, 0x30, 0x0f, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x02,
		0x01, 0x01, 0x09, 0x01, 0x04, 0x03, 0x43, 0x01, 0x15, 0x30, 0x0f, 0x06,
		0x0a, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x01, 0x09, 0x01, 0x04, 0x04, 0x43,
		0x01, 0x15, 0x30, 0x0f, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x01,
		0x09, 0x01, 0x04, 0x05, 0x43, 0x01, 0x15, 0x30, 0x0f, 0x06, 0x0a, 0x2b,
		0x06, 0x01, 0x02, 0x01, 0x01, 0x09, 0x01, 0x04, 0x06, 0x43, 0x01, 0x17,
		0x30, 0x0f, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x01, 0x09, 0x01,
		0x04, 0x07, 0x43, 0x01, 0x17, 0x30, 0x0f, 0x06, 0x0a, 0x2b, 0x06, 0x01,
		0x02, 0x01, 0x01, 0x09, 0x01, 0x04, 0x08, 0x43, 0x01, 0x17, 0x30, 0x0d,
		0x06, 0x08, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x02, 0x01, 0x00, 0x02, 0x01,
		0x03, 0x30, 0x0f, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x02, 0x02,
		0x01, 0x01, 0x01, 0x02, 0x01, 0x01,
	}
}

/*
Issue 35, empty responses.
Simple Network Management Protocol

	version: v2c (1)
	community: public
	data: get-request (0)
	    get-request
	        request-id: 1883298028
	        error-status: noError (0)
	        error-index: 0
	        variable-bindings: 0 items
*/
func emptyErrRequest() []byte {
	return []byte{
		0x30, 0x1b, 0x02, 0x01, 0x01, 0x04, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69,
		0x63, 0xa0, 0x0e, 0x02, 0x04, 0x70, 0x40, 0xd8, 0xec, 0x02, 0x01, 0x00,
		0x02, 0x01, 0x00, 0x30, 0x00,
	}
}

/*
Issue 35, empty responses.

Simple Network Management Protocol

	version: v2c (1)
	community: public
	data: get-response (2)
	    get-response
	        request-id: 1883298028
	        error-status: noError (0)
	        error-index: 0
	        variable-bindings: 0 items
*/
func emptyErrResponse() []byte {
	return []byte{
		0x30, 0x1b, 0x02, 0x01, 0x01, 0x04, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69,
		0x63, 0xa2, 0x0e, 0x02, 0x04, 0x70, 0x40, 0xd8, 0xec, 0x02, 0x01, 0x00,
		0x02, 0x01, 0x00, 0x30, 0x00,
	}
}

/*
Issue 15, test Counter64.

Simple Network Management Protocol

	version: v2c (1)
	community: public
	data: get-response (2)
	    get-response
	        request-id: 190378322
	        error-status: noError (0)
	        error-index: 0
	        variable-bindings: 1 item
	            1.3.6.1.2.1.31.1.1.1.10.1: 1527943
	                Object Name: 1.3.6.1.2.1.31.1.1.1.10.1 (iso.3.6.1.2.1.31.1.1.1.10.1)
	                Value (Counter64): 1527943
*/
func counter64Response() []byte {
	return []byte{
		0x30, 0x2f, 0x02, 0x01, 0x01, 0x04, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69,
		0x63, 0xa2, 0x22, 0x02, 0x04, 0x0b, 0x58, 0xf1, 0x52, 0x02, 0x01, 0x00,
		0x02, 0x01, 0x00, 0x30, 0x14, 0x30, 0x12, 0x06, 0x0b, 0x2b, 0x06, 0x01,
		0x02, 0x01, 0x1f, 0x01, 0x01, 0x01, 0x0a, 0x01, 0x46, 0x03, 0x17, 0x50,
		0x87,
	}
}

/*
Issue 370, test Opaque.

Simple Network Management Protocol

	version: 1 (1)
	community: public
	data: get-response (2)
	    get-response
	        request-id: 2033938493
	        error-status: noError (0)
	        error-index: 0
	        variable-bindings: 1 item
	            1.3.6.1.4.1.34187.74195.2.1.24590: 41f00000
	                Object Name: 1.3.6.1.4.1.34187.74195.2.1.24590 (iso.3.6.1.4.1.34187.74195.2.1.24590)
	                Value (Opaque): 41f00000
*/
func opaqueResponse() []byte {
	return []byte{
		0x30, 0x35, 0x02, 0x01, 0x00, 0x04, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69,
		0x63, 0xa2, 0x28, 0x02, 0x04, 0x79, 0x3b, 0x70, 0x3d, 0x02, 0x01, 0x00,
		0x02, 0x01, 0x00, 0x30, 0x1a, 0x30, 0x18, 0x06, 0x10, 0x2b, 0x06, 0x01,
		0x04, 0x01, 0x82, 0x8b, 0x0b, 0x84, 0xc3, 0x53, 0x02, 0x01, 0x81, 0xc0,
		0x0e, 0x44, 0x04, 0x41, 0xf0, 0x00, 0x00,
	}
}

/*
Opaque Float, observed from Synology NAS UPS MIB

	snmpget -v 2c -c public host 1.3.6.1.4.1.6574.4.2.12.1.0
*/
func opaqueFloatResponse() []byte {
	return []byte{
		0x30, 0x34, 0x02, 0x01, 0x01, 0x04, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69,
		0x63, 0xa2, 0x27, 0x02, 0x04, 0x23, 0xd5, 0xd7, 0x05, 0x02, 0x01, 0x00,
		0x02, 0x01, 0x00, 0x30, 0x19, 0x30, 0x17, 0x06, 0x0c, 0x2b, 0x06, 0x01,
		0x04, 0x01, 0xb3, 0x2e, 0x04, 0x02, 0x0c, 0x01, 0x00, 0x44, 0x07, 0x9f,
		0x78, 0x04, 0x41, 0x20, 0x00, 0x00,
	}
}

/*
Opaque Double, not observed, crafted based on description:

	https://tools.ietf.org/html/draft-perkins-float-00
*/
func opaqueDoubleResponse() []byte {
	return []byte{
		0x30, 0x38, 0x02, 0x01, 0x01, 0x04, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69,
		0x63, 0xa2, 0x2b, 0x02, 0x04, 0x23, 0xd5, 0xd7, 0x05, 0x02, 0x01, 0x00,
		0x02, 0x01, 0x00, 0x30, 0x1d, 0x30, 0x1b, 0x06, 0x0c, 0x2b, 0x06, 0x01,
		0x04, 0x01, 0xb3, 0x2e, 0x04, 0x02, 0x0c, 0x01, 0x00, 0x44, 0x0b, 0x9f,
		0x79, 0x08, 0x40, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	}
}

func TestUnmarshalEmptyPanic(t *testing.T) {
	var in = []byte{}
	var res = new(SnmpPacket)

	_, err := Default.unmarshalHeader(in, res)
	if err == nil {
		t.Errorf("unmarshalHeader did not gracefully detect empty packet")
	}
}

func TestV3USMInitialPacket(t *testing.T) {
	logger := NewLogger(log.New(io.Discard, "", 0))
	var emptyPdus []SnmpPDU
	blankPacket := &SnmpPacket{
		Version:            Version3,
		MsgFlags:           Reportable | NoAuthNoPriv,
		SecurityModel:      UserSecurityModel,
		SecurityParameters: &UsmSecurityParameters{Logger: logger},
		PDUType:            GetRequest,
		Logger:             logger,
		Variables:          emptyPdus,
	}
	iBytes, err := blankPacket.marshalMsg()
	if err != nil {
		t.Errorf("#TestV3USMInitialPacket: marshalMsg() err returned: %v", err)
	}
	engine := GoSNMP{Logger: Default.Logger}
	pktNew, errDecode := engine.SnmpDecodePacket(iBytes)
	if errDecode != nil {
		t.Logf("-->Bytes=%v", iBytes)
		t.Logf("-->Expect=%v", blankPacket)
		t.Logf("-->got=%v", pktNew)
		t.Errorf("#TestV3USMInitialPacket: SnmpDecodePacket() err returned: %v. ", errDecode)
	}

}

func TestSendOneRequest_dups(t *testing.T) {
	srvr, err := net.ListenUDP("udp4", &net.UDPAddr{})
	if err != nil {
		t.Fatalf("udp4 error listening: %s", err)
	}
	defer srvr.Close()

	x := &GoSNMP{
		Version: Version2c,
		Target:  srvr.LocalAddr().(*net.UDPAddr).IP.String(),
		Port:    uint16(srvr.LocalAddr().(*net.UDPAddr).Port),
		Timeout: time.Millisecond * 100,
		Retries: 2,
	}
	if err := x.Connect(); err != nil {
		t.Fatalf("error connecting: %s", err)
	}

	go func() {
		buf := make([]byte, 256)
		for {
			n, addr, err := srvr.ReadFrom(buf)
			if err != nil {
				return
			}
			buf := buf[:n]

			var reqPkt SnmpPacket
			var cursor int
			cursor, err = x.unmarshalHeader(buf, &reqPkt)
			if err != nil {
				t.Errorf("error: %s", err)
			}
			// if x.Version == Version3 {
			//	buf, cursor, err = x.decryptPacket(buf, cursor, &reqPkt)
			//	if err != nil {
			//		t.Errorf("error: %s", err)
			//	}
			//}
			err = x.unmarshalPayload(buf, cursor, &reqPkt)
			if err != nil {
				t.Errorf("error: %s", err)
			}

			rspPkt := x.mkSnmpPacket(GetResponse, []SnmpPDU{
				{
					Name:  ".1.2",
					Type:  Integer,
					Value: 123,
				},
			}, 0, 0)
			rspPkt.RequestID = reqPkt.RequestID
			outBuf, err := rspPkt.marshalMsg()
			if err != nil {
				t.Errorf("ERR: %s", err)
			}
			srvr.WriteTo(outBuf, addr)
			for i := 0; i <= x.Retries; i++ {
				srvr.WriteTo(outBuf, addr)
			}
		}
	}()

	pdus := []SnmpPDU{{Name: ".1.2", Type: Null}}
	// This is not actually a GetResponse, but we need something our test server can unmarshal.
	reqPkt := x.mkSnmpPacket(GetResponse, pdus, 0, 0)

	_, err = x.sendOneRequest(reqPkt, true)
	if err != nil {
		t.Errorf("error: %s", err)
		return
	}

	_, err = x.sendOneRequest(reqPkt, true)
	if err != nil {
		t.Errorf("error: %s", err)
		return
	}
}

func TestSendOneRequest_TCP_EOF_Reconnect(t *testing.T) {
	listen, err := net.Listen("tcp", "127.0.0.1:0") // dynamic port
	if err != nil {
		t.Fatalf("failed to set up test listener: %v", err)
	}
	defer listen.Close()
	port := listen.Addr().(*net.TCPAddr).Port

	// Start mock SNMP server in goroutine
	go func() {
		conn, err := listen.Accept()
		if err != nil {
			return
		}
		defer conn.Close()

		buf := make([]byte, 2048)
		_, _ = conn.Read(buf) // Read request
		// simulate EOF by closing immediately
	}()

	params := &GoSNMP{
		Target:    "127.0.0.1",
		Port:      uint16(port),
		Transport: "tcp",
		Community: "public",
		Version:   Version2c,
		Timeout:   time.Second,
		Retries:   1,
		Logger:    NewLogger(log.New(io.Discard, "", 0)),
	}

	err = params.Connect()
	if err != nil {
		t.Fatalf("Connect failed: %v", err)
	}
	defer params.Conn.Close()

	// Use an arbitrary OID
	_, err = params.Get([]string{".1.3.6.1.2.1.1.1.0"})

	if err == nil {
		t.Fatal("expected error due to EOF, got nil")
	}
	if !strings.Contains(err.Error(), "timeout") && !strings.Contains(err.Error(), "max retries") {
		t.Fatalf("unexpected error: %v", err)
	}
}

func BenchmarkSendOneRequest(b *testing.B) {
	b.StopTimer()

	srvr, err := net.ListenUDP("udp4", &net.UDPAddr{})
	if err != nil {
		b.Fatalf("udp4 error listening: %s", err)
	}
	defer srvr.Close()

	x := &GoSNMP{
		Version: Version2c,
		Target:  srvr.LocalAddr().(*net.UDPAddr).IP.String(),
		Port:    uint16(srvr.LocalAddr().(*net.UDPAddr).Port),
		Timeout: time.Millisecond * 100,
		Retries: 2,
	}
	if err := x.Connect(); err != nil {
		b.Fatalf("error connecting: %s", err)
	}

	go func() {
		buf := make([]byte, 256)
		outBuf := counter64Response()
		for {
			_, addr, err := srvr.ReadFrom(buf)
			if err != nil {
				return
			}

			copy(outBuf[17:21], buf[11:15]) // evil: copy request ID
			srvr.WriteTo(outBuf, addr)
		}
	}()

	pdus := []SnmpPDU{{Name: ".1.3.6.1.2.1.31.1.1.1.10.1", Type: Null}}
	reqPkt := x.mkSnmpPacket(GetRequest, pdus, 0, 0)

	// make sure everything works before starting the test
	_, err = x.sendOneRequest(reqPkt, true)
	if err != nil {
		b.Fatalf("Precheck failed: %s", err)
	}

	b.StartTimer()

	for n := 0; n < b.N; n++ {
		_, err = x.sendOneRequest(reqPkt, true)
		if err != nil {
			b.Fatalf("error: %s", err)
			return
		}
	}
}

func TestUnconnectedSocket_fail(t *testing.T) {
	withUnconnectedSocket(t, false)
}

func TestUnconnectedSocket_success(t *testing.T) {
	withUnconnectedSocket(t, true)
}

func withUnconnectedSocket(t *testing.T, enable bool) {
	srvr, err := net.ListenUDP("udp", &net.UDPAddr{})
	if err != nil {
		t.Fatalf("udp error listening: %s", err)
	}
	defer srvr.Close()

	x := &GoSNMP{
		Version:                 Version2c,
		Target:                  srvr.LocalAddr().(*net.UDPAddr).IP.String(),
		Port:                    uint16(srvr.LocalAddr().(*net.UDPAddr).Port),
		Timeout:                 time.Millisecond * 100,
		Retries:                 2,
		UseUnconnectedUDPSocket: enable,
		LocalAddr:               "0.0.0.0:",
	}
	if err := x.Connect(); err != nil {
		t.Fatalf("error connecting: %s", err)
	}
	defer x.Conn.Close()

	go func() {
		buf := make([]byte, 256)
		for {
			n, addr, err := srvr.ReadFrom(buf)
			if err != nil {
				return
			}
			buf := buf[:n]

			var reqPkt SnmpPacket
			var cursor int
			cursor, err = x.unmarshalHeader(buf, &reqPkt)
			if err != nil {
				t.Errorf("error: %s", err)
			}
			err = x.unmarshalPayload(buf, cursor, &reqPkt)
			if err != nil {
				t.Errorf("error: %s", err)
			}

			rspPkt := x.mkSnmpPacket(GetResponse, []SnmpPDU{
				{
					Name:  ".1.2",
					Type:  Integer,
					Value: 123,
				},
			}, 0, 0)
			rspPkt.RequestID = reqPkt.RequestID
			outBuf, err := rspPkt.marshalMsg()
			if err != nil {
				t.Errorf("ERR: %s", err)
			}
			// Temporary socket will use different source port, it's enough to break
			// connected socket reply filters.
			nsock, err := net.ListenUDP("udp", nil)
			if err != nil {
				t.Errorf("can't create temporary reply socket: %v", err)
			}
			nsock.WriteTo(outBuf, addr)
			nsock.Close()
		}
	}()

	pdus := []SnmpPDU{{Name: ".1.2", Type: Null}}
	// This is not actually a GetResponse, but we need something our test server can unmarshal.
	reqPkt := x.mkSnmpPacket(GetResponse, pdus, 0, 0)

	_, err = x.sendOneRequest(reqPkt, true)
	if err != nil && enable {
		t.Errorf("with unconnected socket enabled got unexpected error: %v", err)
	} else if err == nil && !enable {
		t.Errorf("with unconnected socket disabled didn't get an error")
	}
}

/*
$ snmptrap -v 2c -c public 192.168.1.10 '' SNMPv2-MIB::system SNMPv2-MIB::sysDescr.0 s "red laptop" SNMPv2-MIB::sysServices.0 i "5"

Simple Network Management Protocol
    version: v2c (1)
    community: public
    data: snmpV2-trap (7)
        snmpV2-trap
            request-id: 1271509950
            error-status: noError (0)
            error-index: 0
            variable-bindings: 5 items
                1.3.6.1.2.1.1.3.0: 1034156
                    Object Name: 1.3.6.1.2.1.1.3.0 (iso.3.6.1.2.1.1.3.0)
                    Value (Timeticks): 1034156
                1.3.6.1.6.3.1.1.4.1.0: 1.3.6.1.2.1.1 (iso.3.6.1.2.1.1)
                    Object Name: 1.3.6.1.6.3.1.1.4.1.0 (iso.3.6.1.6.3.1.1.4.1.0)
                    Value (OID): 1.3.6.1.2.1.1 (iso.3.6.1.2.1.1)
                1.3.6.1.2.1.1.1.0: 726564206c6170746f70
                    Object Name: 1.3.6.1.2.1.1.1.0 (iso.3.6.1.2.1.1.1.0)
                    Value (OctetString): 726564206c6170746f70
                        Variable-binding-string: red laptop
                1.3.6.1.2.1.1.7.0:
                    Object Name: 1.3.6.1.2.1.1.7.0 (iso.3.6.1.2.1.1.7.0)
                    Value (Integer32): 5
                1.3.6.1.2.1.1.2: 1.3.6.1.4.1.2.3.4.5 (iso.3.6.1.4.1.2.3.4.5)
                    Object Name: 1.3.6.1.2.1.1.2 (iso.3.6.1.2.1.1.2)
                    Value (OID): 1.3.6.1.4.1.2.3.4.5 (iso.3.6.1.4.1.2.3.4.5)
*/

func trap1() []byte {
	return []byte{
		0x30, 0x81, 0x80, 0x02, 0x01, 0x01, 0x04, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0xa7, 0x73,
		0x02, 0x04, 0x72, 0x5c, 0xef, 0x42, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x30, 0x65, 0x30, 0x10,
		0x06, 0x08, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x01, 0x03, 0x00, 0x43, 0x04, 0x01, 0x1a, 0xef, 0xa5,
		0x30, 0x14, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x06, 0x03, 0x01, 0x01, 0x04, 0x01, 0x00, 0x06, 0x06,
		0x2b, 0x06, 0x01, 0x02, 0x01, 0x01, 0x30, 0x16, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x01,
		0x01, 0x00, 0x04, 0x0a, 0x72, 0x65, 0x64, 0x20, 0x6c, 0x61, 0x70, 0x74, 0x6f, 0x70, 0x30, 0x0d,
		0x06, 0x08, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x01, 0x07, 0x00, 0x02, 0x01, 0x05, 0x30, 0x14, 0x06,
		0x07, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x01, 0x02, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x02,
		0x03, 0x04, 0x05, 0x00, 0x00, 0x00, 0xd0, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x68, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x3a, 0x05, 0x00, 0xa1, 0x27, 0x42, 0x0c, 0x46, 0x00,
		0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x10, 0x4a, 0x7d, 0x34, 0x3a, 0xa5, 0x74, 0xda, 0x38, 0x4d,
		0x6c, 0x6c, 0x08, 0x00, 0x45, 0x00, 0x00, 0x38, 0xcc, 0xdb, 0x40, 0x00, 0xff, 0x01, 0x2b, 0x74,
		0xc0, 0xa8, 0x01, 0x0a, 0xc0, 0xa8, 0x01, 0x1a, 0x03, 0x03, 0x11, 0x67, 0x00, 0x00, 0x00, 0x00,
		0x45, 0x00, 0x00, 0x9f, 0xe6, 0x8f, 0x40, 0x00, 0x40, 0x11, 0x00, 0x00, 0xc0, 0xa8, 0x01, 0x1a,
		0xc0, 0xa8, 0x01, 0x0a, 0xaf, 0x78, 0x00, 0xa2, 0x00, 0x8b, 0x0b, 0x3a, 0x00, 0x00, 0x68, 0x00,
		0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x3a,
		0x05, 0x00, 0xca, 0x94, 0x67, 0x0c, 0x01, 0x00, 0x1c, 0x00, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65,
		0x72, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x64,
		0x75, 0x6d, 0x70, 0x63, 0x61, 0x70, 0x02, 0x00, 0x08, 0x00, 0x74, 0x3a, 0x05, 0x00, 0xdf, 0xba,
		0x27, 0x0c, 0x03, 0x00, 0x08, 0x00, 0x74, 0x3a, 0x05, 0x00, 0x18, 0x94, 0x67, 0x0c, 0x04, 0x00,
		0x08, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00}
}

// Simple Network Management Protocol
//     msgVersion: snmpv3 (3)
//     msgGlobalData
//         msgID: 91040642
//         msgMaxSize: 65507
//         msgFlags: 04
//         msgSecurityModel: USM (3)
//     msgAuthoritativeEngineID: <MISSING>
//     msgAuthoritativeEngineBoots: 0
//     msgAuthoritativeEngineTime: 0
//     msgUserName:
//     msgAuthenticationParameters: <MISSING>
//     msgPrivacyParameters: <MISSING>
//     msgData: plaintext (0)
//         plaintext

func snmpv3HelloRequest() []byte {
	return []byte{0x30, 0x52, 0x02, 0x01, 0x03, 0x30, 0x11, 0x02,
		0x04, 0x05, 0x6d, 0x2b, 0x82, 0x02, 0x03, 0x00,
		0xff, 0xe3, 0x04, 0x01, 0x04, 0x02, 0x01, 0x03,
		0x04, 0x10, 0x30, 0x0e, 0x04, 0x00, 0x02, 0x01,
		0x00, 0x02, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00,
		0x04, 0x00, 0x30, 0x28, 0x04, 0x00, 0x04, 0x14,
		0x66, 0x6f, 0x72, 0x65, 0x69, 0x67, 0x6e, 0x66,
		0x6f, 0x72, 0x6d, 0x61, 0x74, 0x73, 0x2f, 0x6c,
		0x69, 0x6e, 0x75, 0x78, 0xa0, 0x0e, 0x02, 0x04,
		0x44, 0xfa, 0x16, 0xe1, 0x02, 0x01, 0x00, 0x02,
		0x01, 0x00, 0x30, 0x00}
}

// msgData: plaintext (0)
//     plaintext
//         contextEngineID: 80004fb8054445534b544f502d4a3732533245343ab63bc8
//             1... .... = Engine ID Conformance: RFC3411 (SNMPv3)
//             Engine Enterprise ID: pysnmp (20408)
//             Engine ID Format: Octets, administratively assigned (5)
//             Engine ID Data: 4445534b544f502d4a3732533245343ab63bc8
//         contextName: foreignformats/linux
//         data: report (8)
//             report
//                 request-id: 1157240545
//                 error-status: noError (0)
//                 error-index: 0
//                 variable-bindings: 1 item
//                     1.3.6.1.6.3.15.1.1.4.0: 21
//                         Object Name: 1.3.6.1.6.3.15.1.1.4.0 (iso.3.6.1.6.3.15.1.1.4.0)
//                         Value (Counter32): 21

func snmpv3HelloResponse() []byte {
	return []byte{
		0x30, 0x81, 0x95, 0x02, 0x01, 0x03, 0x30, 0x11,
		0x02, 0x04, 0x05, 0x6d, 0x2b, 0x82, 0x02, 0x03,
		0x00, 0xff, 0xe3, 0x04, 0x01, 0x00, 0x02, 0x01,
		0x03, 0x04, 0x2a, 0x30, 0x28, 0x04, 0x18, 0x80,
		0x00, 0x4f, 0xb8, 0x05, 0x44, 0x45, 0x53, 0x4b,
		0x54, 0x4f, 0x50, 0x2d, 0x4a, 0x37, 0x32, 0x53,
		0x32, 0x45, 0x34, 0x3a, 0xb6, 0x3b, 0xc8, 0x02,
		0x01, 0x02, 0x02, 0x03, 0x00, 0xc4, 0x7a, 0x04,
		0x00, 0x04, 0x00, 0x04, 0x00, 0x30, 0x51, 0x04,
		0x18, 0x80, 0x00, 0x4f, 0xb8, 0x05, 0x44, 0x45,
		0x53, 0x4b, 0x54, 0x4f, 0x50, 0x2d, 0x4a, 0x37,
		0x32, 0x53, 0x32, 0x45, 0x34, 0x3a, 0xb6, 0x3b,
		0xc8, 0x04, 0x14, 0x66, 0x6f, 0x72, 0x65, 0x69,
		0x67, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74,
		0x73, 0x2f, 0x6c, 0x69, 0x6e, 0x75, 0x78, 0xa8,
		0x1f, 0x02, 0x04, 0x44, 0xfa, 0x16, 0xe1, 0x02,
		0x01, 0x00, 0x02, 0x01, 0x00, 0x30, 0x11, 0x30,
		0x0f, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x06, 0x03,
		0x0f, 0x01, 0x01, 0x04, 0x00, 0x41, 0x01, 0x15,
	}
}

// dump bytes in a format similar to Wireshark
func dumpBytes1(data []byte, msg string, maxlength int) {
	var buffer bytes.Buffer
	buffer.WriteString(msg)
	length := maxlength
	if len(data) < maxlength {
		length = len(data)
	}
	length *= 2 //One Byte Symbols Two Hex
	hexStr := hex.EncodeToString(data)
	for i := 0; length >= i+16; i += 16 {
		buffer.WriteString("\n")
		buffer.WriteString(strconv.Itoa(i / 2))
		buffer.WriteString("\t")
		buffer.WriteString(hexStr[i : i+2])
		buffer.WriteString(" ")
		buffer.WriteString(hexStr[i+2 : i+4])
		buffer.WriteString(" ")
		buffer.WriteString(hexStr[i+4 : i+6])
		buffer.WriteString(" ")
		buffer.WriteString(hexStr[i+6 : i+8])
		buffer.WriteString(" ")
		buffer.WriteString(hexStr[i+8 : i+10])
		buffer.WriteString(" ")
		buffer.WriteString(hexStr[i+10 : i+12])
		buffer.WriteString(" ")
		buffer.WriteString(hexStr[i+12 : i+14])
		buffer.WriteString(" ")
		buffer.WriteString(hexStr[i+14 : i+16])
	}
	leftOver := length % 16
	if leftOver != 0 {
		buffer.WriteString("\n")
		buffer.WriteString(strconv.Itoa((length - leftOver) / 2))
		buffer.WriteString("\t")
		for i := 0; leftOver >= i+2; i += 2 {
			buffer.WriteString(hexStr[i : i+2])
			buffer.WriteString(" ")
		}
	}
	buffer.WriteString("\n")
}

// dump bytes in one row, up to about screen width. Returns a string
// rather than (dumpBytes1) writing to debugging log.
func dumpBytes2(desc string, bb []byte, cursor int) string {
	cursor = cursor - 4 // give some context to dump
	if cursor < 0 {
		cursor = 0
	}
	result := desc
	for i, b := range bb[cursor:] {
		if i > 30 { // about screen width...
			break
		}
		result += fmt.Sprintf(" %02x", b)
	}
	return result
}

// TestMarshalVarbindRoundTrip verifies that marshaled varbinds can be parsed back correctly
// for all PDU types with various OID sizes including boundary cases.
func TestMarshalVarbindRoundTrip(t *testing.T) {
	logger := NewLogger(log.New(io.Discard, "", 0))

	// OID that encodes to exactly 128 bytes (requires long-form BER length)
	// Base .1.3.6.1.4.1 = 5 bytes, each 268435455 = 4 bytes, each 127 = 1 byte
	// 5 + (30 * 4) + 3 = 128 bytes
	largeOID := ".1.3.6.1.4.1"
	for i := 0; i < 30; i++ {
		largeOID += ".268435455"
	}
	largeOID += ".127.127.127"

	smallOID := ".1.3.6.1.2.1.1.1.0"

	tests := []struct {
		name string
		pdu  SnmpPDU
	}{
		// Integer with various OID sizes
		{"Integer/small OID", SnmpPDU{Name: smallOID, Type: Integer, Value: 42}},
		{"Integer/large OID", SnmpPDU{Name: largeOID, Type: Integer, Value: 42}},

		// Null
		{"Null/small OID", SnmpPDU{Name: smallOID, Type: Null, Value: nil}},
		{"Null/large OID", SnmpPDU{Name: largeOID, Type: Null, Value: nil}},

		// Counter32
		{"Counter32/small OID", SnmpPDU{Name: smallOID, Type: Counter32, Value: uint(1000)}},
		{"Counter32/large OID", SnmpPDU{Name: largeOID, Type: Counter32, Value: uint(1000)}},

		// Gauge32
		{"Gauge32/small OID", SnmpPDU{Name: smallOID, Type: Gauge32, Value: uint(500)}},
		{"Gauge32/large OID", SnmpPDU{Name: largeOID, Type: Gauge32, Value: uint(500)}},

		// TimeTicks
		{"TimeTicks/small OID", SnmpPDU{Name: smallOID, Type: TimeTicks, Value: uint32(123456)}},
		{"TimeTicks/large OID", SnmpPDU{Name: largeOID, Type: TimeTicks, Value: uint32(123456)}},

		// OctetString with various sizes
		{"OctetString/small OID/small value", SnmpPDU{Name: smallOID, Type: OctetString, Value: []byte("test")}},
		{"OctetString/large OID/small value", SnmpPDU{Name: largeOID, Type: OctetString, Value: []byte("test")}},
		{"OctetString/small OID/large value", SnmpPDU{Name: smallOID, Type: OctetString, Value: make([]byte, 200)}},

		// ObjectIdentifier as value
		{"ObjectIdentifier/small OID", SnmpPDU{Name: smallOID, Type: ObjectIdentifier, Value: ".1.3.6.1.2.1"}},
		{"ObjectIdentifier/large OID", SnmpPDU{Name: largeOID, Type: ObjectIdentifier, Value: ".1.3.6.1.2.1"}},

		// IPAddress
		{"IPAddress/small OID", SnmpPDU{Name: smallOID, Type: IPAddress, Value: "192.168.1.1"}},
		{"IPAddress/large OID", SnmpPDU{Name: largeOID, Type: IPAddress, Value: "192.168.1.1"}},

		// Counter64
		{"Counter64/small OID", SnmpPDU{Name: smallOID, Type: Counter64, Value: uint64(9999999999)}},
		{"Counter64/large OID", SnmpPDU{Name: largeOID, Type: Counter64, Value: uint64(9999999999)}},

		// NoSuchObject, NoSuchInstance, EndOfMibView
		{"NoSuchObject/small OID", SnmpPDU{Name: smallOID, Type: NoSuchObject, Value: nil}},
		{"NoSuchObject/large OID", SnmpPDU{Name: largeOID, Type: NoSuchObject, Value: nil}},
		{"NoSuchInstance/small OID", SnmpPDU{Name: smallOID, Type: NoSuchInstance, Value: nil}},
		{"EndOfMibView/small OID", SnmpPDU{Name: smallOID, Type: EndOfMibView, Value: nil}},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result, err := marshalVarbind(&tt.pdu)
			if err != nil {
				t.Fatalf("marshalVarbind() error = %v", err)
			}

			// Verify SEQUENCE tag
			if result[0] != byte(Sequence) {
				t.Fatalf("expected SEQUENCE tag 0x30, got 0x%02x", result[0])
			}

			// Parse length and get cursor position
			_, cursor, err := parseLength(result)
			if err != nil {
				t.Fatalf("parseLength() error = %v", err)
			}

			// Parse OID
			rawOid, oidLength, err := parseRawField(logger, result[cursor:], "OID")
			if err != nil {
				t.Fatalf("parseRawField(OID) error = %v", err)
			}

			parsedOID, ok := rawOid.(string)
			if !ok {
				t.Fatalf("OID type assertion failed, got %T", rawOid)
			}
			if parsedOID != tt.pdu.Name {
				t.Errorf("OID mismatch: got %q, want %q", parsedOID, tt.pdu.Name)
			}

			// Parse value
			cursor += oidLength
			var decodedVal variable
			x := &GoSNMP{Logger: logger}
			if err = x.decodeValue(result[cursor:], &decodedVal); err != nil {
				t.Fatalf("decodeValue() error = %v", err)
			}

			if decodedVal.Type != tt.pdu.Type {
				t.Errorf("Type mismatch: got %v, want %v", decodedVal.Type, tt.pdu.Type)
			}
		})
	}
}

// TestMarshalTLV verifies BER TLV encoding for various length values.
func TestMarshalTLV(t *testing.T) {
	tests := []struct {
		name         string
		tag          byte
		valueLen     int
		wantLenBytes []byte // expected BER length encoding
	}{
		// Short form: lengths 0-127 encoded in single byte
		{"length 10", byte(ObjectIdentifier), 10, []byte{0x0a}},
		{"length 127", byte(ObjectIdentifier), 127, []byte{0x7f}},

		// Long form: lengths >= 128 use 0x8n prefix where n = number of length bytes
		{"length 128", byte(ObjectIdentifier), 128, []byte{0x81, 0x80}},
		{"length 255", byte(ObjectIdentifier), 255, []byte{0x81, 0xff}},
		{"length 256", byte(ObjectIdentifier), 256, []byte{0x82, 0x01, 0x00}},

		// Different tags
		{"Sequence length 100", byte(Sequence), 100, []byte{0x64}},
		{"Sequence length 200", byte(Sequence), 200, []byte{0x81, 0xc8}},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			buf := new(bytes.Buffer)
			value := make([]byte, tt.valueLen)

			err := marshalTLV(buf, tt.tag, value)
			if err != nil {
				t.Fatalf("marshalTLV() error = %v", err)
			}

			result := buf.Bytes()

			// Check tag
			if result[0] != tt.tag {
				t.Errorf("tag = 0x%02x, want 0x%02x", result[0], tt.tag)
			}

			// Check length encoding exactly
			gotLenBytes := result[1 : 1+len(tt.wantLenBytes)]
			for i, b := range tt.wantLenBytes {
				if gotLenBytes[i] != b {
					t.Errorf("length byte[%d] = 0x%02x, want 0x%02x (full: got %x, want %x)",
						i, gotLenBytes[i], b, gotLenBytes, tt.wantLenBytes)
					break
				}
			}

			// Check total size
			expectedTotal := 1 + len(tt.wantLenBytes) + tt.valueLen
			if len(result) != expectedTotal {
				t.Errorf("total length = %d, want %d", len(result), expectedTotal)
			}
		})
	}
}
