pax_global_header00006660000000000000000000000064146547071630014527gustar00rootroot0000000000000052 comment=448fcd8a01ec358c87ca8aea78ba8b73ae123590 golang-github-gosnmp-gosnmp-1.38.0/000077500000000000000000000000001465470716300171515ustar00rootroot00000000000000golang-github-gosnmp-gosnmp-1.38.0/.circleci/000077500000000000000000000000001465470716300210045ustar00rootroot00000000000000golang-github-gosnmp-gosnmp-1.38.0/.circleci/config.yml000066400000000000000000000034401465470716300227750ustar00rootroot00000000000000--- version: 2.1 executors: golang: docker: - image: cimg/go:1.21 jobs: generate: executor: golang steps: - checkout - run: go install golang.org/x/tools/cmd/stringer@latest - run: go install github.com/golang/mock/mockgen@latest - run: go generate - run: git diff --exit-code lint: executor: golang steps: - checkout - run: make tools - run: make lint fuzz: executor: golang steps: - checkout - run: go test -fuzztime 60s -v -tags marshal -fuzz '^FuzzUnmarshal$' test: parameters: goarch: type: string goversion: type: string docker: - image: cimg/go:<< parameters.goversion >> environment: GOARCH: << parameters.goarch >> GOSNMP_TARGET: "127.0.0.1" GOSNMP_PORT: "161" GOSNMP_TARGET_IPV4: "127.0.0.1" GOSNMP_PORT_IPV4: "161" GOSNMP_TARGET_IPV6: "::1" GOSNMP_PORT_IPV6: "161" steps: - checkout - run: sudo apt-get update - run: sudo apt-get -y install snmpd - run: sudo ./snmp_users.sh - run: sudo sed -i -e 's/^agentaddress.*$/agentaddress 127.0.0.1/' /etc/snmp/snmpd.conf - run: sudo service snmpd restart - run: go test -v -tags helper - run: go test -v -tags marshal - run: go test -v -tags misc - run: go test -v -tags api - run: go test -v -tags end2end - run: go test -v -tags trap - run: | if [[ "${GOARCH}" == "amd64" ]]; then go test -v -tags all -race else echo "Not running -race" fi workflows: version: 2 test: jobs: - generate - lint - fuzz - test: matrix: parameters: goarch: - "amd64" - "386" goversion: - "1.20" - "1.21" golang-github-gosnmp-gosnmp-1.38.0/.github/000077500000000000000000000000001465470716300205115ustar00rootroot00000000000000golang-github-gosnmp-gosnmp-1.38.0/.github/dependabot.yml000066400000000000000000000001561465470716300233430ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: "gomod" directory: "/" schedule: interval: "monthly" golang-github-gosnmp-gosnmp-1.38.0/.github/workflows/000077500000000000000000000000001465470716300225465ustar00rootroot00000000000000golang-github-gosnmp-gosnmp-1.38.0/.github/workflows/codeql-analysis.yml000066400000000000000000000032201465470716300263560ustar00rootroot00000000000000name: "Code scanning - action" on: push: branches: [master, ] pull_request: # The branches below must be a subset of the branches above branches: [master] schedule: - cron: '0 4 * * 3' jobs: CodeQL-Build: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v2 with: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. fetch-depth: 2 # If this run was triggered by a pull request event, then checkout # the head of the pull request instead of the merge commit. - run: git checkout HEAD^2 if: ${{ github.event_name == 'pull_request' }} # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v2 # Override language selection by uncommenting this and choosing your languages # with: # languages: go, javascript, csharp, python, cpp, java # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild uses: github/codeql-action/autobuild@v2 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines # and modify them (or add more) to build your code if your project # uses a compiled language #- run: | # make bootstrap # make release - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 golang-github-gosnmp-gosnmp-1.38.0/.gitignore000066400000000000000000000021131465470716300211360ustar00rootroot00000000000000# Created by https://www.gitignore.io/api/go,osx,vim ### Go ### # Binaries for programs and plugins *.exe *.dll *.so *.dylib # Test binary, build with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 .glide/ ### OSX ### *.DS_Store .AppleDouble .LSOverride # Icon must end with two \r Icon # Thumbnails ._* # Files that might appear in the root of a volume .DocumentRevisions-V100 .fseventsd .Spotlight-V100 .TemporaryItems .Trashes .VolumeIcon.icns .com.apple.timemachine.donotpresent # Directories potentially created on remote AFP share .AppleDB .AppleDesktop Network Trash Folder Temporary Items .apdisk ### Vim ### # swap [._]*.s[a-v][a-z] [._]*.sw[a-p] [._]s[a-v][a-z] [._]sw[a-p] # session Session.vim # temporary .netrwhist *~ # auto-generated tag files tags # End of https://www.gitignore.io/api/go,osx,vim # gogland .idea/ # git rebase files *.orig # test coverage outputs coverage.json gosnmp.html # profiling outputs cpu.out mem.out gosnmp.test golang-github-gosnmp-gosnmp-1.38.0/.golangci.yml000066400000000000000000000017161465470716300215420ustar00rootroot00000000000000--- run: timeout: 5m linters: enable: - bodyclose - dogsled - dupl - exportloopref # Replaces scopelint - gochecknoglobals - goconst - gocritic - goimports - goprintffuncname - gosec - misspell - nakedret - nolintlint - revive # Replaces golint - unconvert - unparam - whitespace # TODO the following linters # - gocognit # - gocyclo # - goerr113 # - gomnd # - lll # - nestif # - prealloc disable: # Disable soon to deprecated[1] linters that lead to false # positives when build tags disable certain files[2] # 1: https://github.com/golangci/golangci-lint/issues/1841 # 2: https://github.com/prometheus/node_exporter/issues/1545 - deadcode - unused - structcheck - varcheck linters-settings: gofmt: simplify: true gocyclo: min-complexity: 20 govet: check-shadowing: true issues: exclude-rules: - path: _test.go linters: - gochecknoglobals - nolintlint golang-github-gosnmp-gosnmp-1.38.0/AUTHORS.md000066400000000000000000000064011465470716300206210ustar00rootroot00000000000000# GoSNMP authors `git log --pretty=format:"* %an %ae" df49b4fc0b10ed2cab253cecc8c3d86b72cec41d..HEAD | sort -f | uniq >> AUTHORS.md` `TODO: something clever with sed, etc to autogenerate this` * 10074432 liu.xuefeng1@zte.com.cn * Andreas Louca andreas@louca.org * Andrew Filonov aef@bks.tv * Andris Raugulis moo@arthepsy.eu * Balogh Ákos akos@rubin.hu * Benjamin benjamin.guy.thomas@gmail.com * Benjamin Thomas benjamin.guy.thomas@gmail.com * Ben Kochie superq@gmail.com * benthor github@benthor.name * Brian Brazil brian.brazil@robustperception.io * Bryan Hill bryan.d.hill@gmail.com * Bryan Hill bryan.hill@ontario.ca * Chris chris.dance@papercut.com * codedance dance.chris@gmail.com * Daniel Swarbrick daniel.swarbrick@gmail.com * davidbj david_bj@126.com * David Riley fraveydank@gmail.com * Douglas Heriot git@douglasheriot.com * dramirez dramirez@rackspace.com * Dr Josef Karthauser joe@truespeed.com * Eamon Bauman eamon@eamonbauman.com * Eduardo Ferro Aldama eduardo.ferro.aldama@gmail.com * Eduardo Ferro eduardo.ferro.aldama@gmail.com * Eli Yukelzon reflog@gmail.com * Felix Maurer felix@felix-maurer.de * frozenbubbleboy github@wildtongue.net * geofduf 46729592+geofduf@users.noreply.github.com * Guillem Jover gjover@sipwise.com * HD Moore x@hdm.io * Igor Novgorodov igor@novg.net * Ivan Radakovic iradakovic13@gmail.com * jacob dubinsky dubinskyjm@gmail.com * Jacob Dubinsky dubinskyjm@gmail.com * Jaime Gil de Sagredo Luna jgil@alea-soluciones.com * Jan Kodera koderja2@fit.cvut.cz * Jared Housh j.housh@f5.com * jclc jclc@protonmail.com * Joe Cracchiolo jjc@simplybits.com * Jon Auer jda@coldshore.com * Jon Auer jda@tapodi.net * Joshua Green joshua.green@mail.com * JP Kekkonen karatepekka@gmail.com * kauppine 24810630+kauppine@users.noreply.github.com * Kauppine 24810630+kauppine@users.noreply.github.com * Kian Ostvar kiano@jurumani.com * krkini16 krkini16@users.noreply.github.com * lilinzhe slayercat.registiononly@gmail.com * lilinzhe slayercat.subscription@gmail.com * Marc Arndt marcarndt@Marcs-MacBook-Pro.local * Marc Arndt marc@marcarndt.com * Martin Lindhe martinlindhe@users.noreply.github.com * Marty Schoch marty.schoch@gmail.com * Mattias Folke mattias.folke@gmail.com * Mattias Folke mattias.folke@tre.se * Mehdi Pourfar mehdipourfar@gmail.com * meifakun runner.mei@gmail.com * Michał Derkacz michal@Lnet.pl * Michel Blanc mb@mbnet.fr * Miroslav Genov mgenov@gmail.com * Nathan Owens nathan_owens@cable.comcast.com * Nathan Owens virtuallynathan@gmail.com * NewHooker yaocanwu@gmail.com * nikandfor nikandfor@gmail.com * Patrick Hemmer patrick.hemmer@gmail.com * Patryk Najda ptrknjd@gmail.com * Paul Komkoff i@stingr.net * Peter Vypov peter.vypov@gmail.com * pschou pschou@users.noreply.github.com * Rene Fragoso ctrlrsf@gmail.com * rjammalamadaka rajanikanth.jammalamadaka@mandiant.com * Ross Wilson ross.wilson@iomart.com * Sonia Hamilton sonia@snowfrog.net * StefanHauth 63204425+StefanHauth@users.noreply.github.com * Stefan Hauth stefan.hauth@dynatrace.com * Tara taramerin@gmail.com * The Binary binary4bytes@gmail.com * Tim Rots tim.rots@protonmail.ch * toni-moreno toni.moreno@gmail.com * Vallimamod Abdullah vma@users.noreply.github.com * WangShouLin wang.shoulin1@zte.com.cn * Whitham D. Reeve II thetawaves@gmail.com * Whitham D. Reeve II wreeve@gci.com * x1unix ascii@live.ru golang-github-gosnmp-gosnmp-1.38.0/CHANGELOG.md000066400000000000000000000145021465470716300207640ustar00rootroot00000000000000## unreleased * [CHANGE] * [FEATURE] * [ENHANCEMENT] * [BUGFIX] ## v1.38.0 * [CHANGE] Refactor netsnmp playback function to use an io.Reader #459 * [FEATURE] Support multiple security parameters for receiving SNMP V3 traps #457 * [ENHANCEMENT] netsnmp tests: tame overzealous file / dir permissions #458 ## v1.37.0 * [CHANGE] Refactor TrapListener's Close Method #449 * [FEATURE] Allow global password cache to be turned off #454 * [ENHANCEMENT] Make InitPacket and InitSecurityKeys public #447 * [ENHANCEMENT] Add net-snmp validation testing #452 * [BUGFIX] Allow RequestID to be shrunk if possible #451 ## v1.36.1 * [BUGFIX] address panics, add tests, fuzzing #443 ## v1.36.0 This release now requires Go 1.20 or higher. * [ENHANCEMENT] Allow sending v1 traps that have no varbinds #426 * [BUGFIX] Fix getBulk SnmpPacket MaxRepetitions value #413 * [BUGFIX] Refactor security logger #422 * [BUGFIX] Add privacy passphrase in extendKeyBlumenthal cacheKey call #425 * [BUGFIX] unmarshal: fix panic from reading beyond slice #441 ## v1.35.0 This release now requires Go 1.17 or higher. NOTE: The UnmarshalTrap now returns both an SnmpPacket and an error (#394) * [BUGFIX] gosnmp.Set(): permit ObjectIdentifier PDU Type #378 * [BUGFIX] SendTrap: do not set Reportable MsgFlags for v3 #398 * [CHANGE] Support authoritative engineID discovery when listening for traps #394 * [CHANGE] Require Go 1.17+ * [ENHANCEMENT] marshalUint32: Values above 2^31-1 encodes in 5 bytes #377 * [ENHANCEMENT] Add Control function to GoSNMP dialer parameters #397 ## v1.34.0 NOTE: marshalInt32 now always encodes an integer value in the smallest possible number of octets as per ITU-T Rec. X.690 (07/2002). * [ENHANCEMENT] gosnmp/marshalInt32: adhere to ITU-T Rec. X.690 integer encoding #372 * [ENHANCEMENT] parseInt64: throw error on zero length as per X690 #373 * [ENHANCEMENT] helper.go: Interpreting the value of an Opaque type as binary data if the Opaque sub-type cannot be recognized #374 * [ENHANCEMENT] helper.go: Implemented Opaque type marshaling #374 * [BUGFIX] marshal.go: Fixed invalid OpaqueFloat and OpaqueDouble marshaling in marshalVarbind() function #374 * [BUGFIX] marshal.go: stricter cursor bounds checking in unmarshalPayload #384 ## v1.33.0 * [BUGFIX] parseLength: avoid OOB read, prevent panic #354 * [BUGFIX] Detect negative lengths in parseLength, prevent panic #369 * [FEATURE] Add LocalAddr setting to bind source address of SNMP queries #342 * [ENHANCEMENT] Validate SNMPv3 Auth/Priv Protocol for incoming trap message #351 * [ENHANCEMENT] helper.go: add error handling to parseLength #358 * [ENHANCEMENT] Rename v3_testing_credentials to avoid testing import in prod builds #360 * [ENHANCEMENT] helper.go: Improved decodeValue() function #340 ## v1.32.0 NOTE: This release changes the Logger interface. The loggingEnabled variable has been deprecated. * [BUGFIX] marshal.go: improve packet validation and error handling #323 * [BUGFIX] marshal.go: Fix on-error-continue flow in sendOneRequest #324 * [BUGFIX] Fix SNMPv3 trap authentication #332 * [CHANGE] New Logger interface has been implemented #329 * [ENHANCEMENT] helper.go: Improved OID marshaling with sub-identifier validation as per rfc2578 section-3.5 #321 * [ENHANCEMENT] Add rfc3412 report errors #333 ## v1.31.0 * [BUGFIX] Add validation to prevent calling updatePktSecurityParameters with non v3 packet #251 #314 * [ENHANCEMENT] walk.go: improve BulkWalk error handling #306 * [ENHANCEMENT] return received SNMP error code immediately instead of waiting for timeout #319 ## v1.30.0 NOTE: This release changes the MaxRepetitions type to uint32. * [BUGFIX] Add bounds checking for reqID and msgID #273 * [FEATURE] New packet inspection hook methods for in-flight measurements #276 * [ENHANCEMENT] Support for local e2e tests against net-snmpd #292 * [CHANGE] Fix GetBulkRequest MaxRepetitions signedness issue in marshalPDU() #293 * [CHANGE] mocks/gosnmp_mock.go: Update UnmarshalTrap mock base method #294 * [BUGFIX] marshal.go: Fix signedness issue in marshalPDU() #295 * [ENHANCEMENT] marshalPDU(): stricter integer conversion #301 * [ENHANCEMENT] Use Go 1.13 error wrapping #304 * [ENHANCEMENT] walk.go: improve BulkWalk error handling #306 * [ENHANCEMENT] MaxRepetitions now allows values between 0..2147483647 and wraps to 0 at max int32. ## v1.29.0 NOTE: This release returns the OctetString []byte behavior for v1.26.0 and earlier. * [CHANGE] Return OctetString as []byte #264 ## v1.28.0 This release updates the Go import path from `github.com/soniah/gosnmp` to `github.com/gosnmp/gosnmp`. * [CHANGE] Update project path #257 * [ENHANCEMENT] Improve SNMPv3 trap support #253 ## v1.27.0 * fix a race condition - logger * INFORM responses * linting ## v1.26.0 * more SNMPv3 * various bug fixes * linting ## v1.25.0 * SNMPv3 new hash functions for SNMPV3 USM RFC7860 * SNMPv3 tests for SNMPv3 traps * go versions 1.12 1.13 ## v1.24.0 * doco, fix AUTHORS, fix copyright * decode more packet types * TCP trap listening ## v1.23.1 * add support for contexts * fix panic conditions by checking for out-of-bounds reads ## v1.23.0 * BREAKING CHANGE: The mocks have been moved to `github.com/gosnmp/gosnmp/mocks`. If you use them, you will need to adjust your imports. * bug fix: issue 170: No results when performing a walk starting on a leaf OID * bug fix: issue 210: Set function fails if value is an Integer * doco: loggingEnabled, MIB parser * linting ## v1.22.0 * travis now failing build when goimports needs running * gometalinter * shell script for running local tests * SNMPv3 - avoid crash when missing SecurityParameters * add support for Walk and Get over TCP - RFC 3430 * SNMPv3 - allow input of private key instead of passphrase ## v1.21.0 * add netsnmp functionality "not check returned OIDs are increasing" ## v1.20.0 * convert all tags to correct semantic versioning, and remove old tags * SNMPv1 trap IDs should be marshalInt32() not single byte * use packetSecParams not sp secretKey in v3 isAuthentic() * fix IPAddress marshalling in Set() ## v1.19.0 * bug fix: handle uninitialized v3 SecurityParameters in SnmpDecodePacket() * SNMPError, Asn1BER - stringers; types on constants ## v1.18.0 * bug fix: use format flags - logPrintf() not logPrint() * bug fix: parseObjectIdentifier() now returns []byte{0} rather than error when it receive zero length input * use gomock * start using go modules * start a changelog golang-github-gosnmp-gosnmp-1.38.0/Dockerfile000066400000000000000000000021061465470716300211420ustar00rootroot00000000000000FROM golang:1.19-alpine # Install deps RUN apk add --no-cache \ bash \ curl \ gcc \ libc-dev \ make \ net-snmp \ net-snmp-tools \ openssl-dev \ python3 \ py3-pip \ vim # add new user RUN addgroup -g 1001 \ -S gosnmp; \ adduser -u 1001 -D -S \ -s /bin/bash \ -h /home/gosnmp \ -G gosnmp gosnmp RUN chmod -R a+rw /etc/snmp /var/lib/net-snmp/ RUN pip install snmpsim # Copy local branch into container USER gosnmp WORKDIR /go/src/github.com/gosnmp/gosnmp COPY --chown=gosnmp . . RUN go get github.com/stretchr/testify/assert && \ make tools && \ make lint ENV GOSNMP_TARGET=127.0.0.1 ENV GOSNMP_PORT=1024 ENV GOSNMP_TARGET_IPV4=127.0.0.1 ENV GOSNMP_PORT_IPV4=1024 ENV GOSNMP_TARGET_IPV6='::1' ENV GOSNMP_PORT_IPV6=1024 ENV GOSNMP_SNMPD=true ENTRYPOINT ["/go/src/github.com/gosnmp/gosnmp/build_tests.sh"] golang-github-gosnmp-gosnmp-1.38.0/LICENSE000066400000000000000000000057531465470716300201700ustar00rootroot00000000000000Copyright 2012-2020 The GoSNMP Authors. All rights reserved. Use of this rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Parts of the gosnmp code are from GoLang ASN.1 Library (as marked in the source code). For those part of code the following license applies: Copyright (c) 2009 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. golang-github-gosnmp-gosnmp-1.38.0/Makefile000066400000000000000000000013731465470716300206150ustar00rootroot00000000000000.PHONY: test lint lint-all lint-examples tools GOLANGCI_LINT_VERSION ?= v1.54.2 test: go test *.go lint: check_license golangci-lint run -v tools: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/$(GOLANGCI_LINT_VERSION)/install.sh \ | sh -s -- -b $(GOPATH)/bin $(GOLANGCI_LINT_VERSION) .PHONY: check_license check_license: @echo ">> checking license header" @licRes=$$(for file in $$(find . -type f -iname '*.go' ! -path './vendor/*') ; do \ awk 'NR<=3' $$file | grep -Eq "(Copyright [0-9]+ The GoSNMP Authors|generated|GENERATED)" || echo $$file; \ done); \ if [ -n "$${licRes}" ]; then \ echo "license header checking failed:"; echo "$${licRes}"; \ exit 1; \ fi golang-github-gosnmp-gosnmp-1.38.0/README.md000066400000000000000000000216651465470716300204420ustar00rootroot00000000000000gosnmp ====== [![Mentioned in Awesome Go](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/avelino/awesome-go#networking) [![Build Status](https://circleci.com/gh/gosnmp/gosnmp.svg?style=shield)](https://circleci.com/gh/gosnmp/gosnmp/tree/master) [![PkgGoDev](https://pkg.go.dev/badge/github.com/gosnmp/gosnmp)](https://pkg.go.dev/github.com/gosnmp/gosnmp) GoSNMP is an SNMP client library fully written in Go. It provides Get, GetNext, GetBulk, Walk, BulkWalk, Set and Traps. It supports IPv4 and IPv6, using __SNMPv1__, __SNMPv2c__ or __SNMPv3__. Builds are tested against linux/amd64 and linux/386. # About **gosnmp** was started by [Andreas Louca](https://github.com/alouca), then completely rewritten by [Sonia Hamilton](https://github.com/soniah) (2012-2020), and now ownership has been transferred to the community at [gosnmp/gosnmp](https://github.com/gosnmp/gosnmp). For support and help, join us in the #snmp channel of [Gophers Slack](https://invite.slack.golangbridge.org/). # Overview GoSNMP has the following SNMP functions: * **Get** (single or multiple OIDs) * **GetNext** * **GetBulk** (SNMPv2c and SNMPv3 only) * **Walk** - retrieves a subtree of values using GETNEXT. * **BulkWalk** - retrieves a subtree of values using GETBULK (SNMPv2c and SNMPv3 only). * **BulkWalkAll** - similar to BulkWalk but returns a filled array of all values rather than using a callback function to stream results. * **Set** - supports Integers and OctetStrings. * **SendTrap** - send SNMP TRAPs. * **Listen** - act as an NMS for receiving TRAPs. GoSNMP has the following **helper** functions: * **ToBigInt** - treat returned values as `*big.Int` * **Partition** - facilitates dividing up large slices of OIDs **gosnmp/gosnmp** has completely diverged from **alouca/gosnmp**, your code will require modification in these (and other) locations: * the **Get** function has a different method signature * the **NewGoSNMP** function has been removed, use **Connect** instead (see Usage below). `Connect` uses the `GoSNMP` struct; `gosnmp.Default` is provided for you to build on. * GoSNMP no longer relies on **alouca/gologger** - you can use your logger if it conforms to the `gosnmp.LoggerInterface` interface; otherwise debugging will disabled. ```go type LoggerInterface interface { Print(v ...interface{}) Printf(format string, v ...interface{}) } ``` To enable logging, you must call gosnmp.NewLogger() function, and pass a pointer to your logging interface, for example with standard *log.Logger: ```go gosnmp.Default.Logger = gosnmp.NewLogger(log.New(os.Stdout, "", 0)) ``` or ```go g := &gosnmp.GoSNMP{ ... Logger: gosnmp.NewLogger(log.New(os.Stdout, "", 0)), } ``` You can completely remove the logging code from your application using the golang build tag "gosnmp_nodebug", for example: ``` go build -tags gosnmp_nodebug ``` This will completely disable the logging of the gosnmp library, even if the logger interface is specified in the code. This provides a small performance improvement. # Installation ```shell go get github.com/gosnmp/gosnmp ``` # Documentation https://pkg.go.dev/github.com/gosnmp/gosnmp # Usage Here is `examples/example/main.go`, demonstrating how to use GoSNMP: ```go // Default is a pointer to a GoSNMP struct that contains sensible defaults // eg port 161, community public, etc g.Default.Target = "192.168.1.10" err := g.Default.Connect() if err != nil { log.Fatalf("Connect() err: %v", err) } defer g.Default.Conn.Close() oids := []string{"1.3.6.1.2.1.1.4.0", "1.3.6.1.2.1.1.7.0"} result, err2 := g.Default.Get(oids) // Get() accepts up to g.MAX_OIDS if err2 != nil { log.Fatalf("Get() err: %v", err2) } for i, variable := range result.Variables { fmt.Printf("%d: oid: %s ", i, variable.Name) // the Value of each variable returned by Get() implements // interface{}. You could do a type switch... switch variable.Type { case g.OctetString: bytes := variable.Value.([]byte) fmt.Printf("string: %s\n", string(bytes)) default: // ... or often you're just interested in numeric values. // ToBigInt() will return the Value as a BigInt, for plugging // into your calculations. fmt.Printf("number: %d\n", g.ToBigInt(variable.Value)) } } ``` Running this example gives the following output (from my printer): ```shell % go run example.go 0: oid: 1.3.6.1.2.1.1.4.0 string: Administrator 1: oid: 1.3.6.1.2.1.1.7.0 number: 104 ``` * `examples/example2.go` is similar to `example.go`, however it uses a custom `&GoSNMP` rather than `g.Default` * `examples/walkexample.go` demonstrates using `BulkWalk` * `examples/example3.go` demonstrates `SNMPv3` * `examples/trapserver.go` demonstrates writing an SNMP v2c trap server # MIB Parser I don't have any plans to write a mib parser. Others have suggested https://github.com/sleepinggenius2/gosmi # Contributions Contributions are welcome, especially ones that have packet captures (see below). If you've never contributed to a Go project before, here is an example workflow. 1. [fork this repo on the GitHub webpage](https://github.com/gosnmp/gosnmp/fork) 1. `go get github.com/gosnmp/gosnmp` 1. `cd $GOPATH/src/github.com/gosnmp/gosnmp` 1. `git remote rename origin upstream` 1. `git remote add origin git@github.com:/gosnmp.git` 1. `git checkout -b development` 1. `git push -u origin development` (setup where you push to, check it works) # Packet Captures Create your packet captures in the following way: Expected output, obtained via an **snmp** command. For example: ```shell % 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.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 ``` A packet capture, obtained while running the snmpget. For example: ```shell sudo tcpdump -s 0 -i eth0 -w foo.pcap host 203.50.251.17 and port 161 ``` # Bugs Rane's document [SNMP: Simple? Network Management Protocol](https://www.ranecommercial.com/legacy/note161.html) was useful when learning the SNMP protocol. Please create an [issue](https://github.com/gosnmp/gosnmp/issues) on Github with packet captures (upload capture to Google Drive, Dropbox, or similar) containing samples of missing BER types, or of any other bugs you find. If possible, please include 2 or 3 examples of the missing/faulty BER type. The following BER types have been implemented: * 0x00 UnknownType * 0x01 Boolean * 0x02 Integer * 0x03 BitString * 0x04 OctetString * 0x05 Null * 0x06 ObjectIdentifier * 0x07 ObjectDescription * 0x40 IPAddress (IPv4 & IPv6) * 0x41 Counter32 * 0x42 Gauge32 * 0x43 TimeTicks * 0x44 Opaque (Float & Double) * 0x45 NsapAddress * 0x46 Counter64 * 0x47 Uinteger32 * 0x78 OpaqueFloat * 0x79 OpaqueDouble * 0x80 NoSuchObject * 0x81 NoSuchInstance * 0x82 EndOfMibView # Running the Tests Local testing in Docker ```shell docker build -t gosnmp/gosnmp:latest . docker run -it gosnmp/gosnmp:latest ``` or ```shell export GOSNMP_TARGET=1.2.3.4 export GOSNMP_PORT=161 export GOSNMP_TARGET_IPV4=1.2.3.4 export GOSNMP_PORT_IPV4=161 export GOSNMP_TARGET_IPV6='0:0:0:0:0:ffff:102:304' export GOSNMP_PORT_IPV6=161 go test -v -tags all # for example go test -v -tags helper # for example ``` Tests are grouped as follows: * Unit tests (validating data packing and marshalling): * `marshal_test.go` * `misc_test.go` * Public API consistency tests: * `gosnmp_api_test.go` * End-to-end integration tests: * `generic_e2e_test.go` The generic end-to-end integration test `generic_e2e_test.go` should work against any SNMP MIB-2 compliant host (e.g. a router, NAS box, printer). Mocks were generated using: `mockgen -source=interface.go -destination=mocks/gosnmp_mock.go -package=mocks` However they're currently removed, as they were breaking linting. To profile cpu usage: ```shell go test -cpuprofile cpu.out go test -c go tool pprof gosnmp.test cpu.out ``` To profile memory usage: ```shell go test -memprofile mem.out go test -c go tool pprof gosnmp.test mem.out ``` To check test coverage: ```shell go get github.com/axw/gocov/gocov go get github.com/matm/gocov-html gocov test github.com/gosnmp/gosnmp | gocov-html > gosnmp.html && firefox gosnmp.html & ``` To measure the performance of password hash caching: Password hash caching can be disabled during benchmark tests by using the golang build tag "gosnmp_nopwdcache", so: ``` go build -tags gosnmp_nopwdcache -bench=Benchmark.*Hash ``` will benchmark the code without password hash caching. Removing the tag will run the benchmark with caching enabled (default behavior of package). # License Parts of the code are taken from the Golang project (specifically some functions for unmarshaling BER responses), which are under the same terms and conditions as the Go language. The rest of the code is under a BSD license. See the LICENSE file for more details. The remaining code is Copyright 2012 the GoSNMP Authors - see AUTHORS.md for a list of authors. golang-github-gosnmp-gosnmp-1.38.0/RELEASE.md000066400000000000000000000010411465470716300205470ustar00rootroot00000000000000gosnmp release process --- ### Steps * Have a [signingkey](#add-a-signingkey-to-gitconfig) setup. * File a PR to set a release in the CHANGELOG. * git [tag-release](#add-a-tag-release-alias-to-gitconfig) X.Y.Z * In github UI, create the release. * Copy-n-paste the CHANGELOG entries. * Publish release. ### add a signingkey to gitconfig ``` [user] signingkey = ... ``` ### add a tag-release alias to gitconfig ``` [alias] tag-release = "!f() { tag=v${1:-$(cat VERSION)} ; git tag -s ${tag} -m ${tag} && git push origin ${tag}; }; f" ``` golang-github-gosnmp-gosnmp-1.38.0/asn1ber_string.go000066400000000000000000000034411465470716300224230ustar00rootroot00000000000000// Code generated by "stringer -type Asn1BER"; DO NOT EDIT. package gosnmp import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[EndOfContents-0] _ = x[UnknownType-0] _ = x[Boolean-1] _ = x[Integer-2] _ = x[BitString-3] _ = x[OctetString-4] _ = x[Null-5] _ = x[ObjectIdentifier-6] _ = x[ObjectDescription-7] _ = x[IPAddress-64] _ = x[Counter32-65] _ = x[Gauge32-66] _ = x[TimeTicks-67] _ = x[Opaque-68] _ = x[NsapAddress-69] _ = x[Counter64-70] _ = x[Uinteger32-71] _ = x[OpaqueFloat-120] _ = x[OpaqueDouble-121] _ = x[NoSuchObject-128] _ = x[NoSuchInstance-129] _ = x[EndOfMibView-130] } const ( _Asn1BER_name_0 = "EndOfContentsBooleanIntegerBitStringOctetStringNullObjectIdentifierObjectDescription" _Asn1BER_name_1 = "IPAddressCounter32Gauge32TimeTicksOpaqueNsapAddressCounter64Uinteger32" _Asn1BER_name_2 = "OpaqueFloatOpaqueDouble" _Asn1BER_name_3 = "NoSuchObjectNoSuchInstanceEndOfMibView" ) var ( _Asn1BER_index_0 = [...]uint8{0, 13, 20, 27, 36, 47, 51, 67, 84} _Asn1BER_index_1 = [...]uint8{0, 9, 18, 25, 34, 40, 51, 60, 70} _Asn1BER_index_2 = [...]uint8{0, 11, 23} _Asn1BER_index_3 = [...]uint8{0, 12, 26, 38} ) func (i Asn1BER) String() string { switch { case i <= 7: return _Asn1BER_name_0[_Asn1BER_index_0[i]:_Asn1BER_index_0[i+1]] case 64 <= i && i <= 71: i -= 64 return _Asn1BER_name_1[_Asn1BER_index_1[i]:_Asn1BER_index_1[i+1]] case 120 <= i && i <= 121: i -= 120 return _Asn1BER_name_2[_Asn1BER_index_2[i]:_Asn1BER_index_2[i+1]] case 128 <= i && i <= 130: i -= 128 return _Asn1BER_name_3[_Asn1BER_index_3[i]:_Asn1BER_index_3[i+1]] default: return "Asn1BER(" + strconv.FormatInt(int64(i), 10) + ")" } } golang-github-gosnmp-gosnmp-1.38.0/build_tests.sh000077500000000000000000000012671465470716300220370ustar00rootroot00000000000000#!/usr/bin/env bash if [ "${GOSNMP_SNMPD}" != "" ]; then echo "Using $(snmpd --version | awk /version:/)" ./snmp_users.sh sed -i -e 's/^agentAddress.*/agentAddress udp:127.0.0.1:1024/' /etc/snmp/snmpd.conf sed -i -e 's/ localhost / 127.0.0.1 /' /etc/snmp/snmpd.conf sed -i -e 's/.*trapsink.*//' /etc/snmp/snmpd.conf sed -i -e 's/.*master\s*agentx//' /etc/snmp/snmpd.conf snmpd else echo "Using snmpsimd simulator" snmpsimd.py --logging-method=null --agent-udpv4-endpoint=127.0.0.1:1024 & fi go test -v -tags helper go test -v -tags marshal go test -v -tags misc go test -v -tags api go test -v -tags end2end go test -v -tags trap go test -v -tags all -race golang-github-gosnmp-gosnmp-1.38.0/examples/000077500000000000000000000000001465470716300207675ustar00rootroot00000000000000golang-github-gosnmp-gosnmp-1.38.0/examples/Hex-String/000077500000000000000000000000001465470716300227575ustar00rootroot00000000000000golang-github-gosnmp-gosnmp-1.38.0/examples/Hex-String/main.go000066400000000000000000000027171465470716300242410ustar00rootroot00000000000000// 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. package main import ( "fmt" "log" "strings" "strconv" g "github.com/gosnmp/gosnmp" ) func main() { // Default is a pointer to a GoSNMP struct that contains sensible defaults // eg port 161, community public, etc g.Default.Target = "192.168.1.10" err := g.Default.Connect() if err != nil { log.Fatalf("Connect() err: %v", err) } defer g.Default.Conn.Close() oids := []string{"1.3.6.1.2.1.1.4.0", "1.3.6.1.2.1.1.7.0"} result, err2 := g.Default.Get(oids) // Get() accepts up to g.MAX_OIDS if err2 != nil { log.Fatalf("Get() err: %v", err2) } for i, variable := range result.Variables { fmt.Printf("%d: oid: %s ", i, variable.Name) switch variable.Type { case g.OctetString: value := variable.Value.([]byte) if strings.Contains(strconv.Quote(string(value)), "\\x") { tmp := "" for i := 0; i < len(value); i++ { tmp += fmt.Sprintf("%v", value[i]) if i != (len(value) - 1) { tmp += " " } } fmt.Printf("Hex-String: %s\n", tmp) } else { fmt.Printf("string: %s\n", string(variable.Value.([]byte))) } default: // ... or often you're just interested in numeric values. // ToBigInt() will return the Value as a BigInt, for plugging // into your calculations. fmt.Printf("number: %d\n", g.ToBigInt(variable.Value)) } } } golang-github-gosnmp-gosnmp-1.38.0/examples/example/000077500000000000000000000000001465470716300224225ustar00rootroot00000000000000golang-github-gosnmp-gosnmp-1.38.0/examples/example/main.go000066400000000000000000000023461465470716300237020ustar00rootroot00000000000000// 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. package main import ( "fmt" "log" g "github.com/gosnmp/gosnmp" ) func main() { // Default is a pointer to a GoSNMP struct that contains sensible defaults // eg port 161, community public, etc g.Default.Target = "192.168.1.10" err := g.Default.Connect() if err != nil { log.Fatalf("Connect() err: %v", err) } defer g.Default.Conn.Close() oids := []string{"1.3.6.1.2.1.1.4.0", "1.3.6.1.2.1.1.7.0"} result, err2 := g.Default.Get(oids) // Get() accepts up to g.MAX_OIDS if err2 != nil { log.Fatalf("Get() err: %v", err2) } for i, variable := range result.Variables { fmt.Printf("%d: oid: %s ", i, variable.Name) // the Value of each variable returned by Get() implements // interface{}. You could do a type switch... switch variable.Type { case g.OctetString: fmt.Printf("string: %s\n", string(variable.Value.([]byte))) default: // ... or often you're just interested in numeric values. // ToBigInt() will return the Value as a BigInt, for plugging // into your calculations. fmt.Printf("number: %d\n", g.ToBigInt(variable.Value)) } } } golang-github-gosnmp-gosnmp-1.38.0/examples/example2/000077500000000000000000000000001465470716300225045ustar00rootroot00000000000000golang-github-gosnmp-gosnmp-1.38.0/examples/example2/main.go000066400000000000000000000073511465470716300237650ustar00rootroot00000000000000// 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. /* Example usage and output: $ GOSNMP_TARGET=10.12.0.1 GOSNMP_PORT=161 go run main.go SEND INIT SENDING PACKET: gosnmp.SnmpPacket{Version:0x1, MsgFlags:0x0, SecurityModel:0x0, SecurityParameters:gosnmp.SnmpV3SecurityParameters(nil), ContextEngineID:"", ContextName:"", Community:"public", PDUType:0xa0, MsgID:0x0, RequestID:0x48ef671d, MsgMaxSize:0x0, Error:0x0, ErrorIndex:0x0, NonRepeaters:0x0, MaxRepetitions:0x0, Variables:[]gosnmp.SnmpPDU{gosnmp.SnmpPDU{Name:"1.3.6.1.2.1.1.4.0", Type:0x5, Value:interface {}(nil)}, gosnmp.SnmpPDU{Name:"1.3.6.1.2.1.1.7.0", Type:0x5, Value:interface {}(nil)}}, Logger:gosnmp.Logger(nil), SnmpTrap:gosnmp.SnmpTrap{Variables:[]gosnmp.SnmpPDU(nil), IsInform:false, Enterprise:"", AgentAddress:"", GenericTrap:0, SpecificTrap:0, Timestamp:0x0}} WAITING RESPONSE... 2021/01/03 13:31:15 Query latency in seconds: 0.000625777 GET RESPONSE OK: [48 63 2 1 1 4 6 112 117 98 108 105 99 162 50 2 4 72 239 103 29 2 1 0 2 1 0 48 36 48 19 6 8 43 6 1 2 1 1 4 0 4 7 99 111 110 116 97 99 116 48 13 6 8 43 6 1 2 1 1 7 0 2 1 78] Packet sanity verified, we got all the bytes (65) parseRawField: version Parsed version 1 parseRawField: community Parsed community public UnmarshalPayload Meet PDUType 0xa2. Offset 13 getResponseLength: 52 parseRawField: request id requestID: 1223649053 parseRawField: error-status errorStatus: 0 parseRawField: error index error-index: 0 vblLength: 38 parseRawField: OID OID: .1.3.6.1.2.1.1.4.0 decodeValue: msg: value decodeValue: type is OctetString decodeValue: value is []byte{0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74} parseRawField: OID OID: .1.3.6.1.2.1.1.7.0 decodeValue: msg: value decodeValue: type is Integer decodeValue: value is 78 0: oid: .1.3.6.1.2.1.1.4.0 string: contact 1: oid: .1.3.6.1.2.1.1.7.0 number: 78 */ package main import ( "fmt" "log" "os" "strconv" "time" g "github.com/gosnmp/gosnmp" ) func main() { // get Target and Port from environment envTarget := os.Getenv("GOSNMP_TARGET") envPort := os.Getenv("GOSNMP_PORT") if len(envTarget) <= 0 { log.Fatalf("environment variable not set: GOSNMP_TARGET") } if len(envPort) <= 0 { log.Fatalf("environment variable not set: GOSNMP_PORT") } port, _ := strconv.ParseUint(envPort, 10, 16) // Build our own GoSNMP struct, rather than using g.Default. // Do verbose logging of packets. params := &g.GoSNMP{ Target: envTarget, Port: uint16(port), Community: "public", Version: g.Version2c, Timeout: time.Duration(2) * time.Second, Logger: g.NewLogger(log.New(os.Stdout, "", 0)), } err := params.Connect() if err != nil { log.Fatalf("Connect() err: %v", err) } defer params.Conn.Close() // Function handles for collecting metrics on query latencies. var sent time.Time params.OnSent = func(x *g.GoSNMP) { sent = time.Now() } params.OnRecv = func(x *g.GoSNMP) { log.Println("Query latency in seconds:", time.Since(sent).Seconds()) } oids := []string{"1.3.6.1.2.1.1.4.0", "1.3.6.1.2.1.1.7.0"} result, err2 := params.Get(oids) // Get() accepts up to g.MAX_OIDS if err2 != nil { log.Fatalf("Get() err: %v", err2) } for i, variable := range result.Variables { fmt.Printf("%d: oid: %s ", i, variable.Name) // the Value of each variable returned by Get() implements // interface{}. You could do a type switch... switch variable.Type { case g.OctetString: fmt.Printf("string: %s\n", string(variable.Value.([]byte))) default: // ... or often you're just interested in numeric values. // ToBigInt() will return the Value as a BigInt, for plugging // into your calculations. fmt.Printf("number: %d\n", g.ToBigInt(variable.Value)) } } } golang-github-gosnmp-gosnmp-1.38.0/examples/example3/000077500000000000000000000000001465470716300225055ustar00rootroot00000000000000golang-github-gosnmp-gosnmp-1.38.0/examples/example3/main.go000066400000000000000000000031171465470716300237620ustar00rootroot00000000000000// 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. package main import ( "fmt" "log" "time" g "github.com/gosnmp/gosnmp" ) func main() { // build our own GoSNMP struct, rather than using g.Default params := &g.GoSNMP{ Target: "192.168.91.20", Port: 161, Version: g.Version3, SecurityModel: g.UserSecurityModel, MsgFlags: g.AuthPriv, Timeout: time.Duration(30) * time.Second, SecurityParameters: &g.UsmSecurityParameters{UserName: "user", AuthenticationProtocol: g.SHA, AuthenticationPassphrase: "password", PrivacyProtocol: g.DES, PrivacyPassphrase: "password", }, } err := params.Connect() if err != nil { log.Fatalf("Connect() err: %v", err) } defer params.Conn.Close() oids := []string{"1.3.6.1.2.1.1.4.0", "1.3.6.1.2.1.1.7.0"} result, err2 := params.Get(oids) // Get() accepts up to g.MAX_OIDS if err2 != nil { log.Fatalf("Get() err: %v", err2) } for i, variable := range result.Variables { fmt.Printf("%d: oid: %s ", i, variable.Name) // the Value of each variable returned by Get() implements // interface{}. You could do a type switch... switch variable.Type { case g.OctetString: fmt.Printf("string: %s\n", string(variable.Value.([]byte))) default: // ... or often you're just interested in numeric values. // ToBigInt() will return the Value as a BigInt, for plugging // into your calculations. fmt.Printf("number: %d\n", g.ToBigInt(variable.Value)) } } } golang-github-gosnmp-gosnmp-1.38.0/examples/example4/000077500000000000000000000000001465470716300225065ustar00rootroot00000000000000golang-github-gosnmp-gosnmp-1.38.0/examples/example4/main.go000066400000000000000000000026151465470716300237650ustar00rootroot00000000000000// 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. package main import ( "fmt" "log" "os" "time" g "github.com/gosnmp/gosnmp" ) const ( On int = 1 Off = 2 ) func main() { var Client = &g.GoSNMP{ Target: "192.168.91.20", Port: 161, Community: "private", Version: g.Version2c, Timeout: time.Duration(2) * time.Second, Logger: g.NewLogger(log.New(os.Stdout, "", 0)), } err := Client.Connect() if err != nil { log.Fatalf("Connect() err: %v", err) } defer Client.Conn.Close() var mySnmpPDU = []g.SnmpPDU{{ Name: "1.3.6.1.4.1.318.1.1.4.4.2.1.3.15", Type: g.Integer, Value: On, }} setResult, setErr := Client.Set(mySnmpPDU) if setErr != nil { log.Fatalf("SNMP set() fialed due to err: %v", setErr) } for i, variable := range setResult.Variables { fmt.Printf("%d: oid: %s ", i, variable.Name) // the Value of each variable returned by Get() implements // interface{}. You could do a type switch... switch variable.Type { case g.OctetString: fmt.Printf("string: %s\n", string(variable.Value.([]byte))) default: // ... or often you're just interested in numeric values. // ToBigInt() will return the Value as a BigInt, for plugging // into your calculations. fmt.Printf("number: %d\n", g.ToBigInt(variable.Value)) } } } golang-github-gosnmp-gosnmp-1.38.0/examples/linux_vrf/000077500000000000000000000000001465470716300230035ustar00rootroot00000000000000golang-github-gosnmp-gosnmp-1.38.0/examples/linux_vrf/main.go000066400000000000000000000034751465470716300242670ustar00rootroot00000000000000// 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. package main import ( "fmt" "log" "syscall" "time" g "github.com/gosnmp/gosnmp" ) func main() { // build our own GoSNMP struct, rather than using g.Default params := &g.GoSNMP{ Target: "192.168.1.1", Port: 161, Version: g.Version2c, Community: "public", Timeout: time.Duration(30) * time.Second, UseUnconnectedUDPSocket: false, // Use a the Control function to bind the underlying socket // to the VRF device on Linux. The VRF must already exists. // https://www.kernel.org/doc/Documentation/networking/vrf.txt Control: func(_, _ string, c syscall.RawConn) error { return c.Control(func(fd uintptr) { syscall.BindToDevice(int(fd), "VRF1") }) }, // Specify an IP address within the VRF LocalAddr: "192.168.1.2:0", } err := params.Connect() if err != nil { log.Fatalf("Connect() err: %v", err) } defer params.Conn.Close() oids := []string{"1.3.6.1.2.1.1.4.0", "1.3.6.1.2.1.1.7.0"} result, err2 := params.Get(oids) // Get() accepts up to g.MAX_OIDS if err2 != nil { log.Fatalf("Get() err: %v", err2) } for i, variable := range result.Variables { fmt.Printf("%d: oid: %s ", i, variable.Name) // the Value of each variable returned by Get() implements // interface{}. You could do a type switch... switch variable.Type { case g.OctetString: fmt.Printf("string: %s\n", string(variable.Value.([]byte))) default: // ... or often you're just interested in numeric values. // ToBigInt() will return the Value as a BigInt, for plugging // into your calculations. fmt.Printf("number: %d\n", g.ToBigInt(variable.Value)) } } } golang-github-gosnmp-gosnmp-1.38.0/examples/logger/000077500000000000000000000000001465470716300222465ustar00rootroot00000000000000golang-github-gosnmp-gosnmp-1.38.0/examples/logger/main.go000066400000000000000000000027161465470716300235270ustar00rootroot00000000000000// Copyright 2021 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. // building this code with the gosnmp_nodebug tag will completely disable compiler-level logging. // If you however want to enable or disable logging at runtime you could choose to do so as folow: package main import ( "log" "os" g "github.com/gosnmp/gosnmp" ) func main() { stdout_logger := g.NewLogger(log.New(os.Stdout, "", 0)) // enable logging with stdout disabled_logger := g.NewLogger(nil) // disable logging params := &g.GoSNMP{ Target: "127.0.0.1", Port: uint16(1161), Community: "public", } params.Connect() // no logger specified, logging is disabled params.Conn.Close() params.Logger = stdout_logger params.Connect() // logging enabled using stdout params.Conn.Close() params.Logger = disabled_logger params.Connect() // logging is disabled params.Conn.Close() } // on v1.31 with logging enabled, and Logger variable is not set // go test -v -bench=. -benchmem -benchtime=100000x -tags all // BenchmarkSendOneRequest-24 100000 70542 ns/op 3088 B/op 84 allocs/op // on v1.31 with logging enabled, and Logger variable is set to NewLogger(nil) // go test -v -bench=. -benchmem -benchtime=100000x -tags all // BenchmarkSendOneRequest-24 100000 70377 ns/op 3088 B/op 84 allocs/op golang-github-gosnmp-gosnmp-1.38.0/examples/tcp_trapserver/000077500000000000000000000000001465470716300240325ustar00rootroot00000000000000golang-github-gosnmp-gosnmp-1.38.0/examples/tcp_trapserver/main.go000066400000000000000000000053751465470716300253170ustar00rootroot00000000000000// Copyright 2020 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. package main import ( "fmt" "log" "net" "time" gosnmp "github.com/gosnmp/gosnmp" ) func main() { log.Println("Starting") var port uint16 = 2162 go Start(fmt.Sprintf("tcp://0.0.0.0:%d", port)) gosnmp.Default.Target = "127.0.0.1" gosnmp.Default.Transport = "tcp" gosnmp.Default.Port = port gosnmp.Default.Community = "public" gosnmp.Default.Version = gosnmp.Version1 time.Sleep(time.Duration(1) * time.Second) oid := gosnmp.SnmpPDU{ Name: "1.3.6.1.2.1.1.6", Type: gosnmp.ObjectIdentifier, Value: "1.3.6.1.2.1.1.6.10", } oid1 := gosnmp.SnmpPDU{ Name: "1.3.6.1.2.1.1.7", Type: gosnmp.OctetString, Value: "Testing TCP trap...", } oid2 := gosnmp.SnmpPDU{ Name: "1.3.6.1.2.1.1.8", Type: gosnmp.Integer, Value: 123, } cou := 5 for cou > 0 { time.Sleep(time.Duration(1) * time.Second) err := gosnmp.Default.Connect() if err != nil { log.Fatal(err) } defer gosnmp.Default.Conn.Close() //RebuildCron() log.Printf("Running (%d)\n", cou) trap := gosnmp.SnmpTrap{ Variables: []gosnmp.SnmpPDU{oid, oid1, oid2}, Enterprise: ".1.3.6.1.6.3.1.1.5.1", AgentAddress: "127.0.0.1", GenericTrap: 0, SpecificTrap: 0, Timestamp: 300, } _, err = gosnmp.Default.SendTrap(trap) if err != nil { log.Fatalf("SendTrap() err: %v\n", err) } cou-- } //time.Sleep(time.Duration(10) * time.Second) log.Println("Stop...") } // Start SNMP server func Start(address string) { log.Printf("Starting SNMP TRAP Server on: %s\n", address) tl := gosnmp.NewTrapListener() tl.OnNewTrap = myTrapHandlerTCP tl.Params = gosnmp.Default err := tl.Listen(address) if err != nil { time.Sleep(1 * time.Second) log.Fatalf("Error in TRAP listen: %s\n", err) } } func myTrapHandlerTCP(packet *gosnmp.SnmpPacket, addr *net.UDPAddr) { log.Printf("SNMP trap received from: %s:%d. Community:%s, SnmpVersion:%s\n", addr.IP, addr.Port, packet.Community, packet.Version) for i, variable := range packet.Variables { var val string switch variable.Type { case gosnmp.OctetString: val = string(variable.Value.([]byte)) case gosnmp.ObjectIdentifier: val = fmt.Sprintf("%s", variable.Value) case gosnmp.TimeTicks: a := gosnmp.ToBigInt(variable.Value) val = fmt.Sprintf("%d", (*a).Int64()) case gosnmp.Null: val = "" default: // ... or often you're just interested in numeric values. // ToBigInt() will return the Value as a BigInt, for plugging // into your calculations. a := gosnmp.ToBigInt(variable.Value) val = fmt.Sprintf("%d", (*a).Int64()) } log.Printf("- oid[%d]: %s (%s) = %v \n", i, variable.Name, variable.Type, val) } } golang-github-gosnmp-gosnmp-1.38.0/examples/trapsend_v1/000077500000000000000000000000001465470716300232155ustar00rootroot00000000000000golang-github-gosnmp-gosnmp-1.38.0/examples/trapsend_v1/main.go000066400000000000000000000016631465470716300244760ustar00rootroot00000000000000// 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. package main import ( "log" g "github.com/gosnmp/gosnmp" ) func main() { // Default is a pointer to a GoSNMP struct that contains sensible defaults // eg port 161, community public, etc g.Default.Target = "127.0.0.1" g.Default.Port = 162 g.Default.Version = g.Version1 err := g.Default.Connect() if err != nil { log.Fatalf("Connect() err: %v", err) } defer g.Default.Conn.Close() pdu := g.SnmpPDU{ Name: "1.3.6.1.2.1.1.6", Type: g.OctetString, Value: "Oval Office", } trap := g.SnmpTrap{ Variables: []g.SnmpPDU{pdu}, Enterprise: ".1.3.6.1.6.3.1.1.5.1", AgentAddress: "127.0.0.1", GenericTrap: 0, SpecificTrap: 0, Timestamp: 300, } _, err = g.Default.SendTrap(trap) if err != nil { log.Fatalf("SendTrap() err: %v", err) } } golang-github-gosnmp-gosnmp-1.38.0/examples/trapsend_v2plus/000077500000000000000000000000001465470716300241225ustar00rootroot00000000000000golang-github-gosnmp-gosnmp-1.38.0/examples/trapsend_v2plus/main.go000066400000000000000000000016461465470716300254040ustar00rootroot00000000000000// 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. package main import ( "log" "os" g "github.com/gosnmp/gosnmp" ) func main() { // Default is a pointer to a GoSNMP struct that contains sensible defaults // eg port 161, community public, etc g.Default.Target = "127.0.0.1" g.Default.Port = 162 g.Default.Version = g.Version2c g.Default.Community = "public" g.Default.Logger = g.NewLogger(log.New(os.Stdout, "", 0)) err := g.Default.Connect() if err != nil { log.Fatalf("Connect() err: %v", err) } defer g.Default.Conn.Close() pdu := g.SnmpPDU{ Name: ".1.3.6.1.6.3.1.1.4.1.0", Type: g.ObjectIdentifier, Value: ".1.3.6.1.6.3.1.1.5.1", } trap := g.SnmpTrap{ Variables: []g.SnmpPDU{pdu}, } _, err = g.Default.SendTrap(trap) if err != nil { log.Fatalf("SendTrap() err: %v", err) } } golang-github-gosnmp-gosnmp-1.38.0/examples/trapsend_v3/000077500000000000000000000000001465470716300232175ustar00rootroot00000000000000golang-github-gosnmp-gosnmp-1.38.0/examples/trapsend_v3/main.go000066400000000000000000000024621465470716300244760ustar00rootroot00000000000000// 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. package main import ( _ "crypto/md5" _ "crypto/sha1" "log" "os" "time" g "github.com/gosnmp/gosnmp" ) func main() { // Default is a pointer to a GoSNMP struct that contains sensible defaults // eg port 161, community public, etc params := &g.GoSNMP{ Target: "127.0.0.1", Port: 162, Version: g.Version3, Timeout: time.Duration(30) * time.Second, SecurityModel: g.UserSecurityModel, MsgFlags: g.AuthPriv, Logger: g.NewLogger(log.New(os.Stdout, "", 0)), SecurityParameters: &g.UsmSecurityParameters{UserName: "user", AuthoritativeEngineID: "1234", AuthenticationProtocol: g.SHA, AuthenticationPassphrase: "password", PrivacyProtocol: g.DES, PrivacyPassphrase: "password", }, } err := params.Connect() if err != nil { log.Fatalf("Connect() err: %v", err) } defer params.Conn.Close() pdu := g.SnmpPDU{ Name: ".1.3.6.1.6.3.1.1.4.1.0", Type: g.ObjectIdentifier, Value: ".1.3.6.1.6.3.1.1.5.1", } trap := g.SnmpTrap{ Variables: []g.SnmpPDU{pdu}, } _, err = params.SendTrap(trap) if err != nil { log.Fatalf("SendTrap() err: %v", err) } } golang-github-gosnmp-gosnmp-1.38.0/examples/trapserver/000077500000000000000000000000001465470716300231645ustar00rootroot00000000000000golang-github-gosnmp-gosnmp-1.38.0/examples/trapserver/main.go000066400000000000000000000022301465470716300244340ustar00rootroot00000000000000// 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. /* The developer of the trapserver code (https://github.com/jda) says "I'm working on the best level of abstraction but I'm able to receive traps from a Cisco switch and Net-SNMP". Pull requests welcome. */ package main import ( "flag" "fmt" "log" "net" "os" "path/filepath" g "github.com/gosnmp/gosnmp" ) func main() { flag.Usage = func() { fmt.Printf("Usage:\n") fmt.Printf(" %s\n", filepath.Base(os.Args[0])) flag.PrintDefaults() } tl := g.NewTrapListener() tl.OnNewTrap = myTrapHandler tl.Params = g.Default tl.Params.Logger = g.NewLogger(log.New(os.Stdout, "", 0)) err := tl.Listen("0.0.0.0:9162") if err != nil { log.Panicf("error in listen: %s", err) } } func myTrapHandler(packet *g.SnmpPacket, addr *net.UDPAddr) { log.Printf("got trapdata from %s\n", addr.IP) for _, v := range packet.Variables { switch v.Type { case g.OctetString: b := v.Value.([]byte) fmt.Printf("OID: %s, string: %x\n", v.Name, b) default: log.Printf("trap: %+v\n", v) } } } golang-github-gosnmp-gosnmp-1.38.0/examples/trapserver_v3/000077500000000000000000000000001465470716300235745ustar00rootroot00000000000000golang-github-gosnmp-gosnmp-1.38.0/examples/trapserver_v3/main.go000066400000000000000000000046671465470716300250640ustar00rootroot00000000000000// Copyright 2023 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. /* The developer of the trapserver code (https://github.com/jda) says "I'm working on the best level of abstraction but I'm able to receive traps from a Cisco switch and Net-SNMP". Pull requests welcome. */ package main import ( "flag" "fmt" "log" "net" "os" "path/filepath" g "github.com/gosnmp/gosnmp" ) var secParamsList = []*g.UsmSecurityParameters{ { UserName: "myuser", AuthenticationProtocol: g.MD5, AuthenticationPassphrase: "mypassword", PrivacyProtocol: g.AES, PrivacyPassphrase: "myprivacy", }, { UserName: "myuser2", AuthenticationProtocol: g.SHA, AuthenticationPassphrase: "mypassword2", PrivacyProtocol: g.DES, PrivacyPassphrase: "myprivacy2", }, { UserName: "myuser2", AuthenticationProtocol: g.MD5, AuthenticationPassphrase: "mypassword2", PrivacyProtocol: g.AES, PrivacyPassphrase: "myprivacy2", }, } func main() { flag.Usage = func() { fmt.Printf("Usage:\n") fmt.Printf(" %s\n", filepath.Base(os.Args[0])) flag.PrintDefaults() } tl := g.NewTrapListener() tl.OnNewTrap = myTrapHandler usmTable := g.NewSnmpV3SecurityParametersTable(g.NewLogger(log.New(os.Stdout, "", 0))) for _, sp := range secParamsList { err := usmTable.Add(sp.UserName, sp) if err != nil { usmTable.Logger.Print(err) } } gs := &g.GoSNMP{ Port: 161, Transport: "udp", Version: g.Version3, // Always using version3 for traps, only option that works with all SNMP versions simultaneously SecurityModel: g.UserSecurityModel, SecurityParameters: &g.UsmSecurityParameters{AuthoritativeEngineID: "12345"}, // Use for server's engine ID TrapSecurityParametersTable: usmTable, } tl.Params = gs tl.Params.Logger = g.NewLogger(log.New(os.Stdout, "", 0)) err := tl.Listen("0.0.0.0:9162") if err != nil { log.Panicf("error in listen: %s", err) } } func myTrapHandler(packet *g.SnmpPacket, addr *net.UDPAddr) { log.Printf("got trapdata from %s\n", addr.IP) for _, v := range packet.Variables { switch v.Type { case g.OctetString: b := v.Value.([]byte) fmt.Printf("OID: %s, string: %x\n", v.Name, b) default: log.Printf("trap: %+v\n", v) } } } golang-github-gosnmp-gosnmp-1.38.0/examples/walkexample/000077500000000000000000000000001465470716300233015ustar00rootroot00000000000000golang-github-gosnmp-gosnmp-1.38.0/examples/walkexample/main.go000066400000000000000000000031031465470716300245510ustar00rootroot00000000000000// 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. // This program demonstrates BulkWalk. package main import ( "flag" "fmt" "os" "path/filepath" "time" "github.com/gosnmp/gosnmp" ) func main() { flag.Usage = func() { fmt.Printf("Usage:\n") fmt.Printf(" %s [-community=] host [oid]\n", filepath.Base(os.Args[0])) fmt.Printf(" host - the host to walk/scan\n") fmt.Printf(" oid - the MIB/Oid defining a subtree of values\n\n") flag.PrintDefaults() } var community string flag.StringVar(&community, "community", "public", "the community string for device") flag.Parse() if len(flag.Args()) < 1 { flag.Usage() os.Exit(1) } target := flag.Args()[0] var oid string if len(flag.Args()) > 1 { oid = flag.Args()[1] } gosnmp.Default.Target = target gosnmp.Default.Community = community gosnmp.Default.Timeout = time.Duration(10 * time.Second) // Timeout better suited to walking err := gosnmp.Default.Connect() if err != nil { fmt.Printf("Connect err: %v\n", err) os.Exit(1) } defer gosnmp.Default.Conn.Close() err = gosnmp.Default.BulkWalk(oid, printValue) if err != nil { fmt.Printf("Walk Error: %v\n", err) os.Exit(1) } } func printValue(pdu gosnmp.SnmpPDU) error { fmt.Printf("%s = ", pdu.Name) switch pdu.Type { case gosnmp.OctetString: b := pdu.Value.([]byte) fmt.Printf("STRING: %s\n", string(b)) default: fmt.Printf("TYPE %d: %d\n", pdu.Type, gosnmp.ToBigInt(pdu.Value)) } return nil } golang-github-gosnmp-gosnmp-1.38.0/examples/walktcpexample/000077500000000000000000000000001465470716300240105ustar00rootroot00000000000000golang-github-gosnmp-gosnmp-1.38.0/examples/walktcpexample/main.go000066400000000000000000000036111465470716300252640ustar00rootroot00000000000000// 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. // This program demonstrates BulkWalk. package main import ( "flag" "fmt" "log" "os" "path/filepath" "time" "github.com/gosnmp/gosnmp" ) func main() { flag.Usage = func() { fmt.Printf("Usage:\n") fmt.Printf(" %s [-community=] host [oid]\n", filepath.Base(os.Args[0])) fmt.Printf(" host - the host to walk/scan\n") fmt.Printf(" oid - the MIB/Oid defining a subtree of values\n\n") flag.PrintDefaults() } var community string flag.StringVar(&community, "community", "public", "the community string for device") flag.Parse() if len(flag.Args()) < 1 { flag.Usage() os.Exit(1) } target := flag.Args()[0] var oid string if len(flag.Args()) > 1 { oid = flag.Args()[1] } gosnmp.Default.Target = target gosnmp.Default.Transport = "tcp" gosnmp.Default.Community = community gosnmp.Default.Timeout = time.Duration(10 * time.Second) // Timeout better suited to walking gosnmp.Default.Logger = gosnmp.NewLogger(log.New(os.Stdout, "", 0)) err := gosnmp.Default.Connect() if err != nil { fmt.Printf("Connect err: %v\n", err) os.Exit(1) } defer gosnmp.Default.Conn.Close() err = gosnmp.Default.BulkWalk(oid, printValue) if err != nil { fmt.Printf("Walk Error: %v\n", err) os.Exit(1) } // This may lead to the remote server closing the TCP connection time.Sleep(15 * time.Second) err = gosnmp.Default.BulkWalk(oid, printValue) if err != nil { fmt.Printf("Walk Error: %v\n", err) os.Exit(1) } } func printValue(pdu gosnmp.SnmpPDU) error { fmt.Printf("%s = ", pdu.Name) switch pdu.Type { case gosnmp.OctetString: b := pdu.Value.([]byte) fmt.Printf("STRING: %s\n", string(b)) default: fmt.Printf("TYPE %d: %d\n", pdu.Type, gosnmp.ToBigInt(pdu.Value)) } return nil } golang-github-gosnmp-gosnmp-1.38.0/generic_e2e_test.go000066400000000000000000000626341465470716300227210ustar00rootroot00000000000000// 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. // This set of end-to-end integration tests execute gosnmp against a real // SNMP MIB-2 host. Potential test systems could include a router, NAS box, printer, // or a linux box running snmpd, snmpsimd.py, etc. // // Ensure "gosnmp-test-host" is defined in your hosts file, and points to your // generic test system. //go:build all || end2end // +build all end2end package gosnmp import ( "fmt" "os" "strconv" "strings" "testing" "time" ) func getTarget(t *testing.T) (string, uint16) { var envTarget string var envPort string // set this flag to true in v3_testing_credentials.go if you want to use the // public SNMP demo service for tests if isUsingSnmpLabs() { envTarget = "demo.snmplabs.com" envPort = "161" } else { envTarget = os.Getenv("GOSNMP_TARGET") envPort = os.Getenv("GOSNMP_PORT") } if len(envTarget) <= 0 { t.Skip("environment variable not set: GOSNMP_TARGET") } if len(envPort) <= 0 { t.Skip("environment variable not set: GOSNMP_PORT") } port, _ := strconv.ParseUint(envPort, 10, 16) if port > 65535 { t.Skipf("invalid port number %d", port) } return envTarget, uint16(port) } func setupConnection(t *testing.T) { target, port := getTarget(t) Default.Target = target Default.Port = port err := Default.Connect() if err != nil { if len(target) > 0 { t.Fatalf("Connection failed. Is snmpd reachable on %s:%d?\n(err: %v)", target, port, err) } } } func setupConnectionInstance(gs *GoSNMP, t *testing.T) { target, port := getTarget(t) gs.Target = target gs.Port = port err := gs.Connect() if err != nil { if len(target) > 0 { t.Fatalf("Connection failed. Is snmpd reachable on %s:%d?\n(err: %v)", target, port, err) } } } func setupConnectionIPv4(t *testing.T) { target, port := getTarget(t) Default.Target = target Default.Port = port err := Default.ConnectIPv4() if err != nil { if len(target) > 0 { t.Fatalf("Connection failed. Is snmpd reachable on %s:%d?\n(err: %v)", target, port, err) } } } /* TODO work out ipv6 networking, etc func setupConnectionIPv6(t *testing.T) { envTarget := os.Getenv("GOSNMP_TARGET_IPV6") envPort := os.Getenv("GOSNMP_PORT_IPV6") if len(envTarget) <= 0 { t.Error("environment variable not set: GOSNMP_TARGET_IPV6") } Default.Target = envTarget if len(envPort) <= 0 { t.Error("environment variable not set: GOSNMP_PORT_IPV6") } port, _ := strconv.ParseUint(envPort, 10, 16) Default.Port = uint16(port) err := Default.ConnectIPv6() if err != nil { if len(envTarget) > 0 { t.Fatalf("Connection failed. Is snmpd reachable on %s:%s?\n(err: %v)", envTarget, envPort, err) } } } */ func TestGenericBasicGet(t *testing.T) { setupConnection(t) defer Default.Conn.Close() result, err := Default.Get([]string{".1.3.6.1.2.1.1.1.0"}) // SNMP MIB-2 sysDescr if err != nil { t.Fatalf("Get() failed with error => %v", err) } if len(result.Variables) != 1 { t.Fatalf("Expected result of size 1") } if result.Variables[0].Type != OctetString { t.Fatalf("Expected sysDescr to be OctetString") } sysDescr := result.Variables[0].Value.([]byte) if len(sysDescr) == 0 { t.Fatalf("Got a zero length sysDescr") } } func TestGenericBasicGetIPv4Only(t *testing.T) { setupConnectionIPv4(t) defer Default.Conn.Close() result, err := Default.Get([]string{".1.3.6.1.2.1.1.1.0"}) // SNMP MIB-2 sysDescr if err != nil { t.Fatalf("Get() failed with error => %v", err) } if len(result.Variables) != 1 { t.Fatalf("Expected result of size 1") } if result.Variables[0].Type != OctetString { t.Fatalf("Expected sysDescr to be OctetString") } sysDescr := result.Variables[0].Value.([]byte) if len(sysDescr) == 0 { t.Fatalf("Got a zero length sysDescr") } } /* func TestGenericBasicGetIPv6Only(t *testing.T) { setupConnectionIPv6(t) defer Default.Conn.Close() result, err := Default.Get([]string{".1.3.6.1.2.1.1.1.0"}) // SNMP MIB-2 sysDescr if err != nil { t.Fatalf("Get() failed with error => %v", err) } if len(result.Variables) != 1 { t.Fatalf("Expected result of size 1") } if result.Variables[0].Type != OctetString { t.Fatalf("Expected sysDescr to be OctetString") } sysDescr := result.Variables[0].Value.([]byte) if len(sysDescr) == 0 { t.Fatalf("Got a zero length sysDescr") } } */ func TestGenericMultiGet(t *testing.T) { setupConnection(t) defer Default.Conn.Close() oids := []string{ ".1.3.6.1.2.1.1.1.0", // SNMP MIB-2 sysDescr ".1.3.6.1.2.1.1.5.0", // SNMP MIB-2 sysName } result, err := Default.Get(oids) if err != nil { t.Fatalf("Get() failed with error => %v", err) } if len(result.Variables) != 2 { t.Fatalf("Expected result of size 2") } for _, v := range result.Variables { if v.Type != OctetString { t.Fatalf("Expected OctetString") } } } func TestGenericGetNext(t *testing.T) { setupConnection(t) defer Default.Conn.Close() sysDescrOid := ".1.3.6.1.2.1.1.1.0" // SNMP MIB-2 sysDescr result, err := Default.GetNext([]string{sysDescrOid}) if err != nil { t.Fatalf("GetNext() failed with error => %v", err) } if len(result.Variables) != 1 { t.Fatalf("Expected result of size 1") } if result.Variables[0].Name == sysDescrOid { t.Fatalf("Expected next OID") } } func TestGenericWalk(t *testing.T) { setupConnection(t) defer Default.Conn.Close() result, err := Default.WalkAll("") if err != nil { t.Fatalf("WalkAll() Failed with error => %v", err) } if len(result) <= 1 { t.Fatalf("Expected multiple values, got %d", len(result)) } } func TestGenericBulkWalk(t *testing.T) { setupConnection(t) defer Default.Conn.Close() result, err := Default.BulkWalkAll("") if err != nil { t.Fatalf("BulkWalkAll() Failed with error => %v", err) } if len(result) <= 1 { t.Fatalf("Expected multiple values, got %d", len(result)) } } func TestV1BulkWalkError(t *testing.T) { g := &GoSNMP{ Version: Version1, } setupConnectionInstance(g, t) g.Conn.Close() _, err := g.BulkWalkAll("") if err == nil { t.Fatalf("BulkWalkAll() should fail in SNMPv1 but returned nil") } } // Standard exception/error tests func TestMaxOids(t *testing.T) { setupConnection(t) defer Default.Conn.Close() Default.MaxOids = 1 var err error oids := []string{".1.3.6.1.2.1.1.7.0", ".1.3.6.1.2.1.2.2.1.10.1"} // 2 arbitrary Oids errString := "oid count (2) is greater than MaxOids (1)" _, err = Default.Get(oids) if err == nil { t.Fatalf("Expected too many oids failure. Got nil") } else if err.Error() != errString { t.Fatalf("Expected too many oids failure. Got => %v", err) } _, err = Default.GetNext(oids) if err == nil { t.Fatalf("Expected too many oids failure. Got nil") } else if err.Error() != errString { t.Fatalf("Expected too many oids failure. Got => %v", err) } _, err = Default.GetBulk(oids, 0, 0) if err == nil { t.Fatalf("Expected too many oids failure. Got nil") } else if err.Error() != errString { t.Fatalf("Expected too many oids failure. Got => %v", err) } } func TestGenericFailureUnknownHost(t *testing.T) { unknownHost := fmt.Sprintf("gosnmp-test-unknown-host-%d", time.Now().UTC().UnixNano()) Default.Target = unknownHost err := Default.Connect() if err == nil { t.Fatalf("Expected connection failure due to unknown host") } if !strings.Contains(strings.ToLower(err.Error()), "no such host") { t.Fatalf("Expected connection error of type 'no such host'! Got => %v", err) } _, err = Default.Get([]string{".1.3.6.1.2.1.1.1.0"}) // SNMP MIB-2 sysDescr if err == nil { t.Fatalf("Expected get to fail due to missing connection") } } func TestGenericFailureConnectionTimeout(t *testing.T) { t.Skip("local testing - skipping this slow one") // TODO test tag, or something envTarget := os.Getenv("GOSNMP_TARGET") if len(envTarget) <= 0 { t.Skip("local testing - skipping this slow one") } Default.Target = "198.51.100.1" // Black hole err := Default.Connect() if err != nil { t.Fatalf("Did not expect connection error with IP address") } _, err = Default.Get([]string{".1.3.6.1.2.1.1.1.0"}) // SNMP MIB-2 sysDescr if err == nil { t.Fatalf("Expected Get() to fail due to invalid IP") } if !strings.Contains(err.Error(), "timeout") { t.Fatalf("Expected timeout error. Got => %v", err) } } func TestGenericFailureConnectionRefused(t *testing.T) { Default.Target = "127.0.0.1" Default.Port = 1 // Don't expect SNMP to be running here! err := Default.Connect() if err != nil { t.Fatalf("Did not expect connection error with IP address") } _, err = Default.Get([]string{".1.3.6.1.2.1.1.1.0"}) // SNMP MIB-2 sysDescr if err == nil { t.Fatalf("Expected Get() to fail due to invalid port") } if !(strings.Contains(err.Error(), "connection refused") || strings.Contains(err.Error(), "forcibly closed")) { t.Fatalf("Expected connection refused error. Got => %v", err) } } func TestSnmpV3NoAuthNoPrivBasicGet(t *testing.T) { Default.Version = Version3 Default.MsgFlags = NoAuthNoPriv Default.SecurityModel = UserSecurityModel Default.SecurityParameters = &UsmSecurityParameters{UserName: getUserName(t, NoAuth, NoPriv)} setupConnection(t) defer Default.Conn.Close() result, err := Default.Get([]string{".1.3.6.1.2.1.1.1.0"}) // SNMP MIB-2 sysDescr if err != nil { t.Fatalf("Get() failed with error => %v", err) } if len(result.Variables) != 1 { t.Fatalf("Expected result of size 1") } sysDescr := result.Variables[0].Value.([]byte) if len(sysDescr) == 0 { t.Fatalf("Got a zero length sysDescr") } } func TestSnmpV3AuthMD5NoPrivGet(t *testing.T) { if !isUsingSnmpLabs() { t.Skip("This test is currently only working when using demo.snmplabs.com as test device.") } Default.Version = Version3 Default.MsgFlags = AuthNoPriv Default.SecurityModel = UserSecurityModel Default.SecurityParameters = &UsmSecurityParameters{UserName: getUserName(t, MD5, NoPriv), AuthenticationProtocol: MD5, AuthenticationPassphrase: getAuthKey(t, MD5, NoPriv)} setupConnection(t) defer Default.Conn.Close() result, err := Default.Get([]string{".1.3.6.1.2.1.1.1.0"}) // SNMP MIB-2 sysDescr if err != nil { t.Fatalf("Get() failed with error => %v", err) } if len(result.Variables) != 1 { t.Fatalf("Expected result of size 1") } if result.Variables[0].Type != OctetString { t.Fatalf("Expected sysDescr to be OctetString") } sysDescr := result.Variables[0].Value.([]byte) if len(sysDescr) == 0 { t.Fatalf("Got a zero length sysDescr") } } func TestSnmpV3AuthMD5PrivAES256CGet(t *testing.T) { if !isUsingSnmpLabs() { t.Skip("This test is currently only working when using demo.snmplabs.com as test device.") } Default.Version = Version3 Default.MsgFlags = AuthPriv Default.SecurityModel = UserSecurityModel Default.SecurityParameters = &UsmSecurityParameters{ UserName: getUserName(t, MD5, AES256C), AuthenticationProtocol: MD5, AuthenticationPassphrase: getAuthKey(t, MD5, AES256C), PrivacyProtocol: AES256C, PrivacyPassphrase: getPrivKey(t, MD5, AES256C), } setupConnection(t) defer Default.Conn.Close() result, err := Default.Get([]string{".1.3.6.1.2.1.1.1.0"}) // SNMP MIB-2 sysDescr if err != nil { t.Fatalf("Get() failed with error => %v", err) } if len(result.Variables) != 1 { t.Fatalf("Expected result of size 1") } if result.Variables[0].Type != OctetString { t.Fatalf("Expected sysDescr to be OctetString") } sysDescr := result.Variables[0].Value.([]byte) if len(sysDescr) == 0 { t.Fatalf("Got a zero length sysDescr") } } func TestSnmpV3AuthSHANoPrivGet(t *testing.T) { if !isUsingSnmpLabs() { t.Skip("This test is currently only working when using demo.snmplabs.com as test device.") } Default.Version = Version3 Default.MsgFlags = AuthNoPriv Default.SecurityModel = UserSecurityModel Default.SecurityParameters = &UsmSecurityParameters{UserName: getUserName(t, SHA, NoPriv), AuthenticationProtocol: SHA, AuthenticationPassphrase: getAuthKey(t, SHA, NoPriv)} setupConnection(t) defer Default.Conn.Close() result, err := Default.Get([]string{".1.3.6.1.2.1.1.1.0"}) // SNMP MIB-2 sysDescr if err != nil { t.Fatalf("Get() failed with error => %v", err) } if len(result.Variables) != 1 { t.Fatalf("Expected result of size 1") } if result.Variables[0].Type != OctetString { t.Fatalf("Expected sysDescr to be OctetString") } sysDescr := result.Variables[0].Value.([]byte) if len(sysDescr) == 0 { t.Fatalf("Got a zero length sysDescr") } } func TestSnmpV3AuthSHAPrivAESGet(t *testing.T) { if !isUsingSnmpLabs() { t.Skip("This test is currently only working when using demo.snmplabs.com as test device.") } Default.Version = Version3 Default.MsgFlags = AuthPriv Default.SecurityModel = UserSecurityModel Default.SecurityParameters = &UsmSecurityParameters{ UserName: getUserName(t, SHA, AES), AuthenticationProtocol: SHA, AuthenticationPassphrase: getAuthKey(t, SHA, AES), PrivacyProtocol: AES, PrivacyPassphrase: getPrivKey(t, SHA, AES), } setupConnection(t) defer Default.Conn.Close() result, err := Default.Get([]string{".1.3.6.1.2.1.1.1.0"}) // SNMP MIB-2 sysDescr if err != nil { t.Fatalf("Get() failed with error => %v", err) } if len(result.Variables) != 1 { t.Fatalf("Expected result of size 1") } if result.Variables[0].Type != OctetString { t.Fatalf("Expected sysDescr to be OctetString") } sysDescr := result.Variables[0].Value.([]byte) if len(sysDescr) == 0 { t.Fatalf("Got a zero length sysDescr") } } func TestSnmpV3AuthSHAPrivAES256CGet(t *testing.T) { if !isUsingSnmpLabs() { t.Skip("This test is currently only working when using demo.snmplabs.com as test device.") } Default.Version = Version3 Default.MsgFlags = AuthPriv Default.SecurityModel = UserSecurityModel Default.SecurityParameters = &UsmSecurityParameters{ UserName: getUserName(t, SHA, AES256C), AuthenticationProtocol: SHA, AuthenticationPassphrase: getAuthKey(t, SHA, AES256C), PrivacyProtocol: AES256C, PrivacyPassphrase: getPrivKey(t, SHA, AES256C), } setupConnection(t) defer Default.Conn.Close() result, err := Default.Get([]string{".1.3.6.1.2.1.1.1.0"}) // SNMP MIB-2 sysDescr if err != nil { t.Fatalf("Get() failed with error => %v", err) } if len(result.Variables) != 1 { t.Fatalf("Expected result of size 1") } if result.Variables[0].Type != OctetString { t.Fatalf("Expected sysDescr to be OctetString") } sysDescr := result.Variables[0].Value.([]byte) if len(sysDescr) == 0 { t.Fatalf("Got a zero length sysDescr") } } func TestSnmpV3AuthSHA224NoPrivGet(t *testing.T) { if !isUsingSnmpLabs() { t.Skip("This test is currently only working when using demo.snmplabs.com as test device.") } Default.Version = Version3 Default.MsgFlags = AuthNoPriv Default.SecurityModel = UserSecurityModel Default.SecurityParameters = &UsmSecurityParameters{UserName: getUserName(t, SHA224, NoPriv), AuthenticationProtocol: SHA224, AuthenticationPassphrase: getAuthKey(t, SHA224, NoPriv)} setupConnection(t) defer Default.Conn.Close() result, err := Default.Get([]string{".1.3.6.1.2.1.1.1.0"}) // SNMP MIB-2 sysDescr if err != nil { t.Fatalf("Get() failed with error => %v", err) } if len(result.Variables) != 1 { t.Fatalf("Expected result of size 1") } if result.Variables[0].Type != OctetString { t.Fatalf("Expected sysDescr to be OctetString") } sysDescr := result.Variables[0].Value.([]byte) if len(sysDescr) == 0 { t.Fatalf("Got a zero length sysDescr") } } func TestSnmpV3AuthSHA256NoPrivGet(t *testing.T) { if !isUsingSnmpLabs() { t.Skip("This test is currently only working when using demo.snmplabs.com as test device.") } Default.Version = Version3 Default.MsgFlags = AuthNoPriv Default.SecurityModel = UserSecurityModel Default.SecurityParameters = &UsmSecurityParameters{UserName: getUserName(t, SHA256, NoPriv), AuthenticationProtocol: SHA256, AuthenticationPassphrase: getAuthKey(t, SHA256, NoPriv)} setupConnection(t) defer Default.Conn.Close() result, err := Default.Get([]string{".1.3.6.1.2.1.1.1.0"}) // SNMP MIB-2 sysDescr if err != nil { t.Fatalf("Get() failed with error => %v", err) } if len(result.Variables) != 1 { t.Fatalf("Expected result of size 1") } if result.Variables[0].Type != OctetString { t.Fatalf("Expected sysDescr to be OctetString") } sysDescr := result.Variables[0].Value.([]byte) if len(sysDescr) == 0 { t.Fatalf("Got a zero length sysDescr") } } func TestSnmpV3AuthSHA384NoPrivGet(t *testing.T) { if !isUsingSnmpLabs() { t.Skip("This test is currently only working when using demo.snmplabs.com as test device.") } Default.Version = Version3 Default.MsgFlags = AuthNoPriv Default.SecurityModel = UserSecurityModel Default.SecurityParameters = &UsmSecurityParameters{UserName: getUserName(t, SHA384, NoPriv), AuthenticationProtocol: SHA384, AuthenticationPassphrase: getAuthKey(t, SHA384, NoPriv)} setupConnection(t) defer Default.Conn.Close() result, err := Default.Get([]string{".1.3.6.1.2.1.1.1.0"}) // SNMP MIB-2 sysDescr if err != nil { t.Fatalf("Get() failed with error => %v", err) } if len(result.Variables) != 1 { t.Fatalf("Expected result of size 1") } if result.Variables[0].Type != OctetString { t.Fatalf("Expected sysDescr to be OctetString") } sysDescr := result.Variables[0].Value.([]byte) if len(sysDescr) == 0 { t.Fatalf("Got a zero length sysDescr") } } func TestSnmpV3AuthSHA512NoPrivGet(t *testing.T) { if !isUsingSnmpLabs() { t.Skip("This test is currently only working when using demo.snmplabs.com as test device.") } Default.Version = Version3 Default.MsgFlags = AuthNoPriv Default.SecurityModel = UserSecurityModel Default.SecurityParameters = &UsmSecurityParameters{UserName: getUserName(t, SHA512, NoPriv), AuthenticationProtocol: SHA512, AuthenticationPassphrase: getAuthKey(t, SHA512, NoPriv)} setupConnection(t) defer Default.Conn.Close() result, err := Default.Get([]string{".1.3.6.1.2.1.1.1.0"}) // SNMP MIB-2 sysDescr if err != nil { t.Fatalf("Get() failed with error => %v", err) } if len(result.Variables) != 1 { t.Fatalf("Expected result of size 1") } if result.Variables[0].Type != OctetString { t.Fatalf("Expected sysDescr to be OctetString") } sysDescr := result.Variables[0].Value.([]byte) if len(sysDescr) == 0 { t.Fatalf("Got a zero length sysDescr") } } func TestSnmpV3AuthSHA512PrivAES192Get(t *testing.T) { t.Skip("AES-192 Blumenthal is currently known to have issues.") Default.Version = Version3 Default.MsgFlags = AuthPriv Default.SecurityModel = UserSecurityModel Default.SecurityParameters = &UsmSecurityParameters{ UserName: getUserName(t, SHA512, AES192), AuthenticationProtocol: SHA512, AuthenticationPassphrase: getAuthKey(t, SHA512, AES192), PrivacyProtocol: AES192, PrivacyPassphrase: getPrivKey(t, SHA512, AES192), } setupConnection(t) defer Default.Conn.Close() result, err := Default.Get([]string{".1.3.6.1.2.1.1.1.0"}) // SNMP MIB-2 sysDescr if err != nil { t.Fatalf("Get() failed with error => %v", err) } if len(result.Variables) != 1 { t.Fatalf("Expected result of size 1") } if result.Variables[0].Type != OctetString { t.Fatalf("Expected sysDescr to be OctetString") } sysDescr := result.Variables[0].Value.([]byte) if len(sysDescr) == 0 { t.Fatalf("Got a zero length sysDescr") } } func TestSnmpV3AuthSHA512PrivAES192CGet(t *testing.T) { if !isUsingSnmpLabs() { t.Skip("This test is currently only working when using demo.snmplabs.com as test device.") } Default.Version = Version3 Default.MsgFlags = AuthPriv Default.SecurityModel = UserSecurityModel Default.SecurityParameters = &UsmSecurityParameters{ UserName: getUserName(t, SHA512, AES192C), AuthenticationProtocol: SHA512, AuthenticationPassphrase: getAuthKey(t, SHA512, AES192C), PrivacyProtocol: AES192C, PrivacyPassphrase: getPrivKey(t, SHA512, AES192C), } setupConnection(t) defer Default.Conn.Close() result, err := Default.Get([]string{".1.3.6.1.2.1.1.1.0"}) // SNMP MIB-2 sysDescr if err != nil { t.Fatalf("Get() failed with error => %v", err) } if len(result.Variables) != 1 { t.Fatalf("Expected result of size 1") } if result.Variables[0].Type != OctetString { t.Fatalf("Expected sysDescr to be OctetString") } sysDescr := result.Variables[0].Value.([]byte) if len(sysDescr) == 0 { t.Fatalf("Got a zero length sysDescr") } } // SHA 512 + AES256C (Reeder) func TestSnmpV3AuthSHA512PrivAES256CGet(t *testing.T) { if !isUsingSnmpLabs() { t.Skip("This test is currently only working when using demo.snmplabs.com as test device.") } Default.Version = Version3 Default.MsgFlags = AuthPriv Default.SecurityModel = UserSecurityModel Default.SecurityParameters = &UsmSecurityParameters{ UserName: getUserName(t, SHA512, AES256C), AuthenticationProtocol: SHA512, AuthenticationPassphrase: getAuthKey(t, SHA512, AES256C), PrivacyProtocol: AES256C, PrivacyPassphrase: getPrivKey(t, SHA512, AES256C), } setupConnection(t) defer Default.Conn.Close() result, err := Default.Get([]string{".1.3.6.1.2.1.1.1.0"}) // SNMP MIB-2 sysDescr if err != nil { t.Fatalf("Get() failed with error => %v", err) } if len(result.Variables) != 1 { t.Fatalf("Expected result of size 1") } if result.Variables[0].Type != OctetString { t.Fatalf("Expected sysDescr to be OctetString") } sysDescr := result.Variables[0].Value.([]byte) if len(sysDescr) == 0 { t.Fatalf("Got a zero length sysDescr") } } func TestSnmpV3AuthMD5PrivDESGet(t *testing.T) { if !isUsingSnmpLabs() { t.Skip("This test is currently only working when using demo.snmplabs.com as test device.") } Default.Version = Version3 Default.MsgFlags = AuthPriv Default.SecurityModel = UserSecurityModel Default.SecurityParameters = &UsmSecurityParameters{UserName: getUserName(t, MD5, DES), AuthenticationProtocol: MD5, AuthenticationPassphrase: getAuthKey(t, MD5, DES), PrivacyProtocol: DES, PrivacyPassphrase: getPrivKey(t, MD5, DES)} setupConnection(t) defer Default.Conn.Close() result, err := Default.Get([]string{".1.3.6.1.2.1.1.1.0"}) // SNMP MIB-2 sysDescr if err != nil { t.Fatalf("Get() failed with error => %v", err) } if len(result.Variables) != 1 { t.Fatalf("Expected result of size 1") } if result.Variables[0].Type != OctetString { t.Fatalf("Expected sysDescr to be OctetString") } sysDescr := result.Variables[0].Value.([]byte) if len(sysDescr) == 0 { t.Fatalf("Got a zero length sysDescr") } } func TestSnmpV3AuthSHAPrivDESGet(t *testing.T) { if !isUsingSnmpLabs() { t.Skip("This test is currently only working when using demo.snmplabs.com as test device.") } Default.Version = Version3 Default.MsgFlags = AuthPriv Default.SecurityModel = UserSecurityModel Default.SecurityParameters = &UsmSecurityParameters{UserName: getUserName(t, SHA, DES), AuthenticationProtocol: SHA, AuthenticationPassphrase: getAuthKey(t, SHA, DES), PrivacyProtocol: DES, PrivacyPassphrase: getPrivKey(t, SHA, DES)} setupConnection(t) defer Default.Conn.Close() result, err := Default.Get([]string{".1.3.6.1.2.1.1.1.0"}) // SNMP MIB-2 sysDescr if err != nil { t.Fatalf("Get() failed with error => %v", err) } if len(result.Variables) != 1 { t.Fatalf("Expected result of size 1") } if result.Variables[0].Type != OctetString { t.Fatalf("Expected sysDescr to be OctetString") } sysDescr := result.Variables[0].Value.([]byte) if len(sysDescr) == 0 { t.Fatalf("Got a zero length sysDescr") } } func TestSnmpV3AuthMD5PrivAESGet(t *testing.T) { if !isUsingSnmpLabs() { t.Skip("This test is currently only working when using demo.snmplabs.com as test device.") } Default.Version = Version3 Default.MsgFlags = AuthPriv Default.SecurityModel = UserSecurityModel Default.SecurityParameters = &UsmSecurityParameters{UserName: getUserName(t, MD5, AES), AuthenticationProtocol: MD5, AuthenticationPassphrase: getAuthKey(t, MD5, AES), PrivacyProtocol: AES, PrivacyPassphrase: getPrivKey(t, MD5, AES)} setupConnection(t) defer Default.Conn.Close() result, err := Default.Get([]string{".1.3.6.1.2.1.1.1.0"}) // SNMP MIB-2 sysDescr if err != nil { t.Fatalf("Get() failed with error => %v", err) } if len(result.Variables) != 1 { t.Fatalf("Expected result of size 1") } if result.Variables[0].Type != OctetString { t.Fatalf("Expected sysDescr to be OctetString") } sysDescr := result.Variables[0].Value.([]byte) if len(sysDescr) == 0 { t.Fatalf("Got a zero length sysDescr") } } func TestSnmpV3PrivEmptyPrivatePassword(t *testing.T) { if !isUsingSnmpLabs() { t.Skip("This test is currently only working when using demo.snmplabs.com as test device.") } Default.Version = Version3 Default.MsgFlags = AuthPriv Default.SecurityModel = UserSecurityModel Default.SecurityParameters = &UsmSecurityParameters{UserName: getUserName(t, SHA, AES), AuthenticationProtocol: SHA, AuthenticationPassphrase: getAuthKey(t, SHA, AES), PrivacyProtocol: AES, PrivacyPassphrase: ""} err := Default.Connect() if err == nil { t.Fatalf("Expected validation error for empty PrivacyPassphrase") } } func TestSnmpV3AuthNoPrivEmptyPrivatePassword(t *testing.T) { if !isUsingSnmpLabs() { t.Skip("This test is currently only working when using demo.snmplabs.com as test device.") } Default.Version = Version3 Default.MsgFlags = AuthNoPriv Default.SecurityModel = UserSecurityModel Default.SecurityParameters = &UsmSecurityParameters{UserName: getUserName(t, SHA, NoPriv), AuthenticationProtocol: SHA, AuthenticationPassphrase: getAuthKey(t, SHA, NoPriv), PrivacyProtocol: AES, PrivacyPassphrase: getPrivKey(t, SHA, NoPriv)} err := Default.Connect() if err == nil { t.Fatalf("Expected validation error for empty PrivacyPassphrase") } } golang-github-gosnmp-gosnmp-1.38.0/go.mod000066400000000000000000000006351465470716300202630ustar00rootroot00000000000000module github.com/gosnmp/gosnmp go 1.20 require ( github.com/golang/mock v1.6.0 github.com/google/go-cmp v0.6.0 github.com/google/gopacket v1.1.19 github.com/stretchr/testify v1.9.0 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect golang.org/x/net v0.15.0 // indirect golang.org/x/sys v0.13.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) golang-github-gosnmp-gosnmp-1.38.0/go.sum000066400000000000000000000104261465470716300203070ustar00rootroot00000000000000github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= golang-github-gosnmp-gosnmp-1.38.0/goimports2000077500000000000000000000004571465470716300212120ustar00rootroot00000000000000#!/bin/bash # remove all blank lines in go 'imports' statements, # then sort with goimports if [ $# != 1 ] ; then echo "usage: $0 " exit 1 fi EXE="sed" if [[ "$OSTYPE" == "darwin"* ]]; then EXE="ssed" fi $EXE -i ' /^import/,/)/ { /^$/ d } ' $1 goimports -w $1 gofmt -s -w $1 golang-github-gosnmp-gosnmp-1.38.0/goimports2_all000077500000000000000000000003021465470716300220270ustar00rootroot00000000000000#!/bin/bash # run goimports2 script across all go files, excluding the following directories: # - mocks find . -type d -name mocks -prune -o -type f -name '*.go' -exec ./goimports2 '{}' ';' golang-github-gosnmp-gosnmp-1.38.0/gosnmp.go000066400000000000000000000542661465470716300210200ustar00rootroot00000000000000// 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. // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package gosnmp import ( "context" "crypto/rand" "fmt" "math" "math/big" "net" "strconv" "sync/atomic" "syscall" "time" ) const ( // MaxOids is the maximum number of OIDs permitted in a single call, // otherwise error. MaxOids too high can cause remote devices to fail // strangely. 60 seems to be a common value that works, but you will want // to change this in the GoSNMP struct MaxOids = 60 // Base OID for MIB-2 defined SNMP variables baseOid = ".1.3.6.1.2.1" // Max oid sub-identifier value // https://tools.ietf.org/html/rfc2578#section-7.1.3 MaxObjectSubIdentifierValue = 4294967295 // Java SNMP uses 50, snmp-net uses 10 defaultMaxRepetitions = 50 // "udp" and "tcp" are used regularly, prevent 'goconst' complaints udp = "udp" tcp = "tcp" ) // GoSNMP represents GoSNMP library state. type GoSNMP struct { // Conn is net connection to use, typically established using GoSNMP.Connect(). Conn net.Conn // Target is an ipv4 address. Target string // Port is a port. Port uint16 // Transport is the transport protocol to use ("udp" or "tcp"); if unset "udp" will be used. Transport string // Community is an SNMP Community string. Community string // Version is an SNMP Version. Version SnmpVersion // Context allows for overall deadlines and cancellation. Context context.Context // Timeout is the timeout for one SNMP request/response. Timeout time.Duration // Set the number of retries to attempt. Retries int // Double timeout in each retry. ExponentialTimeout bool // Logger is the GoSNMP.Logger to use for debugging. // For verbose logging to stdout: // x.Logger = NewLogger(log.New(os.Stdout, "", 0)) // For Release builds, you can turn off logging entirely by using the go build tag "gosnmp_nodebug" even if the logger was installed. Logger Logger // Message hook methods allow passing in a functions at various points in the packet handling. // For example, this can be used to collect packet timing, add metrics, or implement tracing. /* */ // PreSend is called before a packet is sent. PreSend func(*GoSNMP) // OnSent is called when a packet is sent. OnSent func(*GoSNMP) // OnRecv is called when a packet is received. OnRecv func(*GoSNMP) // OnRetry is called when a retry attempt is done. OnRetry func(*GoSNMP) // OnFinish is called when the request completed. OnFinish func(*GoSNMP) // MaxOids is the maximum number of oids allowed in a Get(). // (default: MaxOids) MaxOids int // MaxRepetitions sets the GETBULK max-repetitions used by BulkWalk* // Unless MaxRepetitions is specified it will use defaultMaxRepetitions (50) // This may cause issues with some devices, if so set MaxRepetitions lower. // See comments in https://github.com/gosnmp/gosnmp/issues/100 MaxRepetitions uint32 // NonRepeaters sets the GETBULK max-repeaters used by BulkWalk*. // (default: 0 as per RFC 1905) NonRepeaters int // UseUnconnectedUDPSocket if set, changes net.Conn to be unconnected UDP socket. // Some multi-homed network gear isn't smart enough to send SNMP responses // from the address it received the requests on. To work around that, // we open unconnected UDP socket and use sendto/recvfrom. UseUnconnectedUDPSocket bool // If Control is not nil, it is called after creating the network // connection but before actually dialing. // // Can be used when UseUnconnectedUDPSocket is set to false or when using TCP // in scenario where specific options on the underlying socket are nedded. // Refer to https://pkg.go.dev/net#Dialer Control func(network, address string, c syscall.RawConn) error // LocalAddr is the local address in the format "address:port" to use when connecting an Target address. // If the port parameter is empty or "0", as in // "127.0.0.1:" or "[::1]:0", a port number is automatically (random) chosen. LocalAddr string // netsnmp has '-C APPOPTS - set various application specific behaviours' // // - 'c: do not check returned OIDs are increasing' - use AppOpts = map[string]interface{"c":true} with // Walk() or BulkWalk(). The library user needs to implement their own policy for terminating walks. // - 'p,i,I,t,E' -> pull requests welcome AppOpts map[string]interface{} // Internal - used to sync requests to responses. requestID uint32 random uint32 rxBuf *[rxBufSize]byte // has to be pointer due to https://github.com/golang/go/issues/11728 // MsgFlags is an SNMPV3 MsgFlags. MsgFlags SnmpV3MsgFlags // SecurityModel is an SNMPV3 Security Model. SecurityModel SnmpV3SecurityModel // SecurityParameters is an SNMPV3 Security Model parameters struct. SecurityParameters SnmpV3SecurityParameters // TrapSecurityParametersTable is a mapping of identifiers to corresponding SNMP V3 Security Model parameters // right now only supported for receiving traps, variable name to make that clear TrapSecurityParametersTable *SnmpV3SecurityParametersTable // ContextEngineID is SNMPV3 ContextEngineID in ScopedPDU. ContextEngineID string // ContextName is SNMPV3 ContextName in ScopedPDU ContextName string // Internal - used to sync requests to responses - snmpv3. msgID uint32 // Internal - we use to send packets if using unconnected socket. uaddr *net.UDPAddr } // Default connection settings // //nolint:gochecknoglobals var Default = &GoSNMP{ Port: 161, Transport: udp, Community: "public", Version: Version2c, Timeout: time.Duration(2) * time.Second, Retries: 3, ExponentialTimeout: true, MaxOids: MaxOids, } // SnmpPDU will be used when doing SNMP Set's type SnmpPDU struct { // The value to be set by the SNMP set, or the value when // sending a trap Value interface{} // Name is an oid in string format eg ".1.3.6.1.4.9.27" Name string // The type of the value eg Integer Type Asn1BER } const AsnContext = 0x80 const AsnExtensionID = 0x1F const AsnExtensionTag = (AsnContext | AsnExtensionID) // 0x9F //go:generate stringer -type Asn1BER // Asn1BER is the type of the SNMP PDU type Asn1BER byte // Asn1BER's - http://www.ietf.org/rfc/rfc1442.txt const ( EndOfContents Asn1BER = 0x00 UnknownType Asn1BER = 0x00 Boolean Asn1BER = 0x01 Integer Asn1BER = 0x02 BitString Asn1BER = 0x03 OctetString Asn1BER = 0x04 Null Asn1BER = 0x05 ObjectIdentifier Asn1BER = 0x06 ObjectDescription Asn1BER = 0x07 IPAddress Asn1BER = 0x40 Counter32 Asn1BER = 0x41 Gauge32 Asn1BER = 0x42 TimeTicks Asn1BER = 0x43 Opaque Asn1BER = 0x44 NsapAddress Asn1BER = 0x45 Counter64 Asn1BER = 0x46 Uinteger32 Asn1BER = 0x47 OpaqueFloat Asn1BER = 0x78 OpaqueDouble Asn1BER = 0x79 NoSuchObject Asn1BER = 0x80 NoSuchInstance Asn1BER = 0x81 EndOfMibView Asn1BER = 0x82 ) //go:generate stringer -type SNMPError // SNMPError is the type for standard SNMP errors. type SNMPError uint8 // SNMP Errors const ( NoError SNMPError = iota // No error occurred. This code is also used in all request PDUs, since they have no error status to report. TooBig // The size of the Response-PDU would be too large to transport. NoSuchName // The name of a requested object was not found. BadValue // A value in the request didn't match the structure that the recipient of the request had for the object. For example, an object in the request was specified with an incorrect length or type. ReadOnly // An attempt was made to set a variable that has an Access value indicating that it is read-only. GenErr // An error occurred other than one indicated by a more specific error code in this table. NoAccess // Access was denied to the object for security reasons. WrongType // The object type in a variable binding is incorrect for the object. WrongLength // A variable binding specifies a length incorrect for the object. WrongEncoding // A variable binding specifies an encoding incorrect for the object. WrongValue // The value given in a variable binding is not possible for the object. NoCreation // A specified variable does not exist and cannot be created. InconsistentValue // A variable binding specifies a value that could be held by the variable but cannot be assigned to it at this time. ResourceUnavailable // An attempt to set a variable required a resource that is not available. CommitFailed // An attempt to set a particular variable failed. UndoFailed // An attempt to set a particular variable as part of a group of variables failed, and the attempt to then undo the setting of other variables was not successful. AuthorizationError // A problem occurred in authorization. NotWritable // The variable cannot be written or created. InconsistentName // The name in a variable binding specifies a variable that does not exist. ) // // Public Functions (main interface) // // Connect creates and opens a socket. Because UDP is a connectionless // protocol, you won't know if the remote host is responding until you send // packets. Neither will you know if the host is regularly disappearing and reappearing. // // For historical reasons (ie this is part of the public API), the method won't // be renamed to Dial(). func (x *GoSNMP) Connect() error { return x.connect("") } // ConnectIPv4 forces an IPv4-only connection func (x *GoSNMP) ConnectIPv4() error { return x.connect("4") } // ConnectIPv6 forces an IPv6-only connection func (x *GoSNMP) ConnectIPv6() error { return x.connect("6") } // connect to address addr on the given network // // https://golang.org/pkg/net/#Dial gives acceptable network values as: // // "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only), "udp", "udp4" (IPv4-only),"udp6" (IPv6-only), "ip", // "ip4" (IPv4-only), "ip6" (IPv6-only), "unix", "unixgram" and "unixpacket" func (x *GoSNMP) connect(networkSuffix string) error { err := x.validateParameters() if err != nil { return err } x.Transport += networkSuffix if err = x.netConnect(); err != nil { return fmt.Errorf("error establishing connection to host: %w", err) } if x.random == 0 { n, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt32)) // returns a uniform random value in [0, 2147483647]. if err != nil { return fmt.Errorf("error occurred while generating random: %w", err) } x.random = uint32(n.Uint64()) } // http://tools.ietf.org/html/rfc3412#section-6 - msgID only uses the first 31 bits // msgID INTEGER (0..2147483647) x.msgID = x.random // RequestID is Integer32 from SNMPV2-SMI and uses all 32 bits x.requestID = x.random x.rxBuf = new([rxBufSize]byte) return nil } // Performs the real socket opening network operation. This can be used to do a // reconnect (needed for TCP) func (x *GoSNMP) netConnect() error { var err error var localAddr net.Addr addr := net.JoinHostPort(x.Target, strconv.Itoa(int(x.Port))) switch x.Transport { case "udp", "udp4", "udp6": if localAddr, err = net.ResolveUDPAddr(x.Transport, x.LocalAddr); err != nil { return err } if addr4 := localAddr.(*net.UDPAddr).IP.To4(); addr4 != nil { x.Transport = "udp4" } if x.UseUnconnectedUDPSocket { x.uaddr, err = net.ResolveUDPAddr(x.Transport, addr) if err != nil { return err } x.Conn, err = net.ListenUDP(x.Transport, localAddr.(*net.UDPAddr)) return err } case "tcp", "tcp4", "tcp6": if localAddr, err = net.ResolveTCPAddr(x.Transport, x.LocalAddr); err != nil { return err } if addr4 := localAddr.(*net.TCPAddr).IP.To4(); addr4 != nil { x.Transport = "tcp4" } } dialer := net.Dialer{Timeout: x.Timeout, LocalAddr: localAddr, Control: x.Control} x.Conn, err = dialer.DialContext(x.Context, x.Transport, addr) return err } func (x *GoSNMP) validateParameters() error { if x.Transport == "" { x.Transport = udp } if x.MaxOids == 0 { x.MaxOids = MaxOids } else if x.MaxOids < 0 { return fmt.Errorf("field MaxOids cannot be less than 0") } if x.Version == Version3 { // TODO: setting the Reportable flag violates rfc3412#6.4 if PDU is of type SNMPv2Trap. // See if we can do this smarter and remove bitclear fix from trap.go:57 x.MsgFlags |= Reportable // tell the snmp server that a report PDU MUST be sent err := x.validateParametersV3() if err != nil { return err } err = x.SecurityParameters.init(x.Logger) if err != nil { return err } } if x.Context == nil { x.Context = context.Background() } return nil } func (x *GoSNMP) MkSnmpPacket(pdutype PDUType, pdus []SnmpPDU, nonRepeaters uint8, maxRepetitions uint32) *SnmpPacket { return x.mkSnmpPacket(pdutype, pdus, nonRepeaters, maxRepetitions) } func (x *GoSNMP) mkSnmpPacket(pdutype PDUType, pdus []SnmpPDU, nonRepeaters uint8, maxRepetitions uint32) *SnmpPacket { var newSecParams SnmpV3SecurityParameters if x.SecurityParameters != nil { newSecParams = x.SecurityParameters.Copy() } return &SnmpPacket{ Version: x.Version, Community: x.Community, MsgFlags: x.MsgFlags, SecurityModel: x.SecurityModel, SecurityParameters: newSecParams, ContextEngineID: x.ContextEngineID, ContextName: x.ContextName, Error: 0, ErrorIndex: 0, PDUType: pdutype, NonRepeaters: nonRepeaters, MaxRepetitions: (maxRepetitions & 0x7FFFFFFF), Variables: pdus, } } // Get sends an SNMP GET request func (x *GoSNMP) Get(oids []string) (result *SnmpPacket, err error) { oidCount := len(oids) if oidCount > x.MaxOids { return nil, fmt.Errorf("oid count (%d) is greater than MaxOids (%d)", oidCount, x.MaxOids) } // convert oids slice to pdu slice pdus := make([]SnmpPDU, 0, oidCount) for _, oid := range oids { pdus = append(pdus, SnmpPDU{Name: oid, Type: Null, Value: nil}) } // build up SnmpPacket packetOut := x.mkSnmpPacket(GetRequest, pdus, 0, 0) return x.send(packetOut, true) } // Set sends an SNMP SET request func (x *GoSNMP) Set(pdus []SnmpPDU) (result *SnmpPacket, err error) { var packetOut *SnmpPacket switch pdus[0].Type { // TODO test Gauge32 case Integer, OctetString, Gauge32, IPAddress, ObjectIdentifier, Counter32, Counter64, Null, TimeTicks, Uinteger32, OpaqueFloat, OpaqueDouble: packetOut = x.mkSnmpPacket(SetRequest, pdus, 0, 0) default: return nil, fmt.Errorf("ERR:gosnmp currently only supports SNMP SETs for Integer, OctetString, Gauge32, IPAddress, ObjectIdentifier, Counter32, Counter64, Null, TimeTicks, Uinteger32, OpaqueFloat, and OpaqueDouble. Not %s", pdus[0].Type) } return x.send(packetOut, true) } // GetNext sends an SNMP GETNEXT request func (x *GoSNMP) GetNext(oids []string) (result *SnmpPacket, err error) { oidCount := len(oids) if oidCount > x.MaxOids { return nil, fmt.Errorf("oid count (%d) is greater than MaxOids (%d)", oidCount, x.MaxOids) } // convert oids slice to pdu slice pdus := make([]SnmpPDU, 0, oidCount) for _, oid := range oids { pdus = append(pdus, SnmpPDU{Name: oid, Type: Null, Value: nil}) } // Marshal and send the packet packetOut := x.mkSnmpPacket(GetNextRequest, pdus, 0, 0) return x.send(packetOut, true) } // GetBulk sends an SNMP GETBULK request // // For maxRepetitions greater than 255, use BulkWalk() or BulkWalkAll() func (x *GoSNMP) GetBulk(oids []string, nonRepeaters uint8, maxRepetitions uint32) (result *SnmpPacket, err error) { if x.Version == Version1 { return nil, fmt.Errorf("GETBULK not supported in SNMPv1") } oidCount := len(oids) if oidCount > x.MaxOids { return nil, fmt.Errorf("oid count (%d) is greater than MaxOids (%d)", oidCount, x.MaxOids) } // convert oids slice to pdu slice pdus := make([]SnmpPDU, 0, oidCount) for _, oid := range oids { pdus = append(pdus, SnmpPDU{Name: oid, Type: Null, Value: nil}) } // Marshal and send the packet packetOut := x.mkSnmpPacket(GetBulkRequest, pdus, nonRepeaters, maxRepetitions) return x.send(packetOut, true) } // SnmpEncodePacket exposes SNMP packet generation to external callers. // This is useful for generating traffic for use over separate transport // stacks and creating traffic samples for test purposes. func (x *GoSNMP) SnmpEncodePacket(pdutype PDUType, pdus []SnmpPDU, nonRepeaters uint8, maxRepetitions uint32) ([]byte, error) { err := x.validateParameters() if err != nil { return []byte{}, err } pkt := x.mkSnmpPacket(pdutype, pdus, nonRepeaters, maxRepetitions) // Request ID is an atomic counter that wraps to 0 at max int32. reqID := (atomic.AddUint32(&(x.requestID), 1) & 0x7FFFFFFF) pkt.RequestID = reqID if x.Version == Version3 { msgID := (atomic.AddUint32(&(x.msgID), 1) & 0x7FFFFFFF) pkt.MsgID = msgID err = x.initPacket(pkt) if err != nil { return []byte{}, err } } var out []byte out, err = pkt.marshalMsg() if err != nil { return []byte{}, err } return out, nil } // SnmpDecodePacket exposes SNMP packet parsing to external callers. // This is useful for processing traffic from other sources and // building test harnesses. func (x *GoSNMP) SnmpDecodePacket(resp []byte) (*SnmpPacket, error) { var err error result := &SnmpPacket{} err = x.validateParameters() if err != nil { return result, err } result.Logger = x.Logger if x.SecurityParameters != nil { result.SecurityParameters = x.SecurityParameters.Copy() } var cursor int cursor, err = x.unmarshalHeader(resp, result) if err != nil { err = fmt.Errorf("unable to decode packet header: %w", err) return result, err } if result.Version == Version3 { resp, cursor, err = x.decryptPacket(resp, cursor, result) if err != nil { return result, err } } err = x.unmarshalPayload(resp, cursor, result) if err != nil { err = fmt.Errorf("unable to decode packet body: %w", err) return result, err } return result, nil } // SetRequestID sets the base ID value for future requests func (x *GoSNMP) SetRequestID(reqID uint32) { x.requestID = reqID & 0x7fffffff } // SetMsgID sets the base ID value for future messages func (x *GoSNMP) SetMsgID(msgID uint32) { x.msgID = msgID & 0x7fffffff } // // SNMP Walk functions - Analogous to net-snmp's snmpwalk commands // // WalkFunc is the type of the function called for each data unit visited // by the Walk function. If an error is returned processing stops. type WalkFunc func(dataUnit SnmpPDU) error // BulkWalk retrieves a subtree of values using GETBULK. As the tree is // walked walkFn is called for each new value. The function immediately returns // an error if either there is an underlaying SNMP error (e.g. GetBulk fails), // or if walkFn returns an error. func (x *GoSNMP) BulkWalk(rootOid string, walkFn WalkFunc) error { return x.walk(GetBulkRequest, rootOid, walkFn) } // BulkWalkAll is similar to BulkWalk but returns a filled array of all values // rather than using a callback function to stream results. Caution: if you // have set x.AppOpts to 'c', BulkWalkAll may loop indefinitely and cause an // Out Of Memory - use BulkWalk instead. func (x *GoSNMP) BulkWalkAll(rootOid string) (results []SnmpPDU, err error) { return x.walkAll(GetBulkRequest, rootOid) } // Walk retrieves a subtree of values using GETNEXT - a request is made for each // value, unlike BulkWalk which does this operation in batches. As the tree is // walked walkFn is called for each new value. The function immediately returns // an error if either there is an underlaying SNMP error (e.g. GetNext fails), // or if walkFn returns an error. func (x *GoSNMP) Walk(rootOid string, walkFn WalkFunc) error { return x.walk(GetNextRequest, rootOid, walkFn) } // WalkAll is similar to Walk but returns a filled array of all values rather // than using a callback function to stream results. Caution: if you have set // x.AppOpts to 'c', WalkAll may loop indefinitely and cause an Out Of Memory - // use Walk instead. func (x *GoSNMP) WalkAll(rootOid string) (results []SnmpPDU, err error) { return x.walkAll(GetNextRequest, rootOid) } // // Public Functions (helpers) - in alphabetical order // // Partition - returns true when dividing a slice into // partitionSize lengths, including last partition which may be smaller // than partitionSize. This is useful when you have a large array of OIDs // to run Get() on. See the tests for example usage. // // For example for a slice of 8 items to be broken into partitions of // length 3, Partition returns true for the currentPosition having // the following values: // // 0 1 2 3 4 5 6 7 // // T T T func Partition(currentPosition, partitionSize, sliceLength int) bool { if currentPosition < 0 || currentPosition >= sliceLength { return false } if partitionSize == 1 { // redundant, but an obvious optimisation return true } if currentPosition%partitionSize == partitionSize-1 { return true } if currentPosition == sliceLength-1 { return true } return false } // ToBigInt converts SnmpPDU.Value to big.Int, or returns a zero big.Int for // non int-like types (eg strings). // // This is a convenience function to make working with SnmpPDU's easier - it // reduces the need for type assertions. A big.Int is convenient, as SNMP can // return int32, uint32, and uint64. func ToBigInt(value interface{}) *big.Int { var val int64 switch value := value.(type) { // shadow case int: val = int64(value) case int8: val = int64(value) case int16: val = int64(value) case int32: val = int64(value) case int64: val = value case uint: val = int64(value) case uint8: val = int64(value) case uint16: val = int64(value) case uint32: val = int64(value) case uint64: // beware: int64(MaxUint64) overflow, handle different return new(big.Int).SetUint64(value) case string: // for testing and other apps - numbers may appear as strings var err error if val, err = strconv.ParseInt(value, 10, 64); err != nil { val = 0 } default: val = 0 } return big.NewInt(val) } golang-github-gosnmp-gosnmp-1.38.0/gosnmp_api_test.go000066400000000000000000000041651465470716300227010ustar00rootroot00000000000000// 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. // The purpose of these tests is to validate gosnmp's public APIs. // // IMPORTANT: If you're modifying _any_ existing code in this file, you // should be asking yourself about API compatibility! //go:build all || api // +build all api package gosnmp_test // force external view import ( "io" "log" "net" "testing" "time" "github.com/gosnmp/gosnmp" ) func TestAPIConfigTypes(t *testing.T) { g := &gosnmp.GoSNMP{} g.Target = "" g.Port = 0 g.Community = "" g.Version = gosnmp.Version1 g.Version = gosnmp.Version2c g.Timeout = time.Duration(0) g.Retries = 0 g.MaxOids = 0 g.MaxRepetitions = 0 g.NonRepeaters = 0 g.Logger = gosnmp.NewLogger(log.New(io.Discard, "", 0)) var c net.Conn c = g.Conn _ = c } func TestAPIDefault(t *testing.T) { var g *gosnmp.GoSNMP g = gosnmp.Default _ = g } func TestAPIConnectMethodSignature(t *testing.T) { var f func() error f = gosnmp.Default.Connect _ = f } func TestAPIGetMethodSignature(t *testing.T) { var f func([]string) (*gosnmp.SnmpPacket, error) f = gosnmp.Default.Get _ = f } func TestAPISetMethodSignature(t *testing.T) { var f func([]gosnmp.SnmpPDU) (*gosnmp.SnmpPacket, error) f = gosnmp.Default.Set _ = f } func TestAPIGetNextMethodSignature(t *testing.T) { var f func([]string) (*gosnmp.SnmpPacket, error) f = gosnmp.Default.GetNext _ = f } func TestAPIBulkWalkMethodSignature(t *testing.T) { var f func(string, gosnmp.WalkFunc) error f = gosnmp.Default.BulkWalk _ = f } func TestAPIBulkWalkAllMethodSignature(t *testing.T) { var f func(string) ([]gosnmp.SnmpPDU, error) f = gosnmp.Default.BulkWalkAll _ = f } func TestAPIWalkMethodSignature(t *testing.T) { var f func(string, gosnmp.WalkFunc) error f = gosnmp.Default.Walk _ = f } func TestAPIWalkAllMethodSignature(t *testing.T) { var f func(string) ([]gosnmp.SnmpPDU, error) f = gosnmp.Default.WalkAll _ = f } func TestAPIWalkFuncSignature(t *testing.T) { var f gosnmp.WalkFunc f = func(du gosnmp.SnmpPDU) (err error) { return } _ = f } golang-github-gosnmp-gosnmp-1.38.0/helper.go000066400000000000000000000577151465470716300207760ustar00rootroot00000000000000// 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. // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package gosnmp import ( "bytes" "encoding/binary" "errors" "fmt" "io" "log" "math" "net" "os" "strconv" ) // variable struct is used by decodeValue() type variable struct { Value interface{} Type Asn1BER } // helper error modes var ( ErrBase128IntegerTooLarge = errors.New("base 128 integer too large") ErrBase128IntegerTruncated = errors.New("base 128 integer truncated") ErrFloatBufferTooShort = errors.New("float buffer too short") ErrFloatTooLarge = errors.New("float too large") ErrIntegerTooLarge = errors.New("integer too large") ErrInvalidOidLength = errors.New("invalid OID length") ErrInvalidPacketLength = errors.New("invalid packet length") ErrZeroByteBuffer = errors.New("zero byte buffer") ErrZeroLenInteger = errors.New("zero length integer") ) // -- helper functions (mostly) in alphabetical order -------------------------- // Check makes checking errors easy, so they actually get a minimal check func (x *GoSNMP) Check(err error) { if err != nil { x.Logger.Printf("Check: %v\n", err) os.Exit(1) } } // Check makes checking errors easy, so they actually get a minimal check func (packet *SnmpPacket) Check(err error) { if err != nil { packet.Logger.Printf("Check: %v\n", err) os.Exit(1) } } // Check makes checking errors easy, so they actually get a minimal check func Check(err error) { if err != nil { log.Fatalf("Check: %v\n", err) } } func (x *GoSNMP) decodeValue(data []byte, retVal *variable) error { if len(data) == 0 { return ErrZeroByteBuffer } switch Asn1BER(data[0]) { case Integer, Uinteger32: // 0x02. signed x.Logger.Printf("decodeValue: type is %s", Asn1BER(data[0]).String()) length, cursor, err := parseLength(data) if err != nil { return err } // check for truncated packets if length > len(data) { return fmt.Errorf("bytes: % x err: truncated (data %d length %d)", data, len(data), length) } var ret int if ret, err = parseInt(data[cursor:length]); err != nil { x.Logger.Printf("%v:", err) return fmt.Errorf("bytes: % x err: %w", data, err) } retVal.Type = Asn1BER(data[0]) switch Asn1BER(data[0]) { case Uinteger32: retVal.Value = uint32(ret) default: retVal.Value = ret } case OctetString: // 0x04 x.Logger.Print("decodeValue: type is OctetString") length, cursor, err := parseLength(data) if err != nil { return err } // check for truncated packet and throw an error if length > len(data) { return fmt.Errorf("bytes: % x err: truncated (data %d length %d)", data, len(data), length) } retVal.Type = OctetString retVal.Value = data[cursor:length] case Null: // 0x05 x.Logger.Print("decodeValue: type is Null") retVal.Type = Null retVal.Value = nil case ObjectIdentifier: // 0x06 x.Logger.Print("decodeValue: type is ObjectIdentifier") rawOid, _, err := parseRawField(x.Logger, data, "OID") if err != nil { return fmt.Errorf("error parsing OID Value: %w", err) } oid, ok := rawOid.(string) if !ok { return fmt.Errorf("unable to type assert rawOid |%v| to string", rawOid) } retVal.Type = ObjectIdentifier retVal.Value = oid case IPAddress: // 0x40 x.Logger.Print("decodeValue: type is IPAddress") retVal.Type = IPAddress if len(data) < 2 { return fmt.Errorf("not enough data for ipv4 address: %x", data) } switch data[1] { case 0: // real life, buggy devices returning bad data retVal.Value = nil return nil case 4: // IPv4 if len(data) < 6 { return fmt.Errorf("not enough data for ipv4 address: %x", data) } retVal.Value = net.IPv4(data[2], data[3], data[4], data[5]).String() case 16: // IPv6 if len(data) < 18 { return fmt.Errorf("not enough data for ipv6 address: %x", data) } d := make(net.IP, 16) copy(d, data[2:17]) retVal.Value = d.String() default: return fmt.Errorf("got ipaddress len %d, expected 4 or 16", data[1]) } case Counter32: // 0x41. unsigned x.Logger.Print("decodeValue: type is Counter32") length, cursor, err := parseLength(data) if err != nil { return err } if length > len(data) { return fmt.Errorf("not enough data for Counter32 %x (data %d length %d)", data, len(data), length) } ret, err := parseUint(data[cursor:length]) if err != nil { x.Logger.Printf("decodeValue: err is %v", err) break } retVal.Type = Counter32 retVal.Value = ret case Gauge32: // 0x42. unsigned x.Logger.Print("decodeValue: type is Gauge32") length, cursor, err := parseLength(data) if err != nil { return err } if length > len(data) { return fmt.Errorf("not enough data for Gauge32 %x (data %d length %d)", data, len(data), length) } ret, err := parseUint(data[cursor:length]) if err != nil { x.Logger.Printf("decodeValue: err is %v", err) break } retVal.Type = Gauge32 retVal.Value = ret case TimeTicks: // 0x43 x.Logger.Print("decodeValue: type is TimeTicks") length, cursor, err := parseLength(data) if err != nil { return err } if length > len(data) { return fmt.Errorf("not enough data for TimeTicks %x (data %d length %d)", data, len(data), length) } ret, err := parseUint32(data[cursor:length]) if err != nil { x.Logger.Printf("decodeValue: err is %v", err) break } retVal.Type = TimeTicks retVal.Value = ret case Opaque: // 0x44 x.Logger.Print("decodeValue: type is Opaque") length, cursor, err := parseLength(data) if err != nil { return err } if length > len(data) { return fmt.Errorf("not enough data for Opaque %x (data %d length %d)", data, len(data), length) } return parseOpaque(x.Logger, data[cursor:length], retVal) case Counter64: // 0x46 x.Logger.Print("decodeValue: type is Counter64") length, cursor, err := parseLength(data) if err != nil { return err } if length > len(data) { return fmt.Errorf("not enough data for Counter64 %x (data %d length %d)", data, len(data), length) } ret, err := parseUint64(data[cursor:length]) if err != nil { x.Logger.Printf("decodeValue: err is %v", err) break } retVal.Type = Counter64 retVal.Value = ret case NoSuchObject: // 0x80 x.Logger.Print("decodeValue: type is NoSuchObject") retVal.Type = NoSuchObject retVal.Value = nil case NoSuchInstance: // 0x81 x.Logger.Print("decodeValue: type is NoSuchInstance") retVal.Type = NoSuchInstance retVal.Value = nil case EndOfMibView: // 0x82 x.Logger.Print("decodeValue: type is EndOfMibView") retVal.Type = EndOfMibView retVal.Value = nil default: x.Logger.Printf("decodeValue: type %x isn't implemented", data[0]) retVal.Type = UnknownType retVal.Value = nil } x.Logger.Printf("decodeValue: value is %#v", retVal.Value) return nil } func marshalBase128Int(out io.ByteWriter, n int64) (err error) { if n == 0 { err = out.WriteByte(0) return } l := 0 for i := n; i > 0; i >>= 7 { l++ } for i := l - 1; i >= 0; i-- { o := byte(n >> uint(i*7)) o &= 0x7f if i != 0 { o |= 0x80 } err = out.WriteByte(o) if err != nil { return } } return nil } /* snmp Integer32 and INTEGER: -2^31 and 2^31-1 inclusive (-2147483648 to 2147483647 decimal) (FYI https://groups.google.com/forum/#!topic/comp.protocols.snmp/1xaAMzCe_hE) versus: snmp Counter32, Gauge32, TimeTicks, Unsigned32: (below) non-negative integer, maximum value of 2^32-1 (4294967295 decimal) */ // marshalInt32 builds a byte representation of a signed 32 bit int in BigEndian form // ie -2^31 and 2^31-1 inclusive (-2147483648 to 2147483647 decimal) func marshalInt32(value int) ([]byte, error) { if value < math.MinInt32 || value > math.MaxInt32 { return nil, fmt.Errorf("unable to marshal: %d overflows int32", value) } const mask1 uint32 = 0xFFFFFF80 const mask2 uint32 = 0xFFFF8000 const mask3 uint32 = 0xFF800000 const mask4 uint32 = 0x80000000 // ITU-T Rec. X.690 (2002) 8.3.2 // If the contents octets of an integer value encoding consist of more than // one octet, then the bits of the first octet and bit 8 of the second octet: // a) shall not all be ones; and // b) shall not all be zero // These rules ensure that an integer value is always encoded in the smallest // possible number of octets. val := uint32(value) switch { case val&mask1 == 0 || val&mask1 == mask1: return []byte{byte(val)}, nil case val&mask2 == 0 || val&mask2 == mask2: return []byte{byte(val >> 8), byte(val)}, nil case val&mask3 == 0 || val&mask3 == mask3: return []byte{byte(val >> 16), byte(val >> 8), byte(val)}, nil default: return []byte{byte(val >> 24), byte(val >> 16), byte(val >> 8), byte(val)}, nil } } func marshalUint64(v interface{}) []byte { bs := make([]byte, 8) source := v.(uint64) binary.BigEndian.PutUint64(bs, source) // will panic on failure // truncate leading zeros. Cleaner technique? return bytes.TrimLeft(bs, "\x00") } // Counter32, Gauge32, TimeTicks, Unsigned32, SNMPError func marshalUint32(v interface{}) ([]byte, error) { var source uint32 switch val := v.(type) { case uint32: source = val case uint: source = uint32(val) case uint8: source = uint32(val) case SNMPError: source = uint32(val) // We could do others here, but coercing from anything else is dangerous. // Even uint could be 64 bits, though in practice nothing we work with is. default: return nil, fmt.Errorf("unable to marshal %T to uint32", v) } buf := make([]byte, 4) binary.BigEndian.PutUint32(buf, source) var i int for i = 0; i < 3; i++ { if buf[i] != 0 { break } } buf = buf[i:] // if the highest bit in buf is set and x is not negative - prepend a byte to make it positive if len(buf) > 0 && buf[0]&0x80 > 0 { buf = append([]byte{0}, buf...) } return buf, nil } func marshalFloat32(v interface{}) ([]byte, error) { source := v.(float32) out := bytes.NewBuffer(nil) err := binary.Write(out, binary.BigEndian, source) return out.Bytes(), err } func marshalFloat64(v interface{}) ([]byte, error) { source := v.(float64) out := bytes.NewBuffer(nil) err := binary.Write(out, binary.BigEndian, source) return out.Bytes(), err } // marshalLength builds a byte representation of length // // http://luca.ntop.org/Teaching/Appunti/asn1.html // // Length octets. There are two forms: short (for lengths between 0 and 127), // and long definite (for lengths between 0 and 2^1008 -1). // // - Short form. One octet. Bit 8 has value "0" and bits 7-1 give the length. // - Long form. Two to 127 octets. Bit 8 of first octet has value "1" and bits // 7-1 give the number of additional length octets. Second and following // octets give the length, base 256, most significant digit first. func marshalLength(length int) ([]byte, error) { // more convenient to pass length as int than uint64. Therefore check < 0 if length < 0 { return nil, fmt.Errorf("length must be greater than zero") } else if length < 127 { return []byte{byte(length)}, nil } buf := new(bytes.Buffer) err := binary.Write(buf, binary.BigEndian, uint64(length)) if err != nil { return nil, err } bufBytes := buf.Bytes() // strip leading zeros for idx, octect := range bufBytes { if octect != 00 { bufBytes = bufBytes[idx:] break } } header := []byte{byte(128 | len(bufBytes))} return append(header, bufBytes...), nil } func marshalObjectIdentifier(oid string) ([]byte, error) { out := new(bytes.Buffer) oidLength := len(oid) oidBase := 0 var err error i := 0 for j := 0; j < oidLength; { if oid[j] == '.' { j++ continue } var val int64 for j < oidLength && oid[j] != '.' { ch := int64(oid[j] - '0') if ch > 9 { return []byte{}, fmt.Errorf("unable to marshal OID: Invalid object identifier") } val *= 10 val += ch j++ } switch i { case 0: if val > 6 { return []byte{}, fmt.Errorf("unable to marshal OID: Invalid object identifier") } oidBase = int(val * 40) case 1: if val >= 40 { return []byte{}, fmt.Errorf("unable to marshal OID: Invalid object identifier") } oidBase += int(val) err = out.WriteByte(byte(oidBase)) if err != nil { return []byte{}, fmt.Errorf("unable to marshal OID: Invalid object identifier") } default: if val > MaxObjectSubIdentifierValue { return []byte{}, fmt.Errorf("unable to marshal OID: Value out of range") } err = marshalBase128Int(out, val) if err != nil { return []byte{}, fmt.Errorf("unable to marshal OID: Invalid object identifier") } } i++ } if i < 2 || i > 128 { return []byte{}, fmt.Errorf("unable to marshal OID: Invalid object identifier") } return out.Bytes(), nil } // TODO no tests func ipv4toBytes(ip net.IP) []byte { return []byte(ip)[12:] } // parseOpaque parses a Opaque encoded data // Known data-types is OpaqueDouble and OpaqueFloat // Other data decoded as binary Opaque data // TODO: add OpaqueCounter64 (0x76), OpaqueInteger64 (0x80), OpaqueUinteger64 (0x81) func parseOpaque(logger Logger, data []byte, retVal *variable) error { if len(data) == 0 { return ErrZeroByteBuffer } if len(data) > 2 && data[0] == AsnExtensionTag { switch Asn1BER(data[1]) { case OpaqueDouble: // 0x79 data = data[1:] logger.Print("decodeValue: type is OpaqueDouble") length, cursor, err := parseLength(data) if err != nil { return err } if length > len(data) { return fmt.Errorf("not enough data for OpaqueDouble %x (data %d length %d)", data, len(data), length) } retVal.Type = OpaqueDouble retVal.Value, err = parseFloat64(data[cursor:length]) if err != nil { return err } case OpaqueFloat: // 0x78 data = data[1:] logger.Print("decodeValue: type is OpaqueFloat") length, cursor, err := parseLength(data) if err != nil { return err } if length > len(data) { return fmt.Errorf("not enough data for OpaqueFloat %x (data %d length %d)", data, len(data), length) } if cursor > length { return fmt.Errorf("invalid cursor position for OpaqueFloat %x (data %d length %d cursor %d)", data, len(data), length, cursor) } retVal.Type = OpaqueFloat retVal.Value, err = parseFloat32(data[cursor:length]) if err != nil { return err } default: logger.Print("decodeValue: type is Opaque") retVal.Type = Opaque retVal.Value = data[0:] } } else { logger.Print("decodeValue: type is Opaque") retVal.Type = Opaque retVal.Value = data[0:] } return nil } // parseBase128Int parses a base-128 encoded int from the given offset in the // given byte slice. It returns the value and the new offset. func parseBase128Int(bytes []byte, initOffset int) (int64, int, error) { var ret int64 var offset = initOffset for shifted := 0; offset < len(bytes); shifted++ { if shifted > 4 { return 0, 0, ErrBase128IntegerTooLarge } ret <<= 7 b := bytes[offset] ret |= int64(b & 0x7f) offset++ if b&0x80 == 0 { return ret, offset, nil } } return 0, 0, ErrBase128IntegerTruncated } // parseInt64 treats the given bytes as a big-endian, signed integer and // returns the result. func parseInt64(bytes []byte) (int64, error) { switch { case len(bytes) == 0: // X.690 8.3.1: Encoding of an integer value: // The encoding of an integer value shall be primitive. // The contents octets shall consist of one or more octets. return 0, ErrZeroLenInteger case len(bytes) > 8: // We'll overflow an int64 in this case. return 0, ErrIntegerTooLarge } var ret int64 for bytesRead := 0; bytesRead < len(bytes); bytesRead++ { ret <<= 8 ret |= int64(bytes[bytesRead]) } // Shift up and down in order to sign extend the result. ret <<= 64 - uint8(len(bytes))*8 ret >>= 64 - uint8(len(bytes))*8 return ret, nil } // parseInt treats the given bytes as a big-endian, signed integer and returns // the result. func parseInt(bytes []byte) (int, error) { ret64, err := parseInt64(bytes) if err != nil { return 0, err } if ret64 != int64(int(ret64)) { return 0, ErrIntegerTooLarge } return int(ret64), nil } // parseLength parses and calculates an snmp packet length // and returns an error when invalid data is detected // // http://luca.ntop.org/Teaching/Appunti/asn1.html // // Length octets. There are two forms: short (for lengths between 0 and 127), // and long definite (for lengths between 0 and 2^1008 -1). // // - Short form. One octet. Bit 8 has value "0" and bits 7-1 give the length. // - Long form. Two to 127 octets. Bit 8 of first octet has value "1" and bits // 7-1 give the number of additional length octets. Second and following // octets give the length, base 256, most significant digit first. func parseLength(bytes []byte) (int, int, error) { var cursor, length int switch { case len(bytes) <= 2: // handle null octet strings ie "0x04 0x00" cursor = len(bytes) length = len(bytes) case int(bytes[1]) <= 127: length = int(bytes[1]) length += 2 cursor += 2 default: numOctets := int(bytes[1]) & 127 for i := 0; i < numOctets; i++ { length <<= 8 if len(bytes) < 2+i+1 { // Invalid data detected, return an error return 0, 0, ErrInvalidPacketLength } length += int(bytes[2+i]) if length < 0 { // Invalid length due to overflow, return an error return 0, 0, ErrInvalidPacketLength } } length += 2 + numOctets cursor += 2 + numOctets } if length < 0 { // Invalid data detected, return an error return 0, 0, ErrInvalidPacketLength } return length, cursor, nil } // parseObjectIdentifier parses an OBJECT IDENTIFIER from the given bytes and // returns it. An object identifier is a sequence of variable length integers // that are assigned in a hierarchy. func parseObjectIdentifier(src []byte) (string, error) { if len(src) == 0 { return "", ErrInvalidOidLength } out := new(bytes.Buffer) out.WriteByte('.') out.WriteString(strconv.FormatInt(int64(int(src[0])/40), 10)) out.WriteByte('.') out.WriteString(strconv.FormatInt(int64(int(src[0])%40), 10)) var v int64 var err error for offset := 1; offset < len(src); { out.WriteByte('.') v, offset, err = parseBase128Int(src, offset) if err != nil { return "", err } out.WriteString(strconv.FormatInt(v, 10)) } return out.String(), nil } func parseRawField(logger Logger, data []byte, msg string) (interface{}, int, error) { if len(data) == 0 { return nil, 0, fmt.Errorf("empty data passed to parseRawField") } logger.Printf("parseRawField: %s", msg) switch Asn1BER(data[0]) { case Integer: length, cursor, err := parseLength(data) if err != nil { return nil, 0, err } if length > len(data) { return nil, 0, fmt.Errorf("not enough data for Integer (%d vs %d): %x", length, len(data), data) } if cursor > length { return nil, 0, fmt.Errorf("invalid cursor position for Integer %x (data %d length %d cursor %d)", data, len(data), length, cursor) } i, err := parseInt(data[cursor:length]) if err != nil { return nil, 0, fmt.Errorf("unable to parse raw INTEGER: %x err: %w", data, err) } return i, length, nil case OctetString: length, cursor, err := parseLength(data) if err != nil { return nil, 0, err } if length > len(data) { return nil, 0, fmt.Errorf("not enough data for OctetString (%d vs %d): %x", length, len(data), data) } if cursor > length { return nil, 0, fmt.Errorf("invalid cursor position for OctetString %x (data %d length %d cursor %d)", data, len(data), length, cursor) } return string(data[cursor:length]), length, nil case ObjectIdentifier: length, cursor, err := parseLength(data) if err != nil { return nil, 0, err } if length > len(data) { return nil, 0, fmt.Errorf("not enough data for OID (%d vs %d): %x", length, len(data), data) } if cursor > length { return nil, 0, fmt.Errorf("invalid cursor position for OID %x (data %d length %d cursor %d)", data, len(data), length, cursor) } oid, err := parseObjectIdentifier(data[cursor:length]) return oid, length, err case IPAddress: length, _, err := parseLength(data) if err != nil { return nil, 0, err } if len(data) < 2 { return nil, 0, fmt.Errorf("not enough data for ipv4 address: %x", data) } switch data[1] { case 0: // real life, buggy devices returning bad data return nil, length, nil case 4: // IPv4 if len(data) < 6 { return nil, 0, fmt.Errorf("not enough data for ipv4 address: %x", data) } return net.IPv4(data[2], data[3], data[4], data[5]).String(), length, nil default: return nil, 0, fmt.Errorf("got ipaddress len %d, expected 4", data[1]) } case TimeTicks: length, cursor, err := parseLength(data) if err != nil { return nil, 0, err } if length > len(data) { return nil, 0, fmt.Errorf("not enough data for TimeTicks (%d vs %d): %x", length, len(data), data) } if cursor > length { return nil, 0, fmt.Errorf("invalid cursor position for TimeTicks %x (data %d length %d cursor %d)", data, len(data), length, cursor) } ret, err := parseUint(data[cursor:length]) if err != nil { return nil, 0, fmt.Errorf("error in parseUint: %w", err) } return ret, length, nil } return nil, 0, fmt.Errorf("unknown field type: %x", data[0]) } // parseUint64 treats the given bytes as a big-endian, unsigned integer and returns // the result. func parseUint64(bytes []byte) (uint64, error) { var ret uint64 if len(bytes) > 9 || (len(bytes) > 8 && bytes[0] != 0x0) { // We'll overflow a uint64 in this case. return 0, ErrIntegerTooLarge } for bytesRead := 0; bytesRead < len(bytes); bytesRead++ { ret <<= 8 ret |= uint64(bytes[bytesRead]) } return ret, nil } // parseUint32 treats the given bytes as a big-endian, signed integer and returns // the result. func parseUint32(bytes []byte) (uint32, error) { ret, err := parseUint(bytes) if err != nil { return 0, err } return uint32(ret), nil } // parseUint treats the given bytes as a big-endian, signed integer and returns // the result. func parseUint(bytes []byte) (uint, error) { ret64, err := parseUint64(bytes) if err != nil { return 0, err } if ret64 != uint64(uint(ret64)) { return 0, ErrIntegerTooLarge } return uint(ret64), nil } func parseFloat32(bytes []byte) (float32, error) { if len(bytes) > 4 { // We'll overflow a uint64 in this case. return 0, ErrFloatTooLarge } if len(bytes) < 4 { // We'll cause a panic in binary.BigEndian.Uint32() in this case return 0, ErrFloatBufferTooShort } return math.Float32frombits(binary.BigEndian.Uint32(bytes)), nil } func parseFloat64(bytes []byte) (float64, error) { if len(bytes) > 8 { // We'll overflow a uint64 in this case. return 0, ErrFloatTooLarge } if len(bytes) < 8 { // We'll cause a panic in binary.BigEndian.Uint64() in this case return 0, ErrFloatBufferTooShort } return math.Float64frombits(binary.BigEndian.Uint64(bytes)), nil } // -- Bit String --------------------------------------------------------------- // BitStringValue is the structure to use when you want an ASN.1 BIT STRING type. A // bit string is padded up to the nearest byte in memory and the number of // valid bits is recorded. Padding bits will be zero. type BitStringValue struct { Bytes []byte // bits packed into bytes. BitLength int // length in bits. } // At returns the bit at the given index. If the index is out of range it // returns false. func (b BitStringValue) At(i int) int { if i < 0 || i >= b.BitLength { return 0 } x := i / 8 y := 7 - uint(i%8) return int(b.Bytes[x]>>y) & 1 } // RightAlign returns a slice where the padding bits are at the beginning. The // slice may share memory with the BitString. func (b BitStringValue) RightAlign() []byte { shift := uint(8 - (b.BitLength % 8)) if shift == 8 || len(b.Bytes) == 0 { return b.Bytes } a := make([]byte, len(b.Bytes)) a[0] = b.Bytes[0] >> shift for i := 1; i < len(b.Bytes); i++ { a[i] = b.Bytes[i-1] << (8 - shift) a[i] |= b.Bytes[i] >> shift } return a } // -- SnmpVersion -------------------------------------------------------------- func (s SnmpVersion) String() string { switch s { case Version1: return "1" case Version2c: return "2c" case Version3: return "3" default: return "3" } } golang-github-gosnmp-gosnmp-1.38.0/helper_test.go000066400000000000000000000155351465470716300220270ustar00rootroot00000000000000// 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 || helper // +build all helper package gosnmp import ( "encoding/base64" "testing" "github.com/stretchr/testify/assert" ) // https://www.scadacore.com/tools/programming-calculators/online-hex-converter/ is useful func TestParseObjectIdentifier(t *testing.T) { oid := []byte{43, 6, 1, 2, 1, 31, 1, 1, 1, 10, 143, 255, 255, 255, 127} expected := ".1.3.6.1.2.1.31.1.1.1.10.4294967295" buf, err := parseObjectIdentifier(oid) if err != nil { t.Errorf("parseObjectIdentifier(%v) want %s, error: %v", oid, expected, err) } result := string(buf) if string(result) != expected { t.Errorf("parseObjectIdentifier(%v) = %s, want %s", oid, result, expected) } } func TestParseObjectIdentifierWithOtherOid(t *testing.T) { oid := []byte{43, 6, 3, 30, 11, 1, 10} expected := ".1.3.6.3.30.11.1.10" buf, err := parseObjectIdentifier(oid) if err != nil { t.Errorf("parseObjectIdentifier(%v) want %s, error: %v", oid, expected, err) } result := string(buf) if string(result) != expected { t.Errorf("parseObjectIdentifier(%v) = %s, want %s", oid, result, expected) } } func BenchmarkParseObjectIdentifier(b *testing.B) { oid := []byte{43, 6, 3, 30, 11, 1, 10} for i := 0; i < b.N; i++ { parseObjectIdentifier(oid) } } func BenchmarkMarshalObjectIdentifier(b *testing.B) { oid := ".1.3.6.3.30.11.1.10" for i := 0; i < b.N; i++ { marshalObjectIdentifier(oid) } } type testsMarshalUint32T struct { value uint32 goodBytes []byte } var testsMarshalUint32 = []testsMarshalUint32T{ {0, []byte{0x00}}, {2, []byte{0x02}}, // 2 {128, []byte{0x00, 0x80}}, {257, []byte{0x01, 0x01}}, // FF + 2 {65537, []byte{0x01, 0x00, 0x01}}, // FFFF + 2 {16777217, []byte{0x01, 0x00, 0x00, 0x01}}, // FFFFFF + 2 {18542501, []byte{0x01, 0x1a, 0xef, 0xa5}}, {2147483647, []byte{0x7f, 0xff, 0xff, 0xff}}, {2147483648, []byte{0x00, 0x80, 0x00, 0x0, 0x0}}, } func TestMarshalUint32(t *testing.T) { for i, test := range testsMarshalUint32 { result, err := marshalUint32(test.value) if err != nil { t.Errorf("%d: expected %0x got err %v", i, test.goodBytes, err) } if !checkByteEquality2(test.goodBytes, result) { t.Errorf("%d: expected %0x got %0x", i, test.goodBytes, result) } } } var testsMarshalInt32 = []struct { value int goodBytes []byte }{ {0, []byte{0x00}}, {2, []byte{0x02}}, // 2 {128, []byte{0x00, 0x80}}, {257, []byte{0x01, 0x01}}, // FF + 2 {65537, []byte{0x01, 0x00, 0x01}}, // FFFF + 2 {16777217, []byte{0x01, 0x00, 0x00, 0x01}}, // FFFFFF + 2 {2147483647, []byte{0x7f, 0xff, 0xff, 0xff}}, {-2147483648, []byte{0x80, 0x00, 0x00, 0x00}}, {-16777217, []byte{0xfe, 0xff, 0xff, 0xff}}, {-16777216, []byte{0xff, 0x00, 0x00, 0x00}}, {-65537, []byte{0xfe, 0xff, 0xff}}, {-65536, []byte{0xff, 0x00, 0x00}}, {-257, []byte{0xfe, 0xff}}, {-256, []byte{0xff, 0x00}}, {-2, []byte{0xfe}}, {-1, []byte{0xff}}, } func TestMarshalInt32(t *testing.T) { for _, aTest := range testsMarshalInt32 { result, err := marshalInt32(aTest.value) assert.NoErrorf(t, err, "value %d", aTest.value) assert.EqualValues(t, aTest.goodBytes, result, "bad marshalInt32()") } } func TestParseUint64(t *testing.T) { tests := []struct { data []byte n uint64 }{ {[]byte{}, 0}, {[]byte{0x00}, 0}, {[]byte{0x01}, 1}, {[]byte{0x01, 0x01}, 257}, {[]byte{0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1e, 0xb3, 0xbf}, 18446744073694786495}, } for _, test := range tests { if ret, err := parseUint64(test.data); err != nil || ret != test.n { t.Errorf("parseUint64(%v) = %d, %v want %d, ", test.data, ret, err, test.n) } } } var testsInvalidSNMPResponses = []string{ "MIIHIQIBAQQHcHJpdmF0ZaKCBxECBGwvRyoCAQACAQAwggcBMBgGCCsGAQIBAQIABgwrBgEEAZJRAwE/AQYwEAYIKwYBAgEBAwBDBBU2aN0wDAYIKwYBAgEBBAAEADAMBggrBgECAQEFAAQAMAwGCCsGAQIBAQYABAAwDQYIKwYBAgEBBwACAUgwDQYIKwYBAgECAQACAQAwDwYKKwYBAgECAgEBAQIBATAPBgorBgECAQICAQcBAgEBMA8GCisGAQIBAgIBBwECAQEwDwYKKwYBAgECAgEHAQIBATAPBgorBgECAQICAQcBAgEBMA8GCisGAQIBAgIBBwECAQEwDwYKKwYBAgECAgEHAQIBATAPBgorBgECAQICAQcBAgEBMBAGCCsGAQIBAQMAQwQVNmjdMAwGCCsGAQIBAQQABAAwDAYIKwYBAgEBBQAEADAMBggrBgECAQEGAAQAMA0GCCsGAQIBAQcAAgFIMA0GCCsGAQIBAgEAAgEAMA8GCisGAQIBAgIBAQECAQEwFgYKKwYBAgECAgECAQQIRXRoZXJuZXQwDwYKKwYBAgECAgEIAQIBATAPBgorBgECAQICAQgBAgEBMA8GCisGAQIBAgIBCAECAQEwDwYKKwYBAgECAgEIAQIBATAPBgorBgECAQICAQgBAgEBMA8GCisGAQIBAgIBCAECAQEwDwYKKwYBAgECAgEIAQIBATAMBggrBgECAQEEAAQAMAwGCCsGAQIBAQUABAAwDAYIKwYBAgEBBgAEADANBggrBgECAQEHAAIBSDANBggrBgECAQIBAAIBADAPBgorBgECAQICAQEBAgEBMBYGCisGAQIBAgIBAgEECEV0aGVybmV0MA8GCisGAQIBAgIBAwECAQYwDwYKKwYBAgECAgEJAUMBADAPBgorBgECAQICAQkBQwEAMA8GCisGAQIBAgIBCQFDAQAwDwYKKwYBAgECAgEJAUMBADAPBgorBgECAQICAQkBQwEAMA8GCisGAQIBAgIBCQFDAQAwDwYKKwYBAgECAgEJAUMBADAMBggrBgECAQEFAAQAMAwGCCsGAQIBAQYABAAwDQYIKwYBAgEBBwACAUgwDQYIKwYBAgECAQACAQAwDwYKKwYBAgECAgEBAQIBATAWBgorBgECAQICAQIBBAhFdGhlcm5ldDAPBgorBgECAQICAQMBAgEGMBAGCisGAQIBAgIBBAECAgXqMBIGCisGAQIBAgIBCgFBBQCUMR+2MBIGCisGAQIBAgIBCgFBBQCUMR+2MBIGCisGAQIBAgIBCgFBBQCUMR+2MBIGCisGAQIBAgIBCgFBBQCUMR+2MBIGCisGAQIBAgIBCgFBBQCUMR+2MBIGCisGAQIBAgIBCgFBBQCUMR+2MBIGCisGAQIBAgIBCgFBBQCUMR+2MAwGCCsGAQIBAQYABAAwDQYIKwYBAgEBBwACAUgwDQYIKwYBAgECAQACAQAwDwYKKwYBAgECAgEBAQIBATAWBgorBgECAQICAQIBBAhFdGhlcm5ldDAPBgorBgECAQICAQMBAgEGMBAGCisGAQIBAgIBBAECAgXqMBIGCisGAQIBAgIBBQFCBDuaygAwEQYKKwYBAgECAgELAUEDDQDJMBEGCisGAQIBAgIBCwFBAw0AyTARBgorBgECAQICAQsBQQMNAMkwEQYKKwYBAgECAgELAUEDDQDJMBEGCisGAQIBAgIBCwFBAw0AyTARBgorBgECAQICAQsBQQMNAMkwEQYKKwYBAgECAgELAUEDDQDJMA0GCCsGAQIBAQcAAgFIMA0GCCsGAQIBAgEAAgEAMA8GCisGAQIBAgIBAQECAQEwFgYKKwYBAgECAgECAQQIRXRoZXJuZXQwDwYKKwYBAgECAgEDAQIBBjAQBgorBgECAQICAQQBAgIF6jASBgorBgECAQICAQUBQgQ7msoAMBQGCisGAQIBAgIBBgEEBryxgWZeBTASBgorBgECAQICAQwBQQQAu5coMBIGCisGAQIBAgIBDAFBBAC7lygwEgYKKwYBAgECAgEMAUEEALuXKDASBgorBgECAQICAQwBQQQAu5coMBIGCisGAQIBAgIBDAFBBAC7lygwEgYKKwYBAgECAgEMAUEEALuXKDASBgorBgECAQICAQwBQQQAu5coMA0GCCsGAQIBAgEAAgEAMA8GCisGAQIBAgIBAQECAQEwFgYKKwYBAgECAgECAQQIRXRoZXJuZXQwDwYKKwYBAgECAgEDAQIBBjAQBgorBgECAQICAQQBAgIF6jASBgorBgECAQICAQUBQgQ7msoAMBQGCisGAQIBAgIBBgEEBryxgWZeBTAPBgorBgECAQICAQcBAgEBMBEGCisGAQIBAgIBDQFBAwdNFzARBgorBgECAQICAQ0BQQMHTRcwEQYKKwYBAgECAgE=", "MBoCAQEEB3ByaXZhdGWiDAIESESkywIBBQIBAA==", "MEo=", } func TestInvalidSNMPResponses(t *testing.T) { g := &GoSNMP{ Target: "127.0.0.1", Port: 161, Community: "public", Version: Version2c, } for i, test := range testsInvalidSNMPResponses { testBytes, _ := base64.StdEncoding.DecodeString(test) result, err := g.SnmpDecodePacket(testBytes) if err == nil { t.Errorf("#%d, failed to error %v", i, result) } } } func checkByteEquality2(a, b []byte) bool { if a == nil && b == nil { return true } if a == nil || b == nil { return false } if len(a) != len(b) { return false } for i := range a { if a[i] != b[i] { return false } } return true } golang-github-gosnmp-gosnmp-1.38.0/interface.go000066400000000000000000000213461465470716300214460ustar00rootroot00000000000000// 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. // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package gosnmp import ( "time" ) //go:generate mockgen --destination gosnmp_mock.go --package=gosnmp --source interface.go // Handler is a GoSNMP interface // // Handler is provided to assist with testing using mocks type Handler interface { // Connect creates and opens a socket. Because UDP is a connectionless // protocol, you won't know if the remote host is responding until you send // packets. And if the host is regularly disappearing and reappearing, you won't // know if you've only done a Connect(). // // For historical reasons (ie this is part of the public API), the method won't // be renamed. Connect() error // ConnectIPv4 connects using IPv4 ConnectIPv4() error // ConnectIPv6 connects using IPv6 ConnectIPv6() error // Get sends an SNMP GET request Get(oids []string) (result *SnmpPacket, err error) // GetBulk sends an SNMP GETBULK request GetBulk(oids []string, nonRepeaters uint8, maxRepetitions uint32) (result *SnmpPacket, err error) // GetNext sends an SNMP GETNEXT request GetNext(oids []string) (result *SnmpPacket, err error) // Walk retrieves a subtree of values using GETNEXT - a request is made for each // value, unlike BulkWalk which does this operation in batches. As the tree is // walked walkFn is called for each new value. The function immediately returns // an error if either there is an underlaying SNMP error (e.g. GetNext fails), // or if walkFn returns an error. Walk(rootOid string, walkFn WalkFunc) error // WalkAll is similar to Walk but returns a filled array of all values rather // than using a callback function to stream results. WalkAll(rootOid string) (results []SnmpPDU, err error) // BulkWalk retrieves a subtree of values using GETBULK. As the tree is // walked walkFn is called for each new value. The function immediately returns // an error if either there is an underlaying SNMP error (e.g. GetBulk fails), // or if walkFn returns an error. BulkWalk(rootOid string, walkFn WalkFunc) error // BulkWalkAll is similar to BulkWalk but returns a filled array of all values // rather than using a callback function to stream results. BulkWalkAll(rootOid string) (results []SnmpPDU, err error) // SendTrap sends a SNMP Trap (v2c/v3 only) // // pdus[0] can a pdu of Type TimeTicks (with the desired uint32 epoch // time). Otherwise a TimeTicks pdu will be prepended, with time set to // now. This mirrors the behaviour of the Net-SNMP command-line tools. // // SendTrap doesn't wait for a return packet from the NMS (Network // Management Station). // // See also Listen() and examples for creating an NMS. SendTrap(trap SnmpTrap) (result *SnmpPacket, err error) // UnmarshalTrap unpacks the SNMP Trap. UnmarshalTrap(trap []byte, useResponseSecurityParameters bool) (result *SnmpPacket, err error) // Set sends an SNMP SET request Set(pdus []SnmpPDU) (result *SnmpPacket, err error) // Check makes checking errors easy, so they actually get a minimal check Check(err error) // Close closes the connection Close() error // Target gets the Target Target() string // SetTarget sets the Target SetTarget(target string) // Port gets the Port Port() uint16 // SetPort sets the Port SetPort(port uint16) // Community gets the Community Community() string // SetCommunity sets the Community SetCommunity(community string) // Version gets the Version Version() SnmpVersion // SetVersion sets the Version SetVersion(version SnmpVersion) // Timeout gets the Timeout Timeout() time.Duration // SetTimeout sets the Timeout SetTimeout(timeout time.Duration) // Retries gets the Retries Retries() int // SetRetries sets the Retries SetRetries(retries int) // GetExponentialTimeout gets the ExponentialTimeout GetExponentialTimeout() bool // SetExponentialTimeout sets the ExponentialTimeout SetExponentialTimeout(value bool) // Logger gets the Logger Logger() Logger // SetLogger sets the Logger SetLogger(logger Logger) // MaxOids gets the MaxOids MaxOids() int // SetMaxOids sets the MaxOids SetMaxOids(maxOids int) // MaxRepetitions gets the maxRepetitions MaxRepetitions() uint32 // SetMaxRepetitions sets the maxRepetitions SetMaxRepetitions(maxRepetitions uint32) // NonRepeaters gets the nonRepeaters NonRepeaters() int // SetNonRepeaters sets the nonRepeaters SetNonRepeaters(nonRepeaters int) // MsgFlags gets the MsgFlags MsgFlags() SnmpV3MsgFlags // SetMsgFlags sets the MsgFlags SetMsgFlags(msgFlags SnmpV3MsgFlags) // SecurityModel gets the SecurityModel SecurityModel() SnmpV3SecurityModel // SetSecurityModel sets the SecurityModel SetSecurityModel(securityModel SnmpV3SecurityModel) // SecurityParameters gets the SecurityParameters SecurityParameters() SnmpV3SecurityParameters // SetSecurityParameters sets the SecurityParameters SetSecurityParameters(securityParameters SnmpV3SecurityParameters) // ContextEngineID gets the ContextEngineID ContextEngineID() string // SetContextEngineID sets the ContextEngineID SetContextEngineID(contextEngineID string) // ContextName gets the ContextName ContextName() string // SetContextName sets the ContextName SetContextName(contextName string) } // snmpHandler is a wrapper around gosnmp type snmpHandler struct { GoSNMP } // NewHandler creates a new Handler using gosnmp func NewHandler() Handler { return &snmpHandler{ GoSNMP{ Port: Default.Port, Community: Default.Community, Version: Default.Version, Timeout: Default.Timeout, Retries: Default.Retries, MaxOids: Default.MaxOids, }, } } func (x *snmpHandler) Target() string { // not x.Target because it would reference function Target return x.GoSNMP.Target } func (x *snmpHandler) SetTarget(target string) { x.GoSNMP.Target = target } func (x *snmpHandler) Port() uint16 { return x.GoSNMP.Port } func (x *snmpHandler) SetPort(port uint16) { x.GoSNMP.Port = port } func (x *snmpHandler) Community() string { return x.GoSNMP.Community } func (x *snmpHandler) SetCommunity(community string) { x.GoSNMP.Community = community } func (x *snmpHandler) Version() SnmpVersion { return x.GoSNMP.Version } func (x *snmpHandler) SetVersion(version SnmpVersion) { x.GoSNMP.Version = version } func (x *snmpHandler) Timeout() time.Duration { return x.GoSNMP.Timeout } func (x *snmpHandler) SetTimeout(timeout time.Duration) { x.GoSNMP.Timeout = timeout } func (x *snmpHandler) Retries() int { return x.GoSNMP.Retries } func (x *snmpHandler) SetRetries(retries int) { x.GoSNMP.Retries = retries } func (x *snmpHandler) GetExponentialTimeout() bool { return x.GoSNMP.ExponentialTimeout } func (x *snmpHandler) SetExponentialTimeout(value bool) { x.GoSNMP.ExponentialTimeout = value } func (x *snmpHandler) Logger() Logger { return x.GoSNMP.Logger } func (x *snmpHandler) SetLogger(logger Logger) { x.GoSNMP.Logger = logger } func (x *snmpHandler) MaxOids() int { return x.GoSNMP.MaxOids } func (x *snmpHandler) SetMaxOids(maxOids int) { x.GoSNMP.MaxOids = maxOids } func (x *snmpHandler) MaxRepetitions() uint32 { return (x.GoSNMP.MaxRepetitions & 0x7FFFFFFF) } // SetMaxRepetitions wraps to 0 at max int32. func (x *snmpHandler) SetMaxRepetitions(maxRepetitions uint32) { x.GoSNMP.MaxRepetitions = (maxRepetitions & 0x7FFFFFFF) } func (x *snmpHandler) NonRepeaters() int { return x.GoSNMP.NonRepeaters } func (x *snmpHandler) SetNonRepeaters(nonRepeaters int) { x.GoSNMP.NonRepeaters = nonRepeaters } func (x *snmpHandler) MsgFlags() SnmpV3MsgFlags { return x.GoSNMP.MsgFlags } func (x *snmpHandler) SetMsgFlags(msgFlags SnmpV3MsgFlags) { x.GoSNMP.MsgFlags = msgFlags } func (x *snmpHandler) SecurityModel() SnmpV3SecurityModel { return x.GoSNMP.SecurityModel } func (x *snmpHandler) SetSecurityModel(securityModel SnmpV3SecurityModel) { x.GoSNMP.SecurityModel = securityModel } func (x *snmpHandler) SecurityParameters() SnmpV3SecurityParameters { return x.GoSNMP.SecurityParameters } func (x *snmpHandler) SetSecurityParameters(securityParameters SnmpV3SecurityParameters) { x.GoSNMP.SecurityParameters = securityParameters } func (x *snmpHandler) ContextEngineID() string { return x.GoSNMP.ContextEngineID } func (x *snmpHandler) SetContextEngineID(contextEngineID string) { x.GoSNMP.ContextEngineID = contextEngineID } func (x *snmpHandler) ContextName() string { return x.GoSNMP.ContextName } func (x *snmpHandler) SetContextName(contextName string) { x.GoSNMP.ContextName = contextName } func (x *snmpHandler) Close() error { // not x.Conn for consistency return x.GoSNMP.Conn.Close() } golang-github-gosnmp-gosnmp-1.38.0/local_tests.sh000077500000000000000000000001771465470716300220310ustar00rootroot00000000000000#!/bin/bash go test -v -tags helper go test -v -tags marshal go test -v -tags misc go test -v -tags api go test -v -tags trap golang-github-gosnmp-gosnmp-1.38.0/logger.go000066400000000000000000000007341465470716300207630ustar00rootroot00000000000000// Copyright 2021 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 gosnmp_nodebug // +build gosnmp_nodebug // When building, specify the gosnmp_nodebug tag and logging will be completely disabled // for example: go build -tags gosnmp_nodebug package gosnmp func (l *Logger) Print(v ...interface{}) { } func (l *Logger) Printf(format string, v ...interface{}) { } golang-github-gosnmp-gosnmp-1.38.0/logger_debug.go000066400000000000000000000006771465470716300221370ustar00rootroot00000000000000// Copyright 2021 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 !gosnmp_nodebug // +build !gosnmp_nodebug package gosnmp func (l *Logger) Print(v ...interface{}) { if l.logger != nil { l.logger.Print(v...) } } func (l *Logger) Printf(format string, v ...interface{}) { if l.logger != nil { l.logger.Printf(format, v...) } } golang-github-gosnmp-gosnmp-1.38.0/marshal.go000066400000000000000000001173241465470716300211370ustar00rootroot00000000000000// 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. package gosnmp import ( "bytes" "context" "encoding/asn1" "encoding/binary" "errors" "fmt" "io" "net" "runtime" "strings" "sync/atomic" "time" ) // // Remaining globals and definitions located here. // See http://www.rane.com/note161.html for a succint description of the SNMP // protocol. // // SnmpVersion 1, 2c and 3 implemented type SnmpVersion uint8 // SnmpVersion 1, 2c and 3 implemented const ( Version1 SnmpVersion = 0x0 Version2c SnmpVersion = 0x1 Version3 SnmpVersion = 0x3 ) // SnmpPacket struct represents the entire SNMP Message or Sequence at the // application layer. type SnmpPacket struct { Version SnmpVersion MsgFlags SnmpV3MsgFlags SecurityModel SnmpV3SecurityModel SecurityParameters SnmpV3SecurityParameters // interface ContextEngineID string ContextName string Community string PDUType PDUType MsgID uint32 RequestID uint32 MsgMaxSize uint32 Error SNMPError ErrorIndex uint8 NonRepeaters uint8 MaxRepetitions uint32 Variables []SnmpPDU Logger Logger // v1 traps have a very different format from v2c and v3 traps. // // These fields are set via the SnmpTrap parameter to SendTrap(). SnmpTrap } // SnmpTrap is used to define a SNMP trap, and is passed into SendTrap type SnmpTrap struct { Variables []SnmpPDU // If true, the trap is an InformRequest, not a trap. This has no effect on // v1 traps, as Inform is not part of the v1 protocol. IsInform bool // These fields are required for SNMPV1 Trap Headers Enterprise string AgentAddress string GenericTrap int SpecificTrap int Timestamp uint } // VarBind struct represents an SNMP Varbind. type VarBind struct { Name asn1.ObjectIdentifier Value asn1.RawValue } // PDUType describes which SNMP Protocol Data Unit is being sent. type PDUType byte // The currently supported PDUType's const ( Sequence PDUType = 0x30 GetRequest PDUType = 0xa0 GetNextRequest PDUType = 0xa1 GetResponse PDUType = 0xa2 SetRequest PDUType = 0xa3 Trap PDUType = 0xa4 // v1 GetBulkRequest PDUType = 0xa5 InformRequest PDUType = 0xa6 SNMPv2Trap PDUType = 0xa7 // v2c, v3 Report PDUType = 0xa8 // v3 ) //go:generate stringer -type=PDUType // SNMPv3: User-based Security Model Report PDUs and // error types as per https://tools.ietf.org/html/rfc3414 const ( usmStatsUnsupportedSecLevels = ".1.3.6.1.6.3.15.1.1.1.0" usmStatsNotInTimeWindows = ".1.3.6.1.6.3.15.1.1.2.0" usmStatsUnknownUserNames = ".1.3.6.1.6.3.15.1.1.3.0" usmStatsUnknownEngineIDs = ".1.3.6.1.6.3.15.1.1.4.0" usmStatsWrongDigests = ".1.3.6.1.6.3.15.1.1.5.0" usmStatsDecryptionErrors = ".1.3.6.1.6.3.15.1.1.6.0" snmpUnknownSecurityModels = ".1.3.6.1.6.3.11.2.1.1.0" snmpInvalidMsgs = ".1.3.6.1.6.3.11.2.1.2.0" snmpUnknownPDUHandlers = ".1.3.6.1.6.3.11.2.1.3.0" ) var ( ErrDecryption = errors.New("decryption error") ErrInvalidMsgs = errors.New("invalid messages") ErrNotInTimeWindow = errors.New("not in time window") ErrUnknownEngineID = errors.New("unknown engine id") ErrUnknownPDUHandlers = errors.New("unknown pdu handlers") ErrUnknownReportPDU = errors.New("unknown report pdu") ErrUnknownSecurityLevel = errors.New("unknown security level") ErrUnknownSecurityModels = errors.New("unknown security models") ErrUnknownUsername = errors.New("unknown username") ErrWrongDigest = errors.New("wrong digest") ) const rxBufSize = 65535 // max size of IPv4 & IPv6 packet // Logger is an interface used for debugging. Both Print and // Printf have the same interfaces as Package Log in the std library. The // Logger interface is small to give you flexibility in how you do // your debugging. // // Logger // For verbose logging to stdout: // gosnmp_logger = NewLogger(log.New(os.Stdout, "", 0)) type LoggerInterface interface { Print(v ...interface{}) Printf(format string, v ...interface{}) } type Logger struct { logger LoggerInterface } func NewLogger(logger LoggerInterface) Logger { return Logger{ logger: logger, } } func (packet *SnmpPacket) SafeString() string { sp := "" if packet.SecurityParameters != nil { sp = packet.SecurityParameters.SafeString() } return fmt.Sprintf("Version:%s, MsgFlags:%s, SecurityModel:%s, SecurityParameters:%s, ContextEngineID:%s, ContextName:%s, Community:%s, PDUType:%s, MsgID:%d, RequestID:%d, MsgMaxSize:%d, Error:%s, ErrorIndex:%d, NonRepeaters:%d, MaxRepetitions:%d, Variables:%v", packet.Version, packet.MsgFlags, packet.SecurityModel, sp, packet.ContextEngineID, packet.ContextName, packet.Community, packet.PDUType, packet.MsgID, packet.RequestID, packet.MsgMaxSize, packet.Error, packet.ErrorIndex, packet.NonRepeaters, packet.MaxRepetitions, packet.Variables, ) } // GoSNMP // send/receive one snmp request func (x *GoSNMP) sendOneRequest(packetOut *SnmpPacket, wait bool) (result *SnmpPacket, err error) { allReqIDs := make([]uint32, 0, x.Retries+1) // allMsgIDs := make([]uint32, 0, x.Retries+1) // unused timeout := x.Timeout withContextDeadline := false for retries := 0; ; retries++ { if retries > 0 { if x.OnRetry != nil { x.OnRetry(x) } x.Logger.Printf("Retry number %d. Last error was: %v", retries, err) if withContextDeadline && strings.Contains(err.Error(), "timeout") { err = context.DeadlineExceeded break } if retries > x.Retries { if strings.Contains(err.Error(), "timeout") { err = fmt.Errorf("request timeout (after %d retries)", retries-1) } break } if x.ExponentialTimeout { // https://www.webnms.com/snmp/help/snmpapi/snmpv3/v1/timeout.html timeout *= 2 } withContextDeadline = false } err = nil if x.Context.Err() != nil { return nil, x.Context.Err() } reqDeadline := time.Now().Add(timeout) if contextDeadline, ok := x.Context.Deadline(); ok { if contextDeadline.Before(reqDeadline) { reqDeadline = contextDeadline withContextDeadline = true } } err = x.Conn.SetDeadline(reqDeadline) if err != nil { return nil, err } // Request ID is an atomic counter that wraps to 0 at max int32. reqID := (atomic.AddUint32(&(x.requestID), 1) & 0x7FFFFFFF) allReqIDs = append(allReqIDs, reqID) packetOut.RequestID = reqID if x.Version == Version3 { msgID := (atomic.AddUint32(&(x.msgID), 1) & 0x7FFFFFFF) // allMsgIDs = append(allMsgIDs, msgID) // unused packetOut.MsgID = msgID err = x.initPacket(packetOut) if err != nil { break } } if x.Version == Version3 { packetOut.SecurityParameters.Log() } var outBuf []byte outBuf, err = packetOut.marshalMsg() if err != nil { // Don't retry - not going to get any better! err = fmt.Errorf("marshal: %w", err) break } if x.PreSend != nil { x.PreSend(x) } x.Logger.Printf("SENDING PACKET: %s", packetOut.SafeString()) // If using UDP and unconnected socket, send packet directly to stored address. if uconn, ok := x.Conn.(net.PacketConn); ok && x.uaddr != nil { _, err = uconn.WriteTo(outBuf, x.uaddr) } else { _, err = x.Conn.Write(outBuf) } if err != nil { continue } if x.OnSent != nil { x.OnSent(x) } // all sends wait for the return packet, except for SNMPv2Trap if !wait { return &SnmpPacket{}, nil } waitingResponse: for { x.Logger.Print("WAITING RESPONSE...") // Receive response and try receiving again on any decoding error. // Let the deadline abort us if we don't receive a valid response. var resp []byte resp, err = x.receive() if err == io.EOF && strings.HasPrefix(x.Transport, tcp) { // EOF on TCP: reconnect and retry. Do not count // as retry as socket was broken x.Logger.Printf("ERROR: EOF. Performing reconnect") err = x.netConnect() if err != nil { return nil, err } retries-- break } else if err != nil { // receive error. retrying won't help. abort break } if x.OnRecv != nil { x.OnRecv(x) } x.Logger.Printf("GET RESPONSE OK: %+v", resp) result = new(SnmpPacket) result.Logger = x.Logger result.MsgFlags = packetOut.MsgFlags if packetOut.SecurityParameters != nil { result.SecurityParameters = packetOut.SecurityParameters.Copy() } var cursor int cursor, err = x.unmarshalHeader(resp, result) if err != nil { x.Logger.Printf("ERROR on unmarshall header: %s", err) break } if x.Version == Version3 { useResponseSecurityParameters := false if usp, ok := x.SecurityParameters.(*UsmSecurityParameters); ok { if usp.AuthoritativeEngineID == "" { useResponseSecurityParameters = true } } err = x.testAuthentication(resp, result, useResponseSecurityParameters) if err != nil { x.Logger.Printf("ERROR on Test Authentication on v3: %s", err) break } resp, cursor, err = x.decryptPacket(resp, cursor, result) if err != nil { x.Logger.Printf("ERROR on decryptPacket on v3: %s", err) break } } err = x.unmarshalPayload(resp, cursor, result) if err != nil { x.Logger.Printf("ERROR on UnmarshalPayload on v3: %s", err) break } if result.Error == NoError && len(result.Variables) < 1 { x.Logger.Printf("ERROR on UnmarshalPayload on v3: Empty result") break } // While Report PDU was defined by RFC 1905 as part of SNMPv2, it was never // used until SNMPv3. Report PDU's allow a SNMP engine to tell another SNMP // engine that an error was detected while processing an SNMP message. // // The format for a Report PDU is // ----------------------------------- // | 0xA8 | reqid | 0 | 0 | varbinds | // ----------------------------------- // where: // - PDU type 0xA8 indicates a Report PDU. // - reqid is either: // The request identifier of the message that triggered the report // or zero if the request identifier cannot be extracted. // - The variable bindings will contain a single object identifier and its value // // usmStatsNotInTimeWindows and usmStatsUnknownEngineIDs are recoverable errors // and will be retransmitted, for others we return the result with an error. if result.Version == Version3 && result.PDUType == Report && len(result.Variables) == 1 { switch result.Variables[0].Name { case usmStatsUnsupportedSecLevels: return result, ErrUnknownSecurityLevel case usmStatsNotInTimeWindows: break waitingResponse case usmStatsUnknownUserNames: return result, ErrUnknownUsername case usmStatsUnknownEngineIDs: break waitingResponse case usmStatsWrongDigests: return result, ErrWrongDigest case usmStatsDecryptionErrors: return result, ErrDecryption case snmpUnknownSecurityModels: return result, ErrUnknownSecurityModels case snmpInvalidMsgs: return result, ErrInvalidMsgs case snmpUnknownPDUHandlers: return result, ErrUnknownPDUHandlers default: return result, ErrUnknownReportPDU } } validID := false for _, id := range allReqIDs { if id == result.RequestID { validID = true } } if result.RequestID == 0 { validID = true } if !validID { x.Logger.Print("ERROR out of order") continue } break } if err != nil { continue } if x.OnFinish != nil { x.OnFinish(x) } // Success! return result, nil } // Return last error return nil, err } // generic "sender" that negotiate any version of snmp request // // all sends wait for the return packet, except for SNMPv2Trap func (x *GoSNMP) send(packetOut *SnmpPacket, wait bool) (result *SnmpPacket, err error) { defer func() { if e := recover(); e != nil { var buf = make([]byte, 8192) runtime.Stack(buf, true) err = fmt.Errorf("recover: %v Stack:%v", e, string(buf)) } }() if x.Conn == nil { return nil, fmt.Errorf("&GoSNMP.Conn is missing. Provide a connection or use Connect()") } if x.Retries < 0 { x.Retries = 0 } x.Logger.Print("SEND INIT") if packetOut.Version == Version3 { x.Logger.Print("SEND INIT NEGOTIATE SECURITY PARAMS") if err = x.negotiateInitialSecurityParameters(packetOut); err != nil { return &SnmpPacket{}, err } x.Logger.Print("SEND END NEGOTIATE SECURITY PARAMS") } // perform request result, err = x.sendOneRequest(packetOut, wait) if err != nil { x.Logger.Printf("SEND Error on the first Request Error: %s", err) return result, err } if result.Version == Version3 { x.Logger.Printf("SEND STORE SECURITY PARAMS from result: %s", result.SecurityParameters.SafeString()) err = x.storeSecurityParameters(result) if result.PDUType == Report && len(result.Variables) == 1 { switch result.Variables[0].Name { case usmStatsNotInTimeWindows: x.Logger.Print("WARNING detected out-of-time-window ERROR") if err = x.updatePktSecurityParameters(packetOut); err != nil { x.Logger.Printf("ERROR updatePktSecurityParameters error: %s", err) return nil, err } // retransmit with updated auth engine params result, err = x.sendOneRequest(packetOut, wait) if err != nil { x.Logger.Printf("ERROR out-of-time-window retransmit error: %s", err) return result, ErrNotInTimeWindow } case usmStatsUnknownEngineIDs: x.Logger.Print("WARNING detected unknown engine id ERROR") if err = x.updatePktSecurityParameters(packetOut); err != nil { x.Logger.Printf("ERROR updatePktSecurityParameters error: %s", err) return nil, err } // retransmit with updated engine id result, err = x.sendOneRequest(packetOut, wait) if err != nil { x.Logger.Printf("ERROR unknown engine id retransmit error: %s", err) return result, ErrUnknownEngineID } } } } return result, err } // -- Marshalling Logic -------------------------------------------------------- // MarshalMsg marshalls a snmp packet, ready for sending across the wire func (packet *SnmpPacket) MarshalMsg() ([]byte, error) { return packet.marshalMsg() } // marshal an SNMP message func (packet *SnmpPacket) marshalMsg() ([]byte, error) { var err error buf := new(bytes.Buffer) // version buf.Write([]byte{2, 1, byte(packet.Version)}) if packet.Version == Version3 { buf, err = packet.marshalV3(buf) if err != nil { return nil, err } } else { // community buf.Write([]byte{4, uint8(len(packet.Community))}) buf.WriteString(packet.Community) // pdu pdu, err2 := packet.marshalPDU() if err2 != nil { return nil, err2 } buf.Write(pdu) } // build up resulting msg - sequence, length then the tail (buf) msg := new(bytes.Buffer) msg.WriteByte(byte(Sequence)) bufLengthBytes, err2 := marshalLength(buf.Len()) if err2 != nil { return nil, err2 } msg.Write(bufLengthBytes) _, err = buf.WriteTo(msg) if err != nil { return nil, err } authenticatedMessage, err := packet.authenticate(msg.Bytes()) if err != nil { return nil, err } return authenticatedMessage, nil } func (packet *SnmpPacket) marshalSNMPV1TrapHeader() ([]byte, error) { buf := new(bytes.Buffer) // marshal OID oidBytes, err := marshalObjectIdentifier(packet.Enterprise) if err != nil { return nil, fmt.Errorf("unable to marshal OID: %w", err) } buf.Write([]byte{byte(ObjectIdentifier), byte(len(oidBytes))}) buf.Write(oidBytes) // marshal AgentAddress (ip address) ip := net.ParseIP(packet.AgentAddress) ipAddressBytes := ipv4toBytes(ip) buf.Write([]byte{byte(IPAddress), byte(len(ipAddressBytes))}) buf.Write(ipAddressBytes) // marshal GenericTrap. Could just cast GenericTrap to a single byte as IDs greater than 6 are unknown, // but do it properly. See issue 182. var genericTrapBytes []byte genericTrapBytes, err = marshalInt32(packet.GenericTrap) if err != nil { return nil, fmt.Errorf("unable to marshal SNMPv1 GenericTrap: %w", err) } buf.Write([]byte{byte(Integer), byte(len(genericTrapBytes))}) buf.Write(genericTrapBytes) // marshal SpecificTrap var specificTrapBytes []byte specificTrapBytes, err = marshalInt32(packet.SpecificTrap) if err != nil { return nil, fmt.Errorf("unable to marshal SNMPv1 SpecificTrap: %w", err) } buf.Write([]byte{byte(Integer), byte(len(specificTrapBytes))}) buf.Write(specificTrapBytes) // marshal timeTicks timeTickBytes, err := marshalUint32(packet.Timestamp) if err != nil { return nil, fmt.Errorf("unable to Timestamp: %w", err) } buf.Write([]byte{byte(TimeTicks), byte(len(timeTickBytes))}) buf.Write(timeTickBytes) return buf.Bytes(), nil } // marshal a PDU func (packet *SnmpPacket) marshalPDU() ([]byte, error) { buf := new(bytes.Buffer) switch packet.PDUType { case GetBulkRequest: // requestid err := shrinkAndWriteUint(buf, int(packet.RequestID)) if err != nil { return nil, err } // non repeaters nonRepeaters, err := marshalUint32(packet.NonRepeaters) if err != nil { return nil, fmt.Errorf("marshalPDU: unable to marshal NonRepeaters to uint32: %w", err) } buf.Write([]byte{2, byte(len(nonRepeaters))}) if err = binary.Write(buf, binary.BigEndian, nonRepeaters); err != nil { return nil, fmt.Errorf("marshalPDU: unable to marshal NonRepeaters: %w", err) } // max repetitions maxRepetitions, err := marshalUint32(packet.MaxRepetitions) if err != nil { return nil, fmt.Errorf("marshalPDU: unable to marshal maxRepetitions to uint32: %w", err) } buf.Write([]byte{2, byte(len(maxRepetitions))}) if err = binary.Write(buf, binary.BigEndian, maxRepetitions); err != nil { return nil, fmt.Errorf("marshalPDU: unable to marshal maxRepetitions: %w", err) } case Trap: // write SNMP V1 Trap Header fields snmpV1TrapHeader, err := packet.marshalSNMPV1TrapHeader() if err != nil { return nil, err } buf.Write(snmpV1TrapHeader) default: // requestid err := shrinkAndWriteUint(buf, int(packet.RequestID)) if err != nil { return nil, err } // error status errorStatus, err := marshalUint32(packet.Error) if err != nil { return nil, fmt.Errorf("marshalPDU: unable to marshal errorStatus to uint32: %w", err) } buf.Write([]byte{2, byte(len(errorStatus))}) if err = binary.Write(buf, binary.BigEndian, errorStatus); err != nil { return nil, fmt.Errorf("marshalPDU: unable to marshal errorStatus: %w", err) } // error index errorIndex, err := marshalUint32(packet.ErrorIndex) if err != nil { return nil, fmt.Errorf("marshalPDU: unable to marshal errorIndex to uint32: %w", err) } buf.Write([]byte{2, byte(len(errorIndex))}) if err = binary.Write(buf, binary.BigEndian, errorIndex); err != nil { return nil, fmt.Errorf("marshalPDU: unable to marshal errorIndex: %w", err) } } // build varbind list vbl, err := packet.marshalVBL() if err != nil { return nil, fmt.Errorf("marshalPDU: unable to marshal varbind list: %w", err) } buf.Write(vbl) // build up resulting pdu pdu := new(bytes.Buffer) // calculate pdu length bufLengthBytes, err := marshalLength(buf.Len()) if err != nil { return nil, fmt.Errorf("marshalPDU: unable to marshal pdu length: %w", err) } // write request type pdu.WriteByte(byte(packet.PDUType)) // write pdu length pdu.Write(bufLengthBytes) // write the tail (buf) if _, err = buf.WriteTo(pdu); err != nil { return nil, fmt.Errorf("marshalPDU: unable to marshal pdu: %w", err) } return pdu.Bytes(), nil } // marshal a varbind list func (packet *SnmpPacket) marshalVBL() ([]byte, error) { vblBuf := new(bytes.Buffer) for _, pdu := range packet.Variables { pdu := pdu vb, err := marshalVarbind(&pdu) if err != nil { return nil, err } vblBuf.Write(vb) } vblBytes := vblBuf.Bytes() vblLengthBytes, err := marshalLength(len(vblBytes)) if err != nil { return nil, err } // FIX does bytes.Buffer give better performance than byte slices? result := []byte{byte(Sequence)} result = append(result, vblLengthBytes...) result = append(result, vblBytes...) return result, nil } // marshal a varbind func marshalVarbind(pdu *SnmpPDU) ([]byte, error) { oid, err := marshalObjectIdentifier(pdu.Name) if err != nil { return nil, err } pduBuf := new(bytes.Buffer) tmpBuf := new(bytes.Buffer) // Marshal the PDU type into the appropriate BER switch pdu.Type { case Null: ltmp, err2 := marshalLength(len(oid)) if err2 != nil { return nil, err2 } tmpBuf.Write([]byte{byte(ObjectIdentifier)}) tmpBuf.Write(ltmp) tmpBuf.Write(oid) tmpBuf.Write([]byte{byte(Null), byte(EndOfContents)}) ltmp, err2 = marshalLength(tmpBuf.Len()) if err2 != nil { return nil, err2 } pduBuf.Write([]byte{byte(Sequence)}) pduBuf.Write(ltmp) _, err2 = tmpBuf.WriteTo(pduBuf) if err2 != nil { return nil, err2 } case Integer: // Oid tmpBuf.Write([]byte{byte(ObjectIdentifier), byte(len(oid))}) tmpBuf.Write(oid) // Number var intBytes []byte switch value := pdu.Value.(type) { case byte: intBytes = []byte{byte(pdu.Value.(int))} case int: if intBytes, err = marshalInt32(value); err != nil { return nil, fmt.Errorf("error mashalling PDU Integer: %w", err) } default: return nil, fmt.Errorf("unable to marshal PDU Integer; not byte or int") } tmpBuf.Write([]byte{byte(Integer), byte(len(intBytes))}) tmpBuf.Write(intBytes) // Sequence, length of oid + integer, then oid/integer data pduBuf.WriteByte(byte(Sequence)) pduBuf.WriteByte(byte(len(oid) + len(intBytes) + 4)) pduBuf.Write(tmpBuf.Bytes()) case Counter32, Gauge32, TimeTicks, Uinteger32: // Oid tmpBuf.Write([]byte{byte(ObjectIdentifier), byte(len(oid))}) tmpBuf.Write(oid) // Number var intBytes []byte switch value := pdu.Value.(type) { case uint32: if intBytes, err = marshalUint32(value); err != nil { return nil, fmt.Errorf("error marshalling PDU Uinteger32 type from uint32: %w", err) } case uint: if intBytes, err = marshalUint32(value); err != nil { return nil, fmt.Errorf("error marshalling PDU Uinteger32 type from uint: %w", err) } default: return nil, fmt.Errorf("unable to marshal pdu.Type %v; unknown pdu.Value %v[type=%T]", pdu.Type, pdu.Value, pdu.Value) } tmpBuf.Write([]byte{byte(pdu.Type), byte(len(intBytes))}) tmpBuf.Write(intBytes) // Sequence, length of oid + integer, then oid/integer data pduBuf.WriteByte(byte(Sequence)) pduBuf.WriteByte(byte(len(oid) + len(intBytes) + 4)) pduBuf.Write(tmpBuf.Bytes()) case OctetString, BitString, Opaque: // Oid tmpBuf.Write([]byte{byte(ObjectIdentifier), byte(len(oid))}) tmpBuf.Write(oid) // OctetString var octetStringBytes []byte switch value := pdu.Value.(type) { case []byte: octetStringBytes = value case string: octetStringBytes = []byte(value) default: return nil, fmt.Errorf("unable to marshal PDU OctetString; not []byte or string") } var length []byte length, err = marshalLength(len(octetStringBytes)) if err != nil { return nil, fmt.Errorf("unable to marshal PDU length: %w", err) } tmpBuf.WriteByte(byte(pdu.Type)) tmpBuf.Write(length) tmpBuf.Write(octetStringBytes) tmpBytes := tmpBuf.Bytes() length, err = marshalLength(len(tmpBytes)) if err != nil { return nil, fmt.Errorf("unable to marshal PDU data length: %w", err) } // Sequence, length of oid + octetstring, then oid/octetstring data pduBuf.WriteByte(byte(Sequence)) pduBuf.Write(length) pduBuf.Write(tmpBytes) case ObjectIdentifier: // Oid tmpBuf.Write([]byte{byte(ObjectIdentifier), byte(len(oid))}) tmpBuf.Write(oid) value := pdu.Value.(string) oidBytes, err := marshalObjectIdentifier(value) if err != nil { return nil, fmt.Errorf("error marshalling ObjectIdentifier: %w", err) } // Oid data var length []byte length, err = marshalLength(len(oidBytes)) if err != nil { return nil, fmt.Errorf("error marshalling ObjectIdentifier length: %w", err) } tmpBuf.WriteByte(byte(pdu.Type)) tmpBuf.Write(length) tmpBuf.Write(oidBytes) tmpBytes := tmpBuf.Bytes() length, err = marshalLength(len(tmpBytes)) if err != nil { return nil, fmt.Errorf("error marshalling ObjectIdentifier data length: %w", err) } // Sequence, length of oid + oid, then oid/oid data pduBuf.WriteByte(byte(Sequence)) pduBuf.Write(length) pduBuf.Write(tmpBytes) case IPAddress: // Oid tmpBuf.Write([]byte{byte(ObjectIdentifier), byte(len(oid))}) tmpBuf.Write(oid) // OctetString var ipAddressBytes []byte switch value := pdu.Value.(type) { case []byte: ipAddressBytes = value case string: ip := net.ParseIP(value) ipAddressBytes = ipv4toBytes(ip) default: return nil, fmt.Errorf("unable to marshal PDU IPAddress; not []byte or string") } tmpBuf.Write([]byte{byte(IPAddress), byte(len(ipAddressBytes))}) tmpBuf.Write(ipAddressBytes) // Sequence, length of oid + octetstring, then oid/octetstring data pduBuf.WriteByte(byte(Sequence)) pduBuf.WriteByte(byte(len(oid) + len(ipAddressBytes) + 4)) pduBuf.Write(tmpBuf.Bytes()) case OpaqueFloat, OpaqueDouble: converters := map[Asn1BER]func(interface{}) ([]byte, error){ OpaqueFloat: marshalFloat32, OpaqueDouble: marshalFloat64, } intBuf := new(bytes.Buffer) intBuf.WriteByte(byte(AsnExtensionTag)) intBuf.WriteByte(byte(pdu.Type)) intBytes, err := converters[pdu.Type](pdu.Value) if err != nil { return nil, fmt.Errorf("error converting PDU value type %v to %v: %w", pdu.Value, pdu.Type, err) } intLength, err := marshalLength(len(intBytes)) if err != nil { return nil, fmt.Errorf("error marshalling Float type length: %w", err) } intBuf.Write(intLength) intBuf.Write(intBytes) opaqueLength, err := marshalLength(len(intBuf.Bytes())) if err != nil { return nil, fmt.Errorf("error marshalling Float type length: %w", err) } tmpBuf.Write([]byte{byte(ObjectIdentifier), byte(len(oid))}) tmpBuf.Write(oid) tmpBuf.WriteByte(byte(Opaque)) tmpBuf.Write(opaqueLength) tmpBuf.Write(intBuf.Bytes()) length, err := marshalLength(len(tmpBuf.Bytes())) if err != nil { return nil, fmt.Errorf("error marshalling Float type length: %w", err) } // Sequence, length of oid + oid, then oid/oid data pduBuf.WriteByte(byte(Sequence)) pduBuf.Write(length) pduBuf.Write(tmpBuf.Bytes()) case Counter64: tmpBuf.Write([]byte{byte(ObjectIdentifier), byte(len(oid))}) tmpBuf.Write(oid) tmpBuf.WriteByte(byte(pdu.Type)) intBytes := marshalUint64(pdu.Value) tmpBuf.WriteByte(byte(len(intBytes))) tmpBuf.Write(intBytes) tmpBytes := tmpBuf.Bytes() length, err := marshalLength(len(tmpBytes)) if err != nil { return nil, fmt.Errorf("error marshalling Float type length: %w", err) } // Sequence, length of oid + oid, then oid/oid data pduBuf.WriteByte(byte(Sequence)) pduBuf.Write(length) pduBuf.Write(tmpBytes) case NoSuchInstance, NoSuchObject, EndOfMibView: tmpBuf.Write([]byte{byte(ObjectIdentifier), byte(len(oid))}) tmpBuf.Write(oid) tmpBuf.WriteByte(byte(pdu.Type)) tmpBuf.WriteByte(byte(EndOfContents)) tmpBytes := tmpBuf.Bytes() length, err := marshalLength(len(tmpBytes)) if err != nil { return nil, fmt.Errorf("error marshalling Null type data length: %w", err) } // Sequence, length of oid + oid, then oid/oid data pduBuf.WriteByte(byte(Sequence)) pduBuf.Write(length) pduBuf.Write(tmpBytes) default: return nil, fmt.Errorf("unable to marshal PDU: unknown BER type %q", pdu.Type) } return pduBuf.Bytes(), nil } // -- Unmarshalling Logic ------------------------------------------------------ func (x *GoSNMP) unmarshalVersionFromHeader(packet []byte, response *SnmpPacket) (SnmpVersion, int, error) { if len(packet) < 2 { return 0, 0, fmt.Errorf("cannot unmarshal empty packet") } if response == nil { return 0, 0, fmt.Errorf("cannot unmarshal response into nil packet reference") } response.Variables = make([]SnmpPDU, 0, 5) // Start parsing the packet cursor := 0 // First bytes should be 0x30 if PDUType(packet[0]) != Sequence { return 0, 0, fmt.Errorf("invalid packet header") } length, cursor, err := parseLength(packet) if err != nil { return 0, 0, err } if len(packet) != length { return 0, 0, fmt.Errorf("error verifying packet sanity: Got %d Expected: %d", len(packet), length) } x.Logger.Printf("Packet sanity verified, we got all the bytes (%d)", length) // Parse SNMP Version rawVersion, count, err := parseRawField(x.Logger, packet[cursor:], "version") if err != nil { return 0, 0, fmt.Errorf("error parsing SNMP packet version: %w", err) } cursor += count if cursor >= len(packet) { return 0, 0, fmt.Errorf("error parsing SNMP packet, packet length %d cursor %d", len(packet), cursor) } if version, ok := rawVersion.(int); ok { x.Logger.Printf("Parsed version %d", version) return SnmpVersion(version), cursor, nil } return 0, cursor, err } func (x *GoSNMP) unmarshalHeader(packet []byte, response *SnmpPacket) (int, error) { version, cursor, err := x.unmarshalVersionFromHeader(packet, response) if err != nil { return 0, err } response.Version = version if response.Version == Version3 { oldcursor := cursor cursor, err = x.unmarshalV3Header(packet, cursor, response) if err != nil { return 0, err } x.Logger.Printf("UnmarshalV3Header done. [with SecurityParameters]. Header Size %d. Last 4 Bytes=[%v]", cursor-oldcursor, packet[cursor-4:cursor]) } else { // Parse community rawCommunity, count, err := parseRawField(x.Logger, packet[cursor:], "community") if err != nil { return 0, fmt.Errorf("error parsing community string: %w", err) } cursor += count if cursor > len(packet) { return 0, fmt.Errorf("error parsing SNMP packet, packet length %d cursor %d", len(packet), cursor) } if community, ok := rawCommunity.(string); ok { response.Community = community x.Logger.Printf("Parsed community %s", community) } } return cursor, nil } func (x *GoSNMP) unmarshalPayload(packet []byte, cursor int, response *SnmpPacket) error { if len(packet) == 0 { return errors.New("cannot unmarshal nil or empty payload packet") } if cursor >= len(packet) { return fmt.Errorf("cannot unmarshal payload, packet length %d cursor %d", len(packet), cursor) } if response == nil { return errors.New("cannot unmarshal payload response into nil packet reference") } // Parse SNMP packet type requestType := PDUType(packet[cursor]) x.Logger.Printf("UnmarshalPayload Meet PDUType %#x. Offset %v", requestType, cursor) switch requestType { // known, supported types case GetResponse, GetNextRequest, GetBulkRequest, Report, SNMPv2Trap, GetRequest, SetRequest, InformRequest: response.PDUType = requestType if err := x.unmarshalResponse(packet[cursor:], response); err != nil { return fmt.Errorf("error in unmarshalResponse: %w", err) } // If it's an InformRequest, mark the trap. response.IsInform = (requestType == InformRequest) case Trap: response.PDUType = requestType if err := x.unmarshalTrapV1(packet[cursor:], response); err != nil { return fmt.Errorf("error in unmarshalTrapV1: %w", err) } default: x.Logger.Printf("UnmarshalPayload Meet Unknown PDUType %#x. Offset %v", requestType, cursor) return fmt.Errorf("unknown PDUType %#x", requestType) } return nil } func (x *GoSNMP) unmarshalResponse(packet []byte, response *SnmpPacket) error { cursor := 0 getResponseLength, cursor, err := parseLength(packet) if err != nil { return err } if len(packet) != getResponseLength { return fmt.Errorf("error verifying Response sanity: Got %d Expected: %d", len(packet), getResponseLength) } x.Logger.Printf("getResponseLength: %d", getResponseLength) // Parse Request-ID rawRequestID, count, err := parseRawField(x.Logger, packet[cursor:], "request id") if err != nil { return fmt.Errorf("error parsing SNMP packet request ID: %w", err) } cursor += count if cursor > len(packet) { return fmt.Errorf("error parsing SNMP packet, packet length %d cursor %d", len(packet), cursor) } if requestid, ok := rawRequestID.(int); ok { response.RequestID = uint32(requestid) x.Logger.Printf("requestID: %d", response.RequestID) } if response.PDUType == GetBulkRequest { // Parse Non Repeaters rawNonRepeaters, count, err := parseRawField(x.Logger, packet[cursor:], "non repeaters") if err != nil { return fmt.Errorf("error parsing SNMP packet non repeaters: %w", err) } cursor += count if cursor > len(packet) { return fmt.Errorf("error parsing SNMP packet, packet length %d cursor %d", len(packet), cursor) } if nonRepeaters, ok := rawNonRepeaters.(int); ok { response.NonRepeaters = uint8(nonRepeaters) } // Parse Max Repetitions rawMaxRepetitions, count, err := parseRawField(x.Logger, packet[cursor:], "max repetitions") if err != nil { return fmt.Errorf("error parsing SNMP packet max repetitions: %w", err) } cursor += count if cursor > len(packet) { return fmt.Errorf("error parsing SNMP packet, packet length %d cursor %d", len(packet), cursor) } if maxRepetitions, ok := rawMaxRepetitions.(int); ok { response.MaxRepetitions = uint32(maxRepetitions & 0x7FFFFFFF) } } else { // Parse Error-Status rawError, count, err := parseRawField(x.Logger, packet[cursor:], "error-status") if err != nil { return fmt.Errorf("error parsing SNMP packet error: %w", err) } cursor += count if cursor > len(packet) { return fmt.Errorf("error parsing SNMP packet, packet length %d cursor %d", len(packet), cursor) } if errorStatus, ok := rawError.(int); ok { response.Error = SNMPError(errorStatus) x.Logger.Printf("errorStatus: %d", uint8(errorStatus)) } // Parse Error-Index rawErrorIndex, count, err := parseRawField(x.Logger, packet[cursor:], "error index") if err != nil { return fmt.Errorf("error parsing SNMP packet error index: %w", err) } cursor += count if cursor > len(packet) { return fmt.Errorf("error parsing SNMP packet, packet length %d cursor %d", len(packet), cursor) } if errorindex, ok := rawErrorIndex.(int); ok { response.ErrorIndex = uint8(errorindex) x.Logger.Printf("error-index: %d", uint8(errorindex)) } } return x.unmarshalVBL(packet[cursor:], response) } func (x *GoSNMP) unmarshalTrapV1(packet []byte, response *SnmpPacket) error { cursor := 0 getResponseLength, cursor, err := parseLength(packet) if err != nil { return err } if len(packet) != getResponseLength { return fmt.Errorf("error verifying Response sanity: Got %d Expected: %d", len(packet), getResponseLength) } x.Logger.Printf("getResponseLength: %d", getResponseLength) // Parse Enterprise rawEnterprise, count, err := parseRawField(x.Logger, packet[cursor:], "enterprise") if err != nil { return fmt.Errorf("error parsing SNMP packet error: %w", err) } cursor += count if cursor > len(packet) { return fmt.Errorf("error parsing SNMP packet, packet length %d cursor %d", len(packet), cursor) } if Enterprise, ok := rawEnterprise.(string); ok { response.Enterprise = Enterprise x.Logger.Printf("Enterprise: %+v", Enterprise) } // Parse AgentAddress rawAgentAddress, count, err := parseRawField(x.Logger, packet[cursor:], "agent-address") if err != nil { return fmt.Errorf("error parsing SNMP packet error: %w", err) } cursor += count if cursor > len(packet) { return fmt.Errorf("error parsing SNMP packet, packet length %d cursor %d", len(packet), cursor) } if AgentAddress, ok := rawAgentAddress.(string); ok { response.AgentAddress = AgentAddress x.Logger.Printf("AgentAddress: %s", AgentAddress) } // Parse GenericTrap rawGenericTrap, count, err := parseRawField(x.Logger, packet[cursor:], "generic-trap") if err != nil { return fmt.Errorf("error parsing SNMP packet error: %w", err) } cursor += count if cursor > len(packet) { return fmt.Errorf("error parsing SNMP packet, packet length %d cursor %d", len(packet), cursor) } if GenericTrap, ok := rawGenericTrap.(int); ok { response.GenericTrap = GenericTrap x.Logger.Printf("GenericTrap: %d", GenericTrap) } // Parse SpecificTrap rawSpecificTrap, count, err := parseRawField(x.Logger, packet[cursor:], "specific-trap") if err != nil { return fmt.Errorf("error parsing SNMP packet error: %w", err) } cursor += count if cursor > len(packet) { return fmt.Errorf("error parsing SNMP packet, packet length %d cursor %d", len(packet), cursor) } if SpecificTrap, ok := rawSpecificTrap.(int); ok { response.SpecificTrap = SpecificTrap x.Logger.Printf("SpecificTrap: %d", SpecificTrap) } // Parse TimeStamp rawTimestamp, count, err := parseRawField(x.Logger, packet[cursor:], "time-stamp") if err != nil { return fmt.Errorf("error parsing SNMP packet error: %w", err) } cursor += count if cursor > len(packet) { return fmt.Errorf("error parsing SNMP packet, packet length %d cursor %d", len(packet), cursor) } if Timestamp, ok := rawTimestamp.(uint); ok { response.Timestamp = Timestamp x.Logger.Printf("Timestamp: %d", Timestamp) } return x.unmarshalVBL(packet[cursor:], response) } // unmarshal a Varbind list func (x *GoSNMP) unmarshalVBL(packet []byte, response *SnmpPacket) error { var cursor, cursorInc int var vblLength int if len(packet) == 0 || cursor > len(packet) { return fmt.Errorf("truncated packet when unmarshalling a VBL, got length %d cursor %d", len(packet), cursor) } if packet[cursor] != 0x30 { return fmt.Errorf("expected a sequence when unmarshalling a VBL, got %x", packet[cursor]) } vblLength, cursor, err := parseLength(packet) if err != nil { return err } if vblLength == 0 || vblLength > len(packet) { return fmt.Errorf("truncated packet when unmarshalling a VBL, packet length %d cursor %d", len(packet), cursor) } if len(packet) != vblLength { return fmt.Errorf("error verifying: packet length %d vbl length %d", len(packet), vblLength) } x.Logger.Printf("vblLength: %d", vblLength) // check for an empty response if vblLength == 2 && packet[1] == 0x00 { return nil } // Loop & parse Varbinds for cursor < vblLength { if packet[cursor] != 0x30 { return fmt.Errorf("expected a sequence when unmarshalling a VB, got %x", packet[cursor]) } _, cursorInc, err = parseLength(packet[cursor:]) if err != nil { return err } cursor += cursorInc if cursor > len(packet) { return fmt.Errorf("error parsing OID Value: packet %d cursor %d", len(packet), cursor) } // Parse OID rawOid, oidLength, err := parseRawField(x.Logger, packet[cursor:], "OID") if err != nil { return fmt.Errorf("error parsing OID Value: %w", err) } cursor += oidLength if cursor > len(packet) { return fmt.Errorf("error parsing OID Value: truncated, packet length %d cursor %d", len(packet), cursor) } oid, ok := rawOid.(string) if !ok { return fmt.Errorf("unable to type assert rawOid |%v| to string", rawOid) } x.Logger.Printf("OID: %s", oid) // Parse Value var decodedVal variable if err = x.decodeValue(packet[cursor:], &decodedVal); err != nil { return fmt.Errorf("error decoding value: %w", err) } valueLength, _, err := parseLength(packet[cursor:]) if err != nil { return err } cursor += valueLength if cursor > len(packet) { return fmt.Errorf("error decoding OID Value: truncated, packet length %d cursor %d", len(packet), cursor) } response.Variables = append(response.Variables, SnmpPDU{Name: oid, Type: decodedVal.Type, Value: decodedVal.Value}) } return nil } // receive response from network and read into a byte array func (x *GoSNMP) receive() ([]byte, error) { var n int var err error // If we are using UDP and unconnected socket, read the packet and // disregard the source address. if uconn, ok := x.Conn.(net.PacketConn); ok { n, _, err = uconn.ReadFrom(x.rxBuf[:]) } else { n, err = x.Conn.Read(x.rxBuf[:]) } if err == io.EOF { return nil, err } else if err != nil { return nil, fmt.Errorf("error reading from socket: %w", err) } if n == rxBufSize { // This should never happen unless we're using something like a unix domain socket. return nil, fmt.Errorf("response buffer too small") } resp := make([]byte, n) copy(resp, x.rxBuf[:n]) return resp, nil } func shrinkAndWriteUint(buf io.Writer, in int) error { out, err := asn1.Marshal(in) if err != nil { return err } _, err = buf.Write(out) return err } golang-github-gosnmp-gosnmp-1.38.0/marshal_test.go000066400000000000000000002000211465470716300221610ustar00rootroot00000000000000// 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 // +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, }, } 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, } } /* 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 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: // msgAuthoritativeEngineBoots: 0 // msgAuthoritativeEngineTime: 0 // msgUserName: // msgAuthenticationParameters: // msgPrivacyParameters: // 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 } golang-github-gosnmp-gosnmp-1.38.0/misc_test.go000066400000000000000000000133301465470716300214720ustar00rootroot00000000000000// 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 || misc // +build all misc package gosnmp import ( "bytes" "crypto" _ "crypto/md5" _ "crypto/sha1" "errors" "math" "math/big" "reflect" "testing" "github.com/stretchr/testify/assert" ) // ----------------------------------------------------------------------------- var testsMarshalLength = []struct { length int expected []byte }{ {1, []byte{0x01}}, {129, []byte{0x81, 0x81}}, {256, []byte{0x82, 0x01, 0x00}}, {272, []byte{0x82, 0x01, 0x10}}, {435, []byte{0x82, 0x01, 0xb3}}, } func TestMarshalLength(t *testing.T) { for i, test := range testsMarshalLength { testBytes, err := marshalLength(test.length) if err != nil { t.Errorf("%d: length %d got err %v", i, test.length, err) } if !reflect.DeepEqual(testBytes, test.expected) { t.Errorf("%d: length %d got |%x| expected |%x|", i, test.length, testBytes, test.expected) } } } // ----------------------------------------------------------------------------- var testsPartition = []struct { currentPosition int partitionSize int sliceLength int ok bool }{ {-1, 3, 8, false}, // test out of range {8, 3, 8, false}, // test out of range {0, 3, 8, false}, // test 0-7/3 per doco {1, 3, 8, false}, {2, 3, 8, true}, {3, 3, 8, false}, {4, 3, 8, false}, {5, 3, 8, true}, {6, 3, 8, false}, {7, 3, 8, true}, {-1, 1, 3, false}, // partition size of one {0, 1, 3, true}, {1, 1, 3, true}, {2, 1, 3, true}, {3, 1, 3, false}, } func TestPartition(t *testing.T) { for i, test := range testsPartition { ok := Partition(test.currentPosition, test.partitionSize, test.sliceLength) if ok != test.ok { t.Errorf("#%d: Bad result: %v (expected %v)", i, ok, test.ok) } } } // --------------------------------------------------------------------- var testsToBigInt = []struct { in interface{} expected *big.Int }{ {int8(-42), big.NewInt(-42)}, {int16(42), big.NewInt(42)}, {int32(-42), big.NewInt(-42)}, {int64(42), big.NewInt(42)}, {uint8(42), big.NewInt(42)}, {uint16(42), big.NewInt(42)}, {uint32(42), big.NewInt(42)}, {uint64(42), big.NewInt(42)}, // edge case, max uint64 {uint64(math.MaxUint64), new(big.Int).SetUint64(math.MaxUint64)}, // string: valid number {"-123456789", big.NewInt(-123456789)}, // string: invalid number {"foo", new(big.Int)}, // unhandled type {struct{}{}, new(big.Int)}, } func TestToBigInt(t *testing.T) { for i, test := range testsToBigInt { result := ToBigInt(test.in) if result.Cmp(test.expected) != 0 { t.Errorf("#%d, %T: got %v expected %v", i, test.in, result, test.expected) } } } // --------------------------------------------------------------------- var testsSnmpVersionString = []struct { in SnmpVersion out string }{ {Version1, "1"}, {Version2c, "2c"}, {Version3, "3"}, } func TestSnmpVersionString(t *testing.T) { for i, test := range testsSnmpVersionString { result := test.in.String() if result != test.out { t.Errorf("#%d, got %v expected %v", i, result, test.out) } } } // --------------------------------------------------------------------- var testSnmpV3MD5HMAC = []struct { password string engineid string outKey []byte }{ {"maplesyrup", string([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}), []byte{0x52, 0x6f, 0x5e, 0xed, 0x9f, 0xcc, 0xe2, 0x6f, 0x89, 0x64, 0xc2, 0x93, 0x07, 0x87, 0xd8, 0x2b}}, } func TestMD5HMAC(t *testing.T) { for i, test := range testSnmpV3MD5HMAC { cacheKey := make([]byte, 1+len(test.password)) cacheKey = append(cacheKey, 'h'+byte(MD5)) cacheKey = append(cacheKey, []byte(test.password)...) result, err := hMAC(crypto.MD5, string(cacheKey), test.password, test.engineid) assert.NoError(t, err) if !bytes.Equal(result, test.outKey) { t.Errorf("#%d, got %v expected %v", i, result, test.outKey) } } } var testSnmpV3SHAHMAC = []struct { password string engineid string outKey []byte }{ {"maplesyrup", string([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}), []byte{0x66, 0x95, 0xfe, 0xbc, 0x92, 0x88, 0xe3, 0x62, 0x82, 0x23, 0x5f, 0xc7, 0x15, 0x1f, 0x12, 0x84, 0x97, 0xb3, 0x8f, 0x3f}}, } func TestSHAHMAC(t *testing.T) { for i, test := range testSnmpV3SHAHMAC { cacheKey := make([]byte, 1+len(test.password)) cacheKey = append(cacheKey, 'h'+byte(SHA)) cacheKey = append(cacheKey, []byte(test.password)...) result, err := hMAC(crypto.SHA1, string(cacheKey), test.password, test.engineid) if err != nil { t.Fatal(err) } if !bytes.Equal(result, test.outKey) { t.Errorf("#%d, got %v expected %v", i, result, test.outKey) } } } // --------------------------------------------------------------------- /* var testMarshalTimeticks = []struct { timeticks uint32 out []byte }{ {1034156, []byte{0x0f, 0xc7, 0xac}}, } func TestMarshalTimeticks(t *testing.T) { for i, test := range testMarshalTimeticks { result, err := marshalTimeticks(test.timeticks) if err != nil { t.Errorf("%d: expected %v got err %v", i, result, err) } if !bytes.Equal(result, test.out) { t.Errorf("#%d, got %v expected %v", i, result, test.out) } } } */ // parseBitString parses an ASN.1 bit string from the given byte slice and returns it. func parseBitString(bytes []byte) (ret BitStringValue, err error) { if len(bytes) == 0 { err = errors.New("zero length BIT STRING") return } paddingBits := int(bytes[0]) if paddingBits > 7 || len(bytes) == 1 && paddingBits > 0 || bytes[len(bytes)-1]&((1< #include #include #include #include u_char* getPktStart(u_char* pkt, ulong len, ulong off){ return pkt+len-off; } */ import "C" import ( "errors" "fmt" "strconv" "strings" "unsafe" "github.com/gosnmp/gosnmp" ) func isPlayback() bool { return false } type netSnmpValType int /* case TYPE_INTEGER: case TYPE_INTEGER32: type = 'i'; break; case TYPE_GAUGE: case TYPE_UNSIGNED32: type = 'u'; break; case TYPE_UINTEGER: type = '3'; break; case TYPE_COUNTER: type = 'c'; break; case TYPE_COUNTER64: type = 'C'; break; case TYPE_TIMETICKS: type = 't'; break; case TYPE_OCTETSTR: type = 's'; break; case TYPE_BITSTRING: type = 'b'; break; case TYPE_IPADDR: type = 'a'; break; case TYPE_OBJID: type = 'o'; break; */ func berToSnmpValType(in gosnmp.Asn1BER) (C.char, error) { switch in { case gosnmp.Gauge32: return 'u', nil case gosnmp.Integer: return 'i', nil case gosnmp.OctetString: return 's', nil case gosnmp.IPAddress: return 'a', nil case gosnmp.ObjectIdentifier: return 'o', nil case gosnmp.Counter32: return 'c', nil case gosnmp.Counter64: return 'C', nil case gosnmp.OpaqueFloat: return 'F', nil case gosnmp.OpaqueDouble: return 'D', nil case gosnmp.TimeTicks: return 't', nil case gosnmp.Uinteger32: return '3', nil default: return 0, errors.New("unhandled asn1 ber type" + in.String()) } } func verToSnmpVer(in gosnmp.SnmpVersion) (C.int, error) { switch in { case gosnmp.Version1: return C.SNMP_VERSION_1, nil case gosnmp.Version2c: return C.SNMP_VERSION_2c, nil case gosnmp.Version3: return C.SNMP_VERSION_3, nil default: return 0, errors.New("handled snmp version " + in.String()) } } func netSnmpPduPkt(fname string, gopdu gosnmp.SnmpPDU, gosess *gosnmp.GoSNMP, reqid uint32, verbose bool) ([]byte, error) { var errout *C.char var err error oid := oidStringToInts(gopdu.Name) if verbose { netSnmpEnableLogging() } //enable reverse encode C.netsnmp_ds_set_boolean(C.NETSNMP_DS_LIBRARY_ID, C.NETSNMP_DS_LIB_REVERSE_ENCODE, C.NETSNMP_DEFAULT_ASNENCODING_DIRECTION) //create session sess := &C.struct_snmp_session{ version: C.SNMP_DEFAULT_VERSION, community: (*C.uchar)((unsafe.Pointer)(C.CString(gosess.Community))), community_len: C.size_t(len(gosess.Community)), } defer C.free(unsafe.Pointer(sess.community)) //create pdu pdu := C.snmp_pdu_create(C.SNMP_MSG_SET) defer C.free(unsafe.Pointer(pdu)) tmp, err := verToSnmpVer(gosess.Version) if err != nil { return nil, err } pdu.version = C.long(tmp) pdu.reqid = C.long(reqid) tstoid := [1024]C.oid{} tstoidcnt := C.size_t(0) for i := range oid { tstoid[i] = C.oid(oid[i]) tstoidcnt++ } cval := C.CString(valToString(gopdu)) defer C.free(unsafe.Pointer(cval)) ctype, err := berToSnmpValType(gopdu.Type) if err != nil { return nil, err } rv := C.snmp_add_var(pdu, (*C.oid)(unsafe.Pointer(&tstoid)), tstoidcnt, ctype, cval) if rv < 0 { C.snmp_perror(errout) err = fmt.Errorf("net-snmp error: snmp_add_var: %s", C.GoString(errout)) return nil, err } //render packet sz := 2048 pktout := (*C.uchar)(C.malloc(C.size_t(sz))) pktoutlen := C.size_t(sz) var pktoutoffset C.size_t rv = C.snmp_build(&pktout, &pktoutlen, &pktoutoffset, sess, pdu) if rv < C.int(0) { C.snmp_error(sess, nil, nil, &errout) err = fmt.Errorf("net-snmp error: snmp_build: %s", C.GoString(errout)) return nil, err } defer C.free(unsafe.Pointer(pktout)) return C.GoBytes(unsafe.Pointer(C.getPktStart(pktout, pktoutlen, pktoutoffset)), C.int(pktoutoffset)), nil } func netSnmpEnableLogging() { C.snmp_enable_stderrlog() C.snmp_set_do_debugging(1) C.snmp_set_dump_packet(1) tmp := C.CString("") C.debug_register_tokens(tmp) C.free(unsafe.Pointer(tmp)) } func oidStringToInts(in string) []int { out := make([]int, 0, len(in)) for _, oi := range strings.Split(in, ".") { tmp, err := strconv.Atoi(oi) if err == nil { out = append(out, tmp) } } return out } func valToString(gopdu gosnmp.SnmpPDU) string { var val any switch gopdu.Type { case gosnmp.OctetString: b, ok := gopdu.Value.([]byte) if ok { val = string(b) } else { val = gopdu.Value } default: val = gopdu.Value } return fmt.Sprintf("%v", val) } golang-github-gosnmp-gosnmp-1.38.0/netsnmp/gosnmp_playback.go000066400000000000000000000015241465470716300243370ustar00rootroot00000000000000// 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 !netsnmp package netsnmp import ( "encoding/base64" "errors" "fmt" "io" "os" "github.com/gosnmp/gosnmp" ) func isPlayback() bool { return true } func netSnmpPduPkt(fname string, _ gosnmp.SnmpPDU, _ *gosnmp.GoSNMP, _ uint32, _ bool) ([]byte, error) { f, err := os.Open(fname) if err != nil { if os.IsNotExist(err) { return nil, fmt.Errorf("%w; run test with `-rec=true` to create missing playback files", err) } return nil, err } defer f.Close() out, err := io.ReadAll(base64.NewDecoder(base64.StdEncoding, f)) if err != nil { return nil, err } else if len(out) == 0 { return nil, errors.New("netsnmp playback is empty") } return out, nil } golang-github-gosnmp-gosnmp-1.38.0/netsnmp/gosnmp_test.go000066400000000000000000000116061465470716300235320ustar00rootroot00000000000000// 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. package netsnmp import ( "encoding/base64" "flag" "fmt" "math" "net" "os" "path/filepath" "testing" "time" "github.com/google/go-cmp/cmp" "github.com/google/gopacket" "github.com/google/gopacket/layers" "github.com/google/gopacket/pcapgo" "github.com/gosnmp/gosnmp" ) var ( rec = flag.Bool("rec", false, "record and use net-snmp expected outputs, else use recorded values [if true requires libsnmp/cgo]. ") pcap = flag.String("pcap", "", "dir to put exp and got data as pcap files, no pcaps made if blank.") ) func TestPDU(t *testing.T) { flag.Parse() if isPlayback() { t.Log("playback mode enabled") } recdir := filepath.Join("testdata", t.Name()) if *rec && !isPlayback() { if err := os.MkdirAll(recdir, 0755); err != nil { t.Fatalf("error creating record dir: %s", err) } } else if *rec { t.Fatal("record mode requires `-tags netsnmp` and libsnmpd installed") } pcapdir := "" if *pcap != "" { pcapdir = filepath.Join(*pcap, t.Name()) if err := os.MkdirAll(pcapdir, 0755); err != nil { t.Fatalf("error creating pcap dir: %s", err) } } tstpdus := []gosnmp.SnmpPDU{ { Name: ".1.3.6.1.2.1.1.7.0", Type: gosnmp.Integer, Value: 104, }, { Name: ".1.3.6.1.2.1.2.2.1.10.1", Type: gosnmp.Counter32, Value: uint32(271070065), }, { Name: ".1.3.6.1.2.1.2.2.1.5.1", Type: gosnmp.Gauge32, Value: uint32(math.MaxUint32), }, { Name: ".1.3.6.1.2.1.1.4.0", Type: gosnmp.OctetString, Value: []byte("Administrator"), }, { Name: ".1.3.6.1.2.1.4.21.1.1.127.0.0.1", Type: gosnmp.IPAddress, Value: "127.0.0.1", }, { Name: ".1.3.6.1.4.1.6574.4.2.12.1.0", Type: gosnmp.OpaqueFloat, Value: float32(10.0), }, { Name: ".1.3.6.1.4.1.6574.4.2.12.1.1", Type: gosnmp.OpaqueFloat, Value: float32(0.0), }, { Name: ".1.3.6.1.4.1.6574.4.2.12.2.0", Type: gosnmp.OpaqueDouble, Value: float64(10.0), }, { Name: ".1.3.6.1.4.1.6574.4.2.12.2.1", Type: gosnmp.OpaqueDouble, Value: float64(0.0), }, { Name: ".1.3.6.1.2.1.2.2.1.10.1", Type: gosnmp.Counter32, Value: uint32(271070065), }, { Name: ".1.3.6.1.2.1.1.3.0", Type: gosnmp.TimeTicks, Value: uint32(318870100), }, { Name: ".1.3.6.1.2.1.1.3.1", Type: gosnmp.Uinteger32, Value: uint32(math.MaxInt32), }, { Name: ".1.3.6.1.2.1.1.3.2", Type: gosnmp.Counter64, Value: uint64(math.MaxInt64), }, } sess := gosnmp.Default sess.Version = gosnmp.Version2c for i, tstpdu := range tstpdus { tname := fmt.Sprintf("test%d_PDU%s", i, tstpdu.Type.String()) t.Run(tname, func(t *testing.T) { fname := filepath.Join(recdir, tname+"_pkt.b64") pdus := []gosnmp.SnmpPDU{tstpdu} pkt := sess.MkSnmpPacket(gosnmp.SetRequest, pdus, 0, 0) pkt.RequestID++ exp, err := netSnmpPduPkt(fname, pdus[0], sess, pkt.RequestID, testing.Verbose()) if err != nil { t.Fatal(err) } if *rec { pktrec := base64.StdEncoding.EncodeToString(exp) if err = os.WriteFile(fname, []byte(pktrec), 0600); err != nil { t.Logf("error writing record file: %s", err) } } got, err := pkt.MarshalMsg() if err != nil { t.Fatal(err) } if *pcap != "" { savePcap(t, filepath.Join(pcapdir, tname), exp, got) } if diff := cmp.Diff(exp, got); diff != "" { t.Fatalf("\ngot(%d): %q\nexp(%d): %q\ndiff:\n%s", len(got), got, len(exp), exp, diff) } }) } } func writePcap(fn string, payload []byte) error { fn += ".pcap" f, err := os.OpenFile(fn, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644) if err != nil { return err } defer f.Close() l3 := &layers.IPv4{ SrcIP: net.ParseIP("192.168.2.1"), DstIP: net.ParseIP("192.168.2.2"), Protocol: layers.IPProtocolUDP, Version: 4, } l4 := &layers.UDP{ SrcPort: 161, DstPort: 161, } err = l4.SetNetworkLayerForChecksum(l3) if err != nil { return err } buf := gopacket.NewSerializeBuffer() err = gopacket.SerializeLayers( buf, gopacket.SerializeOptions{ ComputeChecksums: true, FixLengths: true, }, l3, l4, gopacket.Payload(payload), ) if err != nil { return err } pkt := gopacket.NewPacket(buf.Bytes(), layers.LinkTypeIPv4, gopacket.Default) w := pcapgo.NewWriter(f) err = w.WriteFileHeader(1600, layers.LinkTypeIPv4) if err != nil { return err } err = w.WritePacket(gopacket.CaptureInfo{ Timestamp: time.Now(), CaptureLength: len(pkt.Data()), Length: len(pkt.Data()), }, pkt.Data()) if err != nil { return err } return nil } func savePcap(t *testing.T, fp string, exp, got []byte) { err := writePcap(fp+"_exp", exp) if err != nil { t.Logf("error saving exp pcap: %s", err.Error()) } err = writePcap(fp+"_got", got) if err != nil { t.Logf("error saving got pcap: %s", err.Error()) } } golang-github-gosnmp-gosnmp-1.38.0/netsnmp/netsnmp.go000066400000000000000000000005031465470716300226460ustar00rootroot00000000000000// 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. // Package netsnmp validates the gosnmp implementation against net-snmp // either via recorded results or directly via the `libsnmp` library. package netsnmp golang-github-gosnmp-gosnmp-1.38.0/netsnmp/testdata/000077500000000000000000000000001465470716300224465ustar00rootroot00000000000000golang-github-gosnmp-gosnmp-1.38.0/netsnmp/testdata/TestPDU/000077500000000000000000000000001465470716300237365ustar00rootroot00000000000000golang-github-gosnmp-gosnmp-1.38.0/netsnmp/testdata/TestPDU/test0_PDUInteger_pkt.b64000066400000000000000000000000701465470716300302130ustar00rootroot00000000000000MCcCAQEEBnB1YmxpY6MaAgEBAgEAAgEAMA8wDQYIKwYBAgEBBwACAWg=golang-github-gosnmp-gosnmp-1.38.0/netsnmp/testdata/TestPDU/test10_PDUTimeTicks_pkt.b64000066400000000000000000000000741465470716300305770ustar00rootroot00000000000000MCoCAQEEBnB1YmxpY6MdAgEBAgEAAgEAMBIwEAYIKwYBAgEBAwBDBBMBklQ=golang-github-gosnmp-gosnmp-1.38.0/netsnmp/testdata/TestPDU/test11_PDUUinteger32_pkt.b64000066400000000000000000000000741465470716300306330ustar00rootroot00000000000000MCoCAQEEBnB1YmxpY6MdAgEBAgEAAgEAMBIwEAYIKwYBAgEBAwFHBH////8=golang-github-gosnmp-gosnmp-1.38.0/netsnmp/testdata/TestPDU/test12_PDUCounter64_pkt.b64000066400000000000000000000001001465470716300304640ustar00rootroot00000000000000MC4CAQEEBnB1YmxpY6MhAgEBAgEAAgEAMBYwFAYIKwYBAgEBAwJGCH//////////golang-github-gosnmp-gosnmp-1.38.0/netsnmp/testdata/TestPDU/test1_PDUCounter32_pkt.b64000066400000000000000000000001001465470716300303750ustar00rootroot00000000000000MCwCAQEEBnB1YmxpY6MfAgEBAgEAAgEAMBQwEgYKKwYBAgECAgEKAUEEECgzcQ==golang-github-gosnmp-gosnmp-1.38.0/netsnmp/testdata/TestPDU/test2_PDUGauge32_pkt.b64000066400000000000000000000001001465470716300300070ustar00rootroot00000000000000MC0CAQEEBnB1YmxpY6MgAgEBAgEAAgEAMBUwEwYKKwYBAgECAgEFAUIFAP////8=golang-github-gosnmp-gosnmp-1.38.0/netsnmp/testdata/TestPDU/test3_PDUOctetString_pkt.b64000066400000000000000000000001101465470716300310610ustar00rootroot00000000000000MDMCAQEEBnB1YmxpY6MmAgEBAgEAAgEAMBswGQYIKwYBAgEBBAAEDUFkbWluaXN0cmF0b3I=golang-github-gosnmp-gosnmp-1.38.0/netsnmp/testdata/TestPDU/test4_PDUIPAddress_pkt.b64000066400000000000000000000001041465470716300304360ustar00rootroot00000000000000MC8CAQEEBnB1YmxpY6MiAgEBAgEAAgEAMBcwFQYNKwYBAgEEFQEBfwAAAUAEfwAAAQ==golang-github-gosnmp-gosnmp-1.38.0/netsnmp/testdata/TestPDU/test5_PDUOpaqueFloat_pkt.b64000066400000000000000000000001041465470716300310410ustar00rootroot00000000000000MDECAQEEBnB1YmxpY6MkAgEBAgEAAgEAMBkwFwYMKwYBBAGzLgQCDAEARAefeARBIAAAgolang-github-gosnmp-gosnmp-1.38.0/netsnmp/testdata/TestPDU/test6_PDUOpaqueFloat_pkt.b64000066400000000000000000000001041465470716300310420ustar00rootroot00000000000000MDECAQEEBnB1YmxpY6MkAgEBAgEAAgEAMBkwFwYMKwYBBAGzLgQCDAEBRAefeAQAAAAAgolang-github-gosnmp-gosnmp-1.38.0/netsnmp/testdata/TestPDU/test7_PDUOpaqueDouble_pkt.b64000066400000000000000000000001141465470716300312110ustar00rootroot00000000000000MDUCAQEEBnB1YmxpY6MoAgEBAgEAAgEAMB0wGwYMKwYBBAGzLgQCDAIARAufeQhAJAAAAAAAAA==golang-github-gosnmp-gosnmp-1.38.0/netsnmp/testdata/TestPDU/test8_PDUOpaqueDouble_pkt.b64000066400000000000000000000001141465470716300312120ustar00rootroot00000000000000MDUCAQEEBnB1YmxpY6MoAgEBAgEAAgEAMB0wGwYMKwYBBAGzLgQCDAIBRAufeQgAAAAAAAAAAA==golang-github-gosnmp-gosnmp-1.38.0/netsnmp/testdata/TestPDU/test9_PDUCounter32_pkt.b64000066400000000000000000000001001465470716300304050ustar00rootroot00000000000000MCwCAQEEBnB1YmxpY6MfAgEBAgEAAgEAMBQwEgYKKwYBAgECAgEKAUEEECgzcQ==golang-github-gosnmp-gosnmp-1.38.0/pdutype_string.go000066400000000000000000000017571465470716300225720ustar00rootroot00000000000000// Code generated by "stringer -type=PDUType"; DO NOT EDIT. package gosnmp import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[Sequence-48] _ = x[GetRequest-160] _ = x[GetNextRequest-161] _ = x[GetResponse-162] _ = x[SetRequest-163] _ = x[Trap-164] _ = x[GetBulkRequest-165] _ = x[InformRequest-166] _ = x[SNMPv2Trap-167] _ = x[Report-168] } const ( _PDUType_name_0 = "Sequence" _PDUType_name_1 = "GetRequestGetNextRequestGetResponseSetRequestTrapGetBulkRequestInformRequestSNMPv2TrapReport" ) var ( _PDUType_index_1 = [...]uint8{0, 10, 24, 35, 45, 49, 63, 76, 86, 92} ) func (i PDUType) String() string { switch { case i == 48: return _PDUType_name_0 case 160 <= i && i <= 168: i -= 160 return _PDUType_name_1[_PDUType_index_1[i]:_PDUType_index_1[i+1]] default: return "PDUType(" + strconv.FormatInt(int64(i), 10) + ")" } } golang-github-gosnmp-gosnmp-1.38.0/snmp_users.sh000077500000000000000000000040061465470716300217060ustar00rootroot00000000000000#!/bin/bash cat << EOF >> /etc/snmp/snmpd.conf createUser noAuthNoPrivUser createUser authMD5OnlyUser MD5 testingpass0123456789 createUser authSHAOnlyUser SHA testingpass9876543210 createUser authSHA224OnlyUser SHA224 testingpass5123456 createUser authSHA256OnlyUser SHA256 testingpass5223456 createUser authSHA384OnlyUser SHA384 testingpass5323456 createUser authSHA512OnlyUser SHA512 testingpass5423456 createUser authMD5PrivDESUser MD5 testingpass9876543210 DES createUser authSHAPrivDESUser SHA testingpassabc6543210 DES createUser authSHA224PrivDESUser SHA224 testingpass6123456 DES createUser authSHA256PrivDESUser SHA256 testingpass6223456 DES createUser authSHA384PrivDESUser SHA384 testingpass6323456 DES createUser authSHA512PrivDESUser SHA512 testingpass6423456 DES createUser authMD5PrivAESUser MD5 AEStestingpass9876543210 AES createUser authSHAPrivAESUser SHA AEStestingpassabc6543210 AES createUser authSHA224PrivAESUser SHA224 testingpass7123456 AES createUser authSHA256PrivAESUser SHA256 testingpass7223456 AES createUser authSHA384PrivAESUser SHA384 testingpass7323456 AES createUser authSHA512PrivAESUser SHA512 testingpass7423456 AES rouser noAuthNoPrivUser noauth rouser authMD5OnlyUser auth rouser authSHAOnlyUser auth rouser authSHA224OnlyUser auth rouser authSHA256OnlyUser auth rouser authSHA384OnlyUser auth rouser authSHA512OnlyUser auth rouser authMD5PrivDESUser authPriv rouser authSHAPrivDESUser authPriv rouser authSHA224PrivDESUser authPriv rouser authSHA256PrivDESUser authPriv rouser authSHA384PrivDESUser authPriv rouser authSHA512PrivDESUser authPriv rouser authMD5PrivAESUser authPriv rouser authSHAPrivAESUser authPriv rouser authSHA224PrivAESUser authPriv rouser authSHA256PrivAESUser authPriv rouser authSHA384PrivAESUser authPriv rouser authSHA512PrivAESUser authPriv EOF # enable ipv6 TODO restart fails - need to enable ipv6 on interface; spin up a Linux instance to check this # sed -i -e '/agentAddress/ s/^/#/' -e '/agentAddress/ s/^##//' /etc/snmp/snmpd.conf golang-github-gosnmp-gosnmp-1.38.0/snmperror_string.go000066400000000000000000000024011465470716300231120ustar00rootroot00000000000000// Code generated by "stringer -type SNMPError"; DO NOT EDIT. package gosnmp import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[NoError-0] _ = x[TooBig-1] _ = x[NoSuchName-2] _ = x[BadValue-3] _ = x[ReadOnly-4] _ = x[GenErr-5] _ = x[NoAccess-6] _ = x[WrongType-7] _ = x[WrongLength-8] _ = x[WrongEncoding-9] _ = x[WrongValue-10] _ = x[NoCreation-11] _ = x[InconsistentValue-12] _ = x[ResourceUnavailable-13] _ = x[CommitFailed-14] _ = x[UndoFailed-15] _ = x[AuthorizationError-16] _ = x[NotWritable-17] _ = x[InconsistentName-18] } const _SNMPError_name = "NoErrorTooBigNoSuchNameBadValueReadOnlyGenErrNoAccessWrongTypeWrongLengthWrongEncodingWrongValueNoCreationInconsistentValueResourceUnavailableCommitFailedUndoFailedAuthorizationErrorNotWritableInconsistentName" var _SNMPError_index = [...]uint8{0, 7, 13, 23, 31, 39, 45, 53, 62, 73, 86, 96, 106, 123, 142, 154, 164, 182, 193, 209} func (i SNMPError) String() string { if i >= SNMPError(len(_SNMPError_index)-1) { return "SNMPError(" + strconv.FormatInt(int64(i), 10) + ")" } return _SNMPError_name[_SNMPError_index[i]:_SNMPError_index[i+1]] } golang-github-gosnmp-gosnmp-1.38.0/snmpv3authprotocol_string.go000066400000000000000000000015051465470716300247610ustar00rootroot00000000000000// Code generated by "stringer -type=SnmpV3AuthProtocol"; DO NOT EDIT. package gosnmp import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[NoAuth-1] _ = x[MD5-2] _ = x[SHA-3] _ = x[SHA224-4] _ = x[SHA256-5] _ = x[SHA384-6] _ = x[SHA512-7] } const _SnmpV3AuthProtocol_name = "NoAuthMD5SHASHA224SHA256SHA384SHA512" var _SnmpV3AuthProtocol_index = [...]uint8{0, 6, 9, 12, 18, 24, 30, 36} func (i SnmpV3AuthProtocol) String() string { i -= 1 if i >= SnmpV3AuthProtocol(len(_SnmpV3AuthProtocol_index)-1) { return "SnmpV3AuthProtocol(" + strconv.FormatInt(int64(i+1), 10) + ")" } return _SnmpV3AuthProtocol_name[_SnmpV3AuthProtocol_index[i]:_SnmpV3AuthProtocol_index[i+1]] } golang-github-gosnmp-gosnmp-1.38.0/snmpv3msgflags_string.go000066400000000000000000000016561465470716300240500ustar00rootroot00000000000000// Code generated by "stringer -type=SnmpV3MsgFlags"; DO NOT EDIT. package gosnmp import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[NoAuthNoPriv-0] _ = x[AuthNoPriv-1] _ = x[AuthPriv-3] _ = x[Reportable-4] } const ( _SnmpV3MsgFlags_name_0 = "NoAuthNoPrivAuthNoPriv" _SnmpV3MsgFlags_name_1 = "AuthPrivReportable" ) var ( _SnmpV3MsgFlags_index_0 = [...]uint8{0, 12, 22} _SnmpV3MsgFlags_index_1 = [...]uint8{0, 8, 18} ) func (i SnmpV3MsgFlags) String() string { switch { case i <= 1: return _SnmpV3MsgFlags_name_0[_SnmpV3MsgFlags_index_0[i]:_SnmpV3MsgFlags_index_0[i+1]] case 3 <= i && i <= 4: i -= 3 return _SnmpV3MsgFlags_name_1[_SnmpV3MsgFlags_index_1[i]:_SnmpV3MsgFlags_index_1[i+1]] default: return "SnmpV3MsgFlags(" + strconv.FormatInt(int64(i), 10) + ")" } } golang-github-gosnmp-gosnmp-1.38.0/snmpv3privprotocol_string.go000066400000000000000000000015111465470716300247750ustar00rootroot00000000000000// Code generated by "stringer -type=SnmpV3PrivProtocol"; DO NOT EDIT. package gosnmp import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[NoPriv-1] _ = x[DES-2] _ = x[AES-3] _ = x[AES192-4] _ = x[AES256-5] _ = x[AES192C-6] _ = x[AES256C-7] } const _SnmpV3PrivProtocol_name = "NoPrivDESAESAES192AES256AES192CAES256C" var _SnmpV3PrivProtocol_index = [...]uint8{0, 6, 9, 12, 18, 24, 31, 38} func (i SnmpV3PrivProtocol) String() string { i -= 1 if i >= SnmpV3PrivProtocol(len(_SnmpV3PrivProtocol_index)-1) { return "SnmpV3PrivProtocol(" + strconv.FormatInt(int64(i+1), 10) + ")" } return _SnmpV3PrivProtocol_name[_SnmpV3PrivProtocol_index[i]:_SnmpV3PrivProtocol_index[i+1]] } golang-github-gosnmp-gosnmp-1.38.0/snmpv3securitymodel_string.go000066400000000000000000000013211465470716300251220ustar00rootroot00000000000000// Code generated by "stringer -type=SnmpV3SecurityModel"; DO NOT EDIT. package gosnmp import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[UserSecurityModel-3] } const _SnmpV3SecurityModel_name = "UserSecurityModel" var _SnmpV3SecurityModel_index = [...]uint8{0, 17} func (i SnmpV3SecurityModel) String() string { i -= 3 if i >= SnmpV3SecurityModel(len(_SnmpV3SecurityModel_index)-1) { return "SnmpV3SecurityModel(" + strconv.FormatInt(int64(i+3), 10) + ")" } return _SnmpV3SecurityModel_name[_SnmpV3SecurityModel_index[i]:_SnmpV3SecurityModel_index[i+1]] } golang-github-gosnmp-gosnmp-1.38.0/trap.go000066400000000000000000000400551465470716300204520ustar00rootroot00000000000000// 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. package gosnmp import ( "errors" "fmt" "net" "strings" "sync" "sync/atomic" "time" ) // // Sending Traps ie GoSNMP acting as an Agent // // SendTrap sends a SNMP Trap // // pdus[0] can a pdu of Type TimeTicks (with the desired uint32 epoch // time). Otherwise a TimeTicks pdu will be prepended, with time set to // now. This mirrors the behaviour of the Net-SNMP command-line tools. // // SendTrap doesn't wait for a return packet from the NMS (Network // Management Station). // // See also Listen() and examples for creating an NMS. // // NOTE: the trap code is currently unreliable when working with snmpv3 - pull requests welcome func (x *GoSNMP) SendTrap(trap SnmpTrap) (result *SnmpPacket, err error) { var pdutype PDUType switch x.Version { case Version2c, Version3: // Default to a v2 trap. pdutype = SNMPv2Trap if len(trap.Variables) == 0 { return nil, fmt.Errorf("function SendTrap requires at least 1 PDU") } if trap.Variables[0].Type == TimeTicks { // check is uint32 if _, ok := trap.Variables[0].Value.(uint32); !ok { return nil, fmt.Errorf("function SendTrap TimeTick must be uint32") } } switch x.MsgFlags { // as per https://www.rfc-editor.org/rfc/rfc3412.html#section-6.4 // The reportableFlag MUST always be zero when the message contains // a PDU from the Unconfirmed Class such as an SNMPv2-trap PDU case 0x4, 0x5, 0x7: // .. therefor bitclear the Reportable flag from the MsgFlags // that we inherited from validateParameters() x.MsgFlags = (x.MsgFlags &^ Reportable) } // If it's an inform, do that instead. if trap.IsInform { pdutype = InformRequest } if trap.Variables[0].Type != TimeTicks { now := uint32(time.Now().Unix()) timetickPDU := SnmpPDU{Name: "1.3.6.1.2.1.1.3.0", Type: TimeTicks, Value: now} // prepend timetickPDU trap.Variables = append([]SnmpPDU{timetickPDU}, trap.Variables...) } case Version1: pdutype = Trap if len(trap.Enterprise) == 0 { return nil, fmt.Errorf("function SendTrap for SNMPV1 requires an Enterprise OID") } if len(trap.AgentAddress) == 0 { return nil, fmt.Errorf("function SendTrap for SNMPV1 requires an Agent Address") } default: err = fmt.Errorf("function SendTrap doesn't support %s", x.Version) return nil, err } packetOut := x.mkSnmpPacket(pdutype, trap.Variables, 0, 0) if x.Version == Version1 { packetOut.Enterprise = trap.Enterprise packetOut.AgentAddress = trap.AgentAddress packetOut.GenericTrap = trap.GenericTrap packetOut.SpecificTrap = trap.SpecificTrap packetOut.Timestamp = trap.Timestamp } // all sends wait for the return packet, except for SNMPv2Trap // -> wait is only for informs return x.send(packetOut, trap.IsInform) } // // Receiving Traps ie GoSNMP acting as an NMS (Network Management // Station). // // GoSNMP.unmarshal() currently only handles SNMPv2Trap // // A TrapListener defines parameters for running a SNMP Trap receiver. // nil values will be replaced by default values. type TrapListener struct { done chan bool listening chan bool sync.Mutex // Params is a reference to the TrapListener's "parent" GoSNMP instance. Params *GoSNMP // OnNewTrap handles incoming Trap and Inform PDUs. OnNewTrap TrapHandlerFunc // CloseTimeout is the max wait time for the socket to gracefully signal its closure. CloseTimeout time.Duration // These unexported fields are for letting test cases // know we are ready. conn *net.UDPConn proto string // Total number of packets received referencing an unknown snmpEngineID usmStatsUnknownEngineIDsCount uint32 finish int32 // Atomic flag; set to 1 when closing connection } // Default timeout value for CloseTimeout of 3 seconds const defaultCloseTimeout = 3 * time.Second // TrapHandlerFunc is a callback function type which receives SNMP Trap and // Inform packets when they are received. If this callback is null, Trap and // Inform PDUs will not be received (Inform responses will still be sent, // however). This callback should not modify the contents of the SnmpPacket // nor the UDPAddr passed to it, and it should copy out any values it wishes to // use instead of retaining references in order to avoid memory fragmentation. // // The general effect of received Trap and Inform packets do not differ for the // receiver, and the response is handled by the caller of the handler, so there // is no need for the application to handle Informs any different than Traps. // Nonetheless, the packet's Type field can be examined to determine what type // of event this is for e.g. statistics gathering functions, etc. type TrapHandlerFunc func(s *SnmpPacket, u *net.UDPAddr) // NewTrapListener returns an initialized TrapListener. // // NOTE: the trap code is currently unreliable when working with snmpv3 - pull requests welcome func NewTrapListener() *TrapListener { tl := &TrapListener{ finish: 0, done: make(chan bool), listening: make(chan bool, 1), // Buffered because one doesn't have to block on it. CloseTimeout: defaultCloseTimeout, } return tl } // Listening returns a sentinel channel on which one can block // until the listener is ready to receive requests. // // NOTE: the trap code is currently unreliable when working with snmpv3 - pull requests welcome func (t *TrapListener) Listening() <-chan bool { t.Lock() defer t.Unlock() return t.listening } // Close terminates the listening on TrapListener socket func (t *TrapListener) Close() { if atomic.CompareAndSwapInt32(&t.finish, 0, 1) { t.Lock() defer t.Unlock() if t.conn == nil { return } if err := t.conn.Close(); err != nil { t.Params.Logger.Printf("failed to Close() the TrapListener socket: %s", err) } select { case <-t.done: case <-time.After(t.CloseTimeout): // A timeout can prevent blocking forever t.Params.Logger.Printf("timeout while awaiting done signal on TrapListener Close()") } } } // SendUDP sends a given SnmpPacket to the provided address using the currently opened connection. func (t *TrapListener) SendUDP(packet *SnmpPacket, addr *net.UDPAddr) error { ob, err := packet.marshalMsg() if err != nil { return fmt.Errorf("error marshaling SnmpPacket: %w", err) } // Send the return packet back. count, err := t.conn.WriteTo(ob, addr) if err != nil { return fmt.Errorf("error sending SnmpPacket: %w", err) } // This isn't fatal, but should be logged. if count != len(ob) { t.Params.Logger.Printf("Failed to send all bytes of SnmpPacket!\n") } return nil } func (t *TrapListener) listenUDP(addr string) error { // udp udpAddr, err := net.ResolveUDPAddr(t.proto, addr) if err != nil { return err } t.conn, err = net.ListenUDP(udp, udpAddr) if err != nil { return err } defer t.conn.Close() // Mark that we are listening now. t.listening <- true for { switch { case atomic.LoadInt32(&t.finish) == 1: t.done <- true return nil default: var buf [4096]byte rlen, remote, err := t.conn.ReadFromUDP(buf[:]) if err != nil { if atomic.LoadInt32(&t.finish) == 1 { // err most likely comes from reading from a closed connection continue } t.Params.Logger.Printf("TrapListener: error in read %s\n", err) continue } msg := buf[:rlen] trap, err := t.Params.UnmarshalTrap(msg, false) if err != nil { t.Params.Logger.Printf("TrapListener: error in UnmarshalTrap %s\n", err) continue } if trap.Version == Version3 && trap.SecurityModel == UserSecurityModel && t.Params.SecurityModel == UserSecurityModel { securityParams, ok := t.Params.SecurityParameters.(*UsmSecurityParameters) if !ok { t.Params.Logger.Printf("TrapListener: Invalid SecurityParameters types") } packetSecurityParams, ok := trap.SecurityParameters.(*UsmSecurityParameters) if !ok { t.Params.Logger.Printf("TrapListener: Invalid SecurityParameters types") } snmpEngineID := securityParams.AuthoritativeEngineID msgAuthoritativeEngineID := packetSecurityParams.AuthoritativeEngineID if msgAuthoritativeEngineID != snmpEngineID { if len(msgAuthoritativeEngineID) < 5 || len(msgAuthoritativeEngineID) > 32 { // RFC3411 section 5. – SnmpEngineID definition. // SnmpEngineID is an OCTET STRING which size should be between 5 and 32 // According to RFC3414 3.2.3b: stop processing and report // the listener authoritative engine ID atomic.AddUint32(&t.usmStatsUnknownEngineIDsCount, 1) err := t.reportAuthoritativeEngineID(trap, snmpEngineID, remote) if err != nil { t.Params.Logger.Printf("TrapListener: %s\n", err) } continue } // RFC3414 3.2.3a: Continue processing } } // Here we assume that t.OnNewTrap will not alter the contents // of the PDU (per documentation, because Go does not have // compile-time const checking). We don't pass a copy because // the SnmpPacket type is somewhat large, but we could without // violating any implicit or explicit spec. t.OnNewTrap(trap, remote) // If it was an Inform request, we need to send a response. if trap.PDUType == InformRequest { //nolint:whitespace // Reuse the packet, since we're supposed to send it back // with the exact same variables unless there's an error. // Change the PDUType to the response, though. trap.PDUType = GetResponse // If the response can be sent, the error-status is // supposed to be set to noError and the error-index set to // zero. trap.Error = NoError trap.ErrorIndex = 0 // TODO: Check that the message marshalled is not too large // for the originator to accept and if so, send a tooBig // error PDU per RFC3416 section 4.2.7. This maximum size, // however, does not have a well-defined mechanism in the // RFC other than using the path MTU (which is difficult to // determine), so it's left to future implementations. err := t.SendUDP(trap, remote) if err != nil { t.Params.Logger.Printf("TrapListener: %s\n", err) } } } } } func (t *TrapListener) reportAuthoritativeEngineID(trap *SnmpPacket, snmpEngineID string, addr *net.UDPAddr) error { newSecurityParams, ok := trap.SecurityParameters.Copy().(*UsmSecurityParameters) if !ok { return errors.New("unable to cast SecurityParams to UsmSecurityParameters") } newSecurityParams.AuthoritativeEngineID = snmpEngineID reportPacket := trap reportPacket.PDUType = Report reportPacket.MsgFlags &= AuthPriv reportPacket.SecurityParameters = newSecurityParams reportPacket.Variables = []SnmpPDU{ { Name: usmStatsUnknownEngineIDs, Value: int(atomic.LoadUint32(&t.usmStatsUnknownEngineIDsCount)), Type: Integer, }, } return t.SendUDP(reportPacket, addr) } func (t *TrapListener) handleTCPRequest(conn net.Conn) { // Make a buffer to hold incoming data. buf := make([]byte, 4096) // Read the incoming connection into the buffer. reqLen, err := conn.Read(buf) if err != nil { t.Params.Logger.Printf("TrapListener: error in read %s\n", err) return } msg := buf[:reqLen] traps, err := t.Params.UnmarshalTrap(msg, false) if err != nil { t.Params.Logger.Printf("TrapListener: error in read %s\n", err) return } // TODO: lying for backward compatibility reason - create UDP Address ... not nice r, _ := net.ResolveUDPAddr("", conn.RemoteAddr().String()) t.OnNewTrap(traps, r) // Close the connection when you're done with it. conn.Close() } func (t *TrapListener) listenTCP(addr string) error { tcpAddr, err := net.ResolveTCPAddr(t.proto, addr) if err != nil { return err } l, err := net.ListenTCP(tcp, tcpAddr) if err != nil { return err } defer l.Close() // Mark that we are listening now. t.listening <- true for { switch { case atomic.LoadInt32(&t.finish) == 1: t.done <- true return nil default: // Listen for an incoming connection. conn, err := l.Accept() fmt.Printf("ACCEPT: %s", conn) if err != nil { fmt.Println("error accepting: ", err.Error()) return err } // Handle connections in a new goroutine. go t.handleTCPRequest(conn) } } } // Listen listens on the UDP address addr and calls the OnNewTrap // function specified in *TrapListener for every trap received. // // NOTE: the trap code is currently unreliable when working with snmpv3 - pull requests welcome func (t *TrapListener) Listen(addr string) error { if t.Params == nil { t.Params = Default } // TODO TODO returning an error cause the following to hang/break // TestSendTrapBasic // TestSendTrapWithoutWaitingOnListen // TestSendV1Trap _ = t.Params.validateParameters() if t.OnNewTrap == nil { t.OnNewTrap = t.debugTrapHandler } splitted := strings.SplitN(addr, "://", 2) t.proto = udp if len(splitted) > 1 { t.proto = splitted[0] addr = splitted[1] } switch t.proto { case tcp: return t.listenTCP(addr) case udp: return t.listenUDP(addr) default: return fmt.Errorf("not implemented network protocol: %s [use: tcp/udp]", t.proto) } } // Default trap handler func (t *TrapListener) debugTrapHandler(s *SnmpPacket, u *net.UDPAddr) { t.Params.Logger.Printf("got trapdata from %+v: %+v\n", u, s) } // UnmarshalTrap unpacks the SNMP Trap. func (x *GoSNMP) UnmarshalTrap(trap []byte, useResponseSecurityParameters bool) (result *SnmpPacket, err error) { // Get only the version from the header of the trap version, _, err := x.unmarshalVersionFromHeader(trap, new(SnmpPacket)) if err != nil { x.Logger.Printf("UnmarshalTrap version unmarshal: %s\n", err) return nil, err } // If there are multiple users configured and the SNMP trap is v3, see which user has valid credentials // by iterating through the list matching the identifier and seeing which credentials are authentic / can be used to decrypt if x.TrapSecurityParametersTable != nil && version == Version3 { identifier, err := x.getTrapIdentifier(trap) if err != nil { x.Logger.Printf("UnmarshalTrap V3 get trap identifier: %s\n", err) return nil, err } secParamsList, err := x.TrapSecurityParametersTable.Get(identifier) if err != nil { x.Logger.Printf("UnmarshalTrap V3 get security parameters from table: %s\n", err) return nil, err } for _, secParams := range secParamsList { // Copy the trap and pass the security parameters to try to unmarshal with cpTrap := make([]byte, len(trap)) copy(cpTrap, trap) if result, err = x.unmarshalTrapBase(cpTrap, secParams.Copy(), true); err == nil { return result, nil } } return nil, fmt.Errorf("no credentials successfully unmarshaled trap: %w", err) } return x.unmarshalTrapBase(trap, nil, useResponseSecurityParameters) } func (x *GoSNMP) getTrapIdentifier(trap []byte) (string, error) { // Initialize a packet with no auth/priv to unmarshal ID/key for security parameters to use packet := new(SnmpPacket) _, err := x.unmarshalHeader(trap, packet) // Return err if no identifier was able to be parsed after unmarshaling if err != nil && packet.SecurityParameters.getIdentifier() == "" { return "", err } return packet.SecurityParameters.getIdentifier(), nil } func (x *GoSNMP) unmarshalTrapBase(trap []byte, sp SnmpV3SecurityParameters, useResponseSecurityParameters bool) (*SnmpPacket, error) { result := new(SnmpPacket) if x.SecurityParameters != nil && sp == nil { err := x.SecurityParameters.InitSecurityKeys() if err != nil { return nil, err } result.SecurityParameters = x.SecurityParameters.Copy() } else { result.SecurityParameters = sp } cursor, err := x.unmarshalHeader(trap, result) if err != nil { x.Logger.Printf("UnmarshalTrap: %s\n", err) return nil, err } if result.Version == Version3 { if result.SecurityModel == UserSecurityModel { err = x.testAuthentication(trap, result, useResponseSecurityParameters) if err != nil { x.Logger.Printf("UnmarshalTrap v3 auth: %s\n", err) return nil, err } } trap, cursor, err = x.decryptPacket(trap, cursor, result) if err != nil { x.Logger.Printf("UnmarshalTrap v3 decrypt: %s\n", err) return nil, err } } err = x.unmarshalPayload(trap, cursor, result) if err != nil { x.Logger.Printf("UnmarshalTrap: %s\n", err) return nil, err } return result, nil } golang-github-gosnmp-gosnmp-1.38.0/trap.md000066400000000000000000000037611465470716300204500ustar00rootroot00000000000000# setup for working on traps ``` $ sudo aptitude -y install snmp-mibs-downloader snmp snmpd snmp-mibs-downloader ``` In the file `/etc/snmp/snmp.conf` ``` mibs +ALL ``` In the file `/etc/snmp/snmpd.conf` ``` comment out: agentAddress udp:127.0.0.1:161 uncomment: agentAddress udp:161,udp6:[::1]:161 comment out: rocommunity public default -V systemonly uncomment: rocommunity public 10.0.0.0/16 comment out: trapsink localhost public uncomment: trap2sink localhost public ``` Create the file `~/.snmp/snmp.conf` with the contents: ``` # ~ expansion fails persistentDir /home/sonia/.snmp_persist ``` ``` $ sudo /etc/init.d/snmpd restart ``` # test ``` 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" SNMPv2-MIB::sysObjectID o "1.3.6.1.4.1.2.3.4.5" ``` # tshark, wireshark ``` sudo aptitude -y install wireshark tshark sudo dpkg-reconfigure wireshark-common # allow captures sudo usermod -a -G wireshark sonia sudo setcap cap_net_raw,cap_net_admin=eip /usr/bin/dumpcap sudo getcap /usr/bin/dumpcap # still 'Couldn't run /usr/bin/dumpcap in child process', so nuke it sudo chmod 777 /usr/bin/dumpcap ``` Logout, login to apply wireshark and tshark permissions In a second terminal, run: ``` tshark -i eth0 -f "port 161" -w trap.pcap ``` # snmptrap and MIBs ``` The TYPE is a single character, one of: i INTEGER INTEGER u UNSIGNED c COUNTER32 s STRING DisplayString x HEX STRING d DECIMAL STRING n NULLOBJ o OBJID OBJECT IDENTIFIER t TIMETICKS a IPADDRESS b BITS ``` # finding MIBs Look in the file `/usr/share/mibs/ietf/SNMPv2-MIB`. Here are some example lines: ``` line:77 sysDescr line:88 sysObjectID line:146 sysServices ``` For a gui MIB browser: https://l3net.wordpress.com/2013/05/12/installing-net-snmp-mibs-on-ubuntu-and-debian/ golang-github-gosnmp-gosnmp-1.38.0/trap_test.go000066400000000000000000001057101465470716300215110ustar00rootroot00000000000000// 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 || trap // +build all trap package gosnmp import ( "io" "log" "net" "reflect" "testing" "time" "github.com/stretchr/testify/require" ) const ( trapTestAddress = "127.0.0.1" // TODO this is bad. Listen and Connect expect different address formats // so we need an int version and a string version - they should be the same. trapTestPort = 9162 trapTestPortString = "9162" trapTestOid = ".1.2.1234.4.5" trapTestPayload = "TRAPTEST1234" trapTestEnterpriseOid = ".1.2.1234" trapTestAgentAddress = "127.0.0.1" trapTestGenericTrap = 6 trapTestSpecificTrap = 55 trapTestTimestamp = 300 ) var secParamsList = []*UsmSecurityParameters{ &UsmSecurityParameters{ UserName: "myuser", AuthenticationProtocol: MD5, AuthenticationPassphrase: "mypassword", Logger: NewLogger(log.New(io.Discard, "", 0)), }, &UsmSecurityParameters{ UserName: "myuser1", AuthenticationProtocol: MD5, AuthenticationPassphrase: "mypassword1", PrivacyProtocol: AES, PrivacyPassphrase: "myprivacy1", Logger: NewLogger(log.New(io.Discard, "", 0)), }, &UsmSecurityParameters{ UserName: "myuser2", AuthenticationProtocol: SHA, AuthenticationPassphrase: "mypassword2", PrivacyProtocol: DES, PrivacyPassphrase: "myprivacy2", Logger: NewLogger(log.New(io.Discard, "", 0)), }, &UsmSecurityParameters{ UserName: "myuser2", AuthenticationProtocol: MD5, AuthenticationPassphrase: "mypassword2", PrivacyProtocol: AES, PrivacyPassphrase: "myprivacy2", Logger: NewLogger(log.New(io.Discard, "", 0)), }, } var testsUnmarshalTrap = []struct { in func() []byte out *SnmpPacket }{ {genericV3Trap, &SnmpPacket{ Version: Version3, PDUType: SNMPv2Trap, RequestID: 957979745, MsgFlags: AuthNoPriv, SecurityParameters: &UsmSecurityParameters{ UserName: "myuser", AuthenticationProtocol: MD5, AuthenticationPassphrase: "mypassword", Logger: NewLogger(log.New(io.Discard, "", 0)), }, }, }, { snmpV3AuthPrivTrap, &SnmpPacket{ Version: 3, PDUType: SNMPv2Trap, RequestID: 1318065890, MsgFlags: AuthPriv, SecurityParameters: &UsmSecurityParameters{ UserName: "myuser2", AuthenticationProtocol: MD5, AuthenticationPassphrase: "mypassword2", PrivacyProtocol: AES, PrivacyPassphrase: "myprivacy2", Logger: NewLogger(log.New(io.Discard, "", 0)), }, }, }, } func TestUnmarshalTrap(t *testing.T) { Default.Logger = NewLogger(log.New(io.Discard, "", 0)) SANITY: for i, test := range testsUnmarshalTrap { Default.SecurityParameters = test.out.SecurityParameters.Copy() Default.Version = Version3 var buf = test.in() res, err := Default.UnmarshalTrap(buf, true) require.NoError(t, err, "unmarshalTrap failed") if res == nil { t.Errorf("#%d, UnmarshalTrap returned nil", i) continue SANITY } // test enough fields to ensure unmarshalling was successful. // full unmarshal testing is performed in TestUnmarshal if res.Version != test.out.Version { t.Errorf("#%d Version result: %v, test: %v", i, res.Version, test.out.Version) } if res.RequestID != test.out.RequestID { t.Errorf("#%d RequestID result: %v, test: %v", i, res.RequestID, test.out.RequestID) } } } func TestUnmarshalTrapWithMultipleUsers(t *testing.T) { Default.Logger = NewLogger(log.New(io.Discard, "", 0)) usmMap := NewSnmpV3SecurityParametersTable(NewLogger(log.New(io.Discard, "", 0))) for _, sp := range secParamsList { usmMap.Add(sp.UserName, sp) } SANITY: for i, test := range testsUnmarshalTrap { Default.TrapSecurityParametersTable = usmMap Default.Version = Version3 var buf = test.in() res, err := Default.UnmarshalTrap(buf, true) require.NoError(t, err, "unmarshalTrap failed") if res == nil { t.Errorf("#%d, UnmarshalTrap returned nil", i) continue SANITY } // test enough fields to ensure unmarshalling was successful. // full unmarshal testing is performed in TestUnmarshal require.Equal(t, test.out.Version, res.Version) require.Equal(t, test.out.RequestID, res.RequestID) Default.TrapSecurityParametersTable = nil } } func genericV3Trap() []byte { return []byte{ 0x30, 0x81, 0xd7, 0x02, 0x01, 0x03, 0x30, 0x11, 0x02, 0x04, 0x62, 0xaf, 0x5a, 0x8e, 0x02, 0x03, 0x00, 0xff, 0xe3, 0x04, 0x01, 0x01, 0x02, 0x01, 0x03, 0x04, 0x33, 0x30, 0x31, 0x04, 0x11, 0x80, 0x00, 0x1f, 0x88, 0x80, 0x77, 0xdf, 0xe4, 0x4f, 0xaa, 0x70, 0x02, 0x58, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x0f, 0x02, 0x01, 0x00, 0x04, 0x06, 0x6d, 0x79, 0x75, 0x73, 0x65, 0x72, 0x04, 0x0c, 0xd8, 0xb6, 0x9c, 0xb8, 0x22, 0x91, 0xfc, 0x65, 0xb6, 0x84, 0xcb, 0xfe, 0x04, 0x00, 0x30, 0x81, 0x89, 0x04, 0x11, 0x80, 0x00, 0x1f, 0x88, 0x80, 0x77, 0xdf, 0xe4, 0x4f, 0xaa, 0x70, 0x02, 0x58, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xa7, 0x72, 0x02, 0x04, 0x39, 0x19, 0x9c, 0x61, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x30, 0x64, 0x30, 0x0f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x02, 0x01, 0x01, 0x03, 0x00, 0x43, 0x03, 0x15, 0x2f, 0xec, 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} } /* snmptrap -v3 -l authPriv -u myuser2 -a MD5 -A mypassword2 -x AES -X myprivacy2 127.0.0.1:9162 ” 1.3.6.1.4.1.8072.2.3.0.1 1.3.6.1.4.1.8072.2.3.2.1 i 60 */ func snmpV3AuthPrivTrap() []byte { return []byte{ 0x30, 0x81, 0xbb, 0x02, 0x01, 0x03, 0x30, 0x11, 0x02, 0x04, 0x3a, 0x1c, 0xf4, 0xf7, 0x02, 0x03, 0x00, 0xff, 0xe3, 0x04, 0x01, 0x03, 0x02, 0x01, 0x03, 0x04, 0x3c, 0x30, 0x3a, 0x04, 0x11, 0x80, 0x00, 0x1f, 0x88, 0x80, 0x6b, 0x8f, 0xad, 0x3b, 0x07, 0xc2, 0x70, 0x65, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x01, 0x02, 0x01, 0x00, 0x04, 0x07, 0x6d, 0x79, 0x75, 0x73, 0x65, 0x72, 0x32, 0x04, 0x0c, 0xa8, 0xe2, 0xf4, 0xab, 0x3c, 0xd5, 0x9c, 0x22, 0x5e, 0x0a, 0x12, 0xdd, 0x04, 0x08, 0x95, 0x7b, 0xdc, 0x33, 0x6a, 0xf4, 0x3c, 0x8f, 0x04, 0x65, 0x70, 0x64, 0xbd, 0xcf, 0x4b, 0xa8, 0x19, 0xda, 0xf4, 0x0d, 0x09, 0x8f, 0x7a, 0x28, 0xa6, 0x82, 0x00, 0xe0, 0xbd, 0x96, 0x76, 0xf8, 0xc2, 0xa3, 0xe3, 0xb0, 0x92, 0x00, 0x82, 0x2d, 0xba, 0xce, 0x34, 0x2f, 0x53, 0x19, 0x18, 0xba, 0xfc, 0xe5, 0xf5, 0x0e, 0x9a, 0xba, 0x52, 0xaf, 0x6b, 0x67, 0xaa, 0x20, 0x23, 0xb5, 0x17, 0x04, 0x7e, 0x17, 0x08, 0xb8, 0xc6, 0x67, 0x14, 0xb5, 0x91, 0x4d, 0x6b, 0xd8, 0xbf, 0x94, 0x24, 0x22, 0x0f, 0x21, 0x4f, 0xde, 0x6f, 0x41, 0x51, 0xa6, 0x10, 0x86, 0xf2, 0x01, 0xd1, 0xd6, 0xa9, 0x3c, 0x88, 0xea, 0x41, 0x25, 0x25, 0xbc, 0x12, 0x12, 0xa6, 0xd6, 0x8f, 0x55, 0x6a, 0x55, 0xcb} } func makeTestTrapHandler(t *testing.T, done chan int, version SnmpVersion) func(*SnmpPacket, *net.UDPAddr) { Default.Logger = NewLogger(log.New(io.Discard, "", 0)) return func(packet *SnmpPacket, addr *net.UDPAddr) { //log.Printf("got trapdata from %s\n", addr.IP) defer close(done) if version == Version1 { if packet.Enterprise != trapTestEnterpriseOid { t.Fatalf("incorrect trap Enterprise OID received, expected %s got %s", trapTestEnterpriseOid, packet.Enterprise) } if packet.AgentAddress != trapTestAgentAddress { t.Fatalf("incorrect trap Agent Address received, expected %s got %s", trapTestAgentAddress, packet.AgentAddress) } if packet.GenericTrap != trapTestGenericTrap { t.Fatalf("incorrect trap Generic Trap identifier received, expected %v got %v", trapTestGenericTrap, packet.GenericTrap) } if packet.SpecificTrap != trapTestSpecificTrap { t.Fatalf("incorrect trap Specific Trap identifier received, expected %v got %v", trapTestSpecificTrap, packet.SpecificTrap) } if packet.Timestamp != trapTestTimestamp { t.Fatalf("incorrect trap Timestamp received, expected %v got %v", trapTestTimestamp, packet.Timestamp) } } for _, v := range packet.Variables { switch v.Type { case OctetString: b := v.Value.([]byte) // log.Printf("OID: %s, string: %x\n", v.Name, b) // Only one OctetString in the payload, so it must be the expected one if v.Name != trapTestOid { t.Fatalf("incorrect trap OID received, expected %s got %s", trapTestOid, v.Name) } if string(b) != trapTestPayload { t.Fatalf("incorrect trap payload received, expected %s got %x", trapTestPayload, b) } default: // log.Printf("trap: %+v\n", v) } } } } // TODO: This restores global state set by other tests so that these tests can // run. Tests should be avoiding use of global state where possible (and, if // possible, use of global state other than possibly loggers should be // eliminated entirely). func TestRestoreGlobals(t *testing.T) { Default.Version = Version2c Default.SecurityModel = 0 Default.SecurityParameters = nil } // test sending a basic SNMP trap, using our own listener to receive func TestSendTrapBasic(t *testing.T) { done := make(chan int) tl := NewTrapListener() defer tl.Close() tl.OnNewTrap = makeTestTrapHandler(t, done, Version2c) tl.Params = Default // listener goroutine errch := make(chan error) go func() { // defer close(errch) err := tl.Listen(net.JoinHostPort(trapTestAddress, trapTestPortString)) if err != nil { errch <- err } }() // Wait until the listener is ready. select { case <-tl.Listening(): case err := <-errch: t.Fatalf("error in listen: %v", err) } ts := &GoSNMP{ Target: trapTestAddress, Port: trapTestPort, Community: "public", Version: Version2c, Timeout: time.Duration(2) * time.Second, Retries: 3, MaxOids: MaxOids, Logger: NewLogger(log.New(io.Discard, "", 0)), } err := ts.Connect() if err != nil { t.Fatalf("Connect() err: %v", err) } defer ts.Conn.Close() pdu := SnmpPDU{ Name: trapTestOid, Type: OctetString, Value: trapTestPayload, } trap := SnmpTrap{ Variables: []SnmpPDU{pdu}, } _, err = ts.SendTrap(trap) if err != nil { t.Fatalf("SendTrap() err: %v", err) } // wait for response from handler select { case <-done: case <-time.After(2 * time.Second): t.Fatal("timed out waiting for trap to be received") } } // test sending a basic SNMP inform and receiving the response func TestSendInformBasic(t *testing.T) { done := make(chan int) tl := NewTrapListener() defer tl.Close() tl.OnNewTrap = makeTestTrapHandler(t, done, Version2c) tl.Params = Default // listener goroutine errch := make(chan error) go func() { // defer close(errch) err := tl.Listen(net.JoinHostPort(trapTestAddress, trapTestPortString)) if err != nil { errch <- err } }() // Wait until the listener is ready. select { case <-tl.Listening(): case err := <-errch: t.Fatalf("error in listen: %v", err) } ts := &GoSNMP{ Target: trapTestAddress, Port: trapTestPort, Community: "public", Version: Version2c, Timeout: time.Duration(2) * time.Second, Retries: 3, MaxOids: MaxOids, } err := ts.Connect() if err != nil { t.Fatalf("Connect() err: %v", err) } defer ts.Conn.Close() pdu := SnmpPDU{ Name: trapTestOid, Type: OctetString, Value: trapTestPayload, } // Make it an inform. trap := SnmpTrap{ Variables: []SnmpPDU{pdu}, IsInform: true, } var resp *SnmpPacket resp, err = ts.SendTrap(trap) if err != nil { t.Fatalf("SendTrap() err: %v", err) } // wait for response from handler select { case <-done: case <-time.After(2 * time.Second): t.Fatal("timed out waiting for trap to be received") } if resp.PDUType != GetResponse { t.Fatal("Inform response is not a response PDU") } for i, tv := range trap.Variables { rv := resp.Variables[i+1] if reflect.DeepEqual(tv, rv) { t.Fatalf("Expected variable %d = %#v, got %#v", i, tv, rv) } } } // test the listener is not blocked if Listening is not used func TestSendTrapWithoutWaitingOnListen(t *testing.T) { done := make(chan int) tl := NewTrapListener() defer tl.Close() tl.OnNewTrap = makeTestTrapHandler(t, done, Version2c) tl.Params = Default errch := make(chan error) listening := make(chan bool) go func() { // Reduce the chance of necessity for a restart. listening <- true err := tl.Listen(net.JoinHostPort(trapTestAddress, trapTestPortString)) if err != nil { errch <- err } }() select { case <-listening: case err := <-errch: t.Fatalf("error in listen: %v", err) } ts := &GoSNMP{ Target: trapTestAddress, Port: trapTestPort, Community: "public", Version: Version2c, Timeout: time.Duration(2) * time.Second, Retries: 3, MaxOids: MaxOids, } err := ts.Connect() if err != nil { t.Fatalf("Connect() err: %v", err) } defer ts.Conn.Close() pdu := SnmpPDU{ Name: trapTestOid, Type: OctetString, Value: trapTestPayload, } trap := SnmpTrap{ Variables: []SnmpPDU{pdu}, } _, err = ts.SendTrap(trap) if err != nil { t.Fatalf("SendTrap() err: %v", err) } // Wait for a response from the handler and restart the SendTrap // if the listener wasn't ready. select { case <-done: case <-time.After(2 * time.Second): _, err = ts.SendTrap(trap) if err != nil { t.Fatalf("restarted SendTrap() err: %v", err) } t.Log("restarted") select { case <-done: case <-time.After(2 * time.Second): t.Fatal("timed out waiting for trap to be received") } } } // test sending a basic SNMP trap, using our own listener to receive func TestSendV1Trap(t *testing.T) { done := make(chan int) tl := NewTrapListener() defer tl.Close() tl.OnNewTrap = makeTestTrapHandler(t, done, Version1) tl.Params = Default // listener goroutine errch := make(chan error) go func() { err := tl.Listen(net.JoinHostPort(trapTestAddress, trapTestPortString)) if err != nil { errch <- err } }() // Wait until the listener is ready. select { case <-tl.Listening(): case err := <-errch: t.Fatalf("error in listen: %v", err) } ts := &GoSNMP{ Target: trapTestAddress, Port: trapTestPort, //Community: "public", Version: Version1, Timeout: time.Duration(2) * time.Second, Retries: 3, MaxOids: MaxOids, } err := ts.Connect() if err != nil { t.Fatalf("Connect() err: %v", err) } defer ts.Conn.Close() pdu := SnmpPDU{ Name: trapTestOid, Type: OctetString, Value: trapTestPayload, } trap := SnmpTrap{ Variables: []SnmpPDU{pdu}, Enterprise: trapTestEnterpriseOid, AgentAddress: trapTestAgentAddress, GenericTrap: trapTestGenericTrap, SpecificTrap: trapTestSpecificTrap, Timestamp: trapTestTimestamp, } _, err = ts.SendTrap(trap) if err != nil { t.Fatalf("SendTrap() err: %v", err) } // wait for response from handler select { case <-done: case <-time.After(2 * time.Second): t.Fatal("timed out waiting for trap to be received") } } func TestSendV3TrapNoAuthNoPriv(t *testing.T) { done := make(chan int) tl := NewTrapListener() defer tl.Close() sp := &UsmSecurityParameters{ UserName: "test", AuthoritativeEngineBoots: 1, AuthoritativeEngineTime: 1, AuthoritativeEngineID: string([]byte{0x80, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04}), } tl.OnNewTrap = makeTestTrapHandler(t, done, Version3) tl.Params = Default tl.Params.Version = Version3 tl.Params.SecurityParameters = sp tl.Params.SecurityModel = UserSecurityModel tl.Params.MsgFlags = NoAuthNoPriv // listener goroutine errch := make(chan error) go func() { err := tl.Listen(net.JoinHostPort(trapTestAddress, trapTestPortString)) if err != nil { errch <- err } }() // Wait until the listener is ready. select { case <-tl.Listening(): case err := <-errch: t.Fatalf("error in listen: %v", err) } ts := &GoSNMP{ Target: trapTestAddress, Port: trapTestPort, //Community: "public", Version: Version3, Timeout: time.Duration(2) * time.Second, Retries: 3, MaxOids: MaxOids, SecurityModel: UserSecurityModel, SecurityParameters: sp, MsgFlags: NoAuthNoPriv, } err := ts.Connect() if err != nil { t.Fatalf("Connect() err: %v", err) } defer ts.Conn.Close() pdu := SnmpPDU{ Name: trapTestOid, Type: OctetString, Value: trapTestPayload, } trap := SnmpTrap{ Variables: []SnmpPDU{pdu}, Enterprise: trapTestEnterpriseOid, AgentAddress: trapTestAgentAddress, GenericTrap: trapTestGenericTrap, SpecificTrap: trapTestSpecificTrap, Timestamp: trapTestTimestamp, } _, err = ts.SendTrap(trap) if err != nil { t.Fatalf("SendTrap() err: %v", err) } // wait for response from handler select { case <-done: case <-time.After(2 * time.Second): t.Fatal("timed out waiting for trap to be received") } } func TestSendV3TrapMD5AuthNoPriv(t *testing.T) { done := make(chan int) tl := NewTrapListener() defer tl.Close() sp := &UsmSecurityParameters{ UserName: "test", AuthenticationProtocol: MD5, AuthenticationPassphrase: "password", AuthoritativeEngineBoots: 1, AuthoritativeEngineTime: 1, AuthoritativeEngineID: string([]byte{0x80, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04}), } tl.OnNewTrap = makeTestTrapHandler(t, done, Version3) tl.Params = Default tl.Params.Version = Version3 tl.Params.SecurityParameters = sp tl.Params.SecurityModel = UserSecurityModel tl.Params.MsgFlags = AuthNoPriv // listener goroutine errch := make(chan error) go func() { err := tl.Listen(net.JoinHostPort(trapTestAddress, trapTestPortString)) if err != nil { errch <- err } }() // Wait until the listener is ready. select { case <-tl.Listening(): case err := <-errch: t.Fatalf("error in listen: %v", err) } ts := &GoSNMP{ Target: trapTestAddress, Port: trapTestPort, //Community: "public", Version: Version3, Timeout: time.Duration(2) * time.Second, Retries: 3, MaxOids: MaxOids, SecurityModel: UserSecurityModel, SecurityParameters: sp, MsgFlags: AuthNoPriv, } err := ts.Connect() if err != nil { t.Fatalf("Connect() err: %v", err) } defer ts.Conn.Close() pdu := SnmpPDU{ Name: trapTestOid, Type: OctetString, Value: trapTestPayload, } trap := SnmpTrap{ Variables: []SnmpPDU{pdu}, Enterprise: trapTestEnterpriseOid, AgentAddress: trapTestAgentAddress, GenericTrap: trapTestGenericTrap, SpecificTrap: trapTestSpecificTrap, Timestamp: trapTestTimestamp, } _, err = ts.SendTrap(trap) if err != nil { t.Fatalf("SendTrap() err: %v", err) } // wait for response from handler select { case <-done: case <-time.After(2 * time.Second): t.Fatal("timed out waiting for trap to be received") } } func TestSendV3TrapSHAAuthNoPriv(t *testing.T) { done := make(chan int) tl := NewTrapListener() defer tl.Close() sp := &UsmSecurityParameters{ UserName: "test", AuthenticationProtocol: SHA, AuthenticationPassphrase: "password", AuthoritativeEngineBoots: 1, AuthoritativeEngineTime: 1, AuthoritativeEngineID: string([]byte{0x80, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04}), } tl.OnNewTrap = makeTestTrapHandler(t, done, Version3) tl.Params = Default tl.Params.Version = Version3 tl.Params.SecurityParameters = sp tl.Params.SecurityModel = UserSecurityModel tl.Params.MsgFlags = AuthNoPriv // listener goroutine errch := make(chan error) go func() { err := tl.Listen(net.JoinHostPort(trapTestAddress, trapTestPortString)) if err != nil { errch <- err } }() // Wait until the listener is ready. select { case <-tl.Listening(): case err := <-errch: t.Fatalf("error in listen: %v", err) } ts := &GoSNMP{ Target: trapTestAddress, Port: trapTestPort, //Community: "public", Version: Version3, Timeout: time.Duration(2) * time.Second, Retries: 3, MaxOids: MaxOids, SecurityModel: UserSecurityModel, SecurityParameters: sp, MsgFlags: AuthNoPriv, } err := ts.Connect() if err != nil { t.Fatalf("Connect() err: %v", err) } defer ts.Conn.Close() pdu := SnmpPDU{ Name: trapTestOid, Type: OctetString, Value: trapTestPayload, } trap := SnmpTrap{ Variables: []SnmpPDU{pdu}, Enterprise: trapTestEnterpriseOid, AgentAddress: trapTestAgentAddress, GenericTrap: trapTestGenericTrap, SpecificTrap: trapTestSpecificTrap, Timestamp: trapTestTimestamp, } _, err = ts.SendTrap(trap) if err != nil { t.Fatalf("SendTrap() err: %v", err) } // wait for response from handler select { case <-done: case <-time.After(2 * time.Second): t.Fatal("timed out waiting for trap to be received") } } func TestSendV3TrapSHAAuthDESPriv(t *testing.T) { done := make(chan int) tl := NewTrapListener() defer tl.Close() sp := &UsmSecurityParameters{ UserName: "test", AuthenticationProtocol: SHA, AuthenticationPassphrase: "password", PrivacyProtocol: DES, PrivacyPassphrase: "password", AuthoritativeEngineBoots: 1, AuthoritativeEngineTime: 1, AuthoritativeEngineID: string([]byte{0x80, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04}), } tl.OnNewTrap = makeTestTrapHandler(t, done, Version3) tl.Params = Default tl.Params.Version = Version3 tl.Params.SecurityParameters = sp tl.Params.SecurityModel = UserSecurityModel tl.Params.MsgFlags = AuthPriv // listener goroutine errch := make(chan error) go func() { err := tl.Listen(net.JoinHostPort(trapTestAddress, trapTestPortString)) if err != nil { errch <- err } }() // Wait until the listener is ready. select { case <-tl.Listening(): case err := <-errch: t.Fatalf("error in listen: %v", err) } ts := &GoSNMP{ Target: trapTestAddress, Port: trapTestPort, //Community: "public", Version: Version3, Timeout: time.Duration(2) * time.Second, Retries: 3, MaxOids: MaxOids, SecurityModel: UserSecurityModel, SecurityParameters: sp, MsgFlags: AuthPriv, } err := ts.Connect() if err != nil { t.Fatalf("Connect() err: %v", err) } defer ts.Conn.Close() pdu := SnmpPDU{ Name: trapTestOid, Type: OctetString, Value: trapTestPayload, } trap := SnmpTrap{ Variables: []SnmpPDU{pdu}, Enterprise: trapTestEnterpriseOid, AgentAddress: trapTestAgentAddress, GenericTrap: trapTestGenericTrap, SpecificTrap: trapTestSpecificTrap, Timestamp: trapTestTimestamp, } _, err = ts.SendTrap(trap) if err != nil { t.Fatalf("SendTrap() err: %v", err) } // wait for response from handler select { case <-done: case <-time.After(2 * time.Second): t.Fatal("timed out waiting for trap to be received") } } func TestSendV3TrapSHAAuthAESPriv(t *testing.T) { done := make(chan int) tl := NewTrapListener() defer tl.Close() sp := &UsmSecurityParameters{ UserName: "test", AuthenticationProtocol: SHA, AuthenticationPassphrase: "password", PrivacyProtocol: AES, PrivacyPassphrase: "password", AuthoritativeEngineBoots: 1, AuthoritativeEngineTime: 1, AuthoritativeEngineID: string([]byte{0x80, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04}), } tl.OnNewTrap = makeTestTrapHandler(t, done, Version3) tl.Params = Default tl.Params.Version = Version3 tl.Params.SecurityParameters = sp tl.Params.SecurityModel = UserSecurityModel tl.Params.MsgFlags = AuthPriv // listener goroutine errch := make(chan error) go func() { err := tl.Listen(net.JoinHostPort(trapTestAddress, trapTestPortString)) if err != nil { errch <- err } }() // Wait until the listener is ready. select { case <-tl.Listening(): case err := <-errch: t.Fatalf("error in listen: %v", err) } ts := &GoSNMP{ Target: trapTestAddress, Port: trapTestPort, //Community: "public", Version: Version3, Timeout: time.Duration(2) * time.Second, Retries: 3, MaxOids: MaxOids, SecurityModel: UserSecurityModel, SecurityParameters: sp, MsgFlags: AuthPriv, } err := ts.Connect() if err != nil { t.Fatalf("Connect() err: %v", err) } defer ts.Conn.Close() pdu := SnmpPDU{ Name: trapTestOid, Type: OctetString, Value: trapTestPayload, } trap := SnmpTrap{ Variables: []SnmpPDU{pdu}, Enterprise: trapTestEnterpriseOid, AgentAddress: trapTestAgentAddress, GenericTrap: trapTestGenericTrap, SpecificTrap: trapTestSpecificTrap, Timestamp: trapTestTimestamp, } _, err = ts.SendTrap(trap) if err != nil { t.Fatalf("SendTrap() err: %v", err) } // wait for response from handler select { case <-done: case <-time.After(2 * time.Second): t.Fatal("timed out waiting for trap to be received") } } func TestSendV3TrapSHAAuthAES192Priv(t *testing.T) { done := make(chan int) tl := NewTrapListener() defer tl.Close() sp := &UsmSecurityParameters{ UserName: "test", AuthenticationProtocol: SHA, AuthenticationPassphrase: "password", PrivacyProtocol: AES192, PrivacyPassphrase: "password", AuthoritativeEngineBoots: 1, AuthoritativeEngineTime: 1, AuthoritativeEngineID: string([]byte{0x80, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04}), } tl.OnNewTrap = makeTestTrapHandler(t, done, Version3) tl.Params = Default tl.Params.Version = Version3 tl.Params.SecurityParameters = sp tl.Params.SecurityModel = UserSecurityModel tl.Params.MsgFlags = AuthPriv // listener goroutine errch := make(chan error) go func() { err := tl.Listen(net.JoinHostPort(trapTestAddress, trapTestPortString)) if err != nil { errch <- err } }() // Wait until the listener is ready. select { case <-tl.Listening(): case err := <-errch: t.Fatalf("error in listen: %v", err) } ts := &GoSNMP{ Target: trapTestAddress, Port: trapTestPort, //Community: "public", Version: Version3, Timeout: time.Duration(2) * time.Second, Retries: 3, MaxOids: MaxOids, SecurityModel: UserSecurityModel, SecurityParameters: sp, MsgFlags: AuthPriv, } err := ts.Connect() if err != nil { t.Fatalf("Connect() err: %v", err) } defer ts.Conn.Close() pdu := SnmpPDU{ Name: trapTestOid, Type: OctetString, Value: trapTestPayload, } trap := SnmpTrap{ Variables: []SnmpPDU{pdu}, Enterprise: trapTestEnterpriseOid, AgentAddress: trapTestAgentAddress, GenericTrap: trapTestGenericTrap, SpecificTrap: trapTestSpecificTrap, Timestamp: trapTestTimestamp, } _, err = ts.SendTrap(trap) if err != nil { t.Fatalf("SendTrap() err: %v", err) } // wait for response from handler select { case <-done: case <-time.After(2 * time.Second): t.Fatal("timed out waiting for trap to be received") } } func TestSendV3TrapSHAAuthAES192CPriv(t *testing.T) { done := make(chan int) tl := NewTrapListener() defer tl.Close() sp := &UsmSecurityParameters{ UserName: "test", AuthenticationProtocol: SHA, AuthenticationPassphrase: "password", PrivacyProtocol: AES192C, PrivacyPassphrase: "password", AuthoritativeEngineBoots: 1, AuthoritativeEngineTime: 1, AuthoritativeEngineID: string([]byte{0x80, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04}), } tl.OnNewTrap = makeTestTrapHandler(t, done, Version3) tl.Params = Default tl.Params.Version = Version3 tl.Params.SecurityParameters = sp tl.Params.SecurityModel = UserSecurityModel tl.Params.MsgFlags = AuthPriv // listener goroutine errch := make(chan error) go func() { err := tl.Listen(net.JoinHostPort(trapTestAddress, trapTestPortString)) if err != nil { errch <- err } }() // Wait until the listener is ready. select { case <-tl.Listening(): case err := <-errch: t.Fatalf("error in listen: %v", err) } ts := &GoSNMP{ Target: trapTestAddress, Port: trapTestPort, //Community: "public", Version: Version3, Timeout: time.Duration(2) * time.Second, Retries: 3, MaxOids: MaxOids, SecurityModel: UserSecurityModel, SecurityParameters: sp, MsgFlags: AuthPriv, } err := ts.Connect() if err != nil { t.Fatalf("Connect() err: %v", err) } defer ts.Conn.Close() pdu := SnmpPDU{ Name: trapTestOid, Type: OctetString, Value: trapTestPayload, } trap := SnmpTrap{ Variables: []SnmpPDU{pdu}, Enterprise: trapTestEnterpriseOid, AgentAddress: trapTestAgentAddress, GenericTrap: trapTestGenericTrap, SpecificTrap: trapTestSpecificTrap, Timestamp: trapTestTimestamp, } _, err = ts.SendTrap(trap) if err != nil { t.Fatalf("SendTrap() err: %v", err) } // wait for response from handler select { case <-done: case <-time.After(2 * time.Second): t.Fatal("timed out waiting for trap to be received") } } func TestSendV3TrapSHAAuthAES256Priv(t *testing.T) { done := make(chan int) tl := NewTrapListener() defer tl.Close() sp := &UsmSecurityParameters{ UserName: "test", AuthenticationProtocol: SHA, AuthenticationPassphrase: "password", PrivacyProtocol: AES256, PrivacyPassphrase: "password", AuthoritativeEngineBoots: 1, AuthoritativeEngineTime: 1, AuthoritativeEngineID: string([]byte{0x80, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04}), } tl.OnNewTrap = makeTestTrapHandler(t, done, Version3) tl.Params = Default tl.Params.Version = Version3 tl.Params.SecurityParameters = sp tl.Params.SecurityModel = UserSecurityModel tl.Params.MsgFlags = AuthPriv // listener goroutine errch := make(chan error) go func() { err := tl.Listen(net.JoinHostPort(trapTestAddress, trapTestPortString)) if err != nil { errch <- err } }() // Wait until the listener is ready. select { case <-tl.Listening(): case err := <-errch: t.Fatalf("error in listen: %v", err) } ts := &GoSNMP{ Target: trapTestAddress, Port: trapTestPort, //Community: "public", Version: Version3, Timeout: time.Duration(2) * time.Second, Retries: 3, MaxOids: MaxOids, SecurityModel: UserSecurityModel, SecurityParameters: sp, MsgFlags: AuthPriv, } err := ts.Connect() if err != nil { t.Fatalf("Connect() err: %v", err) } defer ts.Conn.Close() pdu := SnmpPDU{ Name: trapTestOid, Type: OctetString, Value: trapTestPayload, } trap := SnmpTrap{ Variables: []SnmpPDU{pdu}, Enterprise: trapTestEnterpriseOid, AgentAddress: trapTestAgentAddress, GenericTrap: trapTestGenericTrap, SpecificTrap: trapTestSpecificTrap, Timestamp: trapTestTimestamp, } _, err = ts.SendTrap(trap) if err != nil { t.Fatalf("SendTrap() err: %v", err) } // wait for response from handler select { case <-done: case <-time.After(2 * time.Second): t.Fatal("timed out waiting for trap to be received") } } func TestSendV3TrapSHAAuthAES256CPriv(t *testing.T) { done := make(chan int) tl := NewTrapListener() defer tl.Close() sp := &UsmSecurityParameters{ UserName: "test", AuthenticationProtocol: SHA, AuthenticationPassphrase: "password", PrivacyProtocol: AES256C, PrivacyPassphrase: "password", AuthoritativeEngineBoots: 1, AuthoritativeEngineTime: 1, AuthoritativeEngineID: string([]byte{0x80, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04}), } tl.OnNewTrap = makeTestTrapHandler(t, done, Version3) tl.Params = Default tl.Params.Version = Version3 tl.Params.SecurityParameters = sp tl.Params.SecurityModel = UserSecurityModel tl.Params.MsgFlags = AuthPriv // listener goroutine errch := make(chan error) go func() { err := tl.Listen(net.JoinHostPort(trapTestAddress, trapTestPortString)) if err != nil { errch <- err } }() // Wait until the listener is ready. select { case <-tl.Listening(): case err := <-errch: t.Fatalf("error in listen: %v", err) } ts := &GoSNMP{ Target: trapTestAddress, Port: trapTestPort, //Community: "public", Version: Version3, Timeout: time.Duration(2) * time.Second, Retries: 3, MaxOids: MaxOids, SecurityModel: UserSecurityModel, SecurityParameters: sp, MsgFlags: AuthPriv, } err := ts.Connect() if err != nil { t.Fatalf("Connect() err: %v", err) } defer ts.Conn.Close() pdu := SnmpPDU{ Name: trapTestOid, Type: OctetString, Value: trapTestPayload, } trap := SnmpTrap{ Variables: []SnmpPDU{pdu}, Enterprise: trapTestEnterpriseOid, AgentAddress: trapTestAgentAddress, GenericTrap: trapTestGenericTrap, SpecificTrap: trapTestSpecificTrap, Timestamp: trapTestTimestamp, } _, err = ts.SendTrap(trap) if err != nil { t.Fatalf("SendTrap() err: %v", err) } // wait for response from handler select { case <-done: case <-time.After(2 * time.Second): t.Fatal("timed out waiting for trap to be received") } } func TestSendV3EngineIdDiscovery(t *testing.T) { tl := NewTrapListener() defer tl.Close() authorativeEngineID := string([]byte{0x80, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04}) unknownEngineID := string([]byte{0x80, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x05}) sp := &UsmSecurityParameters{ UserName: "test", AuthenticationProtocol: SHA, AuthenticationPassphrase: "password", PrivacyProtocol: AES256, PrivacyPassphrase: "password", AuthoritativeEngineBoots: 1, AuthoritativeEngineTime: 1, AuthoritativeEngineID: authorativeEngineID, } tl.Params = Default tl.Params.Version = Version3 tl.Params.SecurityParameters = sp tl.Params.SecurityModel = UserSecurityModel tl.Params.MsgFlags = AuthPriv // listener goroutine errch := make(chan error) go func() { err := tl.Listen(net.JoinHostPort(trapTestAddress, trapTestPortString)) if err != nil { errch <- err } }() // Wait until the listener is ready. select { case <-tl.Listening(): case err := <-errch: t.Fatalf("error in listen: %v", err) } ts := &GoSNMP{ Target: trapTestAddress, Port: trapTestPort, //Community: "public", Version: Version3, Timeout: time.Duration(2) * time.Second, Retries: 3, MaxOids: MaxOids, SecurityModel: UserSecurityModel, SecurityParameters: sp, MsgFlags: AuthPriv, } err := ts.Connect() if err != nil { t.Fatalf("Connect() err: %v", err) } defer ts.Conn.Close() getEngineIDRequest := SnmpPacket{ Version: Version3, MsgFlags: Reportable, SecurityModel: UserSecurityModel, SecurityParameters: &UsmSecurityParameters{}, ContextEngineID: unknownEngineID, PDUType: GetRequest, MsgID: 1824792385, RequestID: 1411852680, MsgMaxSize: 65507, } result, err := ts.sendOneRequest(&getEngineIDRequest, true) require.NoError(t, err, "sendOneRequest failed") require.Equal(t, result.SecurityParameters.(*UsmSecurityParameters).AuthoritativeEngineID, authorativeEngineID, "invalid authoritativeEngineID") require.Equal(t, result.PDUType, Report, "invalid received PDUType") } golang-github-gosnmp-gosnmp-1.38.0/v3.go000066400000000000000000000351331465470716300200350ustar00rootroot00000000000000// 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. // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package gosnmp import ( "bytes" "encoding/binary" "errors" "fmt" "runtime" ) // SnmpV3MsgFlags contains various message flags to describe Authentication, Privacy, and whether a report PDU must be sent. type SnmpV3MsgFlags uint8 // Possible values of SnmpV3MsgFlags const ( NoAuthNoPriv SnmpV3MsgFlags = 0x0 // No authentication, and no privacy AuthNoPriv SnmpV3MsgFlags = 0x1 // Authentication and no privacy AuthPriv SnmpV3MsgFlags = 0x3 // Authentication and privacy Reportable SnmpV3MsgFlags = 0x4 // Report PDU must be sent. ) //go:generate stringer -type=SnmpV3MsgFlags // SnmpV3SecurityModel describes the security model used by a SnmpV3 connection type SnmpV3SecurityModel uint8 // UserSecurityModel is the only SnmpV3SecurityModel currently implemented. const ( UserSecurityModel SnmpV3SecurityModel = 3 ) //go:generate stringer -type=SnmpV3SecurityModel // SnmpV3SecurityParameters is a generic interface type to contain various implementations of SnmpV3SecurityParameters type SnmpV3SecurityParameters interface { Log() Copy() SnmpV3SecurityParameters Description() string SafeString() string InitPacket(packet *SnmpPacket) error InitSecurityKeys() error validate(flags SnmpV3MsgFlags) error init(log Logger) error discoveryRequired() *SnmpPacket getDefaultContextEngineID() string setSecurityParameters(in SnmpV3SecurityParameters) error marshal(flags SnmpV3MsgFlags) ([]byte, error) unmarshal(flags SnmpV3MsgFlags, packet []byte, cursor int) (int, error) authenticate(packet []byte) error isAuthentic(packetBytes []byte, packet *SnmpPacket) (bool, error) encryptPacket(scopedPdu []byte) ([]byte, error) decryptPacket(packet []byte, cursor int) ([]byte, error) getIdentifier() string getLogger() Logger setLogger(log Logger) } func (x *GoSNMP) validateParametersV3() error { // update following code if you implement a new security model if x.SecurityModel != UserSecurityModel { return errors.New("the SNMPV3 User Security Model is the only SNMPV3 security model currently implemented") } if x.SecurityParameters == nil { return errors.New("SNMPV3 SecurityParameters must be set") } return x.SecurityParameters.validate(x.MsgFlags) } // authenticate the marshalled result of a snmp version 3 packet func (packet *SnmpPacket) authenticate(msg []byte) ([]byte, error) { defer func() { if e := recover(); e != nil { var buf = make([]byte, 8192) runtime.Stack(buf, true) fmt.Printf("[v3::authenticate]recover: %v. Stack=%v\n", e, string(buf)) } }() if packet.Version != Version3 { return msg, nil } if packet.MsgFlags&AuthNoPriv > 0 { err := packet.SecurityParameters.authenticate(msg) if err != nil { return nil, err } } return msg, nil } func (x *GoSNMP) testAuthentication(packet []byte, result *SnmpPacket, useResponseSecurityParameters bool) error { if x.Version != Version3 { return fmt.Errorf("testAuthentication called with non Version3 connection") } msgFlags := x.MsgFlags if useResponseSecurityParameters { msgFlags = result.MsgFlags } if msgFlags&AuthNoPriv > 0 { var authentic bool var err error if useResponseSecurityParameters { authentic, err = result.SecurityParameters.isAuthentic(packet, result) } else { authentic, err = x.SecurityParameters.isAuthentic(packet, result) } if err != nil { return err } if !authentic { return fmt.Errorf("incoming packet is not authentic, discarding") } } return nil } func (x *GoSNMP) initPacket(packetOut *SnmpPacket) error { if x.MsgFlags&AuthPriv > AuthNoPriv { return x.SecurityParameters.InitPacket(packetOut) } return nil } // http://tools.ietf.org/html/rfc2574#section-2.2.3 This code does not // check if the last message received was more than 150 seconds ago The // snmpds that this code was tested on emit an 'out of time window' // error with the new time and this code will retransmit when that is // received. func (x *GoSNMP) negotiateInitialSecurityParameters(packetOut *SnmpPacket) error { if x.Version != Version3 || packetOut.Version != Version3 { return fmt.Errorf("negotiateInitialSecurityParameters called with non Version3 connection or packet") } if x.SecurityModel != packetOut.SecurityModel { return fmt.Errorf("connection security model does not match security model defined in packet") } if discoveryPacket := packetOut.SecurityParameters.discoveryRequired(); discoveryPacket != nil { discoveryPacket.ContextName = x.ContextName result, err := x.sendOneRequest(discoveryPacket, true) if err != nil { return err } err = x.storeSecurityParameters(result) if err != nil { return err } err = x.updatePktSecurityParameters(packetOut) if err != nil { return err } } else { err := packetOut.SecurityParameters.InitSecurityKeys() if err == nil { return err } } return nil } // save the connection security parameters after a request/response func (x *GoSNMP) storeSecurityParameters(result *SnmpPacket) error { if x.Version != Version3 || result.Version != Version3 { return fmt.Errorf("storeParameters called with non Version3 connection or packet") } if x.SecurityModel != result.SecurityModel { return fmt.Errorf("connection security model does not match security model extracted from packet") } if x.ContextEngineID == "" { x.ContextEngineID = result.SecurityParameters.getDefaultContextEngineID() } return x.SecurityParameters.setSecurityParameters(result.SecurityParameters) } // update packet security parameters to match connection security parameters func (x *GoSNMP) updatePktSecurityParameters(packetOut *SnmpPacket) error { if x.Version != Version3 || packetOut.Version != Version3 { return fmt.Errorf("updatePktSecurityParameters called with non Version3 connection or packet") } if x.SecurityModel != packetOut.SecurityModel { return fmt.Errorf("connection security model does not match security model extracted from packet") } err := packetOut.SecurityParameters.setSecurityParameters(x.SecurityParameters) if err != nil { return err } if packetOut.ContextEngineID == "" { packetOut.ContextEngineID = x.ContextEngineID } return nil } func (packet *SnmpPacket) marshalV3(buf *bytes.Buffer) (*bytes.Buffer, error) { //nolint:interfacer emptyBuffer := new(bytes.Buffer) // used when returning errors header, err := packet.marshalV3Header() if err != nil { return emptyBuffer, err } buf.Write([]byte{byte(Sequence), byte(len(header))}) packet.Logger.Printf("Marshal V3 Header len=%d. Eaten Last 4 Bytes=%v", len(header), header[len(header)-4:]) buf.Write(header) var securityParameters []byte securityParameters, err = packet.SecurityParameters.marshal(packet.MsgFlags) if err != nil { return emptyBuffer, err } packet.Logger.Printf("Marshal V3 SecurityParameters len=%d. Eaten Last 4 Bytes=%v", len(securityParameters), securityParameters[len(securityParameters)-4:]) buf.Write([]byte{byte(OctetString)}) secParamLen, err := marshalLength(len(securityParameters)) if err != nil { return emptyBuffer, err } buf.Write(secParamLen) buf.Write(securityParameters) scopedPdu, err := packet.marshalV3ScopedPDU() if err != nil { return emptyBuffer, err } buf.Write(scopedPdu) return buf, nil } // marshal a snmp version 3 packet header func (packet *SnmpPacket) marshalV3Header() ([]byte, error) { buf := new(bytes.Buffer) // msg id buf.Write([]byte{byte(Integer), 4}) err := binary.Write(buf, binary.BigEndian, packet.MsgID) if err != nil { return nil, err } oldLen := 0 packet.Logger.Printf("MarshalV3Header msgID len=%v", buf.Len()-oldLen) oldLen = buf.Len() // maximum response msg size var maxBufSize uint32 = rxBufSize if packet.MsgMaxSize != 0 { maxBufSize = packet.MsgMaxSize } maxmsgsize, err := marshalUint32(maxBufSize) if err != nil { return nil, err } buf.Write([]byte{byte(Integer), byte(len(maxmsgsize))}) buf.Write(maxmsgsize) packet.Logger.Printf("MarshalV3Header maxmsgsize len=%v", buf.Len()-oldLen) oldLen = buf.Len() // msg flags buf.Write([]byte{byte(OctetString), 1, byte(packet.MsgFlags)}) packet.Logger.Printf("MarshalV3Header msg flags len=%v", buf.Len()-oldLen) oldLen = buf.Len() // msg security model buf.Write([]byte{byte(Integer), 1, byte(packet.SecurityModel)}) packet.Logger.Printf("MarshalV3Header msg security model len=%v", buf.Len()-oldLen) return buf.Bytes(), nil } // marshal and encrypt (if necessary) a snmp version 3 Scoped PDU func (packet *SnmpPacket) marshalV3ScopedPDU() ([]byte, error) { var b []byte scopedPdu, err := packet.prepareV3ScopedPDU() if err != nil { return nil, err } pduLen, err := marshalLength(len(scopedPdu)) if err != nil { return nil, err } b = append([]byte{byte(Sequence)}, pduLen...) scopedPdu = append(b, scopedPdu...) if packet.MsgFlags&AuthPriv > AuthNoPriv { scopedPdu, err = packet.SecurityParameters.encryptPacket(scopedPdu) if err != nil { return nil, err } } return scopedPdu, nil } // prepare the plain text of a snmp version 3 Scoped PDU func (packet *SnmpPacket) prepareV3ScopedPDU() ([]byte, error) { var buf bytes.Buffer // ContextEngineID idlen, err := marshalLength(len(packet.ContextEngineID)) if err != nil { return nil, err } buf.Write(append([]byte{byte(OctetString)}, idlen...)) buf.WriteString(packet.ContextEngineID) // ContextName namelen, err := marshalLength(len(packet.ContextName)) if err != nil { return nil, err } buf.Write(append([]byte{byte(OctetString)}, namelen...)) buf.WriteString(packet.ContextName) data, err := packet.marshalPDU() if err != nil { return nil, err } buf.Write(data) return buf.Bytes(), nil } func (x *GoSNMP) unmarshalV3Header(packet []byte, cursor int, response *SnmpPacket) (int, error) { if PDUType(packet[cursor]) != Sequence { return 0, fmt.Errorf("invalid SNMPV3 Header") } _, cursorTmp, err := parseLength(packet[cursor:]) if err != nil { return 0, err } cursor += cursorTmp if cursor > len(packet) { return 0, errors.New("error parsing SNMPV3 message ID: truncted packet") } rawMsgID, count, err := parseRawField(x.Logger, packet[cursor:], "msgID") if err != nil { return 0, fmt.Errorf("error parsing SNMPV3 message ID: %w", err) } cursor += count if cursor > len(packet) { return 0, errors.New("error parsing SNMPV3 message ID: truncted packet") } if MsgID, ok := rawMsgID.(int); ok { response.MsgID = uint32(MsgID) x.Logger.Printf("Parsed message ID %d", MsgID) } rawMsgMaxSize, count, err := parseRawField(x.Logger, packet[cursor:], "msgMaxSize") if err != nil { return 0, fmt.Errorf("error parsing SNMPV3 msgMaxSize: %w", err) } cursor += count if cursor > len(packet) { return 0, errors.New("error parsing SNMPV3 message ID: truncted packet") } if MsgMaxSize, ok := rawMsgMaxSize.(int); ok { response.MsgMaxSize = uint32(MsgMaxSize) x.Logger.Printf("Parsed message max size %d", MsgMaxSize) } rawMsgFlags, count, err := parseRawField(x.Logger, packet[cursor:], "msgFlags") if err != nil { return 0, fmt.Errorf("error parsing SNMPV3 msgFlags: %w", err) } cursor += count if cursor > len(packet) { return 0, errors.New("error parsing SNMPV3 message ID: truncted packet") } if MsgFlags, ok := rawMsgFlags.(string); ok && len(MsgFlags) > 0 { response.MsgFlags = SnmpV3MsgFlags(MsgFlags[0]) x.Logger.Printf("parsed msg flags %s", MsgFlags) } rawSecModel, count, err := parseRawField(x.Logger, packet[cursor:], "msgSecurityModel") if err != nil { return 0, fmt.Errorf("error parsing SNMPV3 msgSecModel: %w", err) } cursor += count if cursor >= len(packet) { return 0, errors.New("error parsing SNMPV3 message ID: truncted packet") } if SecModel, ok := rawSecModel.(int); ok { response.SecurityModel = SnmpV3SecurityModel(SecModel) x.Logger.Printf("Parsed security model %d", SecModel) } if PDUType(packet[cursor]) != PDUType(OctetString) { return 0, errors.New("invalid SNMPV3 Security Parameters") } _, cursorTmp, err = parseLength(packet[cursor:]) if err != nil { return 0, err } cursor += cursorTmp if cursor > len(packet) { return 0, errors.New("error parsing SNMPV3 message ID: truncted packet") } if response.SecurityParameters == nil { response.SecurityParameters = &UsmSecurityParameters{Logger: x.Logger} } cursor, err = response.SecurityParameters.unmarshal(response.MsgFlags, packet, cursor) if err != nil { return 0, err } x.Logger.Printf("Parsed Security Parameters. now offset=%v,", cursor) return cursor, nil } func (x *GoSNMP) decryptPacket(packet []byte, cursor int, response *SnmpPacket) ([]byte, int, error) { var err error var decrypted = false if cursor >= len(packet) { return nil, 0, errors.New("error parsing SNMPV3: truncated packet") } switch PDUType(packet[cursor]) { case PDUType(OctetString): // pdu is encrypted packet, err = response.SecurityParameters.decryptPacket(packet, cursor) if err != nil { return nil, 0, err } decrypted = true fallthrough case Sequence: // pdu is plaintext or has been decrypted tlength, cursorTmp, err := parseLength(packet[cursor:]) if err != nil { return nil, 0, err } if decrypted { // truncate padding that might have been included with // the encrypted PDU if cursor+tlength > len(packet) { return nil, 0, errors.New("error parsing SNMPV3: truncated packet") } packet = packet[:cursor+tlength] } cursor += cursorTmp if cursor > len(packet) { return nil, 0, errors.New("error parsing SNMPV3: truncated packet") } rawContextEngineID, count, err := parseRawField(x.Logger, packet[cursor:], "contextEngineID") if err != nil { return nil, 0, fmt.Errorf("error parsing SNMPV3 contextEngineID: %w", err) } cursor += count if cursor > len(packet) { return nil, 0, errors.New("error parsing SNMPV3: truncated packet") } if contextEngineID, ok := rawContextEngineID.(string); ok { response.ContextEngineID = contextEngineID x.Logger.Printf("Parsed contextEngineID %s", contextEngineID) } rawContextName, count, err := parseRawField(x.Logger, packet[cursor:], "contextName") if err != nil { return nil, 0, fmt.Errorf("error parsing SNMPV3 contextName: %w", err) } cursor += count if cursor > len(packet) { return nil, 0, errors.New("error parsing SNMPV3: truncated packet") } if contextName, ok := rawContextName.(string); ok { response.ContextName = contextName x.Logger.Printf("Parsed contextName %s", contextName) } default: return nil, 0, errors.New("error parsing SNMPV3 scoped PDU") } return packet, cursor, nil } golang-github-gosnmp-gosnmp-1.38.0/v3_credentials_test.go000066400000000000000000000227521465470716300234540ustar00rootroot00000000000000// Copyright 2021 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. package gosnmp import "testing" // GO SNMP credentials table // //nolint:gochecknoglobals,unused var authenticationCredentials = map[string][]string{ NoAuth.String() + NoPriv.String(): {"noAuthNoPrivUser", "", ""}, MD5.String() + NoPriv.String(): {"authMD5OnlyUser", "testingpass0123456789", ""}, MD5.String() + DES.String(): {"authMD5PrivDESUser", "testingpass9876543210", "testingpass9876543210"}, MD5.String() + AES.String(): {"authMD5PrivAESUser", "AEStestingpass9876543210", "AEStestingpass9876543210"}, // MD5.String() + AES192.String(): { "authMD5PrivAES192BlmtUser", "authkey1", "privkey1" }, // MD5.String() + AES192C.String(): { "authMD5PrivAES192User", "authkey1", "privkey1" }, // MD5.String() + AES256.String(): { "authMD5PrivAES256BlmtUser", "authkey1", "privkey1" }, // MD5.String() + AES256C.String(): { "authMD5PrivAES256User", "authkey1", "privkey1" }, SHA.String() + NoPriv.String(): {"authSHAOnlyUser", "testingpass9876543210", ""}, SHA.String() + DES.String(): {"authSHAPrivDESUser", "testingpassabc6543210", "testingpassabc6543210"}, SHA.String() + AES.String(): {"authSHAPrivAESUser", "AEStestingpassabc6543210", "AEStestingpassabc6543210"}, // SHA.String() + AES192.String(): { "authSHAPrivAES192BlmtUser", "authkey1", "privkey1" }, // SHA.String() + AES192C.String(): { "authSHAPrivAES192User", "authkey1", "privkey1" }, // SHA.String() + AES256.String(): { "authSHAPrivAES256BlmtUser", "authkey1", "privkey1" }, // SHA.String() + AES256C.String(): { "authSHAPrivAES256User", "authkey1", "privkey1" }, SHA224.String() + NoPriv.String(): {"authSHA224OnlyUser", "testingpass5123456", ""}, SHA224.String() + DES.String(): {"authSHA224PrivDESUser", "testingpass6123456", "testingpass6123456"}, SHA224.String() + AES.String(): {"authSHA224PrivAESUser", "testingpass7123456", "testingpass7123456"}, // SHA224.String() + AES192.String(): { "authSHA224PrivAES192BlmtUser", "authkey1", "privkey1" }, // SHA224.String() + AES192C.String(): { "authSHA224PrivAES192User", "authkey1", "privkey1" }, // SHA224.String() + AES256.String(): { "authSHA224PrivAES256BlmtUser", "authkey1", "privkey1" }, // SHA224.String() + AES256C.String(): { "authSHA224PrivAES256User", "authkey1", "privkey1" }, SHA256.String() + NoPriv.String(): {"authSHA256OnlyUser", "testingpass5223456", ""}, SHA256.String() + DES.String(): {"authSHA256PrivDESUser", "testingpass6223456", "testingpass6223456"}, SHA256.String() + AES.String(): {"authSHA256PrivAESUser", "testingpass7223456", "testingpass7223456"}, // SHA256.String() + AES192.String(): { "authSHA256PrivAES192BlmtUser", "authkey1", "privkey1" }, // SHA256.String() + AES192C.String(): { "authSHA256PrivAES192User", "authkey1", "privkey1" }, // SHA256.String() + AES256.String(): { "authSHA256PrivAES256BlmtUser", "authkey1", "privkey1" }, // SHA256.String() + AES256C.String(): { "authSHA256PrivAES256User", "authkey1", "privkey1" }, SHA384.String() + NoPriv.String(): {"authSHA384OnlyUser", "testingpass5323456", ""}, SHA384.String() + DES.String(): {"authSHA384PrivDESUser", "testingpass6323456", "testingpass6323456"}, SHA384.String() + AES.String(): {"authSHA384PrivAESUser", "testingpass7323456", "testingpass7323456"}, // SHA384.String() + AES192.String(): { "authSHA384PrivAES192BlmtUser", "authkey1", "privkey1" }, // SHA384.String() + AES192C.String(): { "authSHA384PrivAES192User", "authkey1", "privkey1" }, // SHA384.String() + AES256.String(): { "authSHA384PrivAES256BlmtUser", "authkey1", "privkey1" }, // SHA384.String() + AES256C.String(): { "authSHA384PrivAES256User", "authkey1", "privkey1" }, SHA512.String() + NoPriv.String(): {"authSHA512OnlyUser", "testingpass5423456", ""}, SHA512.String() + DES.String(): {"authSHA512PrivDESUser", "testingpass6423456", "testingpass6423456"}, SHA512.String() + AES.String(): {"authSHA512PrivAESUser", "testingpass7423456", "testingpass7423456"}, // SHA512.String() + AES192.String(): { "authSHA512PrivAES192BlmtUser", "authkey1", "privkey1" }, // SHA512.String() + AES192C.String(): { "authSHA512PrivAES192User", "authkey1", "privkey1" }, // SHA512.String() + AES256.String(): { "authSHA512PrivAES256BlmtUser", "authkey1", "privkey1" }, // SHA512.String() + AES256C.String(): { "authSHA512PrivAES256User", "authkey1", "privkey1" }, } // Credentials table for public demo.snmplabs.org // //nolint:unused,gochecknoglobals var authenticationCredentialsSnmpLabs = map[string][]string{ NoAuth.String() + NoPriv.String(): {"usr-none-none", "", ""}, MD5.String() + NoPriv.String(): {"usr-md5-none", "authkey1", ""}, MD5.String() + DES.String(): {"usr-md5-des", "authkey1", "privkey1"}, MD5.String() + AES.String(): {"usr-md5-aes", "authkey1", "privkey1"}, MD5.String() + AES192.String(): {"usr-md5-aes192-blmt", "authkey1", "privkey1"}, MD5.String() + AES192C.String(): {"usr-md5-aes192", "authkey1", "privkey1"}, MD5.String() + AES256.String(): {"usr-md5-aes256-blmt", "authkey1", "privkey1"}, MD5.String() + AES256C.String(): {"usr-md5-aes256", "authkey1", "privkey1"}, SHA.String() + NoPriv.String(): {"usr-sha-none", "authkey1", ""}, SHA.String() + DES.String(): {"usr-sha-des", "authkey1", "privkey1"}, SHA.String() + AES.String(): {"usr-sha-aes", "authkey1", "privkey1"}, SHA.String() + AES192.String(): {"usr-sha-aes192-blmt", "authkey1", "privkey1"}, SHA.String() + AES192C.String(): {"usr-sha-aes192", "authkey1", "privkey1"}, SHA.String() + AES256.String(): {"usr-sha-aes256-blmt", "authkey1", "privkey1"}, SHA.String() + AES256C.String(): {"usr-sha-aes256", "authkey1", "privkey1"}, SHA224.String() + NoPriv.String(): {"usr-sha224-none", "authkey1", ""}, SHA224.String() + DES.String(): {"usr-sha224-des", "authkey1", "privkey1"}, SHA224.String() + AES.String(): {"usr-sha224-aes", "authkey1", "privkey1"}, SHA224.String() + AES192.String(): {"usr-sha224-aes192-blmt", "authkey1", "privkey1"}, SHA224.String() + AES192C.String(): {"usr-sha224-aes192", "authkey1", "privkey1"}, SHA224.String() + AES256.String(): {"usr-sha224-aes256-blmt", "authkey1", "privkey1"}, SHA224.String() + AES256C.String(): {"usr-sha224-aes256", "authkey1", "privkey1"}, SHA256.String() + NoPriv.String(): {"usr-sha256-none", "authkey1", ""}, SHA256.String() + DES.String(): {"usr-sha256-des", "authkey1", "privkey1"}, SHA256.String() + AES.String(): {"usr-sha256-aes", "authkey1", "privkey1"}, SHA256.String() + AES192.String(): {"usr-sha256-aes192-blmt", "authkey1", "privkey1"}, SHA256.String() + AES192C.String(): {"usr-sha256-aes192", "authkey1", "privkey1"}, SHA256.String() + AES256.String(): {"usr-sha256-aes256-blmt", "authkey1", "privkey1"}, SHA256.String() + AES256C.String(): {"usr-sha256-aes256", "authkey1", "privkey1"}, SHA384.String() + NoPriv.String(): {"usr-sha384-none", "authkey1", ""}, SHA384.String() + DES.String(): {"usr-sha384-des", "authkey1", "privkey1"}, SHA384.String() + AES.String(): {"usr-sha384-aes", "authkey1", "privkey1"}, SHA384.String() + AES192.String(): {"usr-sha384-aes192-blmt", "authkey1", "privkey1"}, SHA384.String() + AES192C.String(): {"usr-sha384-aes192", "authkey1", "privkey1"}, SHA384.String() + AES256.String(): {"usr-sha384-aes256-blmt", "authkey1", "privkey1"}, SHA384.String() + AES256C.String(): {"usr-sha384-aes256", "authkey1", "privkey1"}, SHA512.String() + NoPriv.String(): {"usr-sha512-none", "authkey1", ""}, SHA512.String() + DES.String(): {"usr-sha512-des", "authkey1", "privkey1"}, SHA512.String() + AES.String(): {"usr-sha512-aes", "authkey1", "privkey1"}, SHA512.String() + AES192.String(): {"usr-sha512-aes192-blmt", "authkey1", "privkey1"}, SHA512.String() + AES192C.String(): {"usr-sha512-aes192", "authkey1", "privkey1"}, SHA512.String() + AES256.String(): {"usr-sha512-aes256-blmt", "authkey1", "privkey1"}, SHA512.String() + AES256C.String(): {"usr-sha512-aes256", "authkey1", "privkey1"}, } //nolint:unused,gochecknoglobals var useSnmpLabsCredentials = false // TODO get above credentials into snmpsimd, so *all* tests can be run. Combine with settings in `snmp_users.sh` const cIdxUserName = 0 const cIdxAuthKey = 1 const cIdxPrivKey = 2 func isUsingSnmpLabs() bool { return useSnmpLabsCredentials } // conveniently enable demo.snmplabs.com for a one test func useSnmpLabs(use bool) { useSnmpLabsCredentials = use } //nolint:misspell func getCredentials(t *testing.T, authProtocol SnmpV3AuthProtocol, privProtocol SnmpV3PrivProtocol) []string { var credentials []string if useSnmpLabsCredentials { credentials = authenticationCredentialsSnmpLabs[authProtocol.String()+privProtocol.String()] } else { credentials = authenticationCredentials[authProtocol.String()+privProtocol.String()] } if credentials == nil { t.Skipf("No user credentials found for %s/%s", authProtocol.String(), privProtocol.String()) return []string{"unknown", "unknown", "unkown"} } return credentials } func getUserName(t *testing.T, authProtocol SnmpV3AuthProtocol, privProtocol SnmpV3PrivProtocol) string { return getCredentials(t, authProtocol, privProtocol)[cIdxUserName] } //nolint:unused,deadcode func getAuthKey(t *testing.T, authProtocol SnmpV3AuthProtocol, privProtocol SnmpV3PrivProtocol) string { return getCredentials(t, authProtocol, privProtocol)[cIdxAuthKey] } //nolint:unused,deadcode func getPrivKey(t *testing.T, authProtocol SnmpV3AuthProtocol, privProtocol SnmpV3PrivProtocol) string { return getCredentials(t, authProtocol, privProtocol)[cIdxPrivKey] } golang-github-gosnmp-gosnmp-1.38.0/v3_map.go000066400000000000000000000026211465470716300206660ustar00rootroot00000000000000// Copyright 2023 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. package gosnmp import ( "fmt" "sync" ) // SnmpV3SecurityParametersTable is a mapping of identifiers to corresponding SNMP V3 Security Model parameters type SnmpV3SecurityParametersTable struct { table map[string][]SnmpV3SecurityParameters Logger Logger mu sync.RWMutex } func NewSnmpV3SecurityParametersTable(logger Logger) *SnmpV3SecurityParametersTable { return &SnmpV3SecurityParametersTable{ table: make(map[string][]SnmpV3SecurityParameters), Logger: logger, } } func (spm *SnmpV3SecurityParametersTable) Add(key string, sp SnmpV3SecurityParameters) error { spm.mu.Lock() defer spm.mu.Unlock() if err := sp.InitSecurityKeys(); err != nil { return err } // If no logger is set for the security params (empty struct), use the one from the table if (Logger{}) == sp.getLogger() { sp.setLogger(spm.Logger) } spm.table[key] = append(spm.table[key], sp) spm.Logger.Printf("Added security parameters %s for key: %s", sp.SafeString(), key) return nil } func (spm *SnmpV3SecurityParametersTable) Get(key string) ([]SnmpV3SecurityParameters, error) { spm.mu.RLock() defer spm.mu.RUnlock() if sp, ok := spm.table[key]; ok { return sp, nil } return nil, fmt.Errorf("no security parameters found for the key %s", key) } golang-github-gosnmp-gosnmp-1.38.0/v3_usm.go000066400000000000000000000743031465470716300207230ustar00rootroot00000000000000// 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. // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package gosnmp import ( "bytes" "crypto" "crypto/aes" "crypto/cipher" "crypto/des" //nolint:gosec "crypto/hmac" "crypto/md5" //nolint:gosec crand "crypto/rand" "crypto/sha1" //nolint:gosec _ "crypto/sha256" // Register hash function #4 (SHA224), #5 (SHA256) _ "crypto/sha512" // Register hash function #6 (SHA384), #7 (SHA512) "encoding/binary" "encoding/hex" "errors" "fmt" "hash" "strings" "sync" "sync/atomic" ) // SnmpV3AuthProtocol describes the authentication protocol in use by an authenticated SnmpV3 connection. type SnmpV3AuthProtocol uint8 // NoAuth, MD5, and SHA are implemented const ( NoAuth SnmpV3AuthProtocol = 1 MD5 SnmpV3AuthProtocol = 2 SHA SnmpV3AuthProtocol = 3 SHA224 SnmpV3AuthProtocol = 4 SHA256 SnmpV3AuthProtocol = 5 SHA384 SnmpV3AuthProtocol = 6 SHA512 SnmpV3AuthProtocol = 7 ) //go:generate stringer -type=SnmpV3AuthProtocol // HashType maps the AuthProtocol's hash type to an actual crypto.Hash object. func (authProtocol SnmpV3AuthProtocol) HashType() crypto.Hash { switch authProtocol { default: return crypto.MD5 case SHA: return crypto.SHA1 case SHA224: return crypto.SHA224 case SHA256: return crypto.SHA256 case SHA384: return crypto.SHA384 case SHA512: return crypto.SHA512 } } //nolint:gochecknoglobals var macVarbinds = [][]byte{ {}, {byte(OctetString), 0}, {byte(OctetString), 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {byte(OctetString), 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {byte(OctetString), 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {byte(OctetString), 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {byte(OctetString), 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {byte(OctetString), 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}} // SnmpV3PrivProtocol is the privacy protocol in use by an private SnmpV3 connection. type SnmpV3PrivProtocol uint8 // NoPriv, DES implemented, AES planned // Changed: AES192, AES256, AES192C, AES256C added const ( NoPriv SnmpV3PrivProtocol = 1 DES SnmpV3PrivProtocol = 2 AES SnmpV3PrivProtocol = 3 AES192 SnmpV3PrivProtocol = 4 // Blumenthal-AES192 AES256 SnmpV3PrivProtocol = 5 // Blumenthal-AES256 AES192C SnmpV3PrivProtocol = 6 // Reeder-AES192 AES256C SnmpV3PrivProtocol = 7 // Reeder-AES256 ) //go:generate stringer -type=SnmpV3PrivProtocol // UsmSecurityParameters is an implementation of SnmpV3SecurityParameters for the UserSecurityModel type UsmSecurityParameters struct { mu sync.Mutex // localAESSalt must be 64bit aligned to use with atomic operations. localAESSalt uint64 localDESSalt uint32 AuthoritativeEngineID string AuthoritativeEngineBoots uint32 AuthoritativeEngineTime uint32 UserName string AuthenticationParameters string PrivacyParameters []byte AuthenticationProtocol SnmpV3AuthProtocol PrivacyProtocol SnmpV3PrivProtocol AuthenticationPassphrase string PrivacyPassphrase string SecretKey []byte PrivacyKey []byte Logger Logger } func (sp *UsmSecurityParameters) getIdentifier() string { return sp.UserName } func (sp *UsmSecurityParameters) getLogger() Logger { return sp.Logger } func (sp *UsmSecurityParameters) setLogger(log Logger) { sp.Logger = log } // Description logs authentication paramater information to the provided GoSNMP Logger func (sp *UsmSecurityParameters) Description() string { var sb strings.Builder sb.WriteString("user=") sb.WriteString(sp.UserName) sb.WriteString(",engine=(") sb.WriteString(hex.EncodeToString([]byte(sp.AuthoritativeEngineID))) // sb.WriteString(sp.AuthoritativeEngineID) sb.WriteString(")") switch sp.AuthenticationProtocol { case NoAuth: sb.WriteString(",auth=noauth") case MD5: sb.WriteString(",auth=md5") case SHA: sb.WriteString(",auth=sha") case SHA224: sb.WriteString(",auth=sha224") case SHA256: sb.WriteString(",auth=sha256") case SHA384: sb.WriteString(",auth=sha384") case SHA512: sb.WriteString(",auth=sha512") } sb.WriteString(",authPass=") sb.WriteString(sp.AuthenticationPassphrase) switch sp.PrivacyProtocol { case NoPriv: sb.WriteString(",priv=NoPriv") case DES: sb.WriteString(",priv=DES") case AES: sb.WriteString(",priv=AES") case AES192: sb.WriteString(",priv=AES192") case AES256: sb.WriteString(",priv=AES256") case AES192C: sb.WriteString(",priv=AES192C") case AES256C: sb.WriteString(",priv=AES256C") } sb.WriteString(",privPass=") sb.WriteString(sp.PrivacyPassphrase) return sb.String() } // SafeString returns a logging safe (no secrets) string of the UsmSecurityParameters func (sp *UsmSecurityParameters) SafeString() string { return fmt.Sprintf("AuthoritativeEngineID:%s, AuthoritativeEngineBoots:%d, AuthoritativeEngineTimes:%d, UserName:%s, AuthenticationParameters:%s, PrivacyParameters:%v, AuthenticationProtocol:%s, PrivacyProtocol:%s", sp.AuthoritativeEngineID, sp.AuthoritativeEngineBoots, sp.AuthoritativeEngineTime, sp.UserName, sp.AuthenticationParameters, sp.PrivacyParameters, sp.AuthenticationProtocol, sp.PrivacyProtocol, ) } // Log logs security paramater information to the provided GoSNMP Logger func (sp *UsmSecurityParameters) Log() { sp.mu.Lock() defer sp.mu.Unlock() sp.Logger.Printf("SECURITY PARAMETERS:%s", sp.SafeString()) } // Copy method for UsmSecurityParameters used to copy a SnmpV3SecurityParameters without knowing it's implementation func (sp *UsmSecurityParameters) Copy() SnmpV3SecurityParameters { sp.mu.Lock() defer sp.mu.Unlock() return &UsmSecurityParameters{AuthoritativeEngineID: sp.AuthoritativeEngineID, AuthoritativeEngineBoots: sp.AuthoritativeEngineBoots, AuthoritativeEngineTime: sp.AuthoritativeEngineTime, UserName: sp.UserName, AuthenticationParameters: sp.AuthenticationParameters, PrivacyParameters: sp.PrivacyParameters, AuthenticationProtocol: sp.AuthenticationProtocol, PrivacyProtocol: sp.PrivacyProtocol, AuthenticationPassphrase: sp.AuthenticationPassphrase, PrivacyPassphrase: sp.PrivacyPassphrase, SecretKey: sp.SecretKey, PrivacyKey: sp.PrivacyKey, localDESSalt: sp.localDESSalt, localAESSalt: sp.localAESSalt, Logger: sp.Logger, } } func (sp *UsmSecurityParameters) getDefaultContextEngineID() string { return sp.AuthoritativeEngineID } // InitSecurityKeys initializes the Priv and Auth keys if needed func (sp *UsmSecurityParameters) InitSecurityKeys() error { sp.mu.Lock() defer sp.mu.Unlock() return sp.initSecurityKeysNoLock() } func (sp *UsmSecurityParameters) initSecurityKeysNoLock() error { var err error if sp.AuthenticationProtocol > NoAuth && len(sp.SecretKey) == 0 { sp.SecretKey, err = genlocalkey(sp.AuthenticationProtocol, sp.AuthenticationPassphrase, sp.AuthoritativeEngineID) if err != nil { return err } } if sp.PrivacyProtocol > NoPriv && len(sp.PrivacyKey) == 0 { switch sp.PrivacyProtocol { // Changed: The Output of SHA1 is a 20 octets array, therefore for AES128 (16 octets) either key extension algorithm can be used. case AES, AES192, AES256, AES192C, AES256C: // Use abstract AES key localization algorithms. sp.PrivacyKey, err = genlocalPrivKey(sp.PrivacyProtocol, sp.AuthenticationProtocol, sp.PrivacyPassphrase, sp.AuthoritativeEngineID) if err != nil { return err } default: sp.PrivacyKey, err = genlocalkey(sp.AuthenticationProtocol, sp.PrivacyPassphrase, sp.AuthoritativeEngineID) if err != nil { return err } } } return nil } func (sp *UsmSecurityParameters) setSecurityParameters(in SnmpV3SecurityParameters) error { var insp *UsmSecurityParameters var err error sp.mu.Lock() defer sp.mu.Unlock() if insp, err = castUsmSecParams(in); err != nil { return err } if sp.AuthoritativeEngineID != insp.AuthoritativeEngineID { sp.AuthoritativeEngineID = insp.AuthoritativeEngineID sp.SecretKey = nil sp.PrivacyKey = nil err = sp.initSecurityKeysNoLock() if err != nil { return err } } sp.AuthoritativeEngineBoots = insp.AuthoritativeEngineBoots sp.AuthoritativeEngineTime = insp.AuthoritativeEngineTime return nil } func (sp *UsmSecurityParameters) validate(flags SnmpV3MsgFlags) error { securityLevel := flags & AuthPriv // isolate flags that determine security level switch securityLevel { case AuthPriv: if sp.PrivacyProtocol <= NoPriv { return fmt.Errorf("securityParameters.PrivacyProtocol is required") } fallthrough case AuthNoPriv: if sp.AuthenticationProtocol <= NoAuth { return fmt.Errorf("securityParameters.AuthenticationProtocol is required") } fallthrough case NoAuthNoPriv: if sp.UserName == "" { return fmt.Errorf("securityParameters.UserName is required") } default: return fmt.Errorf("validate: MsgFlags must be populated with an appropriate security level") } if sp.PrivacyProtocol > NoPriv && len(sp.PrivacyKey) == 0 { if sp.PrivacyPassphrase == "" { return fmt.Errorf("securityParameters.PrivacyPassphrase is required when a privacy protocol is specified") } } if sp.AuthenticationProtocol > NoAuth && len(sp.SecretKey) == 0 { if sp.AuthenticationPassphrase == "" { return fmt.Errorf("securityParameters.AuthenticationPassphrase is required when an authentication protocol is specified") } } return nil } func (sp *UsmSecurityParameters) init(log Logger) error { var err error sp.Logger = log switch sp.PrivacyProtocol { case AES, AES192, AES256, AES192C, AES256C: salt := make([]byte, 8) _, err = crand.Read(salt) if err != nil { return fmt.Errorf("error creating a cryptographically secure salt: %w", err) } sp.localAESSalt = binary.BigEndian.Uint64(salt) case DES: salt := make([]byte, 4) _, err = crand.Read(salt) if err != nil { return fmt.Errorf("error creating a cryptographically secure salt: %w", err) } sp.localDESSalt = binary.BigEndian.Uint32(salt) } return nil } func castUsmSecParams(secParams SnmpV3SecurityParameters) (*UsmSecurityParameters, error) { s, ok := secParams.(*UsmSecurityParameters) if !ok || s == nil { return nil, fmt.Errorf("param SnmpV3SecurityParameters is not of type *UsmSecurityParameters") } return s, nil } var ( passwordKeyHashCache = make(map[string][]byte) //nolint:gochecknoglobals passwordKeyHashMutex sync.RWMutex //nolint:gochecknoglobals passwordCacheDisable atomic.Bool //nolint:gochecknoglobals ) // PasswordCaching is enabled by default for performance reason. If the cache was disabled then // re-enabled, the cache is reset. func PasswordCaching(enable bool) { oldCacheEnable := !passwordCacheDisable.Load() passwordKeyHashMutex.Lock() if !enable { // if off passwordKeyHashCache = nil } else if !oldCacheEnable && enable { // if off then on passwordKeyHashCache = make(map[string][]byte) } passwordCacheDisable.Store(!enable) passwordKeyHashMutex.Unlock() } func hashPassword(hash hash.Hash, password string) ([]byte, error) { if len(password) == 0 { return []byte{}, errors.New("hashPassword: password is empty") } var pi int // password index for i := 0; i < 1048576; i += 64 { var chunk []byte for e := 0; e < 64; e++ { chunk = append(chunk, password[pi%len(password)]) pi++ } if _, err := hash.Write(chunk); err != nil { return []byte{}, err } } hashed := hash.Sum(nil) return hashed, nil } // Common passwordToKey algorithm, "caches" the result to avoid extra computation each reuse func cachedPasswordToKey(hash hash.Hash, cacheKey string, password string) ([]byte, error) { cacheDisable := passwordCacheDisable.Load() if !cacheDisable { passwordKeyHashMutex.RLock() value := passwordKeyHashCache[cacheKey] passwordKeyHashMutex.RUnlock() if value != nil { return value, nil } } hashed, err := hashPassword(hash, password) if err != nil { return nil, err } if !cacheDisable { passwordKeyHashMutex.Lock() passwordKeyHashCache[cacheKey] = hashed passwordKeyHashMutex.Unlock() } return hashed, nil } func hMAC(hash crypto.Hash, cacheKey string, password string, engineID string) ([]byte, error) { hashed, err := cachedPasswordToKey(hash.New(), cacheKey, password) if err != nil { return []byte{}, nil } local := hash.New() _, err = local.Write(hashed) if err != nil { return []byte{}, err } _, err = local.Write([]byte(engineID)) if err != nil { return []byte{}, err } _, err = local.Write(hashed) if err != nil { return []byte{}, err } final := local.Sum(nil) return final, nil } func cacheKey(authProtocol SnmpV3AuthProtocol, passphrase string) string { if passwordCacheDisable.Load() { return "" } var cacheKey = make([]byte, 1+len(passphrase)) cacheKey = append(cacheKey, 'h'+byte(authProtocol)) cacheKey = append(cacheKey, []byte(passphrase)...) return string(cacheKey) } // Extending the localized privacy key according to Reeder Key extension algorithm: // https://tools.ietf.org/html/draft-reeder-snmpv3-usm-3dese // Many vendors, including Cisco, use the 3DES key extension algorithm to extend the privacy keys that are too short when using AES,AES192 and AES256. // Previously implemented in net-snmp and pysnmp libraries. // Tested for AES128 and AES256 func extendKeyReeder(authProtocol SnmpV3AuthProtocol, password string, engineID string) ([]byte, error) { var key []byte var err error key, err = hMAC(authProtocol.HashType(), cacheKey(authProtocol, password), password, engineID) if err != nil { return nil, err } newkey, err := hMAC(authProtocol.HashType(), cacheKey(authProtocol, string(key)), string(key), engineID) return append(key, newkey...), err } // Extending the localized privacy key according to Blumenthal key extension algorithm: // https://tools.ietf.org/html/draft-blumenthal-aes-usm-04#page-7 // Not many vendors use this algorithm. // Previously implemented in the net-snmp and pysnmp libraries. // TODO: Not tested func extendKeyBlumenthal(authProtocol SnmpV3AuthProtocol, password string, engineID string) ([]byte, error) { var key []byte var err error key, err = hMAC(authProtocol.HashType(), cacheKey(authProtocol, password), password, engineID) if err != nil { return nil, err } newkey := authProtocol.HashType().New() _, _ = newkey.Write(key) return append(key, newkey.Sum(nil)...), err } // Changed: New function to calculate the Privacy Key for abstract AES func genlocalPrivKey(privProtocol SnmpV3PrivProtocol, authProtocol SnmpV3AuthProtocol, password string, engineID string) ([]byte, error) { var keylen int var localPrivKey []byte var err error switch privProtocol { case AES, DES: keylen = 16 case AES192, AES192C: keylen = 24 case AES256, AES256C: keylen = 32 } switch privProtocol { case AES, AES192C, AES256C: localPrivKey, err = extendKeyReeder(authProtocol, password, engineID) case AES192, AES256: localPrivKey, err = extendKeyBlumenthal(authProtocol, password, engineID) default: localPrivKey, err = genlocalkey(authProtocol, password, engineID) } if err != nil { return nil, err } if len(localPrivKey) < keylen { return []byte{}, fmt.Errorf("genlocalPrivKey: privProtocol: %v len(localPrivKey): %d, keylen: %d", privProtocol, len(localPrivKey), keylen) } return localPrivKey[:keylen], nil } func genlocalkey(authProtocol SnmpV3AuthProtocol, passphrase string, engineID string) ([]byte, error) { var secretKey []byte var err error secretKey, err = hMAC(authProtocol.HashType(), cacheKey(authProtocol, passphrase), passphrase, engineID) if err != nil { return []byte{}, err } return secretKey, nil } // http://tools.ietf.org/html/rfc2574#section-8.1.1.1 // localDESSalt needs to be incremented on every packet. func (sp *UsmSecurityParameters) usmAllocateNewSalt() interface{} { sp.mu.Lock() defer sp.mu.Unlock() var newSalt interface{} switch sp.PrivacyProtocol { case AES, AES192, AES256, AES192C, AES256C: newSalt = atomic.AddUint64(&(sp.localAESSalt), 1) default: newSalt = atomic.AddUint32(&(sp.localDESSalt), 1) } return newSalt } func (sp *UsmSecurityParameters) usmSetSalt(newSalt interface{}) error { sp.mu.Lock() defer sp.mu.Unlock() switch sp.PrivacyProtocol { case AES, AES192, AES256, AES192C, AES256C: aesSalt, ok := newSalt.(uint64) if !ok { return fmt.Errorf("salt provided to usmSetSalt is not the correct type for the AES privacy protocol") } var salt = make([]byte, 8) binary.BigEndian.PutUint64(salt, aesSalt) sp.PrivacyParameters = salt default: desSalt, ok := newSalt.(uint32) if !ok { return fmt.Errorf("salt provided to usmSetSalt is not the correct type for the DES privacy protocol") } var salt = make([]byte, 8) binary.BigEndian.PutUint32(salt, sp.AuthoritativeEngineBoots) binary.BigEndian.PutUint32(salt[4:], desSalt) sp.PrivacyParameters = salt } return nil } // InitPacket ensures the enc salt is incremented for packets marked for AuthPriv func (sp *UsmSecurityParameters) InitPacket(packet *SnmpPacket) error { // http://tools.ietf.org/html/rfc2574#section-8.1.1.1 // localDESSalt needs to be incremented on every packet. newSalt := sp.usmAllocateNewSalt() if packet.MsgFlags&AuthPriv > AuthNoPriv { s, err := castUsmSecParams(packet.SecurityParameters) if err != nil { return err } return s.usmSetSalt(newSalt) } return nil } func (sp *UsmSecurityParameters) discoveryRequired() *SnmpPacket { if sp.AuthoritativeEngineID == "" { var emptyPdus []SnmpPDU // send blank packet to discover authoriative engine ID/boots/time blankPacket := &SnmpPacket{ Version: Version3, MsgFlags: Reportable | NoAuthNoPriv, SecurityModel: UserSecurityModel, SecurityParameters: &UsmSecurityParameters{Logger: sp.Logger}, PDUType: GetRequest, Logger: sp.Logger, Variables: emptyPdus, } return blankPacket } return nil } func (sp *UsmSecurityParameters) calcPacketDigest(packet []byte) ([]byte, error) { return calcPacketDigest(packet, sp) } // calcPacketDigest calculate authenticate digest for incoming messages (TRAP or // INFORM). // Support MD5, SHA1, SHA224, SHA256, SHA384, SHA512 protocols func calcPacketDigest(packetBytes []byte, secParams *UsmSecurityParameters) ([]byte, error) { var digest []byte var err error switch secParams.AuthenticationProtocol { case MD5, SHA: digest, err = digestRFC3414( secParams.AuthenticationProtocol, packetBytes, secParams.SecretKey) case SHA224, SHA256, SHA384, SHA512: digest, err = digestRFC7860( secParams.AuthenticationProtocol, packetBytes, secParams.SecretKey) } return digest, err } // digestRFC7860 calculate digest for incoming messages using HMAC-SHA2 protcols // according to RFC7860 4.2.2 func digestRFC7860(h SnmpV3AuthProtocol, packet []byte, authKey []byte) ([]byte, error) { mac := hmac.New(h.HashType().New, authKey) _, err := mac.Write(packet) if err != nil { return []byte{}, err } msgDigest := mac.Sum(nil) return msgDigest, nil } // digestRFC3414 calculate digest for incoming messages using MD5 or SHA1 // according to RFC3414 6.3.2 and 7.3.2 func digestRFC3414(h SnmpV3AuthProtocol, packet []byte, authKey []byte) ([]byte, error) { var extkey [64]byte var err error var k1, k2 [64]byte var h1, h2 hash.Hash copy(extkey[:], authKey) switch h { case MD5: h1 = md5.New() //nolint:gosec h2 = md5.New() //nolint:gosec case SHA: h1 = sha1.New() //nolint:gosec h2 = sha1.New() //nolint:gosec } for i := 0; i < 64; i++ { k1[i] = extkey[i] ^ 0x36 k2[i] = extkey[i] ^ 0x5c } _, err = h1.Write(k1[:]) if err != nil { return []byte{}, err } _, err = h1.Write(packet) if err != nil { return []byte{}, err } d1 := h1.Sum(nil) _, err = h2.Write(k2[:]) if err != nil { return []byte{}, err } _, err = h2.Write(d1) if err != nil { return []byte{}, err } return h2.Sum(nil)[:12], nil } func (sp *UsmSecurityParameters) authenticate(packet []byte) error { var msgDigest []byte var err error if msgDigest, err = sp.calcPacketDigest(packet); err != nil { return err } idx := bytes.Index(packet, macVarbinds[sp.AuthenticationProtocol]) if idx < 0 { return fmt.Errorf("unable to locate the position in packet to write authentication key") } copy(packet[idx+2:idx+len(macVarbinds[sp.AuthenticationProtocol])], msgDigest) return nil } // determine whether a message is authentic func (sp *UsmSecurityParameters) isAuthentic(packetBytes []byte, packet *SnmpPacket) (bool, error) { var msgDigest []byte var packetSecParams *UsmSecurityParameters var err error if packetSecParams, err = castUsmSecParams(packet.SecurityParameters); err != nil { return false, err } // TODO: investigate call chain to determine if this is really the best spot for this if msgDigest, err = calcPacketDigest(packetBytes, packetSecParams); err != nil { return false, err } for k, v := range []byte(packetSecParams.AuthenticationParameters) { if msgDigest[k] != v { return false, nil } } return true, nil } func (sp *UsmSecurityParameters) encryptPacket(scopedPdu []byte) ([]byte, error) { var b []byte switch sp.PrivacyProtocol { case AES, AES192, AES256, AES192C, AES256C: var iv [16]byte binary.BigEndian.PutUint32(iv[:], sp.AuthoritativeEngineBoots) binary.BigEndian.PutUint32(iv[4:], sp.AuthoritativeEngineTime) copy(iv[8:], sp.PrivacyParameters) // aes.NewCipher(sp.PrivacyKey[:16]) changed to aes.NewCipher(sp.PrivacyKey) block, err := aes.NewCipher(sp.PrivacyKey) if err != nil { return nil, err } stream := cipher.NewCFBEncrypter(block, iv[:]) ciphertext := make([]byte, len(scopedPdu)) stream.XORKeyStream(ciphertext, scopedPdu) pduLen, err := marshalLength(len(ciphertext)) if err != nil { return nil, err } b = append([]byte{byte(OctetString)}, pduLen...) scopedPdu = append(b, ciphertext...) //nolint:gocritic case DES: preiv := sp.PrivacyKey[8:] var iv [8]byte for i := 0; i < len(iv); i++ { iv[i] = preiv[i] ^ sp.PrivacyParameters[i] } block, err := des.NewCipher(sp.PrivacyKey[:8]) //nolint:gosec if err != nil { return nil, err } mode := cipher.NewCBCEncrypter(block, iv[:]) pad := make([]byte, des.BlockSize-len(scopedPdu)%des.BlockSize) scopedPdu = append(scopedPdu, pad...) ciphertext := make([]byte, len(scopedPdu)) mode.CryptBlocks(ciphertext, scopedPdu) pduLen, err := marshalLength(len(ciphertext)) if err != nil { return nil, err } b = append([]byte{byte(OctetString)}, pduLen...) scopedPdu = append(b, ciphertext...) //nolint:gocritic } return scopedPdu, nil } func (sp *UsmSecurityParameters) decryptPacket(packet []byte, cursor int) ([]byte, error) { _, cursorTmp, err := parseLength(packet[cursor:]) if err != nil { return nil, err } cursorTmp += cursor if cursorTmp > len(packet) { return nil, errors.New("error decrypting ScopedPDU: truncated packet") } switch sp.PrivacyProtocol { case AES, AES192, AES256, AES192C, AES256C: var iv [16]byte binary.BigEndian.PutUint32(iv[:], sp.AuthoritativeEngineBoots) binary.BigEndian.PutUint32(iv[4:], sp.AuthoritativeEngineTime) copy(iv[8:], sp.PrivacyParameters) block, err := aes.NewCipher(sp.PrivacyKey) if err != nil { return nil, err } stream := cipher.NewCFBDecrypter(block, iv[:]) plaintext := make([]byte, len(packet[cursorTmp:])) stream.XORKeyStream(plaintext, packet[cursorTmp:]) copy(packet[cursor:], plaintext) packet = packet[:cursor+len(plaintext)] case DES: if len(packet[cursorTmp:])%des.BlockSize != 0 { return nil, errors.New("error decrypting ScopedPDU: not multiple of des block size") } preiv := sp.PrivacyKey[8:] var iv [8]byte for i := 0; i < len(iv); i++ { iv[i] = preiv[i] ^ sp.PrivacyParameters[i] } block, err := des.NewCipher(sp.PrivacyKey[:8]) //nolint:gosec if err != nil { return nil, err } mode := cipher.NewCBCDecrypter(block, iv[:]) plaintext := make([]byte, len(packet[cursorTmp:])) mode.CryptBlocks(plaintext, packet[cursorTmp:]) copy(packet[cursor:], plaintext) // truncate packet to remove extra space caused by the // octetstring/length header that was just replaced packet = packet[:cursor+len(plaintext)] } return packet, nil } // marshal a snmp version 3 security parameters field for the User Security Model func (sp *UsmSecurityParameters) marshal(flags SnmpV3MsgFlags) ([]byte, error) { var buf bytes.Buffer var err error // msgAuthoritativeEngineID buf.Write([]byte{byte(OctetString), byte(len(sp.AuthoritativeEngineID))}) buf.WriteString(sp.AuthoritativeEngineID) // msgAuthoritativeEngineBoots msgAuthoritativeEngineBoots, err := marshalUint32(sp.AuthoritativeEngineBoots) if err != nil { return nil, err } buf.Write([]byte{byte(Integer), byte(len(msgAuthoritativeEngineBoots))}) buf.Write(msgAuthoritativeEngineBoots) // msgAuthoritativeEngineTime msgAuthoritativeEngineTime, err := marshalUint32(sp.AuthoritativeEngineTime) if err != nil { return nil, err } buf.Write([]byte{byte(Integer), byte(len(msgAuthoritativeEngineTime))}) buf.Write(msgAuthoritativeEngineTime) // msgUserName buf.Write([]byte{byte(OctetString), byte(len(sp.UserName))}) buf.WriteString(sp.UserName) // msgAuthenticationParameters if flags&AuthNoPriv > 0 { buf.Write(macVarbinds[sp.AuthenticationProtocol]) } else { buf.Write([]byte{byte(OctetString), 0}) } // msgPrivacyParameters if flags&AuthPriv > AuthNoPriv { privlen, err2 := marshalLength(len(sp.PrivacyParameters)) if err2 != nil { return nil, err2 } buf.Write([]byte{byte(OctetString)}) buf.Write(privlen) buf.Write(sp.PrivacyParameters) } else { buf.Write([]byte{byte(OctetString), 0}) } // wrap security parameters in a sequence paramLen, err := marshalLength(buf.Len()) if err != nil { return nil, err } tmpseq := append([]byte{byte(Sequence)}, paramLen...) tmpseq = append(tmpseq, buf.Bytes()...) return tmpseq, nil } func (sp *UsmSecurityParameters) unmarshal(flags SnmpV3MsgFlags, packet []byte, cursor int) (int, error) { var err error if cursor >= len(packet) { return 0, errors.New("error parsing SNMPV3 User Security Model parameters: end of packet") } if PDUType(packet[cursor]) != Sequence { return 0, errors.New("error parsing SNMPV3 User Security Model parameters") } _, cursorTmp, err := parseLength(packet[cursor:]) if err != nil { return 0, err } cursor += cursorTmp if cursorTmp > len(packet) { return 0, errors.New("error parsing SNMPV3 User Security Model parameters: truncated packet") } rawMsgAuthoritativeEngineID, count, err := parseRawField(sp.Logger, packet[cursor:], "msgAuthoritativeEngineID") if err != nil { return 0, fmt.Errorf("error parsing SNMPV3 User Security Model msgAuthoritativeEngineID: %w", err) } cursor += count if AuthoritativeEngineID, ok := rawMsgAuthoritativeEngineID.(string); ok { if sp.AuthoritativeEngineID != AuthoritativeEngineID { sp.AuthoritativeEngineID = AuthoritativeEngineID sp.SecretKey = nil sp.PrivacyKey = nil sp.Logger.Printf("Parsed authoritativeEngineID %0x", []byte(AuthoritativeEngineID)) err = sp.initSecurityKeysNoLock() if err != nil { return 0, err } } } rawMsgAuthoritativeEngineBoots, count, err := parseRawField(sp.Logger, packet[cursor:], "msgAuthoritativeEngineBoots") if err != nil { return 0, fmt.Errorf("error parsing SNMPV3 User Security Model msgAuthoritativeEngineBoots: %w", err) } cursor += count if AuthoritativeEngineBoots, ok := rawMsgAuthoritativeEngineBoots.(int); ok { sp.AuthoritativeEngineBoots = uint32(AuthoritativeEngineBoots) sp.Logger.Printf("Parsed authoritativeEngineBoots %d", AuthoritativeEngineBoots) } rawMsgAuthoritativeEngineTime, count, err := parseRawField(sp.Logger, packet[cursor:], "msgAuthoritativeEngineTime") if err != nil { return 0, fmt.Errorf("error parsing SNMPV3 User Security Model msgAuthoritativeEngineTime: %w", err) } cursor += count if AuthoritativeEngineTime, ok := rawMsgAuthoritativeEngineTime.(int); ok { sp.AuthoritativeEngineTime = uint32(AuthoritativeEngineTime) sp.Logger.Printf("Parsed authoritativeEngineTime %d", AuthoritativeEngineTime) } rawMsgUserName, count, err := parseRawField(sp.Logger, packet[cursor:], "msgUserName") if err != nil { return 0, fmt.Errorf("error parsing SNMPV3 User Security Model msgUserName: %w", err) } cursor += count if msgUserName, ok := rawMsgUserName.(string); ok { sp.UserName = msgUserName sp.Logger.Printf("Parsed userName %s", msgUserName) } rawMsgAuthParameters, count, err := parseRawField(sp.Logger, packet[cursor:], "msgAuthenticationParameters") if err != nil { return 0, fmt.Errorf("error parsing SNMPV3 User Security Model msgAuthenticationParameters: %w", err) } if msgAuthenticationParameters, ok := rawMsgAuthParameters.(string); ok { sp.AuthenticationParameters = msgAuthenticationParameters sp.Logger.Printf("Parsed authenticationParameters %s", msgAuthenticationParameters) } // blank msgAuthenticationParameters to prepare for authentication check later if flags&AuthNoPriv > 0 { // In case if the authentication protocol is not configured or set to NoAuth, then the packet cannot // be processed further if sp.AuthenticationProtocol <= NoAuth { return 0, errors.New("error parsing SNMPv3 User Security Model: authentication parameters are not configured to parse incoming authenticated message") } copy(packet[cursor+2:cursor+len(macVarbinds[sp.AuthenticationProtocol])], macVarbinds[sp.AuthenticationProtocol][2:]) } cursor += count rawMsgPrivacyParameters, count, err := parseRawField(sp.Logger, packet[cursor:], "msgPrivacyParameters") if err != nil { return 0, fmt.Errorf("error parsing SNMPV3 User Security Model msgPrivacyParameters: %w", err) } cursor += count if msgPrivacyParameters, ok := rawMsgPrivacyParameters.(string); ok { sp.PrivacyParameters = []byte(msgPrivacyParameters) sp.Logger.Printf("Parsed privacyParameters %s", msgPrivacyParameters) if flags&AuthPriv >= AuthPriv { if sp.PrivacyProtocol <= NoPriv { return 0, errors.New("error parsing SNMPv3 User Security Model: privacy parameters are not configured to parse incoming encrypted message") } } } return cursor, nil } golang-github-gosnmp-gosnmp-1.38.0/v3_usm_nopwdcache_test.go000066400000000000000000000004021465470716300241420ustar00rootroot00000000000000// 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 gosnmp_nopwdcache package gosnmp func SetPwdCache() { PasswordCaching(false) } golang-github-gosnmp-gosnmp-1.38.0/v3_usm_pwdcache_test.go000066400000000000000000000004021465470716300236050ustar00rootroot00000000000000// 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 !gosnmp_nopwdcache package gosnmp func SetPwdCache() { PasswordCaching(true) } golang-github-gosnmp-gosnmp-1.38.0/v3_usm_test.go000066400000000000000000000213671465470716300217640ustar00rootroot00000000000000// Copyright 2020 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. package gosnmp import ( "encoding/hex" "io" "log" "testing" "github.com/stretchr/testify/require" ) /** * This tests use hex dumps from real network traffic produced using net-snmp's snmpget with demo.snmplabs.com as SNMP agent. */ func authorativeEngineID(t *testing.T) string { // engine ID of demo.snmplabs.com engineID, err := hex.DecodeString("80004fb805636c6f75644dab22cc") require.NoError(t, err, "EngineId decoding failed.") return string(engineID) } func correctKeySHA224(t *testing.T) []byte { correctKey, err := hex.DecodeString("f2a2ebaa9677ad286255596286ca4fb7ec22f52405cb0aac334c5f15") require.NoError(t, err, "Correct key initialization failed.") return correctKey } func packetSHA224NoAuthentication(t *testing.T) []byte { packet, err := hex.DecodeString("308184020103300e02025f84020205c0040105020103043f303d040e80004fb805636c6f75644dab22cc02012b0203203ea5040f7573722d7368613232342d6e6f6e650410000000000000000000000000000000000400302e040e80004fb805636c6f75644dab22cc0400a01a02023ced020100020100300e300c06082b060102010101000500") require.NoError(t, err, "Non-authenticated packet data SHA224 decoding failed.") return packet } func packetSHA224Authenticated(t *testing.T) []byte { packet, err := hex.DecodeString("308184020103300e02025f84020205c0040105020103043f303d040e80004fb805636c6f75644dab22cc02012b0203203ea5040f7573722d7368613232342d6e6f6e65041066cd2d9b04cd48b02a9df0c77dc3415d0400302e040e80004fb805636c6f75644dab22cc0400a01a02023ced020100020100300e300c06082b060102010101000500") require.NoError(t, err, "Authenticated packet data SHA224 decoding failed.") return packet } func packetSHA224AuthenticationParams(t *testing.T) string { params, err := hex.DecodeString("66cd2d9b04cd48b02a9df0c77dc3415d") require.NoError(t, err, "Authentication parameters SHA224 decoding failed.") return string(params) } func TestAuthenticationSHA224(t *testing.T) { var err error sp := UsmSecurityParameters{ localAESSalt: 0, localDESSalt: 0, AuthoritativeEngineBoots: 43, AuthoritativeEngineID: authorativeEngineID(t), AuthoritativeEngineTime: 2113189, UserName: "usr-sha224-none", AuthenticationParameters: "", PrivacyParameters: nil, AuthenticationProtocol: SHA224, PrivacyProtocol: 0, AuthenticationPassphrase: "authkey1", PrivacyPassphrase: "", SecretKey: nil, Logger: NewLogger(log.New(io.Discard, "", 0)), PrivacyKey: nil, } sp.SecretKey, err = genlocalkey(sp.AuthenticationProtocol, sp.AuthenticationPassphrase, sp.AuthoritativeEngineID) require.NoError(t, err, "Generation of key failed") require.Equal(t, correctKeySHA224(t), sp.SecretKey, "Wrong key generated") srcPacket := packetSHA224NoAuthentication(t) err = sp.authenticate(srcPacket) require.NoError(t, err, "Authentication of packet failed") require.Equal(t, packetSHA224Authenticated(t), srcPacket, "Wrong message authentication parameters.") } func TestIsAuthenticaSHA224(t *testing.T) { var err error sp := UsmSecurityParameters{ localAESSalt: 0, localDESSalt: 0, AuthoritativeEngineBoots: 43, AuthoritativeEngineID: authorativeEngineID(t), AuthoritativeEngineTime: 2113189, UserName: "usr-sha224-none", AuthenticationParameters: packetSHA224AuthenticationParams(t), PrivacyParameters: nil, AuthenticationProtocol: SHA224, PrivacyProtocol: 0, AuthenticationPassphrase: "authkey1", PrivacyPassphrase: "", SecretKey: nil, PrivacyKey: nil, Logger: NewLogger(log.New(io.Discard, "", 0)), } sp.SecretKey, err = genlocalkey(sp.AuthenticationProtocol, sp.AuthenticationPassphrase, sp.AuthoritativeEngineID) require.NoError(t, err, "Generation of key failed") require.Equal(t, correctKeySHA224(t), sp.SecretKey, "Wrong key generated") srcPacket := packetSHA224NoAuthentication(t) snmpPacket := SnmpPacket{ SecurityParameters: &sp, } authentic, err := sp.isAuthentic(srcPacket, &snmpPacket) require.NoError(t, err, "Authentication check of key failed") require.True(t, authentic, "Packet was not considered to be authentic") } func correctKeySHA512(t *testing.T) []byte { correctKey, err := hex.DecodeString("c336e5e6396926813d623984610e8f0cd7f419da75c82ac50927c84fd92027f7cdd849ce983036dca67bfb1e8fde2a8c2d45cd2f0d3e0b0b929f7dda462a58cf") require.NoError(t, err, "Correct key initialization failed.") return correctKey } func packetSHA512NoAuthentication(t *testing.T) []byte { packet, err := hex.DecodeString("3081a4020103300e0202366e020205c0040105020103045f305d040e80004fb805636c6f75644dab22cc02012b0203203eea040f7573722d7368613531322d6e6f6e6504300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400302e040e80004fb805636c6f75644dab22cc0400a01a020214d9020100020100300e300c06082b060102010101000500") require.NoError(t, err, "Not-authenticated packet data SHA512 decoding failed.") return packet } func packetSHA512Authenticated(t *testing.T) []byte { packet, err := hex.DecodeString("3081a4020103300e0202366e020205c0040105020103045f305d040e80004fb805636c6f75644dab22cc02012b0203203eea040f7573722d7368613531322d6e6f6e65043026f8087ced336a394642b8698eba9810929a9bfa44afbf43975a7ad6c4cc55bd279b549a77ec56d791467612747d6f570400302e040e80004fb805636c6f75644dab22cc0400a01a020214d9020100020100300e300c06082b060102010101000500") require.NoError(t, err, "Authenticated packet data SHA512 decoding failed.") return packet } func packetSHA512AuthenticationParams(t *testing.T) string { params, err := hex.DecodeString("26f8087ced336a394642b8698eba9810929a9bfa44afbf43975a7ad6c4cc55bd279b549a77ec56d791467612747d6f57") require.NoError(t, err, "Authentication parameters SHA512 decoding failed.") return string(params) } func TestAuthenticationSHA512(t *testing.T) { var err error sp := UsmSecurityParameters{ localAESSalt: 0, localDESSalt: 0, AuthoritativeEngineBoots: 43, AuthoritativeEngineID: authorativeEngineID(t), AuthoritativeEngineTime: 2113258, UserName: "usr-sha512-none", AuthenticationParameters: "", PrivacyParameters: nil, AuthenticationProtocol: SHA512, PrivacyProtocol: 0, AuthenticationPassphrase: "authkey1", PrivacyPassphrase: "", SecretKey: nil, PrivacyKey: nil, Logger: NewLogger(log.New(io.Discard, "", 0)), } sp.SecretKey, err = genlocalkey(sp.AuthenticationProtocol, sp.AuthenticationPassphrase, sp.AuthoritativeEngineID) require.NoError(t, err, "Generation of key failed") require.Equal(t, correctKeySHA512(t), sp.SecretKey, "Wrong key generated") srcPacket := packetSHA512NoAuthentication(t) err = sp.authenticate(srcPacket) require.NoError(t, err, "Generation of key failed") require.Equal(t, packetSHA512Authenticated(t), srcPacket, "Wrong message authentication parameters.") } func TestIsAuthenticaSHA512(t *testing.T) { var err error sp := UsmSecurityParameters{ localAESSalt: 0, localDESSalt: 0, AuthoritativeEngineBoots: 43, AuthoritativeEngineID: authorativeEngineID(t), AuthoritativeEngineTime: 2113189, UserName: "usr-sha512-none", AuthenticationParameters: packetSHA512AuthenticationParams(t), PrivacyParameters: nil, AuthenticationProtocol: SHA512, PrivacyProtocol: 0, AuthenticationPassphrase: "authkey1", PrivacyPassphrase: "", SecretKey: nil, Logger: NewLogger(log.New(io.Discard, "", 0)), PrivacyKey: nil, } sp.SecretKey, err = genlocalkey(sp.AuthenticationProtocol, sp.AuthenticationPassphrase, sp.AuthoritativeEngineID) require.NoError(t, err, "Generation of key failed") require.Equal(t, correctKeySHA512(t), sp.SecretKey, "Wrong key generated") srcPacket := packetSHA512NoAuthentication(t) snmpPacket := SnmpPacket{ SecurityParameters: &sp, } authentic, err := sp.isAuthentic(srcPacket, &snmpPacket) require.NoError(t, err, "Authentication check of key failed") require.True(t, authentic, "Packet was not considered to be authentic") } func BenchmarkSingleHash(b *testing.B) { SetPwdCache() engineID, _ := hex.DecodeString("80004fb805636c6f75644dab22cc") for i := MD5; i < SHA512; i++ { b.Run(b.Name()+i.String(), func(b *testing.B) { for n := 0; n < b.N; n++ { _, err := genlocalkey(i, "authkey1", string(engineID)) if err != nil { b.Fatal(err) } } }) } passwordKeyHashMutex.RLock() b.Logf("cache size %d", len(passwordKeyHashCache)) passwordKeyHashMutex.RUnlock() } golang-github-gosnmp-gosnmp-1.38.0/walk.go000066400000000000000000000112061465470716300204360ustar00rootroot00000000000000// 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. package gosnmp import ( "fmt" "strings" ) func (x *GoSNMP) walk(getRequestType PDUType, rootOid string, walkFn WalkFunc) error { if rootOid == "" || rootOid == "." { rootOid = baseOid } if !strings.HasPrefix(rootOid, ".") { rootOid = string(".") + rootOid } oid := rootOid requests := 0 maxReps := x.MaxRepetitions if maxReps == 0 { maxReps = defaultMaxRepetitions } // AppOpt 'c: do not check returned OIDs are increasing' checkIncreasing := true if x.AppOpts != nil { if _, ok := x.AppOpts["c"]; ok { if getRequestType == GetBulkRequest || getRequestType == GetNextRequest { checkIncreasing = false } } } RequestLoop: for { requests++ var response *SnmpPacket var err error switch getRequestType { case GetBulkRequest: response, err = x.GetBulk([]string{oid}, uint8(x.NonRepeaters), maxReps) case GetNextRequest: response, err = x.GetNext([]string{oid}) case GetRequest: response, err = x.Get([]string{oid}) default: response, err = nil, fmt.Errorf("unsupported request type: %d", getRequestType) } if err != nil { return err } if len(response.Variables) == 0 { break RequestLoop } switch response.Error { case TooBig: x.Logger.Print("Walk terminated with TooBig") break RequestLoop case NoSuchName: x.Logger.Print("Walk terminated with NoSuchName") break RequestLoop case BadValue: x.Logger.Print("Walk terminated with BadValue") break RequestLoop case ReadOnly: x.Logger.Print("Walk terminated with ReadOnly") break RequestLoop case GenErr: x.Logger.Print("Walk terminated with GenErr") break RequestLoop case NoAccess: x.Logger.Print("Walk terminated with NoAccess") break RequestLoop case WrongType: x.Logger.Print("Walk terminated with WrongType") break RequestLoop case WrongLength: x.Logger.Print("Walk terminated with WrongLength") break RequestLoop case WrongEncoding: x.Logger.Print("Walk terminated with WrongEncoding") break RequestLoop case WrongValue: x.Logger.Print("Walk terminated with WrongValue") break RequestLoop case NoCreation: x.Logger.Print("Walk terminated with NoCreation") break RequestLoop case InconsistentValue: x.Logger.Print("Walk terminated with InconsistentValue") break RequestLoop case ResourceUnavailable: x.Logger.Print("Walk terminated with ResourceUnavailable") break RequestLoop case CommitFailed: x.Logger.Print("Walk terminated with CommitFailed") break RequestLoop case UndoFailed: x.Logger.Print("Walk terminated with UndoFailed") break RequestLoop case AuthorizationError: x.Logger.Print("Walk terminated with AuthorizationError") break RequestLoop case NotWritable: x.Logger.Print("Walk terminated with NotWritable") break RequestLoop case InconsistentName: x.Logger.Print("Walk terminated with InconsistentName") break RequestLoop case NoError: x.Logger.Print("Walk completed with NoError") } for i, pdu := range response.Variables { if pdu.Type == EndOfMibView || pdu.Type == NoSuchObject || pdu.Type == NoSuchInstance { x.Logger.Printf("BulkWalk terminated with type 0x%x", pdu.Type) break RequestLoop } if !strings.HasPrefix(pdu.Name, rootOid+".") { // Not in the requested root range. // if this is the first request, and the first variable in that request // and this condition is triggered - the first result is out of range // need to perform a regular get request // this request has been too narrowly defined to be found with a getNext // Issue #78 #93 if requests == 1 && i == 0 { getRequestType = GetRequest continue RequestLoop } else if pdu.Name == rootOid && pdu.Type != NoSuchInstance { // Call walk function if the pdu instance is found // considering that the rootOid is a leafOid if err := walkFn(pdu); err != nil { return err } } break RequestLoop } if checkIncreasing && pdu.Name == oid { return fmt.Errorf("OID not increasing: %s", pdu.Name) } // Report our pdu if err := walkFn(pdu); err != nil { return err } } // Save last oid for next request oid = response.Variables[len(response.Variables)-1].Name } x.Logger.Printf("BulkWalk completed in %d requests", requests) return nil } func (x *GoSNMP) walkAll(getRequestType PDUType, rootOid string) (results []SnmpPDU, err error) { err = x.walk(getRequestType, rootOid, func(dataUnit SnmpPDU) error { results = append(results, dataUnit) return nil }) return results, err }