Send email without an email service (like Sendgrid, AWS SES etc.)
October 20, 2018
As supportbee.com’s Backend Eng. & DevOps Lead, I run SupportBee’s email sync and delivery infrastructure and often find myself in situations where I’ve to send different types of emails (Plain text emails, HTML emails, malformed emails etc.) to our emails importers for debugging/testing purposes.
While using an email service (like Sendgrid etc.) is typically the easiest way to send an email to any email server, these services often modify the email slightly. They may add extra headers or remove malformed headers or perform other modification which many times renders them unsuitable for debugging/testing purposes.
A better approach in these scenarios is to just send an email to any email server using your preferred programming language.
Here’s how you can easily send an email from the Ruby programming language.
Install the mail gem
gem install mail
Save the script below on your laptop
require "mail"
require "net/smtp"
from = "[email protected]"
to = "[email protected]"
destination_smtp_server_ip = "172.217.194.26"
mail = Mail.new do
from from
to to
subject "Just a test email"
body "Just a test email"
end
smtp = Net::SMTP.new(destination_smtp_server_ip)
smtp.open_timeout = 5
smtp.start do |smtp|
smtp.send_message(mail.to_s, from, to)
end
Modify the from
and the to
address. Modify the destination_smtp_server_ip
to the ip address of email server you wish to deliver the email to. If you don’t have the destination email server’s ip address, query the recipient domain’s (gmail.com
in this case) MX records
~$ dig MX gmail.com +short
5 gmail-smtp-in.l.google.com.
40 alt4.gmail-smtp-in.l.google.com.
10 alt1.gmail-smtp-in.l.google.com.
20 alt2.gmail-smtp-in.l.google.com.
30 alt3.gmail-smtp-in.l.google.com.
~$ dig gmail-smtp-in.l.google.com +short
172.217.194.26
Run the script
ruby send_email_directly.rb
That’s it! In case you see a timeout error (like I did), your broadband provider may be preventing you from sending emails. I switched to my phone’s 4G (which is from a different provider) to resolve this.
~/tmp [r-2.2.3]% ruby send_email_directly.rb
/Users/nisanth/.rvm/rubies/ruby-2.2.3/lib/ruby/2.2.0/net/smtp.rb:541:in `initialize': execution expired (Net::OpenTimeout)
from /Users/nisanth/.rvm/rubies/ruby-2.2.3/lib/ruby/2.2.0/net/smtp.rb:541:in `open'
from /Users/nisanth/.rvm/rubies/ruby-2.2.3/lib/ruby/2.2.0/net/smtp.rb:541:in `tcp_socket'
from /Users/nisanth/.rvm/rubies/ruby-2.2.3/lib/ruby/2.2.0/net/smtp.rb:551:in `block in do_start'
from /Users/nisanth/.rvm/rubies/ruby-2.2.3/lib/ruby/2.2.0/timeout.rb:88:in `block in timeout'
from /Users/nisanth/.rvm/rubies/ruby-2.2.3/lib/ruby/2.2.0/timeout.rb:98:in `call'
from /Users/nisanth/.rvm/rubies/ruby-2.2.3/lib/ruby/2.2.0/timeout.rb:98:in `timeout'
from /Users/nisanth/.rvm/rubies/ruby-2.2.3/lib/ruby/2.2.0/net/smtp.rb:550:in `do_start'
from /Users/nisanth/.rvm/rubies/ruby-2.2.3/lib/ruby/2.2.0/net/smtp.rb:520:in `start'
from send_email_directly.rb:21:in `<main>'
Please note that if you send emails directly to a popular email service provider like Gmail and not to your own email servers, there is a chance that your email may be a classified as spam.
Automatically query MX records
If you don’t care which email server receives your email, you can modify the Ruby script to automatically query the recipient’s domain MX records for you
require "mail"
require "resolv"
require "net/smtp"
from = "[email protected]"
to = "[email protected]"
mail = Mail.new do
from from
to to
subject "Just a test email"
body "Just a test email"
end
class Domain
def initialize(domain)
@domain = domain
end
def mx_ip
mx_ips.first
end
private
attr_reader :domain
def mx_ips
Resolv::DNS.open do |dns|
mx_resources = dns.getresources(domain, Resolv::DNS::Resource::IN::MX)
mx_ips = mx_resources.map { |mx_resource| IPSocket::getaddress(mx_resource.exchange.to_s) }
end
end
end
recipient_domain = mail['To'].addrs.first.domain
destination_smtp_server_ip = Domain.new(recipient_domain).mx_ip
smtp = Net::SMTP.new(destination_smtp_server_ip)
smtp.open_timeout = 5
smtp.start do |smtp|
smtp.send_message(mail.to_s, from, to)
end