lib/net/ldap/connection.rb in net-ldap-0.14.0 vs lib/net/ldap/connection.rb in net-ldap-0.15.0
- old
+ new
@@ -29,30 +29,31 @@
def socket_class=(socket_class)
@socket_class = socket_class
end
- def prepare_socket(server)
+ def prepare_socket(server, timeout=nil)
socket = server[:socket]
encryption = server[:encryption]
@conn = socket
- setup_encryption encryption if encryption
+ setup_encryption(encryption, timeout) if encryption
end
def open_connection(server)
hosts = server[:hosts]
encryption = server[:encryption]
+ timeout = server[:connect_timeout] || DefaultConnectTimeout
socket_opts = {
- connect_timeout: server[:connect_timeout] || DefaultConnectTimeout,
+ connect_timeout: timeout,
}
errors = []
hosts.each do |host, port|
begin
- prepare_socket(server.merge(socket: @socket_class.new(host, port, socket_opts)))
+ prepare_socket(server.merge(socket: @socket_class.new(host, port, socket_opts)), timeout)
return
rescue Net::LDAP::Error, SocketError, SystemCallError,
OpenSSL::SSL::SSLError => e
# Ensure the connection is closed in the event a setup failure.
close
@@ -74,22 +75,41 @@
super
io.close
end
end
- def self.wrap_with_ssl(io, tls_options = {})
+ def self.wrap_with_ssl(io, tls_options = {}, timeout=nil)
raise Net::LDAP::NoOpenSSLError, "OpenSSL is unavailable" unless Net::LDAP::HasOpenSSL
ctx = OpenSSL::SSL::SSLContext.new
# By default, we do not verify certificates. For a 1.0 release, this should probably be changed at some point.
# See discussion in https://github.com/ruby-ldap/ruby-net-ldap/pull/161
ctx.set_params(tls_options) unless tls_options.empty?
conn = OpenSSL::SSL::SSLSocket.new(io, ctx)
- conn.connect
+ begin
+ if timeout
+ conn.connect_nonblock
+ else
+ conn.connect
+ end
+ rescue IO::WaitReadable
+ if IO.select([conn], nil, nil, timeout)
+ retry
+ else
+ raise Errno::ETIMEDOUT, "OpenSSL connection read timeout"
+ end
+ rescue IO::WaitWritable
+ if IO.select(nil, [conn], nil, timeout)
+ retry
+ else
+ raise Errno::ETIMEDOUT, "OpenSSL connection write timeout"
+ end
+ end
+
# Doesn't work:
# conn.sync_close = true
conn.extend(GetbyteForSSLSocket) unless conn.respond_to?(:getbyte)
conn.extend(FixSSLSocketSyncClose)
@@ -121,15 +141,15 @@
# The start_tls method is supported by many servers over the standard LDAP
# port. It does not require an alternative port for encrypted
# communications, as with simple_tls. Thanks for Kouhei Sutou for
# generously contributing the :start_tls path.
#++
- def setup_encryption(args)
+ def setup_encryption(args, timeout=nil)
args[:tls_options] ||= {}
case args[:method]
when :simple_tls
- @conn = self.class.wrap_with_ssl(@conn, args[:tls_options])
+ @conn = self.class.wrap_with_ssl(@conn, args[:tls_options], timeout)
# additional branches requiring server validation and peer certs, etc.
# go here.
when :start_tls
message_id = next_msgid
request = [
@@ -142,10 +162,10 @@
if pdu.nil? || pdu.app_tag != Net::LDAP::PDU::ExtendedResponse
raise Net::LDAP::NoStartTLSResultError, "no start_tls result"
end
if pdu.result_code.zero?
- @conn = self.class.wrap_with_ssl(@conn, args[:tls_options])
+ @conn = self.class.wrap_with_ssl(@conn, args[:tls_options], timeout)
else
raise Net::LDAP::StartTLSError, "start_tls failed: #{pdu.result_code}"
end
else
raise Net::LDAP::EncMethodUnsupportedError, "unsupported encryption method #{args[:method]}"