@@ -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
0 commit comments