This repository has been archived by the owner on Apr 21, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 24
/
varbind.rb
147 lines (131 loc) · 4.69 KB
/
varbind.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# frozen_string_literal: true
module NETSNMP
# Abstracts the PDU variable structure into a ruby object
#
class Varbind
using StringExtensions
attr_reader :oid, :value
def initialize(oid, value: nil, type: nil)
@oid = OID.build(oid)
@type = type
@value = convert_val(value) if value
end
def to_s
"#<#{self.class}:0x#{object_id.to_s(16)} @oid=#{@oid} @value=#{@value}>"
end
def to_der
to_asn.to_der
end
def to_asn
asn_oid = OID.to_asn(@oid)
type = @type
asn_val = if type
convert_to_asn(type, @value)
else
case @value
when String
OpenSSL::ASN1::OctetString.new(@value)
when Integer
OpenSSL::ASN1::Integer.new(@value)
when true, false
OpenSSL::ASN1::Boolean.new(@value)
when nil
OpenSSL::ASN1::Null.new(nil)
when IPAddr
# @type ivar @value: IPAddr
OpenSSL::ASN1::ASN1Data.new(@value.hton, 0, :APPLICATION)
when Timetick
# @type ivar @value: Timetick
@value.to_asn
else
raise Error, "#{@value}: unsupported varbind type"
end
end
OpenSSL::ASN1::Sequence.new([asn_oid, asn_val])
end
def convert_val(asn_value)
case asn_value
when OpenSSL::ASN1::OctetString
val = asn_value.value
# it's kind of common in snmp, some stuff can't be converted,
# like Hexa Strings. Parse them into a readable format a la netsnmp
# https://github.com/net-snmp/net-snmp/blob/ed90aaaaea0d9cc6c5c5533f1863bae598d3b820/snmplib/mib.c#L650
is_hex_string = val.each_char.any? { |c| !c.match?(/[[:print:]]/) && !c.match?(/[[:space:]]/) }
val = HexString.new(val) if is_hex_string
val
when OpenSSL::ASN1::Primitive
val = asn_value.value
val = val.to_i if val.is_a?(OpenSSL::BN)
val
when OpenSSL::ASN1::ASN1Data
# application data
convert_application_asn(asn_value)
# when OpenSSL::BN
else
asn_value # assume it's already primitive
end
end
def convert_to_asn(typ, value)
asn_val = value
asn_type = if typ.is_a?(Symbol)
case typ
when :ipaddress then 0
when :counter32
asn_val = [value].pack("N*")
asn_val = asn_val.delete_prefix("\x00") while asn_val[0] == "\x00".b && String(asn_val[1]).unpack1("B") != "1"
1
when :gauge
asn_val = [value].pack("N*")
asn_val = asn_val.delete_prefix("\x00") while asn_val[0] == "\x00".b && String(asn_val[1]).unpack1("B") != "1"
2
when :timetick
# @type var value: Integer
return Timetick.new(value).to_asn
when :opaque then 4
when :nsap then 5
when :counter64
# @type var value: Integer
asn_val = [
(value >> 96) & 0xFFFFFFFF,
(value >> 64) & 0xFFFFFFFF,
(value >> 32) & 0xFFFFFFFF,
value & 0xFFFFFFFF
].pack("NNNN")
asn_val = asn_val.delete_prefix("\x00") while asn_val.start_with?("\x00")
6
when :uinteger then 7
else
raise Error, "#{typ}: unsupported application type"
end
else
typ
end
OpenSSL::ASN1::ASN1Data.new(asn_val, asn_type, :APPLICATION)
end
def convert_application_asn(asn)
case asn.tag
when 0 # IP Address
IPAddr.new_ntoh(asn.value)
when 1, # ASN counter 32
2 # gauge
unpack_32bit_integer(asn.value)
when 3 # timeticks
Timetick.new(unpack_32bit_integer(asn.value))
# when 4 # opaque
# when 5 # NSAP
when 6 # ASN Counter 64
unpack_64bit_integer(asn.value)
# when 7 # ASN UInteger
end
end
private
def unpack_32bit_integer(payload)
payload.prepend("\x00") until (payload.bytesize % 4).zero?
payload.unpack("N*")[-1].to_i
end
def unpack_64bit_integer(payload)
payload.prepend("\x00") until (payload.bytesize % 16).zero?
payload.unpack("NNNN").reduce(0) { |sum, elem| (sum << 32) + elem.to_i }
end
end
end