From 4c24cf239aa02d482d2ce0b1cb6f68354143fc00 Mon Sep 17 00:00:00 2001 From: Rory OConnell Date: Wed, 10 Feb 2010 12:13:59 -0600 Subject: [PATCH] Import fixes from kschiess --- Rakefile | 16 ++- lib/net/ber/ber_parser.rb | 289 +++++++++++++++++++------------------- lib/net/ldap/dataset.rb | 132 +++++++++-------- lib/net/ldap/psw.rb | 56 ++++---- test/test_ldif.rb | 2 +- test/test_snmp.rb | 2 +- 6 files changed, 249 insertions(+), 248 deletions(-) diff --git a/Rakefile b/Rakefile index 75c8182..068d773 100644 --- a/Rakefile +++ b/Rakefile @@ -7,12 +7,18 @@ require 'hoe' $LOAD_PATH.unshift( "#{File.dirname(__FILE__)}/lib" ) # Pull in local 'net/ldap' as opposed to an installed version. -require 'net/ldap' +require 'net' -Hoe.new('net-ldap', Net::LDAP::VERSION) do |p| - p.rubyforge_name = 'net-ldap' - p.developer('Francis Cianfrocca', 'garbagecat10@gmail.com') - p.developer('Emiel van de Laar', 'gemiel@gmail.com') +Hoe.spec "net-ldap" do + developer 'Francis Cianfrocca', 'garbagecat10@gmail.com' + developer 'Emiel van de Laar', 'gemiel@gmail.com' + developer "Rory O'Connell", 'rory.ocon@gmail.com' end +# Hoe.new('net-ldap', Net::LDAP::VERSION) do |p| +# p.rubyforge_name = 'net-ldap' +# p.developer('Francis Cianfrocca', 'garbagecat10@gmail.com') +# p.developer('Emiel van de Laar', 'gemiel@gmail.com') +# end + # vim: syntax=Ruby diff --git a/lib/net/ber/ber_parser.rb b/lib/net/ber/ber_parser.rb index 264cd66..f9e17fa 100644 --- a/lib/net/ber/ber_parser.rb +++ b/lib/net/ber/ber_parser.rb @@ -40,100 +40,97 @@ module Net # of this one. # def read_ber syntax=nil - # don't bother with this line, since IO#getc by definition returns nil on eof. - #return nil if eof? + # don't bother with this line, since IO#getc by definition returns nil on eof. + #return nil if eof? - # here we'll create two different procs, one for 1.8 and one for 1.9 - # the reason being getc doesn't return a byte value in 1.9, so we need to - # get the byte code out of the 1.9 encoded string + # here we'll create two different procs, one for 1.8 and one for 1.9 + # the reason being getc doesn't return a byte value in 1.9, so we need to + # get the byte code out of the 1.9 encoded string - if RUBY_VERSION =~ /^1\.9/ - fetch_byte = Proc.new { getc.bytes.first } + if RUBY_VERSION =~ /^1\.9/ + fetch_byte = Proc.new { getc.bytes.first } elsif RUBY_VERSION =~ /^1\.8/ - fetch_byte = Proc.new { getc } - end - - id = fetch_byte.call or return nil # don't trash this value, we'll use it later - #tag = id & 31 - #tag < 31 or raise BerError.new( "unsupported tag encoding: #{id}" ) - #tagclass = TagClasses[ id >> 6 ] - #encoding = (id & 0x20 != 0) ? :constructed : :primitive - - n = fetch_byte.call - lengthlength,contentlength = if n <= 127 - [1,n] - else - # Replaced the inject because it profiles hot. - #j = (0...(n & 127)).inject(0) {|mem,x| mem = (mem << 8) + getc} - j = 0 - read( n & 127 ).each_byte {|n1| j = (j << 8) + n1} - [1 + (n & 127), j] - end - - newobj = read contentlength - - # This exceptionally clever and clear bit of code is verrrry slow. - objtype = (syntax && syntax[id]) || BuiltinSyntax[id] - - - # == is expensive so sort this if/else so the common cases are at the top. - obj = if objtype == :string - #(newobj || "").dup - s = BerIdentifiedString.new( newobj || "" ) - s.ber_identifier = id - s - elsif objtype == :integer - j = 0 - newobj.each_byte {|b| j = (j << 8) + b} - j - elsif objtype == :oid - # cf X.690 pgh 8.19 for an explanation of this algorithm. - # Potentially not good enough. We may need a BerIdentifiedOid - # as a subclass of BerIdentifiedArray, to get the ber identifier - # and also a to_s method that produces the familiar dotted notation. - oid = newobj.unpack("w*") - f = oid.shift - g = if f < 40 - [0, f] - elsif f < 80 - [1, f-40] - else - [2, f-80] # f-80 can easily be > 80. What a weird optimization. + fetch_byte = Proc.new { getc } end - oid.unshift g.last - oid.unshift g.first - oid - elsif objtype == :array - #seq = [] - seq = BerIdentifiedArray.new - seq.ber_identifier = id - sio = StringIO.new( newobj || "" ) - # Interpret the subobject, but note how the loop - # is built: nil ends the loop, but false (a valid - # BER value) does not! - while (e = sio.read_ber(syntax)) != nil - seq << e - end - seq - elsif objtype == :boolean - newobj != "\000" - elsif objtype == :null - n = BerIdentifiedNull.new - n.ber_identifier = id - n - else - #raise BerError.new( "unsupported object type: class=#{tagclass}, encoding=#{encoding}, tag=#{tag}" ) - raise BerError.new( "unsupported object type: id=#{id}" ) - end - # Add the identifier bits into the object if it's a String or an Array. - # We can't add extra stuff to Fixnums and booleans, not that it makes much sense anyway. - # Replaced this mechanism with subclasses because the instance_eval profiled too hot. - #obj and ([String,Array].include? obj.class) and obj.instance_eval "def ber_identifier; #{id}; end" - #obj.ber_identifier = id if obj.respond_to?(:ber_identifier) - obj + id = fetch_byte.call or return nil # don't trash this value, we'll use it later + #tag = id & 31 + #tag < 31 or raise BerError.new( "unsupported tag encoding: #{id}" ) + #tagclass = TagClasses[ id >> 6 ] + #encoding = (id & 0x20 != 0) ? :constructed : :primitive - end + n = fetch_byte.call + lengthlength,contentlength = if n <= 127 + [1,n] + else + # Replaced the inject because it profiles hot. + #j = (0...(n & 127)).inject(0) {|mem,x| mem = (mem << 8) + getc} + j = 0 + read( n & 127 ).each_byte {|n1| j = (j << 8) + n1} + [1 + (n & 127), j] + end + + newobj = read contentlength + + # This exceptionally clever and clear bit of code is verrrry slow. + objtype = (syntax && syntax[id]) || BuiltinSyntax[id] + + # == is expensive so sort this if/else so the common cases are at the top. + obj = if objtype == :string + #(newobj || "").dup + s = BerIdentifiedString.new( newobj || "" ) + s.ber_identifier = id + s + elsif objtype == :integer + j = 0 + newobj.each_byte {|b| j = (j << 8) + b} + j + elsif objtype == :oid + # cf X.690 pgh 8.19 for an explanation of this algorithm. + # Potentially not good enough. We may need a BerIdentifiedOid + # as a subclass of BerIdentifiedArray, to get the ber identifier + # and also a to_s method that produces the familiar dotted notation. + oid = newobj.unpack("w*") + f = oid.shift + g = if f < 40 + [0, f] + elsif f < 80 + [1, f-40] + else + [2, f-80] # f-80 can easily be > 80. What a weird optimization. + end + oid.unshift g.last + oid.unshift g.first + oid + elsif objtype == :array + #seq = [] + seq = BerIdentifiedArray.new + seq.ber_identifier = id + sio = StringIO.new( newobj || "" ) + # Interpret the subobject, but note how the loop + # is built: nil ends the loop, but false (a valid + # BER value) does not! + while (e = sio.read_ber(syntax)) != nil + seq << e + end + seq + elsif objtype == :boolean + newobj != "\000" + elsif objtype == :null + n = BerIdentifiedNull.new + n.ber_identifier = id + n + else + #raise BerError.new( "unsupported object type: class=#{tagclass}, encoding=#{encoding}, tag=#{tag}" ) + raise BerError.new( "unsupported object type: id=#{id}" ) + end + # Add the identifier bits into the object if it's a String or an Array. + # We can't add extra stuff to Fixnums and booleans, not that it makes much sense anyway. + # Replaced this mechanism with subclasses because the instance_eval profiled too hot. + #obj and ([String,Array].include? obj.class) and obj.instance_eval "def ber_identifier; #{id}; end" + #obj.ber_identifier = id if obj.respond_to?(:ber_identifier) + obj + end #-- # Violates DRY! This replicates the functionality of #read_ber. @@ -145,24 +142,24 @@ module Net # # Observe that weirdly we recursively call the original #read_ber in here. # That needs to be fixed if we ever obsolete the original method in favor of this one. - def read_ber_from_string str, syntax=nil - id = str[0] or return nil - n = str[1] or return nil - n_consumed = 2 - lengthlength,contentlength = if n <= 127 - [1,n] - else - n1 = n & 127 - return nil unless str.length >= (n_consumed + n1) - j = 0 - n1.times { - j = (j << 8) + str[n_consumed] - n_consumed += 1 - } - [1 + (n1), j] - end - - return nil unless str.length >= (n_consumed + contentlength) + def read_ber_from_string str, syntax=nil + id = str[0].ord or return nil + n = str[1].ord or return nil + n_consumed = 2 + lengthlength,contentlength = if n <= 127 + [1,n] + else + n1 = n & 127 + return nil unless str.length >= (n_consumed + n1) + j = 0 + n1.times { + j = (j << 8) + str[n_consumed] + n_consumed += 1 + } + [1 + (n1), j] + end + + return nil unless str.length >= (n_consumed + contentlength) newobj = str[n_consumed...(n_consumed + contentlength)] n_consumed += contentlength @@ -170,52 +167,52 @@ module Net # == is expensive so sort this if/else so the common cases are at the top. obj = if objtype == :array - seq = BerIdentifiedArray.new - seq.ber_identifier = id - sio = StringIO.new( newobj || "" ) - # Interpret the subobject, but note how the loop - # is built: nil ends the loop, but false (a valid - # BER value) does not! - # Also, we can use the standard read_ber method because - # we know for sure we have enough data. (Although this - # might be faster than the standard method.) - while (e = sio.read_ber(syntax)) != nil - seq << e - end - seq + seq = BerIdentifiedArray.new + seq.ber_identifier = id + sio = StringIO.new( newobj || "" ) + # Interpret the subobject, but note how the loop + # is built: nil ends the loop, but false (a valid + # BER value) does not! + # Also, we can use the standard read_ber method because + # we know for sure we have enough data. (Although this + # might be faster than the standard method.) + while (e = sio.read_ber(syntax)) != nil + seq << e + end + seq elsif objtype == :string - s = BerIdentifiedString.new( newobj || "" ) - s.ber_identifier = id - s + s = BerIdentifiedString.new( newobj || "" ) + s.ber_identifier = id + s elsif objtype == :integer - j = 0 - newobj.each_byte {|b| j = (j << 8) + b} - j + j = 0 + newobj.each_byte {|b| j = (j << 8) + b} + j elsif objtype == :oid - # cf X.690 pgh 8.19 for an explanation of this algorithm. - # Potentially not good enough. We may need a BerIdentifiedOid - # as a subclass of BerIdentifiedArray, to get the ber identifier - # and also a to_s method that produces the familiar dotted notation. - oid = newobj.unpack("w*") - f = oid.shift - g = if f < 40 - [0,f] - elsif f < 80 - [1, f-40] - else - [2, f-80] # f-80 can easily be > 80. What a weird optimization. - end - oid.unshift g.last - oid.unshift g.first - oid + # cf X.690 pgh 8.19 for an explanation of this algorithm. + # Potentially not good enough. We may need a BerIdentifiedOid + # as a subclass of BerIdentifiedArray, to get the ber identifier + # and also a to_s method that produces the familiar dotted notation. + oid = newobj.unpack("w*") + f = oid.shift + g = if f < 40 + [0,f] + elsif f < 80 + [1, f-40] + else + [2, f-80] # f-80 can easily be > 80. What a weird optimization. + end + oid.unshift g.last + oid.unshift g.first + oid elsif objtype == :boolean - newobj != "\000" + newobj != "\000" elsif objtype == :null - n = BerIdentifiedNull.new - n.ber_identifier = id - n + n = BerIdentifiedNull.new + n.ber_identifier = id + n else - raise BerError.new( "unsupported object type: id=#{id}" ) + raise BerError.new( "unsupported object type: id=#{id}" ) end [obj, n_consumed] diff --git a/lib/net/ldap/dataset.rb b/lib/net/ldap/dataset.rb index 3fc60d0..3752e6f 100644 --- a/lib/net/ldap/dataset.rb +++ b/lib/net/ldap/dataset.rb @@ -29,80 +29,78 @@ module Net -class LDAP - -class Dataset < Hash - - attr_reader :comments - - - def Dataset::read_ldif io - ds = Dataset.new - - line = io.gets && chomp - dn = nil - - while line - io.gets and chomp - if $_ =~ /^[\s]+/ - line << " " << $' - else - nextline = $_ - - if line =~ /^\#/ - ds.comments << line - elsif line =~ /^dn:[\s]*/i - dn = $' - ds[dn] = Hash.new {|k,v| k[v] = []} - elsif line.length == 0 - dn = nil - elsif line =~ /^([^:]+):([\:]?)[\s]*/ - # $1 is the attribute name - # $2 is a colon iff the attr-value is base-64 encoded - # $' is the attr-value - # Avoid the Base64 class because not all Ruby versions have it. - attrvalue = ($2 == ":") ? $'.unpack('m').shift : $' - ds[dn][$1.downcase.intern] << attrvalue + class LDAP + class Dataset < Hash + attr_reader :comments + class IOFilter + def initialize(io) + @io = io + end + + def gets + s = @io.gets + s.chomp if s end - - line = nextline end - end + + def self.read_ldif io + ds = Dataset.new + + line = io.gets + dn = nil + + while line + io.gets and chomp + if new_line =~ /^[\s]+/ + line << " " << $' + else + nextline = new_line + + if line =~ /^\#/ + ds.comments << line + elsif line =~ /^dn:[\s]*/i + dn = $' + ds[dn] = Hash.new {|k,v| k[v] = []} + elsif line.length == 0 + dn = nil + elsif line =~ /^([^:]+):([\:]?)[\s]*/ + # $1 is the attribute name + # $2 is a colon iff the attr-value is base-64 encoded + # $' is the attr-value + # Avoid the Base64 class because not all Ruby versions have it. + attrvalue = ($2 == ":") ? $'.unpack('m').shift : $' + ds[dn][$1.downcase.intern] << attrvalue + end + + line = nextline + end + end - ds - end + ds + end - def initialize - @comments = [] - end + def initialize + @comments = [] + end - def to_ldif - ary = [] - ary += (@comments || []) + def to_ldif + ary = [] + ary += (@comments || []) + keys.sort.each do |dn| + ary << "dn: #{dn}" - keys.sort.each {|dn| - ary << "dn: #{dn}" - - self[dn].keys.map {|sym| sym.to_s}.sort.each {|attr| - self[dn][attr.intern].each {|val| - ary << "#{attr}: #{val}" - } - } - - ary << "" - } - - block_given? and ary.each {|line| yield line} - - ary - end - - -end # Dataset - -end # LDAP -end # Net + self[dn].keys.map {|sym| sym.to_s}.sort.each do |attr| + self[dn][attr.intern].each {|val| ary << "#{attr}: #{val}" } + end + ary << "" + end + block_given? and ary.each {|line| yield line} + ary + end + end + end +end \ No newline at end of file diff --git a/lib/net/ldap/psw.rb b/lib/net/ldap/psw.rb index 01c4dbb..46095c2 100644 --- a/lib/net/ldap/psw.rb +++ b/lib/net/ldap/psw.rb @@ -27,36 +27,36 @@ module Net -class LDAP + class LDAP + class Password + class << self - -class Password - class << self - - # Generate a password-hash suitable for inclusion in an LDAP attribute. - # Pass a hash type (currently supported: :md5 and :sha) and a plaintext - # password. This function will return a hashed representation. - # STUB: This is here to fulfill the requirements of an RFC, which one? - # TODO, gotta do salted-sha and (maybe) salted-md5. - # Should we provide sha1 as a synonym for sha1? I vote no because then - # should you also provide ssha1 for symmetry? - def generate( type, str ) - case type - when :md5 - "{MD5}#{ [MD5.new( str.to_s ).digest].pack("m").chomp }" - when :sha - "{SHA}#{ [SHA1.new( str.to_s ).digest].pack("m").chomp }" - # when ssha - else - raise Net::LDAP::LdapError.new( "unsupported password-hash type (#{type})" ) + # Generate a password-hash suitable for inclusion in an LDAP attribute. + # Pass a hash type (currently supported: :md5 and :sha) and a plaintext + # password. This function will return a hashed representation. + # STUB: This is here to fulfill the requirements of an RFC, which one? + # TODO, gotta do salted-sha and (maybe) salted-md5. + # Should we provide sha1 as a synonym for sha1? I vote no because then + # should you also provide ssha1 for symmetry? + def generate( type, str ) + digest, digest_name = case type + when :md5 + [Digest::MD5.new, 'MD5'] + when :sha + [Digest::SHA1.new, 'sha'] + # when ssha + else + raise Net::LDAP::LdapError.new( "unsupported password-hash type (#{type})" ) + end + + digest << str.to_s + return "{#{digest_name}}#{[digest.digest].pack('m').chomp }" + end + + + end end - end - - end + end end -end # class LDAP -end # module Net - - diff --git a/test/test_ldif.rb b/test/test_ldif.rb index 9ecf697..2f7340f 100644 --- a/test/test_ldif.rb +++ b/test/test_ldif.rb @@ -11,7 +11,7 @@ class TestLdif < Test::Unit::TestCase TestLdifFilename = "#{File.dirname(__FILE__)}/testdata.ldif" def test_empty_ldif - ds = Net::LDAP::Dataset::read_ldif( StringIO.new ) + ds = Net::LDAP::Dataset.read_ldif( StringIO.new ) assert_equal( true, ds.empty? ) end diff --git a/test/test_snmp.rb b/test/test_snmp.rb index 249afb6..b233b4d 100644 --- a/test/test_snmp.rb +++ b/test/test_snmp.rb @@ -32,7 +32,7 @@ class TestSnmp < Test::Unit::TestCase # partially-received data streams, such as from network connections. def test_consume_string data = "xxx" - assert_equal( nil, data.read_ber! ) + assert_equal( data.read_ber!, nil ) assert_equal( "xxx", data ) data = SnmpGetRequest + "!!!"