Skip to content

Commit dd9409b

Browse files
authored
Merge pull request #273 from tmm1/ssl-connect-timeout
Respect connect_timeout when establishing SSL connections
2 parents 3bf849d + 749c22b commit dd9409b

File tree

2 files changed

+30
-10
lines changed

2 files changed

+30
-10
lines changed

lib/net/ldap/connection.rb

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,26 +31,27 @@ def socket_class=(socket_class)
3131
@socket_class = socket_class
3232
end
3333

34-
def prepare_socket(server)
34+
def prepare_socket(server, timeout=nil)
3535
socket = server[:socket]
3636
encryption = server[:encryption]
3737

3838
@conn = socket
39-
setup_encryption encryption if encryption
39+
setup_encryption(encryption, timeout) if encryption
4040
end
4141

4242
def open_connection(server)
4343
hosts = server[:hosts]
4444
encryption = server[:encryption]
4545

46+
timeout = server[:connect_timeout] || DefaultConnectTimeout
4647
socket_opts = {
47-
connect_timeout: server[:connect_timeout] || DefaultConnectTimeout,
48+
connect_timeout: timeout,
4849
}
4950

5051
errors = []
5152
hosts.each do |host, port|
5253
begin
53-
prepare_socket(server.merge(socket: @socket_class.new(host, port, socket_opts)))
54+
prepare_socket(server.merge(socket: @socket_class.new(host, port, socket_opts)), timeout)
5455
return
5556
rescue Net::LDAP::Error, SocketError, SystemCallError,
5657
OpenSSL::SSL::SSLError => e
@@ -76,7 +77,7 @@ def close
7677
end
7778
end
7879

79-
def self.wrap_with_ssl(io, tls_options = {})
80+
def self.wrap_with_ssl(io, tls_options = {}, timeout=nil)
8081
raise Net::LDAP::NoOpenSSLError, "OpenSSL is unavailable" unless Net::LDAP::HasOpenSSL
8182

8283
ctx = OpenSSL::SSL::SSLContext.new
@@ -86,7 +87,26 @@ def self.wrap_with_ssl(io, tls_options = {})
8687
ctx.set_params(tls_options) unless tls_options.empty?
8788

8889
conn = OpenSSL::SSL::SSLSocket.new(io, ctx)
89-
conn.connect
90+
91+
begin
92+
if timeout
93+
conn.connect_nonblock
94+
else
95+
conn.connect
96+
end
97+
rescue IO::WaitReadable
98+
if IO.select([conn], nil, nil, timeout)
99+
retry
100+
else
101+
raise Errno::ETIMEDOUT, "OpenSSL connection read timeout"
102+
end
103+
rescue IO::WaitWritable
104+
if IO.select(nil, [conn], nil, timeout)
105+
retry
106+
else
107+
raise Errno::ETIMEDOUT, "OpenSSL connection write timeout"
108+
end
109+
end
90110

91111
# Doesn't work:
92112
# conn.sync_close = true
@@ -123,11 +143,11 @@ def self.wrap_with_ssl(io, tls_options = {})
123143
# communications, as with simple_tls. Thanks for Kouhei Sutou for
124144
# generously contributing the :start_tls path.
125145
#++
126-
def setup_encryption(args)
146+
def setup_encryption(args, timeout=nil)
127147
args[:tls_options] ||= {}
128148
case args[:method]
129149
when :simple_tls
130-
@conn = self.class.wrap_with_ssl(@conn, args[:tls_options])
150+
@conn = self.class.wrap_with_ssl(@conn, args[:tls_options], timeout)
131151
# additional branches requiring server validation and peer certs, etc.
132152
# go here.
133153
when :start_tls
@@ -144,7 +164,7 @@ def setup_encryption(args)
144164
end
145165

146166
if pdu.result_code.zero?
147-
@conn = self.class.wrap_with_ssl(@conn, args[:tls_options])
167+
@conn = self.class.wrap_with_ssl(@conn, args[:tls_options], timeout)
148168
else
149169
raise Net::LDAP::StartTLSError, "start_tls failed: #{pdu.result_code}"
150170
end

test/test_ldap_connection.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ def test_queued_read_setup_encryption_with_start_tls
291291
and_return(result2)
292292
mock.should_receive(:write)
293293
conn = Net::LDAP::Connection.new(:socket => mock)
294-
flexmock(Net::LDAP::Connection).should_receive(:wrap_with_ssl).with(mock, {}).
294+
flexmock(Net::LDAP::Connection).should_receive(:wrap_with_ssl).with(mock, {}, nil).
295295
and_return(mock)
296296

297297
conn.next_msgid # simulates ongoing query

0 commit comments

Comments
 (0)