|
#!/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 |