@@ -31,26 +31,27 @@ def socket_class=(socket_class)
31
31
@socket_class = socket_class
32
32
end
33
33
34
- def prepare_socket ( server )
34
+ def prepare_socket ( server , timeout = nil )
35
35
socket = server [ :socket ]
36
36
encryption = server [ :encryption ]
37
37
38
38
@conn = socket
39
- setup_encryption encryption if encryption
39
+ setup_encryption ( encryption , timeout ) if encryption
40
40
end
41
41
42
42
def open_connection ( server )
43
43
hosts = server [ :hosts ]
44
44
encryption = server [ :encryption ]
45
45
46
+ timeout = server [ :connect_timeout ] || DefaultConnectTimeout
46
47
socket_opts = {
47
- connect_timeout : server [ :connect_timeout ] || DefaultConnectTimeout ,
48
+ connect_timeout : timeout ,
48
49
}
49
50
50
51
errors = [ ]
51
52
hosts . each do |host , port |
52
53
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 )
54
55
return
55
56
rescue Net ::LDAP ::Error , SocketError , SystemCallError ,
56
57
OpenSSL ::SSL ::SSLError => e
@@ -76,7 +77,7 @@ def close
76
77
end
77
78
end
78
79
79
- def self . wrap_with_ssl ( io , tls_options = { } )
80
+ def self . wrap_with_ssl ( io , tls_options = { } , timeout = nil )
80
81
raise Net ::LDAP ::NoOpenSSLError , "OpenSSL is unavailable" unless Net ::LDAP ::HasOpenSSL
81
82
82
83
ctx = OpenSSL ::SSL ::SSLContext . new
@@ -86,7 +87,26 @@ def self.wrap_with_ssl(io, tls_options = {})
86
87
ctx . set_params ( tls_options ) unless tls_options . empty?
87
88
88
89
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
90
110
91
111
# Doesn't work:
92
112
# conn.sync_close = true
@@ -123,11 +143,11 @@ def self.wrap_with_ssl(io, tls_options = {})
123
143
# communications, as with simple_tls. Thanks for Kouhei Sutou for
124
144
# generously contributing the :start_tls path.
125
145
#++
126
- def setup_encryption ( args )
146
+ def setup_encryption ( args , timeout = nil )
127
147
args [ :tls_options ] ||= { }
128
148
case args [ :method ]
129
149
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 )
131
151
# additional branches requiring server validation and peer certs, etc.
132
152
# go here.
133
153
when :start_tls
@@ -144,7 +164,7 @@ def setup_encryption(args)
144
164
end
145
165
146
166
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 )
148
168
else
149
169
raise Net ::LDAP ::StartTLSError , "start_tls failed: #{ pdu . result_code } "
150
170
end
0 commit comments