netsnmp 0.1.8 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +60 -27
- data/lib/netsnmp.rb +3 -21
- data/lib/netsnmp/client.rb +4 -5
- data/lib/netsnmp/encryption/aes.rb +1 -3
- data/lib/netsnmp/encryption/des.rb +0 -2
- data/lib/netsnmp/errors.rb +1 -0
- data/lib/netsnmp/extensions.rb +113 -0
- data/lib/netsnmp/loggable.rb +36 -0
- data/lib/netsnmp/message.rb +70 -28
- data/lib/netsnmp/mib.rb +172 -0
- data/lib/netsnmp/mib/parser.rb +750 -0
- data/lib/netsnmp/oid.rb +7 -12
- data/lib/netsnmp/pdu.rb +23 -12
- data/lib/netsnmp/scoped_pdu.rb +8 -2
- data/lib/netsnmp/security_parameters.rb +22 -14
- data/lib/netsnmp/session.rb +14 -16
- data/lib/netsnmp/v3_session.rb +21 -9
- data/lib/netsnmp/varbind.rb +27 -22
- data/lib/netsnmp/version.rb +1 -1
- data/sig/client.rbs +24 -0
- data/sig/loggable.rbs +16 -0
- data/sig/message.rbs +9 -0
- data/sig/mib.rbs +21 -0
- data/sig/mib/parser.rbs +7 -0
- data/sig/netsnmp.rbs +19 -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 +58 -0
- data/sig/session.rbs +38 -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 +26 -8
- data/spec/handlers/celluloid_spec.rb +4 -3
- data/spec/mib_spec.rb +13 -0
- data/spec/session_spec.rb +2 -2
- data/spec/spec_helper.rb +9 -5
- data/spec/support/request_examples.rb +2 -2
- data/spec/v3_session_spec.rb +4 -4
- data/spec/varbind_spec.rb +5 -3
- metadata +31 -71
- 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 -28
- 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
data/lib/netsnmp/oid.rb
CHANGED
@@ -4,21 +4,16 @@ 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
|
-
def build(
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
o.join(".")
|
16
|
-
when OIDREGEX
|
17
|
-
o = o[1..-1] if o.start_with?(".")
|
18
|
-
o
|
19
|
-
# TODO: MIB to OID
|
20
|
-
else raise Error, "can't convert #{o} to OID"
|
21
|
-
end
|
13
|
+
def build(id)
|
14
|
+
oid = MIB.oid(id)
|
15
|
+
oid = oid[1..-1] if oid.start_with?(".")
|
16
|
+
oid
|
22
17
|
end
|
23
18
|
|
24
19
|
def to_asn(oid)
|
@@ -29,7 +24,7 @@ module NETSNMP
|
|
29
24
|
# @return [true, false] whether the given OID belongs to the sub-tree
|
30
25
|
#
|
31
26
|
def parent?(parent_oid, child_oid)
|
32
|
-
child_oid.match(/\A#{parent_oid}\./)
|
27
|
+
child_oid.match?(/\A#{parent_oid}\./)
|
33
28
|
end
|
34
29
|
end
|
35
30
|
end
|
data/lib/netsnmp/pdu.rb
CHANGED
@@ -5,7 +5,11 @@ module NETSNMP
|
|
5
5
|
# Abstracts the PDU base structure into a ruby object. It gives access to its varbinds.
|
6
6
|
#
|
7
7
|
class PDU
|
8
|
+
using ASNExtensions
|
9
|
+
|
8
10
|
MAXREQUESTID = 2147483647
|
11
|
+
|
12
|
+
using ASNExtensions
|
9
13
|
class << self
|
10
14
|
def decode(der)
|
11
15
|
asn_tree = case der
|
@@ -53,9 +57,10 @@ module NETSNMP
|
|
53
57
|
when :inform then 6
|
54
58
|
when :trap then 7
|
55
59
|
when :response then 2
|
60
|
+
when :report then 8
|
56
61
|
else raise Error, "#{type} is not supported as type"
|
57
62
|
end
|
58
|
-
new(
|
63
|
+
new(type: typ, **args)
|
59
64
|
end
|
60
65
|
end
|
61
66
|
|
@@ -64,7 +69,7 @@ module NETSNMP
|
|
64
69
|
attr_reader :version, :community, :request_id
|
65
70
|
|
66
71
|
def initialize(type:, headers:,
|
67
|
-
request_id:
|
72
|
+
request_id: SecureRandom.random_number(MAXREQUESTID),
|
68
73
|
error_status: 0,
|
69
74
|
error_index: 0,
|
70
75
|
varbinds: [])
|
@@ -75,9 +80,9 @@ module NETSNMP
|
|
75
80
|
@type = type
|
76
81
|
@varbinds = []
|
77
82
|
varbinds.each do |varbind|
|
78
|
-
add_varbind(varbind)
|
83
|
+
add_varbind(**varbind)
|
79
84
|
end
|
80
|
-
@request_id = request_id
|
85
|
+
@request_id = request_id
|
81
86
|
check_error_status(@error_status)
|
82
87
|
end
|
83
88
|
|
@@ -85,6 +90,10 @@ module NETSNMP
|
|
85
90
|
to_asn.to_der
|
86
91
|
end
|
87
92
|
|
93
|
+
def to_hex
|
94
|
+
to_asn.to_hex
|
95
|
+
end
|
96
|
+
|
88
97
|
# Adds a request varbind to the pdu
|
89
98
|
#
|
90
99
|
# @param [OID] oid a valid oid
|
@@ -96,25 +105,27 @@ module NETSNMP
|
|
96
105
|
alias << add_varbind
|
97
106
|
|
98
107
|
def to_asn
|
99
|
-
request_id_asn = OpenSSL::ASN1::Integer.new(@request_id)
|
100
|
-
error_asn = OpenSSL::ASN1::Integer.new(@error_status)
|
101
|
-
error_index_asn = OpenSSL::ASN1::Integer.new(@error_index)
|
108
|
+
request_id_asn = OpenSSL::ASN1::Integer.new(@request_id).with_label(:request_id)
|
109
|
+
error_asn = OpenSSL::ASN1::Integer.new(@error_status).with_label(:error)
|
110
|
+
error_index_asn = OpenSSL::ASN1::Integer.new(@error_index).with_label(:error_index)
|
102
111
|
|
103
|
-
varbind_asns = OpenSSL::ASN1::Sequence.new(@varbinds.map(&:to_asn))
|
112
|
+
varbind_asns = OpenSSL::ASN1::Sequence.new(@varbinds.map(&:to_asn)).with_label(:varbinds)
|
104
113
|
|
105
114
|
request_asn = OpenSSL::ASN1::ASN1Data.new([request_id_asn,
|
106
115
|
error_asn, error_index_asn,
|
107
116
|
varbind_asns], @type,
|
108
|
-
:CONTEXT_SPECIFIC)
|
117
|
+
:CONTEXT_SPECIFIC).with_label(:request)
|
109
118
|
|
110
|
-
OpenSSL::ASN1::Sequence.new([*encode_headers_asn, request_asn])
|
119
|
+
OpenSSL::ASN1::Sequence.new([*encode_headers_asn, request_asn]).with_label(:pdu)
|
111
120
|
end
|
112
121
|
|
113
122
|
private
|
114
123
|
|
115
124
|
def encode_headers_asn
|
116
|
-
[
|
117
|
-
|
125
|
+
[
|
126
|
+
OpenSSL::ASN1::Integer.new(@version).with_label(:snmp_version),
|
127
|
+
OpenSSL::ASN1::OctetString.new(@community).with_label(:community)
|
128
|
+
]
|
118
129
|
end
|
119
130
|
|
120
131
|
# http://www.tcpipguide.com/free/t_SNMPVersion2SNMPv2MessageFormats-5.htm#Table_219
|
data/lib/netsnmp/scoped_pdu.rb
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
module NETSNMP
|
4
4
|
class ScopedPDU < PDU
|
5
|
+
using ASNExtensions
|
6
|
+
|
5
7
|
attr_reader :engine_id
|
6
8
|
|
7
9
|
def initialize(type:, headers:, **options)
|
@@ -9,9 +11,13 @@ module NETSNMP
|
|
9
11
|
super(type: type, headers: [3, nil], **options)
|
10
12
|
end
|
11
13
|
|
14
|
+
private
|
15
|
+
|
12
16
|
def encode_headers_asn
|
13
|
-
[
|
14
|
-
|
17
|
+
[
|
18
|
+
OpenSSL::ASN1::OctetString.new(@engine_id || "").with_label(:engine_id),
|
19
|
+
OpenSSL::ASN1::OctetString.new(@context || "").with_label(:context)
|
20
|
+
]
|
15
21
|
end
|
16
22
|
end
|
17
23
|
end
|
@@ -8,6 +8,9 @@ module NETSNMP
|
|
8
8
|
# It also provides validation of the security options passed with a client is initialized in v3 mode.
|
9
9
|
class SecurityParameters
|
10
10
|
using StringExtensions
|
11
|
+
using ASNExtensions
|
12
|
+
|
13
|
+
prepend Loggable
|
11
14
|
|
12
15
|
IPAD = "\x36" * 64
|
13
16
|
OPAD = "\x5c" * 64
|
@@ -24,7 +27,7 @@ module NETSNMP
|
|
24
27
|
|
25
28
|
# @param [String] username the snmp v3 username
|
26
29
|
# @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, :
|
30
|
+
# @param [Symbol, integer] security_level allowed snmp v3 security level (:auth_priv, :auth_no_priv, etc)
|
28
31
|
# @param [Symbol, nil] auth_protocol a supported authentication protocol (currently supported: :md5, :sha)
|
29
32
|
# @param [Symbol, nil] priv_protocol a supported privacy protocol (currently supported: :des, :aes)
|
30
33
|
# @param [String, nil] auth_password the authentication password
|
@@ -72,7 +75,10 @@ module NETSNMP
|
|
72
75
|
if encryption
|
73
76
|
encrypted_pdu, salt = encryption.encrypt(pdu.to_der, engine_boots: engine_boots,
|
74
77
|
engine_time: engine_time)
|
75
|
-
[
|
78
|
+
[
|
79
|
+
OpenSSL::ASN1::OctetString.new(encrypted_pdu).with_label(:encrypted_pdu),
|
80
|
+
OpenSSL::ASN1::OctetString.new(salt).with_label(:salt)
|
81
|
+
]
|
76
82
|
else
|
77
83
|
[pdu.to_asn, salt]
|
78
84
|
end
|
@@ -82,15 +88,16 @@ module NETSNMP
|
|
82
88
|
# @param [String] salt the salt from the incoming der
|
83
89
|
# @param [Integer] engine_time the reported engine time
|
84
90
|
# @param [Integer] engine_boots the reported engine boots
|
85
|
-
def decode(der, salt:, engine_time:, engine_boots:)
|
91
|
+
def decode(der, salt:, engine_time:, engine_boots:, security_level: @security_level)
|
86
92
|
asn = OpenSSL::ASN1.decode(der)
|
87
|
-
if
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
93
|
+
return asn if security_level < 3
|
94
|
+
|
95
|
+
return asn unless encryption
|
96
|
+
|
97
|
+
encrypted_pdu = asn.value
|
98
|
+
pdu_der = encryption.decrypt(encrypted_pdu, salt: salt, engine_time: engine_time, engine_boots: engine_boots)
|
99
|
+
log(level: 2) { "message has been decrypted" }
|
100
|
+
OpenSSL::ASN1.decode(pdu_der)
|
94
101
|
end
|
95
102
|
|
96
103
|
# @param [String] message the already encoded snmp v3 message
|
@@ -99,7 +106,7 @@ module NETSNMP
|
|
99
106
|
# @note this method is used in the process of authenticating a message
|
100
107
|
def sign(message)
|
101
108
|
# don't sign unless you have to
|
102
|
-
return
|
109
|
+
return unless @auth_protocol
|
103
110
|
|
104
111
|
key = auth_key.dup
|
105
112
|
|
@@ -120,10 +127,11 @@ module NETSNMP
|
|
120
127
|
# @param [String] salt the incoming payload''s salt
|
121
128
|
#
|
122
129
|
# @raise [NETSNMP::Error] if the message's integration has been violated
|
123
|
-
def verify(stream, salt)
|
124
|
-
return if
|
130
|
+
def verify(stream, salt, security_level: @security_level)
|
131
|
+
return if security_level < 1
|
125
132
|
verisalt = sign(stream)
|
126
133
|
raise Error, "invalid message authentication salt" unless verisalt == salt
|
134
|
+
log(level: 2) { "message has been verified" }
|
127
135
|
end
|
128
136
|
|
129
137
|
def must_revalidate?
|
@@ -211,7 +219,7 @@ module NETSNMP
|
|
211
219
|
end
|
212
220
|
|
213
221
|
def authorizable?
|
214
|
-
@auth_protocol
|
222
|
+
@auth_protocol != :none
|
215
223
|
end
|
216
224
|
end
|
217
225
|
end
|
data/lib/netsnmp/session.rb
CHANGED
@@ -4,13 +4,15 @@ module NETSNMP
|
|
4
4
|
# Let's just remind that there is no session in snmp, this is just an abstraction.
|
5
5
|
#
|
6
6
|
class Session
|
7
|
+
prepend Loggable
|
8
|
+
|
7
9
|
TIMEOUT = 2
|
8
10
|
|
9
11
|
# @param [Hash] opts the options set
|
10
12
|
def initialize(version: 1, community: "public", **options)
|
11
13
|
@version = version
|
12
14
|
@community = community
|
13
|
-
validate(options)
|
15
|
+
validate(**options)
|
14
16
|
end
|
15
17
|
|
16
18
|
# Closes the session
|
@@ -36,23 +38,27 @@ module NETSNMP
|
|
36
38
|
# @return [NETSNMP::PDU] the response pdu
|
37
39
|
#
|
38
40
|
def send(pdu)
|
39
|
-
|
41
|
+
log { "sending request..." }
|
42
|
+
log(level: 2) { pdu.to_hex }
|
43
|
+
encoded_request = pdu.to_der
|
44
|
+
log { Hexdump.dump(encoded_request) }
|
40
45
|
encoded_response = @transport.send(encoded_request)
|
41
|
-
|
46
|
+
log { "received response" }
|
47
|
+
log { Hexdump.dump(encoded_response) }
|
48
|
+
response_pdu = PDU.decode(encoded_response)
|
49
|
+
log(level: 2) { response_pdu.to_hex }
|
50
|
+
response_pdu
|
42
51
|
end
|
43
52
|
|
44
53
|
private
|
45
54
|
|
46
|
-
def validate(**
|
47
|
-
proxy = options[:proxy]
|
55
|
+
def validate(host: nil, port: 161, proxy: nil, timeout: TIMEOUT, **)
|
48
56
|
if proxy
|
49
57
|
@proxy = true
|
50
58
|
@transport = proxy
|
51
59
|
else
|
52
|
-
host, port = options.values_at(:host, :port)
|
53
60
|
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))
|
61
|
+
@transport = Transport.new(host, port.to_i, timeout: timeout)
|
56
62
|
end
|
57
63
|
@version = case @version
|
58
64
|
when Integer then @version # assume the use know what he's doing
|
@@ -64,14 +70,6 @@ module NETSNMP
|
|
64
70
|
end
|
65
71
|
end
|
66
72
|
|
67
|
-
def encode(pdu)
|
68
|
-
pdu.to_der
|
69
|
-
end
|
70
|
-
|
71
|
-
def decode(stream)
|
72
|
-
PDU.decode(stream)
|
73
|
-
end
|
74
|
-
|
75
73
|
class Transport
|
76
74
|
MAXPDUSIZE = 0xffff + 1
|
77
75
|
|
data/lib/netsnmp/v3_session.rb
CHANGED
@@ -4,10 +4,11 @@ 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
|
11
|
+
@message_serializer = Message.new(**opts)
|
11
12
|
end
|
12
13
|
|
13
14
|
# @see {NETSNMP::Session#build_pdu}
|
@@ -19,9 +20,19 @@ module NETSNMP
|
|
19
20
|
end
|
20
21
|
|
21
22
|
# @see {NETSNMP::Session#send}
|
22
|
-
def send(
|
23
|
-
|
24
|
-
pdu
|
23
|
+
def send(pdu)
|
24
|
+
log { "sending request..." }
|
25
|
+
encoded_request = encode(pdu)
|
26
|
+
encoded_response = @transport.send(encoded_request)
|
27
|
+
response_pdu, *args = decode(encoded_response)
|
28
|
+
if response_pdu.type == 8
|
29
|
+
varbind = response_pdu.varbinds.first
|
30
|
+
if varbind.oid == "1.3.6.1.6.3.15.1.1.2.0" # IdNotInTimeWindow
|
31
|
+
_, @engine_boots, @engine_time = args
|
32
|
+
raise IdNotInTimeWindowError, "request timestamp is already out of time window"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
response_pdu
|
25
36
|
end
|
26
37
|
|
27
38
|
private
|
@@ -58,7 +69,8 @@ module NETSNMP
|
|
58
69
|
report_sec_params = SecurityParameters.new(security_level: 0,
|
59
70
|
username: @security_parameters.username)
|
60
71
|
pdu = ScopedPDU.build(:get, headers: [])
|
61
|
-
|
72
|
+
log { "sending probe..." }
|
73
|
+
encoded_report_pdu = @message_serializer.encode(pdu, security_parameters: report_sec_params)
|
62
74
|
|
63
75
|
encoded_response_pdu = @transport.send(encoded_report_pdu)
|
64
76
|
|
@@ -67,13 +79,13 @@ module NETSNMP
|
|
67
79
|
end
|
68
80
|
|
69
81
|
def encode(pdu)
|
70
|
-
|
71
|
-
|
72
|
-
|
82
|
+
@message_serializer.encode(pdu, security_parameters: @security_parameters,
|
83
|
+
engine_boots: @engine_boots,
|
84
|
+
engine_time: @engine_time)
|
73
85
|
end
|
74
86
|
|
75
87
|
def decode(stream, security_parameters: @security_parameters)
|
76
|
-
|
88
|
+
@message_serializer.decode(stream, security_parameters: security_parameters)
|
77
89
|
end
|
78
90
|
end
|
79
91
|
end
|
data/lib/netsnmp/varbind.rb
CHANGED
@@ -4,9 +4,11 @@ module NETSNMP
|
|
4
4
|
# Abstracts the PDU variable structure into a ruby object
|
5
5
|
#
|
6
6
|
class Varbind
|
7
|
+
using StringExtensions
|
8
|
+
|
7
9
|
attr_reader :oid, :value
|
8
10
|
|
9
|
-
def initialize(oid, value: nil, type: nil
|
11
|
+
def initialize(oid, value: nil, type: nil)
|
10
12
|
@oid = OID.build(oid)
|
11
13
|
@type = type
|
12
14
|
@value = convert_val(value) if value
|
@@ -48,18 +50,15 @@ module NETSNMP
|
|
48
50
|
def convert_val(asn_value)
|
49
51
|
case asn_value
|
50
52
|
when OpenSSL::ASN1::OctetString
|
51
|
-
|
53
|
+
val = asn_value.value
|
54
|
+
|
52
55
|
# it's kind of common in snmp, some stuff can't be converted,
|
53
56
|
# like Hexa Strings. Parse them into a readable format a la netsnmp
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
Encoding::InvalidByteSequenceError
|
60
|
-
# hexdump me!
|
61
|
-
val.unpack("H*")[0].upcase.scan(/../).join(" ")
|
62
|
-
end
|
57
|
+
# https://github.com/net-snmp/net-snmp/blob/ed90aaaaea0d9cc6c5c5533f1863bae598d3b820/snmplib/mib.c#L650
|
58
|
+
is_hex_string = val.each_char.any? { |c| !c.match?(/[[:print:]]/) && !c.match?(/[[:space:]]/) }
|
59
|
+
|
60
|
+
val = HexString.new(val) if is_hex_string
|
61
|
+
val
|
63
62
|
when OpenSSL::ASN1::Primitive
|
64
63
|
val = asn_value.value
|
65
64
|
val = val.to_i if val.is_a?(OpenSSL::BN)
|
@@ -81,11 +80,11 @@ module NETSNMP
|
|
81
80
|
when :ipaddress then 0
|
82
81
|
when :counter32
|
83
82
|
asn_val = [value].pack("N*")
|
84
|
-
asn_val = asn_val[1..-1] while asn_val[0] == "\x00".b && asn_val[1].
|
83
|
+
asn_val = asn_val[1..-1] while asn_val[0] == "\x00".b && asn_val[1].unpack1("B") != "1"
|
85
84
|
1
|
86
85
|
when :gauge
|
87
86
|
asn_val = [value].pack("N*")
|
88
|
-
asn_val = asn_val[1..-1] while asn_val[0] == "\x00".b && asn_val[1].
|
87
|
+
asn_val = asn_val[1..-1] while asn_val[0] == "\x00".b && asn_val[1].unpack1("B") != "1"
|
89
88
|
2
|
90
89
|
when :timetick
|
91
90
|
return Timetick.new(value).to_asn
|
@@ -114,21 +113,27 @@ module NETSNMP
|
|
114
113
|
IPAddr.new_ntoh(asn.value)
|
115
114
|
when 1, # ASN counter 32
|
116
115
|
2 # gauge
|
117
|
-
|
118
|
-
val.prepend("\x00") while val.bytesize < 4
|
119
|
-
val.unpack("N*")[0] || 0
|
116
|
+
unpack_32bit_integer(asn.value)
|
120
117
|
when 3 # timeticks
|
121
|
-
|
122
|
-
val.prepend("\x00") while val.bytesize < 4
|
123
|
-
Timetick.new(val.unpack("N*")[0] || 0)
|
118
|
+
Timetick.new(unpack_32bit_integer(asn.value))
|
124
119
|
# when 4 # opaque
|
125
120
|
# when 5 # NSAP
|
126
121
|
when 6 # ASN Counter 64
|
127
|
-
|
128
|
-
val.prepend("\x00") while val.bytesize % 16 != 0
|
129
|
-
val.unpack("NNNN").reduce(0) { |sum, elem| (sum << 32) + elem }
|
122
|
+
unpack_64bit_integer(asn.value)
|
130
123
|
# when 7 # ASN UInteger
|
131
124
|
end
|
132
125
|
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
def unpack_32bit_integer(payload)
|
130
|
+
payload.prepend("\x00") until (payload.bytesize % 4).zero?
|
131
|
+
payload.unpack("N*")[-1] || 0
|
132
|
+
end
|
133
|
+
|
134
|
+
def unpack_64bit_integer(payload)
|
135
|
+
payload.prepend("\x00") until (payload.bytesize % 16).zero?
|
136
|
+
payload.unpack("NNNN").reduce(0) { |sum, elem| (sum << 32) + elem }
|
137
|
+
end
|
133
138
|
end
|
134
139
|
end
|