TCPSocket and RFC 821

I wanted to send an email from my desktop using a simple Ruby program.
I installed 1st SMTP Server
(1st SMTP Server : EmailArms.Com) - an easy to use
SMTP mail relay server. It is used for relaying your email messages to
its destinations quickly and easily. For this server, I need to use
localhost and port 25 on my PC.

Then the program I wrote was (based on RFC 821) -

require ‘socket’
t = TCPSocket.new(‘localhost’, 25)
puts t.gets
t.puts ‘HELO Welcome from Ruby’
puts t.gets
t.puts ‘MAIL FROM:[email protected]
puts t.gets
t.puts ‘RCPT TO:[email protected]
puts t.gets
t.puts ‘DATA’
puts t.gets
t.puts ‘Test Email from Ruby’
t.puts “\r\n.\r\n”
puts t.gets
t.puts ‘QUIT’
puts t.gets
t.close

However, while running the program, I get an error as follows:

220 Welcome to the 1st SMTP Server

250 Hello Welcome from Ruby

250 [email protected] Address Okay

250 [email protected] Address Okay

354 Start mail input; end with .

nil
email.rb:15:in write': Invalid argument (Errno::EINVAL) from email.rb:15:in puts’
from email.rb:15

Exit code: 1

I am unable to figure out what the problem is. All help appreciated.

This line:
t.puts “\r\n.\r\n”
puts one more linefeed on the output than you perhaps intended, which
may account for the nil in your output. The QUIT command may be
causing the localhost server-connection to close before your process
has a chance to read the input. Meaning that the EINVAL error is
generated by the gets call you make right after sending QUIT.

Try the same program with a mail server located on a different machine
and see if you get a different result. After sending QUIT on an SMTP
connection, you don’t really need to read the server response. (Unless
you suspect for some reason the server will send an interesting
error-response to your QUIT, which I’ve never seen in many years of
working with SMTP servers.)

One more thing that affects localhost connections. I like to avoid
puts(some_line) in favor of write("#{some_line}\r\n") because on many
kernels, the latter will result in one kernel-crossing system call while
the
former results in two. I haven’t investigated but I suspect this is
caused
by an implementation detail in Ruby.

Satish T. wrote:

t.puts ‘DATA’
puts t.gets
t.puts ‘Test Email from Ruby’
t.puts “\r\n.\r\n”

Ok, so obviously you’re not sending a normal RFC-822 header here. Is it
possible that your SMTP server is so strict about this that it closes
the connection silently?

“Satish T.” [email protected] writes:

t = TCPSocket.new(‘localhost’, 25)
puts t.gets
t.puts ‘HELO Welcome from Ruby’
puts t.gets
t.puts ‘MAIL FROM:[email protected]
puts t.gets
t.puts ‘RCPT TO:[email protected]
puts t.gets
t.puts ‘DATA’
puts t.gets
t.puts ‘Test Email from Ruby’
t.puts “test email from ruby\r\n”

“Satish T.” [email protected] writes:

t.puts “MAIL FROM:[email protected]\r\n”

354 Start mail input; end with .

nil

Exit code: 0

/tmp $ ruby try.rb
220 jenny-gnome.dyndns.org ESMTP
250 jenny-gnome.dyndns.org
250 ok
250 ok
354 go ahead
250 ok 1154319423 qp 25461
/tmp $

What is your SMTP server?

I’d appreciate any other suggestions. Incidentally, if I write a program
using the Net::SMTP class, I am able to send off emails with my current
configuration.

Please paste this code that uses Net::SMTP.

YS.

I tried the various suggestions given by Francis and Yohanes, but still
it
does not work.

The executable code now is:

require ‘socket’
t = TCPSocket.new(‘localhost’, 25)
puts t.gets
t.puts “HELO Welcome from Ruby\r\n”
puts t.gets
t.puts “MAIL FROM:[email protected]\r\n”
puts t.gets
t.puts “RCPT TO:[email protected]\r\n”
puts t.gets
t.puts ‘DATA’
puts t.gets
t.puts “Test email from ruby\r\n”
t.puts “\r\n.\r\n”
t.puts ‘QUIT’
puts t.gets
t.close

and the output is:

ruby email.rb
220 Welcome to the 1st SMTP Server

250 Hello Welcome from Ruby

250 [email protected] Address Okay

250 [email protected] Address Okay

354 Start mail input; end with .

nil

Exit code: 0

I’d appreciate any other suggestions. Incidentally, if I write a program
using the Net::SMTP class, I am able to send off emails with my current
configuration.

Like I said before I am using a relay SMTP server called 1st SMTP server
on
my Windows XP. The code using TCPSocket does not do authenticate and as
such
I am not using our actual SMTP server.

Since you asked for the code using Net::SMTP class, I have pasted it
here -

rubysmtp.rb

require ‘net/smtp’
user_from = “[email protected]
user_to = “[email protected]
the_email = “From: [email protected]\nSubject: Hello\n\nEmail by
Ruby.\n\n”

handling exceptions

begin
Net::SMTP.start(‘auth.smtp.1and1.co.uk’, 25, ‘auth.smtp.1and1.co.uk’,
[email protected]’, ‘password’, :login) do
|smtpclient|
smtpclient.send_message(the_email, user_from, user_to)
end
rescue Exception => e
print "Exception occured: " + e
end

Satish T. wrote:

I tried the various suggestions given by Francis and Yohanes, but still
it
does not work.

What happens if you simply telnet to the SMTP server and enter all of
the commands interactively? And, based on your last code framgment, you
haven’t cleaned up all the extra linefeeds you’re sending. Change puts
to write throughout, and change “QUIT” to “QUIT\r\n”

Also, your Net::SMTP fragment uses authentication and your socket-code
fragment does not. Although most mail servers would give a recognizable
error msg if they required auth.

Satish T. wrote:

I used Telnet and it got stuck when I typed . to stop the DATA. What
change
do I need to make in the TCPSocket code for authentication?

What does “got stuck” mean? Did it give an error message? Did it close
the connection? Did it hang? Show us a screenshot of the telnet
conversation.

I used Telnet and it got stuck when I typed . to stop the DATA. What
change
do I need to make in the TCPSocket code for authentication?

Here’s my Telnet session using 1st SMTP server.


220 Welcome to the 1st SMTP Server
HELO
250 Hello
MAIL FROM:[email protected]
250 [email protected] Address Okay
RCPT TO:[email protected]
250 [email protected] Address Okay
DATA
354 Start mail input; end with .
This is the Telnet transcript
For Ruby T.
.

After this no progress.

Francis, thanks for helping me along. I tried your suggestions. However,
it
still does not work. Do you know the ESMTP command sequence to send an
email
over TSL and Authenticate?

Satish T. wrote:

Francis, thanks for helping me along. I tried your suggestions. However,
it
still does not work. Do you know the ESMTP command sequence to send an
email
over TSL and Authenticate?

Well, maybe try something like this:
Use EHLO instead of HELO.
After EHLO and before MAIL FROM, send:

AUTH PLAIN xxx\r\n

where xxx is the base-64 encoding of a string consisting of a binary
zero followed by your account name (on the mail server), followed by
another binary zero, followed by your password. In Ruby, perhaps
something like

require ‘base64’
Base64.encode64( “\000#{username}\000#{psw}” ).chomp

Satish T. wrote:

Here’s my Telnet session using 1st SMTP server.


220 Welcome to the 1st SMTP Server
HELO
250 Hello
MAIL FROM:[email protected]
250 [email protected] Address Okay
RCPT TO:[email protected]
250 [email protected] Address Okay
DATA
354 Start mail input; end with .
This is the Telnet transcript
For Ruby T.
.

After this no progress.

Try this instead:

DATA
354 Start…
Subject: This is an email

This is the telnet transcript
.

Notice the blank line between the RFC-822 header and the message
content. Also, try running the same telnet conversation across a network
from a Windows client. (Telnet on Windows sends CRLF line-endings, which
your mail server may be anal-retentive enough to require.)

Yohanes, you are right. The RFC’s and specifically which ones to use are
a
bit confusing. I read thro’ RFC’s 1869, 2554, 2821 and 3207 but am still
not
clear of the exact sequence of commands to be executed while using
TCPSocket. I searched Google but did not find a specific solution. Still
searching…

I am abonding the local relay server and now trying to connect to the
remote
ESMTP server that requires authentication.

“Satish T.” [email protected] writes:

Like I said before I am using a relay SMTP server called 1st SMTP server on
my Windows XP. The code using TCPSocket does not do authenticate and as such
I am not using our actual SMTP server.

Could it be that the SMTP server (relay or not), does a running
verification for the content of the data?

That is, if the data is not in RFC822 format, then it aborts the
connection ungracefully.

Or, could it be aborting the connection because you don’t authenticate
when using the TCPSocket version?

YS.