├── .gitignore
├── .gitmodules
├── Makefile
├── Makefile.deps
├── Makefile.targ
├── README.md
├── agent.js
├── agent.json
├── docs
├── agent.restdown
├── index.restdown
├── media
│ └── img
│ │ ├── favicon.ico
│ │ └── logo.png
├── mib.restdown
├── protocol.restdown
├── provider.restdown
└── snmp.restdown
├── lib
├── agent.js
├── client.js
├── errors
│ ├── message.js
│ └── varbind.js
├── index.js
├── lexer.js
├── listener.js
├── mib.js
├── mib
│ ├── index.js
│ └── mib-2
│ │ └── system.js
├── protocol
│ ├── data.js
│ ├── message.js
│ ├── pdu.js
│ ├── uint64_t.js
│ └── varbind.js
├── provider.js
├── receiver.js
├── snmp.jison
└── trap_listener.js
├── package.json
├── smf
└── manifests
│ └── snmpd.xml
├── snmpbulkget.js
├── snmpget.js
├── snmpinform.js
├── snmpset.js
├── snmptrap.js
├── snmpwalk.js
├── test
└── protocol
│ ├── data.test.js
│ └── uint64_t.test.js
├── tl.js
├── tl.json
└── tools
├── jsl.node.conf
└── service_bundle.dtd.1
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | lib/parser.js
3 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "deps/javascriptlint"]
2 | path = deps/javascriptlint
3 | url = git://github.com/davepacheco/javascriptlint.git
4 | [submodule "deps/jsstyle"]
5 | path = deps/jsstyle
6 | url = git://github.com/davepacheco/jsstyle.git
7 | [submodule "deps/restdown"]
8 | path = deps/restdown
9 | url = git://github.com/trentm/restdown.git
10 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright (c) 2012, Joyent, Inc. All rights reserved.
3 | #
4 | # Makefile: basic Makefile for template API service
5 | #
6 | # This Makefile is a template for new repos. It contains only repo-specific
7 | # logic and uses included makefiles to supply common targets (javascriptlint,
8 | # jsstyle, restdown, etc.), which are used by other repos as well. You may well
9 | # need to rewrite most of this file, but you shouldn't need to touch the
10 | # included makefiles.
11 | #
12 | # If you find yourself adding support for new targets that could be useful for
13 | # other projects too, you should add these to the original versions of the
14 | # included Makefiles (in eng.git) so that other teams can use them too.
15 | #
16 |
17 | #
18 | # Tools
19 | #
20 | NPM := npm
21 | TAP := ./node_modules/.bin/tap
22 | JISON := ./node_modules/.bin/jison
23 |
24 | #
25 | # Files
26 | #
27 | DOC_FILES = \
28 | agent.restdown \
29 | index.restdown \
30 | mib.restdown \
31 | protocol.restdown \
32 | provider.restdown \
33 | snmp.restdown
34 |
35 | JS_FILES := \
36 | snmpbulkget.js \
37 | snmpget.js \
38 | snmpset.js \
39 | snmptrap.js \
40 | snmpwalk.js \
41 | tl.js \
42 | agent.js \
43 | lib/agent.js \
44 | lib/client.js \
45 | lib/index.js \
46 | lib/lexer.js \
47 | lib/errors/message.js \
48 | lib/errors/varbind.js \
49 | lib/listener.js \
50 | lib/mib/index.js \
51 | lib/mib/mib-2/system.js \
52 | lib/mib.js \
53 | lib/protocol/data.js \
54 | lib/protocol/message.js \
55 | lib/protocol/pdu.js \
56 | lib/protocol/uint64_t.js \
57 | lib/protocol/varbind.js \
58 | lib/receiver.js \
59 | lib/trap_listener.js \
60 | lib/provider.js
61 |
62 | JSL_CONF_NODE = tools/jsl.node.conf
63 | JSL_FILES_NODE = $(JS_FILES)
64 | JSSTYLE_FILES = $(JS_FILES)
65 | JSSTYLE_FLAGS = -o indent=tab,doxygen,unparenthesized-return=1
66 | SMF_MANIFESTS = smf/manifests/snmpd.xml
67 |
68 | CLEAN_FILES += lib/parser.js
69 |
70 | #
71 | # Repo-specific targets
72 | #
73 | .PHONY: all
74 | all: rebuild lib/parser.js
75 |
76 | .PHONY: rebuild
77 | rebuild:
78 | $(NPM) rebuild
79 |
80 | .PHONY: test
81 | test: $(TAP)
82 | TAP=1 $(TAP) test
83 |
84 | lib/parser.js: lib/snmp.jison rebuild
85 | $(JISON) -o $@ $<
86 |
87 | include ./Makefile.deps
88 | include ./Makefile.targ
89 |
--------------------------------------------------------------------------------
/Makefile.deps:
--------------------------------------------------------------------------------
1 | # -*- mode: makefile -*-
2 | #
3 | # This Source Code Form is subject to the terms of the Mozilla Public
4 | # License, v. 2.0. If a copy of the MPL was not distributed with this
5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 | #
7 |
8 | #
9 | # Copyright (c) 2014, Joyent, Inc.
10 | #
11 |
12 | #
13 | # Makefile.deps: Makefile for including common tools as dependencies
14 | #
15 | # NOTE: This makefile comes from the "eng" repo. It's designed to be dropped
16 | # into other repos as-is without requiring any modifications. If you find
17 | # yourself changing this file, you should instead update the original copy in
18 | # eng.git and then update your repo to use the new version.
19 | #
20 | # This file is separate from Makefile.targ so that teams can choose
21 | # independently whether to use the common targets in Makefile.targ and the
22 | # common tools here.
23 | #
24 |
25 | #
26 | # javascriptlint
27 | #
28 | JSL_EXEC ?= deps/javascriptlint/build/install/jsl
29 | JSL ?= $(JSL_EXEC)
30 |
31 | $(JSL_EXEC): | deps/javascriptlint/.git
32 | cd deps/javascriptlint && make install
33 |
34 | distclean::
35 | if [[ -f deps/javascriptlint/Makefile ]]; then \
36 | cd deps/javascriptlint && make clean; \
37 | fi
38 |
39 | #
40 | # jsstyle
41 | #
42 | JSSTYLE_EXEC ?= deps/jsstyle/jsstyle
43 | JSSTYLE ?= $(JSSTYLE_EXEC)
44 |
45 | $(JSSTYLE_EXEC): | deps/jsstyle/.git
46 |
47 | #
48 | # restdown
49 | #
50 | RESTDOWN_EXEC ?= deps/restdown/bin/restdown
51 | RESTDOWN ?= python $(RESTDOWN_EXEC)
52 | $(RESTDOWN_EXEC): | deps/restdown/.git
53 |
54 | EXTRA_DOC_DEPS ?=
55 |
--------------------------------------------------------------------------------
/Makefile.targ:
--------------------------------------------------------------------------------
1 | # -*- mode: makefile -*-
2 | #
3 | # This Source Code Form is subject to the terms of the Mozilla Public
4 | # License, v. 2.0. If a copy of the MPL was not distributed with this
5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 | #
7 |
8 | #
9 | # Copyright (c) 2014, Joyent, Inc.
10 | #
11 |
12 | #
13 | # Makefile.targ: common targets.
14 | #
15 | # NOTE: This makefile comes from the "eng" repo. It's designed to be dropped
16 | # into other repos as-is without requiring any modifications. If you find
17 | # yourself changing this file, you should instead update the original copy in
18 | # eng.git and then update your repo to use the new version.
19 | #
20 | # This Makefile defines several useful targets and rules. You can use it by
21 | # including it from a Makefile that specifies some of the variables below.
22 | #
23 | # Targets defined in this Makefile:
24 | #
25 | # check Checks JavaScript files for lint and style
26 | # Checks bash scripts for syntax
27 | # Checks SMF manifests for validity against the SMF DTD
28 | #
29 | # clean Removes built files
30 | #
31 | # docs Builds restdown documentation in docs/
32 | #
33 | # prepush Depends on "check" and "test"
34 | #
35 | # test Does nothing (you should override this)
36 | #
37 | # xref Generates cscope (source cross-reference index)
38 | #
39 | # For details on what these targets are supposed to do, see the Joyent
40 | # Engineering Guide.
41 | #
42 | # To make use of these targets, you'll need to set some of these variables. Any
43 | # variables left unset will simply not be used.
44 | #
45 | # BASH_FILES Bash scripts to check for syntax
46 | # (paths relative to top-level Makefile)
47 | #
48 | # CLEAN_FILES Files to remove as part of the "clean" target. Note
49 | # that files generated by targets in this Makefile are
50 | # automatically included in CLEAN_FILES. These include
51 | # restdown-generated HTML and JSON files.
52 | #
53 | # DOC_FILES Restdown (documentation source) files. These are
54 | # assumed to be contained in "docs/", and must NOT
55 | # contain the "docs/" prefix.
56 | #
57 | # JSL_CONF_NODE Specify JavaScriptLint configuration files
58 | # JSL_CONF_WEB (paths relative to top-level Makefile)
59 | #
60 | # Node.js and Web configuration files are separate
61 | # because you'll usually want different global variable
62 | # configurations. If no file is specified, none is given
63 | # to jsl, which causes it to use a default configuration,
64 | # which probably isn't what you want.
65 | #
66 | # JSL_FILES_NODE JavaScript files to check with Node config file.
67 | # JSL_FILES_WEB JavaScript files to check with Web config file.
68 | #
69 | # JSON_FILES JSON files to be validated
70 | #
71 | # JSSTYLE_FILES JavaScript files to be style-checked
72 | #
73 | # You can also override these variables:
74 | #
75 | # BASH Path to bash (default: "bash")
76 | #
77 | # CSCOPE_DIRS Directories to search for source files for the cscope
78 | # index. (default: ".")
79 | #
80 | # JSL Path to JavaScriptLint (default: "jsl")
81 | #
82 | # JSL_FLAGS_NODE Additional flags to pass through to JSL
83 | # JSL_FLAGS_WEB
84 | # JSL_FLAGS
85 | #
86 | # JSON Path to json tool (default: "json")
87 | #
88 | # JSSTYLE Path to jsstyle (default: "jsstyle")
89 | #
90 | # JSSTYLE_FLAGS Additional flags to pass through to jsstyle
91 | #
92 | # RESTDOWN_EXT By default '.md' is required for DOC_FILES (see above).
93 | # If you want to use, say, '.restdown' instead, then set
94 | # 'RESTDOWN_EXT=.restdown' in your Makefile.
95 | #
96 |
97 | #
98 | # Defaults for the various tools we use.
99 | #
100 | BASH ?= bash
101 | BASHSTYLE ?= tools/bashstyle
102 | CP ?= cp
103 | CSCOPE ?= cscope
104 | CSCOPE_DIRS ?= .
105 | JSL ?= jsl
106 | JSON ?= json
107 | JSSTYLE ?= jsstyle
108 | MKDIR ?= mkdir -p
109 | MV ?= mv
110 | RESTDOWN_FLAGS ?=
111 | RESTDOWN_EXT ?= .md
112 | RMTREE ?= rm -rf
113 | JSL_FLAGS ?= --nologo --nosummary
114 |
115 | ifeq ($(shell uname -s),SunOS)
116 | TAR ?= gtar
117 | else
118 | TAR ?= tar
119 | endif
120 |
121 |
122 | #
123 | # Defaults for other fixed values.
124 | #
125 | BUILD = build
126 | DISTCLEAN_FILES += $(BUILD)
127 | DOC_BUILD = $(BUILD)/docs/public
128 |
129 | #
130 | # Configure JSL_FLAGS_{NODE,WEB} based on JSL_CONF_{NODE,WEB}.
131 | #
132 | ifneq ($(origin JSL_CONF_NODE), undefined)
133 | JSL_FLAGS_NODE += --conf=$(JSL_CONF_NODE)
134 | endif
135 |
136 | ifneq ($(origin JSL_CONF_WEB), undefined)
137 | JSL_FLAGS_WEB += --conf=$(JSL_CONF_WEB)
138 | endif
139 |
140 | #
141 | # Targets. For descriptions on what these are supposed to do, see the
142 | # Joyent Engineering Guide.
143 | #
144 |
145 | #
146 | # Instruct make to keep around temporary files. We have rules below that
147 | # automatically update git submodules as needed, but they employ a deps/*/.git
148 | # temporary file. Without this directive, make tries to remove these .git
149 | # directories after the build has completed.
150 | #
151 | .SECONDARY: $($(wildcard deps/*):%=%/.git)
152 |
153 | #
154 | # This rule enables other rules that use files from a git submodule to have
155 | # those files depend on deps/module/.git and have "make" automatically check
156 | # out the submodule as needed.
157 | #
158 | deps/%/.git:
159 | git submodule update --init deps/$*
160 |
161 | #
162 | # These recipes make heavy use of dynamically-created phony targets. The parent
163 | # Makefile defines a list of input files like BASH_FILES. We then say that each
164 | # of these files depends on a fake target called filename.bashchk, and then we
165 | # define a pattern rule for those targets that runs bash in check-syntax-only
166 | # mode. This mechanism has the nice properties that if you specify zero files,
167 | # the rule becomes a noop (unlike a single rule to check all bash files, which
168 | # would invoke bash with zero files), and you can check individual files from
169 | # the command line with "make filename.bashchk".
170 | #
171 | .PHONY: check-bash
172 | check-bash: $(BASH_FILES:%=%.bashchk) $(BASH_FILES:%=%.bashstyle)
173 |
174 | %.bashchk: %
175 | $(BASH) -n $^
176 |
177 | %.bashstyle: %
178 | $(BASHSTYLE) $^
179 |
180 | .PHONY: check-json
181 | check-json: $(JSON_FILES:%=%.jsonchk)
182 |
183 | %.jsonchk: %
184 | $(JSON) --validate -f $^
185 |
186 | #
187 | # The above approach can be slow when there are many files to check because it
188 | # requires that "make" invoke the check tool once for each file, rather than
189 | # passing in several files at once. For the JavaScript check targets, we define
190 | # a variable for the target itself *only if* the list of input files is
191 | # non-empty. This avoids invoking the tool if there are no files to check.
192 | #
193 | JSL_NODE_TARGET = $(if $(JSL_FILES_NODE), check-jsl-node)
194 | .PHONY: check-jsl-node
195 | check-jsl-node: $(JSL_EXEC)
196 | $(JSL) $(JSL_FLAGS) $(JSL_FLAGS_NODE) $(JSL_FILES_NODE)
197 |
198 | JSL_WEB_TARGET = $(if $(JSL_FILES_WEB), check-jsl-web)
199 | .PHONY: check-jsl-web
200 | check-jsl-web: $(JSL_EXEC)
201 | $(JSL) $(JSL_FLAGS) $(JSL_FLAGS_WEB) $(JSL_FILES_WEB)
202 |
203 | .PHONY: check-jsl
204 | check-jsl: $(JSL_NODE_TARGET) $(JSL_WEB_TARGET)
205 |
206 | JSSTYLE_TARGET = $(if $(JSSTYLE_FILES), check-jsstyle)
207 | .PHONY: check-jsstyle
208 | check-jsstyle: $(JSSTYLE_EXEC)
209 | $(JSSTYLE) $(JSSTYLE_FLAGS) $(JSSTYLE_FILES)
210 |
211 | .PHONY: check
212 | check:: check-jsl check-json $(JSSTYLE_TARGET) check-bash
213 | @echo check ok
214 |
215 | .PHONY: clean
216 | clean::
217 | -$(RMTREE) $(CLEAN_FILES)
218 |
219 | .PHONY: distclean
220 | distclean:: clean
221 | -$(RMTREE) $(DISTCLEAN_FILES)
222 |
223 | CSCOPE_FILES = cscope.in.out cscope.out cscope.po.out
224 | CLEAN_FILES += $(CSCOPE_FILES)
225 |
226 | .PHONY: xref
227 | xref: cscope.files
228 | $(CSCOPE) -bqR
229 |
230 | .PHONY: cscope.files
231 | cscope.files:
232 | find $(CSCOPE_DIRS) -name '*.c' -o -name '*.h' -o -name '*.cc' \
233 | -o -name '*.js' -o -name '*.s' -o -name '*.cpp' > $@
234 |
235 | #
236 | # The "docs" target is complicated because we do several things here:
237 | #
238 | # (1) Use restdown to build HTML and JSON files from each of DOC_FILES.
239 | #
240 | # (2) Copy these files into $(DOC_BUILD) (build/docs/public), which
241 | # functions as a complete copy of the documentation that could be
242 | # mirrored or served over HTTP.
243 | #
244 | # (3) Then copy any directories and media from docs/media into
245 | # $(DOC_BUILD)/media. This allows projects to include their own media,
246 | # including files that will override same-named files provided by
247 | # restdown.
248 | #
249 | # Step (3) is the surprisingly complex part: in order to do this, we need to
250 | # identify the subdirectories in docs/media, recreate them in
251 | # $(DOC_BUILD)/media, then do the same with the files.
252 | #
253 | DOC_MEDIA_DIRS := $(shell find docs/media -type d 2>/dev/null | grep -v "^docs/media$$")
254 | DOC_MEDIA_DIRS := $(DOC_MEDIA_DIRS:docs/media/%=%)
255 | DOC_MEDIA_DIRS_BUILD := $(DOC_MEDIA_DIRS:%=$(DOC_BUILD)/media/%)
256 |
257 | DOC_MEDIA_FILES := $(shell find docs/media -type f 2>/dev/null)
258 | DOC_MEDIA_FILES := $(DOC_MEDIA_FILES:docs/media/%=%)
259 | DOC_MEDIA_FILES_BUILD := $(DOC_MEDIA_FILES:%=$(DOC_BUILD)/media/%)
260 |
261 | #
262 | # Like the other targets, "docs" just depends on the final files we want to
263 | # create in $(DOC_BUILD), leveraging other targets and recipes to define how
264 | # to get there.
265 | #
266 | .PHONY: docs
267 | docs: \
268 | $(DOC_FILES:%$(RESTDOWN_EXT)=$(DOC_BUILD)/%.html) \
269 | $(DOC_FILES:%$(RESTDOWN_EXT)=$(DOC_BUILD)/%.json) \
270 | $(DOC_MEDIA_FILES_BUILD)
271 |
272 | #
273 | # We keep the intermediate files so that the next build can see whether the
274 | # files in DOC_BUILD are up to date.
275 | #
276 | .PRECIOUS: \
277 | $(DOC_FILES:%$(RESTDOWN_EXT)=docs/%.html) \
278 | $(DOC_FILES:%$(RESTDOWN_EXT)=docs/%json)
279 |
280 | #
281 | # We do clean those intermediate files, as well as all of DOC_BUILD.
282 | #
283 | CLEAN_FILES += \
284 | $(DOC_BUILD) \
285 | $(DOC_FILES:%$(RESTDOWN_EXT)=docs/%.html) \
286 | $(DOC_FILES:%$(RESTDOWN_EXT)=docs/%.json)
287 |
288 | #
289 | # Before installing the files, we must make sure the directories exist. The |
290 | # syntax tells make that the dependency need only exist, not be up to date.
291 | # Otherwise, it might try to rebuild spuriously because the directory itself
292 | # appears out of date.
293 | #
294 | $(DOC_MEDIA_FILES_BUILD): | $(DOC_MEDIA_DIRS_BUILD)
295 |
296 | $(DOC_BUILD)/%: docs/% | $(DOC_BUILD)
297 | $(CP) $< $@
298 |
299 | docs/%.json docs/%.html: docs/%$(RESTDOWN_EXT) | $(DOC_BUILD) $(RESTDOWN_EXEC) \
300 | $(EXTRA_DOC_DEPS)
301 | $(RESTDOWN) $(RESTDOWN_FLAGS) -m $(DOC_BUILD) $<
302 |
303 | $(DOC_BUILD):
304 | $(MKDIR) $@
305 |
306 | $(DOC_MEDIA_DIRS_BUILD):
307 | $(MKDIR) $@
308 |
309 | #
310 | # The default "test" target does nothing. This should usually be overridden by
311 | # the parent Makefile. It's included here so we can define "prepush" without
312 | # requiring the repo to define "test".
313 | #
314 | .PHONY: test
315 | test:
316 |
317 | .PHONY: prepush
318 | prepush: check test
319 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | snmpjs provides a toolkit for SNMP agents and management applications in
2 | Node.js.
3 |
4 | ## Usage
5 |
6 | For full docs, see .
7 |
8 | var os = require('os');
9 | var snmp = require('snmpjs');
10 |
11 | var agent = snmp.createAgent();
12 |
13 | agent.request({ oid: '.1.3.6.1.2.1.1.5', handler: function (prq) {
14 | var nodename = os.hostname();
15 | var val = snmp.data.createData({ type: 'OctetString',
16 | value: nodename });
17 |
18 | snmp.provider.readOnlyScalar(prq, val);
19 | } });
20 |
21 | agent.bind({ family: 'udp4', port: 161 });
22 |
23 | Try hitting that with your favourite SNMP get utility, such as:
24 |
25 | $ snmpget -v 2c -c any localhost .1.3.6.1.2.1.1.5.0
26 |
27 | ## Installation
28 |
29 | $ npm install snmpjs
30 |
31 | ## License
32 |
33 | MIT.
34 |
35 | ## Bugs
36 |
37 | See .
38 |
--------------------------------------------------------------------------------
/agent.js:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env node
2 | /*
3 | * Copyright (c) 2012, Joyent, Inc. All rights reserved.
4 | */
5 |
6 | var snmp = require('./lib/index.js');
7 | var mib = require('./lib/mib/index.js');
8 | var bunyan = require('bunyan');
9 | var fs = require('fs');
10 |
11 | var config = process.argv[2] || 'agent.json';
12 | var cfstr = fs.readFileSync(config);
13 | var cf, log_cf;
14 | var log, agent;
15 |
16 | cf = JSON.parse(cfstr);
17 | log_cf = cf.log || {
18 | name: 'snmpd',
19 | level: 'trace'
20 | };
21 |
22 | log = new bunyan(log_cf);
23 |
24 | agent = snmp.createAgent({
25 | log: log
26 | });
27 |
28 | /* XXX MIB configuration */
29 |
30 | agent.request(mib);
31 |
32 | agent.bind({ family: 'udp4', port: 161 });
33 |
--------------------------------------------------------------------------------
/agent.json:
--------------------------------------------------------------------------------
1 | {
2 | "log": {
3 | "name": "snmpd",
4 | "level": "trace"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/docs/agent.restdown:
--------------------------------------------------------------------------------
1 | ---
2 | title: Agent API | snmpjs
3 | markdown2extras: wiki-tables
4 | ---
5 |
6 | # snmpjs Agent API
7 |
8 | Agents are software components that make data available to management
9 | applications via SNMP. They are analogous to HTTP servers. With snmpjs, it is
10 | as easy to implement an SNMP agent as it is to implement an HTTP server using
11 | built-in [Node.js][] functionality. The agent framework provides only a few
12 | interfaces, which are described in the remainder of this document.
13 |
14 | # createAgent(options)
15 |
16 | This function creates and returns an Agent object. Its sole argument, which is
17 | optional, contains any or all of the following members:
18 |
19 | {
20 | name: [String],
21 | dtrace: [Object (DTraceProvider)],
22 | log: [Object (bunyan)]
23 | }
24 |
25 | The interfaces provided by the Agent object are described in detail below.
26 |
27 | ### name
28 |
29 | If present, the `name` member will be used to name the DTrace provider and to
30 | decorate log output. The default value is `'snmpjs'`.
31 |
32 | ### dtrace
33 |
34 | If present, the agent's DTrace probes will be appended to the existing provider
35 | referenced by the `dtrace` member. Otherwise, a new provider identified by the
36 | `name` member (or its default value if absent) will be created.
37 |
38 | ### log
39 |
40 | If present, the agent will add its own child logger to the existing [Bunyan][]
41 | logger referenced by the `log` member. Otherwise, a new logger identified by
42 | the `name` member (or its default value if absent) will be created.
43 |
44 | # Agent
45 |
46 | The Agent object, created by a call to `createAgent`, provides interfaces for
47 | defining where the agent listens for requests and what it should do with them.
48 | An agent will not receive any requests until it is bound to an endpoint that
49 | generates them, and it will respond with errors to all requests if no providers
50 | have been loaded.
51 |
52 | ## Agent.request(prov)
53 |
54 | This method attaches one or more MIB providers to the agent, allowing it to
55 | usefully handle requests. The sole argument must be either a provider
56 | descriptor as specified by the [provider API][], or an array of such objects.
57 |
58 | ## Agent.bind(endpoint, cb)
59 |
60 | Analogous to the `dgram.bind()` method, this method causes the agent to bind a
61 | newly-created socket to the specified IPv4 or IPv6 UDP port and begin receiving
62 | messages. The endpoint argument must be an object with the following members:
63 |
64 | {
65 | family: [String],
66 | port: [Number],
67 | [ addr: [String] ]
68 | }
69 |
70 | The `family` member must be a string acceptable to `dgram.createSocket()`. The
71 | `port` member must be an integer in \[1, 65535\], and otherwise acceptable to
72 | `dgram.bind()`. If present, the optional `addr` member will be used to restrict
73 | the IPv4 or IPv6 address on which the agent will receive messages; by default,
74 | it will receive messages on all addresses of the specified address family.
75 |
76 | If provided, the optional argument cb must be a function; it will be invoked
77 | when the 'listening' event is emitted by the underlying socket.
78 |
79 | This method may be called multiple times, such that it is possible to receive
80 | incoming messages on any number of endpoints.
81 |
82 | ## Agent.close()
83 |
84 | Analogous to the `dgram.close()` method, this method causes the agent to cease
85 | receiving incoming requests on all previously-bound endpoints. It takes no
86 | arguments.
87 |
88 | ---
89 | [provider API]: provider.html
90 | [Node.js]: http://nodejs.org
91 | [Bunyan]: https://github.com/trentm/node-bunyan
92 |
--------------------------------------------------------------------------------
/docs/index.restdown:
--------------------------------------------------------------------------------
1 | ---
2 | title: snmpjs
3 | markdown2extras: wiki-tables, code-friendly
4 | apisections:
5 | ---
6 |
7 |
11 |
12 | # Overview
13 |
14 | snmpjs is a pure JavaScript, from-scratch framework for implementing [SNMP][]
15 | agents, trap generators, and management applications in [Node.js][]. It is
16 | intended primarily to enable the construction of a simple, general-purpose agent
17 | as an alternative to Net-SNMP.
18 |
19 | var os = require('os');
20 | var snmp = require('snmpjs');
21 | var logger = require('bunyan');
22 |
23 | var log = new logger({
24 | name: 'snmpd',
25 | level: 'info'
26 | });
27 |
28 | var agent = snmp.createAgent({
29 | log: log
30 | });
31 |
32 | agent.request({ oid: '.1.3.6.1.2.1.1.5', handler: function (prq) {
33 | var nodename = os.hostname();
34 | var val = snmp.data.createData({ type: 'OctetString',
35 | value: nodename });
36 |
37 | snmp.provider.readOnlyScalar(prq, val);
38 | } });
39 |
40 | agent.bind({ family: 'udp4', port: 161 });
41 |
42 | Try hitting that with your favourite SNMP get utility, such as:
43 |
44 | $ snmpget -v 2c -c any localhost .1.3.6.1.2.1.1.5
45 |
46 | # Features
47 |
48 | snmpjs allows you to implement agents, trap generators, and management
49 | applications that support the full range of SNMPv1 and SNMPv2c functionality.
50 | It is mostly compatible with all relevant standards -- see notes below -- and
51 | can also be extended to handle (and, optionally, to generate) SNMP messages that
52 | incorporate nonstandard data types and other deviations. Several pieces of
53 | standard MIB functionality are included along with a simple agent, but you can
54 | easily implement your own MIB subtrees or alternative agent. The library can
55 | also be used to incorporate SNMP functionality into other daemons; for example,
56 | to allow them to generate traps if a fault condition is detected.
57 |
58 | # Getting Started
59 |
60 | $ npm install snmpjs
61 |
62 | If you're new to SNMP, check out the [gentle introduction][]. Otherwise, the
63 | API documentation is:
64 |
65 | ||[agent][]||Reference for implementing SNMP agents.||
66 | ||[mib][]||API reference for the Management Information Base (MIB).||
67 | ||[protocol][]||API reference for low-level message encoding and decoding.||
68 | ||[provider][]||API reference for MIB providers.||
69 |
70 | # What's Not Included
71 |
72 | Many pieces of functionality that would be useful are missing. The most
73 | noteworthy, some of which are really needed in order to create a complete suite
74 | of standard MIB providers, include:
75 |
76 | - Agent: handling GetBulkRequests (*)
77 | - Agent: error responses in the face of unparseable messages
78 | - Agent: handling of excessively large responses (i.e., tooBig)
79 | - Agent: access control; there is none at all presently (*)
80 | - Agent: AgentX support, for aggregating disjoint MIBs on the same host and
81 | allowing multiple daemons to provide their own MIB subtrees
82 | - Agent: introspection; i.e., an interface for providers to access information
83 | about the agent itself
84 | - Agent: provider configuration; i.e., an interface for making agent
85 | configuration data available to MIB providers
86 | - Other: More standard MIB providers
87 | - Other: convenience functions for generating traps
88 | - Management: command-line utilities for sending requests and displaying
89 | responses
90 | - MIB: a MIB definition parser (to decorate the MIB for the benefit of
91 | management applications or for strict checking of provider responses)
92 | - Provider: Convenience functions for implementing tabular providers; e.g.,
93 | lexicographically-sorting data structures
94 | - All: SNMPv3 support
95 |
96 | (*) These items are really bugs that have practical solutions; they will be
97 | addressed in subsequent revisions of the software.
98 |
99 | # Conformance
100 |
101 | In general, deviations from the relevant specifications are considered bugs and
102 | should have issues opened to track them. However, there is one notable
103 | exception, in which the design of snmpjs precludes an effective implementation
104 | of required behaviour. Specifically, SetRequests, clearly intended by the
105 | standard to be treated as an atomic transaction when multiple varbinds are
106 | present, are not handled that way. Instead, they are treated as multiple
107 | independent requests that can succeed or fail without affecting the others. If
108 | a failure occurs, the first varbind to cause a failure will be highlighted by
109 | the `error_index` field in the response, and the nature of the error will be
110 | specified by `error_status`. Subsequent varbinds may or may not have been
111 | assigned successfully. The `commitFailed` and `undoFailed` status codes are not
112 | used. This approach dramatically simplifies the implementation (the standard
113 | disdainfully yet correctly calls this "taking the easy way out"), and practical
114 | use of SNMP for control operations where such transactionality is required is
115 | exceedingly rare. If you are unfortunate enough to need it, however, you will
116 | need to select an agent that is not based on snmpjs.
117 |
118 | # More Information
119 |
120 | ||License||[MIT][]||
121 | ||Code||[joyent/node-snmpjs][]||
122 | ||node.js version||0.6.x||
123 |
124 | ---
125 | [SNMP]: http://tools.ietf.org/html/rfc3411
126 | [Node.js]: http://nodejs.org
127 | [gentle introduction]: snmp.html
128 | [agent]: agent.html
129 | [mib]: mib.html
130 | [protocol]: protocol.html
131 | [provider]: provider.html
132 | [joyent/node-snmpjs]: https://github.com/joyent/node-snmpjs
133 | [MIT]: http://www.opensource.org/licenses/mit-license.php
134 |
--------------------------------------------------------------------------------
/docs/media/img/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TritonDataCenter/node-snmpjs/c4a77ef8556c47bfd35cb02a5c9d4876c117d99f/docs/media/img/favicon.ico
--------------------------------------------------------------------------------
/docs/media/img/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TritonDataCenter/node-snmpjs/c4a77ef8556c47bfd35cb02a5c9d4876c117d99f/docs/media/img/logo.png
--------------------------------------------------------------------------------
/docs/mib.restdown:
--------------------------------------------------------------------------------
1 | ---
2 | title: MIB API | snmpjs
3 | markdown2extras: wiki-tables
4 | ---
5 |
6 | # snmpjs MIB API
7 |
8 | The MIB is the key-value database on which an SNMP agent operates on behalf of
9 | management applications. A MIB contains information about the set of object
10 | types and identifiers known to the SNMP entity. The agent uses this to store
11 | information about how to act on requests that it received; a management
12 | application may wish to store information about how to display data items
13 | received from an agent, how to validate the format of values being sent to an
14 | agent, or how to translate between numeric object identifiers and the
15 | human-readable text that names those objects. The MIB functionality in snmpjs
16 | can be used for any of these purposes, though the implementation provides only
17 | basic functionality and leaves the details of these activities to consumers.
18 |
19 | ## createMIB(def)
20 |
21 | # MIB
22 |
23 | A MIB object, created by a call to `snmp.createMIB`, provides simple interfaces
24 | for adding and searching for SNMP objects within the MIB.
25 |
26 | ## MIB.add(def)
27 |
28 | This method adds a node, and any previously missing ancestor nodes, to the MIB.
29 | Its sole argument, which is required, may be any of the following: an OID in
30 | string form, an OID in array form, or any object containing a member `oid` of
31 | either form. If an object is provided, only the `oid` member will be examined
32 | and the object will not be modified.
33 |
34 | ## MIB.lookup(oid)
35 |
36 | This method returns the `MIBNode`, if any, that either matches exactly the
37 | specified OID or, if there is no such node, is the nearest ancestor present in
38 | the MIB. The sole argument, which is required, must be an OID in either string
39 | or array form. The returned node may be inspected to determine whether an exact
40 | match was found in the MIB; see the `MIBNode` interfaces specified below.
41 |
42 | ## MIB.next_match(arg)
43 |
44 | This method iterates over the MIB or a portion thereof, in lexicographical order
45 | as defined by ASN.1, and returns the first node that satisfies caller-specified
46 | criteria. Its sole argument, which is required, must be an object with the
47 | following members:
48 |
49 | {
50 | node: [Object (MIBNode)],
51 | match: [Function],
52 | [ start: [Number] ]
53 | }
54 |
55 | The `node` specifies the starting point for the search. If the `start` member
56 | is present, only children whose first OID component *after* `node`'s name is at
57 | least `start` will be evaluated. For example, if `node` refers to a `MIBNode`
58 | whose `oid` member contains `'1.3.6.1'` and the `start` member contains `6`, the
59 | first node eligible for evaluate after `node` itself would have OID 1.3.6.1.6.
60 | Without specifying the `start` member, the first node eligible for evaluation
61 | after `node` itself would have OID 1.3.6.1.0; because that OID references an
62 | instance and the MIB does not contain instances, the first node that could
63 | actually be evaluated would have OID 1.3.6.1.1.
64 |
65 | The `match` member references a function used to evaluate nodes. Its sole
66 | argument is an object of type `MIBNode`. If it returns `true`, the search will
67 | stop and the most recently evaluated node will be returned by `next_match`.
68 | Otherwise, the search will continue.
69 |
70 | If the MIB is exhausted without satisfying the matching criteria, `null` is
71 | returned.
72 |
73 | # MIB Nodes
74 |
75 | The entries in the MIB are part of a tree, in which each subtree contains the
76 | portion of the OID space that shares a common prefix. The length of the path
77 | from a given node to the root of the tree is equal to the number of integer
78 | components in the node's OID. The MIB contains only object identifiers and
79 | object types; it does not contain any instances. Nodes in the MIB are
80 | represented by objects descended from `MIBNode`; objects of this type cannot be
81 | created directly but are created when new objects are added to the MIB. The
82 | remainder of this section describes their interfaces.
83 |
84 | ## MIBNode.oid
85 |
86 | This member contains the string form of the node's OID.
87 |
88 | ## MIBNode.addr
89 |
90 | This member contains an array of integers in \[0, 2^31 - 1\] representing the
91 | components of the node's OID.
92 |
93 | ## MIBNode.parent
94 |
95 | This member holds a reference to the node's parent node, which is either an
96 | object descended from `MIBNode` or `null` (if the node is the root node).
97 |
98 | ## MIBNode.child(idx)
99 |
100 | This method returns the child node whose last OID component is `idx`, which must
101 | be an integer in \[0, 2^31 - 1\]. If no such child exists, `null` is returned.
102 |
103 | ## MIBNode.listChildren(start)
104 |
105 | This method returns a dense array of integers, each of which is the index of a
106 | valid child of this node, such that it may be passed to `node.child()` and
107 | result in a non-`null` return value. If no matching children exist, an empty
108 | array is returned.
109 |
110 | The single argument, which is optional, is the lowest child index that should be
111 | included in the array. By default, all child indices are included.
112 |
113 | ## MIBNode.decorate(arg)
114 |
115 | MIB nodes may be decorated with consumer-specific data, which can later be
116 | retrieved when looking them up. Each consumer may add and retrieve its own
117 | decorations by using a unique tag.
118 |
119 | This method attaches a decorative object to a MIB node. Its sole argument,
120 | which is required, must be an object of the form:
121 |
122 | {
123 | tag: [String],
124 | obj: [Any]
125 | }
126 |
127 | The `obj` member's contents will be attached to the node as a decoration using
128 | the specified `tag`. As is customary throughout snmpjs, the portion of the tag
129 | namespace beginning with the `_` character is reserved for snmpjs's internal
130 | use, and must not be polluted by consumers. The effects of doing so are
131 | undefined.
132 |
133 | If a node has already been decorated with an object associated with the same tag
134 | that is specified in the argument, the previous decoration is replaced.
135 |
136 | ## MIBNode.decor(tag)
137 |
138 | This method returns the object or primitive that was previously associated with
139 | this node by a call to `MIBNode.decorate()`. The sole argument must be a string
140 | naming the tag under which the decoration was attached. If no decoration with
141 | the specified tag has been attached to the node, `undefined` is returned.
142 |
143 | ## MIBNode.isAncestor(oid)
144 |
145 | This method returns `true` if the node is an ancestor of the object or instance
146 | (which need not exist in the MIB) named by its sole argument, which may be an
147 | OID in string or array form. Otherwise it returns `false`.
148 |
149 | ## MIBNode.isDescended(oid)
150 |
151 | This method returns `true` if the node is a descendant of the object or instance
152 | (which need not exist in the MIB) named by its sole argument, which may be an
153 | OID in string or array form. Otherwise it returns false. Note that if the
154 | specified OID does not exist in the MIB, the node cannot possibly be its
155 | descendent, as the MIB always contains a node for every OID that is an ancestor
156 | of any object added to it.
157 |
158 | ---
159 |
--------------------------------------------------------------------------------
/docs/provider.restdown:
--------------------------------------------------------------------------------
1 | ---
2 | title: MIB Provider API | snmpjs
3 | markdown2extras: wiki-tables
4 | ---
5 |
6 | # snmpjs MIB Provider API
7 |
8 | MIB providers are software components that know how to handle and respond to
9 | requests for SNMP data within a portion of the MIB. Once a MIB provider has
10 | been registered with an agent, the agent will begin directing requests for the
11 | provider's MIB subtree to the provider's handler function(s), which are expected
12 | to notify the agent via a callback when they have determined the correct
13 | response to the request. A MIB provider has no knowledge of any part of the MIB
14 | other than the one for which it is responsible.
15 |
16 | # MIB Provider descriptors
17 |
18 | MIB provider descriptors are made available by providers for use by software
19 | incorporating SNMP agent functionality. Consumers collect and pass these
20 | descriptors to the `Agent.request` method to register them with the agent. Your
21 | provider module must therefore export its descriptor(s) and should document how
22 | they are structured. You are strongly encouraged to make your module's
23 | `exports` member a single Array object containing provider descriptors for each
24 | object your provider supports (see the mib-2 provider included with snmpjs for
25 | an example).
26 |
27 | A MIB provider descriptor is any object instance with the following members:
28 |
29 | {
30 | oid: [String],
31 | handler: [Function] or [Array of Function],
32 | [ columns: [Array of Number] ]
33 | }
34 |
35 | or a dense Array containing any number of such objects.
36 |
37 | Example:
38 |
39 | {
40 | oid: '.1.3.6.1.2.1.1.1',
41 | handler: _mib2_system_descr_handler
42 | }
43 |
44 | Members are described in detail as follows:
45 |
46 | ## oid
47 |
48 | The `oid` member must be present, and must be a string. It specifies the
49 | address within the MIB of either a scalar (singleton) object or an object
50 | representing a table entry type. It must not contain any components identifying
51 | an instance. For example:
52 |
53 | '.1.3.6.1.2.1.1.1'
54 |
55 | is the OID of `.iso.org.dod.internet.mgmt.mib-2.system.1` or `sysDescr`. It
56 | would be incorrect to specify this as `'.1.3.6.1.2.1.1.1.0'` because the latter
57 | OID contains the instance number of the (singleton) instance of this object
58 | within the MIB.
59 |
60 | Similarly, if the provider manages a table within the MIB, its oid should
61 | reflect the entry object type, not that of the table itself nor any of its
62 | columns or rows. For example:
63 |
64 | '.1.3.6.1.2.1.2.2.1'
65 |
66 | is the OID of `.iso.org.dod.internet.mgmt.mib-2.interfaces.ifTable.1` or
67 | `ifEntry`. Do not specify any of the columns below this node in the MIB such as
68 | ifIndex (`.1.3.6.1.2.1.2.2.1.1`).
69 |
70 | ## handler
71 |
72 | The `handler` is a function or array of functions that will be invoked when a
73 | request is received that is within the provider's subtree. If this object is an
74 | array, each handler will be invoked in an arbitrary order.
75 |
76 | Each handler function will be invoked with a single argument of type
77 | `ProviderRequest`, described in detail below.
78 |
79 | ## columns
80 |
81 | The provider descriptor for a tabular object specifies the OID of the entry type
82 | in its `oid` member and a dense array of column numbers in the `columns` member.
83 | It is not required that the column identifier space be dense, nor is it required
84 | that the identifiers be ordered within the array. Each entry is a positive
85 | integer less than 2^31 specifying the last OID component of a column in the
86 | table entry type managed by this provider. For example, the `ifEntry` provider
87 | has columns numbered 1 through 22 inclusive, so the `columns` member of its
88 | provider definition would be a dense array of those integers.
89 |
90 | # Request Processing Overview
91 |
92 | When an agent receives a request that it associates with a MIB provider, it will
93 | invoke each of that provider's handler functions in succession. The handler(s)
94 | are responsible for performing the requested operation (if possible) and
95 | invoking a callback with the result, which may indicate that an error occurred.
96 | Exactly one of an object's handlers must invoke the completion callback; if
97 | multiple handlers invoke the completion callback, the result is undefined. This
98 | behaviour may change in a future API revision.
99 |
100 | Requests are directed to a provider as `ProviderRequest` instances, described in
101 | detail below. An agent will not direct a request to a provider that has not
102 | been registered with it, nor will it direct requests that are outside a
103 | provider's registered portion of the MIB.
104 |
105 | The `ProviderRequest` instance contains a complete description of the requested
106 | operation and a completion callback. There are four basic operation types that
107 | a provider may receive, which are defined by [RFC 3416][]. Because the language
108 | in the standard is somewhat complex, a simplified version is presented here with
109 | a focus on the facilities provided by snmpjs. Recall that there are two basic
110 | types of provider: one that handles scalar data items and one that handles
111 | tabular arrays of data items. We will examine the necessary processing for each
112 | in turn. There are also four request types, defined by relevant standards, that
113 | any provider must handle; each will be described here in brief.
114 |
115 | ### Operation Types
116 |
117 | The operation types are defined in [RFC 3416][] and, for convenience, have
118 | symbolic constants associated with them attached to the `snmp.pdu` object
119 | exported by snmpjs. They are:
120 |
121 | ||GetRequest||Read the single value specified by the request object||
122 | ||SetRequest||Set the single value specified by the request object and value||
123 | ||GetNextRequest||Read the single value FOLLOWING the request object||
124 | ||GetBulkRequest||Read one or more values FOLLOWING the request object(s)||
125 |
126 | The Get and Set requests do exactly what their names imply: retrieve or attempt
127 | to change a specific data value.
128 |
129 | The GetNext and GetBulk requests are used to walk the entire MIB or a subtree,
130 | by issuing a series of requests such that the first request specifies the base
131 | of the subtree and each subsequent request specifies the OID of the instance
132 | returned by the previous request. Such requests may be issued until the
133 | management application reaches some defined stopping condition (such as
134 | receiving a response for an instance beyond the end of the desired subtree) or
135 | the MIB contents are exhausted. The functionality offered by GetBulk is
136 | somewhat complex, but is logically equivalent to a series of GetNext requests.
137 |
138 | A more detailed discussion of how to handle each of these operations correctly
139 | follows in the sections below for each type of provider.
140 |
141 | ### Results
142 |
143 | Each operation has at least one result, which must be passed to the completion
144 | callback. In the case of GetBulk requests directed to a tabular data provider,
145 | there may be multiple results; in that case, the results should be passed to the
146 | completion callback as members of a dense array. Each such result must be one
147 | of the following:
148 |
149 | #### undefined
150 |
151 | If the completion callback is invoked with no argument or an argument of type
152 | `undefined`, the agent will interpret this to mean that the requested instance
153 | does not exist within the subtree. If the request was of type GetRequest or
154 | SetRequest (see `ProviderRequest.op` below), this will result in an error being
155 | returned to the management application in this varbind. If the request was of
156 | type GetNextRequest or GetBulkRequest, the agent will retry the request in the
157 | next MIB subtree for which a provider has been registered, or return an error
158 | indicating that the MIB view has been exhausted if none exists. This behaviour
159 | is intended to be compliant with [RFC 3416][].
160 |
161 | #### An integer error code
162 |
163 | If the requested instance exists but a fatal error occurred during retrieval of
164 | the requested data value for that instance, an integer corresponding to the
165 | appropriate error code must be passed to the completion callback. Any of the
166 | error codes defined for the `error-status` object in section 3 of [RFC 3416][]
167 | may be passed; for convenience, the `snmp.pdu` object has symbolic constants
168 | attached to it associated with these error status values.
169 |
170 | Note that most operations are not permitted by the relevant standards to return
171 | all of these error codes. If a provider responds with an error code that is
172 | prohibited to the operation requested, the agent's behaviour is unspecified.
173 | The detailed guidance below for implementing each type of provider describes the
174 | circumstances in which each error code should be used.
175 |
176 | #### A varbind object (or an array thereof)
177 |
178 | Varbind objects are those created by calls to `snmp.varbind.createVarbind`.
179 | Most requests should result in the generation of one of these objects (in the
180 | case of GetRequest, SetRequest, and GetNextRequest) or an array containing
181 | several of these objects (in the case of GetBulkRequest). A varbind is simply a
182 | key/value pair in which the key is an OID and the value is a typed data value
183 | created by `snmp.data.createData`. In all cases, the `oid` member should be set
184 | to the OID of the instance whose value is being provided, which in some cases
185 | may be different from the OID that was requested.
186 |
187 | ### Scalar Providers
188 |
189 | To a scalar provider, Get, GetNext, and GetBulk requests can all be handled in
190 | exactly the same way. The agent will not direct any of these requests to a
191 | scalar provider that should not be satisfied by the singleton instance's data
192 | value. That is, a scalar provider should ignore whether the request is for the
193 | singleton instance or the object itself, whether the request is for the named
194 | instance or the next instance, and how many instances (in the case of GetBulk)
195 | are requested.
196 |
197 | To handle a Get, GetNext, or GetBulk request:
198 |
199 | 1. Retrieve or compute the data item requested. If the data item's value could
200 | not be computed, invoke the completion callback with the single argument
201 | `snmp.pdu.genErr`.
202 | 1. Use `snmp.data.createData` to construct a data object of the appropriate type
203 | with the value obtained during the previous step.
204 | 1. Use `snmp.varbind.createVarbind` to construct a varbind object from the data
205 | object created in the previous step. The OID of the varbind must be the OID of
206 | the singleton instance (i.e., it must end in `.0`).
207 | 1. Invoke the completion callback with the varbind object as its sole argument.
208 |
209 | To handle a Set request:
210 |
211 | 1. If the object is not modifiable, invoke the completion callback with the
212 | single argument `snmp.pdu.notWritable`. This addresses clauses (2) and (9) in
213 | section 4.2.5 of [RFC 3416][].
214 | 1. Inspect the value provided according to the rules described in section 4.2.5
215 | of [RFC 3416][], specifically clauses (3) through (6) and (10). If the value
216 | fails any of the specified criteria, invoke the completion callback with a
217 | single argument of `snmp.pdu.wrongType`, `snmp.pdu.wrongLength`,
218 | `snmp.pdu.wrongEncoding`, `snmp.pdu.wrongValue`, or
219 | `snmp.pdu.inconsistentValue`, respectively.
220 | 1. Attempt to make the requested operation take effect. The meaning of this is
221 | specific to the semantics of the MIB subtree and may include persisting
222 | something in a database, altering a system parameter, or performing an operation
223 | on a remote server. If the operation fails because of resource exhaustion or
224 | unavailability (even if retryable), invoke the completion callback with a single
225 | argument of `snmp.pdu.resourceUnavailable`. If the operation fails for any
226 | other reason, invoke the completion callback with a single argument of
227 | `snmp.pdu.genErr`.
228 | 1. Use `snmp.data.createData` to construct a data object of the appropriate type
229 | with the value assigned during the previous step.
230 | 1. Use `snmp.varbind.createVarbind` to construct a varbind object from the data
231 | object created in the previous step. The OID of the varbind must be the OID of
232 | the singleton instance (i.e., it must end in `.0`).
233 | 1. Invoke the completion callback with the varbind object as its sole argument.
234 |
235 | ### Tabular Providers
236 |
237 | To handle a GetRequest:
238 |
239 | 1. Determine whether the instance requested refers to a row in the table that
240 | exists. If not, or if the object itself was requested, invoke the completion
241 | callback with no argument.
242 | 1. Retrieve or compute the data item requested. If the data item's value could
243 | not be computed, invoke the completion callback with the single argument
244 | `snmp.pdu.genErr`.
245 | 1. Use `snmp.data.createData` to construct a data object of the appropriate type
246 | with the value obtained during the previous step.
247 | 1. Use `snmp.varbind.createVarbind` to construct a varbind object from the data
248 | object created in the previous step. The OID of the varbind must be the OID of
249 | the instance requested.
250 | 1. Invoke the completion callback with the varbind object as its sole argument.
251 |
252 | To handle a SetRequest:
253 |
254 | SetRequests are handled in the same way as by scalar providers, with the
255 | following exceptions:
256 |
257 | 1. Prior to performing the procedures described above, determine whether the
258 | instance requested refers to a row in the table exists. If not, and it cannot
259 | be created, or if the object itself was requested, or if the provider does not
260 | support modifying its data, invoke the completion callback with the single
261 | argument `snmp.pdu.notWritable`.
262 | 1. In the second step, clauses (7) and (8) must be considered as well, and the
263 | completion callback invoked with a single argument of `snmp.pdu.noCreation` or
264 | `snmp.pdu.inconsistentName`, respectively.
265 |
266 | To handle a GetNextRequest:
267 |
268 | 1. Determine which instance, if any, immediately follows that specified in the
269 | request in the lexicographically ordered OID space, or would if such an instance
270 | existed. Examples of lexicographic OID ordering may be found in [RFC 3416][]
271 | sections 4.2.2.1 and 4.2.3.1. If there is no such instance in the column,
272 | invoke the completion callback with no argument.
273 | 1. Retrieve or compute the data item associated with the instance identified in
274 | the previous step. If the data item's value could not be computed, invoke the
275 | completion callback with the single argument `snmp.pdu.genErr`.
276 | 1. Use `snmp.data.createData` to construct a data object of the appropriate type
277 | with the value obtained during the previous step.
278 | 1. Use `snmp.varbind.createVarbind` to construct a varbind object from the data
279 | object created in the previous step. The OID of the varbind must be the OID of
280 | the instance identified in the first step above (i.e., not that which was
281 | referenced in the request).
282 | 1. Invoke the completion callback with the varbind object as its sole argument.
283 |
284 | To handle a GetBulkRequest:
285 |
286 | A GetBulkRequest is handled in the same manner as a series of GetNextRequests in
287 | which each request's OID after the first is the OID of the response to the
288 | previous request. The only exception is that the completion callback should not
289 | be invoked until all iterations have been performed and the result of each
290 | iteration stored in a dense array. The completion callback must be invoked with
291 | the single argument of this array, each element of which is the result of the
292 | corresponding iteration. If an iteration's result if `undefined`, all
293 | subsequent results must be `undefined` as well. If a single iteration is
294 | requested, the use of the array to store the result is optional; it may instead
295 | be passed directly to the completion callback.
296 |
297 | ### Important Notes
298 |
299 | 1. If a GetNext or GetBulk request is received by a tabular provider for a
300 | column object itself (i.e., no instance is identified), the response should
301 | refer to the first instance in the table according to lexicographical OID
302 | ordering. If the table is empty or no row in the table has a value in the
303 | requested column, the result of this portion of the operation is the empty
304 | value; i.e., `undefined`. If the table is not empty but the first row(s) have
305 | no value in the requested column, the value should be returned from the first
306 | row in the column which does have a value.
307 |
308 | 1. The agent does not validate the type of the data object passed by a provider
309 | back to the agent, nor passed into a provider as the value of a SetRequest,
310 | against any MIB definition. Values of incorrect or unexpected type passed to
311 | the agent by a provider will be returned to the management application
312 | unmodified. This behaviour may change in a future API revision.
313 |
314 | 1. The processing of Set requests is clearly intended by the standard to treat
315 | multiple varbinds within a single set request message as an atomic transaction.
316 | This implementation does not provide those semantics and does not conform to the
317 | transactionality requirements laid out in [RFC 3416][] section 4.2.5. While the
318 | use of SNMP for control operations is somewhere between uncommon and nonexistent
319 | in practice, be aware that if your deployment does expect to perform control
320 | operations, agents based on snmpjs must not be used if you expect or require the
321 | atomicity specified by [RFC 3416][] section 4.2.5.
322 |
323 | 1. For scalar providers whose data items are relatively inexpensive to retrieve,
324 | portions of the above procedures may be performed automatically by using the
325 | `snmp.provider.readOnlyScalar` or `snmp.provider.writableScalar` interfaces.
326 | Specifically, these interfaces will create the varbind objects from data value
327 | objects and, in the case of read-only values, will reject SetRequests
328 | appropriately. They also invoke the completion callback.
329 |
330 | 1. The `snmp.provider.readOnlyScalar` and `snmp.provider.writableScalar` utility
331 | routines must not be used by tabular providers.
332 |
333 | # ProviderRequest
334 |
335 | This section describes the request objects passed to providers; the control flow
336 | is described above.
337 |
338 | Instances of ProviderRequest provide no methods. All members and all contents
339 | of those members are read-only and must not be modified by consumers. Any
340 | attempt to modify a member will result in an exception. Any attempt to modify
341 | the contents of a member will result either in an exception or undefined
342 | behaviour.
343 |
344 | ## ProviderRequest.done
345 |
346 | This callback function must be invoked by exactly one handler for each MIB
347 | provider to which a request is issued. Its sole argument is the result of the
348 | requested operation, which is interpreted by the agent as described above.
349 |
350 | ## ProviderRequest.op
351 |
352 | The requested operation, which will be one of the four request types in
353 | `snmp.pdu.{Get,GetNext,GetBulk,Set}Request`.
354 |
355 | The complete set of rules defining each of these operations may be found in
356 | [RFC 3416][], but the discussion in this document should be sufficient to write
357 | correct MIB providers for many types of commonly-encountered data. Operations
358 | of other types will not be passed to a MIB provider.
359 |
360 | ## ProviderRequest.oid
361 |
362 | The OID found in the actual request from the management application, as a
363 | string.
364 |
365 | Example:
366 |
367 | '.1.3.6.1.2.1.2.2.1.3.14'
368 |
369 | ## ProviderRequest.addr
370 |
371 | The OID found in the actual request from the management application, as an array
372 | of non-negative integers less than 2^31.
373 |
374 | Example:
375 |
376 | [ 1, 3, 6, 1, 2, 1, 2, 2, 1, 3, 14 ]
377 |
378 | ## ProviderRequest.value
379 |
380 | The value to which the management application requested that this instance be
381 | set, in the case of a SetRequest. If the `op` member is other than
382 | `snmp.pdu.SetRequest`, this member will be absent or undefined and should be
383 | ignored by the provider.
384 |
385 | The member is an object instance descended from `snmp.data.SnmpData` and
386 | provides the interfaces defined in the [Protocol API][].
387 |
388 | ## ProviderRequest.node
389 |
390 | A `MIBNode` object corresponding to the location in the MIB where the provider
391 | for this subtree was located. This object provides the interfaces described in
392 | the [MIB API][].
393 |
394 | ## ProviderRequest.instance
395 |
396 | The instance portion of the object identifier to which this request pertains, as
397 | an array of non-negative integers less than 2^31. This is the portion after the
398 | column number in a table entry or `[ 0 ]` for scalars. If the request pertains
399 | to something that it not an instance, this member will be undefined or absent.
400 |
401 | Note that the correct interpretation of this value may require it to be
402 | translated into some other form; e.g., a string. The agent has no awareness of
403 | the expected type of the index into a table, even if it is defined by a MIB
404 | definition. This behaviour may change in a future API revision.
405 |
406 | Example:
407 |
408 | [ 14 ]
409 |
410 | ## ProviderRequest.iterate
411 |
412 | The number of consecutive instances following the specified instance for which
413 | data is requested. This will be set to 1, absent, or undefined unless the
414 | requested operation is GetBulkRequest, and may be ignored by providers for all
415 | other operations.
416 |
417 | If this member is set to 1 and the operation type is GetBulkRequest, the
418 | provider should behave exactly as it would if the operation type were
419 | GetNextRequest.
420 |
421 | # Convenience Interfaces
422 |
423 | While the interfaces described above are sufficient to write any MIB provider
424 | that may be needed, several convenience interfaces are also available to reduce
425 | the amount of boilerplate code required to do so. Use of these interfaces is
426 | optional but recommended where appropriate.
427 |
428 | These interfaces may be found as children of the `snmp.provider` object.
429 |
430 | ## readOnlyScalar(prq, rsd)
431 |
432 | This function constructs the appropriate response to the `ProviderRequest` `prq`
433 | from the data object `rsd`, then invokes the request's completion callback. It
434 | is appropriate only for scalar entities whose values cannot be changed by a
435 | SetRequest.
436 |
437 | Example:
438 |
439 | function
440 | myProvider(prq)
441 | {
442 | var val = snmp.data.createData({ type: 'OctetString',
443 | value: 'foo' });
444 | snmp.provider.readOnlyScalar(prq, val);
445 | }
446 |
447 | In general it is assumed that data may take some time to acquire from its
448 | source, in which case this can be done asynchronously:
449 |
450 | function
451 | myProvider(prq)
452 | {
453 | slowOperation(function (result, err) {
454 | var val;
455 |
456 | if (err) {
457 | prq.done(snmp.pdu.genErr);
458 | } else {
459 | var val = snmp.data.createData({
460 | type: 'OctetString',
461 | value: result
462 | });
463 | snmp.provider.readOnlyScalar(prq, val);
464 | }
465 | });
466 | }
467 |
468 | Note that if the computation of the result is especially expensive, it may be
469 | preferable to short-circuit the error case in which `prq.op` indicates a
470 | SetRequest. This convenience function handles this case correctly, but only
471 | after the response has been computed.
472 |
473 | ## writableScalar(prq[, rsd])
474 |
475 | Analogous to `readOnlyScalar`, this function constructs an appropriate response
476 | to any request for a scalar data item that may be modified. In the case of a
477 | SetRequest, your provider must perform whatever actions are necessary to persist
478 | the modification or otherwise cause it to become effective, then invoke this
479 | function. Note that it is not necessary to pass any value argument if the
480 | operation was successful. In response to requests for other operations, it
481 | should obtain the value of the data item and pass it to this function as for
482 | `readOnlyScalar`.
483 |
484 | If a SetRequest operation fails because the data value could not be updated or
485 | is of inappropriate type or out of range, your provider should invoke the
486 | completion callback directly with the appropriate error code as described above.
487 |
488 | ---
489 | [RFC 3416]: http://www.ietf.org/rfc/rfc3416.txt
490 | [Protocol API]: protocol.html
491 | [MIB API]: mib.html
492 |
--------------------------------------------------------------------------------
/docs/snmp.restdown:
--------------------------------------------------------------------------------
1 | ---
2 | title: Introduction to SNMP and snmpjs
3 | markdown2extras: wiki-tables, code-friendly
4 | apisections:
5 | ---
6 |
7 | # Introduction to SNMP and snmpjs
8 |
9 | The Simple Network Management Protocol (SNMP) has two strikes against it right
10 | from the outset: its name includes the toxicity warning keyword 'simple', and
11 | its specification consists of literally dozens of RFCs (many now on their second
12 | or third revisions) that read like they were written by lawyers. Bit-twiddling
13 | lawyers. If you already know SNMP well, you can probably skip this document and
14 | go straight to the snmpjs API reference. If not, read on for a condensed --
15 | some might even say oversimplified -- explanation of just what snmpjs allows you
16 | to do.
17 |
18 | # In a Nutshell
19 |
20 | SNMP defines a typed key/value data store backed by the actual state of a
21 | running computer, and a remote retrieval mechanism using datagrams (UDP,
22 | basically). That's it. Because the early (and still most commonly deployed)
23 | variants included no meaningful authentication or privacy mechanisms, the
24 | database is often made read-only, and because the contents of the database
25 | represent the state of a live system, the database contents are normally treated
26 | by clients as uncacheable. Without reading any specifications or the source
27 | code to an implementation, it's easy to understand why its authors considered
28 | the protocol to be simple: it is.
29 |
30 | # Basic Terminology
31 |
32 | There are a number of other terms used in the standards, but these are the
33 | essential ones:
34 |
35 | - The database itself is called the *Management Information Base (MIB)*.
36 | - The entries in the database that describe data are called *object types*; the
37 | data items themselves are called *instances* of these object types.
38 | - The piece of software that brokers requests for data (the "server") and
39 | generates telemetry when data values change is called an *agent*.
40 | - The piece of software that makes requests (the "client") is called a
41 | *management application* or *manager*.
42 | - Finally, network hosts with agents or managers installed are called
43 | *entities*.
44 |
45 | # Access Control
46 |
47 | In the original SNMP specification, access to objects and their data was to be
48 | controlled by the exchange of a simple string, essentially a plaintext password,
49 | called the community. The intent was that all management applications and
50 | agents in an administrative domain would be configured as part of a single
51 | community and would exchange and verify the same community name. This
52 | mechanism, while extremely weak, is still in widespread use, though other,
53 | stronger, mechanisms have since been proposed and implemented.
54 |
55 | There are three main versions of SNMP in common deployment: 1, 2c, and 3.
56 | SNMPv1 is largely obsolete, replaced by the mostly-compatible and very similar
57 | v2c, so-named because it refers to "community-based v2" and preserves the same
58 | extremely weak but simple authentication mechanism present in v1. The original
59 | proposal for SNMPv2 incorporated a complex and unpopular security mechanism,
60 | eventually dropped in favour of v2c but later implemented as a component of v3.
61 | Nearly all SNMP management applications and agents support v2c, and, to the
62 | extent that they differ, v1 as well. While there are several software packages
63 | available that support SNMPv3, it is not widely deployed. The snmpjs software
64 | supports v1 and v2c only; it can neither parse nor generate a v3 message, and
65 | lacks utility routines for manipulating v3-specific objects.
66 |
67 | # The Management Information Base (MIB)
68 |
69 | The term MIB is used interchangeably to mean either the entire database an SNMP
70 | entity makes available to management applications, or a thematically unified
71 | subset of that information. For example, standards documents typically use "the
72 | MIB" to mean the whole database, while administrators frequently speak of
73 | support for "a MIB" to mean a subset of that data relevant to a particular
74 | application or system component ("the Sun FMA MIB"), or a text file that
75 | describes its format ("SUN-FM-MIB.mib"). It is important to note that the
76 | contents of such a file are purely advisory; management applications rely on
77 | them for metadata about what objects are likely to be accessible, how they
78 | should be accessed, and how their data contents should be presented, but there
79 | is no guarantee that an agent or management application will adhere to the
80 | specified syntax when transmitting a data object.
81 |
82 | The MIB contains a hierarchically-named set of objects, each of which may have
83 | zero or more instances. An instance is an object with which a single data item
84 | is associated; each data item has a type and a value. An object may have no
85 | instances either because it is specified to serve only as a hierarchical
86 | container for other objects or because the SNMP entity does not contain anything
87 | of the type represented by the object. For example, a network host with no
88 | storage devices would have no instances of an object that represents a disk
89 | drive. The object namespace is an ordered sequence of integers in the range
90 | \[0, 255\], and such a sequence is called an object identifier or OID. OIDs are
91 | typically written as strings in dotted-decimal notation; e.g., `1.3.6.1`. Each
92 | component integer in the hierarchy typically has a convenient string name; in
93 | this example, the name is `iso.org.dod.internet`, which for historical reasons
94 | happens to be the root of most of the interesting parts of the OID space.
95 |
96 | Objects which may have instances come in two basic flavours: scalar and tabular.
97 | A scalar object has at most one instance, always named 0 (if it exists). For
98 | example, the nodename of a network host is available in in instance named
99 | mib-2.system.sysName.0.
100 |
101 | A tabular object consists of a subtree of objects whose leaves are instances
102 | corresponding to rows in the table. Such a subtree usually consists of an
103 | object referring to the table type, a single child object referring to a type
104 | specifying the format of each table entry, which in turn has one child object
105 | referring to a type specifying the format of each column in the table for which
106 | data items are available. For example, a table in which each row describes a
107 | software module and each module is described by an index, a name, and a version
108 | might be specified as:
109 |
110 | sunFmModuleTable
111 | |
112 | sunFmModuleEntry
113 | / | \
114 | sunFmModuleIndex sunFmModuleName sunFmModuleVersion
115 |
116 | Each of these columnar objects would have one child for each instance that
117 | exists on the system. Each of the objects corresponding to the same row in the
118 | table has the same name beneath the column identifier. Therefore, if there are
119 | two rows named 1 and 2, the following instances would exist within the MIB, each
120 | with a data item associated with it:
121 |
122 | sunFmModuleTable.sunFmModuleEntry.sunFmModuleIndex.1
123 | sunFmModuleTable.sunFmModuleEntry.sunFmModuleIndex.2
124 | sunFmModuleTable.sunFmModuleEntry.sunFmModuleName.1
125 | sunFmModuleTable.sunFmModuleEntry.sunFmModuleName.2
126 | sunFmModuleTable.sunFmModuleEntry.sunFmModuleVersion.1
127 | sunFmModuleTable.sunFmModuleEntry.sunFmModuleVersion.2
128 |
129 | There are three general approaches to tabular instance naming (that is,
130 | indexing) in common use:
131 |
132 | - Integer indices, normally starting from 1 (i.e., the table is an
133 | array)
134 |
135 | - Complex identifiers such as an IP address or string (i.e., the table
136 | is an associative array, hash, or object)
137 |
138 | - A concatentation of multiple IP addresses, port numbers, strings, or
139 | other data that constructs a virtual multidimensional table (i.e., the
140 | table is a nested object or a content-addressable data store).
141 |
142 | Because each instance in the MIB references a single scalar data item, it is
143 | possible (and common) to use complex instance identifiers to form a
144 | content-addressable data store by using several of the columns within a
145 | conceptual table to form the name of an instance rather than (or in addition to)
146 | providing the values in those columns as data items within instances. The
147 | canonical example is the routing table, which is indexed by the aggregation of
148 | the destination prefix and mask just as it is inside the networking stack
149 | itself.
150 |
151 | Further examples, descriptions, and specifications of tabular and scalar data
152 | structures can be found in [RFC 1155][], [RFC 1212][], [RFC 2578][],
153 | [RFC 3416][], and the many documents specifying the format of various portions
154 | of the MIB.
155 |
156 | # Messages and PDUs
157 |
158 | An SNMP message is a datagram, almost always implemented over UDP. Each message
159 | contains a small amount of metadata and exactly one Protocol Data Units (PDU).
160 | A PDU contains an operation type, some metadata, and possibly some object
161 | data. There are four basic types of operation: requests, responses, traps, and
162 | reports. Reports have a standard role only in SNMPv3 and are not discussed
163 | here. See [RFC 3412][] for details on reports.
164 |
165 | Within any PDU type, an object, including its name (OID) and possibly its data
166 | value, is called a variable binding or varbind. For practical purposes, this is
167 | best thought of simply as a triple (name, type, value).
168 |
169 | ### Requests
170 |
171 | There are four types of requests that are generated by management applications
172 | and sent to agents on managed entities: Get, GetNext, GetBulk, and Set. As
173 | their names imply, three of these requests attempt to retrieve existing data and
174 | one attempts to modify it. Again, since the "model" in SNMP is a live system,
175 | modifying the contents of the database modifies the system and affects its
176 | operation.
177 |
178 | The Get and Set requests retrieve or modify a single data item, specified by an
179 | OID. The data item may be scalar or tabular data, but it must itself be scalar
180 | (that is, these requests cannot operate on multiple data items, even if they are
181 | logically contiguous or part of a tabular structure). In general, if the OID
182 | names a scalar object definition rather than its instance, the request is
183 | treated as if it referenced the instance. This may not apply to OIDs that name
184 | tabular object definitions such as entries or columns.
185 |
186 | The GetNext request is similar to the Get request in that it operates on a
187 | single data item. Unlike a Get request, however, the GetNext request operates
188 | on the object whose name *immediately follows* the object named in the request.
189 | This allows a management application to traverse all or part of the MIB without
190 | knowing the names of all the objects it contains.
191 |
192 | The GetBulk request works like a list of Get and/or GetNext requests submitted
193 | as a single request, and expects the results of those requests in a single
194 | response. This was intended to allow more rapid processing of tabular data; by
195 | requesting, for example, the next 10 data items following each of the objects
196 | naming columns in a table, a management client could make fewer requests to
197 | retrieve the same data, and an agent could respond with multiple pieces of data
198 | that are likely to be stored together anyway without having to look them up
199 | multiple times. Unfortunately, this is less useful than it sounds, because the
200 | amount of data that can be exchanged in a single transaction is limited by the
201 | size of datagrams that can be transported between the managed entity and the
202 | management host. That may be 1500 bytes, or even less in some cases, and is
203 | always limited to just under 64kB in IPv4. While the standard makes an
204 | allowance for this by requiring that an error message be returned if the
205 | response would be too large to transport, it is often impossible to know how
206 | large a message can be sent to the requesting host.
207 |
208 | ### Responses
209 |
210 | Responses are generated by agents when they receive requests. A response PDU
211 | has a 1-1 mapping to the request PDU to which it pertains. It contains metadata
212 | such as error state and may include one or more varbinds describing the
213 | result(s) of the requested operation(s).
214 |
215 | ### Traps
216 |
217 | There are three types of trap: Trap, SNMPv2_Trap, and InformRequest. All three
218 | may be generated by any entity and serve the purpose of providing notification
219 | of an asynchronous event. The original SNMPv1 Trap type is obsolete; the SNMPv2
220 | Trap performs the same function but uses a different on-wire format. An
221 | InformRequest is very similar to an SNMPv2 Trap, but will be periodically
222 | retransmitted until the transmitter receives an acknowledgement Response from
223 | the recipient (both of the older Trap types are fire-and-forget).
224 |
225 | Any of the traps may include varbinds that contain object data items relevant to
226 | the event that occurred.
227 |
228 | # Agents
229 |
230 | An agent is responsible for receiving requests, delegating them to appropriate
231 | software, and assembling and transmitting responses. Agents normally listen on
232 | UDP port 161. There are a wide variety of mechanisms by which an agent may
233 | delegate requests; the most common are:
234 |
235 | - Direct incorporation, in which the agent contains one or more software
236 | components that satisfy requests from data contained within the agent
237 | itself. This is commonly used for portions of the MIB that are
238 | introspective in nature; that is, which concern the functioning of the
239 | agent itself. Some agent software also directly incorporates commonly
240 | used portions of the MIB defined by standards.
241 |
242 | - Loadable modules, in which the agent loads at runtime a set of
243 | software components specified by the administrator. Each module
244 | handles a subset of the MIB and executes in the same address space or
245 | virtual execution environment as the rest of the agent.
246 |
247 | - AgentX, a mechanism defined by [RFC 2741][] for delegating portions of
248 | the MIB to subagents that do not directly handle SNMP communication
249 | but instead communicate with the master agent using a separate
250 | protocol. These subagents may be part of separate processes or
251 | execution environments, or may be contained within the same process or
252 | execution environment that contains the master agent.
253 |
254 | - Proprietary RPC mechanisms that delegate portions of the MIB to other
255 | processes, execution environments, or even separate virtual or
256 | physical hosts.
257 |
258 | The snmpjs software supports a loadable module mechanism using standard Node.js
259 | functionality. This mechanism could be used to implement either of the dynamic
260 | mechanisms, but at present there is no support for communicating with subagents
261 | that are not part of the Node.js process.
262 |
263 | # Trap Generators
264 |
265 | Any SNMP entity may generate traps, but in the common use case, managed entities
266 | containing agents will also generate traps. Because, prior to the introduction
267 | of Inform functionality in SNMPv3, traps were not acknowledged and datagram
268 | delivery is unreliable, trap generation by itself is usually considered
269 | insufficient. It must be supplemented by an agent providing access to relevant
270 | portions of the MIB so that a management application can poll the entity for its
271 | status in case a trap was not generated or delivered. In addition, it is likely
272 | that the data items included in a trap's varbinds will be a subset of those
273 | available in the managed entity's MIB.
274 |
275 | The agent itself, in most software architectures, will not actively poll the
276 | live system unless requests are being received, and is unlikely to be aware of
277 | conditions that require the generation of a trap. Therefore, trap generation is
278 | typically incorporated into other software components in the managed entity such
279 | as daemons that provide critical services or monitor the health of the system.
280 | The snmpjs package provides trap generator functionality that can be
281 | incorporated into any Node.js application.
282 |
283 | # Management Applications and Trap Listeners
284 |
285 | These components of SNMP infrastructure generate requests for data items and
286 | direct them to agents on managed entities throughout the network, then receive
287 | and present that information to network administrators. They also typically
288 | receive traps from the same or an overlapping set of managed entities and
289 | present information about them as well. Implementations vary widely, from
290 | simple single-purpose scripts that log events, to packages of discrete utilities
291 | that can perform many functions, to large-scale shrinkwrapped software that
292 | incorporates SNMP as part of a comprehensive management system with multiple
293 | presentation choices.
294 |
295 | The snmpjs package includes some simple example utilities that illustrate how to
296 | use the library to build management applications. It does not include a
297 | full-featured SNMP management application, nor does it include a comprehensive
298 | suite of utilities appropriate for managing a network.
299 |
300 | ---
301 | [RFC 1155]: http://www.ietf.org/rfc/rfc1155.txt
302 | [RFC 1212]: http://www.ietf.org/rfc/rfc1212.txt
303 | [RFC 2578]: http://www.ietf.org/rfc/rfc2578.txt
304 | [RFC 2741]: http://www.ietf.org/rfc/rfc2741.txt
305 | [RFC 3412]: http://www.ietf.org/rfc/rfc3412.txt
306 | [RFC 3416]: http://www.ietf.org/rfc/rfc3416.txt
307 |
--------------------------------------------------------------------------------
/lib/agent.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012 Joyent, Inc. All rights reserved.
3 | */
4 |
5 | var assert = require('assert');
6 | var dgram = require('dgram');
7 | var util = require('util');
8 | var Listener = require('./listener');
9 | var message = require('./protocol/message');
10 | var PDU = require('./protocol/pdu');
11 | var varbind = require('./protocol/varbind');
12 | var data = require('./protocol/data');
13 | var MIB = require('./mib');
14 | var ProviderRequest = require('./provider').ProviderRequest;
15 |
16 | var AGENT_PROBES = {
17 | /* id, op, srcaddr */
18 | 'agent-req-start': [ 'int', 'int', 'char *' ],
19 | /* id, op, srcaddr, status, index */
20 | 'agent-req-done': [ 'int', 'int', 'char *', 'int', 'int' ],
21 | /* id, op, index, oid */
22 | 'agent-varbind-dispatch': [ 'int', 'int', 'int', 'char *' ],
23 | /* id, op, index, oid, result */
24 | 'agent-varbind-result': [ 'int', 'int', 'int', 'char *', 'char *' ]
25 | };
26 |
27 | function
28 | AgentMIBDecor(prov, column)
29 | {
30 | this._scalar = (prov.columns) ? false : true;
31 | this._column = (column === true) ? true : false;
32 |
33 | if (util.isArray(prov.handler))
34 | this._handler = prov.handler;
35 | else
36 | this._handler = [ prov.handler ];
37 | }
38 |
39 | AgentMIBDecor.prototype.instancePossible = function () {
40 | return (this._scalar || this._column);
41 | };
42 |
43 | Agent.prototype._add_provider = function _add_provider(prov) {
44 | var self = this;
45 | var decor;
46 | var node;
47 |
48 | if (typeof (prov) !== 'object')
49 | throw new TypeError('prov (object) is required');
50 | if (typeof (prov.oid) !== 'string')
51 | throw new TypeError('prov.oid (string) is required');
52 | if (typeof (prov.handler) !== 'function' &&
53 | (typeof (prov.handler) !== 'object' || !util.isArray(prov.handler)))
54 | throw new TypeError('prov.handler (function) is required');
55 | if (typeof (prov.columns) !== 'undefined' &&
56 | (typeof (prov.columns) !== 'object' || !util.isArray(prov.columns)))
57 | throw new TypeError('prov.columns must be an array');
58 |
59 | node = this._mib.add(prov);
60 | decor = new AgentMIBDecor(prov);
61 | node.decorate({ tag: '_agent', obj: decor });
62 |
63 | if (prov.columns) {
64 | prov.columns.forEach(function (c) {
65 | node = self._mib.add(prov.oid + '.' + c);
66 | decor = new AgentMIBDecor(prov, true);
67 | node.decorate({ tag: '_agent', obj: decor });
68 | });
69 | }
70 | };
71 |
72 | Agent.prototype.addProviders = function addProviders(prov) {
73 | var self = this;
74 |
75 | if (typeof (prov) !== 'object')
76 | throw new TypeError('prov (object) is required');
77 |
78 | if (util.isArray(prov)) {
79 | prov.forEach(function (p) {
80 | self._add_provider(p);
81 | });
82 | } else {
83 | this._add_provider(prov);
84 | }
85 | };
86 |
87 | function
88 | Agent(options)
89 | {
90 | var self = this;
91 |
92 | Listener.call(this, options);
93 |
94 | if (typeof (options.dtrace) !== 'object')
95 | throw new TypeError('options.dtrace (object) is required');
96 |
97 | this._dtrace = options.dtrace;
98 |
99 | Object.keys(AGENT_PROBES).forEach(function (p) {
100 | var args = AGENT_PROBES[p].splice(0);
101 | args.unshift(p);
102 |
103 | self._dtrace.addProbe.apply(self._dtrace, args);
104 | });
105 |
106 | this._dtrace.enable();
107 |
108 | this._mib = new MIB();
109 | }
110 | util.inherits(Agent, Listener);
111 |
112 | /*
113 | * The provider is expected to provide one of three things for each iteration
114 | * requested of it:
115 | *
116 | * - undefined, meaning that there is no matching instance. This should happen
117 | * only for tabular providers; scalar providers are never passed GetNext
118 | * requests nor any Get or Set with an instance other than 0.
119 | *
120 | * - an integer, representing an error status from the set of errors enumerated
121 | * in protocol/pdu.js.
122 | *
123 | * - an instance of SnmpVarbind containing the data requested.
124 | *
125 | * These end up here, one way or another, and we just stuff them into the
126 | * response object.
127 | */
128 | Agent.prototype._varbind_set_single =
129 | function _varbind_set_single(req, rsp, idx, vb) {
130 | if (typeof (vb) === 'undefined' && req.pdu.op == PDU.GetNextRequest) {
131 | rsp.pdu.varbinds[idx] = req.pdu.varbinds[idx].clone();
132 | rsp.pdu.varbinds[idx].data = data.createData({ type: 'Null',
133 | value: data.noSuchInstance });
134 | } else if (typeof (vb) === 'number') {
135 | if (req.pdu.op != PDU.SetRequest && vb != PDU.genErr) {
136 | this.log.warn({ snmpmsg: req },
137 | 'provider attempted to set prohibited ' +
138 | 'error code ' + vb + ' for varbind ' + idx);
139 | vb = PDU.genErr;
140 | }
141 | rsp.pdu.varbinds[idx] = req.pdu.varbinds[idx].clone();
142 | if (!rsp.pdu.error_status || idx + 1 < rsp.pdu.error_index) {
143 | rsp.pdu.error_status = vb;
144 | rsp.pdu.error_index = idx + 1;
145 | }
146 | } else if (typeof (vb) !== 'object' || !varbind.isSnmpVarbind(vb)) {
147 | throw new TypeError('Response data is of incompatible type');
148 | } else {
149 | rsp.pdu.varbinds[idx] = vb;
150 | }
151 | };
152 |
153 | Agent.prototype._transmit_response = function _transmit_response(sock, rsp) {
154 | var dst = rsp.dst;
155 | var local_sock = false;
156 |
157 | rsp.encode();
158 | this._log.trace({ raw: rsp.raw, dst: dst, snmpmsg: rsp },
159 | 'Sending SNMP response message');
160 |
161 | if (!sock) {
162 | sock = dgram.createSocket(dst.family);
163 | local_sock = false;
164 | }
165 | sock.send(rsp.raw.buf, 0, rsp.raw.len, dst.port, dst.address,
166 | function (err, len) {
167 | if (local_sock)
168 | sock.close();
169 | });
170 | };
171 |
172 | Agent.prototype._do_getset = function _do_getset(req, rsp) {
173 | var self = this;
174 | var nvb = req.pdu.varbinds.length;
175 | var ndone = 0;
176 |
177 | req.pdu.varbinds.forEach(function (vb, i) {
178 | var node = self._mib.lookup(vb.oid);
179 | var prq = new ProviderRequest(req.pdu.op, vb.oid, node);
180 | var decor = node.decor('_agent');
181 | var handler;
182 |
183 | if (!node || !decor || !decor.instancePossible()) {
184 | handler = [ function _getset_nsohandler(pr) {
185 | var rsd = data.createData({ type: 'Null',
186 | value: data.noSuchObject });
187 | var rsvb = varbind.createVarbind({
188 | oid: pr.oid, data: rsd });
189 | pr.done(rsvb);
190 | } ];
191 | } else {
192 | if (!prq.instance || decor._scalar &&
193 | (prq.instance.length > 1 || prq.instance[0] != 0)) {
194 | handler = [ function _getset_nsihandler(pr) {
195 | var rsd = data.createData({
196 | type: 'Null',
197 | value: data.noSuchInstance
198 | });
199 | var rsvb = varbind.createVarbind({
200 | oid: pr.oid, data: rsd });
201 | pr.done(rsvb);
202 | } ];
203 | } else {
204 | if (prq.op == PDU.SetRequest)
205 | prq._value = vb.data;
206 | handler = decor._handler;
207 | }
208 | }
209 |
210 | prq._done = function _getset_done(rsvb) {
211 | self._varbind_set_single(req, rsp, i, rsvb);
212 | if (++ndone == nvb)
213 | self._transmit_response(req._conn_recv, rsp);
214 | };
215 | handler.forEach(function (h) {
216 | h(prq);
217 | });
218 | });
219 | };
220 |
221 | Agent.prototype._getnext_lookup = function _getnext_lookup(oid, exactok) {
222 | var node;
223 | var addr = data.canonicalizeOID(oid);
224 | var match;
225 | var decor;
226 |
227 | oid = addr.join('.');
228 | node = this._mib.lookup(oid);
229 |
230 | if (!node)
231 | return (null);
232 |
233 | decor = node.decor('_agent');
234 | match = function (n) {
235 | var d = n.decor('_agent');
236 |
237 | return (d && d.instancePossible() || false);
238 | };
239 |
240 | /*
241 | * Please note that this code is optimised for readability because the
242 | * logic for choosing where to look for the next instance is somewhat
243 | * complex. It should be very apparent from this that we are in fact
244 | * covering all possible cases, and doing the right thing for each.
245 | */
246 |
247 | /*
248 | * Exact match, the node is a column. Use the node if an exact match is
249 | * ok; the provider will figure out the first row, or else we'll end up
250 | * right back here with exactok clear.
251 | */
252 | if (node.oid === oid && decor && decor._column && exactok)
253 | return (node);
254 |
255 | /*
256 | * Exact match, the node is a column but we need the next subtree.
257 | * Walk the parent starting from the next sibling.
258 | */
259 | if (node.oid === oid && decor && decor._column) {
260 | return (this._mib.next_match({
261 | node: node.parent,
262 | match: match,
263 | start: node.addr[node.addr.length - 1] + 1
264 | }));
265 | }
266 |
267 | /*
268 | * Exact match, the node is a scalar. Use it; we want the instance,
269 | * becuase it follows this OID in the lexicographical order.
270 | */
271 | if (node.oid === oid && decor && decor._scalar)
272 | return (node);
273 |
274 | /*
275 | * Exact match, the node is just an OID. Walk the node from the
276 | * beginning to find any instances within this subtree. If there aren't
277 | * any, the walk will proceed back up and on to the next sibling.
278 | */
279 | if (node.oid === oid && (!decor || !decor.instancePossible())) {
280 | return (this._mib.next_match({
281 | node: node,
282 | match: match
283 | }));
284 | }
285 |
286 | /*
287 | * Ancestor, and the node is just an OID. Walk the node from the first
288 | * child after the first non-matching component.
289 | *
290 | * Ex: GetNext(.1.3.3.6.2) finds (.1.3); walk (.1.3), 4
291 | */
292 | if (!decor || !decor.instancePossible()) {
293 | return (this._mib.next_match({
294 | node: node,
295 | match: match,
296 | start: addr[node.addr.length] + 1
297 | }));
298 | }
299 |
300 | /*
301 | * Ancestor, and the node is a scalar. Walk the parent starting from
302 | * the next sibling, because there can be only one intance below this
303 | * node. So no matter what the instance portion of the OID is, the next
304 | * instance in the MIB can't be in this subtree.
305 | */
306 | if (decor._scalar) {
307 | return (this._mib.next_match({
308 | node: node.parent,
309 | match: match,
310 | start: node.addr[node.addr.length - 1] + 1
311 | }));
312 | }
313 |
314 | /*
315 | * Ancestor, and the node is a column. Use the node if an exact match
316 | * is ok, otherwise keep going. Note that this is the same as the very
317 | * first case above; we don't actually care whether we're being asked
318 | * for the first row in the column or the next one after some identified
319 | * instance, because that's the provider's job to figure out.
320 | */
321 | if (exactok)
322 | return (node);
323 |
324 | /*
325 | * Ancestor, and the node is a column. An exact match is not ok, so
326 | * walk the parent starting from the next sibling. Again, this is the
327 | * same as the case in which we hit the exact match -- we know we've
328 | * already asked this provider and we need to advance to another one.
329 | */
330 | return (this._mib.next_match({
331 | node: node.parent,
332 | match: match,
333 | start: node.addr[node.addr.length - 1] + 1
334 | }));
335 | };
336 |
337 | Agent.prototype._do_getnext_one =
338 | function (req, rsp, i, oid, cookie, first) {
339 | var self = this;
340 | var node = this._getnext_lookup(oid, first);
341 | var nvb = req.pdu.varbinds.length;
342 | var prq = new ProviderRequest(req.pdu.op, oid, node);
343 | var handler;
344 |
345 | if (!node || !node.decor('_agent').instancePossible()) {
346 | handler = [ function _getset_nsohandler(pr) {
347 | var rsd = data.createData({ type: 'Null',
348 | value: data.endOfMibView });
349 | var rsvb = varbind.createVarbind({
350 | oid: pr.oid, data: rsd });
351 | pr.done(rsvb);
352 | } ];
353 | } else {
354 | handler = node.decor('_agent')._handler;
355 | }
356 |
357 | prq._done = function _getnext_done(rsvb) {
358 | if (rsvb !== undefined) {
359 | self._varbind_set_single(req, rsp, i, rsvb);
360 | if (++cookie.ndone == nvb)
361 | self._transmit_response(req._conn_recv, rsp);
362 | return;
363 | }
364 | self._do_getnext_one(req, rsp, i, prq.node.oid, cookie, false);
365 | };
366 |
367 | handler.forEach(function (h) {
368 | h(prq);
369 | });
370 | };
371 |
372 | Agent.prototype._do_getnext = function _do_getnext(req, rsp) {
373 | var self = this;
374 | var cookie = { ndone: 0 };
375 |
376 | req.pdu.varbinds.forEach(function (vb, i) {
377 | self._do_getnext_one(req, rsp, i, vb.oid, cookie, true);
378 | });
379 | };
380 |
381 | Agent.prototype._do_getbulk = function _do_getbulk(req, rsp) {
382 | /* XXX yuck */
383 | };
384 |
385 | Agent.prototype._process_req = function _process_req(req) {
386 | var rsp;
387 |
388 | assert.ok(req.version >= 0 && req.version <= 1);
389 |
390 | /* XXX check community here */
391 |
392 | rsp = message.createMessage({ version: req.version,
393 | community: req.community });
394 | rsp.dst = req.src;
395 |
396 | rsp.pdu = PDU.createPDU({ op: PDU.Response,
397 | request_id: req.pdu.request_id });
398 | rsp.pdu.error_status = 0;
399 | rsp.pdu.error_index = 0;
400 |
401 | switch (req.pdu.op) {
402 | case PDU.GetRequest:
403 | case PDU.SetRequest:
404 | this._do_getset(req, rsp);
405 | break;
406 | case PDU.GetNextRequest:
407 | this._do_getnext(req, rsp);
408 | break;
409 | case PDU.GetBulkRequest:
410 | this._do_getbulk(req, rsp);
411 | break;
412 | case PDU.Response:
413 | case PDU.Trap:
414 | case PDU.InformRequest:
415 | case PDU.SNMPv2_Trap:
416 | case PDU.Report:
417 | default:
418 | Listener.prototype._process_msg.call(this, req);
419 | break;
420 | }
421 | };
422 |
423 | Agent.prototype._process_msg = function _process_msg(msg) {
424 | this._process_req(msg);
425 | };
426 |
427 | Agent.prototype._augment_msg = function _augment_msg(msg, conn) {
428 | msg._conn_recv = conn;
429 | };
430 |
431 | Agent.prototype.request = function request(oid, handler, columns) {
432 | var prov;
433 |
434 | if (typeof (oid) === 'string') {
435 | if (typeof (handler) !== 'function')
436 | throw new TypeError('handler must be a function');
437 |
438 | this.addProviders({
439 | oid: oid,
440 | handler: handler,
441 | columns: columns
442 | });
443 |
444 | return;
445 | }
446 |
447 | prov = oid;
448 | if (typeof (prov) === 'object') {
449 | this.addProviders(prov);
450 | return;
451 | }
452 |
453 | throw new TypeError('MIB provider must be specified');
454 | };
455 |
456 | module.exports = Agent;
457 |
--------------------------------------------------------------------------------
/lib/client.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015 Jan Van Buggenhout. All rights reserved.
3 | */
4 |
5 | var dgram = require('dgram');
6 | var util = require('util');
7 | var dns = require('dns');
8 | var os = require('os');
9 | var Receiver = require('./receiver');
10 | var message = require('./protocol/message');
11 | var PDU = require('./protocol/pdu');
12 | var varbind = require('./protocol/varbind');
13 | var data = require('./protocol/data');
14 |
15 | var request_id = 1;
16 |
17 | function
18 | Client(options)
19 | {
20 | Receiver.call(this, options);
21 | this._callbacks = {};
22 | this._socket = this.createSocket(options.family || 'udp4');
23 | this._startuptime = 0;
24 | this.__defineSetter__('startuptime', function (t) {
25 | if (typeof (t) === 'number')
26 | this._startuptime = t;
27 | else
28 | throw new TypeError('startuptime must be an integer');
29 | });
30 | this.__defineGetter__('hostip', function () { return _hostip; });
31 | }
32 | util.inherits(Client, Receiver);
33 |
34 | function getUptime(client)
35 | {
36 | return (Math.floor((Date.now() / 10) - (client._startuptime * 100)));
37 | }
38 |
39 | function send(client, ip, port, req, cb) {
40 | if (typeof (cb) !== 'undefined' && typeof (cb) !== 'function')
41 | throw new TypeError('cb must be a function');
42 |
43 | if (cb && 'request_id' in req.pdu)
44 | client._callbacks[req.pdu.request_id] = cb;
45 |
46 | req.encode();
47 | client._socket.send(req._raw.buf, 0, req._raw.len, port, ip,
48 | function (err, bytes)
49 | {
50 | if (err)
51 | console.log(err); // FIXME: meh
52 | });
53 | }
54 |
55 | function OIDNull(oid)
56 | {
57 | return varbind.createVarbind({
58 | oid: oid,
59 | data: data.createData({ type: 'Null', value: 5 })
60 | });
61 | }
62 |
63 | Client.prototype._process_msg = function _process_msg(msg) {
64 | switch (msg.pdu.op) {
65 | case PDU.Response:
66 | this._callbacks[msg.pdu.request_id](msg);
67 | delete this._callbacks[msg.pdu.request_id];
68 | break;
69 | case PDU.Trap:
70 | case PDU.InformRequest:
71 | case PDU.SNMPv2_Trap:
72 | case PDU.GetRequest:
73 | case PDU.SetRequest:
74 | case PDU.GetNextRequest:
75 | case PDU.GetBulkRequest:
76 | case PDU.Report:
77 | default:
78 | Receiver.prototype._process_msg.call(this, msg);
79 | break;
80 | }
81 | };
82 |
83 | Client.prototype.get = function (ip, community, version, oid, cb) {
84 | send(this, ip, 161, message.createMessage({
85 | version: version,
86 | community: community,
87 | pdu: PDU.createPDU({
88 | op: PDU.GetRequest,
89 | request_id: request_id++,
90 | varbinds: [ OIDNull(oid) ]
91 | })
92 | }), cb);
93 | };
94 |
95 | Client.prototype.getNext = function (ip, community, version, oid, cb) {
96 | send(this, ip, 161, message.createMessage({
97 | version: version,
98 | community: community,
99 | pdu: PDU.createPDU({
100 | op: PDU.GetNextRequest,
101 | request_id: request_id++,
102 | varbinds: [ OIDNull(oid) ]
103 | })
104 | }), cb);
105 | };
106 |
107 | Client.prototype.set = function (ip, community, version, oid, value, cb) {
108 | send(this, ip, 161, message.createMessage({
109 | version: version,
110 | community: community,
111 | pdu: PDU.createPDU({
112 | op: PDU.SetRequest,
113 | request_id: request_id++,
114 | varbinds: [
115 | varbind.createVarbind({
116 | oid: oid,
117 | data: value
118 | })
119 | ]
120 | })
121 | }), cb);
122 | };
123 |
124 | var _hostip = '127.0.0.1';
125 | dns.lookup(os.hostname(), function (err, address, family) {
126 | _hostip = address;
127 | });
128 |
129 | Client.prototype.trap = function (ip, community, options, varbinds) {
130 | if ('specific_trap' in options) {
131 | if (!('generic_trap' in options))
132 | options.generic_trap = PDU.enterpriseSpecific;
133 | if (PDU.enterpriseSpecific == options.generic_trap &&
134 | !('enterprise' in options))
135 | throw new TypeError('options.enterprise is required '+
136 | 'for enterpriseSpecific traps');
137 | }
138 | if (!('generic_trap' in options))
139 | throw new TypeError('Need either generic_trap or '+
140 | 'specific_trap');
141 |
142 | var self = this;
143 | send(this, ip, 162, message.createMessage({
144 | version: 0,
145 | community: community,
146 | pdu: PDU.createPDU({
147 | op: PDU.Trap,
148 | enterprise: options.enterprise || '1.3.6.1.4.1.3.1.1',
149 | generic_trap: options.generic_trap,
150 | specific_trap: options.specific_trap || 0,
151 | agent_addr: options.agent_addr || _hostip,
152 | time_stamp: options.time_stamp || options.uptime ||
153 | getUptime(self),
154 | varbinds: varbinds
155 | })
156 | }));
157 | };
158 |
159 | Client.prototype.getBulk = function (ip, community, non_repeaters, repeaters,
160 | max_repetitions, cb) {
161 | var msg = message.createMessage({
162 | version: 1,
163 | community: community,
164 | pdu: PDU.createPDU({
165 | op: PDU.GetBulkRequest,
166 | request_id: request_id++,
167 | varbinds: non_repeaters.map(OIDNull).
168 | concat(repeaters.map(OIDNull))
169 | })
170 | });
171 | msg.pdu.non_repeaters = non_repeaters.length;
172 | msg.pdu.max_repetitions = max_repetitions;
173 | send(this, ip, 161, msg, cb);
174 | };
175 |
176 | Client.prototype.SNMPv2_Trap =
177 | Client.prototype.inform = function (ip, community, uptime, oid, varbinds, cb) {
178 | var self = this;
179 | send(this, ip, 162, message.createMessage({
180 | version: 1,
181 | community: community,
182 | pdu: PDU.createPDU({
183 | op: typeof (cb) === 'function' ? PDU.InformRequest :
184 | PDU.SNMPv2_Trap,
185 | request_id: request_id++,
186 | varbinds: [
187 | varbind.createVarbind({
188 | // sysUpTime.0
189 | oid: '1.3.6.1.2.1.1.3.0',
190 | data: data.createData({
191 | type: 'TimeTicks',
192 | value: typeof (uptime) !==
193 | 'undefined' ? uptime :
194 | getUptime(self)
195 | })
196 | }),
197 | varbind.createVarbind({
198 | // snmpTrapOID.0
199 | oid: '1.3.6.1.6.3.1.1.4.1.0',
200 | data: data.createData({
201 | type: 'ObjectIdentifier',
202 | value: oid
203 | })
204 | })
205 | ].concat(varbinds)
206 | })
207 | }), cb);
208 | };
209 |
210 | Client.prototype.close = function () {
211 | this._socket.unref();
212 | };
213 |
214 | module.exports = Client;
215 |
--------------------------------------------------------------------------------
/lib/errors/message.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012 Joyent, Inc. All rights reserved.
3 | */
4 |
5 | var util = require('util');
6 |
7 | function
8 | EmptyMessageError()
9 | {
10 | this.name = 'EmptyMessageError';
11 | }
12 | util.inherits(EmptyMessageError, Error);
13 |
14 | function
15 | MessageParseError(str, info)
16 | {
17 | this.name = 'MessageParseError';
18 | this.message = str;
19 | this.token = info.token;
20 | this.offset = info.loc;
21 | this.expected = info.expected;
22 | }
23 | util.inherits(MessageParseError, Error);
24 |
25 | function
26 | NoSupportError(str)
27 | {
28 | this.name = 'NoSupportError';
29 | this.message = str;
30 | }
31 | util.inherits(NoSupportError, Error);
32 |
33 | module.exports = {
34 | EmptyMessageError: EmptyMessageError,
35 | MessageParseError: MessageParseError,
36 | NoSupportError: NoSupportError
37 | };
38 |
--------------------------------------------------------------------------------
/lib/errors/varbind.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012 Joyent, Inc. All rights reserved.
3 | */
4 | var util = require('util');
5 |
6 | function
7 | TypeConflictError(str)
8 | {
9 | this.name = 'TypeConflictError';
10 | this.message = str;
11 | }
12 | util.inherits(TypeConflictError, Error);
13 |
14 | module.exports = {
15 | TypeConflictError: TypeConflictError
16 | };
17 |
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 Joyent, Inc. All rights reserved.
3 | */
4 |
5 | /*
6 | * SNMP is defined by a large set of specifications. See
7 | * http://www.snmp.com/protocol/snmp_rfcs.shtml as a starting point.
8 | */
9 |
10 | var dtrace = require('dtrace-provider');
11 | var Agent = require('./agent');
12 | var Client = require('./client');
13 | var TrapListener = require('./trap_listener');
14 | var Logger = require('bunyan');
15 | var MIB = require('./mib');
16 | var provider = require('./provider');
17 | var message = require('./protocol/message');
18 | var PDU = require('./protocol/pdu');
19 | var varbind = require('./protocol/varbind');
20 | var data = require('./protocol/data');
21 | var uint64_t = require('./protocol/uint64_t');
22 |
23 | var agent_provider;
24 |
25 | function
26 | bunyan_serialize_raw(raw)
27 | {
28 | var obj = {
29 | buf: (raw.buf ? raw.buf.inspect() : ''),
30 | len: raw.len || 0
31 | };
32 | return (obj);
33 | }
34 |
35 | function
36 | bunyan_serialize_endpoint(endpoint)
37 | {
38 | var obj = {
39 | family: endpoint.family || '',
40 | address: endpoint.address || '',
41 | port: endpoint.port || 0
42 | };
43 | return (obj);
44 | }
45 |
46 | function
47 | defaultDTrace(name)
48 | {
49 | if (!agent_provider)
50 | agent_provider = dtrace.createDTraceProvider(name);
51 |
52 | return (agent_provider);
53 | }
54 |
55 | function
56 | defaultLogger(log, name)
57 | {
58 | var serializers = {
59 | err: Logger.stdSerializers.err,
60 | raw: bunyan_serialize_raw,
61 | origin: bunyan_serialize_endpoint,
62 | dst: bunyan_serialize_endpoint,
63 | snmpmsg: message.serializer
64 | };
65 |
66 | if (log) {
67 | return (log.child({
68 | component: 'snmp-agent',
69 | serializers: serializers
70 | }));
71 | }
72 |
73 | return (new Logger({
74 | name: name,
75 | level: 'info',
76 | stream: process.stderr,
77 | serializers: serializers
78 | }));
79 | }
80 |
81 | module.exports = {
82 | createAgent: function createAgent(options) {
83 | if (!options)
84 | options = {};
85 | if (!options.name)
86 | options.name = 'snmpjs';
87 | if (!options.dtrace)
88 | options.dtrace = defaultDTrace(options.name);
89 |
90 | options.log = defaultLogger(options.log, options.name);
91 |
92 | return (new Agent(options));
93 | },
94 |
95 | createMIB: function createMIB(def) {
96 | var mib = new MIB();
97 | if (def)
98 | mib.add(def);
99 |
100 | return (mib);
101 | },
102 |
103 | createClient: function createClient(options) {
104 | if (!options)
105 | options = {};
106 | if (!options.name)
107 | options.name = 'snmpjs';
108 |
109 | options.log = defaultLogger(options.log, options.name);
110 |
111 | return (new Client(options));
112 | },
113 |
114 | createTrapListener: function createTrapListener(options) {
115 | if (!options)
116 | options = {};
117 | if (!options.name)
118 | options.name = 'snmpjs';
119 |
120 | options.log = defaultLogger(options.log, options.name);
121 |
122 | return (new TrapListener(options));
123 | },
124 |
125 | message: message,
126 | pdu: PDU,
127 | varbind: varbind,
128 | data: data,
129 | provider: provider,
130 | uint64_t: uint64_t
131 | };
132 |
--------------------------------------------------------------------------------
/lib/lexer.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012 Joyent, Inc. All rights reserved.
3 | */
4 |
5 | var ASN1 = require('asn1').Ber;
6 |
7 | function
8 | Lexer()
9 | {
10 | this.reader = null;
11 | this.yytext = null;
12 | this.yyleng = 0;
13 | this.yylineno = 0;
14 | this.yylloc = 0;
15 | }
16 |
17 | Lexer.prototype.showPosition = function showPosition()
18 | {
19 | return (this.yylloc);
20 | };
21 |
22 | /*
23 | * When we return something other than undefined:
24 | *
25 | * yytext is a Buffer containing only this token.
26 | * yyleng is yytext.length (for compatibility).
27 | * yylloc is the offset in the input buffer at which the token starts.
28 | */
29 | Lexer.prototype.lex = function lex()
30 | {
31 | var tag = this.reader.peek();
32 | var soff = this.reader.offset + 1;
33 | var lenlen = this.reader.readLength(soff) - soff;
34 | var len = this.reader.length;
35 | var token = undefined;
36 | var skip = 0;
37 | var token_len = 1 + lenlen + len; /* TLV */
38 | var i;
39 |
40 | this.yylloc = this.reader.offset;
41 | this.yytext = undefined;
42 | this.yyleng = 0;
43 |
44 | switch (tag) {
45 | case ASN1.Integer:
46 | token = 'INTEGER';
47 | break;
48 | case ASN1.OctetString:
49 | token = 'OCTET_STRING';
50 | break;
51 | case ASN1.Null:
52 | token = 'NULL';
53 | break;
54 | case ASN1.OID:
55 | token = 'OBJECT_IDENTIFIER';
56 | break;
57 | case ASN1.Constructor | ASN1.Sequence:
58 | token = 'SEQUENCE';
59 | skip = 1 + lenlen;
60 | token_len = 0;
61 | break;
62 | case 0x40 | 0x00:
63 | token = 'IP_ADDRESS';
64 | break;
65 | case 0x40 | 0x03:
66 | token = 'TIME_TICKS';
67 | break;
68 | case ASN1.Context | ASN1.Constructor | 0x00:
69 | case ASN1.Context | ASN1.Constructor | 0x01:
70 | case ASN1.Context | ASN1.Constructor | 0x02:
71 | case ASN1.Context | ASN1.Constructor | 0x03:
72 | case ASN1.Context | ASN1.Constructor | 0x04:
73 | case ASN1.Context | ASN1.Constructor | 0x05:
74 | case ASN1.Context | ASN1.Constructor | 0x06:
75 | case ASN1.Context | ASN1.Constructor | 0x07:
76 | case ASN1.Context | ASN1.Constructor | 0x08:
77 | token = 'CONTEXT_CONSTRUCTED_' +
78 | (tag & ~(ASN1.Context | ASN1.Constructor));
79 | skip = 1 + lenlen;
80 | token_len = 0;
81 | break;
82 | case null:
83 | token = null;
84 | break;
85 | default:
86 | token = 'DATA';
87 | break;
88 | }
89 |
90 | if (token_len > 0) {
91 | this.yytext = new Buffer(token_len);
92 | this.yyleng = token_len;
93 | this.reader.buffer.copy(this.yytext, 0, 0, token_len);
94 | skip = token_len;
95 | }
96 |
97 | for (i = 0; i < skip; i++)
98 | this.reader.readByte();
99 |
100 | return (token);
101 | };
102 |
103 | Lexer.prototype.setInput = function setInput(buffer)
104 | {
105 | this.buffer = buffer;
106 | this.reader = new ASN1.Reader(buffer);
107 | };
108 |
109 | module.exports = Lexer;
110 |
--------------------------------------------------------------------------------
/lib/listener.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015 Jan Van Buggenhout. All rights reserved.
3 | * Copyright (c) 2013 Joyent, Inc. All rights reserved.
4 | */
5 |
6 | var util = require('util');
7 | var Receiver = require('./receiver');
8 | var PDU = require('./protocol/pdu');
9 |
10 | function
11 | Listener(options)
12 | {
13 | Receiver.call(this, options);
14 | this._connections = [];
15 | }
16 | util.inherits(Listener, Receiver);
17 |
18 | Listener.prototype.bind = function bind(arg, cb) {
19 | var self = this;
20 | var conn;
21 |
22 | if (typeof (arg) !== 'object')
23 | throw new TypeError('arg (object) is required');
24 | if (typeof (arg.family) !== 'string')
25 | throw new TypeError('arg.family (string) is required');
26 | if (typeof (arg.port) !== 'number')
27 | throw new TypeError('arg.port (number) is required');
28 | if (typeof (arg.addr) !== 'undefined' &&
29 | typeof (arg.addr) !== 'string')
30 | throw new TypeError('arg.addr must be a string');
31 |
32 | if (typeof (cb) !== 'undefined' && typeof (cb) !== 'function')
33 | throw new TypeError('cb must be a function');
34 |
35 | conn = this.createSocket(arg.family);
36 | this._connections.push(conn);
37 |
38 | conn.on('listening', function () {
39 | self._log.info('Bound to ' + conn.address().address + ':' +
40 | conn.address().port);
41 | if (cb)
42 | cb();
43 | });
44 |
45 | conn.bind(arg.port, arg.addr);
46 | };
47 |
48 | Listener.prototype.close = function close() {
49 | var self = this;
50 |
51 | this._connections.forEach(function (c) {
52 | self._log.info('Shutting down endpoint ' + c.address().address +
53 | ':' + c.address().port);
54 | c.close();
55 | });
56 | };
57 |
58 | module.exports = Listener;
59 |
--------------------------------------------------------------------------------
/lib/mib.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012 Joyent, Inc. All rights reserved.
3 | */
4 |
5 | var assert = require('assert');
6 | var util = require('util');
7 | var data = require('./protocol/data');
8 |
9 | function
10 | MIBNode(addr, parent)
11 | {
12 | var self = this;
13 |
14 | if (typeof (addr) !== 'object' || !util.isArray(addr))
15 | throw new TypeError('addr (array) is required');
16 |
17 | this._addr = addr;
18 | this._oid = this._addr.join('.');
19 | this._children = [];
20 | this._parent = parent;
21 | this._decor = {};
22 |
23 | this.__defineGetter__('oid', function () { return (self._oid); });
24 | this.__defineGetter__('addr', function () { return (self._addr); });
25 | this.__defineGetter__('parent', function () { return (self._parent); });
26 | }
27 |
28 | MIBNode.prototype.child = function child(idx) {
29 | if (typeof (idx) !== 'number')
30 | throw new TypeError('idx (number) is required');
31 |
32 | return (this._children[idx]);
33 | };
34 |
35 | MIBNode.prototype.decor = function (tag) {
36 | return (this._decor[tag]);
37 | };
38 |
39 | MIBNode.prototype.decorate = function (arg) {
40 | if (typeof (arg) !== 'object')
41 | throw new TypeError('arg (object) is required');
42 | if (typeof (arg.tag) !== 'string')
43 | throw new TypeError('arg.tag (string) is required');
44 |
45 | this._decor[arg.tag] = arg.obj;
46 | };
47 |
48 | MIBNode.prototype.listChildren = function listChildren(lowest) {
49 | var sorted = [];
50 |
51 | if (typeof (lowest) === 'undefined')
52 | lowest = 0;
53 |
54 | this._children.forEach(function (c, i) {
55 | if (i >= lowest)
56 | sorted.push(i);
57 | });
58 |
59 | sorted.sort(function (a, b) {
60 | return (a - b);
61 | });
62 |
63 | return (sorted);
64 | };
65 |
66 | function
67 | oid_is_descended(oid, ancestor)
68 | {
69 | var a_addr = data.canonicalizeOID(ancestor);
70 | var addr = data.canonicalizeOID(oid);
71 | var is_a = true;
72 |
73 | if (addr.length <= a_addr.length)
74 | return (false);
75 |
76 | a_addr.forEach(function (o, i) {
77 | if (addr[i] !== a_addr[i])
78 | is_a = false;
79 | });
80 |
81 | return (is_a);
82 | }
83 |
84 | MIBNode.prototype.isDescendant = function isDescendant(oid) {
85 | return (oid_is_descended(this._addr, oid));
86 | };
87 |
88 | MIBNode.prototype.isAncestor = function isAncestor(oid) {
89 | return (oid_is_descended(oid, this._addr));
90 | };
91 |
92 | function
93 | MIB()
94 | {
95 | this._root = new MIBNode([], null);
96 | }
97 |
98 | MIB.prototype.add = function add(def) {
99 | var addr;
100 | var node;
101 | var i;
102 |
103 | if (typeof (def) === 'string' ||
104 | typeof (def) === 'object' && util.isArray(def))
105 | addr = def;
106 | else
107 | addr = def.oid;
108 |
109 | addr = data.canonicalizeOID(addr);
110 | node = this._root;
111 |
112 | for (i = 0; i < addr.length; i++) {
113 | if (!node._children.hasOwnProperty(addr[i])) {
114 | node._children[addr[i]] =
115 | new MIBNode(addr.slice(0, i + 1), node);
116 | }
117 | node = node._children[addr[i]];
118 | }
119 |
120 | return (node);
121 | };
122 |
123 | MIB.prototype.lookup = function lookup(addr) {
124 | var i, node;
125 |
126 | addr = data.canonicalizeOID(addr);
127 | node = this._root;
128 | for (i = 0; i < addr.length; i++) {
129 | if (!node._children.hasOwnProperty(addr[i]))
130 | break;
131 | node = node._children[addr[i]];
132 | }
133 |
134 | return (node);
135 | };
136 |
137 | MIB.prototype.next_match = function (arg) {
138 | var child_indices;
139 | var sub;
140 | var i;
141 |
142 | if (typeof (arg) !== 'object')
143 | throw new TypeError('arg (object) is required');
144 | if (typeof (arg.node) !== 'object' || !(arg.node instanceof MIBNode))
145 | throw new TypeError('arg.node (object) is required');
146 | if (typeof (arg.match) !== 'function')
147 | throw new TypeError('arg.match (function) is required');
148 | if (typeof (arg.start) !== 'undefined' &&
149 | typeof (arg.start) !== 'number')
150 | throw new TypeError('arg.start must be a number');
151 |
152 | if (arg.match(arg.node) === true)
153 | return (arg.node);
154 |
155 | child_indices = arg.node.listChildren(arg.start);
156 | for (i = 0; i < child_indices.length; i++) {
157 | sub = this.next_match({
158 | node: arg.node._children[child_indices[i]],
159 | match: arg.match
160 | });
161 | if (sub)
162 | return (sub);
163 | }
164 | if (!arg.node._parent)
165 | return (null);
166 |
167 | return (this.next_match({
168 | node: arg.node._parent,
169 | match: arg.match,
170 | start: arg.node._addr[arg.node._addr.length - 1] + 1
171 | }));
172 | };
173 |
174 | module.exports = MIB;
175 |
--------------------------------------------------------------------------------
/lib/mib/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012 Joyent, Inc. All rights reserved.
3 | */
4 |
5 | var mib2_system = require('./mib-2/system');
6 |
7 | module.exports = function () {
8 | var providers = [];
9 |
10 | providers = providers.concat(mib2_system);
11 |
12 | return (providers);
13 | }();
14 |
--------------------------------------------------------------------------------
/lib/mib/mib-2/system.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012 Joyent, Inc. All rights reserved.
3 | */
4 |
5 | var os = require('os');
6 | var snmp = require('../../index.js');
7 |
8 | function
9 | mib2_system_sysDescr(prq)
10 | {
11 | /* XXX configuration */
12 | var val = snmp.data.createData({ type: 'OctetString',
13 | value: 'node-snmpjs' });
14 | snmp.provider.readOnlyScalar(prq, val);
15 | }
16 |
17 | function
18 | mib2_system_sysObjectID(prq)
19 | {
20 | var sun = '.1.3.6.1.4.1.42';
21 | var data = {
22 | type: 'ObjectIdentifier',
23 | value: sun + '.2.2.5'
24 | };
25 | var val = snmp.data.createData(data);
26 | snmp.provider.readOnlyScalar(prq, val);
27 | }
28 |
29 | function
30 | mib2_system_sysUpTime(prq)
31 | {
32 | /* XXX introspection */
33 | var val = snmp.data.createData({ type: 'TimeTicks', value: 0 });
34 | snmp.provider.readOnlyScalar(prq, val);
35 | }
36 |
37 | function
38 | mib2_system_sysContact(prq)
39 | {
40 | /* XXX configuration */
41 | var val = snmp.data.createData({ type: 'OctetString',
42 | value: 'Richard Nixon, trickydick@whitehouse.gov' });
43 | snmp.provider.readOnlyScalar(prq, val);
44 | }
45 |
46 | function
47 | mib2_system_sysName(prq)
48 | {
49 | var nodename = os.hostname();
50 | var val = snmp.data.createData({ type: 'OctetString',
51 | value: nodename });
52 | snmp.provider.readOnlyScalar(prq, val);
53 | }
54 |
55 | function
56 | mib2_system_sysLocation(prq)
57 | {
58 | /* XXX configuration */
59 | var val = snmp.data.createData({ type: 'OctetString',
60 | value: 'home' });
61 | snmp.provider.readOnlyScalar(prq, val);
62 | }
63 |
64 | function
65 | mib2_system_sysServices(prq)
66 | {
67 | /* XXX configuration */
68 | var val = snmp.data.createData({ type: 'Integer', value: 72 });
69 | snmp.provider.readOnlyScalar(prq, val);
70 | }
71 |
72 | var system = [
73 | {
74 | oid: '.1.3.6.1.2.1.1.1',
75 | handler: mib2_system_sysDescr
76 | },
77 | {
78 | oid: '.1.3.6.1.2.1.1.2',
79 | handler: mib2_system_sysObjectID
80 | },
81 | {
82 | oid: '.1.3.6.1.2.1.1.3',
83 | handler: mib2_system_sysUpTime
84 | },
85 | {
86 | oid: '.1.3.6.1.2.1.1.4',
87 | handler: mib2_system_sysContact
88 | },
89 | {
90 | oid: '.1.3.6.1.2.1.1.5',
91 | handler: mib2_system_sysName
92 | },
93 | {
94 | oid: '.1.3.6.1.2.1.1.6',
95 | handler: mib2_system_sysLocation
96 | },
97 | {
98 | oid: '.1.3.6.1.2.1.1.7',
99 | handler: mib2_system_sysServices
100 | } ];
101 |
102 | module.exports = system;
103 |
--------------------------------------------------------------------------------
/lib/protocol/data.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2014 Joyent, Inc. All rights reserved.
3 | */
4 |
5 | var util = require('util');
6 | var assert = require('assert');
7 | var ASN1 = require('asn1').Ber;
8 | var TypeConflictError = require('../errors/varbind').TypeConflictError;
9 | var uint64_t = require('./uint64_t.js');
10 |
11 | var MIN_INT32 = -2147483648;
12 | var MAX_INT32 = 2147483647;
13 | var MAX_UINT32 = 4294967295;
14 |
15 | var ERRORS = {
16 | noSuchObject: 0,
17 | noSuchInstance: 1,
18 | endOfMibView: 2
19 | };
20 |
21 | var tag_to_type = {};
22 | var type_to_obj = {};
23 |
24 | function
25 | registerType(arg)
26 | {
27 | if (typeof (arg) !== 'object')
28 | throw new TypeError('arg (required) must be an object');
29 | if (!arg.type || typeof (arg.type) !== 'string')
30 | throw new TypeError('arg.type (required) must be a string');
31 | if (!arg.tag && !arg.f) {
32 | throw new TypeError('at least one of arg.tag and arg.f ' +
33 | ' is required');
34 | }
35 | if (arg.tag && typeof (arg.tag) !== 'number')
36 | throw new TypeError('arg.tag must be an integer');
37 | if (arg.tag % 1 !== 0)
38 | throw new TypeError('arg.tag must be an integer');
39 | if (arg.tag < 0 || arg.tag > 255)
40 | throw new RangeError('arg.tag must be in [0, 255]');
41 | if (arg.f && typeof (arg.f) !== 'function')
42 | throw new TypeError('arg.f must be a function');
43 |
44 | if (arg.tag && !type_to_obj[arg.type] && !arg.f) {
45 | throw new TypeConflictError('type ' + arg.type +
46 | ' has no constructor registered');
47 | }
48 | if (arg.tag && arg.f && tag_to_type[arg.tag] &&
49 | tag_to_type[arg.tag] != arg.type) {
50 | throw new TypeConflictError('tag ' + arg.tag +
51 | ' is already registered to type ' + tag_to_type[arg.tag]);
52 | }
53 | if (arg.f && type_to_obj[arg.type] && type_to_obj[arg.type] != arg.f) {
54 | throw new TypeConflictError('type ' + arg.type +
55 | ' is already registered to a constructor');
56 | }
57 |
58 | if (arg.tag)
59 | tag_to_type[arg.tag] = arg.type;
60 | if (arg.f)
61 | type_to_obj[arg.type] = arg.f;
62 | }
63 |
64 | function
65 | isTagRegistered(tag) {
66 | if (typeof (tag) !== 'number')
67 | throw new TypeError('tag (number) is required');
68 | if (tag % 1 !== 0)
69 | throw new TypeError('tag must be an integer');
70 | if (tag < 0 || tag > 255)
71 | throw new RangeError('tag must be in [0, 255]');
72 |
73 | return (tag_to_type[tag] ? true : false);
74 | }
75 |
76 | function
77 | isTypeRegistered(type) {
78 | if (typeof (type) !== 'string')
79 | throw new TypeError('type (string) is required');
80 |
81 | return (type_to_obj[type] ? true : false);
82 | }
83 |
84 | function
85 | canonicalizeOID(oid)
86 | {
87 | var addr, canon;
88 | var i;
89 |
90 | if (typeof (oid) === 'object' && util.isArray(oid)) {
91 | addr = oid;
92 | } else if (typeof (oid) === 'string') {
93 | addr = oid.split('.');
94 | } else {
95 | throw new TypeError('oid (string or array) is required');
96 | }
97 |
98 | if (addr.length < 3)
99 | throw new RangeError('object identifier is too short');
100 |
101 | canon = [];
102 | for (i = 0; i < addr.length; i++) {
103 | var n;
104 |
105 | if (addr[i] === '') /* skip empty components */
106 | continue;
107 |
108 | /*
109 | * Number(x) will convert these to 1 and 0, but they're not
110 | * legal in an OID. All other bogus values will result in
111 | * NaN which we check below.
112 | */
113 | if (addr[i] === true || addr[i] === false) {
114 | throw new TypeError('object identifier component ' +
115 | addr[i] + ' is malformed');
116 | }
117 |
118 | n = Number(addr[i]);
119 |
120 | if (isNaN(n)) {
121 | throw new TypeError('object identifier component ' +
122 | addr[i] + ' is malformed');
123 | }
124 | if (n % 1 !== 0) {
125 | throw new TypeError('object identifier component ' +
126 | addr[i] + ' is not an integer');
127 | }
128 | if (i === 0 && n > 2) {
129 | throw new RangeError('object identifier does not ' +
130 | 'begin with 0, 1, or 2');
131 | }
132 | if (i === 1 && n > 39) {
133 | throw new RangeError('object identifier second ' +
134 | 'component ' + n + ' exceeds encoding limit of 39');
135 | }
136 | if (n < 0) {
137 | throw new RangeError('object identifier component ' +
138 | addr[i] + ' is negative');
139 | }
140 | if (n > MAX_INT32) {
141 | throw new RangeError('object identifier component ' +
142 | addr[i] + ' is too large');
143 | }
144 | canon.push(n);
145 | }
146 |
147 | return (canon);
148 | }
149 |
150 | function
151 | SnmpData()
152 | {
153 | var self = this;
154 |
155 | this._value = undefined;
156 |
157 | this.__defineGetter__('typename', function () {
158 | return (self._typename);
159 | });
160 | this.__defineGetter__('tag', function () {
161 | return (self._tag);
162 | });
163 | this.__defineGetter__('value', function () {
164 | return (self._value);
165 | });
166 | this.__defineSetter__('value', function (v) {
167 | throw new TypeError('Cannot set untyped value');
168 | });
169 | }
170 |
171 | SnmpData.prototype._tag = 0x100;
172 | SnmpData.prototype._typename = '__snmpjs_InvalidType';
173 | SnmpData.prototype.__snmpjs_magic = 'SnmpData';
174 |
175 | SnmpData.prototype.encode = function _encode(writer) {
176 | if (typeof (this._value) === 'undefined')
177 | throw new TypeError('Cannot encode undefined value');
178 |
179 | throw new TypeError('Cannot encode untyped data');
180 | };
181 |
182 | SnmpData.prototype.clone = function _clone() {
183 | var clone = new this.constructor(this._value);
184 |
185 | clone._tag = this._tag;
186 | clone._typename = this._typename;
187 |
188 | return (clone);
189 | };
190 |
191 | function
192 | SnmpInteger(value)
193 | {
194 | var self = this;
195 |
196 | SnmpData.call(this);
197 |
198 | this.__defineSetter__('value', function (v) {
199 | if (typeof (v) === 'object' && (v instanceof ASN1.Reader)) {
200 | self._tag = v.peek();
201 | v = v._readTag(self._tag);
202 | }
203 | if (typeof (v) !== 'number' || v % 1 !== 0)
204 | throw new TypeError('value is of incompatible type');
205 | if (v < MIN_INT32 || v > MAX_INT32) {
206 | throw new RangeError('value ' + v +
207 | ' out of range for ' + self._typename);
208 | }
209 | self._value = v >> 0;
210 | });
211 |
212 | if (value !== undefined)
213 | this.value = value;
214 | }
215 | util.inherits(SnmpInteger, SnmpData);
216 |
217 | SnmpInteger.prototype._tag = ASN1.Integer;
218 | SnmpInteger.prototype._typename = 'Integer';
219 |
220 | SnmpInteger.prototype.encode = function _integer_encode(writer) {
221 | writer.writeInt(this._value, this._tag);
222 | };
223 |
224 | function
225 | SnmpOctetString(value)
226 | {
227 | var self = this;
228 |
229 | SnmpData.call(this);
230 |
231 | this.__defineSetter__('value', function (v) {
232 | if (typeof (v) === 'object' && (v instanceof ASN1.Reader)) {
233 | var b;
234 | self._tag = v.peek();
235 | b = v.readString(self._tag, true);
236 | v = new Buffer(b.length);
237 | b.copy(v);
238 | self._value = b;
239 | } else if (Buffer.isBuffer(v)) {
240 | self._value = new Buffer(v.length);
241 | v.copy(self._value);
242 | } else if (typeof (v) === 'string') {
243 | self._value = new Buffer(v, 'ascii');
244 | } else {
245 | throw new TypeError('value is of incompatible type');
246 | }
247 | });
248 |
249 | if (value !== undefined)
250 | this.value = value;
251 | }
252 | util.inherits(SnmpOctetString, SnmpData);
253 |
254 | SnmpOctetString.prototype._tag = ASN1.OctetString;
255 | SnmpOctetString.prototype._typename = 'OctetString';
256 |
257 | SnmpOctetString.prototype.encode = function _octetstring_encode(writer) {
258 | writer.writeBuffer(this._value, this._tag);
259 | };
260 |
261 | function
262 | SnmpOID(value)
263 | {
264 | var self = this;
265 |
266 | SnmpData.call(this);
267 |
268 | this.__defineSetter__('value', function (v) {
269 | if (typeof (v) === 'object' && (v instanceof ASN1.Reader)) {
270 | self._tag = v.peek();
271 | v = v.readOID(self._tag);
272 | }
273 | if (typeof (v) !== 'string' &&
274 | (typeof (v) !== 'object' || !util.isArray(v)))
275 | throw new TypeError('value is of incompatible type');
276 | self._value = canonicalizeOID(v).join('.');
277 | });
278 |
279 | if (value !== undefined)
280 | this.value = value;
281 | }
282 | util.inherits(SnmpOID, SnmpData);
283 |
284 | SnmpOID.prototype._tag = ASN1.OID;
285 | SnmpOID.prototype._typename = 'ObjectIdentifier';
286 |
287 | SnmpOID.prototype.encode = function _oid_encode(writer) {
288 | writer.writeOID(this._value, this._tag);
289 | };
290 |
291 | function
292 | SnmpIpAddress(value)
293 | {
294 | var self = this;
295 |
296 | SnmpData.call(this);
297 |
298 | this.__defineSetter__('value', function (v) {
299 | if (typeof (v) === 'object' && (v instanceof ASN1.Reader)) {
300 | var buf, str;
301 |
302 | self._tag = v.peek();
303 | buf = v.readString(self._tag, true);
304 | if (buf.length != 4) {
305 | throw new RangeError('address length ' +
306 | buf.length + ' is incorrect');
307 | }
308 | str = buf.readUInt8(0) + '.' +
309 | buf.readUInt8(1) + '.' +
310 | buf.readUInt8(2) + '.' +
311 | buf.readUInt8(3);
312 | v = str;
313 | }
314 | if (typeof (v) !== 'string')
315 | throw new TypeError('value is of incompatible type');
316 |
317 | var o = v.split('.');
318 | var i;
319 |
320 | if (o.length != 4)
321 | throw new TypeError('address ' + v + ' is malformed');
322 |
323 | for (i = 0; i < 4; i++) {
324 | var n = Number(o[i]);
325 |
326 | if (isNaN(n) || n % 1 != 0) {
327 | throw new TypeError('component ' + o[i] +
328 | ' is not an integer');
329 | }
330 | if (o[i] < 0 || o[i] > 255) {
331 | throw new RangeError('component ' + o[i] +
332 | ' is out of range');
333 | }
334 | }
335 |
336 | self._value = v;
337 | });
338 |
339 | if (value !== undefined)
340 | this.value = value;
341 | }
342 | util.inherits(SnmpIpAddress, SnmpData);
343 |
344 | SnmpIpAddress.prototype._tag = 0x40 | 0x00;
345 | SnmpIpAddress.prototype._typename = 'IpAddress';
346 |
347 | SnmpIpAddress.prototype.encode = function _ipaddress_encode(writer) {
348 | var o = this._value.split('.');
349 | var buf = new Buffer(4);
350 |
351 | buf[0] = Number(o[0]);
352 | buf[1] = Number(o[1]);
353 | buf[2] = Number(o[2]);
354 | buf[3] = Number(o[3]);
355 |
356 | writer.writeBuffer(buf, this._tag);
357 | };
358 |
359 | function
360 | SnmpCounter32(value)
361 | {
362 | var self = this;
363 |
364 | SnmpData.call(this);
365 |
366 | this.__defineSetter__('value', function (v) {
367 | if (typeof (v) === 'object' && (v instanceof ASN1.Reader)) {
368 | var x, len, soff, lenlen;
369 |
370 | self._tag = v.readByte();
371 | soff = v.offset;
372 | lenlen = v.readLength() - soff;
373 | len = v.length;
374 | v.readByte(); /* Consume length byte */
375 | /*
376 | * ASN.1 BER 8.3. The standard allows integer encodings
377 | * to have the first octet contain all zeros as long as
378 | * the high bit of the second octet is set. While this
379 | * seems silly when there are 5 total octets for an
380 | * integer type that is constrained to 32 bits, it's
381 | * not technically illegal. If we hit this, just ignore
382 | * the first all-zero byte.
383 | *
384 | * Note that we need not handle the case where the first
385 | * byte is all 1s; that encoding would also have the
386 | * high bit of the second octet set, which is illegal.
387 | */
388 | if (len === 5) {
389 | x = v.readByte();
390 | if (x === 0)
391 | --len;
392 | }
393 | if (lenlen > 1 || len > 4)
394 | throw new RangeError('integer is too long');
395 |
396 | x = 0;
397 | for (; len > 0; len--) {
398 | x <<= 8;
399 | x |= v.readByte();
400 | }
401 | v = x >>> 0;
402 | }
403 | if (typeof (v) !== 'number')
404 | throw new TypeError('value is not a number');
405 | if (v % 1 !== 0)
406 | throw new TypeError('value is not an integer');
407 | if (v < 0 || v > MAX_UINT32) {
408 | throw new RangeError('value ' + v +
409 | ' out of range for ' + self._typename);
410 | }
411 |
412 | self._value = v;
413 | });
414 |
415 | if (value !== undefined)
416 | this.value = value;
417 | }
418 | util.inherits(SnmpCounter32, SnmpData);
419 |
420 | SnmpCounter32.prototype._tag = 0x40 | 0x01;
421 | SnmpCounter32.prototype._typename = 'Counter32';
422 |
423 | SnmpCounter32.prototype.encode = function _counter32_encode(writer) {
424 | var bytes = [];
425 | var v = this._value;
426 |
427 | writer.writeByte(this._tag);
428 |
429 | do {
430 | bytes.unshift(v & 0xff);
431 | v = v >>> 8;
432 | } while (v != 0);
433 |
434 | assert.ok(bytes.length <= 4);
435 | writer.writeByte(bytes.length);
436 |
437 | for (v = 0; v < bytes.length; v++)
438 | writer.writeByte(bytes[v]);
439 | };
440 |
441 | function
442 | SnmpUnsigned32(value)
443 | {
444 | SnmpCounter32.call(this);
445 |
446 | if (value !== undefined)
447 | this.value = value;
448 | }
449 | util.inherits(SnmpUnsigned32, SnmpCounter32);
450 |
451 | SnmpUnsigned32.prototype._tag = 0x40 | 0x02;
452 | SnmpUnsigned32.prototype._typename = 'Unsigned32';
453 |
454 | function
455 | SnmpTimeTicks(value)
456 | {
457 | SnmpCounter32.call(this);
458 |
459 | if (value !== undefined)
460 | this.value = value;
461 | }
462 | util.inherits(SnmpTimeTicks, SnmpCounter32);
463 |
464 | SnmpTimeTicks.prototype._tag = 0x40 | 0x03;
465 | SnmpTimeTicks.prototype._typename = 'TimeTicks';
466 |
467 | function
468 | SnmpOpaque(value)
469 | {
470 | SnmpOctetString.call(this);
471 |
472 | if (value !== undefined)
473 | this.value = value;
474 | }
475 | util.inherits(SnmpOpaque, SnmpOctetString);
476 |
477 | SnmpOpaque.prototype._tag = 0x40 | 0x04;
478 | SnmpOpaque.prototype._typename = 'Opaque';
479 |
480 | function
481 | SnmpCounter64(value)
482 | {
483 | var self = this;
484 |
485 | SnmpData.call(this);
486 |
487 | this.__defineSetter__('value', function (v) {
488 | if (typeof (v) === 'object' && (v instanceof ASN1.Reader)) {
489 | var hi, lo, soff, len, lenlen;
490 |
491 | self._tag = v.readByte();
492 | soff = v.offset;
493 | lenlen = v.readLength() - soff;
494 | len = v.length;
495 | v.readByte(); /* Consume length byte */
496 | /* See analogous comment for Counter32. */
497 | if (len === 9) {
498 | lo = v.readByte();
499 | if (lo === 0)
500 | --len;
501 | }
502 | if (lenlen > 1 || len > 8)
503 | throw new RangeError('integer is too long');
504 | hi = lo = 0;
505 | for (; len > 4; len--) {
506 | hi <<= 8;
507 | hi |= v.readByte();
508 | }
509 | for (; len > 0; len--) {
510 | lo <<= 8;
511 | lo |= v.readByte();
512 | }
513 | self._value = new uint64_t(hi, lo);
514 | } else if (typeof (v) === 'number') {
515 | if (v % 1 !== 0)
516 | throw new TypeError('value ' + v +
517 | ' is not an integer');
518 | if (v < 0 || v > MAX_UINT32)
519 | throw new RangeError('value ' + v +
520 | ' is out of range or unrepresentable');
521 |
522 | self._value = new uint64_t(0, v);
523 | } else if (typeof (v) === 'object') {
524 | if (!v.hasOwnProperty('lo') || !v.hasOwnProperty('hi'))
525 | throw new TypeError('value is missing ' +
526 | 'required lo and/or hi properties');
527 | if (typeof (v.hi) !== 'number' || v.hi % 1 !== 0)
528 | throw new TypeError('v.hi is not an integer');
529 | if (typeof (v.lo) !== 'number' || v.lo % 1 !== 0)
530 | throw new TypeError('v.lo is not an integer');
531 | if (v.hi < 0 || v.hi > MAX_UINT32 ||
532 | v.lo < 0 || v.lo > MAX_UINT32)
533 | throw new RangeError('one or both components ' +
534 | 'is out of representable range');
535 |
536 | self._value = new uint64_t(v.hi, v.lo);
537 | } else {
538 | throw new TypeError('value is of incompatible type');
539 | }
540 | });
541 |
542 | if (value !== undefined)
543 | this.value = value;
544 | }
545 | util.inherits(SnmpCounter64, SnmpData);
546 |
547 | SnmpCounter64.prototype._tag = 0x40 | 0x06;
548 | SnmpCounter64.prototype._typename = 'Counter64';
549 |
550 | SnmpCounter64.prototype.encode = function _counter64_encode(writer) {
551 | var bytes;
552 | var i;
553 |
554 | writer.writeByte(this._tag);
555 |
556 | bytes = this._value.toOctets();
557 | assert.ok(bytes.length <= 8);
558 | writer.writeByte(bytes.length);
559 |
560 | for (i = 0; i < bytes.length; i++)
561 | writer.writeByte(bytes[i]);
562 | };
563 |
564 | function
565 | SnmpNull(value)
566 | {
567 | var self = this;
568 |
569 | SnmpData.call(this);
570 |
571 | this.__defineSetter__('value', function (v) {
572 | if (typeof (v) === 'object' && (v instanceof ASN1.Reader)) {
573 | self._tag = v.readByte();
574 | if (v.readByte() !== 0) {
575 | throw new RangeError('Null value has nonzero ' +
576 | 'length');
577 | }
578 | } else if (v === null) {
579 | self._value = null;
580 | return;
581 | } else if (typeof (v) === 'number') {
582 | if (v % 1 !== 0) {
583 | throw new TypeError('null value ' + v +
584 | ' is not an integer');
585 | }
586 | if (v < 0 || v > 0x7f) {
587 | throw new RangeError('null value ' + v +
588 | ' is out of range [0, 0x7f]');
589 | }
590 | self._tag = ASN1.Context | v;
591 | } else {
592 | throw new TypeError('value must be null or a number');
593 | }
594 | switch (self._tag) {
595 | case ASN1.Context | ERRORS.noSuchObject:
596 | self._value = ERRORS.noSuchObject;
597 | break;
598 | case ASN1.Context | ERRORS.noSuchInstance:
599 | self._value = ERRORS.noSuchInstance;
600 | break;
601 | case ASN1.Context | ERRORS.endOfMibView:
602 | self._value = ERRORS.endOfMibView;
603 | break;
604 | case ASN1.Null:
605 | default:
606 | self._value = null;
607 | break;
608 | }
609 | });
610 |
611 | if (value !== undefined)
612 | this.value = value;
613 | }
614 | util.inherits(SnmpNull, SnmpData);
615 |
616 | SnmpNull.prototype._tag = ASN1.Null;
617 | SnmpNull.prototype._typename = 'Null';
618 |
619 | SnmpNull.prototype.encode = function _null_encode(writer) {
620 | if (this._value === null) {
621 | writer.writeNull();
622 | } else {
623 | writer.writeByte(ASN1.Context | this._value);
624 | writer.writeByte(0x00);
625 | }
626 | };
627 |
628 | function
629 | createData(arg)
630 | {
631 | var type, tag;
632 | var f;
633 |
634 | if (typeof (arg) !== 'object')
635 | throw new TypeError('arg (object) is required');
636 |
637 | type = arg.type;
638 |
639 | if (typeof (type) === 'undefined') {
640 | if (typeof (arg.value) !== 'object' ||
641 | !(arg.value instanceof ASN1.Reader)) {
642 | throw new TypeError('type (string) is required ' +
643 | 'when value is not an ASN.1 BER reader');
644 | }
645 | tag = arg.value.peek();
646 | type = tag_to_type[tag];
647 | if (!type) {
648 | throw new ReferenceError('data type for ASN.1 tag ' +
649 | tag + ' unknown');
650 | }
651 | }
652 |
653 | if (typeof (type) === 'number' && type % 1 === 0) {
654 | type = tag_to_type[type];
655 | if (!type) {
656 | throw new ReferenceError('tag ' + arg.type +
657 | ' has no registered type');
658 | }
659 | } else if (typeof (type) !== 'string') {
660 | throw new TypeError('type (string) is required');
661 | }
662 |
663 | if (!(f = type_to_obj[type])) {
664 | throw new ReferenceError('data type ' + type +
665 | ' has no registered constructor');
666 | }
667 |
668 | return (new f(arg.value));
669 | }
670 |
671 | module.exports = function _data_init() {
672 | var data = {
673 | SnmpData: SnmpData,
674 | createData: createData,
675 | registerType: registerType,
676 | isTagRegistered: isTagRegistered,
677 | isTypeRegistered: isTypeRegistered,
678 | canonicalizeOID: canonicalizeOID
679 | };
680 |
681 | var stock = [
682 | SnmpInteger,
683 | SnmpOctetString,
684 | SnmpOID,
685 | SnmpIpAddress,
686 | SnmpCounter32,
687 | SnmpUnsigned32,
688 | SnmpTimeTicks,
689 | SnmpOpaque,
690 | SnmpCounter64,
691 | SnmpNull
692 | ];
693 |
694 | stock.forEach(function (t) {
695 | registerType({
696 | type: t.prototype._typename,
697 | tag: t.prototype._tag,
698 | f: t
699 | });
700 | });
701 |
702 | /*
703 | * These aliases exist so that we can encode inline varbind error status
704 | * markers. The specifications describe these as being of the NULL type
705 | * even though they have their own tags (which are in fact values more
706 | * than anything, since the value portion of these objects is empty).
707 | */
708 | registerType({
709 | type: 'Null',
710 | tag: ASN1.Context | ERRORS.noSuchObject
711 | });
712 | registerType({
713 | type: 'Null',
714 | tag: ASN1.Context | ERRORS.noSuchInstance
715 | });
716 | registerType({
717 | type: 'Null',
718 | tag: ASN1.Context | ERRORS.endOfMibView
719 | });
720 |
721 | Object.keys(ERRORS).forEach(function (e) {
722 | data.__defineGetter__(e, function () { return (ERRORS[e]); });
723 | });
724 |
725 | data.isSnmpData = function (d) {
726 | return ((typeof (d.__snmpjs_magic) == 'string' &&
727 | d.__snmpjs_magic === 'SnmpData') ? true : false);
728 | };
729 |
730 | return (data);
731 | }();
732 |
--------------------------------------------------------------------------------
/lib/protocol/message.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012 Joyent, Inc. All rights reserved.
3 | */
4 |
5 | var util = require('util');
6 | var ASN1 = require('asn1').Ber;
7 | var lexer = require('../lexer');
8 | var PDU = require('./pdu');
9 | var varbind = require('./varbind');
10 | var data = require('./data');
11 | var parser = require('../parser').parser;
12 | var EmptyMessageError = require('../errors/message').EmptyMessageError;
13 | var MessageParseError = require('../errors/message').MessageParseError;
14 | var NoSupportError = require('../errors/message').NoSupportError;
15 |
16 | function
17 | _set_bind(msg, key, primitive, type) {
18 | return (function (v) {
19 | if (typeof (v) === primitive) {
20 | v = data.createData({ value: v,
21 | type: type });
22 | }
23 | if (typeof (v) !== 'object' || !data.isSnmpData(v) ||
24 | v.typename != type) {
25 | throw new TypeError(key + ' must be a ' + primitive +
26 | ' or SNMP data object of type ' + type);
27 | }
28 |
29 | msg[key] = v;
30 | });
31 | }
32 |
33 | function
34 | SnmpMessage(arg)
35 | {
36 | var self = this;
37 | var version, community;
38 |
39 | if (typeof (arg) !== 'object')
40 | throw new TypeError('arg must be an object');
41 |
42 | if (typeof (arg.version) === 'undefined')
43 | throw new TypeError('arg.version is required');
44 | if (typeof (arg.version) === 'object' && data.isSnmpData(arg.version))
45 | version = arg.version;
46 | else
47 | version = data.createData({ value: arg.version,
48 | type: 'Integer' });
49 | if (typeof (version) !== 'object' || !data.isSnmpData(version) ||
50 | version.typename != 'Integer') {
51 | throw new TypeError('arg.version must be an integer or ' +
52 | ' an SNMP data object of type Integer');
53 | }
54 |
55 | if (typeof (arg.community) === 'undefined')
56 | throw new TypeError('arg.community is required');
57 | if (typeof (arg.community) === 'object' &&
58 | data.isSnmpData(arg.community))
59 | community = arg.community;
60 | else
61 | community = data.createData({ value: arg.community,
62 | type: 'OctetString' });
63 | if (typeof (community) !== 'object' || !data.isSnmpData(community) ||
64 | community.typename != 'OctetString') {
65 | throw new TypeError('arg.community must be a string or ' +
66 | ' an SNMP data object of type OctetString');
67 | }
68 | switch (version.value) {
69 | case 0:
70 | case 1:
71 | break;
72 | case 3:
73 | default:
74 | throw new NoSupportError('SNMPv3 is unsupported');
75 | }
76 |
77 | this._version = version;
78 | this._community = community;
79 | this._raw = this._src = undefined;
80 |
81 | this.__defineGetter__('version', function () {
82 | return (self._version.value);
83 | });
84 | this.__defineGetter__('community', function () {
85 | return (self._community.value);
86 | });
87 | this.__defineGetter__('raw', function () {
88 | return (self._raw);
89 | });
90 | this.__defineGetter__('src', function () {
91 | return (self._src);
92 | });
93 | this.__defineGetter__('pdu', function () {
94 | return (self._pdu);
95 | });
96 | this.__defineSetter__('pdu', function (v) {
97 | if (typeof (v) !== 'object' || !PDU.isSnmpPDU(v))
98 | throw new TypeError('pdu must be an object');
99 |
100 | self._pdu = v;
101 | });
102 |
103 | if (arg.pdu)
104 | this.pdu = arg.pdu;
105 | }
106 |
107 | SnmpMessage.prototype.__snmpjs_magic = 'SnmpMessage';
108 |
109 | SnmpMessage.prototype._setOrigin = function _setOrigin(raw, src)
110 | {
111 | this._raw = raw;
112 | this._src = src;
113 | };
114 |
115 | SnmpMessage.prototype.encode = function encode()
116 | {
117 | var writer = new ASN1.Writer();
118 |
119 | if (!this._community)
120 | throw new TypeError('Message is missing a community');
121 | if (!this._pdu)
122 | throw new TypeError('Message contains no PDU');
123 | if (this._raw)
124 | throw new TypeError('Message has already been encoded');
125 |
126 | writer.startSequence();
127 | this._version.encode(writer);
128 | this._community.encode(writer);
129 | this._pdu.encode(writer);
130 | writer.endSequence();
131 |
132 | this._raw = {
133 | buf: writer.buffer,
134 | len: writer.buffer.length
135 | };
136 | };
137 |
138 | function
139 | ParseContext()
140 | {
141 | this.ASN1 = ASN1;
142 | this.pdu = PDU;
143 | this.varbind = varbind;
144 | this.data = data;
145 | this.message = module.exports;
146 | this.content = undefined;
147 | }
148 |
149 | ParseContext.prototype.parse = function parse(raw, src)
150 | {
151 | /*
152 | * This is vile. Unfortunately, the parser generated by Jison isn't an
153 | * object instance, nor is it anything that can construct one. This
154 | * doesn't really matter because we don't do anything asynchronous
155 | * during parsing, but it's still wrong.
156 | */
157 | parser.yy = this;
158 |
159 | parser.parse(raw.buf);
160 | if (!this.content)
161 | throw new EmptyMessageError();
162 |
163 | this.content._setOrigin(raw, src);
164 | return (this.content);
165 | };
166 |
167 | ParseContext.prototype.parseError = function parseError(str, hash)
168 | {
169 | throw new MessageParseError(str, hash);
170 | };
171 |
172 | ParseContext.prototype.setContent = function setContent(content)
173 | {
174 | this.content = content;
175 | };
176 |
177 | function
178 | parseMessage(arg)
179 | {
180 | var ctx;
181 |
182 | if (typeof (arg) !== 'object')
183 | throw new TypeError('arg (object) is required');
184 | if (typeof (arg.raw) !== 'object')
185 | throw new TypeError('arg.raw (object) is required');
186 | if (Buffer.isBuffer(arg.raw)) {
187 | arg.raw = {
188 | buf: arg.raw,
189 | len: arg.raw.length
190 | };
191 | }
192 | if (typeof (arg.raw.buf) !== 'object' || !Buffer.isBuffer(arg.raw.buf))
193 | throw new TypeError('arg.raw does not contain a Buffer');
194 |
195 | ctx = new ParseContext();
196 | return (ctx.parse(arg.raw, arg.src));
197 | }
198 |
199 | function
200 | createMessage(arg)
201 | {
202 | return (new SnmpMessage(arg));
203 | }
204 |
205 | function
206 | strversion(ver)
207 | {
208 | if (typeof (ver) !== 'number')
209 | throw new TypeError('ver (number) is required');
210 | switch (ver) {
211 | case 0:
212 | return ('v1(0)');
213 | case 1:
214 | return ('v2c(1)');
215 | case 3:
216 | return ('v3(3)');
217 | default:
218 | return ('(' + ver + ')');
219 | }
220 | }
221 |
222 | function
223 | bunyan_serialize_snmpmsg(snmpmsg)
224 | {
225 | var i;
226 | var obj = {
227 | version: strversion(snmpmsg.version),
228 | community: snmpmsg.community.toString()
229 | };
230 |
231 | if (snmpmsg.pdu.op === PDU.Trap) {
232 | obj.pdu = {
233 | op: PDU.strop(snmpmsg.pdu.op),
234 | enterprise: snmpmsg.pdu.enterprise,
235 | agent_addr: snmpmsg.pdu.agent_addr,
236 | generic_trap: snmpmsg.pdu.generic_trap,
237 | specific_trap: snmpmsg.pdu.specific_trap,
238 | time_stamp: snmpmsg.pdu.time_stamp,
239 | varbinds: []
240 | };
241 | } else {
242 | obj.pdu = {
243 | op: PDU.strop(snmpmsg.pdu.op),
244 | request_id: snmpmsg.pdu.request_id,
245 | error_status: PDU.strerror(snmpmsg.pdu.error_status),
246 | error_index: snmpmsg.pdu.error_index,
247 | varbinds: []
248 | };
249 | }
250 | for (i = 0; i < snmpmsg.pdu.varbinds.length; i++) {
251 | var dv = snmpmsg.pdu.varbinds[i].data.value;
252 | var vb = {
253 | oid: snmpmsg.pdu.varbinds[i].oid,
254 | typename: snmpmsg.pdu.varbinds[i].data.typename,
255 | value: dv,
256 | string_value: dv.toString()
257 | };
258 | obj.pdu.varbinds.push(vb);
259 | }
260 |
261 | return (obj);
262 | }
263 |
264 | module.exports = function _message_init() {
265 | var message = {
266 | parseMessage: parseMessage,
267 | createMessage: createMessage,
268 | strversion: strversion,
269 | serializer: bunyan_serialize_snmpmsg
270 | };
271 |
272 | message.isSnmpMessage = function (m) {
273 | return ((typeof (m.__snmpjs_magic) === 'string' &&
274 | m.__snmpjs_magic === 'SnmpMessage') ? true : false);
275 | };
276 |
277 | parser.lexer = new lexer();
278 |
279 | return (message);
280 | }();
281 |
--------------------------------------------------------------------------------
/lib/protocol/pdu.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012 Joyent, Inc. All rights reserved.
3 | */
4 |
5 | var util = require('util');
6 | var ASN1 = require('asn1').Ber;
7 | var varbind = require('./varbind');
8 | var data = require('./data');
9 |
10 | var OPS = {
11 | GetRequest: 0,
12 | GetNextRequest: 1,
13 | Response: 2,
14 | SetRequest: 3,
15 | Trap: 4, /* OBSOLETE! */
16 | GetBulkRequest: 5,
17 | InformRequest: 6,
18 | SNMPv2_Trap: 7,
19 | Report: 8
20 | };
21 |
22 | var ERRORS = {
23 | noError: 0,
24 | tooBig: 1,
25 | noSuchName: 2,
26 | badValue: 3,
27 | readOnly: 4,
28 | genErr: 5,
29 | noAccess: 6,
30 | wrongType: 7,
31 | wrongLength: 8,
32 | wrongEncoding: 9,
33 | wrongValue: 10,
34 | noCreation: 11,
35 | inconsistentValue: 12,
36 | resourceUnavailable: 13,
37 | commitFailed: 14,
38 | undoFailed: 15,
39 | authorizationError: 16,
40 | notWritable: 17,
41 | inconsistentName: 18
42 | };
43 |
44 | var TRAPS_V1 = {
45 | coldStart: 0,
46 | warmStart: 1,
47 | linkDown: 2,
48 | linkUp: 3,
49 | authenticationFailure: 4,
50 | egpNeighborLoss: 5,
51 | enterpriseSpecific: 6
52 | };
53 |
54 | function
55 | SnmpPDU(arg)
56 | {
57 | var self = this;
58 |
59 | if (typeof (arg) !== 'object')
60 | throw new TypeError('arg (object) is required');
61 |
62 | if (typeof (arg.op) !== 'number')
63 | throw new TypeError('arg.op (number) is required');
64 | if (arg.op < 0 || arg.op > 31) /* ASN.1 limitation */
65 | throw new RangeError('op ' + arg.op + ' is out of range');
66 |
67 | this._op = arg.op;
68 | this._varbinds = [];
69 |
70 | this.__defineGetter__('op', function () {
71 | return (self._op);
72 | });
73 | this.__defineGetter__('varbinds', function () {
74 | return (self._varbinds);
75 | });
76 | this.__defineSetter__('varbinds', function (v) {
77 | var i = 0;
78 |
79 | if (typeof (v) === 'object' && varbind.isSnmpVarbind(v)) {
80 | self._varbinds = [ v ];
81 | return;
82 | }
83 |
84 | if (typeof (v) !== 'object' || !util.isArray(v))
85 | throw new TypeError('varbinds must be an array');
86 |
87 | for (i = 0; i < v.length; i++) {
88 | if (typeof (v[i]) !== 'object' ||
89 | !varbind.isSnmpVarbind(v[i])) {
90 | throw new TypeError('varbinds[' + i + '] is ' +
91 | 'of incompatible type');
92 | }
93 | }
94 | self._varbinds = v;
95 | });
96 |
97 | if (arg.varbinds)
98 | this.varbinds = arg.varbinds;
99 | }
100 |
101 | SnmpPDU.prototype.__snmpjs_magic = 'SnmpPDU';
102 |
103 | SnmpPDU.prototype.clone = function () {
104 | throw new TypeError('Cannot clone base PDU object');
105 | };
106 |
107 | SnmpPDU.prototype.cloneAs = function (op) {
108 | throw new TypeError('Cannot clone base PDU object');
109 | };
110 |
111 | SnmpPDU.prototype.encode = function (writer) {
112 | throw new TypeError('Cannot encode base PDU object');
113 | };
114 |
115 | function
116 | _set_bind(pdu, key, primitive, type) {
117 | return (function (v) {
118 | if (typeof (v) === primitive) {
119 | v = data.createData({ value: v,
120 | type: type });
121 | }
122 | if (typeof (v) !== 'object' || !data.isSnmpData(v) ||
123 | v.typename != type) {
124 | throw new TypeError(key + ' must be a ' + primitive +
125 | ' or SNMP data object of type ' + type);
126 | }
127 |
128 | pdu[key] = v;
129 | });
130 | }
131 |
132 | function
133 | SnmpStdPDU(arg)
134 | {
135 | var self = this;
136 | var request_id;
137 | var f;
138 | var errst_if_name;
139 | var erridx_if_name;
140 |
141 | SnmpPDU.call(this, arg);
142 |
143 | if (this._op === SnmpPDU.Trap)
144 | throw new TypeError('cannot create standard PDU as v1 trap');
145 |
146 | if (typeof (arg.request_id) === 'undefined')
147 | throw new TypeError('arg.request_id is required');
148 | if (typeof (arg.request_id) === 'number') {
149 | request_id = data.createData({ value: arg.request_id,
150 | type: 'Integer' });
151 | } else {
152 | request_id = arg.request_id;
153 | }
154 | if (typeof (request_id) !== 'object' || !data.isSnmpData(request_id) ||
155 | request_id.typename != 'Integer') {
156 | throw new TypeError('arg.request_id must be integer or ' +
157 | ' and SNMP data object of type Integer');
158 | }
159 |
160 | this._request_id = request_id;
161 | this._error_status = data.createData({ value: 0, type: 'Integer' });
162 | this._error_index = data.createData({ value: 0, type: 'Integer' });
163 |
164 | this.__defineGetter__('request_id', function () {
165 | return (self._request_id.value);
166 | });
167 |
168 | /*
169 | * We cheat a little here, taking advantage of the standard's very
170 | * deliberate use of the same structure for all PDU types. The
171 | * interpretation of these two values are different for GetBulk
172 | * requests, but they are otherwise the same so we will avoid creating
173 | * yet another PDU type and just present the appropriate names as
174 | * getters and setters.
175 | */
176 | if (this.op == OPS.GetBulkRequest) {
177 | errst_if_name = 'non_repeaters';
178 | erridx_if_name = 'max_repetitions';
179 | } else {
180 | errst_if_name = 'error_status';
181 | erridx_if_name = 'error_index';
182 | }
183 |
184 | this.__defineGetter__(errst_if_name, function () {
185 | return (self._error_status.value);
186 | });
187 | this.__defineSetter__(errst_if_name,
188 | _set_bind(self, '_error_status', 'number', 'Integer'));
189 | this.__defineGetter__(erridx_if_name, function () {
190 | return (self._error_index.value);
191 | });
192 | f = _set_bind(self, '_error_index', 'number', 'Integer');
193 | if (this.op == OPS.GetBulkRequest) {
194 | this.__defineSetter__(erridx_if_name, f);
195 | } else {
196 | this.__defineSetter__(erridx_if_name, function (v) {
197 | if (v < 0 || v > self._varbinds.length) {
198 | throw new RangeError('error index ' + v +
199 | ' is out of range');
200 | }
201 | f(v);
202 | });
203 | }
204 | }
205 | util.inherits(SnmpStdPDU, SnmpPDU);
206 |
207 | SnmpStdPDU.prototype.cloneAs = function (op) {
208 | var clone = new this.constructor({ op: op,
209 | request_id: this._request_id });
210 | var i;
211 |
212 | clone._error_status = this._error_status;
213 | clone._error_index = this._error_index;
214 | for (i = 0; i < this._varbinds.length; i++)
215 | clone.varbinds.push(this._varbinds[i].clone());
216 |
217 | return (clone);
218 | };
219 |
220 | SnmpStdPDU.prototype.clone = function () {
221 | return (this.cloneAs(this._op));
222 | };
223 |
224 | SnmpStdPDU.prototype.encode = function (writer) {
225 | var i;
226 |
227 | if (this._op == OPS.GetBulkRequest &&
228 | this._varbinds.length < this.non_repeaters) {
229 | throw new RangeError('number of non-repeater varbinds is ' +
230 | 'greater than the total varbind count');
231 | }
232 |
233 | writer.startSequence(ASN1.Context | ASN1.Constructor | this._op);
234 | this._request_id.encode(writer);
235 | this._error_status.encode(writer);
236 | this._error_index.encode(writer);
237 |
238 | writer.startSequence();
239 | for (i = 0; i < this._varbinds.length; i++)
240 | this._varbinds[i].encode(writer);
241 | writer.endSequence();
242 | writer.endSequence();
243 | };
244 |
245 | function
246 | SnmpTrapV1PDU(arg)
247 | {
248 | var self = this;
249 |
250 | SnmpPDU.call(this, arg);
251 |
252 | this.__defineGetter__('enterprise', function () {
253 | if (self._enterprise)
254 | return (self._enterprise.value);
255 | return (undefined);
256 | });
257 | this.__defineSetter__('enterprise',
258 | _set_bind(self, '_enterprise', 'string', 'ObjectIdentifier'));
259 | this.__defineGetter__('agent_addr', function () {
260 | if (self._agent_addr)
261 | return (self._agent_addr.value);
262 | return (undefined);
263 | });
264 | this.__defineSetter__('agent_addr',
265 | _set_bind(self, '_agent_addr', 'string', 'IpAddress'));
266 | this.__defineGetter__('generic_trap', function () {
267 | if (self._generic_trap)
268 | return (self._generic_trap.value);
269 | return (undefined);
270 | });
271 | this.__defineSetter__('generic_trap',
272 | _set_bind(self, '_generic_trap', 'number', 'Integer'));
273 | this.__defineGetter__('specific_trap', function () {
274 | if (self._specific_trap)
275 | return (self._specific_trap.value);
276 | return (undefined);
277 | });
278 | this.__defineSetter__('specific_trap',
279 | _set_bind(self, '_specific_trap', 'number', 'Integer'));
280 | this.__defineGetter__('time_stamp', function () {
281 | if (self._time_stamp)
282 | return (self._time_stamp.value);
283 | return (undefined);
284 | });
285 | this.__defineSetter__('time_stamp',
286 | _set_bind(self, '_time_stamp', 'number', 'TimeTicks'));
287 |
288 | if (arg.enterprise)
289 | this.enterprise = arg.enterprise;
290 | if (arg.agent_addr)
291 | this.agent_addr = arg.agent_addr;
292 | if (arg.generic_trap)
293 | this.generic_trap = arg.generic_trap;
294 | if (arg.specific_trap)
295 | this.specific_trap = arg.specific_trap;
296 | if (arg.time_stamp)
297 | this.time_stamp = arg.time_stamp;
298 | }
299 | util.inherits(SnmpTrapV1PDU, SnmpPDU);
300 |
301 | SnmpTrapV1PDU.prototype.encode = function (writer) {
302 | var i;
303 |
304 | writer.startSequence(ASN1.Context | ASN1.Constructor | this._op);
305 | this._enterprise.encode(writer);
306 | this._agent_addr.encode(writer);
307 | this._generic_trap.encode(writer);
308 | this._specific_trap.encode(writer);
309 | this._time_stamp.encode(writer);
310 |
311 | writer.startSequence();
312 | for (i = 0; i < this._varbinds.length; i++)
313 | this._varbinds[i].encode(writer);
314 | writer.endSequence();
315 | writer.endSequence();
316 | };
317 |
318 | function
319 | createPDU(arg)
320 | {
321 | if (typeof (arg) !== 'object')
322 | throw new TypeError('arg (object) is required');
323 | if (typeof (arg.op) !== 'number')
324 | throw new TypeError('arg.op (number) is reguired');
325 |
326 | switch (arg.op) {
327 | case OPS.Trap:
328 | return (new SnmpTrapV1PDU(arg));
329 | default:
330 | return (new SnmpStdPDU(arg));
331 | }
332 | }
333 |
334 | function
335 | strop(op)
336 | {
337 | var i;
338 | if (typeof (op) != 'number')
339 | throw new TypeError('op (number) is required');
340 | for (i in OPS) {
341 | if (OPS.hasOwnProperty(i) && OPS[i] == op)
342 | return (i + '(' + op + ')');
343 | }
344 | return ('(' + op + ')');
345 | }
346 |
347 | function
348 | strerror(err)
349 | {
350 | var i;
351 | if (typeof (err) != 'number')
352 | throw new TypeError('err (number) is required');
353 | for (i in ERRORS) {
354 | if (ERRORS.hasOwnProperty(i) && ERRORS[i] == err)
355 | return (i + '(' + err + ')');
356 | }
357 | return ('(' + err + ')');
358 | }
359 |
360 | function
361 | strtrap(trap)
362 | {
363 | var i;
364 | if (typeof (trap) !== 'number')
365 | throw new TypeError('trap (number) is required');
366 | for (i in TRAPS_V1) {
367 | if (TRAPS_V1.hasOwnProperty(i) && TRAPS_V1[i] == trap)
368 | return (i + '(' + trap + ')');
369 | }
370 | return ('(' + trap + ')');
371 | }
372 |
373 | module.exports = function _pdu_init() {
374 | var PDU = {
375 | SnmpPDU: SnmpPDU,
376 | createPDU: createPDU,
377 | strop: strop,
378 | strerror: strerror,
379 | strtrap: strtrap
380 | };
381 |
382 | Object.keys(OPS).forEach(function (o) {
383 | PDU.__defineGetter__(o, function () { return (OPS[o]); });
384 | });
385 |
386 | Object.keys(ERRORS).forEach(function (e) {
387 | PDU.__defineGetter__(e, function () { return (ERRORS[e]); });
388 | });
389 |
390 | Object.keys(TRAPS_V1).forEach(function (t) {
391 | PDU.__defineGetter__(t, function () { return (TRAPS_V1[t]); });
392 | });
393 |
394 | PDU.isSnmpPDU = function (p) {
395 | return ((typeof (p.__snmpjs_magic) === 'string' &&
396 | p.__snmpjs_magic === 'SnmpPDU') ? true : false);
397 | };
398 |
399 | return (PDU);
400 | }();
401 |
--------------------------------------------------------------------------------
/lib/protocol/uint64_t.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2014 Joyent, Inc. All rights reserved.
3 | */
4 |
5 | /*
6 | * Rudimentary 64-bit unsigned integer operations. This is needed only for
7 | * one thing: formatting Counter64 values as decimal strings. Since we wrap
8 | * the pair of 32-bit values in this object type, we also use toOctets() to
9 | * encode the value without inspecting its contents.
10 | *
11 | * The division by 10 algorithm is courtesy of the Hacker's Delight 10-17,
12 | * figure 10-10. The rest is trivial and obvious. The operations are named
13 | * in the same way as the amd64 instructions that do the same thing. Two
14 | * things are not supported, at all, because they aren't needed by snmpjs:
15 | *
16 | * - signed numbers
17 | * - division, except by 10
18 | *
19 | * This is all needed only because Javascript provides only 53 bits of
20 | * precision in Numbers, a notorious language defect.
21 | */
22 |
23 | var fmt = require('util').format;
24 | var TEN = new uint64_t(0, 10);
25 |
26 | function
27 | uint64_t(hi, lo)
28 | {
29 | if (typeof (hi) === 'string' && typeof (lo) === 'undefined') {
30 | var s = hi;
31 | if (hi.substr(0, 2) === '0x') {
32 | hi = hi.slice(2);
33 |
34 | if (hi.length > 16)
35 | throw new RangeError('hex value too large');
36 |
37 | lo = parseInt(hi.substr(-8, 8), 16);
38 | if (hi.length > 8)
39 | hi = parseInt(hi.substr(0, hi.length - 8), 16);
40 | else
41 | hi = 0;
42 | } else if (hi.substr(0, 1) === '0') {
43 | var i;
44 | hi = 0;
45 | lo = 0;
46 |
47 | for (i = 0; i < (s.length - 1) * 3; i++) {
48 | var p = s.length - 1 - ((i / 3) >>> 0);
49 | var d = parseInt(s.charAt(p), 8);
50 |
51 | if (i > 8)
52 | hi |= !!(d & (1 << (i % 3))) << (i - 8);
53 | else
54 | lo |= !!(d & (1 << (i % 3))) << i;
55 | }
56 | } else {
57 | var a = new uint64_t(0, 0);
58 |
59 | for (i = 0; i < s.length; i++) {
60 | a = mulq(a, 10);
61 | a = addq(a, new uint64_t(0,
62 | parseInt(s.charAt(i), 10)));
63 | }
64 |
65 | this._hi = a._hi;
66 | this._lo = a._lo;
67 | return;
68 | }
69 | } else if (typeof (hi) === 'number' && typeof (lo) === 'undefined') {
70 | lo = hi;
71 | hi = 0;
72 | }
73 |
74 | if (hi % 1 !== 0 || lo % 1 !== 0)
75 | throw new TypeError('hi and lo components must be integers');
76 |
77 | this._hi = hi >>> 0;
78 | this._lo = lo >>> 0;
79 | }
80 |
81 | function
82 | typecheck(a)
83 | {
84 | if (typeof (a) !== 'object')
85 | a = new uint64_t(a);
86 |
87 | if (typeof (a) !== 'object' ||
88 | typeof (a._hi) !== 'number' || typeof (a._lo) !== 'number')
89 | throw new TypeError('argument must be a uint64_t');
90 |
91 | return (a);
92 | }
93 |
94 | function
95 | addq(a, b)
96 | {
97 | var s0, s16, s32, s48;
98 | var a0, a16, a32, a48;
99 | var b0, b16, b32, b48;
100 |
101 | a = typecheck(a);
102 | b = typecheck(b);
103 |
104 | a0 = (a._lo & 0xffff) >>> 0;
105 | b0 = (b._lo & 0xffff) >>> 0;
106 | s0 = (a0 + b0) >>> 0;
107 |
108 | a16 = (a._lo & 0xffff0000) >>> 16;
109 | b16 = (b._lo & 0xffff0000) >>> 16;
110 | s16 = (a16 + b16 + ((s0 & 0x10000) ? 1 : 0)) >>> 0;
111 |
112 | a32 = (a._hi & 0xffff) >>> 0;
113 | b32 = (b._hi & 0xffff) >>> 0;
114 | s32 = (a32 + b32 + ((s16 & 0x10000) ? 1 : 0)) >>> 0;
115 |
116 | a48 = (a._hi & 0xffff0000) >>> 16;
117 | b48 = (b._hi & 0xffff0000) >>> 16;
118 | s48 = (a48 + b48 + ((s32 & 0x10000) ? 1 : 0)) >>> 0;
119 |
120 | return (new uint64_t(((s48 & 0xffff) << 16) | (s32 & 0xffff),
121 | ((s16 & 0xffff) << 16) | (s0 & 0xffff)));
122 | }
123 | uint64_t.addq = addq;
124 |
125 | function
126 | subq(a, b)
127 | {
128 | var d0, d16, d32, d48;
129 | var a0, a16, a32, a48;
130 | var b0, b16, b32, b48;
131 |
132 | a = typecheck(a);
133 | b = typecheck(b);
134 |
135 | a0 = (a._lo & 0xffff) >>> 0;
136 | b0 = (b._lo & 0xffff) >>> 0;
137 | a16 = (a._lo & 0xffff0000) >>> 16;
138 | b16 = (b._lo & 0xffff0000) >>> 16;
139 | a32 = (a._hi & 0xffff) >>> 0;
140 | b32 = (b._hi & 0xffff) >>> 0;
141 | a48 = (a._hi & 0xffff0000) >>> 16;
142 | b48 = (b._hi & 0xffff0000) >>> 16;
143 |
144 | if (b0 > a0) {
145 | a0 += 0x10000;
146 | if (a16 === 0) {
147 | a16 = 0xffff >>> 0;
148 | if (a32 === 0) {
149 | a32 = 0xffff >>> 0;
150 | --a48;
151 | } else {
152 | --a32;
153 | }
154 | } else {
155 | --a16;
156 | }
157 | }
158 | d0 = (a0 - b0) >>> 0;
159 |
160 | if (b16 > a16) {
161 | a16 += 0x10000;
162 | if (a32 === 0) {
163 | a32 = 0xffff >>> 0;
164 | --a48;
165 | } else {
166 | --a32;
167 | }
168 | }
169 | d16 = (a16 - b16) >>> 0;
170 |
171 | if (b32 > a32) {
172 | a32 += 0x10000;
173 | --a48;
174 | }
175 | d32 = (a32 - b32) >>> 0;
176 |
177 | d48 = (a48 - b48) >>> 0;
178 |
179 | return (new uint64_t(((d48 & 0xffff) << 16) | (d32 & 0xffff),
180 | ((d16 & 0xffff) << 16) | (d0 & 0xffff)));
181 | }
182 | uint64_t.subq = subq;
183 |
184 | function
185 | shlq(a, c)
186 | {
187 | a = typecheck(a);
188 | if (typeof (c) !== 'number' || c % 1 !== 0)
189 | throw new TypeError('count argument must be an integer');
190 | if (c < 0)
191 | throw new RangeError('count argument must be positive');
192 |
193 | if (c >= 64)
194 | return (new uint64_t(0, 0));
195 |
196 | if (c >= 32)
197 | return (new uint64_t((a._lo << (c - 32)) & 0xffffffff, 0));
198 |
199 | if (c === 0)
200 | return (new uint64_t(a._hi, a._lo));
201 |
202 | return (new uint64_t(((a._hi << c) & 0xffffffff) | (a._lo >>> (32 - c)),
203 | (a._lo << c) & 0xffffffff));
204 | }
205 | uint64_t.shlq = shlq;
206 |
207 | function
208 | shrlq(a, c)
209 | {
210 | a = typecheck(a);
211 | if (typeof (c) !== 'number' || c % 1 !== 0)
212 | throw new TypeError('count argument must be an integer');
213 | if (c < 0)
214 | throw new RangeError('count argument must be positive');
215 |
216 | if (c >= 64)
217 | return (new uint64_t(0, 0));
218 |
219 | if (c >= 32)
220 | return (new uint64_t(0, a._hi >>> (c - 32)));
221 |
222 | if (c === 0)
223 | return (new uint64_t(a._hi, a._lo));
224 |
225 | return (new uint64_t(a._hi >>> c,
226 | (a._lo >>> c) | ((a._hi << (32 - c)) & 0xffffffff)));
227 | }
228 | uint64_t.shrlq = shrlq;
229 |
230 | function
231 | mulq(a, b)
232 | {
233 | var p0, p16, p32, p48;
234 | var a0, a16, a32, a48;
235 | var b0, b16, b32, b48;
236 | var s;
237 |
238 | a = typecheck(a);
239 | b = typecheck(b);
240 |
241 | a0 = (a._lo & 0xffff) >>> 0;
242 | b0 = (b._lo & 0xffff) >>> 0;
243 | a16 = (a._lo & 0xffff0000) >>> 16;
244 | b16 = (b._lo & 0xffff0000) >>> 16;
245 | a32 = (a._hi & 0xffff) >>> 0;
246 | b32 = (b._hi & 0xffff) >>> 0;
247 | a48 = (a._hi & 0xffff0000) >>> 16;
248 | b48 = (b._hi & 0xffff0000) >>> 16;
249 |
250 | p0 = new uint64_t(0, a0 * b0);
251 | p16 = addq(new uint64_t(0, a0 * b16), new uint64_t(0, a16 * b0));
252 | p32 = addq(new uint64_t(0, a32 * b0), new uint64_t(0, a16 * b16));
253 | p32 = addq(p32, new uint64_t(0, a0 * b32));
254 | p48 = addq(new uint64_t(0, a48 * b0), new uint64_t(0, a32 * b16));
255 | p48 = addq(p48, new uint64_t(0, a16 * b32));
256 | p48 = addq(p48, new uint64_t(0, a0 * b48));
257 |
258 | s = addq(p0, shlq(p16, 16));
259 | s = addq(s, shlq(p32, 32));
260 | s = addq(s, shlq(p48, 48));
261 |
262 | return (s);
263 | }
264 | uint64_t.mulq = mulq;
265 |
266 | function
267 | tstq(a)
268 | {
269 | a = typecheck(a);
270 |
271 | return ((a._hi !== 0) || (a._lo !== 0));
272 | }
273 | uint64_t.tstq = tstq;
274 |
275 | function
276 | cmpq(a, b)
277 | {
278 | a = typecheck(a);
279 | b = typecheck(b);
280 |
281 | if (a._hi > b._hi)
282 | return (1);
283 | if (a._hi < b._hi)
284 | return (-1);
285 | if (a._lo > b._lo)
286 | return (1);
287 | if (a._lo < b._lo)
288 | return (-1);
289 |
290 | return (0);
291 | }
292 | uint64_t.cmpq = cmpq;
293 |
294 | uint64_t.prototype.toString_internal = function uint64_t_toString_internal() {
295 | return (fmt('[%d,%d]', this._hi, this._lo));
296 | };
297 |
298 | uint64_t.prototype.toString = function uint64_t_toString() {
299 | var s = '';
300 | var n = this, q, r;
301 | var v;
302 |
303 | if (!tstq(n))
304 | return ('0');
305 |
306 | while (tstq(n)) {
307 | q = addq(shrlq(n, 1), shrlq(n, 2));
308 | q = addq(q, shrlq(q, 4));
309 | q = addq(q, shrlq(q, 8));
310 | q = addq(q, shrlq(q, 16));
311 | q = addq(q, shrlq(q, 32));
312 | q = shrlq(q, 3);
313 |
314 | r = subq(n, mulq(q, TEN));
315 | v = (r._lo + 6) >>> 4;
316 | q = addq(q, new uint64_t(0, v));
317 | r._lo -= v * 10;
318 | n = q;
319 |
320 | s = r._lo.toString().concat(s);
321 | }
322 |
323 | return (s);
324 | };
325 |
326 | uint64_t.prototype.toOctets = function uint64_t_toOctets() {
327 | var a = new Array();
328 | var v = {
329 | hi: this._hi,
330 | lo: this._lo
331 | };
332 | var len;
333 |
334 | if (v.hi === 0 && v.lo === 0) {
335 | a.push(0);
336 | return (a);
337 | }
338 |
339 | len = 4;
340 | while (v.lo !== 0 || v.hi !== 0 && len > 0) {
341 | a.unshift(v.lo & 0xff);
342 | /* JSSTYLED */
343 | v.lo >>>= 8;
344 | --len;
345 | }
346 |
347 | while (v.hi !== 0) {
348 | a.unshift(v.hi & 0xff);
349 | /* JSSTYLED */
350 | v.hi >>>= 8;
351 | }
352 |
353 | return (a);
354 | };
355 |
356 | module.exports = uint64_t;
357 |
--------------------------------------------------------------------------------
/lib/protocol/varbind.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012 Joyent, Inc. All rights reserved.
3 | */
4 |
5 | var ASN1 = require('asn1').Ber;
6 | var data = require('./data');
7 |
8 | function
9 | SnmpVarbind(arg)
10 | {
11 | var self = this;
12 |
13 | if (!arg)
14 | arg = {};
15 | if (typeof (arg) !== 'object')
16 | throw new TypeError('arg must be an object');
17 |
18 | this._oid = undefined;
19 | this._data = undefined;
20 |
21 | this.__defineGetter__('oid', function () {
22 | if (self._oid)
23 | return (self._oid.value);
24 | return (undefined);
25 | });
26 | this.__defineSetter__('oid', function (v) {
27 | if (typeof (v) === 'string')
28 | v = data.createData({ value: v,
29 | type: 'ObjectIdentifier' });
30 |
31 | if (typeof (v) !== 'object' || !data.isSnmpData(v) ||
32 | v.typename != 'ObjectIdentifier') {
33 | throw new TypeError('oid must be a string or ' +
34 | 'SNMP data object of type ObjectIdentifier');
35 | }
36 |
37 | self._oid = v;
38 | });
39 | this.__defineGetter__('data', function () {
40 | return (self._data);
41 | });
42 | this.__defineSetter__('data', function (v) {
43 | if (typeof (v) === 'object' && data.isSnmpData(v))
44 | self._data = v;
45 | else if (typeof (v) === 'object' && (v instanceof ASN1.Reader))
46 | self._data = data.createData({ value: v });
47 | else if (typeof (v) === 'object')
48 | self._data = data.createData(v);
49 | });
50 |
51 | if (typeof (arg.oid) !== 'undefined')
52 | this.oid = arg.oid;
53 | if (typeof (arg.data) !== 'undefined')
54 | this.data = arg.data;
55 | }
56 |
57 | SnmpVarbind.prototype.__snmpjs_magic = 'SnmpVarbind';
58 |
59 | SnmpVarbind.prototype.clone = function clone() {
60 | var oclone, dclone;
61 |
62 | if (this._oid)
63 | oclone = this._oid.clone();
64 | if (this._data)
65 | dclone = this._data.clone();
66 |
67 | return (new this.constructor({ oid: oclone, data: dclone }));
68 | };
69 |
70 | SnmpVarbind.prototype.encode = function encode(writer) {
71 | if (typeof (this._oid) === 'undefined')
72 | throw new ReferenceError('Cannot encode undefined oid');
73 | if (typeof (this._data) == 'undefined')
74 | throw new ReferenceError('Cannot encode undefined data');
75 |
76 | writer.startSequence();
77 | this._oid.encode(writer);
78 | this._data.encode(writer);
79 | writer.endSequence();
80 | };
81 |
82 | function
83 | createVarbind(arg)
84 | {
85 | return (new SnmpVarbind(arg));
86 | }
87 |
88 | module.exports = {
89 | SnmpVarbind: SnmpVarbind,
90 | createVarbind: createVarbind,
91 | isSnmpVarbind: function (v) {
92 | return ((typeof (v.__snmpjs_magic) === 'string' &&
93 | v.__snmpjs_magic === 'SnmpVarbind') ? true : false);
94 | }
95 | };
96 |
--------------------------------------------------------------------------------
/lib/provider.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2012 Joyent, Inc. All rights reserved.
3 | */
4 |
5 | var util = require('util');
6 | var PDU = require('./protocol/pdu');
7 | var varbind = require('./protocol/varbind');
8 | var data = require('./protocol/data');
9 | var MIB = require('./mib');
10 |
11 | function
12 | ProviderRequest(op, addr, node, iterate)
13 | {
14 | var self = this;
15 |
16 | this._op = op;
17 | addr = this._addr = data.canonicalizeOID(addr);
18 | this._oid = this._addr.join('.');
19 | this._done = function () {
20 | throw new Error('BUG in snmpjs! No completion callback set.');
21 | };
22 | this._iterate = iterate || 1;
23 |
24 | this.__defineGetter__('op', function () { return (self._op); });
25 | this.__defineGetter__('addr', function () { return (self._addr); });
26 | this.__defineGetter__('oid', function () { return (self._oid); });
27 | this.__defineGetter__('node', function () { return (self._node); });
28 | this.__defineGetter__('instance',
29 | function () { return (self._instance); });
30 | this.__defineGetter__('iterate',
31 | function () { return (self._iterate); });
32 | this.__defineGetter__('done', function () { return (self._done); });
33 | this.__defineGetter__('value', function () { return (self._value); });
34 |
35 | this._node = node;
36 |
37 | if (node && node.isAncestor(addr))
38 | this._instance = addr.slice(node.addr.length, addr.length);
39 | }
40 |
41 | function
42 | _createVarbind(oid, rsd)
43 | {
44 | var vb;
45 |
46 | if (typeof (rsd) === 'number')
47 | vb = rsd;
48 | else if (typeof (rsd) === 'undefined')
49 | vb = undefined;
50 | else if (typeof (rsd) === 'object' && data.isSnmpData(rsd))
51 | vb = varbind.createVarbind({ oid: oid, data: rsd });
52 | else
53 | throw new TypeError('Response is of incompatible type');
54 |
55 | return (vb);
56 | }
57 |
58 | function
59 | readOnlyScalar(prq, rsd)
60 | {
61 | var oid = prq.node.oid + '.0';
62 |
63 | if (prq.op == PDU.SetRequest) {
64 | prq.done(_createVarbind(PDU.notWritable));
65 | return;
66 | }
67 |
68 | prq.done(_createVarbind(oid, rsd));
69 | }
70 |
71 | function
72 | writableScalar(prq, rsd)
73 | {
74 | var oid = prq.node.oid + '.0';
75 |
76 | prq.done(_createVarbind(oid, rsd));
77 | }
78 |
79 | module.exports = {
80 | ProviderRequest: ProviderRequest,
81 | readOnlyScalar: readOnlyScalar,
82 | writableScalar: writableScalar
83 | };
84 |
--------------------------------------------------------------------------------
/lib/receiver.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2015 Jan Van Buggenhout. All rights reserved.
3 | * Copyright (c) 2013 Joyent, Inc. All rights reserved.
4 | */
5 |
6 | var dgram = require('dgram');
7 | var util = require('util');
8 | var events = require('events');
9 | var PDU = require('./protocol/pdu');
10 | var message = require('./protocol/message');
11 |
12 | function
13 | Receiver(options)
14 | {
15 | if (typeof (options) !== 'object')
16 | throw new TypeError('options (object) is required');
17 | if (typeof (options.log) !== 'object')
18 | throw new TypeError('options.log (object) is required');
19 |
20 | this._log = options.log;
21 | this._name = options.name || 'snmpjs';
22 |
23 | this._malformed_messages = 0;
24 | }
25 | util.inherits(Receiver, events.EventEmitter);
26 |
27 | Receiver.prototype._process_msg = function _process_msg(msg) {
28 | this._log.debug({
29 | raw: msg.raw,
30 | origin: msg.src,
31 | snmpmsg: msg
32 | }, 'Ignoring PDU of inappropriate type ' +
33 | PDU.strop(msg.pdu.op));
34 | };
35 |
36 | Receiver.prototype._augment_msg = function _augment_msg(msg, conn) {
37 | };
38 |
39 | Receiver.prototype._recv = function _recv(raw, src, conn) {
40 | var msg;
41 |
42 | try {
43 | msg = message.parseMessage({ raw: raw, src: src });
44 | } catch (err) {
45 | this._malformed_messages++;
46 | this._log.debug({
47 | err: err,
48 | raw: raw,
49 | origin: src }, 'Invalid SNMP message');
50 | return;
51 | }
52 |
53 | this._augment_msg(msg, conn);
54 | this._log.trace({ raw: raw, origin: src, snmpmsg: msg },
55 | 'Received SNMP message');
56 | this._process_msg(msg);
57 | };
58 |
59 | Receiver.prototype.createSocket = function createSocket(family) {
60 | var self = this;
61 | var conn;
62 |
63 | if (typeof (family) !== 'string')
64 | throw new TypeError('family (string) is required');
65 |
66 | conn = dgram.createSocket(family);
67 | conn.on('message', function _recv_binder(msg, rinfo) {
68 | var raw = {
69 | buf: msg,
70 | len: rinfo.size
71 | };
72 | var src = {
73 | family: family,
74 | address: rinfo.address,
75 | port: rinfo.port
76 | };
77 | self._recv(raw, src, conn);
78 | });
79 |
80 | return (conn);
81 | };
82 |
83 | module.exports = Receiver;
84 |
--------------------------------------------------------------------------------
/lib/snmp.jison:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2012 Joyent, Inc. All rights reserved.
3 | */
4 |
5 | /*
6 | * The SNMP base message format is almost completely undefined by the relevant
7 | * RFCs (1157, most notably, which uses the verboten "ANY"). We do, however,
8 | * know the following:
9 | *
10 | * 1. A message is a CONSTRUCTED SEQUENCE. If that's not the first thing we
11 | * see, give up hope. 2. The first element in the sequence is an integer that
12 | * identifies the SNMP version. The following values are defined:
13 | *
14 | * 0 - SNMPv1
15 | * 1 - SNMPv2c
16 | * 3 - SNMPv3
17 | *
18 | * After this, it gets spotty. SNMPv1 (RFC 1157) and SNMPv2c (RFC 1901)
19 | * specify that the community string follows, then they diverge: SNMPv1 allows
20 | * anything to follow, while SNMPv2c requires that a PDU follow. The intent in
21 | * v1 appears to have been for authentication data to precede PDU(s), but this
22 | * was never specified in v1 and it seems that a PDU always follows there as
23 | * well. In any case, this is all we will support for now. SNMPv3 (RFC 3412)
24 | * specifies that the version field is followed by another header that
25 | * describes, among other things, which security model is in use, followed by
26 | * security parameters, followed once again by anything (which is
27 | * "e.g., PDUs..."; thanks for the example, jackass, now how about a
28 | * specification?). See also RFC 3416.
29 | *
30 | * What we're actually willing to support: v1 and v2c headers followed by a
31 | * PDU; i.e.,
32 | *
33 | * struct message {
34 | * int version_minus_one;
35 | * string community;
36 | * PDU pdu;
37 | * }
38 | *
39 | * The standards provide for a very limited subset of types allowed in varbinds
40 | * within PDUs; namely, the three types specified by simple_syntax, and a
41 | * handful of others (all with the ASN.1 Application bit set) that are
42 | * enumerated explicitly. However, it does not seem at all implausible that
43 | * various MIBs out there may incorporate values of other data types. Therefore
44 | * we can parse any data type in a varbind that (a) has a data handler
45 | * registered for it, and (b) does not conflict with one of the non-Application
46 | * constructed types used by SNMP itself: SEQUENCE, CONTEXT_CONSTRUCTED_[0-9].
47 | * We do not support any of the latter because the lexer would have to support
48 | * both treating these as zero-length tokens (where we wish to parse the
49 | * contents separately, as in this parser) and as large objects to be bulk
50 | * decoded without interpretation by the parser (where used to instantiate data
51 | * objects within varbinds). For simplicity, this is not permitted and any
52 | * message containing such a varbind will be unparseable. There is only so much
53 | * we are willing to do to accommodate blatant standards violations.
54 | *
55 | * We can parse v3 messages but don't support them; i.e., we don't attempt to
56 | * construct and return protocol objects for them.
57 | *
58 | * Critical conventions to understand:
59 | *
60 | * - yytext is a Buffer. It it not a String, because it must be able to contain
61 | * unprintable characters including NUL.
62 | *
63 | * - The value of any scalar data object is a descendant of SnmpData. It is a
64 | * bug for this parser to interpret the value of any such object. Instead, all
65 | * objects with SNMP/ASN.1 data types are constructed into SnmpData objects and
66 | * passed along as part of the parsed message.
67 | *
68 | * - The value of a list is always an array, and the members (if any) are always
69 | * of the same type as the value of the type the list contains.
70 | */
71 |
72 | %token 'SEQUENCE'
73 | %token 'INTEGER'
74 | %token 'IP_ADDRESS'
75 | %token 'TIME_TICKS'
76 | %token 'NULL'
77 | %token 'OBJECT_IDENTIFIER'
78 | %token 'OCTET_STRING'
79 | %token 'CONTEXT_CONSTRUCTED_0'
80 | %token 'CONTEXT_CONSTRUCTED_1'
81 | %token 'CONTEXT_CONSTRUCTED_2'
82 | %token 'CONTEXT_CONSTRUCTED_3'
83 | %token 'CONTEXT_CONSTRUCTED_4'
84 | %token 'CONTEXT_CONSTRUCTED_5'
85 | %token 'CONTEXT_CONSTRUCTED_6'
86 | %token 'CONTEXT_CONSTRUCTED_7'
87 | %token 'CONTEXT_CONSTRUCTED_8'
88 |
89 | %start message
90 |
91 | %%
92 |
93 | message
94 | : 'SEQUENCE' integer content {{
95 | var msg = yy.message.createMessage({ version: $2,
96 | community: $3.community, pdu: $3.pdu });
97 | yy.setContent(msg);
98 | }}
99 | ;
100 |
101 | content
102 | : string pdu {{
103 | $$ = {
104 | community: $1,
105 | pdu: $2
106 | };
107 | }}
108 | | v3_header v3_sec v3_pdu {{
109 | throw new RangeError('SNMPv3 is not supported yet');
110 | }}
111 | ;
112 |
113 | v3_header
114 | : 'SEQUENCE' integer integer string integer
115 | ;
116 |
117 | v3_sec
118 | : string
119 | ;
120 |
121 | v3_pdu
122 | : scoped_pdu
123 | | string
124 | ;
125 |
126 | scoped_pdu
127 | : 'SEQUENCE' string string pdu
128 | ;
129 |
130 | pdu
131 | : std_pdu_tag integer integer integer varbind_list {{
132 | $$ = yy.pdu.createPDU({ op: $1, request_id: $2,
133 | varbinds: $5 });
134 | $$.error_status = $3;
135 | $$.error_index = $4;
136 | }}
137 | | obsolete_trap_pdu_tag oid ip_address integer integer time_ticks
138 | varbind_list_v1 {{
139 | $$ = yy.pdu.createPDU({ op: $1, varbinds: $7 });
140 | $$.enterprise = $2;
141 | $$.agent_addr = $3;
142 | $$.generic_trap = $4;
143 | $$.specific_trap = $5;
144 | $$.time_stamp = $6;
145 | }}
146 | ;
147 |
148 | std_pdu_tag
149 | : 'CONTEXT_CONSTRUCTED_0' {{ $$ = yy.pdu.GetRequest; }}
150 | | 'CONTEXT_CONSTRUCTED_1' {{ $$ = yy.pdu.GetNextRequest; }}
151 | | 'CONTEXT_CONSTRUCTED_2' {{ $$ = yy.pdu.Response; }}
152 | | 'CONTEXT_CONSTRUCTED_3' {{ $$ = yy.pdu.SetRequest; }}
153 | | 'CONTEXT_CONSTRUCTED_5' {{ $$ = yy.pdu.GetBulkRequest; }}
154 | | 'CONTEXT_CONSTRUCTED_6' {{ $$ = yy.pdu.InformRequest; }}
155 | | 'CONTEXT_CONSTRUCTED_7' {{ $$ = yy.pdu.SNMPv2_Trap; }}
156 | | 'CONTEXT_CONSTRUCTED_8' {{ $$ = yy.pdu.Report; }}
157 | ;
158 |
159 | obsolete_trap_pdu_tag
160 | : 'CONTEXT_CONSTRUCTED_4' {{ $$ = yy.pdu.Trap; }}
161 | ;
162 |
163 | varbind_list_v1
164 | : 'SEQUENCE' varbinds {{
165 | $$ = $2;
166 | }}
167 | | 'SEQUENCE'
168 | |
169 | ;
170 |
171 | varbind_list
172 | : 'SEQUENCE' varbinds {{
173 | $$ = $2;
174 | }}
175 | |
176 | ;
177 |
178 | varbinds
179 | : varbinds varbind {{
180 | $$ = $1;
181 | $$.push($2);
182 | }}
183 | | varbind {{
184 | $$ = [ $1 ];
185 | }}
186 | ;
187 |
188 | varbind
189 | : 'SEQUENCE' oid value {{
190 | $$ = yy.varbind.createVarbind({ oid: $2, data: $3 });
191 | }}
192 | ;
193 |
194 | value
195 | : object_syntax
196 | | null
197 | ;
198 |
199 | object_syntax
200 | : simple_syntax
201 | | application_syntax
202 | ;
203 |
204 | simple_syntax
205 | : integer
206 | | string
207 | | oid
208 | ;
209 |
210 | application_syntax
211 | : ip_address
212 | | time_ticks
213 | | data
214 | ;
215 |
216 | integer
217 | : 'INTEGER' {{
218 | var reader = new yy.ASN1.Reader(yytext);
219 | $$ = yy.data.createData({ value: reader, type: 'Integer' });
220 | }}
221 | ;
222 |
223 | string
224 | : 'OCTET_STRING' {{
225 | var reader = new yy.ASN1.Reader(yytext);
226 | $$ = yy.data.createData({ value: reader,
227 | type: 'OctetString' });
228 | }}
229 | ;
230 |
231 | oid
232 | : 'OBJECT_IDENTIFIER' {{
233 | var reader = new yy.ASN1.Reader(yytext);
234 | $$ = yy.data.createData({ value: reader,
235 | type: 'ObjectIdentifier'});
236 | }}
237 | ;
238 |
239 | ip_address
240 | : 'IP_ADDRESS' {{
241 | var reader = new yy.ASN1.Reader(yytext);
242 | $$ = yy.data.createData({ value: reader,
243 | type: 'IpAddress' });
244 | }}
245 | ;
246 |
247 | time_ticks
248 | : 'TIME_TICKS' {{
249 | var reader = new yy.ASN1.Reader(yytext);
250 | $$ = yy.data.createData({ value: reader,
251 | type: 'TimeTicks' });
252 | }}
253 | ;
254 |
255 | null
256 | : 'NULL' {{
257 | var reader = new yy.ASN1.Reader(yytext);
258 | $$ = yy.data.createData({ value: reader, type: 'Null' });
259 | }}
260 | ;
261 |
262 | data
263 | : 'DATA' {{
264 | var reader = new yy.ASN1.Reader(yytext);
265 | $$ = yy.data.createData({ value: reader });
266 | }}
267 | ;
268 |
--------------------------------------------------------------------------------
/lib/trap_listener.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 Joyent, Inc. All rights reserved.
3 | */
4 |
5 | var util = require('util');
6 | var Listener = require('./listener');
7 | var PDU = require('./protocol/pdu');
8 |
9 | function
10 | TrapListener(options)
11 | {
12 | Listener.call(this, options);
13 | }
14 | util.inherits(TrapListener, Listener);
15 |
16 | TrapListener.prototype._process_msg = function _process_msg(msg) {
17 | switch (msg.pdu.op) {
18 | case PDU.Trap:
19 | case PDU.InformRequest:
20 | case PDU.SNMPv2_Trap:
21 | this.emit('trap', msg);
22 | break;
23 | case PDU.GetRequest:
24 | case PDU.SetRequest:
25 | case PDU.GetNextRequest:
26 | case PDU.GetBulkRequest:
27 | case PDU.Response:
28 | case PDU.Report:
29 | default:
30 | Listener.prototype._process_msg.call(this, msg);
31 | break;
32 | }
33 | };
34 |
35 | module.exports = TrapListener;
36 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "author": "Keith M Wesolowski ",
3 | "name": "snmpjs",
4 | "description": "Simple Network Management Protocol toolkit",
5 | "version": "0.1.8",
6 | "repository": {
7 | "type": "git",
8 | "url": "git://github.com/joyent/node-snmpjs.git"
9 | },
10 | "bugs": {
11 | "url" : "http://github.com/joyent/node-snmpjs/issues"
12 | },
13 | "main": "lib/index.js",
14 | "engines": {
15 | "node": ">=0.6.9"
16 | },
17 | "dependencies": {
18 | "jison": "0.3",
19 | "asn1": "~0.2.2",
20 | "bunyan": "~0.21",
21 | "dtrace-provider": "~0.4"
22 | },
23 | "devDependencies": {
24 | "tap": "~0.4"
25 | },
26 | "scripts": {
27 | "install": "jison -o lib/parser.js lib/snmp.jison",
28 | "update": "jison -o lib/parser.js lib/snmp.jison",
29 | "pretest": "which gjslint; if [[ \"$?\" = 0 ]] ; then gjslint --nojsdoc -r lib -r tst; else echo \"Missing gjslint. Skipping lint\"; fi",
30 | "test": "./node_modules/.bin/tap ./test",
31 | "start": "node agent.js"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/smf/manifests/snmpd.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | Joyent SNMP Agent
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/snmpbulkget.js:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env node
2 | /*
3 | * Copyright (c) 2015 Jan Van Buggenhout. All rights reserved.
4 | */
5 |
6 | var snmp = require('./lib/index.js');
7 | var bunyan = require('bunyan');
8 | var util = require('util');
9 |
10 | var client = snmp.createClient({
11 | log: new bunyan({ name: 'snmpget', level: 'info' })
12 | });
13 |
14 | function print_get_response(snmpmsg)
15 | {
16 | snmpmsg.pdu.varbinds.forEach(function (varbind) {
17 | console.log(varbind.oid + ' = ' + varbind.data.value);
18 | });
19 | }
20 |
21 | var ip = '127.0.0.1'; // process.argv[2];
22 | var community = 'public'; // process.argv[3];
23 | var non_repeaters = [ '1.3.6.1.2.1.1' ];
24 | var repeaters = [ '1.3.6.1.2.1.1.9.1.2', '1.3.6.1.2.1.1.9.1.3' ];
25 |
26 | client.getBulk(ip, community, non_repeaters, repeaters, 5, function (snmpmsg) {
27 | print_get_response(snmpmsg);
28 | client.unref();
29 | });
30 |
--------------------------------------------------------------------------------
/snmpget.js:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env node
2 | /*
3 | * Copyright (c) 2015 Jan Van Buggenhout. All rights reserved.
4 | */
5 |
6 | var snmp = require('./lib/index.js');
7 | var bunyan = require('bunyan');
8 | var util = require('util');
9 |
10 | var client = snmp.createClient({
11 | log: new bunyan({ name: 'snmpget', level: 'info' })
12 | });
13 |
14 | function print_get_response(snmpmsg)
15 | {
16 | snmpmsg.pdu.varbinds.forEach(function (varbind) {
17 | console.log(varbind.oid + ' = ' + varbind.data.value);
18 | });
19 | }
20 |
21 | var ip = process.argv[2];
22 | var community = process.argv[3];
23 | var oid = process.argv[4];
24 |
25 | client.get(ip, community, 0, oid, function (snmpmsg) {
26 | print_get_response(snmpmsg);
27 | client.unref();
28 | });
29 |
--------------------------------------------------------------------------------
/snmpinform.js:
--------------------------------------------------------------------------------
1 | snmptrap.js
--------------------------------------------------------------------------------
/snmpset.js:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env node
2 | /*
3 | * Copyright (c) 2015 Jan Van Buggenhout. All rights reserved.
4 | */
5 |
6 | var snmp = require('./lib/index.js');
7 | var bunyan = require('bunyan');
8 | var util = require('util');
9 |
10 | var client = snmp.createClient({
11 | log: new bunyan({ name: 'snmpset', level: 'info' })
12 | });
13 |
14 | var ip = process.argv[2];
15 | var community = process.argv[3];
16 | var oid = process.argv[4];
17 | var value = process.argv[5];
18 |
19 | client.set(ip, community, 0, oid, snmp.data.createData({ type: 'Integer',
20 | value: parseInt(value, 10) }), function (snmpmsg) {
21 | // console.log(snmp.pdu.strerror(snmpmsg.pdu.error_status));
22 | process.exitCode = snmpmsg.pdu.error_status;
23 | client.unref();
24 | });
25 |
--------------------------------------------------------------------------------
/snmptrap.js:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env node
2 | /*
3 | * Copyright (c) 2015 Jan Van Buggenhout. All rights reserved.
4 | */
5 |
6 | var snmp = require('./lib/index.js');
7 | var bunyan = require('bunyan');
8 | var util = require('util');
9 | var path = require('path');
10 |
11 | var callback;
12 | if ('snmpinform' == path.basename(process.argv[1]).split('.')[0])
13 | callback = function (snmpmsg) {
14 | console.log(util.inspect(snmp.message.serializer(snmpmsg),
15 | false, null, true));
16 | process.exitCode = snmpmsg.pdu.error_status;
17 | client.unref();
18 | };
19 |
20 | var client = snmp.createClient({
21 | log: new bunyan({
22 | name: typeof (callback) === 'function' ? 'snmpinform'
23 | : 'snmptrap',
24 | level: 'info'
25 | })
26 | });
27 |
28 | var ip = process.argv[2];
29 | var community = process.argv[3];
30 | // coldStart
31 | var oid = '1.3.6.1.6.3.1.1.5.1'; // process.argv[4];
32 | // var value = process.argv[5];
33 |
34 | client.inform(ip, community, 0, oid, [
35 | snmp.varbind.createVarbind({
36 | // sysDescr.0
37 | oid: '1.3.6.1.2.1.1.1.0',
38 | data: snmp.data.createData({
39 | type: 'OctetString',
40 | value: 'TEST'
41 | })
42 | })
43 | ], callback);
44 |
--------------------------------------------------------------------------------
/snmpwalk.js:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env node
2 | /*
3 | * Copyright (c) 2015 Jan Van Buggenhout. All rights reserved.
4 | */
5 |
6 | var snmp = require('./lib/index.js');
7 | var bunyan = require('bunyan');
8 | var util = require('util');
9 |
10 | var client = snmp.createClient({
11 | log: new bunyan({ name: 'snmpwalk', level: 'info' })
12 | });
13 |
14 | function print_get_response(snmpmsg)
15 | {
16 | snmpmsg.pdu.varbinds.forEach(function (varbind) {
17 | console.log(varbind.oid + ' = ' + varbind.data.value);
18 | });
19 | }
20 |
21 | /* jsl:ignore */
22 | function snmpwalk(ip, community, version, oid, cb, donecb)
23 | {
24 | /* jsl:end */
25 | function walk(snmpmsg) {
26 | if (snmpmsg.pdu.error_status == snmp.pdu.noSuchName) {
27 | if (donecb)
28 | donecb();
29 | return;
30 | }
31 | cb(snmpmsg);
32 | client.getNext(ip, community, version,
33 | snmpmsg.pdu.varbinds[0].oid, walk);
34 | }
35 |
36 | client.getNext(ip, community, version, oid, walk);
37 | }
38 |
39 | var ip = process.argv[2];
40 | var community = process.argv[3];
41 | var oid = process.argv[4];
42 |
43 | snmpwalk(ip, community, 0, oid, print_get_response, function () {
44 | client.unref();
45 | });
46 |
--------------------------------------------------------------------------------
/test/protocol/uint64_t.test.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2014 Joyent, Inc. All rights reserved.
3 | */
4 |
5 | var test = require('tap').test;
6 | var uint64_t;
7 |
8 | test('load library', function (t) {
9 | uint64_t = require('../../lib/protocol/uint64_t');
10 | t.ok(uint64_t, 'module require should work');
11 |
12 | t.end();
13 | });
14 |
15 | test('creation', function (t) {
16 | var a = new uint64_t(0, 0);
17 | t.deepEqual(a, { _hi: 0, _lo: 0 }, 'explicit double zero');
18 |
19 | a = new uint64_t(0);
20 | t.deepEqual(a, { _hi: 0, _lo: 0 }, 'explicit single zero');
21 |
22 | a = new uint64_t(42);
23 | t.deepEqual(a, { _hi: 0, _lo: 42 }, 'single small integer');
24 |
25 | a = new uint64_t('0');
26 | t.deepEqual(a, { _hi: 0, _lo: 0 }, 'string 0');
27 |
28 | a = new uint64_t('42');
29 | t.deepEqual(a, { _hi: 0, _lo: 42 }, 'string small decimal');
30 |
31 | a = new uint64_t('042');
32 | t.deepEqual(a, { _hi: 0, _lo: 042 }, 'string small octal');
33 |
34 | a = new uint64_t('0x42');
35 | t.deepEqual(a, { _hi: 0, _lo: 0x42 }, 'string small hex');
36 |
37 | a = new uint64_t('0x1fff00ff');
38 | t.deepEqual(a, { _hi: 0, _lo: 0x1fff00ff }, 'string medium hex');
39 |
40 | a = new uint64_t('0x1ffff0000');
41 | t.deepEqual(a, { _hi: 1, _lo: 0xffff0000 }, 'string 33 hex');
42 |
43 | a = new uint64_t('0xffff1234ebc068ac');
44 | t.deepEqual(a, { _hi: 0xffff1234, _lo: 0xebc068ac },
45 | 'string large hex');
46 |
47 | a = new uint64_t('18446744073709551615');
48 | t.deepEqual(a, { _hi: 0xffffffff, _lo: 0xffffffff },
49 | 'string max decimal');
50 |
51 | a = new uint64_t(0x12345678, 0x9abcdef0);
52 | t.deepEqual(a, { _hi: 0x12345678, _lo: 0x9abcdef0 },
53 | 'explicit hex 61 pair');
54 |
55 | t.throws(function () {
56 | a = new uint64_t('0x123456789abcdef01');
57 | }, new RangeError('hex value too large'));
58 |
59 | t.throws(function () {
60 | a = new uint64_t({ foo: 'bar' });
61 | }, new TypeError('hi and lo components must be integers'));
62 |
63 | t.throws(function () {
64 | a = new uint64_t(4.6, 299.000001);
65 | }, new TypeError('hi and lo components must be integers'));
66 |
67 | t.throws(function () {
68 | a = new uint64_t(-2944.12);
69 | }, new TypeError('hi and lo components must be integers'));
70 |
71 | t.end();
72 | });
73 |
74 | test('addition', function (t) {
75 | var a = new uint64_t(0, 0x456);
76 | var b = new uint64_t(0, 0x125);
77 | var s = uint64_t.addq(a, b);
78 | t.deepEqual(s, { _hi: 0, _lo: 0x57b }, '456 + 125 = 57b');
79 | s = uint64_t.addq(b, a);
80 | t.deepEqual(s, { _hi: 0, _lo: 0x57b }, '125 + 456 = 57b');
81 |
82 | a = new uint64_t(0);
83 | b = new uint64_t(0x147, 0x2c0509);
84 | s = uint64_t.addq(a, b);
85 | t.deepEqual(s, { _hi: 0x147, _lo: 0x2c0509 },
86 | '0 + 147002c0509 = 147002c0509');
87 | s = uint64_t.addq(b, a);
88 | t.deepEqual(s, { _hi: 0x147, _lo: 0x2c0509 },
89 | '147002c0509 + 0 = 147002c0509');
90 |
91 | a = new uint64_t(0x1bd1, 0x29c00410);
92 | b = new uint64_t(0, 0x29aa05c0);
93 | s = uint64_t.addq(a, b);
94 | t.deepEqual(s, { _hi: 0x1bd1, _lo: 0x536a09d0 },
95 | '1bd129c00410 + 29aa05c0 = 1bd1536a09d0');
96 | s = uint64_t.addq(b, a);
97 | t.deepEqual(s, { _hi: 0x1bd1, _lo: 0x536a09d0 },
98 | '29aa05c0 + 1bd129c00410 = 1bd1536a09d0');
99 |
100 | a = new uint64_t(0xfeedface, 0xdeadbeef);
101 | b = new uint64_t(0, 0xbeeff00d);
102 | s = uint64_t.addq(a, b);
103 | t.deepEqual(s, { _hi: 0xfeedfacf, _lo: 0x9d9daefc },
104 | 'feedfacedeadbeef + beeff00d = feedfacf9d9daefc');
105 | s = uint64_t.addq(b, a);
106 | t.deepEqual(s, { _hi: 0xfeedfacf, _lo: 0x9d9daefc },
107 | 'beeff00d + feedfacedeadbeef = feedfacf9d9daefc');
108 |
109 | a = new uint64_t(0xffffffff, 0xffffffff);
110 | b = new uint64_t(0, 1);
111 | s = uint64_t.addq(a, b);
112 | t.deepEqual(s, { _hi: 0, _lo: 0 },
113 | 'ffffffffffffffff + 1 = 0');
114 | s = uint64_t.addq(b, a);
115 | t.deepEqual(s, { _hi: 0, _lo: 0 },
116 | '1 + ffffffffffffffff = 0');
117 |
118 | t.end();
119 | });
120 |
121 | test('subtraction', function (t) {
122 | var a = new uint64_t(0, 0x456);
123 | var b = new uint64_t(0, 0x125);
124 | var d = uint64_t.subq(a, b);
125 | t.deepEqual(d, { _hi: 0, _lo: 0x331 }, '456 - 125 = 331');
126 |
127 | a = new uint64_t(0xc60a4221, 0xa8c8917b);
128 | b = new uint64_t(0);
129 | d = uint64_t.subq(a, b);
130 | t.deepEqual(d, { _hi: 0xc60a4221, _lo: 0xa8c8917b },
131 | 'c60a4221a8c8917b - 0 = c60a4221a8c8917b');
132 |
133 | a = new uint64_t(0x1bd1, 0x4444aaaa);
134 | b = new uint64_t(0x1000, 0x00000000);
135 | d = uint64_t.subq(a, b);
136 | t.deepEqual(d, { _hi: 0xbd1, _lo: 0x4444aaaa },
137 | '1bd14444aaaa - 100000000000 = bd14444aaaa');
138 |
139 | a = new uint64_t(0x80000000, 0);
140 | b = new uint64_t(0x7fffffff, 0xffffffff);
141 | d = uint64_t.subq(a, b);
142 | t.deepEqual(d, { _hi: 0, _lo: 1 },
143 | '8000000000000000 - 7fffffffffffffff = 1');
144 |
145 | a = new uint64_t(0x4001, 0x00001cc8);
146 | b = new uint64_t(0x2e, 0xfc002bfc);
147 | d = uint64_t.subq(a, b);
148 | t.deepEqual(d, { _hi: 0x3fd2, _lo: 0x3fff0cc },
149 | '400100001cc8 - 2efc002bfc = 3fd203fff0cc');
150 |
151 | a = new uint64_t(0);
152 | b = new uint64_t(1);
153 | d = uint64_t.subq(a, b);
154 | t.deepEqual(d, { _hi: 0xffffffff, _lo: 0xffffffff },
155 | '0 - 1 = ffffffffffffffff');
156 |
157 | a = new uint64_t(0xa000);
158 | b = new uint64_t(1, 0);
159 | d = uint64_t.subq(a, b);
160 | t.deepEqual(d, { _hi: 0xffffffff, _lo: 0xa000 },
161 | 'a000 - 100000000 = ffffffff0000a000');
162 |
163 | a = new uint64_t(0x3cf2, 0xfb002dee);
164 | b = new uint64_t(0x68ad0, 0x016400c2);
165 | d = uint64_t.subq(a, b);
166 | t.deepEqual(d, { _hi: 0xfff9b222, _lo: 0xf99c2d2c },
167 | '3cf2fb002dee - 68ad0016400c2 = fff9b222f99c2d2c');
168 |
169 | t.end();
170 | });
171 |
172 | test('multiplication', function (t) {
173 | var a = new uint64_t(0, 0x456);
174 | var b = new uint64_t(0, 0x125);
175 | var p = uint64_t.mulq(a, b);
176 | t.deepEqual(p, { _hi: 0, _lo: 0x4f66e }, '456 * 125 = 4f66e');
177 |
178 | a = new uint64_t(0, 0);
179 | b = new uint64_t(0xffffffff, 0xffffffff);
180 | p = uint64_t.mulq(a, b);
181 | t.deepEqual(p, { _hi: 0, _lo: 0 }, 'product with 0 is 0');
182 | p = uint64_t.mulq(b, a);
183 | t.deepEqual(p, { _hi: 0, _lo: 0 }, 'product with 0 is 0');
184 |
185 | a = new uint64_t(0, 1);
186 | b = new uint64_t(0xffffffff, 0);
187 | p = uint64_t.mulq(a, b);
188 | t.deepEqual(p, { _hi: 0xffffffff, _lo: 0 },
189 | 'ffffffff00000000 * 1 = ffffffff00000000');
190 | p = uint64_t.mulq(b, a);
191 | t.deepEqual(p, { _hi: 0xffffffff, _lo: 0 },
192 | '1 * ffffffff00000000 = ffffffff00000000');
193 |
194 | a = new uint64_t(0, 0x10000000);
195 | p = uint64_t.mulq(a, a);
196 | t.deepEqual(p, { _hi: 0x1000000, _lo: 0 },
197 | '10000000 * 10000000 = 100000000000000');
198 |
199 | a = new uint64_t(1, 0);
200 | p = uint64_t.mulq(a, a);
201 | t.deepEqual(p, { _hi: 0, _lo: 0 },
202 | '100000000 * 100000000 = 0');
203 |
204 | a = new uint64_t(0, 0x2cc9a);
205 | b = new uint64_t(0x174, 0x5a0236c4);
206 | p = uint64_t.mulq(a, b);
207 | t.deepEqual(p, { _hi: 0x4124bbc, _lo: 0x568121e8 },
208 | '2cc9a * 1745a0236c4 = 4124bbc568121e8');
209 | p = uint64_t.mulq(b, a);
210 | t.deepEqual(p, { _hi: 0x4124bbc, _lo: 0x568121e8 },
211 | '1745a0236c4 * 2cc9a = 4124bbc568121e8');
212 |
213 | a = new uint64_t(0, 0xffffffff);
214 | p = uint64_t.mulq(a, a);
215 | t.deepEqual(p, { _hi: 0xfffffffe, _lo: 1 },
216 | 'ffffffff * ffffffff = fffffffe00000001');
217 |
218 | a = new uint64_t(0, 0xffffffff);
219 | b = new uint64_t(1, 0);
220 | p = uint64_t.mulq(a, b);
221 | t.deepEqual(p, { _hi: 0xffffffff, _lo: 0 },
222 | 'ffffffff * 100000000 = ffffffff00000000');
223 | p = uint64_t.mulq(b, a);
224 | t.deepEqual(p, { _hi: 0xffffffff, _lo: 0 },
225 | '100000000 * ffffffff = ffffffff00000000');
226 |
227 | a = new uint64_t(0, 0xffffffff);
228 | b = new uint64_t(1, 1);
229 | p = uint64_t.mulq(a, b);
230 | t.deepEqual(p, { _hi: 0xffffffff, _lo: 0xffffffff },
231 | 'ffffffff * 100000001 = ffffffffffffffff');
232 | p = uint64_t.mulq(b, a);
233 | t.deepEqual(p, { _hi: 0xffffffff, _lo: 0xffffffff },
234 | '100000001 * ffffffff = ffffffffffffffff');
235 |
236 | a = new uint64_t(0, 0xffff);
237 | b = new uint64_t(0, 0x1ffff);
238 | p = uint64_t.mulq(a, b);
239 | t.deepEqual(p, { _hi: 1, _lo: 0xfffd0001 },
240 | 'ffff * 1ffff = 1fffd0001');
241 | p = uint64_t.mulq(b, a);
242 | t.deepEqual(p, { _hi: 1, _lo: 0xfffd0001 },
243 | '1ffff * ffff = 1fffd0001');
244 |
245 | t.end();
246 | });
247 |
248 | test('shift left', function (t) {
249 | var a = new uint64_t(0, 0x456);
250 | var r = uint64_t.shlq(a, 0);
251 | t.deepEqual(r, { _hi: 0, _lo: 0x456 }, '456 << 0 = 456');
252 |
253 | a = new uint64_t(0, 0);
254 | r = uint64_t.shlq(a, 18);
255 | t.deepEqual(r, { _hi: 0, _lo: 0 }, '0 << 18 = 0');
256 | r = uint64_t.shlq(a, 46);
257 | t.deepEqual(r, { _hi: 0, _lo: 0 }, '0 << 46 = 0');
258 | r = uint64_t.shlq(a, 64);
259 | t.deepEqual(r, { _hi: 0, _lo: 0 }, '0 << 64 = 0');
260 | r = uint64_t.shlq(a, 72);
261 | t.deepEqual(r, { _hi: 0, _lo: 0 }, '0 << 72 = 0');
262 |
263 | a = new uint64_t(0x5a5a5a5a, 0xa5a5a5a5);
264 | r = uint64_t.shlq(a, 0);
265 | t.deepEqual(r, { _hi: 0x5a5a5a5a, _lo: 0xa5a5a5a5 },
266 | '5a5a5a5aa5a5a5a5 << 0 = 5a5a5a5aa5a5a5a5');
267 | r = uint64_t.shlq(a, 1);
268 | t.deepEqual(r, { _hi: 0xb4b4b4b5, _lo: 0x4b4b4b4a },
269 | '5a5a5a5aa5a5a5a5 << 1 = b4b4b4b54b4b4b4a');
270 | r = uint64_t.shlq(a, 31);
271 | t.deepEqual(r, { _hi: 0x52d2d2d2, _lo: 0x80000000 },
272 | '5a5a5a5aa5a5a5a5 << 31 = 52d2d2d280000000');
273 | r = uint64_t.shlq(a, 32);
274 | t.deepEqual(r, { _hi: 0xa5a5a5a5, _lo: 0 },
275 | '5a5a5a5aa5a5a5a5 << 32 = a5a5a5a500000000');
276 | r = uint64_t.shlq(a, 33);
277 | t.deepEqual(r, { _hi: 0x4b4b4b4a, _lo: 0 },
278 | '5a5a5a5aa5a5a5a5 << 33 = 4b4b4b4a00000000');
279 | r = uint64_t.shlq(a, 63);
280 | t.deepEqual(r, { _hi: 0x80000000, _lo: 0 },
281 | '5a5a5a5aa5a5a5a5 << 63 = 8000000000000000');
282 | r = uint64_t.shlq(a, 64);
283 | t.deepEqual(r, { _hi: 0, _lo: 0 },
284 | '5a5a5a5aa5a5a5a5 << 64 = 0');
285 | r = uint64_t.shlq(a, 65);
286 | t.deepEqual(r, { _hi: 0, _lo: 0 },
287 | '5a5a5a5aa5a5a5a5 << 65 = 0');
288 |
289 | a = new uint64_t(0, 0xc0010001);
290 | r = uint64_t.shlq(a, 1);
291 | t.deepEqual(r, { _hi: 1, _lo: 0x80020002 },
292 | 'c0010001 << 1 = 180020002');
293 |
294 | a = new uint64_t(0xf0000000, 0xf0000000);
295 | r = uint64_t.shlq(a, 1);
296 | t.deepEqual(r, { _hi: 0xe0000001, _lo: 0xe0000000 },
297 | 'f0000000f0000000 << 1 = e0000001e0000000');
298 | r = uint64_t.shlq(a, 16);
299 | t.deepEqual(r, { _hi: 0xf000, _lo: 0 },
300 | 'f0000000f0000000 << 16 = f00000000000');
301 |
302 | t.end();
303 | });
304 |
305 | test('shift right', function (t) {
306 | var a = new uint64_t(0, 0x456);
307 | var r = uint64_t.shrlq(a, 0);
308 | t.deepEqual(r, { _hi: 0, _lo: 0x456 }, '456 >>> 0 = 456');
309 |
310 | a = new uint64_t(0);
311 | r = uint64_t.shrlq(a, 6);
312 | t.deepEqual(r, { _hi: 0, _lo: 0 }, '0 >>> 6 = 0');
313 |
314 | a = new uint64_t(0x10000000, 0);
315 | r = uint64_t.shrlq(a, 1);
316 | t.deepEqual(r, { _hi: 0x8000000, _lo: 0 },
317 | '1000000000000000 >>> 1 = 800000000000000');
318 | r = uint64_t.shrlq(a, 20);
319 | t.deepEqual(r, { _hi: 0x100, _lo: 0 },
320 | '1000000000000000 >>> 20 = 10000000000');
321 | r = uint64_t.shrlq(a, 28);
322 | t.deepEqual(r, { _hi: 1, _lo: 0 },
323 | '1000000000000000 >>> 28 = 100000000');
324 | r = uint64_t.shrlq(a, 32);
325 | t.deepEqual(r, { _hi: 0, _lo: 0x10000000 },
326 | '1000000000000000 >>> 32 = 10000000');
327 | r = uint64_t.shrlq(a, 33);
328 | t.deepEqual(r, { _hi: 0, _lo: 0x8000000 },
329 | '1000000000000000 >>> 33 = 8000000');
330 |
331 | a = new uint64_t(0x4c00, 0x80000000);
332 | r = uint64_t.shrlq(a, 4);
333 | t.deepEqual(r, { _hi: 0x4c0, _lo: 0x8000000 },
334 | '4c0080000000 >>> 4 = 4c008000000');
335 | r = uint64_t.shrlq(a, 16);
336 | t.deepEqual(r, { _hi: 0, _lo: 0x4c008000 },
337 | '4c0080000000 >>> 16 = 4c008000');
338 |
339 | a = new uint64_t(0xe7802376, 0x5ddefa11);
340 | r = uint64_t.shrlq(a, 30);
341 | t.deepEqual(r, { _hi: 3, _lo: 0x9e008dd9 },
342 | 'e78023765ddefa11 >>> 30 = 39e008dd9');
343 | r = uint64_t.shrlq(a, 31);
344 | t.deepEqual(r, { _hi: 1, _lo: 0xcf0046ec },
345 | 'e78023765ddefa11 >>> 31 = 1cf0046ec');
346 | r = uint64_t.shrlq(a, 36);
347 | t.deepEqual(r, { _hi: 0, _lo: 0xe780237 },
348 | 'e78023765ddefa11 >>> 36 = e780237');
349 | r = uint64_t.shrlq(a, 63);
350 | t.deepEqual(r, { _hi: 0, _lo: 1 },
351 | 'e78023765ddefa11 >>> 63 = 1');
352 | r = uint64_t.shrlq(a, 64);
353 | t.deepEqual(r, { _hi: 0, _lo: 0 },
354 | 'e78023765ddefa11 >>> 64 = 0');
355 | r = uint64_t.shrlq(a, 65);
356 | t.deepEqual(r, { _hi: 0, _lo: 0 },
357 | 'e78023765ddefa11 >>> 65 = 0');
358 |
359 | t.end();
360 | });
361 |
362 | test('test for nonzero', function (t) {
363 | var a = new uint64_t(0, 0);
364 | t.notOk(uint64_t.tstq(a), '0 is 0');
365 |
366 | a = new uint64_t(0, 1);
367 | t.ok(uint64_t.tstq(a), '1 is not 0');
368 |
369 | a = new uint64_t(1, 0);
370 | t.ok(uint64_t.tstq(a), '100000000 is not 0');
371 |
372 | a = new uint64_t(1, 1);
373 | t.ok(uint64_t.tstq(a), '100000001 is not 0');
374 |
375 | a = new uint64_t(0xef002345, 0xffcc4622);
376 | t.ok(uint64_t.tstq(a), 'ef002345ffcc4622 is not 0');
377 |
378 | t.end();
379 | });
380 |
381 | test('comparison', function (t) {
382 | var z = new uint64_t(0, 0);
383 | t.equal(uint64_t.cmpq(z, z), 0, '0 == 0');
384 |
385 | var a = new uint64_t(1, 0);
386 | t.equal(uint64_t.cmpq(a, z), 1, '100000000 > 0');
387 | t.equal(uint64_t.cmpq(z, a), -1, '0 < 100000000');
388 |
389 | var b = new uint64_t(0xfff00000);
390 | t.equal(uint64_t.cmpq(a, b), 1, '100000000 > fff00000');
391 | t.equal(uint64_t.cmpq(b, a), -1, 'fff00000 < 100000000');
392 |
393 | var a = new uint64_t(0xffffffff, 0xffffffff);
394 | t.equal(uint64_t.cmpq(a, z), 1, 'ffffffffffffffff > 0');
395 | t.equal(uint64_t.cmpq(z, a), -1, '0 < ffffffffffffffff');
396 | t.equal(uint64_t.cmpq(a, b), 1, 'ffffffffffffffff > fff00000');
397 | t.equal(uint64_t.cmpq(b, a), -1, 'fff00000 < ffffffffffffffff');
398 |
399 | t.equal(uint64_t.cmpq(42, 878), -1, 'explicit 42 < 878');
400 | t.equal(uint64_t.cmpq('9000000000', 2000000000), 1, '9e9 > 2e9');
401 |
402 | t.end();
403 | });
404 |
405 | test('toString', function (t) {
406 | var a = new uint64_t(0, 0);
407 | var s = a.toString();
408 | t.equal(s, '0', 'should be 0');
409 |
410 | a = new uint64_t(0, 0x456);
411 | s = a.toString();
412 | t.equal(s, '1110', 'should be 1110');
413 |
414 | a = new uint64_t(0, 0xffffffff);
415 | s = a.toString();
416 | t.equal(s, '4294967295', 'should be 4294967295');
417 |
418 | a = new uint64_t(1, 0);
419 | s = a.toString();
420 | t.equal(s, '4294967296', 'should be 4294967296');
421 |
422 | a = new uint64_t(0, 4095);
423 | s = a.toString();
424 | t.equal(s, '4095', 'should be 4095');
425 |
426 | a = new uint64_t(0, 6928001);
427 | s = a.toString();
428 | t.equal(s, '6928001', 'should be 6928001');
429 |
430 | a = new uint64_t(0x4055a, 0xc80a2941);
431 | s = a.toString();
432 | t.equal(s, '1131787368147265', 'should be 1131787368147265');
433 |
434 | a = new uint64_t(0xffffffff, 0xffffffff);
435 | s = a.toString();
436 | t.equal(s, '18446744073709551615', 'should be 18446744073709551615');
437 |
438 | t.end();
439 | });
440 |
441 | test('toOctets', function (t) {
442 | var a = new uint64_t(0, 0)
443 | var o = a.toOctets();
444 | t.deepEqual(o, [ 0 ], 'should be [ 0 ]');
445 |
446 | a = new uint64_t(0x44556677, 0x01020304);
447 | o = a.toOctets();
448 | t.deepEqual(o, [ 0x44, 0x55, 0x66, 0x77, 0x01, 0x02, 0x03, 0x04 ],
449 | 'should be [ 0x44, 0x55, 0x66, 0x77, 0x01, 0x02, 0x03, 0x04 ]');
450 |
451 | a = new uint64_t(0, 0x456);
452 | o = a.toOctets();
453 | t.deepEqual(o, [ 0x04, 0x56 ], 'should be [ 0x04, 0x56 ]');
454 |
455 | a = new uint64_t(0x456, 0);
456 | o = a.toOctets();
457 | t.deepEqual(o, [ 0x04, 0x56, 0, 0, 0, 0 ],
458 | 'should be [ 0x04, 0x56, 0, 0, 0, 0 ]');
459 |
460 | a = new uint64_t(0xfcfcfcfc, 0x80808080);
461 | o = a.toOctets();
462 | t.deepEqual(o, [ 0xfc, 0xfc, 0xfc, 0xfc, 0x80, 0x80, 0x80, 0x80 ],
463 | 'should be [ 0xfc, 0xfc, 0xfc, 0xfc, 0x80, 0x80, 0x80, 0x80 ]');
464 |
465 | t.end();
466 | });
467 |
--------------------------------------------------------------------------------
/tl.js:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env node
2 | /*
3 | * Copyright (c) 2013, Joyent, Inc. All rights reserved.
4 | */
5 |
6 | var snmp = require('./lib/index.js');
7 | var mib = require('./lib/mib/index.js');
8 | var bunyan = require('bunyan');
9 | var fs = require('fs');
10 | var util = require('util');
11 |
12 | var config = process.argv[2] || 'tl.json';
13 | var cfstr = fs.readFileSync(config);
14 | var cf, log_cf;
15 | var log, tl;
16 |
17 | cf = JSON.parse(cfstr);
18 | log_cf = cf.log || {
19 | name: 'snmpd',
20 | level: 'trace'
21 | };
22 |
23 | log = new bunyan(log_cf);
24 |
25 | tl = snmp.createTrapListener({
26 | log: log
27 | });
28 |
29 | tl.on('trap', function (msg) {
30 | console.log(util.inspect(snmp.message.serializer(msg), false, null));
31 | });
32 | tl.bind({ family: 'udp4', port: 162 });
33 |
--------------------------------------------------------------------------------
/tl.json:
--------------------------------------------------------------------------------
1 | {
2 | "log": {
3 | "name": "snmpd",
4 | "level": "trace"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/tools/jsl.node.conf:
--------------------------------------------------------------------------------
1 | #
2 | # Configuration File for JavaScript Lint
3 | #
4 | # This configuration file can be used to lint a collection of scripts, or to enable
5 | # or disable warnings for scripts that are linted via the command line.
6 | #
7 |
8 | ### Warnings
9 | # Enable or disable warnings based on requirements.
10 | # Use "+WarningName" to display or "-WarningName" to suppress.
11 | #
12 | +ambiguous_else_stmt # the else statement could be matched with one of multiple if statements (use curly braces to indicate intent
13 | +ambiguous_nested_stmt # block statements containing block statements should use curly braces to resolve ambiguity
14 | +ambiguous_newline # unexpected end of line; it is ambiguous whether these lines are part of the same statement
15 | +anon_no_return_value # anonymous function does not always return value
16 | +assign_to_function_call # assignment to a function call
17 | -block_without_braces # block statement without curly braces
18 | +comma_separated_stmts # multiple statements separated by commas (use semicolons?)
19 | +comparison_type_conv # comparisons against null, 0, true, false, or an empty string allowing implicit type conversion (use === or !==)
20 | +default_not_at_end # the default case is not at the end of the switch statement
21 | +dup_option_explicit # duplicate "option explicit" control comment
22 | +duplicate_case_in_switch # duplicate case in switch statement
23 | +duplicate_formal # duplicate formal argument {name}
24 | +empty_statement # empty statement or extra semicolon
25 | +identifier_hides_another # identifer {name} hides an identifier in a parent scope
26 | -inc_dec_within_stmt # increment (++) and decrement (--) operators used as part of greater statement
27 | +incorrect_version # Expected /*jsl:content-type*/ control comment. The script was parsed with the wrong version.
28 | +invalid_fallthru # unexpected "fallthru" control comment
29 | +invalid_pass # unexpected "pass" control comment
30 | +jsl_cc_not_understood # couldn't understand control comment using /*jsl:keyword*/ syntax
31 | +leading_decimal_point # leading decimal point may indicate a number or an object member
32 | +legacy_cc_not_understood # couldn't understand control comment using /*@keyword@*/ syntax
33 | +meaningless_block # meaningless block; curly braces have no impact
34 | +mismatch_ctrl_comments # mismatched control comment; "ignore" and "end" control comments must have a one-to-one correspondence
35 | +misplaced_regex # regular expressions should be preceded by a left parenthesis, assignment, colon, or comma
36 | +missing_break # missing break statement
37 | +missing_break_for_last_case # missing break statement for last case in switch
38 | +missing_default_case # missing default case in switch statement
39 | +missing_option_explicit # the "option explicit" control comment is missing
40 | +missing_semicolon # missing semicolon
41 | +missing_semicolon_for_lambda # missing semicolon for lambda assignment
42 | +multiple_plus_minus # unknown order of operations for successive plus (e.g. x+++y) or minus (e.g. x---y) signs
43 | +nested_comment # nested comment
44 | +no_return_value # function {name} does not always return a value
45 | +octal_number # leading zeros make an octal number
46 | +parseint_missing_radix # parseInt missing radix parameter
47 | +partial_option_explicit # the "option explicit" control comment, if used, must be in the first script tag
48 | +redeclared_var # redeclaration of {name}
49 | +trailing_comma_in_array # extra comma is not recommended in array initializers
50 | +trailing_decimal_point # trailing decimal point may indicate a number or an object member
51 | +undeclared_identifier # undeclared identifier: {name}
52 | +unreachable_code # unreachable code
53 | -unreferenced_argument # argument declared but never referenced: {name}
54 | -unreferenced_function # function is declared but never referenced: {name}
55 | +unreferenced_variable # variable is declared but never referenced: {name}
56 | +unsupported_version # JavaScript {version} is not supported
57 | +use_of_label # use of label
58 | +useless_assign # useless assignment
59 | +useless_comparison # useless comparison; comparing identical expressions
60 | -useless_quotes # the quotation marks are unnecessary
61 | +useless_void # use of the void type may be unnecessary (void is always undefined)
62 | +var_hides_arg # variable {name} hides argument
63 | +want_assign_or_call # expected an assignment or function call
64 | +with_statement # with statement hides undeclared variables; use temporary variable instead
65 |
66 |
67 | ### Output format
68 | # Customize the format of the error message.
69 | # __FILE__ indicates current file path
70 | # __FILENAME__ indicates current file name
71 | # __LINE__ indicates current line
72 | # __COL__ indicates current column
73 | # __ERROR__ indicates error message (__ERROR_PREFIX__: __ERROR_MSG__)
74 | # __ERROR_NAME__ indicates error name (used in configuration file)
75 | # __ERROR_PREFIX__ indicates error prefix
76 | # __ERROR_MSG__ indicates error message
77 | #
78 | # For machine-friendly output, the output format can be prefixed with
79 | # "encode:". If specified, all items will be encoded with C-slashes.
80 | #
81 | # Visual Studio syntax (default):
82 | +output-format __FILE__(__LINE__): __ERROR__
83 | # Alternative syntax:
84 | #+output-format __FILE__:__LINE__: __ERROR__
85 |
86 |
87 | ### Context
88 | # Show the in-line position of the error.
89 | # Use "+context" to display or "-context" to suppress.
90 | #
91 | +context
92 |
93 |
94 | ### Control Comments
95 | # Both JavaScript Lint and the JScript interpreter confuse each other with the syntax for
96 | # the /*@keyword@*/ control comments and JScript conditional comments. (The latter is
97 | # enabled in JScript with @cc_on@). The /*jsl:keyword*/ syntax is preferred for this reason,
98 | # although legacy control comments are enabled by default for backward compatibility.
99 | #
100 | -legacy_control_comments
101 |
102 |
103 | ### Defining identifiers
104 | # By default, "option explicit" is enabled on a per-file basis.
105 | # To enable this for all files, use "+always_use_option_explicit"
106 | -always_use_option_explicit
107 |
108 | # Define certain identifiers of which the lint is not aware.
109 | # (Use this in conjunction with the "undeclared identifier" warning.)
110 | #
111 | # Common uses for webpages might be:
112 | +define __dirname
113 | +define clearInterval
114 | +define clearTimeout
115 | +define console
116 | +define exports
117 | +define global
118 | +define module
119 | +define process
120 | +define require
121 | +define setInterval
122 | +define setTimeout
123 | +define Buffer
124 | +define JSON
125 | +define Math
126 |
127 | ### JavaScript Version
128 | # To change the default JavaScript version:
129 | #+default-type text/javascript;version=1.5
130 | #+default-type text/javascript;e4x=1
131 |
132 | ### Files
133 | # Specify which files to lint
134 | # Use "+recurse" to enable recursion (disabled by default).
135 | # To add a set of files, use "+process FileName", "+process Folder\Path\*.js",
136 | # or "+process Folder\Path\*.htm".
137 | #
138 |
139 |
--------------------------------------------------------------------------------