|  | #!/usr/bin/env ruby | 
        
          |  |  | 
        
          |  | require 'socket' | 
        
          |  | require 'openssl' | 
        
          |  |  | 
        
          |  | class FakeSmtpServer | 
        
          |  | def initialize(port) | 
        
          |  | @port = port | 
        
          |  | @socket = TCPServer.new(@port) | 
        
          |  | @client = nil | 
        
          |  | @ssl_client = nil | 
        
          |  | end | 
        
          |  |  | 
        
          |  | def start | 
        
          |  | return unless @client.nil? | 
        
          |  |  | 
        
          |  | puts "fake_smtp_server: Waiting for one connection to port #{@port} ..." | 
        
          |  | @client = @socket.accept | 
        
          |  |  | 
        
          |  | send "220 dummy-smtp.example.com SMTP" | 
        
          |  | cmd = receive | 
        
          |  |  | 
        
          |  | while cmd !~ /^QUIT\r/ | 
        
          |  | if cmd =~ /^HELO(.*)\r/ | 
        
          |  | send "250-Welcome to a dummy smtp server" | 
        
          |  | send "250-STARTTLS" | 
        
          |  | send "250-AUTH PLAIN LOGIN" | 
        
          |  | send "250 Ok" | 
        
          |  | elsif cmd =~ /^AUTH(.*)\r/ | 
        
          |  | send "235 2.7.0 Authentication successful" | 
        
          |  | elsif cmd =~ /^STARTTLS\r/ | 
        
          |  | send "220 Ready to start TLS" | 
        
          |  | @socket, @ssl_client = tlsconnect(@socket) | 
        
          |  | else | 
        
          |  | send "502 I am so dumb I only understand HELO, AUTH, STARTTLS and QUIT" | 
        
          |  | end | 
        
          |  |  | 
        
          |  | cmd = receive | 
        
          |  | end | 
        
          |  | send "221 Bye Bye" | 
        
          |  |  | 
        
          |  | close | 
        
          |  | end | 
        
          |  |  | 
        
          |  | private | 
        
          |  |  | 
        
          |  | def client | 
        
          |  | @ssl_client || @client | 
        
          |  | end | 
        
          |  |  | 
        
          |  | def close | 
        
          |  | @client.close unless @client.nil? | 
        
          |  | @ssl_client.close unless @ssl_client.nil? | 
        
          |  | end | 
        
          |  |  | 
        
          |  | def send(line) | 
        
          |  | client.puts line | 
        
          |  | puts "-> #{line}" | 
        
          |  | end | 
        
          |  |  | 
        
          |  | def receive | 
        
          |  | line = client.gets | 
        
          |  | puts "<- #{line}" | 
        
          |  | line | 
        
          |  | end | 
        
          |  |  | 
        
          |  | def ssl_socket(socket, context) | 
        
          |  | OpenSSL::SSL::SSLServer.new(socket, context) | 
        
          |  | end | 
        
          |  |  | 
        
          |  | def ssl_context | 
        
          |  | @_ssl_context ||= begin | 
        
          |  | key, cert = generate_certificate | 
        
          |  |  | 
        
          |  | c = OpenSSL::SSL::SSLContext.new | 
        
          |  | c.key = key | 
        
          |  | c.cert = cert | 
        
          |  | c | 
        
          |  | end | 
        
          |  | end | 
        
          |  |  | 
        
          |  | def tlsconnect(s) | 
        
          |  | s = ssl_socket(s, ssl_context) | 
        
          |  | puts "=> TLS connection started" | 
        
          |  | client = s.accept | 
        
          |  | puts "=> TLS connection accepted" | 
        
          |  |  | 
        
          |  | [s, client] | 
        
          |  | end | 
        
          |  |  | 
        
          |  | def generate_certificate | 
        
          |  | key = OpenSSL::PKey::RSA.new(2048) | 
        
          |  | name = OpenSSL::X509::Name.parse('CN=nobody/DC=example') | 
        
          |  |  | 
        
          |  | cert = OpenSSL::X509::Certificate.new | 
        
          |  | cert.version = 2 | 
        
          |  | cert.serial = 0 | 
        
          |  | cert.not_before = Time.now | 
        
          |  | cert.not_after = Time.now + 3600 | 
        
          |  |  | 
        
          |  | cert.public_key = key.public_key | 
        
          |  | cert.subject = name | 
        
          |  |  | 
        
          |  | extension_factory = OpenSSL::X509::ExtensionFactory.new nil, cert | 
        
          |  |  | 
        
          |  | cert.add_extension extension_factory.create_extension('basicConstraints', 'CA:FALSE', true) | 
        
          |  | cert.add_extension extension_factory.create_extension('keyUsage', 'keyEncipherment,dataEncipherment,digitalSignature') | 
        
          |  | cert.add_extension extension_factory.create_extension('subjectKeyIdentifier', 'hash') | 
        
          |  |  | 
        
          |  | cert.issuer = name | 
        
          |  | cert.sign key, OpenSSL::Digest::SHA256.new | 
        
          |  |  | 
        
          |  | [key, cert] | 
        
          |  | end | 
        
          |  | end | 
        
          |  |  | 
        
          |  | FakeSmtpServer.new(3555).start | 
        
          |  |  | 
        
          |  | puts "fake_smtp_server: Exiting now the conversation has finished." | 
        
          |  | exit 0 |