From 41c8166d3583effd7015dac983fd7c608d3cac7f Mon Sep 17 00:00:00 2001 From: Brian Moran Date: Wed, 13 Jun 2012 15:08:46 -0700 Subject: [PATCH] use a nonblocking socket, wait for socket in a select with a timeout --- lib/net/ldap.rb | 42 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/lib/net/ldap.rb b/lib/net/ldap.rb index 6a959a76..15d1f512 100644 --- a/lib/net/ldap.rb +++ b/lib/net/ldap.rb @@ -383,6 +383,7 @@ def initialize(args = {}) @verbose = false # Make this configurable with a switch on the class. @auth = args[:auth] || DefaultAuth @base = args[:base] || DefaultTreebase + @timeout = args[:timeout] || 60 encryption args[:encryption] # may be nil if pr = @auth[:password] and pr.respond_to?(:call) @@ -562,7 +563,7 @@ def open @open_connection = Net::LDAP::Connection.new(:host => @host, :port => @port, :encryption => - @encryption) + @encryption, :timeout => @timeout) @open_connection.bind(@auth) yield self ensure @@ -634,7 +635,7 @@ def search(args = {}) else begin conn = Net::LDAP::Connection.new(:host => @host, :port => @port, - :encryption => @encryption) + :encryption => @encryption, :timeout => @timeout) if (@result = conn.bind(args[:auth] || @auth)).result_code == 0 @result = conn.search(args) { |entry| result_set << entry if result_set @@ -716,7 +717,7 @@ def bind(auth = @auth) else begin conn = Connection.new(:host => @host, :port => @port, - :encryption => @encryption) + :encryption => @encryption, :timeout => @timeout) @result = conn.bind(auth) ensure conn.close if conn @@ -817,7 +818,7 @@ def add(args) @result = 0 begin conn = Connection.new(:host => @host, :port => @port, - :encryption => @encryption) + :encryption => @encryption, :timeout => @timeout) if (@result = conn.bind(args[:auth] || @auth)).result_code == 0 @result = conn.add(args) end @@ -915,7 +916,7 @@ def modify(args) @result = 0 begin conn = Connection.new(:host => @host, :port => @port, - :encryption => @encryption) + :encryption => @encryption, :timeout => @timeout) if (@result = conn.bind(args[:auth] || @auth)).result_code == 0 @result = conn.modify(args) end @@ -987,7 +988,7 @@ def rename(args) @result = 0 begin conn = Connection.new(:host => @host, :port => @port, - :encryption => @encryption) + :encryption => @encryption, :timeout => @timeout) if (@result = conn.bind(args[:auth] || @auth)).result_code == 0 @result = conn.rename(args) end @@ -1015,7 +1016,7 @@ def delete(args) @result = 0 begin conn = Connection.new(:host => @host, :port => @port, - :encryption => @encryption) + :encryption => @encryption, :timeout => @timeout) if (@result = conn.bind(args[:auth] || @auth)).result_code == 0 @result = conn.delete(args) end @@ -1119,9 +1120,34 @@ class Net::LDAP::Connection #:nodoc: LdapVersion = 3 MaxSaslChallenges = 10 + def socket_connect_with_timeout(host, port, timeout=nil) + addr = Socket.getaddrinfo(host, nil) + sock = Socket.new(Socket.const_get(addr[0][0]), Socket::SOCK_STREAM, 0) + + if timeout + secs = Integer(timeout) + usecs = Integer((timeout - secs) * 1_000_000) + optval = [secs, usecs].pack("l_2") + + sock.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, optval + sock.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, optval + end + + begin + sock.connect_nonblock(Socket.pack_sockaddr_in(port, addr[0][3])) + rescue Errno::EINPROGRESS => ex + rzult = select([sock], [sock], [sock], timeout) + if !rzult + raise Errno::ETIMEDOUT, "Timeout #{timeout ? "(#{timeout} seconds)" : "(default)"} exceeded" + end + end + return sock + end + def initialize(server) begin - @conn = TCPSocket.new(server[:host], server[:port]) + @conn = socket_connect_with_timeout(server[:host], server[:port], server[:timeout]) + #@conn = TCPSocket.new(server[:host], server[:port]) rescue SocketError raise Net::LDAP::LdapError, "No such address or other socket error." rescue Errno::ECONNREFUSED