From 3b8cd7b082e712852c6b8940c8d1370357fd2342 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Sun, 30 Nov 2014 20:33:32 -0800 Subject: [PATCH 1/7] Extract read_ber_id --- lib/net/ber/ber_parser.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/net/ber/ber_parser.rb b/lib/net/ber/ber_parser.rb index ea6c0788..8f3e1d0d 100644 --- a/lib/net/ber/ber_parser.rb +++ b/lib/net/ber/ber_parser.rb @@ -157,7 +157,7 @@ def read_ber(syntax = nil) # from streams that don't block when we ask for more data (like # StringIOs). At it is, this can throw TypeErrors and other nasties. - id = getbyte or return nil # don't trash this value, we'll use it later + id = read_ber_id or return nil # don't trash this value, we'll use it later content_length = read_ber_length yield id, content_length if block_given? @@ -170,4 +170,9 @@ def read_ber(syntax = nil) parse_ber_object(syntax, id, data) end + + # Internal: Returns the BER message ID or nil. + def read_ber_id + getbyte + end end From b711edf4c9a4a4689d9e8aa5e3084dde5fe7be17 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Sun, 30 Nov 2014 20:33:51 -0800 Subject: [PATCH 2/7] Implement read_ber_id with nonblocking read --- lib/net/ber/ber_parser.rb | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/net/ber/ber_parser.rb b/lib/net/ber/ber_parser.rb index 8f3e1d0d..aad5a03d 100644 --- a/lib/net/ber/ber_parser.rb +++ b/lib/net/ber/ber_parser.rb @@ -25,6 +25,9 @@ module Net::BER::BERParser BuiltinSyntax = Net::BER.compile_syntax(:universal => universal, :context_specific => context) + # Public: specify the BER socket read timeouts, nil by default (no timeout). + attr_accessor :read_ber_timeout + ## # This is an extract of our BER object parsing to simplify our # understanding of how we parse basic BER object types. @@ -173,6 +176,17 @@ def read_ber(syntax = nil) # Internal: Returns the BER message ID or nil. def read_ber_id - getbyte + begin + read_nonblock(1).ord + rescue IO::WaitReadable + if IO.select([self], nil, nil, read_ber_timeout) + read_nonblock(1).ord + else + raise Net::LDAP::LdapError, "Timed out reading from the socket" + end + end + rescue EOFError + # nothing to read on the socket (StringIO) + nil end end From a5c30dd30aece903ce92d92f8cb718061653fd90 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Sun, 30 Nov 2014 20:39:03 -0800 Subject: [PATCH 3/7] Extract getbyte_nonblock, use with read_ber_length --- lib/net/ber/ber_parser.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/net/ber/ber_parser.rb b/lib/net/ber/ber_parser.rb index aad5a03d..83716d50 100644 --- a/lib/net/ber/ber_parser.rb +++ b/lib/net/ber/ber_parser.rb @@ -127,7 +127,7 @@ def parse_ber_object(syntax, id, data) # invalid BER length case. Because the "lengthlength" value was not used # inside of #read_ber, we no longer return it. def read_ber_length - n = getbyte + n = getbyte_nonblock if n <= 0x7f n @@ -176,6 +176,11 @@ def read_ber(syntax = nil) # Internal: Returns the BER message ID or nil. def read_ber_id + getbyte_nonblock + end + + # Internal: Replaces `getbyte` with nonblocking implementation. + def getbyte_nonblock begin read_nonblock(1).ord rescue IO::WaitReadable From 15bf5948ca97da3b79717eda37352b002db30ad6 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Sun, 30 Nov 2014 20:45:18 -0800 Subject: [PATCH 4/7] Read length nonblocking --- lib/net/ber/ber_parser.rb | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/net/ber/ber_parser.rb b/lib/net/ber/ber_parser.rb index 83716d50..79083faa 100644 --- a/lib/net/ber/ber_parser.rb +++ b/lib/net/ber/ber_parser.rb @@ -137,7 +137,20 @@ def read_ber_length raise Net::BER::BerError, "Invalid BER length 0xFF detected." else v = 0 - read(n & 0x7f).each_byte do |b| + len = n & 0x7f + + buffer = + begin + read_nonblock(len) + rescue IO::WaitReadable + if IO.select([self], nil, nil, read_ber_timeout) + read_nonblock(len) + else + raise Net::LDAP::LdapError, "Timed out reading from the socket" + end + end + + buffer.each_byte do |b| v = (v << 8) + b end From 65595871f112fc1f6db3c0effd37c5a56fb8f80e Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Sun, 30 Nov 2014 20:45:27 -0800 Subject: [PATCH 5/7] Make internal methods private --- lib/net/ber/ber_parser.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/net/ber/ber_parser.rb b/lib/net/ber/ber_parser.rb index 79083faa..f6dee3f1 100644 --- a/lib/net/ber/ber_parser.rb +++ b/lib/net/ber/ber_parser.rb @@ -191,6 +191,7 @@ def read_ber(syntax = nil) def read_ber_id getbyte_nonblock end + private :read_ber_id # Internal: Replaces `getbyte` with nonblocking implementation. def getbyte_nonblock @@ -207,4 +208,5 @@ def getbyte_nonblock # nothing to read on the socket (StringIO) nil end + private :getbyte_nonblock end From 3366bdaf957b5a01260b304aa612e5334b14e84c Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Sun, 30 Nov 2014 21:53:38 -0800 Subject: [PATCH 6/7] Extract read_ber_nonblock, use for all reads --- lib/net/ber/ber_parser.rb | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/net/ber/ber_parser.rb b/lib/net/ber/ber_parser.rb index f6dee3f1..3c5af2e9 100644 --- a/lib/net/ber/ber_parser.rb +++ b/lib/net/ber/ber_parser.rb @@ -137,23 +137,9 @@ def read_ber_length raise Net::BER::BerError, "Invalid BER length 0xFF detected." else v = 0 - len = n & 0x7f - - buffer = - begin - read_nonblock(len) - rescue IO::WaitReadable - if IO.select([self], nil, nil, read_ber_timeout) - read_nonblock(len) - else - raise Net::LDAP::LdapError, "Timed out reading from the socket" - end - end - - buffer.each_byte do |b| + read_ber_nonblock(n & 0x7f).each_byte do |b| v = (v << 8) + b end - v end end @@ -181,7 +167,7 @@ def read_ber(syntax = nil) if -1 == content_length raise Net::BER::BerError, "Indeterminite BER content length not implemented." else - data = read(content_length) + data = read_ber_nonblock(content_length) end parse_ber_object(syntax, id, data) @@ -209,4 +195,18 @@ def getbyte_nonblock nil end private :getbyte_nonblock + + # Internal: Read `len` bytes, respecting timeout. + def read_ber_nonblock(len) + begin + read_nonblock(len) + rescue IO::WaitReadable + if IO.select([self], nil, nil, read_ber_timeout) + read_nonblock(len) + else + raise Net::LDAP::LdapError, "Timed out reading from the socket" + end + end + end + private :read_ber_nonblock end From 03e51554738fbf27b95865732bbc446e6f8f6315 Mon Sep 17 00:00:00 2001 From: Matt Todd Date: Sun, 30 Nov 2014 22:16:58 -0800 Subject: [PATCH 7/7] Configure socket read timeouts --- lib/net/ldap/connection.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/net/ldap/connection.rb b/lib/net/ldap/connection.rb index 6371f636..9942308f 100644 --- a/lib/net/ldap/connection.rb +++ b/lib/net/ldap/connection.rb @@ -21,6 +21,10 @@ def initialize(server) raise Net::LDAP::LdapError, "Connection to #{server[:host]} timed out." end + if server[:timeout] + @conn.read_ber_timeout = server[:timeout] + end + if server[:encryption] setup_encryption server[:encryption] end