Skip to content

Commit 4db9f2c

Browse files
author
blackhedd
committed
Added a more-disciplined version of #read_ber.
1 parent 8323341 commit 4db9f2c

File tree

1 file changed

+73
-34
lines changed

1 file changed

+73
-34
lines changed

lib/net/ber.rb

Lines changed: 73 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,10 @@ module BERParser
102102
# packets coming from streams that don't block when
103103
# we ask for more data (like StringIOs). At it is,
104104
# this can throw TypeErrors and other nasties.
105+
#--
106+
# BEWARE, this violates DRY and is largely equal in functionality to
107+
# read_ber_from_string. Eventually that method may subsume the functionality
108+
# of this one.
105109
#
106110
def read_ber syntax=nil
107111
# don't bother with this line, since IO#getc by definition returns nil on eof.
@@ -127,42 +131,8 @@ def read_ber syntax=nil
127131
newobj = read contentlength
128132

129133
# This exceptionally clever and clear bit of code is verrrry slow.
130-
=begin
131-
objtype = nil
132-
[syntax, BuiltinSyntax].each {|syn|
133-
if syn && (ot = syn[tagclass]) && (ot = ot[encoding]) && ot[tag]
134-
objtype = ot[tag]
135-
break
136-
end
137-
}
138-
=end
139134
objtype = (syntax && syntax[id]) || BuiltinSyntax[id]
140135

141-
=begin
142-
Replaced this case with if/else because Symbol#=== profiled surprisingly hot.
143-
obj = case objtype
144-
when :boolean
145-
newobj != "\000"
146-
when :string
147-
(newobj || "").dup
148-
when :integer
149-
j = 0
150-
newobj.each_byte {|b| j = (j << 8) + b}
151-
j
152-
when :array
153-
seq = []
154-
sio = StringIO.new( newobj || "" )
155-
# Interpret the subobject, but note how the loop
156-
# is built: nil ends the loop, but false (a valid
157-
# BER value) does not!
158-
while (e = sio.read_ber(syntax)) != nil
159-
seq << e
160-
end
161-
seq
162-
else
163-
raise BerError.new( "unsupported object type: class=#{tagclass}, encoding=#{encoding}, tag=#{tag}" )
164-
end
165-
=end
166136

167137
# == is expensive so sort this if/else so the common cases are at the top.
168138
obj = if objtype == :string
@@ -206,6 +176,75 @@ def read_ber syntax=nil
206176

207177
end
208178

179+
#--
180+
# Violates DRY! This replicates the functionality of #read_ber.
181+
# Eventually this method may replace that one.
182+
# This version of #read_ber behaves properly in the face of incomplete
183+
# data packets. If a full BER object is detected, we return an array containing
184+
# the detected object and the number of bytes consumed from the string.
185+
# If we don't detect a complete packet, return nil.
186+
#
187+
# Observe that weirdly we recursively call the original #read_ber in here.
188+
# That needs to be fixed if we ever obsolete the original method in favor of this one.
189+
def read_ber_from_string str, syntax=nil
190+
id = str[0] or return nil
191+
n = str[1] or return nil
192+
n_consumed = 2
193+
lengthlength,contentlength = if n <= 127
194+
[1,n]
195+
else
196+
n1 = n & 127
197+
return nil unless str.length >= (n_consumed + n1)
198+
j = 0
199+
n1.times {
200+
j = (j << 8) + str[n_consumed]
201+
n_consumed += 1
202+
}
203+
[1 + (n1), j]
204+
end
205+
206+
return nil unless str.length >= (n_consumed + contentlength)
207+
newobj = str[n_consumed...(n_consumed + contentlength)]
208+
n_consumed += contentlength
209+
210+
objtype = (syntax && syntax[id]) || BuiltinSyntax[id]
211+
212+
# == is expensive so sort this if/else so the common cases are at the top.
213+
obj = if objtype == :array
214+
seq = BerIdentifiedArray.new
215+
seq.ber_identifier = id
216+
sio = StringIO.new( newobj || "" )
217+
# Interpret the subobject, but note how the loop
218+
# is built: nil ends the loop, but false (a valid
219+
# BER value) does not!
220+
# Also, we can use the standard read_ber method because
221+
# we know for sure we have enough data. (Although this
222+
# might be faster than the standard method.)
223+
while (e = sio.read_ber(syntax)) != nil
224+
seq << e
225+
end
226+
seq
227+
elsif objtype == :string
228+
s = BerIdentifiedString.new( newobj || "" )
229+
s.ber_identifier = id
230+
s
231+
elsif objtype == :integer
232+
j = 0
233+
newobj.each_byte {|b| j = (j << 8) + b}
234+
j
235+
elsif objtype == :boolean
236+
newobj != "\000"
237+
elsif objtype == :null
238+
n = BerIdentifiedNull.new
239+
n.ber_identifier = id
240+
n
241+
else
242+
raise BerError.new( "unsupported object type: id=#{id}" )
243+
end
244+
245+
[obj, n_consumed]
246+
end
247+
209248
end # module BERParser
210249
end # module BER
211250

0 commit comments

Comments
 (0)