minbox 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2c500238d8a8cfc853fd4ec9a252a7a0cde7f421e1bd31a4695c312288110e23
4
- data.tar.gz: 80b489dd81313d68e7e8e8e479060d22dd105b3b0570357eb489340d145ab0d4
3
+ metadata.gz: 7aa7df98b9f03f4db8a1971c248579b5f033fef52ff74479c5bababac5ef416f
4
+ data.tar.gz: 30507d43a907f6a4eb730930b88e11b56b99bfdd0f45b63500271efcee3d8a90
5
5
  SHA512:
6
- metadata.gz: 7bd692169477f0e2fe78d1611ac32bdc83cdd9980a8feacf15c6574cd6d471ebe98503e6d049fba23ef991db8d5eaafc960bad31ec38d50e8c47e02efaf3b3a7
7
- data.tar.gz: 601d89064f6f2189b097615815a6b46f13d4a66989c4138815a9ea5bfb8e56c449aa829d6df10e8ddf065d737dba0520d07b98ddbe9b9f9ac1ea088a79ebe1ce
6
+ metadata.gz: ea55e3fd8bd032f1666bdbcf7a6892456502379cbabeac6de95a3572e66a8386f25e2f3ef6027b04ae806710b1359d46b7d149d14af836aa627a3f4e18777865
7
+ data.tar.gz: 0c2ad2a2d4803945869874e5bbd2cd1e6aed03944cdf1a18b774d173569c2a61481a7e0b1a21c385f4e4343346c5ea7f213331314a95e021694ec002a7d41ae3
data/.gitignore CHANGED
@@ -6,6 +6,7 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
+ server.pem
9
10
 
10
11
  # rspec failure tracking
11
12
  .rspec_status
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.6.1
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- minbox (0.1.1)
4
+ minbox (0.1.2)
5
5
  mail (~> 2.7)
6
6
  redis (~> 4.1)
7
7
  thor (~> 0.20)
data/lib/minbox/cli.rb CHANGED
@@ -1,5 +1,7 @@
1
- require 'thor'
2
1
  require 'mail'
2
+ require 'net/smtp'
3
+ require 'openssl'
4
+ require 'thor'
3
5
 
4
6
  require 'minbox'
5
7
 
@@ -16,18 +18,19 @@ module Minbox
16
18
  subject 'test message'
17
19
  body "#{Time.now} This is a test message."
18
20
  end
19
- require 'net/smtp'
20
21
  Net::SMTP.start(host, port) do |smtp|
22
+ smtp.debug_output= Minbox.logger
21
23
  smtp.send_message(mail.to_s, 'me+1@example.org', 'them+1@example.com')
22
24
  smtp.send_message(mail.to_s, 'me+2@example.org', 'them+2@example.com')
23
25
  end
24
26
  end
25
27
 
26
28
  method_option :output, type: :array, default: ['stdout']
29
+ method_option :tls, type: :boolean, default: false
27
30
  desc 'server <HOST> <PORT>', 'SMTP server'
28
31
  def server(host = 'localhost', port = '25')
29
32
  publisher = Publisher.from(options[:output])
30
- Server.new(host, port).listen! do |mail|
33
+ Server.new(host, port, options[:tls]).listen! do |mail|
31
34
  publisher.publish(mail)
32
35
  end
33
36
  end
data/lib/minbox/client.rb CHANGED
@@ -1,15 +1,15 @@
1
1
  module Minbox
2
2
  class Client
3
- attr_reader :host, :socket, :logger
3
+ attr_reader :server, :socket, :logger
4
4
 
5
- def initialize(host, socket, logger)
6
- @host = host
5
+ def initialize(server, socket, logger)
6
+ @server = server
7
7
  @logger = logger
8
8
  @socket = socket
9
9
  end
10
10
 
11
11
  def handle(&block)
12
- write "220"
12
+ write "220 #{server.host} ESMTP"
13
13
  while connected? && (line = read)
14
14
  case line
15
15
  when /^EHLO/i then ehlo(line)
@@ -28,6 +28,10 @@ module Minbox
28
28
  write '502 Invalid/unsupported command'
29
29
  end
30
30
  end
31
+ close
32
+ rescue Errno::ECONNRESET, Errno::EPIPE => error
33
+ logger.error(error)
34
+ close
31
35
  end
32
36
 
33
37
  private
@@ -46,7 +50,7 @@ module Minbox
46
50
  line = read
47
51
  end
48
52
  write "250 OK"
49
- block.call(Mail.new(body.join))
53
+ block.call(Mail.new(body.join)) unless body.empty?
50
54
  end
51
55
 
52
56
  def rcpt_to(line)
@@ -59,19 +63,25 @@ module Minbox
59
63
 
60
64
  def ehlo(line)
61
65
  _ehlo, _client_domain = line.split(" ")
62
- write "250-#{host}"
63
- #write "250 AUTH PLAIN LOGIN"
66
+ write "250-#{server.host} offers a warm hug of welcome"
67
+ write "250-8BITMIME"
64
68
  write "250-ENHANCEDSTATUSCODES"
69
+ #write "250-STARTTLS"# if server.tls?
70
+ #write "250 AUTH PLAIN LOGIN"
65
71
  write "250 OK"
66
72
  end
67
73
 
68
74
  def helo(line)
69
75
  _ehlo, _client_domain = line.split(" ")
70
- write "250 #{host}"
76
+ write "250 #{server.host}"
71
77
  end
72
78
 
73
79
  def start_tls
74
- write "502 TLS not available"
80
+ write "220 Ready to start TLS"
81
+
82
+ socket = OpenSSL::SSL::SSLSocket.new(@socket, server.ssl_context)
83
+ socket.sync_close = true
84
+ @socket = socket.accept
75
85
  end
76
86
 
77
87
  def reset
data/lib/minbox/server.rb CHANGED
@@ -1,16 +1,23 @@
1
1
  module Minbox
2
2
  class Server
3
- attr_reader :host, :port, :logger
3
+ attr_reader :host, :port, :logger, :key
4
4
 
5
- def initialize(host = 'localhost', port = 25, logger = Minbox.logger)
5
+ def initialize(host = 'localhost', port = 25, tls = false, logger = Minbox.logger)
6
6
  @host = host
7
7
  @port = port
8
8
  @logger = logger
9
+ @tls = tls
10
+ @key = OpenSSL::PKey::RSA.new(2048)
11
+ end
12
+
13
+ def tls?
14
+ @tls
9
15
  end
10
16
 
11
17
  def listen!(&block)
12
18
  logger.debug("Starting server on port #{port}...")
13
19
  @server = TCPServer.new(port.to_i)
20
+ @server = upgrade(@server) if tls?
14
21
  logger.debug("Server started!")
15
22
 
16
23
  loop do
@@ -22,11 +29,56 @@ module Minbox
22
29
 
23
30
  def handle(socket, &block)
24
31
  logger.debug("client connected: #{socket.inspect}")
25
- Client.new(host, socket, logger).handle(&block)
32
+ Client.new(self, socket, logger).handle(&block)
26
33
  end
27
34
 
28
35
  def shutdown!
29
36
  @server&.close
30
37
  end
38
+
39
+ def ssl_context
40
+ @ssl_context ||=
41
+ begin
42
+ ssl_context = OpenSSL::SSL::SSLContext.new
43
+ ssl_context.cert = certificate_for(key)
44
+ ssl_context.key = key
45
+ ssl_context.ssl_version = :TLSv1_2
46
+ ssl_context
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def upgrade(tcp_server)
53
+ server = OpenSSL::SSL::SSLServer.new(tcp_server, ssl_context)
54
+ server.start_immediately = true
55
+ server
56
+ end
57
+
58
+ def certificate_for(private_key)
59
+ certificate = OpenSSL::X509::Certificate.new
60
+ subject = '/C=CA/ST=AB/L=Calgary/O=minbox/OU=development/CN=minbox'
61
+ certificate.subject = certificate.issuer = OpenSSL::X509::Name.parse(subject)
62
+ certificate.not_before = Time.now
63
+ certificate.not_after = certificate.not_before + 30 * 24 * 60 * 60 # 30 days
64
+ certificate.public_key = private_key.public_key
65
+ certificate.serial = 1
66
+ certificate.version = 2
67
+ apply_ski_extension_to(certificate)
68
+ certificate.sign(private_key, OpenSSL::Digest::SHA256.new)
69
+ certificate
70
+ end
71
+
72
+ def apply_ski_extension_to(certificate)
73
+ extensions = OpenSSL::X509::ExtensionFactory.new
74
+ extensions.subject_certificate = certificate
75
+ extensions.issuer_certificate = certificate
76
+ certificate.add_extension(
77
+ extensions.create_extension('subjectKeyIdentifier', 'hash', false)
78
+ )
79
+ certificate.add_extension(
80
+ extensions.create_extension('keyUsage', 'keyEncipherment,digitalSignature', true)
81
+ )
82
+ end
31
83
  end
32
84
  end
@@ -1,3 +1,3 @@
1
1
  module Minbox
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: minbox
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - mo khan
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-03-08 00:00:00.000000000 Z
11
+ date: 2019-03-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mail
@@ -118,6 +118,7 @@ extra_rdoc_files: []
118
118
  files:
119
119
  - ".gitignore"
120
120
  - ".rspec"
121
+ - ".ruby-version"
121
122
  - ".travis.yml"
122
123
  - Dockerfile
123
124
  - Gemfile