class RubyTls::SSL::Box
Constants
- CIPHER_DISPATCH_FAILED
- InstanceLookup
- READ_BUFFER
- SSL_ERROR_SSL
- SSL_ERROR_WANT_READ
- SSL_ERROR_WANT_WRITE
- SSL_RECEIVED_SHUTDOWN
- SSL_VERIFY_CLIENT_ONCE
- SSL_VERIFY_PEER
- VerifyCB
Attributes
context[R]
handshake_completed[R]
hosts[R]
is_server[R]
Public Class Methods
new(server, transport, options = {})
click to toggle source
# File lib/ruby-tls/ssl.rb, line 548 def initialize(server, transport, options = {}) @ready = true @handshake_completed = false @handshake_signaled = false @negotiated = false @transport = transport @read_buffer = FFI::MemoryPointer.new(:char, READ_BUFFER, false) @is_server = server @context = Context.new(server, options) @bioRead = SSL.BIO_new(SSL.BIO_s_mem) @bioWrite = SSL.BIO_new(SSL.BIO_s_mem) @ssl = SSL.SSL_new(@context.ssl_ctx) SSL.SSL_set_bio(@ssl, @bioRead, @bioWrite) @write_queue = [] InstanceLookup[@ssl.address] = self @alpn_fallback = options[:fallback] if options[:verify_peer] SSL.SSL_set_verify(@ssl, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, VerifyCB) end # Add Server Name Indication (SNI) for client connections if options[:host_name] if server @hosts = ::Concurrent::Map.new @hosts[options[:host_name].to_s] = @context @context.add_server_name_indication else SSL.SSL_set_tlsext_host_name(@ssl, options[:host_name]) end end SSL.SSL_connect(@ssl) unless server end
Public Instance Methods
add_host(host_name:, **options)
click to toggle source
# File lib/ruby-tls/ssl.rb, line 589 def add_host(host_name:, **options) raise 'Server Name Indication (SNI) not configured for default host' unless @hosts raise 'only valid for server mode context' unless @is_server context = Context.new(true, options) @hosts[host_name.to_s] = context context.add_server_name_indication nil end
cleanup()
click to toggle source
# File lib/ruby-tls/ssl.rb, line 718 def cleanup return unless @ready @ready = false InstanceLookup.delete @ssl.address if (SSL.SSL_get_shutdown(@ssl) & SSL_RECEIVED_SHUTDOWN) != 0 SSL.SSL_shutdown @ssl else SSL.SSL_clear @ssl end SSL.SSL_free @ssl if @hosts @hosts.each_value do |context| context.cleanup end @hosts = nil else @context.cleanup end end
decrypt(data)
click to toggle source
# File lib/ruby-tls/ssl.rb, line 657 def decrypt(data) return unless @ready put_cipher_text data if not SSL.SSL_is_init_finished(@ssl) resp = @is_server ? SSL.SSL_accept(@ssl) : SSL.SSL_connect(@ssl) if resp < 0 err_code = SSL.SSL_get_error(@ssl, resp) if err_code != SSL_ERROR_WANT_READ @transport.close_cb if err_code == SSL_ERROR_SSL return end end @handshake_completed = true signal_handshake unless @handshake_signaled end while true do size = get_plain_text(@read_buffer, READ_BUFFER) if size > 0 @transport.dispatch_cb @read_buffer.read_string(size) else break end end dispatch_cipher_text end
encrypt(data)
click to toggle source
# File lib/ruby-tls/ssl.rb, line 644 def encrypt(data) return unless @ready wrote = put_plain_text data if wrote < 0 @transport.close_cb else dispatch_cipher_text end end
get_peer_cert()
click to toggle source
# File lib/ruby-tls/ssl.rb, line 617 def get_peer_cert return '' unless @ready SSL.SSL_get_peer_certificate(@ssl) end
negotiated()
click to toggle source
# File lib/ruby-tls/ssl.rb, line 713 def negotiated @negotiated = true end
negotiated_protocol()
click to toggle source
# File lib/ruby-tls/ssl.rb, line 622 def negotiated_protocol return nil unless @context.alpn_set proto = FFI::MemoryPointer.new(:pointer, 1, true) len = FFI::MemoryPointer.new(:uint, 1, true) SSL.SSL_get0_alpn_selected(@ssl, proto, len) resp = proto.get_pointer(0) if resp.address == 0 :failed else length = len.get_uint(0) resp.read_string(length).to_sym end end
remove_host(host_name)
click to toggle source
Careful with this. If you remove all the hosts you'll end up with a segfault
# File lib/ruby-tls/ssl.rb, line 600 def remove_host(host_name) raise 'Server Name Indication (SNI) not configured for default host' unless @hosts raise 'only valid for server mode context' unless @is_server context = @hosts[host_name.to_s] if context @hosts.delete(host_name.to_s) context.cleanup end nil end
signal_handshake()
click to toggle source
# File lib/ruby-tls/ssl.rb, line 689 def signal_handshake @handshake_signaled = true # Check protocol support here if @context.alpn_set proto = negotiated_protocol if proto == :failed if @negotiated # We should shutdown if this is the case @transport.close_cb return elsif @alpn_fallback # Client or Server with a client that doesn't support ALPN proto = @alpn_fallback.to_sym end end else proto = nil end @transport.handshake_cb(proto) end
start()
click to toggle source
# File lib/ruby-tls/ssl.rb, line 638 def start return unless @ready dispatch_cipher_text end
verify(cert)
click to toggle source
Called from class level callback function
# File lib/ruby-tls/ssl.rb, line 743 def verify(cert) @transport.verify_cb(cert) == true ? 1 : 0 end
Private Instance Methods
dispatch_cipher_text()
click to toggle source
# File lib/ruby-tls/ssl.rb, line 835 def dispatch_cipher_text begin did_work = false # Get all the encrypted data and transmit it pending = pending_data(@bioWrite) if pending > 0 buffer = FFI::MemoryPointer.new(:char, pending, false) resp = get_cipher_text(buffer, pending) raise CIPHER_DISPATCH_FAILED unless resp > 0 @transport.transmit_cb(buffer.read_string(resp)) did_work = true end # Send any queued out going data unless @write_queue.empty? resp = put_plain_text nil if resp > 0 did_work = true elsif resp < 0 @transport.close_cb end end end while did_work end
get_cipher_text(buffer, length)
click to toggle source
# File lib/ruby-tls/ssl.rb, line 784 def get_cipher_text(buffer, length) SSL.BIO_read(@bioWrite, buffer, length) end
get_plain_text(buffer, ready)
click to toggle source
# File lib/ruby-tls/ssl.rb, line 751 def get_plain_text(buffer, ready) # Read the buffered clear text size = SSL.SSL_read(@ssl, buffer, ready) if size >= 0 size else SSL.SSL_get_error(@ssl, size) == SSL_ERROR_WANT_READ ? 0 : -1 end end
pending_data(bio)
click to toggle source
# File lib/ruby-tls/ssl.rb, line 780 def pending_data(bio) SSL.BIO_pending(bio) end
put_cipher_text(data)
click to toggle source
# File lib/ruby-tls/ssl.rb, line 788 def put_cipher_text(data) len = data.bytesize wrote = SSL.BIO_write(@bioRead, data, len) wrote == len end
put_plain_text(data)
click to toggle source
# File lib/ruby-tls/ssl.rb, line 796 def put_plain_text(data) @write_queue.push(data) if data return 0 unless SSL.SSL_is_init_finished(@ssl) fatal = false did_work = false while !@write_queue.empty? do data = @write_queue.pop len = data.bytesize wrote = SSL.SSL_write(@ssl, data, len) if wrote > 0 did_work = true; else err_code = SSL.SSL_get_error(@ssl, wrote) if (err_code != SSL_ERROR_WANT_READ) && (err_code != SSL_ERROR_WANT_WRITE) fatal = true else # Not fatal - add back to the queue @write_queue.unshift data end break end end if did_work 1 elsif fatal -1 else 0 end end