Достаточно полное описание протокола SMTP

18 ноября 2009

Я так подозреваю, что среди вас найдется те, кто скажет, что этот пост боян и незачет. Действительно, описание сетевых протоколов появляется в интернете довольно часто, да и по запросу «описание протокола SMTP» в Google находится немало релевантных страниц.

Тем не менее, сегодня мне потребовалось на скорую руку написать скрипт отправки электронной почты с аттачем, а подходящих библиотек и документации под рукой не оказалось. Тут же началось диагональное чтение RFC, поиск по форумам, разыскивание старых исходных кодов, паника и нервы.

Потому я решил начать публиковать описания сетевых протоколов в этом блоге, чтобы в будущем они всегда были у меня под рукой. Так что готовьтесь — POP3, IMAP, DNS, FTP, HTTP, IRC и о чем я там забыл — все на подходе (ссылки появляются по мере написания соответствующих постов).

А пока поговорим об SMTP. Если кто-то не знает или вдруг забыл, Simple Mail Transfer Protocol — это протокол, с помощью которого происходит отправка электронной почты в интернете на протяжении последних лет сорока. Полное его описание занимает почти два десятка документов RFC, и это как бы намекает, что протокол не прост. Однако по всей видимости здесь применимо правило 10/90 в нетрадиционной трактовке — «90% времени мы пользуемся частью протокола, описанной на 10% документации».

Для отправки письма воспользуемся почтовым сервером smtp.mail.ru и утилитой telnet, входящую в состав любой unixlike операционной системы.

$ telnet smtp.mail.ru 25
Trying 94.100.177.1...
Connected to smtp.mail.ru.
Escape character is '^]'.
220 mail.ru ESMTP Wed, 18 Nov 2009 20:44:13 +0300
HELO some.host.ru
250 mx30.mail.ru Hello some.host.ru [123.45.67.89]
AUTH LOGIN
334 VXNlcm5hbWU6
dXNlckBtYWlsLnJ1
334 UGFzc3dvcmQ6
cXdlcnR5
235 Authentication succeeded

Тут мы коннектимся к SMTP-серверу, он приветствует нас кодом 220, в ответ мы называем имя своего хоста HELO some.host.ru. Мы можем выдать себя за кого угодно, например за localhost, но не факт, что сервер нам поверит. Он может не полениться сделать resolve доменного имени и сравнить его с IP клиента, потому честность приветствуется.

Далее мы говорим, что хотели бы пройти аутентификацию (AUTH LOGIN). LOGIN — это тип аутентификации, по логину и паролю. Есть и другие способы, но они нас мало интересуют, особенно если вспомнить про существование SSL. Следом за этой командой мы шлем имя пользователя и пароль в кодировке Base64. В этом примере я использовал имя пользователя user@mail.ru и пароль qwerty. Если сервер отвечает нам кодом 235, значит аутентификация прошла успешно.

Тут следует обратить внимание на следующее.

  • Имя пользователя может не равняться его почтовому адресу. Я знаю, что на некоторых хостингах используется имя пользователя типа user#mail.ru. И вообще оно может быть любым и никак не связанным с почтовым адресом.
  • Аутентификация является необязательным шагом в протоколе. Есть много причин, по которым smtp-сервер должен обслуживать анонимных пользователей. Например, сервер должен принимать почту от других smtp-серверов, если он указан в MX-записи домена. Но об этом мы поговорим в посте о DNS.

Следующий шаг — непосредственно посылка сообщения. Тут есть два варианта, простой и сложный. Для начала разберем простой:

MAIL FROM: Test User <user@mail.ru>
250 OK
RCPT TO: Gmail User <user@gmail.com>
250 Accepted
DATA
354 Enter message, ending with "." on a line by itself
From: Test User <user@mail.ru>
Subject: Hello, Gmail User!

How are you?
.
250 OK id=1NAof5-000BeN-00

Командами MAIL FROM и RCPT TO мы сообщаем серверу, кто является отправителем, а кто — получателем сообщения. Да, одной учетной записи может соответствовать много почтовых адресов, а если сервер неправильно настроен (или принимает письмо от другого SMTP сервера), мы можем с легкостью слать письма от чужого имени.

Затем мы говорим DATA, вводим текст письма и обозначаем его конец, введя одну точку. Вы спросите, что делать, если в тексте письма есть строка, содержащая одну точку? Все просто — ввести две точки.

Кроме текста сообщения в теле письма мы можем также указать (помимо прочего) тему сообщения (Subject) и отправителя (точно, еще раз!). Последнее, в частности, используется Gmail — см «Настройки → Аккаунты и импорт → Отправить сообщение с другого адреса».

Если все сделано правильно, сервер ответит нам 250 и сообщит id сообщения. Немного отступая от темы — а вы знали, что согласно спецификации SMTP, письмо может идти до получателя в течение 48 часов?

Но вернемся к нашим баранам. А точнее — второму, «сложному» способу отправки сообщения. Рассмотрим пример:

DATA
354 Enter message, ending with "." on a line by itself
From: Test User <user@mail.ru>
Subject: Hello, Gmail User!
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="aaaaaaaaaa"
--aaaaaaaaaa
Content-Type: text/html; charser="koi8-r"

Привет, как твои дела?
--aaaaaaaaaa
Content-Type: application/octet-stream; name="file.txt"
Content-Transfer-Encoding: base64

z/Do4uXyLCDs6PAh
--aaaaaaaaaa--
.
250 OK id=1NAof5-000BeN-00

Следующая строчка:

Content-Type: multipart/mixed; boundary="aaaaaaaaaa"

говорит серверу о том, что сообщение состоит из частей, а в качестве разделителя используется строка из 10-и латинских букв а.

Дополнение: Еще одна интересная возможность MIME: Alternative Subtype.

Остальное, на мой взгляд, должно быть понятно. Сообщение состоит из двух частей — текста сообщения в формате HTML и кодировке KOI8-R и аттача в кодировке Base64. Отмечу только, что текст сообщения также может передаваться в Base64.

После отправки сообщения мы можем отправить еще несколько сообщений, после чего нам предстоит попрощаться:

QUIT
221 mx76.mail.ru closing connection
Connection closed by foreign host.

Да, кстати, любой ответ сервера может состоять из нескольких строк. Тогда он выглядит примерно так (см пояснения в описании протокола FTP):

HELO some.host.name
250-mx.google.com at your service, [123.45.67.89]
SIZE 35651584
8BITMIME
AUTH LOGIN PLAIN
ENHANCEDSTATUSCODES
250 PIPELINING

Вот и все, что я хотел сегодня написать. Надеюсь, вы узнали что-нибудь новое или хотя бы освежили старые знания.

Метки: , .


Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.