netsnmp 0.1.5 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +17 -21
- data/lib/netsnmp.rb +12 -0
- data/lib/netsnmp/client.rb +2 -3
- data/lib/netsnmp/oid.rb +3 -2
- data/lib/netsnmp/pdu.rb +4 -4
- data/lib/netsnmp/scoped_pdu.rb +2 -0
- data/lib/netsnmp/security_parameters.rb +3 -3
- data/lib/netsnmp/session.rb +5 -16
- data/lib/netsnmp/v3_session.rb +5 -3
- data/lib/netsnmp/varbind.rb +28 -9
- data/lib/netsnmp/version.rb +1 -1
- data/sig/client.rbs +24 -0
- data/sig/message.rbs +7 -0
- data/sig/netsnmp.rbs +23 -0
- data/sig/oid.rbs +18 -0
- data/sig/openssl.rbs +20 -0
- data/sig/pdu.rbs +48 -0
- data/sig/scoped_pdu.rbs +15 -0
- data/sig/security_parameters.rbs +56 -0
- data/sig/session.rbs +36 -0
- data/sig/timeticks.rbs +7 -0
- data/sig/v3_session.rbs +21 -0
- data/sig/varbind.rbs +30 -0
- data/spec/client_spec.rb +2 -2
- data/spec/handlers/celluloid_spec.rb +3 -2
- data/spec/session_spec.rb +2 -2
- data/spec/spec_helper.rb +7 -4
- data/spec/support/request_examples.rb +2 -2
- data/spec/v3_session_spec.rb +4 -4
- data/spec/varbind_spec.rb +100 -3
- metadata +26 -90
- data/.coveralls.yml +0 -1
- data/.gitignore +0 -14
- data/.rspec +0 -2
- data/.rubocop.yml +0 -11
- data/.rubocop_todo.yml +0 -69
- data/.travis.yml +0 -32
- data/Gemfile +0 -22
- data/Rakefile +0 -30
- data/netsnmp.gemspec +0 -31
- data/spec/support/Dockerfile +0 -14
- data/spec/support/specs.sh +0 -51
- data/spec/support/stop_docker.sh +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d6bb62b9abd416188660e04be1c12cf009b1df0e307030b9c239ea4b2acd73cc
|
4
|
+
data.tar.gz: ee88f7eb292f1a0077ab25e4677713290447e0e7755049bbe6db50f07d15d6a1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fc90cc626d4be2a04d598ec267bcfc108e87aea8e8a585a7611033fc56cb6c174a222250bb9eeeb05b2d03d058983b106c92c30ba3d91e922b171ecfbc37041a
|
7
|
+
data.tar.gz: '09dbbb277ae24d9b760c0de70738a02037da527d653d5e003632c1b41d808cc21c44034fb51bec63394a132fe3c30ffaac1adc846dc061ca483e13fc0719af97'
|
data/README.md
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# netsnmp
|
2
2
|
|
3
|
-
|
4
|
-
[![Coverage Status](https://coveralls.io/repos/github/swisscom/ruby-netsnmp/badge.svg?branch=master)](https://coveralls.io/github/swisscom/ruby-netsnmp?branch=master)
|
3
|
+
![Tests](https://github.com/swisscom/ruby-netsnmp/workflows/Tests/badge.svg)
|
5
4
|
[![Code Climate](https://codeclimate.com/github/swisscom/ruby-netsnmp/badges/gpa.svg)](https://codeclimate.com/github/swisscom/ruby-netsnmp)
|
6
5
|
[![Docs](http://img.shields.io/badge/yard-docs-blue.svg)](https://www.rubydoc.info/github/swisscom/ruby-netsnmp/master)
|
7
6
|
|
@@ -123,6 +122,8 @@ If you create an `IPAddr` object (ruby standard library `ipaddr`) and pass it to
|
|
123
122
|
The `NETSNMP::Timeticks` type is internal to this library, but it is a ruby `Numeric` type. You are safe to use it "as a numeric", that is, perform calculations.
|
124
123
|
|
125
124
|
|
125
|
+
Counter32 and Counter64 types will map to plain integers.
|
126
|
+
|
126
127
|
You can find usage examples [here](https://github.com/swisscom/ruby-netsnmp/blob/master/spec/varbind_spec.rb). If you need support to a missing type, you have the following options:
|
127
128
|
|
128
129
|
* Use the `:type` parameter in `#set` calls:
|
@@ -221,6 +222,7 @@ All encoding/decoding/encryption/decryption/digests are done using `openssl`, wh
|
|
221
222
|
|
222
223
|
This library uses RSpec. The client specs are "integration" tests, in that we communicate with an [snmpsim-built snmp agent simulator](https://github.com/etingof/snmpsim).
|
223
224
|
|
225
|
+
|
224
226
|
### RSpec
|
225
227
|
|
226
228
|
You can run all tests by typing:
|
@@ -232,37 +234,31 @@ You can run all tests by typing:
|
|
232
234
|
...
|
233
235
|
```
|
234
236
|
|
235
|
-
### SNMP Simulator
|
236
237
|
|
237
|
-
|
238
|
+
### Docker
|
238
239
|
|
239
|
-
|
240
|
-
|
241
|
-
The preferred way to use the snmp simulator is by using docker.
|
242
|
-
|
243
|
-
In order to start the simulator container, run:
|
240
|
+
The most straightforward way of running the tests is by using the `docker-compose` setup (which is also what's used in the CI). Run it against the ruby version you're targeting:
|
244
241
|
|
245
242
|
```
|
246
|
-
>
|
243
|
+
> docker-compose -f docker-compose.yml -f docker-compose-ruby-${RUBY_MAJOR_VERSION}.${RUBY_MAJOR_VERSION}.yml run netsnmp
|
247
244
|
```
|
248
245
|
|
249
|
-
|
246
|
+
The CI runs the tests against all supported ruby versions. If changes break a specific version of ruby, make sure you commit appropriate changes addressing the edge case, or let me know in the issues board, so I can help.
|
250
247
|
|
251
|
-
|
252
|
-
> docker port test-snmp 1161/udp
|
253
|
-
```
|
248
|
+
### SNMP Simulator
|
254
249
|
|
255
|
-
|
250
|
+
The SNMP simulator runs in its own container in the `docker` setup.
|
256
251
|
|
257
|
-
|
252
|
+
You can install the package yourself (ex: `pip install snmpsim`) and run the server locally, and then set the `SNMP_PORT` environment variable, where the snmp simulator is running.
|
258
253
|
|
259
|
-
|
254
|
+
#### CI
|
260
255
|
|
261
|
-
|
262
|
-
> spec/support/spec.sh run
|
263
|
-
```
|
256
|
+
The job of the CI is:
|
264
257
|
|
265
|
-
|
258
|
+
* Run all the tests;
|
259
|
+
* Make sure the tests cover an appropriate surface of the code;
|
260
|
+
* Lint the code;
|
261
|
+
* (for ruby 3.0) type check the code;
|
266
262
|
|
267
263
|
|
268
264
|
## Contributing
|
data/lib/netsnmp.rb
CHANGED
@@ -34,6 +34,18 @@ rescue LoadError
|
|
34
34
|
end
|
35
35
|
|
36
36
|
module NETSNMP
|
37
|
+
module StringExtensions
|
38
|
+
# If you wonder why this is there: the oauth feature uses a refinement to enhance the
|
39
|
+
# Regexp class locally with #match? , but this is never tested, because ActiveSupport
|
40
|
+
# monkey-patches the same method... Please ActiveSupport, stop being so intrusive!
|
41
|
+
# :nocov:
|
42
|
+
refine(String) do
|
43
|
+
def match?(*args)
|
44
|
+
!match(*args).nil?
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
37
49
|
def self.debug=(io)
|
38
50
|
@debug_output = io
|
39
51
|
end
|
data/lib/netsnmp/client.rb
CHANGED
@@ -23,8 +23,7 @@ module NETSNMP
|
|
23
23
|
# puts client.get(oid: "1.3.6.1.2.1.1.5.0")
|
24
24
|
# end
|
25
25
|
#
|
26
|
-
def initialize(**options)
|
27
|
-
version = options[:version]
|
26
|
+
def initialize(version: nil, **options)
|
28
27
|
version = case version
|
29
28
|
when Integer then version # assume the use know what he's doing
|
30
29
|
when /v?1/ then 0
|
@@ -33,7 +32,7 @@ module NETSNMP
|
|
33
32
|
end
|
34
33
|
|
35
34
|
@retries = options.fetch(:retries, RETRIES)
|
36
|
-
@session ||= version == 3 ? V3Session.new(options) : Session.new(options)
|
35
|
+
@session ||= version == 3 ? V3Session.new(**options) : Session.new(version: version, **options)
|
37
36
|
return unless block_given?
|
38
37
|
begin
|
39
38
|
yield self
|
data/lib/netsnmp/oid.rb
CHANGED
@@ -4,13 +4,14 @@ module NETSNMP
|
|
4
4
|
# Abstracts the OID structure
|
5
5
|
#
|
6
6
|
module OID
|
7
|
+
using StringExtensions unless String.method_defined?(:match?)
|
8
|
+
|
7
9
|
OIDREGEX = /^[\d\.]*$/
|
8
10
|
|
9
11
|
module_function
|
10
12
|
|
11
13
|
def build(o)
|
12
14
|
case o
|
13
|
-
when OID then o
|
14
15
|
when Array
|
15
16
|
o.join(".")
|
16
17
|
when OIDREGEX
|
@@ -29,7 +30,7 @@ module NETSNMP
|
|
29
30
|
# @return [true, false] whether the given OID belongs to the sub-tree
|
30
31
|
#
|
31
32
|
def parent?(parent_oid, child_oid)
|
32
|
-
child_oid.match(/\A#{parent_oid}\./)
|
33
|
+
child_oid.match?(/\A#{parent_oid}\./)
|
33
34
|
end
|
34
35
|
end
|
35
36
|
end
|
data/lib/netsnmp/pdu.rb
CHANGED
@@ -55,7 +55,7 @@ module NETSNMP
|
|
55
55
|
when :response then 2
|
56
56
|
else raise Error, "#{type} is not supported as type"
|
57
57
|
end
|
58
|
-
new(
|
58
|
+
new(type: typ, **args)
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
@@ -64,7 +64,7 @@ module NETSNMP
|
|
64
64
|
attr_reader :version, :community, :request_id
|
65
65
|
|
66
66
|
def initialize(type:, headers:,
|
67
|
-
request_id:
|
67
|
+
request_id: SecureRandom.random_number(MAXREQUESTID),
|
68
68
|
error_status: 0,
|
69
69
|
error_index: 0,
|
70
70
|
varbinds: [])
|
@@ -75,9 +75,9 @@ module NETSNMP
|
|
75
75
|
@type = type
|
76
76
|
@varbinds = []
|
77
77
|
varbinds.each do |varbind|
|
78
|
-
add_varbind(varbind)
|
78
|
+
add_varbind(**varbind)
|
79
79
|
end
|
80
|
-
@request_id = request_id
|
80
|
+
@request_id = request_id
|
81
81
|
check_error_status(@error_status)
|
82
82
|
end
|
83
83
|
|
data/lib/netsnmp/scoped_pdu.rb
CHANGED
@@ -24,7 +24,7 @@ module NETSNMP
|
|
24
24
|
|
25
25
|
# @param [String] username the snmp v3 username
|
26
26
|
# @param [String] engine_id the device engine id (initialized to '' for report)
|
27
|
-
# @param [Symbol, integer] security_level allowed snmp v3 security level (:auth_priv, :
|
27
|
+
# @param [Symbol, integer] security_level allowed snmp v3 security level (:auth_priv, :auth_no_priv, etc)
|
28
28
|
# @param [Symbol, nil] auth_protocol a supported authentication protocol (currently supported: :md5, :sha)
|
29
29
|
# @param [Symbol, nil] priv_protocol a supported privacy protocol (currently supported: :des, :aes)
|
30
30
|
# @param [String, nil] auth_password the authentication password
|
@@ -99,7 +99,7 @@ module NETSNMP
|
|
99
99
|
# @note this method is used in the process of authenticating a message
|
100
100
|
def sign(message)
|
101
101
|
# don't sign unless you have to
|
102
|
-
return
|
102
|
+
return unless @auth_protocol
|
103
103
|
|
104
104
|
key = auth_key.dup
|
105
105
|
|
@@ -211,7 +211,7 @@ module NETSNMP
|
|
211
211
|
end
|
212
212
|
|
213
213
|
def authorizable?
|
214
|
-
@auth_protocol
|
214
|
+
@auth_protocol != :none
|
215
215
|
end
|
216
216
|
end
|
217
217
|
end
|
data/lib/netsnmp/session.rb
CHANGED
@@ -10,7 +10,7 @@ module NETSNMP
|
|
10
10
|
def initialize(version: 1, community: "public", **options)
|
11
11
|
@version = version
|
12
12
|
@community = community
|
13
|
-
validate(options)
|
13
|
+
validate(**options)
|
14
14
|
end
|
15
15
|
|
16
16
|
# Closes the session
|
@@ -36,23 +36,20 @@ module NETSNMP
|
|
36
36
|
# @return [NETSNMP::PDU] the response pdu
|
37
37
|
#
|
38
38
|
def send(pdu)
|
39
|
-
encoded_request =
|
39
|
+
encoded_request = pdu.to_der
|
40
40
|
encoded_response = @transport.send(encoded_request)
|
41
|
-
decode(encoded_response)
|
41
|
+
PDU.decode(encoded_response)
|
42
42
|
end
|
43
43
|
|
44
44
|
private
|
45
45
|
|
46
|
-
def validate(**
|
47
|
-
proxy = options[:proxy]
|
46
|
+
def validate(host: nil, port: 161, proxy: nil, timeout: TIMEOUT, **)
|
48
47
|
if proxy
|
49
48
|
@proxy = true
|
50
49
|
@transport = proxy
|
51
50
|
else
|
52
|
-
host, port = options.values_at(:host, :port)
|
53
51
|
raise "you must provide an hostname/ip under :host" unless host
|
54
|
-
|
55
|
-
@transport = Transport.new(host, port.to_i, timeout: options.fetch(:timeout, TIMEOUT))
|
52
|
+
@transport = Transport.new(host, port.to_i, timeout: timeout)
|
56
53
|
end
|
57
54
|
@version = case @version
|
58
55
|
when Integer then @version # assume the use know what he's doing
|
@@ -64,14 +61,6 @@ module NETSNMP
|
|
64
61
|
end
|
65
62
|
end
|
66
63
|
|
67
|
-
def encode(pdu)
|
68
|
-
pdu.to_der
|
69
|
-
end
|
70
|
-
|
71
|
-
def decode(stream)
|
72
|
-
PDU.decode(stream)
|
73
|
-
end
|
74
|
-
|
75
64
|
class Transport
|
76
65
|
MAXPDUSIZE = 0xffff + 1
|
77
66
|
|
data/lib/netsnmp/v3_session.rb
CHANGED
@@ -4,7 +4,7 @@ module NETSNMP
|
|
4
4
|
# Abstraction for the v3 semantics.
|
5
5
|
class V3Session < Session
|
6
6
|
# @param [String, Integer] version SNMP version (always 3)
|
7
|
-
def initialize(
|
7
|
+
def initialize(context: "", **opts)
|
8
8
|
@context = context
|
9
9
|
@security_parameters = opts.delete(:security_parameters)
|
10
10
|
super
|
@@ -19,8 +19,10 @@ module NETSNMP
|
|
19
19
|
end
|
20
20
|
|
21
21
|
# @see {NETSNMP::Session#send}
|
22
|
-
def send(
|
23
|
-
|
22
|
+
def send(pdu)
|
23
|
+
encoded_request = encode(pdu)
|
24
|
+
encoded_response = @transport.send(encoded_request)
|
25
|
+
pdu, = decode(encoded_response)
|
24
26
|
pdu
|
25
27
|
end
|
26
28
|
|
data/lib/netsnmp/varbind.rb
CHANGED
@@ -6,7 +6,7 @@ module NETSNMP
|
|
6
6
|
class Varbind
|
7
7
|
attr_reader :oid, :value
|
8
8
|
|
9
|
-
def initialize(oid, value: nil, type: nil
|
9
|
+
def initialize(oid, value: nil, type: nil)
|
10
10
|
@oid = OID.build(oid)
|
11
11
|
@type = type
|
12
12
|
@value = convert_val(value) if value
|
@@ -81,17 +81,25 @@ module NETSNMP
|
|
81
81
|
when :ipaddress then 0
|
82
82
|
when :counter32
|
83
83
|
asn_val = [value].pack("N*")
|
84
|
-
asn_val = asn_val[1..-1] while asn_val
|
84
|
+
asn_val = asn_val[1..-1] while asn_val[0] == "\x00".b && asn_val[1].unpack("B").first != "1"
|
85
85
|
1
|
86
86
|
when :gauge
|
87
87
|
asn_val = [value].pack("N*")
|
88
|
-
asn_val = asn_val[1..-1] while asn_val
|
88
|
+
asn_val = asn_val[1..-1] while asn_val[0] == "\x00".b && asn_val[1].unpack("B").first != "1"
|
89
89
|
2
|
90
90
|
when :timetick
|
91
91
|
return Timetick.new(value).to_asn
|
92
92
|
when :opaque then 4
|
93
93
|
when :nsap then 5
|
94
|
-
when :counter64
|
94
|
+
when :counter64
|
95
|
+
asn_val = [
|
96
|
+
(value >> 96) & 0xFFFFFFFF,
|
97
|
+
(value >> 64) & 0xFFFFFFFF,
|
98
|
+
(value >> 32) & 0xFFFFFFFF,
|
99
|
+
value & 0xFFFFFFFF
|
100
|
+
].pack("NNNN")
|
101
|
+
asn_val = asn_val[1..-1] while asn_val.start_with?("\x00")
|
102
|
+
6
|
95
103
|
when :uinteger then 7
|
96
104
|
else
|
97
105
|
raise Error, "#{typ}: unsupported application type"
|
@@ -106,16 +114,27 @@ module NETSNMP
|
|
106
114
|
IPAddr.new_ntoh(asn.value)
|
107
115
|
when 1, # ASN counter 32
|
108
116
|
2 # gauge
|
109
|
-
|
110
|
-
val.prepend("\x00") while val.bytesize < 4
|
111
|
-
asn.value.unpack("N*")[0] || 0
|
117
|
+
unpack_32bit_integer(asn.value)
|
112
118
|
when 3 # timeticks
|
113
|
-
Timetick.new(asn.value
|
119
|
+
Timetick.new(unpack_32bit_integer(asn.value))
|
114
120
|
# when 4 # opaque
|
115
121
|
# when 5 # NSAP
|
116
|
-
|
122
|
+
when 6 # ASN Counter 64
|
123
|
+
unpack_64bit_integer(asn.value)
|
117
124
|
# when 7 # ASN UInteger
|
118
125
|
end
|
119
126
|
end
|
127
|
+
|
128
|
+
private
|
129
|
+
|
130
|
+
def unpack_32bit_integer(payload)
|
131
|
+
payload.prepend("\x00") until (payload.bytesize % 4).zero?
|
132
|
+
payload.unpack("N*")[-1] || 0
|
133
|
+
end
|
134
|
+
|
135
|
+
def unpack_64bit_integer(payload)
|
136
|
+
payload.prepend("\x00") until (payload.bytesize % 16).zero?
|
137
|
+
payload.unpack("NNNN").reduce(0) { |sum, elem| (sum << 32) + elem }
|
138
|
+
end
|
120
139
|
end
|
121
140
|
end
|
data/lib/netsnmp/version.rb
CHANGED
data/sig/client.rbs
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
module NETSNMP
|
2
|
+
class Client
|
3
|
+
|
4
|
+
def get: (*untyped) -> untyped
|
5
|
+
| (*untyped) { (PDU) -> void } -> untyped
|
6
|
+
|
7
|
+
def get_next: (*untyped) -> untyped
|
8
|
+
| (*untyped) { (PDU) -> void } -> untyped
|
9
|
+
|
10
|
+
def set: (*untyped) -> untyped
|
11
|
+
| (*untyped) { (PDU) -> void } -> untyped
|
12
|
+
|
13
|
+
def inform: (*untyped) -> untyped
|
14
|
+
| (*untyped) { (PDU) -> void } -> untyped
|
15
|
+
|
16
|
+
def walk: (oid: oid) -> _Each[oid_type, oid_value]
|
17
|
+
|
18
|
+
def close: () -> void
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def initialize: (?version?: snmp_version, **untyped) -> untyped
|
23
|
+
end
|
24
|
+
end
|
data/sig/message.rbs
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
module NETSNMP
|
2
|
+
module Message
|
3
|
+
def self?.decode: (String stream, security_parameters: SecurityParameters) -> [ScopedPDU, String, Integer, Integer]
|
4
|
+
|
5
|
+
def self?.encode: (ScopedPDU pdu, security_parameters: SecurityParameters, ?engine_boots: Integer, ?engine_time: Integer) -> String
|
6
|
+
end
|
7
|
+
end
|
data/sig/netsnmp.rbs
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
module NETSNMP
|
2
|
+
interface _Logger
|
3
|
+
def <<: (string) -> void
|
4
|
+
end
|
5
|
+
|
6
|
+
interface _Authenticate
|
7
|
+
def reset: () -> void
|
8
|
+
def <<: (string) -> void
|
9
|
+
def digest: () -> String
|
10
|
+
end
|
11
|
+
|
12
|
+
interface _Encrypt
|
13
|
+
def encrypt: (String payload, engine_boots: Integer, engine_time: Integer) -> String
|
14
|
+
|
15
|
+
def decrypt: (String payload, salt: String, engine_boots: Integer, engine_time: Integer) -> String
|
16
|
+
end
|
17
|
+
|
18
|
+
type snmp_version = 0 | 1 | 3 | :v1 | :v2c | :v3 | nil
|
19
|
+
|
20
|
+
def self.debug=: (_Logger) -> void
|
21
|
+
|
22
|
+
def self.debug: { () -> string } -> void
|
23
|
+
end
|
data/sig/oid.rbs
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
module NETSNMP
|
2
|
+
type oid = String | Array[Integer]
|
3
|
+
|
4
|
+
type oid_type = Integer | :ipaddress | :counter32 | :gauge | :timetick | :opaque | :nsap | :counter64 | :uinteger
|
5
|
+
|
6
|
+
type oid_value = String | Integer | Timetick | true | false | nil | IPAddr
|
7
|
+
|
8
|
+
# type oid_options = {}
|
9
|
+
|
10
|
+
module OID
|
11
|
+
|
12
|
+
def self?.build: (oid) -> String
|
13
|
+
|
14
|
+
def self?.to_asn: (String oid) -> OpenSSL::ASN1::ObjectId
|
15
|
+
|
16
|
+
def self?.parent?: (String oid, String oid) -> bool
|
17
|
+
end
|
18
|
+
end
|