diff --git a/Manifest.txt b/Manifest.txt new file mode 100644 index 0000000..e69de29 diff --git a/Rakefile b/Rakefile index 068d773..c8ecc35 100644 --- a/Rakefile +++ b/Rakefile @@ -1,24 +1,69 @@ -# -*- ruby -*- +require "rubygems" +require "rake/gempackagetask" +require "rake/rdoctask" -require 'rubygems' -require 'hoe' - -# Add 'lib' to load path. -$LOAD_PATH.unshift( "#{File.dirname(__FILE__)}/lib" ) - -# Pull in local 'net/ldap' as opposed to an installed version. -require 'net' - -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' +require "rake/testtask" +Rake::TestTask.new do |t| + t.libs << "test" + t.test_files = FileList["test/test_*.rb"] + t.verbose = true 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 +require 'spec/rake/spectask' +Spec::Rake::SpecTask.new -# vim: syntax=Ruby +task :default => ["test", 'spec'] + +# This builds the actual gem. For details of what all these options +# mean, and other ones you can add, check the documentation here: +# +# http://rubygems.org/read/chapter/20 +# +spec = Gem::Specification.new do |s| + + # Change these as appropriate + s.name = "net-ldap" + s.version = "0.1.0" + s.summary = "What this thing does" + s.author = "Francis Cianfrocca", 'garbagecat10@gmail.com' + s.author = 'Emiel van de Laar', 'gemiel@gmail.com' + s.author = "Rory O'Connell", 'rory.ocon@gmail.com' + s.author = "Kaspar Schiess", 'kaspar.schiess@absurd.li' + + s.description = "Pure Ruby LDAP library" + + # s.has_rdoc = true + # s.extra_rdoc_files = %w(README.txt) + # s.rdoc_options = %w(--main README.txt) + + # Add any extra files to include in the gem + s.files = %w(COPYING History.txt LICENSE Manifest.txt pre-setup.rb Rakefile README.txt Release-Announcement setup.rb) + Dir.glob("{test,lib/**/*}") + s.require_paths = ["lib"] +end + +# This task actually builds the gem. We also regenerate a static +# .gemspec file, which is useful if something (i.e. GitHub) will +# be automatically building a gem for this project. If you're not +# using GitHub, edit as appropriate. +# +# To publish your gem online, install the 'gemcutter' gem; Read more +# about that here: http://gemcutter.org/pages/gem_docs +Rake::GemPackageTask.new(spec) do |pkg| + pkg.gem_spec = spec + + # Generate the gemspec file for github. + file = File.dirname(__FILE__) + "/#{spec.name}.gemspec" + File.open(file, "w") {|f| f << spec.to_ruby } +end + +# Generate documentation +Rake::RDocTask.new do |rd| + rd.main = "README.txt" + rd.rdoc_files.include("README.txt", "lib/**/*.rb") + rd.rdoc_dir = "rdoc" +end + +desc 'Clear out RDoc and generated packages' +task :clean => [:clobber_rdoc, :clobber_package] do + rm "#{spec.name}.gemspec" +end diff --git a/lib/net/ber.rb b/lib/net/ber.rb index 4ecf3fa..8fe8caf 100644 --- a/lib/net/ber.rb +++ b/lib/net/ber.rb @@ -1,5 +1,3 @@ -# $Id$ -# # NET::BER # Mixes ASN.1/BER convenience methods into several standard classes. # Also provides BER parsing functionality. @@ -25,8 +23,6 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # #--------------------------------------------------------------------------- -# -# module Net module BER @@ -105,3 +101,4 @@ module Net end end +require 'net/ber/ber_parser' \ No newline at end of file diff --git a/lib/net/ber/ber_parser.rb b/lib/net/ber/ber_parser.rb index f9e17fa..241ddca 100644 --- a/lib/net/ber/ber_parser.rb +++ b/lib/net/ber/ber_parser.rb @@ -28,49 +28,35 @@ module Net } }) - # - # read_ber - # TODO: clean this up so it works properly with partial - # packets coming from streams that don't block when - # we ask for more data (like StringIOs). At it is, - # this can throw TypeErrors and other nasties. - #-- - # BEWARE, this violates DRY and is largely equal in functionality to - # read_ber_from_string. Eventually that method may subsume the functionality - # 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? - - # 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 } - elsif RUBY_VERSION =~ /^1\.8/ - fetch_byte = Proc.new { getc } + # TODO: clean this up so it works properly with partial + # packets coming from streams that don't block when + # we ask for more data (like StringIOs). At it is, + # this can throw TypeErrors and other nasties. + + # We might have been included in two kinds of things: IO-like + # (answering to getbyte) and String-like (answering to to_s). Have + # stream be a stream that is IO-like in both cases. + if respond_to? :getbyte + stream = self + else + stream = StringIO.new(self.to_s) end + + id = stream.getbyte or return nil # don't trash this value, we'll use it later - 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 + n = stream.getbyte 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...(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 + newobj = stream.read contentlength # This exceptionally clever and clear bit of code is verrrry slow. objtype = (syntax && syntax[id]) || BuiltinSyntax[id] @@ -121,102 +107,11 @@ module Net 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. - # Eventually this method may replace that one. - # This version of #read_ber behaves properly in the face of incomplete - # data packets. If a full BER object is detected, we return an array containing - # the detected object and the number of bytes consumed from the string. - # If we don't detect a complete packet, return nil. - # - # 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].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 - - objtype = (syntax && syntax[id]) || BuiltinSyntax[id] - - # == 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 - elsif objtype == :string - 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 == :boolean - newobj != "\000" - elsif objtype == :null - n = BerIdentifiedNull.new - n.ber_identifier = id - n - else - raise BerError.new( "unsupported object type: id=#{id}" ) - end - - [obj, n_consumed] - end end end end \ No newline at end of file diff --git a/lib/net/ldap.rb b/lib/net/ldap.rb index 16d591e..01e2249 100644 --- a/lib/net/ldap.rb +++ b/lib/net/ldap.rb @@ -1,25 +1,12 @@ -# $Id$ -# -# Net::LDAP for Ruby -# -# -# Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved. -# -# Written and maintained by Francis Cianfrocca, gmail: garbagecat10. -# -# This program is free software. -# You may re-distribute and/or modify this program under the same terms -# as Ruby itself: Ruby Distribution License or GNU General Public License.x -# -# -# See Net::LDAP for documentation and usage samples. -# +require 'openssl' +require 'net/ber' require 'net/ldap/pdu' require 'net/ldap/filter' require 'net/ldap/dataset' require 'net/ldap/psw' require 'net/ldap/entry' +require 'net/ldap/core_ext/all' module Net @@ -258,7 +245,7 @@ module Net SearchScope_WholeSubtree = 2 SearchScopes = [SearchScope_BaseObject, SearchScope_SingleLevel, SearchScope_WholeSubtree] - AsnSyntax = BER.compile_syntax({ + AsnSyntax = Net::BER.compile_syntax({ :application => { :primitive => { 2 => :null # UnbindRequest body diff --git a/lib/net/ldap/core_ext/string.rb b/lib/net/ldap/core_ext/string.rb index f1c0b77..64a7cbc 100644 --- a/lib/net/ldap/core_ext/string.rb +++ b/lib/net/ldap/core_ext/string.rb @@ -32,16 +32,15 @@ module Net StringIO.new(self).read_ber(syntax) end - def read_ber! syntax=nil - obj,n_consumed = read_ber_from_string(self, syntax) - if n_consumed - self.slice!(0...n_consumed) - obj - else - nil - end - end - + # def read_ber! syntax=nil + # obj,n_consumed = read_ber_from_string(self, syntax) + # if n_consumed + # self.slice!(0...n_consumed) + # obj + # else + # nil + # end + # end end end end diff --git a/lib/net/ldap/dataset.rb b/lib/net/ldap/dataset.rb index 3752e6f..2459b72 100644 --- a/lib/net/ldap/dataset.rb +++ b/lib/net/ldap/dataset.rb @@ -32,25 +32,26 @@ module Net 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 end - + def self.read_ldif io ds = Dataset.new + io = IOFilter.new(io) line = io.gets dn = nil while line - io.gets and chomp + new_line = io.gets if new_line =~ /^[\s]+/ line << " " << $' else @@ -75,7 +76,7 @@ module Net line = nextline end end - + ds end diff --git a/net-ldap.gemspec b/net-ldap.gemspec new file mode 100644 index 0000000..a3f2c74 --- /dev/null +++ b/net-ldap.gemspec @@ -0,0 +1,25 @@ +# -*- encoding: utf-8 -*- + +Gem::Specification.new do |s| + s.name = %q{net-ldap} + s.version = "0.1.0" + + s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= + s.authors = [["Kaspar Schiess", "kaspar.schiess@absurd.li"]] + s.date = %q{2010-02-12} + s.description = %q{Pure Ruby LDAP library} + s.files = ["COPYING", "History.txt", "LICENSE", "Manifest.txt", "pre-setup.rb", "Rakefile", "README.txt", "Release-Announcement", "setup.rb", "test", "lib/net", "lib/net/ber", "lib/net/ber/ber_parser.rb", "lib/net/ber.rb", "lib/net/ldap", "lib/net/ldap/core_ext", "lib/net/ldap/core_ext/all.rb", "lib/net/ldap/core_ext/array.rb", "lib/net/ldap/core_ext/bignum.rb", "lib/net/ldap/core_ext/false_class.rb", "lib/net/ldap/core_ext/fixnum.rb", "lib/net/ldap/core_ext/string.rb", "lib/net/ldap/core_ext/true_class.rb", "lib/net/ldap/dataset.rb", "lib/net/ldap/entry.rb", "lib/net/ldap/filter.rb", "lib/net/ldap/pdu.rb", "lib/net/ldap/psw.rb", "lib/net/ldap.rb", "lib/net/ldif.rb", "lib/net/snmp.rb", "lib/net.rb"] + s.require_paths = ["lib"] + s.rubygems_version = %q{1.3.5} + s.summary = %q{What this thing does} + + if s.respond_to? :specification_version then + current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION + s.specification_version = 3 + + if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then + else + end + else + end +end diff --git a/test/test_snmp.rb b/test/test_snmp.rb index b233b4d..f41c20b 100644 --- a/test/test_snmp.rb +++ b/test/test_snmp.rb @@ -18,111 +18,94 @@ class TestSnmp < Test::Unit::TestCase end def test_invalid_packet - data = "xxxx" - assert_raise( Net::BER::BerError ) { - ary = data.read_ber(Net::SNMP::AsnSyntax) - } + data = "xxxx" + assert_raise( Net::BER::BerError ) { +ary = data.read_ber(Net::SNMP::AsnSyntax) + } end - # The method String#read_ber! added by Net::BER consumes a well-formed BER object - # from the head of a string. If it doesn't find a complete, well-formed BER object, - # it returns nil and leaves the string unchanged. If it finds an object, it returns - # the object and removes it from the head of the string. This is good for handling - # partially-received data streams, such as from network connections. - def test_consume_string - data = "xxx" - assert_equal( data.read_ber!, nil ) - assert_equal( "xxx", data ) - - data = SnmpGetRequest + "!!!" - ary = data.read_ber!( Net::SNMP::AsnSyntax ) - assert_equal( "!!!", data ) - assert ary.is_a?(Array) - assert ary.is_a?(Net::BER::BerIdentifiedArray) - end - def test_weird_packet - assert_raise( Net::SnmpPdu::Error ) { - Net::SnmpPdu.parse("aaaaaaaaaaaaaa") - } + assert_raise( Net::SnmpPdu::Error ) { +Net::SnmpPdu.parse("aaaaaaaaaaaaaa") + } end def test_get_request - data = SnmpGetRequest.dup - pkt = data.read_ber(Net::SNMP::AsnSyntax) - assert pkt.is_a?(Net::BER::BerIdentifiedArray) - assert_equal( 48, pkt.ber_identifier) # Constructed [0], signifies GetRequest + data = SnmpGetRequest.dup + pkt = data.read_ber(Net::SNMP::AsnSyntax) + assert pkt.is_a?(Net::BER::BerIdentifiedArray) + assert_equal( 48, pkt.ber_identifier) # Constructed [0], signifies GetRequest - pdu = Net::SnmpPdu.parse(pkt) - assert_equal(:get_request, pdu.pdu_type ) - assert_equal(16170, pdu.request_id ) # whatever was in the test data. 16170 is not magic. - assert_equal( [[[1,3,6,1,2,1,1,1,0],nil]], pdu.variables ) + pdu = Net::SnmpPdu.parse(pkt) + assert_equal(:get_request, pdu.pdu_type ) + assert_equal(16170, pdu.request_id ) # whatever was in the test data. 16170 is not magic. + assert_equal( [[[1,3,6,1,2,1,1,1,0],nil]], pdu.variables ) - assert_equal( pdu.to_ber_string, SnmpGetRequest ) + assert_equal( pdu.to_ber_string, SnmpGetRequest ) end def test_empty_pdu - pdu = Net::SnmpPdu.new - assert_raise( Net::SnmpPdu::Error ) { - pdu.to_ber_string - } + pdu = Net::SnmpPdu.new + assert_raise( Net::SnmpPdu::Error ) { +pdu.to_ber_string + } end def test_malformations - pdu = Net::SnmpPdu.new - pdu.version = 0 - pdu.version = 2 - assert_raise( Net::SnmpPdu::Error ) { - pdu.version = 100 - } + pdu = Net::SnmpPdu.new + pdu.version = 0 + pdu.version = 2 + assert_raise( Net::SnmpPdu::Error ) { + pdu.version = 100 + } - pdu.pdu_type = :get_request - pdu.pdu_type = :get_next_request - pdu.pdu_type = :get_response - pdu.pdu_type = :set_request - pdu.pdu_type = :trap - assert_raise( Net::SnmpPdu::Error ) { - pdu.pdu_type = :something_else - } + pdu.pdu_type = :get_request + pdu.pdu_type = :get_next_request + pdu.pdu_type = :get_response + pdu.pdu_type = :set_request + pdu.pdu_type = :trap + assert_raise( Net::SnmpPdu::Error ) { + pdu.pdu_type = :something_else + } end def test_make_response - pdu = Net::SnmpPdu.new - pdu.version = 0 - pdu.community = "public" - pdu.pdu_type = :get_response - pdu.request_id = 9999 - pdu.error_status = 0 - pdu.error_index = 0 - pdu.add_variable_binding [1,3,6,1,2,1,1,1,0], "test" + pdu = Net::SnmpPdu.new + pdu.version = 0 + pdu.community = "public" + pdu.pdu_type = :get_response + pdu.request_id = 9999 + pdu.error_status = 0 + pdu.error_index = 0 + pdu.add_variable_binding [1,3,6,1,2,1,1,1,0], "test" - assert_equal( SnmpGetResponse, pdu.to_ber_string ) + assert_equal( SnmpGetResponse, pdu.to_ber_string ) end def test_make_bad_response - pdu = Net::SnmpPdu.new - assert_raise(Net::SnmpPdu::Error) {pdu.to_ber_string} - pdu.pdu_type = :get_response - pdu.request_id = 999 - pdu.to_ber_string - # Not specifying variables doesn't create an error. (Maybe it should?) + pdu = Net::SnmpPdu.new + assert_raise(Net::SnmpPdu::Error) {pdu.to_ber_string} + pdu.pdu_type = :get_response + pdu.request_id = 999 + pdu.to_ber_string + # Not specifying variables doesn't create an error. (Maybe it should?) end def test_snmp_integers - c32 = Net::SNMP::Counter32.new(100) - assert_equal( "A\001d", c32.to_ber ) - g32 = Net::SNMP::Gauge32.new(100) - assert_equal( "B\001d", g32.to_ber ) - t32 = Net::SNMP::TimeTicks32.new(100) - assert_equal( "C\001d", t32.to_ber ) + c32 = Net::SNMP::Counter32.new(100) + assert_equal( "A\001d", c32.to_ber ) + g32 = Net::SNMP::Gauge32.new(100) + assert_equal( "B\001d", g32.to_ber ) + t32 = Net::SNMP::TimeTicks32.new(100) + assert_equal( "C\001d", t32.to_ber ) end def test_community - data = SnmpGetRequestXXX.dup - ary = data.read_ber(Net::SNMP::AsnSyntax) - pdu = Net::SnmpPdu.parse( ary ) - assert_equal( "xxxxxx", pdu.community ) + data = SnmpGetRequestXXX.dup + ary = data.read_ber(Net::SNMP::AsnSyntax) + pdu = Net::SnmpPdu.parse( ary ) + assert_equal( "xxxxxx", pdu.community ) end end diff --git a/testserver/ldapserver.rb b/testserver/ldapserver.rb index 60b8572..d05f03f 100644 --- a/testserver/ldapserver.rb +++ b/testserver/ldapserver.rb @@ -17,22 +17,6 @@ require 'stringio' #------------------------------------------------ -class String - def read_ber! syntax=nil - s = StringIO.new self - pdu = s.read_ber(syntax) - if pdu - if s.eof? - slice!(0, length) - else - slice!(0, length - s.read.length) - end - end - pdu - end -end - - module LdapServer LdapServerAsnSyntax = {