jruby-openssl 0.11.0-java → 0.12.1-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +20 -0
  3. data/Mavenfile +21 -26
  4. data/README.md +3 -0
  5. data/Rakefile +21 -35
  6. data/lib/jopenssl/load.rb +0 -14
  7. data/lib/jopenssl/version.rb +1 -1
  8. data/lib/jopenssl.jar +0 -0
  9. data/lib/openssl/bn.rb +40 -9
  10. data/lib/openssl/buffering.rb +478 -9
  11. data/lib/openssl/cipher.rb +67 -9
  12. data/lib/openssl/config.rb +496 -12
  13. data/lib/openssl/digest.rb +73 -9
  14. data/lib/openssl/hmac.rb +13 -0
  15. data/lib/openssl/marshal.rb +30 -0
  16. data/lib/openssl/pkcs5.rb +3 -3
  17. data/lib/openssl/pkey.rb +42 -5
  18. data/lib/openssl/ssl.rb +543 -9
  19. data/lib/openssl/x509.rb +369 -9
  20. data/lib/openssl.rb +43 -1
  21. data/pom.xml +35 -127
  22. metadata +8 -42
  23. data/lib/jopenssl19/openssl/bn.rb +0 -29
  24. data/lib/jopenssl19/openssl/buffering.rb +0 -449
  25. data/lib/jopenssl19/openssl/cipher.rb +0 -28
  26. data/lib/jopenssl19/openssl/config.rb +0 -472
  27. data/lib/jopenssl19/openssl/digest.rb +0 -32
  28. data/lib/jopenssl19/openssl/ssl-internal.rb +0 -223
  29. data/lib/jopenssl19/openssl/ssl.rb +0 -2
  30. data/lib/jopenssl19/openssl/x509-internal.rb +0 -115
  31. data/lib/jopenssl19/openssl/x509.rb +0 -2
  32. data/lib/jopenssl19/openssl.rb +0 -22
  33. data/lib/jopenssl21/openssl/bn.rb +0 -28
  34. data/lib/jopenssl21/openssl/buffering.rb +0 -1
  35. data/lib/jopenssl21/openssl/cipher.rb +0 -1
  36. data/lib/jopenssl21/openssl/config.rb +0 -1
  37. data/lib/jopenssl21/openssl/digest.rb +0 -1
  38. data/lib/jopenssl21/openssl/ssl.rb +0 -1
  39. data/lib/jopenssl21/openssl/x509.rb +0 -119
  40. data/lib/jopenssl21/openssl.rb +0 -22
  41. data/lib/jopenssl22/openssl/bn.rb +0 -39
  42. data/lib/jopenssl22/openssl/buffering.rb +0 -456
  43. data/lib/jopenssl22/openssl/cipher.rb +0 -28
  44. data/lib/jopenssl22/openssl/config.rb +0 -313
  45. data/lib/jopenssl22/openssl/digest.rb +0 -54
  46. data/lib/jopenssl22/openssl/ssl.rb +0 -330
  47. data/lib/jopenssl22/openssl/x509.rb +0 -139
  48. data/lib/jopenssl22/openssl.rb +0 -22
  49. data/lib/jopenssl23/openssl/bn.rb +0 -38
  50. data/lib/jopenssl23/openssl/buffering.rb +0 -455
  51. data/lib/jopenssl23/openssl/cipher.rb +0 -25
  52. data/lib/jopenssl23/openssl/config.rb +0 -474
  53. data/lib/jopenssl23/openssl/digest.rb +0 -43
  54. data/lib/jopenssl23/openssl/pkey.rb +0 -25
  55. data/lib/jopenssl23/openssl/ssl.rb +0 -508
  56. data/lib/jopenssl23/openssl/x509.rb +0 -208
  57. data/lib/jopenssl23/openssl.rb +0 -19
  58. data/lib/openssl/ssl-internal.rb +0 -5
  59. data/lib/openssl/x509-internal.rb +0 -5
data/lib/openssl/ssl.rb CHANGED
@@ -1,9 +1,543 @@
1
- if RUBY_VERSION > '2.3'
2
- load "jopenssl23/openssl/#{File.basename(__FILE__)}"
3
- elsif RUBY_VERSION > '2.2'
4
- load "jopenssl22/openssl/#{File.basename(__FILE__)}"
5
- elsif RUBY_VERSION > '2.1'
6
- load "jopenssl21/openssl/#{File.basename(__FILE__)}"
7
- else
8
- load "jopenssl19/openssl/#{File.basename(__FILE__)}"
9
- end
1
+ # frozen_string_literal: true
2
+ =begin
3
+ = Info
4
+ 'OpenSSL for Ruby 2' project
5
+ Copyright (C) 2001 GOTOU YUUZOU <gotoyuzo@notwork.org>
6
+ All rights reserved.
7
+
8
+ = Licence
9
+ This program is licensed under the same licence as Ruby.
10
+ (See the file 'LICENCE'.)
11
+ =end
12
+
13
+ require "openssl/buffering"
14
+ require "io/nonblock"
15
+ require "socket"
16
+
17
+ module OpenSSL
18
+ module SSL
19
+ class SSLContext
20
+ DEFAULT_PARAMS = { # :nodoc:
21
+ :min_version => OpenSSL::SSL::TLS1_VERSION,
22
+ :verify_mode => OpenSSL::SSL::VERIFY_PEER,
23
+ :verify_hostname => nil, # TODO => true needs JRuby support to call verify_certificate_identity
24
+ :options => OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_COMPRESSION
25
+ }
26
+
27
+ # JRuby does not want this non (recent) OpenSSL fallback to happen
28
+ # if !(OpenSSL::OPENSSL_VERSION.start_with?("OpenSSL") &&
29
+ # OpenSSL::OPENSSL_VERSION_NUMBER >= 0x10100000)
30
+ # DEFAULT_PARAMS.merge!(
31
+ # ciphers: %w{
32
+ # ECDHE-ECDSA-AES128-GCM-SHA256
33
+ # ECDHE-RSA-AES128-GCM-SHA256
34
+ # ECDHE-ECDSA-AES256-GCM-SHA384
35
+ # ECDHE-RSA-AES256-GCM-SHA384
36
+ # DHE-RSA-AES128-GCM-SHA256
37
+ # DHE-DSS-AES128-GCM-SHA256
38
+ # DHE-RSA-AES256-GCM-SHA384
39
+ # DHE-DSS-AES256-GCM-SHA384
40
+ # ECDHE-ECDSA-AES128-SHA256
41
+ # ECDHE-RSA-AES128-SHA256
42
+ # ECDHE-ECDSA-AES128-SHA
43
+ # ECDHE-RSA-AES128-SHA
44
+ # ECDHE-ECDSA-AES256-SHA384
45
+ # ECDHE-RSA-AES256-SHA384
46
+ # ECDHE-ECDSA-AES256-SHA
47
+ # ECDHE-RSA-AES256-SHA
48
+ # DHE-RSA-AES128-SHA256
49
+ # DHE-RSA-AES256-SHA256
50
+ # DHE-RSA-AES128-SHA
51
+ # DHE-RSA-AES256-SHA
52
+ # DHE-DSS-AES128-SHA256
53
+ # DHE-DSS-AES256-SHA256
54
+ # DHE-DSS-AES128-SHA
55
+ # DHE-DSS-AES256-SHA
56
+ # AES128-GCM-SHA256
57
+ # AES256-GCM-SHA384
58
+ # AES128-SHA256
59
+ # AES256-SHA256
60
+ # AES128-SHA
61
+ # AES256-SHA
62
+ # }.join(":"),
63
+ # )
64
+ # end
65
+
66
+ if defined?(OpenSSL::PKey::DH)
67
+ DEFAULT_2048 = OpenSSL::PKey::DH.new <<-_end_of_pem_
68
+ -----BEGIN DH PARAMETERS-----
69
+ MIIBCAKCAQEA7E6kBrYiyvmKAMzQ7i8WvwVk9Y/+f8S7sCTN712KkK3cqd1jhJDY
70
+ JbrYeNV3kUIKhPxWHhObHKpD1R84UpL+s2b55+iMd6GmL7OYmNIT/FccKhTcveab
71
+ VBmZT86BZKYyf45hUF9FOuUM9xPzuK3Vd8oJQvfYMCd7LPC0taAEljQLR4Edf8E6
72
+ YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3
73
+ 1bNveX5wInh5GDx1FGhKBZ+s1H+aedudCm7sCgRwv8lKWYGiHzObSma8A86KG+MD
74
+ 7Lo5JquQ3DlBodj3IDyPrxIv96lvRPFtAwIBAg==
75
+ -----END DH PARAMETERS-----
76
+ _end_of_pem_
77
+ private_constant :DEFAULT_2048
78
+
79
+ DEFAULT_TMP_DH_CALLBACK = lambda { |ctx, is_export, keylen| # :nodoc:
80
+ warn "using default DH parameters." if $VERBOSE
81
+ DEFAULT_2048
82
+ }
83
+ end
84
+
85
+ DEFAULT_CERT_STORE = OpenSSL::X509::Store.new # :nodoc:
86
+ DEFAULT_CERT_STORE.set_default_paths
87
+ DEFAULT_CERT_STORE.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL
88
+
89
+ # A callback invoked when DH parameters are required.
90
+ #
91
+ # The callback is invoked with the Session for the key exchange, an
92
+ # flag indicating the use of an export cipher and the keylength
93
+ # required.
94
+ #
95
+ # The callback must return an OpenSSL::PKey::DH instance of the correct
96
+ # key length.
97
+
98
+ attr_accessor :tmp_dh_callback
99
+
100
+ # A callback invoked at connect time to distinguish between multiple
101
+ # server names.
102
+ #
103
+ # The callback is invoked with an SSLSocket and a server name. The
104
+ # callback must return an SSLContext for the server name or nil.
105
+ attr_accessor :servername_cb
106
+
107
+ # call-seq:
108
+ # SSLContext.new -> ctx
109
+ # SSLContext.new(:TLSv1) -> ctx
110
+ # SSLContext.new("SSLv23") -> ctx
111
+ #
112
+ # Creates a new SSL context.
113
+ #
114
+ # If an argument is given, #ssl_version= is called with the value. Note
115
+ # that this form is deprecated. New applications should use #min_version=
116
+ # and #max_version= as necessary.
117
+ # def initialize(version = nil)
118
+ # self.options |= OpenSSL::SSL::OP_ALL
119
+ # self.ssl_version = version if version
120
+ # end
121
+
122
+ ##
123
+ # call-seq:
124
+ # ctx.set_params(params = {}) -> params
125
+ #
126
+ # Sets saner defaults optimized for the use with HTTP-like protocols.
127
+ #
128
+ # If a Hash _params_ is given, the parameters are overridden with it.
129
+ # The keys in _params_ must be assignment methods on SSLContext.
130
+ #
131
+ # If the verify_mode is not VERIFY_NONE and ca_file, ca_path and
132
+ # cert_store are not set then the system default certificate store is
133
+ # used.
134
+ def set_params(params={})
135
+ params = DEFAULT_PARAMS.merge(params)
136
+ self.options = params.delete(:options) # set before min_version/max_version
137
+ params.each{|name, value| self.__send__("#{name}=", value) }
138
+ if self.verify_mode != OpenSSL::SSL::VERIFY_NONE
139
+ unless self.ca_file or self.ca_path or self.cert_store
140
+ self.cert_store = DEFAULT_CERT_STORE
141
+ end
142
+ end
143
+ return params
144
+ end
145
+
146
+ # call-seq:
147
+ # ctx.min_version = OpenSSL::SSL::TLS1_2_VERSION
148
+ # ctx.min_version = :TLS1_2
149
+ # ctx.min_version = nil
150
+ #
151
+ # Sets the lower bound on the supported SSL/TLS protocol version. The
152
+ # version may be specified by an integer constant named
153
+ # OpenSSL::SSL::*_VERSION, a Symbol, or +nil+ which means "any version".
154
+ #
155
+ # Be careful that you don't overwrite OpenSSL::SSL::OP_NO_{SSL,TLS}v*
156
+ # options by #options= once you have called #min_version= or
157
+ # #max_version=.
158
+ #
159
+ # === Example
160
+ # ctx = OpenSSL::SSL::SSLContext.new
161
+ # ctx.min_version = OpenSSL::SSL::TLS1_1_VERSION
162
+ # ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION
163
+ #
164
+ # sock = OpenSSL::SSL::SSLSocket.new(tcp_sock, ctx)
165
+ # sock.connect # Initiates a connection using either TLS 1.1 or TLS 1.2
166
+ def min_version=(version)
167
+ set_minmax_proto_version(version, @max_proto_version ||= nil)
168
+ @min_proto_version = version
169
+ end
170
+
171
+ # call-seq:
172
+ # ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION
173
+ # ctx.max_version = :TLS1_2
174
+ # ctx.max_version = nil
175
+ #
176
+ # Sets the upper bound of the supported SSL/TLS protocol version. See
177
+ # #min_version= for the possible values.
178
+ def max_version=(version)
179
+ set_minmax_proto_version(@min_proto_version ||= nil, version)
180
+ @max_proto_version = version
181
+ end
182
+
183
+ # call-seq:
184
+ # ctx.ssl_version = :TLSv1
185
+ # ctx.ssl_version = "SSLv23"
186
+ #
187
+ # Sets the SSL/TLS protocol version for the context. This forces
188
+ # connections to use only the specified protocol version. This is
189
+ # deprecated and only provided for backwards compatibility. Use
190
+ # #min_version= and #max_version= instead.
191
+ #
192
+ # === History
193
+ # As the name hints, this used to call the SSL_CTX_set_ssl_version()
194
+ # function which sets the SSL method used for connections created from
195
+ # the context. As of Ruby/OpenSSL 2.1, this accessor method is
196
+ # implemented to call #min_version= and #max_version= instead.
197
+ # def ssl_version=(meth)
198
+ # meth = meth.to_s if meth.is_a?(Symbol)
199
+ # if /(?<type>_client|_server)\z/ =~ meth
200
+ # meth = $`
201
+ # if $VERBOSE
202
+ # warn "#{caller(1, 1)[0]}: method type #{type.inspect} is ignored"
203
+ # end
204
+ # end
205
+ # version = METHODS_MAP[meth.intern] or
206
+ # raise ArgumentError, "unknown SSL method `%s'" % meth
207
+ # set_minmax_proto_version(version, version)
208
+ # @min_proto_version = @max_proto_version = version
209
+ # end
210
+ #
211
+ # METHODS_MAP = {
212
+ # SSLv23: 0,
213
+ # SSLv2: OpenSSL::SSL::SSL2_VERSION,
214
+ # SSLv3: OpenSSL::SSL::SSL3_VERSION,
215
+ # TLSv1: OpenSSL::SSL::TLS1_VERSION,
216
+ # TLSv1_1: OpenSSL::SSL::TLS1_1_VERSION,
217
+ # TLSv1_2: OpenSSL::SSL::TLS1_2_VERSION,
218
+ # }.freeze
219
+ # private_constant :METHODS_MAP
220
+
221
+ # METHODS setup from native (JRuby)
222
+ # The list of available SSL/TLS methods. This constant is only provided
223
+ # for backwards compatibility.
224
+ # METHODS = METHODS_MAP.flat_map { |name,|
225
+ # [name, :"#{name}_client", :"#{name}_server"]
226
+ # }.freeze
227
+ # deprecate_constant :METHODS
228
+ end
229
+
230
+ module SocketForwarder
231
+ # The file descriptor for the socket.
232
+ def fileno
233
+ to_io.fileno
234
+ end
235
+
236
+ def addr
237
+ to_io.addr
238
+ end
239
+
240
+ def peeraddr
241
+ to_io.peeraddr
242
+ end
243
+
244
+ def setsockopt(level, optname, optval)
245
+ to_io.setsockopt(level, optname, optval)
246
+ end
247
+
248
+ def getsockopt(level, optname)
249
+ to_io.getsockopt(level, optname)
250
+ end
251
+
252
+ def fcntl(*args)
253
+ to_io.fcntl(*args)
254
+ end
255
+
256
+ def closed?
257
+ to_io.closed?
258
+ end
259
+
260
+ def do_not_reverse_lookup=(flag)
261
+ to_io.do_not_reverse_lookup = flag
262
+ end
263
+ end
264
+
265
+ def verify_certificate_identity(cert, hostname)
266
+ should_verify_common_name = true
267
+ cert.extensions.each{|ext|
268
+ next if ext.oid != "subjectAltName"
269
+ ext.value.split(/,\s+/).each { |general_name|
270
+ #case san.tag
271
+ # MRI 2.2.3 (JRuby parses ASN.1 differently)
272
+ #when 2 # dNSName in GeneralName (RFC5280)
273
+ if /\ADNS:(.*)/ =~ general_name
274
+ should_verify_common_name = false
275
+ return true if verify_hostname(hostname, $1)
276
+ # MRI 2.2.3 (JRuby parses ASN.1 differently)
277
+ #when 7 # iPAddress in GeneralName (RFC5280)
278
+ elsif /\AIP(?: Address)?:(.*)/ =~ general_name
279
+ should_verify_common_name = false
280
+ return true if $1 == hostname
281
+ # NOTE: bellow logic makes little sense JRuby reads exts differently
282
+ # follows GENERAL_NAME_print() in x509v3/v3_alt.c
283
+ # if san.value.size == 4 || san.value.size == 16
284
+ # begin
285
+ # return true if $1 == IPAddr.new(hostname).to_s
286
+ # rescue IPAddr::InvalidAddressError
287
+ # end
288
+ # end
289
+ end
290
+ }
291
+ }
292
+ if should_verify_common_name
293
+ cert.subject.to_a.each{|oid, value|
294
+ if oid == "CN"
295
+ return true if verify_hostname(hostname, value)
296
+ end
297
+ }
298
+ end
299
+ return false
300
+ end
301
+ module_function :verify_certificate_identity
302
+
303
+ def verify_hostname(hostname, san) # :nodoc:
304
+ # RFC 5280, IA5String is limited to the set of ASCII characters
305
+ return false unless san.ascii_only?
306
+ return false unless hostname.ascii_only?
307
+
308
+ # See RFC 6125, section 6.4.1
309
+ # Matching is case-insensitive.
310
+ san_parts = san.downcase.split(".")
311
+
312
+ # TODO: this behavior should probably be more strict
313
+ return san == hostname if san_parts.size < 2
314
+
315
+ # Matching is case-insensitive.
316
+ host_parts = hostname.downcase.split(".")
317
+
318
+ # RFC 6125, section 6.4.3, subitem 2.
319
+ # If the wildcard character is the only character of the left-most
320
+ # label in the presented identifier, the client SHOULD NOT compare
321
+ # against anything but the left-most label of the reference
322
+ # identifier (e.g., *.example.com would match foo.example.com but
323
+ # not bar.foo.example.com or example.com).
324
+ return false unless san_parts.size == host_parts.size
325
+
326
+ # RFC 6125, section 6.4.3, subitem 1.
327
+ # The client SHOULD NOT attempt to match a presented identifier in
328
+ # which the wildcard character comprises a label other than the
329
+ # left-most label (e.g., do not match bar.*.example.net).
330
+ return false unless verify_wildcard(host_parts.shift, san_parts.shift)
331
+
332
+ san_parts.join(".") == host_parts.join(".")
333
+ end
334
+ module_function :verify_hostname
335
+
336
+ def verify_wildcard(domain_component, san_component) # :nodoc:
337
+ parts = san_component.split("*", -1)
338
+
339
+ return false if parts.size > 2
340
+ return san_component == domain_component if parts.size == 1
341
+
342
+ # RFC 6125, section 6.4.3, subitem 3.
343
+ # The client SHOULD NOT attempt to match a presented identifier
344
+ # where the wildcard character is embedded within an A-label or
345
+ # U-label of an internationalized domain name.
346
+ return false if domain_component.start_with?("xn--") && san_component != "*"
347
+
348
+ parts[0].length + parts[1].length < domain_component.length &&
349
+ domain_component.start_with?(parts[0]) &&
350
+ domain_component.end_with?(parts[1])
351
+ end
352
+ module_function :verify_wildcard
353
+
354
+ class SSLSocket
355
+ include Buffering
356
+ include SocketForwarder
357
+
358
+ # attr_reader :hostname
359
+ #
360
+ # # The underlying IO object.
361
+ # attr_reader :io
362
+ # alias :to_io :io
363
+ #
364
+ # # The SSLContext object used in this connection.
365
+ # attr_reader :context
366
+ #
367
+ # # Whether to close the underlying socket as well, when the SSL/TLS
368
+ # # connection is shut down. This defaults to +false+.
369
+ # attr_accessor :sync_close
370
+
371
+ # call-seq:
372
+ # ssl.sysclose => nil
373
+ #
374
+ # Sends "close notify" to the peer and tries to shut down the SSL
375
+ # connection gracefully.
376
+ #
377
+ # If sync_close is set to +true+, the underlying IO is also closed.
378
+ def sysclose
379
+ return if closed?
380
+ stop
381
+ io.close if sync_close
382
+ end unless method_defined? :sysclose
383
+
384
+ # call-seq:
385
+ # ssl.post_connection_check(hostname) -> true
386
+ #
387
+ # Perform hostname verification following RFC 6125.
388
+ #
389
+ # This method MUST be called after calling #connect to ensure that the
390
+ # hostname of a remote peer has been verified.
391
+ def post_connection_check(hostname)
392
+ if peer_cert.nil?
393
+ msg = "Peer verification enabled, but no certificate received."
394
+ if using_anon_cipher?
395
+ msg += " Anonymous cipher suite #{cipher[0]} was negotiated. " \
396
+ "Anonymous suites must be disabled to use peer verification."
397
+ end
398
+ raise SSLError, msg
399
+ end
400
+
401
+ unless OpenSSL::SSL.verify_certificate_identity(peer_cert, hostname)
402
+ raise SSLError, "hostname \"#{hostname}\" does not match the server certificate"
403
+ end
404
+ return true
405
+ end
406
+
407
+ # call-seq:
408
+ # ssl.session -> aSession
409
+ #
410
+ # Returns the SSLSession object currently used, or nil if the session is
411
+ # not established.
412
+ def session
413
+ SSL::Session.new(self)
414
+ rescue SSL::Session::SessionError
415
+ nil
416
+ end unless method_defined? :session # JRuby
417
+
418
+ private
419
+
420
+ def using_anon_cipher?
421
+ ctx = OpenSSL::SSL::SSLContext.new
422
+ ctx.ciphers = "aNULL"
423
+ ctx.ciphers.include?(cipher)
424
+ end
425
+
426
+ def client_cert_cb
427
+ @context.client_cert_cb
428
+ end
429
+
430
+ def tmp_dh_callback
431
+ @context.tmp_dh_callback || OpenSSL::SSL::SSLContext::DEFAULT_TMP_DH_CALLBACK
432
+ end
433
+
434
+ def tmp_ecdh_callback
435
+ @context.tmp_ecdh_callback
436
+ end
437
+
438
+ def session_new_cb
439
+ @context.session_new_cb
440
+ end
441
+
442
+ def session_get_cb
443
+ @context.session_get_cb
444
+ end
445
+
446
+ class << self
447
+
448
+ # call-seq:
449
+ # open(remote_host, remote_port, local_host=nil, local_port=nil, context: nil)
450
+ #
451
+ # Creates a new instance of SSLSocket.
452
+ # _remote\_host_ and _remote\_port_ are used to open TCPSocket.
453
+ # If _local\_host_ and _local\_port_ are specified,
454
+ # then those parameters are used on the local end to establish the connection.
455
+ # If _context_ is provided,
456
+ # the SSL Sockets initial params will be taken from the context.
457
+ #
458
+ # === Examples
459
+ #
460
+ # sock = OpenSSL::SSL::SSLSocket.open('localhost', 443)
461
+ # sock.connect # Initiates a connection to localhost:443
462
+ #
463
+ # with SSLContext:
464
+ #
465
+ # ctx = OpenSSL::SSL::SSLContext.new
466
+ # sock = OpenSSL::SSL::SSLSocket.open('localhost', 443, context: ctx)
467
+ # sock.connect # Initiates a connection to localhost:443 with SSLContext
468
+ def open(remote_host, remote_port, local_host=nil, local_port=nil, context: nil)
469
+ sock = ::TCPSocket.open(remote_host, remote_port, local_host, local_port)
470
+ if context.nil?
471
+ return OpenSSL::SSL::SSLSocket.new(sock)
472
+ else
473
+ return OpenSSL::SSL::SSLSocket.new(sock, context)
474
+ end
475
+ end
476
+ end
477
+ end
478
+
479
+ ##
480
+ # SSLServer represents a TCP/IP server socket with Secure Sockets Layer.
481
+ class SSLServer
482
+ include SocketForwarder
483
+ # When true then #accept works exactly the same as TCPServer#accept
484
+ attr_accessor :start_immediately
485
+
486
+ # Creates a new instance of SSLServer.
487
+ # * _srv_ is an instance of TCPServer.
488
+ # * _ctx_ is an instance of OpenSSL::SSL::SSLContext.
489
+ def initialize(svr, ctx)
490
+ @svr = svr
491
+ @ctx = ctx
492
+ unless ctx.session_id_context
493
+ # see #6137 - session id may not exceed 32 bytes
494
+ prng = ::Random.new($0.hash)
495
+ session_id = prng.bytes(16).unpack('H*')[0]
496
+ @ctx.session_id_context = session_id
497
+ end
498
+ @start_immediately = true
499
+ end
500
+
501
+ # Returns the TCPServer passed to the SSLServer when initialized.
502
+ def to_io
503
+ @svr
504
+ end
505
+
506
+ # See TCPServer#listen for details.
507
+ def listen(backlog=Socket::SOMAXCONN)
508
+ @svr.listen(backlog)
509
+ end
510
+
511
+ # See BasicSocket#shutdown for details.
512
+ def shutdown(how=Socket::SHUT_RDWR)
513
+ @svr.shutdown(how)
514
+ end
515
+
516
+ # Works similar to TCPServer#accept.
517
+ def accept
518
+ # Socket#accept returns [socket, addrinfo].
519
+ # TCPServer#accept returns a socket.
520
+ # The following comma strips addrinfo.
521
+ sock, = @svr.accept
522
+ begin
523
+ ssl = OpenSSL::SSL::SSLSocket.new(sock, @ctx)
524
+ ssl.sync_close = true
525
+ ssl.accept if @start_immediately
526
+ ssl
527
+ rescue Exception => ex
528
+ if ssl
529
+ ssl.close
530
+ else
531
+ sock.close
532
+ end
533
+ raise ex
534
+ end
535
+ end
536
+
537
+ # See IO#close for details.
538
+ def close
539
+ @svr.close
540
+ end
541
+ end
542
+ end
543
+ end