| 
    
       #!/usr/bin/env ruby
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       # This code was written by Sam Stelfox (http://www.stelfox.net/) and is
 
     | 
  
  
     | 
    
       # licensed under GPLv2
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       require 'openssl'
 
     | 
  
  
     | 
    
       require 'socket'
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       # This bug has been tested on:
 
     | 
  
  
     | 
    
       #   ruby 2.2.0dev (2014-04-16 trunk 45603) [x86_64-linux]
 
     | 
  
  
     | 
    
       #   ruby 2.1.0p0 (2013-12-25 revision 44422) [x86_64-linux]
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       # Too connect to this server once it is running you can use the following
 
     | 
  
  
     | 
    
       # command (only tested on Linux):
 
     | 
  
  
     | 
    
       #
 
     | 
  
  
     | 
    
       #     openssl s_client -connect 127.0.0.1:4443
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       # This class is a work around for OpenSSL::SSL::SSLServer when a raw socket
 
     | 
  
  
     | 
    
       # needs to be passed too it. The Ruby sample code I've been able to find for
 
     | 
  
  
     | 
    
       # interacting with OpenSSL::SSL::SSLServer shows passing a TCPServer object
 
     | 
  
  
     | 
    
       # which is not always ideal.
 
     | 
  
  
     | 
    
       #
 
     | 
  
  
     | 
    
       # TCPServer#accept only returns the socket file descriptor, while Socket#accept
 
     | 
  
  
     | 
    
       # returns both the socket file descriptor and an Addrinfo object with
 
     | 
  
  
     | 
    
       # information about the client connected.
 
     | 
  
  
     | 
    
       class TweakedSSLServer < OpenSSL::SSL::SSLServer
 
     | 
  
  
     | 
    
         # This method was pulled in it's entirety out of trunk with one line adjusted
 
     | 
  
  
     | 
    
         # so it can handle both Socket and TCPServer.
 
     | 
  
  
     | 
    
         def accept
 
     | 
  
  
     | 
    
           sock, addrinfo = @svr.accept # This line was adjusted
 
     | 
  
  
     | 
    
           begin
 
     | 
  
  
     | 
    
             ssl = OpenSSL::SSL::SSLSocket.new(sock, @ctx)
 
     | 
  
  
     | 
    
             ssl.sync_close = true
 
     | 
  
  
     | 
    
             ssl.accept if @start_immediately
 
     | 
  
  
     | 
    
             ssl
 
     | 
  
  
     | 
    
           rescue SSLError => ex
 
     | 
  
  
     | 
    
             sock.close
 
     | 
  
  
     | 
    
             raise ex
 
     | 
  
  
     | 
    
           end
 
     | 
  
  
     | 
    
         end
 
     | 
  
  
     | 
    
       end
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       # Helper method for generating a self-signed certificate so we're not dependent
 
     | 
  
  
     | 
    
       # on outside files
 
     | 
  
  
     | 
    
       def generate_cert(name, key)
 
     | 
  
  
     | 
    
         cert = OpenSSL::X509::Certificate.new
 
     | 
  
  
     | 
    
         cert.version = 2
 
     | 
  
  
     | 
    
         cert.serial = 1
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
         cert.not_before = (Time.now - 3600)
 
     | 
  
  
     | 
    
         cert.not_after = (Time.now + 3600)
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
         cert.subject = OpenSSL::X509::Name.parse("CN=#{name}")
 
     | 
  
  
     | 
    
         cert.issuer = cert.subject
 
     | 
  
  
     | 
    
         cert.public_key = key.public_key
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
         cert.sign(key, OpenSSL::Digest::SHA384.new)
 
     | 
  
  
     | 
    
       end
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       # Sample non-production key & certificate. These are not relevant to the issue,
 
     | 
  
  
     | 
    
       # but are required to setup the SSL context.
 
     | 
  
  
     | 
    
       SERVER_KEY = OpenSSL::PKey::RSA.new(1024)
 
     | 
  
  
     | 
    
       SERVER_CERT = generate_cert('example', SERVER_KEY)
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       sock = Socket.new(Socket::AF_INET6, Socket::SOCK_STREAM, 0)
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       sock.bind(Socket.pack_sockaddr_in(4443, '::'))
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       ssl_context = OpenSSL::SSL::SSLContext.new
 
     | 
  
  
     | 
    
       ssl_context.key = SERVER_KEY
 
     | 
  
  
     | 
    
       ssl_context.cert = SERVER_CERT
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       # Switch the comments on these lines to see the issue with the SSLServer code.
 
     | 
  
  
     | 
    
       # When the regular SSLServer is used, this will break with the following error
 
     | 
  
  
     | 
    
       # after a client attempts to connect:
 
     | 
  
  
     | 
    
       #
 
     | 
  
  
     | 
    
       #    wrong argument type Array (expected File)
 
     | 
  
  
     | 
    
       #    /home/dev_user/.rvm/rubies/ruby-head/lib/ruby/2.2.0/openssl/ssl.rb:230:in `initialize'
 
     | 
  
  
     | 
    
       #    /home/dev_user/.rvm/rubies/ruby-head/lib/ruby/2.2.0/openssl/ssl.rb:230:in `new'
 
     | 
  
  
     | 
    
       #    /home/dev_user/.rvm/rubies/ruby-head/lib/ruby/2.2.0/openssl/ssl.rb:230:in `accept'
 
     | 
  
  
     | 
    
       #    test_ssl_server.rb:84:in `block in <main>'
 
     | 
  
  
     | 
    
       #    test_ssl_server.rb:83:in `each'
 
     | 
  
  
     | 
    
       #    test_ssl_server.rb:83:in `<main>'
 
     | 
  
  
     | 
    
       #
 
     | 
  
  
     | 
    
       ssl_sock = OpenSSL::SSL::SSLServer.new(sock, ssl_context)
 
     | 
  
  
     | 
    
       #ssl_sock = TweakedSSLServer.new(sock, ssl_context)
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       ssl_sock.listen(5)
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       # Properly close the socket on a Ctrl-C before closing.
 
     | 
  
  
     | 
    
       trap(:INT) { ssl_sock.close; exit }
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       puts "Server ready"
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       # Standard simplified server loop
 
     | 
  
  
     | 
    
       begin
 
     | 
  
  
     | 
    
         ready_socket = IO.select([ssl_sock], nil, nil, 1)
 
     | 
  
  
     | 
    
         if ready_socket
 
     | 
  
  
     | 
    
           ready_socket[0].each do |s|
 
     | 
  
  
     | 
    
             client = s.accept
 
     | 
  
  
     | 
    
             client.write("Client successfully connected!\n")
 
     | 
  
  
     | 
    
             client.close
 
     | 
  
  
     | 
    
           end
 
     | 
  
  
     | 
    
         end
 
     | 
  
  
     | 
    
       rescue => e
 
     | 
  
  
     | 
    
         puts e.message
 
     | 
  
  
     | 
    
         puts e.backtrace
 
     | 
  
  
     | 
    
       end while true
 
     | 
  
  
     | 
    
       
     |